diff --git a/NEsper.Avro/NEsper.Avro.csproj b/NEsper.Avro/NEsper.Avro.csproj index 768a4bf76..61da7307f 100755 --- a/NEsper.Avro/NEsper.Avro.csproj +++ b/NEsper.Avro/NEsper.Avro.csproj @@ -9,7 +9,7 @@ Properties NEsper.Avro NEsper.Avro - v4.7 + v4.5.2 512 ..\ true diff --git a/NEsper.Core/NEsper.Core.sln b/NEsper.Core/NEsper.Core.sln new file mode 100755 index 000000000..1107ef054 --- /dev/null +++ b/NEsper.Core/NEsper.Core.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26730.10 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NEsper.Core", "NEsper.Core\NEsper.Core.csproj", "{20283250-A109-4931-8C63-C86EED410979}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {20283250-A109-4931-8C63-C86EED410979}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {20283250-A109-4931-8C63-C86EED410979}.Debug|Any CPU.Build.0 = Debug|Any CPU + {20283250-A109-4931-8C63-C86EED410979}.Release|Any CPU.ActiveCfg = Release|Any CPU + {20283250-A109-4931-8C63-C86EED410979}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {04C96F91-C4A5-4F50-B78F-5771632D470B} + EndGlobalSection +EndGlobal diff --git a/NEsper.Core/NEsper.Core/NEsper.Core.csproj b/NEsper.Core/NEsper.Core/NEsper.Core.csproj new file mode 100755 index 000000000..b62613299 --- /dev/null +++ b/NEsper.Core/NEsper.Core/NEsper.Core.csproj @@ -0,0 +1,20 @@ + + + + netstandard2.0 + + + + + + + + + + + + + + + + diff --git a/NEsper.Core/NEsper.Core/adapter/Adapter.cs b/NEsper.Core/NEsper.Core/adapter/Adapter.cs new file mode 100755 index 000000000..20370963c --- /dev/null +++ b/NEsper.Core/NEsper.Core/adapter/Adapter.cs @@ -0,0 +1,39 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.adapter +{ + /// + /// An Adapter takes some external data, converts it into events, and sends + /// it into the runtime engine. + /// + public interface Adapter : IDisposable + { + /// Start the sending of events into the runtime egine. + /// EPException in case of errors processing the events + void Start(); + + /// Pause the sending of events after a Adapter has been started. + /// EPException if this Adapter has already been stopped + void Pause(); + + /// Resume sending events after the Adapter has been paused. + /// EPException in case of errors processing the events + void Resume(); + + /// Stop sending events and return the Adapter to the OPENED state, ready to be started once again. + /// EPException in case of errors releasing resources + void Stop(); + + /// Get the state of this Adapter. + /// state + AdapterState GetState(); + } +} diff --git a/NEsper.Core/NEsper.Core/adapter/AdapterSPI.cs b/NEsper.Core/NEsper.Core/adapter/AdapterSPI.cs new file mode 100755 index 000000000..f8c24f5e7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/adapter/AdapterSPI.cs @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; + +namespace com.espertech.esper.adapter +{ + /// + /// An Adapter takes some external data, converts it into events, and sends it into + /// the runtime engine. + /// + public interface AdapterSPI + { + /// Gets or sets the engine instance. + /// engine + EPServiceProvider EPServiceProvider { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/adapter/AdapterState.cs b/NEsper.Core/NEsper.Core/adapter/AdapterState.cs new file mode 100755 index 000000000..912e60376 --- /dev/null +++ b/NEsper.Core/NEsper.Core/adapter/AdapterState.cs @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.adapter +{ + /// The state of a Adapter. + public enum AdapterState + { + /// Opened state. + OPENED, + + /// Started state. + STARTED, + + /// Paused state. + PAUSED, + + /// Destroyed state. + DESTROYED + } +} diff --git a/NEsper.Core/NEsper.Core/adapter/AdapterStateManager.cs b/NEsper.Core/NEsper.Core/adapter/AdapterStateManager.cs new file mode 100755 index 000000000..fe7723b01 --- /dev/null +++ b/NEsper.Core/NEsper.Core/adapter/AdapterStateManager.cs @@ -0,0 +1,120 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.adapter +{ + /// + /// A utility to manage the state transitions for an InputAdapter. + /// + public class AdapterStateManager + { + private bool _stateTransitionsAllowed = true; + + /// + /// Initializes a new instance of the class. + /// + public AdapterStateManager() + { + State = AdapterState.OPENED; + } + + /// + /// Gets the state. + /// + /// the state + public AdapterState State { get; private set; } + + /// + /// Transition into the STARTED state (from the OPENED state). + /// + /// IllegalStateTransitionException if the transition is not allowed + public void Start() + { + AssertStateTransitionsAllowed(); + if(State != AdapterState.OPENED) + { + throw new IllegalStateTransitionException("Cannot start from the " + State + " state"); + } + State = AdapterState.STARTED; + } + + /// + /// Transition into the OPENED state. + /// + /// IllegalStateTransitionException if the transition isn't allowed + public void Stop() + { + AssertStateTransitionsAllowed(); + if(State != AdapterState.STARTED && State != AdapterState.PAUSED) + { + throw new IllegalStateTransitionException("Cannot stop from the " + State + " state"); + } + State = AdapterState.OPENED; + } + + /// + /// Transition into the PAUSED state. + /// + /// IllegalStateTransitionException if the transition isn't allowed + public void Pause() + { + AssertStateTransitionsAllowed(); + if(State != AdapterState.STARTED) + { + throw new IllegalStateTransitionException("Cannot pause from the " + State + " state"); + } + State = AdapterState.PAUSED; + } + + /// + /// Transition into the STARTED state (from the PAUSED state). + /// + /// IllegalStateTransitionException if the state transition is not allowed + public void Resume() + { + AssertStateTransitionsAllowed(); + if(State != AdapterState.PAUSED) + { + throw new IllegalStateTransitionException("Cannot resume from the " + State + " state"); + } + State = AdapterState.STARTED; + } + + /// + /// Transition into the DESTROYED state. + /// + /// IllegalStateTransitionException if the transition isn't allowed + public void Dispose() + { + if(State == AdapterState.DESTROYED) + { + throw new IllegalStateTransitionException("Cannot destroy from the " + State + " state"); + } + State = AdapterState.DESTROYED; + } + + /// + /// Disallow future state changes, and throw an IllegalStateTransitionException if they are attempted. + /// + public void DisallowStateTransitions() + { + _stateTransitionsAllowed = false; + } + + /// + /// Asserts the state transitions allowed. + /// + private void AssertStateTransitionsAllowed() + { + if(!_stateTransitionsAllowed) + { + throw new IllegalStateTransitionException("State transitions have been disallowed"); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/adapter/BaseSubscription.cs b/NEsper.Core/NEsper.Core/adapter/BaseSubscription.cs new file mode 100755 index 000000000..b80217d95 --- /dev/null +++ b/NEsper.Core/NEsper.Core/adapter/BaseSubscription.cs @@ -0,0 +1,69 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.threading; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.core.service.multimatch; +using com.espertech.esper.filter; + +namespace com.espertech.esper.adapter +{ + /// + /// Subscription is a concept for selecting events for processing out of all + /// events available from an engine instance. + /// + public abstract class BaseSubscription : Subscription, FilterHandleCallback + { + /// The event type of the events we are subscribing for. + public String EventTypeName { get; set; } + + /// The name of the subscription. + public String SubscriptionName { get; set; } + + public OutputAdapter Adapter { get; private set; } + + public abstract int StatementId { get; } + + public abstract bool IsSubSelect { get; } + + public abstract void MatchFound(EventBean theEvent, ICollection allStmtMatches); + + /// Ctor, assigns default name. + protected BaseSubscription() + { + SubscriptionName = "default"; + } + + public void RegisterAdapter(OutputAdapter adapter) + { + Adapter = adapter; + RegisterAdapter(((AdapterSPI) adapter).EPServiceProvider); + } + + /// Register an adapter. + /// engine + public void RegisterAdapter(EPServiceProvider epService) + { + var spi = (EPServiceProviderSPI) epService; + var eventType = spi.EventAdapterService.GetEventTypeByName(EventTypeName); + var fvs = new FilterSpecCompiled(eventType, null, new IList[0], null).GetValueSet(null, null, null); + + var name = "subscription:" + SubscriptionName; + var metricsHandle = spi.MetricReportingService.GetStatementHandle(-1, name); + var statementHandle = new EPStatementHandle(-1, name, name, StatementType.ESPERIO, name, false, metricsHandle, 0, false, false, spi.ServicesContext.MultiMatchHandlerFactory.GetDefaultHandler()); + var agentHandle = new EPStatementAgentInstanceHandle(statementHandle, ReaderWriterLockManager.CreateDefaultLock(), -1, new StatementAgentInstanceFilterVersion(), null); + var registerHandle = new EPStatementHandleCallback(agentHandle, this); + spi.FilterService.Add(fvs, registerHandle); + } + } +} diff --git a/NEsper.Core/NEsper.Core/adapter/IllegalStateTransitionException.cs b/NEsper.Core/NEsper.Core/adapter/IllegalStateTransitionException.cs new file mode 100755 index 000000000..2c7155ef2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/adapter/IllegalStateTransitionException.cs @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.adapter +{ + /// + /// Thrown when an illegal Adapter state transition is attempted. + /// + [Serializable] + public class IllegalStateTransitionException : EPException + { + /// + /// an explanation of the cause of the exception + public IllegalStateTransitionException(String message) + : base(message) + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/adapter/InputAdapter.cs b/NEsper.Core/NEsper.Core/adapter/InputAdapter.cs new file mode 100755 index 000000000..e4875ad9e --- /dev/null +++ b/NEsper.Core/NEsper.Core/adapter/InputAdapter.cs @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.adapter +{ + /// + /// An InputAdapter takes some external data, converts it into events, and sends it into the runtime engine. + /// + public interface InputAdapter : Adapter + { + } + + public class InputAdapterConstants + { + /// + /// Use for MapMessage events to indicate the event type name. + /// + public static readonly string ESPERIO_MAP_EVENT_TYPE = typeof(InputAdapter).FullName + "_maptype"; + } +} diff --git a/NEsper.Core/NEsper.Core/adapter/OutputAdapter.cs b/NEsper.Core/NEsper.Core/adapter/OutputAdapter.cs new file mode 100755 index 000000000..5b867b7ab --- /dev/null +++ b/NEsper.Core/NEsper.Core/adapter/OutputAdapter.cs @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.adapter +{ + /// An output adapter transforms engine events and + public interface OutputAdapter : Adapter + { + /// Returns the subscriptions. + /// map of name and subscription + IDictionary SubscriptionMap { get; set; } + + /// Returns a given subscription by it's name, or null if not found + /// is the subscription + /// subcription or null + Subscription GetSubscription(String subscriptionName); + } +} diff --git a/NEsper.Core/NEsper.Core/adapter/Subscription.cs b/NEsper.Core/NEsper.Core/adapter/Subscription.cs new file mode 100755 index 000000000..e5065a4d3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/adapter/Subscription.cs @@ -0,0 +1,33 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.adapter +{ + /// + /// Subscriptions are associated with an output adapter and dictate which events are + /// sent to a given adapter. + /// + public interface Subscription + { + /// Returns the subscription name. + /// subscription name + string SubscriptionName { get; set; } + + /// Returns the type name of the event type we are looking for. + /// event type name + string EventTypeName { get; } + + /// Returns the output adapter this subscription is associated with. + /// output adapter + OutputAdapter Adapter { get; } + + /// Sets the output adapter this subscription is associated with. + /// to set + void RegisterAdapter(OutputAdapter adapter); + } +} diff --git a/NEsper.Core/NEsper.Core/client/AutoImportDesc.cs b/NEsper.Core/NEsper.Core/client/AutoImportDesc.cs new file mode 100755 index 000000000..0b7867087 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/AutoImportDesc.cs @@ -0,0 +1,121 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using com.espertech.esper.compat; + +namespace com.espertech.esper.client +{ + [Serializable] + public class AutoImportDesc + { + /// + /// Gets or sets the type of the namespace or. + /// + /// The type of the namespace or. + public string TypeOrNamespace { get; set; } + + /// + /// Gets or sets an optional assembly name or file. + /// + /// The assembly name or file. + public string AssemblyNameOrFile { get; set; } + + /// + /// Initializes a new instance of the class. + /// + public AutoImportDesc() + { + TypeOrNamespace = null; + AssemblyNameOrFile = null; + } + + /// + /// Initializes a new instance of the class. + /// + /// Type of the namespace or. + public AutoImportDesc(string namespaceOrType) + { + TypeOrNamespace = namespaceOrType; + AssemblyNameOrFile = null; + } + + /// + /// Initializes a new instance of the class. + /// + /// Type of the namespace or. + /// The assembly name or file. + public AutoImportDesc(string namespaceOrType, string assemblyNameOrFile) + { + TypeOrNamespace = namespaceOrType; + AssemblyNameOrFile = assemblyNameOrFile; + } + + public AutoImportDesc(string namespaceOrType, Assembly assembly) + { + TypeOrNamespace = namespaceOrType; + AssemblyNameOrFile = assembly.FullName; + } + + /// + /// Compares the equality of two instances. + /// + /// The other. + /// + public bool Equals(AutoImportDesc other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return Equals(other.TypeOrNamespace, TypeOrNamespace) && Equals(other.AssemblyNameOrFile, AssemblyNameOrFile); + } + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + /// + /// The parameter is null. + /// + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != typeof (AutoImportDesc)) return false; + return Equals((AutoImportDesc) obj); + } + + /// + /// Returns a hash code for this instance. + /// + /// + /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. + /// + public override int GetHashCode() + { + unchecked { + return ((TypeOrNamespace != null ? TypeOrNamespace.GetHashCode() : 0)*397) ^ (AssemblyNameOrFile != null ? AssemblyNameOrFile.GetHashCode() : 0); + } + } + + /// + /// Returns a that represents this instance. + /// + /// + /// A that represents this instance. + /// + public override string ToString() + { + return string.Format("TypeOrNamespace: {0}, AssemblyNameOrFile: {1}", TypeOrNamespace, AssemblyNameOrFile); + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/Configuration.cs b/NEsper.Core/NEsper.Core/client/Configuration.cs new file mode 100755 index 000000000..88eaf3600 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/Configuration.cs @@ -0,0 +1,1405 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Reflection; +using System.Xml; + +using com.espertech.esper.client.annotation; +using com.espertech.esper.client.hook; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.dataflow.ops; +using com.espertech.esper.events; +using com.espertech.esper.util; + +namespace com.espertech.esper.client +{ + /// + /// An instance of Configuration allows the application + /// to specify properties to be used when + /// creating a EPServiceProvider. Usually an application will create + /// a single Configuration, then get one or more instances of + /// via . + /// The Configuration is meant + /// only as an initialization-time object. EPServiceProviders are + /// immutable and do not retain any association back to the + /// Configuration. + /// + /// The format of an Esper XML configuration file is defined in + /// esper-configuration-x.y.xsd. + /// + /// + [Serializable] + public class Configuration + : ConfigurationOperations + , ConfigurationInformation + { + /// + /// Import name of the package that hosts the annotation classes. + /// + public readonly static String ANNOTATION_IMPORT = typeof(NameAttribute).Namespace; + + /// Default name of the configuration file. + internal static readonly string ESPER_DEFAULT_CONFIG = "esper.cfg.xml"; + + private static readonly ILog Log = + LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// Map of event name and fully-qualified class name. + private IDictionary _eventClasses; + + /// Map of event type name and XML DOM configuration. + private IDictionary _eventTypesXmldom; + + /// Map of event type name and XML DOM configuration. + private IDictionary _eventTypesAvro; + + /// Map of event type name and Legacy-type event configuration. + private IDictionary _eventTypesLegacy; + + /// + /// The type names for events that are backed by IDictionary, + /// not containing strongly-typed nested maps. + /// + private IDictionary _mapNames; + + /// + /// The type names for events that are backed by IDictionary, + /// possibly containing strongly-typed nested maps. + /// + /// Each entrie's value must be either a Type or a Map<string,Object> to + /// define nested maps. + /// + /// + private IDictionary> _nestableMapNames; + + /// + /// The type names for events that are backed by IDictionary, + /// possibly containing strongly-typed nested maps. + /// + /// Each entrie's value must be either a Type or a Map<string,Object> to + /// define nested maps. + /// + /// + private IDictionary> _nestableObjectArrayNames; + + /// Map event types additional configuration information. + private IDictionary _mapTypeConfigurations; + + /// Map event types additional configuration information. + private IDictionary _objectArrayTypeConfigurations; + + /// + /// The class and package name imports that + /// will be used to resolve partial class names. + /// + private IList _imports; + + /// + /// For annotations only, the class and package name imports that + /// will be used to resolve partial class names (not available in EPL statements unless used in an annotation). + /// + private IList _annotationImports; + + /// + /// The class and package name imports that + /// will be used to resolve partial class names. + /// + private IDictionary _databaseReferences; + + /// + /// Optional classname to use for constructing services context. + /// + private string _epServicesContextFactoryClassName; + + /// List of configured plug-in views. + private IList _plugInViews; + + /// List of configured plug-in views. + private IList _plugInVirtualDataWindows; + + /// List of configured plug-in pattern objects. + private IList _plugInPatternObjects; + + /// List of configured plug-in aggregation functions. + private IList _plugInAggregationFunctions; + + /// List of configured plug-in aggregation multi-functions. + private IList _plugInAggregationMultiFunctions; + + /// List of configured plug-in single-row functions. + private IList _plugInSingleRowFunctions; + + /// List of adapter loaders. + private IList _pluginLoaders; + + /// Saves engine default configs such as threading settings + private ConfigurationEngineDefaults _engineDefaults; + + /// Saves the packages to search to resolve event type names. + private ISet _eventTypeAutoNamePackages; + + /// Map of variables. + private IDictionary _variables; + + /// + /// Map of class name and configuration for method invocations on that class. + /// + private IDictionary _methodInvocationReferences; + + /// Map of plug-in event representation name and configuration + private IDictionary _plugInEventRepresentation; + + /// Map of plug-in event types. + private IDictionary _plugInEventTypes; + + /// + /// Uris that point to plug-in event representations that are given a chance to dynamically resolve an event type name to an + /// event type, as it occurs in a new EPL statement. + /// + private IList _plugInEventTypeResolutionUris; + + /// + /// All revision event types which allow updates to past events. + /// + private IDictionary _revisionEventTypes; + + /// + /// Variant streams allow events of disparate types to be treated the same. + /// + private IDictionary _variantStreams; + + [NonSerialized] private IDictionary _transientConfiguration; + + /// + /// Constructs an empty configuration. The auto import values + /// are set by default to System, System.Collections and + /// System.Text. + /// + public Configuration() + { + Reset(); + } + + /// + /// Get the configuration file as an InputStream. Might be overridden + /// by subclasses to allow the configuration to be located by some arbitrary + /// mechanism. + /// + /// See getResourceAsStream for information on how the resource name is resolved. + /// + /// + /// is the resource name + /// thrown to indicate error reading configuration + /// input stream for resource + internal static Stream GetConfigurationInputStream(string resource) + { + return GetResourceAsStream(resource); + } + + /// + /// Returns an input stream from an application resource in the classpath. + /// + /// The method first removes the '/' character from the resource name if + /// the first character is '/'. + /// + /// + /// The lookup order is as follows: + /// + /// + /// If a thread context class loader exists, use Thread.CurrentThread().getResourceAsStream + /// to obtain an InputStream. + /// + /// + /// If no input stream was returned, use the typeof(Configuration).getResourceAsStream. + /// to obtain an InputStream. + /// + /// + /// If no input stream was returned, use the typeof(Configuration).ClassLoader.getResourceAsStream. + /// to obtain an InputStream. + /// + /// + /// If no input stream was returned, throw an Exception. + /// + /// + /// to get input stream for + /// input stream for resource + internal static Stream GetResourceAsStream(String resource) + { + String stripped = resource.StartsWith("/", StringComparison.CurrentCultureIgnoreCase) ? resource.Substring(1) : resource; + Stream stream = ResourceManager.GetResourceAsStream(resource) ?? + ResourceManager.GetResourceAsStream(stripped); + if (stream == null) + { + throw new EPException(resource + " not found"); + } + return stream; + } + + public string EPServicesContextFactoryClassName + { + get { return _epServicesContextFactoryClassName; } + } + + /// + /// Sets the class name of the services context factory class to use. + /// + /// service context factory class name + public void SetEPServicesContextFactoryClassName(string epServicesContextFactoryClassName) + { + _epServicesContextFactoryClassName = epServicesContextFactoryClassName; + } + + public void AddPlugInAggregationFunctionFactory(string functionName, string aggregationFactoryClassName) + { + var entry = new ConfigurationPlugInAggregationFunction(); + entry.Name = functionName; + entry.FactoryClassName = aggregationFactoryClassName; + _plugInAggregationFunctions.Add(entry); + } + + /// + /// Adds a plug-in aggregation function given a EPL function name and an aggregation factory class name. + /// + /// The same function name cannot be added twice. + /// + /// is the new aggregation function name for use in EPL + /// Type of the aggregation factory. Must implement + /// ConfigurationException is thrown to indicate a problem adding the aggregation function + public void AddPlugInAggregationFunctionFactory(String functionName, Type aggregationFactoryType) + { + AddPlugInAggregationFunctionFactory(functionName, aggregationFactoryType.AssemblyQualifiedName); + } + + /// + /// Adds the plug in aggregation function factory. + /// + /// Type of the aggregation factory. Must implement + /// Name of the function. + public void AddPlugInAggregationFunctionFactory(String functionName) where T : AggregationFunctionFactory + { + AddPlugInAggregationFunctionFactory(functionName, typeof(T).AssemblyQualifiedName); + } + + public void AddPlugInAggregationMultiFunction(ConfigurationPlugInAggregationMultiFunction config) + { + _plugInAggregationMultiFunctions.Add(config); + } + + public void AddPlugInSingleRowFunction(ConfigurationPlugInSingleRowFunction singleRowFunction) + { + _plugInSingleRowFunctions.Add(singleRowFunction); + } + + public void AddPlugInSingleRowFunction(string functionName, string className, string methodName) + { + AddPlugInSingleRowFunction( + functionName, className, methodName, ValueCacheEnum.DISABLED); + } + + public void AddPlugInSingleRowFunction( + string functionName, + string className, + string methodName, + ValueCacheEnum valueCache) + { + AddPlugInSingleRowFunction( + functionName, className, methodName, valueCache, FilterOptimizableEnum.ENABLED); + } + + public void AddPlugInSingleRowFunction( + string functionName, + string className, + string methodName, + FilterOptimizableEnum filterOptimizable) + { + AddPlugInSingleRowFunction( + functionName, className, methodName, ValueCacheEnum.DISABLED, filterOptimizable); + } + + /// + /// Returns transient configuration, i.e. information that is passed along as a reference and not as a value + /// + /// map of transients + public IDictionary TransientConfiguration + { + get { return _transientConfiguration; } + set { this._transientConfiguration = value; } + } + + /// + /// Add single-row function with configurations. + /// + /// EPL name of function + /// providing fully-qualified class name + /// providing method name + /// value cache settings + /// settings whether subject to optimizations + /// thrown to indicate that the configuration is invalid + public void AddPlugInSingleRowFunction( + string functionName, + string className, + string methodName, + ValueCacheEnum valueCache, + FilterOptimizableEnum filterOptimizable) + { + AddPlugInSingleRowFunction(functionName, className, methodName, valueCache, filterOptimizable, false); + } + + /// + /// Add single-row function with configurations. + /// + /// EPL name of function + /// providing fully-qualified class name + /// providing method name + /// value cache settings + /// settings whether subject to optimizations + /// whether exceptions generated by the UDF are rethrown + /// thrown to indicate that the configuration is invalid + public void AddPlugInSingleRowFunction( + string functionName, + string className, + string methodName, + ValueCacheEnum valueCache, + FilterOptimizableEnum filterOptimizable, + bool rethrowExceptions) + { + var entry = new ConfigurationPlugInSingleRowFunction(); + entry.FunctionClassName = className; + entry.FunctionMethodName = methodName; + entry.Name = functionName; + entry.ValueCache = valueCache; + entry.FilterOptimizable = filterOptimizable; + entry.IsRethrowExceptions = rethrowExceptions; + AddPlugInSingleRowFunction(entry); + } + + /// + /// Checks if an event type has already been registered for that name. + /// + /// the name + /// true if already registered + public bool IsEventTypeExists(string eventTypeName) + { + return _eventClasses.ContainsKey(eventTypeName) + || _mapNames.ContainsKey(eventTypeName) + || _nestableMapNames.ContainsKey(eventTypeName) + || _nestableObjectArrayNames.ContainsKey(eventTypeName) + || _eventTypesXmldom.ContainsKey(eventTypeName) + || _eventTypesAvro.ContainsKey(eventTypeName); + //note: no need to check legacy as they get added as class event type + } + + /// + /// Add an name for an event type represented by object events. + /// Note that when adding multiple names for the same class the names represent an + /// alias to the same event type since event type identity for classes is per class. + /// + /// is the name for the event type + /// fully-qualified class name of the event type + public void AddEventType(string eventTypeName, string eventClassName) + { + _eventClasses[eventTypeName] = eventClassName; + } + + /// + /// Add an name for an event type represented by plain-old object events. + /// Note that when adding multiple names for the same class the names represent an + /// alias to the same event type since event type identity for classes is per class. + /// + /// is the name for the event type + /// is the event class for which to add the name + public void AddEventType(string eventTypeName, Type eventClass) + { + AddEventType(eventTypeName, eventClass.AssemblyQualifiedName); + } + + /// + /// Add event type represented by plain-old object events, + /// and the name is the simple class name of the class. + /// + /// is the event class for which to add the name + public void AddEventType(Type eventClass) + { + AddEventType(eventClass.Name, eventClass.AssemblyQualifiedName); + } + + /// + /// Adds a name for an event type represented by the type parameter. + /// + /// + /// Name of the event type. + public void AddEventType(String eventTypeName) + { + AddEventType(eventTypeName, typeof(T).AssemblyQualifiedName); + } + + /// + /// Adds a name for an event type represented by the type parameter. + /// + /// + public void AddEventType() + { + AddEventType(typeof(T).Name, typeof(T).AssemblyQualifiedName); + } + + /// + /// Add an name for an event type that represents IDictionary events. + /// + /// Each entry in the type map is the property name and the fully-qualified + /// class name or primitive type name. + /// + /// + /// is the name for the event type + /// + /// maps the name of each property in the Map event to the type + /// (fully qualified classname) of its value in Map event instances. + /// + public void AddEventType(string eventTypeName, Properties typeMap) + { + _mapNames.Put(eventTypeName, typeMap); + } + + public void AddEventType(string eventTypeName, IDictionary typeMap) + { + _nestableMapNames.Put(eventTypeName, new Dictionary(typeMap)); + } + + public void AddEventType(string eventTypeName, IDictionary typeMap, string[] superTypes) + { + _nestableMapNames.Put(eventTypeName, new Dictionary(typeMap)); + if (superTypes != null) + { + for (int i = 0; i < superTypes.Length; i++) + { + AddMapSuperType(eventTypeName, superTypes[i]); + } + } + } + + public void AddEventType( + string eventTypeName, + IDictionary typeMap, + ConfigurationEventTypeMap mapConfig) + { + _nestableMapNames.Put(eventTypeName, new Dictionary(typeMap)); + _mapTypeConfigurations.Put(eventTypeName, mapConfig); + } + + /// + /// Add, for a given Map event type identified by the first parameter, the supertype (by its event type name). + /// + /// Each Map event type may have any number of supertypes, each supertype must also be of a Map-type event. + /// + /// + /// the name of a Map event type, that is to have a supertype + /// the name of a Map event type that is the supertype + public void AddMapSuperType(string mapeventTypeName, string mapSupertypeName) + { + ConfigurationEventTypeMap current = _mapTypeConfigurations.Get(mapeventTypeName); + if (current == null) + { + current = new ConfigurationEventTypeMap(); + _mapTypeConfigurations.Put(mapeventTypeName, current); + } + ICollection superTypes = current.SuperTypes; + superTypes.Add(mapSupertypeName); + } + + /// + /// Add, for a given Object-array event type identified by the first parameter, the supertype (by its event type name). + /// + /// Each Object array event type may have any number of supertypes, each supertype must also be of a Object-array-type event. + /// + /// + /// the name of a Map event type, that is to have a supertype + /// the name of a Map event type that is the supertype + public void AddObjectArraySuperType(string eventTypeName, string supertypeName) + { + ConfigurationEventTypeObjectArray current = _objectArrayTypeConfigurations.Get(eventTypeName); + if (current == null) + { + current = new ConfigurationEventTypeObjectArray(); + _objectArrayTypeConfigurations.Put(eventTypeName, current); + } + ICollection superTypes = current.SuperTypes; + if (!superTypes.IsEmpty()) + { + throw new ConfigurationException("Object-array event types may not have multiple supertypes"); + } + superTypes.Add(supertypeName); + } + + /// + /// Add configuration for a map event type. + /// + /// configuration to add + /// map type configuration + public void AddMapConfiguration(string mapeventTypeName, ConfigurationEventTypeMap config) + { + _mapTypeConfigurations.Put(mapeventTypeName, config); + } + + /// + /// Add configuration for a object array event type. + /// + /// configuration to add + /// map type configuration + public void AddObjectArrayConfiguration( + string objectArrayeventTypeName, + ConfigurationEventTypeObjectArray config) + { + _objectArrayTypeConfigurations.Put(objectArrayeventTypeName, config); + } + + /// + /// Add an name for an event type that represents org.w3c.dom.Node events. + /// + /// is the name for the event type + /// descriptor containing property and mapping information for XML-DOM events + public void AddEventType(string eventTypeName, ConfigurationEventTypeXMLDOM xmlDOMEventTypeDesc) + { + _eventTypesXmldom.Put(eventTypeName, xmlDOMEventTypeDesc); + } + + public void AddEventType(string eventTypeName, string[] propertyNames, Object[] propertyTypes) + { + var propertyTypesMap = EventTypeUtility.ValidateObjectArrayDef( + propertyNames, propertyTypes); + _nestableObjectArrayNames.Put(eventTypeName, propertyTypesMap); + } + + public void AddEventType( + string eventTypeName, + string[] propertyNames, + Object[] propertyTypes, + ConfigurationEventTypeObjectArray config) + { + var propertyTypesMap = EventTypeUtility.ValidateObjectArrayDef( + propertyNames, propertyTypes); + _nestableObjectArrayNames.Put(eventTypeName, propertyTypesMap); + _objectArrayTypeConfigurations.Put(eventTypeName, config); + if (config.SuperTypes != null && config.SuperTypes.Count > 1) + { + throw new ConfigurationException(ConfigurationEventTypeObjectArray.SINGLE_SUPERTYPE_MSG); + } + } + + public void AddRevisionEventType( + string revisioneventTypeName, + ConfigurationRevisionEventType revisionEventTypeConfig) + { + _revisionEventTypes.Put(revisioneventTypeName, revisionEventTypeConfig); + } + + /// + /// Add a database reference with a given database name. + /// + /// is the database name + /// descriptor containing database connection and access policy information + public void AddDatabaseReference(string name, ConfigurationDBRef configurationDBRef) + { + _databaseReferences.Put(name, configurationDBRef); + } + + public void AddEventType(ConfigurationEventTypeLegacy legacyEventTypeDesc) + { + AddEventType(typeof(T).Name, typeof(T).AssemblyQualifiedName, legacyEventTypeDesc); + } + + public void AddEventType(string eventTypeName, ConfigurationEventTypeLegacy legacyEventTypeDesc) + { + AddEventType(eventTypeName, typeof(T).AssemblyQualifiedName, legacyEventTypeDesc); + } + + /// + /// Add an name for an event type that represents legacy object type events. + /// Note that when adding multiple names for the same class the names represent an + /// alias to the same event type since event type identity for classes is per class. + /// + /// is the name for the event type + /// fully-qualified class name of the event type + /// descriptor containing property and mapping information for legacy type events + public void AddEventType( + string eventTypeName, + string eventClass, + ConfigurationEventTypeLegacy legacyEventTypeDesc) + { + eventClass = TypeHelper.TryResolveAbsoluteTypeName(eventClass); + _eventClasses.Put(eventTypeName, eventClass); + _eventTypesLegacy.Put(eventTypeName, legacyEventTypeDesc); + } + + /// + /// Add a namespace. Adding will suppress the use of the default namespaces. + /// + /// is a fully-qualified class name or a package name with wildcard + /// + /// ConfigurationException if incorrect package or class names are encountered + /// + public void AddImport(string importName) + { + string[] importParts = importName.Split(','); + if (importParts.Length == 1) + { + AddImport(importName, null); + } + else + { + AddImport(importParts[0], importName.Substring(importParts[0].Length + 1).TrimStart()); + } + } + + /// + /// Adds the class or namespace (importName) ot the list of automatically imported types. + /// + /// Name of the import. + /// The assembly name or file. + public void AddImport(String importName, String assemblyNameOrFile) + { + _imports.Add(new AutoImportDesc(importName, assemblyNameOrFile)); + } + + /// + /// Adds an import for a specific type. + /// + /// The auto import. + public void AddImport(Type autoImport) + { + AddImport(autoImport.AssemblyQualifiedName); + } + + /// + /// Adds the import. + /// + /// + public void AddImport() + { + AddImport(typeof(T)); + } + + /// + /// Adds an import for the namespace associated with the given type. + /// + /// + public void AddNamespaceImport() + { + AddImport(typeof(T).Namespace); + } + + /// + /// Adds the annotation import. + /// + /// Name of the import. + /// The assembly name or file. + public void AddAnnotationImport(String importName, String assemblyNameOrFile) + { + _annotationImports.Add(new AutoImportDesc(importName, assemblyNameOrFile)); + } + + /// + /// Adds the annotation import. + /// + /// The automatic import. + /// + public void AddAnnotationImport(string autoImport) + { + string[] importParts = autoImport.Split(','); + if (importParts.Length == 1) + { + AddAnnotationImport(autoImport, null); + } + else + { + AddAnnotationImport(importParts[0], importParts[1]); + } + } + + /// + /// Adds the annotation import. + /// + /// The automatic import. + public void AddAnnotationImport(Type autoImport) + { + AddAnnotationImport(autoImport.FullName, autoImport.Assembly.FullName); + } + + /// + /// Adds the annotation import. + /// + /// + /// + public void AddAnnotationImport(bool importNamespace = false) + { + if (importNamespace) + { + AddAnnotationImport(typeof(T).Namespace, typeof(T).Assembly.FullName); + } + else + { + AddAnnotationImport(typeof(T).FullName, typeof(T).Assembly.FullName); + } + } + + /// + /// Remove an import. + /// + /// + public void RemoveImport(AutoImportDesc autoImportDesc) + { + _imports.Remove(autoImportDesc); + } + + /// + /// Adds a cache configuration for a class providing methods for use in the from-clause. + /// + /// is the class name (simple or fully-qualified) providing methods + /// is the cache configuration + public void AddMethodRef(string className, ConfigurationMethodRef methodInvocationConfig) + { + _methodInvocationReferences.Put(className, methodInvocationConfig); + } + + /// + /// Adds a cache configuration for a class providing methods for use in the from-clause. + /// + /// is the class providing methods + /// is the cache configuration + public void AddMethodRef(Type clazz, ConfigurationMethodRef methodInvocationConfig) + { + _methodInvocationReferences.Put(clazz.Name, methodInvocationConfig); + } + + public IDictionary EventTypeNames + { + get { return _eventClasses; } + } + + public IDictionary EventTypesMapEvents + { + get { return _mapNames; } + } + + public IDictionary> EventTypesNestableMapEvents + { + get { return _nestableMapNames; } + } + + public IDictionary> EventTypesNestableObjectArrayEvents + { + get { return _nestableObjectArrayNames; } + } + + public IDictionary EventTypesXMLDOM + { + get { return _eventTypesXmldom; } + } + + public IDictionary EventTypesAvro + { + get { return _eventTypesAvro; } + } + + public IDictionary EventTypesLegacy + { + get { return _eventTypesLegacy; } + } + + public IList Imports + { + get { return _imports; } + } + + public IList AnnotationImports + { + get { return _annotationImports; } + } + + public IDictionary DatabaseReferences + { + get { return _databaseReferences; } + } + + public IList PlugInViews + { + get { return _plugInViews; } + } + + public IDictionary ObjectArrayTypeConfigurations + { + get { return _objectArrayTypeConfigurations; } + } + + public IList PlugInVirtualDataWindows + { + get { return _plugInVirtualDataWindows; } + } + + public IList PluginLoaders + { + get { return _pluginLoaders; } + } + + public IList PlugInAggregationFunctions + { + get { return _plugInAggregationFunctions; } + } + + public IList PlugInAggregationMultiFunctions + { + get { return _plugInAggregationMultiFunctions; } + } + + public IList PlugInSingleRowFunctions + { + get { return _plugInSingleRowFunctions; } + } + + public IList PlugInPatternObjects + { + get { return _plugInPatternObjects; } + } + + public IDictionary Variables + { + get { return _variables; } + } + + public IDictionary MethodInvocationReferences + { + get { return _methodInvocationReferences; } + } + + public IDictionary RevisionEventTypes + { + get { return _revisionEventTypes; } + } + + public IDictionary MapTypeConfigurations + { + get { return _mapTypeConfigurations; } + } + + /// + /// Add a plugin loader (f.e. an input/output adapter loader). + /// + /// The class is expected to implement . + /// + /// + /// is the name of the loader + /// is the fully-qualified classname of the loader class + /// is loader cofiguration entries + public void AddPluginLoader(string loaderName, string className, Properties configuration) + { + AddPluginLoader(loaderName, className, configuration, null); + } + + /// + /// Add a plugin loader (f.e. an input/output adapter loader) without any additional loader configuration + /// + /// The class is expected to implement . + /// + /// + /// is the name of the loader + /// is the fully-qualified classname of the loader class + public void AddPluginLoader(string loaderName, string className) + { + AddPluginLoader(loaderName, className, null, null); + } + + /// + /// Add a plugin loader (f.e. an input/output adapter loader). + /// + /// The class is expected to implement . + /// + /// + /// is the name of the loader + /// is the fully-qualified classname of the loader class + /// is loader cofiguration entries + /// config xml if any + public void AddPluginLoader( + string loaderName, + string className, + Properties configuration, + string configurationXML) + { + var pluginLoader = new ConfigurationPluginLoader(); + pluginLoader.LoaderName = loaderName; + pluginLoader.TypeName = className; + pluginLoader.ConfigProperties = configuration; + pluginLoader.ConfigurationXML = configurationXML; + _pluginLoaders.Add(pluginLoader); + } + + /// + /// Add a view for plug-in. + /// + /// is the namespace the view should be available under + /// is the name of the view + /// is the view factory class to use + public void AddPlugInView(string @namespace, string name, string viewFactoryClass) + { + var configurationPlugInView = new ConfigurationPlugInView(); + configurationPlugInView.Namespace = @namespace; + configurationPlugInView.Name = name; + configurationPlugInView.FactoryClassName = viewFactoryClass; + _plugInViews.Add(configurationPlugInView); + } + + /// + /// Add a virtual data window for plug-in. + /// + /// is the namespace the virtual data window should be available under + /// is the name of the data window + /// is the view factory class to use + public void AddPlugInVirtualDataWindow(string @namespace, string name, string factoryClass) + { + AddPlugInVirtualDataWindow(@namespace, name, factoryClass, null); + } + + /// + /// Add a virtual data window for plug-in. + /// + /// is the namespace the virtual data window should be available under + /// is the name of the data window + /// is the view factory class to use + /// additional configuration to be passed along + public void AddPlugInVirtualDataWindow( + string @namespace, + string name, + string factoryClass, + object customConfigurationObject) + { + var configurationPlugInVirtualDataWindow = new ConfigurationPlugInVirtualDataWindow(); + configurationPlugInVirtualDataWindow.Namespace = @namespace; + configurationPlugInVirtualDataWindow.Name = name; + configurationPlugInVirtualDataWindow.FactoryClassName = factoryClass; + configurationPlugInVirtualDataWindow.Config = customConfigurationObject; + _plugInVirtualDataWindows.Add(configurationPlugInVirtualDataWindow); + } + + /// + /// Add a pattern event observer for plug-in. + /// + /// is the namespace the observer should be available under + /// is the name of the observer + /// is the observer factory class to use + public void AddPlugInPatternObserver(string @namespace, string name, string observerFactoryClass) + { + var entry = new ConfigurationPlugInPatternObject(); + entry.Namespace = @namespace; + entry.Name = name; + entry.FactoryClassName = observerFactoryClass; + entry.PatternObjectType = ConfigurationPlugInPatternObject.PatternObjectTypeEnum.OBSERVER; + _plugInPatternObjects.Add(entry); + } + + /// + /// Add a pattern guard for plug-in. + /// + /// is the namespace the guard should be available under + /// is the name of the guard + /// is the guard factory class to use + public void AddPlugInPatternGuard(string @namespace, string name, string guardFactoryClass) + { + var entry = new ConfigurationPlugInPatternObject(); + entry.Namespace = @namespace; + entry.Name = name; + entry.FactoryClassName = guardFactoryClass; + entry.PatternObjectType = ConfigurationPlugInPatternObject.PatternObjectTypeEnum.GUARD; + _plugInPatternObjects.Add(entry); + } + + public void AddEventTypeAutoName(string packageName) + { + _eventTypeAutoNamePackages.Add(packageName); + } + + public void AddVariable(string variableName, TValue initializationValue) + { + AddVariable(variableName, typeof(TValue).FullName, initializationValue, false); + } + + public void AddVariable(string variableName, Type type, Object initializationValue) + { + AddVariable(variableName, type.FullName, initializationValue, false); + } + + /// + /// Add variable that can be a constant. + /// + /// name of variable + /// variable type + /// initial value + /// constant indicator + public void AddVariable(string variableName, Type type, Object initializationValue, bool constant) + { + AddVariable(variableName, type.FullName, initializationValue, constant); + } + + public void AddVariable(string variableName, string type, Object initializationValue) + { + AddVariable(variableName, type, initializationValue, false); + } + + public void AddVariable(string variableName, string type, Object initializationValue, bool constant) + { + var configVar = new ConfigurationVariable(); + configVar.VariableType = type; + configVar.InitializationValue = initializationValue; + configVar.IsConstant = constant; + _variables.Put(variableName, configVar); + } + + /// + /// Adds an event representation responsible for creating event types (event metadata) and event bean instances (events) for + /// a certain kind of object representation that holds the event property values. + /// + /// + /// uniquely identifies the event representation and acts as a parent + /// for child Uris used in resolving + /// + /// is the name of the class implementing . + /// is optional configuration or initialization information, or null if none required + public void AddPlugInEventRepresentation( + Uri eventRepresentationRootUri, + string eventRepresentationClassName, + object initializer) + { + var config = new ConfigurationPlugInEventRepresentation(); + config.EventRepresentationTypeName = eventRepresentationClassName; + config.Initializer = initializer; + _plugInEventRepresentation.Put(eventRepresentationRootUri, config); + } + + /// + /// Adds an event representation responsible for creating event types (event metadata) and event bean instances (events) for + /// a certain kind of object representation that holds the event property values. + /// + /// + /// uniquely identifies the event representation and acts as a parent + /// for child Uris used in resolving + /// + /// is the class implementing . + /// is optional configuration or initialization information, or null if none required + public void AddPlugInEventRepresentation( + Uri eventRepresentationRootUri, + Type eventRepresentationClass, + object initializer) + { + AddPlugInEventRepresentation(eventRepresentationRootUri, eventRepresentationClass.Name, initializer); + } + + public void AddPlugInEventType(string eventTypeName, Uri[] resolutionUris, object initializer) + { + var config = new ConfigurationPlugInEventType(); + config.EventRepresentationResolutionURIs = resolutionUris; + config.Initializer = initializer; + _plugInEventTypes.Put(eventTypeName, config); + } + + public IList PlugInEventTypeResolutionURIs + { + get { return _plugInEventTypeResolutionUris; } + set { _plugInEventTypeResolutionUris = value; } + } + + public IDictionary PlugInEventRepresentation + { + get { return _plugInEventRepresentation; } + } + + public IDictionary PlugInEventTypes + { + get { return _plugInEventTypes; } + } + + public ISet EventTypeAutoNamePackages + { + get { return _eventTypeAutoNamePackages; } + } + + public ConfigurationEngineDefaults EngineDefaults + { + get { return _engineDefaults; } + } + + public void AddVariantStream(string varianteventTypeName, ConfigurationVariantStream variantStreamConfig) + { + _variantStreams.Put(varianteventTypeName, variantStreamConfig); + } + + public IDictionary VariantStreams + { + get { return _variantStreams; } + } + + public void UpdateMapEventType(string mapeventTypeName, IDictionary typeMap) + { + throw new UnsupportedOperationException("Map type update is only available in runtime configuration"); + } + + public void UpdateObjectArrayEventType(string myEvent, string[] namesNew, Object[] typesNew) + { + throw new UnsupportedOperationException( + "Object-array type update is only available in runtime configuration"); + } + + public void ReplaceXMLEventType(string xmlEventTypeName, ConfigurationEventTypeXMLDOM config) + { + throw new UnsupportedOperationException("XML type update is only available in runtime configuration"); + } + + public ICollection GetEventTypeNameUsedBy(string name) + { + throw new UnsupportedOperationException("Get event type by name is only available in runtime configuration"); + } + + public bool IsVariantStreamExists(string name) + { + return _variantStreams.ContainsKey(name); + } + + public void SetMetricsReportingInterval(string stmtGroupName, long newInterval) + { + EngineDefaults.MetricsReporting.SetStatementGroupInterval(stmtGroupName, newInterval); + } + + public void SetMetricsReportingStmtEnabled(string statementName) + { + throw new UnsupportedOperationException( + "Statement metric reporting can only be enabled or disabled at runtime"); + } + + public void SetMetricsReportingStmtDisabled(string statementName) + { + throw new UnsupportedOperationException( + "Statement metric reporting can only be enabled or disabled at runtime"); + } + + public EventType GetEventType(string eventTypeName) + { + throw new UnsupportedOperationException("Obtaining an event type by name is only available at runtime"); + } + + public ICollection EventTypes + { + get { throw new UnsupportedOperationException("Obtaining event types is only available at runtime"); } + } + + public void SetMetricsReportingEnabled() + { + EngineDefaults.MetricsReporting.IsEnableMetricsReporting = true; + } + + public void SetMetricsReportingDisabled() + { + EngineDefaults.MetricsReporting.IsEnableMetricsReporting = false; + } + + public long PatternMaxSubexpressions + { + set { EngineDefaults.Patterns.MaxSubexpressions = value; } + } + + public long? MatchRecognizeMaxStates + { + set { EngineDefaults.MatchRecognize.MaxStates = value; } + } + + /// + /// Use the configuration specified in an application + /// resource named esper.cfg.xml. + /// + /// thrown to indicate error reading configuration + /// Configuration initialized from the resource + public Configuration Configure() + { + Configure('/' + ESPER_DEFAULT_CONFIG); + return this; + } + + /// + /// Use the configuration specified in the given application + /// resource. The format of the resource is defined in + /// esper-configuration-2.0.xsd. + /// + /// The resource is found via GetConfigurationInputStream(resource). + /// That method can be overridden to implement an arbitrary lookup strategy. + /// + /// + /// See getResourceAsStream for information on how the resource name is resolved. + /// + /// + /// if the file name of the resource + /// thrown to indicate error reading configuration + /// Configuration initialized from the resource + public Configuration Configure(string resource) + { + if (Log.IsDebugEnabled) + { + Log.Debug("Configuring from resource: " + resource); + } + Stream stream = GetConfigurationInputStream(resource); + ConfigurationParser.DoConfigure(this, stream, resource); + return this; + } + + /// + /// Use the configuration specified by the given URL. + /// The format of the document obtained from the URL is defined in + /// esper-configuration-2.0.xsd. + /// + /// URL from which you wish to load the configuration + /// is thrown when the URL could not be access + /// A configuration configured via the file + public Configuration Configure(Uri url) + { + if (Log.IsDebugEnabled) + { + Log.Debug("configuring from url: " + url); + } + try + { + ConfigurationParser.DoConfigure(this, WebRequest.Create(url).GetResponse().GetResponseStream(), url.ToString()); + return this; + } + catch (IOException ioe) + { + throw new EPException("could not configure from URL: " + url, ioe); + } + } + + /// + /// Use the configuration specified in the given application + /// file. The format of the file is defined in + /// esper-configuration-2.0.xsd. + /// + /// File from which you wish to load the configuration + /// when the file could not be found + /// A configuration configured via the file + public Configuration Configure(FileInfo configFile) + { + if (Log.IsDebugEnabled) + { + Log.Debug("configuring from file: " + configFile.Name); + } + try + { + using (var stream = new FileStream(configFile.FullName, FileMode.Open, FileAccess.Read)) + { + ConfigurationParser.DoConfigure(this, stream, configFile.ToString()); + } + } + catch (FileNotFoundException fnfe) + { + throw new EPException("could not find file: " + configFile, fnfe); + } + return this; + } + + public bool RemoveEventType(string eventTypeName, bool force) + { + _eventClasses.Remove(eventTypeName); + _eventTypesXmldom.Remove(eventTypeName); + _eventTypesAvro.Remove(eventTypeName); + _eventTypesLegacy.Remove(eventTypeName); + _mapNames.Remove(eventTypeName); + _nestableMapNames.Remove(eventTypeName); + _mapTypeConfigurations.Remove(eventTypeName); + _plugInEventTypes.Remove(eventTypeName); + _revisionEventTypes.Remove(eventTypeName); + _variantStreams.Remove(eventTypeName); + return true; + } + + public ICollection GetVariableNameUsedBy(string variableName) + { + throw new UnsupportedOperationException( + "Get variable use information is only available in runtime configuration"); + } + + public bool RemoveVariable(string name, bool force) + { + return _variables.Remove(name); + } + + public void AddEventTypeAvro(string eventTypeName, ConfigurationEventTypeAvro avro) + { + _eventTypesAvro.Put(eventTypeName, avro); + } + + /// + /// Use the mappings and properties specified in the given XML document. + /// The format of the file is defined in + /// esper-configuration-2.0.xsd. + /// + /// an XML document from which you wish to load the configuration + /// if there is problem in accessing the document. + /// A configuration configured via the Document + public Configuration Configure(XmlDocument document) + { + if (Log.IsDebugEnabled) + { + Log.Debug("configuring from XML document"); + } + ConfigurationParser.DoConfigure(this, document); + return this; + } + + /// Reset to an empty configuration. + protected void Reset() + { + _eventClasses = new Dictionary(); + _mapNames = new Dictionary(); + _nestableMapNames = new Dictionary>(); + _nestableObjectArrayNames = new Dictionary>(); + _eventTypesXmldom = new Dictionary(); + _eventTypesAvro = new Dictionary(); + _eventTypesLegacy = new Dictionary(); + _databaseReferences = new Dictionary(); + _imports = new List(); + _annotationImports = new List(); + AddDefaultImports(); + _plugInViews = new List(); + _plugInVirtualDataWindows = new List(); + _pluginLoaders = new List(); + _plugInAggregationFunctions = new List(); + _plugInAggregationMultiFunctions = new List(); + _plugInSingleRowFunctions = new List(); + _plugInPatternObjects = new List(); + _engineDefaults = new ConfigurationEngineDefaults(); + _eventTypeAutoNamePackages = new FIFOHashSet(); + _variables = new Dictionary(); + _methodInvocationReferences = new Dictionary(); + _plugInEventRepresentation = new Dictionary(); + _plugInEventTypes = new Dictionary(); + _revisionEventTypes = new Dictionary(); + _variantStreams = new Dictionary(); + _mapTypeConfigurations = new Dictionary(); + _objectArrayTypeConfigurations = new Dictionary(); + _transientConfiguration = new Dictionary(); + } + + /// Use these imports until the user specifies something else. + private void AddDefaultImports() + { + _imports.Add(new AutoImportDesc("System")); + _imports.Add(new AutoImportDesc("System.Collections")); + _imports.Add(new AutoImportDesc("System.Text")); + _imports.Add(new AutoImportDesc(ANNOTATION_IMPORT)); + _imports.Add(new AutoImportDesc(typeof(BeaconSource).Namespace, (string) null)); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/client/ConfigurationCacheReferenceType.cs b/NEsper.Core/NEsper.Core/client/ConfigurationCacheReferenceType.cs new file mode 100755 index 000000000..cb4b1a606 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/ConfigurationCacheReferenceType.cs @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.client +{ + /// + /// Enum indicating what kind of references are used to store the cache map's keys and values. + /// + public enum ConfigurationCacheReferenceType + { + /// + /// Constant indicating that hard references should be used. + /// + /// Does not allow garbage collection to remove cache entries. + /// + /// + HARD, + + /// + /// Constant indicating that soft references should be used. + /// + /// Allows garbage collection to remove cache entries only after all weak references have been collected. + /// + /// + SOFT, + + /// + /// Constant indicating that weak references should be used. + /// + /// Allows garbage collection to remove cache entries. + /// + /// + WEAK + } + + public class ConfigurationCacheReferenceTypeHelper + { + /// + /// The default policy is set to WEAK to reduce the chance that out-of-memory errors occur + /// as caches fill, and stay backwards compatible with prior Esper releases. + /// + /// + public static ConfigurationCacheReferenceType GetDefault() + { + return ConfigurationCacheReferenceType.WEAK; + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/ConfigurationDBRef.cs b/NEsper.Core/NEsper.Core/client/ConfigurationDBRef.cs new file mode 100755 index 000000000..ae36d8784 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/ConfigurationDBRef.cs @@ -0,0 +1,312 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; +using System.Data; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.util; + +namespace com.espertech.esper.client +{ + /// + /// Container for database configuration information, such as + /// options around getting a database connection and options to control the lifecycle + /// of connections and set connection parameters. + /// + + [Serializable] + public class ConfigurationDBRef + { + private ConnectionFactoryDesc connectionFactoryDesc; + private readonly ConnectionSettings connectionSettings; + private ConnectionLifecycleEnum connectionLifecycleEnum; + private ConfigurationDataCache dataCacheDesc; + private MetadataOriginEnum metadataOrigin; + private ColumnChangeCaseEnum columnChangeCase; + private readonly IDictionary dataTypeMapping; + + /// + /// Gets or sets the auto-commit connection settings for new connections to this database. + /// + + virtual public bool? ConnectionAutoCommit + { + get { return connectionSettings.AutoCommit ; } + set { connectionSettings.AutoCommit = value; } + } + + /// + /// Gets or sets the transaction isolation level on new connections created for this database. + /// + + virtual public IsolationLevel ConnectionTransactionIsolation + { + get { return connectionSettings.TransactionIsolation.GetValueOrDefault(); } + set { connectionSettings.TransactionIsolation = value; } + } + + /// + /// Gets or sets the catalog name for new connections created for this database. + /// + + virtual public String ConnectionCatalog + { + get { return connectionSettings.Catalog; } + set { connectionSettings.Catalog = value; } + } + + /// + /// Gets or sets the LRU cache to a given size for the database. + /// + + virtual public int LRUCache + { + set { dataCacheDesc = new ConfigurationLRUCache(value); } + } + + /// + /// Ctor. + /// + + public ConfigurationDBRef() + { + connectionLifecycleEnum = ConnectionLifecycleEnum.RETAIN; + connectionSettings = new ConnectionSettings(); + metadataOrigin = MetadataOriginEnum.DEFAULT; + columnChangeCase = ColumnChangeCaseEnum.NONE; + dataTypeMapping = new Dictionary(); + } + + /// + /// Sets the database provider connection. + /// + /// Name of the driver. + /// The properties. + + public void SetDatabaseDriver(String driverName, Properties properties) + { + connectionFactoryDesc = new DbDriverFactoryConnection(driverName, properties); + } + + /// + /// Sets the database driver. + /// + /// The db driver factory connection. + public void SetDatabaseDriver( DbDriverFactoryConnection dbDriverFactoryConnection ) + { + connectionFactoryDesc = dbDriverFactoryConnection; + } + + /// + /// Sets the database driver. + /// + /// The db driver factory connection. + /// The properties. + public void SetDatabaseDriver(DbDriverFactoryConnection dbDriverFactoryConnection, Properties properties) + { + connectionFactoryDesc = new DbDriverFactoryConnection( + dbDriverFactoryConnection.Driver.GetType(), + properties); + } + + /// + /// Sets the database driver. + /// + /// The db specification. + public void SetDatabaseDriver(DbDriverConfiguration dbSpecification) + { + connectionFactoryDesc = new DbDriverFactoryConnection(dbSpecification); + } + + /// Returns the connection settings for this database. + /// connection settings + /// + + public virtual ConnectionSettings ConnectionSettings + { + get { return connectionSettings; } + } + + /// + /// Gets or sets the setting to control whether a new connection is obtained + /// for each lookup, or connections are retained between lookups. Controls + /// whether a new connection is obtained for each lookup, or connections + /// are retained between lookups. + /// + + public virtual ConnectionLifecycleEnum ConnectionLifecycle + { + get { return connectionLifecycleEnum; } + set { connectionLifecycleEnum = value; } + } + + /// + /// Gets the descriptor controlling connection creation settings. + /// + + public virtual ConnectionFactoryDesc ConnectionFactoryDesc + { + get { return connectionFactoryDesc; } + set { connectionFactoryDesc = value; } + } + + /// + /// Configures an expiry-time cache of the given maximum age in seconds and purge interval in seconds. + /// + /// Specifies the cache reference type to be weak references. Weak reference cache entries become + /// eligible for garbage collection and are removed from cache when the garbage collection requires so. + /// + /// + /// is the maximum number of seconds before a query result is considered stale (also known as time-to-live) + /// + /// is the interval at which the engine purges stale data from the cache + /// + + public virtual void SetExpiryTimeCache(double maxAgeSeconds, double purgeIntervalSeconds) + { + dataCacheDesc = new ConfigurationExpiryTimeCache(maxAgeSeconds, purgeIntervalSeconds, ConfigurationCacheReferenceTypeHelper.GetDefault()); + } + + /// + /// Configures an expiry-time cache of the given maximum age in seconds and purge interval in seconds. Also allows + /// setting the reference type indicating whether garbage collection may remove entries from cache. + /// + /// the maximum number of seconds before a query result is considered stale (also known as time-to-live) + /// the interval at which the engine purges stale data from the cache. + /// specifies the reference type to use + public virtual void SetExpiryTimeCache(double maxAgeSeconds, double purgeIntervalSeconds, ConfigurationCacheReferenceType cacheReferenceType) + { + dataCacheDesc = new ConfigurationExpiryTimeCache(maxAgeSeconds, purgeIntervalSeconds, cacheReferenceType); + } + + /// + /// Gets a query result data cache descriptor. + /// + public virtual ConfigurationDataCache DataCacheDesc + { + get { return dataCacheDesc; } + } + + /// + /// Adds the SQL types binding. + /// + /// Type of the SQL. + /// The desired type. + public void AddSqlTypeBinding(Type sqlType, Type desiredType) + { + DatabaseTypeEnum typeEnum = DatabaseTypeEnum.GetEnum(desiredType.FullName); + if (typeEnum == null) + { + String supported = DatabaseTypeEnum.Values.Render(); + throw new ConfigurationException("Unsupported type '" + desiredType.FullName + "' when expecting any of: " + supported); + } + this.dataTypeMapping[sqlType] = desiredType; + } + + /// + /// Returns the mapping of types that the engine must perform + /// when receiving output columns of that sql types. + /// + public IDictionary DataTypeMapping + { + get { return dataTypeMapping; } + } + + /// + /// Returns an enumeration indicating how the engine retrieves metadata about the columns + /// that a given SQL query returns. + /// + /// The engine requires to retrieve result column names and types in order to build a resulting + /// event type and perform expression type checking. + /// + /// indication how to retrieve metadata + public MetadataOriginEnum MetadataRetrievalEnum + { + get { return metadataOrigin; } + } + + /// + /// Gets and sets an indicator that indicates how the engine should retrieve + /// metadata about the columns that a given SQL query returns. + /// + /// The engine requires to retrieve result column names and types in order to build a resulting + /// event type and perform expression type checking. + /// + /// + public MetadataOriginEnum MetadataOrigin + { + get { return metadataOrigin; } + set { metadataOrigin = value; } + } + + /// + /// Gets or sets an enum value determining how the engine changes case + /// on output column names returned from statement or statement result + /// set metadata. + /// + /// change case enums + public ColumnChangeCaseEnum ColumnChangeCase + { + get { return columnChangeCase; } + set { columnChangeCase = value; } + } + + /// + /// Indicates how the engine retrieves metadata about a statement's output columns. + /// + [Serializable] + public enum MetadataOriginEnum + { + /// + /// By default, get output column metadata from the prepared statement., unless + /// an Oracle connection class is used in which case the behavior is SAMPLE. + /// + DEFAULT, + + /// + /// Always get output column metadata from the prepared statement regardless of what driver + /// or connection is used. + /// + METADATA, + + /// + /// Obtain output column metadata by executing a sample query statement at statement + /// compilation time. The sample statement + /// returns the same columns as the statement executed during event processing. + /// See the documentation for the generation or specication of the sample query statement. + /// + SAMPLE + } + + /// + /// Controls how output column names get reflected in the event type. + /// + [Serializable] + public enum ColumnChangeCaseEnum + { + /// + /// Leave the column names the way the database driver represents the column. + /// + NONE, + + /// + /// Change case to lowercase on any column names returned by statement metadata. + /// + LOWERCASE, + + /// + /// Change case to uppercase on any column names returned by statement metadata. + /// + UPPERCASE + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/ConfigurationDataCache.cs b/NEsper.Core/NEsper.Core/client/ConfigurationDataCache.cs new file mode 100755 index 000000000..08140266e --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/ConfigurationDataCache.cs @@ -0,0 +1,18 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +namespace com.espertech.esper.client +{ + /// Marker interface for different cache settings. + public interface ConfigurationDataCache + { + + } + +} diff --git a/NEsper.Core/NEsper.Core/client/ConfigurationEngineDefaults.cs b/NEsper.Core/NEsper.Core/client/ConfigurationEngineDefaults.cs new file mode 100755 index 000000000..c9a4ab299 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/ConfigurationEngineDefaults.cs @@ -0,0 +1,1150 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client.hook; +using com.espertech.esper.client.soda; +using com.espertech.esper.client.util; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.client +{ + /// + /// Provides access to engine configuration defaults for modification. + /// + [Serializable] + public class ConfigurationEngineDefaults + { + /// Ctor. + public ConfigurationEngineDefaults() + { + Threading = new ThreadingConfig(); + ViewResources = new ViewResourcesConfig(); + EventMeta = new EventMetaConfig(); + Logging = new LoggingConfig(); + Variables = new VariablesConfig(); + StreamSelection = new StreamSelectionConfig(); + TimeSource = new TimeSourceConfig(); + MetricsReporting = new MetricsReportingConfig(); + Language = new LanguageConfig(); + Expression = new ExpressionConfig(); + Execution = new ExecutionConfig(); + ExceptionHandling = new ExceptionHandlingConfig(); + ConditionHandling = new ConditionHandlingConfig(); + AlternativeContext = new AlternativeContextConfig(); + Patterns = new PatternsConfig(); + MatchRecognize = new MatchRecognizeConfig(); + Scripts = new ScriptsConfig(); + } + + /// + /// Returns threading settings. + /// + /// threading settings object + public ThreadingConfig Threading { get; private set; } + + /// + /// Returns view resources defaults. + /// + /// view resources defaults + public ViewResourcesConfig ViewResources { get; private set; } + + /// + /// Returns event representation default settings. + /// + /// event representation default settings + public EventMetaConfig EventMeta { get; private set; } + + /// + /// Returns logging settings applicable to the engine, other then Log4J settings. + /// + /// logging settings + public LoggingConfig Logging { get; private set; } + + /// + /// Returns engine defaults applicable to variables. + /// + /// variable engine defaults + public VariablesConfig Variables { get; private set; } + + /// + /// Returns engine defaults applicable to streams (insert and remove, insert only or remove only) selected for a statement. + /// + /// stream selection defaults + public StreamSelectionConfig StreamSelection { get; private set; } + + /// + /// Returns the time source configuration. + /// + /// time source enum + public TimeSourceConfig TimeSource { get; private set; } + + /// + /// Returns the metrics reporting configuration. + /// + /// metrics reporting config + public MetricsReportingConfig MetricsReporting { get; private set; } + + /// + /// Returns the language-related settings for the engine. + /// + /// language-related settings + public LanguageConfig Language { get; private set; } + + /// + /// Returns the expression-related settings for the engine. + /// + /// expression-related settings + public ExpressionConfig Expression { get; private set; } + + /// + /// Returns statement execution-related settings, settings that + /// influence event/schedule to statement processing. + /// + /// execution settings + public ExecutionConfig Execution { get; private set; } + + /// + /// For software-provider-interface use. + /// + /// alternative context + public AlternativeContextConfig AlternativeContext { get; set; } + + /// + /// Returns the exception handling configuration. + /// + /// exception handling configuration + public ExceptionHandlingConfig ExceptionHandling { get; set; } + + /// + /// Returns the condition handling configuration. + /// + /// condition handling configuration + public ConditionHandlingConfig ConditionHandling { get; set; } + + /// + /// Return pattern settings. + /// + /// pattern settings + public PatternsConfig Patterns { get; set; } + + /// + /// Return match-recognize settings. + /// + /// match-recognize settings + public MatchRecognizeConfig MatchRecognize { get; set; } + + /// + /// Returns script engine settings. + /// + /// script engine settings + public ScriptsConfig Scripts { get; set; } + + /// Threading profile. + public enum ThreadingProfile + { + /// + /// Large for use with 100 threads or more. Please see the documentation for more information. + /// + LARGE, + + /// For use with 100 threads or less. + NORMAL + } + + /// Filter service profile. + public enum FilterServiceProfile + { + /// If filters are mostly static, the default. + READMOSTLY, + + /// + /// For very dynamic filters that come and go in a highly threaded environment. + /// + READWRITE + } + + /// TimeInMillis source type. + public enum TimeSourceType + { + /// + /// Millisecond time source type with time originating from System.currentTimeMillis + /// + MILLI, + + /// + /// Nanosecond time source from a wallclock-adjusted System.nanoTime + /// + NANO + } + + /// Interface for cluster configurator. + public interface ClusterConfigurator + { + /// + /// Provide cluster configuration information. + /// + /// information + void Configure(Configuration configuration); + } + + /// Holds threading settings. + [Serializable] + public class ThreadingConfig + { + /// Ctor - sets up defaults. + public ThreadingConfig() + { + ListenerDispatchTimeout = 1000; + IsListenerDispatchPreserveOrder = true; + ListenerDispatchLocking = Locking.SPIN; + + InsertIntoDispatchTimeout = 100; + IsInsertIntoDispatchPreserveOrder = true; + InsertIntoDispatchLocking = Locking.SPIN; + + NamedWindowConsumerDispatchTimeout = long.MaxValue; + IsNamedWindowConsumerDispatchPreserveOrder = true; + NamedWindowConsumerDispatchLocking = Locking.SPIN; + + IsInternalTimerEnabled = true; + InternalTimerMsecResolution = 100; + + IsThreadPoolInbound = false; + IsThreadPoolOutbound = false; + IsThreadPoolRouteExec = false; + IsThreadPoolTimerExec = false; + + ThreadPoolTimerExecNumThreads = 2; + ThreadPoolInboundNumThreads = 2; + ThreadPoolRouteExecNumThreads = 2; + ThreadPoolOutboundNumThreads = 2; + + ThreadPoolInboundBlocking = Locking.SUSPEND; + ThreadPoolOutboundBlocking = Locking.SUSPEND; + ThreadPoolRouteExecBlocking = Locking.SUSPEND; + ThreadPoolTimerExecBlocking = Locking.SUSPEND; + } + + /// + /// Returns true to indicate preserve order for dispatch to listeners, + /// or false to indicate not to preserve order + /// + /// true or false + public bool IsListenerDispatchPreserveOrder { get; set; } + + /// + /// Returns the timeout in millisecond to wait for listener code to complete + /// before dispatching the next result, if dispatch order is preserved + /// + /// listener dispatch timeout + public long ListenerDispatchTimeout { get; set; } + + /// + /// Returns true to indicate preserve order for inter-statement insert-into, + /// or false to indicate not to preserve order + /// + /// true or false + public bool IsInsertIntoDispatchPreserveOrder { get; set; } + + /// + /// Returns true if internal timer is enabled (the default), or false for internal timer disabled. + /// + /// + /// true for internal timer enabled, false for internal timer disabled + /// + public bool IsInternalTimerEnabled { get; set; } + + /// + /// Returns the millisecond resolutuion of the internal timer thread. + /// + /// number of msec between timer processing intervals + public long InternalTimerMsecResolution { get; set; } + + /// + /// Returns the number of milliseconds that a thread may maximually be blocking + /// to deliver statement results from a producing statement that employs insert-into + /// to a consuming statement. + /// + /// + /// millisecond timeout for order-of-delivery blocking between statements + /// + public long InsertIntoDispatchTimeout { get; set; } + + /// + /// Returns the blocking strategy to use when multiple threads deliver results for + /// a single statement to listeners, and the guarantee of order of delivery must be maintained. + /// + /// is the blocking technique + public Locking ListenerDispatchLocking { get; set; } + + /// + /// Returns the blocking strategy to use when multiple threads deliver results for + /// a single statement to consuming statements of an insert-into, and the guarantee of order of delivery must be maintained. + /// + /// is the blocking technique + public Locking InsertIntoDispatchLocking { get; set; } + + /// + /// Returns true for inbound threading enabled, the default is false for not enabled. + /// + /// indicator whether inbound threading is enabled + public bool IsThreadPoolInbound { get; set; } + + /// + /// Returns true for timer execution threading enabled, the default is false for not enabled. + /// + /// indicator whether timer execution threading is enabled + public bool IsThreadPoolTimerExec { get; set; } + + /// + /// Returns true for route execution threading enabled, the default is false for not enabled. + /// + /// indicator whether route execution threading is enabled + public bool IsThreadPoolRouteExec { get; set; } + + /// + /// Returns true for outbound threading enabled, the default is false for not enabled. + /// + /// indicator whether outbound threading is enabled + public bool IsThreadPoolOutbound { get; set; } + + /// + /// Returns the number of thread in the inbound threading pool. + /// + /// number of threads + public int ThreadPoolInboundNumThreads { get; set; } + + /// + /// Returns the number of thread in the outbound threading pool. + /// + /// number of threads + public int ThreadPoolOutboundNumThreads { get; set; } + + /// + /// Returns the number of thread in the route execution thread pool. + /// + /// number of threads + public int ThreadPoolRouteExecNumThreads { get; set; } + + /// + /// Returns the number of thread in the timer execution threading pool. + /// + /// number of threads + public int ThreadPoolTimerExecNumThreads { get; set; } + + /// + /// Returns the capacity of the timer execution queue, or null if none defined (the unbounded case, default). + /// + /// capacity or null if none defined + public int? ThreadPoolTimerExecCapacity { get; set; } + + /// + /// Returns the capacity of the inbound execution queue, or null if none defined (the unbounded case, default). + /// + /// capacity or null if none defined + public int? ThreadPoolInboundCapacity { get; set; } + + /// + /// Returns the capacity of the route execution queue, or null if none defined (the unbounded case, default). + /// + /// capacity or null if none defined + public int? ThreadPoolRouteExecCapacity { get; set; } + + /// + /// Returns the capacity of the outbound queue, or null if none defined (the unbounded case, default). + /// + /// capacity or null if none defined + public int? ThreadPoolOutboundCapacity { get; set; } + + public Locking ThreadPoolInboundBlocking { get; set; } + public Locking ThreadPoolOutboundBlocking { get; set; } + public Locking ThreadPoolTimerExecBlocking { get; set; } + public Locking ThreadPoolRouteExecBlocking { get; set; } + + /// + /// Returns true if the engine-level lock is configured as a fair lock (default is false). + /// + /// This lock coordinates + /// event processing threads (threads that send events) with threads that + /// perform administrative functions (threads that start or destroy statements, for example). + /// + /// + /// true for fair lock + public bool IsEngineFairlock { get; set; } + + /// + /// In multithreaded environments, this setting controls whether named window dispatches to named window consumers preserve + /// the order of events inserted and removed such that statements that consume a named windows delta stream + /// behave deterministic (true by default). + /// + /// flag + public bool IsNamedWindowConsumerDispatchPreserveOrder { get; set; } + + /// + /// Returns the timeout millisecond value for named window dispatches to named window consumers. + /// + /// timeout milliseconds + public long NamedWindowConsumerDispatchTimeout { get; set; } + + /// + /// Returns the locking strategy value for named window dispatches to named window consumers (default is spin). + /// + /// strategy + public Locking NamedWindowConsumerDispatchLocking { get; set; } + + /// Enumeration of blocking techniques. + public enum Locking + { + /// + /// Spin lock blocking is good for locks held very shortly or generally uncontended locks and + /// is therefore the default. + /// + SPIN, + + /// + /// Blocking that suspends a thread and notifies a thread to wake up can be + /// more expensive then spin locks. + /// + SUSPEND + } + } + + /// Holds view resources settings. + [Serializable] + public class ViewResourcesConfig + { + /// Ctor - sets up defaults. + public ViewResourcesConfig() + { + IsShareViews = true; + IsAllowMultipleExpiryPolicies = false; + IsIterableUnbound = false; + } + + /// + /// Gets or set to true to indicate the engine shares view resources between statements, or false + /// to indicate the engine does not share view resources between statements. + /// + /// + /// indicator whether view resources are shared between statements if + /// statements share same-views and the engine sees opportunity to reuse an existing view. + /// + public bool IsShareViews { get; set; } + + /// + /// By default this setting is false and thereby multiple expiry policies + /// provided by views can only be combined if any of the retain-keywords is also specified for the stream. + /// + /// If set to true then multiple expiry policies are allowed and the following statement compiles without exception: + /// "select * from MyEvent#TimeInMillis(10)#TimeInMillis(10)". + /// + /// + /// + /// allowMultipleExpiryPolicies indicator whether to allow combining expiry policies provided by views + /// + public bool IsAllowMultipleExpiryPolicies { get; set; } + + /// + /// Returns flag to indicate whether engine-wide unbound statements are iterable and return the last event. + /// + /// indicator + public bool IsIterableUnbound { get; set; } + } + + /// Event representation metadata. + [Serializable] + public class EventMetaConfig + { + /// Ctor. + public EventMetaConfig() + { + AnonymousCacheSize = 5; + ClassPropertyResolutionStyle = PropertyResolutionStyle.DEFAULT; + DefaultAccessorStyle = AccessorStyleEnum.NATIVE; + DefaultEventRepresentation = EventUnderlyingTypeExtensions.GetDefault(); + AvroSettings = new AvroSettings(); + } + + /// + /// Returns the default accessor style, native unless changed. + /// + /// style enum + public AccessorStyleEnum DefaultAccessorStyle { get; set; } + + /// + /// Returns the property resolution style to use for resolving property names + /// of classes. + /// + /// style of property resolution + public PropertyResolutionStyle ClassPropertyResolutionStyle { get; set; } + + /// + /// Returns the default event representation. + /// + /// setting + public EventUnderlyingType DefaultEventRepresentation { get; set; } + + /// + /// Returns the cache size for anonymous event types. + /// + /// cache size + public int AnonymousCacheSize { get; set; } + + /// + /// Returns the Avro settings. + /// + /// avro settings + public AvroSettings AvroSettings { get; set; } + } + + /// Avro settings. + [Serializable] + public class AvroSettings + { + public AvroSettings() + { + IsEnableSchemaDefaultNonNull = true; + IsEnableNativeString = true; + IsEnableAvro = true; + } + + /// + /// Returns the indicator whether Avro support is enabled when available (true by default). + /// + /// indicator + public bool IsEnableAvro { get; set; } + + /// + /// Returns indicator whether for string-type values to use the "avro.string=string" (true by default) + /// + /// indicator + public bool IsEnableNativeString { get; set; } + + /// + /// Returns indicator whether generated schemas should assume non-null values (true by default) + /// + /// indicator + public bool IsEnableSchemaDefaultNonNull { get; set; } + + /// + /// Returns class name of mapping provider that maps types to an Avro schema; a mapper should implement + /// (null by default, using default mapping) + /// + /// class name + public string TypeRepresentationMapperClass { get; set; } + + /// + /// Returns the class name of widening provider that widens, coerces or transforms object values to an Avro field value or record; a widener should implement + /// (null by default, using default widening) + /// + /// class name + public string ObjectValueTypeWidenerFactoryClass { get; set; } + } + + /// + /// Holds view logging settings other then the Apache commons or Log4 settings. + /// + [Serializable] + public class LoggingConfig + { + /// Ctor - sets up defaults. + public LoggingConfig() + { + IsEnableExecutionDebug = false; + IsEnableTimerDebug = true; + IsEnableQueryPlan = false; + IsEnableADO = false; + } + + /// + /// Returns true if execution path debug logging is enabled. + /// + /// Only if this flag is set to true, in addition to LOG4 settings set to DEBUG, does an engine instance, + /// produce debug out. + /// + /// + /// + /// true if debug logging through Log4 is enabled for any event processing execution paths + /// + public bool IsEnableExecutionDebug { get; set; } + + /// + /// Returns true if timer debug level logging is enabled (true by default). + /// + /// Set this value to false to reduce the debug-level logging output for the timer Thread(s). + /// For use only when debug-level logging is enabled. + /// + /// + /// indicator whether timer execution is noisy in debug or not + public bool IsEnableTimerDebug { get; set; } + + /// + /// Returns indicator whether query plan logging is enabled or not. + /// + /// indicator + public bool IsEnableQueryPlan { get; set; } + + /// + /// Returns an indicator whether ADO query reporting is enabled. + /// + /// indicator + public bool IsEnableADO { get; set; } + + /// + /// Returns the pattern that formats audit logs. + /// + /// Available conversion characters are: + /// + /// + /// %m - Used to output the audit message. + /// %s - Used to output the statement name. + /// %u - Used to output the engine URI. + /// + /// + /// audit formatting pattern + public string AuditPattern { get; set; } + } + + /// Holds variables settings. + [Serializable] + public class VariablesConfig + { + /// Ctor - sets up defaults. + public VariablesConfig() + { + MsecVersionRelease = 15000; + } + + /// + /// Returns the number of milliseconds that a version of a variables is held stable for + /// use by very long-running atomic statement execution. + /// + /// A slow-executing statement such as an SQL join may use variables that, at the time + /// the statement starts to execute, have certain values. The engine guarantees that during + /// statement execution the value of the variables stays the same as long as the statement + /// does not take longer then the given number of milliseconds to execute. If the statement does take longer + /// to execute then the variables release time, the current variables value applies instead. + /// + /// + /// + /// millisecond time interval that a variables version is guaranteed to be stable + /// in the context of an atomic statement execution + /// + public long MsecVersionRelease { get; set; } + } + + /// Holder for script settings. + [Serializable] + public class ScriptsConfig + { + public ScriptsConfig() + { + DefaultDialect = "jscript"; + } + + /// + /// Returns the default script dialect. + /// + /// dialect + public string DefaultDialect { get; set; } + } + + /// Holds pattern settings. + [Serializable] + public class PatternsConfig + { + public PatternsConfig() + { + IsMaxSubexpressionPreventStart = true; + } + + /// + /// Returns the maximum number of subexpressions + /// + /// subexpression count + public long? MaxSubexpressions { get; set; } + + /// + /// Returns true, the default, to indicate that if there is a maximum defined + /// it is being enforced and new subexpressions are not allowed. + /// + /// indicate whether enforced or not + public bool IsMaxSubexpressionPreventStart { get; set; } + } + + /// Holds match-reconize settings. + [Serializable] + public class MatchRecognizeConfig + { + public MatchRecognizeConfig() + { + IsMaxStatesPreventStart = true; + } + + /// + /// Returns the maximum number of states + /// + /// state count + public long? MaxStates { get; set; } + + /// + /// Returns true, the default, to indicate that if there is a maximum defined + /// it is being enforced and new states are not allowed. + /// + /// indicate whether enforced or not + public bool IsMaxStatesPreventStart { get; set; } + } + + /// + /// Holds default settings for stream selection in the select-clause. + /// + [Serializable] + public class StreamSelectionConfig + { + /// Ctor - sets up defaults. + public StreamSelectionConfig() + { + DefaultStreamSelector = StreamSelector.ISTREAM_ONLY; + } + + /// + /// Returns the default stream selector. + /// + /// Statements that select data from streams and that do not use one of the explicit stream + /// selection keywords (istream/rstream/irstream), by default, + /// generate selection results for the insert stream only, and not for the remove stream. + /// + /// + /// This setting can be used to change the default behavior: Use the RSTREAM_ISTREAM_BOTH + /// value to have your statements generate both insert and remove stream results + /// without the use of the "irstream" keyword in the select clause. + /// + /// + /// + /// default stream selector, which is ISTREAM_ONLY unless changed + /// + public StreamSelector DefaultStreamSelector { get; set; } + } + + /// + /// TimeInMillis source configuration, the default in MILLI (millisecond resolution from System.currentTimeMillis). + /// + [Serializable] + public class TimeSourceConfig + { + /// Ctor. + public TimeSourceConfig() + { + TimeUnit = TimeUnit.MILLISECONDS; + TimeSourceType = TimeSourceType.MILLI; + } + + /// + /// Returns the time source type. + /// + /// time source type enum + public TimeSourceType TimeSourceType { get; set; } + + /// + /// Returns the time unit time resolution level of time tracking + /// + /// time resolution + public TimeUnit TimeUnit { get; set; } + } + + /// Language settings in the engine are for string comparisons. + [Serializable] + public class LanguageConfig + { + /// Ctor. + public LanguageConfig() + { + IsSortUsingCollator = false; + } + + /// + /// Returns true to indicate to perform locale-independent string comparisons using Collator. + /// + /// By default this setting is false, i.e. string comparisons use the compare method. + /// + /// + /// indicator whether to use Collator for string comparisons + public bool IsSortUsingCollator { get; set; } + } + + /// + /// Expression evaluation settings in the engine are for results of expressions. + /// + [Serializable] + public class ExpressionConfig + { + /// Ctor. + public ExpressionConfig() + { + IsIntegerDivision = false; + IsDivisionByZeroReturnsNull = false; + IsUdfCache = true; + IsSelfSubselectPreeval = true; + IsExtendedAggregation = true; + TimeZone = TimeZoneInfo.Local; + } + + /// + /// Returns false (the default) for integer division returning double values. + /// + /// Returns true to signal that Java-convention integer division semantics + /// are used for divisions, whereas the division between two non-FP numbers + /// returns only the whole number part of the result and any fractional part is dropped. + /// + /// + /// indicator + public bool IsIntegerDivision { get; set; } + + /// + /// Returns false (default) when division by zero returns double?.Infinity. + /// Returns true when division by zero return null. + /// + /// If integer devision is set, then division by zero for non-FP operands also returns null. + /// + /// + /// indicator for division-by-zero results + public bool IsDivisionByZeroReturnsNull { get; set; } + + /// + /// By default true, indicates that user-defined functions cache return results + /// if the parameter set is empty or has constant-only return values. + /// + /// cache flag + public bool IsUdfCache { get; set; } + + /// + /// Set to true (the default) to indicate that sub-selects within a statement are updated first when a new + /// event arrives. This is only relevant for statements in which both subselects + /// and the from-clause may react to the same exact event. + /// + /// + /// indicator whether to evaluate sub-selects first or last on new event arrival + /// + public bool IsSelfSubselectPreeval { get; set; } + + /// + /// Enables or disables non-SQL standard builtin aggregation functions. + /// + /// indicator + public bool IsExtendedAggregation { get; set; } + + /// + /// Returns true to indicate that duck typing is enable for the specific syntax where it is allowed (check the documentation). + /// + /// indicator + public bool IsDuckTyping { get; set; } + + /// + /// Returns the math context for big decimal operations, or null to leave the math context undefined. + /// + /// math context or null + public MathContext MathContext { get; set; } + + /// + /// Returns the time zone for calendar operations. + /// + /// time zone + public TimeZoneInfo TimeZone { get; set; } + } + + /// Holds engine execution-related settings. + [Serializable] + public class ExecutionConfig + { + /// Ctor - sets up defaults. + public ExecutionConfig() + { + ThreadingProfile = ThreadingProfile.NORMAL; + FilterServiceProfile = FilterServiceProfile.READMOSTLY; + FilterServiceMaxFilterWidth = 16; + DeclaredExprValueCacheSize = 1; + IsPrioritized = false; + } + + /// + /// Returns false (the default) if the engine does not consider statement priority and preemptive instructions, + /// or true to enable priority-based statement execution order. + /// + /// + /// false by default to indicate unprioritized statement execution + /// + public bool IsPrioritized { get; set; } + + /// + /// Returns true for fair locking, false for unfair locks. + /// + /// fairness flag + public bool IsFairlock { get; set; } + + /// + /// Returns indicator whether statement-level locks are disabled. + /// The default is false meaning statement-level locks are taken by default and depending on EPL optimizations. + /// If set to true statement-level locks are never taken. + /// + /// indicator for statement-level locks + public bool IsDisableLocking { get; set; } + + /// + /// Returns the threading profile + /// + /// profile + public ThreadingProfile ThreadingProfile { get; set; } + + /// + /// Returns indicator whether isolated services providers are enabled or disabled (the default). + /// + /// indicator value + public bool IsAllowIsolatedService { get; set; } + + /// + /// Returns the filter service profile for tuning filtering operations. + /// + /// filter service profile + public FilterServiceProfile FilterServiceProfile { get; set; } + + /// + /// Returns the maximum width for breaking up "or" expression in filters to + /// subexpressions for reverse indexing. + /// + /// max filter width + public int FilterServiceMaxFilterWidth { get; set; } + + /// + /// Returns the cache size for declared expression values + /// + /// value + public int DeclaredExprValueCacheSize { get; set; } + } + + /// + /// Returns the provider for runtime and administrative interfaces. + /// + [Serializable] + public class AlternativeContextConfig + { + /// + /// Type name of runtime provider. + /// + /// provider class + public string Runtime { get; set; } + + /// + /// Type name of admin provider. + /// + /// provider class + public string Admin { get; set; } + + /// + /// Returns the class name of the event type id generator. + /// + /// class name + public string EventTypeIdGeneratorFactory { get; set; } + + /// + /// Returns the class name of the virtual data window view factory. + /// + /// factory class name + public string VirtualDataWindowViewFactory { get; set; } + + /// + /// Sets the class name of the statement metadata factory. + /// + /// factory class name + public string StatementMetadataFactory { get; set; } + + /// + /// Returns the application-provided configurarion object carried as part of the configurations. + /// + /// config user object + public object UserConfiguration { get; set; } + + /// + /// Returns the member name. + /// + /// member name + public string MemberName { get; set; } + } + + /// + /// Configuration object for defining exception handling behavior. + /// + [Serializable] + public class ExceptionHandlingConfig + { + private List _handlerFactories; + + public ExceptionHandlingConfig() + { + UndeployRethrowPolicy = UndeployRethrowPolicy.WARN; + } + + /// + /// Returns the list of exception handler factory class names, + /// see + /// + /// list of fully-qualified class names + public IList HandlerFactories + { + get { return _handlerFactories; } + } + + /// + /// Add an exception handler factory class name. + /// + /// Provide a fully-qualified class name of the implementation + /// of the + /// interface. + /// + /// + /// class name of exception handler factory + public void AddClass(string exceptionHandlerFactoryClassName) + { + if (_handlerFactories == null) + { + _handlerFactories = new List(); + } + _handlerFactories.Add(exceptionHandlerFactoryClassName); + } + + /// + /// Add a list of exception handler class names. + /// + /// to add + public void AddClasses(IEnumerable classNames) + { + if (_handlerFactories == null) + { + _handlerFactories = new List(); + } + _handlerFactories.AddAll(classNames); + } + + /// + /// Add an exception handler factory class. + /// + /// The class provided should implement the + /// + /// interface. + /// + /// + /// class of implementation + public void AddClass(Type exceptionHandlerFactoryClass) + { + AddClass(exceptionHandlerFactoryClass.AssemblyQualifiedName); + } + + /// + /// Add an exception handler factory class. + /// + /// + public void AddClass() where T : ExceptionHandlerFactory + { + AddClass(typeof(T).AssemblyQualifiedName); + } + + /// + /// Returns the policy to instruct the engine whether a module un-deploy rethrows runtime exceptions that are encountered + /// during the undeploy for any statement that is undeployed. By default we are logging exceptions. + /// + /// indicator + public UndeployRethrowPolicy UndeployRethrowPolicy { get; set; } + } + + /// Enumeration of blocking techniques. + public enum UndeployRethrowPolicy + { + /// Warn. + WARN, + + /// Rethrow First Encountered Exception. + RETHROW_FIRST + } + + /// + /// Configuration object for defining condition handling behavior. + /// + [Serializable] + public class ConditionHandlingConfig + { + private List _handlerFactories; + + /// + /// Returns the list of condition handler factory class names, + /// see + /// + /// list of fully-qualified class names + public IList HandlerFactories + { + get { return _handlerFactories; } + } + + /// + /// Add an condition handler factory class name. + /// + /// Provide a fully-qualified class name of the implementation + /// of the + /// interface. + /// + /// + /// class name of condition handler factory + public void AddClass(string className) + { + if (_handlerFactories == null) + { + _handlerFactories = new List(); + } + _handlerFactories.Add(className); + } + + /// + /// Add a list of condition handler class names. + /// + /// to add + public void AddClasses(IEnumerable classNames) + { + if (_handlerFactories == null) + { + _handlerFactories = new List(); + } + _handlerFactories.AddAll(classNames); + } + + /// + /// Add a condition handler factory class. + /// + /// The class provided should implement the + /// + /// interface. + /// + /// + /// class of implementation + public void AddClass(Type clazz) + { + AddClass(clazz.AssemblyQualifiedName); + } + + /// + /// Add a condition handler factory class. + /// + /// + public void AddClass() where T : ConditionHandlerFactory + { + AddClass(typeof(T).AssemblyQualifiedName); + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/client/ConfigurationEventTypeAvro.cs b/NEsper.Core/NEsper.Core/client/ConfigurationEventTypeAvro.cs new file mode 100755 index 000000000..068283630 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/ConfigurationEventTypeAvro.cs @@ -0,0 +1,80 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.util; + +namespace com.espertech.esper.client +{ + /// Configuration for Avro event types. + [Serializable] + public class ConfigurationEventTypeAvro + : ConfigurationEventTypeWithSupertype, + MetaDefItem + { + private string _avroSchemaText; + private Object _avroSchema; + + /// Ctor. + public ConfigurationEventTypeAvro() + { + } + + /// + /// Ctor. + /// + /// avro schema + public ConfigurationEventTypeAvro(Object avroSchema) + { + _avroSchema = avroSchema; + } + + /// + /// Returns the avro schema + /// + /// avro schema + public object AvroSchema + { + get { return _avroSchema; } + set { _avroSchema = value; } + } + + /// + /// Sets the avro schema + /// + /// avro schema + /// this + public ConfigurationEventTypeAvro SetAvroSchema(Object avroSchema) + { + _avroSchema = avroSchema; + return this; + } + + /// + /// Returns the avro schema text + /// + /// avro schema text + public string AvroSchemaText + { + get { return _avroSchemaText; } + set { _avroSchemaText = value; } + } + + /// + /// Returns the avro schema text + /// + /// avro schema text + /// this + public ConfigurationEventTypeAvro SetAvroSchemaText(string avroSchemaText) + { + _avroSchemaText = avroSchemaText; + return this; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/client/ConfigurationEventTypeLegacy.cs b/NEsper.Core/NEsper.Core/client/ConfigurationEventTypeLegacy.cs new file mode 100755 index 000000000..bd4cb392b --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/ConfigurationEventTypeLegacy.cs @@ -0,0 +1,200 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.client +{ + /// + /// Configuration information for legacy event types. + /// + + [Serializable] + public class ConfigurationEventTypeLegacy + { + /// + /// Ctor. + /// + + public ConfigurationEventTypeLegacy() + { + AccessorStyle = AccessorStyleEnum.NATIVE; + CodeGeneration = CodeGenerationEnum.ENABLED; + PropertyResolutionStyle = PropertyResolutionStyleHelper.DefaultPropertyResolutionStyle; + + FieldProperties = new List(); + MethodProperties = new List(); + } + + /// + /// Gets or sets the accessor style. + /// + /// The accessor style. + public AccessorStyleEnum AccessorStyle { get; set; } + + /// + /// Gets or sets the code generation. Thus controls whether or + /// not the engine generates code for access to event property values. + /// + /// The code generation. + public CodeGenerationEnum CodeGeneration { get; set; } + + /// + /// Gets or sets the property resolution style. + /// + /// The property resolution style. + public PropertyResolutionStyle PropertyResolutionStyle { get; set; } + + /// + /// Returns a list of descriptors specifying explicitly configured method names + /// and their property name. + /// + /// list of explicit method-access descriptors + /// + public IList MethodProperties { get; private set; } + + /// Returns a list of descriptors specifying explicitly configured field names + /// and their property name. + /// + /// list of explicit field-access descriptors + /// + public IList FieldProperties { get; private set; } + + /// + /// Gets or sets the start name of the timestamp property. + /// + /// The start name of the timestamp property. + public String StartTimestampPropertyName { get; set; } + + /// + /// Gets or sets the end name of the timestamp property. + /// + /// The end name of the timestamp property. + public String EndTimestampPropertyName { get; set; } + + /// + /// Adds the named event property backed by the named accessor method. + /// The accessor method is expected to be a public method with no parameters + /// for simple event properties, or with a single integer parameter for indexed + /// event properties, or with a single String parameter for mapped event properties. + /// + /// is the event property name + /// is the accessor method name. + + public virtual void AddMethodProperty(String name, String accessorMethod) + { + MethodProperties.Add(new LegacyMethodPropDesc(name, accessorMethod)); + } + + /// + /// Adds the named event property backed by the named accessor field. + /// + /// is the event property name + /// is the accessor field underlying the name + + public virtual void AddFieldProperty(String name, String accessorField) + { + FieldProperties.Add(new LegacyFieldPropDesc(name, accessorField)); + } + + /// + /// Gets or sets the the name of the factory method, either fully-qualified or just + /// a method name if the method is on the same class as the configured class, to use + /// when instantiating objects of the type. + /// + public string FactoryMethod { get; set; } + + /// + /// Gets or sets the method name of the method to use to copy the underlying event object. + /// + public string CopyMethod { get; set; } + + /// + /// Encapsulates information about an accessor field backing a named event property. + /// + + [Serializable] + public class LegacyFieldPropDesc + { + /// Returns the event property name. + /// event property name + /// + public string Name { get; private set; } + + /// Returns the accessor field name. + /// accessor field name + /// + public string AccessorFieldName { get; private set; } + + /// Ctor. + /// is the event property name + /// + /// is the accessor field name + /// + public LegacyFieldPropDesc(String name, String accessorFieldName) + { + Name = name; + AccessorFieldName = accessorFieldName; + } + } + + /// + /// Encapsulates information about an accessor method backing a named event property. + /// + + [Serializable] + public class LegacyMethodPropDesc + { + /// Returns the event property name. + /// event property name + /// + public string Name { get; private set; } + + /// Returns the accessor method name. + /// accessor method name + /// + public string AccessorMethodName { get; private set; } + + /// Ctor. + /// is the event method name + /// + /// is the accessor method name + /// + public LegacyMethodPropDesc(String name, String accessorMethodName) + { + Name = name; + AccessorMethodName = accessorMethodName; + } + } + } + + + /// + /// Accessor style defines the methods of a class that are automatically exposed via event property. + /// + + public enum AccessorStyleEnum + { + /// Expose properties only, plus explicitly configured properties. + NATIVE, + /// Expose only the explicitly configured properties and public members as event properties. + EXPLICIT, + /// Expose all public properties and public members as event properties, plus explicitly configured properties. + PUBLIC + } + + /// Enum to control code generation. + public enum CodeGenerationEnum + { + /// Enables code generation. + ENABLED, + /// Dispables code generation. + DISABLED + } +} diff --git a/NEsper.Core/NEsper.Core/client/ConfigurationEventTypeMap.cs b/NEsper.Core/NEsper.Core/client/ConfigurationEventTypeMap.cs new file mode 100755 index 000000000..48adfacf3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/ConfigurationEventTypeMap.cs @@ -0,0 +1,21 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client +{ + /// + /// Configuration object for Map-based event types. + /// + [Serializable] + public class ConfigurationEventTypeMap + : ConfigurationEventTypeWithSupertype + { + } +} diff --git a/NEsper.Core/NEsper.Core/client/ConfigurationEventTypeObjectArray.cs b/NEsper.Core/NEsper.Core/client/ConfigurationEventTypeObjectArray.cs new file mode 100755 index 000000000..40d842cd4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/ConfigurationEventTypeObjectArray.cs @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.client +{ + /// + /// Configuration object for Object array-based event types. + /// + [Serializable] + public class ConfigurationEventTypeObjectArray : ConfigurationEventTypeWithSupertype + { + /// + /// Message for single supertype for object-arrays. + /// + public const string SINGLE_SUPERTYPE_MSG = "Object-array event types only allow a single supertype"; + + /// Ctor. + /// super types + public ConfigurationEventTypeObjectArray(ICollection superTypes) + : base(superTypes) + { + if (superTypes.Count > 1) + { + throw new ConfigurationException("Object-array event types may not have multiple supertypes"); + } + } + + /// Ctor. + public ConfigurationEventTypeObjectArray() + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/ConfigurationEventTypeWithSupertype.cs b/NEsper.Core/NEsper.Core/client/ConfigurationEventTypeWithSupertype.cs new file mode 100755 index 000000000..f61c38ab5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/ConfigurationEventTypeWithSupertype.cs @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.util; + +namespace com.espertech.esper.client +{ + /// + /// Configuration object for event types with super-types and timestamp. + /// + [Serializable] + public class ConfigurationEventTypeWithSupertype : MetaDefItem + { + /// Ctor. + /// super types + protected ConfigurationEventTypeWithSupertype(ICollection superTypes) + { + SuperTypes = new LinkedHashSet(superTypes); + } + + /// Ctor. + public ConfigurationEventTypeWithSupertype() + { + SuperTypes = new LinkedHashSet(); + } + + /// Returns the super types, if any. + /// set of super type names + public ICollection SuperTypes { get; set; } + + /// Returns the property name of the property providing the start timestamp value. + /// start timestamp property name + public string StartTimestampPropertyName { get; set; } + + /// Returns the property name of the property providing the end timestamp value. + /// end timestamp property name + public string EndTimestampPropertyName { get; set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/ConfigurationEventTypeXMLDOM.cs b/NEsper.Core/NEsper.Core/client/ConfigurationEventTypeXMLDOM.cs new file mode 100755 index 000000000..5897042db --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/ConfigurationEventTypeXMLDOM.cs @@ -0,0 +1,406 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Xml.XPath; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.util; + +namespace com.espertech.esper.client +{ + /// + /// Configuration object for enabling the engine to process events represented as XML DOM document nodes. + /// + /// Use this class to configure the engine for processing of XML DOM objects that represent events + /// and contain all the data for event properties used by statements. + /// + /// Minimally required is the root element name which allows the engine to map the document + /// to the event type that has been named in an EPL or pattern statement. + /// + /// Event properties that are results of XPath expressions can be made known to the engine via this class. + /// For XPath expressions that must refer to namespace prefixes those prefixes and their + /// namespace name must be supplied to the engine. A default namespace can be supplied as well. + /// + /// By supplying a schema resource the engine can interrogate the schema, allowing the engine to + /// verify event properties and return event properties in the type defined by the schema. + /// When a schema resource is supplied, the optional root element namespace defines the namespace in case the + /// root element name occurs in multiple namespaces. + /// + + [Serializable] + public class ConfigurationEventTypeXMLDOM : MetaDefItem + { + // Root element namespace. + // Used to find root element in schema. Useful and required in the case where the root element exists in + // multiple namespaces. + + // Default name space. + // For XPath expression evaluation. + + private readonly IDictionary _namespacePrefixes; + + /// Gets or sets the root element name. + public string RootElementName { get; set; } + + /// Gets or sets the root element namespace. + public string RootElementNamespace { get; set; } + + /// Gets or sets the default namespace. + public string DefaultNamespace { get; set; } + + /// + /// Gets or sets the schema resource. + /// + public string SchemaResource { get; set; } + + /// + /// Gets or sets the schema text. If provided instead of a schema resource, this + /// returns the actual text of the schema document. + /// + /// Set a schema text first. This will not resolve the schema resource to a text. + /// + /// The schema text. + public string SchemaText { get; set; } + + /// + /// Indicates whether or not that property expressions are evaluated by the DOM-walker implementation + /// (the default), or true to indicate that property expressions are rewritten into XPath expressions. + /// + public bool IsXPathPropertyExpr { get; set; } + + /// + /// When set to true (the default), indicates that when properties are compiled to XPath expressions that the + /// compilation should generate an absolute XPath expression such as "/getQuote/request" for the simple request + /// property, or "/getQuote/request/symbol" for a "request.symbol" nested property, wherein the root element + /// node is "getQuote". + /// + /// When set to false, indicates that when properties are compiled to XPath expressions that the compilation + /// should generate a deep XPath expression such as "//symbol" for the simple symbol property, or "//request/symbol" + /// for a "request.symbol" nested property. + /// + /// + /// true if this instance is X path resolve properties absolute; otherwise, false. + /// + public bool IsXPathResolvePropertiesAbsolute { get; set; } + + /// + /// Gets or sets a flag that indicates that an returned for this event type + /// validates the root document element name against the one configured (the default), or false + /// to not validate the root document element name as configured. + /// + /// + /// true if this instance is event sender validates root; otherwise, false. + /// + public bool IsEventSenderValidatesRoot { get; set; } + + /// + /// Set to true (the default) to look up or create event types representing fragments of an XML + /// document automatically upon request for fragment event type information; Or false when only + /// explicit properties may return fragments. + /// + public bool IsAutoFragment { get; set; } + + /// + /// Indicator for use with EsperHA, false by default to indicate that stored type + /// information takes Precedence over configuration type information provided at + /// engine initialization time. Set to true to indicate that configuration type + /// information takes Precedence over stored type information. + /// + /// When setting this flag to true care should be taken about the compatibility + /// of the supplied XML type configuration information and the existing EPL + /// statements and stored events, if any. For more information please consult + /// . + /// + /// set to false (the default) to indicate that stored type information takes Precedence over configuration type information + public bool IsUpdateStoredType { get; set; } + + /// + /// Gets or sets the property name of the property providing the start timestamp value. + /// + /// The start name of the timestamp property. + public string StartTimestampPropertyName { get; set; } + + /// + /// Gets or sets the property name of the property providing the end timestamp value. + /// + /// The end name of the timestamp property. + public string EndTimestampPropertyName { get; set; } + + /// + /// Ctor. + /// + + public ConfigurationEventTypeXMLDOM() + { + XPathProperties = new Dictionary(); + _namespacePrefixes = new Dictionary(); + IsXPathResolvePropertiesAbsolute = true; + IsXPathPropertyExpr = false; + IsEventSenderValidatesRoot = true; + IsAutoFragment = true; + } + + /// Returns a map of property name and descriptor for XPath-expression properties. + /// XPath property information + /// + public IDictionary XPathProperties { get; private set; } + + /// + /// Adds an event property for which the engine uses the supplied XPath expression against + /// a DOM document node to resolve a property value. + /// + /// name of the event property + /// is an arbitrary xpath expression + /// is the return type of the expression + + public void AddXPathProperty(String name, String xpath, XPathResultType type) + { + XPathPropertyDesc desc = new XPathPropertyDesc(name, xpath, type); + XPathProperties[name] = desc; + } + + /// + /// Adds an event property for which the engine uses the supplied XPath expression againsta DOM document node to resolve a property value. + /// + /// the event property + /// an arbitrary xpath expression + /// a constant obtained from System.Xml.XPath.XPathResultType. + /// is the type name of the type that the return value of the xpath expression is casted to + public void AddXPathProperty(String name, String xpath, XPathResultType type, String castToType) + { + Type castToTypeClass = null; + + if (castToType != null) + { + bool isArray = false; + if (castToType.Trim().EndsWith("[]")) + { + isArray = true; + castToType = castToType.Replace("[]", ""); + } + + castToTypeClass = TypeHelper.GetTypeForSimpleName(castToType); + if (castToTypeClass == null) + { + throw new ConfigurationException("Invalid cast-to type for xpath expression named '" + name + "', the type is not recognized"); + } + + if (isArray) + { + castToTypeClass = Array.CreateInstance(castToTypeClass, 0).GetType(); + } + } + + XPathPropertyDesc desc = new XPathPropertyDesc(name, xpath, type, castToTypeClass); + XPathProperties.Put(name, desc); + } + + /// Adds an event property for which the engine uses the supplied XPath expression against a DOM document node to resolve a property value. + /// of the event property + /// is an arbitrary xpath expression + /// is a constant obtained from XPathResultType. Typical values are XPathResultType.NodeSet. + /// is the name of another event type that represents the XPath nodes + public void AddXPathPropertyFragment(String name, String xpath, XPathResultType type, String eventTypeName) + { + if ((type != XPathResultType.Any) && (type != XPathResultType.NodeSet)) + { + throw new ArgumentException("XPath property for fragments requires an Node or Nodeset return value for property '" + name + "'"); + } + + XPathPropertyDesc desc = new XPathPropertyDesc(name, xpath, type, eventTypeName); + XPathProperties.Put(name, desc); + } + + /// Returns the namespace prefixes in a map of prefix as key and namespace name as value. + /// namespace prefixes + /// + + public IDictionary NamespacePrefixes + { + get { return _namespacePrefixes; } + } + + /// Add a prefix and namespace name for use in XPath expressions refering to that prefix. + /// is the prefix of the namespace + /// + /// is the namespace name + + public void AddNamespacePrefix(String prefix, String @namespace) + { + _namespacePrefixes[prefix] = @namespace; + } + + + /// Add prefixes and namespace names for use in XPath expressions refering to that prefix. + /// map of prefixes and namespaces + public void AddNamespacePrefixes(IDictionary prefixNamespaceMap) + { + _namespacePrefixes.PutAll(prefixNamespaceMap); + } + + /// Gets or sets the type name of the XPath function resolver to be assigned to the XPath factory instanceupon type initialization. + /// class name of xpath function resolver, or null if none set + public string XPathFunctionResolver { get; set; } + + /// Gets or sets the class name of the XPath variable resolver to be assigned to the XPath factory instanceupon type initialization. + /// class name of xpath function resolver, or null if none set + public string XPathVariableResolver { get; set; } + + public override bool Equals(Object otherObj) + { + ConfigurationEventTypeXMLDOM other = otherObj as ConfigurationEventTypeXMLDOM; + if (other == null) + { + return false; + } + + if (other.RootElementName != RootElementName) + { + return false; + } + + if (((other.RootElementNamespace == null) && (RootElementNamespace != null)) || + ((other.RootElementNamespace != null) && (RootElementNamespace == null))) + { + return false; + } + return RootElementNamespace == other.RootElementNamespace; + } + + /// + /// Serves as a hash function for a particular type. is suitable for use in hashing algorithms and data structures like a hash table. + /// + /// + /// A hash code for the current . + /// + public override int GetHashCode() + { + return RootElementName.GetHashCode(); + } + + /// + /// Descriptor class for event properties that are resolved via XPath-expression. + /// + + [Serializable] + public class XPathPropertyDesc + { + /// Returns the event property name. + /// event property name + /// + public string Name { get; private set; } + + /// Returns the XPath expression. + /// XPath expression + /// + public string XPath { get; private set; } + + /// Returns the representing the event property type. + /// type infomation + /// + public XPathResultType ResultType { get; private set; } + + /// + /// Returns the native data type representing the event property. + /// + public Type ResultDataType { get; private set; } + + + /// + /// Gets the optional cast to type. + /// + /// The type of the optional cast to. + public Type OptionalCastToType { get; private set; } + + /// + /// Gets the name of the optional event type. + /// + /// The name of the optional event type. + public string OptionalEventTypeName { get; private set; } + + /// Ctor. + /// the event property name + /// an arbitrary XPath expression + /// an XPathConstants constant + + public XPathPropertyDesc(String name, String xpath, XPathResultType type) + : this(name, xpath, type, (Type)null) + { + } + + /// Ctor. + /// the event property name + /// an arbitrary XPath expression + /// a System.Xml.XPath.XPathResultType constant + /// if non-null then the return value of the xpath expression is cast to this value + public XPathPropertyDesc(String name, String xpath, XPathResultType type, Type optionalCastToType) + { + Name = name; + XPath = xpath; + ResultType = type; + OptionalCastToType = optionalCastToType; + ResultDataType = typeof(string); + + switch (type) + { + case XPathResultType.Boolean: + ResultDataType = typeof(bool); + break; + case XPathResultType.String: + ResultDataType = typeof(string); + break; + case XPathResultType.Number: + ResultDataType = typeof(double); + break; + } + } + + /// Ctor. + /// is the event property name + /// is an arbitrary XPath expression + /// is a javax.xml.xpath.XPathConstants constant + /// the name of an event type that represents the fragmented property value + public XPathPropertyDesc(String name, String xpath, XPathResultType type, String eventTypeName) + { + Name = name; + XPath = xpath; + ResultType = type; + OptionalEventTypeName = eventTypeName; + ResultDataType = typeof(string); + + switch (type) + { + case XPathResultType.Boolean: + ResultDataType = typeof(bool); + break; + case XPathResultType.String: + ResultDataType = typeof(string); + break; + case XPathResultType.Number: + ResultDataType = typeof(double); + break; + } + } + + /// + /// Serves as a hash function for a particular type. + /// + /// + /// A hash code for the current . + /// + public override int GetHashCode() + { + return + Name.GetHashCode() * 31 + + XPath.GetHashCode(); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/ConfigurationException.cs b/NEsper.Core/NEsper.Core/client/ConfigurationException.cs new file mode 100755 index 000000000..42c3373b6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/ConfigurationException.cs @@ -0,0 +1,43 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client +{ + /// Thrown to indicate a configuration problem. + [Serializable] + public sealed class ConfigurationException : EPException + { + /// Ctor. + /// error message + /// + public ConfigurationException(String message) + : base(message) + { + } + + /// Ctor for an inner exception and message. + /// error message + /// + /// inner exception + /// + public ConfigurationException(String message, Exception cause) + : base(message, cause) + { + } + + /// Ctor - just an inner exception. + /// inner exception + /// + public ConfigurationException(Exception cause) + : base(cause) + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/ConfigurationExpiryTimeCache.cs b/NEsper.Core/NEsper.Core/client/ConfigurationExpiryTimeCache.cs new file mode 100755 index 000000000..190add843 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/ConfigurationExpiryTimeCache.cs @@ -0,0 +1,71 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client +{ + /// Expiring cache settings. + [Serializable] + public class ConfigurationExpiryTimeCache : ConfigurationDataCache + { + private ConfigurationCacheReferenceType cacheReferenceType; + private double maxAgeSeconds; + private double purgeIntervalSeconds; + + /// Ctor. + /// is the maximum age in seconds + /// is the purge interval + /// the reference type may allow garbage collection to remove entries fromcache unless HARD reference type indicates otherwise + public ConfigurationExpiryTimeCache(double maxAgeSeconds, double purgeIntervalSeconds, ConfigurationCacheReferenceType cacheReferenceType) + { + this.maxAgeSeconds = maxAgeSeconds; + this.purgeIntervalSeconds = purgeIntervalSeconds; + this.cacheReferenceType = cacheReferenceType; + } + + + /// + /// Gets the type of the cache reference. + /// + /// The type of the cache reference. + public ConfigurationCacheReferenceType CacheReferenceType + { + get { return cacheReferenceType; } + } + + /// + /// Gets the max age in seconds. + /// + /// The max age seconds. + public double MaxAgeSeconds + { + get { return maxAgeSeconds; } + } + + /// + /// Gets the purge interval in seconds. + /// + /// The purge interval seconds. + public double PurgeIntervalSeconds + { + get { return purgeIntervalSeconds; } + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override String ToString() + { + return "ExpiryTimeCacheDesc maxAgeSeconds=" + maxAgeSeconds + " purgeIntervalSeconds=" + purgeIntervalSeconds; + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/ConfigurationInformation.cs b/NEsper.Core/NEsper.Core/client/ConfigurationInformation.cs new file mode 100755 index 000000000..8b603a791 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/ConfigurationInformation.cs @@ -0,0 +1,227 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.compat; + +namespace com.espertech.esper.client +{ + /// Provides configurations for an engine instance. + public interface ConfigurationInformation + { + /// + /// Returns the service context factory class name + /// + /// class name + string EPServicesContextFactoryClassName { get; } + + /// + /// Returns the mapping of event type name to class name. + /// + /// event type names for Java class names + IDictionary EventTypeNames { get; } + + /// + /// Returns a map keyed by event type name, and values being the definition for the + /// Map event type of the property names and types that make up the event. + /// + /// map of event type name and definition of event properties + IDictionary EventTypesMapEvents { get; } + + /// + /// Returns a map keyed by event type name, and values being the definition for the + /// event type of the property names and types that make up the event, + /// for nestable, strongly-typed Map-based event representations. + /// + /// map of event type name and definition of event properties + IDictionary> EventTypesNestableMapEvents { get; } + + /// + /// Returns the mapping of event type name to XML DOM event type information. + /// + /// event type name mapping to XML DOM configs + IDictionary EventTypesXMLDOM { get; } + + /// + /// Returns the Avro event types. + /// + /// Avro event types + IDictionary EventTypesAvro { get; } + + /// + /// Returns the mapping of event type name to legacy java event type information. + /// + /// event type name mapping to legacy java class configs + IDictionary EventTypesLegacy { get; } + + /// + /// Returns the class and package imports. + /// + /// imported names + IList Imports { get; } + + /// + /// Returns the class and package imports for annotation-only use. + /// + /// imported names + IList AnnotationImports { get; } + + /// + /// Returns a map of string database names to database configuration options. + /// + /// map of database configurations + IDictionary DatabaseReferences { get; } + + /// + /// Returns a list of configured plug-in views. + /// + /// list of plug-in view configs + IList PlugInViews { get; } + + /// + /// Returns a list of configured plug-in virtual data windows. + /// + /// list of plug-in virtual data windows + IList PlugInVirtualDataWindows { get; } + + /// + /// Returns a list of configured plugin loaders. + /// + /// adapter loaders + IList PluginLoaders { get; } + + /// + /// Returns a list of configured plug-in aggregation functions. + /// + /// list of configured aggregations + IList PlugInAggregationFunctions { get; } + + /// + /// Returns a list of configured plug-in multi-function aggregation functions. + /// + /// list of configured multi-function aggregations + IList PlugInAggregationMultiFunctions { get; } + + /// + /// Returns a list of configured plug-in single-row functions. + /// + /// list of configured single-row functions + IList PlugInSingleRowFunctions { get; } + + /// + /// Returns a list of configured plug-ins for pattern observers and guards. + /// + /// list of pattern plug-ins + IList PlugInPatternObjects { get; } + + /// + /// Returns engine default settings. + /// + /// engine defaults + ConfigurationEngineDefaults EngineDefaults { get; } + + /// + /// Returns the global variables by name as key and type plus initialization value as value + /// + /// map of variable name and variable configuration + IDictionary Variables { get; } + + /// + /// Returns a map of class name and cache configurations, for use in + /// method invocations in the from-clause of methods provided by the class. + /// + /// + /// map of fully-qualified or simple class name and cache configuration + /// + IDictionary MethodInvocationReferences { get; } + + /// + /// Returns a set of namespace names that event classes reside in. + /// + /// This setting allows an application to place all it's events into one or more namespaces + /// and then declare these packages via this method. The engine + /// attempts to resolve an event type name to a class residing in each declared package. + /// + /// + /// For example, in the statement "select * from MyEvent" the engine attempts to load class "javaPackageName.MyEvent" + /// and if successful, uses that class as the event type. + /// + /// + /// + /// set of namespaces to look for events types when encountering a new event type name + /// + ISet EventTypeAutoNamePackages { get; } + + /// + /// Returns a map of plug-in event representation URI and their event representation class and initializer. + /// + /// map of URI keys and event representation configuration + IDictionary PlugInEventRepresentation { get; } + + /// + /// Returns a map of event type name of those event types that will be supplied by a plug-in event representation, + /// and their configuration. + /// + /// map of names to plug-in event type config + IDictionary PlugInEventTypes { get; } + + /// + /// Returns the URIs that point to plug-in event representations that are given a chance to dynamically resolve an event + /// type name to an event type, when a new (unseen) event type name occurs in a new EPL statement. + /// + /// The order of the URIs matters as event representations are asked in turn, to accept the name. + /// + /// + /// URIs can be child URIs of plug-in event representations and can add additional parameters or fragments + /// for use by the event representation. + /// + /// + /// URIs for resolving an event type name + IList PlugInEventTypeResolutionURIs { get; } + + /// + /// Returns a map of revision event type name and revision event type configuration. Revision event types handle updates (new versions) + /// for past events. + /// + /// map of name and revision event type config + IDictionary RevisionEventTypes { get; } + + /// + /// Returns a map of variant stream name and variant configuration information. Variant streams allows handling + /// events of all sorts of different event types the same way. + /// + /// map of name and variant stream config + IDictionary VariantStreams { get; } + + /// + /// Returns for each Map event type name the set of supertype event type names (Map types only). + /// + /// map of name to set of supertype names + IDictionary MapTypeConfigurations { get; } + + /// + /// Returns the object-array event type configurations. + /// + /// type configs + IDictionary ObjectArrayTypeConfigurations { get; } + + /// + /// Returns the object-array event types. + /// + /// object-array event types + IDictionary> EventTypesNestableObjectArrayEvents { get; } + + /// + /// Returns the transient configuration, which are configuration values that are passed by reference (and not by value) + /// + /// transient configuration + IDictionary TransientConfiguration { get; } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/client/ConfigurationLRUCache.cs b/NEsper.Core/NEsper.Core/client/ConfigurationLRUCache.cs new file mode 100755 index 000000000..8cde41ce3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/ConfigurationLRUCache.cs @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client +{ + /// LRU cache settings. + [Serializable] + public class ConfigurationLRUCache : ConfigurationDataCache + { + /// Ctor. + /// is the maximum cache size + public ConfigurationLRUCache(int size) + { + Size = size; + } + + /// + /// Gets the maximum cache size. + /// + /// The size. + public int Size { get; private set; } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override String ToString() + { + return "LRUCacheDesc size=" + Size; + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/ConfigurationMethodRef.cs b/NEsper.Core/NEsper.Core/client/ConfigurationMethodRef.cs new file mode 100755 index 000000000..bb6ef0855 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/ConfigurationMethodRef.cs @@ -0,0 +1,60 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client +{ + /// + /// Holds configuration information for data caches for use in method invocations in the from-clause. + /// + [Serializable] + public class ConfigurationMethodRef + { + /// + /// Return a method invocation result data cache descriptor. + /// + /// cache descriptor + public ConfigurationDataCache DataCacheDesc { get; private set; } + + /// + /// Configures a LRU cache of the given size for the method invocation. + /// + /// is the maximum number of entries before method invocation results are evicted + public void SetLRUCache(int size) + { + DataCacheDesc = new ConfigurationLRUCache(size); + } + + /// + /// Configures an expiry-time cache of the given maximum age in seconds and purge interval in seconds. + /// + /// Specifies the cache reference type to be weak references. Weak reference cache entries become + /// eligible for garbage collection and are removed from cache when the garbage collection requires so. + /// + /// + /// is the maximum number of seconds before a method invocation result is considered stale (also known as time-to-live) + /// is the interval at which the engine purges stale data from the cache + public void SetExpiryTimeCache(double maxAgeSeconds, double purgeIntervalSeconds) + { + DataCacheDesc = new ConfigurationExpiryTimeCache(maxAgeSeconds, purgeIntervalSeconds, ConfigurationCacheReferenceTypeHelper.GetDefault()); + } + + /// + /// Configures an expiry-time cache of the given maximum age in seconds and purge interval in seconds. Also allows + /// setting the reference type indicating whether garbage collection may remove entries from cache. + /// + /// is the maximum number of seconds before a method invocation result is considered stale (also known as time-to-live) + /// is the interval at which the engine purges stale data from the cache + /// specifies the reference type to use + public void SetExpiryTimeCache(double maxAgeSeconds, double purgeIntervalSeconds, ConfigurationCacheReferenceType configurationCacheReferenceType) + { + DataCacheDesc = new ConfigurationExpiryTimeCache(maxAgeSeconds, purgeIntervalSeconds, configurationCacheReferenceType); + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/client/ConfigurationOperations.cs b/NEsper.Core/NEsper.Core/client/ConfigurationOperations.cs new file mode 100755 index 000000000..27f95cd20 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/ConfigurationOperations.cs @@ -0,0 +1,852 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.compat; + +namespace com.espertech.esper.client +{ + /// + /// Provides configuration operations for configuration-time and runtime parameters. + /// + public interface ConfigurationOperations + { + /// + /// Returns an array of event types tracked or available within the engine in any order. Included are all + /// application-configured or EPL-created schema types + /// as well as dynamically-allocated stream's event types or types otherwise known to the engine as a dependeny type or + /// supertype to another type. + /// + /// Event types that are associated to statement output may not necessarily be returned as such types, + /// depending on the statement, are considered anonymous. + /// + /// + /// This operation is not available for static configuration and is only available for runtime use. + /// + /// + /// event type array + ICollection EventTypes { get; } + + /// + /// Adds a namespace that event classes reside in. + /// + /// This setting allows an application to place all it's events into one or more namespaces + /// and then declare these namespaces via this method. The engine attempts to resolve an event + /// type name to a type residing in each declared namespace. + /// + /// + /// For example, in the statement "select * from MyEvent" the engine attempts to load class + /// "namespace.MyEvent" + /// and if successful, uses that class as the event type. + /// + /// + /// is the fully-qualified namespace name of the namespace that event classes reside in + void AddEventTypeAutoName(string @namespace); + + /// + /// Adds a plug-in aggregation multi-function. + /// + /// the configuration + /// is thrown to indicate a configuration problem + void AddPlugInAggregationMultiFunction(ConfigurationPlugInAggregationMultiFunction config); + + /// + /// Adds a plug-in aggregation function given a EPL function name and an aggregation factory class name. + /// + /// The same function name cannot be added twice. + /// + /// + /// is the new aggregation function name for use in EPL + /// + /// is the fully-qualified class name of the class implementing the aggregation + /// function factory interface + /// + /// is thrown to indicate a problem adding the aggregation function + void AddPlugInAggregationFunctionFactory(string functionName, string aggregationFactoryClassName); + + /// + /// Adds a plug-in single-row function given a EPL function name, a class name, method name and setting for value-cache + /// behavior. + /// + /// The same function name cannot be added twice. + /// + /// + /// is the new single-row function name for use in EPL + /// is the fully-qualified class name of the class implementing the single-row function + /// is the static method provided by the class that : the single-row function + /// set the behavior for caching the return value when constant parameters are provided + /// is thrown to indicate a problem adding the single-row function + void AddPlugInSingleRowFunction( + string functionName, + string className, + string methodName, + ValueCacheEnum valueCache); + + /// + /// Adds a plug-in single-row function given a EPL function name, a class name, method name and setting for value-cache + /// behavior. + /// + /// The same function name cannot be added twice. + /// + /// + /// is the new single-row function name for use in EPL + /// is the fully-qualified class name of the class implementing the single-row function + /// is the static method provided by the class that : the single-row function + /// + /// whether the single-row function, when used in filters, may be subject to reverse index + /// lookup based on the function result + /// + /// is thrown to indicate a problem adding the single-row function + void AddPlugInSingleRowFunction( + string functionName, + string className, + string methodName, + FilterOptimizableEnum filterOptimizable); + + /// + /// Adds a plug-in single-row function given a EPL function name, a class name and a method name. + /// + /// The same function name cannot be added twice. + /// + /// + /// is the new single-row function name for use in EPL + /// is the fully-qualified class name of the class implementing the single-row function + /// is the static method provided by the class that : the single-row function + /// is thrown to indicate a problem adding the single-row function + void AddPlugInSingleRowFunction(string functionName, string className, string methodName); + + /// + /// Adds a plug-in single-row function given a EPL function name, a class name, method name and setting for value-cache + /// behavior. + /// + /// The same function name cannot be added twice. + /// + /// + /// is the new single-row function name for use in EPL + /// is the fully-qualified class name of the class implementing the single-row function + /// is the static method provided by the class that : the single-row function + /// set the behavior for caching the return value when constant parameters are provided + /// + /// whether the single-row function, when used in filters, may be subject to reverse index + /// lookup based on the function result + /// + /// whether exceptions generated by the UDF are rethrown + /// is thrown to indicate a problem adding the single-row function + void AddPlugInSingleRowFunction( + string functionName, + string className, + string methodName, + ValueCacheEnum valueCache, + FilterOptimizableEnum filterOptimizable, + bool rethrowExceptions); + + /// + /// Adds a package or class to the list of automatically-imported types. + /// + /// To import a single class offering a static method, simply supply the fully-qualified name of the + /// class and use the syntax classname.Methodname(...) + /// + /// To import a whole package and use the classname.Methodname(...) syntax. + /// + /// is a fully-qualified class name or a package name with wildcard + /// ConfigurationException if incorrect package or class names are encountered + void AddImport(String importName); + + /// + /// Adds the class or namespace (importName) ot the list of automatically imported types. + /// + /// Name of the import. + /// The assembly name or file. + void AddImport(String importName, String assemblyNameOrFile); + + /// + /// Adds a class to the list of automatically-imported classes. + /// + void AddImport(Type importClass); + + /// + /// Adds the import. + /// + /// + void AddImport(); + + /// + /// Adds the annotation import. + /// + /// Name of the import. + /// The assembly name or file. + void AddAnnotationImport(String importName, String assemblyNameOrFile); + + /// + /// Adds the annotation import. + /// + /// The automatic import. + void AddAnnotationImport(String autoImport); + + /// + /// Adds the annotation import. + /// + /// The automatic import. + void AddAnnotationImport(Type autoImport); + + /// + /// Adds the annotation import. + /// + /// + /// + void AddAnnotationImport(bool importNamespace = false); + + /// + /// Adds an import for the namespace associated with the given type. + /// + /// + void AddNamespaceImport(); + + /// + /// Checks if an eventTypeName has already been registered for that name. + /// + /// the name + /// true if already registered + bool IsEventTypeExists(string eventTypeName); + + /// + /// Add an name for an event type represented by object events. + /// + /// Allows a second name to be added for the same type. + /// Does not allow the same name to be used for different types. + /// + /// + /// Note that when adding multiple names for the same type the names represent an + /// alias to the same event type since event type identity for types is per type. + /// + /// + /// is the name for the event type + /// fully-qualified class name of the event type + /// if the name is already in used for a different typeJ + void AddEventType(string eventTypeName, string eventClassName); + + /// + /// Add an name for an event type represented by object events. + /// + /// Allows a second name to be added for the same type. + /// Does not allow the same name to be used for different types. + /// + /// + /// Note that when adding multiple names for the same type the names represent an + /// alias to the same event type since event type identity for types is per type. + /// + /// + /// is the name for the event type + /// is the event class for which to create the name + /// if the name is already in used for a different type + void AddEventType(string eventTypeName, Type eventClass); + + /// + /// Add a name for an event type represented by object events, + /// using the simple name of the type as the name. + /// + /// For example, if your class is "com.mycompany.MyEvent", then this method + /// adds the name "MyEvent" for the class. + /// + /// + /// Allows a second name to be added for the same type. + /// Does not allow the same name to be used for different types. + /// + /// + /// is the event class for which to create the name from the class simple name + /// if the name is already in used for a different type + void AddEventType(Type eventClass); + + /// + /// Add a name for an event type represented by object events, + /// using the simple name of the type as the name. + /// + /// is the event class for which to create the name from the class simple name + /// if the name is already in used for a different type + void AddEventType(String eventTypeName); + + /// + /// Adds a name for an event type represented by the type parameter. + /// + /// + void AddEventType(); + + /// + /// Add an event type that represents IDictionary events. + /// + /// Allows a second name to be added for the same type. + /// Does not allow the same name to be used for different types. + /// + /// + /// is the name for the event type + /// + /// maps the name of each property in the Map event to the type + /// (fully qualified classname) of its value in Map event instances. + /// + /// if the name is already in used for a different type + void AddEventType(string eventTypeName, Properties typeMap); + + /// + /// Add an event type that represents Object-array (Object[]) events. + /// + /// is the name for the event type + /// name of each property, length must match number of types + /// type of each property, length must match number of names + /// if the name is already in used for a different type + void AddEventType(string eventTypeName, string[] propertyNames, Object[] propertyTypes); + + /// + /// Add an event type that represents Object-array (Object[]) events. + /// + /// is the name for the event type + /// name of each property, length must match number of types + /// type of each property, length must match number of names + /// object-array type configuration + /// if the name is already in used for a different type + void AddEventType( + string eventTypeName, + string[] propertyNames, + Object[] propertyTypes, + ConfigurationEventTypeObjectArray optionalConfiguration); + + /// + /// Add an name for an event type that represents IDictionary events, + /// and for which each property may itself be a Map of further properties, + /// with unlimited nesting levels. + /// + /// Each entry in the type mapping must contain the string property name as the key value, + /// and either a Type, or a further Map<string, Object>, or the name + /// of another previously-register Map event type (append [] for array of Map). + /// + /// + /// is the name for the event type + /// + /// maps the name of each property in the Map event to the type + /// (fully qualified classname) of its value in Map event instances. + /// + /// if the name is already in used for a different type + void AddEventType(string eventTypeName, IDictionary typeMap); + + /// + /// Add a name for an event type that represents IDictionary events, + /// and for which each property may itself be a Map of further properties, + /// with unlimited nesting levels. + /// + /// Each entry in the type mapping must contain the string property name as the key value, + /// and either a Type, or a further Map<string, Object>, or the name + /// of another previously-register Map event type (append [] for array of Map). + /// + /// + /// is the name for the event type + /// + /// maps the name of each property in the Map event to the type + /// (fully qualified classname) of its value in Map event instances. + /// + /// is an array of event type name of further Map types that this + /// if the name is already in used for a different type + void AddEventType(string eventTypeName, IDictionary typeMap, string[] superTypes); + + /// + /// Add a name for an event type that represents IDictionary events, + /// and for which each property may itself be a Map of further properties, + /// with unlimited nesting levels. + /// + /// Each entry in the type mapping must contain the string property name as the key value, + /// and either a Type, or a further Map<string, Object>, or the name + /// of another previously-register Map event type (append [] for array of Map). + /// + /// + /// is the name for the event type + /// + /// maps the name of each property in the Map event to the type + /// (fully qualified classname) of its value in Map event instances. + /// + /// is the Map-event type configuration that may defined super-types, timestamp-property-name etc. + /// if the name is already in used for a different type + void AddEventType( + string eventTypeName, + IDictionary typeMap, + ConfigurationEventTypeMap mapConfig); + + /// + /// Add an name for an event type that represents org.w3c.dom.Node events. + /// + /// Allows a second name to be added for the same type. + /// Does not allow the same name to be used for different types. + /// + /// + /// is the name for the event type + /// descriptor containing property and mapping information for XML-DOM events + /// if the name is already in used for a different type + void AddEventType(string eventTypeName, ConfigurationEventTypeXMLDOM xmlDOMEventTypeDesc); + + /// + /// Add a global variable. + /// + /// Use the runtime API to set variable values or EPL statements to change variable values. + /// + /// + /// name of the variable to add + /// + /// the type name of the variable, must be a primitive or boxed builtin scalar type or "object" for any + /// value or an event type name or a class name or fully-qualified class name. Append "[]" for array. + /// + /// + /// is the first assigned value. + /// For static initialization via the object the value can + /// be string-typed and will be parsed. + /// For static initialization the initialization value, if provided, must be + /// Serializable. + /// + /// + /// if the type and initialization value don't match or the variable name + /// is already in use + /// + void AddVariable(string variableName, Type type, Object initializationValue); + + /// + /// Add a global variable. + /// + /// Use the runtime API to set variable values or EPL statements to change variable values. + /// + /// + /// name of the variable to add + /// + /// is the first assigned value. + /// For static initialization via the object the value can + /// be string-typed and will be parsed. + /// For static initialization the initialization value, if provided, must be + /// Serializable. + /// + /// + /// if the type and initialization value don't match or the variable name + /// is already in use + /// + void AddVariable(string variableName, TValue initializationValue); + + /// + /// Add a global variable. + /// + /// Use the runtime API to set variable values or EPL statements to change variable values. + /// + /// + /// name of the variable to add + /// + /// the type name of the variable, must be a primitive or boxed builtin scalar type or "object" for any + /// value or an event type name or a class name or fully-qualified class name. Append "[]" for array. + /// + /// + /// is the first assigned value + /// For static initialization via the object the value can + /// be string-typed and will be parsed. + /// For static initialization the initialization value, if provided, must be + /// Serializable. + /// + /// + /// if the type and initialization value don't match or the variable name + /// is already in use + /// + void AddVariable(string variableName, string type, Object initializationValue); + + /// + /// Add a global variable, allowing constants. + /// + /// Use the runtime API to set variable values or EPL statements to change variable values. + /// + /// + /// name of the variable to add + /// + /// the type name of the variable, must be a primitive or boxed builtin scalar type or "object" for any + /// value or an event type name or a class name or fully-qualified class name. Append "[]" for array. + /// + /// + /// is the first assigned value + /// For static initialization via the object the value can + /// be string-typed and will be parsed. + /// For static initialization the initialization value, if provided, must be + /// Serializable. + /// + /// true to identify the variable as a constant + /// + /// if the type and initialization value don't match or the variable name + /// is already in use + /// + void AddVariable(string variableName, string type, Object initializationValue, bool constant); + + /// + /// Adds an name for an event type that one of the plug-in event representations resolves to an event type. + /// + /// The order of the URIs matters as event representations are asked in turn, to accept the event type. + /// + /// + /// URIs can be child URIs of plug-in event representations and can add additional parameters or fragments + /// for use by the event representation. + /// + /// + /// is the name of the event type + /// is URIs that are matched to registered event representations + /// is an optional value for parameterizing or configuring the event type + void AddPlugInEventType(string eventTypeName, Uri[] resolutionURIs, object initializer); + + /// + /// Sets the URIs that point to plug-in event representations that are given a chance to dynamically resolve an event + /// type name to an event type, when a new (unseen) event type name occurs in a new EPL statement. + /// + /// The order of the URIs matters as event representations are asked in turn, to accept the name. + /// + /// + /// URIs can be child URIs of plug-in event representations and can add additional parameters or fragments + /// for use by the event representation. + /// + /// + /// URIs for resolving the name + IList PlugInEventTypeResolutionURIs { get; set; } + + /// + /// Adds an revision event type. The name of the event type may be used with named windows + /// to indicate that updates or new versions of events are processed. + /// + /// the name of the revision event type + /// the configuration + void AddRevisionEventType(string revisioneventTypeName, ConfigurationRevisionEventType revisionEventTypeConfig); + + /// + /// Adds a new variant stream. Variant streams allow events of disparate types to be treated the same. + /// + /// is the name of the variant stream + /// the configuration such as variant type names and any-type setting + void AddVariantStream(string variantStreamName, ConfigurationVariantStream variantStreamConfig); + + /// + /// Updates an existing Map event type with additional properties. + /// + /// Does not update existing properties of the updated Map event type. + /// + /// + /// Adds additional nested properties to nesting levels, if any. + /// + /// + /// Each entry in the type mapping must contain the string property name of the additional property + /// and either a Type or further Map<string, Object> value for nested properties. + /// + /// + /// Map event types can only be updated at runtime, at configuration time updates are not allowed. + /// + /// + /// The type Map may list previously declared properties or can also contain only the new properties to be added. + /// + /// + /// the name of the map event type to update + /// a Map of string property name and type + /// if the event type name could not be found or is not a Map + void UpdateMapEventType(string mapeventTypeName, IDictionary typeMap); + + /// + /// Returns true if a variant stream by the name has been declared, or false if not. + /// + /// of variant stream + /// indicator whether the variant stream by that name exists + bool IsVariantStreamExists(string name); + + /// + /// Sets a new interval for metrics reporting for a pre-configured statement group, or changes + /// the default statement reporting interval if supplying a null value for the statement group name. + /// + /// + /// name of statement group, provide a null value for the default statement interval (default + /// group) + /// + /// millisecond interval, use zero or negative value to disable + /// if the statement group cannot be found + void SetMetricsReportingInterval(string stmtGroupName, long newIntervalMSec); + + /// + /// Enable metrics reporting for the given statement. + /// + /// This operation can only be performed at runtime and is not available at engine initialization time. + /// + /// + /// Statement metric reporting follows the configured default or statement group interval. + /// + /// + /// Only if metrics reporting (on the engine level) has been enabled at initialization time + /// can statement-level metrics reporting be enabled through this method. + /// + /// + /// for which to enable metrics reporting + /// if the statement cannot be found + void SetMetricsReportingStmtEnabled(string statementName); + + /// + /// Disable metrics reporting for a given statement. + /// + /// for which to disable metrics reporting + /// if the statement cannot be found + void SetMetricsReportingStmtDisabled(string statementName); + + /// + /// Enable engine-level metrics reporting. + /// + /// Use this operation to control, at runtime, metrics reporting globally. + /// + /// + /// Only if metrics reporting (on the engine level) has been enabled at initialization time + /// can metrics reporting be re-enabled at runtime through this method. + /// + /// + /// + /// if use at runtime and metrics reporting had not been enabled at initialization + /// time + /// + void SetMetricsReportingEnabled(); + + /// + /// Disable engine-level metrics reporting. + /// + /// Use this operation to control, at runtime, metrics reporting globally. Setting metrics reporting + /// to disabled removes all performance cost for metrics reporting. + /// + /// + /// + /// if use at runtime and metrics reporting had not been enabled at initialization + /// time + /// + void SetMetricsReportingDisabled(); + + /// + /// Remove an event type by its name, returning an indicator whether the event type was found and removed. + /// + /// This method deletes the event type by it's name from the memory of the engine, + /// thereby allowing that the name to be reused for a new event type and disallowing new statements + /// that attempt to use the deleted name. + /// + /// + /// If there are one or more statements in started or stopped state that reference the event type, + /// this operation throws ConfigurationException unless the force flag is passed. + /// + /// + /// If using the force flag to remove the type while statements use the type, the exact + /// behavior of the engine depends on the event representation of the deleted event type and is thus + /// not well defined. It is recommended to destroy statements that use the type before removing the type. + /// Use #geteventTypeNameUsedBy to obtain a list of statements that use a type. + /// + /// + /// The method can be used for event types implicitly created for insert-into streams and for named windows. + /// The method does not remove variant streams and does not remove revision event types. + /// + /// + /// the name of the event type to remove + /// + /// false to include a check that the type is no longer in use, true to force the remove + /// even though there can be one or more statements relying on that type + /// + /// thrown to indicate that the remove operation failed + /// indicator whether the event type was found and removed + bool RemoveEventType(string name, bool force); + + /// + /// Return the set of statement names of statements that are in started or stopped state and + /// that reference the given event type name. + /// + /// A reference counts as any mention of the event type in a from-clause, a pattern, a insert-into or + /// as part of on-trigger. + /// + /// + /// name of the event type + /// statement names referencing that type + ICollection GetEventTypeNameUsedBy(string eventTypeName); + + /// + /// Return the set of statement names of statements that are in started or stopped state and + /// that reference the given variable name. + /// + /// A reference counts as any mention of the variable in any expression. + /// + /// + /// name of the variable + /// statement names referencing that variable + ICollection GetVariableNameUsedBy(string variableName); + + /// + /// Remove a global non-context-partitioned variable by its name, returning an indicator whether the variable was found + /// and removed. + /// + /// This method deletes the variable by it's name from the memory of the engine, + /// thereby allowing that the name to be reused for a new variable and disallowing new statements + /// that attempt to use the deleted name. + /// + /// + /// If there are one or more statements in started or stopped state that reference the variable, + /// this operation throws ConfigurationException unless the force flag is passed. + /// + /// + /// If using the force flag to remove the variable while statements use the variable, the exact + /// behavior is not well defined and affected statements may log errors. + /// It is recommended to destroy statements that use the variable before removing the variable. + /// Use #getVariableNameUsedBy to obtain a list of statements that use a variable. + /// + /// + /// + /// + /// the name of the variable to remove + /// + /// false to include a check that the variable is no longer in use, true to force the remove + /// even though there can be one or more statements relying on that variable + /// + /// thrown to indicate that the remove operation failed + /// indicator whether the variable was found and removed + bool RemoveVariable(string name, bool force); + + /// + /// Rebuild the XML event type based on changed type informaton, please read below for limitations. + /// + /// Your application must ensure that the rebuild type information is compatible + /// with existing EPL statements and existing events. + /// + /// + /// The method can be used to change XPath expressions of existing attributes and to reload the schema and to add + /// attributes. + /// + /// + /// It is not recommended to remove attributes, change attribute type or change the root element name or namespace, + /// or to change type configuration other then as above. + /// + /// + /// If an existing EPL statement exists that refers to the event type then changes to the event type + /// do not become visible for those existing statements. + /// + /// + /// the name of the XML event type + /// the new type configuration + /// thrown when the type information change failed + void ReplaceXMLEventType(string xmlEventTypeName, ConfigurationEventTypeXMLDOM config); + + /// + /// Returns the event type for a given event type name. Returns null if a type by that name does not exist. + /// + /// This operation is not available for static configuration and is only available for runtime use. + /// + /// + /// to return event type for + /// event type or null if a type by that name does not exists + EventType GetEventType(string eventTypeName); + + /// + /// Add an name for an event type that represents legacy type events. + /// + /// This operation cannot be used to change an existing type. + /// + /// + /// Note that when adding multiple names for the same type the names represent an + /// alias to the same event type since event type identity for types is per type. + /// + /// + /// class of the event type + /// descriptor containing property and mapping information for legacy type events + void AddEventType(ConfigurationEventTypeLegacy legacyEventTypeDesc); + + /// + /// Add an name for an event type that represents legacy type events. + /// + /// This operation cannot be used to change an existing type. + /// + /// + /// Note that when adding multiple names for the same type the names represent an + /// alias to the same event type since event type identity for types is per type. + /// + /// + /// class of the event type + /// is the name for the event type + /// descriptor containing property and mapping information for legacy type events + void AddEventType(string eventTypeName, ConfigurationEventTypeLegacy legacyEventTypeDesc); + + /// + /// Add an name for an event type that represents legacy type events. + /// + /// This operation cannot be used to change an existing type. + /// + /// + /// Note that when adding multiple names for the same type the names represent an + /// alias to the same event type since event type identity for types is per type. + /// + /// + /// is the name for the event type + /// assembly qualified class name of the event type + /// descriptor containing property and mapping information for legacy type events + void AddEventType(string eventTypeName, string eventClass, ConfigurationEventTypeLegacy legacyEventTypeDesc); + + /// + /// Add a new plug-in view for use as a data window or derived value view. + /// + /// view namespace name + /// view name + /// factory class of view + void AddPlugInView(string @namespace, string name, string viewFactoryClass); + + /// + /// Set the current maximum pattern sub-expression count. + /// + /// Use null to indicate that there is no current maximum. + /// + /// + /// to set + long PatternMaxSubexpressions { set; } + + /// + /// Set the current maximum match-recognize state count. + /// + /// Use null to indicate that there is no current maximum. + /// + /// + /// to set + long? MatchRecognizeMaxStates { set; } + + /// + /// Updates an existing Object-array event type with additional properties. + /// + /// Does not update existing properties of the updated Object-array event type. + /// + /// + /// Adds additional nested properties to nesting levels, if any. + /// + /// + /// Object-array event types can only be updated at runtime, at configuration time updates are not allowed. + /// + /// + /// The type properties may list previously declared properties or can also contain only the new properties to be + /// added. + /// + /// + /// the name of the object-array event type to update + /// property names + /// property types + /// if the event type name could not be found or is not a Map + void UpdateObjectArrayEventType(string myEvent, string[] namesNew, Object[] typesNew); + + /// + /// Returns the transient configuration, which are configuration values that are passed by reference (and not by value) + /// + /// transient configuration + IDictionary TransientConfiguration { get; } + + /// + /// Adds an Avro event type + /// + /// type name + /// configs + void AddEventTypeAvro(string eventTypeName, ConfigurationEventTypeAvro avro); + + /// + /// Add a plug-in single-row function + /// + /// configuration + void AddPlugInSingleRowFunction(ConfigurationPlugInSingleRowFunction singleRowFunction); + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/ConfigurationParser.cs b/NEsper.Core/NEsper.Core/client/ConfigurationParser.cs new file mode 100755 index 000000000..03a7987c6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/ConfigurationParser.cs @@ -0,0 +1,1921 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Data; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Xml; +using System.Xml.XPath; +using System.Xml.Xsl; + +using com.espertech.esper.client.soda; +using com.espertech.esper.client.util; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.type; +using com.espertech.esper.util; + +namespace com.espertech.esper.client +{ + using Stream = System.IO.Stream; + + /// Parser for configuration XML. + public class ConfigurationParser + { + private static readonly ILog Log = + LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private static readonly XslCompiledTransform InitializerTransform; + + /// + /// Initializes the class. + /// + static ConfigurationParser() + { + var transformDocument = new XmlDocument(); + using ( + var transformDocumentStream = Assembly.GetExecutingAssembly().GetManifestResourceStream( + "com.espertech.esper.client.InitializerTransform.xslt")) + { + transformDocument.Load(transformDocumentStream); + } + + InitializerTransform = new XslCompiledTransform(false); + InitializerTransform.Load(new XmlNodeReader(transformDocument)); + } + + /// + /// Use the configuration specified in the given input stream. + /// + /// is the configuration object to populate + /// The stream. + /// The name to use in warning/error messages + /// com.espertech.esper.client.EPException + public static void DoConfigure(Configuration configuration, Stream stream, String resourceName) + { + var document = GetDocument(stream, resourceName); + DoConfigure(configuration, document); + } + + public static XmlDocument GetDocument(Stream stream, String resourceName) + { + XmlDocument document; + + try + { + document = new XmlDocument(); + document.Load(stream); + } + catch (XmlException ex) + { + throw new EPException("Could not parse configuration: " + resourceName, ex); + } + catch (IOException ex) + { + throw new EPException("Could not read configuration: " + resourceName, ex); + } + finally + { + try + { + stream.Close(); + } + catch (IOException ioe) + { + Log.Warn("could not close input stream for: " + resourceName, ioe); + } + } + + return document; + } + + /// + /// Parse the W3C DOM document. + /// + /// is the configuration object to populate + /// to parse + /// to indicate parse errors + internal static void DoConfigure(Configuration configuration, XmlDocument doc) + { + DoConfigure(configuration, doc.DocumentElement); + } + + internal static void DoConfigure(Configuration configuration, XmlElement rootElement) + { + foreach (var element in CreateElementEnumerable(rootElement.ChildNodes)) + { + var nodeName = element.Name; + switch (nodeName) + { + case "event-type-auto-name": + HandleEventTypeAutoNames(configuration, element); + break; + case "event-type": + HandleEventTypes(configuration, element); + break; + case "auto-import": + HandleAutoImports(configuration, element); + break; + case "auto-import-annotations": + HandleAutoImportAnnotations(configuration, element); + break; + case "method-reference": + HandleMethodReference(configuration, element); + break; + case "database-reference": + HandleDatabaseRefs(configuration, element); + break; + case "plugin-view": + HandlePlugInView(configuration, element); + break; + case "plugin-virtualdw": + HandlePlugInVirtualDW(configuration, element); + break; + case "plugin-aggregation-function": + HandlePlugInAggregation(configuration, element); + break; + case "plugin-aggregation-multifunction": + HandlePlugInMultiFunctionAggregation(configuration, element); + break; + case "plugin-singlerow-function": + HandlePlugInSingleRow(configuration, element); + break; + case "plugin-pattern-guard": + HandlePlugInPatternGuard(configuration, element); + break; + case "plugin-pattern-observer": + HandlePlugInPatternObserver(configuration, element); + break; + case "variable": + HandleVariable(configuration, element); + break; + case "plugin-loader": + HandlePluginLoaders(configuration, element); + break; + case "engine-settings": + HandleEngineSettings(configuration, element); + break; + case "plugin-event-representation": + HandlePlugInEventRepresentation(configuration, element); + break; + case "plugin-event-type": + HandlePlugInEventType(configuration, element); + break; + case "plugin-event-type-name-resolution": + HandlePlugInEventTypeNameResolution(configuration, element); + break; + case "revision-event-type": + HandleRevisionEventType(configuration, element); + break; + case "variant-stream": + HandleVariantStream(configuration, element); + break; + } + } + } + + private static void HandleEventTypeAutoNames(Configuration configuration, XmlElement element) + { + var name = GetRequiredAttribute(element, "package-name"); + configuration.AddEventTypeAutoName(name); + } + + private static void HandleEventTypes(Configuration configuration, XmlElement element) + { + var name = GetRequiredAttribute(element, "name"); + var classNode = element.Attributes.GetNamedItem("class"); + + string optionalClassName = null; + if (classNode != null) + { + optionalClassName = classNode.InnerText; + configuration.AddEventType(name, optionalClassName); + } + + HandleEventTypeDef(name, optionalClassName, configuration, element); + } + + private static void HandleEventTypeDef( + string name, + string optionalClassName, + Configuration configuration, + XmlNode parentNode) + { + foreach (var eventTypeElement in CreateElementEnumerable(parentNode.ChildNodes)) + { + var nodeName = eventTypeElement.Name; + if (nodeName == "xml-dom") + { + HandleXMLDOM(name, configuration, eventTypeElement); + } + else if (nodeName == "map") + { + HandleMap(name, configuration, eventTypeElement); + } + else if (nodeName == "objectarray") + { + HandleObjectArray(name, configuration, eventTypeElement); + } + else if (nodeName == "legacy-type") + { + HandleLegacy(name, optionalClassName, configuration, eventTypeElement); + } + else if (nodeName == "avro") + { + HandleAvro(name, configuration, eventTypeElement); + } + } + } + + private static void HandleMap(string name, Configuration configuration, XmlElement eventTypeElement) + { + ConfigurationEventTypeMap config; + var startTimestampProp = GetOptionalAttribute(eventTypeElement, "start-timestamp-property-name"); + var endTimestampProp = GetOptionalAttribute(eventTypeElement, "end-timestamp-property-name"); + var superTypesList = GetOptionalAttribute(eventTypeElement, "supertype-names"); + if (superTypesList != null || startTimestampProp != null || endTimestampProp != null) + { + config = new ConfigurationEventTypeMap(); + if (superTypesList != null) + { + var names = superTypesList.SplitCsv(); + foreach (var superTypeName in names) + { + config.SuperTypes.Add(superTypeName.Trim()); + } + } + config.EndTimestampPropertyName = endTimestampProp; + config.StartTimestampPropertyName = startTimestampProp; + configuration.AddMapConfiguration(name, config); + } + + var propertyTypeNames = new Properties(); + var propertyList = eventTypeElement.GetElementsByTagName("map-property"); + for (var i = 0; i < propertyList.Count; i++) + { + var nameProperty = GetRequiredAttribute(propertyList.Item(i), "name"); + var clazz = GetRequiredAttribute(propertyList.Item(i), "class"); + propertyTypeNames.Put(nameProperty, clazz); + } + configuration.AddEventType(name, propertyTypeNames); + } + + private static void HandleObjectArray(string name, Configuration configuration, XmlElement eventTypeElement) + { + ConfigurationEventTypeObjectArray config; + var startTimestampProp = GetOptionalAttribute(eventTypeElement, "start-timestamp-property-name"); + var endTimestampProp = GetOptionalAttribute(eventTypeElement, "end-timestamp-property-name"); + var superTypesList = eventTypeElement.Attributes.GetNamedItem("supertype-names"); + if (superTypesList != null || startTimestampProp != null || endTimestampProp != null) + { + config = new ConfigurationEventTypeObjectArray(); + if (superTypesList != null) + { + var value = superTypesList.InnerText; + var names = value.SplitCsv(); + foreach (var superTypeName in names) + { + config.SuperTypes.Add(superTypeName.Trim()); + } + } + config.EndTimestampPropertyName = endTimestampProp; + config.StartTimestampPropertyName = startTimestampProp; + configuration.AddObjectArrayConfiguration(name, config); + } + + var propertyNames = new List(); + var propertyTypes = new List(); + var propertyList = eventTypeElement.GetElementsByTagName("objectarray-property"); + for (var i = 0; i < propertyList.Count; i++) + { + var nameProperty = GetRequiredAttribute(propertyList.Item(i), "name"); + var clazz = GetRequiredAttribute(propertyList.Item(i), "class"); + propertyNames.Add(nameProperty); + propertyTypes.Add(clazz); + } + configuration.AddEventType(name, propertyNames.ToArray(), propertyTypes.ToArray()); + } + + private static void HandleXMLDOM(string name, Configuration configuration, XmlElement xmldomElement) + { + var rootElementName = GetRequiredAttribute(xmldomElement, "root-element-name"); + var rootElementNamespace = GetOptionalAttribute(xmldomElement, "root-element-namespace"); + var schemaResource = GetOptionalAttribute(xmldomElement, "schema-resource"); + var schemaText = GetOptionalAttribute(xmldomElement, "schema-text"); + var defaultNamespace = GetOptionalAttribute(xmldomElement, "default-namespace"); + var resolvePropertiesAbsoluteStr = GetOptionalAttribute( + xmldomElement, "xpath-resolve-properties-absolute"); + var propertyExprXPathStr = GetOptionalAttribute(xmldomElement, "xpath-property-expr"); + var eventSenderChecksRootStr = GetOptionalAttribute(xmldomElement, "event-sender-validates-root"); + var xpathFunctionResolverClass = GetOptionalAttribute(xmldomElement, "xpath-function-resolver"); + var xpathVariableResolverClass = GetOptionalAttribute(xmldomElement, "xpath-variable-resolver"); + var autoFragmentStr = GetOptionalAttribute(xmldomElement, "auto-fragment"); + var startTimestampProperty = GetOptionalAttribute(xmldomElement, "start-timestamp-property-name"); + var endTimestampProperty = GetOptionalAttribute(xmldomElement, "end-timestamp-property-name"); + + var xmlDOMEventTypeDesc = new ConfigurationEventTypeXMLDOM(); + xmlDOMEventTypeDesc.RootElementName = rootElementName; + xmlDOMEventTypeDesc.SchemaResource = schemaResource; + xmlDOMEventTypeDesc.SchemaText = schemaText; + xmlDOMEventTypeDesc.RootElementNamespace = rootElementNamespace; + xmlDOMEventTypeDesc.DefaultNamespace = defaultNamespace; + xmlDOMEventTypeDesc.XPathFunctionResolver = xpathFunctionResolverClass; + xmlDOMEventTypeDesc.XPathVariableResolver = xpathVariableResolverClass; + xmlDOMEventTypeDesc.StartTimestampPropertyName = startTimestampProperty; + xmlDOMEventTypeDesc.EndTimestampPropertyName = endTimestampProperty; + + if (resolvePropertiesAbsoluteStr != null) + { + xmlDOMEventTypeDesc.IsXPathResolvePropertiesAbsolute = Boolean.Parse(resolvePropertiesAbsoluteStr); + } + + if (propertyExprXPathStr != null) + { + xmlDOMEventTypeDesc.IsXPathPropertyExpr = Boolean.Parse(propertyExprXPathStr); + } + if (eventSenderChecksRootStr != null) + { + xmlDOMEventTypeDesc.IsEventSenderValidatesRoot = Boolean.Parse(eventSenderChecksRootStr); + } + if (autoFragmentStr != null) + { + xmlDOMEventTypeDesc.IsAutoFragment = Boolean.Parse(autoFragmentStr); + } + + configuration.AddEventType(name, xmlDOMEventTypeDesc); + + foreach (var propertyElement in CreateElementEnumerable(xmldomElement.ChildNodes)) + { + if (propertyElement.Name.Equals("namespace-prefix")) + { + var prefix = GetRequiredAttribute(propertyElement, "prefix"); + var namespace_ = GetRequiredAttribute(propertyElement, "namespace"); + xmlDOMEventTypeDesc.AddNamespacePrefix(prefix, namespace_); + } + if (propertyElement.Name.Equals("xpath-property")) + { + var propertyName = GetRequiredAttribute(propertyElement, "property-name"); + var xPath = GetRequiredAttribute(propertyElement, "xpath"); + var propertyType = GetRequiredAttribute(propertyElement, "type"); + + XPathResultType xpathConstantType; + switch (propertyType.ToUpperInvariant()) + { + case "NUMBER": + xpathConstantType = XPathResultType.Number; + break; + case "STRING": + xpathConstantType = XPathResultType.String; + break; + case "BOOLEAN": + xpathConstantType = XPathResultType.Boolean; + break; + case "NODE": + case "NODESET": + xpathConstantType = XPathResultType.NodeSet; + break; + default: + throw new ArgumentException("Invalid xpath property type for property '" + propertyName + + "' and type '" + propertyType + "'"); + } + + String castToClass = null; + if (propertyElement.Attributes.GetNamedItem("cast") != null) + { + castToClass = GetRequiredAttribute(propertyElement, "cast"); + } + + String optionalEventTypeName = null; + if (propertyElement.Attributes.GetNamedItem("event-type-name") != null) + { + optionalEventTypeName = GetRequiredAttribute(propertyElement, "event-type-name"); + } + + if (optionalEventTypeName != null) + { + xmlDOMEventTypeDesc.AddXPathPropertyFragment(propertyName, xPath, xpathConstantType, + optionalEventTypeName); + } + else + { + xmlDOMEventTypeDesc.AddXPathProperty(propertyName, xPath, xpathConstantType, castToClass); + } + } + } + } + + private static void HandleAvro(string name, Configuration configuration, XmlElement element) + { + var schemaText = GetOptionalAttribute(element, "schema-text"); + + var avroEventTypeDesc = new ConfigurationEventTypeAvro(); + avroEventTypeDesc.AvroSchemaText = schemaText; + configuration.AddEventTypeAvro(name, avroEventTypeDesc); + + avroEventTypeDesc.StartTimestampPropertyName = GetOptionalAttribute( + element, "start-timestamp-property-name"); + avroEventTypeDesc.EndTimestampPropertyName = GetOptionalAttribute(element, "end-timestamp-property-name"); + + var names = GetOptionalAttribute(element, "supertype-names"); + if (names != null) + { + var split = names.SplitCsv(); + for (var i = 0; i < split.Length; i++) + { + avroEventTypeDesc.SuperTypes.Add(split[i].Trim()); + } + } + } + + private static void HandleLegacy( + string name, + string className, + Configuration configuration, + XmlElement xmldomElement) + { + // Type name is required for legacy classes + if (className == null) + { + throw new ConfigurationException("Required class name not supplied for legacy type definition"); + } + + var accessorStyle = GetRequiredAttribute(xmldomElement, "accessor-style"); + var codeGeneration = GetRequiredAttribute(xmldomElement, "code-generation"); + var propertyResolution = GetRequiredAttribute(xmldomElement, "property-resolution-style"); + var factoryMethod = GetOptionalAttribute(xmldomElement, "factory-method"); + var copyMethod = GetOptionalAttribute(xmldomElement, "copy-method"); + var startTimestampProp = GetOptionalAttribute(xmldomElement, "start-timestamp-property-name"); + var endTimestampProp = GetOptionalAttribute(xmldomElement, "end-timestamp-property-name"); + + var legacyDesc = new ConfigurationEventTypeLegacy(); + if (accessorStyle != null) + { + legacyDesc.AccessorStyle = EnumHelper.Parse(accessorStyle); + } + if (codeGeneration != null) + { + legacyDesc.CodeGeneration = EnumHelper.Parse(codeGeneration); + } + if (propertyResolution != null) + { + legacyDesc.PropertyResolutionStyle = EnumHelper.Parse(propertyResolution); + } + legacyDesc.FactoryMethod = factoryMethod; + legacyDesc.CopyMethod = copyMethod; + legacyDesc.StartTimestampPropertyName = startTimestampProp; + legacyDesc.EndTimestampPropertyName = endTimestampProp; + configuration.AddEventType(name, className, legacyDesc); + + foreach (var propertyElement in CreateElementEnumerable(xmldomElement.ChildNodes)) + { + if (propertyElement.Name.Equals("method-property")) + { + var nameProperty = GetRequiredAttribute(propertyElement, "name"); + var method = GetRequiredAttribute(propertyElement, "accessor-method"); + legacyDesc.AddMethodProperty(nameProperty, method); + } + else if (propertyElement.Name.Equals("field-property")) + { + var nameProperty = GetRequiredAttribute(propertyElement, "name"); + var field = GetRequiredAttribute(propertyElement, "accessor-field"); + legacyDesc.AddFieldProperty(nameProperty, field); + } + else + { + throw new ConfigurationException( + "Invalid node " + propertyElement.Name + + " encountered while parsing legacy type definition"); + } + } + } + + private static void HandleAutoImports(Configuration configuration, XmlElement element) + { + var name = GetRequiredAttribute(element, "import-name"); + var assembly = GetOptionalAttribute(element, "assembly"); + configuration.AddImport(name, assembly); + } + + private static void HandleAutoImportAnnotations(Configuration configuration, XmlElement element) + { + var name = GetRequiredAttribute(element, "import-name"); + configuration.AddAnnotationImport(name); + } + + private static void HandleDatabaseRefs(Configuration configuration, XmlElement element) + { + var name = GetRequiredAttribute(element, "name"); + var configDBRef = new ConfigurationDBRef(); + configuration.AddDatabaseReference(name, configDBRef); + + foreach (var subElement in CreateElementEnumerable(element.ChildNodes)) + { + switch (subElement.Name) + { + case "driver": + { + var properties = CreatePropertiesFromAttributes(subElement.Attributes); + var driverName = subElement.Attributes.GetNamedItem("type").Value; + configDBRef.SetDatabaseDriver(driverName, properties); + break; + } + case "connection-lifecycle": + { + var value = GetRequiredAttribute(subElement, "value"); + configDBRef.ConnectionLifecycle = + EnumHelper.Parse(value); + break; + } + case "connection-settings": + { + if (subElement.Attributes.GetNamedItem("auto-commit") != null) + { + var autoCommit = GetRequiredAttribute(subElement, "auto-commit"); + configDBRef.ConnectionAutoCommit = Boolean.Parse(autoCommit); + } + if (subElement.Attributes.GetNamedItem("transaction-isolation") != null) + { + var transactionIsolation = + GetRequiredAttribute(subElement, "transaction-isolation"); + configDBRef.ConnectionTransactionIsolation = + EnumHelper.Parse(transactionIsolation); + } + if (subElement.Attributes.GetNamedItem("catalog") != null) + { + var catalog = GetRequiredAttribute(subElement, "catalog"); + configDBRef.ConnectionCatalog = catalog; + } +#if NOT_SUPPORTED_IN_DOTNET + if (subElement.Attributes.GetNamedItem("read-only") != null) + { + String readOnly = GetRequiredAttribute(subElement, "read-only"); + configDBRef.ConnectionReadOnly = Boolean.Parse(readOnly); + } +#endif + break; + } + case "column-change-case": + { + var value = GetRequiredAttribute(subElement, "value"); + var parsed = + EnumHelper.Parse(value); + configDBRef.ColumnChangeCase = parsed; + break; + } + case "metadata-origin": + { + var value = GetRequiredAttribute(subElement, "value"); + var parsed = + EnumHelper.Parse(value); + configDBRef.MetadataOrigin = parsed; + break; + } + + // NOTE: How does this translate in a world based on ADO.NET + // Does it translate at all? + //case "sql-types-mapping": + // { + // String sqlType = GetRequiredAttribute(subElement, "sql-type"); + // String dataType = GetRequiredAttribute(subElement, "data-type"); + // int sqlTypeInt; + + // if (!Int32.TryParse(sqlType, out sqlTypeInt)) + // { + // throw new ConfigurationException("Error converting sql type '" + sqlType + + // "' to integer constant"); + // } + // configDBRef.AddSqlTypesBinding(sqlTypeInt, dataType); + // break; + // } + + case "expiry-time-cache": + { + var maxAge = subElement.Attributes.GetNamedItem("max-age-seconds").Value; + var purgeInterval = + subElement.Attributes.GetNamedItem("purge-interval-seconds").Value; + + var refTypeEnum = ConfigurationCacheReferenceTypeHelper.GetDefault(); + if (subElement.Attributes.GetNamedItem("ref-type") != null) + { + var refType = subElement.Attributes.GetNamedItem("ref-type").Value; + refTypeEnum = EnumHelper.Parse(refType); + } + configDBRef.SetExpiryTimeCache(Double.Parse(maxAge), Double.Parse(purgeInterval), refTypeEnum); + break; + } + case "lru-cache": + { + var size = GetRequiredAttribute(subElement, "size"); + configDBRef.LRUCache = Int32.Parse(size); + break; + } + } + } + } + + private static void HandleMethodReference(Configuration configuration, XmlElement element) + { + var className = GetRequiredAttribute(element, "class-name"); + var configMethodRef = new ConfigurationMethodRef(); + configuration.AddMethodRef(className, configMethodRef); + + foreach (var subElement in CreateElementEnumerable(element.ChildNodes)) + { + if (subElement.Name == "expiry-time-cache") + { + var maxAge = GetRequiredAttribute(subElement, "max-age-seconds"); + var purgeInterval = GetRequiredAttribute(subElement, "purge-interval-seconds"); + ConfigurationCacheReferenceType refTypeEnum = ConfigurationCacheReferenceTypeHelper.GetDefault(); + if (subElement.Attributes.GetNamedItem("ref-type") != null) + { + var refType = subElement.Attributes.GetNamedItem("ref-type").InnerText; + refTypeEnum = EnumHelper.Parse(refType); + } + configMethodRef.SetExpiryTimeCache(Double.Parse(maxAge), Double.Parse(purgeInterval), refTypeEnum); + } + else if (subElement.Name == "lru-cache") + { + var size = GetRequiredAttribute(subElement, "size"); + configMethodRef.SetLRUCache(Int32.Parse(size)); + } + } + } + + private static void HandlePlugInView(Configuration configuration, XmlElement element) + { + var @namespace = GetRequiredAttribute(element, "namespace"); + var name = GetRequiredAttribute(element, "name"); + var factoryClassName = GetRequiredAttribute(element, "factory-class"); + configuration.AddPlugInView(@namespace, name, factoryClassName); + } + + private static void HandlePlugInVirtualDW(Configuration configuration, XmlElement element) + { + var @namespace = GetRequiredAttribute(element, "namespace"); + var name = GetRequiredAttribute(element, "name"); + var factoryClassName = GetRequiredAttribute(element, "factory-class"); + var config = GetOptionalAttribute(element, "config"); + configuration.AddPlugInVirtualDataWindow(@namespace, name, factoryClassName, config); + } + + private static void HandlePlugInAggregation(Configuration configuration, XmlElement element) + { + var name = GetRequiredAttribute(element, "name"); + var factoryClassName = GetRequiredAttribute(element, "factory-class"); + configuration.AddPlugInAggregationFunctionFactory(name, factoryClassName); + } + + private static void HandlePlugInMultiFunctionAggregation(Configuration configuration, XmlElement element) + { + var functionNames = GetRequiredAttribute(element, "function-names"); + var factoryClassName = GetOptionalAttribute(element, "factory-class"); + + IDictionary additionalProps = null; + foreach (var subElement in CreateElementEnumerable(element.ChildNodes)) + { + if (subElement.Name == "init-arg") + { + var name = GetRequiredAttribute(subElement, "name"); + var value = GetRequiredAttribute(subElement, "value"); + if (additionalProps == null) + { + additionalProps = new Dictionary(); + } + additionalProps.Put(name, value); + } + } + + var config = new ConfigurationPlugInAggregationMultiFunction(functionNames.SplitCsv(), factoryClassName); + config.AdditionalConfiguredProperties = additionalProps; + configuration.AddPlugInAggregationMultiFunction(config); + } + + private static void HandlePlugInSingleRow(Configuration configuration, XmlElement element) + { + var name = element.Attributes.GetNamedItem("name").InnerText; + var functionClassName = element.Attributes.GetNamedItem("function-class").InnerText; + var functionMethodName = element.Attributes.GetNamedItem("function-method").InnerText; + var valueCache = ValueCacheEnum.DISABLED; + var filterOptimizable = FilterOptimizableEnum.ENABLED; + + WhenOptionalAttribute( + element, "value-cache", valueCacheStr => + { + valueCache = EnumHelper.Parse(valueCacheStr); + }); + + WhenOptionalAttribute( + element, "filter-optimizable", filterOptimizableStr => + { + filterOptimizable = EnumHelper.Parse(filterOptimizableStr); + }); + + var rethrowExceptionsStr = GetOptionalAttribute(element, "rethrow-exceptions"); + var rethrowExceptions = false; + if (rethrowExceptionsStr != null) + { + rethrowExceptions = Boolean.Parse(rethrowExceptionsStr); + } + + var eventTypeName = GetOptionalAttribute(element, "event-type-name"); + configuration.AddPlugInSingleRowFunction( + new ConfigurationPlugInSingleRowFunction( + name, functionClassName, functionMethodName, valueCache, filterOptimizable, rethrowExceptions, + eventTypeName)); + } + + private static void HandlePlugInPatternGuard(Configuration configuration, XmlElement element) + { + var @namespace = GetRequiredAttribute(element, "namespace"); + var name = GetRequiredAttribute(element, "name"); + var factoryClassName = GetRequiredAttribute(element, "factory-class"); + configuration.AddPlugInPatternGuard(@namespace, name, factoryClassName); + } + + private static void HandlePlugInPatternObserver(Configuration configuration, XmlElement element) + { + var @namespace = GetRequiredAttribute(element, "namespace"); + var name = GetRequiredAttribute(element, "name"); + var factoryClassName = GetRequiredAttribute(element, "factory-class"); + configuration.AddPlugInPatternObserver(@namespace, name, factoryClassName); + } + + private static void HandleVariable(Configuration configuration, XmlElement element) + { + var variableName = GetRequiredAttribute(element, "name"); + var type = GetRequiredAttribute(element, "type"); + + var variableType = TypeHelper.GetTypeForSimpleName(type); + if (variableType == null) + { + throw new ConfigurationException( + "Invalid variable type for variable '" + variableName + "', the type is not recognized"); + } + + var initValueNode = element.Attributes.GetNamedItem("initialization-value"); + string initValue = null; + if (initValueNode != null) + { + initValue = initValueNode.InnerText; + } + + var isConstant = false; + if (GetOptionalAttribute(element, "constant") != null) + { + isConstant = Boolean.Parse(GetOptionalAttribute(element, "constant")); + } + + configuration.AddVariable(variableName, variableType, initValue, isConstant); + } + + private static void HandlePluginLoaders(Configuration configuration, XmlElement element) + { + var loaderName = GetRequiredAttribute(element, "name"); + var className = GetRequiredAttribute(element, "class-name"); + var properties = new Properties(); + string configXML = null; + + foreach (var subElement in CreateElementEnumerable(element.ChildNodes)) + { + if (subElement.Name == "init-arg") + { + var name = GetRequiredAttribute(subElement, "name"); + var value = GetRequiredAttribute(subElement, "value"); + properties.Put(name, value); + } + else if (subElement.Name == "config-xml") + { + var stringWriter = new StringWriter(); + var textWriter = new XmlTextWriter(stringWriter); + textWriter.WriteStartDocument(); + subElement.WriteContentTo(textWriter); + textWriter.WriteEndDocument(); + configXML = stringWriter.ToString(); + } + } + configuration.AddPluginLoader(loaderName, className, properties, configXML); + } + + private static void HandlePlugInEventRepresentation(Configuration configuration, XmlElement element) + { + var uri = GetRequiredAttribute(element, "uri"); + var className = GetRequiredAttribute(element, "class-name"); + string initializer = null; + foreach (var subElement in CreateElementEnumerable(element.ChildNodes)) + { + if (subElement.Name == "initializer") + { + var elementList = CreateElementList(subElement.ChildNodes); + if (elementList.Count == 0) + { + throw new ConfigurationException( + "Error handling initializer for plug-in event representation '" + uri + + "', no child node found under initializer element, expecting an element node"); + } + + try + { + // Convert the contents of the initializer element into a form that is more suitable for the initializer. + // The transform that we have put together for this takes the child nodes and removes them from the + // parent namespace. + var sWriter = new StringWriter(); + XmlWriter xWriter = new XmlTextWriter(sWriter); + + // Esper expects that the fragment below is enough to construct a complete document. We need to write + // the start document element so that we end up with a valid XML document complete with header. + xWriter.WriteStartDocument(); + + InitializerTransform.Transform(new XmlNodeReader(subElement), xWriter); + + // The result of the transform should be a document with a single node. I'm not 100% certain though because + // the transform does not take into account the fact that the initializer can have more than one child and + // this would cause the transform to be "technically" invalid. + initializer = sWriter.ToString(); + } + catch (XsltException e) + { + throw new ConfigurationException( + "Error handling initializer for plug-in event representation '" + uri + "' :" + e.Message, e); + } + catch (XmlException e) + { + throw new ConfigurationException( + "Error handling initializer for plug-in event representation '" + uri + "' :" + e.Message, e); + } + } + } + + Uri uriParsed; + try + { + uriParsed = new Uri(uri); + } + catch (UriFormatException ex) + { + throw new ConfigurationException( + "Error parsing URI '" + uri + "' as a valid System.Uri string:" + ex.Message, ex); + } + + configuration.AddPlugInEventRepresentation(uriParsed, className, initializer); + } + + private static void HandlePlugInEventType(Configuration configuration, XmlElement element) + { + var uris = new List(); + var name = GetRequiredAttribute(element, "name"); + string initializer = null; + + foreach (var subElement in CreateElementEnumerable(element.ChildNodes)) + { + switch (subElement.Name) + { + case "resolution-uri": + do + { + var uriValue = GetRequiredAttribute(subElement, "value"); + Uri uri; + try + { + uri = new Uri(uriValue); + } + catch (UriFormatException ex) + { + throw new ConfigurationException( + "Error parsing URI '" + uriValue + "' as a valid System.Uri string:" + + ex.Message, + ex); + } + uris.Add(uri); + } while (false); + break; + + case "initializer": + var elementList = CreateElementList(subElement.ChildNodes); + if (elementList.Count == 0) + { + throw new ConfigurationException( + "Error handling initializer for plug-in event type '" + name + + "', no child node found under initializer element, expecting an element node"); + } + + try + { + // Convert the contents of the initializer element into a form that is more suitable for the initializer. + // The transform that we have put together for this takes the child nodes and removes them from the + // parent namespace. + var sWriter = new StringWriter(); + XmlWriter xWriter = new XmlTextWriter(sWriter); + + // Esper expects that the fragment below is enough to construct a complete document. We need to write + // the start document element so that we end up with a valid XML document complete with header. + xWriter.WriteStartDocument(); + + InitializerTransform.Transform(new XmlNodeReader(subElement), xWriter); + + // The result of the transform should be a document with a single node. I'm not 100% certain though because + // the transform does not take into account the fact that the initializer can have more than one child and + // this would cause the transform to be "technically" invalid. + initializer = sWriter.ToString(); + } + catch (XsltException e) + { + throw new ConfigurationException( + "Error handling initializer for plug-in event type '" + name + "' :" + e.Message, e); + } + catch (XmlException e) + { + throw new ConfigurationException( + "Error handling initializer for plug-in event type '" + name + "' :" + e.Message, e); + } + + break; + } + } + + + configuration.AddPlugInEventType(name, uris.ToArray(), initializer); + } + + private static void HandlePlugInEventTypeNameResolution(Configuration configuration, XmlElement element) + { + var uris = new List(); + foreach (var subElement in CreateElementEnumerable(element.ChildNodes)) + { + if (subElement.Name == "resolution-uri") + { + var uriValue = GetRequiredAttribute(subElement, "value"); + Uri uri; + try + { + uri = new Uri(uriValue); + } + catch (UriFormatException ex) + { + throw new ConfigurationException( + "Error parsing URI '" + uriValue + "' as a valid System.Uri string:" + ex.Message, ex); + } + uris.Add(uri); + } + } + + configuration.PlugInEventTypeResolutionURIs = uris.ToArray(); + } + + private static void HandleRevisionEventType(Configuration configuration, XmlElement element) + { + var revEventType = new ConfigurationRevisionEventType(); + var revTypeName = GetRequiredAttribute(element, "name"); + + if (element.Attributes.GetNamedItem("property-revision") != null) + { + var propertyRevision = element.Attributes.GetNamedItem("property-revision").InnerText; + PropertyRevisionEnum propertyRevisionEnum; + try + { + propertyRevisionEnum = EnumHelper.Parse(propertyRevision); + revEventType.PropertyRevision = propertyRevisionEnum; + } + catch + { + throw new ConfigurationException( + "Invalid enumeration value for property-revision attribute '" + propertyRevision + "'"); + } + } + + var keyProperties = new HashSet(); + + foreach (var subElement in CreateElementEnumerable(element.ChildNodes)) + { + switch (subElement.Name) + { + case "base-event-type": + { + var name = GetRequiredAttribute(subElement, "name"); + revEventType.AddNameBaseEventType(name); + break; + } + case "delta-event-type": + { + var name = GetRequiredAttribute(subElement, "name"); + revEventType.AddNameDeltaEventType(name); + break; + } + case "key-property": + { + var name = GetRequiredAttribute(subElement, "name"); + keyProperties.Add(name); + break; + } + } + } + + string[] keyProps = keyProperties.ToArray(); + revEventType.KeyPropertyNames = keyProps; + + configuration.AddRevisionEventType(revTypeName, revEventType); + } + + private static void HandleVariantStream(Configuration configuration, XmlElement element) + { + var variantStream = new ConfigurationVariantStream(); + var varianceName = GetRequiredAttribute(element, "name"); + + if (element.Attributes.GetNamedItem("type-variance") != null) + { + var typeVar = element.Attributes.GetNamedItem("type-variance").InnerText; + TypeVarianceEnum typeVarianceEnum; + try + { + typeVarianceEnum = EnumHelper.Parse(typeVar); + variantStream.TypeVariance = typeVarianceEnum; + } + catch + { + throw new ConfigurationException( + "Invalid enumeration value for type-variance attribute '" + typeVar + "'"); + } + } + + foreach (var subElement in CreateElementEnumerable(element.ChildNodes)) + { + if (subElement.Name == "variant-event-type") + { + var name = subElement.Attributes.GetNamedItem("name").InnerText; + variantStream.AddEventTypeName(name); + } + } + + configuration.AddVariantStream(varianceName, variantStream); + } + + private static void HandleEngineSettings(Configuration configuration, XmlElement element) + { + foreach (var subElement in CreateElementEnumerable(element.ChildNodes)) + { + if (subElement.Name == "defaults") + { + HandleEngineSettingsDefaults(configuration, subElement); + } + } + } + + private static void HandleEngineSettingsDefaults(Configuration configuration, XmlElement parentElement) + { + foreach (var subElement in CreateElementEnumerable(parentElement.ChildNodes)) + { + switch (subElement.Name) + { + case "threading": + HandleDefaultsThreading(configuration, subElement); + break; + case "event-meta": + HandleDefaultsEventMeta(configuration, subElement); + break; + case "view-resources": + HandleDefaultsViewResources(configuration, subElement); + break; + case "logging": + HandleDefaultsLogging(configuration, subElement); + break; + case "variables": + HandleDefaultsVariables(configuration, subElement); + break; + case "patterns": + HandleDefaultsPatterns(configuration, subElement); + break; + case "match-recognize": + HandleDefaultsMatchRecognize(configuration, subElement); + break; + case "stream-selection": + HandleDefaultsStreamSelection(configuration, subElement); + break; + case "time-source": + HandleDefaultsTimeSource(configuration, subElement); + break; + case "metrics-reporting": + HandleMetricsReporting(configuration, subElement); + break; + case "language": + HandleLanguage(configuration, subElement); + break; + case "expression": + HandleExpression(configuration, subElement); + break; + case "execution": + HandleExecution(configuration, subElement); + break; + case "exceptionHandling": + { + configuration.EngineDefaults.ExceptionHandling.AddClasses(GetHandlerFactories(subElement)); + var enableUndeployRethrowStr = GetOptionalAttribute(subElement, "undeploy-rethrow-policy"); + if (enableUndeployRethrowStr != null) + { + configuration.EngineDefaults.ExceptionHandling.UndeployRethrowPolicy = + EnumHelper.Parse( + enableUndeployRethrowStr); + } + } + break; + case "conditionHandling": + configuration.EngineDefaults.ConditionHandling.AddClasses(GetHandlerFactories(subElement)); + break; + case "scripts": + HandleDefaultScriptConfig(configuration, subElement); + break; + } + } + } + + private static void HandleDefaultsThreading(Configuration configuration, XmlElement parentElement) + { + var engineFairlockStr = GetOptionalAttribute(parentElement, "engine-fairlock"); + if (engineFairlockStr != null) + { + var isEngineFairlock = Boolean.Parse(engineFairlockStr); + configuration.EngineDefaults.Threading.IsEngineFairlock = isEngineFairlock; + } + + foreach (var subElement in CreateElementEnumerable(parentElement.ChildNodes)) + { + if (subElement.Name == "listener-dispatch") + { + var preserveOrderText = GetRequiredAttribute(subElement, "preserve-order"); + var preserveOrder = Boolean.Parse(preserveOrderText); + configuration.EngineDefaults.Threading.IsListenerDispatchPreserveOrder = preserveOrder; + + WhenOptionalAttribute( + subElement, "timeout-msec", timeoutMSecText => + { + var timeoutMSec = Int64.Parse(timeoutMSecText); + configuration.EngineDefaults.Threading.ListenerDispatchTimeout = timeoutMSec; + }); + + WhenOptionalAttribute( + subElement, "locking", value => + { + configuration.EngineDefaults.Threading.ListenerDispatchLocking = + EnumHelper.Parse(value); + }); + } + else if (subElement.Name == "insert-into-dispatch") + { + var preserveOrderText = GetRequiredAttribute(subElement, "preserve-order"); + var preserveOrder = Boolean.Parse(preserveOrderText); + configuration.EngineDefaults.Threading.IsInsertIntoDispatchPreserveOrder = preserveOrder; + + WhenOptionalAttribute( + subElement, "timeout-msec", value => + { + var timeoutMSec = Int64.Parse(value); + configuration.EngineDefaults.Threading.InsertIntoDispatchTimeout = timeoutMSec; + }); + + WhenOptionalAttribute( + subElement, "locking", value => + { + configuration.EngineDefaults.Threading.InsertIntoDispatchLocking = + EnumHelper.Parse(value); + }); + } + else if (subElement.Name == "named-window-consumer-dispatch") + { + var preserveOrderText = GetRequiredAttribute(subElement, "preserve-order"); + var preserveOrder = Boolean.Parse(preserveOrderText); + configuration.EngineDefaults.Threading.IsNamedWindowConsumerDispatchPreserveOrder = + preserveOrder; + + WhenOptionalAttribute( + subElement, "timeout-msec", value => + { + var timeoutMSec = Int64.Parse(value); + configuration.EngineDefaults.Threading.NamedWindowConsumerDispatchTimeout = timeoutMSec; + }); + + WhenOptionalAttribute( + subElement, "locking", value => + { + configuration.EngineDefaults.Threading.NamedWindowConsumerDispatchLocking = + EnumHelper.Parse(value); + }); + } + else if (subElement.Name == "internal-timer") + { + var enabledText = GetRequiredAttribute(subElement, "enabled"); + var enabled = Boolean.Parse(enabledText); + var msecResolutionText = GetRequiredAttribute(subElement, "msec-resolution"); + long msecResolution = Int64.Parse(msecResolutionText); + configuration.EngineDefaults.Threading.IsInternalTimerEnabled = enabled; + configuration.EngineDefaults.Threading.InternalTimerMsecResolution = msecResolution; + } + else if (subElement.Name == "threadpool-inbound") + { + var result = ParseThreadPoolConfig(subElement); + configuration.EngineDefaults.Threading.IsThreadPoolInbound = result.IsEnabled; + configuration.EngineDefaults.Threading.ThreadPoolInboundNumThreads = result.NumThreads; + configuration.EngineDefaults.Threading.ThreadPoolInboundCapacity = result.Capacity; + } + else if (subElement.Name == "threadpool-outbound") + { + var result = ParseThreadPoolConfig(subElement); + configuration.EngineDefaults.Threading.IsThreadPoolOutbound = result.IsEnabled; + configuration.EngineDefaults.Threading.ThreadPoolOutboundNumThreads = result.NumThreads; + configuration.EngineDefaults.Threading.ThreadPoolOutboundCapacity = result.Capacity; + } + else if (subElement.Name == "threadpool-timerexec") + { + var result = ParseThreadPoolConfig(subElement); + configuration.EngineDefaults.Threading.IsThreadPoolTimerExec = result.IsEnabled; + configuration.EngineDefaults.Threading.ThreadPoolTimerExecNumThreads = result.NumThreads; + configuration.EngineDefaults.Threading.ThreadPoolTimerExecCapacity = result.Capacity; + } + else if (subElement.Name == "threadpool-routeexec") + { + var result = ParseThreadPoolConfig(subElement); + configuration.EngineDefaults.Threading.IsThreadPoolRouteExec = result.IsEnabled; + configuration.EngineDefaults.Threading.ThreadPoolRouteExecNumThreads = result.NumThreads; + configuration.EngineDefaults.Threading.ThreadPoolRouteExecCapacity = result.Capacity; + } + } + } + + private static ThreadPoolConfig ParseThreadPoolConfig(XmlElement parentElement) + { + var enabled = GetRequiredAttribute(parentElement, "enabled"); + var isEnabled = Boolean.Parse(enabled); + + var numThreadsStr = GetRequiredAttribute(parentElement, "num-threads"); + var numThreads = Int32.Parse(numThreadsStr); + + var capacityStr = GetOptionalAttribute(parentElement, "capacity"); + int? capacity = null; + if (capacityStr != null) + { + capacity = Int32.Parse(capacityStr); + } + + return new ThreadPoolConfig(isEnabled, numThreads, capacity); + } + + private static void HandleDefaultsViewResources(Configuration configuration, XmlElement parentElement) + { + foreach (var subElement in CreateElementEnumerable(parentElement.ChildNodes)) + { + switch (subElement.Name) + { + case "share-views": + { + var valueText = GetRequiredAttribute(subElement, "enabled"); + var value = Boolean.Parse(valueText); + configuration.EngineDefaults.ViewResources.IsShareViews = value; + } + break; + case "allow-multiple-expiry-policy": + { + var valueText = GetRequiredAttribute(subElement, "enabled"); + var value = Boolean.Parse(valueText); + configuration.EngineDefaults.ViewResources.IsAllowMultipleExpiryPolicies = value; + } + break; + case "iterable-unbound": + { + var valueText = GetRequiredAttribute(subElement, "enabled"); + var value = Boolean.Parse(valueText); + configuration.EngineDefaults.ViewResources.IsIterableUnbound = value; + } + break; + } + } + } + + private static void HandleDefaultsLogging(Configuration configuration, XmlElement parentElement) + { + foreach (var subElement in CreateElementEnumerable(parentElement.ChildNodes)) + { + switch (subElement.Name) + { + case "execution-path": + { + var valueText = GetRequiredAttribute(subElement, "enabled"); + var value = Boolean.Parse(valueText); + configuration.EngineDefaults.Logging.IsEnableExecutionDebug = value; + break; + } + case "timer-debug": + { + var valueText = GetRequiredAttribute(subElement, "enabled"); + var value = Boolean.Parse(valueText); + configuration.EngineDefaults.Logging.IsEnableTimerDebug = value; + break; + } + case "query-plan": + { + var valueText = GetRequiredAttribute(subElement, "enabled"); + var value = Boolean.Parse(valueText); + configuration.EngineDefaults.Logging.IsEnableQueryPlan = value; + break; + } + case "ado": + { + var valueText = GetRequiredAttribute(subElement, "enabled"); + var value = Boolean.Parse(valueText); + configuration.EngineDefaults.Logging.IsEnableADO = value; + break; + } + case "audit": + configuration.EngineDefaults.Logging.AuditPattern = GetOptionalAttribute( + subElement, "pattern"); + break; + } + } + } + + private static void HandleDefaultsVariables(Configuration configuration, XmlElement parentElement) + { + foreach (var subElement in CreateElementEnumerable(parentElement.ChildNodes)) + { + if (subElement.Name == "msec-version-release") + { + var valueText = GetRequiredAttribute(subElement, "value"); + long value = Int64.Parse(valueText); + configuration.EngineDefaults.Variables.MsecVersionRelease = value; + } + } + } + + private static void HandleDefaultsPatterns(Configuration configuration, XmlElement parentElement) + { + foreach (var subElement in CreateElementEnumerable(parentElement.ChildNodes)) + { + if (subElement.Name == "max-subexpression") + { + var valueText = GetRequiredAttribute(subElement, "value"); + long value = Int64.Parse(valueText); + configuration.EngineDefaults.Patterns.MaxSubexpressions = value; + + var preventText = GetOptionalAttribute(subElement, "prevent-start"); + if (preventText != null) + { + configuration.EngineDefaults.Patterns.IsMaxSubexpressionPreventStart = Boolean.Parse(preventText); + } + } + } + } + + private static void HandleDefaultsMatchRecognize(Configuration configuration, XmlElement parentElement) + { + foreach (var subElement in CreateElementEnumerable(parentElement.ChildNodes)) + { + if (subElement.Name == "max-state") + { + var valueText = GetRequiredAttribute(subElement, "value"); + var value = Int64.Parse(valueText); + configuration.EngineDefaults.MatchRecognize.MaxStates = value; + + var preventText = GetOptionalAttribute(subElement, "prevent-start"); + if (preventText != null) + { + configuration.EngineDefaults.MatchRecognize.IsMaxStatesPreventStart = Boolean.Parse(preventText); + } + } + } + } + + private static void HandleDefaultsStreamSelection(Configuration configuration, XmlElement parentElement) + { + foreach (var subElement in CreateElementEnumerable(parentElement.ChildNodes)) + { + if (subElement.Name == "stream-selector") + { + var valueText = GetRequiredAttribute(subElement, "value"); + if (valueText == null) + { + throw new ConfigurationException("No value attribute supplied for stream-selector element"); + } + StreamSelector defaultSelector; + if (string.Equals(valueText, "ISTREAM", StringComparison.InvariantCultureIgnoreCase)) + { + defaultSelector = StreamSelector.ISTREAM_ONLY; + } + else if (string.Equals(valueText, "RSTREAM", StringComparison.InvariantCultureIgnoreCase)) + { + defaultSelector = StreamSelector.RSTREAM_ONLY; + } + else if (string.Equals(valueText, "IRSTREAM", StringComparison.InvariantCultureIgnoreCase)) + { + defaultSelector = StreamSelector.RSTREAM_ISTREAM_BOTH; + } + else + { + throw new ConfigurationException( + "Value attribute for stream-selector element invalid, " + + "expected one of the following keywords: istream, irstream, rstream"); + } + configuration.EngineDefaults.StreamSelection.DefaultStreamSelector = defaultSelector; + } + } + } + + private static void HandleDefaultsTimeSource(Configuration configuration, XmlElement parentElement) + { + foreach (var subElement in CreateElementEnumerable(parentElement.ChildNodes)) + { + if (subElement.Name == "time-source-type") + { + var valueText = GetRequiredAttribute(subElement, "value"); + if (valueText == null) + { + throw new ConfigurationException("No value attribute supplied for time-source element"); + } + ConfigurationEngineDefaults.TimeSourceType timeSourceType; + if (string.Equals(valueText, "NANO", StringComparison.InvariantCultureIgnoreCase)) + { + timeSourceType = ConfigurationEngineDefaults.TimeSourceType.NANO; + } + else if (string.Equals(valueText, "MILLI" , StringComparison.InvariantCultureIgnoreCase)) + { + timeSourceType = ConfigurationEngineDefaults.TimeSourceType.MILLI; + } + else + { + throw new ConfigurationException( + "Value attribute for time-source element invalid, " + + "expected one of the following keywords: nano, milli"); + } + configuration.EngineDefaults.TimeSource.TimeSourceType = timeSourceType; + } + + if (subElement.Name == "time-unit") + { + var valueText = GetRequiredAttribute(subElement, "value"); + if (valueText == null) + { + throw new ConfigurationException("No value attribute supplied for time-unit element"); + } + try + { + TimeUnit timeUnit = EnumHelper.Parse(valueText); + configuration.EngineDefaults.TimeSource.TimeUnit = timeUnit; + } + catch(Exception ex) + { + throw new ConfigurationException( + "Value attribute for time-unit element invalid: " + ex.Message, ex); + } + } + } + } + + private static void HandleMetricsReporting(Configuration configuration, XmlElement parentElement) + { + var enabled = GetRequiredAttribute(parentElement, "enabled"); + var isEnabled = Boolean.Parse(enabled); + configuration.EngineDefaults.MetricsReporting.IsEnableMetricsReporting = isEnabled; + + var engineInterval = GetOptionalAttribute(parentElement, "engine-interval"); + if (engineInterval != null) + { + configuration.EngineDefaults.MetricsReporting.EngineInterval = Int64.Parse(engineInterval); + } + + var statementInterval = GetOptionalAttribute(parentElement, "statement-interval"); + if (statementInterval != null) + { + configuration.EngineDefaults.MetricsReporting.StatementInterval = Int64.Parse(statementInterval); + } + + var threading = GetOptionalAttribute(parentElement, "threading"); + if (threading != null) + { + configuration.EngineDefaults.MetricsReporting.IsThreading = Boolean.Parse(threading); + } + + foreach (var subElement in CreateElementEnumerable(parentElement.ChildNodes)) + { + if (subElement.Name == "stmtgroup") + { + var name = GetRequiredAttribute(subElement, "name"); + long interval = Int64.Parse(GetRequiredAttribute(subElement, "interval")); + + var metrics = new MetricsReportingConfig.StmtGroupMetrics(); + metrics.Interval = interval; + configuration.EngineDefaults.MetricsReporting.AddStmtGroup(name, metrics); + + var defaultInclude = GetOptionalAttribute(subElement, "default-include"); + if (defaultInclude != null) + { + metrics.IsDefaultInclude = Boolean.Parse(defaultInclude); + } + + var numStmts = GetOptionalAttribute(subElement, "num-stmts"); + if (numStmts != null) + { + metrics.NumStatements = Int32.Parse(numStmts); + } + + var reportInactive = GetOptionalAttribute(subElement, "report-inactive"); + if (reportInactive != null) + { + metrics.IsReportInactive = Boolean.Parse(reportInactive); + } + + HandleMetricsReportingPatterns(metrics, subElement); + } + } + } + + private static void HandleLanguage(Configuration configuration, XmlElement parentElement) + { + var sortUsingCollator = GetOptionalAttribute(parentElement, "sort-using-collator"); + if (sortUsingCollator != null) + { + var isSortUsingCollator = Boolean.Parse(sortUsingCollator); + configuration.EngineDefaults.Language.IsSortUsingCollator = isSortUsingCollator; + } + } + + private static void HandleExpression(Configuration configuration, XmlElement parentElement) + { + var integerDivision = GetOptionalAttribute(parentElement, "integer-division"); + if (integerDivision != null) + { + var isIntegerDivision = Boolean.Parse(integerDivision); + configuration.EngineDefaults.Expression.IsIntegerDivision = isIntegerDivision; + } + var divZero = GetOptionalAttribute(parentElement, "division-by-zero-is-null"); + if (divZero != null) + { + var isDivZero = Boolean.Parse(divZero); + configuration.EngineDefaults.Expression.IsDivisionByZeroReturnsNull = isDivZero; + } + var udfCache = GetOptionalAttribute(parentElement, "udf-cache"); + if (udfCache != null) + { + var isUdfCache = Boolean.Parse(udfCache); + configuration.EngineDefaults.Expression.IsUdfCache = isUdfCache; + } + var selfSubselectPreeval = GetOptionalAttribute(parentElement, "self-subselect-preeval"); + if (selfSubselectPreeval != null) + { + var isSelfSubselectPreeval = Boolean.Parse(selfSubselectPreeval); + configuration.EngineDefaults.Expression.IsSelfSubselectPreeval = isSelfSubselectPreeval; + } + var extendedAggregationStr = GetOptionalAttribute(parentElement, "extended-agg"); + if (extendedAggregationStr != null) + { + var extendedAggregation = Boolean.Parse(extendedAggregationStr); + configuration.EngineDefaults.Expression.IsExtendedAggregation = extendedAggregation; + } + var duckTypingStr = GetOptionalAttribute(parentElement, "ducktyping"); + if (duckTypingStr != null) + { + var duckTyping = Boolean.Parse(duckTypingStr); + configuration.EngineDefaults.Expression.IsDuckTyping = duckTyping; + } + var mathContextStr = GetOptionalAttribute(parentElement, "math-context"); + if (mathContextStr != null) + { + try + { + var mathContext = new MathContext(mathContextStr); + configuration.EngineDefaults.Expression.MathContext = mathContext; + } + catch (ArgumentException) + { + throw new ConfigurationException("Failed to parse '" + mathContextStr + "' as a MathContext"); + } + } + + var timeZoneStr = GetOptionalAttribute(parentElement, "time-zone"); + if (timeZoneStr != null) + { + TimeZoneInfo timeZone = TimeZoneHelper.GetTimeZoneInfo(timeZoneStr); + configuration.EngineDefaults.Expression.TimeZone = timeZone; + } + } + + private static void HandleExecution(Configuration configuration, XmlElement parentElement) + { + var prioritizedStr = GetOptionalAttribute(parentElement, "prioritized"); + if (prioritizedStr != null) + { + var isPrioritized = Boolean.Parse(prioritizedStr); + configuration.EngineDefaults.Execution.IsPrioritized = isPrioritized; + } + var fairlockStr = GetOptionalAttribute(parentElement, "fairlock"); + if (fairlockStr != null) + { + var isFairlock = Boolean.Parse(fairlockStr); + configuration.EngineDefaults.Execution.IsFairlock = isFairlock; + } + var disableLockingStr = GetOptionalAttribute(parentElement, "disable-locking"); + if (disableLockingStr != null) + { + var isDisablelock = Boolean.Parse(disableLockingStr); + configuration.EngineDefaults.Execution.IsDisableLocking = isDisablelock; + } + var threadingProfileStr = GetOptionalAttribute(parentElement, "threading-profile"); + if (threadingProfileStr != null) + { + var profile = + EnumHelper.Parse(threadingProfileStr); + configuration.EngineDefaults.Execution.ThreadingProfile = profile; + } + var filterServiceProfileStr = GetOptionalAttribute(parentElement, "filter-service-profile"); + if (filterServiceProfileStr != null) + { + var profile = + EnumHelper.Parse < ConfigurationEngineDefaults.FilterServiceProfile>(filterServiceProfileStr); + configuration.EngineDefaults.Execution.FilterServiceProfile = profile; + } + var filterServiceMaxFilterWidthStr = GetOptionalAttribute( + parentElement, "filter-service-max-filter-width"); + if (filterServiceMaxFilterWidthStr != null) + { + configuration.EngineDefaults.Execution.FilterServiceMaxFilterWidth = + Int32.Parse(filterServiceMaxFilterWidthStr); + } + var allowIsolatedServiceStr = GetOptionalAttribute(parentElement, "allow-isolated-service"); + if (allowIsolatedServiceStr != null) + { + var isAllowIsolatedService = Boolean.Parse(allowIsolatedServiceStr); + configuration.EngineDefaults.Execution.IsAllowIsolatedService = isAllowIsolatedService; + } + var declExprValueCacheSizeStr = GetOptionalAttribute(parentElement, "declared-expr-value-cache-size"); + if (declExprValueCacheSizeStr != null) + { + configuration.EngineDefaults.Execution.DeclaredExprValueCacheSize = + Int32.Parse(declExprValueCacheSizeStr); + } + } + + private static void HandleDefaultScriptConfig(Configuration configuration, XmlElement parentElement) + { + var defaultDialect = GetOptionalAttribute(parentElement, "default-dialect"); + if (defaultDialect != null) + { + configuration.EngineDefaults.Scripts.DefaultDialect = defaultDialect; + } + } + + private static IList GetHandlerFactories(XmlElement parentElement) + { + var list = new List(); + + foreach (var subElement in CreateElementEnumerable(parentElement.ChildNodes)) + { + if (subElement.Name == "handlerFactory") + { + var text = GetRequiredAttribute(subElement, "class"); + list.Add(text); + } + } + return list; + } + + private static void HandleMetricsReportingPatterns( + MetricsReportingConfig.StmtGroupMetrics groupDef, + XmlElement parentElement) + { + foreach (var subElement in CreateElementEnumerable(parentElement.ChildNodes)) + { + switch (subElement.Name) + { + case "include-regex": + { + var text = subElement.ChildNodes.Item(0).InnerText; + groupDef.Patterns.Add(new Pair(new StringPatternSetRegex(text), true)); + break; + } + case "exclude-regex": + { + var text = subElement.ChildNodes.Item(0).InnerText; + groupDef.Patterns.Add(new Pair(new StringPatternSetRegex(text), false)); + break; + } + case "include-like": + { + var text = subElement.ChildNodes.Item(0).InnerText; + groupDef.Patterns.Add(new Pair(new StringPatternSetLike(text), true)); + break; + } + case "exclude-like": + { + var text = subElement.ChildNodes.Item(0).InnerText; + groupDef.Patterns.Add(new Pair(new StringPatternSetLike(text), false)); + break; + } + } + } + } + + private static void HandleDefaultsEventMeta(Configuration configuration, XmlElement parentElement) + { + foreach (var subElement in CreateElementEnumerable(parentElement.ChildNodes)) + { + if (subElement.Name == "class-property-resolution") + { + var styleNode = subElement.Attributes.GetNamedItem("style"); + if (styleNode != null) + { + var styleText = styleNode.InnerText; + var value = EnumHelper.Parse(styleText); + configuration.EngineDefaults.EventMeta.ClassPropertyResolutionStyle = value; + } + + var accessorStyleNode = subElement.Attributes.GetNamedItem("accessor-style"); + if (accessorStyleNode != null) + { + var accessorStyleText = accessorStyleNode.InnerText; + var value = EnumHelper.Parse(accessorStyleText); + configuration.EngineDefaults.EventMeta.DefaultAccessorStyle = value; + } + } + + else if (subElement.Name == "event-representation") + { + var typeNode = subElement.Attributes.GetNamedItem("type"); + if (typeNode != null) + { + var typeText = typeNode.InnerText; + var value = EnumHelper.Parse(typeText); + configuration.EngineDefaults.EventMeta.DefaultEventRepresentation = value; + } + } + + else if (subElement.Name == "anonymous-cache") + { + var sizeNode = subElement.Attributes.GetNamedItem("size"); + if (sizeNode != null) + { + configuration.EngineDefaults.EventMeta.AnonymousCacheSize = + Int32.Parse(sizeNode.InnerText); + } + } + + else if (subElement.Name == "avro-settings") + { + var enableAvroStr = GetOptionalAttribute(subElement, "enable-avro"); + if (enableAvroStr != null) + { + configuration.EngineDefaults.EventMeta.AvroSettings.IsEnableAvro = + Boolean.Parse(enableAvroStr); + } + + var enableNativeStringStr = GetOptionalAttribute(subElement, "enable-native-string"); + if (enableNativeStringStr != null) + { + configuration.EngineDefaults.EventMeta.AvroSettings.IsEnableNativeString = + Boolean.Parse(enableNativeStringStr); + } + + var enableSchemaDefaultNonNullStr = GetOptionalAttribute( + subElement, "enable-schema-default-nonnull"); + if (enableSchemaDefaultNonNullStr != null) + { + configuration.EngineDefaults.EventMeta.AvroSettings.IsEnableSchemaDefaultNonNull = + Boolean.Parse(enableSchemaDefaultNonNullStr); + } + + var objectvalueTypewidenerFactoryClass = GetOptionalAttribute( + subElement, "objectvalue-typewidener-factory-class"); + if (objectvalueTypewidenerFactoryClass != null && + objectvalueTypewidenerFactoryClass.Trim().Length > 0) + { + configuration.EngineDefaults.EventMeta.AvroSettings.ObjectValueTypeWidenerFactoryClass = + objectvalueTypewidenerFactoryClass.Trim(); + } + + var typeRepresentationMapperClass = GetOptionalAttribute( + subElement, "type-representation-mapper-class"); + configuration.EngineDefaults.EventMeta.AvroSettings.TypeRepresentationMapperClass = + typeRepresentationMapperClass; + } + } + } + + private static Properties HandleProperties(XmlElement element, string propElementName) + { + var properties = new Properties(); + foreach (var subElement in CreateElementEnumerable(element.ChildNodes)) + { + if (subElement.Name.Equals(propElementName)) + { + var name = GetRequiredAttribute(subElement, "name"); + var value = GetRequiredAttribute(subElement, "value"); + properties.Put(name, value); + } + } + return properties; + } + + public static Properties CreatePropertiesFromAttributes(XmlAttributeCollection attributes) + { + var properties = new Properties(); + foreach (XmlAttribute attribute in attributes) + { + switch (attribute.Name) + { + case "type": + break; + default: + properties[attribute.Name] = attribute.InnerText; + break; + } + } + return properties; + } + + /// + /// Returns an input stream from an application resource in the classpath. + /// + /// to get input stream for + /// input stream for resource + public static Stream GetResourceAsStream(String resource) + { + var stripped = resource.StartsWith("/") ? resource.Substring(1) : resource; + var stream = ResourceManager.GetResourceAsStream(resource) ?? + ResourceManager.GetResourceAsStream(stripped); + if (stream == null) + { + throw new EPException(resource + " not found"); + } + return stream; + } + + private static void WhenOptionalAttribute(XmlNode node, String key, Action action) + { + var valueNode = node.Attributes.GetNamedItem(key); + if (valueNode != null) + { + action.Invoke(valueNode.InnerText); + } + } + + private static String GetOptionalAttribute(XmlNode node, String key) + { + var valueNode = node.Attributes.GetNamedItem(key); + if (valueNode != null) + { + return valueNode.InnerText; + } + return null; + } + + private static String GetRequiredAttribute(XmlNode node, String key) + { + var valueNode = node.Attributes.GetNamedItem(key); + if (valueNode == null) + { + var name = String.IsNullOrEmpty(node.Name) + ? node.LocalName + : node.Name; + throw new ConfigurationException( + "Required attribute by name '" + key + "' not found for element '" + name + "'"); + } + return valueNode.InnerText; + } + + private static IList CreateElementList(XmlNodeList nodeList) + { + return new List(CreateElementEnumerable(nodeList)); + } + + private static IEnumerable CreateElementEnumerable(XmlNodeList nodeList) + { + foreach (XmlNode node in nodeList) + { + if (node is XmlElement) + { + yield return node as XmlElement; + } + } + } + + + private class ThreadPoolConfig + { + public ThreadPoolConfig(bool enabled, int numThreads, int? capacity) + { + IsEnabled = enabled; + NumThreads = numThreads; + Capacity = capacity; + } + + public bool IsEnabled { get; private set; } + + public int NumThreads { get; private set; } + + public int? Capacity { get; private set; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/client/ConfigurationPlugInAggregationFunction.cs b/NEsper.Core/NEsper.Core/client/ConfigurationPlugInAggregationFunction.cs new file mode 100755 index 000000000..7f474507d --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/ConfigurationPlugInAggregationFunction.cs @@ -0,0 +1,82 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client +{ + /// Configuration information for plugging in a custom aggregation function. + [Serializable] + public class ConfigurationPlugInAggregationFunction + { + /// + /// Ctor. + /// + public ConfigurationPlugInAggregationFunction() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The name. + /// Name of the factory class. + public ConfigurationPlugInAggregationFunction(String name, String factoryClassName) + { + Name = name; + FactoryClassName = factoryClassName; + } + + /// Returns the aggregation function name. + /// aggregation function name + public string Name { get; set; } + + /// Returns the class name of the aggregation function factory class. + /// class name + public string FactoryClassName { get; set; } + + public bool Equals(ConfigurationPlugInAggregationFunction other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return Equals(other.Name, Name) && Equals(other.FactoryClassName, FactoryClassName); + } + + /// + /// Determines whether the specified is equal to the current . + /// + /// + /// true if the specified is equal to the current ; otherwise, false. + /// + /// The to compare with the current . 2 + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != typeof (ConfigurationPlugInAggregationFunction)) return false; + return Equals((ConfigurationPlugInAggregationFunction) obj); + } + + /// + /// Serves as a hash function for a particular type. + /// + /// + /// A hash code for the current . + /// + /// 2 + public override int GetHashCode() + { + unchecked + { + int result = (Name != null ? Name.GetHashCode() : 0); + result = (result*397) ^ (FactoryClassName != null ? FactoryClassName.GetHashCode() : 0); + return result; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/ConfigurationPlugInAggregationMultiFunction.cs b/NEsper.Core/NEsper.Core/client/ConfigurationPlugInAggregationMultiFunction.cs new file mode 100755 index 000000000..0a0019c33 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/ConfigurationPlugInAggregationMultiFunction.cs @@ -0,0 +1,46 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.client +{ + /// + /// Configuration information for plugging in a custom aggregation multi-function. + /// + [Serializable] + public class ConfigurationPlugInAggregationMultiFunction + { + /// Ctor. + public ConfigurationPlugInAggregationMultiFunction() + { + } + + /// Ctor. + /// the aggregation function names + /// the factory class name + public ConfigurationPlugInAggregationMultiFunction(String[] functionNames, String multiFunctionFactoryClassName) + { + FunctionNames = functionNames; + MultiFunctionFactoryClassName = multiFunctionFactoryClassName; + } + + /// Returns aggregation function names. + /// names + public string[] FunctionNames { get; set; } + + /// Returns the factory class name. + /// class name + public string MultiFunctionFactoryClassName { get; set; } + + /// Returns a map of optional configuration properties, or null if none provided. + /// additional optional properties + public IDictionary AdditionalConfiguredProperties { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/ConfigurationPlugInEventRepresentation.cs b/NEsper.Core/NEsper.Core/client/ConfigurationPlugInEventRepresentation.cs new file mode 100755 index 000000000..9f2dc5e7e --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/ConfigurationPlugInEventRepresentation.cs @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client +{ + /// + /// Configuration object for plug-in event representations. + /// + [Serializable] + public class ConfigurationPlugInEventRepresentation + { + private String eventRepresentationTypeName; + private Object initializer; + + /// + /// Gets or sets the class name of the class providing the pluggable event representation. + /// + /// The name of the event representation type. + /// class name of class implementing + public string EventRepresentationTypeName + { + get { return eventRepresentationTypeName; } + set { eventRepresentationTypeName = value; } + } + + /// + /// Gets or sets the optional initialization or configuration information for the plug-in event + /// representation. + /// + /// The initializer. + /// + /// any configuration object specific to the event representation, or a XML string + /// if supplied via configuration XML file, or null if none supplied + /// + public Object Initializer + { + get { return initializer; } + set { initializer = value; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/ConfigurationPlugInEventType.cs b/NEsper.Core/NEsper.Core/client/ConfigurationPlugInEventType.cs new file mode 100755 index 000000000..bb4d42371 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/ConfigurationPlugInEventType.cs @@ -0,0 +1,33 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.client +{ + /// + /// Configuration for a plug-in event type, which is an event type resolved via + /// plug-in event representation. + /// + [Serializable] + public class ConfigurationPlugInEventType + { + /// + /// Gets or sets the URIs to use to resolve the new event type against the plug-in event representations registered. + /// + /// The event representation resolution URI is. + public IList EventRepresentationResolutionURIs { get; set; } + + /// + /// Gets or sets the optional initialization information that the plug-in event representation may use to set up the event type. + /// + /// The initializer. + public object Initializer { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/ConfigurationPlugInPatternObject.cs b/NEsper.Core/NEsper.Core/client/ConfigurationPlugInPatternObject.cs new file mode 100755 index 000000000..fd207d039 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/ConfigurationPlugInPatternObject.cs @@ -0,0 +1,70 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client +{ + /// + /// Configuration information for plugging in a custom view. + /// + [Serializable] + public class ConfigurationPlugInPatternObject + { + private String _namespace; + private String name; + private String factoryClassName; + private PatternObjectTypeEnum? patternObjectType; + + /// + /// Gets or sets the view namespace + /// + public String Namespace + { + get { return _namespace; } + set { _namespace = value; } + } + + /// + /// Gets or sets the view name. + /// + public String Name + { + get { return name; } + set { this.name = value ; } + } + + /// + /// Gets or sets the view factory class name. + /// + public String FactoryClassName + { + get { return factoryClassName; } + set { this.factoryClassName = value ; } + } + + /// + /// Gets or sets the type of the pattern object for the plug-in. + /// + public PatternObjectTypeEnum? PatternObjectType + { + get { return patternObjectType; } + set { this.patternObjectType = value; } + } + + /// Choice for type of pattern object. + public enum PatternObjectTypeEnum + { + /// Observer observes externally-supplied events. + OBSERVER, + + /// Guard allows or disallows events from child expressions to pass. + GUARD + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/ConfigurationPlugInSingleRowFunction.cs b/NEsper.Core/NEsper.Core/client/ConfigurationPlugInSingleRowFunction.cs new file mode 100755 index 000000000..2904cdb76 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/ConfigurationPlugInSingleRowFunction.cs @@ -0,0 +1,137 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client +{ + /// + /// Configuration information for plugging in a custom single-row function. + /// + [Serializable] + public class ConfigurationPlugInSingleRowFunction + { + /// + /// Ctor. + /// + /// UDF name + /// class name + /// method name + /// value cache + /// optimizable setting + /// rethrow setting + /// optional event type name + public ConfigurationPlugInSingleRowFunction( + string name, + string functionClassName, + string functionMethodName, + ValueCacheEnum valueCache, + FilterOptimizableEnum filterOptimizable, + bool rethrowExceptions, + string eventTypeName) + { + Name = name; + FunctionClassName = functionClassName; + FunctionMethodName = functionMethodName; + ValueCache = valueCache; + FilterOptimizable = filterOptimizable; + IsRethrowExceptions = rethrowExceptions; + EventTypeName = eventTypeName; + } + + /// Ctor. + public ConfigurationPlugInSingleRowFunction() + { + IsRethrowExceptions = false; + FilterOptimizable = FilterOptimizableEnum.ENABLED; + ValueCache = ValueCacheEnum.DISABLED; + } + + /// + /// Returns the single-row function name for use in EPL. + /// + /// single-row function name + public string Name { get; set; } + + /// + /// Returns the single-row function name. + /// + /// name + public string FunctionClassName { get; set; } + + /// + /// Returns the name of the single-row function. + /// + /// function name + public string FunctionMethodName { get; set; } + + /// + /// Returns the setting for the cache behavior. + /// + /// cache behavior + public ValueCacheEnum ValueCache { get; set; } + + /// + /// Returns filter optimization settings. + /// + /// filter optimization settings + public FilterOptimizableEnum FilterOptimizable { get; set; } + + /// + /// Returns indicator whether the engine re-throws exceptions + /// thrown by the single-row function. The default is false + /// therefore the engine by default does not rethrow exceptions. + /// + /// indicator + public bool IsRethrowExceptions { get; set; } + + /// + /// Returns the event type name for functions that return instances. + /// + /// event type name + public string EventTypeName { get; set; } + } + + /// + /// Controls whether a single-row function is eligible for optimization if it occurs in a filter expression. + /// + public enum FilterOptimizableEnum + { + /// + /// The engine does not consider the single-row function for optimizing evaluation: The function gets evaluated for + /// each event possibly multiple times. + /// + DISABLED, + + /// + /// The engine considers the single-row function for optimizing evaluation: The function gets evaluated only once per + /// event. + /// + ENABLED + } + + /// Enum for single-row function value cache setting. + public enum ValueCacheEnum + { + /// + /// The default, the result of a single-row function is always computed anew. + /// + DISABLED, + + /// + /// Causes the engine to not actually invoke the single-row function and instead return a cached precomputed value + /// when all parameters are constants or there are no parameters. + /// + ENABLED, + + /// + /// Causes the engine to follow the engine-wide policy as configured for user-defined functions. + /// + CONFIGURED + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/ConfigurationPlugInView.cs b/NEsper.Core/NEsper.Core/client/ConfigurationPlugInView.cs new file mode 100755 index 000000000..1a3e331f7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/ConfigurationPlugInView.cs @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client +{ + /// + /// Configuration information for plugging in a custom view. + /// + + [Serializable] + public class ConfigurationPlugInView + { + /// Gets or sets the namespace + /// namespace + public String Namespace { get; set; } + + /// Gets or sets the view name. + /// view name + public String Name { get; set; } + + /// Gets or sets the view factory class name. + /// factory class name + public String FactoryClassName { get; set; } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/client/ConfigurationPlugInVirtualDataWindow.cs b/NEsper.Core/NEsper.Core/client/ConfigurationPlugInVirtualDataWindow.cs new file mode 100755 index 000000000..77abe2d86 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/ConfigurationPlugInVirtualDataWindow.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client +{ + /// + /// Configuration information for plugging in a custom view. + /// + [Serializable] + public class ConfigurationPlugInVirtualDataWindow + { + /// Returns the namespace + /// namespace + public string Namespace { get; set; } + + /// Returns the view name. + /// view name + public string Name { get; set; } + + /// Returns the view factory class name. + /// factory class name + public string FactoryClassName { get; set; } + + /// + /// Returns any additional configuration passed to the factory as part of the context. + /// + /// The config. + public object Config { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/ConfigurationPluginLoader.cs b/NEsper.Core/NEsper.Core/client/ConfigurationPluginLoader.cs new file mode 100755 index 000000000..5c459bb48 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/ConfigurationPluginLoader.cs @@ -0,0 +1,56 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using com.espertech.esper.compat; + +namespace com.espertech.esper.client +{ + /// + /// Holds configuration for a plugin such as an input/output + /// adapter loader. + /// + [Serializable] + public class ConfigurationPluginLoader + { + /// + /// Gets or sets the loader class name. + /// + /// The name of the class. + public string TypeName { get; set; } + + /// + /// Gets or sets the config properties. + /// + /// The config properties. + public Properties ConfigProperties { get; set; } + + /// + /// Gets or sets the configuration XML. + /// + /// The configuration XML. + public string ConfigurationXML { get; set; } + + /// + /// Gets or sets the name of the loader. + /// + /// The name of the loader. + public string LoaderName { get; set; } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override string ToString() + { + return string.Format("MaskTypeName: {0}, ConfigProperties: {1}, LoaderName: {2}", TypeName, ConfigProperties, LoaderName); + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/ConfigurationRevisionEventType.cs b/NEsper.Core/NEsper.Core/client/ConfigurationRevisionEventType.cs new file mode 100755 index 000000000..a55efdd6d --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/ConfigurationRevisionEventType.cs @@ -0,0 +1,170 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.client +{ + /// + /// Configuration information for revision event types. + /// + /// The configuration information consists of the names of the base event type and + /// the delta event types, as well as the names of properties that supply key values, + /// and a strategy. + /// + /// Events of the base event type arrive before delta events; Delta events arriving + /// before the base event for the same key value are not processed, as delta events + /// as well as base events represent new versions. + /// + [Serializable] + public class ConfigurationRevisionEventType + { + private readonly ICollection nameBaseEventTypes; + private readonly ICollection nameDeltaEventTypes; + + /// + /// Ctor. + /// + public ConfigurationRevisionEventType() + { + nameBaseEventTypes = new HashSet(); + nameDeltaEventTypes = new HashSet(); + PropertyRevision = PropertyRevisionEnum.OVERLAY_DECLARED; + } + + /// + /// Add a base event type by it's name. + /// + /// the name of the base event type to add + public void AddNameBaseEventType(String nameBaseEventType) + { + nameBaseEventTypes.Add(nameBaseEventType); + } + + /// + /// Returns the set of event type names that are base event types. + /// + /// + /// names of base event types + /// + public ICollection NameBaseEventTypes + { + get { return nameBaseEventTypes; } + } + + /// + /// Returns the set of names of delta event types. + /// + /// + /// names of delta event types + /// + public ICollection NameDeltaEventTypes + { + get { return nameDeltaEventTypes; } + } + + /// + /// Add a delta event type by it's name. + /// + /// the name of the delta event type to add + public void AddNameDeltaEventType(String nameDeltaEventType) + { + nameDeltaEventTypes.Add(nameDeltaEventType); + } + + /// + /// Returns the enumeration value defining the strategy to use for overlay or + /// merging multiple versions of an event (instances of base and delta events). + /// + /// + /// strategy enumerator + /// + public PropertyRevisionEnum PropertyRevision { get; set; } + + /// + /// Returns the key property names, which are the names of the properties that + /// supply key values for relating base and delta events. + /// + /// + /// array of names of key properties + /// + public string[] KeyPropertyNames { get; set; } + } + + /// + /// Enumeration for specifying a strategy to use to merge or overlay properties. + /// + public enum PropertyRevisionEnum + { + /// + /// A fast strategy for revising events that groups properties provided by base and + /// delta events and overlays contributed properties to compute a revision. + /// + /// For use when there is a limited number of combinations of properties that + /// change on an event, and such combinations are known in advance. + /// + /// The properties available on the output revision events are all properties of + /// the base event type. Delta event types do not add any additional properties that + /// are not present on the base event type. + /// + /// Any null values or non-existing property on a delta (or base) event results in + /// a null values for the same property on the output revision event. + /// + OVERLAY_DECLARED, + + /// + /// A strategy for revising events by merging properties provided by base and delta + /// events, considering null values and non-existing (dynamic) properties as well. + /// + /// For use when there is a limited number of combinations of properties that + /// change on an event, and such combinations are known in advance. + /// + /// The properties available on the output revision events are all properties of + /// the base event type plus all additional properties that any of the delta event + /// types provide. + /// + /// Any null values or non-existing property on a delta (or base) event results in + /// a null values for the same property on the output revision event. + /// + MERGE_DECLARED, + + /// + /// A strategy for revising events by merging properties provided by base and delta + /// events, considering only non-null values. + /// + /// For use when there is an unlimited number of combinations of properties that + /// change on an event, or combinations are not known in advance. + /// + /// The properties available on the output revision events are all properties of + /// the base event type plus all additional properties that any of the delta event + /// types provide. + /// + /// Null values returned by delta (or base) event properties provide no value to + /// output revision events, i.e. null values are not merged. + /// + MERGE_NON_NULL, + + /// + /// A strategy for revising events by merging properties provided by base and delta + /// events, considering only values supplied by event properties that exist. + /// + /// For use when there is an unlimited number of combinations of properties that + /// change on an event, or combinations are not known in advance. + /// + /// The properties available on the output revision events are all properties of + /// the base event type plus all additional properties that any of the delta event + /// types provide. + /// + /// All properties are treated as dynamic properties: If an event property does not + /// exist on a delta event (or base) event the property provides no value to output + /// revision events, i.e. non-existing property values are not merged. + /// + MERGE_EXISTS + } +} diff --git a/NEsper.Core/NEsper.Core/client/ConfigurationVariable.cs b/NEsper.Core/NEsper.Core/client/ConfigurationVariable.cs new file mode 100755 index 000000000..bdfbcf9fd --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/ConfigurationVariable.cs @@ -0,0 +1,39 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client +{ + /// + /// Provides variable configuration. + /// + [Serializable] + public class ConfigurationVariable + { + /// + /// Gets or sets the variable type. + /// + /// variable type + public string VariableType { get; set; } + + /// + /// Gets or sets the initialization value, or null if none was supplied. + /// String-type initialization values for numeric or bool types are allowed and are parsed. + /// Variables are scalar values and primitive or boxed builtin types are accepted. + /// + /// default value + public object InitializationValue { get; set; } + + /// + /// Gets or sets a value indicating whether this is constant. + /// + /// true if constant; otherwise, false. + public bool IsConstant { get; set; } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/client/ConfigurationVariantStream.cs b/NEsper.Core/NEsper.Core/client/ConfigurationVariantStream.cs new file mode 100755 index 000000000..ead2d0ab7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/ConfigurationVariantStream.cs @@ -0,0 +1,73 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.client +{ + /// + /// Configures a variant stream. + /// + [Serializable] + public class ConfigurationVariantStream + { + /// + /// Ctor. + /// + public ConfigurationVariantStream() + { + VariantTypeNames = new List(); + TypeVariance = TypeVarianceEnum.PREDEFINED; + } + + /// + /// Returns the type variance setting specifying whether the variant stream accepts + /// event of only the predefined types or any type. + /// + /// + /// type variance setting + /// + public TypeVarianceEnum TypeVariance { get; set; } + + /// + /// Returns the names of event types that a predefined for the variant stream. + /// + /// + /// predefined types in the variant stream + /// + public IList VariantTypeNames { get; private set; } + + /// + /// Adds names of an event types that is one of the predefined event typs allowed + /// for the variant stream. + /// + /// name of the event type to allow in the variant stream + public void AddEventTypeName(String eventTypeName) + { + VariantTypeNames.Add(eventTypeName); + } + } + + /// + /// Enumeration specifying whether only the predefine types or any type of event is + /// accepted by the variant stream. + /// + public enum TypeVarianceEnum + { + /// + /// Allow only the predefined types to be inserted into the stream. + /// + PREDEFINED, + + /// + /// Allow any types to be inserted into the stream. + /// + ANY + } +} diff --git a/NEsper.Core/NEsper.Core/client/ConnectionFactoryDesc.cs b/NEsper.Core/NEsper.Core/client/ConnectionFactoryDesc.cs new file mode 100755 index 000000000..61ef3f0d8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/ConnectionFactoryDesc.cs @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +namespace com.espertech.esper.client +{ + /// + /// Marker for different connection factory settings. + /// + public interface ConnectionFactoryDesc + { + } + + +} diff --git a/NEsper.Core/NEsper.Core/client/ConnectionLifecycleEnum.cs b/NEsper.Core/NEsper.Core/client/ConnectionLifecycleEnum.cs new file mode 100755 index 000000000..66483ad9f --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/ConnectionLifecycleEnum.cs @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +namespace com.espertech.esper.client +{ + /// + /// Enum controlling connection lifecycle. + /// + + public enum ConnectionLifecycleEnum + { + /// Retain connection between lookups, not getting a new connection each lookup. + RETAIN, + /// Obtain a new connection each lookup closing the connection when done. + POOLED + } +} diff --git a/NEsper.Core/NEsper.Core/client/ConnectionSettings.cs b/NEsper.Core/NEsper.Core/client/ConnectionSettings.cs new file mode 100755 index 000000000..5e700723f --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/ConnectionSettings.cs @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Data; + +namespace com.espertech.esper.client +{ + /// + /// Supplies connection-level settings for a given database name. + /// + + [Serializable] + public class ConnectionSettings + { + //private bool? readOnly; + + /// Returns a bool indicating auto-commit, or null if not set and default accepted. + /// true for auto-commit on, false for auto-commit off, or null to accept the default + /// + /// Indicates whether to set any new connections for this database to auto-commit. + public bool? AutoCommit { get; set; } + + /// Gets the name of the catalog to set on new database connections, or null for default. + /// name of the catalog to set, or null to accept the default + /// + /// Sets the name of the catalog on new database connections. + public string Catalog { get; set; } + + /// Returns the connection settings for transaction isolation level. + /// transaction isolation level + /// + /// + /// Sets the transaction isolation level for new database connections, + /// can be null to accept the default. + /// + public IsolationLevel? TransactionIsolation { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/DataCacheDesc.cs b/NEsper.Core/NEsper.Core/client/DataCacheDesc.cs new file mode 100755 index 000000000..9ed7d8e6c --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/DataCacheDesc.cs @@ -0,0 +1,101 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client +{ + /// + /// Marker for different cache settings. + /// + + public interface DataCacheDesc + { + } + + /// + /// LRU cache settings. + /// + [Serializable] + public class LRUCacheDesc : DataCacheDesc + { + /// Returns the maximum cache size. + /// max cache size + /// + public int Size { get; private set; } + + /// Ctor. + /// is the maximum cache size + /// + public LRUCacheDesc(int size) + { + Size = size; + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override String ToString() + { + return "LRUCacheDesc size=" + Size; + } + } + + /// + /// Expiring cache settings. + /// + + [Serializable] + public class ExpiryTimeCacheDesc : DataCacheDesc + { + /// Returns the maximum age in seconds. + /// number of seconds + /// + public double MaxAgeSeconds { get; private set; } + + /// Returns the purge interval length. + /// purge interval in seconds + /// + public double PurgeIntervalSeconds { get; private set; } + + /// + /// Returns the enumeration whether hard, soft or weak reference type are used + /// to control whether the garbage collection can remove entries from cache. + /// + /// The type of the cache reference. + public ConfigurationCacheReferenceType ConfigurationCacheReferenceType { get; private set; } + + /// + /// Ctor. + /// + /// is the maximum age in seconds + /// is the purge interval + /// cacheReferenceType the reference type may allow garbage collection to remove entries from + /// cache unless HARD reference type indicates otherwise + public ExpiryTimeCacheDesc(double maxAgeSeconds, double purgeIntervalSeconds, ConfigurationCacheReferenceType configurationCacheReferenceType) + { + MaxAgeSeconds = maxAgeSeconds; + PurgeIntervalSeconds = purgeIntervalSeconds; + ConfigurationCacheReferenceType = configurationCacheReferenceType; + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override String ToString() + { + return "ExpiryTimeCacheDesc maxAgeSeconds=" + MaxAgeSeconds + " purgeIntervalSeconds=" + PurgeIntervalSeconds; + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/DbDriverConfiguration.cs b/NEsper.Core/NEsper.Core/client/DbDriverConfiguration.cs new file mode 100755 index 000000000..615d69d00 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/DbDriverConfiguration.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using com.espertech.esper.compat; + +namespace com.espertech.esper.client +{ + public class DbDriverConfiguration + { + /// + /// Gets or sets the name of the driver. + /// + /// The name of the driver. + public string DriverName { get; set; } + + /// + /// Gets or sets the properties. + /// + /// The properties. + public Properties Properties { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + public DbDriverConfiguration() + { + DriverName = null; + Properties = new Properties(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/DbDriverFactoryConnection.cs b/NEsper.Core/NEsper.Core/client/DbDriverFactoryConnection.cs new file mode 100755 index 000000000..c7cf155ad --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/DbDriverFactoryConnection.cs @@ -0,0 +1,144 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.epl.db; +using com.espertech.esper.util; + +namespace com.espertech.esper.client +{ + /// + /// Connection factory settings for using a DbDriverFactory. + /// + + [Serializable] + public class DbDriverFactoryConnection : ConnectionFactoryDesc + { + /// + /// Database driver. + /// + private readonly DbDriver _driver; + + /// + /// Gets the database driver. + /// + /// The database driver. + public virtual DbDriver Driver + { + get { return _driver; } + } + + /// + /// Where do drivers in the default search path reside + /// + + public const string driverNamespace = "com.espertech.esper.epl.db.drivers"; + + /// + /// Resolves the driver type from the name provided. If the driver can not be + /// resolved, the method throws an exception to indicate that one could not + /// be found. The method first looks for a class that matches the name of + /// the driver. If one can not be found, it checks in the com.espertech.esper.epl.drivers + /// namespace to see if one can be found. Lastly, if checks in the + /// com.espertech.espers.eql.drivers namespace with a variation of the given driver name. + /// + /// Name of the driver. + /// + public static Type ResolveDriverTypeFromName(String driverName) + { + Type driverType; + + // Check for the type with no modifications + if ((driverType = TypeHelper.ResolveType(driverName, false)) != null) + { + return driverType; + } + + // Check for the type in the driverNamespace + String specificName = String.Format("{0}.{1}", driverNamespace, driverName); + if ((driverType = TypeHelper.ResolveType(specificName, false)) != null) + { + return driverType; + } + + // Check for the type in the driverNamespace, but modified to include + // a prefix to the name. + String pseudoName = String.Format("{0}.DbDriver{1}", driverNamespace, driverName); + if ((driverType = TypeHelper.ResolveType(pseudoName, false)) != null) + { + return driverType; + } + + // Driver was not found, throw an exception + throw new EPException("Unable to resolve type for driver '" + driverName + "'"); + } + + /// + /// Resolves the driver from the name. + /// + /// Name of the driver. + /// + public static DbDriver ResolveDriverFromName(String driverName) + { + Type driverType = ResolveDriverTypeFromName(driverName); + return ResolveDriverFromType(driverType); + } + + /// + /// Resolves the driver from the type. + /// + /// Type of the driver. + /// + public static DbDriver ResolveDriverFromType(Type driverType) + { + if (typeof(DbDriver).IsAssignableFrom(driverType)) + { + return Activator.CreateInstance(driverType) as DbDriver; + } + + throw new EPException("Unable to create driver because it was not assignable from " + + typeof(DbDriver).FullName); + } + + /// + /// Initializes a new instance of the class. + /// + /// Type of the driver. + /// The properties. + public DbDriverFactoryConnection(Type driverType, Properties properties) + { + _driver = ResolveDriverFromType(driverType); + _driver.Properties = properties; + } + + /// + /// Ctor. + /// + /// Name of the driver. + /// Properties that should be applied to the connection. + + public DbDriverFactoryConnection(String driverName, Properties properties) + { + _driver = ResolveDriverFromName(driverName); + _driver.Properties = properties; + } + + /// + /// Initializes a new instance of the class. + /// + /// The db specification. + public DbDriverFactoryConnection(DbDriverConfiguration dbSpecification) + { + _driver = ResolveDriverFromName(dbSpecification.DriverName); + _driver.Properties = dbSpecification.Properties; + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/Directory.cs b/NEsper.Core/NEsper.Core/client/Directory.cs new file mode 100755 index 000000000..cb9df7469 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/Directory.cs @@ -0,0 +1,66 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.client +{ + public interface Directory : IDisposable + { + /// + /// Lookup an object by name. + /// + /// + /// + + Object Lookup(string name) ; + + /// + /// Bind an object to a name. Throws an exception if + /// the name is already bound. + /// + /// + /// + + void Bind(string name, Object obj); + + /// + /// Bind an object to a name. If the object is already + /// bound, rebind it. + /// + /// + /// + + void Rebind(string name, Object obj); + + /// + /// Unbind the object at the given name. + /// + /// + + void Unbind(string name) ; + + /// + /// Rename the object at oldName with newName. + /// + /// + /// + + void Rename(String oldName, String newName); + + /// + /// Enumerates the names bound in the named context. + /// + /// + /// + + IEnumerator List(string name); + } +} diff --git a/NEsper.Core/NEsper.Core/client/DirectoryException.cs b/NEsper.Core/NEsper.Core/client/DirectoryException.cs new file mode 100755 index 000000000..e340778d5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/DirectoryException.cs @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; + +namespace com.espertech.esper.client +{ + public class DirectoryException : Exception + { + /// + /// Initializes a new instance of the class. + /// + public DirectoryException() { } + /// + /// Initializes a new instance of the class. + /// + /// The message. + public DirectoryException( string message ) : base( message ) { } + + } +} diff --git a/NEsper.Core/NEsper.Core/client/DirectoryFactory.cs b/NEsper.Core/NEsper.Core/client/DirectoryFactory.cs new file mode 100755 index 000000000..94ca4dddb --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/DirectoryFactory.cs @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace com.espertech.esper.client +{ + public interface DirectoryFactory : IDisposable + { + } +} diff --git a/NEsper.Core/NEsper.Core/client/EPAdministrator.cs b/NEsper.Core/NEsper.Core/client/EPAdministrator.cs new file mode 100755 index 000000000..6311be2e7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/EPAdministrator.cs @@ -0,0 +1,333 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client.context; +using com.espertech.esper.client.deploy; +using com.espertech.esper.client.soda; + +namespace com.espertech.esper.client +{ + /// + /// Administrative interface to the event stream processing engine. Includes methods + /// to create patterns and EPL statements. + /// + public interface EPAdministrator + { + /// + /// Returns deployment administrative services. + /// + /// deployment administration + EPDeploymentAdmin DeploymentAdmin { get; } + + /// + /// Create and starts an event pattern statement for the expressing string passed. + /// + /// The engine assigns a unique name to the statement. + /// + /// must follow the documented syntax for pattern statements + /// + /// EPStatement to poll data from or to add listeners to + /// + /// EPException when the expression was not valid + EPStatement CreatePattern(String onExpression); + + /// + /// Creates and starts an EPL statement. + /// + /// The engine assigns a unique name to the statement. The returned statement is in + /// started state. + /// + /// is the query language statement + /// + /// EPStatement to poll data from or to add listeners to + /// + /// EPException when the expression was not valid + EPStatement CreateEPL(String eplStatement); + + /// + /// Create and starts an event pattern statement for the expressing string passed + /// and assign the name passed. + /// + /// The statement name is optimally a unique name. If a statement of the same name + /// has already been created, the engine assigns a postfix to create a unique + /// statement name. + /// + /// must follow the documented syntax for pattern statements + /// is the name to assign to the statement for use in managing the statement + /// + /// EPStatement to poll data from or to add listeners to + /// + /// EPException when the expression was not valid + EPStatement CreatePattern(String onExpression, String statementName); + + /// + /// Create and starts an event pattern statement for the expressing string passed + /// and assign the name passed. + /// + /// The statement name is optimally a unique name. If a statement of the same name + /// has already been created, the engine assigns a postfix to create a unique + /// statement name. + /// + /// Accepts an application defined user data object associated with the statement. + /// The user object is a single, unnamed field that is stored with every + /// statement. Applications may put arbitrary objects in this field or a null value. + /// + /// must follow the documented syntax for pattern statements + /// is the name to assign to the statement for use in managing the statement + /// is the application-defined user object + /// + /// EPStatement to poll data from or to add listeners to + /// + /// EPException when the expression was not valid + EPStatement CreatePattern(String onExpression, String statementName, Object userObject); + + /// + /// Create and starts an event pattern statement for the expressing string passed + /// and assign the name passed. + /// + /// Accepts an application defined user data object associated with the statement. + /// The user object is a single, unnamed field that is stored with every + /// statement. Applications may put arbitrary objects in this field or a null value. + /// + /// must follow the documented syntax for pattern statements + /// is the application-defined user object + /// + /// EPStatement to poll data from or to add listeners to + /// + /// EPException when the expression was not valid + EPStatement CreatePattern(String onExpression, Object userObject); + + /// + /// Create and starts an EPL statement. + /// + /// The statement name is optimally a unique name. If a statement of the same name + /// has already been created, the engine assigns a postfix to create a unique + /// statement name. + /// + /// is the query language statement + /// is the name to assign to the statement for use in managing the statement + /// + /// EPStatement to poll data from or to add listeners to + /// + /// EPException when the expression was not valid + EPStatement CreateEPL(String eplStatement, String statementName); + + /// + /// Create and starts an EPL statement. + /// + /// The statement name is optimally a unique name. If a statement of the same name + /// has already been created, the engine assigns a postfix to create a unique + /// statement name. + /// + /// Accepts an application defined user data object associated with the statement. + /// The user object is a single, unnamed field that is stored with every + /// statement. Applications may put arbitrary objects in this field or a null value. + /// + /// is the query language statement + /// is the name to assign to the statement for use in managing the statement + /// is the application-defined user object + /// + /// EPStatement to poll data from or to add listeners to + /// + /// EPException when the expression was not valid + EPStatement CreateEPL(String eplStatement, String statementName, Object userObject); + + /// + /// Create and starts an EPL statement. + /// + /// Accepts an application defined user data object associated with the statement. + /// The user object is a single, unnamed field that is stored with every + /// statement. Applications may put arbitrary objects in this field or a null value. + /// + /// is the query language statement + /// is the application-defined user object + /// + /// EPStatement to poll data from or to add listeners to + /// + /// EPException when the expression was not valid + EPStatement CreateEPL(String eplStatement, Object userObject); + + /// + /// Creates and starts an EPL statement. + /// + /// The statement name is optimally a unique name. If a statement of the same name + /// has already been created, the engine assigns a postfix to create a unique + /// statement name. + /// + /// is the statement object model + /// is the name to assign to the statement for use in managing the statement + /// + /// EPStatement to poll data from or to add listeners to + /// + /// EPException when the expression was not valid + EPStatement Create(EPStatementObjectModel sodaStatement, String statementName); + + /// + /// Creates and starts an EPL statement. + /// + /// The statement name is optimally a unique name. If a statement of the same name + /// has already been created, the engine assigns a postfix to create a unique + /// statement name. + /// + /// Accepts an application defined user data object associated with the statement. + /// The user object is a single, unnamed field that is stored with every + /// statement. Applications may put arbitrary objects in this field or a null value. + /// + /// is the statement object model + /// is the name to assign to the statement for use in managing the statement + /// is the application-defined user object + /// + /// EPStatement to poll data from or to add listeners to + /// + /// EPException when the expression was not valid + EPStatement Create(EPStatementObjectModel sodaStatement, String statementName, Object userObject); + + /// + /// Creates and starts an EPL statement. + /// + /// is the statement object model + /// + /// EPStatement to poll data from or to add listeners to + /// + /// EPException when the expression was not valid + EPStatement Create(EPStatementObjectModel sodaStatement); + + /// + /// Compiles a given EPL into an object model representation of the query. + /// + /// is the statement text to compile + /// + /// object model of statement + /// + /// EPException indicates compilation errors. + EPStatementObjectModel CompileEPL(String eplExpression); + + /// + /// Prepares a statement for the given EPL, which can include substitution + /// parameters marked via question mark '?'. + /// + /// is the statement text to prepare + /// + /// prepared statement + /// + /// EPException indicates compilation errors. + EPPreparedStatement PrepareEPL(String eplExpression); + + /// + /// Prepares a statement for the given pattern, which can include substitution + /// parameters marked via question mark '?'. + /// + /// is the statement text to prepare + /// + /// prepared statement + /// + /// EPException indicates compilation errors. + EPPreparedStatement PreparePattern(String patternExpression); + + /// + /// Creates and starts a prepared statement. + /// + /// The statement name is optimally a unique name. If a statement of the same name + /// has already been created, the engine assigns a postfix to create a unique + /// statement name. + /// + /// is the prepared statement for which all substitution values have been provided + /// is the name to assign to the statement for use in managing the statement + /// + /// EPStatement to poll data from or to add listeners to + /// + /// EPException when the prepared statement was not valid + EPStatement Create(EPPreparedStatement prepared, String statementName); + + /// + /// Creates and starts a prepared statement. + /// + /// The statement name is optimally a unique name. If a statement of the same name + /// has already been created, the engine assigns a postfix to create a unique + /// statement name. + /// + /// Accepts an application defined user data object associated with the statement. + /// The user object is a single, unnamed field that is stored with every + /// statement. Applications may put arbitrary objects in this field or a null value. + /// + /// is the prepared statement for which all substitution values have been provided + /// is the name to assign to the statement for use in managing the statement + /// is the application-defined user object + /// + /// EPStatement to poll data from or to add listeners to + /// + /// EPException when the prepared statement was not valid + EPStatement Create(EPPreparedStatement prepared, String statementName, Object userObject); + + /// + /// Creates and starts a prepared statement. + /// + /// is the prepared statement for which all substitution values have been provided + /// + /// EPStatement to poll data from or to add listeners to + /// + /// EPException when the expression was not valid + EPStatement Create(EPPreparedStatement prepared); + + /// + /// Returns the statement by the given statement name. Returns null if a statement + /// of that name has not been created, or if the statement by that name has been + /// destroyed. + /// + /// is the statement name to return the statement for + /// + /// statement for the given name, or null if no such started or stopped statement + /// exists + /// + EPStatement GetStatement(String name); + + /// + /// Returns the statement names of all started and stopped statements. + /// + /// This excludes the name of destroyed statements. + /// + /// + /// statement names + /// + IList StatementNames { get; } + + /// + /// Starts all statements that are in stopped state. Statements in started state are + /// not affected by this method. + /// + /// EPException when an error occured starting statements. + void StartAllStatements(); + + /// + /// Stops all statements that are in started state. Statements in stopped state are + /// not affected by this method. + /// + /// EPException when an error occured stopping statements + void StopAllStatements(); + + /// + /// Stops and destroys all statements. + /// + /// EPException when an error occured stopping or destroying statements + void DestroyAllStatements(); + + /// + /// Gets the configuration. + /// + /// + ConfigurationOperations Configuration { get; } + + /// + /// Returns the administrative interface for context partitions. + /// + EPContextPartitionAdmin ContextPartitionAdmin { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/EPAdministratorIsolated.cs b/NEsper.Core/NEsper.Core/client/EPAdministratorIsolated.cs new file mode 100755 index 000000000..e2f6e3113 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/EPAdministratorIsolated.cs @@ -0,0 +1,78 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.client +{ + /// + /// Administrative interfae + /// + public interface EPAdministratorIsolated + { + /// + /// Create and starts an EPL statement. + /// + /// The statement name is optimally a unique name. If a statement of the same name + /// has already been created, the engine assigns a postfix to create a unique + /// statement name. + /// + /// Accepts an application defined user data object associated with the statement. + /// The user object is a single, unnamed field that is stored with every + /// statement. Applications may put arbitrary objects in this field or a null value. + /// + /// is the query language statement + /// is the statement name or null if not provided or provided via annotation instead + /// is the application-defined user object, or null if none provided + /// + /// EPStatement to poll data from or to add listeners to, or null if provided via + /// annotation + /// + /// com.espertech.esper.client.EPException when the expression was not valid + EPStatement CreateEPL(String eplStatement, String statementName, Object userObject); + + /// + /// Returns the statement names of all started and stopped statements. + /// + /// This excludes the name of destroyed statements. + /// + /// + /// statement names + /// + IList StatementNames { get; } + + /// + /// Add a statement to the isolated service. + /// + /// to add + /// EPServiceIsolationException if the statement cannot be isolated, typically because it already is isolated + void AddStatement(EPStatement statement); + + /// + /// Remove a statement from the isolated service. This does not change engine state. + /// + /// to remove + /// EPServiceIsolationException if the statement was not isolated herein + void RemoveStatement(EPStatement statement); + + /// + /// Add statements to the isolated service. + /// + /// to add + /// EPServiceIsolationException if the statement cannot be isolated, typically because it already is isolated + void AddStatement(EPStatement[] statements); + + /// + /// Remove statements from the isolated service. This does not change engine state. + /// + /// to remove + /// EPServiceIsolationException if the statement was not isolated herein + void RemoveStatement(IList statements); + } +} diff --git a/NEsper.Core/NEsper.Core/client/EPException.cs b/NEsper.Core/NEsper.Core/client/EPException.cs new file mode 100755 index 000000000..93a3ac36e --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/EPException.cs @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client +{ + /// + /// This exception is thrown to indicate a problem in administration and runtime. + /// + [Serializable] + public class EPException : Exception + { + private static readonly Type MyType = + System.Reflection.MethodBase.GetCurrentMethod().DeclaringType; + + /// Ctor. + /// error message + /// + public EPException(String message) + : base(message) + { + } + + /// Ctor for an inner exception and message. + /// error message + /// + /// inner exception + /// + public EPException(String message, Exception cause) + : base(message, cause) + { + } + + /// Ctor - just an inner exception. + /// inner exception + /// + public EPException(Exception cause) + : base(MyType.FullName + ": " + cause.Message, cause) + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/EPIterable.cs b/NEsper.Core/NEsper.Core/client/EPIterable.cs new file mode 100755 index 000000000..df23efa98 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/EPIterable.cs @@ -0,0 +1,70 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +namespace com.espertech.esper.client +{ + /// + /// Interface to iterate over events. + /// + /// A concurrency-unsafe iterator over events representing statement results (pull API). + /// + /// + /// The iterator is useful for applications that are single-threaded, or that coordinate the iterating thread + /// with event processing threads that use the sendEvent method using application code locks or synchronization. + /// + /// + /// The iterator returned by this method does not make any guarantees towards correctness of + /// results and fail-behavior, if your application processes events into the engine instance + /// using the sendEvent method by multiple threads. + /// + /// + /// Use the safeIterator method for concurrency-safe iteration. Note the safe iterator requires + /// applications to explicitly close the safe iterator when done iterating. + /// + /// + /// event iterator + + public interface EPIterable : IEnumerable + { + /// Returns the type of events the iterable returns. + /// event type of events the iterator returns + /// + + EventType EventType { get; } + + /// + /// Returns a concurrency-safe iterator that iterates over events representing statement results (pull API) + /// in the face of concurrent event processing by further threads. + /// + /// In comparison to the regular iterator, the safe iterator guarantees correct results even + /// as events are being processed by other threads. The cost is that the iterator holds + /// one or more locks that must be released. Any locks are acquired at the time this method + /// is called. + /// + /// + /// This method is a blocking method. It may block until statement processing locks are released + /// such that the safe iterator can acquire any required locks. + /// + /// + /// An application MUST explicitly close the safe iterator instance using the close method, to release locks held by the + /// iterator. The call to the close method should be done in a finally block to make sure + /// the iterator gets closed. + /// + /// + /// Multiple safe iterators may be not be used at the same time by different application threads. + /// A single application thread may hold and use multiple safe iterators however this is discouraged. + /// + /// + /// + /// safe iterator; + /// + IEnumerator GetSafeEnumerator(); + } +} diff --git a/NEsper.Core/NEsper.Core/client/EPListenable.cs b/NEsper.Core/NEsper.Core/client/EPListenable.cs new file mode 100755 index 000000000..2a3740b4e --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/EPListenable.cs @@ -0,0 +1,67 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.client +{ + /// + /// Interface to add and remove Update listeners. + /// + + public interface EPListenable + { + /// + /// Occurs whenever new events are available or old events are removed. + /// + event UpdateEventHandler Events; + + /// + /// Removes all event handlers. + /// + void RemoveAllEventHandlers(); + + #if UPDATE_LISTENERS + + + /// Add an listener that observes events. + /// to add + /// + void AddListener( UpdateListener listener ); + + /// Remove an listener that observes events. + /// to remove + /// + void RemoveListener( UpdateListener listener ); + + /// Remove all listeners. + void RemoveAllListeners(); + + /// + /// Add a statement-aware listener that observes events. + /// + /// The listener. + void AddListener(StatementAwareUpdateListener listener); + + /// + /// Remove a statement-aware listener that observes events. + /// + void RemoveListener(StatementAwareUpdateListener listener); + + /// + /// Returns an enumerable of statement-aware Update listeners. + /// + IEnumerable StatementAwareListeners { get;} + + /// + /// Gets the Update listeners. + /// + /// The Update listeners. + IEnumerable UpdateListeners { get; } + + #endif + } +} diff --git a/NEsper.Core/NEsper.Core/client/EPManagedStatement.cs b/NEsper.Core/NEsper.Core/client/EPManagedStatement.cs new file mode 100755 index 000000000..6c7642e25 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/EPManagedStatement.cs @@ -0,0 +1,19 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.client +{ + public interface EPManagedStatement : EPStatement + { + /// + /// Clears the event handlers and statement aware event handlers. Should be + /// used with caution since this clears anyone who has registered a handler. + /// + void ClearEventHandlers(); + } +} diff --git a/NEsper.Core/NEsper.Core/client/EPOnDemandPreparedQuery.cs b/NEsper.Core/NEsper.Core/client/EPOnDemandPreparedQuery.cs new file mode 100755 index 000000000..904cb94aa --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/EPOnDemandPreparedQuery.cs @@ -0,0 +1,36 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client.context; + +namespace com.espertech.esper.client +{ + /// + /// Interface for a prepared on-demand query that can be executed multiple times. + /// + public interface EPOnDemandPreparedQuery + { + /// + /// Execute the prepared query returning query results. + /// + /// query result + EPOnDemandQueryResult Execute(); + + /// + /// For use with named windows that have a context declared and that may therefore have multiple context + /// partitions, allows to target context partitions for query execution selectively. + /// + /// selects context partitions to consider + /// query result + EPOnDemandQueryResult Execute(ContextPartitionSelector[] contextPartitionSelectors); + + /// Returns the event type, representing the columns of the select-clause. + /// event type + EventType EventType { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/EPOnDemandPreparedQueryParameterized.cs b/NEsper.Core/NEsper.Core/client/EPOnDemandPreparedQueryParameterized.cs new file mode 100755 index 000000000..19d4d51e2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/EPOnDemandPreparedQueryParameterized.cs @@ -0,0 +1,33 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.client +{ + /// + /// Parameter holder for parameterized on-demand queries that are prepared with substitution parameters and that + /// can be executed efficiently multiple times with different actual values for parameters. + /// A pre-compiled query can only be executed when actual values for all + /// substitution parameters are set. + /// + public interface EPOnDemandPreparedQueryParameterized + { + /// + /// Sets the value of the designated parameter using the given object. + /// + /// the first parameter is 1, the second is 2, ... + /// the object containing the input parameter value + /// EPException if the substitution parameter could not be located + void SetObject(int parameterIndex, object value) ; + } + +} diff --git a/NEsper.Core/NEsper.Core/client/EPOnDemandQueryResult.cs b/NEsper.Core/NEsper.Core/client/EPOnDemandQueryResult.cs new file mode 100755 index 000000000..7788f1eda --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/EPOnDemandQueryResult.cs @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System.Collections.Generic; + +namespace com.espertech.esper.client +{ + /// Results of an on-demand (fire-and-forget non-continuous) query. + public interface EPOnDemandQueryResult + { + /// Returns an array representing query result rows, may return a null value or empty array to indicate an empty result set. + /// result array + EventBean[] Array { get; } + + /// Returns the event type of the result. + /// event type of result row + EventType EventType { get; } + + /// Returns an enumerator representing query result rows. + /// result row enumerator + IEnumerator GetEnumerator(); + } +} diff --git a/NEsper.Core/NEsper.Core/client/EPPreparedStatement.cs b/NEsper.Core/NEsper.Core/client/EPPreparedStatement.cs new file mode 100755 index 000000000..f0088e34d --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/EPPreparedStatement.cs @@ -0,0 +1,43 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client +{ + /// + /// Precompiled statement that is prepared with substitution parameters and that + /// can be created and started efficiently multiple times with different actual values for parameters. + /// + /// When a precompiled statement is prepared via the prepare method on , + /// it typically has one or more substitution parameters in the statement text, + /// for which the placeholder character is the question mark. This class provides methods to set + /// the actual value for the substitution parameter. + /// + /// + /// A precompiled statement can only be created and started when actual values for all + /// substitution parameters are set. + /// + /// + public interface EPPreparedStatement + { + /// + /// Sets the value of the designated parameter using the given object. + /// + /// the first parameter is 1, the second is 2, ... + /// the object containing the input parameter value + void SetObject(int parameterIndex, Object value); + + /// + /// Sets the value of the designated parameter using the given object. + /// + /// the name of the parameter + /// the object containing the input parameter value + void SetObject(String parameterName, Object value); + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/client/EPRuntime.cs b/NEsper.Core/NEsper.Core/client/EPRuntime.cs new file mode 100755 index 000000000..c2c5d7d34 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/EPRuntime.cs @@ -0,0 +1,505 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Xml; +using System.Xml.Linq; + +using com.espertech.esper.client.context; +using com.espertech.esper.client.dataflow; +using com.espertech.esper.client.soda; +using com.espertech.esper.client.util; + +namespace com.espertech.esper.client +{ + using DataMap = IDictionary; + + /// + /// Interface to event stream processing runtime services. + /// + public interface EPRuntime + { + /// + /// Send an event represented by a plain object to the event stream processing + /// runtime. + /// + /// Use the route method for sending events into the runtime from within + /// UpdateListener code, to avoid the possibility of a stack overflow due to nested calls to + /// SendEvent. + /// (except with the outbound-threading configuration), see . + /// + /// is the event to sent to the runtime + /// EPException is thrown when the processing of the event lead to an error + void SendEvent(Object obj); + + /// + /// Send a map containing event property values to the event stream processing + /// runtime. + /// + /// Use the route method for sending events into the runtime from within + /// event handler code. to avoid the possibility of a stack overflow due to nested calls to + /// SendEvent. + /// (except with the outbound-threading configuration), see . + /// + /// map that contains event property values. Keys are expected to be of type string while value scan be of any type. Keys and values should match those declared via Configuration for the given eventTypeName. + /// the name for the Map event type that was previously configured + /// EPException - when the processing of the event leads to an error + void SendEvent(DataMap map, string mapEventTypeName); + + /// + /// Send an object array containing event property values to the event stream processing runtime. + /// + /// Use the route method for sending events into the runtime from within UpdateListener code. + /// to avoid the possibility of a stack overflow due to nested calls to sendEvent. + /// (except with the outbound-threading configuration), see . + /// + /// array that contains event property values. Your application must ensure that property values match the exact same order that the property names and types have been declared, and that the array length matches the number of properties declared. + /// the name for the Object-array event type that was previously configured + /// EPException - when the processing of the event leads to an error + void SendEvent(Object[] objectarray, String objectArrayEventTypeName); + + /// + /// Send an event represented by a LINQ element to the event stream processing runtime. + /// + /// Use the route method for sending events into the runtime from within + /// event handler code. to avoid the possibility of a stack overflow due to nested calls to + /// SendEvent. + /// (except with the outbound-threading configuration), see . + /// + /// The element. + void SendEvent(XElement element); + + /// + /// Send an event represented by a DOM node to the event stream processing runtime. + /// + /// Use the route method for sending events into the runtime from within + /// event handler code. to avoid the possibility of a stack overflow due to nested calls to + /// SendEvent. + /// (except with the outbound-threading configuration), see . + /// + /// is the DOM node as an event + /// EPException is thrown when the processing of the event lead to an error + void SendEvent(XmlNode node); + + /// + /// Number of events evaluated over the lifetime of the event stream processing + /// runtime, or since the last ResetStats() call. + /// + /// + /// number of events received + /// + long NumEventsEvaluated { get; } + + /// + /// Reset number of events received and emitted + /// + void ResetStats(); + + /// + /// Route the event object back to the event stream processing runtime for internal + /// dispatching, to avoid the possibility of a stack overflow due to nested calls to + /// SendEvent. The route event is processed just like it was sent to the runtime, + /// that is any active expressions seeking that event receive it. The routed event has + /// priority over other events sent to the runtime. In a single-threaded application + /// the routed event is processed before the next event is sent to the runtime through + /// the EPRuntime.SendEvent method. + /// + /// Note: when outbound-threading is enabled, the thread delivering to listeners + /// is not the thread processing the original event. Therefore with outbound-threading + /// enabled the SendEvent method should be used by listeners instead. + /// + /// + /// to route internally for processing by the event stream processing runtime + void Route(Object evnt); + + /// + /// Route the event object back to the event stream processing runtime for internal + /// dispatching, to avoid the possibility of a stack overflow due to nested calls to + /// SendEvent. The route event is processed just like it was sent to the runtime, + /// that is any active expressions seeking that event receive it. The routed event has + /// priority over other events sent to the runtime. In a single-threaded application + /// the routed event is processed before the next event is sent to the runtime through + /// the EPRuntime.SendEvent method. + /// + /// Note: when outbound-threading is enabled, the thread delivering to listeners + /// is not the thread processing the original event. Therefore with outbound-threading + /// enabled the SendEvent method should be used by listeners instead. + /// + /// + /// map that contains event property values. Keys are expected to be of type string while value scan be of any type. Keys and values should match those declared via Configuration for the given eventTypeName. + /// the name for Map event type that was previously configured + /// EPException - when the processing of the event leads to an error + void Route(DataMap map, string eventTypeName); + + /// + /// Route the event object back to the event stream processing runtime for internal + /// dispatching, to avoid the possibility of a stack overflow due to nested calls to sendEvent. + /// The route event is processed just like it was sent to the runtime, that is any active + /// expressions seeking that event receive it. The routed event has priority over other events + /// sent to the runtime. In a single-threaded application the routed event is processed before + /// the next event is sent to the runtime through the EPRuntime.sendEvent method. + /// + /// Note: when outbound-threading is enabled, the thread delivering to listeners + /// is not the thread processing the original event. Therefore with outbound-threading + /// enabled the SendEvent method should be used by listeners instead. + /// + /// + /// object array that contains event property values. Your application must ensure that property valuesmatch the exact same order that the property names and types have been declared, and that the array length matches the number of properties declared. + /// the name for Object-array event type that was previously configured + /// EPException - when the processing of the event leads to an error + void Route(Object[] objectArray, String eventTypeName); + + /// + /// Route the event object back to the event stream processing runtime for internal + /// dispatching, to avoid the possibility of a stack overflow due to nested calls to + /// SendEvent. The route event is processed just like it was sent to the runtime, + /// that is any active expressions seeking that event receive it. The routed event has + /// priority over other events sent to the runtime. In a single-threaded application + /// the routed event is processed before the next event is sent to the runtime through + /// the EPRuntime.SendEvent method. + /// + /// Note: when outbound-threading is enabled, the thread delivering to listeners + /// is not the thread processing the original event. Therefore with outbound-threading + /// enabled the SendEvent method should be used by listeners instead. + /// + /// + /// The LINQ element as an event. + /// EPException is thrown when the processing of the event lead to an error + void Route(XElement element); + + /// + /// Route the event object back to the event stream processing runtime for internal + /// dispatching, to avoid the possibility of a stack overflow due to nested calls to + /// SendEvent. The route event is processed just like it was sent to the runtime, + /// that is any active expressions seeking that event receive it. The routed event has + /// priority over other events sent to the runtime. In a single-threaded application + /// the routed event is processed before the next event is sent to the runtime through + /// the EPRuntime.SendEvent method. + /// + /// Note: when outbound-threading is enabled, the thread delivering to listeners + /// is not the thread processing the original event. Therefore with outbound-threading + /// enabled the SendEvent method should be used by listeners instead. + /// + /// + /// is the DOM node as an event + /// EPException is thrown when the processing of the event lead to an error + void Route(XmlNode node); + + /// + /// Gets or sets a listener to receive events that are unmatched by any statement. + /// + /// Events that can be unmatched are all events that are send into a runtime via + /// one of the SendEvent methods, or that have been generated via insert-into clause. + /// + /// For an event to be unmatched by any statement, the event must not match any + /// statement's event stream filter criteria (a where-clause is NOT a filter criteria + /// for a stream, as below). + /// + /// Note: In the following statement a MyEvent event does always match this + /// statement's event stream filter criteria, regardless of the value of the 'quantity' + /// property. + ///
select * from MyEvent where quantity > 5
+ /// + /// In the following statement only a MyEvent event with a 'quantity' property value of 5 or less does + /// not match this statement's event stream filter criteria: + ///
select * from MyEvent(quantity > 5)
+ /// + /// For patterns, if no pattern sub-expression is active for such event, the event + /// is also unmatched. + ///
+ event EventHandler UnmatchedEvent; + + /// + /// Removes all unmatched event handlers. + /// + void RemoveAllUnmatchedEventHandlers(); + + /// + /// Returns the current variable value for a global variable. A null value is a valid value for a variable. + /// Not for use with context-partitioned variables. + /// + /// is the name of the variable to return the value for + /// + /// current variable value + /// + /// VariableNotFoundException if a variable by that name has not been declared + Object GetVariableValue(string variableName); + + /// + /// Returns the current variable value for a global variable. A null value is a valid value for a variable. + /// Not for use with context-partitioned variables. + /// + /// is a set of variable names for which to return values + /// + /// map of variable name and variable value + /// + /// VariableNotFoundException if any of the variable names has not been declared + DataMap GetVariableValue(ICollection variableNames); + + /// + /// Returns the current variable values for a context-partitioned variable, per context partition. + /// A null value is a valid value for a variable. + /// Only for use with context-partitioned variables. + /// Variable names provided must all be associated to the same context partition. + /// + /// are the names of the variables to return the value for + /// selector for the context partition to return the value for + /// current variable value + /// VariableNotFoundException if a variable by that name has not been declared + IDictionary> GetVariableValue( + ISet variableNames, ContextPartitionSelector contextPartitionSelector); + + /// + /// Returns current variable values for all global variables, + /// guaranteeing consistency in the face of concurrent updates to the variables. + /// Not for use with context-partitioned variables. + /// + /// + /// map of variable name and variable value + /// + DataMap VariableValueAll { get; } + + /// + /// Sets the value of a single global variable. + /// + /// Note that the thread setting the variable value queues the changes, i.e. it does not itself + /// re-evaluate such new variable value for any given statement. The timer thread performs this work. + /// + /// Not for use with context-partitioned variables. + /// + /// is the name of the variable to change the value of + /// is the new value of the variable, with null an allowed value + /// VariableValueException if the value does not match variable type or cannot be safely coercedto the variable type + /// VariableNotFoundException if the variable name has not been declared + void SetVariableValue(string variableName, Object variableValue); + + /// + /// Sets the value of multiple global variables in one Update, applying all or none of the + /// changes to variable values in one atomic transaction. + /// + /// Note that the thread setting the variable value queues the changes, i.e. it does not itself + /// re-evaluate such new variable value for any given statement. The timer thread performs this work. + /// + /// Not for use with context-partitioned variables. + /// + /// is the map of variable name and variable value, with null an allowed value + /// VariableValueException if any value does not match variable type or cannot be safely coercedto the variable type + /// VariableNotFoundException if any of the variable names has not been declared + void SetVariableValue(DataMap variableValues); + + /// + /// Sets the value of multiple context-partitioned variables in one update, applying all or none of the changes + /// to variable values in one atomic transaction. + /// + /// Note that the thread setting the variable value queues the changes, i.e. it does not itself + /// re-evaluate such new variable value for any given statement. The timer thread performs this work. + /// + /// Only for use with context-partitioned variables. + /// + /// is the map of variable name and variable value, with null an allowed value + /// the id of the context partition + /// VariableValueException if any value does not match variable type or cannot be safely coerced to the variable type + /// VariableNotFoundException if any of the variable names has not been declared + void SetVariableValue(DataMap variableValues, int agentInstanceId); + + /// + /// Returns a facility to process event objects that are of a known type. + /// + /// Given an event type name this method returns a sender that allows to send in + /// event objects of that type. The event objects send in via the event sender are + /// expected to match the event type, thus the event sender does not inspect the event + /// object other then perform basic checking. + /// + /// For events backed by a type, the sender ensures that the object send in matches in + /// class, or implements or extends the class underlying the event type for the given event + /// type name. Note that event type identity for type events is the type. When assigning + /// two different event type names to the same type the names are an alias for the same + /// event type i.e. there is always a single event type to represent a given type class. + /// + /// For events backed by a Object[] (Object-array events), the sender does not perform any checking other + /// then checking that the event object indeed is an array of object. + /// + /// For events backed by a Dictionary(Map events), the sender does not perform + /// any checking other then checking that the event object indeed : Map. + /// + /// For events backed by a XmlNode (XML DOM events), the sender checks that + /// the root element name indeed does match the root element name for the event type + /// name. + /// + /// is the name of the event type + /// + /// sender for fast-access processing of event objects of known type (and content) + /// + /// EventTypeException thrown to indicate that the name does not exist + EventSender GetEventSender(string eventTypeName); + + /// + /// For use with plug-in event representations, returns a facility to process event + /// objects that are of one of a number of types that one or more of the registered + /// plug-in event representation extensions can reflect upon and provide an event for. + /// + /// + /// is the URIs that specify which plug-in event representations may process an event object. + /// URIs do not need to match event representation URIs exactly, a child (hierarchical) match is enough for an event representation to participate. + /// The order of URIs is relevant as each event representation's factory is asked in turn to process the event, until the first factory processes the event. + /// + /// + /// sender for processing of event objects of one of the plug-in event + /// representations + /// + /// EventTypeException thrown to indicate that the URI list was invalid + EventSender GetEventSender(Uri[] uris); + + /// + /// Execute an on-demand query. + /// + /// On-demand queries are EPL queries that execute non-continuous fire-and-forget + /// queries against named windows. + /// + /// is the EPL to execute + /// + /// query result + /// + EPOnDemandQueryResult ExecuteQuery(string epl); + + /// + /// For use with named windows that have a context declared and that may therefore have multiple + /// context partitions, allows to target context partitions for query execution selectively. + /// + /// is the EPL query to execute + /// selects context partitions to consider + /// result + EPOnDemandQueryResult ExecuteQuery(String epl, ContextPartitionSelector[] contextPartitionSelectors); + + /// + /// Execute an on-demand query. <p> On-demand queries are EPL queries that execute + /// non-continuous fire-and-forget queries against named windows. + /// + /// is the EPL query to execute, obtain a model object using or via the API + /// query result + EPOnDemandQueryResult ExecuteQuery(EPStatementObjectModel model); + + /// + /// For use with named windows that have a context declared and that may therefore have multiple context + /// partitions, allows to target context partitions for query execution selectively. + /// + /// is the EPL query to execute, obtain a model object using or via the API + /// selects context partitions to consider + /// result + EPOnDemandQueryResult ExecuteQuery(EPStatementObjectModel model, ContextPartitionSelector[] contextPartitionSelectors); + + /// + /// Prepare an unparameterized on-demand query before execution and for repeated execution. + /// + /// to prepare + /// proxy to execute upon, that also provides the event type of the returned results + EPOnDemandPreparedQuery PrepareQuery(String epl); + + /// + /// Prepare an unparameterized on-demand query before execution and for repeated execution. + /// + /// is the EPL query to prepare, obtain a model object using or via the API + /// proxy to execute upon, that also provides the event type of the returned results + EPOnDemandPreparedQuery PrepareQuery(EPStatementObjectModel model); + + /// + /// Prepare a parameterized on-demand query for repeated parameter setting and execution. Set all values on the + /// returned holder then execute using . + /// + /// to prepare + /// parameter holder upon which to set values + EPOnDemandPreparedQueryParameterized PrepareQueryWithParameters(String epl); + + /// + /// Execute an on-demand parameterized query. + /// + /// On-demand queries are EPL queries that execute non-continuous fire-and-forget queries against named windows. + /// + /// + /// contains the query and parameter values + /// query result + EPOnDemandQueryResult ExecuteQuery(EPOnDemandPreparedQueryParameterized parameterizedQuery); + + /// + /// Execute an on-demand parameterized query. + /// + /// On-demand queries are EPL queries that execute non-continuous fire-and-forget queries against named windows. + /// + /// contains the query and parameter values + /// selects context partitions to consider + /// query result + EPOnDemandQueryResult ExecuteQuery(EPOnDemandPreparedQueryParameterized parameterizedQuery, ContextPartitionSelector[] contextPartitionSelectors); + + /// + /// Returns the event renderer for events generated by this runtime. + /// + /// + /// event renderer + /// + EventRenderer EventRenderer { get; } + + /// + /// Returns current engine time. + /// + /// If time is provided externally via timer events, the function returns current + /// time as externally provided. + /// + /// + /// current engine time + /// + long CurrentTime { get; } + + /// + /// Returns the time at which the next schedule execution is expected, returns null + /// if no schedule execution is outstanding. + /// + long? NextScheduledTime { get; } + + /// Returns the data flow runtime. + /// data flow runtime + EPDataFlowRuntime DataFlowRuntime { get; } + + /// + /// Returns true for external clocking, false for internal clocking. + /// + /// clocking indicator + bool IsExternalClockingEnabled { get; } + + /// + /// Send an event represented by a Avro GenericRecord to the event stream processing runtime. + /// + /// Use the route method for sending events into the runtime from within UpdateListener code, + /// to avoid the possibility of a stack overflow due to nested calls to sendEvent + /// (except with the outbound-threading configuration), see . + /// + /// + /// is the event to sent to the runtime + /// event type name + /// is thrown when the processing of the event lead to an error + void SendEventAvro(object avroGenericDataDotRecord, string avroEventTypeName); + + /// + /// Route the event object back to the event stream processing runtime for internal dispatching, + /// to avoid the possibility of a stack overflow due to nested calls to sendEvent. + /// The route event is processed just like it was sent to the runtime, that is any + /// active expressions seeking that event receive it. The routed event has priority over other + /// events sent to the runtime. In a single-threaded application the routed event is + /// processed before the next event is sent to the runtime through the + /// EPRuntime.sendEvent method. + /// + /// Note: when outbound-threading is enabled, the thread delivering to listeners + /// is not the thread processing the original event. Therefore with outbound-threading + /// enabled the sendEvent method should be used by listeners instead. + /// + /// + /// is the event to sent to the runtime + /// event type name + void RouteAvro(object avroGenericDataDotRecord, string avroEventTypeName); + } +} diff --git a/NEsper.Core/NEsper.Core/client/EPRuntimeException.cs b/NEsper.Core/NEsper.Core/client/EPRuntimeException.cs new file mode 100755 index 000000000..aca2eb5e0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/EPRuntimeException.cs @@ -0,0 +1,44 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client +{ + public class EPRuntimeException : EPException + { + private static readonly Type MyType = + System.Reflection.MethodBase.GetCurrentMethod().DeclaringType; + + /// Ctor. + /// error message + /// + public EPRuntimeException(String message) + : base(message) + { + } + + /// Ctor for an inner exception and message. + /// error message + /// + /// inner exception + /// + public EPRuntimeException(String message, Exception cause) + : base(message, cause) + { + } + + /// Ctor - just an inner exception. + /// inner exception + /// + public EPRuntimeException(Exception cause) + : base(MyType.FullName + ": " + cause.Message, cause) + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/EPRuntimeIsolated.cs b/NEsper.Core/NEsper.Core/client/EPRuntimeIsolated.cs new file mode 100755 index 000000000..af04e1536 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/EPRuntimeIsolated.cs @@ -0,0 +1,137 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Xml; +using System.Xml.Linq; +using DataMap = System.Collections.Generic.IDictionary; + +namespace com.espertech.esper.client +{ + /// + /// Runtime interface for the isolated service provider, for controlling event + /// visibility and scheduling for the statements contained within the isolated service. + /// + public interface EPRuntimeIsolated + { + /// + /// Send an event represented by a plain object to the event stream processing + /// runtime. + /// + /// Use the route method for sending events into the runtime from within + /// UpdateListener code, to avoid the possibility of a stack overflow due to nested calls to + /// sendEvent. + /// + /// is the event to sent to the runtime + /// com.espertech.esper.client.EPException is thrown when the processing of the event lead to an error + void SendEvent(Object @object); + + /// + /// Send a map containing event property values to the event stream processing + /// runtime. + /// + /// Use the route method for sending events into the runtime from within + /// UpdateListener code. to avoid the possibility of a stack overflow due to nested calls to + /// sendEvent. + /// + /// map that contains event property values. Keys are expected to be of type String while value scan be of any type. Keys and values should match those declared via Configuration for the given eventTypeName. + /// the name for the Map event type that was previously configured + /// com.espertech.esper.client.EPException - when the processing of the event leads to an error + void SendEvent(DataMap map, String eventTypeName); + + /// Send an object array containing event property values to the event stream processing runtime. <p> Use the route method for sending events into the runtime from within UpdateListener code. to avoid the possibility of a stack overflow due to nested calls to sendEvent. + /// array that contains event property values. Your application must ensure that property valuesmatch the exact same order that the property names and types have been declared, and that the array length matches the number of properties declared. + /// the name for the Object-array event type that was previously configured + /// EPException - when the processing of the event leads to an error + void SendEvent(Object[] objectarray, String objectArrayEventTypeName); + + /// + /// Send an event represented by a LINQ element to the event stream processing runtime. + /// + /// Use the route method for sending events into the runtime from within + /// event handler code. to avoid the possibility of a stack overflow due to nested calls to + /// SendEvent. + /// + /// The element. + void SendEvent(XElement element); + + /// + /// Send an event represented by a DOM node to the event stream processing runtime. + /// + /// Use the route method for sending events into the runtime from within + /// UpdateListener code. to avoid the possibility of a stack overflow due to nested calls to + /// sendEvent. + /// + /// is the DOM node as an event + /// com.espertech.esper.client.EPException is thrown when the processing of the event lead to an error + void SendEvent(XmlNode node); + + /// + /// Returns current engine time. + /// + /// If time is provided externally via timer events, the function returns current + /// time as externally provided. + /// + /// + /// current engine time + /// + long CurrentTime { get; } + + /// + /// Returns the time at which the next schedule execution is expected, returns null if no schedule execution is + /// outstanding. + /// + long? NextScheduledTime { get; } + + /// + /// Returns a facility to process event objects that are of a known type. + /// + /// Given an event type name this method returns a sender that allows to send in event objects of that type. The + /// event objects send in via the event sender are expected to match the event type, thus the event sender does + /// not inspect the event object other then perform basic checking. + /// + /// + /// For events backed by a class, the sender ensures that the object send in matches in class, or : or + /// : the class underlying the event type for the given event type name. + /// + /// + /// For events backed by a Object[] (Object-array events), the sender does not perform any checking other then checking + /// that the event object indeed is an array of object. + /// + /// + /// For events backed by a DataMap (Map events), the sender does not perform any checking other then checking that the + /// event object indeed : Map. + /// + /// + /// For events backed by a XmlNode (XML DOM events), the sender checks that the root element name indeed does match the + /// root element name for the event type name. + /// + /// + /// is the name of the event type + /// sender for fast-access processing of event objects of known type (and content) + /// EventTypeException thrown to indicate that the name does not exist + EventSender GetEventSender(String eventTypeName) ; + + /// + /// For use with plug-in event representations, returns a facility to process event objects that are of one of a number + /// of types that one or more of the registered plug-in event representation extensions can reflect upon and provide an event for. + /// + /// + /// is the URIs that specify which plug-in event representations may process an event object. + /// + /// URIs do not need to match event representation URIs exactly, a child (hierarchical) match is enough for an event representation to participate. + /// + /// + /// The order of URIs is relevant as each event representation's factory is asked in turn to process the event, until the first factory processes the event. + /// + /// + /// sender for processing of event objects of one of the plug-in event representations + /// EventTypeException thrown to indicate that the URI list was invalid + EventSender GetEventSender(Uri[] uris) ; + } +} diff --git a/NEsper.Core/NEsper.Core/client/EPSender.cs b/NEsper.Core/NEsper.Core/client/EPSender.cs new file mode 100755 index 000000000..d02825cb6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/EPSender.cs @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using DataMap = System.Collections.Generic.IDictionary; + +namespace com.espertech.esper.client +{ + /// + /// Send a map containing event property values to the event stream processing runtime. + /// Use the route method for sending events into the runtime from within UpdateListener code. + /// + /// map that contains event property values. Keys are expected to be of type String while values + /// can be of any type. Keys and values should match those declared via Configuration for the given eventTypeAlias. + /// + /// EPException - when the processing of the event leads to an error + public delegate void EPSender(DataMap mappedEvent); +} diff --git a/NEsper.Core/NEsper.Core/client/EPServiceDestroyedException.cs b/NEsper.Core/NEsper.Core/client/EPServiceDestroyedException.cs new file mode 100755 index 000000000..644f33623 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/EPServiceDestroyedException.cs @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client +{ + /// + /// This exception is thrown to indicate that the EPServiceProvider (engine) instance has been destroyed. + /// + /// This exception applies to destroyed engine instances when a client attempts to receive the runtime or administrative interfaces from a destroyed engine instance. + /// + public class EPServiceDestroyedException : Exception + { + /// Ctor. + /// engine URI + public EPServiceDestroyedException(String engineURI) + : base("EPServiceProvider has already been destroyed for engine URI '" + engineURI + "'") + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/EPServiceIsolationException.cs b/NEsper.Core/NEsper.Core/client/EPServiceIsolationException.cs new file mode 100755 index 000000000..ecedb0731 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/EPServiceIsolationException.cs @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client +{ + /// + /// This exception is thrown to indicate a problem isolating statements. + /// + [Serializable] + public class EPServiceIsolationException : Exception + { + /// + /// Ctor. + /// + /// error message + public EPServiceIsolationException(String message) + : base(message) + { + } + + /// + /// Ctor for an inner exception and message. + /// + /// error message + /// inner exception + public EPServiceIsolationException(String message, Exception inner) + : base(message, inner) + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/EPServiceNotAllowedException.cs b/NEsper.Core/NEsper.Core/client/EPServiceNotAllowedException.cs new file mode 100755 index 000000000..7acdf5bba --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/EPServiceNotAllowedException.cs @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client +{ + /// + /// This exception is thrown to indicate that the operation is not allowed. + /// + public class EPServiceNotAllowedException : Exception + { + /// Ctor. + /// message + public EPServiceNotAllowedException(String message) + : base(message) + { + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/EPServiceProvider.cs b/NEsper.Core/NEsper.Core/client/EPServiceProvider.cs new file mode 100755 index 000000000..5e6db30bf --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/EPServiceProvider.cs @@ -0,0 +1,133 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.compat.threading; + +namespace com.espertech.esper.client +{ + /// + /// This class provides access to the EPRuntime and EPAdministrator implementations. + /// + public interface EPServiceProvider : IDisposable + { + /// Returns a class instance of EPRuntime. + /// an instance of EPRuntime + /// + EPRuntime EPRuntime { get; } + + /// Returns a class instance of EPAdministrator. + /// an instance of EPAdministrator + /// + EPAdministrator EPAdministrator { get; } + + /// + /// Returns the engine environment directory for engine-external + /// resources such as adapters. + /// + /// engine environment directory + Directory Directory { get; } + + /// + /// Frees any resources associated with this engine instance, and leaves + /// the engine instance ready for further use. + /// + /// Do not use the administrative and + /// runtime instances obtained before the initialize (including related services such as configuration, + /// module management, etc.). Your application must obtain new administrative and runtime instances. + /// + /// Retains the existing configuration of the engine instance but forgets any runtime configuration changes. + /// + /// Stops and destroys any existing statement resources such as filters, patterns, expressions, views. + /// + void Initialize(); + + /// Returns the provider URI, or "default" if this is the default provider. + /// provider URI + String URI { get; } + + /// + /// Returns true if the service is in destroyed state, or false if not. + /// + /// indicator whether the service has been destroyed + bool IsDestroyed { get; } + + /// + /// Clears the service state event handlers. For internal use only. + /// + void RemoveAllServiceStateEventHandlers(); + + /// + /// Returns the isolated service provider for that name, creating an isolated + /// service if the name is a new name, or returning an existing isolated service for an + /// existing name. + /// + /// Note: Requires configuration setting. + /// + /// + /// to return isolated service for + /// isolated service + EPServiceProviderIsolated GetEPServiceIsolated(String name); + + /// + /// Returns the names of isolated service providers currently allocated. + /// + /// + /// isolated service provider names + /// + IList EPServiceIsolatedNames { get; } + + /// + /// Returns the engine-instance global read-write lock. + /// + /// The method takes a read lock. + /// The methods take a write lock. + /// engine instance global read-write lock + IReaderWriterLock EngineInstanceWideLock { get; } + + /// + /// Occurs before an is destroyed. + /// + event EventHandler ServiceDestroyRequested; + + /// + /// Occurs after an is initialized. + /// + event EventHandler ServiceInitialized; + + /// + /// Occurs when a statement created. + /// + event EventHandler StatementCreate; + + /// + /// Occurs when a statement state changes. + /// + event EventHandler StatementStateChange; + } + + public class ServiceProviderEventArgs : EventArgs + { + /// + /// Gets the service provider. + /// + /// The service provider. + public EPServiceProvider ServiceProvider { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// The service provider. + public ServiceProviderEventArgs(EPServiceProvider serviceProvider) + { + ServiceProvider = serviceProvider; + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/EPServiceProviderIsolated.cs b/NEsper.Core/NEsper.Core/client/EPServiceProviderIsolated.cs new file mode 100755 index 000000000..b9d3b2008 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/EPServiceProviderIsolated.cs @@ -0,0 +1,43 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client +{ + /// + /// Isolated service provider for controlling event visibility and scheduling on a + /// statement level. + /// + public interface EPServiceProviderIsolated : IDisposable + { + /// + /// Returns a class instance of EPRuntime. + /// + /// + /// an instance of EPRuntime + /// + EPRuntimeIsolated EPRuntime { get; } + + /// + /// Returns a class instance of EPAdministrator. + /// + /// + /// an instance of EPAdministrator + /// + EPAdministratorIsolated EPAdministrator { get; } + + /// + /// Name of isolated service. + /// + /// + /// isolated service name + /// + string Name { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/EPServiceProviderManager.cs b/NEsper.Core/NEsper.Core/client/EPServiceProviderManager.cs new file mode 100755 index 000000000..a393ec18a --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/EPServiceProviderManager.cs @@ -0,0 +1,180 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.threading; +using com.espertech.esper.core.service; + +namespace com.espertech.esper.client +{ + /// + /// Factory for instances of . + /// + public sealed class EPServiceProviderManager + { + private static readonly ILockable LockObj; + private static readonly IDictionary Runtimes; + + static EPServiceProviderManager() + { + LockObj = LockManager.CreateLock(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + Runtimes = new ConcurrentDictionary(); + } + + /// + /// Returns the default EPServiceProvider. The URI value for the service returned is "default". + /// + /// default instance of the service. + public static EPServiceProvider GetDefaultProvider() + { + return GetProvider(EPServiceProviderConstants.DEFAULT_ENGINE_URI, new Configuration()); + } + + /// + /// Returns the default EPServiceProvider. The URI value for the service returned is "default". + /// + /// is the configuration for the service + /// to indicate a configuration problem + /// default instance of the service. + public static EPServiceProvider GetDefaultProvider(Configuration configuration) + { + return GetProvider(EPServiceProviderConstants.DEFAULT_ENGINE_URI, configuration); + } + + /// + /// Returns an EPServiceProvider for a given provider URI. + /// + /// Use the URI of "default" or null to return the default service provider. + /// + /// + /// - the provider URI + /// EPServiceProvider for the given provider URI. + public static EPServiceProvider GetProvider(string providerURI) + { + return GetProvider(providerURI, new Configuration()); + } + + /// + /// Returns an EPServiceProvider for a given provider URI. + /// Use the URI of "default" or null to return the default service provider. + /// + /// - the provider URI. If null provided it assumes "default". + /// is the configuration for the service + /// to indicate a configuration problem + /// EPServiceProvider for the given provider URI. + public static EPServiceProvider GetProvider(string providerURI, Configuration configuration) + { + using (LockObj.Acquire()) + { + var providerURINonNull = providerURI ?? EPServiceProviderConstants.DEFAULT_ENGINE_URI; + + if (Runtimes.ContainsKey(providerURINonNull)) + { + var provider = Runtimes.Get(providerURINonNull); + if (provider.IsDestroyed) + { + provider = GetProviderInternal(configuration, providerURINonNull); + Runtimes.Put(providerURINonNull, provider); + } + else + { + provider.SetConfiguration(configuration); + } + return provider; + } + + // New runtime + var runtime = GetProviderInternal(configuration, providerURINonNull); + Runtimes.Put(providerURINonNull, runtime); + runtime.PostInitialize(); + + return runtime; + } + } + + /// + /// Returns an existing provider. Returns null if the provider for the given URI has not been initialized + /// or the provider for the given URI is in destroyed state. + /// + /// - the provider URI. If null provided it assumes "default". + /// EPServiceProvider for the given provider URI. + public static EPServiceProvider GetExistingProvider(string providerURI) + { + var providerURINonNull = providerURI ?? EPServiceProviderConstants.DEFAULT_ENGINE_URI; + var provider = Runtimes.Get(providerURINonNull); + if (provider == null || provider.IsDestroyed) + { + return null; + } + return provider; + } + + /// + /// Returns a list of known provider URIs. + /// + /// Returns a the value "default" for the default provider. + /// + /// + /// Returns URIs for all engine instances including destroyed instances. + /// + /// + /// array of URI strings + public static string[] ProviderURIs + { + get { return Runtimes.Keys.ToArray(); } + } + + private static EPServiceProviderSPI GetProviderInternal(Configuration configuration, string providerURINonNull) + { + return new EPServiceProviderImpl(configuration, providerURINonNull, Runtimes); + } + + /// + /// Clears references to the provider. + /// + /// + + public static void PurgeProvider(string providerURI) + { + using (LockObj.Acquire()) + { + if (string.IsNullOrEmpty(providerURI)) + { + providerURI = EPServiceProviderConstants.DEFAULT_ENGINE_URI; + } + + Runtimes.Remove(providerURI); + } + } + + /// + /// Clears references to the default provider. + /// + + public static void PurgeDefaultProvider() + { + PurgeProvider(null); + } + + /// + /// Purges all providers. + /// + public static void PurgeAllProviders() + { + using (LockObj.Acquire()) + { + Runtimes.Clear(); + } + } + + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/client/EPStatement.cs b/NEsper.Core/NEsper.Core/client/EPStatement.cs new file mode 100755 index 000000000..162432ec4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/EPStatement.cs @@ -0,0 +1,182 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Security.Cryptography.X509Certificates; + +using com.espertech.esper.client.context; + +namespace com.espertech.esper.client +{ + /// + /// Statement interface that provides methods to start, stop and destroy a statement as well as + /// get statement information such as statement name, expression text and current state. + /// + /// Statements have 3 states: STARTED, STOPPED and DESTROYED. + /// + /// + /// In started state, statements are actively evaluating event streams according to the statement expression. Started + /// statements can be stopped and destroyed. + /// + /// + /// In stopped state, statements are inactive. Stopped statements can either be started, in which case + /// they begin to actively evaluate event streams, or destroyed. + /// + /// + /// Destroyed statements have relinguished all statement resources and cannot be started or stopped. + /// + /// + + public interface EPStatement : EPListenable, EPIterable, IDisposable + { + /// Returns the statement name. + /// statement name + String Name { get; } + + /// Returns the underlying expression text or XML. + /// expression text + String Text { get; } + + /// Gets the statement's current state + EPStatementState State { get; } + + /// Returns true if the statement state is started. + /// + /// true for started statements, false for stopped or destroyed statements. + /// + bool IsStarted { get; } + + /// Returns true if the statement state is stopped. + /// + /// true for stopped statements, false for started or destroyed statements. + /// + bool IsStopped { get; } + + /// Returns true if the statement state is destroyed. + /// + /// true for destroyed statements, false for started or stopped statements. + /// + bool IsDisposed { get; } + + /// Start the statement. + void Start(); + + /// Stop the statement. + void Stop(); + + /// + /// Returns the system time in milliseconds of when the statement last change state. + /// + /// time in milliseconds of last statement state change + long TimeLastStateChange { get; } + + /// + /// Gets or sets the current subscriber instance that receives statement results. + /// + /// Only a single subscriber may be set for a statement. If this method is invoked twice + /// any previously-set subscriber is no longer used. + /// + /// The subscriber. + Object Subscriber { get; set; } + + /// Returns true if statement is a pattern + /// true if statement is a pattern + bool IsPattern { get; } + + /// + /// Returns the application defined user data object associated with the statement, + /// or null if none was supplied at time of statement creation. + /// + /// The user object is a single, unnamed field that is stored with every + /// statement. Applications may put arbitrary objects in this field or a null value. + /// + /// User objects are passed at time of statement creation as a parameter the create + /// method. + /// + /// + /// user object or null if none defined + /// + object UserObject { get; } + + /// + /// Add an event handler to the current statement and replays current statement + /// results to the handler. + /// + /// The handler receives current statement results as the first call to the Update + /// method of the event handler, passing in the newEvents parameter the current statement + /// results as an array of zero or more events. Subsequent calls to the Update + /// method of the event handler are statement results. + /// + /// Current statement results are the events returned by the GetEnumerator or + /// GetSafeEnumerator methods. + /// + /// Delivery of current statement results in the first call is performed by the + /// same thread invoking this method, while subsequent calls to the event handler may + /// deliver statement results by the same or other threads. + /// + /// Note: this is a blocking call, delivery is atomic: Events occurring during + /// iteration and delivery to the event handler are guaranteed to be delivered in a separate + /// call and not lost. The event handler implementation should minimize long-running or + /// blocking operations. + /// + /// Delivery is only atomic relative to the current statement. If the same event handler + /// instance is registered with other statements it may receive other statement + /// result s simultaneously. + /// + /// If a statement is not started an therefore does not have current results, the + /// event handler receives a single invocation with a null value in newEvents. + /// + /// eventHandler that will receive events + void AddEventHandlerWithReplay(UpdateEventHandler eventHandler); + + /// + /// Returns EPL or pattern statement attributes provided in the statement text, if any. + /// + /// See the annotation namespace + /// for additional attributes / annotations. + /// + ICollection Annotations { get; } + + /// + /// Returns the name of the isolated service provided is the statement is currently + /// isolated in terms of event visibility and scheduling, or returns null if the + /// statement is live in the engine. + /// + /// + /// isolated service name or null for statements that are not currently isolated + /// + string ServiceIsolated { get; set; } + + /// + /// For use with statements that have a context declared and that may therefore have + /// multiple context partitions, allows to iterate over context partitions selectively. + /// + /// selects context partitions to consider + /// iterator + IEnumerator GetEnumerator(ContextPartitionSelector selector); + + /// + /// For use with statements that have a context declared and that may therefore have + /// multiple context partitions, allows to safe-iterate over context partitions selectively. + /// + /// selects context partitions to consider + /// safe iterator + IEnumerator GetSafeEnumerator(ContextPartitionSelector selector); + } + + public class ProxySubscriber + { + public Action ProcUpdate { get; set; } + + public void Update(string value) + { + ProcUpdate.Invoke(value); + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/EPStatementException.cs b/NEsper.Core/NEsper.Core/client/EPStatementException.cs new file mode 100755 index 000000000..fa542e5d3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/EPStatementException.cs @@ -0,0 +1,94 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Text; + +namespace com.espertech.esper.client +{ + /// + /// This exception is thrown to indicate a problem in statement creation, such as syntax error or type + /// checking problem etc. + /// + + [Serializable] + public class EPStatementException : EPException + { + /// + /// Gets or sets the expression text for statement. + /// + /// The expression. + public string Expression { get; set; } + + /// + /// Gets a message that describes the current exception. + /// + /// + /// The error message that explains the reason for the exception, or an empty string(""). + public override String Message + { + get + { + var msg = new StringBuilder(); + var baseMessage = base.Message; + + if (String.IsNullOrEmpty(baseMessage)) { + msg.Append("Unexpected exception"); + } + else { + msg.Append(baseMessage); + } + + if (Expression != null) + { + msg.Append( " [" ) ; + msg.Append( Expression ) ; + msg.Append( ']' ) ; + } + + return msg.ToString(); + } + } + + /// Ctor. + /// error message + /// + /// expression text + /// + public EPStatementException(String message, String expression) + : base(message) + { + Expression = expression; + } + + + /// + /// Ctor. + /// + /// error message + /// The inner cause. + /// expression text + public EPStatementException(String message, Exception cause, String expression) + : base(message, cause) + { + Expression = expression; + } + + /// + /// Ctor. + /// + /// error message + /// expression text + /// The inner exception. + public EPStatementException(String message, String expression, Exception innerException) + : base(message, innerException) + { + Expression = expression; + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/EPStatementState.cs b/NEsper.Core/NEsper.Core/client/EPStatementState.cs new file mode 100755 index 000000000..ee48be268 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/EPStatementState.cs @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.client +{ + /// Enumerates all statement states. + public enum EPStatementState + { + /// Started state. + STARTED, + + /// Stopped state. + STOPPED, + + /// Destroyed state. + DESTROYED, + + /// Failed state. + FAILED + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/client/EPStatementStateListener.cs b/NEsper.Core/NEsper.Core/client/EPStatementStateListener.cs new file mode 100755 index 000000000..721a76de0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/EPStatementStateListener.cs @@ -0,0 +1,61 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client +{ + /// + /// Interface for receiving callback events pertaining to statement creation and + /// statement state transitions. + /// + /// Implementations must not block the operation. + /// + public interface EPStatementStateListener + { + /// + /// Called to indicate that a new statement has been created in stopped state. + /// + /// The #onStatementStateChange method is also invoked upon statement start. + /// + /// the service provider instance under which the statement has been created + /// the new statement + void OnStatementCreate(EPServiceProvider serviceProvider, EPStatement statement); + + /// + /// Called to indicate that a statement has changed state. + /// + /// the service provider instance under which the statement has been created + /// the statement that changed state + void OnStatementStateChange(EPServiceProvider serviceProvider, EPStatement statement); + } + + public class StatementStateEventArgs : EventArgs + { + public EPServiceProvider ServiceProvider { get; set; } + public EPStatement Statement { get; set; } + + /// + /// Initializes a new instance of the class. + /// + public StatementStateEventArgs() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The service provider. + /// The statement. + public StatementStateEventArgs(EPServiceProvider serviceProvider, EPStatement statement) + { + ServiceProvider = serviceProvider; + Statement = statement; + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/EPStatementSyntaxException.cs b/NEsper.Core/NEsper.Core/client/EPStatementSyntaxException.cs new file mode 100755 index 000000000..2ea8d86de --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/EPStatementSyntaxException.cs @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client +{ + /// + /// This exception is thrown to indicate a problem in statement creation. + /// + [Serializable] + public class EPStatementSyntaxException : EPStatementException + { + /// Ctor. + /// error message + /// expression text + public EPStatementSyntaxException(String message, String expression) + : base(message, expression) + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/EPSubscriber.cs b/NEsper.Core/NEsper.Core/client/EPSubscriber.cs new file mode 100755 index 000000000..b2a9da2a3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/EPSubscriber.cs @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.client +{ + public class EPSubscriber + { + /// + /// Initializes a new instance of the class. + /// + public EPSubscriber() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The subscriber. + public EPSubscriber(object subscriber) + { + Subscriber = subscriber; + } + + /// + /// Initializes a new instance of the class. + /// + /// The subscriber. + /// The subscriber method. + public EPSubscriber(object subscriber, string subscriberMethod) + { + Subscriber = subscriber; + SubscriberMethod = subscriberMethod; + } + + /// + /// Gets or sets the subscriber instance. + /// + public object Subscriber { get; set; } + + /// + /// Gets or sets the subscriber method. + /// + public string SubscriberMethod { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/EPSubscriberException.cs b/NEsper.Core/NEsper.Core/client/EPSubscriberException.cs new file mode 100755 index 000000000..0008c66f6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/EPSubscriberException.cs @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client +{ + /// + /// This exception is thrown to indicate that a subscriber registration failed + /// such as when the subscribe does not expose an acceptable method to receive + /// statement results. + /// + [Serializable] + public class EPSubscriberException : EPException + { + /// Ctor. + /// error message + public EPSubscriberException(String message) + : base(message) + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/EventBean.cs b/NEsper.Core/NEsper.Core/client/EventBean.cs new file mode 100755 index 000000000..2734c078b --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/EventBean.cs @@ -0,0 +1,84 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client +{ + /// + /// Interface for event representation. All events have an . + /// Events also usually have one or more event properties. This interface allows the querying + /// of event type, event property values and the underlying event object. + /// + public interface EventBean + { + /// + /// Return the instance that describes the set of properties available for this event. + /// + /// event type + EventType EventType { get; } + + /// + /// Returns the value of an event property for the given property name or property expression. + /// Returns null if the property value is null. Throws an exception if the expression is not valid against the event type. + /// The method takes a property name or property expression as a parameter. Property expressions may include indexed properties + /// via the syntax "name[index]", mapped properties via the syntax "name('key')", nested properties via the syntax "outer.inner" or + /// combinations thereof. + /// + Object this[string property] { get; } + + /// + /// Returns the value of an event property for the given property name or property expression. + /// Returns null if the property value is null. Throws an exception if the expression is not valid against the event type. + /// The method takes a property name or property expression as a parameter. Property expressions may include indexed properties + /// via the syntax "name[index]", mapped properties via the syntax "name('key')", nested properties via the syntax "outer.inner" or + /// combinations thereof. + /// + /// name or expression of the property whose value is to be retrieved + /// + /// the value of a property with the specified name. + /// + /// PropertyAccessException - if there is no property of the specified name, or the property cannot be accessed + Object Get(String propertyExpression); + + /// + /// Get the underlying data object to this event wrapper. + /// + /// + /// underlying data object, usually either a Map or a bean instance. + /// + object Underlying { get; } + + /// + /// Returns event beans or array of event bean for a property name or property expression. + /// + /// For use with properties whose value is itself an event or whose value can be represented as + /// an event by the underlying event representation. + /// + /// The of the event bean Instance(s) returned by this method can be + /// determined by + /// . Use + /// to obtain a list of properties that return fragments from an event type. + /// + /// Returns null if the property value is null or the property value cannot be represented as a + /// fragment by the underlying representation. + /// + /// The method takes a property name or property expression as a parameter. Property expressions may + /// include indexed properties via the syntax "name[index]", mapped properties via the syntax "name('key')", + /// nested properties via the syntax "outer.inner" or combinations thereof. + /// + /// name or expression of the property whose value is to be presented as an EventBean or array of EventBean + /// + /// the value of a property as an EventBean or array of EventBean + /// + /// PropertyAccessException - if there is no property of the specified name, or the property cannot be accessed + Object GetFragment(String propertyExpression); + + //T GetFragment(string propertyExpression); + } +} diff --git a/NEsper.Core/NEsper.Core/client/EventBeanFactory.cs b/NEsper.Core/NEsper.Core/client/EventBeanFactory.cs new file mode 100755 index 000000000..2bb8ffb87 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/EventBeanFactory.cs @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client +{ + /// + /// Factory for instances given an underlying event object. + /// + /// Not transferable between engine instances. + /// + public interface EventBeanFactory + { + /// Wraps the underlying event object. + /// event to wrap + /// event bean + EventBean Wrap(Object underlying); + } +} diff --git a/NEsper.Core/NEsper.Core/client/EventExtensions.cs b/NEsper.Core/NEsper.Core/client/EventExtensions.cs new file mode 100755 index 000000000..f6df38774 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/EventExtensions.cs @@ -0,0 +1,126 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; +using System.Linq; +using System.Xml.Linq; + +namespace com.espertech.esper.client +{ + using util; + using events.util; + + public static class EventExtensions + { + /// + /// Converts the element to Update event args. + /// + /// The event element. + /// The event bean factory. + /// + public static UpdateEventArgs ToUpdateEventArgs(this XElement eventElement, Func eventBeanFactory) + { + if (eventElement == null) { + throw new ArgumentNullException("eventElement"); + } + + var oldElement = eventElement.Element(XName.Get("old")); + if (oldElement == null) { + throw new InvalidDataException("missing required element 'old'"); + } + + var newElement = eventElement.Element(XName.Get("new")); + if (newElement == null) { + throw new InvalidDataException("missing required element 'new'"); + } + + // find the xelements that represent the old and new event beans + // and convert them back into an eventBean form + var oldEvents = oldElement + .Elements() + .Select(eventBeanFactory.Invoke) + .ToArray(); + var newEvents = newElement + .Elements() + .Select(eventBeanFactory.Invoke) + .ToArray(); + + // construct event args + var updateEventArgs = new UpdateEventArgs( + null, + null, + newEvents, + oldEvents); + + return updateEventArgs; + } + + /// + /// Converts the event bean into a contract event. + /// + /// The event bean. + /// + public static XElement ToXElement(this EventBean eventBean) + { + if (eventBean.Underlying is XElement) + { + return (XElement)eventBean.Underlying; + } + + var renderingOptions = new XMLRenderingOptions {IsDefaultAsAttribute = false, PreventLooping = true}; + var elementRendererImpl = new XElementRendererImpl(eventBean.EventType, renderingOptions); + + return elementRendererImpl.Render( + "eventBean", eventBean); + } + + /// + /// Converts the array of even beans into an XElement. + /// + /// The event beans. + /// Name of the element. + /// + public static XElement ToXElement(this EventBean[] eventBeans, string elementName) + { + if (eventBeans != null) + { + return new XElement( + elementName, + eventBeans.Select(ToXElement).Cast().ToArray()); + } + + return new XElement(elementName); + } + + /// + /// Converts the Update events args into an XElement. + /// + /// The instance containing the event data. + /// Name of the element. + /// + public static XElement ToXElement(this UpdateEventArgs updateEventArgs, string elementName) + { + return new XElement( + elementName, + new XElement("statement", updateEventArgs.Statement.Name), + ToXElement(updateEventArgs.NewEvents, "new"), + ToXElement(updateEventArgs.OldEvents, "old")); + } + + /// + /// Converts the Update events args into an XElement. + /// + /// The instance containing the event data. + /// + public static XElement ToXElement(this UpdateEventArgs updateEventArgs) + { + return ToXElement(updateEventArgs, "Update"); + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/EventPropertyDescriptor.cs b/NEsper.Core/NEsper.Core/client/EventPropertyDescriptor.cs new file mode 100755 index 000000000..acb79e1f5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/EventPropertyDescriptor.cs @@ -0,0 +1,203 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client +{ + /// + /// Descriptor for event property names, property types and access metadata. + /// + public class EventPropertyDescriptor + { + /// + /// Ctor. + /// + /// name of the property + /// the property type + /// is the component type if the property is an indexed property + /// true if the access to property value access requires an integer index value + /// true if the access to property value access requires a string map key + /// true if the property is an indexed property, i.e. type is an array or the property value access requires an integer index value + /// true if the property is a mapped property, i.e. type is an Map or the property value access requires an string map key + /// true if the property value can be represented as an EventBean and property type can be represented as an EventType + public EventPropertyDescriptor(String propertyName, Type propertyType, Type propertyComponentType, bool requiresIndex, bool requiresMapkey, bool indexed, bool mapped, bool fragment) + { + PropertyName = propertyName; + PropertyType = propertyType; // GetBoxedType + PropertyComponentType = propertyComponentType; + RequiresIndex = requiresIndex; + RequiresMapKey = requiresMapkey; + IsIndexed = indexed; + IsMapped = mapped; + IsFragment = fragment; + } + + /// + /// Returns the property name. + /// + /// + /// property name + /// + public string PropertyName { get; private set; } + + /// + /// Returns the property underlying type. + /// + /// Note that a null values is possible as null values can be selected. + /// + /// + /// underlying property type + /// + public Type PropertyType { get; private set; } + + /// + /// Gets property component type. + /// + /// The type of the property component. + public Type PropertyComponentType { get; set; } + + /// + /// Returns true to indicate that the property is an indexed property and requires + /// an index to access elements of the indexed property. Returns false to indicate + /// that the property is not an indexed property or does not require an index for + /// property value access. + /// + /// For object events, a getter-method that takes a single integer + /// parameter is considered an indexed property that requires an index for access. + /// + /// A getter-method that returns an array is considered an index property but does + /// not require an index for access. + /// + /// + /// true to indicate that property value access requires an index value + /// + public bool RequiresIndex { get; private set; } + + /// + /// Returns true to indicate that the property is a mapped property and requires a + /// map key to access elements of the mapped property. Returns false to indicate that + /// the property is not a mapped property or does not require a map key for property + /// value access. + /// + /// For object events, a getter-method that takes a single string parameter + /// is considered a mapped property that requires a map key for access. + /// + /// A getter-method that returns a Map is considered a mapped property but does not + /// require a map key for access. + /// + /// + /// true to indicate that property value access requires an index value + /// + public bool RequiresMapKey { get; private set; } + + /// + /// Returns true for indexed properties, returns false for all other property + /// styles. + /// + /// An indexed property is a property returning an array value or a getter-method + /// taking a single integer parameter. + /// + /// + /// indicator whether this property is an index property + /// + public bool IsIndexed { get; private set; } + + /// + /// Returns true for mapped properties, returns false for all other property styles. + /// + /// A mapped property is a property returning a Map value or a getter-method taking + /// a single string (key) parameter. + /// + /// + /// indicator whether this property is a mapped property + /// + public bool IsMapped { get; private set; } + + /// + /// Returns true to indicate that the property value can itself be represented as an + /// and that the property type can be represented as an + /// . + /// + /// + /// indicator whether property is itself a complex data structure representable as a + /// nested + /// + public bool IsFragment { get; private set; } + + /// + /// Serves as a hash function for a particular type. + /// + /// + /// A hash code for the current . + /// + public override int GetHashCode() + { + unchecked { + int result = (PropertyName != null ? PropertyName.GetHashCode() : 0); + result = (result*397) ^ (PropertyType != null ? PropertyType.GetHashCode() : 0); + result = (result * 397) ^ (PropertyComponentType != null ? PropertyComponentType.GetHashCode() : 0); + result = (result * 397) ^ RequiresIndex.GetHashCode(); + result = (result*397) ^ RequiresMapKey.GetHashCode(); + result = (result*397) ^ IsIndexed.GetHashCode(); + result = (result*397) ^ IsMapped.GetHashCode(); + result = (result*397) ^ IsFragment.GetHashCode(); + return result; + } + } + + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// + /// true if the specified is equal to the current ; otherwise, false. + /// + /// + /// The parameter is null. + /// + public override bool Equals(Object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != typeof(EventPropertyDescriptor)) return false; + + EventPropertyDescriptor that = (EventPropertyDescriptor) obj; + + if (IsFragment != that.IsFragment) return false; + if (IsIndexed != that.IsIndexed) return false; + if (IsMapped != that.IsMapped) return false; + if (RequiresIndex != that.RequiresIndex) return false; + if (RequiresMapKey != that.RequiresMapKey) return false; + if (PropertyComponentType != null ? !Equals(PropertyComponentType, that.PropertyComponentType) : that.PropertyComponentType != null) return false; + if (!Equals(PropertyComponentType, that.PropertyComponentType)) return false; + if (!Equals(PropertyName, that.PropertyName)) return false; + if (PropertyType != null ? !Equals(PropertyType, that.PropertyType) : that.PropertyType != null) return false; + + return true; + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override String ToString() + { + return "name " + PropertyName + + " propertyType " + PropertyType + + " propertyComponentType " + PropertyComponentType + + " isRequiresIndex " + RequiresIndex + + " isRequiresMapkey " + RequiresMapKey + + " isIndexed " + IsIndexed + + " isMapped " + IsMapped + + " isFragment " + IsFragment; + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/EventPropertyGetter.cs b/NEsper.Core/NEsper.Core/client/EventPropertyGetter.cs new file mode 100755 index 000000000..2d81a32b3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/EventPropertyGetter.cs @@ -0,0 +1,154 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client +{ + /// + /// Get property values from an event instance for a given event property. Instances that implement this interface + /// are usually bound to a particular and cannot be used to + /// access instances of a different type. + /// + public interface EventPropertyGetter + { + /// + /// Return the value for the property in the event object specified when the instance was obtained. Useful for + /// fast access to event properties. Throws a PropertyAccessException if the getter instance doesn't match the + /// EventType it was obtained from, and to indicate other property access problems. + /// + /// is the event to get the value of a property from + /// value of property in event + /// PropertyAccessException to indicate that property access failed + Object Get(EventBean eventBean); + + /// + /// Returns true if the property exists, or false if the type does not have such a property. + /// + /// Useful for dynamic properties of the syntax "property?" and the dynamic nested/indexed/mapped versions. + /// Dynamic nested properties follow the syntax "property?.nested" which is equivalent to "property?.nested?". + /// If any of the properties in the path of a dynamic nested property return null, the dynamic nested property + /// does not exists and the method returns false. For non-dynamic properties, this method always returns + /// true since a getter would not be available unless + /// + /// is the event to check if the dynamic property exists + /// + /// indictor whether the property exists, always true for non-dynamic (default) properties + /// + bool IsExistsProperty(EventBean eventBean); + + /// + /// Returns or array of for a property name or + /// property expression. For use with properties whose value is itself an event or whose value can + /// be represented as an event by the underlying event representation. The of + /// the Instance(s) returned by this method can be determined by + /// . Use to obtain a list of + /// properties that return fragments from an event type. Returns null if the property value is null or the + /// property value cannot be represented as a fragment by the underlying representation. + /// + /// is the event to get the fragment value of a property + /// + /// the value of a property as an EventBean or array of EventBean + /// + /// PropertyAccessException - if there is no property of the specified name, or the property cannot be accessed + Object GetFragment(EventBean eventBean); + } + + public class ProxyEventPropertyGetter : EventPropertyGetter + { + public Func ProcGet { get; set; } + public Func ProcGetFragment { get; set; } + public Func ProcIsExistsProperty { get; set; } + + /// + /// Initializes a new instance of the class. + /// + public ProxyEventPropertyGetter() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The _p method get. + /// The _p method get fragment. + /// The _p method is exists property. + public ProxyEventPropertyGetter(Func pMethodGet, + Func pMethodGetFragment, + Func pMethodIsExistsProperty) + { + ProcGet = pMethodGet; + ProcGetFragment = pMethodGetFragment; + ProcIsExistsProperty = pMethodIsExistsProperty; + } + + /// + /// Return the value for the property in the event object specified when the + /// instance was obtained. Useful for fast access to event properties. Throws a + /// PropertyAccessException if the getter instance doesn't match the EventType it was obtained + /// from, and to indicate other property access problems. + /// + /// is the event to get the value of a property from + /// + /// value of property in event + /// + /// PropertyAccessException to indicate that property access failed + public object Get(EventBean eventBean) + { + return ProcGet.Invoke(eventBean); + } + + /// + /// Returns true if the property exists, or false if the type does not have such a + /// property. + /// + /// Useful for dynamic properties of the syntax "property?" and the dynamic + /// nested/indexed/mapped versions. Dynamic nested properties follow the syntax + /// "property?.nested" which is equivalent to "property?.nested?". If any of the properties in + /// the path of a dynamic nested property return null, the dynamic nested property does + /// not exists and the method returns false. + /// + /// For non-dynamic properties, this method always returns true since a getter + /// would not be available unless + /// + /// is the event to check if the dynamic property exists + /// + /// indictor whether the property exists, always true for non-dynamic (default) + /// properties + /// + public bool IsExistsProperty(EventBean eventBean) + { + return ProcIsExistsProperty.Invoke(eventBean); + } + + /// + /// Returns or array of for + /// a property name or property expression. + /// + /// For use with properties whose value is itself an event or whose value can be + /// represented as an event by the underlying event representation. + /// + /// The of the Instance(s) + /// returned by this method can be determined by. + /// Use to obtain a list of + /// properties that return fragments from an event type. + /// + /// Returns null if the property value is null or the property value cannot be + /// represented as a fragment by the underlying representation. + /// + /// is the event to get the fragment value of a property + /// + /// the value of a property as an EventBean or array of EventBean + /// + /// PropertyAccessException - if there is no property of the specified name, or the property cannot be accessed + public object GetFragment(EventBean eventBean) + { + return ProcGetFragment.Invoke(eventBean); + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/EventPropertyGetterIndexed.cs b/NEsper.Core/NEsper.Core/client/EventPropertyGetterIndexed.cs new file mode 100755 index 000000000..edc25f9b5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/EventPropertyGetterIndexed.cs @@ -0,0 +1,68 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client +{ + /// + /// Get property values from an event instance for a given indexed event property by passing the array index. + /// Instances that implement this interface are usually bound to a particular + /// and cannot be used to access + /// instances of a different type. + /// + public interface EventPropertyGetterIndexed + { + /// + /// Return the value for the property in the event object specified when the instance was obtained. + /// Useful for fast access to event properties. Throws a PropertyAccessException if the getter instance + /// doesn't match the EventType it was obtained from, and to indicate other property access problems. + /// + /// is the event to get the value of a property from + /// the index value + /// value of indexed property in event + /// PropertyAccessException to indicate that property access failed + Object Get(EventBean eventBean, int index); + } + + public class ProxyEventPropertyGetterIndexed : EventPropertyGetterIndexed + { + public Func ProcGet { get; set; } + + /// + /// Initializes a new instance of the class. + /// + public ProxyEventPropertyGetterIndexed() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The method get delegate. + public ProxyEventPropertyGetterIndexed(Func procGet) + { + ProcGet = procGet; + } + + /// + /// Return the value for the property in the event object specified when the instance was obtained. + /// Useful for fast access to event properties. Throws a PropertyAccessException if the getter instance + /// doesn't match the EventType it was obtained from, and to indicate other property access problems. + /// + /// is the event to get the value of a property from + /// the index value + /// value of indexed property in event + /// PropertyAccessException to indicate that property access failed + public Object Get(EventBean eventBean, int index) + { + return ProcGet.Invoke(eventBean, index); + } + } + +} diff --git a/NEsper.Core/NEsper.Core/client/EventPropertyGetterMapped.cs b/NEsper.Core/NEsper.Core/client/EventPropertyGetterMapped.cs new file mode 100755 index 000000000..6c63100a7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/EventPropertyGetterMapped.cs @@ -0,0 +1,68 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client +{ + /// + /// Get property values from an event instance for a given mapped event property by passing + /// the map string key. Instances that implement this interface are usually bound to a particular + /// and cannot be used to access + /// instances of a different type. + /// + public interface EventPropertyGetterMapped + { + /// + /// Return the value for the property in the event object specified when the instance was obtained. + /// Useful for fast access to event properties. Throws a PropertyAccessException if the getter instance + /// doesn't match the EventType it was obtained from, and to indicate other property access problems. + /// + /// is the event to get the value of a property from + /// the map key value + /// value of property in event + /// com.espertech.esper.client.PropertyAccessException to indicate that property access failed + Object Get(EventBean eventBean, String mapKey); + } + + public class ProxyEventPropertyGetterMapped : EventPropertyGetterMapped + { + public Func ProcGet { get; set; } + + /// + /// Initializes a new instance of the class. + /// + public ProxyEventPropertyGetterMapped() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The method get delegate. + public ProxyEventPropertyGetterMapped(Func procGet) + { + ProcGet = procGet; + } + + + /// + /// Return the value for the property in the event object specified when the instance was obtained. + /// Useful for fast access to event properties. Throws a PropertyAccessException if the getter instance + /// doesn't match the EventType it was obtained from, and to indicate other property access problems. + /// + /// is the event to get the value of a property from + /// the map key value + /// value of property in event + /// com.espertech.esper.client.PropertyAccessException to indicate that property access failed + public Object Get(EventBean eventBean, String mapKey) + { + return ProcGet.Invoke(eventBean, mapKey); + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/EventSender.cs b/NEsper.Core/NEsper.Core/client/EventSender.cs new file mode 100755 index 000000000..81e98b8f9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/EventSender.cs @@ -0,0 +1,61 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client +{ + /// + /// Returns a facility to process event objects that are of a known type. + /// + /// Obtained via the method the sender is + /// specific to a given event type and may not process event objects of any other event + /// type; See the method documentatiom for more details. + /// + /// Obtained via the method the sender cooperates + /// with plug-in event representations to reflect upon the event object to determine an + /// appropriate event type to process the event. + /// + public interface EventSender + { + /// + /// Processes the event object. + /// + /// Use the route method for sending events into the runtime from within event code. + /// to avoid the possibility of a stack overflow due to nested calls to SendEvent. + /// + /// to process + /// EPException if a runtime error occured. + void SendEvent(Object theEvent); + + /// + /// Route the event object back to the event stream processing runtime for internal + /// dispatching, to avoid the possibility of a stack overflow due to nested calls to + /// SendEvent. The route event is processed just like it was sent to the runtime, + /// that is any active expressions seeking that event receive it. The routed event + /// has priority over other events sent to the runtime. In a single-threaded application + /// the routed event is processed before the next event is sent to the runtime through + /// the EPRuntime.SendEvent method. + /// + /// to process + /// EPException is thrown when the processing of the event lead to an error + void Route(Object theEvent); + } + + /// + /// Returns a facility to process event objects that are of a known type. + /// Obtained via the method the sender + /// is specific to a given event type and may not process event objects of any + /// other event type; See the method documentatiom for more details. + /// + /// Obtained via the method the + /// sender cooperates with plug-in event representations to reflect upon the event + /// object to determine an appropriate event type to process the event. + /// + public delegate void EventSenderDelegate(Object theEvent); +} diff --git a/NEsper.Core/NEsper.Core/client/EventType.cs b/NEsper.Core/NEsper.Core/client/EventType.cs new file mode 100755 index 000000000..ecf9bb06c --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/EventType.cs @@ -0,0 +1,210 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.events; + +namespace com.espertech.esper.client +{ + /// + /// This interface provides metadata on events. + /// + /// The interface exposes events as organizations of named values. The contract is that any event in the system must have a + /// name-based way of accessing sub-data within its event type. A simple example is an object: the names can be property names, + /// and those properties can have still more properties beneath them. Another example is a Map structure. Here string names + /// can referto data objects. + /// + /// The interface presents an immutable view of events. There are no methods to change property values. Events by definition are + /// an observation of a past occurrance or state change and may not be modified. + /// + /// Information on the super-types (superclass and interfaces implemented by events) is also available, for PONO events as well + /// as for Map event types that has supertypes. + /// + /// Implementations provide metadata on the properties that an implementation itself provides. + /// + /// Implementations also allow property expressioms that may use nested, indexed, mapped or a combination of these as a syntax + /// to access property types and values. + /// + /// Implementations in addition may provide a means to access property values as event fragments, which are typed events themselves. + /// + public interface EventType + { + /// + /// Get the type of an event property. + /// + /// Returns null if the property name or property expression is not valid against the event type. Can also return null + /// if a select-clause selects a constant null value. The method takes a property name or property expression as + /// a parameter. Property expressions may include indexed properties via the syntax "name[index]", mapped properties via the syntax + /// "name('key')", nested properties via the syntax "outer.inner" or combinations thereof. + /// + /// Returns unboxed (such as 'typeof(int)') as well as boxed 'typeof(int?)' type. + /// + /// is the property name or property expression + /// + /// type of the property, the unboxed or the boxed type. + /// + Type GetPropertyType(String propertyExpression); + + /// + /// Check that the given property name or property expression is valid for this event type, ie. that the property exists + /// on the event type. + /// + /// The method takes a property name or property expression as a parameter. Property expressions may include indexed + /// properties via the syntax "name[index]", mapped properties via the syntax "name('key')", nested properties via the + /// syntax "outer.inner" or combinations thereof. + /// + /// is the property name or property expression to check + /// true if exists, false if not + bool IsProperty(String propertyExpression); + + /// + /// Get the getter of an event property or property expression: + /// Getters are useful when an application receives events of the same event type multiple times and requires fast access + /// to an event property or nested, indexed or mapped property. + /// + /// Returns null if the property name or property expression is not valid against the event type. + /// + /// The method takes a property name or property expression as a parameter. Property expressions may include indexed + /// properties via the syntax "name[index]", mapped properties via the syntax "name('key')", nested properties via the + /// syntax "outer.inner" or combinations thereof. + /// + /// is the property name or property expression + /// + /// a getter that can be used to obtain property values for event instances of the same event type + /// + EventPropertyGetter GetGetter(String propertyExpression); + + /// + /// Returns the event type of the fragment that is the value of a property name or property expression. + /// + /// Returns null if the property name or property expression is not valid or does not return a fragment for the event type. + /// + /// The provides a flag that indicates which properties provide fragment events. + /// + /// This is useful for navigating properties that are itself events or other well-defined types that the underlying event + /// representation may represent as an event type. It is up to each event representation to determine what properties can + /// be represented as event types themselves. + /// + /// The method takes a property name or property expression as a parameter. Property expressions may include indexed properties + /// via the syntax "name[index]", mapped properties via the syntax "name('key')", nested properties via the syntax "outer.inner" + /// or combinations thereof. + /// + /// The underlying event representation may not support providing fragments or therefore fragment event types for any or all + /// properties, in which case the method returns null. + /// + /// Use the method to obtain a list of properties for which a fragment event type may be + /// retrieved by this method. + /// + /// is the name of the property to return the fragment event type + /// fragment event type of the property + FragmentEventType GetFragmentType(String propertyExpression); + + /// + /// Get the class that represents the type of the event type. + /// Returns an event class if the schema represents a PONO event type. + /// Returns IDictionary is the schema represents a collection of values in a Map. + /// + /// type of the event object + Type UnderlyingType { get; } + + /// + /// Get the property names for the event type. + /// + /// Note that properties do not have a defined order. Your application should not rely on the order of properties returned by this method. + /// + /// + /// The method does not return property names of inner or nested types. + /// + /// + /// + /// A string array containing the property names of this typed event data object. + /// + string[] PropertyNames { get; } + + /// Get property descriptors for the event type. + /// + /// Note that properties do not have a defined order. Your application should not rely + /// on the order of properties returned by this method. + /// + /// + /// The method does not return property information of inner or nested types. + /// + /// + /// descriptors for all known properties of the event type. + IList PropertyDescriptors { get; } + + /// + /// Get the property descriptor for a given property of the event, or null if a property by that name was not found. + /// + /// The property name parameter does accept a property expression. It therefore does not allow the indexed, + /// mapped or nested property expression syntax and only returns the descriptor for the event type's known + /// properties. + /// + /// + /// The method does not return property information of inner or nested types. + /// + /// + /// For returning a property descriptor for nested, indexed or mapped properties + /// + /// + /// + /// property name + /// descriptor for the named property + EventPropertyDescriptor GetPropertyDescriptor(String propertyName); + + /// + /// Returns an enumeration of event types that are super to this event type, from which this event type inherited event properties. + /// + /// For PONO instances underlying the event this method returns the event types for all superclasses extended by + /// the PONO and all interfaces implemented by the PONO. + /// + /// The super types. + /// an array of event types + EventType[] SuperTypes { get; } + + /// + /// Returns enumerator over all super types to event type, going up the hierarchy and including all interfaces (and their + /// extended interfaces) and superclasses as EventType instances. + /// + /// The deep super types. + EventType[] DeepSuperTypes { get; } + + /// + /// Returns the type name or null if no type name is assigned. + /// A type name is available for application-configured event types and for event types that represent events of a stream populated by insert-into. + /// No type name is available for anonymous statement-specific event type. + /// + /// The name. + /// type name or null if none assigned + string Name { get; } + + /// Get the getter of an event property that is a mapped event property: Getters are useful when an application receives events of the same event type multiple times and requires fast access to a mapped property. Returns null if the property name is not valid against the event type or the property is not a mapped property. The method takes a mapped property name (and not a property expression) as a parameter. + /// is the property name + /// a getter that can be used to obtain property values for event instances of the same event type + EventPropertyGetterMapped GetGetterMapped(String mappedPropertyName); + + /// Get the getter of an event property that is a indexed event property: Getters are useful when an application receives events of the same event type multiple times and requires fast access to a indexed property. Returns null if the property name is not valid against the event type or the property is not an indexed property. The method takes a indexed property name (and not a property expression) as a parameter. + /// is the property name + /// a getter that can be used to obtain property values for event instances of the same event type + EventPropertyGetterIndexed GetGetterIndexed(string indexedPropertyName); + + /// Returns the event type id assigned to the event type. + /// event type id + int EventTypeId { get; } + + /// Returns the property name of the property providing the start timestamp value. + /// start timestamp property name + string StartTimestampPropertyName { get; } + + /// Returns the property name of the property providing the end timestamp value. + /// end timestamp property name + string EndTimestampPropertyName { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/EventTypeException.cs b/NEsper.Core/NEsper.Core/client/EventTypeException.cs new file mode 100755 index 000000000..10621d971 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/EventTypeException.cs @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client +{ + /// + /// Indicates that a problem occurred looking up, assigning or creating and event type. + /// + [Serializable] + public class EventTypeException : EPException + { + /// Ctor. + /// supplies exception details + public EventTypeException(String message) + : base(message) + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/FragmentEventType.cs b/NEsper.Core/NEsper.Core/client/FragmentEventType.cs new file mode 100755 index 000000000..3c2a02685 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/FragmentEventType.cs @@ -0,0 +1,56 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.client +{ + /// + /// Provides an event type for a property of an event. + /// + /// A fragment is a property value that is itself an event, or that can be represented + /// as an event. Thereby a fragment comes with event type metadata and means of querying + /// the fragment's properties. + /// + /// A array or collection of property values that is an array of events or that can be + /// represented as an array of events has the indexed flag set. + /// + /// A map of property values that is an map of events or that can be represented as a map + /// of events has the mapped flag set. + /// + public class FragmentEventType + { + /// Ctor. + /// the event type for a property value for an event. + /// true to indicate that property value is an array of events + /// true + public FragmentEventType(EventType fragmentType, bool indexed, bool isNative) + { + FragmentType = fragmentType; + IsIndexed = indexed; + IsNative = isNative; + } + + /// + /// Returns true if the fragment type is an array. + /// If a property value is an array and thereby a fragment array, this flag is set to true. + /// + /// indicator if array fragment + public bool IsIndexed { get; private set; } + + /// + /// Returns the type of the fragment. + /// + /// fragment type + public EventType FragmentType { get; private set; } + + /// + /// Returns true if the fragment is a native representation, i.e. a type. + /// + /// indicator whether fragment is a type. + public bool IsNative { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/InitializerTransform.xslt b/NEsper.Core/NEsper.Core/client/InitializerTransform.xslt new file mode 100755 index 000000000..c230dfecb --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/InitializerTransform.xslt @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/MetricsReportingConfig.cs b/NEsper.Core/NEsper.Core/client/MetricsReportingConfig.cs new file mode 100755 index 000000000..aeedba002 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/MetricsReportingConfig.cs @@ -0,0 +1,186 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.type; + +namespace com.espertech.esper.client +{ + /// Configuratiom for metrics reporting. + [Serializable] + public class MetricsReportingConfig + { + /// Ctor. + public MetricsReportingConfig() + { + IsEnableMetricsReporting = false; + IsThreading = true; + EngineInterval = 10 * 1000; // 10 seconds + StatementInterval = 10 * 1000; + StatementGroups = new Dictionary(); + } + + /// + /// Add a statement group, allowing control of metrics reporting interval per statement or per multiple statements. + /// The reporting interval and be changed at runtime. + /// + /// Add pattern include and exclude criteria to control which + /// + /// of statement group, not connected to statement name, assigned as anarbitrary identifier for runtime changes to the interval + /// the statement group metrics configuration + public void AddStmtGroup(String name, StmtGroupMetrics config) + { + StatementGroups.Put(name, config); + } + + /// Returns true if metrics reporting is turned on, false if not. + /// indicator whether metrics reporting is turned on + public bool IsEnableMetricsReporting { get; set; } + + /// Returns true to indicate that metrics reporting takes place in a separate thread (default), or false to indicate that metrics reporting takes place as part of timer processing. + /// indicator whether metrics reporting is threaded + public bool IsThreading { get; set; } + + /// + /// Gets or sets the engine metrics production interval in milliseconds. + /// + /// Use a negative or zero value to disable engine metrics production. + /// + /// The engine interval. + public long EngineInterval { set; get; } + + /// + /// Gets or sets the statement metrics production interval in milliseconds, unless statement groups have been defined that override this setting for certain statements. + /// + /// The statement interval. + public long StatementInterval { get; set; } + + /// Returns a map of statement group and metrics configuration for the statement group. + /// map of statement group and metrics configuration + public IDictionary StatementGroups { get; private set; } + + /// Sets a new interval for a statement group identified by name. + /// name of statement group as assigned through configuration + /// new interval, or a -1 or zero value to disable reporting + public void SetStatementGroupInterval(String stmtGroupName, long newInterval) + { + StmtGroupMetrics metrics = StatementGroups.Get(stmtGroupName); + if (metrics != null) + { + metrics.Interval = newInterval; + } + else + { + throw new ConfigurationException("Statement group by name '" + stmtGroupName + "' could not be found"); + } + } + + /// + /// Class to configure statement metrics reporting for a group of one or more statements. + /// + [Serializable] + public class StmtGroupMetrics + { + /// + /// Ctor. + /// + public StmtGroupMetrics() + { + Patterns = new List>(); + Interval = 10000; + NumStatements = 100; + } + + /// + /// Include all statements in the statement group that match the SQL like-expression by statement name. + /// + /// to match + public void AddIncludeLike(String likeExpression) + { + Patterns.Add(new Pair(new StringPatternSetLike(likeExpression), true)); + } + + /// + /// Exclude all statements from the statement group that match the SQL like-expression by statement name. + /// + /// to match + public void AddExcludeLike(String likeExpression) + { + Patterns.Add(new Pair(new StringPatternSetLike(likeExpression), false)); + } + + /// + /// Include all statements in the statement group that match the regular expression by statement name. + /// + /// to match + public void AddIncludeRegex(String regexExpression) + { + Patterns.Add(new Pair(new StringPatternSetRegex(regexExpression), true)); + } + + /// + /// Exclude all statements in the statement group that match the regular expression by statement name. + /// + /// to match + public void AddExcludeRegEx(String regexExpression) + { + Patterns.Add(new Pair(new StringPatternSetRegex(regexExpression), false)); + } + + /// + /// Gets or sets the reporting interval for statement metrics for statements in the statement group. + /// + /// The interval. + public long Interval { get; set; } + + /// + /// Returns a list of patterns that indicate whether a statement, by the statement name matching or not matching + /// each pattern, falls within the statement group. + /// + /// Include-patterns are bool true in the pair of pattern and bool. Exclude-patterns are bool false. + /// + /// The patterns. + /// list of include and exclude pattern + public IList> Patterns { get; private set; } + + /// + /// Gets or sets the initial capacity number of statements held by the statement group. + /// + /// The num statements. + public int NumStatements { get; set; } + + /// + /// Gets or sets a value to indicate that inactive statements (statements without events or timer activity) + /// are also reported, or false to omit reporting for inactive statements. + /// + /// + /// true if this instance is report inactive; otherwise, false. + /// + public bool IsReportInactive { get; set; } + + /// + /// If this flag is set then all statement names are automatically included in this statement group, + /// and through exclude-pattern certain statement names can be omitted + /// + /// If this flag is not set then all statement names are automatically excluded in this statement group, + /// and through include-pattern certain statement names can be included. + /// + /// The default is false, i.e. statements must be explicitly included. + /// + /// + /// true if this instance is default include; otherwise, false. + /// + /// true for include all statements, false for explicitly include + public bool IsDefaultInclude { get; set; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/ParameterModel.cs b/NEsper.Core/NEsper.Core/client/ParameterModel.cs new file mode 100755 index 000000000..11e2e70a4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/ParameterModel.cs @@ -0,0 +1,207 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; +using System.Data.Common; +using System.Text; + +using com.espertech.esper.util; + +namespace com.espertech.esper.client +{ + /// + /// ParameterModel lets the user specify the way that the parameter + /// model works for backend repositories. ADO.NET allows providers + /// to specify the manner in which parameters work. This causes a + /// great deal of ambiguity in how to deal with them in code. This + /// class allows the client to determine how to bind parameters to + /// the ADO.NET provider. + /// + + public class ParameterModel + { + /// + /// Initializes a new instance of the class. + /// + public ParameterModel() + { + this.m_prefix = "@"; + this.m_style = ParameterStyle.Named; + } + + /// + /// Initializes a new instance of the class. + /// + /// The prefix. + /// The style. + public ParameterModel(String prefix, ParameterStyle style) + { + this.m_prefix = prefix; + this.m_style = style; + } + + private string m_prefix; + + /// + /// Gets or sets the prefix used before a parameter. + /// + /// The parameter prefix. + public string Prefix + { + get { return m_prefix; } + set { m_prefix = value; } + } + + private ParameterStyle m_style; + + /// + /// Gets or sets the parameter style. + /// + /// The parameter style. + public ParameterStyle Style + { + get { return m_style; } + set { m_style = value; } + } + + /// + /// Gets the formatted version of the named paramter. + /// + /// The index. + /// + public String GetNamedParameter( int index ) + { + return String.Format("{0}arg{1}", m_prefix, index); + } + + /// + /// Creates the db command. + /// + /// The parse fragments. + /// + public String CreateDbCommand(IEnumerable parseFragments) + { + int parameterCount = 0; + + StringBuilder buffer = new StringBuilder(); + foreach (PlaceholderParser.Fragment fragment in parseFragments) + { + if (!fragment.IsParameter) + { + buffer.Append(fragment.Value); + } + else if ( m_style == ParameterStyle.Positional ) + { + buffer.Append(m_prefix); + } + else if ( m_style == ParameterStyle.Named ) + { + buffer.Append(GetNamedParameter(parameterCount++)); + } + } + + return buffer.ToString(); + } + + /// + /// Creates the a pseudo sql command that replaces parameters with + /// question marks. The question marks can then be parsed at the + /// cache and converted back into native ADO.NET parameters. + /// + /// The parse fragments. + /// + public String CreatePseudoCommand(IEnumerable parseFragments) + { + StringBuilder buffer = new StringBuilder(); + foreach (PlaceholderParser.Fragment fragment in parseFragments) + { + if (fragment.IsParameter) + { + buffer.Append('?'); + } + else + { + buffer.Append(fragment.Value); + } + } + + return buffer.ToString(); + } + + /// + /// Creates the db parameters. + /// + /// The command. + /// The parameter names. + public void CreateDbParameters(DbCommand command, IEnumerable parameterNames) + { + int parameterCount = 0; + + command.Parameters.Clear(); + + foreach (string parameterName in parameterNames) + { + if (m_style == ParameterStyle.Positional) + { + DbParameter paramObj = command.CreateParameter(); + command.Parameters.Add(paramObj); + } + else if (m_style == ParameterStyle.Named) + { + DbParameter paramObj = command.CreateParameter(); + paramObj.ParameterName = GetNamedParameter(parameterCount++); + command.Parameters.Add(paramObj); + } + } + } + + /// + /// Creates the db parameters. + /// + /// The command. + /// The parse fragments. + public void CreateDbParameters( DbCommand command, IEnumerable parseFragments ) + { + int parameterCount = 0; + + command.Parameters.Clear(); + + foreach (PlaceholderParser.Fragment fragment in parseFragments) + { + if (!fragment.IsParameter) + { + } + else if (m_style == ParameterStyle.Positional) + { + DbParameter paramObj = command.CreateParameter(); + command.Parameters.Add(paramObj); + } + else if (m_style == ParameterStyle.Named) + { + DbParameter paramObj = command.CreateParameter(); + paramObj.ParameterName = GetNamedParameter(parameterCount++); + command.Parameters.Add(paramObj); + } + } + } + } + + public enum ParameterStyle + { + /// + /// Provider expects parameters to be named. + /// + Named, + /// + /// Provider expects parameters to be positional. + /// + Positional + } +} diff --git a/NEsper.Core/NEsper.Core/client/PropertyAccessException.cs b/NEsper.Core/NEsper.Core/client/PropertyAccessException.cs new file mode 100755 index 000000000..a20a4036b --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/PropertyAccessException.cs @@ -0,0 +1,97 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Text; + +namespace com.espertech.esper.client +{ + /// + /// This exception is thrown to indicate a problem with a accessing a property of an . + /// + public sealed class PropertyAccessException : Exception + { + private readonly string _expression; + + /// + /// Constructor. + /// + /// is the error message + /// property expression + public PropertyAccessException(string message, string propertyExpression) + : base(message) + { + _expression = propertyExpression; + } + + /// + /// Constructor for an inner exception and message. + /// + /// is the error message + /// is the inner exception + public PropertyAccessException(string message, Exception cause) + : base(message, cause) + { + } + + /// + /// Constructor for an inner exception and message. + /// + /// is the error message + public PropertyAccessException(string message) + : base(message) + { + } + + /// + /// Constructor. + /// + /// is the inner exception + public PropertyAccessException(Exception cause) + : base(String.Empty, cause) + { + } + + /// + /// Generates the Not-A-Valid-Property exception + /// + /// property expression + /// exception exception + public static PropertyAccessException NotAValidProperty(string propertyExpression) + { + return new PropertyAccessException(string.Format("Property named '{0}' is not a valid property name for this type", propertyExpression)); + } + + /// + /// Gets a message that describes the current exception. + /// + public override string Message + { + get + { + StringBuilder msg; + + if (!string.IsNullOrEmpty(base.Message)) + { + msg = new StringBuilder(base.Message); + } + else + { + msg = new StringBuilder("Unexpected exception"); + } + if (_expression != null) + { + msg.Append(" ["); + msg.Append(_expression); + msg.Append(']'); + } + return msg.ToString(); + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/client/PropertyResolutionStyle.cs b/NEsper.Core/NEsper.Core/client/PropertyResolutionStyle.cs new file mode 100755 index 000000000..f382537ff --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/PropertyResolutionStyle.cs @@ -0,0 +1,86 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System.Configuration; + +using com.espertech.esper.compat; + +namespace com.espertech.esper.client +{ + public enum PropertyResolutionStyle + { + /// + /// Properties are only matched if the names are identical in name + /// and case to the original property name. + /// + + CASE_SENSITIVE, + + /// + /// Properties are matched if the names are identical. A case insensitive + /// search is used and will choose the first property that matches + /// the name exactly or the first property that matches case insensitively + /// should no match be found. + /// + + CASE_INSENSITIVE, + + /// + /// Properties are matched if the names are identical. A case insensitive + /// search is used and will choose the first property that matches + /// the name exactly case insensitively. If more than one 'name' can be + /// mapped to the property an exception is thrown. + /// + + DISTINCT_CASE_INSENSITIVE, + + /// + /// Default + /// + DEFAULT = CASE_SENSITIVE + } + + /// + /// A class that helps with the use of the PropertyResolutionStyle. Among other + /// things it allows developers to get or set the property resolution style that + /// should be used when one is not specified. + /// + + public class PropertyResolutionStyleHelper + { + private static PropertyResolutionStyle? defaultPropertyResolutionStyle; + + /// + /// Initializes the class. + /// + static PropertyResolutionStyleHelper() + { + // Default setting is consistent with the way that esper works + defaultPropertyResolutionStyle = PropertyResolutionStyle.CASE_SENSITIVE; + // Check with configuration management to see if the user has specified a + // setting that overrides the default behavior. + string styleSetting = ConfigurationManager.AppSettings["PropertyResolutionStyle"]; + if ( styleSetting != null ) + { + defaultPropertyResolutionStyle = EnumHelper.Parse(styleSetting); + } + } + + /// + /// Gets or sets the property resolution style that should be used whe + /// one is not specified. + /// + + public static PropertyResolutionStyle DefaultPropertyResolutionStyle + { + get { return defaultPropertyResolutionStyle.GetValueOrDefault(PropertyResolutionStyle.CASE_SENSITIVE); } + set { defaultPropertyResolutionStyle = value; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/StatementAwareUpdateListener.cs b/NEsper.Core/NEsper.Core/client/StatementAwareUpdateListener.cs new file mode 100755 index 000000000..d9ceea87f --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/StatementAwareUpdateListener.cs @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client +{ + /// + /// Defines an interface to notify of new and old events. + /// Also see for Update listeners that only + /// require event delivery. + /// + [Obsolete] + public interface StatementAwareUpdateListener + { + /// + /// Notify that new events are available or old events are removed. + /// + /// If the call to Update contains new (inserted) events, then the first argument will be a non-empty list and + /// the second will be empty. Similarly, if the call is a notification of deleted events, then the first argument + /// will be empty and the second will be non-empty. + /// + /// + /// Either the newEvents or oldEvents will be non-null. This method won't be called with both arguments being null, + /// (unless using output rate limiting or force-output options), + /// but either one could be null. The same is true for zero-length arrays. + /// Either newEvents or oldEvents will be non-empty. If both are non-empty, then the Update is a modification + /// notification. + /// + /// + /// + /// is any new events. This will be null or empty if the Update is for old events only. + /// + /// + /// is any old events. This will be null or empty if the Update is for new events only. + /// + /// is the statement producing the result + /// + /// is the engine instance that provided the administrative API that created the statement which produces the result + /// + void Update(EventBean[] newEvents, EventBean[] oldEvents, EPStatement statement, EPServiceProvider epServiceProvider); + } +} diff --git a/NEsper.Core/NEsper.Core/client/StatementEventArgs.cs b/NEsper.Core/NEsper.Core/client/StatementEventArgs.cs new file mode 100755 index 000000000..ef7c6c189 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/StatementEventArgs.cs @@ -0,0 +1,36 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; + +namespace com.espertech.esper.client +{ + public class StatementEventArgs : EventArgs + { + private readonly EPStatement _statement; + + /// + /// Gets the statement. + /// + /// The statement. + public EPStatement Statement + { + get { return _statement; } + } + + /// + /// Initializes a new instance of the class. + /// + /// The _statement. + public StatementEventArgs(EPStatement _statement) + { + this._statement = _statement; + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/UnmatchedListener.cs b/NEsper.Core/NEsper.Core/client/UnmatchedListener.cs new file mode 100755 index 000000000..922dbd48e --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/UnmatchedListener.cs @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.client +{ + /// + /// Receives notification from an engine that an event that has been sent into the engine or that + /// has been generated via insert-into has not been matched to any statement, considering all started statement's + /// event stream filter criteria (not considering where-clause and having-clauses). + /// + /// + public delegate void UnmatchedListener(EventBean theEvent); + + public class UnmatchedEventArgs : System.EventArgs + { + public EventBean Event { get; private set; } + + public UnmatchedEventArgs(EventBean theEvent) + { + Event = theEvent; + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/client/UpdateEventArgs.cs b/NEsper.Core/NEsper.Core/client/UpdateEventArgs.cs new file mode 100755 index 000000000..41447fe4e --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/UpdateEventArgs.cs @@ -0,0 +1,58 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client +{ + [Serializable] + public sealed class UpdateEventArgs : EventArgs + { + public static readonly UpdateEventArgs EmptyArgs = new UpdateEventArgs(null, null, null, null); + + [NonSerialized] + private EPStatement _statement; + [NonSerialized] + private EPServiceProvider _serviceProvider; + + public EPServiceProvider ServiceProvider + { + get { return _serviceProvider; } + private set { _serviceProvider = value; } + } + + public EPStatement Statement + { + get { return _statement; } + private set { _statement = value; } + } + + public EventBean[] NewEvents { get; private set; } + public EventBean[] OldEvents { get; private set; } + + public UpdateEventArgs(EPServiceProvider serviceProvider, + EPStatement statement, + EventBean[] newEvents, + EventBean[] oldEvents) + { + ServiceProvider = serviceProvider; + Statement = statement; + NewEvents = newEvents; + OldEvents = oldEvents; + } + } + + + /// + /// Defines a delegate that is notified of new and old events. + /// + /// The event sender + /// The Update event arguments + + public delegate void UpdateEventHandler(Object sender, UpdateEventArgs e); +} diff --git a/NEsper.Core/NEsper.Core/client/UpdateListener.cs b/NEsper.Core/NEsper.Core/client/UpdateListener.cs new file mode 100755 index 000000000..748dd9b4a --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/UpdateListener.cs @@ -0,0 +1,43 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client +{ + /// + /// Defines an interface to notify of new and old events. + /// + /// Also see for Update listeners that require + /// the statement and service provider instance to be passed to the listener in addition + /// to events. + /// + /// + [Obsolete] + public interface UpdateListener + { + /// + /// Notify that new events are available or old events are removed. + /// If the call to Update contains new (inserted) events, then the first argument will be a non-empty list and + /// the second will be empty. Similarly, if the call is a notification of deleted events, then the first argument + /// will be empty and the second will be non-empty. + /// + /// Either the newEvents or oldEvents will be non-null. This method won't be called with both arguments being null, + /// (unless using output rate limiting or force-output options), + /// but either one could be null. The same is true for zero-length arrays. + /// + /// + /// Either newEvents or oldEvents will be non-empty. If both are non-empty, then the Update is a modification + /// notification. + /// + /// + /// is any new events. This will be null or empty if the Update is for old events only. + /// is any old events. This will be null or empty if the Update is for new events only. + void Update(EventBean[] newEvents, EventBean[] oldEvents); + } +} diff --git a/NEsper.Core/NEsper.Core/client/VariableConstantValueException.cs b/NEsper.Core/NEsper.Core/client/VariableConstantValueException.cs new file mode 100755 index 000000000..e76f2847e --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/VariableConstantValueException.cs @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client +{ + /// Indicates that a variable cannot be set. + public class VariableConstantValueException : EPException + { + /// Ctor. + /// supplies exception details + public VariableConstantValueException(String message) + : base(message) + { + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/VariableNotFoundException.cs b/NEsper.Core/NEsper.Core/client/VariableNotFoundException.cs new file mode 100755 index 000000000..8ea488eb2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/VariableNotFoundException.cs @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client +{ + /// + /// Indicates that a variable was not found. + /// + [Serializable] + public class VariableNotFoundException : EPException + { + /// Ctor. + /// supplies exception details + public VariableNotFoundException(String message) + : base(message) + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/VariableValueException.cs b/NEsper.Core/NEsper.Core/client/VariableValueException.cs new file mode 100755 index 000000000..0fda36515 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/VariableValueException.cs @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client +{ + /// + /// Indicates that a variable value could not be assigned. + /// + [Serializable] + public class VariableValueException : EPException + { + /// Ctor. + /// supplies exception details + public VariableValueException(String message) + : base(message) + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/annotation/AppliesTo.cs b/NEsper.Core/NEsper.Core/client/annotation/AppliesTo.cs new file mode 100755 index 000000000..ab04c49f6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/annotation/AppliesTo.cs @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; + +namespace com.espertech.esper.client.annotation +{ + /// Annotation to target certain constructs. + public enum AppliesTo { + UNDEFINED, + UNIQUE, + GROUPBY, + INDEX, + OUTPUTLIMIT, + MATCHRECOGNIZE, + CONTEXT, + PRIOR, + RANK, + EVERYDISTINCT, + SORTEDWIN, + TIMEORDERWIN, + KEEPALLWIN, + PATTERN, + TIMEACCUMWIN, + TIMEBATCHWIN, + TIMELENGTHBATCHWIN, + GROUPWIN, + LENGTHWIN, + TIMEWIN, + LENGTHBATCHWIN, + PREV, + EXPRESSIONWIN, + EXPRESSIONBATCHWIN, + FOLLOWEDBY, + FIRSTLENGTHWIN, + EXTTIMEDWIN, + EXTTIMEDBATCHWIN + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/client/annotation/AuditAttribute.cs b/NEsper.Core/NEsper.Core/client/annotation/AuditAttribute.cs new file mode 100755 index 000000000..681d161b1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/annotation/AuditAttribute.cs @@ -0,0 +1,42 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.annotation +{ + /// + /// Annotation for use in EPL statements to add a debug. + /// + public class AuditAttribute : Attribute + { + /// + /// Comma-separated list of keywords (not case-sentitive), see for a list of keywords. + /// + /// The value. + /// comma-separated list of audit keywords + public String Value { get; set; } + + /// + /// Initializes a new instance of the class. + /// + /// The value. + public AuditAttribute(string value) + { + Value = value; + } + + /// + /// Initializes a new instance of the class. + /// + public AuditAttribute() + { + Value = "*"; + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/annotation/AuditEnum.cs b/NEsper.Core/NEsper.Core/client/annotation/AuditEnum.cs new file mode 100755 index 000000000..d63856bba --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/annotation/AuditEnum.cs @@ -0,0 +1,179 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; + +namespace com.espertech.esper.client.annotation +{ + /// + /// Enumeration of audit values. Since audits may be a comma-separate list in a single + /// @Audit annotation they are listed as enumeration values here. + /// + public enum AuditEnum + { + /// For use with property audit. + PROPERTY, + + /// For use with expression audit. + EXPRESSION, + + /// For use with expression audit. + EXPRESSION_NESTED, + + /// For use with expression-definition audit. + EXPRDEF, + + /// For use with view audit. + VIEW, + + /// For use with pattern audit. + PATTERN, + + /// For use with pattern audit. + PATTERNINSTANCES, + + /// For use with stream-audit. + STREAM, + + /// For use with stream-audit. + SCHEDULE, + + /// For use with insert-into audit. + INSERT, + + /// For use with data flow source operators. + DATAFLOW_SOURCE, + + /// + /// For use with data flow (non-source and source) operators. + /// + DATAFLOW_OP, + + /// + /// For use with data flows specifically for transitions. + /// + DATAFLOW_TRANSITION, + + /// + /// For use with insert-into audit. + /// + CONTEXTPARTITION + } ; + + public static class AuditEnumExtensions { + /// Returns the constant. + /// The enum value. + /// constant + public static string GetValue(this AuditEnum enumValue) + { + switch (enumValue) { + case AuditEnum.PROPERTY: + return "PROPERTY"; + case AuditEnum.EXPRESSION: + return "EXPRESSION"; + case AuditEnum.EXPRESSION_NESTED: + return "EXPRESSION-NESTED"; + case AuditEnum.EXPRDEF: + return "EXPRDEF"; + case AuditEnum.VIEW: + return "VIEW"; + case AuditEnum.PATTERN: + return "PATTERN"; + case AuditEnum.PATTERNINSTANCES: + return "PATTERN-INSTANCES"; + case AuditEnum.STREAM: + return "STREAM"; + case AuditEnum.SCHEDULE: + return "SCHEDULE"; + case AuditEnum.INSERT: + return "INSERT"; + case AuditEnum.DATAFLOW_SOURCE: + return "DATAFLOW-SOURCE"; + case AuditEnum.DATAFLOW_OP: + return "DATAFLOW-OP"; + case AuditEnum.DATAFLOW_TRANSITION: + return "DATAFLOW-TRANSITION"; + case AuditEnum.CONTEXTPARTITION: + return "CONTEXTPARTITION"; + default: + throw new ArgumentException("invalid value for enum value", "enumValue"); + } + } + + /// + /// Returns text used for the category of the audit log item. + /// + /// + /// + + public static string GetPrettyPrintText(this AuditEnum enumValue) + { + return GetValue(enumValue).ToLower(); + } + + /// + /// Check if the hint is present in the attributes provided. + /// + /// The enum value. + /// the attributes to inspect + /// indicator + public static AuditAttribute GetAudit(this AuditEnum enumValue, Attribute[] attributes) + { + if (attributes == null) + { + return null; + } + + foreach (Attribute attribute in attributes) + { + var auditAnnotation = attribute as AuditAttribute; + if (auditAnnotation == null) { + continue; + } + + var auditAnnoValue = auditAnnotation.Value; + if (auditAnnoValue == "*") { + return auditAnnotation; + } + + var isListed = IsListed(auditAnnoValue, GetValue(enumValue)); + if (isListed) { + return auditAnnotation; + } + } + + return null; + } + + private static bool IsListed(String list, String lookedForValue) + { + if (list == null) + { + return false; + } + + lookedForValue = lookedForValue.Trim().ToUpper(); + list = list.Trim().ToUpper(); + + if (list.ToUpper() == lookedForValue) + { + return true; + } + + return list.Split(',') + .Select(item => item.Trim().ToUpper()) + .Any(listItem => listItem == lookedForValue); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/client/annotation/AvroSchemaField.cs b/NEsper.Core/NEsper.Core/client/annotation/AvroSchemaField.cs new file mode 100755 index 000000000..f814903d0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/annotation/AvroSchemaField.cs @@ -0,0 +1,52 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; + +namespace com.espertech.esper.client.annotation +{ + /// + /// Attribute for use with Avro to provide a schema for a given event property. + /// + public class AvroSchemaFieldAttribute : Attribute + { + /// + /// Property name. + /// + /// name + public string Name { get; set; } + + /// + /// Schema text. + /// + /// schema text + public string Schema { get; set; } + + /// + /// Initializes a new instance of the class. + /// + public AvroSchemaFieldAttribute() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The name. + /// The schema. + public AvroSchemaFieldAttribute(string name = "", string schema = "") + { + Name = name; + Schema = schema; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/client/annotation/BuiltinAnnotation.cs b/NEsper.Core/NEsper.Core/client/annotation/BuiltinAnnotation.cs new file mode 100755 index 000000000..b0b834b2a --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/annotation/BuiltinAnnotation.cs @@ -0,0 +1,59 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.client.annotation +{ + /// List of built-in annotations. + /// + /// List of built-in annotations. + /// + public class BuiltinAnnotation + { + + /// + /// List of built-in annotations. + /// + public static readonly IDictionary BUILTIN = new Dictionary(); + + static BuiltinAnnotation() + { + foreach (Type clazz in new Type[] + { + typeof (AuditAttribute), + typeof (DescriptionAttribute), + typeof (DropAttribute), + typeof (DurableAttribute), + typeof (EventRepresentationAttribute), + typeof (ExternalDWAttribute), + typeof (ExternalDWKeyAttribute), + typeof (ExternalDWListenerAttribute), + typeof (ExternalDWQueryAttribute), + typeof (ExternalDWSettingAttribute), + typeof (ExternalDWValueAttribute), + typeof (HintAttribute), + typeof (HookAttribute), + typeof (IterableUnboundAttribute), + typeof (NameAttribute), + typeof (NoLockAttribute), + typeof (OverflowAttribute), + typeof (PriorityAttribute), + typeof (ResilientAttribute), + typeof (TagAttribute), + typeof (TransientAttribute) + }) + { + BUILTIN.Put(clazz.Name.ToLower(), clazz); + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/client/annotation/DescriptionAttribute.cs b/NEsper.Core/NEsper.Core/client/annotation/DescriptionAttribute.cs new file mode 100755 index 000000000..49329a2ec --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/annotation/DescriptionAttribute.cs @@ -0,0 +1,54 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.annotation +{ + /// + /// Annotation for use in EPL statements to add a description. + /// + + public class DescriptionAttribute : Attribute + { + /// + /// Returns the description text. + /// + /// + /// description text + /// + public string Value { get; set; } + + /// + /// Initializes a new instance of the class. + /// + /// The value. + public DescriptionAttribute(string value) + { + Value = value; + } + + /// + /// Initializes a new instance of the class. + /// + public DescriptionAttribute() + { + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override string ToString() + { + return string.Format("@Description(\"{0}\")", Value); + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/annotation/DropAttribute.cs b/NEsper.Core/NEsper.Core/client/annotation/DropAttribute.cs new file mode 100755 index 000000000..085b19afd --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/annotation/DropAttribute.cs @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.annotation +{ + /// + /// An execution directive for use in an EPL statement, that causes processing of an + /// event to stop after the EPL statement marked with @Drop has processed the event, + /// applicable only if multiple statements must process the same event. + /// + /// Ensure the engine configuration for prioritized execution is set before using + /// this annotation. + /// + public class DropAttribute : Attribute + { + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override string ToString() + { + return "@Drop()"; + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/annotation/DurableAttribute.cs b/NEsper.Core/NEsper.Core/client/annotation/DurableAttribute.cs new file mode 100755 index 000000000..d4ff65dd0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/annotation/DurableAttribute.cs @@ -0,0 +1,17 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.annotation +{ + /// Annotation marking a durable-profile EPL statement. + public class DurableAttribute : Attribute + { + } +} diff --git a/NEsper.Core/NEsper.Core/client/annotation/EventRepresentationAttribute.cs b/NEsper.Core/NEsper.Core/client/annotation/EventRepresentationAttribute.cs new file mode 100755 index 000000000..1550a35c3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/annotation/EventRepresentationAttribute.cs @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client.util; + +namespace com.espertech.esper.client.annotation +{ + /// + /// Annotation that can be attached to specify which underlying event representation to use for events. + /// + public class EventRepresentationAttribute : Attribute + { + /// + /// Define the event underlying type. + /// + /// + /// The event underlying type. + /// + public EventUnderlyingType Value { get; set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/annotation/ExternalDWAttribute.cs b/NEsper.Core/NEsper.Core/client/annotation/ExternalDWAttribute.cs new file mode 100755 index 000000000..9360a61f8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/annotation/ExternalDWAttribute.cs @@ -0,0 +1,59 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.annotation +{ + /// Annotation for defining an external data window name and open/close functon. + public class ExternalDWAttribute : Attribute + { + public ExternalDWAttribute() + { + FunctionOpen = string.Empty; + FunctionClose = string.Empty; + IsUnique = false; + } + + /// + /// Name + /// + /// The name. + /// name + public String Name { get; set; } + + /// + /// Open function. + /// + /// The function open. + /// open function. + public String FunctionOpen { get; set; } + + /// + /// Close function. + /// + /// The function close. + /// close function + public String FunctionClose { get; set; } + + /// + /// Indicator whether unique-key semantics should apply. + /// + /// This indicator is false by default meaning that the implementation should not + /// assume unique-data-window semantics, and would not need to post the previous value + /// of the key as a remove stream event. + /// + /// Setting this indicator is interpreted by an implementation to assume unique-data-window + /// semantics, thereby instructing to post the previous value for the currently-updated key + /// as a remove stream event. + /// + /// true if this instance is unique; otherwise, false. + /// unique-key semantics + public bool IsUnique { get; set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/annotation/ExternalDWKeyAttribute.cs b/NEsper.Core/NEsper.Core/client/annotation/ExternalDWKeyAttribute.cs new file mode 100755 index 000000000..05db6c4bd --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/annotation/ExternalDWKeyAttribute.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.annotation +{ + /// + /// Annotation for defining the name of the property or the function name returning the external data window key values. + /// + public class ExternalDWKeyAttribute : Attribute + { + public ExternalDWKeyAttribute() + { + Property = string.Empty; + PropertyNames = new string[0]; + Function = string.Empty; + } + + /// Property name acting as key. + /// key property name + public String Property { get; set; } + + /// Multiple property names acting as key (check for support in the documentation). + /// property names array + public String[] PropertyNames { get; set; } + + /// Value generator function. + /// function name + public String Function { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/annotation/ExternalDWListenerAttribute.cs b/NEsper.Core/NEsper.Core/client/annotation/ExternalDWListenerAttribute.cs new file mode 100755 index 000000000..71844b26b --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/annotation/ExternalDWListenerAttribute.cs @@ -0,0 +1,30 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.annotation +{ + /// Annotation for configuring external data window listeners. + public class ExternalDWListenerAttribute : Attribute + { + public ExternalDWListenerAttribute() + { + Threaded = true; + NumThreads = 1; + } + + /// Returns indicator whether a listener thread is required or not. + /// indicator + public bool Threaded { get; set; } + + /// Returns indicator the number of listener threads. + /// number of threads + public int NumThreads { get; set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/annotation/ExternalDWQueryAttribute.cs b/NEsper.Core/NEsper.Core/client/annotation/ExternalDWQueryAttribute.cs new file mode 100755 index 000000000..4075ac1ef --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/annotation/ExternalDWQueryAttribute.cs @@ -0,0 +1,33 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.annotation +{ + /// + /// Annotation for defining the name of the functions returning external data window key and value + /// objects for use with queries against external data windows. + /// + public class ExternalDWQueryAttribute : Attribute + { + public ExternalDWQueryAttribute() + { + FunctionKeys = string.Empty; + FunctionValues = string.Empty; + } + + /// Returns function name that return key objects. + /// function name + public String FunctionKeys { get; set; } + + /// Returns function name that return value objects. + /// function name + public String FunctionValues { get; set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/annotation/ExternalDWSettingAttribute.cs b/NEsper.Core/NEsper.Core/client/annotation/ExternalDWSettingAttribute.cs new file mode 100755 index 000000000..715293ed5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/annotation/ExternalDWSettingAttribute.cs @@ -0,0 +1,36 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.annotation +{ + /// Annotation for defining a external data window settings. + public class ExternalDWSettingAttribute : Attribute + { + public ExternalDWSettingAttribute() + { + IEnumerable = true; + FunctionLookupCompleted = string.Empty; + } + + /// + /// Indicator whether iterable or not. + /// + /// true if iterable; otherwise, false. + /// iterable flag + public bool IEnumerable { get; set; } + + /// + /// Function name to invoke when a lookup completed. + /// + /// The function lookup completed. + /// function name + public string FunctionLookupCompleted { get; set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/annotation/ExternalDWValueAttribute.cs b/NEsper.Core/NEsper.Core/client/annotation/ExternalDWValueAttribute.cs new file mode 100755 index 000000000..2b213cff9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/annotation/ExternalDWValueAttribute.cs @@ -0,0 +1,24 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.annotation +{ + /// Annotation for mapping of event-to-value and value-to-event for external data windows. + public class ExternalDWValueAttribute : Attribute + { + /// Returns the function name of the function that maps event beans to value objects. + /// event to value mapping function name + public String FunctionBeanToValue { get; set; } + + /// Returns the function name of the function that maps values to event objects. + /// value to event mapping function name + public String FunctionValueToBean { get; set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/annotation/HintAttribute.cs b/NEsper.Core/NEsper.Core/client/annotation/HintAttribute.cs new file mode 100755 index 000000000..934c55ee1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/annotation/HintAttribute.cs @@ -0,0 +1,58 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.annotation +{ + /// + /// Annotation for providing a statement execution hint. + /// + /// Hints are providing instructions that can change latency, throughput or memory + /// requirements of a statement. + /// + public class HintAttribute : Attribute + { + /// + /// Hint Keyword(s), comma-separated. + /// + /// + /// keywords + /// + public string Value { get; set; } + + public string Model { get; set; } + + public AppliesTo Applies { get; set; } + + /// + /// Initializes a new instance of the class. + /// + /// The value. + /// The model. + /// What this hint applies to. + public HintAttribute(string value = "", string model = "", AppliesTo applies = AppliesTo.UNDEFINED) + { + Value = value; + Model = model; + Applies = applies; + } + + /// + /// Initializes a new instance of the class. + /// + public HintAttribute() + { + } + + public override string ToString() + { + return string.Format("@Hint(\"{0}\")", Value); + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/annotation/HintEnum.cs b/NEsper.Core/NEsper.Core/client/annotation/HintEnum.cs new file mode 100755 index 000000000..9ab87b3f6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/annotation/HintEnum.cs @@ -0,0 +1,618 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.annotation; + +namespace com.espertech.esper.client.annotation +{ + /// + /// Enumeration of hint values. Since hints may be a comma-separate list in a single + /// @Hint annotation they are listed as enumeration values here. + /// + public enum HintEnum + { + /// + /// For use with match_recognize, iterate-only matching. + /// + ITERATE_ONLY, + + /// + /// For use with group-by, disabled reclaim groups. + /// + DISABLE_RECLAIM_GROUP, + + /// + /// For use with group-by and std:groupwin, reclaim groups for unbound streams based on time. + /// The number of seconds after which a groups is reclaimed if inactive. + /// + RECLAIM_GROUP_AGED, + + /// + /// For use with group-by and std:groupwin, reclaim groups for unbound streams based on time, + /// this number is the frequency in seconds at which a sweep occurs for aged groups, if not + /// provided then the sweep frequency is the same number as the age. + /// + RECLAIM_GROUP_FREQ, + + /// + /// For use with create-named-window statements only, to indicate that statements that subquery + /// the named window use named window data structures (unless the subquery statement specifies + /// below DISBABLE hint and as listed below). + /// + /// By default and if this hint is not specified or subqueries specify a stream filter on a + /// named window, subqueries use statement-local data structures representing named window + /// contents (table, index). Such data structure is maintained by consuming the named window + /// insert and remove stream. + /// + /// + ENABLE_WINDOW_SUBQUERY_INDEXSHARE, + + /// + /// If ENABLE_WINDOW_SUBQUERY_INDEXSHARE is not specified for a named window (the default) + /// then this instruction is ignored. + /// + /// For use with statements that subquery a named window and that benefit from a statement-local + /// data structure representing named window contents (table, index), maintained through consuming + /// the named window insert and remove stream. + /// + /// + DISABLE_WINDOW_SUBQUERY_INDEXSHARE, + + /// + /// For use with subqueries and on-select, on-merge, on-Update and on-delete to specify the + /// query engine neither build an implicit index nor use an existing index, always performing + /// a full table scan. + /// + SET_NOINDEX, + + /// + /// For use with join query plans to force a nested iteration plan. + /// + FORCE_NESTED_ITER, + + /// + /// For use with join query plans to indicate preferance of the merge-join query plan. + /// + PREFER_MERGE_JOIN, + + /// + /// For use everywhere where indexes are used (subquery, joins, fire-and-forget, on-select etc.), index hint. + /// + INDEX, + + /// + /// For use where query planning applies. + /// + EXCLUDE_PLAN, + + /// + /// For use everywhere where unique data window are used + /// + DISABLE_UNIQUE_IMPLICIT_IDX, + + /// + /// For use when filter expression optimization may widen the filter expression + /// + MAX_FILTER_WIDTH, + + /// + /// For use everywhere where unique data window are used + /// + DISABLE_WHEREEXPR_MOVETO_FILTER, + + /// + /// For use with output rate limiting to enable certain optimization that may however change output. + /// + ENABLE_OUTPUTLIMIT_OPT + } + + public static class HintEnumExtensions + { + /// Returns the constant. + /// constant + public static String GetValue(this HintEnum @enum) + { + switch (@enum) + { + case HintEnum.ITERATE_ONLY: + return "ITERATE_ONLY"; + case HintEnum.DISABLE_RECLAIM_GROUP: + return "DISABLE_RECLAIM_GROUP"; + case HintEnum.RECLAIM_GROUP_AGED: + return "RECLAIM_GROUP_AGED"; + case HintEnum.RECLAIM_GROUP_FREQ: + return "RECLAIM_GROUP_FREQ"; + case HintEnum.ENABLE_WINDOW_SUBQUERY_INDEXSHARE: + return "ENABLE_WINDOW_SUBQUERY_INDEXSHARE"; + case HintEnum.DISABLE_WINDOW_SUBQUERY_INDEXSHARE: + return "DISABLE_WINDOW_SUBQUERY_INDEXSHARE"; + case HintEnum.SET_NOINDEX: + return "SET_NOINDEX"; + case HintEnum.FORCE_NESTED_ITER: + return "FORCE_NESTED_ITER"; + case HintEnum.PREFER_MERGE_JOIN: + return "PREFER_MERGE_JOIN"; + case HintEnum.INDEX: + return "INDEX"; + case HintEnum.EXCLUDE_PLAN: + return "EXCLUDE_PLAN"; + case HintEnum.DISABLE_UNIQUE_IMPLICIT_IDX: + return "DISABLE_UNIQUE_IMPLICIT_IDX"; + case HintEnum.MAX_FILTER_WIDTH: + return "MAX_FILTER_WIDTH"; + case HintEnum.DISABLE_WHEREEXPR_MOVETO_FILTER: + return "DISABLE_WHEREEXPR_MOVETO_FILTER"; + case HintEnum.ENABLE_OUTPUTLIMIT_OPT: + return "ENABLE_OUTPUTLIMIT_OPT"; + } + + throw new ArgumentException(); + } + + /// True if the hint accepts params. + /// indicator + public static bool IsAcceptsParameters(this HintEnum @enum) + { + switch (@enum) + { + case HintEnum.ITERATE_ONLY: + return false; + case HintEnum.DISABLE_RECLAIM_GROUP: + return false; + case HintEnum.RECLAIM_GROUP_AGED: + return true; + case HintEnum.RECLAIM_GROUP_FREQ: + return true; + case HintEnum.ENABLE_WINDOW_SUBQUERY_INDEXSHARE: + return false; + case HintEnum.DISABLE_WINDOW_SUBQUERY_INDEXSHARE: + return false; + case HintEnum.SET_NOINDEX: + return false; + case HintEnum.FORCE_NESTED_ITER: + return false; + case HintEnum.PREFER_MERGE_JOIN: + return false; + case HintEnum.INDEX: + return false; + case HintEnum.EXCLUDE_PLAN: + return false; + case HintEnum.DISABLE_UNIQUE_IMPLICIT_IDX: + return false; + case HintEnum.MAX_FILTER_WIDTH: + return true; + case HintEnum.DISABLE_WHEREEXPR_MOVETO_FILTER: + return false; + case HintEnum.ENABLE_OUTPUTLIMIT_OPT: + return false; + } + + throw new ArgumentException(); + } + + /// True if the hint requires params. + /// indicator + public static bool IsRequiresParameters(this HintEnum @enum) + { + if (IsAcceptsParameters(@enum)) + return true; + + switch (@enum) + { + case HintEnum.ITERATE_ONLY: + return false; + case HintEnum.DISABLE_RECLAIM_GROUP: + return false; + case HintEnum.RECLAIM_GROUP_AGED: + return true; + case HintEnum.RECLAIM_GROUP_FREQ: + return true; + case HintEnum.ENABLE_WINDOW_SUBQUERY_INDEXSHARE: + return false; + case HintEnum.DISABLE_WINDOW_SUBQUERY_INDEXSHARE: + return false; + case HintEnum.SET_NOINDEX: + return false; + case HintEnum.FORCE_NESTED_ITER: + return false; + case HintEnum.PREFER_MERGE_JOIN: + return false; + case HintEnum.INDEX: + return false; + case HintEnum.EXCLUDE_PLAN: + return false; + case HintEnum.DISABLE_UNIQUE_IMPLICIT_IDX: + return false; + case HintEnum.MAX_FILTER_WIDTH: + return true; + case HintEnum.DISABLE_WHEREEXPR_MOVETO_FILTER: + return false; + case HintEnum.ENABLE_OUTPUTLIMIT_OPT: + return false; + } + + throw new ArgumentException(); + } + + public static bool IsRequiresParentheses(this HintEnum @enum) + { + switch (@enum) + { + case HintEnum.ITERATE_ONLY: + return false; + case HintEnum.DISABLE_RECLAIM_GROUP: + return false; + case HintEnum.RECLAIM_GROUP_AGED: + return false; + case HintEnum.RECLAIM_GROUP_FREQ: + return false; + case HintEnum.ENABLE_WINDOW_SUBQUERY_INDEXSHARE: + return false; + case HintEnum.DISABLE_WINDOW_SUBQUERY_INDEXSHARE: + return false; + case HintEnum.SET_NOINDEX: + return false; + case HintEnum.FORCE_NESTED_ITER: + return false; + case HintEnum.PREFER_MERGE_JOIN: + return false; + case HintEnum.INDEX: + return true; + case HintEnum.EXCLUDE_PLAN: + return true; + case HintEnum.DISABLE_UNIQUE_IMPLICIT_IDX: + return false; + case HintEnum.MAX_FILTER_WIDTH: + return false; + case HintEnum.DISABLE_WHEREEXPR_MOVETO_FILTER: + return false; + case HintEnum.ENABLE_OUTPUTLIMIT_OPT: + return false; + } + + throw new ArgumentException(); + } + + /// + /// Check if the hint is present in the attributes provided. + /// + /// The @enum. + /// the attributes to inspect + /// indicator + public static HintAttribute GetHint(this HintEnum @enum, IEnumerable attributes) + { + if (attributes == null) + { + return null; + } + + foreach (HintAttribute hintAnnotation in attributes.OfType()) + { + try + { + var setOfHints = ValidateGetListed(hintAnnotation); + if (setOfHints.ContainsKey(@enum)) + { + return hintAnnotation; + } + } + catch (AttributeException e) + { + throw new EPException("Invalid hint: " + e.Message, e); + } + } + return null; + } + + /// Validate a hint attribute ensuring it contains only recognized hints. + /// to validate + /// AnnotationException if an invalid text was found + public static IDictionary> ValidateGetListed(Attribute attribute) + { + if (!(attribute is HintAttribute)) + { + return new EmptyDictionary>(); + } + + var hint = (HintAttribute)attribute; + var hintValueCaseNeutral = hint.Value.Trim(); + var hintValueUppercase = hintValueCaseNeutral.ToUpper(); + + foreach (HintEnum val in EnumHelper.GetValues()) + { + if ((val.GetValue() == hintValueUppercase) && !val.IsRequiresParentheses()) + { + ValidateParameters(val, hint.Value.Trim()); + IList parameters; + if (val.IsAcceptsParameters()) + { + var assignment = GetAssignedValue(hint.Value.Trim(), val.GetValue()); + if (assignment == null) + { + parameters = Collections.GetEmptyList(); + } + else + { + parameters = assignment.AsSingleton(); + } + } + else + { + parameters = Collections.GetEmptyList(); + } + return Collections.SingletonMap(val, parameters); + } + } + + var hints = SplitCommaUnlessInParen(hint.Value); + var listed = new Dictionary>(); + for (int i = 0; i < hints.Length; i++) + { + var hintValUppercase = hints[i].Trim().ToUpper(); + var hintValNeutralcase = hints[i].Trim(); + + HintEnum? found = null; + String parameter = null; + + foreach (HintEnum val in EnumHelper.GetValues()) + { + if ((val.GetValue() == hintValUppercase) && !val.IsRequiresParentheses()) + { + found = val; + parameter = GetAssignedValue(hint.Value.Trim(), val.GetValue()); + break; + } + + if (val.IsRequiresParentheses()) + { + int indexOpen = hintValUppercase.IndexOf('('); + int indexClosed = hintValUppercase.LastIndexOf(')'); + if (indexOpen != -1) + { + var hintNameNoParen = hintValUppercase.Substring(0, indexOpen); + if (val.GetValue() == hintNameNoParen) + { + if (indexClosed == -1 || indexClosed < indexOpen) + { + throw new AttributeException("Hint '" + val + "' mismatches parentheses"); + } + if (indexClosed != hintValUppercase.Length - 1) + { + throw new AttributeException( + "Hint '" + val + "' has additional text after parentheses"); + } + found = val; + parameter = hintValNeutralcase.Substring(indexOpen + 1, indexClosed - indexOpen - 1); + break; + } + } + if ((hintValUppercase == val.GetValue()) && indexOpen == -1) + { + throw new AttributeException( + "Hint '" + val + "' requires additional parameters in parentheses"); + } + } + + if (hintValUppercase.IndexOf('=') != -1) + { + var hintName = hintValUppercase.Substring(0, hintValUppercase.IndexOf('=')); + if (val.GetValue() == hintName.Trim().ToUpper()) + { + found = val; + parameter = GetAssignedValue(hint.Value.Trim(), val.GetValue()); + break; + } + } + } + + if (found == null) + { + var hintName = hints[i].Trim(); + if (hintName.IndexOf('=') != -1) + { + hintName = hintName.Substring(0, hintName.IndexOf('=')); + } + throw new AttributeException( + "Hint annotation value '" + hintName.Trim() + "' is not one of the known values"); + } + else + { + if (!found.Value.IsRequiresParentheses()) + { + ValidateParameters(found.Value, hintValUppercase); + } + var existing = listed.Get(found.Value); + if (existing == null) + { + existing = new List(); + listed.Put(found.Value, existing); + } + if (parameter != null) + { + existing.Add(parameter); + } + } + } + return listed; + } + + private static void ValidateParameters(HintEnum val, String hintVal) + { + if (IsRequiresParameters(val)) + { + if (hintVal.IndexOf('=') == -1) + { + throw new AttributeException("Hint '" + val + "' requires a parameter value"); + } + } + if (!IsAcceptsParameters(val)) + { + if (hintVal.IndexOf('=') != -1) + { + throw new AttributeException("Hint '" + val + "' does not accept a parameter value"); + } + } + } + + /// + /// Returns hint value. + /// + /// The hint enum. + /// The annotation to look for. + /// hint assigned first value provided + public static string GetHintAssignedValue(this HintEnum hintEnum, HintAttribute annotation) + { + try + { + var hintValues = ValidateGetListed(annotation); + if (hintValues == null || !hintValues.ContainsKey(hintEnum)) + { + return null; + } + return hintValues.Get(hintEnum)[0]; + } + catch (AttributeException ex) + { + throw new EPException("Failed to interpret hint annotation: " + ex.Message, ex); + } + } + + + /// + /// Returns all values assigned for a given hint, if any + /// + /// The hint enum. + /// the to be interogated + /// + /// hint assigned values or null if none found + /// + public static IList GetHintAssignedValues(this HintEnum hintEnum, Attribute[] annotations) + { + IList allHints = null; + try + { + foreach (Attribute annotation in annotations) + { + var hintValues = ValidateGetListed(annotation); + if (hintValues == null || !hintValues.ContainsKey(hintEnum)) + { + continue; + } + if (allHints == null) + { + allHints = hintValues.Get(hintEnum); + } + else + { + allHints.AddAll(hintValues.Get(hintEnum)); + } + } + } + catch (AttributeException ex) + { + throw new EPException("Failed to interpret hint annotation: " + ex.Message, ex); + } + return allHints; + } + + private static String GetAssignedValue(String value, String enumValue) + { + String valMixed = value.Trim(); + String val = valMixed.ToUpper(); + + if (!val.Contains(",")) + { + if (val.IndexOf('=') == -1) + { + return null; + } + + String hintName = val.Substring(0, val.IndexOf('=')); + if (hintName != enumValue) + { + return null; + } + return valMixed.Substring(val.IndexOf('=') + 1); + } + + String[] hints = valMixed.Split(','); + foreach (var hint in hints) + { + int indexOfEquals = hint.IndexOf('='); + if (indexOfEquals == -1) + { + continue; + } + + val = hint.Substring(0, indexOfEquals).Trim().ToUpper(); + if (val != enumValue) + { + continue; + } + + var strValue = hint.Substring(indexOfEquals + 1).Trim(); + if (strValue.Length == 0) + { + return null; + } + + return strValue; + } + return null; + } + + /// + /// Split a line of comma-separated values allowing parenthesis. + /// + /// The line to split. + /// + public static String[] SplitCommaUnlessInParen(string line) + { + var nestingLevelParen = 0; + var lastComma = -1; + var parts = new List(); + for (int i = 0; i < line.Length; i++) + { + char c = line[i]; + if (c == '(') + { + nestingLevelParen++; + } + if (c == ')') + { + if (nestingLevelParen == 0) + { + throw new EPException("Close parenthesis ')' found but none open"); + } + nestingLevelParen--; + } + if (c == ',' && nestingLevelParen == 0) + { + var part = line.Substring(lastComma + 1, i - lastComma - 1); + if (!string.IsNullOrWhiteSpace(part)) + { + parts.Add(part); + } + lastComma = i; + } + } + var lastPart = line.Substring(lastComma + 1); + if (!string.IsNullOrWhiteSpace(lastPart)) + { + parts.Add(lastPart); + } + + return parts.ToArray(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/annotation/HookAttribute.cs b/NEsper.Core/NEsper.Core/client/annotation/HookAttribute.cs new file mode 100755 index 000000000..fec6932ad --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/annotation/HookAttribute.cs @@ -0,0 +1,24 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; + +namespace com.espertech.esper.client.annotation +{ + public class HookAttribute : Attribute + { + /// Returns the simple class name (using imports) or fully-qualified class name of the hook. + /// class name + public String Hook { get; set; } + + /// Returns hook type. + /// hook type + public HookType Type { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/annotation/HookType.cs b/NEsper.Core/NEsper.Core/client/annotation/HookType.cs new file mode 100755 index 000000000..8cdde2df5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/annotation/HookType.cs @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.client.annotation +{ + /// + /// Enumeration for the different types of statement-processing hooks (callbacks) that can be provided for a statement. + /// + public enum HookType + { + /// For use when installing a callback for converting SQL input parameters or column output values. + SQLCOL, + + /// For use when installing a callback for converting SQL row results to an object. + SQLROW, + + /// For internal use, query planning reporting. + INTERNAL_QUERY_PLAN, + + /// For internal use, group rollup plan reporting. + INTERNAL_GROUPROLLUP_PLAN, + + /// For internal use, aggregation level reporting. + INTERNAL_AGGLOCALLEVEL, + + /// For internal use, context state cache. + CONTEXT_STATE_CACHE + } +} diff --git a/NEsper.Core/NEsper.Core/client/annotation/IterableUnboundAttribute.cs b/NEsper.Core/NEsper.Core/client/annotation/IterableUnboundAttribute.cs new file mode 100755 index 000000000..f78be3f33 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/annotation/IterableUnboundAttribute.cs @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.annotation +{ + /// + /// Annotation for use in EPL statements for making an unbound stream iterable returning + /// the last event without requiring a data window to be declared. + /// + public class IterableUnboundAttribute : Attribute + { + } +} diff --git a/NEsper.Core/NEsper.Core/client/annotation/NameAttribute.cs b/NEsper.Core/NEsper.Core/client/annotation/NameAttribute.cs new file mode 100755 index 000000000..a240121a2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/annotation/NameAttribute.cs @@ -0,0 +1,47 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.annotation +{ + /// + /// Annotation for use in EPL statement to define a statement name. + /// + public class NameAttribute : Attribute + { + /// + /// Returns the statement name. + /// + /// + /// statement name + /// + public string Value { get; set; } + + /// + /// Initializes a new instance of the class. + /// + /// The value. + public NameAttribute(string value) + { + Value = value; + } + + /// + /// Initializes a new instance of the class. + /// + public NameAttribute() + { + } + + public override string ToString() + { + return string.Format("@Name(\"{0}\")", Value); + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/annotation/NoLockAttribute.cs b/NEsper.Core/NEsper.Core/client/annotation/NoLockAttribute.cs new file mode 100755 index 000000000..55a195c0e --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/annotation/NoLockAttribute.cs @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.annotation +{ + /// + /// Annotation for use in EPL statements to suppress any statement-level locking + /// (use with caution, see below). + /// + /// Caution: We provide this annotation for the purpose of identifing locking overhead, + /// or when your application is single-threaded, or when using an external mechanism + /// for concurreny control or for example with virtual data windows or plug-in data + /// windows to allow customizing concurrency for application-provided data windows. + /// Using this annotation may have unpredictable results unless your application is + /// taking concurrency under consideration. + /// + public class NoLockAttribute : Attribute + { + } +} diff --git a/NEsper.Core/NEsper.Core/client/annotation/OverflowAttribute.cs b/NEsper.Core/NEsper.Core/client/annotation/OverflowAttribute.cs new file mode 100755 index 000000000..5495d22a3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/annotation/OverflowAttribute.cs @@ -0,0 +1,17 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.annotation +{ + /// Annotation marking a overflow-profile EPL statement. + public class OverflowAttribute : Attribute + { + } +} diff --git a/NEsper.Core/NEsper.Core/client/annotation/PriorityAttribute.cs b/NEsper.Core/NEsper.Core/client/annotation/PriorityAttribute.cs new file mode 100755 index 000000000..01d19ff05 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/annotation/PriorityAttribute.cs @@ -0,0 +1,60 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.annotation +{ + /// + /// An execution directive for use in an EPL statement, by which processing of an + /// event by statements start with the statement that has the highest priority, + /// applicable only if multiple statements must process the same event. + /// + /// Ensure the engine configuration for prioritized execution is set before using + /// this annotation. + /// + /// The default priority value is zero (0). + /// + public class PriorityAttribute : Attribute + { + /// + /// Priority value. + /// + /// + /// value + /// + public int Value { get; set; } + + /// + /// Initializes a new instance of the class. + /// + public PriorityAttribute() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The value. + public PriorityAttribute(int value) + { + Value = value; + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override string ToString() + { + return string.Format("@Priority({0})", Value); + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/annotation/PropertyNameAttribute.cs b/NEsper.Core/NEsper.Core/client/annotation/PropertyNameAttribute.cs new file mode 100755 index 000000000..810d54710 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/annotation/PropertyNameAttribute.cs @@ -0,0 +1,49 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; + +namespace com.espertech.esper.client.annotation +{ + public class PropertyNameAttribute : Attribute + { + /// + /// Gets or sets the name. + /// + /// The name. + public string Name { get; set; } + + /// + /// Initializes a new instance of the class. + /// + /// The name. + public PropertyNameAttribute(string name) + { + Name = name; + } + + /// + /// Initializes a new instance of the class. + /// + public PropertyNameAttribute() + { + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override string ToString() + { + return string.Format("@PropertyName(\"{0}\")", Name); + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/annotation/RequiredAttribute.cs b/NEsper.Core/NEsper.Core/client/annotation/RequiredAttribute.cs new file mode 100755 index 000000000..c4b6e7f51 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/annotation/RequiredAttribute.cs @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; + +namespace com.espertech.esper.client.annotation +{ + /// + /// Indicates that the specified property is required. + /// + [AttributeUsage(AttributeTargets.Property)] + public class RequiredAttribute : Attribute + { + public override string ToString() + { + return "@Required()"; + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/annotation/ResilientAttribute.cs b/NEsper.Core/NEsper.Core/client/annotation/ResilientAttribute.cs new file mode 100755 index 000000000..a46f84007 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/annotation/ResilientAttribute.cs @@ -0,0 +1,17 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.annotation +{ + /// Annotation marking a resilient-profile EPL statement. + public class ResilientAttribute : Attribute + { + } +} diff --git a/NEsper.Core/NEsper.Core/client/annotation/SQLTimeoutAttribute.cs b/NEsper.Core/NEsper.Core/client/annotation/SQLTimeoutAttribute.cs new file mode 100755 index 000000000..1f4bf43fe --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/annotation/SQLTimeoutAttribute.cs @@ -0,0 +1,43 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; + +namespace com.espertech.esper.client.annotation +{ + public class SQLTimeoutAttribute : Attribute + { + /// + /// Gets or sets the value. + /// + /// The value. + public int Value { get; set; } + + /// + /// Initializes a new instance of the class. + /// + /// The value. + public SQLTimeoutAttribute(int value) + { + Value = value; + } + + /// + /// Initializes a new instance of the class. + /// + public SQLTimeoutAttribute() + { + } + + public override string ToString() + { + return string.Format("@SQLQueryTimeout(\"{0}\")", Value); + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/annotation/TagAttribute.cs b/NEsper.Core/NEsper.Core/client/annotation/TagAttribute.cs new file mode 100755 index 000000000..40fd2e54e --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/annotation/TagAttribute.cs @@ -0,0 +1,63 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.annotation +{ + /// + /// Annotation for use in EPL statement to tag a statement with a name-value pair. + /// + public class TagAttribute : Attribute + { + /// + /// Returns the tag name. + /// + /// + /// tag name. + /// + public string Name { get; set; } + + /// + /// Returns the tag value. + /// + /// + /// tag value. + /// + public string Value { get; set; } + + /// + /// Initializes a new instance of the class. + /// + /// The name. + /// The value. + public TagAttribute(string name, string value) + { + Name = name; + Value = value; + } + + /// + /// Initializes a new instance of the class. + /// + public TagAttribute() + { + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override string ToString() + { + return string.Format("@Tag(Name=\"{0}\", Value=\"{1}\")", Name, Value); + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/annotation/TransientAttribute.cs b/NEsper.Core/NEsper.Core/client/annotation/TransientAttribute.cs new file mode 100755 index 000000000..ac28cbecf --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/annotation/TransientAttribute.cs @@ -0,0 +1,17 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.annotation +{ + /// Annotation marking a transient-profile EPL statement. + public class TransientAttribute : Attribute + { + } +} diff --git a/NEsper.Core/NEsper.Core/client/context/ContextPartitionCollection.cs b/NEsper.Core/NEsper.Core/client/context/ContextPartitionCollection.cs new file mode 100755 index 000000000..55bc4a353 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/context/ContextPartitionCollection.cs @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +namespace com.espertech.esper.client.context +{ + /// + /// A collection of context partitions each uniquely identified by a context partition id (agent instance id). + /// + public class ContextPartitionCollection + { + /// Ctor. + /// per agent instance id + public ContextPartitionCollection(IDictionary descriptors) + { + Descriptors = descriptors; + } + + /// Returns the descriptors per agent instance id + /// descriptors + public IDictionary Descriptors { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/context/ContextPartitionDescriptor.cs b/NEsper.Core/NEsper.Core/client/context/ContextPartitionDescriptor.cs new file mode 100755 index 000000000..d20690115 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/context/ContextPartitionDescriptor.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.client.context +{ + /// Descriptor encapsulates information about a context partition. + public class ContextPartitionDescriptor + { + /// Ctor. + /// context partition id + /// identifier object specific to context declaration + /// current state + public ContextPartitionDescriptor(int agentInstanceId, ContextPartitionIdentifier identifier, ContextPartitionState state) + { + AgentInstanceId = agentInstanceId; + Identifier = identifier; + State = state; + } + + /// Returns the context partition id. + /// id + public int AgentInstanceId { get; private set; } + + /// Returns an identifier object that identifies the context partition. + /// identifier + public ContextPartitionIdentifier Identifier { get; private set; } + + /// Returns context partition state. + /// state + public ContextPartitionState State { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/context/ContextPartitionIdentifier.cs b/NEsper.Core/NEsper.Core/client/context/ContextPartitionIdentifier.cs new file mode 100755 index 000000000..977426d6b --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/context/ContextPartitionIdentifier.cs @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.context +{ + /// + /// Context partition identifiers are provided by the API when interrogating context partitions for a given statement. + /// + [Serializable] + public abstract class ContextPartitionIdentifier + { + /// Compare identifiers returning a bool indicator whether identifier information matches. + /// to compare to + /// true for objects identifying the same context partition (could be different context) + public abstract bool CompareTo(ContextPartitionIdentifier identifier); + + /// Returns the context partition id. + /// context partition id + public int? ContextPartitionId { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/context/ContextPartitionIdentifierCategory.cs b/NEsper.Core/NEsper.Core/client/context/ContextPartitionIdentifierCategory.cs new file mode 100755 index 000000000..2b98bc4d3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/context/ContextPartitionIdentifierCategory.cs @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.context +{ + /// + /// Context partition identifier for category context. + /// + [Serializable] + public class ContextPartitionIdentifierCategory : ContextPartitionIdentifier + { + /// Ctor. + public ContextPartitionIdentifierCategory() + { + } + + /// Ctor. + /// of category + public ContextPartitionIdentifierCategory(String label) + { + Label = label; + } + + /// Returns the category label. + /// label + public string Label { get; set; } + + public override bool CompareTo(ContextPartitionIdentifier other) + { + if (!(other is ContextPartitionIdentifierCategory)) + { + return false; + } + return Label.Equals(((ContextPartitionIdentifierCategory) other).Label); + } + + public override String ToString() + { + return "ContextPartitionIdentifierCategory{" + + "label='" + Label + '\'' + + '}'; + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/context/ContextPartitionIdentifierHash.cs b/NEsper.Core/NEsper.Core/client/context/ContextPartitionIdentifierHash.cs new file mode 100755 index 000000000..be17be123 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/context/ContextPartitionIdentifierHash.cs @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.context +{ + /// Context partition identifier for hash context. + [Serializable] + public class ContextPartitionIdentifierHash : ContextPartitionIdentifier + { + private int _hash; + + /// Ctor. + public ContextPartitionIdentifierHash() + { + } + + /// Ctor. + /// code + public ContextPartitionIdentifierHash(int hash) + { + _hash = hash; + } + + /// Returns the hash code. + /// hash code + public int Hash + { + get { return _hash; } + set { _hash = value; } + } + + public override bool CompareTo(ContextPartitionIdentifier other) + { + return other is ContextPartitionIdentifierHash && _hash == ((ContextPartitionIdentifierHash) other)._hash; + } + + public override String ToString() + { + return "ContextPartitionIdentifierHash{" + + "hash=" + _hash + + '}'; + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/context/ContextPartitionIdentifierInitiatedTerminated.cs b/NEsper.Core/NEsper.Core/client/context/ContextPartitionIdentifierInitiatedTerminated.cs new file mode 100755 index 000000000..fa3b58721 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/context/ContextPartitionIdentifierInitiatedTerminated.cs @@ -0,0 +1,70 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.core.context.mgr; + +namespace com.espertech.esper.client.context +{ + /// Context partition identifier for overlapping and non-overlapping contexts. + public class ContextPartitionIdentifierInitiatedTerminated : ContextPartitionIdentifier + { + /// + /// Initializes a new instance of the class. + /// + public ContextPartitionIdentifierInitiatedTerminated() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The properties. + /// The start time. + /// The end time. + public ContextPartitionIdentifierInitiatedTerminated(IDictionary properties, long startTime, long? endTime) + { + Properties = properties; + StartTime = startTime; + EndTime = endTime; + } + + /// Event or pattern information. + /// starting or initiating information + public IDictionary Properties { get; set; } + + /// Returns the start time of the context partition. + /// start time + public long StartTime { get; set; } + + /// Returns the end time of the context partition, if it can be computed + /// end time + public long? EndTime { get; set; } + + public override bool CompareTo(ContextPartitionIdentifier other) + { + if (!(other is ContextPartitionIdentifierInitiatedTerminated)) + { + return false; + } + var ito = (ContextPartitionIdentifierInitiatedTerminated) other; + return ContextControllerInitTerm.Compare( + StartTime, Properties, EndTime, ito.StartTime, ito.Properties, ito.EndTime); + } + + public override string ToString() + { + return "ContextPartitionIdentifierInitiatedTerminated{" + + "properties=" + Properties + + ", startTime=" + StartTime + + ", endTime=" + EndTime + + '}'; + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/context/ContextPartitionIdentifierNested.cs b/NEsper.Core/NEsper.Core/client/context/ContextPartitionIdentifierNested.cs new file mode 100755 index 000000000..9deffd079 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/context/ContextPartitionIdentifierNested.cs @@ -0,0 +1,61 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.context +{ + /// + /// Context partition identifier for nested contexts. + /// + [Serializable] + public class ContextPartitionIdentifierNested : ContextPartitionIdentifier + { + private ContextPartitionIdentifier[] _identifiers; + + /// Ctor. + public ContextPartitionIdentifierNested() { + } + + /// Ctor. + /// nested identifiers, count should match nesting level of context + public ContextPartitionIdentifierNested(ContextPartitionIdentifier[] identifiers) { + _identifiers = identifiers; + } + + /// Returns nested partition identifiers. + /// identifiers + public ContextPartitionIdentifier[] Identifiers + { + get { return _identifiers; } + set { this._identifiers = value; } + } + + public override bool CompareTo(ContextPartitionIdentifier other) { + if (!(other is ContextPartitionIdentifierNested)) { + return false; + } + var nestedOther = (ContextPartitionIdentifierNested) other; + if (nestedOther.Identifiers.Length != _identifiers.Length) { + return false; + } + for (int i = 0; i < _identifiers.Length; i++) { + if (!_identifiers[i].CompareTo(nestedOther.Identifiers[i])) { + return false; + } + } + return true; + } + + public override String ToString() { + return "ContextPartitionIdentifierNested{" + + "identifiers=" + (_identifiers) + + '}'; + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/context/ContextPartitionIdentifierPartitioned.cs b/NEsper.Core/NEsper.Core/client/context/ContextPartitionIdentifierPartitioned.cs new file mode 100755 index 000000000..1be829045 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/context/ContextPartitionIdentifierPartitioned.cs @@ -0,0 +1,59 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.client.context +{ + /// + /// Context partition identifier for segmented contexts. + /// + [Serializable] + public class ContextPartitionIdentifierPartitioned : ContextPartitionIdentifier + { + private object[] _keys; + + /// Ctor. + public ContextPartitionIdentifierPartitioned() + { + } + + /// Ctor. + /// partitioning keys + public ContextPartitionIdentifierPartitioned(Object[] keys) + { + _keys = keys; + } + + /// Returns the partition keys. + /// keys + public object[] Keys + { + get { return _keys; } + set { _keys = value; } + } + + public override bool CompareTo(ContextPartitionIdentifier other) + { + if (!(other is ContextPartitionIdentifierPartitioned)) + { + return false; + } + return Collections.AreEqual(_keys, ((ContextPartitionIdentifierPartitioned) other)._keys); + } + + public override String ToString() + { + return "ContextPartitionIdentifierPartitioned{" + + "keys=" + (_keys) + + '}'; + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/context/ContextPartitionSelector.cs b/NEsper.Core/NEsper.Core/client/context/ContextPartitionSelector.cs new file mode 100755 index 000000000..3977354a9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/context/ContextPartitionSelector.cs @@ -0,0 +1,15 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.client.context +{ + /// Interface for classes representing a selector for navigating, querying or selecting among context partitions. + public interface ContextPartitionSelector + { + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/context/ContextPartitionSelectorAll.cs b/NEsper.Core/NEsper.Core/client/context/ContextPartitionSelectorAll.cs new file mode 100755 index 000000000..00d5cc05e --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/context/ContextPartitionSelectorAll.cs @@ -0,0 +1,22 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +namespace com.espertech.esper.client.context +{ + /// + /// Selects all context paritions. + /// + public sealed class ContextPartitionSelectorAll : ContextPartitionSelector + { + /// + /// Instance for selecting all context partitions. + /// + public static readonly ContextPartitionSelectorAll INSTANCE = new ContextPartitionSelectorAll(); + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/context/ContextPartitionSelectorById.cs b/NEsper.Core/NEsper.Core/client/context/ContextPartitionSelectorById.cs new file mode 100755 index 000000000..c9a64a1b2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/context/ContextPartitionSelectorById.cs @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +namespace com.espertech.esper.client.context +{ + /// Selects a context partition by providing the context partition Id(s). + public interface ContextPartitionSelectorById : ContextPartitionSelector + { + /// Return the context partition ids to select. + /// id set + ICollection ContextPartitionIds { get; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/context/ContextPartitionSelectorCategory.cs b/NEsper.Core/NEsper.Core/client/context/ContextPartitionSelectorCategory.cs new file mode 100755 index 000000000..4a9dc9c7a --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/context/ContextPartitionSelectorCategory.cs @@ -0,0 +1,42 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.client.context +{ + /// + /// Selects context partitions for use with a category context by providing a set of labels. + /// + public interface ContextPartitionSelectorCategory : ContextPartitionSelector + { + /// Returns a set of category label names. + /// label names + ICollection Labels { get; } + } + + public class ProxyContextPartitionSelectorCategory : ContextPartitionSelectorCategory + { + public Func> ProcLabels { get; set; } + + public ProxyContextPartitionSelectorCategory() + { + } + + public ProxyContextPartitionSelectorCategory(Func> procLabels) + { + ProcLabels = procLabels; + } + + public ICollection Labels + { + get { return ProcLabels.Invoke(); } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/context/ContextPartitionSelectorFiltered.cs b/NEsper.Core/NEsper.Core/client/context/ContextPartitionSelectorFiltered.cs new file mode 100755 index 000000000..d8ed6cc69 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/context/ContextPartitionSelectorFiltered.cs @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.context +{ + /// Selects context partitions by receiving a context partition identifier for interrogation. + public interface ContextPartitionSelectorFiltered : ContextPartitionSelector + { + /// + /// Filter function should return true or false to indicate interest in this context partition. + /// + /// Do not hold on to ContextIdentifier instance between calls. The engine may reused an reassing values to this object. + /// + /// provides context partition information, may + /// true to pass filter, false to reject + Boolean Filter(ContextPartitionIdentifier contextPartitionIdentifier); + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/context/ContextPartitionSelectorHash.cs b/NEsper.Core/NEsper.Core/client/context/ContextPartitionSelectorHash.cs new file mode 100755 index 000000000..b1759505a --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/context/ContextPartitionSelectorHash.cs @@ -0,0 +1,24 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +namespace com.espertech.esper.client.context +{ + /// + /// Selects context partitions based on hash codes, for use with hashed context. + /// + public interface ContextPartitionSelectorHash : ContextPartitionSelector + { + /// + /// Returns a set of hashes. + /// + /// hashes + ICollection Hashes { get; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/context/ContextPartitionSelectorNested.cs b/NEsper.Core/NEsper.Core/client/context/ContextPartitionSelectorNested.cs new file mode 100755 index 000000000..64acd04ec --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/context/ContextPartitionSelectorNested.cs @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +namespace com.espertech.esper.client.context +{ + /// Selects context partitions of a nested context by providing selector according to the nested contexts. + public interface ContextPartitionSelectorNested : ContextPartitionSelector + { + /// Selectors for each level of the nested context. + /// selectors + IList Selectors { get; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/context/ContextPartitionSelectorSegmented.cs b/NEsper.Core/NEsper.Core/client/context/ContextPartitionSelectorSegmented.cs new file mode 100755 index 000000000..182a505ca --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/context/ContextPartitionSelectorSegmented.cs @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.client.context +{ + /// + /// Selector of context partitions for use with segmented contexts, provides a set + /// of partition keys to select. + /// + public interface ContextPartitionSelectorSegmented : ContextPartitionSelector + { + /// Returns the partition keys. + /// key set + IList PartitionKeys { get; } + } + + public class ProxyContextPartitionSelectorSegmented : ContextPartitionSelectorSegmented + { + public Func> ProcPartitionKeys { get; set; } + + public IList PartitionKeys + { + get { return ProcPartitionKeys.Invoke(); } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/context/ContextPartitionState.cs b/NEsper.Core/NEsper.Core/client/context/ContextPartitionState.cs new file mode 100755 index 000000000..023e04211 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/context/ContextPartitionState.cs @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.client.context +{ + /// State of a context partition. + public enum ContextPartitionState + { + /// Started state. + STARTED, + + /// Stopped state. + STOPPED + } +} diff --git a/NEsper.Core/NEsper.Core/client/context/ContextPartitionVariableState.cs b/NEsper.Core/NEsper.Core/client/context/ContextPartitionVariableState.cs new file mode 100755 index 000000000..d9600c411 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/context/ContextPartitionVariableState.cs @@ -0,0 +1,47 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.client.context +{ + /// + /// The variable state for a context partitioned variable. + /// + public class ContextPartitionVariableState + { + /// + /// Ctor. + /// + /// agent instance id + /// context partition identification + /// variable state + public ContextPartitionVariableState(int agentInstanceId, ContextPartitionIdentifier identifier, object state) + { + AgentInstanceId = agentInstanceId; + Identifier = identifier; + State = state; + } + + /// + /// Returns the agent instance id + /// + /// id + public int AgentInstanceId { get; private set; } + + /// + /// Returns context partition identifier + /// + /// context partition INFO + public ContextPartitionIdentifier Identifier { get; private set; } + + /// + /// Returns the variable state + /// + /// state + public object State { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/context/EPContextPartitionAdmin.cs b/NEsper.Core/NEsper.Core/client/context/EPContextPartitionAdmin.cs new file mode 100755 index 000000000..264d7915a --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/context/EPContextPartitionAdmin.cs @@ -0,0 +1,141 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.client.context +{ + /// + /// Service interface for administration of contexts and context partitions. + /// + public interface EPContextPartitionAdmin + { + /// + /// Returns the statement names associated to the context of the given name. + /// Returns null if a context declaration for the name does not exist. + /// + /// context name to return statements for + /// statement names, or null if the context does not exist, or empty list if no statements areassociated to the context (counting started and stopped statements, not destroyed ones). + /// + string[] GetContextStatementNames(string contextName); + + /// + /// Returns the nesting level for the context declaration, i.e. 1 for unnested and >1 for nested contexts. + /// + /// context name + /// nesting level + /// ArgumentException if a context by that name was not declared + int GetContextNestingLevel(string contextName); + + /// + /// Dispose one or more context partitions dropping the associated state and removing associated context partition metadata. + /// For key-partitioned contexts and hash-segmented contexts the next event for such + /// context partition allocates a new context partition for that key or hash. + /// If context partitions cannot be found they are not part of the collection returned. + /// Only context partitions in stopped or started state can be destroyed. + /// + /// context name + /// a selector that identifies the context partitions + /// collection of the destroyed context partition ids and descriptors + /// ArgumentException if a context by that name was not declared + /// InvalidContextPartitionSelector if the selector type and context declaration mismatch + ContextPartitionCollection DestroyContextPartitions(string contextName, ContextPartitionSelector selector); + + /// + /// Stop one or more context partitions that are currently started, dropping the associated state and but keeping + /// associated context partition metadata for the purpose of starting it again. + /// Stopping a context partition means any associated statements no longer process + /// events or time for that context partition only, and dropping all such associated state. + /// If context partitions cannot be found they are not part of the collection returned. + /// Stopped context partitions remain stopped and are not returned. + /// + /// context name + /// a selector that identifies the context partitions + /// collection of the stopped context partition ids and descriptors + /// ArgumentException if a context by that name was not declared + /// InvalidContextPartitionSelector if the selector type and context declaration mismatch + ContextPartitionCollection StopContextPartitions(string contextName, ContextPartitionSelector selector); + + /// + /// Start one or more context partitions that were previously stopped. + /// Starting a context partition means any associated statements beging to process + /// events or time for that context partition, starting fresh with newly allocated state. + /// If context partitions cannot be found they are not part of the collection returned. + /// Started context partitions remain started and are not returned. + /// + /// context name + /// a selector that identifies the context partitions + /// collection of the started context partition ids and descriptors + /// ArgumentException if a context by that name was not declared + /// InvalidContextPartitionSelector if the selector type and context declaration mismatch + ContextPartitionCollection StartContextPartitions(string contextName, ContextPartitionSelector selector); + + /// + /// Returns information about selected context partitions including state. + /// + /// context name + /// a selector that identifies the context partitions + /// collection of the context partition ids and descriptors + /// ArgumentException if a context by that name was not declared + /// InvalidContextPartitionSelector if the selector type and context declaration mismatch + ContextPartitionCollection GetContextPartitions(string contextName, ContextPartitionSelector selector); + + /// + /// Returns the context partition ids. + /// + /// context name + /// a selector that identifies the context partitions + /// set of the context partition ids + /// ArgumentException if a context by that name was not declared + /// InvalidContextPartitionSelector if the selector type and context declaration mismatch + ISet GetContextPartitionIds(string contextName, ContextPartitionSelector selector); + + /// + /// Dispose the context partition returning its descriptor. + /// For key-partitioned contexts and hash-segmented contexts the next event for such + /// context partition allocates a new context partition for that key or hash. + /// Only context partitions in stopped or started state can be destroyed. + /// + /// context name + /// the context partition id number + /// descriptor or null if the context partition is not found + /// ArgumentException if a context by that name was not declared + ContextPartitionDescriptor DestroyContextPartition(string contextName, int agentInstanceId); + + /// + /// Stop the context partition if it is currently started and returning its descriptor. + /// + /// context name + /// the context partition id number + /// descriptor or null if the context partition is not found or is already stopped + /// ArgumentException if a context by that name was not declared + ContextPartitionDescriptor StopContextPartition(string contextName, int agentInstanceId); + + /// + /// Start the context partition if it is currently stopped and returning its descriptor. + /// + /// context name + /// the context partition id number + /// descriptor or null if the context partition is not found or is already started + /// ArgumentException if a context by that name was not declared + ContextPartitionDescriptor StartContextPartition(string contextName, int agentInstanceId); + + /// + /// Returning the descriptor of a given context partition. + /// + /// context name + /// the context partition id number + /// descriptor or null if the context partition is not found + /// ArgumentException if a context by that name was not declared + ContextPartitionDescriptor GetDescriptor(string contextName, int agentInstanceId); + } +} diff --git a/NEsper.Core/NEsper.Core/client/context/InvalidContextPartitionSelector.cs b/NEsper.Core/NEsper.Core/client/context/InvalidContextPartitionSelector.cs new file mode 100755 index 000000000..8c41b31e1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/context/InvalidContextPartitionSelector.cs @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.context +{ + /// Indicates an invalid combination of context declaration and context partition selector, i.e. cageory context with hash context partition selector. + public class InvalidContextPartitionSelector : EPException + { + /// Ctor. + /// exception message + public InvalidContextPartitionSelector(String message) + : base(message) + { + } + + /// Ctor. + /// exception message + /// inner exception + public InvalidContextPartitionSelector(String message, Exception cause) + : base(message, cause) + { + } + + /// Ctor. + /// inner exception + public InvalidContextPartitionSelector(Exception cause) + : base(cause) + { + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowAlreadyExistsException.cs b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowAlreadyExistsException.cs new file mode 100755 index 000000000..d93cc58dd --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowAlreadyExistsException.cs @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.dataflow +{ + /// + /// Thrown to indicate a data flow saved configuration already exists. + /// + public class EPDataFlowAlreadyExistsException : EPException + { + /// Ctor. + /// error message + public EPDataFlowAlreadyExistsException(String message) + : base(message) + { + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowCancellationException.cs b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowCancellationException.cs new file mode 100755 index 000000000..4081b2efd --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowCancellationException.cs @@ -0,0 +1,47 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.dataflow +{ + /// Indicates cancellation of a data flow instance. + [Serializable] + public class EPDataFlowCancellationException : EPException + { + public string DataFlowName { get; private set; } + + /// Ctor. + /// cancel message + /// data flow name + public EPDataFlowCancellationException(String message, String dataFlowName) + : base(message) + { + DataFlowName = dataFlowName; + } + + /// Ctor. + /// cancel message + /// cause + /// data flow name + public EPDataFlowCancellationException(String message, Exception cause, String dataFlowName) + : base(message, cause) + { + DataFlowName = dataFlowName; + } + + /// Ctor. + /// cause + /// data flow name + public EPDataFlowCancellationException(Exception cause, String dataFlowName) + : base(cause) + { + DataFlowName = dataFlowName; + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowDescriptor.cs b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowDescriptor.cs new file mode 100755 index 000000000..8129a99e0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowDescriptor.cs @@ -0,0 +1,39 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.dataflow +{ + /// Data flow descriptor. + public class EPDataFlowDescriptor + { + /// Ctor. + /// data flow name + /// statement state + /// statement name + public EPDataFlowDescriptor(String dataFlowName, EPStatementState statementState, String statementName) + { + DataFlowName = dataFlowName; + StatementState = statementState; + StatementName = statementName; + } + + /// Returns the data flow name. + /// name + public string DataFlowName { get; private set; } + + /// Returns the statement state. + /// statement state + public EPStatementState StatementState { get; private set; } + + /// Returns the statement name. + /// statement name. + public string StatementName { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowEPStatementFilter.cs b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowEPStatementFilter.cs new file mode 100755 index 000000000..9f4b956a4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowEPStatementFilter.cs @@ -0,0 +1,19 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.client.dataflow +{ + /// Filter for use with operator. + public interface EPDataFlowEPStatementFilter + { + /// Pass or skip the statement. + /// to test + /// indicator whether to include (true) or exclude (false) the statement. + bool Pass(EPStatement statement); + } +} diff --git a/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowEventBeanCollector.cs b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowEventBeanCollector.cs new file mode 100755 index 000000000..1b0ef49f3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowEventBeanCollector.cs @@ -0,0 +1,22 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.client.dataflow +{ + /// + /// Collector for use with the operator. + /// + public interface EPDataFlowEventBeanCollector + { + /// + /// Collect: use the context to transform an event bean to a data flow event. + /// + /// contains event bean, emitter and related information + void Collect(EPDataFlowEventBeanCollectorContext context); + } +} diff --git a/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowEventBeanCollectorContext.cs b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowEventBeanCollectorContext.cs new file mode 100755 index 000000000..8e890349e --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowEventBeanCollectorContext.cs @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.dataflow.interfaces; + +namespace com.espertech.esper.client.dataflow +{ + /// + /// For use with provides collection context. <p> Do not retain handles to this instance as its contents may change. </p> + /// + public class EPDataFlowEventBeanCollectorContext + { + /// Ctor. + /// to emit into the data flow + /// indicator whether to submit EventBean or underlying events + /// to process + public EPDataFlowEventBeanCollectorContext(EPDataFlowEmitter emitter, bool submitEventBean, EventBean theEvent) + { + Emitter = emitter; + IsSubmitEventBean = submitEventBean; + Event = theEvent; + } + + /// Returns the event to process. + /// event + public EventBean Event { get; set; } + + /// Returns the emitter. + /// emitter + public EPDataFlowEmitter Emitter { get; private set; } + + /// Returns true to submit EventBean instances, false to submit underlying event. + /// indicator whether wrapper required or not + public bool IsSubmitEventBean { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowEventCollector.cs b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowEventCollector.cs new file mode 100755 index 000000000..f84bf5fbe --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowEventCollector.cs @@ -0,0 +1,21 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.client.dataflow +{ + /// + /// Collector for use with the operator. + /// + public interface EPDataFlowEventCollector { + /// + /// Collect: use the context to transform insert and remove stream events to data flow events. + /// + /// contains event beans, emitter and related information + void Collect(EPDataFlowEventCollectorContext context); + } +} diff --git a/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowEventCollectorContext.cs b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowEventCollectorContext.cs new file mode 100755 index 000000000..4f0d6b80c --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowEventCollectorContext.cs @@ -0,0 +1,36 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.dataflow +{ + /// + /// For use with provides collection context. + /// + /// Do not retain handles to this instance as its contents may change. + /// + public class EPDataFlowEventCollectorContext + { + /// Ctor. + /// for sending events to the event bus + /// to process + public EPDataFlowEventCollectorContext(EventBusCollector eventBusCollector, Object theEvent) { + EventBusCollector = eventBusCollector; + Event = theEvent; + } + + /// Returns the event. + /// event + public object Event { get; set; } + + /// Returns the emitter for the event bus. + /// emitter + public EventBusCollector EventBusCollector { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowExceptionContext.cs b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowExceptionContext.cs new file mode 100755 index 000000000..7092f04b1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowExceptionContext.cs @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.dataflow +{ + /// + /// Context for use with + /// + public class EPDataFlowExceptionContext + { + /// Ctor. + /// data flow name + /// operator name + /// operator number + /// pretty-print of operator + /// cause + public EPDataFlowExceptionContext(String dataFlowName, String operatorName, Object operatorNumber, Object operatorPrettyPrint, Exception throwable) + { + DataFlowName = dataFlowName; + OperatorName = operatorName; + OperatorNumber = operatorNumber; + OperatorPrettyPrint = operatorPrettyPrint; + Exception = throwable; + } + + /// Returns the data flow name. + /// data flow name + public string DataFlowName { get; private set; } + + /// Returns the operator name. + /// operator name + public string OperatorName { get; private set; } + + /// Returns the cause. + /// cause + public Exception Exception { get; private set; } + + /// Returns the operator number. + /// operator num + public object OperatorNumber { get; private set; } + + /// Returns the pretty-print for the operator. + /// operator string + public object OperatorPrettyPrint { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowExceptionHandler.cs b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowExceptionHandler.cs new file mode 100755 index 000000000..47e014e31 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowExceptionHandler.cs @@ -0,0 +1,21 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.client.dataflow +{ + /// + /// Handler for exceptions thrown by data flow operators. + /// + public interface EPDataFlowExceptionHandler { + /// + /// Handle exception. + /// + /// provides all exception information + void Handle(EPDataFlowExceptionContext context); + } +} diff --git a/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowExecutionException.cs b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowExecutionException.cs new file mode 100755 index 000000000..025395b0a --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowExecutionException.cs @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.dataflow +{ + /// + /// Thrown to indicate a data flow execution exception. + /// + public class EPDataFlowExecutionException : EPException + { + public string DataFlowName { get; private set; } + + /// Ctor. + /// error message + /// data flow name + public EPDataFlowExecutionException(String message, String dataFlowName) + : base(message) + { + DataFlowName = dataFlowName; + } + + /// Ctor. + /// error message + /// cuase + /// data flow name + public EPDataFlowExecutionException(String message, Exception cause, String dataFlowName) + : base(message, cause) + { + DataFlowName = dataFlowName; + } + + /// Ctor. + /// cuase + /// data flow name + public EPDataFlowExecutionException(Exception cause, String dataFlowName) + : base(cause) + { + DataFlowName = dataFlowName; + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowIRStreamCollector.cs b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowIRStreamCollector.cs new file mode 100755 index 000000000..c68166ceb --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowIRStreamCollector.cs @@ -0,0 +1,22 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.client.dataflow +{ + /// + /// Collector for use with the operator. + /// + public interface EPDataFlowIRStreamCollector + { + /// + /// Collect: use the context to transform statement output Event(s) to data flow Event(s). + /// + /// contains event bean, emitter and related information + void Collect(EPDataFlowIRStreamCollectorContext context); + } +} diff --git a/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowIRStreamCollectorContext.cs b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowIRStreamCollectorContext.cs new file mode 100755 index 000000000..494bbc04a --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowIRStreamCollectorContext.cs @@ -0,0 +1,63 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.dataflow.interfaces; + +namespace com.espertech.esper.client.dataflow +{ + /// + /// Context for use with . <p> Do not retain a handle of this object as its contents are subject to change. </p> + /// + public class EPDataFlowIRStreamCollectorContext + { + /// + /// Ctor. + /// + /// data flow emitter + /// indicator whether the EventBean or the underlying event object must be submmitted + /// insert stream events + /// remove stream events + /// statement posting events + /// engine instances + public EPDataFlowIRStreamCollectorContext(EPDataFlowEmitter emitter, bool submitEventBean, EventBean[] newEvents, EventBean[] oldEvents, EPStatement statement, EPServiceProvider epServiceProvider) + { + Emitter = emitter; + IsSubmitEventBean = submitEventBean; + NewEvents = newEvents; + OldEvents = oldEvents; + Statement = statement; + ServiceProvider = epServiceProvider; + } + + /// Returns the emitter. + /// emitter + public EPDataFlowEmitter Emitter { get; private set; } + + /// Returns insert stream. + /// events + public EventBean[] NewEvents { get; set; } + + /// Returns remove stream. + /// events + public EventBean[] OldEvents { get; set; } + + /// Returns the statement. + /// statement + public EPStatement Statement { get; internal set; } + + /// Returns the engine instance. + /// engine instance + public EPServiceProvider ServiceProvider { get; set; } + + /// + /// Returns indicator whether to submit wrapped events (EventBean) or underlying events + /// + /// wrapped event indicator + public bool IsSubmitEventBean { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowInstance.cs b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowInstance.cs new file mode 100755 index 000000000..ec8f42430 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowInstance.cs @@ -0,0 +1,64 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +namespace com.espertech.esper.client.dataflow +{ + /// + /// Data flow instanve. + /// + public interface EPDataFlowInstance + { + /// Returns the data flow name. + /// name + string DataFlowName { get; } + + /// Returns the state. + /// state + EPDataFlowState State { get; } + + /// Blocking execution of the data flow instance. + /// IllegalStateException thrown to indicate that the state is not instantiated. + /// EPDataFlowExecutionException thrown when an execution exception occurs + /// EPDataFlowCancellationException throw to indicate the data flow was cancelled. + void Run(); + + /// Non-Blocking execution of the data flow instance. + /// IllegalStateException thrown to indicate that the state is not instantiated. + void Start(); + + /// Captive execution of the data flow instance. + /// runnables and emitters + EPDataFlowInstanceCaptive StartCaptive(); + + /// Join an executing data flow instance. + /// IllegalStateException thrown if it cannot be joined + /// InterruptedException thrown if interrupted + void Join(); + + /// Cancel execution. + void Cancel(); + + /// Get data flow instance statistics, required instantiation with statistics option, use to turn on stats. + /// stats + EPDataFlowInstanceStatistics Statistics { get; } + + /// Returns the user object associated, if any. Use to associate. + /// user object + object UserObject { get; } + + /// Returns the instance id associated, if any. Use to associate. + /// instance if + string InstanceId { get; } + + /// Returns runtime parameters provided at instantiation time, or null if none have been provided. + /// runtime parameters + IDictionary Parameters { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowInstanceCaptive.cs b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowInstanceCaptive.cs new file mode 100755 index 000000000..4c72c0172 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowInstanceCaptive.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.dataflow.ops; +using com.espertech.esper.dataflow.runnables; + +namespace com.espertech.esper.client.dataflow +{ + /// Holder for captive data flow execution. + public class EPDataFlowInstanceCaptive + { + /// Ctor. + /// any emitters that are part of the data flow + /// any runnables that represent source operators + public EPDataFlowInstanceCaptive(IDictionary emitters, IList runnables) + { + Emitters = emitters; + Runnables = runnables; + } + + /// Map of named emitters. + /// emitters + public IDictionary Emitters { get; private set; } + + /// List of operator source runnables. + /// runnables + public IList Runnables { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowInstanceOperatorStat.cs b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowInstanceOperatorStat.cs new file mode 100755 index 000000000..85cc2f2d7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowInstanceOperatorStat.cs @@ -0,0 +1,63 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.dataflow +{ + /// Statistics holder for data flow instances. + public class EPDataFlowInstanceOperatorStat + { + /// Ctor. + /// operator name + /// operator pretty print + /// operator number + /// count of submitted events + /// count of events submitted per port + /// time spent submitting events + /// time spent submitting events per port + public EPDataFlowInstanceOperatorStat(String operatorName, String operatorPrettyPrint, int operatorNumber, long submittedOverallCount, long[] submittedPerPortCount, long timeOverall, long[] timePerPort) + { + OperatorName = operatorName; + OperatorPrettyPrint = operatorPrettyPrint; + OperatorNumber = operatorNumber; + SubmittedOverallCount = submittedOverallCount; + SubmittedPerPortCount = submittedPerPortCount; + TimeOverall = timeOverall; + TimePerPort = timePerPort; + } + + /// Returns operator name. + /// op name + public string OperatorName { get; private set; } + + /// Returns count of submitted events. + /// count + public long SubmittedOverallCount { get; private set; } + + /// Returns count of submitted events per port. + /// count per port + public long[] SubmittedPerPortCount { get; private set; } + + /// Returns operator pretty print + /// textual representation of op + public string OperatorPrettyPrint { get; private set; } + + /// Returns the operator number. + /// op number + public int OperatorNumber { get; private set; } + + /// Returns total time spent submitting events + /// time + public long TimeOverall { get; private set; } + + /// Returns total time spent submitting events per port + /// time per port + public long[] TimePerPort { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowInstanceStatistics.cs b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowInstanceStatistics.cs new file mode 100755 index 000000000..ecf8e6fb7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowInstanceStatistics.cs @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +namespace com.espertech.esper.client.dataflow +{ + /// Interface for statictics of data flow instance. + public interface EPDataFlowInstanceStatistics + { + /// Returns operator stats. + /// stats + IList OperatorStatistics { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowInstantiationException.cs b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowInstantiationException.cs new file mode 100755 index 000000000..c3696b7d4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowInstantiationException.cs @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.dataflow +{ + /// Indicates an exception instantiating a data flow. + public class EPDataFlowInstantiationException : EPException + { + /// Ctor. + /// the message + public EPDataFlowInstantiationException(String message) + : base(message) + { + } + + /// Ctor. + /// the message + /// the inner exception + public EPDataFlowInstantiationException(String message, Exception cause) + : base(message, cause) + { + } + + /// Ctor. + /// the inner exception + public EPDataFlowInstantiationException(Exception cause) + : base(cause) + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowInstantiationOptions.cs b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowInstantiationOptions.cs new file mode 100755 index 000000000..8fd7e89eb --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowInstantiationOptions.cs @@ -0,0 +1,217 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.core.service; + +namespace com.espertech.esper.client.dataflow +{ + /// + /// Options for use when instantiating a data flow in + /// + [Serializable] + public class EPDataFlowInstantiationOptions + { + private bool _cpuStatistics; + private String _dataFlowInstanceId; + private Object _dataFlowInstanceUserObject; + private EPDataFlowExceptionHandler _exceptionHandler; + private EPDataFlowOperatorProvider _operatorProvider; + private bool _operatorStatistics; + private EPDataFlowOperatorParameterProvider _parameterProvider; + + /// + /// Gets or sets the event sender /runtime to use + /// + /// The surrogate event sender. + public EPRuntimeEventSender SurrogateEventSender { get; set; } + + /// + /// Gets or sets the parameters. + /// + public IDictionary ParametersURIs { get; private set; } + + /// Returns the operator provider. + /// operator provider + public EPDataFlowOperatorProvider GetOperatorProvider() + { + return _operatorProvider; + } + + /// Sets the the operator provider. + /// operator provider + /// this options object + public EPDataFlowInstantiationOptions OperatorProvider(EPDataFlowOperatorProvider operatorProvider) + { + _operatorProvider = operatorProvider; + return this; + } + + /// Sets the the operator provider. + /// operator provider + public void SetOperatorProvider(EPDataFlowOperatorProvider operatorProvider) + { + _operatorProvider = operatorProvider; + } + + /// Sets the parameter provider. + /// parameter provider + /// this options object + public EPDataFlowInstantiationOptions ParameterProvider(EPDataFlowOperatorParameterProvider parameterProvider) + { + _parameterProvider = parameterProvider; + return this; + } + + /// Returns the parameter provider. + /// parameter provider + public EPDataFlowOperatorParameterProvider GetParameterProvider() + { + return _parameterProvider; + } + + /// Sets the parameter provider. + /// parameter provider + public void SetParameterProvider(EPDataFlowOperatorParameterProvider parameterProvider) + { + _parameterProvider = parameterProvider; + } + + /// Returns the exception handler. + /// exception handler. + public EPDataFlowExceptionHandler GetExceptionHandler() + { + return _exceptionHandler; + } + + /// Sets the exception handler. + /// exception handler. + /// this options object + public EPDataFlowInstantiationOptions ExceptionHandler(EPDataFlowExceptionHandler exceptionHandler) + { + _exceptionHandler = exceptionHandler; + return this; + } + + /// Sets the exception handler. + /// exception handler. + public void SetExceptionHandler(EPDataFlowExceptionHandler exceptionHandler) + { + _exceptionHandler = exceptionHandler; + } + + /// Returns the instance id assigned. + /// instance if + public String GetDataFlowInstanceId() + { + return _dataFlowInstanceId; + } + + /// Sets the data flow instance id + /// instance id + /// this options object + public EPDataFlowInstantiationOptions DataFlowInstanceId(String dataFlowInstanceId) + { + _dataFlowInstanceId = dataFlowInstanceId; + return this; + } + + /// Sets the data flow instance id + /// instance id + public void SetDataFlowInstanceId(String dataFlowInstanceId) + { + _dataFlowInstanceId = dataFlowInstanceId; + } + + /// Returns the user object associated to the data flow instance. + /// user object + public Object GetDataFlowInstanceUserObject() + { + return _dataFlowInstanceUserObject; + } + + /// Sets the user object associated to the data flow instance. + /// user object + /// this options object + public EPDataFlowInstantiationOptions DataFlowInstanceUserObject(Object dataFlowInstanceUserObject) + { + _dataFlowInstanceUserObject = dataFlowInstanceUserObject; + return this; + } + + /// Sets the user object associated to the data flow instance. + /// this options object + public void SetDataFlowInstanceUserObject(Object dataFlowInstanceUserObject) + { + _dataFlowInstanceUserObject = dataFlowInstanceUserObject; + } + + /// Returns indicator whether to collect operator statistics. + /// operator stats indicator + public bool IsOperatorStatistics() + { + return _operatorStatistics; + } + + /// Sets indicator whether to collect operator statistics. + /// operator stats indicator + /// this options object + public EPDataFlowInstantiationOptions OperatorStatistics(bool statistics) + { + _operatorStatistics = statistics; + return this; + } + + /// Sets indicator whether to collect operator statistics. + /// operator stats indicator + public void SetOperatorStatistics(bool operatorStatistics) + { + _operatorStatistics = operatorStatistics; + } + + /// Returns indicator whether to collect CPU statistics. + /// CPU stats + public bool IsCpuStatistics() + { + return _cpuStatistics; + } + + /// Sets indicator whether to collect CPU statistics. + /// CPU stats + public void SetCpuStatistics(bool cpuStatistics) + { + _cpuStatistics = cpuStatistics; + } + + /// Sets indicator whether to collect CPU statistics. + /// CPU stats + /// this options object + public EPDataFlowInstantiationOptions CpuStatistics(bool cpuStatistics) + { + _cpuStatistics = cpuStatistics; + return this; + } + + /// + /// Adds the parameter URI. + /// + /// The name. + /// The value. + public void AddParameterURI(string name, object value) + { + if (ParametersURIs == null) + { + ParametersURIs = new Dictionary(); + } + + ParametersURIs[name] = value; + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowNotFoundException.cs b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowNotFoundException.cs new file mode 100755 index 000000000..dda72f327 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowNotFoundException.cs @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.dataflow +{ + /// Thrown to indicate a data flow is not found. + public class EPDataFlowNotFoundException : EPException + { + /// Ctor. + /// error message + public EPDataFlowNotFoundException(String message) + : base(message) + { + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowOperatorParameterProvider.cs b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowOperatorParameterProvider.cs new file mode 100755 index 000000000..83e89c0c1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowOperatorParameterProvider.cs @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.dataflow +{ + /// + /// Handles setting or overriding properties for operators in a data flow. + /// + public interface EPDataFlowOperatorParameterProvider + { + /// + /// Return new value for operator + /// + /// operator and parameter information + /// value + Object Provide(EPDataFlowOperatorParameterProviderContext context); + } +} diff --git a/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowOperatorParameterProviderContext.cs b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowOperatorParameterProviderContext.cs new file mode 100755 index 000000000..a57927b14 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowOperatorParameterProviderContext.cs @@ -0,0 +1,64 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.dataflow +{ + /// + /// Context for use with describes the operator and parameters to provide. + /// + public class EPDataFlowOperatorParameterProviderContext + { + /// Ctor. + /// operator name + /// parameter name + /// operator instance + /// operator number + /// value if any was provided as part of the declaration + /// data flow name + public EPDataFlowOperatorParameterProviderContext(String operatorName, + String parameterName, + Object operatorInstance, + int operatorNum, + Object providedValue, + String dataFlowName) + { + OperatorName = operatorName; + ParameterName = parameterName; + OperatorInstance = operatorInstance; + OperatorNum = operatorNum; + ProvidedValue = providedValue; + DataFlowName = dataFlowName; + } + + /// Returns the operator name. + /// operator name + public string OperatorName { get; private set; } + + /// Returns the parameter name. + /// parameter name + public string ParameterName { get; private set; } + + /// Returns the operator instance. + /// operator instance + public object OperatorInstance { get; private set; } + + /// Returns the operator number + /// operator num + public int OperatorNum { get; private set; } + + /// Returns the parameters declared value, if any + /// value + public object ProvidedValue { get; private set; } + + /// Returns the data flow name. + /// data flow name + public string DataFlowName { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowOperatorProvider.cs b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowOperatorProvider.cs new file mode 100755 index 000000000..5d7e3f0df --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowOperatorProvider.cs @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.dataflow +{ + /// + /// For use in data flow instantiation, may provide operator instances. + /// + public interface EPDataFlowOperatorProvider + { + /// + /// Called to see if the provider would like to provide the operator instance as described in the context. + /// + /// operator instance requested + /// + /// operator instance, or null if the default empty construct instantiation for the operator class should be used + /// + Object Provide(EPDataFlowOperatorProviderContext context); + } +} diff --git a/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowOperatorProviderContext.cs b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowOperatorProviderContext.cs new file mode 100755 index 000000000..143da99ba --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowOperatorProviderContext.cs @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.client.dataflow +{ + /// Context for use with . + public class EPDataFlowOperatorProviderContext + { + /// Ctor. + /// data flow name + /// operator name + /// specification + public EPDataFlowOperatorProviderContext(String dataFlowName, String operatorName, GraphOperatorSpec spec) + { + DataFlowName = dataFlowName; + OperatorName = operatorName; + Spec = spec; + } + + /// Operator name. + /// name + public string OperatorName { get; private set; } + + /// Data flow name + /// name + public string DataFlowName { get; private set; } + + /// Operator specification + /// spec + public GraphOperatorSpec Spec { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowRuntime.cs b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowRuntime.cs new file mode 100755 index 000000000..d92fe4b76 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowRuntime.cs @@ -0,0 +1,88 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.dataflow +{ + /// + /// Data flow runtime for instantiating data flows. + /// + public interface EPDataFlowRuntime + { + /// Returns a descriptor for the given data flow, or null if the data flow has not been declared. + /// data flow name + /// data flow descriptor + EPDataFlowDescriptor GetDataFlow(String dataFlowName); + + /// Returns the names of all declared data flows. + /// data flow names + String[] GetDataFlows(); + + /// Instantiate a data flow. + /// name of data flow to instantiate + /// data flow instance + /// EPDataFlowInstantiationException when the instantiation failed + EPDataFlowInstance Instantiate(String dataFlowName); + + /// Instantiate a data flow, with options. + /// name of data flow to instantiate + /// populate options to control parameterization, instantiation etc. + /// data flow instance + /// EPDataFlowInstantiationException when the instantiation failed + EPDataFlowInstance Instantiate(String dataFlowName, EPDataFlowInstantiationOptions options); + + /// Save an existing data flow configuration (data flow name and its options) for later retrieval. + /// configuration name to save, must be unique + /// data flow name + /// options object + /// EPDataFlowAlreadyExistsException if the configuration name is already used + /// EPDataFlowNotFoundException if the data flow by this name does not exist + void SaveConfiguration(String dataflowConfigName, String dataFlowName, EPDataFlowInstantiationOptions options); + + /// Returns the names of a saved data flow configurations. + /// data flow configuration names + string[] SavedConfigurations { get; } + + /// Returns a saved dataflow configuration or null if it is not found. + /// name to find + /// data flow configuration + EPDataFlowSavedConfiguration GetSavedConfiguration(String configurationName); + + /// Instantiate a data flow from a saved configuration. + /// configuration name + /// instance + /// EPDataFlowInstantiationException if the configuration name could not be found + EPDataFlowInstance InstantiateSavedConfiguration(String configurationName); + + /// Remove a previously saved data flow configuration. + /// to remove + /// indicator whether found and removed + bool RemoveSavedConfiguration(String configurationName); + + /// Save an existing instance with the runtime, for later retrieval. + /// name to use to save, must be unique among currently saved instances + /// saved + /// EPDataFlowAlreadyExistsException if an instance by this name already exists + void SaveInstance(String instanceName, EPDataFlowInstance instance); + + /// Returns the instance names of a saved data flow instances. + /// data flow instance names + string[] SavedInstances { get; } + + /// Returns a specific saved data flow instance, or null if it has not been found + /// to look for + /// instance + EPDataFlowInstance GetSavedInstance(String instanceName); + + /// Remove an instance previously saved. + /// to be removed + /// indicator whether found or not + bool RemoveSavedInstance(String instanceName); + } +} diff --git a/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowSavedConfiguration.cs b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowSavedConfiguration.cs new file mode 100755 index 000000000..0a0fc9d30 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowSavedConfiguration.cs @@ -0,0 +1,42 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.dataflow +{ + /// A data flow configuration is just a configuration name, a data flow name and an instantiation options object. + [Serializable] + public class EPDataFlowSavedConfiguration + { + /// Ctor. + /// name of saved configuration + /// data flow name + /// options object + public EPDataFlowSavedConfiguration(String savedConfigurationName, + String dataflowName, + EPDataFlowInstantiationOptions options) + { + SavedConfigurationName = savedConfigurationName; + DataflowName = dataflowName; + Options = options; + } + + /// Configuation name. + /// name + public string SavedConfigurationName { get; private set; } + + /// Data flow name. + /// data flow name + public string DataflowName { get; private set; } + + /// Data flow instantiation options. + /// options + public EPDataFlowInstantiationOptions Options { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowSignal.cs b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowSignal.cs new file mode 100755 index 000000000..36f2f4b2d --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowSignal.cs @@ -0,0 +1,15 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.client.dataflow +{ + /// Base marker interface for data flow signals. + public interface EPDataFlowSignal + { + } +} diff --git a/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowSignalFinalMarker.cs b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowSignalFinalMarker.cs new file mode 100755 index 000000000..e187dab14 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowSignalFinalMarker.cs @@ -0,0 +1,19 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.client.dataflow +{ + /// Final marker for data flows. + public interface EPDataFlowSignalFinalMarker : EPDataFlowSignal + { + } + + public class EPDataFlowSignalFinalMarkerImpl : EPDataFlowSignalFinalMarker + { + } +} diff --git a/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowSignalWindowMarker.cs b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowSignalWindowMarker.cs new file mode 100755 index 000000000..2118bdc77 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowSignalWindowMarker.cs @@ -0,0 +1,15 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.client.dataflow +{ + /// Window marker for data flows. + public interface EPDataFlowSignalWindowMarker : EPDataFlowSignal + { + } +} diff --git a/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowState.cs b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowState.cs new file mode 100755 index 000000000..8232233da --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/dataflow/EPDataFlowState.cs @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.client.dataflow +{ + /// Data flow instance states. + public enum EPDataFlowState { + /// Start state: the state a data flow instance is in when it gets instantiated. + INSTANTIATED, + + /// Running means the data flow instance is currently executing. + RUNNING, + + /// Complete means the data flow instance completed. + COMPLETE, + + /// Cancelled means the data flow instance was cancelled. + CANCELLED, + } +} diff --git a/NEsper.Core/NEsper.Core/client/dataflow/EventBusCollector.cs b/NEsper.Core/NEsper.Core/client/dataflow/EventBusCollector.cs new file mode 100755 index 000000000..d75b5469b --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/dataflow/EventBusCollector.cs @@ -0,0 +1,76 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Xml; +using System.Xml.Linq; + +namespace com.espertech.esper.client.dataflow +{ + /// + /// Collector for send events into the event bus. + /// + public interface EventBusCollector + { + /// + /// Send an event represented by a plain object to the event stream processing runtime. + /// + /// Use the route method for sending events into the runtime from within UpdateListener code, + /// to avoid the possibility of a stack overflow due to nested calls to sendEvent. + /// + /// + /// is the event to sent to the runtime + /// com.espertech.esper.client.EPException is thrown when the processing of the event lead to an error + void SendEvent(Object @object); + + /// + /// Send a map containing event property values to the event stream processing runtime. + /// + /// Use the route method for sending events into the runtime from within UpdateListener code + /// to avoid the possibility of a stack overflow due to nested calls to sendEvent. + /// + /// + /// map that contains event property values. Keys are expected to be of type String while value scan be of any type. Keys and values should match those declared via Configuration for the given eventTypeName. + /// the name for the Map event type that was previously configured + /// EPException - when the processing of the event leads to an error + void SendEvent(IDictionary map, String eventTypeName); + + /// + /// Send an object array containing event property values as array elements to the event stream processing runtime. + /// + /// Use the route method for sending events into the runtime from within UpdateListener code to avoid + /// the possibility of a stack overflow due to nested calls to sendEvent. + /// + /// + /// object array that contains event property values.Your application must ensure that property values match the exact same order that the property names and types have been declared, and that the array length matches the number of properties declared. + /// the name for the Object-array event type that was previously configured + /// EPException - when the processing of the event leads to an error + void SendEvent(Object[] objectArray, String eventTypeName); + + /// + /// Send an event represented by a DOM node to the event stream processing runtime. + /// + /// Use the route method for sending events into the runtime from within UpdateListener code. to avoid the possibility of a stack overflow due to nested calls to sendEvent. + /// + /// + /// is the DOM node as an event + /// EPException is thrown when the processing of the event lead to an error + void SendEvent(XmlNode node); + + /// + /// Send an event represented by a DOM node to the event stream processing runtime. + /// + /// Use the route method for sending events into the runtime from within UpdateListener code. to avoid the possibility of a stack overflow due to nested calls to sendEvent. + /// + /// + /// is the DOM node as an event + /// EPException is thrown when the processing of the event lead to an error + void SendEvent(XElement node); + } +} diff --git a/NEsper.Core/NEsper.Core/client/deploy/DeploymentActionException.cs b/NEsper.Core/NEsper.Core/client/deploy/DeploymentActionException.cs new file mode 100755 index 000000000..c8090e66f --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/deploy/DeploymentActionException.cs @@ -0,0 +1,55 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +namespace com.espertech.esper.client.deploy +{ + /// + /// Exception list populated in a deployment operation. + /// + [Serializable] + public class DeploymentActionException : DeploymentException + { + /// Ctor. + /// deployment error message + /// that occured deploying + public DeploymentActionException(String message, IList exceptions) + : base(message) + { + Exceptions = exceptions; + } + + /// Returns the exception list. + /// exceptions + public IList Exceptions { get; private set; } + + /// Returns a detail print of all exceptions and messages line-separated. + /// exception list + public String GetDetail() + { + var detail = new StringWriter(); + var count = 0; + var delimiter = ""; + for (int ii = 0; ii < Exceptions.Count; ii++) + { + var item = Exceptions[ii]; + detail.Write(delimiter); + detail.Write("Exception #"); + detail.Write(Convert.ToString(count)); + detail.Write(" : "); + detail.Write(item.InnerException.Message); + delimiter = Environment.NewLine + Environment.NewLine; + count++; + } + return detail.ToString(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/deploy/DeploymentException.cs b/NEsper.Core/NEsper.Core/client/deploy/DeploymentException.cs new file mode 100755 index 000000000..748fbbb63 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/deploy/DeploymentException.cs @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.deploy +{ + /// + /// Base deployment exception. + /// + [Serializable] + public class DeploymentException : Exception + { + /// Ctor. + /// error message + public DeploymentException(String message) + : base(message) + { + } + + /// Ctor. + /// error message + /// cause + public DeploymentException(String message, Exception cause) + : base(message, cause) + { + } + + /// Ctor. + /// cause + public DeploymentException(Exception cause) + : base(string.Empty, cause) + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/deploy/DeploymentInformation.cs b/NEsper.Core/NEsper.Core/client/deploy/DeploymentInformation.cs new file mode 100755 index 000000000..fa23147a2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/deploy/DeploymentInformation.cs @@ -0,0 +1,72 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; + +namespace com.espertech.esper.client.deploy +{ + /// + /// Available information about deployment made. + /// + [Serializable] + public class DeploymentInformation + { + /// Ctor. + /// deployment id + /// date the deployment was added + /// date of last Update to state + /// module statement-level details + /// current state + /// the module + public DeploymentInformation( + String deploymentId, + Module module, + DateTimeEx addedDate, + DateTimeEx lastUpdateDate, + DeploymentInformationItem[] items, + DeploymentState state) + { + DeploymentId = deploymentId; + Module = module; + LastUpdateDate = lastUpdateDate; + AddedDate = addedDate; + Items = items; + State = state; + } + + /// Returns the deployment id. + /// deployment id + public string DeploymentId { get; private set; } + + /// Returns the last Update date, i.e. date the information was last updated with new state. + /// last Update date + public DateTimeEx LastUpdateDate { get; private set; } + + /// Returns deployment statement-level details: Note that for an newly-added undeployed modules not all statement-level information is available and therefore returns an empty array. + /// statement details or empty array for newly added deployments + public DeploymentInformationItem[] Items { get; private set; } + + /// Returns current deployment state. + /// state + public DeploymentState State { get; private set; } + + /// Returns date the deployment was added. + /// added-date + public DateTimeEx AddedDate { get; private set; } + + /// Returns the module. + /// module + public Module Module { get; private set; } + + public override String ToString() { + return string.Format("id '{0}' " + " added on {1}", DeploymentId, AddedDate); + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/deploy/DeploymentInformationItem.cs b/NEsper.Core/NEsper.Core/client/deploy/DeploymentInformationItem.cs new file mode 100755 index 000000000..50123b413 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/deploy/DeploymentInformationItem.cs @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.deploy +{ + /// + /// Statement level information for deployed modules. + /// + [Serializable] + public class DeploymentInformationItem + { + /// + /// Ctor. + /// + /// name of statement + /// EPL text + public DeploymentInformationItem(String statementName, String expression) + { + StatementName = statementName; + Expression = expression; + } + + /// + /// Returns statement name. + /// + /// name + public string StatementName { get; private set; } + + /// + /// Returns EPL text. + /// + /// expression + public string Expression { get; private set; } + + public override String ToString() + { + return "name '" + StatementName + "' " + + " expression " + Expression; + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/deploy/DeploymentItemException.cs b/NEsper.Core/NEsper.Core/client/deploy/DeploymentItemException.cs new file mode 100755 index 000000000..f2d0d0bc4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/deploy/DeploymentItemException.cs @@ -0,0 +1,42 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.deploy +{ + /// Inner exception to available on statement level. + [Serializable] + public class DeploymentItemException : DeploymentException + { + /// Ctor. + /// exception text + /// EPL + /// compile or start exception + /// line number + public DeploymentItemException(String message, String expression, Exception inner, int lineNumber) + : base(message, inner) + { + Expression = expression; + Inner = inner; + LineNumber = lineNumber; + } + + /// Returns EPL expression. + /// expression + public string Expression { get; private set; } + + /// Returns EPL compile or start exception. + /// exception + public Exception Inner { get; private set; } + + /// Returns line number. + /// line number + public int LineNumber { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/deploy/DeploymentLockException.cs b/NEsper.Core/NEsper.Core/client/deploy/DeploymentLockException.cs new file mode 100755 index 000000000..18e482b01 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/deploy/DeploymentLockException.cs @@ -0,0 +1,44 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.deploy +{ + /// Exception to indicate a problem taking a lock + public class DeploymentLockException : DeploymentException + { + /// + /// Ctor. + /// + /// message + public DeploymentLockException(string message) + : base(message) + { + } + + /// + /// Ctor + /// + /// message + /// cause + public DeploymentLockException(string message, Exception cause) + : base(message, cause) + { + } + + /// + /// Ctor. + /// + /// cause + public DeploymentLockException(Exception cause) + : base(cause) + { + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/client/deploy/DeploymentLockStrategy.cs b/NEsper.Core/NEsper.Core/client/deploy/DeploymentLockStrategy.cs new file mode 100755 index 000000000..e859d2907 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/deploy/DeploymentLockStrategy.cs @@ -0,0 +1,35 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Threading; + +using com.espertech.esper.compat.threading; + +namespace com.espertech.esper.client.deploy +{ + /// + /// Implement this interface to provide a custom deployment lock strategy. + /// The default lock strategy is . + /// + public interface DeploymentLockStrategy + { + /// + /// Acquire should acquire the write lock of the provided read-write lock and may retry and backoff or fail. + /// + /// the engine-wide event processing read-write lock + /// to indicate lock attempt failed + /// when lock-taking is interrupted + void Acquire(IReaderWriterLock engineWideLock) ; + + /// + /// Release should release the write lock of the provided read-write lock and should never fail. + /// + /// the engine-wide event processing read-write lock + void Release(IReaderWriterLock engineWideLock); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/client/deploy/DeploymentLockStrategyDefault.cs b/NEsper.Core/NEsper.Core/client/deploy/DeploymentLockStrategyDefault.cs new file mode 100755 index 000000000..146e33e51 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/deploy/DeploymentLockStrategyDefault.cs @@ -0,0 +1,33 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.compat.threading; + +namespace com.espertech.esper.client.deploy +{ + /// + /// Obtains the write lock of the engine-wide event processing read-write lock by simply blocking until the lock was obtained. + /// + public class DeploymentLockStrategyDefault : DeploymentLockStrategy + { + public static readonly DeploymentLockStrategyDefault INSTANCE = new DeploymentLockStrategyDefault(); + + private DeploymentLockStrategyDefault() { + } + + public void Acquire(IReaderWriterLock engineWideLock) + { + engineWideLock.WriteLock.Acquire(); + } + + public void Release(IReaderWriterLock engineWideLock) + { + engineWideLock.WriteLock.Release(); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/client/deploy/DeploymentLockStrategyNone.cs b/NEsper.Core/NEsper.Core/client/deploy/DeploymentLockStrategyNone.cs new file mode 100755 index 000000000..2edb7ccb4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/deploy/DeploymentLockStrategyNone.cs @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.compat.threading; + +namespace com.espertech.esper.client.deploy +{ + /// + /// Obtains the write lock of the engine-wide event processing read-write lock by simply blocking until the lock was obtained. + /// + public class DeploymentLockStrategyNone : DeploymentLockStrategy + { + public static readonly DeploymentLockStrategyNone INSTANCE = new DeploymentLockStrategyNone(); + + private DeploymentLockStrategyNone() + { + } + + public void Acquire(IReaderWriterLock engineWideLock) + { + } + + public void Release(IReaderWriterLock engineWideLock) + { + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/client/deploy/DeploymentLockStrategyWTimeout.cs b/NEsper.Core/NEsper.Core/client/deploy/DeploymentLockStrategyWTimeout.cs new file mode 100755 index 000000000..03d1bea13 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/deploy/DeploymentLockStrategyWTimeout.cs @@ -0,0 +1,70 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Threading; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.compat.threading; +using com.espertech.esper.util; + +namespace com.espertech.esper.client.deploy +{ + /// + /// Obtains the write lock of the engine-wide event processing read-write lock by trying the lock + /// waiting for the timeout and throwing an exception if the lock was not taken. + /// + public class DeploymentLockStrategyWTimeout : DeploymentLockStrategy + { + private TimeSpan _timeout; + private IDisposable _currentLock; + + /// + /// Ctor. + /// + /// timeout value in the unit given + public DeploymentLockStrategyWTimeout(TimeSpan timeout) + { + _timeout = timeout; + _currentLock = null; + } + + public void Acquire(IReaderWriterLock engineWideLock) + { + try + { + var newLock = engineWideLock.WriteLock.Acquire((long) _timeout.TotalMilliseconds); + // only assign the new lock if the current one is obtained... are we safe to assign + // at this point? I believe so because we have not returned control back to the + // calling application. As such, there can be no chance that the lock has been + // released by the calling thread. If it was released, its a defect because some + // other thread called release while we were in acquire. + _currentLock = newLock; + } + catch (TimeoutException) + { + throw new DeploymentLockException( + "Failed to obtain write lock of engine-wide processing read-write lock"); + } + } + + public void Release(IReaderWriterLock engineWideLock) + { + var existingLock = Interlocked.Exchange(ref _currentLock, null); + if (existingLock != null) + { + existingLock.Dispose(); + } + + //engineWideLock.WriteLock.Release(); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/client/deploy/DeploymentNotFoundException.cs b/NEsper.Core/NEsper.Core/client/deploy/DeploymentNotFoundException.cs new file mode 100755 index 000000000..6c67246ef --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/deploy/DeploymentNotFoundException.cs @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.deploy +{ + /// + /// Inner exception to available on statement level. + /// + [Serializable] + public class DeploymentNotFoundException : DeploymentException + { + /// Ctor. + /// error message + public DeploymentNotFoundException(String message) + : base(message) + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/deploy/DeploymentOptions.cs b/NEsper.Core/NEsper.Core/client/deploy/DeploymentOptions.cs new file mode 100755 index 000000000..fab652946 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/deploy/DeploymentOptions.cs @@ -0,0 +1,91 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.deploy +{ + /// + /// Options for use in deployment of a module to control the behavior of the deploy operation. + /// + [Serializable] + public class DeploymentOptions + { + public DeploymentOptions() + { + IsCompile = true; + IsFailFast = true; + IsRollbackOnFail = true; + IsCompileOnly = false; + IsolatedServiceProvider = null; + IsValidateOnly = false; + DeploymentLockStrategy = DeploymentLockStrategyDefault.INSTANCE; + } + + /// + /// Returns true (the default) to indicate that the deploy operation first performs a compile step for + /// each statement before attempting to start a statement. + /// + /// true for compile before start, false for start-only + public bool IsCompile { get; set; } + + /// + /// Returns true (the default) to indicate that the first statement to fail starting will + /// fail the complete module deployment, or set to false to indicate that the operation should attempt + /// to start all statements regardless of any failures. + /// + /// indicator + public bool IsFailFast { get; set; } + + /// + /// Returns true (the default) to indicate that the engine destroys any started statement when + /// a subsequent statement fails to start, or false if the engine should leave any started statement + /// as-is even when exceptions occur for one or more statements. + /// + /// indicator + public bool IsRollbackOnFail { get; set; } + + /// + /// Returns true to indicate to compile only and not start any statements, or false (the default) to + /// indicate that statements are started as part of the deploy. + /// + /// indicator + public bool IsCompileOnly { get; set; } + + /// + /// Returns the isolated service provider to deploy to, if specified. + /// + /// isolated service provider name + public string IsolatedServiceProvider { get; set; } + + /// + /// Returns true to validate the module syntax and EPL syntax only. Use this option to + /// not deploy any EPL statement, performing only syntax checking. + /// + /// validate flag + public bool IsValidateOnly { get; set; } + + /// + /// Returns the statement name resolver. + /// + /// statement name resolver + public StatementNameResolver StatementNameResolver { get; set; } + + /// + /// Returns the statement user object resolver. + /// + /// statement user object resolver + public StatementUserObjectResolver StatementUserObjectResolver { get; set; } + + /// + /// Return the deployment lock strategy, the default is + /// + /// lock strategy + public DeploymentLockStrategy DeploymentLockStrategy { get; set; } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/client/deploy/DeploymentOrder.cs b/NEsper.Core/NEsper.Core/client/deploy/DeploymentOrder.cs new file mode 100755 index 000000000..335b4bc82 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/deploy/DeploymentOrder.cs @@ -0,0 +1,33 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.client.deploy +{ + /// + /// Returned by the + /// operation to holds an ordered list of modules considering each module's + /// uses-dependencies on other modules. + /// + [Serializable] + public class DeploymentOrder + { + /// Cotr. + /// list of modules + public DeploymentOrder(IList ordered) + { + Ordered = ordered; + } + + /// Returns the list of modules. + /// modules + public IList Ordered { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/deploy/DeploymentOrderException.cs b/NEsper.Core/NEsper.Core/client/deploy/DeploymentOrderException.cs new file mode 100755 index 000000000..e161890e4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/deploy/DeploymentOrderException.cs @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.deploy +{ + /// + /// Exception indicates a problem when determining delpoyment order and uses-dependency checking. + /// + [Serializable] + public class DeploymentOrderException : DeploymentException + { + /// + /// Ctor. + /// + /// error message + public DeploymentOrderException(String message) + : base(message) + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/deploy/DeploymentOrderOptions.cs b/NEsper.Core/NEsper.Core/client/deploy/DeploymentOrderOptions.cs new file mode 100755 index 000000000..a6e6043c8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/deploy/DeploymentOrderOptions.cs @@ -0,0 +1,31 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.client.deploy +{ + /// + /// Options class passed to + /// for controlling the behavior of ordering and dependency checking logic. + /// + public class DeploymentOrderOptions + { + public DeploymentOrderOptions() + { + IsCheckUses = true; + IsCheckCircularDependency = true; + } + + /// Returns true (the default) to indicate that the algorithm checks for circular dependencies among the uses-dependency graph, or false to not perform this check. + /// indicator. + public bool IsCheckCircularDependency { get; set; } + + /// Returns true (the default) to cause the algorithm to check uses-dependencies ensuring all dependencies are satisfied i.e. all dependent modules are either deployed or are part of the modules passed in, or false to not perform the checking. + /// indicator + public bool IsCheckUses { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/deploy/DeploymentResult.cs b/NEsper.Core/NEsper.Core/client/deploy/DeploymentResult.cs new file mode 100755 index 000000000..b4cbcf6ae --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/deploy/DeploymentResult.cs @@ -0,0 +1,43 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.client.deploy +{ + /// + /// Result of a deployment operation carries a deployment id for use in undeploy and + /// statement-level information. + /// + public class DeploymentResult + { + /// Ctor. + /// deployment id + /// statements deployed and started + /// the imports that are part of the deployment + public DeploymentResult(String deploymentId, IList statements, IList imports) + { + DeploymentId = deploymentId; + Statements = statements; + Imports = imports; + } + + /// Returns the deployment id. + /// id + public string DeploymentId { get; private set; } + + /// Returns the statements. + /// statements + public IList Statements { get; private set; } + + /// Returns a list of imports that were declared in the deployment. + /// imports + public IList Imports { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/deploy/DeploymentState.cs b/NEsper.Core/NEsper.Core/client/deploy/DeploymentState.cs new file mode 100755 index 000000000..8906f25d1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/deploy/DeploymentState.cs @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.client.deploy +{ + /// Deployment state. + public enum DeploymentState + { + /// In undeployed state a deployment is added but not currently deployed. + UNDEPLOYED, + + /// In deployed state a deployment is added and it is deployed, i.e. has zero to many active EPL statements associated. + DEPLOYED + } +} diff --git a/NEsper.Core/NEsper.Core/client/deploy/DeploymentStateException.cs b/NEsper.Core/NEsper.Core/client/deploy/DeploymentStateException.cs new file mode 100755 index 000000000..4dade1db9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/deploy/DeploymentStateException.cs @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.deploy +{ + /// + /// Inner exception to available on statement level. + /// + public class DeploymentStateException + : DeploymentException + { + /// Ctor. + /// error message + public DeploymentStateException(String message) + : base(message) + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/deploy/EPDeploymentAdmin.cs b/NEsper.Core/NEsper.Core/client/deploy/EPDeploymentAdmin.cs new file mode 100755 index 000000000..834853d67 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/deploy/EPDeploymentAdmin.cs @@ -0,0 +1,195 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +namespace com.espertech.esper.client.deploy +{ + /// + /// Service to package and deploy EPL statements organized into an EPL module. + /// + public interface EPDeploymentAdmin + { + /// Read the input stream and return the module. It is up to the calling method to close the stream when done. + /// to read + /// uri of the module + /// module + /// IOException when the io operation failed + /// ParseException when parsing of the module failed + Module Read(Stream stream, String moduleUri); + + /// Read the resource by opening from classpath and return the module. + /// name of the classpath resource + /// module + /// IOException when the resource could not be read + /// ParseException when parsing of the module failed + Module Read(String resource); + + /// Read the module by reading the text file and return the module. + /// the file to read + /// module + /// IOException when the file could not be read + /// ParseException when parsing of the module failed + Module Read(FileInfo file); + + /// Read the module by reading from the URL provided and return the module. + /// the URL to read + /// module + /// IOException when the url input stream could not be read + /// ParseException when parsing of the module failed + Module Read(Uri url); + + /// Parse the module text passed in, returning the module. + /// to parse + /// module + /// IOException when the parser failed to read the string buffer + /// ParseException when parsing of the module failed + Module Parse(String eplModuleText); + + /// Compute a deployment order among the modules passed in considering their uses-dependency declarations and considering the already-deployed modules. The operation also checks and reports circular dependencies. Pass in @{link DeploymentOrderOptions} to customize the behavior if this method. When passing no options or passing default options, the default behavior checks uses-dependencies and circular dependencies. + /// to determine ordering for + /// operation options or null for default options + /// ordered modules + /// DeploymentOrderException when any module dependencies are not satisfied + DeploymentOrder GetDeploymentOrder(ICollection modules, DeploymentOrderOptions options); + + /// Deploy a single module returning a generated deployment id to use when undeploying statements as well as additional statement-level information. Pass in @{link DeploymentOptions} to customize the behavior. When passing no options or passing default options, the operation first compiles all EPL statements before starting each statement, fails-fast on the first statement that fails to start and rolls back (destroys) any started statement on a failure. When setting validate-only in the deployment options, the method returns a null-value on success. + /// to deploy + /// operation options or null for default options + /// result object with statement detail, or null for pass on validate-only + /// DeploymentActionException when the deployment fails, contains a list of deployment failures + DeploymentResult Deploy(Module module, DeploymentOptions options); + + /// Deploy a single module using the deployment id provided as a parameter. Pass in @{link DeploymentOptions} to customize the behavior. When passing no options or passing default options, the operation first compiles all EPL statements before starting each statement, fails-fast on the first statement that fails to start and rolls back (destroys) any started statement on a failure. When setting validate-only in the deployment options, the method returns a null-value on success. + /// to deploy + /// operation options or null for default options + /// the deployment id to assign + /// result object with statement detail, or null for pass on validate-only + /// DeploymentActionException when the deployment fails, contains a list of deployment failures + DeploymentResult Deploy(Module module, DeploymentOptions options, String assignedDeploymentId); + + /// Undeploy a single module, if its in deployed state, and removes it from the known modules. This operation destroys all statements previously associated to the deployed module and also removes this module from the list deployments list. + /// of the deployment to undeploy. + /// result object with statement-level detail + /// DeploymentNotFoundException when the deployment id could not be resolved to a deployment + UndeploymentResult UndeployRemove(String deploymentId); + + /// Undeploy a single module, if its in deployed state, and removes it from the known modules. This operation, by default, destroys all statements previously associated to the deployed module and also removes this module from the list deployments list. Use the options object to control whether statements get destroyed. + /// of the deployment to undeploy. + /// for controlling undeployment, can be a null value + /// result object with statement-level detail + /// DeploymentNotFoundException when the deployment id could not be resolved to a deployment + UndeploymentResult UndeployRemove(String deploymentId, UndeploymentOptions undeploymentOptions); + + /// Return deployment ids of all currently known modules. + /// array of deployment ids + string[] Deployments { get; } + + /// Returns the deployment information for a given deployment. + /// to return the deployment information for. + /// deployment info + DeploymentInformation GetDeployment(String deploymentId); + + /// Returns deployment information for all known modules. + /// deployment information. + DeploymentInformation[] DeploymentInformation { get; } + + /// Determine if a named module is already deployed (in deployed state), returns true if one or more modules of the same name are deployed or false when no module of that name is deployed. + /// to look up + /// indicator + bool IsDeployed(String moduleName); + + /// Shortcut method to read and deploy a single module from a classpath resource. Uses default options for performing deployment dependency checking and deployment. + /// to read + /// uri of module to assign or null if not applicable + /// archive name of module to assign or null if not applicable + /// user object to assign to module, passed along unused as part of deployment information, or null if not applicable + /// deployment result object + /// IOException when the file could not be read + /// ParseException when parsing of the module failed + /// DeploymentOrderException when any module dependencies are not satisfied + /// DeploymentActionException when the deployment fails, contains a list of deployment failures + DeploymentResult ReadDeploy(String resource, String moduleURI, String moduleArchive, Object userObject); + + /// Shortcut method to read and deploy a single module from an input stream. Uses default options for performing deployment dependency checking and deployment. Leaves the stream unclosed. + /// to read + /// uri of module to assign or null if not applicable + /// archive name of module to assign or null if not applicable + /// user object to assign to module, passed along unused as part of deployment information, or null if not applicable + /// deployment result object + /// IOException when the file could not be read + /// ParseException when parsing of the module failed + /// DeploymentOrderException when any module dependencies are not satisfied + /// DeploymentActionException when the deployment fails, contains a list of deployment failures + DeploymentResult ReadDeploy(Stream stream, String moduleURI, String moduleArchive, Object userObject); + + /// Shortcut method to parse and deploy a single module from a string text buffer. Uses default options for performing deployment dependency checking and deployment. + /// to parse + /// uri of module to assign or null if not applicable + /// archive name of module to assign or null if not applicable + /// user object to assign to module, passed along unused as part of deployment information, or null if not applicable + /// deployment result object + /// IOException when the file could not be read + /// ParseException when parsing of the module failed + /// DeploymentOrderException when any module dependencies are not satisfied + /// DeploymentActionException when the deployment fails, contains a list of deployment failures + DeploymentResult ParseDeploy(String eplModuleText, String moduleURI, String moduleArchive, Object userObject); + + /// Shortcut method to parse and deploy a single module from a string text buffer, without providing a module URI name or archive name or user object. The module URI, archive name and user object are defaulted to null. Uses default options for performing deployment dependency checking and deployment. + /// to parse + /// deployment result object + /// IOException when the file could not be read + /// ParseException when parsing of the module failed + /// DeploymentOrderException when any module dependencies are not satisfied + /// DeploymentActionException when the deployment fails, contains a list of deployment failures + DeploymentResult ParseDeploy(String eplModuleText); + + /// Adds a module in undeployed state, generating a deployment id and returning the generated deployment id of the module. + /// to add + /// The deployment id assigned to the module + String Add(Module module); + + /// Adds a module in undeployed state, using the provided deployment id as a unique identifier for the module. + /// to add + /// deployment id to assign + void Add(Module module, String assignedDeploymentId); + + /// Remove a module that is currently in undeployed state. This call may only be used on undeployed modules. + /// of the module to remove + /// DeploymentStateException when attempting to remove a module that does not exist or a module that is not in undeployed state + /// DeploymentNotFoundException if no such deployment id is known + void Remove(String deploymentId); + + /// Deploy a previously undeployed module. + /// of the module to deploy + /// deployment options + /// deployment result + /// DeploymentStateException when attempting to deploy a module that does not exist is already deployed + /// DeploymentOrderException when deployment dependencies are not satisfied + /// DeploymentActionException when the deployment (or validation when setting validate-only) failed + /// DeploymentNotFoundException if no such deployment id is known + DeploymentResult Deploy(String deploymentId, DeploymentOptions options); + + /// Undeploy a previously deployed module. + /// of the module to undeploy + /// undeployment result + /// DeploymentStateException when attempting to undeploy a module that does not exist is already undeployed + /// DeploymentNotFoundException when the deployment id could not be resolved + UndeploymentResult Undeploy(String deploymentId); + + /// Undeploy a previously deployed module. + /// of the module to undeploy + /// undeployment options, or null for default behavior + /// undeployment result + /// DeploymentStateException when attempting to undeploy a module that does not exist is already undeployed + /// DeploymentNotFoundException when the deployment id could not be resolved + UndeploymentResult Undeploy(String deploymentId, UndeploymentOptions undeploymentOptions); + } +} diff --git a/NEsper.Core/NEsper.Core/client/deploy/EngineInitializerAttribute.cs b/NEsper.Core/NEsper.Core/client/deploy/EngineInitializerAttribute.cs new file mode 100755 index 000000000..087150128 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/deploy/EngineInitializerAttribute.cs @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.deploy +{ + /// + /// For use with server environments that support dynamic engine initialization + /// (enterprise edition server), indicates that this method should be called after + /// the engine instance is initialized and the initial set of EPL statements have + /// been deployed, for example to set up listeners and subscribers. + /// + /// Apply this attribute to any method that accepts a single string parameter providing + /// the engine name. + /// + //@Retention(RetentionPolicy.RUNTIME) + //@Target(ElementType.METHOD) + public class EngineInitializerAttribute : Attribute + { + } +} diff --git a/NEsper.Core/NEsper.Core/client/deploy/Module.cs b/NEsper.Core/NEsper.Core/client/deploy/Module.cs new file mode 100755 index 000000000..5d7a22e88 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/deploy/Module.cs @@ -0,0 +1,99 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Text; + +namespace com.espertech.esper.client.deploy +{ + /// + /// Represent a deployment unit consisting of deployment declarative information + /// (module name, uses and imports) as well as EPL statements represented by + /// . May have an additional user object and archive name + /// and uri pointing to the module source attached. + /// + /// The module URI gets initialized with the filename, resource or URL being read, + /// however may be overridden and has not further meaning to the deployment. + /// + /// The archive name and user object are opportunities to attach additional deployment + /// information. + /// + [Serializable] + public class Module + { + /// Ctor. + /// module name + /// module uri + /// names of modules that this module depends on + /// the type imports + /// EPL statements + /// text of module + public Module(String name, String uri, + ICollection uses, + ICollection imports, + IList items, + String moduleText) + { + Name = name; + Uri = uri; + Uses = uses; + Imports = imports; + Items = new List(items); + ModuleText = moduleText; + } + + /// Returns the name of the archive this module originated from, or null if not applicable. + /// archive name + public string ArchiveName { get; set; } + + /// Returns the optional user object that may be attached to the module. + /// user object + public object UserObject { get; set; } + + /// Returns the module name, if provided. + /// module name + public string Name { get; set; } + + /// Returns the module URI if provided. + /// module URI + public string Uri { get; set; } + + /// Returns the dependencies the module may have on other modules. + /// module dependencies + public ICollection Uses { get; set; } + + /// Returns a list of statements (some may be comments only) that make up the module. + /// statements + public IList Items { get; set; } + + /// Returns the imports defined by the module. + /// module imports + public ICollection Imports { get; set; } + + /// Returns module text. + /// text + public string ModuleText { get; set; } + + public override String ToString() + { + var buf = new StringBuilder(); + if (Name == null) { + buf.Append("(unnamed)"); + } + else { + buf.Append("'" + Name + "'"); + } + if (Uri != null) { + buf.Append(" uri '" + Uri + "'"); + } + + return buf.ToString(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/deploy/ModuleItem.cs b/NEsper.Core/NEsper.Core/client/deploy/ModuleItem.cs new file mode 100755 index 000000000..b3106c19c --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/deploy/ModuleItem.cs @@ -0,0 +1,56 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.deploy +{ + /// + /// Represents an EPL statement as part of a . + /// + /// Character position start and end are only available for non-comment only. + /// + [Serializable] + public class ModuleItem + { + /// Ctor. + /// EPL + /// true if the statement consists only of comments or whitespace + /// line number + /// character position of start of segment + /// character position of end of segment + public ModuleItem(String expression, bool commentOnly, int lineNumber, int charPosStart, int charPosEnd) + { + Expression = expression; + IsCommentOnly = commentOnly; + LineNumber = lineNumber; + CharPosStart = charPosStart; + CharPosEnd = charPosEnd; + } + + /// Returns the EPL. + /// expression + public string Expression { get; set; } + + /// Returns true to indicate comments-only expression. + /// comments-only indicator + public bool IsCommentOnly { get; set; } + + /// Returns the line number of item. + /// item line num + public int LineNumber { get; set; } + + /// Returns item char position in line. + /// char position + public int CharPosStart { get; set; } + + /// Returns end position of character on line for the item. + /// position + public int CharPosEnd { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/deploy/ParseException.cs b/NEsper.Core/NEsper.Core/client/deploy/ParseException.cs new file mode 100755 index 000000000..b0b7bfdda --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/deploy/ParseException.cs @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.deploy +{ + /// + /// Exception thrown when an EPL text could not be parsed. + /// + [Serializable] + public class ParseException : Exception + { + /// Ctor. + /// error message + public ParseException(String message) + : base(message) + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/deploy/SingleRowFunctionAttribute.cs b/NEsper.Core/NEsper.Core/client/deploy/SingleRowFunctionAttribute.cs new file mode 100755 index 000000000..8bf415979 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/deploy/SingleRowFunctionAttribute.cs @@ -0,0 +1,30 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.deploy +{ + /// + /// For use with server environments that support dynamic engine initialization + /// (enterprise edition server), indicates that this method provide a single-row + /// function and should be registered as such so it becomes callable from EPL + /// statements using the name specified. + /// + //@Retention(RetentionPolicy.RUNTIME) + //@Target(ElementType.METHOD) + public class SingleRowFunctionAttribute : Attribute + { + /// + /// Single-row function name for use in EPL statements. + /// + /// The name. + /// function name. + public String Name { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/deploy/StatementDeploymentContext.cs b/NEsper.Core/NEsper.Core/client/deploy/StatementDeploymentContext.cs new file mode 100755 index 000000000..5d1e8c71d --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/deploy/StatementDeploymentContext.cs @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.deploy +{ + /// + /// Context object passed to or + /// to help in determining the + /// right statement name or user object for a statement deployed via the + /// deployment admin API. + /// + public class StatementDeploymentContext + { + /// Ctor. + /// EPL expression + /// encapsulating module + /// item in module + /// deployment id + public StatementDeploymentContext(String epl, Module module, ModuleItem moduleItem, String deploymentId) + { + Epl = epl; + Module = module; + ModuleItem = moduleItem; + DeploymentId = deploymentId; + } + + /// Returns the EPL expression. + /// EPL + public string Epl { get; private set; } + + /// Returns the module. + /// module + public Module Module { get; private set; } + + /// Returns the deployment id. + /// deployment id + public string DeploymentId { get; private set; } + + /// Returns the module item. + /// module item + public ModuleItem ModuleItem { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/deploy/StatementNameResolver.cs b/NEsper.Core/NEsper.Core/client/deploy/StatementNameResolver.cs new file mode 100755 index 000000000..a5073cd1a --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/deploy/StatementNameResolver.cs @@ -0,0 +1,43 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.deploy +{ + /// + /// Implement this interface to provide a custom statement name for the statements deployed + /// via the deployment API. + /// + /// Statement names provided by the resolver override the statement name provided via the @Name annotation. + /// + public interface StatementNameResolver + { + /// + /// Returns the statement name to assign to a newly-deployed statement. + /// + /// Implementations would typically interrogate the context object EPL expression or module and module + /// item information and determine the right statement name to assign. + /// + /// the statement's deployment context + /// + /// statement name or null if none needs to be assigned and the default or @Name annotated name should be used + /// + String GetStatementName(StatementDeploymentContext context); + } + + public class ProxyStatementNameResolver : StatementNameResolver + { + public Func ProcGetStatementName { get; set; } + + public string GetStatementName(StatementDeploymentContext context) + { + return ProcGetStatementName(context); + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/deploy/StatementUserObjectResolver.cs b/NEsper.Core/NEsper.Core/client/deploy/StatementUserObjectResolver.cs new file mode 100755 index 000000000..060aaebbf --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/deploy/StatementUserObjectResolver.cs @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.deploy +{ + /// + /// Implement this interface to provide a custom user object for the statements deployed via the deployment API. + /// + public interface StatementUserObjectResolver + { + /// + /// Returns the user object to assign to a newly-deployed statement. + /// + /// Implementations would typically interrogate the context object EPL expression or module and module + /// item information and determine the right user object to assign. + /// + /// the statement's deployment context + /// + /// user object or null if none needs to be assigned + /// + Object GetUserObject(StatementDeploymentContext context); + } + + public class ProxyStatementUserObjectResolver : StatementUserObjectResolver + { + public Func ProcGetUserObject { get; set; } + + public Object GetUserObject(StatementDeploymentContext context) + { + return ProcGetUserObject(context); + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/deploy/UndeploymentOptions.cs b/NEsper.Core/NEsper.Core/client/deploy/UndeploymentOptions.cs new file mode 100755 index 000000000..4b4ae7ff8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/deploy/UndeploymentOptions.cs @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.deploy +{ + /// + /// Options for use in undeployment of a module to control the behavior of the undeploy operation. + /// + [Serializable] + public class UndeploymentOptions { + public UndeploymentOptions() + { + IsDestroyStatements = true; + DeploymentLockStrategy = DeploymentLockStrategyDefault.INSTANCE; + } + + /// + /// Returns indicator whether undeploy will destroy any associated statements (true by default). + /// + /// + /// flag indicating whether undeploy also destroys associated statements + /// + public bool IsDestroyStatements { get; set; } + + /// + /// Return the deployment lock strategy, the default is + /// + /// lock strategy + public DeploymentLockStrategy DeploymentLockStrategy { get; set; } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/client/deploy/UndeploymentResult.cs b/NEsper.Core/NEsper.Core/client/deploy/UndeploymentResult.cs new file mode 100755 index 000000000..2901e85fd --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/deploy/UndeploymentResult.cs @@ -0,0 +1,36 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.client.deploy +{ + /// + /// Result object of an undeployment operation. + /// + public class UndeploymentResult + { + /// Ctor. + /// id generated by deployment operation + /// statement-level deployment information + public UndeploymentResult(String deploymentId, IList statementInfo) + { + DeploymentId = deploymentId; + StatementInfo = statementInfo; + } + + /// Returns the deployment id. + /// id + public string DeploymentId { get; private set; } + + /// Statement-level undeploy information. + /// statement INFO + public IList StatementInfo { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/hook/AggregationFunctionFactory.cs b/NEsper.Core/NEsper.Core/client/hook/AggregationFunctionFactory.cs new file mode 100755 index 000000000..bd3f65fcd --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/hook/AggregationFunctionFactory.cs @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.agg.aggregator; +using com.espertech.esper.epl.agg.service; + +namespace com.espertech.esper.client.hook +{ + /// + /// Interface to implement for factories of aggregation functions. + /// + public interface AggregationFunctionFactory + { + /// + /// Sets the EPL function name assigned to the factory. + /// + /// Name of the function. + string FunctionName { set; } + + /// + /// Implemented by plug-in aggregation functions to allow such functions to validate the type of values passed to the function at statement compile time and to generally interrogate parameter expressions. + /// + /// expression information + void Validate(AggregationValidationContext validationContext); + + /// Make a new, initalized aggregation state. + /// initialized aggregator + AggregationMethod NewAggregator(); + + /// Returns the type of the current value. + /// type of value returned by the aggregation methods + Type ValueType { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/hook/BaseCondition.cs b/NEsper.Core/NEsper.Core/client/hook/BaseCondition.cs new file mode 100755 index 000000000..d8d92e17f --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/hook/BaseCondition.cs @@ -0,0 +1,17 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.client.hook +{ + /// + /// Marker interface for conditions reported. + /// + public interface BaseCondition + { + } +} diff --git a/NEsper.Core/NEsper.Core/client/hook/ConditionHandler.cs b/NEsper.Core/NEsper.Core/client/hook/ConditionHandler.cs new file mode 100755 index 000000000..4034a550d --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/hook/ConditionHandler.cs @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.client.hook +{ + /// + /// Interface for a handler registered with an engine instance to receive reported + /// engine conditions. + /// + /// Handle the engine condition as contained in the context object passed. + /// + /// the condition information + + public delegate void ConditionHandler(ConditionHandlerContext context); +} diff --git a/NEsper.Core/NEsper.Core/client/hook/ConditionHandlerContext.cs b/NEsper.Core/NEsper.Core/client/hook/ConditionHandlerContext.cs new file mode 100755 index 000000000..0e951e438 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/hook/ConditionHandlerContext.cs @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.hook +{ + /// + /// Context provided to implementations providing + /// engine-condition-contextual information. + /// + /// Statement information pertains to the statement currently being processed when + /// the condition occured. + /// + public class ConditionHandlerContext + { + /// Ctor. + /// engine URI + /// statement name + /// statement EPL expression text + /// condition reported + public ConditionHandlerContext(String engineURI, String statementName, String epl, BaseCondition engineCondition) + { + EngineURI = engineURI; + StatementName = statementName; + Epl = epl; + EngineCondition = engineCondition; + } + + /// Returns the engine URI. + /// engine URI + public string EngineURI { get; private set; } + + /// Returns the statement name, if provided, or the statement id assigned to the statement if no name was provided. + /// statement name or id + public string StatementName { get; private set; } + + /// Returns the expression text of the statement. + /// statement. + public string Epl { get; private set; } + + /// Returns the condition reported. + /// condition reported + public BaseCondition EngineCondition { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/hook/ConditionHandlerFactory.cs b/NEsper.Core/NEsper.Core/client/hook/ConditionHandlerFactory.cs new file mode 100755 index 000000000..aad22b734 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/hook/ConditionHandlerFactory.cs @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.client.hook +{ + /// + /// Factory for engine condition handler Instance(s). + /// + /// Receives CEP engine contextual information and should return an implementation + /// of the interface. + /// + public interface ConditionHandlerFactory + { + + /// + /// Returns an exception handler instances, or null if the factory decided not + /// to contribute an exception handler. + /// + /// contains the engine URI + /// exception handler + ConditionHandler GetHandler(ConditionHandlerFactoryContext context); + } +} diff --git a/NEsper.Core/NEsper.Core/client/hook/ConditionHandlerFactoryContext.cs b/NEsper.Core/NEsper.Core/client/hook/ConditionHandlerFactoryContext.cs new file mode 100755 index 000000000..6e394657c --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/hook/ConditionHandlerFactoryContext.cs @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.hook +{ + /// + /// Context provided to implementations + /// providing engine contextual information. + public class ConditionHandlerFactoryContext + { + /// Ctor. + /// engine URI + public ConditionHandlerFactoryContext(String engineURI) + { + EngineURI = engineURI; + } + + /// Returns the engine URI. + /// engine URI + public string EngineURI { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/hook/ConditionMatchRecognizeStatesMax.cs b/NEsper.Core/NEsper.Core/client/hook/ConditionMatchRecognizeStatesMax.cs new file mode 100755 index 000000000..a14b27b57 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/hook/ConditionMatchRecognizeStatesMax.cs @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +namespace com.espertech.esper.client.hook +{ + /// + /// Indicates that on the engine level the match-recognize has reached the configured engine-wide limit at runtime. + /// + public class ConditionMatchRecognizeStatesMax : BaseCondition + { + /// + /// Ctor. + /// + /// limit reached + /// the number of state counts per statement + public ConditionMatchRecognizeStatesMax(long max, IDictionary counts) { + Max = max; + Counts = counts; + } + + /// + /// Returns the limit reached. + /// + /// limit + public long Max { get; private set; } + + /// + /// Returns the per-statement count. + /// + /// count + public IDictionary Counts { get; private set; } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/client/hook/ConditionPatternEngineSubexpressionMax.cs b/NEsper.Core/NEsper.Core/client/hook/ConditionPatternEngineSubexpressionMax.cs new file mode 100755 index 000000000..b17cdd337 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/hook/ConditionPatternEngineSubexpressionMax.cs @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.client.hook +{ + /// + /// Indicates that on the engine level the followed-by pattern operator, regardless + /// whether parameterized with a max number of sub-expressions or not, has reached + /// the configured engine-wide limit at runtime. + /// + public class ConditionPatternEngineSubexpressionMax : BaseCondition + { + /// Ctor. + /// limit reached + /// the number of subexpression counts per statement + public ConditionPatternEngineSubexpressionMax(long max, IDictionary counts) + { + Max = max; + Counts = counts; + } + + /// Returns the limit reached. + /// limit + public long Max { get; private set; } + + /// Returns the per-statement count. + /// count + public IDictionary Counts { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/hook/ConditionPatternSubexpressionMax.cs b/NEsper.Core/NEsper.Core/client/hook/ConditionPatternSubexpressionMax.cs new file mode 100755 index 000000000..7ed143685 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/hook/ConditionPatternSubexpressionMax.cs @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.client.hook +{ + /// + /// Indicates that the followed-by pattern operator, when parameterized with a + /// max number of sub-expressions, has reached that limit at runtime. + /// + public class ConditionPatternSubexpressionMax : BaseCondition + { + /// Ctor. + /// limit reached + public ConditionPatternSubexpressionMax(int max) { + Max = max; + } + + /// Returns the limit reached. + /// limit + public int Max { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/hook/EPLExpressionEvaluationContext.cs b/NEsper.Core/NEsper.Core/client/hook/EPLExpressionEvaluationContext.cs new file mode 100755 index 000000000..dd1a8cf0f --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/hook/EPLExpressionEvaluationContext.cs @@ -0,0 +1,39 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.client.hook +{ + /// + /// Provides expression evaluation context information in an expression. + /// + public class EPLExpressionEvaluationContext + { + /// + /// Initializes a new instance of the class. + /// + /// Name of the statement. + /// The context partition identifier. + /// The engine URI. + /// The statement user object. + public EPLExpressionEvaluationContext(string statementName, int contextPartitionId, string engineURI, object statementUserObject) + { + StatementName = statementName; + ContextPartitionId = contextPartitionId; + EngineURI = engineURI; + StatementUserObject = statementUserObject; + } + + public string EngineURI { get; private set; } + + public string StatementName { get; private set; } + + public int ContextPartitionId { get; private set; } + + public object StatementUserObject { get; private set; } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/client/hook/EPLMethodInvocationContext.cs b/NEsper.Core/NEsper.Core/client/hook/EPLMethodInvocationContext.cs new file mode 100755 index 000000000..8eb6e6f16 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/hook/EPLMethodInvocationContext.cs @@ -0,0 +1,85 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.hook +{ + /// + /// Invocation context for method invocations that invoke static methods or plug-in single-row functions. + /// + public class EPLMethodInvocationContext + { + /// + /// Ctor. + /// + /// the statement name + /// context partition id if using contexts, or -1 if not using context partitions + /// the engine URI + /// + /// the name of the plug-in single row function, or the method name if not a plug-in single row + /// function + /// + /// the statement user object or null if not assigned + /// event and event type services + public EPLMethodInvocationContext( + string statementName, + int contextPartitionId, + string engineURI, + string functionName, + Object statementUserObject, + EventBeanService eventBeanService) + { + StatementName = statementName; + ContextPartitionId = contextPartitionId; + EngineURI = engineURI; + FunctionName = functionName; + StatementUserObject = statementUserObject; + EventBeanService = eventBeanService; + } + + /// + /// Returns the statement name, or null if the invocation context is not associated to a specific statement and is + /// shareable between statements + /// + /// statement name or null + public string StatementName { get; private set; } + + /// + /// Returns the context partition id, or -1 if no contexts or if the invocation context is not associated to a specific + /// statement and is shareable between statements + /// + /// context partition id + public int ContextPartitionId { get; private set; } + + /// + /// Returns the engine URI + /// + /// engine URI + public string EngineURI { get; private set; } + + /// + /// Returns the function name that appears in the EPL statement. + /// + /// function name + public string FunctionName { get; private set; } + + /// + /// Returns the statement user object or null if not assigned or if the invocation context is not associated to a + /// specific statement and is shareable between statements + /// + /// statement user object or null + public object StatementUserObject { get; private set; } + + /// + /// Returns event and event type services. + /// + /// eventBeanService + public EventBeanService EventBeanService { get; private set; } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/hook/EPLScriptContext.cs b/NEsper.Core/NEsper.Core/client/hook/EPLScriptContext.cs new file mode 100755 index 000000000..ec46dd1f6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/hook/EPLScriptContext.cs @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.hook +{ + /// + /// Available when using JSR-223 scripts or MVEL, for access of script attributes. + /// + public interface EPLScriptContext + { + /// + /// Returns event and event type services + /// + /// event type and event services + EventBeanService EventBeanService { get; } + + /// + /// Set a script attributed. + /// + /// name to use + /// value to set + void SetScriptAttribute(string attribute, Object value); + + /// + /// Return a script attribute value. + /// + /// name to retrieve value for + /// attribute value or null if undefined + Object GetScriptAttribute(string attribute); + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/hook/EventBeanService.cs b/NEsper.Core/NEsper.Core/client/hook/EventBeanService.cs new file mode 100755 index 000000000..c3bad1376 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/hook/EventBeanService.cs @@ -0,0 +1,133 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Xml; +using System.Xml.Linq; + +namespace com.espertech.esper.client.hook +{ + /// + /// Services for obtaining information and constructing events. + /// + public interface EventBeanService + { + /// + /// Look up an event type by name, + /// + /// to look up + /// event type or null if not found + EventType GetEventTypeByName(string eventTypeName); + + /// + /// Construct an event bean for a given object using the class of the object to + /// determine the event type (not for Map/Object-Array/Avro/XML events) + /// + /// event underlying + /// event bean + EventBean AdapterForObject(Object theEvent); + + /// + /// Construct an event bean for a given object and given the event-type + /// + /// event underlying + /// event type (Bean only) + /// event bean + EventBean AdapterForTypedObject(Object bean, EventType eventType); + + /// + /// Construct an event bean for a given Avro GenericRecord using the event type name to look up the Avro event type + /// + /// event underlying + /// name of the Avro event type + /// event bean + EventBean AdapterForAvro(Object avroGenericDataDotRecord, string eventTypeName); + + /// + /// Construct an event bean for a given Avro GenericRecord and given the Avro-event-type + /// + /// event underlying + /// event type (Avro only) + /// event bean + EventBean AdapterForTypedAvro(Object avroGenericDataDotRecord, EventType eventType); + + /// + /// Construct an event bean for a given Map using the event type name to look up the Map event type + /// + /// event underlying + /// name of the Map event type + /// event bean + EventBean AdapterForMap(IDictionary theEvent, string eventTypeName); + + /// + /// Construct an event bean for a given Map and given the Map-event-type + /// + /// event underlying + /// event type (Map only) + /// event bean + EventBean AdapterForTypedMap(IDictionary properties, EventType eventType); + + /// + /// Construct an event bean for a given Object-Array using the event type name to look up the Object-Array event type + /// + /// event underlying + /// name of the Object-Array event type + /// event bean + EventBean AdapterForObjectArray(Object[] theEvent, string eventTypeName); + + /// + /// Construct an event bean for a given Object-Array and given the Object-Array-event-type + /// + /// event underlying + /// event type (Object-array only) + /// event bean + EventBean AdapterForTypedObjectArray(Object[] props, EventType eventType); + + /// + /// Returns an adapter for the LINQ element that exposes it's data as event + /// properties for use in statements. + /// + /// is the element to wrap + /// + /// event wrapper for document + /// + EventBean AdapterForDOM(XElement element); + + /// + /// Construct an event bean for a given XML-DOM using the node root node name to look up the XML-DOM event type + /// + /// event underlying + /// event bean + EventBean AdapterForDOM(XmlNode node); + + /// + /// Construct an event bean for a given Node and given the XML-event-type + /// + /// event underlying + /// event type (XML only) + /// event bean + EventBean AdapterForTypedDOM(XObject node, EventType eventType); + + /// + /// Construct an event bean for a given Node and given the XML-event-type + /// + /// event underlying + /// event type (XML only) + /// event bean + EventBean AdapterForTypedDOM(XmlNode node, EventType eventType); + + /// + /// Construct an event bean for a given object and event type, wherein it is assumed that the object matches the event type + /// + /// event underlying + /// event type + /// event bean + EventBean AdapterForType(Object theEvent, EventType eventType); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/client/hook/ExceptionHandler.cs b/NEsper.Core/NEsper.Core/client/hook/ExceptionHandler.cs new file mode 100755 index 000000000..0a4ca8a67 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/hook/ExceptionHandler.cs @@ -0,0 +1,30 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.client.hook +{ + /// + /// Interface for an exception handler. + /// + /// When the engine encounters an unchecked exception processing a continous-query + /// statement it allows any exception handler that is registered with the engine to + /// handle the exception, in the order any handlers are registered. + /// + /// On-demand queries as well as any exceptions thrown by static method invocations + /// or event method invocations or the API other then the sendEvent method are not + /// provided to an exception handler. + /// + /// An application may throw a runtime exception in the @handle method to cancel further + /// processing of an event against + /// statements. + /// + /// Handle the exception as contained in the context object passed. + /// + /// the exception information + public delegate void ExceptionHandler(ExceptionHandlerContext context); +} diff --git a/NEsper.Core/NEsper.Core/client/hook/ExceptionHandlerContext.cs b/NEsper.Core/NEsper.Core/client/hook/ExceptionHandlerContext.cs new file mode 100755 index 000000000..763ba2fe3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/hook/ExceptionHandlerContext.cs @@ -0,0 +1,77 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.hook +{ + /// + /// Context provided to implementations providing + /// exception-contextual information as well as the exception itself. + /// + /// Statement information pertains to the statement currently being processed when + /// the unchecked exception occured. + /// + public class ExceptionHandlerContext + { + /// Ctor. + /// engine URI + /// exception + /// statement name + /// statement EPL expression text + /// + /// + public ExceptionHandlerContext( + string engineURI, + Exception exception, + string statementName, + string epl, + ExceptionHandlerExceptionType exceptionType, + EventBean currentEvent) + { + EngineURI = engineURI; + Exception = exception; + StatementName = statementName; + Epl = epl; + ExceptionType = exceptionType; + CurrentEvent = currentEvent; + } + + /// Returns the engine URI. + /// engine URI + public string EngineURI { get; private set; } + + /// Returns the exception. + /// exception + public Exception Exception { get; private set; } + + /// Returns the statement name, if provided, or the statement id assigned to the statement if no name was provided. + /// statement name or id + public string StatementName { get; private set; } + + /// Returns the expression text of the statement. + /// statement. + public string Epl { get; private set; } + + /// + /// Gets the type of the exception. + /// + /// + /// The type of the exception. + /// + public ExceptionHandlerExceptionType ExceptionType { get; private set; } + + /// + /// Gets the current event, when available. + /// + /// + /// The current event. + /// + public EventBean CurrentEvent { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/hook/ExceptionHandlerExceptionType.cs b/NEsper.Core/NEsper.Core/client/hook/ExceptionHandlerExceptionType.cs new file mode 100755 index 000000000..19f4be5aa --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/hook/ExceptionHandlerExceptionType.cs @@ -0,0 +1,19 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.client.hook +{ + /// + /// Indicates the phase during which and exception was encountered. + /// + public enum ExceptionHandlerExceptionType + { + PROCESS, + STOP + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/client/hook/ExceptionHandlerFactory.cs b/NEsper.Core/NEsper.Core/client/hook/ExceptionHandlerFactory.cs new file mode 100755 index 000000000..a57464ed4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/hook/ExceptionHandlerFactory.cs @@ -0,0 +1,24 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.client.hook +{ + /// + /// Factory for exception handler Instance(s). + /// + /// Receives CEP engine contextual information and should return an implementation + /// of the interface. + /// + public interface ExceptionHandlerFactory + { + /// Returns an exception handler instances, or null if the factory decided not to contribute an exception handler. + /// contains the engine URI + /// exception handler + ExceptionHandler GetHandler(ExceptionHandlerFactoryContext context); + } +} diff --git a/NEsper.Core/NEsper.Core/client/hook/ExceptionHandlerFactoryContext.cs b/NEsper.Core/NEsper.Core/client/hook/ExceptionHandlerFactoryContext.cs new file mode 100755 index 000000000..c18e8d012 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/hook/ExceptionHandlerFactoryContext.cs @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.hook +{ + /// + /// Context provided to implementations + /// providing engine contextual information. + /// + public class ExceptionHandlerFactoryContext + { + /// Ctor. + /// engine URI + public ExceptionHandlerFactoryContext(String engineURI) { + EngineURI = engineURI; + } + + /// Returns the engine URI. + /// engine URI + public string EngineURI { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/hook/ObjectValueTypeWidenerFactory.cs b/NEsper.Core/NEsper.Core/client/hook/ObjectValueTypeWidenerFactory.cs new file mode 100755 index 000000000..e72f16140 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/hook/ObjectValueTypeWidenerFactory.cs @@ -0,0 +1,42 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.util; + +namespace com.espertech.esper.client.hook +{ + /// + /// For Avro use widener or transformer of object values to Avro record values + /// + public interface ObjectValueTypeWidenerFactory { + /// + /// Returns a type widener or coercer. + /// + /// Implementations can provide custom widening behavior from an object to a a widened, coerced or related object value. + /// + /// + /// Implementations should check whether an object value is assignable with or without coercion or widening. + /// + /// + /// This method can return null to use the default widening behavior. + /// + /// + /// Throw to indicate an unsupported widening or Coercion(default behavior checking still applies if no exception is thrown) + /// + /// + /// context + /// to indicate an unsupported assignment (where not already covered by the default checking) + /// coercer/widener + TypeWidener Make(ObjectValueTypeWidenerFactoryContext context); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/client/hook/ObjectValueTypeWidenerFactoryContext.cs b/NEsper.Core/NEsper.Core/client/hook/ObjectValueTypeWidenerFactoryContext.cs new file mode 100755 index 000000000..032c6029a --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/hook/ObjectValueTypeWidenerFactoryContext.cs @@ -0,0 +1,84 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; + +namespace com.espertech.esper.client.hook +{ + /// + /// For Avro types for widening objects to Avro record values, see + /// + public class ObjectValueTypeWidenerFactoryContext { + private readonly Type clazz; + private readonly string propertyName; + private readonly EventType eventType; + private readonly string statementName; + private readonly string engineURI; + + /// + /// Ctor. + /// + /// class + /// property name + /// event type + /// statement name + /// engine URI + public ObjectValueTypeWidenerFactoryContext(Type clazz, string propertyName, EventType eventType, string statementName, string engineURI) { + this.clazz = clazz; + this.propertyName = propertyName; + this.eventType = eventType; + this.statementName = statementName; + this.engineURI = engineURI; + } + + /// + /// Returns the class + /// + /// class + public Type GetClazz() { + return clazz; + } + + /// + /// Returns the property name + /// + /// property name + public string GetPropertyName() { + return propertyName; + } + + /// + /// Returns the statement name + /// + /// statement name + public string GetStatementName() { + return statementName; + } + + /// + /// Returns the engine URI + /// + /// engine URI + public string GetEngineURI() { + return engineURI; + } + + /// + /// Returns the event type + /// + /// event type + public EventType GetEventType() { + return eventType; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/client/hook/SQLColumnTypeContext.cs b/NEsper.Core/NEsper.Core/client/hook/SQLColumnTypeContext.cs new file mode 100755 index 000000000..e82b51df8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/hook/SQLColumnTypeContext.cs @@ -0,0 +1,61 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.util; + +namespace com.espertech.esper.client.hook +{ + /// + /// For use with , context of column conversion. + /// + public class SQLColumnTypeContext + { + /// Ctor. + /// database + /// sql + /// column name + /// column type + /// sql type + /// column number starting at 1 + public SQLColumnTypeContext(String db, String sql, String columnName, Type columnClassType, string columnSqlType, int columnNumber) + { + Db = db; + Sql = sql; + ColumnName = columnName; + ColumnClassType = columnClassType.GetBoxedType(); + ColumnSqlType = columnSqlType; + ColumnNumber = columnNumber; + } + + /// Get database name. + /// db name + public string Db { get; private set; } + + /// Returns sql. + /// sql + public string Sql { get; private set; } + + /// Returns column name. + /// name + public string ColumnName { get; private set; } + + /// Returns column type. + /// column type + public Type ColumnClassType { get; private set; } + + /// Returns column sql type. + /// sql type + public string ColumnSqlType { get; private set; } + + /// Returns column number starting at 1. + /// column number + public int ColumnNumber { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/hook/SQLColumnTypeConversion.cs b/NEsper.Core/NEsper.Core/client/hook/SQLColumnTypeConversion.cs new file mode 100755 index 000000000..a49bee89f --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/hook/SQLColumnTypeConversion.cs @@ -0,0 +1,46 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.hook +{ + /// + /// Implement this interface when providing a callback for SQL input parameter and column result processing for a + /// statement, converting an input parameter or converting an output column value into any other value. + /// + /// An instance of the class implementating this interface exists typically per statement that the callback has + /// been registered for by means of EPL statement annotation. + /// + public interface SQLColumnTypeConversion + { + /// + /// Return the new type of the column. To leave the type unchanged, return + /// or null. + /// + /// contains the database name, query fired, column name, column type and column number + /// type of column after conversion + Type GetColumnType(SQLColumnTypeContext context); + + /// + /// Return the new value of the column. To leave the value unchanged, + /// return . + /// + /// contains the column name, column value and column number + /// value of column after conversion + Object GetColumnValue(SQLColumnValueContext context); + + /// + /// Return the new value of the input parameter. To leave the value unchanged, + /// return . + /// + /// contains the parameter name and number + /// value of parameter after conversion + Object GetParameterValue(SQLInputParameterContext context); + } +} diff --git a/NEsper.Core/NEsper.Core/client/hook/SQLColumnValueContext.cs b/NEsper.Core/NEsper.Core/client/hook/SQLColumnValueContext.cs new file mode 100755 index 000000000..7a64b22a3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/hook/SQLColumnValueContext.cs @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Data; + +namespace com.espertech.esper.client.hook +{ + /// + /// For use with , context of column conversion. + /// Contains the columns information as well as the column result value after reading + /// the value and the result set itself for direct access, if required. + /// + /// Applications should not retain instances of this class as the engine may change + /// and reuse values here. + /// + public class SQLColumnValueContext + { + /// + /// Returns column name. + /// + /// The name of the column. + /// name + public string ColumnName { get; set; } + + /// + /// Returns column number. + /// + /// The column number. + /// column number + public int ColumnNumber { get; set; } + + /// + /// Returns column value + /// + /// The column value. + /// value + public object ColumnValue { get; set; } + + /// + /// Returns the result set. + /// + /// result set + public IDataReader ResultSet { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/hook/SQLInputParameterContext.cs b/NEsper.Core/NEsper.Core/client/hook/SQLInputParameterContext.cs new file mode 100755 index 000000000..3a5d0e182 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/hook/SQLInputParameterContext.cs @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.client.hook +{ + /// + /// For use with , context of parameter conversion. + /// + public class SQLInputParameterContext + { + /// Ctor. + public SQLInputParameterContext() + { + } + + /// Returns the parameter number. + /// number of parameter + public int ParameterNumber { get; set; } + + /// Returns the parameter value. + /// parameter value + public object ParameterValue { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/hook/SQLOutputRowConversion.cs b/NEsper.Core/NEsper.Core/client/hook/SQLOutputRowConversion.cs new file mode 100755 index 000000000..c4f408739 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/hook/SQLOutputRowConversion.cs @@ -0,0 +1,35 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.hook +{ + /// + /// Implement this interface when providing a callback for SQL row result processing for a statement, + /// converting each row's values into a PONO. + /// + /// Rows can also be skipped via this callback, determined by the implementation returning a null value + /// for a row. + /// + /// An instance of the class implementating this interface exists typically per statement that the + /// callback has been registered for by means of EPL statement annotation. + /// + public interface SQLOutputRowConversion + { + /// Return the PONO class that represents a row of the SQL query result. + /// receives the context information such as database name, query fired and types returned by query + /// class that represents a result row + Type GetOutputRowType(SQLOutputRowTypeContext context); + + /// Returns the PONO object that represents a row of the SQL query result, or null to indicate to skip this row. + /// receives row result information + /// PONO or null value to skip the row + Object GetOutputRow(SQLOutputRowValueContext context); + } +} diff --git a/NEsper.Core/NEsper.Core/client/hook/SQLOutputRowTypeContext.cs b/NEsper.Core/NEsper.Core/client/hook/SQLOutputRowTypeContext.cs new file mode 100755 index 000000000..be49d8ff1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/hook/SQLOutputRowTypeContext.cs @@ -0,0 +1,42 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.client.hook +{ + /// + /// For use with , context of row conversion. + /// + public class SQLOutputRowTypeContext + { + /// Ctor. + /// database + /// sql + /// columns and their types + public SQLOutputRowTypeContext(String db, String sql, IDictionary fields) + { + Db = db; + Sql = sql; + Fields = fields; + } + + /// Returns the database name. + /// database name + public string Db { get; private set; } + + /// Returns the sql. + /// sql + public string Sql { get; private set; } + + /// Returns the column names and types. + /// columns + public IDictionary Fields { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/hook/SQLOutputRowValueContext.cs b/NEsper.Core/NEsper.Core/client/hook/SQLOutputRowValueContext.cs new file mode 100755 index 000000000..92e0cf3f3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/hook/SQLOutputRowValueContext.cs @@ -0,0 +1,46 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.Data; + +namespace com.espertech.esper.client.hook +{ + /// + /// For use with , context of row conversion. Provides + /// row number, column values after reading the row as well as the result set itself for direct + /// access. + /// + /// Applications should not retain instances of this class as the engine may change and reuse + /// values here. + /// + public class SQLOutputRowValueContext + { + /// Return row number, the number of the current output row. + /// row number + public int RowNum { get; set; } + + /// Returns column values. + /// values for all columns + public IDictionary Values { get; set; } + + /// + /// Returns the result set. + /// + /// result set + public IDataReader ResultSet { get; set; } + } +} diff --git a/NEsper/NEsper/util/SimpleNumberCoercer.cs b/NEsper.Core/NEsper.Core/client/hook/TypeRepresentationMapper.cs old mode 100644 new mode 100755 similarity index 58% rename from NEsper/NEsper/util/SimpleNumberCoercer.cs rename to NEsper.Core/NEsper.Core/client/hook/TypeRepresentationMapper.cs index 733212ead..e22700422 --- a/NEsper/NEsper/util/SimpleNumberCoercer.cs +++ b/NEsper.Core/NEsper.Core/client/hook/TypeRepresentationMapper.cs @@ -12,18 +12,17 @@ using com.espertech.esper.compat.collections; using com.espertech.esper.compat.logging; -namespace com.espertech.esper.util +namespace com.espertech.esper.client.hook { - /// Interface for number coercion. - public interface SimpleNumberCoercer { + /// + /// For Avro schemas for mapping a given type to a given Avro schema. + /// + public interface TypeRepresentationMapper { /// - /// Coerce the given number to a previously determined type, assuming the type is a Boxed type. Allows coerce to lower resultion number. - /// Does't coerce to primitive types. + /// Return Avro schema for type information provided. /// - /// is the number to coerce to the given type - /// the numToCoerce as a value in the given result type - public Number CoerceBoxed(Number numToCoerce); - - public Type GetReturnType(); + /// type and contextual information + /// schema + Object Map(TypeRepresentationMapperContext context); } } // end of namespace diff --git a/NEsper.Core/NEsper.Core/client/hook/TypeRepresentationMapperContext.cs b/NEsper.Core/NEsper.Core/client/hook/TypeRepresentationMapperContext.cs new file mode 100755 index 000000000..ec347567d --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/hook/TypeRepresentationMapperContext.cs @@ -0,0 +1,72 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; + +namespace com.espertech.esper.client.hook +{ + /// + /// For Avro customized type mapping, use with + /// + public class TypeRepresentationMapperContext { + private readonly Type clazz; + private readonly string propertyName; + private readonly string statementName; + private readonly string engineURI; + + /// + /// Ctor + /// + /// class + /// property name + /// statement name + /// engine URI + public TypeRepresentationMapperContext(Type clazz, string propertyName, string statementName, string engineURI) { + this.clazz = clazz; + this.propertyName = propertyName; + this.statementName = statementName; + this.engineURI = engineURI; + } + + /// + /// Returns the class. + /// + /// class + public Type GetClazz() { + return clazz; + } + + /// + /// Returns the property name + /// + /// property name + public string GetPropertyName() { + return propertyName; + } + + /// + /// Returns the statement name + /// + /// statement name + public string GetStatementName() { + return statementName; + } + + /// + /// Returns the engine URI + /// + /// engine URI + public string GetEngineURI() { + return engineURI; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/client/hook/VirtualDataWindow.cs b/NEsper.Core/NEsper.Core/client/hook/VirtualDataWindow.cs new file mode 100755 index 000000000..7786aea5d --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/hook/VirtualDataWindow.cs @@ -0,0 +1,83 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.client.hook +{ + /// + /// A virtual data window exposes externally-managed data transparently as a named window + /// without the need to retain any data in memory. An instance is associated to each + /// named window that is backed by a virtual data window. + /// + public interface VirtualDataWindow : IEnumerable, IDisposable + { + /// + /// Returns the lookup strategy for use by an EPL statement to obtain data. + /// + /// This method is invoked one or more times at the time an EPL statement is created that + /// performs a subquery, join, on-action or fire-and-forget query against the virtual data window. + /// + /// The lookup strategy returned is used when the EPL statement for which it was created performs + /// a read-operation against the managed data. Multiple lookup strategies for the same EPL statement + /// are possible for join statements. + /// + /// The context object passed in is derived from an analysis of the where-clause and lists the + /// unique property names of the event type that are index fields, i.e. fields against which the + /// lookup occurs. + /// + /// The order of hash and btree properties provided by the context matches the order that lookup + /// values are provided to the lookup strategy. + /// + /// hash and binary tree (sorted access for ranges) index fields + /// + /// lookup strategy, or null to veto the statement + /// + VirtualDataWindowLookup GetLookup(VirtualDataWindowLookupContext desc); + + /// + /// Handle a management event. + /// + /// Management events indicate: + ///
  • Create/Start of an index on a virtual data window.
  • + ///
  • Stop/Dispose of an index.
  • + ///
  • Dispose of the virtual data window.
  • + ///
  • Add/Remove of a consumer to the virtual data window.
  • + ///
    + /// to handle + void HandleEvent(VirtualDataWindowEvent theEvent); + + /// + /// This method is invoked when events are inserted-into or removed-from the virtual data window. + /// + /// When a statement uses insert-into to insert events into the virtual data window the newData + /// parameter carries the inserted event. + /// + /// When a statement uses on-delete to delete events from the virtual data window the oldData + /// parameter carries the deleted event. + /// + /// When a statement uses on-merge to merge events with the virtual data window the events passed + /// depends on the action: For then-delete the oldData carries the removed event, for then-Update + /// the newData carries the after-Update event and the oldData carries the before-Update event, + /// for then-insert the newData carries the inserted event. + /// + /// When a statement uses on-Update to Update events in the virtual data window the newData carries + /// the after-Update event and the oldData parameter carries the before-Update event. + /// + /// Implement as follows to post all inserted or removed events to consuming statements: + /// context.OutputStream.Update(newData, oldData); + /// + /// For data originating from the virtual data window use the SendEvent() method with "insert-into" + /// statement to insert events. + /// + /// the insert stream + /// the remove stream + void Update(EventBean[] newData, EventBean[] oldData); + } +} diff --git a/NEsper.Core/NEsper.Core/client/hook/VirtualDataWindowContext.cs b/NEsper.Core/NEsper.Core/client/hook/VirtualDataWindowContext.cs new file mode 100755 index 000000000..a56e7845c --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/hook/VirtualDataWindowContext.cs @@ -0,0 +1,102 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.view; + +namespace com.espertech.esper.client.hook +{ + /// + /// Context for use with virtual data window factory provides contextual + /// information about the named window and the type of events held, handle for posting insert and remove streams and + /// factory for event bean instances. + /// + public class VirtualDataWindowContext + { + /// + /// Ctor. + /// + /// statement services and statement information such as statement name, statement id, EPL expression + /// the event type that the named window is declared to hold. + /// the parameters passed when declaring the named window, for example "create window ABC.my:vdw("10.0.0.1")" passes one paramater here. + /// parameter expressions passed to the virtual data window + /// factory for converting row objects to EventBean instances + /// forward the input and output stream received from the Update method here + /// the name of the named window + /// context of services + /// additional configuration + public VirtualDataWindowContext(AgentInstanceContext agentInstanceContext, + EventType eventType, + Object[] parameters, + ExprNode[] parameterExpressions, + EventBeanFactory eventFactory, + VirtualDataWindowOutStream outputStream, + String namedWindowName, + ViewFactoryContext viewFactoryContext, + Object customConfiguration) + { + AgentInstanceContext = agentInstanceContext; + EventType = eventType; + Parameters = parameters; + ParameterExpressions = parameterExpressions; + EventFactory = eventFactory; + OutputStream = outputStream; + NamedWindowName = namedWindowName; + ViewFactoryContext = viewFactoryContext; + CustomConfiguration = customConfiguration; + } + + /// Returns the statement context which holds statement information (name, expression, id) and statement-level services. + /// statement context + public StatementContext StatementContext + { + get { return AgentInstanceContext.StatementContext; } + } + + /// Returns the event type of the events held in the virtual data window as per declaration of the named window. + /// event type + public EventType EventType { get; private set; } + + /// Returns the parameters passed; for example "create window ABC.my:vdw("10.0.0.1")" passes one paramater here. + /// parameters + public object[] Parameters { get; private set; } + + /// Returns the factory for creating instances of EventBean from rows. + /// event bean factory + public EventBeanFactory EventFactory { get; private set; } + + /// Returns a handle for use to send insert and remove stream data to consuming statements. Typically use "context.getOutputStream().Update(newData, oldData);" in the Update method of the virtual data window. + /// handle for posting insert and remove stream + public VirtualDataWindowOutStream OutputStream { get; private set; } + + /// Returns the name of the named window used in connection with the virtual data window. + /// named window + public string NamedWindowName { get; private set; } + + /// Returns the expressions passed as parameters to the virtual data window. + /// parameter expressions + public ExprNode[] ParameterExpressions { get; private set; } + + /// Returns the engine services context. + /// engine services context + public ViewFactoryContext ViewFactoryContext { get; private set; } + + /// Returns any additional configuration provided. + /// additional config + public object CustomConfiguration { get; private set; } + + /// Returns the agent instance (context partition) context. + /// context + public AgentInstanceContext AgentInstanceContext { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/hook/VirtualDataWindowEvent.cs b/NEsper.Core/NEsper.Core/client/hook/VirtualDataWindowEvent.cs new file mode 100755 index 000000000..3935168e4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/hook/VirtualDataWindowEvent.cs @@ -0,0 +1,15 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.client.hook +{ + /// Base class for events related to virtual data windows. + public abstract class VirtualDataWindowEvent + { + } +} diff --git a/NEsper.Core/NEsper.Core/client/hook/VirtualDataWindowEventConsumerAdd.cs b/NEsper.Core/NEsper.Core/client/hook/VirtualDataWindowEventConsumerAdd.cs new file mode 100755 index 000000000..3a06a2089 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/hook/VirtualDataWindowEventConsumerAdd.cs @@ -0,0 +1,49 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.client.hook +{ + /// + /// Event indicating a named-window consuming statement is being added. + /// + public class VirtualDataWindowEventConsumerAdd : VirtualDataWindowEventConsumerBase + { + /// Ctor. + /// the named window name + /// an object that identifies the consumer, the same instance or the add and for the remove event + /// statement name + /// agent instance id + /// filter expressions + /// for expression evaluation + public VirtualDataWindowEventConsumerAdd( + String namedWindowName, + Object consumerObject, + String statementName, + int agentInstanceId, + ExprNode[] filterExpressions, + ExprEvaluatorContext exprEvaluatorContext) + : base(namedWindowName, consumerObject, statementName, agentInstanceId) + { + FilterExpressions = filterExpressions; + ExprEvaluatorContext = exprEvaluatorContext; + } + + /// Provides the filter expressions. Evaluate filter expressions, if any, as follows: Boolean pass = filterExpressions[...].ExprEvaluator.Evaluate(new EventBean[] {vdwEvent}, true, addEvent._exprEvaluatorContext); Filter expressions must be evaluated using the same _exprEvaluatorContext instance as provided by this event. + /// filter expression list + public ExprNode[] FilterExpressions { get; private set; } + + /// Returns the expression evaluator context for evaluating filter expressions. + /// expression evaluator context + public ExprEvaluatorContext ExprEvaluatorContext { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/hook/VirtualDataWindowEventConsumerBase.cs b/NEsper.Core/NEsper.Core/client/hook/VirtualDataWindowEventConsumerBase.cs new file mode 100755 index 000000000..6e51f19a8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/hook/VirtualDataWindowEventConsumerBase.cs @@ -0,0 +1,60 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.hook +{ + /// + /// Base class for events indicating a named-window consumer management. + /// + public abstract class VirtualDataWindowEventConsumerBase : VirtualDataWindowEvent + { + /// + /// Ctor. + /// + /// the named window name + /// an object that identifies the consumer, the same instance or the add and for the remove event + /// statement name + /// agent instance id + protected VirtualDataWindowEventConsumerBase(String namedWindowName, Object consumerObject, String statementName, int agentInstanceId) + { + NamedWindowName = namedWindowName; + ConsumerObject = consumerObject; + StatementName = statementName; + AgentInstanceId = agentInstanceId; + } + + /// + /// Returns the named window name. + /// + /// named window name + public string NamedWindowName { get; private set; } + + /// + /// Returns an object that serves as a unique identifier for the consumer, with multiple consumer + /// per statements possible. + /// + /// Upon remove the removal event contains the same consumer object. + /// + /// consumer object + public object ConsumerObject { get; private set; } + + /// + /// Returns the statement name. + /// + /// statement name + public string StatementName { get; private set; } + + /// + /// Returns the agent instance id (context partition id). + /// + /// agent instance id + public int AgentInstanceId { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/hook/VirtualDataWindowEventConsumerRemove.cs b/NEsper.Core/NEsper.Core/client/hook/VirtualDataWindowEventConsumerRemove.cs new file mode 100755 index 000000000..9708e7294 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/hook/VirtualDataWindowEventConsumerRemove.cs @@ -0,0 +1,30 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.hook +{ + /// Event indicating a named-window consuming statement is being removed. + public class VirtualDataWindowEventConsumerRemove : VirtualDataWindowEventConsumerBase + { + /// Ctor. + /// named window name + /// identifying object for consumer + /// statement name + /// agent instance id + public VirtualDataWindowEventConsumerRemove( + String namedWindowName, + Object consumerObject, + String statementName, + int agentInstanceId) + : base(namedWindowName, consumerObject, statementName, agentInstanceId) + { + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/hook/VirtualDataWindowEventStartIndex.cs b/NEsper.Core/NEsper.Core/client/hook/VirtualDataWindowEventStartIndex.cs new file mode 100755 index 000000000..dbd132a6e --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/hook/VirtualDataWindowEventStartIndex.cs @@ -0,0 +1,79 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.client.hook +{ + /// Event raised when an index gets created or started via the "create index" syntax. + public class VirtualDataWindowEventStartIndex : VirtualDataWindowEvent + { + /// + /// Ctor. + /// + /// named window name + /// index name + /// index fields + /// if set to true [is unique]. + public VirtualDataWindowEventStartIndex(String namedWindowName, + String indexName, + IList fields, + bool isUnique) + { + NamedWindowName = namedWindowName; + IndexName = indexName; + Fields = fields; + IsUnique = isUnique; + } + + /// Returns the index name. + /// index name + public string IndexName { get; private set; } + + /// Returns a list of fields that are part of the index. + /// list of index fields + public IList Fields { get; private set; } + + /// Returns the named window name. + /// named window name + public string NamedWindowName { get; private set; } + + /// + /// Gets or sets a value indicating whether the index is unique. + /// + public bool IsUnique { get; private set; } + + + #region Nested type: VDWCreateIndexField + + /// Captures virtual data window indexed field informaion. + public class VDWCreateIndexField + { + /// Ctor. + /// named window name + /// true for hash-based index, false for btree index + public VDWCreateIndexField(String name, + bool hash) + { + Name = name; + IsHash = hash; + } + + /// Name of the indexed field. + /// field name + public string Name { get; private set; } + + /// Indicate whether the index is hash or btree, true for hash. + /// index type indicator + public bool IsHash { get; private set; } + } + + #endregion + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/hook/VirtualDataWindowEventStopIndex.cs b/NEsper.Core/NEsper.Core/client/hook/VirtualDataWindowEventStopIndex.cs new file mode 100755 index 000000000..43c2def93 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/hook/VirtualDataWindowEventStopIndex.cs @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.hook +{ + /// + /// Event to indicate that for a virtual data window an exitsing index is being stopped or destroyed. + /// + public class VirtualDataWindowEventStopIndex : VirtualDataWindowEvent + { + /// Ctor. + /// named window name + /// index name + public VirtualDataWindowEventStopIndex(String namedWindowName, String indexName) { + NamedWindowName = namedWindowName; + IndexName = indexName; + } + + /// Returns the index name. + /// index name + public string IndexName { get; private set; } + + /// Returns the named window name. + /// named window name + public string NamedWindowName { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/hook/VirtualDataWindowEventStopWindow.cs b/NEsper.Core/NEsper.Core/client/hook/VirtualDataWindowEventStopWindow.cs new file mode 100755 index 000000000..6d20b038d --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/hook/VirtualDataWindowEventStopWindow.cs @@ -0,0 +1,31 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.hook +{ + /// This event is raised when a virtual data window is stopped. + public class VirtualDataWindowEventStopWindow : VirtualDataWindowEvent { + + private readonly String namedWindowName; + + /// Ctor. + /// named window name + public VirtualDataWindowEventStopWindow(String namedWindowName) { + this.namedWindowName = namedWindowName; + } + + /// Returns the named window name. + /// named window name + public string NamedWindowName + { + get { return namedWindowName; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/hook/VirtualDataWindowFactory.cs b/NEsper.Core/NEsper.Core/client/hook/VirtualDataWindowFactory.cs new file mode 100755 index 000000000..96d333ba5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/hook/VirtualDataWindowFactory.cs @@ -0,0 +1,59 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +namespace com.espertech.esper.client.hook +{ + /// + /// Factory for . + /// + /// Register an implementation of this interface with the engine before use: + /// configuration.AddPlugInVirtualDataWindow("test", "vdw", typeof(SupportVirtualDWFactory).FullName); + /// + public interface VirtualDataWindowFactory + { + /// + /// Invoked once after instantiation of the factory, exactly once per named window. + /// + /// factory context provides contextual information such as event type, named window name and parameters. + void Initialize(VirtualDataWindowFactoryContext factoryContext); + + /// + /// Invoked for each context partition (or once if not using contexts), + /// return a virtual data window to handle the specific event type, named window or paramaters + /// as provided in the context. + ///

    + /// This method is invoked for each named window instance after the initialize method. + /// If using context partitions, the method is invoked once per context partition per named window. + ///

    + ///
    + /// provides contextual information such as event type, named window name and parameters and including context partition information + /// virtual data window + VirtualDataWindow Create(VirtualDataWindowContext context); + + /// + /// Invoked to indicate the named window is destroyed. + ///

    + /// This method is invoked once per named window (and not once per context partition). + ///

    + ///

    + /// For reference, the VirtualDataWindow destroy method is called once per context partition, + /// before this method is invoked. + ///

    + ///
    + void DestroyAllContextPartitions(); + + /// + /// Return the names of properties that taken together (combined, composed, not individually) are the unique keys of a row, + /// return null if there are no unique keys that can be identified. + /// + /// set of unique key property names + ICollection UniqueKeyPropertyNames { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/hook/VirtualDataWindowFactoryContext.cs b/NEsper.Core/NEsper.Core/client/hook/VirtualDataWindowFactoryContext.cs new file mode 100755 index 000000000..211b6ed68 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/hook/VirtualDataWindowFactoryContext.cs @@ -0,0 +1,108 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.view; + +namespace com.espertech.esper.client.hook +{ + /// + /// Context for use with virtual data window factory + /// provides contextual information about the named window and the type of events held, handle + /// for posting insert and remove streams and factory for event bean instances. + /// + public class VirtualDataWindowFactoryContext + { + private readonly EventType _eventType; + private readonly Object[] _parameters; + private readonly ExprNode[] _parameterExpressions; + private readonly EventBeanFactory _eventFactory; + private readonly String _namedWindowName; + private readonly ViewFactoryContext _viewFactoryContext; + private readonly object _customConfiguration; + + /// Ctor. + /// the event type that the named window is declared to hold. + /// the parameters passed when declaring the named window, for example "create window ABC.my:vdw("10.0.0.1")" passes one paramater here. + /// factory for converting row objects to EventBean instances + /// the name of the named window + /// parameter expressions passed to the virtual data window + /// context of services + /// additional configuration + public VirtualDataWindowFactoryContext(EventType eventType, Object[] parameters, ExprNode[] parameterExpressions, EventBeanFactory eventFactory, String namedWindowName, ViewFactoryContext viewFactoryContext, object customConfiguration) + { + _eventType = eventType; + _parameters = parameters; + _parameterExpressions = parameterExpressions; + _eventFactory = eventFactory; + _namedWindowName = namedWindowName; + _viewFactoryContext = viewFactoryContext; + _customConfiguration = customConfiguration; + } + + /// Returns the event type of the events held in the virtual data window as per declaration of the named window. + /// event type + public EventType EventType + { + get { return _eventType; } + } + + /// Returns the parameters passed; for example "create window ABC.my:vdw("10.0.0.1")" passes one paramater here. + /// parameters + public object[] Parameters + { + get { return _parameters; } + } + + /// Returns the factory for creating instances of EventBean from rows. + /// event bean factory + public EventBeanFactory EventFactory + { + get { return _eventFactory; } + } + + /// Returns the name of the named window used in connection with the virtual data window. + /// named window + public string NamedWindowName + { + get { return _namedWindowName; } + } + + /// Returns the expressions passed as parameters to the virtual data window. + /// parameter expressions + public ExprNode[] ParameterExpressions + { + get { return _parameterExpressions; } + } + + /// Returns the engine services context. + /// engine services context + public ViewFactoryContext ViewFactoryContext + { + get { return _viewFactoryContext; } + } + + /// Returns any additional configuration provided. + /// additional config + public object CustomConfiguration + { + get { return _customConfiguration; } + } + + /// Returns the statement contextual information and services. + /// statement context + public StatementContext StatementContext + { + get { return _viewFactoryContext.StatementContext; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/hook/VirtualDataWindowKeyRange.cs b/NEsper.Core/NEsper.Core/client/hook/VirtualDataWindowKeyRange.cs new file mode 100755 index 000000000..059c508e6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/hook/VirtualDataWindowKeyRange.cs @@ -0,0 +1,89 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.hook +{ + /// + /// Provides a range as a start and end value, for use as a paramater to the lookup values passed to the + /// lookup method. + /// + /// Consult for information on the type of range represented (open, closed, inverted etc.) . + /// + public class VirtualDataWindowKeyRange + { + /// Ctor. + /// range start + /// range end + public VirtualDataWindowKeyRange(Object start, Object end) + { + Start = start; + End = end; + } + + /// Returns the start value of the range. + /// start value + public object Start { get; private set; } + + /// Returns the end value of the range. + /// end value + public object End { get; private set; } + + public bool Equals(VirtualDataWindowKeyRange other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return Equals(other.Start, Start) && Equals(other.End, End); + } + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + /// + /// The parameter is null. + /// + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != typeof (VirtualDataWindowKeyRange)) return false; + return Equals((VirtualDataWindowKeyRange) obj); + } + + /// + /// Returns a hash code for this instance. + /// + /// + /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. + /// + public override int GetHashCode() + { + unchecked { + return ((Start != null ? Start.GetHashCode() : 0)*397) ^ (End != null ? End.GetHashCode() : 0); + } + } + + /// + /// Returns a that represents this instance. + /// + /// + /// A that represents this instance. + /// + public override String ToString() { + return "VirtualDataWindowKeyRange{" + + "start=" + Start + + ", end=" + End + + '}'; + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/hook/VirtualDataWindowLookup.cs b/NEsper.Core/NEsper.Core/client/hook/VirtualDataWindowLookup.cs new file mode 100755 index 000000000..279169410 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/hook/VirtualDataWindowLookup.cs @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.client.hook +{ + /// Represents a lookup strategy object that an EPL statement that queries a virtual data window obtains to perform read operations into the virtual data window. An instance is associated to each EPL statement querying (join, subquery, on-action etc.) the virtual data window. Optimally an implementation returns only those rows matching the complete lookup context filtered field information.* It is legal for an implementation to return rows that are not matching lookup context filter field information. Such rows are removed by where-clause criteria, when provided. + public interface VirtualDataWindowLookup { + + /// Invoked by an EPL statement that queries a virtual data window to perform a lookup. Keys passed are the actual query lookup values. For range lookups, the key passed is an instance of . Value values follow . EventsPerStream contains the events participating in the subquery or join. It is not necessary to use eventsPerStream and the events are provided for additional information. Please consider eventsPerStream for Esper internal use. + /// lookup values + /// input events for the lookup + /// set of events + ISet Lookup(Object[] keys, EventBean[] eventsPerStream); + } +} diff --git a/NEsper.Core/NEsper.Core/client/hook/VirtualDataWindowLookupContext.cs b/NEsper.Core/NEsper.Core/client/hook/VirtualDataWindowLookupContext.cs new file mode 100755 index 000000000..1335ab68c --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/hook/VirtualDataWindowLookupContext.cs @@ -0,0 +1,91 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.client.hook +{ + /// + /// Context passed to upon obtaining a lookup strategy for use by + /// an EPL statement that queries the virtual data window. + /// + /// Represents an analysis of correlation information provided in the where-clause of the querying + /// EPL statement (join, subquery etc.). Hash-fields are always operator-equals semantics. Btree fields + /// require sorted access as the operator is always a range or + /// Relational(>, <, >=, <=) operator. + /// + /// For example, the query + /// "select * from MyVirtualDataWindow, MyTrigger where prop = trigger and prop2 between trigger1 and trigger2" + /// indicates a single hash-field "prop" and a single btree field "prop2" with a range operator. + /// + public class VirtualDataWindowLookupContext + { + /// + /// Initializes a new instance of the class. + /// + /// Name of the statement. + /// The statement id. + /// The statement annotations. + /// if set to true [fire and forget]. + /// Name of the named window. + /// The hash fields. + /// The btree fields. + public VirtualDataWindowLookupContext( + string statementName, + int statementId, + Attribute[] statementAnnotations, + bool fireAndForget, + string namedWindowName, + IList hashFields, + IList btreeFields) + { + StatementName = statementName; + StatementId = statementId; + StatementAnnotations = statementAnnotations; + IsFireAndForget = fireAndForget; + NamedWindowName = namedWindowName; + HashFields = hashFields; + BtreeFields = btreeFields; + } + + /// + /// Gets or sets the named window name. + /// + /// The name of the named window. + public string NamedWindowName { get; private set; } + + /// Returns the list of hash field descriptors. + /// hash fields + public IList HashFields { get; private set; } + + /// Returns the list of btree field descriptors. + /// btree fields + public IList BtreeFields { get; private set; } + + /// + /// Returns the statement name of the statement to be performing the lookup, or null for fire-and-forget statements. + /// + public string StatementName { get; private set; } + + /// + /// Returns the statement id of the statement to be performing the lookup, or -1 for fire-and-forget statements. + /// + public int StatementId { get; private set; } + + /// + /// Returns the statement annotations of the statement to be performing the lookup, or null for fire-and-forget statements. + /// + public Attribute[] StatementAnnotations { get; private set; } + + /// + /// Returns true for fire-and-forget queries. + /// + public bool IsFireAndForget { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/hook/VirtualDataWindowLookupFieldDesc.cs b/NEsper.Core/NEsper.Core/client/hook/VirtualDataWindowLookupFieldDesc.cs new file mode 100755 index 000000000..2e29becdd --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/hook/VirtualDataWindowLookupFieldDesc.cs @@ -0,0 +1,42 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.hook +{ + /// + /// As part of a lookup context, see , this object + /// encapsulates information about a single property in a correlated where-clause. + /// + public class VirtualDataWindowLookupFieldDesc + { + /// Ctor. + /// property name queried in where-clause + /// operator + /// lookup key type + public VirtualDataWindowLookupFieldDesc(String propertyName, VirtualDataWindowLookupOp? @operator, Type lookupValueType) + { + PropertyName = propertyName; + Operator = @operator; + LookupValueType = lookupValueType; + } + + /// Returns the property name queried in the where-clause. + /// property name. + public string PropertyName { get; private set; } + + /// Returns the type of lookup value provided. + /// lookup value type (aka. key type) + public Type LookupValueType { get; set; } + + /// Returns the operator. + /// operator + public VirtualDataWindowLookupOp? Operator { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/hook/VirtualDataWindowLookupOp.cs b/NEsper.Core/NEsper.Core/client/hook/VirtualDataWindowLookupOp.cs new file mode 100755 index 000000000..420a896f9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/hook/VirtualDataWindowLookupOp.cs @@ -0,0 +1,133 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.hook +{ + /// + /// Enumeration for indicating the type of operator for a lookup against a virtual data window, + /// see . + /// + public enum VirtualDataWindowLookupOp + { + /// Equals (=). + EQUALS, + + /// Less (<). + LESS, + + /// Less or equal (<=). + LESS_OR_EQUAL, + + /// Greater or equal (>=). + GREATER_OR_EQUAL, + + /// Greater (>). + GREATER, + + /// Range contains neither endpoint, i.e. (a,b) + RANGE_OPEN, + + /// Range contains low and high endpoint, i.e. [a,b] + RANGE_CLOSED, + + /// Range includes low endpoint but not high endpoint, i.e. [a,b) + RANGE_HALF_OPEN, + + /// Range includes high endpoint but not low endpoint, i.e. (a,b] + RANGE_HALF_CLOSED, + + /// Inverted-Range contains neither endpoint, i.e. (a,b) + NOT_RANGE_OPEN, + + /// Inverted-Range contains low and high endpoint, i.e. [a,b] + NOT_RANGE_CLOSED, + + /// Inverted-Range includes low endpoint but not high endpoint, i.e. [a,b) + NOT_RANGE_HALF_OPEN, + + /// Inverted-Range includes high endpoint but not low endpoint, i.e. (a,b] + NOT_RANGE_HALF_CLOSED + } + + public static class VirtualDataWindowLookupOpExtensions + { + public static string GetOp(this VirtualDataWindowLookupOp enumValue) + { + switch (enumValue) { + case VirtualDataWindowLookupOp.EQUALS: + return ("="); + case VirtualDataWindowLookupOp.LESS: + return ("<"); + case VirtualDataWindowLookupOp.LESS_OR_EQUAL: + return ("<="); + case VirtualDataWindowLookupOp.GREATER_OR_EQUAL: + return (">="); + case VirtualDataWindowLookupOp.GREATER: + return (">"); + case VirtualDataWindowLookupOp.RANGE_OPEN: + return ("(,)"); + case VirtualDataWindowLookupOp.RANGE_CLOSED: + return ("[,]"); + case VirtualDataWindowLookupOp.RANGE_HALF_OPEN: + return ("[,)"); + case VirtualDataWindowLookupOp.RANGE_HALF_CLOSED: + return ("(,]"); + case VirtualDataWindowLookupOp.NOT_RANGE_OPEN: + return ("-(,)"); + case VirtualDataWindowLookupOp.NOT_RANGE_CLOSED: + return ("-[,]"); + case VirtualDataWindowLookupOp.NOT_RANGE_HALF_OPEN: + return ("-[,)"); + case VirtualDataWindowLookupOp.NOT_RANGE_HALF_CLOSED: + return ("-(,]"); + default: + throw new ArgumentException("invalid value", "enumValue"); + } + } + + /// Map the operator from a string-value. + /// to map from + /// operator + /// ArgumentException if the string operator cannot be understood + public static VirtualDataWindowLookupOp FromOpString(this string stringOp) + { + switch (stringOp) { + case ("="): + return VirtualDataWindowLookupOp.EQUALS; + case ("<"): + return VirtualDataWindowLookupOp.LESS; + case ("<="): + return VirtualDataWindowLookupOp.LESS_OR_EQUAL; + case (">="): + return VirtualDataWindowLookupOp.GREATER_OR_EQUAL; + case (">"): + return VirtualDataWindowLookupOp.GREATER; + case ("(,)"): + return VirtualDataWindowLookupOp.RANGE_OPEN; + case ("[,]"): + return VirtualDataWindowLookupOp.RANGE_CLOSED; + case ("[,)"): + return VirtualDataWindowLookupOp.RANGE_HALF_OPEN; + case ("(,]"): + return VirtualDataWindowLookupOp.RANGE_HALF_CLOSED; + case ("-(,)"): + return VirtualDataWindowLookupOp.NOT_RANGE_OPEN; + case ("-[,]"): + return VirtualDataWindowLookupOp.NOT_RANGE_CLOSED; + case ("-[,)"): + return VirtualDataWindowLookupOp.NOT_RANGE_HALF_OPEN; + case ("-(,]"): + return VirtualDataWindowLookupOp.NOT_RANGE_HALF_CLOSED; + default: + throw new ArgumentException("invalid value", "stringOp"); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/hook/VirtualDataWindowOutStream.cs b/NEsper.Core/NEsper.Core/client/hook/VirtualDataWindowOutStream.cs new file mode 100755 index 000000000..e8bdc2dab --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/hook/VirtualDataWindowOutStream.cs @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.client.hook +{ + /// + /// For use with virtual data windows, handles any insert stream and remove stream events that a virtual data window may post to consuming statements. + /// + public interface VirtualDataWindowOutStream + { + /// + /// Post insert stream (new data) and remove stream (old data) events. + /// + /// insert stream, or null if no insert stream events + /// remove stream, or null if no remove stream events + void Update(EventBean[] newData, EventBean[] oldData); + } +} diff --git a/NEsper.Core/NEsper.Core/client/metric/EngineMetric.cs b/NEsper.Core/NEsper.Core/client/metric/EngineMetric.cs new file mode 100755 index 000000000..8af9d5b98 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/metric/EngineMetric.cs @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.metric +{ + /// Reports engine-level instrumentation values. + public class EngineMetric : MetricEvent + { + /// Ctor. + /// engine URI + /// engine timestamp + /// number of input events + /// number of input events since last + /// schedule depth + public EngineMetric(String engineURI, + long timestamp, + long inputCount, + long inputCountDelta, + long scheduleDepth) + : base(engineURI) + { + Timestamp = timestamp; + InputCount = inputCount; + InputCountDelta = inputCountDelta; + ScheduleDepth = scheduleDepth; + } + + /// Returns input count since engine initialization cumulative. + /// input count + public long InputCount { get; private set; } + + /// Returns schedule depth. + /// schedule depth + public long ScheduleDepth { get; private set; } + + /// Returns engine timestamp. + /// timestamp + public long Timestamp { get; private set; } + + /// Returns input count since last reporting period. + /// input count + public long InputCountDelta { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/metric/MetricEvent.cs b/NEsper.Core/NEsper.Core/client/metric/MetricEvent.cs new file mode 100755 index 000000000..96cb4ab24 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/metric/MetricEvent.cs @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.metric +{ + /// + /// Base metric event. + /// + public abstract class MetricEvent + { + /// + /// Ctor. + /// + /// the engine URI + protected MetricEvent(String engineURI) + { + EngineURI = engineURI; + } + + /// + /// Returns the engine URI. + /// + /// The engine URI. + /// uri + public string EngineURI { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/metric/StatementMetric.cs b/NEsper.Core/NEsper.Core/client/metric/StatementMetric.cs new file mode 100755 index 000000000..7cb3b2b3a --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/metric/StatementMetric.cs @@ -0,0 +1,114 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Threading; + +namespace com.espertech.esper.client.metric +{ + /// + /// Reports statement-level instrumentation values. + /// + public class StatementMetric : MetricEvent + { +#if DEBUG_STATEMENT_METRIC + private readonly Guid _id = Guid.NewGuid(); +#endif + /// + /// Gets or sets engine timestamp. + /// + public long Timestamp { get; set; } + + /// Returns statement name. + /// statement name + public String StatementName { get; private set; } + + private long _cpuTime; + private long _wallTime; + private long _outputRStreamCount; + private long _outputIStreamCount; + private long _numInput; + + /// Ctor. + /// engine URI + /// statement name + public StatementMetric(String engineURI, String statementName) + : base(engineURI) + { + StatementName = statementName; + _wallTime = 0L; + _cpuTime = 0L; + _outputIStreamCount = 0L; + _outputRStreamCount = 0L; + _numInput = 0L; + } + + public double CpuTime + { + get { return Interlocked.Read(ref _cpuTime); } + } + + public double WallTime + { + get { return Interlocked.Read(ref _wallTime); } + } + + /// Returns number of output rows in remove stream. + /// number of output rows in remove stream + public long OutputRStreamCount + { + get { return Interlocked.Read(ref _outputRStreamCount); } + } + + /// Returns number of output rows in insert stream. + /// number of output rows in insert stream + public long OutputIStreamCount + { + get { return Interlocked.Read(ref _outputIStreamCount); } + } + + /// Adds number of output rows in insert stream. + /// to add + public void AddNumOutputIStream(int numIStream) + { + Interlocked.Add(ref _outputIStreamCount, numIStream); +#if DEBUG_STATEMENT_METRIC + Console.WriteLine("{0}: Reporting num output istream {1}, {2}", _id, outputIStreamCount, numIStream); +#endif + } + + /// Adds number of output rows in remove stream. + /// to add + public void AddNumOutputRStream(int numRStream) + { + Interlocked.Add(ref _outputRStreamCount, numRStream); +#if DEBUG_STATEMENT_METRIC + Console.WriteLine("{0}: Reporting num output rstream {1}, {2}", _id, outputRStreamCount, numRStream); +#endif + } + + public void IncrementTime(long pCpuTime, long pWallTime) + { + Interlocked.Add(ref _cpuTime, pCpuTime); + Interlocked.Add(ref _wallTime, pWallTime); +#if DEBUG_STATEMENT_METRIC + Console.WriteLine("{0}: Reporting time {1}/{2}, {3}/{4}", _id, pCpuTime, cpuTime, pWallTime, wallTime); +#endif + } + + public long NumInput + { + get { return Interlocked.Read(ref _numInput); } + } + + public void AddNumInput(long numInputAdd) + { + Interlocked.Add(ref _numInput, numInputAdd); + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/scopetest/AssertProxy.cs b/NEsper.Core/NEsper.Core/client/scopetest/AssertProxy.cs new file mode 100755 index 000000000..7512bb21e --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/scopetest/AssertProxy.cs @@ -0,0 +1,77 @@ +using System; +using System.Linq; + +using com.espertech.esper.util; + +namespace com.espertech.esper.client.scopetest +{ + /// + /// Internal abstraction around the assertion process. This method can proxy its calls to NUnit, System.Diagnostics + /// or any other assertion framework. + /// + public class AssertProxy + { + /// + /// An action that is triggered when a condition fails. + /// + public static Action AssertFail { get; set; } + + /// + /// Static constructor + /// + static AssertProxy() + { + AssertFail = message => System.Diagnostics.Debug.Assert(true, message); + + // See if NUnit is loaded into the domain. If it is, then by default, we will switch to using it. + // Remember, you can change whatever you'd like about how the AssertProxy class works by simply + // changing the AssertFail property. + + var appDomain = AppDomain.CurrentDomain; + var assemblies = appDomain.GetAssemblies(); + var appTypes = assemblies.SelectMany(assembly => assembly.GetTypes()).ToArray(); + + var nunitAssertionType = TypeHelper.ResolveType("NUnit.Framework.Assert", false); + if (nunitAssertionType != null) + { + var asMethod = nunitAssertionType.GetMethod("Fail", new Type[] { typeof(string) }); + if (asMethod != null) + { + AssertFail = message => asMethod.Invoke(null, new object[] { message }); + } + } + } + + private static string SanitizeMessage(string message, string defaultMessage) + { + if (message == null) + { + return defaultMessage; + } + + return message; + } + + + public static void True(bool condition, string message = null) + { + if (!condition) + { + AssertFail(SanitizeMessage(message, "Expected true, but received false")); + } + } + + public static void False(bool condition, string message = null) + { + if (condition) + { + AssertFail(SanitizeMessage(message, "Expected false, but received true")); + } + } + + public static void Fail(string message = null) + { + AssertFail(SanitizeMessage(message, string.Empty)); + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/scopetest/EPAssertionUtil.cs b/NEsper.Core/NEsper.Core/client/scopetest/EPAssertionUtil.cs new file mode 100755 index 000000000..fb1c08fd7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/scopetest/EPAssertionUtil.cs @@ -0,0 +1,1525 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.compat.magic; +using com.espertech.esper.core.support; +using com.espertech.esper.events; + +namespace com.espertech.esper.client.scopetest +{ + /// + /// Assertion methods for event processing applications. + /// + public class EPAssertionUtil + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + /// Deep compare two 2-dimensional string arrays for the exact same length of arrays and order. + /// is the expected values + /// is the actual values + public static void AssertEqualsExactOrder(String[][] expected, String[][] actual) + { + if (CompareCount(expected, actual)) + { + return; + } + for (int i = 0; i < expected.Length; i++) + { + ScopeTestHelper.AssertTrue(Collections.AreEqual(actual[i], expected[i])); + } + } + + /// Compare two 2-dimensional arrays, and using property names for messages, against expected values. + /// array of objects + /// property names + /// expected values + public static void AssertEqualsExactOrder(Object[][] actual, String[] propertyNames, Object[][] expected) + { + if (CompareCount(expected, actual)) + { + return; + } + for (int i = 0; i < expected.Length; i++) + { + Object[] propertiesThisRow = expected[i]; + for (int j = 0; j < propertiesThisRow.Length; j++) + { + String name = propertyNames[j]; + Object value = propertiesThisRow[j]; + Object eventProp = actual[i][j]; + ScopeTestHelper.AssertEquals("Error asserting property named " + name, value, eventProp); + } + } + } + + /// Compare the collection of object arrays, and using property names for messages, against expected values. + /// colleciton of array of objects + /// property names + /// expected values + public static void AssertEqualsExactOrder(ICollection actual, String[] propertyNames, Object[][] expected) + { + Object[][] arr = actual.ToArray(); + AssertEqualsExactOrder(arr, propertyNames, expected); + } + + /// Compare the objects in the expected arrays and actual collection assuming the exact same order. + /// is the expected values + /// is the actual values + public static void AssertEqualsExactOrder(Object[] expected, ICollection actual) + { + Object[] actualArray = null; + if (actual != null) + { + actualArray = actual.UnwrapIntoArray(); + } + AssertEqualsExactOrder(expected, actualArray); + } + + /// Compare the enumerator-returned events against the expected events + /// is the expected values + /// is the actual values + public static void AssertEqualsExactOrder(EventBean[] expected, IEnumerator actual) + { + AssertEqualsExactOrder((Object[]) expected, actual); + } + + /// Compare the underlying events returned by the enumerator to the expected values. + /// is the expected values + /// is the actual values + public static void AssertEqualsExactOrderUnderlying(Object[] expected, IEnumerator actual) + { + var underlyingValues = new List(); + while (actual.MoveNext()) + { + underlyingValues.Add(actual.Current.Underlying); + } + + if (actual.MoveNext()) + { + ScopeTestHelper.Fail(); + } + + Object[] data = null; + if (underlyingValues.Count > 0) + { + data = underlyingValues.ToArray(); + } + + AssertEqualsExactOrder(expected, data); + } + + /// Comparing the underlying events to the expected events using equals-semantics. + /// is the expected values + /// is the actual values + public static void AssertEqualsExactOrderUnderlying(Object[] expected, EventBean[] actual) + { + if (CompareCount(expected, actual)) + { + return; + } + + AssertEqualsExactOrder(expected, actual.Select(theEvent => theEvent.Underlying).ToArray()); + } + + /// + /// Converts to an object collection. + /// + /// + /// The collection. + /// + public static ICollection ToObjectCollection(ICollection collection) + { + if (collection == null) + return null; + return new MagicCollection(collection); + } + + /// Compare the objects in the 2-dimension object arrays assuming the exact same order. + /// is the expected values + /// is the actual values + public static void AssertEqualsExactOrder(Object[][] expected, IList actual) + { + var transpose = ToObjectCollection(actual); + if (CompareArrayAndCollSize(expected, transpose)) + { + return; + } + + for (int i = 0; i < expected.Length; i++) + { + Object[] receivedThisRow = actual[i]; + Object[] propertiesThisRow = expected[i]; + ScopeTestHelper.AssertEquals(receivedThisRow.Length, propertiesThisRow.Length); + + for (int j = 0; j < propertiesThisRow.Length; j++) + { + Object expectedValue = propertiesThisRow[j]; + Object receivedValue = receivedThisRow[j]; + ScopeTestHelper.AssertEquals("Error asserting property", expectedValue, receivedValue); + } + } + } + + /// Compare the objects in the two object arrays assuming the exact same order. + /// is the expected values + /// is the actual values + public static void AssertEqualsExactOrder(Object[] expected, Object[] actual) + { + if (CompareCount(expected, actual)) + { + return; + } + + if ((expected == null) && (actual == null)) + { + return; + } + + if (expected == null) + { + expected = new object[0]; + } + + for (int i = 0; i < expected.Length; i++) + { + Object value = actual[i]; + Object expectedValue = expected[i]; + AssertEqualsAllowArray("Failed to assert at element " + i, expectedValue, value); + } + } + + /// Reference-equals the objects in the two object arrays assuming the exact same order. + /// is the expected values + /// is the actual values + public static void AssertSameExactOrder(Object[] expected, Object[] actual) + { + if (CompareCount(expected, actual)) + { + return; + } + for (int i = 0; i < expected.Length; i++) + { + ScopeTestHelper.AssertSame("at element " + i, expected[i], actual[i]); + } + } + + public static void AssertEqualsExactOrder(T[][] expected, T[][] actual) + { + if (CompareCount(expected, actual)) + { + return; + } + + if ((expected == null) && (actual == null)) + return; + if (expected == null) + ScopeTestHelper.Fail("expected was null; actual was not"); + if (actual == null) + ScopeTestHelper.Fail("actual was null; expected was not"); + + for (int i = 0; i < expected.Length; i++) + { + AssertEqualsExactOrder(expected[i], actual[i]); + } + } + + /// Compare the short values in the two arrays assuming the exact same order. + /// is the expected values + /// is the actual values + public static void AssertEqualsExactOrder(T[] expected, T[] actual) + { + if (CompareCount(expected, actual)) + { + return; + } + + if ((expected == null) && (actual == null)) + return; + if (expected == null) + ScopeTestHelper.Fail("expected was null; actual was not"); + if (actual == null) + ScopeTestHelper.Fail("actual was null; expected was not"); + + for (int i = 0; i < expected.Length; i++) + { + ScopeTestHelper.AssertEquals(expected[i], actual[i]); + } + } + + /// Compare the short values in the two arrays assuming the exact same order. + /// is the expected values + /// is the actual values + public static void AssertEqualsExactOrder(T[] expected, T?[] actual) where T : struct + { + if (CompareArraySize(expected, actual)) + { + return; + } + for (int i = 0; i < expected.Length; i++) + { + ScopeTestHelper.AssertNotNull(actual[i]); + ScopeTestHelper.AssertEquals(expected[i], actual[i].Value); + } + } + + /// Compare the objects returned by the enumerable to the an object array. + /// is the expected values + /// is the actual values + public static void AssertEqualsExactOrder(ICollection expected, ICollection actual) + { + if (CompareCount(expected, actual)) + { + return; + } + + foreach (var tuple in actual.Merge(expected)) + { + ScopeTestHelper.AssertEquals(tuple.A, tuple.B); + } + } + + /// Compare the objects returned by the enumerator to the an object array. + /// is the expected values + /// is the actual values + public static void AssertEqualsExactOrder(T[] expected, IEnumerator actual) + { + var values = new List(); + while (actual.MoveNext()) + { + values.Add(actual.Current); + } + + if (actual.MoveNext()) + { + ScopeTestHelper.Fail(); + } + + T[] data = null; + if (values.Count > 0) + { + data = values.ToArray(); + } + + AssertEqualsExactOrder(expected, data); + } + + /// Assert that each integer value in the expected array is contained in the actual array. + /// is the expected values + /// is the actual values + public static void AssertEqualsAnyOrder(T[] expected, ICollection actual) + { + var transposeActual = ToObjectCollection(actual); + if (CompareArrayAndCollSize(expected, transposeActual)) + { + return; + } + + foreach (T anExpected in expected) + { + if (anExpected is ICollection) + { + bool containsExpected = transposeActual.Any(i => SmartEquals(i, anExpected)); + ScopeTestHelper.AssertTrue("not found: " + ((ICollection)anExpected).Render(), containsExpected); + } + else + { + ScopeTestHelper.AssertTrue("not found: " + anExpected, actual.Contains(anExpected)); + } + } + } + + /// + /// Performs an intelligent equality test. + /// + /// + /// The value A. + /// The value B. + /// + private static bool SmartEquals(T valueA, T valueB) + { + if ((valueA is ICollection) && (valueB is ICollection)) + { + var collectionA = ((ICollection)valueA).Cast().ToList(); + var collectionB = ((ICollection)valueA).Cast().ToList(); + return Collections.AreEqual(collectionA, collectionB); + } + else + { + return Equals(valueA, valueB); + } + } + + /// Compare the two object arrays allowing any order. + /// is the expected values + /// is the actual values + public static void AssertEqualsAnyOrder(ICollection expected, ICollection actual) + { + if (CompareCount(expected, actual)) + { + return; + } + + var received = new LinkedList(actual); + foreach (T expectedObject in expected) + { + var node = received.FirstNode(value => SmartEquals(value, expectedObject)); + if (node == null) + { + AssertProxy.Fail(string.Format("missing expected value: {0}", expectedObject)); + } + else + { + received.Remove(node); + } + } + + AssertProxy.True(received.Count == 0); + } + + /// Compare the property values returned by events of both iterators with the expected values, using exact-order semantics. + /// provides events + /// provides events + /// array of property names + /// expected values + public static void AssertPropsPerRow(IEnumerator enumerator, IEnumerator safeEnumerator, String[] propertyNames, Object[][] expected) + { + AssertPropsPerRow(EnumeratorToArray(enumerator), propertyNames, expected); + AssertPropsPerRow(EnumeratorToArray(safeEnumerator), propertyNames, expected); + safeEnumerator.Dispose(); + } + + /// Compare the property values returned by events of both iterators with the expected values, using any-order semantics. + /// provides events + /// provides events + /// array of property names + /// expected values + public static void AssertPropsPerRowAnyOrder(IEnumerator enumerator, IEnumerator safeEnumerator, String[] propertyNames, Object[][] expected) + { + AssertPropsPerRowAnyOrder(EnumeratorToArray(enumerator), propertyNames, expected); + AssertPropsPerRowAnyOrder(EnumeratorToArray(safeEnumerator), propertyNames, expected); + safeEnumerator.Dispose(); + } + + /// Compare the property values returned by events of the enumerator with the expected values, using any-order semantics. + /// provides events + /// array of property names + /// expected values + public static void AssertPropsPerRowAnyOrder(IEnumerator enumerator, String[] propertyNames, Object[][] expected) + { + AssertPropsPerRowAnyOrder(EnumeratorToArray(enumerator), propertyNames, expected); + } + + /// Compare the property values returned by events of both iterators with the expected values, using exact-order semantics. + /// provides events + /// array of property names + /// expected values + public static void AssertPropsPerRow(IEnumerator enumerator, String[] propertyNames, Object[][] expected) + { + AssertPropsPerRow(EnumeratorToArray(enumerator), propertyNames, expected); + } + + /// Assert that property values of rows, wherein each row can either be Map or PONO objects, matches the expected values. + /// array of objects may contain Map and PONO events + /// property names + /// expected value + public static void AssertPropsPerRow(IList received, String[] propertyNames, Object[][] expected) + { + ScopeTestHelper.AssertEquals(received.Count, expected.Length); + for (int row = 0; row < received.Count; row++) + { + AssertProps(received[row], propertyNames, expected[row]); + } + } + + /// Compare the Map values identified by property names against expected values. + /// array of Maps, one for each row + /// property names + /// expected values + public static void AssertPropsPerRow(IDictionary[] actual, String[] propertyNames, Object[][] expected) + { + if (CompareArraySize(expected, actual)) + { + return; + } + for (int i = 0; i < expected.Length; i++) + { + Object[] propertiesThisRow = expected[i]; + for (int j = 0; j < propertiesThisRow.Length; j++) + { + String name = propertyNames[j]; + Object value = propertiesThisRow[j]; + Object eventProp = actual[i].Get(name); + ScopeTestHelper.AssertEquals("Error asserting property named " + name, value, eventProp); + } + } + } + + /// Compare the property values of events with the expected values, using exact-order semantics. + /// provides events + /// array of property names + /// expected values + public static void AssertPropsPerRow(EventBean[] received, String[] propertyNames, Object[][] expected) + { + AssertPropsPerRow(received, propertyNames, expected, ""); + } + + /// Compare the property values of events with the expected values, using exact-order semantics. + /// provides events + /// array of property names + /// expected values + /// an optional name for the stream for use in messages + public static void AssertPropsPerRow(EventBean[] actual, String[] propertyNames, Object[][] expected, String streamName) + { + if (CompareArraySize(expected, actual)) + { + return; + } + for (int i = 0; i < expected.Length; i++) + { + Object[] propertiesThisRow = expected[i]; + ScopeTestHelper.AssertEquals("Number of properties expected mismatches for row " + i, propertyNames.Length, propertiesThisRow.Length); + for (int j = 0; j < propertiesThisRow.Length; j++) + { + String name = propertyNames[j]; + Object value = propertiesThisRow[j]; + Object eventProp = actual[i].Get(name); + StringWriter writer = new StringWriter(); + writer.Write("Error asserting property named "); + writer.Write(name); + writer.Write(" for row "); + writer.Write(i); + if (streamName != null && streamName.Trim().Length != 0) + { + writer.Write(" for stream "); + writer.Write(streamName); + } + AssertEqualsAllowArray(writer.ToString(), value, eventProp); + } + } + } + + /// Compare the property values of events with the expected values, using any-order semantics. + /// provides events + /// array of property names + /// expected values + public static void AssertPropsPerRowAnyOrder(EventBean[] actual, String[] propertyNames, Object[][] expected) + { + if (CompareArraySize(expected, actual)) + { + return; + } + + // build expected + var expectedArray = new Object[expected.Length]; + Array.Copy(expected, 0, expectedArray, 0, expectedArray.Length); + + // build received + var receivedArray = new Object[actual.Length]; + for (int i = 0; i < actual.Length; i++) + { + var data = new Object[propertyNames.Length]; + receivedArray[i] = data; + for (int j = 0; j < propertyNames.Length; j++) + { + var name = propertyNames[j]; + var eventProp = actual[i].Get(name); + data[j] = eventProp; + } + } + + AssertEqualsAnyOrder(expectedArray, receivedArray); + } + + public static void AssertPropsPerRowAnyOrder(UniformPair pair, String[] propertyNames, Object[][] expectedNew, Object[][] expectedOld) + { + AssertPropsPerRowAnyOrder(pair.First, propertyNames, expectedNew); + AssertPropsPerRowAnyOrder(pair.Second, propertyNames, expectedOld); + } + + /// Assert that the property values of a single event match the expected values. + /// provides events + /// array of property names + /// expected values + public static void AssertProps(EventBean received, String[] propertyNames, Object[] expected) + { + if (CompareCount(expected, propertyNames)) + { + return; + } + + for (int j = 0; j < expected.Length; j++) + { + String name = propertyNames[j].Trim(); + Object value = expected[j]; + Object eventProp = received.Get(name); + AssertEqualsAllowArray("Failed to assert property '" + name + "'", value, eventProp); + } + } + + /// Assert that the property values of a new event and a removed event match the expected insert and removed values. + /// provides events + /// array of property names + /// expected values insert stream + /// expected values remove stream + public static void AssertProps(UniformPair received, String[] propertyNames, Object[] expectedInsert, Object[] expectedRemoved) + { + AssertProps(received.First, propertyNames, expectedInsert); + AssertProps(received.Second, propertyNames, expectedRemoved); + } + + /// Assert that the property values of a new event and a removed event match the expected insert and removed values. + /// provides events + /// array of property names + /// expected values insert stream + /// expected values remove stream + public static void AssertPropsPerRow(UniformPair received, String[] propertyNames, Object[][] expectedInsert, Object[][] expectedRemoved) + { + AssertPropsPerRow(received.First, propertyNames, expectedInsert); + AssertPropsPerRow(received.Second, propertyNames, expectedRemoved); + } + + /// Assert that the property values of the events (insert and remove pair) match the expected insert and removed values for a single property. + /// provides events + /// property name + /// expected values insert stream + /// expected values remove stream + public static void AssertPropsPerRow(UniformPair received, String propertyName, Object[] expectedInsert, Object[] expectedRemoved) + { + Object[] propsInsert = EventsToObjectArr(received.First, propertyName); + AssertEqualsExactOrder(expectedInsert, propsInsert); + + Object[] propsRemove = EventsToObjectArr(received.Second, propertyName); + AssertEqualsExactOrder(expectedRemoved, propsRemove); + } + + /// Assert that the underlying objects of the events (insert and remove pair) match the expected insert and removed objects. + /// provides events + /// expected underlying object insert stream + /// expected underlying object remove stream + public static void AssertUnderlyingPerRow(UniformPair received, Object[] expectedUnderlyingInsert, Object[] expectedUnderlyingRemove) + { + EventBean[] newEvents = received.First; + EventBean[] oldEvents = received.Second; + + if (expectedUnderlyingInsert != null) + { + ScopeTestHelper.AssertEquals(expectedUnderlyingInsert.Length, newEvents.Length); + for (int i = 0; i < expectedUnderlyingInsert.Length; i++) + { + ScopeTestHelper.AssertSame(expectedUnderlyingInsert[i], newEvents[i].Underlying); + } + } + else + { + ScopeTestHelper.AssertNull(newEvents); + } + + if (expectedUnderlyingRemove != null) + { + ScopeTestHelper.AssertEquals(expectedUnderlyingRemove.Length, oldEvents.Length); + for (int i = 0; i < expectedUnderlyingRemove.Length; i++) + { + ScopeTestHelper.AssertSame(expectedUnderlyingRemove[i], oldEvents[i].Underlying); + } + } + else + { + ScopeTestHelper.AssertNull(oldEvents); + } + } + + /// Asserts that the property values of a single event, using property names as provided by the event type in sorted order by property name, match against the expected values. + /// provides events + /// expected values + public static void AssertAllPropsSortedByName(EventBean received, Object[] expected) + { + if (expected == null) + { + if (received == null) + { + return; + } + } + else + { + ScopeTestHelper.AssertNotNull(received); + } + + if (expected != null) + { + var propertyNames = received.EventType.PropertyNames.ToArray(); + var propertyNamesSorted = new String[propertyNames.Length]; + Array.Copy(propertyNames, 0, propertyNamesSorted, 0, propertyNames.Length); + Array.Sort(propertyNamesSorted); + + for (int j = 0; j < expected.Length; j++) + { + var name = propertyNamesSorted[j].Trim(); + var value = expected[j]; + var eventProp = received.Get(name); + ScopeTestHelper.AssertEquals("Error asserting property named '" + name + "'", value, eventProp); + } + } + } + + public static void AssertPropsMap(IDictionary received, String[] propertyNames, params Object[] expected) + { + if (expected == null) + { + if (received == null) + { + return; + } + } + else + { + ScopeTestHelper.AssertNotNull(received); + ScopeTestHelper.AssertEquals("Mismatch in number of values to compare", expected.Length, propertyNames.Length); + } + + if (expected != null) + { + for (int j = 0; j < expected.Length; j++) + { + String name = propertyNames[j].Trim(); + Object value = expected[j]; + Object eventProp = received.Get(name); + AssertEqualsAllowArray("Error asserting property named '" + name + "'", value, eventProp); + } + } + } + + /// Compare the values of a Map against the expected values. + /// provides events + /// expected values + /// property names to assert + public static void AssertPropsMap(IDictionary received, String[] propertyNames, params Object[] expected) + { + if (expected == null) + { + if (received == null) + { + return; + } + } + else + { + ScopeTestHelper.AssertNotNull(received); + ScopeTestHelper.AssertEquals("Mismatch in number of values to compare", expected.Length, propertyNames.Length); + } + + if (expected != null) + { + for (int j = 0; j < expected.Length; j++) + { + String name = propertyNames[j].Trim(); + Object value = expected[j]; + Object eventProp = received.Get(name); + AssertEqualsAllowArray("Error asserting property named '" + name + "'", value, eventProp); + } + } + } + + /// Compare the values of a object array (single row) against the expected values. + /// provides properties + /// expected values + /// property names to assert + public static void AssertPropsObjectArray(Object[] received, String[] propertyNames, params object[] expected) + { + if (expected == null) + { + if (received == null) + { + return; + } + } + else { + ScopeTestHelper.AssertNotNull(received); + } + + if (expected != null) { + for (int j = 0; j < expected.Length; j++) + { + String name = propertyNames[j].Trim(); + Object value = expected[j]; + Object eventProp = received[j]; + ScopeTestHelper.AssertEquals("Error asserting property named '" + name + "'", value, eventProp); + } + } + } + + /// Compare the properties of an object against the expected values. + /// property names + /// provides events + /// expected values + public static void AssertPropsPONO(Object received, String[] propertyNames, params Object[] expected) + { + if (received == null) + { + throw new ArgumentException("No object provided to compare to"); + } + var theEvent = GetEventAdapterService().AdapterForObject(received); + AssertProps(theEvent, propertyNames, expected); + } + + /// Compare two 2-dimensional event arrays. + /// expected values + /// actual values + public static void AssertEqualsAnyOrder(EventBean[][] expected, EventBean[][] actual) + { + if (CompareCount(expected, actual)) + { + return; + } + + // For each expected object find a received object + var numMatches = 0; + var foundReceived = new bool[actual.Length]; + foreach (EventBean[] expectedObject in expected) + { + bool found = false; + for (int i = 0; i < actual.Length; i++) + { + // Ignore found received objects + if (foundReceived[i]) + { + continue; + } + + bool match = CompareEqualsExactOrder(actual[i], expectedObject); + if (match) + { + found = true; + numMatches++; + foundReceived[i] = true; + break; + } + } + + if (!found) + { + Log.Error(".assertEqualsAnyOrder Not found in received results is expected=" + CompatExtensions.Render(expectedObject)); + Log.Error(".assertEqualsAnyOrder received=" + CompatExtensions.Render(actual)); + } + + ScopeTestHelper.AssertTrue("Failed to find value " + expectedObject + ", check the error logs", found); + } + + // Must have matched exactly the number of objects times + ScopeTestHelper.AssertEquals(numMatches, expected.Length); + } + + /// Compare two 2-dimensional object arrays using reference-equals semantics. + /// expected values + /// actual values + public static void AssertSameAnyOrder(Object[][] expected, Object[][] actual) + { + if (CompareCount(expected, actual)) + { + return; + } + + // For each expected object find a received object + var numMatches = 0; + var foundReceived = new bool[actual.Length]; + foreach (Object[] expectedArr in expected) + { + bool found = false; + for (int i = 0; i < actual.Length; i++) + { + // Ignore found received objects + if (foundReceived[i]) + { + continue; + } + + bool match = CompareRefExactOrder(actual[i], expectedArr); + if (match) + { + found = true; + numMatches++; + // Blank out received object so as to not match again + foundReceived[i] = true; + break; + } + } + + if (!found) + { + Log.Error(".assertEqualsAnyOrder Not found in received results is expected=" + CompatExtensions.Render(expectedArr)); + for (int j = 0; j < actual.Length; j++) + { + Log.Error(".assertEqualsAnyOrder received (" + j + "):" + CompatExtensions.Render(actual[j])); + } + ScopeTestHelper.Fail(); + } + } + + // Must have matched exactly the number of objects times + ScopeTestHelper.AssertEquals(numMatches, expected.Length); + } + + /// Asserts that all values in the given object array are bool-typed values and are true + /// values to assert that they are all true + public static void AssertAllBooleanTrue(Object[] objects) + { + foreach (Object @object in objects) + { + ScopeTestHelper.AssertTrue(@object.AsBoolean()); + } + } + + /// Assert the class of the objects in the object array matches the expected classes in the classes array. + /// is the expected class + /// is the objects to check the class for + public static void AssertTypeEqualsAnyOrder(Type[] classes, Object[] objects) + { + ScopeTestHelper.AssertEquals(classes.Length, objects.Length); + var resultClasses = new Type[objects.Length]; + for (int i = 0; i < objects.Length; i++) + { + resultClasses[i] = objects[i].GetType(); + } + AssertEqualsAnyOrder(resultClasses, classes); + } + + /// Convert an enumerator of event beans to an array of event beans. + /// to convert + /// array of events + public static EventBean[] EnumeratorToArray(IEnumerator enumerator) + { + if (enumerator == null) + { + ScopeTestHelper.Fail("Null enumerator"); + return null; + } + var events = new List(); + for (; enumerator.MoveNext(); ) + { + events.Add(enumerator.Current); + } + return events.ToArray(); + } + + /// Convert an enumerator of event beans to an array of underlying objects. + /// to convert + /// array of event underlying objects + public static Object[] EnumeratorToArrayUnderlying(IEnumerator enumerator) + { + var events = new List(); + for (; enumerator.MoveNext(); ) + { + events.Add(enumerator.Current.Underlying); + } + return events.ToArray(); + } + + /// + /// Count the number of object provided by an enumerator. + /// + /// + /// to count + /// + /// count + /// + public static int EnumeratorCount(IEnumerator enumerator) + { + int count = 0; + for (; enumerator.MoveNext(); ) + { + count++; + } + return count; + } + + /// Compare properties of events against a list of maps. + /// actual events + /// expected values + public static void AssertPropsPerRow(EventBean[] received, IList> expected) + { + if ((expected == null) && (received == null)) + { + return; + } + if (expected == null || received == null) + { + ScopeTestHelper.Fail(); + } + else + { + ScopeTestHelper.AssertEquals(expected.Count, received.Length); + for (int i = 0; i < expected.Count; i++) + { + AssertProps(received[i], expected[i]); + } + } + } + + /// Compare properties of events against a list of maps. + /// actual events + /// expected values + public static void AssertPropsPerRow(IEnumerator enumerator, IList> expected) + { + var values = new List(); + while (enumerator.MoveNext()) + { + values.Add(enumerator.Current); + } + + if (enumerator.MoveNext()) + { + ScopeTestHelper.Fail(); + } + + EventBean[] data = null; + if (values.Count > 0) + { + data = values.ToArray(); + } + + AssertPropsPerRow(data, expected); + } + + /// Concatenate two arrays. + /// array to concatenate + /// array to concatenate + /// concatenated array + public static Object[] ConcatenateArray(Object[] srcOne, Object[] srcTwo) + { + var result = new Object[srcOne.Length + srcTwo.Length]; + Array.Copy(srcOne, 0, result, 0, srcOne.Length); + Array.Copy(srcTwo, 0, result, srcOne.Length, srcTwo.Length); + return result; + } + + /// Concatenate two arrays. + /// array to concatenate + /// array to concatenate + /// concatenated array + public static Object[][] ConcatenateArray2Dim(Object[][] first, params Object[][][] more) + { + int len = first.Length; + for (int i = 0; i < more.Length; i++) + { + Object[][] next = more[i]; + len += next.Length; + } + + var result = new Object[len][]; + int count = 0; + for (int i = 0; i < first.Length; i++) + { + result[count] = first[i]; + count++; + } + + for (int i = 0; i < more.Length; i++) + { + Object[][] next = more[i]; + for (int j = 0; j < next.Length; j++) + { + result[count] = next[j]; + count++; + } + } + + return result; + } + + /// Concatenate multiple arrays. + /// arrays to concatenate + /// concatenated array + public static Object[] ConcatenateArray(params Object[][] more) + { + var list = new List(); + for (int i = 0; i < more.Length; i++) + { + for (int j = 0; j < more[i].Length; j++) + { + list.Add(more[i][j]); + } + } + return list.ToArray(); + } + + /// Sort events according to natural ordering of the values or a property. + /// to sort + /// name of property providing sort values + /// sorted array + public static EventBean[] Sort(IEnumerator events, String property) + { + return Sort(EnumeratorToArray(events), property); + } + + /// Sort events according to natural ordering of the values or a property. + /// to sort + /// name of property providing sort values + /// sorted array + public static EventBean[] Sort(EventBean[] events, String property) + { + var list = new List(events); + var standardComparer = new StandardComparer( + (o1, o2) => + { + var val1 = (IComparable)o1.Get(property); + var val2 = (IComparable)o2.Get(property); + return val1.CompareTo(val2); + }); + + list.Sort(standardComparer); + return list.ToArray(); + } + + public static void AssertNotContains(ICollection stringSet, params String[] values) + { + ICollection set = new HashSet(stringSet); + foreach (String value in values) + { + ScopeTestHelper.AssertFalse(set.Contains(value)); + } + } + + /// Assert that a string set does not contain one or more values. + /// to compare against + /// to find + public static void AssertNotContains(String[] stringSet, params String[] values) + { + ICollection set = new HashSet(stringSet); + foreach (String value in values) + { + ScopeTestHelper.AssertFalse(set.Contains(value)); + } + } + + /// Assert that a string set does contain each of one or more values. + /// to compare against + /// to find + public static void AssertContains(ICollection stringSet, params String[] values) + { + ICollection set = new HashSet(stringSet); + foreach (String value in values) + { + ScopeTestHelper.AssertTrue(set.Contains(value)); + } + } + + /// Assert that a string set does contain each of one or more values. + /// to compare against + /// to find + public static void AssertContains(String[] stringSet, params String[] values) + { + ICollection set = new HashSet(stringSet); + foreach (String value in values) + { + ScopeTestHelper.AssertTrue(set.Contains(value)); + } + } + + /// Return an array of underlying objects for an array of events. + /// to return underlying objects + /// events + public static Object[] GetUnderlying(EventBean[] events) + { + var arr = new Object[events.Length]; + for (int i = 0; i < events.Length; i++) + { + arr[i] = events[i].Underlying; + } + return arr; + } + + /// Assert that all properties of an event have the same value as passed in. + /// to inspect + /// property names + /// value + public static void AssertPropsAllValuesSame(EventBean received, String[] propertyNames, Object expected) + { + foreach (String field in propertyNames) + { + ScopeTestHelper.AssertEquals("Field " + field, expected, received.Get(field)); + } + } + + /// Extract the property value of the event property for the given events and return an object array of values. + /// to extract value from + /// name of property to extract values for + /// value object array + public static Object[] EventsToObjectArr(EventBean[] events, String propertyName) + { + if (events == null) + { + return null; + } + var objects = new Object[events.Length]; + for (int i = 0; i < events.Length; i++) + { + objects[i] = events[i].Get(propertyName); + } + return objects; + } + + /// Extract the property value of the event properties for the given events and return an object array of values. + /// to extract value from + /// names of properties to extract values for + /// value object array + public static Object[][] EventsToObjectArr(EventBean[] events, String[] propertyNames) + { + if (events == null) + { + return null; + } + + return events + .Select(ev => propertyNames.Select(ev.Get).ToArray()) + .ToArray(); + } + + /// Extract the property value of the event property for the given events and return an object array of values. + /// events to extract value from + /// name of property to extract values for + /// value object array + public static Object[] EnumeratorToObjectArr(IEnumerator enumerator, String propertyName) + { + if (enumerator == null) + { + return null; + } + return EventsToObjectArr(EnumeratorToArray(enumerator), propertyName); + } + + /// Extract the property value of the event properties for the given events and return an object array of values. + /// events to extract value from + /// names of properties to extract values for + /// value object array + public static Object[][] EnumeratorToObjectArr(IEnumerator enumerator, String[] propertyNames) + { + if (enumerator == null) + { + return null; + } + return EventsToObjectArr(EnumeratorToArray(enumerator), propertyNames); + } + + /// Compare the events in the two object arrays assuming the exact same order. + /// is the actual results + /// is the expected values + /// indicate whether compared successfully + public static bool CompareEqualsExactOrder(EventBean[] actual, EventBean[] expected) + { + if ((expected == null) && (actual == null)) + { + return true; + } + if (expected == null || actual == null) + { + return false; + } + + if (expected.Length != actual.Length) + { + return false; + } + + for (int i = 0; i < expected.Length; i++) + { + if ((actual[i] == null) && (expected[i] == null)) + { + continue; + } + + if (!actual[i].Equals(expected[i])) + { + return false; + } + } + return true; + } + + /// Reference-compare the objects in the two object arrays assuming the exact same order. + /// is the actual results + /// is the expected values + /// indicate whether compared successfully + public static bool CompareRefExactOrder(Object[] actual, Object[] expected) + { + if ((expected == null) && (actual == null)) + { + return true; + } + if (expected == null || actual == null) + { + return false; + } + + if (expected.Length != actual.Length) + { + return false; + } + + for (int i = 0; i < expected.Length; i++) + { + if (expected[i] != actual[i]) + { + return false; + } + } + + return true; + } + + /// Assert that property values of rows, wherein each row can either be Map or PONO objects, matches the expected values. + /// array of objects may contain Map and PONO events + /// property names + /// expected value + public static void AssertPropsPerRow(Object[] received, String[] propertyNames, Object[][] expected) + { + ScopeTestHelper.AssertEquals(received.Length, expected.Length); + for (int row = 0; row < received.Length; row++) + { + AssertProps(received[row], propertyNames, expected[row]); + } + } + + /// Assert that property values, wherein the row can either be a Map or a PONO object, matches the expected values. + /// Map or PONO + /// property names + /// expected value + public static void AssertProps(Object received, String[] propertyNames, Object[] expected) + { + if (received is IDictionary) + { + AssertPropsMap((IDictionary)received, propertyNames, expected); + } + else if (received is Object[]) + { + AssertPropsObjectArray((Object[]) received, propertyNames, expected); + } + else if (received is EventBean) + { + AssertProps((EventBean)received, propertyNames, expected); + } + else + { + AssertPropsPONO(received, propertyNames, expected); + } + } + + /// For a given array, copy the array elements into a new array of Object[] type. + /// input array + /// object array + public static Object[] ToObjectArray(Object array) + { + var asArray = array as Array; + if (asArray == null) + { + throw new ArgumentException("Object not an array but type '" + (array == null ? "null" : array.GetType().FullName) + "'"); + } + var size = asArray.Length; + var val = new Object[size]; + for (int i = 0; i < size; i++) + { + val[i] = asArray.GetValue(i); + } + return val; + } + + /// Assert that two property values are the same, allowing arrays as properties. + /// to use + /// expected value + /// actual value + public static void AssertEqualsAllowArray(String message, Object expected, Object actual) + { + if ((expected != null) && (expected.GetType().IsArray) && (actual != null) && (actual.GetType().IsArray)) + { + Object[] valueArray = ToObjectArray(expected); + Object[] eventPropArray = ToObjectArray(actual); + AssertEqualsExactOrder(valueArray, eventPropArray); + return; + } + ScopeTestHelper.AssertEquals(message, expected, actual); + } + + private static EventAdapterService GetEventAdapterService() + { + return SupportEventAdapterService.Service; + } + + /// Assert that the event properties of the event match the properties provided by the map, taking the map properties as the comparison source. + /// event + /// expected values + public static void AssertProps(EventBean received, IDictionary expected) + { + foreach (KeyValuePair entry in expected) + { + Object valueExpected = entry.Value; + Object property = received.Get(entry.Key); + + ScopeTestHelper.AssertEquals(valueExpected, property); + } + } + + private static bool CompareArrayAndCollSize(Object expected, ICollection actual) + { + if (expected == null && (actual == null || actual.Count == 0)) + { + return true; + } + if (expected == null || actual == null) + { + if (expected == null) + { + ScopeTestHelper.AssertNull("Expected is null but actual is not null", actual); + } + ScopeTestHelper.AssertNull("Actual is null but expected is not null", expected); + } + else + { + var expectedArray = expected as Array; + int expectedLength = expectedArray.Length; + int actualLength = actual.Count; + ScopeTestHelper.AssertEquals("Mismatch in the number of expected and actual length", expectedLength, actualLength); + } + return false; + } + + private static bool CompareCount(ICollection expected, ICollection actual) + { + if ((expected == null) && (actual == null)) + return false; + if ((expected == null)) + ScopeTestHelper.AssertNull("Expected is null but actual is not null", actual); + if ((actual == null)) + ScopeTestHelper.AssertNull("Actual is null but expected is not null", expected); + + int expectedLength = expected.Count; + int actualLength = actual.Count; + ScopeTestHelper.AssertEquals("Mismatch in the number of expected and actual length", expectedLength, actualLength); + + return false; + } + + private static bool CompareArraySize(Object expected, Object actual) + { + var actualArray = actual as Array; + var expectedArray = expected as Array; + + if ((expectedArray == null) && (actualArray == null || actualArray.Length == 0)) + { + return true; + } + if (expected == null || actual == null) + { + if (expected == null) + { + ScopeTestHelper.AssertNull("Expected is null but actual is not null", actual); + } + ScopeTestHelper.AssertNull("Actual is null but expected is not null", expected); + } + else + { + int expectedLength = expectedArray.Length; + int actualLength = actualArray.Length; + ScopeTestHelper.AssertEquals("Mismatch in the number of expected and actual number of values asserted", expectedLength, actualLength); + } + return false; + } + + + /// Compare two strings removing all NewLine characters. + /// expected value + /// received value + public static void AssertEqualsIgnoreNewline(String expected, String received) + { + String expectedClean = RemoveNewline(expected); + String receivedClean = RemoveNewline(received); + if (expectedClean != receivedClean) + { + Log.Error("Expected: " + expectedClean); + Log.Error("Received: " + receivedClean); + ScopeTestHelper.AssertEquals("Mismatch ", expected, received); + } + } + + /// Assert that a map of collections (IDictionary<String, ICollection>) has expected keys and values. + /// of string keys and collection-type values + /// array of key values + /// for each key a string that is a comma-separated list of values + /// the function to apply to each collection value to convert to a string + public static void AssertMapOfCollection(IDictionary map, String[] keys, String[] expectedList, AssertionCollectionValueString collectionValue) + { + ScopeTestHelper.AssertEquals(expectedList.Length, keys.Length); + if (keys.Length == 0 && map.IsEmpty()) + { + return; + } + + ScopeTestHelper.AssertEquals(map.Count, keys.Length); + + for (int i = 0; i < keys.Length; i++) + { + var value = (ICollection) map.Get(keys[i]); + String[] itemsExpected = expectedList[i].Split(','); + ScopeTestHelper.AssertEquals(itemsExpected.Length, value.Count); + + IEnumerator it = value.GetEnumerator(); + for (int j = 0; j < itemsExpected.Length; j++) + { + ScopeTestHelper.AssertTrue(it.MoveNext()); + String received = collectionValue.ExtractValue(it.Current); + ScopeTestHelper.AssertEquals(itemsExpected[j], received); + } + } + } + + private static String RemoveNewline(String raw) + { + raw = raw.Replace("\t", ""); + raw = raw.Replace("\n", ""); + raw = raw.Replace("\r", ""); + return raw; + } + + /// Callback for extracting individual collection items for assertion. + public interface AssertionCollectionValueString + { + + /// Extract value. + /// to extract from + /// extracted value + String ExtractValue(Object collectionItem); + } + + public class ProxyAssertionCollectionValueString : AssertionCollectionValueString + { + public Func ProcExtractValue { get; set; } + + public ProxyAssertionCollectionValueString(Func procExtractValue) + { + ProcExtractValue = procExtractValue; + } + + public ProxyAssertionCollectionValueString() + { + } + + public string ExtractValue(object collectionItem) + { + return ProcExtractValue.Invoke(collectionItem); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/scopetest/ScopeTestHelper.cs b/NEsper.Core/NEsper.Core/client/scopetest/ScopeTestHelper.cs new file mode 100755 index 000000000..eba3fec65 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/scopetest/ScopeTestHelper.cs @@ -0,0 +1,242 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Text; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.magic; + +namespace com.espertech.esper.client.scopetest +{ + /// + /// Helper for asserting conditions. + /// + public class ScopeTestHelper + { + /// Assert a condition is false. + /// to assert + public static void AssertFalse(bool condition) + { + AssertTrue(!condition); + } + + /// Assert a condition is false. + /// an optional message + /// to assert + public static void AssertFalse(String message, bool condition) + { + if (condition) + { + Fail(message); + } + } + + /// Assert a condition is true. + /// to assert + public static void AssertTrue(bool condition) + { + AssertTrue(null, condition); + } + + /// Assert a condition is true. + /// an optional message + /// to assert + public static void AssertTrue(String message, bool condition) + { + if (!condition) + { + Fail(message); + } + } + + private static bool AreCollectionsEqual(Object expected, Object actual) + { + var magicExpected = MagicMarker.GetCollectionFactory(expected.GetType()) + .Invoke(expected); + var magicActual = MagicMarker.GetCollectionFactory(actual.GetType()) + .Invoke(actual); + if (magicExpected.Count == magicActual.Count) + { + using (var magicExpectedEnum = magicExpected.GetEnumerator()) + { + using (var magicActualEnum = magicActual.GetEnumerator()) + { + while (true) + { + var mvExpected = magicExpectedEnum.MoveNext(); + var mvActual = magicActualEnum.MoveNext(); + if (mvExpected && mvActual) + { + if (!Equals(magicExpectedEnum.Current, magicActualEnum.Current)) + { + break; + } + } + else if (!mvExpected && !mvActual) + { + return true; + } + else + { + throw new IllegalStateException("collection has been modified"); + } + } + } + } + } + + return false; + } + + /// Assert that two values equal. + /// an optional message + /// expected value + /// actual value + public static void AssertEquals(String message, Object expected, Object actual) + { + if (expected == null && actual == null) + { + return; + } + if (expected != null && expected.Equals(actual)) + { + return; + } + if (expected != null && actual != null) + { + if (expected.GetType().IsGenericCollection() && actual.GetType().IsGenericCollection()) + { + if (AreCollectionsEqual(expected, actual)) + { + return; + } + } + } + FailNotEquals(message, expected, actual); + } + + /// Assert that two values equal. + /// expected value + /// actual value + public static void AssertEquals(Object expected, Object actual) + { + AssertEquals(null, expected, actual); + } + + /// Fail assertion. + public static void Fail() + { + Fail(null); + } + + /// Assert that two values are the same. + /// an optional message + /// expected value + /// actual value + public static void AssertSame(String message, Object expected, Object actual) + { + if (expected == actual) + { + return; + } + FailNotSame(message, expected, actual); + } + + /// Assert that two values are the same. + /// expected value + /// actual value + public static void AssertSame(Object expected, Object actual) + { + if (expected == actual) + { + return; + } + FailNotSame(null, expected, actual); + } + + /// Assert that a value is null. + /// an optional message + /// the object to check + public static void AssertNull(String message, Object @object) + { + AssertTrue(message, @object == null); + } + + /// Assert that a value is not null. + /// the object to check + public static void AssertNotNull(Object @object) + { + AssertTrue(@object != null); + } + + /// + /// Assert that a value is not null. + /// + /// The message. + /// the object to check + public static void AssertNotNull(String message, Object @object) + { + AssertTrue(message, @object != null); + } + + /// Assert that a value is null. + /// the object to check + public static void AssertNull(Object @object) + { + AssertTrue(@object == null); + } + + /// Fail assertion formatting a message for not-same. + /// an optional message + /// expected value + /// actual value + public static void FailNotSame(String message, Object expected, Object actual) + { + Fail(Format(message, expected, actual, true)); + } + + /// Fail assertion formatting a message for not-equals. + /// an optional message + /// expected value + /// actual value + public static void FailNotEquals(String message, Object expected, Object actual) + { + Fail(Format(message, expected, actual, false)); + } + + /// Fail assertion. + /// an optional message + public static void Fail(String message) + { + AssertProxy.Fail(message); + } + + private static String Format(String message, Object expected, Object actual, bool isSame) + { + var buf = new StringBuilder(); + if (!string.IsNullOrEmpty(message)) + { + buf.Append(message); + buf.Append(' '); + } + buf.Append("expected"); + if (isSame) + { + buf.Append(" same"); + } + buf.Append(":<"); + buf.Append(expected == null ? "null" : expected.ToString()); + buf.Append("> but was:<"); + buf.Append(actual == null ? "null" : actual.ToString()); + buf.Append(">"); + return buf.ToString(); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/scopetest/SupportSubscriber.cs b/NEsper.Core/NEsper.Core/client/scopetest/SupportSubscriber.cs new file mode 100755 index 000000000..ae4b51a91 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/scopetest/SupportSubscriber.cs @@ -0,0 +1,222 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; +using com.espertech.esper.collection; + + +namespace com.espertech.esper.client.scopetest +{ + /// + /// EPSubscriber for that retains the events it receives for use in assertions. + /// + public class SupportSubscriber + { + private readonly IList _newDataList; + private readonly IList _oldDataList; + private Object[] _lastNewData; + private Object[] _lastOldData; + private bool _isInvoked; + + /// Ctor. + public SupportSubscriber() + { + _newDataList = new List(); + _oldDataList = new List(); + } + + /// Receive events. + /// insert stream + /// remove stream + public void Update(Object[] newData, Object[] oldData) + { + lock(this) + { + _oldDataList.Add(oldData); + _newDataList.Add(newData); + + _lastNewData = newData; + _lastOldData = oldData; + + _isInvoked = true; + } + } + + /// + /// Reset subscriber, clearing all associated state. + /// + public void Reset() + { + lock(this) + { + _oldDataList.Clear(); + _newDataList.Clear(); + _lastNewData = null; + _lastOldData = null; + _isInvoked = false; + } + } + + /// Returns the last array of events (insert stream) that were received. + /// insert stream events or null if either a null value was received or when no events have been received since the last reset + public Object[] GetLastNewData() + { + return _lastNewData; + } + + /// Returns the last array of events (insert stream) that were received and resets the subscriber. + /// insert stream events or null if either a null value was received or when no events have been received since the last reset + public Object[] GetAndResetLastNewData() + { + lock(this) + { + Object[] lastNew = _lastNewData; + Reset(); + return lastNew; + } + } + + /// Asserts that exactly one insert stream event was received and no remove stream events, resets the listener clearing all state and returns the received event. + /// single insert-stream event + public Object AssertOneGetNewAndReset() + { + lock(this) + { + ScopeTestHelper.AssertTrue("EPSubscriber invocation not received but expected", _isInvoked); + + ScopeTestHelper.AssertEquals("Mismatch in the number of invocations", 1, _newDataList.Count); + ScopeTestHelper.AssertEquals("Mismatch in the number of invocations", 1, _oldDataList.Count); + + if (_lastNewData == null) + { + ScopeTestHelper.Fail("No new-data events received"); + } + + ScopeTestHelper.AssertEquals("Mismatch in the number of new-data events", 1, _lastNewData.Length); + ScopeTestHelper.AssertNull("No old-data events are expected but some were received", _lastOldData); + + Object lastNew = _lastNewData[0]; + Reset(); + return lastNew; + } + } + + /// Asserts that exactly one remove stream event was received and no insert stream events, resets the listener clearing all state and returns the received event. + /// single remove-stream event + public Object AssertOneGetOldAndReset() + { + ScopeTestHelper.AssertTrue("Listener invocation not received but expected", _isInvoked); + + ScopeTestHelper.AssertEquals("Mismatch in the number of invocations", 1, _newDataList.Count); + ScopeTestHelper.AssertEquals("Mismatch in the number of invocations", 1, _oldDataList.Count); + + if (_lastOldData == null) { + ScopeTestHelper.Fail("No old-data events received"); + } + ScopeTestHelper.AssertEquals("Mismatch in the number of old-data events", 1, _lastOldData.Length); + ScopeTestHelper.AssertNull("Expected no new-data events", _lastNewData); + + Object lastNew = _lastOldData[0]; + Reset(); + return lastNew; + } + + /// Returns the last array of remove-stream events that were received. + /// remove stream events or null if either a null value was received or when no events have been received since the last reset + public Object[] GetLastOldData() + { + return _lastOldData; + } + + /// Get a list of all insert-stream event arrays received. + /// list of event arrays + public IList GetNewDataList() + { + return _newDataList; + } + + /// Get a list of all remove-stream event arrays received. + /// list of event arrays + public IList GetOldDataList() + { + return _oldDataList; + } + + /// Returns true if the subscriber was invoked at least once. + /// invoked flag + public bool IsInvoked() + { + return _isInvoked; + } + + /// Returns true if the subscriber was invoked at least once and clears the invocation flag. + /// invoked flag + public bool GetAndClearIsInvoked() + { + lock(this) + { + bool invoked = _isInvoked; + _isInvoked = false; + return invoked; + } + } + + /// Returns an event array that represents all insert-stream events received so far. + /// event array + public Object[] GetNewDataListFlattened() + { + lock(this) + { + return Flatten(_newDataList); + } + } + + /// Returns an event array that represents all remove-stream events received so far. + /// event array + public Object[] GetOldDataListFlattened() + { + lock(this) + { + return Flatten(_oldDataList); + } + } + + /// Returns a pair of insert and remove stream event arrays considering the all invocations. + /// pair of event arrays, the first in the pair is the insert stream data, the second in the pair is the remove stream data + public UniformPair GetDataListsFlattened() + { + lock (this) + { + return new UniformPair(Flatten(_newDataList), Flatten(_oldDataList)); + } + } + + private static Object[] Flatten(IEnumerable list) + { + int count = list + .Where(events => events != null) + .Sum(events => events.Length); + + var array = new Object[count]; + count = 0; + foreach (Object[] events in list) + { + if (events != null) + { + for (int i = 0; i < events.Length; i++) + { + array[count++] = events[i]; + } + } + } + return array; + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/scopetest/SupportSubscriberMRD.cs b/NEsper.Core/NEsper.Core/client/scopetest/SupportSubscriberMRD.cs new file mode 100755 index 000000000..c0e72d9a5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/scopetest/SupportSubscriberMRD.cs @@ -0,0 +1,94 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.client.scopetest +{ + /// EPSubscriber for multi-row delivery that retains the events it receives for use in assertions. + public class SupportSubscriberMRD + { + private readonly List _insertStreamList = new List(); + private readonly List _removeStreamList = new List(); + private bool _isInvoked; + + /// + /// Receive multi-row subscriber data through this methods. + /// + /// new data + /// removed data + public void Update(Object[][] insertStream, Object[][] removeStream) + { + lock (this) + { + _isInvoked = true; + _insertStreamList.Add(insertStream); + _removeStreamList.Add(insertStream); + } + } + + /// + /// Returns all insert-stream events received so far. + /// + /// The list contains an item for each delivery. Each item contains a row with the event and each event is itself a tuple (object array). + /// + /// list of Object array-array + public IList InsertStreamList + { + get { return _insertStreamList; } + } + + /// + /// Returns all removed-stream events received so far. + /// + /// The list contains an item for each delivery. Each item contains a row with the event and each event is itself a tuple (object array). + /// + /// list of Object array-array + public IList RemoveStreamList + { + get { return _removeStreamList; } + } + + /// + /// Reset subscriber, clearing all associated state. + /// + public void Reset() + { + lock (this) + { + _isInvoked = false; + _insertStreamList.Clear(); + _removeStreamList.Clear(); + } + } + + /// + /// Returns true if the subscriber was invoked at least once. + /// + /// invoked flag + public bool IsInvoked() + { + return _isInvoked; + } + + /// + /// Returns true if the subscriber was invoked at least once and clears the invocation flag. + /// + /// invoked flag + public bool GetAndClearIsInvoked() + { + lock (this) + { + bool invoked = _isInvoked; + _isInvoked = false; + return invoked; + } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/scopetest/SupportUpdateListener.cs b/NEsper.Core/NEsper.Core/client/scopetest/SupportUpdateListener.cs new file mode 100755 index 000000000..51b1dffd1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/scopetest/SupportUpdateListener.cs @@ -0,0 +1,435 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.Linq; +using System.Threading; + +using com.espertech.esper.collection; +using com.espertech.esper.compat; + +namespace com.espertech.esper.client.scopetest +{ + /// + /// Update listener that retains the events it receives for use in assertions. + /// + public class SupportUpdateListener + { + private readonly List _newDataList; + private readonly List _oldDataList; + private EventBean[] _lastNewData; + private EventBean[] _lastOldData; + private bool _isInvoked; + + /// + /// Gets or sets a public tag for the listener. This is useful for tracking the listener + /// when more than one listener might be in use. + /// + /// The tag. + public string Tag { get; set; } + + /// + /// Ctor. + /// + public SupportUpdateListener() + { + _newDataList = new List(); + _oldDataList = new List(); + } + + /// + /// For multiple listeners, return the invoked flags and reset each listener + /// + + public static bool[] GetInvokedFlagsAndReset(SupportUpdateListener[] listeners) + { + bool[] invoked = new bool[listeners.Length]; + for (int i = 0; i < listeners.Length; i++) + { + invoked[i] = listeners[i].IsInvokedAndReset(); + } + return invoked; + } + + + /// Wait for the listener invocation for up to the given number of milliseconds. + /// to wait + /// RuntimeException when no results were received + public void WaitForInvocation(long msecWait) + { + var startTime = DateTimeHelper.CurrentTimeMillis; + while (true) + { + if ((DateTimeHelper.CurrentTimeMillis - startTime) > msecWait) + { + throw new EPException("No result received"); + } + if (_isInvoked) + { + return; + } + try + { + Thread.Sleep(50); + } + catch (ThreadInterruptedException e) + { + return; + } + catch (ThreadAbortException e) + { + return; + } + } + } + + /// + /// Wait for the listener invocation for up to the given number of milliseconds. + /// + /// to wait + /// in any number of separate invocations required before returning + /// RuntimeException when no results or insufficient number of events were received + public void WaitForInvocation(long msecWait, int numberOfNewEvents) + { + var startTime = DateTimeHelper.CurrentTimeMillis; + while (true) + { + if ((DateTimeHelper.CurrentTimeMillis - startTime) > msecWait) + { + throw new EPException("No events or less then the number of expected events received, expected " + numberOfNewEvents + " received " + GetNewDataListFlattened().Length); + } + + var events = GetNewDataListFlattened(); + if (events.Length >= numberOfNewEvents) + { + return; + } + + try + { + Thread.Sleep(50); + } + catch (ThreadInterruptedException e) + { + return; + } + } + } + + public void Update(object sender, UpdateEventArgs e) + { + lock (this) + { + Update(e.NewEvents, e.OldEvents); + } + } + + public void Update(EventBean[] newData, EventBean[] oldData) + { + lock (this) + { + _oldDataList.Add(oldData); + _newDataList.Add(newData); + + _lastNewData = newData; + _lastOldData = oldData; + + _isInvoked = true; + } + } + + /// Reset listener, clearing all associated state. + public void Reset() + { + lock (this) + { + _oldDataList.Clear(); + _newDataList.Clear(); + _lastNewData = null; + _lastOldData = null; + _isInvoked = false; + } + } + + /// Returns the last array of events (insert stream) that were received. + /// insert stream events or null if either a null value was received or when no events have been received since the last + /// reset + public EventBean[] LastNewData + { + get { return _lastNewData; } + set { _lastNewData = value; } + } + + /// Returns the last array of remove-stream events that were received. + /// remove stream events or null if either a null value was received or when no events have been received since the last + /// reset + public EventBean[] LastOldData + { + get { return _lastOldData; } + set { _lastOldData = value; } + } + + /// Returns the last array of events (insert stream) that were received and resets the listener. + /// insert stream events or null if either a null value was received or when no events have been received since the last reset + public EventBean[] GetAndResetLastNewData() + { + lock (this) + { + EventBean[] lastNew = _lastNewData; + Reset(); + return lastNew; + } + } + + /// Returns the last array of events (insert stream) that were received and resets the listener. + /// insert stream events or null if either a null value was received or when no events have been received since the last reset + public EventBean[] GetAndResetLastOldData() + { + lock (this) + { + EventBean[] lastOld = _lastOldData; + Reset(); + return lastOld; + } + } + + /// Asserts that exactly one insert stream event was received and no remove stream events, resets the listener clearing all state and returns the received event. + /// single insert-stream event + public EventBean AssertOneGetNewAndReset() + { + lock (this) + { + ScopeTestHelper.AssertTrue("Listener invocation not received but expected", _isInvoked); + + ScopeTestHelper.AssertEquals("Mismatch in the number of invocations", 1, _newDataList.Count); + ScopeTestHelper.AssertEquals("Mismatch in the number of invocations", 1, _oldDataList.Count); + + if (_lastNewData == null) + { + ScopeTestHelper.Fail("No new-data events received"); + } + ScopeTestHelper.AssertEquals("Mismatch in the number of new-data events", 1, _lastNewData.Length); + ScopeTestHelper.AssertNull("No old-data events are expected but some were received", _lastOldData); + + EventBean lastNew = _lastNewData[0]; + Reset(); + return lastNew; + } + } + + /// Asserts that exactly one remove stream event was received and no insert stream events, resets the listener clearing all state and returns the received event. + /// single remove-stream event + public EventBean AssertOneGetOldAndReset() + { + lock (this) + { + ScopeTestHelper.AssertTrue("Listener invocation not received but expected", _isInvoked); + + ScopeTestHelper.AssertEquals("Mismatch in the number of invocations", 1, _newDataList.Count); + ScopeTestHelper.AssertEquals("Mismatch in the number of invocations", 1, _oldDataList.Count); + + if (_lastOldData == null) + { + ScopeTestHelper.Fail("No old-data events received"); + } + ScopeTestHelper.AssertEquals("Mismatch in the number of old-data events", 1, _lastOldData.Length); + ScopeTestHelper.AssertNull("Expected no new-data events", _lastNewData); + + EventBean lastNew = _lastOldData[0]; + Reset(); + return lastNew; + } + } + + /// Asserts that exactly one insert stream event and exactly one remove stream event was received, resets the listener clearing all state and returns the received events as a pair. + /// pair of insert-stream and remove-stream events + public UniformPair AssertPairGetIRAndReset() + { + lock (this) + { + ScopeTestHelper.AssertTrue("Listener invocation not received but expected", _isInvoked); + + ScopeTestHelper.AssertEquals("Mismatch in the number of invocations", 1, _newDataList.Count); + ScopeTestHelper.AssertEquals("Mismatch in the number of invocations", 1, _oldDataList.Count); + + if (_lastNewData == null) + { + ScopeTestHelper.Fail("No new-data events received"); + } + if (_lastOldData == null) + { + ScopeTestHelper.Fail("No old-data events received"); + } + ScopeTestHelper.AssertEquals("Mismatch in the number of new-data events", 1, _lastNewData.Length); + ScopeTestHelper.AssertEquals("Mismatch in the number of old-data events", 1, _lastOldData.Length); + + EventBean lastNew = _lastNewData[0]; + EventBean lastOld = _lastOldData[0]; + Reset(); + return new UniformPair(lastNew, lastOld); + } + } + + /// Asserts that exactly one insert stream event was received not checking remove stream data, and returns the received event. + /// single insert-stream event + public EventBean AssertOneGetNew() + { + lock (this) + { + ScopeTestHelper.AssertTrue("Listener invocation not received but expected", _isInvoked); + + ScopeTestHelper.AssertEquals("Mismatch in the number of invocations", 1, _newDataList.Count); + ScopeTestHelper.AssertEquals("Mismatch in the number of invocations", 1, _oldDataList.Count); + + if (_lastNewData == null) + { + ScopeTestHelper.Fail("No new-data events received"); + } + ScopeTestHelper.AssertEquals("Mismatch in the number of new-data events", 1, _lastNewData.Length); + return _lastNewData[0]; + } + } + + /// Asserts that exactly one remove stream event was received not checking insert stream data, and returns the received event. + /// single remove-stream event + public EventBean AssertOneGetOld() + { + lock (this) + { + ScopeTestHelper.AssertTrue("Listener invocation not received but expected", _isInvoked); + + ScopeTestHelper.AssertEquals("Mismatch in the number of invocations", 1, _newDataList.Count); + ScopeTestHelper.AssertEquals("Mismatch in the number of invocations", 1, _oldDataList.Count); + + if (_lastOldData == null) + { + ScopeTestHelper.Fail("No old-data events received"); + } + ScopeTestHelper.AssertEquals("Mismatch in the number of old-data events", 1, _lastOldData.Length); + return _lastOldData[0]; + } + } + + /// Get a list of all insert-stream event arrays received. + /// list of event arrays + public List NewDataList + { + get { return _newDataList; } + } + + /// Get a list of all remove-stream event arrays received. + /// list of event arrays + public List OldDataList + { + get { return _oldDataList; } + } + + /// Returns true if the listener was invoked at least once. + /// invoked flag + public bool IsInvoked + { + get { return _isInvoked; } + } + + /// Returns true if the listener was invoked at least once and clears the invocation flag. + /// invoked flag + public bool GetAndClearIsInvoked() + { + lock (this) + { + bool invoked = _isInvoked; + _isInvoked = false; + return invoked; + } + } + + /// Returns true if the listener was invoked at least once and clears the invocation flag. + /// invoked flag + public bool IsInvokedAndReset() + { + lock (this) + { + var invoked = _isInvoked; + Reset(); + return invoked; + } + } + + /// Returns an event array that represents all insert-stream events received so far. + /// event array + public EventBean[] GetNewDataListFlattened() + { + lock (this) + { + return Flatten(_newDataList); + } + } + + /// Returns an event array that represents all remove-stream events received so far. + /// event array + public EventBean[] GetOldDataListFlattened() + { + lock (this) + { + return Flatten(_oldDataList); + } + } + + private static EventBean[] Flatten(IEnumerable list) + { + return list.Where(events => events != null).SelectMany(events => events).ToArray(); + } + + /// Returns a pair of insert and remove stream event arrays considering the last invocation only, asserting that only a single invocation occured, and resetting the listener. + /// pair of event arrays, the first in the pair is the insert stream data, the second in the pair is the remove stream data + public UniformPair AssertInvokedAndReset() + { + lock (this) + { + ScopeTestHelper.AssertTrue("Listener invocation not received but expected", _isInvoked); + ScopeTestHelper.AssertEquals("Received more then one invocation", 1, NewDataList.Count); + ScopeTestHelper.AssertEquals("Received more then one invocation", 1, OldDataList.Count); + EventBean[] newEvents = LastNewData; + EventBean[] oldEvents = LastOldData; + Reset(); + return new UniformPair(newEvents, oldEvents); + } + } + + /// Returns a pair of insert and remove stream event arrays considering the all invocations. + /// pair of event arrays, the first in the pair is the insert stream data, the second in the pair is the remove stream data + public UniformPair GetDataListsFlattened() + { + lock (this) + { + return new UniformPair(Flatten(_newDataList), Flatten(_oldDataList)); + } + } + + /// Returns a pair of insert and remove stream event arrays considering the all invocations, and resets the listener. + /// pair of event arrays, the first in the pair is the insert stream data, the second in the pair is the remove stream data + public UniformPair GetAndResetDataListsFlattened() + { + lock (this) + { + UniformPair pair = GetDataListsFlattened(); + Reset(); + return pair; + } + } + } + + public static class SupportUpdateListenerExtensions + { + public static EPStatement AddListener(this EPStatement statement, SupportUpdateListener listener) + { + statement.Events += listener.Update; + return statement; + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/AccessProjectionExpressionBase.cs b/NEsper.Core/NEsper.Core/client/soda/AccessProjectionExpressionBase.cs new file mode 100755 index 000000000..304ac26d9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/AccessProjectionExpressionBase.cs @@ -0,0 +1,71 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Represents the base expression for "first", "last" and "window" aggregation functions. + /// + [Serializable] + public abstract class AccessProjectionExpressionBase : ExpressionBase + { + /// + /// Ctor. + /// + protected AccessProjectionExpressionBase() + { + } + + /// + /// Returns the function name of the aggregation function. + /// + /// function name + public abstract string AggregationFunctionName { get; } + + /// + /// Ctor. + /// + /// to aggregate + protected AccessProjectionExpressionBase(Expression expression) + { + this.Children.Add(expression); + } + + /// + /// Gets the precedence. + /// + /// + /// The Precedence. + /// + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.UNARY; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + writer.Write(AggregationFunctionName); + writer.Write('('); + var delimiter = ""; + var children = this.Children; + if (children.Count > 0) + { + writer.Write(delimiter); + children[0].ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + for(int i=1; i + /// Represents a single annotation attribute, the value of which may itself be a single + /// value, array or further annotations. + /// + [Serializable] + public class AnnotationAttribute + { + /// Ctor. + public AnnotationAttribute() { + } + + /// Ctor. + /// annotation name + /// annotation value, could be a primitive, array or another annotation + public AnnotationAttribute(String name, Object value) { + Name = name; + Value = value; + } + + /// Returns annotation name. + /// name + public string Name { get; set; } + + /// Returns annotation value. + /// value + public object Value { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/AnnotationPart.cs b/NEsper.Core/NEsper.Core/client/soda/AnnotationPart.cs new file mode 100755 index 000000000..1797d81fc --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/AnnotationPart.cs @@ -0,0 +1,168 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.client.soda +{ + /// + /// Represents a single annotation. + /// + [Serializable] + public class AnnotationPart + { + // Map of identifier name and value can be any of the following: + // <"value"|attribute name, constant|array of value (Object[])| AnnotationPart + + /// Ctor. + public AnnotationPart() + { + Attributes = new List(); + } + + /// Copy annotation values. + /// to copy + public void Copy(AnnotationPart other) { + Name = other.Name; + Attributes = other.Attributes; + } + + /// Returns the internal expression id assigned for tools to identify the expression. + /// object name + public string TreeObjectName { get; set; } + + /// Ctor. + /// of annotation + public AnnotationPart(String name) + { + Attributes = new List(); + Name = name; + } + + /// Ctor. + /// name of annotation + /// are the attribute values + public AnnotationPart(String name, IList attributes) + { + Name = name; + Attributes = attributes; + } + + /// Returns annotation interface class name. + /// name of class, can be fully qualified + public string Name { get; set; } + + /// Add value. + /// to add + public void AddValue(Object value) { + Attributes.Add(new AnnotationAttribute("Value", value)); + } + + /// Add named value. + /// name + /// value + public void AddValue(String name, Object value) { + Attributes.Add(new AnnotationAttribute(name, value)); + } + + /// Returns annotation attributes. + /// the attribute values + public IList Attributes { get; private set; } + + /// + /// Print. + /// + /// to print to + /// annotations + /// The formatter. + public static void ToEPL(TextWriter writer, IList attributes, EPStatementFormatter formatter) { + if ((attributes == null) || (attributes.IsEmpty())) { + return; + } + + foreach (AnnotationPart part in attributes) { + if (part.Name == null) { + continue; + } + formatter.BeginAnnotation(writer); + part.ToEPL(writer); + } + } + + /// Print part. + /// to write to + public void ToEPL(TextWriter writer) { + writer.Write("@"); + writer.Write(Name); + + if (Attributes.IsEmpty()) { + return; + } + + if (Attributes.Count == 1) { + if ((Attributes[0].Name == null) || (Attributes[0].Name == "Value")) + { + writer.Write("("); + ToEPL(writer, Attributes[0].Value); + writer.Write(")"); + return; + } + } + + String delimiter = ""; + writer.Write("("); + foreach (AnnotationAttribute attribute in Attributes) { + if (attribute.Value == null) { + return; + } + writer.Write(delimiter); + writer.Write(attribute.Name); + writer.Write("="); + ToEPL(writer, attribute.Value); + delimiter = ","; + } + writer.Write(")"); + } + + private static void ToEPL(TextWriter writer, Object second) { + if (second is String) { + writer.Write("'"); + writer.Write(second.ToString()); + writer.Write("'"); + } + else if (second is AnnotationPart) { + ((AnnotationPart) second).ToEPL(writer); + } + else if (second.GetType().IsEnum) { + writer.Write(second.GetType().FullName); + writer.Write("."); + writer.Write(second.ToString()); + } + else if (second.GetType().IsArray) + { + var array = (Array) second; + var delimiter = ""; + writer.Write("{"); + for (int ii = 0; ii < array.Length; ii++) + { + writer.Write(delimiter); + ToEPL(writer, array.GetValue(ii)); + delimiter = ","; + } + writer.Write("}"); + } + else { + writer.Write(second.ToString()); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/ArithmaticExpression.cs b/NEsper.Core/NEsper.Core/client/soda/ArithmaticExpression.cs new file mode 100755 index 000000000..f353ecd15 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/ArithmaticExpression.cs @@ -0,0 +1,102 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Arithmatic expression for addition, subtraction, multiplication, division and modulo. + /// + [Serializable] + public class ArithmaticExpression : ExpressionBase + { + /// + /// Initializes a new instance of the class. + /// + public ArithmaticExpression() + { + } + + /// Ctor. + /// can be any of '-', '+', '*', '/' or '%' (modulo). + public ArithmaticExpression(String @operator) + { + Operator = @operator; + } + + /// Ctor. + /// the left hand side + /// can be any of '-', '+', '*', '/' or '%' (modulo). + /// the right hand side + public ArithmaticExpression(Expression left, String @operator, Expression right) + { + Operator = @operator; + AddChild(left); + AddChild(right); + } + + /// Gets the arithmatic operator. + /// operator + public string Operator { get; set; } + + /// Add a constant to include in the computation. + /// constant to add + /// expression + public ArithmaticExpression Add(Object obj) + { + Children.Add(new ConstantExpression(obj)); + return this; + } + + /// Add an expression to include in the computation. + /// to add + /// expression + public ArithmaticExpression Add(Expression expression) + { + Children.Add(expression); + return this; + } + + /// Add a property to include in the computation. + /// is the name of the property + /// expression + public ArithmaticExpression Add(String propertyName) + { + Children.Add(new PropertyValueExpression(propertyName)); + return this; + } + + public override ExpressionPrecedenceEnum Precedence + { + get + { + switch (Operator) { + case "*": + case "/": + case "%": + return ExpressionPrecedenceEnum.MULTIPLY; + default: + return ExpressionPrecedenceEnum.ADDITIVE; + } + } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + String delimiter = ""; + foreach (Expression child in Children) + { + writer.Write(delimiter); + child.ToEPL(writer, Precedence); + delimiter = Operator; + } + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/client/soda/ArrayExpression.cs b/NEsper.Core/NEsper.Core/client/soda/ArrayExpression.cs new file mode 100755 index 000000000..afacc4cce --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/ArrayExpression.cs @@ -0,0 +1,78 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Array expression forms array results, similar to the EPL syntax + /// of "{element 1, element 2, ... element n}". + /// + [Serializable] + public class ArrayExpression : ExpressionBase + { + /// + /// Initializes a new instance of the class. + /// + public ArrayExpression() + { + } + + /// Add a property to the expression. + /// to add + /// expression + public ArrayExpression Add(String property) + { + this.Children.Add(new PropertyValueExpression(property)); + return this; + } + + /// + /// Add a constant to the expression. + /// + /// Constant object that is to be added. + /// expression + public ArrayExpression Add(Object @object) + { + Children.Add(new ConstantExpression(@object)); + return this; + } + + /// Add an expression representing an array element to the expression. + /// to add + /// expression + public ArrayExpression Add(Expression expression) + { + Children.Add(expression); + return this; + } + + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.UNARY; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + writer.Write("{"); + bool isFirst = true; + foreach (Expression child in this.Children) + { + if (!isFirst) + { + writer.Write(","); + } + child.ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + isFirst = false; + } + writer.Write("}"); + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/client/soda/Assignment.cs b/NEsper.Core/NEsper.Core/client/soda/Assignment.cs new file mode 100755 index 000000000..f5cd462af --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/Assignment.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.soda +{ + /// + /// An assignment is an expression specifically for the purpose of usage in updates. Usually + /// an assignment is an equal-expression with the lhs being an event property or variable and + /// the rhs being the new value expression. + /// + [Serializable] + public class Assignment + { + /// Ctor. + public Assignment() + { + } + + /// Ctor. + /// value to assign + public Assignment(Expression value) + { + Value = value; + } + + /// Returns expression to eval. + /// eval expression + public Expression Value { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/AvedevProjectionExpression.cs b/NEsper.Core/NEsper.Core/client/soda/AvedevProjectionExpression.cs new file mode 100755 index 000000000..c71729187 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/AvedevProjectionExpression.cs @@ -0,0 +1,71 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.client.soda +{ + /// + /// Mean deviation of the (distinct) values returned by an expression. + /// + [Serializable] + public class AvedevProjectionExpression : ExpressionBase + { + private bool distinct; + + /// + /// Ctor. + /// + public AvedevProjectionExpression() { + } + + /// + /// Ctor - for use to create an expression tree, without inner expression. + /// + /// true if distinct + public AvedevProjectionExpression(bool isDistinct) + { + this.distinct = isDistinct; + } + + /// + /// Ctor - adds the expression to project. + /// + /// returning values to project + /// true if distinct + public AvedevProjectionExpression(Expression expression, bool isDistinct) + { + this.distinct = isDistinct; + this.Children.Add(expression); + } + + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.UNARY; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + ExpressionBase.RenderAggregation(writer, "avedev", distinct, this.Children); + } + + /// + /// Returns true if the projection considers distinct values only. + /// + /// true if distinct + public bool IsDistinct + { + get { return distinct; } + set { distinct = value; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/AvgProjectionExpression.cs b/NEsper.Core/NEsper.Core/client/soda/AvgProjectionExpression.cs new file mode 100755 index 000000000..5bffbbf11 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/AvgProjectionExpression.cs @@ -0,0 +1,66 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.client.soda +{ + /// + /// Average of the (distinct) values returned by an expression. + /// Expects a single child expression providing the values to aggregate. + /// + [Serializable] + public class AvgProjectionExpression : ExpressionBase + { + /// + /// Ctor. + /// + public AvgProjectionExpression() { + } + + /// + /// Ctor - for use to create an expression tree, without inner expression + /// + /// true if distinct + public AvgProjectionExpression(bool isDistinct) + { + this.IsDistinct = isDistinct; + } + + /// + /// Ctor - adds the expression to project. + /// + /// returning values to project + /// true if distinct + public AvgProjectionExpression(Expression expression, bool isDistinct) + { + this.IsDistinct = isDistinct; + this.Children.Add(expression); + } + + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.UNARY; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + ExpressionBase.RenderAggregation(writer, "avg", IsDistinct, this.Children); + } + + /// + /// Returns true if the projection considers distinct values only. + /// + /// true if distinct + public bool IsDistinct { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/BetweenExpression.cs b/NEsper.Core/NEsper.Core/client/soda/BetweenExpression.cs new file mode 100755 index 000000000..cc6ff12ed --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/BetweenExpression.cs @@ -0,0 +1,142 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Between checks that a given value is in a range between a low endpoint and a high endpoint. + /// + /// Closed and open ranges (endpoint included or excluded) are supported by this class, as is not-between. + /// + /// + public class BetweenExpression : ExpressionBase + { + private bool _isLowEndpointIncluded; + private bool _isHighEndpointIncluded; + private bool _isNotBetween; + + /// Ctor. + public BetweenExpression() + { + } + + /// + /// Ctor, creates a between range check. + /// + /// provides the datapoint + /// provides lower boundary + /// provides upper boundary + public BetweenExpression(Expression datapoint, Expression lower, Expression higher) + : this(datapoint, lower, higher, true, true, false) + { + } + + /// + /// Ctor - for use to create an expression tree, without child expression. + /// + /// Use add methods to add child expressions to acts upon. + /// + /// + /// true if the low endpoint is included, false if not + /// true if the high endpoint is included, false if not + /// true for not-between, false for between + public BetweenExpression(bool lowEndpointIncluded, bool highEndpointIncluded, bool notBetween) { + _isLowEndpointIncluded = lowEndpointIncluded; + _isHighEndpointIncluded = highEndpointIncluded; + _isNotBetween = notBetween; + } + + /// + /// Ctor. + /// + /// provides the datapoint + /// provides lower boundary + /// provides upper boundary + /// true if the low endpoint is included, false if not + /// true if the high endpoint is included, false if not + /// true for not-between, false for between + public BetweenExpression(Expression datapoint, Expression lower, Expression higher, bool lowEndpointIncluded, bool highEndpointIncluded, bool notBetween) { + Children.Add(datapoint); + Children.Add(lower); + Children.Add(higher); + + _isLowEndpointIncluded = lowEndpointIncluded; + _isHighEndpointIncluded = highEndpointIncluded; + _isNotBetween = notBetween; + } + + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.RELATIONAL_BETWEEN_IN; } + } + + /// + /// Renders the clause in textual representation. + /// + /// to output to + public override void ToPrecedenceFreeEPL(TextWriter writer) { + if (_isLowEndpointIncluded && _isHighEndpointIncluded) { + Children[0].ToEPL(writer, Precedence); + writer.Write(" between "); + Children[1].ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + writer.Write(" and "); + Children[2].ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + } else { + Children[0].ToEPL(writer, Precedence); + writer.Write(" in "); + if (_isLowEndpointIncluded) { + writer.Write('['); + } else { + writer.Write('('); + } + Children[1].ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + writer.Write(':'); + Children[2].ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + if (_isHighEndpointIncluded) { + writer.Write(']'); + } else { + writer.Write(')'); + } + } + } + + /// + /// True if the low endpoint is included. + /// + /// true for inclusive range. + public bool IsLowEndpointIncluded + { + get { return _isLowEndpointIncluded; } + set { _isLowEndpointIncluded = value; } + } + + /// + /// True if the high endpoint is included. + /// + /// true for inclusive range. + public bool IsHighEndpointIncluded + { + get { return _isHighEndpointIncluded; } + set { _isHighEndpointIncluded = value; } + } + + /// + /// Returns true for not-between, or false for between range. + /// + /// + /// false is the default range check, true checks if the value is outside of the range + /// + public bool IsNotBetween + { + get { return _isNotBetween; } + set { _isNotBetween = value; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/client/soda/BitwiseOpExpression.cs b/NEsper.Core/NEsper.Core/client/soda/BitwiseOpExpression.cs new file mode 100755 index 000000000..571c3423d --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/BitwiseOpExpression.cs @@ -0,0 +1,96 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +using com.espertech.esper.type; + +namespace com.espertech.esper.client.soda +{ + /// + /// Bitwise (binary) operator for binary AND, binary OR and binary XOR. + /// + [Serializable] + public class BitwiseOpExpression : ExpressionBase + { + private BitWiseOpEnum _binaryOp; + + /// + /// Initializes a new instance of the class. + /// + public BitwiseOpExpression() + { + } + + /// + /// Ctor - for use to create an expression tree, without child expression. + /// + /// Use add methods to add child expressions to acts upon. + /// + /// the binary operator + public BitwiseOpExpression(BitWiseOpEnum binaryOp) + { + _binaryOp = binaryOp; + } + + /// Add a property to the expression. + /// to add + /// expression + public BitwiseOpExpression Add(String property) + { + Children.Add(new PropertyValueExpression(property)); + return this; + } + + /// Add a constant to the expression. + /// constant to add + /// expression + public BitwiseOpExpression Add(Object @object) + { + Children.Add(new ConstantExpression(@object)); + return this; + } + + /// Add an expression to the expression. + /// to add + /// expression + public BitwiseOpExpression Add(Expression expression) + { + Children.Add(expression); + return this; + } + + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.BITWISE; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + bool isFirst = true; + foreach (Expression child in Children) + { + if (!isFirst) + { + writer.Write(_binaryOp.GetExpressionText()); + } + child.ToEPL(writer, Precedence); + isFirst = false; + } + } + + /// Gets or sets the binary operator. + /// operator + public BitWiseOpEnum BinaryOp + { + get { return _binaryOp; } + set { _binaryOp = value ; } + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/client/soda/CaseSwitchExpression.cs b/NEsper.Core/NEsper.Core/client/soda/CaseSwitchExpression.cs new file mode 100755 index 000000000..7388ce914 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/CaseSwitchExpression.cs @@ -0,0 +1,120 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.compat; + +namespace com.espertech.esper.client.soda +{ + /// + /// Case-expression that acts as a switch testing a value against other values. + /// + /// The first child expression provides the value to switch on. + /// The following pairs of child expressions provide the "when expression then expression" results. + /// The last child expression provides the "else" result. + /// + /// + [Serializable] + public class CaseSwitchExpression : ExpressionBase + { + /// + /// Ctor - for use to create an expression tree, without inner expression + /// + public CaseSwitchExpression() + { + } + + /// Ctor. + /// is the expression providing the value to switch on + public CaseSwitchExpression(Expression switchValue) + { + // switch value expression is first + AddChild(switchValue); + } + + /// + /// Adds a pair of expressions representing a "when" and a "then" in the switch. + /// + /// expression to match on + /// + /// expression to return a conditional result when the when-expression matches + /// + /// expression + public CaseSwitchExpression Add(Expression when, Expression then) + { + int size = Children.Count; + if (size % 2 != 0) + { + AddChild(when); + AddChild(then); + } + else + { + // add next to last as the last node is the else clause + Children.Insert(Children.Count - 1, when); + Children.Insert(Children.Count - 1, then); + } + return this; + } + + /// + /// Sets the else-part of the case-switch. This result of this expression is returned + /// when no when-expression matched. + /// + /// is the expression returning the no-match value + /// expression + public CaseSwitchExpression SetElse(Expression elseExpr) + { + int size = Children.Count; + // remove last node representing the else + if (size % 2 == 0) + { + Children.RemoveAt(size - 1); + } + AddChild(elseExpr); + return this; + } + + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.CASE; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + IList children = Children; + + writer.Write("case "); + children[0].ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + int index = 1; + while (index < children.Count - 1) + { + writer.Write(" when "); + children[index].ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + index++; + if (index == children.Count) + { + throw new IllegalStateException("Invalid case-when expression, count of when-to-then nodes not matching"); + } + writer.Write(" then "); + children[index].ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + index++; + } + + if (index < children.Count) + { + writer.Write(" else "); + children[index].ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + } + writer.Write(" end"); + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/client/soda/CaseWhenThenExpression.cs b/NEsper.Core/NEsper.Core/client/soda/CaseWhenThenExpression.cs new file mode 100755 index 000000000..af8ae08e7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/CaseWhenThenExpression.cs @@ -0,0 +1,106 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.compat; + +namespace com.espertech.esper.client.soda +{ + /// + /// Case expression that act as a when-then-else. + /// + + [Serializable] + public class CaseWhenThenExpression : ExpressionBase + { + /// + /// Ctor - for use to create an expression tree, without child expression. + /// + /// Use add methods to add child expressions to acts upon. + /// + /// + public CaseWhenThenExpression() + { + } + + /// Adds a when-then pair of expressions. + /// providings conditions to evaluate + /// provides the result when a condition evaluates to true + /// expression + public CaseWhenThenExpression Add(Expression when, Expression then) + { + int size = this.Children.Count; + if (size % 2 == 0) + { + this.AddChild(when); + this.AddChild(then); + } + else + { + // add next to last as the last node is the else clause + this.Children.Insert(this.Children.Count - 1, when); + this.Children.Insert(this.Children.Count - 1, then); + } + return this; + } + + /// + /// Sets the expression to provide a value when no when-condition matches. + /// + /// expression providing default result + /// expression + public CaseWhenThenExpression SetElse(Expression elseExpr) + { + int size = this.Children.Count; + // remove last node representing the else + if (size % 2 != 0) + { + this.Children.RemoveAt(size - 1); + } + this.AddChild(elseExpr); + return this; + } + + + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.CASE; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + IList children = Children; + + writer.Write("case"); + int index = 0; + while(index < children.Count - 1) + { + writer.Write(" when "); + children[index].ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + index++; + if (index == children.Count) + { + throw new IllegalStateException("Invalid case-when expression, count of when-to-then nodes not matching"); + } + writer.Write(" then "); + children[index].ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + index++; + } + + if (index < children.Count) + { + writer.Write(" else "); + children[index].ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + } + writer.Write(" end"); + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/client/soda/CastExpression.cs b/NEsper.Core/NEsper.Core/client/soda/CastExpression.cs new file mode 100755 index 000000000..fe1bb91e3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/CastExpression.cs @@ -0,0 +1,83 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; +using System.Linq; + +namespace com.espertech.esper.client.soda +{ + /// + /// Cast expression casts the return value of an expression to a specified type. + /// + [Serializable] + public class CastExpression : ExpressionBase + { + private String _typeName; + + /// + /// Initializes a new instance of the class. + /// + public CastExpression() + { + } + + /// + /// Ctor - for use to create an expression tree, without child expression. + /// + /// + /// is the type to cast to: a fully-qualified class name or primitive type name or "string" + /// + public CastExpression(String typeName) + { + _typeName = typeName; + } + + /// Ctor. + /// provides values to cast + /// + /// is the type to cast to: a fully-qualified class names or primitive type names or "string" + /// + public CastExpression(Expression expressionToCheck, String typeName) + { + Children.Add(expressionToCheck); + _typeName = typeName; + } + + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.UNARY; } + } + + /// Renders the clause in textual representation. + /// to output to + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + writer.Write("cast("); + Children[0].ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + writer.Write(","); + writer.Write(_typeName); + + for (int ii = 1; ii < Children.Count ; ii++) + { + writer.Write(","); + Children[ii].ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + } + + writer.Write(")"); + } + + /// Gets or sets the name of the type to cast to. + /// type name + public String TypeName + { + get { return _typeName; } + set { _typeName = value; } + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/client/soda/CoalesceExpression.cs b/NEsper.Core/NEsper.Core/client/soda/CoalesceExpression.cs new file mode 100755 index 000000000..253c9a0c2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/CoalesceExpression.cs @@ -0,0 +1,96 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Coalesce-function which returns the first non-null value in a list of values. + /// + [Serializable] + public class CoalesceExpression : ExpressionBase + { + /// + /// Ctor - for use to create an expression tree, without child expression. + /// + /// Use add methods to add child expressions to acts upon. + /// + public CoalesceExpression() + { + } + + /// Ctor. + /// the first property in the expression + /// the second property in the expression + /// optional more properties in the expression + public CoalesceExpression(String propertyOne, String propertyTwo, params String[] moreProperties) + { + AddChild(new PropertyValueExpression(propertyOne)); + AddChild(new PropertyValueExpression(propertyTwo)); + for (int i = 0; i < moreProperties.Length; i++) + { + AddChild(new PropertyValueExpression(moreProperties[i])); + } + } + + /// Ctor. + /// provides the first value in the expression + /// provides the second value in the expression + /// + /// optional more expressions that are part of the function + /// + public CoalesceExpression(Expression exprOne, Expression exprTwo, params Expression[] moreExpressions) + { + AddChild(exprOne); + AddChild(exprTwo); + for (int i = 0; i < moreExpressions.Length; i++) + { + AddChild(moreExpressions[i]); + } + } + + /// Add a constant to include in the computation. + /// constant to add + /// expression + public CoalesceExpression Add(Object @object) + { + Children.Add(new ConstantExpression(@object)); + return this; + } + + /// Add an expression to include in the computation. + /// to add + /// expression + public CoalesceExpression Add(Expression expression) + { + Children.Add(expression); + return this; + } + + /// Add a property to include in the computation. + /// is the name of the property + /// expression + public CoalesceExpression Add(String propertyName) + { + Children.Add(new PropertyValueExpression(propertyName)); + return this; + } + + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.UNARY; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + ToPrecedenceFreeEPL("coalesce", Children, writer); + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/client/soda/CompareListExpression.cs b/NEsper.Core/NEsper.Core/client/soda/CompareListExpression.cs new file mode 100755 index 000000000..04ff0adf6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/CompareListExpression.cs @@ -0,0 +1,77 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Represents a list-compare of the format "expression operator all/any (expressions)". + /// + [Serializable] + public class CompareListExpression + : ExpressionBase + { + /// + /// Ctor. + /// + public CompareListExpression() + { + } + + /// + /// Ctor. + /// + /// is all, false if any + /// =, !=, <, >, <=, >=, <> + public CompareListExpression(bool all, String @operator) + { + IsAll = all; + Operator = @operator; + } + + /// + /// Returns all flag, true for ALL and false for ANY. + /// + /// indicator if all or any + public bool IsAll { get; private set; } + + /// Returns the operator. + /// operator + public string Operator { get; set; } + + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.RELATIONAL_BETWEEN_IN; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + Children[0].ToEPL(writer, Precedence); + writer.Write(Operator); + if (IsAll) + { + writer.Write("all ("); + } + else + { + writer.Write("any("); + } + + String delimiter = ""; + for (int i = 1; i < Children.Count; i++) + { + writer.Write(delimiter); + Children[i].ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + delimiter = ","; + } + writer.Write(')'); + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/ConcatExpression.cs b/NEsper.Core/NEsper.Core/client/soda/ConcatExpression.cs new file mode 100755 index 000000000..c965909a4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/ConcatExpression.cs @@ -0,0 +1,70 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Concatenation expression that concatenates the result of child expressions to the expression. + /// + [Serializable] + public class ConcatExpression : ExpressionBase + { + /// + /// Initializes a new instance of the class. + /// + public ConcatExpression() + { + } + + /// Add a constant to include in the computation. + /// constant to add + /// expression + public ConcatExpression Add(Object @object) + { + Children.Add(new ConstantExpression(@object)); + return this; + } + + /// Add an expression to include in the computation. + /// to add + /// expression + public ConcatExpression Add(Expression expression) + { + Children.Add(expression); + return this; + } + + /// Add a property to include in the computation. + /// is the name of the property + /// expression + public ConcatExpression Add(String propertyName) + { + Children.Add(new PropertyValueExpression(propertyName)); + return this; + } + + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.CONCAT; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + String delimiter = ""; + foreach (Expression child in Children) + { + writer.Write(delimiter); + child.ToEPL(writer, Precedence); + delimiter = "||"; + } + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/client/soda/Conjunction.cs b/NEsper.Core/NEsper.Core/client/soda/Conjunction.cs new file mode 100755 index 000000000..7fabceaf4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/Conjunction.cs @@ -0,0 +1,59 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Conjunction represents a logical AND allowing multiple sub-expressions to be connected by AND. + /// + [Serializable] + public class Conjunction : Junction + { + /// + /// Ctor - for use to create an expression tree, without child expression. + /// + /// Use add methods to add child expressions to acts upon. + /// + public Conjunction() + { + } + + /// Ctor. + /// provides value to AND + /// provides value to AND + /// is more expressions to put in the AND-relationship. + public Conjunction(Expression first, Expression second, params Expression[] expressions) + { + AddChild(first); + AddChild(second); + for (int i = 0; i < expressions.Length; i++) + { + AddChild(expressions[i]); + } + } + + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.AND; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + String delimiter = ""; + foreach (Expression child in this.Children) + { + writer.Write(delimiter); + child.ToEPL(writer, Precedence); + delimiter = " and "; + } + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/client/soda/ConstantExpression.cs b/NEsper.Core/NEsper.Core/client/soda/ConstantExpression.cs new file mode 100755 index 000000000..f56fda71d --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/ConstantExpression.cs @@ -0,0 +1,94 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.core.service; + +namespace com.espertech.esper.client.soda +{ + /// Constant value returns a fixed value for use in expressions. + [Serializable] + public class ConstantExpression : ExpressionBase + { + /// Ctor. + public ConstantExpression() { + } + + /// Returns the type of the constant. + /// type + public string ConstantType { get; private set; } + + /// Ctor. + /// is the constant value, or null to represent the null value + public ConstantExpression(Object constant) + { + Constant = constant; + } + + /// Ctor. + /// value + /// type + public ConstantExpression(Object constant, String constantType) + { + Constant = constant; + ConstantType = constantType; + } + + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.UNARY; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + var constant = Constant; + if (constant is IDictionary) { + var map = (IDictionary) constant; + writer.Write("{"); + var delimiter = ""; + + foreach (var entry in map) { + writer.Write(delimiter); + writer.Write(entry.Key); + writer.Write(": "); + DataFlowOperatorParameter.RenderValue(writer, entry.Value); + delimiter = ","; + } + writer.Write("}"); + } + else if (constant is string) + { + EPStatementObjectModelHelper.RenderEPL(writer, constant); + } + else if (constant is IEnumerable) { + var iterable = (IEnumerable) constant; + writer.Write("["); + var delimiter = ""; + + foreach(var next in iterable) { + writer.Write(delimiter); + DataFlowOperatorParameter.RenderValue(writer, next); + delimiter = ","; + } + writer.Write("]"); + } + else + { + EPStatementObjectModelHelper.RenderEPL(writer, constant); + } + } + + /// Returns the constant value that the expression represents. + /// value of constant + public object Constant { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/ContainedEventSelect.cs b/NEsper.Core/NEsper.Core/client/soda/ContainedEventSelect.cs new file mode 100755 index 000000000..e7d7a0cb4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/ContainedEventSelect.cs @@ -0,0 +1,105 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// Represents a contained-event selection. + [Serializable] + public class ContainedEventSelect + { + /// Ctor. + public ContainedEventSelect() + { + } + + /// + /// Ctor. + /// + /// the property expression or other expression for splitting the event + public ContainedEventSelect(Expression splitExpression) + { + SplitExpression = splitExpression; + } + + /// + /// RenderAny contained-event select + /// + /// to render to + /// to use + /// to render + public static void ToEPL(TextWriter writer, EPStatementFormatter formatter, IList items) + { + foreach (ContainedEventSelect propertySelect in items) + { + writer.Write('['); + propertySelect.ToEPL(writer, formatter); + writer.Write(']'); + } + } + + /// + /// Returns the property alias. + /// + /// alias + public string OptionalAsName { get; set; } + + /// + /// Returns the select clause. + /// + /// select clause + public SelectClause SelectClause { get; set; } + + /// + /// Returns the where clause. + /// + /// where clause + public Expression WhereClause { get; set; } + + /// + /// Returns the event type name assigned to events that result by applying the split (contained @event) expression. + /// + /// type name, or null if none assigned + public string OptionalSplitExpressionTypeName { get; set; } + + /// + /// Returns the expression that returns the contained events. + /// + /// contained event expression + public Expression SplitExpression { get; set; } + + /// + /// Returns the EPL. + /// + /// to write to + /// for newline-whitespace formatting + public void ToEPL(TextWriter writer, EPStatementFormatter formatter) { + if (SelectClause != null) { + SelectClause.ToEPL(writer, formatter, false, false); + writer.Write(" from "); + } + SplitExpression.ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + if (OptionalSplitExpressionTypeName != null) { + writer.Write("@Type("); + writer.Write(OptionalSplitExpressionTypeName); + writer.Write(")"); + } + if (OptionalAsName != null) { + writer.Write(" as "); + writer.Write(OptionalAsName); + } + if (WhereClause != null) { + writer.Write(" where "); + WhereClause.ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/client/soda/ContextDescriptor.cs b/NEsper.Core/NEsper.Core/client/soda/ContextDescriptor.cs new file mode 100755 index 000000000..39ac8eafa --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/ContextDescriptor.cs @@ -0,0 +1,21 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// Interface for context dimension descriptors. + public interface ContextDescriptor + { + /// Format as EPL. + /// output + /// formatter + void ToEPL(TextWriter writer, EPStatementFormatter formatter); + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/soda/ContextDescriptorCategory.cs b/NEsper.Core/NEsper.Core/client/soda/ContextDescriptorCategory.cs new file mode 100755 index 000000000..e957f6442 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/ContextDescriptorCategory.cs @@ -0,0 +1,63 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Category-segmented context. + /// + [Serializable] + public class ContextDescriptorCategory : ContextDescriptor + { + /// Ctor. + public ContextDescriptorCategory() + { + Items = new List(); + } + + /// Ctor. + /// categories + /// event type and predicate + public ContextDescriptorCategory(IList items, + Filter filter) + { + Items = items; + Filter = filter; + } + + /// Returns categories. + /// categories + public IList Items { get; set; } + + /// Returns type name and predicate expressions (filter) + /// filter + public Filter Filter { get; set; } + + #region ContextDescriptor Members + + public void ToEPL(TextWriter writer, + EPStatementFormatter formatter) + { + String delimiter = ""; + foreach (ContextDescriptorCategoryItem item in Items) + { + writer.Write(delimiter); + item.ToEPL(writer, formatter); + delimiter = ", "; + } + writer.Write(" from "); + Filter.ToEPL(writer, formatter); + } + + #endregion + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/soda/ContextDescriptorCategoryItem.cs b/NEsper.Core/NEsper.Core/client/soda/ContextDescriptorCategoryItem.cs new file mode 100755 index 000000000..43ccf122f --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/ContextDescriptorCategoryItem.cs @@ -0,0 +1,57 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Context descriptor for categories. + /// + public class ContextDescriptorCategoryItem : ContextDescriptor + { + /// + /// Ctor. + /// + public ContextDescriptorCategoryItem() + { + } + + /// Ctor. + /// category expression + /// category label + public ContextDescriptorCategoryItem(Expression expression, + String label) + { + Expression = expression; + Label = label; + } + + /// Returns the category expression. + /// expression + public Expression Expression { get; set; } + + /// Returns the category label + /// category label + public string Label { get; set; } + + #region ContextDescriptor Members + + public void ToEPL(TextWriter writer, + EPStatementFormatter formatter) + { + writer.Write("group "); + Expression.ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + writer.Write(" as "); + writer.Write(Label); + } + + #endregion + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/soda/ContextDescriptorCondition.cs b/NEsper.Core/NEsper.Core/client/soda/ContextDescriptorCondition.cs new file mode 100755 index 000000000..c6616d1e5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/ContextDescriptorCondition.cs @@ -0,0 +1,24 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// For use with overlapping or non-overlapping contexts, implementations represents a + /// condition for starting/initiating or ending/terminating a context. + /// + public interface ContextDescriptorCondition { + + /// Populate the EPL. + /// output + /// formatter + void ToEPL(TextWriter writer, EPStatementFormatter formatter); + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/ContextDescriptorConditionCrontab.cs b/NEsper.Core/NEsper.Core/client/soda/ContextDescriptorConditionCrontab.cs new file mode 100755 index 000000000..79a895fed --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/ContextDescriptorConditionCrontab.cs @@ -0,0 +1,65 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Context condition that start/initiated or ends/terminates context partitions based on a crontab expression. + /// + [Serializable] + public class ContextDescriptorConditionCrontab : ContextDescriptorCondition + { + /// Ctor. + public ContextDescriptorConditionCrontab() + { + } + + /// Ctor. + /// crontab expressions returning number sets for each crontab position + /// indicator whethet to include "now" + public ContextDescriptorConditionCrontab(IList crontabExpressions, bool now) + { + CrontabExpressions = crontabExpressions; + IsNow = now; + } + + /// Returns the crontab expressions. + /// crontab + public IList CrontabExpressions { get; set; } + + /// Returns "now" indicator + /// "now" indicator + public bool IsNow { get; set; } + + public void ToEPL(TextWriter writer, EPStatementFormatter formatter) + { + Write(writer, CrontabExpressions, IsNow); + } + + private static void Write(TextWriter writer, IList expressions, bool now) + { + if (now) + { + writer.Write("@now and "); + } + writer.Write("("); + string delimiter = ""; + foreach (Expression e in expressions) + { + writer.Write(delimiter); + e.ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + delimiter = ", "; + } + writer.Write(")"); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/soda/ContextDescriptorConditionFilter.cs b/NEsper.Core/NEsper.Core/client/soda/ContextDescriptorConditionFilter.cs new file mode 100755 index 000000000..357506c78 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/ContextDescriptorConditionFilter.cs @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Context condition that start/initiated or ends/terminates context partitions based on a filter expression. + /// + public class ContextDescriptorConditionFilter : ContextDescriptorCondition + { + /// Ctor. + public ContextDescriptorConditionFilter() + { + } + + /// Ctor. + /// event filter + /// tag name of the filtered events + public ContextDescriptorConditionFilter(Filter filter, String optionalAsName) + { + Filter = filter; + OptionalAsName = optionalAsName; + } + + /// Returns the event stream filter. + /// filter + public Filter Filter { get; set; } + + /// Returns the tag name assigned, if any. + /// tag name + public string OptionalAsName { get; set; } + + public void ToEPL(TextWriter writer, EPStatementFormatter formatter) + { + Filter.ToEPL(writer, formatter); + if (OptionalAsName != null) + { + writer.Write(" as "); + writer.Write(OptionalAsName); + } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/soda/ContextDescriptorConditionImmediate.cs b/NEsper.Core/NEsper.Core/client/soda/ContextDescriptorConditionImmediate.cs new file mode 100755 index 000000000..ad7bcda5b --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/ContextDescriptorConditionImmediate.cs @@ -0,0 +1,30 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Context condition that starts/initiates immediately. + /// + [Serializable] + public class ContextDescriptorConditionImmediate : ContextDescriptorCondition + { + /// Ctor. + public ContextDescriptorConditionImmediate() + { + } + + public void ToEPL(TextWriter writer, EPStatementFormatter formatter) + { + writer.Write("@now"); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/soda/ContextDescriptorConditionNever.cs b/NEsper.Core/NEsper.Core/client/soda/ContextDescriptorConditionNever.cs new file mode 100755 index 000000000..f9a8a4946 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/ContextDescriptorConditionNever.cs @@ -0,0 +1,24 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// Context condition that starts/initiates immediately. + public class ContextDescriptorConditionNever : ContextDescriptorCondition + { + /// Ctor. + public ContextDescriptorConditionNever() { + } + + public void ToEPL(TextWriter writer, EPStatementFormatter formatter) { + + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/client/soda/ContextDescriptorConditionPattern.cs b/NEsper.Core/NEsper.Core/client/soda/ContextDescriptorConditionPattern.cs new file mode 100755 index 000000000..dabd81eee --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/ContextDescriptorConditionPattern.cs @@ -0,0 +1,67 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// Context condition that start/initiated or ends/terminates context partitions based on a pattern. + [Serializable] + public class ContextDescriptorConditionPattern : ContextDescriptorCondition + { + /// Ctor. + public ContextDescriptorConditionPattern() + { + } + + /// Ctor. + /// pattern expression + /// if the events of the pattern should be included in the contextual statements + /// indicator whether "now" + public ContextDescriptorConditionPattern(PatternExpr pattern, bool inclusive, bool now) + { + Pattern = pattern; + IsInclusive = inclusive; + IsNow = now; + } + + /// Returns the pattern expression. + /// pattern + public PatternExpr Pattern { get; set; } + + /// + /// Return the inclusive flag, meaning events that constitute the pattern match should be considered for + /// context-associated statements. + /// + /// inclusive flag + public bool IsInclusive { get; set; } + + /// Returns "now" indicator + /// "now" indicator + public bool IsNow { get; set; } + + public void ToEPL(TextWriter writer, EPStatementFormatter formatter) + { + if (IsNow) + { + writer.Write("@now and"); + } + writer.Write("pattern ["); + if (Pattern != null) + { + Pattern.ToEPL(writer, PatternExprPrecedenceEnum.MINIMUM, formatter); + } + writer.Write("]"); + if (IsInclusive) + { + writer.Write("@Inclusive"); + } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/soda/ContextDescriptorConditionTimePeriod.cs b/NEsper.Core/NEsper.Core/client/soda/ContextDescriptorConditionTimePeriod.cs new file mode 100755 index 000000000..00e4af76e --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/ContextDescriptorConditionTimePeriod.cs @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// Context condition that start/initiated or ends/terminates context partitions based on a time period. + [Serializable] + public class ContextDescriptorConditionTimePeriod : ContextDescriptorCondition + { + /// Ctor. + public ContextDescriptorConditionTimePeriod() + { + } + + /// Ctor. + /// time period expression + /// indicator whether "now" + public ContextDescriptorConditionTimePeriod(Expression timePeriod, bool now) + { + TimePeriod = timePeriod; + IsNow = now; + } + + /// Returns the time period expression + /// time period expression + public Expression TimePeriod { get; set; } + + /// Returns "now" indicator + /// "now" indicator + public bool IsNow { get; set; } + + public void ToEPL(TextWriter writer, EPStatementFormatter formatter) + { + if (IsNow) + { + writer.Write("@now and"); + } + writer.Write("after "); + TimePeriod.ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/soda/ContextDescriptorHashSegmented.cs b/NEsper.Core/NEsper.Core/client/soda/ContextDescriptorHashSegmented.cs new file mode 100755 index 000000000..77940c927 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/ContextDescriptorHashSegmented.cs @@ -0,0 +1,66 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// Hash-segmented context. + [Serializable] + public class ContextDescriptorHashSegmented : ContextDescriptor + { + /// Ctor. + public ContextDescriptorHashSegmented() + { + Items = new List(); + } + + /// Ctor. + /// list of hash code functions and event types to apply to + /// a number between 1 and Integer.MAX for parallelism + /// true to allocate each context partition at time of statement creation + public ContextDescriptorHashSegmented(IList items, int granularity, bool preallocate) + { + Items = items; + Granularity = granularity; + IsPreallocate = preallocate; + } + + /// Returns hash items. + /// hash items + public IList Items { get; set; } + + /// Returns the granularity. + /// granularity + public int Granularity { get; set; } + + /// Returns flag indicating whether to allocate context partitions upon statement creation, or only when actually referred to + /// preallocation flag + public bool IsPreallocate { get; set; } + + public void ToEPL(TextWriter writer, EPStatementFormatter formatter) + { + writer.Write("coalesce "); + String delimiter = ""; + foreach (ContextDescriptorHashSegmentedItem item in Items) + { + writer.Write(delimiter); + item.ToEPL(writer, formatter); + delimiter = ", "; + } + writer.Write(" granularity "); + writer.Write(Convert.ToString(Granularity)); + if (IsPreallocate) + { + writer.Write(" preallocate"); + } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/soda/ContextDescriptorHashSegmentedItem.cs b/NEsper.Core/NEsper.Core/client/soda/ContextDescriptorHashSegmentedItem.cs new file mode 100755 index 000000000..91237f285 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/ContextDescriptorHashSegmentedItem.cs @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// Context detail for a library-func and filter pair for the hash segmented context. + [Serializable] + public class ContextDescriptorHashSegmentedItem : ContextDescriptor + { + /// Ctor. + public ContextDescriptorHashSegmentedItem() + { + } + + /// Ctor. + /// the hash function, expecting SingleRowMethodExpression + /// the event types to apply to + public ContextDescriptorHashSegmentedItem(Expression hashFunction, Filter filter) + { + HashFunction = hashFunction; + Filter = filter; + } + + /// Returns the filter. + /// filter + public Filter Filter { get; set; } + + /// Returns the hash function. + /// hash function + public Expression HashFunction { get; set; } + + public void ToEPL(TextWriter writer, EPStatementFormatter formatter) + { + if (HashFunction != null) + { + HashFunction.ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + } + writer.Write(" from "); + Filter.ToEPL(writer, formatter); + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/ContextDescriptorInitiatedTerminated.cs b/NEsper.Core/NEsper.Core/client/soda/ContextDescriptorInitiatedTerminated.cs new file mode 100755 index 000000000..2263ba31c --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/ContextDescriptorInitiatedTerminated.cs @@ -0,0 +1,107 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Context dimension descriptor for a start-and-end temporal (single instance) or initiated-terminated (overlapping) context + /// + public class ContextDescriptorInitiatedTerminated : ContextDescriptor + { + /// Ctor. + public ContextDescriptorInitiatedTerminated() + { + } + + /// + /// Ctor. + /// + /// the condition that starts/initiates a context partition + /// the condition that ends/terminates a context partition + /// true for overlapping contexts + /// list of distinct-value expressions, can be null + public ContextDescriptorInitiatedTerminated( + ContextDescriptorCondition startCondition, + ContextDescriptorCondition endCondition, + bool overlapping, + IList optionalDistinctExpressions) + { + StartCondition = startCondition; + EndCondition = endCondition; + IsOverlapping = overlapping; + OptionalDistinctExpressions = optionalDistinctExpressions; + } + + /// + /// Ctor. + /// + /// the condition that starts/initiates a context partition + /// the condition that ends/terminates a context partition + /// true for overlapping contexts + public ContextDescriptorInitiatedTerminated( + ContextDescriptorCondition startCondition, + ContextDescriptorCondition endCondition, + bool overlapping) + { + StartCondition = startCondition; + EndCondition = endCondition; + IsOverlapping = overlapping; + } + + /// + /// Returns the condition that starts/initiates a context partition + /// + /// start condition + public ContextDescriptorCondition StartCondition { get; set; } + + /// + /// Returns the condition that ends/terminates a context partition + /// + /// end condition + public ContextDescriptorCondition EndCondition { get; set; } + + /// + /// Returns true for overlapping context, false for non-overlapping. + /// + /// overlap indicator + public bool IsOverlapping { get; set; } + + /// + /// Returns the list of expressions providing distinct keys, if any + /// + /// distinct expressions + public IList OptionalDistinctExpressions { get; set; } + + public void ToEPL(TextWriter writer, EPStatementFormatter formatter) + { + writer.Write(IsOverlapping ? "initiated by " : "start "); + if (OptionalDistinctExpressions != null && OptionalDistinctExpressions.Count > 0) + { + writer.Write("distinct("); + string delimiter = ""; + foreach (Expression expression in OptionalDistinctExpressions) + { + writer.Write(delimiter); + expression.ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + delimiter = ", "; + } + writer.Write(") "); + } + StartCondition.ToEPL(writer, formatter); + if (!(EndCondition is ContextDescriptorConditionNever)) + { + writer.Write(" "); + writer.Write(IsOverlapping ? "terminated " : "end "); + EndCondition.ToEPL(writer, formatter); + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/client/soda/ContextDescriptorKeyedSegmented.cs b/NEsper.Core/NEsper.Core/client/soda/ContextDescriptorKeyedSegmented.cs new file mode 100755 index 000000000..05818d8af --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/ContextDescriptorKeyedSegmented.cs @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Context dimension information for keyed segmented context. + /// + [Serializable] + public class ContextDescriptorKeyedSegmented : ContextDescriptor + { + /// Ctor. + public ContextDescriptorKeyedSegmented() + { + Items = new List(); + } + + /// Ctor. + /// key set descriptions + public ContextDescriptorKeyedSegmented(IList items) + { + Items = items; + } + + /// Returns the key set descriptions + /// list + public IList Items { get; set; } + + public void ToEPL(TextWriter writer, EPStatementFormatter formatter) + { + writer.Write("partition by "); + String delimiter = ""; + foreach (ContextDescriptorKeyedSegmentedItem item in Items) + { + writer.Write(delimiter); + item.ToEPL(writer, formatter); + delimiter = ", "; + } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/soda/ContextDescriptorKeyedSegmentedItem.cs b/NEsper.Core/NEsper.Core/client/soda/ContextDescriptorKeyedSegmentedItem.cs new file mode 100755 index 000000000..c03657235 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/ContextDescriptorKeyedSegmentedItem.cs @@ -0,0 +1,59 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// Context detail for a key-filter pair for the keyed segmented context. + public class ContextDescriptorKeyedSegmentedItem : ContextDescriptor + { + /// Ctor. + public ContextDescriptorKeyedSegmentedItem() + { + } + + /// Ctor. + /// list of property names + /// event type name and optional filter predicates + public ContextDescriptorKeyedSegmentedItem(IList propertyNames, + Filter filter) + { + PropertyNames = propertyNames; + Filter = filter; + } + + /// Returns the filter. + /// filter + public Filter Filter { get; set; } + + /// Returns the property names. + /// list + public IList PropertyNames { get; set; } + + #region ContextDescriptor Members + + public void ToEPL(TextWriter writer, + EPStatementFormatter formatter) + { + String delimiter = ""; + foreach (String prop in PropertyNames) + { + writer.Write(delimiter); + writer.Write(prop); + delimiter = " and "; + } + writer.Write(" from "); + Filter.ToEPL(writer, formatter); + } + + #endregion + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/soda/ContextDescriptorNested.cs b/NEsper.Core/NEsper.Core/client/soda/ContextDescriptorNested.cs new file mode 100755 index 000000000..e5bd96c46 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/ContextDescriptorNested.cs @@ -0,0 +1,49 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// Nested context. + public class ContextDescriptorNested : ContextDescriptor + { + /// Ctor. + public ContextDescriptorNested() + { + Contexts = new List(); + } + + /// Ctor. + /// the nested contexts + public ContextDescriptorNested(IList contexts) + { + Contexts = contexts; + } + + /// Returns the list of nested contexts + /// contexts + public IList Contexts { get; set; } + + public void ToEPL(TextWriter writer, EPStatementFormatter formatter) + { + String delimiter = ""; + foreach (CreateContextClause context in Contexts) + { + writer.Write(delimiter); + writer.Write("context "); + writer.Write(context.ContextName); + writer.Write(" as "); + context.Descriptor.ToEPL(writer, formatter); + delimiter = ", "; + } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/soda/CountEverProjectionExpression.cs b/NEsper.Core/NEsper.Core/client/soda/CountEverProjectionExpression.cs new file mode 100755 index 000000000..e9af9cb26 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/CountEverProjectionExpression.cs @@ -0,0 +1,71 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.client.soda +{ + /// + /// Represents the "countever" aggregation function. + /// + [Serializable] + public class CountEverProjectionExpression : ExpressionBase + { + private bool _distinct; + + /// + /// Ctor. + /// + public CountEverProjectionExpression() { + } + + /// + /// Ctor. + /// + /// true for distinct + public CountEverProjectionExpression(bool isDistinct) + { + _distinct = isDistinct; + } + + /// + /// Ctor. + /// + /// to aggregate + /// true for distinct + public CountEverProjectionExpression(Expression expression, bool isDistinct) + { + _distinct = isDistinct; + Children.Add(expression); + } + + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.UNARY; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + RenderAggregation(writer, "countever", _distinct, Children); + } + + /// + /// Returns true for distinct. + /// + /// boolean indicating distinct or not + public bool IsDistinct + { + get { return _distinct; } + set { _distinct = value; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/CountProjectionExpression.cs b/NEsper.Core/NEsper.Core/client/soda/CountProjectionExpression.cs new file mode 100755 index 000000000..d06d0852a --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/CountProjectionExpression.cs @@ -0,0 +1,65 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.client.soda +{ + /// + /// Count of the (distinct) values returned by an expression, equivalent to "count(distinct property)" + /// + [Serializable] + public class CountProjectionExpression : ExpressionBase + { + /// + /// Ctor. + /// + public CountProjectionExpression() { + } + + /// + /// Ctor - for use to create an expression tree, without inner expression + /// + /// true if distinct + public CountProjectionExpression(bool isDistinct) + { + this.IsDistinct = isDistinct; + } + + /// + /// Ctor - adds the expression to project. + /// + /// returning values to project + /// true if distinct + public CountProjectionExpression(Expression expression, bool isDistinct) + { + this.IsDistinct = isDistinct; + this.Children.Add(expression); + } + + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.UNARY; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + ExpressionBase.RenderAggregation(writer, "count", IsDistinct, this.Children); + } + + /// + /// Returns true if the projection considers distinct values only. + /// + /// true if distinct + public bool IsDistinct { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/CountStarProjectionExpression.cs b/NEsper.Core/NEsper.Core/client/soda/CountStarProjectionExpression.cs new file mode 100755 index 000000000..f14152b48 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/CountStarProjectionExpression.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// Count of (distinct) rows, equivalent to "count(*)" + [Serializable] + public class CountStarProjectionExpression : ExpressionBase + { + /// Ctor - for use to create an expression tree, without inner expression. + public CountStarProjectionExpression() + { + } + + /// + /// Gets the Precedence. + /// + /// The Precedence. + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.UNARY; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + ExpressionBase.RenderAggregation(writer, "count", false, Children); + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/CreateContextClause.cs b/NEsper.Core/NEsper.Core/client/soda/CreateContextClause.cs new file mode 100755 index 000000000..053333166 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/CreateContextClause.cs @@ -0,0 +1,52 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + + +namespace com.espertech.esper.client.soda +{ + /// Create a context. + [Serializable] + public class CreateContextClause + { + /// Ctor. + public CreateContextClause() + { + } + + /// Ctor. + /// context name + /// context dimension descriptor + public CreateContextClause(String contextName, ContextDescriptor descriptor) + { + ContextName = contextName; + Descriptor = descriptor; + } + + /// Returns the context name + /// context name + public string ContextName { get; set; } + + /// Returns the context dimension informatin + /// context descriptor + public ContextDescriptor Descriptor { get; set; } + + /// RenderAny as EPL. + /// to output to + /// formatter + public void ToEPL(TextWriter writer, EPStatementFormatter formatter) + { + writer.Write("create context "); + writer.Write(ContextName); + writer.Write(" as "); + Descriptor.ToEPL(writer, formatter); + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/CreateDataFlowClause.cs b/NEsper.Core/NEsper.Core/client/soda/CreateDataFlowClause.cs new file mode 100755 index 000000000..c5ff472ff --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/CreateDataFlowClause.cs @@ -0,0 +1,69 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// Represents a create-variable syntax for creating a new variable. + [Serializable] + public class CreateDataFlowClause + { + /// Ctor. + public CreateDataFlowClause() { + } + + /// Ctor. + /// data flow name + /// schemas + /// operators + public CreateDataFlowClause(String dataFlowName, IList schemas, IList operators) + { + DataFlowName = dataFlowName; + Schemas = schemas; + Operators = operators; + } + + /// Returns the data flow name. + /// name + public string DataFlowName { get; set; } + + /// Returns schemas. + /// schemas + public IList Schemas { get; set; } + + /// Returns operators. + /// operator definitions + public IList Operators { get; set; } + + /// RenderAny as EPL. + /// to output to + /// to use + public void ToEPL(TextWriter writer, EPStatementFormatter formatter) + { + writer.Write("create dataflow "); + writer.Write(DataFlowName); + if (Schemas != null) { + foreach (CreateSchemaClause clause in Schemas) { + formatter.BeginDataFlowSchema(writer); + clause.ToEPL(writer); + writer.Write(","); + } + } + if (Operators != null) { + formatter.BeginDataFlowOperator(writer); + foreach (DataFlowOperator clause in Operators) { + clause.ToEPL(writer, formatter); + } + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/CreateExpressionClause.cs b/NEsper.Core/NEsper.Core/client/soda/CreateExpressionClause.cs new file mode 100755 index 000000000..2f0a9f11c --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/CreateExpressionClause.cs @@ -0,0 +1,64 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Clause for creating an expression for use across one or more statements. + /// + /// Both expressions and scripts can be created using this clause. + /// + [Serializable] + public class CreateExpressionClause + { + /// Ctor. + public CreateExpressionClause() + { + } + + /// Ctor. + /// expression + public CreateExpressionClause(ExpressionDeclaration expressionDeclaration) + { + ExpressionDeclaration = expressionDeclaration; + } + + /// Ctor. + /// script + public CreateExpressionClause(ScriptExpression scriptExpression) + { + ScriptExpression = scriptExpression; + } + + /// Returns the expression declaration or null if script instead. + /// expression declaration + public ExpressionDeclaration ExpressionDeclaration { get; set; } + + /// Returns the script expression or null if declaring an EPL expression. + /// script expression + public ScriptExpression ScriptExpression { get; set; } + + /// EPL output + /// to write to + public void ToEPL(TextWriter writer) + { + writer.Write("create "); + if (ExpressionDeclaration != null) + { + ExpressionDeclaration.ToEPL(writer); + } + else + { + ScriptExpression.ToEPL(writer); + } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/soda/CreateIndexClause.cs b/NEsper.Core/NEsper.Core/client/soda/CreateIndexClause.cs new file mode 100755 index 000000000..a4faf6847 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/CreateIndexClause.cs @@ -0,0 +1,140 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// Create an index on a named window. + [Serializable] + public class CreateIndexClause + { + /// Ctor. + public CreateIndexClause() + { + Columns = new List(); + } + + /// Ctor. + /// index name + /// named window name + /// columns indexed + public CreateIndexClause(String indexName, String windowName, List columns) + { + } + + /// + /// Ctor. + /// + /// index name + /// named window name + /// columns indexed + /// if set to true [is unique]. + public CreateIndexClause(String indexName, String windowName, List columns, bool isUnique) + { + IndexName = indexName; + WindowName = windowName; + Columns = columns; + IsUnique = isUnique; + } + + + /// Ctor. + /// is the name of the window to create + /// index name + /// properties to index + public CreateIndexClause(String indexName, String windowName, String[] properties) + : this(indexName, windowName, properties, false) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// is the name of the window to create + /// index name + /// properties to index + /// if set to true [is unique]. + public CreateIndexClause(String indexName, String windowName, String[] properties, bool isUnique) + { + Columns = new List(); + IndexName = indexName; + WindowName = windowName; + IsUnique = isUnique; + foreach (String prop in properties) + { + Columns.Add(new CreateIndexColumn(prop)); + } + } + + /// Returns index name. + /// name of index + public string IndexName { get; set; } + + /// Returns window name. + /// name of window + public string WindowName { get; set; } + + /// Returns columns. + /// columns + public List Columns { get; set; } + + /// + /// Gets or sets a value indicating whether this instance is unique. + /// + /// true if this instance is unique; otherwise, false. + public bool IsUnique { get; set; } + + /// Creates a clause to create a named window. + /// is the name of the named window + /// properties to index + /// name of index + /// create variable clause + public static CreateIndexClause Create(String indexName, String windowName, params String[] properties) + { + return new CreateIndexClause(indexName, windowName, properties); + } + + /// Creates a clause to create a named window. + /// is the name of the named window + /// properties to index + /// name of index + /// for unique index + /// create variable clause + public static CreateIndexClause Create(bool unique, String indexName, String windowName, params String[] properties) + { + return new CreateIndexClause(indexName, windowName, properties, unique); + } + + /// Renders the clause in textual representation. + /// to output to + public void ToEPL(TextWriter writer) + { + writer.Write("create "); + if (IsUnique) + { + writer.Write("unique "); + } + writer.Write("index "); + writer.Write(IndexName); + writer.Write(" on "); + writer.Write(WindowName); + writer.Write('('); + String delimiter = ""; + foreach (CreateIndexColumn prop in Columns) + { + writer.Write(delimiter); + prop.ToEPL(writer); + delimiter = ", "; + } + writer.Write(')'); + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/CreateIndexColumn.cs b/NEsper.Core/NEsper.Core/client/soda/CreateIndexColumn.cs new file mode 100755 index 000000000..1415f7268 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/CreateIndexColumn.cs @@ -0,0 +1,71 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// Create an index on a named window. + [Serializable] + public class CreateIndexColumn + { + /// Ctor. + public CreateIndexColumn() + { + IndexColumnType = CreateIndexColumnType.HASH; + } + + /// + /// Ctor. + /// + /// column name + public CreateIndexColumn(string columnName) + { + IndexColumnType = CreateIndexColumnType.HASH; + ColumnName = columnName; + } + + /// + /// Ctor. + /// + /// colum name + /// index type + public CreateIndexColumn(string columnName, CreateIndexColumnType type) + { + ColumnName = columnName; + IndexColumnType = type; + } + + /// + /// Renders the clause in textual representation. + /// + /// to output to + public void ToEPL(TextWriter writer) + { + writer.Write(ColumnName); + if (IndexColumnType != CreateIndexColumnType.HASH) + { + writer.Write(' '); + writer.Write(IndexColumnType.ToString().ToLowerInvariant()); + } + } + + /// + /// Returns the column name. + /// + /// column name + public string ColumnName { get; set; } + + /// + /// Returns the index type. + /// + /// index type + public CreateIndexColumnType IndexColumnType { get; set; } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/client/soda/CreateIndexColumnType.cs b/NEsper.Core/NEsper.Core/client/soda/CreateIndexColumnType.cs new file mode 100755 index 000000000..1157dbe34 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/CreateIndexColumnType.cs @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.client.soda +{ + /// Enumeration to represents the index type. + public enum CreateIndexColumnType + { + /// Hash-index. + HASH, + + /// Binary-tree (sorted) index. + BTREE + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/CreateSchemaClause.cs b/NEsper.Core/NEsper.Core/client/soda/CreateSchemaClause.cs new file mode 100755 index 000000000..c61b29600 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/CreateSchemaClause.cs @@ -0,0 +1,163 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.client.soda +{ + /// Represents a create-schema syntax for creating a new event type. + [Serializable] + public class CreateSchemaClause + { + /// Ctor. + public CreateSchemaClause() { + } + + /// Ctor. + /// name of type + /// are for model-after, could be multiple when declaring a variant stream, or a single fully-qualified class name + /// type definition + public CreateSchemaClause(String schemaName, ICollection types, CreateSchemaClauseTypeDef typeDefinition) + { + SchemaName = schemaName; + Types = types; + TypeDefinition = typeDefinition; + } + + /// Ctor. + /// name of type + /// column definition + /// inherited types, if any + public CreateSchemaClause(String schemaName, IList columns, ICollection inherits) + { + SchemaName = schemaName; + Columns = columns; + Inherits = inherits; + } + + /// Ctor. + /// name of type + /// are for model-after, could be multiple when declaring a variant stream, or a single fully-qualified class name + /// for variant streams, map or object array + /// column definition + /// inherited types, if any + public CreateSchemaClause(String schemaName, ICollection types, IList columns, ICollection inherits, CreateSchemaClauseTypeDef typeDefinition) + { + SchemaName = schemaName; + Types = types; + Columns = columns; + Inherits = inherits; + TypeDefinition = typeDefinition; + } + + /// Returns the type name, aka. schema name. + /// type name + public string SchemaName { get; set; } + + /// Returns model-after types, i.e. (fully-qualified) class name or event type Name(s), multiple for variant types. + /// type names or class names + public ICollection Types { get; set; } + + /// Returns the column definition. + /// column def + public IList Columns { get; set; } + + /// Returns the names of event types inherited from, if any + /// types inherited + public ICollection Inherits { get; set; } + + /// + /// Gets or sets the type definition. + /// + /// The type definition. + public CreateSchemaClauseTypeDef? TypeDefinition { get; set; } + + /// Returns the property name of the property providing the start timestamp value. + /// start timestamp property name + public string StartTimestampPropertyName { get; set; } + + /// Returns the property name of the property providing the end timestamp value. + /// end timestamp property name + public string EndTimestampPropertyName { get; set; } + + /// Returns the optional set of event type names that properties are copied from. + /// copy-from event types + public ICollection CopyFrom { get; set; } + + /// + /// Gets or sets the id of expression assigned by tools. + /// + /// The name of the tree object. + public string TreeObjectName { get; set; } + + /// RenderAny as EPL. + /// to output to + public void ToEPL(TextWriter writer) + { + writer.Write("create"); + if (TypeDefinition != null) + { + TypeDefinition.Value.Write(writer); + } + writer.Write(" schema "); + writer.Write(SchemaName); + writer.Write(" as "); + if ((Types != null) && (Types.IsNotEmpty())) { + String delimiter = ""; + foreach (String type in Types) { + writer.Write(delimiter); + writer.Write(type); + delimiter = ", "; + } + } + else { + writer.Write("("); + String delimiter = ""; + foreach (SchemaColumnDesc col in Columns) { + writer.Write(delimiter); + col.ToEPL(writer); + delimiter = ", "; + } + writer.Write(")"); + } + + if ((Inherits != null) && (Inherits.IsNotEmpty())) { + writer.Write(" inherits "); + String delimiter = ""; + foreach (String name in Inherits) { + writer.Write(delimiter); + writer.Write(name); + delimiter = ", "; + } + } + + if (StartTimestampPropertyName != null) { + writer.Write(" starttimestamp "); + writer.Write(StartTimestampPropertyName); + } + if (EndTimestampPropertyName != null) { + writer.Write(" endtimestamp "); + writer.Write(EndTimestampPropertyName); + } + + if ((CopyFrom != null) && (CopyFrom.IsNotEmpty())) { + writer.Write(" copyFrom "); + String delimiter = ""; + foreach (String name in CopyFrom) { + writer.Write(delimiter); + writer.Write(name); + delimiter = ", "; + } + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/CreateSchemaClauseTypeDef.cs b/NEsper.Core/NEsper.Core/client/soda/CreateSchemaClauseTypeDef.cs new file mode 100755 index 000000000..2bc0ac6c4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/CreateSchemaClauseTypeDef.cs @@ -0,0 +1,64 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// Represents a type definition for use with the create-schema syntax for creating a new event type. + [Serializable] + public enum CreateSchemaClauseTypeDef + { + /// Variant type. + VARIANT, + + /// Map underlying type. + MAP, + + /// Object-array underlying type. + OBJECTARRAY, + + /// Avro-array underlying type. + AVRO, + + /// Undefined (system default) underlying type. + NONE + } + + public static class CreateSchemaClauseTypeDefExtensions + { + /// + /// Write keyword according to type def. + /// + /// The type def. + /// to write to + public static void Write(this CreateSchemaClauseTypeDef typeDef, TextWriter writer) + { + switch (typeDef) + { + case CreateSchemaClauseTypeDef.VARIANT: + writer.Write(" variant"); + break; + case CreateSchemaClauseTypeDef.MAP: + writer.Write(" map"); + break; + case CreateSchemaClauseTypeDef.OBJECTARRAY: + writer.Write(" objectarray"); + break; + case CreateSchemaClauseTypeDef.AVRO: + writer.Write(" avro"); + break; + case CreateSchemaClauseTypeDef.NONE: + break; + default: + throw new ArgumentException("invalid value", nameof(typeDef)); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/CreateTableClause.cs b/NEsper.Core/NEsper.Core/client/soda/CreateTableClause.cs new file mode 100755 index 000000000..674a9a6b2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/CreateTableClause.cs @@ -0,0 +1,89 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.client.soda +{ + /// + /// Represents a create-variable syntax for creating a new variable. + /// + [Serializable] + public class CreateTableClause + { + private string _tableName; + private IList _columns; + + /// + /// Ctor. + /// + public CreateTableClause() { + } + + /// + /// Ctor. + /// + /// the table name + public CreateTableClause(string tableName) { + this._tableName = tableName; + } + + /// + /// Ctor. + /// + /// the table name + /// table columns + public CreateTableClause(string tableName, IList columns) { + this._tableName = tableName; + this._columns = columns; + } + + /// + /// Returns the table name + /// + /// table name + public string TableName + { + get { return _tableName; } + set { this._tableName = value; } + } + + /// + /// Returns the table columns + /// + /// table columns + public IList Columns + { + get { return _columns; } + set { this._columns = value; } + } + + /// + /// RenderAny create-table clause + /// + /// to render to + public void ToEPL(TextWriter writer) + { + writer.Write("create table "); + writer.Write(_tableName); + writer.Write(" ("); + string delimiter = ""; + foreach (CreateTableColumn col in _columns) { + writer.Write(delimiter); + col.ToEPL(writer); + delimiter = ", "; + } + writer.Write(")"); + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/CreateTableColumn.cs b/NEsper.Core/NEsper.Core/client/soda/CreateTableColumn.cs new file mode 100755 index 000000000..49ecdf8f6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/CreateTableColumn.cs @@ -0,0 +1,134 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.client.soda +{ + /// + /// Table column in a create-table statement. + /// + [Serializable] + public class CreateTableColumn + { + /// + /// Ctor. + /// + /// the table column name + /// an optional aggregation expression (exclusive of type name) + /// a type name (exclusive of aggregation expression) + /// flag whether type is array + /// flag whether array of primitive (requires array flag) + /// optional annotations + /// flag indicating whether the column is a primary key + public CreateTableColumn(string columnName, Expression optionalExpression, string optionalTypeName, bool? optionalTypeIsArray, bool? optionalTypeIsPrimitiveArray, IList annotations, bool? primaryKey) + { + ColumnName = columnName; + OptionalExpression = optionalExpression; + OptionalTypeName = optionalTypeName; + OptionalTypeIsArray = optionalTypeIsArray; + OptionalTypeIsPrimitiveArray = optionalTypeIsPrimitiveArray; + Annotations = annotations; + PrimaryKey = primaryKey; + } + + /// + /// Ctor. + /// + public CreateTableColumn() + { + } + + /// + /// Returns the table column name + /// + /// column name + public string ColumnName { get; set; } + + /// + /// Returns optional annotations, or null if there are none + /// + /// annotations + public IList Annotations { get; set; } + + /// + /// Returns the aggragtion expression, if the type of the column is aggregation, + /// or null if a type name is provided instead. + /// + /// expression + public Expression OptionalExpression { get; set; } + + /// + /// Returns the type name, or null if the column is an aggregation and an + /// aggregation expression is provided instead. + /// + /// type name + public string OptionalTypeName { get; set; } + + /// + /// Returns indicator whether type is an array type, applicable only if a type name is provided + /// + /// array type indicator + public bool? OptionalTypeIsArray { get; set; } + + /// + /// Returns indicator whether the column is a primary key + /// + /// primary key indicator + public bool? PrimaryKey { get; set; } + + /// + /// Returns indicator whether the array is an array of primitives or boxed types (only when a type name is provided and array flag set) + /// + /// primitive array indicator + public bool? OptionalTypeIsPrimitiveArray { get; set; } + + /// + /// RenderAny create-table column + /// + /// to render to + public void ToEPL(TextWriter writer) + { + writer.Write(ColumnName); + writer.Write(" "); + if (OptionalExpression != null) { + OptionalExpression.ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + } + else { + writer.Write(OptionalTypeName); + if (OptionalTypeIsArray != null && OptionalTypeIsArray.Value) { + if (OptionalTypeIsPrimitiveArray != null && OptionalTypeIsPrimitiveArray.Value) { + writer.Write("[primitive]"); + } + else { + writer.Write("[]"); + } + } + if (PrimaryKey.GetValueOrDefault()) { + writer.Write(" primary key"); + } + } + if (Annotations != null && !Annotations.IsEmpty()) { + writer.Write(" "); + string delimiter = ""; + foreach (AnnotationPart part in Annotations) { + if (part.Name == null) { + continue; + } + writer.Write(delimiter); + delimiter = " "; + part.ToEPL(writer); + } + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/CreateVariableClause.cs b/NEsper.Core/NEsper.Core/client/soda/CreateVariableClause.cs new file mode 100755 index 000000000..75ef15117 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/CreateVariableClause.cs @@ -0,0 +1,140 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Represents a create-variable syntax for creating a new variable. + /// + [Serializable] + public class CreateVariableClause + { + /// + /// Ctor. + /// + public CreateVariableClause() { + } + + /// + /// Ctor. + /// + /// variable name + public CreateVariableClause(string variableName) { + VariableName = variableName; + } + + /// + /// Creates a create-variable syntax for declaring a variable. + /// + /// is the variable type name + /// is the name of the variable + /// create-variable clause + public static CreateVariableClause Create(string variableType, string variableName) + { + return new CreateVariableClause(variableType, variableName, null, false); + } + + /// + /// Creates a create-variable syntax for declaring a variable. + /// + /// is the variable type name + /// is the name of the variable + /// is the assignment expression supplying the initial value + /// create-variable clause + public static CreateVariableClause Create(string variableType, string variableName, Expression expression) + { + return new CreateVariableClause(variableType, variableName, expression, false); + } + + /// + /// Ctor. + /// + /// is the variable type name + /// is the name of the variable + /// is the optional assignment expression supplying the initial value, or null if theinitial value is null + /// + /// true for constant, false for regular variable + public CreateVariableClause(string variableType, string variableName, Expression optionalAssignment, bool constant) + { + VariableType = variableType; + VariableName = variableName; + OptionalAssignment = optionalAssignment; + IsConstant = constant; + } + + /// + /// Returns the variable type name. + /// + /// type of the variable + public string VariableType { get; set; } + + /// + /// Returns the variable name. + /// + /// name of the variable + public string VariableName { get; set; } + + /// + /// Returns the optional assignment expression, or null to initialize to a null value + /// + /// assignment expression, if present + public Expression OptionalAssignment { get; set; } + + /// + /// Returns indicator whether the variable is a constant. + /// + /// constant false + public bool IsConstant { get; set; } + + /// + /// Returns indictor whether array or not array. + /// + /// array indicator + public bool IsArray { get; set; } + + /// + /// Returns true for array of primitive values (also set the array flag) + /// + /// indicator + public bool IsArrayOfPrimitive { get; set; } + + /// + /// RenderAny as EPL. + /// + /// to output to + public virtual void ToEPL(TextWriter writer) + { + writer.Write("create"); + if (IsConstant) { + writer.Write(" constant"); + } + writer.Write(" variable "); + if (VariableType != null) { + writer.Write(VariableType); + if (IsArray) { + if (IsArrayOfPrimitive) { + writer.Write("[primitive]"); + } + else { + writer.Write("[]"); + } + } + writer.Write(" "); + } + writer.Write(VariableName); + if (OptionalAssignment != null) + { + writer.Write(" = "); + OptionalAssignment.ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/CreateWindowClause.cs b/NEsper.Core/NEsper.Core/client/soda/CreateWindowClause.cs new file mode 100755 index 000000000..3e2b23a16 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/CreateWindowClause.cs @@ -0,0 +1,237 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.client.soda +{ + /// + /// Create a named window, defining the parameter of the named window such as window name and data window view Name(s). + /// + [Serializable] + public class CreateWindowClause { + /// Ctor. + public CreateWindowClause() + { + Columns = new List(); + Views = new List(); + } + + /// + /// Ctor. + /// + /// is the name of the window to create + /// is the list of data window views + public CreateWindowClause(string windowName, View[] viewArr) { + Columns = new List(); + WindowName = windowName; + Views = new List(); + if (viewArr != null) { + Views.AddAll(viewArr); + } + } + + /// + /// Ctor. + /// + /// is the name of the window to create + /// is a list of data window views + public CreateWindowClause(string windowName, IList views) { + Columns = new List(); + WindowName = windowName; + Views = views; + } + + /// + /// Creates a clause to create a named window. + /// + /// is the name of the named window + /// is a data window view + /// create window clause + public static CreateWindowClause Create(string windowName, View view) { + return new CreateWindowClause(windowName, new View[]{view}); + } + + /// + /// Creates a clause to create a named window. + /// + /// is the name of the named window + /// is the data window views + /// create window clause + public static CreateWindowClause Create(string windowName, params View[] views) { + return new CreateWindowClause(windowName, views); + } + + /// + /// Adds an un-parameterized view to the named window. + /// + /// is the view namespace, for example "win" for most data windows + /// is the view name, for example "length" for a length window + /// named window creation clause + public CreateWindowClause AddView(string @namespace, string name) { + Views.Add(View.Create(@namespace, name)); + return this; + } + + /// + /// Adds an un-parameterized view to the named window. + /// + /// is the view name, for example "length" for a length window + /// named window creation clause + public CreateWindowClause AddView(string name) { + Views.Add(View.Create(null, name)); + return this; + } + + /// + /// Adds a parameterized view to the named window. + /// + /// is the view namespace, for example "win" for most data windows + /// is the view name, for example "length" for a length window + /// is a list of view parameters + /// named window creation clause + public CreateWindowClause AddView(string @namespace, string name, List parameters) { + Views.Add(View.Create(@namespace, name, parameters)); + return this; + } + + /// + /// Adds a parameterized view to the named window. + /// + /// is the view name, for example "length" for a length window + /// is a list of view parameters + /// named window creation clause + public CreateWindowClause AddView(string name, List parameters) { + Views.Add(View.Create(name, parameters)); + return this; + } + + /// + /// Adds a parameterized view to the named window. + /// + /// is the view namespace, for example "win" for most data windows + /// is the view name, for example "length" for a length window + /// is a list of view parameters + /// named window creation clause + public CreateWindowClause AddView(string @namespace, string name, params Expression[] parameters) { + Views.Add(View.Create(@namespace, name, parameters)); + return this; + } + + /// + /// Adds a parameterized view to the named window. + /// + /// is the view name, for example "length" for a length window + /// is a list of view parameters + /// named window creation clause + public CreateWindowClause AddView(string name, params Expression[] parameters) { + Views.Add(View.Create(null, name, parameters)); + return this; + } + + /// + /// Renders the clause in textual representation. + /// + /// to output to + public void ToEPL(TextWriter writer) { + writer.Write("create window "); + writer.Write(WindowName); + ProjectedStream.ToEPLViews(writer, Views); + } + + /// + /// Renders the clause in textual representation. + /// + /// to output to + public void ToEPLInsertPart(TextWriter writer) { + if (IsInsert) { + writer.Write(" insert"); + if (InsertWhereClause != null) { + writer.Write(" where "); + InsertWhereClause.ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + } + } + } + + /// + /// Returns the window name. + /// + /// window name + public string WindowName { get; set; } + + /// + /// Returns the views onto the named window. + /// + /// named window data views + public IList Views { get; set; } + + /// + /// Returns true if inserting from another named window, false if not. + /// + /// insert from named window + public bool IsInsert { get; set; } + + /// + /// Filter expression for inserting from another named window, or null if not inserting from another named window. + /// + /// filter expression + public Expression InsertWhereClause { get; set; } + + /// + /// Returns all columns for use when create-table syntax is used to define the named window type. + /// + /// columns + public IList Columns { get; set; } + + /// + /// Adds a column for use when create-table syntax is used to define the named window type. + /// + /// column to add + public void AddColumn(SchemaColumnDesc col) + { + Columns.Add(col); + } + + public CreateWindowClause SetColumns(IList value) + { + Columns = value; + return this; + } + + public CreateWindowClause SetIsInsert(bool value) + { + IsInsert = value; + return this; + } + + public CreateWindowClause SetInsertWhereClause(Expression value) + { + InsertWhereClause = value; + return this; + } + + /// + /// To-EPL for create-table syntax. + /// + /// to use + public void ToEPLCreateTablePart(TextWriter writer) { + string delimiter = ""; + writer.Write('('); + foreach (SchemaColumnDesc col in Columns) { + writer.Write(delimiter); + col.ToEPL(writer); + delimiter = ", "; + } + writer.Write(')'); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/client/soda/CrontabFrequencyExpression.cs b/NEsper.Core/NEsper.Core/client/soda/CrontabFrequencyExpression.cs new file mode 100755 index 000000000..566915509 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/CrontabFrequencyExpression.cs @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// Frequency expression for use in crontab expressions. + [Serializable] + public class CrontabFrequencyExpression : ExpressionBase + { + /// Ctor. + public CrontabFrequencyExpression() + { + } + + /// Ctor. + /// the frequency value + public CrontabFrequencyExpression(Expression numericParameter) + { + Children.Add(numericParameter); + } + + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.UNARY; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + writer.Write("*/"); + Children[0].ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/soda/CrontabParameterExpression.cs b/NEsper.Core/NEsper.Core/client/soda/CrontabParameterExpression.cs new file mode 100755 index 000000000..bd0ef6ac7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/CrontabParameterExpression.cs @@ -0,0 +1,52 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.client.soda +{ + /// + /// Parameter expression such as last/lastweek/weekday/wildcard for use in crontab expressions. + /// + [Serializable] + public class CrontabParameterExpression : ExpressionBase + { + /// Ctor. + public CrontabParameterExpression() + { + } + + /// Ctor. + /// of crontab parameter + public CrontabParameterExpression(ScheduleItemType type) + { + ItemType = type; + } + + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.UNARY; } + } + + /// Returns crontab parameter type. + /// crontab parameter type + public ScheduleItemType ItemType { get; set; } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + if (!Children.IsEmpty()) + { + Children[0].ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + writer.Write(' '); + } + writer.Write(ItemType.GetSyntax()); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/soda/CrontabParameterSetExpression.cs b/NEsper.Core/NEsper.Core/client/soda/CrontabParameterSetExpression.cs new file mode 100755 index 000000000..f32c6d374 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/CrontabParameterSetExpression.cs @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// An expression for use in crontab provides all child expression as part of a parameter list. + /// + [Serializable] + public class CrontabParameterSetExpression : ExpressionBase + { + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.UNARY; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + String delimiter = ""; + writer.Write("["); + foreach (Expression expr in Children) + { + writer.Write(delimiter); + expr.ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + delimiter = ","; + } + writer.Write("]"); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/soda/CrontabRangeExpression.cs b/NEsper.Core/NEsper.Core/client/soda/CrontabRangeExpression.cs new file mode 100755 index 000000000..73ffc9b39 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/CrontabRangeExpression.cs @@ -0,0 +1,47 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Parameter expression for use in crontab expressions and representing a range. + /// + [Serializable] + public class CrontabRangeExpression : ExpressionBase + { + /// Ctor. + public CrontabRangeExpression() + { + } + + /// Ctor. + /// provides lower bounds + /// provides upper bounds + public CrontabRangeExpression(Expression lowerBounds, + Expression upperBounds) + { + Children.Add(lowerBounds); + Children.Add(upperBounds); + } + + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.UNARY; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + Children[0].ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + writer.Write(":"); + Children[1].ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/soda/CurrentEvaluationContextExpression.cs b/NEsper.Core/NEsper.Core/client/soda/CurrentEvaluationContextExpression.cs new file mode 100755 index 000000000..4f60c6bac --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/CurrentEvaluationContextExpression.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Current execution context supplies the current expression execution context. + /// + [Serializable] + public class CurrentEvaluationContextExpression : ExpressionBase + { + /// + /// Ctor. + /// + public CurrentEvaluationContextExpression() + { + } + + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.UNARY; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + writer.Write("current_evaluation_context()"); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/client/soda/CurrentTimestampExpression.cs b/NEsper.Core/NEsper.Core/client/soda/CurrentTimestampExpression.cs new file mode 100755 index 000000000..10bad16f3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/CurrentTimestampExpression.cs @@ -0,0 +1,35 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Current timestamp supplies the current engine time in an expression. + /// + [Serializable] + public class CurrentTimestampExpression : ExpressionBase + { + /// Ctor. + public CurrentTimestampExpression() + { + } + + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.UNARY; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + writer.Write("current_timestamp()"); + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/client/soda/DataFlowOperator.cs b/NEsper.Core/NEsper.Core/client/soda/DataFlowOperator.cs new file mode 100755 index 000000000..48bba9813 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/DataFlowOperator.cs @@ -0,0 +1,183 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.client.soda +{ + /// Object model of a data flow operator declaration. + [Serializable] + public class DataFlowOperator + { + /// Ctor + /// annotations + /// operator name + /// input stream definitions + /// output stream definitions + /// parameters + public DataFlowOperator(IList annotations, + String operatorName, + IList input, + IList output, + IList parameters) + { + Annotations = annotations; + OperatorName = operatorName; + Input = input; + Output = output; + Parameters = parameters; + } + + /// Ctor. + public DataFlowOperator() + { + } + + /// Returns the annotations. + /// annotations + public IList Annotations { get; set; } + + /// Returns the operator name. + /// operator name + public string OperatorName { get; set; } + + /// Returns the input stream definitions, if any. + /// input streams + public IList Input { get; set; } + + /// Returns the output stream definitions, if any. + /// output streams + public IList Output { get; set; } + + /// + /// Returns operator parameters. Object values may be expressions, constants, JSON values or EPL statements. + /// + /// map of parameters + public IList Parameters { get; set; } + + /// RenderAny to string. + /// to render + /// for formatting + public void ToEPL(TextWriter writer, EPStatementFormatter formatter) + { + writer.Write(OperatorName); + + if (Input.IsNotEmpty()) + { + writer.Write("("); + String delimiter = ""; + foreach (DataFlowOperatorInput inputItem in Input) + { + writer.Write(delimiter); + WriteInput(inputItem, writer); + if (inputItem.OptionalAsName != null) + { + writer.Write(" as "); + writer.Write(inputItem.OptionalAsName); + } + delimiter = ", "; + } + writer.Write(")"); + } + + if (Output.IsNotEmpty()) + { + writer.Write(" -> "); + String delimiter = ""; + foreach (DataFlowOperatorOutput outputItem in Output) + { + writer.Write(delimiter); + writer.Write(outputItem.StreamName); + WriteTypes(outputItem.TypeInfo, writer); + delimiter = ", "; + } + } + + if (Parameters.IsEmpty()) + { + writer.Write(" {}"); + formatter.EndDataFlowOperatorDetails(writer); + } + else + { + writer.Write(" {"); + formatter.BeginDataFlowOperatorDetails(writer); + String delimiter = ","; + int count = 0; + foreach (DataFlowOperatorParameter parameter in Parameters) + { + parameter.ToEPL(writer); + count++; + if (Parameters.Count > count) + { + writer.Write(delimiter); + formatter.EndDataFlowOperatorConfig(writer); + } + + formatter.EndDataFlowOperatorConfig(writer); + } + + writer.Write("}"); + formatter.EndDataFlowOperatorDetails(writer); + } + } + + private void WriteInput(DataFlowOperatorInput inputItem, TextWriter writer) + { + if (inputItem.InputStreamNames.Count > 1) + { + String delimiterNames = ""; + writer.Write("("); + foreach (String name in inputItem.InputStreamNames) + { + writer.Write(delimiterNames); + writer.Write(name); + delimiterNames = ", "; + } + writer.Write(")"); + } + else + { + writer.Write(inputItem.InputStreamNames[0]); + } + } + + private void WriteTypes(ICollection types, TextWriter writer) + { + if (types == null || types.IsEmpty()) + { + return; + } + + writer.Write("<"); + String typeDelimiter = ""; + foreach (DataFlowOperatorOutputType type in types) + { + writer.Write(typeDelimiter); + WriteType(type, writer); + typeDelimiter = ","; + } + writer.Write(">"); + } + + private void WriteType(DataFlowOperatorOutputType type, TextWriter writer) + { + if (type.IsWildcard) + { + writer.Write('?'); + return; + } + writer.Write(type.TypeOrClassname); + WriteTypes(type.TypeParameters, writer); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/soda/DataFlowOperatorInput.cs b/NEsper.Core/NEsper.Core/client/soda/DataFlowOperatorInput.cs new file mode 100755 index 000000000..36bbd34b2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/DataFlowOperatorInput.cs @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.client.soda +{ + /// Represents an input port of an operator. + [Serializable] + public class DataFlowOperatorInput + { + /// Ctor. + public DataFlowOperatorInput() + { + } + + /// Ctor. + /// names of input streams for the same port + /// optional alias + public DataFlowOperatorInput(IList inputStreamNames, String optionalAsName) + { + InputStreamNames = inputStreamNames; + OptionalAsName = optionalAsName; + } + + /// Returns the input stream names. + /// input stream names + public IList InputStreamNames { get; set; } + + /// Returns the alias name. + /// alias + public string OptionalAsName { get; set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/soda/DataFlowOperatorOutput.cs b/NEsper.Core/NEsper.Core/client/soda/DataFlowOperatorOutput.cs new file mode 100755 index 000000000..774cdcd57 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/DataFlowOperatorOutput.cs @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.client.soda +{ + /// Represents an output port of an operator. + [Serializable] + public class DataFlowOperatorOutput + { + /// Ctor. + public DataFlowOperatorOutput() + { + } + + /// Ctor. + /// output stream name + /// type information + public DataFlowOperatorOutput(String streamName, IList typeInfo) + { + StreamName = streamName; + TypeInfo = typeInfo; + } + + /// Returns the output stream name. + /// stream name. + public string StreamName { get; set; } + + /// Returns output port type information + /// type INFO + public IList TypeInfo { get; set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/soda/DataFlowOperatorOutputType.cs b/NEsper.Core/NEsper.Core/client/soda/DataFlowOperatorOutputType.cs new file mode 100755 index 000000000..56046e93a --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/DataFlowOperatorOutputType.cs @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.client.soda +{ + /// Represents type information for data flow operators. + [Serializable] + public class DataFlowOperatorOutputType + { + /// Ctor. + public DataFlowOperatorOutputType() + { + } + + /// Ctor. + /// true for wildcard type + /// type name + /// optional additional type parameters + public DataFlowOperatorOutputType(bool wildcard, + String typeOrClassname, + IList typeParameters) + { + IsWildcard = wildcard; + TypeOrClassname = typeOrClassname; + TypeParameters = typeParameters; + } + + /// Returns true for wildcard type. + /// wildcard type indicator + public bool IsWildcard { get; set; } + + /// Returns the type name or class name. + /// name + public string TypeOrClassname { get; set; } + + /// Returns optional additional type parameters + /// type params + public IList TypeParameters { get; set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/soda/DataFlowOperatorParameter.cs b/NEsper.Core/NEsper.Core/client/soda/DataFlowOperatorParameter.cs new file mode 100755 index 000000000..2e6eeeef1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/DataFlowOperatorParameter.cs @@ -0,0 +1,87 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Object model of a data flow operator parameter. + /// + [Serializable] + public class DataFlowOperatorParameter + { + /// Ctor. + /// parameter name + /// parameter value + public DataFlowOperatorParameter(String parameterName, Object parameterValue) + { + ParameterName = parameterName; + ParameterValue = parameterValue; + } + + /// Ctor. + public DataFlowOperatorParameter() + { + } + + /// + /// Get the parameter name. + /// + /// parameter name + public string ParameterName { get; set; } + + /// + /// Get the parameter value, which can be either a constant, an + /// or a JSON object or a . + /// + /// parameter value + public object ParameterValue { get; set; } + + /// RenderAny parameter. + /// to write to + public void ToEPL(TextWriter writer) + { + writer.Write(ParameterName); + writer.Write(": "); + RenderValue(writer, ParameterValue); + } + + /// RenderAny prameter. + /// to render to + /// value + public static void RenderValue(TextWriter writer, Object parameterValue) + { + if (parameterValue is EPStatementObjectModel) + { + writer.Write("("); + ((EPStatementObjectModel) parameterValue).ToEPL(writer); + writer.Write(")"); + } + else if (parameterValue is Expression) + { + ((Expression) parameterValue).ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + } + else if (parameterValue == null) + { + writer.Write("null"); + } + else if (parameterValue is String) + { + writer.Write("\""); + writer.Write(parameterValue.ToString()); + writer.Write("\""); + } + else + { + writer.Write(parameterValue.ToString()); + } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/soda/Disjunction.cs b/NEsper.Core/NEsper.Core/client/soda/Disjunction.cs new file mode 100755 index 000000000..9fa73bd72 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/Disjunction.cs @@ -0,0 +1,60 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Disjunction represents a logical OR allowing multiple sub-expressions to be connected by OR. + /// + [Serializable] + public class Disjunction : Junction + { + /// + /// Ctor - for use to create an expression tree, without child expression. + /// + /// Use add methods to add child expressions to acts upon. + /// + /// + public Disjunction() + { + } + + /// Ctor. + /// an expression to add to the OR-test + /// an expression to add to the OR-test + /// is the expression to put in the OR-relationship. + public Disjunction(Expression first, Expression second, params Expression[] expressions) + { + AddChild(first); + AddChild(second); + for (int i = 0; i < expressions.Length; i++) + { + AddChild(expressions[i]); + } + } + + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.OR; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + String delimiter = ""; + foreach (Expression child in this.Children) + { + writer.Write(delimiter); + child.ToEPL(writer, Precedence); + delimiter = " or "; + } + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/client/soda/DotExpression.cs b/NEsper.Core/NEsper.Core/client/soda/DotExpression.cs new file mode 100755 index 000000000..ba5c1288a --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/DotExpression.cs @@ -0,0 +1,84 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.client.soda +{ + /// + /// Dot-expresson is for use in "(inner_expression).dot_expression". + /// + [Serializable] + public class DotExpression : ExpressionBase + { + private readonly IList _chain = new List(); + + /// + /// Ctor. + /// + public DotExpression() { + } + + /// + /// Ctor. + /// + /// the expression in parenthesis + public DotExpression(Expression innerExpression) + { + Children.Add(innerExpression); + } + + /// + /// Add a method to the chain of methods after the dot. + /// + /// to add + /// parameters to method + public void Add(string methodName, IList parameters) + { + _chain.Add(new DotExpressionItem(methodName, parameters, false)); + } + + /// + /// Add a method to the chain of methods after the dot, indicating the this segment is a property and does not need parenthesis and won't have paramaters. + /// + /// method name + /// parameter expressions + /// property flag + public void Add(string methodName, IList parameters, bool isProperty) + { + _chain.Add(new DotExpressionItem(methodName, parameters, isProperty)); + } + + /// + /// Returns the method chain of all methods after the dot. + /// + /// method name ane list of parameters + public IList Chain + { + get { return _chain; } + } + + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.MINIMUM; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + if (!this.Children.IsEmpty()) { + this.Children[0].ToEPL(writer, Precedence); + } + DotExpressionItem.Render(_chain, writer, !this.Children.IsEmpty()); + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/DotExpressionItem.cs b/NEsper.Core/NEsper.Core/client/soda/DotExpressionItem.cs new file mode 100755 index 000000000..9dd2a45f6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/DotExpressionItem.cs @@ -0,0 +1,89 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.client.soda +{ + /// + /// Dot-expresson item is for use in "(inner_expression).dot_expression". + /// + /// Each item represent an individual chain item and may either be a method name with method parameters, + /// or a (nested) property name typically with an empty list of parameters or for mapped properties a + /// non-empty list of parameters. + /// + [Serializable] + public class DotExpressionItem + { + /// Ctor. + public DotExpressionItem() + { + } + + /// Ctor. + /// the property (or nested property) or method name + /// are optional and should only be provided if this chain item is a method;Parameters are expressions for parameters to the method (use only for methods and not for properties unless mapped property). + /// true if this is a nested property name + public DotExpressionItem(String name, IList parameters, bool isProperty) + { + Name = name; + IsProperty = isProperty; + Parameters = parameters; + } + + /// Gets or sets the method name or nested property name. + /// method name or nested property name + public string Name { get; set; } + + /// + /// Gets or sets method parameters or parameters for mapped properties or empty list if this item represents a simple nested property. + /// + /// parameter expressions + public IList Parameters { get; set; } + + /// Returns true if this dot-item is a property name. + /// true for property, false for method + public bool IsProperty { get; set; } + + /// RenderAny to EPL. + /// chain to render + /// writer to output to + /// indicator whether to prefix with "." + internal static void Render(IList chain, TextWriter writer, bool prefixDot) + { + var delimiterOuter = prefixDot ? "." : ""; + foreach (var item in chain) + { + writer.Write(delimiterOuter); + writer.Write(item.Name); + + if (!item.IsProperty || !item.Parameters.IsEmpty()) + { + writer.Write("("); + if (!item.Parameters.IsEmpty()) + { + String delimiter = ""; + foreach (var param in item.Parameters) + { + writer.Write(delimiter); + delimiter = ","; + param.ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + } + } + writer.Write(")"); + } + delimiterOuter = "."; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/EPBaseNamedObject.cs b/NEsper.Core/NEsper.Core/client/soda/EPBaseNamedObject.cs new file mode 100755 index 000000000..590c8bc54 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/EPBaseNamedObject.cs @@ -0,0 +1,83 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Base class for named engine objects such as views, patterns guards and observers. + /// + [Serializable] + public abstract class EPBaseNamedObject + { + private String _namespace; + private String _name; + private IList _parameters; + + /// + /// Initializes a new instance of the class. + /// + protected EPBaseNamedObject() + { + } + + /// + /// Ctor. + /// + /// is the namespace of the object, i.e. view namespace or pattern object namespace + /// is the name of the object, such as the view name + /// is the optional parameters to the view or pattern object, or empty list for no parameters + protected EPBaseNamedObject(String @namespace, String name, IList parameters) + { + _namespace = @namespace; + _name = name; + _parameters = parameters; + } + + /// Gets or sets the object namespace name. + public String Namespace + { + get { return _namespace; } + set { _namespace = value; } + } + + /// Gets or sets the object name. + /// object name + public String Name + { + get { return _name; } + set { _name = value; } + } + + /// Gets or sets the object parameters. + /// parameters for object, empty list for no parameters + public IList Parameters + { + get { return _parameters; } + set { _parameters = value; } + } + + /// + /// Writes the object in EPL-syntax in the format "namespace:name(parameter, parameter, ..., parameter)" + /// + /// to output to + public virtual void ToEPL(TextWriter writer) + { + writer.Write(_namespace); + writer.Write(':'); + writer.Write(_name); + + writer.Write('('); + ExpressionBase.ToPrecedenceFreeEPL(Parameters, writer); + writer.Write(')'); + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/client/soda/EPStatementFormatter.cs b/NEsper.Core/NEsper.Core/client/soda/EPStatementFormatter.cs new file mode 100755 index 000000000..ffad459d7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/EPStatementFormatter.cs @@ -0,0 +1,263 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + public class EPStatementFormatter + { + private string SYSTEM_NEWLINE = Environment.NewLine; + private const string SPACE = " "; + + private readonly bool _isNewline; + private readonly string _newlineString; + + private string _delimiter; + + public EPStatementFormatter() + { + _isNewline = false; + _newlineString = " "; + } + + public EPStatementFormatter(bool newline) + { + _isNewline = newline; + _newlineString = SYSTEM_NEWLINE; + } + + public EPStatementFormatter(string newlineString) + { + _isNewline = true; + _newlineString = newlineString; + } + + public void BeginContext(TextWriter writer) + { + WriteDelimiter(writer); + } + + public void BeginAnnotation(TextWriter writer) + { + WriteDelimiter(writer); + } + + public void BeginExpressionDecl(TextWriter writer) + { + WriteDelimiter(writer); + } + + public void BeginInsertInto(TextWriter writer, bool topLevel) + { + WriteDelimiter(writer, topLevel); + } + + public void BeginFromStream(TextWriter writer, bool first) + { + WriteDelimiter(writer, !first); + } + + public void BeginWhere(TextWriter writer) + { + WriteDelimiter(writer); + } + + public void BeginGroupBy(TextWriter writer) + { + WriteDelimiter(writer); + } + + public void BeginHaving(TextWriter writer) + { + WriteDelimiter(writer); + } + + public void BeginOutput(TextWriter writer) + { + WriteDelimiter(writer); + } + + public void BeginOrderBy(TextWriter writer) + { + WriteDelimiter(writer); + } + + public void BeginLimit(TextWriter writer) + { + WriteDelimiter(writer); + } + + public void BeginFor(TextWriter writer) + { + WriteDelimiter(writer); + } + + public void BeginOnTrigger(TextWriter writer) + { + WriteDelimiter(writer); + } + + public void BeginSelect(TextWriter writer, bool topLevel) + { + if (topLevel) + { + WriteDelimiter(writer, topLevel); + } + SetDelimiter(); + } + + public void BeginMerge(TextWriter writer) + { + WriteDelimiter(writer); + } + + public void BeginFrom(TextWriter writer) + { + WriteDelimiter(writer); + } + + public void BeginMergeWhere(TextWriter writer) + { + WriteDelimiter(writer); + } + + public void BeginMergeWhenMatched(TextWriter writer) + { + WriteDelimiter(writer); + } + + public void BeginMergeAction(TextWriter writer) + { + WriteDelimiter(writer); + } + + public void BeginOnSet(TextWriter writer) + { + WriteDelimiter(writer); + } + + public void BeginOnDelete(TextWriter writer) + { + WriteDelimiter(writer); + } + + public void BeginOnUpdate(TextWriter writer) + { + WriteDelimiter(writer); + } + + private void SetDelimiter() + { + if (_isNewline) + { + _delimiter = _newlineString; + } + else + { + _delimiter = SPACE; + } + } + + private void WriteDelimiter(TextWriter writer) + { + if (_delimiter != null) + { + writer.Write(_delimiter); + } + SetDelimiter(); + } + + private void WriteDelimiter(TextWriter writer, bool topLevel) + { + if (_delimiter != null) + { + if (!topLevel) + { + writer.Write(SPACE); + } + else + { + writer.Write(_delimiter); + } + } + SetDelimiter(); + } + + public void BeginCreateDataFlow(TextWriter writer) + { + WriteDelimiter(writer); + } + + public void BeginCreateVariable(TextWriter writer) + { + WriteDelimiter(writer); + } + + public void BeginUpdate(TextWriter writer) + { + WriteDelimiter(writer); + } + + public void BeginCreateWindow(TextWriter writer) + { + WriteDelimiter(writer); + } + + public void BeginCreateContext(TextWriter writer) + { + WriteDelimiter(writer); + } + + public void BeginCreateSchema(TextWriter writer) + { + WriteDelimiter(writer); + } + + public void BeginCreateIndex(TextWriter writer) + { + WriteDelimiter(writer); + } + + public void BeginDataFlowSchema(TextWriter writer) + { + WriteDelimiter(writer); + } + + public void BeginDataFlowOperator(TextWriter writer) + { + WriteDelimiter(writer); + } + + public void BeginDataFlowOperatorDetails(TextWriter writer) + { + WriteDelimiter(writer); + } + + public void EndDataFlowOperatorConfig(TextWriter writer) + { + WriteDelimiter(writer); + } + + public void EndDataFlowOperatorDetails(TextWriter writer) + { + WriteDelimiter(writer); + } + + public void BeginCreateExpression(TextWriter writer) + { + WriteDelimiter(writer); + } + + public void BeginCreateTable(TextWriter writer) + { + WriteDelimiter(writer); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/soda/EPStatementObjectModel.cs b/NEsper.Core/NEsper.Core/client/soda/EPStatementObjectModel.cs new file mode 100755 index 000000000..a1b741fe0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/EPStatementObjectModel.cs @@ -0,0 +1,592 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.client.soda +{ + /// + /// Object model of an EPL statement. + /// Applications can create an object model by instantiating this class and then setting the various clauses. + /// When done, use to create a statement from the model. + /// Alternativly, a given textual EPL can be compiled into an object model representation via the compile method on + /// . + /// Use the toEPL method to generate a textual EPL from an object model. + /// Minimally, and EPL statement consists of the select-clause and the where-clause. These are represented by and respectively. + /// Here is a short example that create a simple EPL statement such as "select page, responseTime from PageLoad" : + /// EPStatementObjectModel model = new EPStatementObjectModel(); + /// model.setSelectClause(SelectClause.create("page", "responseTime")); + /// model.setFromClause(FromClause.create(FilterStream.create("PageLoad"))); + /// The select-clause and from-clause must be set for the statement object model to be useable by the + /// administrative API. All other clauses a optional. + /// Please see the documentation set for further examples. + /// + [Serializable] + public class EPStatementObjectModel + { + /// + /// Ctor. + /// + public EPStatementObjectModel() + { + } + + /// + /// Specify an insert-into-clause. + /// + /// specifies the insert-into-clause, or null to indicate that the clause is absent + /// model + public EPStatementObjectModel SetInsertInto(InsertIntoClause insertInto) + { + InsertInto = insertInto; + return this; + } + + /// + /// Return the insert-into-clause, or null to indicate that the clause is absent. + /// + /// specification of the insert-into-clause, or null if none present + public InsertIntoClause InsertInto { get; set; } + + /// + /// Specify a select-clause. + /// + /// specifies the select-clause, the select-clause cannot be null and must be set + /// model + public EPStatementObjectModel Select(SelectClause selectClause) + { + SelectClause = selectClause; + return this; + } + + /// + /// Return the select-clause. + /// + /// specification of the select-clause + public SelectClause SelectClause { get; set; } + + /// + /// Specify a from-clause. + /// + /// specifies the from-clause, the from-clause cannot be null and must be set + public FromClause FromClause { get; set; } + + /// + /// Specify a from-clause. + /// + /// specifies the from-clause, the from-clause cannot be null and must be set + /// model + public EPStatementObjectModel SetFrom(FromClause fromClause) + { + FromClause = fromClause; + return this; + } + + /// + /// Return the where-clause, or null to indicate that the clause is absent. + /// + /// specification of the where-clause, or null if none present + public Expression WhereClause { get; set; } + + /// + /// Specify a where-clause. + /// + /// specifies the where-clause, which is optional and can be null + /// model + public EPStatementObjectModel SetWhere(Expression whereClause) + { + WhereClause = whereClause; + return this; + } + + /// + /// Return the group-by-clause, or null to indicate that the clause is absent. + /// + /// specification of the group-by-clause, or null if none present + public GroupByClause GroupByClause { get; set; } + + /// + /// Specify a group-by-clause. + /// + /// specifies the group-by-clause, which is optional and can be null + /// model + public EPStatementObjectModel SetGroupBy(GroupByClause groupByClause) + { + GroupByClause = groupByClause; + return this; + } + + /// + /// Return the having-clause, or null to indicate that the clause is absent. + /// + /// specification of the having-clause, or null if none present + public Expression HavingClause { get; set; } + + /// + /// Specify a having-clause. + /// + /// specifies the having-clause, which is optional and can be null + /// model + public EPStatementObjectModel SetHaving(Expression havingClause) + { + HavingClause = havingClause; + return this; + } + + /// + /// Return the order-by-clause, or null to indicate that the clause is absent. + /// + /// specification of the order-by-clause, or null if none present + public OrderByClause OrderByClause { get; set; } + + /// + /// Specify an order-by-clause. + /// + /// specifies the order-by-clause, which is optional and can be null + /// model + public EPStatementObjectModel SetOrderBy(OrderByClause orderByClause) + { + OrderByClause = orderByClause; + return this; + } + + /// + /// Return the output-rate-limiting-clause, or null to indicate that the clause is absent. + /// + /// specification of the output-rate-limiting-clause, or null if none present + public OutputLimitClause OutputLimitClause { get; set; } + + /// + /// Specify an output-rate-limiting-clause. + /// + /// specifies the output-rate-limiting-clause, which is optional and can be null + /// model + public EPStatementObjectModel SetOutputLimit(OutputLimitClause outputLimitClause) + { + OutputLimitClause = outputLimitClause; + return this; + } + + /// + /// Renders the object model in it's EPL syntax textual representation. + /// + /// EPL representing the statement object model + /// IllegalStateException if required clauses do not exist + public string ToEPL() + { + var writer = new StringWriter(); + ToEPL(new EPStatementFormatter(false), writer); + return writer.ToString(); + } + + /// + /// Rendering using the provided writer. + /// + /// to use + public void ToEPL(TextWriter writer) + { + ToEPL(new EPStatementFormatter(false), writer); + } + + /// + /// Rendering using the provided formatter. + /// + /// to use + /// rendered string + public string ToEPL(EPStatementFormatter formatter) + { + var writer = new StringWriter(); + ToEPL(formatter, writer); + return writer.ToString(); + } + + /// + /// Renders the object model in it's EPL syntax textual representation, using a whitespace-formatter as provided. + /// + /// the formatter to use + /// writer to use + /// IllegalStateException if required clauses do not exist + public void ToEPL(EPStatementFormatter formatter, TextWriter writer) + { + AnnotationPart.ToEPL(writer, Annotations, formatter); + ExpressionDeclaration.ToEPL(writer, ExpressionDeclarations, formatter); + ScriptExpression.ToEPL(writer, ScriptExpressions, formatter); + + if (ContextName != null) + { + formatter.BeginContext(writer); + writer.Write("context "); + writer.Write(ContextName); + } + + if (CreateIndex != null) + { + formatter.BeginCreateIndex(writer); + CreateIndex.ToEPL(writer); + return; + } + else if (CreateSchema != null) + { + formatter.BeginCreateSchema(writer); + CreateSchema.ToEPL(writer); + return; + } + else if (CreateExpression != null) + { + formatter.BeginCreateExpression(writer); + CreateExpression.ToEPL(writer); + return; + } + else if (CreateContext != null) + { + formatter.BeginCreateContext(writer); + CreateContext.ToEPL(writer, formatter); + return; + } + else if (CreateWindow != null) + { + formatter.BeginCreateWindow(writer); + CreateWindow.ToEPL(writer); + + if (FromClause != null) + { + var fs = (FilterStream)FromClause.Streams[0]; + if (fs.IsRetainUnion) + { + writer.Write(" retain-union"); + } + } + + writer.Write(" as "); + if ((SelectClause == null) || (SelectClause.SelectList.IsEmpty()) && !CreateWindow.Columns.IsEmpty()) + { + CreateWindow.ToEPLCreateTablePart(writer); + } + else + { + SelectClause.ToEPL(writer, formatter, false, false); + FromClause.ToEPL(writer, formatter); + CreateWindow.ToEPLInsertPart(writer); + } + return; + } + else if (CreateVariable != null) + { + formatter.BeginCreateVariable(writer); + CreateVariable.ToEPL(writer); + return; + } + else if (CreateTable != null) + { + formatter.BeginCreateTable(writer); + CreateTable.ToEPL(writer); + return; + } + else if (CreateDataFlow != null) + { + formatter.BeginCreateDataFlow(writer); + CreateDataFlow.ToEPL(writer, formatter); + return; + } + + var displayWhereClause = true; + if (UpdateClause != null) + { + formatter.BeginUpdate(writer); + UpdateClause.ToEPL(writer); + } + else if (OnExpr != null) + { + formatter.BeginOnTrigger(writer); + writer.Write("on "); + FromClause.Streams[0].ToEPL(writer, formatter); + + if (OnExpr is OnDeleteClause) + { + formatter.BeginOnDelete(writer); + writer.Write("delete from "); + ((OnDeleteClause)OnExpr).ToEPL(writer); + } + else if (OnExpr is OnUpdateClause) + { + formatter.BeginOnUpdate(writer); + writer.Write("update "); + ((OnUpdateClause)OnExpr).ToEPL(writer); + } + else if (OnExpr is OnSelectClause) + { + var onSelect = (OnSelectClause)OnExpr; + if (InsertInto != null) + { + InsertInto.ToEPL(writer, formatter, true); + } + SelectClause.ToEPL(writer, formatter, true, onSelect.IsDeleteAndSelect); + writer.Write(" from "); + onSelect.ToEPL(writer); + } + else if (OnExpr is OnSetClause) + { + var onSet = (OnSetClause)OnExpr; + onSet.ToEPL(writer, formatter); + } + else if (OnExpr is OnMergeClause) + { + var merge = (OnMergeClause)OnExpr; + merge.ToEPL(writer, WhereClause, formatter); + displayWhereClause = false; + } + else + { + var split = (OnInsertSplitStreamClause)OnExpr; + InsertInto.ToEPL(writer, formatter, true); + SelectClause.ToEPL(writer, formatter, true, false); + if (WhereClause != null) + { + writer.Write(" where "); + WhereClause.ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + } + split.ToEPL(writer, formatter); + displayWhereClause = false; + } + } + else + { + if (IntoTableClause != null) + { + IntoTableClause.ToEPL(writer); + } + + if (SelectClause == null) + { + throw new IllegalStateException("Select-clause has not been defined"); + } + if (FromClause == null) + { + throw new IllegalStateException("From-clause has not been defined"); + } + + if (FireAndForgetClause is FireAndForgetUpdate) + { + var update = (FireAndForgetUpdate)FireAndForgetClause; + writer.Write("update "); + FromClause.ToEPLOptions(writer, formatter, false); + writer.Write(" "); + UpdateClause.RenderEPLAssignments(writer, update.Assignments); + } + else if (FireAndForgetClause is FireAndForgetInsert) + { + var insert = (FireAndForgetInsert)FireAndForgetClause; + InsertInto.ToEPL(writer, formatter, true); + if (insert.IsUseValuesKeyword) + { + writer.Write(" values ("); + var delimiter = ""; + foreach (var element in SelectClause.SelectList) + { + writer.Write(delimiter); + element.ToEPLElement(writer); + delimiter = ", "; + } + writer.Write(")"); + } + else + { + SelectClause.ToEPL(writer, formatter, true, false); + } + } + else if (FireAndForgetClause is FireAndForgetDelete) + { + writer.Write("delete "); + FromClause.ToEPLOptions(writer, formatter, true); + } + else + { + if (InsertInto != null) + { + InsertInto.ToEPL(writer, formatter, true); + } + SelectClause.ToEPL(writer, formatter, true, false); + FromClause.ToEPLOptions(writer, formatter, true); + } + } + + if (MatchRecognizeClause != null) + { + MatchRecognizeClause.ToEPL(writer); + } + if ((WhereClause != null) && (displayWhereClause)) + { + formatter.BeginWhere(writer); + writer.Write("where "); + WhereClause.ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + } + if (GroupByClause != null) + { + formatter.BeginGroupBy(writer); + writer.Write("group by "); + GroupByClause.ToEPL(writer); + } + if (HavingClause != null) + { + formatter.BeginHaving(writer); + writer.Write("having "); + HavingClause.ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + } + if (OutputLimitClause != null) + { + formatter.BeginOutput(writer); + writer.Write("output "); + OutputLimitClause.ToEPL(writer); + } + if (OrderByClause != null) + { + formatter.BeginOrderBy(writer); + writer.Write("order by "); + OrderByClause.ToEPL(writer); + } + if (RowLimitClause != null) + { + formatter.BeginLimit(writer); + writer.Write("limit "); + RowLimitClause.ToEPL(writer); + } + if (ForClause != null) + { + formatter.BeginFor(writer); + ForClause.ToEPL(writer); + } + } + + /// + /// Returns the create-window clause for creating named windows, or null if this statement does not + /// create a named window. + /// + /// named window creation clause + public CreateWindowClause CreateWindow { get; set; } + + /// + /// Returns the on-delete clause for deleting from named windows, or null if this statement + /// does not delete from a named window + /// + /// on delete clause + public OnClause OnExpr { get; set; } + + /// + /// Returns the create-variable clause if this is a statement creating a variable, or null if not. + /// + /// create-variable clause + public CreateVariableClause CreateVariable { get; set; } + + /// + /// Returns the row limit specification, or null if none supplied. + /// + /// row limit spec if any + public RowLimitClause RowLimitClause { get; set; } + + /// + /// Returns the update specification. + /// + /// update spec if defined + public UpdateClause UpdateClause { get; set; } + + /// + /// Returns annotations. + /// + /// annotations + public IList Annotations { get; set; } + + /// + /// Match-recognize clause. + /// + /// clause + public MatchRecognizeClause MatchRecognizeClause { get; set; } + + /// + /// Returns create-index clause. + /// + /// clause + public CreateIndexClause CreateIndex { get; set; } + + /// + /// Returns the create-schema clause. + /// + /// clause + public CreateSchemaClause CreateSchema { get; set; } + + /// + /// Returns the create-context clause. + /// + /// clause + public CreateContextClause CreateContext { get; set; } + + /// + /// Returns the for-clause. + /// + /// for-clause + public ForClause ForClause { get; set; } + + /// + /// Returns the expression declarations, if any. + /// + /// expression declarations + public IList ExpressionDeclarations { get; set; } + + /// + /// Returns the context name if context dimensions apply to statement. + /// + /// context name + public string ContextName { get; set; } + + /// + /// Returns the scripts defined. + /// + /// scripts + public IList ScriptExpressions { get; set; } + + /// + /// Returns the "create dataflow" part, if present. + /// + /// create dataflow clause + public CreateDataFlowClause CreateDataFlow { get; set; } + + /// + /// Returns the internal expression id assigned for tools to identify the expression. + /// + /// object name + public string TreeObjectName { get; set; } + + /// + /// Returns the create-expression clause, if any + /// + /// clause + public CreateExpressionClause CreateExpression { get; set; } + + /// + /// Returns fire-and-forget (on-demand) query information for FAF select, insert, update and delete. + /// + /// fire and forget query information + public FireAndForgetClause FireAndForgetClause { get; set; } + + /// + /// Returns the into-table clause, or null if none found. + /// + /// into-table clause + public IntoTableClause IntoTableClause { get; set; } + + /// + /// Returns the create-table clause if present or null if not present + /// + /// create-table clause + public CreateTableClause CreateTable { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/Expression.cs b/NEsper.Core/NEsper.Core/client/soda/Expression.cs new file mode 100755 index 000000000..be44bf7d5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/Expression.cs @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Interface representing an expression for use in select-clauses, where-clauses, having-clauses, order-by clauses and + /// streams based on filters and pattern filter expressions. + /// + /// Expressions are organized into a tree-like structure with nodes representing sub-expressions. + /// + /// + /// Certain types of nodes have certain requirements towards the number or types of nodes that + /// are expected as sub-expressions to an expression. + /// + /// + public interface Expression + { + /// + /// Returns the list of sub-expressions (child expressions) to the current expression node. + /// + /// child expressions or empty list if there are no child expressions + IList Children { get; set; } + + /// + /// Gets or sets the name of the tree object. + /// + /// The name of the tree object. + string TreeObjectName { get; set; } + + /// + /// Gets the Precedence. + /// + /// The Precedence. + ExpressionPrecedenceEnum Precedence { get; } + + /// + /// Renders the expressions and all it's child expression, in full tree depth, as a string in + /// language syntax. + /// + /// is the output to use + /// The parent Precedence. + void ToEPL(TextWriter writer, ExpressionPrecedenceEnum parentPrecedence); + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/client/soda/ExpressionBase.cs b/NEsper.Core/NEsper.Core/client/soda/ExpressionBase.cs new file mode 100755 index 000000000..d6b80fd46 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/ExpressionBase.cs @@ -0,0 +1,138 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// Base expression. + [Serializable] + public abstract class ExpressionBase : Expression + { + private IList _children; + private string _treeObjectName; + + /// Ctor. + public ExpressionBase() + { + _children = new List(); + } + + /// + /// Renders child expression of a function in a comma-separated list. + /// + /// function name + /// child nodes + /// writer + internal static void ToPrecedenceFreeEPL(string functionName, IList children, TextWriter writer) + { + writer.Write(functionName); + writer.Write("("); + ToPrecedenceFreeEPL(children, writer); + writer.Write(')'); + } + + /// + /// RenderAny expression list + /// + /// expressions to render + /// writer to render to + public static void ToPrecedenceFreeEPL(IList children, TextWriter writer) + { + string delimiter = ""; + foreach (Expression expr in children) + { + writer.Write(delimiter); + expr.ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + delimiter = ","; + } + } + + /// + /// RenderAny an aggregation function with distinct and parameter expressions + /// + /// to render to + /// function name + /// distinct flag + /// parameters to render + internal static void RenderAggregation( + TextWriter writer, + string name, + bool distinct, + IList children) + { + writer.Write(name); + writer.Write("("); + if (distinct) + { + writer.Write("distinct "); + } + string delimiter = ""; + foreach (Expression param in children) + { + writer.Write(delimiter); + delimiter = ","; + param.ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + } + writer.Write(")"); + } + + public string TreeObjectName + { + get { return _treeObjectName; } + set { _treeObjectName = value; } + } + + /// + /// Returns the list of sub-expressions to the current expression. + /// + /// list of child expressions + public IList Children + { + get { return _children; } + set { _children = value; } + } + + /// + /// Adds a new child expression to the current expression. + /// + /// to add + public void AddChild(Expression expression) + { + _children.Add(expression); + } + + public void ToEPL(TextWriter writer, ExpressionPrecedenceEnum parentPrecedence) + { + if (Precedence < parentPrecedence) + { + writer.Write("("); + ToPrecedenceFreeEPL(writer); + writer.Write(")"); + } + else + { + ToPrecedenceFreeEPL(writer); + } + } + + /// + /// Renders the expressions and all it's child expression, in full tree depth, as a string in + /// language syntax. + /// + /// is the output to use + public abstract void ToPrecedenceFreeEPL(TextWriter writer); + + /// + /// Returns the precedence. + /// + public abstract ExpressionPrecedenceEnum Precedence { get; } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/soda/ExpressionDeclaration.cs b/NEsper.Core/NEsper.Core/client/soda/ExpressionDeclaration.cs new file mode 100755 index 000000000..ede1afba8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/ExpressionDeclaration.cs @@ -0,0 +1,141 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.client.soda +{ + /// + /// Represents a single expression declaration that applies to a given statement. + /// + [Serializable] + public class ExpressionDeclaration + { + /// + /// Ctor. + /// + public ExpressionDeclaration() + { + } + + /// + /// Ctor. + /// + /// of expression + /// expression paramater names + /// the expression body + /// indicator whether this is an expression alias or not + public ExpressionDeclaration(string name, IList parameterNames, Expression expression, bool alias) + { + this.Name = name; + this.ParameterNames = parameterNames; + this.Expression = expression; + this.IsAlias = alias; + } + + /// + /// Returns expression name. + /// + /// name + public string Name { get; set; } + + /// + /// Returns the expression body. + /// + /// expression body + public Expression Expression { get; set; } + + /// + /// Returns the paramater names. + /// + /// paramater names + public IList ParameterNames { get; set; } + + /// + /// Returns indicator whether the expression is an alias or not. + /// + /// alias indicator + public bool IsAlias { get; set; } + + /// + /// Print. + /// + /// to print to + /// expression declarations + /// for newline-whitespace formatting + public static void ToEPL( + TextWriter writer, + IList expressionDeclarations, + EPStatementFormatter formatter) + { + if ((expressionDeclarations == null) || (expressionDeclarations.IsEmpty())) + { + return; + } + + foreach (var part in expressionDeclarations) + { + if (part.Name == null) + { + continue; + } + formatter.BeginExpressionDecl(writer); + part.ToEPL(writer); + } + } + + /// + /// Print part. + /// + /// to write to + public void ToEPL(TextWriter writer) + { + writer.Write("expression "); + writer.Write(Name); + if (IsAlias) + { + writer.Write(" alias for"); + } + writer.Write(" {"); + if (!IsAlias) + { + if (ParameterNames != null && ParameterNames.Count == 1) + { + writer.Write(ParameterNames[0]); + } + else if (ParameterNames != null && !ParameterNames.IsEmpty()) + { + var delimiter = ""; + writer.Write("("); + foreach (var name in ParameterNames) + { + writer.Write(delimiter); + writer.Write(name); + delimiter = ","; + } + writer.Write(")"); + } + + if (ParameterNames != null && !ParameterNames.IsEmpty()) + { + writer.Write(" => "); + } + } + if (Expression != null) + { + Expression.ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + } + writer.Write("}"); + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/ExpressionPlaceholder.cs b/NEsper.Core/NEsper.Core/client/soda/ExpressionPlaceholder.cs new file mode 100755 index 000000000..73a76dfea --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/ExpressionPlaceholder.cs @@ -0,0 +1,33 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// For use in expression as a placeholder to represent its child nodes. + /// + [Serializable] + public class ExpressionPlaceholder : ExpressionBase + { + public override void ToPrecedenceFreeEPL(TextWriter writer) { + if ((Children == null) || (Children.Count == 0)) { + return; + } + Children[0].ToEPL(writer, Precedence); + } + + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.MINIMUM; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/ExpressionPrecedenceEnum.cs b/NEsper.Core/NEsper.Core/client/soda/ExpressionPrecedenceEnum.cs new file mode 100755 index 000000000..c05ed2f65 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/ExpressionPrecedenceEnum.cs @@ -0,0 +1,42 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.client.soda +{ + /// + /// Precendence levels for expressions. + /// + public enum ExpressionPrecedenceEnum + { + /// Precedence. + UNARY = (11), + /// Precedence. + MULTIPLY = (10), + /// Precedence. + ADDITIVE = (9), + /// Precedence. + CONCAT = (8), + /// Precedence. + RELATIONAL_BETWEEN_IN = (7), + /// Precedence. + EQUALS = (6), + /// Precedence. + NEGATED = (5), + /// Precedence. + BITWISE = (4), + /// Precedence. + AND = (3), + /// Precedence. + OR = (2), + /// Precedence. + CASE = (1), + + /// Precedence. + MINIMUM = (int.MinValue) + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/Expressions.cs b/NEsper.Core/NEsper.Core/client/soda/Expressions.cs new file mode 100755 index 000000000..3a051e643 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/Expressions.cs @@ -0,0 +1,1825 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.type; +using com.espertech.esper.util; + +namespace com.espertech.esper.client.soda +{ + /// + /// Convenience factory for creating instances. + /// + /// Provides quick-access methods to create all possible expressions and provides typical parameter lists to each. + /// + /// + /// Note that only the typical parameter lists are provided and expressions can allow adding + /// additional parameters. + /// + /// + /// Many expressions, for example logical AND and OR (conjunction and disjunction), allow + /// adding an unlimited number of additional sub-expressions to an expression. For those expressions + /// there are additional add methods provided. + /// + /// + [Serializable] + public class Expressions + { + + /// + /// Current system time supplies internal-timer provided time or + /// the time provided by external timer events. + /// + /// expression + public static CurrentTimestampExpression CurrentTimestamp() + { + return new CurrentTimestampExpression(); + } + + /// + /// Exists-function for use with dynamic properties to test property existence. + /// + /// name of the property to test whether it exists or not + /// expression + public static PropertyExistsExpression ExistsProperty(string propertyName) + { + return new PropertyExistsExpression(propertyName); + } + + /// + /// Cast function, casts the result on an expression to the desired type, or + /// returns null if the type cannot be casted to the type. + /// + /// The list of types can include fully-qualified class names plus any of the + /// primitive type names: byte, char, short, int, long, float, double, bool. + /// Alternatively to "System.String" the simple "string" is also permitted. + /// + /// + /// Type checks include all superclasses and interfaces of the value returned by the expression. + /// + /// + /// returns the value to cast + /// is type to cast to + /// expression + public static CastExpression Cast(Expression expression, string typeName) + { + return new CastExpression(expression, typeName); + } + + /// + /// Cast function, casts the result on an expression to the desired type, or + /// returns null if the type cannot be casted to the type. + /// + /// The list of types can include fully-qualified class names plus any of the + /// primitive type names: byte, char, short, int, long, float, double, bool. + /// Alternatively to "System.String" the simple "string" is also permitted. + /// + /// + /// Type checks include all superclasses and interfaces of the value returned by the expression. + /// + /// + /// name of the property supplying the value to cast + /// is type to cast to + /// expression + public static CastExpression Cast(string propertyName, string typeName) + { + return new CastExpression(GetPropExpr(propertyName), typeName); + } + + /// + /// Instance-of function, tests if the type of the return value of an expression is in a list of types. + /// + /// The list of types can include fully-qualified class names plus any of the + /// primitive type names: byte, char, short, int, long, float, double, bool. + /// Alternatively to "System.String" the simple "string" is also permitted. + /// + /// + /// Type checks include all superclasses and interfaces of the value returned by the expression. + /// + /// + /// returns the value to test whether the type returned is any of the is the function name + /// is one type to check for + /// is optional additional types to check for in a list + /// expression + public static InstanceOfExpression InstanceOf(Expression expression, string typeName, params string[] typeNames) + { + return new InstanceOfExpression(expression, typeName, typeNames); + } + + /// + /// Instance-of function, tests if the type of the return value of a property is in a list of types. + /// + /// Useful with dynamic (unchecked) properties to check the type of property returned. + /// + /// + /// The list of types can include fully-qualified class names plus any of the + /// primitive type names: byte, char, short, int, long, float, double, bool. + /// Alternatively to "System.String" the simple "string" is also permitted. + /// + /// + /// Type checks include all superclasses and interfaces of the value returned by the expression. + /// + /// + /// name of the property supplying the value to test + /// is one type to check for + /// is optional additional types to check for in a list + /// expression + public static InstanceOfExpression InstanceOf(string propertyName, string typeName, params string[] typeNames) + { + return new InstanceOfExpression(GetPropExpr(propertyName), typeName, typeNames); + } + + /// + /// Type-of function, returns the event type name or result type as a string of a stream name, property or expression. + /// + /// to evaluate and return it's result type as a string + /// expression + public static TypeOfExpression TypeOf(Expression expression) + { + return new TypeOfExpression(expression); + } + + /// + /// Type-of function, returns the event type name or result type as a string of a stream name, property or expression. + /// + /// returns the property to evaluate and return its event type name or property class type + /// expression + public static TypeOfExpression TypeOf(string propertyName) + { + return new TypeOfExpression(GetPropExpr(propertyName)); + } + + /// + /// Plug-in aggregation function. + /// + /// is the function name + /// provides the values to aggregate + /// expression + public static PlugInProjectionExpression PlugInAggregation( + string functionName, + params Expression[] moreExpressions) + { + return new PlugInProjectionExpression(functionName, false, moreExpressions); + } + + /// + /// Regular expression. + /// + /// returns the values to match + /// returns the value to match against + /// expression + public static RegExpExpression Regexp(Expression left, Expression right) + { + return new RegExpExpression(left, right); + } + + /// + /// Regular expression. + /// + /// returns the values to match + /// returns the value to match against + /// is the escape character + /// expression + public static RegExpExpression Regexp(Expression left, Expression right, string escape) + { + return new RegExpExpression(left, right, new ConstantExpression(escape)); + } + + /// + /// Regular expression. + /// + /// the name of the property returning values to match + /// a regular expression to match against + /// expression + public static RegExpExpression Regexp(string property, string regExExpression) + { + return new RegExpExpression(GetPropExpr(property), new ConstantExpression(regExExpression)); + } + + /// + /// Regular expression. + /// + /// the name of the property returning values to match + /// a regular expression to match against + /// is the escape character + /// expression + public static RegExpExpression Regexp(string property, string regExExpression, string escape) + { + return new RegExpExpression( + GetPropExpr(property), new ConstantExpression(regExExpression), new ConstantExpression(escape)); + } + + /// + /// Regular expression negated (not regexp). + /// + /// returns the values to match + /// returns the value to match against + /// expression + public static RegExpExpression NotRegexp(Expression left, Expression right) + { + return new RegExpExpression(left, right, true); + } + + /// + /// Regular expression negated (not regexp). + /// + /// returns the values to match + /// returns the value to match against + /// is the escape character + /// expression + public static RegExpExpression NotRegexp(Expression left, Expression right, string escape) + { + return new RegExpExpression(left, right, new ConstantExpression(escape), true); + } + + /// + /// Regular expression negated (not regexp). + /// + /// the name of the property returning values to match + /// a regular expression to match against + /// expression + public static RegExpExpression NotRegexp(string property, string regExExpression) + { + return new RegExpExpression(GetPropExpr(property), new ConstantExpression(regExExpression), true); + } + + /// + /// Regular expression negated (not regexp). + /// + /// the name of the property returning values to match + /// a regular expression to match against + /// is the escape character + /// expression + public static RegExpExpression NotRegexp(string property, string regExExpression, string escape) + { + return new RegExpExpression( + GetPropExpr(property), new ConstantExpression(regExExpression), new ConstantExpression(escape), true); + } + + /// + /// Array expression, representing the syntax of "{1, 2, 3}" returning an integer array of 3 elements valued 1, 2, 3. + /// + /// expression + public static ArrayExpression Array() + { + return new ArrayExpression(); + } + + /// + /// Bitwise (binary) AND. + /// + /// expression + public static BitwiseOpExpression BinaryAnd() + { + return new BitwiseOpExpression(BitWiseOpEnum.BAND); + } + + /// + /// Bitwise (binary) OR. + /// + /// expression + public static BitwiseOpExpression BinaryOr() + { + return new BitwiseOpExpression(BitWiseOpEnum.BOR); + } + + /// + /// Bitwise (binary) XOR. + /// + /// expression + public static BitwiseOpExpression BinaryXor() + { + return new BitwiseOpExpression(BitWiseOpEnum.BXOR); + } + + /// + /// Minimum value per-row function (not aggregating). + /// + /// the name of a first property to compare + /// the name of a second property to compare + /// optional additional properties to compare + /// expression + public static MinRowExpression Min(string propertyOne, string propertyTwo, params string[] moreProperties) + { + return new MinRowExpression(propertyOne, propertyTwo, moreProperties); + } + + /// + /// Minimum value per-row function (not aggregating). + /// + /// returns the first value to compare + /// returns the second value to compare + /// optional additional values to compare + /// expression + public static MinRowExpression Min(Expression exprOne, Expression exprTwo, params Expression[] moreExpressions) + { + return new MinRowExpression(exprOne, exprTwo, moreExpressions); + } + + /// + /// Maximum value per-row function (not aggregating). + /// + /// the name of a first property to compare + /// the name of a second property to compare + /// optional additional properties to compare + /// expression + public static MaxRowExpression Max(string propertyOne, string propertyTwo, params string[] moreProperties) + { + return new MaxRowExpression(propertyOne, propertyTwo, moreProperties); + } + + /// + /// Maximum value per-row function (not aggregating). + /// + /// returns the first value to compare + /// returns the second value to compare + /// optional additional values to compare + /// expression + public static MaxRowExpression Max(Expression exprOne, Expression exprTwo, params Expression[] moreExpressions) + { + return new MaxRowExpression(exprOne, exprTwo, moreExpressions); + } + + /// + /// Coalesce. + /// + /// name of the first property returning value to coealesce + /// name of the second property returning value to coealesce + /// name of the optional additional properties returning values to coealesce + /// expression + public static CoalesceExpression Coalesce( + string propertyOne, + string propertyTwo, + params string[] moreProperties) + { + return new CoalesceExpression(propertyOne, propertyTwo, moreProperties); + } + + /// + /// Coalesce. + /// + /// returns value to coalesce + /// returns value to coalesce + /// returning optional additional values to coalesce + /// expression + public static CoalesceExpression Coalesce( + Expression exprOne, + Expression exprTwo, + params Expression[] moreExpressions) + { + return new CoalesceExpression(exprOne, exprTwo, moreExpressions); + } + + /// + /// Constant. + /// + /// is the constant value + /// expression + public static ConstantExpression Constant(Object value) + { + return new ConstantExpression(value); + } + + /// + /// Constant, use when the value is null. + /// + /// is the constant value + /// is the type of the constant + /// expression + public static ConstantExpression Constant(Object value, Type constantType) + { + return new ConstantExpression(value, constantType.Name); + } + + /// + /// Case-when-then expression. + /// + /// expression + public static CaseWhenThenExpression CaseWhenThen() + { + return new CaseWhenThenExpression(); + } + + /// + /// Case-switch expresssion. + /// + /// provides the switch value + /// expression + public static CaseSwitchExpression CaseSwitch(Expression valueToSwitchOn) + { + return new CaseSwitchExpression(valueToSwitchOn); + } + + /// + /// Case-switch expresssion. + /// + /// the name of the property that provides the switch value + /// expression + public static CaseSwitchExpression CaseSwitch(string propertyName) + { + return new CaseSwitchExpression(GetPropExpr(propertyName)); + } + + /// + /// In-expression that is equivalent to the syntax of "property in (value, value, ... value)". + /// + /// is the name of the property + /// are the constants to check against + /// expression + public static InExpression In(string property, params Object[] values) + { + return new InExpression(GetPropExpr(property), false, values); + } + + /// + /// Not-In-expression that is equivalent to the syntax of "property not in (value, value, ... value)". + /// + /// is the name of the property + /// are the constants to check against + /// expression + public static InExpression NotIn(string property, params Object[] values) + { + return new InExpression(GetPropExpr(property), true, values); + } + + /// + /// In-expression that is equivalent to the syntax of "property in (value, value, ... value)". + /// + /// provides values to match + /// are expressons that provide match-against values + /// expression + public static InExpression In(Expression value, params Expression[] set) + { + return new InExpression(value, false, set); + } + + /// + /// Not-In-expression that is equivalent to the syntax of "property not in (value, value, ... value)". + /// + /// provides values to match + /// are expressons that provide match-against values + /// expression + public static InExpression NotIn(Expression value, params Expression[] set) + { + return new InExpression(value, true, (Object) set); + } + + /// + /// Not expression negates the sub-expression to the not which is expected to return bool-typed values. + /// + /// is the sub-expression + /// expression + public static NotExpression Not(Expression inner) + { + return new NotExpression(inner); + } + + /// + /// Static method invocation. + /// + /// the name of the class to invoke a method on + /// the name of the method to invoke + /// zero, one or more constants that are the parameters to the static method + /// expression + public static StaticMethodExpression StaticMethod(string className, string method, params Object[] parameters) + { + return new StaticMethodExpression(className, method, parameters); + } + + /// + /// Static method invocation. + /// + /// the name of the class to invoke a method on + /// the name of the method to invoke + /// zero, one or more expressions that provide parameters to the static method + /// expression + public static StaticMethodExpression StaticMethod( + string className, + string method, + params Expression[] parameters) + { + return new StaticMethodExpression(className, method, parameters); + } + + /// + /// Prior function. + /// + /// the numeric index of the prior event + /// the name of the property to obtain the value for + /// expression + public static PriorExpression Prior(int index, string property) + { + return new PriorExpression(index, property); + } + + /// + /// Previous function. + /// + /// provides the numeric index of the previous event + /// the name of the property to obtain the value for + /// expression + public static PreviousExpression Previous(Expression expression, string property) + { + return new PreviousExpression(expression, property); + } + + /// + /// Previous function. + /// + /// the numeric index of the previous event + /// the name of the property to obtain the value for + /// expression + public static PreviousExpression Previous(int index, string property) + { + return new PreviousExpression(index, property); + } + + /// + /// Previous tail function. + /// + /// provides the numeric index of the previous event + /// the name of the property to obtain the value for + /// expression + public static PreviousExpression PreviousTail(Expression expression, string property) + { + var expr = new PreviousExpression(expression, property); + expr.ExpressionType = PreviousExpressionType.PREVTAIL; + return expr; + } + + /// + /// Previous tail function. + /// + /// the numeric index of the previous event + /// the name of the property to obtain the value for + /// expression + public static PreviousExpression PreviousTail(int index, string property) + { + var expr = new PreviousExpression(index, property); + expr.ExpressionType = PreviousExpressionType.PREVTAIL; + return expr; + } + + /// + /// Previous count function. + /// + /// provides the properties or stream name to select for the previous event + /// expression + public static PreviousExpression PreviousCount(string property) + { + return new PreviousExpression(PreviousExpressionType.PREVCOUNT, Property(property)); + } + + /// + /// Previous window function. + /// + /// provides the properties or stream name to select for the previous event + /// expression + public static PreviousExpression PreviousWindow(string property) + { + return new PreviousExpression(PreviousExpressionType.PREVWINDOW, Property(property)); + } + + /// + /// Between. + /// + /// the name of the property supplying data points. + /// the name of the property supplying lower boundary. + /// the name of the property supplying upper boundary. + /// expression + public static BetweenExpression BetweenProperty( + string property, + string lowBoundaryProperty, + string highBoundaryProperty) + { + return new BetweenExpression( + GetPropExpr(property), GetPropExpr(lowBoundaryProperty), GetPropExpr(highBoundaryProperty)); + } + + /// + /// Between. + /// + /// the name of the property that returns the datapoint to check range + /// constant indicating the lower boundary + /// constant indicating the upper boundary + /// expression + public static BetweenExpression Between(string property, Object lowBoundary, Object highBoundary) + { + return new BetweenExpression( + GetPropExpr(property), new ConstantExpression(lowBoundary), new ConstantExpression(highBoundary)); + } + + /// + /// Between. + /// + /// returns the datapoint to check range + /// returns values for the lower boundary + /// returns values for the upper boundary + /// expression + public static BetweenExpression Between(Expression datapoint, Expression lowBoundary, Expression highBoundary) + { + return new BetweenExpression(datapoint, lowBoundary, highBoundary); + } + + /// + /// Between (or range). + /// + /// returns the datapoint to check range + /// returns values for the lower boundary + /// returns values for the upper boundary + /// true to indicate lower boundary itself is included in the range + /// true to indicate upper boundary itself is included in the range + /// expression + public static BetweenExpression Range( + Expression datapoint, + Expression lowBoundary, + Expression highBoundary, + bool isLowIncluded, + bool isHighIncluded) + { + return new BetweenExpression(datapoint, lowBoundary, highBoundary, isLowIncluded, isHighIncluded, false); + } + + /// + /// Logical OR disjunction. Use add methods to add expressions. + /// + /// expression + public static Disjunction Or() + { + return new Disjunction(); + } + + /// + /// Logical OR disjunction. + /// + /// an expression returning values to junction + /// an expression returning values to junction + /// an optional list of expressions returning values to junction + /// expression + public static Disjunction Or(Expression first, Expression second, params Expression[] expressions) + { + return new Disjunction(first, second, expressions); + } + + /// + /// Logical AND conjunction. Use add methods to add expressions. + /// + /// expression + public static Conjunction And() + { + return new Conjunction(); + } + + /// + /// Logical AND conjunction. + /// + /// an expression returning values to junction + /// an expression returning values to junction + /// an optional list of expressions returning values to junction + /// expression + public static Conjunction And(Expression first, Expression second, params Expression[] expressions) + { + return new Conjunction(first, second, expressions); + } + + /// + /// Greater-or-equal between a property and a constant. + /// + /// the name of the property providing left hand side values + /// is the constant to compare + /// expression + public static RelationalOpExpression Ge(string propertyName, Object value) + { + return new RelationalOpExpression(GetPropExpr(propertyName), ">=", new ConstantExpression(value)); + } + + /// + /// Greater-or-equals between expression results. + /// + /// the expression providing left hand side values + /// the expression providing right hand side values + /// expression + public static RelationalOpExpression Ge(Expression left, Expression right) + { + return new RelationalOpExpression(left, ">=", right); + } + + /// + /// Greater-or-equal between properties. + /// + /// the name of the property providing left hand side values + /// the name of the property providing right hand side values + /// expression + public static RelationalOpExpression GeProperty(string propertyLeft, string propertyRight) + { + return new RelationalOpExpression( + GetPropExpr(propertyLeft), ">=", new PropertyValueExpression(propertyRight)); + } + + /// + /// Greater-then between a property and a constant. + /// + /// the name of the property providing left hand side values + /// is the constant to compare + /// expression + public static RelationalOpExpression Gt(string propertyName, Object value) + { + return new RelationalOpExpression(GetPropExpr(propertyName), ">", new ConstantExpression(value)); + } + + /// + /// Greater-then between expression results. + /// + /// the expression providing left hand side values + /// the expression providing right hand side values + /// expression + public static RelationalOpExpression Gt(Expression left, Expression right) + { + return new RelationalOpExpression(left, ">", right); + } + + /// + /// Greater-then between properties. + /// + /// the name of the property providing left hand side values + /// the name of the property providing right hand side values + /// expression + public static RelationalOpExpression GtProperty(string propertyLeft, string propertyRight) + { + return new RelationalOpExpression( + GetPropExpr(propertyLeft), ">", new PropertyValueExpression(propertyRight)); + } + + /// + /// Less-or-equals between a property and a constant. + /// + /// the name of the property providing left hand side values + /// is the constant to compare + /// expression + public static RelationalOpExpression Le(string propertyName, Object value) + { + return new RelationalOpExpression(GetPropExpr(propertyName), "<=", new ConstantExpression(value)); + } + + /// + /// Less-or-equal between properties. + /// + /// the name of the property providing left hand side values + /// the name of the property providing right hand side values + /// expression + public static RelationalOpExpression LeProperty(string propertyLeft, string propertyRight) + { + return new RelationalOpExpression( + GetPropExpr(propertyLeft), "<=", new PropertyValueExpression(propertyRight)); + } + + /// + /// Less-or-equal between expression results. + /// + /// the expression providing left hand side values + /// the expression providing right hand side values + /// expression + public static RelationalOpExpression Le(Expression left, Expression right) + { + return new RelationalOpExpression(left, "<=", right); + } + + /// + /// Less-then between a property and a constant. + /// + /// the name of the property providing left hand side values + /// is the constant to compare + /// expression + public static RelationalOpExpression Lt(string propertyName, Object value) + { + return new RelationalOpExpression(GetPropExpr(propertyName), "<", new ConstantExpression(value)); + } + + /// + /// Less-then between properties. + /// + /// the name of the property providing left hand side values + /// the name of the property providing right hand side values + /// expression + public static RelationalOpExpression LtProperty(string propertyLeft, string propertyRight) + { + return new RelationalOpExpression( + GetPropExpr(propertyLeft), "<", new PropertyValueExpression(propertyRight)); + } + + /// + /// Less-then between expression results. + /// + /// the expression providing left hand side values + /// the expression providing right hand side values + /// expression + public static RelationalOpExpression Lt(Expression left, Expression right) + { + return new RelationalOpExpression(left, "<", right); + } + + /// + /// Equals between a property and a constant. + /// + /// the name of the property providing left hand side values + /// is the constant to compare + /// expression + public static RelationalOpExpression Eq(string propertyName, Object value) + { + return new RelationalOpExpression(GetPropExpr(propertyName), "=", new ConstantExpression(value)); + } + + /// + /// Not-Equals between a property and a constant. + /// + /// the name of the property providing left hand side values + /// is the constant to compare + /// expression + public static RelationalOpExpression Neq(string propertyName, Object value) + { + return new RelationalOpExpression(GetPropExpr(propertyName), "!=", new ConstantExpression(value)); + } + + /// + /// Equals between properties. + /// + /// the name of the property providing left hand side values + /// the name of the property providing right hand side values + /// expression + public static RelationalOpExpression EqProperty(string propertyLeft, string propertyRight) + { + return new RelationalOpExpression( + GetPropExpr(propertyLeft), "=", new PropertyValueExpression(propertyRight)); + } + + /// + /// Not-Equals between properties. + /// + /// the name of the property providing left hand side values + /// the name of the property providing right hand side values + /// expression + public static RelationalOpExpression NeqProperty(string propertyLeft, string propertyRight) + { + return new RelationalOpExpression( + GetPropExpr(propertyLeft), "!=", new PropertyValueExpression(propertyRight)); + } + + /// + /// Equals between expression results. + /// + /// the expression providing left hand side values + /// the expression providing right hand side values + /// expression + public static RelationalOpExpression Eq(Expression left, Expression right) + { + return new RelationalOpExpression(left, "=", right); + } + + /// + /// Not-Equals between expression results. + /// + /// the expression providing left hand side values + /// the expression providing right hand side values + /// expression + public static RelationalOpExpression Neq(Expression left, Expression right) + { + return new RelationalOpExpression(left, "!=", right); + } + + /// + /// Not-null test. + /// + /// the name of the property supplying the value to check for null + /// expression + public static RelationalOpExpression IsNotNull(string property) + { + return new RelationalOpExpression(GetPropExpr(property), "is not", null); + } + + /// + /// Not-null test. + /// + /// supplies the value to check for null + /// expression + public static RelationalOpExpression IsNotNull(Expression expression) + { + return new RelationalOpExpression(expression, "is not", null); + } + + /// + /// Is-null test. + /// + /// the name of the property supplying the value to check for null + /// expression + public static RelationalOpExpression IsNull(string property) + { + return new RelationalOpExpression(GetPropExpr(property), "is", null); + } + + /// + /// Is-null test. + /// + /// supplies the value to check for null + /// expression + public static RelationalOpExpression IsNull(Expression expression) + { + return new RelationalOpExpression(expression, "is", null); + } + + /// + /// Property value. + /// + /// An expression that returns the value of the named property. + /// + /// + /// Nested, indexed or mapped properties follow the documented sytnax. + /// + /// + /// is the name of the property to return the value for. + /// expression + public static PropertyValueExpression Property(string propertyName) + { + return GetPropExpr(propertyName); + } + + /// + /// SQL-Like. + /// + /// the name of the property providing values to match + /// is the string to match against + /// expression + public static LikeExpression Like(string propertyName, string value) + { + return new LikeExpression(GetPropExpr(propertyName), new ConstantExpression(value)); + } + + /// + /// SQL-Like. + /// + /// provides value to match + /// provides string to match against + /// expression + public static LikeExpression Like(Expression left, Expression right) + { + return new LikeExpression(left, right); + } + + /// + /// SQL-Like. + /// + /// the name of the property providing values to match + /// is the string to match against + /// the escape Character(s) + /// expression + public static LikeExpression Like(string propertyName, Object value, string escape) + { + return new LikeExpression( + GetPropExpr(propertyName), new ConstantExpression(value), new ConstantExpression(escape)); + } + + /// + /// SQL-Like. + /// + /// provides value to match + /// provides string to match against + /// the escape Character(s) + /// expression + public static LikeExpression Like(Expression left, Expression right, Expression escape) + { + return new LikeExpression(left, right, escape); + } + + /// + /// SQL-Like negated (not like). + /// + /// the name of the property providing values to match + /// is the string to match against + /// expression + public static LikeExpression NotLike(string propertyName, string value) + { + return new LikeExpression(GetPropExpr(propertyName), new ConstantExpression(value), true); + } + + /// + /// SQL-Like negated (not like). + /// + /// provides value to match + /// provides string to match against + /// expression + public static LikeExpression NotLike(Expression left, Expression right) + { + return new LikeExpression(left, right, true); + } + + /// + /// SQL-Like negated (not like). + /// + /// the name of the property providing values to match + /// is the string to match against + /// the escape Character(s) + /// expression + public static LikeExpression NotLike(string propertyName, Object value, string escape) + { + return new LikeExpression( + GetPropExpr(propertyName), new ConstantExpression(value), new ConstantExpression(escape), true); + } + + /// + /// SQL-Like negated (not like). + /// + /// provides value to match + /// provides string to match against + /// the escape Character(s) + /// expression + public static LikeExpression NotLike(Expression left, Expression right, Expression escape) + { + return new LikeExpression(left, right, escape, true); + } + + /// + /// Average aggregation function. + /// + /// name of the property providing the values to aggregate. + /// expression + public static AvgProjectionExpression Avg(string propertyName) + { + return new AvgProjectionExpression(GetPropExpr(propertyName), false); + } + + /// + /// Average aggregation function. + /// + /// provides the values to aggregate. + /// expression + public static AvgProjectionExpression Avg(Expression expression) + { + return new AvgProjectionExpression(expression, false); + } + + /// + /// Average aggregation function considering distinct values only. + /// + /// name of the property providing the values to aggregate. + /// expression + public static AvgProjectionExpression AvgDistinct(string propertyName) + { + return new AvgProjectionExpression(GetPropExpr(propertyName), true); + } + + /// + /// Average aggregation function considering distinct values only. + /// + /// provides the values to aggregate. + /// expression + public static AvgProjectionExpression AvgDistinct(Expression expression) + { + return new AvgProjectionExpression(expression, true); + } + + /// + /// Median aggregation function. + /// + /// name of the property providing the values to aggregate. + /// expression + public static MedianProjectionExpression Median(string propertyName) + { + return new MedianProjectionExpression(GetPropExpr(propertyName), false); + } + + /// + /// Median aggregation function. + /// + /// provides the values to aggregate. + /// expression + public static MedianProjectionExpression Median(Expression expression) + { + return new MedianProjectionExpression(expression, false); + } + + /// + /// Median aggregation function considering distinct values only. + /// + /// name of the property providing the values to aggregate. + /// expression + public static MedianProjectionExpression MedianDistinct(string propertyName) + { + return new MedianProjectionExpression(GetPropExpr(propertyName), true); + } + + /// + /// Median aggregation function considering distinct values only. + /// + /// provides the values to aggregate. + /// expression + public static MedianProjectionExpression MedianDistinct(Expression expression) + { + return new MedianProjectionExpression(expression, true); + } + + /// + /// Standard deviation aggregation function. + /// + /// name of the property providing the values to aggregate. + /// expression + public static StddevProjectionExpression Stddev(string propertyName) + { + return new StddevProjectionExpression(GetPropExpr(propertyName), false); + } + + /// + /// Standard deviation aggregation function. + /// + /// provides the values to aggregate. + /// expression + public static StddevProjectionExpression Stddev(Expression expression) + { + return new StddevProjectionExpression(expression, false); + } + + /// + /// Standard deviation function considering distinct values only. + /// + /// name of the property providing the values to aggregate. + /// expression + public static StddevProjectionExpression StddevDistinct(string propertyName) + { + return new StddevProjectionExpression(GetPropExpr(propertyName), true); + } + + /// + /// Standard deviation function considering distinct values only. + /// + /// provides the values to aggregate. + /// expression + public static StddevProjectionExpression StddevDistinct(Expression expression) + { + return new StddevProjectionExpression(expression, true); + } + + /// + /// Mean deviation aggregation function. + /// + /// name of the property providing the values to aggregate. + /// expression + public static AvedevProjectionExpression Avedev(string propertyName) + { + return new AvedevProjectionExpression(GetPropExpr(propertyName), false); + } + + /// + /// Lastever-value aggregation function. + /// + /// name of the property providing the values to aggregate. + /// expression + public static LastEverProjectionExpression LastEver(string propertyName) + { + return new LastEverProjectionExpression(GetPropExpr(propertyName), false); + } + + /// + /// Lastever-value aggregation function. + /// + /// name of the property providing the values to aggregate. + /// expression + public static LastProjectionExpression Last(string propertyName) + { + return new LastProjectionExpression(GetPropExpr(propertyName)); + } + + /// + /// Lastever-value aggregation function. + /// + /// provides the values to aggregate. + /// expression + public static LastEverProjectionExpression LastEver(Expression expression) + { + return new LastEverProjectionExpression(expression, false); + } + + /// + /// Lastever-value aggregation function. + /// + /// provides the values to aggregate. + /// expression + public static LastProjectionExpression Last(Expression expression) + { + return new LastProjectionExpression(expression); + } + + /// + /// First-value (windowed) aggregation function. + /// + /// name of the property providing the values to aggregate. + /// expression + public static FirstProjectionExpression First(string propertyName) + { + return new FirstProjectionExpression(GetPropExpr(propertyName)); + } + + /// + /// First-value (ever) aggregation function. + /// + /// name of the property providing the values to aggregate. + /// expression + public static FirstEverProjectionExpression FirstEver(string propertyName) + { + return new FirstEverProjectionExpression(GetPropExpr(propertyName), false); + } + + /// + /// First-value (in window) aggregation function. + /// + /// provides the values to aggregate. + /// expression + public static FirstProjectionExpression First(Expression expression) + { + return new FirstProjectionExpression(expression); + } + + /// + /// First-value (ever) aggregation function. + /// + /// provides the values to aggregate. + /// expression + public static FirstEverProjectionExpression FirstEver(Expression expression) + { + return new FirstEverProjectionExpression(expression, false); + } + + /// + /// Mean deviation aggregation function. + /// + /// provides the values to aggregate. + /// expression + public static AvedevProjectionExpression Avedev(Expression expression) + { + return new AvedevProjectionExpression(expression, false); + } + + /// + /// Mean deviation function considering distinct values only. + /// + /// name of the property providing the values to aggregate. + /// expression + public static AvedevProjectionExpression AvedevDistinct(string propertyName) + { + return new AvedevProjectionExpression(GetPropExpr(propertyName), false); + } + + /// + /// Mean deviation function considering distinct values only. + /// + /// provides the values to aggregate. + /// expression + public static AvedevProjectionExpression AvedevDistinct(Expression expression) + { + return new AvedevProjectionExpression(expression, false); + } + + /// + /// Sum aggregation function. + /// + /// name of the property providing the values to aggregate. + /// expression + public static SumProjectionExpression Sum(string propertyName) + { + return new SumProjectionExpression(GetPropExpr(propertyName), false); + } + + /// + /// Sum aggregation function. + /// + /// provides the values to aggregate. + /// expression + public static SumProjectionExpression Sum(Expression expression) + { + return new SumProjectionExpression(expression, false); + } + + /// + /// Sum aggregation function considering distinct values only. + /// + /// name of the property providing the values to aggregate. + /// expression + public static SumProjectionExpression SumDistinct(string propertyName) + { + return new SumProjectionExpression(GetPropExpr(propertyName), true); + } + + /// + /// Sum aggregation function considering distinct values only. + /// + /// provides the values to aggregate. + /// expression + public static SumProjectionExpression SumDistinct(Expression expression) + { + return new SumProjectionExpression(expression, true); + } + + /// + /// Count aggregation function not counting values, equivalent to "Count(*)". + /// + /// expression + public static CountStarProjectionExpression CountStar() + { + var expr = new CountStarProjectionExpression(); + expr.AddChild(new WildcardExpression()); + return expr; + } + + /// + /// Count aggregation function. + /// + /// name of the property providing the values to count. + /// expression + public static CountProjectionExpression Count(string propertyName) + { + return new CountProjectionExpression(GetPropExpr(propertyName), false); + } + + /// + /// Count aggregation function. + /// + /// provides the values to count. + /// expression + public static CountProjectionExpression Count(Expression expression) + { + return new CountProjectionExpression(expression, false); + } + + /// + /// Count aggregation function considering distinct values only. + /// + /// name of the property providing the values to count. + /// expression + public static CountProjectionExpression CountDistinct(string propertyName) + { + return new CountProjectionExpression(GetPropExpr(propertyName), true); + } + + /// + /// Count aggregation function considering distinct values only. + /// + /// provides the values to count. + /// expression + public static CountProjectionExpression CountDistinct(Expression expression) + { + return new CountProjectionExpression(expression, true); + } + + /// + /// Minimum aggregation function. + /// + /// name of the property providing the values to aggregate. + /// expression + public static MinProjectionExpression Min(string propertyName) + { + return new MinProjectionExpression(GetPropExpr(propertyName), false); + } + + /// + /// Minimum aggregation function. + /// + /// provides the values to aggregate. + /// expression + public static MinProjectionExpression Min(Expression expression) + { + return new MinProjectionExpression(expression, false); + } + + /// + /// Minimum aggregation function considering distinct values only. + /// + /// name of the property providing the values to aggregate. + /// expression + public static MinProjectionExpression MinDistinct(string propertyName) + { + return new MinProjectionExpression(GetPropExpr(propertyName), true); + } + + /// + /// Minimum aggregation function considering distinct values only. + /// + /// provides the values to aggregate. + /// expression + public static MinProjectionExpression MinDistinct(Expression expression) + { + return new MinProjectionExpression(expression, true); + } + + /// + /// Maximum aggregation function. + /// + /// name of the property providing the values to aggregate. + /// expression + public static MaxProjectionExpression Max(string propertyName) + { + return new MaxProjectionExpression(GetPropExpr(propertyName), false); + } + + /// + /// Maximum aggregation function. + /// + /// provides the values to aggregate. + /// expression + public static MaxProjectionExpression Max(Expression expression) + { + return new MaxProjectionExpression(expression, false); + } + + /// + /// Maximum aggregation function considering distinct values only. + /// + /// name of the property providing the values to aggregate. + /// expression + public static MaxProjectionExpression MaxDistinct(string propertyName) + { + return new MaxProjectionExpression(GetPropExpr(propertyName), true); + } + + /// + /// Maximum aggregation function considering distinct values only. + /// + /// provides the values to aggregate. + /// expression + public static MaxProjectionExpression MaxDistinct(Expression expression) + { + return new MaxProjectionExpression(expression, true); + } + + /// + /// Modulo. + /// + /// the expression providing left hand values + /// the expression providing right hand values + /// expression + public static ArithmaticExpression Modulo(Expression left, Expression right) + { + return new ArithmaticExpression(left, "%", right); + } + + /// + /// Modulo. + /// + /// the name of the property providing left hand values + /// the name of the property providing right hand values + /// expression + public static ArithmaticExpression Modulo(string propertyLeft, string propertyRight) + { + return new ArithmaticExpression( + new PropertyValueExpression(propertyLeft), "%", new PropertyValueExpression(propertyRight)); + } + + /// + /// Subtraction. + /// + /// the expression providing left hand values + /// the expression providing right hand values + /// expression + public static ArithmaticExpression Minus(Expression left, Expression right) + { + return new ArithmaticExpression(left, "-", right); + } + + /// + /// Subtraction. + /// + /// the name of the property providing left hand values + /// the name of the property providing right hand values + /// expression + public static ArithmaticExpression Minus(string propertyLeft, string propertyRight) + { + return new ArithmaticExpression( + new PropertyValueExpression(propertyLeft), "-", new PropertyValueExpression(propertyRight)); + } + + /// + /// Addition. + /// + /// the expression providing left hand values + /// the expression providing right hand values + /// expression + public static ArithmaticExpression Plus(Expression left, Expression right) + { + return new ArithmaticExpression(left, "+", right); + } + + /// + /// Addition. + /// + /// the name of the property providing left hand values + /// the name of the property providing right hand values + /// expression + public static ArithmaticExpression Plus(string propertyLeft, string propertyRight) + { + return new ArithmaticExpression( + new PropertyValueExpression(propertyLeft), "+", new PropertyValueExpression(propertyRight)); + } + + /// + /// Multiplication. + /// + /// the expression providing left hand values + /// the expression providing right hand values + /// expression + public static ArithmaticExpression Multiply(Expression left, Expression right) + { + return new ArithmaticExpression(left, "*", right); + } + + /// + /// Multiplication. + /// + /// the name of the property providing left hand values + /// the name of the property providing right hand values + /// expression + public static ArithmaticExpression Multiply(string propertyLeft, string propertyRight) + { + return new ArithmaticExpression( + new PropertyValueExpression(propertyLeft), "*", new PropertyValueExpression(propertyRight)); + } + + /// + /// Division. + /// + /// the expression providing left hand values + /// the expression providing right hand values + /// expression + public static ArithmaticExpression Divide(Expression left, Expression right) + { + return new ArithmaticExpression(left, "/", right); + } + + /// + /// Division. + /// + /// the name of the property providing left hand values + /// the name of the property providing right hand values + /// expression + public static ArithmaticExpression Divide(string propertyLeft, string propertyRight) + { + return new ArithmaticExpression( + new PropertyValueExpression(propertyLeft), "/", new PropertyValueExpression(propertyRight)); + } + + /// + /// Concatenation. + /// + /// the name of property returning values to concatenate + /// the names of additional properties returning values to concatenate + /// expression + public static ConcatExpression Concat(string property, params string[] properties) + { + var concat = new ConcatExpression(); + concat.Children.Add(new PropertyValueExpression(property)); + concat.Children.AddAll(ToPropertyExpressions(properties)); + return concat; + } + + /// + /// Subquery. + /// + /// is the object model of the lookup + /// expression + public static SubqueryExpression Subquery(EPStatementObjectModel model) + { + return new SubqueryExpression(model); + } + + /// + /// Subquery with in-clause, represents the syntax of "value in (select ... from ...)". + /// + /// is the name of the property that returns the value to match against the values returned by the lookup + /// is the object model of the lookup + /// expression + public static SubqueryInExpression SubqueryIn(string property, EPStatementObjectModel model) + { + return new SubqueryInExpression(GetPropExpr(property), model, false); + } + + /// + /// Subquery with not-in-clause, represents the syntax of "value not in (select ... from ...)". + /// + /// is the name of the property that returns the value to match against the values returned by the lookup + /// is the object model of the lookup + /// expression + public static SubqueryInExpression SubqueryNotIn(string property, EPStatementObjectModel model) + { + return new SubqueryInExpression(GetPropExpr(property), model, true); + } + + /// + /// Subquery with exists-clause, represents the syntax of "select * from ... where exists (select ... from ...)". + /// + /// is the object model of the lookup + /// expression + public static SubqueryExistsExpression SubqueryExists(EPStatementObjectModel model) + { + return new SubqueryExistsExpression(model); + } + + /// + /// Subquery with in-clause, represents the syntax of "value in (select ... from ...)". + /// + /// returns the value to match against the values returned by the lookup + /// is the object model of the lookup + /// expression + public static SubqueryInExpression SubqueryIn(Expression expression, EPStatementObjectModel model) + { + return new SubqueryInExpression(expression, model, false); + } + + /// + /// Subquery with not-in-clause, represents the syntax of "value not in (select ... from ...)". + /// + /// returns the value to match against the values returned by the lookup + /// is the object model of the lookup + /// expression + public static SubqueryInExpression SubqueryNotIn(Expression expression, EPStatementObjectModel model) + { + return new SubqueryInExpression(expression, model, true); + } + + /// + /// Returns a time period expression for the specified parts. + /// + /// Each part can be a null value in which case the part is left out. + /// + /// + /// day part + /// hour part + /// minute part + /// seconds part + /// milliseconds part + /// time period expression + public static TimePeriodExpression TimePeriod( + double? days, + double? hours, + double? minutes, + double? seconds, + double? milliseconds) + { + Expression daysExpr = (days != null) ? Constant(days) : null; + Expression hoursExpr = (hours != null) ? Constant(hours) : null; + Expression minutesExpr = (minutes != null) ? Constant(minutes) : null; + Expression secondsExpr = (seconds != null) ? Constant(seconds) : null; + Expression millisecondsExpr = (milliseconds != null) ? Constant(milliseconds) : null; + return new TimePeriodExpression(daysExpr, hoursExpr, minutesExpr, secondsExpr, millisecondsExpr); + } + + /// + /// Returns a time period expression for the specified parts. + /// Each part can be a null value in which case the part is left out. + /// Each object value may be a String value for an event property, or a number for a constant. + /// + /// day part + /// hour part + /// minute part + /// seconds part + /// milliseconds part + /// time period expression + public static TimePeriodExpression TimePeriod( + int? days, + int? hours, + int? minutes, + int? seconds, + int? milliseconds) + { + Expression daysExpr = ConvertVariableNumeric(days); + Expression hoursExpr = ConvertVariableNumeric(hours); + Expression minutesExpr = ConvertVariableNumeric(minutes); + Expression secondsExpr = ConvertVariableNumeric(seconds); + Expression millisecondsExpr = ConvertVariableNumeric(milliseconds); + return new TimePeriodExpression(daysExpr, hoursExpr, minutesExpr, secondsExpr, millisecondsExpr); + } + + /// + /// Returns a time period expression for the specified parts. + /// + /// Each part can be a null value in which case the part is left out. + /// + /// + /// Each object value may be a string value for an event property, or a number for a constant. + /// + /// + /// day part + /// hour part + /// minute part + /// seconds part + /// milliseconds part + /// time period expression + public static TimePeriodExpression TimePeriod( + Object days, + Object hours, + Object minutes, + Object seconds, + Object milliseconds) + { + Expression daysExpr = ConvertVariableNumeric(days); + Expression hoursExpr = ConvertVariableNumeric(hours); + Expression minutesExpr = ConvertVariableNumeric(minutes); + Expression secondsExpr = ConvertVariableNumeric(seconds); + Expression millisecondsExpr = ConvertVariableNumeric(milliseconds); + return new TimePeriodExpression(daysExpr, hoursExpr, minutesExpr, secondsExpr, millisecondsExpr); + } + + /// + /// Creates a wildcard parameter. + /// + /// parameter + public static CrontabParameterExpression CrontabScheduleWildcard() + { + return new CrontabParameterExpression(ScheduleItemType.WILDCARD); + } + + /// + /// Creates a parameter of the given type and parameterized by a number. + /// + /// the constant parameter for the type + /// the type of crontab parameter + /// crontab parameter + public static CrontabParameterExpression CrontabScheduleItem(int? parameter, ScheduleItemType type) + { + var param = new CrontabParameterExpression(type); + if (parameter != null) + { + param.AddChild(Expressions.Constant(parameter)); + } + return param; + } + + /// + /// Creates a frequency cron parameter. + /// + /// the constant for the frequency + /// cron parameter + public static CrontabFrequencyExpression CrontabScheduleFrequency(int frequency) + { + return new CrontabFrequencyExpression(Constant(frequency)); + } + + /// + /// Creates a range cron parameter. + /// + /// the lower bounds + /// the upper bounds + /// crontab parameter + public static CrontabRangeExpression CrontabScheduleRange(int lowerBounds, int upperBounds) + { + return new CrontabRangeExpression(Constant(lowerBounds), Constant(upperBounds)); + } + + /// + /// Returns a list of expressions returning property values for the property names passed in. + /// + /// is a list of property names + /// list of property value expressions + internal static List ToPropertyExpressions(params string[] properties) + { + var expr = new List(); + foreach (string property in properties) + { + expr.Add(GetPropExpr(property)); + } + return expr; + } + + /// + /// Returns an expression returning the propertyName value for the propertyName name passed in. + /// + /// the name of the property returning property values + /// expression + internal static PropertyValueExpression GetPropExpr(string propertyName) + { + return new PropertyValueExpression(propertyName); + } + + private static Expression ConvertVariableNumeric(Object @object) + { + if (@object == null) + { + return null; + } + if (@object is string) + { + return Property(@object.ToString()); + } + if (@object.IsNumber()) + { + return Constant(@object); + } + throw new ArgumentException("Invalid object value, expecting string or numeric value"); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/client/soda/Filter.cs b/NEsper.Core/NEsper.Core/client/soda/Filter.cs new file mode 100755 index 000000000..9f59d9eae --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/Filter.cs @@ -0,0 +1,98 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Filter defines the event type to be filtered for, and an optional expression that returns true if + /// the filter should consider the event, or false to reject the event. + /// + [Serializable] + public class Filter + { + /// Ctor. + public Filter() { + } + + /// + /// Ctor. + /// + /// is the event type name + public Filter(string eventTypeName) { + EventTypeName = eventTypeName; + } + + /// + /// Ctor. + /// + /// is the event type name + /// is the filter expression + public Filter(string eventTypeName, Expression filterExpression) { + EventTypeName = eventTypeName; + FilterExpression = filterExpression; + } + + /// + /// Creates a filter to the given named event type. + /// + /// is the event type name to filter for + /// filter + public static Filter Create(string eventTypeName) { + return new Filter(eventTypeName); + } + + /// + /// Creates a filter to the given named event type and filter expression. + /// + /// is the event type name to filter for + /// is the expression filtering out events + /// filter is the filter expression + public static Filter Create(string eventTypeName, Expression filter) { + return new Filter(eventTypeName, filter); + } + + /// + /// Returns the name of the event type to filter for. + /// + /// event type name + public string EventTypeName { get; set; } + + /// + /// Returns the optional filter expression that tests the event, or null if no filter expression was defined. + /// + /// filter expression + public Expression FilterExpression { get; set; } + + /// + /// Returns contained-event spec. + /// + /// spec + public IList OptionalPropertySelects { get; set; } + + /// + /// Returns a textual representation of the filter. + /// + /// to output to + /// for newline-whitespace formatting + public void ToEPL(TextWriter writer, EPStatementFormatter formatter) { + writer.Write(EventTypeName); + if (FilterExpression != null) { + writer.Write('('); + FilterExpression.ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + writer.Write(')'); + } + if (OptionalPropertySelects != null) { + ContainedEventSelect.ToEPL(writer, formatter, OptionalPropertySelects); + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/client/soda/FilterStream.cs b/NEsper.Core/NEsper.Core/client/soda/FilterStream.cs new file mode 100755 index 000000000..355e70697 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/FilterStream.cs @@ -0,0 +1,145 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.client.soda +{ + /// + /// A stream upon which projections (views) can be added that selects events by name and filter expression. + /// + [Serializable] + public class FilterStream : ProjectedStream + { + /// + /// Ctor. + /// + public FilterStream() + { + } + + /// + /// Creates a stream using a filter that provides the event type name and filter expression to filter for. + /// + /// defines what to look for + /// stream + public static FilterStream Create(Filter filter) + { + return new FilterStream(filter); + } + + /// + /// Creates a stream of events of the given name. + /// + /// is the event type name to filter for + /// stream + public static FilterStream Create(string eventTypeName) + { + return new FilterStream(Filter.Create(eventTypeName)); + } + + /// + /// Creates a stream of events of the given event type name and names that stream. Example: "select * from MyeventTypeName as MaskTypeName". + /// + /// is the event type name to filter for + /// is an optional stream name + /// stream + public static FilterStream Create(string eventTypeName, string streamName) + { + return new FilterStream(Filter.Create(eventTypeName), streamName); + } + + /// + /// Creates a stream using a filter that provides the event type name and filter expression to filter for. + /// + /// defines what to look for + /// is an optional stream name + /// stream + public static FilterStream Create(Filter filter, string streamName) + { + return new FilterStream(filter, streamName); + } + + /// + /// Creates a stream of events of the given event type name and names that stream. Example: "select * from MyeventTypeName as MaskTypeName". + /// + /// is the event type name to filter for + /// is the filter expression removing events from the stream + /// stream + public static FilterStream Create(string eventTypeName, Expression filter) + { + return new FilterStream(Filter.Create(eventTypeName, filter)); + } + + /// + /// Creates a stream of events of the given event type name and names that stream. Example: "select * from MyeventTypeName as MaskTypeName". + /// + /// is the event type name to filter for + /// is the filter expression removing events from the stream + /// is an optional stream name + /// stream + public static FilterStream Create(string eventTypeName, string streamName, Expression filter) + { + return new FilterStream(Filter.Create(eventTypeName, filter), streamName); + } + + /// + /// Ctor. + /// + /// specifies what events to look for + public FilterStream(Filter filter) + : base(new List(), null) + { + Filter = filter; + } + + /// + /// Ctor. + /// + /// specifies what events to look for + /// is the as-name for the stream + public FilterStream(Filter filter, string name) + : base(new List(), name) + { + Filter = filter; + } + + /// + /// Ctor. + /// + /// specifies what events to look for + /// is the as-name for the stream + /// is a list of projections onto the stream + public FilterStream(Filter filter, string name, IList views) + : base(views, name) + { + Filter = filter; + } + + /// + /// Returns the filter. + /// + /// filter + public Filter Filter { get; set; } + + public override void ToEPLProjectedStream(TextWriter writer, EPStatementFormatter formatter) + { + Filter.ToEPL(writer, formatter); + } + + public override void ToEPLProjectedStreamType(TextWriter writer) + { + writer.Write(Filter.EventTypeName); + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/FireAndForgetClause.cs b/NEsper.Core/NEsper.Core/client/soda/FireAndForgetClause.cs new file mode 100755 index 000000000..ffbda565e --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/FireAndForgetClause.cs @@ -0,0 +1,18 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.client.soda +{ + /// + /// Marker interface used for fire-and-forget (on-demand) queries such as "Update...set" + /// and "delete from..." that can be executed via the API. + /// + public interface FireAndForgetClause + { + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/FireAndForgetDelete.cs b/NEsper.Core/NEsper.Core/client/soda/FireAndForgetDelete.cs new file mode 100755 index 000000000..a2e645a12 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/FireAndForgetDelete.cs @@ -0,0 +1,17 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.client.soda +{ + /// + /// Fire-and-forget (on-demand) delete DML. + /// + public class FireAndForgetDelete : FireAndForgetClause + { + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/FireAndForgetInsert.cs b/NEsper.Core/NEsper.Core/client/soda/FireAndForgetInsert.cs new file mode 100755 index 000000000..b019fe1d2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/FireAndForgetInsert.cs @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.client.soda +{ + /// + /// Fire-and-forget (on-demand) insert DML. + /// + /// The insert-into clause holds the named window name and column names. The select-clause + /// list holds the values to be inserted. + /// + public class FireAndForgetInsert : FireAndForgetClause + { + private bool _useValuesKeyword = true; + + /// Ctor. + /// whether to use the "values" keyword or whether the syntax is based on select + public FireAndForgetInsert(bool useValuesKeyword) + { + this._useValuesKeyword = useValuesKeyword; + } + + /// Ctor. + public FireAndForgetInsert() + { + } + + /// Returns indicator whether to use the values keyword. + /// indicator + public bool IsUseValuesKeyword + { + get { return _useValuesKeyword; } + set { this._useValuesKeyword = value; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/FireAndForgetUpdate.cs b/NEsper.Core/NEsper.Core/client/soda/FireAndForgetUpdate.cs new file mode 100755 index 000000000..a5afea00d --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/FireAndForgetUpdate.cs @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.client.soda +{ + /// Fire-and-forget (on-demand) Update DML. + [Serializable] + public class FireAndForgetUpdate : FireAndForgetClause + { + /// Returns the set-assignments. + /// assignments + public IList Assignments { get; set; } + + /// + /// Initializes a new instance of the class. + /// + public FireAndForgetUpdate() + { + Assignments = new List(); + } + + + /// Add an assignment + /// to add + /// assignment + public IList AddAssignment(Assignment assignment) + { + Assignments.Add(assignment); + return Assignments; + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/soda/FirstEverProjectionExpression.cs b/NEsper.Core/NEsper.Core/client/soda/FirstEverProjectionExpression.cs new file mode 100755 index 000000000..256f475af --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/FirstEverProjectionExpression.cs @@ -0,0 +1,62 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Represents the "firstever" aggregation function. + /// + [Serializable] + public class FirstEverProjectionExpression : ExpressionBase + { + /// + /// Ctor. + /// + public FirstEverProjectionExpression() { + } + + /// + /// Ctor. + /// + /// true for distinct + public FirstEverProjectionExpression(bool isDistinct) + { + IsDistinct = isDistinct; + } + + /// + /// Ctor. + /// + /// to aggregate + /// true for distinct + public FirstEverProjectionExpression(Expression expression, bool isDistinct) + { + IsDistinct = isDistinct; + Children.Add(expression); + } + + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.UNARY; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + RenderAggregation(writer, "firstever", IsDistinct, Children); + } + + /// + /// Returns true for distinct. + /// + /// boolean indicating distinct or not + public bool IsDistinct { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/FirstProjectionExpression.cs b/NEsper.Core/NEsper.Core/client/soda/FirstProjectionExpression.cs new file mode 100755 index 000000000..38318bdd3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/FirstProjectionExpression.cs @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.soda +{ + /// + /// Represents the "first" aggregation function. + /// + [Serializable] + public class FirstProjectionExpression + : AccessProjectionExpressionBase + { + /// + /// Ctor. + /// + public FirstProjectionExpression() + { + } + + /// + /// Ctor. + /// + /// to aggregate + public FirstProjectionExpression(Expression expression) + : base(expression) + { + } + + /// + /// Returns the function name of the aggregation function. + /// + /// function name + public override string AggregationFunctionName + { + get { return "first"; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/ForClause.cs b/NEsper.Core/NEsper.Core/client/soda/ForClause.cs new file mode 100755 index 000000000..c3822814b --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/ForClause.cs @@ -0,0 +1,54 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// A for-clause is a means to specify listener and observer delivery. + /// + [Serializable] + public class ForClause + { + /// + /// Initializes a new instance of the class. + /// + public ForClause() + { + Items = new List(); + } + + /// Creates an empty group-by clause, to add to via add methods. + /// group-by clause + public static ForClause Create() + { + return new ForClause(); + } + + /// Returns for-clause items. + /// items + public IList Items { get; set; } + + /// Renders the clause in textual representation. + /// to output to + public void ToEPL(TextWriter writer) + { + String delimiter = ""; + foreach (ForClauseItem child in Items) + { + writer.Write(delimiter); + child.ToEPL(writer); + delimiter = " "; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/ForClauseItem.cs b/NEsper.Core/NEsper.Core/client/soda/ForClauseItem.cs new file mode 100755 index 000000000..22f966c27 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/ForClauseItem.cs @@ -0,0 +1,77 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// An item in a for-clause for controlling delivery of result events to listeners and subscribers. + /// + [Serializable] + public class ForClauseItem + { + /// Ctor. Must set a keyword and optionally add expressions. + public ForClauseItem() + { + Expressions = new List(); + } + + /// Ctor. + /// the delivery keyword + public ForClauseItem(ForClauseKeyword keyword) + : this() + { + Keyword = keyword; + } + + /// Returns the for-clause keyword. + /// keyword + public ForClauseKeyword? Keyword { get; set; } + + /// Returns for-clause expressions. + /// expressions + public IList Expressions { get; set; } + + /// Creates a for-clause with no expressions. + /// keyword to use + /// for-clause + public static ForClauseItem Create(ForClauseKeyword keyword) + { + return new ForClauseItem(keyword); + } + + /// Renders the clause in textual representation. + /// to output to + public void ToEPL(TextWriter writer) + { + if (Keyword == null) + { + return; + } + writer.Write("for "); + writer.Write(Keyword.GetValueOrDefault().GetName()); + if (Expressions.Count == 0) + { + return; + } + + writer.Write("("); + String delimiter = ""; + foreach (Expression child in Expressions) + { + writer.Write(delimiter); + child.ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + delimiter = ", "; + } + writer.Write(")"); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/soda/ForClauseKeyword.cs b/NEsper.Core/NEsper.Core/client/soda/ForClauseKeyword.cs new file mode 100755 index 000000000..40f14add5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/ForClauseKeyword.cs @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.client.soda +{ + /// + /// Keywords for use in the for-clause. + /// + public enum ForClauseKeyword + { + /// + /// Grouped delivery - listener receives invocation per group. + /// + GROUPED_DELIVERY, + + /// + /// Discrete delivery - listener receives invocation per event. + /// + DISCRETE_DELIVERY, + } + + public static class ForClauseKeywordExtensions + { + /// + /// Returns for-keyword. + /// + /// The keyword. + /// keyword + public static string GetName(this ForClauseKeyword keyword) { + switch(keyword) { + case ForClauseKeyword.GROUPED_DELIVERY: + return "grouped_delivery"; + case ForClauseKeyword.DISCRETE_DELIVERY: + return "discrete_delivery"; + default: + return null; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/FromClause.cs b/NEsper.Core/NEsper.Core/client/soda/FromClause.cs new file mode 100755 index 000000000..27e5e9a3c --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/FromClause.cs @@ -0,0 +1,182 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; +using com.espertech.esper.type; + +namespace com.espertech.esper.client.soda +{ + /// The from-clause names the streams to select upon. The most common projected stream is a filter-based stream which is created by . Multiple streams can be joined by adding each stream individually. Outer joins are also handled by this class. To create an outer join consisting of 2 streams, add one that defines the outer join relationship between the 2 streams. The outer joins between Count streams, add Count-1 qualifiers. + [Serializable] + public class FromClause + { + /// Returns the list of streams in the from-clause. + /// list of streams + public List Streams { get; set; } + + /// + /// Returns the outer join descriptors, if this is an outer join, or an empty list if none + /// of the streams are outer joined. + /// + /// list of outer join qualifiers + public List OuterJoinQualifiers { get; set; } + + /// Ctor. + public FromClause() + { + Streams = new List(); + OuterJoinQualifiers = new List(); + } + + /// Creates an empty from-clause to which one adds streams via the add methods. + /// empty from clause + public static FromClause Create() + { + return new FromClause(); + } + + /// Creates a from-clause that lists 2 projected streams joined via outer join. + /// first stream in outer join + /// qualifies the outer join + /// second stream in outer join + /// from clause + public static FromClause Create(Stream stream, OuterJoinQualifier outerJoinQualifier, Stream streamSecond) + { + return new FromClause(stream, outerJoinQualifier, streamSecond); + } + + /// Creates a from clause that selects from a single stream. Use to create filter-based streams to add. + /// is one or more streams to add to the from clause. + /// from clause + public static FromClause Create(params Stream[] streams) + { + return new FromClause(streams); + } + + /// Ctor for an outer join between two streams. + /// first stream in outer join + /// type of outer join and fields joined on + /// second stream in outer join + public FromClause(Stream streamOne, OuterJoinQualifier outerJoinQualifier, Stream streamTwo) + : this(streamOne) + { + Add(streamTwo); + OuterJoinQualifiers.Add(outerJoinQualifier); + } + + /// Ctor. + /// is zero or more streams in the from-clause. + public FromClause(params Stream[] streamsList) + { + Streams = new List(); + OuterJoinQualifiers = new List(); + Streams.AddRange(streamsList); + } + + /// Adds a stream. Use to add filter-based streams. + /// to add + /// from clause + public FromClause Add(Stream stream) + { + Streams.Add(stream); + return this; + } + + /// Adds an outer join descriptor that defines how the streams are related via outer joins. For joining Count streams, add Count-1 outer join qualifiers. + /// is the type of outer join and the fields in the outer join + /// from clause + public FromClause Add(OuterJoinQualifier outerJoinQualifier) + { + OuterJoinQualifiers.Add(outerJoinQualifier); + return this; + } + + /// Renders the from-clause in textual representation. + /// to output to + /// for NewLine-whitespace formatting + public void ToEPL(TextWriter writer, EPStatementFormatter formatter) + { + ToEPLOptions(writer, formatter, true); + } + + /// Renders the from-clause in textual representation. + /// to output to + /// flag whether to add the "from" literal + /// for NewLine-whitespace formatting + public void ToEPLOptions(TextWriter writer, EPStatementFormatter formatter, bool includeFrom) + { + String delimiter = ""; + if (includeFrom) + { + formatter.BeginFrom(writer); + writer.Write("from"); + } + + if ((OuterJoinQualifiers == null) || (OuterJoinQualifiers.Count == 0)) + { + bool first = true; + foreach (Stream stream in Streams) + { + writer.Write(delimiter); + formatter.BeginFromStream(writer, first); + first = false; + stream.ToEPL(writer, formatter); + delimiter = ","; + } + } + else + { + if (OuterJoinQualifiers.Count != (Streams.Count - 1)) + { + throw new ArgumentException("Number of outer join qualifiers must be one less then the number of streams."); + } + bool first = true; + for (int i = 0; i < Streams.Count; i++) + { + Stream stream = Streams[i]; + formatter.BeginFromStream(writer, first); + first = false; + stream.ToEPL(writer, formatter); + + if (i > 0) + { + OuterJoinQualifier qualCond = OuterJoinQualifiers[i - 1]; + if (qualCond.Left != null) + { + writer.Write(" on "); + qualCond.Left.ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + writer.Write(" = "); + qualCond.Right.ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + + if (qualCond.AdditionalProperties.Count > 0) + { + foreach (PropertyValueExpressionPair pair in qualCond.AdditionalProperties) + { + writer.Write(" and "); + pair.Left.ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + writer.Write(" = "); + pair.Right.ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + } + } + } + } + + if (i < Streams.Count - 1) + { + OuterJoinQualifier qualType = OuterJoinQualifiers[i]; + writer.Write(" "); + writer.Write(qualType.JoinType.GetText()); + writer.Write(" outer join"); + } + } + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/GroupByClause.cs b/NEsper.Core/NEsper.Core/client/soda/GroupByClause.cs new file mode 100755 index 000000000..6c62c6ee7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/GroupByClause.cs @@ -0,0 +1,114 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.client.soda +{ + /// + /// The group-by clause consists of a list of expressions that provide the grouped-by values. + /// + [Serializable] + public class GroupByClause + { + private readonly IList _groupByExpressions; + + /// Creates an empty group-by clause, to add to via add methods. + /// group-by clause + public static GroupByClause Create() + { + return new GroupByClause(); + } + + /// Creates a group-by clause from property names. + /// a list of one or more property names + /// group-by clause consisting of the properties + public static GroupByClause Create(params String[] properties) + { + return new GroupByClause(properties); + } + + /// Creates a group-by clause from expressions. + /// a list of one or more expressions + /// group-by clause consisting of the expressions + public static GroupByClause Create(params Expression[] expressions) + { + return new GroupByClause(expressions); + } + + /// + /// Initializes a new instance of the class. + /// + /// The group by expressions. + public GroupByClause(IList groupByExpressions) + { + _groupByExpressions = groupByExpressions; + } + + /// + /// Ctor - for use to create an expression tree, without child expression. + /// + /// Use add methods to add child expressions to acts upon. + /// + public GroupByClause() + { + _groupByExpressions = new List(); + } + + /// Ctor. + /// is a list of property names + public GroupByClause(params String[] properties) + : this() + { + foreach (string property in properties) + { + _groupByExpressions.Add(new GroupByClauseExpressionSingle(Expressions.Property(property))); + } + } + + /// Ctor. + /// list of expressions + public GroupByClause(params Expression[] expressions) + : this() + { + foreach (Expression expression in expressions) + { + _groupByExpressions.Add(new GroupByClauseExpressionSingle(expression)); + } + } + + /// Gets or sets the expressions providing the grouped-by values. + /// expressions + public IList GroupByExpressions + { + get { return _groupByExpressions; } + set + { + _groupByExpressions.Clear(); + _groupByExpressions.AddAll(value); + } + } + + /// Renders the clause in textual representation. + /// to output to + public void ToEPL(TextWriter writer) + { + String delimiter = ""; + foreach (GroupByClauseExpression child in _groupByExpressions) + { + writer.Write(delimiter); + child.ToEPL(writer); + delimiter = ", "; + } + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/client/soda/GroupByClauseExpression.cs b/NEsper.Core/NEsper.Core/client/soda/GroupByClauseExpression.cs new file mode 100755 index 000000000..dc180cf15 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/GroupByClauseExpression.cs @@ -0,0 +1,24 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Base interface for group-by clause expressions, covers all possible combinations + /// of expressions, parenthesis-expression-combinations, rollup, cube and grouping sets + /// and their parameters. + /// + public interface GroupByClauseExpression + { + /// RenderAny group by expression + /// to render to + void ToEPL(TextWriter writer); + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/GroupByClauseExpressionCombination.cs b/NEsper.Core/NEsper.Core/client/soda/GroupByClauseExpressionCombination.cs new file mode 100755 index 000000000..c44a8b875 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/GroupByClauseExpressionCombination.cs @@ -0,0 +1,63 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// A combination of expressions is for example "(a, b)", wherein the list of expressions + /// provided together logically make up a grouping level. + /// + [Serializable] + public class GroupByClauseExpressionCombination : GroupByClauseExpression + { + private IList _expressions; + + /// + /// Ctor. + /// + /// combination of expressions + public GroupByClauseExpressionCombination(IList expressions) + { + _expressions = expressions; + } + + /// + /// Ctor. + /// + public GroupByClauseExpressionCombination() + { + } + + /// + /// Returns the combined expressions. + /// + /// expressions + public IList Expressions + { + get { return _expressions; } + set { this._expressions = value; } + } + + public void ToEPL(TextWriter writer) + { + writer.Write("("); + String delimiter = ""; + foreach (Expression e in _expressions) + { + writer.Write(delimiter); + e.ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + delimiter = ", "; + } + writer.Write(")"); + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/GroupByClauseExpressionGroupingSet.cs b/NEsper.Core/NEsper.Core/client/soda/GroupByClauseExpressionGroupingSet.cs new file mode 100755 index 000000000..bbb684cac --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/GroupByClauseExpressionGroupingSet.cs @@ -0,0 +1,56 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Represents the "grouping sets" keywords. + /// + [Serializable] + public class GroupByClauseExpressionGroupingSet : GroupByClauseExpression + { + private IList _expressions; + + /// Ctor. + /// group-by expressions withing grouping set + public GroupByClauseExpressionGroupingSet(IList expressions) + { + _expressions = expressions; + } + + /// Ctor. + public GroupByClauseExpressionGroupingSet() + { + } + + /// Returns list of expressions in grouping set. + /// group-by expressions + public IList Expressions + { + get { return _expressions; } + set { _expressions = value; } + } + + public void ToEPL(TextWriter writer) + { + writer.Write("grouping sets("); + String delimiter = ""; + foreach (GroupByClauseExpression child in _expressions) + { + writer.Write(delimiter); + child.ToEPL(writer); + delimiter = ", "; + } + writer.Write(")"); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/soda/GroupByClauseExpressionRollupOrCube.cs b/NEsper.Core/NEsper.Core/client/soda/GroupByClauseExpressionRollupOrCube.cs new file mode 100755 index 000000000..3df40fa85 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/GroupByClauseExpressionRollupOrCube.cs @@ -0,0 +1,70 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Represents a rollup or cube in a group-by clause. + /// + [Serializable] + public class GroupByClauseExpressionRollupOrCube : GroupByClauseExpression + { + private bool _cube; + private IList _expressions; + + /// Ctor. + /// true for cube, false for rollup + /// group-by expressions as part of rollup or cube + public GroupByClauseExpressionRollupOrCube(bool cube, IList expressions) + { + _cube = cube; + _expressions = expressions; + } + + /// Ctor. + public GroupByClauseExpressionRollupOrCube() { + } + + /// Returns the rollup or cube group-by expressions. + /// expressions + public IList Expressions + { + get { return _expressions; } + set { _expressions = value; } + } + + /// Returns true for cube, false for rollup. + /// cube + public bool IsCube + { + get { return _cube; } + set { _cube = value; } + } + + public void ToEPL(TextWriter writer) { + if (_cube) { + writer.Write("cube("); + } + else { + writer.Write("rollup("); + } + String delimiter = ""; + foreach (GroupByClauseExpression child in _expressions) + { + writer.Write(delimiter); + child.ToEPL(writer); + delimiter = ", "; + } + writer.Write(")"); + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/GroupByClauseExpressionSingle.cs b/NEsper.Core/NEsper.Core/client/soda/GroupByClauseExpressionSingle.cs new file mode 100755 index 000000000..98a600c89 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/GroupByClauseExpressionSingle.cs @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Represents a single expression (non-combined, rollup/cube or grouping set) as part + /// of a group-by expression. + /// + [Serializable] + public class GroupByClauseExpressionSingle : GroupByClauseExpression + { + private Expression _expression; + + /// Ctor. + /// the expression + public GroupByClauseExpressionSingle(Expression expression) + { + _expression = expression; + } + + /// Ctor. + public GroupByClauseExpressionSingle() + { + } + + /// Returns the expression. + /// expressions + public Expression Expression + { + get { return _expression; } + set { _expression = value; } + } + + public void ToEPL(TextWriter writer) + { + _expression.ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/soda/GroupingExpression.cs b/NEsper.Core/NEsper.Core/client/soda/GroupingExpression.cs new file mode 100755 index 000000000..fe5dfa764 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/GroupingExpression.cs @@ -0,0 +1,39 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Grouping-function for use with rollup, cube or grouping sets. + /// + [Serializable] + public class GroupingExpression : ExpressionBase + { + /// + /// Ctor - for use to create an expression tree, without child expression. + /// + /// Use add methods to add child expressions to acts upon. + /// + public GroupingExpression() + { + } + + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.UNARY; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + ToPrecedenceFreeEPL("grouping", this.Children, writer); + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/GroupingIdExpression.cs b/NEsper.Core/NEsper.Core/client/soda/GroupingIdExpression.cs new file mode 100755 index 000000000..9f1a46329 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/GroupingIdExpression.cs @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Grouping_id-function for use with rollup, cube or grouping sets. + /// + [Serializable] + public class GroupingIdExpression : ExpressionBase + { + /// Ctor - for use to create an expression tree, without child expression. + /// + /// Use add methods to add child expressions to acts upon. + /// + public GroupingIdExpression() + { + } + + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.UNARY; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + ToPrecedenceFreeEPL("grouping_id", this.Children, writer); + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/IStreamBuiltinExpression.cs b/NEsper.Core/NEsper.Core/client/soda/IStreamBuiltinExpression.cs new file mode 100755 index 000000000..23c7743a6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/IStreamBuiltinExpression.cs @@ -0,0 +1,35 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Returns true for insert stream and false for remove stream, same as the "istream()" builtin function. + /// + [Serializable] + public class IStreamBuiltinExpression : ExpressionBase + { + /// Ctor. + public IStreamBuiltinExpression() + { + } + + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.UNARY; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + writer.Write("istream()"); + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/InExpression.cs b/NEsper.Core/NEsper.Core/client/soda/InExpression.cs new file mode 100755 index 000000000..c87f1432e --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/InExpression.cs @@ -0,0 +1,142 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// In-expresson checks that a value is in (or not in) a set of values, equivalent to the syntax "color in ('red', 'blue')". + /// + [Serializable] + public class InExpression : ExpressionBase + { + /// + /// Ctor. + /// + public InExpression() + { + } + + /// + /// Ctor - for use to create an expression tree, without child expression. + /// Use add methods to add child expressions to acts upon. + /// + /// true for the not-in expression, false for the in-expression + public InExpression(bool isNotIn) + { + IsNotIn = isNotIn; + } + + /// + /// Ctor - for use to create an expression tree, without child expression. + /// Use add methods to add child expressions to acts upon. + /// + /// an expression that provides the value to search for in the set + /// true for the not-in expression, false for the in-expression + /// is a set of constants to match against + public InExpression(Expression value, bool isNotIn, params object[] parameters) + { + IsNotIn = isNotIn; + Children.Add(value); + for (int i = 0; i < parameters.Length; i++) + { + if (parameters[i] is Expression) + { + Children.Add((Expression)parameters[i]); + } + else + { + Children.Add(new ConstantExpression(parameters[i])); + } + } + } + + /// + /// Ctor. + /// + /// expression to check + /// indicator whether not-in (true) or in (false) + /// expression list + public InExpression(Expression value, bool isNotIn, Expression[] parameters) + { + IsNotIn = isNotIn; + Children.Add(value); + foreach (Expression parameter in parameters) { + Children.Add(parameter); + } + } + + /// + /// Returns true for the not-in expression, or false for an in-expression. + /// + /// true for not-in + public bool IsNotIn { get; set; } + + /// + /// Add a constant to include in the computation. + /// + /// constant to add + /// expression + public InExpression Add(object @object) + { + Children.Add(new ConstantExpression(@object)); + return this; + } + + /// + /// Add an expression to include in the computation. + /// + /// to add + /// expression + public InExpression Add(Expression expression) + { + Children.Add(expression); + return this; + } + + /// + /// Add a property to include in the computation. + /// + /// is the name of the property + /// expression + public InExpression Add(string propertyName) + { + Children.Add(new PropertyValueExpression(propertyName)); + return this; + } + + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.RELATIONAL_BETWEEN_IN; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + Children[0].ToEPL(writer, Precedence); + if (IsNotIn) + { + writer.Write(" not in ("); + } + else + { + writer.Write(" in ("); + } + + string delimiter = ""; + for (int i = 1; i < Children.Count; i++) + { + writer.Write(delimiter); + Children[i].ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + delimiter = ","; + } + writer.Write(')'); + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/InsertIntoClause.cs b/NEsper.Core/NEsper.Core/client/soda/InsertIntoClause.cs new file mode 100755 index 000000000..d60e7eae3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/InsertIntoClause.cs @@ -0,0 +1,136 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// An insert-into clause consists of a stream name and column names and an optional stream selector. + /// + [Serializable] + public class InsertIntoClause + { + /// Ctor. + public InsertIntoClause() { + } + + /// Creates the insert-into clause. + /// the name of the stream to insert into + /// clause + public static InsertIntoClause Create(String streamName) + { + return new InsertIntoClause(streamName); + } + + /// Creates the insert-into clause. + /// the name of the stream to insert into + /// is a list of column names + /// clause + public static InsertIntoClause Create(string streamName, params string[] columns) + { + return new InsertIntoClause(streamName, columns); + } + + /// Creates the insert-into clause. + /// the name of the stream to insert into + /// is a list of column names + /// selects the stream + /// clause + public static InsertIntoClause Create(string streamName, string[] columns, StreamSelector streamSelector) + { + if (streamSelector == StreamSelector.RSTREAM_ISTREAM_BOTH) + { + throw new ArgumentException("Insert into only allows istream or rstream selection, not both"); + } + return new InsertIntoClause(streamName, columns, streamSelector); + } + + /// Ctor. + /// is the stream name to insert into + public InsertIntoClause(String streamName) + { + StreamSelector = StreamSelector.ISTREAM_ONLY; + StreamName = streamName; + ColumnNames = new List(); + } + + /// Ctor. + /// is the stream name to insert into + /// column names + public InsertIntoClause(String streamName, String[] columnNames) + { + StreamSelector = StreamSelector.ISTREAM_ONLY; + StreamName = streamName; + ColumnNames = columnNames; + } + + /// Ctor. + /// is the stream name to insert into + /// column names + /// selector for either insert stream (the default) or remove stream or both + public InsertIntoClause(String streamName, IList columnNames, StreamSelector streamSelector) + { + StreamSelector = streamSelector; + StreamName = streamName; + ColumnNames = columnNames; + } + + /// Returns the stream selector for the insert into. + /// stream selector + public StreamSelector StreamSelector { get; set; } + + /// Returns name of stream name to use for insert-into stream. + /// stream name + public string StreamName { get; set; } + + /// Returns a list of column names specified optionally in the insert-into clause, or empty if none specified. + /// column names or empty list if none supplied + public IList ColumnNames { get; set; } + + /// Add a column name to the insert-into clause. + /// to add + public void Add(String columnName) + { + ColumnNames.Add(columnName); + } + + /// Renders the clause in textual representation. + /// to output to + /// for NewLine-whitespace formatting + /// to indicate if this insert-into-clause is inside other clauses. + public void ToEPL(TextWriter writer, EPStatementFormatter formatter, bool isTopLevel) + { + formatter.BeginInsertInto(writer, isTopLevel); + writer.Write("insert "); + if (StreamSelector != StreamSelector.ISTREAM_ONLY) + { + writer.Write(StreamSelector.GetEPL()); + writer.Write(" "); + } + + writer.Write("into "); + writer.Write(StreamName); + + if (ColumnNames.Count > 0) + { + writer.Write("("); + String delimiter = ""; + foreach (var name in ColumnNames) + { + writer.Write(delimiter); + writer.Write(name); + delimiter = ", "; + } + writer.Write(")"); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/InstanceOfExpression.cs b/NEsper.Core/NEsper.Core/client/soda/InstanceOfExpression.cs new file mode 100755 index 000000000..72fdafa53 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/InstanceOfExpression.cs @@ -0,0 +1,106 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace com.espertech.esper.client.soda +{ + /// + /// Instance-of expression checks if an expression returns a certain type. + /// + [Serializable] + public class InstanceOfExpression : ExpressionBase + { + private String[] _typeNames; + + /// + /// Initializes a new instance of the class. + /// + public InstanceOfExpression() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The type names. + public InstanceOfExpression(string[] typeNames) + { + _typeNames = typeNames; + } + + /// + /// Ctor - for use to create an expression tree, without child expression. + /// + /// + /// is the fully-qualified class names or primitive type names or "string" + /// + public InstanceOfExpression(IList typeNames) + { + _typeNames = typeNames.ToArray(); + } + + /// Ctor. + /// provides values to check the type of + /// + /// is one fully-qualified class names or primitive type names or "string" + /// + /// + /// is additional optional fully-qualified class names or primitive type names or "string" + /// + public InstanceOfExpression(Expression expressionToCheck, String typeName, params String[] moreTypes) + { + Children.Add(expressionToCheck); + if (moreTypes == null) + { + _typeNames = new String[] {typeName}; + } + else + { + String[] tempList = new String[moreTypes.Length + 1]; + tempList[0] = typeName; + Array.Copy(moreTypes, 0, tempList, 1, moreTypes.Length); + _typeNames = tempList; + } + } + + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.UNARY; } + } + + /// Renders the clause in textual representation. + /// to output to + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + writer.Write("instanceof("); + Children[0].ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + writer.Write(","); + + String delimiter = ""; + foreach (String typeName in _typeNames) + { + writer.Write(delimiter); + writer.Write(typeName); + delimiter = ","; + } + writer.Write(")"); + } + + /// Gets or sets the types to compare to. + /// list of types to compare to + public string[] TypeNames + { + get { return _typeNames; } + set { _typeNames = value ; } + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/client/soda/IntoTableClause.cs b/NEsper.Core/NEsper.Core/client/soda/IntoTableClause.cs new file mode 100755 index 000000000..b51f4740a --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/IntoTableClause.cs @@ -0,0 +1,54 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.client.soda +{ + /// + /// Into-table clause. + /// + [Serializable] + public class IntoTableClause + { + /// + /// Ctor. + /// + /// table name + public IntoTableClause(string tableName) { + this.TableName = tableName; + } + + /// + /// Ctor. + /// + public IntoTableClause() { + } + + /// + /// Returns the table name. + /// + /// table name + public string TableName { get; set; } + + /// + /// Renders the clause. + /// + /// to write to + public void ToEPL(TextWriter writer) + { + writer.Write("into table "); + writer.Write(TableName); + writer.Write(" "); + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/Junction.cs b/NEsper.Core/NEsper.Core/client/soda/Junction.cs new file mode 100755 index 000000000..30ef16750 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/Junction.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.soda +{ + /// + /// Base junction for conjunction (and) and disjunction (or). + /// + [Serializable] + public abstract class Junction : ExpressionBase + { + /// Expression to add to the conjunction (AND) or disjunction (OR). + /// to add + /// expression + public Junction Add(Expression expression) + { + Children.Add(expression); + return this; + } + + /// Property to add to the conjunction (AND) or disjunction (OR). + /// is the name of the property + /// expression + public Junction Add(String propertyName) + { + Children.Add(new PropertyValueExpression(propertyName)); + return this; + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/LambdaExpression.cs b/NEsper.Core/NEsper.Core/client/soda/LambdaExpression.cs new file mode 100755 index 000000000..21aa6e5e6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/LambdaExpression.cs @@ -0,0 +1,65 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Lambda-expression is an expression of the form "parameter => body" where-in the "=>" reads as goes-to. + /// + /// The form "x => x * x" reads as "x goes to x times x", for an example expression that yields x multiplied by x. + /// + /// Used with expression declaration and with enumeration methods, for example, to parameterize by an expression. + /// + [Serializable] + public class LambdaExpression : ExpressionBase + { + /// Ctor. + public LambdaExpression() { + } + + /// Ctor. + /// the lambda expression parameters + public LambdaExpression(IList parameters) + { + Parameters = parameters; + } + + /// Returns the lambda expression parameters. + /// lambda expression parameters + public IList Parameters { get; set; } + + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.MINIMUM; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + if (Parameters.Count > 1) { + writer.Write("("); + + var delimiter = ""; + foreach (var parameter in Parameters) { + writer.Write(delimiter); + writer.Write(parameter); + delimiter = ","; + } + writer.Write(")"); + } + else { + writer.Write(Parameters[0]); + } + writer.Write(" => "); + Children[0].ToEPL(writer, Precedence); + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/LastEverProjectionExpression.cs b/NEsper.Core/NEsper.Core/client/soda/LastEverProjectionExpression.cs new file mode 100755 index 000000000..5a0a094c8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/LastEverProjectionExpression.cs @@ -0,0 +1,63 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Represents the "lastever" aggregation function. + /// + [Serializable] + public class LastEverProjectionExpression : ExpressionBase + { + /// + /// Ctor. + /// + public LastEverProjectionExpression() + { + } + + /// + /// Ctor. + /// + /// true for distinct + public LastEverProjectionExpression(bool isDistinct) + { + IsDistinct = isDistinct; + } + + /// + /// Ctor. + /// + /// to aggregate + /// true for distinct + public LastEverProjectionExpression(Expression expression, bool isDistinct) + { + IsDistinct = isDistinct; + Children.Add(expression); + } + + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.UNARY; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + RenderAggregation(writer, "lastever", IsDistinct, Children); + } + + /// + /// Returns true for distinct. + /// + /// boolean indicating distinct or not + public bool IsDistinct { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/LastProjectionExpression.cs b/NEsper.Core/NEsper.Core/client/soda/LastProjectionExpression.cs new file mode 100755 index 000000000..69abbfcc0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/LastProjectionExpression.cs @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.soda +{ + /// + /// Represents the "last" aggregation function. + /// + [Serializable] + public class LastProjectionExpression + : AccessProjectionExpressionBase + { + /// + /// Initializes a new instance of the class. + /// + public LastProjectionExpression() + { + } + + /// + /// Ctor. + /// + /// to aggregate + public LastProjectionExpression(Expression expression) + { + Children.Add(expression); + } + + /// + /// Returns the function name of the aggregation function. + /// + /// function name + public override string AggregationFunctionName + { + get { return "last"; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/LikeExpression.cs b/NEsper.Core/NEsper.Core/client/soda/LikeExpression.cs new file mode 100755 index 000000000..d8d119102 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/LikeExpression.cs @@ -0,0 +1,130 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// SQL-Like expression for matching '%' and '_' wildcard strings following SQL + /// standards. + /// + [Serializable] + public class LikeExpression : ExpressionBase + { + /// + /// Ctor - for use to create an expression tree, without child expression. + /// + /// Use add methods to add child expressions to acts upon. + /// + public LikeExpression() + { + IsNot = false; + } + + /// + /// Ctor - for use to create an expression tree, without child expression. + /// + /// Use add methods to add child expressions to acts upon. + /// + /// if the like-expression is negated + public LikeExpression(bool isNot) + { + IsNot = isNot; + } + + /// + /// Ctor. + /// + /// provides the value to match + /// provides the like-expression to match against + public LikeExpression(Expression left, Expression right) + : this(left, right, null) + { + } + + /// + /// Ctor. + /// + /// provides the value to match + /// provides the like-expression to match against + /// is the expression providing the string escape character + public LikeExpression(Expression left, Expression right, Expression escape) + { + IList children = Children; + children.Add(left); + children.Add(right); + if (escape != null) { + children.Add(escape); + } + IsNot = false; + } + + /// + /// Ctor. + /// + /// provides the value to match + /// provides the like-expression to match against + /// if the like-expression is negated + public LikeExpression(Expression left, Expression right, bool isNot) + : this(left, right, null, isNot) + { + } + + /// + /// Ctor. + /// + /// provides the value to match + /// provides the like-expression to match against + /// is the expression providing the string escape character + /// if the like-expression is negated + public LikeExpression(Expression left, Expression right, Expression escape, bool isNot) + { + Children.Add(left); + Children.Add(right); + if (escape != null) { + Children.Add(escape); + } + this.IsNot = isNot; + } + + /// + /// Returns true if this is a "not like", or false if just a like + /// + /// + /// indicator whether negated or not + /// + public bool IsNot { get; set; } + + /// + /// Gets the Precedence. + /// + /// The Precedence. + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.RELATIONAL_BETWEEN_IN; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + Children[0].ToEPL(writer, Precedence); + if (IsNot) { + writer.Write(" not"); + } + writer.Write(" like "); + Children[1].ToEPL(writer, Precedence); + + if (Children.Count > 2) { + writer.Write(" escape "); + Children[2].ToEPL(writer, Precedence); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/MatchRecognizeClause.cs b/NEsper.Core/NEsper.Core/client/soda/MatchRecognizeClause.cs new file mode 100755 index 000000000..79c6f25ee --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/MatchRecognizeClause.cs @@ -0,0 +1,140 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.client.soda +{ + /// + /// Match-recognize clause. + /// + [Serializable] + public class MatchRecognizeClause + { + /// + /// Ctor. + /// + public MatchRecognizeClause() + { + Defines = new List(); + SkipClause = MatchRecognizeSkipClause.PAST_LAST_ROW; + Measures = new List(); + PartitionExpressions = new List(); + } + + /// + /// Renders the clause in textual representation. + /// + /// to output to + public void ToEPL(TextWriter writer) + { + string delimiter; + + writer.Write(" match_recognize ("); + + if (PartitionExpressions.Count > 0) { + delimiter = ""; + writer.Write(" partition by "); + foreach (Expression part in PartitionExpressions) { + writer.Write(delimiter); + part.ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + delimiter = ", "; + } + } + + delimiter = ""; + writer.Write(" measures "); + foreach (SelectClauseExpression part in Measures) { + writer.Write(delimiter); + part.ToEPLElement(writer); + delimiter = ", "; + } + + if (IsAll) { + writer.Write(" all matches"); + } + + if (SkipClause != MatchRecognizeSkipClause.PAST_LAST_ROW) { + writer.Write(" after match skip " + SkipClause.GetText()); + } + + writer.Write(" pattern ("); + Pattern.WriteEPL(writer); + writer.Write(")"); + + if ((IntervalClause != null) && (IntervalClause.Expression != null)){ + writer.Write(" interval "); + IntervalClause.Expression.ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + if (IntervalClause.IsOrTerminated) { + writer.Write(" or terminated"); + } + } + + delimiter = ""; + if (!Defines.IsEmpty()) { + writer.Write(" define "); + foreach (MatchRecognizeDefine def in Defines) { + writer.Write(delimiter); + writer.Write(def.Name); + writer.Write(" as "); + def.Expression.ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + delimiter = ", "; + } + } + + writer.Write(")"); + } + + /// + /// Get partition expressions. + /// + /// partition expressions + public IList PartitionExpressions { get; set; } + + /// + /// Returns measures. + /// + /// measures + public IList Measures { get; set; } + + /// + /// Indicator whether all matches. + /// + /// all matches + public bool IsAll { get; set; } + + /// + /// Returns skip-clause. + /// + /// skip-clause + public MatchRecognizeSkipClause SkipClause { get; set; } + + /// + /// Returns the defines-clause + /// + /// defines-clause + public IList Defines { get; set; } + + /// + /// Returns the interval clause. + /// + /// interval clause + public MatchRecognizeIntervalClause IntervalClause { get; set; } + + /// + /// Returns regex-pattern. + /// + /// pattern + public MatchRecognizeRegEx Pattern { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/MatchRecognizeDefine.cs b/NEsper.Core/NEsper.Core/client/soda/MatchRecognizeDefine.cs new file mode 100755 index 000000000..cc0e3814f --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/MatchRecognizeDefine.cs @@ -0,0 +1,47 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.soda +{ + /// + /// Define-clause in match-recognize expression. + /// + [Serializable] + public class MatchRecognizeDefine + { + /// + /// Ctor. + /// + public MatchRecognizeDefine() { + } + + /// + /// Ctor. + /// + /// variable name + /// expression + public MatchRecognizeDefine(String name, Expression expression) { + Name = name; + Expression = expression; + } + + /// + /// Returns the variable name. + /// + /// variable name + public string Name { get; set; } + + /// + /// Returns the expression. + /// + /// expression + public Expression Expression { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/MatchRecognizeIntervalClause.cs b/NEsper.Core/NEsper.Core/client/soda/MatchRecognizeIntervalClause.cs new file mode 100755 index 000000000..0f40b39f7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/MatchRecognizeIntervalClause.cs @@ -0,0 +1,39 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.soda +{ + /// Interval used within match recognize. + [Serializable] + public class MatchRecognizeIntervalClause + { + /// Ctor. + public MatchRecognizeIntervalClause() + { + } + + /// Ctor. + /// interval expression + /// indicator whether or-terminated + public MatchRecognizeIntervalClause(TimePeriodExpression expression, bool orTerminated) + { + Expression = expression; + IsOrTerminated = orTerminated; + } + + /// Returns the interval expression. + /// expression + public Expression Expression { get; set; } + + /// Returns indicator whether or-terminated is set + /// indicator + public bool IsOrTerminated { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/MatchRecognizePatternElementType.cs b/NEsper.Core/NEsper.Core/client/soda/MatchRecognizePatternElementType.cs new file mode 100755 index 000000000..09ade34d5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/MatchRecognizePatternElementType.cs @@ -0,0 +1,65 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.soda +{ + /// Enum for match recognize pattern atom types. + public enum MatchRecognizePatternElementType + { + /// For single multiplicity. + SINGLE, + + /// For greedy '*' multiplicity. + ZERO_TO_MANY, + + /// For greedy '+' multiplicity. + ONE_TO_MANY, + + /// For greedy '?' multiplicity. + ONE_OPTIONAL, + + /// For reluctant '*' multiplicity. + ZERO_TO_MANY_RELUCTANT, + + /// For reluctant '+' multiplicity. + ONE_TO_MANY_RELUCTANT, + + /// For reluctant '?' multiplicity. + ONE_OPTIONAL_RELUCTANT + } + + public static class MatchRecognizePatternElementTypeExtensions + { + /// Returns the multiplicity text. + /// text + public static string GetText(this MatchRecognizePatternElementType value) + { + switch(value) + { + case MatchRecognizePatternElementType.SINGLE: + return (""); + case MatchRecognizePatternElementType.ZERO_TO_MANY: + return ("*"); + case MatchRecognizePatternElementType.ONE_TO_MANY: + return ("+"); + case MatchRecognizePatternElementType.ONE_OPTIONAL: + return ("?"); + case MatchRecognizePatternElementType.ZERO_TO_MANY_RELUCTANT: + return ("*?"); + case MatchRecognizePatternElementType.ONE_TO_MANY_RELUCTANT: + return ("+?"); + case MatchRecognizePatternElementType.ONE_OPTIONAL_RELUCTANT: + return ("??"); + } + + throw new ArgumentException("invalid value", "value"); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/soda/MatchRecognizeRegEx.cs b/NEsper.Core/NEsper.Core/client/soda/MatchRecognizeRegEx.cs new file mode 100755 index 000000000..512714d26 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/MatchRecognizeRegEx.cs @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Interface representing an expression for use in match-recognize. + /// + /// Event row regular expressions are organized into a tree-like structure with nodes representing sub-expressions. + /// + [Serializable] + public abstract class MatchRecognizeRegEx + { + /// Returns id of expression assigned by tools. + /// id + public string TreeObjectName { get; set; } + + /// Ctor. + protected MatchRecognizeRegEx() + { + Children = new List(); + } + + /// Returns child nodes. + /// child nodes + public List Children { get; set; } + + /// Write EPL. + /// to use + public abstract void WriteEPL(TextWriter writer); + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/soda/MatchRecognizeRegExAlteration.cs b/NEsper.Core/NEsper.Core/client/soda/MatchRecognizeRegExAlteration.cs new file mode 100755 index 000000000..c450f7df7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/MatchRecognizeRegExAlteration.cs @@ -0,0 +1,36 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Interface representing an expression for use in match-recognize. + /// + /// Event row regular expressions are organized into a tree-like structure + /// with nodes representing sub-expressions. + /// + + [Serializable] + public class MatchRecognizeRegExAlteration + : MatchRecognizeRegEx + { + public override void WriteEPL(TextWriter writer) + { + String delimiter = ""; + foreach (MatchRecognizeRegEx node in this.Children) + { + writer.Write(delimiter); + node.WriteEPL(writer); + delimiter = "|"; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/MatchRecognizeRegExAtom.cs b/NEsper.Core/NEsper.Core/client/soda/MatchRecognizeRegExAtom.cs new file mode 100755 index 000000000..c4cd6a6a9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/MatchRecognizeRegExAtom.cs @@ -0,0 +1,70 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Atom representing an expression for use in match-recognize. + /// + /// Event row regular expressions are organized into a tree-like structure with nodes + /// representing sub-expressions. + /// + + [Serializable] + public class MatchRecognizeRegExAtom : MatchRecognizeRegEx + { + /// Ctor. + public MatchRecognizeRegExAtom() { + } + + /// Ctor. + /// of variable + /// multiplicity + public MatchRecognizeRegExAtom(String name, MatchRecognizePatternElementType type) { + Name = name; + ElementType = type; + } + + /// + /// Initializes a new instance of the class. + /// + /// The name. + /// The type. + /// The optional repeat. + public MatchRecognizeRegExAtom(String name, MatchRecognizePatternElementType type, MatchRecognizeRegExRepeat optionalRepeat) + { + Name = name; + ElementType = type; + OptionalRepeat = optionalRepeat; + } + + /// Returns variable name. + /// name + public string Name { get; set; } + + /// Returns multiplicity. + /// multiplicity + public MatchRecognizePatternElementType ElementType { get; set; } + + /// Returns the optional repeat. + /// The optional repeat. + public MatchRecognizeRegExRepeat OptionalRepeat { get; set; } + + public override void WriteEPL(TextWriter writer) { + writer.Write(Name); + writer.Write(ElementType.GetText()); + if (OptionalRepeat != null) + { + OptionalRepeat.WriteEPL(writer); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/MatchRecognizeRegExConcatenation.cs b/NEsper.Core/NEsper.Core/client/soda/MatchRecognizeRegExConcatenation.cs new file mode 100755 index 000000000..b3bf5570f --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/MatchRecognizeRegExConcatenation.cs @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Interface representing an expression for use in match-recognize. + /// + /// Event row regular expressions are organized into a tree-like structure with + /// nodes representing sub-expressions. + /// + + [Serializable] + public class MatchRecognizeRegExConcatenation : MatchRecognizeRegEx + { + public override void WriteEPL(TextWriter writer) { + String delimiter = ""; + foreach (MatchRecognizeRegEx node in Children) + { + writer.Write(delimiter); + node.WriteEPL(writer); + delimiter = " "; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/MatchRecognizeRegExNested.cs b/NEsper.Core/NEsper.Core/client/soda/MatchRecognizeRegExNested.cs new file mode 100755 index 000000000..7403624e4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/MatchRecognizeRegExNested.cs @@ -0,0 +1,74 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Atom representing an expression for use in match-recognize. + /// + /// Event row regular expressions are organized into a tree-like structure + /// with nodes representing sub-expressions. + /// + [Serializable] + public class MatchRecognizeRegExNested + : MatchRecognizeRegEx + { + /// Ctor. + public MatchRecognizeRegExNested() + { + } + + /// Ctor. + /// multiplicity + public MatchRecognizeRegExNested(MatchRecognizePatternElementType type) + { + ElementType = type; + } + + /// + /// Initializes a new instance of the class. + /// + /// Type of the element. + /// The optional repeat. + public MatchRecognizeRegExNested(MatchRecognizePatternElementType elementType, MatchRecognizeRegExRepeat optionalRepeat) + { + ElementType = elementType; + OptionalRepeat = optionalRepeat; + } + + /// Returns multiplicity. + /// multiplicity + public MatchRecognizePatternElementType ElementType { get; set; } + + /// Returns the optional repeat. + /// The optional repeat. + public MatchRecognizeRegExRepeat OptionalRepeat { get; set; } + + public override void WriteEPL(TextWriter writer) + { + writer.Write("("); + String delimiter = ""; + foreach (MatchRecognizeRegEx node in Children) + { + writer.Write(delimiter); + node.WriteEPL(writer); + delimiter = " "; + } + writer.Write(")"); + writer.Write(ElementType.GetText()); + + if (OptionalRepeat != null) + { + OptionalRepeat.WriteEPL(writer); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/MatchRecognizeRegExPermutation.cs b/NEsper.Core/NEsper.Core/client/soda/MatchRecognizeRegExPermutation.cs new file mode 100755 index 000000000..4d2c04bc5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/MatchRecognizeRegExPermutation.cs @@ -0,0 +1,33 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Interface representing a permutation expression for use in match-recognize. + /// + [Serializable] + public class MatchRecognizeRegExPermutation : MatchRecognizeRegEx + { + public override void WriteEPL(TextWriter writer) + { + string delimiter = ""; + writer.Write("match_recognize_permute("); + foreach (MatchRecognizeRegEx node in Children) + { + writer.Write(delimiter); + node.WriteEPL(writer); + delimiter = ","; + } + writer.Write(")"); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/client/soda/MatchRecognizeRegExPlaceholder.cs b/NEsper.Core/NEsper.Core/client/soda/MatchRecognizeRegExPlaceholder.cs new file mode 100755 index 000000000..efa60593f --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/MatchRecognizeRegExPlaceholder.cs @@ -0,0 +1,31 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// For use in match recognize pattern expression as a placeholder to represent its child nodes. + /// + + [Serializable] + public class MatchRecognizeRegExPlaceholder + : MatchRecognizeRegEx + { + public override void WriteEPL(TextWriter writer) + { + if ((Children == null) || (Children.Count == 0)) { + return; + } + Children[0].WriteEPL(writer); + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/MatchRecognizeRegExRepeat.cs b/NEsper.Core/NEsper.Core/client/soda/MatchRecognizeRegExRepeat.cs new file mode 100755 index 000000000..c2faf8d2f --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/MatchRecognizeRegExRepeat.cs @@ -0,0 +1,86 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Match-recognize pattern descriptor for repetition + /// + [Serializable] + public class MatchRecognizeRegExRepeat + { + /// + /// Initializes a new instance of the class. + /// + public MatchRecognizeRegExRepeat() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The low. + /// The high. + /// The single. + public MatchRecognizeRegExRepeat(Expression low, Expression high, Expression single) + { + this.Low = low; + this.High = high; + this.Single = single; + } + + /// + /// Gets or sets the low endpoint or null. + /// + /// + /// The low. + /// + public Expression Low { get; set; } + + /// + /// Gets or sets the high endpoint or null. + /// + /// + /// The high. + /// + public Expression High { get; set; } + + /// + /// Gets or sets the single exact-match repetition, should be null if low or high is provided. + /// + /// + /// The single. + /// + public Expression Single { get; set; } + + /// + /// RenderAny as epl. + /// + /// The writer. + public void WriteEPL(TextWriter writer) + { + writer.Write("{"); + if (Single != null) { + Single.ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + } + else { + if (Low != null) { + Low.ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + } + writer.Write(","); + if (High != null) { + High.ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + } + } + writer.Write("}"); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/client/soda/MatchRecognizeSkipClause.cs b/NEsper.Core/NEsper.Core/client/soda/MatchRecognizeSkipClause.cs new file mode 100755 index 000000000..835263d3b --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/MatchRecognizeSkipClause.cs @@ -0,0 +1,46 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.soda +{ + /// Skip clause enum for match recognize. + public enum MatchRecognizeSkipClause + { + /// Skip to current row. + TO_CURRENT_ROW, + + /// Skip to next row. + TO_NEXT_ROW, + + /// Skip past last row. + PAST_LAST_ROW + } + + public static class MatchRecognizeSkipClauseExtensions + { + /// Returns clause text. + /// textual + public static string GetText(this MatchRecognizeSkipClause value) + { + switch(value) + { + case MatchRecognizeSkipClause.TO_CURRENT_ROW: + return ("to current row"); + case MatchRecognizeSkipClause.TO_NEXT_ROW: + return ("to next row"); + case MatchRecognizeSkipClause.PAST_LAST_ROW: + return ("past last row"); + } + + throw new ArgumentException("value", "value"); + } + } +} + diff --git a/NEsper.Core/NEsper.Core/client/soda/MaxProjectionExpression.cs b/NEsper.Core/NEsper.Core/client/soda/MaxProjectionExpression.cs new file mode 100755 index 000000000..749c5002b --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/MaxProjectionExpression.cs @@ -0,0 +1,94 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.client.soda +{ + /// + /// Maximum of the (distinct) values returned by an expression. + /// + [Serializable] + public class MaxProjectionExpression : ExpressionBase + { + /// + /// Ctor. + /// + public MaxProjectionExpression() { + } + + /// + /// Ctor - for use to create an expression tree, without inner expression + /// + /// true if distinct + public MaxProjectionExpression(bool isDistinct) + { + IsDistinct = isDistinct; + } + + /// + /// Ctor - for use to create an expression tree, without inner expression + /// + /// true if distinct + /// indicator max-ever + public MaxProjectionExpression(bool isDistinct, bool isEver) + { + IsDistinct = isDistinct; + IsEver = isEver; + } + + /// + /// Ctor - adds the expression to project. + /// + /// returning values to project + /// true if distinct + public MaxProjectionExpression(Expression expression, bool isDistinct) + { + IsDistinct = isDistinct; + Children.Add(expression); + } + + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.UNARY; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + string name; + if (Children.Count > 1) { + name = "fmax"; + } + else { + if (IsEver) { + name = "maxever"; + } + else { + name = "max"; + } + } + RenderAggregation(writer, name, IsDistinct, Children); + } + + /// + /// Returns true if the projection considers distinct values only. + /// + /// true if distinct + public bool IsDistinct { get; set; } + + /// + /// Returns true for max-ever + /// + /// indicator for "ever" + public bool IsEver { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/MaxRowExpression.cs b/NEsper.Core/NEsper.Core/client/soda/MaxRowExpression.cs new file mode 100755 index 000000000..775f724e5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/MaxRowExpression.cs @@ -0,0 +1,107 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Maximum-value per-row expression (not aggregating) determines the maximum value among a set of values. + /// + [Serializable] + public class MaxRowExpression : ExpressionBase + { + /// + /// Ctor - for use to create an expression tree, without child expression. + /// + /// Use add methods to add child expressions to acts upon. + /// + public MaxRowExpression() + { + } + + /// Ctor. + /// + /// the name of the property providing a value to determine the maximum of + /// + /// + /// the name of the property providing a value to determine the maximum of + /// + /// optional additional properties to consider + public MaxRowExpression(String propertyOne, String propertyTwo, String[] moreProperties) + { + AddChild(new PropertyValueExpression(propertyOne)); + AddChild(new PropertyValueExpression(propertyTwo)); + for (int i = 0; i < moreProperties.Length; i++) + { + AddChild(new PropertyValueExpression(moreProperties[i])); + } + } + + /// Ctor. + /// provides a value to determine the maximum of + /// provides a value to determine the maximum of + /// optional additional values to consider + public MaxRowExpression(Expression exprOne, Expression exprTwo, params Expression[] moreExpressions) + { + AddChild(exprOne); + AddChild(exprTwo); + for (int i = 0; i < moreExpressions.Length; i++) + { + AddChild(moreExpressions[i]); + } + } + + /// Add a constant to include in the computation. + /// constant to add + /// expression + public MaxRowExpression Add(Object @object) + { + Children.Add(new ConstantExpression(@object)); + return this; + } + + /// Add an expression to include in the computation. + /// to add + /// expression + public MaxRowExpression Add(Expression expression) + { + Children.Add(expression); + return this; + } + + /// Add a property to include in the computation. + /// is the name of the property + /// expression + public MaxRowExpression Add(String propertyName) + { + Children.Add(new PropertyValueExpression(propertyName)); + return this; + } + + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.UNARY; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + writer.Write("max("); + + String delimiter = ""; + foreach (Expression expr in this.Children) + { + writer.Write(delimiter); + expr.ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + delimiter = ","; + } + writer.Write(')'); + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/client/soda/MedianProjectionExpression.cs b/NEsper.Core/NEsper.Core/client/soda/MedianProjectionExpression.cs new file mode 100755 index 000000000..fdb6f82df --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/MedianProjectionExpression.cs @@ -0,0 +1,66 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.client.soda +{ + /// + /// Median projection (aggregation) in the distinct and regular form. + /// + [Serializable] + public class MedianProjectionExpression : ExpressionBase + { + /// + /// Ctor. + /// + public MedianProjectionExpression() + { + } + + /// + /// Ctor - for use to create an expression tree, without inner expression + /// + /// true if distinct + public MedianProjectionExpression(bool isDistinct) + { + IsDistinct = isDistinct; + } + + /// + /// Ctor - adds the expression to project. + /// + /// returning values to project + /// true if distinct + public MedianProjectionExpression(Expression expression, bool isDistinct) + { + IsDistinct = isDistinct; + Children.Add(expression); + } + + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.UNARY; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + RenderAggregation(writer, "median", IsDistinct, Children); + } + + /// + /// Returns true if the projection considers distinct values only. + /// + /// true if distinct + public bool IsDistinct { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/MethodInvocationStream.cs b/NEsper.Core/NEsper.Core/client/soda/MethodInvocationStream.cs new file mode 100755 index 000000000..0b91eda72 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/MethodInvocationStream.cs @@ -0,0 +1,125 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// An stream that polls from a method. + public class MethodInvocationStream : Stream{ + /// Ctor. + public MethodInvocationStream() + { + } + + /// + /// Ctor. + /// + /// is the name of the class providing the method + /// is the name of the public static method + /// is the optional as-name of the stream, or null if unnamed + public MethodInvocationStream(string className, string methodName, string optStreamName) + : base(optStreamName) + { + ClassName = className; + MethodName = methodName; + ParameterExpressions = new List(); + } + + /// + /// Creates a new method-invocation-based stream without parameters. + /// + /// is the name of the class providing the method + /// is the name of the public static method + /// stream + public static MethodInvocationStream Create(string className, string methodName) { + return new MethodInvocationStream(className, methodName, null); + } + + /// + /// Creates a new method-invocation-based stream without parameters. + /// + /// is the name of the class providing the method + /// is the name of the public static method + /// is the optional as-name of the stream, or null if unnamed + /// stream + public static MethodInvocationStream Create(string className, string methodName, string optStreamName) { + return new MethodInvocationStream(className, methodName, optStreamName); + } + + /// + /// Returns the name of the class providing the method. + /// + /// class name + public string ClassName { get; set; } + + /// + /// Returns the name of the static method to invoke in the from-clause. + /// + /// method name + public string MethodName { get; set; } + + /// + /// Returns a list of expressions that are parameters to the method. + /// + /// list of parameter expressions + public IList ParameterExpressions { get; set; } + + /// + /// Returns the optional event type name + /// + /// event type name name + public string OptionalEventTypeName { get; set; } + + /// + /// Adds a parameters to the method invocation. + /// + /// is the expression to add + /// stream + public MethodInvocationStream AddParameter(Expression parameterExpression) { + ParameterExpressions.Add(parameterExpression); + return this; + } + + public override void ToEPLStream(TextWriter writer, EPStatementFormatter formatter) { + writer.Write("method:"); + writer.Write(ClassName); + writer.Write("."); + writer.Write(MethodName); + writer.Write("("); + + string delimiter = ""; + foreach (Expression expr in ParameterExpressions) { + writer.Write(delimiter); + expr.ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + delimiter = ","; + } + writer.Write(")"); + + if (OptionalEventTypeName != null) { + writer.Write(" @Type("); + writer.Write(OptionalEventTypeName); + writer.Write(")"); + } + } + + public override void ToEPLStreamType(TextWriter writer) + { + writer.Write("method:"); + writer.Write(ClassName); + writer.Write("."); + writer.Write(MethodName); + writer.Write("(..)"); + } + + public override void ToEPLStreamOptions(TextWriter writer) + { + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/client/soda/MinProjectionExpression.cs b/NEsper.Core/NEsper.Core/client/soda/MinProjectionExpression.cs new file mode 100755 index 000000000..97558be5b --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/MinProjectionExpression.cs @@ -0,0 +1,95 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.client.soda +{ + /// + /// Minimum of the (distinct) values returned by an expression. + /// + [Serializable] + public class MinProjectionExpression : ExpressionBase + { + /// + /// Ctor. + /// + public MinProjectionExpression() + { + } + + /// + /// Ctor - for use to create an expression tree, without inner expression + /// + /// true if distinct + public MinProjectionExpression(bool isDistinct) + { + this.IsDistinct = isDistinct; + } + + /// + /// Ctor - for use to create an expression tree, without inner expression + /// + /// true if distinct + /// ever-indicator + public MinProjectionExpression(bool isDistinct, bool isEver) + { + this.IsDistinct = isDistinct; + this.IsEver = isEver; + } + + /// + /// Ctor - adds the expression to project. + /// + /// returning values to project + /// true if distinct + public MinProjectionExpression(Expression expression, bool isDistinct) + { + this.IsDistinct = isDistinct; + this.Children.Add(expression); + } + + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.UNARY; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + string name; + if (this.Children.Count > 1) { + name = "fmin"; + } + else { + if (IsEver) { + name = "minever"; + } + else { + name = "min"; + } + } + ExpressionBase.RenderAggregation(writer, name, IsDistinct, this.Children); + } + + /// + /// Returns true if the projection considers distinct values only. + /// + /// true if distinct + public bool IsDistinct { get; set; } + + /// + /// Returns true for max-ever + /// + /// indicator for "ever" + public bool IsEver { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/MinRowExpression.cs b/NEsper.Core/NEsper.Core/client/soda/MinRowExpression.cs new file mode 100755 index 000000000..86d46707c --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/MinRowExpression.cs @@ -0,0 +1,107 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Minimum-value per-row expression (not aggregating) determines the minimum value among a set of values. + /// + [Serializable] + public class MinRowExpression : ExpressionBase + { + /// + /// Ctor - for use to create an expression tree, without child expression. + /// + /// Use add methods to add child expressions to acts upon. + /// + public MinRowExpression() + { + } + + /// Ctor. + /// + /// the name of the property providing a value to determine the minimum of + /// + /// + /// the name of the property providing a value to determine the minimum of + /// + /// optional additional properties to consider + public MinRowExpression(String propertyOne, String propertyTwo, String[] moreProperties) + { + AddChild(new PropertyValueExpression(propertyOne)); + AddChild(new PropertyValueExpression(propertyTwo)); + for (int i = 0; i < moreProperties.Length; i++) + { + AddChild(new PropertyValueExpression(moreProperties[i])); + } + } + + /// Ctor. + /// provides a value to determine the maximum of + /// provides a value to determine the maximum of + /// optional additional values to consider + public MinRowExpression(Expression exprOne, Expression exprTwo, params Expression[] moreExpressions) + { + AddChild(exprOne); + AddChild(exprTwo); + for (int i = 0; i < moreExpressions.Length; i++) + { + AddChild(moreExpressions[i]); + } + } + + /// Add a constant to include in the computation. + /// constant to add + /// expression + public MinRowExpression Add(Object @object) + { + Children.Add(new ConstantExpression(@object)); + return this; + } + + /// Add an expression to include in the computation. + /// to add + /// expression + public MinRowExpression Add(Expression expression) + { + Children.Add(expression); + return this; + } + + /// Add a property to include in the computation. + /// is the name of the property + /// expression + public MinRowExpression Add(String propertyName) + { + Children.Add(new PropertyValueExpression(propertyName)); + return this; + } + + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.UNARY; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + writer.Write("min("); + + String delimiter = ""; + foreach (Expression expr in Children) + { + writer.Write(delimiter); + expr.ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + delimiter = ","; + } + writer.Write(')'); + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/client/soda/NamedParameterExpression.cs b/NEsper.Core/NEsper.Core/client/soda/NamedParameterExpression.cs new file mode 100755 index 000000000..5e10e05d2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/NamedParameterExpression.cs @@ -0,0 +1,72 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.client.soda +{ + /// + /// Named parameter expression of the form "name:expression" or "name:(expression, params Expression[])" + /// + [Serializable] + public class NamedParameterExpression : ExpressionBase + { + /// + /// Ctor - for use to create an expression tree, without child expression. + /// Use add methods to add child expressions to acts upon. + /// + public NamedParameterExpression() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The name. + public NamedParameterExpression(string name) + { + Name = name; + } + + /// + /// Gets or sets the parameter name. + /// + /// + /// The name. + /// + public string Name { get; set; } + + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.UNARY; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + writer.Write(Name); + writer.Write(':'); + if (Children.Count > 1 || Children.IsEmpty()) { + writer.Write('('); + } + + string delimiter = ""; + foreach (Expression expr in Children) + { + writer.Write(delimiter); + expr.ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + delimiter = ","; + } + if (Children.Count > 1 || Children.IsEmpty()) { + writer.Write(')'); + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/client/soda/NewInstanceOperatorExpression.cs b/NEsper.Core/NEsper.Core/client/soda/NewInstanceOperatorExpression.cs new file mode 100755 index 000000000..7b36ad908 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/NewInstanceOperatorExpression.cs @@ -0,0 +1,52 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// The "new instance" operator instantiates a host language object. + /// + [Serializable] + public class NewInstanceOperatorExpression : ExpressionBase + { + /// + /// Ctor. + /// + public NewInstanceOperatorExpression() + { + } + + /// + /// Ctor. + /// + /// the class name + public NewInstanceOperatorExpression(string className) + { + ClassName = className; + } + + public string ClassName { get; set; } + + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.UNARY; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + writer.Write("new "); + writer.Write(ClassName); + writer.Write("("); + ExpressionBase.ToPrecedenceFreeEPL(this.Children, writer); + writer.Write(")"); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/client/soda/NewOperatorExpression.cs b/NEsper.Core/NEsper.Core/client/soda/NewOperatorExpression.cs new file mode 100755 index 000000000..7983f7028 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/NewOperatorExpression.cs @@ -0,0 +1,83 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// The "new" operator is useful to format an event or event property from a list of column names and expressions. + /// + /// Useful with enumeration methods and with case-when clauses that return multiple result values, for example. + /// + /// Column names are part of the state and the number of column names must match the number of sub-expressions to the expression. + /// + [Serializable] + public class NewOperatorExpression : ExpressionBase + { + /// Ctor. + public NewOperatorExpression() + { + } + + /// + /// Ctor. The list of column names should match the number of expressions provided hereunder. + /// + /// list of column names + public NewOperatorExpression(IList columnNames) + { + ColumnNames = columnNames; + } + + /// Returns the column names. + /// colum names + public IList ColumnNames { get; set; } + + /// + /// Gets the Precedence. + /// + /// The Precedence. + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.NEGATED; } + } + + /// + /// Renders the expressions and all it's child expression, in full tree depth, as a string in language syntax. + /// + /// is the output to use + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + writer.Write("new{"); + + var delimiter = ""; + for (int i = 0; i < Children.Count; i++) { + writer.Write(delimiter); + writer.Write(ColumnNames[i]); + Expression expr = Children[i]; + + bool outputexpr = true; + if (expr is PropertyValueExpression) { + var prop = (PropertyValueExpression) expr; + if (prop.PropertyName.Equals( ColumnNames[i])) { + outputexpr = false; + } + } + + if (outputexpr) { + writer.Write("="); + expr.ToEPL(writer, Precedence); + } + delimiter = ","; + } + writer.Write("}"); + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/NotExpression.cs b/NEsper.Core/NEsper.Core/client/soda/NotExpression.cs new file mode 100755 index 000000000..a5a19b1f6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/NotExpression.cs @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Negates the contained-within subexpression. + /// + /// Has a single child expression to be negated. + /// + /// + [Serializable] + public class NotExpression : ExpressionBase + { + /// Ctor. + /// is the expression to negate + public NotExpression(Expression inner) + { + this.AddChild(inner); + } + + /// + /// Ctor - for use to create an expression tree, without child expression. + /// + public NotExpression() + { + } + + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.NEGATED; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + writer.Write("not "); + Children[0].ToEPL(writer, Precedence); + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/client/soda/OnClause.cs b/NEsper.Core/NEsper.Core/client/soda/OnClause.cs new file mode 100755 index 000000000..441f441c2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/OnClause.cs @@ -0,0 +1,65 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.soda +{ + /// A clause to delete from a named window based on a triggering event arriving and correlated to the named window events to be deleted. + [Serializable] + public abstract class OnClause + { + /// Ctor. + public OnClause() { + } + + /// Creates an on-delete clause for deleting from a named window. + /// is the named window name + /// is the as-provided name of the named window + /// on-delete clause + public static OnDeleteClause CreateOnDelete(String windowName, String asName) + { + return OnDeleteClause.Create(windowName, asName); + } + + /// Creates a split-stream clause. + /// split-stream on-insert clause + public static OnInsertSplitStreamClause CreateOnInsertSplitStream() + { + return OnInsertSplitStreamClause.Create(); + } + + /// Creates an on-select clause for selecting from a named window. + /// is the named window name + /// is the as-provided name of the named window + /// on-select clause + public static OnSelectClause CreateOnSelect(String windowName, String asName) + { + return OnSelectClause.Create(windowName, asName); + } + + /// Creates an on-Update clause for updating a named window. + /// is the named window name + /// is the as-provided name of the named window + /// expression + /// on-Update clause + public static OnUpdateClause CreateOnUpdate(String windowName, String asName, Expression expression) + { + return OnUpdateClause.Create(windowName, asName).AddAssignment(expression); + } + + /// Creates an on-set clause for setting variable values. + /// is the assignment expression + /// on-set clause + public static OnSetClause CreateOnSet(Expression expression) + { + return OnSetClause.Create(expression); + } + + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/OnDeleteClause.cs b/NEsper.Core/NEsper.Core/client/soda/OnDeleteClause.cs new file mode 100755 index 000000000..5f70a7a49 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/OnDeleteClause.cs @@ -0,0 +1,81 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// A clause to delete from a named window based on a triggering event arriving and + /// correlated to the named window events to be deleted. + /// + public class OnDeleteClause : OnClause + { + /// + /// Initializes a new instance of the class. + /// + public OnDeleteClause() + { + } + + /// + /// Creates an on-delete clause. + /// + /// is the named window name + /// is the optional as-provided name + /// + /// on-delete clause + /// + public static OnDeleteClause Create(String windowName, String optionalAsName) + { + return new OnDeleteClause(windowName, optionalAsName); + } + + /// + /// Ctor. + /// + /// is the named window name + /// is the as-provided name of the named window + public OnDeleteClause(String windowName, String optionalAsName) + { + WindowName = windowName; + OptionalAsName = optionalAsName; + } + + /// + /// Renders the clause in textual representation. + /// + /// to output to + public void ToEPL(TextWriter writer) + { + writer.Write(WindowName); + if (OptionalAsName != null) + { + writer.Write(" as "); + writer.Write(OptionalAsName); + } + } + + /// + /// Returns the name of the named window to delete from. + /// + /// + /// named window name + /// + public string WindowName { get; set; } + + /// + /// Returns the as-provided name for the named window. + /// + /// + /// name or null + /// + public string OptionalAsName { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/OnInsertSplitStreamClause.cs b/NEsper.Core/NEsper.Core/client/soda/OnInsertSplitStreamClause.cs new file mode 100755 index 000000000..75a3f916e --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/OnInsertSplitStreamClause.cs @@ -0,0 +1,103 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + + +namespace com.espertech.esper.client.soda +{ + /// A clause to insert into zero, one or more streams based on criteria. + public class OnInsertSplitStreamClause : OnClause + { + /// Ctor. + public OnInsertSplitStreamClause() + { + Items = new List(); + } + + /// Creates a split-stream on-insert clause from an indicator whether to consider the first of all where-clauses, and a list of items. + /// true for first where-clause, false for all where-clauses fire + /// is a list of insert-into, select and optional where-clauses + /// split-stream on-insert clause + public static OnInsertSplitStreamClause Create(bool isFirst, List items) + { + return new OnInsertSplitStreamClause(isFirst, items); + } + + /// Creates an split-stream on-insert clause considering only the first where-clause that matches. + /// split-stream on-insert clause + public static OnInsertSplitStreamClause Create() + { + return new OnInsertSplitStreamClause(true, new List()); + } + + /// Ctor. + /// indicator whether only the first where-clause is to match or all where-clauses. + /// tuples of insert-into, select and where-clauses. + public OnInsertSplitStreamClause(bool isFirst, List items) + { + IsFirst = isFirst; + Items = items; + } + + /// Renders the clause in textual representation. + /// to output to + /// for NewLine-whitespace formatting + public void ToEPL(TextWriter writer, EPStatementFormatter formatter) + { + foreach (OnInsertSplitStreamItem item in Items) + { + item.InsertInto.ToEPL(writer, formatter, true); + item.SelectClause.ToEPL(writer, formatter, true, false); + if (item.PropertySelects != null) + { + writer.Write(" from "); + ContainedEventSelect.ToEPL(writer, formatter, item.PropertySelects); + if (item.PropertySelectsStreamName != null) + { + writer.Write(" as "); + writer.Write(item.PropertySelectsStreamName); + } + } + if (item.WhereClause != null) + { + writer.Write(" where "); + item.WhereClause.ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + } + } + + if (!IsFirst) + { + writer.Write(" output all"); + } + } + + /// + /// Returns true for firing the insert-into for only the first where-clause that matches, or false for firing the insert-into for all where-clauses that match. + /// + /// indicator first or all + public bool IsFirst { get; set; } + + /// + /// Returns a list of insert-into, select and where-clauses. + /// + /// split-stream lines + public IList Items { get; set; } + + /// + /// Add a insert-into, select and where-clause. + /// + /// to add + public void AddItem(OnInsertSplitStreamItem item) + { + Items.Add(item); + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/OnInsertSplitStreamItem.cs b/NEsper.Core/NEsper.Core/client/soda/OnInsertSplitStreamItem.cs new file mode 100755 index 000000000..dd161adeb --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/OnInsertSplitStreamItem.cs @@ -0,0 +1,122 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.client.soda +{ + /// + /// Items within the split-stream syntax to contain a tuple of insert-into, select and where-clause. + /// + [Serializable] + public class OnInsertSplitStreamItem + { + /// Ctor. + public OnInsertSplitStreamItem() + { + } + + /// + /// Ctor. + /// + /// the insert-into clause + /// the select-clause + /// contained-event selections + /// contained-event selection stream name + /// where-expression or null + public OnInsertSplitStreamItem( + InsertIntoClause insertInto, + SelectClause selectClause, + IList propertySelects, + string propertySelectsStreamName, + Expression whereClause) + { + InsertInto = insertInto; + SelectClause = selectClause; + PropertySelects = propertySelects; + PropertySelectsStreamName = propertySelectsStreamName; + WhereClause = whereClause; + } + + /// + /// Ctor. + /// + /// the insert-into clause + /// the select-clause + /// where-expression or null + public OnInsertSplitStreamItem(InsertIntoClause insertInto, SelectClause selectClause, Expression whereClause) + : this(insertInto, selectClause, null, null, whereClause) + { + } + + /// + /// Factory method for split-stream items. + /// + /// the insert-into clause + /// the select-clause + /// where-expression or null + /// split-stream item + public static OnInsertSplitStreamItem Create( + InsertIntoClause insertInto, + SelectClause selectClause, + Expression whereClause) + { + return new OnInsertSplitStreamItem(insertInto, selectClause, whereClause); + } + + /// + /// Factory method for split-stream items. + /// + /// the insert-into clause + /// the select-clause + /// contained-event selects in the from-clause + /// stream name for contained-event selection + /// where-expression or null + /// split-stream item + public static OnInsertSplitStreamItem Create( + InsertIntoClause insertInto, + SelectClause selectClause, + IList propertySelects, + string propertySelectsStreamName, + Expression whereClause) + { + return new OnInsertSplitStreamItem(insertInto, selectClause, propertySelects, propertySelectsStreamName, whereClause); + } + + /// + /// Returns the insert-into clause. + /// + /// insert-into clause + public InsertIntoClause InsertInto { get; set; } + + /// + /// Returns the select-clause. + /// + /// select-clause + public SelectClause SelectClause { get; set; } + + /// + /// Returns the optional where-clause. + /// + /// where-clause + public Expression WhereClause { get; set; } + + /// + /// Returns contained-event selection, if any. + /// + /// list or null + public IList PropertySelects { get; set; } + + /// + /// Returns the stream name assigned to contained-event selects, or null + /// + /// stream name + public string PropertySelectsStreamName { get; set; } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/client/soda/OnMergeClause.cs b/NEsper.Core/NEsper.Core/client/soda/OnMergeClause.cs new file mode 100755 index 000000000..b10834745 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/OnMergeClause.cs @@ -0,0 +1,101 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// A clause to insert, Update or delete to/from a named window based on a triggering event arriving and correlated to the named window events to be updated. + public class OnMergeClause : OnClause + { + /// Ctor. + public OnMergeClause() + { + MatchItems = new List(); + } + + /// Ctor. + /// is the named window name + /// is the as-provided name of the named window + /// is the matched and non-matched action items + public OnMergeClause(String windowName, + String optionalAsName, + IList matchItems) + { + WindowName = windowName; + OptionalAsName = optionalAsName; + MatchItems = matchItems; + } + + /// Returns the name of the named window to Update. + /// named window name + public string WindowName { get; set; } + + /// Returns the as-provided name for the named window. + /// name or null + public string OptionalAsName { get; set; } + + /// Returns all actions. + /// actions + public IList MatchItems { get; set; } + + /// Creates an on-Update clause. + /// is the named window name + /// is the optional as-provided name + /// is the matched and non-matched action items + /// on-Update clause without assignments + public static OnMergeClause Create(String windowName, + String optionalAsName, + IList matchItems) + { + return new OnMergeClause(windowName, optionalAsName, matchItems); + } + + /// Renders the clause in textual representation. + /// to output to + /// where clause if present, or null + /// for NewLine-whitespace formatting + public void ToEPL(TextWriter writer, + Expression optionalWhereClause, + EPStatementFormatter formatter) + { + formatter.BeginMerge(writer); + writer.Write("merge "); + writer.Write(WindowName); + + if (OptionalAsName != null) + { + writer.Write(" as "); + writer.Write(OptionalAsName); + } + + if (optionalWhereClause != null) + { + formatter.BeginMergeWhere(writer); + writer.Write("where "); + optionalWhereClause.ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + } + + foreach (OnMergeMatchItem item in MatchItems) + { + item.ToEPL(writer, formatter); + } + } + + /// Add a new action to the list of actions. + /// to add + /// clause + public OnMergeClause AddAction(OnMergeMatchItem action) + { + MatchItems.Add(action); + return this; + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/soda/OnMergeMatchItem.cs b/NEsper.Core/NEsper.Core/client/soda/OnMergeMatchItem.cs new file mode 100755 index 000000000..bebbe6558 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/OnMergeMatchItem.cs @@ -0,0 +1,71 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// As part of on-merge, this represents a single "matched" or "not matched" entry. + [Serializable] + public class OnMergeMatchItem : OnClause + { + /// Ctor. + public OnMergeMatchItem() { + Actions = new List(); + } + + /// Ctor. + /// true for matched, false for not-matched + /// an optional additional filter + /// one or more actions to take + public OnMergeMatchItem(bool matched, Expression optionalCondition, IList actions) + { + IsMatched = matched; + OptionalCondition = optionalCondition; + Actions = actions; + } + + /// Renders the clause in textual representation. + /// to output to + /// for NewLine-whitespace formatting + public void ToEPL(TextWriter writer, EPStatementFormatter formatter) + { + formatter.BeginMergeWhenMatched(writer); + if (IsMatched) { + writer.Write("when matched"); + } + else { + writer.Write("when not matched"); + } + if (OptionalCondition != null) + { + writer.Write(" and "); + OptionalCondition.ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + } + foreach (OnMergeMatchedAction action in Actions) { + formatter.BeginMergeAction(writer); + action.ToEPL(writer); + } + } + + /// Returns true for matched, and false for not-matched. + /// matched or not-matched indicator + public bool IsMatched { get; set; } + + /// Returns the condition to apply or null if none is provided. + /// condition + public Expression OptionalCondition { get; set; } + + /// Returns all actions. + /// actions + public IList Actions { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/OnMergeMatchedAction.cs b/NEsper.Core/NEsper.Core/client/soda/OnMergeMatchedAction.cs new file mode 100755 index 000000000..2dea5ea6d --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/OnMergeMatchedAction.cs @@ -0,0 +1,24 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Marker interface for an on-merge clause action item. + /// + public interface OnMergeMatchedAction + { + /// + /// RenderAny to EPL. + /// + /// to render to + void ToEPL(TextWriter writer); + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/OnMergeMatchedDeleteAction.cs b/NEsper.Core/NEsper.Core/client/soda/OnMergeMatchedDeleteAction.cs new file mode 100755 index 000000000..7f5f35ff5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/OnMergeMatchedDeleteAction.cs @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// For use with on-merge clauses, deletes from a named window if matching rows are found. + /// + public class OnMergeMatchedDeleteAction : OnMergeMatchedAction + { + /// Ctor. + /// condition for action, or null if none required + public OnMergeMatchedDeleteAction(Expression whereClause) + { + WhereClause = whereClause; + } + + /// Ctor. + public OnMergeMatchedDeleteAction() + { + } + + /// Returns the action condition, or null if undefined. + /// condition + public Expression WhereClause { get; set; } + + #region OnMergeMatchedAction Members + + public void ToEPL(TextWriter writer) + { + writer.Write("then delete"); + if (WhereClause != null) + { + writer.Write(" where "); + WhereClause.ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + } + } + + #endregion + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/soda/OnMergeMatchedInsertAction.cs b/NEsper.Core/NEsper.Core/client/soda/OnMergeMatchedInsertAction.cs new file mode 100755 index 000000000..2da829131 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/OnMergeMatchedInsertAction.cs @@ -0,0 +1,98 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// For use with on-merge clauses, inserts into a named window if matching rows are not found. + public class OnMergeMatchedInsertAction : OnMergeMatchedAction + { + /// Ctor. + /// insert-into column names, or empty list if none provided + /// select expression list + /// optional condition or null + /// optionally a stream name for insert-into + public OnMergeMatchedInsertAction(IList columnNames, + IList selectList, + Expression whereClause, + String optionalStreamName) + { + ColumnNames = columnNames; + SelectList = selectList; + WhereClause = whereClause; + OptionalStreamName = optionalStreamName; + } + + /// Ctor. + public OnMergeMatchedInsertAction() + { + ColumnNames = new List(); + SelectList = new List(); + } + + /// Returns the action condition, or null if undefined. + /// condition + public Expression WhereClause { get; set; } + + /// Returns the insert-into column names, if provided. + /// column names + public IList ColumnNames { get; set; } + + /// Returns the select expressions. + /// expression list + public IList SelectList { get; set; } + + /// Returns the insert-into stream name. + /// stream name + public string OptionalStreamName { get; set; } + + #region OnMergeMatchedAction Members + + public void ToEPL(TextWriter writer) + { + writer.Write("then insert"); + if (OptionalStreamName != null) + { + writer.Write(" into "); + writer.Write(OptionalStreamName); + } + + string delimiter; + if (ColumnNames.Count > 0) + { + writer.Write("("); + delimiter = ""; + foreach (String name in ColumnNames) + { + writer.Write(delimiter); + writer.Write(name); + delimiter = ", "; + } + writer.Write(")"); + } + writer.Write(" select "); + delimiter = ""; + foreach (SelectClauseElement element in SelectList) + { + writer.Write(delimiter); + element.ToEPLElement(writer); + delimiter = ", "; + } + if (WhereClause != null) + { + writer.Write(" where "); + WhereClause.ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + } + } + + #endregion + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/soda/OnMergeMatchedUpdateAction.cs b/NEsper.Core/NEsper.Core/client/soda/OnMergeMatchedUpdateAction.cs new file mode 100755 index 000000000..de8f5d0c8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/OnMergeMatchedUpdateAction.cs @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.client.soda +{ + /// + /// For use with on-merge clauses, updates rows in a named window if matching rows are found. + /// + public class OnMergeMatchedUpdateAction : OnMergeMatchedAction + { + /// Ctor. + public OnMergeMatchedUpdateAction() + { + Assignments = Collections.GetEmptyList(); + } + + /// Ctor. + /// assignments of values to columns + /// optional condition or null + public OnMergeMatchedUpdateAction(IList assignments, Expression whereClause) { + Assignments = assignments; + WhereClause = whereClause; + } + + /// Returns the action condition, or null if undefined. + /// condition + public Expression WhereClause { get; set; } + + /// Returns the assignments to execute against any rows found in a named window + /// assignments + public IList Assignments { get; set; } + + public void ToEPL(TextWriter writer) + { + writer.Write("then update "); + UpdateClause.RenderEPLAssignments(writer, Assignments); + if (WhereClause != null) { + writer.Write(" where "); + WhereClause.ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/OnSelectClause.cs b/NEsper.Core/NEsper.Core/client/soda/OnSelectClause.cs new file mode 100755 index 000000000..7a8d3d07c --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/OnSelectClause.cs @@ -0,0 +1,87 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// A clause to delete from a named window based on a triggering event arriving and + /// correlated to the named window events to be deleted. + /// + public class OnSelectClause : OnClause + { + /// + /// Initializes a new instance of the class. + /// + public OnSelectClause() + { + } + + /// + /// Creates an on-select clause. + /// + /// is the named window name + /// is the optional name + /// + /// on-select clause + /// + public static OnSelectClause Create(String windowName, String optionalAsName) + { + return new OnSelectClause(windowName, optionalAsName); + } + + /// + /// Ctor. + /// + /// is the named window name + /// is the name of the named window + public OnSelectClause(String windowName, String optionalAsName) + { + WindowName = windowName; + OptionalAsName = optionalAsName; + } + + /// + /// Renders the clause in textual representation. + /// + /// to output to + public void ToEPL(TextWriter writer) + { + writer.Write(WindowName); + if (OptionalAsName != null) + { + writer.Write(" as "); + writer.Write(OptionalAsName); + } + } + + /// + /// Returns the name of the named window to delete from. + /// + /// + /// named window name + /// + public string WindowName { get; set; } + + /// + /// Returns the as-provided name for the named window. + /// + /// + /// name + /// + public string OptionalAsName { get; set; } + + /// + /// Gets or sets a value indicating whether select-and-delete or just select. + /// + /// true if [select and delete]; otherwise, false. + public bool IsDeleteAndSelect { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/OnSetClause.cs b/NEsper.Core/NEsper.Core/client/soda/OnSetClause.cs new file mode 100755 index 000000000..4f81bf746 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/OnSetClause.cs @@ -0,0 +1,67 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// A clause to assign new values to variables based on a triggering event arriving. + /// + public class OnSetClause : OnClause + { + /// + /// Creates a new on-set clause for setting variables, and adds a variable to set. + /// + /// is the assignment expression providing the new variable value + /// on-set clause + public static OnSetClause Create(Expression expression) + { + var clause = new OnSetClause(); + clause.AddAssignment(expression); + return clause; + } + + /// + /// Ctor. + /// + public OnSetClause() + { + Assignments = new List(); + } + + /// + /// Adds a variable to set to the clause. + /// + /// expression providing the new variable value + /// clause + public OnSetClause AddAssignment(Expression expression) + { + Assignments.Add(new Assignment(expression)); + return this; + } + + /// + /// Returns the list of variable assignments. + /// + /// pair of variable name and expression + public IList Assignments { get; set; } + + /// + /// Renders the clause in EPL. + /// + /// to output to + /// for NewLine-whitespace formatting + public void ToEPL(TextWriter writer, EPStatementFormatter formatter) + { + formatter.BeginOnSet(writer); + UpdateClause.RenderEPLAssignments(writer, Assignments); + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/OnUpdateClause.cs b/NEsper.Core/NEsper.Core/client/soda/OnUpdateClause.cs new file mode 100755 index 000000000..ed666358f --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/OnUpdateClause.cs @@ -0,0 +1,92 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// A clause to Update a named window based on a triggering event arriving and correlated + /// to the named window events to be updated. + /// + public class OnUpdateClause : OnClause + { + /// + /// Ctor. + /// + public OnUpdateClause() + { + Assignments = new List(); + } + + /// + /// Creates an on-Update clause. + /// + /// is the named window name + /// is the optional as-provided name + /// on-Update clause without assignments + public static OnUpdateClause Create(String windowName, String optionalAsName) + { + return new OnUpdateClause(windowName, optionalAsName); + } + + /// + /// Ctor. + /// + /// is the named window name + /// is the as-provided name of the named window + public OnUpdateClause(String windowName, String optionalAsName) + { + WindowName = windowName; + OptionalAsName = optionalAsName; + Assignments = new List(); + } + + /// + /// Renders the clause in textual representation. + /// + /// to output to + public void ToEPL(TextWriter writer) + { + writer.Write(WindowName); + if (OptionalAsName != null) + { + writer.Write(" as "); + writer.Write(OptionalAsName); + } + + writer.Write(" "); + UpdateClause.RenderEPLAssignments(writer, Assignments); + } + + /// + /// Returns the name of the named window to Update. + /// + /// named window name + public string WindowName { get; set; } + + /// Returns the as-provided name for the named window. + /// name or null + public string OptionalAsName { get; set; } + + /// Adds a variable to set to the clause. + /// expression providing the new variable value + /// clause + public OnUpdateClause AddAssignment(Expression expression) + { + Assignments.Add(new Assignment(expression)); + return this; + } + + /// Returns the list of variable assignments. + /// pair of variable name and expression + public IList Assignments { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/OrderByClause.cs b/NEsper.Core/NEsper.Core/client/soda/OrderByClause.cs new file mode 100755 index 000000000..9ecf77b79 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/OrderByClause.cs @@ -0,0 +1,115 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// An order-by clause consists of expressions and flags indicating if ascending or descending. + /// + [Serializable] + public class OrderByClause + { + private IList orderByExpressions; + + /// Create an empty order-by clause. + /// clause + public static OrderByClause Create() + { + return new OrderByClause(); + } + + /// Create an order-by clause. + /// is the property names to order by + /// clause + public static OrderByClause Create(params String[] properties) + { + return new OrderByClause(properties); + } + + /// Create an order-by clause. + /// is the expressios returning values to order by + /// clause + public static OrderByClause Create(params Expression[] expressions) + { + return new OrderByClause(expressions); + } + + /// Adds a property and flag. + /// is the name of the property to add + /// true for descending, false for ascending sort + /// clause + public OrderByClause Add(String property, bool isDescending) + { + orderByExpressions.Add(new OrderByElement(Expressions.GetPropExpr(property), isDescending)); + return this; + } + + /// Adds an expression and flag. + /// returns values to order by + /// true for descending, false for ascending sort + /// clause + public OrderByClause Add(Expression expression, bool isDescending) + { + orderByExpressions.Add(new OrderByElement(expression, isDescending)); + return this; + } + + /// Ctor. + public OrderByClause() + { + orderByExpressions = new List(); + } + + /// Ctor. + /// property names + public OrderByClause(params String[] properties) + : this() + { + for (int i = 0; i < properties.Length; i++) + { + orderByExpressions.Add(new OrderByElement(Expressions.GetPropExpr(properties[i]), false)); + } + } + + /// Ctor. + /// is the expressions + public OrderByClause(params Expression[] expressions) + : this() + { + for (int i = 0; i < expressions.Length; i++) + { + orderByExpressions.Add(new OrderByElement(expressions[i], false)); + } + } + + /// Gets or set a list of expressions and flags to order by. + /// order-by elements + public IList OrderByExpressions + { + get { return orderByExpressions; } + set { orderByExpressions = value; } + } + + /// Renders the clause in textual representation. + /// to output to + public void ToEPL(TextWriter writer) + { + String delimiter = ""; + foreach (OrderByElement element in orderByExpressions) + { + writer.Write(delimiter); + element.ToEPL(writer); + delimiter = ", "; + } + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/client/soda/OrderByElement.cs b/NEsper.Core/NEsper.Core/client/soda/OrderByElement.cs new file mode 100755 index 000000000..9ca179f2d --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/OrderByElement.cs @@ -0,0 +1,57 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// A single entry in an order-by clause consisting of an expression and order ascending or descending flag. + /// + [Serializable] + public class OrderByElement + { + /// + /// Initializes a new instance of the class. + /// + public OrderByElement() + { + } + + /// Ctor. + /// is the expression to order by + /// true for descending sort, false for ascending sort + public OrderByElement(Expression expression, bool descending) + { + Expression = expression; + IsDescending = descending; + } + + /// Gets or sets the order-by value expression. + /// expression + public Expression Expression { get; set; } + + /// + /// True for descending sorts for this column, false for ascending sort. + /// + /// true for descending sort + public bool IsDescending { get; set; } + + /// Renders the clause in textual representation. + /// to output to + public void ToEPL(TextWriter writer) + { + Expression.ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + if (IsDescending) + { + writer.Write(" desc"); + } + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/client/soda/OrderedObjectParamExpression.cs b/NEsper.Core/NEsper.Core/client/soda/OrderedObjectParamExpression.cs new file mode 100755 index 000000000..e7c8b0591 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/OrderedObjectParamExpression.cs @@ -0,0 +1,67 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// For use in view parameter lists, this is a wrapper expression that adds an ascending or descending sort indicator to its single child expression. + /// + [Serializable] + public class OrderedObjectParamExpression : ExpressionBase + { + /// + /// Ctor. + /// + public OrderedObjectParamExpression() + { + } + + /// + /// Ctor. + /// + /// to indicate a descending sort, or false for ascending + public OrderedObjectParamExpression(bool descending) + { + IsDescending = descending; + } + + /// + /// Returns true for descending, false for ascending. + /// + /// + /// true if this instance is descending; otherwise, false. + /// + /// indicator for descending sort + public bool IsDescending { get; set; } + + /// + /// Return Precedence. + /// + /// Precedence + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.UNARY; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + Children[0].ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + if (IsDescending) + { + writer.Write(" desc"); + } + else + { + writer.Write(" asc"); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/OuterJoinQualifier.cs b/NEsper.Core/NEsper.Core/client/soda/OuterJoinQualifier.cs new file mode 100755 index 000000000..dbd3094d3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/OuterJoinQualifier.cs @@ -0,0 +1,98 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using com.espertech.esper.type; + +namespace com.espertech.esper.client.soda +{ + /// + /// Qualifies a join by providing the outer join type (full/left/right) and joined-on properties. + /// + [Serializable] + public class OuterJoinQualifier + { + /// + /// Initializes a new instance of the class. + /// + public OuterJoinQualifier() + { + } + + /// + /// Ctor. + /// + /// is the type of outer join + /// is a property providing joined-on values + /// is a property providing joined-on values + public OuterJoinQualifier(OuterJoinType type, PropertyValueExpression left, PropertyValueExpression right) + : this(type, left, right, new List()) + { + } + + /// + /// Ctor. + /// + /// is the type of outer join + /// is a property providing joined-on values + /// is a property providing joined-on values + /// for any pairs of additional on-clause properties + public OuterJoinQualifier(OuterJoinType type, Expression left, Expression right, + IList additionalProperties) + { + JoinType = type; + Left = left; + Right = right; + AdditionalProperties = additionalProperties; + } + + /// Gets or sets the type of outer join. + /// outer join type + public OuterJoinType JoinType { get; set; } + + /// Gets or sets the property value expression to join on. + /// expression providing joined-on values + public Expression Left { get; set; } + + /// Gets or sets the property value expression to join on. + /// expression providing joined-on values + public Expression Right { get; set; } + + /// + /// Gets the optional additional properties in the on-clause of the outer join. + /// + /// pairs of properties connected via logical-and in an on-clause + public IList AdditionalProperties { get; set; } + + /// Creates qualifier. + /// is a property name providing joined-on values + /// is the type of outer join + /// is a property name providing joined-on values + /// qualifier + public static OuterJoinQualifier Create(String propertyLeft, OuterJoinType type, String propertyRight) + { + return new OuterJoinQualifier(type, new PropertyValueExpression(propertyLeft), + new PropertyValueExpression(propertyRight)); + } + + /// + /// Add additional properties to the on-clause, which are logical-and to existing properties + /// + /// property providing joined-on value + /// property providing joined-on value + /// outer join qualifier + public OuterJoinQualifier Add(String propertyLeft, String propertyRight) + { + AdditionalProperties.Add(new PropertyValueExpressionPair( + new PropertyValueExpression(propertyLeft), + new PropertyValueExpression(propertyRight))); + return this; + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/client/soda/OutputLimitClause.cs b/NEsper.Core/NEsper.Core/client/soda/OutputLimitClause.cs new file mode 100755 index 000000000..c986b4473 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/OutputLimitClause.cs @@ -0,0 +1,394 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.compat; + +namespace com.espertech.esper.client.soda +{ + /// + /// An output limit clause defines how to limit output of statements and consists of a + /// selector specifiying which events to select to output, a frequency and a unit. + /// + [Serializable] + public class OutputLimitClause + { + /// Ctor. + public OutputLimitClause() + { + } + + /// Ctor. + /// selector + /// unit + public OutputLimitClause(OutputLimitSelector selector, OutputLimitUnit unit) + { + Selector = selector; + Unit = unit; + } + + /// Creates an output limit clause. + /// a frequency to output at + /// clause + public static OutputLimitClause Create(TimePeriodExpression timePeriodExpression) + { + return new OutputLimitClause(OutputLimitSelector.DEFAULT, timePeriodExpression); + } + + /// Create with after-only time period. + /// time period + /// clause + public static OutputLimitClause CreateAfter(TimePeriodExpression afterTimePeriodExpression) + { + return new OutputLimitClause( + OutputLimitSelector.DEFAULT, OutputLimitUnit.AFTER, afterTimePeriodExpression, null); + } + + /// Create with after-only and number of events. + /// num events + /// clause + public static OutputLimitClause CreateAfter(int afterNumEvents) + { + return new OutputLimitClause(OutputLimitSelector.DEFAULT, OutputLimitUnit.AFTER, null, afterNumEvents); + } + + /// Creates an output limit clause. + /// is the events to select + /// a frequency to output at + /// clause + public static OutputLimitClause Create(OutputLimitSelector selector, TimePeriodExpression timePeriodExpression) + { + return new OutputLimitClause(selector, timePeriodExpression); + } + + /// Creates an output limit clause. + /// is the events to select + /// a frequency to output at + /// clause + public static OutputLimitClause Create(OutputLimitSelector selector, double frequency) + { + return new OutputLimitClause(selector, frequency); + } + + /// Creates an output limit clause. + /// is the events to select + /// is the variable providing the output limit frequency + /// clause + public static OutputLimitClause Create(OutputLimitSelector selector, String frequencyVariable) + { + return new OutputLimitClause(selector, frequencyVariable); + } + + /// Creates an output limit clause. + /// a frequency to output at + /// clause + public static OutputLimitClause Create(double frequency) + { + return new OutputLimitClause(OutputLimitSelector.DEFAULT, frequency); + } + + /// Creates an output limit clause. + /// is the variable name providing output rate frequency values + /// clause + public static OutputLimitClause Create(String frequencyVariable) + { + return new OutputLimitClause(OutputLimitSelector.DEFAULT, frequencyVariable); + } + + /// Creates an output limit clause with a when-expression and optional then-assignment expressions to be added. + /// the expression that returns true to trigger output + /// clause + public static OutputLimitClause Create(Expression whenExpression) + { + return new OutputLimitClause(OutputLimitSelector.DEFAULT, whenExpression, new List()); + } + + /// Creates an output limit clause with a crontab 'at' schedule parameters, see and related. + /// the crontab schedule parameters + /// clause + public static OutputLimitClause CreateSchedule(Expression[] scheduleParameters) + { + return new OutputLimitClause(OutputLimitSelector.DEFAULT, scheduleParameters); + } + + /// Ctor. + /// is the events to select + /// a frequency to output at + public OutputLimitClause(OutputLimitSelector selector, Double frequency) + { + Selector = selector; + Frequency = frequency; + Unit = OutputLimitUnit.EVENTS; + } + + /// Ctor. + /// is the events to select + /// the unit for the frequency + public OutputLimitClause(OutputLimitSelector selector, TimePeriodExpression timePeriodExpression) + { + Selector = selector; + TimePeriodExpression = timePeriodExpression; + Unit = OutputLimitUnit.TIME_PERIOD; + } + + /// Ctor. + /// timer period for after. + public OutputLimitClause(TimePeriodExpression afterTimePeriodExpression) + { + Unit = OutputLimitUnit.AFTER; + AfterTimePeriodExpression = afterTimePeriodExpression; + } + + /// Ctor. + /// is the events to select + /// is the variable name providing output rate frequency values + public OutputLimitClause(OutputLimitSelector selector, String frequencyVariable) + { + Selector = selector; + FrequencyVariable = frequencyVariable; + Unit = OutputLimitUnit.EVENTS; + } + + /// Ctor. + /// is the events to select + /// a frequency to output at + /// the unit for the frequency + /// is the variable name providing output rate frequency values + public OutputLimitClause( + OutputLimitSelector selector, + Double? frequency, + String frequencyVariable, + OutputLimitUnit unit) + { + Selector = selector; + Frequency = frequency; + FrequencyVariable = frequencyVariable; + Unit = unit; + } + + /// Ctor. + /// is the events to select + /// the unit of selection + /// after-keyword time period + /// after-keyword number of events + public OutputLimitClause( + OutputLimitSelector selector, + OutputLimitUnit unit, + TimePeriodExpression afterTimePeriod, + int? afterNumberOfEvents) + { + Selector = selector; + Unit = unit; + AfterTimePeriodExpression = afterTimePeriod; + AfterNumberOfEvents = afterNumberOfEvents; + } + + /// Ctor. + /// is the events to select + /// the crontab schedule parameters + public OutputLimitClause(OutputLimitSelector selector, Expression[] crontabAtParameters) + { + Selector = selector; + CrontabAtParameters = crontabAtParameters; + Unit = OutputLimitUnit.CRONTAB_EXPRESSION; + } + + /// Ctor. + /// is the events to select + /// the bool expression to evaluate to control output + /// the variable assignments, optional or an empty list + public OutputLimitClause( + OutputLimitSelector selector, + Expression whenExpression, + IList thenAssignments) + { + Selector = selector; + WhenExpression = whenExpression; + ThenAssignments = thenAssignments; + Unit = OutputLimitUnit.WHEN_EXPRESSION; + } + + /// Returns the selector indicating the events to output. + /// selector + public OutputLimitSelector Selector { get; set; } + + /// Returns output frequency. + /// frequency of output + public double? Frequency { get; private set; } + + /// Returns the unit the frequency is in. + /// unit for the frequency. + public OutputLimitUnit Unit { get; set; } + + /// Returns the variable name of the variable providing output rate frequency values, or null if the frequency is a fixed value. + /// variable name or null if no variable is used + public string FrequencyVariable { get; set; } + + /// Returns the expression that controls output for use with the when-keyword. + /// expression should be bool result + public Expression WhenExpression { get; private set; } + + /// Returns the time period, or null if none provided. + /// time period + public Expression TimePeriodExpression { get; private set; } + + /// Returns the list of optional then-keyword variable assignments, if any + /// list of variable assignments or null if none + public IList ThenAssignments { get; private set; } + + /// Adds a then-keyword variable assigment for use with the when-keyword. + /// expression to calculate new value + /// clause + public OutputLimitClause AddThenAssignment(Expression assignmentExpression) + { + ThenAssignments.Add(new Assignment(assignmentExpression)); + return this; + } + + /// Returns the crontab parameters, or null if not using crontab-like schedule. + /// parameters + public Expression[] CrontabAtParameters { get; private set; } + + /// Returns true for output upon termination of a context partition + /// indicator + public bool IsAndAfterTerminate { get; set; } + + /// Renders the clause in textual representation. + /// to output to + public void ToEPL(TextWriter writer) + { + if (AfterTimePeriodExpression != null) + { + writer.Write("after "); + AfterTimePeriodExpression.ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + writer.Write(" "); + } + else if ((AfterNumberOfEvents != null) && (AfterNumberOfEvents != 0)) + { + writer.Write("after "); + writer.Write(Convert.ToString(AfterNumberOfEvents)); + writer.Write(" events "); + } + + if (Selector != OutputLimitSelector.DEFAULT) + { + writer.Write(Selector.GetText()); + writer.Write(" "); + } + if (Unit == OutputLimitUnit.WHEN_EXPRESSION) + { + writer.Write("when "); + WhenExpression.ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + + if ((ThenAssignments != null) && (ThenAssignments.Count > 0)) + { + WriteThenAssignments(writer, ThenAssignments); + } + } + else if (Unit == OutputLimitUnit.CRONTAB_EXPRESSION) + { + writer.Write("at ("); + String delimiter = ""; + for (int i = 0; i < CrontabAtParameters.Length; i++) + { + writer.Write(delimiter); + CrontabAtParameters[i].ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + delimiter = ", "; + } + writer.Write(")"); + } + else if (Unit == OutputLimitUnit.TIME_PERIOD && TimePeriodExpression != null) + { + writer.Write("every "); + TimePeriodExpression.ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + } + else if (Unit == OutputLimitUnit.AFTER) + { + // no action required + } + else if (Unit == OutputLimitUnit.CONTEXT_PARTITION_TERM) + { + writer.Write("when terminated"); + OutputAndAfter(writer); + } + else + { + writer.Write("every "); + if (FrequencyVariable == null) + { + writer.Write(Convert.ToString(Frequency.AsInt())); + } + else + { + writer.Write(FrequencyVariable); + } + writer.Write(" events"); + } + + if (IsAndAfterTerminate) + { + writer.Write(" and when terminated"); + OutputAndAfter(writer); + } + } + + /// Returns the after-keyword time period. + /// after-keyword time period + public Expression AfterTimePeriodExpression { get; set; } + + /// Sets the after-keyword time period. + /// after-keyword time period + public OutputLimitClause SetAfterTimePeriodExpression(Expression afterTimePeriodExpression) + { + AfterTimePeriodExpression = afterTimePeriodExpression; + return this; + } + + /// Returns the after-keyword number of events, or null if undefined. + /// num events for after-keyword + public int? AfterNumberOfEvents { get; set; } + + /// Sets the after-keyword number of events, or null if undefined. + /// set num events for after-keyword + public OutputLimitClause SetAfterNumberOfEvents(int? afterNumberOfEvents) + { + AfterNumberOfEvents = afterNumberOfEvents; + return this; + } + + /// Returns the optional expression evaluated when a context partition terminates before triggering output. + /// expression + public Expression AndAfterTerminateAndExpr { get; set; } + + /// Returns the set-assignments to execute when a context partition terminates. + /// set-assignments + public IList AndAfterTerminateThenAssignments { get; set; } + + private void WriteThenAssignments(TextWriter writer, IList thenAssignments) + { + writer.Write(" then "); + UpdateClause.RenderEPLAssignments(writer, thenAssignments); + } + + private void OutputAndAfter(TextWriter writer) + { + if (AndAfterTerminateAndExpr != null) + { + writer.Write(" and "); + AndAfterTerminateAndExpr.ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + } + if (AndAfterTerminateThenAssignments != null && AndAfterTerminateThenAssignments.Count > 0) + { + WriteThenAssignments(writer, AndAfterTerminateThenAssignments); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/OutputLimitSelector.cs b/NEsper.Core/NEsper.Core/client/soda/OutputLimitSelector.cs new file mode 100755 index 000000000..5ab1da5f9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/OutputLimitSelector.cs @@ -0,0 +1,63 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.soda +{ + /// + /// Selector for use in output rate limiting. + /// + [Serializable] + public enum OutputLimitSelector + { + /// + /// Output first event of last interval. + /// + FIRST, + /// + /// Output last event of last interval. + /// + LAST, + /// + /// Output all events of last interval. For group-by statements, output all groups + /// regardless whether the group changed between the last output. + /// + ALL, + /// + /// Output all events as a snapshot considering the current state regardless of interval. + /// + SNAPSHOT, + /// + /// Output all events of last interval. + /// + DEFAULT + } + + public static class OutputLimitSelectorExtensions + { + public static string GetText(this OutputLimitSelector @enum) + { + switch (@enum) + { + case OutputLimitSelector.FIRST: + return "first"; + case OutputLimitSelector.LAST: + return "last"; + case OutputLimitSelector.ALL: + return "all"; + case OutputLimitSelector.SNAPSHOT: + return "snapshot"; + case OutputLimitSelector.DEFAULT: + return "default"; + } + + throw new ArgumentException(); + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/client/soda/OutputLimitUnit.cs b/NEsper.Core/NEsper.Core/client/soda/OutputLimitUnit.cs new file mode 100755 index 000000000..6584d224a --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/OutputLimitUnit.cs @@ -0,0 +1,58 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.soda +{ + /// Unit for output rate limiting. + public enum OutputLimitUnit + { + /// The time period unit. + TIME_PERIOD, + + /// The number of events unit. + EVENTS, + + /// The unit representing a when-expression. + WHEN_EXPRESSION, + + /// The unit representing a crontab-at-expression. + CRONTAB_EXPRESSION, + + /// The unit representing just after a time period or after a number of events. + AFTER, + + /// The unit representing that output occurs when the context partition terminates. + CONTEXT_PARTITION_TERM + } + + public static class OutputLimitUnitExtensions + { + public static string GetText(this OutputLimitUnit value) + { + switch (value) + { + case OutputLimitUnit.TIME_PERIOD: + return ("timeperiod"); + case OutputLimitUnit.EVENTS: + return ("events"); + case OutputLimitUnit.WHEN_EXPRESSION: + return ("when"); + case OutputLimitUnit.CRONTAB_EXPRESSION: + return ("crontab"); + case OutputLimitUnit.AFTER: + return ("after"); + case OutputLimitUnit.CONTEXT_PARTITION_TERM: + return ("when terminated"); + } + + throw new ArgumentException(); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/soda/PatternAndExpr.cs b/NEsper.Core/NEsper.Core/client/soda/PatternAndExpr.cs new file mode 100755 index 000000000..f58eb581f --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/PatternAndExpr.cs @@ -0,0 +1,66 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Logical AND for use in pattern expressions. + /// + [Serializable] + public class PatternAndExpr : PatternExprBase + { + /// + /// Ctor - for use to create a pattern expression tree, without pattern child expression. + /// + public PatternAndExpr() + { + } + + /// Ctor. + /// a first pattern expression in the AND relationship + /// a second pattern expression in the AND relationship + /// further optional pattern expressions in the AND relationship + public PatternAndExpr(PatternExpr first, PatternExpr second, params PatternExpr[] patternExprs) + { + AddChild(first); + AddChild(second); + for (int i = 0; i < patternExprs.Length; i++) + { + AddChild(patternExprs[i]); + } + } + + /// Adds a pattern expression to the AND relationship between patterns. + /// to add + /// pattern expression + public PatternAndExpr Add(PatternExpr expr) + { + Children.Add(expr); + return this; + } + + public override PatternExprPrecedenceEnum Precedence + { + get { return PatternExprPrecedenceEnum.AND; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer, EPStatementFormatter formatter) + { + String delimiter = ""; + foreach (PatternExpr child in Children) + { + writer.Write(delimiter); + child.ToEPL(writer, Precedence, formatter); + delimiter = " and "; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/PatternEveryDistinctExpr.cs b/NEsper.Core/NEsper.Core/client/soda/PatternEveryDistinctExpr.cs new file mode 100755 index 000000000..659cb14ba --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/PatternEveryDistinctExpr.cs @@ -0,0 +1,60 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Every-Distinct construct for use in pattern expressions. + /// + [Serializable] + public class PatternEveryDistinctExpr : PatternExprBase + { + /// + /// Ctor - for use to create a pattern expression tree, without unique-criterial expression. + /// + public PatternEveryDistinctExpr() + { + } + + /// + /// Ctor - for use to create a pattern expression tree, without unique-criterial expression. + /// + /// distinct expressions + public PatternEveryDistinctExpr(IList expressions) + { + Expressions = expressions; + } + + /// Returns distinct expressions + /// expr + public IList Expressions { get; set; } + + public override PatternExprPrecedenceEnum Precedence + { + get { return PatternExprPrecedenceEnum.EVERY_NOT; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer, EPStatementFormatter formatter) + { + writer.Write("every-distinct("); + String delimiter = ""; + foreach (Expression expr in Expressions) { + writer.Write(delimiter); + expr.ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + delimiter = ","; + } + writer.Write(") "); + + Children[0].ToEPL(writer, Precedence, formatter); + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/PatternEveryExpr.cs b/NEsper.Core/NEsper.Core/client/soda/PatternEveryExpr.cs new file mode 100755 index 000000000..93c7a4931 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/PatternEveryExpr.cs @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// Pattern 'every' expression that controls the lifecycle of pattern sub-expressions. + [Serializable] + public class PatternEveryExpr : PatternExprBase + { + /// Ctor - for use to create a pattern expression tree, without pattern child expression. + public PatternEveryExpr() + { + } + + /// Ctor. + /// is the pattern expression to control lifecycle on + public PatternEveryExpr(PatternExpr inner) + { + AddChild(inner); + } + + public override PatternExprPrecedenceEnum Precedence + { + get { return PatternExprPrecedenceEnum.EVERY_NOT; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer, EPStatementFormatter formatter) + { + writer.Write("every "); + PatternExprPrecedenceEnum precedence = Precedence; + if (Children[0] is PatternEveryExpr) { + precedence = PatternExprPrecedenceEnum.MAXIMIM; + } + Children[0].ToEPL(writer, precedence, formatter); + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/PatternExpr.cs b/NEsper.Core/NEsper.Core/client/soda/PatternExpr.cs new file mode 100755 index 000000000..f84d9829b --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/PatternExpr.cs @@ -0,0 +1,42 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Interface representing a pattern expression. + /// + /// Pattern expressions are organized into a tree-like structure with nodes representing sub-expressions (composite). + /// + /// Certain types of nodes have certain requirements towards the number or types of nodes that are expected as pattern + /// sub-expressions to an pattern expression. + /// + public interface PatternExpr + { + /// Returns the list of pattern sub-expressions (child expressions) to the current pattern expression node. + /// pattern child expressions or empty list if there are no child expressions + List Children { get; set; } + + /// Returns the Precedence. + /// Precedence + PatternExprPrecedenceEnum Precedence { get; } + + /// Renders the pattern expression and all it's child expressions, in full tree depth, as a string in language syntax. + /// is the output to use + /// Precedence + /// formatter + void ToEPL(TextWriter writer, PatternExprPrecedenceEnum parentPrecedence, EPStatementFormatter formatter); + + /// Returns the id for the pattern expression, for use by tools. + /// id + string TreeObjectName { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/PatternExprBase.cs b/NEsper.Core/NEsper.Core/client/soda/PatternExprBase.cs new file mode 100755 index 000000000..3ae19188e --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/PatternExprBase.cs @@ -0,0 +1,60 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Abstract base class for all pattern expressions. + /// + [Serializable] + public abstract class PatternExprBase : PatternExpr + { + public string TreeObjectName { get; set; } + public List Children { get; set; } + + /// Ctor. + protected PatternExprBase() + { + Children = new List(); + } + + /// Adds a sub-expression to the pattern expression. + /// to add + protected void AddChild(PatternExpr expression) + { + Children.Add(expression); + } + + public virtual void ToEPL(TextWriter writer, PatternExprPrecedenceEnum parentPrecedence, EPStatementFormatter formatter) + { + if (Precedence.GetLevel() < parentPrecedence.GetLevel()) + { + writer.Write("("); + ToPrecedenceFreeEPL(writer, formatter); + writer.Write(")"); + } + else + { + ToPrecedenceFreeEPL(writer, formatter); + } + } + + /// Returns the Precedence. + /// Precedence + public abstract PatternExprPrecedenceEnum Precedence { get; } + + /// Renders the expressions and all it's child expression, in full tree depth, as a string in language syntax. + /// is the output to use + /// for NewLine-whitespace formatting + public abstract void ToPrecedenceFreeEPL(TextWriter writer, EPStatementFormatter formatter); + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/soda/PatternExprPlaceholder.cs b/NEsper.Core/NEsper.Core/client/soda/PatternExprPlaceholder.cs new file mode 100755 index 000000000..e5df9b7f3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/PatternExprPlaceholder.cs @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// For use in pattern expression as a placeholder to represent its child nodes. + /// + [Serializable] + public class PatternExprPlaceholder : PatternExprBase + { + public override void ToPrecedenceFreeEPL(TextWriter writer, EPStatementFormatter formatter) + { + if ((Children == null) || (Children.Count == 0)) + { + return; + } + PatternExpr patternExpr = Children[0]; + if (patternExpr != null) + { + patternExpr.ToEPL(writer, Precedence, formatter); + } + } + + public override PatternExprPrecedenceEnum Precedence + { + get { return PatternExprPrecedenceEnum.MINIMUM; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/PatternExprPrecedenceEnum.cs b/NEsper.Core/NEsper.Core/client/soda/PatternExprPrecedenceEnum.cs new file mode 100755 index 000000000..60592b913 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/PatternExprPrecedenceEnum.cs @@ -0,0 +1,42 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.client.soda +{ + /// Pattern precendences. + public enum PatternExprPrecedenceEnum { + /// Precedence. + MAXIMIM = (int.MaxValue), + + /// Precedence. + ATOM = (7), + /// Precedence. + GUARD = (6), + /// Precedence. + EVERY_NOT = (5), + /// Precedence. + MATCH_UNTIL = (4), + /// Precedence. + AND = (3), + /// Precedence. + OR = (2), + /// Precedence. + FOLLOWED_BY = (1), + + /// Precedence. + MINIMUM = (int.MinValue) + } + + public static class PatternExprPrecedenceEnumExtensions { + /// Returns Precedence. + /// Precedence + public static int GetLevel(this PatternExprPrecedenceEnum value) { + return (int) value; + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/PatternFilterExpr.cs b/NEsper.Core/NEsper.Core/client/soda/PatternFilterExpr.cs new file mode 100755 index 000000000..bc99b7566 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/PatternFilterExpr.cs @@ -0,0 +1,73 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// Filter for use in pattern expressions. + [Serializable] + public class PatternFilterExpr : PatternExprBase + { + /// Ctor. + public PatternFilterExpr() { + } + + /// Ctor. + /// specifies to events to filter out + public PatternFilterExpr(Filter filter) + : this(filter, null) + { + } + + /// Ctor. + /// specifies to events to filter out + /// specifies the name of the tag to assigned to matching events + public PatternFilterExpr(Filter filter, String tagName) + { + TagName = tagName; + Filter = filter; + } + + /// Returns the tag name. + /// tag name. + public string TagName { get; set; } + + /// Returns the filter specification. + /// filter + public Filter Filter { get; set; } + + public override PatternExprPrecedenceEnum Precedence + { + get { return PatternExprPrecedenceEnum.ATOM; } + } + + /// Returns the consume level, if assigned. + /// consume level + public int? OptionalConsumptionLevel { get; set; } + + public override void ToPrecedenceFreeEPL(TextWriter writer, EPStatementFormatter formatter) + { + if (TagName != null) + { + writer.Write(TagName); + writer.Write('='); + } + Filter.ToEPL(writer, formatter); + if (OptionalConsumptionLevel != null) { + writer.Write("@consume"); + if (OptionalConsumptionLevel != 1) { + writer.Write("("); + writer.Write(Convert.ToString(OptionalConsumptionLevel)); + writer.Write(")"); + } + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/PatternFollowedByExpr.cs b/NEsper.Core/NEsper.Core/client/soda/PatternFollowedByExpr.cs new file mode 100755 index 000000000..a2aa4e980 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/PatternFollowedByExpr.cs @@ -0,0 +1,96 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + + +namespace com.espertech.esper.client.soda +{ + /// + /// Followed-by for use in pattern expressions. + /// + [Serializable] + public class PatternFollowedByExpr : PatternExprBase + { + /// + /// Ctor - for use to create a pattern expression tree, without pattern child expression. + /// + public PatternFollowedByExpr() + { + } + + /// + /// Ctor. + /// + /// if parameterized by a max-limits for each pattern sub-expressions + public PatternFollowedByExpr(IList optionalMaxPerSubexpression) + { + OptionalMaxPerSubexpression = optionalMaxPerSubexpression; + } + + /// + /// Ctor. + /// + /// a first pattern expression in the followed-by relationship + /// a second pattern expression in the followed-by relationship + /// further optional pattern expressions in the followed-by relationship + public PatternFollowedByExpr(PatternExpr first, PatternExpr second, params PatternExpr[] patternExprs) + { + AddChild(first); + AddChild(second); + for (int i = 0; i < patternExprs.Length; i++) + { + AddChild(patternExprs[i]); + } + } + + /// + /// Adds a pattern expression to the followed-by relationship between patterns. + /// + /// to add + /// pattern expression + public PatternFollowedByExpr Add(PatternExpr expr) + { + Children.Add(expr); + return this; + } + + public override PatternExprPrecedenceEnum Precedence + { + get { return PatternExprPrecedenceEnum.FOLLOWED_BY; } + } + + /// Returns the instance limits, if any, for pattern-subexpressions. + /// list of max-limit or null + public IList OptionalMaxPerSubexpression { get; set; } + + public override void ToPrecedenceFreeEPL(TextWriter writer, EPStatementFormatter formatter) + { + String delimiter = ""; + int childNum = 0; + foreach (PatternExpr child in Children) + { + writer.Write(delimiter); + child.ToEPL(writer, Precedence, formatter); + + delimiter = " -> "; + if (OptionalMaxPerSubexpression != null && OptionalMaxPerSubexpression.Count > childNum) { + var maxExpr = OptionalMaxPerSubexpression[childNum]; + if (maxExpr != null) { + var inner = new StringWriter(); + maxExpr.ToEPL(inner, ExpressionPrecedenceEnum.MINIMUM); + delimiter = " -[" + inner.ToString() + "]> "; + } + } + childNum++; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/PatternGuardExpr.cs b/NEsper.Core/NEsper.Core/client/soda/PatternGuardExpr.cs new file mode 100755 index 000000000..cc5177ded --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/PatternGuardExpr.cs @@ -0,0 +1,111 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.pattern.guard; + +namespace com.espertech.esper.client.soda +{ + /// + /// Guard is the where timer-within pattern object for use in pattern expressions. + /// + [Serializable] + public class PatternGuardExpr + : EPBaseNamedObject + , PatternExpr + { + /// Ctor - for use to create a pattern expression tree, without pattern child expression. + /// is the guard object namespace + /// is the guard object name + /// is guard object parameters + public PatternGuardExpr(String @namespace, String name, IList parameters) + : base(@namespace, name, parameters) + { + Children = new List(); + } + + /// Ctor - for use to create a pattern expression tree, without pattern child expression. + /// is the guard object namespace + /// is the guard object name + /// is guard object parameters + /// is the guarded pattern expression + public PatternGuardExpr(String @namespace, String name, Expression[] parameters, PatternExpr guarded) + : base(@namespace, name, parameters) + { + Children = new List(); + Children.Add(guarded); + } + + /// Ctor - for use to create a pattern expression tree, without pattern child expression. + /// is the guard object namespace + /// is the guard object name + /// is guard object parameters + /// is the guarded pattern expression + public PatternGuardExpr(String @namespace, String name, IList parameters, PatternExpr guardedPattern) + : base(@namespace, name, parameters) + { + Children = new List(); + Children.Add(guardedPattern); + } + + public List Children { get; set; } + + /// Get sub expression + /// sub pattern + public List Guarded + { + get { return Children; } + set { Children = value; } + } + + public string TreeObjectName { get; set; } + + public PatternExprPrecedenceEnum Precedence + { + get { return PatternExprPrecedenceEnum.GUARD; } + } + + public void ToEPL(TextWriter writer, PatternExprPrecedenceEnum parentPrecedence, EPStatementFormatter formatter) + { + if (Precedence.GetLevel() < parentPrecedence.GetLevel()) + { + writer.Write("("); + ToPrecedenceFreeEPL(writer, formatter); + writer.Write(")"); + } + else + { + ToPrecedenceFreeEPL(writer, formatter); + } + } + + /// + /// Renders the expressions and all it's child expression, in full tree depth, as a string in language syntax. + /// + /// is the output to use + /// for NewLine-whitespace formatting + public void ToPrecedenceFreeEPL(TextWriter writer, EPStatementFormatter formatter) + { + Children[0].ToEPL(writer, Precedence, formatter); + if (GuardEnumExtensions.IsWhile(Namespace, Name)) + { + writer.Write(" while ("); + Parameters[0].ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + writer.Write(")"); + } + else + { + writer.Write(" where "); + base.ToEPL(writer); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/PatternMatchUntilExpr.cs b/NEsper.Core/NEsper.Core/client/soda/PatternMatchUntilExpr.cs new file mode 100755 index 000000000..7c187dc44 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/PatternMatchUntilExpr.cs @@ -0,0 +1,120 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Match-Until construct for use in pattern expressions. + /// + [Serializable] + public class PatternMatchUntilExpr : PatternExprBase + { + /// Ctor - for use to create a pattern expression tree, without pattern child expression. + public PatternMatchUntilExpr() + { + } + + /// Ctor - for use when adding required child nodes later. + /// low number of matches, or null if no lower boundary + /// high number of matches, or null if no high boundary + /// if a single bound is provided, this carries the single bound (all others should be null) + public PatternMatchUntilExpr(Expression low, Expression high, Expression single) + { + Low = low; + High = high; + Single = single; + } + + /// Ctor. + /// the single bound expression + public PatternMatchUntilExpr(Expression single) + { + Single = single; + } + + /// Ctor. + /// low number of matches, or null if no lower boundary + /// high number of matches, or null if no high boundary + /// the pattern expression that is sought to match repeatedly + /// the pattern expression that ends matching (optional, can be null) + public PatternMatchUntilExpr(Expression low, Expression high, PatternExpr match, PatternExpr until) + { + Low = low; + High = high; + AddChild(match); + AddChild(until); + } + + /// Returns the optional low endpoint for the repeat, or null if none supplied. + /// low endpoint + public Expression Low { get; set; } + + /// Returns the optional high endpoint for the repeat, or null if none supplied. + /// high endpoint + public Expression High { get; set; } + + /// Returns the single-bounds expression. + /// single-bound expression + public Expression Single { get; set; } + + public override PatternExprPrecedenceEnum Precedence + { + get { return PatternExprPrecedenceEnum.MATCH_UNTIL; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer, EPStatementFormatter formatter) + { + if (Single != null) + { + writer.Write("["); + Single.ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + writer.Write("]"); + } + else + { + if (Low != null || High != null) + { + writer.Write("["); + if ((Low != null) && (High != null)) + { + Low.ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + writer.Write(":"); + High.ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + } + else if (Low != null) + { + Low.ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + writer.Write(":"); + } + else + { + writer.Write(":"); + High.ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + } + writer.Write("] "); + } + } + + PatternExprPrecedenceEnum precedence = Precedence; + if (Children[0] is PatternMatchUntilExpr) + { + precedence = PatternExprPrecedenceEnum.MAXIMIM; + } + Children[0].ToEPL(writer, precedence, formatter); + + if (Children.Count > 1) + { + writer.Write(" until "); + Children[1].ToEPL(writer, Precedence, formatter); + } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/soda/PatternNotExpr.cs b/NEsper.Core/NEsper.Core/client/soda/PatternNotExpr.cs new file mode 100755 index 000000000..28e1ad19f --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/PatternNotExpr.cs @@ -0,0 +1,42 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + + +namespace com.espertech.esper.client.soda +{ + /// Not-expression for negating a pattern sub-expression for use in pattern expressions. + [Serializable] + public class PatternNotExpr : PatternExprBase + { + /// Ctor - for use to create a pattern expression tree, without pattern child expression. + public PatternNotExpr() + { + } + + /// Ctor. + /// is the pattern expression to negate + public PatternNotExpr(PatternExpr inner) + { + this.Children.Add(inner); + } + + public override PatternExprPrecedenceEnum Precedence + { + get { return PatternExprPrecedenceEnum.EVERY_NOT; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer, EPStatementFormatter formatter) + { + writer.Write("not "); + this.Children[0].ToEPL(writer, Precedence, formatter); + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/PatternObserverExpr.cs b/NEsper.Core/NEsper.Core/client/soda/PatternObserverExpr.cs new file mode 100755 index 000000000..938fce9f8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/PatternObserverExpr.cs @@ -0,0 +1,77 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + + +namespace com.espertech.esper.client.soda +{ + /// + /// Pattern observer expression observes occurances such as timer-at (crontab) and timer-interval. + /// + [Serializable] + public class PatternObserverExpr + : EPBaseNamedObject + , PatternExpr + { + /// + /// Ctor. + /// + public PatternObserverExpr() + { + } + + public string TreeObjectName { get; set; } + + /// Ctor - for use to create a pattern expression tree, without pattern child expression. + /// is the guard object namespace + /// is the guard object name + /// is guard object parameters + public PatternObserverExpr(String @namespace, String name, IList parameters) + : base(@namespace, name, parameters) + { + } + + public List Children + { + get { return new List(); } + set + { + // this expression has no child expressions + } + } + + public PatternExprPrecedenceEnum Precedence + { + get { return PatternExprPrecedenceEnum.ATOM; } + } + + public void ToEPL(TextWriter writer, PatternExprPrecedenceEnum parentPrecedence, EPStatementFormatter formatter) + { + if (Precedence.GetLevel() < parentPrecedence.GetLevel()) + { + writer.Write("("); + ToPrecedenceFreeEPL(writer); + writer.Write(")"); + } + else + { + ToPrecedenceFreeEPL(writer); + } + } + + /// Renders the expressions and all it's child expression, in full tree depth, as a string in language syntax. + /// is the output to use + public void ToPrecedenceFreeEPL(TextWriter writer) + { + base.ToEPL(writer); + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/PatternOrExpr.cs b/NEsper.Core/NEsper.Core/client/soda/PatternOrExpr.cs new file mode 100755 index 000000000..39a4d8b87 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/PatternOrExpr.cs @@ -0,0 +1,63 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + + +namespace com.espertech.esper.client.soda +{ + /// Logical OR for use in pattern expressions. + [Serializable] + public class PatternOrExpr : PatternExprBase + { + /// Ctor - for use to create a pattern expression tree, without pattern child expression. + public PatternOrExpr() + { + } + + /// Ctor. + /// a first pattern expression in the OR relationship + /// a second pattern expression in the OR relationship + /// further optional pattern expressions in the OR relationship + public PatternOrExpr(PatternExpr first, PatternExpr second, params PatternExpr[] patternExprs) + { + AddChild(first); + AddChild(second); + for (int i = 0; i < patternExprs.Length; i++) + { + AddChild(patternExprs[i]); + } + } + + /// Adds a pattern expression to the OR relationship between patterns. + /// to add + /// pattern expression + public PatternOrExpr Add(PatternExpr expr) + { + Children.Add(expr); + return this; + } + + public override PatternExprPrecedenceEnum Precedence + { + get { return PatternExprPrecedenceEnum.OR; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer, EPStatementFormatter formatter) + { + String delimiter = ""; + foreach (PatternExpr child in Children) + { + writer.Write(delimiter); + child.ToEPL(writer, Precedence, formatter); + delimiter = " or "; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/PatternStream.cs b/NEsper.Core/NEsper.Core/client/soda/PatternStream.cs new file mode 100755 index 000000000..308635eff --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/PatternStream.cs @@ -0,0 +1,99 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// A stream of events that is generated by pattern matches. Patterns matches are events that match pattern expressions. Pattern expressions are built using . + /// + [Serializable] + public class PatternStream : ProjectedStream + { + /// Ctor. + public PatternStream() { + } + + /// Creates a pattern stream from a pattern expression. + /// pattern expression + /// stream + public static PatternStream Create(PatternExpr expression) + { + return new PatternStream(expression); + } + + /// Creates a named pattern stream from a pattern expression. + /// pattern expression + /// is the pattern stream name (as-name) + /// stream + public static PatternStream Create(PatternExpr expression, String optStreamName) + { + return new PatternStream(expression, optStreamName); + } + + /// Ctor. + /// pattern expression + public PatternStream(PatternExpr expression) + : this(expression, null) + { + + } + + /// Ctor. + /// pattern expression + /// is the pattern stream name (as-name) + public PatternStream(PatternExpr expression, String optStreamName) + : base(new List(), optStreamName) + { + Expression = expression; + } + + /// Ctor. + /// pattern expression + /// is the pattern stream name (as-name) + /// annotations on pattern-level, if any + public PatternStream(PatternExpr expression, String optStreamName, AnnotationPart[] annotations) + : base(new List(), optStreamName) + { + Expression = expression; + Annotations = annotations; + } + + /// Returns the pattern expression providing events to the stream. + /// pattern expression + public PatternExpr Expression { get; set; } + + /// Returns the pattern-level annotations, if any + /// pattern-level annotations + public AnnotationPart[] Annotations { get; set; } + + public override void ToEPLProjectedStream(TextWriter writer, EPStatementFormatter formatter) + { + writer.Write("pattern"); + if (Annotations != null) { + foreach (var part in Annotations) { + writer.Write(' '); + part.ToEPL(writer); + } + } + writer.Write(" ["); + if (Expression != null) { + Expression.ToEPL(writer, PatternExprPrecedenceEnum.MINIMUM, formatter); + } + writer.Write(']'); + } + + public override void ToEPLProjectedStreamType(TextWriter writer) + { + writer.Write("pattern"); + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/Patterns.cs b/NEsper.Core/NEsper.Core/client/soda/Patterns.cs new file mode 100755 index 000000000..4b61b33a8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/Patterns.cs @@ -0,0 +1,390 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using com.espertech.esper.pattern.guard; + +namespace com.espertech.esper.client.soda +{ + /// + /// Convenience factory for creating instances, which represent pattern expression trees. + /// + /// Provides quick-access method to create all possible pattern expressions and provides typical parameter lists to each. + /// + /// + /// Note that only the typical parameter lists are provided and pattern expressions can allow adding + /// additional parameters. + /// + /// + /// Many expressions, for example logical AND and OR (conjunction and disjunction), allow + /// adding an unlimited number of additional sub-expressions to a pattern expression. For those pattern expressions + /// there are additional add methods. + /// + /// + public class Patterns + { + /// + /// Pattern-every expression control the lifecycle of the pattern sub-expression. + /// + /// sub-expression to the every-keyword + /// pattern expression + public static PatternEveryExpr Every(PatternExpr inner) + { + return new PatternEveryExpr(inner); + } + + /// + /// Pattern-AND expression, allows adding sub-expressions that are connected by a logical AND. + /// + /// pattern expression representing the AND relationship + public static PatternAndExpr And() + { + return new PatternAndExpr(); + } + + /// + /// Pattern-AND expression, allows adding sub-expressions that are connected by a logical AND. + /// + /// is the first pattern sub-expression to add to the AND + /// is a second pattern sub-expression to add to the AND + /// + /// is optional additional pattern sub-expression to add to the AND + /// + /// pattern expression representing the AND relationship + public static PatternAndExpr And(PatternExpr first, PatternExpr second, params PatternExpr[] more) + { + return new PatternAndExpr(first, second, more); + } + + /// + /// Pattern-OR expression, allows adding sub-expressions that are connected by a logical OR. + /// + /// is the first pattern sub-expression to add to the OR + /// is a second pattern sub-expression to add to the OR + /// + /// is optional additional pattern sub-expression to add to the OR + /// + /// pattern expression representing the OR relationship + public static PatternOrExpr Or(PatternExpr first, PatternExpr second, params PatternExpr[] more) + { + return new PatternOrExpr(first, second, more); + } + + /// + /// Pattern-OR expression, allows adding sub-expressions that are connected by a logical OR. + /// + /// pattern expression representing the OR relationship + public static PatternOrExpr Or() + { + return new PatternOrExpr(); + } + + /// + /// Pattern followed-by expression, allows adding sub-expressions that are connected by a followed-by. + /// + /// + /// is the first pattern sub-expression to add to the followed-by + /// + /// + /// is a second pattern sub-expression to add to the followed-by + /// + /// + /// is optional additional pattern sub-expression to add to the followed-by + /// + /// pattern expression representing the followed-by relationship + public static PatternFollowedByExpr FollowedBy(PatternExpr first, PatternExpr second, params PatternExpr[] more) + { + return new PatternFollowedByExpr(first, second, more); + } + + /// + /// Pattern followed-by expression, allows adding sub-expressions that are connected by a followed-by. + /// + /// pattern expression representing the followed-by relationship + public static PatternFollowedByExpr FollowedBy() + { + return new PatternFollowedByExpr(); + } + + /// + /// Pattern every-operator and filter in combination, equivalent to the "every + /// MyEvent" syntax. + /// + /// is the event type name to filter for + /// + /// pattern expression + /// + public static PatternEveryExpr EveryFilter(String eventTypeName) + { + PatternExpr filter = new PatternFilterExpr(soda.Filter.Create(eventTypeName)); + return new PatternEveryExpr(filter); + } + + /// + /// Pattern every-operator and filter in combination, equivalent to the "every + /// tag=MyEvent" syntax. + /// + /// is the event type name to filter for + /// is the tag name to assign to matching events + /// + /// pattern expression + /// + public static PatternEveryExpr EveryFilter(String eventTypeName, String tagName) + { + PatternExpr filter = new PatternFilterExpr(soda.Filter.Create(eventTypeName), tagName); + return new PatternEveryExpr(filter); + } + + /// + /// Pattern every-operator and filter in combination, equivalent to the "every MyEvent(vol > 100)" syntax. + /// + /// + /// specifies the event type name and filter expression to filter for + /// + /// pattern expression + public static PatternEveryExpr EveryFilter(Filter filter) + { + PatternExpr inner = new PatternFilterExpr(filter); + return new PatternEveryExpr(inner); + } + + /// + /// Pattern every-operator and filter in combination, equivalent to the "every tag=MyEvent(vol > 100)" syntax. + /// + /// + /// specifies the event type name and filter expression to filter for + /// + /// is the tag name to assign to matching events + /// pattern expression + public static PatternEveryExpr EveryFilter(Filter filter, String tagName) + { + PatternExpr inner = new PatternFilterExpr(filter, tagName); + return new PatternEveryExpr(inner); + } + + /// + /// Filter expression for use in patterns, equivalent to the simple "MyEvent" + /// syntax. + /// + /// is the event type name of the events to filter for + /// + /// pattern expression + /// + public static PatternFilterExpr Filter(String eventTypeName) + { + return new PatternFilterExpr(soda.Filter.Create(eventTypeName)); + } + + /// + /// Filter expression for use in patterns, equivalent to the simple "tag=MyEvent" + /// syntax. + /// + /// is the event type name of the events to filter for + /// is the tag name to assign to matching events + /// + /// pattern expression + /// + public static PatternFilterExpr Filter(String eventTypeName, String tagName) + { + return new PatternFilterExpr(soda.Filter.Create(eventTypeName), tagName); + } + + /// + /// Filter expression for use in patterns, equivalent to the "MyEvent(vol > 100)" syntax. + /// + /// + /// specifies the event type name and filter expression to filter for + /// + /// pattern expression + public static PatternFilterExpr Filter(Filter filter) + { + return new PatternFilterExpr(filter); + } + + /// + /// Filter expression for use in patterns, equivalent to the "tag=MyEvent(vol > 100)" syntax. + /// + /// + /// specifies the event type name and filter expression to filter for + /// + /// is the tag name to assign to matching events + /// pattern expression + public static PatternFilterExpr Filter(Filter filter, String tagName) + { + return new PatternFilterExpr(filter, tagName); + } + + /// + /// Guard pattern expression guards a sub-expression, equivalent to the "every MyEvent where timer:within(1 sec)" syntax + /// + /// is the guard objects namespace, i.e. "timer" + /// is the guard objects name, i.e. ""within" + /// is the guard objects optional parameters, i.e. integer 1 for 1 second + /// is the pattern sub-expression to be guarded + /// pattern guard expression + public static PatternGuardExpr Guard(String @namespace, String name, Expression[] parameters, PatternExpr guarded) + { + return new PatternGuardExpr(@namespace, name, parameters, guarded); + } + + /// + /// Observer pattern expression, equivalent to the "every timer:interval(1 sec)" syntax + /// + /// is the observer objects namespace, i.e. "timer" + /// is the observer objects name, i.e. ""within" + /// is the observer objects optional parameters, i.e. integer 1 for 1 second + /// pattern observer expression + public static PatternObserverExpr Observer(String @namespace, String name, Expression[] parameters) + { + return new PatternObserverExpr(@namespace, name, parameters); + } + + /// Timer-within guard expression. + /// is the number of seconds for the guard + /// is the sub-expression to guard + /// pattern guard + public static PatternGuardExpr TimerWithin(double seconds, PatternExpr guarded) + { + return new PatternGuardExpr("timer", "within", new Expression[] { Expressions.Constant(seconds) }, guarded); + } + + /// While-guard expression. + /// expression to evaluate against matches + /// is the sub-expression to guard + /// pattern guard + public static PatternGuardExpr WhileGuard(PatternExpr guarded, Expression expression) + { + return new PatternGuardExpr( + GuardEnum.WHILE_GUARD.GetNamespace(), + GuardEnum.WHILE_GUARD.GetName(), + new[] { expression }, guarded); + } + + /// Timer-within-max guard expression. + /// is the number of seconds for the guard + /// the maximum number of invocations for the guard + /// is the sub-expression to guard + /// pattern guard + public static PatternGuardExpr TimerWithinMax(double seconds, int max, PatternExpr guarded) + { + return new PatternGuardExpr( + "timer", "withinmax", + new Expression[] { Expressions.Constant(seconds), Expressions.Constant(max) }, + guarded); + } + + /// Timer-interval observer expression. + /// is the number of seconds in the interval + /// pattern observer + public static PatternObserverExpr TimerInterval(double seconds) + { + return new PatternObserverExpr("timer", "interval", new Expression[] { Expressions.Constant(seconds) }); + } + + /// + /// Pattern not-operator and filter in combination, equivalent to the "not MyEvent" + /// syntax. + /// + /// is the event type name to filter for + /// + /// pattern expression + /// + public static PatternNotExpr NotFilter(String eventTypeName) + { + return new PatternNotExpr(new PatternFilterExpr(soda.Filter.Create(eventTypeName))); + } + + /// + /// Pattern not-operator and filter in combination, equivalent to the "not + /// tag=MyEvent" syntax. + /// + /// is the event type name to filter for + /// is the tag name to assign to matching events + /// + /// pattern expression + /// + public static PatternNotExpr NotFilter(String name, String tagName) + { + return new PatternNotExpr(new PatternFilterExpr(soda.Filter.Create(name), tagName)); + } + + /// + /// Pattern not-operator and filter in combination, equivalent to the "not MyEvent(vol > 100)" syntax. + /// + /// + /// specifies the event type name and filter expression to filter for + /// + /// pattern expression + public static PatternNotExpr NotFilter(Filter filter) + { + return new PatternNotExpr(new PatternFilterExpr(filter)); + } + + /// + /// Pattern not-operator and filter in combination, equivalent to the "not tag=MyEvent(vol > 100)" syntax. + /// + /// + /// specifies the event type name and filter expression to filter for + /// + /// is the tag name to assign to matching events + /// pattern expression + public static PatternNotExpr NotFilter(Filter filter, String tagName) + { + return new PatternNotExpr(new PatternFilterExpr(filter, tagName)); + } + + /// + /// Not-keyword pattern expression flips the truth-value of the pattern sub-expression. + /// + /// is the expression whose truth value to flip + /// pattern expression + public static PatternNotExpr Not(PatternExpr subexpression) + { + return new PatternNotExpr(subexpression); + } + + /// + /// Match-until-pattern expression matches a certain number of + /// occurances until a second expression becomes true. + /// + /// low number of matches, or null if no lower boundary + /// high number of matches, or null if no high boundary + /// the pattern expression that is sought to match repeatedly + /// the pattern expression that ends matching (optional, can be null) + /// pattern expression + public static PatternMatchUntilExpr MatchUntil(Expression low, Expression high, PatternExpr match, PatternExpr until) + { + return new PatternMatchUntilExpr(low, high, match, until); + } + + /// + /// Timer-at observer + /// + /// a single integer value supplying the minute to fire the timer, or null for any (wildcard) minute + /// a single integer value supplying the hour to fire the timer, or null for any (wildcard) hour + /// a single integer value supplying the day of the month to fire the timer, or null for any (wildcard) day of the month + /// a single integer value supplying the month to fire the timer, or null for any (wildcard) month + /// a single integer value supplying the days of the week to fire the timer, or null for any (wildcard) day of the week + /// a single integer value supplying the second to fire the timer, or null for any (wildcard) second + /// timer-at observer + public static PatternObserverExpr TimerAt(int? minutes, int? hours, int? daysOfMonth, int? month, int? daysOfWeek, int? seconds) + { + Expression wildcard = new CrontabParameterExpression(ScheduleItemType.WILDCARD); + + var paramList = new List(); + paramList.Add(minutes == null ? wildcard : Expressions.Constant(minutes)); + paramList.Add(hours == null ? wildcard : Expressions.Constant(hours)); + paramList.Add(daysOfMonth == null ? wildcard : Expressions.Constant(daysOfMonth)); + paramList.Add(month == null ? wildcard : Expressions.Constant(month)); + paramList.Add(daysOfWeek == null ? wildcard : Expressions.Constant(daysOfWeek)); + paramList.Add(seconds == null ? wildcard : Expressions.Constant(seconds)); + return new PatternObserverExpr("timer", "at", paramList); + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/client/soda/PlugInProjectionExpression.cs b/NEsper.Core/NEsper.Core/client/soda/PlugInProjectionExpression.cs new file mode 100755 index 000000000..a7f6a1872 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/PlugInProjectionExpression.cs @@ -0,0 +1,78 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.client.soda +{ + /// + /// Represents a plug-in aggregation function. + /// + [Serializable] + public class PlugInProjectionExpression : ExpressionBase + { + /// + /// Ctor. + /// + public PlugInProjectionExpression() { + } + + /// + /// Ctor. + /// + /// the name of the function + /// true for distinct + public PlugInProjectionExpression(string functionName, bool isDistinct) + { + FunctionName = functionName; + IsDistinct = isDistinct; + } + + /// + /// Ctor. + /// + /// the name of the function + /// true for distinct + /// provides aggregated values + public PlugInProjectionExpression(string functionName, bool isDistinct, params Expression[] moreExpressions) + { + FunctionName = functionName; + IsDistinct = isDistinct; + for (int i = 0; i < moreExpressions.Length; i++) + { + Children.Add(moreExpressions[i]); + } + } + + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.UNARY; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + RenderAggregation(writer, FunctionName, IsDistinct, Children); + } + + /// + /// Returns the function name. + /// + /// name of function + public string FunctionName { get; set; } + + /// + /// Returns true for distinct. + /// + /// boolean indicating distinct or not + public bool IsDistinct { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/PreviousExpression.cs b/NEsper.Core/NEsper.Core/client/soda/PreviousExpression.cs new file mode 100755 index 000000000..18fccadb5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/PreviousExpression.cs @@ -0,0 +1,85 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Previous function for obtaining property values of previous events. + /// + [Serializable] + public class PreviousExpression : ExpressionBase + { + private PreviousExpressionType _expressionType = + PreviousExpressionType.PREV; + + /// Ctor. + public PreviousExpression() + { + } + + /// Ctor. + /// provides the index to use + /// + /// is the name of the property to return the value for + /// + public PreviousExpression(Expression expression, String propertyName) + { + AddChild(expression); + AddChild(new PropertyValueExpression(propertyName)); + } + + /// Ctor. + /// provides the index + /// + /// is the name of the property to return the value for + /// + public PreviousExpression(int index, String propertyName) + { + AddChild(new ConstantExpression(index)); + AddChild(new PropertyValueExpression(propertyName)); + } + + /// + /// Ctor. + /// + /// Type of the expression. + /// to evaluate + public PreviousExpression(PreviousExpressionType expressionType, Expression expression) + { + _expressionType = expressionType; + AddChild(expression); + } + + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.UNARY; } + } + + public PreviousExpressionType ExpressionType + { + get { return _expressionType; } + set { _expressionType = value; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + writer.Write(_expressionType.ToString().ToLower()); + writer.Write("("); + Children[0].ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + if (Children.Count > 1) + { + writer.Write(","); + Children[1].ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + } + writer.Write(')'); + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/client/soda/PreviousExpressionType.cs b/NEsper.Core/NEsper.Core/client/soda/PreviousExpressionType.cs new file mode 100755 index 000000000..679392169 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/PreviousExpressionType.cs @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.client.soda +{ + /// + /// Previous function type. + /// + public enum PreviousExpressionType + { + /// + /// Returns a previous event with the index counting from the last event + /// towards the first event. + /// + PREV, + + /// + /// Returns the count of previous events. + /// + PREVCOUNT, + + /// + /// Returns a previous event with the index counting from the first event + /// towards the last event. + /// + PREVTAIL, + + /// + /// Returns all previous events. + /// + PREVWINDOW + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/PriorExpression.cs b/NEsper.Core/NEsper.Core/client/soda/PriorExpression.cs new file mode 100755 index 000000000..94ef190fa --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/PriorExpression.cs @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Expression representing the prior function. + /// + [Serializable] + public class PriorExpression : ExpressionBase + { + /// + /// Ctor - for use to create an expression tree, without child expression. + /// + public PriorExpression() + { + } + + /// Ctor. + /// is the index of the prior event + /// is the property to return + public PriorExpression(int index, String propertyName) + { + AddChild(new ConstantExpression(index)); + AddChild(new PropertyValueExpression(propertyName)); + } + + + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.UNARY; } + } + + /// Renders the clause in textual representation. + /// to output to + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + writer.Write("prior("); + Children[0].ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + writer.Write(","); + Children[1].ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + writer.Write(')'); + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/client/soda/ProjectedStream.cs b/NEsper.Core/NEsper.Core/client/soda/ProjectedStream.cs new file mode 100755 index 000000000..44ee9d02d --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/ProjectedStream.cs @@ -0,0 +1,234 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace com.espertech.esper.client.soda +{ + /// + /// Abstract base class for streams that can be projected via views providing data window, uniqueness or other projections + /// or deriving further information from streams. + /// + [Serializable] + public abstract class ProjectedStream : Stream + { + private IList _views; + + /// Ctor. + public ProjectedStream() + { + } + + /// + /// Ctor. + /// + /// is a list of views upon the stream + /// is the stream as-name, or null if unnamed + protected ProjectedStream(IList views, string optStreamName) + : base(optStreamName) + { + _views = views; + } + + /// + /// Renders the views onto the projected stream. + /// + /// to render to + /// to render + internal static void ToEPLViews(TextWriter writer, IList views) + { + if ((views != null) && (views.Count != 0)) + { + if (views.First().Namespace == null) + { + writer.Write('#'); + string delimiter = ""; + foreach (View view in views) + { + writer.Write(delimiter); + view.ToEPLWithHash(writer); + delimiter = "#"; + } + } + else + { + writer.Write('.'); + string delimiter = ""; + foreach (View view in views) + { + writer.Write(delimiter); + view.ToEPL(writer); + delimiter = "."; + } + } + } + } + + /// + /// Represent as textual. + /// + /// to output to + /// for newline-whitespace formatting + public abstract void ToEPLProjectedStream(TextWriter writer, EPStatementFormatter formatter); + + /// + /// Represent type as textual non complete. + /// + /// to output to + public abstract void ToEPLProjectedStreamType(TextWriter writer); + + /// + /// Adds an un-parameterized view to the stream. + /// + /// is the view namespace, for example "win" for most data windows + /// is the view name, for example "length" for a length window + /// stream + public ProjectedStream AddView(string @namespace, string name) + { + _views.Add(View.Create(@namespace, name)); + return this; + } + + /// + /// Adds a parameterized view to the stream. + /// + /// is the view namespace, for example "win" for most data windows + /// is the view name, for example "length" for a length window + /// is a list of view parameters + /// stream + public ProjectedStream AddView(string @namespace, string name, List parameters) + { + _views.Add(View.Create(@namespace, name, parameters)); + return this; + } + + /// + /// Adds a parameterized view to the stream. + /// + /// is the view namespace, for example "win" for most data windows + /// is the view name, for example "length" for a length window + /// is a list of view parameters + /// stream + public ProjectedStream AddView(string @namespace, string name, params Expression[] parameters) + { + _views.Add(View.Create(@namespace, name, parameters)); + return this; + } + + /// + /// Adds a parameterized view to the stream. + /// + /// is the view name, for example "length" for a length window + /// is a list of view parameters + /// stream + public ProjectedStream AddView(string name, params Expression[] parameters) + { + _views.Add(View.Create(null, name, parameters)); + return this; + } + + /// + /// Add a view to the stream. + /// + /// to add + /// stream + public ProjectedStream AddView(View view) + { + _views.Add(view); + return this; + } + + /// + /// Returns the list of views added to the stream. + /// + /// list of views + public IList Views + { + get { return _views; } + set { this._views = value; } + } + + /// + /// Renders the clause in textual representation. + /// + /// to output to + public override void ToEPLStream(TextWriter writer, EPStatementFormatter formatter) + { + ToEPLProjectedStream(writer, formatter); + ToEPLViews(writer, _views); + } + + public override void ToEPLStreamType(TextWriter writer) + { + ToEPLProjectedStreamType(writer); + + if ((_views != null) && (_views.Count != 0)) + { + writer.Write('.'); + string delimiter = ""; + foreach (View view in _views) + { + writer.Write(delimiter); + writer.Write(view.Namespace); + writer.Write("."); + writer.Write(view.Name); + writer.Write("()"); + delimiter = "."; + } + } + } + + /// + /// Returns true if the stream as unidirectional, for use in unidirectional joins. + /// + /// true for unidirectional stream, applicable only for joins + public bool IsUnidirectional { get; set; } + + /// + /// Set to unidirectional. + /// + /// try if unidirectional + /// stream + public ProjectedStream Unidirectional(bool isUnidirectional) + { + this.IsUnidirectional = isUnidirectional; + return this; + } + + /// + /// Returns true if multiple data window shall be treated as a union. + /// + /// retain union + public bool IsRetainUnion { get; set; } + + /// + /// Returns true if multiple data window shall be treated as an intersection. + /// + /// retain intersection + public bool IsRetainIntersection { get; set; } + + public override void ToEPLStreamOptions(TextWriter writer) + { + if (IsUnidirectional) + { + writer.Write(" unidirectional"); + } + else if (IsRetainUnion) + { + writer.Write(" retain-union"); + } + else if (IsRetainIntersection) + { + writer.Write(" retain-intersection"); + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/client/soda/PropertyExistsExpression.cs b/NEsper.Core/NEsper.Core/client/soda/PropertyExistsExpression.cs new file mode 100755 index 000000000..85e617a5e --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/PropertyExistsExpression.cs @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Property-exists checks if a dynamic property exists. + /// + [Serializable] + public class PropertyExistsExpression : ExpressionBase + { + /// + /// Ctor - for use to create an expression tree, without child expression. + /// + public PropertyExistsExpression() + { + } + + /// Ctor. + /// is the name of the property to check existence + public PropertyExistsExpression(String propertyName) + { + Children.Add(Expressions.GetPropExpr(propertyName)); + } + + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.UNARY; } + } + + /// Renders the clause in textual representation. + /// to output to + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + writer.Write("exists("); + Children[0].ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + writer.Write(")"); + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/client/soda/PropertyValueExpression.cs b/NEsper.Core/NEsper.Core/client/soda/PropertyValueExpression.cs new file mode 100755 index 000000000..e4615adca --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/PropertyValueExpression.cs @@ -0,0 +1,57 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Expression returning a property value. + /// + [Serializable] + public class PropertyValueExpression : ExpressionBase + { + /// + /// Initializes a new instance of the class. + /// + public PropertyValueExpression() + { + } + + /// Ctor. + /// is the name of the property + public PropertyValueExpression(String propertyName) + { + PropertyName = propertyName.Trim(); + } + + /// Gets or sets the property name. + /// name of the property + public string PropertyName { get; set; } + + /// + /// Gets the Precedence. + /// + /// The Precedence. + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.UNARY; } + } + + /// + /// Renders the expressions and all it's child expression, in full tree depth, as a string in + /// language syntax. + /// + /// is the output to use + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + writer.Write(PropertyName); + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/client/soda/PropertyValueExpressionPair.cs b/NEsper.Core/NEsper.Core/client/soda/PropertyValueExpressionPair.cs new file mode 100755 index 000000000..0d23f094b --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/PropertyValueExpressionPair.cs @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.soda +{ + /// + /// Pair of expressions with "equals" operator between. + /// + + [Serializable] + public class PropertyValueExpressionPair + { + /// Ctor. + public PropertyValueExpressionPair() { + } + + /// Ctor. + /// expression + /// expression + public PropertyValueExpressionPair(PropertyValueExpression left, PropertyValueExpression right) { + Left = left; + Right = right; + } + + /// Returns left expr. + /// left + public Expression Left { get; set; } + + /// Returns right side. + /// right side + public Expression Right { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/RegExpExpression.cs b/NEsper.Core/NEsper.Core/client/soda/RegExpExpression.cs new file mode 100755 index 000000000..fd6a3e60f --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/RegExpExpression.cs @@ -0,0 +1,122 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Regular expression evaluates a "regexp" regular expression. + /// + [Serializable] + public class RegExpExpression : ExpressionBase + { + /// + /// Ctor - for use to create an expression tree, without child expression. + /// + /// true for negated regex + public RegExpExpression(bool isNot) + { + IsNot = isNot; + } + + /// + /// Ctor. + /// + /// provides values to match against regexp string + /// provides the regexp string + /// true for negated regex + public RegExpExpression(Expression left, Expression right, bool isNot) + : this(left, right, null, isNot) + { + } + + /// + /// Ctor. + /// + /// provides values to match against regexp string + /// provides the regexp string + /// provides the escape character + /// true for negated regex + public RegExpExpression(Expression left, Expression right, Expression escape, bool isNot) + { + Children.Add(left); + Children.Add(right); + if (escape != null) { + Children.Add(escape); + } + + IsNot = isNot; + } + + /// + /// Ctor - for use to create an expression tree, without child expression. + /// + public RegExpExpression() + { + IsNot = false; + } + + /// + /// Ctor. + /// + /// provides values to match against regexp string + /// provides the regexp string + public RegExpExpression(Expression left, Expression right) + : this(left, right, null) + { + } + + /// + /// Ctor. + /// + /// provides values to match against regexp string + /// provides the regexp string + /// provides the escape character + public RegExpExpression(Expression left, Expression right, Expression escape) + { + Children.Add(left); + Children.Add(right); + if (escape != null) { + Children.Add(escape); + } + IsNot = false; + } + + /// + /// Returns true if negated. + /// + /// + /// indicator whether negated + /// + public bool IsNot { get; private set; } + + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.RELATIONAL_BETWEEN_IN; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + Children[0].ToEPL(writer, Precedence); + if (IsNot) + { + writer.Write(" not"); + } + writer.Write(" regexp "); + Children[1].ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + + if (Children.Count > 2) + { + writer.Write(" escape "); + Children[2].ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/RelationalOpExpression.cs b/NEsper.Core/NEsper.Core/client/soda/RelationalOpExpression.cs new file mode 100755 index 000000000..56de15ff2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/RelationalOpExpression.cs @@ -0,0 +1,87 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Comparison using one of the relational operators (=, !=, <, <=, >, >=). + /// + [Serializable] + public class RelationalOpExpression : ExpressionBase + { + /// + /// Initializes a new instance of the class. + /// + public RelationalOpExpression() + { + } + + /// Ctor. + /// is the relational operator. + public RelationalOpExpression(String @operator) + { + this.Operator = @operator.Trim(); + } + + /// Ctor. + /// provides a value to compare against + /// is the operator to use + /// provides a value to compare against + public RelationalOpExpression(Expression left, String @operator, Expression right) + { + Operator = @operator.Trim(); + AddChild(left); + + if (right != null) + { + AddChild(right); + } + else + { + AddChild(new ConstantExpression(null)); + } + } + + /// Gets or sets the operator to use. + /// operator. + public string Operator { get; set; } + + public override ExpressionPrecedenceEnum Precedence + { + get + { + if (Operator == "=") + { + return ExpressionPrecedenceEnum.EQUALS; + } + return ExpressionPrecedenceEnum.RELATIONAL_BETWEEN_IN; + } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + Children[0].ToEPL(writer, Precedence); + if (Operator.ToLower().Trim().Equals("is") || + Operator.ToLower().Trim().Equals("is not")) + { + writer.Write(' '); + writer.Write(Operator); + writer.Write(' '); + } + else + { + writer.Write(Operator); + } + + Children[1].ToEPL(writer, Precedence); + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/client/soda/RowLimitClause.cs b/NEsper.Core/NEsper.Core/client/soda/RowLimitClause.cs new file mode 100755 index 000000000..8421fb2ad --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/RowLimitClause.cs @@ -0,0 +1,164 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Specification object for a row limit. + /// + [Serializable] + public class RowLimitClause + { + /// + /// Initializes a new instance of the class. + /// + public RowLimitClause() + { + } + + /// + /// Ctor. + /// + /// maximum number of rows + /// offset + /// name of the variable providing the maximum number of rows + /// name of the variable providing the offset + public RowLimitClause(int? numRows, + int? optionalOffsetRows, + string numRowsVariable, + string optionalOffsetRowsVariable) + { + NumRows = numRows; + OptionalOffsetRows = optionalOffsetRows; + NumRowsVariable = numRowsVariable; + OptionalOffsetRowsVariable = optionalOffsetRowsVariable; + } + + /// + /// Returns the maximum number of rows, or null if using variable. + /// + /// + /// max num rows + /// + public int? NumRows { get; set; } + + /// + /// Returns the offset, or null if using variable or not using offset. + /// + /// + /// offset + /// + public int? OptionalOffsetRows { get; set; } + + /// + /// Returns the variable providing maximum number of rows, or null if using + /// constant. + /// + /// + /// max num rows variable + /// + public string NumRowsVariable { get; set; } + + /// + /// Returns the name of the variable providing offset values. + /// + /// + /// variable name for offset + /// + public string OptionalOffsetRowsVariable { get; set; } + + /// + /// Creates a row limit clause. + /// + /// name of the variable providing the maximum number of rows + /// + /// clause + /// + public static RowLimitClause Create(String numRowsVariable) + { + return new RowLimitClause(null, null, numRowsVariable, null); + } + + /// + /// Creates a row limit clause. + /// + /// name of the variable providing the maximum number of rows + /// name of the variable providing the offset + /// + /// clause + /// + public static RowLimitClause Create(String numRowsVariable, String offsetVariable) + { + return new RowLimitClause(null, null, numRowsVariable, offsetVariable); + } + + /// + /// Creates a row limit clause. + /// + /// maximum number of rows + /// + /// clause + /// + public static RowLimitClause Create(int numRows) + { + return new RowLimitClause(numRows, null, null, null); + } + + /// + /// Creates a row limit clause. + /// + /// maximum number of rows + /// offset + /// + /// clause + /// + public static RowLimitClause Create(int numRows, int offset) + { + return new RowLimitClause(numRows, offset, null, null); + } + + /// + /// Renders the clause in textual representation. + /// + /// to output to + public void ToEPL(TextWriter writer) + { + var numRowsVariable = NumRowsVariable; + if (numRowsVariable != null) + { + writer.Write(numRowsVariable); + } + else + { + var numRows = NumRows; + if (numRows != null) + { + writer.Write(numRows); + } + else + { + writer.Write(Int32.MaxValue); + } + } + + if (OptionalOffsetRowsVariable != null) + { + writer.Write(" offset "); + writer.Write(OptionalOffsetRowsVariable); + } + else if (OptionalOffsetRows.GetValueOrDefault(0) != 0) + { + writer.Write(" offset "); + writer.Write(OptionalOffsetRows); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/SQLStream.cs b/NEsper.Core/NEsper.Core/client/soda/SQLStream.cs new file mode 100755 index 000000000..e03203484 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/SQLStream.cs @@ -0,0 +1,117 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// An SQL stream that polls via SQL for events via join. + /// + [Serializable] + public class SQLStream : Stream + { + private String _databaseName; + private String _sqlWithSubsParams; + private String _optionalMetadataSQL; + + /// Ctor. + public SQLStream() { + } + + /// Creates a new SQL-based stream. + /// is the database name to poll + /// is the SQL to use + /// stream + public static SQLStream Create(String databaseName, String sqlWithSubsParams) + { + return new SQLStream(databaseName, sqlWithSubsParams, null, null); + } + + /// Creates a new SQL-based stream. + /// is the database name to poll + /// is the SQL to use + /// is the as-name of the stream + /// stream + public static SQLStream Create(String databaseName, String sqlWithSubsParams, String optStreamName) + { + return new SQLStream(databaseName, sqlWithSubsParams, optStreamName, null); + } + + /// Creates a new SQL-based stream. + /// is the database name to poll + /// is the SQL to use + /// is the as-name of the stream + /// optional SQL delivering metadata of statement + /// stream + public static SQLStream Create(String databaseName, String sqlWithSubsParams, String optStreamName, String optionalMetadataSQL) + { + return new SQLStream(databaseName, sqlWithSubsParams, optStreamName, optionalMetadataSQL); + } + + /// Ctor. + /// is the database name to poll + /// is the SQL to use + /// is the optional as-name of the stream, or null if unnamed + /// optional SQL delivering metadata of statement + public SQLStream(String databaseName, String sqlWithSubsParams, String optStreamName, String optionalMetadataSQL) + + : base(optStreamName) + { + _databaseName = databaseName; + _sqlWithSubsParams = sqlWithSubsParams; + _optionalMetadataSQL = optionalMetadataSQL; + } + + /// Returns the database name. + /// database name + public string DatabaseName + { + get { return _databaseName; } + set { this._databaseName = value; } + } + + /// Returns the SQL with optional substitution parameters in the SQL. + /// SQL + public string SqlWithSubsParams + { + get { return _sqlWithSubsParams; } + set { this._sqlWithSubsParams = value; } + } + + + /// Returns the metadata SQL if any. + /// metadata SQL + public string OptionalMetadataSQL + { + get { return _optionalMetadataSQL; } + set { this._optionalMetadataSQL = value; } + } + + public override void ToEPLStream(TextWriter writer, EPStatementFormatter formatter) + { + writer.Write("sql:"); + writer.Write(_databaseName); + writer.Write("[\""); + writer.Write(_sqlWithSubsParams); + writer.Write("\"]"); + } + + public override void ToEPLStreamType(TextWriter writer) + { + writer.Write("sql:"); + writer.Write(_databaseName); + writer.Write("[..]"); + } + + public override void ToEPLStreamOptions(TextWriter writer) + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/ScheduleItemType.cs b/NEsper.Core/NEsper.Core/client/soda/ScheduleItemType.cs new file mode 100755 index 000000000..c0b45252f --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/ScheduleItemType.cs @@ -0,0 +1,65 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.soda +{ + /// + /// Type of schedule item. + /// + [Serializable] + public enum ScheduleItemType + { + /// + /// Wildcard means any value. + /// + WILDCARD, + + /// + /// Last day of week or month. + /// + LASTDAY, + + /// + /// Weekday (nearest to a date) + /// + WEEKDAY, + + /// + /// Last weekday in a month + /// + LASTWEEKDAY + } + + public static class ScheduleItemTypeExtensions + { + /// + /// Returns the syntax string. + /// + /// + /// syntax + /// + public static string GetSyntax(this ScheduleItemType value) + { + switch(value) + { + case ScheduleItemType.WILDCARD: + return ("*"); + case ScheduleItemType.LASTDAY: + return ("last"); + case ScheduleItemType.WEEKDAY: + return ("weekday"); + case ScheduleItemType.LASTWEEKDAY: + return ("lastweekday"); + } + + throw new ArgumentException(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/SchemaColumnDesc.cs b/NEsper.Core/NEsper.Core/client/soda/SchemaColumnDesc.cs new file mode 100755 index 000000000..a166406b5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/SchemaColumnDesc.cs @@ -0,0 +1,102 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.client.soda +{ + /// + /// Descriptor for use in create-schema syntax to define property name and type of an event property. + /// + [Serializable] + public class SchemaColumnDesc + { + /// + /// Ctor. + /// + public SchemaColumnDesc() + { + } + + /// + /// Ctor. + /// + /// column name + /// type name + /// array flag + public SchemaColumnDesc(string name, string type, bool array) + { + Name = name; + Type = type; + IsArray = array; + } + + /// + /// Ctor. + /// + /// property name + /// property type, can be any simple class name or fully-qualified class name or existing event type name + /// true for array property + /// true for array of primitive (requires array property to be set and a primitive type) + public SchemaColumnDesc(string name, string type, bool array, bool primitiveArray) + { + Name = name; + Type = type; + IsArray = array; + IsPrimitiveArray = primitiveArray; + } + + /// + /// Returns property name. + /// + /// name + public string Name { get; set; } + + /// + /// Returns property type. + /// + /// type + public string Type { get; set; } + + /// + /// Returns true for array properties. + /// + /// indicator + public bool IsArray { get; set; } + + /// + /// Returns indicator whether array of primitives (requires array and a primitive type) + /// + /// indicator + public bool IsPrimitiveArray { get; set; } + + /// + /// RenderAny to EPL. + /// + /// to render to + public void ToEPL(TextWriter writer) + { + writer.Write(Name); + writer.Write(' '); + writer.Write(Type); + if (IsArray) { + if (IsPrimitiveArray) { + writer.Write("[primitive]"); + } + else { + writer.Write("[]"); + } + } + } + + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/ScriptExpression.cs b/NEsper.Core/NEsper.Core/client/soda/ScriptExpression.cs new file mode 100755 index 000000000..bb65a6ff6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/ScriptExpression.cs @@ -0,0 +1,203 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.client.soda +{ + /// + /// Script-expression is external scripting language expression such as JavaScript, Groovy or MVEL, for example. + /// + [Serializable] + public class ScriptExpression + { + private string _name; + private IList _parameterNames; + private string _expressionText; + private string _optionalReturnType; + private string _optionalEventTypeName; + private string _optionalDialect; + + /// Ctor. + public ScriptExpression() + { + } + + /// + /// Ctor. + /// + /// script name + /// parameter list + /// script text + /// return type + /// dialect + /// optional event type name + public ScriptExpression( + string name, + IList parameterNames, + string expressionText, + string optionalReturnType, + string optionalDialect, + string optionalEventTypeName) + { + _name = name; + _parameterNames = parameterNames; + _expressionText = expressionText; + _optionalReturnType = optionalReturnType; + _optionalDialect = optionalDialect; + _optionalEventTypeName = optionalEventTypeName; + } + + /// + /// Ctor. + /// + /// script name + /// parameter list + /// script text + /// return type + /// dialect + public ScriptExpression( + string name, + IList parameterNames, + string expressionText, + string optionalReturnType, + string optionalDialect) + : this(name, parameterNames, expressionText, optionalReturnType, optionalDialect, null) + { + } + + /// + /// Print. + /// + /// to print to + /// scripts + /// for newline-whitespace formatting + public static void ToEPL(TextWriter writer, IList scripts, EPStatementFormatter formatter) + { + if ((scripts == null) || (scripts.IsEmpty())) + { + return; + } + + foreach (ScriptExpression part in scripts) + { + if (part.Name == null) + { + continue; + } + formatter.BeginExpressionDecl(writer); + part.ToEPL(writer); + } + } + + /// + /// Returns the script name. + /// + /// script name + public string Name + { + get { return _name; } + set { _name = value; } + } + + /// + /// Returns the return type, if any is specified. + /// + /// return type + public string OptionalReturnType + { + get { return _optionalReturnType; } + set { _optionalReturnType = value; } + } + + /// + /// Returns a dialect name, or null if none is defined and the configured default applies + /// + /// dialect name + public string OptionalDialect + { + get { return _optionalDialect; } + set { _optionalDialect = value; } + } + + /// + /// Returns the script body. + /// + /// script body + public string ExpressionText + { + get { return _expressionText; } + set { _expressionText = value; } + } + + /// + /// Returns the lambda expression parameters. + /// + /// lambda expression parameters + public IList ParameterNames + { + get { return _parameterNames; } + set { _parameterNames = value; } + } + + /// + /// Returns the optional event type name. + /// + /// type name + public string OptionalEventTypeName + { + get { return _optionalEventTypeName; } + set { _optionalEventTypeName = value; } + } + + /// + /// Print part. + /// + /// to write to + public void ToEPL(TextWriter writer) + { + writer.Write("expression "); + if (_optionalReturnType != null) + { + writer.Write(_optionalReturnType); + writer.Write(" "); + } + if (_optionalEventTypeName != null) + { + writer.Write("@type("); + writer.Write(_optionalEventTypeName); + writer.Write(") "); + } + if (!string.IsNullOrWhiteSpace(_optionalDialect)) + { + writer.Write(_optionalDialect); + writer.Write(":"); + } + writer.Write(_name); + writer.Write("("); + if (_parameterNames != null && !_parameterNames.IsEmpty()) + { + string delimiter = ""; + foreach (string name in _parameterNames) + { + writer.Write(delimiter); + writer.Write(name); + delimiter = ","; + } + } + writer.Write(")"); + writer.Write(" ["); + writer.Write(_expressionText); + writer.Write("]"); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/client/soda/SelectClause.cs b/NEsper.Core/NEsper.Core/client/soda/SelectClause.cs new file mode 100755 index 000000000..e1bec41ae --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/SelectClause.cs @@ -0,0 +1,294 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.client.soda +{ + /// + /// A select-clause consists of a list of selection elements (expressions, Wildcard(s), stream wildcard + /// and the like) and an optional stream selector. + /// + [Serializable] + public class SelectClause + { + private IList _selectList; + private StreamSelector _streamSelector; + + /// Ctor. + public SelectClause() + { + } + + /// Ctor. + /// selects the stream + /// is a list of elements in the select-clause + protected SelectClause(StreamSelector streamSelector, + List selectList) + { + _streamSelector = streamSelector; + _selectList = selectList; + } + + /// Creates a wildcard select-clause, additional expressions can still be added. + /// select-clause + public static SelectClause CreateWildcard() + { + var selectList = new List(); + selectList.Add(new SelectClauseWildcard()); + return new SelectClause(soda.StreamSelector.ISTREAM_ONLY, selectList); + } + + /// Creates an empty select-clause to be added to via add methods. + /// select-clause + public static SelectClause Create() + { + return new SelectClause(soda.StreamSelector.ISTREAM_ONLY, new List()); + } + + /// Creates a select-clause consisting of a list of property names. + /// is the names of properties to select + /// select-clause + public static SelectClause Create(params String[] propertyNames) + { + var selectList = new List(); + foreach (String name in propertyNames) + { + selectList.Add(new SelectClauseExpression(new PropertyValueExpression(name))); + } + return new SelectClause(soda.StreamSelector.ISTREAM_ONLY, selectList); + } + + /// Creates a select-clause with a single stream wildcard selector (e.g. select streamName.* from MyStream as streamName) + /// is the name given to a stream + /// select-clause + public static SelectClause CreateStreamWildcard(String streamName) + { + var selectList = new List(); + selectList.Add(new SelectClauseStreamWildcard(streamName, null)); + return new SelectClause(soda.StreamSelector.ISTREAM_ONLY, selectList); + } + + /// Creates a wildcard select-clause, additional expressions can still be added. + /// can be used to select insert or remove streams + /// select-clause + public static SelectClause CreateWildcard(StreamSelector streamSelector) + { + var selectList = new List(); + selectList.Add(new SelectClauseWildcard()); + return new SelectClause(streamSelector, selectList); + } + + /// Creates an empty select-clause. + /// can be used to select insert or remove streams + /// select-clause + public static SelectClause Create(StreamSelector streamSelector) + { + return new SelectClause(streamSelector, new List()); + } + + /// Creates a select-clause consisting of a list of property names. + /// is the names of properties to select + /// can be used to select insert or remove streams + /// select-clause + public static SelectClause Create(StreamSelector streamSelector, + params String[] propertyNames) + { + var selectList = new List(); + foreach (String name in propertyNames) + { + selectList.Add(new SelectClauseExpression(new PropertyValueExpression(name))); + } + return new SelectClause(streamSelector, selectList); + } + + /// Adds property names to be selected. + /// is a list of property names to add + /// clause + public SelectClause Add(params String[] propertyNames) + { + foreach (String name in propertyNames) + { + _selectList.Add(new SelectClauseExpression(new PropertyValueExpression(name))); + } + return this; + } + + /// Adds a single property name and an "as"-asName for the column. + /// name of property + /// is the "as"-asName for the column + /// clause + public SelectClause AddWithAsProvidedName(String propertyName, + String asName) + { + _selectList.Add(new SelectClauseExpression(new PropertyValueExpression(propertyName), asName)); + return this; + } + + /// Adds an expression to the select clause. + /// to add + /// clause + public SelectClause Add(Expression expression) + { + _selectList.Add(new SelectClauseExpression(expression)); + return this; + } + + /// Adds an expression to the select clause and an "as"-asName for the column. + /// to add + /// is the "as"-provided for the column + /// clause + public SelectClause Add(Expression expression, + String asName) + { + _selectList.Add(new SelectClauseExpression(expression, asName)); + return this; + } + + /// Returns the list of expressions in the select clause. + /// list of expressions with column names + public IList SelectList + { + get { return _selectList; } + set { _selectList = value; } + } + + /// Adds to the select-clause a stream wildcard selector (e.g. select streamName.* from MyStream as streamName) + /// is the name given to a stream + /// select-clause + public SelectClause AddStreamWildcard(String streamName) + { + _selectList.Add(new SelectClauseStreamWildcard(streamName, null)); + return this; + } + + /// Adds to the select-clause a wildcard selector (e.g. select * from MyStream as streamName) + /// select-clause + public SelectClause AddWildcard() + { + _selectList.Add(new SelectClauseWildcard()); + return this; + } + + /// Adds to the select-clause a stream wildcard selector with column name (e.g. select streamName.* as colName from MyStream as streamName) + /// is the name given to a stream + /// the name given to the column + /// select-clause + public SelectClause AddStreamWildcard(String streamName, + String columnName) + { + _selectList.Add(new SelectClauseStreamWildcard(streamName, columnName)); + return this; + } + + /// Returns the stream selector. + /// stream selector + public StreamSelector GetStreamSelector() + { + return _streamSelector; + } + + /// Sets the stream selector. + /// stream selector to set + public SelectClause SetStreamSelector(StreamSelector streamSelector) + { + _streamSelector = streamSelector; + return this; + } + + /// + /// Gets or sets the stream selector. + /// + /// The stream selector. + public StreamSelector StreamSelector + { + get { return _streamSelector; } + set { _streamSelector = value; } + } + + + /// Add a select expression element. + /// to add + public void AddElements(IEnumerable selectClauseElements) + { + _selectList.AddAll(selectClauseElements); + } + + /// + /// Renders the clause in textual representation. + /// + /// to output to + /// for NewLine-whitespace formatting + /// to indicate if this select-clause is inside other clauses. + /// indicator whether select and delete. + public void ToEPL(TextWriter writer, EPStatementFormatter formatter, Boolean isTopLevel, Boolean andDelete) + { + formatter.BeginSelect(writer, isTopLevel); + writer.Write("select "); + if (andDelete) + { + writer.Write("and delete "); + } + if (IsDistinct) + { + writer.Write("distinct "); + } + if (_streamSelector == soda.StreamSelector.ISTREAM_ONLY) + { + // the default, no action + } + else if (_streamSelector == soda.StreamSelector.RSTREAM_ONLY) + { + writer.Write("rstream "); + } + else if (_streamSelector == soda.StreamSelector.RSTREAM_ISTREAM_BOTH) + { + writer.Write("irstream "); + } + + if (_selectList != null && !_selectList.IsEmpty()) + { + String delimiter = ""; + foreach (SelectClauseElement element in _selectList) + { + writer.Write(delimiter); + element.ToEPLElement(writer); + delimiter = ", "; + } + } + else + { + writer.Write('*'); + } + } + + /// Returns indicator whether distinct or not. + /// distinct indicator + public bool IsDistinct { get; set; } + + /// Sets distinct + /// distinct indicator + /// the select clause + public SelectClause Distinct(bool distinct) + { + IsDistinct = distinct; + return this; + } + + /// Sets distinct to true. + /// the select clause + public SelectClause Distinct() + { + IsDistinct = true; + return this; + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/soda/SelectClauseElement.cs b/NEsper.Core/NEsper.Core/client/soda/SelectClauseElement.cs new file mode 100755 index 000000000..80ba65fff --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/SelectClauseElement.cs @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Item in a select-clause to describe individual select-clause expressions or Wildcard(s). + /// + public interface SelectClauseElement + { + /// Output the string rendering of the select clause element. + /// to output to + void ToEPLElement(TextWriter writer); + } + + public static class SelectClauseElementExtension + { + /// + /// Converts the element to a string. + /// + /// The select clause element. + /// + public static string ToEPL(this SelectClauseElement selectClauseElement) + { + using(var textWriter = new StringWriter()) { + selectClauseElement.ToEPLElement(textWriter); + return textWriter.ToString(); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/SelectClauseExpression.cs b/NEsper.Core/NEsper.Core/client/soda/SelectClauseExpression.cs new file mode 100755 index 000000000..f06f26e4e --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/SelectClauseExpression.cs @@ -0,0 +1,68 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Part of a select-clause to describe individual select-clause expressions. + /// + [Serializable] + public class SelectClauseExpression : SelectClauseElement + { + /// Ctor. + public SelectClauseExpression() + { + } + + /// Ctor. + /// is the selection expression + public SelectClauseExpression(Expression expression) + { + Expression = expression; + } + + /// Ctor. + /// is the selection expression + /// is the "as"-tag for the expression + public SelectClauseExpression(Expression expression, String optionalAsName) + { + Expression = expression; + AsName = optionalAsName; + } + + /// Returns the selection expression. + /// expression + public Expression Expression { get; set; } + + /// Returns the optional "as"-name of the expression, or null if not defined + /// tag or null for selection expression + public string AsName { get; set; } + + /// Returns indicator whether annotated as "@eventbean" + public bool IsAnnotatedByEventFlag { get; set; } + + /// Renders the element in textual representation. + /// to output to + public void ToEPLElement(TextWriter writer) + { + Expression.ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + if (IsAnnotatedByEventFlag) + { + writer.Write(" @eventbean"); + } + if (AsName != null) + { + writer.Write(" as "); + writer.Write(AsName); + } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/client/soda/SelectClauseStreamWildcard.cs b/NEsper.Core/NEsper.Core/client/soda/SelectClauseStreamWildcard.cs new file mode 100755 index 000000000..e23412965 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/SelectClauseStreamWildcard.cs @@ -0,0 +1,75 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// For use in a select clause, this element in a select clause defines that for a + /// given stream we want to select the underlying type. Most often used in joins to + /// select wildcard from one of the joined streams. + /// + /// For example:
    select streamOne.* from StreamOne as streamOne, StreamTwo as
    +    /// streamTwo
    + ///
    + public class SelectClauseStreamWildcard : SelectClauseElement + { + /// + /// Initializes a new instance of the class. + /// + public SelectClauseStreamWildcard() + { + } + + /// + /// Ctor. + /// + /// is the name assigned to a stream + /// is the name to assign to the column carrying the streams generated events, ornull if the event should not appear in a column + public SelectClauseStreamWildcard(String streamName, String optionalColumnName) + { + this.StreamName = streamName; + this.OptionalColumnName = optionalColumnName; + } + + /// + /// Returns the stream name (e.g. select streamName.* as colName from MyStream as + /// streamName) + /// + /// + /// name + /// + public string StreamName { get; set; } + + /// + /// Returns the optional column name (e.g. select streamName.* as colName from + /// MyStream as streamName) + /// + /// + /// name of column, or null if none defined + /// + public string OptionalColumnName { get; set; } + + /// + /// Renders the element in textual representation. + /// + /// to output to + public void ToEPLElement(TextWriter writer) + { + writer.Write(StreamName); + writer.Write(".*"); + if (OptionalColumnName != null) + { + writer.Write(" as "); + writer.Write(OptionalColumnName); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/SelectClauseWildcard.cs b/NEsper.Core/NEsper.Core/client/soda/SelectClauseWildcard.cs new file mode 100755 index 000000000..99ff8d033 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/SelectClauseWildcard.cs @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Represents a wildcard in the select-clause. + /// + [Serializable] + public class SelectClauseWildcard : SelectClauseElement + { + /// Renders the element in textual representation. + /// to output to + public void ToEPLElement(TextWriter writer) + { + writer.Write("*"); + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/client/soda/SingleRowMethodExpression.cs b/NEsper.Core/NEsper.Core/client/soda/SingleRowMethodExpression.cs new file mode 100755 index 000000000..38a360ab4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/SingleRowMethodExpression.cs @@ -0,0 +1,67 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Generic single-row method call consists of a method name and parameters, possibly chained. + /// + [Serializable] + public class SingleRowMethodExpression : ExpressionBase + { + private readonly IList _chain; + + /// Ctor. + /// method name + /// an optiona array of parameters + public SingleRowMethodExpression(String method, Object[] parameters) + { + List parameterList = new List(); + for (int i = 0; i < parameters.Length; i++) + { + if (parameters[i] is Expression) + { + parameterList.Add((Expression)parameters[i]); + } + else + { + parameterList.Add(new ConstantExpression(parameters[i])); + } + } + _chain.Add(new DotExpressionItem(method, parameterList, false)); + } + + /// Returns the optional method invocation chain for the single-row method consisting of pairs of method name and list of parameters. + /// chain of method invocations + public IList Chain + { + get { return _chain; } + } + + /// Ctor. + /// of method invocations with at least one element, each pair a method name and list of parameter expressions + public SingleRowMethodExpression(IList chain) + { + _chain = chain; + } + + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.UNARY; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + DotExpressionItem.Render(_chain, writer, false); + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/StaticMethodExpression.cs b/NEsper.Core/NEsper.Core/client/soda/StaticMethodExpression.cs new file mode 100755 index 000000000..42ba368bd --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/StaticMethodExpression.cs @@ -0,0 +1,72 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + + +namespace com.espertech.esper.client.soda +{ + /// Static method call consists of a class name and method name. + [Serializable] + public class StaticMethodExpression : ExpressionBase + { + /// Ctor. + /// class name providing the static method + /// method name + /// an optiona array of parameters + public StaticMethodExpression(String className, String method, Object[] parameters) + { + Chain = new List(); + ClassName = className; + + var parameterList = new List(); + for (int i = 0; i < parameters.Length; i++) + { + if (parameters[i] is Expression) + { + parameterList.Add((Expression)parameters[i]); + } + else + { + parameterList.Add(new ConstantExpression(parameters[i])); + } + } + Chain.Add(new DotExpressionItem(method, parameterList, false)); + } + + /// Returns the chain of method invocations, each pair a method name and list of parameter expressions + /// method chain + public List Chain { get; set; } + + /// Ctor. + /// class name providing the static method + /// method chain + public StaticMethodExpression(String className, List chain) + { + ClassName = className; + Chain = chain; + } + + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.UNARY; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + writer.Write(ClassName); + DotExpressionItem.Render(Chain, writer, true); + } + + /// Returns the class name. + /// class name + public string ClassName { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/StddevProjectionExpression.cs b/NEsper.Core/NEsper.Core/client/soda/StddevProjectionExpression.cs new file mode 100755 index 000000000..480fe1fa2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/StddevProjectionExpression.cs @@ -0,0 +1,63 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Standard deviation of the (distinct) values returned by an expression. + /// + [Serializable] + public class StddevProjectionExpression : ExpressionBase + { + /// + /// Ctor. + /// + public StddevProjectionExpression() + { + } + + /// + /// Ctor - for use to create an expression tree, without inner expression + /// + /// true if distinct + public StddevProjectionExpression(bool isDistinct) + { + IsDistinct = isDistinct; + } + + /// + /// Ctor - adds the expression to project. + /// + /// returning values to project + /// true if distinct + public StddevProjectionExpression(Expression expression, bool isDistinct) + { + IsDistinct = isDistinct; + Children.Add(expression); + } + + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.UNARY; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + RenderAggregation(writer, "stddev", IsDistinct, Children); + } + + /// + /// Returns true if the projection considers distinct values only. + /// + /// true if distinct + public bool IsDistinct { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/Stream.cs b/NEsper.Core/NEsper.Core/client/soda/Stream.cs new file mode 100755 index 000000000..aacd10dc1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/Stream.cs @@ -0,0 +1,68 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// An abstract base class for a named or unnamed stream. + /// + /// Named streams provide an as-name for the stream, for example "select * from MyEvents(id=10) as StreamZero". + /// Unnamed streams provide no as-name for the stream, for example "select * from MyEvents(id=10)". + /// + [Serializable] + public abstract class Stream + { + /// Ctor. + protected Stream() + { + } + + /// Renders the stream in textual representation. + /// to output to + /// for NewLine-whitespace formatting + public abstract void ToEPLStream(TextWriter writer, EPStatementFormatter formatter); + + /// Renders the stream in textual representation any stream options, if present. + /// to output to + public abstract void ToEPLStreamOptions(TextWriter writer); + + /// Renders the stream type under a non-complete textual representation for tool use + /// to output to + public abstract void ToEPLStreamType(TextWriter writer); + + /// Ctor. + /// is null for unnamed streams, or a stream name for named streams. + protected Stream(String streamName) + { + StreamName = streamName; + } + + /// Returns the stream name. + /// name of stream, or null if unnamed. + public string StreamName { get; set; } + + /// Renders the clause in textual representation. + /// to output to + /// for NewLine-whitespace formatting + public void ToEPL(TextWriter writer, EPStatementFormatter formatter) + { + ToEPLStream(writer, formatter); + + if (StreamName != null) + { + writer.Write(" as "); + writer.Write(StreamName); + } + + ToEPLStreamOptions(writer); + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/StreamSelector.cs b/NEsper.Core/NEsper.Core/client/soda/StreamSelector.cs new file mode 100755 index 000000000..1c34f47f5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/StreamSelector.cs @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.soda +{ + /// + /// Enumeration for representing selection of the remove stream or the insert stream, or both. + /// + public enum StreamSelector + { + /// Indicates selection of the remove stream only. + RSTREAM_ONLY, + + /// Indicates selection of the insert stream only. + ISTREAM_ONLY, + + /// Indicates selection of both the insert and the remove stream. + RSTREAM_ISTREAM_BOTH + } + + public static class StreamSelectorExtensions + { + public static string GetEPL(this StreamSelector enumValue) + { + switch(enumValue) + { + case StreamSelector.RSTREAM_ONLY: + return "rstream"; + case StreamSelector.ISTREAM_ONLY: + return "istream"; + case StreamSelector.RSTREAM_ISTREAM_BOTH: + return "irstream"; + default: + throw new ArgumentException("invalid value for enum value", "enumValue"); + } + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/client/soda/StreamWildcardExpression.cs b/NEsper.Core/NEsper.Core/client/soda/StreamWildcardExpression.cs new file mode 100755 index 000000000..c9a877722 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/StreamWildcardExpression.cs @@ -0,0 +1,59 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Represents "stream.*" in for example "mystream.*" + /// + [Serializable] + public class StreamWildcardExpression : ExpressionBase + { + private string _streamName; + + /// + /// Ctor. + /// + /// stream name + public StreamWildcardExpression(string streamName) + { + this._streamName = streamName; + } + + /// + /// Ctor. + /// + public StreamWildcardExpression() + { + } + + /// + /// Returns the stream name. + /// + /// stream name + public string StreamName + { + get { return _streamName; } + set { this._streamName = value; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + writer.Write(_streamName); + writer.Write(".*"); + } + + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.UNARY; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/SubqueryExistsExpression.cs b/NEsper.Core/NEsper.Core/client/soda/SubqueryExistsExpression.cs new file mode 100755 index 000000000..6cdefeb46 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/SubqueryExistsExpression.cs @@ -0,0 +1,52 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Exists-expression for a set of values returned by a lookup. + /// + [Serializable] + public class SubqueryExistsExpression : ExpressionBase + { + /// + /// Initializes a new instance of the class. + /// + public SubqueryExistsExpression() + { + } + + /// + /// Ctor - for use to create an expression tree, without child expression. + /// + /// is the lookup statement object model + public SubqueryExistsExpression(EPStatementObjectModel model) + { + Model = model; + } + + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.UNARY; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + writer.Write("exists ("); + writer.Write(Model.ToEPL()); + writer.Write(')'); + } + + /// Gets or sets the lookup statement object model. + /// lookup model + public EPStatementObjectModel Model { get; set; } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/client/soda/SubqueryExpression.cs b/NEsper.Core/NEsper.Core/client/soda/SubqueryExpression.cs new file mode 100755 index 000000000..1393c67d3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/SubqueryExpression.cs @@ -0,0 +1,52 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Subquery-expression returns values returned by a lookup modelled by a further . + /// + [Serializable] + public class SubqueryExpression : ExpressionBase + { + /// + /// Initializes a new instance of the class. + /// + public SubqueryExpression() + { + } + + /// + /// Ctor - for use to create an expression tree, without child expression. + /// + /// is the lookup statement object model + public SubqueryExpression(EPStatementObjectModel model) + { + Model = model; + } + + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.UNARY; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + writer.Write('('); + writer.Write(Model.ToEPL()); + writer.Write(')'); + } + + /// Gets or sets the lookup statement object model. + /// lookup model + public EPStatementObjectModel Model { get; set; } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/client/soda/SubqueryInExpression.cs b/NEsper.Core/NEsper.Core/client/soda/SubqueryInExpression.cs new file mode 100755 index 000000000..5871c2c99 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/SubqueryInExpression.cs @@ -0,0 +1,88 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// In-expression for a set of values returned by a lookup. + /// + [Serializable] + public class SubqueryInExpression : ExpressionBase + { + private bool isNotIn; + private EPStatementObjectModel model; + + /// + /// Initializes a new instance of the class. + /// + public SubqueryInExpression() + { + } + + /// + /// Ctor - for use to create an expression tree, without child expression. + /// + /// is the lookup statement object model + /// is true for not-in + public SubqueryInExpression(EPStatementObjectModel model, bool isNotIn) + { + this.model = model; + this.isNotIn = isNotIn; + } + + /// + /// Ctor - for use to create an expression tree, without child expression. + /// + /// is the expression providing the value to match + /// is the lookup statement object model + /// is true for not-in + public SubqueryInExpression(Expression expression, EPStatementObjectModel model, bool isNotIn) + { + this.Children.Add(expression); + this.model = model; + this.isNotIn = isNotIn; + } + + /// Gets or sets the true for not-in, or false for in-lookup. + /// true for not-in + public bool IsNotIn + { + get { return isNotIn; } + set { isNotIn = value; } + } + + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.UNARY; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + Children[0].ToEPL(writer, Precedence); + if (IsNotIn) { + writer.Write(" not in ("); + } + else { + writer.Write(" in ("); + } + writer.Write(model.ToEPL()); + writer.Write(')'); + } + + /// Gets or sets the lookup statement object model. + /// lookup model + public EPStatementObjectModel Model + { + get { return model; } + set { model = value; } + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/client/soda/SubqueryQualifiedExpression.cs b/NEsper.Core/NEsper.Core/client/soda/SubqueryQualifiedExpression.cs new file mode 100755 index 000000000..7fd2fde20 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/SubqueryQualifiedExpression.cs @@ -0,0 +1,75 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Exists-expression for a set of values returned by a lookup. + /// + [Serializable] + public class SubqueryQualifiedExpression : ExpressionBase + { + /// + /// Ctor - for use to create an expression tree, without child expression. + /// + /// is the lookup statement object model + /// the op + /// true for ALL, false for ANY + public SubqueryQualifiedExpression(EPStatementObjectModel model, String @operator, bool all) + { + Model = model; + Operator = @operator; + IsAll = all; + } + + /// + /// Returns the lookup statement object model. + /// + /// + /// lookup model + /// + public EPStatementObjectModel Model { get; set; } + + /// + /// Gets or sets the operator. + /// + /// The operator. + public string Operator { get; set; } + + /// + /// Returns true for ALL, false for ANY. + /// + /// + /// all/any flag + /// + public bool IsAll { get; set; } + + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.UNARY; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + Children[0].ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + writer.Write(' '); + writer.Write(Operator); + if (IsAll) { + writer.Write(" all ("); + } + else { + writer.Write(" any ("); + } + writer.Write(Model.ToEPL()); + writer.Write(')'); + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/SumProjectionExpression.cs b/NEsper.Core/NEsper.Core/client/soda/SumProjectionExpression.cs new file mode 100755 index 000000000..93acb2ca7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/SumProjectionExpression.cs @@ -0,0 +1,63 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Sum of the (distinct) values returned by an expression. + /// + [Serializable] + public class SumProjectionExpression : ExpressionBase + { + /// + /// Ctor. + /// + public SumProjectionExpression() + { + } + + /// + /// Ctor - for use to create an expression tree, without inner expression + /// + /// true if distinct + public SumProjectionExpression(bool isDistinct) + { + this.IsDistinct = isDistinct; + } + + /// + /// Ctor - adds the expression to project. + /// + /// returning values to project + /// true if distinct + public SumProjectionExpression(Expression expression, bool isDistinct) + { + this.IsDistinct = isDistinct; + this.Children.Add(expression); + } + + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.UNARY; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + ExpressionBase.RenderAggregation(writer, "sum", IsDistinct, this.Children); + } + + /// + /// Returns true if the projection considers distinct values only. + /// + /// true if distinct + public bool IsDistinct { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/TableAccessExpression.cs b/NEsper.Core/NEsper.Core/client/soda/TableAccessExpression.cs new file mode 100755 index 000000000..b517c2583 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/TableAccessExpression.cs @@ -0,0 +1,92 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.client.soda +{ + /// + /// Table access expression. + /// + [Serializable] + public class TableAccessExpression : ExpressionBase + { + /// + /// Ctor. + /// + public TableAccessExpression() + { + } + + /// + /// Ctor. + /// + /// the table name + /// the list of key expressions for each table primary key in the same order as declared + /// optional column name + /// optional aggregation function + public TableAccessExpression(string tableName, IList keyExpressions, string optionalColumn, Expression optionalAggregate) + { + TableName = tableName; + KeyExpressions = keyExpressions; + OptionalColumn = optionalColumn; + OptionalAggregate = optionalAggregate; + } + + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.UNARY; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + writer.Write(TableName); + if (KeyExpressions != null && !KeyExpressions.IsEmpty()) { + writer.Write("["); + ToPrecedenceFreeEPL(KeyExpressions, writer); + writer.Write("]"); + } + if (OptionalColumn != null) { + writer.Write("."); + writer.Write(OptionalColumn); + } + if (OptionalAggregate != null) { + writer.Write("."); + OptionalAggregate.ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + } + } + + /// + /// Returns the table name. + /// + /// table name + public string TableName { get; set; } + + /// + /// Returns the primary key expressions. + /// + /// primary key expressions + public IList KeyExpressions { get; set; } + + /// + /// Returns the optional table column name to access. + /// + /// table column name or null if accessing row + public string OptionalColumn { get; set; } + + /// + /// Returns the optional table column aggregation accessor to use. + /// + /// table column aggregation accessor + public Expression OptionalAggregate { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/TimePeriodExpression.cs b/NEsper.Core/NEsper.Core/client/soda/TimePeriodExpression.cs new file mode 100755 index 000000000..50e01262f --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/TimePeriodExpression.cs @@ -0,0 +1,356 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// Represent an expression + [Serializable] + public class TimePeriodExpression : ExpressionBase + { + /// Ctor. + public TimePeriodExpression() + { + } + + /// + /// Ctor. + /// + /// flag to indicate that a year-part expression exists + /// flag to indicate that a month-part expression exists + /// flag to indicate that a week-part expression exists + /// flag to indicate that a day-part expression exists + /// flag to indicate that a hour-part expression exists + /// flag to indicate that a minute-part expression exists + /// flag to indicate that a seconds-part expression exists + /// flag to indicate that a millisec-part expression exists + /// flag to indicate that a microsecond-part expression exists + public TimePeriodExpression( + bool hasYears, + bool hasMonths, + bool hasWeeks, + bool hasDays, + bool hasHours, + bool hasMinutes, + bool hasSeconds, + bool hasMilliseconds, + bool hasMicroseconds) + { + HasYears = hasYears; + HasMonths = hasMonths; + HasWeeks = hasWeeks; + HasDays = hasDays; + HasHours = hasHours; + HasMinutes = hasMinutes; + HasSeconds = hasSeconds; + HasMilliseconds = hasMilliseconds; + HasMicroseconds = hasMicroseconds; + } + + /// + /// Ctor. + /// + /// expression returning years value, or null if no such part + /// expression returning months value, or null if no such part + /// expression returning weeks value, or null if no such part + /// expression returning days value, or null if no such part + /// expression returning hours value, or null if no such part + /// expression returning minutes value, or null if no such part + /// expression returning seconds value, or null if no such part + /// expression returning millisec value, or null if no such part + /// expression returning microsecond value, or null if no such part + public TimePeriodExpression( + Expression yearsExpr, + Expression monthsExpr, + Expression weeksExpr, + Expression daysExpr, + Expression hoursExpr, + Expression minutesExpr, + Expression secondsExpr, + Expression millisecondsExpr, + Expression microsecondsExpr) + { + AddExpr( + yearsExpr, monthsExpr, weeksExpr, daysExpr, hoursExpr, minutesExpr, secondsExpr, millisecondsExpr, + microsecondsExpr); + } + + /// + /// Ctor. + /// + /// flag to indicate that a year-part expression exists + /// flag to indicate that a month-part expression exists + /// flag to indicate that a week-part expression exists + /// flag to indicate that a day-part expression exists + /// flag to indicate that a hour-part expression exists + /// flag to indicate that a minute-part expression exists + /// flag to indicate that a seconds-part expression exists + /// flag to indicate that a millisec-part expression exists + public TimePeriodExpression( + bool hasYears, + bool hasMonths, + bool hasWeeks, + bool hasDays, + bool hasHours, + bool hasMinutes, + bool hasSeconds, + bool hasMilliseconds) + : this(hasYears, hasMonths, hasWeeks, hasDays, hasHours, hasMinutes, hasSeconds, hasMilliseconds, false) + { + } + + /// + /// Ctor. + /// + /// flag to indicate that a day-part expression exists + /// flag to indicate that a hour-part expression exists + /// flag to indicate that a minute-part expression exists + /// flag to indicate that a seconds-part expression exists + /// flag to indicate that a millisec-part expression exists + public TimePeriodExpression(bool hasDays, bool hasHours, bool hasMinutes, bool hasSeconds, bool hasMilliseconds) + : this(false, false, false, hasDays, hasHours, hasMinutes, hasSeconds, hasMilliseconds, false) + { + } + + /// + /// Ctor. + /// + /// expression returning years value, or null if no such part + /// expression returning months value, or null if no such part + /// expression returning weeks value, or null if no such part + /// expression returning days value, or null if no such part + /// expression returning hours value, or null if no such part + /// expression returning minutes value, or null if no such part + /// expression returning seconds value, or null if no such part + /// expression returning millisec value, or null if no such part + public TimePeriodExpression( + Expression yearsExpr, + Expression monthsExpr, + Expression weeksExpr, + Expression daysExpr, + Expression hoursExpr, + Expression minutesExpr, + Expression secondsExpr, + Expression millisecondsExpr) + : this(yearsExpr, monthsExpr, weeksExpr, daysExpr, hoursExpr, minutesExpr, secondsExpr, millisecondsExpr, null) + { + } + + /// + /// Ctor. + /// + /// expression returning days value, or null if no such part + /// expression returning hours value, or null if no such part + /// expression returning minutes value, or null if no such part + /// expression returning seconds value, or null if no such part + /// expression returning millisec value, or null if no such part + public TimePeriodExpression( + Expression daysExpr, + Expression hoursExpr, + Expression minutesExpr, + Expression secondsExpr, + Expression millisecondsExpr) + : this(null, null, null, daysExpr, hoursExpr, minutesExpr, secondsExpr, millisecondsExpr, null) + { + } + + private void AddExpr( + Expression yearsExpr, + Expression monthExpr, + Expression weeksExpr, + Expression daysExpr, + Expression hoursExpr, + Expression minutesExpr, + Expression secondsExpr, + Expression millisecondsExpr, + Expression microsecondsExpr) + { + if (yearsExpr != null) + { + HasYears = true; + AddChild(yearsExpr); + } + if (monthExpr != null) + { + HasMonths = true; + AddChild(monthExpr); + } + if (weeksExpr != null) + { + HasWeeks = true; + AddChild(weeksExpr); + } + if (daysExpr != null) + { + HasDays = true; + AddChild(daysExpr); + } + if (hoursExpr != null) + { + HasHours = true; + AddChild(hoursExpr); + } + if (minutesExpr != null) + { + HasMinutes = true; + AddChild(minutesExpr); + } + if (secondsExpr != null) + { + HasSeconds = true; + AddChild(secondsExpr); + } + if (millisecondsExpr != null) + { + HasMilliseconds = true; + AddChild(millisecondsExpr); + } + if (microsecondsExpr != null) + { + HasMicroseconds = true; + AddChild(microsecondsExpr); + } + } + + /// + /// Set to true if a subexpression exists that is a day-part. + /// + /// for presence of part + public bool HasDays { get; set; } + + /// + /// Set to true if a subexpression exists that is a hour-part. + /// + /// for presence of part + public bool HasHours { get; set; } + + /// + /// Set to true if a subexpression exists that is a minutes-part. + /// + /// for presence of part + public bool HasMinutes { get; set; } + + /// + /// Set to true if a subexpression exists that is a seconds-part. + /// + /// for presence of part + public bool HasSeconds { get; set; } + + /// + /// Set to true if a subexpression exists that is a msec-part. + /// + /// for presence of part + public bool HasMilliseconds { get; set; } + + /// + /// Set to true if a subexpression exists that is a year-part. + /// + /// for presence of part + public bool HasYears { get; set; } + + /// + /// Set to true if a subexpression exists that is a month-part. + /// + /// for presence of part + public bool HasMonths { get; set; } + + /// + /// Set to true if a subexpression exists that is a weeks-part. + /// + /// for presence of part + public bool HasWeeks { get; set; } + + /// + /// Set to true if a subexpression exists that is a microsecond-part. + /// + /// indicator for presence of part + public bool HasMicroseconds { get; set; } + + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.UNARY; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + string delimiter = ""; + int countExpr = 0; + if (HasYears) + { + Children[countExpr].ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + writer.Write(" years"); + delimiter = " "; + countExpr++; + } + if (HasMonths) + { + writer.Write(delimiter); + Children[countExpr].ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + writer.Write(" months"); + delimiter = " "; + countExpr++; + } + if (HasWeeks) + { + writer.Write(delimiter); + Children[countExpr].ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + writer.Write(" weeks"); + delimiter = " "; + countExpr++; + } + if (HasDays) + { + writer.Write(delimiter); + Children[countExpr].ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + writer.Write(" days"); + delimiter = " "; + countExpr++; + } + if (HasHours) + { + writer.Write(delimiter); + Children[countExpr].ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + writer.Write(" hours"); + delimiter = " "; + countExpr++; + } + if (HasMinutes) + { + writer.Write(delimiter); + Children[countExpr].ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + writer.Write(" minutes"); + delimiter = " "; + countExpr++; + } + if (HasSeconds) + { + writer.Write(delimiter); + Children[countExpr].ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + writer.Write(" seconds"); + delimiter = " "; + countExpr++; + } + if (HasMilliseconds) + { + writer.Write(delimiter); + Children[countExpr].ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + writer.Write(" milliseconds"); + delimiter = " "; + countExpr++; + } + if (HasMicroseconds) + { + writer.Write(delimiter); + Children[countExpr].ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + writer.Write(" microseconds"); + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/client/soda/TypeOfExpression.cs b/NEsper.Core/NEsper.Core/client/soda/TypeOfExpression.cs new file mode 100755 index 000000000..31c23db5a --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/TypeOfExpression.cs @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.client.soda +{ + /// + /// Type-of expression return the type name, as a string value, of the events in + /// the stream if passing a stream name or the fragment event type if passing a + /// property name that results in a fragment event otherwise the class simple name + /// of the expression result or null if the expression returns a null value. + /// + [Serializable] + public class TypeOfExpression : ExpressionBase + { + /// + /// Ctor. + /// + public TypeOfExpression() { + } + + /// + /// Ctor. + /// + /// for which to return the result type or null if the result is null + public TypeOfExpression(Expression expression) + { + Children.Add(expression); + } + + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.UNARY; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + writer.Write("typeof("); + Children[0].ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + writer.Write(")"); + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/UpdateClause.cs b/NEsper.Core/NEsper.Core/client/soda/UpdateClause.cs new file mode 100755 index 000000000..dac01366a --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/UpdateClause.cs @@ -0,0 +1,129 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.util; + +namespace com.espertech.esper.client.soda +{ + /// + /// Specification for the Update clause. + /// + [Serializable] + public class UpdateClause : MetaDefItem + { + /// + /// Ctor. + /// + public UpdateClause() + { + } + + /// + /// Ctor. + /// + /// the name of the type to Update + /// expression returning a value to write + /// Update clause + public static UpdateClause Create(String eventType, Expression expression) + { + var clause = new UpdateClause(eventType, null); + clause.AddAssignment(expression); + return clause; + } + + /// + /// Ctor. + /// + /// the name of the type to Update + /// as-clause for Update, if any + public UpdateClause(String eventType, String optionalAsClauseStreamName) + { + EventType = eventType; + OptionalAsClauseStreamName = optionalAsClauseStreamName; + Assignments = new List(); + } + + /// + /// Adds a property to set to the clause. + /// + /// expression providing the new property value + /// clause + public UpdateClause AddAssignment(Expression expression) + { + Assignments.Add(new Assignment(expression)); + return this; + } + + /// + /// Returns the list of property assignments. + /// + /// pair of property name and expression + public IList Assignments { get; set; } + + /// + /// Returns the name of the event type to Update. + /// + /// name of type + public string EventType { get; set; } + + /// + /// Returns the where-clause if any. + /// + /// where clause + public Expression OptionalWhereClause { get; set; } + + /// + /// Returns the stream name. + /// + /// stream name + public string OptionalAsClauseStreamName { get; set; } + + /// + /// Renders the clause in EPL. + /// + /// to output to + public void ToEPL(TextWriter writer) + { + writer.Write("update istream "); + writer.Write(EventType); + if (OptionalAsClauseStreamName != null) { + writer.Write(" as "); + writer.Write(OptionalAsClauseStreamName); + } + writer.Write(" "); + RenderEPLAssignments(writer, Assignments); + + if (OptionalWhereClause != null) + { + writer.Write(" where "); + OptionalWhereClause.ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + } + } + + /// + /// Write assignments. + /// + /// to write to + /// to write + public static void RenderEPLAssignments(TextWriter writer, IList assignments) + { + writer.Write("set "); + String delimiter = ""; + foreach (Assignment pair in assignments) + { + writer.Write(delimiter); + pair.Value.ToEPL(writer, ExpressionPrecedenceEnum.MINIMUM); + delimiter = ", "; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/View.cs b/NEsper.Core/NEsper.Core/client/soda/View.cs new file mode 100755 index 000000000..16d6a0672 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/View.cs @@ -0,0 +1,137 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.client.soda +{ + /// + /// A view provides a projection upon a stream, such as a data window, grouping or unique. + /// For views, the namespace is an optional value and can be null for any-namespace. + /// + [Serializable] + public class View : EPBaseNamedObject + { + + /// Ctor. + public View() + { + } + + /// + /// Creates a view. + /// + /// is thie view namespace, i.e. "win" for data windows + /// is the view name, i.e. "length" for length window + /// is a list of view parameters, or empty if there are no parameters for the view + public View(string @namespace, string name, IList parameters) + : base(@namespace, name, parameters) + { + } + + /// + /// Creates a view. + /// + /// is thie view namespace, i.e. "win" for data windows + /// is the view name, i.e. "length" for length window + /// view + public static View Create(string @namespace, string name) + { + return new View(@namespace, name, new List()); + } + + /// + /// Creates a view. + /// + /// is the view name, i.e. "length" for length window + /// view + public static View Create(string name) + { + return new View(null, name, new List()); + } + + /// + /// Creates a view. + /// + /// is thie view namespace, i.e. "win" for data windows + /// is the view name, i.e. "length" for length window + /// is a list of view parameters, or empty if there are no parameters for the view + /// view + public static View Create(string @namespace, string name, IList parameters) + { + return new View(@namespace, name, parameters); + } + + /// + /// Creates a view. + /// + /// is the view name, i.e. "length" for length window + /// is a list of view parameters, or empty if there are no parameters for the view + /// view + public static View Create(string name, List parameters) + { + return new View(null, name, parameters); + } + + /// + /// Creates a view. + /// + /// is thie view namespace, i.e. "win" for data windows + /// is the view name, i.e. "length" for length window + /// is a list of view parameters, or empty if there are no parameters for the view + /// view + public static View Create(string @namespace, string name, params Expression[] parameters) + { + if (parameters != null) + { + return new View(@namespace, name, parameters); + } + else + { + return new View(@namespace, name, new List()); + } + } + + /// + /// Creates a view. + /// + /// is the view name, i.e. "length" for length window + /// is a list of view parameters, or empty if there are no parameters for the view + /// view + public static View Create(string name, params Expression[] parameters) + { + if (parameters != null) + { + return new View(null, name, parameters); + } + else + { + return new View(null, name, new List()); + } + } + + /// + /// RenderAny view. + /// + /// to render to + public void ToEPLWithHash(TextWriter writer) + { + writer.Write(Name); + if (!Parameters.IsEmpty()) + { + writer.Write('('); + ExpressionBase.ToPrecedenceFreeEPL(Parameters, writer); + writer.Write(')'); + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/client/soda/WildcardExpression.cs b/NEsper.Core/NEsper.Core/client/soda/WildcardExpression.cs new file mode 100755 index 000000000..6a508aba8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/WildcardExpression.cs @@ -0,0 +1,33 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.client.soda +{ + /// + /// Represents "*" in for example "last(*)" + /// + [Serializable] + public class WildcardExpression : ExpressionBase + { + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + writer.Write("*"); + } + + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.UNARY; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/soda/WindowProjectionExpression.cs b/NEsper.Core/NEsper.Core/client/soda/WindowProjectionExpression.cs new file mode 100755 index 000000000..f48a4f2cf --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/soda/WindowProjectionExpression.cs @@ -0,0 +1,35 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.soda +{ + /// + /// Represents the "window" aggregation function. + /// + [Serializable] + public class WindowProjectionExpression : AccessProjectionExpressionBase + { + /// Ctor. + public WindowProjectionExpression() { + } + + /// Ctor. + /// to aggregate + public WindowProjectionExpression(Expression expression) + : base(expression) + { + } + + public override string AggregationFunctionName + { + get { return "window"; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/time/CurrentTimeEvent.cs b/NEsper.Core/NEsper.Core/client/time/CurrentTimeEvent.cs new file mode 100755 index 000000000..6b5ec15a6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/time/CurrentTimeEvent.cs @@ -0,0 +1,67 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; + +namespace com.espertech.esper.client.time +{ + /// + /// Event for externally controlling the TimeInMillis within an + /// or instance. + /// External clocking must be enabled via before this class can be used + /// to externally feed TimeInMillis. + /// + public sealed class CurrentTimeEvent : TimerEvent + { + /// + /// Returns the TimeInMillis in milliseconds. + /// + /// TimeInMillis in milliseconds + public long Time { get; set; } + + /// + /// Initializes a new instance of the class. + /// + /// The time in millis. + public CurrentTimeEvent(long time) + { + Time = time; + } + + /// + /// Initializes a new instance of the class. + /// + /// The date time. + public CurrentTimeEvent(DateTime dateTime) + { + Time = (new DateTimeOffset(dateTime, TimeZoneInfo.Local.GetUtcOffset(dateTime))).TimeInMillis(); + } + + /// + /// Initializes a new instance of the class. + /// + /// The date time. + public CurrentTimeEvent(DateTimeOffset dateTime) + { + Time = dateTime.TimeInMillis(); + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override String ToString() + { + return DateTimeOffsetHelper.TimeFromMillis(Time, TimeZoneInfo.Utc).ToString(); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/client/time/CurrentTimeSpanEvent.cs b/NEsper.Core/NEsper.Core/client/time/CurrentTimeSpanEvent.cs new file mode 100755 index 000000000..41663d612 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/time/CurrentTimeSpanEvent.cs @@ -0,0 +1,76 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; + +namespace com.espertech.esper.client.time +{ + /// + /// Event for externally controlling the time within an or instance, + /// advancing time over a span of time. + /// + /// The engine advances time according to the resolution passed in, completing at the target time provided. + /// + /// + /// When used without a resolution or with a negative or zero value for resolution the engine advances time according + /// to any statement schedules that may be present. If no statement schedules are present, the engine simply advances time + /// to the target time provided. + /// + /// + /// External clocking must be enabled via before this class can be used + /// to externally feed time. + /// + /// + [Serializable] + public sealed class CurrentTimeSpanEvent : TimerEvent + { + /// + /// Constructor taking only a target time to advance to. + /// + /// Use this constructor to have the engine decide the resolution at which time advances, according to + /// present statement schedules. + /// + /// + /// target time + public CurrentTimeSpanEvent(long targetTime) + { + TargetTime = targetTime; + } + + /// + /// Constructor taking a target time to advance to and a resoultion to use to advance time. + /// + /// Use this constructor to dictate a resolution at which time advances. + /// + /// + /// target time + /// should be a positive value + public CurrentTimeSpanEvent(long targetTime, long optionalResolution) + { + TargetTime = targetTime; + OptionalResolution = optionalResolution; + } + + /// + /// Returns the target time to advance engine time to. + /// + /// target time + public long TargetTime { get; set; } + + /// + /// Returns the resolution for advancing time, or null if none provided. + /// + /// resolution + public long? OptionalResolution { get; set; } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/client/time/TimerControlEvent.cs b/NEsper.Core/NEsper.Core/client/time/TimerControlEvent.cs new file mode 100755 index 000000000..49a0d3b5a --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/time/TimerControlEvent.cs @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.time +{ + /// + /// Event for controlling clocking, i.e. to enable and disable external clocking. + /// + + [Serializable] + public sealed class TimerControlEvent : TimerEvent + { + /// + /// Enumeration that describes what type of clock we are using. + /// + + public enum ClockTypeEnum + { + /// For external clocking. + CLOCK_EXTERNAL, + /// For internal clocking. + CLOCK_INTERNAL + }; + + /// Constructor takes a clocking type as parameter. + /// for internal or external clocking + /// + public TimerControlEvent(ClockTypeEnum clockType) + { + ClockType = clockType; + } + + /// Returns clocking type. + /// clocking type + /// + public ClockTypeEnum ClockType { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/time/TimerEvent.cs b/NEsper.Core/NEsper.Core/client/time/TimerEvent.cs new file mode 100755 index 000000000..7cd4cf2ad --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/time/TimerEvent.cs @@ -0,0 +1,16 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + + +/// Abstract base class marker for events that control time keeping by an event stream processor instance. + [Serializable] +public abstract class TimerEvent +{ +} diff --git a/NEsper.Core/NEsper.Core/client/util/ClassForNameProvider.cs b/NEsper.Core/NEsper.Core/client/util/ClassForNameProvider.cs new file mode 100755 index 000000000..421e166c7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/util/ClassForNameProvider.cs @@ -0,0 +1,30 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.util +{ + /// + /// Provider of lookup of a class name resolving into a class. + /// + public interface ClassForNameProvider + { + /// + /// Lookup class name returning class. + /// + /// to look up + /// class + Type ClassForName(string className) ; + } + + public class ClassForNameProviderConstants + { + public const string NAME = "ClassForNameProvider"; + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/client/util/ClassForNameProviderDefault.cs b/NEsper.Core/NEsper.Core/client/util/ClassForNameProviderDefault.cs new file mode 100755 index 000000000..ccedb97b4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/util/ClassForNameProviderDefault.cs @@ -0,0 +1,31 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.util; + +namespace com.espertech.esper.client.util +{ + /// Default provider for classname lookups. + public class ClassForNameProviderDefault : ClassForNameProvider + { + public const string NAME = "ClassForNameProvider"; + + public static readonly ClassForNameProviderDefault INSTANCE = new ClassForNameProviderDefault(); + + private ClassForNameProviderDefault() + { + } + + public Type ClassForName(string className) + { + return TypeHelper.GetTypeForSimpleName(className, false, true); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/client/util/ClassLoaderProvider.cs b/NEsper.Core/NEsper.Core/client/util/ClassLoaderProvider.cs new file mode 100755 index 000000000..5f90a5702 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/util/ClassLoaderProvider.cs @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.compat; + +namespace com.espertech.esper.client.util +{ + /// + /// Provider of a classloader. + /// + public interface ClassLoaderProvider + { + /// + /// Returns the classloader. + /// + /// classloader + ClassLoader Classloader(); + } + + public class ClassLoaderProviderConstants + { + public const string NAME = "ClassLoaderProvider"; + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/client/util/ClassLoaderProviderDefault.cs b/NEsper.Core/NEsper.Core/client/util/ClassLoaderProviderDefault.cs new file mode 100755 index 000000000..bed9b65ce --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/util/ClassLoaderProviderDefault.cs @@ -0,0 +1,31 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.compat; + +namespace com.espertech.esper.client.util +{ + /// + /// Default class loader provider returns the current thread context classloader. + /// + public class ClassLoaderProviderDefault : ClassLoaderProvider + { + public const string NAME = "ClassLoaderProvider"; + + public static readonly ClassLoaderProviderDefault INSTANCE = new ClassLoaderProviderDefault(); + + private ClassLoaderProviderDefault() + { + } + + public ClassLoader Classloader() + { + return ClassLoaderDefault.GetInstance(); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/client/util/CountMinSketchAgent.cs b/NEsper.Core/NEsper.Core/client/util/CountMinSketchAgent.cs new file mode 100755 index 000000000..0b550253b --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/util/CountMinSketchAgent.cs @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.util +{ + /// + /// For use with Count-min sketch aggregation functions: + /// The agent implementation encapsulates transformation of value objects to byte-array and back (when needed), + /// and may override or provide custom behavior. + /// + public interface CountMinSketchAgent + { + /// + /// Returns an array of types that the agent can handle, for validation purposes. + /// For example, an agent that accepts byte-array type values should return "new Class[] {typeof(String)}". + /// Interfaces and supertype classes can also be part of the class array. + /// + /// class array of acceptable type + Type[] AcceptableValueTypes { get; } + + /// + /// Add a value to the Count-min sketch. + /// Implementations typically check for null value, convert the value object to a byte-array + /// and invoke a method on the state object to add the byte-array value. + /// + /// contains value to add as well as the state + void Add(CountMinSketchAgentContextAdd ctx); + + /// + /// Return the estimated count for a given value. + /// Implementations typically check for null value, convert the value object to a byte-array + /// and invoke a method on the state object to retrieve a count. + /// + /// contains value to query as well as the state + /// estimated count + long? Estimate(CountMinSketchAgentContextEstimate ctx); + + /// + /// Return the value object for a given byte-array, for use with top-K. + /// Implementations typically simply convert a byte-array into a value object. + /// + /// value object and state + /// value object + object FromBytes(CountMinSketchAgentContextFromBytes ctx); + } +} diff --git a/NEsper.Core/NEsper.Core/client/util/CountMinSketchAgentContext.cs b/NEsper.Core/NEsper.Core/client/util/CountMinSketchAgentContext.cs new file mode 100755 index 000000000..aea4e9d74 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/util/CountMinSketchAgentContext.cs @@ -0,0 +1,33 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.approx; + +namespace com.espertech.esper.client.util +{ + /// + /// Count-min sketch base context object. + /// + public abstract class CountMinSketchAgentContext + { + /// + /// Ctor. + /// + /// the state + protected CountMinSketchAgentContext(CountMinSketchState state) + { + State = state; + } + + /// + /// Returns state + /// + /// state + public CountMinSketchState State { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/util/CountMinSketchAgentContextAdd.cs b/NEsper.Core/NEsper.Core/client/util/CountMinSketchAgentContextAdd.cs new file mode 100755 index 000000000..a90770b64 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/util/CountMinSketchAgentContextAdd.cs @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.approx; + +namespace com.espertech.esper.client.util +{ + /// + /// Count-min sketch context object for add-operations. + /// + public class CountMinSketchAgentContextAdd : CountMinSketchAgentContext + { + /// + /// Ctor. + /// + /// the state + public CountMinSketchAgentContextAdd(CountMinSketchState state) : base(state) + { + } + + /// + /// Returns the value. + /// + /// value + public object Value { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/util/CountMinSketchAgentContextEstimate.cs b/NEsper.Core/NEsper.Core/client/util/CountMinSketchAgentContextEstimate.cs new file mode 100755 index 000000000..bcc05be32 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/util/CountMinSketchAgentContextEstimate.cs @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.approx; + +namespace com.espertech.esper.client.util +{ + /// + /// Count-min sketch context object for estimate-operations. + /// + public class CountMinSketchAgentContextEstimate : CountMinSketchAgentContext + { + /// + /// Ctor. + /// + /// the state + public CountMinSketchAgentContextEstimate(CountMinSketchState state) : base(state) + { + } + + /// + /// Returns the value. + /// + /// value + public object Value { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/util/CountMinSketchAgentContextFromBytes.cs b/NEsper.Core/NEsper.Core/client/util/CountMinSketchAgentContextFromBytes.cs new file mode 100755 index 000000000..46b346468 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/util/CountMinSketchAgentContextFromBytes.cs @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.approx; + +namespace com.espertech.esper.client.util +{ + /// + /// Count-min sketch context object for topk-operations. + /// + public class CountMinSketchAgentContextFromBytes : CountMinSketchAgentContext + { + /// + /// Ctor. + /// + /// the state + public CountMinSketchAgentContextFromBytes(CountMinSketchState state) : base(state) + { + } + + /// + /// Returns the byte value. + /// + /// bytes + public byte[] Bytes { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/util/CountMinSketchAgentStringUTF16.cs b/NEsper.Core/NEsper.Core/client/util/CountMinSketchAgentStringUTF16.cs new file mode 100755 index 000000000..a3246f9ff --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/util/CountMinSketchAgentStringUTF16.cs @@ -0,0 +1,57 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Text; + +namespace com.espertech.esper.client.util +{ + /// + /// Count-min sketch agent that handles String-type values and uses UTF-16 encoding + /// to transform strings to byte-array and back. + /// + public class CountMinSketchAgentStringUTF16 : CountMinSketchAgent + { + public Type[] AcceptableValueTypes + { + get + { + return new Type[] + { + typeof (string) + }; + } + } + + public void Add(CountMinSketchAgentContextAdd ctx) { + string text = (string) ctx.Value; + if (text == null) { + return; + } + byte[] bytes = ToBytesUTF16(text); + ctx.State.Add(bytes, 1); + } + + public long? Estimate(CountMinSketchAgentContextEstimate ctx) { + string text = (string) ctx.Value; + if (text == null) { + return null; + } + byte[] bytes = ToBytesUTF16(text); + return ctx.State.Frequency(bytes); + } + + public object FromBytes(CountMinSketchAgentContextFromBytes ctx) { + return Encoding.Unicode.GetString(ctx.Bytes); + } + + private byte[] ToBytesUTF16(string text) { + return Encoding.Unicode.GetBytes(text); + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/util/CountMinSketchTopK.cs b/NEsper.Core/NEsper.Core/client/util/CountMinSketchTopK.cs new file mode 100755 index 000000000..48fbac541 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/util/CountMinSketchTopK.cs @@ -0,0 +1,46 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.client.util +{ + /// + /// ConstantValue object for count-min-sketch top-k. + /// + public class CountMinSketchTopK + { + /// + /// Ctor. + /// + /// the value frequency + /// the value object + public CountMinSketchTopK(long frequency, object value) + { + Frequency = frequency; + Value = value; + } + + /// + /// Returns the frequency + /// + /// frequency + public long Frequency { get; private set; } + + /// + /// Returns the value object + /// + /// value + public object Value { get; private set; } + + public override string ToString() { + return "CountMinSketchFrequency{" + + "frequency=" + Frequency + + ", value=" + Value + + '}'; + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/util/EventPropertyRenderer.cs b/NEsper.Core/NEsper.Core/client/util/EventPropertyRenderer.cs new file mode 100755 index 000000000..d1d5df50f --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/util/EventPropertyRenderer.cs @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.client.util +{ + /// + /// Interface for use with the JSON or XML event renderes to handle custom event property rendering. + /// Implementations of this interface are called for each event property and may utilize the context object provided to render the event property value to a string. + /// The context itself contains a reference to the default renderer that can be delegated to for properties that use the default rendering. + /// Do not retain a handle to the renderer context as the context object changes for each event property. + /// + public interface EventPropertyRenderer + { + /// RenderAny an event property. + /// provides information about the property + void Render(EventPropertyRendererContext context); + } +} diff --git a/NEsper.Core/NEsper.Core/client/util/EventPropertyRendererContext.cs b/NEsper.Core/NEsper.Core/client/util/EventPropertyRendererContext.cs new file mode 100755 index 000000000..2287cf9bd --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/util/EventPropertyRendererContext.cs @@ -0,0 +1,86 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Text; + +using com.espertech.esper.events.util; + +namespace com.espertech.esper.client.util +{ + /// + /// Context for use with the interface for use with the JSON + /// or XML event renderes to handle custom event property rendering. + /// + /// Do not retain a handle to the renderer context as this object changes for each event property. + /// + /// + public class EventPropertyRendererContext + { + /// Ctor. + /// event type + /// bool if JSON formatted + public EventPropertyRendererContext(EventType eventType, bool jsonFormatted) + { + EventType = eventType; + IsJsonFormatted = jsonFormatted; + } + + /// Returns the property name to be rendered. + /// property name + public string PropertyName { get; set; } + + /// Returns the property value. + /// value + public object PropertyValue { get; set; } + + /// Returns the output value default renderer. + /// renderer + public OutputValueRenderer DefaultRenderer { get; set; } + + /// Sets the string builer + /// to set + public void SetStringBuilderAndReset(StringBuilder stringBuilder) { + StringBuilder = stringBuilder; + MappedPropertyKey = null; + IndexedPropertyIndex = null; + } + + /// Returns the string builder. + /// string builder to use + public StringBuilder StringBuilder { get; private set; } + + /// Returns the event type + /// event type + public EventType EventType { get; private set; } + + /// Returns the index for indexed properties. + /// property index + public int? IndexedPropertyIndex { get; set; } + + /// Returns the map key for mapped properties + /// map key + public string MappedPropertyKey { get; set; } + + /// Returns true for JSON formatted. + /// indicator + public bool IsJsonFormatted { get; private set; } + + /// Copies context. + /// copy + public EventPropertyRendererContext Copy() + { + var copy = new EventPropertyRendererContext(EventType, IsJsonFormatted); + copy.MappedPropertyKey = MappedPropertyKey; + copy.IndexedPropertyIndex = IndexedPropertyIndex; + copy.DefaultRenderer = DefaultRenderer; + copy.PropertyName = PropertyName; + copy.PropertyValue = PropertyValue; + return copy; + } + } +} diff --git a/NEsper.Core/NEsper.Core/client/util/EventRenderer.cs b/NEsper.Core/NEsper.Core/client/util/EventRenderer.cs new file mode 100755 index 000000000..25d3244dd --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/util/EventRenderer.cs @@ -0,0 +1,122 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.util +{ + /// + /// Provider for rendering services of events. + /// + public interface EventRenderer + { + /// + /// Returns a render for the JSON format, valid only for the given event type and + /// its subtypes. + /// + /// to return renderer for + /// rendering options + /// + /// JSON format renderer + /// + JSONEventRenderer GetJSONRenderer(EventType eventType, JSONRenderingOptions options); + + /// + /// Returns a render for the JSON format, valid only for the given event type and + /// its subtypes. + /// + /// to return renderer for + /// + /// JSON format renderer + /// + JSONEventRenderer GetJSONRenderer(EventType eventType); + + /// + /// Quick-access method to render a given event in the JSON format. + /// + /// Use the #getJSONRenderer to obtain a renderer instance that allows repeated + /// rendering of the same type of event. For performance reasons obtaining a dedicated + /// renderer instance is the preferred method compared to repeated rendering via this + /// method. + /// + /// the JSON root title + /// the event to render + /// + /// JSON formatted text + /// + String RenderJSON(String title, EventBean theEvent); + + /// + /// Quick-access method to render a given event in the JSON format. + /// + /// Use the #getJSONRenderer to obtain a renderer instance that allows repeated + /// rendering of the same type of event. For performance reasons obtaining a dedicated + /// renderer instance is the preferred method compared to repeated rendering via this + /// method. + /// + /// the JSON root title + /// the event to render + /// are JSON rendering options + /// + /// JSON formatted text + /// + String RenderJSON(String title, EventBean theEvent, JSONRenderingOptions options); + + /// + /// Returns a render for the XML format, valid only for the given event type and its + /// subtypes. + /// + /// to return renderer for + /// + /// XML format renderer + /// + XMLEventRenderer GetXMLRenderer(EventType eventType); + + /// + /// Returns a render for the XML format, valid only for the given event type and its + /// subtypes. + /// + /// to return renderer for + /// rendering options + /// + /// XML format renderer + /// + XMLEventRenderer GetXMLRenderer(EventType eventType, XMLRenderingOptions options); + + /// + /// Quick-access method to render a given event in the XML format. + /// + /// Use the #getXMLRenderer to obtain a renderer instance that allows repeated + /// rendering of the same type of event. For performance reasons obtaining a dedicated + /// renderer instance is the preferred method compared to repeated rendering via this + /// method. + /// + /// the root element name that may also include namespace information + /// the event to render + /// + /// XML formatted text + /// + String RenderXML(String rootElementName, EventBean theEvent); + + /// + /// Quick-access method to render a given event in the XML format. + /// + /// Use the #getXMLRenderer to obtain a renderer instance that allows repeated + /// rendering of the same type of event. For performance reasons obtaining a dedicated + /// renderer instance is the preferred method compared to repeated rendering via this + /// method. + /// + /// the root element name that may also include namespace information + /// the event to render + /// are XML rendering options + /// + /// XML formatted text + /// + String RenderXML(String rootElementName, EventBean theEvent, XMLRenderingOptions options); + } +} diff --git a/NEsper.Core/NEsper.Core/client/util/EventUnderlyingType.cs b/NEsper.Core/NEsper.Core/client/util/EventUnderlyingType.cs new file mode 100755 index 000000000..ba4e9a396 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/util/EventUnderlyingType.cs @@ -0,0 +1,68 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.events.avro; + +namespace com.espertech.esper.client.util +{ + /// Enumeration of event representation. + public enum EventUnderlyingType + { + /// Event representation is object-array (Object[]). + OBJECTARRAY, + + /// + /// Event representation is Map (any IDictionary interface implementation). + /// + MAP, + + /// Event representation is Avro (GenericRecord). + AVRO + } + + public static class EventUnderlyingTypeExtensions + { + private static readonly string OA_TYPE_NAME = typeof(Object[]).FullName; + private static readonly string MAP_TYPE_NAME = typeof(IDictionary).FullName; + private static readonly string AVRO_TYPE_NAME = AvroConstantsNoDep.GENERIC_RECORD_CLASSNAME; + + /// + /// Returns the default underlying type. + /// + /// default underlying type + public static EventUnderlyingType GetDefault() + { + return EventUnderlyingType.MAP; + } + + /// + /// Returns the class name of the default underlying type. + /// + /// default underlying type class name + public static string GetUnderlyingClassName(this EventUnderlyingType enumValue) + { + switch (enumValue) + { + case EventUnderlyingType.OBJECTARRAY: + return OA_TYPE_NAME; + case EventUnderlyingType.MAP: + return MAP_TYPE_NAME; + case EventUnderlyingType.AVRO: + return AVRO_TYPE_NAME; + } + + throw new ArgumentException("illegal enum value"); + } + } +} // end of namespace diff --git a/NEsper/NEsper/client/annotation/Hint.cs b/NEsper.Core/NEsper.Core/client/util/FastClassClassLoaderProvider.cs old mode 100644 new mode 100755 similarity index 52% rename from NEsper/NEsper/client/annotation/Hint.cs rename to NEsper.Core/NEsper.Core/client/util/FastClassClassLoaderProvider.cs index 8f3696cb2..51578bafa --- a/NEsper/NEsper/client/annotation/Hint.cs +++ b/NEsper.Core/NEsper.Core/client/util/FastClassClassLoaderProvider.cs @@ -12,32 +12,24 @@ using com.espertech.esper.compat.collections; using com.espertech.esper.compat.logging; -namespace com.espertech.esper.client.annotation +namespace com.espertech.esper.client.util { /// - /// Annotation for providing a statement execution hint. - /// - /// Hints are providing instructions that can change latency, throughput or memory requirements of a statement. - /// + /// Type loader provider for use with FastClass-instance creation. /// - public @interface Hint { - + public interface FastClassClassLoaderProvider + { /// - /// Hint Keyword(s), comma-separated. + /// Returns the classloader to use. /// - /// keywords - string Value() default ""; - - /// - /// Optional information to what the hint applies to - /// - /// applies - AppliesTo Applies() default AppliesTo.UNDEFINED; - - /// - /// Optional model name. - /// - /// model name - string Model() default ""; + /// class to generate FastClass for + /// class loader + ClassLoader Classloader(Type clazz); } + + public class FastClassClassLoaderProviderConstants + { + public const string NAME = "FastClassClassLoaderProvider"; + } + } // end of namespace diff --git a/NEsper.Core/NEsper.Core/client/util/FastClassClassLoaderProviderDefault.cs b/NEsper.Core/NEsper.Core/client/util/FastClassClassLoaderProviderDefault.cs new file mode 100755 index 000000000..d0f784d0b --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/util/FastClassClassLoaderProviderDefault.cs @@ -0,0 +1,33 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; + +namespace com.espertech.esper.client.util +{ + /// + /// Default class loader provider for use with FastClass-instance creation. + /// + public class FastClassClassLoaderProviderDefault : FastClassClassLoaderProvider + { + public const string NAME = "FastClassClassLoaderProvider"; + + public static readonly FastClassClassLoaderProviderDefault INSTANCE = new FastClassClassLoaderProviderDefault(); + + private FastClassClassLoaderProviderDefault() + { + } + + public ClassLoader Classloader(Type clazz) + { + throw new NotSupportedException(); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/client/util/JSONEventRenderer.cs b/NEsper.Core/NEsper.Core/client/util/JSONEventRenderer.cs new file mode 100755 index 000000000..43b58f391 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/util/JSONEventRenderer.cs @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.util +{ + /// Renderer for an event into the JSON textual format. A renderer is dedicated to rendering only a certain type of events and subtypes of that type, as the render cache type metadata and prepares structures to enable fast rendering. For rendering events of different types, use a quick-access method in . + public interface JSONEventRenderer + { + /// RenderAny a given event in the JSON format. + /// the JSON root title + /// the event to render + /// JSON formatted text + String Render(String title, EventBean theEvent); + + /// RenderAny a given event in the JSON format. + /// the event to render + /// JSON formatted text + String Render(EventBean theEvent); + + } +} diff --git a/NEsper.Core/NEsper.Core/client/util/JSONRenderingOptions.cs b/NEsper.Core/NEsper.Core/client/util/JSONRenderingOptions.cs new file mode 100755 index 000000000..b27c75c53 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/util/JSONRenderingOptions.cs @@ -0,0 +1,43 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.client.util +{ + /// + /// JSON rendering options. + /// + public class JSONRenderingOptions + { + /// + /// Ctor. + /// + public JSONRenderingOptions() + { + PreventLooping = true; + } + + /// + /// Indicator whether to prevent looping, by default set to true. Set to false to + /// allow looping in the case where nested properties may refer to themselves, for + /// example. + /// + /// The algorithm to control looping considers the combination of event type and + /// property name for each level of nested property. + /// + /// + /// indicator whether the rendering algorithm prevents looping behavior + /// + public bool PreventLooping { get; set; } + + /// + /// Gets or sets the event property renderer to use. + /// + /// The renderer. + public EventPropertyRenderer Renderer { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/client/util/TimePeriod.cs b/NEsper.Core/NEsper.Core/client/util/TimePeriod.cs new file mode 100755 index 000000000..1e272cb61 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/util/TimePeriod.cs @@ -0,0 +1,234 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Text; + +namespace com.espertech.esper.client.util +{ + [Serializable] + public class TimePeriod + { + public TimePeriod(int? years, int? months, int? weeks, int? days, int? hours, int? minutes, int? seconds, int? milliseconds, int? microseconds) + { + Years = years; + Months = months; + Weeks = weeks; + Days = days; + Hours = hours; + Minutes = minutes; + Seconds = seconds; + Milliseconds = milliseconds; + Microseconds = microseconds; + } + + public TimePeriod() + { + } + + public int? Years { get; set; } + + public int? Months { get; set; } + + public int? Weeks { get; set; } + + public int? Days { get; set; } + + public int? Hours { get; set; } + + public int? Minutes { get; set; } + + public int? Seconds { get; set; } + + public int? Milliseconds { get; set; } + + public int? Microseconds { get; set; } + + public TimePeriod SetYears(int? years) + { + Years = years; + return this; + } + + public TimePeriod SetMonths(int? months) + { + Months = months; + return this; + } + + public TimePeriod SetWeeks(int? weeks) + { + Weeks = weeks; + return this; + } + + public TimePeriod SetDays(int? days) + { + Days = days; + return this; + } + + public TimePeriod SetHours(int? hours) + { + Hours = hours; + + return this; + } + + public TimePeriod SetMinutes(int? minutes) + { + Minutes = minutes; + return this; + } + + public TimePeriod SetSeconds(int? seconds) + { + Seconds = seconds; + return this; + } + + public TimePeriod SetMillis(int? milliseconds) + { + Milliseconds = milliseconds; + return this; + } + + public TimePeriod SetMicros(int? microseconds) + { + Microseconds = microseconds; + return this; + } + + protected bool Equals(TimePeriod other) + { + return Years == other.Years && + Months == other.Months && + Weeks == other.Weeks && + Days == other.Days && + Hours == other.Hours && + Minutes == other.Minutes && + Seconds == other.Seconds && + Milliseconds == other.Milliseconds && + Microseconds == other.Microseconds; + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) + return false; + if (ReferenceEquals(this, obj)) + return true; + if (obj.GetType() != this.GetType()) + return false; + return Equals((TimePeriod)obj); + } + + public override int GetHashCode() + { + unchecked + { + var hashCode = Years.GetHashCode(); + hashCode = (hashCode * 397) ^ Months.GetHashCode(); + hashCode = (hashCode * 397) ^ Weeks.GetHashCode(); + hashCode = (hashCode * 397) ^ Days.GetHashCode(); + hashCode = (hashCode * 397) ^ Hours.GetHashCode(); + hashCode = (hashCode * 397) ^ Minutes.GetHashCode(); + hashCode = (hashCode * 397) ^ Seconds.GetHashCode(); + hashCode = (hashCode * 397) ^ Milliseconds.GetHashCode(); + hashCode = (hashCode * 397) ^ Microseconds.GetHashCode(); + return hashCode; + } + } + + public string ToStringISO8601() + { + var buf = new StringBuilder(); + if (Years != null) + { + Append(buf, Years, "Y"); + } + if (Months != null) + { + Append(buf, Months, "M"); + } + if (Weeks != null) + { + Append(buf, Weeks, "W"); + } + if (Days != null) + { + Append(buf, Days, "D"); + } + if (Hours != null || Minutes != null || Seconds != null) + { + buf.Append("T"); + if (Hours != null) + { + Append(buf, Hours, "H"); + } + if (Minutes != null) + { + Append(buf, Minutes, "M"); + } + if (Seconds != null) + { + Append(buf, Seconds, "S"); + } + } + return buf.ToString(); + } + + public int? LargestAbsoluteValue() + { + int? absMax = null; + if (Years != null && (absMax == null || Math.Abs(Years.Value) > absMax)) + { + absMax = Math.Abs(Years.Value); + } + if (Months != null && (absMax == null || Math.Abs(Months.Value) > absMax)) + { + absMax = Math.Abs(Months.Value); + } + if (Weeks != null && (absMax == null || Math.Abs(Weeks.Value) > absMax)) + { + absMax = Math.Abs(Weeks.Value); + } + if (Days != null && (absMax == null || Math.Abs(Days.Value) > absMax)) + { + absMax = Math.Abs(Days.Value); + } + if (Hours != null && (absMax == null || Math.Abs(Hours.Value) > absMax)) + { + absMax = Math.Abs(Hours.Value); + } + if (Minutes != null && (absMax == null || Math.Abs(Minutes.Value) > absMax)) + { + absMax = Math.Abs(Minutes.Value); + } + if (Seconds != null && (absMax == null || Math.Abs(Seconds.Value) > absMax)) + { + absMax = Math.Abs(Seconds.Value); + } + if (Milliseconds != null && (absMax == null || Math.Abs(Milliseconds.Value) > absMax)) + { + absMax = Math.Abs(Milliseconds.Value); + } + if (Microseconds != null && (absMax == null || Math.Abs(Microseconds.Value) > absMax)) + { + absMax = Math.Abs(Microseconds.Value); + } + return absMax; + } + + private void Append(StringBuilder buf, int? units, string unit) + { + buf.Append(units.ToString()); + buf.Append(unit); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/client/util/XMLEventRenderer.cs b/NEsper.Core/NEsper.Core/client/util/XMLEventRenderer.cs new file mode 100755 index 000000000..c93458b07 --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/util/XMLEventRenderer.cs @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.client.util +{ + /// + /// Renderer for an event into the XML textual format. + /// + /// A renderer is dedicated to rendering only a certain type of events and subtypes + /// of that type, as the render cache type metadata and prepares structures to + /// enable fast rendering. + /// + /// For rendering events of different types, use a quick-access method in . + /// + public interface XMLEventRenderer + { + /// + /// RenderAny a given event in the XML format. + /// + /// the name of the root element, may include namespace information + /// the event to render + /// + /// XML formatted text + /// + String Render(String rootElementName, EventBean theEvent); + } +} diff --git a/NEsper.Core/NEsper.Core/client/util/XMLRenderingOptions.cs b/NEsper.Core/NEsper.Core/client/util/XMLRenderingOptions.cs new file mode 100755 index 000000000..6a770b41b --- /dev/null +++ b/NEsper.Core/NEsper.Core/client/util/XMLRenderingOptions.cs @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.client.util +{ + /// + /// XML rendering options. + /// + public class XMLRenderingOptions + { + /// + /// Ctor. + /// + public XMLRenderingOptions() + { + PreventLooping = true; + IsDefaultAsAttribute = false; + } + + /// + /// Indicator whether to prevent looping, by default set to true. Set to false to + /// allow looping in the case where nested properties may refer to themselves, for + /// example. + /// + /// The algorithm to control looping considers the combination of event type and + /// property name for each level of nested property. + /// + /// + /// indicator whether the rendering algorithm prevents looping behavior + /// + public bool PreventLooping { get; set; } + + /// + /// Indicator whether simple properties are rendered as attributes, this setting is + /// false by default thereby simple properties are rendered as elements. + /// + /// + /// true for simple properties rendered as attributes + /// + public bool IsDefaultAsAttribute { get; set; } + + /// + /// Gets or sets the event property renderer to use. + /// + /// The renderer. + public EventPropertyRenderer Renderer { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/collection/ArrayBackedCollection.cs b/NEsper.Core/NEsper.Core/collection/ArrayBackedCollection.cs new file mode 100755 index 000000000..b524e5dc6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/collection/ArrayBackedCollection.cs @@ -0,0 +1,194 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections; +using System.Collections.Generic; + +using com.espertech.esper.compat; + +namespace com.espertech.esper.collection +{ + /// + /// A fast collection backed by an array with severe limitations. Allows direct access to the backing array + /// - this must be used with care as old elements could be in the array and the array is only valid until + /// the number of elements indicated by size. + /// + /// : only the add, size and clear methods of the collection interface. + /// + /// When running out of space for the underlying array, allocates a new array of double the size + /// of the current array. + /// + /// Not synchronized and not thread-safe. + /// + /// + public class ArrayBackedCollection : ICollection + { + private int _lastIndex; + private int _currentIndex; + private T[] _handles; + + /// Ctor. + /// is the initial size of the backing array. + public ArrayBackedCollection(int currentSize) + { + _lastIndex = currentSize - 1; + _currentIndex = 0; + _handles = new T[currentSize]; + } + + /// + /// Adds an item to the . + /// + /// The object to add to the . + /// The is read-only. + + public void Add(T item) + { + if (_currentIndex <= _lastIndex) + { + _handles[_currentIndex++] = item; + return; + } + + // allocate more by duplicating the current size + int newSize = _lastIndex * 2 + 2; + T[] newHandles = new T[newSize]; + _handles.CopyTo(newHandles, 0); + _handles = newHandles; + _lastIndex = newSize - 1; + + // add + _handles[_currentIndex++] = item; + return; + } + + /// + /// Removes all items from the . + /// + /// The is read-only. + public void Clear() + { + _currentIndex = 0; + } + + /// + /// Gets the number of elements contained in the . + /// + /// + /// The number of elements contained in the . + public int Count + { + get { return _currentIndex; } + } + + /// + /// Returns the backing object array, valid until the current size. + /// + /// Applications must ensure to not read past current size as old elements can be encountered. + /// + /// backing array + public T[] Array + { + get { return _handles; } + } + + /// + /// Gets a value indicating whether this instance is empty. + /// + /// true if this instance is empty; otherwise, false. + public bool IsEmpty + { + get { throw new UnsupportedOperationException(); } + } + + /// + /// Determines whether the contains a specific value. + /// + /// The object to locate in the . + /// + /// true if item is found in the ; otherwise, false. + /// + public bool Contains(T item) + { + throw new UnsupportedOperationException(); + } + + /// + /// Returns an enumerator that iterates through the collection. + /// + /// + /// A that can be used to iterate through the collection. + /// + public IEnumerator GetEnumerator() + { + for (int ii = 0; ii < _currentIndex; ii++ ) + { + yield return _handles[ii]; + } + } + + /// + /// Returns an enumerator that iterates through a collection. + /// + /// + /// An object that can be used to iterate through the collection. + /// + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + /// + /// Copies the elements of the to an , starting at a particular index. + /// + /// The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing. + /// The zero-based index in array at which copying begins. + /// arrayIndex is less than 0. + /// array is null. + /// array is multidimensional.-or-arrayIndex is equal to or greater than the length of array.-or-The number of elements in the source is greater than the available space from arrayIndex to the end of the destination array.-or-Type T cannot be cast automatically to the type of the destination array. + public void CopyTo(T[] array, int arrayIndex) + { + throw new UnsupportedOperationException(); + } + + /// + /// Gets a value indicating whether the is read-only. + /// + /// + /// true if the is read-only; otherwise, false. + public bool IsReadOnly + { + get { return false; } + } + + /// + /// Removes the first occurrence of a specific object from the . + /// + /// The object to remove from the . + /// + /// true if item was successfully removed from the ; otherwise, false. This method also returns false if item is not found in the original . + /// + /// The is read-only. + public bool Remove(T item) + { + throw new UnsupportedOperationException(); + } + + + public Object[] ToArray() + { + throw new UnsupportedOperationException(); + } + + public T[] ToArray(T[] a) + { + throw new UnsupportedOperationException(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/collection/ArrayWrap.cs b/NEsper.Core/NEsper.Core/collection/ArrayWrap.cs new file mode 100755 index 000000000..af41ad882 --- /dev/null +++ b/NEsper.Core/NEsper.Core/collection/ArrayWrap.cs @@ -0,0 +1,102 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections; +using System.Collections.Generic; + +namespace com.espertech.esper.collection +{ + public class ArrayWrap : IList + { + private T[] _handles; + + public ArrayWrap(int currentSize) + { + _handles = new T[currentSize]; + } + + public void Expand(int size) + { + var newSize = _handles.Length + size; + var newHandles = new T[newSize]; + System.Array.Copy(_handles, 0, newHandles, 0, _handles.Length); + _handles = newHandles; + } + + public T this[int index] + { + get { return _handles[index]; } + set { _handles[index] = value; } + } + + public T[] Array + { + get { return _handles; } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public IEnumerator GetEnumerator() + { + return ((IList) _handles).GetEnumerator(); + } + + public void Add(T item) + { + ((IList) _handles).Add(item); + } + + public void Clear() + { + ((IList) _handles).Clear(); + } + + public bool Contains(T item) + { + return ((IList)_handles).Contains(item); + } + + public void CopyTo(T[] array, int arrayIndex) + { + ((IList) _handles).CopyTo(array, arrayIndex); + } + + public bool Remove(T item) + { + return ((IList)_handles).Remove(item); + } + + public int Count + { + get { return ((IList) _handles).Count; } + } + + public bool IsReadOnly + { + get { return ((IList)_handles).IsReadOnly; } + } + + public int IndexOf(T item) + { + return ((IList) _handles).IndexOf(item); + } + + public void Insert(int index, T item) + { + ((IList)_handles).Insert(index, item); + } + + public void RemoveAt(int index) + { + ((IList)_handles).RemoveAt(index); + } + } +} diff --git a/NEsper.Core/NEsper.Core/collection/CombinationEnumeration.cs b/NEsper.Core/NEsper.Core/collection/CombinationEnumeration.cs new file mode 100755 index 000000000..6ea7d8504 --- /dev/null +++ b/NEsper.Core/NEsper.Core/collection/CombinationEnumeration.cs @@ -0,0 +1,152 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.compat; + +namespace com.espertech.esper.collection +{ + /// + /// Provided a 2-dimensional array of values, provide all possible combinations: + ///
    +    ///     For example, an array { {1}, {"A", "B"}, {"X", "Y"} } provides these combinations:
    +    ///        1 A X
    +    ///        1 A Y
    +    ///        1 B X
    +    ///        1 B Y
    +    /// 
    . + /// Usage Note: Do not hold on to the returned object array as {@link #nextElement()} returns the same array + /// with changed values for each enumeration. + ///

    + /// Each array element must be non-null and length 1 or more. + ///

    + ///

    + /// Does not detect duplicates in values. + ///

    + ///

    + /// Allows any number for the first dimension. + ///

    + ///

    + /// The algorithm adds 1 to the right and overflows until done. + ///

    + ///
    + public class CombinationEnumeration : IEnumerator + { + private readonly IEnumerator _subEnumerator; + + public CombinationEnumeration(Object[][] combinations) + { + _subEnumerator = New(combinations).GetEnumerator(); + } + + object IEnumerator.Current + { + get { return Current; } + } + + public object[] Current + { + get { return _subEnumerator.Current; } + } + + public bool MoveNext() + { + return _subEnumerator.MoveNext(); + } + + /// + /// Semi-linear syntactic view of the enumeration process + /// + /// + /// + + private static IEnumerable New(Object[][] combinations) + { + if (combinations.Any(element => element == null || element.Length < 1)) + { + throw new ArgumentException("Expecting non-null element of minimum length 1"); + } + + return NewInternal(combinations); + } + + public static IEnumerable FromZeroBasedRanges(int[] zeroBasedRanges) + { + var combinations = new Object[zeroBasedRanges.Length][]; + for (int i = 0; i < zeroBasedRanges.Length; i++) + { + combinations[i] = new Object[zeroBasedRanges[i]]; + for (int j = 0; j < zeroBasedRanges[i]; j++) + { + combinations[i][j] = j; + } + } + return NewInternal(combinations); + } + + /// + /// Guts of the enumeration process go here. This allows the method above to + /// validate the inputs while this portion does the rest on demand. + /// + /// + /// + + private static IEnumerable NewInternal(object[][] combinations) + { + var current = new int[combinations.Length]; + var prototype = new Object[combinations.Length]; + var hasMore = true; + + while (hasMore) + { + Populate(combinations, prototype, current); + hasMore = DetermineNext(combinations, prototype, current); + yield return prototype; + } + } + + private static void Populate(object[][] combinations, object[] prototype, int[] current) + { + for (int i = 0; i < prototype.Length; i++) + { + prototype[i] = combinations[i][current[i]]; + } + } + + private static bool DetermineNext(object[][] combinations, object[] prototype, int[] current) + { + for (int i = combinations.Length - 1; i >= 0; i--) + { + int max = combinations[i].Length; + if (current[i] < max - 1) + { + current[i]++; + return true; + } + // overflowing + current[i] = 0; + } + + return false; + } + + + public void Reset() + { + throw new UnsupportedOperationException(); + } + + public void Dispose() + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/collection/DualWorkQueue.cs b/NEsper.Core/NEsper.Core/collection/DualWorkQueue.cs new file mode 100755 index 000000000..f9b0f9d8f --- /dev/null +++ b/NEsper.Core/NEsper.Core/collection/DualWorkQueue.cs @@ -0,0 +1,39 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.collection +{ + /// + /// Work queue wherein items can be added to the front and to the back, wherein both front and back + /// have a given order, with the idea that all items of the front queue get processed before any + /// given single item of the back queue gets processed. + /// + public class DualWorkQueue + { + /// Ctor. + public DualWorkQueue() + { + FrontQueue = new ArrayDeque(); + BackQueue = new ArrayDeque(); + } + + /// + /// Items to be processed first, in the order to be processed. + /// + /// front queue + public ArrayDeque FrontQueue { get; private set; } + + /// + /// Items to be processed after front-queue is empty, in the order to be processed. + /// + /// back queue + public ArrayDeque BackQueue { get; private set; } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/collection/FIFOHashSet.cs b/NEsper.Core/NEsper.Core/collection/FIFOHashSet.cs new file mode 100755 index 000000000..360ad6651 --- /dev/null +++ b/NEsper.Core/NEsper.Core/collection/FIFOHashSet.cs @@ -0,0 +1,726 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; + +namespace com.espertech.esper.collection +{ + /// + /// FIFOHashSet is a collection that : "set" principals. Members of a set + /// are unique and can only occur once. Additionally, iteration of the set is + /// governed by first-in first-out principal. This means that the order in which + /// items are added to the set is preserved through iteration. + /// + + [Serializable] + public sealed class FIFOHashSet : ISet + { + private int _primeIndex; + + /// + /// NodeTable of nodes ... + /// + private Node[] _nodeTable; + + /// + /// NodeTable that is indexed by hash code and points to the first node + /// in the chain for that hash code. + /// + private int[] _hashIndex; + + /// + /// MapIndex that represents the first valid node in the ordered list. Value should + /// be -1 if there is no head value. + /// + private int _headIndex; + + /// + /// MapIndex that represents the last valid node in the ordered list. Value should be + /// -1 if there is no last value. + /// + private int _tailIndex; + + /// + /// Head of the free node chain + /// + private int _freeListHead; + + /// + /// Version of the collection. + /// + private int _version; + + /// + /// Total number of entries in the set. + /// + private int _nodeCount; + + /// + /// # of nodes that overlap in the same bucket + /// + private int _collisions; + + public double Load + { + get { return (_collisions * 100.0) / _hashIndex.Length; } + } + + private static readonly int[] PrimeTable = + { + 67, + 131, + 257, + 521, + 1031, + 2053, + 4099, + 8209, + 16411, + 32771, + 65537, + 131101, + 262147, + 524309, + 1048583, + 2097169, + 4194319, + 8388617, + 16777259, + 33554467, + 67108879, + 134217757, + 268435459, + 536870923, + }; + + /// + /// Initializes a new instance of the class. + /// + /// The source collection. + public FIFOHashSet(ICollection sourceCollection) + : this(sourceCollection.Count) + { + foreach (var item in sourceCollection) { + Add(item); + } + } + + /// + /// Initializes a new instance of the class. + /// + /// The minimum capacity. + public FIFOHashSet(int minCapacity) + { + for (int ii = 0; ii < PrimeTable.Length; ii++) + { + if (PrimeTable[ii] > minCapacity) + { + _primeIndex = ii; + break; + } + } + + var tableSize = PrimeTable[_primeIndex]; + + _headIndex = -1; + _tailIndex = -1; + _freeListHead = -1; + + _hashIndex = new int[tableSize]; + for (int ii = 0; ii < tableSize; ii++) + _hashIndex[ii] = -1; + + _nodeTable = new Node[tableSize]; + _nodeCount = 0; + _collisions = 0; + _version = 0; + } + + /// + /// Initializes a new instance of the class. + /// + public FIFOHashSet() + { + _primeIndex = 0; + + var tableSize = PrimeTable[_primeIndex]; + + _headIndex = -1; + _tailIndex = -1; + _freeListHead = -1; + + _hashIndex = new int[tableSize]; + for (int ii = 0; ii < tableSize; ii++) + _hashIndex[ii] = -1; + + _nodeTable = new Node[tableSize]; + _nodeCount = 0; + _collisions = 0; + _version = 0; + } + + /// + /// Returns an enumerator that iterates through a collection. + /// + /// + /// An object that can be used to iterate through the collection. + /// + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + /// + /// Returns an enumerator that iterates through the collection. + /// + /// + /// A that can be used to iterate through the collection. + /// + public IEnumerator GetEnumerator() + { + var versionAtHead = _version; + var nodeIndex = _nodeTable; + + for (var index = _headIndex; index != -1; index = nodeIndex[index].NextNodeInOrder) + { + if (versionAtHead != _version) + { + throw new InvalidOperationException("Collection modified"); + } + + yield return nodeIndex[index].Value; + } + } + + /// + /// Iterates over the collection performing one operation on each element in + /// the set. + /// + /// The action. + public void ForEach(Action action) + { + var versionAtHead = _version; + var nodeIndex = _nodeTable; + + for (var index = _headIndex; index != -1; index = nodeIndex[index].NextNodeInOrder) + { + if (versionAtHead != _version) + { + throw new InvalidOperationException("Collection modified"); + } + + action.Invoke(nodeIndex[index].Value); + } + } + + /// + /// Removes all items from the . + /// + /// + /// The is read-only. + /// + public void Clear() + { +#if DIAGNOSTICS + System.Diagnostics.Debug.Print("Clear: {0}", id); +#endif + + _primeIndex = 0; + + var tableSize = PrimeTable[_primeIndex]; + + _headIndex = -1; + _tailIndex = -1; + _freeListHead = -1; + + _hashIndex = new int[tableSize]; + for (int ii = 0; ii < tableSize; ii++) + _hashIndex[ii] = -1; + + _nodeAllocIndex = 0; + _nodeTable = new Node[tableSize]; + _nodeCount = 0; + _collisions = 0; + ++_version; + } + + /// + /// Gets an enumeration of all items in a chain. + /// + /// MapIndex of the hash index. + /// + internal IEnumerable GetChain(int hashIndexIndex) + { + var nodeTable = _nodeTable; + for (var nodeIndex = _hashIndex[hashIndexIndex]; nodeIndex != -1; nodeIndex = nodeTable[nodeIndex].NextNodeInChain) + { + yield return nodeTable[nodeIndex]; + } + } + + /// + /// Gets the histogram. + /// + /// + public String DebugHashDistribution() + { + var basicHistogram = new SortedDictionary(); + + var length = _hashIndex.Length; + for (int ii = 0; ii < length; ii++) + { + int chainCount = GetChain(ii).Count(); + int[] chainCountMatch; + + if (basicHistogram.TryGetValue(chainCount, out chainCountMatch)) + { + chainCountMatch[0]++; + } + else + { + basicHistogram[chainCount] = new[] { 1 }; + } + } + + var writer = new StringWriter(); + foreach (var entry in basicHistogram) + { + writer.WriteLine("{0}\t{1}", entry.Key, entry.Value[0]); + } + + return writer.ToString(); + } + + /// + /// Indicates the index where the next node needs to be allocated from. + /// + private int _nodeAllocIndex; + + /// + /// Allocates a node for use and return the index of the node. + /// + /// The item. + /// The hash code. + /// + private int AllocNode(T item, int hashCode) + { + // Allocate from the freeList first. If there are no nodes available on + // the freeList then allocate from the current "count" on. If the freeList + // is empty then we have a completely allocated list from 0 to count - 1. + if (_freeListHead == -1) + { + // No items on the freeList. + // Space must be allocated from the existing node table. + int index = _nodeAllocIndex; + if (index == _nodeTable.Length) + { + var newTableSize = _nodeTable.Length * 2; + var newTable = new Node[newTableSize]; + Array.Copy(_nodeTable, 0, newTable, 0, _nodeTable.Length); + _nodeTable = newTable; + ReIndex(); + } + + _nodeTable[index].SetValues(item, hashCode); + _nodeAllocIndex++; + return index; + } + else + { + int index = _freeListHead; + _freeListHead = _nodeTable[index].NextNodeInChain; + _nodeTable[index].SetValues(item, hashCode); + return index; + } + } + + /// + /// Reindexes the internal bucket table. + /// + private void ReIndex() + { + _primeIndex++; + var newHashIndexLength = PrimeTable[_primeIndex]; + var newHashIndex = new int[newHashIndexLength]; + for (int ii = 0; ii < newHashIndexLength; ii++) + { + newHashIndex[ii] = -1; + } + + var collisions = 0; + var nodeTable = _nodeTable; + + for (var nodeIndex = _headIndex; nodeIndex != -1; nodeIndex = nodeTable[nodeIndex].NextNodeInOrder) + { + var node = nodeTable[nodeIndex]; + // Modulus the hash code with new table size + var bucket = node.HashCode % newHashIndexLength; + // Get the current entry at the bucket + var entry = newHashIndex[bucket]; + // Attach the node at the head of the bucket chain + nodeTable[nodeIndex].NextNodeInChain = entry; + newHashIndex[bucket] = nodeIndex; + // Increment the collision space if appropriate + if (entry != -1) collisions++; + } + + _hashIndex = newHashIndex; + _collisions = collisions; + } + + public void UnionWith(IEnumerable other) + { + throw new NotSupportedException(); + } + + public void IntersectWith(IEnumerable other) + { + throw new NotSupportedException(); + } + + public void ExceptWith(IEnumerable other) + { + throw new NotSupportedException(); + } + + public void SymmetricExceptWith(IEnumerable other) + { + throw new NotSupportedException(); + } + + public bool IsSubsetOf(IEnumerable other) + { + throw new NotSupportedException(); + } + + public bool IsSupersetOf(IEnumerable other) + { + throw new NotSupportedException(); + } + + public bool IsProperSupersetOf(IEnumerable other) + { + throw new NotSupportedException(); + } + + public bool IsProperSubsetOf(IEnumerable other) + { + throw new NotSupportedException(); + } + + public bool Overlaps(IEnumerable other) + { + throw new NotSupportedException(); + } + + public bool SetEquals(IEnumerable other) + { + throw new NotSupportedException(); + } + + /// + /// Adds the specified item. + /// + /// The item. + void ICollection.Add(T item) + { + Add(item); + } + + /// + /// Adds an item to the . + /// + /// The object to add to the . + /// + /// The is read-only. + /// + public bool Add(T item) + { + var nodeTable = _nodeTable; + var hashIndex = _hashIndex; + + // Look for the node in the current space + var hashCode = item.GetHashCode() & 0x7fffffff; + // Get the appropriate node index - remember there are no direct node references + var chainIndex = hashIndex[hashCode % hashIndex.Length]; + + // Scan the chain + int currIndex; + for (currIndex = chainIndex; + currIndex >= 0; + currIndex = nodeTable[currIndex].NextNodeInChain) + { + // Skip entries that do not share the same hashcode + if (nodeTable[currIndex].HashCode == hashCode) + { + // Check for node equality + if (Equals(item, nodeTable[currIndex].Value)) + { + return true; + } + } + } + + // Add the node to our current collection + var nodeIndex = AllocNode(item, hashCode); + + nodeTable = _nodeTable; + hashIndex = _hashIndex; + chainIndex = hashIndex[hashCode % hashIndex.Length]; + + if (_tailIndex != -1) + { + nodeTable[_tailIndex].NextNodeInOrder = nodeIndex; + } + if (_headIndex == -1) + { + _headIndex = nodeIndex; + } + + nodeTable[nodeIndex].SetReferences(_tailIndex, -1, chainIndex); + +#if DIAGNOSTICS + System.Diagnostics.Debug.Print("Add: {0} => {1} / {2} / {3}", + id, + hashCode%hashIndex.Length, + nodeIndex, + chainIndex); +#endif + + _tailIndex = nodeIndex; + + // Add the node to the current chain for the hash + if (chainIndex != -1) _collisions++; + hashIndex[hashCode % hashIndex.Length] = nodeIndex; + + ++_nodeCount; + ++_version; + + return false; + } + + /// + /// Determines whether [contains] [the specified item]. + /// + /// The item. + /// + /// true if [contains] [the specified item]; otherwise, false. + /// + public bool Contains(T item) + { + var nodeTable = _nodeTable; + var hashIndex = _hashIndex; + + // Look for the node in the current space + var hashCode = item.GetHashCode() & 0x7fffffff; + + // Get the appropriate bucket - this indexes into HashIndex + var hashIndexIndex = hashCode % hashIndex.Length; + // Get the appropriate node index - remember there are no direct node references + var headIndex = hashIndex[hashIndexIndex]; + + int currIndex; + for (currIndex = headIndex; + currIndex >= 0; + currIndex = nodeTable[currIndex].NextNodeInChain) + { + // Skip entries that do not share the same hashcode + if (nodeTable[currIndex].HashCode != hashCode) continue; + // Check for node equality + if (Equals(item, nodeTable[currIndex].Value)) + { + return true; // Node already exists in set + } + } + + return false; + } + + /// + /// Copies the elements of the to an , starting at a particular index. + /// + /// The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing. + /// The zero-based index in at which copying begins. + /// Collection modified + public void CopyTo(T[] array, int arrayIndex) + { + var arrCount = array.Length - arrayIndex; + var maxCount = _nodeCount; + if (maxCount < arrCount) + maxCount = arrCount; + + var arrLast = arrayIndex + maxCount; + var versionAtHead = _version; + var nodeIndex = _nodeTable; + + for (var index = _headIndex; index != -1; index = nodeIndex[index].NextNodeInOrder) { + if (versionAtHead != _version) { + throw new InvalidOperationException("Collection modified"); + } + + array[arrayIndex] = nodeIndex[index].Value; + arrayIndex++; + + if (arrayIndex >= arrLast ) { + return; + } + } + } + + /// + /// Removes the first occurrence of a specific object from the . + /// + /// The object to remove from the . + /// + /// true if was successfully removed from the ; otherwise, false. This method also returns false if is not found in the original . + /// + /// + /// The is read-only. + /// + public bool Remove(T item) + { + var nodeTable = _nodeTable; + var hashIndex = _hashIndex; + + // Look for the node in the current space + var hashCode = item.GetHashCode() & 0x7fffffff; + // Get the appropriate bucket - this indexes into HashIndex + var hashIndexIndex = hashCode % hashIndex.Length; + // Get the appropriate node index - remember there are no direct node references + var chainIndex = hashIndex[hashIndexIndex]; + + // Scan the chain + int currIndex, prevIndex; + for (prevIndex = -1, currIndex = chainIndex; + currIndex >= 0; + prevIndex = currIndex, currIndex = nodeTable[currIndex].NextNodeInChain) + { + // Skip entries that do not share the same hashcode + if (nodeTable[currIndex].HashCode == hashCode) + { + // Check for node equality + if (Equals(item, nodeTable[currIndex].Value)) + { + // Node found ... + var prevInOrder = nodeTable[currIndex].PrevNodeInOrder; + var nextInOrder = nodeTable[currIndex].NextNodeInOrder; + var nextInChain = nodeTable[currIndex].NextNodeInChain; + if (prevInOrder != -1) nodeTable[prevInOrder].NextNodeInOrder = nextInOrder; + if (nextInOrder != -1) nodeTable[nextInOrder].PrevNodeInOrder = prevInOrder; + if (_tailIndex == currIndex) _tailIndex = prevInOrder; + if (_headIndex == currIndex) _headIndex = nextInOrder; + if (chainIndex == currIndex) hashIndex[hashIndexIndex] = nextInChain; + if (prevIndex != -1) nodeTable[prevIndex].NextNodeInChain = nextInChain; + + nodeTable[currIndex].SetValues(default(T), -1); + nodeTable[currIndex].SetReferences(-1, _freeListHead, -1); + _freeListHead = currIndex; + + _nodeCount--; + +#if DIAGNOSTICS + System.Diagnostics.Debug.Print("Del: {0} => {1} / {2} / {3} / {4}", + id, + hashIndexIndex, + currIndex, + chainIndex, + nextInChain); +#endif + + return true; + } + } + } + + return false; + } + + /// + /// Gets the number of elements contained in the . + /// + /// + /// + /// The number of elements contained in the . + /// + public int Count + { + get { return _nodeCount; } + } + + /// + /// Gets a value indicating whether the is read-only. + /// + /// + /// true if the is read-only; otherwise, false. + /// + public bool IsReadOnly + { + get { return false; } + } + + /// + /// Each node contains the content for the node and references to + /// the next node in it's respective chain and order. + /// + [StructLayout(LayoutKind.Sequential)] + [Serializable] + internal struct Node + { + /// + /// Gets or sets the next node in chain. + /// + /// The next node in chain. + internal int NextNodeInChain; + + /// + /// Gets or sets the hash code. + /// + /// The hash code. + internal int HashCode; + + /// + /// Gets or sets the value. + /// + /// The value. + internal T Value; + + /// + /// Gets or sets the next node in order. + /// + /// The next node in order. + internal int NextNodeInOrder; + + /// + /// Gets or sets the previous node in order. + /// + internal int PrevNodeInOrder; + + internal void SetReferences(int prevInOrder, int nextInOrder, int nextInChain) + { + PrevNodeInOrder = prevInOrder; + NextNodeInOrder = nextInOrder; + NextNodeInChain = nextInChain; + } + + internal void SetValues(T value, int hashCode) + { + Value = value; + HashCode = hashCode; + NextNodeInChain = -1; + NextNodeInOrder = -1; + PrevNodeInOrder = -1; + } + } + } +} + diff --git a/NEsper.Core/NEsper.Core/collection/FilteredEventEnumerator.cs b/NEsper.Core/NEsper.Core/collection/FilteredEventEnumerator.cs new file mode 100755 index 000000000..98bc70bb6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/collection/FilteredEventEnumerator.cs @@ -0,0 +1,69 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.collection +{ + public class FilteredEventEnumerator + { + public static IEnumerable New( + ExprEvaluator[] filters, + IEnumerable parent, + ExprEvaluatorContext exprEvaluatorContext) + { + if ((filters == null) || (filters.Length == 0)) + { + foreach (var eventBean in parent) + { + yield return eventBean; + } + } + else + { + var evaluatorContext = exprEvaluatorContext; + var eventArray = new EventBean[1]; + foreach (var eventBean in parent) + { + var isFiltered = true; + eventArray[0] = eventBean; + foreach (var filter in filters) + { + var result = filter.Evaluate(new EvaluateParams(eventArray, true, evaluatorContext)); + if (result == null || false.Equals(result)) + { + // Event was filtered; end processing so that we can proceed + // to the next eventBean. + isFiltered = false; + break; + } + } + // Event was not filtered + if (isFiltered) + { + yield return eventBean; + } + } + } + } + + public static IEnumerator Enumerate( + ExprEvaluator[] filters, + IEnumerable parent, + ExprEvaluatorContext exprEvaluatorContext) + { + return New(filters, parent, exprEvaluatorContext).GetEnumerator(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/collection/FlushedEventBuffer.cs b/NEsper.Core/NEsper.Core/collection/FlushedEventBuffer.cs new file mode 100755 index 000000000..12c710add --- /dev/null +++ b/NEsper.Core/NEsper.Core/collection/FlushedEventBuffer.cs @@ -0,0 +1,70 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; + +namespace com.espertech.esper.collection +{ + /// + /// Buffer for events - accumulates events until flushed. + /// + + public class FlushedEventBuffer + { + private int _remainEventsCount; + private readonly List _remainEvents = + new List(); + + /// Add an event array to buffer. + /// to add + public void Add(EventBean[] events) + { + if (events != null) + { + _remainEvents.Add(events); + _remainEventsCount += events.Length; + } + } + + /// + /// Get the events currently buffered. Returns null if the buffer is empty. Flushes the buffer. + /// + /// array of events in buffer or null if empty + public EventBean[] GetAndFlush() + { + if (_remainEventsCount == 0) + { + return null; + } + + var index = 0; + var flattened = new EventBean[_remainEventsCount]; + var remainEventsLength = _remainEvents.Count; + for (int ii = 0; ii < remainEventsLength ; ii++) + { + var eventArray = _remainEvents[ii]; + eventArray.CopyTo(flattened, index); + index += eventArray.Length; + } + + _remainEvents.Clear(); + _remainEventsCount = 0; + + return flattened; + } + + /// EmptyFalse buffer. + public void Flush() + { + _remainEvents.Clear(); + _remainEventsCount = 0; + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/collection/InterchangeablePair.cs b/NEsper.Core/NEsper.Core/collection/InterchangeablePair.cs new file mode 100755 index 000000000..abccd517f --- /dev/null +++ b/NEsper.Core/NEsper.Core/collection/InterchangeablePair.cs @@ -0,0 +1,104 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.collection +{ + + /// General-purpose pair of values of any type. The pair equals another pair if + /// the objects that form the pair equal in any order, ie. first pair first object equals (.equals) + /// the second pair first object or second object, and the first pair second object equals the second pair first object + /// or second object. + /// + public sealed class InterchangeablePair + { + /// + /// Gets or sets the first value within the pair. + /// + public FirstT First { get; set; } + + /// + /// Gets or sets the second value within the pair. + /// + public SecondT Second { get; set; } + + /// Construct pair of values. + /// is the first value + /// is the second value + + public InterchangeablePair(FirstT first, SecondT second) + { + First = first; + Second = second; + } + + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// + /// true if the specified is equal to the current ; otherwise, false. + /// + public override bool Equals(Object obj) + { + if (this == obj) + { + return true; + } + + var other = obj as InterchangeablePair; + if (other == null) + { + return false; + } + + return + (Equals(First, other.First) && Equals(Second, other.Second)) || + (Equals(First, other.Second) && Equals(Second, other.First)); + } + + /// + /// Serves as a hash function for a particular type. + /// + /// + /// A hash code for the current . + /// + public override int GetHashCode() + { + var o1 = First; + var o2 = Second; + + var a1 = ReferenceEquals(o1, null); + var a2 = ReferenceEquals(o2, null); + + if (a1 && a2) return 0; + if (a1) return o2.GetHashCode(); + if (a2) return o1.GetHashCode(); + + var h1 = o1.GetHashCode(); + var h2 = o2.GetHashCode(); + + if (h1 > h2) + return h1 * 397 ^ h2; + else + return h2 * 397 ^ h1; + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override String ToString() + { + return "Pair [" + First + ':' + Second + ']'; + } + } +} diff --git a/NEsper.Core/NEsper.Core/collection/MixedEventBeanAndCollectionEnumeratorBase.cs b/NEsper.Core/NEsper.Core/collection/MixedEventBeanAndCollectionEnumeratorBase.cs new file mode 100755 index 000000000..95dcd4349 --- /dev/null +++ b/NEsper.Core/NEsper.Core/collection/MixedEventBeanAndCollectionEnumeratorBase.cs @@ -0,0 +1,138 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; + +namespace com.espertech.esper.collection +{ + public abstract class MixedEventBeanAndCollectionEnumeratorBase + : IEnumerator + { + private readonly IEnumerator _eventBeanEnum; + + protected abstract Object GetValue(Object keyValue); + + protected MixedEventBeanAndCollectionEnumeratorBase(IEnumerable keyEnumerator) + : this(keyEnumerator.GetEnumerator()) + { + } + + protected MixedEventBeanAndCollectionEnumeratorBase(IEnumerator keyEnumerator) + { + _eventBeanEnum = MakeEnumerator(keyEnumerator); + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + } + + /// + /// Sets the enumerator to its initial position, which is before the first element in the collection. + /// + /// The collection was modified after the enumerator was created. + public void Reset() + { + throw new InvalidOperationException(); + } + + /// + /// Advances the enumerator to the next element of the collection. + /// + /// + /// true if the enumerator was successfully advanced to the next element; false if the enumerator has passed the end of the collection. + /// + /// The collection was modified after the enumerator was created. + public bool MoveNext() + { + return _eventBeanEnum.MoveNext(); + } + + /// + /// Gets the current. + /// + /// The current. + object IEnumerator.Current + { + get { return _eventBeanEnum.Current; } + } + + /// + /// Gets the current. + /// + /// The current. + public EventBean Current + { + get { return _eventBeanEnum.Current; } + } + + private IEnumerator MakeEnumerator(IEnumerator keyEnumerator) + { + while (keyEnumerator.MoveNext()) + { + var entry = GetValue(keyEnumerator.Current); + if (entry is ICollection) + { + var collection = (ICollection) entry; + foreach (var eventBean in collection) + { + yield return eventBean; + } + } + else if (entry is EventBean) + { + yield return ((EventBean) entry); + } + else if (entry == null) + { + } + else + { + throw new IllegalStateException(); + } + } + } + } + + public class MixedEventBeanAndCollectionEnumerator + : MixedEventBeanAndCollectionEnumeratorBase + { + public Func ProcGetValue { get; set; } + + public MixedEventBeanAndCollectionEnumerator(IEnumerator keyEnumerator, Func procGetValue) + : base(keyEnumerator) + { + ProcGetValue = procGetValue; + } + + public MixedEventBeanAndCollectionEnumerator(IEnumerable keyEnumerator, Func procGetValue) : base(keyEnumerator) + { + ProcGetValue = procGetValue; + } + + public MixedEventBeanAndCollectionEnumerator(IEnumerator keyEnumerator) : base(keyEnumerator) + { + } + + public MixedEventBeanAndCollectionEnumerator(IEnumerable keyEnumerator) : base(keyEnumerator) + { + } + + protected override object GetValue(object keyValue) + { + return ProcGetValue(keyValue); + } + } +} diff --git a/NEsper.Core/NEsper.Core/collection/MultiKey.cs b/NEsper.Core/NEsper.Core/collection/MultiKey.cs new file mode 100755 index 000000000..421d3a9a6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/collection/MultiKey.cs @@ -0,0 +1,125 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.collection +{ + /// Functions as a key value for Maps where keys need to be composite values. + /// The class allows a Map that uses MultiKeyUntyped entries for key values to use multiple objects as keys. + /// It calculates the hashCode from the key objects on construction and caches the hashCode. + /// + + public sealed class MultiKey where T : class + { + /// Returns the key value array. + /// key value array + /// + + public T[] Array + { + get { return _keys; } + } + + private readonly T[] _keys; + private readonly int _hashCode; + + /// Constructor for multiple keys supplied in an object array. + /// is an array of key objects + /// + + public MultiKey(params T[] keys) + { + if (keys == null) + { + throw new ArgumentException("The array of keys must not be null"); + } + + int total = 0; + for (int i = 0; i < keys.Length; i++) + { + if (keys[i] != null) + { + total = (total*397) ^ keys[i].GetHashCode(); + } + } + + _hashCode = total; + _keys = keys; + } + + /// Returns the number of key objects. + /// size of key object array + /// + + public int Count + { + get { return _keys.Length; } + } + + /// Returns the key object at the specified position. + /// is the array position + /// + /// key object at position + /// + + public T this[int index] + { + get { return _keys[index]; } + } + + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// + /// true if the specified is equal to the current ; otherwise, false. + /// + public override bool Equals(Object other) + { + if (other == this) + { + return true; + } + + if (other is MultiKey) + { + MultiKey otherKeys = (MultiKey) other; + return ArrayHelper.AreEqual( _keys, otherKeys._keys ) ; + } + + return false; + } + + /// + /// Serves as a hash function for a particular type. + /// + /// + /// A hash code for the current . + /// + public override int GetHashCode() + { + return _hashCode; + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override String ToString() + { + return "MultiKeyUntyped{{" + _keys.Render() + "}}"; + } + } +} diff --git a/NEsper.Core/NEsper.Core/collection/MultiKeyInt.cs b/NEsper.Core/NEsper.Core/collection/MultiKeyInt.cs new file mode 100755 index 000000000..e6938bda3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/collection/MultiKeyInt.cs @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.collection +{ + public sealed class MultiKeyInt + { + private readonly int[] _keys; + + public MultiKeyInt(int[] keys) + { + _keys = keys; + } + + public int[] Keys + { + get { return _keys; } + } + + public override bool Equals(Object o) + { + if (this == o) + return true; + if (o == null || GetType() != o.GetType()) + return false; + + var that = (MultiKeyInt) o; + return Collections.AreEqual(_keys, that._keys); + } + + public override int GetHashCode() + { + if (_keys.Length == 0) + return 0; + return System.Linq.Enumerable.Aggregate( + _keys, 0, (a, b) => a ^ b.GetHashCode()); + } + } +} diff --git a/NEsper.Core/NEsper.Core/collection/MultiKeyUntyped.cs b/NEsper.Core/NEsper.Core/collection/MultiKeyUntyped.cs new file mode 100755 index 000000000..ec5e16f20 --- /dev/null +++ b/NEsper.Core/NEsper.Core/collection/MultiKeyUntyped.cs @@ -0,0 +1,137 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.util; + +namespace com.espertech.esper.collection +{ + /// + /// Functions as a key value for Maps where keys need to be composite values. The class allows a + /// Map that uses MultiKeyUntyped entries for key values to use multiple objects as keys. It + /// calculates the hashCode from the key objects on construction and caches the hashCode. + /// + [Serializable] + public class MultiKeyUntyped : MetaDefItem + { + private readonly Object[] _keys; + private readonly int _hashCode; + + /// + /// Constructor for multiple keys supplied in an object array. + /// + /// is an array of key objects + public MultiKeyUntyped(Object[] keys) + { + if (keys == null) + { + throw new ArgumentException("The array of keys must not be null"); + } + + unchecked + { + int total = 0; + int length = keys.Length; + + for (int ii = 0; ii < length; ii++) + { + if (keys[ii] != null) + { + total *= 397; + total ^= keys[ii].GetHashCode(); + } + } + + _hashCode = total; + _keys = keys; + } + } + + /// Constructor for a single key object. + /// is the single key object + public MultiKeyUntyped(Object key) + : this(new [] { key }) + { + } + + /// Constructor for a pair of key objects. + /// is the first key object + /// is the second key object + public MultiKeyUntyped(Object key1, Object key2) + : this(new [] { key1, key2 }) + { + } + + /// Constructor for three key objects. + /// is the first key object + /// is the second key object + /// is the third key object + public MultiKeyUntyped(Object key1, Object key2, Object key3) + : this(new [] { key1, key2, key3 }) + { + } + + /// Constructor for four key objects. + /// is the first key object + /// is the second key object + /// is the third key object + /// is the fourth key object + public MultiKeyUntyped(Object key1, Object key2, Object key3, Object key4) + : this(new [] { key1, key2, key3, key4 }) + { + } + + /// Returns the number of key objects. + /// size of key object array + public int Count + { + get { return _keys.Length; } + } + + /// Returns the key object at the specified position. + /// is the array position + /// key object at position + public Object Get(int index) + { + return _keys[index]; + } + + public override bool Equals(Object other) + { + if (ReferenceEquals(this, other)) + { + return true; + } + if (other is MultiKeyUntyped) + { + var otherKeys = (MultiKeyUntyped)other; + return Collections.AreEqual(_keys, otherKeys._keys); + } + return false; + } + + /// Returns keys. + /// keys object array + public object[] Keys + { + get { return _keys; } + } + + public override int GetHashCode() + { + return _hashCode; + } + + public override String ToString() + { + return "MultiKeyUntyped" + _keys.Render(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/collection/MultiKeyUntypedEventPair.cs b/NEsper.Core/NEsper.Core/collection/MultiKeyUntypedEventPair.cs new file mode 100755 index 000000000..535a2c2cd --- /dev/null +++ b/NEsper.Core/NEsper.Core/collection/MultiKeyUntypedEventPair.cs @@ -0,0 +1,108 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.util; + +namespace com.espertech.esper.collection +{ + /// + /// Functions as a key value for Maps where keys need to be composite values, and includes + /// an handle. + /// The class allows a Map that uses MultiKeyUntyped entries for key values to use multiple objects + /// as keys. It calculates the hashCode from the key objects on construction and caches the hashCode. + /// + [Serializable] + public sealed class MultiKeyUntypedEventPair : MetaDefItem + { + [NonSerialized] private readonly Object[] _keys; + [NonSerialized] private readonly EventBean _eventBean = null; + private readonly int _hashCode; + + /// Constructor for multiple keys supplied in an object array. + /// is an array of key objects + /// for pair + public MultiKeyUntypedEventPair(Object[] keys, EventBean eventBean) + { + if (keys == null) + { + throw new ArgumentException("The array of keys must not be null"); + } + + int total = 0; + for (int i = 0; i < keys.Length; i++) + { + if (keys[i] != null) + { + total *= 31; + total ^= keys[i].GetHashCode(); + } + } + + _hashCode = total; + _keys = keys; + _eventBean = eventBean; + } + + /// Returns the event. + /// event + public EventBean EventBean + { + get { return _eventBean; } + } + + /// Returns the number of key objects. + /// size of key object array + public int Count + { + get { return _keys.Length; } + } + + /// Returns the key object at the specified position. + /// is the array position + /// key object at position + public Object Get(int index) + { + return _keys[index]; + } + + public override bool Equals(Object other) + { + if (other == this) + { + return true; + } + if (other is MultiKeyUntypedEventPair) + { + MultiKeyUntypedEventPair otherKeys = (MultiKeyUntypedEventPair)other; + return Collections.AreEqual(_keys, otherKeys._keys); + } + return false; + } + + /// Returns keys. + /// keys object array + public object[] Keys + { + get { return _keys; } + } + + public override int GetHashCode() + { + return _hashCode; + } + + public override String ToString() + { + return "MultiKeyUntyped" + _keys.Render(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/collection/NameParameterCountKey.cs b/NEsper.Core/NEsper.Core/collection/NameParameterCountKey.cs new file mode 100755 index 000000000..3b5a27459 --- /dev/null +++ b/NEsper.Core/NEsper.Core/collection/NameParameterCountKey.cs @@ -0,0 +1,64 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.collection +{ + /// + /// A hash key that include a name and a count, wherein the combination of name and count defines the uniqueness. + /// + public class NameParameterCountKey + { + public NameParameterCountKey(String name, int count) { + Name = name; + Count = count; + } + + public string Name { get; private set; } + + public int Count { get; private set; } + + public bool Equals(NameParameterCountKey other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return other.Count == Count && Equals(other.Name, Name); + } + + /// + /// Determines whether the specified is equal to the current . + /// + /// + /// true if the specified is equal to the current ; otherwise, false. + /// + /// The to compare with the current . 2 + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != typeof (NameParameterCountKey)) return false; + return Equals((NameParameterCountKey) obj); + } + + /// + /// Serves as a hash function for a particular type. + /// + /// + /// A hash code for the current . + /// + /// 2 + public override int GetHashCode() + { + unchecked + { + return (Count*397) ^ (Name != null ? Name.GetHashCode() : 0); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/collection/NamedEntity.cs b/NEsper.Core/NEsper.Core/collection/NamedEntity.cs new file mode 100755 index 000000000..2c6d62c6c --- /dev/null +++ b/NEsper.Core/NEsper.Core/collection/NamedEntity.cs @@ -0,0 +1,96 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.collection +{ + [Serializable] + public sealed class NamedEntity + { + /// + /// Gets or sets the name. + /// + /// The name. + public string Name; + + /// + /// Gets or sets the value. + /// + /// The value. + public TValue Value; + + /// + /// Initializes a new instance of the class. + /// + public NamedEntity() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The name. + /// The value. + public NamedEntity(string name, + TValue value) + { + Name = name; + Value = value; + } + + public bool Equals(NamedEntity other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return Equals(other.Name, Name) && Equals(other.Value, Value); + } + + /// + /// Determines whether the specified is equal to the current . + /// + /// + /// true if the specified is equal to the current ; otherwise, false. + /// + /// The to compare with the current . 2 + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != typeof (NamedEntity)) return false; + return Equals((NamedEntity) obj); + } + + /// + /// Serves as a hash function for a particular type. + /// + /// + /// A hash code for the current . + /// + /// 2 + public override int GetHashCode() + { + unchecked + { + return ((Name != null ? Name.GetHashCode() : 0)*397) ^ Value.GetHashCode(); + } + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + /// 2 + public override string ToString() + { + return string.Format("Name: {0}, Value: {1}", Name, Value); + } + } +} diff --git a/NEsper.Core/NEsper.Core/collection/NullEnumerator.cs b/NEsper.Core/NEsper.Core/collection/NullEnumerator.cs new file mode 100755 index 000000000..a22024ee5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/collection/NullEnumerator.cs @@ -0,0 +1,82 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2015 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.collection +{ + /// + /// Enumerator that never returns an elements. + /// + + public class NullEnumerator : IEnumerator + { + #region IEnumerator Members + + /// + /// Gets the element in the collection at the current position of the enumerator. + /// + /// + /// The element in the collection at the current position of the enumerator. + public T Current + { + get { throw new InvalidOperationException(); } + } + + #endregion + + #region IDisposable Members + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + } + + #endregion + + #region IEnumerator Members + + /// + /// Gets the element in the collection at the current position of the enumerator. + /// + /// + /// The element in the collection at the current position of the enumerator. + object System.Collections.IEnumerator.Current + { + get + { + throw new InvalidOperationException(); + } + } + + /// + /// Advances the enumerator to the next element of the collection. + /// + /// + /// true if the enumerator was successfully advanced to the next element; false if the enumerator has passed the end of the collection. + /// + /// The collection was modified after the enumerator was created. + public bool MoveNext() + { + return false; + } + + /// + /// Sets the enumerator to its initial position, which is before the first element in the collection. + /// + /// The collection was modified after the enumerator was created. + public void Reset() + { + } + + #endregion + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/collection/NumberAscCombinationEnumeration.cs b/NEsper.Core/NEsper.Core/collection/NumberAscCombinationEnumeration.cs new file mode 100755 index 000000000..197cbb0f3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/collection/NumberAscCombinationEnumeration.cs @@ -0,0 +1,140 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections; +using System.Collections.Generic; + +namespace com.espertech.esper.collection +{ + /// + /// Provides an enumeration of each combination of numbers between zero and Count-1 with Count must + /// be at least 1, with each combination cannot have duplicates, with each combination at least + /// one element, with the longest combination gets returned first and the least long combination + /// of the highest Count-1 value last. For example, for Count=3: + /// {0, 1, 2} {0, 1} {0, 2} {1, 2} {0} {1} {2} + /// + public class NumberAscCombinationEnumeration : IEnumerator + { + private readonly int _n; + private int _level; + private int[] _current; + private bool _first; + + public NumberAscCombinationEnumeration(int n) + { + if (n < 1) + { + throw new ArgumentException(); + } + _n = n; + _level = n; + _first = true; + } + + public void Dispose() + { + throw new NotImplementedException(); + } + + public bool MoveNext() + { + if (_first) + { + _current = LevelCurrent(_n); + _first = false; + return _current != null; + } + + ComputeNext(); + + return (_current != null); + } + + public void Reset() + { + throw new NotSupportedException(); + } + + object IEnumerator.Current + { + get { return Current; } + } + + public int[] Current + { + get + { + if (_current == null) + { + throw new InvalidOperationException(); + } + + var copy = new int[_current.Length]; + Array.Copy(_current, 0, copy, 0, _current.Length); + return copy; + } + } + + private void ComputeNext() + { + // determine whether there is a next for the outermost + int last = _current.Length - 1; + if (_current[last] + 1 < _n) + { + _current[last]++; + return; + } + + // overflow + int currOverflowedLevel = last - 1; + while (currOverflowedLevel >= 0) + { + int maxAtPosition = _n - _level + currOverflowedLevel; + if (_current[currOverflowedLevel] < maxAtPosition) + { + _current[currOverflowedLevel]++; + for (int i = currOverflowedLevel + 1; i < _current.Length; i++) + { + _current[i] = _current[i - 1] + 1; + } + return; + } + currOverflowedLevel--; + } + + // bump level down + _level--; + if (_level == 0) + { + _current = null; + } + else + { + _current = LevelCurrent(_level); + } + } + + private static int[] LevelCurrent(int level) + { + var current = new int[level]; + for (int i = 0; i < level; i++) + { + current[i] = i; + } + return current; + } + + private int[] CopyCurrent(int[] current) + { + var updated = new int[current.Length]; + Array.Copy(current, 0, updated, 0, updated.Length); + return updated; + } + } +} diff --git a/NEsper.Core/NEsper.Core/collection/NumberSetPermutationEnumeration.cs b/NEsper.Core/NEsper.Core/collection/NumberSetPermutationEnumeration.cs new file mode 100755 index 000000000..121b1cdf5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/collection/NumberSetPermutationEnumeration.cs @@ -0,0 +1,42 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.collection +{ + /// + /// Based on the this enumeration provides, among a set of supplied integer + /// values, all permutations of order these values can come in, ie. + /// Example: {0, 2, 3} results in 6 enumeration values ending in {3, 2, 0}. + /// + public class NumberSetPermutationEnumeration + { + /// + /// Creates the specified number set. + /// + /// The number set. + /// + public static IEnumerable New(int[] numberSet) + { + var permutationEnumerator = PermutationEnumerator.Create(numberSet.Length).GetEnumerator(); + while (permutationEnumerator.MoveNext()) + { + var permutation = permutationEnumerator.Current; + var result = new int[numberSet.Length]; + for (var i = 0; i < numberSet.Length; i++) + { + result[i] = numberSet[permutation[i]]; + } + + yield return result; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/collection/NumberSetShiftGroupEnumeration.cs b/NEsper.Core/NEsper.Core/collection/NumberSetShiftGroupEnumeration.cs new file mode 100755 index 000000000..5966a0efe --- /dev/null +++ b/NEsper.Core/NEsper.Core/collection/NumberSetShiftGroupEnumeration.cs @@ -0,0 +1,88 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.collection +{ + /// + /// Enumeration that first returns a round-shift-left of all numbers and + /// when that is exhausted it returns number sets using grouped algo until + /// exhausted. + /// + public class NumberSetShiftGroupEnumeration + { + public static IEnumerable New(int[] numberSet) + { + if (numberSet.Length < 6) + { + throw new ArgumentException("Only supported for at least 6-number sets"); + } + + return CreateInternal(numberSet); + } + + internal static IEnumerable CreateInternal(int[] numberSet) + { + for (var shiftCount = 0; shiftCount < numberSet.Length; shiftCount++) { + int[] result = new int[numberSet.Length]; + int count = shiftCount; + for (int i = 0; i < numberSet.Length; i++) { + int index = count + i; + if (index >= numberSet.Length) { + index -= numberSet.Length; + } + result[i] = numberSet[index]; + } + + yield return result; + } + + // Initialize the permutation + // simply always make 4 buckets + var buckets = new Dictionary>(); + for (int i = 0; i < numberSet.Length; i++) + { + int bucketNum = i % 4; + List bucket = buckets.Get(bucketNum); + if (bucket == null) { + bucket = new List(); + buckets[bucketNum] = bucket; + } + + bucket.Add(numberSet[i]); + } + + var permutationEnumerator = PermutationEnumerator.Create(4).GetEnumerator(); + // we throw the first one away, it is the same as a shift result + permutationEnumerator.MoveNext(); + + while(permutationEnumerator.MoveNext()) { + yield return Translate(numberSet, buckets, permutationEnumerator.Current); + } + } + + private static int[] Translate(int[] numberSet, + IDictionary> buckets, + int[] bucketsPermuted) + { + int[] result = new int[numberSet.Length]; + int count = 0; + for (int i = 0; i < bucketsPermuted.Length; i++) { + List bucket = buckets.Get(bucketsPermuted[i]); + foreach (int j in bucket) { + result[count++] = j; + } + } + return result; + } + } +} diff --git a/NEsper.Core/NEsper.Core/collection/OneEventCollection.cs b/NEsper.Core/NEsper.Core/collection/OneEventCollection.cs new file mode 100755 index 000000000..f872f7635 --- /dev/null +++ b/NEsper.Core/NEsper.Core/collection/OneEventCollection.cs @@ -0,0 +1,120 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; + +namespace com.espertech.esper.collection +{ + /// + /// Simple collection that exposes a limited add-and-get interface and + /// that is optimized towards holding a single event, but can hold multiple + /// events. If more then one event is added, the class allocates a linked + /// list for additional events. + /// + public class OneEventCollection + { + private EventBean _firstEvent; + private LinkedList _additionalEvents; + + /// + /// Gets the first event. + /// + /// + /// The first event. + /// + public EventBean FirstEvent + { + get { return _firstEvent; } + } + + /// + /// Gets the additional events. + /// + /// + /// The additional events. + /// + public LinkedList AdditionalEvents + { + get { return _additionalEvents; } + } + + /// + /// Add an event to the collection. + /// + /// is the event to add + public void Add(EventBean theEvent) + { + if (theEvent == null) + { + throw new ArgumentException("Null event not allowed"); + } + + if (_firstEvent == null) + { + _firstEvent = theEvent; + return; + } + + if (_additionalEvents == null) + { + _additionalEvents = new LinkedList(); + } + _additionalEvents.AddLast(theEvent); + } + + /// + /// Returns true if the collection is empty. + /// + /// true if this instance is empty; otherwise, false. + /// true if empty, false if not + public bool IsEmpty() + { + return _firstEvent == null; + } + + /// + /// Returns an array holding the collected events. + /// + /// event array + public EventBean[] ToArray() + { + if (_firstEvent == null) + { + return new EventBean[0]; + } + + if (_additionalEvents == null) + { + return new[] {_firstEvent}; + } + + EventBean[] events = new EventBean[1 + _additionalEvents.Count]; + events[0] = _firstEvent; + + int count = 1; + foreach (EventBean theEvent in _additionalEvents) + { + events[count] = theEvent; + count++; + } + + return events; + } + + public void Add(EventBean[] events) + { + foreach (EventBean ev in events) + { + Add(ev); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/collection/Pair.cs b/NEsper.Core/NEsper.Core/collection/Pair.cs new file mode 100755 index 000000000..3ec9ed538 --- /dev/null +++ b/NEsper.Core/NEsper.Core/collection/Pair.cs @@ -0,0 +1,108 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.collection +{ + /// + /// General-purpose pair of values of any type. The pair only equals another pair if + /// the objects that form the pair equal, ie. first pair first object equals (.equals) the second pair first object, + /// and the first pair second object equals the second pair second object. + /// + [Serializable] + public sealed class Pair + { + /// + /// Gets or sets the first value within pair. + /// + /// The first. + public TFirst First; + + /// + /// Gets or sets the second value within pair. + /// + /// The second. + public TSecond Second; + + /// + /// Construct pair of values. + /// + /// is the first value + /// is the second value + + public Pair(TFirst first, TSecond second) + { + First = first; + Second = second; + } + + /// + /// Initializes a new instance of the class. + /// + /// The key value pair. + public Pair(KeyValuePair keyValuePair) + { + First = keyValuePair.Key; + Second = keyValuePair.Value; + } + + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// + /// true if the specified is equal to the current ; otherwise, false. + /// + public override bool Equals(Object obj) + { + if (this == obj) + { + return true; + } + + var other = obj as Pair; + if (other == null) + { + return false; + } + + return + Equals(First, other.First) && + Equals(Second, other.Second); + } + + /// + /// Serves as a hash function for a particular type. + /// + /// + /// A hash code for the current . + /// + public override int GetHashCode() + { + Object o1 = First; + Object o2 = Second; + + return + (o1 != null ? o1.GetHashCode() * 397 : 0) ^ + (o2 != null ? o2.GetHashCode() : 0); + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override String ToString() + { + return "Pair [" + First + ':' + Second + ']'; + } + } +} diff --git a/NEsper.Core/NEsper.Core/collection/PermutationEnumerator.cs b/NEsper.Core/NEsper.Core/collection/PermutationEnumerator.cs new file mode 100755 index 000000000..bdcc4470b --- /dev/null +++ b/NEsper.Core/NEsper.Core/collection/PermutationEnumerator.cs @@ -0,0 +1,134 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.collection +{ + /// Provides a Count! (n-faculty) number of permutations for Count elements. + /// Example: for 3 elements provides 6 permutations exactly as follows: + /// {0, 1, 2} + /// {0, 2, 1} + /// {1, 0, 2} + /// {1, 2, 0} + /// {2, 0, 1} + /// {2, 1, 0} + /// + + public class PermutationEnumerator + { + /// + /// Creates the permutation. + /// + /// The number of elements. + /// + public static IEnumerable Create(int numElements) + { + if (numElements < 1) { + throw new ArgumentException("Invalid element number of 1"); + } + + return CreateInternal(numElements); + } + + public static IEnumerable CreateInternal(int numElements) + { + var factors = GetFactors(numElements); + var maxNumPermutation = Faculty(numElements); + + for (var currentPermutation = 0; currentPermutation < maxNumPermutation; currentPermutation++ ) + { + var element = GetPermutation(numElements, currentPermutation, factors); + yield return element; + } + } + + /// + /// Gets the permutation. + /// + /// The num elements. + /// The permutation. + /// factors for each index + /// permutation + + public static int[] GetPermutation(int numElements, int permutation, int[] factors) + { + /* + Example: + numElements = 4 + permutation = 21 + factors = {6, 2, 1, 0} + + Init: out {0, 1, 2, 3} + + 21 / 6 == index 3 -> result {3}, out {0, 1, 2} + remainder 21 - 3 * 6 == 3 + 3 / 2 = second number == index 1 -> result {3, 1}, out {0, 2} + remainder 3 - 1 * 2 == 1 + == index 1 -> result {3, 1, 2} out {0} + */ + + var result = new int[numElements]; + var outList = new List(); + for (int i = 0; i < numElements; i++) + { + outList.Add(i); + } + int currentVal = permutation; + + for (int position = 0; position < numElements - 1; position++) + { + int factor = factors[position]; + int index = currentVal / factor; + result[position] = outList[index]; + outList.RemoveAt(index); + currentVal -= index * factor; + } + result[numElements - 1] = outList[0]; + + return result; + } + + /// Returns factors for computing the permutation. + /// number of factors to compute + /// factors list + + public static int[] GetFactors(int numElements) + { + int[] facultyFactors = new int[numElements]; + + for (int i = 0; i < numElements - 1; i++) + { + facultyFactors[i] = Faculty(numElements - i - 1); + } + + return facultyFactors; + } + + /// Computes faculty of Count. + /// to compute faculty for + /// Count! + + public static int Faculty(int num) + { + if (num == 0) + { + return 0; + } + + int fac = 1; + for (int i = 1; i <= num; i++) + { + fac *= i; + } + return fac; + } + } +} diff --git a/NEsper.Core/NEsper.Core/collection/RefCountedMap.cs b/NEsper.Core/NEsper.Core/collection/RefCountedMap.cs new file mode 100755 index 000000000..a3797c3c0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/collection/RefCountedMap.cs @@ -0,0 +1,131 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; + +using com.espertech.esper.compat; + +namespace com.espertech.esper.collection +{ + /// + /// Reference-counting map based on a HashMap implementation that stores as a value a pair of value and reference counter. + /// The class provides a reference method that takes a key + /// and increments the reference count for the key. It also provides a de-reference method that takes a key and + /// decrements the reference count for the key, and removes the key if the reference count reached zero. + /// Null values are not allowed as keys. + /// + + public class RefCountedMap + { + private readonly IDictionary> _refMap; + + /// + /// Constructor. + /// + + public RefCountedMap() + { + _refMap = new Dictionary>(); + } + + + /// + /// Gets or sets the item with the specified key. + /// + /// + public virtual V this[K key] + { + get + { + Pair refValue = null; + if (!_refMap.TryGetValue(key, out refValue)) + { + return default(V); + } + return refValue.First; + } + + set + { + if (key == null) + { + throw new ArgumentException("Collection does not allow null key values"); + } + if (_refMap.ContainsKey(key)) + { + throw new IllegalStateException("Value value already in collection"); + } + + Pair refValue = new Pair(value, 1); + _refMap[key] = refValue; + + //return val; + } + } + + /// Increase the reference count for a given key by one. + /// Throws an ArgumentException if the key was not found. + /// + /// is the key to increase the ref count for + /// + + public void Reference(K key) + { + Pair refValue ; + if (!_refMap.TryGetValue(key, out refValue)) + { + throw new IllegalStateException("Value value not found in collection"); + } + refValue.Second = refValue.Second + 1; + } + + /// Decreases the reference count for a given key by one. Returns true if the reference count reaches zero. + /// Removes the key from the collection when the reference count reaches zero. + /// Throw an ArgumentException if the key is not found. + /// + /// to de-reference + /// + /// true to indicate the reference count reached zero, false to indicate more references to the key exist. + /// + + public virtual bool Dereference(K key) + { + Pair refValue ; + if (!_refMap.TryGetValue(key, out refValue)) + { + throw new IllegalStateException("Value value not found in collection"); + } + + int refCounter = refValue.Second; + if (refCounter < 1) + { + throw new IllegalStateException("Unexpected reference counter value " + refValue.Second + " encountered for key " + key); + } + + // Remove key on dereference of last reference + if (refCounter == 1) + { + _refMap.Remove(key); + return true; + } + + refValue.Second = refCounter - 1; + return false; + } + + /// + /// Clear out the collection. + /// + public virtual void Clear() + { + _refMap.Clear(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/collection/RefCountedSet.cs b/NEsper.Core/NEsper.Core/collection/RefCountedSet.cs new file mode 100755 index 000000000..6e15f8912 --- /dev/null +++ b/NEsper.Core/NEsper.Core/collection/RefCountedSet.cs @@ -0,0 +1,231 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.collection +{ + /// reference-counting set based on a HashMap implementation that stores keys and a reference counter for + /// each unique key value. Each time the same key is added, the reference counter increases. + /// Each time a key is removed, the reference counter decreases. + /// + + public class RefCountedSet + { + private bool _hasNullEntry; + private int _nullEntry; + private readonly IDictionary _refSet; + private int _numValues; + + /// + /// Constructor. + /// + + public RefCountedSet() + { + _refSet = new Dictionary(); + } + + public RefCountedSet(IDictionary refSet, int numValues) + { + _refSet = refSet; + _numValues = numValues; + } + + /// + /// Adds a key to the set, but the key is null. It behaves the same, but has its own + /// variables that need to be incremented. + /// + + private bool AddNull() + { + if (!_hasNullEntry) + { + _hasNullEntry = true; + _numValues++; + _nullEntry = 0; + return true; + } + + _numValues++; + _nullEntry++; + + return false; + } + + /// Add a key to the set. Add with a reference count of one if the key didn't exist in the set. + /// Increase the reference count by one if the key already exists. + /// Return true if this is the first time the key was encountered, or false if key is already in set. + /// + /// to add + /// + /// true if the key is not in the set already, false if the key is already in the set + /// + public virtual bool Add(TK key) + { + if (ReferenceEquals(key, null)) + { + return AddNull(); + } + + int value; + if (!_refSet.TryGetValue(key, out value)) + { + _refSet[key] = 1; + _numValues++; + return true; + } + + value++; + _numValues++; + _refSet[key] = value; + return false; + } + + /// + /// Removes the null key + /// + + private bool RemoveNull() + { + if (_nullEntry == 1) + { + _hasNullEntry = false; + _nullEntry--; + return true; + } + + _nullEntry--; + _numValues--; + + return false; + } + + /// + /// Adds the specified key. + /// + /// The key. + /// The num references. + public void Add(TK key, int numReferences) + { + int value; + if (!_refSet.TryGetValue(key, out value)) + { + _refSet[key] = numReferences; + _numValues += numReferences; + return; + } + throw new ArgumentException("Value '" + key + "' already in collection"); + } + + /// Removed a key to the set. Removes the key if the reference count is one. + /// Decreases the reference count by one if the reference count is more then one. + /// Return true if the reference count was one and the key thus removed, or false if key is stays in set. + /// + /// to add + /// + /// true if the key is removed, false if it stays in the set + /// + /// IllegalStateException is a key is removed that wasn't added to the map + + public virtual bool Remove(TK key) + { + if (ReferenceEquals(key, null)) + { + return RemoveNull(); + } + + int value; + if (!_refSet.TryGetValue(key, out value)) + { + return true; // ignore duplcate removals + } + + if (value == 1) + { + _refSet.Remove(key); + _numValues--; + return true; + } + + value--; + _refSet[key] = value; + _numValues--; + return false; + } + + /// + /// Remove a key from the set regardless of the number of references. + /// + /// to add + /// + /// true if the key is removed, false if the key was not found + /// + /// IllegalStateException if a key is removed that wasn't added to the map + public bool RemoveAll(TK key) + { + return _refSet.Remove(key); + } + + /// Returns an iterator over the entry set. + /// entry set iterator + /// + + public IEnumerator> GetEnumerator() + { + if (_hasNullEntry) + { + yield return new KeyValuePair(default(TK), _nullEntry); + } + + foreach (KeyValuePair value in _refSet) + { + yield return value; + } + } + + /// + /// Gets the keys. + /// + /// The keys. + public ICollection Keys + { + get { return _refSet.Keys; } + } + + /// Returns the number of values in the collection. + /// size + /// + + public virtual int Count + { + get { return _numValues; } + } + + /// + /// Clear out the collection. + /// + public virtual void Clear() + { + _refSet.Clear(); + _numValues = 0; + } + + public IDictionary RefSet + { + get { return _refSet; } + } + + public int NumValues + { + get { return _numValues; } + set { _numValues = value; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/collection/RefCountedSetAtomicInteger.cs b/NEsper.Core/NEsper.Core/collection/RefCountedSetAtomicInteger.cs new file mode 100755 index 000000000..3051bae6a --- /dev/null +++ b/NEsper.Core/NEsper.Core/collection/RefCountedSetAtomicInteger.cs @@ -0,0 +1,88 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Threading; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.collection +{ + public class RefCountedSetAtomicInteger + { + private readonly IDictionary _refs; + + public RefCountedSetAtomicInteger() { + _refs = new Dictionary(); + } + + /// Clear out the collection. + public void Clear() + { + _refs.Clear(); + } + + public bool Add(K key) { + var count = _refs.Get(key); + if (count == null) + { + _refs[key] = 1; + return true; + } + else if (count is Mutable) + { + var mutable = (Mutable) count; + Interlocked.Increment(ref mutable.Value); + return false; + } + else + { + _refs[key] = new Mutable(2); + return false; + } + } + + public bool Remove(K key) { + var count = _refs.Get(key); + if (count == null) { + return false; + } + else if (count is Mutable) + { + var mutable = (Mutable)count; + var val = Interlocked.Decrement(ref mutable.Value); + if (val == 0) { + _refs.Remove(key); + return true; + } + return false; + } + else + { + _refs.Remove(key); + return true; + } + } + + public void RemoveAll(K key) + { + _refs.Remove(key); + } + + public bool IsEmpty() { + return _refs.IsEmpty(); + } + + public IDictionary Refs + { + get { return _refs; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/collection/RollingEventBuffer.cs b/NEsper.Core/NEsper.Core/collection/RollingEventBuffer.cs new file mode 100755 index 000000000..812e3bbc0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/collection/RollingEventBuffer.cs @@ -0,0 +1,120 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.collection +{ + /// + /// Event buffer of a given size provides a random access API into the most current event to prior events + /// up to the given size. Oldest events roll out of the buffer first. + /// + /// Backed by a fixed-size array that is filled forward, then rolls back to the beginning + /// keeping track of the current position. + /// + /// + public class RollingEventBuffer + { + private EventBean[] _buffer; + private int _nextFreeIndex; + + /// Ctor. + /// is the maximum number of events in buffer + public RollingEventBuffer(int size) + { + if (size <= 0) + { + throw new ArgumentException("Minimum buffer size is 1"); + } + + _nextFreeIndex = 0; + _buffer = new EventBean[size]; + } + + /// Add events to the buffer. + /// to add + public void Add(EventBean[] events) + { + if (events == null) + { + return; + } + + for (int i = 0; i < events.Length; i++) + { + Add(events[i]); + } + } + + /// Add an event to the buffer. + /// to add + public void Add(EventBean @event) + { + _buffer[_nextFreeIndex] = @event; + _nextFreeIndex++; + + if (_nextFreeIndex == _buffer.Length) + { + _nextFreeIndex = 0; + } + } + + /// + /// Get an event prior to the last event posted given a number of events before the last. + /// + /// Thus index 0 returns the last event added, index 1 returns the prior to the last event added + /// up to the maximum buffer size. + /// + /// + /// prior event index from zero to max size + /// prior event at given index + public EventBean Get(int index) + { + if (index >= _buffer.Length) + { + throw new ArgumentException("Invalid index " + index + " for size " + _buffer.Length); + } + + // The newest event is at (nextFreeIndex + 1) + int newest = _nextFreeIndex - 1; + int relative = newest - index; + if (relative < 0) + { + relative += _buffer.Length; + } + return _buffer[relative]; + } + + public EventBean this[int index] + { + get + { + return Get(index); + } + } + + public int Count + { + get { return _buffer.Length; } + } + + internal EventBean[] Buffer + { + get { return _buffer; } + set { _buffer = value; } + } + + public int NextFreeIndex + { + get { return _nextFreeIndex; } + set { _nextFreeIndex = value; } + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/collection/RollingTwoValueBuffer.cs b/NEsper.Core/NEsper.Core/collection/RollingTwoValueBuffer.cs new file mode 100755 index 000000000..72035d941 --- /dev/null +++ b/NEsper.Core/NEsper.Core/collection/RollingTwoValueBuffer.cs @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.collection +{ + public class RollingTwoValueBuffer + { + private int _nextFreeIndex; + + public RollingTwoValueBuffer(A[] bufferA, B[] bufferB) + { + if (bufferA.Length != bufferB.Length || bufferA.Length == 0) + { + throw new ArgumentException("Minimum buffer size is 1, buffer sizes must be identical"); + } + + BufferA = bufferA; + BufferB = bufferB; + _nextFreeIndex = 0; + } + + public A[] BufferA { get; private set; } + + public B[] BufferB { get; private set; } + + public void Add(A valueA, B valueB) + { + BufferA[_nextFreeIndex] = valueA; + BufferB[_nextFreeIndex] = valueB; + _nextFreeIndex++; + + if (_nextFreeIndex == BufferA.Length) + { + _nextFreeIndex = 0; + } + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/collection/SingleEventIterable.cs b/NEsper.Core/NEsper.Core/collection/SingleEventIterable.cs new file mode 100755 index 000000000..a0f88deaa --- /dev/null +++ b/NEsper.Core/NEsper.Core/collection/SingleEventIterable.cs @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; + +namespace com.espertech.esper.collection +{ + public class SingleEventIterable : IEnumerable where T : class, EventBean + { + private readonly Atomic _ref; + + public SingleEventIterable(Atomic @ref) + { + _ref = @ref; + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public IEnumerator GetEnumerator() + { + var theEvent = _ref.Get(); + if (theEvent != null) + yield return theEvent; + } + } +} diff --git a/NEsper.Core/NEsper.Core/collection/SortedDoubleVector.cs b/NEsper.Core/NEsper.Core/collection/SortedDoubleVector.cs new file mode 100755 index 000000000..c1ba9e6be --- /dev/null +++ b/NEsper.Core/NEsper.Core/collection/SortedDoubleVector.cs @@ -0,0 +1,243 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.compat; + +namespace com.espertech.esper.collection +{ + public class SortedDoubleVector + { + private readonly List _values; + + /// + /// Constructor. + /// + + public SortedDoubleVector() + { + _values = new List(); + } + + /// + /// Initializes a new instance of the class. + /// + /// The values. + public SortedDoubleVector(List values) + { + _values = values; + } + + /// Returns the number of items in the collection. + /// size + /// + public virtual int Count + { + get { return _values.Count; } + } + + /// Returns the value at a given index. + /// for which to return value for + /// + /// value at index + /// + public virtual double this[int index] + { + get { return _values[index]; } + } + + /// Add a value to the collection. + /// is the double-type value to add + /// + public virtual void Add(double val) + { + if (Double.IsNaN(val)) { + return; + } + + int index = FindInsertIndex(val); + + if (index == -1) + { + _values.Add(val); + } + else + { + _values.Insert(index, val); + } + } + + /// Remove a value from the collection. + /// to remove + /// + /// IllegalStateException if the value has not been added + + public virtual void Remove(double val) + { + if (Double.IsNaN(val)) + { + return; + } + + int index = FindInsertIndex(val); + if (index == -1) + { + throw new IllegalStateException("Value not found in collection"); + } + double? valueAtIndex = _values[index]; + if ((valueAtIndex != null) && (valueAtIndex != val)) + { + throw new IllegalStateException("Value not found in collection"); + } + _values.RemoveAt(index); + } + + /// + /// Clear out the collection. + /// + public virtual void Clear() + { + _values.Clear(); + } + + /// + /// Returns underlying vector, for testing purposes only. + /// + /// vector with double values + + public IList Values + { + get { return _values; } + } + + /// Returns the index into which to insert to. + /// Proptected access level for convenient testing. + /// + /// to find insert index + /// + /// position to insert the value to, or -1 to indicate to add to the end. + /// + public virtual int FindInsertIndex(double val) + { + if (_values.Count > 2) + { + int startIndex = _values.Count >> 1; + double startValue = _values[startIndex]; + int insertAt = -1; + + if (val < startValue) + { + // find in lower half + insertAt = FindInsertIndex(0, startIndex - 1, val); + } + else if (val > startValue) + { + // find in upper half + insertAt = FindInsertIndex(startIndex + 1, _values.Count - 1, val); + } + else + { + // we hit the value + insertAt = startIndex; + } + + if (insertAt == _values.Count) + { + return -1; + } + return insertAt; + } + + if (_values.Count == 2) + { + if (val > _values[1]) + { + return -1; + } + else if (val <= _values[0]) + { + return 0; + } + else + { + return 1; + } + } + + if (_values.Count == 1) + { + if (val > _values[0]) + { + return -1; + } + else + { + return 0; + } + } + + return -1; + } + + private int FindInsertIndex(int lowerBound, int upperBound, double val) + { + while (true) + { + if (upperBound == lowerBound) + { + double valueLowerBound = _values[lowerBound]; + if (val <= valueLowerBound) + { + return lowerBound; + } + else + { + return lowerBound + 1; + } + } + + if (upperBound - lowerBound == 1) + { + double valueLowerBound = _values[lowerBound]; + if (val <= valueLowerBound) + { + return lowerBound; + } + + double valueUpperBound = _values[upperBound]; + if (val > valueUpperBound) + { + return upperBound + 1; + } + + return upperBound; + } + + int nextMiddle = lowerBound + ((upperBound - lowerBound) >> 1); + double valueAtMiddle = _values[nextMiddle]; + + if (val < valueAtMiddle) + { + // find in lower half + upperBound = nextMiddle - 1; + } + else if (val > valueAtMiddle) + { + // find in upper half + lowerBound = nextMiddle; + } + else + { + return nextMiddle; + } + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/collection/SortedRefCountedSet.cs b/NEsper.Core/NEsper.Core/collection/SortedRefCountedSet.cs new file mode 100755 index 000000000..c55ac2778 --- /dev/null +++ b/NEsper.Core/NEsper.Core/collection/SortedRefCountedSet.cs @@ -0,0 +1,180 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.collection +{ + /// + /// Sorted, reference-counting set based on a SortedDictionary implementation that stores keys and a reference counter for + /// each unique key value. Each time the same key is added, the reference counter increases. + /// Each time a key is removed, the reference counter decreases. + /// + + public class SortedRefCountedSet + { + private readonly SortedList _refSet; + private long _countPoints; + + /// + /// Gets the number of data points. + /// + public long CountPoints + { + get { return _countPoints; } + set { _countPoints = value; } + } + + /// + /// Gets the ref set. + /// + /// The ref set. + public SortedList RefSet + { + get { return _refSet; } + } + + /// + /// Constructor. + /// + + public SortedRefCountedSet() + { + _countPoints = 0; + _refSet = new SortedList(); + } + + /// Add a key to the set. Add with a reference count of one if the key didn't exist in the set. + /// Increase the reference count by one if the key already exists. + /// + /// to add + /// + + public virtual void Add(K key) + { + MutableInt value; + if (!_refSet.TryGetValue(key, out value)) + { + _refSet.Add(key, new MutableInt()); + _countPoints++; + } + else + { + value.Value++; + } + } + + /// + /// Add a key to the set with the given number of references. + /// + /// The key. + /// The num references. + public void Add(K key, int numReferences) + { + MutableInt value; + if (!_refSet.TryGetValue(key, out value)) + { + _refSet[key] = new MutableInt(numReferences); + return; + } + throw new ArgumentException("Value '" + key + "' already in collection"); + } + + /// + /// Clear out the collection. + /// + public void Clear() + { + _refSet.Clear(); + _countPoints = 0; + } + + /// Remove a key from the set. Removes the key if the reference count is one. + /// Decreases the reference count by one if the reference count is more then one. + /// + /// to add + /// + /// IllegalStateException is a key is removed that wasn't added to the map + + public virtual void Remove(K key) + { + MutableInt value; + + if (!_refSet.TryGetValue(key, out value)) + { + // This could happen if a sort operation gets a remove stream that duplicates events. + // Generally points to an invalid combination of data windows. + // throw new IllegalStateException("Attempting to remove key from map that wasn't added"); + return; + } + + --_countPoints; + if (value.Value == 1) + { + _refSet.Remove(key); + return ; + } + + value.Value--; + //refSet[key] = value; + } + + /// Returns the largest key value, or null if the collection is empty. + /// largest key value, null if none + /// + + public virtual K MaxValue + { + get + { + return + ( _refSet.Count != 0 ) ? + ( _refSet.Keys[_refSet.Count - 1] ) : + ( default(K) ) ; + } + } + + /// Returns the smallest key value, or null if the collection is empty. + /// smallest key value, default(K) if none + /// + + public virtual K MinValue + { + get + { + return + ( _refSet.Count != 0 ) ? + ( _refSet.Keys[0] ) : + ( default(K) ) ; + } + } + + public sealed class MutableInt : IComparable + { + public int Value = 1; + + public int CompareTo(object obj) + { + var other = obj as MutableInt; + if (other == null) + { + throw new ArgumentException("invalid argument to comparison"); + } + + return Value.CompareTo(other.Value); + } + + public MutableInt() {} + public MutableInt(int initialValue) + { + Value = initialValue; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/collection/SortedVector.cs b/NEsper.Core/NEsper.Core/collection/SortedVector.cs new file mode 100755 index 000000000..037787e47 --- /dev/null +++ b/NEsper.Core/NEsper.Core/collection/SortedVector.cs @@ -0,0 +1,275 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.compat; + +namespace com.espertech.esper.collection +{ + public class SortedVector where T : IComparable + { + private readonly List _values; + private readonly IComparer _comparer; + + /// + /// Constructor. + /// + + public SortedVector() + { + _values = new List(); + _comparer = new DefaultComparer(); + } + + /// + /// Initializes a new instance of the class. + /// + /// The values. + public SortedVector(List values) + { + _values = values; + _comparer = new DefaultComparer(); + } + + /// Returns the number of items in the collection. + /// size + /// + public virtual int Count + { + get { return _values.Count; } + } + + /// Returns the value at a given index. + /// for which to return value for + /// + /// value at index + /// + public virtual T this[int index] + { + get { return _values[index]; } + } + + /// Add a value to the collection. + /// is the double-type value to add + /// + public virtual void Add(T val) + { + int index = FindInsertIndex(val); + + if (index == -1) + { + _values.Add(val); + } + else + { + _values.Insert(index, val); + } + } + + /// Remove a value from the collection. + /// to remove + /// + /// IllegalStateException if the value has not been added + + public virtual void Remove(T val) + { + int index = FindInsertIndex(val); + if (index == -1) + { + throw new IllegalStateException("Value not found in collection"); + } + + T valueAtIndex = _values[index]; + if (IsEQ(valueAtIndex, val)) + { + throw new IllegalStateException("Value not found in collection"); + } + _values.RemoveAt(index); + } + + /// + /// Clear out the collection. + /// + public virtual void Clear() + { + _values.Clear(); + } + + /// + /// Returns underlying vector, for testing purposes only. + /// + /// vector with double values + + public IList Values + { + get { return _values; } + } + + private bool IsLT(T x, T y) + { + return _comparer.Compare(x, y) < 0; + } + + private bool IsLTE(T x, T y) + { + return _comparer.Compare(x, y) <= 0; + } + + private bool IsGT(T x, T y) + { + return _comparer.Compare(x, y) > 0; + } + + private bool IsGTE(T x, T y) + { + return _comparer.Compare(x, y) >= 0; + } + + private bool IsEQ(T x, T y) + { + return _comparer.Compare(x, y) == 0; + } + + /// Returns the index into which to insert to. + /// Proptected access level for convenient testing. + /// + /// to find insert index + /// + /// position to insert the value to, or -1 to indicate to add to the end. + /// + public virtual int FindInsertIndex(T val) + { + if (_values.Count > 2) + { + var startIndex = _values.Count >> 1; + var startValue = _values[startIndex]; + var insertAt = -1; + + if (IsLT(val, startValue)) + { + // find in lower half + insertAt = FindInsertIndex(0, startIndex - 1, val); + } + else if (IsGT(val, startValue)) + { + // find in upper half + insertAt = FindInsertIndex(startIndex + 1, _values.Count - 1, val); + } + else + { + // we hit the value + insertAt = startIndex; + } + + if (insertAt == _values.Count) + { + return -1; + } + return insertAt; + } + + if (_values.Count == 2) + { + if (IsGT(val, _values[1])) + { + return -1; + } + else if (IsLTE(val, _values[0])) + { + return 0; + } + else + { + return 1; + } + } + + if (_values.Count == 1) + { + if (IsGT(val, _values[0])) + { + return -1; + } + else + { + return 0; + } + } + + return -1; + } + + private int FindInsertIndex(int lowerBound, int upperBound, T val) + { + while (true) + { + if (upperBound == lowerBound) + { + T valueLowerBound = _values[lowerBound]; + if (IsLTE(val, valueLowerBound)) + { + return lowerBound; + } + else + { + return lowerBound + 1; + } + } + + if (upperBound - lowerBound == 1) + { + T valueLowerBound = _values[lowerBound]; + if (IsLTE(val, valueLowerBound)) + { + return lowerBound; + } + + T valueUpperBound = _values[upperBound]; + if (IsGT(val, valueUpperBound)) + { + return upperBound + 1; + } + + return upperBound; + } + + int nextMiddle = lowerBound + ((upperBound - lowerBound) >> 1); + T valueAtMiddle = _values[nextMiddle]; + + if (IsLT(val, valueAtMiddle)) + { + // find in lower half + upperBound = nextMiddle - 1; + } + else if (IsGT(val, valueAtMiddle)) + { + // find in upper half + lowerBound = nextMiddle; + } + else + { + return nextMiddle; + } + } + } + + internal class DefaultComparer : Comparer + { + #region Overrides of Comparer + + public override int Compare(T x, T y) + { + return ((IComparable)x).CompareTo(y); + } + + #endregion + } + } +} diff --git a/NEsper.Core/NEsper.Core/collection/ThreadWorkQueue.cs b/NEsper.Core/NEsper.Core/collection/ThreadWorkQueue.cs new file mode 100755 index 000000000..60bb3bb43 --- /dev/null +++ b/NEsper.Core/NEsper.Core/collection/ThreadWorkQueue.cs @@ -0,0 +1,59 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; + +using com.espertech.esper.compat.threading; + +namespace com.espertech.esper.collection +{ + /// + /// Simple queue implementation based on a Linked List per thread. + /// Objects can be added to the queue tail or queue head. + /// + + public class ThreadWorkQueue + { + private readonly IThreadLocal> _threadQueue; + + /// + /// Gets the thread queue. + /// + /// The thread queue. + public DualWorkQueue ThreadQueue + { + get { return _threadQueue.GetOrCreate(); } + } + + /// + /// Initializes a new instance of the class. + /// + public ThreadWorkQueue() + { + _threadQueue = ThreadLocalManager.Create( + () => new DualWorkQueue()); + } + + /// Adds event to the end of the event queue. + /// event to add + public void AddBack(Object ev) + { + DualWorkQueue queue = ThreadQueue; + queue.BackQueue.AddLast(ev); + } + + /// Adds event to the front of the queue. + /// event to add + public void AddFront(Object ev) + { + DualWorkQueue queue = ThreadQueue; + queue.FrontQueue.AddLast(ev); + } + } +} diff --git a/NEsper.Core/NEsper.Core/collection/TimeWindow.cs b/NEsper.Core/NEsper.Core/collection/TimeWindow.cs new file mode 100755 index 000000000..8cc1f5a20 --- /dev/null +++ b/NEsper.Core/NEsper.Core/collection/TimeWindow.cs @@ -0,0 +1,288 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.view; + +namespace com.espertech.esper.collection +{ + /// + /// Container for events per time slot. The time is provided as long milliseconds by + /// client classes. Events are for a specified timestamp and the implementation creates + /// and adds the event to a slot for that timestamp. Events can be expired from the + /// window via the expireEvents method when their timestamp is before (or less then) + /// an expiry timestamp passed in. Expiry removes the event from the window. The window + /// allows iteration through its contents. It is assumed that the timestamp passed to the + /// add method is ascending. The window is backed by a collection reflecting the timestamp + /// order rather then any sorted map or linked hash map for performance reasons. + /// + public sealed class TimeWindow : IEnumerable + { + private ArrayDeque _window; + private IDictionary _reverseIndex; + private int _size; + + /// Ctor. + /// + /// true to indicate the time window should support effective removal of events + /// in the window based on the remove stream events received, or false to not accomodate removal at all + /// + public TimeWindow(bool isSupportRemoveStream) + { + _window = new ArrayDeque(); + + if (isSupportRemoveStream) + { + _reverseIndex = new Dictionary(); + } + } + + /// Adjust expiry dates. + /// delta to adjust for + public void Adjust(long delta) + { + foreach (var data in _window) + { + data.Timestamp = data.Timestamp + delta; + } + } + + /// Adds event to the time window for the specified timestamp. + /// the time slot for the event + /// event to add + public void Add(long timestamp, EventBean bean) + { + // EmptyFalse 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) + { + var list = (IList) lastPair.EventHolder; + list.Add(bean); + } + else if (lastPair.EventHolder == null) + { + lastPair.EventHolder = bean; + } + else + { + var existing = (EventBean) lastPair.EventHolder; + IList list = new List(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++; + } + + /// Removes the event from the window, if remove stream handling is enabled. + /// to remove + public void Remove(EventBean theEvent) + { + if (_reverseIndex == null) + { + throw new UnsupportedOperationException("TimeInMillis window does not accept event removal"); + } + var pair = _reverseIndex.Get(theEvent); + if (pair != null) + { + if (pair.EventHolder != null && pair.EventHolder.Equals(theEvent)) + { + pair.EventHolder = null; + _size--; + } + else if (pair.EventHolder != null) + { + var list = (IList) pair.EventHolder; + var removed = list.Remove(theEvent); + if (removed) + { + _size--; + } + } + _reverseIndex.Remove(theEvent); + } + } + + /// + /// Return and remove events in time-slots earlier (less) then the timestamp passed in, returning the list of events expired. + /// + /// is the timestamp from which on to keep events in the window + /// + /// a list of events expired and removed from the window, or null if none expired + /// + public ArrayDeque ExpireEvents(long expireBefore) + { + if (_window.IsEmpty()) + { + return null; + } + + var pair = _window.First; + + // If the first entry's timestamp is after the expiry date, nothing to expire + if (pair.Timestamp >= expireBefore) + { + return null; + } + + var resultBeans = new ArrayDeque(); + + // Repeat until the window is empty or the timestamp is above the expiry time + do + { + if (pair.EventHolder != null) + { + if (pair.EventHolder is EventBean) + { + resultBeans.Add((EventBean) pair.EventHolder); + } + else + { + resultBeans.AddAll((IList) pair.EventHolder); + } + } + + _window.RemoveFirst(); + + if (_window.IsEmpty()) + { + break; + } + + pair = _window.First; + } while (pair.Timestamp < expireBefore); + + if (_reverseIndex != null) + { + foreach (var expired in resultBeans) + { + _reverseIndex.Remove(expired); + } + } + + _size -= resultBeans.Count; + return resultBeans; + } + + /// + /// Returns an enumerator that iterates through a collection. + /// + /// + /// An object that can be used to iterate through the collection. + /// + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + /// + /// Returns event iterator. + /// + /// iterator over events currently in window + public IEnumerator GetEnumerator() + { + return new TimeWindowEnumerator(_window); + } + + /// + /// Returns the oldest timestamp in the collection if there is at + /// least one entry, else it returns null if the window is empty. + /// + /// null if empty, oldest timestamp if not empty + public long? OldestTimestamp + { + get + { + if (_window.IsEmpty()) + { + return null; + } + if (_window.First.EventHolder != null) + { + return _window.First.Timestamp; + } + foreach (var pair in _window) + { + if (pair.EventHolder != null) + { + return pair.Timestamp; + } + } + return null; + } + } + + /// Returns true if the window is currently empty. + /// true if empty, false if not + public bool IsEmpty() + { + return OldestTimestamp == null; + } + + /// Returns the reverse index, for testing purposes. + /// reverse index + public IDictionary ReverseIndex + { + get { return _reverseIndex; } + set { _reverseIndex = value; } + } + + public ArrayDeque Window + { + get { return _window; } + } + + public void SetWindow(ArrayDeque window, int size) + { + _window = window; + _size = size; + } + + public void VisitView(ViewDataVisitor viewDataVisitor, DataWindowViewFactory viewFactory) + { + viewDataVisitor.VisitPrimary(_window, false, viewFactory.ViewName, _size); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/collection/TimeWindowEnumerator.cs b/NEsper.Core/NEsper.Core/collection/TimeWindowEnumerator.cs new file mode 100755 index 000000000..d495e7f28 --- /dev/null +++ b/NEsper.Core/NEsper.Core/collection/TimeWindowEnumerator.cs @@ -0,0 +1,36 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.collection +{ + /// + /// GetEnumerator for to iterate over a timestamp slots that hold events. + /// + + public sealed class TimeWindowEnumerator : MixedEventBeanAndCollectionEnumeratorBase + { + /// + /// Ctor. + /// + /// is the time-slotted collection + + public TimeWindowEnumerator(IEnumerable window) + : base(window) + { + } + + protected override Object GetValue(Object keyValue) + { + return ((TimeWindowPair)keyValue).EventHolder; + } + } +} diff --git a/NEsper.Core/NEsper.Core/collection/TimeWindowPair.cs b/NEsper.Core/NEsper.Core/collection/TimeWindowPair.cs new file mode 100755 index 000000000..015528a2d --- /dev/null +++ b/NEsper.Core/NEsper.Core/collection/TimeWindowPair.cs @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.collection +{ + public class TimeWindowPair + { + /// + /// Initializes a new instance of the class. + /// + /// The timestamp. + /// The event holder. + public TimeWindowPair(long timestamp, Object eventHolder) + { + Timestamp = timestamp; + EventHolder = eventHolder; + } + + /// + /// Gets or sets the timestamp. + /// + /// The timestamp. + public long Timestamp { get; set; } + + /// + /// Gets or sets the event holder. + /// + /// The event holder. + public object EventHolder { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/collection/TransformEventMethod.cs b/NEsper.Core/NEsper.Core/collection/TransformEventMethod.cs new file mode 100755 index 000000000..1f619236e --- /dev/null +++ b/NEsper.Core/NEsper.Core/collection/TransformEventMethod.cs @@ -0,0 +1,17 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; + +namespace com.espertech.esper.collection +{ + /// Transform event returning the transformed event. + /// event to transform + /// transformed event + public delegate EventBean TransformEventMethod( EventBean _event ) ; +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/collection/TransformEventUtil.cs b/NEsper.Core/NEsper.Core/collection/TransformEventUtil.cs new file mode 100755 index 000000000..053d6e522 --- /dev/null +++ b/NEsper.Core/NEsper.Core/collection/TransformEventUtil.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; + +namespace com.espertech.esper.collection +{ + /// + /// Utility for reading and transforming a source event iterator. + /// Works with a as the transformation method. + /// + + public class TransformEventUtil + { + /// + /// Transforms the enumerator using the transform method supplied. + /// + /// The source enum. + /// The transform event method. + /// + public static IEnumerator Transform(IEnumerator sourceEnum, Func transformEventMethod) + { + while (sourceEnum.MoveNext()) + { + yield return transformEventMethod(sourceEnum.Current); + } + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/collection/UniformPair.cs b/NEsper.Core/NEsper.Core/collection/UniformPair.cs new file mode 100755 index 000000000..9b20ccb63 --- /dev/null +++ b/NEsper.Core/NEsper.Core/collection/UniformPair.cs @@ -0,0 +1,96 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; + +using com.espertech.esper.util; + +namespace com.espertech.esper.collection +{ + /// General-purpose pair of values of any type. The pair only equals another pair if + /// the objects that form the pair equal, ie. first pair first object equals (.equals) the second pair first object, + /// and the first pair second object equals the second pair second object. + /// + + [Serializable] + public sealed class UniformPair : MetaDefItem + { + /// + /// Gets or sets the first value within pair. + /// + /// The first. + public T First { get; set; } + + /// + /// Gets or sets the second value within pair. + /// + /// The second. + public T Second { get; set; } + + /// + /// Construct pair of values. + /// + /// is the first value + /// is the second value + + public UniformPair(T first, T second) + { + this.First = first; + this.Second = second; + } + + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// + /// true if the specified is equal to the current ; otherwise, false. + /// + public override bool Equals(Object obj) + { + if (this == obj) + { + return true; + } + + if (!(obj is UniformPair)) + { + return false; + } + + UniformPair other = (UniformPair) obj; + + return + (First == null?other.First == null:First.Equals(other.First)) && + (Second == null?other.Second == null:Second.Equals(other.Second)); + } + + /// + /// Serves as a hash function for a particular type. + /// + /// + /// A hash code for the current . + /// + public override int GetHashCode() + { + return (First == null?0:First.GetHashCode()) ^ (Second == null?0:Second.GetHashCode()); + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override String ToString() + { + return "Pair [" + First + ':' + Second + ']'; + } + } +} diff --git a/NEsper.Core/NEsper.Core/collection/ViewUpdatedCollection.cs b/NEsper.Core/NEsper.Core/collection/ViewUpdatedCollection.cs new file mode 100755 index 000000000..a91dced9d --- /dev/null +++ b/NEsper.Core/NEsper.Core/collection/ViewUpdatedCollection.cs @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; + +namespace com.espertech.esper.collection +{ + /// A general-purpose collection interface for collections updated by view data. Views post delta-data in terms of new data (insert stream) events and old data (remove stream) event that leave a window. + public interface ViewUpdatedCollection + { + /// Accepts view insert and remove stream. + /// is the insert stream events or null if no data + /// is the remove stream events or null if no data + void Update(EventBean[] newData, EventBean[] oldData); + + /// De-allocate resources held by the collection. + void Destroy(); + + int NumEventsInsertBuf { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/Atomic.cs b/NEsper.Core/NEsper.Core/compat/Atomic.cs new file mode 100755 index 000000000..755dd0c59 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/Atomic.cs @@ -0,0 +1,60 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Threading; + +namespace com.espertech.esper.compat +{ + public class Atomic where T : class + { + private T _value; + + /// + /// Initializes a new instance of the class. + /// + public Atomic() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The value. + public Atomic(T value) + { + _value = value; + } + + /// + /// Gets this instance. + /// + /// + public T Get() + { + return _value; + } + + /// + /// Sets the specified value. + /// + /// The value. + public void Set(T value) + { + Interlocked.Exchange(ref _value, value); + } + + /// + /// Gets the value. + /// + public T Value + { + get { return _value; } + } + + } +} diff --git a/NEsper.Core/NEsper.Core/compat/AtomicBoolean.cs b/NEsper.Core/NEsper.Core/compat/AtomicBoolean.cs new file mode 100755 index 000000000..92d434822 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/AtomicBoolean.cs @@ -0,0 +1,44 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Threading; + +namespace com.espertech.esper.compat +{ + public class AtomicBoolean + { + private long _value; + + /// + /// Initializes a new instance of the class. + /// + /// The value. + public AtomicBoolean(bool value = false) + { + _value = value ? 1 : 0; + } + + /// + /// Gets this instance. + /// + /// + public bool Get() + { + return Interlocked.Read(ref _value) == 1; + } + + /// + /// Sets the specified value. + /// + /// The value. + public void Set(bool value) + { + Interlocked.Exchange(ref _value, value ? 1 : 0); + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/AtomicLong.cs b/NEsper.Core/NEsper.Core/compat/AtomicLong.cs new file mode 100755 index 000000000..341190281 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/AtomicLong.cs @@ -0,0 +1,87 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Threading; + +namespace com.espertech.esper.compat +{ + public class AtomicLong + { + private long _value; + + /// + /// Initializes a new instance of the class. + /// + public AtomicLong() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The value. + public AtomicLong(long value) + { + _value = value; + } + + /// + /// Gets this instance. + /// + /// + public long Get() + { + return Interlocked.Read(ref _value); + } + + /// + /// Sets the specified value. + /// + /// The value. + public void Set(long value) + { + Interlocked.Exchange(ref _value, value); + } + + /// + /// Increments and returns the value. + /// + /// + public long IncrementAndGet() + { + return Interlocked.Increment(ref _value); + } + + /// + /// Increments and returns the value. + /// + /// + public long IncrementAndGet(long value) + { + return Interlocked.Add(ref _value, value); + } + + /// + /// Decrements and returns the value. + /// + /// + public long DecrementAndGet() + { + return Interlocked.Decrement(ref _value); + } + + /// + /// Increments and returns the value. + /// + /// + public long DecrementAndGet(long value) + { + return Interlocked.Add(ref _value, -value); + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/Blob.cs b/NEsper.Core/NEsper.Core/compat/Blob.cs new file mode 100755 index 000000000..27de65f02 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/Blob.cs @@ -0,0 +1,107 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.util; + +namespace com.espertech.esper.compat +{ + [Serializable] + public class Blob + { + private readonly byte[] _data; + private readonly int _hash; + + /// + /// Gets the data. + /// + /// + /// The data. + /// + public byte[] Data + { + get { return _data; } + } + + /// + /// Gets the hash. + /// + /// + /// The hash. + /// + public int Hash + { + get { return _hash; } + } + + /// + /// Initializes a new instance of the class. + /// + /// The data. + public Blob(byte[] data) + { + _data = data; + _hash = MurmurHash.Hash(data, 0, data.Length, 0); + } + + protected bool Equals(Blob other) + { + if (_hash != other._hash) + return false; + if (_data.Length != other._data.Length) + return false; + + unchecked + { + int length = _data.Length; + for (int ii = 0; ii < length; ii++) + { + if (_data[ii] != other._data[ii]) + { + return false; + } + } + } + + return true; + } + + /// + /// Determines whether the specified , is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) + return false; + if (ReferenceEquals(this, obj)) + return true; + if (obj.GetType() != this.GetType()) + return false; + return Equals((Blob) obj); + } + + /// + /// Returns a hash code for this instance. + /// + /// + /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. + /// + public override int GetHashCode() + { + unchecked + { + return _hash; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/ByteExtensions.cs b/NEsper.Core/NEsper.Core/compat/ByteExtensions.cs new file mode 100755 index 000000000..779adf384 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/ByteExtensions.cs @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using Nito.KitchenSink.CRC; + +namespace com.espertech.esper.compat +{ + public static class ByteExtensions + { + public static int GetCrc32(this byte[] input) + { + var algo = new CRC32(); + var hash = algo.ComputeHash(input); + return BitConverter.ToInt32(hash, 0); + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/Cache.cs b/NEsper.Core/NEsper.Core/compat/Cache.cs new file mode 100755 index 000000000..0c87d72d9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/Cache.cs @@ -0,0 +1,64 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +namespace com.espertech.esper.compat +{ + public sealed class Cache : ICache where K : class + { + private Entry _entry; + + public Cache() + { + Invalidate(); + } + + + public bool TryGet(K key, out V value) + { + Entry e = _entry; + if (key == e.Key) + { + value = e.Value; + return true; + } + + value = default(V); + return false; + } + + public V Get(K key) + { + var e = _entry; + return e.Key == key ? e.Value : default(V); + } + + public V Put(K key, V value) + { + _entry = new Entry(key, value); + return value; + } + + public void Invalidate() + { + _entry = new Entry(null, default(V)); + } + + class Entry + { + public readonly K Key; + public readonly V Value; + + public Entry(K key, V value) + { + Key = key; + Value = value; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/Cache2D.cs b/NEsper.Core/NEsper.Core/compat/Cache2D.cs new file mode 100755 index 000000000..6ac650540 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/Cache2D.cs @@ -0,0 +1,74 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +namespace com.espertech.esper.compat +{ + public sealed class Cache2D : ICache where K : class + { + private Entry _entry1; + private Entry _entry2; + + public Cache2D() + { + Invalidate(); + } + + public bool TryGet(K key, out V value) + { + Entry e = _entry1; + if (key == e.Key) + { + value = e.Value; + return true; + } + + e = _entry2; + if (key == e.Key) + { + value = e.Value; + return true; + } + + value = default(V); + return false; + } + + public V Get(K key) + { + Entry e = _entry1; + if (key == e.Key) return e.Value; + e = _entry2; if (key == e.Key) return e.Value; + return default(V); + } + + public V Put(K key, V value) + { + _entry1 = _entry2; + _entry2 = new Entry(key, value); + return value; + } + + public void Invalidate() + { + _entry1 = _entry2 = new Entry(null, default(V)); + } + + class Entry + { + public readonly K Key; + public readonly V Value; + + public Entry(K key, V value) + { + Key = key; + Value = value; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/Cache3D.cs b/NEsper.Core/NEsper.Core/compat/Cache3D.cs new file mode 100755 index 000000000..b68888251 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/Cache3D.cs @@ -0,0 +1,83 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.compat +{ + public sealed class Cache3D : ICache where K : class + { + private Entry _entry1; + private Entry _entry2; + private Entry _entry3; + + public Cache3D() + { + Invalidate(); + } + + public bool TryGet(K key, out V value) + { + Entry e = _entry1; + if (key == e.Key) + { + value = e.Value; + return true; + } + + e = _entry2; + if (key == e.Key) + { + value = e.Value; + return true; + } + + e = _entry3; + if (key == e.Key) + { + value = e.Value; + return true; + } + + value = default(V); + return false; + } + + public V Get(K key) + { + Entry e = _entry1; + if (key == e.Key) return e.Value; + e = _entry2; if (key == e.Key) return e.Value; + e = _entry3; if (key == e.Key) return e.Value; + return default(V); + } + + public V Put(K key, V value) + { + _entry1 = _entry2; + _entry2 = _entry3; + _entry3 = new Entry(key, value); + return value; + } + + public void Invalidate() + { + _entry1 = _entry2 = _entry3 = new Entry(null, default(V)); + } + + class Entry + { + public K Key; + public V Value; + + public Entry(K key, V value) + { + Key = key; + Value = value; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/Callable.cs b/NEsper.Core/NEsper.Core/compat/Callable.cs new file mode 100755 index 000000000..4b5d310b3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/Callable.cs @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; + +namespace com.espertech.esper.compat +{ + public delegate Object Callable() ; + + public interface ICallable + { + T Call(); + } +} diff --git a/NEsper.Core/NEsper.Core/compat/CastHelper.cs b/NEsper.Core/NEsper.Core/compat/CastHelper.cs new file mode 100755 index 000000000..a3fa6bb7c --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/CastHelper.cs @@ -0,0 +1,1902 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Numerics; + +using com.espertech.esper.util; + +namespace com.espertech.esper.compat +{ + using TypeParser = Func; + + /// + /// Provides efficient cast methods for converting from object to + /// primitive types. The cast method provided here-in is consistent + /// with the cast mechanics of C#. These cast mechanics are not + /// the same as those provided by the IConvertible interface. + /// + + public class CastHelper + { + private static TypeParser _parseSingle = TypeHelper.GetParser(); + private static TypeParser _parseDouble = TypeHelper.GetParser(); + private static TypeParser _parseDecimal = TypeHelper.GetParser(); + private static TypeParser _parseByte = TypeHelper.GetParser(); + private static TypeParser _parseSByte = TypeHelper.GetParser(); + private static TypeParser _parseInt16 = TypeHelper.GetParser(); + private static TypeParser _parseInt32 = TypeHelper.GetParser(); + private static TypeParser _parseInt64 = TypeHelper.GetParser(); + private static TypeParser _parseUInt16 = TypeHelper.GetParser(); + private static TypeParser _parseUInt32 = TypeHelper.GetParser(); + private static TypeParser _parseUInt64 = TypeHelper.GetParser(); + private static TypeParser _parseBigInteger = TypeHelper.GetParser(); + + public static GenericTypeCaster GetCastConverter() + { + var typeCaster = GetCastConverter(typeof(T)); + return o => (T) typeCaster.Invoke(o); + } + + /// + /// Gets the cast converter. + /// + /// Type of the source. + /// Type of the target. + /// + public static TypeCaster GetTypeCaster(Type sourceType, Type targetType) + { + return GetCastConverter(targetType); + //return typeCasterFactory.GetTypeCaster(sourceType, targetType); + } + + /// + /// Gets the cast converter for the specified type. If none is + /// found, this method returns null. + /// + /// The t. + /// + public static TypeCaster GetCastConverter(Type t) + { + var baseT = Nullable.GetUnderlyingType(t); + if (baseT != null) + { + t = baseT; + } + + if (t == typeof(Int32)) + { + return PrimitiveCastInt32; + } + if (t == typeof(Int64)) + { + return PrimitiveCastInt64; + } + if (t == typeof(Int16)) + { + return PrimitiveCastInt16; + } + if (t == typeof(SByte)) + { + return PrimitiveCastSByte; + } + if (t == typeof(Single)) + { + return PrimitiveCastSingle; + } + if (t == typeof(Double)) + { + return PrimitiveCastDouble; + } + if (t == typeof(Decimal)) + { + return PrimitiveCastDecimal; + } + if (t == typeof (BigInteger)) + { + return PrimitiveCastBigInteger; + } + if (t == typeof(UInt32)) + { + return PrimitiveCastUInt32; + } + if (t == typeof(UInt64)) + { + return PrimitiveCastUInt64; + } + if (t == typeof(UInt16)) + { + return PrimitiveCastUInt16; + } + if (t == typeof(Char)) + { + return PrimitiveCastChar; + } + if (t == typeof(Byte)) + { + return PrimitiveCastByte; + } + if (t.IsEnum) + { + return sourceObj => PrimitiveCastEnum(t, sourceObj); + } + + return delegate(Object sourceObj) + { + var sourceObjType = sourceObj.GetType(); + if (t.IsAssignableFrom(sourceObjType)) + { + return sourceObj; + } + + return null; + }; + } + + public static object WithParser(TypeParser parser, object sourceObj) + { + try + { + return parser.Invoke((string)sourceObj); + } + catch (FormatException) + { + return null; + } + } + + /// + /// Casts the object to a enumerated type + /// + /// The type. + /// The source object + /// + + public static Object PrimitiveCastEnum(Type enumType, Object sourceObj) + { + if (sourceObj == null) + { + return null; + } + + var sourceObjType = sourceObj.GetType(); + if (sourceObjType == enumType) + { + return sourceObj; + } + if (sourceObjType == typeof(SByte)) + { + return Enum.ToObject(enumType, (SByte) sourceObj); + } + + if (sourceObjType == typeof(SByte?)) + { + return Enum.ToObject(enumType, ((SByte?) sourceObj).Value); + } + + if (sourceObjType == typeof(Byte)) + { + return Enum.ToObject(enumType, (Byte) sourceObj); + } + + if (sourceObjType == typeof(Byte?)) + { + return Enum.ToObject(enumType, ((Byte?) sourceObj).Value); + } + + if (sourceObjType == typeof(Char)) + { + return Enum.ToObject(enumType, ((Char)sourceObj)); + } + + if (sourceObjType == typeof(Char?)) + { + return Enum.ToObject(enumType, ((Char?)sourceObj).Value); + } + + if (sourceObjType == typeof(Int16)) + { + return Enum.ToObject(enumType, ((Int16)sourceObj)); + } + + if (sourceObjType == typeof(Int16?)) + { + return Enum.ToObject(enumType, ((Int16?)sourceObj).Value); + } + + if (sourceObjType == typeof(Int32)) + { + return Enum.ToObject(enumType, ((Int32)sourceObj)); + } + + if (sourceObjType == typeof(Int32?)) + { + return Enum.ToObject(enumType, ((Int32?) sourceObj).Value); + } + + if (sourceObjType == typeof(Int64)) + { + return Enum.ToObject(enumType, ((Int64)sourceObj)); + } + + if (sourceObjType == typeof(Int64?)) + { + return Enum.ToObject(enumType, ((Int64?)sourceObj).Value); + } + + if (sourceObjType == typeof(UInt16)) + { + return Enum.ToObject(enumType, ((UInt16)sourceObj)); + } + + if (sourceObjType == typeof(UInt16?)) + { + return Enum.ToObject(enumType, ((UInt16?)sourceObj).Value); + } + + if (sourceObjType == typeof(UInt32)) + { + return Enum.ToObject(enumType, ((UInt32)sourceObj)); + } + + if (sourceObjType == typeof(UInt32?)) + { + return Enum.ToObject(enumType, ((UInt32?)sourceObj).Value); + } + + if (sourceObjType == typeof(UInt64)) + { + return Enum.ToObject(enumType, ((UInt64)sourceObj)); + } + + if (sourceObjType == typeof(UInt64?)) + { + return Enum.ToObject(enumType, ((UInt64?)sourceObj).Value); + } + + if (sourceObjType == typeof(Single)) + { + return Enum.ToObject(enumType, ((Single)sourceObj)); + } + + if (sourceObjType == typeof(Single?)) + { + return Enum.ToObject(enumType, ((Single?)sourceObj).Value); + } + + if (sourceObjType == typeof(Double)) + { + return Enum.ToObject(enumType, ((Double)sourceObj)); + } + + if (sourceObjType == typeof(Double?)) + { + return Enum.ToObject(enumType, ((Double?)sourceObj).Value); + } + + if (sourceObjType == typeof(Decimal)) + { + return Enum.ToObject(enumType, ((Decimal)sourceObj)); + } + + if (sourceObjType == typeof(Decimal?)) + { + return Enum.ToObject(enumType, ((Decimal?)sourceObj).Value); + } + + if (sourceObjType == typeof(string)) + { + return Enum.ToObject(enumType, (((string)sourceObj)[0])); + } + + return null; + } + + /// + /// Casts the object to the System.SByte + /// + /// The source object + + public static Object PrimitiveCastSByte(Object sourceObj) + { + if (sourceObj == null) + { + return null; + } + + var sourceObjType = sourceObj.GetType(); + if (sourceObjType == typeof (string)) + { + return WithParser(_parseSByte, sourceObj); + } + + if (sourceObjType == typeof(SByte)) + { + return (SByte)sourceObj; + } + + if (sourceObjType == typeof(SByte?)) + { + return ((SByte?)sourceObj).Value; + } + + if (sourceObjType == typeof(Byte)) + { + return (SByte)((Byte)sourceObj); + } + + if (sourceObjType == typeof(Byte?)) + { + return (SByte)((Byte?)sourceObj).Value; + } + + if (sourceObjType == typeof(Char)) + { + return (SByte)((Char)sourceObj); + } + + if (sourceObjType == typeof(Char?)) + { + return (SByte)((Char?)sourceObj).Value; + } + + if (sourceObjType == typeof(Int16)) + { + return (SByte)((Int16)sourceObj); + } + + if (sourceObjType == typeof(Int16?)) + { + return (SByte)((Int16?)sourceObj).Value; + } + + if (sourceObjType == typeof(Int32)) + { + return (SByte)((Int32)sourceObj); + } + + if (sourceObjType == typeof(Int32?)) + { + return (SByte)((Int32?)sourceObj).Value; + } + + if (sourceObjType == typeof(Int64)) + { + return (SByte)((Int64)sourceObj); + } + + if (sourceObjType == typeof(Int64?)) + { + return (SByte)((Int64?)sourceObj).Value; + } + + if (sourceObjType == typeof(UInt16)) + { + return (SByte)((UInt16)sourceObj); + } + + if (sourceObjType == typeof(UInt16?)) + { + return (SByte)((UInt16?)sourceObj).Value; + } + + if (sourceObjType == typeof(UInt32)) + { + return (SByte)((UInt32)sourceObj); + } + + if (sourceObjType == typeof(UInt32?)) + { + return (SByte)((UInt32?)sourceObj).Value; + } + + if (sourceObjType == typeof(UInt64)) + { + return (SByte)((UInt64)sourceObj); + } + + if (sourceObjType == typeof(UInt64?)) + { + return (SByte)((UInt64?)sourceObj).Value; + } + + if (sourceObjType == typeof(Single)) + { + return (SByte)((Single)sourceObj); + } + + if (sourceObjType == typeof(Single?)) + { + return (SByte)((Single?)sourceObj).Value; + } + + if (sourceObjType == typeof(Double)) + { + return (SByte)((Double)sourceObj); + } + + if (sourceObjType == typeof(Double?)) + { + return (SByte)((Double?)sourceObj).Value; + } + + if (sourceObjType == typeof(Decimal)) + { + return (SByte)((Decimal)sourceObj); + } + + if (sourceObjType == typeof(Decimal?)) + { + return (SByte)((Decimal?)sourceObj).Value; + } + + if (sourceObjType == typeof(string)) + { + return (SByte)(((string)sourceObj)[0]); + } + + return null; + } + + /// + /// Casts the object to the System.Byte + /// + /// The source object + + public static Object PrimitiveCastByte(Object sourceObj) + { + if (sourceObj == null) + { + return null; + } + + var sourceObjType = sourceObj.GetType(); + if (sourceObjType == typeof(string)) + { + return WithParser(_parseByte, sourceObj); + } + + if (sourceObjType == typeof(SByte)) + { + return (Byte)((SByte)sourceObj); + } + + if (sourceObjType == typeof(SByte?)) + { + return (Byte)((SByte?)sourceObj).Value; + } + + if (sourceObjType == typeof(Byte)) + { + return (Byte)sourceObj; + } + + if (sourceObjType == typeof(Byte?)) + { + return ((Byte?)sourceObj).Value; + } + + if (sourceObjType == typeof(Char)) + { + return (Byte)((Char)sourceObj); + } + + if (sourceObjType == typeof(Char?)) + { + return (Byte)((Char?)sourceObj).Value; + } + + if (sourceObjType == typeof(Int16)) + { + return (Byte)((Int16)sourceObj); + } + + if (sourceObjType == typeof(Int16?)) + { + return (Byte)((Int16?)sourceObj).Value; + } + + if (sourceObjType == typeof(Int32)) + { + return (Byte)((Int32)sourceObj); + } + + if (sourceObjType == typeof(Int32?)) + { + return (Byte)((Int32?)sourceObj).Value; + } + + if (sourceObjType == typeof(Int64)) + { + return (Byte)((Int64)sourceObj); + } + + if (sourceObjType == typeof(Int64?)) + { + return (Byte)((Int64?)sourceObj).Value; + } + + if (sourceObjType == typeof(UInt16)) + { + return (Byte)((UInt16)sourceObj); + } + + if (sourceObjType == typeof(UInt16?)) + { + return (Byte)((UInt16?)sourceObj).Value; + } + + if (sourceObjType == typeof(UInt32)) + { + return (Byte)((UInt32)sourceObj); + } + + if (sourceObjType == typeof(UInt32?)) + { + return (Byte)((UInt32?)sourceObj).Value; + } + + if (sourceObjType == typeof(UInt64)) + { + return (Byte)((UInt64)sourceObj); + } + + if (sourceObjType == typeof(UInt64?)) + { + return (Byte)((UInt64?)sourceObj).Value; + } + + if (sourceObjType == typeof(Single)) + { + return (Byte)((Single)sourceObj); + } + + if (sourceObjType == typeof(Single?)) + { + return (Byte)((Single?)sourceObj).Value; + } + + if (sourceObjType == typeof(Double)) + { + return (Byte)((Double)sourceObj); + } + + if (sourceObjType == typeof(Double?)) + { + return (Byte)((Double?)sourceObj).Value; + } + + if (sourceObjType == typeof(Decimal)) + { + return (Byte)((Decimal)sourceObj); + } + + if (sourceObjType == typeof(Decimal?)) + { + return (Byte)((Decimal?)sourceObj).Value; + } + + if (sourceObjType == typeof(string)) + { + return (Byte) (((string) sourceObj)[0]); + } + + return null; + } + + /// + /// Casts the object to the System.Char + /// + /// The source object + + public static Object PrimitiveCastChar(Object sourceObj) + { + if (sourceObj == null) + { + return null; + } + + var sourceObjType = sourceObj.GetType(); + if (sourceObjType == typeof(SByte)) + { + return (Char)((SByte)sourceObj); + } + + if (sourceObjType == typeof(SByte?)) + { + return (Char)((SByte?)sourceObj).Value; + } + + if (sourceObjType == typeof(Byte)) + { + return (Char)((Byte)sourceObj); + } + + if (sourceObjType == typeof(Byte?)) + { + return (Char)((Byte?)sourceObj).Value; + } + + if (sourceObjType == typeof(Char)) + { + return (Char)sourceObj; + } + + if (sourceObjType == typeof(Char?)) + { + return ((Char?)sourceObj).Value; + } + + if (sourceObjType == typeof(Int16)) + { + return (Char)((Int16)sourceObj); + } + + if (sourceObjType == typeof(Int16?)) + { + return (Char)((Int16?)sourceObj).Value; + } + + if (sourceObjType == typeof(Int32)) + { + return (Char)((Int32)sourceObj); + } + + if (sourceObjType == typeof(Int32?)) + { + return (Char)((Int32?)sourceObj).Value; + } + + if (sourceObjType == typeof(Int64)) + { + return (Char)((Int64)sourceObj); + } + + if (sourceObjType == typeof(Int64?)) + { + return (Char)((Int64?)sourceObj).Value; + } + + if (sourceObjType == typeof(UInt16)) + { + return (Char)((UInt16)sourceObj); + } + + if (sourceObjType == typeof(UInt16?)) + { + return (Char)((UInt16?)sourceObj).Value; + } + + if (sourceObjType == typeof(UInt32)) + { + return (Char)((UInt32)sourceObj); + } + + if (sourceObjType == typeof(UInt32?)) + { + return (Char)((UInt32?)sourceObj).Value; + } + + if (sourceObjType == typeof(UInt64)) + { + return (Char)((UInt64)sourceObj); + } + + if (sourceObjType == typeof(UInt64?)) + { + return (Char)((UInt64?)sourceObj).Value; + } + + if (sourceObjType == typeof(Single)) + { + return (Char)((Single)sourceObj); + } + + if (sourceObjType == typeof(Single?)) + { + return (Char)((Single?)sourceObj).Value; + } + + if (sourceObjType == typeof(Double)) + { + return (Char)((Double)sourceObj); + } + + if (sourceObjType == typeof(Double?)) + { + return (Char)((Double?)sourceObj).Value; + } + + if (sourceObjType == typeof(Decimal)) + { + return (Char)((Decimal)sourceObj); + } + + if (sourceObjType == typeof(Decimal?)) + { + return (Char)((Decimal?)sourceObj).Value; + } + + if (sourceObjType == typeof(string)) + { + return ((string) sourceObj)[0]; + } + + return null; + } + + /// + /// Casts the object to the System.Int16 + /// + /// The source object + + public static Object PrimitiveCastInt16(Object sourceObj) + { + if (sourceObj == null) + { + return null; + } + + var sourceObjType = sourceObj.GetType(); + if (sourceObjType == typeof(string)) + { + return WithParser(_parseInt16, sourceObj); + } + + if (sourceObjType == typeof(SByte)) + { + return (Int16)((SByte)sourceObj); + } + + if (sourceObjType == typeof(SByte?)) + { + return (Int16)((SByte?)sourceObj).Value; + } + + if (sourceObjType == typeof(Byte)) + { + return (Int16)((Byte)sourceObj); + } + + if (sourceObjType == typeof(Byte?)) + { + return (Int16)((Byte?)sourceObj).Value; + } + + if (sourceObjType == typeof(Char)) + { + return (Int16)((Char)sourceObj); + } + + if (sourceObjType == typeof(Char?)) + { + return (Int16)((Char?)sourceObj).Value; + } + + if (sourceObjType == typeof(Int16)) + { + return (Int16)sourceObj; + } + + if (sourceObjType == typeof(Int16?)) + { + return ((Int16?)sourceObj).Value; + } + + if (sourceObjType == typeof(Int32)) + { + return (Int16)((Int32)sourceObj); + } + + if (sourceObjType == typeof(Int32?)) + { + return (Int16)((Int32?)sourceObj).Value; + } + + if (sourceObjType == typeof(Int64)) + { + return (Int16)((Int64)sourceObj); + } + + if (sourceObjType == typeof(Int64?)) + { + return (Int16)((Int64?)sourceObj).Value; + } + + if (sourceObjType == typeof(UInt16)) + { + return (Int16)((UInt16)sourceObj); + } + + if (sourceObjType == typeof(UInt16?)) + { + return (Int16)((UInt16?)sourceObj).Value; + } + + if (sourceObjType == typeof(UInt32)) + { + return (Int16)((UInt32)sourceObj); + } + + if (sourceObjType == typeof(UInt32?)) + { + return (Int16)((UInt32?)sourceObj).Value; + } + + if (sourceObjType == typeof(UInt64)) + { + return (Int16)((UInt64)sourceObj); + } + + if (sourceObjType == typeof(UInt64?)) + { + return (Int16)((UInt64?)sourceObj).Value; + } + + if (sourceObjType == typeof(Single)) + { + return (Int16)((Single)sourceObj); + } + + if (sourceObjType == typeof(Single?)) + { + return (Int16)((Single?)sourceObj).Value; + } + + if (sourceObjType == typeof(Double)) + { + return (Int16)((Double)sourceObj); + } + + if (sourceObjType == typeof(Double?)) + { + return (Int16)((Double?)sourceObj).Value; + } + + if (sourceObjType == typeof(Decimal)) + { + return (Int16)((Decimal)sourceObj); + } + + if (sourceObjType == typeof(Decimal?)) + { + return (Int16)((Decimal?)sourceObj).Value; + } + + return null; + } + + /// + /// Casts the object to the System.Int32 + /// + /// The source object + + public static Object PrimitiveCastInt32(Object sourceObj) + { + if (sourceObj == null) + { + return null; + } + + var sourceObjType = sourceObj.GetType(); + if (sourceObjType == typeof (string)) + { + return WithParser(_parseInt32, sourceObj); + } + + if (sourceObjType == typeof(SByte)) + { + return (Int32)((SByte)sourceObj); + } + + if (sourceObjType == typeof(SByte?)) + { + return (Int32)((SByte?)sourceObj).Value; + } + + if (sourceObjType == typeof(Byte)) + { + return (Int32)((Byte)sourceObj); + } + + if (sourceObjType == typeof(Byte?)) + { + return (Int32)((Byte?)sourceObj).Value; + } + + if (sourceObjType == typeof(Char)) + { + return (Int32)((Char)sourceObj); + } + + if (sourceObjType == typeof(Char?)) + { + return (Int32)((Char?)sourceObj).Value; + } + + if (sourceObjType == typeof(Int16)) + { + return (Int32)((Int16)sourceObj); + } + + if (sourceObjType == typeof(Int16?)) + { + return (Int32)((Int16?)sourceObj).Value; + } + + if (sourceObjType == typeof(Int32)) + { + return (Int32)sourceObj; + } + + if (sourceObjType == typeof(Int32?)) + { + return ((Int32?)sourceObj).Value; + } + + if (sourceObjType == typeof(Int64)) + { + return (Int32)((Int64)sourceObj); + } + + if (sourceObjType == typeof(Int64?)) + { + return (Int32)((Int64?)sourceObj).Value; + } + + if (sourceObjType == typeof(UInt16)) + { + return (Int32)((UInt16)sourceObj); + } + + if (sourceObjType == typeof(UInt16?)) + { + return (Int32)((UInt16?)sourceObj).Value; + } + + if (sourceObjType == typeof(UInt32)) + { + return (Int32)((UInt32)sourceObj); + } + + if (sourceObjType == typeof(UInt32?)) + { + return (Int32)((UInt32?)sourceObj).Value; + } + + if (sourceObjType == typeof(UInt64)) + { + return (Int32)((UInt64)sourceObj); + } + + if (sourceObjType == typeof(UInt64?)) + { + return (Int32)((UInt64?)sourceObj).Value; + } + + if (sourceObjType == typeof(Single)) + { + return (Int32)((Single)sourceObj); + } + + if (sourceObjType == typeof(Single?)) + { + return (Int32)((Single?)sourceObj).Value; + } + + if (sourceObjType == typeof(Double)) + { + return (Int32)((Double)sourceObj); + } + + if (sourceObjType == typeof(Double?)) + { + return (Int32)((Double?)sourceObj).Value; + } + + if (sourceObjType == typeof(Decimal)) + { + return (Int32)((Decimal)sourceObj); + } + + if (sourceObjType == typeof(Decimal?)) + { + return (Int32)((Decimal?)sourceObj).Value; + } + + return null; + } + + /// + /// Casts the object to the System.Int64 + /// + /// The source object + + public static Object PrimitiveCastInt64(Object sourceObj) + { + if (sourceObj == null) + { + return null; + } + + var sourceObjType = sourceObj.GetType(); + if (sourceObjType == typeof(string)) + { + return WithParser(_parseInt64, sourceObj); + } + + if (sourceObjType == typeof(SByte)) + { + return (Int64)((SByte)sourceObj); + } + + if (sourceObjType == typeof(SByte?)) + { + return (Int64)((SByte?)sourceObj).Value; + } + + if (sourceObjType == typeof(Byte)) + { + return (Int64)((Byte)sourceObj); + } + + if (sourceObjType == typeof(Byte?)) + { + return (Int64)((Byte?)sourceObj).Value; + } + + if (sourceObjType == typeof(Char)) + { + return (Int64)((Char)sourceObj); + } + + if (sourceObjType == typeof(Char?)) + { + return (Int64)((Char?)sourceObj).Value; + } + + if (sourceObjType == typeof(Int16)) + { + return (Int64)((Int16)sourceObj); + } + + if (sourceObjType == typeof(Int16?)) + { + return (Int64)((Int16?)sourceObj).Value; + } + + if (sourceObjType == typeof(Int32)) + { + return (Int64)((Int32)sourceObj); + } + + if (sourceObjType == typeof(Int32?)) + { + return (Int64)((Int32?)sourceObj).Value; + } + + if (sourceObjType == typeof(Int64)) + { + return (Int64)sourceObj; + } + + if (sourceObjType == typeof(Int64?)) + { + return ((Int64?)sourceObj).Value; + } + + if (sourceObjType == typeof(UInt16)) + { + return (Int64)((UInt16)sourceObj); + } + + if (sourceObjType == typeof(UInt16?)) + { + return (Int64)((UInt16?)sourceObj).Value; + } + + if (sourceObjType == typeof(UInt32)) + { + return (Int64)((UInt32)sourceObj); + } + + if (sourceObjType == typeof(UInt32?)) + { + return (Int64)((UInt32?)sourceObj).Value; + } + + if (sourceObjType == typeof(UInt64)) + { + return (Int64)((UInt64)sourceObj); + } + + if (sourceObjType == typeof(UInt64?)) + { + return (Int64)((UInt64?)sourceObj).Value; + } + + if (sourceObjType == typeof(Single)) + { + return (Int64)((Single)sourceObj); + } + + if (sourceObjType == typeof(Single?)) + { + return (Int64)((Single?)sourceObj).Value; + } + + if (sourceObjType == typeof(Double)) + { + return (Int64)((Double)sourceObj); + } + + if (sourceObjType == typeof(Double?)) + { + return (Int64)((Double?)sourceObj).Value; + } + + if (sourceObjType == typeof(Decimal)) + { + return (Int64)((Decimal)sourceObj); + } + + if (sourceObjType == typeof(Decimal?)) + { + return (Int64)((Decimal?)sourceObj).Value; + } + + return null; + } + + /// + /// Casts the object to the System.UInt16 + /// + /// The source object + + public static Object PrimitiveCastUInt16(Object sourceObj) + { + if (sourceObj == null) + { + return null; + } + + var sourceObjType = sourceObj.GetType(); + if (sourceObjType == typeof(string)) + { + return WithParser(_parseUInt16, sourceObj); + } + + if (sourceObjType == typeof(SByte)) + { + return (UInt16)((SByte)sourceObj); + } + + if (sourceObjType == typeof(SByte?)) + { + return (UInt16)((SByte?)sourceObj).Value; + } + + if (sourceObjType == typeof(Byte)) + { + return (UInt16)((Byte)sourceObj); + } + + if (sourceObjType == typeof(Byte?)) + { + return (UInt16)((Byte?)sourceObj).Value; + } + + if (sourceObjType == typeof(Char)) + { + return (UInt16)((Char)sourceObj); + } + + if (sourceObjType == typeof(Char?)) + { + return (UInt16)((Char?)sourceObj).Value; + } + + if (sourceObjType == typeof(Int16)) + { + return (UInt16)((Int16)sourceObj); + } + + if (sourceObjType == typeof(Int16?)) + { + return (UInt16)((Int16?)sourceObj).Value; + } + + if (sourceObjType == typeof(Int32)) + { + return (UInt16)((Int32)sourceObj); + } + + if (sourceObjType == typeof(Int32?)) + { + return (UInt16)((Int32?)sourceObj).Value; + } + + if (sourceObjType == typeof(Int64)) + { + return (UInt16)((Int64)sourceObj); + } + + if (sourceObjType == typeof(Int64?)) + { + return (UInt16)((Int64?)sourceObj).Value; + } + + if (sourceObjType == typeof(UInt16)) + { + return (UInt16)sourceObj; + } + + if (sourceObjType == typeof(UInt16?)) + { + return ((UInt16?)sourceObj).Value; + } + + if (sourceObjType == typeof(UInt32)) + { + return (UInt16)((UInt32)sourceObj); + } + + if (sourceObjType == typeof(UInt32?)) + { + return (UInt16)((UInt32?)sourceObj).Value; + } + + if (sourceObjType == typeof(UInt64)) + { + return (UInt16)((UInt64)sourceObj); + } + + if (sourceObjType == typeof(UInt64?)) + { + return (UInt16)((UInt64?)sourceObj).Value; + } + + if (sourceObjType == typeof(Single)) + { + return (UInt16)((Single)sourceObj); + } + + if (sourceObjType == typeof(Single?)) + { + return (UInt16)((Single?)sourceObj).Value; + } + + if (sourceObjType == typeof(Double)) + { + return (UInt16)((Double)sourceObj); + } + + if (sourceObjType == typeof(Double?)) + { + return (UInt16)((Double?)sourceObj).Value; + } + + if (sourceObjType == typeof(Decimal)) + { + return (UInt16)((Decimal)sourceObj); + } + + if (sourceObjType == typeof(Decimal?)) + { + return (UInt16)((Decimal?)sourceObj).Value; + } + + return null; + } + + /// + /// Casts the object to the System.UInt32 + /// + /// The source object + + public static Object PrimitiveCastUInt32(Object sourceObj) + { + if (sourceObj == null) + { + return null; + } + + var sourceObjType = sourceObj.GetType(); + if (sourceObjType == typeof(string)) + { + return WithParser(_parseUInt32, sourceObj); + } + + if (sourceObjType == typeof(SByte)) + { + return (UInt32)((SByte)sourceObj); + } + + if (sourceObjType == typeof(SByte?)) + { + return (UInt32)((SByte?)sourceObj).Value; + } + + if (sourceObjType == typeof(Byte)) + { + return (UInt32)((Byte)sourceObj); + } + + if (sourceObjType == typeof(Byte?)) + { + return (UInt32)((Byte?)sourceObj).Value; + } + + if (sourceObjType == typeof(Char)) + { + return (UInt32)((Char)sourceObj); + } + + if (sourceObjType == typeof(Char?)) + { + return (UInt32)((Char?)sourceObj).Value; + } + + if (sourceObjType == typeof(Int16)) + { + return (UInt32)((Int16)sourceObj); + } + + if (sourceObjType == typeof(Int16?)) + { + return (UInt32)((Int16?)sourceObj).Value; + } + + if (sourceObjType == typeof(Int32)) + { + return (UInt32)((Int32)sourceObj); + } + + if (sourceObjType == typeof(Int32?)) + { + return (UInt32)((Int32?)sourceObj).Value; + } + + if (sourceObjType == typeof(Int64)) + { + return (UInt32)((Int64)sourceObj); + } + + if (sourceObjType == typeof(Int64?)) + { + return (UInt32)((Int64?)sourceObj).Value; + } + + if (sourceObjType == typeof(UInt16)) + { + return (UInt32)((UInt16)sourceObj); + } + + if (sourceObjType == typeof(UInt16?)) + { + return (UInt32)((UInt16?)sourceObj).Value; + } + + if (sourceObjType == typeof(UInt32)) + { + return (UInt32)sourceObj; + } + + if (sourceObjType == typeof(UInt32?)) + { + return ((UInt32?)sourceObj).Value; + } + + if (sourceObjType == typeof(UInt64)) + { + return (UInt32)((UInt64)sourceObj); + } + + if (sourceObjType == typeof(UInt64?)) + { + return (UInt32)((UInt64?)sourceObj).Value; + } + + if (sourceObjType == typeof(Single)) + { + return (UInt32)((Single)sourceObj); + } + + if (sourceObjType == typeof(Single?)) + { + return (UInt32)((Single?)sourceObj).Value; + } + + if (sourceObjType == typeof(Double)) + { + return (UInt32)((Double)sourceObj); + } + + if (sourceObjType == typeof(Double?)) + { + return (UInt32)((Double?)sourceObj).Value; + } + + if (sourceObjType == typeof(Decimal)) + { + return (UInt32)((Decimal)sourceObj); + } + + if (sourceObjType == typeof(Decimal?)) + { + return (UInt32)((Decimal?)sourceObj).Value; + } + + return null; + } + + /// + /// Casts the object to the System.UInt64 + /// + /// The source object + + public static Object PrimitiveCastUInt64(Object sourceObj) + { + if (sourceObj == null) + { + return null; + } + + var sourceObjType = sourceObj.GetType(); + if (sourceObjType == typeof(string)) + { + return WithParser(_parseUInt64, sourceObj); + } + + if (sourceObjType == typeof(SByte)) + { + return (UInt64)((SByte)sourceObj); + } + + if (sourceObjType == typeof(SByte?)) + { + return (UInt64)((SByte?)sourceObj).Value; + } + + if (sourceObjType == typeof(Byte)) + { + return (UInt64)((Byte)sourceObj); + } + + if (sourceObjType == typeof(Byte?)) + { + return (UInt64)((Byte?)sourceObj).Value; + } + + if (sourceObjType == typeof(Char)) + { + return (UInt64)((Char)sourceObj); + } + + if (sourceObjType == typeof(Char?)) + { + return (UInt64)((Char?)sourceObj).Value; + } + + if (sourceObjType == typeof(Int16)) + { + return (UInt64)((Int16)sourceObj); + } + + if (sourceObjType == typeof(Int16?)) + { + return (UInt64)((Int16?)sourceObj).Value; + } + + if (sourceObjType == typeof(Int32)) + { + return (UInt64)((Int32)sourceObj); + } + + if (sourceObjType == typeof(Int32?)) + { + return (UInt64)((Int32?)sourceObj).Value; + } + + if (sourceObjType == typeof(Int64)) + { + return (UInt64)((Int64)sourceObj); + } + + if (sourceObjType == typeof(Int64?)) + { + return (UInt64)((Int64?)sourceObj).Value; + } + + if (sourceObjType == typeof(UInt16)) + { + return (UInt64)((UInt16)sourceObj); + } + + if (sourceObjType == typeof(UInt16?)) + { + return (UInt64)((UInt16?)sourceObj).Value; + } + + if (sourceObjType == typeof(UInt32)) + { + return (UInt64)((UInt32)sourceObj); + } + + if (sourceObjType == typeof(UInt32?)) + { + return (UInt64)((UInt32?)sourceObj).Value; + } + + if (sourceObjType == typeof(UInt64)) + { + return (UInt64)sourceObj; + } + + if (sourceObjType == typeof(UInt64?)) + { + return ((UInt64?)sourceObj).Value; + } + + if (sourceObjType == typeof(Single)) + { + return (UInt64)((Single)sourceObj); + } + + if (sourceObjType == typeof(Single?)) + { + return (UInt64)((Single?)sourceObj).Value; + } + + if (sourceObjType == typeof(Double)) + { + return (UInt64)((Double)sourceObj); + } + + if (sourceObjType == typeof(Double?)) + { + return (UInt64)((Double?)sourceObj).Value; + } + + if (sourceObjType == typeof(Decimal)) + { + return (UInt64)((Decimal)sourceObj); + } + + if (sourceObjType == typeof(Decimal?)) + { + return (UInt64)((Decimal?)sourceObj).Value; + } + + return null; + } + + /// + /// Casts the object to the System.Single + /// + /// The source object + + public static Object PrimitiveCastSingle(Object sourceObj) + { + if (sourceObj == null) + { + return null; + } + + var sourceObjType = sourceObj.GetType(); + if (sourceObjType == typeof(string)) + { + return WithParser(_parseSingle, sourceObj); + } + + if (sourceObjType == typeof(SByte)) + { + return (Single)((SByte)sourceObj); + } + + if (sourceObjType == typeof(SByte?)) + { + return (Single)((SByte?)sourceObj).Value; + } + + if (sourceObjType == typeof(Byte)) + { + return (Single)((Byte)sourceObj); + } + + if (sourceObjType == typeof(Byte?)) + { + return (Single)((Byte?)sourceObj).Value; + } + + if (sourceObjType == typeof(Char)) + { + return (Single)((Char)sourceObj); + } + + if (sourceObjType == typeof(Char?)) + { + return (Single)((Char?)sourceObj).Value; + } + + if (sourceObjType == typeof(Int16)) + { + return (Single)((Int16)sourceObj); + } + + if (sourceObjType == typeof(Int16?)) + { + return (Single)((Int16?)sourceObj).Value; + } + + if (sourceObjType == typeof(Int32)) + { + return (Single)((Int32)sourceObj); + } + + if (sourceObjType == typeof(Int32?)) + { + return (Single)((Int32?)sourceObj).Value; + } + + if (sourceObjType == typeof(Int64)) + { + return (Single)((Int64)sourceObj); + } + + if (sourceObjType == typeof(Int64?)) + { + return (Single)((Int64?)sourceObj).Value; + } + + if (sourceObjType == typeof(UInt16)) + { + return (Single)((UInt16)sourceObj); + } + + if (sourceObjType == typeof(UInt16?)) + { + return (Single)((UInt16?)sourceObj).Value; + } + + if (sourceObjType == typeof(UInt32)) + { + return (Single)((UInt32)sourceObj); + } + + if (sourceObjType == typeof(UInt32?)) + { + return (Single)((UInt32?)sourceObj).Value; + } + + if (sourceObjType == typeof(UInt64)) + { + return (Single)((UInt64)sourceObj); + } + + if (sourceObjType == typeof(UInt64?)) + { + return (Single)((UInt64?)sourceObj).Value; + } + + if (sourceObjType == typeof(Single)) + { + return (Single)sourceObj; + } + + if (sourceObjType == typeof(Single?)) + { + return ((Single?)sourceObj).Value; + } + + if (sourceObjType == typeof(Double)) + { + return (Single)((Double)sourceObj); + } + + if (sourceObjType == typeof(Double?)) + { + return (Single)((Double?)sourceObj).Value; + } + + if (sourceObjType == typeof(Decimal)) + { + return (Single)((Decimal)sourceObj); + } + + if (sourceObjType == typeof(Decimal?)) + { + return (Single)((Decimal?)sourceObj).Value; + } + + return null; + } + + /// + /// Casts the object to the System.Double + /// + /// The source object + + public static Object PrimitiveCastDouble(Object sourceObj) + { + if (sourceObj == null) + { + return null; + } + + var sourceObjType = sourceObj.GetType(); + if (sourceObjType == typeof(string)) + { + try + { + return WithParser(_parseDouble, sourceObj); + } + catch (FormatException) + { + return null; + } + } + + if (sourceObjType == typeof(SByte)) + { + return (Double)((SByte)sourceObj); + } + + if (sourceObjType == typeof(SByte?)) + { + return (Double)((SByte?)sourceObj).Value; + } + + if (sourceObjType == typeof(Byte)) + { + return (Double)((Byte)sourceObj); + } + + if (sourceObjType == typeof(Byte?)) + { + return (Double)((Byte?)sourceObj).Value; + } + + if (sourceObjType == typeof(Char)) + { + return (Double)((Char)sourceObj); + } + + if (sourceObjType == typeof(Char?)) + { + return (Double)((Char?)sourceObj).Value; + } + + if (sourceObjType == typeof(Int16)) + { + return (Double)((Int16)sourceObj); + } + + if (sourceObjType == typeof(Int16?)) + { + return (Double)((Int16?)sourceObj).Value; + } + + if (sourceObjType == typeof(Int32)) + { + return (Double)((Int32)sourceObj); + } + + if (sourceObjType == typeof(Int32?)) + { + return (Double)((Int32?)sourceObj).Value; + } + + if (sourceObjType == typeof(Int64)) + { + return (Double)((Int64)sourceObj); + } + + if (sourceObjType == typeof(Int64?)) + { + return (Double)((Int64?)sourceObj).Value; + } + + if (sourceObjType == typeof(UInt16)) + { + return (Double)((UInt16)sourceObj); + } + + if (sourceObjType == typeof(UInt16?)) + { + return (Double)((UInt16?)sourceObj).Value; + } + + if (sourceObjType == typeof(UInt32)) + { + return (Double)((UInt32)sourceObj); + } + + if (sourceObjType == typeof(UInt32?)) + { + return (Double)((UInt32?)sourceObj).Value; + } + + if (sourceObjType == typeof(UInt64)) + { + return (Double)((UInt64)sourceObj); + } + + if (sourceObjType == typeof(UInt64?)) + { + return (Double)((UInt64?)sourceObj).Value; + } + + if (sourceObjType == typeof(Single)) + { + return (Double)((Single)sourceObj); + } + + if (sourceObjType == typeof(Single?)) + { + return (Double)((Single?)sourceObj).Value; + } + + if (sourceObjType == typeof(Double)) + { + return (Double)sourceObj; + } + + if (sourceObjType == typeof(Double?)) + { + return ((Double?)sourceObj).Value; + } + + if (sourceObjType == typeof(Decimal)) + { + return (Double)((Decimal)sourceObj); + } + + if (sourceObjType == typeof(Decimal?)) + { + return (Double)((Decimal?)sourceObj).Value; + } + + return null; + } + + /// + /// Casts the object to the System.Decimal + /// + /// The source object + + public static Object PrimitiveCastDecimal(Object sourceObj) + { + if (sourceObj == null) + return null; + + var stringValue = sourceObj as string; + if (stringValue != null) + return WithParser(_parseDecimal, stringValue); + + return sourceObj.AsDecimal(); + } + + /// + /// Casts the object to the System.Numerics.BigInteger + /// + /// The source object + + public static Object PrimitiveCastBigInteger(Object sourceObj) + { + if (sourceObj == null) + return null; + + var stringValue = sourceObj as string; + if (stringValue != null) + return WithParser(_parseBigInteger, stringValue); + + return sourceObj.AsBigInteger(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/ChronoField.cs b/NEsper.Core/NEsper.Core/compat/ChronoField.cs new file mode 100755 index 000000000..dd555b87d --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/ChronoField.cs @@ -0,0 +1,21 @@ +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.compat +{ + public enum ChronoField + { + MILLI_OF_SECOND, + SECOND_OF_MINUTE, + MINUTE_OF_HOUR, + HOUR_OF_DAY, + DAY_OF_MONTH, + ALIGNED_WEEK_OF_YEAR, + MONTH_OF_YEAR, + YEAR + } +} diff --git a/NEsper.Core/NEsper.Core/compat/ChronoUnit.cs b/NEsper.Core/NEsper.Core/compat/ChronoUnit.cs new file mode 100755 index 000000000..5098bc5d4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/ChronoUnit.cs @@ -0,0 +1,33 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.compat +{ + /// + /// Designed to provide compatibility with the ChronoUnit from Java. + /// + public enum ChronoUnit + { + FOREVER, + ERAS, + MILLENNIA, + CENTURIES, + DECADES, + YEARS, + MONTHS, + WEEKS, + DAYS, + HALF_DAYS, + HOURS, + MINUTES, + SECONDS, + MILLIS, + MICROS, + NANOS + } +} diff --git a/NEsper.Core/NEsper.Core/compat/ClassLoader.cs b/NEsper.Core/NEsper.Core/compat/ClassLoader.cs new file mode 100755 index 000000000..962c4cd07 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/ClassLoader.cs @@ -0,0 +1,17 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.IO; + +namespace com.espertech.esper.compat +{ + public interface ClassLoader + { + Stream GetResourceAsStream(string resourceName); + } +} diff --git a/NEsper.Core/NEsper.Core/compat/ClassLoaderDefault.cs b/NEsper.Core/NEsper.Core/compat/ClassLoaderDefault.cs new file mode 100755 index 000000000..60a6a40ee --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/ClassLoaderDefault.cs @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.IO; + +namespace com.espertech.esper.compat +{ + public class ClassLoaderDefault : ClassLoader + { + public static ClassLoader GetInstance() + { + return new ClassLoaderDefault(); + } + + public Stream GetResourceAsStream(string resourceName) + { + return ResourceManager.GetResourceAsStream(resourceName); + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/ContextVar.cs b/NEsper.Core/NEsper.Core/compat/ContextVar.cs new file mode 100755 index 000000000..3f07ab511 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/ContextVar.cs @@ -0,0 +1,58 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; + +namespace com.espertech.esper.compat +{ + /// + /// Provides a stack-like object that can be used to maintain the state of a + /// thread-local value. Unlike a pure threadstatic variable a ContextVar can + /// have multiple values that can be stacked. + /// + /// + + public class ContextVar : IDisposable + { + [ThreadStatic] private static T current; + + /// + /// Gets the current value associated with the context. + /// + /// The current. + public static T Current + { + get { return current; } + } + + private readonly T previous; + + /// + /// Initializes a new instance of the class. + /// + /// The value. + public ContextVar(T value) + { + previous = current; + current = value; + } + + #region IDisposable Members + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + current = previous; + } + + #endregion + } +} diff --git a/NEsper.Core/NEsper.Core/compat/DateTimeConstants.cs b/NEsper.Core/NEsper.Core/compat/DateTimeConstants.cs new file mode 100755 index 000000000..34751df4d --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/DateTimeConstants.cs @@ -0,0 +1,22 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.compat +{ + public class DateTimeConstants + { + /// + /// Boundary offset represents a number of seconds that represent 24 hours worth of milliseconds. + /// All times are aligned to UTC time of zero ticks. However, during conversion, its common for + /// the time to be shifted due to an offset from UTC. This causes the number of milliseconds to + /// actually become negative. The boundary offset represents a number of milliseconds required + /// to handle values from zero and greater from a UTC standpoint. + /// + public const long Boundary = 86400 * 1000; + } +} diff --git a/NEsper.Core/NEsper.Core/compat/DateTimeEx.cs b/NEsper.Core/NEsper.Core/compat/DateTimeEx.cs new file mode 100755 index 000000000..1273cdf11 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/DateTimeEx.cs @@ -0,0 +1,474 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.datetime.calop; +using com.espertech.esper.epl.named; + +namespace com.espertech.esper.compat +{ + /// + /// DateTime with offset and timezone tracking. When math operations are performed against this + /// structure, they can take into account the timezone the date was associated with. + /// + public class DateTimeEx + : IComparable + , IComparable + { + private DateTimeOffset _dateTime; + private readonly TimeZoneInfo _timeZone; + + public int Year { get { return _dateTime.Year; } } + public int Month { get { return _dateTime.Month; } } + public int Day { get { return _dateTime.Day; } } + public int Hour { get { return _dateTime.Hour; } } + public int Minute { get { return _dateTime.Minute; } } + public int Second { get { return _dateTime.Second; } } + public int Millisecond { get { return _dateTime.Millisecond; } } + + public DayOfWeek DayOfWeek { get { return _dateTime.DayOfWeek; } } + + public long TimeInMillis { get { return _dateTime.TimeInMillis(); } } + + public int DayOfYear { get { return _dateTime.DateTime.DayOfYear; } } + + /// + /// Gets the underlying date time object. + /// + /// + /// The date time. + /// + public DateTimeOffset DateTime + { + get { return _dateTime; } + } + + /// + /// Gets the underlying date time object, translated to UTC. + /// + /// + /// The UTC date time. + /// + public DateTimeOffset UtcDateTime + { + get { return _dateTime.TranslateTo(TimeZoneInfo.Utc); } + } + + /// + /// Gets the time zone associated with the value. + /// + /// + /// The time zone. + /// + public TimeZoneInfo TimeZone + { + get { return _timeZone; } + } + + /// + /// Initializes a new instance of the class. + /// + /// The date time. + /// The time zone. + public DateTimeEx(DateTimeOffset dateTime, TimeZoneInfo timeZone) + { + DateTime dateTimeUTC = dateTime.UtcDateTime; + TimeSpan dateTimeOffset = timeZone.GetUtcOffset(dateTimeUTC); + _dateTime = new DateTimeOffset(dateTimeUTC).ToOffset(dateTimeOffset); + _timeZone = timeZone; + } + + public DateTimeEx( + int year, + int month, + int day, + int hour, + int minute, + int second, + int millis, + TimeZoneInfo timeZoneInfo) + { + var dateTimeReference = new DateTime(year, month, day, hour, minute, second, millis); + var dateTimeUtcOffset = timeZoneInfo.GetUtcOffset(dateTimeReference); + _dateTime = new DateTimeOffset(year, month, day, hour, minute, second, millis, dateTimeUtcOffset); + _timeZone = timeZoneInfo; + } + + public DateTimeEx(DateTimeEx source) + { + _dateTime = source._dateTime; + _timeZone = source._timeZone; + } + + public DateTimeEx Clone() + { + return new DateTimeEx(this); + } + + public DateTimeEx SetUtcMillis(long millis) + { + _dateTime = DateTimeOffsetHelper.TimeFromMillis(millis, _timeZone); + return this; + } + + public DateTimeEx Set(DateTimeOffset dateTime) + { + var dateTimeOffset = _timeZone.GetUtcOffset(dateTime); + _dateTime = new DateTimeOffset(dateTime.DateTime, dateTimeOffset); + return this; + } + + public DateTimeEx Set(int year, int month, int day, int hour = 0, int minute = 0, int second = 0, int millisecond = 0) + { + _dateTime = DateTimeOffsetHelper.CreateDateTime( + year, month, day, hour, minute, second, millisecond, _timeZone); + return this; + } + + public DateTimeEx SetYear(int year) + { + _dateTime = DateTimeOffsetHelper.CreateDateTime( + year, _dateTime.Month, _dateTime.Day, + _dateTime.Hour, _dateTime.Minute, _dateTime.Second, + _dateTime.Millisecond, _timeZone); + return this; + } + + public DateTimeEx SetMonth(int month) + { + _dateTime = DateTimeOffsetHelper.CreateDateTime( + _dateTime.Year, month, _dateTime.Day, + _dateTime.Hour, _dateTime.Minute, _dateTime.Second, + _dateTime.Millisecond, _timeZone); + return this; + } + + public DateTimeEx SetDay(int day) + { + _dateTime = DateTimeOffsetHelper.CreateDateTime( + _dateTime.Year, _dateTime.Month, day, + _dateTime.Hour, _dateTime.Minute, _dateTime.Second, + _dateTime.Millisecond, _timeZone); + return this; + } + + public DateTimeEx SetHour(int hour) + { + _dateTime = DateTimeOffsetHelper.CreateDateTime( + _dateTime.Year, _dateTime.Month, _dateTime.Day, + hour, _dateTime.Minute, _dateTime.Second, + _dateTime.Millisecond, _timeZone); + return this; + } + + public DateTimeEx SetMinute(int minute) + { + _dateTime = DateTimeOffsetHelper.CreateDateTime( + _dateTime.Year, _dateTime.Month, _dateTime.Day, + _dateTime.Hour, minute, _dateTime.Second, + _dateTime.Millisecond, _timeZone); + return this; + } + + public DateTimeEx SetSecond(int second) + { + _dateTime = DateTimeOffsetHelper.CreateDateTime( + _dateTime.Year, _dateTime.Month, _dateTime.Day, + _dateTime.Hour, _dateTime.Minute, second, + _dateTime.Millisecond, _timeZone); + return this; + } + + public DateTimeEx SetMillis(int millis) + { + _dateTime = DateTimeOffsetHelper.CreateDateTime( + _dateTime.Year, _dateTime.Month, _dateTime.Day, + _dateTime.Hour, _dateTime.Minute, _dateTime.Second, + millis, _timeZone); + return this; + } + + /// + /// Compares the current object with another object of the same type. + /// + /// An object to compare with this object. + /// + /// A value that indicates the relative order of the objects being compared. + /// + /// + public int CompareTo(DateTimeEx other) + { + return _dateTime.CompareTo(other._dateTime); + } + + /// + /// Compares the current instance with another object of the same type and + /// returns an integer that indicates whether the current instance precedes, + /// follows, or occurs in the same position in the sort order as the other + /// object. + /// + /// An object to compare with this instance. + /// + /// A value that indicates the relative order of the objects being compared. The return value has these meanings: Value Meaning Less than zero This instance precedes in the sort order. Zero This instance occurs in the same position in the sort order as . Greater than zero This instance follows in the sort order. + /// + public int CompareTo(object obj) + { + var otherDateTime = obj as DateTimeEx; + if (otherDateTime == null) + { + throw new ArgumentException("invalid value", nameof(obj)); + } + + return CompareTo(otherDateTime); + } + + protected bool Equals(DateTimeEx other) + { + return _dateTime.Equals(other._dateTime) && Equals(_timeZone, other._timeZone); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) + return false; + if (ReferenceEquals(this, obj)) + return true; + if (obj.GetType() != this.GetType()) + return false; + return Equals((DateTimeEx) obj); + } + + public override int GetHashCode() + { + unchecked + { + return (_dateTime.GetHashCode() * 397) ^ (_timeZone != null ? _timeZone.GetHashCode() : 0); + } + } + + public string ToString(TimeZoneInfo timeZone) + { + if (timeZone != null) + { + var offset = timeZone.GetUtcOffset(_dateTime); + return string.Format("{0} [TZ = {1}]", _dateTime.ToOffset(offset), _timeZone); + } + else + { + return string.Format("{0} [TZ = {1}]", _dateTime, _timeZone); + } + } + + public override string ToString() + { + return ToString(TimeZoneInfo.Local); + } + + public static bool operator >(DateTimeEx d1, DateTimeEx d2) { return d1._dateTime > d2._dateTime; } + public static bool operator >=(DateTimeEx d1, DateTimeEx d2) { return d1._dateTime >= d2._dateTime; } + public static bool operator <(DateTimeEx d1, DateTimeEx d2) { return d1._dateTime < d2._dateTime; } + public static bool operator <=(DateTimeEx d1, DateTimeEx d2) { return d1._dateTime <= d2._dateTime; } + + public static bool operator ==(DateTimeEx d1, DateTimeEx d2) + { + if (ReferenceEquals(d1, null)) + return ReferenceEquals(d2, null); + if (ReferenceEquals(d2, null)) + return false; + return d1._dateTime == d2._dateTime; + } + + public static bool operator !=(DateTimeEx d1, DateTimeEx d2) + { + if (ReferenceEquals(d1, null)) + return !ReferenceEquals(d2, null); + if (ReferenceEquals(d2, null)) + return true; + return d1._dateTime != d2._dateTime; + } + + public DateTimeEx AddYears(int amount, DateTimeMathStyle style = DateTimeMathStyle.CLR) + { + _dateTime = _dateTime.AddYears(amount); + if (style == DateTimeMathStyle.Java) + return Rebase(); + return this; + } + + public DateTimeEx AddMonths(int amount, DateTimeMathStyle style = DateTimeMathStyle.CLR) + { + if (style == DateTimeMathStyle.CLR) + { + _dateTime = _dateTime.AddMonths(amount); + } + else + { + _dateTime = _dateTime.AddMonthsLikeJava(amount); + return Rebase(); + } + + return this; + } + + public DateTimeEx AddDays(int amount, DateTimeMathStyle style = DateTimeMathStyle.CLR) + { + _dateTime = _dateTime.AddDays(amount); + if (style == DateTimeMathStyle.Java) + return Rebase(); + return this; + } + + public DateTimeEx AddHours(int amount, DateTimeMathStyle style = DateTimeMathStyle.CLR) + { + _dateTime = _dateTime.AddHours(amount); + if (style == DateTimeMathStyle.Java) + return Realign(); + return this; + } + + public DateTimeEx AddMinutes(int amount, DateTimeMathStyle style = DateTimeMathStyle.CLR) + { + _dateTime = _dateTime.AddMinutes(amount); + if (style == DateTimeMathStyle.Java) + return Realign(); + return this; + } + + public DateTimeEx AddSeconds(int amount, DateTimeMathStyle style = DateTimeMathStyle.CLR) + { + _dateTime = _dateTime.AddSeconds(amount); + if (style == DateTimeMathStyle.Java) + return Realign(); + return this; + } + + public DateTimeEx AddMilliseconds(int amount, DateTimeMathStyle style = DateTimeMathStyle.CLR) + { + _dateTime = _dateTime.AddMilliseconds(amount); + if (style == DateTimeMathStyle.Java) + return Realign(); + return this; + } + + /// + /// Adds to fields in the datetime. + /// + /// The field. + /// The value. + /// + /// + public DateTimeEx AddUsingField(int field, int value) + { + _dateTime = _dateTime.AddUsingField(field, value); + return this; + } + + public int GetFieldValue(int field) + { + return _dateTime.GetFieldValue(field); + } + + public DateTimeEx SetFieldValue(int field, int value) + { + _dateTime = _dateTime.SetFieldValue(field, value, _timeZone); + return this; + } + + public DateTimeEx SetMaximumDay() + { + _dateTime = _dateTime.GetWithMaximumDay(_timeZone); + return Rebase(); + } + + public DateTimeEx SetMaximumMonth() + { + _dateTime = _dateTime.GetWithMaximumMonth(_timeZone); + return Rebase(); + } + + private DateTimeEx Realign() + { + var offset = _timeZone.GetUtcOffset(_dateTime); + _dateTime = _dateTime.ToOffset(offset); + return this; + } + + private DateTimeEx Rebase() + { + _dateTime = new DateTimeOffset( + _dateTime.Year, + _dateTime.Month, + _dateTime.Day, + _dateTime.Hour, + _dateTime.Minute, + _dateTime.Second, + _dateTime.Millisecond, + _timeZone.GetUtcOffset(_dateTime)); + return this; + } + + public DateTimeEx SetMinimumWeek() + { + _dateTime = _dateTime.GetWithMinimumWeek(_timeZone); + return Rebase(); + } + + public DateTimeEx SetMaximumWeek() + { + _dateTime = _dateTime.GetWithMaximumWeek(_timeZone); + return Rebase(); + } + + public int GetActualMaximum(int dayOfMonth) + { + return DateTimeFieldMath.GetActualMaximum(_dateTime, DateTimeFieldEnum.DAY_OF_MONTH); + } + + public DateTimeEx MoveToWeek(int week) + { + _dateTime = _dateTime.MoveToWeek(week, _timeZone); + return Rebase(); + } + + public static DateTimeEx NowUtc() + { + return GetInstance(TimeZoneInfo.Utc); + } + + public static DateTimeEx NowLocal() + { + return GetInstance(TimeZoneInfo.Local); + } + + public static DateTimeEx GetInstance(TimeZoneInfo timeZoneInfo) + { + return new DateTimeEx( + DateTimeOffsetHelper.Now(timeZoneInfo), + timeZoneInfo + ); + } + + public static DateTimeEx GetInstance(TimeZoneInfo timeZoneInfo, DateTimeOffset dtoffset) + { + return new DateTimeEx(dtoffset, timeZoneInfo); + } + + public static DateTimeEx GetInstance(TimeZoneInfo timeZoneInfo, DateTime dateTime) + { + var baseDt = DateTimeOffsetHelper.ToDateTimeOffset(dateTime, timeZoneInfo); + return new DateTimeEx(baseDt, timeZoneInfo); + } + + public static DateTimeEx GetInstance(TimeZoneInfo timeZoneInfo, long timeInMillis) + { + var baseDt = DateTimeOffsetHelper.TimeFromMillis(timeInMillis, timeZoneInfo); + return new DateTimeEx(baseDt, timeZoneInfo); + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/DateTimeFormatter.cs b/NEsper.Core/NEsper.Core/compat/DateTimeFormatter.cs new file mode 100755 index 000000000..e73ea2f2c --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/DateTimeFormatter.cs @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.compat +{ + public class DateTimeFormatter + { + public static Func ISO_DATE_TIME; + + static DateTimeFormatter() + { + ISO_DATE_TIME = (dateTimeEx) => + { + return dateTimeEx.DateTime.UtcDateTime.ToString("s", System.Globalization.CultureInfo.InvariantCulture); + }; + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/DateTimeHelper.cs b/NEsper.Core/NEsper.Core/compat/DateTimeHelper.cs new file mode 100755 index 000000000..c3aa33305 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/DateTimeHelper.cs @@ -0,0 +1,222 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.compat +{ + /// + /// Assistant class to help with conversions between Java-style and + /// granularity dates and CLR-style DateTime. + /// + + public static class DateTimeHelper + { + /// + /// Number of ticks per millisecond + /// + + public const int TICKS_PER_MILLI = 10000; + + /// + /// Number of ticks per microsecond + /// + + public const int TICKS_PER_MICRO = 10; + + /// + /// Number of nanoseconds per tick + /// + + public const int NANOS_PER_TICK = 100; + + /// + /// Converts ticks to milliseconds + /// + /// + /// + + public static long TicksToMillis(long ticks) + { + return ticks / TICKS_PER_MILLI; + } + + /// + /// Converts ticks to nanoseconds + /// + /// + /// + + public static long TicksToNanos(long ticks) + { + return ticks * NANOS_PER_TICK; + } + + /// + /// Converts microseconds to ticks + /// + /// + /// + + public static long MicrosToTicks(long micros) + { + return micros * TICKS_PER_MICRO; + } + + /// + /// Converts milliseconds to ticks + /// + /// + /// + + public static long MillisToTicks(long millis) + { + return millis * TICKS_PER_MILLI; + } + + /// + /// Nanoses to ticks. + /// + /// The nanos. + public static long NanosToTicks(long nanos) + { + return nanos / NANOS_PER_TICK; + } + + /// + /// Gets the number of nanoseconds needed to represent + /// the datetime. + /// + /// The date time. + /// + public static long UtcNanos(DateTime dateTime) + { + if (dateTime.Kind == DateTimeKind.Utc) + return TicksToNanos(dateTime.Ticks) - (DateTimeConstants.Boundary * 100000); + else if (dateTime.Kind == DateTimeKind.Local) + return TicksToNanos(dateTime.ToUniversalTime().Ticks) - (DateTimeConstants.Boundary * 1000000); + + throw new ArgumentException("dateTime does not have kind specified"); + } + + /// + /// Gets the number of milliseconds needed to represent + /// the datetime. This is needed to convert from Java + /// datetime granularity (milliseconds) to CLR datetimes. + /// + /// + /// + + public static long UtcMillis(this DateTime dateTime) + { + if (dateTime.Kind == DateTimeKind.Utc) + return TicksToMillis(dateTime.Ticks) - DateTimeConstants.Boundary; + else if (dateTime.Kind == DateTimeKind.Local) + return TicksToMillis(dateTime.ToUniversalTime().Ticks) - DateTimeConstants.Boundary; + + throw new ArgumentException("dateTime does not have kind specified"); + } + + public static DateTime UtcFromMillis(this long millis) + { + return new DateTime(MillisToTicks(millis + DateTimeConstants.Boundary), DateTimeKind.Utc); + } + + public static DateTime UtcFromMicros(this long micros) + { + return new DateTime(MicrosToTicks(micros + DateTimeConstants.Boundary * 1000), DateTimeKind.Utc); + } + + public static DateTime FromMillis(this long millis) + { + return UtcFromMillis(millis).ToLocalTime(); + } + + public static DateTime FromMicros(this long micros) + { + return UtcFromMicros(micros).ToLocalTime(); + } + + public static DateTime GetCurrentTimeUniversal() + { + return DateTime.UtcNow; + } + + public static DateTime GetCurrentTime() + { + return DateTime.Now; + } + + /// + /// Returns the current time in millis + /// + + public static long GetCurrentTimeMillis() + { + return UtcMillis(DateTime.UtcNow); + } + + /// + /// Returns the current time in millis + /// + + public static long CurrentTimeMillis + { + get { return UtcMillis(DateTime.UtcNow); } + } + + /// + /// Gets the current time in nanoseconds. + /// + /// The current time nanos. + public static long CurrentTimeNanos + { + get { return UtcNanos(DateTime.UtcNow); } + } + + public static DateTimeOffset TranslateTo(this DateTime dateTime, TimeZoneInfo timeZone) + { + if (timeZone == null) + return dateTime; + return TimeZoneInfo.ConvertTime(new DateTimeOffset(dateTime), timeZone); + } + + public static string Print(this long timeInMillis, TimeZoneInfo timeZoneInfo = null) + { + if (timeZoneInfo == null) + timeZoneInfo = TimeZoneInfo.Local; + + return Print(DateTimeOffsetHelper.TimeFromMillis(timeInMillis, timeZoneInfo)); + } + + public static string Print(this DateTime dateTime, string format = "yyyy-MM-dd HH:mm:ss.fff") + { + return dateTime.ToString(format); + } + + public static string Print(this DateTimeOffset dateTime, string format = "yyyy-MM-dd HH:mm:ss.fff") + { + return dateTime.ToString(format); + } + + public static String PrintWithZone(this DateTimeOffset date) + { + return Print(date, "yyyy-MM-dd HH:mm:ss.fff%K"); + } + + public static string ToShortDateString(this DateTimeOffset dateTime) + { + return dateTime.Date.ToShortDateString(); + } + + public static string ToShortTimeString(this DateTimeOffset dateTime) + { + return dateTime.Date.ToShortTimeString(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/DateTimeMath.cs b/NEsper.Core/NEsper.Core/compat/DateTimeMath.cs new file mode 100755 index 000000000..329b6cf80 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/DateTimeMath.cs @@ -0,0 +1,289 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; + +namespace com.espertech.esper.compat +{ + public static class DateTimeMath + { + private static readonly Calendar Calendar = DateTimeFormatInfo.CurrentInfo.Calendar; + + public static DateTimeOffset AddMonthsLikeJava(this DateTimeOffset dateTime, int numMonths) + { + int month = dateTime.Month + numMonths; + int year = dateTime.Year; + + if (month > 12) + year += month/12; + else if (month == 0) + year--; + else if (month < 0) + year += 1 - (month / 12); // number will be negative + + // if we are moving the needle forward, then its simply a modulus + // to determine what month we should end up at. + if (month >= 0) + { + month %= 12; + if (month == 0) + { + month = 12; + } + } + else + { + // negative months occur when the numMonths is negative... + // reverse step requires that we do a modulus to see what + // negative month we are in and then subtracting that from + // twelve. + + month = month % 12 + 12; + } + + // its not enough to set the new month, we actually need to know + // how many days are in that month so that we avoid trying to set + // a date that's invalid... for example, adding a month to Jan 31st + // could end up with Feb 31s which is invalid. However, we want + // to ensure the date remains in the month of Feb, so we will + // pull it back to the maximum date. + + var daysInMonth = Calendar.GetDaysInMonth(year, month); + var dayOfMonth = dateTime.Day; + if (dayOfMonth > daysInMonth) + dayOfMonth = daysInMonth; + + return new DateTimeOffset( + year, + month, + dayOfMonth, + dateTime.Hour, + dateTime.Minute, + dateTime.Second, + dateTime.Millisecond, + dateTime.Offset + ); + } + + /// + /// Returns a datetime for the week given on the specified day of the week. + /// + /// From. + /// The day of week. + /// + public static DateTimeOffset MoveToDayOfWeek(DateTimeOffset from, DayOfWeek dayOfWeek) + { + var current = @from.DayOfWeek; + return @from.AddDays(dayOfWeek - current); + } + + /// + /// Returns a datetime for the end of the month. + /// + /// From. + /// + public static DateTimeOffset EndOfMonth(DateTimeOffset from) + { + return new DateTimeOffset( + @from.Year, + @from.Month, + DateTime.DaysInMonth(@from.Year, @from.Month), + 0, + 0, + 0, + @from.Offset); + } + + public static int GetWeekOfYear(this DateTime dateTime) + { + return Calendar.GetWeekOfYear(dateTime, CalendarWeekRule.FirstDay, DayOfWeek.Sunday); + } + + public static int GetWeekOfYear(this DateTimeOffset dateTime) + { + // we need the "offset" translated to a datetime because the calendar function does + // not support datetime offset objects. + var dateTimeRaw = new DateTime( + dateTime.Year, + dateTime.Month, + dateTime.Day, + 0, 0, 0, + DateTimeKind.Unspecified); + + return Calendar.GetWeekOfYear(dateTimeRaw, CalendarWeekRule.FirstDay, DayOfWeek.Sunday); + } + + public static DateTime GetWithMaximumDay(this DateTime dateTime) + { + var daysInMonth = Calendar.GetDaysInMonth(dateTime.Year, dateTime.Month); + return new DateTime(dateTime.Year, dateTime.Month, daysInMonth, dateTime.Hour, dateTime.Minute, + dateTime.Second, dateTime.Millisecond); + } + + public static DateTimeOffset GetWithMaximumDay(this DateTimeOffset dateTime, TimeZoneInfo timeZone = null) + { + var daysInMonth = Calendar.GetDaysInMonth(dateTime.Year, dateTime.Month); + if (timeZone == null) + { + return new DateTimeOffset( + dateTime.Year, dateTime.Month, daysInMonth, dateTime.Hour, dateTime.Minute, + dateTime.Second, dateTime.Millisecond, dateTime.Offset); + } + + return DateTimeOffsetHelper.CreateDateTime( + dateTime.Year, dateTime.Month, daysInMonth, dateTime.Hour, dateTime.Minute, + dateTime.Second, dateTime.Millisecond, timeZone); + } + + public static DateTime GetWithMaximumMonth(this DateTime dateTime) + { + var daysInMonth = Calendar.GetDaysInMonth(dateTime.Year, 12); + if (dateTime.Day < daysInMonth) + daysInMonth = dateTime.Day; + + return new DateTime(dateTime.Year, 12, daysInMonth, dateTime.Hour, dateTime.Minute, + dateTime.Second, dateTime.Millisecond); + } + + public static DateTimeOffset GetWithMaximumMonth(this DateTimeOffset dateTime, TimeZoneInfo timeZone = null) + { + var daysInMonth = Calendar.GetDaysInMonth(dateTime.Year, 12); + if (dateTime.Day < daysInMonth) + daysInMonth = dateTime.Day; + + if (timeZone == null) + { + return new DateTimeOffset( + dateTime.Year, 12, daysInMonth, dateTime.Hour, dateTime.Minute, + dateTime.Second, dateTime.Millisecond, dateTime.Offset); + } + + return DateTimeOffsetHelper.CreateDateTime( + dateTime.Year, 12, daysInMonth, dateTime.Hour, dateTime.Minute, + dateTime.Second, dateTime.Millisecond, timeZone); + } + + public static DateTime MoveToWeek(this DateTime dateTime, int targetWeek) + { + if ((targetWeek < 1) || (targetWeek > 52)) + throw new ArgumentException("invalid target week", "targetWeek"); + + var week = GetWeekOfYear(dateTime); + if (week == targetWeek) + return dateTime; + for (; week > targetWeek; week = GetWeekOfYear(dateTime)) + dateTime = dateTime.AddDays(-7); + for (; week < targetWeek; week = GetWeekOfYear(dateTime)) + dateTime = dateTime.AddDays(7); + + return dateTime; + } + + public static DateTimeOffset MoveToWeek(this DateTimeOffset dateTime, int targetWeek, TimeZoneInfo timeZone = null) + { + if ((targetWeek < 1) || (targetWeek > 52)) + throw new ArgumentException("invalid target week", "targetWeek"); + + var week = GetWeekOfYear(dateTime); + if (week == targetWeek) + return dateTime; + for (; week > targetWeek; week = GetWeekOfYear(dateTime)) + dateTime = dateTime.AddDays(-7); + for (; week < targetWeek; week = GetWeekOfYear(dateTime)) + dateTime = dateTime.AddDays(7); + + return dateTime; + } + + public static DateTime GetWithMaximumWeek(this DateTime dateTime) + { + do + { + var nextTime = dateTime.AddDays(7); + if ((dateTime.GetWeekOfYear() > 2) && (nextTime.GetWeekOfYear() <= 2)) + { + return dateTime; + } + + dateTime = nextTime; + } while (true); + } + + public static DateTimeOffset GetWithMaximumWeek(this DateTimeOffset dateTime, TimeZoneInfo timeZone = null) + { + do + { + var nextTime = dateTime.AddDays(7); + if ((GetWeekOfYear(dateTime) > 2) && (GetWeekOfYear(nextTime) <= 2)) + { + return dateTime; + } + + dateTime = nextTime; + } while (true); + } + + public static DateTime GetWithMinimumWeek(this DateTime dateTime) + { + var week = dateTime.GetWeekOfYear(); + if (week == 1) + { + return dateTime; + } + + do + { + var nextTime = dateTime.AddDays(-7); + if (dateTime.GetWeekOfYear() == 2) + { + // See if this day in the previous week is still in week 1. It's + // possible that a week started with a day like Friday and that the + // date in question was a Thursday. Technically, Thursday would + // have begun on week 2 not 1. + if (nextTime.GetWeekOfYear() == 1) + return nextTime; + // First occurrence of this date occurred on week 2 + return dateTime; + } + + dateTime = nextTime; + } while (true); + } + + public static DateTimeOffset GetWithMinimumWeek(this DateTimeOffset dateTime, TimeZoneInfo timeZone = null) + { + var week = GetWeekOfYear(dateTime); + if (week == 1) + { + return dateTime; + } + + do + { + var nextTime = dateTime.AddDays(-7); + if (GetWeekOfYear(dateTime) == 2) + { + // See if this day in the previous week is still in week 1. It's + // possible that a week started with a day like Friday and that the + // date in question was a Thursday. Technically, Thursday would + // have begun on week 2 not 1. + if (GetWeekOfYear(nextTime) == 1) + return nextTime; + // First occurrence of this date occurred on week 2 + return dateTime; + } + + dateTime = nextTime; + } while (true); + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/DateTimeMathStyle.cs b/NEsper.Core/NEsper.Core/compat/DateTimeMathStyle.cs new file mode 100755 index 000000000..54f8233f1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/DateTimeMathStyle.cs @@ -0,0 +1,21 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace com.espertech.esper.compat +{ + public enum DateTimeMathStyle + { + CLR, + Java + } +} diff --git a/NEsper.Core/NEsper.Core/compat/DateTimeOffsetHelper.cs b/NEsper.Core/NEsper.Core/compat/DateTimeOffsetHelper.cs new file mode 100755 index 000000000..aec87a821 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/DateTimeOffsetHelper.cs @@ -0,0 +1,159 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Globalization; +using System.Text.RegularExpressions; + +namespace com.espertech.esper.compat +{ + /// + /// Assistant class to help with conversions between Java-style and + /// granularity dates and CLR-style DateTime. + /// + + public static class DateTimeOffsetHelper + { + private static readonly TimeSpan BaseUtcOffset = TimeZoneInfo.Utc.BaseUtcOffset ; + + /// + /// Converts millis in CLR to millis in Java. In this case, it assumes that your + /// millis are the result of a conversion from UtcTicks. + /// + /// + /// + + public static long MillisToJavaMillis(long millis) + { + return millis - 62135596800000L; + } + + /// + /// Converts milliseconds into a datetime offset. + /// + /// The millis. + /// + public static DateTimeOffset MillisToDateTimeOffset(long millis) + { + return new DateTimeOffset(DateTimeHelper.MillisToTicks(millis + DateTimeConstants.Boundary), BaseUtcOffset); + } + + /// + /// Gets the number of milliseconds needed to represent the datetime. + /// + /// + /// + public static long TimeInMillis(this DateTimeOffset dateTime) + { + return DateTimeHelper.TicksToMillis(dateTime.UtcTicks) - DateTimeConstants.Boundary; + } + + /// + /// Gets the number of milliseconds needed to represent + /// the datetime. + /// + /// The date time. + /// + public static long InMillis(this DateTimeOffset dateTime) + { + return DateTimeHelper.TicksToMillis(dateTime.UtcTicks) - DateTimeConstants.Boundary; + } + + /// + /// Gets the datetime that matches the number of milliseconds provided. + /// + /// + /// + public static DateTimeOffset TimeFromMillis(this long millis, TimeSpan offset) + { + return new DateTimeOffset(DateTimeHelper.MillisToTicks(millis + DateTimeConstants.Boundary), BaseUtcOffset) + .ToOffset(offset); + } + + /// + /// Gets the datetime that matches the number of milliseconds provided. + /// + /// + /// + public static DateTimeOffset TimeFromMillis(this long millis, TimeZoneInfo timeZone) + { + timeZone = timeZone ?? TimeZoneInfo.Local; + var baseDateTime = new DateTimeOffset(DateTimeHelper.MillisToTicks(millis + DateTimeConstants.Boundary), BaseUtcOffset); + var timeZoneOffset = timeZone.GetUtcOffset(baseDateTime); + return baseDateTime.ToOffset(timeZoneOffset); + } + + /// + /// Creates the date time within the specified timezone + /// + /// The year. + /// The month. + /// The day. + /// The hour. + /// The minute. + /// The second. + /// The millisecond. + /// The time zone. + /// + public static DateTimeOffset CreateDateTime( + int year, + int month, + int day, + int hour, + int minute, + int second, + int millisecond, + TimeZoneInfo timeZone) + { + var dateTimeOffset = timeZone.GetUtcOffset(new DateTime(year, month, day, hour, minute, second, millisecond)); + return new DateTimeOffset(year, month, day, hour, minute, second, millisecond, dateTimeOffset); + } + + public static DateTimeOffset CreateDateTime( + int year, + int month, + int day, + int hour, + int minute, + int second, + int millisecond, + TimeSpan offset) + { + return new DateTimeOffset(year, month, day, hour, minute, second, millisecond, offset); + } + + /// + /// Normalizes the specified date time. + /// + /// The date time. + /// The time zone. + /// + public static DateTimeOffset Normalize(this DateTimeOffset dateTime, TimeZoneInfo timeZone) + { + if (timeZone == null) + return dateTime; + return dateTime.ToOffset(timeZone.GetUtcOffset(dateTime)); + } + + public static DateTimeOffset ToDateTimeOffset(this DateTime dateTime, TimeZoneInfo timeZone) + { + return new DateTimeOffset(dateTime, timeZone.GetUtcOffset(dateTime)); + } + + public static DateTimeOffset TranslateTo(this DateTimeOffset dateTime, TimeZoneInfo timeZone) + { + return timeZone == null ? dateTime : TimeZoneInfo.ConvertTime(dateTime, timeZone); + } + + public static DateTimeOffset Now(TimeZoneInfo timeZone) + { + timeZone = timeZone ?? TimeZoneInfo.Local; + return TimeZoneInfo.ConvertTime(DateTimeOffset.Now, timeZone); + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/DateTimeParser.cs b/NEsper.Core/NEsper.Core/compat/DateTimeParser.cs new file mode 100755 index 000000000..76e5bbf5d --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/DateTimeParser.cs @@ -0,0 +1,127 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Globalization; +using System.Text.RegularExpressions; + +namespace com.espertech.esper.compat +{ + public static class DateTimeParser + { + public static long ParseDefaultMSec(string dateTimeString) + { + return ParseDefaultEx(dateTimeString).TimeInMillis; + } + + public static DateTimeOffset ParseDefaultDate(string dateTimeString) + { + return ParseDefaultEx(dateTimeString).DateTime; + } + + public static DateTimeOffset ParseDefault(string dateTimeString) + { + return ParseDefaultEx(dateTimeString).DateTime; + } + + public static DateTimeEx ParseDefaultEx(string dateTimeString) + { + DateTimeOffset dateTime; + + var match = Regex.Match(dateTimeString, @"^(\d+)-(\d+)-(\d+)T(\d+):(\d+):(\d+)\.(\d+)$"); + if (match != Match.Empty) + { + dateTimeString = String.Format( + "{0}-{1}-{2} {3}:{4}:{5}.{6}", + Int32.Parse(match.Groups[1].Value).ToString(CultureInfo.InvariantCulture).PadLeft(4, '0'), + Int32.Parse(match.Groups[2].Value).ToString(CultureInfo.InvariantCulture).PadLeft(2, '0'), + Int32.Parse(match.Groups[3].Value).ToString(CultureInfo.InvariantCulture).PadLeft(2, '0'), + Int32.Parse(match.Groups[4].Value).ToString(CultureInfo.InvariantCulture).PadLeft(2, '0'), + Int32.Parse(match.Groups[5].Value).ToString(CultureInfo.InvariantCulture).PadLeft(2, '0'), + Int32.Parse(match.Groups[6].Value).ToString(CultureInfo.InvariantCulture).PadLeft(2, '0'), + match.Groups[7].Value); + } + + var timeZone = dateTimeString.EndsWith("Z") ? TimeZoneInfo.Utc : TimeZoneInfo.Local; + + if ((DateTimeOffset.TryParseExact(dateTimeString, "yyyy-MM-dd HH:mm:ss.fff", null, DateTimeStyles.None, out dateTime)) || + (DateTimeOffset.TryParseExact(dateTimeString, "yyyy-MM-dd HH:mm:ss.ff", null, DateTimeStyles.None, out dateTime))) + return new DateTimeEx(dateTime, timeZone); + + // there is an odd situation where we intend to parse down to milliseconds but someone passes a four digit value + // - in this case, Java interprets this as a millisecond value but the CLR will interpret this as a tenth of a + // - millisecond value. to be consistent, I've made our implementation behave in a fashion similar to the java + // - implementation. + + if (DateTimeOffset.TryParseExact(dateTimeString, "yyyy-MM-dd HH:mm:ss.ffff", null, DateTimeStyles.None, out dateTime)) + { + var millis = (dateTime.Ticks % 10000000) / 1000; + dateTime = dateTime.AddMilliseconds(-millis / 10).AddMilliseconds(millis); + return new DateTimeEx(dateTime, timeZone); + } + + return new DateTimeEx(DateTimeOffset.Parse(dateTimeString), timeZone); + } + + public static DateTimeEx ParseDefaultExWZone(string dateTimeWithZone) + { + var match = Regex.Match(dateTimeWithZone, @"^(\d{1,4}-\d{1,2}-\d{1,2})[T ](\d{1,2}:\d{1,2}:\d{1,2})(\.\d{1,4}|)(.*)$"); + if (match != Match.Empty) + { + var matchDate = Regex.Match(match.Groups[1].Value, @"(\d{1,4})-(\d{1,2})-(\d{1,2})"); + var rwDate = string.Format("{0}-{1}-{2}", + int.Parse(matchDate.Groups[1].Value).ToString(CultureInfo.InvariantCulture).PadLeft(4, '0'), + int.Parse(matchDate.Groups[2].Value).ToString(CultureInfo.InvariantCulture).PadLeft(2, '0'), + int.Parse(matchDate.Groups[3].Value).ToString(CultureInfo.InvariantCulture).PadLeft(2, '0')); + + var matchTime = Regex.Match(match.Groups[2].Value, @"(\d{1,2}):(\d{1,2}):(\d{1,2})"); + var rwTime = string.Format("{0}:{1}:{2}", + int.Parse(matchTime.Groups[1].Value).ToString(CultureInfo.InvariantCulture).PadLeft(2, '0'), + int.Parse(matchTime.Groups[2].Value).ToString(CultureInfo.InvariantCulture).PadLeft(2, '0'), + int.Parse(matchTime.Groups[3].Value).ToString(CultureInfo.InvariantCulture).PadLeft(2, '0')); + + var provider = CultureInfo.InvariantCulture; + var dateTimeText = rwDate + ' ' + rwTime + match.Groups[3].Value; + + DateTimeOffset dateTime; + + // quick rewrite + dateTimeWithZone = rwDate + ' ' + rwTime + match.Groups[3].Value + match.Groups[4].Value; + if ((DateTimeOffset.TryParseExact(dateTimeWithZone, "yyyy-MM-dd HH:mm:ss.ffff'GMT'zzz", provider, DateTimeStyles.None, out dateTime)) || + (DateTimeOffset.TryParseExact(dateTimeWithZone, "yyyy-MM-dd HH:mm:ss.fff'GMT'zzz", provider, DateTimeStyles.None, out dateTime)) || + (DateTimeOffset.TryParseExact(dateTimeWithZone, "yyyy-MM-dd HH:mm:ss.ff'GMT'zzz", provider, DateTimeStyles.None, out dateTime))) + { + var timeZoneText = match.Groups[4].Value; + var timeZone = (timeZoneText != string.Empty) + ? TimeZoneHelper.GetTimeZoneInfo(timeZoneText) + : TimeZoneInfo.Local; + return new DateTimeEx(dateTime, timeZone); + } + + if ((DateTimeOffset.TryParseExact(dateTimeText, "yyyy-MM-dd HH:mm:ss.ffff", provider, DateTimeStyles.None, out dateTime)) || + (DateTimeOffset.TryParseExact(dateTimeText, "yyyy-MM-dd HH:mm:ss.fff", provider, DateTimeStyles.None, out dateTime)) || + (DateTimeOffset.TryParseExact(dateTimeText, "yyyy-MM-dd HH:mm:ss.ff", provider, DateTimeStyles.None, out dateTime))) + { + var timeZoneText = match.Groups[3].Value; + var timeZone = (timeZoneText != string.Empty) + ? TimeZoneHelper.GetTimeZoneInfo(timeZoneText) + : TimeZoneInfo.Local; + return new DateTimeEx(dateTime, timeZone); + } + } + + var timeZoneEx = dateTimeWithZone.EndsWith("Z") ? TimeZoneInfo.Utc : TimeZoneInfo.Local; + return new DateTimeEx(DateTimeOffset.Parse(dateTimeWithZone), timeZoneEx); + } + + public static long ParseDefaultMSecWZone(string dateTimeWithZone) + { + return ParseDefaultExWZone(dateTimeWithZone).TimeInMillis; + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/DefaultTypeCasterFactory.cs b/NEsper.Core/NEsper.Core/compat/DefaultTypeCasterFactory.cs new file mode 100755 index 000000000..cff148cf4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/DefaultTypeCasterFactory.cs @@ -0,0 +1,113 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; +using System.Linq.Expressions; + +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.compat +{ + /// + /// DefaultTypeCasterFactory is a class that : the methods required to + /// transform objects from one type to another type. This specific class allows + /// the developer to override the behavior that occurs during creation of new + /// TypeCasters. + /// + public class DefaultTypeCasterFactory + { + private static readonly Dictionary typePairConverterTable = + new Dictionary(); + + /// + /// Gets or creates a typeCaster for the specified pair of types. + /// + /// Type of the source. + /// Type of the target. + /// + public TypeCaster GetTypeCaster(Type sourceType, Type targetType) + { + // If the target is nullable then it's because of boxing. From the perspective + // of how we put items on the stack its not that much different from an unboxed + // value since the return value must be boxed for return anyway. + sourceType = GetTrueType(sourceType); + targetType = GetTrueType(targetType); + + var typePair = new TypePair(sourceType, targetType); + var typePairConverter = typePairConverterTable.Get(typePair); + if (typePairConverter == null) + { + typePairConverter = EmitTypePairConverter(typePair); + typePairConverterTable[typePair] = typePairConverter; + } + + return typePairConverter; + } + + /// + /// Returns the source as the target; this is used when the source type and + /// target types are identical. + /// + /// The source. + /// + private static Object IdentityTypePairConverter(Object source) + { + return source; + } + + /// + /// Returns the source as a string. + /// + /// The source. + /// + private static Object StringTypePairConverter(Object source) + { + return source != null ? source.ToString() : null; + } + + /// + /// Emits a type caster for the type pair. + /// + /// The type pair. + /// + public TypeCaster EmitTypePairConverter(TypePair typePair) + { + // Are the source type and target types identical + if (typePair.TypeA == typePair.TypeB) return IdentityTypePairConverter; + // Are the type pairs assignable + if (typePair.TypeB.IsAssignableFrom(typePair.TypeA)) return IdentityTypePairConverter; + // Is the target type a string + if (typePair.TypeB == typeof (string)) return StringTypePairConverter; + // Are the source and target types primitives + // Dynamically emit a typeCaster + var eParam = Expression.Parameter(typeof (object), "source"); + var eCastA = Expression.ConvertChecked(eParam, typePair.TypeA); // Cast to source type + var eCastB = Expression.ConvertChecked(eCastA, typePair.TypeB); // Cast to target type + var eCastC = Expression.Convert(eCastB, typeof(object)); // Cast to return type + var eCheck = Expression.Equal(eParam, Expression.Constant(null)); + var eCondition = Expression.Condition(eCheck, Expression.Constant(null), eCastC); + var eLambda = Expression.Lambda>(eCondition, eParam); + Func tempFunc = eLambda.Compile(); + return tempFunc.Invoke; + } + + /// + /// Gets the true underlying type of the provided type. Basically it unmasks + /// nullables. + /// + /// The type. + /// + public static Type GetTrueType(Type type) + { + var baseT = Nullable.GetUnderlyingType(type); + return baseT ?? type; + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/EnumHelper.cs b/NEsper.Core/NEsper.Core/compat/EnumHelper.cs new file mode 100755 index 000000000..690a9965e --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/EnumHelper.cs @@ -0,0 +1,131 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.compat +{ + /// + /// Collection of utility methods to help with enumerated types. + /// + public static class EnumHelper + { + public static TOut Xlate(this object value) + where TOut : struct + { + TOut ovalue; + Enum.TryParse(value.ToString(), true, out ovalue); + return ovalue; + } + + public static TOut Translate(this TIn value) + where TIn : struct + where TOut : struct + { + TOut ovalue; + Enum.TryParse(value.ToString(), true, out ovalue); + return ovalue; + } + + /// + /// Parses the specified text value and converts it into the specified + /// type of enumeration. + /// + /// + /// The text value. + /// if set to true [ignore case]. + /// + public static T Parse( String textValue, bool ignoreCase = true ) + { + return (T) Enum.Parse(typeof (T), textValue, ignoreCase); + } + + /// + /// Parses the specified enumeration returning the value in a boxable container + /// to allow for null values. + /// + /// + /// The text value. + /// if set to true [ignore case]. + /// + public static T? ParseBoxed( String textValue, bool ignoreCase = true ) where T : struct + { + T value; + if (Enum.TryParse(textValue, ignoreCase, out value)) + return value; + return null; + } + + /// + /// Gets the names. + /// + /// + /// + public static IEnumerable GetNames() + { + return Enum.GetNames(typeof (T)); + } + + /// + /// Gets the name associated with the value presented in enumValue. + /// + /// + /// The enum value. + /// + public static string GetName( this T enumValue ) + { + if (typeof(T).IsEnum) + return Enum.GetName(typeof (T), enumValue); + throw new ArgumentException("type is not an enumeration"); + } + + public static T GetValue(int ordinal) + { + foreach(T value in GetValues()) + { + if (Equals(ordinal, value)) + { + return value; + } + } + + throw new ArgumentException("ordinal value not found"); + } + + /// + /// Gets the values. + /// + /// + /// + public static IEnumerable GetValues() + { + Array array = Enum.GetValues(typeof (T)); + for(int ii = 0 ; ii < array.Length ; ii++) + { + yield return (T) array.GetValue(ii); + } + } + + public static int CountValues() + { + Array array = Enum.GetValues(typeof(T)); + return array.Length; + } + + public static void ForEach(Action valueHandler) + { + Array array = Enum.GetValues(typeof(T)); + for(int ii = 0; ii < array.Length; ii++) + { + valueHandler((T)array.GetValue(ii)); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/EventCoordinator.cs b/NEsper.Core/NEsper.Core/compat/EventCoordinator.cs new file mode 100755 index 000000000..84b2511d6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/EventCoordinator.cs @@ -0,0 +1,67 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Threading; + +namespace com.espertech.esper.compat +{ + /// + /// Use this to coordinate an event that has multiple participants. + /// To use it, have each participant increment the coordinator during + /// their initialization and have each participant signal the coordinator + /// when they are ready. An application that is pending coordination + /// should call the WaitAll() method to wait for all participants. + /// + + public class EventCoordinator + { + private int numCounter = 0; + private readonly Object subLock = new object(); + + /// + /// Signals this instance. + /// + public void Signal() + { + lock( subLock ) + { + if ( --numCounter == 0 ) + { + Monitor.PulseAll(subLock); + } + } + } + + /// + /// Increments the counter. + /// + public void Increment() + { + lock( subLock ) + { + numCounter++; + } + } + + /// + /// Waits all. + /// + public void WaitAll() + { + lock( subLock ) + { + while( numCounter != 0 ) + { + Monitor.Wait(subLock); + } + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/FactoryDelegate.cs b/NEsper.Core/NEsper.Core/compat/FactoryDelegate.cs new file mode 100755 index 000000000..aa17d4ac2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/FactoryDelegate.cs @@ -0,0 +1,18 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +namespace com.espertech.esper.compat +{ + /// + /// Factory pattern delegate that creates an object of type T. + /// + /// + /// + public delegate T FactoryDelegate(); +} diff --git a/NEsper.Core/NEsper.Core/compat/FlowTracer.cs b/NEsper.Core/NEsper.Core/compat/FlowTracer.cs new file mode 100755 index 000000000..77e3db24d --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/FlowTracer.cs @@ -0,0 +1,92 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat.logging; + +namespace com.espertech.esper.compat +{ + /// + /// Can be used to TRACE flow through a process. + /// + public class FlowTracer : IDisposable + { + [ThreadStatic] + private static string _indent; + + private readonly ILog _log; + private readonly string _id; + private readonly string _saved; + + /// + /// Initializes a new instance of the class. + /// + public FlowTracer() + : this(DefaultLog, CurrentMethodName()) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The log. + public FlowTracer(ILog log) + : this(log, CurrentMethodName()) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The id. + public FlowTracer(String id) + : this(DefaultLog, id) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The log. + /// The id. + public FlowTracer(ILog log, string id) + { + if (String.IsNullOrEmpty(_indent)) + { + _indent = ""; + } + + _id = id; + _saved = _indent; + _log = log; + + _indent = _indent + '>'; + _log.Debug("{0} Enter > {1}", _indent, _id); + } + + private static string CurrentMethodName() + { + var stackTrace = new System.Diagnostics.StackTrace(); + var stackFrame = stackTrace.GetFrame(2); + return stackFrame.GetMethod().Name; + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + _log.Debug("{0} Leave > {1}", _indent, _id); + _indent = _saved; + } + + private static readonly ILog DefaultLog = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + } +} diff --git a/NEsper.Core/NEsper.Core/compat/HighResolutionTimeProvider.cs b/NEsper.Core/NEsper.Core/compat/HighResolutionTimeProvider.cs new file mode 100755 index 000000000..8af6a1a1c --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/HighResolutionTimeProvider.cs @@ -0,0 +1,109 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Runtime.InteropServices; + +namespace com.espertech.esper.compat +{ + public class HighResolutionTimeProvider + { + [DllImport("Kernel32.dll")] + private static extern bool QueryPerformanceCounter(out long lpPerformanceCount); + + [DllImport("Kernel32.dll")] + private static extern bool QueryPerformanceFrequency(out long lpFrequency); + + public const long DriftAllowance = 5000000000L; + + public static readonly HighResolutionTimeProvider Instance = new HighResolutionTimeProvider(); + + /// + /// Gets the # of nano-seconds that were reported by DateTime.GetInstance + /// when we reset the baseline. It is used to determine the starting + /// point from which all other performance measurements are calculated. + /// + public long BaseNano + { + get { return _baseNano; } + } + + /// + /// Gets the # of nano-seconds reported by NanoTime when we + /// initialized the timer. + /// + /// The base time. + public long BaseTime + { + get { return _baseTime; } + } + + private long _frequency; + + /// + /// Gets the current time. + /// + /// The current time. + public long CurrentTime + { + get + { + long time; + QueryPerformanceCounter(out time); + double nanoTime = (time * 1000000000.0) / _frequency; + if (nanoTime > _resetTime) { + ResetBaseline(); + } + + return (long) (_baseDelta + nanoTime); + } + } + + /// + /// Represents the # of nano-seconds that were reported by DateTime.GetInstance + /// when we reset the baseline. It is used to determine the starting + /// point from which all other performance measurements are calculated. + /// + private long _baseNano; + /// + /// Represents the # of nano-seconds reported by NanoTime when we + /// initialized the timer. + /// + private long _baseTime; + /// + /// Represents the # of nano-seconds at which we will reset the baseline. + /// This accounts for drift between the high resolution timer and the + /// internal clock. + /// + private double _resetTime; + + private long _baseDelta; + + /// + /// Initializes a new instance of the class. + /// + public HighResolutionTimeProvider() + { + ResetBaseline(); + } + + private void ResetBaseline() + { + long time; + DateTime now = DateTime.Now; + QueryPerformanceFrequency(out _frequency); + QueryPerformanceCounter(out time); + // ~1-5us can pass calling perf counter + _baseNano = now.Ticks * 100; + _baseTime = (long)(time * 1000000000.0m / _frequency); + _baseDelta = _baseNano - _baseTime; + _resetTime = _baseTime + DriftAllowance; + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/ICache.cs b/NEsper.Core/NEsper.Core/compat/ICache.cs new file mode 100755 index 000000000..991054b1e --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/ICache.cs @@ -0,0 +1,19 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +namespace com.espertech.esper.compat +{ + public interface ICache where K : class + { + bool TryGet(K key, out V value); + V Get(K key); + V Put(K key, V value); + void Invalidate(); + } +} diff --git a/NEsper.Core/NEsper.Core/compat/IllegalStateException.cs b/NEsper.Core/NEsper.Core/compat/IllegalStateException.cs new file mode 100755 index 000000000..ec76622c1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/IllegalStateException.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; + +namespace com.espertech.esper.compat +{ + /// + /// An exception that occurs when some illegal state occurs. + /// + + [Serializable] + public class IllegalStateException : Exception + { + /// + /// Initializes a new instance of the class. + /// + public IllegalStateException() : base() { } + /// + /// Initializes a new instance of the class. + /// + /// The message. + public IllegalStateException( string message ) : base( message ) { } + /// + /// Initializes a new instance of the class. + /// + /// The message. + /// The underlying exception. + public IllegalStateException(string message, Exception e) : base(message, e) { } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/MathContext.cs b/NEsper.Core/NEsper.Core/compat/MathContext.cs new file mode 100755 index 000000000..d874f347f --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/MathContext.cs @@ -0,0 +1,87 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Text.RegularExpressions; + +namespace com.espertech.esper.compat +{ + [Serializable] + public class MathContext + { + public static readonly MathContext DECIMAL32; + + static MathContext() + { + DECIMAL32 = new MathContext(MidpointRounding.AwayFromZero, 7); + } + + /// + /// Gets the rounding mode. + /// + /// The rounding mode. + public MidpointRounding RoundingMode { get; set; } + + /// + /// Gets the precision. + /// + /// The precision. + public int Precision { get; set; } + + /// + /// Initializes a new instance of the class. + /// + public MathContext() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The rounding mode. + /// The precision. + public MathContext(MidpointRounding roundingMode, int precision) + { + RoundingMode = roundingMode; + Precision = precision; + } + + /// + /// Initializes a new instance of the class. + /// + /// The val. + public MathContext(string val) + { + var matchPrecision = Regex.Match(val, @"Precision=[ ]*(\d+)"); + if (matchPrecision == Match.Empty) + { + throw new ArgumentException("Precision missing"); + } + + var matchRounding = Regex.Match(val, @"RoundingMode=[ ]*([\.\w-]+)[,]*"); + if (matchRounding == Match.Empty) + { + throw new ArgumentException("RoundingMode missing"); + } + + Precision = int.Parse(matchPrecision.Groups[1].Value); + RoundingMode = EnumHelper.Parse(matchRounding.Groups[1].Value, true); + } + + /// + /// Returns a that represents this instance. + /// + /// + /// A that represents this instance. + /// + public override string ToString() + { + return string.Format("RoundingMode= {0}, Precision= {1}", RoundingMode, Precision); + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/MetaEnum.cs b/NEsper.Core/NEsper.Core/compat/MetaEnum.cs new file mode 100755 index 000000000..b385ad44f --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/MetaEnum.cs @@ -0,0 +1,81 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Linq; +using System.Reflection; + +namespace com.espertech.esper.compat +{ + /// + /// Methos for meta enumerations. + /// + public static class MetaEnum + { + public static String GetMetaName(this T metaEnumInstance) + { + return typeof (T) + .GetFields(BindingFlags.Public | BindingFlags.Static) + .Where(field => field.FieldType == typeof (T)) + .Where(field => ReferenceEquals(field.GetValue(null), metaEnumInstance)) + .Select(field => field.Name) + .FirstOrDefault(); + } + + /// + /// Gets the named clause. + /// + /// Name of the enum. + /// + public static T GetMetaEnum(this String enumName) + { + enumName = enumName.ToUpperInvariant(); + + FieldInfo fieldInfo = typeof (T) + .GetFields(BindingFlags.Public | BindingFlags.Static) + .Where(field => field.FieldType == typeof (T)) + .Where(field => field.Name == enumName) + .FirstOrDefault(); + if (fieldInfo == null) { + throw new ArgumentException("enumName"); + } + + return (T) fieldInfo.GetValue(null); + } + + public static TResult GetMetaEnum(this T2 enumValue) + { + string enumName = enumValue.ToString().ToUpperInvariant(); + + FieldInfo fieldInfo = typeof(TResult) + .GetFields(BindingFlags.Public | BindingFlags.Static) + .Where(field => field.FieldType == typeof(TResult)) + .Where(field => field.Name == enumName) + .FirstOrDefault(); + if (fieldInfo == null) + { + throw new ArgumentException("enumName"); + } + + return (TResult)fieldInfo.GetValue(null); + } + + /// + /// Gets the named enumerated value. + /// + /// + /// The type of the result. + /// The enum ordinal. + /// + public static TResult GetNamedEnum(int enumOrdinal) + { + return GetMetaEnum (Enum.GetName(typeof(T), enumOrdinal)); + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/MetaName.cs b/NEsper.Core/NEsper.Core/compat/MetaName.cs new file mode 100755 index 000000000..4ee0cf490 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/MetaName.cs @@ -0,0 +1,33 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2015 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.compat +{ + public class MetaName + { + public String Name { get; set; } + + /// + /// Initializes a new instance of the class. + /// + public MetaName() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The name. + public MetaName(string name) + { + Name = name; + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/Mutable.cs b/NEsper.Core/NEsper.Core/compat/Mutable.cs new file mode 100755 index 000000000..6d9aef0c5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/Mutable.cs @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.compat +{ + /// + /// Thread local data + /// + public class Mutable + { + public T Value; + + public Mutable() + { + } + + public Mutable(T value) + { + Value = value; + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/Name.cs b/NEsper.Core/NEsper.Core/compat/Name.cs new file mode 100755 index 000000000..85394f05e --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/Name.cs @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.util; + +namespace com.espertech.esper.compat +{ + public class Name + { + public static string Clean(Type type, bool useBoxed = true) + { + return (useBoxed ? type.GetBoxedType() : type).GetCleanName(); + } + + public static string Clean(bool useBoxed = true) + { + return (useBoxed ? typeof (T).GetBoxedType() : typeof (T)).GetCleanName(); + } + + public static string Of(Type type, bool useBoxed = true) + { + if (useBoxed) + { + type = type.GetBoxedType(); + } + + return type.FullName; + + } + + public static string Of(bool useBoxed = true) + { + if (useBoxed) + { + return typeof (T).GetBoxedType().FullName; + } + + return typeof (T).FullName; + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/compat/NoCache.cs b/NEsper.Core/NEsper.Core/compat/NoCache.cs new file mode 100755 index 000000000..3496384c9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/NoCache.cs @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +namespace com.espertech.esper.compat +{ + public class NoCache : ICache where K : class + { + public bool TryGet(K key, out V value) + { + value = default(V); + return false; + } + + public V Get(K key) + { + return default(V); + } + + public V Put(K key, V value) + { + return value; + } + + public void Invalidate() + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/ObjectFactory.cs b/NEsper.Core/NEsper.Core/compat/ObjectFactory.cs new file mode 100755 index 000000000..dff3de389 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/ObjectFactory.cs @@ -0,0 +1,21 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; + +namespace com.espertech.esper.compat +{ + /// + /// Parses an object from an input. + /// + /// + /// + + public delegate Object ObjectFactory(T input); +} diff --git a/NEsper.Core/NEsper.Core/compat/ObservableCall.cs b/NEsper.Core/NEsper.Core/compat/ObservableCall.cs new file mode 100755 index 000000000..da5ce9a12 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/ObservableCall.cs @@ -0,0 +1,18 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +namespace com.espertech.esper.compat +{ + /// + /// A simple delegate that can be observed. Observable delegates are primarily + /// designed to be used with calls that wrap the child call for collection of + /// diagnostics. + /// + public delegate void ObservableCall(); +} diff --git a/NEsper.Core/NEsper.Core/compat/PerformanceObserver.cs b/NEsper.Core/NEsper.Core/compat/PerformanceObserver.cs new file mode 100755 index 000000000..73709a3a5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/PerformanceObserver.cs @@ -0,0 +1,91 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.compat +{ + public class PerformanceObserver + { + public static long NanoTime + { + get + { +#if MONO + return PerformanceObserverMono.NanoTime; +#else + return PerformanceObserverWin.NanoTime; +#endif + } + } + + public static long MicroTime + { + get + { +#if MONO + return PerformanceObserverMono.MicroTime; +#else + return PerformanceObserverWin.MicroTime; +#endif + } + } + + public static long MilliTime + { + get + { +#if MONO + return PerformanceObserverMono.MilliTime; +#else + return PerformanceObserverWin.MilliTime; +#endif + } + } + + public static long TimeNano( Runnable r ) + { +#if MONO + return PerformanceObserverMono.TimeNano(r); +#else + return PerformanceObserverWin.TimeNano(r); +#endif + } + + public static long TimeMicro(Runnable r) + { +#if MONO + return PerformanceObserverMono.TimeMicro(r); +#else + return PerformanceObserverWin.TimeMicro(r); +#endif + } + + public static long TimeMillis(Runnable r) + { +#if MONO + return PerformanceObserverMono.TimeMillis(r); +#else + return PerformanceObserverWin.TimeMillis(r); +#endif + } + + public static long GetTimeMillis() + { + return MilliTime; + } + + public static long GetTimeMicros() + { + return MicroTime; + } + + public static long GetTimeNanos() + { + return NanoTime; + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/PerformanceObserverMono.cs b/NEsper.Core/NEsper.Core/compat/PerformanceObserverMono.cs new file mode 100755 index 000000000..04ee32735 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/PerformanceObserverMono.cs @@ -0,0 +1,58 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.compat +{ + /// + /// Implementation of the performance observer turned for use on Windows. + /// + + public class PerformanceObserverMono + { + public static long NanoTime + { + get { return DateTime.Now.Ticks*100; } + } + + public static long MicroTime + { + get { return DateTime.Now.Ticks / 10; } + } + + public static long MilliTime + { + get { return DateTime.Now.Ticks / 10000; } + } + + public static long TimeNano(Runnable r) + { + long timeA = DateTime.Now.Ticks; + r.Invoke(); + long timeB = DateTime.Now.Ticks; + return 100*(timeB - timeA); + } + + public static long TimeMicro(Runnable r) + { + long timeA = DateTime.Now.Ticks; + r.Invoke(); + long timeB = DateTime.Now.Ticks; + return (timeB - timeA)/10; + } + + public static long TimeMillis(Runnable r) + { + long timeA = DateTime.Now.Ticks; + r.Invoke(); + long timeB = DateTime.Now.Ticks; + return (timeB - timeA)/10000; + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/PerformanceObserverWin.cs b/NEsper.Core/NEsper.Core/compat/PerformanceObserverWin.cs new file mode 100755 index 000000000..60f44498f --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/PerformanceObserverWin.cs @@ -0,0 +1,141 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Runtime.InteropServices; + +namespace com.espertech.esper.compat +{ + /// + /// Implementation of the performance observer turned for use on Windows. + /// + + public class PerformanceObserverWin + { + [DllImport("Kernel32.dll")] + public static extern bool QueryPerformanceCounter(out long lpPerformanceCount); + + [DllImport("Kernel32.dll")] + public static extern bool QueryPerformanceFrequency(out long lpFrequency); + + [DllImport("Kernel32.dll", EntryPoint = "GetCurrentThreadId", ExactSpelling = true)] + public static extern Int32 GetCurrentWin32ThreadId(); + + public static long Frequency; + public static double MpMilli; + public static double MpMicro; + public static double MpNano; + + public static int SpinIterationsPerMicro; + + static PerformanceObserverWin() + { + Calibrate(); + } + + public static void Calibrate() + { + QueryPerformanceFrequency(out Frequency); + MpMilli = 1000.0 / Frequency; + MpMicro = 1000000.0 / Frequency; + MpNano = 1000000000.0 / Frequency; + + // Our goal is to increase the iterations until we get at least 100 microseconds of + // actual spin latency. + + long numCounter = (long) (Frequency / 1000.0); + + for( int nn = 2 ;; nn *= 2 ) { + long timeA; + long timeB; + QueryPerformanceCounter(out timeA); + System.Threading.Thread.SpinWait(nn); + QueryPerformanceCounter(out timeB); + + var measured = timeB - timeA; + if (measured >= numCounter) { + // We have achieved at least 1000 microseconds of delay, now computer + // the number of iterations per microsecond. + var numMicros = measured * MpMicro; + SpinIterationsPerMicro = (int) (((double) nn) / numMicros); + break; + } + } + } + + public static long GetCounter() + { + long counter; + QueryPerformanceCounter(out counter); + return counter; + } + + public static long NanoTime + { + get + { + long time; + QueryPerformanceCounter(out time); + return (long)(time * MpNano); + } + } + + public static long MicroTime + { + get + { + long time; + QueryPerformanceCounter(out time); + return (long)(time * MpMicro); + } + } + + public static long MilliTime + { + get + { + long time; + QueryPerformanceCounter(out time); + return (long)(time * MpMilli); + } + } + + public static long TimeNano(Runnable r) + { + long timeA; + long timeB; + + QueryPerformanceCounter(out timeA); + r.Invoke(); + QueryPerformanceCounter(out timeB); + return (long)((timeB - timeA) * MpNano); + } + + public static long TimeMicro(Runnable r) + { + long timeA; + long timeB; + + QueryPerformanceCounter(out timeA); + r.Invoke(); + QueryPerformanceCounter(out timeB); + return (long)((timeB - timeA) * MpMicro); + } + + public static long TimeMillis(Runnable r) + { + long timeA; + long timeB; + + QueryPerformanceCounter(out timeA); + r.Invoke(); + QueryPerformanceCounter(out timeB); + return (long)((timeB - timeA) * MpMilli); + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/PerformanceTracker.cs b/NEsper.Core/NEsper.Core/compat/PerformanceTracker.cs new file mode 100755 index 000000000..b06dc5e0d --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/PerformanceTracker.cs @@ -0,0 +1,64 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; +using System.Text; + +namespace com.espertech.esper.compat +{ + public class PerformanceTracker : IDisposable + { + private string _label; + private readonly long _baseTime; + private readonly LinkedList _timeLine; + + /// + /// Initializes a new instance of the class. + /// + public PerformanceTracker(string label) + { + _label = label; + _baseTime = PerformanceObserver.MicroTime; + _timeLine = new LinkedList(); + } + + /// + /// Gets or sets the label. + /// + /// The label. + public string Label + { + get { return _label; } + set { _label = value; } + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.Append(string.Format("{0,-10} ", _label)); + foreach (var measurement in _timeLine) { + stringBuilder.AppendFormat("|{0,8}", measurement - _baseTime); + } + + Console.Out.WriteLine(stringBuilder.ToString()); + } + + /// + /// Adds the measurment. + /// + public void AddMeasurement() + { + _timeLine.AddLast(PerformanceObserver.MicroTime); + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/Prime.cs b/NEsper.Core/NEsper.Core/compat/Prime.cs new file mode 100755 index 000000000..56863f08f --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/Prime.cs @@ -0,0 +1,47 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.compat +{ + public class Prime + { + /// + /// Prime numbers for working with hash sets. + /// + public static readonly int[] HashPrimes = + { + 67, + 131, + 257, + 521, + 1031, + 2053, + 4099, + 8209, + 16411, + 32771, + 65537, + 131101, + 262147, + 524309, + 1048583, + 2097169, + 4194319, + 8388617, + 16777259, + 33554467, + 67108879, + 134217757, + 268435459, + 536870923, + 797003437, + 961748941, + 1461749677 + }; + } +} diff --git a/NEsper.Core/NEsper.Core/compat/Properties.cs b/NEsper.Core/NEsper.Core/compat/Properties.cs new file mode 100755 index 000000000..56d9cae34 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/Properties.cs @@ -0,0 +1,70 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; + +namespace com.espertech.esper.compat +{ + /// + /// Collection that maps a string to a string. + /// + + [Serializable] + public class Properties : Dictionary + { + public Properties() {} + + protected Properties(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Compares one properties set to another. + /// + /// The other. + /// + public bool Equals(Properties other) + { + if (Count != other.Count) + return false; + + return !(Keys.Any(k => !other.Keys.Contains(k) || !Equals(this[k], other[k]))); + } + + /// + /// Determines whether the specified is equal to the current . + /// + /// + /// true if the specified is equal to the current ; otherwise, false. + /// + /// The to compare with the current . 2 + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != typeof (Properties)) return false; + return Equals((Properties) obj); + } + + /// + /// Returns a hash code for this instance. + /// + /// + /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. + /// + public override int GetHashCode() + { + return Keys.Aggregate(0, (current, key) => current*397 + key.GetHashCode()); + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/ResourceManager.cs b/NEsper.Core/NEsper.Core/compat/ResourceManager.cs new file mode 100755 index 000000000..c67d0a31c --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/ResourceManager.cs @@ -0,0 +1,183 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; + +namespace com.espertech.esper.compat +{ + /// + /// Manages access to named resources + /// + + public sealed class ResourceManager + { + private static List m_searchPath; + + /// + /// Gets or sets the search path. + /// + /// The search path. + public static IEnumerable SearchPath + { + get { return m_searchPath; } + set { m_searchPath = new List(value); } + } + + /// + /// Adds to the search path + /// + /// + + public static void AddSearchPathElement(String searchPathElement) + { + if (!m_searchPath.Contains(searchPathElement)) + { + m_searchPath.Add(searchPathElement); + } + } + + /// + /// Resolves a resource and returns the file INFO. + /// + /// The name. + /// The search path. + /// + + public static FileInfo ResolveResourceFile(string name, string searchPath) + { + name = name.Replace('/', '\\').TrimStart('\\'); + + string filename = Path.Combine(searchPath, name.Replace('/', '\\')); + if (File.Exists(filename)) + { + return new FileInfo(filename); + } + else + { + return null; + } + } + + /// + /// Resolves a resource and returns the file INFO. + /// + /// The name. + + public static FileInfo ResolveResourceFile(string name) + { + foreach (String pathElement in SearchPath) + { + FileInfo fileInfo = ResolveResourceFile(name, pathElement); + if ( fileInfo != null ) + { + return fileInfo; + } + } + + if (File.Exists(name)) + { + return new FileInfo(name); + } + + return null; + } + + /// + /// Resolves a resource and the URL for the resource + /// + /// + /// + + public static Uri ResolveResourceURL(string name) + { + if (Uri.IsWellFormedUriString(name, UriKind.Absolute)) { + return new Uri(name, UriKind.Absolute); + } + + FileInfo fileInfo = ResolveResourceFile(name); + if (fileInfo != null) + { + UriBuilder builder = new UriBuilder(); + builder.Scheme = Uri.UriSchemeFile; + builder.Host = String.Empty; + builder.Path = fileInfo.FullName; + return builder.Uri; + } + + return null; + } + + /// + /// Attempts to retrieve the resource identified by the specified + /// name as a stream. If the stream can not be retrieved, this + /// method returns null. + /// + /// + /// + + public static Stream GetResourceAsStream(string name) + { + if (Uri.IsWellFormedUriString(name, UriKind.Absolute)) + { + var uri = new Uri(name, UriKind.Absolute); + return (new WebClient()).OpenRead(uri); + } + + // Currently using file-based search and lookup. This needs to be expanded + // to cover a broader search-lookup strategy that includes true web-based + // pathing and internal stream lookups like those in the manifest. + + FileInfo fileInfo = ResolveResourceFile(name); + if (fileInfo != null) + { + Stream stream = new FileStream(fileInfo.FullName, FileMode.Open, FileAccess.Read); + return stream; + } + + return null; + } + + static void AddDefaultSearchPath() + { + m_searchPath.Add(Environment.CurrentDirectory); + m_searchPath.Add(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)); + m_searchPath.Add(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)); + } + + /// + /// Initializes the class + /// + + static ResourceManager() + { + m_searchPath = new List(); + + var settings = CompatSettings.Default; + if ( settings != null ) { + if (settings.SearchPath != null) { + foreach( var path in settings.SearchPath ) { + var testPath = Path.GetFullPath(path); + if (Directory.Exists(testPath)) { + m_searchPath.Add(testPath); + } + } + } + + if (settings.UseDefaultSearchPath) { + AddDefaultSearchPath(); + } + } else { + AddDefaultSearchPath(); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/Runnable.cs b/NEsper.Core/NEsper.Core/compat/Runnable.cs new file mode 100755 index 000000000..1f3a645cd --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/Runnable.cs @@ -0,0 +1,24 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +namespace com.espertech.esper.compat +{ + /// + /// Represents a delegate that can be called. + /// + public delegate void Runnable(); + + /// + /// Represents an interface that can be called. + /// + public interface IRunnable + { + void Run(); + } +} diff --git a/NEsper.Core/NEsper.Core/compat/ScopedInstance.cs b/NEsper.Core/NEsper.Core/compat/ScopedInstance.cs new file mode 100755 index 000000000..c5e140448 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/ScopedInstance.cs @@ -0,0 +1,80 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; + +namespace com.espertech.esper.compat +{ + /// + /// Provides a generic item that can be scoped statically as a singleton; avoids the + /// need to define a threadstatic variable. Also provides a consistent model for + /// providing this service. + /// + /// + + public class ScopedInstance where T : class + { + [ThreadStatic] + private static T _instance; + + /// + /// Gets the current instance value. + /// + /// The current. + public static T Current + { + get { return _instance; } + } + + public static bool IsSet + { + get { return _instance != default(T); } + } + + /// + /// Sets the specified instance. + /// + /// The item. + /// + public static IDisposable Set(T item) + { + return new DisposableScope(item); + } + + /// + /// Disposable scope + /// + private class DisposableScope : IDisposable + { + private readonly T _previous; + + /// + /// Initializes a new instance of the class. + /// + /// The item. + internal DisposableScope( T item ) + { + _previous = _instance; + _instance = item; + } + + #region IDisposable Members + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + _instance = _previous; + } + + #endregion + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/ScopedThreadCulture.cs b/NEsper.Core/NEsper.Core/compat/ScopedThreadCulture.cs new file mode 100755 index 000000000..c17664ea6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/ScopedThreadCulture.cs @@ -0,0 +1,42 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Globalization; +using System.Threading; + +namespace com.espertech.esper.compat +{ + public class ScopedThreadCulture : IDisposable + { + private readonly CultureInfo previous; + + /// + /// Initializes a new instance of the class. + /// + /// The culture. + public ScopedThreadCulture(CultureInfo culture) + { + previous = Thread.CurrentThread.CurrentCulture; + Thread.CurrentThread.CurrentCulture = culture; + } + + #region IDisposable Members + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + Thread.CurrentThread.CurrentCulture = previous; + } + + #endregion + } +} diff --git a/NEsper.Core/NEsper.Core/compat/Sequencer.cs b/NEsper.Core/NEsper.Core/compat/Sequencer.cs new file mode 100755 index 000000000..eaebc95d9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/Sequencer.cs @@ -0,0 +1,125 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.compat +{ + public class Sequencer + { + private int _currentPos; + private int _currentGaps; + private int _startPos; + private readonly List _seqList; + + /// + /// Initializes a new instance of the class. + /// + public Sequencer() + { + _seqList = new List(); + _currentPos = 0; + _currentGaps = 0; + _startPos = 0; + } + + /// + /// Allocates this instance. + /// + /// + internal ISequence Allocate() + { + _PrivateSequence _seq; + + if (_currentGaps != 0) + { + int _endPos = _seqList.Count; + for (int ii = _startPos; ii < _endPos; ii++) + { + if (_seqList[ii] == null) + { + _seq = new _PrivateSequence(this, ii); + if (--_currentGaps == 0) + { + _startPos = 0; + } + else + { + _startPos = ii + 1; + } + + return _seq; + } + } + + throw new ArgumentException("Sequencer failed to render unique value, but reported gaps"); + } + + _seq = new _PrivateSequence(this, ++_currentPos); + _seqList.Add(_seq); + + return _seq; + } + + /// + /// Releases the specified sequence. + /// + /// The sequence. + internal void Release(int sequence) + { + _seqList[sequence] = null; + ++_currentGaps; + if (sequence < _startPos) + { + _startPos = sequence; + } + } + + internal class _PrivateSequence : ISequence + { + private readonly Sequencer _sequencer; + private readonly int _sequence; + + /// + /// Initializes a new instance of the class. + /// + /// The sequencer. + /// The sequence. + public _PrivateSequence(Sequencer sequencer, int sequence) + { + this._sequencer = sequencer; + this._sequence = sequence; + } + + /// + /// Gets the sequence. + /// + /// The sequence. + public int Sequence + { + get { return _sequence; } + } + + #region IDisposable Members + + public void Dispose() + { + _sequencer.Release(_sequence); + } + + #endregion + } + } + + public interface ISequence : IDisposable + { + int Sequence { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/SoftReference.cs b/NEsper.Core/NEsper.Core/compat/SoftReference.cs new file mode 100755 index 000000000..4e571b82e --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/SoftReference.cs @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.compat +{ + /// + /// Soft references are like weak references in that they allow an object to go out of scope + /// when it is not referenced. Any "hard" reference to an object causes the object to remain + /// out of the eyes of the GC. The soft reference splits the difference by marking an object + /// with a hard reference and using a counter. When the object is accessed, the reference + /// count increases and over time decays to zero. When it decays to zero, the reference + /// effectively becomes a weak reference and is available to the GC. + /// + public class SoftReference : WeakReference where T : class + { + private T _ref; + private int _refCount; + private int _refIncrementCount; + private int _refDecrementCount; + + /// + /// Initializes a new instance of the class. + /// + /// The target. + /// The increment count. + /// The decrement count. + public SoftReference(T target, int incrementCount = 20, int decrementCount = 1) + : base(target) + { + _ref = target; + _refCount = incrementCount; + _refIncrementCount = incrementCount; + _refDecrementCount = decrementCount; + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/StringBuilderExtensions.cs b/NEsper.Core/NEsper.Core/compat/StringBuilderExtensions.cs new file mode 100755 index 000000000..ab6cdbcd9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/StringBuilderExtensions.cs @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Text; + +namespace com.espertech.esper.compat +{ + public static class StringBuilderExtensions + { + public static int IndexOf(this StringBuilder stringBuilder, string value, int startIndex = 0) + { + int valueLength = value.Length; + int stringLength = stringBuilder.Length - valueLength; + for(int ii = startIndex ; ii < stringLength ; ii++) + { + bool isMatch = true; + + for(int jj = 0 ; jj < valueLength ; jj++) + { + if (value[jj] != stringBuilder[ii + jj]) + { + isMatch = false; + break; + } + } + + if (isMatch) + { + return ii; + } + } + + return -1; + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/StringExtensions.cs b/NEsper.Core/NEsper.Core/compat/StringExtensions.cs new file mode 100755 index 000000000..6314ddcf2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/StringExtensions.cs @@ -0,0 +1,95 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Text; +using System.Text.RegularExpressions; + +using Nito.KitchenSink.CRC; + +namespace com.espertech.esper.compat +{ + public static class StringExtensions + { + public static string Between(this string input, int startIndex, int endIndex) + { + return input.Substring(startIndex, endIndex - startIndex); + } + + public static string Capitalize(this string input) + { + if (input == null) + return null; + if (input.Length == 1) + return char.ToUpperInvariant(input[0]) + ""; + + return char.ToUpperInvariant(input[0]) + input.Substring(1); + } + + public static string[] SplitCsv(this string input) + { + return input.Split(','); + } + + public static bool Matches(this string input, string regex) + { + if (regex.Length > 0) + { + if (regex[0] != '^') + regex = '^' + regex; + if (regex[regex.Length - 1] != '$') + regex = regex + '$'; + } + + return Regex.IsMatch(input, regex); + } + + public static string[] RegexSplit(this string input, string pattern) + { + return Regex.Split(input, pattern); + } + + public static string RegexReplaceAll(this string input, string pattern, string replacement) + { + return Regex.Replace( + input, + pattern, + match => replacement); + } + + public static long GetCrc32(this string input, Encoding encoding = null) + { + if (encoding == null) + encoding = Encoding.UTF8; + + var algo = new CRC32(); + var hash = algo.ComputeHash(encoding.GetBytes(input)); + return BitConverter.ToUInt32(hash, 0); + } + + /// + /// Gets the UTF8 byte encoding for the input string. + /// + /// The input. + /// + public static byte[] GetUTF8Bytes(this string input) + { + return Encoding.UTF8.GetBytes(input); + } + + /// + /// Gets the unicode byte encoding for the input string. + /// + /// The input. + /// + public static byte[] GetUnicodeBytes(this string input) + { + return Encoding.Unicode.GetBytes(input); + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/ThreadWatch.cs b/NEsper.Core/NEsper.Core/compat/ThreadWatch.cs new file mode 100755 index 000000000..e275e2d46 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/ThreadWatch.cs @@ -0,0 +1,17 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.compat +{ + public class ThreadWatch + { + public static readonly ThreadWatch Instance = new ThreadWatch(); + + public bool EnableLog; + } +} diff --git a/NEsper.Core/NEsper.Core/compat/TimeUnit.cs b/NEsper.Core/NEsper.Core/compat/TimeUnit.cs new file mode 100755 index 000000000..f1a10eed9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/TimeUnit.cs @@ -0,0 +1,21 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.compat +{ + public enum TimeUnit + { + DAYS, + HOURS, + MINUTES, + SECONDS, + MILLISECONDS, + MICROSECONDS, + NANOSECONDS + } +} diff --git a/NEsper.Core/NEsper.Core/compat/TimeZoneHelper.cs b/NEsper.Core/NEsper.Core/compat/TimeZoneHelper.cs new file mode 100755 index 000000000..8e4cb2593 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/TimeZoneHelper.cs @@ -0,0 +1,70 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text.RegularExpressions; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.threading; + +namespace com.espertech.esper.compat +{ + public class TimeZoneHelper + { + private static readonly ILockable TimeZoneInfoLock = + LockManager.CreateLock(typeof (TimeZoneHelper)); + private static readonly IDictionary TimeZoneInfoDictionary = + new Dictionary(); + + /// + /// Returns the local timezone. + /// + public static TimeZoneInfo Local + { + get { return TimeZoneInfo.Local; } + } + + public static TimeZoneInfo GetTimeZoneInfo(string specOrId) + { + using (TimeZoneInfoLock.Acquire()) + { + var timeZoneInfo = TimeZoneInfoDictionary.Get(specOrId); + if (timeZoneInfo == null) + { + try + { + timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(specOrId); + } + catch (TimeZoneNotFoundException) + { + var regex = new Regex("^GMT([+-])(\\d{1,2}):(\\d{2})$"); + var match = regex.Match(specOrId); + if (match == Match.Empty) + { + throw; + } + + var multiplier = match.Groups[1].Value == "+" ? 1 : -1; + var offset = new TimeSpan( + multiplier*int.Parse(match.Groups[2].Value), // hours + multiplier*int.Parse(match.Groups[3].Value), + 0); + + timeZoneInfo = TimeZoneInfo.CreateCustomTimeZone(specOrId, offset, specOrId, specOrId); + } + + TimeZoneInfoDictionary[specOrId] = timeZoneInfo; + } + + return timeZoneInfo; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/TrackedDisposable.cs b/NEsper.Core/NEsper.Core/compat/TrackedDisposable.cs new file mode 100755 index 000000000..fc62b0074 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/TrackedDisposable.cs @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.compat +{ + public sealed class TrackedDisposable : IDisposable + { + private readonly Action _actionOnDispose; + + /// + /// Initializes a new instance of the class. + /// + /// The action on dispose. + public TrackedDisposable(Action actionOnDispose) + { + _actionOnDispose = actionOnDispose; + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + _actionOnDispose.Invoke(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/Transformer.cs b/NEsper.Core/NEsper.Core/compat/Transformer.cs new file mode 100755 index 000000000..2a1fd96bc --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/Transformer.cs @@ -0,0 +1,13 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +namespace com.espertech.esper.compat +{ + public delegate Target Transformer(Source source); +} diff --git a/NEsper.Core/NEsper.Core/compat/TypeCaster.cs b/NEsper.Core/NEsper.Core/compat/TypeCaster.cs new file mode 100755 index 000000000..21e4b342b --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/TypeCaster.cs @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; + +namespace com.espertech.esper.compat +{ + /// + /// Casts an opaque object into a different type of opaque object. + /// The TypeCaster encapsulates the behavior to make the transformation + /// occur but the input and output types are loosely defined. + /// + /// + /// + + public delegate Object TypeCaster(Object sourceObj); + + /// + /// Casts (not converts) an opaque object into a strongly typed result. + /// + /// + /// + /// + + public delegate T GenericTypeCaster(Object sourceObj); +} diff --git a/NEsper.Core/NEsper.Core/compat/TypeCasterFactory.cs b/NEsper.Core/NEsper.Core/compat/TypeCasterFactory.cs new file mode 100755 index 000000000..c221e5d08 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/TypeCasterFactory.cs @@ -0,0 +1,22 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; + +namespace com.espertech.esper.compat +{ + /// + /// TypeCasterFactory provides factory methods for the creation of TypeCaster + /// that transform between two known types. + /// + /// + /// + /// + public delegate TypeCaster TypeCasterFactory(Type sourceType, Type targetType); +} diff --git a/NEsper.Core/NEsper.Core/compat/TypeExtensions.cs b/NEsper.Core/NEsper.Core/compat/TypeExtensions.cs new file mode 100755 index 000000000..9902f584f --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/TypeExtensions.cs @@ -0,0 +1,425 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Numerics; +using System.Reflection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.util; +using com.espertech.esper.compat.magic; + +namespace com.espertech.esper.compat +{ + public static class TypeExtensions + { + /// + /// Finds the property. + /// + /// + public static PropertyInfo FindProperty(this Type t, string name) + { + var propertyInfo = t.GetProperty(name); + if (propertyInfo != null) + return propertyInfo; + + foreach( var property in t.GetProperties() ) { + if ( String.Equals(name, property.Name, StringComparison.CurrentCultureIgnoreCase) ) { + return property; + } + } + + return null; + } + + /// + /// Ases the singleton. + /// + /// + /// The value. + /// + public static T[] AsSingleton(this T value) + { + return new T[] {value}; + } + + public static ISet AsSet(this T value) + { + return new Singleton(value); + } + + public static bool? AsBoxedBoolean(this object value) + { + if (value == null) return null; + if (value is bool) return (bool)value; + return AsBoolean(value); + } + + /// + /// Returns the value as a boxed short. + /// + /// The value. + /// + public static short? AsBoxedShort(this object value) + { + if (value == null) return null; + if (value is short) return (short) value; + return AsShort(value); + } + + /// + /// Returns the value as a short. + /// + /// The value. + /// + public static short AsShort(this object value) + { + if (value is short) + return (short)value; + return Convert.ToInt16(value); + } + + /// + /// Returns the value as a boxed int. + /// + /// The value. + /// + public static int? AsBoxedInt(this object value) + { + if (value == null) return null; + if (value is int) return (int) value; + return AsInt(value); + } + + /// + /// Returns the value as an int. + /// + /// The value. + /// + public static int AsInt(this object value) + { + if (value is int) + return (int) value; + return Convert.ToInt32(value); + } + + /// + /// Returns the value as a boxed long. + /// + /// The value. + /// + public static long? AsBoxedLong(this object value) + { + if (value == null) return null; + if (value is long) return (long)value; + return AsLong(value); + } + + /// + /// Returns the value as a long. + /// + /// The value. + /// + public static long AsLong(this object value) + { + if (value is long) + return (long)value; + if (value is int) + return (int)value; + return Convert.ToInt64(value); + } + + /// + /// Returns the value as a float. + /// + /// The value. + /// + public static float AsFloat(this object value) + { + if (value is decimal) + return (float) ((decimal) value); + if (value is double) + return (float)((double)value); + if (value is float) + return (float)value; + if (value is long) + return (long)value; + if (value is int) + return (int)value; + if (value is BigInteger) + return (float) ((BigInteger)value); + + return Convert.ToSingle(value); + } + + /// + /// Returns the value as a double. + /// + /// The value. + /// + public static double AsDouble(this object value) + { + if (value is decimal) + return (double)((decimal)value); + if (value is double) + return (double) value; + if (value is float) + return (float) value; + if (value is long) + return (long) value; + if (value is int) + return (int) value; + if (value is BigInteger) + return (double) ((BigInteger) value); + + return Convert.ToDouble(value); + } + + + /// + /// Returns the value as a boxed double. + /// + /// The value. + /// + public static double? AsBoxedDouble(this object value) + { + if (value == null) return null; + return AsDouble(value); + } + + /// + /// Returns the value as a decimal. + /// + /// The value. + /// + public static decimal AsDecimal(this object value) + { + if (value is decimal) + return (decimal)value; + if (value is double) + return (decimal) ((double) value); + if (value is float) + return (decimal) ((float) value); + if (value is long) + return (long)value; + if (value is int) + return (int)value; + if (value is BigInteger) + return (decimal) ((BigInteger)value); + + return Convert.ToDecimal(value); + } + + public static BigInteger AsBigInteger(this object value) + { + if (value is BigInteger) + return (BigInteger) value; + if (value is decimal) + return new BigInteger((decimal) value); + if (value is double) + return new BigInteger((double) value); + if (value is float) + return new BigInteger((float) value); + if (value is long) + return new BigInteger((long) value); + if (value is int) + return new BigInteger((int) value); + if (value is uint) + return new BigInteger((uint) value); + if (value is ulong) + return new BigInteger((ulong) value); + if (value is byte) + return new BigInteger((byte) value); + + throw new ArgumentException("invalid value for BigInteger", "value"); + } + + public static bool AsBoolean(this object value) + { + if (value == null) + return false; + if (value is bool) + return (bool)value; + + throw new ArgumentException("invalid value for bool", "value"); + } + + public static DateTime AsDateTime(this object value) + { + if (value is DateTime) + return (DateTime) value; + if (value is int) + return DateTimeHelper.FromMillis((int) value); + if (value is long) + return DateTimeHelper.FromMillis((long) value); + + throw new ArgumentException("invalid value for datetime", "value"); + } + + public static DateTimeOffset AsDateTimeOffset(this object value) + { + return AsDateTimeOffset(value, TimeZoneInfo.Local); + } + + public static DateTimeOffset AsDateTimeOffset(this object value, TimeZoneInfo timeZone) + { + if (value is DateTimeOffset) + return ((DateTimeOffset) value).TranslateTo(timeZone); + if (value is DateTime) + return ((DateTime) value).TranslateTo(timeZone); + if (value is long) + return DateTimeOffsetHelper.TimeFromMillis((long) value, timeZone); + if (value is int) + return DateTimeOffsetHelper.TimeFromMillis((int) value, timeZone); + + throw new ArgumentException("invalid value for datetime", "value"); + } + + /// + /// Determines whether the specified value is long. + /// + /// The value. + /// + /// true if the specified value is long; otherwise, false. + /// + public static bool IsLong(this object value) + { + return value.IsIntegralNumber(typeof (long)); + } + + /// + /// Determines whether the specified value is int. + /// + /// The value. + /// if set to true [with upcast]. + /// + /// true if the specified value is int; otherwise, false. + /// + public static bool IsInt(this object value, bool withUpcast = true) + { + if (withUpcast) + { + return value.IsIntegralNumber(typeof (int)); + } + + return (value is int); + } + + /// + /// Determines whether [is date time] [the specified value]. + /// + /// The value. + /// + /// true if [is date time] [the specified value]; otherwise, false. + /// + public static bool IsDateTime(this object value) + { + var asType = value as Type; + if (asType == null) + return false; + + asType = asType.GetBoxedType(); + return (asType == typeof(DateTimeEx)) || + (asType == typeof(DateTimeOffset?)) || + (asType == typeof(DateTime?)) || + (asType == typeof(long?)); + } + + /// + /// Gets the base type tree. + /// + /// The type. + /// + public static IEnumerable GetBaseTypeTree(this Type type) + { + for (; type != null; type = type.BaseType) + { + yield return type; + } + } + + public static Type FindAttributeInTypeTree(this Type type, Type attributeType) + { + // look for a data contract in the hierarchy of types + foreach (var superType in type.GetBaseTypeTree()) + { + var attributes = superType.GetCustomAttributes(attributeType, false); + if (attributes.Length != 0) + { + return superType; + } + } + + foreach (var interfaceType in type.GetInterfaces()) + { + var attributes = interfaceType.GetCustomAttributes(attributeType, false); + if (attributes.Length != 0) + { + return interfaceType; + } + } + + return null; + } + + /// + /// Transparent cast for the lazy + /// + /// The o. + /// + public static IDictionary AsDataMap(this object o) + { + return o as IDictionary; + } + + public static IDictionary AsStringDictionary(this object value) + { + if (value == null) + return null; + if (value is IDictionary) + return (IDictionary)value; + if (value.GetType().IsGenericDictionary()) + return MagicMarker.GetStringDictionary(value); + + throw new ArgumentException("invalid value for string dictionary", "value"); + } + + public static Attribute[] UnwrapAttributes(this MemberInfo memberInfo, bool inherit = true) + { + return memberInfo.GetCustomAttributes(inherit).UnwrapIntoArray(); + } + + public static T[] UnwrapAttributes(this MemberInfo memberInfo, bool inherit = true) + where T : Attribute + { + return memberInfo.GetCustomAttributes(inherit).UnwrapIntoArray(); + } + + public static bool IsVarArgs(this MethodInfo methodInfo) + { + var parameters = methodInfo.GetParameters(); + if (parameters.Length == 0) + { + return false; + } + + return parameters[parameters.Length - 1].GetCustomAttributes(typeof (ParamArrayAttribute), false).Length >= 1; + } + + public static bool IsVarArgs(this ConstructorInfo constructorInfo) + { + var parameters = constructorInfo.GetParameters(); + if (parameters.Length == 0) + { + return false; + } + + return parameters[parameters.Length - 1].GetCustomAttributes(typeof(ParamArrayAttribute), false).Length > 1; + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/TypePair.cs b/NEsper.Core/NEsper.Core/compat/TypePair.cs new file mode 100755 index 000000000..e15c5fa68 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/TypePair.cs @@ -0,0 +1,103 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; + +namespace com.espertech.esper.compat +{ + public class TypePair + { + private readonly Type typeA; + private readonly Type typeB; + + /// + /// Gets the type A. + /// + /// The type A. + public Type TypeA + { + get { return typeA; } + } + + /// + /// Gets the type B. + /// + /// The type B. + public Type TypeB + { + get { return typeB; } + } + + /// + /// Initializes a new instance of the class. + /// + /// The type A. + /// The type B. + public TypePair(Type typeA, Type typeB) + { + this.typeA = typeA; + this.typeB = typeB; + } + + /// + /// Performs the underlying equality comparison. + /// + /// The obj. + /// + public bool Equals(TypePair obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + return Equals(obj.typeA, typeA) && Equals(obj.typeB, typeB); + } + + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// + /// true if the specified is equal to the current ; otherwise, false. + /// + /// + /// The parameter is null. + /// + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != typeof(TypePair)) return false; + return Equals((TypePair)obj); + } + + /// + /// Serves as a hash function for a particular type. + /// + /// + /// A hash code for the current . + /// + public override int GetHashCode() + { + unchecked + { + return ((typeA != null ? typeA.GetHashCode() : 0) * 397) ^ (typeB != null ? typeB.GetHashCode() : 0); + } + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override string ToString() + { + return string.Format("TypeA: {0}, TypeB: {1}", typeA, typeB); + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/UnsupportedOperationException.cs b/NEsper.Core/NEsper.Core/compat/UnsupportedOperationException.cs new file mode 100755 index 000000000..44311d74b --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/UnsupportedOperationException.cs @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; + +namespace com.espertech.esper.compat +{ + [Serializable] + public class UnsupportedOperationException : NotSupportedException + { + /// + /// Initializes a new instance of the class. + /// + public UnsupportedOperationException() : base() { } + /// + /// Initializes a new instance of the class. + /// + /// The message. + public UnsupportedOperationException( string message ) : base( message ) { } } +} diff --git a/NEsper.Core/NEsper.Core/compat/VoidDisposable.cs b/NEsper.Core/NEsper.Core/compat/VoidDisposable.cs new file mode 100755 index 000000000..971de9701 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/VoidDisposable.cs @@ -0,0 +1,17 @@ +using System; + +namespace com.espertech.esper.compat +{ + /// + /// Does nothing + /// + public class VoidDisposable : IDisposable + { + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/WeakDictionary.cs b/NEsper.Core/NEsper.Core/compat/WeakDictionary.cs new file mode 100755 index 000000000..4ddf6882d --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/WeakDictionary.cs @@ -0,0 +1,540 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.compat +{ + /// + /// A generic dictionary, which allows its keys + /// to be garbage collected if there are no other references + /// to them than from the dictionary itself. + /// + /// + /// + /// If the key of a particular entry in the dictionary has been + /// collected, then both the key and value become effectively + /// unreachable. However, left-over WeakReference objects for the key + /// will physically remain in the dictionary until RemoveCollectedEntries + /// is called. This will lead to a discrepancy between the Count property + /// and the number of iterations required to visit all of the elements of + /// the dictionary using its enumerator or those of the Keys and Values + /// collections. Similarly, CopyTo will copy fewer than Count elements + /// in this situation. + /// + + public sealed class WeakDictionary : IDictionary + where TKey : class + where TValue : class + { + private readonly Dictionary dictionary; + private readonly WeakKeyComparer comparer; + + /// + /// Initializes a new instance of the class. + /// + public WeakDictionary() + : this(16, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The capacity. + public WeakDictionary(int capacity) + : this(capacity, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The comparer. + public WeakDictionary(IEqualityComparer comparer) + : this(16, comparer) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The capacity. + /// The comparer. + public WeakDictionary(int capacity, IEqualityComparer comparer) + { + this.comparer = new WeakKeyComparer(comparer); + this.dictionary = new Dictionary(capacity, this.comparer); + } + + /// + /// Gets the number of elements contained in the . + /// + /// + /// WARNING: The count returned here may include entries for which + /// either the key or value objects have already been garbage + /// collected. Call RemoveCollectedEntries to weed out collected + /// entries and Update the count accordingly. + /// + /// + /// The number of elements contained in the . + + public int Count + { + get { return this.dictionary.Count; } + } + + /// + /// Adds an element with the provided key and value to the . + /// + /// The object to use as the key of the element to add. + /// The object to use as the value of the element to add. + /// The is read-only. + /// An element with the same key already exists in the . + /// key is null. + public void Add(TKey key, TValue value) + { + if (key == null) throw new ArgumentNullException("key"); + WeakReference weakKey = new WeakKeyReference(key, this.comparer); + this.dictionary.Add(weakKey, value); + } + + /// + /// Determines whether the contains an element with the specified key. + /// + /// The key to locate in the . + /// + /// true if the contains an element with the key; otherwise, false. + /// + /// key is null. + public bool ContainsKey(TKey key) + { + return this.dictionary.ContainsKey( key ) ; + } + + /// + /// Removes the element with the specified key from the . + /// + /// The key of the element to remove. + /// + /// true if the element is successfully removed; otherwise, false. This method also returns false if key was not found in the original . + /// + /// The is read-only. + /// key is null. + public bool Remove(TKey key) + { + return this.dictionary.Remove(key); + } + + /// + /// Tries to get the value. + /// + /// The key. + /// The value. + /// + public bool TryGetValue(TKey key, out TValue value) + { + WeakReference tempKey = new WeakKeyReference(key, this.comparer); + + TValue rvalue ; + if ( this.dictionary.TryGetValue( tempKey, out rvalue ) ) + { + WeakReference weakKey = (WeakReference) tempKey ; + if ( weakKey.IsAlive ) + { + value = rvalue ; + return true ; + } + + this.dictionary.Remove( key ) ; + } + + value = default(TValue); + return false ; + } + + /// + /// Sets the value. + /// + /// The key. + /// The value. + private void SetValue(TKey key, TValue value) + { + WeakReference weakKey = new WeakKeyReference(key, this.comparer); + this.dictionary[weakKey] = value; + } + + /// + /// Removes all items from the . + /// + /// The is read-only. + public void Clear() + { + this.dictionary.Clear(); + } + + /// + /// Returns an enumerator that iterates through the collection. + /// + /// + /// A that can be used to iterate through the collection. + /// + public IEnumerator> GetEnumerator() + { + foreach (KeyValuePair kvp in this.dictionary) + { + WeakReference weakKey = (WeakReference)(kvp.Key); + TKey key = weakKey.Target; + TValue value = kvp.Value; + if (weakKey.IsAlive) + { + yield return new KeyValuePair(key, value); + } + } + } + + + /// + /// Removes the left-over weak references for entries in the dictionary + /// whose key or value has already been reclaimed by the garbage + /// collector. This will reduce the dictionary's Count by the number + /// of dead key-value pairs that were eliminated. + /// + public void RemoveCollectedEntries() + { + List toRemove = null; + foreach (KeyValuePair pair in this.dictionary) + { + WeakReference weakKey = (WeakReference)(pair.Key); + + if (!weakKey.IsAlive) + { + if (toRemove == null) + { + toRemove = new List(); + } + toRemove.Add(weakKey); + } + } + + if (toRemove != null) + { + foreach (object key in toRemove) + { + this.dictionary.Remove(key); + } + } + } + + /// + /// Gets an enumerator that enumerates the keys. + /// + /// The keys enum. + + public IEnumerator KeysEnum { + get + { + foreach( WeakReference weakKey in this.dictionary.Keys ) + { + if ( weakKey.IsAlive ) + { + yield return weakKey.Target; + } + } + } + } + + + #region IDictionary Members + + /// + /// Gets an containing the keys of the . + /// + /// + /// An containing the keys of the object that : . + public ICollection Keys + { + get + { + List keyList = new List() ; + IEnumerator keyEnum = this.KeysEnum ; + while( keyEnum.MoveNext() ) + { + keyList.Add( keyEnum.Current ) ; + } + + return keyList ; + } + } + + /// + /// Gets an containing the values in the . + /// + /// + /// An containing the values in the object that : . + public ICollection Values + { + get { throw new Exception("The method or operation is not implemented."); } + } + + /// + /// Gets or sets the item with the specified key. + /// + /// + public TValue this[TKey key] + { + get + { + Object tempKey = key ; + TValue rvalue ; + if ( this.dictionary.TryGetValue( tempKey, out rvalue ) ) + { + WeakReference weakKey = (WeakReference) tempKey ; + if ( weakKey.IsAlive ) + { + return rvalue ; + } + + this.dictionary.Remove( key ) ; + } + + throw new KeyNotFoundException( "Value '" + key + "' not found" ) ; + } + set + { + if (key == null) throw new ArgumentNullException("key"); + WeakReference weakKey = new WeakKeyReference(key, this.comparer); + this.dictionary[weakKey] = value; + } + } + + #endregion + + #region ICollection> Members + + /// + /// Adds an item to the . + /// + /// The object to add to the . + /// The is read-only. + + public void Add(KeyValuePair item) + { + Add(item.Key, item.Value); + } + + /// + /// Determines whether the contains a specific value. + /// + /// The object to locate in the . + /// + /// true if item is found in the ; otherwise, false. + /// + + public bool Contains(KeyValuePair item) + { + Object tempKey = item.Key ; + + TValue value ; + if ( this.dictionary.TryGetValue( tempKey, out value ) ) + { + WeakReference weakKey = (WeakReference) tempKey ; + if ( weakKey.IsAlive ) + { + return Equals( value, item.Value ) ; + } + + this.dictionary.Remove( item.Key ) ; + } + + return false; + } + + /// + /// Copies the elements of the to an , Starting at a particular index. + /// + /// The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing. + /// The zero-based index in array at which copying begins. + /// arrayIndex is less than 0. + /// array is null. + /// array is multidimensional.-or-arrayIndex is equal to or greater than the length of array.-or-The number of elements in the source is greater than the available space from arrayIndex to the end of the destination array.-or-Type T cannot be cast automatically to the type of the destination array. + + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + throw new Exception("The method or operation is not implemented."); + } + + /// + /// Gets a value indicating whether the is read-only. + /// + /// + /// true if the is read-only; otherwise, false. + + public bool IsReadOnly + { + get { return false; } + } + + /// + /// Removes the first occurrence of a specific object from the . + /// + /// The object to remove from the . + /// + /// true if item was successfully removed from the ; otherwise, false. This method also returns false if item is not found in the original . + /// + /// The is read-only. + + public bool Remove(KeyValuePair item) + { + return Remove(item.Key); + } + + #endregion + + #region IEnumerable Members + + /// + /// Returns an enumerator that iterates through a collection. + /// + /// + /// An object that can be used to iterate through the collection. + /// + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return this.GetEnumerator(); + } + + #endregion + + #region IDictionary Members + + /// + /// Fetches the value associated with the specified key. + /// If no value can be found, then the defaultValue is + /// returned. + /// + /// + /// + /// + public TValue Get(TKey key, TValue defaultValue) + { + TValue returnValue = defaultValue; + if (key != null) + { + if (!TryGetValue(key, out returnValue)) + { + returnValue = defaultValue; + } + } + return returnValue; + } + + /// + /// Fetches the value associated with the specified key. + /// If no value can be found, then default(V) is returned. + /// + /// + /// + public TValue Get(TKey key) + { + return Get(key, default(TValue)); + } + + /// + /// Sets the given key in the dictionary. If the key + /// already exists, then it is remapped to thenew value. + /// + /// + /// + public void Put(TKey key, TValue value) + { + this[key] = value; + } + + /// + /// Sets the given key in the dictionary. If the key + /// already exists, then it is remapped to the new value. + /// If a value was previously mapped it is returned. + /// + + public TValue Push(TKey key, TValue value) + { + TValue temp; + TryGetValue(key, out temp); + this[key] = value; + return temp; + } + + /// + /// Puts all values from the source dictionary into + /// this dictionary. + /// + /// + public void PutAll(IDictionary source) + { + foreach( KeyValuePair entry in source ) + { + this[entry.Key] = entry.Value; + } + } + + /// + /// Returns the first value in the enumeration of values + /// + /// + /// + public TValue FirstValue + { + get + { + IEnumerator> enumObj = GetEnumerator(); + return enumObj.MoveNext() + ? enumObj.Current.Value + : default(TValue); + } + } + + /// + /// Removes the item from the dictionary that is associated with + /// the specified key. + /// + /// Search key into the dictionary + /// The value removed from the dictionary (if found). + /// + public bool Remove(TKey key, out TValue value) + { + if (!TryGetValue(key, out value)) + { + return false; + } + + return Remove(key); + } + + /// + /// Removes the item from the dictionary that is associated with + /// the specified key. The item if found is returned; if not, + /// default(V) is returned. + /// + /// + /// + public TValue RemoveAndReturn(TKey key) + { + TValue tempItem; + + return Remove(key, out tempItem) + ? tempItem + : default(TValue); + } + + #endregion + } +} diff --git a/NEsper.Core/NEsper.Core/compat/WeakKeyComparer.cs b/NEsper.Core/NEsper.Core/compat/WeakKeyComparer.cs new file mode 100755 index 000000000..b5b2ca810 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/WeakKeyComparer.cs @@ -0,0 +1,98 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System.Collections.Generic; + +namespace com.espertech.esper.compat +{ + // Compares objects of the given type or WeakKeyReferences to them + // for equality based on the given comparer. Note that we can only + // implement IEqualityComparer for T = object as there is no + // other common base between T and WeakKeyReference. We need a + // single comparer to handle both types because we don't want to + // allocate a new weak reference for every lookup. + internal sealed class WeakKeyComparer : IEqualityComparer + where T : class + { + private IEqualityComparer comparer; + + internal WeakKeyComparer( IEqualityComparer comparer ) + { + if ( comparer == null ) + comparer = EqualityComparer.Default; + + this.comparer = comparer; + } + + public int GetHashCode( object obj ) + { + WeakKeyReference weakKey = obj as WeakKeyReference; + if ( weakKey != null ) return weakKey.HashCode; + return this.comparer.GetHashCode( (T) obj ); + } + + /// + /// Determines whether the specified is equal to the current . + /// + /// The first object of type T to compare. + /// The second object of type T to compare. + /// + /// true if the specified is equal to the current ; otherwise, false. + /// + /// + /// Note: There are actually 9 cases to handle here. + /// Let Wa = Alive Weak Reference + /// Let Wd = Dead Weak Reference + /// Let S = Strong Reference + /// x | y | Equals(x,y) + /// ------------------------------------------------- + /// Wa | Wa | comparer.Equals(x.Target, y.Target) + /// Wa | Wd | false + /// Wa | S | comparer.Equals(x.Target, y) + /// Wd | Wa | false + /// Wd | Wd | x == y + /// Wd | S | false + /// S | Wa | comparer.Equals(x, y.Target) + /// S | Wd | false + /// S | S | comparer.Equals(x, y) + /// ------------------------------------------------- + /// + public new bool Equals( object x, object y ) + { + bool xIsDead, yIsDead; + T first = GetTarget( x, out xIsDead ); + T second = GetTarget( y, out yIsDead ); + + if ( xIsDead ) + return yIsDead ? x == y : false; + + if ( yIsDead ) + return false; + + return this.comparer.Equals( first, second ); + } + + private static T GetTarget( object obj, out bool isDead ) + { + WeakKeyReference wref = obj as WeakKeyReference; + T target; + if ( wref != null ) + { + target = wref.Target; + isDead = !wref.IsAlive; + } + else + { + target = (T) obj; + isDead = false; + } + return target; + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/WeakKeyReference.cs b/NEsper.Core/NEsper.Core/compat/WeakKeyReference.cs new file mode 100755 index 000000000..aad452e5f --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/WeakKeyReference.cs @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +namespace com.espertech.esper.compat +{ + /// + /// Provides a weak reference to an object of the given type to be used in + /// a WeakDictionary along with the given comparer. + /// + internal sealed class WeakKeyReference : WeakReference where T : class + { + public readonly int HashCode; + + public WeakKeyReference( T key, WeakKeyComparer comparer ) + : base( key ) + { + // retain the object's hash code immediately so that even + // if the target is GC'ed we will be able to find and + // remove the dead weak reference. + this.HashCode = comparer.GetHashCode( key ); + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/WeakNullReference.cs b/NEsper.Core/NEsper.Core/compat/WeakNullReference.cs new file mode 100755 index 000000000..1513c34f2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/WeakNullReference.cs @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +namespace com.espertech.esper.compat +{ + // Provides a weak reference to a null target object, which, unlike + // other weak references, is always considered to be alive. This + // facilitates handling null dictionary values, which are perfectly + // legal. + internal class WeakNullReference : WeakReference where T : class + { + public static readonly WeakNullReference Singleton = new WeakNullReference(); + + private WeakNullReference() : base( null ) { } + + public override bool IsAlive + { + get { return true; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/WeakReference.cs b/NEsper.Core/NEsper.Core/compat/WeakReference.cs new file mode 100755 index 000000000..b32f1b780 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/WeakReference.cs @@ -0,0 +1,66 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.compat +{ + public class WeakReference : WeakReference where T : class + { + private readonly int _hashCode; + + /// + /// Initializes a new instance of the class. + /// + /// The target. + public WeakReference( T target ) + : base( target, false ) + { + _hashCode = target.GetHashCode(); + } + + /// + /// Gets a value indicating whether this instance is dead. + /// + /// true if this instance is dead; otherwise, false. + public bool IsDead + { + get { return !IsAlive; } + } + + /// + /// Gets the object (the target) referenced by the current object. + /// + /// + /// null if the object referenced by the current object has been garbage collected; otherwise, a reference to the object referenced by the current object. + /// The reference to the target object is invalid. This can occur if the current object has been finalized. + public new T Target + { + get { return (T) base.Target; } + } + + /// + /// Gets the object (the target) referenced by the current object. + /// + public T Get() + { + return (T) base.Target; + } + + /// + /// Serves as a hash function for a particular type. is suitable for use in hashing algorithms and data structures like a hash table. + /// + /// + /// A hash code for the current . + /// + public override int GetHashCode() + { + return _hashCode; + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/XMLConstants.cs b/NEsper.Core/NEsper.Core/compat/XMLConstants.cs new file mode 100755 index 000000000..29029d2f5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/XMLConstants.cs @@ -0,0 +1,142 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +namespace com.espertech.esper.compat +{ + /// + /// Contains constants for XML processing. + /// + + public class XMLConstants + { + /// + /// Namespace URI to use to represent that there is no Namespace. + /// + /// Defined by the Namespace specification to be "". + /// + /// Namespaces in XML, 5.2 Namespace Defaulting + /// + public const string NULL_NS_URI = ""; + + /// + /// Prefix to use to represent the default XML Namespace. + /// + /// Defined by the XML specification to be "". + /// + /// Namespaces in XML, 3. Qualified Names + /// + public const string DEFAULT_NS_PREFIX = ""; + + /// + /// The official XML Namespace name URI. + /// + /// Defined by the XML specification to be + /// http://www.w3.org/XML/1998/namespace. + /// + /// Namespaces in XML, 3. Qualified Names + /// + public const string XML_NS_URI = + "http://www.w3.org/XML/1998/namespace"; + + /// + /// The official XML Namespace prefix. + /// + /// Defined by the XML specification to be xml. + /// + /// Namespaces in XML, 3. Qualified Names + /// + public const string XML_NS_PREFIX = "xml"; + + /// + /// The official XML attribute used for specifying XML Namespace declarations, + /// , Namespace name URI. + /// + /// Defined by the XML specification to be + /// http://www.w3.org/2000/xmlns/. + /// Namespaces in XML, 3. Qualified Names + /// Namespaces in XML Errata + /// + public const string XMLNS_ATTRIBUTE_NS_URI = + "http://www.w3.org/2000/xmlns/"; + + /// + /// The official XML attribute used for specifying XML Namespace declarations. + /// + /// It is NOT valid to use as a prefix. Defined by the + /// XML specification to be xmlns. + /// + /// Namespaces in XML, 3. Qualified Names + /// + public const string XMLNS_ATTRIBUTE = "xmlns"; + + /// + /// W3C XML Schema Namespace URI. + /// + /// Defined to be http://www.w3.org/2001/XMLSchema. + /// + /// XML Schema Part 1: Structures, 2.6 Schema-Related Markup in Documents Being Validated + /// + public const string W3C_XML_SCHEMA_NS_URI = "http://www.w3.org/2001/XMLSchema"; + + /// + /// W3C XML Schema Instance Namespace URI. + /// + /// Defined to be http://www.w3.org/2001/XMLSchema-instance. + /// + /// XML Schema Part 1: Structures, 2.6 Schema-Related Markup in Documents Being Validated + /// + public const string W3C_XML_SCHEMA_INSTANCE_NS_URI = + "http://www.w3.org/2001/XMLSchema-instance"; + + /// + /// W3C XPath Datatype Namespace URI. + /// + /// Defined to be "http://www.w3.org/2003/11/xpath-datatypes". + /// + /// XQuery 1.0 and XPath 2.0 Data Model + /// + public const string W3C_XPATH_DATATYPE_NS_URI = "http://www.w3.org/2003/11/xpath-datatypes"; + + /// + /// XML Document Type Declaration Namespace URI as an arbitrary value. + /// + /// Since not formally defined by any existing standard, arbitrarily define to be + /// http://www.w3.org/TR/REC-xml. + /// + /// + public const string XML_DTD_NS_URI = "http://www.w3.org/TR/REC-xml"; + + /// + /// RELAX NG Namespace URI. + /// + /// Defined to be http://relaxng.org/ns/structure/1.0. + /// + /// RELAX NG Specification + /// + public const string RELAXNG_NS_URI = "http://relaxng.org/ns/structure/1.0"; + + /// + /// Feature for secure processing. + /// + /// + /// true instructs the + /// implementation to process XML securely. This may set limits on XML constructs to + /// avoid conditions such as denial of service attacks. + /// + /// + /// false + /// instructs the implementation to process XML acording the letter of the XML + /// specifications ingoring security issues such as limits on XML constructs to avoid + /// conditions such as denial of service attacks. + /// + /// + /// + public const string FEATURE_SECURE_PROCESSING = "http://javax.xml.XMLConstants/feature/secure-processing"; + } +} diff --git a/NEsper.Core/NEsper.Core/compat/attributes/EsperVersionAttribute.cs b/NEsper.Core/NEsper.Core/compat/attributes/EsperVersionAttribute.cs new file mode 100755 index 000000000..333803bb6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/attributes/EsperVersionAttribute.cs @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.compat.attributes +{ + public class EsperVersionAttribute : Attribute + { + public string Version { get; set; } + + public EsperVersionAttribute() + { + } + + public EsperVersionAttribute(string version) + { + Version = version; + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/attributes/RenderWithToStringAttribute.cs b/NEsper.Core/NEsper.Core/compat/attributes/RenderWithToStringAttribute.cs new file mode 100755 index 000000000..9d82e0f98 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/attributes/RenderWithToStringAttribute.cs @@ -0,0 +1,16 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.compat.attributes +{ + public class RenderWithToStringAttribute : Attribute + { + } +} diff --git a/NEsper.Core/NEsper.Core/compat/collections/ArrayDeque.cs b/NEsper.Core/NEsper.Core/compat/collections/ArrayDeque.cs new file mode 100755 index 000000000..1239ebf56 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/collections/ArrayDeque.cs @@ -0,0 +1,446 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections; +using System.Collections.Generic; + +namespace com.espertech.esper.compat.collections +{ + [Serializable] + public class ArrayDeque : Deque + { + private const int DEFAULT_INITIAL_CAPACITY = 256; + + private int _head; + private int _tail; + private T[] _array; + + public ArrayDeque() : this(DEFAULT_INITIAL_CAPACITY) + { + } + + public ArrayDeque(int capacity) + { + _head = 0; + _tail = 0; + _array = new T[capacity]; + } + + public ArrayDeque(ICollection source) + { + var ncount = source.Count; + var lcount = (int) Math.Log(ncount, 2); + var mcount = 1 << lcount; + if (mcount <= ncount) + mcount <<= 1; + + _array = new T[mcount]; + source.CopyTo(_array, 0); + _head = 0; + _tail = ncount; + } + + public T[] Array + { + get + { + var array = new T[Count]; + CopyTo(array, 0); + return array; + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public IEnumerator GetEnumerator() + { + if (_tail == _head) + { + } + else if (_tail > _head) + { + for (int ii = _head; ii < _tail; ii++) + yield return _array[ii]; + } + else + { + for (int ii = _head; ii < _array.Length; ii++) + yield return _array[ii]; + for (int ii = 0; ii < _tail; ii++) + yield return _array[ii]; + } + } + + public void Visit(Action action) + { + if (_tail == _head) + { + } + else if (_tail > _head) + { + for (int ii = _head; ii < _tail; ii++) + action.Invoke(_array[ii]); + } + else + { + for (int ii = _head; ii < _array.Length; ii++) + action.Invoke(_array[ii]); + for (int ii = 0; ii < _tail; ii++) + action.Invoke(_array[ii]); + } + } + + private void DoubleCapacity() + { + int newLength = _array.Length << 1; + if (newLength < 0) + throw new IllegalStateException("ArrayDeque overflow"); + + var narray = new T[newLength]; + + if (_tail > _head) + { + System.Array.Copy(_array, _head, narray, 0, _tail - _head); + } + else + { + var nl = _head; + var nr = _array.Length - _head; + + System.Array.Copy(_array, _head, narray, 0, nr); + System.Array.Copy(_array, 0, narray, nr, nl); + } + + _head = 0; + _tail = _array.Length; + _array = narray; + } + + public void AddFirst(T item) + { + if (--_head == -1) + _head = _array.Length - 1; + if (_head == _tail) + DoubleCapacity(); + _array[_head] = item; + } + + public void AddLast(T item) + { + _array[_tail] = item; + if (++_tail == _array.Length) + _tail = 0; + if (_head == _tail) + DoubleCapacity(); + } + + public void Add(T item) + { + AddLast(item); + } + + public T RemoveFirst() + { + if (_head == _tail) + throw new NoSuchElementException(); + // preserve the value at the head + var result = _array[_head]; + // clear the value in the array + _array[_head] = default(T); + // increment the head + if (++_head == _array.Length) + _head = 0; + + return result; + } + + public T RemoveLast() + { + if (_tail == _head) + throw new NoSuchElementException(); + if (--_tail < 0) + _tail = _array.Length - 1; + var result = _array[_tail]; + _array[_tail] = default(T); + return result; + } + + public TM RemoveInternal(int index, TM returnValue) + { + if (_tail > _head) + { + int tindex = _head + index; + if (tindex >= _tail) + throw new ArgumentOutOfRangeException(); + for (int ii = tindex + 1 ; ii < _tail ; ii++) + _array[ii - 1] = _array[ii]; + _array[--_tail] = default(T); + } + else if (index > (_tail + _array.Length - _head)) + { + throw new ArgumentOutOfRangeException(); + } + else + { + int tindex = (_head + index)%_array.Length; + if (tindex > _head) + { + for (int ii = tindex + 1 ; ii < _array.Length ; ii++) + _array[ii - 1] = _array[ii]; + _array[_array.Length - 1] = _array[0]; + for (int ii = 1 ; ii < _tail ; ii++) + _array[ii - 1] = _array[ii]; + _array[--_tail] = default(T); + } + else + { + for (int ii = 1 ; ii < _tail ; ii++) + _array[ii - 1] = _array[ii]; + _array[--_tail] = default(T); + } + + if (_tail == -1) + _tail = _array.Length - 1; + } + + return returnValue; + } + + public void RemoveAt(int index) + { + RemoveInternal(index, 0); + } + + public void RemoveWhere(Func predicate, Action onRemoveItem = null) + { + T value; + + if (_tail > _head) + { + for (int ii = _head; ii < _tail; ii++) + if (predicate.Invoke(value = _array[ii])) + if (RemoveInternal(ii--, true) && (onRemoveItem != null)) + onRemoveItem(value); + } + else + { + for (int ii = _head; ii < _array.Length; ii++) + if (predicate.Invoke(value = _array[ii])) + if (RemoveInternal(ii--, true) && (onRemoveItem != null)) + onRemoveItem(value); + for (int ii = 0; ii < _tail; ii++) + if (predicate.Invoke(value = _array[ii])) + if (RemoveInternal(ii--, true) && (onRemoveItem != null)) + onRemoveItem(value); + } + } + + + public bool Remove(T item) + { + if (_tail > _head) + { + for (int ii = _head; ii < _tail; ii++) + if (Equals(_array[ii], item)) + return RemoveInternal(ii, true); + } + else + { + for (int ii = _head; ii < _array.Length; ii++) + if (Equals(_array[ii], item)) + return RemoveInternal(ii, true); + for (int ii = 0; ii < _tail; ii++) + if (Equals(_array[ii], item)) + return RemoveInternal(ii, true); + } + + return false; + } + + public void Clear() + { + _head = 0; + _tail = 0; + _array.Fill(default(T)); + } + + public bool Contains(T item) + { + if (_tail > _head) + { + for (int ii = _head; ii < _tail; ii++) + if (Equals(_array[ii], item)) + return true; + } + else + { + for (int ii = _head; ii < _array.Length; ii++) + if (Equals(_array[ii], item)) + return true; + for (int ii = 0; ii < _tail; ii++) + if (Equals(_array[ii], item)) + return true; + } + + return false; + } + + public void CopyTo(T[] array, int arrayIndex) + { + if (_tail == _head) + { + + } + else if (_tail > _head) + { + System.Array.Copy(_array, _head, array, 0, _tail - _head); + } + else + { + var nl = _head; + var nr = _array.Length - _head; + + System.Array.Copy(_array, _head, array, 0, nr); + System.Array.Copy(_array, 0, array, nr, nl); + } + } + + public T this[int index] + { + get + { + if (index < 0) + throw new ArgumentOutOfRangeException(); + if (_tail > _head) + return _array[_head + index]; + if (index >= _tail + _array.Length - _head) + throw new ArgumentOutOfRangeException(); + + var offset = _head + index; + if (offset < _array.Length) + return _array[offset]; + + return _array[offset%_array.Length]; + } + + set + { + if (index < 0) + throw new ArgumentOutOfRangeException(); + if (_tail > _head) + { + _array[_head + index] = value; + return; + } + + if (index >= _tail + _array.Length - _head) + throw new ArgumentOutOfRangeException(); + + var offset = _head + index; + if (offset < _array.Length) + { + _array[offset] = value; + } + + _array[offset%_array.Length] = value; + } + } + + /// + /// Retrieves and removes the head of the queue represented by + /// this deque or returns default(T) if deque is empty. + /// + /// + public T Poll() + { + if (_head == _tail) + return default(T); + return RemoveFirst(); + } + + public T PopFront() + { + return RemoveFirst(); + } + + public T PopBack() + { + return RemoveLast(); + } + + /// + /// Retrieves, but does not remove, the head of the queue represented by this deque, + /// or returns default(T) if this deque is empty. + /// + /// + public T Peek() + { + if (_head == _tail) + { + return default(T); + } + + return First; + } + + public T First + { + get + { + if (_head == _tail) + { + throw new ArgumentOutOfRangeException(); + } + + int indx = _head; + if (indx == _array.Length) + indx = 0; + + return _array[indx]; + } + } + + public T Last + { + get + { + if (_head == _tail) + { + throw new ArgumentOutOfRangeException(); + } + + int indx = _tail - 1; + if (indx == -1) + indx = _array.Length - 1; + + return _array[indx]; + } + } + + public int Count + { + get + { + if (_tail == _head) + return 0; + if (_tail > _head) + return _tail - _head; + return _tail + _array.Length - _head; + } + } + + public bool IsReadOnly + { + get { return false; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/collections/ArrayDictionary.cs b/NEsper.Core/NEsper.Core/compat/collections/ArrayDictionary.cs new file mode 100755 index 000000000..fbb3edae1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/collections/ArrayDictionary.cs @@ -0,0 +1,132 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace com.espertech.esper.compat.collections +{ + public class ArrayDictionary : IDictionary + { + private KeyValuePair[] _keyValuePairs; + + public ArrayDictionary(params KeyValuePair[] values) + { + _keyValuePairs = values; + } + + public TV this[TK key] + { + get + { + for (int ii = 0; ii < _keyValuePairs.Length; ii++) + { + if (Equals(key, _keyValuePairs[ii].Key)) + { + return _keyValuePairs[ii].Value; + } + } + + throw new KeyNotFoundException(); + } + set + { + throw new NotSupportedException(); + } + } + + public ICollection Keys => _keyValuePairs.Select(kv => kv.Key).ToArray(); + + public ICollection Values => _keyValuePairs.Select(kv => kv.Value).ToArray(); + + public int Count => _keyValuePairs.Length; + + public bool IsReadOnly => true; + + public void Add(TK key, TV value) + { + throw new NotSupportedException(); + } + + public void Add(KeyValuePair item) + { + throw new NotSupportedException(); + } + + public void Clear() + { + throw new NotSupportedException(); + } + + public bool Contains(KeyValuePair item) + { + for (int ii = 0; ii < _keyValuePairs.Length; ii++) + { + if (Equals(item, _keyValuePairs[ii])) + { + return true; + } + } + + return false; + } + + public bool ContainsKey(TK key) + { + for (int ii = 0; ii < _keyValuePairs.Length; ii++) + { + if (Equals(key, _keyValuePairs[ii].Key)) + { + return true; + } + } + + return false; + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + throw new NotSupportedException(); + } + + public IEnumerator> GetEnumerator() + { + for (int ii = 0; ii < _keyValuePairs.Length; ii++) + { + yield return _keyValuePairs[ii]; + } + } + + public bool Remove(TK key) + { + throw new NotSupportedException(); + } + + public bool Remove(KeyValuePair item) + { + throw new NotSupportedException(); + } + + public bool TryGetValue(TK key, out TV value) + { + for (int ii = 0; ii < _keyValuePairs.Length; ii++) + { + if (Equals(key, _keyValuePairs[ii].Key)) + { + value = _keyValuePairs[ii].Value; + return true; + } + } + + value = default(TV); + return false; + } + + IEnumerator IEnumerable.GetEnumerator() + { + for (int ii = 0; ii < _keyValuePairs.Length; ii++) + { + yield return _keyValuePairs[ii]; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/collections/ArrayHelper.cs b/NEsper.Core/NEsper.Core/compat/collections/ArrayHelper.cs new file mode 100755 index 000000000..87025fbef --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/collections/ArrayHelper.cs @@ -0,0 +1,47 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.compat.collections +{ + /// + /// Helper class that assists with operations on arrays. + /// + + public static class ArrayHelper + { + /// + /// Compares two arrays for equality + /// + /// + /// + /// + + public static bool AreEqual( Array array1, Array array2 ) + { + if ( array1 == null ) { + throw new ArgumentNullException( "array1" ) ; + } + if ( array2 == null ) { + throw new ArgumentNullException( "array2" ) ; + } + if ( array1.Length != array2.Length ) { + return false ; + } + + for( int ii = array1.Length - 1 ; ii >= 0 ; ii-- ) { + if ( ! Object.Equals( array1.GetValue(ii), array2.GetValue(ii) ) ) { + return false ; + } + } + + return true; + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/collections/BaseMap.cs b/NEsper.Core/NEsper.Core/compat/collections/BaseMap.cs new file mode 100755 index 000000000..3d8304d1f --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/collections/BaseMap.cs @@ -0,0 +1,546 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.compat.collections +{ + /// + /// Base for dictionaries that contain extended functionality. + /// + /// + /// + + [Serializable] + public class BaseMap : IDictionary + where K : class + { + private IDictionary _subDictionary; + + /// + /// Record for handling null keys. + /// + + private KeyValuePair? _nullEntry; + + /// + /// Gets or sets the subdictionary. + /// + + protected IDictionary SubDictionary + { + get { return _subDictionary ; } + set { _subDictionary = value ; } + } + + /// + /// Allows subclasses to bind the subdictionary later in their + /// initialization process. + /// + + protected BaseMap() + { + _subDictionary = null; + _nullEntry = null; + } + + /// + /// Constructs a new dictionary + /// + /// + + public BaseMap( IDictionary subDictionary ) + { + _subDictionary = subDictionary ; + _nullEntry = null; + } + + /// + /// Fetches the value associated with the specified key. + /// If no value can be found, then the defaultValue is + /// returned. + /// + /// + /// + /// + + public virtual V Get( K key, V defaultValue ) + { + V returnValue = defaultValue; + if (key != null) + { + if (!TryGetValue(key, out returnValue)) + { + returnValue = defaultValue; + } + } + else if ( _nullEntry != null ) + { + returnValue = _nullEntry.Value.Value; + } + return returnValue; + } + + /// + /// Fetches the value associated with the specified key. + /// If no value can be found, then default(V) is returned. + /// + /// + /// + + public virtual V Get( K key ) { + return Get( key, default(V) ); + } + + /// + /// Removes the item from the dictionary that is associated with + /// the specified key. Returns the value that was found at that + /// location and removed or the defaultValue. + /// + /// Search key into the dictionary + /// The value removed from the dictionary (if found). + /// + + public bool Remove(K key, out V value) + { + if ( key != null ) + { + if (!_subDictionary.TryGetValue(key, out value)) + { + return false; + } + } + else + { + if ( _nullEntry != null ) + { + value = _nullEntry.Value.Value; + _nullEntry = null ; + } + else + { + value = default(V); + } + } + + return _subDictionary.Remove(key); + } + + /// + /// Removes the item from the dictionary that is associated with + /// the specified key. The item if found is returned; if not, + /// default(V) is returned. + /// + /// + /// + + public V RemoveAndReturn(K key) + { + V tempItem; + + return Remove(key, out tempItem) + ? tempItem + : default(V); + } + + /// + /// Sets the given key in the dictionary. If the key + /// already exists, then it is remapped to thenew value. + /// + + public virtual void Put( K key, V value ) + { + this[key] = value; + } + + /// + /// Sets the given key in the dictionary. If the key + /// already exists, then it is remapped to the new value. + /// If a value was previously mapped it is returned. + /// + + public virtual V Push(K key, V value) + { + V temp; + TryGetValue(key, out temp); + this[key] = value; + return temp; + } + + /// + /// Puts all values from the source dictionary into + /// this dictionary. + /// + /// + + public virtual void PutAll( IDictionary source ) + { + foreach( KeyValuePair kvPair in source ) { + this[kvPair.Key] = kvPair.Value; + } + } + + /// + /// Returns the first value in the enumeration of values + /// + /// + + public virtual V FirstValue + { + get + { + IEnumerator> kvPairEnum = GetEnumerator() ; + kvPairEnum.MoveNext() ; + return kvPairEnum.Current.Value; + } + } + + /// + /// Gets or sets the item with the specified key. + /// + /// + public V this[K key] + { + get + { + if ( key != null ) { + return _subDictionary[key] ; + } else if ( _nullEntry != null ) { + return _nullEntry.Value.Value; + } else { + throw new KeyNotFoundException() ; + } + } + set + { + if ( key != null ) { + _subDictionary[key] = value; + } else { + _nullEntry = new KeyValuePair(key, value); + } + } + } + + /// + /// Gets an containing the keys of the . + /// + /// + /// An containing the keys of the object that : . + public ICollection Keys { + get { + return + _nullEntry == null + ? _subDictionary.Keys + : new CollectionPlus( _subDictionary.Keys, _nullEntry.Value.Key ); + } + } + + /// + /// Gets an containing the values in the . + /// + /// + /// An containing the values in the object that : . + public ICollection Values { + get { + return + _nullEntry == null + ? _subDictionary.Values + : new CollectionPlus( _subDictionary.Values, _nullEntry.Value.Value ) ; + } + } + + /// + /// Gets the number of elements contained in the . + /// + /// + /// The number of elements contained in the . + public int Count { + get { + return + _nullEntry == null + ? _subDictionary.Count + : _subDictionary.Count + 1 ; + } + } + + /// + /// Gets a value indicating whether the is read-only. + /// + /// + /// true if the is read-only; otherwise, false. + public bool IsReadOnly { + get { return _subDictionary.IsReadOnly ; } + } + + /// + /// Determines whether the contains an element with the specified key. + /// + /// The key to locate in the . + /// + /// true if the contains an element with the key; otherwise, false. + /// + /// key is null. + public bool ContainsKey(K key) + { + if ( key != null ) { + return _subDictionary.ContainsKey(key); + } else { + return _nullEntry != null; + } + } + + /// + /// Adds an element with the provided key and value to the . + /// + /// The object to use as the key of the element to add. + /// The object to use as the value of the element to add. + /// The is read-only. + /// An element with the same key already exists in the . + /// key is null. + public void Add(K key, V value) + { + if ( key != null ) { + _subDictionary.Add(key, value); + } else if (_nullEntry == null) { + _nullEntry = new KeyValuePair(key, value); + } else { + throw new ArgumentException( "An element with the same key already exists in the dictionary" ) ; + } + } + + /// + /// Removes the element with the specified key from the . + /// + /// The key of the element to remove. + /// + /// true if the element is successfully removed; otherwise, false. This method also returns false if key was not found in the original . + /// + /// The is read-only. + /// key is null. + public bool Remove(K key) + { + if ( key != null ) { + return _subDictionary.Remove( key ) ; + } else { + bool wasFound = _nullEntry != null ; + _nullEntry = null ; + return wasFound; + } + } + + /// + /// Tries the get value. + /// + /// The key. + /// The value. + /// + public bool TryGetValue(K key, out V value) + { + if ( key != null ) { + return _subDictionary.TryGetValue( key, out value ) ; + } else if ( _nullEntry != null ) { + value = _nullEntry.Value.Value; + return true; + } else { + value = default(V); + return false; + } + } + + /// + /// Adds an item to the . + /// + /// The object to add to the . + /// The is read-only. + public void Add(KeyValuePair item) + { + if ( item.Key != null ) { + _subDictionary.Add( item ); + } else if ( _nullEntry != null ) { + throw new ArgumentException( "An element with the same key already exists in the dictionary" ) ; + } else { + _nullEntry = new KeyValuePair(item.Key, item.Value); + } + } + + /// + /// Removes all items from the . + /// + /// The is read-only. + public void Clear() + { + _nullEntry = null; + _subDictionary.Clear() ; + } + + /// + /// Determines whether the contains a specific value. + /// + /// The object to locate in the . + /// + /// true if item is found in the ; otherwise, false. + /// + public bool Contains(KeyValuePair item) + { + if ( item.Key != null ) { + return _subDictionary.Contains( item ) ; + } else { + return Object.Equals(_nullEntry, item); + } + } + + /// + /// Copies the elements of the to an , starting at a particular index. + /// + /// The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing. + /// The zero-based index in array at which copying begins. + /// arrayIndex is less than 0. + /// array is null. + /// array is multidimensional.-or-arrayIndex is equal to or greater than the length of array.-or-The number of elements in the source is greater than the available space from arrayIndex to the end of the destination array.-or-Type T cannot be cast automatically to the type of the destination array. + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + if ( _nullEntry != null ) + { + array[arrayIndex++] = new KeyValuePair( + _nullEntry.Value.Key, + _nullEntry.Value.Value); + } + + _subDictionary.CopyTo( array, arrayIndex ) ; + } + + /// + /// Removes the first occurrence of a specific object from the . + /// + /// The object to remove from the . + /// + /// true if item was successfully removed from the ; otherwise, false. This method also returns false if item is not found in the original . + /// + /// The is read-only. + public bool Remove(KeyValuePair item) + { + if ( item.Key != null ) { + return _subDictionary.Remove( item ) ; + } else if ( Object.Equals( item, _nullEntry ) ) { + _nullEntry = null ; + return true; + } else { + return false; + } + } + + /// + /// Returns an enumerator that iterates through the collection. + /// + /// + /// A that can be used to iterate through the collection. + /// + public IEnumerator> GetEnumerator() + { + if ( _nullEntry != null ) { + yield return _nullEntry.Value; + } + + IEnumerator> temp = _subDictionary.GetEnumerator() ; + while( temp.MoveNext() ) { + yield return temp.Current; + } + } + + /// + /// Returns an enumerator that iterates through a collection. + /// + /// + /// An object that can be used to iterate through the collection. + /// + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + if ( _nullEntry != null ) { + yield return _nullEntry; + } + + IEnumerator> temp = _subDictionary.GetEnumerator() ; + while( temp.MoveNext() ) { + yield return temp.Current; + } + } + + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// + /// true if the specified is equal to the current ; otherwise, false. + /// + + public override bool Equals(object obj) + { + if ((obj == null) || (obj.GetType() != GetType())) + { + return false; + } + + BaseMap oMap = (BaseMap) obj ; + return + Object.Equals( _nullEntry, oMap._nullEntry ) && + Collections.AreEqual( _subDictionary, oMap._subDictionary ) ; + } + + /// + /// Serves as a hash function for a particular type. is suitable for use in hashing algorithms and data structures like a hash table. + /// + /// + /// A hash code for the current . + /// + + public override int GetHashCode() + { + return _subDictionary.GetHashCode(); + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + + public override string ToString() + { + if ( _nullEntry == null ) { + return _subDictionary.ToString(); + } else { + return _subDictionary.ToString() + '+' + _nullEntry; + } + } + + /// + /// Creates an Map from the IDictionary. + /// + /// + /// + + public static IDictionary AsEDictionary( IDictionary sourceDictionary ) { + IDictionary result = sourceDictionary as IDictionary; + if ( result == null ) + { + if ( sourceDictionary != null ) + { + result = new BaseMap(sourceDictionary); + } else + { + result = null; + } + } + + return result; + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/collections/BoundBlockingQueue.cs b/NEsper.Core/NEsper.Core/compat/collections/BoundBlockingQueue.cs new file mode 100755 index 000000000..b6cfd27e7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/collections/BoundBlockingQueue.cs @@ -0,0 +1,197 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Threading; + +using com.espertech.esper.compat.threading; + +namespace com.espertech.esper.compat.collections +{ + public class BoundBlockingQueue : IBlockingQueue + { + private readonly LinkedList _queue; + private readonly ILockable _queueLock; + private readonly Object _queuePopWaitHandle; + private readonly Object _queuePushWaitHandle; + private readonly int _maxCapacity; + + /// + /// Initializes a new instance of the class. + /// + /// The max capacity. + public BoundBlockingQueue(int maxCapacity) + { + _queue = new LinkedList(); + _queueLock = new MonitorSpinLock(); + _queuePopWaitHandle = new AutoResetEvent(false); + _queuePushWaitHandle = new AutoResetEvent(false); + _maxCapacity = maxCapacity; + } + + /// + /// Gets the number of items in the queue. + /// + /// The count. + public int Count + { + get + { + using (_queueLock.Acquire()) + { + return _queue.Count; + } + } + } + + /// + /// Clears all items from the queue + /// + public void Clear() + { + using (_queueLock.Acquire()) + { + _queue.Clear(); + + PulsePushHandle(); + PulsePopHandle(); + + //_queuePushWaitHandle.Insert(); // Push is clear + //_queuePopWaitHandle.Reset(); // Pop now waits + } + } + + /// + /// Pushes an item onto the queue. If the queue has reached + /// capacity, the call will pend until the queue has space to + /// receive the request. + /// + /// + public void Push(T item) + { + while (true) + { + using (_queueLock.Acquire()) { + if ((_queue.Count < _maxCapacity) || BoundBlockingQueueOverride.IsEngaged) + { + _queue.AddLast(item); + PulsePopHandle(); + return; + } + } + + WaitPushHandle(); + + //_queuePushWaitHandle.WaitOne(); + } + } + + /// + /// Pops an item off the queue. If there is nothing on the queue + /// the call will pend until there is an item on the queue. + /// + /// + public T Pop() + { + while (true) + { + using (_queueLock.Acquire()) + { + var first = _queue.First; + if (first != null) + { + var value = first.Value; + _queue.RemoveFirst(); + + PulsePushHandle(); + + //_queuePushWaitHandle.Insert(); // Push is clear + return value; + } + } + + WaitPopHandle(); + + //_queuePopWaitHandle.WaitOne(); + } + } + + /// + /// Pops an item off the queue. If there is nothing on the queue + /// the call will pend until there is an item on the queue or + /// the timeout has expired. If the timeout has expired, the + /// method will return false. + /// + /// The max timeout in millis. + /// The item. + /// + public bool Pop(int maxTimeoutInMillis, out T item) + { + long endTime = DateTimeHelper.CurrentTimeMillis + maxTimeoutInMillis; + + do { + using (_queueLock.Acquire()) { + var first = _queue.First; + if (first != null) { + var value = first.Value; + _queue.RemoveFirst(); + + PulsePushHandle(); + + item = value; + return true; + } + } + + var nowTime = DateTimeHelper.CurrentTimeMillis; + if (nowTime >= endTime) { + item = default(T); + return false; + } + + WaitPopHandle((int) (endTime - nowTime)); + } while (true); + } + + private void PulsePushHandle() + { + Monitor.Enter(_queuePushWaitHandle); + Monitor.PulseAll(_queuePushWaitHandle); + Monitor.Exit(_queuePushWaitHandle); + } + + private void PulsePopHandle() + { + Monitor.Enter(_queuePopWaitHandle); + Monitor.PulseAll(_queuePopWaitHandle); + Monitor.Exit(_queuePopWaitHandle); + } + + private void WaitPushHandle() + { + Monitor.Enter(_queuePushWaitHandle); + Monitor.Wait(_queuePushWaitHandle); + Monitor.Exit(_queuePushWaitHandle); + } + + private void WaitPopHandle() + { + Monitor.Enter(_queuePopWaitHandle); + Monitor.Wait(_queuePopWaitHandle); + Monitor.Exit(_queuePopWaitHandle); + } + + private void WaitPopHandle(int timeToWait) + { + Monitor.Enter(_queuePopWaitHandle); + Monitor.Wait(_queuePopWaitHandle, timeToWait); + Monitor.Exit(_queuePopWaitHandle); + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/collections/BoundBlockingQueueOverride.cs b/NEsper.Core/NEsper.Core/compat/collections/BoundBlockingQueueOverride.cs new file mode 100755 index 000000000..cddf74ea9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/collections/BoundBlockingQueueOverride.cs @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.compat.collections +{ + internal sealed class BoundBlockingQueueOverride + { + internal static BoundBlockingQueueOverride Default; + + /// + /// Gets a value indicating whether the override is engaged. + /// + /// true if the override is engaged; otherwise, false. + internal static bool IsEngaged + { + get { return ScopedInstance.Current != null; } + } + + /// + /// Initializes the class. + /// + static BoundBlockingQueueOverride() + { + Default = new BoundBlockingQueueOverride(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/collections/ChainedArrayList.cs b/NEsper.Core/NEsper.Core/compat/collections/ChainedArrayList.cs new file mode 100755 index 000000000..550b29239 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/collections/ChainedArrayList.cs @@ -0,0 +1,231 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections; +using System.Collections.Generic; + +namespace com.espertech.esper.compat.collections +{ + public sealed class ChainedArrayList : ICollection + { + private int _count; + private readonly int _chainLength; + private Entry _chainHead; + private Entry _chainTail; + + public ChainedArrayList(int chainLength) + { + _chainLength = chainLength; + _chainHead = _chainTail = new Entry + { + Index = 0, + Value = new T[_chainLength], + Next = null + }; + } + + public ChainedArrayList(IEnumerable source, int chainLength) + { + _chainLength = chainLength; + _chainHead = _chainTail = new Entry + { + Index = 0, + Value = new T[_chainLength], + Next = null + }; + + AddRange(source); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public IEnumerator GetEnumerator() + { +#if true + return new EnumeratorImpl(_chainHead, 0); +#else + for (var curr = _chainHead; curr != null; curr = curr.Next) + { + for (int ii = 0; ii < curr.Index; ii++) + { + yield return curr.ConstantValue[ii]; + } + } +#endif + } + + public void ForEach(Action action) + { + for (var curr = _chainHead; curr != null; curr = curr.Next) + { + for (int ii = 0; ii < curr.Index; ii++) + { + action.Invoke(curr.Value[ii]); + } + } + } + + private void Expand() + { + var curr = new Entry + { + Index = 0, + Value = new T[_chainLength], + Next = null + }; + + _chainTail.Next = curr; + _chainTail = curr; + } + + public void Add(T item) + { + _count++; + _chainTail.Value[_chainTail.Index++] = item; + if (_chainTail.Index == _chainLength) + { + Expand(); + } + } + + private void AddRange(IEnumerable source) + { + var e = source.GetEnumerator(); + while (e.MoveNext()) + { + Add(e.Current); + } + } + + public void Clear() + { + _count = 0; + _chainHead = _chainTail = new Entry + { + Index = 0, + Value = new T[_chainLength], + Next = null + }; + } + + public bool Contains(T item) + { + for (var curr = _chainHead; curr != null; curr = curr.Next) + { + for (int ii = 0; ii < curr.Index; ii++) + { + if (Equals(item, curr.Value[ii])) + { + return true; + } + } + } + + return false; + } + + public void CopyTo(T[] array, int arrayIndex) + { + for (var curr = _chainHead; curr != null; curr = curr.Next) + { + for (int ii = 0; ii < curr.Index; ii++) + { + array[arrayIndex++] = curr.Value[ii]; + } + } + } + + public bool Remove(T item) + { + throw new NotSupportedException(); + } + + public int Count + { + get { return _count; } + } + + public bool IsReadOnly + { + get { return true; } + } + + internal sealed class Entry + { + internal Entry Next; + internal int Index; + internal T[] Value; + } + + internal sealed class EnumeratorImpl : IEnumerator + { + private bool _hasNext; + private Entry _entry; + private int _index; + private T _value; + + public EnumeratorImpl(Entry entry, int index) + { + _entry = entry; + _index = index; + _value = default(T); + + if (!(_hasNext = (_index < _entry.Index))) + { + _index = 0; + _entry = _entry.Next; // probably null + } + } + + public void Dispose() + { + } + + public bool MoveNext() + { + if (_hasNext) + { + // ldarg.0 + // ldfld _indx + // ldarg.0 + // stfld _value + _value = _entry.Value[_index]; + if (++_index >= _entry.Index) + { + _index = 0; + _entry = _entry.Next; + _hasNext = _entry != null; + } + + return true; + } + + return false; + } + + public void Reset() + { + throw new NotSupportedException(); + } + + object IEnumerator.Current + { + get { return Current; } + } + + public T Current + { + get { return _value; } + } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/compat/collections/CollectionPlus.cs b/NEsper.Core/NEsper.Core/compat/collections/CollectionPlus.cs new file mode 100755 index 000000000..3537321a1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/collections/CollectionPlus.cs @@ -0,0 +1,165 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.compat.collections +{ + /// + /// Collection that wraps another collection + an item + /// + public class CollectionPlus : ICollection + { + private readonly ICollection m_baseCollection ; + private readonly T m_additionalItem; + + /// + /// Constructs a new collection plus an item + /// + /// + /// + public CollectionPlus(ICollection baseCollection, T item) + { + this.m_baseCollection = baseCollection; + this.m_additionalItem = item; + } + + /// + ///Gets the number of elements contained in the . + /// + /// + /// + ///The number of elements contained in the . + /// + /// + public int Count { + get { + return m_baseCollection.Count + 1 ; + } + } + + /// + ///Gets a value indicating whether the is read-only. + /// + /// + /// + ///true if the is read-only; otherwise, false. + /// + /// + public bool IsReadOnly { + get { + return true; + } + } + + /// + ///Adds an item to the . + /// + /// + ///The object to add to the . + ///The is read-only. + public void Add(T item) + { + throw new NotSupportedException(); + } + + /// + ///Removes all items from the . + /// + /// + ///The is read-only. + public void Clear() + { + throw new NotSupportedException(); + } + + /// + ///Determines whether the contains a specific value. + /// + /// + /// + ///true if item is found in the ; otherwise, false. + /// + /// + ///The object to locate in the . + public bool Contains(T item) + { + if ( Equals( m_additionalItem, item ) ) { + return true ; + } + + return m_baseCollection.Contains(item); + } + + /// + ///Copies the elements of the to an , starting at a particular index. + /// + /// + ///The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing. + ///The zero-based index in array at which copying begins. + ///arrayIndex is less than 0. + ///array is null. + ///array is multidimensional.-or-arrayIndex is equal to or greater than the length of array.-or-The number of elements in the source is greater than the available space from arrayIndex to the end of the destination array.-or-Type T cannot be cast automatically to the type of the destination array. + public void CopyTo(T[] array, int arrayIndex) + { + array[arrayIndex++] = m_additionalItem; + m_baseCollection.CopyTo(array, arrayIndex); + } + + /// + ///Removes the first occurrence of a specific object from the . + /// + /// + /// + ///true if item was successfully removed from the ; otherwise, false. This method also returns false if item is not found in the original . + /// + /// + ///The object to remove from the . + ///The is read-only. + public bool Remove(T item) + { + throw new NotSupportedException(); + } + + /// + ///Returns an enumerator that iterates through the collection. + /// + /// + /// + ///A that can be used to iterate through the collection. + /// + ///1 + public IEnumerator GetEnumerator() + { + yield return m_additionalItem ; + + foreach( T item in m_baseCollection ) { + yield return item; + } + } + + /// + ///Returns an enumerator that iterates through a collection. + /// + /// + /// + ///An object that can be used to iterate through the collection. + /// + ///2 + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + yield return m_additionalItem ; + + foreach( T item in m_baseCollection ) { + yield return item; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/collections/Collections.cs b/NEsper.Core/NEsper.Core/compat/collections/Collections.cs new file mode 100755 index 000000000..4d354e99e --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/collections/Collections.cs @@ -0,0 +1,377 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace com.espertech.esper.compat.collections +{ + /// + /// Provides additional functions that are useful when operating on + /// collections. + /// + + public static class Collections + { + public static readonly IDictionary EmptyDataMap = + new Dictionary(); + + public static void SortInPlace(this IList list) + { + if (list is List) + { + ((List) list).Sort(); + } + else if (list is T[]) + { + System.Array.Sort((T[]) list); + } + else + { + var replList = new List(list); + replList.Sort(); + for (int ii = list.Count - 1; ii >= 0; ii--) + list[ii] = replList[ii]; + } + } + + public static void SortInPlace(this IList list, IComparer comparer) + { + if (list is List) + { + ((List)list).Sort(comparer); + } + else if (list is T[]) + { + System.Array.Sort((T[])list, comparer); + } + else + { + var replList = new List(list); + replList.Sort(comparer); + for (int ii = list.Count - 1; ii >= 0; ii--) + list[ii] = replList[ii]; + } + } + + public static void SortInPlace(this IList list, Func comparerFunc) + { + SortInPlace(list, new ProxyComparer(comparerFunc)); + } + + public static T[] Array(params T[] items) + { + return items; + } + + /// + /// Instas the list. + /// + /// + /// The items. + /// + public static IList List(params T[] items) + { + return items; + } + + /// + /// Creates a singleton list. + /// + /// + /// The item. + /// + public static IList SingletonList(T item) + { + return new T[] {item}; + } + + /// + /// Creates a singleton set. + /// + /// + /// The item. + /// + public static ISet SingletonSet(T item) + { + return new HashSet + { + item + }; + } + + /// + /// Returns an empty IDictionary for type K,V + /// + /// + public static IDictionary GetEmptyMap() + { + return EmptyDictionary.Instance; + } + + /// + /// Returns an empty IList for type T. + /// + /// + /// + public static IList GetEmptyList() + { + return EmptyList.Instance; + } + + /// + /// Returns an empty collection for type T. + /// + /// + /// + public static ISet GetEmptySet() + { + return EmptySet.Instance; + } + + /// + /// Compares two collections of objects. The objects must share the same generic + /// parameter, but can be of different collections. + /// + /// + /// + /// + + public static bool AreEqual(T[] baseObj, T[] compObj) + { + bool baseIsNull = baseObj == null; + bool compIsNull = compObj == null; + + if (baseIsNull && compIsNull) + { + return true; + } + + if (baseIsNull || compIsNull) + { + return false; + } + + if (baseObj.Length != compObj.Length) + { + return false; + } + + var objCount = baseObj.Length; + for( int ii = 0 ; ii < objCount ; ii++ ) + { + if (!Equals(baseObj[ii], compObj[ii])) + { + return false; + } + } + + return true; + } + + + /// + /// Compares two collections of objects. The objects must share the same generic + /// parameter, but can be of different collections. + /// + /// + /// + /// + + public static bool AreEqual(ICollection baseObj, ICollection compObj) + { + bool baseIsNull = baseObj == null; + bool compIsNull = compObj == null; + + if ( baseIsNull && compIsNull ) + { + return true; + } + + if ( baseIsNull || compIsNull ) + { + return false; + } + + if (baseObj.Count != compObj.Count) + { + return false; + } + + IEnumerator baseEnum = baseObj.GetEnumerator(); + IEnumerator compEnum = compObj.GetEnumerator(); + + return AreEqual(baseEnum, compEnum); + } + + /// + /// Compares two collections of objects. The objects must share the same generic + /// parameter, but can be of different collections. + /// + /// The base enumerator. + /// The comp enumerator. + /// + /// + + public static bool AreEqual(IEnumerator baseEnum, IEnumerator compEnum) + { + for (; ;) + { +#if true + int bitMask = + (baseEnum.MoveNext() ? 2 : 0) | + (compEnum.MoveNext() ? 1 : 0); + switch(bitMask) + { + case 0: + return true; + case 3: + if (!Equals(baseEnum.Current, compEnum.Current)) + { + return false; + } + break; + default: + return false; + } +#else + bool baseTest = baseEnum.MoveNext(); + bool compTest = compEnum.MoveNext(); + + if (baseTest && compTest) + { + if (!Object.Equals(baseEnum.Current, compEnum.Current)) + { + return false; + } + } + else if (!baseTest && !compTest) + { + return true; + } + else + { + // Both baseTest and compTest should both be returning + // false at this point. Failure to do so indicates that + // one enumerator is returning more results than the + // other. + + return false; + } +#endif + } + } + + /// + /// Converts all of the items in source to an array. + /// + /// + /// + + public static T[] ToArray( ICollection source ) + { + if ( source is T[] ) { + return ((T[]) source); + } else if ( source is List ) { + return ((List) source).ToArray() ; + } + + T[] array = new T[source.Count] ; + source.CopyTo( array, 0 ) ; + return array ; + } + + /// + /// Advances the enumerator and returns the next item. + /// + /// + /// + /// + + public static T Next(IEnumerator enumObj) + { + enumObj.MoveNext(); + return enumObj.Current; + } + + /// + /// Shuffles the list. + /// + /// + + public static void Shuffle( IList list ) + { + Shuffle( list, list.Count * 2, new Random() ) ; + } + + /// + /// Shuffles the list. User supplies the randomizer. + /// + /// + /// + + public static void Shuffle( IList list, Random random ) + { + Shuffle( list, list.Count * 2, random ) ; + } + + /// + /// Shuffles the list. User supplies the randomizer. Performs + /// at least iteration swaps. + /// + /// + /// + /// + + public static void Shuffle( IList list, int iterations, Random random ) + { + int count = list.Count ; + + for( int ii = iterations ; ii >= 0 ; ii-- ) + { + int index1 = random.Next( count ) ; + int index2 = random.Next( count ) ; + T temp = list[index1] ; + list[index1] = list[index2] ; + list[index2] = temp ; + } + } + + public static int GetHashCode(IEnumerable enumerable) where T : class + { + return enumerable.Aggregate(0, (current, temp) => current ^ (temp != null ? temp.GetHashCode() : 0)); + } + +#if false + public static int GetHashCode( Array a ) + { + int hashCode = 0; + for( int ii = a.Length - 1 ; ii >= 0 ; ii-- ) { + Object temp = a.GetValue(ii); + int tempHash = temp != null ? temp.GetHashCode() : 0; + hashCode = 31*hashCode + tempHash; + } + + return hashCode; + } +#endif + + public static IDictionary SingletonDataMap(string key, object value) + { + return SingletonMap(key, value); + } + + public static IDictionary SingletonMap(TK key, TV value) + { + var dictionary = new Dictionary(); + dictionary[key] = value; + return dictionary; + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/collections/CompatExtensions.cs b/NEsper.Core/NEsper.Core/compat/collections/CompatExtensions.cs new file mode 100755 index 000000000..98d8028f9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/collections/CompatExtensions.cs @@ -0,0 +1,1237 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Reflection; +using System.Text; + +using com.espertech.esper.collection; +using com.espertech.esper.compat.attributes; +using com.espertech.esper.compat.magic; + +namespace com.espertech.esper.compat.collections +{ + public static class CompatExtensions + { + public static LookaheadEnumerator WithLookahead(this IEnumerator en) + { + return new LookaheadEnumerator(en); + } + + public static LookaheadEnumerator EnumerateWithLookahead(this IEnumerable en) + { + return new LookaheadEnumerator(en); + } + + public static ICollection TransformInto(this ICollection collection) + where TExt : TInt + { + return TransformInto( + collection, + e => (TInt) e, + i => (TExt) i); + } + + public static ICollection TransformInto(this ICollection collection, + Func transformExtInt, + Func transformIntExt) + { + return new TransformCollection(collection, transformExtInt, transformIntExt); + } + + public static ReadOnlyCollection AsReadOnlyCollection(this ICollection enumerable) + { + return new ReadOnlyCollection(enumerable); + } + + public static ReadOnlyList AsReadOnlyList(this IList enumerable) + { + return new ReadOnlyList(enumerable); + } + + public static T[] MaterializeArray(this IEnumerable enumerable) + { + if (enumerable == null) + { + return new T[0]; + } + + return enumerable.ToArray(); + } + + public static T[] ToArrayOrNull(this IEnumerable enumerable) + { + if (enumerable == null) { + return null; + } + + var tempArray = enumerable.ToArray(); + if (tempArray.Length == 0) { + return null; + } + + return tempArray; + } + + public static T[] ToListOrNull(this IEnumerable enumerable, bool nullWhenEmpty = true) + { + if (enumerable == null) + { + return null; + } + + var tempArray = enumerable.ToArray(); + if (tempArray.Length == 0 && nullWhenEmpty) + { + return null; + } + + return tempArray; + } + + public static bool IsGreaterThan(this IComparable self, T that) + { + return self.CompareTo(that) > 0; + } + + public static bool IsGreaterThanOrEqual(this IComparable self, T that) + { + return self.CompareTo(that) >= 0; + } + + public static bool IsLessThan(this IComparable self, T that) + { + return self.CompareTo(that) < 0; + } + + public static bool IsLessThanOrEqual(this IComparable self, T that) + { + return self.CompareTo(that) <= 0; + } + + public static T[] ToPopArray(this Stack stack) + { + T[] array = new T[stack.Count]; + for( int ii = 0 ; stack.Count != 0 ; ii++) { + array[ii] = stack.Pop(); + } + + return array; + } + + public static bool Contains(this IList list, Predicate predicate) + { + for( int ii = 0 ; ii < list.Count ; ii++ ) { + if ( predicate.Invoke(list[ii] ) ) { + return true; + } + } + return false; + } + + public static bool Contains(this T[] array, T value) + { + return Array.IndexOf(array, value) != -1; + } + + public static HashSet AsHashSet(params T[] values) + { + return new HashSet(values); + } + + public static HashSet AsHashSet(this IEnumerable enumerable) + { + if (enumerable == null) + return null; + if (enumerable is HashSet) + return (HashSet) enumerable; + return new HashSet(enumerable); + } + + public static SynchronizedCollection AsSyncCollection(this ICollection unsyncCollection) + { + return new SynchronizedCollection(unsyncCollection); + } + + public static SynchronizedList AsSyncList( this IList unsyncList ) + { + return new SynchronizedList(unsyncList); + } + + public static SynchronizedSet AsSyncSet(this ISet unsyncSet) + { + return new SynchronizedSet(unsyncSet); + } + + public static LinkedList AsLinkedList(this IEnumerable enumerable) + { + if (enumerable is LinkedList) + return (LinkedList) enumerable; + return new LinkedList(enumerable); + } + + public static IList AsList(this IEnumerable enumerable) + { + if ( enumerable is IList ) + return (IList) enumerable; + return new List(enumerable); + } + + public static IList AsList(params T[] array) + { + return array; + } + + public static IList AsMutableList( this IEnumerable enumerable ) + { + return new List(enumerable); + } + + public static IList AsSortedList(this IEnumerable enumerable) + { + if (enumerable == null) + return null; + + var asList = enumerable as List; + if ( asList == null ) { + asList = new List(enumerable); + } + + asList.Sort(); + + return asList; + } + + public static void ForEach(this LinkedList listThis, Action action) + { + if (listThis != null) + { + var node = listThis.First; + while (node != null) + { + action.Invoke(node.Value); + node = node.Next; + } + } + } + + public static void ForEach(this IList arrayThis, Action action) + { + if (arrayThis != null) + { + int length = arrayThis.Count; + for (int ii = 0; ii < length; ii++) + { + action.Invoke(arrayThis[ii]); + } + } + } + + public static void ForEvery(this T[] arrayThis, Action action) + { + if (arrayThis != null) + { + int length = arrayThis.Length; + for (int ii = 0; ii < length; ii++) + { + action.Invoke(arrayThis[ii]); + } + } + } + + public static void For(this IEnumerable enumThis, Action action) + { + unchecked + { + if (enumThis == null) + { + return; + } + if (enumThis is ChainedArrayList) + { + var arrayThis = (ChainedArrayList) enumThis; + arrayThis.ForEach(action); + } + else if (enumThis is List) + { + var arrayThis = (List)enumThis; + arrayThis.ForEach(action); + } + else if (enumThis is IList) + { + var arrayThis = (IList) enumThis; + var length = arrayThis.Count; + for (int ii = 0; ii < length; ii++) + { + action.Invoke(arrayThis[ii]); + } + } + else + { + foreach (T item in enumThis) + { + action.Invoke(item); + } + } + } + } + + public static IEnumerable Is(this IEnumerable enumThis) + { + return enumThis.Where(item => item is TX); + } + + public static void Fill(this IList listThis, T value) + { + for (int ii = 0; ii < listThis.Count; ii++) + { + listThis[ii] = value; + } + } + + public static void Fill(this T[] arrayThis, T value) + { + for (int ii = 0; ii < arrayThis.Length; ii++) + { + arrayThis[ii] = value; + } + } + + public static void Fill(this T[] arrayThis, Func generator) + { + for (int ii = 0; ii < arrayThis.Length; ii++) + { + arrayThis[ii] = generator.Invoke(); + } + } + + public static void Fill(this T[] arrayThis, Func generator) + { + for (int ii = 0; ii < arrayThis.Length; ii++) + { + arrayThis[ii] = generator.Invoke(ii); + } + } + + public static bool IsEqual(this T[] arrayThis, T[] arrayThat) + { + if (arrayThis.Length != arrayThat.Length) + { + return false; + } + + for (int ii = 0; ii < arrayThis.Length; ii++) + { + if (!Equals(arrayThis[ii], arrayThat[ii])) + { + return false; + } + } + + return true; + } + + public static bool IsEqual( this IEnumerator enumThis, IEnumerator enumThat ) + { + while (true) + { + var testThis = enumThis.MoveNext(); + var testThat = enumThat.MoveNext(); + if (testThis && testThat) + { + if (!Equals(enumThis.Current, enumThat.Current)) + { + return false; + } + } + else if (!testThis && !testThat) + { + return true; + } + else + { + return false; + } + } + } + + /// + /// Gets the item at the nth index of the enumerable. + /// + /// The enumerable. + /// The index. + /// + public static Object AtIndex( this IEnumerable enumerable, int index ) + { + return AtIndex(enumerable, index, failIndex => null); + } + + /// + /// Gets the item at the nth index of the enumerable. + /// + /// The enumerable. + /// The index. + /// The item not found. + /// + public static Object AtIndex( this IEnumerable enumerable, int index, Func itemNotFound ) + { + if ( enumerable is IList ) { + var asList = (IList) enumerable; + return index < asList.Count ? asList[index] : itemNotFound(index); + } + + var enumerator = enumerable.GetEnumerator(); + if (index == 0) + { + return enumerator.MoveNext() ? enumerator.Current : itemNotFound(index) ; + } + + for (var myIndex = 1; myIndex <= index; myIndex++) + { + if (!enumerator.MoveNext()) + { + return itemNotFound(index); + } + } + + return enumerator.Current; + } + + /// + /// Returns true if all items in the itemEnum are contained in referenceCollection + /// + /// + /// + /// + /// + + public static bool ContainsAll( this ICollection referenceCollection, IEnumerable itemEnum) + { + foreach (T item in itemEnum) + { + if (!referenceCollection.Contains(item)) + { + return false; + } + } + + return true; + } + + /// + /// Advances the specified enumerator. + /// + /// + /// The enumerator. + /// + public static T Advance( this IEnumerator enumerator ) + { + enumerator.MoveNext(); + return enumerator.Current; + } + + public static bool DeepUnorderedEquals( this IList pthis, IList pthat ) + { + if (pthis.Count != pthat.Count) + { + return false; + } + + for( int ii = pthis.Count - 1 ; ii >= 0 ; ii-- ) { + if (!pthat.Contains(pthis[ii])) return false; + } + + return true; + } + + + /// + /// Does a deep equality test on a set of lists. Order is assumed to be the same. + /// + /// + /// The pthis. + /// The pthat. + /// + public static bool DeepEquals( this IList pthis, IList pthat ) + { + if (pthis == null && pthat == null) + return true; + if (pthis == null) + return pthat.Count == 0; + if (pthat == null) + return pthis.Count == 0; + + if (pthis.Count != pthat.Count) + { + return false; + } + + IEnumerator thisEnum = pthis.GetEnumerator(); + IEnumerator thatEnum = pthat.GetEnumerator(); + + while( true ) + { + var thisHasMore = thisEnum.MoveNext(); + var thatHasMore = thatEnum.MoveNext(); + if (!thisHasMore || !thatHasMore) + { + return !thisHasMore && !thatHasMore; + } + + if (! Equals(thisEnum.Current, thatEnum.Current)) + { + return false; + } + } + } + + /// + /// Determines whether the specified collection in the parameter is null or + /// empty. + /// + /// + /// The parameter. + /// + /// true if [is empty or null] [the specified parameter]; otherwise, false. + /// + public static bool IsEmptyOrNull(this ICollection parameter) + { + return + (parameter == null) || + (parameter.Count == 0); + } + + /// + /// Determines whether the specified collection in the parameter is empty. + /// + /// + /// The parameter. + /// + /// true if the specified parameter is empty; otherwise, false. + /// + public static bool IsEmpty(this ICollection parameter) + { + return parameter.Count == 0; + } + + /// + /// Determines whether the specified collection in the parameter is empty. + /// + /// The parameter. + /// + /// true if the specified parameter is empty; otherwise, false. + /// + public static bool IsEmptyCollection(this ICollection parameter) + { + return parameter.Count == 0; + } + + /// + /// Determines whether the specified collection in the parameter is not empty. + /// + /// + /// The parameter. + /// + /// true if the specified parameter is empty; otherwise, false. + /// + public static bool IsNotEmpty(this ICollection parameter) + { + return parameter.Count != 0; + } + + /// + /// Adds all of the items in the source. + /// + /// + /// The pthis. + /// The source. + + public static void AddAll(this ICollection pthis, IEnumerable source) + { + if (source is IList) + { + var asList = (IList)source; + var asListCount = asList.Count; + for (int ii = 0; ii < asListCount; ii++) + { + pthis.Add(asList[ii]); + } + } + else if (source != null) + { + foreach (T value in source) + { + pthis.Add(value); + } + } + } + + public static IEnumerable> Merge(this IEnumerable enumA, IEnumerable enumB) + { + if ((enumA != null) && (enumB != null)) + { + var enA = enumA.GetEnumerator(); + var enB = enumB.GetEnumerator(); + while (enA.MoveNext() && enB.MoveNext()) + { + yield return new Tuple + { + A = enA.Current, B = enB.Current + }; + } + } + } + + public static bool HasFirst(this IEnumerable pthis) + { + IEnumerator tableEnum = pthis.GetEnumerator(); + return tableEnum != null && tableEnum.MoveNext(); + } + + /// + /// Returns the second item in the set + /// + /// + + public static T Second(this IEnumerable pthis) + { + IEnumerator tableEnum = pthis.GetEnumerator(); + tableEnum.MoveNext(); + tableEnum.MoveNext(); + return tableEnum.Current; + } + + public static void RemoveWhere(this LinkedList list, Func where) + { + for (var curr = list.First; curr != null; ) + { + var next = curr.Next; + if (where.Invoke(curr.Value)) + { + list.Remove(curr); + } + + curr = next; + } + } + + public static void RemoveWhere(this IList list, Func where) + { + for (int ii = 0; ii < list.Count;) + { + var testItem = where.Invoke(list[ii]); + if (testItem) + { + list.RemoveAt(ii); + } + else + { + ii++; + } + } + } + + public static int RemoveWhere(this IList list, Func where, Action collector) + { + var count = 0; + + for (int ii = 0; ii < list.Count; ) + { + var testItem = where.Invoke(list[ii]); + if (testItem) + { + count++; + collector.Invoke(list[ii]); + list.RemoveAt(ii); + } + else + { + ii++; + } + } + + return count; + } + + /// + /// Removes all items. + /// + /// + /// The collection. + /// The items. + + public static void RemoveAll(this ICollection collection, IEnumerable items) + { + foreach (T item in items) + { + while (collection.Remove(item)) + { + } + } + } + + /// + /// Retains all items in the passed enumerable. + /// + /// + /// The collection. + /// The items. + public static void RetainAll(this ICollection collection, IEnumerable items) + { + foreach (T item in items.Where(item => !collection.Contains(item))) + { + collection.Remove(item); + } + } + + /// + /// Primitive reversal of a collection + /// + /// + /// + + public static void Reverse(this ICollection collection) + { + List tempList = collection as List; + if (tempList != null) + { + tempList.Reverse(); + return; + } + + tempList = new List(); + tempList.AddRange(collection); + tempList.Reverse(); + + collection.Clear(); + + foreach (T item in tempList) + { + collection.Add(item); + } + } + + public static double Render(object p) + { + throw new NotImplementedException(); + } + + public static string Render(this IEnumerable> source) + { + string fieldDelimiter = String.Empty; + + var builder = new StringBuilder(); + builder.Append('['); + + if (source != null) + { + foreach(var current in source) + { + builder.Append(fieldDelimiter); + builder.Append(RenderAny(current.Key)); + builder.Append('='); + if (ReferenceEquals(current.Value, null)) + builder.Append("null"); + else if (current.Value.GetType().IsGenericDictionary()) + builder.Append(MagicMarker.NewMagicDictionary(current.Value)); + else if (current.Value is string) + builder.Append(RenderAny(current.Value)); + else if (current.Value is IEnumerable) + builder.Append(Render((IEnumerable)current.Value)); + else + builder.Append(RenderAny(current.Value)); + fieldDelimiter = ", "; + } + } + + builder.Append(']'); + return builder.ToString(); + } + + /// + /// Renders an enumerable source + /// + /// + /// + + public static string Render(this IEnumerable source) + { + string fieldDelimiter = String.Empty; + + var builder = new StringBuilder(); + if (source != null) + { + builder.Append('['); + + IEnumerator sourceEnum = source.GetEnumerator(); + while (sourceEnum.MoveNext()) { + builder.Append(fieldDelimiter); + builder.Append(RenderAny(sourceEnum.Current)); + fieldDelimiter = ", "; + } + + builder.Append(']'); + } + else + { + builder.Append("null"); + } + + return builder.ToString(); + } + + /// + /// Renders an enumerable source + /// + /// The source. + /// The render engine. + /// + + public static string Render(this IEnumerable source, Func renderEngine) + { + string fieldDelimiter = String.Empty; + + StringBuilder builder = new StringBuilder(); + builder.Append('['); + + IEnumerator sourceEnum = source.GetEnumerator(); + while (sourceEnum.MoveNext()) + { + builder.Append(fieldDelimiter); + builder.Append(renderEngine(sourceEnum.Current)); + fieldDelimiter = ", "; + } + + builder.Append(']'); + return builder.ToString(); + } + + /// + /// Removes the item at the specified index from the list and + /// returns the item. + /// + /// + /// The list. + /// The index. + /// + + public static V DeleteAt(this IList list, int index) + { + V tempItem = list[index]; + list.RemoveAt(index); + return tempItem; + } + + /// + /// Removes the item at the front of the list and returns it. + /// + /// + /// The list. + /// + + public static V PopFront(this LinkedList list) + { + V tempItem = list.First.Value; + list.RemoveFirst(); + return tempItem; + } + + public static V Poll(this LinkedList list, V defaultValue) + { + if (list.First == null) + return defaultValue; + + V tempItem = list.First.Value; + list.RemoveFirst(); + return tempItem; + } + + public static V Poll(this LinkedList list) + { + return Poll(list, default(V)); + } + + + public static V Poll(this IList list, V defaultValue) + { + if (list.Count == 0) + return defaultValue; + + V tempItem = list[0]; + list.RemoveAt(0); + return tempItem; + } + + public static V Poll(this IList list) + { + return Poll(list, default(V)); + } + + public static LinkedListNode FirstNode(this LinkedList list, Func whereClause) + { + for(var curr = list.First; curr != null; curr = curr.Next) + { + if (whereClause(curr.Value)) + { + return curr; + } + } + + return null; + } + + + public static String[] ToUpper( this string[] inArray ) + { + string[] outArray = new string[inArray.Length]; + for( int ii = 0 ; ii < inArray.Length; ii++ ) { + var inItem = inArray[ii]; + if (inItem != null) { + outArray[ii] = inItem.ToUpper(); + } + } + + return outArray; + } + + public static Type[] GetParameterTypes(this MethodInfo method) + { + return method.GetParameters().Select(p => p.ParameterType).ToArray(); + } + + public static Type[] GetParameterTypes(this ConstructorInfo ctor) + { + return ctor.GetParameters().Select(p => p.ParameterType).ToArray(); + } + + public static IEnumerable XRange(int lowerBound, int upperBound) + { + for( int ii = lowerBound ; ii < upperBound ; ii++ ) + { + yield return ii; + } + } + + public static bool CanUnwrap(this object value) + { + if (value == null) + return true; + if (value is ICollection) + return true; + if (value is IEnumerable) + return true; + if (value is IEnumerable) + return true; + if (value is IEnumerator) + return true; + + return false; + } + + public static T[] UnwrapIntoArray(this object value, bool includeNullValues = true) + { + if (value == null) + return null; + if (value is T[]) + return (T[]) value; + + return Unwrap(value, includeNullValues).ToArray(); + } + + public static IList UnwrapIntoList(this object value, bool includeNullValues = true) + { + if (value == null) + return null; + if (value is IList) + return ((IList) value); + + return Unwrap(value, includeNullValues).ToList(); + } + + public static ICollection UnwrapSafe(this object value, bool includeNullValues = false) + { + if (value == null) + return null; + if (value is ICollection) + return ((ICollection)value); + + return UnwrapEnumerable(value, includeNullValues).ToArray(); + } + + public static ICollection Unwrap(this object value, bool includeNullValues = false) + { + if (value == null) + return null; + if (value is ICollection) + return ((ICollection) value); + + return UnwrapEnumerable(value, includeNullValues).ToArray(); + } + + public static IEnumerable UnwrapEnumerable( + this object value, + bool includeNullValues = false) + { + if (value == null) + return null; + if (value is IEnumerable) + return ((IEnumerable)value); + + if (value is IEnumerable) + { + IEnumerable expression = ((IEnumerable)value); + if (includeNullValues) + { + expression = expression.Where(o => o == null || o is T); + } + else + { + expression = expression.Where(o => o != null && o is T); + } + + return expression.Cast(); + } + + if (value is IEnumerable) + { + IEnumerable expression = ((IEnumerable)value).Cast(); + if (includeNullValues) + { + expression = expression.Where(o => o == null || o is T); + } + else + { + expression = expression.Where(o => o != null && o is T); + } + + return expression.Cast(); + } + + if (value is IEnumerator) + { + var result = new List(); + var enumerator = (IEnumerator)value; + while (enumerator.MoveNext()) + { + var current = enumerator.Current; + if (includeNullValues) + { + if (current is T) + { + result.Add((T) current); + } + } + else if ((current != null) && (current is T)) + { + result.Add((T) current); + } + } + + return result; + } + + throw new ArgumentException("invalid value"); + } + + public static IDictionary UnwrapDictionary(this object value) + { + if (value == null) + return null; + if (value is IDictionary) + return (IDictionary) value; + + var valueType = value.GetType(); + if (valueType.IsGenericStringDictionary()) + { + return MagicMarker.GetStringDictionaryFactory(valueType).Invoke(value); + } + + if (value is IEnumerable>) + { + var valueDataMap = new Dictionary(); + foreach (var valueKeyValuePair in ((IEnumerable>) value)) + { + valueDataMap[valueKeyValuePair.Key] = valueKeyValuePair.Value; + } + + return valueDataMap; + } + + if (value is KeyValuePair) + { + var valueDataMap = new Dictionary(); + var valueKeyValuePair = (KeyValuePair) value; + valueDataMap[valueKeyValuePair.Key] = valueKeyValuePair.Value; + return valueDataMap; + } + + throw new ArgumentException("unable to convert input to string dictionary"); + } + + public static string RenderAny(this object value) + { + if (value == null) + { + return "null"; + } + + if (value is string) + { + return (string) value; + } + + if ((value is decimal) || + (value is double) || + (value is float)) + { + var text = value.ToString(); + if (text.IndexOf('.') == -1) + { + text += ".0"; + } + + return text; + } + + if (value is DateTimeOffset) + { + var dateTime = (DateTimeOffset)value; + var dateOnly = dateTime.Date; + if (dateTime == dateOnly) + { + return dateTime.ToString("yyyy-MM-dd z"); + } + else if (dateTime.Millisecond == 0) + { + return dateTime.ToString("yyyy-MM-dd hh:mm:ss z"); + } + else + { + return dateTime.ToString("yyyy-MM-dd hh:mm:ss.ffff z"); + } + } + + if (value is DateTime) + { + var dateTime = (DateTime) value; + var dateOnly = dateTime.Date; + if (dateTime == dateOnly) + { + return dateTime.ToString("yyyy-MM-dd"); + } + else if (dateTime.Millisecond == 0) + { + return dateTime.ToString("yyyy-MM-dd hh:mm:ss"); + } + else + { + return dateTime.ToString("yyyy-MM-dd hh:mm:ss.ffff"); + } + } + + if (value is Array) + { + return Render(value as Array); + } + + if (value.GetType().GetCustomAttributes(typeof (RenderWithToStringAttribute), true).Length > 0) + { + return value.ToString(); + } + + if (value is IEnumerable) + { + return Render((IEnumerable) value); + } + + return value.ToString(); + } + + /// + /// Renders the array as a string. + /// + /// The array. + /// + public static String Render(this Array array) + { + return Render(array, ", ", "[]"); + } + + /// + /// Renders the array as a string + /// + /// The array. + /// The item separator. + /// The first and last. + /// + + public static String Render(this Array array, String itemSeparator, String firstAndLast) + { + string fieldDelimiter = String.Empty; + + StringBuilder builder = new StringBuilder(); + builder.Append(firstAndLast[0]); + + if (array != null) + { + int length = array.Length; + for (int ii = 0; ii < length; ii++) + { + builder.Append(fieldDelimiter); + builder.Append(RenderAny(array.GetValue(ii))); + fieldDelimiter = itemSeparator; + } + } + + builder.Append(firstAndLast[1]); + return builder.ToString(); + } + + public static String FormatInt(this int? value) + { + return value.HasValue ? value.Value.ToString(CultureInfo.CurrentCulture) : "null"; + } + + /// + /// Returns a view into a subsection of the list. Since the resulting list is a shallow view, care + /// should be taken to ensure that the underlying list is not adversely modified while using the + /// view. + /// + /// + /// The list. + /// From index. + /// To index. + /// + public static IList SubList(this IList list, int fromIndex, int toIndex) + { + if (toIndex >= list.Count) + toIndex = list.Count; + + return new SubList(list, fromIndex, toIndex); + } + + /// + /// Appends one or more items to the source. + /// + /// + /// The source. + /// The more. + /// + public static IEnumerable Append(this IEnumerable source, params T[] more) + { + foreach (T item in source) + yield return item; + foreach (T item in more) + yield return item; + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/collections/CopyOnWriteArraySet.cs b/NEsper.Core/NEsper.Core/compat/collections/CopyOnWriteArraySet.cs new file mode 100755 index 000000000..1496f8ac9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/collections/CopyOnWriteArraySet.cs @@ -0,0 +1,83 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +namespace com.espertech.esper.compat.collections +{ + public sealed class CopyOnWriteArraySet : CopyOnWriteList + { + #region ICollection Members + + /// + /// Adds all of the items in the source. + /// + /// The source. + public void AddAll(IEnumerable source) + { + List tempList = new List(); + + using( WriteLock.Acquire() ) + { + foreach (T item in source) + { + if (! Contains(item)) + { + tempList.Add(item); + } + } + + if (tempList.Count != 0) + { + AddRange(tempList); + } + } + } + + /// + /// Returns the first item in the set + /// + /// + /// + public T First + { + get { return this[0]; } + } + + /// + /// Gets a value indicating whether this instance is empty. + /// + /// true if this instance is empty; otherwise, false. + public bool IsEmpty + { + get { return Count == 0; } + } + + #endregion + + #region ICollection Members + + /// + /// Adds an item to the . + /// + /// The object to add to the . + /// The is read-only. + public override void Add(T item) + { + using (WriteLock.Acquire()) + { + if (!Contains( item )) + { + base.Add(item); + } + } + } + + #endregion + } +} diff --git a/NEsper.Core/NEsper.Core/compat/collections/CopyOnWriteList.cs b/NEsper.Core/NEsper.Core/compat/collections/CopyOnWriteList.cs new file mode 100755 index 000000000..61d947a52 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/collections/CopyOnWriteList.cs @@ -0,0 +1,303 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using com.espertech.esper.compat.threading; + +namespace com.espertech.esper.compat.collections +{ + public class CopyOnWriteList : IList + { + private T[] _arrayList; + private readonly ILockable _writerLock; + + /// + /// Initializes a new instance of the class. + /// + public CopyOnWriteList() + { + _arrayList = new T[0]; + _writerLock = LockManager.CreateLock(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + } + + /// + /// Gets the write lock. + /// + /// The write lock. + public ILockable WriteLock + { + get { return _writerLock; } + } + + /// + /// Converts the list to an array. + /// + /// + public T[] ToArray() + { + return _arrayList; + } + + /// + /// Iterates over each item in the list executing the specified + /// action. + /// + /// The action. + public void ForEach(Action action) + { + var list = _arrayList; + var length = list.Length; + for (int ii = 0; ii < length; ii++ ) + { + action.Invoke(list[ii]); + } + } + + #region ICollection Members + + /// + /// Adds an item to the . + /// + /// The object to add to the . + /// The is read-only. + public virtual void Add(T item) + { + using (_writerLock.Acquire()) + { + List tempList = new List(_arrayList); + tempList.Add(item); + _arrayList = tempList.ToArray(); + } + } + + /// + /// Adds the range. + /// + /// The item list. + public virtual void AddRange(IEnumerable itemList) + { + using (_writerLock.Acquire()) + { + List tempList = new List(_arrayList); + tempList.AddRange(itemList); + _arrayList = tempList.ToArray(); + } + } + + /// + /// Removes all items from the . + /// + /// The is read-only. + public virtual void Clear() + { + using (_writerLock.Acquire()) + { + _arrayList = new T[0]; + } + } + + /// + /// Determines whether the contains a specific value. + /// + /// The object to locate in the . + /// + /// true if item is found in the ; otherwise, false. + /// + public virtual bool Contains(T item) + { + return _arrayList.Contains(item); + } + + /// + /// Copies the elements of the to an , starting at a particular index. + /// + /// The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing. + /// The zero-based index in array at which copying begins. + /// arrayIndex is less than 0. + /// array is null. + /// array is multidimensional.-or-arrayIndex is equal to or greater than the length of array.-or-The number of elements in the source is greater than the available space from arrayIndex to the end of the destination array.-or-Type T cannot be cast automatically to the type of the destination array. + public virtual void CopyTo(T[] array, int arrayIndex) + { + _arrayList.CopyTo(array, arrayIndex); + } + + /// + /// Gets the number of elements contained in the . + /// + /// + /// The number of elements contained in the . + public virtual int Count + { + get { return _arrayList.Length; } + } + + /// + /// Gets a value indicating whether the is read-only. + /// + /// + /// true if the is read-only; otherwise, false. + public virtual bool IsReadOnly + { + get { return false; } + } + + /// + /// Removes the first occurrence of a specific object from the . + /// + /// The object to remove from the . + /// + /// true if item was successfully removed from the ; otherwise, false. This method also returns false if item is not found in the original . + /// + /// The is read-only. + public bool Remove(T item) + { + bool result; + + using (_writerLock.Acquire()) + { + List tempList = new List(_arrayList); + result = tempList.Remove(item); + if (result) + { + _arrayList = tempList.ToArray(); + } + } + + return result; + } + + /// + /// Removes all items. + /// + /// + public void RemoveAll(IEnumerable items) + { + using (_writerLock.Acquire()) + { + List tempList = new List(_arrayList); + foreach (T item in items) + { + tempList.Remove(item); + } + _arrayList = tempList.ToArray(); + } + } + + #endregion + + #region IEnumerable Members + + /// + /// Returns an enumerator that iterates through the collection. + /// + /// + /// A that can be used to iterate through the collection. + /// + public IEnumerator GetEnumerator() + { + var list = _arrayList; + var length = list.Length; + for (int ii = 0; ii < length; ii++) + { + yield return list[ii]; + } + } + + #endregion + + #region IEnumerable Members + + /// + /// Returns an enumerator that iterates through a collection. + /// + /// + /// An object that can be used to iterate through the collection. + /// + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return _arrayList.GetEnumerator(); + } + + #endregion + + #region IList Members + + /// + /// Determines the index of a specific item in the . + /// + /// The object to locate in the . + /// + /// The index of item if found in the list; otherwise, -1. + /// + public int IndexOf(T item) + { + return Array.IndexOf(_arrayList, item); + } + + /// + /// Inserts an item to the at the specified index. + /// + /// The zero-based index at which item should be inserted. + /// The object to insert into the . + /// The is read-only. + /// index is not a valid index in the . + public void Insert(int index, T item) + { + using (_writerLock.Acquire()) + { + List tempList = new List(_arrayList); + tempList.Insert(index, item); + _arrayList = tempList.ToArray(); + } + } + + /// + /// Removes the item at the specified index. + /// + /// The zero-based index of the item to remove. + /// The is read-only. + /// index is not a valid index in the . + public void RemoveAt(int index) + { + using (_writerLock.Acquire()) + { + List tempList = new List(_arrayList); + tempList.RemoveAt(index); + _arrayList = tempList.ToArray(); + } + } + + /// + /// Gets or sets the item at the specified index. + /// + /// + public T this[int index] + { + get + { + return _arrayList[index]; + } + set + { + // Should this be using copy-on-write semantics or + // is the set instruction essentially atomic? Let's + // not take any chances. + + using (_writerLock.Acquire()) + { + List tempList = new List(_arrayList); + tempList[index] = value; + _arrayList = tempList.ToArray(); + } + } + } + + #endregion + } +} diff --git a/NEsper.Core/NEsper.Core/compat/collections/DebugDictionary.cs b/NEsper.Core/NEsper.Core/compat/collections/DebugDictionary.cs new file mode 100755 index 000000000..857d40533 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/collections/DebugDictionary.cs @@ -0,0 +1,195 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Threading; + +namespace com.espertech.esper.compat.collections +{ + public class DebugDictionary : IDictionary + { + private readonly Guid _id; + private readonly IDictionary _subDictionary; + + /// + /// Initializes a new instance of the class. + /// + /// The sub dictionary. + public DebugDictionary(IDictionary subDictionary) + { + _id = Guid.NewGuid(); + _subDictionary = subDictionary; + } + + /// + /// Returns an enumerator that iterates through a collection. + /// + /// + /// An object that can be used to iterate through the collection. + /// + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + /// + /// Returns an enumerator that iterates through the collection. + /// + /// + /// A that can be used to iterate through the collection. + /// + public IEnumerator> GetEnumerator() + { + return _subDictionary.GetEnumerator(); + } + + /// + /// Adds an item to the . + /// + /// The object to add to the . + public void Add(KeyValuePair item) + { + Console.WriteLine(" ~~> A1: {0} | {1} | {2}", _id, Thread.CurrentThread.ManagedThreadId, item.Key.GetHashCode()); + _subDictionary.Add(item); + } + + /// + /// Removes all items from the . + /// + public void Clear() + { + Console.WriteLine(" ~~> C1: {0} | {1}", _id, Thread.CurrentThread.ManagedThreadId); + _subDictionary.Clear(); + } + + /// + /// Determines whether the contains a specific value. + /// + /// The object to locate in the . + /// + /// true if is found in the ; otherwise, false. + /// + public bool Contains(KeyValuePair item) + { + return _subDictionary.Contains(item); + } + + /// + /// Copies to. + /// + /// The array. + /// Index of the array. + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + _subDictionary.CopyTo(array, arrayIndex); + } + + /// + /// Removes the first occurrence of a specific object from the . + /// + /// The object to remove from the . + /// + /// true if was successfully removed from the ; otherwise, false. This method also returns false if is not found in the original . + /// + public bool Remove(KeyValuePair item) + { + Console.WriteLine(" ~~> R1: {0} | {1} | {2}", _id, Thread.CurrentThread.ManagedThreadId, item.Key.GetHashCode()); + return _subDictionary.Remove(item); + } + + /// + /// Gets the number of elements contained in the . + /// + public int Count + { + get { return _subDictionary.Count; } + } + + /// + /// Gets a value indicating whether the is read-only. + /// + public bool IsReadOnly + { + get { return false; } + } + + /// + /// Determines whether the contains an element with the specified key. + /// + /// The key to locate in the . + /// + /// true if the contains an element with the key; otherwise, false. + /// + public bool ContainsKey(K key) + { + return _subDictionary.ContainsKey(key); + } + + /// + /// Adds an element with the provided key and value to the . + /// + /// The object to use as the key of the element to add. + /// The object to use as the value of the element to add. + public void Add(K key, V value) + { + Console.WriteLine(" ~~> A2: {0} | {1} | {2}", _id, Thread.CurrentThread.ManagedThreadId, key.GetHashCode()); + _subDictionary.Add(key, value); + } + + /// + /// Removes the element with the specified key from the . + /// + /// The key of the element to remove. + /// + /// true if the element is successfully removed; otherwise, false. This method also returns false if was not found in the original . + /// + public bool Remove(K key) + { + Console.WriteLine(" ~~> R2: {0} | {1} | {2}", _id, Thread.CurrentThread.ManagedThreadId, key.GetHashCode()); + return _subDictionary.Remove(key); + } + + /// + /// Gets the value associated with the specified key. + /// + /// The key whose value to get. + /// When this method returns, the value associated with the specified key, if the key is found; otherwise, the default value for the type of the parameter. This parameter is passed uninitialized. + /// + /// true if the object that : contains an element with the specified key; otherwise, false. + /// + public bool TryGetValue(K key, out V value) + { + Console.WriteLine(" ~~> G1: {0} | {1} | {2}", _id, Thread.CurrentThread.ManagedThreadId, key.GetHashCode()); + return _subDictionary.TryGetValue(key, out value); + } + + public V this[K key] + { + get + { + Console.WriteLine(" ~~> G2: {0} | {1} | {2}", _id, Thread.CurrentThread.ManagedThreadId, key.GetHashCode()); + return _subDictionary[key]; + } + set + { + Console.WriteLine(" ~~> S1: {0} | {1} | {2}", _id, Thread.CurrentThread.ManagedThreadId, key.GetHashCode()); + _subDictionary[key] = value; + } + } + + /// + /// Gets an containing the values in the . + /// + public ICollection Values + { + get { return _subDictionary.Values; } + } + + /// + /// Gets an containing the keys of the . + /// + public ICollection Keys + { + get { return _subDictionary.Keys; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/collections/DebugList.cs b/NEsper.Core/NEsper.Core/compat/collections/DebugList.cs new file mode 100755 index 000000000..fbfb6a85b --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/collections/DebugList.cs @@ -0,0 +1,233 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; + +namespace com.espertech.esper.compat.collections +{ + /// + /// Used to debug calls to a list. + /// + /// + public class DebugList : IList + { + private readonly Guid m_id; + private readonly IList m_subList; + + /// + /// Gets the id. + /// + /// The id. + public Guid Id + { + get { return m_id; } + } + + /// + ///Determines the index of a specific item in the . + /// + /// + /// + ///The index of item if found in the list; otherwise, -1. + /// + /// + ///The object to locate in the . + public int IndexOf(T item) + { + return m_subList.IndexOf(item); + } + + /// + ///Inserts an item to the at the specified index. + /// + /// + ///The object to insert into the . + ///The zero-based index at which item should be inserted. + ///The is read-only. + ///index is not a valid index in the . + public void Insert(int index, T item) + { + Debug.WriteLine(" > " + m_id + " > Insert"); + m_subList.Insert(index, item); + } + + /// + ///Removes the item at the specified index. + /// + /// + ///The zero-based index of the item to remove. + ///The is read-only. + ///index is not a valid index in the . + public void RemoveAt(int index) + { + Debug.WriteLine(" > " + m_id + " > RemoveAt"); + m_subList.RemoveAt(index); + } + + /// + ///Gets or sets the element at the specified index. + /// + /// + /// + ///The element at the specified index. + /// + /// + ///The zero-based index of the element to get or set. + ///index is not a valid index in the . + ///The property is set and the is read-only. + public T this[int index] + { + get { return m_subList[index]; } + set { m_subList[index] = value; } + } + + /// + ///Adds an item to the . + /// + /// + ///The object to add to the . + ///The is read-only. + public void Add(T item) + { + Debug.WriteLine(" > " + m_id + " > Add"); + m_subList.Add(item); + } + + /// + ///Removes all items from the . + /// + /// + ///The is read-only. + public void Clear() + { + Debug.WriteLine(" > " + m_id + " > Clear"); + m_subList.Clear(); + } + + /// + ///Determines whether the contains a specific value. + /// + /// + /// + ///true if item is found in the ; otherwise, false. + /// + /// + ///The object to locate in the . + public bool Contains(T item) + { + return m_subList.Contains(item); + } + + /// + ///Copies the elements of the to an , starting at a particular index. + /// + /// + ///The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing. + ///The zero-based index in array at which copying begins. + ///arrayIndex is less than 0. + ///array is null. + ///array is multidimensional.-or-arrayIndex is equal to or greater than the length of array.-or-The number of elements in the source is greater than the available space from arrayIndex to the end of the destination array.-or-Type T cannot be cast automatically to the type of the destination array. + public void CopyTo(T[] array, int arrayIndex) + { + m_subList.CopyTo(array, arrayIndex); + } + + /// + ///Removes the first occurrence of a specific object from the . + /// + /// + /// + ///true if item was successfully removed from the ; otherwise, false. This method also returns false if item is not found in the original . + /// + /// + ///The object to remove from the . + ///The is read-only. + public bool Remove(T item) + { + Debug.WriteLine(" > " + m_id + " > Remove"); + return m_subList.Remove(item); + } + + /// + ///Gets the number of elements contained in the . + /// + /// + /// + ///The number of elements contained in the . + /// + /// + public int Count + { + get { return m_subList.Count; } + } + + /// + ///Gets a value indicating whether the is read-only. + /// + /// + /// + ///true if the is read-only; otherwise, false. + /// + /// + public bool IsReadOnly + { + get { return m_subList.IsReadOnly; } + } + + /// + ///Returns an enumerator that iterates through the collection. + /// + /// + /// + ///A that can be used to iterate through the collection. + /// + ///1 + IEnumerator IEnumerable.GetEnumerator() + { + return m_subList.GetEnumerator(); + } + + /// + ///Returns an enumerator that iterates through a collection. + /// + /// + /// + ///An object that can be used to iterate through the collection. + /// + ///2 + public IEnumerator GetEnumerator() + { + return ((IEnumerable) this).GetEnumerator(); + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override string ToString() + { + return "DebugList{" + m_id + "}"; + } + + /// + /// Initializes a new instance of the class. + /// + /// The sub list. + public DebugList(IList subList) + { + m_id = Guid.NewGuid(); + m_subList = subList; + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/collections/Deque.cs b/NEsper.Core/NEsper.Core/compat/collections/Deque.cs new file mode 100755 index 000000000..67c67eda3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/collections/Deque.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.compat.collections +{ + public interface Deque : ICollection, IVisitable + { + void AddLast(T value); + void AddFirst(T value); + + T RemoveFirst(); + T RemoveLast(); + + /// + /// Retrieves and removes the head of the queue represented by this deque or returns null if deque is empty. + /// + /// + T Poll(); + + /// + /// Retrieves, but does not remove, the head of the queue represented by this deque, or returns null if this deque is empty. + /// + /// + T Peek(); + + T First { get; } + T Last { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/collections/DictionaryExtensions.cs b/NEsper.Core/NEsper.Core/compat/collections/DictionaryExtensions.cs new file mode 100755 index 000000000..29f997936 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/collections/DictionaryExtensions.cs @@ -0,0 +1,309 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; + +using XLR8.CGLib; + +namespace com.espertech.esper.compat.collections +{ + public static class DictionaryExtensions + { + public static IDictionary AsSyncDictionary(this IDictionary dictionary) + where TK : class + { + if (dictionary is ConcurrentDictionary) + { + return dictionary; + } + + return new ConcurrentDictionary(dictionary); + } + + public static IDictionary WithDebugSupport(this IDictionary dictionary) + where TK : class + { + if (dictionary is DebugDictionary) + { + return dictionary; + } + + return new DebugDictionary(dictionary); + } + + + public static IDictionary WithNullSupport(this IDictionary dictionary) + where TK : class + { + if (dictionary is NullableDictionary) + { + return dictionary; + } + + return new NullableDictionary(dictionary); + } + + public static IDictionary WithValueTypeSupport(this IDictionary dictionary) + where TK : struct + { + if (dictionary is NullableValueTypeDictionary) + { + return dictionary; + } + + return new NullableValueTypeDictionary(dictionary); + } + + private static readonly IDictionary SafeDictionaryMethodTable = + new Dictionary(ReferenceEqualityComparer.Default); + + public static IDictionary WithSafeSupport(this IDictionary dictionary) + { + FastMethod fastMethod; + + var type = typeof(IDictionary); + + if (typeof(TK).IsNullable()) + { + var keyType = Nullable.GetUnderlyingType(typeof (TK)); + + lock (SafeDictionaryMethodTable) + { + if (!SafeDictionaryMethodTable.TryGetValue(type, out fastMethod)) + { + var slowMethod = typeof(DictionaryExtensions) + .GetMethod("WithValueTypeSupport") + .MakeGenericMethod(keyType, typeof(TV)); + fastMethod = FastClass.CreateMethod(slowMethod); + SafeDictionaryMethodTable[type] = fastMethod; + } + } + + return (IDictionary)fastMethod.InvokeStatic(dictionary, null); + } + + if (typeof(TK).IsValueType) + return dictionary; // this dictionary does not have the possibility of null entries + + lock (SafeDictionaryMethodTable) + { + if (!SafeDictionaryMethodTable.TryGetValue(type, out fastMethod)) + { + var slowMethod = typeof (DictionaryExtensions) + .GetMethod("WithNullSupport") + .MakeGenericMethod(typeof (TK), typeof (TV)); + fastMethod = FastClass.CreateMethod(slowMethod); + SafeDictionaryMethodTable[type] = fastMethod; + } + } + + return (IDictionary) fastMethod.InvokeStatic(dictionary, null); + } + + /// + /// Transforms the specified dictionary. + /// + /// The dictionary. + /// The key out. + /// The value out. + /// The key in. + /// The value in. + /// + public static IDictionary Transform( + this IDictionary dictionary, + Func keyIntToExt, + Func valueIntToExt, + Func keyExtToInt, + Func valueExtToInt) + { + return new TransformDictionary( + dictionary, + keyIntToExt, + keyExtToInt, + valueIntToExt, + valueExtToInt); + } + + /// + /// Removes the item from the dictionary that is associated with + /// the specified key. Returns the value that was found at that + /// location and removed or the defaultValue. + /// + /// + /// + /// The dictionary. + /// Search key into the dictionary + /// The value removed from the dictionary (if found). + /// + + public static bool Remove(this IDictionary dictionary, K key, out V value) + { + dictionary.TryGetValue(key, out value); + return dictionary.Remove(key); + } + + /// + /// Removes the item from the dictionary that is associated with + /// the specified key. The item if found is returned; if not, + /// default(V) is returned. + /// + /// + /// + /// The dictionary. + /// The key. + /// + + public static V Delete(this IDictionary dictionary, K key) + { + V tempItem; + + return dictionary.Remove(key, out tempItem) + ? tempItem + : default(V); + } + + /// + /// Fetches the value associated with the specified key. + /// If no value can be found, then the defaultValue is + /// returned. + /// + /// + /// + /// The dictionary. + /// The key. + /// The default value. + /// + + public static V Get(this IDictionary dictionary, K key, V defaultValue) + { + V returnValue; + if (!dictionary.TryGetValue(key, out returnValue)) + { + returnValue = defaultValue; + } + + return returnValue; + } + + /// + /// Fetches the value associated with the specified key. + /// If no value can be found, then default(V) is returned. + /// + /// + /// + /// The dictionary. + /// The key. + /// + + public static V Get(this IDictionary dictionary, K key) + { + return Get(dictionary, key, default(V)); + } + + /// + /// Sets the given key in the dictionary. If the key + /// already exists, then it is remapped to thenew value. + /// + /// + /// + /// The dictionary. + /// The key. + /// The value. + + public static void Put(this IDictionary dictionary, K key, V value) + { + dictionary[key] = value; + } + + /// + /// Sets the given key in the dictionary. If the key + /// already exists, then it is remapped to the new value. + /// If a value was previously mapped it is returned. + /// + /// + /// + /// The dictionary. + /// The key. + /// The value. + /// + + public static V Push(this IDictionary dictionary, K key, V value) + { + V temp; + dictionary.TryGetValue(key, out temp); + dictionary[key] = value; + return temp; + } + + /// + /// Puts all values from the source dictionary into + /// this dictionary. + /// + /// + /// + /// The dictionary. + /// The source. + + public static void PutAll(this IDictionary dictionary, IEnumerable> source) + { + foreach (KeyValuePair kvPair in source) + { + dictionary[kvPair.Key] = kvPair.Value; + } + } + + /// + /// Puts all values from the source dictionary into this dictionary. This variation + /// of the method allows the values to be transformed from one type to another. + /// + /// + /// + /// + /// The dictionary. + /// The source. + /// The transformer. + + public static void PutAll(this IDictionary dictionary, IEnumerable> source, Transformer transformer) + { + foreach (KeyValuePair kvPair in source) + { + dictionary[kvPair.Key] = transformer(kvPair.Value); + } + } + + /// + /// Returns the first value in the enumeration of values + /// + /// + /// + /// The dictionary. + /// + + public static V FirstValue(this IDictionary dictionary) + { + IEnumerator> kvPairEnum = dictionary.GetEnumerator(); + kvPairEnum.MoveNext(); + return kvPairEnum.Current.Value; + } + + public static SortedDictionary Invert(this SortedDictionary dictionary) + { + var comparer = dictionary.Comparer; + var inverted = new StandardComparer( + (a, b) => -comparer.Compare(a, b)); + return new SortedDictionary(dictionary, inverted); + } + + public static IDictionary AsBasicDictionary(this object anyEntity) + { + var asRawDictionary = anyEntity as Dictionary; + if (asRawDictionary != null) + return asRawDictionary; + + var asFuzzyDictionary = anyEntity as IDictionary; + if (asFuzzyDictionary != null) + return new Dictionary(asFuzzyDictionary); + + throw new ArgumentException("unable to translate dictionary", "anyEntity"); + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/collections/EmptyDictionary.cs b/NEsper.Core/NEsper.Core/compat/collections/EmptyDictionary.cs new file mode 100755 index 000000000..329cb5f7a --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/collections/EmptyDictionary.cs @@ -0,0 +1,227 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections; +using System.Collections.Generic; + +namespace com.espertech.esper.compat.collections +{ + [Serializable] + public class EmptyDictionary : IDictionary + { + public static readonly EmptyDictionary Instance = new EmptyDictionary(); + + /// + /// Determines whether the contains an element with the specified key. + /// + /// The key to locate in the . + /// + /// true if the contains an element with the key; otherwise, false. + /// + /// + /// is null. + /// + public bool ContainsKey(K key) + { + return false; + } + + /// + /// Adds an element with the provided key and value to the . + /// + /// The object to use as the key of the element to add. + /// The object to use as the value of the element to add. + /// + /// is null. + /// + /// + /// An element with the same key already exists in the . + /// + /// + /// The is read-only. + /// + public void Add(K key, V value) + { + throw new NotSupportedException(); + } + + /// + /// Removes the element with the specified key from the . + /// + /// The key of the element to remove. + /// + /// true if the element is successfully removed; otherwise, false. This method also returns false if was not found in the original . + /// + /// + /// is null. + /// + /// + /// The is read-only. + /// + public bool Remove(K key) + { + return false; + } + + /// + /// Gets the value associated with the specified key. + /// + /// The key whose value to get. + /// When this method returns, the value associated with the specified key, if the key is found; otherwise, the default value for the type of the parameter. This parameter is passed uninitialized. + /// + /// true if the object that : contains an element with the specified key; otherwise, false. + /// + /// + /// is null. + /// + public bool TryGetValue(K key, out V value) + { + value = default(V); + return false; + } + + /// + /// Gets or sets the value for the specified key. + /// + /// + public V this[K key] + { + get { throw new KeyNotFoundException(); } + set { throw new NotSupportedException(); } + } + + /// + /// Gets an containing the keys of the . + /// + /// + /// + /// An containing the keys of the object that : . + /// + public ICollection Keys + { + get { return new K[0]; } + } + + /// + /// Gets an containing the values in the . + /// + /// + /// + /// An containing the values in the object that : . + /// + public ICollection Values + { + get { return new V[0]; } + } + + /// + /// Returns an enumerator that iterates through a collection. + /// + /// + /// An object that can be used to iterate through the collection. + /// + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + /// + /// Returns an enumerator that iterates through the collection. + /// + /// + /// A that can be used to iterate through the collection. + /// + public IEnumerator> GetEnumerator() + { + IList> nullArray = new KeyValuePair[0]; + return nullArray.GetEnumerator(); + } + + /// + /// Adds an item to the . + /// + /// The object to add to the . + /// + /// The is read-only. + /// + public void Add(KeyValuePair item) + { + throw new NotSupportedException(); + } + + /// + /// Removes all items from the . + /// + /// + /// The is read-only. + /// + public void Clear() + { + } + + /// + /// Determines whether the contains a specific value. + /// + /// The object to locate in the . + /// + /// true if is found in the ; otherwise, false. + /// + public bool Contains(KeyValuePair item) + { + return false; + } + + /// + /// Copies the elements of the to an , starting at a particular index. + /// + /// The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing. + /// The zero-based index in at which copying begins. + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + } + + /// + /// Removes the first occurrence of a specific object from the . + /// + /// The object to remove from the . + /// + /// true if was successfully removed from the ; otherwise, false. This method also returns false if is not found in the original . + /// + /// + /// The is read-only. + /// + public bool Remove(KeyValuePair item) + { + return false; + } + + /// + /// Gets the number of elements contained in the . + /// + /// + /// + /// The number of elements contained in the . + /// + public int Count + { + get { return 0; } + } + + /// + /// Gets a value indicating whether the is read-only. + /// + /// + /// true if the is read-only; otherwise, false. + /// + public bool IsReadOnly + { + get { return true; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/collections/EmptyList.cs b/NEsper.Core/NEsper.Core/compat/collections/EmptyList.cs new file mode 100755 index 000000000..d647867b8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/collections/EmptyList.cs @@ -0,0 +1,86 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections; +using System.Collections.Generic; + +namespace com.espertech.esper.compat.collections +{ + [Serializable] + public class EmptyList : IList + { + public static readonly EmptyList Instance = new EmptyList(); + + private static readonly IList EmptyItem = new T[0]; + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public IEnumerator GetEnumerator() + { + return EmptyItem.GetEnumerator(); + } + + public void Clear() + { + } + + public bool Contains(T item) + { + return false; + } + + public void CopyTo(T[] array, int arrayIndex) + { + } + + public bool Remove(T item) + { + throw new UnsupportedOperationException(); + } + + public int Count + { + get { return 0; } + } + + public bool IsReadOnly + { + get { return true; } + } + + public void Add(T item) + { + throw new UnsupportedOperationException(); + } + + public int IndexOf(T item) + { + return -1; + } + + public void Insert(int index, T item) + { + throw new UnsupportedOperationException(); + } + + public void RemoveAt(int index) + { + throw new UnsupportedOperationException(); + } + + public T this[int index] + { + get { throw new UnsupportedOperationException(); } + set { throw new UnsupportedOperationException(); } + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/collections/EmptySet.cs b/NEsper.Core/NEsper.Core/compat/collections/EmptySet.cs new file mode 100755 index 000000000..2b2b92350 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/collections/EmptySet.cs @@ -0,0 +1,123 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace com.espertech.esper.compat.collections +{ + [Serializable] + public class EmptySet : ISet + { + public static readonly EmptySet Instance = new EmptySet(); + + private static readonly T[] EmptyItem = new T[0]; + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public IEnumerator GetEnumerator() + { + return EmptyItem.Cast().GetEnumerator(); + } + + public void Clear() + { + throw new UnsupportedOperationException(); + } + + public bool Contains(T item) + { + return false; + } + + public void CopyTo(T[] array, int arrayIndex) + { + throw new UnsupportedOperationException(); + } + + public bool Remove(T item) + { + throw new UnsupportedOperationException(); + } + + public int Count + { + get { return 0; } + } + + public bool IsReadOnly + { + get { return true; } + } + + public void UnionWith(IEnumerable other) + { + throw new UnsupportedOperationException(); + } + + public void IntersectWith(IEnumerable other) + { + throw new UnsupportedOperationException(); + } + + public bool Add(T item) + { + throw new UnsupportedOperationException(); + } + + void ICollection.Add(T item) + { + throw new UnsupportedOperationException(); + } + + public void ExceptWith(IEnumerable other) + { + throw new UnsupportedOperationException(); + } + + public void SymmetricExceptWith(IEnumerable other) + { + throw new UnsupportedOperationException(); + } + + public bool IsSubsetOf(IEnumerable other) + { + throw new UnsupportedOperationException(); + } + + public bool IsSupersetOf(IEnumerable other) + { + throw new UnsupportedOperationException(); + } + + public bool IsProperSupersetOf(IEnumerable other) + { + throw new UnsupportedOperationException(); + } + + public bool IsProperSubsetOf(IEnumerable other) + { + throw new UnsupportedOperationException(); + } + + public bool Overlaps(IEnumerable other) + { + throw new UnsupportedOperationException(); + } + + public bool SetEquals(IEnumerable other) + { + throw new UnsupportedOperationException(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/collections/EnumerationHelper.cs b/NEsper.Core/NEsper.Core/compat/collections/EnumerationHelper.cs new file mode 100755 index 000000000..406b5bcdf --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/collections/EnumerationHelper.cs @@ -0,0 +1,64 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System.Collections.Generic; + +namespace com.espertech.esper.compat.collections +{ + /// + /// Collection of utilities specifically to help with enumeration. + /// + /// + public class EnumerationHelper + { + /// + /// Creates the empty enumerator. + /// + /// + public static IEnumerator Empty() + { + return new NullEnumerator(); + } + + /// + /// Creates the singleton enumerator. + /// + /// The item. + /// + public static IEnumerator Singleton(T item) + { + yield return item; + } + + /// + /// Creates an enumerator that skips a number of items in the + /// subEnumerator. + /// + /// The child enumerator. + /// The num to advance. + /// + public static IEnumerable AdvanceEnumerable( IEnumerator subEnumerator, int numToAdvance ) + { + bool hasMore = true; + + for( int ii = 0 ; ii < numToAdvance ; ii++ ) { + if (!subEnumerator.MoveNext()) { + hasMore = false; + break; + } + } + + if ( hasMore ) { + while( subEnumerator.MoveNext() ) { + yield return subEnumerator.Current; + } + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/collections/FixedDictionary.cs b/NEsper.Core/NEsper.Core/compat/collections/FixedDictionary.cs new file mode 100755 index 000000000..0ea002269 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/collections/FixedDictionary.cs @@ -0,0 +1,490 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections; +using System.Collections.Generic; + +namespace com.espertech.esper.compat.collections +{ + public class FixedDictionary : IDictionary + { + private readonly FixedDictionarySchema _dictionarySchema; + private readonly Entry[] _dataList; + private int _dataCount; + + /// + /// Initializes a new instance of the class. + /// + /// The schema. + public FixedDictionary(FixedDictionarySchema dictionarySchema) + { + _dictionarySchema = dictionarySchema; + _dataList = new Entry[_dictionarySchema.Count]; + _dataCount = 0; + } + + #region Implementation of IEnumerable + + /// + /// Returns an enumerator that iterates through a collection. + /// + /// + /// An object that can be used to iterate through the collection. + /// + /// 2 + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + #endregion + + #region Implementation of IEnumerable> + + /// + /// Returns an enumerator that iterates through the collection. + /// + /// + /// A that can be used to iterate through the collection. + /// + /// 1 + public IEnumerator> GetEnumerator() + { + foreach( K key in _dictionarySchema.Keys ) { + yield return new KeyValuePair(key, this[key]); + } + } + + #endregion + + #region Implementation of ICollection> + + /// + /// Adds an item to the . + /// + /// The object to add to the . + /// The is read-only. + public void Add(KeyValuePair item) + { + int index; + if (_dictionarySchema.TryGetIndex(item.Key, out index) && _dataList[index].HasValue) { + throw new ArgumentException("An element with the same key already exists in the dictionary"); + } + + _dataList[index].Set(item.Value); + _dataCount++; + } + + /// + /// Removes all items from the . + /// + /// The is read-only. + public void Clear() + { + for (int ii = 0; ii < _dataList.Length; ii++ ) { + _dataList[ii].Clear(); + } + + _dataCount = 0; + } + + /// + /// Determines whether the contains a specific value. + /// + /// + /// true if is found in the ; otherwise, false. + /// + /// The object to locate in the . + public bool Contains(KeyValuePair item) + { + int index; + if ( _dictionarySchema.TryGetIndex( item.Key, out index ) ) { + Entry entry = _dataList[index]; + return entry.HasValue && Equals(entry.Value, item.Value); + } + + return false; + } + + /// + /// Copies the elements of the to an , starting at a particular index. + /// + /// The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing. + /// The zero-based index in at which copying begins. + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + IEnumerator> indexEnum = _dictionarySchema.GetEnumerator(); + for( int ii = arrayIndex ; ii < array.Length ; ii++ ) { + if (!indexEnum.MoveNext()) break; + + KeyValuePair index = indexEnum.Current; + Entry entry = _dataList[index.Value]; + if ( entry.HasValue ) { + array[ii] = new KeyValuePair(index.Key, entry.Value); + } + } + } + + /// + /// Removes the first occurrence of a specific object from the . + /// + /// + /// true if was successfully removed from the ; otherwise, false. This method also returns false if is not found in the original . + /// + /// The object to remove from the . + /// The is read-only. + public bool Remove(KeyValuePair item) + { + int index; + bool isRemoved = _dictionarySchema.TryGetIndex(item.Key, out index) && _dataList[index].Clear(); + if (isRemoved) _dataCount--; + return isRemoved; + } + + /// + /// Gets the number of elements contained in the . + /// + /// + /// The number of elements contained in the . + /// + public int Count + { + get { return _dataCount; } + } + + /// + /// Gets a value indicating whether the is read-only. + /// + /// + /// true if the is read-only; otherwise, false. + /// + public bool IsReadOnly + { + get { return false; } + } + + #endregion + + #region Implementation of IDictionary + + /// + /// Determines whether the contains an element with the specified key. + /// + /// + /// true if the contains an element with the key; otherwise, false. + /// + /// The key to locate in the . + /// is null. + public bool ContainsKey(K key) + { + int index; + return _dictionarySchema.TryGetIndex(key, out index) && _dataList[index].HasValue; + } + + /// + /// Adds an element with the provided key and value to the . + /// + /// The object to use as the key of the element to add. + /// The object to use as the value of the element to add. + /// is null. + /// An element with the same key already exists in the . + /// The is read-only. + public void Add(K key, V value) + { + int index; + if (!_dictionarySchema.TryGetIndex(key, out index)) { + throw new ArgumentException("Value '" + key + "' is not supported by schema"); + } + + // Record was found in the schema, check our local table + if (_dataList[index].HasValue) { + throw new ArgumentException("An element with the same key already exists"); + } + + _dataList[index].Set(value); + _dataCount++; + } + + /// + /// Removes the element with the specified key from the . + /// + /// + /// true if the element is successfully removed; otherwise, false. This method also returns false if was not found in the original . + /// + /// The key of the element to remove. + /// is null. + /// The is read-only. + public bool Remove(K key) + { + int index; + bool isRemoved = _dictionarySchema.TryGetIndex(key, out index) && _dataList[index].Clear(); + if (isRemoved) _dataCount--; + return isRemoved; + } + + /// + /// Gets the value associated with the specified key. + /// + /// + /// true if the object that : contains an element with the specified key; otherwise, false. + /// + /// The key whose value to get. + /// When this method returns, the value associated with the specified key, if the key is found; otherwise, the default value for the type of the parameter. This parameter is passed uninitialized. + /// is null. + public bool TryGetValue(K key, out V value) + { + int index; + if (_dictionarySchema.TryGetIndex(key, out index) && _dataList[index].HasValue) { + value = _dataList[index].Value; + return true; + } + + value = default(V); + return false; + } + + /// + /// Gets or sets the element with the specified key. + /// + /// + /// The element with the specified key. + /// + /// The key of the element to get or set. + /// is null. + /// The property is retrieved and is not found. + /// The property is set and the is read-only. + public V this[K key] + { + get + { + Entry entry = _dataList[_dictionarySchema[key]]; + if (entry.HasValue) { + return entry.Value; + } + + throw new KeyNotFoundException(); + } + + set + { + if (!_dataList[_dictionarySchema[key]].Set(value)) + _dataCount++; + } + } + + /// + /// Assigns the index. + /// + /// MapIndex of the key. + /// The value. + public void AssignIndex(int keyIndex, V value) + { + if (!_dataList[keyIndex].Set(value)) + _dataCount++; + } + + /// + /// Gets an containing the keys of the . + /// + /// + /// An containing the keys of the object that : . + /// + public ICollection Keys + { + get + { + ICollection keyList = new List(); + foreach( KeyValuePair indexEntry in _dictionarySchema ) { + Entry entry = _dataList[indexEntry.Value]; + if (entry.HasValue) { + keyList.Add(indexEntry.Key); + } + } + + return keyList; + } + } + + /// + /// Gets an containing the values in the . + /// + /// + /// An containing the values in the object that : . + /// + public ICollection Values + { + get + { + ICollection valueList = new List(); + foreach (Entry entry in _dataList) { + if (entry.HasValue) { + valueList.Add(entry.Value); + } + } + + return valueList; + } + } + + #endregion + + #region Implementation of IDictionary + + /// + /// Fetches the value associated with the specified key. + /// If no value can be found, then the defaultValue is + /// returned. + /// + /// + /// + /// + public V Get(K key, V defaultValue) + { + V returnValue; + if (!TryGetValue(key, out returnValue)) { + returnValue = defaultValue; + } + + return returnValue; + } + + /// + /// Fetches the value associated with the specified key. + /// If no value can be found, then default(V) is returned. + /// + /// + /// + public V Get(K key) + { + return Get(key, default(V)); + } + + /// + /// Sets the given key in the dictionary. If the key + /// already exists, then it is remapped to the new value. + /// + public void Put(K key, V value) + { + this[key] = value; + } + + /// + /// Sets the given key in the dictionary. If the key + /// already exists, then it is remapped to the new value. + /// If a value was previously mapped it is returned. + /// + public V Push(K key, V value) + { + V temp; + TryGetValue(key, out temp); + this[key] = value; + return temp; + } + + /// + /// Puts all values from the source dictionary into + /// this dictionary. + /// + /// + public void PutAll(IDictionary source) + { + foreach( KeyValuePair sourceEntry in source ) { + this[sourceEntry.Key] = sourceEntry.Value; + } + } + + /// + /// Returns the first value in the enumeration of values + /// + /// + public V FirstValue + { + get + { + for( int ii = 0 ; ii < _dataList.Length ; ii++ ) { + if ( _dataList[ii].HasValue ) { + return _dataList[ii].Value; + } + } + + throw new ArgumentException("Collection had no elements"); + } + } + + /// + /// Removes the item from the dictionary that is associated with + /// the specified key. + /// + /// Search key into the dictionary + /// The value removed from the dictionary (if found). + /// + public bool Remove(K key, out V value) + { + int index; + value = default(V); + bool isRemoved = _dictionarySchema.TryGetIndex(key, out index) && _dataList[index].Clear(out value); + if (isRemoved) _dataCount--; + return isRemoved; + } + + /// + /// Removes the item from the dictionary that is associated with + /// the specified key. The item if found is returned; if not, + /// default(V) is returned. + /// + /// + /// + public V RemoveAndReturn(K key) + { + int index; + V value = default(V); + if (_dictionarySchema.TryGetIndex(key, out index)) { + if (_dataList[index].Clear(out value)) { + _dataCount--; + } + } + + return value; + } + + #endregion + + internal struct Entry + { + internal bool HasValue; + internal V Value; + + internal bool Set(V value) + { + bool hadValue = HasValue; + HasValue = true; + Value = value; + return hadValue; + } + + internal bool Clear() + { + bool hadValue = HasValue; + HasValue = false; + Value = default(V); + return hadValue; + } + + internal bool Clear(out V prevValue) + { + prevValue = Value; + bool hadValue = HasValue; + HasValue = false; + Value = default(V); + return hadValue; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/collections/FixedDictionarySchema.cs b/NEsper.Core/NEsper.Core/compat/collections/FixedDictionarySchema.cs new file mode 100755 index 000000000..160da955a --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/collections/FixedDictionarySchema.cs @@ -0,0 +1,118 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections; +using System.Collections.Generic; + +namespace com.espertech.esper.compat.collections +{ + /// + /// FixedDictionarySchema is a class that represents the structure of a map who's + /// keys are known when the schema is created. FixedSchemas are immutable + /// once they have been created, but because they are known they can be + /// used to create Map objects that have a much smaller memory footprint + /// than conventional hashtables. + /// + /// + + public class FixedDictionarySchema : IEnumerable> + { + private readonly int _keyCount; + + /// + /// This dictionary maps keys to a linear index. + /// + private readonly IDictionary _keyToIndex = + new Dictionary(); + + /// + /// Gets the count. + /// + /// The count. + public int Count + { + get { return _keyCount; } + } + + /// + /// Gets the keys for the schema. + /// + /// The keys. + public ICollection Keys + { + get { return _keyToIndex.Keys; } + } + + /// + /// Gets the index associated with the specified key. + /// + /// + public int this[K key] + { + get { return _keyToIndex[key]; } + } + + /// + /// Tries the get the index for the key. If the index does not + /// exist, the method returns false. + /// + /// The key. + /// The index. + /// + public bool TryGetIndex(K key, out int index) + { + return _keyToIndex.TryGetValue(key, out index); + } + + #region Implementation of IEnumerable + + /// + /// Returns an enumerator that iterates through a collection. + /// + /// + /// An object that can be used to iterate through the collection. + /// + /// 2 + IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + #endregion + + #region Implementation of IEnumerable + + /// + /// Returns an enumerator that iterates through the collection. + /// + /// + /// A that can be used to iterate through the collection. + /// + /// 1 + public IEnumerator> GetEnumerator() + { + return _keyToIndex.GetEnumerator(); + } + + #endregion + + /// + /// Initializes a new instance of the class. + /// + /// The key list. + public FixedDictionarySchema(IEnumerable keyList) + { + int index = 0; + foreach( K key in keyList ) { + _keyToIndex[key] = index++; + } + + _keyCount = _keyToIndex.Count; + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/collections/GenericExtensions.cs b/NEsper.Core/NEsper.Core/compat/collections/GenericExtensions.cs new file mode 100755 index 000000000..1dbc61d55 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/collections/GenericExtensions.cs @@ -0,0 +1,240 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Xml; + +namespace com.espertech.esper.compat.collections +{ + public static class GenericExtensions + { + public static bool IsNullable(this Type t) + { + return Nullable.GetUnderlyingType(t) != null; + } + + public static bool IsAssignableIndex(this Type t) + { + if (t == null) + return false; + if (t.IsArray) + return false; + if (t == typeof(XmlNode)) + return false; + if (t == typeof(string)) + return true; + if (t.IsGenericList()) + return true; + if (util.TypeHelper.IsImplementsInterface(t, typeof(System.Collections.IList))) + return true; + if (t.IsGenericEnumerable()) + return false; + if (util.TypeHelper.IsImplementsInterface(t, typeof(System.Collections.IEnumerable))) + return false; + if (t.IsArray) + return false; + + return false; + } + + public static Type GetIndexType(this Type t) + { + if (t == null) + return null; + if (t.IsArray) + return t.GetElementType(); + if (t == typeof(XmlNode)) + return null; + if (t == typeof(string)) + return typeof(char); + if (t.IsGenericList()) + return FindGenericInterface(t, typeof (System.Collections.Generic.IList<>)).GetGenericArguments()[0]; + if (util.TypeHelper.IsImplementsInterface(t, typeof(System.Collections.IList))) + return typeof(object); + if (t.IsGenericDictionary()) + return null; + if (t.IsGenericEnumerable()) + return FindGenericInterface(t, typeof(System.Collections.Generic.IEnumerable<>)).GetGenericArguments()[0]; + if (util.TypeHelper.IsImplementsInterface(t, typeof(System.Collections.IEnumerable))) + return typeof(object); + + return null; + } + + public static bool IsIndexed(this Type t) + { + if (t == null) + return false; + if (t.IsArray) + return true; + if (t == typeof(XmlNode)) + return false; + if (t == typeof(string)) + return true; + if (t.IsGenericList()) + return true; + if (util.TypeHelper.IsImplementsInterface(t, typeof(System.Collections.IList))) + return true; + if (t.IsGenericDictionary()) + return false; + if (t.IsGenericEnumerable()) + return true; + if (util.TypeHelper.IsImplementsInterface(t, typeof(System.Collections.IEnumerable))) + return true; + + return false; + } + + public static bool IsGenericDictionary(this Type t) + { + var dictType = FindGenericInterface(t, typeof(IDictionary<,>)); + return (dictType != null); + } + + public static bool IsGenericStringDictionary(this Type t) + { + var dictType = FindGenericInterface(t, typeof (IDictionary<,>)); + if (dictType != null) + return dictType.GetGenericArguments()[0] == typeof (string); + return false; + } + + public static bool IsGenericCollection(this Type t) + { + return FindGenericInterface(t, typeof (ICollection<>)) != null; + } + + public static bool IsGenericList(this Type t) + { + return FindGenericInterface(t, typeof(IList<>)) != null; + } + + public static bool IsGenericEnumerable(this Type t) + { + return FindGenericInterface(t, typeof(IEnumerable<>)) != null; + } + + public static bool IsGenericEnumerator(this Type t) + { + return FindGenericInterface(t, typeof(IEnumerator<>)) != null; + } + + public static Type FindGenericList(this Type t) + { + return FindGenericInterface(t, typeof (IList<>)); + } + + public static Type FindGenericInterface(this Type t, Type baseInterface) + { + if (t.IsInterface && t.IsGenericType) + { + var genericType = t.GetGenericTypeDefinition(); + if (genericType == baseInterface) + { + return t; + } + } + + foreach (var iface in t.GetInterfaces()) + { + var subFind = FindGenericInterface(iface, baseInterface); + if (subFind != null) + { + return subFind; + } + } + + return null; + } + + public static Type FindGenericDictionaryInterface(Type t) + { + return t.FindGenericInterface(typeof (IDictionary<,>)); + } + + public static Type FindGenericEnumerationInterface(Type t) + { + return t.FindGenericInterface(typeof (IEnumerable<>)); + } + + public static object FetchGenericKeyedValue(this object o, string key) + { + var dictionary = o as IDictionary; + return dictionary.Get(key); + } + + private static readonly IDictionary> TypeFetchTable = + new Dictionary>(); + + public static object FetchKeyedValue(object o, string key, object defaultValue) + { + var type = o.GetType(); + + Func typeFetchFunc; + + lock( TypeFetchTable ) { + typeFetchFunc = TypeFetchTable.Get(type); + if ( typeFetchFunc == null ) { + var genericDictionaryType = FindGenericDictionaryInterface(o.GetType()); + if (genericDictionaryType == null) + { + typeFetchFunc = ((p1, p2) => defaultValue); + } + else + { + var genericMethod = typeof(GenericExtensions).GetMethod("FetchGenericKeyedValue"); + var specificMethod = genericMethod.MakeGenericMethod( + genericDictionaryType.GetGenericArguments()[1]); + typeFetchFunc = ((p1, p2) => specificMethod.Invoke(null, new[] {p1, p2})); + } + + TypeFetchTable[type] = typeFetchFunc; + } + } + + return typeFetchFunc.Invoke(o, key); + } + + private static readonly Dictionary> CollectionAccessorTable = + new Dictionary>(); + + public static Func CreateCollectionContainsAccessor(this Type t) + { + lock( CollectionAccessorTable ) { + Func accessor; + + if (!CollectionAccessorTable.TryGetValue(t, out accessor)) + { + // Scan the object and make sure that it : the collection interface + var rawInterface = FindGenericInterface(t, typeof(ICollection<>)); + if (rawInterface == null) { + accessor = null; + } else { + var containMethod = rawInterface.GetMethod("Contains"); + var exprParam1 = Expression.Parameter(t, "collection"); + var exprParam2 = Expression.Parameter(typeof (object), "obj"); + var exprMethod = Expression.Call(containMethod, exprParam1, exprParam2); + var exprLambda = Expression.Lambda>( + exprMethod, + exprParam1, + exprParam2); + accessor = exprLambda.Compile(); + } + + CollectionAccessorTable[t] = accessor; + } + + return accessor; + } + + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/collections/HashMap.cs b/NEsper.Core/NEsper.Core/compat/collections/HashMap.cs new file mode 100755 index 000000000..3bc2a49a9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/collections/HashMap.cs @@ -0,0 +1,58 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.compat.collections +{ + /// + /// An extended dictionary based upon a closed hashing + /// algorithm. + /// + /// + /// + + [Serializable] + public class HashMap : BaseMap + where K : class + { + /// + /// Initializes a new instance of the class. + /// + public HashMap() + : base( new Dictionary() ) + { + } + + /// + /// Initializes a new instance of the class. + /// + public HashMap(int initialCapacity) + : base(new Dictionary(initialCapacity)) + { + } + + /// + /// Initializes a new instance of the class. + /// + + public HashMap(IEqualityComparer eqComparer) + : base( new Dictionary( eqComparer ) ) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The sub dictionary. + public HashMap(IDictionary subDictionary) : base(subDictionary) + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/collections/IBlockingQueue.cs b/NEsper.Core/NEsper.Core/compat/collections/IBlockingQueue.cs new file mode 100755 index 000000000..499bbc424 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/collections/IBlockingQueue.cs @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +namespace com.espertech.esper.compat.collections +{ + public interface IBlockingQueue + { + /// + /// Gets the number of items in the queue. + /// + /// The count. + int Count { get; } + /// + /// Clears all items from the queue + /// + void Clear(); + /// + /// Pushes an item onto the queue. If the queue has reached + /// capacity, the call will pend until the queue has space to + /// receive the request. + /// + /// + void Push(T item); + /// + /// Pops an item off the queue. If there is nothing on the queue + /// the call will pend until there is an item on the queue. + /// + /// + T Pop(); + + /// + /// Pops an item off the queue. If there is nothing on the queue + /// the call will pend until there is an item on the queue or + /// the timeout has expired. If the timeout has expired, the + /// method will return false. + /// + /// The max timeout in millis. + /// The item. + /// + bool Pop(int maxTimeoutInMillis, out T item); + } +} diff --git a/NEsper.Core/NEsper.Core/compat/collections/IVisitable.cs b/NEsper.Core/NEsper.Core/compat/collections/IVisitable.cs new file mode 100755 index 000000000..f4bca6e6c --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/collections/IVisitable.cs @@ -0,0 +1,17 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.compat.collections +{ + public interface IVisitable + { + void Visit(Action visitAction); + } +} diff --git a/NEsper.Core/NEsper.Core/compat/collections/IVisitableCollection.cs b/NEsper.Core/NEsper.Core/compat/collections/IVisitableCollection.cs new file mode 100755 index 000000000..3a486f26f --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/collections/IVisitableCollection.cs @@ -0,0 +1,16 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +namespace com.espertech.esper.compat.collections +{ + public interface IVisitableCollection : ICollection, IVisitable + { + } +} diff --git a/NEsper.Core/NEsper.Core/compat/collections/IdentityDictionary.cs b/NEsper.Core/NEsper.Core/compat/collections/IdentityDictionary.cs new file mode 100755 index 000000000..c48231164 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/collections/IdentityDictionary.cs @@ -0,0 +1,61 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System.Collections.Generic; + +namespace com.espertech.esper.compat.collections +{ + /// + /// An extended dictionary based upon a closed hashing + /// algorithm. + /// + /// + /// + + public class IdentityDictionary + : HashMap + where K : class + { + /// + /// Initializes a new instance of the class. + /// + public IdentityDictionary() + : base(new EqualityComparer()) + { + } + + internal class EqualityComparer : IEqualityComparer + { + /// + /// Returns true if the two objects are equal. In the case of the + /// identity dictionary, equality is true only if the objects are + /// the same reference. + /// + /// + /// + /// + + public bool Equals(K x, K y) + { + return x == y; + } + + /// + /// Returns a hashcode for the object. + /// + /// + /// + + public int GetHashCode(K obj) + { + return obj.GetHashCode(); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/collections/ImperfectBlockingQueue.cs b/NEsper.Core/NEsper.Core/compat/collections/ImperfectBlockingQueue.cs new file mode 100755 index 000000000..1516b1ae0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/collections/ImperfectBlockingQueue.cs @@ -0,0 +1,307 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Threading; +using com.espertech.esper.compat.threading; + +namespace com.espertech.esper.compat.collections +{ + /// + /// ImperfectBlockingQueue is a blocking queue designed for very high performance exchanges + /// between threads. Multiple readers and writers can exchange information using the + /// ImperfectBlockingQueue. The design allows for a read node and a write node. Both the + /// read node and write node are assumed to be probablistically incorrect at any given time. + /// Specifically, that means that the read node may have actually been processed and the + /// write node may not actually be the tail. Rather than attempting to correct for this + /// imperfection in the data structure, we leverage it. + /// + /// When a writer attempts to write to the tail, the tail uses an atomic compare-exchange + /// to exchange the next node with the newly allocated node. If the exchange fails, the + /// thread will iterate through the next member until it finds null and the cycle continue + /// again with the atomic compare-exchange. Using this method, the writer will succeed + /// in writing to the tail atomically. The write node does not need to accurately reflect + /// the true end of tail, so adjusting the write node to the written node is "reasonably" + /// accurate. + /// + /// When a reader attempts to read from the head, an atomic compare exchange is used to + /// test against the "IsProcessed" field of the node. If the node has been processed, then + /// the reader moves on to the next node until it can successfully perform a CAS against + /// the node. If none can be found, the method will force a sleep to simulate a block. + /// Once found, the reader extracts the value for return and sets the head equal to the + /// node just read. Again, since we're probablistic, this is fine. Since we've successfully + /// read from the node, we're assured that all nodes before us have been processed. Being + /// "reasonably" accurate with the read node is fine since the next reader will simply + /// advance from this point. + /// + /// This class was tested against various concurrent reader/writer models was equal to or + /// outperformed all other models in all cases. However, it still appears that during + /// tight iterations that there is about a 4-1 call ratio between CAS and the Push method + /// which means there is still some efficiency to be squeezed out. + /// + /// + + public sealed class ImperfectBlockingQueue : IBlockingQueue + { + private long _count; + private Node _rnode; + private Node _wnode; + + private long _slowLockInterest; + private readonly object _slowLock; + + private readonly long _maxLength; + + /// + /// Initializes a new instance of the class. + /// + public ImperfectBlockingQueue() + : this(int.MaxValue) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Length of the max. + public ImperfectBlockingQueue(int maxLength) + { + _slowLockInterest = 0; + _slowLock = new object(); + _count = 0; + _wnode = _rnode = new Node(default(T)) { IsProcessed = 1 }; + _maxLength = maxLength; + } + + /// + /// Gets the number of items in the queue. + /// + /// The count. + public int Count + { + get { return (int)Interlocked.Read(ref _count); } + } + + /// + /// Clears all items from the queue + /// + public void Clear() + { + _rnode = _wnode = new Node(default(T)) { IsProcessed = 1 }; + } + + /// + /// Pushes an item onto the queue. If the queue has reached + /// capacity, the call will pend until the queue has space to + /// receive the request. + /// + /// + public void Push(T item) + { + if (_maxLength != int.MaxValue) + { + if (!BoundBlockingQueueOverride.IsEngaged) + { + for (int ii = 0; Interlocked.Read(ref _count) > _maxLength;) + { + SlimLock.SmartWait(++ii); + } + } + } + + // Create the new node + var node = new Node(item); + // Get the write node for the thread + Node branch = _wnode; + + for (; ;) + { + Node temp; + while ((temp = branch.Next) != null) + { + branch = temp; // temp is guaranteed to not be null + } + + var pnode = Interlocked.CompareExchange( + ref branch.Next, + node, + null); + if (pnode == null) + { + _wnode = node; + // Check for threads that have been waiting a long time ... these + // threads will be using a slowLockInterest rather than a tight spin + // loop. + if (Interlocked.Read(ref _slowLockInterest) > 0) + { + lock (_slowLock) + { + Monitor.Pulse(_slowLock); + } + } + // Increment the counter + Interlocked.Increment(ref _count); + return; + } + } + } + + /// + /// Pops an item off the queue. If there is nothing on the queue + /// the call will pend until there is an item on the queue. + /// + /// + public T Pop() + { + long iteration = 0; + + do + { + var node = _rnode; + while (node.IsProcessed == 1) + { + var temp = node.Next; + if (temp != null) + { + node = temp; + continue; + } + + // Simulate a blocking event - but be careful. The tighter the spin, + // the more cycles you steal from threads that may eventually have to + // push data. + // + // For iterations 1-3: SpinWait + // For iterations 4-10: Sleep(1) + + iteration++; + if (iteration <= 3) + { + Thread.SpinWait(10); + } + else if (iteration <= 6) + { + Thread.Sleep(1); + } + else + { + lock (_slowLock) + { + Interlocked.Increment(ref _slowLockInterest); + Monitor.Wait(_slowLock, 200); + Interlocked.Decrement(ref _slowLockInterest); + } + } + + // Node was being processed, recycle the loop + } + + if (Interlocked.CompareExchange(ref node.IsProcessed, 1, 0) == 0) + { + // found a node that has not been processed and we have + // obtained the IsProcessed flag for this node. Set the rnode + // to the node we have just processed. Worst case, its a little + // stale, but the Pop() will advance past stale nodes anyway. + _rnode = node; + Interlocked.Decrement(ref _count); + return node.Value; + } + } while (true); + } + + /// + /// Pops an item off the queue. If there is nothing on the queue + /// the call will pend until there is an item on the queue or + /// the timeout has expired. If the timeout has expired, the + /// method will return false. + /// + /// The max timeout in millis. + /// The item. + /// + public bool Pop(int maxTimeoutInMillis, out T item) + { + long endTime = Environment.TickCount + maxTimeoutInMillis; + long iteration = 0; + + do { + var node = _rnode; + while (node.IsProcessed == 1) { + var temp = node.Next; + if (temp != null) { + node = temp; + continue; + } + + // Simulate a blocking event - but be careful. The tighter the spin, + // the more cycles you steal from threads that may eventually have to + // push data. + // + // For iterations 1-3: SpinWait + // For iterations 4-10: Sleep(1) + + iteration++; + if ( iteration <= 3 ) { + Thread.SpinWait(10); + } else if (iteration <= 6 ) { + Thread.Sleep(1); + } else { + lock(_slowLock) { + Interlocked.Increment(ref _slowLockInterest); + Monitor.Wait(_slowLock, 200); + Interlocked.Decrement(ref _slowLockInterest); + } + } + + // Node was being processed, recycle the loop + var nowTime = Environment.TickCount; + if (nowTime >= endTime) + { + item = default(T); + return false; + } + } + + if (Interlocked.CompareExchange(ref node.IsProcessed, 1, 0) == 0) { + // found a node that has not been processed and we have + // obtained the IsProcessed flag for this node. Set the rnode + // to the node we have just processed. Worst case, its a little + // stale, but the Pop() will advance past stale nodes anyway. + _rnode = node; + Interlocked.Decrement(ref _count); + item = node.Value; + return true; + } + } while (true); + } + + public sealed class Node + { + /// + /// Indicates whether the node has been processed + /// + public int IsProcessed; + /// + /// Value at this node + /// + public T Value; + /// + /// Next node in list + /// + public Node Next; + + /// + /// Initializes a new instance of the class. + /// + /// The value. + public Node(T value) + { + Value = value; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/collections/LinkedBlockingQueue.cs b/NEsper.Core/NEsper.Core/compat/collections/LinkedBlockingQueue.cs new file mode 100755 index 000000000..a2e44c05a --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/collections/LinkedBlockingQueue.cs @@ -0,0 +1,160 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Threading; + +namespace com.espertech.esper.compat.collections +{ + public class LinkedBlockingQueue : IBlockingQueue + { + private readonly LinkedList _queue; + private readonly Object _queueLock; + + /// + /// Initializes a new instance of the class. + /// + public LinkedBlockingQueue() + { + _queue = new LinkedList(); + _queueLock = new Object(); + } + + /// + /// Gets the number of items in the queue. + /// + /// The count. + public int Count + { + get + { + Monitor.Enter(_queueLock); + + try + { + return _queue.Count; + } + finally + { + Monitor.Exit(_queueLock); + } + } + } + + /// + /// Clears all items from the queue + /// + public void Clear() + { + Monitor.Enter(_queueLock); + + try + { + _queue.Clear(); + } + finally + { + Monitor.Exit(_queueLock); + } + } + + /// + /// Pushes an item onto the queue. If the queue has reached + /// capacity, the call will pend until the queue has space to + /// receive the request. + /// + /// + public void Push(T item) + { + Monitor.Enter(_queueLock); + + try + { + _queue.AddLast(item); + Monitor.Pulse(_queueLock); + } + finally + { + Monitor.Exit(_queueLock); + } + } + + /// + /// Pops an item off the queue. If there is nothing on the queue + /// the call will pend until there is an item on the queue. + /// + /// + public T Pop() + { + Monitor.Enter(_queueLock); + try + { + for (;;) + { + var first = _queue.First; + if (first != null) + { + var value = first.Value; + _queue.RemoveFirst(); + return value; + } + + Monitor.Wait(_queueLock); + } + } + finally + { + Monitor.Exit(_queueLock); + } + } + + /// + /// Pops an item off the queue. If there is nothing on the queue + /// the call will pend until there is an item on the queue or + /// the timeout has expired. If the timeout has expired, the + /// method will return false. + /// + /// The max timeout in millis. + /// The item. + /// + public bool Pop(int maxTimeoutInMillis, out T item) + { + long endTime = DateTimeHelper.CurrentTimeMillis + maxTimeoutInMillis; + + Monitor.Enter(_queueLock); + try + { + for (;;) + { + var first = _queue.First; + if (first != null) + { + var value = first.Value; + _queue.RemoveFirst(); + item = value; + return true; + } + + var nowTime = DateTimeHelper.CurrentTimeMillis; + if (nowTime >= endTime) + { + item = default(T); + return false; + } + + Monitor.Wait(_queueLock, (int) (endTime - nowTime)); + } + } + finally + { + Monitor.Exit(_queueLock); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/collections/LinkedHashMap.cs b/NEsper.Core/NEsper.Core/compat/collections/LinkedHashMap.cs new file mode 100755 index 000000000..92d83cb31 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/collections/LinkedHashMap.cs @@ -0,0 +1,651 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Runtime.Serialization; + +using com.espertech.esper.collection; +using com.espertech.esper.compat.attributes; + +namespace com.espertech.esper.compat.collections +{ + /// + /// Hashtable and linked list implementation designed to mimic Java's LinkedHashMap + /// functionality. + /// + /// + /// + + [Serializable] + [RenderWithToString] + public sealed class LinkedHashMap : IDictionary, ISerializable + { + /// + /// Delegate for handling events on dictionary entries. + /// + /// + /// + + public delegate bool EntryEventHandler(KeyValuePair entry); + + /// + /// A list of all key-value pairs added to the table. The list + /// preserves insertion order and is used to preserve enumeration + /// ordering. + /// + + private readonly LinkedList> _hashList; + + /// + /// Contains a reference to the key and is used for all lookups. Refers + /// to the node in the linked list node. Provides for fast removal of + /// the node upon removal. + /// + + private readonly IDictionary>> _hashTable; + + /// + /// Shuffles items on access + /// + + private bool _shuffleOnAccess; + + /// + /// Returns a value indicating if items should be shuffled (pushed to the + /// head of the list) on access requests. + /// + + public bool ShuffleOnAccess + { + get { return _shuffleOnAccess; } + set { _shuffleOnAccess = value; } + } + + /// + /// Initializes a new instance of the class. + /// + + public LinkedHashMap() + { + _shuffleOnAccess = false; + _hashList = new LinkedList>(); + _hashTable = new Dictionary>>().WithSafeSupport(); + } + + /// + /// Initializes a new instance of the class. + /// + /// + + public LinkedHashMap(IEnumerable> sourceTable) + { + _hashList = new LinkedList>(); + _hashTable = new Dictionary>>().WithSafeSupport(); + + foreach (var entry in sourceTable) + { + Put(entry.Key, entry.Value); + } + } + + /// + /// Serialization constructor + /// + /// The INFO. + /// The context. + + public LinkedHashMap(SerializationInfo info, StreamingContext context) + { + var count = info.GetInt32("Count"); + var pairList = (Pair[]) info.GetValue("_hashList", typeof (Pair[])); + Debug.Assert(pairList.Length == count); + + _shuffleOnAccess = info.GetBoolean("_shuffle"); + + _hashList = new LinkedList>(pairList); + if ( _hashList == null ) { + throw new SerializationException("unable to deserialize hashList"); + } + + _hashTable = new Dictionary>>((_hashList.Count * 3) / 2) + .WithSafeSupport(); + + for (LinkedListNode> node = _hashList.First; node != null; node = node.Next) { + _hashTable.Add(node.Value.First, node); + } + } + + /// + /// Populates a with the data needed to serialize the target object. + /// + /// The to populate with data. + /// The destination (see ) for this serialization. + /// The caller does not have the required permission. + public void GetObjectData(SerializationInfo info, StreamingContext context) + { + // Note: we identified a bug in the serializer that occurs with serialization of a linked list + // Note: henceforth, use an array to serialize and deserialize. + info.AddValue("_hashList", _hashList.ToArray()); + info.AddValue("_shuffle", _shuffleOnAccess); + info.AddValue("Count", _hashList.Count); + } + + #region IDictionary Members + + /// + /// Fetches the value associated with the specified key. + /// If no value can be found, then the defaultValue is + /// returned. + /// + /// + /// + /// + + public TV Get(TK key, TV defaultValue) + { + TV returnValue; + if (!TryGetValue(key, out returnValue)) + { + returnValue = defaultValue; + } + return returnValue; + } + + /// + /// Fetches the value associated with the specified key. + /// If no value can be found, then default(V) is returned. + /// + /// + /// + + public TV Get(TK key) + { + return Get(key, default(TV)); + } + + /// + /// Sets the given key in the dictionary. If the key + /// already exists, then it is remapped to thenew value. + /// + + public void Put(TK key, TV value) + { + this[key] = value; + } + + /// + /// Sets the given key in the dictionary. If the key + /// already exists, then it is remapped to the new value. + /// If a value was previously mapped it is returned. + /// + + public TV Push(TK key, TV value) + { + TV temp; + TryGetValue(key, out temp); + this[key] = value; + return temp; + } + + /// + /// Puts all values from the source dictionary into + /// this dictionary. + /// + /// + + public void PutAll(IDictionary source) + { + if (source.Count != 0) + { + IEnumerator> enumObj = source.GetEnumerator(); + while (enumObj.MoveNext()) + { + this[enumObj.Current.Key] = enumObj.Current.Value; + } + } + } + + /// + /// Returns the first value in the enumeration of values + /// + /// + + public TV FirstValue + { + get + { + IEnumerator> kvPairEnum = GetEnumerator(); + kvPairEnum.MoveNext(); + return kvPairEnum.Current.Value; + } + } + + /// + /// Removes the item from the dictionary that is associated with + /// the specified key. Returns the value that was found at that + /// location and removed or the defaultValue. + /// + /// Search key into the dictionary + /// The value removed from the dictionary (if found). + /// + + public bool Remove(TK key, out TV value) + { + if (!TryGetValue(key, out value)) + { + return false; + } + + return Remove(key); + } + + #endregion + + /// + /// Occurs when a potentially destructive operations occurs on the dictionary + /// and the dictionary is allowed to rebalance. + /// + + public event EntryEventHandler RemoveEldest; + + #region IDictionary Members + + /// + /// Adds an element with the provided key and value to the . + /// + /// The object to use as the key of the element to add. + /// The object to use as the value of the element to add. + /// The is read-only. + /// An element with the same key already exists in the . + /// key is null. + + public void Add(TK key, TV value) + { + if (_hashTable.ContainsKey(key)) + { + throw new ArgumentException("An element with the same key already exists"); + } + + var keyValuePair = new Pair(key, value); + var linkedListNode = _hashList.AddLast(keyValuePair); + _hashTable.Add(key, linkedListNode); + + CheckEldest(); + } + + /// + /// Checks the eldest entry and see if we should remove it. + /// + + private void CheckEldest() + { + if (RemoveEldest != null) + { + var linkedListNode = _hashList.First; + var eldest = new KeyValuePair( + linkedListNode.Value.First, + linkedListNode.Value.Second); + if (RemoveEldest(eldest)) + { + _hashList.Remove(linkedListNode); + _hashTable.Remove(linkedListNode.Value.First); + } + } + } + + /// + /// Determines whether the contains an element with the specified key. + /// + /// The key to locate in the . + /// + /// true if the contains an element with the key; otherwise, false. + /// + /// key is null. + + public bool ContainsKey(TK key) + { + return _hashTable.ContainsKey(key); + } + + /// + /// Gets the key enumerator in a faster and more efficient manner. + /// + /// The fast key enumerator. + public IEnumerator FastKeyEnumerator + { + get { return _hashList.Select(keyValuePair => keyValuePair.First).GetEnumerator(); } + } + + /// + /// Gets the keys in a faster and more efficient manner. + /// + /// The fast key array. + public TK[] FastKeyArray + { + get + { + var rawArray = new TK[Count]; + var rawIndex = 0; + + foreach (Pair keyValuePair in _hashList) + { + rawArray[rawIndex++] = keyValuePair.First; + } + + return rawArray; + } + } + + /// + /// Gets an containing the keys of the . + /// + /// + /// An containing the keys of the object that : . + + public ICollection Keys + { + get + { + return _hashList.Select(keyValuePair => keyValuePair.First).ToList(); + } + } + + /// + /// Gets a faster lighter enumeration of keys. + /// + + public IEnumerator FastKeys + { + get { + return _hashList.Select(keyValuePair => keyValuePair.First).GetEnumerator(); + } + } + + /// + /// Removes the element with the specified key from the . + /// + /// The key of the element to remove. + /// + /// true if the element is successfully removed; otherwise, false. This method also returns false if key was not found in the original . + /// + /// The is read-only. + /// key is null. + + public bool Remove(TK key) + { + LinkedListNode> linkedListNode; + if (_hashTable.TryGetValue(key, out linkedListNode)) + { + _hashTable.Remove(key); + _hashList.Remove(linkedListNode); + return true; + } + + return false; + } + + /// + /// Removes the item from the dictionary that is associated with + /// the specified key. The item if found is returned; if not, + /// default(V) is returned. + /// + /// + /// + + public TV RemoveAndReturn(TK key) + { + TV tempItem; + + return Remove(key, out tempItem) + ? tempItem + : default(TV); + } + + /// + /// Tries the get value. + /// + /// The key. + /// The value. + /// + + public bool TryGetValue(TK key, out TV value) + { + LinkedListNode> linkedListNode; + if (_hashTable.TryGetValue(key, out linkedListNode)) + { + value = linkedListNode.Value.Second; + if (ShuffleOnAccess) + { + _hashList.Remove(linkedListNode); + _hashList.AddLast(linkedListNode); + } + return true; + } + + value = default(TV); + + return false; + } + + /// + /// Gets an containing the values in the . + /// + /// + /// An containing the values in the object that : . + + public ICollection Values + { + get + { + return _hashList.Select(keyValuePair => keyValuePair.Second).ToList(); + } + } + + /// + /// Gets or sets the value the specified key. + /// + /// + + public TV this[TK key] + { + get + { + var linkedListNode = _hashTable[key]; + if (ShuffleOnAccess) + { + _hashList.Remove(linkedListNode); + _hashList.AddLast(linkedListNode); + } + + return linkedListNode.Value.Second; + } + set + { + LinkedListNode> linkedListNode = _hashTable.Get(key); + if (linkedListNode != null) + { + linkedListNode.Value.Second = value; + } + else + { + var keyValuePair = new Pair(key, value); + linkedListNode = _hashList.AddLast(keyValuePair); + _hashTable[key] = linkedListNode; + } + + CheckEldest(); + } + } + + #endregion + + #region ICollection> Members + + /// + /// Adds an item to the . + /// + /// The object to add to the . + /// The is read-only. + + public void Add(KeyValuePair item) + { + Add(item.Key, item.Value); + } + + /// + /// Removes all items from the . + /// + /// The is read-only. + + public void Clear() + { + _hashTable.Clear(); + _hashList.Clear(); + } + + /// + /// Determines whether the contains a specific value. + /// + /// The object to locate in the . + /// + /// true if item is found in the ; otherwise, false. + /// + + public bool Contains(KeyValuePair item) + { + return _hashTable.ContainsKey(item.Key); + } + + /// + /// The table to the target array. + /// + /// The array. + /// MapIndex of the array. + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + if (array == null) + { + throw new ArgumentNullException(); + } + + if (arrayIndex < 0) + { + throw new ArgumentOutOfRangeException(); + } + + int ii = arrayIndex; + + foreach (Pair keyValuePair in _hashList) + { + array[ii++] = new KeyValuePair(keyValuePair.First, keyValuePair.Second); + } + } + + /// + /// Gets the number of elements contained in the . + /// + /// + /// The number of elements contained in the . + + public int Count + { + get { return _hashTable.Count; } + } + + /// + /// Gets a value indicating whether the is read-only. + /// + /// + /// true if the is read-only; otherwise, false. + + public bool IsReadOnly + { + get { return false; } + } + + /// + /// Removes the first occurrence of a specific object from the . + /// + /// The object to remove from the . + /// + /// true if item was successfully removed from the ; otherwise, false. This method also returns false if item is not found in the original . + /// + /// The is read-only. + + public bool Remove(KeyValuePair item) + { + return Remove(item.Key); + } + + public void RemoveWhere(Func, Boolean> whereClause) + { + var node = _hashList.First; + for(; node != null; ) + { + if (whereClause.Invoke(node.Value)) + { + var next = node.Next; + _hashList.Remove(node); + _hashTable.Remove(node.Value.First); + node = next; + } + else + { + node = node.Next; + } + } + } + + #endregion + + #region IEnumerable> Members + + /// + /// Returns an enumerator that iterates through the collection. + /// + /// + /// A that can be used to iterate through the collection. + /// + + public IEnumerator> GetEnumerator() + { + return _hashList.Select(subPair => new KeyValuePair(subPair.First, subPair.Second)).GetEnumerator(); + } + + #endregion + + #region IEnumerable Members + + /// + /// Returns an enumerator that iterates through a collection. + /// + /// + /// An object that can be used to iterate through the collection. + /// + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + #endregion + + public override string ToString() + { + return + "{" + + _hashList + .Select(subPair => string.Format("{0}={1}", subPair.First.RenderAny(), subPair.Second.RenderAny())) + .Aggregate((a, b) => a + ", " + b) + + "}"; + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/collections/LinkedHashSet.cs b/NEsper.Core/NEsper.Core/compat/collections/LinkedHashSet.cs new file mode 100755 index 000000000..192d4fa16 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/collections/LinkedHashSet.cs @@ -0,0 +1,375 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.Serialization; + +namespace com.espertech.esper.compat.collections +{ + /// + /// Description of LinkedHashSet. + /// + + [Serializable] + public sealed class LinkedHashSet + : ISet + , ISerializable + { + internal class Entry + { + internal T Value; + internal Entry Next; + internal Entry Prev; + } + + /// + /// A list of all key-value pairs added to the table. The list + /// preserves insertion order and is used to preserve enumeration + /// ordering. + /// + + private readonly Entry _entryListHead; + private Entry _entryListTail; + private int _entryCount; + + /// + /// Contains a reference to the key and is used for all lookups. Refers + /// to the node in the linked list node. Provides for fast removal of + /// the node upon removal. + /// + + private readonly IDictionary _indexTable; + + /// + /// Initializes a new instance of the class. + /// + public LinkedHashSet() + { + _entryCount = 0; + _entryListHead = new Entry(); + _entryListTail = _entryListHead; + _indexTable = new Dictionary().WithSafeSupport(); + } + + /// + /// Initializes a new instance of the class. + /// + /// The source. + public LinkedHashSet(IEnumerable source) + { + _entryCount = 0; + _entryListHead = new Entry(); + _entryListTail = _entryListHead; + _indexTable = new Dictionary().WithSafeSupport(); + + foreach (var item in source) + { + Add(item); + } + } + + /// + /// Serialization constructor + /// + /// The INFO. + /// The context. + + public LinkedHashSet(SerializationInfo info, StreamingContext context) + { + var count = info.GetInt32("Count"); + var array = (T[])info.GetValue("_list", typeof(T[])); + Debug.Assert(array.Length == count); + + _entryCount = 0; + _entryListHead = new Entry(); + _entryListTail = _entryListHead; + _indexTable = new Dictionary((array.Length * 3) / 2).WithSafeSupport(); + + foreach (T item in array) + { + Add(item); + } + } + + /// + /// Populates a with the data needed to serialize the target object. + /// + /// The to populate with data. + /// The destination (see ) for this serialization. + /// The caller does not have the required permission. + public void GetObjectData(SerializationInfo info, StreamingContext context) + { + var hashArray = ToArray(); + info.AddValue("_list", hashArray); + info.AddValue("Count", _entryCount); + } + + /// + /// Add all values from the source + /// + /// + + public void AddRange(ICollection source) + { + if (source.Count != 0) + { + IEnumerator enumObj = source.GetEnumerator(); + while (enumObj.MoveNext()) + { + Add(enumObj.Current); + } + } + } + + #region ISet Members + + bool ISet.Add(T item) + { + return AddInternal(item); + } + + public void UnionWith(IEnumerable other) + { + throw new NotImplementedException(); + } + + public void IntersectWith(IEnumerable other) + { + throw new NotImplementedException(); + } + + public void ExceptWith(IEnumerable other) + { + throw new NotImplementedException(); + } + + public void SymmetricExceptWith(IEnumerable other) + { + throw new NotImplementedException(); + } + + public bool IsSubsetOf(IEnumerable other) + { + throw new NotImplementedException(); + } + + public bool IsSupersetOf(IEnumerable other) + { + throw new NotImplementedException(); + } + + public bool IsProperSupersetOf(IEnumerable other) + { + throw new NotImplementedException(); + } + + public bool IsProperSubsetOf(IEnumerable other) + { + throw new NotImplementedException(); + } + + public bool Overlaps(IEnumerable other) + { + throw new NotImplementedException(); + } + + public bool SetEquals(IEnumerable other) + { + throw new NotImplementedException(); + } + + #endregion + + #region ICollection Members + + /// + /// Adds an item to the . + /// + /// The object to add to the . + /// The is read-only. + + public void Add(T item) + { + AddInternal(item); + } + + /// + /// Removes all items from the . + /// + /// The is read-only. + + public void Clear() + { + _indexTable.Clear(); + _entryListHead.Next = null; + _entryListTail = _entryListHead; + _entryCount = 0; + } + + /// + /// Determines whether the contains a specific value. + /// + /// The object to locate in the . + /// + /// true if item is found in the ; otherwise, false. + /// + + public bool Contains(T item) + { + return _indexTable.ContainsKey(item); + } + + public T[] ToArray() + { + var hashArray = new T[_entryCount]; + var hashIndex = 0; + for (var entry = _entryListHead.Next; entry != null; entry = entry.Next) + hashArray[hashIndex++] = entry.Value; + return hashArray; + } + + public void CopyTo(T[] array, int arrayIndex) + { + var hashIndex = arrayIndex; + for (var entry = _entryListHead.Next; entry != null; entry = entry.Next) + array[hashIndex++] = entry.Value; + } + + /// + /// Returns true if the window is empty, or false if not empty. + /// + /// true if empty + public bool IsEmpty + { + get { return _entryCount == 0; } + } + + /// + /// Gets the number of elements contained in the . + /// + /// + /// The number of elements contained in the . + + public int Count + { + get { return _entryCount; } + } + + /// + /// Gets a value indicating whether the is read-only. + /// + /// + /// true if the is read-only; otherwise, false. + + public bool IsReadOnly + { + get { return false; } + } + + /// + /// Removes the first occurrence of a specific object from the . + /// + /// The object to remove from the . + /// + /// true if item was successfully removed from the ; otherwise, false. This method also returns false if item is not found in the original . + /// + /// The is read-only. + + public bool Remove(T item) + { + Entry entry; + if (_indexTable.TryGetValue(item, out entry)) + { + RemoveEntry(entry); + return true; + } + + return false; + } + + public void RemoveWhere(Func whereClause) + { + var entry = _entryListHead.Next; + for (; entry != null; ) + { + if (whereClause.Invoke(entry.Value)) + { + var next = entry.Next; + RemoveEntry(entry); + entry = next; + } + else + { + entry = entry.Next; + } + } + } + + private void RemoveEntry(Entry entry) + { + _indexTable.Remove(entry.Value); + _entryCount--; + + entry.Prev.Next = entry.Next; + if (entry.Next != null) + entry.Next.Prev = entry.Prev; + else + _entryListTail = entry.Prev; + } + + #endregion + + private bool AddInternal(T item) + { + if (!_indexTable.ContainsKey(item)) + { + var entry = new Entry + { + Value = item, + Prev = _entryListTail + }; + + _entryListTail.Next = entry; + _entryListTail = entry; + _entryCount++; + + _indexTable[item] = entry; + + return true; + } + + return false; + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public IEnumerator GetEnumerator() + { + for (var entry = _entryListHead.Next; entry != null; entry = entry.Next) + yield return entry.Value; + } + + public void AddTo(ICollection collection) + { + for (var entry = _entryListHead.Next; entry != null; entry = entry.Next) + collection.Add(entry.Value); + } + + public void ForEach(Action action) + { + for (var entry = _entryListHead.Next; entry != null; entry = entry.Next) + action.Invoke(entry.Value); + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/collections/LookaheadEnumerator.cs b/NEsper.Core/NEsper.Core/compat/collections/LookaheadEnumerator.cs new file mode 100755 index 000000000..f1c5a8297 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/collections/LookaheadEnumerator.cs @@ -0,0 +1,138 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections; +using System.Collections.Generic; + +namespace com.espertech.esper.compat.collections +{ + public class LookaheadEnumerator : IEnumerator + { + private IEnumerator _baseEnum; + private T _next; + private bool _hasNext; + + /// + /// Gets or sets a value indicating whether this instance has a value after + /// the current value. + /// + public bool HasNext() + { + return _hasNext; + } + + /// + /// Gets the next item. + /// + /// The next. + public T Next() + { + if (! HasNext()) + throw new ArgumentOutOfRangeException(); + + var temp = _next; + + if (_baseEnum.MoveNext()) + { + _hasNext = true; + _next = _baseEnum.Current; + } + else + { + _hasNext = false; + _next = default(T); + } + + return temp; + } + + /// + /// Initializes a new instance of the class. + /// + /// The base enum. + public LookaheadEnumerator(IEnumerable baseEnum) + : this(baseEnum.GetEnumerator()) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The base enum. + public LookaheadEnumerator(IEnumerator baseEnum) + { + _baseEnum = baseEnum; + if ( baseEnum.MoveNext() ) { + _hasNext = true; + _next = baseEnum.Current; + } + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + _baseEnum.Dispose(); + _baseEnum = null; + } + + /// + /// Advances the enumerator to the next element of the collection. + /// + /// + /// true if the enumerator was successfully advanced to the next element; false if the enumerator has passed the end of the collection. + /// + /// + /// The collection was modified after the enumerator was created. + /// + public bool MoveNext() + { + throw new NotSupportedException(); + } + + /// + /// Sets the enumerator to its initial position, which is before the first element in the collection. + /// + /// + /// The collection was modified after the enumerator was created. + /// + public void Reset() + { + throw new NotSupportedException(); + } + + /// + /// Gets the current element in the collection. + /// + /// + /// + /// The current element in the collection. + /// + /// + /// The enumerator is positioned before the first element of the collection or after the last element. + /// + object IEnumerator.Current + { + get { return Current; } + } + + /// + /// Gets the element in the collection at the current position of the enumerator. + /// + /// + /// + /// The element in the collection at the current position of the enumerator. + /// + public T Current + { + get ; private set; + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/collections/NoSuchElementException.cs b/NEsper.Core/NEsper.Core/compat/collections/NoSuchElementException.cs new file mode 100755 index 000000000..6d1e475e3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/collections/NoSuchElementException.cs @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Runtime.Serialization; + +namespace com.espertech.esper.compat.collections +{ + public class NoSuchElementException : Exception + { + /// + /// Initializes a new instance of the class. + /// + public NoSuchElementException() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The message. + public NoSuchElementException(string message) : base(message) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The message. + /// The inner exception. + public NoSuchElementException(string message, Exception innerException) : base(message, innerException) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The that holds the serialized object data about the exception being thrown. + /// The that contains contextual information about the source or destination. + /// The parameter is null. + /// The class name is null or is zero (0). + protected NoSuchElementException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/compat/collections/NullEnumerator.cs b/NEsper.Core/NEsper.Core/compat/collections/NullEnumerator.cs new file mode 100755 index 000000000..d9df045fe --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/collections/NullEnumerator.cs @@ -0,0 +1,76 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.compat.collections +{ + public class NullEnumerator : IEnumerator + { + + /// + ///Gets the element in the collection at the current position of the enumerator. + /// + /// + /// + ///The element in the collection at the current position of the enumerator. + /// + /// + T IEnumerator.Current + { + get { throw new InvalidOperationException(); } + } + + /// + ///Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + ///2 + public void Dispose() + { + } + + /// + ///Advances the enumerator to the next element of the collection. + /// + /// + /// + ///true if the enumerator was successfully advanced to the next element; false if the enumerator has passed the end of the collection. + /// + /// + ///The collection was modified after the enumerator was created. 2 + public bool MoveNext() + { + return false; + } + + /// + ///Sets the enumerator to its initial position, which is before the first element in the collection. + /// + /// + ///The collection was modified after the enumerator was created. 2 + public void Reset() + { + throw new NotSupportedException(); + } + + /// + ///Gets the current element in the collection. + /// + /// + /// + ///The current element in the collection. + /// + /// + ///The enumerator is positioned before the first element of the collection or after the last element. 2 + public object Current + { + get { throw new InvalidOperationException(); } + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/collections/NullableDictionary.cs b/NEsper.Core/NEsper.Core/compat/collections/NullableDictionary.cs new file mode 100755 index 000000000..b7542a0b9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/collections/NullableDictionary.cs @@ -0,0 +1,847 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections; +using System.Collections.Generic; + +namespace com.espertech.esper.compat.collections +{ + [Serializable] + public class NullableDictionary : IDictionary + where K : class + { + /// + /// Underlying dictionary that handles real requests + /// + private readonly IDictionary _baseDictionary; + + /// + /// Value of the entry at the null key. + /// + private KeyValuePair? _nullEntry; + + /// + /// Gets the base dictionary. + /// + /// The base dictionary. + public IDictionary BaseDictionary + { + get { return _baseDictionary; } + } + + /// + /// Initializes a new instance of the class. + /// + public NullableDictionary() : this(new Dictionary()) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The base dictionary. + public NullableDictionary(IDictionary baseDictionary) + { + _baseDictionary = baseDictionary; + } + + /// + /// Returns an enumerator that iterates through a collection. + /// + /// + /// An object that can be used to iterate through the collection. + /// + IEnumerator IEnumerable.GetEnumerator() + { + if (_nullEntry != null) + { + yield return _nullEntry.Value; + } + + IEnumerator tempEnum = _baseDictionary.GetEnumerator(); + while (tempEnum.MoveNext()) + { + yield return tempEnum.Current; + } + } + + /// + /// Returns an enumerator that iterates through the collection. + /// + /// + /// A that can be used to iterate through the collection. + /// + public IEnumerator> GetEnumerator() + { + if (_nullEntry != null) + { + yield return _nullEntry.Value; + } + + IEnumerator> tempEnum = _baseDictionary.GetEnumerator(); + while (tempEnum.MoveNext()) + { + yield return tempEnum.Current; + } + } + + /// + /// Adds an item to the . + /// + /// The object to add to the . + /// + /// The is read-only. + /// + public void Add(KeyValuePair item) + { + if (item.Key == null) + { + _nullEntry = item; + } + else + { + _baseDictionary.Add(item); + } + } + + /// + /// Removes all items from the . + /// + /// + /// The is read-only. + /// + public void Clear() + { + _baseDictionary.Clear(); + _nullEntry = null; + } + + /// + /// Determines whether the contains a specific value. + /// + /// The object to locate in the . + /// + /// true if is found in the ; otherwise, false. + /// + public bool Contains(KeyValuePair item) + { + if (item.Key == null) + return _nullEntry != null; + return _baseDictionary.Contains(item); + } + + /// + /// Copies the elements of the to an , starting at a particular index. + /// + /// The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing. + /// The zero-based index in at which copying begins. + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + if (_nullEntry == null) + { + _baseDictionary.CopyTo(array, arrayIndex); + } + else + { + array[0] = _nullEntry.Value; + _baseDictionary.CopyTo(array, arrayIndex + 1); + } + } + + /// + /// Removes the first occurrence of a specific object from the . + /// + /// + /// true if was successfully removed from the ; otherwise, false. This method also returns false if is not found in the original . + /// + /// + /// The object to remove from the . + /// + /// + /// The is read-only. + /// + public bool Remove(KeyValuePair item) + { + if ( item.Key == null ) + { + if ( _nullEntry != null ) + { + _nullEntry = null; + return true; + } + + return false; + } + + return _baseDictionary.Remove(item); + } + + /// + /// Gets the count for the null entry. + /// + /// The null entry count. + public int NullEntryCount + { + get + { + return _nullEntry != null ? 1 : 0; + } + } + + /// + /// Gets the number of elements contained in the . + /// + /// + /// The number of elements contained in the . + /// + public int Count + { + get + { + return _baseDictionary.Count + NullEntryCount; + } + } + + /// + /// Gets a value indicating whether the is read-only. + /// + /// + /// true if the is read-only; otherwise, false. + /// + public bool IsReadOnly + { + get { return _baseDictionary.IsReadOnly; } + } + + /// + /// Determines whether the contains an element with the specified key. + /// + /// + /// true if the contains an element with the key; otherwise, false. + /// + /// + /// The key to locate in the . + /// + /// is null. + /// + public bool ContainsKey(K key) + { + if (key == null) + { + return _nullEntry != null; + } + + return _baseDictionary.ContainsKey(key); + } + + /// + /// Adds an element with the provided key and value to the . + /// + /// + /// The object to use as the key of the element to add. + /// + /// + /// The object to use as the value of the element to add. + /// + /// is null. + /// + /// + /// An element with the same key already exists in the . + /// + /// + /// The is read-only. + /// + public void Add(K key, V value) + { + if (key == null) + { + if (_nullEntry != null) + { + throw new ArgumentException("An element with the same key already exists"); + } + + _nullEntry = new KeyValuePair(null, value); + } + else + { + _baseDictionary.Add(key, value); + } + } + + /// + /// Removes the element with the specified key from the . + /// + /// + /// true if the element is successfully removed; otherwise, false. This method also returns false if was not found in the original . + /// + /// + /// The key of the element to remove. + /// + /// is null. + /// + /// + /// The is read-only. + /// + public bool Remove(K key) + { + if (key == null) + { + if (_nullEntry != null) + { + _nullEntry = null; + return true; + } + + return false; + } + else + { + return _baseDictionary.Remove(key); + } + } + + /// + /// Gets the value associated with the specified key. + /// + /// + /// true if the object that : contains an element with the specified key; otherwise, false. + /// + /// + /// The key whose value to get. + /// + /// + /// When this method returns, the value associated with the specified key, if the key is found; otherwise, the default value for the type of the parameter. This parameter is passed uninitialized. + /// + /// is null. + /// + public bool TryGetValue(K key, out V value) + { + if (key == null) + { + if (_nullEntry != null) + { + value = _nullEntry.Value.Value; + return true; + } + + value = default(V); + return false; + } + else + { + return _baseDictionary.TryGetValue(key, out value); + } + } + + /// + /// Gets or sets the element with the specified key. + /// + /// + /// The element with the specified key. + /// + /// + /// The key of the element to get or set. + /// + /// is null. + /// + /// + /// The property is retrieved and is not found. + /// + /// + /// The property is set and the is read-only. + /// + public V this[K key] + { + get + { + if (key == null) + { + if (_nullEntry != null) + { + return _nullEntry.Value.Value; + } + + throw new KeyNotFoundException(); + } + else + { + return _baseDictionary[key]; + } + } + + set + { + if (key == null) + { + _nullEntry = new KeyValuePair(null, value); + } + else + { + _baseDictionary[key] = value; + } + } + } + + /// + /// Gets an containing the keys of the . + /// + /// + /// An containing the keys of the object that : . + /// + public ICollection Keys + { + get + { + if (_nullEntry == null) + { + return _baseDictionary.Keys; + } + else + { + return new CollectionPlus(_baseDictionary.Keys, _nullEntry.Value.Key); + } + } + } + + /// + /// Gets an containing the values in the . + /// + /// + /// An containing the values in the object that : . + /// + public ICollection Values + { + get + { + if (_nullEntry == null) + { + return _baseDictionary.Values; + } + else + { + return new CollectionPlus(_baseDictionary.Values, _nullEntry.Value.Value); + } + } + } + } + + + + public class NullableValueTypeDictionary : IDictionary + where K : struct + { + /// + /// Underlying dictionary that handles real requests + /// + private readonly IDictionary _baseDictionary; + + /// + /// Value of the entry at the null key. + /// + private KeyValuePair? _nullEntry; + + /// + /// Gets the base dictionary. + /// + /// The base dictionary. + public IDictionary BaseDictionary + { + get { return _baseDictionary; } + } + + /// + /// Initializes a new instance of the class. + /// + /// The base dictionary. + public NullableValueTypeDictionary(IDictionary baseDictionary) + { + _baseDictionary = baseDictionary; + } + + /// + /// Returns an enumerator that iterates through a collection. + /// + /// + /// An object that can be used to iterate through the collection. + /// + IEnumerator IEnumerable.GetEnumerator() + { + if (_nullEntry != null) + { + yield return _nullEntry.Value; + } + + IEnumerator tempEnum = _baseDictionary.GetEnumerator(); + while (tempEnum.MoveNext()) + { + yield return tempEnum.Current; + } + } + + /// + /// Returns an enumerator that iterates through the collection. + /// + /// + /// A that can be used to iterate through the collection. + /// + public IEnumerator> GetEnumerator() + { + if (_nullEntry != null) + { + yield return _nullEntry.Value; + } + + var tempEnum = _baseDictionary.GetEnumerator(); + while (tempEnum.MoveNext()) + { + yield return tempEnum.Current; + } + } + + /// + /// Adds an item to the . + /// + /// The object to add to the . + /// + /// The is read-only. + /// + public void Add(KeyValuePair item) + { + if (item.Key == null) + { + _nullEntry = item; + } + else + { + _baseDictionary.Add(item); + } + } + + /// + /// Removes all items from the . + /// + /// + /// The is read-only. + /// + public void Clear() + { + _baseDictionary.Clear(); + _nullEntry = null; + } + + /// + /// Determines whether the contains a specific value. + /// + /// The object to locate in the . + /// + /// true if is found in the ; otherwise, false. + /// + public bool Contains(KeyValuePair item) + { + if (item.Key == null) + return _nullEntry != null; + return _baseDictionary.Contains(item); + } + + /// + /// Copies the elements of the to an , starting at a particular index. + /// + /// The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing. + /// The zero-based index in at which copying begins. + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + if (_nullEntry == null) + { + _baseDictionary.CopyTo(array, arrayIndex); + } + else + { + array[0] = _nullEntry.Value; + _baseDictionary.CopyTo(array, arrayIndex + 1); + } + } + + /// + /// Removes the first occurrence of a specific object from the . + /// + /// + /// true if was successfully removed from the ; otherwise, false. This method also returns false if is not found in the original . + /// + /// + /// The object to remove from the . + /// + /// + /// The is read-only. + /// + public bool Remove(KeyValuePair item) + { + if (item.Key == null) + { + if (_nullEntry != null) + { + _nullEntry = null; + return true; + } + + return false; + } + + return _baseDictionary.Remove(item); + } + + /// + /// Gets the count for the null entry. + /// + /// The null entry count. + public int NullEntryCount + { + get + { + return _nullEntry != null ? 1 : 0; + } + } + + /// + /// Gets the number of elements contained in the . + /// + /// + /// The number of elements contained in the . + /// + public int Count + { + get + { + return _baseDictionary.Count + NullEntryCount; + } + } + + /// + /// Gets a value indicating whether the is read-only. + /// + /// + /// true if the is read-only; otherwise, false. + /// + public bool IsReadOnly + { + get { return _baseDictionary.IsReadOnly; } + } + + /// + /// Determines whether the contains an element with the specified key. + /// + /// + /// true if the contains an element with the key; otherwise, false. + /// + /// + /// The key to locate in the . + /// + /// is null. + /// + public bool ContainsKey(K? key) + { + if (key == null) + { + return _nullEntry != null; + } + + return _baseDictionary.ContainsKey(key); + } + + /// + /// Adds an element with the provided key and value to the . + /// + /// + /// The object to use as the key of the element to add. + /// + /// + /// The object to use as the value of the element to add. + /// + /// is null. + /// + /// + /// An element with the same key already exists in the . + /// + /// + /// The is read-only. + /// + public void Add(K? key, V value) + { + if (key == null) + { + if (_nullEntry != null) + { + throw new ArgumentException("An element with the same key already exists"); + } + + _nullEntry = new KeyValuePair(null, value); + } + else + { + _baseDictionary.Add(key, value); + } + } + + /// + /// Removes the element with the specified key from the . + /// + /// + /// true if the element is successfully removed; otherwise, false. This method also returns false if was not found in the original . + /// + /// + /// The key of the element to remove. + /// + /// is null. + /// + /// + /// The is read-only. + /// + public bool Remove(K? key) + { + if (key == null) + { + if (_nullEntry != null) + { + _nullEntry = null; + return true; + } + + return false; + } + else + { + return _baseDictionary.Remove(key); + } + } + + /// + /// Gets the value associated with the specified key. + /// + /// + /// true if the object that : contains an element with the specified key; otherwise, false. + /// + /// + /// The key whose value to get. + /// + /// + /// When this method returns, the value associated with the specified key, if the key is found; otherwise, the default value for the type of the parameter. This parameter is passed uninitialized. + /// + /// is null. + /// + public bool TryGetValue(K? key, out V value) + { + if (key == null) + { + if (_nullEntry != null) + { + value = _nullEntry.Value.Value; + return true; + } + + value = default(V); + return false; + } + else + { + return _baseDictionary.TryGetValue(key, out value); + } + } + + /// + /// Gets or sets the element with the specified key. + /// + /// + /// The element with the specified key. + /// + /// + /// The key of the element to get or set. + /// + /// is null. + /// + /// + /// The property is retrieved and is not found. + /// + /// + /// The property is set and the is read-only. + /// + public V this[K? key] + { + get + { + if (key == null) + { + if (_nullEntry != null) + { + return _nullEntry.Value.Value; + } + + throw new KeyNotFoundException(); + } + else + { + return _baseDictionary[key]; + } + } + + set + { + if (key == null) + { + _nullEntry = new KeyValuePair(null, value); + } + else + { + _baseDictionary[key] = value; + } + } + } + + /// + /// Gets an containing the keys of the . + /// + /// + /// An containing the keys of the object that : . + /// + public ICollection Keys + { + get + { + if (_nullEntry == null) + { + return _baseDictionary.Keys; + } + else + { + return new CollectionPlus(_baseDictionary.Keys, _nullEntry.Value.Key); + } + } + } + + /// + /// Gets an containing the values in the . + /// + /// + /// An containing the values in the object that : . + /// + public ICollection Values + { + get + { + if (_nullEntry == null) + { + return _baseDictionary.Values; + } + else + { + return new CollectionPlus(_baseDictionary.Values, _nullEntry.Value.Value); + } + } + } + } + +} diff --git a/NEsper.Core/NEsper.Core/compat/collections/ObjectMap.cs b/NEsper.Core/NEsper.Core/compat/collections/ObjectMap.cs new file mode 100755 index 000000000..51386b4e1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/collections/ObjectMap.cs @@ -0,0 +1,16 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +namespace com.espertech.esper.compat.collections +{ + public class ObjectMap : Dictionary + { + } +} diff --git a/NEsper.Core/NEsper.Core/compat/collections/ObsoleteConcurrentDictionary.cs b/NEsper.Core/NEsper.Core/compat/collections/ObsoleteConcurrentDictionary.cs new file mode 100755 index 000000000..b98587596 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/collections/ObsoleteConcurrentDictionary.cs @@ -0,0 +1,329 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections; +using System.Collections.Generic; +using com.espertech.esper.compat.threading; + +namespace com.espertech.esper.compat.collections +{ + public class ObsoleteConcurrentDictionary : IDictionary + { + private readonly IDictionary _subDictionary; + private readonly IReaderWriterLock _rwLock; + private readonly ILockable _rLock; + private readonly ILockable _wLock; + + /// + /// Initializes a new instance of the class. + /// + public ObsoleteConcurrentDictionary() + { + _subDictionary = new Dictionary(); + _rwLock = ReaderWriterLockManager.CreateLock(GetType()); + _rLock = _rwLock.ReadLock; + _wLock = _rwLock.WriteLock; + } + + /// + /// Initializes a new instance of the class. + /// + /// The sub dictionary. + public ObsoleteConcurrentDictionary(IDictionary subDictionary) + { + _subDictionary = subDictionary; + _rwLock = ReaderWriterLockManager.CreateLock(GetType()); + _rLock = _rwLock.ReadLock; + _wLock = _rwLock.WriteLock; + } + + /// + /// Returns an enumerator that iterates through a collection. + /// + /// + /// An object that can be used to iterate through the collection. + /// + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + /// + /// Returns an enumerator that iterates through the collection. + /// + /// + /// A that can be used to iterate through the collection. + /// + public IEnumerator> GetEnumerator() + { + // Iteration over the dictionary causes a read lock to be acquired + // for the duration of the life of the enumeration. Use with care + // and we really need to measure the impact of this to determine if + // we need to use a copy on write structure. + + using( _rLock.Acquire() ) { + foreach( var entry in _subDictionary ) { + yield return entry; + } + } + } + + /// + /// Adds an item to the . + /// + /// The object to add to the . + /// + /// The is read-only. + /// + public void Add(KeyValuePair item) + { + using (_wLock.Acquire()) + { + ICollection> asCollection = _subDictionary; + asCollection.Add(item); + } + } + + /// + /// Removes all items from the . + /// + /// + /// The is read-only. + /// + public void Clear() + { + using (_wLock.Acquire()) + { + _subDictionary.Clear(); + } + } + + /// + /// Determines whether the contains a specific value. + /// + /// + /// true if is found in the ; otherwise, false. + /// + /// + /// The object to locate in the . + /// + public bool Contains(KeyValuePair item) + { + using (_rLock.Acquire()) + { + return _subDictionary.Contains(item); + } + } + + /// + /// Copies the elements of the to an , starting at a particular index. + /// + /// The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing. + /// The zero-based index in at which copying begins. + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + using (_rLock.Acquire()) + { + ICollection> asCollection = _subDictionary; + asCollection.CopyTo(array, arrayIndex); + } + } + + /// + /// Removes the first occurrence of a specific object from the . + /// + /// + /// true if was successfully removed from the ; otherwise, false. This method also returns false if is not found in the original . + /// + /// + /// The object to remove from the . + /// + /// + /// The is read-only. + /// + public bool Remove(KeyValuePair item) + { + using (_wLock.Acquire()) + { + return _subDictionary.Remove(item.Key); + } + } + + /// + /// Gets the number of elements contained in the . + /// + /// + /// The number of elements contained in the . + /// + public int Count + { + get + { + using (_rLock.Acquire()) { + return _subDictionary.Count; + } + } + } + + /// + /// Gets a value indicating whether the is read-only. + /// + /// + /// true if the is read-only; otherwise, false. + /// + public bool IsReadOnly + { + get { return false; } + } + + /// + /// Determines whether the contains an element with the specified key. + /// + /// The key to locate in the . + /// + /// true if the contains an element with the key; otherwise, false. + /// + /// is null. + /// + public bool ContainsKey(K key) + { + using (_rLock.Acquire()) + { + return _subDictionary.ContainsKey(key); + } + } + + /// + /// Adds an element with the provided key and value to the . + /// + /// The object to use as the key of the element to add. + /// The object to use as the value of the element to add. + /// is null. + /// + /// + /// An element with the same key already exists in the . + /// + /// + /// The is read-only. + /// + public void Add(K key, V value) + { + using (_wLock.Acquire()) + { + _subDictionary.Add(key, value); + } + } + + /// + /// Removes the element with the specified key from the . + /// + /// The key of the element to remove. + /// + /// true if the element is successfully removed; otherwise, false. This method also returns false if was not found in the original . + /// + /// is null. + /// + /// + /// The is read-only. + /// + public bool Remove(K key) + { + using (_wLock.Acquire()) + { + return _subDictionary.Remove(key); + } + } + + /// + /// Gets the value associated with the specified key. + /// + /// The key whose value to get. + /// When this method returns, the value associated with the specified key, if the key is found; otherwise, the default value for the type of the parameter. This parameter is passed uninitialized. + /// + /// true if the object that : contains an element with the specified key; otherwise, false. + /// + /// is null. + /// + public bool TryGetValue(K key, out V value) + { + using (_rLock.Acquire()) + { + return _subDictionary.TryGetValue(key, out value); + } + } + + /// + /// Gets or sets the element with the specified key. + /// + /// + /// + /// The element with the specified key. + /// + /// is null. + /// + /// + /// The property is retrieved and is not found. + /// + /// + /// The property is set and the is read-only. + /// + public V this[K key] + { + get + { + using (_rLock.Acquire()) + { + return _subDictionary[key]; + } + } + set + { + using (_wLock.Acquire()) + { + _subDictionary[key] = value; + } + } + } + + /// + /// Gets an containing the keys of the . + /// + /// + /// + /// An containing the keys of the object that : . + /// + public ICollection Keys + { + get + { + using (_rLock.Acquire()) + { + return _subDictionary.Keys; + } + } + } + + /// + /// Gets an containing the values in the . + /// + /// + /// + /// An containing the values in the object that : . + /// + public ICollection Values + { + get + { + using (_rLock.Acquire()) + { + return _subDictionary.Values; + } + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/collections/OrderedDictionary.cs b/NEsper.Core/NEsper.Core/compat/collections/OrderedDictionary.cs new file mode 100755 index 000000000..e5f32d648 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/collections/OrderedDictionary.cs @@ -0,0 +1,588 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections; +using System.Collections.Generic; + +namespace com.espertech.esper.compat.collections +{ + public class OrderedDictionary : IDictionary + { + /// + /// Value List + /// + private readonly List _keyList; + /// + /// Value list + /// + private readonly List _valList; + /// + /// Value comparer + /// + private readonly IComparer _keyComparer; + + /// + /// Gets the key comparer. + /// + /// The key comparer. + public IComparer KeyComparer + { + get { return _keyComparer; } + } + + /// + /// Initializes a new instance of the class. + /// + /// The key list. + /// The val list. + /// The comparer. + internal OrderedDictionary(List keyList, + List valList, + IComparer comparer) + { + _keyList = keyList; + _valList = valList; + _keyComparer = comparer; + } + + /// + /// Initializes a new instance of the class. + /// + /// The key comparer. + public OrderedDictionary(IComparer keyComparer) + { + _keyList = new List(); + _valList = new List(); + _keyComparer = keyComparer; + } + + /// + /// Initializes a new instance of the class. + /// + public OrderedDictionary() + { + _keyList = new List(); + _valList = new List(); + _keyComparer = null; + } + + /// + /// Returns an enumerator that iterates through a collection. + /// + /// + /// An object that can be used to iterate through the collection. + /// + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + /// + /// Gets the enumerator. + /// + /// + public IEnumerator> GetEnumerator() + { + for (int ii = 0; ii < _keyList.Count; ii++) + { + yield return new KeyValuePair( + _keyList[ii], + _valList[ii]); + } + } + + public IEnumerator> GetEnumerator(TK startKey, bool isInclusive) + { + int index = GetHeadIndex(startKey, isInclusive); + if (index == -1) + index = 0; + + for (int ii = index; ii < _keyList.Count; ii++) + { + yield return new KeyValuePair( + _keyList[ii], + _valList[ii]); + } + } + + public void ForEach(Action> action) + { + for (int ii = 0; ii < _keyList.Count; ii++) + { + action.Invoke( + new KeyValuePair( + _keyList[ii], + _valList[ii])); + } + } + + public void ForEach(Action> action) + { + for (int ii = 0; ii < _keyList.Count; ii++) + { + action.Invoke( + ii, new KeyValuePair( + _keyList[ii], + _valList[ii])); + } + } + + /// + /// Searches the list for a given key. The algorithm leverages the binary + /// search routine built into the class libraries. + /// + /// The key. + /// + internal int BinarySearch(TK key) + { + return _keyComparer != null + ? _keyList.BinarySearch(key, _keyComparer) + : _keyList.BinarySearch(key); + } + + /// + /// Adds the specified item. + /// + /// The item. + public void Add(KeyValuePair item) + { + var index = BinarySearch(item.Key); + if (index >= 0) + { + throw new ArgumentException("An element with the same key already exists"); + } + else + { + _keyList.Insert(~index, item.Key); + _valList.Insert(~index, item.Value); + } + } + + /// + /// Clears this instance. + /// + public void Clear() + { + _keyList.Clear(); + _valList.Clear(); + } + + /// + /// Determines whether [contains] [the specified item]. + /// + /// The item. + /// + /// true if [contains] [the specified item]; otherwise, false. + /// + public bool Contains(KeyValuePair item) + { + var index = BinarySearch(item.Key); + return (index >= 0); + } + + /// + /// Copies the array to a target. + /// + /// The array. + /// MapIndex of the array. + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + int arrayLength = array.Length; + for (int ii = arrayIndex, listIndex = 0; ii < arrayLength && listIndex < _keyList.Count ; ii++, listIndex++ ) + { + array[ii] = new KeyValuePair( + _keyList[listIndex], + _valList[listIndex]); + } + } + + /// + /// Removes the specified item. + /// + /// The item. + /// + public bool Remove(KeyValuePair item) + { + var index = BinarySearch(item.Key); + if (index < 0) + return false; + _keyList.RemoveAt(index); + _valList.RemoveAt(index); + return true; + } + + /// + /// Gets the count. + /// + /// The count. + public int Count + { + get { return _keyList.Count; } + } + + /// + /// Gets a value indicating whether this instance is read only. + /// + /// + /// true if this instance is read only; otherwise, false. + /// + public bool IsReadOnly + { + get { return false; } + } + + /// + /// Determines whether the specified key contains key. + /// + /// The key. + /// + /// true if the specified key contains key; otherwise, false. + /// + public bool ContainsKey(TK key) + { + var index = BinarySearch(key); + return (index >= 0); + } + + /// + /// Adds the specified key. + /// + /// The key. + /// The value. + public void Add(TK key, TV value) + { + Add(new KeyValuePair(key, value)); + } + + /// + /// Removes the specified key. + /// + /// The key. + /// + public bool Remove(TK key) + { + return Remove(new KeyValuePair(key, default(TV))); + } + + /// + /// Gets the value associated with the specified key. + /// + /// The key. + /// The value. + /// + public bool TryGetValue(TK key, out TV value) + { + var index = BinarySearch(key); + if (index >= 0) + { + value = _valList[index]; + return true; + } + + value = default(TV); + return false; + } + + public TV TryInsert(TK key, Func valueFactory) + { + var index = BinarySearch(key); + if (index >= 0) + { + return _valList[index]; + } + + var value = valueFactory.Invoke(); + + _keyList.Insert(~index, key); + _valList.Insert(~index, value); + + return value; + } + + /// + /// Gets or sets the value with the specified key. + /// + /// + public TV this[TK key] + { + get + { + var index = BinarySearch(key); + if (index >= 0) + { + return _valList[index]; + } + + throw new KeyNotFoundException(); + } + set + { + var index = BinarySearch(key); + if (index >= 0) + { + _valList[index] = value; + } + else + { + _keyList.Insert(~index, key); + _valList.Insert(~index, value); + } + } + } + + /// + /// Gets the keys. + /// + /// The keys. + public ICollection Keys + { + get { return _keyList; } + } + + /// + /// Gets the values. + /// + /// The values. + public ICollection Values + { + get { return _valList; } + } + + /// + /// Returns a dictionary that includes everything up to the specified value. + /// Whether the value is included in the range depends on whether the isInclusive + /// flag is set. + /// + /// The value. + /// if set to true [is inclusive]. + /// + public IDictionary Head(TK value, bool isInclusive = false) + { + return new SubmapDictionary( + this, + new SubmapDictionary.Bound { HasValue = false }, + new SubmapDictionary.Bound { HasValue = true, IsInclusive = isInclusive, Key = value }); + + } + + /// + /// Gets the index that should be used for an inclusive or exclusive search + /// ending at the head index. + /// + /// The start value. + /// if set to true [is inclusive]. + /// + public int GetHeadIndex(TK value, bool isInclusive) + { + int headIndex = BinarySearch(value); + if (headIndex >= 0) // key found + { + if (isInclusive == false) + { + headIndex--; + } + } + else + { + headIndex = ~headIndex - 1; + } + return headIndex; + } + + public IEnumerable> GetTail(TK value, bool isInclusive) + { + int tailIndex = GetTailIndex(value, isInclusive); + if (tailIndex != -1) + { + int count = Count; + for( ; tailIndex < count ; tailIndex++ ) + { + yield return new KeyValuePair( + _keyList[tailIndex], + _valList[tailIndex]); + } + } + } + + /// + /// Returns a dictionary that includes everything after the value. + /// Whether the value is included in the range depends on whether the isInclusive + /// flag is set. + /// + /// The end value. + /// if set to true [is inclusive]. + /// + public IDictionary Tail(TK value, bool isInclusive = true) + { + return new SubmapDictionary( + this, + new SubmapDictionary.Bound { HasValue = true, IsInclusive = isInclusive, Key = value }, + new SubmapDictionary.Bound { HasValue = false }); + } + + /// + /// Gets the index that should be used for an inclusive or exclusive search + /// starting from tail index. + /// + /// The end value. + /// if set to true [is inclusive]. + /// + public int GetTailIndex(TK value, bool isInclusive) + { + int tailIndex = BinarySearch(value); + if (tailIndex >= 0) // key found + { + if (isInclusive == false) + { + tailIndex++; + } + } + else + { + tailIndex = ~tailIndex; + } + return tailIndex; + } + + public IEnumerable> EnumerateBetween(TK startValue, bool isStartInclusive, TK endValue, bool isEndInclusive) + { + if (_keyComparer != null) + { + if (_keyComparer.Compare(startValue, endValue) > 0) + { + throw new ArgumentException("invalid key order"); + } + } + else + { + var aa = (IComparable) startValue; + var bb = (IComparable) endValue; + if (aa.CompareTo(bb) > 0) + { + throw new ArgumentException("invalid key order"); + } + } + + int tailIndex = GetHeadIndex(endValue, isEndInclusive); + if (tailIndex != -1) + { + int headIndex = GetTailIndex(startValue, isStartInclusive); + if (headIndex != -1) + { + for (int ii = headIndex; ii <= tailIndex; ii++ ) + { + yield return new KeyValuePair(_keyList[ii], _valList[ii]); + } + } + } + } + + public IDictionary Between(TK startValue, bool isStartInclusive, TK endValue, bool isEndInclusive) + { + if (_keyComparer != null) + { + if (_keyComparer.Compare(startValue, endValue) > 0) + { + throw new ArgumentException("invalid key order"); + } + } + else + { + var aa = (IComparable) startValue; + var bb = (IComparable) endValue; + if (aa.CompareTo(bb) > 0) + { + throw new ArgumentException("invalid key order"); + } + } + + return new SubmapDictionary( + this, + new SubmapDictionary.Bound { HasValue = true, IsInclusive = isStartInclusive, Key = startValue }, + new SubmapDictionary.Bound { HasValue = true, IsInclusive = isEndInclusive, Key = endValue }); + } + + public int CountBetween(TK startValue, bool isStartInclusive, TK endValue, bool isEndInclusive) + { + if (_keyComparer != null) + { + if (_keyComparer.Compare(startValue, endValue) > 0) + { + throw new ArgumentException("invalid key order"); + } + } + else + { + var aa = (IComparable)startValue; + var bb = (IComparable)endValue; + if (aa.CompareTo(bb) > 0) + { + throw new ArgumentException("invalid key order"); + } + } + + int tailIndex = GetHeadIndex(endValue, isEndInclusive); + if (tailIndex != -1) + { + int headIndex = GetTailIndex(startValue, isStartInclusive); + if (headIndex != -1) + { + return tailIndex - headIndex + 1; + } + } + + return 0; + } + + public OrderedDictionary Invert() + { + var comparer = _keyComparer; + var inverted = new StandardComparer( + (a, b) => -comparer.Compare(a, b)); + + var invertedKeyList = new List(_keyList); + var invertedValList = new List(_valList); + + invertedKeyList.Reverse(); + invertedValList.Reverse(); + + return new OrderedDictionary( + invertedKeyList, + invertedValList, + inverted); + } + } + +#if false + public class DebugCount + { + public static int TailCount; + public static int GetTailIndexCount; + public static int TryGetValueCount; + public static int GetTailCount; + public static int GetEnumeratorCount; + public static int IndexSetCount; + public static int IndexGetCount; + + public static string DebugString() + { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.AppendFormat("TailCount: {0}", TailCount); + stringBuilder.AppendFormat(", GetTailIndexCount: {0}", GetTailIndexCount); + stringBuilder.AppendFormat(", TryGetValueCount: {0}", TryGetValueCount); + stringBuilder.AppendFormat(", GetEnumeratorCount: {0}", GetEnumeratorCount); + stringBuilder.AppendFormat(", GetTailCount: {0}", GetTailCount); + stringBuilder.AppendFormat(", IndexGetCount: {0}", IndexGetCount); + stringBuilder.AppendFormat(", IndexSetCount: {0}", IndexSetCount); + + return stringBuilder.ToString(); + } + } +#endif +} diff --git a/NEsper.Core/NEsper.Core/compat/collections/ProxyComparer.cs b/NEsper.Core/NEsper.Core/compat/collections/ProxyComparer.cs new file mode 100755 index 000000000..d715f2cc3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/collections/ProxyComparer.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.compat.collections +{ + public class ProxyComparer : IComparer + { + public Func ProcCompare { get; set; } + + /// + /// Initializes a new instance of the class. + /// + public ProxyComparer() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The procCompare. + public ProxyComparer(Func procCompare) + { + ProcCompare = procCompare; + } + + /// + /// Compares two objects and returns a value indicating whether one is less than, equal to, or greater than the other. + /// + /// + /// Value + /// Condition + /// Less than zero + /// is less than . + /// Zero + /// equals . + /// Greater than zero + /// is greater than . + /// + /// The first object to compare. + /// The second object to compare. + /// + public int Compare(T x, T y) + { + return ProcCompare.Invoke(x, y); + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/collections/ProxyEnumerable.cs b/NEsper.Core/NEsper.Core/compat/collections/ProxyEnumerable.cs new file mode 100755 index 000000000..7b9455ce0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/collections/ProxyEnumerable.cs @@ -0,0 +1,67 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections; +using System.Collections.Generic; + +namespace com.espertech.esper.compat.collections +{ + /// + /// An enumerable that leverages a function. + /// + /// + public class ProxyEnumerable : IEnumerable + { + /// + /// Gets or sets the proc enumerator. + /// + /// + /// The proc enumerator. + /// + public Func> ProcEnumerator { get; set; } + + /// + /// Initializes a new instance of the class. + /// + public ProxyEnumerable() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The proc enumerator. + public ProxyEnumerable(Func> procEnumerator) + { + ProcEnumerator = procEnumerator; + } + + /// + /// Returns an enumerator that iterates through a collection. + /// + /// + /// An object that can be used to iterate through the collection. + /// + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + /// + /// Returns an enumerator that iterates through the collection. + /// + /// + /// A that can be used to iterate through the collection. + /// + public IEnumerator GetEnumerator() + { + return ProcEnumerator.Invoke(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/collections/ReadOnlyCollection.cs b/NEsper.Core/NEsper.Core/compat/collections/ReadOnlyCollection.cs new file mode 100755 index 000000000..2e4ccefa6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/collections/ReadOnlyCollection.cs @@ -0,0 +1,153 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.compat.collections +{ + /// + /// A wrapper that provide a list that is readonly. + /// + /// + + public class ReadOnlyCollection : ICollection + { + private readonly ICollection _parent; + + /// + /// Initializes a new instance of the class. + /// + /// The parent list. + public ReadOnlyCollection(ICollection parentList) + { + _parent = parentList; + } + + /// + /// Gets the number of elements contained in the . + /// + /// + /// The number of elements contained in the . + public int Count { + get { return _parent.Count; } + } + + /// + /// Gets a value indicating whether the is read-only. + /// + /// + /// true if the is read-only; otherwise, false. + public bool IsReadOnly { + get { return true ; } + } + + /// + /// Inserts an item to the at the specified index. + /// + /// The zero-based index at which item should be inserted. + /// The object to insert into the . + /// The is read-only. + /// index is not a valid index in the . + public void Insert(int index, T item) + { + throw new NotSupportedException() ; + } + + /// + /// Removes the item at the specified index. + /// + /// The zero-based index of the item to remove. + /// The is read-only. + /// index is not a valid index in the . + public void RemoveAt(int index) + { + throw new NotSupportedException() ; + } + + /// + /// Adds an item to the . + /// + /// The object to add to the . + /// The is read-only. + public void Add(T item) + { + throw new NotSupportedException() ; + } + + /// + /// Removes all items from the . + /// + /// The is read-only. + public void Clear() + { + throw new NotSupportedException() ; + } + + /// + /// Determines whether the contains a specific value. + /// + /// The object to locate in the . + /// + /// true if item is found in the ; otherwise, false. + /// + public bool Contains(T item) + { + return _parent.Contains( item ) ; + } + + /// + /// Copies the elements of the to an , starting at a particular index. + /// + /// The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing. + /// The zero-based index in array at which copying begins. + /// arrayIndex is less than 0. + /// array is null. + /// array is multidimensional.-or-arrayIndex is equal to or greater than the length of array.-or-The number of elements in the source is greater than the available space from arrayIndex to the end of the destination array.-or-Type T cannot be cast automatically to the type of the destination array. + public void CopyTo(T[] array, int arrayIndex) + { + _parent.CopyTo( array, arrayIndex ) ; + } + + /// + /// Removes the first occurrence of a specific object from the . + /// + /// The object to remove from the . + /// + /// true if item was successfully removed from the ; otherwise, false. This method also returns false if item is not found in the original . + /// + /// The is read-only. + public bool Remove(T item) + { + throw new NotSupportedException() ; + } + + /// + /// Returns an enumerator that iterates through the collection. + /// + /// + /// A that can be used to iterate through the collection. + /// + public IEnumerator GetEnumerator() + { + return _parent.GetEnumerator() ; + } + + /// + /// Returns an enumerator that iterates through a collection. + /// + /// + /// An object that can be used to iterate through the collection. + /// + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return _parent.GetEnumerator() ; + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/collections/ReadOnlyList.cs b/NEsper.Core/NEsper.Core/compat/collections/ReadOnlyList.cs new file mode 100755 index 000000000..3cfff4a83 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/collections/ReadOnlyList.cs @@ -0,0 +1,175 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.compat.collections +{ + /// + /// A wrapper that provide a list that is readonly. + /// + /// + + public class ReadOnlyList : IList + { + private readonly IList _parent; + + /// + /// Initializes a new instance of the class. + /// + /// The parent list. + public ReadOnlyList( IList parentList ) + { + _parent = parentList; + } + + /// + /// Gets or sets the item at the specified index. + /// + /// + public T this[int index] + { + get { return _parent[index] ; } + set { throw new NotSupportedException() ; } + } + + /// + /// Gets the number of elements contained in the . + /// + /// + /// The number of elements contained in the . + public int Count { + get { return _parent.Count; } + } + + /// + /// Gets a value indicating whether the is read-only. + /// + /// + /// true if the is read-only; otherwise, false. + public bool IsReadOnly { + get { return true ; } + } + + /// + /// Determines the index of a specific item in the . + /// + /// The object to locate in the . + /// + /// The index of item if found in the list; otherwise, -1. + /// + public int IndexOf(T item) + { + return _parent.IndexOf( item ) ; + } + + /// + /// Inserts an item to the at the specified index. + /// + /// The zero-based index at which item should be inserted. + /// The object to insert into the . + /// The is read-only. + /// index is not a valid index in the . + public void Insert(int index, T item) + { + throw new NotSupportedException() ; + } + + /// + /// Removes the item at the specified index. + /// + /// The zero-based index of the item to remove. + /// The is read-only. + /// index is not a valid index in the . + public void RemoveAt(int index) + { + throw new NotSupportedException() ; + } + + /// + /// Adds an item to the . + /// + /// The object to add to the . + /// The is read-only. + public void Add(T item) + { + throw new NotSupportedException() ; + } + + /// + /// Removes all items from the . + /// + /// The is read-only. + public void Clear() + { + throw new NotSupportedException() ; + } + + /// + /// Determines whether the contains a specific value. + /// + /// The object to locate in the . + /// + /// true if item is found in the ; otherwise, false. + /// + public bool Contains(T item) + { + return _parent.Contains( item ) ; + } + + /// + /// Copies the elements of the to an , starting at a particular index. + /// + /// The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing. + /// The zero-based index in array at which copying begins. + /// arrayIndex is less than 0. + /// array is null. + /// array is multidimensional.-or-arrayIndex is equal to or greater than the length of array.-or-The number of elements in the source is greater than the available space from arrayIndex to the end of the destination array.-or-Type T cannot be cast automatically to the type of the destination array. + public void CopyTo(T[] array, int arrayIndex) + { + _parent.CopyTo( array, arrayIndex ) ; + } + + /// + /// Removes the first occurrence of a specific object from the . + /// + /// The object to remove from the . + /// + /// true if item was successfully removed from the ; otherwise, false. This method also returns false if item is not found in the original . + /// + /// The is read-only. + public bool Remove(T item) + { + throw new NotSupportedException() ; + } + + /// + /// Returns an enumerator that iterates through the collection. + /// + /// + /// A that can be used to iterate through the collection. + /// + public IEnumerator GetEnumerator() + { + return _parent.GetEnumerator() ; + } + + /// + /// Returns an enumerator that iterates through a collection. + /// + /// + /// An object that can be used to iterate through the collection. + /// + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return _parent.GetEnumerator() ; + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/collections/ReferenceMap.cs b/NEsper.Core/NEsper.Core/compat/collections/ReferenceMap.cs new file mode 100755 index 000000000..8cb803439 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/collections/ReferenceMap.cs @@ -0,0 +1,778 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.compat.collections +{ + public sealed class ReferenceMap : IDictionary + where TKey : class + where TValue : class + { + /// + /// Underlying dictionary must be opaque to match the semantics of + /// the reference types. + /// + private readonly Dictionary _dictionary; + + /// + /// Defines the way that keys are maintained in the dictionary + /// + private ReferenceType _keyReferenceType; + private readonly IReferenceAdapter _keyAdapter; + + /// + /// Defines the way that values are maintained in the dictionary + /// + private ReferenceType _valueReferenceType; + private readonly IReferenceAdapter _valueAdapter; + + /// + /// List of dictionary keys that need to be removed + /// + private readonly List _pruneList; + + /// + /// Initializes a new instance of the class. + /// + /// Type of the key reference. + /// Type of the value reference. + public ReferenceMap( ReferenceType keyReferenceType, + ReferenceType valueReferenceType ) + : this( 101, keyReferenceType, valueReferenceType ) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The capacity. + /// Type of the key reference. + /// Type of the value reference. + public ReferenceMap( int capacity, + ReferenceType keyReferenceType, + ReferenceType valueReferenceType ) + { + _pruneList = new List(); + + _keyReferenceType = keyReferenceType; + _keyAdapter = + keyReferenceType == ReferenceType.HARD + ? (IReferenceAdapter) new HardReferenceAdapter() + : (IReferenceAdapter)new SoftReferenceAdapter(); + + _valueReferenceType = valueReferenceType; + _valueAdapter = + valueReferenceType == ReferenceType.HARD + ? (IReferenceAdapter)new HardReferenceAdapter() + : (IReferenceAdapter)new SoftReferenceAdapter(); + + // Create the dictionary + _dictionary = new Dictionary(capacity, _keyAdapter); + } + + /// + /// Gets the number of elements contained in the . + /// + /// + /// WARNING: The count returned here may include entries for which + /// either the key or value objects have already been garbage + /// collected. Call RemoveCollectedEntries to weed out collected + /// entries and Update the count accordingly. + /// + /// + /// The number of elements contained in the . + + public int Count + { + get + { + Prune(); // Clear out known dead items + return _dictionary.Count; + } + } + + /// + /// Adds an element with the provided key and value to the . + /// + /// The object to use as the key of the element to add. + /// The object to use as the value of the element to add. + /// The is read-only. + /// An element with the same key already exists in the . + /// key is null. + public void Add(TKey key, TValue value) + { + if (key == null) throw new ArgumentNullException("key"); + // Create the dictionary key + Object dictKey = _keyAdapter.ReferenceToDictionary(key); + // Create the dictionary value + Object dictValue = _valueAdapter.ReferenceToDictionary(value); + // Add them to the dictionary + _dictionary.Add(dictKey, dictValue); + } + + /// + /// Determines whether the contains an element with the specified key. + /// + /// The key to locate in the . + /// + /// true if the contains an element with the key; otherwise, false. + /// + /// key is null. + public bool ContainsKey(TKey key) + { + return _dictionary.ContainsKey(key); + } + + /// + /// Removes the element with the specified key from the . + /// + /// The key of the element to remove. + /// + /// true if the element is successfully removed; otherwise, false. This method also returns false if key was not found in the original . + /// + /// The is read-only. + /// key is null. + public bool Remove(TKey key) + { + return _dictionary.Remove(key); + } + + /// + /// Tries the get value. + /// + /// The key. + /// The value. + /// + public bool TryGetValue(TKey key, out TValue value) + { + Object tempKey = key ; + Object tempOut; + + if ( _dictionary.TryGetValue( tempKey, out tempOut ) ) + { + if (_valueAdapter.DictionaryToReference(tempOut, out value)) + { + return true; + } + + Prune(tempKey); + } + + value = default(TValue); + return false; + } + + /// + /// Sets the value. + /// + /// The key. + /// The value. + private void SetValue(TKey key, TValue value) + { + if (key == null) throw new ArgumentNullException("key"); + // Create the dictionary key + Object dictKey = _keyAdapter.ReferenceToDictionary(key); + // Create the dictionary value + Object dictValue = _valueAdapter.ReferenceToDictionary(value); + // Add them to the dictionary + _dictionary[dictKey] = dictValue; + } + + /// + /// Removes all items from the . + /// + /// The is read-only. + public void Clear() + { + _dictionary.Clear(); + } + + /// + /// Returns an enumerator that iterates through the collection. + /// + /// + /// A that can be used to iterate through the collection. + /// + public IEnumerator> GetEnumerator() + { + foreach (KeyValuePair entry in _dictionary) + { + TKey entryKey; + TValue entryValue; + + if ((!_keyAdapter.DictionaryToReference(entry.Key, out entryKey)) || + (!_valueAdapter.DictionaryToReference(entry.Value, out entryValue))) + { + Prune(entry.Key); + } + else + { + yield return new KeyValuePair(entryKey, entryValue); + } + } + } + + /// + /// Adds the specified dictionary key to the prune list. + /// + /// The dict key. + private void Prune( Object dictKey ) + { + lock( _pruneList ) + { + _pruneList.Add(dictKey); + } + } + + /// + /// Removes all 'dead' references that have been added to the + /// prune list. + /// + + public void Prune() + { + foreach (Object item in _pruneList) + { + _dictionary.Remove(item); + } + } + + /// + /// Removes the left-over weak references for entries in the dictionary + /// whose key or value has already been reclaimed by the garbage + /// collector. This will reduce the dictionary's Count by the number + /// of dead key-value pairs that were eliminated. + /// + public void Purge() + { + // Iterate over the collection; this will cause entries that are dead to + // be entered into the purgeList. + foreach( KeyValuePair entry in this ) {} + // Prune the tree + Prune(); + } + + /// + /// Gets an enumerator that enumerates the keys. + /// + /// The keys enum. + + public IEnumerator KeysEnum + { + get + { + foreach (KeyValuePair entry in this) + { + yield return entry.Key; + } + } + } + + #region IDictionary Members + + /// + /// Gets an containing the keys of the . + /// + /// + /// An containing the keys of the object that : . + public ICollection Keys + { + get + { + List keyList = new List() ; + foreach (KeyValuePair entry in this) + { + keyList.Add(entry.Key); + } + + return keyList ; + } + } + + /// + /// Gets an containing the values in the . + /// + /// + /// An containing the values in the object that : . + public ICollection Values + { + get + { + List valueList = new List(); + foreach (KeyValuePair entry in this) + { + valueList.Add(entry.Value); + } + + return valueList; + } + } + + /// + /// Gets or sets the item with the specified key. + /// + /// + public TValue this[TKey key] + { + get + { + TValue rvalue = null ; + if ( TryGetValue( key, out rvalue ) ) + { + return rvalue; + } + + throw new KeyNotFoundException( "Value '" + key + "' not found" ) ; + } + set + { + SetValue(key, value); + } + } + + #endregion + + #region ICollection> Members + + /// + /// Adds an item to the . + /// + /// The object to add to the . + /// The is read-only. + + public void Add(KeyValuePair item) + { + Add(item.Key, item.Value); + } + + /// + /// Determines whether the contains a specific value. + /// + /// The object to locate in the . + /// + /// true if item is found in the ; otherwise, false. + /// + + public bool Contains(KeyValuePair item) + { + TKey key = item.Key ; + TValue value ; + if ( TryGetValue( key, out value ) ) + { + return Object.Equals( value, item.Value ) ; + } + + return false; + } + + /// + /// Copies the elements of the to an , Starting at a particular index. + /// + /// The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing. + /// The zero-based index in array at which copying begins. + /// arrayIndex is less than 0. + /// array is null. + /// array is multidimensional.-or-arrayIndex is equal to or greater than the length of array.-or-The number of elements in the source is greater than the available space from arrayIndex to the end of the destination array.-or-Type T cannot be cast automatically to the type of the destination array. + + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + throw new Exception("The method or operation is not implemented."); + } + + /// + /// Gets a value indicating whether the is read-only. + /// + /// + /// true if the is read-only; otherwise, false. + + public bool IsReadOnly + { + get { return false; } + } + + /// + /// Removes the first occurrence of a specific object from the . + /// + /// The object to remove from the . + /// + /// true if item was successfully removed from the ; otherwise, false. This method also returns false if item is not found in the original . + /// + /// The is read-only. + + public bool Remove(KeyValuePair item) + { + return Remove(item.Key); + } + + #endregion + + #region IEnumerable Members + + /// + /// Returns an enumerator that iterates through a collection. + /// + /// + /// An object that can be used to iterate through the collection. + /// + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return this.GetEnumerator(); + } + + #endregion + + #region IDictionary Members + + /// + /// Fetches the value associated with the specified key. + /// If no value can be found, then the defaultValue is + /// returned. + /// + /// + /// + /// + public TValue Get(TKey key, TValue defaultValue) + { + TValue returnValue = defaultValue; + if (key != null) + { + if (!TryGetValue(key, out returnValue)) + { + returnValue = defaultValue; + } + } + return returnValue; + } + + /// + /// Fetches the value associated with the specified key. + /// If no value can be found, then default(V) is returned. + /// + /// + /// + public TValue Get(TKey key) + { + return Get(key, default(TValue)); + } + + /// + /// Sets the given key in the dictionary. If the key + /// already exists, then it is remapped to thenew value. + /// + /// + /// + public void Put(TKey key, TValue value) + { + this[key] = value; + } + + /// + /// Sets the given key in the dictionary. If the key + /// already exists, then it is remapped to the new value. + /// If a value was previously mapped it is returned. + /// + + public TValue Push(TKey key, TValue value) + { + TValue temp; + TryGetValue(key, out temp); + this[key] = value; + return temp; + } + + /// + /// Puts all values from the source dictionary into + /// this dictionary. + /// + /// + public void PutAll(IDictionary source) + { + foreach( KeyValuePair entry in source ) + { + this[entry.Key] = entry.Value; + } + } + + /// + /// Returns the first value in the enumeration of values + /// + /// + /// + public TValue FirstValue + { + get + { + IEnumerator> enumObj = GetEnumerator(); + return enumObj.MoveNext() + ? enumObj.Current.Value + : default(TValue); + } + } + + /// + /// Removes the item from the dictionary that is associated with + /// the specified key. + /// + /// Search key into the dictionary + /// The value removed from the dictionary (if found). + /// + public bool Remove(TKey key, out TValue value) + { + if (!TryGetValue(key, out value)) + { + return false; + } + + return Remove(key); + } + + /// + /// Removes the item from the dictionary that is associated with + /// the specified key. The item if found is returned; if not, + /// default(V) is returned. + /// + /// + /// + public TValue RemoveAndReturn(TKey key) + { + TValue tempItem; + + return Remove(key, out tempItem) + ? tempItem + : default(TValue); + } + + #endregion + + /// + /// Converts items from their reference for to their dictionary form and + /// vice-versa. + /// + /// + + internal interface IReferenceAdapter : IEqualityComparer + { + /// + /// Converts the item from a reference item to a dictionary item. + /// + /// The item. + /// + Object ReferenceToDictionary(T item); + + /// + /// Converts the item from a dictionary item to a reference item. + /// Returns true if the dictionary item is still alive. + /// + /// The item. + /// The reference item. + /// + bool DictionaryToReference(Object item, out T refItem); + } + + internal class HardReferenceAdapter : IReferenceAdapter where T : class + { + #region IReferenceAdapter Members + + /// + /// Converts the item from a reference item to a dictionary item. + /// + /// The item. + /// + public object ReferenceToDictionary(T item) + { + return item; + } + + /// + /// Converts the item from a dictionary item to a reference item. + /// + /// The item. + /// The reference item. + /// + public bool DictionaryToReference(Object item, out T refItem) + { + refItem = item as T; + return true; + } + + #endregion + + #region IEqualityComparer Members + + /// + /// Determines whether the specified objects are equal. + /// + /// The first object of type T to compare. + /// The second object of type T to compare. + /// + /// true if the specified objects are equal; otherwise, false. + /// + public new bool Equals(object x, object y) + { + return + Object.ReferenceEquals(x, y) || + Object.Equals(x, y); + } + + /// + /// Returns a hash code for the specified object. + /// + /// The for which a hash code is to be returned. + /// A hash code for the specified object. + /// The type of obj is a reference type and obj is null. + public int GetHashCode(object obj) + { + return obj.GetHashCode(); + } + + #endregion + } + + internal class SoftReferenceAdapter : IReferenceAdapter where T : class + { + #region IReferenceAdapter Members + + /// + /// Converts the item from a reference item to a dictionary item. + /// + /// The item. + /// + public object ReferenceToDictionary(T item) + { + return new WeakReference(item); + } + + /// + /// Converts the item from a dictionary item to a reference item. + /// + /// The item. + /// The reference item. + /// + public bool DictionaryToReference(Object item, out T refItem) + { + WeakReference reference = item as WeakReference; + if ((reference != null) && (reference.IsAlive)) + { + refItem = reference.Target as T; + return refItem != null; + } + + refItem = null; + return false; + } + #endregion + + #region IEqualityComparer Members + /// + /// Determines whether the specified is equal to the current . + /// + /// The first object of type T to compare. + /// The second object of type T to compare. + /// + /// true if the specified is equal to the current ; otherwise, false. + /// + /// + /// Note: There are actually 9 cases to handle here. + /// Let Wa = Alive Weak Reference + /// Let Wd = Dead Weak Reference + /// Let S = Strong Reference + /// x | y | Equals(x,y) + /// ------------------------------------------------- + /// Wa | Wa | comparer.Equals(x.Target, y.Target) + /// Wa | Wd | false + /// Wa | S | comparer.Equals(x.Target, y) + /// Wd | Wa | false + /// Wd | Wd | x == y + /// Wd | S | false + /// S | Wa | comparer.Equals(x, y.Target) + /// S | Wd | false + /// S | S | comparer.Equals(x, y) + /// ------------------------------------------------- + /// + public new bool Equals(object x, object y) + { + bool xIsDead, yIsDead; + T first = GetTarget(x, out xIsDead); + T second = GetTarget(y, out yIsDead); + + if (xIsDead) + return yIsDead ? x == y : false; + + if (yIsDead) + return false; + + return + Object.ReferenceEquals(first, second) || + Object.Equals(first, second); + } + + /// + /// Returns a hash code for the specified object. + /// + /// The for which a hash code is to be returned. + /// A hash code for the specified object. + /// The type of obj is a reference type and obj is null. + public int GetHashCode(object obj) + { + // Depending upon the path, the object that we are being passed could be + // an object in reference form or dictionary form. In reference form, it + // is just a plain old T; in dictionary form, it would be a WeakReference + + WeakReference refT = obj as WeakReference; + return (refT != null) + ? (refT.GetHashCode()) + : (obj.GetHashCode()); + } + + /// + /// Gets the target of the object. The target can only be a WeakReference of T or + /// T itself. This method distinguishes between the two and returns the actual + /// target object. Status of the target is returned through the out parameter. + /// + /// The obj. + /// if set to true [is dead]. + /// + private static T GetTarget(object obj, out bool isDead) + { + WeakReference wref = obj as WeakReference; + T target; + if (wref != null) + { + target = wref.Target; + isDead = !wref.IsAlive; + } + else + { + target = (T)obj; + isDead = false; + } + return target; + } + + #endregion + } + } + + public enum ReferenceType + { + /// + /// Hard references keep references to the object and prevent the + /// garbage collector from collecting the item. + /// + HARD, + /// + /// Soft references allow the garbage collector to collect items + /// that are not in use. + /// + SOFT + } +} diff --git a/NEsper.Core/NEsper.Core/compat/collections/SetUtil.cs b/NEsper.Core/NEsper.Core/compat/collections/SetUtil.cs new file mode 100755 index 000000000..6cef2adb1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/collections/SetUtil.cs @@ -0,0 +1,62 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System.Collections.Generic; + +namespace com.espertech.esper.compat.collections +{ + public class SetUtil + { + /// + /// Creates the union of two sets. + /// + /// + /// The set1. + /// The set2. + /// + public static ICollection Union(ICollection set1, ICollection set2) + { + ICollection iset = new HashSet(); + + iset.AddAll(set1); + iset.AddAll(set2); + + return iset; + } + + /// + /// Creates the intersection of two sets. + /// + /// + /// The set1. + /// The set2. + /// + public static ICollection Intersect( ICollection set1, ICollection set2 ) + { + ICollection iset = new HashSet(); + + if ((set1 != null) && (set2 != null)) { + // Reversed the sets if set1 is larger than set2 + if (set1.Count > set2.Count) { + ICollection temp = set1; + set2 = set1; + set1 = temp; + } + + foreach (T item in set2) { + if (set1.Contains(item)) { + iset.Add(item); + } + } + } + + return iset; + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/collections/SimpleComparer.cs b/NEsper.Core/NEsper.Core/compat/collections/SimpleComparer.cs new file mode 100755 index 000000000..bdd15906d --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/collections/SimpleComparer.cs @@ -0,0 +1,47 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.compat.collections +{ + public class SimpleComparer : StandardComparer + { + public static SimpleComparer Forward { get; private set; } + public static SimpleComparer Reverse { get; private set; } + + static SimpleComparer() + { + Forward = new SimpleComparer(true); + Reverse = new SimpleComparer(false); + } + + /// + /// Initializes a new instance of the class. + /// + public SimpleComparer(bool ascending) + : base(GetComparisonFunction(ascending)) + { + } + + /// + /// Gets the simple comparison function. + /// + /// if set to true [ascending]. + /// + public static Func GetComparisonFunction(bool ascending) + { + if (ascending) + { + return (x, y) => ((IComparable) x).CompareTo(y); + } + + return (x, y) => -((IComparable)x).CompareTo(y); + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/collections/Singleton.cs b/NEsper.Core/NEsper.Core/compat/collections/Singleton.cs new file mode 100755 index 000000000..8cb2b5999 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/collections/Singleton.cs @@ -0,0 +1,124 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections; +using System.Collections.Generic; + +namespace com.espertech.esper.compat.collections +{ + public class Singleton : ISet + { + private readonly T _item; + + public Singleton(T item) + { + _item = item; + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public IEnumerator GetEnumerator() + { + yield return _item; + } + + void ICollection.Add(T item) + { + throw new NotSupportedException(); + } + + public void Clear() + { + throw new NotSupportedException(); + } + + public bool Contains(T item) + { + return Equals(item, _item); + } + + public void CopyTo(T[] array, int arrayIndex) + { + array[arrayIndex] = _item; + } + + public bool Remove(T item) + { + throw new NotSupportedException(); + } + + public int Count + { + get { return 1; } + } + + public bool IsReadOnly + { + get { return true; } + } + + public bool Add(T item) + { + throw new NotSupportedException(); + } + + public void IntersectWith(IEnumerable other) + { + throw new NotSupportedException(); + } + + public void UnionWith(IEnumerable other) + { + throw new NotSupportedException(); + } + + public void ExceptWith(IEnumerable other) + { + throw new NotSupportedException(); + } + + public void SymmetricExceptWith(IEnumerable other) + { + throw new NotSupportedException(); + } + + public bool IsSubsetOf(IEnumerable other) + { + throw new NotSupportedException(); + } + + public bool IsSupersetOf(IEnumerable other) + { + throw new NotSupportedException(); + } + + public bool IsProperSupersetOf(IEnumerable other) + { + throw new NotSupportedException(); + } + + public bool IsProperSubsetOf(IEnumerable other) + { + throw new NotSupportedException(); + } + + public bool Overlaps(IEnumerable other) + { + throw new NotSupportedException(); + } + + public bool SetEquals(IEnumerable other) + { + throw new NotSupportedException(); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/compat/collections/StandardComparer.cs b/NEsper.Core/NEsper.Core/compat/collections/StandardComparer.cs new file mode 100755 index 000000000..df9c78568 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/collections/StandardComparer.cs @@ -0,0 +1,52 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.compat.collections +{ + public class StandardComparer : IComparer + { + private readonly Func _finalComparison; + + /// + /// Initializes a new instance of the class. + /// + /// The final comparison. + public StandardComparer(Func finalComparison) + { + _finalComparison = finalComparison; + } + + /// + /// Compares two objects and returns a value indicating whether one is less than, equal to, + /// or greater than the other. + /// + /// The first object to compare. + /// The second object to compare. + /// + /// Value + /// Condition + /// Less than zero + /// is less than . + /// Zero + /// equals . + /// Greater than zero + /// is greater than . + /// + public int Compare(T x, T y) + { + if (ReferenceEquals(x, y)) + return 0; + if (Equals(x, y)) + return 0; + return _finalComparison(x, y); + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/collections/StringDictionary.cs b/NEsper.Core/NEsper.Core/compat/collections/StringDictionary.cs new file mode 100755 index 000000000..a1e09948b --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/collections/StringDictionary.cs @@ -0,0 +1,310 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections; +using System.Collections.Generic; + +namespace com.espertech.esper.compat.collections +{ + public interface IStringDictionary : IDictionary + { + /// + /// Gets a value indicating whether the keys for the dictionary + /// are case sensitive. + /// + /// + /// true if this instance is case sensitive; otherwise, false. + /// + bool IsCaseSensitive { get; } + } + + public class StringDictionary : IStringDictionary + { + private readonly Func _normalizeKey; + private readonly IDictionary _subDictionary; + + /// + /// Initializes a new instance of the class. + /// + /// if set to true [is case sensitive]. + public StringDictionary(bool isCaseSensitive) + { + _subDictionary = new OrderedDictionary(); + IsCaseSensitive = isCaseSensitive; + if (isCaseSensitive) { + _normalizeKey = s => s; + } + else { + _normalizeKey = s => s.ToUpper(); + } + } + + /// + /// Initializes a new instance of the class. + /// + /// if set to true [is case sensitive]. + /// The source dictionary. + public StringDictionary(bool isCaseSensitive, IEnumerable> sourceDictionary) + : this(isCaseSensitive) + { + foreach( var entry in sourceDictionary ) + { + this[entry.Key] = entry.Value; + } + } + + #region Implementation of IEnumerable + + /// + /// Returns an enumerator that iterates through a collection. + /// + /// + /// An object that can be used to iterate through the collection. + /// + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + #endregion + + #region Implementation of IEnumerable> + + /// + /// Returns an enumerator that iterates through the collection. + /// + /// + /// A that can be used to iterate through the collection. + /// + public IEnumerator> GetEnumerator() + { + return _subDictionary.GetEnumerator(); + } + + #endregion + + #region Implementation of ICollection> + + /// + /// Adds an item to the . + /// + /// The object to add to the . + /// + /// The is read-only. + /// + public void Add(KeyValuePair item) + { + var temp = new KeyValuePair( + _normalizeKey(item.Key), item.Value); + _subDictionary.Add(temp); + } + + /// + /// Removes all items from the . + /// + /// + /// The is read-only. + /// + public void Clear() + { + _subDictionary.Clear(); + } + + /// + /// Determines whether the contains a specific value. + /// + /// The object to locate in the . + /// + /// true if is found in the ; otherwise, false. + /// + public bool Contains(KeyValuePair item) + { + var temp = new KeyValuePair( + _normalizeKey(item.Key), item.Value); + return _subDictionary.Contains(temp); + } + + /// + /// Copies the elements of the to an , starting at a particular index. + /// + /// The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing. + /// The zero-based index in at which copying begins. + /// + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + throw new NotSupportedException(); + } + + /// + /// Removes the first occurrence of a specific object from the . + /// + /// The object to remove from the . + /// + /// true if was successfully removed from the ; otherwise, false. This method also returns false if is not found in the original . + /// + /// + /// The is read-only. + /// + public bool Remove(KeyValuePair item) + { + var temp = new KeyValuePair( + _normalizeKey(item.Key), item.Value); + return _subDictionary.Remove(temp); + } + + /// + /// Gets the number of elements contained in the . + /// + /// + /// + /// The number of elements contained in the . + /// + public int Count + { + get { return _subDictionary.Count; } + } + + /// + /// Gets a value indicating whether the is read-only. + /// + /// + /// + /// true if the is read-only; otherwise, false. + /// + public bool IsReadOnly + { + get { return _subDictionary.IsReadOnly; } + } + + #endregion + + #region Implementation of IDictionary + + /// + /// Determines whether the contains an element with the specified key. + /// + /// The key to locate in the . + /// + /// true if the contains an element with the key; otherwise, false. + /// + /// is null. + /// + public bool ContainsKey(string key) + { + return _subDictionary.ContainsKey(_normalizeKey(key)); + } + + /// + /// Adds an element with the provided key and value to the . + /// + /// The object to use as the key of the element to add. + /// The object to use as the value of the element to add. + /// is null. + /// + /// + /// An element with the same key already exists in the . + /// + /// + /// The is read-only. + /// + public void Add(string key, V value) + { + _subDictionary.Add(_normalizeKey(key), value); + } + + /// + /// Removes the element with the specified key from the . + /// + /// The key of the element to remove. + /// + /// true if the element is successfully removed; otherwise, false. This method also returns false if was not found in the original . + /// + /// is null. + /// + /// + /// The is read-only. + /// + public bool Remove(string key) + { + return _subDictionary.Remove(_normalizeKey(key)); + } + + /// + /// Gets the value associated with the specified key. + /// + /// The key whose value to get. + /// When this method returns, the value associated with the specified key, if the key is found; otherwise, the default value for the type of the parameter. This parameter is passed uninitialized. + /// + /// true if the object that : contains an element with the specified key; otherwise, false. + /// + /// is null. + /// + public bool TryGetValue(string key, out V value) + { + return _subDictionary.TryGetValue(_normalizeKey(key), out value); + } + + /// + /// Gets or sets the element with the specified key. + /// + /// + /// + /// The element with the specified key. + /// + /// is null. + /// + /// + /// The property is retrieved and is not found. + /// + /// + /// The property is set and the is read-only. + /// + public V this[string key] + { + get { return _subDictionary[_normalizeKey(key)]; } + set { _subDictionary[_normalizeKey(key)] = value; } + } + + /// + /// Gets an containing the keys of the . + /// + /// + /// + /// An containing the keys of the object that : . + /// + public ICollection Keys + { + get { return _subDictionary.Keys; } + } + + /// + /// Gets an containing the values in the . + /// + /// + /// + /// An containing the values in the object that : . + /// + public ICollection Values + { + get { return _subDictionary.Values; } + } + + #endregion + + /// + /// Gets or sets a value indicating whether the keys for the dictionary + /// are case sensitive. + /// + /// + /// true if this instance is case sensitive; otherwise, false. + /// + public bool IsCaseSensitive { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/collections/StringMap.cs b/NEsper.Core/NEsper.Core/compat/collections/StringMap.cs new file mode 100755 index 000000000..44b354364 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/collections/StringMap.cs @@ -0,0 +1,16 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +namespace com.espertech.esper.compat.collections +{ + public class StringMap : Dictionary + { + } +} diff --git a/NEsper.Core/NEsper.Core/compat/collections/SubList.cs b/NEsper.Core/NEsper.Core/compat/collections/SubList.cs new file mode 100755 index 000000000..80bb5dc30 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/collections/SubList.cs @@ -0,0 +1,225 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace com.espertech.esper.compat.collections +{ + public class SubList : IList + { + private readonly IList _parent; + private readonly int _fromIndex; + private int _toIndex; + private int _count; + + /// + /// Initializes a new instance of the class. + /// + /// The parent. + /// From index. + /// To index. + public SubList(IList parent, int fromIndex, int toIndex) + { + _parent = parent; + _fromIndex = fromIndex; + _toIndex = toIndex; + _count = _toIndex - _fromIndex; + } + + /// + /// Gets or sets the element at the specified index. + /// + /// The index. + /// + /// + /// + public T this[int index] + { + get + { + BoundsCheck(index); + return _parent[_fromIndex + index]; + } + set + { + BoundsCheck(index); + _parent[_fromIndex + index] = value; + } + } + + /// + /// Checks the index to ensure it is within the bounds. + /// + /// The index. + /// + /// index exceeds bounds + /// or + /// index exceeds bounds + /// + private void BoundsCheck(int index) + { + if (index < 0) + throw new ArgumentException("index exceeds bounds"); + if (index >= _count) + throw new ArgumentException("index exceeds bounds"); + } + + /// + /// Adds an item to the . + /// + /// The object to add to the . + /// + public void Add(T item) + { + throw new NotSupportedException(); + } + + /// + /// Removes all items from the . + /// + /// + public void Clear() + { + throw new NotSupportedException(); + } + + /// + /// Determines whether the contains a specific value. + /// + /// The object to locate in the . + /// + /// true if is found in the ; otherwise, false. + /// + public bool Contains(T item) + { + for (int ii = _fromIndex; ii < _toIndex; ii++) + { + if (Equals(_parent[ii], item)) + { + return true; + } + } + + return false; + } + + /// + /// Copies to list to the array. + /// + /// The array. + /// Index of the array. + public void CopyTo(T[] array, int arrayIndex) + { + var arrayLen = array.Length; + if (arrayLen > _count) + arrayLen = _count; + + for (int ii = 0, nn = _fromIndex; ii < arrayLen; ii++, nn++) + { + array[ii] = _parent[nn]; + } + } + + /// + /// Removes the first occurrence of a specific object from the . + /// + /// The object to remove from the . + /// + /// true if was successfully removed from the ; otherwise, false. This method also returns false if is not found in the original . + /// + /// + public bool Remove(T item) + { + throw new NotSupportedException(); + } + + /// + /// Gets the number of elements contained in the . + /// + public int Count + { + get { return _count; } + } + + /// + /// Gets a value indicating whether the is read-only. + /// + public bool IsReadOnly + { + get { return false; } + } + + /// + /// Determines the index of a specific item in the . + /// + /// The object to locate in the . + /// + /// The index of if found in the list; otherwise, -1. + /// + /// + public int IndexOf(T item) + { + for (int ii = _fromIndex; ii < _toIndex; ii++) + { + if (Equals(_parent[ii], item)) + { + return ii - _fromIndex; + } + } + + return -1; + } + + /// + /// Inserts an item to the at the specified index. + /// + /// The zero-based index at which should be inserted. + /// The object to insert into the . + /// + public void Insert(int index, T item) + { + BoundsCheck(index); + _parent.Insert(_fromIndex + index, item); + _toIndex++; + _count++; + } + + /// + /// Removes the item at the specified index. + /// + /// The zero-based index of the item to remove. + /// + public void RemoveAt(int index) + { + BoundsCheck(index); + _parent.RemoveAt(_fromIndex + index); + _toIndex--; + _count--; + } + + /// + /// Returns an enumerator that iterates through a collection. + /// + /// + /// An object that can be used to iterate through the collection. + /// + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + /// + /// Returns an enumerator that iterates through the collection. + /// + /// + /// A that can be used to iterate through the collection. + /// + /// + public IEnumerator GetEnumerator() + { + for (int ii = _fromIndex; ii < _toIndex; ii++) + { + yield return _parent[ii]; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/collections/SubmapDictionary.cs b/NEsper.Core/NEsper.Core/compat/collections/SubmapDictionary.cs new file mode 100755 index 000000000..844597ae4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/collections/SubmapDictionary.cs @@ -0,0 +1,278 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace com.espertech.esper.compat.collections +{ + /// + /// An efficient subsection of an ordered or sorted dictionary. Designed for traversal and lookup, but not + /// necessarily efficient at counting elements. + /// + public class SubmapDictionary : IDictionary + { + private readonly OrderedDictionary _subDictionary; + private Bound _lower; + private Bound _upper; + + private readonly Func _lowerTest; + private readonly Func _upperTest; + + internal SubmapDictionary(OrderedDictionary subDictionary, Bound lower, Bound upper) + { + _subDictionary = subDictionary; + _lower = lower; + _upper = upper; + + if (_subDictionary == null) + { + _lowerTest = k1 => false; + _upperTest = k1 => false; + } + else + { + var keyComparer = _subDictionary.KeyComparer ?? new DefaultComparer(); + _lowerTest = MakeLowerTest(keyComparer); + _upperTest = MakeUpperTest(keyComparer); + } + } + + private Func MakeUpperTest(IComparer keyComparer) + { + if (_upper.HasValue) + { + if (_upper.IsInclusive) + { + return k1 => keyComparer.Compare(k1, _upper.Key) <= 0; + } + else + { + return k1 => keyComparer.Compare(k1, _upper.Key) < 0; + } + } + else + { + return k1 => true; + } + } + + private Func MakeLowerTest(IComparer keyComparer) + { + if (_lower.HasValue) + { + if (_lower.IsInclusive) + { + return k1 => keyComparer.Compare(k1, _lower.Key) >= 0; + } + else + { + return k1 => keyComparer.Compare(k1, _lower.Key) > 0; + } + } + else + { + return k1 => true; + } + } + + #region Implementation of IEnumerable + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + #endregion + + #region Implementation of IEnumerable> + + public IEnumerator> GetEnumerator() + { + IEnumerable> enumerator = + _lower.HasValue + ? _subDictionary.GetTail(_lower.Key, _lower.IsInclusive) + : _subDictionary; + + return enumerator + .TakeWhile(pair => _upperTest.Invoke(pair.Key)) + .GetEnumerator(); + } + + #endregion + + #region Implementation of ICollection> + + public void Add(KeyValuePair item) + { + throw new NotSupportedException(); + } + + public void Clear() + { + throw new NotSupportedException(); + } + + public bool Contains(KeyValuePair item) + { + if (_lowerTest.Invoke(item.Key) && _upperTest.Invoke(item.Key)) + { + return _subDictionary.Contains(item); + } + + return false; + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + var enumerator = GetEnumerator(); + while (enumerator.MoveNext()) + { + array[arrayIndex++] = enumerator.Current; + } + } + + public bool Remove(KeyValuePair item) + { + throw new NotSupportedException(); + } + + public int Count + { + get + { + IEnumerable> iterator; + + if (_lower.HasValue) + iterator = _subDictionary.GetTail(_lower.Key, _lower.IsInclusive); + else + iterator = _subDictionary; + + return iterator + .TakeWhile(pair => _upperTest.Invoke(pair.Key)) + .Count(); + } + } + + public bool IsReadOnly + { + get { return true; } + } + + #endregion + + #region Implementation of IDictionary + + public bool ContainsKey(K key) + { + if (_lowerTest.Invoke(key) && _upperTest.Invoke(key)) + { + return _subDictionary.ContainsKey(key); + } + + return false; + } + + public void Add(K key, V value) + { + throw new NotSupportedException(); + } + + public bool Remove(K key) + { + throw new NotSupportedException(); + } + + public bool TryGetValue(K key, out V value) + { + if (_lowerTest.Invoke(key) && _upperTest.Invoke(key)) + { + return _subDictionary.TryGetValue(key, out value); + } + + value = default(V); + return false; + } + + public V this[K key] + { + get + { + if (_lowerTest.Invoke(key) && _upperTest.Invoke(key)) + { + return _subDictionary[key]; + } + + throw new KeyNotFoundException(); + } + set + { + throw new NotSupportedException(); + } + } + + public ICollection Keys + { + get + { + IEnumerable> iterator; + + if (_lower.HasValue) + iterator = _subDictionary.GetTail(_lower.Key, _lower.IsInclusive); + else + iterator = _subDictionary; + + return iterator + .TakeWhile(pair => _upperTest.Invoke(pair.Key)) + .Select(pair => pair.Key) + .ToList(); + } + } + + public ICollection Values + { + get + { + IEnumerable> iterator; + + if (_lower.HasValue) + iterator = _subDictionary.GetTail( _lower.Key, _lower.IsInclusive); + else + iterator = _subDictionary; + + return iterator + .TakeWhile(pair => _upperTest.Invoke(pair.Key)) + .Select(pair => pair.Value) + .ToList(); + } + } + + #endregion + + internal struct Bound + { + internal K Key; + internal bool IsInclusive; + internal bool HasValue; + } + + internal class DefaultComparer : Comparer + { + #region Overrides of Comparer + + public override int Compare(K x, K y) + { + return ((IComparable) x).CompareTo(y); + } + + #endregion + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/collections/SynchronizedCollection.cs b/NEsper.Core/NEsper.Core/compat/collections/SynchronizedCollection.cs new file mode 100755 index 000000000..42089c5dc --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/collections/SynchronizedCollection.cs @@ -0,0 +1,223 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace com.espertech.esper.compat.collections +{ + public class SynchronizedCollection : ICollection + { + /// + /// Underlying set. + /// + private readonly ICollection _facadeCollection; + + /// + /// Initializes a new instance of the class. + /// + /// The facadeCollection. + public SynchronizedCollection(ICollection facadeCollection) + { + _facadeCollection = facadeCollection; + } + + #region ICollection Members + + /// + /// Converts the set to an array. + /// + /// + [MethodImpl(MethodImplOptions.Synchronized)] + public T[] ToArray() + { + return _facadeCollection.ToArray(); + } + + /// + /// Adds all of the items in the source. + /// + /// The source. + [MethodImpl(MethodImplOptions.Synchronized)] + public void AddAll(IEnumerable source) + { + _facadeCollection.AddAll(source); + } + + /// + /// Returns the first item in the set + /// + /// + public T First + { + [MethodImpl(MethodImplOptions.Synchronized)] + get { return _facadeCollection.First(); } + } + + /// + /// Gets a value indicating whether this instance is empty. + /// + /// true if this instance is empty; otherwise, false. + public bool IsEmpty + { + [MethodImpl(MethodImplOptions.Synchronized)] + get { return _facadeCollection.IsEmpty(); } + } + + /// + /// Removes all items. + /// + /// + [MethodImpl(MethodImplOptions.Synchronized)] + public void RemoveAll(IEnumerable items) + { + _facadeCollection.RemoveAll(items); + } + + #endregion + + #region ICollection Members + + /// + ///Adds an item to the . + /// + /// + ///The object to add to the . + ///The is read-only. + [MethodImpl(MethodImplOptions.Synchronized)] + public void Add(T item) + { + _facadeCollection.Add(item); + } + + /// + ///Removes all items from the . + /// + /// + ///The is read-only. + [MethodImpl(MethodImplOptions.Synchronized)] + public void Clear() + { + _facadeCollection.Clear(); + } + + /// + ///Determines whether the contains a specific value. + /// + /// + /// + ///true if item is found in the ; otherwise, false. + /// + /// + ///The object to locate in the . + [MethodImpl(MethodImplOptions.Synchronized)] + public bool Contains(T item) + { + return _facadeCollection.Contains(item); + } + + /// + ///Copies the elements of the to an , starting at a particular index. + /// + /// + ///The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing. + ///The zero-based index in array at which copying begins. + ///arrayIndex is less than 0. + ///array is null. + ///array is multidimensional.-or-arrayIndex is equal to or greater than the length of array.-or-The number of elements in the source is greater than the available space from arrayIndex to the end of the destination array.-or-Type T cannot be cast automatically to the type of the destination array. + [MethodImpl(MethodImplOptions.Synchronized)] + public void CopyTo(T[] array, int arrayIndex) + { + _facadeCollection.CopyTo(array, arrayIndex); + } + + /// + ///Removes the first occurrence of a specific object from the . + /// + /// + /// + ///true if item was successfully removed from the ; otherwise, false. This method also returns false if item is not found in the original . + /// + /// + ///The object to remove from the . + ///The is read-only. + [MethodImpl(MethodImplOptions.Synchronized)] + public bool Remove(T item) + { + return _facadeCollection.Remove(item); + } + + /// + ///Gets the number of elements contained in the . + /// + /// + /// + ///The number of elements contained in the . + /// + /// + public int Count + { + [MethodImpl(MethodImplOptions.Synchronized)] + get { return _facadeCollection.Count; } + } + + /// + ///Gets a value indicating whether the is read-only. + /// + /// + /// + ///true if the is read-only; otherwise, false. + /// + /// + public bool IsReadOnly + { + [MethodImpl(MethodImplOptions.Synchronized)] + get { return _facadeCollection.IsReadOnly; } + } + + #endregion + + #region IEnumerable Members + + /// + ///Returns an enumerator that iterates through the collection. + /// + /// + /// + ///A that can be used to iterate through the collection. + /// + ///1 + [MethodImpl(MethodImplOptions.Synchronized)] + IEnumerator IEnumerable.GetEnumerator() + { + return _facadeCollection.GetEnumerator(); + } + + #endregion + + #region IEnumerable Members + + /// + ///Returns an enumerator that iterates through a collection. + /// + /// + /// + ///An object that can be used to iterate through the collection. + /// + ///2 + public IEnumerator GetEnumerator() + { + return ((IEnumerable) this).GetEnumerator(); + } + + #endregion + } +} diff --git a/NEsper.Core/NEsper.Core/compat/collections/SynchronizedList.cs b/NEsper.Core/NEsper.Core/compat/collections/SynchronizedList.cs new file mode 100755 index 000000000..515649844 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/collections/SynchronizedList.cs @@ -0,0 +1,107 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +namespace com.espertech.esper.compat.collections +{ + public class SynchronizedList : IList + { + private readonly IList _subList; + + /// + /// Initializes a new instance of the class. + /// + /// The _sub list. + public SynchronizedList(IList _subList) + { + this._subList = _subList; + } + + [MethodImpl(MethodImplOptions.Synchronized)] + public int IndexOf(T item) + { + return _subList.IndexOf(item); + } + + [MethodImpl(MethodImplOptions.Synchronized)] + public void Insert(int index, T item) + { + _subList.Insert(index, item); + } + + [MethodImpl(MethodImplOptions.Synchronized)] + public void RemoveAt(int index) + { + _subList.RemoveAt(index); + } + + public T this[int index] + { + [MethodImpl(MethodImplOptions.Synchronized)] + get { return _subList[index]; } + [MethodImpl(MethodImplOptions.Synchronized)] + set { _subList[index] = value; } + } + + [MethodImpl(MethodImplOptions.Synchronized)] + public void Add(T item) + { + _subList.Add(item); + } + + [MethodImpl(MethodImplOptions.Synchronized)] + public void Clear() + { + _subList.Clear(); + } + + [MethodImpl(MethodImplOptions.Synchronized)] + public bool Contains(T item) + { + return _subList.Contains(item); + } + + [MethodImpl(MethodImplOptions.Synchronized)] + public void CopyTo(T[] array, int arrayIndex) + { + _subList.CopyTo(array, arrayIndex); + } + + [MethodImpl(MethodImplOptions.Synchronized)] + public bool Remove(T item) + { + return _subList.Remove(item); + } + + public int Count + { + [MethodImpl(MethodImplOptions.Synchronized)] + get { return _subList.Count; } + } + + public bool IsReadOnly + { + [MethodImpl(MethodImplOptions.Synchronized)] + get { return _subList.IsReadOnly; } + } + + [MethodImpl(MethodImplOptions.Synchronized)] + public IEnumerator GetEnumerator() + { + return _subList.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/collections/SynchronizedSet.cs b/NEsper.Core/NEsper.Core/compat/collections/SynchronizedSet.cs new file mode 100755 index 000000000..73ce95190 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/collections/SynchronizedSet.cs @@ -0,0 +1,122 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +namespace com.espertech.esper.compat.collections +{ + public class SynchronizedSet : SynchronizedCollection, ISet + { + private readonly ISet _facadeSet; + + /// + /// Initializes a new instance of the class. + /// + /// The facade set. + public SynchronizedSet(ISet facadeSet) : base(facadeSet) + { + _facadeSet = facadeSet; + } + + bool ISet.Add(T item) + { + lock (this) + { + return _facadeSet.Add(item); + } + } + + /// + /// Unions with another set. + /// + /// The other. + public void UnionWith(IEnumerable other) + { + lock (this) + { + _facadeSet.UnionWith(other); + } + } + + /// + /// Intersects with another set. + /// + /// The other. + public void IntersectWith(IEnumerable other) + { + lock (this) + { + _facadeSet.IntersectWith(other); + } + } + + public void ExceptWith(IEnumerable other) + { + lock (this) + { + _facadeSet.ExceptWith(other); + } + } + + public void SymmetricExceptWith(IEnumerable other) + { + lock (this) + { + _facadeSet.SymmetricExceptWith(other); + } + } + + public bool IsSubsetOf(IEnumerable other) + { + lock (this) + { + return _facadeSet.IsSubsetOf(other); + } + } + + public bool IsSupersetOf(IEnumerable other) + { + lock (this) + { + return _facadeSet.IsSupersetOf(other); + } + } + + public bool IsProperSupersetOf(IEnumerable other) + { + lock (this) + { + return _facadeSet.IsProperSupersetOf(other); + } + } + + public bool IsProperSubsetOf(IEnumerable other) + { + lock (this) + { + return _facadeSet.IsProperSubsetOf(other); + } + } + + public bool Overlaps(IEnumerable other) + { + lock (this) + { + return _facadeSet.Overlaps(other); + } + } + + public bool SetEquals(IEnumerable other) + { + lock (this) + { + return _facadeSet.SetEquals(other); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/collections/TransformCollection.cs b/NEsper.Core/NEsper.Core/compat/collections/TransformCollection.cs new file mode 100755 index 000000000..8939efb7a --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/collections/TransformCollection.cs @@ -0,0 +1,137 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace com.espertech.esper.compat.collections +{ + public class TransformCollection : ICollection + { + /// + /// Underlying collection + /// + private readonly ICollection _trueCollection; + /// + /// Function that transforms items from the "external" type to the "internal" type + /// + private readonly Func _transformExtInt; + + /// + /// Function that transforms items from the "internal" type to the "external" type + /// + private readonly Func _transformIntExt; + + /// + /// Initializes a new instance of the class. + /// + /// The true collection. + /// The transform ext int. + /// The transform int ext. + public TransformCollection(ICollection trueCollection, + Func transformExtInt, + Func transformIntExt) + { + _trueCollection = trueCollection; + _transformExtInt = transformExtInt; + _transformIntExt = transformIntExt; + } + + /// + /// Returns an enumerator that iterates through a collection. + /// + /// + /// An object that can be used to iterate through the collection. + /// + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + /// + /// Gets the enumerator. + /// + /// + public IEnumerator GetEnumerator() + { +#if true + var enumerator = _trueCollection.GetEnumerator(); + while (enumerator.MoveNext()) + { + yield return _transformIntExt.Invoke(enumerator.Current); + } +#else + return _trueCollection.Select(item => _transformIntExt.Invoke(item)).GetEnumerator(); +#endif + } + + /// + /// Adds the specified item. + /// + /// The item. + public void Add(TExt item) + { + _trueCollection.Add(_transformExtInt(item)); + } + + /// + /// Clears this instance. + /// + public void Clear() + { + _trueCollection.Clear(); + } + + /// + /// Determines whether [contains] [the specified item]. + /// + /// The item. + /// + /// true if [contains] [the specified item]; otherwise, false. + /// + public bool Contains(TExt item) + { + return _trueCollection.Contains(_transformExtInt(item)); + } + + public void CopyTo(TExt[] array, int arrayIndex) + { + var arrayLength = array.Length; + var trueEnum = _trueCollection.GetEnumerator(); + + while(trueEnum.MoveNext() && arrayIndex < arrayLength) + { + array[arrayIndex++] = _transformIntExt(trueEnum.Current); + } + } + + /// + /// Removes the specified item. + /// + /// The item. + /// + public bool Remove(TExt item) + { + return _trueCollection.Remove(_transformExtInt(item)); + } + + /// + /// Gets the count. + /// + /// The count. + public int Count + { + get { return _trueCollection.Count; } + } + + /// + /// Gets a value indicating whether this instance is read only. + /// + /// + /// true if this instance is read only; otherwise, false. + /// + public bool IsReadOnly + { + get { return _trueCollection.IsReadOnly; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/collections/TransformDictionary.cs b/NEsper.Core/NEsper.Core/compat/collections/TransformDictionary.cs new file mode 100755 index 000000000..e5fbf95dd --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/collections/TransformDictionary.cs @@ -0,0 +1,333 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace com.espertech.esper.compat.collections +{ + public class TransformDictionary : IDictionary + { + /// + /// Gets or sets the sub dictionary. + /// + /// + /// The sub dictionary. + /// + public IDictionary SubDictionary { get; set; } + + /// + /// Gets or sets the key out transform. + /// + /// + /// The key transform. + /// + public Func KeyOut { get; set; } + + /// + /// Gets or sets the key out transform. + /// + /// + /// The key transform. + /// + public Func KeyIn { get; set; } + + /// + /// Gets or sets the value transform. + /// + /// + /// The value transform. + /// + public Func ValueOut { get; set; } + + /// + /// Gets or sets the value transform. + /// + /// + /// The value transform. + /// + public Func ValueIn { get; set; } + + /// + /// Initializes a new instance of the class. + /// + public TransformDictionary() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The sub dictionary. + public TransformDictionary(IDictionary subDictionary) + { + SubDictionary = subDictionary; + } + + /// + /// Initializes a new instance of the class. + /// + /// The sub dictionary. + /// The key out. + /// The key in. + /// The value out. + /// The value in. + public TransformDictionary(IDictionary subDictionary, Func keyOut, Func keyIn, Func valueOut, Func valueIn) + { + SubDictionary = subDictionary; + KeyOut = keyOut; + KeyIn = keyIn; + ValueOut = valueOut; + ValueIn = valueIn; + } + + /// + /// Returns an enumerator that iterates through a collection. + /// + /// + /// An object that can be used to iterate through the collection. + /// + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + /// + /// Returns an enumerator that iterates through the collection. + /// + /// + /// A that can be used to iterate through the collection. + /// + /// + public IEnumerator> GetEnumerator() + { + return SubDictionary.Select(ExtCast).GetEnumerator(); + } + + /// + /// Gets an containing the keys of the . + /// + public ICollection Keys + { + get + { + return new TransformCollection( + SubDictionary.Keys, KeyIn, KeyOut); + } + } + + /// + /// Gets an containing the values in the . + /// + public ICollection Values + { + get + { + return new TransformCollection( + SubDictionary.Values, ValueIn, ValueOut); + } + } + + /// + /// Adds an item to the . + /// + /// The object to add to the . + /// + public void Add(KeyValuePair item) + { + SubDictionary.Add(IntCast(item)); + } + + /// + /// Removes all items from the . + /// + /// + public void Clear() + { + SubDictionary.Clear(); + } + + /// + /// Gets the number of elements contained in the . + /// + public int Count + { + get { return SubDictionary.Count; } + } + + /// + /// Gets a value indicating whether the is read-only. + /// + public bool IsReadOnly + { + get { return SubDictionary.IsReadOnly; } + } + + /// + /// Adds an element with the provided key and value to the . + /// + /// The object to use as the key of the element to add. + /// The object to use as the value of the element to add. + /// + public void Add(TK1 key, TV1 value) + { + SubDictionary.Add(IntCast(key), IntCast(value)); + } + + /// + /// Determines whether the contains a specific value. + /// + /// The object to locate in the . + /// + /// true if is found in the ; otherwise, false. + /// + /// + public bool Contains(KeyValuePair item) + { + return SubDictionary.Contains(IntCast(item)); + } + + /// + /// Removes the first occurrence of a specific object from the . + /// + /// The object to remove from the . + /// + /// true if was successfully removed from the ; otherwise, false. This method also returns false if is not found in the original . + /// + /// + public bool Remove(KeyValuePair item) + { + return SubDictionary.Remove(IntCast(item)); + } + + /// + /// Removes the element with the specified key from the . + /// + /// The key of the element to remove. + /// + /// true if the element is successfully removed; otherwise, false. This method also returns false if was not found in the original . + /// + /// + public bool Remove(TK1 key) + { + return SubDictionary.Remove(IntCast(key)); + } + + /// + /// Determines whether the contains an element with the specified key. + /// + /// The key to locate in the . + /// + /// true if the contains an element with the key; otherwise, false. + /// + /// + public bool ContainsKey(TK1 key) + { + return SubDictionary.ContainsKey(IntCast(key)); + } + + /// + /// Gets or sets the element with the specified key. + /// + /// The key. + /// + public TV1 this[TK1 key] + { + get { return ExtCast(SubDictionary[IntCast(key)]); } + set { SubDictionary[IntCast(key)] = IntCast(value); } + } + + /// + /// Gets the value associated with the specified key. + /// + /// The key whose value to get. + /// When this method returns, the value associated with the specified key, if the key is found; otherwise, the default value for the type of the parameter. This parameter is passed uninitialized. + /// + /// true if the object that : contains an element with the specified key; otherwise, false. + /// + /// + public bool TryGetValue(TK1 key, out TV1 value) + { + TV2 evalue; + + if (SubDictionary.TryGetValue(IntCast(key), out evalue)) + { + value = ExtCast(evalue); + return true; + } + + value = default(TV1); + return false; + } + + /// + /// Copies to. + /// + /// The array. + /// Index of the array. + /// + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + throw new NotImplementedException(); + } + + /// + /// Converts the item from the "internal" repesentation to the "external" representation. + /// + /// The item. + /// + private KeyValuePair ExtCast(KeyValuePair item) + { + return new KeyValuePair(KeyOut(item.Key), ValueOut(item.Value)); + } + + /// + /// Converts the item from the "internal" repesentation to the "external" representation. + /// + /// The value. + /// + private TK1 ExtCast(TK2 value) + { + return KeyOut(value); + } + + /// + /// Converts the item from the "internal" repesentation to the "external" representation. + /// + /// The value. + /// + private TV1 ExtCast(TV2 value) + { + return ValueOut(value); + } + + /// + /// Converts the item from the "external" repesentation to the "internal" representation. + /// + /// The item. + /// + private KeyValuePair IntCast(KeyValuePair item) + { + return new KeyValuePair(KeyIn(item.Key), ValueIn(item.Value)); + } + + /// + /// Converts the item from the "external" repesentation to the "internal" representation. + /// + /// The value. + /// + private TK2 IntCast(TK1 value) + { + return KeyIn(value); + } + + /// + /// Converts the item from the "external" repesentation to the "internal" representation. + /// + /// The value. + /// + private TV2 IntCast(TV1 value) + { + return ValueIn(value); + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/collections/TransformDictionaryFactory.cs b/NEsper.Core/NEsper.Core/compat/collections/TransformDictionaryFactory.cs new file mode 100755 index 000000000..e6cc9b3b5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/collections/TransformDictionaryFactory.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; + +using com.espertech.esper.util; + +namespace com.espertech.esper.compat.collections +{ + public class TransformDictionaryFactory + { + public static TransformDictionary Create(object value) + { + var sourceDictionary = (IDictionary) value; + + var key1To2Coercer = CoercerFactory.GetCoercer(typeof(TK1), typeof(TK2)); + var key2To1Coercer = CoercerFactory.GetCoercer(typeof(TK2), typeof(TK1)); + var val1To2Coercer = CoercerFactory.GetCoercer(typeof(TV1), typeof(TV2)); + var val2To1Coercer = CoercerFactory.GetCoercer(typeof(TV2), typeof(TV1)); + + return new TransformDictionary( + sourceDictionary, + k2 => (TK1) key2To1Coercer(k2), + k1 => (TK2) key1To2Coercer(k1), + v2 => (TV1) val2To1Coercer(v2), + v1 => (TV2) val1To2Coercer(v1)); + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/collections/Tuple.cs b/NEsper.Core/NEsper.Core/compat/collections/Tuple.cs new file mode 100755 index 000000000..bc9df36b4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/collections/Tuple.cs @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.compat.collections +{ + public class Tuple + { + public TA A { get; set; } + public TB B { get; set; } + } + + public class Tuple + { + public TA A { get; set; } + public TB B { get; set; } + public TC C { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/logging/ILog.cs b/NEsper.Core/NEsper.Core/compat/logging/ILog.cs new file mode 100755 index 000000000..8c44b13cd --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/logging/ILog.cs @@ -0,0 +1,33 @@ +using System; + +namespace com.espertech.esper.compat.logging +{ + public interface ILog + { + bool IsDebugEnabled { get; } + bool IsInfoEnabled { get; } + bool IsWarnEnabled { get; } + bool IsErrorEnabled { get; } + bool IsFatalEnabled { get; } + + void Debug(string message); + void Debug(string messageFormat, params object[] args); + void Debug(string message, Exception e); + + void Info(string message); + void Info(string messageFormat, params object[] args); + void Info(string message, Exception e); + + void Warn(string message); + void Warn(string messageFormat, params object[] args); + void Warn(string message, Exception e); + + void Error(string message); + void Error(string messageFormat, params object[] args); + void Error(string message, Exception e); + + void Fatal(string message); + void Fatal(string messageFormat, params object[] args); + void Fatal(string message, Exception e); + } +} diff --git a/NEsper.Core/NEsper.Core/compat/logging/LogCommon.cs b/NEsper.Core/NEsper.Core/compat/logging/LogCommon.cs new file mode 100755 index 000000000..92f4cb970 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/logging/LogCommon.cs @@ -0,0 +1,146 @@ +using System; + +namespace com.espertech.esper.compat.logging +{ + public class LogCommon : ILog + { + private readonly Common.Logging.ILog _log; + + /// + /// Initializes a new instance of the class. + /// + /// The log. + public LogCommon(Common.Logging.ILog log) + { + _log = log; + ChangeConfiguration(); + } + + /// + /// Changes the configuration. + /// + private void ChangeConfiguration() + { + IsDebugEnabled = _log.IsDebugEnabled; + IsInfoEnabled = _log.IsInfoEnabled; + IsWarnEnabled = _log.IsWarnEnabled; + IsErrorEnabled = _log.IsErrorEnabled; + IsFatalEnabled = _log.IsFatalEnabled; + } + + public bool IsDebugEnabled { get; set; } + + public bool IsInfoEnabled { get; set; } + + public bool IsWarnEnabled { get; set; } + + public bool IsErrorEnabled { get; set; } + + public bool IsFatalEnabled { get; set; } + + public void Debug(string message) + { + if (IsDebugEnabled) { + _log.Debug(message); + } + } + + public void Debug(string messageFormat, params object[] args) + { + if (IsDebugEnabled) { + _log.DebugFormat(messageFormat, args); + } + } + + public void Debug(string message, Exception e) + { + if (IsDebugEnabled) { + _log.Debug(message, e); + } + } + + public void Info(string message) + { + if (IsInfoEnabled) { + _log.Info(message); + } + } + + public void Info(string messageFormat, params object[] args) + { + if (IsInfoEnabled) { + _log.InfoFormat(messageFormat, args); + } + } + + public void Info(string message, Exception e) + { + if (IsInfoEnabled) { + _log.Info(message, e); + } + } + + public void Warn(string message) + { + if (IsWarnEnabled) { + _log.Warn(message); + } + } + + public void Warn(string messageFormat, params object[] args) + { + if (IsWarnEnabled) { + _log.WarnFormat(messageFormat, args); + } + } + + public void Warn(string message, Exception e) + { + if (IsWarnEnabled) { + _log.Warn(message, e); + } + } + + public void Error(string message) + { + if (IsErrorEnabled) { + _log.Error(message); + } + } + + public void Error(string messageFormat, params object[] args) + { + if (IsErrorEnabled) { + _log.ErrorFormat(messageFormat, args); + } + } + + public void Error(string message, Exception e) + { + if (IsErrorEnabled) { + _log.Error(message, e); + } + } + + public void Fatal(string message) + { + if (IsFatalEnabled) { + _log.Fatal(message); + } + } + + public void Fatal(string messageFormat, params object[] args) + { + if (IsFatalEnabled) { + _log.FatalFormat(messageFormat, args); + } + } + + public void Fatal(string message, Exception e) + { + if (IsFatalEnabled) { + _log.Fatal(message, e); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/logging/LogManager.cs b/NEsper.Core/NEsper.Core/compat/logging/LogManager.cs new file mode 100755 index 000000000..a33d1ff75 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/logging/LogManager.cs @@ -0,0 +1,27 @@ +using System; + +namespace com.espertech.esper.compat.logging +{ + public class LogManager + { + /// + /// Gets a logger instance for the given type. + /// + /// The t. + /// + public static ILog GetLogger(Type t) + { + return new LogCommon(Common.Logging.LogManager.GetLogger(t)); + } + + /// + /// Gets the logger instance for the given name. + /// + /// The name. + /// + public static ILog GetLogger(string name) + { + return new LogCommon(Common.Logging.LogManager.GetLogger(name)); + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/magic/MagicCollection.cs b/NEsper.Core/NEsper.Core/compat/magic/MagicCollection.cs new file mode 100755 index 000000000..51e06eaac --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/magic/MagicCollection.cs @@ -0,0 +1,105 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace com.espertech.esper.compat.magic +{ + public class MagicCollection : ICollection + { + private readonly ICollection _realCollection; + private GenericTypeCaster _typeCaster; + + public MagicCollection(object opaqueCollection) + { + _realCollection = (ICollection) opaqueCollection; + _typeCaster = null; + } + + public MagicCollection(ICollection realCollection) + { + _realCollection = realCollection; + } + + public GenericTypeCaster TypeCaster + { + get + { + if (_typeCaster == null) + _typeCaster = CastHelper.GetCastConverter(); + return _typeCaster; + } + } + + public IEnumerator GetEnumerator() + { + return _realCollection.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + foreach (V item in _realCollection ) + yield return item; + } + + public void Add(object item) + { + _realCollection.Add((V)item); + } + + public void Clear() + { + _realCollection.Clear(); + } + + public bool Contains(object item) + { + if (item is V) { + return _realCollection.Contains((V) item); + } else { + return _realCollection.Contains(TypeCaster.Invoke(item)); + } + } + + public void CopyTo(object[] array, int arrayIndex) + { + foreach(var item in this) + { + if (arrayIndex >= array.Length) + { + break; + } + + array[arrayIndex++] = item; + } + } + + public bool Remove(object item) + { + if (item is V) { + return _realCollection.Remove((V) item); + } else { + return _realCollection.Remove(TypeCaster.Invoke(item)); + } + } + + public int Count + { + get { return _realCollection.Count(); } + } + + public bool IsReadOnly + { + get { return _realCollection.IsReadOnly; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/magic/MagicDictionary.cs b/NEsper.Core/NEsper.Core/compat/magic/MagicDictionary.cs new file mode 100755 index 000000000..906ba1f14 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/magic/MagicDictionary.cs @@ -0,0 +1,138 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections; +using System.Collections.Generic; + +namespace com.espertech.esper.compat.magic +{ + public class MagicDictionary : IDictionary + { + private readonly IDictionary _realDictionary; + private GenericTypeCaster _typeKeyCaster; + + public MagicDictionary(Object opaqueDictionary) + { + _realDictionary = (IDictionary)opaqueDictionary; + _typeKeyCaster = null; + } + + public MagicDictionary(IDictionary realDictionary) + { + _realDictionary = realDictionary; + } + + public GenericTypeCaster TypeKeyCaster + { + get + { + if (_typeKeyCaster == null) + _typeKeyCaster = CastHelper.GetCastConverter(); + return _typeKeyCaster; + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public IEnumerator> GetEnumerator() + { + foreach( var entry in _realDictionary ) { + yield return new KeyValuePair(entry.Key, entry.Value); + } + } + + public void Add(KeyValuePair item) + { + _realDictionary.Add(new KeyValuePair((K1) item.Key, (V1)item.Value)); + } + + public void Clear() + { + _realDictionary.Clear(); + } + + public bool Contains(KeyValuePair item) + { + return _realDictionary.Contains(new KeyValuePair((K1) item.Key, (V1)item.Value)); + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + throw new NotSupportedException(); + } + + public bool Remove(KeyValuePair item) + { + return _realDictionary.Remove(new KeyValuePair((K1) item.Key, (V1)item.Value)); + } + + public int Count + { + get { return _realDictionary.Count; } + } + + public bool IsReadOnly + { + get { return _realDictionary.IsReadOnly; } + } + + public bool ContainsKey(object key) + { + if (key is K1) + { + return _realDictionary.ContainsKey((K1) key); + } + else + { + return _realDictionary.ContainsKey(TypeKeyCaster.Invoke(key)); + } + } + + public void Add(object key, object value) + { + _realDictionary.Add((K1) key, (V1) value); + } + + public bool Remove(object key) + { + return _realDictionary.Remove((K1) key); + } + + public bool TryGetValue(object key, out object value) + { + V1 item; + if (_realDictionary.TryGetValue((K1) key, out item)) { + value = item; + return true; + } + + value = null; + return false; + } + + public object this[object key] + { + get { return _realDictionary[(K1) key]; } + set { _realDictionary[(K1) key] = (V1) value; } + } + + public ICollection Keys + { + get { return new MagicCollection(_realDictionary.Keys); } + } + + public ICollection Values + { + get { return new MagicCollection(_realDictionary.Values); } + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/magic/MagicList.cs b/NEsper.Core/NEsper.Core/compat/magic/MagicList.cs new file mode 100755 index 000000000..b546a64ff --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/magic/MagicList.cs @@ -0,0 +1,146 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace com.espertech.esper.compat.magic +{ + public class MagicList : IList + { + private readonly IList realList; + private GenericTypeCaster typeCaster; + + public MagicList(object opaqueCollection) + { + this.realList = (IList)opaqueCollection; + this.typeCaster = null; + } + + public MagicList(IList realCollection) + { + this.realList = realCollection; + } + + public GenericTypeCaster TypeCaster + { + get + { + if (typeCaster == null) + typeCaster = CastHelper.GetCastConverter(); + return typeCaster; + } + } + + #region Implementation of IList + + public int IndexOf(object item) + { + if (item is V) + { + return realList.IndexOf((V)item); + } + else + { + return realList.IndexOf(typeCaster.Invoke(item)); + } + } + + public void Insert(int index, object item) + { + if (item is V) + { + realList.Insert(index, (V) item); + } + else + { + realList.Insert(index, typeCaster.Invoke(item)); + } + } + + public void RemoveAt(int index) + { + realList.RemoveAt(index); + } + + public object this[int index] + { + get { return realList[index]; } + set + { + if (value is V) { + realList[index] = (V) value; + } + else { + realList[index] = TypeCaster.Invoke(value); + } + } + } + + #endregion + + public IEnumerator GetEnumerator() + { + return realList.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + foreach (V item in realList ) + yield return item; + } + + public void Add(object item) + { + realList.Add((V)item); + } + + public void Clear() + { + realList.Clear(); + } + + public bool Contains(object item) + { + if (item is V) { + return realList.Contains((V) item); + } else { + return realList.Contains(TypeCaster.Invoke(item)); + } + } + + public void CopyTo(object[] array, int arrayIndex) + { + throw new NotSupportedException(); + } + + public bool Remove(object item) + { + if (item is V) { + return realList.Remove((V) item); + } else { + return realList.Remove(TypeCaster.Invoke(item)); + } + } + + public int Count + { + get { return realList.Count(); } + } + + public bool IsReadOnly + { + get { return realList.IsReadOnly; } + } + + + } +} diff --git a/NEsper.Core/NEsper.Core/compat/magic/MagicMarker.cs b/NEsper.Core/NEsper.Core/compat/magic/MagicMarker.cs new file mode 100755 index 000000000..568ffb019 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/magic/MagicMarker.cs @@ -0,0 +1,292 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; +using System.Linq.Expressions; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.threading; + +namespace com.espertech.esper.compat.magic +{ + public class MagicMarker + { + #region Collection Factory + private static readonly ILockable collectionFactoryTableLock = LockManager.CreateLock(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + private static readonly IDictionary>> collectionFactoryTable = + new Dictionary>>(); + + /// + /// Creates a factory object that produces wrappers (MagicCollection) instances for a + /// given object. This method is designed for those who know early on that they are + /// going to be dealing with an opaque object that will have a true generic definition + /// once they receive it. However, to make life easier (relatively speaking), we would + /// prefer to operate with a clean object rather than the type specific detail. + /// + /// The t. + /// + public static Func> GetCollectionFactory(Type t) + { + using(collectionFactoryTableLock.Acquire()) { + Func> collectionFactory; + + if (! collectionFactoryTable.TryGetValue(t, out collectionFactory)) { + collectionFactory = NewCollectionFactory(t); + collectionFactoryTable[t] = collectionFactory; + } + + return collectionFactory; + } + } + + public static ICollection NewMagicCollection(object o) + { + return o == null ? null : new MagicCollection(o); + } + + /// + /// Constructs the factory method for the given type. + /// + /// + /// + + private static Func> NewCollectionFactory(Type t) + { + if (t == typeof(ICollection)) + return o => (ICollection)o; + + var genericType = t.FindGenericInterface(typeof (ICollection<>)); + if (genericType == null) + return null; + + var genericArg = genericType.GetGenericArguments()[0]; + var magicActivator = typeof (MagicMarker) + .GetMethod("NewMagicCollection", new[] {typeof (object)}) + .MakeGenericMethod(genericArg); + + // GetInstance create a function that will create the wrapper type when + // the generic object is presented. + var eParam = Expression.Parameter(typeof (object), "o"); + var eBuild = Expression.Call(magicActivator, eParam); + var eLambda = Expression.Lambda>>(eBuild, eParam); + + // Return the compiled lambda method + return eLambda.Compile(); + } + #endregion + + #region List Factory + private static readonly ILockable listFactoryTableLock = LockManager.CreateLock(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + private static readonly IDictionary>> listFactoryTable = + new Dictionary>>(); + + public static Func> GetListFactory(Type t) + { + using (listFactoryTableLock.Acquire()) + { + Func> listFactory; + + if (!listFactoryTable.TryGetValue(t, out listFactory)) + { + listFactory = NewListFactory(t); + listFactoryTable[t] = listFactory; + } + + return listFactory; + } + } + + public static IList NewMagicList(object o) + { + return o == null ? null : new MagicList(o); + } + + /// + /// Constructs the factory method for the given type. + /// + /// + /// + + private static Func> NewListFactory(Type t) + { + if (t == typeof(IList)) + return o => (IList)o; + + var genericType = t.FindGenericInterface(typeof(IList<>)); + if (genericType == null) + return null; + + var genericArg = genericType.GetGenericArguments()[0]; + var magicActivator = typeof(MagicMarker) + .GetMethod("NewMagicList", new[] { typeof(object) }) + .MakeGenericMethod(genericArg); + + // GetInstance create a function that will create the wrapper type when + // the generic object is presented. + var eParam = Expression.Parameter(typeof(object), "o"); + var eBuild = Expression.Call(magicActivator, eParam); + var eLambda = Expression.Lambda>>(eBuild, eParam); + + // Return the compiled lambda method + return eLambda.Compile(); + } + #endregion + + #region Dictionary Factory + private static readonly ILockable dictionaryFactoryTableLock = LockManager.CreateLock(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + private static readonly IDictionary>> dictionaryFactoryTable = + new Dictionary>>(); + + /// + /// Creates a factory object that produces wrappers (MagicStringDictionary) instances for + /// a given object. This method is designed for those who know early on that they are + /// going to be dealing with an opaque object that will have a true generic definition + /// once they receive it. However, to make life easier (relatively speaking), we would + /// prefer to operate with a clean object rather than the type specific detail. + /// + /// The t. + /// + public static Func> GetDictionaryFactory(Type t) + { + using(dictionaryFactoryTableLock.Acquire()) + { + Func> dictionaryFactory; + + if (!dictionaryFactoryTable.TryGetValue(t, out dictionaryFactory)) + { + dictionaryFactory = NewDictionaryFactory(t); + dictionaryFactoryTable[t] = dictionaryFactory; + } + + return dictionaryFactory; + } + } + + public static IDictionary NewMagicDictionary(object o) + { + return o == null ? null : new MagicDictionary(o); + } + + /// + /// Constructs the factory method for the given type. + /// + /// + /// + + private static Func> NewDictionaryFactory(Type t) + { + if (t == typeof(IDictionary)) + return o => (IDictionary)o; + + var genericType = t.FindGenericInterface(typeof(IDictionary<,>)); + if (genericType == null) + return null; + + var genericKey = genericType.GetGenericArguments()[0]; + var genericArg = genericType.GetGenericArguments()[1]; + var magicActivator = typeof (MagicMarker) + .GetMethod("NewMagicDictionary", new[] {typeof (object)}) + .MakeGenericMethod(genericKey, genericArg); + + // GetInstance create a function that will create the wrapper type when + // the generic object is presented. + var eParam = Expression.Parameter(typeof(object), "o"); + var eBuild = Expression.Call(magicActivator, eParam); + var eLambda = Expression.Lambda>>(eBuild, eParam); + + // Return the compiled lambda method + return eLambda.Compile(); + } + #endregion + + #region String Dictionary Factory + private static readonly ILockable stringDictionaryFactoryTableLock = LockManager.CreateLock(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + private static readonly IDictionary>> stringDictionaryFactoryTable = + new Dictionary>>(); + + public static IDictionary GetStringDictionary(Object o) + { + if ( o == null ) + return null; + + var dictionaryFactory = GetStringDictionaryFactory(o.GetType()); + if (dictionaryFactory == null) + return null; + + return dictionaryFactory(o); + } + + /// + /// Creates a factory object that produces wrappers (MagicStringDictionary) instances for + /// a given object. This method is designed for those who know early on that they are + /// going to be dealing with an opaque object that will have a true generic definition + /// once they receive it. However, to make life easier (relatively speaking), we would + /// prefer to operate with a clean object rather than the type specific detail. + /// + /// The t. + /// + public static Func> GetStringDictionaryFactory(Type t) + { + using (stringDictionaryFactoryTableLock.Acquire()) + { + Func> stringDictionaryFactory; + + if (!stringDictionaryFactoryTable.TryGetValue(t, out stringDictionaryFactory)) + { + stringDictionaryFactory = NewStringDictionaryFactory(t); + stringDictionaryFactoryTable[t] = stringDictionaryFactory; + } + + return stringDictionaryFactory; + } + } + + public static IDictionary NewMagicStringDictionary(object o) + { + return o == null ? null : new MagicStringDictionary(o); + } + + /// + /// Constructs the factory method for the given type. + /// + /// + /// + + private static Func> NewStringDictionaryFactory(Type t) + { + if (t == typeof(IDictionary)) + return o => (IDictionary)o; + + var genericType = t.FindGenericInterface(typeof(IDictionary<,>)); + if (genericType == null) + return null; + + var genericKey = genericType.GetGenericArguments()[0]; + if (genericKey != typeof(string)) + return null; + + var genericArg = genericType.GetGenericArguments()[1]; + var magicActivator = typeof(MagicMarker) + .GetMethod("NewMagicStringDictionary", new[] { typeof(object) }) + .MakeGenericMethod(genericArg); + + // GetInstance create a function that will create the wrapper type when + // the generic object is presented. + var eParam = Expression.Parameter(typeof(object), "o"); + var eBuild = Expression.Call(magicActivator, eParam); + var eLambda = Expression.Lambda>>(eBuild, eParam); + + // Return the compiled lambda method + return eLambda.Compile(); + } + + #endregion + } +} diff --git a/NEsper.Core/NEsper.Core/compat/magic/MagicString.cs b/NEsper.Core/NEsper.Core/compat/magic/MagicString.cs new file mode 100755 index 000000000..7950a716a --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/magic/MagicString.cs @@ -0,0 +1,21 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; + +namespace com.espertech.esper.compat.magic +{ + public class MagicString + { + public static Func GetStringConformer(bool isCaseSensitive) + { + return isCaseSensitive ? (Func) (s => s) : (s => s.ToUpper()); + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/magic/MagicStringDictionary.cs b/NEsper.Core/NEsper.Core/compat/magic/MagicStringDictionary.cs new file mode 100755 index 000000000..15bdc0a08 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/magic/MagicStringDictionary.cs @@ -0,0 +1,120 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections; +using System.Collections.Generic; + +namespace com.espertech.esper.compat.magic +{ + public class MagicStringDictionary : IDictionary + { + private readonly IDictionary _realDictionary; + + public MagicStringDictionary(Object opaqueDictionary) + { + _realDictionary = (IDictionary) opaqueDictionary; + } + + public MagicStringDictionary(IDictionary realDictionary) + { + _realDictionary = realDictionary; + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public IEnumerator> GetEnumerator() + { + foreach( var entry in _realDictionary ) { + yield return new KeyValuePair(entry.Key, entry.Value); + } + } + + public void Add(KeyValuePair item) + { + _realDictionary.Add(new KeyValuePair(item.Key, (V) item.Value)); + } + + public void Clear() + { + _realDictionary.Clear(); + } + + public bool Contains(KeyValuePair item) + { + return _realDictionary.Contains(new KeyValuePair(item.Key, (V) item.Value)); + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + throw new NotSupportedException(); + } + + public bool Remove(KeyValuePair item) + { + return _realDictionary.Remove(new KeyValuePair(item.Key, (V) item.Value)); + } + + public int Count + { + get { return _realDictionary.Count; } + } + + public bool IsReadOnly + { + get { return _realDictionary.IsReadOnly; } + } + + public bool ContainsKey(string key) + { + return _realDictionary.ContainsKey(key); + } + + public void Add(string key, object value) + { + _realDictionary.Add(key, (V) value); + } + + public bool Remove(string key) + { + return _realDictionary.Remove(key); + } + + public bool TryGetValue(string key, out object value) + { + V item; + if (_realDictionary.TryGetValue(key, out item)) { + value = item; + return true; + } + + value = null; + return false; + } + + public object this[string key] + { + get { return _realDictionary[key]; } + set { _realDictionary[key] = (V) value; } + } + + public ICollection Keys + { + get { return _realDictionary.Keys; } + } + + public ICollection Values + { + get { return new MagicCollection(_realDictionary.Values); } + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/magic/MagicType.cs b/NEsper.Core/NEsper.Core/compat/magic/MagicType.cs new file mode 100755 index 000000000..7cc57b4dd --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/magic/MagicType.cs @@ -0,0 +1,1004 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.client.annotation; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.threading; +using com.espertech.esper.events; + +namespace com.espertech.esper.compat.magic +{ + public class MagicType + { + /// + /// Underlying type managed by magic type + /// + private readonly Type _type; + /// + /// MagicType for parent type + /// + private readonly MagicType _parent; + /// + /// Case insensitive property table + /// + private readonly IDictionary _ciPropertyTable = + new Dictionary(); + /// + /// Case sensitive property table + /// + private readonly IDictionary _csPropertyTable = + new Dictionary(); + + /// + /// Initializes a new instance of the class. + /// + /// The type. + public MagicType(Type type) + { + _type = type; + + var baseType = type.BaseType; + if (baseType != null) { + _parent = GetCachedType(baseType); + } + + IndexIndexedProperties(); + IndexMappedProperties(); + IndexSimpleProperties(); + } + + /// + /// Gets the type that magic type reflects. + /// + public Type TargetType + { + get { return _type; } + } + + /// + /// Gets the magic type for the base type. + /// + public MagicType BaseType + { + get { return _parent; } + } + + /// + /// Creates a new instance of the object. Assumes a default constructor. + /// + /// + public Object New() + { + return Activator.CreateInstance(_type); + } + + /// + /// Returns true if this type is an extension of the provided + /// baseType. This method will recurse the type tree to determine + /// if the extension occurs anywhere in the tree. If not found, + /// the method returns false. + /// + /// Type of the base. + /// + public bool ExtendsType(Type baseType) + { + if (_type == baseType) + return true; + if (_parent == null) + return false; + return _parent.ExtendsType(baseType); + } + + private static string GetPropertyName( string assumedName, ICustomAttributeProvider member ) + { + var attributes = member.GetCustomAttributes(typeof(PropertyNameAttribute), true); + if (attributes != null) + { + foreach (PropertyNameAttribute attribute in attributes) + { + return attribute.Name; + } + } + + return assumedName; + } + + private void AddProperty(string csName, string ciName, SimpleMagicPropertyInfo prop) + { + var ciProp = _ciPropertyTable.Get(ciName); + if (ciProp != null) { + ciProp.Next = prop; + } + else { + _ciPropertyTable[ciName] = prop; + } + + // It is possible for a property that is case sensitive to + // exist in csPropertyTable, but not ciPropertyTable. That's + // the nature of case sensitive versus case insensitive. + var csProp = _csPropertyTable.Get(csName); + if (csProp != null) { + csProp.Next = prop; + } + else { + _csPropertyTable[csName] = prop; + } + } + + /// + /// Indexes the simple properties. + /// + private void IndexSimpleProperties() + { + foreach (PropertyInfo propertyInfo in FetchSimpleProperties()) + { + var csName = GetPropertyName(propertyInfo.Name, propertyInfo); + var ciName = csName.ToUpper(); + var prop = new SimpleMagicPropertyInfo( + csName, + propertyInfo, + propertyInfo.GetGetMethod(), + propertyInfo.GetSetMethod(), + EventPropertyType.SIMPLE); + + AddProperty(csName, ciName, prop); + } + + foreach (MethodInfo methodInfo in FetchSimpleAccessors()) + { + var csName = GetAccessorPropertyName(methodInfo); + var ciName = csName.ToUpper(); + var setter = GetSimpleMutator(csName, methodInfo.ReturnType); + var prop = new SimpleMagicPropertyInfo( + csName, + methodInfo, + methodInfo, + setter, + EventPropertyType.SIMPLE); + + AddProperty(csName, ciName, prop); + } + } + + + /// + /// Indexes the mapped properties. + /// + private void IndexMappedProperties() + { +#if false + foreach (PropertyInfo propertyInfo in FetchMappedProperties()) + { + var csName = GetPropertyName(propertyInfo.Name, propertyInfo); + var ciName = csName.ToUpper(); + var prop = new SimpleMagicPropertyInfo( + csName, + propertyInfo, + propertyInfo.GetGetMethod(), + propertyInfo.GetSetMethod(), + EventPropertyType.MAPPED); + + AddProperty(csName, ciName, prop); + } +#endif + + // Mapped properties exposed through accessors may be exposed with both + // a GetXXX accessor and a SetXXX mutator. We need to merge both thought + // processes. + + + foreach (var accessorInfo in FetchMappedAccessors()) + { + var csName = GetAccessorPropertyName(accessorInfo); + var ciName = csName.ToUpper(); + var prop = new SimpleMagicPropertyInfo( + csName, + accessorInfo, + accessorInfo, + null, + EventPropertyType.MAPPED); + + AddProperty(csName, ciName, prop); + } + } + + /// + /// Indexes the indexed properties. + /// + private void IndexIndexedProperties() + { + foreach (PropertyInfo propertyInfo in FetchIndexedProperties()) + { + var csName = GetPropertyName(propertyInfo.Name, propertyInfo); + var ciName = csName.ToUpper(); + var prop = new SimpleMagicPropertyInfo( + csName, + propertyInfo, + propertyInfo.GetGetMethod(), + propertyInfo.GetSetMethod(), + EventPropertyType.INDEXED); + + AddProperty(csName, ciName, prop); + } + + foreach( MethodInfo methodInfo in FetchIndexedAccessors()) { + var csName = GetAccessorPropertyName(methodInfo); + var ciName = csName.ToUpper(); + var prop = new SimpleMagicPropertyInfo( + csName, + methodInfo, + methodInfo, + null, + EventPropertyType.INDEXED); + + AddProperty(csName, ciName, prop); + } + } + + /// + /// Gets all properties. + /// + /// if set to true [is case sensitive]. + /// The filter. + /// + public IEnumerable GetAllProperties(bool isCaseSensitive, Predicate filter) + { + var table = isCaseSensitive ? _csPropertyTable : _ciPropertyTable; + foreach (var entry in table) + { + for (var temp = entry.Value; temp != null; temp = temp.Next ) + { + if (filter.Invoke(temp)) + { + yield return temp; + } + } + } + } + + /// + /// Gets all properties. + /// + /// if set to true [case sensitive]. + /// + public IEnumerable GetAllProperties( bool isCaseSensitive ) + { + return GetAllProperties(isCaseSensitive, magicProperty => true); + } + + /// + /// Gets all simple properties. + /// + /// if set to true [case sensitive]. + /// + public IEnumerable GetSimpleProperties(bool isCaseSensitive) + { + return GetAllProperties(isCaseSensitive, magicProperty => magicProperty.EventPropertyType == EventPropertyType.SIMPLE); + } + + /// + /// Gets all mapped properties. + /// + /// if set to true [case sensitive]. + /// + public IEnumerable GetMappedProperties(bool isCaseSensitive) + { + return GetAllProperties(isCaseSensitive, magicProperty => magicProperty.EventPropertyType == EventPropertyType.MAPPED); + } + + /// + /// Gets all indexed properties. + /// + /// if set to true [case sensitive]. + /// + public IEnumerable GetIndexedProperties(bool isCaseSensitive) + { + return GetAllProperties(isCaseSensitive, magicProperty => magicProperty.EventPropertyType == EventPropertyType.INDEXED); + } + + /// + /// Resolves the complex property. + /// + /// Name of the property. + /// The resolution style. + /// + public MagicPropertyInfo ResolveComplexProperty(string propertyName, PropertyResolutionStyle resolutionStyle) + { + int indexOfDot = propertyName.IndexOf('.'); + if (indexOfDot != -1) { + var head = propertyName.Substring(0, indexOfDot); + var tail = propertyName.Substring(indexOfDot + 1); + var rootProperty = ResolveProperty(head, resolutionStyle); + var rootPropertyType = GetCachedType(rootProperty.PropertyType); + if (rootPropertyType == null) { + return null; + } + + var tailProperty = rootPropertyType.ResolveProperty(tail, resolutionStyle); + return new DynamicMagicPropertyInfo(rootProperty, tailProperty); + } + + return ResolveProperty(propertyName, resolutionStyle); + } + + /// + /// Finds the property. + /// + /// Name of the property. + /// if set to true [is case sensitive]. + /// + public SimpleMagicPropertyInfo ResolveProperty(string propertyName, PropertyResolutionStyle resolutionStyle ) + { + switch (resolutionStyle) { + case PropertyResolutionStyle.CASE_SENSITIVE: + do { + var property = _csPropertyTable.Get(propertyName); + if (property != null) + return property; + if (_parent != null) + return _parent.ResolveProperty(propertyName, resolutionStyle); + return null; + } while (false); + break; + + case PropertyResolutionStyle.CASE_INSENSITIVE: + do { + var property = _ciPropertyTable.Get(propertyName.ToUpper()); + if (property != null) + return property; + if (_parent != null) + return _parent.ResolveProperty(propertyName, resolutionStyle); + return null; + } while (false); + break; + + case PropertyResolutionStyle.DISTINCT_CASE_INSENSITIVE: + do { + var property = _ciPropertyTable.Get(propertyName.ToUpper()); + if (property != null) { + if (property.IsUnique) { + return property; + } + + throw new EPException("Unable to determine which property to use for \"" + propertyName + + "\" because more than one property matched"); + } + + if (_parent != null) + return _parent.ResolveProperty(propertyName, resolutionStyle); + return null; + } while (false); + break; + } + + return null; + } + + /// + /// Search of the type to find a property. + /// + /// The name. + /// The resolution style. + /// + public MethodInfo ResolvePropertyMethod( string propertyName, PropertyResolutionStyle resolutionStyle) + { + var property = ResolveProperty(propertyName, resolutionStyle) as SimpleMagicPropertyInfo; + return property != null ? property.GetMethod : null; + } + + /// + /// Gets the name of the mutator property. + /// + /// The mutator method. + /// + public string GetMutatorPropertyName(MethodInfo mutatorMethod) + { + var attributes = mutatorMethod.GetCustomAttributes(typeof(PropertyNameAttribute), true); + if (attributes != null) + { + foreach (PropertyNameAttribute attribute in attributes) + { + return attribute.Name; + } + } + + // Start by removing the "Insert" from the front of the mutatorMethod name + String inferredName = mutatorMethod.Name.Substring(3); + String newInferredName = null; + // Leave uppercase inferred names such as URL + if (inferredName.Length >= 2) + { + if (Char.IsUpper(inferredName[0]) && + Char.IsUpper(inferredName[1])) + { + newInferredName = inferredName; + } + } + // camelCase the inferred name + if (newInferredName == null) + { + newInferredName = Char.ToString(Char.ToUpper(inferredName[0])); + if (inferredName.Length > 1) + { + newInferredName += inferredName.Substring(1); + } + } + + return newInferredName; + } + + /// + /// Gets the name that should be assigned to the property bound to the accessorMethod + /// + /// + /// + + public string GetAccessorPropertyName(MethodInfo accessorMethod) + { + var attributes = accessorMethod.GetCustomAttributes(typeof(PropertyNameAttribute), true); + if (attributes != null) + { + foreach (PropertyNameAttribute attribute in attributes) + { + return attribute.Name; + } + } + + // Start by removing the "get" from the front of the accessorMethod name + String inferredName = accessorMethod.Name.Substring(3); + String newInferredName = null; + // Leave uppercase inferred names such as URL + if (inferredName.Length >= 2) + { + if (Char.IsUpper(inferredName[0]) && + Char.IsUpper(inferredName[1])) + { + newInferredName = inferredName; + } + } + // camelCase the inferred name + if (newInferredName == null) + { + newInferredName = Char.ToString(Char.ToUpper(inferredName[0])); + if (inferredName.Length > 1) + { + newInferredName += inferredName.Substring(1); + } + } + + return newInferredName; + } + + /// + /// Returns all simple properties + /// + /// + + public IEnumerable FetchSimpleProperties() + { + foreach (PropertyInfo propInfo in _type.GetProperties()) + { + var getMethod = propInfo.GetGetMethod(); + if ((getMethod != null) && + (getMethod.IsStatic == false) && + (getMethod.GetParameters().Length == 0)) + { + yield return propInfo; + } + } + } + + /// + /// Returns an enumerable that provides all accessors that take no + /// parameters. + /// + /// + + public IEnumerable FetchSimpleAccessors() + { + foreach (var methodInfo in GetAccessors()) + { + var methodParams = methodInfo.GetParameters(); + if (methodParams.Length == 0) + { + yield return methodInfo; + } + } + } + + public IEnumerable FetchIndexedProperties() + { + foreach (PropertyInfo propInfo in _type.GetProperties()) + { + var getMethod = propInfo.GetGetMethod(); + if (getMethod == null) + continue; + if (getMethod.IsStatic) + continue; + + var parameters = getMethod.GetParameters(); + if ((parameters.Length != 1) || (parameters[0].ParameterType != typeof(int))) + continue; + + yield return propInfo; + } + } + + /// + /// Returns an enumerable that provides all accessors that take one + /// parameter of type int. + /// + /// + + public IEnumerable FetchIndexedAccessors() + { + foreach (MethodInfo methodInfo in GetAccessors()) + { + ParameterInfo[] methodParams = methodInfo.GetParameters(); + if ((methodParams.Length == 1) && + (methodParams[0].ParameterType == typeof(int))) + { + yield return methodInfo; + } + } + } + + public IEnumerable FetchMappedProperties() + { + foreach (PropertyInfo propInfo in _type.GetProperties()) + { + var getMethod = propInfo.GetGetMethod(); + if (getMethod == null) + continue; + if (getMethod.IsStatic) + continue; + + var returnType = propInfo.PropertyType; + if (returnType.IsGenericStringDictionary()) + yield return propInfo; + } + } + + /// + /// Returns an enumerable that provides all accessors that take one + /// parameter of type string. + /// + /// + + public IEnumerable FetchMappedAccessors() + { + foreach (var methodInfo in GetAccessors()) + { + var methodParams = methodInfo.GetParameters(); + if ((methodParams.Length == 1) && + (methodParams[0].ParameterType == typeof(string))) + { + yield return methodInfo; + } + } + } + + /// + /// Enumerates all accessor methods for a type + /// + /// + + public IEnumerable GetAccessors() + { + foreach (var methodInfo in _type.GetMethods()) + { + var methodName = methodInfo.Name; + + if ((methodInfo.IsSpecialName == false) && + (methodName.StartsWith("Get")) && + (methodName != "Get")) + { + // We don't need any of the pseudo accessors from System.Object + if ((methodName == "GetHashCode") || + (methodName == "GetType")) { + continue; + } + + yield return methodInfo; + } + } + } + + public IEnumerable GetMutators() + { + foreach (var methodInfo in _type.GetMethods()) + { + var methodName = methodInfo.Name; + + if ((methodInfo.IsSpecialName == false) && + (methodInfo.GetParameters().Length == 1) && + (methodName.StartsWith("Set")) && + (methodName != "Set")) + { + yield return methodInfo; + } + } + } + + public IEnumerable GetMappableMutators() + { + foreach (var methodInfo in _type.GetMethods()) + { + var methodName = methodInfo.Name; + + if ((methodInfo.IsSpecialName == false) && + (methodInfo.GetParameters().Length == 2) && + (methodInfo.GetParameters()[0].ParameterType == typeof(string)) && + (methodName.StartsWith("Set")) && + (methodName != "Set")) + { + yield return methodInfo; + } + } + } + + private MethodInfo GetSimpleMutator(string propertyName, Type propertyType) + { + var methodName = "Set" + propertyName; + try + { + var methodInfo = _type.GetMethod(methodName); + if ((methodInfo != null) && methodInfo.IsPublic && !methodInfo.IsStatic) + { + var parameters = methodInfo.GetParameters(); + if ((parameters.Length == 1) && (parameters[0].ParameterType == propertyType)) + { + return methodInfo; + } + } + } + catch(AmbiguousMatchException) + { + var methods = _type + .GetMethods() + .Where(method => method.Name == methodName && method.IsPublic && !method.IsStatic); + foreach(var methodInfo in methods) + { + var parameters = methodInfo.GetParameters(); + if ((parameters.Length == 1) && (parameters[0].ParameterType == propertyType)) + { + return methodInfo; + } + } + + } + + return null; + } + + private static readonly ILockable TypeCacheLock = LockManager.CreateLock(MethodBase.GetCurrentMethod().DeclaringType); + private static readonly Dictionary TypeCacheTable = + new Dictionary(); + + public static MagicType GetCachedType(Type t) + { + using (TypeCacheLock.Acquire()) { + MagicType magicType; + if (!TypeCacheTable.TryGetValue(t, out magicType)) { + TypeCacheTable[t] = magicType = new MagicType(t); + } + + return magicType; + } + } + + private static readonly ILockable AccessorCacheLock = LockManager.CreateLock(MethodBase.GetCurrentMethod().DeclaringType); + private static readonly Dictionary> AccessorCacheTable = + new Dictionary>(); + + public static Func GetLambdaAccessor(MethodInfo methodInfo) + { + using (AccessorCacheLock.Acquire()) { + Func lambdaAccessor; + if (!AccessorCacheTable.TryGetValue(methodInfo, out lambdaAccessor)) { + var eParam = Expression.Parameter(typeof (object), "arg"); + var eCast1 = methodInfo.DeclaringType.IsValueType + ? Expression.Unbox(eParam, methodInfo.DeclaringType) + : Expression.Convert(eParam, methodInfo.DeclaringType); + var eMethod = Expression.Call(methodInfo.IsStatic ? null : eCast1, methodInfo); + var eCast2 = methodInfo.ReturnType.IsValueType + ? (Expression) Expression.Convert(eMethod, typeof(object)) + : eMethod; + var eLambda = Expression.Lambda>(eCast2, eParam); + AccessorCacheTable[methodInfo] = lambdaAccessor = eLambda.Compile(); + } + + return lambdaAccessor; + } + } + } + + #region MagicPropertyInfo + public abstract class MagicPropertyInfo + { + /// + /// Gets or sets the name. + /// + /// The name. + public string Name { get; protected set; } + + /// + /// Gets or sets the type of the property. + /// + /// The type of the property. + public Type PropertyType { get; protected set; } + + /// + /// Gets or sets the event type of the property. + /// + /// The type of the property. + public EventPropertyType EventPropertyType { get; protected set; } + + /// + /// Returns a function that can be used to obtain the value of the + /// property from an object instance. + /// + /// The get function. + abstract public Func GetFunction { get; } + + /// + /// Returns a function that can be used to set the value of the + /// property in an object instance. + /// + /// The set function. + abstract public Action SetFunction { get; } + + /// + /// Gets a value indicating whether this property can be set. + /// + /// true if this instance can write; otherwise, false. + abstract public bool CanWrite { get; } + + /// + /// Static cast method used in assignment. + /// + /// + /// The value. + /// + public static T CastTo(Object value) + { + if (value is T) + return (T)value; + + // Arrays need to be converted by looking at the internal elements + // within the array. Since value is more than likely going to be + // an array of System.Object, the conversion is basically a recursive + // call to this method. + if (typeof(T).IsArray) + { + var valueArray = value as Object[]; + if (valueArray == null) + { + return default(T); // null + } + + var subType = typeof(T).GetElementType(); + var subCast = typeof(MagicPropertyInfo) + .GetMethod("CastTo") + .MakeGenericMethod(subType); + + var returnArray = Array.CreateInstance(subType, valueArray.Length); + for (int ii = 0; ii < valueArray.Length; ii++) + { + returnArray.SetValue(subCast.Invoke(null, new[] { valueArray[ii] }), ii); + } + + return (T)((Object)returnArray); + } + + var genericTypeCaster = CastHelper.GetCastConverter(); + return genericTypeCaster(value); + } + } + + public class SimpleMagicPropertyInfo : MagicPropertyInfo + { + /// + /// Gets or sets the member. + /// + /// The member. + public MemberInfo Member { get; protected set; } + + /// + /// Gets or sets the get method. + /// + /// The get method. + public MethodInfo GetMethod { get; protected set; } + + /// + /// Gets or sets the set method. + /// + /// The set method. + public MethodInfo SetMethod { get; protected set; } + + /// + /// Gets a value indicating whether this property can be set. + /// + /// true if this instance can write; otherwise, false. + public override bool CanWrite + { + get { return SetMethod != null; } + } + + /// + /// Gets or sets the next magic property INFO that shares the same + /// name. + /// + /// The next. + public SimpleMagicPropertyInfo Next { get; internal set; } + + /// + /// Gets a value indicating whether this property is unique. If a property is + /// not unique then it shares the same name as another property but a different + /// implementation. + /// + /// true if this instance is unique; otherwise, false. + public bool IsUnique + { + get { return Next == null; } + } + + private Func _getFunction; + + /// + /// Returns a function that can be used to obtain the value of the + /// property from an object instance. + /// + /// The get function. + public override Func GetFunction + { + get + { + if (_getFunction == null) { + var eParam1 = Expression.Parameter(typeof(object), "obj"); + var eCast1 = Expression.ConvertChecked(eParam1, Member.DeclaringType); + var eMethod = Expression.Call(eCast1, GetMethod); + var eLambda = Expression.Lambda>(eMethod, eParam1); + _getFunction = eLambda.Compile(); + } + + return _getFunction; + } + } + + private Action _setFunction; + + /// + /// Returns a function that can be used to set the value of the + /// property in an object instance. + /// + /// The set function. + public override Action SetFunction + { + get + { + if ((SetMethod != null) && (_setFunction == null)) { + var castTo = typeof(MagicPropertyInfo) + .GetMethod("CastTo") + .MakeGenericMethod(GetMethod.ReturnType); + + var eParam1 = Expression.Parameter(typeof (object), "obj"); + var eParam2 = Expression.Parameter(typeof(object), "value"); + var eCast1 = Expression.ConvertChecked(eParam1, Member.DeclaringType); + var eCast2 = Expression.Call(castTo, eParam2); + var eMethod = Expression.Call(eCast1, SetMethod, eCast2); + var eLambda = Expression.Lambda>(eMethod, eParam1, eParam2); + _setFunction = eLambda.Compile(); + } + + return _setFunction; + } + } + + /// + /// Initializes a new instance of the class. + /// + /// The name. + /// The member. + /// The get method. + /// The set method. + /// Type of the property. + public SimpleMagicPropertyInfo(string name, MemberInfo member, MethodInfo getMethod, MethodInfo setMethod, EventPropertyType propertyType) + { + Name = name; + Member = member; + + if ( member is PropertyInfo ) + PropertyType = ((PropertyInfo) member).PropertyType; + else if ( member is MethodInfo ) + PropertyType = ((MethodInfo) member).ReturnType; + + GetMethod = getMethod; + SetMethod = setMethod; + EventPropertyType = propertyType; + } + } + + public class DynamicMagicPropertyInfo : MagicPropertyInfo + { + public MagicPropertyInfo Parent { get; private set; } + public MagicPropertyInfo Child { get; private set; } + + /// + /// Gets the value from an instance. + /// + /// The instance. + /// + private Object GetValue(Object instance) + { + var parentInstance = Parent.GetFunction.Invoke(instance); + var childInstance = Child.GetFunction.Invoke(parentInstance); + return childInstance; + } + + /// + /// Sets the value within an instance. + /// + /// The instance. + /// The value. + private void SetValue(Object instance, Object value) + { + var parentInstance = Parent.GetFunction.Invoke(instance); + Child.SetFunction.Invoke(parentInstance, value); + } + + /// + /// Returns a function that can be used to obtain the value of the + /// property from an object instance. + /// + /// The get function. + public override Func GetFunction + { + get { return GetValue; } + } + + /// + /// Returns a function that can be used to set the value of the + /// property in an object instance. + /// + /// The set function. + public override Action SetFunction + { + get { return SetValue; } + } + + /// + /// Gets a value indicating whether this property can be set. + /// + /// true if this instance can write; otherwise, false. + public override bool CanWrite + { + get { return Child.CanWrite; } + } + + /// + /// Initializes a new instance of the class. + /// + /// The parent. + /// The child. + public DynamicMagicPropertyInfo(MagicPropertyInfo parent, MagicPropertyInfo child) + { + Parent = parent; + Child = child; + } + } + + #endregion +} diff --git a/NEsper.Core/NEsper.Core/compat/threading/BaseLock.cs b/NEsper.Core/NEsper.Core/compat/threading/BaseLock.cs new file mode 100755 index 000000000..e38457c1a --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/threading/BaseLock.cs @@ -0,0 +1,49 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; + +namespace com.espertech.esper.compat.threading +{ + /// + /// Base class for disposable lock pattern. + /// + abstract public class BaseLock : IDisposable + { + /// Acquire text. + internal const string ACQUIRE_TEXT = "Acquire "; + + /// Acquired text. + internal const string ACQUIRED_TEXT = "Got "; + + /// Release text. + internal const string RELEASE_TEXT = "Release "; + + /// Released text. + internal const string RELEASED_TEXT = "Freed "; + + public static int RLockTimeout; + public static int WLockTimeout; + public static int MLockTimeout; + + static BaseLock() + { + RLockTimeout = CompatSettings.Default.ReaderLockTimeout; + WLockTimeout = CompatSettings.Default.WriterLockTimeout; + MLockTimeout = CompatSettings.Default.MonitorLockTimeout; + } + + + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public abstract void Dispose(); + } +} diff --git a/NEsper.Core/NEsper.Core/compat/threading/BasicExecutorService.cs b/NEsper.Core/NEsper.Core/compat/threading/BasicExecutorService.cs new file mode 100755 index 000000000..632a89707 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/threading/BasicExecutorService.cs @@ -0,0 +1,485 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Threading; +using com.espertech.esper.compat.logging; + + +namespace com.espertech.esper.compat.threading +{ + /// + /// Class that provides access to threadPool like services. This class exists to + /// provide an easier bridge between the CLR thread pool and the JVM thread pool + /// mechanisms. + /// + + public class BasicExecutorService : IExecutorService + { + private static int _idIncrement; + + private readonly int _id; + private readonly ManualResetEvent _futuresMonitor; + private readonly List _futuresPending; + private bool _isActive; + private bool _isShutdown; + private long _numExecuted; + private long _numSubmitted; + private long _numRecycled; + + /// + /// Gets the number of items executed. + /// + /// The num executed. + public int NumExecuted + { + get { return (int) Interlocked.Read(ref _numExecuted); } + } + + public int NumSubmitted + { + get { return (int)Interlocked.Read(ref _numSubmitted); } + } + + public BasicExecutorService() + { + _id = Interlocked.Increment(ref _idIncrement); + + int workerThreads; + int completionThreads; + + _futuresMonitor = new ManualResetEvent(false); + _futuresPending = new List(); + _isActive = true; + _isShutdown = false; + _numExecuted = 0; + _numSubmitted = 0; + _numRecycled = 0; + } + + /// + /// Initializes a new instance of the class. + /// + public BasicExecutorService(int maxNumThreads) + { + _id = Interlocked.Increment(ref _idIncrement); + + int workerThreads; + int completionThreads; + + ThreadPool.GetMaxThreads(out workerThreads, out completionThreads); + if (maxNumThreads != -1) + { + ThreadPool.SetMaxThreads(maxNumThreads, completionThreads); + } + + if (Log.IsDebugEnabled) + { + Log.Debug(String.Format(".ctor - Creating Executor with maxNumThreads = {0}", maxNumThreads)); + } + + _futuresMonitor = new ManualResetEvent(false); + _futuresPending = new List(); + _isActive = true; + _isShutdown = false; + _numExecuted = 0; + } + + /// + /// Dispatches the future. + /// + private void DispatchFuture(Object userData) + { + var future = userData as FutureBase; + + try + { + if (_isActive) + { + if (Log.IsInfoEnabled) + { + Log.Info(".DispatchFuture - Instance {0} dispatching item", _id); + } + + if (future != null) + { + future.Invoke(); + Interlocked.Increment(ref _numExecuted); + } + } + } + catch (Exception e) + { + Log.Error(".DispatchFuture - Instance {0} failed", _id); + Log.Error(".DispatchFuture", e); + } + finally + { + Recycle(future); + } + } + + private void Recycle(FutureBase future) + { + lock (_futuresPending) + { + _futuresPending.Remove(future); + + if (Log.IsInfoEnabled) { + Log.Info( + ".DispatchFuture - Instance {0} done dispatching: {1} pending", + _id, + _futuresPending.Count); + } + + if (_futuresPending.Count == 0) + { + _futuresMonitor.Set(); + } + } + + Interlocked.Increment(ref _numRecycled); + } + + /// + /// Submits the specified runnable to the thread pool. + /// + /// The runnable. + public Future Submit(Action runnable) + { + Func function = + () => { runnable.Invoke(); return null; }; + return Submit(function); + } + + /// + /// Submits the specified callable to the thread pool. + /// + /// The callable. + /// + public Future Submit(ICallable callable) + { + Func function = callable.Call; + return Submit(function); + } + + /// + /// Submits the specified callable to the thread pool. + /// + /// The callable. + /// + public Future Submit(Func callable) + { + if (_isShutdown) + { + throw new IllegalStateException("ExecutorService is shutdown"); + } + + Interlocked.Increment(ref _numSubmitted); + + var future = new FutureImpl {Callable = callable}; + + lock (_futuresPending) + { + _futuresPending.Add(future); + + if (Log.IsInfoEnabled) { + Log.Info( + ".Submit - Instance {0} queued user work item: {1} pending", + _id, + _futuresPending.Count); + } + } + + ThreadPool.QueueUserWorkItem(DispatchFuture, future); + + return future; + } + + /// + /// Shutdowns this instance. + /// + public void Shutdown() + { + if (Log.IsInfoEnabled) + { + Log.Info(".Shutdown - Marking instance {0} to avoid further queuing", _id); + } + + // Mark the executor as inactive so that we + // don't take any new callables. + _isShutdown = true; + } + + /// + /// Awaits the termination. + /// + public void AwaitTermination() + { + AwaitTermination(new TimeSpan(0, 0, 15)); + } + + /// + /// Awaits the termination. + /// + /// The timeout. + public void AwaitTermination(TimeSpan timeout) + { + if (Log.IsInfoEnabled) + { + Log.Info( + ".AwaitTermination - Instance {0} waiting for {1} tasks to complete", + _id, _futuresPending.Count); + } + + if (_futuresPending.Count != 0) + { + _futuresMonitor.WaitOne(timeout, true); + if ( _futuresPending.Count != 0 ) + { + _futuresPending.ForEach( + delegate(FutureBase futureBase) + { + Log.Warn(".AwaitTermination - Forceably terminating future"); + futureBase.Kill(); + }); + } + } + + if (Log.IsInfoEnabled) { + Log.Info( + ".AwaitTermination - Instance {0} marked inactive with {1} tasks to complete", + _id, + _futuresPending.Count); + } + + _isActive = false; + } + + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + } + + /// + /// Class that provides access to threadPool like services. This class exists to + /// provide an easier bridge between the CLR thread pool and the JVM thread pool + /// mechanisms. + /// + /// + public class Executors + { + /// + /// Supposably creates a new thread pool and returns the executor. Ours does + /// nothing as we use the CLR thread pool. + /// + /// + public static BasicExecutorService NewCachedThreadPool() + { + return new BasicExecutorService(-1); + } + + /// + /// Supposably creates a new thread pool and returns the executor. Ours does + /// nothing as we use the CLR thread pool. + /// + /// The max num threads. + /// + public static BasicExecutorService NewFixedThreadPool(int maxNumThreads) + { + return new BasicExecutorService(maxNumThreads); + } + + /// + /// Supposably creates a new thread pool and returns the executor. + /// + /// + public static BasicExecutorService NewSingleThreadExecutor() + { + return new BasicExecutorService(1); + } + } + + public interface Future + { + /// + /// Gets a value indicating whether this instance has value. + /// + /// true if this instance has value; otherwise, false. + bool HasValue { get; } + + /// + /// Gets the value. If a value is not available before the timeout expires, + /// a TimeoutException will be thrown. + /// + /// The time out. + /// + T GetValue(TimeSpan timeOut); + + /// + /// Gets the result value from the execution. + /// + /// + T GetValueOrDefault(); + } + + /// + /// Default implementation of a future + /// + public interface Future : Future + { + } + + /// + /// Base class for all future implementations + /// + abstract internal class FutureBase + { + private Thread _execThread; + + /// + /// Gets the exec thread. + /// + /// The exec thread. + public Thread ExecThread + { + get { return _execThread; } + } + + /// + /// Invokes the impl. + /// + protected abstract void InvokeImpl(); + + /// + /// Invokes this instance. + /// + public void Invoke() + { + Interlocked.Exchange(ref _execThread, Thread.CurrentThread); + try + { + InvokeImpl(); + } + catch (ThreadInterruptedException) + { + Log.Warn(".Invoke - Thread Interrupted"); + } + finally + { + Interlocked.Exchange(ref _execThread, null); + } + } + + /// + /// Kills this instance. + /// + internal void Kill() + { + Thread tempThread = Interlocked.Exchange(ref _execThread, null); + if (tempThread != null) + { + Log.Warn(".Kill - Forceably terminating future"); + tempThread.Interrupt(); + } + } + + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + } + + internal class FutureImpl + : FutureBase + , Future + { + private T _value; + + /// + /// Initializes a new instance of the class. + /// + public FutureImpl() + { + _hasValue = false; + _value = default(T); + } + + private bool _hasValue; + + /// + /// Gets a value indicating whether this instance has value. + /// + /// true if this instance has value; otherwise, false. + public bool HasValue + { + get { return _hasValue; } + } + + /// + /// Gets or sets the value. + /// + /// The value. + public T Value + { + get + { + if (! HasValue) { + throw new InvalidOperationException(); + } + + return _value; + } + set + { + _value = value; + _hasValue = true; + } + } + + /// + /// Gets the value. If a value is not available before the timeout expires, + /// a TimeoutException will be thrown. + /// + /// The time out. + /// + public T GetValue(TimeSpan timeOut) + { + throw new NotSupportedException("implementation does not provide blocking mechanics"); + } + + /// + /// Gets the result value from the execution. + /// + /// + public T GetValueOrDefault() + { + if (! _hasValue) { + return default(T); + } + + return Value; + } + + /// + /// Gets or sets the callable. + /// + /// The callable. + internal Func Callable { get; set; } + + /// + /// Invokes this instance. + /// + protected override void InvokeImpl() + { + Value = Callable.Invoke(); + if ( Log.IsInfoEnabled ) + { + Log.Info("Invoke - Completed with return value of {0}", Value); + } + } + + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + } +} diff --git a/NEsper.Core/NEsper.Core/compat/threading/CommonReadLock.cs b/NEsper.Core/NEsper.Core/compat/threading/CommonReadLock.cs new file mode 100755 index 000000000..30fce15cd --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/threading/CommonReadLock.cs @@ -0,0 +1,99 @@ + +using System; + +namespace com.espertech.esper.compat.threading +{ + /// + /// Description of CommonReadLock. + /// + internal sealed class CommonReadLock + : ILockable + { + private readonly IReaderWriterLockCommon _lockObj; + private readonly IDisposable _disposableObj; + + public IDisposable Acquire() + { + _lockObj.AcquireReaderLock(BaseLock.RLockTimeout); + return _disposableObj; + } + + public IDisposable Acquire(long msec) + { + _lockObj.AcquireReaderLock(msec); + return _disposableObj; + } + + public IDisposable Acquire(bool releaseLock, long? msec = null) + { + _lockObj.AcquireReaderLock(msec ?? BaseLock.RLockTimeout); + if (releaseLock) + return _disposableObj; + return new VoidDisposable(); + } + + public IDisposable ReleaseAcquire() + { + _lockObj.ReleaseReaderLock(); + return new TrackedDisposable(() => _lockObj.AcquireReaderLock(BaseLock.RLockTimeout)); + } + + public void Release() + { + _lockObj.ReleaseReaderLock(); + } + + internal CommonReadLock(IReaderWriterLockCommon lockObj) + { + _lockObj = lockObj; + _disposableObj = new TrackedDisposable(_lockObj.ReleaseReaderLock); + } + } + + /// + /// Description of CommonReadLock. + /// + internal sealed class CommonReadLock : ILockable + { + private readonly IReaderWriterLockCommon _lockObj; + private T _lockValue; + + public IDisposable Acquire() + { + _lockValue = _lockObj.AcquireReaderLock(BaseLock.RLockTimeout); + return new TrackedDisposable(() => _lockObj.ReleaseReaderLock(_lockValue)); + } + + public IDisposable Acquire(long msec) + { + _lockValue = _lockObj.AcquireReaderLock(msec); + return new TrackedDisposable(() => _lockObj.ReleaseReaderLock(_lockValue)); + } + + public IDisposable Acquire(bool releaseLock, long? msec = null) + { + _lockValue = _lockObj.AcquireReaderLock(msec ?? BaseLock.RLockTimeout); + if (releaseLock) + return new TrackedDisposable(() => _lockObj.ReleaseReaderLock(_lockValue)); + return new VoidDisposable(); + } + + public IDisposable ReleaseAcquire() + { + _lockObj.ReleaseReaderLock(_lockValue); + _lockValue = default(T); + return new TrackedDisposable(() => _lockValue = _lockObj.AcquireReaderLock(BaseLock.RLockTimeout)); + } + + public void Release() + { + _lockObj.ReleaseReaderLock(_lockValue); + _lockValue = default(T); + } + + internal CommonReadLock(IReaderWriterLockCommon lockObj) + { + _lockObj = lockObj; + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/threading/CommonWriteLock.cs b/NEsper.Core/NEsper.Core/compat/threading/CommonWriteLock.cs new file mode 100755 index 000000000..eceede6ab --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/threading/CommonWriteLock.cs @@ -0,0 +1,96 @@ + +using System; + +namespace com.espertech.esper.compat.threading +{ + /// + /// Description of CommonWriteLock. + /// + internal class CommonWriteLock : ILockable + { + private readonly IReaderWriterLockCommon _lockObj; + + public IDisposable Acquire() + { + _lockObj.AcquireWriterLock(BaseLock.WLockTimeout); + return new TrackedDisposable(() => _lockObj.ReleaseWriterLock()); + } + + public IDisposable Acquire(long msec) + { + _lockObj.AcquireWriterLock(msec); + return new TrackedDisposable(() => _lockObj.ReleaseWriterLock()); + } + + public IDisposable Acquire(bool releaseLock, long? msec = null) + { + _lockObj.AcquireWriterLock(msec ?? BaseLock.WLockTimeout); + if (releaseLock) + return new TrackedDisposable(() => _lockObj.ReleaseWriterLock()); + return new VoidDisposable(); + } + + public IDisposable ReleaseAcquire() + { + _lockObj.ReleaseWriterLock(); + return new TrackedDisposable(() => _lockObj.AcquireWriterLock(BaseLock.RLockTimeout)); + } + + public void Release() + { + _lockObj.ReleaseWriterLock(); + } + + internal CommonWriteLock(IReaderWriterLockCommon lockObj) + { + _lockObj = lockObj; + } + } + + /// + /// Description of CommonWriteLock. + /// + internal class CommonWriteLock : ILockable + { + private readonly IReaderWriterLockCommon _lockObj; + private T _lockValue; + + public IDisposable Acquire() + { + _lockValue = _lockObj.AcquireWriterLock(BaseLock.WLockTimeout); + return new TrackedDisposable(() => _lockObj.ReleaseWriterLock(_lockValue)); + } + + public IDisposable Acquire(long msec) + { + _lockValue = _lockObj.AcquireWriterLock(msec); + return new TrackedDisposable(() => _lockObj.ReleaseWriterLock(_lockValue)); + } + + public IDisposable Acquire(bool releaseLock, long? msec = null) + { + _lockValue = _lockObj.AcquireWriterLock(msec ?? BaseLock.WLockTimeout); + if (releaseLock) + return new TrackedDisposable(() => _lockObj.ReleaseWriterLock(_lockValue)); + return new VoidDisposable(); + } + + public IDisposable ReleaseAcquire() + { + _lockObj.ReleaseWriterLock(_lockValue); + _lockValue = default(T); + return new TrackedDisposable(() => _lockValue = _lockObj.AcquireWriterLock(BaseLock.RLockTimeout)); + } + + public void Release() + { + _lockObj.ReleaseWriterLock(_lockValue); + _lockValue = default(T); + } + + internal CommonWriteLock(IReaderWriterLockCommon lockObj) + { + _lockObj = lockObj; + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/threading/CountDownLatch.cs b/NEsper.Core/NEsper.Core/compat/threading/CountDownLatch.cs new file mode 100755 index 000000000..021f77cb6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/threading/CountDownLatch.cs @@ -0,0 +1,65 @@ +using System; +using System.Threading; + +namespace com.espertech.esper.compat.threading +{ + public class CountDownLatch + { + private long _latchCount; + + public CountDownLatch(long latchCount) + { + _latchCount = latchCount; + } + + /// + /// Returns the number of outstanding latches that have not been + /// removed. + /// + /// The count. + public long Count + { + get { return Interlocked.Read(ref _latchCount); } + } + + public void CountDown() + { + if (Interlocked.Decrement(ref _latchCount) == 0) + { + + } + } + + /// + /// Waits for the latch to be released for up to the specified amount of time. + /// If the timeout expires a TimeoutException is thrown. + /// + /// The timeout. + /// + public bool Await(TimeSpan timeout) + { + var timeCur = DateTimeHelper.CurrentTimeMillis; + var timeEnd = timeCur + (long) timeout.TotalMilliseconds; + var iteration = 0; + + while(Interlocked.Read(ref _latchCount) > 0) + { + if (!SlimLock.SmartWait(++iteration, timeEnd)) + { + return false; + } + } + + return true; + } + + /// + /// Awaits this instance. + /// + /// + public bool Await() + { + return Await(TimeSpan.MaxValue); + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/threading/DedicatedExecutorService.cs b/NEsper.Core/NEsper.Core/compat/threading/DedicatedExecutorService.cs new file mode 100755 index 000000000..ba20234cc --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/threading/DedicatedExecutorService.cs @@ -0,0 +1,305 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Threading; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; + +namespace com.espertech.esper.compat.threading +{ + public class DedicatedExecutorService : IExecutorService + { + private readonly Guid _id; + private readonly int _numThreads; + private readonly Thread[] _threads; + private readonly IBlockingQueue _taskQueue; + private long _tasksRunning; + private LiveMode _liveMode; + private long _numExecuted; + + enum LiveMode + { + RUN, + STOPPING, + STOPPED + } + + public event ThreadExceptionEventHandler TaskError; + + /// + /// Initializes a new instance of the class. + /// + /// The label. + /// The num threads. + public DedicatedExecutorService(string label, int numThreads) + : this(label, numThreads, new LinkedBlockingQueue()) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The label. + /// The num threads. + /// The task queue. + public DedicatedExecutorService(string label, int numThreads, IBlockingQueue taskQueue) + { + _id = Guid.NewGuid(); + _numThreads = numThreads; + _threads = new Thread[numThreads]; + _tasksRunning = 0L; + _taskQueue = taskQueue; + _liveMode = LiveMode.RUN; + _numExecuted = 0L; + + for( int ii = 0 ; ii < _numThreads ; ii++) { + _threads[ii] = new Thread(HandleTasksInQueue); + _threads[ii].Name = "DE:" + label + ":" + _id + ":" + ii; + _threads[ii].IsBackground = true; + _threads[ii].Start(); + } + } + + /// + /// Gets the number of tasks executed. + /// + /// The number of tasks executed. + public long NumExecuted + { + get { return Interlocked.Read(ref _numExecuted); } + } + + /// + /// Handles the tasks in queue. + /// + private void HandleTasksInQueue() + { + bool isDebugEnabled = Log.IsDebugEnabled; + + Log.Debug("HandleTasksInQueue: thread {0} starting with {1}", Thread.CurrentThread.Name, _taskQueue.GetType().Name); + + using (ScopedInstance>.Set(_taskQueue)) // introduces the queue into scope + { + while (_liveMode != LiveMode.STOPPED) + { + Runnable task; + + Interlocked.Increment(ref _tasksRunning); + try + { + if (_taskQueue.Pop(500, out task)) + { + try + { + task.Invoke(); + } + catch (Exception e) + { + Log.Warn("HandleTasksInQueue: finished with abnormal termination", e); + + if (TaskError != null) + { + TaskError(this, new ThreadExceptionEventArgs(e)); + } + } + finally + { + Interlocked.Increment(ref _numExecuted); + } + } + else if (_liveMode == LiveMode.STOPPING) + { + if (isDebugEnabled) + { + Log.Debug("HandleTasksInQueue: no items detected in queue, terminating"); + } + break; + } + else if (isDebugEnabled) + { + Log.Debug("HandleTasksInQueue: no items detected in queue, start loop again"); + } + } + finally + { + Interlocked.Decrement(ref _tasksRunning); + } + } + } + + Log.Debug("HandleTasksInQueue: thread ending"); + } + + #region Implementation of IExecutorService + + /// + /// Submits the specified runnable to the thread pool. + /// + /// The runnable. + /// + public Future Submit(Action runnable) + { + var future = new SimpleFutureImpl(); + _taskQueue.Push(runnable.Invoke); + return future; + } + + /// + /// Submits the specified callable to the thread pool. + /// + /// The callable. + /// + public Future Submit(ICallable callable) + { + var future = new SimpleFutureImpl(); + _taskQueue.Push( + () => future.Value = callable.Call()); + return future; + } + + /// + /// Submits the specified callable to the thread pool. + /// + /// The callable. + /// + public Future Submit(Func callable) + { + var future = new SimpleFutureImpl(); + _taskQueue.Push( + delegate + { + future.Value = callable.Invoke(); + }); + return future; + } + + /// + /// Shutdowns this instance. + /// + public void Shutdown() + { + _liveMode = LiveMode.STOPPING; + + Log.Debug(".Shutdown - Marking instance " + _id + " to avoid further queuing"); + } + + /// + /// Awaits the termination. + /// + public void AwaitTermination() + { + AwaitTermination(new TimeSpan(0, 0, 15)); + } + + /// + /// Awaits the termination. + /// + /// The timeout. + public void AwaitTermination(TimeSpan timeout) + { + _liveMode = LiveMode.STOPPING; + + long endTime = DateTime.Now.Ticks + ((long) timeout.TotalMilliseconds)*10000; + long nowTime; + + long taskCount; + + do { + taskCount = _taskQueue.Count + Interlocked.Read(ref _tasksRunning); + Log.Debug(".AwaitTermination - Instance {0} waiting for {1} tasks to complete", _id, taskCount); + + if (taskCount == 0) + break; + + Thread.Sleep(10); + nowTime = DateTime.Now.Ticks; + } while (nowTime < endTime); + + _liveMode = LiveMode.STOPPED ; + + Log.Debug(".AwaitTermination - Instance {0} ending for {1} tasks to complete", _id, taskCount); + } + + #endregion + + private class SimpleFutureImpl : Future + { + private T _value; + + /// + /// Initializes a new instance of the class. + /// + public SimpleFutureImpl() + { + _hasValue = false; + _value = default(T); + } + + private bool _hasValue; + + /// + /// Gets a value indicating whether this instance has value. + /// + /// true if this instance has value; otherwise, false. + public bool HasValue + { + get { return _hasValue; } + } + + /// + /// Gets or sets the value. + /// + /// The value. + public T Value + { + get + { + if (!HasValue) + { + throw new InvalidOperationException(); + } + + return _value; + } + set + { + _value = value; + _hasValue = true; + } + } + + /// + /// Gets the value. + /// + /// The time out. + /// + public T GetValue(TimeSpan timeOut) + { + throw new NotSupportedException(); + } + + /// + /// Gets the result value from the execution. + /// + /// + public T GetValueOrDefault() + { + if (!_hasValue) + { + return default(T); + } + + return Value; + } + } + + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + } +} diff --git a/NEsper.Core/NEsper.Core/compat/threading/DummyReaderWriterLock.cs b/NEsper.Core/NEsper.Core/compat/threading/DummyReaderWriterLock.cs new file mode 100755 index 000000000..a9aac952b --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/threading/DummyReaderWriterLock.cs @@ -0,0 +1,59 @@ + +using System; + +namespace com.espertech.esper.compat.threading +{ + /// + /// Uses a standard lock to model a reader-writer ... not for general use + /// + public class DummyReaderWriterLock + : IReaderWriterLock + { + private static readonly VoidDisposable Disposable = new VoidDisposable(); + + /// + /// Constructs a new instance of a DummyReaderWriterLock + /// + public DummyReaderWriterLock() + { + ReadLock = WriteLock = LockManager.CreateDefaultLock(); + } + + /// + /// Gets the read-side lockable + /// + public ILockable ReadLock { get; private set; } + + /// + /// Gets the write-side lockable + /// + public ILockable WriteLock { get; private set; } + + public IDisposable AcquireReadLock() + { + return Disposable; + } + + public IDisposable AcquireWriteLock() + { + return Disposable; + } + + + /// + /// Indicates if the writer lock is held. + /// + /// + /// The is writer lock held. + /// + public bool IsWriterLockHeld + { + get { return false; } + } + + +#if DEBUG + public bool Trace { get; set; } +#endif + } +} diff --git a/NEsper.Core/NEsper.Core/compat/threading/FairReaderWriterLock.cs b/NEsper.Core/NEsper.Core/compat/threading/FairReaderWriterLock.cs new file mode 100755 index 000000000..2ad9f36a7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/threading/FairReaderWriterLock.cs @@ -0,0 +1,302 @@ +using System; +using System.Threading; + +namespace com.espertech.esper.compat.threading +{ + public class FairReaderWriterLock + : IReaderWriterLock + , IReaderWriterLockCommon + { + /// + /// Main lock + /// + private SpinLock _uMainLock; + + private LockFlags _uLockFlags; + + private int _uLockOwner; + + private int _uSharedCount; + + /// + /// Initializes a new instance of the class. + /// + public FairReaderWriterLock() + { + _uMainLock = new SpinLock(true); + _uLockFlags = LockFlags.None; + _uSharedCount = 0; + + ReadLock = new CommonReadLock(this); + WriteLock = new CommonWriteLock(this); + } + + /// + /// Gets the read-side lockable + /// + /// + public ILockable ReadLock { get; private set; } + + /// + /// Gets the write-side lockable + /// + /// + public ILockable WriteLock { get; private set; } + + public IDisposable AcquireReadLock() + { + return ReadLock.Acquire(); + } + + public IDisposable AcquireWriteLock() + { + return WriteLock.Acquire(); + } + + /// + /// Indicates if the writer lock is held. + /// + /// + /// The is writer lock held. + /// + public bool IsWriterLockHeld + { + get + { + var hasWriteLock = new bool[] { false }; + + WithMainLock( + () => + { + hasWriteLock[0] = + (_uLockOwner == System.Threading.Thread.CurrentThread.ManagedThreadId) && + ((_uLockFlags & LockFlags.Exclusive) == LockFlags.Exclusive); + }); + + return hasWriteLock[0]; + } + } + +#if DEBUG + /// + /// Gets or sets a value indicating whether this is TRACE. + /// + /// true if TRACE; otherwise, false. + public bool Trace { get; set; } +#endif + + /// + /// Executes the action within the mainlock. + /// + private void WithMainLock(Action action) + { + var lockTaken = false; + + try + { + _uMainLock.Enter(ref lockTaken); + if (lockTaken) + { + action.Invoke(); + return; + } + + throw new TimeoutException("unable to secure main lock"); + } + finally + { + if (lockTaken) + { + _uMainLock.Exit(); + } + } + } + + /// + /// Executes the action within the mainlock. An end time is provided to this + /// call to set the last millisecond in which this lock must be obtained. + /// + /// + /// The current time. + /// The end time. + /// The action. + /// + private T WithMainLock (long timeCur, long timeEnd, Func action) + { + var timeOut = timeEnd - timeCur; + if (timeOut > 0) + { + var lockTaken = false; + + try + { + _uMainLock.TryEnter((int) timeOut, ref lockTaken); + if (lockTaken) + { + return action.Invoke(); + } + } + finally + { + if (lockTaken) + { + _uMainLock.Exit(); + } + } + } + + throw new TimeoutException("unable to secure main lock"); + } + + /// + /// Acquires the reader lock. + /// + /// The timeout. + public void AcquireReaderLock(long timeout) + { + var timeCur = DateTimeHelper.CurrentTimeMillis; + var timeEnd = timeCur + timeout; + var ii = 0; + + for (;;) + { + var result = WithMainLock( + timeCur, + timeEnd, + () => + { + if ((_uLockFlags == LockFlags.None) || + (_uLockFlags == LockFlags.Shared)) + { + _uSharedCount++; + return true; + } + + return false; + }); + if (result) + { + return; + } + + SlimLock.SmartWait(++ii); + + timeCur = DateTimeHelper.CurrentTimeMillis; + } + } + + /// + /// Acquires the writer lock. + /// + /// The timeout. + public void AcquireWriterLock(long timeout) + { + var timeCur = DateTimeHelper.CurrentTimeMillis; + var timeEnd = timeCur + timeout; + var ii = 0; + + var upgrade = new bool[] {false}; + + try + { + for (;;) + { + var result = WithMainLock( + timeCur, + timeEnd, + () => + { + if (_uLockFlags == LockFlags.None) + { + _uLockFlags = LockFlags.Exclusive; + _uLockOwner = System.Threading.Thread.CurrentThread.ManagedThreadId; + return true; + } + else if (_uLockFlags == LockFlags.Shared) + { + _uLockFlags |= LockFlags.ExclusiveUpgrade; + upgrade[0] = true; + return false; + } + else if (_uLockFlags == LockFlags.ExclusiveUpgrade) + { + // shared flag has been cleared + upgrade[0] = false; + _uLockFlags = LockFlags.Exclusive; + _uLockOwner = System.Threading.Thread.CurrentThread.ManagedThreadId; + return true; + } + // Exclusive - wait + + return false; + }); + if (result) + { + return; + } + + SlimLock.SmartWait(++ii); + + timeCur = DateTimeHelper.CurrentTimeMillis; + } + } + finally + { + if (upgrade[0]) + { + WithMainLock(() => _uLockFlags &= ~LockFlags.ExclusiveUpgrade); + } + } + } + + private void ReleaseReaderInternal() + { + if (--_uSharedCount == 0) + { + _uLockFlags &= ~LockFlags.Shared; + } + } + + /// + /// Releases the reader lock. + /// + public void ReleaseReaderLock() + { + WithMainLock(ReleaseReaderInternal); + } + + private void ReleaseWriterInternal() + { + _uLockFlags &= ~LockFlags.Exclusive; + } + + /// + /// Releases the writer lock. + /// + public void ReleaseWriterLock() + { + WithMainLock(ReleaseWriterInternal); + } + + [Flags] + internal enum LockFlags + { + /// + /// No flags are set + /// + None = 0x0000, + /// + /// Lock is in a shared state ... + /// + Shared = 0x0001, + /// + /// Lock is in an exclusive state ... + /// + Exclusive = 0x0002, + /// + /// Exclusive upgrade pending ... + /// + ExclusiveUpgrade = 0x0004, + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/threading/FastThreadLocal.cs b/NEsper.Core/NEsper.Core/compat/threading/FastThreadLocal.cs new file mode 100755 index 000000000..4a6c1d5c8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/threading/FastThreadLocal.cs @@ -0,0 +1,379 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Threading; + +namespace com.espertech.esper.compat.threading +{ + /// + /// IThreadLocal provides the engine with a way to store information that + /// is local to the instance and a the thread. While the CLR provides the + /// ThreadStatic attribute, it can only be applied to static variables; + /// some usage patterns in esper (such as statement-specific thread-specific + /// processing data) require that data be associated by instance and thread. + /// The CLR provides a solution to this known as LocalDataStoreSlot. It + /// has been documented that this method is slower than its ThreadStatic + /// counterpart, but it allows for instance-based allocation. + /// + /// During recent testing it was determined that the LocalDataStoreSlot was + /// using an amount of time that seemed a bit excessive. We took some + /// snapshots of performance under the profiler. Using that information we + /// retooled the class to provide tight and fast access to thread-local + /// instance-specific data. The class is pretty tightly wound and takes a + /// few liberties in understanding how esper uses it. A ThreadStatic + /// variable is initialized for the IThreadLocal. This item is 'thread-local' + /// and contains an array of 'instance-specific' data. Indexing is done + /// when the IThreadLocal item is created. Under esper this results in roughly + /// one 'index' per statement. Changes to this model resulted in good cost + /// savings in the retrieval and acquisition of local data. + /// + /// + + public sealed class FastThreadLocal : IThreadLocal + where T : class + { + // Technique Config Cycles time-ms avg time-us + // IThreadLocal Release 1183734 6200.5 5.238085583 + // IThreadLocal Release 1224525 5126.6 4.186602968 + // IThreadLocal Release 1153012 5935.3 5.147648073 + // Hashtable Debug 1185562 3848.1 3.245802413 + // List Debug 996737 1678 1.683493238 + // Array Debug 924738 1032 1.115991773 + // Array Debug 1179226 1328.4 1.126501621 + // Array Release 1224513 1296.4 1.058706604 + + private static long _typeInstanceId = 0; + + private static readonly Queue IndexReclaim = new Queue(); + + private readonly int _instanceId; + + /// + /// Gets the instance id ... if you really must know. + /// + /// The instance id. + public int InstanceId + { + get { return _instanceId; } + } + + internal class StaticData + { + internal T[] Table; + internal int Count; + internal T Last; + internal int LastIndx; + + internal StaticData() + { + Table = new T[100]; + Count = Table.Length; + Last = default(T); + LastIndx = -1; + } + } + + /// + /// List of weak reference data. This list is allocated when the + /// class is instantiated and keeps track of data that is allocated + /// regardless of thread. Minimal locks should be used to ensure + /// that normal IThreadLocal activity is not placed in the crossfire + /// of this structure. + /// + private static readonly LinkedList> ThreadDataList; + + /// + /// Lock for the _threadDataList + /// + private static readonly IReaderWriterLock ThreadDataListLock; + + /// + /// Initializes the class. + /// + static FastThreadLocal() + { + ThreadDataList = new LinkedList>(); + ThreadDataListLock = ReaderWriterLockManager.CreateLock(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + } + + [ThreadStatic] + private static StaticData _threadData; + + /// + /// Factory delegate for construction of data on miss. + /// + + private readonly FactoryDelegate _dataFactory; + + private static StaticData GetThreadData() + { + StaticData lThreadData = _threadData; + if (lThreadData != null) + { + return lThreadData; + } + + _threadData = lThreadData = new StaticData(); + using (ThreadDataListLock.AcquireWriteLock()) + { + ThreadDataList.AddLast(new WeakReference(_threadData)); + } + + return lThreadData; //_table; + } + + private static StaticData GetThreadData(int index) + { + var lThreadData = _threadData; + if (lThreadData == null) + { + _threadData = lThreadData = new StaticData(); + using (ThreadDataListLock.AcquireWriteLock()) + { + ThreadDataList.AddLast(new WeakReference(_threadData)); + } + } + + if (index >= lThreadData.Count) + { + Rebalance(lThreadData, index); + } + + return lThreadData; + } + + private static StaticData CreateThreadData(int index) + { + StaticData lThreadData = _threadData = new StaticData(); + using (ThreadDataListLock.AcquireWriteLock()) + { + ThreadDataList.AddLast(new WeakReference(_threadData)); + } + + //T[] lTable = lThreadData.Table; + if (index >= lThreadData.Count) + { + Rebalance(lThreadData, index); + } + + return lThreadData; + } + + private static void Rebalance(StaticData lThreadData, int index) + { + var lTable = lThreadData.Table; + var tempTable = new T[index + 100 - index%100]; + Array.Copy(lTable, tempTable, lTable.Length); + lThreadData.Table = tempTable; + lThreadData.Count = tempTable.Length; + //return lTable; + } + + /// + /// Gets or sets the value. + /// + /// The value. + public T Value + { + get + { + int lInstanceId = _instanceId; + T[] lThreadData = GetThreadData().Table; + T value = lThreadData.Length > lInstanceId + ? lThreadData[lInstanceId] + : default(T); + + return value; + } + + set + { + var lInstanceId = _instanceId; + var lThreadData = GetThreadData(lInstanceId).Table; + lThreadData[lInstanceId] = value; + } + } + + /// + /// Gets the data or creates it if not found. + /// + /// + public T GetOrCreate() // MSIL Length - 0031 bytes + { + var sThreadData = _threadData; + if (sThreadData != null) { + if (sThreadData.LastIndx == _instanceId) { + return sThreadData.Last; + } + } else { + sThreadData = CreateThreadData(_instanceId); + } + + return ReturnRest(sThreadData); + } + + private T ReturnRest(StaticData sThreadData) + { + if (sThreadData.Count <= _instanceId) { + Rebalance(sThreadData, _instanceId); + } + + var value = sThreadData.Table[_instanceId]; + if (value == null) { + value = sThreadData.Table[_instanceId] = _dataFactory(); + } + + sThreadData.LastIndx = _instanceId; + sThreadData.Last = value; + return value; + } + + /// + /// Clears all threads + /// + public void ClearAll() + { + int lInstance = _instanceId; + + using (ThreadDataListLock.AcquireReadLock()) + { + LinkedList>.Enumerator threadDataEnum = + ThreadDataList.GetEnumerator(); + while (threadDataEnum.MoveNext()) + { + WeakReference threadDataRef = threadDataEnum.Current; + if (threadDataRef.IsAlive) + { + StaticData threadData = threadDataRef.Target; + if (threadData != null) + { + if (threadData.Count > lInstance) + { + threadData.Table[lInstance] = null; + } + + continue; + } + } + + // Anything making it to this point indicates that the thread + // has probably terminated and we are still keeping it's static + // data weakly referenced in the threadDataList. We can safely + // remove it, but it needs to be done with a writerLock. + } + } + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + ClearAll(); + + lock (IndexReclaim) + { + IndexReclaim.Enqueue(_instanceId); + } + } + + #region ISerializable Members +#if false + public void GetObjectData(SerializationInfo INFO, StreamingContext context) + { + INFO.AddValue("m_dataFactory", m_dataFactory, typeof(FactoryDelegate)); + } +#endif + #endregion + + /// + /// Releases unmanaged resources and performs other cleanup operations before the + /// is reclaimed by garbage collection. + /// + ~FastThreadLocal() + { + Dispose(); + } + + /// + /// Allocates a usable index. This method looks in the indexReclaim + /// first to determine if there is a slot that has been released. If so, + /// it is reclaimed. If no space is available, a new index is allocated. + /// This can lead to growth of the static data table. + /// + /// + private static int AllocateIndex() + { + if (IndexReclaim.Count != 0) + { + lock (IndexReclaim) + { + if (IndexReclaim.Count != 0) + { + return IndexReclaim.Dequeue() ; + } + } + } + + // indexes in the above range have already been allocated to the + // table space, so it not necessary to worry about them... however, + // down here we may be seeing table growth. + + var index = (int) Interlocked.Increment(ref _typeInstanceId); + if (index >= _threadData.Table.Length) + { + var tempTable = new T[_threadData.Table.Length << 1]; + Array.Copy(_threadData.Table, 0, tempTable, 0, _threadData.Table.Length); + _threadData.Table = tempTable; + } + + return index; + } + + /// + /// Initializes a new instance of the class. + /// + /// The factory. + public FastThreadLocal( FactoryDelegate factory ) + { + _instanceId = AllocateIndex(); + _dataFactory = factory; + } + +#if false + public FastThreadLocal(SerializationInfo information, StreamingContext context) + { + m_instanceId = AllocateIndex(); + m_dataFactory = (FactoryDelegate) information.GetValue("m_dataFactory", typeof (FactoryDelegate)); + } +#endif + } + + /// + /// Creates fast thread local objects. + /// + public class FastThreadLocalFactory : ThreadLocalFactory + { + #region ThreadLocalFactory Members + + /// + /// Create a thread local object of the specified type param. + /// + /// + /// + /// + public IThreadLocal CreateThreadLocal(FactoryDelegate factory) where T : class + { + return new FastThreadLocal(factory); + } + + #endregion + } +} diff --git a/NEsper.Core/NEsper.Core/compat/threading/FastThreadStore.cs b/NEsper.Core/NEsper.Core/compat/threading/FastThreadStore.cs new file mode 100755 index 000000000..e3457ca9f --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/threading/FastThreadStore.cs @@ -0,0 +1,249 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; +using System.Threading; + +namespace com.espertech.esper.compat.threading +{ + /// + /// FastThreadStore is a variation of the FastThreadLocal, but it lacks a factory + /// for object creation. While there are plenty of cases where this makes sense, + /// we actually did this to work around an issue in .NET 3.5 SP1. + /// + /// + + [Serializable] + public class FastThreadStore + where T : class + { + private static long _typeInstanceId = 0; + + private static readonly Queue IndexReclaim = new Queue(); + + [NonSerialized] + private int _instanceId = 0; + + /// + /// Gets the instance id ... if you really must know. + /// + /// The instance id. + public int InstanceId + { + get + { + int temp; + while(( temp = Interlocked.CompareExchange(ref _instanceId, -1, 0) ) <= 0) { + if ( temp == 0 ) { + Interlocked.Exchange(ref _instanceId, temp = AllocateIndex()); + return temp; + } + + Thread.Sleep(0); + } + + return temp; + } + } + + internal class StaticData + { + internal T[] Table; + internal int Count; + internal T Last; + internal int LastIndx; + + internal StaticData() + { + Table = new T[100]; + Count = Table.Length; + Last = default(T); + LastIndx = -1; + } + } + + /// + /// List of weak reference data. This list is allocated when the + /// class is instantiated and keeps track of data that is allocated + /// regardless of thread. Minimal locks should be used to ensure + /// that normal IThreadLocal activity is not placed in the crossfire + /// of this structure. + /// + private static readonly LinkedList> ThreadDataList; + + /// + /// Lock for the _threadDataList + /// + private static readonly IReaderWriterLock ThreadDataListLock; + + /// + /// Initializes the class. + /// + static FastThreadStore() + { + ThreadDataList = new LinkedList>(); + ThreadDataListLock = ReaderWriterLockManager.CreateLock(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + } + + [ThreadStatic] + private static StaticData _threadData; + + private static StaticData GetThreadData() + { + StaticData lThreadData = _threadData; + if (lThreadData == null) + { + _threadData = lThreadData = new StaticData(); + using (ThreadDataListLock.AcquireWriteLock()) + { + ThreadDataList.AddLast(new WeakReference(_threadData)); + } + } + + return lThreadData; //._table; + } + + private static StaticData GetThreadData(int index) + { + StaticData lThreadData = _threadData; + if (lThreadData == null) + { + _threadData = lThreadData = new StaticData(); + using (ThreadDataListLock.AcquireWriteLock()) + { + ThreadDataList.AddLast(new WeakReference(_threadData)); + } + } + + T[] lTable = lThreadData.Table; + if (index >= lThreadData.Count) + { + Rebalance(lThreadData, index, lTable); + } + + return lThreadData; + } + + private static void Rebalance(StaticData lThreadData, int index, T[] lTable) + { + var tempTable = new T[index + 100 - index%100]; + Array.Copy(lTable, tempTable, lTable.Length); + lThreadData.Table = lTable = tempTable; + lThreadData.Count = lTable.Length; + //return lTable; + } + + /// + /// Gets or sets the value. + /// + /// The value. + public T Value + { + get + { + int lInstanceId = InstanceId; + T[] lThreadData = GetThreadData().Table; + T value = lThreadData.Length > lInstanceId + ? lThreadData[lInstanceId] + : default(T); + + return value; + } + + set + { + int lInstanceId = InstanceId; + T[] lThreadData = GetThreadData(lInstanceId).Table; + lThreadData[lInstanceId] = value; + } + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + int lInstance = InstanceId; + + using (ThreadDataListLock.AcquireReadLock()) + { + LinkedList>.Enumerator threadDataEnum = + ThreadDataList.GetEnumerator(); + while (threadDataEnum.MoveNext()) + { + WeakReference threadDataRef = threadDataEnum.Current; + if (threadDataRef.IsAlive) + { + StaticData threadData = threadDataRef.Target; + if (threadData != null) + { + if (threadData.Count > lInstance) + { + threadData.Table[lInstance] = null; + } + + continue; + } + } + + // Anything making it to this point indicates that the thread + // has probably terminated and we are still keeping it's static + // data weakly referenced in the threadDataList. We can safely + // remove it, but it needs to be done with a writerLock. + } + } + + lock (IndexReclaim) + { + IndexReclaim.Enqueue(lInstance); + } + } + + /// + /// Releases unmanaged resources and performs other cleanup operations before the + /// is reclaimed by garbage collection. + /// + ~FastThreadStore() + { + Dispose(); + } + + /// + /// Allocates a usable index. This method looks in the indexReclaim + /// first to determine if there is a slot that has been released. If so, + /// it is reclaimed. If no space is available, a new index is allocated. + /// This can lead to growth of the static data table. + /// + /// + private static int AllocateIndex() + { + if (IndexReclaim.Count != 0) + { + lock (IndexReclaim) + { + if (IndexReclaim.Count != 0) + { + return IndexReclaim.Dequeue() ; + } + } + } + + return (int) Interlocked.Increment(ref _typeInstanceId); + } + + /// + /// Initializes a new instance of the class. + /// + public FastThreadStore() + { + _instanceId = AllocateIndex(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/threading/FifoReaderWriterLock.cs b/NEsper.Core/NEsper.Core/compat/threading/FifoReaderWriterLock.cs new file mode 100755 index 000000000..8498b136f --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/threading/FifoReaderWriterLock.cs @@ -0,0 +1,293 @@ +#if DEBUG +#define STATISTICS +#endif + +using System; +using System.Threading; + +namespace com.espertech.esper.compat.threading +{ + public class FifoReaderWriterLock + : IReaderWriterLock + , IReaderWriterLockCommon + { + private Node _rnode; + private Node _wnode; + +#if STATISTICS + private readonly string _id; +#endif + + /// + /// Initializes a new instance of the class. + /// + public FifoReaderWriterLock() + { +#if STATISTICS + _id = Convert.ToBase64String(Guid.NewGuid().ToByteArray()); +#endif + + _wnode = _rnode = new Node(NodeFlags.None); + +#if STATISTICS + _wnode.LockId = _id; +#endif + + ReadLock = new CommonReadLock(this); + WriteLock = new CommonWriteLock(this); + } + + /// + /// Gets the read-side lockable + /// + /// + public ILockable ReadLock { get; private set; } + + /// + /// Gets the write-side lockable + /// + /// + public ILockable WriteLock { get; private set; } + + public IDisposable AcquireReadLock() + { + return ReadLock.Acquire(); + } + + public IDisposable AcquireWriteLock() + { + return WriteLock.Acquire(); + } + + /// + /// Indicates if the writer lock is held. + /// + /// + /// The is writer lock held. + /// + public bool IsWriterLockHeld + { + get + { + var node = _rnode; + return + node != null && + node.ThreadId == Thread.CurrentThread.ManagedThreadId && + node.Flags == NodeFlags.Exclusive; + } + } + +#if DEBUG + /// + /// Gets or sets a value indicating whether this is TRACE. + /// + /// true if TRACE; otherwise, false. + public bool Trace { get; set; } +#endif + + /// + /// Pushes a node onto the end of the chain. + /// + /// + private Node PushNode(Node node) + { + var curr = _wnode; + +#if STATISTICS + node.LockId = _id; +#endif + + for (; ;) + { + Node temp; + + while((temp = curr.Next) != null) + { + curr = temp; // temp is guaranteed to not be null + } + + var pnode = Interlocked.CompareExchange( + ref curr.Next, + node, + null); + if (pnode == null) + { + _wnode = node; + return node; + } + } + } + + /// + /// Acquires the reader lock. + /// + /// The timeout. + public Node AcquireReaderLock(long timeout) + { + var timeCur = DateTimeHelper.CurrentTimeMillis; + var timeEnd = timeCur + timeout; + + var curr = _rnode; + var node = PushNode(new Node(NodeFlags.Shared)); + var iter = 0; + + + for( ;; ) + { +#if STATISTICS + node.Iterations++; +#endif + if (curr == node) { +#if STATISTICS + node.TimeAcquire = PerformanceObserver.MicroTime; +#endif + return _rnode = node; + } else if (curr.Flags == NodeFlags.Shared) { + curr = curr.Next; +#if STATISTICS + node.ChainLength++; +#endif + } else if (curr.Flags == NodeFlags.Exclusive) { + SlimLock.SmartWait(++iter); + } else if (curr.Flags == NodeFlags.None) { + curr = curr.Next; // dead node +#if STATISTICS + node.ChainLength++; +#endif + } else { + throw new IllegalStateException(); + } + } + } + + /// + /// Acquires the writer lock. + /// + /// The timeout. + public Node AcquireWriterLock(long timeout) + { + var timeCur = DateTimeHelper.CurrentTimeMillis; + var timeEnd = timeCur + timeout; + + var curr = _rnode; + var node = PushNode(new Node(NodeFlags.Exclusive)); + var iter = 0; + + for( ;; ) + { +#if STATISTICS + node.Iterations++; +#endif + + if (curr == node) { +#if STATISTICS + node.TimeAcquire = PerformanceObserver.MicroTime; +#endif + return _rnode = node; + } else if (curr.Flags == NodeFlags.Shared) { + SlimLock.SmartWait(++iter); + } else if (curr.Flags == NodeFlags.Exclusive) { + SlimLock.SmartWait(++iter); + } else if (curr.Flags == NodeFlags.None) { + iter = 0; // clear wait cycling + curr = curr.Next; // dead node +#if STATISTICS + node.ChainLength++; +#endif + } else { + throw new IllegalStateException(); + } + } + } + + /// + /// Releases the reader lock. + /// + public void ReleaseReaderLock(Node node) + { +#if STATISTICS + node.TimeRelease = PerformanceObserver.MicroTime; +#endif + + node.Flags = NodeFlags.None; + +#if (DEBUG && STATISTICS) + node.DumpStatistics(); +#endif + } + + /// + /// Releases the writer lock. + /// + public void ReleaseWriterLock(Node node) + { +#if STATISTICS + node.TimeRelease = PerformanceObserver.MicroTime; +#endif + + node.Flags = NodeFlags.None; + +#if (DEBUG && STATISTICS) + node.DumpStatistics(); +#endif + } + + internal enum NodeFlags + { + None = 0, + Shared = 1, + Exclusive = 2, + } + + public sealed class Node + { +#if STATISTICS + internal String LockId; + internal NodeFlags OrigFlags; + internal long TimeRequest; + internal long TimeAcquire; + internal long TimeRelease; + internal int ChainLength; + internal int Iterations; +#endif + + internal int ThreadId; + internal Node Next; + internal NodeFlags Flags; + +#if STATISTICS + internal void DumpStatistics() + { + #if false + if (Iterations > 10) + { + Console.WriteLine("E:{0}:{1}:{2}:{3}:{4}:{5}", + LockId, + OrigFlags, + Iterations, + TimeAcquire - TimeRequest, + TimeRelease - TimeRequest, + TimeRelease - TimeAcquire + ); + } + #endif + } +#endif + + /// + /// Initializes a new instance of a node + /// + /// + internal Node(NodeFlags flags) + { + Flags = flags; + ThreadId = System.Threading.Thread.CurrentThread.ManagedThreadId; + +#if STATISTICS + OrigFlags = flags; + TimeRequest = PerformanceObserver.MicroTime; +#endif + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/threading/IExecutorService.cs b/NEsper.Core/NEsper.Core/compat/threading/IExecutorService.cs new file mode 100755 index 000000000..442644341 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/threading/IExecutorService.cs @@ -0,0 +1,52 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.compat.threading +{ + public interface IExecutorService + { + /// + /// Submits the specified runnable to the thread pool. + /// + /// The runnable. + /// + Future Submit(Action runnable); + + /// + /// Submits the specified callable to the thread pool. + /// + /// The callable. + /// + Future Submit(ICallable callable); + + /// + /// Submits the specified callable to the thread pool. + /// + /// The callable. + /// + Future Submit(Func callable); + + /// + /// Shutdowns this instance. + /// + void Shutdown(); + + /// + /// Awaits the termination. + /// + void AwaitTermination(); + + /// + /// Awaits the termination. + /// + /// The timeout. + void AwaitTermination(TimeSpan timeout); + } +} diff --git a/NEsper.Core/NEsper.Core/compat/threading/ILockable.cs b/NEsper.Core/NEsper.Core/compat/threading/ILockable.cs new file mode 100755 index 000000000..34a62c636 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/threading/ILockable.cs @@ -0,0 +1,56 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.compat.threading +{ + /// + /// A simple locking mechanism + /// + + public interface ILockable + { + /// + /// Acquires the lock; the lock is released when the disposable + /// object that was returned is disposed. + /// + /// + IDisposable Acquire(); + + /// + /// Acquire the lock; the lock is released when the disposable + /// object that was returned is disposed IF the releaseLock + /// flag is set. + /// + /// + /// + /// + IDisposable Acquire(bool releaseLock, long? msec = null); + + /// + /// Acquires the specified msec. + /// + /// The msec. + /// + IDisposable Acquire(long msec); + + /// + /// Provides a temporary release of the lock if it is acquired. When the + /// disposable object that is returned is disposed, the lock is re-acquired. + /// This method is effectively the opposite of acquire. + /// + /// + IDisposable ReleaseAcquire(); + + /// + /// Releases this instance. + /// + void Release(); + } +} diff --git a/NEsper.Core/NEsper.Core/compat/threading/IReaderWriterLock.cs b/NEsper.Core/NEsper.Core/compat/threading/IReaderWriterLock.cs new file mode 100755 index 000000000..9f0fb5ef4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/threading/IReaderWriterLock.cs @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.compat.threading +{ + public interface IReaderWriterLock + { + /// + /// Gets the read-side lockable + /// + ILockable ReadLock { get; } + + /// + /// Gets the write-side lockable + /// + ILockable WriteLock { get; } + + /// + /// Acquires the read lock; the lock is released when the disposable + /// object that was returned is disposed. + /// + /// + IDisposable AcquireReadLock(); + + /// + /// Acquires the write lock; the lock is released when the disposable + /// object that was returned is disposed. + /// + /// + IDisposable AcquireWriteLock(); + + /// + /// Indicates if the writer lock is held. + /// + /// + /// The is writer lock held. + /// + bool IsWriterLockHeld { get; } + +#if DEBUG + bool Trace { get; set; } +#endif + } +} diff --git a/NEsper.Core/NEsper.Core/compat/threading/IReaderWriterLockCommon.cs b/NEsper.Core/NEsper.Core/compat/threading/IReaderWriterLockCommon.cs new file mode 100755 index 000000000..77a0c8bf8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/threading/IReaderWriterLockCommon.cs @@ -0,0 +1,55 @@ +namespace com.espertech.esper.compat.threading +{ + /// + /// Simple boilerplate for common reader-writer lock implementations + /// + public interface IReaderWriterLockCommon + { + /// + /// Acquires the reader lock. + /// + /// The timeout. + void AcquireReaderLock(long timeout); + + /// + /// Acquires the writer lock. + /// + /// The timeout. + void AcquireWriterLock(long timeout); + + /// + /// Releases the reader lock. + /// + void ReleaseReaderLock(); + + /// + /// Releases the writer lock. + /// + void ReleaseWriterLock(); + } + + public interface IReaderWriterLockCommon + { + /// + /// Acquires the reader lock. + /// + /// The timeout. + T AcquireReaderLock(long timeout); + + /// + /// Acquires the writer lock. + /// + /// The timeout. + T AcquireWriterLock(long timeout); + + /// + /// Releases the reader lock. + /// + void ReleaseReaderLock(T value); + + /// + /// Releases the writer lock. + /// + void ReleaseWriterLock(T value); + } +} diff --git a/NEsper.Core/NEsper.Core/compat/threading/IThreadLocal.cs b/NEsper.Core/NEsper.Core/compat/threading/IThreadLocal.cs new file mode 100755 index 000000000..5a53ad0d0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/threading/IThreadLocal.cs @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; + +namespace com.espertech.esper.compat.threading +{ + /// + /// IThreadLocal provides the engine with a way to store information that + /// is local to the instance and a the thread. While the CLR provides the + /// ThreadStatic attribute, it can only be applied to static variables; + /// some usage patterns in esper (such as statement-specific thread-specific + /// processing data) require that data be associated by instance and thread. + /// The CLR provides a solution to this known as LocalDataStoreSlot. It + /// has been documented that this method is slower than its ThreadStatic + /// counterpart, but it allows for instance-based allocation. + /// + /// + + public interface IThreadLocal : IDisposable + where T : class + { + /// + /// Gets or sets the value. + /// + /// The value. + T Value + { + get; + set; + } + + /// + /// Gets the data or creates it if not found. + /// + /// + T GetOrCreate(); + } +} diff --git a/NEsper.Core/NEsper.Core/compat/threading/LockConstants.cs b/NEsper.Core/NEsper.Core/compat/threading/LockConstants.cs new file mode 100755 index 000000000..1168bd679 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/threading/LockConstants.cs @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +namespace com.espertech.esper.compat.threading +{ + /// + /// Constants we keep for our locking algorithms. + /// + + public class LockConstants + { + /// + /// Number of milliseconds until write locks timeout + /// + public const int WriterTimeout = 50000; + } +} diff --git a/NEsper.Core/NEsper.Core/compat/threading/LockManager.cs b/NEsper.Core/NEsper.Core/compat/threading/LockManager.cs new file mode 100755 index 000000000..3147df03d --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/threading/LockManager.cs @@ -0,0 +1,197 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.compat.threading +{ + public class LockManager + { + private static readonly Object CategoryFactoryTableLock = new object(); + private static readonly IDictionary> CategoryFactoryTable = + new Dictionary>(); + + /// + /// Gets or sets a value indicating whether this instance is telemetry enabled. + /// + /// + /// true if this instance is telemetry enabled; otherwise, false. + /// + public static bool IsTelemetryEnabled { get; set; } + + /// + /// Gets or sets the default lock factory. + /// + /// The default lock factory. + public static Func DefaultLockFactory { get; set; } + + /// + /// Initializes the class. + /// + static LockManager() + { + DefaultLockFactory = CreateMonitorLock; + IsTelemetryEnabled = false; + + // Establishes the default locking style + var defaultLockTypeName = CompatSettings.Default.DefaultLockType; + if (String.IsNullOrEmpty(defaultLockTypeName)) { + return; + } + + switch( defaultLockTypeName.ToUpper() ) { + case "MONITOR": + case "MONITORLOCK": + DefaultLockFactory = CreateMonitorLock; + break; + case "SPIN": + case "MONITORSPIN": + case "MONITORSPINLOCK": + DefaultLockFactory = CreateMonitorSpinLock; + break; + case "SLIM": + case "MONITORSLIM": + case "MONITORSLIMLOCK": + DefaultLockFactory = CreateMonitorSlimLock; + break; + case "VOID": + DefaultLockFactory = CreateVoidLock; + break; + default: + throw new ArgumentException("unknown lock type '" + defaultLockTypeName + "'"); + } + } + + /// + /// Registers the category lock. + /// + /// The type category. + /// The lock factory. + public static void RegisterCategoryLock(Type typeCategory, Func lockFactory) + { + RegisterCategoryLock(typeCategory.FullName, lockFactory); + } + + /// + /// Registers the category lock. + /// + /// The category. + /// The lock factory. + public static void RegisterCategoryLock(string category, Func lockFactory) + { + lock( CategoryFactoryTableLock ) { + CategoryFactoryTable[category] = lockFactory; + } + } + + /// + /// Creates a lock for the category defined by the type. + /// + /// The type category. + /// + public static ILockable CreateLock(Type typeCategory) + { + return CreateLock(typeCategory.FullName); + } + + /// + /// Wraps the lock. + /// + /// + private static ILockable WrapLock(ILockable @lock) + { + if (IsTelemetryEnabled) { + return new TelemetryLock(@lock); + } + + return @lock; + } + + /// + /// Creates a lock for the category. + /// + /// The category. + /// + public static ILockable CreateLock(string category) + { + if (category != null) { + lock (CategoryFactoryTableLock) { + category = category.TrimEnd('.'); + + while( category != String.Empty ) { + Func lockFactory; + // Lookup a factory for the category + if (CategoryFactoryTable.TryGetValue(category, out lockFactory)) { + return WrapLock(lockFactory.Invoke()); + } + // Lock factory not found, back-up one segment of the category + int lastIndex = category.LastIndexOf('.'); + if (lastIndex == -1) { + break; + } + + category = category.Substring(0, lastIndex).TrimEnd('.'); + } + } + } + + return CreateDefaultLock(); + } + + /// + /// Creates the default lock. + /// + /// + public static ILockable CreateDefaultLock() + { + var lockFactory = DefaultLockFactory; + if (lockFactory == null) { + throw new ApplicationException("default lock factory is not set"); + } + + return WrapLock(lockFactory.Invoke()); + } + + /// + /// Creates the monitor lock. + /// + /// + public static ILockable CreateMonitorLock() + { + return new MonitorLock(); + } + + /// + /// Creates the monitor spin lock. + /// + /// + public static ILockable CreateMonitorSpinLock() + { + return new MonitorSpinLock(); + } + + /// + /// Creates the monitor slim lock. + /// + /// + public static ILockable CreateMonitorSlimLock() + { + return new MonitorSlimLock(); + } + + /// + /// Creates a void lock. + /// + /// + public static ILockable CreateVoidLock() + { + return new VoidLock(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/threading/LockableExtensions.cs b/NEsper.Core/NEsper.Core/compat/threading/LockableExtensions.cs new file mode 100755 index 000000000..5f233e72e --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/threading/LockableExtensions.cs @@ -0,0 +1,33 @@ +using System; + +namespace com.espertech.esper.compat.threading +{ + public static class LockableExtensions + { + /// + /// Executes an observable call within the scope of the lock. + /// + /// The lockable. + /// The observable call. + public static void Call(this ILockable lockable, Action observableCall) + { + using(lockable.Acquire()) { + observableCall.Invoke(); + } + } + + /// + /// Executes a function within the scope of the lock. + /// + /// + /// The lockable. + /// The function. + /// + public static T Call(this ILockable lockable, Func function) + { + using (lockable.Acquire()) { + return function.Invoke(); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/threading/MicroThread.cs b/NEsper.Core/NEsper.Core/compat/threading/MicroThread.cs new file mode 100755 index 000000000..313fcf579 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/threading/MicroThread.cs @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Threading; + +namespace com.espertech.esper.compat.threading +{ + public class MicroThread + { + public static void Sleep(long uSeconds) + { + var spins = PerformanceObserverWin.SpinIterationsPerMicro; + var uHead = PerformanceObserver.MicroTime; + var uTail = uHead + uSeconds; + var uApprox = uSeconds/spins; + + do + { + Thread.SpinWait((int) uApprox); + uHead = PerformanceObserver.MicroTime; + if (uHead >= uTail) + return; + + uApprox = (uTail - uHead)/spins; + } while (true); + } + + public static void SleepNano(long nanoSeconds) + { + var spins = 1000*PerformanceObserverWin.SpinIterationsPerMicro; + var uHead = PerformanceObserver.NanoTime; + var uTail = uHead + nanoSeconds; + var uApprox = nanoSeconds/spins; + + do + { + Thread.SpinWait((int)uApprox); + uHead = PerformanceObserver.NanoTime; + if (uHead >= uTail) + return; + + uApprox = (uTail - uHead)/spins; + } while (true); + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/threading/MonitorLock.cs b/NEsper.Core/NEsper.Core/compat/threading/MonitorLock.cs new file mode 100755 index 000000000..263b7e0bf --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/threading/MonitorLock.cs @@ -0,0 +1,243 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Threading; + +namespace com.espertech.esper.compat.threading +{ + /// + /// MonitorLock is a class for assisting people with synchronized operations. + /// Traditionally, code might have looked something like this: + /// + /// lock( object ) { + /// ... + /// } + /// + /// However, this has a few issues. It's prone to deadlock because the lock + /// operator does not have a timeout. It's also difficult to determine who + /// owns a lock at a given time. So eventually people changed to this form: + /// + /// if (Monitor.TryEnter(object, timeout)) { + /// try { + /// ... + /// } finally { + /// Monitor.Exit(object); + /// } + /// } + /// + /// It gets bulky and begins to become difficult to maintain over time. + /// MonitorLock works much like the lock( object ) model except that it relies + /// upon the IDisposable interface to help with scoping of the lock. So to + /// use MonitorLock, first instantiate one and then replace your lock(object) + /// with this: + /// + /// using(lockObj.Acquire()) { + /// ... + /// } + /// + /// Your code will work as before except that the monitorLock will use a timed + /// entry into critical sections and it can be used to diagnose issues that + /// may be occuring in your thread locking. + /// + /// MonitorLock allows users to specify events that can be consumed on lock + /// acquisition or release. Additionally, it can inform you when a lock + /// is acquired within an existing lock. And last, if you want to know where + /// your locks are being acquired, it can maintain a StackTrace of points + /// where allocations are occuring. + /// + /// + + public class MonitorLock : ILockable + { + /// + /// Gets the number of milliseconds until the lock acquisition fails. + /// + /// The lock timeout. + + public int LockTimeout + { + get { return _uLockTimeout; } + } + + /// + /// Uniquely identifies the lock. + /// + + private readonly Guid _uLockId; + + /// + /// Underlying object that is locked + /// + + private readonly Object _uLockObj; + + /// + /// Number of milliseconds until the lock acquisition fails + /// + + private readonly int _uLockTimeout; + + /// + /// Owner of the lock. + /// + + private Thread _uLockOwner; + + /// + /// Used to track recursive locks. + /// + + private int _uLockDepth; + + /// + /// Gets the lock depth. + /// + /// The lock depth. + + public int LockDepth + { + get { return _uLockDepth; } + } + + /// + /// Initializes a new instance of the class. + /// + public MonitorLock() + : this( BaseLock.MLockTimeout ) + { + } + + /// + /// Initializes a new instance of the class. + /// + public MonitorLock(int lockTimeout) + { + _uLockId = Guid.NewGuid(); + _uLockObj = new Object(); + _uLockDepth = 0; + _uLockTimeout = lockTimeout; + } + + /// + /// Gets a value indicating whether this instance is held by current thread. + /// + /// + /// true if this instance is held by current thread; otherwise, false. + /// + public bool IsHeldByCurrentThread + { + get { return _uLockOwner == Thread.CurrentThread; } + } + + /// + /// Acquires a lock against this instance. + /// + public IDisposable Acquire() + { + InternalAcquire(_uLockTimeout); + return new TrackedDisposable(InternalRelease); + } + + public IDisposable Acquire(long msec) + { + InternalAcquire((int) msec); + return new TrackedDisposable(InternalRelease); + } + + /// + /// Acquire the lock; the lock is released when the disposable + /// object that was returned is disposed IF the releaseLock + /// flag is set. + /// + /// + /// + /// + public IDisposable Acquire(bool releaseLock, long? msec = null) + { + InternalAcquire((int) (msec ?? _uLockTimeout)); + if (releaseLock) + return new TrackedDisposable(InternalRelease); + return new VoidDisposable(); + } + + public IDisposable ReleaseAcquire() + { + InternalRelease(); + return new TrackedDisposable(() => InternalAcquire(_uLockTimeout)); + } + + /// + /// Releases this instance. + /// + public void Release() + { + InternalRelease(); + } + + /// + /// Internally acquires the lock. + /// + private void InternalAcquire(int lockTimeout) + { + if ((_uLockOwner != null) && (_uLockOwner == Thread.CurrentThread)) { + // This condition is only true when the lock request + // is nested. The first time in, _uLockOwner is 0 + // because it is not owned and forces the caller to acquire + // the spinlock to set the value; the nested call is true, + // but only because its within an already locked scope. + _uLockDepth++; + } + else + { + if (Monitor.TryEnter(_uLockObj, lockTimeout)) + { + _uLockOwner = Thread.CurrentThread; + _uLockDepth = 1; + } + else + { + throw new ApplicationException("Unable to obtain lock before timeout occurred"); + } + } + } + + /// + /// Internally releases the lock. + /// + private void InternalRelease() + { + if (_uLockOwner != Thread.CurrentThread) { + return; + } + + // Only called when you hold the lock + --_uLockDepth; + + if (_uLockDepth == 0) + { + _uLockOwner = null; + Monitor.Exit(_uLockObj); + } + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override string ToString() + { + return "MonitorLock{" + + "_uLockId=" + _uLockId + + ";_uLockOwner=" + _uLockOwner + + "}"; + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/threading/MonitorSlimLock.cs b/NEsper.Core/NEsper.Core/compat/threading/MonitorSlimLock.cs new file mode 100755 index 000000000..c6fd663ad --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/threading/MonitorSlimLock.cs @@ -0,0 +1,207 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Threading; + +namespace com.espertech.esper.compat.threading +{ + public class MonitorSlimLock : ILockable + { + /// + /// Gets the number of milliseconds until the lock acquisition fails. + /// + /// The lock timeout. + + public int LockTimeout + { + get { return _uLockTimeout; } + } + + /// + /// Uniquely identifies the lock. + /// + + private readonly Guid _uLockId; + + /// + /// Underlying object that is locked + /// + + private readonly SlimLock _uLockObj; + + /// + /// Number of milliseconds until the lock acquisition fails + /// + + private readonly int _uLockTimeout; + + /// + /// Owner of the lock. + /// + + private Thread _uLockOwner; + + /// + /// Used to track recursive locks. + /// + + private int _uLockDepth; + + /// + /// Gets the lock depth. + /// + /// The lock depth. + + public int LockDepth + { + get { return _uLockDepth; } + } + + /// + /// Initializes a new instance of the class. + /// + public MonitorSlimLock() + : this( BaseLock.MLockTimeout ) + { + } + + /// + /// Initializes a new instance of the class. + /// + public MonitorSlimLock(int lockTimeout) + { + _uLockId = Guid.NewGuid(); + _uLockObj = new SlimLock(); + _uLockDepth = 0; + _uLockTimeout = lockTimeout; + } + + /// + /// Gets a value indicating whether this instance is held by current thread. + /// + /// + /// true if this instance is held by current thread; otherwise, false. + /// + public bool IsHeldByCurrentThread + { + get { return _uLockOwner == Thread.CurrentThread; } + } + + /// + /// Acquires a lock against this instance. + /// + public IDisposable Acquire() + { + InternalAcquire(_uLockTimeout); + return new TrackedDisposable(InternalRelease); + } + + public IDisposable Acquire(long msec) + { + InternalAcquire((int) msec); + return new TrackedDisposable(InternalRelease); + } + + /// + /// Acquire the lock; the lock is released when the disposable + /// object that was returned is disposed IF the releaseLock + /// flag is set. + /// + /// + /// + /// + public IDisposable Acquire(bool releaseLock, long? msec = null) + { + InternalAcquire((int)(msec ?? _uLockTimeout)); + if (releaseLock) + return new TrackedDisposable(InternalRelease); + return new VoidDisposable(); + } + + /// + /// Provides a temporary release of the lock if it is acquired. When the + /// disposable object that is returned is disposed, the lock is re-acquired. + /// This method is effectively the opposite of acquire. + /// + /// + public IDisposable ReleaseAcquire() + { + InternalRelease(); + return new TrackedDisposable(() => InternalAcquire(_uLockTimeout)); + } + + /// + /// Releases this instance. + /// + public void Release() + { + InternalRelease(); + } + + /// + /// Internally acquires the lock. + /// + private void InternalAcquire(int lockTimeout) + { + if ((_uLockOwner != null) && (_uLockOwner == Thread.CurrentThread)) + { + // This condition is only true when the lock request + // is nested. The first time in, _uLockOwner is 0 + // because it is not owned and forces the caller to acquire + // the spinlock to set the value; the nested call is true, + // but only because its within an already locked scope. + _uLockDepth++; + } + else + { + if (_uLockObj.Enter(lockTimeout)) + { + _uLockOwner = Thread.CurrentThread; + _uLockDepth = 1; + } + else { + throw new TimeoutException("Unable to obtain lock before timeout occurred"); + } + } + } + + /// + /// Internally releases the lock. + /// + private void InternalRelease() + { + if (_uLockOwner != Thread.CurrentThread) { + return; + } + + // Only called when you hold the lock + --_uLockDepth; + + if (_uLockDepth == 0) + { + _uLockOwner = null; + _uLockObj.Release(); + } + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override string ToString() + { + return "MonitorLock{" + + "_uLockId=" + _uLockId + + ";_uLockOwner=" + _uLockOwner + + "}"; + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/threading/MonitorSpinLock.cs b/NEsper.Core/NEsper.Core/compat/threading/MonitorSpinLock.cs new file mode 100755 index 000000000..18f333fbb --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/threading/MonitorSpinLock.cs @@ -0,0 +1,199 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Threading; + +namespace com.espertech.esper.compat.threading +{ + public class MonitorSpinLock : ILockable + { + /// + /// Gets the number of milliseconds until the lock acquisition fails. + /// + /// The lock timeout. + + public int LockTimeout + { + get { return _uLockTimeout; } + } + + /// + /// Uniquely identifies the lock. + /// + + private readonly Guid _uLockId; + + /// + /// Underlying object that is locked + /// + + private SpinLock _uLockObj; + + /// + /// Number of milliseconds until the lock acquisition fails + /// + + private readonly int _uLockTimeout; + + /// + /// Used to track recursive locks. + /// + + private int _uLockDepth; + + /// + /// Gets the lock depth. + /// + /// The lock depth. + + public int LockDepth + { + get { return _uLockDepth; } + } + + /// + /// Initializes a new instance of the class. + /// + public MonitorSpinLock() + : this( BaseLock.MLockTimeout ) + { + } + + /// + /// Initializes a new instance of the class. + /// + public MonitorSpinLock(int lockTimeout) + { + _uLockId = Guid.NewGuid(); + _uLockObj = new SpinLock(); + _uLockDepth = 0; + _uLockTimeout = lockTimeout; + } + + /// + /// Gets a value indicating whether this instance is held by current thread. + /// + /// + /// true if this instance is held by current thread; otherwise, false. + /// + public bool IsHeldByCurrentThread + { + get { return _uLockObj.IsHeldByCurrentThread; } + } + + /// + /// Acquires a lock against this instance. + /// + public IDisposable Acquire() + { + InternalAcquire(_uLockTimeout); + return new TrackedDisposable(InternalRelease); + } + + public IDisposable Acquire(long msec) + { + InternalAcquire((int) msec); + return new TrackedDisposable(InternalRelease); + } + + /// + /// Acquire the lock; the lock is released when the disposable + /// object that was returned is disposed IF the releaseLock + /// flag is set. + /// + /// + /// + /// + public IDisposable Acquire(bool releaseLock, long? msec = null) + { + InternalAcquire((int) (msec ?? _uLockTimeout)); + if (releaseLock) + return new TrackedDisposable(InternalRelease); + return new VoidDisposable(); + } + + /// + /// Provides a temporary release of the lock if it is acquired. When the + /// disposable object that is returned is disposed, the lock is re-acquired. + /// This method is effectively the opposite of acquire. + /// + /// + public IDisposable ReleaseAcquire() + { + InternalRelease(); + return new TrackedDisposable(() => InternalAcquire(_uLockTimeout)); + } + + /// + /// Releases this instance. + /// + public void Release() + { + InternalRelease(); + } + + /// + /// Internally acquires the lock. + /// + private void InternalAcquire(int lockTimeout) + { + if (IsHeldByCurrentThread) + { + // This condition is only true when the lock request + // is nested. The first time in, _uLockOwner is 0 + // because it is not owned and forces the caller to acquire + // the spinlock to set the value; the nested call is true, + // but only because its within an already locked scope. + _uLockDepth++; + } + else + { + bool lockTaken = false; + + _uLockObj.TryEnter(lockTimeout, ref lockTaken); + if (lockTaken) + { + _uLockDepth = 1; + } + else + { + throw new TimeoutException("Unable to obtain lock before timeout occurred"); + } + } + } + + /// + /// Internally releases the lock. + /// + private void InternalRelease() + { + if (IsHeldByCurrentThread) + { + // Only called when you hold the lock + --_uLockDepth; + + if (_uLockDepth == 0) + { + _uLockObj.Exit(); + } + } + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override string ToString() + { + return string.Format("MonitorLock{{" + "_uLockId={0}}}", _uLockId); + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/threading/ReaderWriterLockManager.cs b/NEsper.Core/NEsper.Core/compat/threading/ReaderWriterLockManager.cs new file mode 100755 index 000000000..bb25292fa --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/threading/ReaderWriterLockManager.cs @@ -0,0 +1,254 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.compat.threading +{ + public class ReaderWriterLockManager + { + private static readonly Object CategoryFactoryTableLock = new object(); + + private static readonly IDictionary> CategoryFactoryTable = + new Dictionary>(); + + /// + /// Engine that captures telemetry data. + /// + public static readonly TelemetryEngine TelemetryEngine = + new TelemetryEngine(); + + /// + /// Gets or sets a value indicating whether lock telemetry is enabled. + /// + /// + /// true if this instance is telemetry enabled; otherwise, false. + /// + public static bool IsTelemetryEnabled { get; set; } + + /// + /// Gets or sets the default lock factory. + /// + /// The default lock factory. + public static Func DefaultLockFactory { get; set; } + + /// + /// Initializes the class. + /// + static ReaderWriterLockManager() + { + DefaultLockFactory = StandardLock; // NEW DEFAULT READER-WRITER LOCK + IsTelemetryEnabled = false; + + // Establishes the default locking style + var defaultLockTypeName = CompatSettings.Default.DefaultReaderWriterLockType; + if (String.IsNullOrEmpty(defaultLockTypeName)) { + return; + } + + switch( defaultLockTypeName.ToUpper() ) { + case "STD": + case "STANDARD": + DefaultLockFactory = StandardLock; + break; + case "SLIM": + DefaultLockFactory = SlimLock; + break; + case "FAIR": + DefaultLockFactory = FairLock; + break; + case "VOID": + DefaultLockFactory = VoidLock; + break; + default: + throw new ArgumentException("unknown lock type '" + defaultLockTypeName + "'"); + } + } + + /// + /// Registers the category lock. + /// + /// The lock factory. + public static void RegisterCategoryLock(Func lockFactory) + { + RegisterCategoryLock(typeof(T).FullName, lockFactory); + } + + /// + /// Registers the category lock. + /// + /// The type category. + /// The lock factory. + public static void RegisterCategoryLock(Type typeCategory, Func lockFactory) + { + RegisterCategoryLock(typeCategory.FullName, lockFactory); + } + + /// + /// Registers the category lock. + /// + /// The category. + /// The lock factory. + public static void RegisterCategoryLock(string category, Func lockFactory) + { + lock( CategoryFactoryTableLock ) { + CategoryFactoryTable[category] = lockFactory; + } + } + + /// + /// Creates a lock for the category defined by the type. + /// + /// The type category. + /// + public static IReaderWriterLock CreateLock(Type typeCategory) + { + var typeName = typeCategory.FullName; + if (typeName != null) { + var typeNameIndex = typeName.IndexOf('`'); + if (typeNameIndex != -1) { + typeName = typeName.Substring(0, typeNameIndex); + } + } + + return CreateLock(typeName); + } + + /// + /// Wraps the lock. + /// + /// The reader writer lock. + /// The category. + /// + private static IReaderWriterLock WrapLock(IReaderWriterLock readerWriterLock, string category) + { + if (IsTelemetryEnabled) { + var rLockCategory = TelemetryEngine.GetCategory(string.Format("{0}.Read", category)); + var wLockCategory = TelemetryEngine.GetCategory(string.Format("{0}.Write", category)); + var lockObject = new TelemetryReaderWriterLock(readerWriterLock); + lockObject.ReadLockReleased += rLockCategory.OnLockReleased; + lockObject.WriteLockReleased += wLockCategory.OnLockReleased; + return lockObject; + } + + return readerWriterLock; + } + + /// + /// Creates a lock for the category. + /// + /// The category. + /// + public static IReaderWriterLock CreateLock(string category) + { + var trueCategory = category; + + if (category != null) { + lock (CategoryFactoryTableLock) { + trueCategory = category = category.TrimEnd('.'); + + while( category != String.Empty ) { + Func lockFactory; + // Lookup a factory for the category + if (CategoryFactoryTable.TryGetValue(category, out lockFactory)) { + return WrapLock(lockFactory.Invoke(), category); + } + // Lock factory not found, back-up one segment of the category + int lastIndex = category.LastIndexOf('.'); + if (lastIndex == -1) { + break; + } + + category = category.Substring(0, lastIndex).TrimEnd('.'); + } + } + } + + return CreateDefaultLock(trueCategory); + } + + /// + /// Creates the default lock. + /// + /// + public static IReaderWriterLock CreateDefaultLock() + { + return CreateDefaultLock(string.Empty); + } + + /// + /// Creates the default lock. + /// + /// The category. + /// + private static IReaderWriterLock CreateDefaultLock(string category) + { + var lockFactory = DefaultLockFactory; + if (lockFactory == null) { + throw new ApplicationException("default lock factory is not set"); + } + + return WrapLock(lockFactory.Invoke(), category); + } + + /// + /// Creates a singularity lock. + /// + /// + public static IReaderWriterLock SingularityLock() + { + return new DummyReaderWriterLock(); + } + + /// + /// Creates the standard reader writer lock. + /// + /// + public static IReaderWriterLock StandardLock() + { + return new StandardReaderWriterLock(); + } + + /// + /// Creates the slim reader writer lock. + /// + /// + public static IReaderWriterLock SlimLock() + { + return new SlimReaderWriterLock(); + } + + /// + /// Creates the void reader writer lock. + /// + /// + public static IReaderWriterLock VoidLock() + { + return new VoidReaderWriterLock(); + } + + /// + /// Creates the fair reader writer lock. + /// + /// + public static IReaderWriterLock FairLock() + { + return new FairReaderWriterLock(); + } + + /// + /// Creates the fifo reader writer lock. + /// + /// + public static IReaderWriterLock FifoLock() + { + return new FifoReaderWriterLock(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/threading/SlimLock.cs b/NEsper.Core/NEsper.Core/compat/threading/SlimLock.cs new file mode 100755 index 000000000..558966f4c --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/threading/SlimLock.cs @@ -0,0 +1,236 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Runtime.InteropServices; +using System.Threading; + +namespace com.espertech.esper.compat.threading +{ + /// + /// : a simple spinLock algorithm. The spinLock will attempt + /// to exchange a value atomically. If the exchange can not be done then + /// the spinLock will enter a loop for a maximum amount of time as + /// specified. In the loop it will use a spinWait to allow the CPU to + /// idle for a few cycles in an attempt to wait for the resource to be + /// freed up. If after a number of attempts the resource has not been + /// freed, the spinLock will give up its quanta using a sleep. The sleep + /// will force the thread to yield and if all goes well releases the thread + /// (which may be on the same processor) to release the critical resource. + /// There's no reason to use this as a general purpose lock, monitors do + /// just fine. + /// + + public sealed class SlimLock + { + private int _myLockDepth; + private Thread _myLockThread; + + /// + /// Acquires the lock. If the lock can be acquired immediately + /// it does so. In the event that the lock can not be acquired + /// the lock will use a spin-lock algorithm to acquire the lock. + /// + + public bool Enter(int timeoutInMillis) + { + Thread thread = Thread.CurrentThread; + + if (_myLockThread == thread) + { + _myLockDepth++; + return true; + } + + if (Interlocked.CompareExchange(ref _myLockThread, thread, null) == null) + { + _myLockDepth = 1; + return true; + } + + return EnterMyLockSpin(thread, timeoutInMillis); + } + + /// + /// Acquires the lock. If the lock can be acquired immediately + /// it does so. In the event that the lock can not be acquired + /// the lock will use a spin-lock algorithm to acquire the lock. + /// + + public void Enter() + { + Thread thread = Thread.CurrentThread; + if (_myLockThread == thread) + { + _myLockDepth++; + return; + } + + if (Interlocked.CompareExchange(ref _myLockThread, thread, null) == null) + { + _myLockDepth = 1; + return; + } + + EnterMyLockSpin(thread); + } + + private void EnterMyLockSpin(Thread thread) + { + bool isMultiProcessor = IsMultiProcessor; + + for (int i = 0; ; i++) + { + if (i < 3 && isMultiProcessor) + { + Thread.SpinWait(20); // Wait a few dozen instructions to let another processor release lock. + } + else + { + Thread.Sleep(0); // Give up my quantum. + } + + if (Interlocked.CompareExchange(ref _myLockThread, thread, null) == null) + { + _myLockDepth = 1; + return; + } + } + } + + [DllImport("Kernel32.dll")] + private static extern bool QueryPerformanceCounter(out long lpPerformanceCount); + + [DllImport("Kernel32.dll")] + private static extern bool QueryPerformanceFrequency(out long lpFrequency); + + /// + /// Enters the lock spin with a timeout. Returns true if the + /// lock was acquired within the time allotted. + /// + /// The thread. + /// The timeout in millis. + /// + + private bool EnterMyLockSpin(Thread thread, int timeoutInMillis) + { + var isMultiProcessor = IsMultiProcessor; + + long time; + + QueryPerformanceCounter(out time); + + var factor = PerformanceObserverWin.MpMilli; + var begTime = time; + var endTime = begTime + timeoutInMillis / factor; + + if (isMultiProcessor) + { + if (EnterMyLockSpinWait(thread)) return true; + if (EnterMyLockSpinWait(thread)) return true; + if (EnterMyLockSpinWait(thread)) return true; + + QueryPerformanceCounter(out time); + if (time > endTime) return false; + } + + if (EnterMyLockSleep(thread, 0)) return true; + if (EnterMyLockSleep(thread, 0)) return true; + if (EnterMyLockSleep(thread, 0)) return true; + + QueryPerformanceCounter(out time); + if (time > endTime) return false; + + while (true) + { + if (EnterMyLockSleep(thread, 10)) return true; + + QueryPerformanceCounter(out time); + if (time > endTime) return false; + } + } + + private bool EnterMyLockSpinWait(Thread thread) + { + Thread.SpinWait(20); + if (Interlocked.CompareExchange(ref _myLockThread, thread, null) == null) + { + _myLockDepth = 1; + return true; + } + + return false; + } + + private bool EnterMyLockSleep(Thread thread, int sleep) + { + Thread.Sleep(sleep); + if (Interlocked.CompareExchange(ref _myLockThread, thread, null) == null) + { + _myLockDepth = 1; + return true; + } + + return false; + } + + /// + /// Releases the lock, allowing waiters to proceed. + /// + + public void Release() + { + if (--_myLockDepth == 0) + { + Interlocked.Exchange(ref _myLockThread, null); + } + } + + public static readonly bool IsMultiProcessor; + + private static readonly int SpinWait0; + private static readonly int SpinWait1; + private static readonly int SpinWait2; + private static readonly int SpinWait3; + + static SlimLock() + { + IsMultiProcessor = Environment.ProcessorCount > 1; + + SpinWait0 = PerformanceObserverWin.SpinIterationsPerMicro * 5; + SpinWait1 = PerformanceObserverWin.SpinIterationsPerMicro * 10; + SpinWait2 = PerformanceObserverWin.SpinIterationsPerMicro * 25; + SpinWait3 = PerformanceObserverWin.SpinIterationsPerMicro * 50; + } + + public static void SmartWait(int iter) + { + if (iter <= 10) Thread.SpinWait(SpinWait0); + else if (iter <= 30) Thread.SpinWait(SpinWait1); + else if (iter <= 60) Thread.SpinWait(SpinWait2); + else if (iter <= 100) Thread.SpinWait(SpinWait3); + else Thread.Sleep(0); + } + + public static bool SmartWait(int iter, long timeEnd) + { + if (iter <= 10) Thread.SpinWait(SpinWait0); // 0.05 ms + else if (iter <= 30) Thread.SpinWait(SpinWait1); // 0.35 ms + else if (iter <= 60) Thread.SpinWait(SpinWait2); // 1.85 ms + else if ((iter % 10) == 0) + { + if (DateTimeHelper.CurrentTimeMillis > timeEnd) + return false; + } + else if (iter <= 100) Thread.SpinWait(SpinWait3); + else Thread.Sleep(0); + + return true; + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/threading/SlimReaderWriterLock.cs b/NEsper.Core/NEsper.Core/compat/threading/SlimReaderWriterLock.cs new file mode 100755 index 000000000..f18286401 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/threading/SlimReaderWriterLock.cs @@ -0,0 +1,157 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Threading; + +namespace com.espertech.esper.compat.threading +{ + public sealed class SlimReaderWriterLock + : IReaderWriterLock + , IReaderWriterLockCommon + { +#if MONO + public const string ExceptionText = "ReaderWriterLockSlim is not supported on this platform"; +#else + private readonly ReaderWriterLockSlim _rwLock; +#endif + + /// + /// Initializes a new instance of the class. + /// + public SlimReaderWriterLock() + { +#if MONO + throw new NotSupportedException(ExceptionText); +#else + _rwLock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); + ReadLock = new CommonReadLock(this); + WriteLock = new CommonWriteLock(this); + + _rDisposable = new TrackedDisposable(ReleaseReaderLock); + _wDisposable = new TrackedDisposable(ReleaseWriterLock); +#endif + } + + /// + /// Gets the read-side lockable + /// + /// + public ILockable ReadLock { get ; private set; } + + /// + /// Gets the write-side lockable + /// + /// + public ILockable WriteLock { get; private set; } + + private readonly IDisposable _rDisposable; + private readonly IDisposable _wDisposable; + + public IDisposable AcquireReadLock() + { +#if MONO + throw new NotSupportedException(ExceptionText); +#else + if (_rwLock.TryEnterReadLock(BaseLock.RLockTimeout)) + return _rDisposable; + + throw new TimeoutException("ReaderWriterLock timeout expired"); +#endif + } + + public IDisposable AcquireWriteLock() + { +#if MONO + throw new NotSupportedException(ExceptionText); +#else + if (_rwLock.TryEnterWriteLock(BaseLock.WLockTimeout)) + return _wDisposable; + + throw new TimeoutException("ReaderWriterLock timeout expired"); +#endif + } + + /// + /// Indicates if the writer lock is held. + /// + /// + /// The is writer lock held. + /// + public bool IsWriterLockHeld + { + get { return _rwLock.IsWriteLockHeld; } + } + +#if DEBUG + /// + /// Gets or sets a value indicating whether this is TRACE. + /// + /// true if TRACE; otherwise, false. + public bool Trace { get; set; } +#endif + + /// + /// Acquires the reader lock. + /// + /// The timeout. + public void AcquireReaderLock(long timeout) + { +#if MONO + throw new NotSupportedException(ExceptionText); +#else + if (_rwLock.TryEnterReadLock((int) timeout)) + return; + + throw new TimeoutException("ReaderWriterLock timeout expired"); +#endif + } + + /// + /// Acquires the writer lock. + /// + /// The timeout. + public void AcquireWriterLock(long timeout) + { +#if MONO + throw new NotSupportedException(ExceptionText); +#else + if (_rwLock.TryEnterWriteLock((int) timeout)) + return; + + throw new TimeoutException("ReaderWriterLock timeout expired"); +#endif + } + + /// + /// Releases the reader lock. + /// + public void ReleaseReaderLock() + { +#if MONO + throw new NotSupportedException(ExceptionText); +#else + _rwLock.ExitReadLock(); +#endif + } + + /// + /// Releases the writer lock. + /// + public void ReleaseWriterLock() + { +#if MONO + throw new NotSupportedException(ExceptionText); +#else + _rwLock.ExitWriteLock(); +#endif + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/threading/SlimThreadLocal.cs b/NEsper.Core/NEsper.Core/compat/threading/SlimThreadLocal.cs new file mode 100755 index 000000000..85e20ab54 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/threading/SlimThreadLocal.cs @@ -0,0 +1,129 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System.Collections.Generic; +using System.Threading; + +namespace com.espertech.esper.compat.threading +{ + public sealed class SlimThreadLocal : IThreadLocal + where T : class + { + private IDictionary _threadTable; + private readonly SlimLock _wLock; + private readonly FactoryDelegate _valueFactory; + + /// + /// Gets or sets the value. + /// + /// The value. + public T Value + { + get + { + T value; + _threadTable.TryGetValue(Thread.CurrentThread, out value); + return value; + } + + set + { + _wLock.Enter(); + try + { + var tempTable = new Dictionary(_threadTable); + tempTable[Thread.CurrentThread] = value; + _threadTable = tempTable; + } + finally + { + _wLock.Release(); + } + } + } + + /// + /// Gets the data or creates it if not found. + /// + /// + public T GetOrCreate() + { + T value; + if (_threadTable.TryGetValue(Thread.CurrentThread, out value)) + { + return value; + } + + _wLock.Enter(); + try + { + var tempTable = new Dictionary(_threadTable); + tempTable[Thread.CurrentThread] = value = _valueFactory.Invoke(); + _threadTable = tempTable; + return value; + } + finally + { + _wLock.Release(); + } + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The factory. + public SlimThreadLocal(FactoryDelegate factory) + { + _threadTable = new Dictionary(new ThreadEq()); + _valueFactory = factory; + _wLock = new SlimLock(); + } + + internal class ThreadEq : IEqualityComparer + { + public bool Equals(Thread x, Thread y) + { + return x == y; + } + + public int GetHashCode(Thread obj) + { + return obj.ManagedThreadId; + } + } + } + + /// + /// Creates slim thread local objects. + /// + public class SlimThreadLocalFactory : ThreadLocalFactory + { + #region ThreadLocalFactory Members + + /// + /// Create a thread local object of the specified type param. + /// + /// + /// + /// + public IThreadLocal CreateThreadLocal(FactoryDelegate factory) where T : class + { + return new SlimThreadLocal(factory); + } + + #endregion + } +} diff --git a/NEsper.Core/NEsper.Core/compat/threading/StandardReaderWriterLock.cs b/NEsper.Core/NEsper.Core/compat/threading/StandardReaderWriterLock.cs new file mode 100755 index 000000000..d30e9a641 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/threading/StandardReaderWriterLock.cs @@ -0,0 +1,113 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Threading; + +namespace com.espertech.esper.compat.threading +{ + public sealed class StandardReaderWriterLock + : IReaderWriterLock + , IReaderWriterLockCommon + { + private readonly ReaderWriterLock _rwLock; + + /// + /// Initializes a new instance of the class. + /// + public StandardReaderWriterLock() + { + _rwLock = new ReaderWriterLock(); + ReadLock = new CommonReadLock(this); + WriteLock = new CommonWriteLock(this); + } + + /// + /// Gets the read-side lockable + /// + /// + public ILockable ReadLock { get ; private set; } + + /// + /// Gets the write-side lockable + /// + /// + public ILockable WriteLock { get; private set; } + + public IDisposable AcquireReadLock() + { + return ReadLock.Acquire(); + } + + public IDisposable AcquireWriteLock() + { + return WriteLock.Acquire(); + } + +#if DEBUG + /// + /// Gets or sets a value indicating whether this is TRACE. + /// + /// true if TRACE; otherwise, false. + public bool Trace { get; set; } +#endif + + public bool IsWriterLockHeld + { + get { return _rwLock.IsWriterLockHeld; } + } + + /// + /// Acquires the reader lock. + /// + /// The timeout. + public void AcquireReaderLock(long timeout) + { + try + { + _rwLock.AcquireReaderLock((int) timeout); + } + catch(ApplicationException) + { + throw new TimeoutException("ReaderWriterLock timeout expired"); + } + } + + /// + /// Acquires the writer lock. + /// + /// The timeout. + public void AcquireWriterLock(long timeout) + { + try + { + _rwLock.AcquireWriterLock((int) timeout); + } + catch(ApplicationException) + { + throw new TimeoutException("ReaderWriterLock timeout expired"); + } + } + + /// + /// Releases the reader lock. + /// + public void ReleaseReaderLock() + { + _rwLock.ReleaseReaderLock(); + } + + /// + /// Releases the writer lock. + /// + public void ReleaseWriterLock() + { + _rwLock.ReleaseWriterLock(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/threading/SystemThreadLocal.cs b/NEsper.Core/NEsper.Core/compat/threading/SystemThreadLocal.cs new file mode 100755 index 000000000..702b92aa5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/threading/SystemThreadLocal.cs @@ -0,0 +1,100 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Threading; + +namespace com.espertech.esper.compat.threading +{ + /// + /// IThreadLocal implementation that uses the native support + /// in the CLR (i.e. the LocalDataStoreSlot). + /// + /// + public class SystemThreadLocal : IThreadLocal + where T : class + { + /// + /// Local data storage slot + /// + private readonly LocalDataStoreSlot m_dataStoreSlot; + + /// + /// Factory delegate for construction of data on miss. + /// + + private readonly FactoryDelegate m_dataFactory; + + /// + /// Gets or sets the value. + /// + /// The value. + public T Value + { + get { return (T) Thread.GetData(m_dataStoreSlot); } + set { Thread.SetData(m_dataStoreSlot, value); } + } + + /// + /// Gets the data or creates it if not found. + /// + /// + public T GetOrCreate() + { + T value; + + value = (T)Thread.GetData(m_dataStoreSlot); + if ( value == null ) + { + value = m_dataFactory(); + Thread.SetData( m_dataStoreSlot, value ); + } + + return value; + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The factory used to create values when not found. + public SystemThreadLocal( FactoryDelegate factory ) + { + m_dataStoreSlot = Thread.AllocateDataSlot(); + m_dataFactory = factory; + } + } + + /// + /// Creates system thread local objects. + /// + public class SystemThreadLocalFactory : ThreadLocalFactory + { + #region ThreadLocalFactory Members + + /// + /// Create a thread local object of the specified type param. + /// + /// + /// + /// + public IThreadLocal CreateThreadLocal(FactoryDelegate factory) where T : class + { + return new SystemThreadLocal(factory); + } + + #endregion + } +} diff --git a/NEsper.Core/NEsper.Core/compat/threading/TelemetryEngine.cs b/NEsper.Core/NEsper.Core/compat/threading/TelemetryEngine.cs new file mode 100755 index 000000000..98e6c718a --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/threading/TelemetryEngine.cs @@ -0,0 +1,112 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.IO; +using System.Xml; + +namespace com.espertech.esper.compat.threading +{ + public class TelemetryEngine + { + private readonly IDictionary _categoryDictionary = + new Dictionary(); + + /// + /// Gets the categories. + /// + /// The categories. + public IEnumerable Categories + { + get { return _categoryDictionary.Values; } + } + + /// + /// Gets the category dictionary. + /// + /// The category dictionary. + public IDictionary CategoryDictionary + { + get { return _categoryDictionary; } + } + + /// + /// Gets the category. + /// + /// The name. + /// + public TelemetryLockCategory GetCategory(string name) + { + TelemetryLockCategory lockCategory; + if (!_categoryDictionary.TryGetValue(name, out lockCategory)) { + lockCategory = new TelemetryLockCategory(name); + _categoryDictionary[name] = lockCategory; + } + + return lockCategory; + } + + /// + /// Dumps telemetry information to a textWriter. + /// + /// The text writer. + public void DumpTo(TextWriter textWriter) + { + var xmlWriterSettings = new XmlWriterSettings { Indent = true, OmitXmlDeclaration = true }; + var xmlWriter = XmlWriter.Create(textWriter, xmlWriterSettings); + + xmlWriter.WriteStartDocument(true); + xmlWriter.WriteStartElement("telemetry"); + + // find all events that belong together - blending category if + // necessary + + foreach (var category in _categoryDictionary.Values) + { + xmlWriter.WriteStartElement("category"); + xmlWriter.WriteAttributeString("name", category.Name); + + foreach (var tEvent in category.Events) { + long tta = tEvent.AcquireTime - tEvent.RequestTime; + long ttp = tEvent.ReleaseTime - tEvent.AcquireTime; + + xmlWriter.WriteStartElement("event"); + xmlWriter.WriteAttributeString("id", tEvent.Id); + xmlWriter.WriteAttributeString("request", tEvent.RequestTime.ToString()); + xmlWriter.WriteAttributeString("acquire", tEvent.AcquireTime.ToString()); + xmlWriter.WriteAttributeString("release", tEvent.ReleaseTime.ToString()); + xmlWriter.WriteAttributeString("tta", tta.ToString()); + xmlWriter.WriteAttributeString("ttp", ttp.ToString()); + xmlWriter.WriteStartElement("stack"); + xmlWriter.WriteString(tEvent.StackTrace.ToString()); + xmlWriter.WriteEndElement(); + xmlWriter.WriteEndElement(); + } + + xmlWriter.WriteEndElement(); + } + + xmlWriter.WriteEndElement(); + xmlWriter.WriteEndDocument(); + xmlWriter.Flush(); + } + + /// + /// Dumps telemetry information to file. + /// + /// The filename. + public void DumpToFile(string filename) + { + using(var writer = File.CreateText(filename)) { + DumpTo(writer); + writer.Flush(); + writer.Close(); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/threading/TelemetryEventArgs.cs b/NEsper.Core/NEsper.Core/compat/threading/TelemetryEventArgs.cs new file mode 100755 index 000000000..4b423eb97 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/threading/TelemetryEventArgs.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Diagnostics; + +namespace com.espertech.esper.compat.threading +{ + public class TelemetryEventArgs : EventArgs + { + /// + /// Unique lock identifier. + /// + public string Id; + /// + /// TimeInMillis lock was requested. + /// + public long RequestTime; + /// + /// TimeInMillis lock was acquired. + /// + public long AcquireTime; + /// + /// TimeInMillis lock was released. + /// + public long ReleaseTime; + /// + /// Stack TRACE associated with lock. + /// + public StackTrace StackTrace; + } +} diff --git a/NEsper.Core/NEsper.Core/compat/threading/TelemetryLock.cs b/NEsper.Core/NEsper.Core/compat/threading/TelemetryLock.cs new file mode 100755 index 000000000..995d2eecc --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/threading/TelemetryLock.cs @@ -0,0 +1,163 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Diagnostics; + +namespace com.espertech.esper.compat.threading +{ + public class TelemetryLock : ILockable + { + /// + /// Unique identifier for the lock. + /// + private readonly String _id; + + /// + /// Lock that holds the real lock implementation. + /// + private readonly ILockable _subLock; + + /// + /// Occurs when the lock is released. + /// + public event EventHandler LockReleased; + + /// + /// Raises the event. + /// + /// The instance containing the event data. + protected void OnLockReleased(TelemetryEventArgs e) + { + if ( LockReleased != null ) { + LockReleased(this, e); + } + } + + /// + /// Finishes tracking performance of a call sequence. + /// + /// The time the lock was requested. + /// The time the lock was acquired. + private void FinishTrackingPerformance(long timeLockRequest, long timeLockAcquire) + { + var eventArgs = new TelemetryEventArgs(); + eventArgs.Id = _id; + eventArgs.RequestTime = timeLockRequest; + eventArgs.AcquireTime = timeLockAcquire; + eventArgs.ReleaseTime = PerformanceObserver.MicroTime; + eventArgs.StackTrace = new StackTrace(); + + OnLockReleased(eventArgs); + } + + /// + /// Acquires the lock; the lock is released when the disposable + /// object that was returned is disposed. + /// + /// + public IDisposable Acquire() + { + var timeLockRequested = PerformanceObserver.MicroTime; + var disposableLock = _subLock.Acquire(); + var timeLockAcquired = PerformanceObserver.MicroTime; + var disposableTrack = new TrackedDisposable( + delegate + { + disposableLock.Dispose(); + disposableLock = null; + FinishTrackingPerformance(timeLockRequested, timeLockAcquired); + }); + + return disposableTrack; + } + + public IDisposable Acquire(long msec) + { + var timeLockRequested = PerformanceObserver.MicroTime; + var disposableLock = _subLock.Acquire(msec); + var timeLockAcquired = PerformanceObserver.MicroTime; + var disposableTrack = new TrackedDisposable( + delegate + { + disposableLock.Dispose(); + disposableLock = null; + FinishTrackingPerformance(timeLockRequested, timeLockAcquired); + }); + + return disposableTrack; + } + + public IDisposable Acquire(bool releaseLock, long? msec = null) + { + var timeLockRequested = PerformanceObserver.MicroTime; + var disposableLock = _subLock.Acquire(releaseLock, msec: msec); + var timeLockAcquired = PerformanceObserver.MicroTime; + var disposableTrack = new TrackedDisposable( + delegate + { + disposableLock.Dispose(); + disposableLock = null; + FinishTrackingPerformance(timeLockRequested, timeLockAcquired); + }); + + return disposableTrack; + } + + /// + /// Provides a temporary release of the lock if it is acquired. When the + /// disposable object that is returned is disposed, the lock is re-acquired. + /// This method is effectively the opposite of acquire. + /// + /// + public IDisposable ReleaseAcquire() + { + var timeLockRequested = PerformanceObserver.MicroTime; + var disposableLock = _subLock.ReleaseAcquire(); + var timeLockAcquired = PerformanceObserver.MicroTime; + var disposableTrack = new TrackedDisposable( + delegate + { + disposableLock.Dispose(); + disposableLock = null; + FinishTrackingPerformance(timeLockRequested, timeLockAcquired); + }); + + return disposableTrack; + } + + /// + /// Releases this instance. + /// + public void Release() + { + _subLock.Release(); + } + + /// + /// Initializes a new instance of the class. + /// + /// The id. + /// The sub lock. + public TelemetryLock(string id, ILockable subLock) + { + _id = id; + _subLock = subLock; + } + + /// + /// Initializes a new instance of the class. + /// + /// The sub lock. + public TelemetryLock(ILockable subLock) + { + _id = Guid.NewGuid().ToString(); + _subLock = subLock; + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/threading/TelemetryLockCategory.cs b/NEsper.Core/NEsper.Core/compat/threading/TelemetryLockCategory.cs new file mode 100755 index 000000000..5feb76c7f --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/threading/TelemetryLockCategory.cs @@ -0,0 +1,64 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace com.espertech.esper.compat.threading +{ + public class TelemetryLockCategory + { + /// + /// List of telemetry events for this category. + /// + private readonly LinkedList _telemetryEvents = + new LinkedList(); + + /// + /// Gets the telemetry events. + /// + /// The events. + public ICollection Events + { + get + { + lock(this) { + return _telemetryEvents.ToArray(); + } + } + } + + /// + /// Gets or sets the name. + /// + /// The name. + public string Name { get; set; } + + /// + /// Called when a lock is released. + /// + /// The sender. + /// The instance containing the event data. + public void OnLockReleased(Object sender, TelemetryEventArgs e) + { + lock (this) { + _telemetryEvents.AddLast(e); + } + } + + /// + /// Initializes a new instance of the class. + /// + /// The name. + public TelemetryLockCategory(string name) + { + Name = name; + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/threading/TelemetryProbe.cs b/NEsper.Core/NEsper.Core/compat/threading/TelemetryProbe.cs new file mode 100755 index 000000000..39e5cd638 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/threading/TelemetryProbe.cs @@ -0,0 +1,14 @@ +using System; + +namespace com.espertech.esper.compat.threading +{ + public class TelemetryProbe : IDisposable + { + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/threading/TelemetryReaderWriterLock.cs b/NEsper.Core/NEsper.Core/compat/threading/TelemetryReaderWriterLock.cs new file mode 100755 index 000000000..05143c2d1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/threading/TelemetryReaderWriterLock.cs @@ -0,0 +1,121 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.compat.threading +{ + public class TelemetryReaderWriterLock : IReaderWriterLock + { + /// + /// Common identifier for the reader-writer + /// + private readonly String _id; + + /// + /// Lock that holds the real lock implementation. + /// + private readonly IReaderWriterLock _subLock; + + /// + /// Occurs when the lock is released. + /// + public event EventHandler ReadLockReleased; + + /// + /// Occurs when the lock is released. + /// + public event EventHandler WriteLockReleased; + + /// + /// Raises the event. + /// + /// The instance containing the event data. + protected void OnReadLockReleased(TelemetryEventArgs e) + { + if (ReadLockReleased != null) + { + ReadLockReleased(this, e); + } + } + + /// + /// Raises the event. + /// + /// The instance containing the event data. + protected void OnWriteLockReleased(TelemetryEventArgs e) + { + if (WriteLockReleased != null) + { + WriteLockReleased(this, e); + } + } + + /// + /// Gets the read-side lockable + /// + /// + public ILockable ReadLock { get; set; } + + /// + /// Gets the write-side lockable + /// + /// + public ILockable WriteLock { get; set; } + + public IDisposable AcquireReadLock() + { + return ReadLock.Acquire(); + } + + public IDisposable AcquireWriteLock() + { + return WriteLock.Acquire(); + } + + /// + /// Indicates if the writer lock is held. + /// + /// + /// The is writer lock held. + /// + public bool IsWriterLockHeld { + get { return _subLock.IsWriterLockHeld; } + } + +#if DEBUG + /// + /// Gets or sets a value indicating whether this is TRACE. + /// + /// true if TRACE; otherwise, false. + public bool Trace { get; set; } +#endif + + /// + /// Initializes a new instance of the class. + /// + /// The sub lock. + public TelemetryReaderWriterLock(IReaderWriterLock subLock) + { + _id = Guid.NewGuid().ToString(); + _subLock = subLock; + + do { + var telemetryLock = new TelemetryLock(_id, _subLock.ReadLock); + telemetryLock.LockReleased += (sender, e) => OnReadLockReleased(e); + ReadLock = telemetryLock; + } while (false); + + do { + var telemetryLock = new TelemetryLock(_id, _subLock.WriteLock); + telemetryLock.LockReleased += (sender, e) => OnWriteLockReleased(e); + WriteLock = telemetryLock; + } while (false); + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/threading/ThreadFactory.cs b/NEsper.Core/NEsper.Core/compat/threading/ThreadFactory.cs new file mode 100755 index 000000000..c1d196ffc --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/threading/ThreadFactory.cs @@ -0,0 +1,14 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Threading; + +namespace com.espertech.esper.compat.threading +{ + public delegate Thread ThreadFactory( ThreadStart threadStart ); +} diff --git a/NEsper.Core/NEsper.Core/compat/threading/ThreadLocalFactory.cs b/NEsper.Core/NEsper.Core/compat/threading/ThreadLocalFactory.cs new file mode 100755 index 000000000..dd6614211 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/threading/ThreadLocalFactory.cs @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +namespace com.espertech.esper.compat.threading +{ + /// + /// Creator and manufacturer of thread local objects. + /// + public interface ThreadLocalFactory + { + /// + /// Create a thread local object of the specified type param. + /// + /// + /// + /// + IThreadLocal CreateThreadLocal(FactoryDelegate factory) + where T : class; + } +} diff --git a/NEsper.Core/NEsper.Core/compat/threading/ThreadLocalManager.cs b/NEsper.Core/NEsper.Core/compat/threading/ThreadLocalManager.cs new file mode 100755 index 000000000..1883e4988 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/threading/ThreadLocalManager.cs @@ -0,0 +1,73 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.compat.threading +{ + public class ThreadLocalManager + { + internal static ThreadLocalFactory DefaultThreadLocalFactory { get; set; } + + /// + /// Initializes the class. + /// + static ThreadLocalManager() + { + // Establishes the default thread local style + var defaultThreadLocalType = CompatSettings.Default.DefaultThreadLocalType; + if (String.IsNullOrEmpty(defaultThreadLocalType)) { + DefaultThreadLocalFactory = new FastThreadLocalFactory(); + return; + } + + switch( defaultThreadLocalType.ToUpper() ) { + case "FAST": + DefaultThreadLocalFactory = new FastThreadLocalFactory(); + break; + case "SLIM": + DefaultThreadLocalFactory = new SlimThreadLocalFactory(); + break; + case "XPER": + DefaultThreadLocalFactory = new XperThreadLocalFactory(); + break; + case "SYSTEM": + DefaultThreadLocalFactory = new SystemThreadLocalFactory(); + break; + default: + throw new ArgumentException("unknown thread local type '" + defaultThreadLocalType + "'"); + } + } + + /// + /// Creates a thread local instance. + /// + /// + public static IThreadLocal Create(FactoryDelegate factoryDelegate) + where T : class + { + return CreateDefaultThreadLocal(factoryDelegate); + } + + + /// + /// Creates the default thread local. + /// + /// + public static IThreadLocal CreateDefaultThreadLocal(FactoryDelegate factoryDelegate) + where T : class + { + var localFactory = DefaultThreadLocalFactory; + if (localFactory == null) { + throw new ApplicationException("default thread local factory is not set"); + } + + return localFactory.CreateThreadLocal(factoryDelegate); + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/threading/ThreadMetrics.cs b/NEsper.Core/NEsper.Core/compat/threading/ThreadMetrics.cs new file mode 100755 index 000000000..1e13422b5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/threading/ThreadMetrics.cs @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Threading; + +namespace com.espertech.esper.compat.threading +{ + public sealed class ThreadMetrics + { + /// + /// Continual counter that tracks the number of locks that have been + /// acquired on the thread. + /// + + [ThreadStatic] private static long _locksAcquired; + + /// + /// Gets the # of times locks have been acquired by this thread. + /// + /// The locks acquired. + public static long LocksAcquired + { + get { return _locksAcquired; } + } + + /// + /// Increments the # of times locks have been acquired. + /// + /// + public static long Increment() + { + return Interlocked.Increment(ref _locksAcquired); + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/threading/VoidLock.cs b/NEsper.Core/NEsper.Core/compat/threading/VoidLock.cs new file mode 100755 index 000000000..a02902405 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/threading/VoidLock.cs @@ -0,0 +1,64 @@ +using System; + +namespace com.espertech.esper.compat.threading +{ + public class VoidLock : ILockable + { + private static VoidDisposable _singleton = new VoidDisposable(); + + /// + /// Acquires the lock; the lock is released when the disposable + /// object that was returned is disposed. + /// + /// + public IDisposable Acquire() + { + return _singleton; + //return new VoidDisposable(); + } + + /// + /// Acquire the lock; the lock is released when the disposable + /// object that was returned is disposed IF the releaseLock + /// flag is set. + /// + /// + /// + /// + public IDisposable Acquire(bool releaseLock, long? msec = null) + { + return _singleton; + //return new VoidDisposable(); + } + + /// + /// Acquires the specified msec. + /// + /// The msec. + /// + public IDisposable Acquire(long msec) + { + return _singleton; + //return new VoidDisposable(); + } + + /// + /// Provides a temporary release of the lock if it is acquired. When the + /// disposable object that is returned is disposed, the lock is re-acquired. + /// This method is effectively the opposite of acquire. + /// + /// + public IDisposable ReleaseAcquire() + { + return _singleton; + //return new VoidDisposable(); + } + + /// + /// Releases this instance. + /// + public void Release() + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/threading/VoidReaderWriterLock.cs b/NEsper.Core/NEsper.Core/compat/threading/VoidReaderWriterLock.cs new file mode 100755 index 000000000..5cf306acc --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/threading/VoidReaderWriterLock.cs @@ -0,0 +1,65 @@ +using System; + +namespace com.espertech.esper.compat.threading +{ + public class VoidReaderWriterLock : IReaderWriterLock + { + private static readonly VoidLock Instance = new VoidLock(); + private static readonly VoidDisposable Disposable = new VoidDisposable(); + + /// + /// Initializes a new instance of the class. + /// + public VoidReaderWriterLock() + { + } + + #region IReaderWriterLock Members + + /// + /// Gets the read-side lockable + /// + public ILockable ReadLock + { + get { return Instance; } + } + + /// + /// Gets the write-side lockable + /// + public ILockable WriteLock + { + get { return Instance; } + } + + public IDisposable AcquireReadLock() + { + return Disposable; + } + + public IDisposable AcquireWriteLock() + { + return Disposable; + } + + /// + /// Indicates if the writer lock is held. + /// + /// + /// The is writer lock held. + /// + public bool IsWriterLockHeld { + get { return false; } + } + + #if DEBUG + /// + /// Gets or sets a value indicating whether this is TRACE. + /// + /// true if TRACE; otherwise, false. + public bool Trace { get; set; } + #endif + + #endregion + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/compat/threading/XperThreadLocal.cs b/NEsper.Core/NEsper.Core/compat/threading/XperThreadLocal.cs new file mode 100755 index 000000000..6a282153a --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/threading/XperThreadLocal.cs @@ -0,0 +1,294 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Runtime.InteropServices; +using System.Threading; + +namespace com.espertech.esper.compat.threading +{ + public sealed class XperThreadLocal : IThreadLocal + where T : class + { + private readonly SlimLock _wLock; + private readonly FactoryDelegate _valueFactory; + + private int _primeIndex; + + /// + /// NodeTable of nodes ... + /// + private Node[] _nodeTable; + + /// + /// NodeTable that is indexed by hash code and points to the first node + /// in the chain for that hash code. + /// + private int[] _hashIndex; + + /// + /// Indicates the index where the next node needs to be allocated from. + /// + private int _nodeAllocIndex = -1; + + /// + /// Allocates a node for use and return the index of the node. + /// + /// The item. + /// The value. + /// The hash code. + /// + private int AllocNode(int threadId, T value, int hashCode) + { + // Space must be allocated from the existing node table. + int index = Interlocked.Increment(ref _nodeAllocIndex); + if (index == _nodeTable.Length) { + var newTableSize = _nodeTable.Length*2; + var newTable = new Node[newTableSize]; + Array.Copy(_nodeTable, 0, newTable, 0, _nodeTable.Length); + _nodeTable = newTable; + ReIndex(); + } + + _nodeTable[index].SetValues(threadId, value, hashCode); + return index; + } + + /// + /// Reindexes the internal bucket table. + /// + private void ReIndex() + { + int[] newHashIndex; + bool hasCollision; + + do { + // We assume there are no collisions going into the process + hasCollision = false; + // Create a new hash array of prime length + int newHashIndexLength = Prime.HashPrimes[++_primeIndex]; + newHashIndex = new int[newHashIndexLength]; + // Reset the index values + for (int ii = 0; ii < newHashIndexLength; ii++) { + newHashIndex[ii] = -1; + } + + var nodeTable = _nodeTable; + + for (var nodeIndex = 0; nodeIndex < _nodeAllocIndex; nodeIndex++) { + var node = nodeTable[nodeIndex]; + // Modulus the hash code with new table size + var bucket = node.HashCode%newHashIndexLength; + if (newHashIndex[bucket] != -1) { + hasCollision = true; + break; + } + // Attach the node at the head of the bucket chain + newHashIndex[bucket] = nodeIndex; + } + + } while (hasCollision); + + _hashIndex = newHashIndex; + } + + private T SetValue(T value, int thread) + { + // Setting values causes the _hashIndex to become unstable ... we + // should probably avoid chaining if at all possible too so that the + // only element in the hash index is the element itself ... + + var nodeTable = _nodeTable; + var hashIndex = _hashIndex; + + // Look for the node in the current space + var hashCode = thread & 0x7fffffff; + // Get the appropriate node index - remember there are no direct node references + var chainIndex = hashIndex[hashCode % hashIndex.Length]; + + // Skip entries that do not share the same hashcode + if (chainIndex != -1) { + if (nodeTable[chainIndex].HashCode == hashCode) { + if (nodeTable[chainIndex].ThreadId == thread) { + nodeTable[chainIndex].Value = value; + return value; + } + } + } + + _wLock.Enter(); + try { + // Allocate a node + var nodeIndex = AllocNode(thread, value, hashCode); + // MapIndex the node + while (true) { + hashIndex = _hashIndex; + chainIndex = hashCode%hashIndex.Length; + if (hashIndex[chainIndex] == -1) { + hashIndex[chainIndex] = nodeIndex; + return value; + } + + ReIndex(); + } + } finally { + _wLock.Release(); + } + } + + /// + /// Gets or sets the value. + /// + /// The value. + public T Value + { + get + { + var nodeTable = _nodeTable; + var hashIndex = _hashIndex; + var thread = Thread.CurrentThread.ManagedThreadId; + + // Look for the node in the current space + var hashCode = thread & 0x7fffffff; + + // Get the appropriate bucket - this indexes into HashIndex + var hashIndexIndex = hashCode % hashIndex.Length; + // Get the appropriate node index - remember there are no direct node references + var headIndex = hashIndex[hashIndexIndex]; + + if (headIndex != -1) { + // Skip entries that do not share the same hashcode + if (nodeTable[headIndex].HashCode == hashCode) { + // Check for node equality + if (thread == nodeTable[headIndex].ThreadId) { + return nodeTable[headIndex].Value; + } + } + } + + return default(T); + } + + set + { + SetValue(value, Thread.CurrentThread.ManagedThreadId); + } + } + + /// + /// Gets the data or creates it if not found. + /// + /// + public T GetOrCreate() // ~32.26 ns/call + { + var nodeTable = _nodeTable; + var hashIndex = _hashIndex; + var thread = Thread.CurrentThread.ManagedThreadId; + + // Look for the node in the current space + var hashCode = thread & 0x7fffffff; + + // Get the appropriate bucket - this indexes into HashIndex + var hashIndexIndex = hashCode % hashIndex.Length; + // Get the appropriate node index - remember there are no direct node references + var headIndex = hashIndex[hashIndexIndex]; + if (headIndex != -1) { + // Skip entries that do not share the same hashcode + if (nodeTable[headIndex].HashCode == hashCode) { + // Check for node equality + if (nodeTable[headIndex].ThreadId == thread) { + return nodeTable[headIndex].Value; + } + } + } + + return SetValue(_valueFactory.Invoke(), thread); + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The factory. + public XperThreadLocal(FactoryDelegate factory) + { + _primeIndex = 0; + + var tableSize = Prime.HashPrimes[_primeIndex]; + + _hashIndex = new int[tableSize]; + for (int ii = 0; ii < tableSize; ii++) + _hashIndex[ii] = -1; + + _nodeTable = new Node[tableSize]; + + _valueFactory = factory; + _wLock = new SlimLock(); + } + + /// + /// Each node contains the content for the node and references to + /// the next node in it's respective chain and order. + /// + [StructLayout(LayoutKind.Sequential)] + [Serializable] + internal struct Node + { + /// + /// Gets or sets the hash code. + /// + /// The hash code. + internal int HashCode; + + /// + /// Gets or sets the thread id. + /// + internal int ThreadId; + + /// + /// Gets or sets the value. + /// + /// The value. + internal T Value; + + internal void SetValues(int threadId, T value, int hashCode) + { + Value = value; + ThreadId = threadId; + HashCode = hashCode; + } + } + } + + /// + /// Creates slim thread local objects. + /// + public class XperThreadLocalFactory : ThreadLocalFactory + { + #region ThreadLocalFactory Members + + /// + /// Create a thread local object of the specified type param. + /// + /// + /// + /// + public IThreadLocal CreateThreadLocal(FactoryDelegate factory) where T : class + { + return new XperThreadLocal(factory); + } + + #endregion + } +} diff --git a/NEsper.Core/NEsper.Core/compat/timers/HighResolutionTimer.cs b/NEsper.Core/NEsper.Core/compat/timers/HighResolutionTimer.cs new file mode 100755 index 000000000..855cd8da5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/timers/HighResolutionTimer.cs @@ -0,0 +1,277 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Threading; +using com.espertech.esper.compat.logging; + + +namespace com.espertech.esper.compat +{ + /// + /// Windows timers are based on the system timer. The system timer runs at a + /// frequency of about 50-60 hz depending on your machine. This presents a + /// problem for applications that require finer granularity. The HighRes timer + /// allows us to get better granularity, but currently it only works on Windows. + /// + /// Thanks to Luc Pattyn for clarifying some of the issues with high resolution + /// timers with the post on CodeProject. + /// + + public class HighResolutionTimer : ITimer + { + /// + /// Delegate that is called by the windows multimedia timer upon trigger + /// of the timer. + /// + /// + /// + /// + /// + /// + + public delegate void TimerEventHandler(uint id, uint msg, IntPtr userCtx, uint rsv1, uint rsv2); + + internal class NativeMethods + { + [DllImport("WinMM.dll", SetLastError = true)] + internal static extern uint timeSetEvent(uint msDelay, uint msResolution, TimerEventHandler handler, IntPtr userCtx, uint eventType); + + [DllImport("WinMM.dll", SetLastError = true)] + internal static extern uint timeKillEvent(uint timerEventId); + } + + private const int TIME_ONESHOT = 0x0000 ; /* program timer for single event */ + private const int TIME_PERIODIC = 0x0001 ; /* program for continuous periodic event */ + + /// + /// Callback is a function + /// + private const uint TIME_CALLBACK_FUNCTION = 0x0000 ; + /// + /// Callback is an event -- use SetEvent + /// + private const uint TIME_CALLBACK_EVENT_SET = 0x0010 ; + /// + /// Callback is an event -- use PulseEvent + /// + private const uint TIME_CALLBACK_EVENT_PULSE = 0x0020 ; + /// + /// This flag prevents the event from occurring after the user calls timeKillEvent() to + /// destroy it. + /// + private const uint TIME_KILL_SYNCHRONOUS = 0x0100 ; + + private readonly TimerCallback m_timerCallback; + private readonly Object m_state; + private readonly uint m_dueTime; + private readonly uint m_period; + private uint? m_timer; + private readonly IntPtr m_data; + + private static readonly TimerEventHandler m_delegate; + + /// + /// Initializes a new instance of the class. + /// + /// The timer callback. + /// The state. + /// The due time. + /// The period. + + public HighResolutionTimer( + TimerCallback timerCallback, + Object state, + long dueTime, + long period ) + { + m_timerCallback = timerCallback; + m_state = state; + m_dueTime = (uint) dueTime; + m_period = (uint) period; + m_data = Marshal.GetIUnknownForObject( this ) ; + m_timer = null; + + if (Log.IsDebugEnabled) + { + Log.Debug( String.Format( + ".Constructor - Creating timer: dueTime={0}, period={1}, data={2}", + m_dueTime, + m_period, + m_data ) ) ; + } + + Start(); + } + + /// + /// Destructor + /// + + ~HighResolutionTimer() + { + Dispose() ; + } + + /// + /// Called when timer event occurs. + /// + /// The id. + /// The MSG. + /// The user CTX. + /// The RSV1. + /// The RSV2. + + private static void OnTimerEvent(uint id, uint msg, IntPtr userCtx, uint rsv1, uint rsv2) + { + // Check for an invalid pointer. Appears that this condition + // occurs when the GC moves memory around. Because we use the + // IntPtr to the data that is provided to use through the Marshaller + // we expect that this value should not change. + + long ptrLongValue = userCtx.ToInt64(); + if ( ptrLongValue <= 0 ) + { + return; + } + + // Convert the IntPtr back into its object form. Once in its object form, the + // object resolution timer can be called. + + try + { + HighResolutionTimer timer = Marshal.GetObjectForIUnknown(userCtx) as HighResolutionTimer; + if (timer != null) + { + timer.m_timerCallback(timer.m_state); + } + } + catch (Exception) + { + } + } + + /// + /// Starts this instance. + /// + + private void Start() + { + if (m_dueTime == 0) + { + m_timer = NativeMethods.timeSetEvent( + m_period, + m_period, + m_delegate, + m_data, + TIME_PERIODIC | TIME_KILL_SYNCHRONOUS); + + Log.Debug(".Start - Timer#{0}", m_timer); + + if (m_timer.Value == 0) + { + throw new TimerException("Unable to allocate multimedia timer"); + } + + m_timerTable[m_timer.Value] = m_timer.Value; + m_timerCallback(m_state); + } + else + { + m_timer = NativeMethods.timeSetEvent( + m_dueTime, + m_period, + m_delegate, + m_data, + TIME_PERIODIC | TIME_KILL_SYNCHRONOUS); + + Log.Debug(".Start - Timer#{0}", m_timer); + + if (m_timer.Value == 0) + { + throw new TimerException("Unable to allocate multimedia timer"); + } + + m_timerTable[m_timer.Value] = m_timer.Value; + } + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + + public void Dispose() + { + Log.Debug( ".Dispose" ) ; + + if (m_timer.HasValue) + { + Log.Debug(".Dispose - Timer#{0}", m_timer.Value) ; + NativeMethods.timeKillEvent(m_timer.Value); + m_timerTable.Remove(m_timer.Value); + m_timer = null; + } + + GC.SuppressFinalize( this ) ; + } + + /// + /// Reference to the appDomain for this instance + /// + + static AppDomain m_appDomain; + static Dictionary m_timerTable; + + static HighResolutionTimer() + { + m_timerTable = new Dictionary(); + m_appDomain = AppDomain.CurrentDomain; + m_appDomain.DomainUnload += new EventHandler(OnAppDomainUnload); + m_delegate = new TimerEventHandler(OnTimerEvent); + } + + /// + /// Called when an AppDomain is unloaded. Our goal here is to ensure that + /// all timers created by this class under the banner of this AppDomain + /// are cleaned up prior to the AppDomain unloading. Failure to do so will + /// cause applications to crash due to exceptions outside of the AppDomain. + /// + /// + /// + + static void OnAppDomainUnload(object sender, EventArgs e) + { + if (sender == m_appDomain) + { + Log.Info(".OnAppDomainUnload - Called; unloading timers"); + + // Current AppDomain is about to unload. It is vital that any + // multimedia timers that were tied to this domain be killed + // immediately so that they do not attempt to make invocations + // back into this domain. + + foreach (uint timerId in m_timerTable.Keys) + { + NativeMethods.timeKillEvent(timerId); + Log.Warn(".OnAppDomainUnload - KillEvent #{0}", timerId) ; + } + + m_timerTable.Clear() ; + + // Give the timers a brief amount of time to recover + + Thread.Sleep(100); + } + } + + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + } +} diff --git a/NEsper.Core/NEsper.Core/compat/timers/HighResolutionTimerFactory.cs b/NEsper.Core/NEsper.Core/compat/timers/HighResolutionTimerFactory.cs new file mode 100755 index 000000000..1a3107c0d --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/timers/HighResolutionTimerFactory.cs @@ -0,0 +1,39 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System.Threading; + +namespace com.espertech.esper.compat +{ + /// + /// Implementation of the TimerFactory that uses the HighResolutionTimer. + /// + + public class HighResolutionTimerFactory : ITimerFactory + { + /// + /// Creates a timer. The timer will begin after dueTime (in milliseconds) + /// has passed and will occur at an interval specified by the period. + /// + /// + /// + /// + + public ITimer CreateTimer( + TimerCallback timerCallback, + long period) + { + return new HighResolutionTimer( + timerCallback, + null, + 0, + period); + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/timers/ITimer.cs b/NEsper.Core/NEsper.Core/compat/timers/ITimer.cs new file mode 100755 index 000000000..2ccb468c7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/timers/ITimer.cs @@ -0,0 +1,22 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; + +namespace com.espertech.esper.compat +{ + /// + /// An object that represents a timer. Timers must be + /// disposable. + /// + + public interface ITimer : IDisposable + { + } +} diff --git a/NEsper.Core/NEsper.Core/compat/timers/ITimerFactory.cs b/NEsper.Core/NEsper.Core/compat/timers/ITimerFactory.cs new file mode 100755 index 000000000..facd031b3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/timers/ITimerFactory.cs @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System.Threading; + +namespace com.espertech.esper.compat +{ + /// + /// Factory object that creates timers. + /// + + public interface ITimerFactory + { + /// + /// Creates a timer. The timer will begin after dueTime (in milliseconds) + /// has passed and will occur at an interval specified by the period. + /// + /// + /// + /// + + ITimer CreateTimer( + TimerCallback timerCallback, + long period); + } +} diff --git a/NEsper.Core/NEsper.Core/compat/timers/SystemTimerFactory.cs b/NEsper.Core/NEsper.Core/compat/timers/SystemTimerFactory.cs new file mode 100755 index 000000000..2e53a34fb --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/timers/SystemTimerFactory.cs @@ -0,0 +1,364 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Threading; + +using com.espertech.esper.compat.logging; +using com.espertech.esper.compat.threading; + +namespace com.espertech.esper.compat.timers +{ + /// + /// Implementation of the timer factory that uses the system timer. + /// + + public class SystemTimerFactory : ITimerFactory + { + private readonly LinkedList _timerCallbackList = new LinkedList(); + private readonly Object _harmonicLock = new Object(); + private ITimer _harmonic; + private long _currGeneration; // current generation + private long _lastGeneration; // last generation that produced an event + + /// + /// Disposable timer kept for internal purposes; cascades the timer effect. + /// + + internal class InternalTimer : ITimer + { + internal TimerCallback TimerCallback; + internal long NextTime; + internal long Interval; + internal SlimLock SlimLock; + + /// + /// Initializes a new instance of the class. + /// + internal InternalTimer(long period, TimerCallback callback) + { + SlimLock = new SlimLock(); + TimerCallback = callback; + Interval = period * 1000; + NextTime = PerformanceObserver.MicroTime; + } + + /// + /// Called when [timer callback]. + /// + /// The curr time. + internal void OnTimerCallback( long currTime ) + { + if (!SlimLock.Enter(0)) { + return; + } + + try + { + while (true) + { + currTime = PerformanceObserver.MicroTime; + if (currTime < NextTime) + { + break; + } + + NextTime += Interval; + + var callback = TimerCallback; + if (callback != null) { + callback.Invoke(null); + } + } + } + finally { + SlimLock.Release(); + } + } + + #region IDisposable Members + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + TimerCallback = null; + } + + #endregion + } + + /// + /// Creates the timer. + /// + private void CreateBaseTimer() + { + lock (_harmonicLock) + { + if (_harmonic == null) + { +#if MONO + _harmonic = new HarmonicTimer(OnTimerEvent); +#else + _harmonic = new HighResolutionTimer(OnTimerEvent, null, 0, 10); +#endif + } + } + } + + /// + /// Determines whether the specified generations are idling. + /// + /// The idle generations. + /// + /// true if the specified idle generations is idling; otherwise, false. + /// + private static bool IsIdling(long idleGenerations) + { + return idleGenerations == 1000; + } + + /// + /// Determines whether [is prune generation] [the specified generation]. + /// + /// The generation. + /// + /// true if [is prune generation] [the specified generation]; otherwise, false. + /// + private static bool IsPruneGeneration(long generation) + { + return generation%200 == 199; + } + + /// + /// Prunes dead callbacks. + /// + private void OnPruneCallbacks() + { + var deadList = new LinkedList>(); + var node = _timerCallbackList.First; + while (node != null) + { + InternalTimer timer = node.Value; + TimerCallback timerCallback = timer.TimerCallback; + + if (timerCallback == null) + { + deadList.AddLast(node); + } + + node = node.Next; + } + + // Prune dead nodes + foreach (LinkedListNode pnode in deadList) + { + _timerCallbackList.Remove(pnode); + } + } + + /// + /// Occurs when the timer event fires. + /// + /// The user data. + private void OnTimerEvent(Object userData) + { + long curr = Interlocked.Increment(ref _currGeneration); + + LinkedListNode node = _timerCallbackList.First; + if (node != null) + { + Interlocked.Exchange(ref _lastGeneration, curr); + + while (node != null) + { + node.Value.OnTimerCallback(0); + //node.Value.OnTimerCallback(curr); + node = node.Next; + } + + // Callbacks that are no longer in use are occassionally purged + // from the list. This is known as the prune cycle and occurs at + // an interval determined by the IsPruneGeneration() method. + if (IsPruneGeneration(curr)) + { + OnPruneCallbacks(); + } + } + else + { + long last = Interlocked.Read(ref _lastGeneration); + + // If the timer is running and doing nothing, we consider the + // timer to be idling. Idling only occurs when there are no callbacks, + // so if the system is idling, we can actively shutdown the thread + // timer. + if (IsIdling(curr - last)) + { + lock (_harmonicLock) + { + if ( _harmonic != null ) + { + _harmonic.Dispose(); + _harmonic = null; + } + } + } + } + } + + /// + /// Creates a timer. The timer will begin after dueTime (in milliseconds) + /// has passed and will occur at an interval specified by the period. + /// + /// + /// + /// + + public ITimer CreateTimer( + TimerCallback timerCallback, + long period) + { + // Create a disposable timer that can be given back to the + // caller. The item is also used to track the lifetime of the + // callback. + var internalTimer = new InternalTimer(period, timerCallback); + _timerCallbackList.AddLast(internalTimer); + + CreateBaseTimer(); + + return internalTimer; + } + + /// + /// Thread-based timer that used a harmonic algorithm to ensure that + /// the thread clicks at a regular interval. + /// + + private class HarmonicTimer : ITimer + { + private const int INTERNAL_CLOCK_SLIP = 100000; + + private readonly Guid _id; + private Thread _thread; + private readonly TimerCallback _timerCallback; + private readonly long _tickAlign; + private readonly long _tickPeriod; + private bool _alive; + + /// + /// Starts thread processing. + /// + + private void Start() + { + Log.Debug(".Run - timer thread starting"); + + long lTickAlign = _tickAlign; + long lTickPeriod = _tickPeriod; + + try + { + Thread.BeginThreadAffinity(); + + while (_alive) + { + // Check the tickAlign to determine if we are here "too early" + // The CLR is a little sloppy in the way that thread timers are handled. + // In Java, when a timer is setup, the timer will adjust the interval + // up and down to match the interval set by the requestor. As a result, + // you will can easily see intervals between calls that look like 109ms, + // 94ms, 109ms, 94ms. This is how the JVM ensures that the caller gets + // an average of 100ms. The CLR however will provide you with 109ms, + // 109ms, 109ms, 109ms. Eventually this leads to slip in the timer. + // To account for that we under allocate the timer interval by some + // small amount and allow the thread to sleep a wee-bit if the timer + // is too early to the next clock cycle. + + long currTickCount = DateTime.Now.Ticks; + long currDelta = lTickAlign - currTickCount; + + //Console.WriteLine("Curr: {0} {1} {2}", currDelta, currTickCount, Environment.TickCount); + + while(currDelta > INTERNAL_CLOCK_SLIP) + { + if (currDelta >= 600000) + Thread.Sleep(1); // force-yield quanta + else + Thread.SpinWait(20); + + currTickCount = DateTime.Now.Ticks; + currDelta = lTickAlign - currTickCount; + + //Console.WriteLine("Curr: {0} {1} {2}", currDelta, currTickCount, Environment.TickCount); + } + + lTickAlign += lTickPeriod; + _timerCallback(null); + } + } + catch (ThreadInterruptedException) + { + Thread.EndThreadAffinity(); + } + + Log.Debug( ".Run - timer thread stopping"); + } + + /// + /// Creates the timer and wraps it + /// + /// + + public HarmonicTimer(TimerCallback timerCallback) + { + _id = Guid.NewGuid(); + _alive = true; + + _timerCallback = timerCallback; + + _tickPeriod = 100000; + _tickAlign = DateTime.Now.Ticks; + + _thread = new Thread(Start); + _thread.Priority = ThreadPriority.AboveNormal; + _thread.IsBackground = true; + _thread.Name = "ThreadBasedTimer{" + _id + "}"; + _thread.Start(); + } + + /// + /// Called when the object is destroyed. + /// + + ~HarmonicTimer() + { + Dispose(); + } + + /// + /// Cleans up system resources + /// + + public void Dispose() + { + _alive = false; + + if (_thread != null) + { + _thread.Interrupt(); + _thread = null; + } + } + + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/timers/TimerException.cs b/NEsper.Core/NEsper.Core/compat/timers/TimerException.cs new file mode 100755 index 000000000..5cb8d3312 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/timers/TimerException.cs @@ -0,0 +1,30 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; + +namespace com.espertech.esper.compat +{ + /// + /// A general purpose exception for timer events + /// + + public class TimerException : Exception + { + /// + /// Initializes a new instance of the class. + /// + public TimerException() : base() { } + /// + /// Initializes a new instance of the class. + /// + /// The message. + public TimerException(string message) : base(message) { } + } +} diff --git a/NEsper.Core/NEsper.Core/compat/timers/TimerFactory.cs b/NEsper.Core/NEsper.Core/compat/timers/TimerFactory.cs new file mode 100755 index 000000000..0f9f1e2fb --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/timers/TimerFactory.cs @@ -0,0 +1,55 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; + +using com.espertech.esper.compat.timers; + +namespace com.espertech.esper.compat +{ + /// + /// Creates timers. + /// + + public class TimerFactory + { + /// + /// Gets the default timer factory + /// + + public static ITimerFactory DefaultTimerFactory + { + get + { + lock( factoryLock ) + { + if (defaultTimerFactory == null) + { + // use the system timer factory unless explicitly instructed + // to do otherwise. + + defaultTimerFactory = new SystemTimerFactory(); + } + } + + return defaultTimerFactory; + } + set + { + lock( factoryLock ) + { + defaultTimerFactory = value; + } + } + } + + private static ITimerFactory defaultTimerFactory; + private static readonly Object factoryLock = new Object(); + } +} diff --git a/NEsper.Core/NEsper.Core/compat/xml/IXPathFunctionResolver.cs b/NEsper.Core/NEsper.Core/compat/xml/IXPathFunctionResolver.cs new file mode 100755 index 000000000..f53f2edcd --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/xml/IXPathFunctionResolver.cs @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Xml.XPath; +using System.Xml.Xsl; + +namespace com.espertech.esper.compat.xml +{ + public interface IXPathFunctionResolver + { + /// + /// Resolves the function that is identified by the specified information. + /// + /// The prefix. + /// The name. + /// The arg types. + /// + IXsltContextFunction ResolveFunction(string prefix, string name, XPathResultType[] argTypes); + } +} diff --git a/NEsper.Core/NEsper.Core/compat/xml/IXPathVariableResolver.cs b/NEsper.Core/NEsper.Core/compat/xml/IXPathVariableResolver.cs new file mode 100755 index 000000000..443d5b2b1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/xml/IXPathVariableResolver.cs @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Xml.Xsl; + +namespace com.espertech.esper.compat.xml +{ + public interface IXPathVariableResolver + { + /// + /// Resolves the variable that is identified by the specified information. + /// + /// The prefix. + /// The name. + /// + IXsltContextVariable ResolveVariable(string prefix, string name); + } +} diff --git a/NEsper.Core/NEsper.Core/compat/xml/XPathIteratorNodeList.cs b/NEsper.Core/NEsper.Core/compat/xml/XPathIteratorNodeList.cs new file mode 100755 index 000000000..44cf4f27e --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/xml/XPathIteratorNodeList.cs @@ -0,0 +1,54 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Xml; +using System.Xml.XPath; + +namespace com.espertech.esper.compat.xml +{ + public class XPathIteratorNodeList : XmlNodeList + { + private readonly List _nodeList; + + public XPathIteratorNodeList(XPathNodeIterator iterator) + { + _nodeList = new List(iterator.Count); + while (iterator.MoveNext()) { + var value = iterator.Current; + if (value is IHasXmlNode) { + _nodeList.Add(((IHasXmlNode) value).GetNode()); + } else { + throw new ArgumentException("unable to handle node type"); + } + } + } + + #region Overrides of XmlNodeList + + public override XmlNode Item(int index) + { + return _nodeList[index]; + } + + public override IEnumerator GetEnumerator() + { + return _nodeList.GetEnumerator(); + } + + public override int Count + { + get { return _nodeList.Count; } + } + + #endregion + } +} diff --git a/NEsper.Core/NEsper.Core/compat/xml/XmlExtensions.cs b/NEsper.Core/NEsper.Core/compat/xml/XmlExtensions.cs new file mode 100755 index 000000000..9b96b9783 --- /dev/null +++ b/NEsper.Core/NEsper.Core/compat/xml/XmlExtensions.cs @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Xml; + +namespace com.espertech.esper.compat.xml +{ + public static class XmlExtensions + { + /// + /// Gets the declaration (if any) from the document. + /// + /// The document. + /// + public static XmlDeclaration GetDeclaration(this XmlDocument document) + { + foreach (var node in document.ChildNodes) + { + if ( node is XmlDeclaration ) { + return (XmlDeclaration) node; + } + } + + return null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/activator/ViewableActivationResult.cs b/NEsper.Core/NEsper.Core/core/context/activator/ViewableActivationResult.cs new file mode 100755 index 000000000..8770560ae --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/activator/ViewableActivationResult.cs @@ -0,0 +1,54 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.compat.threading; +using com.espertech.esper.pattern; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.context.activator +{ + public class ViewableActivationResult + { + public ViewableActivationResult( + Viewable viewable, + StopCallback stopCallback, + IReaderWriterLock optionalLock, + EvalRootState optionalPatternRoot, + EvalRootMatchRemover optEvalRootMatchRemover, + bool suppressSameEventMatches, + bool discardPartialsOnMatch, + ViewableActivationResultExtension viewableActivationResultExtension) + { + Viewable = viewable; + StopCallback = stopCallback; + OptionalLock = optionalLock; + OptionalPatternRoot = optionalPatternRoot; + OptEvalRootMatchRemover = optEvalRootMatchRemover; + IsSuppressSameEventMatches = suppressSameEventMatches; + IsDiscardPartialsOnMatch = discardPartialsOnMatch; + ViewableActivationResultExtension = viewableActivationResultExtension; + } + + public StopCallback StopCallback { get; private set; } + + public Viewable Viewable { get; private set; } + + public IReaderWriterLock OptionalLock { get; private set; } + + public EvalRootState OptionalPatternRoot { get; private set; } + + public EvalRootMatchRemover OptEvalRootMatchRemover { get; private set; } + + public bool IsSuppressSameEventMatches { get; private set; } + + public bool IsDiscardPartialsOnMatch { get; private set; } + + public ViewableActivationResultExtension ViewableActivationResultExtension { get; set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/context/activator/ViewableActivationResultExtension.cs b/NEsper.Core/NEsper.Core/core/context/activator/ViewableActivationResultExtension.cs new file mode 100755 index 000000000..574b42cdb --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/activator/ViewableActivationResultExtension.cs @@ -0,0 +1,14 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.core.context.activator +{ + public interface ViewableActivationResultExtension + { + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/context/activator/ViewableActivator.cs b/NEsper.Core/NEsper.Core/core/context/activator/ViewableActivator.cs new file mode 100755 index 000000000..c0940e2ca --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/activator/ViewableActivator.cs @@ -0,0 +1,62 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.core.context.util; + +namespace com.espertech.esper.core.context.activator +{ + public interface ViewableActivator + { + ViewableActivationResult Activate(AgentInstanceContext agentInstanceContext, bool isSubselect, bool isRecoveringResilient); + } + + public class ProxyViewableActivator : ViewableActivator + { + /// + /// Gets or sets the proc activate. + /// + /// + /// The proc activate. + /// + public Func ProcActivate { get; set; } + + /// + /// Initializes a new instance of the class. + /// + /// The proc activate. + public ProxyViewableActivator(Func procActivate) + { + ProcActivate = procActivate; + } + + /// + /// Initializes a new instance of the class. + /// + public ProxyViewableActivator() + { + } + + /// + /// Activates the specified agent instance context. + /// + /// The agent instance context. + /// if set to true [is subselect]. + /// if set to true [is recovering resilient]. + /// + /// + public ViewableActivationResult Activate( + AgentInstanceContext agentInstanceContext, + bool isSubselect, + bool isRecoveringResilient) + { + return ProcActivate.Invoke(agentInstanceContext, isSubselect, isRecoveringResilient); + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/activator/ViewableActivatorFactory.cs b/NEsper.Core/NEsper.Core/core/context/activator/ViewableActivatorFactory.cs new file mode 100755 index 000000000..9cec3fdf6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/activator/ViewableActivatorFactory.cs @@ -0,0 +1,220 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.named; +using com.espertech.esper.epl.property; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.filter; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.pattern; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.context.activator +{ + public interface ViewableActivatorFactory + { + ViewableActivator CreateActivatorSimple( + FilterStreamSpecCompiled filterStreamSpec); + + ViewableActivator CreateFilterProxy( + EPServicesContext services, + FilterSpecCompiled filterSpec, + Attribute[] annotations, + bool subselect, + InstrumentationAgent instrumentationAgentSubquery, + bool isCanIterate, + int? streamNumFromClause); + + ViewableActivator CreateStreamReuseView( + EPServicesContext services, + StatementContext statementContext, + StatementSpecCompiled statementSpec, + FilterStreamSpecCompiled filterStreamSpec, + bool isJoin, + ExprEvaluatorContextStatement evaluatorContextStmt, + bool filterSubselectSameStream, + int streamNum, + bool isCanIterateUnbound); + + ViewableActivator CreatePattern( + PatternContext patternContext, + EvalRootFactoryNode rootFactoryNode, + EventType eventType, + bool consumingFilters, + bool suppressSameEventMatches, + bool discardPartialsOnMatch, + bool isCanIterateUnbound); + + ViewableActivator CreateNamedWindow( + NamedWindowProcessor processor, + NamedWindowConsumerStreamSpec streamSpec, + StatementContext statementContext); + + ViewableActivator CreateTable( + TableMetadata metadata, ExprEvaluator[] optionalTableFilters); + + ViewableActivator MakeHistorical( + HistoricalEventViewable historicalEventViewable); + + ViewableActivator MakeSubqueryNWIndexShare(); + } + + public class ProxyViewableActivatorFactory : ViewableActivatorFactory + { + public Func ProcCreateActivatorSimple { get; set; } + + public ViewableActivator CreateActivatorSimple( + FilterStreamSpecCompiled filterStreamSpec) + { + if (ProcCreateActivatorSimple == null) + throw new NotSupportedException(); + + return ProcCreateActivatorSimple.Invoke( + filterStreamSpec); + } + + public Func + ProcCreateActivatorProxy { get; set; } + + public ViewableActivator CreateFilterProxy( + EPServicesContext services, + FilterSpecCompiled filterSpec, + Attribute[] annotations, + bool subselect, + InstrumentationAgent instrumentationAgentSubquery, + bool isCanIterate, + int? streamNumFromClause) + { + if (ProcCreateActivatorProxy == null) + throw new NotSupportedException(); + + return ProcCreateActivatorProxy.Invoke( + services, + filterSpec, + annotations, + subselect, + instrumentationAgentSubquery, + isCanIterate, + streamNumFromClause); + } + + public Func + ProcCreateStreamReuseView { get; set; } + + public ViewableActivator CreateStreamReuseView( + EPServicesContext services, + StatementContext statementContext, + StatementSpecCompiled statementSpec, + FilterStreamSpecCompiled filterStreamSpec, + bool isJoin, + ExprEvaluatorContextStatement evaluatorContextStmt, + bool filterSubselectSameStream, + int streamNum, + bool isCanIterateUnbound) + { + if (ProcCreateStreamReuseView == null) + throw new NotSupportedException(); + + return ProcCreateStreamReuseView.Invoke( + services, + statementContext, + statementSpec, + filterStreamSpec, + isJoin, + evaluatorContextStmt, + filterSubselectSameStream, + streamNum, + isCanIterateUnbound); + } + + public Func + ProcCreatePattern { get; set; } + + public ViewableActivator CreatePattern( + PatternContext patternContext, + EvalRootFactoryNode rootFactoryNode, + EventType eventType, + bool consumingFilters, + bool suppressSameEventMatches, + bool discardPartialsOnMatch, + bool isCanIterateUnbound) + { + if (ProcCreatePattern == null) + throw new NotSupportedException(); + + return ProcCreatePattern.Invoke( + patternContext, + rootFactoryNode, + eventType, + consumingFilters, + suppressSameEventMatches, + discardPartialsOnMatch, + isCanIterateUnbound); + } + + public Func + ProcCreateNamedWindow { get; set; } + + public ViewableActivator CreateNamedWindow( + NamedWindowProcessor processor, + NamedWindowConsumerStreamSpec streamSpec, + StatementContext statementContext) + { + if (ProcCreateNamedWindow == null) + throw new NotSupportedException(); + + return ProcCreateNamedWindow.Invoke( + processor, + streamSpec, + statementContext); + } + + public Func + ProcCreateTable { get; set; } + + public ViewableActivator CreateTable(TableMetadata metadata, ExprEvaluator[] optionalTableFilters) + { + if (ProcCreateTable == null) + throw new NotSupportedException(); + + return ProcCreateTable.Invoke( + metadata, + optionalTableFilters); + } + + public Func + ProcMakeHistorical { get; set; } + + public ViewableActivator MakeHistorical(HistoricalEventViewable historicalEventViewable) + { + if (ProcMakeHistorical == null) + throw new NotSupportedException(); + + return ProcMakeHistorical.Invoke(historicalEventViewable); + } + + public Func + ProcMakeSubqueryNWIndexShare { get; set; } + + public ViewableActivator MakeSubqueryNWIndexShare() + { + if (ProcMakeSubqueryNWIndexShare == null) + throw new NotSupportedException(); + + return ProcMakeSubqueryNWIndexShare.Invoke(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/activator/ViewableActivatorFactoryDefault.cs b/NEsper.Core/NEsper.Core/core/context/activator/ViewableActivatorFactoryDefault.cs new file mode 100755 index 000000000..5fe96008f --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/activator/ViewableActivatorFactoryDefault.cs @@ -0,0 +1,69 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.named; +using com.espertech.esper.epl.property; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.filter; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.pattern; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.context.activator +{ + public class ViewableActivatorFactoryDefault : ViewableActivatorFactory + { + public ViewableActivator CreateActivatorSimple(FilterStreamSpecCompiled filterStreamSpec) + { + throw new UnsupportedOperationException(); + } + + public ViewableActivator CreateFilterProxy(EPServicesContext services, FilterSpecCompiled filterSpec, Attribute[] annotations, bool subselect, InstrumentationAgent instrumentationAgentSubquery, bool isCanIterate, int? streamNumFromClause) + { + return new ViewableActivatorFilterProxy(services, filterSpec, annotations, subselect, instrumentationAgentSubquery, isCanIterate); + } + + public ViewableActivator CreateStreamReuseView(EPServicesContext services, StatementContext statementContext, StatementSpecCompiled statementSpec, FilterStreamSpecCompiled filterStreamSpec, bool isJoin, ExprEvaluatorContextStatement evaluatorContextStmt, bool filterSubselectSameStream, int streamNum, bool isCanIterateUnbound) + { + return new ViewableActivatorStreamReuseView(services, statementContext, statementSpec, filterStreamSpec, isJoin, evaluatorContextStmt, filterSubselectSameStream, streamNum, isCanIterateUnbound); + } + + public ViewableActivator CreatePattern(PatternContext patternContext, EvalRootFactoryNode rootFactoryNode, EventType eventType, bool consumingFilters, bool suppressSameEventMatches, bool discardPartialsOnMatch, bool isCanIterateUnbound) + { + return new ViewableActivatorPattern(patternContext, rootFactoryNode, eventType, consumingFilters, suppressSameEventMatches, discardPartialsOnMatch, isCanIterateUnbound); + } + + public ViewableActivator CreateNamedWindow(NamedWindowProcessor processor, NamedWindowConsumerStreamSpec streamSpec, StatementContext statementContext) + { + return new ViewableActivatorNamedWindow(processor, streamSpec.FilterExpressions, streamSpec.OptPropertyEvaluator); + } + + public ViewableActivator CreateTable(TableMetadata metadata, ExprEvaluator[] optionalTableFilters) + { + return new ViewableActivatorTable(metadata, optionalTableFilters); + } + + public ViewableActivator MakeHistorical(HistoricalEventViewable historicalEventViewable) + { + return new ViewableActivatorHistorical(historicalEventViewable); + } + + public ViewableActivator MakeSubqueryNWIndexShare() + { + return new ViewableActivatorSubselectNone(); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/context/activator/ViewableActivatorFilterProxy.cs b/NEsper.Core/NEsper.Core/core/context/activator/ViewableActivatorFilterProxy.cs new file mode 100755 index 000000000..107d8074e --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/activator/ViewableActivatorFilterProxy.cs @@ -0,0 +1,105 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.filter; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.view; +using com.espertech.esper.view.stream; + +namespace com.espertech.esper.core.context.activator +{ + public class ViewableActivatorFilterProxy : ViewableActivator + { + private readonly EPServicesContext _services; + private readonly FilterSpecCompiled _filterSpec; + private readonly Attribute[] _annotations; + private readonly bool _isSubSelect; + private readonly InstrumentationAgent _instrumentationAgent; + private readonly bool _isCanIterate; + + internal ViewableActivatorFilterProxy(EPServicesContext services, FilterSpecCompiled filterSpec, Attribute[] annotations, bool subSelect, InstrumentationAgent instrumentationAgent, bool isCanIterate) + { + _services = services; + _filterSpec = filterSpec; + _annotations = annotations; + _isSubSelect = subSelect; + _instrumentationAgent = instrumentationAgent; + _isCanIterate = isCanIterate; + } + + public ViewableActivationResult Activate(AgentInstanceContext agentInstanceContext, bool isSubselect, bool isRecoveringResilient) + { + // New event stream + EventType resultEventType = _filterSpec.ResultEventType; + EventStream zeroDepthStream = _isCanIterate ? + (EventStream)new ZeroDepthStreamIterable(resultEventType) : + (EventStream)new ZeroDepthStreamNoIterate(resultEventType); + + // audit proxy + var inputStream = EventStreamProxy.GetAuditProxy(agentInstanceContext.StatementContext.EngineURI, agentInstanceContext.EpStatementAgentInstanceHandle.StatementHandle.StatementName, _annotations, _filterSpec, zeroDepthStream); + + var eventStream = inputStream; + var statementId = agentInstanceContext.StatementContext.StatementId; + + var filterCallback = new ProxyFilterHandleCallback + { + ProcStatementId = () => statementId, + ProcIsSubselect = () => _isSubSelect + }; + + if (_filterSpec.OptionalPropertyEvaluator != null) + { + filterCallback.ProcMatchFound = (theEvent, allStmtMatches) => + { + var result = _filterSpec.OptionalPropertyEvaluator.GetProperty(theEvent, agentInstanceContext); + if (result != null) + { + eventStream.Insert(result); + } + }; + } + else + { + filterCallback.ProcMatchFound = (theEvent, allStmtMatches) => + { + if (InstrumentationHelper.ENABLED) { _instrumentationAgent.IndicateQ(); } + eventStream.Insert(theEvent); + if (InstrumentationHelper.ENABLED) { _instrumentationAgent.IndicateA(); } + }; + } + + var filterHandle = new EPStatementHandleCallback(agentInstanceContext.EpStatementAgentInstanceHandle, filterCallback); + + FilterValueSetParam[][] addendum = null; + if (agentInstanceContext.AgentInstanceFilterProxy != null) + { + addendum = agentInstanceContext.AgentInstanceFilterProxy.GetAddendumFilters(_filterSpec); + } + var filterValueSet = _filterSpec.GetValueSet(null, agentInstanceContext, addendum); + var filterServiceEntry = _services.FilterService.Add(filterValueSet, filterHandle); + + var stopCallback = new ViewableActivatorFilterProxyStopCallback(this, filterHandle, filterServiceEntry); + return new ViewableActivationResult(inputStream, stopCallback, null, null, null, false, false, null); + } + + public EPServicesContext Services + { + get { return _services; } + } + + public FilterSpecCompiled FilterSpec + { + get { return _filterSpec; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/activator/ViewableActivatorFilterProxyStopCallback.cs b/NEsper.Core/NEsper.Core/core/context/activator/ViewableActivatorFilterProxyStopCallback.cs new file mode 100755 index 000000000..af4397ba0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/activator/ViewableActivatorFilterProxyStopCallback.cs @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.service; +using com.espertech.esper.filter; +using com.espertech.esper.util; + +namespace com.espertech.esper.core.context.activator +{ + public class ViewableActivatorFilterProxyStopCallback : StopCallback + { + private readonly ViewableActivatorFilterProxy _parent; + private EPStatementHandleCallback _filterHandle; + private readonly FilterServiceEntry _filterServiceEntry; + + public ViewableActivatorFilterProxyStopCallback(ViewableActivatorFilterProxy parent, EPStatementHandleCallback filterHandle, FilterServiceEntry filterServiceEntry) + { + _parent = parent; + _filterHandle = filterHandle; + _filterServiceEntry = filterServiceEntry; + } + + public void Stop() + { + lock (this) + { + if (_filterHandle != null) + { + _parent.Services.FilterService.Remove(_filterHandle, _filterServiceEntry); + } + _filterHandle = null; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/activator/ViewableActivatorHistorical.cs b/NEsper.Core/NEsper.Core/core/context/activator/ViewableActivatorHistorical.cs new file mode 100755 index 000000000..047e3e281 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/activator/ViewableActivatorHistorical.cs @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.context.util; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.context.activator +{ + public class ViewableActivatorHistorical : ViewableActivator + { + private readonly HistoricalEventViewable _historicalEventViewable; + + public ViewableActivatorHistorical(HistoricalEventViewable historicalEventViewable) + { + this._historicalEventViewable = historicalEventViewable; + } + + public ViewableActivationResult Activate(AgentInstanceContext agentInstanceContext, bool isSubselect, bool isRecoveringResilient) + { + return new ViewableActivationResult(_historicalEventViewable, CollectionUtil.STOP_CALLBACK_NONE, null, null, null, false, false, null); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/context/activator/ViewableActivatorNamedWindow.cs b/NEsper.Core/NEsper.Core/core/context/activator/ViewableActivatorNamedWindow.cs new file mode 100755 index 000000000..a692688fc --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/activator/ViewableActivatorNamedWindow.cs @@ -0,0 +1,46 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.named; +using com.espertech.esper.epl.property; + +namespace com.espertech.esper.core.context.activator +{ + public class ViewableActivatorNamedWindow : ViewableActivator + { + private readonly NamedWindowProcessor _processor; + private readonly IList _filterExpressions; + private readonly PropertyEvaluator _optPropertyEvaluator; + + public ViewableActivatorNamedWindow( + NamedWindowProcessor processor, + IList filterExpressions, + PropertyEvaluator optPropertyEvaluator) + { + _processor = processor; + _filterExpressions = filterExpressions; + _optPropertyEvaluator = optPropertyEvaluator; + } + + public ViewableActivationResult Activate( + AgentInstanceContext agentInstanceContext, + bool isSubselect, + bool isRecoveringResilient) + { + var consumerDesc = new NamedWindowConsumerDesc( + _filterExpressions, _optPropertyEvaluator, agentInstanceContext); + var consumerView = _processor.AddConsumer(consumerDesc, isSubselect); + return new ViewableActivationResult(consumerView, consumerView, null, null, null, false, false, null); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/context/activator/ViewableActivatorPattern.cs b/NEsper.Core/NEsper.Core/core/context/activator/ViewableActivatorPattern.cs new file mode 100755 index 000000000..69afd26d9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/activator/ViewableActivatorPattern.cs @@ -0,0 +1,106 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.pattern; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.context.activator +{ + public class ViewableActivatorPattern : ViewableActivator + { + private readonly PatternContext _patternContext; + private readonly EvalRootFactoryNode _rootFactoryNode; + private readonly EventType _eventType; + private readonly bool _hasConsumingFilter; + private readonly bool _suppressSameEventMatches; + private readonly bool _discardPartialsOnMatch; + private readonly bool _isCanIterate; + + internal ViewableActivatorPattern( + PatternContext patternContext, + EvalRootFactoryNode rootFactoryNode, + EventType eventType, + bool hasConsumingFilter, + bool suppressSameEventMatches, + bool discardPartialsOnMatch, + bool isCanIterate) + { + _patternContext = patternContext; + _rootFactoryNode = rootFactoryNode; + _eventType = eventType; + _hasConsumingFilter = hasConsumingFilter; + _suppressSameEventMatches = suppressSameEventMatches; + _discardPartialsOnMatch = discardPartialsOnMatch; + _isCanIterate = isCanIterate; + } + + public ViewableActivationResult Activate( + AgentInstanceContext agentInstanceContext, + bool isSubselect, + bool isRecoveringResilient) + { + PatternAgentInstanceContext patternAgentInstanceContext = + agentInstanceContext.StatementContext.PatternContextFactory.CreatePatternAgentContext( + _patternContext, agentInstanceContext, _hasConsumingFilter); + EvalRootNode rootNode = EvalNodeUtil.MakeRootNodeFromFactory(_rootFactoryNode, patternAgentInstanceContext); + + EventStream sourceEventStream = _isCanIterate ? + (EventStream) new ZeroDepthStreamIterable(_eventType) : + (EventStream) new ZeroDepthStreamNoIterate(_eventType); + StatementContext statementContext = _patternContext.StatementContext; + PatternMatchCallback callback = matchEvent => + { + EventBean compositeEvent = statementContext.EventAdapterService.AdapterForTypedMap( + matchEvent, _eventType); + sourceEventStream.Insert(compositeEvent); + }; + + var rootState = (EvalRootState) rootNode.Start(callback, _patternContext, isRecoveringResilient); + return new ViewableActivationResult( + sourceEventStream, rootState, null, rootState, rootState, _suppressSameEventMatches, _discardPartialsOnMatch, null); + } + + public EvalRootFactoryNode RootFactoryNode + { + get { return _rootFactoryNode; } + } + + public PatternContext PatternContext + { + get { return _patternContext; } + } + + public EventType EventType + { + get { return _eventType; } + } + + public bool HasConsumingFilter + { + get { return _hasConsumingFilter; } + } + + public bool IsSuppressSameEventMatches + { + get { return _suppressSameEventMatches; } + } + + public bool IsDiscardPartialsOnMatch + { + get { return _discardPartialsOnMatch; } + } + + public bool IsCanIterate + { + get { return _isCanIterate; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/activator/ViewableActivatorStreamReuseView.cs b/NEsper.Core/NEsper.Core/core/context/activator/ViewableActivatorStreamReuseView.cs new file mode 100755 index 000000000..bd0f951f3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/activator/ViewableActivatorStreamReuseView.cs @@ -0,0 +1,70 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.spec; +using com.espertech.esper.util; + +namespace com.espertech.esper.core.context.activator +{ + public class ViewableActivatorStreamReuseView + : ViewableActivator + , StopCallback + { + private readonly EPServicesContext _services; + private readonly StatementContext _statementContext; + private readonly StatementSpecCompiled _statementSpec; + private readonly FilterStreamSpecCompiled _filterStreamSpec; + private readonly bool _join; + private readonly ExprEvaluatorContextStatement _evaluatorContextStmt; + private readonly bool _filterSubselectSameStream; + private readonly int _streamNum; + private readonly bool _isCanIterateUnbound; + + internal ViewableActivatorStreamReuseView(EPServicesContext services, StatementContext statementContext, StatementSpecCompiled statementSpec, FilterStreamSpecCompiled filterStreamSpec, bool join, ExprEvaluatorContextStatement evaluatorContextStmt, bool filterSubselectSameStream, int streamNum, bool isCanIterateUnbound) + { + _services = services; + _statementContext = statementContext; + _statementSpec = statementSpec; + _filterStreamSpec = filterStreamSpec; + _join = join; + _evaluatorContextStmt = evaluatorContextStmt; + _filterSubselectSameStream = filterSubselectSameStream; + _streamNum = streamNum; + _isCanIterateUnbound = isCanIterateUnbound; + } + + public ViewableActivationResult Activate(AgentInstanceContext agentInstanceContext, bool isSubselect, bool isRecoveringResilient) + { + var pair = _services.StreamService.CreateStream( + _statementContext.StatementId, _filterStreamSpec.FilterSpec, + _statementContext.FilterService, + agentInstanceContext.EpStatementAgentInstanceHandle, + _join, + agentInstanceContext, + _statementSpec.OrderByList.Length > 0, + _filterSubselectSameStream, + _statementContext.Annotations, + _statementContext.IsStatelessSelect, + _streamNum, + _isCanIterateUnbound); + return new ViewableActivationResult(pair.First, this, pair.Second, null, null, false, false, null); + } + + public void Stop() + { + _services.StreamService.DropStream(_filterStreamSpec.FilterSpec, _statementContext.FilterService, _join, _statementSpec.OrderByList.Length > 0, _filterSubselectSameStream, _statementContext.IsStatelessSelect); + } + + public FilterStreamSpecCompiled FilterStreamSpec + { + get { return _filterStreamSpec; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/activator/ViewableActivatorSubselectNone.cs b/NEsper.Core/NEsper.Core/core/context/activator/ViewableActivatorSubselectNone.cs new file mode 100755 index 000000000..1849f1357 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/activator/ViewableActivatorSubselectNone.cs @@ -0,0 +1,21 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.context.util; +using com.espertech.esper.util; + +namespace com.espertech.esper.core.context.activator +{ + public class ViewableActivatorSubselectNone : ViewableActivator + { + public ViewableActivationResult Activate(AgentInstanceContext agentInstanceContext, bool isSubselect, bool isRecoveringResilient) + { + return new ViewableActivationResult(null, CollectionUtil.STOP_CALLBACK_NONE, null, null, null, false, false, null); + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/activator/ViewableActivatorTable.cs b/NEsper.Core/NEsper.Core/core/context/activator/ViewableActivatorTable.cs new file mode 100755 index 000000000..f64a8f169 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/activator/ViewableActivatorTable.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.util; + +namespace com.espertech.esper.core.context.activator +{ + public class ViewableActivatorTable : ViewableActivator + { + private readonly TableMetadata _tableMetadata; + private readonly ExprEvaluator[] _optionalTableFilters; + + public ViewableActivatorTable(TableMetadata tableMetadata, ExprEvaluator[] optionalTableFilters) + { + _tableMetadata = tableMetadata; + _optionalTableFilters = optionalTableFilters; + } + + public ViewableActivationResult Activate(AgentInstanceContext agentInstanceContext, bool isSubselect, bool isRecoveringResilient) + { + TableStateInstance state = agentInstanceContext.StatementContext.TableService.GetState(_tableMetadata.TableName, agentInstanceContext.AgentInstanceId); + return new ViewableActivationResult(new TableStateViewableInternal(_tableMetadata, state, _optionalTableFilters), CollectionUtil.STOP_CALLBACK_NONE, null, null, null, false, false, null); + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactory.cs b/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactory.cs new file mode 100755 index 000000000..20d67b872 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactory.cs @@ -0,0 +1,19 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.context.util; + +namespace com.espertech.esper.core.context.factory +{ + public interface StatementAgentInstanceFactory + { + StatementAgentInstanceFactoryResult NewContext(AgentInstanceContext agentInstanceContext, bool isRecoveringResilient); + void AssignExpressions(StatementAgentInstanceFactoryResult result); + void UnassignExpressions(); + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryBase.cs b/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryBase.cs new file mode 100755 index 000000000..9e603b76d --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryBase.cs @@ -0,0 +1,49 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client.annotation; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.util; + +namespace com.espertech.esper.core.context.factory +{ + public abstract class StatementAgentInstanceFactoryBase : StatementAgentInstanceFactory + { + private readonly bool _audit; + + protected StatementAgentInstanceFactoryBase(Attribute[] annotations) + { + _audit = AuditEnum.CONTEXTPARTITION.GetAudit(annotations) != null; + } + + protected abstract StatementAgentInstanceFactoryResult NewContextInternal(AgentInstanceContext agentInstanceContext, bool isRecoveringResilient); + public abstract void AssignExpressions(StatementAgentInstanceFactoryResult result); + public abstract void UnassignExpressions(); + + public StatementAgentInstanceFactoryResult NewContext(AgentInstanceContext agentInstanceContext, bool isRecoveringResilient) + { + if (!_audit || agentInstanceContext.AgentInstanceId == -1) { + return NewContextInternal(agentInstanceContext, isRecoveringResilient); + } + + AuditPath.AuditContextPartition(agentInstanceContext.EngineURI, agentInstanceContext.StatementName, true, agentInstanceContext.AgentInstanceId); + var result = NewContextInternal(agentInstanceContext, isRecoveringResilient); + var stopCallback = result.StopCallback; + result.StopCallback = new ProxyStopCallback(() => + { + AuditPath.AuditContextPartition(agentInstanceContext.EngineURI, agentInstanceContext.StatementName, false, agentInstanceContext.AgentInstanceId); + stopCallback.Stop(); + }); + return result; + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryCreateIndex.cs b/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryCreateIndex.cs new file mode 100755 index 000000000..bf5ab5b51 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryCreateIndex.cs @@ -0,0 +1,116 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.named; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.epl.virtualdw; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.context.factory +{ + public class StatementAgentInstanceFactoryCreateIndex : StatementAgentInstanceFactory + { + private readonly EPServicesContext services; + private readonly CreateIndexDesc spec; + private readonly Viewable finalView; + private readonly NamedWindowProcessor namedWindowProcessor; + private readonly string tableName; + private readonly string contextName; + + public StatementAgentInstanceFactoryCreateIndex(EPServicesContext services, CreateIndexDesc spec, Viewable finalView, NamedWindowProcessor namedWindowProcessor, string tableName, string contextName) + { + this.services = services; + this.spec = spec; + this.finalView = finalView; + this.namedWindowProcessor = namedWindowProcessor; + this.tableName = tableName; + this.contextName = contextName; + } + + public StatementAgentInstanceFactoryResult NewContext(AgentInstanceContext agentInstanceContext, bool isRecoveringResilient) + { + StopCallback stopCallback; + + int agentInstanceId = agentInstanceContext.AgentInstanceId; + + if (namedWindowProcessor != null) { + // handle named window index + NamedWindowProcessorInstance processorInstance = namedWindowProcessor.GetProcessorInstance(agentInstanceContext); + + if (namedWindowProcessor.IsVirtualDataWindow) { + VirtualDWView virtualDWView = processorInstance.RootViewInstance.VirtualDataWindow; + virtualDWView.HandleStartIndex(spec); + stopCallback = new ProxyStopCallback(() => virtualDWView.HandleStopIndex(spec)); + } + else { + try { + processorInstance.RootViewInstance.AddExplicitIndex(spec.IsUnique, spec.IndexName, spec.Columns, isRecoveringResilient); + } + catch (ExprValidationException e) { + throw new EPException("Failed to create index: " + e.Message, e); + } + stopCallback = new ProxyStopCallback(() => { + // we remove the index when context partitioned. + // when not context partition the index gets removed when the last reference to the named window gets destroyed. + if (contextName != null) + { + var instance = namedWindowProcessor.GetProcessorInstance(agentInstanceId); + if (instance != null) + { + instance.RemoveExplicitIndex(spec.IndexName); + } + } + + }); + } + } + else { + // handle table access + try { + TableStateInstance instance = services.TableService.GetState(tableName, agentInstanceContext.AgentInstanceId); + instance.AddExplicitIndex(spec, isRecoveringResilient, contextName != null); + } + catch (ExprValidationException ex) { + throw new EPException("Failed to create index: " + ex.Message, ex); + } + stopCallback = new ProxyStopCallback(() => { + // we remove the index when context partitioned. + // when not context partition the index gets removed when the last reference to the table gets destroyed. + if (contextName != null) + { + TableStateInstance instance = services.TableService.GetState(tableName, agentInstanceId); + if (instance != null) + { + instance.RemoveExplicitIndex(spec.IndexName); + } + } + }); + } + + return new StatementAgentInstanceFactoryCreateIndexResult(finalView, stopCallback, agentInstanceContext); + } + + public void AssignExpressions(StatementAgentInstanceFactoryResult result) + { + } + + public void UnassignExpressions() + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryCreateIndexResult.cs b/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryCreateIndexResult.cs new file mode 100755 index 000000000..66feaed3f --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryCreateIndexResult.cs @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.subselect; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.expression.prev; +using com.espertech.esper.epl.expression.prior; +using com.espertech.esper.epl.expression.subquery; +using com.espertech.esper.epl.expression.table; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.context.factory +{ + public class StatementAgentInstanceFactoryCreateIndexResult : StatementAgentInstanceFactoryResult + { + public StatementAgentInstanceFactoryCreateIndexResult(Viewable finalView, StopCallback stopCallback, AgentInstanceContext agentInstanceContext) + : base(finalView, stopCallback, agentInstanceContext, null, + Collections.GetEmptyMap(), + Collections.GetEmptyMap(), + Collections.GetEmptyMap(), + null, + Collections.GetEmptyMap(), + Collections.GetEmptyList()) + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryCreateSchemaResult.cs b/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryCreateSchemaResult.cs new file mode 100755 index 000000000..0a2556b08 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryCreateSchemaResult.cs @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.subselect; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.prev; +using com.espertech.esper.epl.expression.prior; +using com.espertech.esper.epl.expression.subquery; +using com.espertech.esper.epl.expression.table; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.context.factory +{ + public class StatementAgentInstanceFactoryCreateSchemaResult : StatementAgentInstanceFactoryResult + { + public StatementAgentInstanceFactoryCreateSchemaResult(Viewable finalView, StopCallback stopCallback, AgentInstanceContext agentInstanceContext) + : base( + finalView, stopCallback, agentInstanceContext, null, + Collections.GetEmptyMap(), + Collections.GetEmptyMap(), + Collections.GetEmptyMap(), + null, + Collections.GetEmptyMap(), + Collections.GetEmptyList()) + { + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryCreateTable.cs b/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryCreateTable.cs new file mode 100755 index 000000000..e78a25f1e --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryCreateTable.cs @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.util; + +namespace com.espertech.esper.core.context.factory +{ + public class StatementAgentInstanceFactoryCreateTable : StatementAgentInstanceFactory + { + private readonly TableMetadata _tableMetadata; + + public StatementAgentInstanceFactoryCreateTable(TableMetadata tableMetadata) + { + this._tableMetadata = tableMetadata; + } + + public StatementAgentInstanceFactoryResult NewContext(AgentInstanceContext agentInstanceContext, bool isRecoveringResilient) + { + var tableState = _tableMetadata.TableStateFactory.MakeTableState(agentInstanceContext); + var aggregationReportingService = new AggregationServiceTable(tableState); + var finalView = new TableStateViewablePublic(_tableMetadata, tableState); + return new StatementAgentInstanceFactoryCreateTableResult(finalView, CollectionUtil.STOP_CALLBACK_NONE, agentInstanceContext, aggregationReportingService); + } + + public void AssignExpressions(StatementAgentInstanceFactoryResult result) + { + } + + public void UnassignExpressions() + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryCreateTableResult.cs b/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryCreateTableResult.cs new file mode 100755 index 000000000..16472b92a --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryCreateTableResult.cs @@ -0,0 +1,39 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.subselect; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.prev; +using com.espertech.esper.epl.expression.prior; +using com.espertech.esper.epl.expression.subquery; +using com.espertech.esper.epl.expression.table; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.context.factory +{ + public class StatementAgentInstanceFactoryCreateTableResult : StatementAgentInstanceFactoryResult + { + public StatementAgentInstanceFactoryCreateTableResult(Viewable finalView, StopCallback stopCallback, AgentInstanceContext agentInstanceContext, AggregationServiceTable aggregationReportingService) + : base(finalView, stopCallback, agentInstanceContext, aggregationReportingService, + Collections.GetEmptyMap(), + Collections.GetEmptyMap(), + Collections.GetEmptyMap(), + null, + Collections.GetEmptyMap(), + Collections.GetEmptyList()) + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryCreateVariable.cs b/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryCreateVariable.cs new file mode 100755 index 000000000..a86ffab6c --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryCreateVariable.cs @@ -0,0 +1,100 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.core.context.mgr; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.core.start; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.variable; +using com.espertech.esper.epl.view; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.context.factory +{ + public class StatementAgentInstanceFactoryCreateVariable : StatementAgentInstanceFactoryBase + { + private readonly CreateVariableDesc _createDesc; + private readonly StatementSpecCompiled _statementSpec; + private readonly StatementContext _statementContext; + private readonly EPServicesContext _services; + private readonly VariableMetaData _variableMetaData; + private readonly EventType _eventType; + + public StatementAgentInstanceFactoryCreateVariable(CreateVariableDesc createDesc, StatementSpecCompiled statementSpec, StatementContext statementContext, EPServicesContext services, VariableMetaData variableMetaData, EventType eventType) + : base(statementContext.Annotations) + { + _createDesc = createDesc; + _statementSpec = statementSpec; + _statementContext = statementContext; + _services = services; + _variableMetaData = variableMetaData; + _eventType = eventType; + } + + protected override StatementAgentInstanceFactoryResult NewContextInternal(AgentInstanceContext agentInstanceContext, bool isRecoveringResilient) + { + StopCallback stopCallback = new ProxyStopCallback(() => _services.VariableService.DeallocateVariableState(_variableMetaData.VariableName, agentInstanceContext.AgentInstanceId)); + _services.VariableService.AllocateVariableState( + _variableMetaData.VariableName, agentInstanceContext.AgentInstanceId, _statementContext.StatementExtensionServicesContext, isRecoveringResilient); + + CreateVariableView createView = new CreateVariableView( + _statementContext.StatementId, + _services.EventAdapterService, + _services.VariableService, + _createDesc.VariableName, + _statementContext.StatementResultService, + agentInstanceContext.AgentInstanceId); + + _services.VariableService.RegisterCallback(_createDesc.VariableName, agentInstanceContext.AgentInstanceId, createView.Update); + _statementContext.StatementStopService.StatementStopped += () => _services.VariableService.UnregisterCallback(_createDesc.VariableName, 0, createView.Update); + + // Create result set processor, use wildcard selection + _statementSpec.SelectClauseSpec.SetSelectExprList(new SelectClauseElementWildcard()); + _statementSpec.SelectStreamDirEnum = SelectClauseStreamSelectorEnum.RSTREAM_ISTREAM_BOTH; + var typeService = new StreamTypeServiceImpl(new EventType[] {createView.EventType}, new string[] {"create_variable"}, new bool[] {true}, _services.EngineURI, false); + OutputProcessViewBase outputViewBase; + try { + ResultSetProcessorFactoryDesc resultSetProcessorPrototype = ResultSetProcessorFactoryFactory.GetProcessorPrototype( + _statementSpec, _statementContext, typeService, null, new bool[0], true, ContextPropertyRegistryImpl.EMPTY_REGISTRY, null, _services.ConfigSnapshot, _services.ResultSetProcessorHelperFactory, false, false); + ResultSetProcessor resultSetProcessor = EPStatementStartMethodHelperAssignExpr.GetAssignResultSetProcessor(agentInstanceContext, resultSetProcessorPrototype, false, null, false); + + // Attach output view + OutputProcessViewFactory outputViewFactory = OutputProcessViewFactoryFactory.Make( + _statementSpec, + _services.InternalEventRouter, + agentInstanceContext.StatementContext, + resultSetProcessor.ResultEventType, null, + _services.TableService, + resultSetProcessorPrototype.ResultSetProcessorFactory.ResultSetProcessorType, + _services.ResultSetProcessorHelperFactory, + _services.StatementVariableRefService); + outputViewBase = outputViewFactory.MakeView(resultSetProcessor, agentInstanceContext); + createView.AddView(outputViewBase); + } + catch (ExprValidationException ex) + { + throw new EPException("Unexpected exception in create-variable context allocation: " + ex.Message, ex); + } + + return new StatementAgentInstanceFactoryCreateVariableResult(outputViewBase, stopCallback, agentInstanceContext); + } + + public override void AssignExpressions(StatementAgentInstanceFactoryResult result) + { + } + + public override void UnassignExpressions() + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryCreateVariableResult.cs b/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryCreateVariableResult.cs new file mode 100755 index 000000000..974e4a55a --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryCreateVariableResult.cs @@ -0,0 +1,35 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.subselect; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.prev; +using com.espertech.esper.epl.expression.prior; +using com.espertech.esper.epl.expression.subquery; +using com.espertech.esper.epl.expression.table; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.context.factory +{ + public class StatementAgentInstanceFactoryCreateVariableResult : StatementAgentInstanceFactoryResult + { + public StatementAgentInstanceFactoryCreateVariableResult(Viewable finalView, StopCallback stopCallback, AgentInstanceContext agentInstanceContext) + : base(finalView, stopCallback, agentInstanceContext, null, + Collections.GetEmptyMap(), + Collections.GetEmptyMap(), + Collections.GetEmptyMap(), + null, + Collections.GetEmptyMap(), + Collections.GetEmptyList()) + { + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryCreateWindow.cs b/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryCreateWindow.cs new file mode 100755 index 000000000..9a523d9fc --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryCreateWindow.cs @@ -0,0 +1,229 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.activator; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.core.start; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.named; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.view; +using com.espertech.esper.epl.virtualdw; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.context.factory +{ + public class StatementAgentInstanceFactoryCreateWindow : StatementAgentInstanceFactoryBase + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly StatementContext _statementContext; + private readonly StatementSpecCompiled _statementSpec; + private readonly EPServicesContext _services; + private readonly ViewableActivator _activator; + private readonly ViewFactoryChain _unmaterializedViewChain; + private readonly ResultSetProcessorFactoryDesc _resultSetProcessorPrototype; + private readonly OutputProcessViewFactory _outputProcessViewFactory; + private readonly bool _isRecoveringStatement; + + public StatementAgentInstanceFactoryCreateWindow(StatementContext statementContext, StatementSpecCompiled statementSpec, EPServicesContext services, ViewableActivator activator, ViewFactoryChain unmaterializedViewChain, ResultSetProcessorFactoryDesc resultSetProcessorPrototype, OutputProcessViewFactory outputProcessViewFactory, bool recoveringStatement) + : base(statementContext.Annotations) + { + _statementContext = statementContext; + _statementSpec = statementSpec; + _services = services; + _activator = activator; + _unmaterializedViewChain = unmaterializedViewChain; + _resultSetProcessorPrototype = resultSetProcessorPrototype; + _outputProcessViewFactory = outputProcessViewFactory; + _isRecoveringStatement = recoveringStatement; + } + + protected override StatementAgentInstanceFactoryResult NewContextInternal(AgentInstanceContext agentInstanceContext, bool isRecoveringResilient) + { + var stopCallbacks = new List(); + + String windowName = _statementSpec.CreateWindowDesc.WindowName; + Viewable finalView; + Viewable eventStreamParentViewable; + StatementAgentInstancePostLoad postLoad; + Viewable topView; + NamedWindowProcessorInstance processorInstance; + ViewableActivationResult viewableActivationResult; + + try + { + // Register interest + viewableActivationResult = _activator.Activate(agentInstanceContext, false, isRecoveringResilient); + stopCallbacks.Add(viewableActivationResult.StopCallback); + eventStreamParentViewable = viewableActivationResult.Viewable; + + // Obtain processor for this named window + var processor = _services.NamedWindowMgmtService.GetProcessor(windowName); + if (processor == null) + { + throw new Exception("Failed to obtain named window processor for named window '" + windowName + "'"); + } + + // Allocate processor instance + processorInstance = processor.AddInstance(agentInstanceContext); + var rootView = processorInstance.RootViewInstance; + eventStreamParentViewable.AddView(rootView); + + // Materialize views + var viewFactoryChainContext = new AgentInstanceViewFactoryChainContext(agentInstanceContext, true, null, null); + var createResult = _services.ViewService.CreateViews(rootView, _unmaterializedViewChain.FactoryChain, viewFactoryChainContext, false); + topView = createResult.TopViewable; + finalView = createResult.FinalViewable; + + // add views to stop callback if applicable + StatementAgentInstanceFactorySelect.AddViewStopCallback(stopCallbacks, createResult.NewViews); + + // If this is a virtual data window implementation, bind it to the context for easy lookup + StopCallback envStopCallback = null; + if (finalView is VirtualDWView) + { + var objectName = "/virtualdw/" + windowName; + var virtualDWView = (VirtualDWView)finalView; + _services.EngineEnvContext.Bind(objectName, virtualDWView.VirtualDataWindow); + envStopCallback = new ProxyStopCallback(() => + { + virtualDWView.Dispose(); + _services.EngineEnvContext.Unbind(objectName); + }); + } + StopCallback environmentStopCallback = envStopCallback; + + // Only if we are context-allocated: destroy the instance + var contextName = processor.ContextName; + var agentInstanceId = agentInstanceContext.AgentInstanceId; + var allInOneStopMethod = new ProxyStopCallback(() => + { + var windowNameX = _statementSpec.CreateWindowDesc.WindowName; + var processorX = _services.NamedWindowMgmtService.GetProcessor(windowNameX); + if (processorX == null) + { + Log.Warn("Named window processor by name '" + windowNameX + "' has not been found"); + } + else + { + NamedWindowProcessorInstance instance = + processorX.GetProcessorInstanceAllowUnpartitioned(agentInstanceId); + if (instance != null) + { + if (contextName != null) + { + instance.Dispose(); + } + else + { + instance.Stop(); + } + } + } + if (environmentStopCallback != null) + { + environmentStopCallback.Stop(); + } + }); + + stopCallbacks.Add(allInOneStopMethod); + + // Attach tail view + NamedWindowTailViewInstance tailView = processorInstance.TailViewInstance; + finalView.AddView(tailView); + finalView = tailView; + + // obtain result set processor + ResultSetProcessor resultSetProcessor = EPStatementStartMethodHelperAssignExpr.GetAssignResultSetProcessor(agentInstanceContext, _resultSetProcessorPrototype, false, null, false); + + // Attach output view + View outputView = _outputProcessViewFactory.MakeView(resultSetProcessor, agentInstanceContext); + finalView.AddView(outputView); + finalView = outputView; + + // obtain post load + postLoad = processorInstance.PostLoad; + + // Handle insert case + if (_statementSpec.CreateWindowDesc.IsInsert && !_isRecoveringStatement) + { + String insertFromWindow = _statementSpec.CreateWindowDesc.InsertFromWindow; + NamedWindowProcessor namedWindowProcessor = _services.NamedWindowMgmtService.GetProcessor(insertFromWindow); + NamedWindowProcessorInstance sourceWindowInstances = namedWindowProcessor.GetProcessorInstance(agentInstanceContext); + IList events = new List(); + if (_statementSpec.CreateWindowDesc.InsertFilter != null) + { + var eventsPerStream = new EventBean[1]; + var filter = _statementSpec.CreateWindowDesc.InsertFilter.ExprEvaluator; + for (IEnumerator it = sourceWindowInstances.TailViewInstance.GetEnumerator(); it.MoveNext(); ) + { + var candidate = it.Current; + eventsPerStream[0] = candidate; + var result = filter.Evaluate(new EvaluateParams(eventsPerStream, true, agentInstanceContext)); + if ((result == null) || (false.Equals(result))) + { + continue; + } + events.Add(candidate); + } + } + else + { + for (IEnumerator it = sourceWindowInstances.TailViewInstance.GetEnumerator(); it.MoveNext(); ) + { + events.Add(it.Current); + } + } + if (events.Count > 0) + { + EventType rootViewType = rootView.EventType; + EventBean[] convertedEvents = _services.EventAdapterService.TypeCast(events, rootViewType); + rootView.Update(convertedEvents, null); + } + } + } + catch (Exception) + { + StopCallback callback = StatementAgentInstanceUtil.GetStopCallback(stopCallbacks, agentInstanceContext); + StatementAgentInstanceUtil.StopSafe(callback, _statementContext); + throw; + } + + var createWindowResult = new StatementAgentInstanceFactoryCreateWindowResult( + finalView, null, agentInstanceContext, eventStreamParentViewable, postLoad, topView, processorInstance, viewableActivationResult); + if (_statementContext.StatementExtensionServicesContext != null) + { + _statementContext.StatementExtensionServicesContext.ContributeStopCallback(createWindowResult, stopCallbacks); + } + + Log.Debug(".start Statement start completed"); + StopCallback stopCallback = StatementAgentInstanceUtil.GetStopCallback(stopCallbacks, agentInstanceContext); + createWindowResult.StopCallback = stopCallback; + + return createWindowResult; + } + + public override void AssignExpressions(StatementAgentInstanceFactoryResult result) + { + } + + public override void UnassignExpressions() + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryCreateWindowResult.cs b/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryCreateWindowResult.cs new file mode 100755 index 000000000..928aa04bc --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryCreateWindowResult.cs @@ -0,0 +1,52 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.activator; +using com.espertech.esper.core.context.subselect; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.prev; +using com.espertech.esper.epl.expression.prior; +using com.espertech.esper.epl.expression.subquery; +using com.espertech.esper.epl.expression.table; +using com.espertech.esper.epl.named; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.context.factory +{ + public class StatementAgentInstanceFactoryCreateWindowResult : StatementAgentInstanceFactoryResult + { + public StatementAgentInstanceFactoryCreateWindowResult(Viewable finalView, StopCallback stopCallback, AgentInstanceContext agentInstanceContext, Viewable eventStreamParentViewable, StatementAgentInstancePostLoad postLoad, Viewable topView, NamedWindowProcessorInstance processorInstance, ViewableActivationResult viewableActivationResult) + : base(finalView, stopCallback, agentInstanceContext, null, + Collections.GetEmptyMap(), + Collections.GetEmptyMap(), + Collections.GetEmptyMap(), + null, + Collections.GetEmptyMap(), + Collections.GetEmptyList()) + { + EventStreamParentViewable = eventStreamParentViewable; + PostLoad = postLoad; + TopView = topView; + ProcessorInstance = processorInstance; + ViewableActivationResult = viewableActivationResult; + } + + public Viewable EventStreamParentViewable { get; private set; } + + public StatementAgentInstancePostLoad PostLoad { get; private set; } + + public Viewable TopView { get; private set; } + + public NamedWindowProcessorInstance ProcessorInstance { get; private set; } + + public ViewableActivationResult ViewableActivationResult { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryNoAgentInstance.cs b/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryNoAgentInstance.cs new file mode 100755 index 000000000..85ec0d126 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryNoAgentInstance.cs @@ -0,0 +1,43 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.context.factory +{ + public class StatementAgentInstanceFactoryNoAgentInstance : StatementAgentInstanceFactory + { + private readonly Viewable _sharedFinalView; + + public StatementAgentInstanceFactoryNoAgentInstance(Viewable sharedFinalView) + { + _sharedFinalView = sharedFinalView; + } + + public StatementAgentInstanceFactoryResult NewContext(AgentInstanceContext agentInstanceContext, bool isRecoveringResilient) + { + return new StatementAgentInstanceFactoryCreateSchemaResult(_sharedFinalView, CollectionUtil.STOP_CALLBACK_NONE, agentInstanceContext); + } + + public void AssignExpressions(StatementAgentInstanceFactoryResult result) + { + } + + public void UnassignExpressions() + { + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryOnTriggerBase.cs b/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryOnTriggerBase.cs new file mode 100755 index 000000000..a86735038 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryOnTriggerBase.cs @@ -0,0 +1,142 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.activator; +using com.espertech.esper.core.context.subselect; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.core.start; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.subquery; +using com.espertech.esper.epl.expression.table; +using com.espertech.esper.epl.spec; +using com.espertech.esper.pattern; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.context.factory +{ + public abstract class StatementAgentInstanceFactoryOnTriggerBase : StatementAgentInstanceFactory + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + protected readonly StatementContext StatementContext; + protected readonly StatementSpecCompiled StatementSpec; + protected readonly EPServicesContext Services; + private readonly ViewableActivator _activator; + private readonly SubSelectStrategyCollection _subSelectStrategyCollection; + + public abstract OnExprViewResult DetermineOnExprView( + AgentInstanceContext agentInstanceContext, + IList stopCallbacks, + bool isRecoveringResilient); + + public abstract View DetermineFinalOutputView(AgentInstanceContext agentInstanceContext, View onExprView); + + public StatementAgentInstanceFactoryOnTriggerBase( + StatementContext statementContext, + StatementSpecCompiled statementSpec, + EPServicesContext services, + ViewableActivator activator, + SubSelectStrategyCollection subSelectStrategyCollection) + { + StatementContext = statementContext; + StatementSpec = statementSpec; + Services = services; + _activator = activator; + _subSelectStrategyCollection = subSelectStrategyCollection; + } + + public StatementAgentInstanceFactoryResult NewContext( + AgentInstanceContext agentInstanceContext, + bool isRecoveringResilient) + { + IList stopCallbacks = new List(); + View view; + IDictionary subselectStrategies; + AggregationService aggregationService; + EvalRootState optPatternRoot; + IDictionary tableAccessStrategies; + ViewableActivationResult activationResult; + + StopCallback stopCallback; + try + { + var onExprViewResult = DetermineOnExprView(agentInstanceContext, stopCallbacks, isRecoveringResilient); + + view = onExprViewResult.OnExprView; + aggregationService = onExprViewResult.OptionalAggregationService; + + // attach stream to view + activationResult = _activator.Activate(agentInstanceContext, false, isRecoveringResilient); + activationResult.Viewable.AddView(view); + stopCallbacks.Add(activationResult.StopCallback); + optPatternRoot = activationResult.OptionalPatternRoot; + + // determine final output view + view = DetermineFinalOutputView(agentInstanceContext, view); + + // start subselects + subselectStrategies = EPStatementStartMethodHelperSubselect.StartSubselects( + Services, _subSelectStrategyCollection, agentInstanceContext, stopCallbacks, isRecoveringResilient); + + // plan table access + tableAccessStrategies = EPStatementStartMethodHelperTableAccess.AttachTableAccess( + Services, agentInstanceContext, StatementSpec.TableNodes); + } + catch (Exception) + { + stopCallback = StatementAgentInstanceUtil.GetStopCallback(stopCallbacks, agentInstanceContext); + StatementAgentInstanceUtil.StopSafe(stopCallback, StatementContext); + throw; + } + + StatementAgentInstanceFactoryOnTriggerResult onTriggerResult = + new StatementAgentInstanceFactoryOnTriggerResult( + view, null, agentInstanceContext, aggregationService, subselectStrategies, optPatternRoot, + tableAccessStrategies, activationResult); + if (StatementContext.StatementExtensionServicesContext != null) + { + StatementContext.StatementExtensionServicesContext.ContributeStopCallback( + onTriggerResult, stopCallbacks); + } + + Log.Debug(".start Statement start completed"); + stopCallback = StatementAgentInstanceUtil.GetStopCallback(stopCallbacks, agentInstanceContext); + onTriggerResult.StopCallback = stopCallback; + + return onTriggerResult; + } + + public virtual void AssignExpressions(StatementAgentInstanceFactoryResult result) + { + } + + public virtual void UnassignExpressions() + { + } + + public class OnExprViewResult + { + public OnExprViewResult(View onExprView, AggregationService optionalAggregationService) + { + OnExprView = onExprView; + OptionalAggregationService = optionalAggregationService; + } + + public View OnExprView { get; private set; } + + public AggregationService OptionalAggregationService { get; private set; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryOnTriggerNamedWindow.cs b/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryOnTriggerNamedWindow.cs new file mode 100755 index 000000000..1e65cb193 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryOnTriggerNamedWindow.cs @@ -0,0 +1,152 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.activator; +using com.espertech.esper.core.context.subselect; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.core.start; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.@join.hint; +using com.espertech.esper.epl.@join.table; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.epl.named; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.view; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.context.factory +{ + public class StatementAgentInstanceFactoryOnTriggerNamedWindow : StatementAgentInstanceFactoryOnTriggerBase + { + private readonly ResultSetProcessorFactoryDesc _resultSetProcessorPrototype; + private readonly ResultSetProcessorFactoryDesc _outputResultSetProcessorPrototype; + private readonly NamedWindowOnExprFactory _onExprFactory; + private readonly OutputProcessViewFactory _outputProcessViewFactory; + private readonly NamedWindowProcessor _processor; + + private readonly SubordinateWMatchExprQueryPlanResult _queryPlan; + + public StatementAgentInstanceFactoryOnTriggerNamedWindow(StatementContext statementContext, StatementSpecCompiled statementSpec, EPServicesContext services, ViewableActivator activator, SubSelectStrategyCollection subSelectStrategyCollection, ResultSetProcessorFactoryDesc resultSetProcessorPrototype, ExprNode validatedJoin, ResultSetProcessorFactoryDesc outputResultSetProcessorPrototype, NamedWindowOnExprFactory onExprFactory, OutputProcessViewFactory outputProcessViewFactory, EventType activatorResultEventType, NamedWindowProcessor processor, IList stopCallbacks) + : base(statementContext, statementSpec, services, activator, subSelectStrategyCollection) + { + _resultSetProcessorPrototype = resultSetProcessorPrototype; + _outputResultSetProcessorPrototype = outputResultSetProcessorPrototype; + _onExprFactory = onExprFactory; + _outputProcessViewFactory = outputProcessViewFactory; + _processor = processor; + + IndexHintPair pair = GetIndexHintPair(statementContext, statementSpec); + IndexHint indexHint = pair.IndexHint; + ExcludePlanHint excludePlanHint = pair.ExcludePlanHint; + + _queryPlan = SubordinateQueryPlanner.PlanOnExpression( + validatedJoin, activatorResultEventType, indexHint, processor.IsEnableSubqueryIndexShare, -1, excludePlanHint, + processor.IsVirtualDataWindow, processor.EventTableIndexMetadataRepo, processor.NamedWindowType, + processor.OptionalUniqueKeyProps, false, statementContext.StatementName, statementContext.StatementId, statementContext.Annotations); + if (_queryPlan.IndexDescs != null) + { + SubordinateQueryPlannerUtil.AddIndexMetaAndRef(_queryPlan.IndexDescs, processor.EventTableIndexMetadataRepo, statementContext.StatementName); + stopCallbacks.Add(new ProxyStopCallback(() => + { + for (int i = 0; i < _queryPlan.IndexDescs.Length; i++) + { + bool last = processor.EventTableIndexMetadataRepo.RemoveIndexReference(_queryPlan.IndexDescs[i].IndexMultiKey, statementContext.StatementName); + if (last) + { + processor.EventTableIndexMetadataRepo.RemoveIndex(_queryPlan.IndexDescs[i].IndexMultiKey); + processor.RemoveAllInstanceIndexes(_queryPlan.IndexDescs[i].IndexMultiKey); + } + } + })); + } + SubordinateQueryPlannerUtil.QueryPlanLogOnExpr(processor.RootView.IsQueryPlanLogging, NamedWindowRootView.QueryPlanLog, + _queryPlan, statementContext.Annotations, statementContext.EngineImportService); + } + + public override OnExprViewResult DetermineOnExprView(AgentInstanceContext agentInstanceContext, IList stopCallbacks, bool isRecoveringResilient) + { + // get result set processor and aggregation services + Pair pair = EPStatementStartMethodHelperUtil.StartResultSetAndAggregation(_resultSetProcessorPrototype, agentInstanceContext, false, null); + + // get named window processor instance + NamedWindowProcessorInstance processorInstance = _processor.GetProcessorInstance(agentInstanceContext); + + // obtain on-expr view + EventTable[] indexes = null; + if (_queryPlan.IndexDescs != null) + { + indexes = SubordinateQueryPlannerUtil.RealizeTables(_queryPlan.IndexDescs, _processor.NamedWindowType, processorInstance.RootViewInstance.IndexRepository, processorInstance.RootViewInstance.DataWindowContents, processorInstance.TailViewInstance.AgentInstanceContext, isRecoveringResilient); + } + SubordWMatchExprLookupStrategy strategy = _queryPlan.Factory.Realize(indexes, agentInstanceContext, processorInstance.RootViewInstance.DataWindowContents, processorInstance.RootViewInstance.VirtualDataWindow); + NamedWindowOnExprBaseView onExprBaseView = _onExprFactory.Make(strategy, processorInstance.RootViewInstance, agentInstanceContext, pair.First); + + return new OnExprViewResult(onExprBaseView, pair.Second); + } + + public override void AssignExpressions(StatementAgentInstanceFactoryResult result) + { + } + + public override void UnassignExpressions() + { + } + + public override View DetermineFinalOutputView(AgentInstanceContext agentInstanceContext, View onExprView) + { + if ((StatementSpec.OnTriggerDesc.OnTriggerType == OnTriggerType.ON_DELETE) || + (StatementSpec.OnTriggerDesc.OnTriggerType == OnTriggerType.ON_UPDATE) || + (StatementSpec.OnTriggerDesc.OnTriggerType == OnTriggerType.ON_MERGE)) + { + + ResultSetProcessor outputResultSetProcessor = _outputResultSetProcessorPrototype.ResultSetProcessorFactory.Instantiate(null, null, agentInstanceContext); + View outputView = _outputProcessViewFactory.MakeView(outputResultSetProcessor, agentInstanceContext); + onExprView.AddView(outputView); + return outputView; + } + + return onExprView; + } + + internal static IndexHintPair GetIndexHintPair(StatementContext statementContext, StatementSpecCompiled statementSpec) + { + IndexHint indexHint = IndexHint.GetIndexHint(statementContext.Annotations); + ExcludePlanHint excludePlanHint = null; + if (statementSpec.OnTriggerDesc is OnTriggerWindowDesc) + { + OnTriggerWindowDesc onTriggerWindowDesc = (OnTriggerWindowDesc)statementSpec.OnTriggerDesc; + string[] streamNames = { onTriggerWindowDesc.OptionalAsName, statementSpec.StreamSpecs[0].OptionalStreamName }; + excludePlanHint = ExcludePlanHint.GetHint(streamNames, statementContext); + } + return new IndexHintPair(indexHint, excludePlanHint); + } + + public class IndexHintPair + { + public IndexHintPair(IndexHint indexHint, ExcludePlanHint excludePlanHint) + { + IndexHint = indexHint; + ExcludePlanHint = excludePlanHint; + } + + public IndexHint IndexHint { get; private set; } + + public ExcludePlanHint ExcludePlanHint { get; private set; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryOnTriggerResult.cs b/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryOnTriggerResult.cs new file mode 100755 index 000000000..62ce8f16a --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryOnTriggerResult.cs @@ -0,0 +1,56 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.activator; +using com.espertech.esper.core.context.subselect; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.prev; +using com.espertech.esper.epl.expression.prior; +using com.espertech.esper.epl.expression.subquery; +using com.espertech.esper.epl.expression.table; +using com.espertech.esper.pattern; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.context.factory +{ + public class StatementAgentInstanceFactoryOnTriggerResult : StatementAgentInstanceFactoryResult + { + public StatementAgentInstanceFactoryOnTriggerResult( + Viewable finalView, + StopCallback stopCallback, + AgentInstanceContext agentInstanceContext, + AggregationService aggregationService, + IDictionary subselectStrategies, + EvalRootState optPatternRoot, + IDictionary tableAccessStrategies, + ViewableActivationResult viewableActivationResult) : base( + finalView, + stopCallback, + agentInstanceContext, + aggregationService, + subselectStrategies, + Collections.GetEmptyMap(), + Collections.GetEmptyMap(), + null, + tableAccessStrategies, + Collections.GetEmptyList()) + { + OptPatternRoot = optPatternRoot; + ViewableActivationResult = viewableActivationResult; + } + + public EvalRootState OptPatternRoot { get; private set; } + + public ViewableActivationResult ViewableActivationResult { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryOnTriggerSetVariable.cs b/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryOnTriggerSetVariable.cs new file mode 100755 index 000000000..e04ae66a7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryOnTriggerSetVariable.cs @@ -0,0 +1,55 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.activator; +using com.espertech.esper.core.context.subselect; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.variable; +using com.espertech.esper.epl.view; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.context.factory +{ + public class StatementAgentInstanceFactoryOnTriggerSetVariable : StatementAgentInstanceFactoryOnTriggerBase + { + private readonly OnSetVariableViewFactory _onSetVariableViewFactory; + private readonly ResultSetProcessorFactoryDesc _outputResultSetProcessorPrototype; + private readonly OutputProcessViewFactory _outputProcessViewFactory; + + public StatementAgentInstanceFactoryOnTriggerSetVariable(StatementContext statementContext, StatementSpecCompiled statementSpec, EPServicesContext services, ViewableActivator activator, SubSelectStrategyCollection subSelectStrategyCollection, OnSetVariableViewFactory onSetVariableViewFactory, ResultSetProcessorFactoryDesc outputResultSetProcessorPrototype, OutputProcessViewFactory outputProcessViewFactory) + : base(statementContext, statementSpec, services, activator, subSelectStrategyCollection) + { + _onSetVariableViewFactory = onSetVariableViewFactory; + _outputResultSetProcessorPrototype = outputResultSetProcessorPrototype; + _outputProcessViewFactory = outputProcessViewFactory; + } + + public override OnExprViewResult DetermineOnExprView(AgentInstanceContext agentInstanceContext, IList stopCallbacks, bool isRecoveringResilient) + { + OnSetVariableView view = _onSetVariableViewFactory.Instantiate(agentInstanceContext); + return new OnExprViewResult(view, null); + } + + public override View DetermineFinalOutputView(AgentInstanceContext agentInstanceContext, View onExprView) + { + ResultSetProcessor outputResultSetProcessor = _outputResultSetProcessorPrototype.ResultSetProcessorFactory.Instantiate(null, null, agentInstanceContext); + View outputView = _outputProcessViewFactory.MakeView(outputResultSetProcessor, agentInstanceContext); + onExprView.AddView(outputView); + return outputView; + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryOnTriggerSplit.cs b/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryOnTriggerSplit.cs new file mode 100755 index 000000000..d7d383286 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryOnTriggerSplit.cs @@ -0,0 +1,89 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.core.context.activator; +using com.espertech.esper.core.context.subselect; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.core.start; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.util; +using com.espertech.esper.view; +using com.espertech.esper.view.internals; + +namespace com.espertech.esper.core.context.factory +{ + public class StatementAgentInstanceFactoryOnTriggerSplit : StatementAgentInstanceFactoryOnTriggerBase + { + private readonly EPStatementStartMethodOnTriggerItem[] _items; + private readonly EventType _activatorResultEventType; + + public StatementAgentInstanceFactoryOnTriggerSplit( + StatementContext statementContext, + StatementSpecCompiled statementSpec, + EPServicesContext services, + ViewableActivator activator, + SubSelectStrategyCollection subSelectStrategyCollection, + EPStatementStartMethodOnTriggerItem[] items, + EventType activatorResultEventType) + : base(statementContext, statementSpec, services, activator, subSelectStrategyCollection) + { + _items = items; + _activatorResultEventType = activatorResultEventType; + } + + public override OnExprViewResult DetermineOnExprView( + AgentInstanceContext agentInstanceContext, + IList stopCallbacks, + bool isRecoveringReslient) + { + var processors = new ResultSetProcessor[_items.Length]; + for (int i = 0; i < processors.Length; i++) + { + ResultSetProcessorFactoryDesc factory = _items[i].FactoryDesc; + ResultSetProcessor processor = factory.ResultSetProcessorFactory.Instantiate( + null, null, agentInstanceContext); + processors[i] = processor; + } + + var tableStateInstances = new TableStateInstance[processors.Length]; + for (int i = 0; i < _items.Length; i++) + { + string tableName = _items[i].InsertIntoTableNames; + if (tableName != null) + { + tableStateInstances[i] = agentInstanceContext.StatementContext.TableService.GetState( + tableName, agentInstanceContext.AgentInstanceId); + } + } + + var whereClauseEvals = new ExprEvaluator[_items.Length]; + for (int i = 0; i < _items.Length; i++) + { + whereClauseEvals[i] = _items[i].WhereClause == null ? null : _items[i].WhereClause.ExprEvaluator; + } + + var desc = (OnTriggerSplitStreamDesc) base.StatementSpec.OnTriggerDesc; + var view = new RouteResultView( + desc.IsFirst, _activatorResultEventType, base.StatementContext.EpStatementHandle, base.Services.InternalEventRouter, + tableStateInstances, _items, processors, whereClauseEvals, agentInstanceContext); + return new OnExprViewResult(view, null); + } + + public override View DetermineFinalOutputView(AgentInstanceContext agentInstanceContext, View onExprView) + { + return onExprView; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryOnTriggerTable.cs b/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryOnTriggerTable.cs new file mode 100755 index 000000000..615a465b7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryOnTriggerTable.cs @@ -0,0 +1,108 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.core.context.activator; +using com.espertech.esper.core.context.subselect; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.core.start; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.@join.table; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.epl.table.onaction; +using com.espertech.esper.epl.view; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.context.factory +{ + public class StatementAgentInstanceFactoryOnTriggerTable : StatementAgentInstanceFactoryOnTriggerBase + { + private readonly ResultSetProcessorFactoryDesc _resultSetProcessorPrototype; + private readonly ResultSetProcessorFactoryDesc _outputResultSetProcessorPrototype; + private readonly OutputProcessViewFactory _outputProcessViewFactory; + private readonly TableOnViewFactory _onExprFactory; + private readonly SubordinateWMatchExprQueryPlanResult _queryPlanResult; + + public StatementAgentInstanceFactoryOnTriggerTable(StatementContext statementContext, StatementSpecCompiled statementSpec, EPServicesContext services, ViewableActivator activator, SubSelectStrategyCollection subSelectStrategyCollection, ResultSetProcessorFactoryDesc resultSetProcessorPrototype, ExprNode validatedJoin, TableOnViewFactory onExprFactory, EventType activatorResultEventType, TableMetadata tableMetadata, ResultSetProcessorFactoryDesc outputResultSetProcessorPrototype, OutputProcessViewFactory outputProcessViewFactory) + : base(statementContext, statementSpec, services, activator, subSelectStrategyCollection) + { + _resultSetProcessorPrototype = resultSetProcessorPrototype; + _onExprFactory = onExprFactory; + _outputResultSetProcessorPrototype = outputResultSetProcessorPrototype; + _outputProcessViewFactory = outputProcessViewFactory; + + var pair = StatementAgentInstanceFactoryOnTriggerNamedWindow.GetIndexHintPair(statementContext, statementSpec); + var indexHint = pair.IndexHint; + var excludePlanHint = pair.ExcludePlanHint; + + _queryPlanResult = SubordinateQueryPlanner.PlanOnExpression( + validatedJoin, activatorResultEventType, indexHint, true, -1, excludePlanHint, + false, tableMetadata.EventTableIndexMetadataRepo, tableMetadata.InternalEventType, + tableMetadata.UniqueKeyProps, true, statementContext.StatementName, statementContext.StatementId, statementContext.Annotations); + if (_queryPlanResult.IndexDescs != null) + { + for (var i = 0; i < _queryPlanResult.IndexDescs.Length; i++) + { + tableMetadata.AddIndexReference(_queryPlanResult.IndexDescs[i].IndexName, statementContext.StatementName); + } + } + SubordinateQueryPlannerUtil.QueryPlanLogOnExpr(tableMetadata.IsQueryPlanLogging, TableServiceImpl.QueryPlanLog, + _queryPlanResult, statementContext.Annotations, statementContext.EngineImportService); + } + + public override OnExprViewResult DetermineOnExprView(AgentInstanceContext agentInstanceContext, IList stopCallbacks, bool isRecoveringReslient) + { + var onTriggerWindowDesc = (OnTriggerWindowDesc)StatementSpec.OnTriggerDesc; + + // get result set processor and aggregation services + var pair = EPStatementStartMethodHelperUtil.StartResultSetAndAggregation(_resultSetProcessorPrototype, agentInstanceContext, false, null); + + var state = Services.TableService.GetState(onTriggerWindowDesc.WindowName, agentInstanceContext.AgentInstanceId); + EventTable[] indexes; + if (_queryPlanResult.IndexDescs == null) + { + indexes = null; + } + else + { + indexes = new EventTable[_queryPlanResult.IndexDescs.Length]; + for (var i = 0; i < indexes.Length; i++) + { + indexes[i] = state.IndexRepository.GetIndexByDesc(_queryPlanResult.IndexDescs[i].IndexMultiKey); + } + } + var strategy = _queryPlanResult.Factory.Realize(indexes, agentInstanceContext, state.IterableTableScan, null); + var onExprBaseView = _onExprFactory.Make(strategy, state, agentInstanceContext, pair.First); + + return new OnExprViewResult(onExprBaseView, pair.Second); + } + + public override View DetermineFinalOutputView(AgentInstanceContext agentInstanceContext, View onExprView) + { + if ((StatementSpec.OnTriggerDesc.OnTriggerType == OnTriggerType.ON_DELETE) || + (StatementSpec.OnTriggerDesc.OnTriggerType == OnTriggerType.ON_UPDATE) || + (StatementSpec.OnTriggerDesc.OnTriggerType == OnTriggerType.ON_MERGE)) + { + + var outputResultSetProcessor = _outputResultSetProcessorPrototype.ResultSetProcessorFactory.Instantiate(null, null, agentInstanceContext); + View outputView = _outputProcessViewFactory.MakeView(outputResultSetProcessor, agentInstanceContext); + onExprView.AddView(outputView); + return outputView; + } + + return onExprView; + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryResult.cs b/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryResult.cs new file mode 100755 index 000000000..16615918e --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryResult.cs @@ -0,0 +1,71 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.core.context.subselect; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.prev; +using com.espertech.esper.epl.expression.prior; +using com.espertech.esper.epl.expression.subquery; +using com.espertech.esper.epl.expression.table; +using com.espertech.esper.rowregex; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.context.factory +{ + public abstract class StatementAgentInstanceFactoryResult + { + protected StatementAgentInstanceFactoryResult( + Viewable finalView, + StopCallback stopCallback, + AgentInstanceContext agentInstanceContext, + AggregationService optionalAggegationService, + IDictionary subselectStrategies, + IDictionary priorNodeStrategies, + IDictionary previousNodeStrategies, + RegexExprPreviousEvalStrategy regexExprPreviousEvalStrategy, + IDictionary tableAccessStrategies, + IList preloadList) + { + FinalView = finalView; + StopCallback = stopCallback; + AgentInstanceContext = agentInstanceContext; + OptionalAggegationService = optionalAggegationService; + SubselectStrategies = subselectStrategies; + PriorNodeStrategies = priorNodeStrategies; + PreviousNodeStrategies = previousNodeStrategies; + RegexExprPreviousEvalStrategy = regexExprPreviousEvalStrategy; + TableAccessEvalStrategies = tableAccessStrategies; + PreloadList = preloadList; + } + + public Viewable FinalView { get; private set; } + + public StopCallback StopCallback { get; set; } + + public AgentInstanceContext AgentInstanceContext { get; private set; } + + public AggregationService OptionalAggegationService { get; private set; } + + public IDictionary SubselectStrategies { get; private set; } + + public IDictionary PriorNodeStrategies { get; private set; } + + public IDictionary PreviousNodeStrategies { get; private set; } + + public IList PreloadList { get; private set; } + + public RegexExprPreviousEvalStrategy RegexExprPreviousEvalStrategy { get; private set; } + + public IDictionary TableAccessEvalStrategies { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactorySelect.cs b/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactorySelect.cs new file mode 100755 index 000000000..ef8cdddf7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactorySelect.cs @@ -0,0 +1,641 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.activator; +using com.espertech.esper.core.context.subselect; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.core.start; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.prev; +using com.espertech.esper.epl.expression.prior; +using com.espertech.esper.epl.expression.subquery; +using com.espertech.esper.epl.expression.table; +using com.espertech.esper.epl.@join.@base; +using com.espertech.esper.epl.named; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.view; +using com.espertech.esper.filter; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.pattern; +using com.espertech.esper.rowregex; +using com.espertech.esper.util; +using com.espertech.esper.view; +using com.espertech.esper.view.internals; + +namespace com.espertech.esper.core.context.factory +{ + public class StatementAgentInstanceFactorySelect : StatementAgentInstanceFactoryBase + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly int _numStreams; + private readonly ViewableActivator[] _eventStreamParentViewableActivators; + private readonly StatementContext _statementContext; + private readonly StatementSpecCompiled _statementSpec; + private readonly EPServicesContext _services; + private readonly StreamTypeService _typeService; + private readonly ViewFactoryChain[] _unmaterializedViewChain; + private readonly ResultSetProcessorFactoryDesc _resultSetProcessorFactoryDesc; + private readonly StreamJoinAnalysisResult _joinAnalysisResult; + private readonly JoinSetComposerPrototype _joinSetComposerPrototype; + private readonly SubSelectStrategyCollection _subSelectStrategyCollection; + private readonly ViewResourceDelegateVerified _viewResourceDelegate; + private readonly OutputProcessViewFactory _outputProcessViewFactory; + + public StatementAgentInstanceFactorySelect( + int numStreams, + ViewableActivator[] eventStreamParentViewableActivators, + StatementContext statementContext, + StatementSpecCompiled statementSpec, + EPServicesContext services, + StreamTypeService typeService, + ViewFactoryChain[] unmaterializedViewChain, + ResultSetProcessorFactoryDesc resultSetProcessorFactoryDesc, + StreamJoinAnalysisResult joinAnalysisResult, + bool recoveringResilient, + JoinSetComposerPrototype joinSetComposerPrototype, + SubSelectStrategyCollection subSelectStrategyCollection, + ViewResourceDelegateVerified viewResourceDelegate, + OutputProcessViewFactory outputProcessViewFactory) : + base(statementSpec.Annotations) + { + _numStreams = numStreams; + _eventStreamParentViewableActivators = eventStreamParentViewableActivators; + _statementContext = statementContext; + _statementSpec = statementSpec; + _services = services; + _typeService = typeService; + _unmaterializedViewChain = unmaterializedViewChain; + _resultSetProcessorFactoryDesc = resultSetProcessorFactoryDesc; + _joinAnalysisResult = joinAnalysisResult; + _joinSetComposerPrototype = joinSetComposerPrototype; + _subSelectStrategyCollection = subSelectStrategyCollection; + _viewResourceDelegate = viewResourceDelegate; + _outputProcessViewFactory = outputProcessViewFactory; + } + + public ViewResourceDelegateVerified ViewResourceDelegate + { + get { return _viewResourceDelegate; } + } + + protected override StatementAgentInstanceFactoryResult NewContextInternal(AgentInstanceContext agentInstanceContext, bool isRecoveringResilient) + { + IList stopCallbacks = new List(2); + + Viewable finalView; + var viewableActivationResult = new ViewableActivationResult[_eventStreamParentViewableActivators.Length]; + IDictionary subselectStrategies; + AggregationService aggregationService; + Viewable[] streamViews; + Viewable[] eventStreamParentViewable; + Viewable[] topViews; + IDictionary priorNodeStrategies; + IDictionary previousNodeStrategies; + IDictionary tableAccessStrategies; + RegexExprPreviousEvalStrategy regexExprPreviousEvalStrategy = null; + IList preloadList = new List(); + EvalRootState[] patternRoots; + StatementAgentInstancePostLoad postLoadJoin = null; + var suppressSameEventMatches = false; + var discardPartialsOnMatch = false; + EvalRootMatchRemover evalRootMatchRemover = null; + + try + { + // create root viewables + eventStreamParentViewable = new Viewable[_numStreams]; + patternRoots = new EvalRootState[_numStreams]; + + for (var stream = 0; stream < _eventStreamParentViewableActivators.Length; stream++) + { + var activationResult = _eventStreamParentViewableActivators[stream].Activate(agentInstanceContext, false, isRecoveringResilient); + viewableActivationResult[stream] = activationResult; + stopCallbacks.Add(activationResult.StopCallback); + suppressSameEventMatches = activationResult.IsSuppressSameEventMatches; + discardPartialsOnMatch = activationResult.IsDiscardPartialsOnMatch; + + // add stop callback for any stream-level viewable when applicable + if (activationResult.Viewable is StopCallback) + { + stopCallbacks.Add((StopCallback)activationResult.Viewable); + } + + eventStreamParentViewable[stream] = activationResult.Viewable; + patternRoots[stream] = activationResult.OptionalPatternRoot; + if (stream == 0) + { + evalRootMatchRemover = activationResult.OptEvalRootMatchRemover; + } + + if (activationResult.OptionalLock != null) + { + agentInstanceContext.EpStatementAgentInstanceHandle.StatementAgentInstanceLock = activationResult.OptionalLock; + _statementContext.DefaultAgentInstanceLock = activationResult.OptionalLock; + } + } + + // compile view factories adding "prior" as necessary + var viewFactoryChains = new IList[_numStreams]; + for (var i = 0; i < _numStreams; i++) + { + var viewFactoryChain = _unmaterializedViewChain[i].FactoryChain; + + // add "prior" view factory + var hasPrior = _viewResourceDelegate.PerStream[i].PriorRequests != null && !_viewResourceDelegate.PerStream[i].PriorRequests.IsEmpty(); + if (hasPrior) + { + var priorEventViewFactory = EPStatementStartMethodHelperPrior.GetPriorEventViewFactory( + agentInstanceContext.StatementContext, i, viewFactoryChain.IsEmpty(), false, -1); + viewFactoryChain = new List(viewFactoryChain); + viewFactoryChain.Add(priorEventViewFactory); + } + viewFactoryChains[i] = viewFactoryChain; + } + + // create view factory chain context: holds stream-specific services + var viewFactoryChainContexts = new AgentInstanceViewFactoryChainContext[_numStreams]; + for (var i = 0; i < _numStreams; i++) + { + viewFactoryChainContexts[i] = AgentInstanceViewFactoryChainContext.Create(viewFactoryChains[i], agentInstanceContext, _viewResourceDelegate.PerStream[i]); + } + + // handle "prior" nodes and their strategies + priorNodeStrategies = EPStatementStartMethodHelperPrior.CompilePriorNodeStrategies(_viewResourceDelegate, viewFactoryChainContexts); + + // handle "previous" nodes and their strategies + previousNodeStrategies = EPStatementStartMethodHelperPrevious.CompilePreviousNodeStrategies(_viewResourceDelegate, viewFactoryChainContexts); + + // materialize views + streamViews = new Viewable[_numStreams]; + topViews = new Viewable[_numStreams]; + for (var i = 0; i < _numStreams; i++) + { + var hasPreviousNode = _viewResourceDelegate.PerStream[i].PreviousRequests != null && !_viewResourceDelegate.PerStream[i].PreviousRequests.IsEmpty(); + + var createResult = _services.ViewService.CreateViews(eventStreamParentViewable[i], viewFactoryChains[i], viewFactoryChainContexts[i], hasPreviousNode); + topViews[i] = createResult.TopViewable; + streamViews[i] = createResult.FinalViewable; + + var isReuseableView = _eventStreamParentViewableActivators[i] is ViewableActivatorStreamReuseView; + if (isReuseableView) + { + var viewsCreated = createResult.NewViews; + var stopCallback = new ProxyStopCallback(() => + { + ViewServiceHelper.RemoveFirstUnsharedView(viewsCreated); + }); + stopCallbacks.Add(stopCallback); + } + + // add views to stop callback if applicable + AddViewStopCallback(stopCallbacks, createResult.NewViews); + } + + // determine match-recognize "previous"-node strategy (none if not present, or one handling and number of nodes) + var matchRecognize = EventRowRegexHelper.RecursiveFindRegexService(topViews[0]); + if (matchRecognize != null) + { + regexExprPreviousEvalStrategy = matchRecognize.PreviousEvaluationStrategy; + stopCallbacks.Add(new ProxyStopCallback(matchRecognize.Stop)); + } + + // start subselects + subselectStrategies = EPStatementStartMethodHelperSubselect.StartSubselects(_services, _subSelectStrategyCollection, agentInstanceContext, stopCallbacks, isRecoveringResilient); + + // plan table access + tableAccessStrategies = EPStatementStartMethodHelperTableAccess.AttachTableAccess(_services, agentInstanceContext, _statementSpec.TableNodes); + + // obtain result set processor and aggregation services + var processorPair = EPStatementStartMethodHelperUtil.StartResultSetAndAggregation(_resultSetProcessorFactoryDesc, agentInstanceContext, false, null); + var resultSetProcessor = processorPair.First; + aggregationService = processorPair.Second; + stopCallbacks.Add(aggregationService); + stopCallbacks.Add(resultSetProcessor); + + // for just 1 event stream without joins, handle the one-table process separately. + JoinPreloadMethod joinPreloadMethod; + JoinSetComposerDesc joinSetComposer = null; + if (streamViews.Length == 1) + { + finalView = HandleSimpleSelect(streamViews[0], resultSetProcessor, agentInstanceContext, evalRootMatchRemover, suppressSameEventMatches, discardPartialsOnMatch); + joinPreloadMethod = null; + } + else + { + var joinPlanResult = HandleJoin(_typeService.StreamNames, streamViews, resultSetProcessor, + agentInstanceContext, stopCallbacks, _joinAnalysisResult, isRecoveringResilient); + finalView = joinPlanResult.Viewable; + joinPreloadMethod = joinPlanResult.PreloadMethod; + joinSetComposer = joinPlanResult.JoinSetComposerDesc; + } + + // for stoppable final views, add callback + if (finalView is StopCallback) + { + stopCallbacks.Add((StopCallback)finalView); + } + + // Replay any named window data, for later consumers of named data windows + if (_services.EventTableIndexService.AllowInitIndex(isRecoveringResilient)) + { + var hasNamedWindow = false; + var namedWindowPostloadFilters = new FilterSpecCompiled[_statementSpec.StreamSpecs.Length]; + var namedWindowTailViews = new NamedWindowTailViewInstance[_statementSpec.StreamSpecs.Length]; + var namedWindowFilters = new IList[_statementSpec.StreamSpecs.Length]; + + for (var i = 0; i < _statementSpec.StreamSpecs.Length; i++) + { + var streamNum = i; + var streamSpec = _statementSpec.StreamSpecs[i]; + + if (streamSpec is NamedWindowConsumerStreamSpec) + { + hasNamedWindow = true; + var namedSpec = (NamedWindowConsumerStreamSpec)streamSpec; + NamedWindowProcessor processor = _services.NamedWindowMgmtService.GetProcessor(namedSpec.WindowName); + var processorInstance = processor.GetProcessorInstance(agentInstanceContext); + if (processorInstance != null) + { + var consumerView = processorInstance.TailViewInstance; + namedWindowTailViews[i] = consumerView; + var view = (NamedWindowConsumerView)viewableActivationResult[i].Viewable; + + // determine preload/postload filter for index access + if (!namedSpec.FilterExpressions.IsEmpty()) + { + namedWindowFilters[streamNum] = namedSpec.FilterExpressions; + try + { + var streamName = streamSpec.OptionalStreamName != null ? streamSpec.OptionalStreamName : consumerView.EventType.Name; + var types = new StreamTypeServiceImpl(consumerView.EventType, streamName, false, _services.EngineURI); + var tagged = new Dictionary>(); + namedWindowPostloadFilters[i] = FilterSpecCompiler.MakeFilterSpec(types.EventTypes[0], types.StreamNames[0], + namedSpec.FilterExpressions, null, tagged, tagged, types, null, _statementContext, Collections.SingletonSet(0)); + } + catch (Exception ex) + { + Log.Warn("Unexpected exception analyzing filter paths: " + ex.Message, ex); + } + } + + // preload view for stream unless the expiry policy is batch window + var preload = !consumerView.TailView.IsParentBatchWindow && consumerView.HasFirst(); + if (preload) + { + if (isRecoveringResilient && _numStreams < 2) + { + preload = false; + } + } + if (preload) + { + var yesRecoveringResilient = isRecoveringResilient; + var preloadFilterSpec = namedWindowPostloadFilters[i]; + preloadList.Add(new ProxyStatementAgentInstancePreload() + { + ProcExecutePreload = () => + { + var snapshot = consumerView.Snapshot(preloadFilterSpec, _statementContext.Annotations); + var eventsInWindow = new List(snapshot.Count); + ExprNodeUtility.ApplyFilterExpressionsIterable(snapshot, namedSpec.FilterExpressions, agentInstanceContext, eventsInWindow); + EventBean[] newEvents = eventsInWindow.ToArray(); + view.Update(newEvents, null); + if (!yesRecoveringResilient && joinPreloadMethod != null && !joinPreloadMethod.IsPreloading && agentInstanceContext.EpStatementAgentInstanceHandle.OptionalDispatchable != null) + { + agentInstanceContext.EpStatementAgentInstanceHandle.OptionalDispatchable.Execute(); + } + }, + }); + } + } + else + { + Log.Info("Named window access is out-of-context, the named window '" + namedSpec.WindowName + "' has been declared for a different context then the current statement, the aggregation and join state will not be initialized for statement expression [" + _statementContext.Expression + "]"); + } + + preloadList.Add(new ProxyStatementAgentInstancePreload() + { + ProcExecutePreload = () => + { + // in a join, preload indexes, if any + if (joinPreloadMethod != null) + { + joinPreloadMethod.PreloadFromBuffer(streamNum); + } + else + { + if (agentInstanceContext.EpStatementAgentInstanceHandle.OptionalDispatchable != null) + { + agentInstanceContext.EpStatementAgentInstanceHandle.OptionalDispatchable.Execute(); + } + } + }, + }); + } + } + + // last, for aggregation we need to send the current join results to the result set processor + if ((hasNamedWindow) && (joinPreloadMethod != null) && (!isRecoveringResilient) && _resultSetProcessorFactoryDesc.ResultSetProcessorFactory.HasAggregation) + { + preloadList.Add(new ProxyStatementAgentInstancePreload() + { + ProcExecutePreload = () => + { + joinPreloadMethod.PreloadAggregation(resultSetProcessor); + }, + }); + } + + if (isRecoveringResilient) + { + postLoadJoin = new StatementAgentInstancePostLoadSelect(streamViews, joinSetComposer, namedWindowTailViews, namedWindowPostloadFilters, namedWindowFilters, _statementContext.Annotations, agentInstanceContext); + } + else if (joinSetComposer != null) + { + postLoadJoin = new StatementAgentInstancePostLoadIndexVisiting(joinSetComposer.JoinSetComposer); + } + } + } + catch (Exception) + { + var stopCallback = StatementAgentInstanceUtil.GetStopCallback(stopCallbacks, agentInstanceContext); + StatementAgentInstanceUtil.StopSafe(stopCallback, _statementContext); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AContextPartitionAllocate(); } + throw; + } + + var selectResult = new StatementAgentInstanceFactorySelectResult(finalView, null, agentInstanceContext, aggregationService, subselectStrategies, priorNodeStrategies, previousNodeStrategies, regexExprPreviousEvalStrategy, tableAccessStrategies, preloadList, patternRoots, postLoadJoin, topViews, eventStreamParentViewable, viewableActivationResult); + + if (_statementContext.StatementExtensionServicesContext != null) + { + _statementContext.StatementExtensionServicesContext.ContributeStopCallback(selectResult, stopCallbacks); + } + var stopCallbackX = StatementAgentInstanceUtil.GetStopCallback(stopCallbacks, agentInstanceContext); + selectResult.StopCallback = stopCallbackX; + + return selectResult; + } + + internal static void AddViewStopCallback(IList stopCallbacks, IList topViews) + { + foreach (var view in topViews) + { + if (view is StoppableView) + { + stopCallbacks.Add((StoppableView)view); + } + } + } + + public override void AssignExpressions(StatementAgentInstanceFactoryResult result) + { + var selectResult = (StatementAgentInstanceFactorySelectResult)result; + EPStatementStartMethodHelperAssignExpr.AssignAggregations(selectResult.OptionalAggegationService, _resultSetProcessorFactoryDesc.AggregationServiceFactoryDesc.Expressions); + EPStatementStartMethodHelperAssignExpr.AssignSubqueryStrategies(_subSelectStrategyCollection, result.SubselectStrategies); + EPStatementStartMethodHelperAssignExpr.AssignPriorStrategies(result.PriorNodeStrategies); + EPStatementStartMethodHelperAssignExpr.AssignPreviousStrategies(result.PreviousNodeStrategies); + var matchRecognizeNodes = _viewResourceDelegate.PerStream[0].MatchRecognizePreviousRequests; + EPStatementStartMethodHelperAssignExpr.AssignMatchRecognizePreviousStrategies(matchRecognizeNodes, result.RegexExprPreviousEvalStrategy); + } + + public override void UnassignExpressions() + { + EPStatementStartMethodHelperAssignExpr.UnassignAggregations(_resultSetProcessorFactoryDesc.AggregationServiceFactoryDesc.Expressions); + EPStatementStartMethodHelperAssignExpr.UnassignSubqueryStrategies(_subSelectStrategyCollection.Subqueries.Keys); + for (var streamNum = 0; streamNum < _viewResourceDelegate.PerStream.Length; streamNum++) + { + var viewResourceStream = _viewResourceDelegate.PerStream[streamNum]; + var callbacksPerIndex = viewResourceStream.PriorRequests; + foreach (var priorItem in callbacksPerIndex) + { + EPStatementStartMethodHelperAssignExpr.UnassignPriorStrategies(priorItem.Value); + } + EPStatementStartMethodHelperAssignExpr.UnassignPreviousStrategies(viewResourceStream.PreviousRequests); + } + var matchRecognizeNodes = _viewResourceDelegate.PerStream[0].MatchRecognizePreviousRequests; + EPStatementStartMethodHelperAssignExpr.UnassignMatchRecognizePreviousStrategies(matchRecognizeNodes); + } + + public ViewableActivator[] EventStreamParentViewableActivators + { + get { return _eventStreamParentViewableActivators; } + } + + public ViewFactoryChain[] UnmaterializedViewChain + { + get { return _unmaterializedViewChain; } + } + + public int NumStreams + { + get { return _numStreams; } + } + + public StatementContext StatementContext + { + get { return _statementContext; } + } + + public StatementSpecCompiled StatementSpec + { + get { return _statementSpec; } + } + + public EPServicesContext Services + { + get { return _services; } + } + + public StreamTypeService TypeService + { + get { return _typeService; } + } + + public ResultSetProcessorFactoryDesc ResultSetProcessorFactoryDesc + { + get { return _resultSetProcessorFactoryDesc; } + } + + public StreamJoinAnalysisResult JoinAnalysisResult + { + get { return _joinAnalysisResult; } + } + + public JoinSetComposerPrototype JoinSetComposerPrototype + { + get { return _joinSetComposerPrototype; } + } + + public SubSelectStrategyCollection SubSelectStrategyCollection + { + get { return _subSelectStrategyCollection; } + } + + public OutputProcessViewFactory OutputProcessViewFactory + { + get { return _outputProcessViewFactory; } + } + + private Viewable HandleSimpleSelect( + Viewable view, + ResultSetProcessor resultSetProcessor, + AgentInstanceContext agentInstanceContext, + EvalRootMatchRemover evalRootMatchRemover, + bool suppressSameEventMatches, + bool discardPartialsOnMatch) + { + var finalView = view; + + // Add filter view that evaluates the filter expression + if (_statementSpec.FilterRootNode != null) + { + var filterView = new FilterExprView(_statementSpec.FilterRootNode, _statementSpec.FilterRootNode.ExprEvaluator, agentInstanceContext); + finalView.AddView(filterView); + finalView = filterView; + } + + Deque dispatches = null; + + if (evalRootMatchRemover != null && (suppressSameEventMatches || discardPartialsOnMatch)) + { + var v = new PatternRemoveDispatchView(evalRootMatchRemover, suppressSameEventMatches, discardPartialsOnMatch); + dispatches = new ArrayDeque(2); + dispatches.Add(v); + finalView.AddView(v); + finalView = v; + } + + // for ordered deliver without output limit/buffer + if (_statementSpec.OrderByList.Length > 0 && (_statementSpec.OutputLimitSpec == null)) + { + var bf = new SingleStreamDispatchView(); + if (dispatches == null) + { + dispatches = new ArrayDeque(1); + } + dispatches.Add(bf); + finalView.AddView(bf); + finalView = bf; + } + + if (dispatches != null) + { + var handle = agentInstanceContext.EpStatementAgentInstanceHandle; + if (dispatches.Count == 1) + { + handle.OptionalDispatchable = dispatches.First; + } + else + { + EPStatementDispatch[] dispatchArray = dispatches.ToArray(); + handle.OptionalDispatchable = new ProxyEPStatementDispatch() + { + ProcExecute = () => + { + foreach (var dispatch in dispatchArray) + { + dispatch.Execute(); + } + }, + }; + } + } + + View selectView = _outputProcessViewFactory.MakeView(resultSetProcessor, agentInstanceContext); + + finalView.AddView(selectView); + finalView = selectView; + + return finalView; + } + + private JoinPlanResult HandleJoin( + string[] streamNames, + Viewable[] streamViews, + ResultSetProcessor resultSetProcessor, + AgentInstanceContext agentInstanceContext, + IList stopCallbacks, + StreamJoinAnalysisResult joinAnalysisResult, + bool isRecoveringResilient) + { + var joinSetComposerDesc = _joinSetComposerPrototype.Create(streamViews, false, agentInstanceContext, isRecoveringResilient); + + stopCallbacks.Add(new ProxyStopCallback(() => joinSetComposerDesc.JoinSetComposer.Destroy())); + + var filter = new JoinSetFilter(joinSetComposerDesc.PostJoinFilterEvaluator); + var indicatorView = _outputProcessViewFactory.MakeView(resultSetProcessor, agentInstanceContext); + + // Create strategy for join execution + JoinExecutionStrategy execution = new JoinExecutionStrategyImpl(joinSetComposerDesc.JoinSetComposer, filter, indicatorView, agentInstanceContext); + + // The view needs a reference to the join execution to pull iterator values + indicatorView.JoinExecutionStrategy = execution; + + // Hook up dispatchable with buffer and execution strategy + var joinStatementDispatch = new JoinExecStrategyDispatchable(execution, _statementSpec.StreamSpecs.Length); + agentInstanceContext.EpStatementAgentInstanceHandle.OptionalDispatchable = joinStatementDispatch; + + JoinPreloadMethod preloadMethod; + if (joinAnalysisResult.IsUnidirectional) + { + preloadMethod = new JoinPreloadMethodNull(); + } + else + { + if (!joinSetComposerDesc.JoinSetComposer.AllowsInit) + { + preloadMethod = new JoinPreloadMethodNull(); + } + else + { + preloadMethod = new JoinPreloadMethodImpl(streamNames.Length, joinSetComposerDesc.JoinSetComposer); + } + } + + // Create buffer for each view. Point buffer to dispatchable for join. + for (var i = 0; i < _statementSpec.StreamSpecs.Length; i++) + { + var buffer = new BufferView(i); + streamViews[i].AddView(buffer); + buffer.Observer = joinStatementDispatch; + preloadMethod.SetBuffer(buffer, i); + } + + return new JoinPlanResult(indicatorView, preloadMethod, joinSetComposerDesc); + } + + internal class JoinPlanResult + { + internal JoinPlanResult(Viewable viewable, JoinPreloadMethod preloadMethod, JoinSetComposerDesc joinSetComposerDesc) + { + Viewable = viewable; + PreloadMethod = preloadMethod; + JoinSetComposerDesc = joinSetComposerDesc; + } + + public Viewable Viewable { get; private set; } + + public JoinPreloadMethod PreloadMethod { get; private set; } + + public JoinSetComposerDesc JoinSetComposerDesc { get; private set; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactorySelectResult.cs b/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactorySelectResult.cs new file mode 100755 index 000000000..5e8bb0bb6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactorySelectResult.cs @@ -0,0 +1,65 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.core.context.activator; +using com.espertech.esper.core.context.subselect; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.prev; +using com.espertech.esper.epl.expression.prior; +using com.espertech.esper.epl.expression.subquery; +using com.espertech.esper.epl.expression.table; +using com.espertech.esper.pattern; +using com.espertech.esper.rowregex; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.context.factory +{ + public class StatementAgentInstanceFactorySelectResult : StatementAgentInstanceFactoryResult + { + public StatementAgentInstanceFactorySelectResult( + Viewable finalView, + StopCallback stopCallback, + AgentInstanceContext agentInstanceContext, + AggregationService optionalAggegationService, + IDictionary subselectStrategies, + IDictionary priorNodeStrategies, + IDictionary previousNodeStrategies, + RegexExprPreviousEvalStrategy regexExprPreviousEvalStrategy, + IDictionary tableAccessStrategies, + IList preloadList, + EvalRootState[] patternRoots, + StatementAgentInstancePostLoad optionalPostLoadJoin, + Viewable[] topViews, + Viewable[] eventStreamViewables, + ViewableActivationResult[] viewableActivationResults) : base( + finalView, stopCallback, agentInstanceContext, optionalAggegationService, subselectStrategies, + priorNodeStrategies, previousNodeStrategies, regexExprPreviousEvalStrategy, tableAccessStrategies, preloadList) + { + TopViews = topViews; + PatternRoots = patternRoots; + OptionalPostLoadJoin = optionalPostLoadJoin; + EventStreamViewables = eventStreamViewables; + ViewableActivationResults = viewableActivationResults; + } + + public Viewable[] TopViews { get; private set; } + + public EvalRootState[] PatternRoots { get; private set; } + + public StatementAgentInstancePostLoad OptionalPostLoadJoin { get; private set; } + + public Viewable[] EventStreamViewables { get; private set; } + + public ViewableActivationResult[] ViewableActivationResults { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryUpdate.cs b/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryUpdate.cs new file mode 100755 index 000000000..c48402914 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryUpdate.cs @@ -0,0 +1,92 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.subselect; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.core.start; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.subquery; +using com.espertech.esper.epl.spec; +using com.espertech.esper.util; + +namespace com.espertech.esper.core.context.factory +{ + public class StatementAgentInstanceFactoryUpdate : StatementAgentInstanceFactoryBase + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly StatementContext _statementContext; + private readonly EPServicesContext _services; + private readonly EventType _streamEventType; + private readonly UpdateDesc _desc; + private readonly InternalRoutePreprocessView _onExprView; + private readonly InternalEventRouterDesc _routerDesc; + private readonly SubSelectStrategyCollection _subSelectStrategyCollection; + + public StatementAgentInstanceFactoryUpdate(StatementContext statementContext, EPServicesContext services, EventType streamEventType, UpdateDesc desc, InternalRoutePreprocessView onExprView, InternalEventRouterDesc routerDesc, SubSelectStrategyCollection subSelectStrategyCollection) + : base(statementContext.Annotations) + { + _statementContext = statementContext; + _services = services; + _streamEventType = streamEventType; + _desc = desc; + _onExprView = onExprView; + _subSelectStrategyCollection = subSelectStrategyCollection; + _routerDesc = routerDesc; + } + + protected override StatementAgentInstanceFactoryResult NewContextInternal(AgentInstanceContext agentInstanceContext, bool isRecoveringResilient) + { + StopCallback stopCallback; + IList stopCallbacks = new List(); + IDictionary subselectStrategies; + + try + { + stopCallbacks.Add(new ProxyStopCallback(() => _services.InternalEventRouter.RemovePreprocessing(_streamEventType, _desc))); + + _services.InternalEventRouter.AddPreprocessing(_routerDesc, _onExprView, agentInstanceContext.AgentInstanceLock, !_subSelectStrategyCollection.Subqueries.IsEmpty()); + + // start subselects + subselectStrategies = EPStatementStartMethodHelperSubselect.StartSubselects(_services, _subSelectStrategyCollection, agentInstanceContext, stopCallbacks, isRecoveringResilient); + } + catch (Exception ex) + { + stopCallback = StatementAgentInstanceUtil.GetStopCallback(stopCallbacks, agentInstanceContext); + StatementAgentInstanceUtil.StopSafe(stopCallback, _statementContext); + throw; + } + + StatementAgentInstanceFactoryUpdateResult result = new StatementAgentInstanceFactoryUpdateResult(_onExprView, null, agentInstanceContext, subselectStrategies); + if (_statementContext.StatementExtensionServicesContext != null) + { + _statementContext.StatementExtensionServicesContext.ContributeStopCallback(result, stopCallbacks); + } + + stopCallback = StatementAgentInstanceUtil.GetStopCallback(stopCallbacks, agentInstanceContext); + result.StopCallback = stopCallback; + return result; + } + + public override void AssignExpressions(StatementAgentInstanceFactoryResult result) + { + } + + public override void UnassignExpressions() + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryUpdateResult.cs b/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryUpdateResult.cs new file mode 100755 index 000000000..b2f0918b6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstanceFactoryUpdateResult.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.subselect; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.prev; +using com.espertech.esper.epl.expression.prior; +using com.espertech.esper.epl.expression.subquery; +using com.espertech.esper.epl.expression.table; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.context.factory +{ + public class StatementAgentInstanceFactoryUpdateResult : StatementAgentInstanceFactoryResult + { + public StatementAgentInstanceFactoryUpdateResult(Viewable finalView, StopCallback stopCallback, AgentInstanceContext agentInstanceContext, IDictionary subselectStrategies) + : base(finalView, stopCallback, agentInstanceContext, null, + subselectStrategies, + Collections.GetEmptyMap(), + Collections.GetEmptyMap(), + null, + Collections.GetEmptyMap(), + Collections.GetEmptyList()) + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstancePostLoad.cs b/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstancePostLoad.cs new file mode 100755 index 000000000..19fc4bc39 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstancePostLoad.cs @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.core.context.factory +{ + public interface StatementAgentInstancePostLoad + { + void ExecutePostLoad(); + void AcceptIndexVisitor(StatementAgentInstancePostLoadIndexVisitor visitor); + } + + public class ProxyStatementAgentInstancePostLoad : StatementAgentInstancePostLoad + { + public Action ProcExecutePostLoad; + public Action ProcAcceptIndexVisitor; + + public void ExecutePostLoad() + { + ProcExecutePostLoad(); + } + + public void AcceptIndexVisitor(StatementAgentInstancePostLoadIndexVisitor visitor) + { + ProcAcceptIndexVisitor(visitor); + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstancePostLoadIndexVisiting.cs b/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstancePostLoadIndexVisiting.cs new file mode 100755 index 000000000..aea2558e4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstancePostLoadIndexVisiting.cs @@ -0,0 +1,31 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.@join.@base; + +namespace com.espertech.esper.core.context.factory +{ + public class StatementAgentInstancePostLoadIndexVisiting : StatementAgentInstancePostLoad + { + private readonly JoinSetComposer _joinSetComposer; + + public StatementAgentInstancePostLoadIndexVisiting(JoinSetComposer joinSetComposer) + { + _joinSetComposer = joinSetComposer; + } + + public void ExecutePostLoad() + { + } + + public void AcceptIndexVisitor(StatementAgentInstancePostLoadIndexVisitor visitor) + { + _joinSetComposer.VisitIndexes(visitor); + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstancePostLoadIndexVisitor.cs b/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstancePostLoadIndexVisitor.cs new file mode 100755 index 000000000..cb69aa069 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstancePostLoadIndexVisitor.cs @@ -0,0 +1,21 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.epl.join.table; + +namespace com.espertech.esper.core.context.factory +{ + public interface StatementAgentInstancePostLoadIndexVisitor + { + void Visit(EventTable[][] repositories); + void Visit(IList tables); + void Visit(EventTable index); + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstancePostLoadSelect.cs b/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstancePostLoadSelect.cs new file mode 100755 index 000000000..1056f2ddd --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstancePostLoadSelect.cs @@ -0,0 +1,108 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.@join.@base; +using com.espertech.esper.epl.named; +using com.espertech.esper.filter; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.context.factory +{ + public class StatementAgentInstancePostLoadSelect : StatementAgentInstancePostLoad + { + private readonly Viewable[] _streamViews; + private readonly JoinSetComposerDesc _joinSetComposer; + private readonly NamedWindowTailViewInstance[] _namedWindowTailViews; + private readonly FilterSpecCompiled[] _namedWindowPostloadFilters; + private readonly IList[] _namedWindowFilters; + private readonly Attribute[] _annotations; + private readonly ExprEvaluatorContext _exprEvaluatorContext; + + public StatementAgentInstancePostLoadSelect( + Viewable[] streamViews, + JoinSetComposerDesc joinSetComposer, + NamedWindowTailViewInstance[] namedWindowTailViews, + FilterSpecCompiled[] namedWindowPostloadFilters, + IList[] namedWindowFilters, + Attribute[] annotations, + ExprEvaluatorContext exprEvaluatorContext) + { + _streamViews = streamViews; + _joinSetComposer = joinSetComposer; + _namedWindowTailViews = namedWindowTailViews; + _namedWindowPostloadFilters = namedWindowPostloadFilters; + _namedWindowFilters = namedWindowFilters; + _annotations = annotations; + _exprEvaluatorContext = exprEvaluatorContext; + } + + public void ExecutePostLoad() + { + if ((_joinSetComposer == null) || (!_joinSetComposer.JoinSetComposer.AllowsInit)) + { + return; + } + var events = new EventBean[_streamViews.Length][]; + for (var stream = 0; stream < _streamViews.Length; stream++) + { + var streamView = _streamViews[stream]; + if (streamView is HistoricalEventViewable) + { + continue; + } + + ICollection eventsInWindow; + if (_namedWindowTailViews[stream] != null) + { + var nwtail = _namedWindowTailViews[stream]; + var snapshot = nwtail.SnapshotNoLock( + _namedWindowPostloadFilters[stream], _annotations); + if (_namedWindowFilters[stream] != null) + { + eventsInWindow = new List(snapshot.Count); + ExprNodeUtility.ApplyFilterExpressionsIterable( + snapshot, _namedWindowFilters[stream], _exprEvaluatorContext, eventsInWindow); + } + else + { + eventsInWindow = snapshot; + } + } + else if (_namedWindowFilters[stream] != null && !_namedWindowFilters[stream].IsEmpty()) + { + eventsInWindow = new LinkedList(); + ExprNodeUtility.ApplyFilterExpressionsIterable( + _streamViews[stream], _namedWindowFilters[stream], _exprEvaluatorContext, eventsInWindow); + } + else + { + eventsInWindow = new ArrayDeque(); + foreach (var aConsumerView in _streamViews[stream]) + { + eventsInWindow.Add(aConsumerView); + } + } + events[stream] = eventsInWindow.ToArray(); + } + _joinSetComposer.JoinSetComposer.Init(events); + } + + public void AcceptIndexVisitor(StatementAgentInstancePostLoadIndexVisitor visitor) + { + // no action + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstancePreload.cs b/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstancePreload.cs new file mode 100755 index 000000000..b5d5a5a3c --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/factory/StatementAgentInstancePreload.cs @@ -0,0 +1,31 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.core.context.factory +{ + public interface StatementAgentInstancePreload + { + void ExecutePreload(); + } + + public class ProxyStatementAgentInstancePreload : StatementAgentInstancePreload + { + public Action ProcExecutePreload { get; set; } + + /// + /// Executes the preload. + /// + public void ExecutePreload() + { + if (ProcExecutePreload != null) + ProcExecutePreload(); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/AgentInstance.cs b/NEsper.Core/NEsper.Core/core/context/mgr/AgentInstance.cs new file mode 100755 index 000000000..074a6c8a9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/AgentInstance.cs @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.context.util; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.context.mgr +{ + public class AgentInstance + { + public AgentInstance(StopCallback stopCallback, + AgentInstanceContext agentInstanceContext, + Viewable finalView) + { + StopCallback = stopCallback; + AgentInstanceContext = agentInstanceContext; + FinalView = finalView; + } + + public StopCallback StopCallback { get; private set; } + + public AgentInstanceContext AgentInstanceContext { get; private set; } + + public Viewable FinalView { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/AgentInstanceFilterProxy.cs b/NEsper.Core/NEsper.Core/core/context/mgr/AgentInstanceFilterProxy.cs new file mode 100755 index 000000000..69fbbcb75 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/AgentInstanceFilterProxy.cs @@ -0,0 +1,17 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.filter; + +namespace com.espertech.esper.core.context.mgr +{ + public interface AgentInstanceFilterProxy + { + FilterValueSetParam[][] GetAddendumFilters(FilterSpecCompiled filterSpec); + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/AgentInstanceFilterProxyImpl.cs b/NEsper.Core/NEsper.Core/core/context/mgr/AgentInstanceFilterProxyImpl.cs new file mode 100755 index 000000000..23a688ed9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/AgentInstanceFilterProxyImpl.cs @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.compat.collections; +using com.espertech.esper.filter; + +namespace com.espertech.esper.core.context.mgr +{ + public class AgentInstanceFilterProxyImpl : AgentInstanceFilterProxy + { + private readonly IdentityDictionary _addendumMap; + + public AgentInstanceFilterProxyImpl(IdentityDictionary addendums) + { + _addendumMap = addendums; + } + + public FilterValueSetParam[][] GetAddendumFilters(FilterSpecCompiled filterSpec) + { + return _addendumMap.Get(filterSpec); + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/AgentInstanceSelector.cs b/NEsper.Core/NEsper.Core/core/context/mgr/AgentInstanceSelector.cs new file mode 100755 index 000000000..781c4515b --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/AgentInstanceSelector.cs @@ -0,0 +1,14 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.core.context.mgr +{ + public interface AgentInstanceSelector { + bool Select(AgentInstance agentInstance); + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextController.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextController.cs new file mode 100755 index 000000000..759f6e518 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextController.cs @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.client.context; + +namespace com.espertech.esper.core.context.mgr +{ + public interface ContextController + { + int PathId { get; } + void Activate(EventBean optionalTriggeringEvent, IDictionary optionalTriggeringPattern, ContextControllerState states, ContextInternalFilterAddendum filterAddendum, int? importPathId); + ContextControllerFactory Factory { get; } + void Deactivate(); + void VisitSelectedPartitions(ContextPartitionSelector contextPartitionSelector, ContextPartitionVisitor visitor); + void ImportContextPartitions(ContextControllerState state, int pathIdToUse, ContextInternalFilterAddendum filterAddendum, AgentInstanceSelector agentInstanceSelector); + void DeletePath(ContextPartitionIdentifier identifier); + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerAddendumUtil.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerAddendumUtil.cs new file mode 100755 index 000000000..bfae4a346 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerAddendumUtil.cs @@ -0,0 +1,68 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.filter; + +namespace com.espertech.esper.core.context.mgr +{ + public class ContextControllerAddendumUtil + { + public static FilterValueSetParam[][] AddAddendum(FilterValueSetParam[][] filters, FilterValueSetParam toAdd) + { + return AddAddendum(filters, new FilterValueSetParam[] {toAdd}); + } + + public static FilterValueSetParam[][] AddAddendum(FilterValueSetParam[][] filters, FilterValueSetParam[] toAdd) + { + if (filters.Length == 0) + { + filters = new FilterValueSetParam[1][]; + filters[0] = new FilterValueSetParam[0]; + } + + var @params = new FilterValueSetParam[filters.Length][]; + for (var i = 0; i < @params.Length; i++) { + @params[i] = Append(filters[i], toAdd); + } + return @params; + } + + public static FilterValueSetParam[][] MultiplyAddendum(FilterValueSetParam[][] filtersFirst, FilterValueSetParam[][] filtersSecond) + { + if (filtersFirst.Length == 0) { + return filtersSecond; + } + if (filtersSecond.Length == 0) { + return filtersFirst; + } + + var size = filtersFirst.Length * filtersSecond.Length; + var result = new FilterValueSetParam[size][]; + + var count = 0; + foreach (var lineFirst in filtersFirst) { + foreach (var lineSecond in filtersSecond) { + result[count] = Append(lineFirst, lineSecond); + count++; + } + } + + return result; + } + + private static FilterValueSetParam[] Append(FilterValueSetParam[] first, FilterValueSetParam[] second) + { + var appended = new FilterValueSetParam[first.Length + second.Length]; + Array.Copy(first, 0, appended, 0, first.Length); + Array.Copy(second, 0, appended, first.Length, second.Length); + return appended; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerCategory.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerCategory.cs new file mode 100755 index 000000000..1ef0fd268 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerCategory.cs @@ -0,0 +1,262 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.client.context; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.core.context.mgr +{ + public class ContextControllerCategory : ContextController + { + private readonly ContextControllerLifecycleCallback _activationCallback; + + private readonly IDictionary _handleCategories = + new Dictionary(); + + private int _currentSubpathId; + private readonly ContextControllerCategoryFactoryImpl _factory; + + public ContextControllerCategory( + int pathId, + ContextControllerLifecycleCallback activationCallback, + ContextControllerCategoryFactoryImpl factory) + { + PathId = pathId; + _activationCallback = activationCallback; + _factory = factory; + } + + public void ImportContextPartitions( + ContextControllerState state, + int pathIdToUse, + ContextInternalFilterAddendum filterAddendum, + AgentInstanceSelector agentInstanceSelector) + { + InitializeFromState(null, null, filterAddendum, state, pathIdToUse, agentInstanceSelector, true); + } + + public void DeletePath(ContextPartitionIdentifier identifier) + { + var category = (ContextPartitionIdentifierCategory) identifier; + var count = 0; + foreach (var cat in _factory.CategorySpec.Items) + { + if (cat.Name.Equals(category.Label)) + { + _handleCategories.Remove(count); + break; + } + count++; + } + } + + public void VisitSelectedPartitions( + ContextPartitionSelector contextPartitionSelector, + ContextPartitionVisitor visitor) + { + var nestingLevel = Factory.FactoryContext.NestingLevel; + + if (contextPartitionSelector is ContextPartitionSelectorFiltered) + { + var filter = (ContextPartitionSelectorFiltered) contextPartitionSelector; + var identifier = new ContextPartitionIdentifierCategory(); + foreach (var entry in _handleCategories) + { + identifier.ContextPartitionId = entry.Value.ContextPartitionOrPathId; + var categoryName = _factory.CategorySpec.Items[entry.Key].Name; + identifier.Label = categoryName; + if (filter.Filter(identifier)) + { + visitor.Visit(nestingLevel, PathId, _factory.Binding, entry.Key, this, entry.Value); + } + } + return; + } + if (contextPartitionSelector is ContextPartitionSelectorCategory) + { + var category = (ContextPartitionSelectorCategory) contextPartitionSelector; + if (category.Labels == null || category.Labels.IsEmpty()) + { + return; + } + foreach (var entry in _handleCategories) + { + String categoryName = _factory.CategorySpec.Items[entry.Key].Name; + if (category.Labels.Contains(categoryName)) + { + visitor.Visit(nestingLevel, PathId, _factory.Binding, entry.Key, this, entry.Value); + } + } + return; + } + if (contextPartitionSelector is ContextPartitionSelectorById) + { + var byId = (ContextPartitionSelectorById) contextPartitionSelector; + foreach (var entry in _handleCategories) + { + var cpid = entry.Value.ContextPartitionOrPathId; + if (byId.ContextPartitionIds.Contains(cpid)) + { + visitor.Visit(nestingLevel, PathId, _factory.Binding, entry.Key, this, entry.Value); + } + } + return; + } + if (contextPartitionSelector is ContextPartitionSelectorAll) + { + foreach (var entry in _handleCategories) + { + var categoryName = _factory.CategorySpec.Items[entry.Key].Name; + visitor.Visit(nestingLevel, PathId, _factory.Binding, entry.Key, this, entry.Value); + } + return; + } + throw ContextControllerSelectorUtil.GetInvalidSelector( + new Type[] + { + typeof (ContextPartitionSelectorCategory) + }, contextPartitionSelector); + } + + public void Activate( + EventBean optionalTriggeringEvent, + IDictionary optionalTriggeringPattern, + ContextControllerState controllerState, + ContextInternalFilterAddendum activationFilterAddendum, + int? importPathId) + { + if (Factory.FactoryContext.NestingLevel == 1) + { + controllerState = ContextControllerStateUtil.GetRecoveryStates( + Factory.FactoryContext.StateCache, Factory.FactoryContext.OutermostContextName); + } + + if (controllerState == null) + { + var count = 0; + foreach (var category in _factory.CategorySpec.Items) + { + var context = + ContextPropertyEventType.GetCategorizedBean( + Factory.FactoryContext.ContextName, 0, category.Name); + _currentSubpathId++; + + // merge filter addendum, if any + var filterAddendumToUse = activationFilterAddendum; + if (_factory.HasFiltersSpecsNestedContexts) + { + filterAddendumToUse = activationFilterAddendum != null + ? activationFilterAddendum.DeepCopy() + : new ContextInternalFilterAddendum(); + _factory.PopulateContextInternalFilterAddendums(filterAddendumToUse, count); + } + + var handle = _activationCallback.ContextPartitionInstantiate( + null, _currentSubpathId, null, this, optionalTriggeringEvent, optionalTriggeringPattern, count, + context, controllerState, filterAddendumToUse, Factory.FactoryContext.IsRecoveringResilient, + ContextPartitionState.STARTED); + _handleCategories.Put(count, handle); + + Factory.FactoryContext.StateCache.AddContextPath( + Factory.FactoryContext.OutermostContextName, Factory.FactoryContext.NestingLevel, PathId, + _currentSubpathId, handle.ContextPartitionOrPathId, count, _factory.Binding); + count++; + } + return; + } + + var pathIdToUse = importPathId != null ? importPathId.Value : PathId; + InitializeFromState( + optionalTriggeringEvent, optionalTriggeringPattern, activationFilterAddendum, controllerState, + pathIdToUse, null, false); + } + + public ContextControllerFactory Factory + { + get { return _factory; } + } + + public int PathId { get; private set; } + + public void Deactivate() + { + _handleCategories.Clear(); + } + + private void InitializeFromState( + EventBean optionalTriggeringEvent, + IDictionary optionalTriggeringPattern, + ContextInternalFilterAddendum activationFilterAddendum, + ContextControllerState controllerState, + int pathIdToUse, + AgentInstanceSelector agentInstanceSelector, + bool loadingExistingState) + { + var states = controllerState.States; + var childContexts = ContextControllerStateUtil.GetChildContexts( + Factory.FactoryContext, pathIdToUse, states); + + int maxSubpathId = int.MinValue; + foreach (var entry in childContexts) + { + var categoryNumber = (int)_factory.Binding.ByteArrayToObject(entry.Value.Blob, null); + ContextDetailCategoryItem category = _factory.CategorySpec.Items[categoryNumber]; + + // merge filter addendum, if any + var filterAddendumToUse = activationFilterAddendum; + if (_factory.HasFiltersSpecsNestedContexts) + { + filterAddendumToUse = activationFilterAddendum != null + ? activationFilterAddendum.DeepCopy() + : new ContextInternalFilterAddendum(); + _factory.PopulateContextInternalFilterAddendums(filterAddendumToUse, categoryNumber); + } + + // check if exists already + if (controllerState.IsImported) + { + var existingHandle = _handleCategories.Get(categoryNumber); + if (existingHandle != null) + { + _activationCallback.ContextPartitionNavigate( + existingHandle, this, controllerState, entry.Value.OptionalContextPartitionId.Value, + filterAddendumToUse, agentInstanceSelector, entry.Value.Blob, loadingExistingState); + continue; + } + } + + var context = + ContextPropertyEventType.GetCategorizedBean(Factory.FactoryContext.ContextName, 0, category.Name); + + var contextPartitionId = entry.Value.OptionalContextPartitionId.Value; + var assignedSubPathId = !controllerState.IsImported ? entry.Key.SubPath : ++_currentSubpathId; + var handle = + _activationCallback.ContextPartitionInstantiate( + contextPartitionId, assignedSubPathId, entry.Key.SubPath, this, null, null, categoryNumber, + context, controllerState, filterAddendumToUse, loadingExistingState || Factory.FactoryContext.IsRecoveringResilient, + entry.Value.State); + _handleCategories.Put(categoryNumber, handle); + + if (entry.Key.SubPath > maxSubpathId) + { + maxSubpathId = assignedSubPathId; + } + } + if (!controllerState.IsImported) + { + _currentSubpathId = maxSubpathId != int.MinValue ? maxSubpathId : 0; + } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerCategoryFactoryBase.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerCategoryFactoryBase.cs new file mode 100755 index 000000000..2fc9ba39c --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerCategoryFactoryBase.cs @@ -0,0 +1,224 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.client.context; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.stmt; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.spec.util; +using com.espertech.esper.events; +using com.espertech.esper.filter; + +namespace com.espertech.esper.core.context.mgr +{ + public abstract class ContextControllerCategoryFactoryBase + : ContextControllerFactoryBase + , ContextControllerFactory + { + private readonly ContextDetailCategory _categorySpec; + private readonly IList _filtersSpecsNestedContexts; + + private IDictionary _contextBuiltinProps; + + protected ContextControllerCategoryFactoryBase(ContextControllerFactoryContext factoryContext, ContextDetailCategory categorySpec, IList filtersSpecsNestedContexts) + : base(factoryContext) + { + _categorySpec = categorySpec; + _filtersSpecsNestedContexts = filtersSpecsNestedContexts; + } + + public bool HasFiltersSpecsNestedContexts + { + get { return _filtersSpecsNestedContexts != null && !_filtersSpecsNestedContexts.IsEmpty(); } + } + + public override void ValidateFactory() + { + if (_categorySpec.Items.IsEmpty()) + { + throw new ExprValidationException("EmptyFalse list of partition items"); + } + _contextBuiltinProps = ContextPropertyEventType.GetCategorizedType(); + } + + public override ContextControllerStatementCtxCache ValidateStatement(ContextControllerStatementBase statement) + { + var streamAnalysis = StatementSpecCompiledAnalyzer.AnalyzeFilters(statement.StatementSpec); + ValidateStatementForContext(statement, streamAnalysis); + return new ContextControllerStatementCtxCacheFilters(streamAnalysis.Filters); + } + + public override void PopulateFilterAddendums( + IDictionary filterAddendum, + ContextControllerStatementDesc statement, + object categoryIndex, + int contextId) + { + var statementInfo = (ContextControllerStatementCtxCacheFilters)statement.Caches[FactoryContext.NestingLevel - 1]; + var category = _categorySpec.Items[categoryIndex.AsInt()]; + GetAddendumFilters(filterAddendum, category, _categorySpec, statementInfo.FilterSpecs, statement); + } + + public void PopulateContextInternalFilterAddendums(ContextInternalFilterAddendum filterAddendum, object categoryIndex) + { + var category = _categorySpec.Items[categoryIndex.AsInt()]; + GetAddendumFilters(filterAddendum.FilterAddendum, category, _categorySpec, _filtersSpecsNestedContexts, null); + } + + public override FilterSpecLookupable GetFilterLookupable(EventType eventType) + { + return null; + } + + public override bool IsSingleInstanceContext + { + get { return false; } + } + + public override StatementAIResourceRegistryFactory StatementAIResourceRegistryFactory + { + get + { + return () => new StatementAIResourceRegistry( + new AIRegistryAggregationMultiPerm(), new AIRegistryExprMultiPerm()); + } + } + + public override IList ContextDetailPartitionItems + { + get { return Collections.GetEmptyList(); } + } + + public override ContextDetail ContextDetail + { + get { return _categorySpec; } + } + + public ContextDetailCategory CategorySpec + { + get { return _categorySpec; } + } + + public override IDictionary ContextBuiltinProps + { + get { return _contextBuiltinProps; } + } + + public override ContextPartitionIdentifier KeyPayloadToIdentifier(object payload) + { + var index = payload.AsInt(); + return new ContextPartitionIdentifierCategory(_categorySpec.Items[index].Name); + } + + private void ValidateStatementForContext(ContextControllerStatementBase statement, StatementSpecCompiledAnalyzerResult streamAnalysis) + { + var filters = streamAnalysis.Filters; + + var isCreateWindow = statement.StatementSpec.CreateWindowDesc != null; + var message = "Category context '" + FactoryContext.ContextName + "' requires that any of the events types that are listed in the category context also appear in any of the filter expressions of the statement"; + + // if no create-window: at least one of the filters must match one of the filters specified by the context + if (!isCreateWindow) + { + foreach (var filter in filters) + { + var stmtFilterType = filter.FilterForEventType; + var contextType = _categorySpec.FilterSpecCompiled.FilterForEventType; + if (Equals(stmtFilterType, contextType)) + { + return; + } + if (EventTypeUtility.IsTypeOrSubTypeOf(stmtFilterType, contextType)) + { + return; + } + } + + if (!filters.IsEmpty()) + { + throw new ExprValidationException(message); + } + return; + } + + // validate create-window + var declaredAsName = statement.StatementSpec.CreateWindowDesc.AsEventTypeName; + if (declaredAsName != null) + { + if (_categorySpec.FilterSpecCompiled.FilterForEventType.Name.Equals(declaredAsName)) + { + return; + } + throw new ExprValidationException(message); + } + } + + // Compare filters in statement with filters in segmented context, addendum filter compilation + private static void GetAddendumFilters( + IDictionary addendums, + ContextDetailCategoryItem category, + ContextDetailCategory categorySpec, + IList filters, + ContextControllerStatementDesc statement) + { + // determine whether create-named-window + var isCreateWindow = statement != null && statement.Statement.StatementSpec.CreateWindowDesc != null; + if (!isCreateWindow) + { + foreach (var filtersSpec in filters) + { + + var typeOrSubtype = EventTypeUtility.IsTypeOrSubTypeOf(filtersSpec.FilterForEventType, categorySpec.FilterSpecCompiled.FilterForEventType); + if (!typeOrSubtype) + { + continue; // does not apply + } + AddAddendums(addendums, filtersSpec, category, categorySpec); + } + } + else + { + // handle segmented context for create-window + var declaredAsName = statement.Statement.StatementSpec.CreateWindowDesc.AsEventTypeName; + if (declaredAsName != null) + { + foreach (var filtersSpec in filters) + { + AddAddendums(addendums, filtersSpec, category, categorySpec); + } + } + } + } + + private static void AddAddendums( + IDictionary addendums, + FilterSpecCompiled filtersSpec, + ContextDetailCategoryItem category, + ContextDetailCategory categorySpec) + { + var categoryEventFilters = categorySpec.FilterParamsCompiled; + var categoryItemFilters = category.CompiledFilterParam; + + var addendum = ContextControllerAddendumUtil.MultiplyAddendum( + categoryEventFilters, categoryItemFilters); + + var existingFilters = addendums.Get(filtersSpec); + if (existingFilters != null) + { + addendum = ContextControllerAddendumUtil.MultiplyAddendum(existingFilters, addendum); + } + + addendums.Put(filtersSpec, addendum); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerCategoryFactoryImpl.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerCategoryFactoryImpl.cs new file mode 100755 index 000000000..abf3f43a2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerCategoryFactoryImpl.cs @@ -0,0 +1,39 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.epl.spec; +using com.espertech.esper.filter; + +namespace com.espertech.esper.core.context.mgr +{ + public class ContextControllerCategoryFactoryImpl : ContextControllerCategoryFactoryBase + { + private readonly ContextStatePathValueBinding _binding; + + public ContextControllerCategoryFactoryImpl( + ContextControllerFactoryContext factoryContext, + ContextDetailCategory categorySpec, + IList filtersSpecsNestedContexts) + : base(factoryContext, categorySpec, filtersSpecsNestedContexts) + { + _binding = factoryContext.StateCache.GetBinding(typeof(int)); // the integer ordinal of the category + } + + public ContextStatePathValueBinding Binding + { + get { return _binding; } + } + + public override ContextController CreateNoCallback(int pathId, ContextControllerLifecycleCallback callback) + { + return new ContextControllerCategory(pathId, callback, this); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerCondition.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerCondition.cs new file mode 100755 index 000000000..b9f8a4378 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerCondition.cs @@ -0,0 +1,22 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.pattern; + +namespace com.espertech.esper.core.context.mgr +{ + public interface ContextControllerCondition + { + void Activate(EventBean optionalTriggeringEvent, MatchedEventMap priorMatches, long timeOffset, bool isRecoveringResilient); + void Deactivate(); + bool IsRunning { get; } + long? ExpectedEndTime { get; } + bool IsImmediate { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerConditionCallback.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerConditionCallback.cs new file mode 100755 index 000000000..41f33e50e --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerConditionCallback.cs @@ -0,0 +1,24 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; + +namespace com.espertech.esper.core.context.mgr +{ + public interface ContextControllerConditionCallback + { + void RangeNotification(IDictionary builtinProperties, + ContextControllerCondition originEndpoint, + EventBean optionalTriggeringEvent, + IDictionary optionalTriggeringPattern, + ContextInternalFilterAddendum filterAddendum); + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerConditionCrontab.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerConditionCrontab.cs new file mode 100755 index 000000000..92489a1f6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerConditionCrontab.cs @@ -0,0 +1,108 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.spec; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.pattern; +using com.espertech.esper.schedule; + +namespace com.espertech.esper.core.context.mgr +{ + public class ContextControllerConditionCrontab : ContextControllerCondition + { + private readonly StatementContext _statementContext; + private readonly long _scheduleSlot; + private readonly ContextDetailConditionCrontab _spec; + private readonly ContextControllerConditionCallback _callback; + private readonly ContextInternalFilterAddendum _filterAddendum; + + private EPStatementHandleCallback _scheduleHandle; + + public ContextControllerConditionCrontab( + StatementContext statementContext, + long scheduleSlot, + ContextDetailConditionCrontab spec, + ContextControllerConditionCallback callback, + ContextInternalFilterAddendum filterAddendum) + { + _statementContext = statementContext; + _scheduleSlot = scheduleSlot; + _spec = spec; + _callback = callback; + _filterAddendum = filterAddendum; + } + + public void Activate(EventBean optionalTriggerEvent, MatchedEventMap priorMatches, long timeOffset, bool isRecoveringResilient) + { + StartContextCallback(); + } + + public void Deactivate() + { + EndContextCallback(); + } + + public bool IsRunning + { + get { return _scheduleHandle != null; } + } + + private void StartContextCallback() + { + var scheduleCallback = new ProxyScheduleHandleCallback + { + ProcScheduledTrigger = (extensionServicesContext) => + { + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().QContextScheduledEval(_statementContext.ContextDescriptor); + } + _scheduleHandle = null; // terminates automatically unless scheduled again + _callback.RangeNotification(Collections.EmptyDataMap, this, null, null, _filterAddendum); + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().AContextScheduledEval(); + } + } + }; + var agentHandle = new EPStatementAgentInstanceHandle(_statementContext.EpStatementHandle, _statementContext.DefaultAgentInstanceLock, -1, new StatementAgentInstanceFilterVersion(), _statementContext.FilterFaultHandlerFactory); + _scheduleHandle = new EPStatementHandleCallback(agentHandle, scheduleCallback); + var schedulingService = _statementContext.SchedulingService; + var engineImportService = _statementContext.EngineImportService; + var nextScheduledTime = ScheduleComputeHelper.ComputeDeltaNextOccurance(_spec.Schedule, schedulingService.Time, engineImportService.TimeZone, engineImportService.TimeAbacus); + _statementContext.SchedulingService.Add(nextScheduledTime, _scheduleHandle, _scheduleSlot); + } + + private void EndContextCallback() + { + if (_scheduleHandle != null) + { + _statementContext.SchedulingService.Remove(_scheduleHandle, _scheduleSlot); + } + _scheduleHandle = null; + } + + public long? ExpectedEndTime + { + get + { + var engineImportService = _statementContext.EngineImportService; + return ScheduleComputeHelper.ComputeNextOccurance( + _spec.Schedule, _statementContext.TimeProvider.Time, engineImportService.TimeZone, + engineImportService.TimeAbacus); + } + } + + public bool IsImmediate + { + get { return _spec.IsImmediate; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerConditionFactory.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerConditionFactory.cs new file mode 100755 index 000000000..59dc25f44 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerConditionFactory.cs @@ -0,0 +1,68 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.compat; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.core.context.mgr +{ + public class ContextControllerConditionFactory + { + public static ContextControllerCondition GetEndpoint( + string contextName, + EPServicesContext servicesContext, + AgentInstanceContext agentInstanceContext, + ContextDetailCondition endpoint, + ContextControllerConditionCallback callback, + ContextInternalFilterAddendum filterAddendum, + bool isStartEndpoint, + int nestingLevel, + int pathId, + int subpathId) + { + if (endpoint is ContextDetailConditionCrontab) + { + var crontab = (ContextDetailConditionCrontab) endpoint; + long scheduleSlot = agentInstanceContext.StatementContext.ScheduleBucket.AllocateSlot(); + return new ContextControllerConditionCrontab( + agentInstanceContext.StatementContext, scheduleSlot, crontab, callback, filterAddendum); + } + else if (endpoint is ContextDetailConditionFilter) + { + var filter = (ContextDetailConditionFilter) endpoint; + return new ContextControllerConditionFilter( + servicesContext, agentInstanceContext, filter, callback, filterAddendum); + } + else if (endpoint is ContextDetailConditionPattern) + { + var key = new ContextStatePathKey(nestingLevel, pathId, subpathId); + var pattern = (ContextDetailConditionPattern) endpoint; + return new ContextControllerConditionPattern( + servicesContext, agentInstanceContext, pattern, callback, filterAddendum, isStartEndpoint, key); + } + else if (endpoint is ContextDetailConditionTimePeriod) + { + var timePeriod = (ContextDetailConditionTimePeriod) endpoint; + long scheduleSlot = agentInstanceContext.StatementContext.ScheduleBucket.AllocateSlot(); + return new ContextControllerConditionTimePeriod( + contextName, agentInstanceContext, scheduleSlot, timePeriod, callback, filterAddendum); + } + else if (endpoint is ContextDetailConditionImmediate) + { + return new ContextControllerConditionImmediate(); + } + else if (endpoint is ContextDetailConditionNever) + { + return new ContextControllerConditionNever(); + } + throw new IllegalStateException("Unrecognized context range endpoint " + endpoint.GetType()); + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerConditionFilter.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerConditionFilter.cs new file mode 100755 index 000000000..232239f5b --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerConditionFilter.cs @@ -0,0 +1,141 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.spec; +using com.espertech.esper.filter; +using com.espertech.esper.pattern; + +namespace com.espertech.esper.core.context.mgr +{ + public class ContextControllerConditionFilter : ContextControllerCondition + { + private readonly AgentInstanceContext _agentInstanceContext; + private readonly ContextControllerConditionCallback _callback; + private readonly ContextDetailConditionFilter _endpointFilterSpec; + private readonly ContextInternalFilterAddendum _filterAddendum; + private readonly EPServicesContext _servicesContext; + + private EPStatementHandleCallback _filterHandle; + private FilterServiceEntry _filterServiceEntry; + private EventBean _lastEvent; + + public ContextControllerConditionFilter( + EPServicesContext servicesContext, + AgentInstanceContext agentInstanceContext, + ContextDetailConditionFilter endpointFilterSpec, + ContextControllerConditionCallback callback, + ContextInternalFilterAddendum filterAddendum) + { + _servicesContext = servicesContext; + _agentInstanceContext = agentInstanceContext; + _endpointFilterSpec = endpointFilterSpec; + _callback = callback; + _filterAddendum = filterAddendum; + } + + public void Activate( + EventBean optionalTriggeringEvent, + MatchedEventMap priorMatches, + long timeOffset, + bool isRecoveringResilient) + { + var filterCallback = new ProxyFilterHandleCallback + { + ProcStatementId = () => _agentInstanceContext.StatementContext.StatementId, + ProcMatchFound = (theEvent, allStmtMatches) => FilterMatchFound(theEvent), + ProcIsSubselect = () => false + }; + + // determine addendum, if any + FilterValueSetParam[][] addendum = null; + if (_filterAddendum != null) + { + addendum = _filterAddendum.GetFilterAddendum(_endpointFilterSpec.FilterSpecCompiled); + } + + _filterHandle = new EPStatementHandleCallback( + _agentInstanceContext.EpStatementAgentInstanceHandle, filterCallback); + FilterValueSet filterValueSet = _endpointFilterSpec.FilterSpecCompiled.GetValueSet( + null, _agentInstanceContext, addendum); + _filterServiceEntry = _servicesContext.FilterService.Add(filterValueSet, _filterHandle); + long filtersVersion = _servicesContext.FilterService.FiltersVersion; + _agentInstanceContext.EpStatementAgentInstanceHandle.StatementFilterVersion.StmtFilterVersion = + filtersVersion; + + if (optionalTriggeringEvent != null) + { + bool match = StatementAgentInstanceUtil.EvaluateFilterForStatement( + _servicesContext, optionalTriggeringEvent, _agentInstanceContext, _filterHandle); + + if (match) + { + FilterMatchFound(optionalTriggeringEvent); + } + } + } + + public void Deactivate() + { + if (_filterHandle != null) + { + _servicesContext.FilterService.Remove(_filterHandle, _filterServiceEntry); + _filterHandle = null; + _filterServiceEntry = null; + long filtersVersion = _agentInstanceContext.StatementContext.FilterService.FiltersVersion; + _agentInstanceContext.EpStatementAgentInstanceHandle.StatementFilterVersion.StmtFilterVersion = + filtersVersion; + } + } + + private void FilterMatchFound(EventBean theEvent) + { + // For OR-type filters we de-duplicate here by keeping the last event instance + if (_endpointFilterSpec.FilterSpecCompiled.Parameters.Length > 1) + { + if (theEvent == _lastEvent) + { + return; + } + _lastEvent = theEvent; + } + IDictionary props = Collections.EmptyDataMap; + if (_endpointFilterSpec.OptionalFilterAsName != null) + { + props = Collections.SingletonDataMap(_endpointFilterSpec.OptionalFilterAsName, theEvent); + } + _callback.RangeNotification(props, this, theEvent, null, _filterAddendum); + } + + public bool IsRunning + { + get { return _filterHandle != null; } + } + + public long? ExpectedEndTime + { + get { return null; } + } + + public bool IsImmediate + { + get { return false; } + } + + public ContextDetailConditionFilter EndpointFilterSpec + { + get { return _endpointFilterSpec; } + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerConditionImmediate.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerConditionImmediate.cs new file mode 100755 index 000000000..a1c5d29a6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerConditionImmediate.cs @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.pattern; + +namespace com.espertech.esper.core.context.mgr +{ + /// + /// Context condition used for Non-Overlapping contexts only, when @GetInstance is specified. + /// Not used for end-conditions (only for start range conditions). This is enforced by + /// the grammar and spec mapper. + /// + public class ContextControllerConditionImmediate : ContextControllerCondition + { + #region ContextControllerCondition Members + + public void Activate(EventBean optionalTriggerEvent, MatchedEventMap priorMatches, long timeOffset, bool isRecoveringResilient) + { + } + + public void Deactivate() + { + } + + public bool IsRunning + { + get { return false; } + } + + public long? ExpectedEndTime + { + get { return null; } + } + + public bool IsImmediate + { + get { return true; } + } + + #endregion + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerConditionNever.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerConditionNever.cs new file mode 100755 index 000000000..bd7a99398 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerConditionNever.cs @@ -0,0 +1,46 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.pattern; + +namespace com.espertech.esper.core.context.mgr +{ + /// + /// Context condition used for overlapping and non-overlaping to never-end/terminated. + /// + public class ContextControllerConditionNever : ContextControllerCondition + { + public void Activate( + EventBean optionalTriggerEvent, + MatchedEventMap priorMatches, + long timeOffset, + bool isRecoveringResilient) + { + } + + public void Deactivate() + { + } + + public bool IsRunning + { + get { return true; } + } + + public long? ExpectedEndTime + { + get { return null; } + } + + public bool IsImmediate + { + get { return false; } + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerConditionPattern.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerConditionPattern.cs new file mode 100755 index 000000000..6344d50c5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerConditionPattern.cs @@ -0,0 +1,156 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.spec; +using com.espertech.esper.pattern; + +namespace com.espertech.esper.core.context.mgr +{ + public class ContextControllerConditionPattern + : ContextControllerCondition + { + private readonly EPServicesContext _servicesContext; + private readonly AgentInstanceContext _agentInstanceContext; + private readonly ContextDetailConditionPattern _endpointPatternSpec; + private readonly ContextControllerConditionCallback _callback; + private readonly ContextInternalFilterAddendum _filterAddendum; + private readonly bool _isStartEndpoint; + private readonly ContextStatePathKey _contextStatePathKey; + + protected EvalRootState PatternStopCallback; + + public ContextControllerConditionPattern(EPServicesContext servicesContext, AgentInstanceContext agentInstanceContext, ContextDetailConditionPattern endpointPatternSpec, ContextControllerConditionCallback callback, ContextInternalFilterAddendum filterAddendum, bool startEndpoint, ContextStatePathKey contextStatePathKey) + { + _servicesContext = servicesContext; + _agentInstanceContext = agentInstanceContext; + _endpointPatternSpec = endpointPatternSpec; + _callback = callback; + _filterAddendum = filterAddendum; + _isStartEndpoint = startEndpoint; + _contextStatePathKey = contextStatePathKey; + } + + public void Activate(EventBean optionalTriggeringEvent, MatchedEventMap priorMatches, long timeOffset, bool isRecoveringResilient) { + if (PatternStopCallback != null) { + PatternStopCallback.Stop(); + } + + PatternStreamSpecCompiled patternStreamSpec = _endpointPatternSpec.PatternCompiled; + StatementContext stmtContext = _agentInstanceContext.StatementContext; + + EvalRootFactoryNode rootFactoryNode = _servicesContext.PatternNodeFactory.MakeRootNode(patternStreamSpec.EvalFactoryNode); + int streamNum = _isStartEndpoint ? _contextStatePathKey.SubPath : -1 * _contextStatePathKey.SubPath; + bool allowResilient = _contextStatePathKey.Level == 1; + PatternContext patternContext = stmtContext.PatternContextFactory.CreateContext(stmtContext, streamNum, rootFactoryNode, new MatchedEventMapMeta(patternStreamSpec.AllTags, !patternStreamSpec.ArrayEventTypes.IsEmpty()), allowResilient); + + PatternAgentInstanceContext patternAgentInstanceContext = stmtContext.PatternContextFactory.CreatePatternAgentContext(patternContext, _agentInstanceContext, false); + EvalRootNode rootNode = EvalNodeUtil.MakeRootNodeFromFactory(rootFactoryNode, patternAgentInstanceContext); + + if (priorMatches == null) { + priorMatches = new MatchedEventMapImpl(patternContext.MatchedEventMapMeta); + } + + // capture any callbacks that may occur right after start + ConditionPatternMatchCallback callback = new ConditionPatternMatchCallback(this); + PatternStopCallback = rootNode.Start(callback.MatchFound, patternContext, priorMatches, isRecoveringResilient); + callback.ForwardCalls = true; + + if (_agentInstanceContext.StatementContext.StatementExtensionServicesContext != null && + _agentInstanceContext.StatementContext.StatementExtensionServicesContext.StmtResources != null) + { + _agentInstanceContext.StatementContext.StatementExtensionServicesContext.StmtResources + .StartContextPattern(PatternStopCallback, _isStartEndpoint, _contextStatePathKey); + } + + if (callback.IsInvoked) { + MatchFound(Collections.GetEmptyMap()); + } + } + + public void MatchFound(IDictionary matchEvent) { + IDictionary matchEventInclusive = null; + if (_endpointPatternSpec.IsInclusive) { + if (matchEvent.Count < 2) { + matchEventInclusive = matchEvent; + } + else { + // need to reorder according to tag order + var ordered = new Dictionary(); + foreach (String key in _endpointPatternSpec.PatternCompiled.TaggedEventTypes.Keys) { + ordered.Put(key, matchEvent.Get(key)); + } + foreach (String key in _endpointPatternSpec.PatternCompiled.ArrayEventTypes.Keys) { + ordered.Put(key, matchEvent.Get(key)); + } + matchEventInclusive = ordered; + } + } + _callback.RangeNotification(matchEvent, this, null, matchEventInclusive, _filterAddendum); + } + + public void Deactivate() { + if (PatternStopCallback != null) { + PatternStopCallback.Stop(); + PatternStopCallback = null; + if (_agentInstanceContext.StatementContext.StatementExtensionServicesContext != null && + _agentInstanceContext.StatementContext.StatementExtensionServicesContext.StmtResources != null) { + _agentInstanceContext.StatementContext.StatementExtensionServicesContext.StmtResources.StopContextPattern(_isStartEndpoint, _contextStatePathKey); + } + } + } + + public bool IsRunning + { + get { return PatternStopCallback != null; } + } + + public long? ExpectedEndTime + { + get { return null; } + } + + public bool IsImmediate + { + get { return _endpointPatternSpec.IsImmediate; } + } + + public class ConditionPatternMatchCallback + { + private readonly ContextControllerConditionPattern _condition; + + private bool _isInvoked; + internal bool ForwardCalls; + + public ConditionPatternMatchCallback(ContextControllerConditionPattern condition) + { + _condition = condition; + } + + public void MatchFound(IDictionary matchEvent) + { + _isInvoked = true; + if (ForwardCalls) + { + _condition.MatchFound(matchEvent); + } + } + + public bool IsInvoked + { + get { return _isInvoked; } + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerConditionTimePeriod.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerConditionTimePeriod.cs new file mode 100755 index 000000000..09f4329e7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerConditionTimePeriod.cs @@ -0,0 +1,119 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.spec; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.pattern; +using com.espertech.esper.schedule; + +namespace com.espertech.esper.core.context.mgr +{ + public class ContextControllerConditionTimePeriod : ContextControllerCondition + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly String _contextName; + private readonly AgentInstanceContext _agentInstanceContext; + private readonly long _scheduleSlot; + private readonly ContextDetailConditionTimePeriod _spec; + private readonly ContextControllerConditionCallback _callback; + private readonly ContextInternalFilterAddendum _filterAddendum; + + private EPStatementHandleCallback _scheduleHandle; + + public ContextControllerConditionTimePeriod( + String contextName, + AgentInstanceContext agentInstanceContext, + long scheduleSlot, + ContextDetailConditionTimePeriod spec, + ContextControllerConditionCallback callback, + ContextInternalFilterAddendum filterAddendum) + { + _contextName = contextName; + _agentInstanceContext = agentInstanceContext; + _scheduleSlot = scheduleSlot; + _spec = spec; + _callback = callback; + _filterAddendum = filterAddendum; + } + + public void Activate( + EventBean optionalTriggerEvent, + MatchedEventMap priorMatches, + long timeOffset, + bool isRecoveringResilient) + { + StartContextCallback(timeOffset); + } + + public void Deactivate() + { + EndContextCallback(); + } + + public bool IsRunning + { + get { return _scheduleHandle != null; } + } + + public bool IsImmediate + { + get { return _spec.IsImmediate; } + } + + private void StartContextCallback(long timeOffset) + { + var scheduleCallback = new ProxyScheduleHandleCallback() + { + ProcScheduledTrigger = extensionServicesContext => + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QContextScheduledEval(_agentInstanceContext.StatementContext.ContextDescriptor); } + _scheduleHandle = null; // terminates automatically unless scheduled again + _callback.RangeNotification(Collections.GetEmptyMap(), this, null, null, _filterAddendum); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AContextScheduledEval(); } + } + }; + var agentHandle = + new EPStatementAgentInstanceHandle( + _agentInstanceContext.StatementContext.EpStatementHandle, + _agentInstanceContext.StatementContext.DefaultAgentInstanceLock, -1, + new StatementAgentInstanceFilterVersion(), + _agentInstanceContext.StatementContext.FilterFaultHandlerFactory); + _scheduleHandle = new EPStatementHandleCallback(agentHandle, scheduleCallback); + + + long timeDelta = _spec.TimePeriod.NonconstEvaluator().DeltaUseEngineTime(null, _agentInstanceContext) - timeOffset; + _agentInstanceContext.StatementContext.SchedulingService.Add(timeDelta, _scheduleHandle, _scheduleSlot); + } + + private void EndContextCallback() + { + if (_scheduleHandle != null) + { + _agentInstanceContext.StatementContext.SchedulingService.Remove(_scheduleHandle, _scheduleSlot); + } + _scheduleHandle = null; + } + + public long? ExpectedEndTime + { + get + { + return _spec.GetExpectedEndTime(_agentInstanceContext); + } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerFactory.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerFactory.cs new file mode 100755 index 000000000..c62334cfc --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerFactory.cs @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.client.context; +using com.espertech.esper.core.context.stmt; +using com.espertech.esper.epl.spec; +using com.espertech.esper.filter; + +namespace com.espertech.esper.core.context.mgr +{ + public interface ContextControllerFactory + { + ContextControllerFactoryContext FactoryContext { get; } + + IDictionary ContextBuiltinProps { get; } + bool IsSingleInstanceContext { get; } + ContextDetail ContextDetail { get; } + IList ContextDetailPartitionItems { get; } + StatementAIResourceRegistryFactory StatementAIResourceRegistryFactory { get; } + + void ValidateFactory(); + ContextControllerStatementCtxCache ValidateStatement(ContextControllerStatementBase statement); + ContextController CreateNoCallback(int pathId, ContextControllerLifecycleCallback callback); + void PopulateFilterAddendums(IDictionary filterAddendum, ContextControllerStatementDesc statement, Object key, int contextId); + + FilterSpecLookupable GetFilterLookupable(EventType eventType); + + ContextStateCache StateCache { get; } + + ContextPartitionIdentifier KeyPayloadToIdentifier(Object payload); + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerFactoryBase.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerFactoryBase.cs new file mode 100755 index 000000000..ac7f4e1b8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerFactoryBase.cs @@ -0,0 +1,49 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using com.espertech.esper.client; +using com.espertech.esper.client.context; +using com.espertech.esper.core.context.stmt; +using com.espertech.esper.epl.spec; +using com.espertech.esper.filter; + +namespace com.espertech.esper.core.context.mgr +{ + public abstract class ContextControllerFactoryBase : ContextControllerFactory + { + private readonly ContextControllerFactoryContext _factoryContext; + + protected ContextControllerFactoryBase(ContextControllerFactoryContext factoryContext) + { + _factoryContext = factoryContext; + } + + public virtual ContextControllerFactoryContext FactoryContext + { + get { return _factoryContext; } + } + + public virtual ContextStateCache StateCache + { + get { return _factoryContext.StateCache; } + } + + public abstract IDictionary ContextBuiltinProps { get; } + public abstract bool IsSingleInstanceContext { get; } + public abstract ContextDetail ContextDetail { get; } + public abstract IList ContextDetailPartitionItems { get; } + public abstract StatementAIResourceRegistryFactory StatementAIResourceRegistryFactory { get; } + public abstract void ValidateFactory(); + public abstract ContextControllerStatementCtxCache ValidateStatement(ContextControllerStatementBase statement); + public abstract ContextController CreateNoCallback(int pathId, ContextControllerLifecycleCallback callback); + public abstract void PopulateFilterAddendums(IDictionary filterAddendum, ContextControllerStatementDesc statement, object key, int contextId); + public abstract FilterSpecLookupable GetFilterLookupable(EventType eventType); + public abstract ContextPartitionIdentifier KeyPayloadToIdentifier(object payload); + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerFactoryContext.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerFactoryContext.cs new file mode 100755 index 000000000..2d32d47d0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerFactoryContext.cs @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; + +namespace com.espertech.esper.core.context.mgr +{ + public class ContextControllerFactoryContext + { + public ContextControllerFactoryContext( + String outermostContextName, + String contextName, + EPServicesContext servicesContext, + AgentInstanceContext agentInstanceContextCreate, + int nestingLevel, + int numNestingLevels, + bool isRecoveringResilient, + ContextStateCache stateCache) + { + OutermostContextName = outermostContextName; + ContextName = contextName; + ServicesContext = servicesContext; + AgentInstanceContextCreate = agentInstanceContextCreate; + NestingLevel = nestingLevel; + NumNestingLevels = numNestingLevels; + IsRecoveringResilient = isRecoveringResilient; + StateCache = stateCache; + } + + public string OutermostContextName { get; private set; } + + public string ContextName { get; private set; } + + public EPServicesContext ServicesContext { get; private set; } + + public AgentInstanceContext AgentInstanceContextCreate { get; private set; } + + public int NestingLevel { get; private set; } + + public int NumNestingLevels { get; private set; } + + public bool IsRecoveringResilient { get; private set; } + + public ContextStateCache StateCache { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerFactoryFactorySvc.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerFactoryFactorySvc.cs new file mode 100755 index 000000000..540783b6e --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerFactoryFactorySvc.cs @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.epl.spec; +using com.espertech.esper.filter; + +namespace com.espertech.esper.core.context.mgr +{ + public interface ContextControllerFactoryFactorySvc + { + ContextControllerFactory Make(ContextControllerFactoryContext factoryContext, ContextDetail detail, IList optFiltersNested); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerFactoryFactorySvcImpl.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerFactoryFactorySvcImpl.cs new file mode 100755 index 000000000..9717a2cae --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerFactoryFactorySvcImpl.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.compat; +using com.espertech.esper.epl.spec; +using com.espertech.esper.filter; + +namespace com.espertech.esper.core.context.mgr +{ + public class ContextControllerFactoryFactorySvcImpl : ContextControllerFactoryFactorySvc + { + public ContextControllerFactory Make(ContextControllerFactoryContext factoryContext, ContextDetail detail, IList optFiltersNested) + { + ContextControllerFactory factory; + if (detail is ContextDetailInitiatedTerminated) { + factory = new ContextControllerInitTermFactoryImpl(factoryContext, (ContextDetailInitiatedTerminated) detail); + } else if (detail is ContextDetailPartitioned) { + factory = new ContextControllerPartitionedFactoryImpl(factoryContext, (ContextDetailPartitioned) detail, optFiltersNested); + } else if (detail is ContextDetailCategory) { + factory = new ContextControllerCategoryFactoryImpl(factoryContext, (ContextDetailCategory) detail, optFiltersNested); + } else if (detail is ContextDetailHash) { + factory = new ContextControllerHashFactoryImpl(factoryContext, (ContextDetailHash) detail, optFiltersNested); + } else { + throw new UnsupportedOperationException("Context detail " + detail + " is not yet supported in a nested context"); + } + + return factory; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerFactoryHelper.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerFactoryHelper.cs new file mode 100755 index 000000000..f1ea689f1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerFactoryHelper.cs @@ -0,0 +1,129 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; +using com.espertech.esper.filter; + +namespace com.espertech.esper.core.context.mgr +{ + public class ContextControllerFactoryHelper + { + public static ContextControllerFactory[] GetFactory( + ContextControllerFactoryServiceContext serviceContext, + ContextStateCache contextStateCache) + { + if (!(serviceContext.Detail is ContextDetailNested)) + { + var factory = BuildContextFactory( + serviceContext, serviceContext.ContextName, serviceContext.Detail, 1, 1, null, contextStateCache); + factory.ValidateFactory(); + return new ContextControllerFactory[] + { + factory + }; + } + return BuildNestedContextFactories(serviceContext, contextStateCache); + } + + private static ContextControllerFactory[] BuildNestedContextFactories( + ContextControllerFactoryServiceContext serviceContext, + ContextStateCache contextStateCache) + { + var nestedSpec = (ContextDetailNested) serviceContext.Detail; + // determine nested filter use + IDictionary> filtersPerNestedContext = null; + for (var i = 0; i < nestedSpec.Contexts.Count; i++) + { + var contextParent = nestedSpec.Contexts[i]; + for (var j = i + 1; j < nestedSpec.Contexts.Count; j++) + { + var contextControlled = nestedSpec.Contexts[j]; + var specs = contextControlled.FilterSpecs; + if (specs == null) + { + continue; + } + if (filtersPerNestedContext == null) + { + filtersPerNestedContext = new Dictionary>(); + } + var existing = filtersPerNestedContext.Get(contextParent); + if (existing != null) + { + existing.AddAll(specs); + } + else + { + filtersPerNestedContext.Put(contextParent, specs); + } + } + } + + // create contexts + var namesUsed = new HashSet(); + var hierarchy = new ContextControllerFactory[nestedSpec.Contexts.Count]; + for (var i = 0; i < nestedSpec.Contexts.Count; i++) + { + var context = nestedSpec.Contexts[i]; + + if (namesUsed.Contains(context.ContextName)) + { + throw new ExprValidationException( + "Context by name '" + context.ContextName + + "' has already been declared within nested context '" + serviceContext.ContextName + "'"); + } + namesUsed.Add(context.ContextName); + + var nestingLevel = i + 1; + + IList optFiltersNested = null; + if (filtersPerNestedContext != null) + { + optFiltersNested = filtersPerNestedContext.Get(context); + } + + hierarchy[i] = BuildContextFactory( + serviceContext, context.ContextName, context.ContextDetail, nestingLevel, nestedSpec.Contexts.Count, + optFiltersNested, contextStateCache); + hierarchy[i].ValidateFactory(); + } + return hierarchy; + } + + private static ContextControllerFactory BuildContextFactory( + ContextControllerFactoryServiceContext serviceContext, + string contextName, + ContextDetail detail, + int nestingLevel, + int numNestingLevels, + IList optFiltersNested, + ContextStateCache contextStateCache) + { + var factoryContext = + new ContextControllerFactoryContext( + serviceContext.ContextName, contextName, serviceContext.ServicesContext, + serviceContext.AgentInstanceContextCreate, nestingLevel, numNestingLevels, + serviceContext.IsRecoveringResilient, contextStateCache); + return BuildContextFactory(factoryContext, detail, optFiltersNested, contextStateCache); + } + + private static ContextControllerFactory BuildContextFactory( + ContextControllerFactoryContext factoryContext, + ContextDetail detail, + IList optFiltersNested, + ContextStateCache contextStateCache) + { + return factoryContext.ServicesContext.ContextControllerFactoryFactorySvc.Make( + factoryContext, detail, optFiltersNested); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerFactoryService.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerFactoryService.cs new file mode 100755 index 000000000..07527b002 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerFactoryService.cs @@ -0,0 +1,16 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.core.context.mgr +{ + public interface ContextControllerFactoryService + { + ContextControllerFactory[] GetFactory(ContextControllerFactoryServiceContext context) ; + ContextPartitionIdManager AllocatePartitionIdMgr(string contextName, int contextStmtId); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerFactoryServiceContext.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerFactoryServiceContext.cs new file mode 100755 index 000000000..71dad6913 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerFactoryServiceContext.cs @@ -0,0 +1,49 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.core.context.mgr +{ + public class ContextControllerFactoryServiceContext + { + public ContextControllerFactoryServiceContext( + String contextName, + EPServicesContext servicesContext, + ContextDetail detail, + AgentInstanceContext agentInstanceContextCreate, + bool isRecoveringResilient, + EventType statementResultEventType) + { + ContextName = contextName; + ServicesContext = servicesContext; + Detail = detail; + AgentInstanceContextCreate = agentInstanceContextCreate; + IsRecoveringResilient = isRecoveringResilient; + StatementResultEventType = statementResultEventType; + } + + public string ContextName { get; private set; } + + public EPServicesContext ServicesContext { get; private set; } + + public ContextDetail Detail { get; private set; } + + public AgentInstanceContext AgentInstanceContextCreate { get; private set; } + + public bool IsRecoveringResilient { get; private set; } + + public EventType StatementResultEventType { get; private set; } + + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerFactoryServiceImpl.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerFactoryServiceImpl.cs new file mode 100755 index 000000000..b870d4165 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerFactoryServiceImpl.cs @@ -0,0 +1,31 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.core.context.mgr +{ + public class ContextControllerFactoryServiceImpl : ContextControllerFactoryService + { + private readonly static ContextStateCache CACHE_NO_SAVE = new ContextStateCacheNoSave(); + + public readonly static ContextControllerFactoryServiceImpl DEFAULT_FACTORY = new ContextControllerFactoryServiceImpl(CACHE_NO_SAVE); + + private readonly ContextStateCache _cache; + + public ContextControllerFactoryServiceImpl(ContextStateCache cache) { + _cache = cache; + } + + public ContextControllerFactory[] GetFactory(ContextControllerFactoryServiceContext serviceContext) { + return ContextControllerFactoryHelper.GetFactory(serviceContext, _cache); + } + + public ContextPartitionIdManager AllocatePartitionIdMgr(string contextName, int contextStmtId) { + return new ContextPartitionIdManagerImpl(); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerHash.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerHash.cs new file mode 100755 index 000000000..ee94496c4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerHash.cs @@ -0,0 +1,303 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.client.context; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.type; + +namespace com.espertech.esper.core.context.mgr +{ + public class ContextControllerHash + : ContextController + , ContextControllerHashedInstanceCallback + { + private readonly int _pathId; + private readonly ContextControllerLifecycleCallback _activationCallback; + private readonly ContextControllerHashFactoryImpl _factory; + + private readonly IList _filterCallbacks = new List(); + private readonly IDictionary _partitionKeys = new Dictionary(); + + private ContextInternalFilterAddendum _activationFilterAddendum; + private int _currentSubpathId; + + public ContextControllerHash(int pathId, ContextControllerLifecycleCallback activationCallback, ContextControllerHashFactoryImpl factory) + { + _pathId = pathId; + _activationCallback = activationCallback; + _factory = factory; + } + + public void ImportContextPartitions(ContextControllerState state, int pathIdToUse, ContextInternalFilterAddendum filterAddendum, AgentInstanceSelector agentInstanceSelector) + { + InitializeFromState(null, null, state, pathIdToUse, agentInstanceSelector, true); + } + + public void DeletePath(ContextPartitionIdentifier identifier) + { + var hash = (ContextPartitionIdentifierHash)identifier; + _partitionKeys.Remove(hash.Hash); + } + + public void VisitSelectedPartitions(ContextPartitionSelector contextPartitionSelector, ContextPartitionVisitor visitor) + { + int nestingLevel = _factory.FactoryContext.NestingLevel; + + if (contextPartitionSelector is ContextPartitionSelectorHash) + { + var hash = (ContextPartitionSelectorHash)contextPartitionSelector; + if (hash.Hashes == null || hash.Hashes.IsEmpty()) + { + return; + } + foreach (int hashCode in hash.Hashes) + { + var handle = _partitionKeys.Get(hashCode); + if (handle != null) + { + visitor.Visit(nestingLevel, _pathId, _factory.Binding, hashCode, this, handle); + } + } + return; + } + if (contextPartitionSelector is ContextPartitionSelectorFiltered) + { + var filter = (ContextPartitionSelectorFiltered)contextPartitionSelector; + var identifierHash = new ContextPartitionIdentifierHash(); + foreach (var entry in _partitionKeys) + { + identifierHash.Hash = entry.Key; + identifierHash.ContextPartitionId = entry.Value.ContextPartitionOrPathId; + if (filter.Filter(identifierHash)) + { + visitor.Visit(nestingLevel, _pathId, _factory.Binding, entry.Key, this, entry.Value); + } + } + return; + } + if (contextPartitionSelector is ContextPartitionSelectorAll) + { + foreach (var entry in _partitionKeys) + { + visitor.Visit(nestingLevel, _pathId, _factory.Binding, entry.Key, this, entry.Value); + } + return; + } + if (contextPartitionSelector is ContextPartitionSelectorById) + { + var byId = (ContextPartitionSelectorById)contextPartitionSelector; + foreach (var entry in _partitionKeys) + { + int cpid = entry.Value.ContextPartitionOrPathId; + if (byId.ContextPartitionIds.Contains(cpid)) + { + visitor.Visit(nestingLevel, _pathId, _factory.Binding, entry.Key, this, entry.Value); + } + } + return; + } + throw ContextControllerSelectorUtil.GetInvalidSelector(new Type[] { typeof(ContextPartitionSelectorHash) }, contextPartitionSelector); + } + + public void Activate(EventBean optionalTriggeringEvent, IDictionary optionalTriggeringPattern, ContextControllerState controllerState, ContextInternalFilterAddendum activationFilterAddendum, int? importPathId) + { + ContextControllerFactoryContext factoryContext = _factory.FactoryContext; + _activationFilterAddendum = activationFilterAddendum; + + if (factoryContext.NestingLevel == 1) + { + controllerState = ContextControllerStateUtil.GetRecoveryStates(_factory.FactoryContext.StateCache, factoryContext.OutermostContextName); + } + if (controllerState == null) + { + + // handle preallocate + if (_factory.HashedSpec.IsPreallocate) + { + for (int i = 0; i < _factory.HashedSpec.Granularity; i++) + { + var properties = ContextPropertyEventType.GetHashBean(factoryContext.ContextName, i); + _currentSubpathId++; + + // merge filter addendum, if any + var filterAddendumToUse = activationFilterAddendum; + if (_factory.HasFiltersSpecsNestedContexts) + { + filterAddendumToUse = activationFilterAddendum != null ? activationFilterAddendum.DeepCopy() : new ContextInternalFilterAddendum(); + _factory.PopulateContextInternalFilterAddendums(filterAddendumToUse, i); + } + + ContextControllerInstanceHandle handle = _activationCallback.ContextPartitionInstantiate(null, _currentSubpathId, null, this, optionalTriggeringEvent, null, i, properties, controllerState, filterAddendumToUse, _factory.FactoryContext.IsRecoveringResilient, ContextPartitionState.STARTED); + _partitionKeys.Put(i, handle); + + _factory.FactoryContext.StateCache.AddContextPath( + _factory.FactoryContext.OutermostContextName, + _factory.FactoryContext.NestingLevel, + _pathId, _currentSubpathId, handle.ContextPartitionOrPathId, i, + _factory.Binding); + } + return; + } + + // start filters if not preallocated + ActivateFilters(optionalTriggeringEvent); + + return; + } + + // initialize from existing state + int pathIdToUse = importPathId ?? _pathId; + InitializeFromState(optionalTriggeringEvent, optionalTriggeringPattern, controllerState, pathIdToUse, null, false); + + // activate filters + if (!_factory.HashedSpec.IsPreallocate) + { + ActivateFilters(null); + } + } + + protected void ActivateFilters(EventBean optionalTriggeringEvent) + { + var factoryContext = _factory.FactoryContext; + foreach (var item in _factory.HashedSpec.Items) + { + var callback = new ContextControllerHashedFilterCallback(factoryContext.ServicesContext, factoryContext.AgentInstanceContextCreate, item, this, _activationFilterAddendum); + _filterCallbacks.Add(callback); + + if (optionalTriggeringEvent != null) + { + var match = StatementAgentInstanceUtil.EvaluateFilterForStatement(factoryContext.ServicesContext, optionalTriggeringEvent, factoryContext.AgentInstanceContextCreate, callback.FilterHandle); + if (match) + { + callback.MatchFound(optionalTriggeringEvent, null); + } + } + } + } + + public void Create(int id, EventBean theEvent) + { + lock (this) + { + ContextControllerFactoryContext factoryContext = _factory.FactoryContext; + if (_partitionKeys.ContainsKey(id)) + { + return; + } + + IDictionary properties = ContextPropertyEventType.GetHashBean(factoryContext.ContextName, id); + _currentSubpathId++; + + // merge filter addendum, if any + ContextInternalFilterAddendum filterAddendumToUse = _activationFilterAddendum; + if (_factory.HasFiltersSpecsNestedContexts) + { + filterAddendumToUse = _activationFilterAddendum != null ? _activationFilterAddendum.DeepCopy() : new ContextInternalFilterAddendum(); + _factory.PopulateContextInternalFilterAddendums(filterAddendumToUse, id); + } + + ContextControllerInstanceHandle handle = _activationCallback.ContextPartitionInstantiate(null, _currentSubpathId, null, this, theEvent, null, id, properties, null, filterAddendumToUse, _factory.FactoryContext.IsRecoveringResilient, ContextPartitionState.STARTED); + _partitionKeys.Put(id, handle); + _factory.FactoryContext.StateCache.AddContextPath(factoryContext.OutermostContextName, factoryContext.NestingLevel, _pathId, _currentSubpathId, handle.ContextPartitionOrPathId, id, _factory.Binding); + + // update the filter version for this handle + long filterVersion = factoryContext.ServicesContext.FilterService.FiltersVersion; + _factory.FactoryContext.AgentInstanceContextCreate.EpStatementAgentInstanceHandle.StatementFilterVersion.StmtFilterVersion = filterVersion; + } + } + + public ContextControllerFactory Factory + { + get { return _factory; } + } + + public int PathId + { + get { return _pathId; } + } + + public void Deactivate() + { + var factoryContext = _factory.FactoryContext; + foreach (var callback in _filterCallbacks) + { + callback.Destroy(factoryContext.ServicesContext.FilterService); + } + _partitionKeys.Clear(); + _filterCallbacks.Clear(); + _factory.StateCache.RemoveContextParentPath(factoryContext.OutermostContextName, factoryContext.NestingLevel, _pathId); + } + + private void InitializeFromState( + EventBean optionalTriggeringEvent, + IDictionary optionalTriggeringPattern, + ContextControllerState controllerState, + int pathIdToUse, + AgentInstanceSelector agentInstanceSelector, + bool loadingExistingState) + { + var factoryContext = _factory.FactoryContext; + var states = controllerState.States; + var childContexts = ContextControllerStateUtil.GetChildContexts(factoryContext, pathIdToUse, states); + + var maxSubpathId = int.MinValue; + + foreach (var entry in childContexts) + { + var hashAlgoGeneratedId = (int?)_factory.Binding.ByteArrayToObject(entry.Value.Blob, null); + + // merge filter addendum, if any + var filterAddendumToUse = _activationFilterAddendum; + if (_factory.HasFiltersSpecsNestedContexts) + { + filterAddendumToUse = _activationFilterAddendum != null + ? _activationFilterAddendum.DeepCopy() + : new ContextInternalFilterAddendum(); + _factory.PopulateContextInternalFilterAddendums(filterAddendumToUse, hashAlgoGeneratedId); + } + + // check if exists already + if (controllerState.IsImported) + { + var existingHandle = _partitionKeys.Get(hashAlgoGeneratedId.Value); + if (existingHandle != null) + { + _activationCallback.ContextPartitionNavigate( + existingHandle, this, controllerState, entry.Value.OptionalContextPartitionId.Value, + filterAddendumToUse, agentInstanceSelector, entry.Value.Blob, loadingExistingState); + continue; + } + } + + var properties = ContextPropertyEventType.GetHashBean(factoryContext.ContextName, hashAlgoGeneratedId.Value); + + var assignedSubPathId = !controllerState.IsImported ? entry.Key.SubPath : ++_currentSubpathId; + var handle = _activationCallback.ContextPartitionInstantiate( + entry.Value.OptionalContextPartitionId, assignedSubPathId, entry.Key.SubPath, this, + optionalTriggeringEvent, optionalTriggeringPattern, hashAlgoGeneratedId, properties, controllerState, + filterAddendumToUse, loadingExistingState || factoryContext.IsRecoveringResilient, entry.Value.State); + _partitionKeys.Put(hashAlgoGeneratedId.Value, handle); + + if (entry.Key.SubPath > maxSubpathId) + { + maxSubpathId = assignedSubPathId; + } + } + if (!controllerState.IsImported) + { + _currentSubpathId = maxSubpathId != int.MinValue ? maxSubpathId : 0; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerHashFactoryBase.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerHashFactoryBase.cs new file mode 100755 index 000000000..8bd0be8f7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerHashFactoryBase.cs @@ -0,0 +1,391 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.client.context; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.stmt; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.spec.util; +using com.espertech.esper.events; +using com.espertech.esper.filter; + +namespace com.espertech.esper.core.context.mgr +{ + public abstract class ContextControllerHashFactoryBase + : ContextControllerFactoryBase + , ContextControllerFactory + { + private readonly ContextDetailHash _hashedSpec; + private readonly IList _filtersSpecsNestedContexts; + private IDictionary _contextBuiltinProps; + + private readonly IDictionary _nonPropertyExpressions = + new Dictionary(); + + protected ContextControllerHashFactoryBase( + ContextControllerFactoryContext factoryContext, + ContextDetailHash hashedSpec, + IList filtersSpecsNestedContexts) + : base(factoryContext) + { + _hashedSpec = hashedSpec; + _filtersSpecsNestedContexts = filtersSpecsNestedContexts; + } + + // Compare filters in statement with filters in segmented context, addendum filter compilation + public static void GetAddendumFilters( + IDictionary addendums, + int hashCode, + IList filtersSpecs, + ContextDetailHash hashSpec, + ContextControllerStatementDesc statementDesc) + { + foreach (var filtersSpec in filtersSpecs) + { + var addendum = GetAddendumFilters(filtersSpec, hashCode, hashSpec, statementDesc); + if (addendum == null) + { + continue; + } + + var existing = addendums.Get(filtersSpec); + if (existing != null) + { + addendum = ContextControllerAddendumUtil.MultiplyAddendum(existing, addendum); + } + addendums.Put(filtersSpec, addendum); + } + } + + public static FilterValueSetParam[][] GetAddendumFilters( + FilterSpecCompiled filterSpecCompiled, + int hashCode, + ContextDetailHash hashSpec, + ContextControllerStatementDesc statementDesc) + { + + // determine whether create-named-window + var isCreateWindow = statementDesc != null && statementDesc.Statement.StatementSpec.CreateWindowDesc != null; + ContextDetailHashItem foundPartition = null; + + if (!isCreateWindow) + { + foundPartition = FindHashItemSpec(hashSpec, filterSpecCompiled); + } + else + { + string declaredAsName = statementDesc.Statement.StatementSpec.CreateWindowDesc.AsEventTypeName; + foreach (var partitionItem in hashSpec.Items) + { + if (partitionItem.FilterSpecCompiled.FilterForEventType.Name.Equals(declaredAsName)) + { + foundPartition = partitionItem; + break; + } + } + } + + if (foundPartition == null) + { + return null; + } + + var filter = new FilterValueSetParamImpl(foundPartition.Lookupable, FilterOperator.EQUAL, hashCode); + + var addendum = new FilterValueSetParam[1][]; + addendum[0] = new FilterValueSetParam[] + { + filter + }; + + var partitionFilters = foundPartition.ParametersCompiled; + if (partitionFilters != null) + { + addendum = ContextControllerAddendumUtil.AddAddendum(partitionFilters, filter); + } + return addendum; + } + + public static ContextDetailHashItem FindHashItemSpec(ContextDetailHash hashSpec, FilterSpecCompiled filterSpec) + { + ContextDetailHashItem foundPartition = null; + foreach (var partitionItem in hashSpec.Items) + { + var typeOrSubtype = EventTypeUtility.IsTypeOrSubTypeOf( + filterSpec.FilterForEventType, partitionItem.FilterSpecCompiled.FilterForEventType); + if (typeOrSubtype) + { + foundPartition = partitionItem; + } + } + + return foundPartition; + } + + public bool HasFiltersSpecsNestedContexts + { + get { return _filtersSpecsNestedContexts != null && !_filtersSpecsNestedContexts.IsEmpty(); } + } + + public override void ValidateFactory() + { + ValidatePopulateContextDesc(); + _contextBuiltinProps = ContextPropertyEventType.GetHashType(); + } + + public override ContextControllerStatementCtxCache ValidateStatement(ContextControllerStatementBase statement) + { + var factoryContext = base.FactoryContext; + var streamAnalysis = + StatementSpecCompiledAnalyzer.AnalyzeFilters(statement.StatementSpec); + ContextControllerPartitionedUtil.ValidateStatementForContext( + factoryContext.ContextName, statement, streamAnalysis, GetItemEventTypes(_hashedSpec), + factoryContext.ServicesContext.NamedWindowMgmtService); + // register non-property expression to be able to recreated indexes + foreach (var entry in _nonPropertyExpressions) + { + factoryContext.ServicesContext.FilterNonPropertyRegisteryService.RegisterNonPropertyExpression( + statement.StatementContext.StatementName, entry.Key, entry.Value); + } + return new ContextControllerStatementCtxCacheFilters(streamAnalysis.Filters); + } + + public override void PopulateFilterAddendums( + IDictionary filterAddendum, + ContextControllerStatementDesc statement, + Object key, + int contextId) + { + var factoryContext = base.FactoryContext; + var statementInfo = (ContextControllerStatementCtxCacheFilters) statement.Caches[factoryContext.NestingLevel - 1]; + var assignedContextPartition = (int) key; + var code = assignedContextPartition%_hashedSpec.Granularity; + GetAddendumFilters(filterAddendum, code, statementInfo.FilterSpecs, _hashedSpec, statement); + } + + public void PopulateContextInternalFilterAddendums(ContextInternalFilterAddendum filterAddendum, Object key) + { + var assignedContextPartition = (int) key; + var code = assignedContextPartition%_hashedSpec.Granularity; + GetAddendumFilters(filterAddendum.FilterAddendum, code, _filtersSpecsNestedContexts, _hashedSpec, null); + } + + public override FilterSpecLookupable GetFilterLookupable(EventType eventType) + { + foreach (var hashItem in _hashedSpec.Items) + { + if (hashItem.FilterSpecCompiled.FilterForEventType == eventType) + { + return hashItem.Lookupable; + } + } + return null; + } + + public override bool IsSingleInstanceContext + { + get { return false; } + } + + public override StatementAIResourceRegistryFactory StatementAIResourceRegistryFactory + { + get + { + if (_hashedSpec.Granularity <= 65536) + { + return () => new StatementAIResourceRegistry( + new AIRegistryAggregationMultiPerm(), new AIRegistryExprMultiPerm()); + } + else + { + return () => new StatementAIResourceRegistry( + new AIRegistryAggregationMap(), new AIRegistryExprMap()); + } + } + } + + public override IList ContextDetailPartitionItems + { + get { return Collections.GetEmptyList(); } + } + + public override ContextDetail ContextDetail + { + get { return _hashedSpec; } + } + + public ContextDetailHash HashedSpec + { + get { return _hashedSpec; } + } + + public override IDictionary ContextBuiltinProps + { + get { return _contextBuiltinProps; } + } + + public override ContextPartitionIdentifier KeyPayloadToIdentifier(Object payload) + { + return new ContextPartitionIdentifierHash(payload.AsInt()); + } + + private ICollection GetItemEventTypes(ContextDetailHash hashedSpec) + { + return hashedSpec.Items + .Select(item => item.FilterSpecCompiled.FilterForEventType) + .ToList(); + } + + private void ValidatePopulateContextDesc() + { + + if (_hashedSpec.Items.IsEmpty()) + { + throw new ExprValidationException("EmptyFalse list of hash items"); + } + + var factoryContext = base.FactoryContext; + foreach (var item in _hashedSpec.Items) + { + if (item.Function.Parameters.IsEmpty()) + { + throw new ExprValidationException( + "For context '" + factoryContext.ContextName + + "' expected one or more parameters to the hash function, but found no parameter list"); + } + + // determine type of hash to use + var hashFuncName = item.Function.Name; + var hashFunction = HashFunctionEnumExtensions.Determine(factoryContext.ContextName, hashFuncName); + Pair hashSingleRowFunction = null; + if (hashFunction == null) + { + try + { + hashSingleRowFunction = + factoryContext.AgentInstanceContextCreate.StatementContext.EngineImportService + .ResolveSingleRow(hashFuncName); + } + catch (Exception) + { + // expected + } + + if (hashSingleRowFunction == null) + { + throw new ExprValidationException( + "For context '" + factoryContext.ContextName + "' expected a hash function that is any of {" + + HashFunctionEnumExtensions.StringList + + "} or a plug-in single-row function or script but received '" + hashFuncName + "'"); + } + } + + // get first parameter + var paramExpr = item.Function.Parameters[0]; + var eval = paramExpr.ExprEvaluator; + var paramType = eval.ReturnType; + EventPropertyGetter getter; + + if (hashFunction == HashFunctionEnum.CONSISTENT_HASH_CRC32) + { + if (item.Function.Parameters.Count > 1 || paramType != typeof (string)) + { + getter = + new ContextControllerHashedGetterCRC32Serialized( + factoryContext.AgentInstanceContextCreate.StatementContext.StatementName, + item.Function.Parameters, _hashedSpec.Granularity); + } + else + { + getter = new ContextControllerHashedGetterCRC32Single(eval, _hashedSpec.Granularity); + } + } + else if (hashFunction == HashFunctionEnum.HASH_CODE) + { + if (item.Function.Parameters.Count > 1) + { + getter = new ContextControllerHashedGetterHashMultiple( + item.Function.Parameters, _hashedSpec.Granularity); + } + else + { + getter = new ContextControllerHashedGetterHashSingle(eval, _hashedSpec.Granularity); + } + } + else if (hashSingleRowFunction != null) + { + getter = + new ContextControllerHashedGetterSingleRow( + factoryContext.AgentInstanceContextCreate.StatementContext.StatementName, hashFuncName, + hashSingleRowFunction, item.Function.Parameters, _hashedSpec.Granularity, + factoryContext.AgentInstanceContextCreate.StatementContext.EngineImportService, + item.FilterSpecCompiled.FilterForEventType, + factoryContext.AgentInstanceContextCreate.StatementContext.EventAdapterService, + factoryContext.AgentInstanceContextCreate.StatementId, + factoryContext.ServicesContext.TableService, + factoryContext.ServicesContext.EngineURI); + } + else + { + throw new ArgumentException("Unrecognized hash code function '" + hashFuncName + "'"); + } + + // create and register expression + var expression = item.Function.Name + "(" + + ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(paramExpr) + ")"; + var lookupable = new FilterSpecLookupable(expression, getter, typeof (int?), true); + item.Lookupable = lookupable; + factoryContext.ServicesContext.FilterNonPropertyRegisteryService.RegisterNonPropertyExpression( + factoryContext.AgentInstanceContextCreate.StatementName, item.FilterSpecCompiled.FilterForEventType, + lookupable); + _nonPropertyExpressions.Put(item.FilterSpecCompiled.FilterForEventType, lookupable); + } + } + + public enum HashFunctionEnum + { + CONSISTENT_HASH_CRC32, + HASH_CODE + } + + public static class HashFunctionEnumExtensions + { + public static HashFunctionEnum? Determine(String contextName, String name) + { + return EnumHelper.ParseBoxed(name, true); + } + + public static string StringList + { + get + { + var message = new StringWriter(); + var delimiter = ""; + foreach (var name in EnumHelper.GetNames()) + { + message.Write(delimiter); + message.Write(name.ToLower()); + delimiter = ", "; + } + return message.ToString(); + } + } + } + } + +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerHashFactoryImpl.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerHashFactoryImpl.cs new file mode 100755 index 000000000..998ebbce7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerHashFactoryImpl.cs @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.epl.spec; +using com.espertech.esper.filter; + +namespace com.espertech.esper.core.context.mgr +{ + public class ContextControllerHashFactoryImpl + : ContextControllerHashFactoryBase + , ContextControllerFactory + { + private readonly ContextStatePathValueBinding _binding; + + public ContextControllerHashFactoryImpl( + ContextControllerFactoryContext factoryContext, + ContextDetailHash hashedSpec, + IList filtersSpecsNestedContexts) + : base(factoryContext, hashedSpec, filtersSpecsNestedContexts) + { + _binding = factoryContext.StateCache.GetBinding(typeof(int)); + } + + public override ContextController CreateNoCallback(int pathId, ContextControllerLifecycleCallback callback) + { + return new ContextControllerHash(pathId, callback, this); + } + + public ContextStatePathValueBinding Binding + { + get { return _binding; } + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerHashedFilterCallback.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerHashedFilterCallback.cs new file mode 100755 index 000000000..0ab1c0ec1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerHashedFilterCallback.cs @@ -0,0 +1,77 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.spec; +using com.espertech.esper.filter; + +namespace com.espertech.esper.core.context.mgr +{ + public class ContextControllerHashedFilterCallback : FilterHandleCallback + { + private readonly AgentInstanceContext _agentInstanceContextCreateContext; + private readonly EventPropertyGetter _getter; + private readonly ContextControllerHashedInstanceCallback _callback; + private readonly EPStatementHandleCallback _filterHandle; + private readonly FilterServiceEntry _filterServiceEntry; + + public ContextControllerHashedFilterCallback( + EPServicesContext servicesContext, + AgentInstanceContext agentInstanceContextCreateContext, + ContextDetailHashItem hashItem, + ContextControllerHashedInstanceCallback callback, + ContextInternalFilterAddendum filterAddendum) + { + _agentInstanceContextCreateContext = agentInstanceContextCreateContext; + _callback = callback; + _getter = hashItem.Lookupable.Getter; + + _filterHandle = new EPStatementHandleCallback(agentInstanceContextCreateContext.EpStatementAgentInstanceHandle, this); + + FilterValueSetParam[][] addendum = filterAddendum != null ? filterAddendum.GetFilterAddendum(hashItem.FilterSpecCompiled) : null; + FilterValueSet filterValueSet = hashItem.FilterSpecCompiled.GetValueSet(null, null, addendum); + _filterServiceEntry = servicesContext.FilterService.Add(filterValueSet, _filterHandle); + + long filtersVersion = servicesContext.FilterService.FiltersVersion; + agentInstanceContextCreateContext.EpStatementAgentInstanceHandle.StatementFilterVersion.StmtFilterVersion = filtersVersion; + } + + public void MatchFound(EventBean theEvent, ICollection allStmtMatches) + { + int value = _getter.Get(theEvent).AsInt(); + _callback.Create(value, theEvent); + } + + public bool IsSubSelect + { + get { return false; } + } + + public int StatementId + { + get { return _agentInstanceContextCreateContext.StatementContext.StatementId; } + } + + public void Destroy(FilterService filterService) + { + filterService.Remove(_filterHandle, _filterServiceEntry); + var filtersVersion = _agentInstanceContextCreateContext.StatementContext.FilterService.FiltersVersion; + _agentInstanceContextCreateContext.EpStatementAgentInstanceHandle.StatementFilterVersion.StmtFilterVersion = filtersVersion; + } + + public EPStatementHandleCallback FilterHandle + { + get { return _filterHandle; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerHashedGetterCRC32Serialized.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerHashedGetterCRC32Serialized.cs new file mode 100755 index 000000000..7a7d598d7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerHashedGetterCRC32Serialized.cs @@ -0,0 +1,87 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.util; + +namespace com.espertech.esper.core.context.mgr +{ + public class ContextControllerHashedGetterCRC32Serialized : EventPropertyGetter + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly String _statementName; + private readonly ExprEvaluator[] _evaluators; + private readonly Serializer[] _serializers; + private readonly int _granularity; + + public ContextControllerHashedGetterCRC32Serialized(String statementName, IList nodes, int granularity) + { + _statementName = statementName; + _evaluators = new ExprEvaluator[nodes.Count]; + + var returnTypes = new Type[nodes.Count]; + for (int i = 0; i < nodes.Count; i++) + { + _evaluators[i] = nodes[i].ExprEvaluator; + returnTypes[i] = _evaluators[i].ReturnType; + } + + _serializers = SerializerFactory.GetSerializers(returnTypes); + _granularity = granularity; + } + + public Object Get(EventBean eventBean) + { + var events = new[] { eventBean }; + + var @params = new Object[_evaluators.Length]; + for (int i = 0; i < _serializers.Length; i++) + { + @params[i] = _evaluators[i].Evaluate(new EvaluateParams(events, true, null)); + } + + byte[] bytes; + try + { + bytes = SerializerFactory.Serialize(_serializers, @params); + } + catch (IOException e) + { + Log.Error("Exception serializing parameters for computing consistent hash for statement '" + _statementName + "': " + e.Message, e); + bytes = new byte[0]; + } + + var value = bytes.GetCrc32() % _granularity; + var result = value; + if (result >= 0) + { + return result; + } + + return -result; + } + + public bool IsExistsProperty(EventBean eventBean) + { + return false; + } + + public Object GetFragment(EventBean eventBean) + { + return null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerHashedGetterCRC32Single.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerHashedGetterCRC32Single.cs new file mode 100755 index 000000000..4ca72ccd1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerHashedGetterCRC32Single.cs @@ -0,0 +1,60 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.core.context.mgr +{ + public class ContextControllerHashedGetterCRC32Single : EventPropertyGetter + { + private readonly ExprEvaluator _eval; + private readonly int _granularity; + + public ContextControllerHashedGetterCRC32Single(ExprEvaluator eval, int granularity) { + _eval = eval; + _granularity = granularity; + } + + public Object Get(EventBean eventBean) + { + var events = new[] {eventBean}; + var code = (String) _eval.Evaluate(new EvaluateParams(events, true, null)); + + long value; + if (code == null) + { + value = 0; + } + else + { + value = code.GetCrc32() % _granularity; + } + + var result = (int) value; + if (result >= 0) { + return result; + } + return -result; + } + + public bool IsExistsProperty(EventBean eventBean) + { + return false; + } + + public Object GetFragment(EventBean eventBean) + { + return null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerHashedGetterHashMultiple.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerHashedGetterHashMultiple.cs new file mode 100755 index 000000000..bb90d3d72 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerHashedGetterHashMultiple.cs @@ -0,0 +1,79 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.core.context.mgr +{ + public class ContextControllerHashedGetterHashMultiple : EventPropertyGetter + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly ExprEvaluator[] _evaluators; + private readonly int _granularity; + + public ContextControllerHashedGetterHashMultiple(IList nodes, int granularity) + { + _evaluators = new ExprEvaluator[nodes.Count]; + for (int i = 0; i < nodes.Count; i++) + { + _evaluators[i] = nodes[i].ExprEvaluator; + } + _granularity = granularity; + } + + public Object Get(EventBean eventBean) + { + var events = new EventBean[] + { + eventBean + }; + var evaluateParams = new EvaluateParams(events, true, null); + + int hashCode = 0; + for (int i = 0; i < _evaluators.Length; i++) + { + object result = _evaluators[i].Evaluate(evaluateParams); + if (result == null) + { + continue; + } + if (hashCode == 0) + { + hashCode = result.GetHashCode(); + } + else + { + hashCode = 31*hashCode + result.GetHashCode(); + } + } + + if (hashCode >= 0) + { + return hashCode%_granularity; + } + return -hashCode%_granularity; + } + + public bool IsExistsProperty(EventBean eventBean) + { + return false; + } + + public Object GetFragment(EventBean eventBean) + { + return null; + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerHashedGetterHashSingle.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerHashedGetterHashSingle.cs new file mode 100755 index 000000000..15a1af326 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerHashedGetterHashSingle.cs @@ -0,0 +1,63 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using com.espertech.esper.client; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.core.context.mgr +{ + public class ContextControllerHashedGetterHashSingle : EventPropertyGetter + { + private readonly ExprEvaluator _eval; + private readonly int _granularity; + + public ContextControllerHashedGetterHashSingle(ExprEvaluator eval, int granularity) + { + _eval = eval; + _granularity = granularity; + } + + #region EventPropertyGetter Members + + public Object Get(EventBean eventBean) + { + var events = new[] {eventBean}; + object code = _eval.Evaluate(new EvaluateParams(events, true, null)); + + int value; + if (code == null) + { + value = 0; + } + else + { + value = code.GetHashCode()%_granularity; + } + + if (value >= 0) + { + return value; + } + return -value; + } + + public bool IsExistsProperty(EventBean eventBean) + { + return false; + } + + public Object GetFragment(EventBean eventBean) + { + return null; + } + + #endregion + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerHashedGetterSingleRow.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerHashedGetterSingleRow.cs new file mode 100755 index 000000000..4f4cdc872 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerHashedGetterSingleRow.cs @@ -0,0 +1,110 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.events; +using com.espertech.esper.util; + +using XLR8.CGLib; + +namespace com.espertech.esper.core.context.mgr +{ + public class ContextControllerHashedGetterSingleRow : EventPropertyGetter + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly string _statementName; + private readonly FastMethod _fastMethod; + private readonly ExprEvaluator[] _evaluators; + private readonly int _granularity; + + public ContextControllerHashedGetterSingleRow( + string statementName, + string functionName, + Pair func, + IList parameters, + int granularity, + EngineImportService engineImportService, + EventType eventType, + EventAdapterService eventAdapterService, + int statementId, + TableService tableService, + string engineURI) + { + ExprNodeUtilMethodDesc staticMethodDesc = ExprNodeUtility.ResolveMethodAllowWildcardAndStream( + func.First.Name, null, + func.Second.MethodName, + parameters, + engineImportService, + eventAdapterService, + statementId, true, + eventType, + new ExprNodeUtilResolveExceptionHandlerDefault(func.Second.MethodName, true), + func.Second.MethodName, + tableService, engineURI); + _statementName = statementName; + _evaluators = staticMethodDesc.ChildEvals; + _granularity = granularity; + _fastMethod = staticMethodDesc.FastMethod; + } + + public Object Get(EventBean eventBean) + { + var events = new EventBean[]{eventBean}; + var evaluateParams = new EvaluateParams(events, true, null); + var parameters = new Object[_evaluators.Length]; + for (int i = 0; i < _evaluators.Length; i++) + { + parameters[i] = _evaluators[i].Evaluate(evaluateParams); + } + + try + { + Object result = _fastMethod.Invoke(null, parameters); + if (result == null) + { + return 0; + } + int value = result.AsInt(); + if (value >= 0) + { + return value%_granularity; + } + return -value%_granularity; + } + catch (TargetInvocationException e) + { + var message = TypeHelper.GetMessageInvocationTarget( + _statementName, _fastMethod.Target, _fastMethod.DeclaringType.TargetType.FullName, parameters, e); + Log.Error(message, e.InnerException); + } + + return 0; + } + + public bool IsExistsProperty(EventBean eventBean) + { + return false; + } + + public Object GetFragment(EventBean eventBean) + { + return null; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerHashedInstanceCallback.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerHashedInstanceCallback.cs new file mode 100755 index 000000000..e405fc218 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerHashedInstanceCallback.cs @@ -0,0 +1,17 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; + +namespace com.espertech.esper.core.context.mgr +{ + public interface ContextControllerHashedInstanceCallback + { + void Create(int id, EventBean theEvent); + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerInitTerm.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerInitTerm.cs new file mode 100755 index 000000000..1cdf3e1e7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerInitTerm.cs @@ -0,0 +1,696 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.client.context; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.threading; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; +using com.espertech.esper.filter; +using com.espertech.esper.pattern; +using com.espertech.esper.schedule; + +namespace com.espertech.esper.core.context.mgr +{ + public class ContextControllerInitTerm + : ContextController + , ContextControllerConditionCallback + { + private readonly int _pathId; + protected readonly ContextControllerLifecycleCallback ActivationCallback; + private readonly ContextControllerInitTermFactoryImpl _factory; + + protected ContextControllerCondition StartCondition; + private readonly IDictionary _distinctContexts; + private EventBean _nonDistinctLastTrigger; + private readonly ExprEvaluator[] _distinctEvaluators; + private readonly EventBean[] _eventsPerStream = new EventBean[1]; + + protected IDictionary EndConditions = + new LinkedHashMap(); + + protected int CurrentSubpathId; + + internal IDictionary DistinctContexts + { + get { return _distinctContexts; } + } + + internal ExprEvaluator[] DistinctEvaluators + { + get { return _distinctEvaluators; } + } + + public ContextControllerInitTerm(int pathId, ContextControllerLifecycleCallback lifecycleCallback, ContextControllerInitTermFactoryImpl factory) + { + _pathId = pathId; + ActivationCallback = lifecycleCallback; + _factory = factory; + + var contextDetail = factory.ContextDetail as ContextDetailInitiatedTerminated; + if (contextDetail != null && contextDetail.DistinctExpressions != null && contextDetail.DistinctExpressions.Length > 0) + { + _distinctContexts = new Dictionary().WithNullSupport(); + _distinctEvaluators = ExprNodeUtility.GetEvaluators(contextDetail.DistinctExpressions); + } + } + + public void ImportContextPartitions(ContextControllerState state, int pathIdToUse, ContextInternalFilterAddendum filterAddendum, AgentInstanceSelector agentInstanceSelector) + { + InitializeFromState(null, null, filterAddendum, state, pathIdToUse, agentInstanceSelector, true); + } + + public void DeletePath(ContextPartitionIdentifier identifier) + { + var initterm = (ContextPartitionIdentifierInitiatedTerminated)identifier; + foreach (var entry in EndConditions) + { + if (Compare(initterm.StartTime, initterm.Properties, initterm.EndTime, + entry.Value.StartTime, entry.Value.StartProperties, entry.Value.EndTime)) + { + entry.Key.Deactivate(); + EndConditions.Remove(entry.Key); + RemoveDistinctKey(entry.Value); + break; + } + } + } + + public void Activate(EventBean optionalTriggeringEvent, IDictionary optionalTriggeringPattern, ContextControllerState controllerState, ContextInternalFilterAddendum filterAddendum, int? importPathId) + { + if (_factory.FactoryContext.NestingLevel == 1) + { + controllerState = ContextControllerStateUtil.GetRecoveryStates(_factory.FactoryContext.StateCache, _factory.FactoryContext.OutermostContextName); + } + + bool currentlyRunning; + var contextDetailInitiatedTerminated = _factory.ContextDetailInitiatedTerminated; + if (controllerState == null) + { + StartCondition = MakeEndpoint(contextDetailInitiatedTerminated.Start, filterAddendum, true, 0); + + // if this is single-instance mode, check if we are currently running according to schedule + currentlyRunning = StartCondition.IsImmediate; + if (!contextDetailInitiatedTerminated.IsOverlapping) + { + currentlyRunning = DetermineCurrentlyRunning(StartCondition); + } + + if (currentlyRunning) + { + CurrentSubpathId++; + var endEndpoint = MakeEndpoint(contextDetailInitiatedTerminated.End, filterAddendum, false, CurrentSubpathId); + endEndpoint.Activate(optionalTriggeringEvent, null, 0, _factory.FactoryContext.IsRecoveringResilient); + var startTime = _factory.SchedulingService.Time; + var endTime = endEndpoint.ExpectedEndTime; + var builtinProps = GetBuiltinProperties(_factory.FactoryContext.ContextName, startTime, endTime, Collections.GetEmptyMap()); + var instanceHandle = ActivationCallback.ContextPartitionInstantiate(null, CurrentSubpathId, null, this, optionalTriggeringEvent, optionalTriggeringPattern, null, builtinProps, controllerState, filterAddendum, _factory.FactoryContext.IsRecoveringResilient, ContextPartitionState.STARTED); + EndConditions.Put(endEndpoint, new ContextControllerInitTermInstance(instanceHandle, null, startTime, endTime, CurrentSubpathId)); + + var state = new ContextControllerInitTermState(_factory.FactoryContext.ServicesContext.SchedulingService.Time, builtinProps); + _factory.FactoryContext.StateCache.AddContextPath(_factory.FactoryContext.OutermostContextName, _factory.FactoryContext.NestingLevel, _pathId, CurrentSubpathId, instanceHandle.ContextPartitionOrPathId, state, _factory.Binding); + } + + // non-overlapping and not currently running, or overlapping + if ((!contextDetailInitiatedTerminated.IsOverlapping && !currentlyRunning) || + contextDetailInitiatedTerminated.IsOverlapping) + { + StartCondition.Activate(optionalTriggeringEvent, null, 0, _factory.FactoryContext.IsRecoveringResilient); + } + return; + } + + StartCondition = MakeEndpoint(contextDetailInitiatedTerminated.Start, filterAddendum, true, 0); + + // if this is single-instance mode, check if we are currently running according to schedule + currentlyRunning = false; + if (!contextDetailInitiatedTerminated.IsOverlapping) + { + currentlyRunning = DetermineCurrentlyRunning(StartCondition); + } + if (!currentlyRunning) + { + StartCondition.Activate(optionalTriggeringEvent, null, 0, _factory.FactoryContext.IsRecoveringResilient); + } + + int pathIdToUse = importPathId ?? _pathId; + InitializeFromState(optionalTriggeringEvent, optionalTriggeringPattern, filterAddendum, controllerState, pathIdToUse, null, false); + } + + protected ContextControllerCondition MakeEndpoint(ContextDetailCondition endpoint, ContextInternalFilterAddendum filterAddendum, bool isStartEndpoint, int subPathId) + { + return ContextControllerConditionFactory.GetEndpoint(_factory.FactoryContext.ContextName, _factory.FactoryContext.ServicesContext, _factory.FactoryContext.AgentInstanceContextCreate, + endpoint, this, filterAddendum, isStartEndpoint, + _factory.FactoryContext.NestingLevel, _pathId, subPathId); + } + + public void VisitSelectedPartitions(ContextPartitionSelector contextPartitionSelector, ContextPartitionVisitor visitor) + { + var nestingLevel = _factory.FactoryContext.NestingLevel; + if (contextPartitionSelector is ContextPartitionSelectorFiltered) + { + var filter = (ContextPartitionSelectorFiltered)contextPartitionSelector; + var identifier = new ContextPartitionIdentifierInitiatedTerminated(); + foreach (var entry in EndConditions) + { + identifier.EndTime = entry.Value.EndTime; + identifier.StartTime = entry.Value.StartTime; + identifier.Properties = entry.Value.StartProperties; + identifier.ContextPartitionId = entry.Value.InstanceHandle.ContextPartitionOrPathId; + if (filter.Filter(identifier)) + { + var state = new ContextControllerInitTermState(_factory.FactoryContext.ServicesContext.SchedulingService.Time, entry.Value.StartProperties); + visitor.Visit(nestingLevel, _pathId, _factory.Binding, state, this, entry.Value.InstanceHandle); + } + } + return; + } + if (contextPartitionSelector is ContextPartitionSelectorById) + { + var filter = (ContextPartitionSelectorById)contextPartitionSelector; + foreach (var entry in EndConditions) + { + if (filter.ContextPartitionIds.Contains(entry.Value.InstanceHandle.ContextPartitionOrPathId)) + { + var state = new ContextControllerInitTermState(_factory.FactoryContext.ServicesContext.SchedulingService.Time, entry.Value.StartProperties); + visitor.Visit(nestingLevel, _pathId, _factory.Binding, state, this, entry.Value.InstanceHandle); + } + } + return; + } + if (contextPartitionSelector is ContextPartitionSelectorAll) + { + foreach (var entry in EndConditions) + { + var state = new ContextControllerInitTermState(_factory.FactoryContext.ServicesContext.SchedulingService.Time, entry.Value.StartProperties); + visitor.Visit(nestingLevel, _pathId, _factory.Binding, state, this, entry.Value.InstanceHandle); + } + return; + } + throw ContextControllerSelectorUtil.GetInvalidSelector(new Type[0], contextPartitionSelector); + } + + public void RangeNotification( + IDictionary builtinProperties, + ContextControllerCondition originCondition, + EventBean optionalTriggeringEvent, + IDictionary optionalTriggeringPattern, + ContextInternalFilterAddendum filterAddendum) + { + var endConditionNotification = originCondition != StartCondition; + var startNow = StartCondition is ContextControllerConditionImmediate; + IList agentInstancesLocksHeld = null; + + _nonDistinctLastTrigger = optionalTriggeringEvent; + + ILockable tempLock = startNow + ? _factory.FactoryContext.ServicesContext.FilterService.WriteLock + : new VoidLock(); + + using (tempLock.Acquire()) + { + try + { + if (endConditionNotification) + { + + if (originCondition.IsRunning) + { + originCondition.Deactivate(); + } + + // indicate terminate + var instance = EndConditions.Delete(originCondition); + if (instance == null) + { + return; + } + + // For start-now (non-overlapping only) we hold the lock of the existing agent instance + // until the new one is ready. + if (startNow) + { + agentInstancesLocksHeld = new List(); + optionalTriggeringEvent = null; + // since we are restarting, we don't want to evaluate the event twice + optionalTriggeringPattern = null; + } + ActivationCallback.ContextPartitionTerminate( + instance.InstanceHandle, builtinProperties, startNow, agentInstancesLocksHeld); + + // remove distinct key + RemoveDistinctKey(instance); + + // re-activate start condition if not overlapping + if (!_factory.ContextDetailInitiatedTerminated.IsOverlapping) + { + StartCondition.Activate(optionalTriggeringEvent, null, 0, false); + } + + _factory.FactoryContext.StateCache.RemoveContextPath( + _factory.FactoryContext.OutermostContextName, _factory.FactoryContext.NestingLevel, _pathId, + instance.SubPathId); + } + + // handle start-condition notification + if (!endConditionNotification || startNow) + { + + // Check if this is distinct-only and the key already exists + if (_distinctContexts != null) + { + var added = AddDistinctKey(optionalTriggeringEvent); + if (!added) + { + return; + } + } + + // For single-instance mode, deactivate + if (!_factory.ContextDetailInitiatedTerminated.IsOverlapping) + { + if (StartCondition.IsRunning) + { + StartCondition.Deactivate(); + } + } + // For overlapping mode, make sure we activate again or stay activated + else + { + if (!StartCondition.IsRunning) + { + StartCondition.Activate(null, null, 0, _factory.FactoryContext.IsRecoveringResilient); + } + } + + CurrentSubpathId++; + var endEndpoint = MakeEndpoint( + _factory.ContextDetailInitiatedTerminated.End, filterAddendum, false, CurrentSubpathId); + var matchedEventMap = GetMatchedEventMap(builtinProperties); + endEndpoint.Activate(null, matchedEventMap, 0, false); + var startTime = _factory.SchedulingService.Time; + var endTime = endEndpoint.ExpectedEndTime; + var builtinProps = GetBuiltinProperties(_factory.FactoryContext.ContextName, startTime, endTime, builtinProperties); + var instanceHandle = ActivationCallback.ContextPartitionInstantiate( + null, CurrentSubpathId, null, this, optionalTriggeringEvent, optionalTriggeringPattern, + new ContextControllerInitTermState( + _factory.SchedulingService.Time, matchedEventMap.MatchingEventsAsMap), builtinProps, + null, filterAddendum, _factory.FactoryContext.IsRecoveringResilient, + ContextPartitionState.STARTED); + EndConditions.Put( + endEndpoint, + new ContextControllerInitTermInstance( + instanceHandle, builtinProperties, startTime, endTime, CurrentSubpathId)); + + // install filter fault handlers, if necessary + InstallFilterFaultHandler(instanceHandle); + + var state = + new ContextControllerInitTermState( + _factory.FactoryContext.ServicesContext.SchedulingService.Time, builtinProperties); + _factory.FactoryContext.StateCache.AddContextPath( + _factory.FactoryContext.OutermostContextName, _factory.FactoryContext.NestingLevel, _pathId, + CurrentSubpathId, instanceHandle.ContextPartitionOrPathId, state, _factory.Binding); + } + } + finally + { + if (agentInstancesLocksHeld != null) + { + foreach (var agentInstance in agentInstancesLocksHeld) + { + agentInstance.AgentInstanceContext.EpStatementAgentInstanceHandle.StatementFilterVersion.StmtFilterVersion = long.MaxValue; + if (agentInstance.AgentInstanceContext.StatementContext.EpStatementHandle.HasTableAccess) + { + agentInstance.AgentInstanceContext.TableExprEvaluatorContext.ReleaseAcquiredLocks(); + } + agentInstance.AgentInstanceContext.AgentInstanceLock.WriteLock.Release(); + } + } + } + } + } + + private void InstallFilterFaultHandler(ContextControllerInstanceHandle instanceHandle) + { + FilterFaultHandler myFaultHandler = null; + if (DistinctContexts != null) + { + myFaultHandler = new DistinctFilterFaultHandler(this); + } + else + { + if (StartCondition is ContextControllerConditionFilter) + { + myFaultHandler = new NonDistinctFilterFaultHandler(this); + } + } + + if (myFaultHandler != null && instanceHandle.Instances != null) + { + foreach (AgentInstance agentInstance in instanceHandle.Instances.AgentInstances) + { + agentInstance.AgentInstanceContext.EpStatementAgentInstanceHandle.FilterFaultHandler = myFaultHandler; + } + } + } + + protected MatchedEventMap GetMatchedEventMap(IDictionary builtinProperties) + { + var props = new Object[_factory.MatchedEventMapMeta.TagsPerIndex.Length]; + var count = 0; + foreach (var name in _factory.MatchedEventMapMeta.TagsPerIndex) + { + props[count++] = builtinProperties.Get(name); + } + return new MatchedEventMapImpl(_factory.MatchedEventMapMeta, props); + } + + protected bool DetermineCurrentlyRunning(ContextControllerCondition startCondition) + { + // we are not currently running if either of the endpoints is not crontab-triggered + var contextDetailInitiatedTerminated = _factory.ContextDetailInitiatedTerminated; + if ((contextDetailInitiatedTerminated.Start is ContextDetailConditionCrontab) && + ((contextDetailInitiatedTerminated.End is ContextDetailConditionCrontab))) + { + var scheduleStart = ((ContextDetailConditionCrontab)contextDetailInitiatedTerminated.Start).Schedule; + var scheduleEnd = ((ContextDetailConditionCrontab)contextDetailInitiatedTerminated.End).Schedule; + + var engineImportService = _factory.StatementContext.EngineImportService; + var nextScheduledStartTime = ScheduleComputeHelper.ComputeNextOccurance( + scheduleStart, _factory.TimeProvider.Time, engineImportService.TimeZone, + engineImportService.TimeAbacus); + long nextScheduledEndTime = ScheduleComputeHelper.ComputeNextOccurance( + scheduleEnd, _factory.TimeProvider.Time, engineImportService.TimeZone, + engineImportService.TimeAbacus); + + return nextScheduledStartTime >= nextScheduledEndTime; + } + + if (startCondition is ContextControllerConditionTimePeriod) + { + var condition = (ContextControllerConditionTimePeriod)startCondition; + var endTime = condition.ExpectedEndTime; + if (endTime != null && endTime <= 0) + { + return true; + } + } + + return startCondition is ContextControllerConditionImmediate; + } + + public ContextControllerFactory Factory + { + get { return _factory; } + } + + public int PathId + { + get { return _pathId; } + } + + public void Deactivate() + { + if (StartCondition != null) + { + if (StartCondition.IsRunning) + { + StartCondition.Deactivate(); + } + } + + foreach (var entry in EndConditions) + { + if (entry.Key.IsRunning) + { + entry.Key.Deactivate(); + } + } + EndConditions.Clear(); + _factory.FactoryContext.StateCache.RemoveContextParentPath(_factory.FactoryContext.OutermostContextName, _factory.FactoryContext.NestingLevel, _pathId); + } + + internal static IDictionary GetBuiltinProperties(String contextName, long startTime, long? endTime, IDictionary startEndpointData) + { + IDictionary props = new Dictionary(); + props.Put(ContextPropertyEventType.PROP_CTX_NAME, contextName); + props.Put(ContextPropertyEventType.PROP_CTX_STARTTIME, startTime); + props.Put(ContextPropertyEventType.PROP_CTX_ENDTIME, endTime); + props.PutAll(startEndpointData); + return props; + } + + private void InitializeFromState( + EventBean optionalTriggeringEvent, + IDictionary optionalTriggeringPattern, + ContextInternalFilterAddendum filterAddendum, + ContextControllerState controllerState, + int pathIdToUse, + AgentInstanceSelector agentInstanceSelector, + bool loadingExistingState) + { + var states = controllerState.States; + var childContexts = ContextControllerStateUtil.GetChildContexts(_factory.FactoryContext, pathIdToUse, states); + var eventAdapterService = _factory.FactoryContext.ServicesContext.EventAdapterService; + + var maxSubpathId = int.MinValue; + foreach (var entry in childContexts) + { + var state = (ContextControllerInitTermState)_factory.Binding.ByteArrayToObject(entry.Value.Blob, eventAdapterService); + + if (_distinctContexts != null) + { + var filter = (ContextControllerConditionFilter)StartCondition; + var @event = (EventBean)state.PatternData.Get(filter.EndpointFilterSpec.OptionalFilterAsName); + AddDistinctKey(@event); + } + + if (controllerState.IsImported) + { + KeyValuePair? existing = null; + foreach (var entryExisting in EndConditions) + { + if (Compare(state.StartTime, state.PatternData, null, + entryExisting.Value.StartTime, entryExisting.Value.StartProperties, null)) + { + existing = entryExisting; + break; + } + } + if (existing != null) + { + ContextControllerInstanceHandle existingHandle = existing.Value.Value.InstanceHandle; + if (existingHandle != null) + { + ActivationCallback.ContextPartitionNavigate(existingHandle, this, controllerState, entry.Value.OptionalContextPartitionId.Value, filterAddendum, agentInstanceSelector, entry.Value.Blob, loadingExistingState); + continue; + } + } + } + + var endEndpoint = MakeEndpoint(_factory.ContextDetailInitiatedTerminated.End, filterAddendum, false, entry.Key.SubPath); + var timeOffset = _factory.FactoryContext.ServicesContext.SchedulingService.Time - state.StartTime; + + endEndpoint.Activate(optionalTriggeringEvent, null, timeOffset, _factory.FactoryContext.IsRecoveringResilient); + var startTime = state.StartTime; + var endTime = endEndpoint.ExpectedEndTime; + var builtinProps = GetBuiltinProperties(_factory.FactoryContext.ContextName, startTime, endTime, state.PatternData); + var contextPartitionId = entry.Value.OptionalContextPartitionId; + + var assignedSubPathId = !controllerState.IsImported ? entry.Key.SubPath : ++CurrentSubpathId; + var instanceHandle = ActivationCallback.ContextPartitionInstantiate(contextPartitionId, assignedSubPathId, entry.Key.SubPath, this, optionalTriggeringEvent, optionalTriggeringPattern, null, builtinProps, controllerState, filterAddendum, loadingExistingState || _factory.FactoryContext.IsRecoveringResilient, entry.Value.State); + EndConditions.Put(endEndpoint, new ContextControllerInitTermInstance(instanceHandle, state.PatternData, startTime, endTime, assignedSubPathId)); + + if (entry.Key.SubPath > maxSubpathId) + { + maxSubpathId = assignedSubPathId; + } + } + + if (!controllerState.IsImported) + { + CurrentSubpathId = maxSubpathId != int.MinValue ? maxSubpathId : 0; + } + } + + public static bool Compare(long savedStartTime, + IDictionary savedProperties, + long? savedEndTime, + long existingStartTime, + IDictionary existingProperties, + long? existingEndTime) + { + if (savedStartTime != existingStartTime) + { + return false; + } + if (savedEndTime != null && existingEndTime != null && !savedEndTime.Equals(existingEndTime)) + { + return false; + } + + foreach (var savedEntry in savedProperties) + { + var existingValue = existingProperties.Get(savedEntry.Key); + var savedValue = savedEntry.Value; + if (savedValue == null && existingValue == null) + { + continue; + } + if (savedValue == null || existingValue == null) + { + return false; + } + if (existingValue.Equals(savedValue)) + { + continue; + } + if (existingValue is EventBean && savedValue is EventBean) + { + if (((EventBean)existingValue).Underlying.Equals(((EventBean)savedValue).Underlying)) + { + continue; + } + } + return false; + } + return true; + } + + private bool AddDistinctKey(EventBean optionalTriggeringEvent) + { + var key = GetDistinctKey(optionalTriggeringEvent); + if (_distinctContexts.ContainsKey(key)) + { + return false; + } + _distinctContexts.Put(key, optionalTriggeringEvent); + return true; + } + + private void RemoveDistinctKey(ContextControllerInitTermInstance value) + { + if (_distinctContexts == null) + { + return; + } + var filter = (ContextControllerConditionFilter)StartCondition; + var @event = (EventBean)value.StartProperties.Get(filter.EndpointFilterSpec.OptionalFilterAsName); + var key = GetDistinctKey(@event); + _distinctContexts.Remove(key); + } + + private Object GetDistinctKey(EventBean optionalTriggeringEvent) + { + _eventsPerStream[0] = optionalTriggeringEvent; + if (_distinctEvaluators.Length == 1) + { + return _distinctEvaluators[0].Evaluate(new EvaluateParams(_eventsPerStream, true, _factory.FactoryContext.AgentInstanceContextCreate)); + } + + var results = new Object[_distinctEvaluators.Length]; + var count = 0; + foreach (var expr in _distinctEvaluators) + { + results[count] = expr.Evaluate(new EvaluateParams(_eventsPerStream, true, _factory.FactoryContext.AgentInstanceContextCreate)); + count++; + } + return new MultiKeyUntyped(results); + } + + internal class DistinctFilterFaultHandler : FilterFaultHandler + { + private readonly ContextControllerInitTerm _contextControllerInitTerm; + + internal DistinctFilterFaultHandler(ContextControllerInitTerm contextControllerInitTerm) + { + _contextControllerInitTerm = contextControllerInitTerm; + } + + public bool HandleFilterFault(EventBean theEvent, long version) + { + /* + * Handle filter faults such as + * - a) App thread determines event E1 applies to CTX + CP1 + * b) Timer thread destroys CP1 + * c) App thread processes E1 for CTX allocating CP2, processing E1 for CP2 + * d) App thread processes E1 for CP1, filter-faulting and ending up dropping the event for CP1 because of this handler + * + * - a) App thread determines event E1 applies to CTX + CP1 + * b) App thread processes E1 for CTX, no action + * c) Timer thread destroys CP1 + * d) App thread processes E1 for CP1, filter-faulting and ending up processing E1 into CTX because of this handler + */ + + AgentInstanceContext aiCreate = _contextControllerInitTerm.Factory.FactoryContext.AgentInstanceContextCreate; + using (aiCreate.EpStatementAgentInstanceHandle.StatementAgentInstanceLock.AcquireWriteLock()) + { + Object key = _contextControllerInitTerm.GetDistinctKey(theEvent); + EventBean trigger = _contextControllerInitTerm.DistinctContexts.Get(key); + + // see if we find that context partition + if (trigger != null) + { + // true for we have already handled this event + // false for filter fault + return trigger.Equals(theEvent); + } + + // not found: evaluate against context + StatementAgentInstanceUtil.EvaluateEventForStatement( + _contextControllerInitTerm.Factory.FactoryContext.ServicesContext, + theEvent, null, Collections.SingletonList(new AgentInstance(null, aiCreate, null))); + + return true; // we handled the event + } + } + } + + internal class NonDistinctFilterFaultHandler : FilterFaultHandler + { + private readonly ContextControllerInitTerm _contextControllerInitTerm; + + internal NonDistinctFilterFaultHandler(ContextControllerInitTerm contextControllerInitTerm) + { + _contextControllerInitTerm = contextControllerInitTerm; + } + + public bool HandleFilterFault(EventBean theEvent, long version) + { + // + // Handle filter faults such as + // - a) App thread determines event E1 applies to CP1 + // b) Timer thread destroys CP1 + // c) App thread processes E1 for CP1, filter-faulting and ending up reprocessing the event against CTX because of this handler + // + + AgentInstanceContext aiCreate = _contextControllerInitTerm.Factory.FactoryContext.AgentInstanceContextCreate; + using (aiCreate.EpStatementAgentInstanceHandle.StatementAgentInstanceLock.AcquireWriteLock()) + { + EventBean trigger = _contextControllerInitTerm._nonDistinctLastTrigger; + if (theEvent != trigger) + { + StatementAgentInstanceUtil.EvaluateEventForStatement( + _contextControllerInitTerm.Factory.FactoryContext.ServicesContext, + theEvent, null, Collections.SingletonList(new AgentInstance(null, aiCreate, null))); + } + + return true; // we handled the event + } + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerInitTermFactoryBase.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerInitTermFactoryBase.cs new file mode 100755 index 000000000..71494dfc8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerInitTermFactoryBase.cs @@ -0,0 +1,144 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.client.context; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.stmt; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; +using com.espertech.esper.filter; +using com.espertech.esper.pattern; +using com.espertech.esper.schedule; + +namespace com.espertech.esper.core.context.mgr +{ + public abstract class ContextControllerInitTermFactoryBase + : ContextControllerFactoryBase + , ContextControllerFactory + { + private readonly ContextDetailInitiatedTerminated _detail; + private IDictionary _contextBuiltinProps; + private MatchedEventMapMeta _matchedEventMapMeta; + + public ContextControllerInitTermFactoryBase(ContextControllerFactoryContext factoryContext, ContextDetailInitiatedTerminated detail) + : base(factoryContext) + { + this._detail = detail; + } + + public override void ValidateFactory() + { + _contextBuiltinProps = ContextPropertyEventType.GetInitiatedTerminatedType(); + LinkedHashSet allTags = new LinkedHashSet(); + ContextPropertyEventType.AddEndpointTypes(FactoryContext.ContextName, _detail.Start, _contextBuiltinProps, allTags); + ContextPropertyEventType.AddEndpointTypes(FactoryContext.ContextName, _detail.End, _contextBuiltinProps, allTags); + _matchedEventMapMeta = new MatchedEventMapMeta(allTags, false); + } + + public override IDictionary ContextBuiltinProps + { + get { return _contextBuiltinProps; } + } + + public MatchedEventMapMeta MatchedEventMapMeta + { + get { return _matchedEventMapMeta; } + } + + public override ContextControllerStatementCtxCache ValidateStatement(ContextControllerStatementBase statement) + { + return null; + } + + public override void PopulateFilterAddendums(IDictionary filterAddendum, ContextControllerStatementDesc statement, object key, int contextId) + { + } + + public override FilterSpecLookupable GetFilterLookupable(EventType eventType) + { + return null; + } + + public override ContextDetail ContextDetail + { + get { return _detail; } + } + + public ContextDetailInitiatedTerminated ContextDetailInitiatedTerminated + { + get { return _detail; } + } + + public override IList ContextDetailPartitionItems + { + get { return Collections.GetEmptyList(); } + } + + public override bool IsSingleInstanceContext + { + get { return !_detail.IsOverlapping; } + } + + public long AllocateSlot() + { + return FactoryContext.AgentInstanceContextCreate.StatementContext.ScheduleBucket.AllocateSlot(); + } + + public TimeProvider TimeProvider + { + get { return FactoryContext.AgentInstanceContextCreate.StatementContext.TimeProvider; } + } + + public SchedulingService SchedulingService + { + get { return FactoryContext.AgentInstanceContextCreate.StatementContext.SchedulingService; } + } + + public EPStatementHandle EpStatementHandle + { + get { return FactoryContext.AgentInstanceContextCreate.StatementContext.EpStatementHandle; } + } + + public StatementContext StatementContext + { + get { return FactoryContext.AgentInstanceContextCreate.StatementContext; } + } + + public override ContextPartitionIdentifier KeyPayloadToIdentifier(object payload) + { + ContextControllerInitTermState state = (ContextControllerInitTermState) payload; + return new ContextPartitionIdentifierInitiatedTerminated( + state == null ? null : state.PatternData, + state == null ? 0 : state.StartTime, + null); + } + + public override StatementAIResourceRegistryFactory StatementAIResourceRegistryFactory + { + get + { + if (_detail.IsOverlapping) + { + return () => new StatementAIResourceRegistry( + new AIRegistryAggregationMultiPerm(), new AIRegistryExprMultiPerm()); + } + else + { + return () => new StatementAIResourceRegistry( + new AIRegistryAggregationSingle(), new AIRegistryExprSingle()); + } + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerInitTermFactoryImpl.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerInitTermFactoryImpl.cs new file mode 100755 index 000000000..61f391a68 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerInitTermFactoryImpl.cs @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.core.context.mgr +{ + public class ContextControllerInitTermFactoryImpl + : ContextControllerInitTermFactoryBase + , ContextControllerFactory + { + private readonly ContextStatePathValueBinding _binding; + + public ContextControllerInitTermFactoryImpl(ContextControllerFactoryContext factoryContext, ContextDetailInitiatedTerminated detail) + : base(factoryContext, detail) + { + _binding = factoryContext.StateCache.GetBinding(detail); + } + + public ContextStatePathValueBinding Binding + { + get { return _binding; } + } + + public override ContextController CreateNoCallback(int pathId, ContextControllerLifecycleCallback callback) + { + return new ContextControllerInitTerm(pathId, callback, this); + } + + public override bool IsSingleInstanceContext + { + get { return !ContextDetailInitiatedTerminated.IsOverlapping; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerInitTermInstance.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerInitTermInstance.cs new file mode 100755 index 000000000..06c151ac7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerInitTermInstance.cs @@ -0,0 +1,39 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.core.context.mgr +{ + public class ContextControllerInitTermInstance + { + public ContextControllerInitTermInstance(ContextControllerInstanceHandle instanceHandle, + IDictionary startProperties, + long startTime, + long? endTime, + int subPathId) + { + InstanceHandle = instanceHandle; + StartProperties = startProperties; + StartTime = startTime; + EndTime = endTime; + SubPathId = subPathId; + } + + public ContextControllerInstanceHandle InstanceHandle { get; private set; } + + public IDictionary StartProperties { get; private set; } + + public long StartTime { get; private set; } + + public long? EndTime { get; private set; } + + public int SubPathId { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerInitTermState.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerInitTermState.cs new file mode 100755 index 000000000..98ce519a4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerInitTermState.cs @@ -0,0 +1,39 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.core.context.mgr +{ + /// + /// State of the overlapping and non-overlapping context. Serializable for the purpose of SPI testing. + /// + [Serializable] + public class ContextControllerInitTermState + { + private readonly long _startTime; + private readonly IDictionary _patternData; + + public ContextControllerInitTermState(long startTime, IDictionary patternData) + { + _startTime = startTime; + _patternData = patternData; + } + + public long StartTime + { + get { return _startTime; } + } + + public IDictionary PatternData + { + get { return _patternData; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerInstanceHandle.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerInstanceHandle.cs new file mode 100755 index 000000000..d0c85bfac --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerInstanceHandle.cs @@ -0,0 +1,17 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.core.context.mgr +{ + public interface ContextControllerInstanceHandle + { + int SubPathId { get; } + int ContextPartitionOrPathId { get; } + ContextControllerTreeAgentInstanceList Instances { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerLifecycleCallback.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerLifecycleCallback.cs new file mode 100755 index 000000000..8ad1b8f8f --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerLifecycleCallback.cs @@ -0,0 +1,49 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.client.context; + +namespace com.espertech.esper.core.context.mgr +{ + public interface ContextControllerLifecycleCallback + { + ContextControllerInstanceHandle ContextPartitionInstantiate( + int? optionalContextPartitionId, + int subpath, + int? importSubpathId, + ContextController originator, + EventBean optionalTriggeringEvent, + IDictionary optionalTriggeringPattern, + Object partitionKey, + IDictionary contextProperties, + ContextControllerState states, + ContextInternalFilterAddendum filterAddendum, + bool isRecoveringResilient, + ContextPartitionState state); + + void ContextPartitionNavigate( + ContextControllerInstanceHandle existingHandle, + ContextController originator, + ContextControllerState controllerState, + int exportedCPOrPathId, + ContextInternalFilterAddendum filterAddendum, + AgentInstanceSelector agentInstanceSelector, + byte[] payload, + bool isRecoveringResilient); + + void ContextPartitionTerminate( + ContextControllerInstanceHandle contextNestedHandle, + IDictionary terminationProperties, + bool leaveLocksAcquired, + IList agentInstances); + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerPartitioned.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerPartitioned.cs new file mode 100755 index 000000000..a09a2cdc9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerPartitioned.cs @@ -0,0 +1,316 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.client.context; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; + +namespace com.espertech.esper.core.context.mgr +{ + public class ContextControllerPartitioned + : ContextController + , ContextControllerPartitionedInstanceCreateCallback + { + private readonly int _pathId; + private readonly ContextControllerLifecycleCallback _activationCallback; + private readonly ContextControllerPartitionedFactoryImpl _factory; + + private readonly IList _filterCallbacks = + new List(); + + private readonly IDictionary _partitionKeys = + new Dictionary().WithNullSupport(); + + private ContextInternalFilterAddendum _activationFilterAddendum; + private int _currentSubpathId; + + public ContextControllerPartitioned(int pathId, ContextControllerLifecycleCallback activationCallback, ContextControllerPartitionedFactoryImpl factory) + { + _pathId = pathId; + _activationCallback = activationCallback; + _factory = factory; + } + + public void ImportContextPartitions(ContextControllerState state, int pathIdToUse, ContextInternalFilterAddendum filterAddendum, AgentInstanceSelector agentInstanceSelector) + { + InitializeFromState(null, null, filterAddendum, state, pathIdToUse, agentInstanceSelector, true); + } + + public void DeletePath(ContextPartitionIdentifier identifier) + { + var partitioned = (ContextPartitionIdentifierPartitioned)identifier; + _partitionKeys.Remove(GetKeyObjectForLookup(partitioned.Keys)); + } + + public void VisitSelectedPartitions(ContextPartitionSelector contextPartitionSelector, ContextPartitionVisitor visitor) + { + var nestingLevel = _factory.FactoryContext.NestingLevel; + if (contextPartitionSelector is ContextPartitionSelectorFiltered) + { + var filtered = (ContextPartitionSelectorFiltered)contextPartitionSelector; + + var identifier = new ContextPartitionIdentifierPartitioned(); + foreach (var entry in _partitionKeys) + { + identifier.ContextPartitionId = entry.Value.ContextPartitionOrPathId; + var identifierOA = GetKeyObjectsAccountForMultikey(entry.Key); + identifier.Keys = identifierOA; + + if (filtered.Filter(identifier)) + { + visitor.Visit(nestingLevel, _pathId, _factory.Binding, identifierOA, this, entry.Value); + } + } + return; + } + else if (contextPartitionSelector is ContextPartitionSelectorSegmented) + { + var partitioned = (ContextPartitionSelectorSegmented)contextPartitionSelector; + if (partitioned.PartitionKeys == null || partitioned.PartitionKeys.IsEmpty()) + { + return; + } + foreach (var keyObjects in partitioned.PartitionKeys) + { + var key = GetKeyObjectForLookup(keyObjects); + var instanceHandle = _partitionKeys.Get(key); + if (instanceHandle != null && instanceHandle.ContextPartitionOrPathId != null) + { + visitor.Visit(nestingLevel, _pathId, _factory.Binding, keyObjects, this, instanceHandle); + } + } + return; + } + else if (contextPartitionSelector is ContextPartitionSelectorById) + { + var filtered = (ContextPartitionSelectorById)contextPartitionSelector; + + foreach (var entry in _partitionKeys) + { + if (filtered.ContextPartitionIds.Contains(entry.Value.ContextPartitionOrPathId)) + { + visitor.Visit(nestingLevel, _pathId, _factory.Binding, GetKeyObjectsAccountForMultikey(entry.Key), this, entry.Value); + } + } + return; + } + else if (contextPartitionSelector is ContextPartitionSelectorAll) + { + foreach (var entry in _partitionKeys) + { + visitor.Visit(nestingLevel, _pathId, _factory.Binding, GetKeyObjectsAccountForMultikey(entry.Key), this, entry.Value); + } + return; + } + throw ContextControllerSelectorUtil.GetInvalidSelector(new Type[] { typeof(ContextPartitionSelectorSegmented) }, contextPartitionSelector); + } + + public void Activate(EventBean optionalTriggeringEvent, IDictionary optionalTriggeringPattern, ContextControllerState controllerState, ContextInternalFilterAddendum filterAddendum, int? importPathId) + { + var factoryContext = _factory.FactoryContext; + _activationFilterAddendum = filterAddendum; + + foreach (var item in _factory.SegmentedSpec.Items) + { + var callback = new ContextControllerPartitionedFilterCallback(factoryContext.ServicesContext, factoryContext.AgentInstanceContextCreate, item, this, filterAddendum); + _filterCallbacks.Add(callback); + + if (optionalTriggeringEvent != null) + { + var match = StatementAgentInstanceUtil.EvaluateFilterForStatement(factoryContext.ServicesContext, optionalTriggeringEvent, factoryContext.AgentInstanceContextCreate, callback.FilterHandle); + + if (match) + { + callback.MatchFound(optionalTriggeringEvent, null); + } + } + } + + if (factoryContext.NestingLevel == 1) + { + controllerState = ContextControllerStateUtil.GetRecoveryStates(_factory.FactoryContext.StateCache, factoryContext.OutermostContextName); + } + if (controllerState == null) + { + return; + } + + int? pathIdToUse = importPathId ?? _pathId; + InitializeFromState(optionalTriggeringEvent, optionalTriggeringPattern, filterAddendum, controllerState, pathIdToUse.Value, null, false); + } + + public ContextControllerFactory Factory + { + get { return _factory; } + } + + public int PathId + { + get { return _pathId; } + } + + public void Deactivate() + { + lock (this) + { + var factoryContext = _factory.FactoryContext; + foreach (var callback in _filterCallbacks) + { + callback.Destroy(factoryContext.ServicesContext.FilterService); + } + _partitionKeys.Clear(); + _filterCallbacks.Clear(); + _factory.FactoryContext.StateCache.RemoveContextParentPath( + factoryContext.OutermostContextName, factoryContext.NestingLevel, _pathId); + } + } + + public void Create(Object key, EventBean theEvent) + { + lock (this) + { + var exists = _partitionKeys.ContainsKey(key); + if (exists) + { + return; + } + + _currentSubpathId++; + + // determine properties available for querying + var factoryContext = _factory.FactoryContext; + var props = ContextPropertyEventType.GetPartitionBean( + factoryContext.ContextName, 0, key, _factory.SegmentedSpec.Items[0].PropertyNames); + + // merge filter addendum, if any + var filterAddendum = _activationFilterAddendum; + if (_factory.HasFiltersSpecsNestedContexts) + { + filterAddendum = _activationFilterAddendum != null + ? _activationFilterAddendum.DeepCopy() + : new ContextInternalFilterAddendum(); + _factory.PopulateContextInternalFilterAddendums(filterAddendum, key); + } + + var handle = _activationCallback.ContextPartitionInstantiate( + null, _currentSubpathId, null, this, theEvent, null, key, props, null, filterAddendum, false, + ContextPartitionState.STARTED); + + _partitionKeys.Put(key, handle); + + // update the filter version for this handle + long filterVersion = factoryContext.ServicesContext.FilterService.FiltersVersion; + _factory.FactoryContext.AgentInstanceContextCreate.EpStatementAgentInstanceHandle.StatementFilterVersion.StmtFilterVersion = filterVersion; + + var keyObjectSaved = GetKeyObjectsAccountForMultikey(key); + _factory.FactoryContext.StateCache.AddContextPath( + factoryContext.OutermostContextName, factoryContext.NestingLevel, _pathId, _currentSubpathId, + handle.ContextPartitionOrPathId, keyObjectSaved, _factory.Binding); + } + } + + private Object[] GetKeyObjectsAccountForMultikey(Object key) + { + if (key is MultiKeyUntyped) + { + return ((MultiKeyUntyped)key).Keys; + } + else + { + return new Object[] + { + key + }; + } + } + + private Object GetKeyObjectForLookup(Object[] keyObjects) + { + if (keyObjects.Length > 1) + { + return new MultiKeyUntyped(keyObjects); + } + else + { + return keyObjects[0]; + } + } + + private void InitializeFromState( + EventBean optionalTriggeringEvent, + IDictionary optionalTriggeringPattern, + ContextInternalFilterAddendum filterAddendum, + ContextControllerState controllerState, + int pathIdToUse, + AgentInstanceSelector agentInstanceSelector, + bool loadingExistingState) + { + var factoryContext = _factory.FactoryContext; + var states = controllerState.States; + + // restart if there are states + var maxSubpathId = int.MinValue; + var childContexts = ContextControllerStateUtil.GetChildContexts(factoryContext, pathIdToUse, states); + var eventAdapterService = _factory.FactoryContext.ServicesContext.EventAdapterService; + + foreach (var entry in childContexts) + { + var keys = (Object[])_factory.Binding.ByteArrayToObject(entry.Value.Blob, eventAdapterService); + var mapKey = GetKeyObjectForLookup(keys); + + // merge filter addendum, if any + var myFilterAddendum = _activationFilterAddendum; + if (_factory.HasFiltersSpecsNestedContexts) + { + filterAddendum = _activationFilterAddendum != null + ? _activationFilterAddendum.DeepCopy() + : new ContextInternalFilterAddendum(); + _factory.PopulateContextInternalFilterAddendums(filterAddendum, mapKey); + } + + // check if exists already + if (controllerState.IsImported) + { + var existingHandle = _partitionKeys.Get(mapKey); + if (existingHandle != null) + { + _activationCallback.ContextPartitionNavigate( + existingHandle, this, controllerState, entry.Value.OptionalContextPartitionId.Value, + myFilterAddendum, agentInstanceSelector, entry.Value.Blob, loadingExistingState); + continue; + } + } + + var props = ContextPropertyEventType.GetPartitionBean( + factoryContext.ContextName, 0, mapKey, _factory.SegmentedSpec.Items[0].PropertyNames); + + var assignedSubpathId = !controllerState.IsImported ? entry.Key.SubPath : ++_currentSubpathId; + var handle = + _activationCallback.ContextPartitionInstantiate( + entry.Value.OptionalContextPartitionId, assignedSubpathId, entry.Key.SubPath, this, + optionalTriggeringEvent, optionalTriggeringPattern, mapKey, props, controllerState, + myFilterAddendum, loadingExistingState || factoryContext.IsRecoveringResilient, entry.Value.State); + _partitionKeys.Put(mapKey, handle); + + if (entry.Key.SubPath > maxSubpathId) + { + maxSubpathId = assignedSubpathId; + } + } + if (!controllerState.IsImported) + { + _currentSubpathId = maxSubpathId != int.MinValue ? maxSubpathId : 0; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerPartitionedFactoryBase.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerPartitionedFactoryBase.cs new file mode 100755 index 000000000..fc2df1de9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerPartitionedFactoryBase.cs @@ -0,0 +1,134 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.client.context; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.stmt; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.spec.util; +using com.espertech.esper.filter; + +namespace com.espertech.esper.core.context.mgr +{ + public abstract class ContextControllerPartitionedFactoryBase + : ContextControllerFactoryBase + , ContextControllerFactory + { + private readonly ContextDetailPartitioned _segmentedSpec; + private readonly IList _filtersSpecsNestedContexts; + + private IDictionary _contextBuiltinProps; + + protected ContextControllerPartitionedFactoryBase(ContextControllerFactoryContext factoryContext, ContextDetailPartitioned segmentedSpec, IList filtersSpecsNestedContexts) + : base(factoryContext) + { + _segmentedSpec = segmentedSpec; + _filtersSpecsNestedContexts = filtersSpecsNestedContexts; + } + + internal IList FiltersSpecsNestedContexts + { + get { return _filtersSpecsNestedContexts; } + } + + public bool HasFiltersSpecsNestedContexts + { + get { return _filtersSpecsNestedContexts != null && !_filtersSpecsNestedContexts.IsEmpty(); } + } + + public override void ValidateFactory() + { + Type[] propertyTypes = ContextControllerPartitionedUtil.ValidateContextDesc(FactoryContext.ContextName, _segmentedSpec); + _contextBuiltinProps = ContextPropertyEventType.GetPartitionType(_segmentedSpec, propertyTypes); + } + + public override ContextControllerStatementCtxCache ValidateStatement(ContextControllerStatementBase statement) + { + StatementSpecCompiledAnalyzerResult streamAnalysis = StatementSpecCompiledAnalyzer.AnalyzeFilters(statement.StatementSpec); + ContextControllerPartitionedUtil.ValidateStatementForContext(FactoryContext.ContextName, statement, streamAnalysis, GetItemEventTypes(_segmentedSpec), FactoryContext.ServicesContext.NamedWindowMgmtService); + return new ContextControllerStatementCtxCacheFilters(streamAnalysis.Filters); + } + + public override void PopulateFilterAddendums(IDictionary filterAddendum, ContextControllerStatementDesc statement, object key, int contextId) + { + ContextControllerStatementCtxCacheFilters statementInfo = (ContextControllerStatementCtxCacheFilters) statement.Caches[FactoryContext.NestingLevel - 1]; + ContextControllerPartitionedUtil.PopulateAddendumFilters(key, statementInfo.FilterSpecs, _segmentedSpec, statement.Statement.StatementSpec, filterAddendum); + } + + public void PopulateContextInternalFilterAddendums(ContextInternalFilterAddendum filterAddendum, object key) + { + if (_filtersSpecsNestedContexts == null || _filtersSpecsNestedContexts.IsEmpty()) { + return; + } + ContextControllerPartitionedUtil.PopulateAddendumFilters(key, _filtersSpecsNestedContexts, _segmentedSpec, null, filterAddendum.FilterAddendum); + } + + public override FilterSpecLookupable GetFilterLookupable(EventType eventType) + { + return null; + } + + public override bool IsSingleInstanceContext + { + get { return false; } + } + + public override StatementAIResourceRegistryFactory StatementAIResourceRegistryFactory + { + get + { + return () => new StatementAIResourceRegistry(new AIRegistryAggregationMultiPerm(), new AIRegistryExprMultiPerm()); + } + } + + public override IList ContextDetailPartitionItems + { + get { return _segmentedSpec.Items; } + } + + public override ContextDetail ContextDetail + { + get { return _segmentedSpec; } + } + + public ContextDetailPartitioned SegmentedSpec + { + get { return _segmentedSpec; } + } + + public override IDictionary ContextBuiltinProps + { + get { return _contextBuiltinProps; } + } + + public override ContextPartitionIdentifier KeyPayloadToIdentifier(object payload) + { + if (payload is object[]) { + return new ContextPartitionIdentifierPartitioned((object[]) payload); + } + if (payload is MultiKeyUntyped) { + return new ContextPartitionIdentifierPartitioned(((MultiKeyUntyped) payload).Keys); + } + return new ContextPartitionIdentifierPartitioned(new object[] {payload}); + } + + private ICollection GetItemEventTypes(ContextDetailPartitioned segmentedSpec) + { + IList itemEventTypes = new List(); + foreach (ContextDetailPartitionItem item in segmentedSpec.Items) { + itemEventTypes.Add(item.FilterSpecCompiled.FilterForEventType); + } + return itemEventTypes; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerPartitionedFactoryImpl.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerPartitionedFactoryImpl.cs new file mode 100755 index 000000000..6a247fcc1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerPartitionedFactoryImpl.cs @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.epl.spec; +using com.espertech.esper.filter; + +namespace com.espertech.esper.core.context.mgr +{ + public class ContextControllerPartitionedFactoryImpl + : ContextControllerPartitionedFactoryBase + , ContextControllerFactory + { + private readonly ContextStatePathValueBinding _binding; + + public ContextControllerPartitionedFactoryImpl(ContextControllerFactoryContext factoryContext, ContextDetailPartitioned segmentedSpec, IList filtersSpecsNestedContexts) + : base(factoryContext, segmentedSpec, filtersSpecsNestedContexts) + { + _binding = factoryContext.StateCache.GetBinding(typeof(ContextControllerPartitionedState)); + } + + public ContextStatePathValueBinding Binding + { + get { return _binding; } + } + + public override ContextController CreateNoCallback(int pathId, ContextControllerLifecycleCallback callback) + { + return new ContextControllerPartitioned(pathId, callback, this); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerPartitionedFilterCallback.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerPartitionedFilterCallback.cs new file mode 100755 index 000000000..2ce51ec44 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerPartitionedFilterCallback.cs @@ -0,0 +1,95 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.spec; +using com.espertech.esper.filter; + +namespace com.espertech.esper.core.context.mgr +{ + public class ContextControllerPartitionedFilterCallback : FilterHandleCallback + { + private readonly AgentInstanceContext _agentInstanceContextCreateContext; + private readonly EventPropertyGetter[] _getters; + private readonly ContextControllerPartitionedInstanceCreateCallback _callback; + private readonly EPStatementHandleCallback _filterHandle; + private readonly FilterServiceEntry _filterServiceEntry; + + public ContextControllerPartitionedFilterCallback(EPServicesContext servicesContext, AgentInstanceContext agentInstanceContextCreateContext, ContextDetailPartitionItem partitionItem, ContextControllerPartitionedInstanceCreateCallback callback, ContextInternalFilterAddendum filterAddendum) + { + _agentInstanceContextCreateContext = agentInstanceContextCreateContext; + _callback = callback; + + _filterHandle = new EPStatementHandleCallback(agentInstanceContextCreateContext.EpStatementAgentInstanceHandle, this); + + _getters = new EventPropertyGetter[partitionItem.PropertyNames.Count]; + for (int i = 0; i < partitionItem.PropertyNames.Count; i++) + { + var propertyName = partitionItem.PropertyNames[i]; + var getter = partitionItem.FilterSpecCompiled.FilterForEventType.GetGetter(propertyName); + _getters[i] = getter; + } + + var addendum = filterAddendum != null ? filterAddendum.GetFilterAddendum(partitionItem.FilterSpecCompiled) : null; + var filterValueSet = partitionItem.FilterSpecCompiled.GetValueSet(null, null, addendum); + + _filterServiceEntry = servicesContext.FilterService.Add(filterValueSet, _filterHandle); + var filtersVersion = servicesContext.FilterService.FiltersVersion; + agentInstanceContextCreateContext.EpStatementAgentInstanceHandle.StatementFilterVersion.StmtFilterVersion = filtersVersion; + + } + + public void MatchFound(EventBean theEvent, ICollection allStmtMatches) + { + Object key; + if (_getters.Length > 1) + { + var keys = new Object[_getters.Length]; + for (int i = 0; i < keys.Length; i++) + { + keys[i] = _getters[i].Get(theEvent); + } + key = new MultiKeyUntyped(keys); + } + else + { + key = _getters[0].Get(theEvent); + } + + _callback.Create(key, theEvent); + } + + public bool IsSubSelect + { + get { return false; } + } + + public int StatementId + { + get { return _agentInstanceContextCreateContext.StatementContext.StatementId; } + } + + public void Destroy(FilterService filterService) + { + filterService.Remove(_filterHandle, _filterServiceEntry); + long filtersVersion = _agentInstanceContextCreateContext.StatementContext.FilterService.FiltersVersion; + _agentInstanceContextCreateContext.EpStatementAgentInstanceHandle.StatementFilterVersion.StmtFilterVersion = filtersVersion; + } + + public EPStatementHandleCallback FilterHandle + { + get { return _filterHandle; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerPartitionedInstanceCreateCallback.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerPartitionedInstanceCreateCallback.cs new file mode 100755 index 000000000..3be7cc0ea --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerPartitionedInstanceCreateCallback.cs @@ -0,0 +1,19 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.core.context.mgr +{ + public interface ContextControllerPartitionedInstanceCreateCallback + { + void Create(Object key, EventBean theEvent); + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerPartitionedState.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerPartitionedState.cs new file mode 100755 index 000000000..76a020aac --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerPartitionedState.cs @@ -0,0 +1,22 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.core.context.mgr +{ + public class ContextControllerPartitionedState + { + public ContextControllerPartitionedState(Object partitionKey) + { + PartitionKey = partitionKey; + } + + public object PartitionKey { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerPartitionedUtil.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerPartitionedUtil.cs new file mode 100755 index 000000000..45bba6bff --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerPartitionedUtil.cs @@ -0,0 +1,337 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.named; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.spec.util; +using com.espertech.esper.events; +using com.espertech.esper.filter; +using com.espertech.esper.util; + +namespace com.espertech.esper.core.context.mgr +{ + public class ContextControllerPartitionedUtil + { + + protected internal static Type[] ValidateContextDesc(string contextName, ContextDetailPartitioned segmentedSpec) + { + + if (segmentedSpec.Items.IsEmpty()) + { + throw new ExprValidationException("EmptyFalse list of partition items"); + } + + // verify properties exist + foreach (var item in segmentedSpec.Items) + { + var type = item.FilterSpecCompiled.FilterForEventType; + foreach (var property in item.PropertyNames) + { + var getter = type.GetGetter(property); + if (getter == null) + { + throw new ExprValidationException( + "For context '" + contextName + "' property name '" + property + "' not found on type " + + type.Name); + } + } + } + + // verify property number and types compatible + ContextDetailPartitionItem firstItem = segmentedSpec.Items[0]; + if (segmentedSpec.Items.Count > 1) + { + // verify the same filter event type is only listed once + + for (var i = 0; i < segmentedSpec.Items.Count; i++) + { + EventType compareTo = segmentedSpec.Items[i].FilterSpecCompiled.FilterForEventType; + + for (var j = 0; j < segmentedSpec.Items.Count; j++) + { + if (i == j) + { + continue; + } + + EventType compareFrom = segmentedSpec.Items[j].FilterSpecCompiled.FilterForEventType; + if (compareFrom == compareTo) + { + throw new ExprValidationException( + "For context '" + contextName + "' the event type '" + compareFrom.Name + + "' is listed twice"); + } + if (EventTypeUtility.IsTypeOrSubTypeOf(compareFrom, compareTo) || + EventTypeUtility.IsTypeOrSubTypeOf(compareTo, compareFrom)) + { + throw new ExprValidationException( + "For context '" + contextName + "' the event type '" + compareFrom.Name + + "' is listed twice: Event type '" + + compareFrom.Name + "' is a subtype or supertype of event type '" + compareTo.Name + "'"); + } + + } + } + + // build property type information + var names = new string[firstItem.PropertyNames.Count]; + var types = new Type[firstItem.PropertyNames.Count]; + var typesBoxed = new Type[firstItem.PropertyNames.Count]; + for (var i = 0; i < firstItem.PropertyNames.Count; i++) + { + string property = firstItem.PropertyNames[i]; + names[i] = property; + types[i] = firstItem.FilterSpecCompiled.FilterForEventType.GetPropertyType(property); + typesBoxed[i] = types[i].GetBoxedType(); + } + + // compare property types and numbers + for (var item = 1; item < segmentedSpec.Items.Count; item++) + { + ContextDetailPartitionItem nextItem = segmentedSpec.Items[item]; + + // compare number of properties + if (nextItem.PropertyNames.Count != types.Length) + { + throw new ExprValidationException( + "For context '" + contextName + + "' expected the same number of property names for each event type, found " + + types.Length + " properties for event type '" + + firstItem.FilterSpecCompiled.FilterForEventType.Name + + "' and " + nextItem.PropertyNames.Count + " properties for event type '" + + nextItem.FilterSpecCompiled.FilterForEventType.Name + "'"); + } + + // compare property types + for (var i = 0; i < nextItem.PropertyNames.Count; i++) + { + string property = nextItem.PropertyNames[i]; + var type = nextItem.FilterSpecCompiled.FilterForEventType.GetPropertyType(property).GetBoxedType(); + var typeBoxed = type.GetBoxedType(); + var left = TypeHelper.IsSubclassOrImplementsInterface(typeBoxed, typesBoxed[i]); + var right = TypeHelper.IsSubclassOrImplementsInterface(typesBoxed[i], typeBoxed); + if (typeBoxed != typesBoxed[i] && !left && !right) + { + throw new ExprValidationException( + "For context '" + contextName + "' for context '" + contextName + + "' found mismatch of property types, property '" + names[i] + + "' of type '" + types[i].GetTypeNameFullyQualPretty() + + "' compared to property '" + property + + "' of type '" + typeBoxed.GetTypeNameFullyQualPretty() + "'"); + } + } + } + } + + var propertyTypes = new Type[firstItem.PropertyNames.Count]; + for (var i = 0; i < firstItem.PropertyNames.Count; i++) + { + string property = firstItem.PropertyNames[i]; + propertyTypes[i] = firstItem.FilterSpecCompiled.FilterForEventType.GetPropertyType(property); + } + return propertyTypes; + } + + protected internal static void ValidateStatementForContext( + string contextName, + ContextControllerStatementBase statement, + StatementSpecCompiledAnalyzerResult streamAnalysis, + ICollection itemEventTypes, + NamedWindowMgmtService namedWindowMgmtService) + { + var filters = streamAnalysis.Filters; + + var isCreateWindow = statement.StatementSpec.CreateWindowDesc != null; + + // if no create-window: at least one of the filters must match one of the filters specified by the context + if (!isCreateWindow) + { + foreach (var filter in filters) + { + foreach (var itemEventType in itemEventTypes) + { + var stmtFilterType = filter.FilterForEventType; + if (stmtFilterType == itemEventType) + { + return; + } + if (EventTypeUtility.IsTypeOrSubTypeOf(stmtFilterType, itemEventType)) + { + return; + } + + NamedWindowProcessor processor = namedWindowMgmtService.GetProcessor(stmtFilterType.Name); + if (processor != null && processor.ContextName != null && + processor.ContextName.Equals(contextName)) + { + return; + } + } + } + + if (!filters.IsEmpty()) + { + throw new ExprValidationException( + GetTypeValidationMessage(contextName, filters[0].FilterForEventType.Name)); + } + return; + } + + // validate create-window with column definition: not allowed, requires typed + if (statement.StatementSpec.CreateWindowDesc.Columns != null && + statement.StatementSpec.CreateWindowDesc.Columns.Count > 0) + { + throw new ExprValidationException( + "Segmented context '" + contextName + + "' requires that named windows are associated to an existing event type and that the event type is listed among the partitions defined by the create-context statement"); + } + + // validate create-window declared type + var declaredAsName = statement.StatementSpec.CreateWindowDesc.AsEventTypeName; + if (declaredAsName != null) + { + if (itemEventTypes.Any(itemEventType => itemEventType.Name == declaredAsName)) + { + return; + } + + throw new ExprValidationException(GetTypeValidationMessage(contextName, declaredAsName)); + } + } + + // Compare filters in statement with filters in segmented context, addendum filter compilation + public static void PopulateAddendumFilters( + object keyValue, + IList filtersSpecs, + ContextDetailPartitioned segmentedSpec, + StatementSpecCompiled optionalStatementSpecCompiled, + IDictionary addendums) + { + foreach (var filtersSpec in filtersSpecs) + { + var addendum = GetAddendumFilters(keyValue, filtersSpec, segmentedSpec, optionalStatementSpecCompiled); + if (addendum == null) + { + continue; + } + + var existing = addendums.Get(filtersSpec); + if (existing != null) + { + addendum = ContextControllerAddendumUtil.MultiplyAddendum(existing, addendum); + } + addendums.Put(filtersSpec, addendum); + } + } + + public static FilterValueSetParam[][] GetAddendumFilters( + object keyValue, + FilterSpecCompiled filtersSpec, + ContextDetailPartitioned segmentedSpec, + StatementSpecCompiled optionalStatementSpecCompiled) + { + // determine whether create-named-window + var isCreateWindow = optionalStatementSpecCompiled != null && + optionalStatementSpecCompiled.CreateWindowDesc != null; + ContextDetailPartitionItem foundPartition = null; + + if (!isCreateWindow) + { + foreach (var partitionItem in segmentedSpec.Items) + { + var typeOrSubtype = EventTypeUtility.IsTypeOrSubTypeOf( + filtersSpec.FilterForEventType, partitionItem.FilterSpecCompiled.FilterForEventType); + if (typeOrSubtype) + { + foundPartition = partitionItem; + } + } + } + else + { + var declaredAsName = optionalStatementSpecCompiled.CreateWindowDesc.AsEventTypeName; + if (declaredAsName == null) + { + return null; + } + foreach (var partitionItem in segmentedSpec.Items) + { + if (partitionItem.FilterSpecCompiled.FilterForEventType.Name.Equals(declaredAsName)) + { + foundPartition = partitionItem; + break; + } + } + } + + if (foundPartition == null) + { + return null; + } + + var addendumFilters = new List(foundPartition.PropertyNames.Count); + if (foundPartition.PropertyNames.Count == 1) + { + var propertyName = foundPartition.PropertyNames[0]; + var getter = foundPartition.FilterSpecCompiled.FilterForEventType.GetGetter(propertyName); + var resultType = foundPartition.FilterSpecCompiled.FilterForEventType.GetPropertyType(propertyName); + var lookupable = new FilterSpecLookupable(propertyName, getter, resultType, false); + var filter = GetFilterMayEqualOrNull(lookupable, keyValue); + addendumFilters.Add(filter); + } + else + { + var keys = ((MultiKeyUntyped)keyValue).Keys; + for (var i = 0; i < foundPartition.PropertyNames.Count; i++) + { + var partitionPropertyName = foundPartition.PropertyNames[i]; + var getter = foundPartition.FilterSpecCompiled.FilterForEventType.GetGetter(partitionPropertyName); + var resultType = foundPartition.FilterSpecCompiled.FilterForEventType.GetPropertyType(partitionPropertyName); + var lookupable = new FilterSpecLookupable(partitionPropertyName, getter, resultType, false); + var filter = GetFilterMayEqualOrNull(lookupable, keys[i]); + addendumFilters.Add(filter); + } + } + + var addendum = new FilterValueSetParam[1][]; + addendum[0] = addendumFilters.ToArray(); + + var partitionFilters = foundPartition.ParametersCompiled; + if (partitionFilters != null) + { + addendum = ContextControllerAddendumUtil.AddAddendum(partitionFilters, addendum[0]); + } + + return addendum; + } + + private static FilterValueSetParam GetFilterMayEqualOrNull(FilterSpecLookupable lookupable, Object keyValue) + { + if (keyValue == null) + { + return new FilterValueSetParamImpl(lookupable, FilterOperator.IS, null); + } + return new FilterValueSetParamImpl(lookupable, FilterOperator.EQUAL, keyValue); + } + + private static string GetTypeValidationMessage(string contextName, string typeNameEx) + { + return "Segmented context '" + contextName + + "' requires that any of the event types that are listed in the segmented context also appear in any of the filter expressions of the statement, type '" + + typeNameEx + "' is not one of the types listed"; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerState.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerState.cs new file mode 100755 index 000000000..cace5d6da --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerState.cs @@ -0,0 +1,42 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; + +namespace com.espertech.esper.core.context.mgr +{ + public class ContextControllerState + { + private readonly OrderedDictionary _states; + private readonly bool _imported; + private readonly ContextPartitionImportCallback _partitionImportCallback; + + public ContextControllerState(OrderedDictionary states, bool imported, ContextPartitionImportCallback partitionImportCallback) + { + _states = states; + _imported = imported; + _partitionImportCallback = partitionImportCallback; + } + + public OrderedDictionary States + { + get { return _states; } + } + + public bool IsImported + { + get { return _imported; } + } + + public ContextPartitionImportCallback PartitionImportCallback + { + get { return _partitionImportCallback; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerStateUtil.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerStateUtil.cs new file mode 100755 index 000000000..124d620d5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerStateUtil.cs @@ -0,0 +1,35 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.core.context.mgr +{ + public class ContextControllerStateUtil { + + public static ContextControllerState GetRecoveryStates(ContextStateCache cache, String contextName) + { + OrderedDictionary state = cache.GetContextPaths(contextName); + if (state == null || state.IsEmpty()) { + return null; + } + return new ContextControllerState(state, false, null); + } + + public static IDictionary GetChildContexts(ContextControllerFactoryContext factoryContext, int pathId, OrderedDictionary states) + { + ContextStatePathKey start = new ContextStatePathKey(factoryContext.NestingLevel, pathId, int.MinValue); + ContextStatePathKey end = new ContextStatePathKey(factoryContext.NestingLevel, pathId, int.MaxValue); + return states.Between(start, true, end, true); + } + } + +} diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerStatementBase.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerStatementBase.cs new file mode 100755 index 000000000..e3ea438a9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerStatementBase.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.context.factory; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.core.context.mgr +{ + public abstract class ContextControllerStatementBase + { + protected ContextControllerStatementBase(StatementSpecCompiled statementSpec, + StatementContext statementContext, + ContextMergeView mergeView, + StatementAgentInstanceFactory factory) + { + StatementSpec = statementSpec; + StatementContext = statementContext; + MergeView = mergeView; + Factory = factory; + } + + public StatementSpecCompiled StatementSpec { get; private set; } + + public StatementContext StatementContext { get; private set; } + + public ContextMergeView MergeView { get; private set; } + + public StatementAgentInstanceFactory Factory { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerStatementCtxCache.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerStatementCtxCache.cs new file mode 100755 index 000000000..2f1e48303 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerStatementCtxCache.cs @@ -0,0 +1,14 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.core.context.mgr +{ + public interface ContextControllerStatementCtxCache + { + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerStatementCtxCacheFilters.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerStatementCtxCacheFilters.cs new file mode 100755 index 000000000..c67933b14 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerStatementCtxCacheFilters.cs @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using com.espertech.esper.filter; + +namespace com.espertech.esper.core.context.mgr +{ + public class ContextControllerStatementCtxCacheFilters : ContextControllerStatementCtxCache + { + public ContextControllerStatementCtxCacheFilters(IList filterSpecs) + { + FilterSpecs = filterSpecs; + } + + public IList FilterSpecs { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerStatementDesc.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerStatementDesc.cs new file mode 100755 index 000000000..5dc120147 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerStatementDesc.cs @@ -0,0 +1,24 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.core.context.mgr +{ + public class ContextControllerStatementDesc + { + public ContextControllerStatementDesc(ContextControllerStatementBase statement, + ContextControllerStatementCtxCache[] caches) + { + Statement = statement; + Caches = caches; + } + + public ContextControllerStatementBase Statement { get; private set; } + + public ContextControllerStatementCtxCache[] Caches { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerTreeAgentInstanceList.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerTreeAgentInstanceList.cs new file mode 100755 index 000000000..d0e9adad4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerTreeAgentInstanceList.cs @@ -0,0 +1,63 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client.context; + +namespace com.espertech.esper.core.context.mgr +{ + public class ContextControllerTreeAgentInstanceList { + + private readonly long _filterVersionAfterAllocation; + private readonly Object _initPartitionKey; + private readonly IDictionary _initContextProperties; + private readonly IList _agentInstances; + private ContextPartitionState _state; + + public ContextControllerTreeAgentInstanceList(long filterVersionAfterAllocation, Object initPartitionKey, IDictionary initContextProperties, IList agentInstances, ContextPartitionState state) + { + _filterVersionAfterAllocation = filterVersionAfterAllocation; + _initPartitionKey = initPartitionKey; + _initContextProperties = initContextProperties; + _agentInstances = agentInstances; + _state = state; + } + + public long FilterVersionAfterAllocation + { + get { return _filterVersionAfterAllocation; } + } + + public object InitPartitionKey + { + get { return _initPartitionKey; } + } + + public IDictionary InitContextProperties + { + get { return _initContextProperties; } + } + + public IList AgentInstances + { + get { return _agentInstances; } + } + + public ContextPartitionState State + { + get { return _state; } + set { _state = value; } + } + + public void ClearAgentInstances() { + _agentInstances.Clear(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerTreeEntry.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerTreeEntry.cs new file mode 100755 index 000000000..20013e7da --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextControllerTreeEntry.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.core.context.mgr +{ + public class ContextControllerTreeEntry + { + public ContextControllerTreeEntry(ContextController parent, + IDictionary childContexts, + Object initPartitionKey, + IDictionary initContextProperties) + { + Parent = parent; + ChildContexts = childContexts; + InitPartitionKey = initPartitionKey; + InitContextProperties = initContextProperties; + } + + public ContextController Parent { get; private set; } + + public IDictionary ChildContexts { get; set; } + + public object InitPartitionKey { get; private set; } + + public IDictionary AgentInstances { get; set; } + + public IDictionary InitContextProperties { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextInternalFilterAddendum.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextInternalFilterAddendum.cs new file mode 100755 index 000000000..65dd4ad28 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextInternalFilterAddendum.cs @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.filter; + +namespace com.espertech.esper.core.context.mgr +{ + public class ContextInternalFilterAddendum + { + private readonly IdentityDictionary _filterAddendum; + + public ContextInternalFilterAddendum() + { + _filterAddendum = new IdentityDictionary(); + } + + public FilterValueSetParam[][] GetFilterAddendum(FilterSpecCompiled filterSpecCompiled) + { + return _filterAddendum.Get(filterSpecCompiled); + } + + public IdentityDictionary FilterAddendum + { + get { return _filterAddendum; } + } + + public ContextInternalFilterAddendum DeepCopy() + { + var copy = new ContextInternalFilterAddendum(); + foreach (var entry in _filterAddendum) + { + var copy2Dim = new FilterValueSetParam[entry.Value.Length][]; + copy.FilterAddendum[entry.Key] = copy2Dim; + for (int ii = 0; ii < entry.Value.Length; ii++) + { + var copyList = new FilterValueSetParam[entry.Value[ii].Length]; + copy2Dim[ii] = copyList; + Array.Copy(entry.Value[ii], 0, copyList, 0, copyList.Length); + } + copy.FilterAddendum[entry.Key] = copy2Dim; + } + return copy; + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextManagedStatementCreateAggregationVariableDesc.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextManagedStatementCreateAggregationVariableDesc.cs new file mode 100755 index 000000000..f0b9e9c06 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextManagedStatementCreateAggregationVariableDesc.cs @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.factory; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.core.context.mgr +{ + public class ContextManagedStatementCreateAggregationVariableDesc : ContextControllerStatementBase + { + public ContextManagedStatementCreateAggregationVariableDesc(StatementSpecCompiled statementSpec, StatementContext statementContext, ContextMergeView mergeView, StatementAgentInstanceFactory factory) + : base(statementSpec, statementContext, mergeView, factory) + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextManagedStatementCreateIndexDesc.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextManagedStatementCreateIndexDesc.cs new file mode 100755 index 000000000..65fd53d99 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextManagedStatementCreateIndexDesc.cs @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.factory; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.core.context.mgr +{ + public class ContextManagedStatementCreateIndexDesc : ContextControllerStatementBase + { + public ContextManagedStatementCreateIndexDesc(StatementSpecCompiled statementSpec, StatementContext statementContext, ContextMergeView mergeView, StatementAgentInstanceFactory factory) + : base(statementSpec, statementContext, mergeView, factory) + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextManagedStatementCreateVariableDesc.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextManagedStatementCreateVariableDesc.cs new file mode 100755 index 000000000..f675d24d1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextManagedStatementCreateVariableDesc.cs @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.context.factory; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.core.context.mgr +{ + public class ContextManagedStatementCreateVariableDesc : ContextControllerStatementBase + { + public ContextManagedStatementCreateVariableDesc(StatementSpecCompiled statementSpec, StatementContext statementContext, ContextMergeView mergeView, StatementAgentInstanceFactory factory) + : base(statementSpec, statementContext, mergeView, factory) + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextManagedStatementCreateWindowDesc.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextManagedStatementCreateWindowDesc.cs new file mode 100755 index 000000000..573a3cadf --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextManagedStatementCreateWindowDesc.cs @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.context.factory; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.core.context.mgr +{ + public class ContextManagedStatementCreateWindowDesc : ContextControllerStatementBase + { + public ContextManagedStatementCreateWindowDesc(StatementSpecCompiled statementSpec, StatementContext statementContext, ContextMergeView mergeView, StatementAgentInstanceFactory factory) + : base(statementSpec, statementContext, mergeView, factory) + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextManagedStatementOnTriggerDesc.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextManagedStatementOnTriggerDesc.cs new file mode 100755 index 000000000..33a0a10da --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextManagedStatementOnTriggerDesc.cs @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.context.factory; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.core.context.mgr +{ + public class ContextManagedStatementOnTriggerDesc : ContextControllerStatementBase + { + public ContextManagedStatementOnTriggerDesc(StatementSpecCompiled statementSpec, StatementContext statementContext, ContextMergeView mergeView, StatementAgentInstanceFactory factory) + : base(statementSpec, statementContext, mergeView, factory) + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextManagedStatementSelectDesc.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextManagedStatementSelectDesc.cs new file mode 100755 index 000000000..6fb065816 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextManagedStatementSelectDesc.cs @@ -0,0 +1,33 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.core.context.factory; +using com.espertech.esper.core.context.subselect; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.core.context.mgr +{ + public class ContextManagedStatementSelectDesc : ContextControllerStatementBase + { + public ContextManagedStatementSelectDesc(StatementSpecCompiled statementSpec, StatementContext statementContext, ContextMergeView mergeView, StatementAgentInstanceFactory factory, IList aggregationExpressions, SubSelectStrategyCollection subSelectPrototypeCollection) + : base(statementSpec, statementContext, mergeView, factory) + { + AggregationExpressions = aggregationExpressions; + SubSelectPrototypeCollection = subSelectPrototypeCollection; + } + + public IList AggregationExpressions { get; private set; } + + public SubSelectStrategyCollection SubSelectPrototypeCollection { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextManagementService.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextManagementService.cs new file mode 100755 index 000000000..05a8e2a95 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextManagementService.cs @@ -0,0 +1,36 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.core.context.mgr +{ + public interface ContextManagementService + { + void AddContextSpec(EPServicesContext servicesContext, AgentInstanceContext agentInstanceContext, CreateContextDesc contextDesc, bool isRecoveringResilient, EventType statementResultEventType); + int ContextCount { get; } + + ContextDescriptor GetContextDescriptor(string contextName); + + void AddStatement(string contextName, ContextControllerStatementBase statement, bool isRecoveringResilient); + void StoppedStatement(string contextName, string statementName, int statementId, string epl, ExceptionHandlingService exceptionHandlingService); + void DestroyedStatement(string contextName, string statementName, int statementId); + + void DestroyedContext(String contextName); + + IDictionary Contexts { get; } + + ContextManager GetContextManager(String contextName); + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextManagementServiceImpl.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextManagementServiceImpl.cs new file mode 100755 index 000000000..f64b5764e --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextManagementServiceImpl.cs @@ -0,0 +1,159 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.client.hook; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.core.context.mgr +{ + public class ContextManagementServiceImpl : ContextManagementService + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly IDictionary _contexts; + private readonly ICollection _destroyedContexts = new HashSet(); + + public ContextManagementServiceImpl() + { + _contexts = new Dictionary(); + } + + public void AddContextSpec(EPServicesContext servicesContext, AgentInstanceContext agentInstanceContext, CreateContextDesc contextDesc, bool isRecoveringResilient, EventType statementResultEventType) + { + var mgr = _contexts.Get(contextDesc.ContextName); + if (mgr != null) + { + if (_destroyedContexts.Contains(contextDesc.ContextName)) + { + throw new ExprValidationException("Context by name '" + contextDesc.ContextName + "' is still referenced by statements and may not be changed"); + } + throw new ExprValidationException("Context by name '" + contextDesc.ContextName + "' already exists"); + } + + var factoryServiceContext = new ContextControllerFactoryServiceContext(contextDesc.ContextName, servicesContext, contextDesc.ContextDetail, agentInstanceContext, isRecoveringResilient, statementResultEventType); + var contextManager = servicesContext.ContextManagerFactoryService.Make(contextDesc.ContextDetail, factoryServiceContext); + + factoryServiceContext.AgentInstanceContextCreate.EpStatementAgentInstanceHandle.FilterFaultHandler = contextManager; + + _contexts.Put(contextDesc.ContextName, new ContextManagerEntry(contextManager)); + } + + public int ContextCount + { + get { return _contexts.Count; } + } + + public ContextDescriptor GetContextDescriptor(String contextName) + { + ContextManagerEntry entry = _contexts.Get(contextName); + if (entry == null) + { + return null; + } + return entry.ContextManager.ContextDescriptor; + } + + public ContextManager GetContextManager(String contextName) + { + ContextManagerEntry entry = _contexts.Get(contextName); + return entry == null ? null : entry.ContextManager; + } + + public void AddStatement(String contextName, ContextControllerStatementBase statement, bool isRecoveringResilient) + { + ContextManagerEntry entry = _contexts.Get(contextName); + if (entry == null) + { + throw new ExprValidationException(GetNotDecaredText(contextName)); + } + entry.AddStatement(statement.StatementContext.StatementId); + entry.ContextManager.AddStatement(statement, isRecoveringResilient); + } + + public void DestroyedStatement(String contextName, String statementName, int statementId) + { + ContextManagerEntry entry = _contexts.Get(contextName); + if (entry == null) + { + Log.Warn("Dispose statement for statement '" + statementName + "' failed to locate corresponding context manager '" + contextName + "'"); + return; + } + entry.RemoveStatement(statementId); + entry.ContextManager.DestroyStatement(statementName, statementId); + + if (entry.StatementCount == 0 && _destroyedContexts.Contains(contextName)) + { + DestroyContext(contextName, entry); + } + } + + public void StoppedStatement(String contextName, String statementName, int statementId, String epl, ExceptionHandlingService exceptionHandlingService) + { + ContextManagerEntry entry = _contexts.Get(contextName); + if (entry == null) + { + Log.Warn("Stop statement for statement '" + statementName + "' failed to locate corresponding context manager '" + contextName + "'"); + return; + } + try + { + entry.ContextManager.StopStatement(statementName, statementId); + } + catch (Exception ex) + { + exceptionHandlingService.HandleException(ex, statementName, epl, ExceptionHandlerExceptionType.STOP, null); + } + } + + public void DestroyedContext(String contextName) + { + ContextManagerEntry entry = _contexts.Get(contextName); + if (entry == null) + { + Log.Warn("Dispose for context '" + contextName + "' failed to locate corresponding context manager '" + contextName + "'"); + return; + } + if (entry.StatementCount == 0) + { + DestroyContext(contextName, entry); + } + else + { + // some remaining statements have references + _destroyedContexts.Add(contextName); + } + } + + private void DestroyContext(String contextName, ContextManagerEntry entry) + { + entry.ContextManager.SafeDestroy(); + _contexts.Remove(contextName); + _destroyedContexts.Remove(contextName); + } + + public IDictionary Contexts + { + get { return _contexts; } + } + + private String GetNotDecaredText(String contextName) + { + return "Context by name '" + contextName + "' has not been declared"; + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextManager.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextManager.cs new file mode 100755 index 000000000..59c7a5d90 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextManager.cs @@ -0,0 +1,42 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.client.context; +using com.espertech.esper.core.context.util; +using com.espertech.esper.filter; + +namespace com.espertech.esper.core.context.mgr +{ + public interface ContextManager : FilterFaultHandler + { + ContextDescriptor ContextDescriptor { get; } + int NumNestingLevels { get; } + ContextStateCache ContextStateCache { get; } + + void AddStatement(ContextControllerStatementBase statement, bool isRecoveringResilient); + void StopStatement(String statementName, int statementId); + void DestroyStatement(String statementName, int statementId); + + void SafeDestroy(); + + FilterSpecLookupable GetFilterLookupable(EventType eventType); + + ContextStatePathDescriptor ExtractPaths(ContextPartitionSelector contextPartitionSelector); + ContextStatePathDescriptor ExtractStopPaths(ContextPartitionSelector contextPartitionSelector); + ContextStatePathDescriptor ExtractDestroyPaths(ContextPartitionSelector contextPartitionSelector); + void ImportStartPaths(ContextControllerState state, AgentInstanceSelector agentInstanceSelector); + IDictionary StartPaths(ContextPartitionSelector contextPartitionSelector); + + ICollection GetAgentInstanceIds(ContextPartitionSelector contextPartitionSelector); + IDictionary Statements { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextManagerEntry.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextManagerEntry.cs new file mode 100755 index 000000000..f0cee5381 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextManagerEntry.cs @@ -0,0 +1,44 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +namespace com.espertech.esper.core.context.mgr +{ + public class ContextManagerEntry + { + private readonly ContextManager _contextManager; + private readonly ISet _referringStatements; + + public ContextManagerEntry(ContextManager contextManager) + { + _contextManager = contextManager; + _referringStatements = new HashSet(); + } + + public ContextManager ContextManager + { + get { return _contextManager; } + } + + public void AddStatement(int statementId) + { + _referringStatements.Add(statementId); + } + + public int StatementCount + { + get { return _referringStatements.Count; } + } + + public void RemoveStatement(int statementId) + { + _referringStatements.Remove(statementId); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextManagerFactoryService.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextManagerFactoryService.cs new file mode 100755 index 000000000..97c719440 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextManagerFactoryService.cs @@ -0,0 +1,18 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.core.context.mgr +{ + public interface ContextManagerFactoryService + { + ContextManager Make(ContextDetail contextDetail, ContextControllerFactoryServiceContext factoryServiceContext); + bool IsSupportsExtract { get; } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextManagerFactoryServiceImpl.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextManagerFactoryServiceImpl.cs new file mode 100755 index 000000000..a081ff5a0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextManagerFactoryServiceImpl.cs @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.core.context.mgr +{ + public class ContextManagerFactoryServiceImpl : ContextManagerFactoryService + { + public ContextManager Make(ContextDetail contextDetail, ContextControllerFactoryServiceContext factoryServiceContext) + { + if (contextDetail is ContextDetailNested) + { + return new ContextManagerNested(factoryServiceContext); + } + return new ContextManagerImpl(factoryServiceContext); + } + + public bool IsSupportsExtract + { + get { return true; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextManagerImpl.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextManagerImpl.cs new file mode 100755 index 000000000..df44a2220 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextManagerImpl.cs @@ -0,0 +1,557 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.client.context; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.threading; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.events; +using com.espertech.esper.filter; + +namespace com.espertech.esper.core.context.mgr +{ + public class ContextManagerImpl + : ContextManager + , ContextControllerLifecycleCallback + , ContextEnumeratorHandler + , FilterFaultHandler + { + private readonly ILockable _uLock; + private readonly String _contextName; + private readonly EPServicesContext _servicesContext; + private readonly ContextControllerFactory _factory; + private readonly IDictionary _statements = new LinkedHashMap(); // retain order of statement creation + private readonly ContextDescriptor _contextDescriptor; + private readonly IDictionary _agentInstances = new LinkedHashMap(); + + /// The single root context. This represents the context declared first. + private readonly ContextController _rootContext; + private readonly ContextPartitionIdManager _contextPartitionIdManager; + + public ContextManagerImpl(ContextControllerFactoryServiceContext factoryServiceContext) + { + _uLock = LockManager.CreateLock(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + _contextName = factoryServiceContext.ContextName; + _servicesContext = factoryServiceContext.ServicesContext; + _factory = factoryServiceContext.AgentInstanceContextCreate.StatementContext.ContextControllerFactoryService.GetFactory(factoryServiceContext)[0]; + _rootContext = _factory.CreateNoCallback(0, this); // single instance: created here and activated/deactivated later + _contextPartitionIdManager = factoryServiceContext.AgentInstanceContextCreate.StatementContext.ContextControllerFactoryService.AllocatePartitionIdMgr(_contextName, factoryServiceContext.AgentInstanceContextCreate.StatementContext.StatementId); + + var resourceRegistryFactory = _factory.StatementAIResourceRegistryFactory; + + var contextProps = _factory.ContextBuiltinProps; + var contextPropsType = _servicesContext.EventAdapterService.CreateAnonymousMapType(_contextName, contextProps, true); + var registry = new ContextPropertyRegistryImpl(_factory.ContextDetailPartitionItems, contextPropsType); + _contextDescriptor = new ContextDescriptor(_contextName, _factory.IsSingleInstanceContext, registry, resourceRegistryFactory, this, _factory.ContextDetail); + } + + public int NumNestingLevels + { + get { return 1; } + } + + public IDictionary Statements + { + get { return _statements; } + } + + public ContextDescriptor ContextDescriptor + { + get { return _contextDescriptor; } + } + + public ContextStateCache ContextStateCache + { + get { return _factory.StateCache; } + } + + public void AddStatement(ContextControllerStatementBase statement, bool isRecoveringResilient) + { + // validation down the hierarchy + var caches = _factory.ValidateStatement(statement); + + // add statement + var desc = new ContextControllerStatementDesc(statement, new ContextControllerStatementCtxCache[] { caches }); + _statements.Put(statement.StatementContext.StatementId, desc); + + // activate if this is the first statement + if (_statements.Count == 1) + { + Activate(); // this may itself trigger a callback + } + // activate statement in respect to existing context partitions + else + { + foreach (var entry in _agentInstances) + { + if (entry.Value.State == ContextPartitionState.STARTED) + { + var agentInstance = StartStatement(entry.Key, desc, _rootContext, entry.Value.InitPartitionKey, entry.Value.InitContextProperties, isRecoveringResilient); + entry.Value.AgentInstances.Add(agentInstance); + } + } + } + } + + public void StopStatement(String statementName, int statementId) + { + using (_uLock.Acquire()) + { + DestroyStatement(statementName, statementId); + } + } + + public void DestroyStatement(String statementName, int statementId) + { + using (_uLock.Acquire()) + { + if (!_statements.ContainsKey(statementId)) + { + return; + } + if (_statements.Count == 1) + { + SafeDestroy(); + } + else + { + RemoveStatement(statementId); + } + } + } + + public void SafeDestroy() + { + if (_rootContext != null) + { + // deactivate + _rootContext.Deactivate(); + _factory.FactoryContext.StateCache.RemoveContext(_contextName); + + foreach (var entryCP in _agentInstances) + { + StatementAgentInstanceUtil.StopAgentInstances(entryCP.Value.AgentInstances, null, _servicesContext, true, false); + } + _agentInstances.Clear(); + _contextPartitionIdManager.Clear(); + _statements.Clear(); + } + } + + public ContextControllerInstanceHandle ContextPartitionInstantiate( + int? optionalContextPartitionId, + int subPathId, + int? importSubpathId, + ContextController originator, + EventBean optionalTriggeringEvent, + IDictionary optionalTriggeringPattern, + object partitionKey, + IDictionary contextProperties, + ContextControllerState states, + ContextInternalFilterAddendum filterAddendum, + bool isRecoveringResilient, + ContextPartitionState state) + { + using (_uLock.Acquire()) + { + // assign context id + int assignedContextId; + if (optionalContextPartitionId != null && !states.IsImported) + { + assignedContextId = optionalContextPartitionId.Value; + _contextPartitionIdManager.AddExisting(optionalContextPartitionId.Value); + } + else + { + assignedContextId = _contextPartitionIdManager.AllocateId(); + if (states != null && states.PartitionImportCallback != null && optionalContextPartitionId != null) + { + states.PartitionImportCallback.Allocated(assignedContextId, optionalContextPartitionId.Value); + } + } + + // handle leaf creation + IList newInstances = new List(); + if (state == ContextPartitionState.STARTED) + { + foreach (var statementEntry in _statements) + { + var statementDesc = statementEntry.Value; + var instance = StartStatement(assignedContextId, statementDesc, originator, partitionKey, contextProperties, isRecoveringResilient); + newInstances.Add(instance); + } + } + + // for all new contexts: evaluate this event for this statement + if (optionalTriggeringEvent != null || optionalTriggeringPattern != null) + { + StatementAgentInstanceUtil.EvaluateEventForStatement(_servicesContext, optionalTriggeringEvent, optionalTriggeringPattern, newInstances); + } + + // save leaf + var filterVersion = _servicesContext.FilterService.FiltersVersion; + var agentInstanceList = new ContextControllerTreeAgentInstanceList(filterVersion, partitionKey, contextProperties, newInstances, state); + _agentInstances.Put(assignedContextId, agentInstanceList); + + return new ContextNestedHandleImpl(subPathId, assignedContextId, agentInstanceList); + } + } + + public void ContextPartitionTerminate(ContextControllerInstanceHandle contextNestedHandle, IDictionary terminationProperties, bool leaveLocksAcquired, IList agentInstancesCollected) + { + using (_uLock.Acquire()) + { + var handle = (ContextNestedHandleImpl)contextNestedHandle; + var entry = _agentInstances.Delete(handle.ContextPartitionOrPathId); + if (entry != null) + { + StatementAgentInstanceUtil.StopAgentInstances(entry.AgentInstances, terminationProperties, _servicesContext, false, leaveLocksAcquired); + if (agentInstancesCollected != null) + { + agentInstancesCollected.AddAll(entry.AgentInstances); + } + entry.AgentInstances.Clear(); + _contextPartitionIdManager.RemoveId(contextNestedHandle.ContextPartitionOrPathId); + } + } + } + + public void ContextPartitionNavigate(ContextControllerInstanceHandle existingHandle, ContextController originator, ContextControllerState controllerState, int exportedCPOrPathId, ContextInternalFilterAddendum filterAddendum, AgentInstanceSelector agentInstanceSelector, byte[] payload, bool isRecoveringResilient) + { + var entry = _agentInstances.Get(existingHandle.ContextPartitionOrPathId); + if (entry == null) + { + return; + } + + if (entry.State == ContextPartitionState.STOPPED) + { + entry.State = ContextPartitionState.STARTED; + entry.AgentInstances.Clear(); + foreach (var statement in _statements) + { + var instance = StartStatement(existingHandle.ContextPartitionOrPathId, statement.Value, originator, entry.InitPartitionKey, entry.InitContextProperties, false); + entry.AgentInstances.Add(instance); + } + var key = new ContextStatePathKey(1, 0, existingHandle.SubPathId); + var value = new ContextStatePathValue(existingHandle.ContextPartitionOrPathId, payload, ContextPartitionState.STARTED); + _rootContext.Factory.FactoryContext.StateCache.UpdateContextPath(_contextName, key, value); + } + else + { + IList removed = new List(2); + IList added = new List(2); + foreach (var agentInstance in entry.AgentInstances) + { + if (!agentInstanceSelector.Select(agentInstance)) + { + continue; + } + + // remove + StatementAgentInstanceUtil.StopAgentInstanceRemoveResources(agentInstance, null, _servicesContext, false, false); + removed.Add(agentInstance); + + // start + var statementDesc = _statements.Get(agentInstance.AgentInstanceContext.StatementId); + var instance = StartStatement(existingHandle.ContextPartitionOrPathId, statementDesc, originator, entry.InitPartitionKey, entry.InitContextProperties, isRecoveringResilient); + added.Add(instance); + + if (controllerState.PartitionImportCallback != null) + { + controllerState.PartitionImportCallback.Existing(existingHandle.ContextPartitionOrPathId, exportedCPOrPathId); + } + } + entry.AgentInstances.RemoveAll(removed); + entry.AgentInstances.AddAll(added); + } + } + + public FilterSpecLookupable GetFilterLookupable(EventType eventType) + { + return _factory.GetFilterLookupable(eventType); + } + + public IEnumerator GetEnumerator(int statementId) + { + using (_uLock.Acquire()) + { + var instances = GetAgentInstancesForStmt(statementId); + return instances.SelectMany(instance => instance.FinalView).GetEnumerator(); + } + } + + public IEnumerator GetSafeEnumerator(int statementId) + { + using (_uLock.Acquire()) + { + var instances = GetAgentInstancesForStmt(statementId); + return GetEnumeratorWithInstanceLock(instances); + } + } + + private static IEnumerator GetEnumeratorWithInstanceLock(AgentInstance[] instances) + { + foreach (var instance in instances) + { + var instanceLock = instance.AgentInstanceContext.EpStatementAgentInstanceHandle.StatementAgentInstanceLock; + using (instanceLock.AcquireWriteLock()) + { + foreach (var eventBean in instance.FinalView) + { + yield return eventBean; + } + } + } + } + + public IEnumerator GetEnumerator(int statementId, ContextPartitionSelector selector) + { + using (_uLock.Acquire()) + { + var instances = GetAgentInstancesForStmt(statementId, selector); + return instances.SelectMany(instance => instance.FinalView).GetEnumerator(); + } + } + + public IEnumerator GetSafeEnumerator(int statementId, ContextPartitionSelector selector) + { + using (_uLock.Acquire()) + { + var instances = GetAgentInstancesForStmt(statementId, selector); + return GetEnumeratorWithInstanceLock(instances); + } + } + + public ICollection GetAgentInstanceIds(ContextPartitionSelector selector) + { + if (selector is ContextPartitionSelectorById) + { + var byId = (ContextPartitionSelectorById)selector; + var ids = byId.ContextPartitionIds; + if (ids == null || ids.IsEmpty()) + { + return Collections.GetEmptyList(); + } + var agentInstanceIds = new List(ids); + agentInstanceIds.RetainAll(_agentInstances.Keys); + return agentInstanceIds; + } + else if (selector is ContextPartitionSelectorAll) + { + return new List(_agentInstances.Keys); + } + else + { + var visitor = new ContextPartitionVisitorAgentInstanceId(1); + _rootContext.VisitSelectedPartitions(selector, visitor); + return visitor.AgentInstanceIds; + } + } + + public ContextStatePathDescriptor ExtractPaths(ContextPartitionSelector selector) + { + var visitor = new ContextPartitionVisitorState(); + _rootContext.VisitSelectedPartitions(selector, visitor); + return new ContextStatePathDescriptor(visitor.States, visitor.ContextPartitionInfo); + } + + public ContextStatePathDescriptor ExtractStopPaths(ContextPartitionSelector selector) + { + var states = ExtractPaths(selector); + foreach (var entry in states.Paths) + { + var agentInstanceId = entry.Value.OptionalContextPartitionId.Value; + var list = _agentInstances.Get(agentInstanceId); + list.State = ContextPartitionState.STOPPED; + StatementAgentInstanceUtil.StopAgentInstances(list.AgentInstances, null, _servicesContext, false, false); + list.ClearAgentInstances(); + entry.Value.State = ContextPartitionState.STOPPED; + _rootContext.Factory.FactoryContext.StateCache.UpdateContextPath(_contextName, entry.Key, entry.Value); + } + return states; + } + + public ContextStatePathDescriptor ExtractDestroyPaths(ContextPartitionSelector selector) + { + var states = ExtractPaths(selector); + foreach (var entry in states.Paths) + { + var agentInstanceId = entry.Value.OptionalContextPartitionId.Value; + var descriptor = states.ContextPartitionInformation.Get(agentInstanceId); + _rootContext.DeletePath(descriptor.Identifier); + var list = _agentInstances.Delete(agentInstanceId); + StatementAgentInstanceUtil.StopAgentInstances(list.AgentInstances, null, _servicesContext, false, false); + list.ClearAgentInstances(); + _rootContext.Factory.FactoryContext.StateCache.RemoveContextPath(_contextName, entry.Key.Level, entry.Key.ParentPath, entry.Key.SubPath); + } + return states; + } + + public IDictionary StartPaths(ContextPartitionSelector selector) + { + var states = ExtractPaths(selector); + foreach (var entry in states.Paths) + { + var agentInstanceId = entry.Value.OptionalContextPartitionId.GetValueOrDefault(); + var list = _agentInstances.Get(agentInstanceId); + if (list.State == ContextPartitionState.STARTED) + { + continue; + } + list.State = ContextPartitionState.STARTED; + entry.Value.State = ContextPartitionState.STARTED; + foreach (var statement in _statements) + { + var instance = StartStatement(agentInstanceId, statement.Value, _rootContext, list.InitPartitionKey, list.InitContextProperties, false); + list.AgentInstances.Add(instance); + } + _rootContext.Factory.FactoryContext.StateCache.UpdateContextPath(_contextName, entry.Key, entry.Value); + } + SetState(states.ContextPartitionInformation, ContextPartitionState.STARTED); + return states.ContextPartitionInformation; + } + + public void ImportStartPaths(ContextControllerState state, AgentInstanceSelector agentInstanceSelector) + { + _rootContext.ImportContextPartitions(state, 0, null, agentInstanceSelector); + } + + public bool HandleFilterFault(EventBean theEvent, long version) + { + using (_uLock.Acquire()) + { + StatementAgentInstanceUtil.HandleFilterFault(theEvent, version, _servicesContext, _agentInstances); + return false; + } + } + + private void Activate() + { + _rootContext.Activate(null, null, null, null, null); + } + + private AgentInstance[] GetAgentInstancesForStmt(int statementId, ContextPartitionSelector selector) + { + var agentInstanceIds = GetAgentInstanceIds(selector); + if (agentInstanceIds == null || agentInstanceIds.IsEmpty()) + { + return new AgentInstance[0]; + } + + IList instances = new List(agentInstanceIds.Count); + foreach (var agentInstanceId in agentInstanceIds) + { + var instancesList = _agentInstances.Get(agentInstanceId); + if (instancesList != null) + { + foreach (var instance in instancesList.AgentInstances) + { + if (instance.AgentInstanceContext.StatementContext.StatementId == statementId) + { + instances.Add(instance); + } + } + } + } + return instances.ToArray(); + } + + private AgentInstance[] GetAgentInstancesForStmt(int statementId) + { + IList instances = new List(); + foreach (var contextPartitionEntry in _agentInstances) + { + foreach (var instance in contextPartitionEntry.Value.AgentInstances) + { + if (instance.AgentInstanceContext.StatementContext.StatementId == statementId) + { + instances.Add(instance); + } + } + } + return instances.ToArray(); + } + + private void RemoveStatement(int statementId) + { + var statementDesc = _statements.Get(statementId); + if (statementDesc == null) + { + return; + } + + foreach (var contextPartitionEntry in _agentInstances) + { + var instanceList = contextPartitionEntry.Value.AgentInstances + .Where(instance => instance.AgentInstanceContext.StatementContext.StatementId == statementId) + .ToList(); + + instanceList.ForEach( + instance => + { + StatementAgentInstanceUtil.Stop( + instance.StopCallback, instance.AgentInstanceContext, instance.FinalView, _servicesContext, + true, false, true); + contextPartitionEntry.Value.AgentInstances.Remove(instance); + }); + } + + _statements.Remove(statementId); + } + + private AgentInstance StartStatement(int contextId, ContextControllerStatementDesc statementDesc, ContextController originator, Object partitionKey, IDictionary contextProperties, bool isRecoveringResilient) + { + // build filters + var filterAddendum = new IdentityDictionary(); + originator.Factory.PopulateFilterAddendums(filterAddendum, statementDesc, partitionKey, contextId); + AgentInstanceFilterProxy proxy = new AgentInstanceFilterProxyImpl(filterAddendum); + + // build built-in context properties + contextProperties.Put(ContextPropertyEventType.PROP_CTX_NAME, _contextName); + contextProperties.Put(ContextPropertyEventType.PROP_CTX_ID, contextId); + var contextBean = (MappedEventBean)_servicesContext.EventAdapterService.AdapterForTypedMap(contextProperties, _contextDescriptor.ContextPropertyRegistry.ContextEventType); + + // activate + var result = StatementAgentInstanceUtil.Start(_servicesContext, statementDesc.Statement, false, contextId, contextBean, proxy, isRecoveringResilient); + + // save only instance data + return new AgentInstance(result.StopCallback, result.AgentInstanceContext, result.FinalView); + } + + internal static void SetState(IDictionary original, ContextPartitionState state) + { + foreach (var entry in original) + { + entry.Value.State = state; + } + } + + public class ContextNestedHandleImpl : ContextControllerInstanceHandle + { + public ContextNestedHandleImpl(int subPathId, int contextPartitionId, ContextControllerTreeAgentInstanceList instances) + { + SubPathId = subPathId; + ContextPartitionOrPathId = contextPartitionId; + Instances = instances; + } + + public int ContextPartitionOrPathId { get; private set; } + + public ContextControllerTreeAgentInstanceList Instances { get; private set; } + + public int SubPathId { get; private set; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextManagerNested.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextManagerNested.cs new file mode 100755 index 000000000..e925fae7f --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextManagerNested.cs @@ -0,0 +1,1012 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.client.context; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.compat.threading; +using com.espertech.esper.core.context.stmt; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.spec; +using com.espertech.esper.events; +using com.espertech.esper.filter; +using com.espertech.esper.type; + +namespace com.espertech.esper.core.context.mgr +{ + public class ContextManagerNested + : ContextManager + , ContextControllerLifecycleCallback + , ContextEnumeratorHandler + , FilterFaultHandler + { + private static readonly ILog Log = + LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly ILockable _iLock = + LockManager.CreateLock(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly String _contextName; + private readonly EPServicesContext _servicesContext; + + private readonly ContextControllerFactory[] _nestedContextFactories; + + private readonly IDictionary _statements = + new LinkedHashMap(); // retain order of statement creation + + private readonly ContextDescriptor _contextDescriptor; + + /// + /// The single root context. This represents the context declared first. + /// + private ContextController _rootContext; + + /// + /// Double-linked tree of sub-contexts. An entry exists for all branches including the root. For example + /// with 2 contexts declared this map has entries representing the root and all second-level sub-contexts. + /// For example with 3 contexts declared this map has entries for the root, second and third-level contexts. + /// + private readonly IDictionary _subcontexts = + new Dictionary().WithNullSupport(); + + private readonly ContextPartitionIdManager _contextPartitionIdManager; + + public ContextManagerNested(ContextControllerFactoryServiceContext factoryServiceContext) + { + _contextName = factoryServiceContext.ContextName; + _servicesContext = factoryServiceContext.ServicesContext; + _contextPartitionIdManager = factoryServiceContext.AgentInstanceContextCreate.StatementContext.ContextControllerFactoryService.AllocatePartitionIdMgr( + _contextName, factoryServiceContext.AgentInstanceContextCreate.StatementContext.StatementId); + _nestedContextFactories = factoryServiceContext.AgentInstanceContextCreate.StatementContext.ContextControllerFactoryService.GetFactory( + factoryServiceContext); + + StatementAIResourceRegistryFactory resourceRegistryFactory = + () => new StatementAIResourceRegistry(new AIRegistryAggregationMap(), new AIRegistryExprMap()); + + var contextProps = ContextPropertyEventType.GetNestedTypeBase(); + foreach (var factory in _nestedContextFactories) + { + contextProps.Put(factory.FactoryContext.ContextName, factory.ContextBuiltinProps); + } + var contextPropsType = _servicesContext.EventAdapterService.CreateAnonymousMapType( + _contextName, contextProps, true); + var registry = new ContextPropertyRegistryImpl( + Collections.GetEmptyList(), contextPropsType); + _contextDescriptor = new ContextDescriptor( + _contextName, false, registry, resourceRegistryFactory, this, factoryServiceContext.Detail); + } + + public IDictionary Statements + { + get { return _statements; } + } + + public ContextDescriptor ContextDescriptor + { + get { return _contextDescriptor; } + } + + public int NumNestingLevels + { + get { return _nestedContextFactories.Length; } + } + + public IEnumerator GetEnumerator(int statementId, ContextPartitionSelector selector) + { + using (_iLock.Acquire()) + { + var instances = GetAgentInstancesForStmt(statementId, selector); + return instances.SelectMany(instance => instance.FinalView).GetEnumerator(); + } + } + + public IEnumerator GetSafeEnumerator(int statementId, ContextPartitionSelector selector) + { + using (_iLock.Acquire()) + { + var instances = GetAgentInstancesForStmt(statementId, selector); + return GetEnumeratorWithInstanceLock(instances); + } + } + + private static IEnumerator GetEnumeratorWithInstanceLock(AgentInstance[] instances) + { + foreach (var instance in instances) + { + var instanceLock = instance.AgentInstanceContext.EpStatementAgentInstanceHandle.StatementAgentInstanceLock; + using (instanceLock.AcquireWriteLock()) + { + foreach (var eventBean in instance.FinalView) + { + yield return eventBean; + } + } + } + } + + public ICollection GetAgentInstanceIds(ContextPartitionSelector contextPartitionSelector) + { + return GetAgentInstancesForSelector(contextPartitionSelector); + } + + public void ImportStartPaths(ContextControllerState state, AgentInstanceSelector agentInstanceSelector) + { + _rootContext.ImportContextPartitions(state, 0, null, agentInstanceSelector); + } + + internal static ContextPartitionIdentifier[] GetTreeCompositeKey( + ContextControllerFactory[] nestedContextFactories, + Object initPartitionKey, + ContextControllerTreeEntry treeEntry, + IDictionary subcontexts) + { + var length = nestedContextFactories.Length; + var keys = new ContextPartitionIdentifier[length]; + keys[length - 1] = nestedContextFactories[length - 1].KeyPayloadToIdentifier(initPartitionKey); + keys[length - 2] = nestedContextFactories[length - 2].KeyPayloadToIdentifier(treeEntry.InitPartitionKey); + + // get parent's parent + if (length > 2) + { + var parent = treeEntry.Parent; + var parentEntry = subcontexts.Get(parent); + for (var i = 0; i < length - 2; i++) + { + keys[length - 2 - i] = + nestedContextFactories[length - 2 - i].KeyPayloadToIdentifier(parentEntry.InitPartitionKey); + parent = parentEntry.Parent; + parentEntry = subcontexts.Get(parent); + } + } + + return keys; + } + + public ContextStatePathDescriptor ExtractPaths(ContextPartitionSelector selector) + { + var visitor = GetContextPartitionPathsInternal(selector); + return new ContextStatePathDescriptor(visitor.States, visitor.AgentInstanceInfo); + } + + public ContextStatePathDescriptor ExtractStopPaths(ContextPartitionSelector selector) + { + var visitor = GetContextPartitionPathsInternal(selector); + foreach (var entry in visitor.ControllerAgentInstances) + { + var treeEntry = _subcontexts.Get(entry.Key); + foreach (var leaf in entry.Value) + { + var agentInstanceId = leaf.Value.OptionalContextPartitionId.GetValueOrDefault(); + var list = treeEntry.AgentInstances.Get(agentInstanceId); + list.State = ContextPartitionState.STOPPED; + StatementAgentInstanceUtil.StopAgentInstances( + list.AgentInstances, null, _servicesContext, false, false); + list.ClearAgentInstances(); + leaf.Value.State = ContextPartitionState.STOPPED; + _rootContext.Factory.FactoryContext.StateCache.UpdateContextPath(_contextName, leaf.Key, leaf.Value); + } + } + return new ContextStatePathDescriptor(visitor.States, visitor.AgentInstanceInfo); + } + + public ContextStatePathDescriptor ExtractDestroyPaths(ContextPartitionSelector selector) + { + var visitor = GetContextPartitionPathsInternal(selector); + foreach (var entry in visitor.ControllerAgentInstances) + { + var treeEntry = _subcontexts.Get(entry.Key); + foreach (var leaf in entry.Value) + { + var agentInstanceId = leaf.Value.OptionalContextPartitionId.GetValueOrDefault(); + var list = treeEntry.AgentInstances.Get(agentInstanceId); + StatementAgentInstanceUtil.StopAgentInstances( + list.AgentInstances, null, _servicesContext, false, false); + _rootContext.Factory.FactoryContext.StateCache.RemoveContextPath( + _contextName, leaf.Key.Level, leaf.Key.ParentPath, leaf.Key.SubPath); + var descriptor = visitor.AgentInstanceInfo.Get(agentInstanceId); + var nestedIdent = (ContextPartitionIdentifierNested)descriptor.Identifier; + entry.Key.DeletePath(nestedIdent.Identifiers[_nestedContextFactories.Length - 1]); + } + } + return new ContextStatePathDescriptor(visitor.States, visitor.AgentInstanceInfo); + } + + public IDictionary StartPaths(ContextPartitionSelector selector) + { + var visitor = GetContextPartitionPathsInternal(selector); + foreach (var entry in visitor.ControllerAgentInstances) + { + var treeEntry = _subcontexts.Get(entry.Key); + + foreach (var leaf in entry.Value) + { + int agentInstanceId = leaf.Value.OptionalContextPartitionId.GetValueOrDefault(); + var list = treeEntry.AgentInstances.Get(agentInstanceId); + if (list.State == ContextPartitionState.STARTED) + { + continue; + } + foreach (var statement in _statements) + { + var instance = StartStatement( + agentInstanceId, statement.Value, _rootContext, list.InitPartitionKey, + list.InitContextProperties, false); + list.AgentInstances.Add(instance); + } + list.State = ContextPartitionState.STARTED; + leaf.Value.State = ContextPartitionState.STARTED; + _rootContext.Factory.FactoryContext.StateCache.UpdateContextPath(_contextName, leaf.Key, leaf.Value); + } + } + ContextManagerImpl.SetState(visitor.AgentInstanceInfo, ContextPartitionState.STARTED); + return visitor.AgentInstanceInfo; + } + + public ContextPartitionVisitorStateWithPath GetContextPartitionPathsInternal(ContextPartitionSelector selector) + { + var visitor = new ContextPartitionVisitorStateWithPath(_nestedContextFactories, _subcontexts); + IList selectors; + if (selector is ContextPartitionSelectorNested) + { + var nested = (ContextPartitionSelectorNested)selector; + selectors = nested.Selectors; + } + else if (selector is ContextPartitionSelectorAll) + { + var all = new ContextPartitionSelector[NumNestingLevels]; + all.Fill(selector); + selectors = Collections.SingletonList(all); + } + else + { + throw new ArgumentException("Invalid selector for nested context"); + } + foreach (var item in selectors) + { + RecursivePopulateSelector(_rootContext, 1, item, visitor); + } + return visitor; + } + + public void AddStatement(ContextControllerStatementBase statement, bool isRecoveringResilient) + { + // validation down the hierarchy + var caches = new ContextControllerStatementCtxCache[_nestedContextFactories.Length]; + for (var i = 0; i < _nestedContextFactories.Length; i++) + { + var nested = _nestedContextFactories[i]; + caches[i] = nested.ValidateStatement(statement); + } + + // save statement + var desc = new ContextControllerStatementDesc(statement, caches); + _statements.Put(statement.StatementContext.StatementId, desc); + + // activate if this is the first statement + if (_statements.Count == 1) + { + Activate(); // this may itself trigger a callback + } + // activate statement in respect to existing context partitions + else + { + foreach (var subcontext in _subcontexts) + { + if (subcontext.Key.Factory.FactoryContext.NestingLevel != _nestedContextFactories.Length) + { + continue; + } + if (subcontext.Value.AgentInstances == null || subcontext.Value.AgentInstances.IsEmpty()) + { + continue; + } + + foreach (var entry in subcontext.Value.AgentInstances) + { + if (entry.Value.State == ContextPartitionState.STARTED) + { + var agentInstance = StartStatement( + entry.Key, desc, subcontext.Key, entry.Value.InitPartitionKey, + entry.Value.InitContextProperties, isRecoveringResilient); + entry.Value.AgentInstances.Add(agentInstance); + } + } + } + } + } + + public void StopStatement(String statementName, int statementId) + { + using (_iLock.Acquire()) + { + DestroyStatement(statementName, statementId); + } + } + + public void DestroyStatement(String statementName, int statementId) + { + using (_iLock.Acquire()) + { + if (!_statements.ContainsKey(statementId)) + { + return; + } + if (_statements.Count == 1) + { + SafeDestroy(); + } + else + { + RemoveStatement(statementId); + } + } + } + + public void SafeDestroy() + { + if (_rootContext != null) + { + RecursiveDeactivateStop(_rootContext, false, null); + _nestedContextFactories[0].FactoryContext.StateCache.RemoveContext(_contextName); + _rootContext = null; + _statements.Clear(); + _subcontexts.Clear(); + _contextPartitionIdManager.Clear(); + } + } + + public FilterSpecLookupable GetFilterLookupable(EventType eventType) + { + throw new UnsupportedOperationException(); + } + + public void ContextPartitionNavigate( + ContextControllerInstanceHandle existingHandle, + ContextController originator, + ContextControllerState controllerState, + int exportedCPOrPathId, + ContextInternalFilterAddendum filterAddendum, + AgentInstanceSelector agentInstanceSelector, + byte[] payload, + bool isRecoveringResilient) + { + var nestedHandle = (ContextManagerNestedInstanceHandle)existingHandle; + + // detect non-leaf + var nestingLevel = originator.Factory.FactoryContext.NestingLevel; // starts at 1 for root + if (nestingLevel < _nestedContextFactories.Length) + { + nestedHandle.Controller.ImportContextPartitions( + controllerState, exportedCPOrPathId, filterAddendum, agentInstanceSelector); + return; + } + + var entry = _subcontexts.Get(originator); + if (entry == null) + { + return; + } + foreach (var cpEntry in entry.AgentInstances.ToArray()) + { + if (cpEntry.Value.State == ContextPartitionState.STOPPED) + { + cpEntry.Value.State = ContextPartitionState.STARTED; + entry.AgentInstances.Clear(); + foreach (var statement in _statements) + { + var instance = StartStatement( + existingHandle.ContextPartitionOrPathId, statement.Value, originator, + cpEntry.Value.InitPartitionKey, entry.InitContextProperties, false); + cpEntry.Value.AgentInstances.Add(instance); + } + var key = new ContextStatePathKey( + _nestedContextFactories.Length, originator.PathId, existingHandle.SubPathId); + var value = new ContextStatePathValue( + existingHandle.ContextPartitionOrPathId, payload, ContextPartitionState.STARTED); + originator.Factory.FactoryContext.StateCache.UpdateContextPath(_contextName, key, value); + } + else + { + IList removed = new List(2); + IList added = new List(2); + var current = cpEntry.Value.AgentInstances; + + foreach (var agentInstance in current) + { + if (!agentInstanceSelector.Select(agentInstance)) + { + continue; + } + + // remove + StatementAgentInstanceUtil.StopAgentInstanceRemoveResources( + agentInstance, null, _servicesContext, false, false); + removed.Add(agentInstance); + + // start + var statementDesc = _statements.Get(agentInstance.AgentInstanceContext.StatementId); + var instance = StartStatement( + cpEntry.Key, statementDesc, originator, cpEntry.Value.InitPartitionKey, + entry.InitContextProperties, false); + added.Add(instance); + + if (controllerState.PartitionImportCallback != null) + { + controllerState.PartitionImportCallback.Existing( + existingHandle.ContextPartitionOrPathId, exportedCPOrPathId); + } + } + current.RemoveAll(removed); + current.AddAll(added); + } + } + } + + public ContextControllerInstanceHandle ContextPartitionInstantiate( + int? optionalContextPartitionId, + int subPathId, + int? importSubpathId, + ContextController originator, + EventBean optionalTriggeringEvent, + IDictionary optionalTriggeringPattern, + Object partitionKey, + IDictionary contextProperties, + ContextControllerState states, + ContextInternalFilterAddendum filterAddendum, + bool isRecoveringResilient, + ContextPartitionState state) + { + using (_iLock.Acquire()) + { + ContextControllerTreeEntry entry; + + // detect non-leaf + var nestingLevel = originator.Factory.FactoryContext.NestingLevel; // starts at 1 for root + if (nestingLevel < _nestedContextFactories.Length) + { + // next sub-sontext + var nextFactory = _nestedContextFactories[originator.Factory.FactoryContext.NestingLevel]; + var nextContext = nextFactory.CreateNoCallback(subPathId, this); + + // link current context to sub-context + var branch = _subcontexts.Get(originator); + if (branch.ChildContexts == null) + { + branch.ChildContexts = new Dictionary(); + } + branch.ChildContexts.Put(subPathId, nextContext); + + // save child branch, linking sub-context to its parent + entry = new ContextControllerTreeEntry(originator, null, partitionKey, contextProperties); + _subcontexts.Put(nextContext, entry); + + // now post-initialize, this may actually call back + nextContext.Activate( + optionalTriggeringEvent, optionalTriggeringPattern, states, filterAddendum, importSubpathId); + + if (Log.IsDebugEnabled) + { + Log.Debug( + "Instantiating branch context path for " + _contextName + + " from level " + originator.Factory.FactoryContext.NestingLevel + + "(" + originator.Factory.FactoryContext.ContextName + ")" + + " parentPath " + originator.PathId + + " for level " + nextContext.Factory.FactoryContext.NestingLevel + + "(" + nextContext.Factory.FactoryContext.ContextName + ")" + + " childPath " + subPathId + ); + } + + return new ContextManagerNestedInstanceHandle(subPathId, nextContext, subPathId, true, null); + } + + // assign context id + int assignedContextId; + if (optionalContextPartitionId != null && !states.IsImported) + { + assignedContextId = optionalContextPartitionId.Value; + _contextPartitionIdManager.AddExisting(optionalContextPartitionId.Value); + } + else + { + assignedContextId = _contextPartitionIdManager.AllocateId(); + if (states != null && states.PartitionImportCallback != null && optionalContextPartitionId != null) + { + states.PartitionImportCallback.Allocated(assignedContextId, optionalContextPartitionId.Value); + } + } + + if (Log.IsDebugEnabled) + { + Log.Debug( + "Instantiating agent instance for " + _contextName + + " from level " + originator.Factory.FactoryContext.NestingLevel + + "(" + originator.Factory.FactoryContext.ContextName + ")" + + " parentPath " + originator.PathId + + " contextPartId " + assignedContextId); + } + + // handle leaf creation + IList newInstances = new List(); + if (state == ContextPartitionState.STARTED) + { + foreach (var statementEntry in _statements) + { + var statementDesc = statementEntry.Value; + var instance = StartStatement( + assignedContextId, statementDesc, originator, partitionKey, contextProperties, + isRecoveringResilient); + newInstances.Add(instance); + } + } + + // for all new contexts: evaluate this event for this statement + if (optionalTriggeringEvent != null) + { + StatementAgentInstanceUtil.EvaluateEventForStatement( + _servicesContext, optionalTriggeringEvent, optionalTriggeringPattern, newInstances); + } + + // save leaf + entry = _subcontexts.Get(originator); + if (entry.AgentInstances == null) + { + entry.AgentInstances = new LinkedHashMap(); + } + + var filterVersion = _servicesContext.FilterService.FiltersVersion; + var agentInstanceList = new ContextControllerTreeAgentInstanceList( + filterVersion, partitionKey, contextProperties, newInstances, state); + entry.AgentInstances.Put(assignedContextId, agentInstanceList); + + return new ContextManagerNestedInstanceHandle( + subPathId, originator, assignedContextId, false, agentInstanceList); + } + } + + public virtual bool HandleFilterFault(EventBean theEvent, long version) + { + using (_iLock.Acquire()) + { + foreach (var entry in _subcontexts) + { + if (entry.Value.AgentInstances != null) + { + StatementAgentInstanceUtil.HandleFilterFault( + theEvent, version, _servicesContext, entry.Value.AgentInstances); + } + } + + return false; + } + } + + public ContextStateCache ContextStateCache + { + get { return _rootContext.Factory.StateCache; } + } + + /// + /// Provides the sub-context that ends. + /// + /// The context nested handle. + /// The termination properties. + /// if set to true [leave locks acquired]. + /// The agent instances. + public void ContextPartitionTerminate( + ContextControllerInstanceHandle contextNestedHandle, + IDictionary terminationProperties, + bool leaveLocksAcquired, + IList agentInstances) + { + var handle = (ContextManagerNestedInstanceHandle)contextNestedHandle; + if (handle.IsBranch) + { + var branchHandle = handle; + var branch = branchHandle.Controller; + RecursiveDeactivateStop(branch, leaveLocksAcquired, agentInstances); + + if (Log.IsDebugEnabled) + { + Log.Debug( + "Terminated context branch for " + _contextName + + " from level " + branch.Factory.FactoryContext.NestingLevel + + "(" + branch.Factory.FactoryContext.ContextName + ")" + + " parentPath " + branch.PathId); + } + } + else + { + var leafHandle = handle; + var leaf = leafHandle.Controller; + var leafEntry = _subcontexts.Get(leaf); + if (leafEntry != null) + { + // could be terminated earlier + var ailist = leafEntry.AgentInstances.Get(leafHandle.ContextPartitionOrPathId); + if (ailist != null) + { + StatementAgentInstanceUtil.StopAgentInstances( + ailist.AgentInstances, null, _servicesContext, false, false); + _contextPartitionIdManager.RemoveId(leafHandle.ContextPartitionOrPathId); + ailist.AgentInstances.Clear(); + } + + if (Log.IsDebugEnabled) + { + Log.Debug( + "Terminated context leaf for " + _contextName + + " from level " + leaf.Factory.FactoryContext.NestingLevel + + "(" + leaf.Factory.FactoryContext.ContextName + ")" + + " parentPath " + leaf.PathId + + " contextPartId " + leafHandle.ContextPartitionOrPathId); + } + } + } + } + + public IEnumerator GetEnumerator(int statementId) + { + using (_iLock.Acquire()) + { + var instances = GetAgentInstancesForStmt(statementId); + foreach (var instance in instances) + { + foreach (var eventBean in instance.FinalView) + { + yield return eventBean; + } + } + } + } + + public IEnumerator GetSafeEnumerator(int statementId) + { + using (_iLock.Acquire()) + { + var instances = GetAgentInstancesForStmt(statementId); + + foreach (var instance in instances) + { + using (instance.AgentInstanceContext.EpStatementAgentInstanceHandle.StatementAgentInstanceLock.AcquireWriteLock()) + { + foreach (var eventBean in instance.FinalView) + { + yield return eventBean; + } + } + } + } + } + + private AgentInstance StartStatement( + int contextId, + ContextControllerStatementDesc statementDesc, + ContextController originator, + Object partitionKey, + IDictionary contextProperties, + bool isRecoveringResilient) + { + // build filters + var proxy = GetMergedFilterAddendums(statementDesc, originator, partitionKey, contextId); + + // build built-in context properties + var properties = ContextPropertyEventType.GetNestedBeanBase(_contextName, contextId); + properties.Put( + _nestedContextFactories[_nestedContextFactories.Length - 1].FactoryContext.ContextName, + contextProperties); + RecursivePopulateBuiltinProps(originator, properties); + properties.Put(ContextPropertyEventType.PROP_CTX_NAME, _contextName); + properties.Put(ContextPropertyEventType.PROP_CTX_ID, contextId); + var contextBean = + (MappedEventBean) + _servicesContext.EventAdapterService.AdapterForTypedMap( + properties, _contextDescriptor.ContextPropertyRegistry.ContextEventType); + + // activate + var result = StatementAgentInstanceUtil.Start( + _servicesContext, statementDesc.Statement, false, contextId, contextBean, proxy, isRecoveringResilient); + return new AgentInstance(result.StopCallback, result.AgentInstanceContext, result.FinalView); + } + + private void RecursivePopulateBuiltinProps(ContextController originator, IDictionary properties) + { + var entry = _subcontexts.Get(originator); + if (entry != null) + { + if (entry.InitContextProperties != null) + { + properties.Put(entry.Parent.Factory.FactoryContext.ContextName, entry.InitContextProperties); + } + if (entry.Parent != null && entry.Parent.Factory.FactoryContext.NestingLevel > 1) + { + RecursivePopulateBuiltinProps(entry.Parent, properties); + } + } + } + + private AgentInstanceFilterProxy GetMergedFilterAddendums( + ContextControllerStatementDesc statement, + ContextController originator, + Object partitionKey, + int contextId) + { + var result = new IdentityDictionary(); + originator.Factory.PopulateFilterAddendums(result, statement, partitionKey, contextId); + var originatorEntry = _subcontexts.Get(originator); + if (originatorEntry != null) + { + RecursivePopulateFilterAddendum(statement, originatorEntry, contextId, result); + } + return new AgentInstanceFilterProxyImpl(result); + } + + private void RecursivePopulateFilterAddendum( + ContextControllerStatementDesc statement, + ContextControllerTreeEntry originatorEntry, + int contextId, + IdentityDictionary result) + { + if (originatorEntry.Parent == null) + { + return; + } + originatorEntry.Parent.Factory.PopulateFilterAddendums( + result, statement, originatorEntry.InitPartitionKey, contextId); + + var parentEntry = _subcontexts.Get(originatorEntry.Parent); + if (parentEntry != null) + { + RecursivePopulateFilterAddendum(statement, parentEntry, contextId, result); + } + } + + private void Activate() + { + _rootContext = _nestedContextFactories[0].CreateNoCallback(0, this); + _subcontexts.Put(_rootContext, new ContextControllerTreeEntry(null, null, null, null)); + _rootContext.Activate(null, null, null, null, null); + } + + private void RemoveStatement(int statementId) + { + var statementDesc = _statements.Get(statementId); + if (statementDesc == null) + { + return; + } + + foreach (var entry in _subcontexts) + { + // ignore branches + if (entry.Key.Factory.FactoryContext.NestingLevel < _nestedContextFactories.Length) + { + continue; + } + if (entry.Value.AgentInstances == null || entry.Value.AgentInstances.IsEmpty()) + { + continue; + } + + foreach (var contextPartitionEntry in entry.Value.AgentInstances) + { + var agentInstances = contextPartitionEntry.Value.AgentInstances; + + for (int ii = agentInstances.Count - 1; ii >= 0; ii--) + { + var instance = agentInstances[ii]; + if (instance.AgentInstanceContext.StatementContext.StatementId != statementId) + { + continue; + } + StatementAgentInstanceUtil.Stop( + instance.StopCallback, instance.AgentInstanceContext, instance.FinalView, _servicesContext, + true, false, true); + agentInstances.RemoveAt(ii); + } + } + } + + _statements.Remove(statementId); + } + + private void RecursiveDeactivateStop( + ContextController currentContext, + bool leaveLocksAcquired, + IList agentInstancesCollected) + { + // deactivate + currentContext.Deactivate(); + + // remove state + var entry = _subcontexts.Delete(currentContext); + if (entry == null) + { + return; + } + + // remove from parent + var parent = _subcontexts.Get(entry.Parent); + if (parent != null) + { + parent.ChildContexts.Remove(currentContext.PathId); + } + + // stop instances + if (entry.AgentInstances != null) + { + foreach (var entryCP in entry.AgentInstances) + { + StatementAgentInstanceUtil.StopAgentInstances( + entryCP.Value.AgentInstances, null, _servicesContext, false, leaveLocksAcquired); + if (agentInstancesCollected != null) + { + agentInstancesCollected.AddAll(entryCP.Value.AgentInstances); + } + _contextPartitionIdManager.RemoveId(entryCP.Key); + } + } + + // deactivate child contexts + if (entry.ChildContexts == null || entry.ChildContexts.IsEmpty()) + { + return; + } + foreach (ContextController inner in entry.ChildContexts.Values) + { + RecursiveDeactivateStop(inner, leaveLocksAcquired, agentInstancesCollected); + } + } + + private AgentInstance[] GetAgentInstancesForStmt(int statementId) + { + var instances = new List(); + foreach (var subcontext in _subcontexts) + { + if (subcontext.Key.Factory.FactoryContext.NestingLevel != _nestedContextFactories.Length) + { + continue; + } + if (subcontext.Value.AgentInstances == null || subcontext.Value.AgentInstances.IsEmpty()) + { + continue; + } + + foreach (var entry in subcontext.Value.AgentInstances) + { + foreach (var ai in entry.Value.AgentInstances) + { + if (ai.AgentInstanceContext.StatementContext.StatementId == statementId) + { + instances.Add(ai); + } + } + } + } + return instances.ToArray(); + } + + private AgentInstance[] GetAgentInstancesForStmt(int statementId, ContextPartitionSelector selector) + { + var agentInstanceIds = GetAgentInstancesForSelector(selector); + if (agentInstanceIds == null || agentInstanceIds.IsEmpty()) + { + return new AgentInstance[0]; + } + + IList instances = new List(agentInstanceIds.Count); + foreach (var subcontext in _subcontexts) + { + if (subcontext.Key.Factory.FactoryContext.NestingLevel != _nestedContextFactories.Length) + { + continue; + } + if (subcontext.Value.AgentInstances == null || subcontext.Value.AgentInstances.IsEmpty()) + { + continue; + } + + foreach (int agentInstanceId in agentInstanceIds) + { + var instancesList = subcontext.Value.AgentInstances.Get(agentInstanceId); + if (instancesList != null) + { + var instanceIt = instancesList.AgentInstances.GetEnumerator(); + while (instanceIt.MoveNext()) + { + var instance = instanceIt.Current; + if (instance.AgentInstanceContext.StatementContext.StatementId == statementId) + { + instances.Add(instance); + } + } + } + } + } + return instances.ToArray(); + } + + private ICollection GetAgentInstancesForSelector(ContextPartitionSelector selector) + { + if (selector is ContextPartitionSelectorById) + { + var byId = (ContextPartitionSelectorById)selector; + var ids = byId.ContextPartitionIds; + if (ids == null || ids.IsEmpty()) + { + return Collections.GetEmptyList(); + } + var agentInstanceIds = new List(ids); + agentInstanceIds.RetainAll(_contextPartitionIdManager.Ids); + return agentInstanceIds; + } + else if (selector is ContextPartitionSelectorAll) + { + return new List(_contextPartitionIdManager.Ids); + } + else if (selector is ContextPartitionSelectorNested) + { + var nested = (ContextPartitionSelectorNested)selector; + var visitor = new ContextPartitionVisitorAgentInstanceIdWPath(_nestedContextFactories.Length); + foreach (var item in nested.Selectors) + { + RecursivePopulateSelector(_rootContext, 1, item, visitor); + } + return visitor.AgentInstanceIds; + } + throw ContextControllerSelectorUtil.GetInvalidSelector( + new Type[] + { + typeof (ContextPartitionSelectorNested) + }, selector, true); + } + + private void RecursivePopulateSelector( + ContextController currentContext, + int level, + ContextPartitionSelector[] selectorStack, + ContextPartitionVisitorWithPath visitor) + { + var entry = _subcontexts.Get(currentContext); + if (entry == null) + { + return; + } + var selector = selectorStack[level - 1]; + + // handle branch + if (level < _nestedContextFactories.Length) + { + visitor.ResetSubPaths(); + currentContext.VisitSelectedPartitions(selector, visitor); + var selectedPaths = new List(visitor.Subpaths); + foreach (int path in selectedPaths) + { + var controller = entry.ChildContexts.Get(path); + if (controller != null) + { + RecursivePopulateSelector(controller, level + 1, selectorStack, visitor); + } + } + } + // handle leaf + else + { + currentContext.VisitSelectedPartitions(selector, visitor); + } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextManagerNestedInstanceHandle.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextManagerNestedInstanceHandle.cs new file mode 100755 index 000000000..0d6f12392 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextManagerNestedInstanceHandle.cs @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.core.context.mgr +{ + public class ContextManagerNestedInstanceHandle : ContextControllerInstanceHandle + { + private readonly int _subPathId; + private readonly ContextController _controller; + private readonly int _contextPartitionOrPathId; + private readonly bool _branch; + private readonly ContextControllerTreeAgentInstanceList _branchAgentInstances; + + public ContextManagerNestedInstanceHandle(int subPathId, ContextController controller, int contextPartitionOrPathId, bool branch, ContextControllerTreeAgentInstanceList branchAgentInstances) + { + _subPathId = subPathId; + _controller = controller; + _contextPartitionOrPathId = contextPartitionOrPathId; + _branch = branch; + _branchAgentInstances = branchAgentInstances; + } + + public int SubPathId + { + get { return _subPathId; } + } + + public ContextController Controller + { + get { return _controller; } + } + + public int ContextPartitionOrPathId + { + get { return _contextPartitionOrPathId; } + } + + public bool IsBranch + { + get { return _branch; } + } + + public ContextControllerTreeAgentInstanceList Instances + { + get { return _branchAgentInstances; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextManagerStats.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextManagerStats.cs new file mode 100755 index 000000000..b6b6e4c37 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextManagerStats.cs @@ -0,0 +1,22 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.core.context.mgr +{ + public class ContextManagerStats { + private readonly int statementCount; + + public ContextManagerStats(int statementCount) { + this.statementCount = statementCount; + } + + public int GetStatementCount() { + return statementCount; + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextPartitionIdManager.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextPartitionIdManager.cs new file mode 100755 index 000000000..ecea77f68 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextPartitionIdManager.cs @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +namespace com.espertech.esper.core.context.mgr +{ + public interface ContextPartitionIdManager + { + void Clear(); + + void AddExisting(int contextPartitionId); + + int AllocateId(); + + void RemoveId(int contextPartitionId); + + ICollection Ids { get; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextPartitionIdManagerImpl.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextPartitionIdManagerImpl.cs new file mode 100755 index 000000000..36b2b9a25 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextPartitionIdManagerImpl.cs @@ -0,0 +1,64 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +namespace com.espertech.esper.core.context.mgr +{ + public class ContextPartitionIdManagerImpl : ContextPartitionIdManager + { + private int _lastAssignedId = -1; + + public ContextPartitionIdManagerImpl() + { + Ids = new HashSet(); + } + + #region ContextPartitionIdManager Members + + public void Clear() + { + Ids.Clear(); + } + + public void AddExisting(int contextPartitionId) + { + Ids.Add(contextPartitionId); + } + + public int AllocateId() + { + while (true) + { + if (_lastAssignedId < int.MaxValue) + { + _lastAssignedId++; + } + else + { + _lastAssignedId = 0; + } + + if (!Ids.Contains(_lastAssignedId)) + { + Ids.Add(_lastAssignedId); + return _lastAssignedId; + } + } + } + + public void RemoveId(int contextPartitionId) + { + Ids.Remove(contextPartitionId); + } + + #endregion + + public ICollection Ids { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextPartitionVisitor.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextPartitionVisitor.cs new file mode 100755 index 000000000..09ff1d281 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextPartitionVisitor.cs @@ -0,0 +1,21 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.core.context.mgr +{ + public interface ContextPartitionVisitor { + void Visit(int nestingLevel, + int pathId, + ContextStatePathValueBinding binding, + Object payload, + ContextController contextController, + ContextControllerInstanceHandle instanceHandle); + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextPartitionVisitorAgentInstanceId.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextPartitionVisitorAgentInstanceId.cs new file mode 100755 index 000000000..b4ff19270 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextPartitionVisitorAgentInstanceId.cs @@ -0,0 +1,43 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.core.context.mgr +{ + public class ContextPartitionVisitorAgentInstanceId : ContextPartitionVisitor + { + private readonly List _agentInstanceIds = new List(); + private readonly int _maxNestingLevel; + + public ContextPartitionVisitorAgentInstanceId(int maxNestingLevel) + { + _maxNestingLevel = maxNestingLevel; + } + + public IList AgentInstanceIds + { + get { return _agentInstanceIds; } + } + + public void Visit( + int nestingLevel, + int pathId, + ContextStatePathValueBinding binding, + Object payload, + ContextController contextController, + ContextControllerInstanceHandle instanceHandle) + { + if (nestingLevel == _maxNestingLevel) + { + _agentInstanceIds.Add(instanceHandle.ContextPartitionOrPathId); + } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextPartitionVisitorAgentInstanceIdWPath.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextPartitionVisitorAgentInstanceIdWPath.cs new file mode 100755 index 000000000..661cc32fb --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextPartitionVisitorAgentInstanceIdWPath.cs @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.core.context.mgr +{ + public class ContextPartitionVisitorAgentInstanceIdWPath : ContextPartitionVisitorWithPath + { + private readonly int _maxNestingLevel; + private readonly List _agentInstanceIds = new List(); + private readonly List _subpaths = new List(); + + public ContextPartitionVisitorAgentInstanceIdWPath(int maxNestingLevel) + { + _maxNestingLevel = maxNestingLevel; + } + + public void Visit(int nestingLevel, int pathId, ContextStatePathValueBinding binding, Object payload, ContextController contextController, ContextControllerInstanceHandle instanceHandle) + { + if (nestingLevel == _maxNestingLevel) { + _agentInstanceIds.Add(instanceHandle.ContextPartitionOrPathId); + } + else { + _subpaths.Add(instanceHandle.ContextPartitionOrPathId); + } + } + + public IList AgentInstanceIds + { + get { return _agentInstanceIds; } + } + + public void ResetSubPaths() + { + _subpaths.Clear(); + } + + public ICollection Subpaths + { + get { return _subpaths; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextPartitionVisitorState.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextPartitionVisitorState.cs new file mode 100755 index 000000000..1df4f36e3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextPartitionVisitorState.cs @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client.context; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.core.context.mgr +{ + public class ContextPartitionVisitorState : ContextPartitionVisitor + { + public ContextPartitionVisitorState() + { + States = new OrderedDictionary(); + ContextPartitionInfo = new Dictionary(); + } + + public void Visit(int nestingLevel, int pathId, ContextStatePathValueBinding binding, Object payload, ContextController contextController, ContextControllerInstanceHandle instanceHandle) + { + ContextStatePathKey key = new ContextStatePathKey(nestingLevel, pathId, instanceHandle.SubPathId); + int agentInstanceId = instanceHandle.ContextPartitionOrPathId; + States.Put(key, new ContextStatePathValue(agentInstanceId, binding.ToByteArray(payload), instanceHandle.Instances.State)); + + ContextPartitionState state = instanceHandle.Instances.State; + ContextPartitionIdentifier identifier = contextController.Factory.KeyPayloadToIdentifier(payload); + ContextPartitionDescriptor descriptor = new ContextPartitionDescriptor(agentInstanceId, identifier, state); + ContextPartitionInfo.Put(agentInstanceId, descriptor); + } + + public OrderedDictionary States { get; private set; } + + public IDictionary ContextPartitionInfo { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextPartitionVisitorStateWithPath.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextPartitionVisitorStateWithPath.cs new file mode 100755 index 000000000..1a05cc053 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextPartitionVisitorStateWithPath.cs @@ -0,0 +1,86 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client.context; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.core.context.mgr +{ + public class ContextPartitionVisitorStateWithPath : ContextPartitionVisitorWithPath + { + private readonly ContextControllerFactory[] _nestedFactories; + private readonly IDictionary _subcontexts; + + public ContextPartitionVisitorStateWithPath(ContextControllerFactory[] nestedFactories, IDictionary subcontexts) + { + AgentInstanceInfo = new Dictionary(); + ControllerAgentInstances = new Dictionary>(); + Subpaths = new List(); + States = new OrderedDictionary(); + _nestedFactories = nestedFactories; + _subcontexts = subcontexts; + } + + public void Visit(int nestingLevel, int pathId, ContextStatePathValueBinding binding, Object payload, ContextController contextController, ContextControllerInstanceHandle instanceHandle) + { + var key = new ContextStatePathKey(nestingLevel, pathId, instanceHandle.SubPathId); + int maxNestingLevel = _nestedFactories.Length; + + int contextPartitionOrSubPath = instanceHandle.ContextPartitionOrPathId; + + if (contextController.Factory.FactoryContext.NestingLevel == maxNestingLevel) { + var agentInstances = ControllerAgentInstances.Get(contextController); + if (agentInstances == null) { + agentInstances = new List(); + ControllerAgentInstances.Put(contextController, agentInstances); + } + var value = new ContextStatePathValue(contextPartitionOrSubPath, binding.ToByteArray(payload), instanceHandle.Instances.State); + agentInstances.Add(new LeafDesc(key, value)); + + // generate a nice payload text from the paths of keys + var entry = _subcontexts.Get(contextController); + var keys = ContextManagerNested.GetTreeCompositeKey(_nestedFactories, payload, entry, _subcontexts); + var descriptor = new ContextPartitionDescriptor(contextPartitionOrSubPath, new ContextPartitionIdentifierNested(keys), value.State); + AgentInstanceInfo.Put(contextPartitionOrSubPath, descriptor); + States.Put(key, value); + } + else { + // handle non-leaf + Subpaths.Add(contextPartitionOrSubPath); + States.Put(key, new ContextStatePathValue(contextPartitionOrSubPath, binding.ToByteArray(payload), ContextPartitionState.STARTED)); + } + } + + public OrderedDictionary States { get; private set; } + + public void ResetSubPaths() + { + Subpaths.Clear(); + } + + public ICollection Subpaths { get; private set; } + + public IDictionary> ControllerAgentInstances { get; private set; } + + public IDictionary AgentInstanceInfo { get; private set; } + + public class LeafDesc + { + public LeafDesc(ContextStatePathKey key, ContextStatePathValue value) { + Key = key; + Value = value; + } + + public ContextStatePathKey Key; + public ContextStatePathValue Value; + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextPartitionVisitorWithPath.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextPartitionVisitorWithPath.cs new file mode 100755 index 000000000..c849a29a3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextPartitionVisitorWithPath.cs @@ -0,0 +1,18 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +namespace com.espertech.esper.core.context.mgr +{ + public interface ContextPartitionVisitorWithPath : ContextPartitionVisitor + { + void ResetSubPaths(); + ICollection Subpaths { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextPropertyEventType.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextPropertyEventType.cs new file mode 100755 index 000000000..19f26ceb0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextPropertyEventType.cs @@ -0,0 +1,220 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.core.context.mgr +{ + public class ContextPropertyEventType + { + public static readonly String PROP_CTX_NAME = "name"; + public static readonly String PROP_CTX_ID = "id"; + public static readonly String PROP_CTX_LABEL = "label"; + public static readonly String PROP_CTX_STARTTIME = "startTime"; + public static readonly String PROP_CTX_ENDTIME = "endTime"; + public static readonly String PROP_CTX_KEY_PREFIX = "key"; + + private readonly static List LIST_INITIATEDTERM_PROPS; + private readonly static List LIST_CATEGORY_PROPS; + private readonly static List LIST_PARTITION_PROPS; + private readonly static List LIST_HASH_PROPS; + private readonly static List LIST_NESTED_PROPS; + + static ContextPropertyEventType() + { + LIST_INITIATEDTERM_PROPS = new List(); + LIST_INITIATEDTERM_PROPS.Add(new ContextProperty(PROP_CTX_ID, typeof(int))); + LIST_INITIATEDTERM_PROPS.Add(new ContextProperty(PROP_CTX_NAME, typeof(String))); + LIST_INITIATEDTERM_PROPS.Add(new ContextProperty(PROP_CTX_STARTTIME, typeof(long))); + LIST_INITIATEDTERM_PROPS.Add(new ContextProperty(PROP_CTX_ENDTIME, typeof(long))); + + LIST_CATEGORY_PROPS = new List + { + new ContextProperty(PROP_CTX_NAME, typeof (String)), + new ContextProperty(PROP_CTX_ID, typeof (int)), + new ContextProperty(PROP_CTX_LABEL, typeof (String)) + }; + + LIST_PARTITION_PROPS = new List + { + new ContextProperty(PROP_CTX_NAME, typeof (String)), + new ContextProperty(PROP_CTX_ID, typeof (int)) + }; + + LIST_HASH_PROPS = new List + { + new ContextProperty(PROP_CTX_NAME, typeof (String)), + new ContextProperty(PROP_CTX_ID, typeof (int)) + }; + + LIST_NESTED_PROPS = new List + { + new ContextProperty(PROP_CTX_NAME, typeof (String)), + new ContextProperty(PROP_CTX_ID, typeof (int)) + }; + } + + public static IDictionary GetCategorizedType() + { + return MakeEventType(LIST_CATEGORY_PROPS, Collections.EmptyDataMap); + } + + public static IDictionary GetCategorizedBean(String contextName, int agentInstanceId, String label) + { + IDictionary props = new Dictionary(); + props.Put(PROP_CTX_NAME, contextName); + props.Put(PROP_CTX_ID, agentInstanceId); + props.Put(PROP_CTX_LABEL, label); + return props; + } + + public static IDictionary GetInitiatedTerminatedType() + { + return MakeEventType(LIST_INITIATEDTERM_PROPS, Collections.EmptyDataMap); + } + + public static void AddEndpointTypes(String contextName, ContextDetailCondition endpoint, IDictionary properties, ICollection allTags) + { + if (endpoint is ContextDetailConditionFilter) + { + var filter = (ContextDetailConditionFilter)endpoint; + if (filter.OptionalFilterAsName != null) + { + if (properties.ContainsKey(filter.OptionalFilterAsName)) + { + throw new ExprValidationException("For context '" + contextName + "' the stream or tag name '" + filter.OptionalFilterAsName + "' is already declared"); + } + allTags.Add(filter.OptionalFilterAsName); + properties.Put(filter.OptionalFilterAsName, filter.FilterSpecCompiled.FilterForEventType); + } + } + if (endpoint is ContextDetailConditionPattern) + { + var pattern = (ContextDetailConditionPattern)endpoint; + foreach (var entry in pattern.PatternCompiled.TaggedEventTypes) + { + if (properties.ContainsKey(entry.Key) && !properties.Get(entry.Key).Equals(entry.Value.First)) + { + throw new ExprValidationException("For context '" + contextName + "' the stream or tag name '" + entry.Key + "' is already declared"); + } + allTags.Add(entry.Key); + properties.Put(entry.Key, entry.Value.First); + } + } + } + + public static IDictionary GetTempOverlapBean(String contextName, int agentInstanceId, IDictionary matchEvent, EventBean theEvent, String filterAsName) + { + IDictionary props = new Dictionary(); + props.Put(PROP_CTX_NAME, contextName); + props.Put(PROP_CTX_ID, agentInstanceId); + if (matchEvent != null) + { + props.PutAll(matchEvent); + } + else + { + props.Put(filterAsName, theEvent); + } + return props; + } + + public static IDictionary GetPartitionType(ContextDetailPartitioned segmentedSpec, Type[] propertyTypes) + { + IDictionary props = new Dictionary(); + for (var i = 0; i < segmentedSpec.Items[0].PropertyNames.Count; i++) + { + var propertyName = PROP_CTX_KEY_PREFIX + (i + 1); + props.Put(propertyName, propertyTypes[i]); + } + return MakeEventType(LIST_PARTITION_PROPS, props); + } + + public static IDictionary GetPartitionBean(String contextName, int agentInstanceId, Object keyValue, IList propertyNames) + { + Object[] agentInstanceProperties; + if (propertyNames.Count == 1) + { + agentInstanceProperties = new[] { keyValue }; + } + else + { + agentInstanceProperties = ((MultiKeyUntyped)keyValue).Keys; + } + + IDictionary props = new Dictionary(); + props.Put(PROP_CTX_NAME, contextName); + props.Put(PROP_CTX_ID, agentInstanceId); + for (var i = 0; i < agentInstanceProperties.Length; i++) + { + var propertyName = PROP_CTX_KEY_PREFIX + (i + 1); + props.Put(propertyName, agentInstanceProperties[i]); + } + return props; + } + + public static IDictionary GetNestedTypeBase() + { + IDictionary props = new Dictionary(); + return MakeEventType(LIST_NESTED_PROPS, props); + } + + public static IDictionary GetNestedBeanBase(String contextName, int contextPartitionId) + { + IDictionary props = new Dictionary(); + props.Put(PROP_CTX_NAME, contextName); + props.Put(PROP_CTX_ID, contextPartitionId); + return props; + } + + public static IDictionary GetHashType() + { + return MakeEventType(LIST_HASH_PROPS, Collections.EmptyDataMap); + } + + public static IDictionary GetHashBean(String contextName, int agentInstanceId) + { + IDictionary props = new Dictionary(); + props.Put(PROP_CTX_NAME, contextName); + props.Put(PROP_CTX_ID, agentInstanceId); + return props; + } + + private static IDictionary MakeEventType(IEnumerable builtin, IDictionary additionalProperties) + { + IDictionary properties = new Dictionary(); + properties.PutAll(additionalProperties); + foreach (var prop in builtin) + { + properties.Put(prop.PropertyName, prop.PropertyType); + } + return properties; + } + + public class ContextProperty + { + public ContextProperty(String propertyName, Type propertyType) + { + PropertyName = propertyName; + PropertyType = propertyType; + } + + public string PropertyName { get; private set; } + + public Type PropertyType { get; private set; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextPropertyRegistryImpl.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextPropertyRegistryImpl.cs new file mode 100755 index 000000000..5fac75ddb --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextPropertyRegistryImpl.cs @@ -0,0 +1,79 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.core.context.mgr +{ + public class ContextPropertyRegistryImpl : ContextPropertyRegistry + { + public static readonly String CONTEXT_PREFIX = "context"; + public static ContextPropertyRegistry EMPTY_REGISTRY = new ContextPropertyRegistryImpl(null); + + private readonly EventType _contextEventType; + private readonly IList _partitionProperties; + + public ContextPropertyRegistryImpl(IList partitionProperties, + EventType contextEventType) + { + _partitionProperties = partitionProperties; + _contextEventType = contextEventType; + } + + public ContextPropertyRegistryImpl(EventType contextEventType) + { + _partitionProperties = new List(); + _contextEventType = contextEventType; + } + + #region ContextPropertyRegistry Members + + public bool IsPartitionProperty(EventType fromType, + String propertyName) + { + string name = GetPartitionContextPropertyName(fromType, propertyName); + return name != null; + } + + public String GetPartitionContextPropertyName(EventType fromType, + String propertyName) + { + foreach (ContextDetailPartitionItem item in _partitionProperties) + { + if (item.FilterSpecCompiled.FilterForEventType == fromType) + { + for (int i = 0; i < item.PropertyNames.Count; i++) + { + if (item.PropertyNames[i] == propertyName) + { + return ContextPropertyEventType.PROP_CTX_KEY_PREFIX + (i + 1); + } + } + } + } + return null; + } + + public bool IsContextPropertyPrefix(String prefixName) + { + return (prefixName != null) && (String.Equals(prefixName, CONTEXT_PREFIX, StringComparison.InvariantCultureIgnoreCase)); + } + + public EventType ContextEventType + { + get { return _contextEventType; } + } + + #endregion + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextStateCache.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextStateCache.cs new file mode 100755 index 000000000..230ac29c7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextStateCache.cs @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.core.context.mgr +{ + public interface ContextStateCache + { + ContextStatePathValueBinding GetBinding(Object bindingInfo); + void AddContextPath(String contextName, int level, int parentPath, int subPath, int? optionalContextPartitionId, Object additionalInfo, ContextStatePathValueBinding binding); + void UpdateContextPath(String contextName, ContextStatePathKey key, ContextStatePathValue value); + void RemoveContextParentPath(String contextName, int level, int parentPath); + void RemoveContextPath(String contextName, int level, int parentPath, int subPath); + void RemoveContext(String contextName); + OrderedDictionary GetContextPaths(String contextName); + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextStateCacheNoSave.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextStateCacheNoSave.cs new file mode 100755 index 000000000..280cbc089 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextStateCacheNoSave.cs @@ -0,0 +1,149 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.spec; +using com.espertech.esper.events; +using com.espertech.esper.util; + +namespace com.espertech.esper.core.context.mgr +{ + /// + /// For testing, only used within SPIs; Replaced by applicable EsperHA bindings. + /// + public class ContextStateCacheNoSave : ContextStateCache + { + public static ContextStatePathValueBinding DEFAULT_SPI_TEST_BINDING = + new MyContextStatePathValueBindingSerializable(); + + public ContextStatePathValueBinding GetBinding(Object bindingInfo) + { + if (bindingInfo is ContextDetailInitiatedTerminated) + { + return new ContextStateCacheNoSaveInitTermBinding(); + } + return DEFAULT_SPI_TEST_BINDING; + } + + public void UpdateContextPath(String contextName, ContextStatePathKey key, ContextStatePathValue value) + { + // no action required + } + + public void AddContextPath( + String contextName, + int level, + int parentPath, + int subPath, + int? optionalContextPartitionId, + Object additionalInfo, + ContextStatePathValueBinding binding) + { + // no action required + } + + public void RemoveContextParentPath(String contextName, int level, int parentPath) + { + // no action required + } + + public void RemoveContextPath(String contextName, int level, int parentPath, int subPath) + { + // no action required + } + + public OrderedDictionary GetContextPaths(String contextName) + { + return null; // no state required + } + + public void RemoveContext(String contextName) + { + // no action required + } + + /// For testing, only used within SPIs; Replaced by applicable EsperHA bindings. + public class MyContextStatePathValueBindingSerializable : ContextStatePathValueBinding + { + public Object ByteArrayToObject(byte[] bytes, EventAdapterService eventAdapterService) + { + return SerializerUtil.ByteArrToObject(bytes); + } + + public byte[] ToByteArray(Object contextInfo) + { + return SerializerUtil.ObjectToByteArr(contextInfo); + } + } + + /// + /// For testing, only used within SPIs; Replaced by applicable EsperHA bindings. + /// Simple binding where any events get changed to type name and byte array. + /// + public class ContextStateCacheNoSaveInitTermBinding : ContextStatePathValueBinding + { + public Object ByteArrayToObject(byte[] bytes, EventAdapterService eventAdapterService) + { + var state = (ContextControllerInitTermState) SerializerUtil.ByteArrToObject(bytes); + foreach (var entry in state.PatternData.ToArray()) + { + if (entry.Value is EventBeanNameValuePair) + { + var @event = (EventBeanNameValuePair) entry.Value; + var type = eventAdapterService.GetEventTypeByName(@event.EventTypeName); + var underlying = SerializerUtil.ByteArrToObject(@event.Bytes); + state.PatternData.Put(entry.Key, eventAdapterService.AdapterForType(underlying, type)); + } + } + return state; + } + + public byte[] ToByteArray(Object contextInfo) + { + var state = (ContextControllerInitTermState) contextInfo; + var serializableProps = new Dictionary(); + if (state.PatternData != null) + { + serializableProps.PutAll(state.PatternData); + foreach (var entry in state.PatternData) + { + if (entry.Value is EventBean) + { + var @event = (EventBean) entry.Value; + serializableProps.Put( + entry.Key, + new EventBeanNameValuePair( + @event.EventType.Name, SerializerUtil.ObjectToByteArr(@event.Underlying))); + } + } + } + var serialized = new ContextControllerInitTermState(state.StartTime, serializableProps); + return SerializerUtil.ObjectToByteArr(serialized); + } + } + + [Serializable] + private class EventBeanNameValuePair + { + internal EventBeanNameValuePair(String eventTypeName, byte[] bytes) + { + EventTypeName = eventTypeName; + Bytes = bytes; + } + + public string EventTypeName { get; private set; } + + public byte[] Bytes { get; private set; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextStatePathDescriptor.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextStatePathDescriptor.cs new file mode 100755 index 000000000..8a722fa7f --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextStatePathDescriptor.cs @@ -0,0 +1,30 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client.context; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.core.context.mgr +{ + public class ContextStatePathDescriptor + { + public ContextStatePathDescriptor( + OrderedDictionary paths, + IDictionary contextPartitionInformation) + { + Paths = paths; + ContextPartitionInformation = contextPartitionInformation; + } + + public OrderedDictionary Paths { get; private set; } + + public IDictionary ContextPartitionInformation { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextStatePathKey.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextStatePathKey.cs new file mode 100755 index 000000000..317abf0fc --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextStatePathKey.cs @@ -0,0 +1,90 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.core.context.mgr +{ + public class ContextStatePathKey : IComparable + { + private readonly int _level; + private readonly int _parentPath; + private readonly int _subPath; + + public ContextStatePathKey(int level, int parentPath, int subPath) + { + _level = level; + _parentPath = parentPath; + _subPath = subPath; + } + + public int Level + { + get { return _level; } + } + + public int ParentPath + { + get { return _parentPath; } + } + + public int SubPath + { + get { return _subPath; } + } + + public int CompareTo(Object o) + { + if (o.GetType() != typeof(ContextStatePathKey)) { + throw new ArgumentException("Cannot compare " + typeof(ContextStatePathKey).FullName + " to " + o.GetType().FullName); + } + var other = (ContextStatePathKey) o; + if (Level != other.Level) { + return Level < other.Level ? -1 : 1; + } + if (ParentPath != other.ParentPath) { + return ParentPath < other.ParentPath ? -1 : 1; + } + if (SubPath != other.SubPath) { + return SubPath < other.SubPath ? -1 : 1; + } + return 0; + } + + public override bool Equals(Object o) + { + if (this == o) return true; + if (o == null || GetType() != o.GetType()) return false; + + var that = (ContextStatePathKey) o; + + if (_level != that._level) return false; + if (_parentPath != that._parentPath) return false; + if (_subPath != that._subPath) return false; + + return true; + } + + public override int GetHashCode() + { + int result = _level; + result = 31 * result + _parentPath; + result = 31 * result + _subPath; + return result; + } + + public override String ToString() + { + return "ContextStatePathKey{" + + "level=" + _level + + ", parentPath=" + _parentPath + + ", subPath=" + _subPath + + '}'; + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextStatePathValue.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextStatePathValue.cs new file mode 100755 index 000000000..cc378dd3d --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextStatePathValue.cs @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client.context; + +namespace com.espertech.esper.core.context.mgr +{ + public class ContextStatePathValue + { + public ContextStatePathValue(int? optionalContextPartitionId, byte[] blob, ContextPartitionState state) + { + OptionalContextPartitionId = optionalContextPartitionId; + Blob = blob; + State = state; + } + + public int? OptionalContextPartitionId { get; private set; } + + public byte[] Blob { get; private set; } + + public ContextPartitionState State { get; set; } + + public override String ToString() + { + return "ContextStatePathValue{" + + "optionalContextPartitionId=" + OptionalContextPartitionId + + ", state=" + State + + '}'; + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/context/mgr/ContextStatePathValueBinding.cs b/NEsper.Core/NEsper.Core/core/context/mgr/ContextStatePathValueBinding.cs new file mode 100755 index 000000000..52f0f0f42 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/mgr/ContextStatePathValueBinding.cs @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.events; + +namespace com.espertech.esper.core.context.mgr +{ + public interface ContextStatePathValueBinding + { + Object ByteArrayToObject(byte[] bytes, EventAdapterService eventAdapterService); + byte[] ToByteArray(Object contextInfo); + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryAggregation.cs b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryAggregation.cs new file mode 100755 index 000000000..560fb3da5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryAggregation.cs @@ -0,0 +1,19 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.agg.service; + +namespace com.espertech.esper.core.context.stmt +{ + public interface AIRegistryAggregation : AggregationService + { + int InstanceCount { get; } + void AssignService(int serviceId, AggregationService aggregationService); + void DeassignService(int serviceId); + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryAggregationMap.cs b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryAggregationMap.cs new file mode 100755 index 000000000..aba27ad7d --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryAggregationMap.cs @@ -0,0 +1,104 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.core.context.stmt +{ + public class AIRegistryAggregationMap : AIRegistryAggregation { + private readonly IDictionary _services; + + public AIRegistryAggregationMap() { + _services = new Dictionary(); + } + + public void AssignService(int serviceId, AggregationService aggregationService) { + _services.Put(serviceId, aggregationService); + } + + public void DeassignService(int serviceId) { + _services.Remove(serviceId); + } + + public int InstanceCount + { + get { return _services.Count; } + } + + public void ApplyEnter(EventBean[] eventsPerStream, Object optionalGroupKeyPerRow, ExprEvaluatorContext exprEvaluatorContext) { + _services.Get(exprEvaluatorContext.AgentInstanceId).ApplyEnter(eventsPerStream, optionalGroupKeyPerRow, exprEvaluatorContext); + } + + public void ApplyLeave(EventBean[] eventsPerStream, Object optionalGroupKeyPerRow, ExprEvaluatorContext exprEvaluatorContext) { + _services.Get(exprEvaluatorContext.AgentInstanceId).ApplyLeave(eventsPerStream, optionalGroupKeyPerRow, exprEvaluatorContext); + } + + public void SetCurrentAccess(Object groupKey, int agentInstanceId, AggregationGroupByRollupLevel rollupLevel) { + _services.Get(agentInstanceId).SetCurrentAccess(groupKey, agentInstanceId, null); + } + + public AggregationService GetContextPartitionAggregationService(int agentInstanceId) { + return _services.Get(agentInstanceId); + } + + public void ClearResults(ExprEvaluatorContext exprEvaluatorContext) { + _services.Get(exprEvaluatorContext.AgentInstanceId).ClearResults(exprEvaluatorContext); + } + + public object GetValue(int column, int agentInstanceId, EvaluateParams evaluateParams) { + return _services.Get(agentInstanceId).GetValue(column, agentInstanceId, evaluateParams); + } + + public ICollection GetCollectionOfEvents(int column, EvaluateParams evaluateParams) { + return _services.Get(evaluateParams.ExprEvaluatorContext.AgentInstanceId).GetCollectionOfEvents(column, evaluateParams); + } + + public ICollection GetCollectionScalar(int column, EvaluateParams evaluateParams) { + return _services.Get(evaluateParams.ExprEvaluatorContext.AgentInstanceId).GetCollectionScalar(column, evaluateParams); + } + + public EventBean GetEventBean(int column, EvaluateParams evaluateParams) { + return _services.Get(evaluateParams.ExprEvaluatorContext.AgentInstanceId).GetEventBean(column, evaluateParams); + } + + public void SetRemovedCallback(AggregationRowRemovedCallback callback) { + // not applicable + } + + public void Accept(AggregationServiceVisitor visitor) { + throw new UnsupportedOperationException("Not applicable"); + } + + public void AcceptGroupDetail(AggregationServiceVisitorWGroupDetail visitor) { + throw new UnsupportedOperationException("Not applicable"); + } + + public bool IsGrouped + { + get { throw new UnsupportedOperationException("Not applicable"); } + } + + public Object GetGroupKey(int agentInstanceId) { + return _services.Get(agentInstanceId).GetGroupKey(agentInstanceId); + } + + public ICollection GetGroupKeys(ExprEvaluatorContext exprEvaluatorContext) { + return _services.Get(exprEvaluatorContext.AgentInstanceId).GetGroupKeys(exprEvaluatorContext); + } + + public void Stop() { + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryAggregationMultiPerm.cs b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryAggregationMultiPerm.cs new file mode 100755 index 000000000..d3952103a --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryAggregationMultiPerm.cs @@ -0,0 +1,143 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.core.context.stmt +{ + public class AIRegistryAggregationMultiPerm : AIRegistryAggregation + { + private readonly ArrayWrap _services; + private int _count; + + public AIRegistryAggregationMultiPerm() + { + _services = new ArrayWrap(2); + } + + public void AssignService(int serviceId, AggregationService aggregationService) + { + AIRegistryUtil.CheckExpand(serviceId, _services); + _services.Array[serviceId] = aggregationService; + _count++; + } + + public void DeassignService(int serviceId) + { + if (serviceId >= _services.Array.Length) + { + // possible since it may not have been assigned as there was nothing to assign + return; + } + _services.Array[serviceId] = null; + _count--; + } + + public int InstanceCount + { + get { return _count; } + } + + public void ApplyEnter( + EventBean[] eventsPerStream, + Object optionalGroupKeyPerRow, + ExprEvaluatorContext exprEvaluatorContext) + { + _services.Array[exprEvaluatorContext.AgentInstanceId].ApplyEnter( + eventsPerStream, optionalGroupKeyPerRow, exprEvaluatorContext); + } + + public void ApplyLeave( + EventBean[] eventsPerStream, + Object optionalGroupKeyPerRow, + ExprEvaluatorContext exprEvaluatorContext) + { + _services.Array[exprEvaluatorContext.AgentInstanceId].ApplyLeave( + eventsPerStream, optionalGroupKeyPerRow, exprEvaluatorContext); + } + + public void SetCurrentAccess(Object groupKey, int agentInstanceId, AggregationGroupByRollupLevel rollupLevel) + { + _services.Array[agentInstanceId].SetCurrentAccess(groupKey, agentInstanceId, null); + } + + public AggregationService GetContextPartitionAggregationService(int agentInstanceId) + { + return _services.Array[agentInstanceId]; + } + + public void ClearResults(ExprEvaluatorContext exprEvaluatorContext) + { + _services.Array[exprEvaluatorContext.AgentInstanceId].ClearResults(exprEvaluatorContext); + } + + public object GetValue(int column, int agentInstanceId, EvaluateParams evaluateParams) + { + return _services.Array[agentInstanceId].GetValue( + column, agentInstanceId, evaluateParams); + } + + public ICollection GetCollectionOfEvents(int column, EvaluateParams evaluateParams) + { + return _services.Array[evaluateParams.ExprEvaluatorContext.AgentInstanceId].GetCollectionOfEvents(column, evaluateParams); + } + + public ICollection GetCollectionScalar(int column, EvaluateParams evaluateParams) + { + return _services.Array[evaluateParams.ExprEvaluatorContext.AgentInstanceId].GetCollectionScalar(column, evaluateParams); + } + + public EventBean GetEventBean(int column, EvaluateParams evaluateParams) + { + return _services.Array[evaluateParams.ExprEvaluatorContext.AgentInstanceId].GetEventBean(column, evaluateParams); + } + + public void SetRemovedCallback(AggregationRowRemovedCallback callback) + { + // not applicable + } + + public void Accept(AggregationServiceVisitor visitor) + { + throw new UnsupportedOperationException("Not applicable"); + } + + public void AcceptGroupDetail(AggregationServiceVisitorWGroupDetail visitor) + { + throw new UnsupportedOperationException("Not applicable"); + } + + public bool IsGrouped + { + get { throw new UnsupportedOperationException("Not applicable"); } + } + + public Object GetGroupKey(int agentInstanceId) + { + return _services.Array[agentInstanceId].GetGroupKey(agentInstanceId); + } + + public ICollection GetGroupKeys(ExprEvaluatorContext exprEvaluatorContext) + { + return _services.Array[exprEvaluatorContext.AgentInstanceId].GetGroupKeys(exprEvaluatorContext); + } + + public void Stop() + { + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryAggregationSingle.cs b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryAggregationSingle.cs new file mode 100755 index 000000000..8b0d88e57 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryAggregationSingle.cs @@ -0,0 +1,102 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.core.context.stmt +{ + public class AIRegistryAggregationSingle : AIRegistryAggregation, AggregationService { + private AggregationService _service; + + public AIRegistryAggregationSingle() { + } + + public void AssignService(int serviceId, AggregationService aggregationService) { + _service = aggregationService; + } + + public void DeassignService(int serviceId) { + _service = null; + } + + public int InstanceCount + { + get { return _service == null ? 0 : 1; } + } + + public void ApplyEnter(EventBean[] eventsPerStream, Object optionalGroupKeyPerRow, ExprEvaluatorContext exprEvaluatorContext) { + _service.ApplyEnter(eventsPerStream, optionalGroupKeyPerRow, exprEvaluatorContext); + } + + public void ApplyLeave(EventBean[] eventsPerStream, Object optionalGroupKeyPerRow, ExprEvaluatorContext exprEvaluatorContext) { + _service.ApplyLeave(eventsPerStream, optionalGroupKeyPerRow, exprEvaluatorContext); + } + + public void SetCurrentAccess(Object groupKey, int agentInstanceId, AggregationGroupByRollupLevel rollupLevel) { + _service.SetCurrentAccess(groupKey, agentInstanceId, null); + } + + public void ClearResults(ExprEvaluatorContext exprEvaluatorContext) { + _service.ClearResults(exprEvaluatorContext); + } + + public object GetValue(int column, int agentInstanceId, EvaluateParams evaluateParams) { + return _service.GetValue(column, agentInstanceId, evaluateParams); + } + + public ICollection GetCollectionOfEvents(int column, EvaluateParams evaluateParams) { + return _service.GetCollectionOfEvents(column, evaluateParams); + } + + public ICollection GetCollectionScalar(int column, EvaluateParams evaluateParams) { + return _service.GetCollectionScalar(column, evaluateParams); + } + + public EventBean GetEventBean(int column, EvaluateParams evaluateParams) { + return _service.GetEventBean(column, evaluateParams); + } + + public void SetRemovedCallback(AggregationRowRemovedCallback callback) { + // not applicable + } + + public void Accept(AggregationServiceVisitor visitor) { + throw new UnsupportedOperationException("Not applicable"); + } + + public void AcceptGroupDetail(AggregationServiceVisitorWGroupDetail visitor) { + throw new UnsupportedOperationException("Not applicable"); + } + + public bool IsGrouped + { + get { throw new UnsupportedOperationException("Not applicable"); } + } + + public Object GetGroupKey(int agentInstanceId) { + return _service.GetGroupKey(agentInstanceId); + } + + public ICollection GetGroupKeys(ExprEvaluatorContext exprEvaluatorContext) { + return _service.GetGroupKeys(exprEvaluatorContext); + } + + public AggregationService GetContextPartitionAggregationService(int agentInstanceId) { + return _service; + } + + public void Stop() { + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryExpr.cs b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryExpr.cs new file mode 100755 index 000000000..9d4f1f1de --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryExpr.cs @@ -0,0 +1,44 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.expression.prev; +using com.espertech.esper.epl.expression.prior; +using com.espertech.esper.epl.expression.subquery; +using com.espertech.esper.epl.expression.table; + +namespace com.espertech.esper.core.context.stmt +{ + public interface AIRegistryExpr + { + AIRegistrySubselect GetSubselectService(ExprSubselectNode exprSubselectNode); + AIRegistryAggregation GetSubselectAggregationService(ExprSubselectNode exprSubselectNode); + AIRegistryPrior GetPriorServices(ExprPriorNode key); + AIRegistryPrevious GetPreviousServices(ExprPreviousNode key); + AIRegistryMatchRecognizePrevious GetMatchRecognizePrevious(); + AIRegistryTableAccess GetTableAccessServices(ExprTableAccessNode key); + + AIRegistrySubselect AllocateSubselect(ExprSubselectNode subselectNode); + AIRegistryAggregation AllocateSubselectAggregation(ExprSubselectNode subselectNode); + AIRegistryPrior AllocatePrior(ExprPriorNode key); + AIRegistryPrevious AllocatePrevious(ExprPreviousNode previousNode); + AIRegistryMatchRecognizePrevious AllocateMatchRecognizePrevious(); + AIRegistryTableAccess AllocateTableAccess(ExprTableAccessNode tableNode); + + AIRegistryPrior GetOrAllocatePrior(ExprPriorNode key); + AIRegistryPrevious GetOrAllocatePrevious(ExprPreviousNode key); + AIRegistrySubselect GetOrAllocateSubquery(ExprSubselectNode key); + AIRegistryAggregation GetOrAllocateSubselectAggregation(ExprSubselectNode exprSubselectNode); + + int SubselectAgentInstanceCount { get; } + int PreviousAgentInstanceCount { get; } + int PriorAgentInstanceCount { get; } + + void DeassignService(int agentInstanceId); + + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryExprBase.cs b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryExprBase.cs new file mode 100755 index 000000000..92136d5a3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryExprBase.cs @@ -0,0 +1,197 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.prev; +using com.espertech.esper.epl.expression.prior; +using com.espertech.esper.epl.expression.subquery; +using com.espertech.esper.epl.expression.table; + +namespace com.espertech.esper.core.context.stmt +{ + public abstract class AIRegistryExprBase : AIRegistryExpr + { + private readonly IDictionary _subselects; + private readonly IDictionary _subselectAggregations; + private readonly IDictionary _priors; + private readonly IDictionary _previous; + private readonly AIRegistryMatchRecognizePrevious _matchRecognizePrevious; + private readonly IDictionary _tableAccess; + + protected AIRegistryExprBase() + { + _subselects = new Dictionary(); + _subselectAggregations = new Dictionary(); + _priors = new Dictionary(); + _previous = new Dictionary(); + _matchRecognizePrevious = AllocateAIRegistryMatchRecognizePrevious(); + _tableAccess = new NullableDictionary(); + } + + public abstract AIRegistrySubselect AllocateAIRegistrySubselect(); + public abstract AIRegistryPrevious AllocateAIRegistryPrevious(); + public abstract AIRegistryPrior AllocateAIRegistryPrior(); + public abstract AIRegistryAggregation AllocateAIRegistrySubselectAggregation(); + public abstract AIRegistryMatchRecognizePrevious AllocateAIRegistryMatchRecognizePrevious(); + public abstract AIRegistryTableAccess AllocateAIRegistryTableAccess(); + + public AIRegistrySubselect GetSubselectService(ExprSubselectNode exprSubselectNode) + { + return _subselects.Get(exprSubselectNode); + } + + public AIRegistryAggregation GetSubselectAggregationService(ExprSubselectNode exprSubselectNode) + { + return _subselectAggregations.Get(exprSubselectNode); + } + + public AIRegistryPrior GetPriorServices(ExprPriorNode key) + { + return _priors.Get(key); + } + + public AIRegistryPrevious GetPreviousServices(ExprPreviousNode key) + { + return _previous.Get(key); + } + + public AIRegistryMatchRecognizePrevious GetMatchRecognizePrevious() + { + return _matchRecognizePrevious; + } + + public AIRegistryTableAccess GetTableAccessServices(ExprTableAccessNode key) + { + return _tableAccess.Get(key); + } + + public AIRegistrySubselect AllocateSubselect(ExprSubselectNode subselectNode) + { + AIRegistrySubselect subselect = AllocateAIRegistrySubselect(); + _subselects.Put(subselectNode, subselect); + return subselect; + } + + public AIRegistryAggregation AllocateSubselectAggregation(ExprSubselectNode subselectNode) + { + AIRegistryAggregation subselectAggregation = AllocateAIRegistrySubselectAggregation(); + _subselectAggregations.Put(subselectNode, subselectAggregation); + return subselectAggregation; + } + + public AIRegistryPrior AllocatePrior(ExprPriorNode key) + { + AIRegistryPrior service = AllocateAIRegistryPrior(); + _priors.Put(key, service); + return service; + } + + public AIRegistryPrior GetOrAllocatePrior(ExprPriorNode key) + { + AIRegistryPrior existing = _priors.Get(key); + if (existing != null) + { + return existing; + } + return AllocatePrior(key); + } + + public AIRegistryPrevious GetOrAllocatePrevious(ExprPreviousNode key) + { + AIRegistryPrevious existing = _previous.Get(key); + if (existing != null) + { + return existing; + } + return AllocatePrevious(key); + } + + public AIRegistrySubselect GetOrAllocateSubquery(ExprSubselectNode key) + { + AIRegistrySubselect existing = _subselects.Get(key); + if (existing != null) + { + return existing; + } + return AllocateSubselect(key); + } + + public AIRegistryAggregation GetOrAllocateSubselectAggregation(ExprSubselectNode subselectNode) + { + AIRegistryAggregation existing = _subselectAggregations.Get(subselectNode); + if (existing != null) + { + return existing; + } + return AllocateSubselectAggregation(subselectNode); + } + + public AIRegistryPrevious AllocatePrevious(ExprPreviousNode previousNode) + { + AIRegistryPrevious service = AllocateAIRegistryPrevious(); + _previous.Put(previousNode, service); + return service; + } + + public AIRegistryTableAccess AllocateTableAccess(ExprTableAccessNode tableNode) + { + AIRegistryTableAccess service = AllocateAIRegistryTableAccess(); + _tableAccess.Put(tableNode, service); + return service; + } + + public AIRegistryMatchRecognizePrevious AllocateMatchRecognizePrevious() + { + return _matchRecognizePrevious; + } + + public int SubselectAgentInstanceCount + { + get + { + return _subselects.Sum(entry => entry.Value.AgentInstanceCount); + } + } + + public int PreviousAgentInstanceCount + { + get + { + return _previous.Sum(entry => entry.Value.AgentInstanceCount); + } + } + + public int PriorAgentInstanceCount + { + get + { + return _priors.Sum(entry => entry.Value.AgentInstanceCount); + } + } + + public void DeassignService(int agentInstanceId) + { + foreach (var entry in _subselects) + entry.Value.DeassignService(agentInstanceId); + foreach (var entry in _subselectAggregations) + entry.Value.DeassignService(agentInstanceId); + foreach (var entry in _priors) + entry.Value.DeassignService(agentInstanceId); + foreach (var entry in _previous) + entry.Value.DeassignService(agentInstanceId); + foreach (var entry in _tableAccess) + entry.Value.DeassignService(agentInstanceId); + + _matchRecognizePrevious.DeassignService(agentInstanceId); + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryExprMap.cs b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryExprMap.cs new file mode 100755 index 000000000..d436a109c --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryExprMap.cs @@ -0,0 +1,43 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.core.context.stmt +{ + public class AIRegistryExprMap : AIRegistryExprBase + { + public override AIRegistrySubselect AllocateAIRegistrySubselect() + { + return new AIRegistrySubselectMap(); + } + + public override AIRegistryPrevious AllocateAIRegistryPrevious() + { + return new AIRegistryPreviousMap(); + } + + public override AIRegistryPrior AllocateAIRegistryPrior() + { + return new AIRegistryPriorMap(); + } + + public override AIRegistryAggregation AllocateAIRegistrySubselectAggregation() + { + return new AIRegistryAggregationMap(); + } + + public override AIRegistryMatchRecognizePrevious AllocateAIRegistryMatchRecognizePrevious() + { + return new AIRegistryMatchRecognizePreviousMap(); + } + + public override AIRegistryTableAccess AllocateAIRegistryTableAccess() + { + return new AIRegistryTableAccessMap(); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryExprMultiPerm.cs b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryExprMultiPerm.cs new file mode 100755 index 000000000..7f3f82871 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryExprMultiPerm.cs @@ -0,0 +1,43 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.core.context.stmt +{ + public class AIRegistryExprMultiPerm : AIRegistryExprBase + { + public override AIRegistrySubselect AllocateAIRegistrySubselect() + { + return new AIRegistrySubselectMultiPerm(); + } + + public override AIRegistryPrevious AllocateAIRegistryPrevious() + { + return new AIRegistryPreviousMultiPerm(); + } + + public override AIRegistryPrior AllocateAIRegistryPrior() + { + return new AIRegistryPriorMultiPerm(); + } + + public override AIRegistryAggregation AllocateAIRegistrySubselectAggregation() + { + return new AIRegistryAggregationMultiPerm(); + } + + public override AIRegistryMatchRecognizePrevious AllocateAIRegistryMatchRecognizePrevious() + { + return new AIRegistryMatchRecognizePreviousMultiPerm(); + } + + public override AIRegistryTableAccess AllocateAIRegistryTableAccess() + { + return new AIRegistryTableAccessMultiPerm(); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryExprSingle.cs b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryExprSingle.cs new file mode 100755 index 000000000..7948d4ed3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryExprSingle.cs @@ -0,0 +1,43 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.core.context.stmt +{ + public class AIRegistryExprSingle : AIRegistryExprBase + { + public override AIRegistrySubselect AllocateAIRegistrySubselect() + { + return new AIRegistrySubselectSingle(); + } + + public override AIRegistryPrevious AllocateAIRegistryPrevious() + { + return new AIRegistryPreviousSingle(); + } + + public override AIRegistryPrior AllocateAIRegistryPrior() + { + return new AIRegistryPriorSingle(); + } + + public override AIRegistryAggregation AllocateAIRegistrySubselectAggregation() + { + return new AIRegistryAggregationSingle(); + } + + public override AIRegistryMatchRecognizePrevious AllocateAIRegistryMatchRecognizePrevious() + { + return new AIRegistryMatchRecognizePreviousSingle(); + } + + public override AIRegistryTableAccess AllocateAIRegistryTableAccess() + { + return new AIRegistryTableAccessSingle(); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryMatchRecognizePrevious.cs b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryMatchRecognizePrevious.cs new file mode 100755 index 000000000..f2817cd56 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryMatchRecognizePrevious.cs @@ -0,0 +1,19 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.rowregex; + +namespace com.espertech.esper.core.context.stmt +{ + public interface AIRegistryMatchRecognizePrevious : RegexExprPreviousEvalStrategy + { + void AssignService(int num, RegexExprPreviousEvalStrategy value); + void DeassignService(int num); + int AgentInstanceCount { get; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryMatchRecognizePreviousMap.cs b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryMatchRecognizePreviousMap.cs new file mode 100755 index 000000000..90802e798 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryMatchRecognizePreviousMap.cs @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.rowregex; + +namespace com.espertech.esper.core.context.stmt +{ + public class AIRegistryMatchRecognizePreviousMap + : AIRegistryMatchRecognizePrevious + , RegexExprPreviousEvalStrategy + { + private readonly IDictionary _strategies; + + public AIRegistryMatchRecognizePreviousMap() + { + _strategies = new Dictionary(); + } + + public void AssignService(int num, RegexExprPreviousEvalStrategy value) + { + _strategies[num] = value; + } + + public void DeassignService(int num) + { + _strategies.Remove(num); + } + + public int AgentInstanceCount + { + get { return _strategies.Count; } + } + + public RegexPartitionStateRandomAccess GetAccess(ExprEvaluatorContext exprEvaluatorContext) + { + var agentInstanceId = exprEvaluatorContext.AgentInstanceId; + var strategy = _strategies.Get(agentInstanceId); + return strategy.GetAccess(exprEvaluatorContext); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryMatchRecognizePreviousMultiPerm.cs b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryMatchRecognizePreviousMultiPerm.cs new file mode 100755 index 000000000..14a108301 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryMatchRecognizePreviousMultiPerm.cs @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.collection; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.rowregex; + +namespace com.espertech.esper.core.context.stmt +{ + public class AIRegistryMatchRecognizePreviousMultiPerm + : AIRegistryMatchRecognizePrevious + , RegexExprPreviousEvalStrategy + { + private readonly ArrayWrap _strategies; + private int _count; + + public AIRegistryMatchRecognizePreviousMultiPerm() + { + _strategies = new ArrayWrap(10); + } + + public void AssignService(int num, RegexExprPreviousEvalStrategy value) + { + AIRegistryUtil.CheckExpand(num, _strategies); + _strategies.Array[num] = value; + _count++; + } + + public void DeassignService(int num) + { + _strategies.Array[num] = null; + _count--; + } + + public int AgentInstanceCount + { + get { return _count; } + } + + public RegexPartitionStateRandomAccess GetAccess(ExprEvaluatorContext exprEvaluatorContext) + { + int agentInstanceId = exprEvaluatorContext.AgentInstanceId; + RegexExprPreviousEvalStrategy strategy = _strategies.Array[agentInstanceId]; + return strategy.GetAccess(exprEvaluatorContext); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryMatchRecognizePreviousSingle.cs b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryMatchRecognizePreviousSingle.cs new file mode 100755 index 000000000..14e14a7bf --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryMatchRecognizePreviousSingle.cs @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.rowregex; + +namespace com.espertech.esper.core.context.stmt +{ + public class AIRegistryMatchRecognizePreviousSingle + : AIRegistryMatchRecognizePrevious + , RegexExprPreviousEvalStrategy + { + private RegexExprPreviousEvalStrategy _strategy; + + public void AssignService(int num, RegexExprPreviousEvalStrategy value) + { + _strategy = value; + } + + public void DeassignService(int num) + { + _strategy = null; + } + + public int AgentInstanceCount + { + get { return _strategy == null ? 0 : 1; } + } + + public RegexPartitionStateRandomAccess GetAccess(ExprEvaluatorContext exprEvaluatorContext) + { + return _strategy.GetAccess(exprEvaluatorContext); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryPrevious.cs b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryPrevious.cs new file mode 100755 index 000000000..ef12f8ef9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryPrevious.cs @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.prev; + +namespace com.espertech.esper.core.context.stmt +{ + public interface AIRegistryPrevious : ExprPreviousEvalStrategy + { + void AssignService(int num, ExprPreviousEvalStrategy value); + void DeassignService(int num); + int AgentInstanceCount { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryPreviousMap.cs b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryPreviousMap.cs new file mode 100755 index 000000000..d2b3550f5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryPreviousMap.cs @@ -0,0 +1,66 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.prev; + + +namespace com.espertech.esper.core.context.stmt +{ + public class AIRegistryPreviousMap : AIRegistryPrevious, ExprPreviousEvalStrategy + { + private readonly IDictionary _strategies; + + public AIRegistryPreviousMap() { + _strategies = new Dictionary(); + } + + public void AssignService(int num, ExprPreviousEvalStrategy value) { + _strategies.Put(num, value); + } + + public void DeassignService(int num) { + _strategies.Remove(num); + } + + public int AgentInstanceCount + { + get { return _strategies.Count; } + } + + public Object Evaluate(EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext) { + int agentInstanceId = exprEvaluatorContext.AgentInstanceId; + ExprPreviousEvalStrategy strategy = _strategies.Get(agentInstanceId); + return strategy.Evaluate(eventsPerStream, exprEvaluatorContext); + } + + public ICollection EvaluateGetCollEvents(EventBean[] eventsPerStream, ExprEvaluatorContext context) { + int agentInstanceId = context.AgentInstanceId; + ExprPreviousEvalStrategy strategy = _strategies.Get(agentInstanceId); + return strategy.EvaluateGetCollEvents(eventsPerStream, context); + } + + public ICollection EvaluateGetCollScalar(EventBean[] eventsPerStream, ExprEvaluatorContext context) { + int agentInstanceId = context.AgentInstanceId; + ExprPreviousEvalStrategy strategy = _strategies.Get(agentInstanceId); + return strategy.EvaluateGetCollScalar(eventsPerStream, context); + } + + public EventBean EvaluateGetEventBean(EventBean[] eventsPerStream, ExprEvaluatorContext context) { + int agentInstanceId = context.AgentInstanceId; + ExprPreviousEvalStrategy strategy = _strategies.Get(agentInstanceId); + return strategy.EvaluateGetEventBean(eventsPerStream, context); + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryPreviousMultiPerm.cs b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryPreviousMultiPerm.cs new file mode 100755 index 000000000..1e2c646ad --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryPreviousMultiPerm.cs @@ -0,0 +1,79 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.prev; + +namespace com.espertech.esper.core.context.stmt +{ + public class AIRegistryPreviousMultiPerm + : AIRegistryPrevious + , ExprPreviousEvalStrategy + { + private readonly ArrayWrap _strategies; + private int _count; + + public AIRegistryPreviousMultiPerm() + { + _strategies = new ArrayWrap(10); + } + + public void AssignService(int num, ExprPreviousEvalStrategy value) + { + AIRegistryUtil.CheckExpand(num, _strategies); + _strategies.Array[num] = value; + _count++; + } + + public void DeassignService(int num) + { + _strategies.Array[num] = null; + _count--; + } + + public int AgentInstanceCount + { + get { return _count; } + } + + public Object Evaluate(EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext) + { + int agentInstanceId = exprEvaluatorContext.AgentInstanceId; + ExprPreviousEvalStrategy strategy = _strategies.Array[agentInstanceId]; + return strategy.Evaluate(eventsPerStream, exprEvaluatorContext); + } + + public ICollection EvaluateGetCollEvents(EventBean[] eventsPerStream, ExprEvaluatorContext context) + { + int agentInstanceId = context.AgentInstanceId; + ExprPreviousEvalStrategy strategy = _strategies.Array[agentInstanceId]; + return strategy.EvaluateGetCollEvents(eventsPerStream, context); + } + + public ICollection EvaluateGetCollScalar(EventBean[] eventsPerStream, + ExprEvaluatorContext context) + { + int agentInstanceId = context.AgentInstanceId; + ExprPreviousEvalStrategy strategy = _strategies.Array[agentInstanceId]; + return strategy.EvaluateGetCollScalar(eventsPerStream, context); + } + + public EventBean EvaluateGetEventBean(EventBean[] eventsPerStream, ExprEvaluatorContext context) + { + int agentInstanceId = context.AgentInstanceId; + ExprPreviousEvalStrategy strategy = _strategies.Array[agentInstanceId]; + return strategy.EvaluateGetEventBean(eventsPerStream, context); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryPreviousSingle.cs b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryPreviousSingle.cs new file mode 100755 index 000000000..333c8ffdd --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryPreviousSingle.cs @@ -0,0 +1,54 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.prev; + + +namespace com.espertech.esper.core.context.stmt +{ + public class AIRegistryPreviousSingle : AIRegistryPrevious, ExprPreviousEvalStrategy + { + private ExprPreviousEvalStrategy _strategy; + + public void AssignService(int num, ExprPreviousEvalStrategy value) { + this._strategy = value; + } + + public void DeassignService(int num) { + this._strategy = null; + } + + public int AgentInstanceCount + { + get { return _strategy == null ? 0 : 1; } + } + + public Object Evaluate(EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext) { + return _strategy.Evaluate(eventsPerStream, exprEvaluatorContext); + } + + public ICollection EvaluateGetCollEvents(EventBean[] eventsPerStream, ExprEvaluatorContext context) { + return _strategy.EvaluateGetCollEvents(eventsPerStream, context); + } + + public ICollection EvaluateGetCollScalar(EventBean[] eventsPerStream, + ExprEvaluatorContext context) { + return _strategy.EvaluateGetCollScalar(eventsPerStream, context); + } + + public EventBean EvaluateGetEventBean(EventBean[] eventsPerStream, ExprEvaluatorContext context) { + return _strategy.EvaluateGetEventBean(eventsPerStream, context); + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryPrior.cs b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryPrior.cs new file mode 100755 index 000000000..2dbbdb967 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryPrior.cs @@ -0,0 +1,19 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.prior; + +namespace com.espertech.esper.core.context.stmt +{ + public interface AIRegistryPrior : ExprPriorEvalStrategy { + void AssignService(int num, ExprPriorEvalStrategy value); + void DeassignService(int num); + int AgentInstanceCount { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryPriorMap.cs b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryPriorMap.cs new file mode 100755 index 000000000..71a628ae5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryPriorMap.cs @@ -0,0 +1,63 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.prior; + +namespace com.espertech.esper.core.context.stmt +{ + public class AIRegistryPriorMap + : AIRegistryPrior + , ExprPriorEvalStrategy + { + private readonly IDictionary _strategies; + + public AIRegistryPriorMap() + { + _strategies = new Dictionary(); + } + + #region AIRegistryPrior Members + + public void AssignService(int num, + ExprPriorEvalStrategy value) + { + _strategies.Put(num, value); + } + + public void DeassignService(int num) + { + _strategies.Remove(num); + } + + public int AgentInstanceCount + { + get { return _strategies.Count; } + } + + public Object Evaluate(EventBean[] eventsPerStream, + bool isNewData, + ExprEvaluatorContext exprEvaluatorContext, + int streamNumber, + ExprEvaluator evaluator, + int constantIndexNumber) + { + int agentInstanceId = exprEvaluatorContext.AgentInstanceId; + ExprPriorEvalStrategy strategy = _strategies.Get(agentInstanceId); + return strategy.Evaluate(eventsPerStream, isNewData, exprEvaluatorContext, streamNumber, evaluator, + constantIndexNumber); + } + + #endregion + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryPriorMultiPerm.cs b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryPriorMultiPerm.cs new file mode 100755 index 000000000..5ee0efe44 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryPriorMultiPerm.cs @@ -0,0 +1,65 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.prior; + +namespace com.espertech.esper.core.context.stmt +{ + public class AIRegistryPriorMultiPerm + : AIRegistryPrior + , ExprPriorEvalStrategy + { + private readonly ArrayWrap _strategies; + private int _count; + + public AIRegistryPriorMultiPerm() + { + _strategies = new ArrayWrap(10); + } + + #region AIRegistryPrior Members + + public void AssignService(int num, ExprPriorEvalStrategy value) + { + AIRegistryUtil.CheckExpand(num, _strategies); + _strategies.Array[num] = value; + _count++; + } + + public void DeassignService(int num) + { + _strategies.Array[num] = null; + _count--; + } + + public int AgentInstanceCount + { + get { return _count; } + } + + public Object Evaluate(EventBean[] eventsPerStream, + bool isNewData, + ExprEvaluatorContext exprEvaluatorContext, + int streamNumber, + ExprEvaluator evaluator, + int constantIndexNumber) + { + int agentInstanceId = exprEvaluatorContext.AgentInstanceId; + ExprPriorEvalStrategy strategy = _strategies.Array[agentInstanceId]; + return strategy.Evaluate(eventsPerStream, isNewData, exprEvaluatorContext, streamNumber, evaluator, + constantIndexNumber); + } + + #endregion + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryPriorSingle.cs b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryPriorSingle.cs new file mode 100755 index 000000000..18499cde5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryPriorSingle.cs @@ -0,0 +1,43 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.prior; + + +namespace com.espertech.esper.core.context.stmt +{ + public class AIRegistryPriorSingle : AIRegistryPrior, ExprPriorEvalStrategy { + + private ExprPriorEvalStrategy strategy; + + public AIRegistryPriorSingle() { + } + + public void AssignService(int num, ExprPriorEvalStrategy value) { + strategy = value; + } + + public void DeassignService(int num) { + strategy = null; + } + + public int AgentInstanceCount + { + get { return strategy == null ? 0 : 1; } + } + + public Object Evaluate(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext exprEvaluatorContext, int streamNumber, ExprEvaluator evaluator, int constantIndexNumber) { + return strategy.Evaluate(eventsPerStream, isNewData, exprEvaluatorContext, streamNumber, evaluator, constantIndexNumber); + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistrySubselect.cs b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistrySubselect.cs new file mode 100755 index 000000000..8d5a238ae --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistrySubselect.cs @@ -0,0 +1,19 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.subquery; + +namespace com.espertech.esper.core.context.stmt +{ + public interface AIRegistrySubselect : ExprSubselectStrategy { + void AssignService(int num, ExprSubselectStrategy value); + void DeassignService(int num); + int AgentInstanceCount { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistrySubselectMap.cs b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistrySubselectMap.cs new file mode 100755 index 000000000..6aa400858 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistrySubselectMap.cs @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.subquery; + +namespace com.espertech.esper.core.context.stmt +{ + public class AIRegistrySubselectMap : AIRegistrySubselect, ExprSubselectStrategy + { + private readonly IDictionary _strategies; + + public AIRegistrySubselectMap() + { + _strategies = new Dictionary(); + } + + public void AssignService(int num, ExprSubselectStrategy subselectStrategy) + { + _strategies.Put(num, subselectStrategy); + } + + public void DeassignService(int num) + { + _strategies.Remove(num); + } + + public ICollection EvaluateMatching(EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext) + { + int agentInstanceId = exprEvaluatorContext.AgentInstanceId; + ExprSubselectStrategy strategy = _strategies.Get(agentInstanceId); + return strategy.EvaluateMatching(eventsPerStream, exprEvaluatorContext); + } + + public int AgentInstanceCount + { + get { return _strategies.Count; } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistrySubselectMultiPerm.cs b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistrySubselectMultiPerm.cs new file mode 100755 index 000000000..bb9b74548 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistrySubselectMultiPerm.cs @@ -0,0 +1,56 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.subquery; + +namespace com.espertech.esper.core.context.stmt +{ + public class AIRegistrySubselectMultiPerm + : AIRegistrySubselect + , ExprSubselectStrategy + { + private readonly ArrayWrap _strategies; + private int _count; + + public AIRegistrySubselectMultiPerm() + { + _strategies = new ArrayWrap(10); + } + + public void AssignService(int num, ExprSubselectStrategy subselectStrategy) + { + AIRegistryUtil.CheckExpand(num, _strategies); + _strategies.Array[num] = subselectStrategy; + _count++; + } + + public void DeassignService(int num) + { + _strategies.Array[num] = null; + _count--; + } + + public ICollection EvaluateMatching(EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext) + { + int agentInstanceId = exprEvaluatorContext.AgentInstanceId; + ExprSubselectStrategy strategy = _strategies.Array[agentInstanceId]; + return strategy.EvaluateMatching(eventsPerStream, exprEvaluatorContext); + } + + public int AgentInstanceCount + { + get { return _count; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistrySubselectSingle.cs b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistrySubselectSingle.cs new file mode 100755 index 000000000..761ec1a64 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistrySubselectSingle.cs @@ -0,0 +1,39 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.subquery; + +namespace com.espertech.esper.core.context.stmt +{ + public class AIRegistrySubselectSingle : AIRegistrySubselect, ExprSubselectStrategy { + + private ExprSubselectStrategy _strategy; + + public void AssignService(int num, ExprSubselectStrategy subselectStrategy) { + _strategy = subselectStrategy; + } + + public void DeassignService(int num) { + _strategy = null; + } + + public ICollection EvaluateMatching(EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext) { + return _strategy.EvaluateMatching(eventsPerStream, exprEvaluatorContext); + } + + public int AgentInstanceCount + { + get { return _strategy == null ? 0 : 1; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryTableAccess.cs b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryTableAccess.cs new file mode 100755 index 000000000..7873d002a --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryTableAccess.cs @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.table; + +namespace com.espertech.esper.core.context.stmt +{ + public interface AIRegistryTableAccess : ExprTableAccessEvalStrategy + { + void AssignService(int num, ExprTableAccessEvalStrategy value); + void DeassignService(int num); + int AgentInstanceCount { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryTableAccessMap.cs b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryTableAccessMap.cs new file mode 100755 index 000000000..0bc88d76e --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryTableAccessMap.cs @@ -0,0 +1,74 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.table; + +namespace com.espertech.esper.core.context.stmt +{ + public class AIRegistryTableAccessMap : AIRegistryTableAccess, ExprTableAccessEvalStrategy + { + private readonly IDictionary strategies; + + public AIRegistryTableAccessMap() + { + strategies = new Dictionary(); + } + + public void AssignService(int num, ExprTableAccessEvalStrategy value) + { + strategies.Put(num, value); + } + + public void DeassignService(int num) + { + strategies.Remove(num); + } + + public int AgentInstanceCount + { + get { return strategies.Count; } + } + + public object Evaluate(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + return GetStrategy(context).Evaluate(eventsPerStream, isNewData, context); + } + + public object[] EvaluateTypableSingle(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + return GetStrategy(context).EvaluateTypableSingle(eventsPerStream, isNewData, context); + } + + public ICollection EvaluateGetROCollectionEvents(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + return GetStrategy(context).EvaluateGetROCollectionEvents(eventsPerStream, isNewData, context); + } + + public EventBean EvaluateGetEventBean(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + return GetStrategy(context).EvaluateGetEventBean(eventsPerStream, isNewData, context); + } + + public ICollection EvaluateGetROCollectionScalar(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + return GetStrategy(context).EvaluateGetROCollectionScalar(eventsPerStream, isNewData, context); + } + + private ExprTableAccessEvalStrategy GetStrategy(ExprEvaluatorContext context) + { + return strategies.Get(context.AgentInstanceId); + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryTableAccessMultiPerm.cs b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryTableAccessMultiPerm.cs new file mode 100755 index 000000000..d9a2be9f2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryTableAccessMultiPerm.cs @@ -0,0 +1,88 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.table; + +namespace com.espertech.esper.core.context.stmt +{ + public class AIRegistryTableAccessMultiPerm + : AIRegistryTableAccess + , + ExprTableAccessEvalStrategy + { + private readonly ArrayWrap _strategies; + private int _count; + + public AIRegistryTableAccessMultiPerm() + { + _strategies = new ArrayWrap(10); + } + + public void AssignService(int num, ExprTableAccessEvalStrategy value) + { + AIRegistryUtil.CheckExpand(num, _strategies); + _strategies.Array[num] = value; + _count++; + } + + public void DeassignService(int num) + { + _strategies.Array[num] = null; + _count--; + } + + public int AgentInstanceCount + { + get { return _count; } + } + + public object Evaluate(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + return GetStrategy(context).Evaluate(eventsPerStream, isNewData, context); + } + + public object[] EvaluateTypableSingle(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + return GetStrategy(context).EvaluateTypableSingle(eventsPerStream, isNewData, context); + } + + public ICollection EvaluateGetROCollectionEvents( + EventBean[] eventsPerStream, + bool isNewData, + ExprEvaluatorContext context) + { + return GetStrategy(context).EvaluateGetROCollectionEvents(eventsPerStream, isNewData, context); + } + + public EventBean EvaluateGetEventBean(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + return GetStrategy(context).EvaluateGetEventBean(eventsPerStream, isNewData, context); + } + + public ICollection EvaluateGetROCollectionScalar( + EventBean[] eventsPerStream, + bool isNewData, + ExprEvaluatorContext context) + { + return GetStrategy(context).EvaluateGetROCollectionScalar(eventsPerStream, isNewData, context); + } + + private ExprTableAccessEvalStrategy GetStrategy(ExprEvaluatorContext context) + { + return _strategies.Array[context.AgentInstanceId]; + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryTableAccessSingle.cs b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryTableAccessSingle.cs new file mode 100755 index 000000000..b71604864 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryTableAccessSingle.cs @@ -0,0 +1,58 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.table; + +namespace com.espertech.esper.core.context.stmt +{ + public class AIRegistryTableAccessSingle : AIRegistryTableAccess, ExprTableAccessEvalStrategy { + + private ExprTableAccessEvalStrategy strategy; + + public void AssignService(int num, ExprTableAccessEvalStrategy value) { + this.strategy = value; + } + + public void DeassignService(int num) { + this.strategy = null; + } + + public int AgentInstanceCount + { + get { return strategy == null ? 0 : 1; } + } + + public object Evaluate(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext exprEvaluatorContext) { + return strategy.Evaluate(eventsPerStream, isNewData, exprEvaluatorContext); + } + + public object[] EvaluateTypableSingle(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) { + return strategy.EvaluateTypableSingle(eventsPerStream, isNewData, context); + } + + public ICollection EvaluateGetROCollectionEvents(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) { + return strategy.EvaluateGetROCollectionEvents(eventsPerStream, isNewData, context); + } + + public EventBean EvaluateGetEventBean(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) { + return strategy.EvaluateGetEventBean(eventsPerStream, isNewData, context); + } + + public ICollection EvaluateGetROCollectionScalar(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + return strategy.EvaluateGetROCollectionScalar(eventsPerStream, isNewData, context); + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryUtil.cs b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryUtil.cs new file mode 100755 index 000000000..2b3f5e021 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/stmt/AIRegistryUtil.cs @@ -0,0 +1,24 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.collection; + +namespace com.espertech.esper.core.context.stmt +{ + public class AIRegistryUtil + { + public static void CheckExpand(int serviceId, ArrayWrap services) + { + if (serviceId > services.Array.Length - 1) + { + int delta = serviceId - services.Array.Length + 1; + services.Expand(delta); + } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/context/stmt/StatementAIResourceRegistry.cs b/NEsper.Core/NEsper.Core/core/context/stmt/StatementAIResourceRegistry.cs new file mode 100755 index 000000000..30184f19b --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/stmt/StatementAIResourceRegistry.cs @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.core.context.stmt +{ + public class StatementAIResourceRegistry + { + public StatementAIResourceRegistry(AIRegistryAggregation agentInstanceAggregationService, AIRegistryExpr agentInstanceExprService) + { + AgentInstanceAggregationService = agentInstanceAggregationService; + AgentInstanceExprService = agentInstanceExprService; + } + + public AIRegistryExpr AgentInstanceExprService { get; private set; } + + public AIRegistryAggregation AgentInstanceAggregationService { get; private set; } + + public void Deassign(int agentInstanceIds) + { + AgentInstanceAggregationService.DeassignService(agentInstanceIds); + AgentInstanceExprService.DeassignService(agentInstanceIds); + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/stmt/StatementAIResourceRegistryFactory.cs b/NEsper.Core/NEsper.Core/core/context/stmt/StatementAIResourceRegistryFactory.cs new file mode 100755 index 000000000..9614535a8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/stmt/StatementAIResourceRegistryFactory.cs @@ -0,0 +1,12 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.core.context.stmt +{ + public delegate StatementAIResourceRegistry StatementAIResourceRegistryFactory(); +} diff --git a/NEsper.Core/NEsper.Core/core/context/subselect/SubSelectActivationCollection.cs b/NEsper.Core/NEsper.Core/core/context/subselect/SubSelectActivationCollection.cs new file mode 100755 index 000000000..d06cc8c7e --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/subselect/SubSelectActivationCollection.cs @@ -0,0 +1,79 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.subquery; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.context.subselect +{ + /// Holds stream information for subqueries. + public class SubSelectActivationCollection + { + private readonly IDictionary _subqueries; + + /// Ctor. + public SubSelectActivationCollection() + { + _subqueries = new Dictionary(); + } + + /// + /// Add lookup. + /// + /// is the subselect expression node + /// The holder. + public void Add(ExprSubselectNode subselectNode, SubSelectActivationHolder holder) + { + _subqueries.Put(subselectNode, holder); + } + + /// + /// Gets the sub select holder. + /// + /// The subselect node. + /// + public SubSelectActivationHolder GetSubSelectHolder(ExprSubselectNode subselectNode) + { + return _subqueries.Get(subselectNode); + } + + /// Returns stream number. + /// is the lookup node's stream number + /// number of stream + public int GetStreamNumber(ExprSubselectNode subqueryNode) + { + return _subqueries.Get(subqueryNode).StreamNumber; + } + + /// Returns the lookup viewable, child-most view. + /// is the expression node to get this for + /// child viewable + public EventType GetRootViewableType(ExprSubselectNode subqueryNode) + { + return _subqueries.Get(subqueryNode).ViewableType; + } + + /// Returns the lookup's view factory chain. + /// is the node to look for + /// view factory chain + public ViewFactoryChain GetViewFactoryChain(ExprSubselectNode subqueryNode) + { + return _subqueries.Get(subqueryNode).ViewFactoryChain; + } + + public IDictionary Subqueries + { + get { return _subqueries; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/subselect/SubSelectActivationHolder.cs b/NEsper.Core/NEsper.Core/core/context/subselect/SubSelectActivationHolder.cs new file mode 100755 index 000000000..73483a1d2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/subselect/SubSelectActivationHolder.cs @@ -0,0 +1,44 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.core.context.activator; +using com.espertech.esper.epl.spec; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.context.subselect +{ + /// + /// Record holding lookup resource references for use by . + /// + public class SubSelectActivationHolder + { + public SubSelectActivationHolder(int streamNumber, EventType viewableType, ViewFactoryChain viewFactoryChain, ViewableActivator activator, StreamSpecCompiled streamSpecCompiled) + { + StreamNumber = streamNumber; + ViewableType = viewableType; + ViewFactoryChain = viewFactoryChain; + Activator = activator; + StreamSpecCompiled = streamSpecCompiled; + } + + /// Returns lookup stream number. + /// stream num + public int StreamNumber { get; private set; } + + public EventType ViewableType { get; private set; } + + /// Returns the lookup view factory chain + /// view factory chain + public ViewFactoryChain ViewFactoryChain { get; private set; } + + public ViewableActivator Activator { get; private set; } + + public StreamSpecCompiled StreamSpecCompiled { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/subselect/SubSelectStrategyCollection.cs b/NEsper.Core/NEsper.Core/core/context/subselect/SubSelectStrategyCollection.cs new file mode 100755 index 000000000..c46b3e4aa --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/subselect/SubSelectStrategyCollection.cs @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.subquery; + +namespace com.espertech.esper.core.context.subselect +{ + /// + /// Holds stream information for subqueries. + /// + public class SubSelectStrategyCollection + { + private IDictionary _subqueries; + + /// + /// Add lookup. + /// + /// is the subselect expression node + /// The _prototype holder. + public void Add(ExprSubselectNode subselectNode, SubSelectStrategyFactoryDesc prototypeHolder) + { + if (_subqueries == null) + { + _subqueries = new Dictionary(); + } + _subqueries.Put(subselectNode, prototypeHolder); + } + + public IDictionary Subqueries + { + get + { + if (_subqueries == null) + { + return Collections.GetEmptyMap(); + } + return _subqueries; + } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/context/subselect/SubSelectStrategyFactory.cs b/NEsper.Core/NEsper.Core/core/context/subselect/SubSelectStrategyFactory.cs new file mode 100755 index 000000000..157d94c61 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/subselect/SubSelectStrategyFactory.cs @@ -0,0 +1,31 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.context.subselect +{ + /// + /// Record holding lookup resource references for use by . + /// + public interface SubSelectStrategyFactory + { + SubSelectStrategyRealization Instantiate( + EPServicesContext services, + Viewable viewableRoot, + AgentInstanceContext agentInstanceContext, + IList stopCallbackList, + int subqueryNumber, + bool isRecoveringResilient); + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/subselect/SubSelectStrategyFactoryDesc.cs b/NEsper.Core/NEsper.Core/core/context/subselect/SubSelectStrategyFactoryDesc.cs new file mode 100755 index 000000000..9b1961c82 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/subselect/SubSelectStrategyFactoryDesc.cs @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.prev; +using com.espertech.esper.epl.expression.prior; + +namespace com.espertech.esper.core.context.subselect +{ + /// + /// Record holding lookup resource references for use by . + /// + public class SubSelectStrategyFactoryDesc + { + public SubSelectStrategyFactoryDesc( + SubSelectActivationHolder subSelectActivationHolder, + SubSelectStrategyFactory factory, + AggregationServiceFactoryDesc aggregationServiceFactoryDesc, + IList priorNodesList, + IList prevNodesList, + int subqueryNumber) + { + SubSelectActivationHolder = subSelectActivationHolder; + Factory = factory; + AggregationServiceFactoryDesc = aggregationServiceFactoryDesc; + PriorNodesList = priorNodesList; + PrevNodesList = prevNodesList; + SubqueryNumber = subqueryNumber; + } + + public SubSelectActivationHolder SubSelectActivationHolder { get; private set; } + + public SubSelectStrategyFactory Factory { get; private set; } + + public AggregationServiceFactoryDesc AggregationServiceFactoryDesc { get; private set; } + + public IList PriorNodesList { get; private set; } + + public IList PrevNodesList { get; private set; } + + public int SubqueryNumber { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/context/subselect/SubSelectStrategyFactoryIndexShare.cs b/NEsper.Core/NEsper.Core/core/context/subselect/SubSelectStrategyFactoryIndexShare.cs new file mode 100755 index 000000000..9bd54ce81 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/subselect/SubSelectStrategyFactoryIndexShare.cs @@ -0,0 +1,234 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.prev; +using com.espertech.esper.epl.expression.prior; +using com.espertech.esper.epl.@join.hint; +using com.espertech.esper.epl.@join.table; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.epl.named; +using com.espertech.esper.epl.subquery; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.context.subselect +{ + /// + /// Entry holding lookup resource references for use by . + /// + public class SubSelectStrategyFactoryIndexShare : SubSelectStrategyFactory + { + private readonly AggregationServiceFactoryDesc _aggregationServiceFactory; + private readonly ExprEvaluator _filterExprEval; + private readonly ExprEvaluator[] _groupByKeys; + private readonly NamedWindowProcessor _optionalNamedWindowProcessor; + private readonly TableMetadata _optionalTableMetadata; + private readonly TableService _tableService; + private readonly SubordinateQueryPlanDesc _queryPlan; + + public SubSelectStrategyFactoryIndexShare( + string statementName, + int statementId, + int subqueryNum, + EventType[] outerEventTypesSelect, + NamedWindowProcessor optionalNamedWindowProcessor, + TableMetadata optionalTableMetadata, + bool fullTableScan, + IndexHint optionalIndexHint, + SubordPropPlan joinedPropPlan, + ExprEvaluator filterExprEval, + AggregationServiceFactoryDesc aggregationServiceFactory, + ExprEvaluator[] groupByKeys, + TableService tableService, + Attribute[] annotations, + StatementStopService statementStopService, + EngineImportService engineImportService) + { + _optionalNamedWindowProcessor = optionalNamedWindowProcessor; + _optionalTableMetadata = optionalTableMetadata; + _filterExprEval = filterExprEval; + _aggregationServiceFactory = aggregationServiceFactory; + _groupByKeys = groupByKeys; + _tableService = tableService; + + bool isLogging; + ILog log; + if (optionalTableMetadata != null) + { + isLogging = optionalTableMetadata.IsQueryPlanLogging; + log = TableServiceImpl.QueryPlanLog; + _queryPlan = SubordinateQueryPlanner.PlanSubquery( + outerEventTypesSelect, joinedPropPlan, false, fullTableScan, optionalIndexHint, true, subqueryNum, + false, optionalTableMetadata.EventTableIndexMetadataRepo, optionalTableMetadata.UniqueKeyProps, true, + statementName, statementId, annotations); + if (_queryPlan != null) + { + for (int i = 0; i < _queryPlan.IndexDescs.Length; i++) + { + optionalTableMetadata.AddIndexReference(_queryPlan.IndexDescs[i].IndexName, statementName); + } + } + } + else + { + isLogging = optionalNamedWindowProcessor.RootView.IsQueryPlanLogging; + log = NamedWindowRootView.QueryPlanLog; + _queryPlan = SubordinateQueryPlanner.PlanSubquery( + outerEventTypesSelect, joinedPropPlan, false, fullTableScan, optionalIndexHint, true, subqueryNum, + optionalNamedWindowProcessor.IsVirtualDataWindow, + optionalNamedWindowProcessor.EventTableIndexMetadataRepo, + optionalNamedWindowProcessor.OptionalUniqueKeyProps, false, statementName, statementId, annotations); + if (_queryPlan != null && _queryPlan.IndexDescs != null) + { + SubordinateQueryPlannerUtil.AddIndexMetaAndRef( + _queryPlan.IndexDescs, optionalNamedWindowProcessor.EventTableIndexMetadataRepo, statementName); + statementStopService.StatementStopped += () => + { + for (int i = 0; i < _queryPlan.IndexDescs.Length; i++) + { + bool last = + optionalNamedWindowProcessor.EventTableIndexMetadataRepo.RemoveIndexReference( + _queryPlan.IndexDescs[i].IndexMultiKey, statementName); + if (last) + { + optionalNamedWindowProcessor.EventTableIndexMetadataRepo.RemoveIndex( + _queryPlan.IndexDescs[i].IndexMultiKey); + optionalNamedWindowProcessor.RemoveAllInstanceIndexes( + _queryPlan.IndexDescs[i].IndexMultiKey); + } + } + }; + } + } + SubordinateQueryPlannerUtil.QueryPlanLogOnSubq( + isLogging, log, _queryPlan, subqueryNum, annotations, engineImportService); + } + + public SubSelectStrategyRealization Instantiate( + EPServicesContext services, + Viewable viewableRoot, + AgentInstanceContext agentInstanceContext, + IList stopCallbackList, + int subqueryNumber, + bool isRecoveringResilient) + { + SubselectAggregationPreprocessorBase subselectAggregationPreprocessor = null; + + AggregationService aggregationService = null; + if (_aggregationServiceFactory != null) + { + aggregationService = + _aggregationServiceFactory.AggregationServiceFactory.MakeService( + agentInstanceContext, agentInstanceContext.StatementContext.EngineImportService, true, + subqueryNumber); + if (_groupByKeys == null) + { + if (_filterExprEval == null) + { + subselectAggregationPreprocessor = + new SubselectAggregationPreprocessorUnfilteredUngrouped( + aggregationService, _filterExprEval, null); + } + else + { + subselectAggregationPreprocessor = + new SubselectAggregationPreprocessorFilteredUngrouped( + aggregationService, _filterExprEval, null); + } + } + else + { + if (_filterExprEval == null) + { + subselectAggregationPreprocessor = + new SubselectAggregationPreprocessorUnfilteredGrouped( + aggregationService, _filterExprEval, _groupByKeys); + } + else + { + subselectAggregationPreprocessor = + new SubselectAggregationPreprocessorFilteredGrouped( + aggregationService, _filterExprEval, _groupByKeys); + } + } + } + + SubordTableLookupStrategy subqueryLookup; + if (_optionalNamedWindowProcessor != null) + { + NamedWindowProcessorInstance instance = + _optionalNamedWindowProcessor.GetProcessorInstance(agentInstanceContext); + if (_queryPlan == null) + { + if (instance.RootViewInstance.IsQueryPlanLogging && NamedWindowRootView.QueryPlanLog.IsInfoEnabled) + { + NamedWindowRootView.QueryPlanLog.Info("shared, full table scan"); + } + subqueryLookup = + new SubordFullTableScanLookupStrategyLocking( + instance.RootViewInstance.DataWindowContents, + agentInstanceContext.EpStatementAgentInstanceHandle.StatementAgentInstanceLock); + } + else + { + EventTable[] tables = null; + if (!_optionalNamedWindowProcessor.IsVirtualDataWindow) + { + tables = SubordinateQueryPlannerUtil.RealizeTables( + _queryPlan.IndexDescs, instance.RootViewInstance.EventType, + instance.RootViewInstance.IndexRepository, instance.RootViewInstance.DataWindowContents, + agentInstanceContext, isRecoveringResilient); + } + SubordTableLookupStrategy strategy = _queryPlan.LookupStrategyFactory.MakeStrategy( + tables, instance.RootViewInstance.VirtualDataWindow); + subqueryLookup = new SubordIndexedTableLookupStrategyLocking( + strategy, instance.TailViewInstance.AgentInstanceContext.AgentInstanceLock); + } + } + else + { + TableStateInstance state = _tableService.GetState( + _optionalTableMetadata.TableName, agentInstanceContext.AgentInstanceId); + var @lock = agentInstanceContext.StatementContext.IsWritesToTables + ? state.TableLevelRWLock.WriteLock + : state.TableLevelRWLock.ReadLock; + if (_queryPlan == null) + { + subqueryLookup = new SubordFullTableScanTableLookupStrategy(@lock, state.IterableTableScan); + } + else + { + var indexes = new EventTable[_queryPlan.IndexDescs.Length]; + for (int i = 0; i < indexes.Length; i++) + { + indexes[i] = state.IndexRepository.GetIndexByDesc(_queryPlan.IndexDescs[i].IndexMultiKey); + } + subqueryLookup = _queryPlan.LookupStrategyFactory.MakeStrategy(indexes, null); + subqueryLookup = new SubordIndexedTableLookupTableStrategy(subqueryLookup, @lock); + } + } + + return new SubSelectStrategyRealization( + subqueryLookup, subselectAggregationPreprocessor, aggregationService, + Collections.GetEmptyMap(), + Collections.GetEmptyMap(), null, null); + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/context/subselect/SubSelectStrategyFactoryLocalViewPreloaded.cs b/NEsper.Core/NEsper.Core/core/context/subselect/SubSelectStrategyFactoryLocalViewPreloaded.cs new file mode 100755 index 000000000..39c4642e3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/subselect/SubSelectStrategyFactoryLocalViewPreloaded.cs @@ -0,0 +1,363 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.factory; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.core.start; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.@join.table; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.epl.named; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.subquery; +using com.espertech.esper.filter; +using com.espertech.esper.util; +using com.espertech.esper.view; +using com.espertech.esper.view.internals; + +namespace com.espertech.esper.core.context.subselect +{ + /// + /// Record holding lookup resource references for use by . + /// + public class SubSelectStrategyFactoryLocalViewPreloaded : SubSelectStrategyFactory + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private static readonly SubordTableLookupStrategyNullRow NULL_ROW_STRATEGY = + new SubordTableLookupStrategyNullRow(); + + private readonly int _subqueryNumber; + private readonly SubSelectActivationHolder _subSelectHolder; + private readonly Pair _pair; + private readonly ExprNode _filterExprNode; + private readonly ExprEvaluator _filterExprEval; + private readonly bool _correlatedSubquery; + private readonly AggregationServiceFactoryDesc _aggregationServiceFactory; + private readonly ViewResourceDelegateVerified _viewResourceDelegate; + private readonly ExprEvaluator[] _groupKeys; + + public SubSelectStrategyFactoryLocalViewPreloaded( + int subqueryNumber, + SubSelectActivationHolder subSelectHolder, + Pair pair, + ExprNode filterExprNode, + ExprEvaluator filterExprEval, + bool correlatedSubquery, + AggregationServiceFactoryDesc aggregationServiceFactory, + ViewResourceDelegateVerified viewResourceDelegate, + ExprEvaluator[] groupKeys) + { + _subqueryNumber = subqueryNumber; + _subSelectHolder = subSelectHolder; + _pair = pair; + _filterExprNode = filterExprNode; + _filterExprEval = filterExprEval; + _correlatedSubquery = correlatedSubquery; + _aggregationServiceFactory = aggregationServiceFactory; + _viewResourceDelegate = viewResourceDelegate; + _groupKeys = groupKeys; + } + + public SubSelectStrategyRealization Instantiate( + EPServicesContext services, + Viewable viewableRoot, + AgentInstanceContext agentInstanceContext, + IList stopCallbackList, + int subqueryNumber, + bool isRecoveringResilient) + { + IList viewFactoryChain = _subSelectHolder.ViewFactoryChain.FactoryChain; + + // add "prior" view factory + var hasPrior = _viewResourceDelegate.PerStream[0].PriorRequests != null && + !_viewResourceDelegate.PerStream[0].PriorRequests.IsEmpty(); + if (hasPrior) + { + var priorEventViewFactory = EPStatementStartMethodHelperPrior.GetPriorEventViewFactory( + agentInstanceContext.StatementContext, 1024 + _subqueryNumber, viewFactoryChain.IsEmpty(), true, + subqueryNumber); + viewFactoryChain = new List(viewFactoryChain); + viewFactoryChain.Add(priorEventViewFactory); + } + + // create factory chain context to hold callbacks specific to "prior" and "prev" + var viewFactoryChainContext = AgentInstanceViewFactoryChainContext.Create( + viewFactoryChain, agentInstanceContext, _viewResourceDelegate.PerStream[0]); + + // make view + var createResult = services.ViewService.CreateViews( + viewableRoot, viewFactoryChain, viewFactoryChainContext, false); + var subselectView = createResult.FinalViewable; + + // make aggregation service + AggregationService aggregationService = null; + if (_aggregationServiceFactory != null) + { + aggregationService = _aggregationServiceFactory.AggregationServiceFactory.MakeService( + agentInstanceContext, agentInstanceContext.StatementContext.EngineImportService, true, + subqueryNumber); + } + + // handle "prior" nodes and their strategies + var priorNodeStrategies = EPStatementStartMethodHelperPrior.CompilePriorNodeStrategies( + _viewResourceDelegate, new AgentInstanceViewFactoryChainContext[] + { + viewFactoryChainContext + }); + + // handle "previous" nodes and their strategies + var previousNodeStrategies = + EPStatementStartMethodHelperPrevious.CompilePreviousNodeStrategies( + _viewResourceDelegate, new AgentInstanceViewFactoryChainContext[] + { + viewFactoryChainContext + }); + + // handle aggregated and non-correlated queries: there is no strategy or index + if (_aggregationServiceFactory != null && !_correlatedSubquery) + { + View aggregatorView; + if (_groupKeys == null) + { + if (_filterExprEval == null) + { + aggregatorView = new SubselectAggregatorViewUnfilteredUngrouped( + aggregationService, _filterExprEval, agentInstanceContext, null); + } + else + { + aggregatorView = new SubselectAggregatorViewFilteredUngrouped( + aggregationService, _filterExprEval, agentInstanceContext, null, _filterExprNode); + } + } + else + { + if (_filterExprEval == null) + { + aggregatorView = new SubselectAggregatorViewUnfilteredGrouped( + aggregationService, _filterExprEval, agentInstanceContext, _groupKeys); + } + else + { + aggregatorView = new SubselectAggregatorViewFilteredGrouped( + aggregationService, _filterExprEval, agentInstanceContext, _groupKeys, _filterExprNode); + } + } + subselectView.AddView(aggregatorView); + + if (services.EventTableIndexService.AllowInitIndex(isRecoveringResilient)) + { + Preload(services, null, aggregatorView, agentInstanceContext); + } + + return new SubSelectStrategyRealization( + NULL_ROW_STRATEGY, null, aggregationService, priorNodeStrategies, previousNodeStrategies, + subselectView, null); + } + + // create index/holder table + EventTable[] index = + _pair.First.MakeEventTables( + new EventTableFactoryTableIdentAgentInstanceSubq(agentInstanceContext, _subqueryNumber)); + stopCallbackList.Add(new SubqueryStopCallback(index)); + + // create strategy + SubordTableLookupStrategy strategy = _pair.Second.MakeStrategy(index, null); + SubselectAggregationPreprocessorBase subselectAggregationPreprocessor = null; + + // handle unaggregated or correlated queries or + if (_aggregationServiceFactory != null) + { + if (_groupKeys == null) + { + if (_filterExprEval == null) + { + subselectAggregationPreprocessor = + new SubselectAggregationPreprocessorUnfilteredUngrouped( + aggregationService, _filterExprEval, null); + } + else + { + subselectAggregationPreprocessor = + new SubselectAggregationPreprocessorFilteredUngrouped( + aggregationService, _filterExprEval, null); + } + } + else + { + if (_filterExprEval == null) + { + subselectAggregationPreprocessor = + new SubselectAggregationPreprocessorUnfilteredGrouped( + aggregationService, _filterExprEval, _groupKeys); + } + else + { + subselectAggregationPreprocessor = + new SubselectAggregationPreprocessorFilteredGrouped( + aggregationService, _filterExprEval, _groupKeys); + } + } + } + + // preload when allowed + StatementAgentInstancePostLoad postLoad; + if (services.EventTableIndexService.AllowInitIndex(isRecoveringResilient)) + { + Preload(services, index, subselectView, agentInstanceContext); + postLoad = new ProxyStatementAgentInstancePostLoad() + { + ProcExecutePostLoad = () => Preload(services, index, subselectView, agentInstanceContext), + + ProcAcceptIndexVisitor = visitor => + { + foreach (var table in index) + { + visitor.Visit(table); + } + }, + }; + } + else + { + postLoad = new ProxyStatementAgentInstancePostLoad + { + ProcExecutePostLoad = () => + { + // no post-load + }, + + ProcAcceptIndexVisitor = visitor => + { + foreach (var table in index) + { + visitor.Visit(table); + } + }, + }; + } + + var bufferView = new BufferView(_subSelectHolder.StreamNumber); + bufferView.Observer = new SubselectBufferObserver(index); + subselectView.AddView(bufferView); + + return new SubSelectStrategyRealization( + strategy, subselectAggregationPreprocessor, aggregationService, priorNodeStrategies, + previousNodeStrategies, subselectView, postLoad); + } + + private void Preload( + EPServicesContext services, + EventTable[] eventIndex, + Viewable subselectView, + AgentInstanceContext agentInstanceContext) + { + if (_subSelectHolder.StreamSpecCompiled is NamedWindowConsumerStreamSpec) + { + var namedSpec = (NamedWindowConsumerStreamSpec)_subSelectHolder.StreamSpecCompiled; + NamedWindowProcessor processor = services.NamedWindowMgmtService.GetProcessor(namedSpec.WindowName); + if (processor == null) + { + throw new EPRuntimeException("Failed to find named window by name '" + namedSpec.WindowName + "'"); + } + + var processorInstance = processor.GetProcessorInstance(agentInstanceContext); + if (processorInstance == null) + { + throw new EPException( + "Named window '" + namedSpec.WindowName + "' is associated to context '" + processor.ContextName + + "' that is not available for querying"); + } + var consumerView = processorInstance.TailViewInstance; + + // preload view for stream + ICollection eventsInWindow; + if (namedSpec.FilterExpressions != null && !namedSpec.FilterExpressions.IsEmpty()) + { + + try + { + var types = new StreamTypeServiceImpl( + consumerView.EventType, consumerView.EventType.Name, false, services.EngineURI); + var tagged = new LinkedHashMap>(); + var filterSpecCompiled = FilterSpecCompiler.MakeFilterSpec( + types.EventTypes[0], types.StreamNames[0], + namedSpec.FilterExpressions, null, tagged, tagged, types, null, + agentInstanceContext.StatementContext, Collections.SingletonSet(0)); + var snapshot = consumerView.SnapshotNoLock( + filterSpecCompiled, agentInstanceContext.StatementContext.Annotations); + eventsInWindow = new List(snapshot.Count); + ExprNodeUtility.ApplyFilterExpressionsIterable( + snapshot, namedSpec.FilterExpressions, agentInstanceContext, eventsInWindow); + } + catch (Exception ex) + { + Log.Warn("Unexpected exception analyzing filter paths: " + ex.Message, ex); + eventsInWindow = new List(); + ExprNodeUtility.ApplyFilterExpressionsIterable( + consumerView, namedSpec.FilterExpressions, agentInstanceContext, eventsInWindow); + } + } + else + { + eventsInWindow = new List(); + for (IEnumerator it = consumerView.GetEnumerator(); it.MoveNext(); ) + { + eventsInWindow.Add(it.Current); + } + } + EventBean[] newEvents = eventsInWindow.ToArray(); + if (subselectView != null) + { + ((View)subselectView).Update(newEvents, null); + } + if (eventIndex != null) + { + foreach (var table in eventIndex) + { + table.Add(newEvents); // fill index + } + } + } + else // preload from the data window that sit on top + { + // Start up event table from the iterator + IEnumerator it = subselectView.GetEnumerator(); + if (it.MoveNext()) + { + var preloadEvents = new List(); + do + { + preloadEvents.Add(it.Current); + } while (it.MoveNext()); + + if (eventIndex != null) + { + foreach (var table in eventIndex) + { + table.Add(preloadEvents.ToArray()); + } + } + } + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/context/subselect/SubSelectStrategyHolder.cs b/NEsper.Core/NEsper.Core/core/context/subselect/SubSelectStrategyHolder.cs new file mode 100755 index 000000000..f41c73d2d --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/subselect/SubSelectStrategyHolder.cs @@ -0,0 +1,59 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.core.context.activator; +using com.espertech.esper.core.context.factory; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.prev; +using com.espertech.esper.epl.expression.prior; +using com.espertech.esper.epl.expression.subquery; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.context.subselect +{ + /// + /// Record holding lookup resource references for use by + /// + public class SubSelectStrategyHolder + { + public SubSelectStrategyHolder( + ExprSubselectStrategy stategy, + AggregationService subselectAggregationService, + IDictionary priorStrategies, + IDictionary previousNodeStrategies, + Viewable subselectView, + StatementAgentInstancePostLoad postLoad, + ViewableActivationResult subselectActivationResult) + { + Stategy = stategy; + SubselectAggregationService = subselectAggregationService; + PriorStrategies = priorStrategies; + PreviousNodeStrategies = previousNodeStrategies; + SubselectView = subselectView; + PostLoad = postLoad; + SubselectActivationResult = subselectActivationResult; + } + + public ExprSubselectStrategy Stategy { get; private set; } + + public AggregationService SubselectAggregationService { get; private set; } + + public IDictionary PriorStrategies { get; private set; } + + public IDictionary PreviousNodeStrategies { get; private set; } + + public Viewable SubselectView { get; private set; } + + public StatementAgentInstancePostLoad PostLoad { get; private set; } + + public ViewableActivationResult SubselectActivationResult { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/context/subselect/SubSelectStrategyRealization.cs b/NEsper.Core/NEsper.Core/core/context/subselect/SubSelectStrategyRealization.cs new file mode 100755 index 000000000..fa1516dac --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/subselect/SubSelectStrategyRealization.cs @@ -0,0 +1,59 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.core.context.factory; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.prev; +using com.espertech.esper.epl.expression.prior; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.epl.subquery; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.context.subselect +{ + /// + /// Record holding lookup resource references for use by . + /// + public class SubSelectStrategyRealization + { + public SubSelectStrategyRealization( + SubordTableLookupStrategy strategy, + SubselectAggregationPreprocessorBase subselectAggregationPreprocessor, + AggregationService subselectAggregationService, + IDictionary priorNodeStrategies, + IDictionary previousNodeStrategies, + Viewable subselectView, + StatementAgentInstancePostLoad postLoad) + { + Strategy = strategy; + SubselectAggregationPreprocessor = subselectAggregationPreprocessor; + SubselectAggregationService = subselectAggregationService; + PriorNodeStrategies = priorNodeStrategies; + PreviousNodeStrategies = previousNodeStrategies; + SubselectView = subselectView; + PostLoad = postLoad; + } + + public SubordTableLookupStrategy Strategy { get; private set; } + + public SubselectAggregationPreprocessorBase SubselectAggregationPreprocessor { get; private set; } + + public AggregationService SubselectAggregationService { get; private set; } + + public IDictionary PriorNodeStrategies { get; private set; } + + public IDictionary PreviousNodeStrategies { get; private set; } + + public Viewable SubselectView { get; private set; } + + public StatementAgentInstancePostLoad PostLoad { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/context/util/AgentInstanceComparator.cs b/NEsper.Core/NEsper.Core/core/context/util/AgentInstanceComparator.cs new file mode 100755 index 000000000..12ed85beb --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/util/AgentInstanceComparator.cs @@ -0,0 +1,30 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.core.context.mgr; + +namespace com.espertech.esper.core.context.util +{ + [Serializable] + public class AgentInstanceComparator : IComparer + { + public static AgentInstanceComparator INSTANCE = new AgentInstanceComparator(); + + private readonly EPStatementAgentInstanceHandleComparator _innerComparator = new EPStatementAgentInstanceHandleComparator(); + + public int Compare(AgentInstance ai1, AgentInstance ai2) + { + EPStatementAgentInstanceHandle o1 = ai1.AgentInstanceContext.EpStatementAgentInstanceHandle; + EPStatementAgentInstanceHandle o2 = ai2.AgentInstanceContext.EpStatementAgentInstanceHandle; + return _innerComparator.Compare(o1, o2); + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/util/AgentInstanceContext.cs b/NEsper.Core/NEsper.Core/core/context/util/AgentInstanceContext.cs new file mode 100755 index 000000000..1932cda27 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/util/AgentInstanceContext.cs @@ -0,0 +1,196 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.threading; +using com.espertech.esper.core.context.mgr; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.script; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.events; +using com.espertech.esper.schedule; +using com.espertech.esper.util; + +namespace com.espertech.esper.core.context.util +{ + public class AgentInstanceContext : ExprEvaluatorContext + { + private readonly MappedEventBean _agentInstanceProperties; + private StatementContextCPPair _statementContextCPPair; + private Object _terminationCallbacks; + private AgentInstanceScriptContext _agentInstanceScriptContext; + + public AgentInstanceContext( + StatementContext statementContext, + EPStatementAgentInstanceHandle epStatementAgentInstanceHandle, + int agentInstanceId, + AgentInstanceFilterProxy agentInstanceFilterProxy, + MappedEventBean agentInstanceProperties, + AgentInstanceScriptContext agentInstanceScriptContext) + { + StatementContext = statementContext; + EpStatementAgentInstanceHandle = epStatementAgentInstanceHandle; + AgentInstanceId = agentInstanceId; + AgentInstanceFilterProxy = agentInstanceFilterProxy; + _agentInstanceProperties = agentInstanceProperties; + AllocateAgentInstanceScriptContext = agentInstanceScriptContext; + _terminationCallbacks = null; + } + + public AgentInstanceFilterProxy AgentInstanceFilterProxy { get; private set; } + public StatementContext StatementContext { get; private set; } + + public EPStatementAgentInstanceHandle EpStatementAgentInstanceHandle { get; private set; } + + public AgentInstanceScriptContext AllocateAgentInstanceScriptContext + { + get + { + if (_agentInstanceScriptContext == null) + { + _agentInstanceScriptContext = AgentInstanceScriptContext.From(StatementContext.EventAdapterService); + } + + return _agentInstanceScriptContext; + } + private set { _agentInstanceScriptContext = value; } + } + + public TimeProvider TimeProvider + { + get { return StatementContext.TimeProvider; } + } + + public ExpressionResultCacheService ExpressionResultCacheService + { + get { return StatementContext.ExpressionResultCacheServiceSharable; } + } + + public int AgentInstanceId { get; private set; } + + public EventBean ContextProperties + { + get { return _agentInstanceProperties; } + } + + public TableExprEvaluatorContext TableExprEvaluatorContext + { + get { return StatementContext.TableExprEvaluatorContext; } + } + + public string StatementName + { + get { return StatementContext.StatementName; } + } + + public string EngineURI + { + get { return StatementContext.EngineURI; } + } + + public int StatementId + { + get { return StatementContext.StatementId; } + } + + public StatementType? StatementType + { + get { return StatementContext.StatementType; } + } + + public IReaderWriterLock AgentInstanceLock + { + get { return EpStatementAgentInstanceHandle.StatementAgentInstanceLock; } + } + + public Object StatementUserObject + { + get { return StatementContext.StatementUserObject; } + } + + public ICollection TerminationCallbackRO + { + get + { + if (_terminationCallbacks == null) + { + return Collections.GetEmptyList(); + } + else if (_terminationCallbacks is ICollection) + { + return (ICollection)_terminationCallbacks; + } + return Collections.SingletonList((StopCallback)_terminationCallbacks); + } + } + + public void AddTerminationCallback(Action action) + { + AddTerminationCallback(new ProxyStopCallback(action)); + } + + public void AddTerminationCallback(StopCallback callback) + { + if (_terminationCallbacks == null) + { + _terminationCallbacks = callback; + } + else if (_terminationCallbacks is ICollection) + { + ((ICollection)_terminationCallbacks).Add(callback); + } + else + { + var cb = (StopCallback)_terminationCallbacks; + var q = new HashSet(); + q.Add(cb); + q.Add(callback); + _terminationCallbacks = q; + } + } + + public void RemoveTerminationCallback(Action action) + { + RemoveTerminationCallback(new ProxyStopCallback(action)); + } + + public void RemoveTerminationCallback(StopCallback callback) + { + if (_terminationCallbacks == null) + { + } + else if (_terminationCallbacks is ICollection) + { + ((ICollection)_terminationCallbacks).Remove(callback); + } + else if (ReferenceEquals(_terminationCallbacks, callback)) + { + _terminationCallbacks = null; + } + } + + public StatementContextCPPair StatementContextCPPair + { + get + { + if (_statementContextCPPair == null) + { + _statementContextCPPair = new StatementContextCPPair( + StatementContext.StatementId, AgentInstanceId, StatementContext); + } + return _statementContextCPPair; + } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/context/util/AgentInstanceViewFactoryChainContext.cs b/NEsper.Core/NEsper.Core/core/context/util/AgentInstanceViewFactoryChainContext.cs new file mode 100755 index 000000000..4adb34a6c --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/util/AgentInstanceViewFactoryChainContext.cs @@ -0,0 +1,194 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.threading; +using com.espertech.esper.core.service; +using com.espertech.esper.core.start; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.script; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.schedule; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.context.util +{ + public class AgentInstanceViewFactoryChainContext : ExprEvaluatorContext + { + private readonly AgentInstanceContext _agentInstanceContext; + private bool _isRemoveStream; + private readonly Object _previousNodeGetter; + private readonly ViewUpdatedCollection _priorViewUpdatedCollection; + + public AgentInstanceViewFactoryChainContext(AgentInstanceContext agentInstanceContext, bool isRemoveStream, Object previousNodeGetter, ViewUpdatedCollection priorViewUpdatedCollection) + { + _agentInstanceContext = agentInstanceContext; + _isRemoveStream = isRemoveStream; + _previousNodeGetter = previousNodeGetter; + _priorViewUpdatedCollection = priorViewUpdatedCollection; + } + + public IReaderWriterLock AgentInstanceLock + { + get { return _agentInstanceContext.AgentInstanceLock; } + } + + public AgentInstanceContext AgentInstanceContext + { + get { return _agentInstanceContext; } + } + + public AgentInstanceScriptContext AllocateAgentInstanceScriptContext + { + get { return _agentInstanceContext.AllocateAgentInstanceScriptContext; } + } + + public bool IsRemoveStream + { + get { return _isRemoveStream; } + set { _isRemoveStream = value; } + } + + public object PreviousNodeGetter + { + get { return _previousNodeGetter; } + } + + public ViewUpdatedCollection PriorViewUpdatedCollection + { + get { return _priorViewUpdatedCollection; } + } + + public StatementContext StatementContext + { + get { return _agentInstanceContext.StatementContext; } + } + + public TimeProvider TimeProvider + { + get { return _agentInstanceContext.TimeProvider; } + } + + public ExpressionResultCacheService ExpressionResultCacheService + { + get { return _agentInstanceContext.ExpressionResultCacheService; } + } + + public int AgentInstanceId + { + get { return _agentInstanceContext.AgentInstanceId; } + } + + public EventBean ContextProperties + { + get { return _agentInstanceContext.ContextProperties; } + } + + public EPStatementAgentInstanceHandle EpStatementAgentInstanceHandle + { + get { return _agentInstanceContext.EpStatementAgentInstanceHandle; } + } + + public ICollection TerminationCallbacksRO + { + get { return _agentInstanceContext.TerminationCallbackRO; } + } + + public void AddTerminationCallback(Action action) + { + AddTerminationCallback(new ProxyStopCallback(action)); + } + + public void AddTerminationCallback(StopCallback callback) + { + _agentInstanceContext.AddTerminationCallback(callback); + } + + public void RemoveTerminationCallback(Action action) + { + RemoveTerminationCallback(new ProxyStopCallback(action)); + } + + public void RemoveTerminationCallback(StopCallback callback) + { + _agentInstanceContext.RemoveTerminationCallback(callback); + } + + public TableExprEvaluatorContext TableExprEvaluatorContext + { + get { return _agentInstanceContext.TableExprEvaluatorContext; } + } + + public static AgentInstanceViewFactoryChainContext Create(IList viewFactoryChain, AgentInstanceContext agentInstanceContext, ViewResourceDelegateVerifiedStream viewResourceDelegate) + { + + Object previousNodeGetter = null; + if (viewResourceDelegate.PreviousRequests != null && !viewResourceDelegate.PreviousRequests.IsEmpty()) + { + DataWindowViewWithPrevious factoryFound = EPStatementStartMethodHelperPrevious.FindPreviousViewFactory(viewFactoryChain); + previousNodeGetter = factoryFound.MakePreviousGetter(); + } + + ViewUpdatedCollection priorViewUpdatedCollection = null; + if (viewResourceDelegate.PriorRequests != null && !viewResourceDelegate.PriorRequests.IsEmpty()) + { + var priorEventViewFactory = EPStatementStartMethodHelperPrior.FindPriorViewFactory(viewFactoryChain); + var callbacksPerIndex = viewResourceDelegate.PriorRequests; + priorViewUpdatedCollection = priorEventViewFactory.MakeViewUpdatedCollection(callbacksPerIndex, agentInstanceContext.AgentInstanceId); + } + + bool removedStream = false; + if (viewFactoryChain.Count > 1) + { + int countDataWindow = 0; + foreach (ViewFactory viewFactory in viewFactoryChain) + { + if (viewFactory is DataWindowViewFactory) + { + countDataWindow++; + } + } + removedStream = countDataWindow > 1; + } + + return new AgentInstanceViewFactoryChainContext(agentInstanceContext, removedStream, previousNodeGetter, priorViewUpdatedCollection); + } + + public string StatementName + { + get { return _agentInstanceContext.StatementName; } + } + + public string EngineURI + { + get { return _agentInstanceContext.EngineURI; } + } + + public int StatementId + { + get { return _agentInstanceContext.StatementId; } + } + + public StatementType? StatementType + { + get { return _agentInstanceContext.StatementType; } + } + + public Object StatementUserObject + { + get { return _agentInstanceContext.StatementUserObject; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/util/ContextControllerSelectorUtil.cs b/NEsper.Core/NEsper.Core/core/context/util/ContextControllerSelectorUtil.cs new file mode 100755 index 000000000..05af0e553 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/util/ContextControllerSelectorUtil.cs @@ -0,0 +1,43 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client.context; +using com.espertech.esper.compat.collections; +using com.espertech.esper.util; + +namespace com.espertech.esper.core.context.util +{ + public class ContextControllerSelectorUtil + { + public static InvalidContextPartitionSelector GetInvalidSelector(Type[] choice, ContextPartitionSelector selector) + { + return GetInvalidSelector(choice, selector, false); + } + + public static InvalidContextPartitionSelector GetInvalidSelector(Type[] choice, ContextPartitionSelector selector, bool isNested) + { + var expected = new LinkedHashSet(); + expected.Add(typeof(ContextPartitionSelectorAll).Name); + if (!isNested) + { + expected.Add(typeof(ContextPartitionSelectorFiltered).Name); + } + expected.Add(typeof(ContextPartitionSelectorById).Name); + for (int i = 0; i < choice.Length; i++) + { + expected.Add(choice[i].Name); + } + String expectedList = CollectionUtil.ToString(expected); + String receivedClass = selector.GetType().FullName; + String message = "Invalid context partition selector, expected an implementation class of any of [" + expectedList + "] interfaces but received " + receivedClass; + return new InvalidContextPartitionSelector(message); + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/util/ContextDescriptor.cs b/NEsper.Core/NEsper.Core/core/context/util/ContextDescriptor.cs new file mode 100755 index 000000000..d0f161a88 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/util/ContextDescriptor.cs @@ -0,0 +1,82 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.client.context; +using com.espertech.esper.core.context.stmt; +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.core.context.util +{ + public class ContextDescriptor + { + private readonly string _contextName; + private readonly bool _singleInstanceContext; + private readonly ContextPropertyRegistry _contextPropertyRegistry; + private readonly StatementAIResourceRegistryFactory _aiResourceRegistryFactory; + private readonly ContextEnumeratorHandler _iteratorHandler; + private readonly ContextDetail _contextDetail; + + public ContextDescriptor(string contextName, bool singleInstanceContext, ContextPropertyRegistry contextPropertyRegistry, StatementAIResourceRegistryFactory aiResourceRegistryFactory, ContextEnumeratorHandler iteratorHandler, ContextDetail contextDetail) + { + _contextName = contextName; + _singleInstanceContext = singleInstanceContext; + _contextPropertyRegistry = contextPropertyRegistry; + _aiResourceRegistryFactory = aiResourceRegistryFactory; + _iteratorHandler = iteratorHandler; + _contextDetail = contextDetail; + } + + public string ContextName + { + get { return _contextName; } + } + + public bool IsSingleInstanceContext + { + get { return _singleInstanceContext; } + } + + public ContextPropertyRegistry ContextPropertyRegistry + { + get { return _contextPropertyRegistry; } + } + + public StatementAIResourceRegistryFactory AiResourceRegistryFactory + { + get { return _aiResourceRegistryFactory; } + } + + public IEnumerator GetEnumerator(int statementId) { + return _iteratorHandler.GetEnumerator(statementId); + } + + public IEnumerator GetSafeEnumerator(int statementId) + { + return _iteratorHandler.GetSafeEnumerator(statementId); + } + + public IEnumerator GetEnumerator(int statementId, ContextPartitionSelector selector) + { + return _iteratorHandler.GetEnumerator(statementId, selector); + } + + public IEnumerator GetSafeEnumerator(int statementId, ContextPartitionSelector selector) + { + return _iteratorHandler.GetSafeEnumerator(statementId, selector); + } + + public ContextDetail ContextDetail + { + get { return _contextDetail; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/util/ContextEnumeratorHandler.cs b/NEsper.Core/NEsper.Core/core/context/util/ContextEnumeratorHandler.cs new file mode 100755 index 000000000..db02ed3b9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/util/ContextEnumeratorHandler.cs @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.client.context; + +namespace com.espertech.esper.core.context.util +{ + public interface ContextEnumeratorHandler + { + IEnumerator GetEnumerator(int statementId); + IEnumerator GetSafeEnumerator(int statementId); + IEnumerator GetEnumerator(int statementId, ContextPartitionSelector selector); + IEnumerator GetSafeEnumerator(int statementId, ContextPartitionSelector selector); + + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/util/ContextMergeView.cs b/NEsper.Core/NEsper.Core/core/context/util/ContextMergeView.cs new file mode 100755 index 000000000..9db28b4de --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/util/ContextMergeView.cs @@ -0,0 +1,57 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.core.service; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.context.util +{ + public class ContextMergeView + : ViewSupport + , UpdateDispatchView + { + private readonly EventType _eventType; + + public ContextMergeView(EventType eventType) + { + _eventType = eventType; + } + + #region UpdateDispatchView Members + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + // no action required + } + + public void NewResult(UniformPair result) + { + if (result != null) + { + UpdateChildren(result.First, result.Second); + } + } + + public override EventType EventType + { + get { return _eventType; } + } + + public override IEnumerator GetEnumerator() + { + throw new UnsupportedOperationException("GetEnumerator not supported"); + } + + #endregion + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/context/util/ContextMergeViewForwarding.cs b/NEsper.Core/NEsper.Core/core/context/util/ContextMergeViewForwarding.cs new file mode 100755 index 000000000..018c960f8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/util/ContextMergeViewForwarding.cs @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; + +namespace com.espertech.esper.core.context.util +{ + public class ContextMergeViewForwarding : ContextMergeView + { + public ContextMergeViewForwarding(EventType eventType) + : base(eventType) + { + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + UpdateChildren(newData, oldData); + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/util/ContextPartitionImportCallback.cs b/NEsper.Core/NEsper.Core/core/context/util/ContextPartitionImportCallback.cs new file mode 100755 index 000000000..faa879cbf --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/util/ContextPartitionImportCallback.cs @@ -0,0 +1,16 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.core.context.util +{ + public interface ContextPartitionImportCallback + { + void Existing(int agentInstanceId, int exportedAgentInstanceId); + void Allocated(int agentInstanceId, int exportedAgentInstanceId); + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/util/ContextPropertyRegistry.cs b/NEsper.Core/NEsper.Core/core/context/util/ContextPropertyRegistry.cs new file mode 100755 index 000000000..a6bc66656 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/util/ContextPropertyRegistry.cs @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.core.context.util +{ + public interface ContextPropertyRegistry + { + bool IsContextPropertyPrefix(String prefixName); + EventType ContextEventType { get; } + + bool IsPartitionProperty(EventType fromType, String propertyName); + String GetPartitionContextPropertyName(EventType fromType, String propertyName); + } + + public class ContextPropertyRegistryConstants + { + public const String CONTEXT_PREFIX = "context"; + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/util/EPStatementAgentInstanceHandle.cs b/NEsper.Core/NEsper.Core/core/context/util/EPStatementAgentInstanceHandle.cs new file mode 100755 index 000000000..57f1b0786 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/util/EPStatementAgentInstanceHandle.cs @@ -0,0 +1,147 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat.threading; +using com.espertech.esper.core.service; +using com.espertech.esper.core.service.multimatch; +using com.espertech.esper.filter; + +namespace com.espertech.esper.core.context.util +{ + [Serializable] + public class EPStatementAgentInstanceHandle + { + private readonly int _hashCode; + private IReaderWriterLock _statementAgentInstanceLock = null; + private EPStatementDispatch _optionalDispatchable; + + public EPStatementAgentInstanceHandle(EPStatementHandle statementHandle, IReaderWriterLock statementAgentInstanceLock, int agentInstanceId, StatementAgentInstanceFilterVersion statementFilterVersion, FilterFaultHandlerFactory filterFaultHandlerFactory) + { + StatementHandle = statementHandle; + _statementAgentInstanceLock = statementAgentInstanceLock; + AgentInstanceId = agentInstanceId; + _hashCode = 31 * statementHandle.GetHashCode() + agentInstanceId; + StatementFilterVersion = statementFilterVersion; + if (filterFaultHandlerFactory != null) + { + FilterFaultHandler = filterFaultHandlerFactory.MakeFilterFaultHandler(); + } + } + + public EPStatementHandle StatementHandle { get; private set; } + + public IReaderWriterLock StatementAgentInstanceLock + { + get { return _statementAgentInstanceLock; } + set { _statementAgentInstanceLock = value; } + } + + public int AgentInstanceId { get; private set; } + + public int Priority + { + get { return StatementHandle.Priority; } + } + + public bool IsPreemptive + { + get { return StatementHandle.IsPreemptive; } + } + + public bool HasVariables + { + get { return StatementHandle.HasVariables; } + } + + public bool HasTableAccess + { + get { return StatementHandle.HasTableAccess; } + } + + public bool CanSelfJoin + { + get { return StatementHandle.IsCanSelfJoin; } + } + + public StatementAgentInstanceFilterVersion StatementFilterVersion { get; private set; } + + /// Tests filter version. + /// to test + /// indicator whether version is up-to-date + public bool IsCurrentFilter(long filterVersion) { + return StatementFilterVersion.IsCurrentFilter(filterVersion); + } + + public override bool Equals(Object otherObj) + { + if (this == otherObj) { + return true; + } + + if (!(otherObj is EPStatementAgentInstanceHandle)) { + return false; + } + + var other = (EPStatementAgentInstanceHandle) otherObj; + return (other.StatementHandle.StatementId == StatementHandle.StatementId) && (other.AgentInstanceId == AgentInstanceId); + } + + public override int GetHashCode() + { + return _hashCode; + } + + /// + /// Provides a callback for use when statement processing for filters and schedules is done, for use by join + /// statements that require an explicit indicator that all joined streams results have been processed. + /// + /// + /// is the instance for calling onto after statement callback processing + /// + public EPStatementDispatch OptionalDispatchable + { + get { return _optionalDispatchable; } + set { _optionalDispatchable = value; } + } + + /// + /// Invoked by to indicate that a statements's filer + /// and schedule processing is done, and now it's time to process join results. + /// + public void InternalDispatch() + { + if (_optionalDispatchable != null) + { + _optionalDispatchable.Execute(); + } + } + + public bool IsDestroyed { get; set; } + + public override String ToString() + { + return "EPStatementAgentInstanceHandle{" + + "name=" + StatementHandle.StatementName + + "}"; + } + + public FilterFaultHandler FilterFaultHandler { get; set; } + + public int StatementId + { + get { return StatementHandle.StatementId; } + } + + public MultiMatchHandler MultiMatchHandler + { + get { return StatementHandle.MultiMatchHandler; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/util/EPStatementAgentInstanceHandleComparator.cs b/NEsper.Core/NEsper.Core/core/context/util/EPStatementAgentInstanceHandleComparator.cs new file mode 100755 index 000000000..498b998f4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/util/EPStatementAgentInstanceHandleComparator.cs @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + + +namespace com.espertech.esper.core.context.util +{ + public class EPStatementAgentInstanceHandleComparator : IComparer + { + public static EPStatementAgentInstanceHandleComparator Instance = new EPStatementAgentInstanceHandleComparator(); + + public int Compare(EPStatementAgentInstanceHandle o1, EPStatementAgentInstanceHandle o2) + { + if (o1.Priority == o2.Priority) { + if (o1 == o2 || o1.Equals(o2)) { + return 0; + } + if (o1.StatementId != o2.StatementId) { + return o1.StatementId.CompareTo(o2.StatementId); + } + return o1.AgentInstanceId < o2.AgentInstanceId ? -1 : 1; + } + else { + return o1.Priority > o2.Priority ? -1 : 1; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/util/EPStatementAgentInstanceHandlePrioritySort.cs b/NEsper.Core/NEsper.Core/core/context/util/EPStatementAgentInstanceHandlePrioritySort.cs new file mode 100755 index 000000000..05607ea16 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/util/EPStatementAgentInstanceHandlePrioritySort.cs @@ -0,0 +1,51 @@ +using System; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.service; + +namespace com.espertech.esper.core.context.util +{ + public class EPStatementAgentInstanceHandlePrioritySort : StandardComparer + { + /// + /// Initializes a new instance of the class. + /// + public EPStatementAgentInstanceHandlePrioritySort() + : base(GetComparer(false)) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// if set to true [use greater than or equal]. + public EPStatementAgentInstanceHandlePrioritySort(bool useGreaterThanOrEqual) + : base(GetComparer(useGreaterThanOrEqual)) + { + } + + /// + /// Gets the comparer. + /// + /// if set to true [use greater than or equal]. + /// + public static Func GetComparer(bool useGreaterThanOrEqual) + { + if (useGreaterThanOrEqual) + { + return CompareGte; + } + + return CompareGt; + } + + public static int CompareGt(EPStatementAgentInstanceHandle x, EPStatementAgentInstanceHandle y) + { + return x.Priority > y.Priority ? -1 : 1; + } + + public static int CompareGte(EPStatementAgentInstanceHandle x, EPStatementAgentInstanceHandle y) + { + return x.Priority >= y.Priority ? -1 : 1; + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/context/util/StatementAgentInstanceUtil.cs b/NEsper.Core/NEsper.Core/core/context/util/StatementAgentInstanceUtil.cs new file mode 100755 index 000000000..903268ab5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/context/util/StatementAgentInstanceUtil.cs @@ -0,0 +1,520 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.client.hook; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.compat.threading; +using com.espertech.esper.core.context.factory; +using com.espertech.esper.core.context.mgr; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.named; +using com.espertech.esper.epl.script; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.view; +using com.espertech.esper.events; +using com.espertech.esper.filter; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.context.util +{ + public class StatementAgentInstanceUtil + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + public static void HandleFilterFault(EventBean theEvent, long version, EPServicesContext servicesContext, IDictionary agentInstanceListMap) + { + foreach (var agentInstanceEntry in agentInstanceListMap) + { + if (agentInstanceEntry.Value.FilterVersionAfterAllocation > version) + { + StatementAgentInstanceUtil.EvaluateEventForStatement( + servicesContext, theEvent, null, agentInstanceEntry.Value.AgentInstances); + } + } + } + + public static void StopAgentInstances(IList agentInstances, IDictionary terminationProperties, EPServicesContext servicesContext, bool isStatementStop, bool leaveLocksAcquired) + { + if (agentInstances == null) + { + return; + } + foreach (var instance in agentInstances) + { + StopAgentInstanceRemoveResources(instance, terminationProperties, servicesContext, isStatementStop, leaveLocksAcquired); + } + } + + public static void StopAgentInstanceRemoveResources(AgentInstance agentInstance, IDictionary terminationProperties, EPServicesContext servicesContext, bool isStatementStop, bool leaveLocksAcquired) + { + if (terminationProperties != null) + { + var contextProperties = (MappedEventBean)agentInstance.AgentInstanceContext.ContextProperties; + contextProperties.Properties.PutAll(terminationProperties); + } + StatementAgentInstanceUtil.Stop(agentInstance.StopCallback, agentInstance.AgentInstanceContext, agentInstance.FinalView, servicesContext, isStatementStop, leaveLocksAcquired, true); + } + + public static void StopSafe(ICollection terminationCallbacks, StopCallback[] stopCallbacks, StatementContext statementContext) + { + var terminationArr = terminationCallbacks.ToArray(); + StopSafe(terminationArr, statementContext); + StopSafe(stopCallbacks, statementContext); + } + + public static void StopSafe(StopCallback[] stopMethods, StatementContext statementContext) + { + foreach (var stopCallback in stopMethods) + { + StopSafe(stopCallback, statementContext); + } + } + + public static void StopSafe(StopCallback stopMethod, StatementContext statementContext) + { + try + { + stopMethod.Stop(); + } + catch (Exception e) + { + statementContext.ExceptionHandlingService.HandleException(e, statementContext.StatementName, statementContext.Expression, ExceptionHandlerExceptionType.STOP, null); + } + } + + public static void Stop(StopCallback stopCallback, AgentInstanceContext agentInstanceContext, Viewable finalView, EPServicesContext servicesContext, bool isStatementStop, bool leaveLocksAcquired, bool removedStatementResources) + { + using (Instrument.With( + i => i.QContextPartitionDestroy(agentInstanceContext), + i => i.AContextPartitionDestroy())) + { + // obtain statement lock + var iLock = agentInstanceContext.EpStatementAgentInstanceHandle.StatementAgentInstanceLock; + using (var iLockEnd = iLock.WriteLock.Acquire(!leaveLocksAcquired)) + { + try + { + if (finalView is OutputProcessViewTerminable && !isStatementStop) + { + var terminable = (OutputProcessViewTerminable)finalView; + terminable.Terminated(); + } + + StopSafe(stopCallback, agentInstanceContext.StatementContext); + + // release resource + agentInstanceContext.StatementContext.StatementAgentInstanceRegistry.Deassign( + agentInstanceContext.AgentInstanceId); + + // cause any remaining schedules, that may concide with the caller's schedule, to be ignored + agentInstanceContext.EpStatementAgentInstanceHandle.IsDestroyed = true; + + // cause any filters, that may concide with the caller's filters, to be ignored + agentInstanceContext.EpStatementAgentInstanceHandle.StatementFilterVersion.StmtFilterVersion = Int64.MaxValue; + + if (removedStatementResources && + agentInstanceContext.StatementContext.StatementExtensionServicesContext != null && + agentInstanceContext.StatementContext.StatementExtensionServicesContext.StmtResources != null) + { + agentInstanceContext.StatementContext.StatementExtensionServicesContext.StmtResources + .DeallocatePartitioned(agentInstanceContext.AgentInstanceId); + } + } + finally + { + if (!leaveLocksAcquired) + { + if (agentInstanceContext.StatementContext.EpStatementHandle.HasTableAccess) + { + agentInstanceContext.TableExprEvaluatorContext.ReleaseAcquiredLocks(); + } + } + } + } + } + } + + public static StatementAgentInstanceFactoryResult Start(EPServicesContext servicesContext, ContextControllerStatementBase statement, bool isSingleInstanceContext, int agentInstanceId, MappedEventBean agentInstanceProperties, AgentInstanceFilterProxy agentInstanceFilterProxy, bool isRecoveringResilient) + { + var statementContext = statement.StatementContext; + + // for on-trigger statements against named windows we must use the named window lock + OnTriggerDesc optOnTriggerDesc = statement.StatementSpec.OnTriggerDesc; + String namedWindowName = null; + if ((optOnTriggerDesc != null) && (optOnTriggerDesc is OnTriggerWindowDesc)) + { + String windowName = ((OnTriggerWindowDesc)optOnTriggerDesc).WindowName; + if (servicesContext.TableService.GetTableMetadata(windowName) == null) + { + namedWindowName = windowName; + } + } + + // determine lock to use + IReaderWriterLock agentInstanceLock; + if (namedWindowName != null) + { + NamedWindowProcessor processor = servicesContext.NamedWindowMgmtService.GetProcessor(namedWindowName); + NamedWindowProcessorInstance instance = processor.GetProcessorInstance(agentInstanceId); + agentInstanceLock = instance.RootViewInstance.AgentInstanceContext.EpStatementAgentInstanceHandle.StatementAgentInstanceLock; + } + else + { + if (isSingleInstanceContext) + { + agentInstanceLock = statementContext.DefaultAgentInstanceLock; + } + else + { + agentInstanceLock = servicesContext.StatementLockFactory.GetStatementLock( + statementContext.StatementName, statementContext.Annotations, statementContext.IsStatelessSelect); + } + } + + // share the filter version between agent instance handle (callbacks) and agent instance context + var filterVersion = new StatementAgentInstanceFilterVersion(); + + // create handle that comtains lock for use in scheduling and filter callbacks + var agentInstanceHandle = new EPStatementAgentInstanceHandle(statementContext.EpStatementHandle, agentInstanceLock, agentInstanceId, filterVersion, statementContext.FilterFaultHandlerFactory); + + // create agent instance context + AgentInstanceScriptContext agentInstanceScriptContext = null; + if (statementContext.DefaultAgentInstanceScriptContext != null) + { + agentInstanceScriptContext = AgentInstanceScriptContext.From(statementContext.EventAdapterService); + } + var agentInstanceContext = new AgentInstanceContext(statementContext, agentInstanceHandle, agentInstanceId, agentInstanceFilterProxy, agentInstanceProperties, agentInstanceScriptContext); + var statementAgentInstanceLock = agentInstanceContext.EpStatementAgentInstanceHandle.StatementAgentInstanceLock; + + using (Instrument.With( + i => i.QContextPartitionAllocate(agentInstanceContext), + i => i.AContextPartitionAllocate())) + { + using (statementAgentInstanceLock.AcquireWriteLock()) + { + try + { + // start + var startResult = statement.Factory.NewContext(agentInstanceContext, isRecoveringResilient); + + // hook up with listeners+subscribers + startResult.FinalView.AddView(statement.MergeView); // hook output to merge view + + // assign agents for expression-node based strategies + var aiExprSvc = statementContext.StatementAgentInstanceRegistry.AgentInstanceExprService; + var aiAggregationSvc = + statementContext.StatementAgentInstanceRegistry.AgentInstanceAggregationService; + + // allocate aggregation service + if (startResult.OptionalAggegationService != null) + { + aiAggregationSvc.AssignService(agentInstanceId, startResult.OptionalAggegationService); + } + + // allocate subquery + foreach (var item in startResult.SubselectStrategies) + { + var node = item.Key; + var strategyHolder = item.Value; + + aiExprSvc.GetSubselectService(node).AssignService(agentInstanceId, strategyHolder.Stategy); + aiExprSvc.GetSubselectAggregationService(node) + .AssignService(agentInstanceId, strategyHolder.SubselectAggregationService); + + // allocate prior within subquery + foreach (var priorEntry in strategyHolder.PriorStrategies) + { + aiExprSvc.GetPriorServices(priorEntry.Key).AssignService(agentInstanceId, priorEntry.Value); + } + + // allocate previous within subquery + foreach (var prevEntry in strategyHolder.PreviousNodeStrategies) + { + aiExprSvc.GetPreviousServices(prevEntry.Key) + .AssignService(agentInstanceId, prevEntry.Value); + } + } + + // allocate prior-expressions + foreach (var item in startResult.PriorNodeStrategies) + { + aiExprSvc.GetPriorServices(item.Key).AssignService(agentInstanceId, item.Value); + } + + // allocate previous-expressions + foreach (var item in startResult.PreviousNodeStrategies) + { + aiExprSvc.GetPreviousServices(item.Key).AssignService(agentInstanceId, item.Value); + } + + // allocate match-recognize previous expressions + var regexExprPreviousEvalStrategy = startResult.RegexExprPreviousEvalStrategy; + aiExprSvc.GetMatchRecognizePrevious().AssignService(agentInstanceId, regexExprPreviousEvalStrategy); + + // allocate table-access-expressions + foreach (var item in startResult.TableAccessEvalStrategies) + { + aiExprSvc.GetTableAccessServices(item.Key).AssignService(agentInstanceId, item.Value); + } + + // execute preloads, if any + foreach (var preload in startResult.PreloadList) + { + preload.ExecutePreload(); + } + + if (statementContext.StatementExtensionServicesContext != null && + statementContext.StatementExtensionServicesContext.StmtResources != null) + { + var holder = statementContext.StatementExtensionServicesContext.ExtractStatementResourceHolder(startResult); + statementContext.StatementExtensionServicesContext.StmtResources.SetPartitioned(agentInstanceId, holder); + } + + // instantiate + return startResult; + } + finally + { + if (agentInstanceContext.StatementContext.EpStatementHandle.HasTableAccess) + { + agentInstanceContext.TableExprEvaluatorContext.ReleaseAcquiredLocks(); + } + } + } + } + } + + public static void EvaluateEventForStatement(EPServicesContext servicesContext, EventBean theEvent, IDictionary optionalTriggeringPattern, IList agentInstances) + { + if (theEvent != null) + { + EvaluateEventForStatementInternal(servicesContext, theEvent, agentInstances); + } + if (optionalTriggeringPattern != null) + { + // evaluation order definition is up to the originator of the triggering pattern + foreach (var entry in optionalTriggeringPattern) + { + if (entry.Value is EventBean) + { + EvaluateEventForStatementInternal(servicesContext, (EventBean)entry.Value, agentInstances); + } + else if (entry.Value is EventBean[]) + { + var eventsArray = (EventBean[])entry.Value; + for (var ii = 0; ii < eventsArray.Length; ii++) + { + EvaluateEventForStatementInternal(servicesContext, eventsArray[ii], agentInstances); + } + } + } + } + } + + private static void EvaluateEventForStatementInternal(EPServicesContext servicesContext, EventBean theEvent, IList agentInstances) + { + // context was created - reevaluate for the given event + var callbacks = new ArrayDeque(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 stmtCallbacks; + if (!isPrioritized) + { + stmtCallbacks = new Dictionary(); + } + else + { + stmtCallbacks = new SortedDictionary(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) + { + var collection = (ICollection) stmtCallback; + if (!collection.Contains(handleCallback)) // De-duplicate for Filter OR expression paths + { + collection.Add(handleCallback); + } + } + else + { + var deque = new ArrayDeque(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(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) + { + Process(agentInstance, servicesContext, (ICollection)callbackList, theEvent); + } + else + { + Process(agentInstance, servicesContext, Collections.SingletonList((FilterHandle)callbackList), theEvent); + } + if (agentInstance.AgentInstanceContext.EpStatementAgentInstanceHandle.IsPreemptive) + { + return; + } + } + } + + public static bool EvaluateFilterForStatement(EPServicesContext servicesContext, EventBean theEvent, AgentInstanceContext agentInstanceContext, FilterHandle filterHandle) + { + // context was created - reevaluate for the given event + var callbacks = new ArrayDeque(); + servicesContext.FilterService.Evaluate(theEvent, callbacks, agentInstanceContext.StatementContext.StatementId); + + try + { + servicesContext.VariableService.SetLocalVersion(); + + // sub-selects always go first + if (callbacks.Any(handle => handle == filterHandle)) + { + return true; + } + + agentInstanceContext.EpStatementAgentInstanceHandle.InternalDispatch(); + + } + catch (Exception ex) + { + servicesContext.ExceptionHandlingService.HandleException( + ex, agentInstanceContext.EpStatementAgentInstanceHandle, ExceptionHandlerExceptionType.PROCESS, + theEvent); + } + + return false; + } + + public static StopCallback GetStopCallback(IList stopCallbacks, AgentInstanceContext agentInstanceContext) + { + var stopCallbackArr = stopCallbacks.ToArray(); + return new ProxyStopCallback(() => StopSafe( + agentInstanceContext.TerminationCallbackRO, stopCallbackArr, + agentInstanceContext.StatementContext)); + } + + private static void Process( + AgentInstance agentInstance, + EPServicesContext servicesContext, + IEnumerable callbacks, + EventBean theEvent) + { + var agentInstanceContext = agentInstance.AgentInstanceContext; + using (agentInstanceContext.AgentInstanceLock.AcquireWriteLock()) + { + try + { + servicesContext.VariableService.SetLocalVersion(); + + // sub-selects always go first + foreach (var handle in callbacks) + { + var callback = (EPStatementHandleCallback)handle; + if (callback.AgentInstanceHandle != agentInstanceContext.EpStatementAgentInstanceHandle) + { + continue; + } + callback.FilterCallback.MatchFound(theEvent, null); + } + + agentInstanceContext.EpStatementAgentInstanceHandle.InternalDispatch(); + } + catch (Exception ex) + { + servicesContext.ExceptionHandlingService.HandleException( + ex, agentInstanceContext.EpStatementAgentInstanceHandle, ExceptionHandlerExceptionType.PROCESS, + theEvent); + } + finally + { + if (agentInstanceContext.StatementContext.EpStatementHandle.HasTableAccess) + { + agentInstanceContext.TableExprEvaluatorContext.ReleaseAcquiredLocks(); + } + } + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/deploy/DeploymentStateService.cs b/NEsper.Core/NEsper.Core/core/deploy/DeploymentStateService.cs new file mode 100755 index 000000000..356f79d48 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/deploy/DeploymentStateService.cs @@ -0,0 +1,44 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client.deploy; + + +namespace com.espertech.esper.core.deploy +{ + /// Interface for a service maintaining deployment state. + public interface DeploymentStateService : IDisposable + { + /// Allocates a new deployment id. + /// deployment id + string NextDeploymentId { get; } + + /// Returns a list of deployment ids of deployments. + /// deployment ids + string[] Deployments { get; } + + /// Returns the deployment informaton for a given deployment id. + /// id + /// deployment information + DeploymentInformation GetDeployment(String deploymentId); + + /// Returns deployment information for all deployments. + /// array of deployment info + DeploymentInformation[] AllDeployments { get; } + + /// Add or Update the deployment information using the contained deployment id as a key. + /// to store + void AddUpdateDeployment(DeploymentInformation descriptor); + + /// + /// + void Remove(String deploymentId); + } +} diff --git a/NEsper.Core/NEsper.Core/core/deploy/DeploymentStateServiceImpl.cs b/NEsper.Core/NEsper.Core/core/deploy/DeploymentStateServiceImpl.cs new file mode 100755 index 000000000..231419df7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/deploy/DeploymentStateServiceImpl.cs @@ -0,0 +1,97 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client.deploy; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.threading; +using com.espertech.esper.util; + +namespace com.espertech.esper.core.deploy +{ + /// Implementation for storing deployment state. + public class DeploymentStateServiceImpl : DeploymentStateService + { + private readonly IDictionary _deployments; + private readonly ILockable _lock = + LockManager.CreateLock(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + public DeploymentStateServiceImpl() + { + _deployments = new ConcurrentDictionary(); + } + + public string NextDeploymentId + { + get { return UuidGenerator.Generate(); } + } + + public DeploymentInformation[] AllDeployments + { + get + { + using (_lock.Acquire()) + { + return _deployments.Values.ToArrayOrNull(); + } + } + } + + public void AddUpdateDeployment(DeploymentInformation descriptor) + { + using(_lock.Acquire()) + { + _deployments.Put(descriptor.DeploymentId, descriptor); + } + } + + public void Remove(String deploymentId) + { + using(_lock.Acquire()) + { + _deployments.Remove(deploymentId); + } + } + + public string[] Deployments + { + get + { + using (_lock.Acquire()) + { + ICollection keys = _deployments.Keys; + return keys.ToArray(); + } + } + } + + public DeploymentInformation GetDeployment(String deploymentId) + { + using(_lock.Acquire()) + { + if (deploymentId == null) + { + return null; + } + return _deployments.Get(deploymentId); + } + } + + public void Dispose() + { + using(_lock.Acquire()) + { + _deployments.Clear(); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/deploy/EPDeploymentAdminImpl.cs b/NEsper.Core/NEsper.Core/core/deploy/EPDeploymentAdminImpl.cs new file mode 100755 index 000000000..6f46ca49e --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/deploy/EPDeploymentAdminImpl.cs @@ -0,0 +1,872 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.client.deploy; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.compat.threading; +using com.espertech.esper.core.service; +using com.espertech.esper.events; +using com.espertech.esper.filter; +using com.espertech.esper.util; + +using Module = com.espertech.esper.client.deploy.Module; + +namespace com.espertech.esper.core.deploy +{ + /// Deployment administrative implementation. + public class EPDeploymentAdminImpl : EPDeploymentAdminSPI + { + private static readonly ILog Log = + LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly EPAdministratorSPI _epService; + private readonly IReaderWriterLock _eventProcessingRwLock; + private readonly DeploymentStateService _deploymentStateService; + private readonly StatementEventTypeRef _statementEventTypeRef; + private readonly EventAdapterService _eventAdapterService; + private readonly StatementIsolationService _statementIsolationService; + private readonly FilterService _filterService; + private readonly TimeZoneInfo _timeZone; + private readonly ConfigurationEngineDefaults.UndeployRethrowPolicy _undeployRethrowPolicy; + private readonly ILockable _iLock = LockManager.CreateDefaultLock(); + + public EPDeploymentAdminImpl( + EPAdministratorSPI epService, + IReaderWriterLock eventProcessingRWLock, + DeploymentStateService deploymentStateService, + StatementEventTypeRef statementEventTypeRef, + EventAdapterService eventAdapterService, + StatementIsolationService statementIsolationService, + FilterService filterService, + TimeZoneInfo timeZone, + ConfigurationEngineDefaults.UndeployRethrowPolicy undeployRethrowPolicy) + { + _epService = epService; + _eventProcessingRwLock = eventProcessingRWLock; + _deploymentStateService = deploymentStateService; + _statementEventTypeRef = statementEventTypeRef; + _eventAdapterService = eventAdapterService; + _statementIsolationService = statementIsolationService; + _filterService = filterService; + _timeZone = timeZone; + _undeployRethrowPolicy = undeployRethrowPolicy; + } + + public Module Read(Stream stream, string uri) + { + if (Log.IsDebugEnabled) + { + Log.Debug("Reading module from input stream"); + } + return EPLModuleUtil.ReadInternal(stream, uri); + } + + public Module Read(FileInfo file) + { + var absolutePath = Path.GetFullPath(file.Name); + + Log.Debug("Reading resource '{0}'", absolutePath); + + using (var stream = File.OpenRead(absolutePath)) + { + return EPLModuleUtil.ReadInternal(stream, absolutePath); + } + } + + public Module Read(Uri url) + { + Log.Debug("Reading resource from url: {0}", url); + + using (var webClient = new WebClient()) + { + using (var stream = webClient.OpenRead(url)) + { + return EPLModuleUtil.ReadInternal(stream, url.ToString()); + } + } + } + + public Module Read(string resource) + { + if (Log.IsDebugEnabled) + { + Log.Debug("Reading resource '" + resource + "'"); + } + return EPLModuleUtil.ReadResource(resource, _eventAdapterService.EngineImportService); + } + + public DeploymentResult Deploy(Module module, DeploymentOptions options, string assignedDeploymentId) + { + using (_iLock.Acquire()) + { + if (_deploymentStateService.GetDeployment(assignedDeploymentId) != null) + { + throw new ArgumentException( + "Assigned deployment id '" + assignedDeploymentId + "' is already in use"); + } + return DeployInternal(module, options, assignedDeploymentId, DateTimeEx.GetInstance(_timeZone)); + } + } + + public DeploymentResult Deploy(Module module, DeploymentOptions options) + { + using (_iLock.Acquire()) + { + string deploymentId = _deploymentStateService.NextDeploymentId; + return DeployInternal(module, options, deploymentId, DateTimeEx.GetInstance(_timeZone)); + } + } + + private DeploymentResult DeployInternal( + Module module, + DeploymentOptions options, + string deploymentId, + DateTimeEx addedDate) + { + if (options == null) + { + options = new DeploymentOptions(); + } + + options.DeploymentLockStrategy.Acquire(_eventProcessingRwLock); + try + { + return DeployInternalLockTaken(module, options, deploymentId, addedDate); + } + finally + { + options.DeploymentLockStrategy.Release(_eventProcessingRwLock); + } + } + + private DeploymentResult DeployInternalLockTaken( + Module module, + DeploymentOptions options, + string deploymentId, + DateTimeEx addedDate) + { + + if (Log.IsDebugEnabled) + { + Log.Debug("Deploying module " + module); + } + IList imports; + if (module.Imports != null) + { + foreach (string imported in module.Imports) + { + if (Log.IsDebugEnabled) + { + Log.Debug("Adding import " + imported); + } + _epService.Configuration.AddImport(imported); + } + imports = new List(module.Imports); + } + else + { + imports = Collections.GetEmptyList(); + } + + if (options.IsCompile) + { + var exceptionsX = new List(); + foreach (ModuleItem item in module.Items) + { + if (item.IsCommentOnly) + { + continue; + } + + try + { + _epService.CompileEPL(item.Expression); + } + catch (Exception ex) + { + exceptionsX.Add(new DeploymentItemException(ex.Message, item.Expression, ex, item.LineNumber)); + } + } + + if (!exceptionsX.IsEmpty()) + { + throw BuildException("Compilation failed", module, exceptionsX); + } + } + + if (options.IsCompileOnly) + { + return null; + } + + var exceptions = new List(); + var statementNames = new List(); + var statements = new List(); + var eventTypesReferenced = new HashSet(); + + foreach (ModuleItem item in module.Items) + { + if (item.IsCommentOnly) + { + continue; + } + + string statementName = null; + Object userObject = null; + if (options.StatementNameResolver != null || options.StatementUserObjectResolver != null) + { + var ctx = new StatementDeploymentContext(item.Expression, module, item, deploymentId); + statementName = options.StatementNameResolver != null + ? options.StatementNameResolver.GetStatementName(ctx) + : null; + userObject = options.StatementUserObjectResolver != null + ? options.StatementUserObjectResolver.GetUserObject(ctx) + : null; + } + + try + { + EPStatement stmt; + if (options.IsolatedServiceProvider == null) + { + stmt = _epService.CreateEPL(item.Expression, statementName, userObject); + } + else + { + EPServiceProviderIsolated unit = + _statementIsolationService.GetIsolationUnit(options.IsolatedServiceProvider, -1); + stmt = unit.EPAdministrator.CreateEPL(item.Expression, statementName, userObject); + } + statementNames.Add(new DeploymentInformationItem(stmt.Name, stmt.Text)); + statements.Add(stmt); + + string[] types = _statementEventTypeRef.GetTypesForStatementName(stmt.Name); + if (types != null) + { + eventTypesReferenced.AddAll(types); + } + } + catch (EPException ex) + { + exceptions.Add(new DeploymentItemException(ex.Message, item.Expression, ex, item.LineNumber)); + if (options.IsFailFast) + { + break; + } + } + } + + if (!exceptions.IsEmpty()) + { + if (options.IsRollbackOnFail) + { + Log.Debug("Rolling back intermediate statements for deployment"); + foreach (EPStatement stmt in statements) + { + try + { + stmt.Dispose(); + } + catch (Exception ex) + { + Log.Debug("Failed to destroy created statement during rollback: " + ex.Message, ex); + } + } + EPLModuleUtil.UndeployTypes( + eventTypesReferenced, _statementEventTypeRef, _eventAdapterService, _filterService); + } + string text = "Deployment failed"; + if (options.IsValidateOnly) + { + text = "Validation failed"; + } + throw BuildException(text, module, exceptions); + } + + if (options.IsValidateOnly) + { + Log.Debug("Rolling back created statements for validate-only"); + foreach (EPStatement stmt in statements) + { + try + { + stmt.Dispose(); + } + catch (Exception ex) + { + Log.Debug("Failed to destroy created statement during rollback: " + ex.Message, ex); + } + } + EPLModuleUtil.UndeployTypes( + eventTypesReferenced, _statementEventTypeRef, _eventAdapterService, _filterService); + return null; + } + + DeploymentInformationItem[] deploymentInfoArr = statementNames.ToArray(); + var desc = new DeploymentInformation( + deploymentId, module, addedDate, DateTimeEx.GetInstance(_timeZone), deploymentInfoArr, + DeploymentState.DEPLOYED); + _deploymentStateService.AddUpdateDeployment(desc); + + if (Log.IsDebugEnabled) + { + Log.Debug("Module " + module + " was successfully deployed."); + } + return new DeploymentResult(desc.DeploymentId, statements.AsReadOnlyList(), imports); + } + + private DeploymentActionException BuildException( + string msg, + Module module, + List exceptions) + { + string message = msg; + if (module.Name != null) + { + message += " in module '" + module.Name + "'"; + } + if (module.Uri != null) + { + message += " in module url '" + module.Uri + "'"; + } + if (exceptions.Count > 0) + { + message += " in expression '" + GetAbbreviated(exceptions[0].Expression) + "' : " + + exceptions[0].Message; + } + return new DeploymentActionException(message, exceptions); + } + + private string GetAbbreviated(string expression) + { + if (expression.Length < 60) + { + return ReplaceNewline(expression); + } + string subtext = expression.Substring(0, 50) + "...(" + expression.Length + " chars)"; + return ReplaceNewline(subtext); + } + + private string ReplaceNewline(string text) + { + text = text.RegexReplaceAll("\\n", " "); + text = text.RegexReplaceAll("\\t", " "); + text = text.RegexReplaceAll("\\r", " "); + return text; + } + + public Module Parse(string eplModuleText) + { + return EPLModuleUtil.ParseInternal(eplModuleText, null); + } + + public UndeploymentResult UndeployRemove(string deploymentId) + { + using (_iLock.Acquire()) + { + return UndeployRemoveInternal(deploymentId, new UndeploymentOptions()); + } + } + + public UndeploymentResult UndeployRemove(string deploymentId, UndeploymentOptions undeploymentOptions) + { + using (_iLock.Acquire()) + { + return UndeployRemoveInternal( + deploymentId, undeploymentOptions ?? new UndeploymentOptions()); + } + } + + public UndeploymentResult Undeploy(string deploymentId) + { + using (_iLock.Acquire()) + { + return UndeployInternal(deploymentId, new UndeploymentOptions()); + } + } + + public UndeploymentResult Undeploy(string deploymentId, UndeploymentOptions undeploymentOptions) + { + using (_iLock.Acquire()) + { + return UndeployInternal( + deploymentId, undeploymentOptions ?? new UndeploymentOptions()); + } + } + + public string[] Deployments + { + get + { + using (_iLock.Acquire()) + { + return _deploymentStateService.Deployments; + } + } + } + + public DeploymentInformation GetDeployment(string deploymentId) + { + using (_iLock.Acquire()) + { + return _deploymentStateService.GetDeployment(deploymentId); + } + } + + public DeploymentInformation[] DeploymentInformation + { + get + { + using (_iLock.Acquire()) + { + return _deploymentStateService.AllDeployments; + } + } + } + + public DeploymentOrder GetDeploymentOrder(ICollection modules, DeploymentOrderOptions options) + { + using (_iLock.Acquire()) + { + if (options == null) + { + options = new DeploymentOrderOptions(); + } + string[] deployments = _deploymentStateService.Deployments; + + var proposedModules = new List(); + proposedModules.AddAll(modules); + + var availableModuleNames = new HashSet(); + foreach (Module proposedModule in proposedModules) + { + if (proposedModule.Name != null) + { + availableModuleNames.Add(proposedModule.Name); + } + } + + // Collect all uses-dependencies of existing modules + var usesPerModuleName = new Dictionary>(); + foreach (string deployment in deployments) + { + DeploymentInformation info = _deploymentStateService.GetDeployment(deployment); + if (info == null) + { + continue; + } + if ((info.Module.Name == null) || (info.Module.Uses == null)) + { + continue; + } + ISet usesSet = usesPerModuleName.Get(info.Module.Name); + if (usesSet == null) + { + usesSet = new HashSet(); + usesPerModuleName.Put(info.Module.Name, usesSet); + } + usesSet.AddAll(info.Module.Uses); + } + + // Collect uses-dependencies of proposed modules + foreach (Module proposedModule in proposedModules) + { + + // check uses-dependency is available + if (options.IsCheckUses) + { + if (proposedModule.Uses != null) + { + foreach (string uses in proposedModule.Uses) + { + if (availableModuleNames.Contains(uses)) + { + continue; + } + if (IsDeployed(uses)) + { + continue; + } + string message = "Module-dependency not found"; + if (proposedModule.Name != null) + { + message += " as declared by module '" + proposedModule.Name + "'"; + } + message += " for uses-declaration '" + uses + "'"; + throw new DeploymentOrderException(message); + } + } + } + + if ((proposedModule.Name == null) || (proposedModule.Uses == null)) + { + continue; + } + ISet usesSet = usesPerModuleName.Get(proposedModule.Name); + if (usesSet == null) + { + usesSet = new HashSet(); + usesPerModuleName.Put(proposedModule.Name, usesSet); + } + usesSet.AddAll(proposedModule.Uses); + } + + var proposedModuleNames = new Dictionary>().WithNullSupport(); + int count = 0; + foreach (Module proposedModule in proposedModules) + { + ISet moduleNumbers = proposedModuleNames.Get(proposedModule.Name); + if (moduleNumbers == null) + { + moduleNumbers = new SortedSet(); + proposedModuleNames.Put(proposedModule.Name, moduleNumbers); + } + moduleNumbers.Add(count); + count++; + } + + var graph = new DependencyGraph(proposedModules.Count, false); + int fromModule = 0; + foreach (Module proposedModule in proposedModules) + { + if ((proposedModule.Uses == null) || (proposedModule.Uses.IsEmpty())) + { + fromModule++; + continue; + } + var dependentModuleNumbers = new SortedSet(); + foreach (string use in proposedModule.Uses) + { + ISet moduleNumbers = proposedModuleNames.Get(use); + if (moduleNumbers == null) + { + continue; + } + dependentModuleNumbers.AddAll(moduleNumbers); + } + dependentModuleNumbers.Remove(fromModule); + graph.AddDependency(fromModule, dependentModuleNumbers); + fromModule++; + } + + if (options.IsCheckCircularDependency) + { + var circular = graph.FirstCircularDependency; + if (circular != null) + { + string message = ""; + string delimiter = ""; + foreach (int i in circular) + { + message += delimiter; + message += "module '" + proposedModules[i].Name + "'"; + delimiter = " uses (depends on) "; + } + throw new DeploymentOrderException( + "Circular dependency detected in module uses-relationships: " + message); + } + } + + var reverseDeployList = new List(); + var ignoreList = new HashSet(); + while (ignoreList.Count < proposedModules.Count) + { + // secondary sort according to the order of listing + ICollection rootNodes = new SortedSet( + new StandardComparer((o1, o2) => -1*o1.CompareTo(o2))); + + rootNodes.AddAll(graph.GetRootNodes(ignoreList)); + + if (rootNodes.IsEmpty()) + { + // circular dependency could cause this + for (int i = 0; i < proposedModules.Count; i++) + { + if (!ignoreList.Contains(i)) + { + rootNodes.Add(i); + break; + } + } + } + + foreach (int root in rootNodes) + { + ignoreList.Add(root); + reverseDeployList.Add(proposedModules[root]); + } + } + + reverseDeployList.Reverse(); + return new DeploymentOrder(reverseDeployList); + } + } + + public bool IsDeployed(string moduleName) + { + using (_iLock.Acquire()) + { + DeploymentInformation[] infos = _deploymentStateService.AllDeployments; + if (infos == null) + { + return false; + } + foreach (DeploymentInformation info in infos) + { + if ((info.Module.Name != null) && (info.Module.Name.Equals(moduleName))) + { + return info.State == DeploymentState.DEPLOYED; + } + } + return false; + } + } + + public DeploymentResult ReadDeploy(Stream stream, string moduleURI, string moduleArchive, Object userObject) + { + using (_iLock.Acquire()) + { + Module module = EPLModuleUtil.ReadInternal(stream, moduleURI); + return DeployQuick(module, moduleURI, moduleArchive, userObject); + } + } + + public DeploymentResult ReadDeploy(string resource, string moduleURI, string moduleArchive, Object userObject) + { + using (_iLock.Acquire()) + { + Module module = Read(resource); + return DeployQuick(module, moduleURI, moduleArchive, userObject); + } + } + + public DeploymentResult ParseDeploy(string eplModuleText) + { + using (_iLock.Acquire()) + { + return ParseDeploy(eplModuleText, null, null, null); + } + } + + public DeploymentResult ParseDeploy(string buffer, string moduleURI, string moduleArchive, Object userObject) + { + using (_iLock.Acquire()) + { + Module module = EPLModuleUtil.ParseInternal(buffer, moduleURI); + return DeployQuick(module, moduleURI, moduleArchive, userObject); + } + } + + public void Add(Module module, string assignedDeploymentId) + { + using (_iLock.Acquire()) + { + if (_deploymentStateService.GetDeployment(assignedDeploymentId) != null) + { + throw new ArgumentException( + "Assigned deployment id '" + assignedDeploymentId + "' is already in use"); + } + AddInternal(module, assignedDeploymentId); + } + } + + public string Add(Module module) + { + using (_iLock.Acquire()) + { + string deploymentId = _deploymentStateService.NextDeploymentId; + AddInternal(module, deploymentId); + return deploymentId; + } + } + + private void AddInternal(Module module, string deploymentId) + { + + var desc = new DeploymentInformation( + deploymentId, module, DateTimeEx.GetInstance(_timeZone), DateTimeEx.GetInstance(_timeZone), + new DeploymentInformationItem[0], DeploymentState.UNDEPLOYED); + _deploymentStateService.AddUpdateDeployment(desc); + } + + public DeploymentResult Deploy(string deploymentId, DeploymentOptions options) + { + using (_iLock.Acquire()) + { + DeploymentInformation info = _deploymentStateService.GetDeployment(deploymentId); + if (info == null) + { + throw new DeploymentNotFoundException("Deployment by id '" + deploymentId + "' could not be found"); + } + if (info.State == DeploymentState.DEPLOYED) + { + throw new DeploymentStateException( + "Module by deployment id '" + deploymentId + "' is already in deployed state"); + } + GetDeploymentOrder(Collections.SingletonList(info.Module), null); + return DeployInternal(info.Module, options, deploymentId, info.AddedDate); + } + } + + public void Remove(string deploymentId) + { + using (_iLock.Acquire()) + { + DeploymentInformation info = _deploymentStateService.GetDeployment(deploymentId); + if (info == null) + { + throw new DeploymentNotFoundException("Deployment by id '" + deploymentId + "' could not be found"); + } + if (info.State == DeploymentState.DEPLOYED) + { + throw new DeploymentStateException( + "Deployment by id '" + deploymentId + "' is in deployed state, please undeploy first"); + } + _deploymentStateService.Remove(deploymentId); + } + } + + private UndeploymentResult UndeployRemoveInternal(string deploymentId, UndeploymentOptions options) + { + using (_iLock.Acquire()) + { + DeploymentInformation info = _deploymentStateService.GetDeployment(deploymentId); + if (info == null) + { + throw new DeploymentNotFoundException("Deployment by id '" + deploymentId + "' could not be found"); + } + + UndeploymentResult result; + if (info.State == DeploymentState.DEPLOYED) + { + result = UndeployRemoveInternal(info, options); + } + else + { + result = new UndeploymentResult( + deploymentId, Collections.GetEmptyList()); + } + _deploymentStateService.Remove(deploymentId); + return result; + } + } + + private UndeploymentResult UndeployInternal(string deploymentId, UndeploymentOptions undeploymentOptions) + { + undeploymentOptions.DeploymentLockStrategy.Acquire(_eventProcessingRwLock); + try + { + return UndeployInternalLockTaken(deploymentId, undeploymentOptions); + } + finally + { + undeploymentOptions.DeploymentLockStrategy.Release(_eventProcessingRwLock); + } + } + + private UndeploymentResult UndeployInternalLockTaken( + string deploymentId, + UndeploymentOptions undeploymentOptions) + { + DeploymentInformation info = _deploymentStateService.GetDeployment(deploymentId); + if (info == null) + { + throw new DeploymentNotFoundException("Deployment by id '" + deploymentId + "' could not be found"); + } + if (info.State == DeploymentState.UNDEPLOYED) + { + throw new DeploymentStateException( + "Deployment by id '" + deploymentId + "' is already in undeployed state"); + } + + UndeploymentResult result = UndeployRemoveInternal(info, undeploymentOptions); + var updated = new DeploymentInformation( + deploymentId, info.Module, info.AddedDate, DateTimeEx.GetInstance(_timeZone), + new DeploymentInformationItem[0], DeploymentState.UNDEPLOYED); + _deploymentStateService.AddUpdateDeployment(updated); + return result; + } + + private UndeploymentResult UndeployRemoveInternal( + DeploymentInformation info, + UndeploymentOptions undeploymentOptions) + { + var reverted = new DeploymentInformationItem[info.Items.Length]; + for (int i = 0; i < info.Items.Length; i++) + { + reverted[i] = info.Items[info.Items.Length - 1 - i]; + } + + var revertedStatements = new List(); + if (undeploymentOptions.IsDestroyStatements) + { + var referencedTypes = new HashSet(); + + Exception firstExceptionEncountered = null; + + foreach (DeploymentInformationItem item in reverted) + { + EPStatement statement = _epService.GetStatement(item.StatementName); + if (statement == null) + { + Log.Debug("Deployment id '" + info.DeploymentId + "' statement name '" + item + "' not found"); + continue; + } + referencedTypes.AddAll(_statementEventTypeRef.GetTypesForStatementName(statement.Name)); + if (statement.IsDisposed) + { + continue; + } + try + { + statement.Dispose(); + } + catch (Exception ex) + { + Log.Warn("Unexpected exception destroying statement: " + ex.Message, ex); + if (firstExceptionEncountered == null) + { + firstExceptionEncountered = ex; + } + } + revertedStatements.Add(item); + } + EPLModuleUtil.UndeployTypes( + referencedTypes, _statementEventTypeRef, _eventAdapterService, _filterService); + revertedStatements.Reverse(); + + if (firstExceptionEncountered != null && + _undeployRethrowPolicy == + ConfigurationEngineDefaults.UndeployRethrowPolicy.RETHROW_FIRST) + { + throw firstExceptionEncountered; + } + } + + return new UndeploymentResult(info.DeploymentId, revertedStatements); + } + + private DeploymentResult DeployQuick(Module module, string moduleURI, string moduleArchive, Object userObject) + { + module.Uri = moduleURI; + module.ArchiveName = moduleArchive; + module.UserObject = userObject; + GetDeploymentOrder(Collections.SingletonList(module), null); + return Deploy(module, null); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/deploy/EPDeploymentAdminSPI.cs b/NEsper.Core/NEsper.Core/core/deploy/EPDeploymentAdminSPI.cs new file mode 100755 index 000000000..c02363370 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/deploy/EPDeploymentAdminSPI.cs @@ -0,0 +1,16 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client.deploy; + +namespace com.espertech.esper.core.deploy +{ + public interface EPDeploymentAdminSPI : EPDeploymentAdmin + { + } +} diff --git a/NEsper.Core/NEsper.Core/core/deploy/EPLModuleParseItem.cs b/NEsper.Core/NEsper.Core/core/deploy/EPLModuleParseItem.cs new file mode 100755 index 000000000..aa05658fe --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/deploy/EPLModuleParseItem.cs @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.core.deploy +{ + /// Item parsing an EPL module file. + public class EPLModuleParseItem + { + /// Ctor. + /// EPL + /// line number + /// start character number total file + /// end character number + public EPLModuleParseItem(String expression, int lineNum, int startChar, int endChar) + { + Expression = expression; + LineNum = lineNum; + StartChar = startChar; + EndChar = endChar; + } + + /// Returns line number of expression. + /// line number + public int LineNum { get; private set; } + + /// Returns the expression. + /// expression + public string Expression { get; private set; } + + /// Returns the position of the start character. + /// start char position + public int StartChar { get; private set; } + + /// Returns the position of the end character. + /// end char position + public int EndChar { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/core/deploy/EPLModuleUtil.cs b/NEsper.Core/NEsper.Core/core/deploy/EPLModuleUtil.cs new file mode 100755 index 000000000..356bb5c18 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/deploy/EPLModuleUtil.cs @@ -0,0 +1,543 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; + +using Antlr4.Runtime; + +using com.espertech.esper.client; +using com.espertech.esper.client.deploy; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.generated; +using com.espertech.esper.epl.parse; +using com.espertech.esper.events; +using com.espertech.esper.filter; + +namespace com.espertech.esper.core.deploy +{ + using Module = client.deploy.Module; + + public class EPLModuleUtil + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// Newline character. + public static String Newline = Environment.NewLine; + + public static Module ReadInternal(Stream stream, String resourceName) + { + var reader = new StreamReader(stream); + var writer = new StringWriter(); + + string strLine; + while ((strLine = reader.ReadLine()) != null) + { + writer.WriteLine(strLine); + } + stream.Close(); + + return ParseInternal(writer.ToString(), resourceName); + } + + public static Module ParseInternal(String buffer, String resourceName) + { + var semicolonSegments = Parse(buffer); + var nodes = new List(); + foreach (EPLModuleParseItem segment in semicolonSegments) + { + nodes.Add(GetModule(segment, resourceName)); + } + + String moduleName = null; + int count = 0; + foreach (ParseNode node in nodes) + { + if (node is ParseNodeComment) + { + continue; + } + if (node is ParseNodeModule) + { + if (moduleName != null) + { + throw new ParseException( + "Duplicate use of the 'module' keyword for resource '" + resourceName + "'"); + } + if (count > 0) + { + throw new ParseException( + "The 'module' keyword must be the first declaration in the module file for resource '" + + resourceName + "'"); + } + moduleName = ((ParseNodeModule)node).ModuleName; + } + count++; + } + + ICollection uses = new LinkedHashSet(); + ICollection imports = new LinkedHashSet(); + count = 0; + foreach (ParseNode node in nodes) + { + if ((node is ParseNodeComment) || (node is ParseNodeModule)) + { + continue; + } + const string message = "The 'uses' and 'import' keywords must be the first declaration in the module file or follow the 'module' declaration"; + if (node is ParseNodeUses) + { + if (count > 0) + { + throw new ParseException(message); + } + uses.Add(((ParseNodeUses)node).Uses); + continue; + } + if (node is ParseNodeImport) + { + if (count > 0) + { + throw new ParseException(message); + } + imports.Add(((ParseNodeImport)node).Imported); + continue; + } + count++; + } + + var items = new List(); + foreach (ParseNode node in nodes) + { + if ((node is ParseNodeComment) || (node is ParseNodeExpression)) + { + bool isComments = (node is ParseNodeComment); + items.Add(new ModuleItem(node.Item.Expression, isComments, node.Item.LineNum, node.Item.StartChar, node.Item.EndChar)); + } + } + + return new Module(moduleName, resourceName, uses, imports, items, buffer); + } + + public static IList UndeployTypes(ICollection referencedTypes, + StatementEventTypeRef statementEventTypeRef, + EventAdapterService eventAdapterService, + FilterService filterService) + { + var undeployedTypes = new List(); + foreach (String typeName in referencedTypes) + { + bool typeInUse = statementEventTypeRef.IsInUse(typeName); + if (typeInUse) + { + if (Log.IsDebugEnabled) + { + Log.Debug("Event type '" + typeName + "' is in use, not removing type"); + } + continue; + } + + if (Log.IsDebugEnabled) + { + Log.Debug("Event type '" + typeName + "' is no longer in use, removing type"); + } + var type = eventAdapterService.GetEventTypeByName(typeName); + if (type != null) + { + var spi = (EventTypeSPI)type; + if (!spi.Metadata.IsApplicationPreConfigured) + { + eventAdapterService.RemoveType(typeName); + undeployedTypes.Add(spi); + filterService.RemoveType(spi); + } + } + } + return undeployedTypes; + } + + public static ParseNode GetModule(EPLModuleParseItem item, String resourceName) + { + Antlr4.Runtime.ICharStream foo; + + var input = new NoCaseSensitiveStream(item.Expression); + + var lex = ParseHelper.NewLexer(input); + var tokenStream = new CommonTokenStream(lex); + tokenStream.Fill(); + + var tokens = tokenStream.GetTokens(); + var beginIndex = 0; + var isMeta = false; + var isModule = false; + var isUses = false; + var isExpression = false; + + while (beginIndex < tokens.Count) + { + var t = tokens[beginIndex]; + if (t.Type == EsperEPL2GrammarParser.Eof) + { + break; + } + + if ((t.Type == EsperEPL2GrammarParser.WS) || + (t.Type == EsperEPL2GrammarParser.SL_COMMENT) || + (t.Type == EsperEPL2GrammarParser.ML_COMMENT)) + { + beginIndex++; + continue; + } + var tokenText = t.Text.Trim().ToLower(); + switch (tokenText) + { + case "module": + isModule = true; + isMeta = true; + break; + case "uses": + isUses = true; + isMeta = true; + break; + case "import": + isMeta = true; + break; + default: + isExpression = true; + break; + } + beginIndex++; + beginIndex++; // skip space + break; + } + + if (isExpression) + { + return new ParseNodeExpression(item); + } + if (!isMeta) + { + return new ParseNodeComment(item); + } + + // check meta tag (module, uses, import) + var buffer = new StringWriter(); + for (int i = beginIndex; i < tokens.Count; i++) + { + var t = tokens[i]; + if (t.Type == EsperEPL2GrammarParser.Eof) + { + break; + } + + if ((t.Type != EsperEPL2GrammarParser.IDENT) && + (t.Type != EsperEPL2GrammarParser.DOT) && + (t.Type != EsperEPL2GrammarParser.STAR) && + (!t.Text.Matches("[a-zA-Z]*"))) + { + throw GetMessage(isModule, isUses, resourceName, t.Type); + } + buffer.Write(t.Text.Trim()); + } + + String result = buffer.ToString().Trim(); + if (result.Length == 0) + { + throw GetMessage(isModule, isUses, resourceName, -1); + } + + if (isModule) + { + return new ParseNodeModule(item, result); + } + else if (isUses) + { + return new ParseNodeUses(item, result); + } + return new ParseNodeImport(item, result); + } + + private static ParseException GetMessage(bool module, bool uses, String resourceName, int type) + { + String message = "Keyword '"; + if (module) + { + message += "module"; + } + else if (uses) + { + message += "uses"; + } + else + { + message += "import"; + } + message += "' must be followed by a name or package name (set of names separated by dots) for resource '" + + resourceName + "'"; + + if (type != -1) + { + String tokenName = EsperEPL2GrammarParser.GetLexerTokenParaphrases().Get(type); + if (tokenName == null) + { + tokenName = EsperEPL2GrammarParser.GetParserTokenParaphrases().Get(type); + } + if (tokenName != null) + { + message += ", unexpected reserved keyword " + tokenName + " was encountered as part of the name"; + } + } + return new ParseException(message); + } + + public static IList Parse(String module) + { + ICharStream input; + try + { + input = new NoCaseSensitiveStream(module); + } + catch (IOException ex) + { + Log.Error("Exception reading module expression: " + ex.Message, ex); + return null; + } + + var lex = ParseHelper.NewLexer(input); + var tokens = new CommonTokenStream(lex); + + try + { + tokens.Fill(); + } + catch (Exception ex) + { + String message = "Unexpected exception recognizing module text"; + if (ex is LexerNoViableAltException) + { + if (ParseHelper.HasControlCharacters(module)) + { + message = "Unrecognized control characters found in text, failed to parse text"; + } + else + { + message += ", recognition failed for " + ex; + } + } + else if (ex is RecognitionException) + { + var recog = (RecognitionException)ex; + message += ", recognition failed for " + recog; + } + else if (!string.IsNullOrWhiteSpace(ex.Message)) + { + message += ": " + ex.Message; + } + message += " [" + module + "]"; + Log.Error(message, ex); + throw new ParseException(message); + } + + var statements = new List(); + var current = new StringWriter(); + int? lineNum = null; + int charPosStart = 0; + int charPos = 0; + var tokenList = tokens.GetTokens(); + var skippedSemicolonIndexes = GetSkippedSemicolons(tokenList); + int index = -1; + + foreach (var token in tokenList.TakeWhile(t => t.Type != EsperEPL2GrammarParser.Eof)) + { + index++; + var t = token; + bool semi = t.Type == EsperEPL2GrammarParser.SEMI && !skippedSemicolonIndexes.Contains(index); + if (semi) + { + if (current.ToString().Trim().Length > 0) + { + statements.Add( + new EPLModuleParseItem( + current.ToString().Trim(), lineNum ?? 0, charPosStart, charPos)); + lineNum = null; + } + current = new StringWriter(); + } + else + { + if ((lineNum == null) && (t.Type != EsperEPL2GrammarParser.WS)) + { + lineNum = t.Line; + charPosStart = charPos; + } + if (t.Type != EsperEPL2GrammarLexer.Eof) + { + current.Write(t.Text); + charPos += t.Text.Length; + } + } + } + + if (!string.IsNullOrWhiteSpace(current.ToString())) + { + statements.Add(new EPLModuleParseItem(current.ToString().Trim(), lineNum ?? 0, 0, 0)); + } + return statements; + } + + public static Module ReadFile(FileInfo file) + { + using (var stream = File.OpenRead(file.ToString())) + { + return ReadInternal(stream, Path.GetFullPath(file.ToString())); + } + } + + public static Module ReadResource(String resource, EngineImportService engineImportService) + { + Stream stream = null; + + var stripped = resource.StartsWith("/") ? resource.Substring(1) : resource; + var classLoader = engineImportService.GetClassLoader(); + if (classLoader != null) + { + stream = classLoader.GetResourceAsStream(stripped); + } + if (stream == null) + { + stream = ResourceManager.GetResourceAsStream(stripped); + } + if (stream == null) + { + throw new IOException("Failed to find resource '" + resource + "' in classpath"); + } + + try + { + return ReadInternal(stream, resource); + } + finally + { + try + { + stream.Close(); + } + catch (IOException e) + { + Log.Debug("Error closing input stream", e); + } + } + } + + /// Find expression declarations and skip semicolon content between square brackets for scripts + private static ICollection GetSkippedSemicolons(IList tokens) + { + ICollection result = null; + + int index = -1; + foreach (var token in tokens) + { + index++; + var t = (IToken)token; + if (t.Type == EsperEPL2GrammarParser.EXPRESSIONDECL) + { + if (result == null) + { + result = new HashSet(); + } + GetSkippedSemicolonsBetweenSquareBrackets(index, tokens, result); + } + } + + return result ?? Collections.GetEmptySet(); + } + + /// + /// Find content between square brackets + /// + /// The index. + /// The tokens. + /// The result. + private static void GetSkippedSemicolonsBetweenSquareBrackets(int index, IList tokens, ICollection result) + { + // Handle EPL expression "{text}" and script expression "[text]" + int indexFirstCurly = IndexFirstToken(index, tokens, EsperEPL2GrammarParser.LCURLY); + int indexFirstSquare = IndexFirstToken(index, tokens, EsperEPL2GrammarParser.LBRACK); + if (indexFirstSquare == -1) + { + return; + } + if (indexFirstCurly != -1 && indexFirstCurly < indexFirstSquare) + { + return; + } + int indexCloseSquare = FindEndSquareBrackets(indexFirstSquare, tokens); + if (indexCloseSquare == -1) + { + return; + } + + int current = indexFirstSquare; + while (current < indexCloseSquare) + { + IToken t = tokens[current]; + if (t.Type == EsperEPL2GrammarParser.SEMI) + { + result.Add(current); + } + current++; + } + } + + private static int FindEndSquareBrackets(int startIndex, IList tokens) + { + int index = startIndex + 1; + int squareBracketDepth = 0; + while (index < tokens.Count) + { + IToken t = tokens[index]; + if (t.Type == EsperEPL2GrammarParser.RBRACK) + { + if (squareBracketDepth == 0) + { + return index; + } + squareBracketDepth--; + } + if (t.Type == EsperEPL2GrammarParser.LBRACK) + { + squareBracketDepth++; + } + index++; + } + return -1; + } + + private static int IndexFirstToken(int startIndex, IList tokens, int tokenType) + { + int index = startIndex; + while (index < tokens.Count) + { + IToken t = tokens[index]; + if (t.Type == tokenType) + { + return index; + } + index++; + } + return -1; + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/deploy/ParseNode.cs b/NEsper.Core/NEsper.Core/core/deploy/ParseNode.cs new file mode 100755 index 000000000..dbf08840a --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/deploy/ParseNode.cs @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.core.deploy +{ + public abstract class ParseNode + { + public EPLModuleParseItem Item { get; private set; } + + protected ParseNode(EPLModuleParseItem item) + { + Item = item; + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/deploy/ParseNodeComment.cs b/NEsper.Core/NEsper.Core/core/deploy/ParseNodeComment.cs new file mode 100755 index 000000000..e015303e9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/deploy/ParseNodeComment.cs @@ -0,0 +1,18 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.core.deploy +{ + public class ParseNodeComment : ParseNode + { + public ParseNodeComment(EPLModuleParseItem item) + : base(item) + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/deploy/ParseNodeExpression.cs b/NEsper.Core/NEsper.Core/core/deploy/ParseNodeExpression.cs new file mode 100755 index 000000000..bf3822e52 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/deploy/ParseNodeExpression.cs @@ -0,0 +1,19 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.core.deploy +{ + public class ParseNodeExpression : ParseNode + { + public ParseNodeExpression(EPLModuleParseItem item) + + : base(item) + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/deploy/ParseNodeImport.cs b/NEsper.Core/NEsper.Core/core/deploy/ParseNodeImport.cs new file mode 100755 index 000000000..6018177c0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/deploy/ParseNodeImport.cs @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.core.deploy +{ + public class ParseNodeImport : ParseNode + { + public ParseNodeImport(EPLModuleParseItem item, String imported) + : base(item) + { + Imported = imported; + } + + public string Imported { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/core/deploy/ParseNodeModule.cs b/NEsper.Core/NEsper.Core/core/deploy/ParseNodeModule.cs new file mode 100755 index 000000000..437db67e8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/deploy/ParseNodeModule.cs @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.core.deploy +{ + public class ParseNodeModule : ParseNode + { + public ParseNodeModule(EPLModuleParseItem item, String moduleName) + : base(item) + { + ModuleName = moduleName; + } + + public string ModuleName { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/core/deploy/ParseNodeUses.cs b/NEsper.Core/NEsper.Core/core/deploy/ParseNodeUses.cs new file mode 100755 index 000000000..49da23f70 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/deploy/ParseNodeUses.cs @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.core.deploy +{ + public class ParseNodeUses : ParseNode + { + public ParseNodeUses(EPLModuleParseItem item, String uses) + : base(item) + { + Uses = uses; + } + + public string Uses { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/ActiveDirectory.cs b/NEsper.Core/NEsper.Core/core/service/ActiveDirectory.cs new file mode 100755 index 000000000..f9fb2d3bd --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/ActiveDirectory.cs @@ -0,0 +1,90 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; + +namespace com.espertech.esper.core +{ + public class ActiveDirectory : Directory + { + /// + /// Lookup an object by name. + /// + /// + /// + + public Object Lookup(string name) + { + throw new NotImplementedException() ; + } + + /// + /// Bind an object to a name. Throws an exception if + /// the name is already bound. + /// + /// + /// + + public void Bind(string name, Object obj) + { + throw new NotImplementedException() ; + } + + /// + /// Bind an object to a name. If the object is already + /// bound, rebind it. + /// + /// + /// + + public void Rebind(string name, Object obj) + { + throw new NotImplementedException() ; + } + + /// + /// Unbind the object at the given name. + /// + /// + + public void Unbind(string name) + { + throw new NotImplementedException() ; + } + + /// + /// Rename the object at oldName with newName. + /// + /// + /// + + public void Rename(String oldName, String newName) + { + throw new NotImplementedException() ; + } + + /// + /// Enumerates the names bound in the named context. + /// + /// + /// + + public IEnumerator List(string name) + { + throw new NotImplementedException() ; + } + + public void Dispose() + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/ConfigurationOperationsImpl.cs b/NEsper.Core/NEsper.Core/core/service/ConfigurationOperationsImpl.cs new file mode 100755 index 000000000..67053c2db --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/ConfigurationOperationsImpl.cs @@ -0,0 +1,645 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.start; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.metric; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.epl.util; +using com.espertech.esper.epl.variable; +using com.espertech.esper.events; +using com.espertech.esper.events.vaevent; +using com.espertech.esper.events.xml; +using com.espertech.esper.filter; +using com.espertech.esper.pattern.pool; +using com.espertech.esper.rowregex; +using com.espertech.esper.util; + +namespace com.espertech.esper.core.service +{ + /// Provides runtime engine configuration operations. + public class ConfigurationOperationsImpl : ConfigurationOperations + { + private readonly EventAdapterService _eventAdapterService; + private readonly EventTypeIdGenerator _eventTypeIdGenerator; + private readonly EngineImportService _engineImportService; + private readonly VariableService _variableService; + private readonly EngineSettingsService _engineSettingsService; + private readonly ValueAddEventService _valueAddEventService; + private readonly MetricReportingService _metricReportingService; + private readonly StatementEventTypeRef _statementEventTypeRef; + private readonly StatementVariableRef _statementVariableRef; + private readonly PluggableObjectCollection _plugInViews; + private readonly FilterService _filterService; + private readonly PatternSubexpressionPoolEngineSvc _patternSubexpressionPoolSvc; + private readonly MatchRecognizeStatePoolEngineSvc _matchRecognizeStatePoolEngineSvc; + private readonly TableService _tableService; + private readonly IDictionary _transientConfiguration; + + public ConfigurationOperationsImpl(EventAdapterService eventAdapterService, + EventTypeIdGenerator eventTypeIdGenerator, + EngineImportService engineImportService, + VariableService variableService, + EngineSettingsService engineSettingsService, + ValueAddEventService valueAddEventService, + MetricReportingService metricReportingService, + StatementEventTypeRef statementEventTypeRef, + StatementVariableRef statementVariableRef, + PluggableObjectCollection plugInViews, + FilterService filterService, + PatternSubexpressionPoolEngineSvc patternSubexpressionPoolSvc, + MatchRecognizeStatePoolEngineSvc matchRecognizeStatePoolEngineSvc, + TableService tableService, + IDictionary transientConfiguration) { + _eventAdapterService = eventAdapterService; + _eventTypeIdGenerator = eventTypeIdGenerator; + _engineImportService = engineImportService; + _variableService = variableService; + _engineSettingsService = engineSettingsService; + _valueAddEventService = valueAddEventService; + _metricReportingService = metricReportingService; + _statementEventTypeRef = statementEventTypeRef; + _statementVariableRef = statementVariableRef; + _plugInViews = plugInViews; + _filterService = filterService; + _patternSubexpressionPoolSvc = patternSubexpressionPoolSvc; + _matchRecognizeStatePoolEngineSvc = matchRecognizeStatePoolEngineSvc; + _tableService = tableService; + _transientConfiguration = transientConfiguration; + } + + public void AddEventTypeAutoName(string javaPackageName) { + _eventAdapterService.AddAutoNamePackage(javaPackageName); + } + + public void AddPlugInView(string @namespace, string name, string viewFactoryClass) { + var configurationPlugInView = new ConfigurationPlugInView(); + configurationPlugInView.Namespace = @namespace; + configurationPlugInView.Name = name; + configurationPlugInView.FactoryClassName = viewFactoryClass; + _plugInViews.AddViews( + Collections.SingletonList(configurationPlugInView), + Collections.GetEmptyList(), + _engineImportService); + } + + public void AddPlugInAggregationMultiFunction(ConfigurationPlugInAggregationMultiFunction config) { + try { + _engineImportService.AddAggregationMultiFunction(config); + } catch (EngineImportException e) { + throw new ConfigurationException(e.Message, e); + } + } + + public void AddPlugInAggregationFunctionFactory(string functionName, string aggregationFactoryClassName) { + try { + var desc = new ConfigurationPlugInAggregationFunction(functionName, aggregationFactoryClassName); + _engineImportService.AddAggregation(functionName, desc); + } catch (EngineImportException e) { + throw new ConfigurationException(e.Message, e); + } + } + + public void AddPlugInSingleRowFunction(string functionName, string className, string methodName) { + InternalAddPlugInSingleRowFunction(functionName, className, methodName, ValueCacheEnum.DISABLED, FilterOptimizableEnum.ENABLED, false, null); + } + + public void AddPlugInSingleRowFunction(string functionName, string className, string methodName, ValueCacheEnum valueCache) { + InternalAddPlugInSingleRowFunction(functionName, className, methodName, valueCache, FilterOptimizableEnum.ENABLED, false, null); + } + + public void AddPlugInSingleRowFunction(string functionName, string className, string methodName, FilterOptimizableEnum filterOptimizable) { + InternalAddPlugInSingleRowFunction(functionName, className, methodName, ValueCacheEnum.DISABLED, filterOptimizable, false, null); + } + + public void AddPlugInSingleRowFunction(string functionName, string className, string methodName, ValueCacheEnum valueCache, FilterOptimizableEnum filterOptimizable, bool rethrowExceptions) { + InternalAddPlugInSingleRowFunction(functionName, className, methodName, valueCache, filterOptimizable, rethrowExceptions, null); + } + + public void AddPlugInSingleRowFunction(ConfigurationPlugInSingleRowFunction config) + { + InternalAddPlugInSingleRowFunction( + config.Name, + config.FunctionClassName, + config.FunctionMethodName, + config.ValueCache, + config.FilterOptimizable, + config.IsRethrowExceptions, + config.EventTypeName); + } + + private void InternalAddPlugInSingleRowFunction(string functionName, string className, string methodName, ValueCacheEnum valueCache, FilterOptimizableEnum filterOptimizable, bool rethrowExceptions, string optionalEventTypeName) { + try { + _engineImportService.AddSingleRow(functionName, className, methodName, valueCache, filterOptimizable, rethrowExceptions, optionalEventTypeName); + } catch (EngineImportException e) { + throw new ConfigurationException(e.Message, e); + } + } + + public void AddAnnotationImport(string importName, string assemblyNameOrFile) + { + try + { + _engineImportService.AddAnnotationImport(new AutoImportDesc(importName, assemblyNameOrFile)); + } + catch (EngineImportException e) + { + throw new ConfigurationException(e.Message, e); + } + } + + public void AddAnnotationImport(string importName) + { + AddAnnotationImport(importName, null); + } + + public void AddAnnotationImport(Type autoImport) + { + AddAnnotationImport(autoImport.FullName, autoImport.AssemblyQualifiedName); + } + + public void AddAnnotationImport(bool importNamespace) + { + if (importNamespace) + { + AddAnnotationImport(typeof(T).Namespace, typeof(T).Assembly.FullName); + } + else + { + AddAnnotationImport(typeof(T).FullName, typeof(T).Assembly.FullName); + } + } + + public void AddImport(string importName, string assemblyNameOrFile) + { + try + { + _engineImportService.AddImport(new AutoImportDesc(importName, assemblyNameOrFile)); + } + catch (EngineImportException e) + { + throw new ConfigurationException(e.Message, e); + } + } + + public void AddImport(string importName) + { + string[] importParts = importName.Split(','); + if (importParts.Length == 1) + { + AddImport(importName, null); + } + else + { + AddImport(importParts[0], importParts[1]); + } + } + + public void AddImport(Type importClass) + { + if (importClass.IsNested) + AddImport(importClass.DeclaringType.FullName, null); + else + AddImport(importClass.Namespace, null); + } + + public void AddImport() + { + AddImport(typeof(T)); + } + + public void AddNamespaceImport() + { + var importClass = typeof(T); + if (importClass.IsNested) + AddImport(importClass.DeclaringType.FullName, null); + else + AddImport(importClass.Namespace, null); + } + + public bool IsEventTypeExists(string eventTypeName) + { + return _eventAdapterService.GetEventTypeByName(eventTypeName) != null; + } + + public void AddEventType(String eventTypeName) + { + AddEventType(eventTypeName, typeof(T).AssemblyQualifiedName); + } + + public void AddEventType() + { + AddEventType(typeof(T).Name, typeof(T).AssemblyQualifiedName); + } + + public void AddEventType(string eventTypeName, string eventTypeTypeName) + { + CheckTableExists(eventTypeName); + try { + _eventAdapterService.AddBeanType(eventTypeName, eventTypeTypeName, false, false, true, true); + } catch (EventAdapterException t) { + throw new ConfigurationException(t.Message, t); + } + } + + public void AddEventType(string eventTypeName, Type eventType) + { + CheckTableExists(eventTypeName); + try { + _eventAdapterService.AddBeanType(eventTypeName, eventType, false, true, true); + } catch (EventAdapterException t) { + throw new ConfigurationException(t.Message, t); + } + } + + public void AddEventType(Type eventType) { + CheckTableExists(eventType.Name); + try { + _eventAdapterService.AddBeanType(eventType.Name, eventType, false, true, true); + } catch (EventAdapterException t) { + throw new ConfigurationException(t.Message, t); + } + } + + public void AddEventType(string eventTypeName, Properties typeMap) { + CheckTableExists(eventTypeName); + IDictionary types = TypeHelper.GetClassObjectFromPropertyTypeNames( + typeMap, _engineImportService.GetClassForNameProvider()); + try { + _eventAdapterService.AddNestableMapType(eventTypeName, types, null, false, true, true, false, false); + } catch (EventAdapterException t) { + throw new ConfigurationException(t.Message, t); + } + } + + public void AddEventType(string eventTypeName, IDictionary typeMap) { + CheckTableExists(eventTypeName); + try { + IDictionary compiledProperties = EventTypeUtility.CompileMapTypeProperties(typeMap, _eventAdapterService); + _eventAdapterService.AddNestableMapType(eventTypeName, compiledProperties, null, false, true, true, false, false); + } catch (EventAdapterException t) { + throw new ConfigurationException(t.Message, t); + } + } + + public void AddEventType(string eventTypeName, IDictionary typeMap, string[] superTypes) { + CheckTableExists(eventTypeName); + ConfigurationEventTypeMap optionalConfig = null; + if ((superTypes != null) && (superTypes.Length > 0)) { + optionalConfig = new ConfigurationEventTypeMap(); + optionalConfig.SuperTypes.AddAll(superTypes); + } + + try { + _eventAdapterService.AddNestableMapType(eventTypeName, typeMap, optionalConfig, false, true, true, false, false); + } catch (EventAdapterException t) { + throw new ConfigurationException(t.Message, t); + } + } + + public void AddEventType(string eventTypeName, IDictionary typeMap, ConfigurationEventTypeMap mapConfig) { + CheckTableExists(eventTypeName); + try { + _eventAdapterService.AddNestableMapType(eventTypeName, typeMap, mapConfig, false, true, true, false, false); + } catch (EventAdapterException t) { + throw new ConfigurationException(t.Message, t); + } + } + + public void AddEventType(string eventTypeName, string[] propertyNames, Object[] propertyTypes) { + AddEventType(eventTypeName, propertyNames, propertyTypes, null); + } + + public void AddEventType(string eventTypeName, string[] propertyNames, Object[] propertyTypes, ConfigurationEventTypeObjectArray optionalConfiguration) { + CheckTableExists(eventTypeName); + try { + var propertyTypesMap = EventTypeUtility.ValidateObjectArrayDef(propertyNames, propertyTypes); + var compiledProperties = EventTypeUtility.CompileMapTypeProperties(propertyTypesMap, _eventAdapterService); + _eventAdapterService.AddNestableObjectArrayType(eventTypeName, compiledProperties, optionalConfiguration, false, true, true, false, false, false, null); + } catch (EventAdapterException t) { + throw new ConfigurationException(t.Message, t); + } + } + + public void AddEventType(string eventTypeName, ConfigurationEventTypeXMLDOM xmlDOMEventTypeDesc) { + CheckTableExists(eventTypeName); + SchemaModel schemaModel = null; + + if ((xmlDOMEventTypeDesc.SchemaResource != null) || (xmlDOMEventTypeDesc.SchemaText != null)) { + try { + schemaModel = XSDSchemaMapper.LoadAndMap(xmlDOMEventTypeDesc.SchemaResource, xmlDOMEventTypeDesc.SchemaText, _engineImportService); + } catch (Exception ex) { + throw new ConfigurationException(ex.Message, ex); + } + } + + try { + _eventAdapterService.AddXMLDOMType(eventTypeName, xmlDOMEventTypeDesc, schemaModel, false); + } catch (EventAdapterException t) { + throw new ConfigurationException(t.Message, t); + } + } + + public void AddVariable(string variableName, TValue initializationValue) + { + AddVariable(variableName, typeof(TValue).FullName, initializationValue, false); + } + + public void AddVariable(string variableName, Type type, Object initializationValue) { + AddVariable(variableName, type.FullName, initializationValue, false); + } + + public void AddVariable(string variableName, string eventTypeName, Object initializationValue) { + AddVariable(variableName, eventTypeName, initializationValue, false); + } + + public void AddVariable(string variableName, string type, Object initializationValue, bool constant) { + try { + Pair arrayType = TypeHelper.IsGetArrayType(type); + _variableService.CreateNewVariable(null, variableName, arrayType.First, constant, arrayType.Second, false, initializationValue, _engineImportService); + _variableService.AllocateVariableState(variableName, EPStatementStartMethodConst.DEFAULT_AGENT_INSTANCE_ID, null, false); + _statementVariableRef.AddConfiguredVariable(variableName); + } catch (VariableExistsException e) { + throw new ConfigurationException("Error creating variable: " + e.Message, e); + } catch (VariableTypeException e) { + throw new ConfigurationException("Error creating variable: " + e.Message, e); + } + } + + public void AddPlugInEventType(string eventTypeName, Uri[] resolutionURIs, object initializer) { + if (initializer == null) + { + throw new ArgumentNullException("initializer"); + } + + if (!initializer.GetType().IsSerializable) + { + throw new ArgumentException("initializer is not serializable", "initializer"); + } + + try { + _eventAdapterService.AddPlugInEventType(eventTypeName, resolutionURIs, initializer); + } catch (EventAdapterException e) { + throw new ConfigurationException("Error adding plug-in event type: " + e.Message, e); + } + } + + public IList PlugInEventTypeResolutionURIs + { + get { return _engineSettingsService.PlugInEventTypeResolutionURIs; } + set { _engineSettingsService.PlugInEventTypeResolutionURIs = value; } + } + + public void AddRevisionEventType(string revisioneventTypeName, ConfigurationRevisionEventType revisionEventTypeConfig) { + CheckTableExists(revisioneventTypeName); + _valueAddEventService.AddRevisionEventType(revisioneventTypeName, revisionEventTypeConfig, _eventAdapterService); + } + + public void AddVariantStream(string varianteventTypeName, ConfigurationVariantStream variantStreamConfig) { + CheckTableExists(varianteventTypeName); + _valueAddEventService.AddVariantStream(varianteventTypeName, variantStreamConfig, _eventAdapterService, _eventTypeIdGenerator); + } + + public void UpdateMapEventType(string mapeventTypeName, IDictionary typeMap) { + try { + _eventAdapterService.UpdateMapEventType(mapeventTypeName, typeMap); + } catch (EventAdapterException e) { + throw new ConfigurationException("Error updating Map event type: " + e.Message, e); + } + } + + public void UpdateObjectArrayEventType(string objectArrayEventTypeName, string[] propertyNames, Object[] propertyTypes) { + try { + var typeMap = EventTypeUtility.ValidateObjectArrayDef(propertyNames, propertyTypes); + _eventAdapterService.UpdateObjectArrayEventType(objectArrayEventTypeName, typeMap); + } catch (EventAdapterException e) { + throw new ConfigurationException("Error updating Object-array event type: " + e.Message, e); + } + } + + public void ReplaceXMLEventType(string xmlEventTypeName, ConfigurationEventTypeXMLDOM config) { + SchemaModel schemaModel = null; + if (config.SchemaResource != null || config.SchemaText != null) { + try { + schemaModel = XSDSchemaMapper.LoadAndMap(config.SchemaResource, config.SchemaText, _engineImportService); + } catch (Exception ex) { + throw new ConfigurationException(ex.Message, ex); + } + } + + try { + _eventAdapterService.ReplaceXMLEventType(xmlEventTypeName, config, schemaModel); + } catch (EventAdapterException e) { + throw new ConfigurationException("Error updating XML event type: " + e.Message, e); + } + } + + public void SetMetricsReportingInterval(string stmtGroupName, long newInterval) { + try + { + _metricReportingService.SetMetricsReportingInterval(stmtGroupName, newInterval); + } catch (Exception e) { + throw new ConfigurationException("Error updating interval for metric reporting: " + e.Message, e); + } + } + + + public void SetMetricsReportingStmtEnabled(string statementName) { + try { + _metricReportingService.SetMetricsReportingStmtEnabled(statementName); + } catch (Exception e) { + throw new ConfigurationException("Error enabling metric reporting for statement: " + e.Message, e); + } + } + + public void SetMetricsReportingStmtDisabled(string statementName) { + try { + _metricReportingService.SetMetricsReportingStmtDisabled(statementName); + } catch (Exception e) { + throw new ConfigurationException("Error enabling metric reporting for statement: " + e.Message, e); + } + } + + public void SetMetricsReportingEnabled() { + try { + _metricReportingService.SetMetricsReportingEnabled(); + } + catch (Exception e) + { + throw new ConfigurationException("Error enabling metric reporting: " + e.Message, e); + } + } + + public void SetMetricsReportingDisabled() { + try { + _metricReportingService.SetMetricsReportingDisabled(); + } + catch (Exception e) + { + throw new ConfigurationException("Error enabling metric reporting: " + e.Message, e); + } + } + + public bool IsVariantStreamExists(string name) { + ValueAddEventProcessor processor = _valueAddEventService.GetValueAddProcessor(name); + if (processor == null) { + return false; + } + return processor.ValueAddEventType is VariantEventType; + } + + public bool RemoveEventType(string name, bool force) { + if (!force) { + var statements = _statementEventTypeRef.GetStatementNamesForType(name); + if ((statements != null) && (!statements.IsEmpty())) { + throw new ConfigurationException("Event type '" + name + "' is in use by one or more statements"); + } + } + + EventType type = _eventAdapterService.GetEventTypeByName(name); + if (type == null) { + return false; + } + + _eventAdapterService.RemoveType(name); + _statementEventTypeRef.RemoveReferencesType(name); + _filterService.RemoveType(type); + return true; + } + + public bool RemoveVariable(string name, bool force) { + if (!force) { + var statements = _statementVariableRef.GetStatementNamesForVar(name); + if ((statements != null) && (!statements.IsEmpty())) { + throw new ConfigurationException("Variable '" + name + "' is in use by one or more statements"); + } + } + + VariableReader reader = _variableService.GetReader(name, EPStatementStartMethodConst.DEFAULT_AGENT_INSTANCE_ID); + if (reader == null) { + return false; + } + + _variableService.RemoveVariableIfFound(name); + _statementVariableRef.RemoveReferencesVariable(name); + _statementVariableRef.RemoveConfiguredVariable(name); + return true; + } + + public ICollection GetEventTypeNameUsedBy(string name) { + var statements = _statementEventTypeRef.GetStatementNamesForType(name); + if ((statements == null) || (statements.IsEmpty())) { + return Collections.GetEmptySet(); + } + return statements.AsReadOnlyCollection(); + } + + public ICollection GetVariableNameUsedBy(string variableName) { + ICollection statements = _statementVariableRef.GetStatementNamesForVar(variableName); + if ((statements == null) || (statements.IsEmpty())) { + return Collections.GetEmptySet(); + } + return statements.AsReadOnlyCollection(); + } + + public EventType GetEventType(string eventTypeName) { + return _eventAdapterService.GetEventTypeByName(eventTypeName); + } + + public ICollection EventTypes + { + get { return _eventAdapterService.AllTypes; } + } + + public void AddEventType(ConfigurationEventTypeLegacy legacyEventTypeDesc) + { + AddEventType(typeof(T).Name, typeof(T).AssemblyQualifiedName, legacyEventTypeDesc); + } + + public void AddEventType(string eventTypeName, ConfigurationEventTypeLegacy legacyEventTypeDesc) + { + AddEventType(eventTypeName, typeof(T).AssemblyQualifiedName, legacyEventTypeDesc); + } + + public void AddEventType(string eventTypeName, string eventClass, ConfigurationEventTypeLegacy legacyEventTypeDesc) + { + // To ensure proper usage, we have to convert the type to its assembly qualified name. + try + { + eventClass = TypeHelper.ResolveType(eventClass, true).AssemblyQualifiedName; + } + catch (TypeLoadException ex) + { + throw new ConfigurationException("Failed to add legacy event type definition for type '" + eventTypeName + "': " + ex.Message, ex); + } + + CheckTableExists(eventTypeName); + try { + var map = new Dictionary(); + map.Put(eventClass, legacyEventTypeDesc); + _eventAdapterService.TypeLegacyConfigs = map; + _eventAdapterService.AddBeanType(eventTypeName, eventClass, false, false, false, true); + } catch (EventAdapterException ex) { + throw new ConfigurationException("Failed to add legacy event type definition for type '" + eventTypeName + "': " + ex.Message, ex); + } + } + + private void CheckTableExists(string eventTypeName) { + try { + EPLValidationUtil.ValidateTableExists(_tableService, eventTypeName); + } catch (ExprValidationException ex) { + throw new ConfigurationException(ex.Message, ex); + } + } + + public long PatternMaxSubexpressions + { + set + { + if (_patternSubexpressionPoolSvc != null) + { + _patternSubexpressionPoolSvc.PatternMaxSubexpressions = value; + } + } + } + + public long? MatchRecognizeMaxStates + { + set + { + if (_matchRecognizeStatePoolEngineSvc != null) + { + _matchRecognizeStatePoolEngineSvc.MatchRecognizeMaxStates = value; + } + } + } + + public IDictionary TransientConfiguration + { + get { return _transientConfiguration; } + } + + public void AddEventTypeAvro(string eventTypeName, ConfigurationEventTypeAvro avro) { + CheckTableExists(eventTypeName); + try { + _eventAdapterService.AddAvroType(eventTypeName, avro, false, true, true, false, false); + } catch (EventAdapterException t) { + throw new ConfigurationException(t.Message, t); + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/service/Configurator.cs b/NEsper.Core/NEsper.Core/core/service/Configurator.cs new file mode 100755 index 000000000..6d9acbee8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/Configurator.cs @@ -0,0 +1,18 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; + +namespace com.espertech.esper.core.service +{ + public interface Configurator + { + EPServiceProviderSPI Configure(ConfiguratorContext context, + Configuration configuration); + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/service/ConfiguratorContext.cs b/NEsper.Core/NEsper.Core/core/service/ConfiguratorContext.cs new file mode 100755 index 000000000..035b576dc --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/ConfiguratorContext.cs @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + + +namespace com.espertech.esper.core.service +{ + public class ConfiguratorContext { + private readonly String engineURI; + private readonly IDictionary runtimes; + + public ConfiguratorContext(String engineURI, IDictionary runtimes) { + this.engineURI = engineURI; + this.runtimes = runtimes; + } + + public String GetEngineURI() { + return engineURI; + } + + public IDictionary GetRuntimes() { + return runtimes; + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/DeliveryConvertor.cs b/NEsper.Core/NEsper.Core/core/service/DeliveryConvertor.cs new file mode 100755 index 000000000..1a602d3e8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/DeliveryConvertor.cs @@ -0,0 +1,21 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.core.service +{ + /// Converts a row of column selection results into a result for dispatch to a method. + public interface DeliveryConvertor + { + /// Convert result row to dispatchable. + /// to convert + /// converted row + Object[] ConvertRow(Object[] row); + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/DeliveryConvertorMap.cs b/NEsper.Core/NEsper.Core/core/service/DeliveryConvertorMap.cs new file mode 100755 index 000000000..95bcc6f10 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/DeliveryConvertorMap.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.compat.collections; + + +namespace com.espertech.esper.core.service +{ + /// Converts column results into a Map of key-value pairs. + public class DeliveryConvertorMap : DeliveryConvertor + { + private readonly String[] _columnNames; + + /// Ctor. + /// the names for columns + public DeliveryConvertorMap(String[] columnNames) { + this._columnNames = columnNames; + } + + public Object[] ConvertRow(Object[] columns) { + IDictionary map = new Dictionary(); + for (int i = 0; i < columns.Length; i++) + { + map.Put(_columnNames[i], columns[i]); + } + return new Object[] {map}; + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/DeliveryConvertorMapWStatement.cs b/NEsper.Core/NEsper.Core/core/service/DeliveryConvertorMapWStatement.cs new file mode 100755 index 000000000..538f321ed --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/DeliveryConvertorMapWStatement.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.core.service +{ + public class DeliveryConvertorMapWStatement : DeliveryConvertor + { + private readonly string[] _columnNames; + private readonly EPStatement _statement; + + public DeliveryConvertorMapWStatement(string[] columnNames, EPStatement statement) + { + _columnNames = columnNames; + _statement = statement; + } + + public object[] ConvertRow(object[] columns) + { + var map = new Dictionary(); + for (int i = 0; i < columns.Length; i++) + { + map.Put(_columnNames[i], columns[i]); + } + return new object[] {_statement, map}; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/service/DeliveryConvertorNull.cs b/NEsper.Core/NEsper.Core/core/service/DeliveryConvertorNull.cs new file mode 100755 index 000000000..26e5dd686 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/DeliveryConvertorNull.cs @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.core.service +{ + /// Implementation that does not convert columns. + public class DeliveryConvertorNull : DeliveryConvertor + { + public static readonly DeliveryConvertorNull INSTANCE = + new DeliveryConvertorNull(); + + private DeliveryConvertorNull() + { + } + + public Object[] ConvertRow(Object[] columns) { + return columns; + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/DeliveryConvertorNullWStatement.cs b/NEsper.Core/NEsper.Core/core/service/DeliveryConvertorNullWStatement.cs new file mode 100755 index 000000000..8f1ad1636 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/DeliveryConvertorNullWStatement.cs @@ -0,0 +1,35 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.core.service +{ + /// + /// Implementation that does not convert columns. + /// + public class DeliveryConvertorNullWStatement : DeliveryConvertor + { + private readonly EPStatement _statement; + + public DeliveryConvertorNullWStatement(EPStatement statement) + { + this._statement = statement; + } + + public object[] ConvertRow(object[] columns) + { + var deliver = new object[columns.Length + 1]; + deliver[0] = _statement; + Array.Copy(columns, 0, deliver, 1, columns.Length); + return deliver; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/service/DeliveryConvertorObjectArr.cs b/NEsper.Core/NEsper.Core/core/service/DeliveryConvertorObjectArr.cs new file mode 100755 index 000000000..931510f01 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/DeliveryConvertorObjectArr.cs @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.core.service +{ + /// + /// Implementation of a convertor for column results that renders the result as an object array itself. + /// + public class DeliveryConvertorObjectArr : DeliveryConvertor + { + internal static readonly DeliveryConvertorObjectArr INSTANCE = new DeliveryConvertorObjectArr(); + + public Object[] ConvertRow(Object[] columns) + { + return new Object[] + { + columns + }; + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/DeliveryConvertorObjectArrWStatement.cs b/NEsper.Core/NEsper.Core/core/service/DeliveryConvertorObjectArrWStatement.cs new file mode 100755 index 000000000..9d63aa758 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/DeliveryConvertorObjectArrWStatement.cs @@ -0,0 +1,30 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; + +namespace com.espertech.esper.core.service +{ + /// + /// Implementation of a convertor for column results that renders the result as an object array itself. + /// + public class DeliveryConvertorObjectArrWStatement : DeliveryConvertor + { + private readonly EPStatement _statement; + + public DeliveryConvertorObjectArrWStatement(EPStatement statement) + { + _statement = statement; + } + + public object[] ConvertRow(object[] columns) + { + return new object[] {_statement, columns}; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/service/DeliveryConvertorWidener.cs b/NEsper.Core/NEsper.Core/core/service/DeliveryConvertorWidener.cs new file mode 100755 index 000000000..6dddac09c --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/DeliveryConvertorWidener.cs @@ -0,0 +1,44 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.util; + +namespace com.espertech.esper.core.service +{ + /// + /// Implementation of a convertor for column results that renders the result as an object array itself. + /// + public class DeliveryConvertorWidener : DeliveryConvertor + { + private readonly TypeWidener[] _wideners; + + public DeliveryConvertorWidener(TypeWidener[] wideners) + { + _wideners = wideners; + } + + #region DeliveryConvertor Members + + public Object[] ConvertRow(Object[] columns) + { + for (int i = 0; i < columns.Length; i++) + { + if (_wideners[i] == null) + { + continue; + } + columns[i] = _wideners[i].Invoke(columns[i]); + } + return columns; + } + + #endregion + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/service/DeliveryConvertorWidenerWStatement.cs b/NEsper.Core/NEsper.Core/core/service/DeliveryConvertorWidenerWStatement.cs new file mode 100755 index 000000000..9712d8417 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/DeliveryConvertorWidenerWStatement.cs @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.util; + +namespace com.espertech.esper.core.service +{ + /// + /// Implementation of a convertor for column results that renders the result as an object array itself. + /// + public class DeliveryConvertorWidenerWStatement : DeliveryConvertor + { + private readonly TypeWidener[] _wideners; + private readonly EPStatement _statement; + + public DeliveryConvertorWidenerWStatement(TypeWidener[] wideners, EPStatement statement) + { + _wideners = wideners; + _statement = statement; + } + + public object[] ConvertRow(object[] columns) + { + var values = new object[columns.Length + 1]; + values[0] = _statement; + var offset = 1; + for (var i = 0; i < columns.Length; i++) + { + if (_wideners[i] == null) + { + values[offset] = columns[i]; + } + else + { + values[offset] = _wideners[i].Invoke(columns[i]); + } + offset++; + } + return values; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/service/EPAdministratorContext.cs b/NEsper.Core/NEsper.Core/core/service/EPAdministratorContext.cs new file mode 100755 index 000000000..3e973a7bf --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/EPAdministratorContext.cs @@ -0,0 +1,54 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.core.service +{ + /// Context for administrative services. + public class EPAdministratorContext + { + /// + /// Ctor. + /// + /// The runtime SPI. + /// engine services + /// configuration snapshot + /// default stream selection + public EPAdministratorContext(EPRuntimeSPI runtimeSPI, + EPServicesContext services, + ConfigurationOperations configurationOperations, + SelectClauseStreamSelectorEnum defaultStreamSelector + ) + { + RuntimeSPI = runtimeSPI; + ConfigurationOperations = configurationOperations; + DefaultStreamSelector = defaultStreamSelector; + Services = services; + } + + /// Returns configuration. + /// configuration + public ConfigurationOperations ConfigurationOperations { get; private set; } + + /// Returns the default stream selector. + /// default stream selector + public SelectClauseStreamSelectorEnum DefaultStreamSelector { get; private set; } + + /// Returns the engine services context. + /// engine services + public EPServicesContext Services { get; private set; } + + /// + /// Gets or sets the runtime SPI. + /// + /// The runtime SPI. + public EPRuntimeSPI RuntimeSPI { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/EPAdministratorHelper.cs b/NEsper.Core/NEsper.Core/core/service/EPAdministratorHelper.cs new file mode 100755 index 000000000..3c5c16b97 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/EPAdministratorHelper.cs @@ -0,0 +1,216 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using Antlr4.Runtime; +using Antlr4.Runtime.Tree; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.mgr; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.declexpr; +using com.espertech.esper.epl.generated; +using com.espertech.esper.epl.parse; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.epl.variable; +using com.espertech.esper.pattern; +using com.espertech.esper.schedule; + +namespace com.espertech.esper.core.service +{ + /// Helper class for administrative interface. + public class EPAdministratorHelper + { + private static readonly ParseRuleSelector PatternParseRule; + private static readonly ParseRuleSelector EPLParseRule; + + private static readonly ILog Log = + LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + static EPAdministratorHelper() + { + PatternParseRule = DoPatternParseRule; + EPLParseRule = DoEPLParseRule; + } + + #region Parse & Walk Rules + private static ITree DoPatternParseRule(EsperEPL2GrammarParser parser) + { + return parser.startPatternExpressionRule(); + } + + private static ITree DoEPLParseRule(EsperEPL2GrammarParser parser) + { + return parser.startEPLExpressionRule(); + } + #endregion + + /// + /// Compile an EPL statement. + /// + /// to compile + /// the statement to use for indicating error messages + /// true to add please-check message text + /// the name of statement + /// engine services + /// stream selector + /// compiled statement + public static StatementSpecRaw CompileEPL( + string eplStatement, + string eplStatementForErrorMsg, + bool addPleaseCheck, + string statementName, + EPServicesContext services, + SelectClauseStreamSelectorEnum defaultStreamSelector) + { + return CompileEPL( + eplStatement, eplStatementForErrorMsg, addPleaseCheck, statementName, defaultStreamSelector, + services.EngineImportService, services.VariableService, services.SchedulingService, services.EngineURI, + services.ConfigSnapshot, services.PatternNodeFactory, services.ContextManagementService, + services.ExprDeclaredService, services.TableService); + } + + public static StatementSpecRaw CompileEPL( + string eplStatement, + string eplStatementForErrorMsg, + bool addPleaseCheck, + string statementName, + SelectClauseStreamSelectorEnum defaultStreamSelector, + EngineImportService engineImportService, + VariableService variableService, + SchedulingService schedulingService, + string engineURI, + ConfigurationInformation configSnapshot, + PatternNodeFactory patternNodeFactory, + ContextManagementService contextManagementService, + ExprDeclaredService exprDeclaredService, + TableService tableService) + { + if (Log.IsDebugEnabled) + { + Log.Debug(".createEPLStmt statementName=" + statementName + " eplStatement=" + eplStatement); + } + + ParseResult parseResult = ParseHelper.Parse( + eplStatement, eplStatementForErrorMsg, addPleaseCheck, EPLParseRule, true); + ITree ast = parseResult.Tree; + + var walker = new EPLTreeWalkerListener( + parseResult.TokenStream, engineImportService, variableService, schedulingService, defaultStreamSelector, + engineURI, configSnapshot, patternNodeFactory, contextManagementService, parseResult.Scripts, + exprDeclaredService, tableService); + + try + { + ParseHelper.Walk(ast, walker, eplStatement, eplStatementForErrorMsg); + } + catch (ASTWalkException ex) + { + Log.Error(".createEPL Error validating expression", ex); + throw new EPStatementException(ex.Message, ex, eplStatementForErrorMsg); + } + catch (EPStatementSyntaxException ex) + { + throw; + } + catch (Exception ex) + { + string message = "Error in expression"; + Log.Debug(message, ex); + throw new EPStatementException(GetNullableErrortext(message, ex.Message), ex, eplStatementForErrorMsg); + } + + if (Log.IsDebugEnabled) + { + ASTUtil.DumpAST(ast); + } + + StatementSpecRaw raw = walker.StatementSpec; + raw.ExpressionNoAnnotations = parseResult.ExpressionWithoutAnnotations; + return raw; + } + + public static StatementSpecRaw CompilePattern( + string expression, + string expressionForErrorMessage, + bool addPleaseCheck, + EPServicesContext services, + SelectClauseStreamSelectorEnum defaultStreamSelector) + { + // Parse + ParseResult parseResult = ParseHelper.Parse( + expression, expressionForErrorMessage, addPleaseCheck, PatternParseRule, true); + ITree ast = parseResult.Tree; + if (Log.IsDebugEnabled) + { + ASTUtil.DumpAST(ast); + } + + // Walk + var walker = new EPLTreeWalkerListener( + parseResult.TokenStream, services.EngineImportService, services.VariableService, + services.SchedulingService, defaultStreamSelector, services.EngineURI, services.ConfigSnapshot, + services.PatternNodeFactory, services.ContextManagementService, parseResult.Scripts, + services.ExprDeclaredService, services.TableService); + try + { + ParseHelper.Walk(ast, walker, expression, expressionForErrorMessage); + } + catch (ASTWalkException ex) + { + Log.Debug(".createPattern Error validating expression", ex); + throw new EPStatementException(ex.Message, expression); + } + catch (EPStatementSyntaxException ex) + { + throw; + } + catch (Exception ex) + { + string message = "Error in expression"; + Log.Debug(message, ex); + throw new EPStatementException(GetNullableErrortext(message, ex.Message), expression); + } + + if (walker.StatementSpec.StreamSpecs.Count > 1) + { + throw new IllegalStateException("Unexpected multiple stream specifications encountered"); + } + + // Get pattern specification + PatternStreamSpecRaw patternStreamSpec = (PatternStreamSpecRaw) walker.StatementSpec.StreamSpecs[0]; + + // Create statement spec, set pattern stream, set wildcard select + var statementSpec = new StatementSpecRaw(SelectClauseStreamSelectorEnum.ISTREAM_ONLY); + statementSpec.StreamSpecs.Add(patternStreamSpec); + statementSpec.SelectClauseSpec.SelectExprList.Clear(); + statementSpec.SelectClauseSpec.SelectExprList.Add(new SelectClauseElementWildcard()); + statementSpec.Annotations = walker.StatementSpec.Annotations; + statementSpec.ExpressionNoAnnotations = parseResult.ExpressionWithoutAnnotations; + + return statementSpec; + } + + private static string GetNullableErrortext(string msg, string cause) + { + if (cause == null) + { + return msg; + } + else + { + return msg + ": " + cause; + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/service/EPAdministratorImpl.cs b/NEsper.Core/NEsper.Core/core/service/EPAdministratorImpl.cs new file mode 100755 index 000000000..73b7c5cd8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/EPAdministratorImpl.cs @@ -0,0 +1,356 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.client.context; +using com.espertech.esper.client.deploy; +using com.espertech.esper.client.soda; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.deploy; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; +using com.espertech.esper.pattern; + +namespace com.espertech.esper.core.service +{ + /// + /// Implementation for the admin interface. + /// + public class EPAdministratorImpl : EPAdministratorSPI + { + private const string SUBS_PARAM_INVALID_USE = "Invalid use of substitution parameters marked by '?' in statement, use the prepare method to prepare statements with substitution parameters"; + + private EPServicesContext _services; + private ConfigurationOperations _configurationOperations; + private readonly SelectClauseStreamSelectorEnum _defaultStreamSelector; + private readonly EPDeploymentAdmin _deploymentAdminService; + + /// Constructor - takes the services context as argument. + /// administrative context + public EPAdministratorImpl(EPAdministratorContext adminContext) + { + _services = adminContext.Services; + _configurationOperations = adminContext.ConfigurationOperations; + _defaultStreamSelector = adminContext.DefaultStreamSelector; + + _deploymentAdminService = new EPDeploymentAdminImpl( + this, + _services.EventProcessingRWLock, + adminContext.Services.DeploymentStateService, + adminContext.Services.StatementEventTypeRefService, + adminContext.Services.EventAdapterService, + adminContext.Services.StatementIsolationService, + adminContext.Services.FilterService, + _services.ConfigSnapshot.EngineDefaults.Expression.TimeZone, + _services.ConfigSnapshot.EngineDefaults.ExceptionHandling.UndeployRethrowPolicy); + } + + public EPDeploymentAdmin DeploymentAdmin + { + get { return _deploymentAdminService; } + } + + public EPStatement CreatePattern(string onExpression) + { + return CreatePatternStmt(onExpression, null, null, null); + } + + public EPStatement CreateEPL(string eplStatement) + { + return CreateEPLStmt(eplStatement, null, null, null); + } + + public EPStatement CreatePattern(string expression, string statementName) + { + return CreatePatternStmt(expression, statementName, null, null); + } + + public EPStatement CreatePattern(string expression, string statementName, object userobject) + { + return CreatePatternStmt(expression, statementName, userobject, null); + } + + public EPStatement CreateEPL(string eplStatement, string statementName) + { + return CreateEPLStmt(eplStatement, statementName, null, null); + } + + public EPStatement CreateEPLStatementId(string eplStatement, string statementName, object userobject, int statementId) + { + return CreateEPLStmt(eplStatement, statementName, userobject, statementId); + } + + public EPStatement CreateEPL(string eplStatement, string statementName, object userobject) + { + return CreateEPLStmt(eplStatement, statementName, userobject, null); + } + + public EPStatement CreatePattern(string expression, object userobject) + { + return CreatePatternStmt(expression, null, userobject, null); + } + + public EPStatement CreatePatternStatementId(string pattern, string statementName, object userobject, int statementId) + { + return CreatePatternStmt(pattern, statementName, userobject, statementId); + } + + public EPStatement CreateEPL(string eplStatement, object userobject) + { + return CreateEPLStmt(eplStatement, null, userobject, null); + } + + private EPStatement CreatePatternStmt(string expression, string statementName, object userobject, int? optionalStatementId) + { + var rawPattern = EPAdministratorHelper.CompilePattern(expression, expression, true, _services, SelectClauseStreamSelectorEnum.ISTREAM_ONLY); + return _services.StatementLifecycleSvc.CreateAndStart(rawPattern, expression, true, statementName, userobject, null, optionalStatementId, null); + } + + private EPStatement CreateEPLStmt(string eplStatement, string statementName, object userobject, int? optionalStatementId) + { + var statementSpec = EPAdministratorHelper.CompileEPL(eplStatement, eplStatement, true, statementName, _services, _defaultStreamSelector); + var statement = _services.StatementLifecycleSvc.CreateAndStart(statementSpec, eplStatement, false, statementName, userobject, null, optionalStatementId, null); + + Log.Debug(".createEPLStmt Statement created and started"); + return statement; + } + + public EPStatement Create(EPStatementObjectModel sodaStatement) + { + return Create(sodaStatement, null); + } + + public EPStatement CreateModelStatementId(EPStatementObjectModel sodaStatement, string statementName, object userobject, int statementId) + { + return Create(sodaStatement, statementName, userobject, statementId); + } + + public EPStatement Create(EPStatementObjectModel sodaStatement, string statementName, object userobject) + { + return Create(sodaStatement, statementName, userobject, null); + } + + public EPStatement Create(EPStatementObjectModel sodaStatement, string statementName, object userobject, int? optionalStatementId) + { + // Specifies the statement + var statementSpec = MapSODAToRaw(sodaStatement); + var eplStatement = sodaStatement.ToEPL(); + + var statement = _services.StatementLifecycleSvc.CreateAndStart(statementSpec, eplStatement, false, statementName, userobject, null, optionalStatementId, sodaStatement); + + Log.Debug(".createEPLStmt Statement created and started"); + return statement; + } + + public EPStatement Create(EPStatementObjectModel sodaStatement, string statementName) + { + // Specifies the statement + var statementSpec = MapSODAToRaw(sodaStatement); + var eplStatement = sodaStatement.ToEPL(); + + var statement = _services.StatementLifecycleSvc.CreateAndStart(statementSpec, eplStatement, false, statementName, null, null, null, sodaStatement); + + Log.Debug(".createEPLStmt Statement created and started"); + return statement; + } + + public EPPreparedStatement PrepareEPL(string eplExpression) + { + // compile to specification + var statementSpec = EPAdministratorHelper.CompileEPL(eplExpression, eplExpression, true, null, _services, _defaultStreamSelector); + + // map to object model thus finding all substitution parameters and their indexes + var unmapped = StatementSpecMapper.Unmap(statementSpec); + + // the prepared statement is the object model plus a list of substitution parameters + // map to specification will refuse any substitution parameters that are unfilled + return new EPPreparedStatementImpl(unmapped.ObjectModel, unmapped.SubstitutionParams, eplExpression); + } + + public EPPreparedStatement PreparePattern(string patternExpression) + { + var rawPattern = EPAdministratorHelper.CompilePattern(patternExpression, patternExpression, true, _services, SelectClauseStreamSelectorEnum.ISTREAM_ONLY); + + // map to object model thus finding all substitution parameters and their indexes + var unmapped = StatementSpecMapper.Unmap(rawPattern); + + // the prepared statement is the object model plus a list of substitution parameters + // map to specification will refuse any substitution parameters that are unfilled + return new EPPreparedStatementImpl(unmapped.ObjectModel, unmapped.SubstitutionParams, null); + } + + public EPStatement Create(EPPreparedStatement prepared, string statementName, object userobject, int? optionalStatementId) + { + var impl = (EPPreparedStatementImpl)prepared; + + var statementSpec = MapSODAToRaw(impl.Model); + var eplStatement = impl.Model.ToEPL(); + + return _services.StatementLifecycleSvc.CreateAndStart(statementSpec, eplStatement, false, statementName, userobject, null, optionalStatementId, impl.Model); + } + + public EPStatement Create(EPPreparedStatement prepared, string statementName) + { + return Create(prepared, statementName, null, null); + } + + public EPStatement Create(EPPreparedStatement prepared, string statementName, object userobject) + { + return Create(prepared, statementName, userobject, null); + } + + public EPStatement CreatePreparedEPLStatementId(EPPreparedStatementImpl prepared, string statementName, object userobject, int statementId) + { + return Create(prepared, statementName, userobject, statementId); + } + + public EPStatement Create(EPPreparedStatement prepared) + { + return Create(prepared, null); + } + + public EPStatementObjectModel CompileEPL(string eplStatement) + { + var statementSpec = EPAdministratorHelper.CompileEPL(eplStatement, eplStatement, true, null, _services, _defaultStreamSelector); + var unmapped = StatementSpecMapper.Unmap(statementSpec); + if (unmapped.SubstitutionParams.Count != 0) + { + throw new EPException(SUBS_PARAM_INVALID_USE); + } + return unmapped.ObjectModel; + } + + public EPStatement GetStatement(string name) + { + return _services.StatementLifecycleSvc.GetStatementByName(name); + } + + public string GetStatementNameForId(int statementId) + { + return _services.StatementLifecycleSvc.GetStatementNameById(statementId); + } + + public IList StatementNames + { + get { return _services.StatementLifecycleSvc.StatementNames; } + } + + public void StartAllStatements() + { + _services.StatementLifecycleSvc.StartAllStatements(); + } + + public void StopAllStatements() + { + _services.StatementLifecycleSvc.StopAllStatements(); + } + + public void DestroyAllStatements() + { + _services.StatementLifecycleSvc.DestroyAllStatements(); + } + + public ConfigurationOperations Configuration + { + get { return _configurationOperations; } + } + + /// Destroys an engine instance. + public void Dispose() + { + _services = null; + _configurationOperations = null; + } + + public StatementSpecRaw CompileEPLToRaw(string epl) + { + return EPAdministratorHelper.CompileEPL(epl, epl, true, null, _services, _defaultStreamSelector); + } + + public EPStatementObjectModel MapRawToSODA(StatementSpecRaw raw) + { + var unmapped = StatementSpecMapper.Unmap(raw); + if (unmapped.SubstitutionParams.Count != 0) + { + throw new EPException(SUBS_PARAM_INVALID_USE); + } + return unmapped.ObjectModel; + } + + public StatementSpecRaw MapSODAToRaw(EPStatementObjectModel model) + { + return StatementSpecMapper.Map( + model, + _services.EngineImportService, + _services.VariableService, + _services.ConfigSnapshot, + _services.SchedulingService, + _services.EngineURI, + _services.PatternNodeFactory, + _services.NamedWindowMgmtService, + _services.ContextManagementService, + _services.ExprDeclaredService, + _services.TableService); + } + + public EvalFactoryNode CompilePatternToNode(string pattern) + { + var raw = EPAdministratorHelper.CompilePattern(pattern, pattern, false, _services, SelectClauseStreamSelectorEnum.ISTREAM_ONLY); + return ((PatternStreamSpecRaw)raw.StreamSpecs[0]).EvalFactoryNode; + } + + public EPStatementObjectModel CompilePatternToSODAModel(string expression) + { + var rawPattern = EPAdministratorHelper.CompilePattern(expression, expression, true, _services, SelectClauseStreamSelectorEnum.ISTREAM_ONLY); + return MapRawToSODA(rawPattern); + } + + public ExprNode CompileExpression(string expression) + { + var toCompile = "select * from System.object#time(" + expression + ")"; + var raw = EPAdministratorHelper.CompileEPL(toCompile, expression, false, null, _services, SelectClauseStreamSelectorEnum.ISTREAM_ONLY); + return raw.StreamSpecs[0].ViewSpecs[0].ObjectParameters[0]; + } + + public Expression CompileExpressionToSODA(string expression) + { + var node = CompileExpression(expression); + return StatementSpecMapper.Unmap(node); + } + + public PatternExpr CompilePatternToSODA(string expression) + { + var node = CompilePatternToNode(expression); + return StatementSpecMapper.Unmap(node); + } + + public AnnotationPart CompileAnnotationToSODA(string annotationExpression) + { + var toCompile = annotationExpression + " select * from System.object"; + var raw = EPAdministratorHelper.CompileEPL(toCompile, annotationExpression, false, null, _services, SelectClauseStreamSelectorEnum.ISTREAM_ONLY); + return StatementSpecMapper.Unmap(raw.Annotations[0]); + } + + public MatchRecognizeRegEx CompileMatchRecognizePatternToSODA(string matchRecogPatternExpression) + { + var toCompile = "select * from System.object Match_recognize(measures a.b as c pattern (" + matchRecogPatternExpression + ") define A as true)"; + var raw = EPAdministratorHelper.CompileEPL(toCompile, matchRecogPatternExpression, false, null, _services, SelectClauseStreamSelectorEnum.ISTREAM_ONLY); + return StatementSpecMapper.Unmap(raw.MatchRecognizeSpec.Pattern); + } + + public EPContextPartitionAdmin ContextPartitionAdmin + { + get { return new EPContextPartitionAdminImpl(_services); } + } + + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/EPAdministratorIsolatedImpl.cs b/NEsper.Core/NEsper.Core/core/service/EPAdministratorIsolatedImpl.cs new file mode 100755 index 000000000..9ef0aad0d --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/EPAdministratorIsolatedImpl.cs @@ -0,0 +1,282 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.service.resource; +using com.espertech.esper.epl.spec; +using com.espertech.esper.filter; +using com.espertech.esper.schedule; + +namespace com.espertech.esper.core.service +{ + /// Implementation for the admin interface. + public class EPAdministratorIsolatedImpl : EPAdministratorIsolatedSPI + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly string _isolatedServiceName; + private readonly EPIsolationUnitServices _services; + private readonly EPServicesContext _unisolatedServices; + private readonly EPRuntimeIsolatedSPI _isolatedRuntime; + private readonly ICollection _statementNames = new HashSet().AsSyncCollection(); + + /// Ctor. + /// name of the isolated service + /// isolated services + /// engine services + /// the runtime for this isolated service + public EPAdministratorIsolatedImpl(string isolatedServiceName, EPIsolationUnitServices services, EPServicesContext unisolatedServices, EPRuntimeIsolatedSPI isolatedRuntime) + { + _isolatedServiceName = isolatedServiceName; + _services = services; + _unisolatedServices = unisolatedServices; + _isolatedRuntime = isolatedRuntime; + } + + public EPStatement CreateEPL(string eplStatement, string statementName, object userObject) + { + return CreateEPLStatementId(eplStatement, statementName, userObject, null); + } + + public EPStatement CreateEPLStatementId(string eplStatement, string statementName, object userObject, int? optionalStatementId) + { + var defaultStreamSelector = _unisolatedServices.ConfigSnapshot.EngineDefaults.StreamSelection.DefaultStreamSelector.MapFromSODA(); + var statementSpec = EPAdministratorHelper.CompileEPL(eplStatement, eplStatement, true, statementName, _unisolatedServices, defaultStreamSelector); + var statement = _unisolatedServices.StatementLifecycleSvc.CreateAndStart(statementSpec, eplStatement, false, statementName, userObject, _services, optionalStatementId, null); + var stmtSpi = (EPStatementSPI)statement; + stmtSpi.StatementContext.InternalEventEngineRouteDest = _isolatedRuntime; + stmtSpi.ServiceIsolated = _isolatedServiceName; + _statementNames.Add(stmtSpi.Name); + return statement; + } + + public IList StatementNames + { + get { return _statementNames.ToArray(); } + } + + public void AddStatement(string name) + { + _statementNames.Add(name); // for recovery + } + + public void AddStatement(EPStatement stmt) + { + + AddStatement(new[] { stmt }); + } + + public void AddStatement(EPStatement[] stmt) + { + using (_unisolatedServices.EventProcessingRWLock.AcquireWriteLock()) + { + try + { + long fromTime = _unisolatedServices.SchedulingService.Time; + long toTime = _services.SchedulingService.Time; + long delta = toTime - fromTime; + + // perform checking + ICollection statementIds = new HashSet(); + foreach (EPStatement aStmt in stmt) + { + if (aStmt == null) + { + throw new EPServiceIsolationException( + "Illegal argument, a null value was provided in the statement list"); + } + var stmtSpi = (EPStatementSPI)aStmt; + statementIds.Add(stmtSpi.StatementId); + + if (aStmt.ServiceIsolated != null) + { + throw new EPServiceIsolationException( + string.Format("Statement named '{0}' already in service isolation under '{1}'", aStmt.Name, stmtSpi.ServiceIsolated)); + } + } + + // start txn + _unisolatedServices.StatementIsolationService.BeginIsolatingStatements(_isolatedServiceName, + _services.UnitId, stmt); + + FilterSet filters = _unisolatedServices.FilterService.Take(statementIds); + ScheduleSet schedules = _unisolatedServices.SchedulingService.Take(statementIds); + + _services.FilterService.Apply(filters); + _services.SchedulingService.Apply(schedules); + + foreach (EPStatement aStmt in stmt) + { + var stmtSpi = (EPStatementSPI)aStmt; + stmtSpi.StatementContext.FilterService = _services.FilterService; + stmtSpi.StatementContext.SchedulingService = _services.SchedulingService; + stmtSpi.StatementContext.InternalEventEngineRouteDest = _isolatedRuntime; + stmtSpi.StatementContext.ScheduleAdjustmentService.Adjust(delta); + _statementNames.Add(stmtSpi.Name); + stmtSpi.ServiceIsolated = _isolatedServiceName; + ApplyFilterVersion(stmtSpi, _services.FilterService.FiltersVersion); + } + + // commit txn + _unisolatedServices.StatementIsolationService.CommitIsolatingStatements(_isolatedServiceName, + _services.UnitId, stmt); + } + catch (EPServiceIsolationException) + { + throw; + } + catch (Exception ex) + { + _unisolatedServices.StatementIsolationService.RollbackIsolatingStatements(_isolatedServiceName, + _services.UnitId, stmt); + + string message = "Unexpected exception taking statements: " + ex.Message; + Log.Error(message, ex); + throw new EPException(message, ex); + } + } + } + + public void RemoveStatement(EPStatement stmt) + { + RemoveStatement(new[] { stmt }); + } + + public void RemoveStatement(IList stmt) + { + + using (_unisolatedServices.EventProcessingRWLock.AcquireWriteLock()) + { + try + { + long fromTime = _services.SchedulingService.Time; + long toTime = _unisolatedServices.SchedulingService.Time; + long delta = toTime - fromTime; + + ICollection statementIds = new HashSet(); + foreach (EPStatement aStmt in stmt) + { + if (aStmt == null) + { + throw new EPServiceIsolationException( + "Illegal argument, a null value was provided in the statement list"); + } + + var stmtSpi = (EPStatementSPI)aStmt; + statementIds.Add(stmtSpi.StatementId); + + if (aStmt.ServiceIsolated == null) + { + throw new EPServiceIsolationException( + string.Format("Statement named '{0}' is not currently in service isolation", aStmt.Name)); + } + if (!aStmt.ServiceIsolated.Equals(_isolatedServiceName)) + { + throw new EPServiceIsolationException( + string.Format("Statement named '{0}' not in this service isolation but under service isolation '{0}'", aStmt.Name)); + } + } + + // start txn + _unisolatedServices.StatementIsolationService.BeginUnisolatingStatements( + _isolatedServiceName, + _services.UnitId, + stmt); + + FilterSet filters = _services.FilterService.Take(statementIds); + ScheduleSet schedules = _services.SchedulingService.Take(statementIds); + + _unisolatedServices.FilterService.Apply(filters); + _unisolatedServices.SchedulingService.Apply(schedules); + + foreach (EPStatement aStmt in stmt) + { + var stmtSpi = (EPStatementSPI)aStmt; + stmtSpi.StatementContext.FilterService = _unisolatedServices.FilterService; + stmtSpi.StatementContext.SchedulingService = _unisolatedServices.SchedulingService; + stmtSpi.StatementContext.InternalEventEngineRouteDest = + _unisolatedServices.InternalEventEngineRouteDest; + stmtSpi.StatementContext.ScheduleAdjustmentService.Adjust(delta); + _statementNames.Remove(stmtSpi.Name); + stmtSpi.ServiceIsolated = null; + ApplyFilterVersion(stmtSpi, _unisolatedServices.FilterService.FiltersVersion); + } + + // commit txn + _unisolatedServices.StatementIsolationService.CommitUnisolatingStatements( + _isolatedServiceName, _services.UnitId, stmt); + } + catch (EPServiceIsolationException) + { + throw; + } + catch (Exception ex) + { + _unisolatedServices.StatementIsolationService.RollbackUnisolatingStatements(_isolatedServiceName, + _services.UnitId, stmt); + + string message = "Unexpected exception taking statements: " + ex.Message; + Log.Error(message, ex); + throw new EPException(message, ex); + } + } + } + + /// Remove all statements from isolated services, such as upon destroy. + public void RemoveAllStatements() + { + var statements = new List(); + foreach (string stmtName in _statementNames) + { + EPStatement stmt = _unisolatedServices.StatementLifecycleSvc.GetStatementByName(stmtName); + if (stmt == null) + { + Log.Debug("Statement '{0}', the statement could not be found", stmtName); + continue; + } + + if (stmt.ServiceIsolated != null && (!stmt.ServiceIsolated.Equals(_isolatedServiceName))) + { + Log.Error("Error returning statement '{0}', the internal isolation information is incorrect, isolated service for statement is currently '{1}' and mismatches this isolated services named '{2}'", stmtName, stmt.ServiceIsolated, _isolatedServiceName); + continue; + } + + statements.Add(stmt); + } + + RemoveStatement(statements.ToArray()); + } + + private void ApplyFilterVersion(EPStatementSPI stmtSpi, long filtersVersion) + { + var resources = stmtSpi.StatementContext.StatementExtensionServicesContext.StmtResources; + if (resources.Unpartitioned != null) + { + ApplyFilterVersion(resources.Unpartitioned, filtersVersion); + } + else + { + foreach (var entry in resources.ResourcesPartitioned) + { + ApplyFilterVersion(entry.Value, filtersVersion); + } + } + } + + private void ApplyFilterVersion(StatementResourceHolder holder, long filtersVersion) + { + holder.AgentInstanceContext.EpStatementAgentInstanceHandle.StatementFilterVersion.StmtFilterVersion = filtersVersion; + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/EPAdministratorIsolatedSPI.cs b/NEsper.Core/NEsper.Core/core/service/EPAdministratorIsolatedSPI.cs new file mode 100755 index 000000000..29abbac43 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/EPAdministratorIsolatedSPI.cs @@ -0,0 +1,30 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; + +namespace com.espertech.esper.core.service +{ + /// + /// Implementation for the admin interface. + /// + public interface EPAdministratorIsolatedSPI : EPAdministratorIsolated + { + /// + /// Add a statement name to the list of statements held by the isolated service provider. + /// + /// to add + void AddStatement(string name); + + EPStatement CreateEPLStatementId( + string eplStatement, + string statementName, + object userObject, + int? optionalStatementId); + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/EPAdministratorSPI.cs b/NEsper.Core/NEsper.Core/core/service/EPAdministratorSPI.cs new file mode 100755 index 000000000..73d11d113 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/EPAdministratorSPI.cs @@ -0,0 +1,71 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.client.soda; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; +using com.espertech.esper.pattern; + +namespace com.espertech.esper.core.service +{ + /// Administrative SPI. + public interface EPAdministratorSPI : EPAdministrator, IDisposable + { + /// Compile expression. + /// to compile + /// compiled expression + /// EPException if compile failed + ExprNode CompileExpression(string expression); + + /// Compile expression. + /// to compile + /// compiled expression + /// EPException if compile failed + Expression CompileExpressionToSODA(string expression); + + /// Compile pattern. + /// to compile + /// compiled expression + /// EPException if compile failed + EvalFactoryNode CompilePatternToNode(string expression); + + /// Compile pattern. + /// to compile + /// compiled expression + /// EPException if compile failed + PatternExpr CompilePatternToSODA(string expression); + + /// Compile pattern. + /// to compile + /// compiled expression + /// EPException if compile failed + EPStatementObjectModel CompilePatternToSODAModel(string expression); + + /// Compile annotation expressions. + /// to compile + /// model representation + AnnotationPart CompileAnnotationToSODA(string annotationExpression); + + /// Compile match recognize pattern expression. + /// to compile + /// model representation + MatchRecognizeRegEx CompileMatchRecognizePatternToSODA(string matchRecogPatternExpression); + + StatementSpecRaw CompileEPLToRaw(string epl); + EPStatementObjectModel MapRawToSODA(StatementSpecRaw raw); + StatementSpecRaw MapSODAToRaw(EPStatementObjectModel model); + EPStatement CreateEPLStatementId(string eplStatement, string statementName, object userObject, int statementId); + EPStatement CreateModelStatementId(EPStatementObjectModel sodaStatement, string statementName, object userObject, int statementId); + EPStatement CreatePatternStatementId(string pattern, string statementName, object userObject, int statementId); + EPStatement CreatePreparedEPLStatementId(EPPreparedStatementImpl prepared, string statementName, object userObject, int statementId); + string GetStatementNameForId(int statementId); + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/EPContextPartitionAdminImpl.cs b/NEsper.Core/NEsper.Core/core/service/EPContextPartitionAdminImpl.cs new file mode 100755 index 000000000..12b5b6588 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/EPContextPartitionAdminImpl.cs @@ -0,0 +1,200 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client.context; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.mgr; +using com.espertech.esper.core.context.util; + +namespace com.espertech.esper.core.service +{ + public class EPContextPartitionAdminImpl : EPContextPartitionAdminSPI + { + private readonly EPServicesContext _services; + + public EPContextPartitionAdminImpl(EPServicesContext services) + { + _services = services; + } + + public bool IsSupportsExtract + { + get { return _services.ContextManagerFactoryService.IsSupportsExtract; } + } + + public String[] GetContextStatementNames(String contextName) + { + var contextManager = _services.ContextManagementService.GetContextManager(contextName); + if (contextManager == null) + { + return null; + } + + var statements = new String[contextManager.Statements.Count]; + var count = 0; + foreach (var entry in contextManager.Statements) + { + statements[count++] = entry.Value.Statement.StatementContext.StatementName; + } + return statements; + } + + public int GetContextNestingLevel(String contextName) + { + var contextManager = CheckedGetContextManager(contextName); + return contextManager.NumNestingLevels; + } + + public ContextPartitionCollection DestroyContextPartitions(String contextName, ContextPartitionSelector selector) + { + var contextManager = CheckedGetContextManager(contextName); + var descriptor = contextManager.ExtractDestroyPaths(selector); + return new ContextPartitionCollection(descriptor.ContextPartitionInformation); + } + + public ContextPartitionDescriptor DestroyContextPartition(String contextName, int agentInstanceId) + { + var contextManager = CheckedGetContextManager(contextName); + var descriptor = contextManager.ExtractDestroyPaths(new CPSelectorById(agentInstanceId)); + return descriptor.ContextPartitionInformation.Get(agentInstanceId); + } + + public EPContextPartitionExtract ExtractDestroyPaths(String contextName, ContextPartitionSelector selector) + { + var contextManager = CheckedGetContextManager(contextName); + var descriptor = contextManager.ExtractDestroyPaths(selector); + return DescriptorToExtract(contextManager.NumNestingLevels, descriptor); + } + + public ContextPartitionCollection StopContextPartitions(String contextName, ContextPartitionSelector selector) + { + var contextManager = CheckedGetContextManager(contextName); + var descriptor = contextManager.ExtractStopPaths(selector); + return new ContextPartitionCollection(descriptor.ContextPartitionInformation); + } + + public ContextPartitionCollection StartContextPartitions(String contextName, ContextPartitionSelector selector) + { + var contextManager = CheckedGetContextManager(contextName); + return new ContextPartitionCollection(contextManager.StartPaths(selector)); + } + + public ContextPartitionCollection GetContextPartitions(String contextName, ContextPartitionSelector selector) + { + var contextManager = CheckedGetContextManager(contextName); + return new ContextPartitionCollection(contextManager.ExtractPaths(selector).ContextPartitionInformation); + } + + public ContextPartitionDescriptor StopContextPartition(String contextName, int agentInstanceId) + { + var contextManager = CheckedGetContextManager(contextName); + var descriptor = contextManager.ExtractStopPaths(new CPSelectorById(agentInstanceId)); + return descriptor.ContextPartitionInformation.Get(agentInstanceId); + } + + public ContextPartitionDescriptor StartContextPartition(String contextName, int agentInstanceId) + { + var contextManager = CheckedGetContextManager(contextName); + var descriptorMap = contextManager.StartPaths(new CPSelectorById(agentInstanceId)); + return descriptorMap.Get(agentInstanceId); + } + + public ContextPartitionDescriptor GetDescriptor(String contextName, int agentInstanceId) + { + var contextManager = CheckedGetContextManager(contextName); + var descriptor = contextManager.ExtractPaths(new CPSelectorById(agentInstanceId)); + return descriptor.ContextPartitionInformation.Get(agentInstanceId); + } + + public EPContextPartitionExtract ExtractStopPaths(String contextName, ContextPartitionSelector selector) + { + var contextManager = CheckedGetContextManager(contextName); + var descriptor = contextManager.ExtractStopPaths(selector); + return DescriptorToExtract(contextManager.NumNestingLevels, descriptor); + } + + public EPContextPartitionExtract ExtractPaths(String contextName, ContextPartitionSelector selector) + { + var contextManager = CheckedGetContextManager(contextName); + var contextPaths = contextManager.ExtractPaths(selector); + return DescriptorToExtract(contextManager.NumNestingLevels, contextPaths); + } + + public ISet GetContextPartitionIds(String contextName, ContextPartitionSelector selector) + { + var contextManager = CheckedGetContextManager(contextName); + return new HashSet(contextManager.GetAgentInstanceIds(selector)); + } + + public EPContextPartitionImportResult ImportStartPaths(String contextName, EPContextPartitionImportable importable, AgentInstanceSelector agentInstanceSelector) + { + var contextManager = CheckedGetContextManager(contextName); + var importCallback = new CPImportCallback(); + var state = new ContextControllerState(importable.Paths, true, importCallback); + contextManager.ImportStartPaths(state, agentInstanceSelector); + + ContextStateCache contextStateCache = contextManager.ContextStateCache; + foreach (var entry in importable.Paths) { + entry.Value.State = ContextPartitionState.STARTED; + contextStateCache.UpdateContextPath(contextName, entry.Key, entry.Value); + } + + return new EPContextPartitionImportResult(importCallback.ExistingToImported, importCallback.AllocatedToImported); + } + + private ContextManager CheckedGetContextManager(String contextName) + { + var contextManager = _services.ContextManagementService.GetContextManager(contextName); + if (contextManager == null) + { + throw new ArgumentException("Context by name '" + contextName + "' could not be found"); + } + return contextManager; + } + + private EPContextPartitionExtract DescriptorToExtract(int numNestingLevels, ContextStatePathDescriptor contextPaths) + { + var importable = new EPContextPartitionImportable(contextPaths.Paths); + return new EPContextPartitionExtract(new ContextPartitionCollection(contextPaths.ContextPartitionInformation), importable, numNestingLevels); + } + + public class CPImportCallback : ContextPartitionImportCallback + { + internal readonly IDictionary ExistingToImported = new Dictionary(); + internal readonly IDictionary AllocatedToImported = new Dictionary(); + + public void Existing(int agentInstanceId, int exportedAgentInstanceId) + { + ExistingToImported.Put(agentInstanceId, exportedAgentInstanceId); + } + + public void Allocated(int agentInstanceId, int exportedAgentInstanceId) + { + AllocatedToImported.Put(agentInstanceId, exportedAgentInstanceId); + } + } + + public class CPSelectorById : ContextPartitionSelectorById + { + private readonly int _agentInstanceId; + + public CPSelectorById(int agentInstanceId) + { + _agentInstanceId = agentInstanceId; + } + + public ICollection ContextPartitionIds + { + get { return Collections.SingletonList(_agentInstanceId); } + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/EPContextPartitionAdminSPI.cs b/NEsper.Core/NEsper.Core/core/service/EPContextPartitionAdminSPI.cs new file mode 100755 index 000000000..79c8a9548 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/EPContextPartitionAdminSPI.cs @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client.context; +using com.espertech.esper.core.context.mgr; + +namespace com.espertech.esper.core.service +{ + public interface EPContextPartitionAdminSPI : EPContextPartitionAdmin + { + bool IsSupportsExtract { get; } + + EPContextPartitionExtract ExtractDestroyPaths(String contextName, ContextPartitionSelector selector); + EPContextPartitionExtract ExtractStopPaths(String contextName, ContextPartitionSelector selector); + + EPContextPartitionExtract ExtractPaths(String contextName, ContextPartitionSelector selector); + EPContextPartitionImportResult ImportStartPaths(String contextName, EPContextPartitionImportable importable, AgentInstanceSelector agentInstanceSelector); + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/EPContextPartitionExtract.cs b/NEsper.Core/NEsper.Core/core/service/EPContextPartitionExtract.cs new file mode 100755 index 000000000..cc21d7c88 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/EPContextPartitionExtract.cs @@ -0,0 +1,44 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client.context; + +namespace com.espertech.esper.core.service +{ + [Serializable] + public class EPContextPartitionExtract + { + private readonly ContextPartitionCollection _collection; + private readonly EPContextPartitionImportable _importable; + private readonly int _numNestingLevels; + + public EPContextPartitionExtract(ContextPartitionCollection collection, EPContextPartitionImportable importable, int numNestingLevels) + { + _collection = collection; + _importable = importable; + _numNestingLevels = numNestingLevels; + } + + public ContextPartitionCollection Collection + { + get { return _collection; } + } + + public EPContextPartitionImportable Importable + { + get { return _importable; } + } + + public int NumNestingLevels + { + get { return _numNestingLevels; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/EPContextPartitionImportResult.cs b/NEsper.Core/NEsper.Core/core/service/EPContextPartitionImportResult.cs new file mode 100755 index 000000000..940e73a9d --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/EPContextPartitionImportResult.cs @@ -0,0 +1,36 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.core.service +{ + [Serializable] + public class EPContextPartitionImportResult + { + private readonly IDictionary _existingToImported; + private readonly IDictionary _allocatedToImported; + + public EPContextPartitionImportResult(IDictionary existingToImported, IDictionary allocatedToImported) + { + _existingToImported = existingToImported; + _allocatedToImported = allocatedToImported; + } + + public IDictionary AllocatedToImported + { + get { return _allocatedToImported; } + } + + public IDictionary ExistingToImported + { + get { return _existingToImported; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/EPContextPartitionImportable.cs b/NEsper.Core/NEsper.Core/core/service/EPContextPartitionImportable.cs new file mode 100755 index 000000000..4465dd7ee --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/EPContextPartitionImportable.cs @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.mgr; + +namespace com.espertech.esper.core.service +{ + [Serializable] + public class EPContextPartitionImportable + { + public EPContextPartitionImportable(OrderedDictionary paths) + { + Paths = paths; + } + + public OrderedDictionary Paths { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/EPIsolationUnitServices.cs b/NEsper.Core/NEsper.Core/core/service/EPIsolationUnitServices.cs new file mode 100755 index 000000000..4dc768d15 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/EPIsolationUnitServices.cs @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.filter; +using com.espertech.esper.schedule; + +namespace com.espertech.esper.core.service +{ + /// Context for all services that provide the isolated runtime. + public class EPIsolationUnitServices + { + /// Ctor. + /// the isolation unit name + /// id of the isolation unit + /// isolated filter service + /// isolated scheduling service + public EPIsolationUnitServices(String name, + int unitId, + FilterServiceSPI filterService, + SchedulingServiceSPI schedulingService) + { + Name = name; + UnitId = unitId; + FilterService = filterService; + SchedulingService = schedulingService; + } + + /// Returns the name of the isolated service. + /// name of the isolated service + public string Name { get; private set; } + + /// Returns the id assigned to that isolated service. + /// isolated service id + public int UnitId { get; private set; } + + /// Returns the isolated filter service. + /// filter service + public FilterServiceSPI FilterService { get; private set; } + + /// Returns the isolated scheduling service. + /// scheduling service + public SchedulingServiceSPI SchedulingService { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/service/EPOnDemandPreparedQuerySPI.cs b/NEsper.Core/NEsper.Core/core/service/EPOnDemandPreparedQuerySPI.cs new file mode 100755 index 000000000..912c8f90c --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/EPOnDemandPreparedQuerySPI.cs @@ -0,0 +1,18 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.core.start; + +namespace com.espertech.esper.core.service +{ + public interface EPOnDemandPreparedQuerySPI : EPOnDemandPreparedQuery + { + EPPreparedExecuteMethod ExecuteMethod { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/EPPreparedQueryImpl.cs b/NEsper.Core/NEsper.Core/core/service/EPPreparedQueryImpl.cs new file mode 100755 index 000000000..2209f427e --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/EPPreparedQueryImpl.cs @@ -0,0 +1,80 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.client.context; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.start; + +namespace com.espertech.esper.core.service +{ + /// + /// Provides prepared query functionality. + /// + public class EPPreparedQueryImpl : EPOnDemandPreparedQuerySPI + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly EPPreparedExecuteMethod _executeMethod; + private readonly String _epl; + + /// Ctor. + /// used at execution time to obtain query results + /// is the EPL to execute + public EPPreparedQueryImpl(EPPreparedExecuteMethod executeMethod, String epl) + { + _executeMethod = executeMethod; + _epl = epl; + } + + public EPOnDemandQueryResult Execute() + { + return ExecuteInternal(null); + } + + public EPOnDemandQueryResult Execute(ContextPartitionSelector[] contextPartitionSelectors) + { + if (contextPartitionSelectors == null) + { + throw new ArgumentException("No context partition selectors provided"); + } + return ExecuteInternal(contextPartitionSelectors); + } + + private EPOnDemandQueryResult ExecuteInternal(ContextPartitionSelector[] contextPartitionSelectors) + { + try + { + EPPreparedQueryResult result = _executeMethod.Execute(contextPartitionSelectors); + return new EPQueryResultImpl(result); + } + catch (EPStatementException ex) + { + throw; + } + catch (Exception t) + { + String message = "Error executing statement: " + t.Message; + Log.Error("Error executing on-demand statement '" + _epl + "': " + t.Message, t); + throw new EPStatementException(message, _epl, t); + } + } + + public EPPreparedExecuteMethod ExecuteMethod + { + get { return _executeMethod; } + } + + public EventType EventType + { + get { return _executeMethod.EventType; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/EPPreparedQueryResult.cs b/NEsper.Core/NEsper.Core/core/service/EPPreparedQueryResult.cs new file mode 100755 index 000000000..3d39efbb5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/EPPreparedQueryResult.cs @@ -0,0 +1,33 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; + +namespace com.espertech.esper.core.service +{ + /// The result of executing a prepared query. + public class EPPreparedQueryResult + { + /// Ctor. + /// is the type of event produced by the query + /// the result rows + public EPPreparedQueryResult(EventType eventType, EventBean[] result) + { + EventType = eventType; + Result = result; + } + + /// Returs the event type representing the selected columns. + /// metadata + public EventType EventType { get; private set; } + + /// Returns the query result. + /// result rows + public EventBean[] Result { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/EPPreparedStatementImpl.cs b/NEsper.Core/NEsper.Core/core/service/EPPreparedStatementImpl.cs new file mode 100755 index 000000000..57650034c --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/EPPreparedStatementImpl.cs @@ -0,0 +1,101 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.client.soda; +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.core.service +{ + /// + /// Prepared statement implementation that stores the statement object model and a list + /// of substitution parameters, to be mapped into an internal representation upon creation. + /// + [Serializable] + public class EPPreparedStatementImpl : EPPreparedStatement, EPOnDemandPreparedQueryParameterized + { + private readonly EPStatementObjectModel _model; + private readonly IList _subParams; + private readonly String _optionalEPL; + private bool _initialized; + + /// Ctor. + /// is the statement object model + /// is the substitution parameter list + /// the EPL provided if any + public EPPreparedStatementImpl(EPStatementObjectModel model, IList subParams, String optionalEPL) + { + _model = model; + _subParams = subParams; + _optionalEPL = optionalEPL; + } + + public void SetObject(String parameterName, Object value) + { + ValidateNonEmpty(); + if (_subParams[0] is SubstitutionParameterExpressionIndexed) + { + throw new ArgumentException("Substitution parameters are unnamed, please use setObject(index,...) instead"); + } + var found = false; + foreach (SubstitutionParameterExpressionBase subs in _subParams) { + if (((SubstitutionParameterExpressionNamed) subs).Name == parameterName) { + found = true; + subs.Constant = value; + } + } + if (!found) { + throw new ArgumentException("Invalid substitution parameter name of '" + parameterName + "' supplied, failed to find the name"); + } + } + + public void SetObject(int parameterIndex, Object value) + { + ValidateNonEmpty(); + if (_subParams[0] is SubstitutionParameterExpressionNamed) { + throw new ArgumentException("Substitution parameters are named, please use setObject(name,...) instead"); + } + if (parameterIndex < 1) { + throw new ArgumentException("Substitution parameter index starts at 1"); + } + var found = false; + foreach (SubstitutionParameterExpressionBase subs in _subParams) { + if (((SubstitutionParameterExpressionIndexed) subs).Index == parameterIndex) { + found = true; + subs.Constant = value; + } + } + if (!found) { + throw new ArgumentException("Invalid substitution parameter index of " + parameterIndex + " supplied, the maximum for this statement is " + _subParams.Count); + } + } + + /// Returns the statement object model for the prepared statement + /// object model + public EPStatementObjectModel Model + { + get { return _model; } + } + + public string OptionalEPL + { + get { return _optionalEPL; } + } + + private void ValidateNonEmpty() + { + if (_subParams.Count == 0) + { + throw new ArgumentException("Statement does not have substitution parameters indicated by the '?' character"); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/EPQueryResultImpl.cs b/NEsper.Core/NEsper.Core/core/service/EPQueryResultImpl.cs new file mode 100755 index 000000000..b4b5eaf5c --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/EPQueryResultImpl.cs @@ -0,0 +1,42 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; + +namespace com.espertech.esper.core.service +{ + /// Query result. + public class EPQueryResultImpl : EPOnDemandQueryResult + { + private readonly EPPreparedQueryResult _queryResult; + + /// Ctor. + /// is the prepared query + public EPQueryResultImpl(EPPreparedQueryResult queryResult) + { + _queryResult = queryResult; + } + + public IEnumerator GetEnumerator() + { + return ((IEnumerable) _queryResult.Result).GetEnumerator(); + } + + public EventBean[] Array + { + get { return _queryResult.Result; } + } + + public EventType EventType + { + get { return _queryResult.EventType; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/EPRuntimeEventSender.cs b/NEsper.Core/NEsper.Core/core/service/EPRuntimeEventSender.cs new file mode 100755 index 000000000..d107a6026 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/EPRuntimeEventSender.cs @@ -0,0 +1,24 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; + +namespace com.espertech.esper.core.service +{ + /// For use by for direct feed of wrapped events for processing. + public interface EPRuntimeEventSender + { + /// Equivalent to the sendEvent method of EPRuntime, for use to process an known event. + /// is the event object wrapped by an event bean providing the event metadata + void ProcessWrappedEvent(EventBean eventBean); + + /// For processing a routed event. + /// routed event + void RouteEventBean(EventBean theEvent); + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/EPRuntimeImpl.cs b/NEsper.Core/NEsper.Core/core/service/EPRuntimeImpl.cs new file mode 100755 index 000000000..f77c6124f --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/EPRuntimeImpl.cs @@ -0,0 +1,2163 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Xml; +using System.Xml.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.client.context; +using com.espertech.esper.client.dataflow; +using com.espertech.esper.client.hook; +using com.espertech.esper.client.soda; +using com.espertech.esper.client.time; +using com.espertech.esper.client.util; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.compat.threading; +using com.espertech.esper.core.context.mgr; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.start; +using com.espertech.esper.core.thread; +using com.espertech.esper.epl.annotation; +using com.espertech.esper.epl.declexpr; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.subquery; +using com.espertech.esper.epl.expression.visitor; +using com.espertech.esper.epl.metric; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.spec.util; +using com.espertech.esper.epl.variable; +using com.espertech.esper.events.util; +using com.espertech.esper.filter; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.schedule; +using com.espertech.esper.util; + +namespace com.espertech.esper.core.service +{ + using DataMap = IDictionary; + using VariableNotFoundException = com.espertech.esper.client.VariableNotFoundException; + + /// + /// Implements runtime interface. Also accepts timer callbacks for synchronizing time events with regular events sent in. + /// + public class EPRuntimeImpl + : EPRuntimeSPI + , EPRuntimeEventSender + , InternalEventRouteDest + { + private EPServicesContext _services; + private readonly bool _isLatchStatementInsertStream; + private bool _isUsingExternalClocking; + private readonly bool _isPrioritized; + private readonly AtomicLong _routedInternal; + private readonly AtomicLong _routedExternal; + private EventRenderer _eventRenderer; + private InternalEventRouter _internalEventRouter; + private readonly ExprEvaluatorContext _engineFilterAndDispatchTimeContext; + private ThreadWorkQueue _threadWorkQueue; + + public event EventHandler UnmatchedEvent; + + /// + /// Data that remains local to the thread. + /// + + private IThreadLocal _threadLocalData; + + #region Nested type: ThreadLocalData + + /// + /// Group of data that is associated with the thread. + /// + + private class ThreadLocalData + { + internal List MatchesArrayThreadLocal; + internal IDictionary MatchesPerStmtThreadLocal; + internal ArrayBackedCollection ScheduleArrayThreadLocal; + internal IDictionary SchedulePerStmtThreadLocal; + } + + /// + /// Gets the local data. + /// + /// The local data. + +#if NET45 + //[MethodImplOptions.AggressiveInlining] +#endif + private ThreadLocalData ThreadData + { + get + { + return _threadLocalData.GetOrCreate(); + } + } + + private ArrayBackedCollection ScheduleArray + { + get { return ThreadData.ScheduleArrayThreadLocal; } + } + + private IDictionary SchedulePerStmt + { + get { return ThreadData.SchedulePerStmtThreadLocal; } + } + + #endregion + + /// Constructor. + /// references to services + public EPRuntimeImpl(EPServicesContext services) + { + _services = services; + _threadWorkQueue = new ThreadWorkQueue(); + _isLatchStatementInsertStream = _services.EngineSettingsService.EngineSettings.Threading.IsInsertIntoDispatchPreserveOrder; + _isUsingExternalClocking = !_services.EngineSettingsService.EngineSettings.Threading.IsInternalTimerEnabled; + _isPrioritized = services.EngineSettingsService.EngineSettings.Execution.IsPrioritized; + _routedInternal = new AtomicLong(); + _routedExternal = new AtomicLong(); + + var expressionResultCacheService = services.ExpressionResultCacheSharable; + _engineFilterAndDispatchTimeContext = new ProxyExprEvaluatorContext + { + ProcTimeProvider = () => services.SchedulingService, + ProcExpressionResultCacheService = () => expressionResultCacheService, + ProcAgentInstanceId = () => -1, + ProcContextProperties = () => null, + ProcAllocateAgentInstanceScriptContext = () => null, + ProcStatementName = () => null, + ProcEngineURI = () => null, + ProcStatementId = () => -1, + ProcAgentInstanceLock = () => null, + ProcStatementType = () => null, + ProcTableExprEvaluatorContext = () => + { + throw new UnsupportedOperationException("Table-access evaluation is not supported in this expression"); + }, + ProcStatementUserObject = () => null + }; + + InitThreadLocals(); + + services.ThreadingService.InitThreading(services, this); + } + + /// + /// Removes all unmatched event handlers. + /// + + public void RemoveAllUnmatchedEventHandlers() + { + UnmatchedEvent = null; + } + + /// + /// Creates a local data object. + /// + /// + + private ThreadLocalData CreateLocalData() + { + var threadLocalData = new ThreadLocalData + { + MatchesArrayThreadLocal = new List(100), + ScheduleArrayThreadLocal = new ArrayBackedCollection(100) + }; + + if (_isPrioritized) + { + threadLocalData.MatchesPerStmtThreadLocal = + new OrderedDictionary(new EPStatementAgentInstanceHandlePrioritySort()); + threadLocalData.SchedulePerStmtThreadLocal = + new OrderedDictionary(new EPStatementAgentInstanceHandlePrioritySort()); + } + else + { + threadLocalData.MatchesPerStmtThreadLocal = + new Dictionary(10000); + threadLocalData.SchedulePerStmtThreadLocal = + new Dictionary(10000); + } + + return threadLocalData; + } + + + /// Sets the route for events to use + /// router + public InternalEventRouter InternalEventRouter + { + set { _internalEventRouter = value; } + } + + public long RoutedInternal + { + get { return _routedInternal.Get(); } + } + + public long RoutedExternal + { + get { return _routedExternal.Get(); } + } + + public void TimerCallback() + { + var msec = _services.TimeSource.GetTimeMillis(); + + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled && (ExecutionPathDebugLog.IsTimerDebugEnabled))) + { + Log.Debug(".timerCallback Evaluating scheduled callbacks, time is " + msec); + } + + SendEvent(new CurrentTimeEvent(msec)); + } + + public void SendEventAvro(Object avroGenericDataDotRecord, String avroEventTypeName) + { + if (avroGenericDataDotRecord == null) + { + throw new ArgumentException("Invalid null event object", "avroGenericDataDotRecord"); + } + + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled)) + { + Log.Debug(".sendMap Processing event " + avroGenericDataDotRecord.ToString()); + } + + if ((ThreadingOption.IsThreadingEnabled) && (_services.ThreadingService.IsInboundThreading)) + { + _services.ThreadingService.SubmitInbound( + new InboundUnitSendAvro(avroGenericDataDotRecord, avroEventTypeName, _services, this).Run); + } + else + { + // Process event + EventBean eventBean = WrapEventAvro(avroGenericDataDotRecord, avroEventTypeName); + ProcessWrappedEvent(eventBean); + } + } + + public void SendEvent(Object theEvent) + { + if (theEvent == null) + { + Log.Error(".sendEvent Null object supplied"); + return; + } + + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled)) + { + if ((!(theEvent is CurrentTimeEvent)) || (ExecutionPathDebugLog.IsTimerDebugEnabled)) + { + Log.Debug(".sendEvent Processing event " + theEvent); + } + } + + // Process event + if ((ThreadingOption.IsThreadingEnabledValue) && (_services.ThreadingService.IsInboundThreading)) + { + _services.ThreadingService.SubmitInbound( + new InboundUnitSendEvent(theEvent, this).Run); + } + else + { + ProcessEvent(theEvent); + } + } + + public void SendEvent(XmlNode document) + { + if (document == null) + { + Log.Error(".sendEvent Null object supplied"); + return; + } + + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled)) + { + Log.Debug(".sendEvent Processing DOM node event " + document); + } + + // Process event + if ((ThreadingOption.IsThreadingEnabledValue) && (_services.ThreadingService.IsInboundThreading)) + { + _services.ThreadingService.SubmitInbound( + new InboundUnitSendDOM(document, _services, this).Run); + } + else + { + // Get it wrapped up, process event + var eventBean = WrapEvent(document); + ProcessEvent(eventBean); + } + } + + /// + /// Send an event represented by a LINQ element to the event stream processing runtime. + /// + /// Use the route method for sending events into the runtime from within + /// event handler code. to avoid the possibility of a stack overflow due to nested calls to + /// SendEvent. + /// + /// The element. + + public void SendEvent(XElement element) + { + if (element == null) + { + Log.Error(".sendEvent Null object supplied"); + return; + } + + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled)) + { + Log.Debug(".sendEvent Processing DOM node event {0}", element); + } + + // Process event + if ((ThreadingOption.IsThreadingEnabledValue) && (_services.ThreadingService.IsInboundThreading)) + { + _services.ThreadingService.SubmitInbound( + new InboundUnitSendLINQ(element, _services, this).Run); + } + else + { + // Get it wrapped up, process event + var eventBean = WrapEvent(element); + ProcessEvent(eventBean); + } + } + + public EventBean WrapEvent(XmlNode node) + { + return _services.EventAdapterService.AdapterForDOM(node); + } + + public void Route(XmlNode document) + { + if (document == null) + { + Log.Error(".sendEvent Null object supplied"); + return; + } + + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled)) + { + Log.Debug(".sendEvent Processing DOM node event " + document); + } + + // Get it wrapped up, process event + var eventBean = _services.EventAdapterService.AdapterForDOM(document); + _threadWorkQueue.AddBack(eventBean); + } + + public void RouteAvro(Object avroGenericDataDotRecord, String avroEventTypeName) + { + if (avroGenericDataDotRecord == null) { + Log.Error(".sendEvent Null object supplied"); + return; + } + + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled)) + { + Log.Debug(".sendEvent Processing Avro event " + avroGenericDataDotRecord); + } + + // Get it wrapped up, process event + EventBean eventBean = _services.EventAdapterService.AdapterForAvro(avroGenericDataDotRecord, avroEventTypeName); + _threadWorkQueue.AddBack(eventBean); + } + + public EventBean WrapEvent(XElement node) + { + return _services.EventAdapterService.AdapterForDOM(node); + } + + public EventBean WrapEventAvro(Object avroGenericDataDotRecord, String eventTypeName) + { + return _services.EventAdapterService.AdapterForAvro(avroGenericDataDotRecord, eventTypeName); + } + + public void Route(XElement element) + { + if (element == null) + { + Log.Fatal(".sendEvent Null object supplied"); + return; + } + + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled)) + { + Log.Debug(".sendEvent Processing DOM node event " + element); + } + + // Get it wrapped up, process event + var eventBean = _services.EventAdapterService.AdapterForDOM(element); + _threadWorkQueue.AddBack(eventBean); + } + + public void SendEvent(DataMap map, String mapEventTypeName) + { + if (map == null) + { + throw new ArgumentException("Invalid null event object"); + } + + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled)) + { + Log.Debug(".sendMap Processing event " + map); + } + + if ((ThreadingOption.IsThreadingEnabledValue) && (_services.ThreadingService.IsInboundThreading)) + { + _services.ThreadingService.SubmitInbound( + new InboundUnitSendMap(map, mapEventTypeName, _services, this).Run); + } + else + { + // Process event + var eventBean = WrapEvent(map, mapEventTypeName); + ProcessWrappedEvent(eventBean); + } + } + + public EventBean WrapEvent(DataMap map, String eventTypeName) + { + return _services.EventAdapterService.AdapterForMap(map, eventTypeName); + } + + public void SendEvent(Object[] propertyValues, String objectArrayEventTypeName) + { + if (propertyValues == null) + { + throw new ArgumentException("Invalid null event object"); + } + + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled)) + { + Log.Debug(".sendMap Processing event " + propertyValues.Render()); + } + + if ((ThreadingOption.IsThreadingEnabledValue) && (_services.ThreadingService.IsInboundThreading)) + { + _services.ThreadingService.SubmitInbound( + new InboundUnitSendObjectArray(propertyValues, objectArrayEventTypeName, _services, this).Run); + } + else + { + // Process event + var eventBean = WrapEvent(propertyValues, objectArrayEventTypeName); + ProcessWrappedEvent(eventBean); + } + } + + public EventBean WrapEvent(Object[] objectArray, String eventTypeName) + { + return _services.EventAdapterService.AdapterForObjectArray(objectArray, eventTypeName); + } + + public void Route(DataMap map, String eventTypeName) + { + if (map == null) + { + throw new ArgumentException("Invalid null event object"); + } + + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled)) + { + Log.Debug(".route Processing event " + map); + } + + // Process event + var theEvent = _services.EventAdapterService.AdapterForMap(map, eventTypeName); + if (_internalEventRouter.HasPreprocessing) + { + theEvent = _internalEventRouter.Preprocess(theEvent, _engineFilterAndDispatchTimeContext); + if (theEvent == null) + { + return; + } + } + _threadWorkQueue.AddBack(theEvent); + } + + public void Route(Object[] objectArray, String eventTypeName) + { + if (objectArray == null) + { + throw new ArgumentException("Invalid null event object"); + } + + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled)) + { + Log.Debug(".route Processing event " + objectArray.Render()); + } + + // Process event + var theEvent = _services.EventAdapterService.AdapterForObjectArray(objectArray, eventTypeName); + if (_internalEventRouter.HasPreprocessing) + { + theEvent = _internalEventRouter.Preprocess(theEvent, _engineFilterAndDispatchTimeContext); + if (theEvent == null) + { + return; + } + } + _threadWorkQueue.AddBack(theEvent); + } + + public long NumEventsEvaluated + { + get { return _services.FilterService.NumEventsEvaluated; } + } + + public void ResetStats() + { + _services.FilterService.ResetStats(); + _routedInternal.Set(0); + _routedExternal.Set(0); + } + + public void RouteEventBean(EventBean theEvent) + { + _threadWorkQueue.AddBack(theEvent); + } + + public void Route(Object theEvent) + { + _routedExternal.IncrementAndGet(); + + if (_internalEventRouter.HasPreprocessing) + { + var eventBean = _services.EventAdapterService.AdapterForObject(theEvent); + theEvent = _internalEventRouter.Preprocess(eventBean, _engineFilterAndDispatchTimeContext); + if (theEvent == null) + { + return; + } + } + + _threadWorkQueue.AddBack(theEvent); + } + + // Internal route of events via insert-into, holds a statement lock + public void Route(EventBean theEvent, EPStatementHandle epStatementHandle, bool addToFront) + { + if (InstrumentationHelper.ENABLED) + InstrumentationHelper.Get().QRouteBetweenStmt(theEvent, epStatementHandle, addToFront); + try + { + _routedInternal.IncrementAndGet(); + + if (_isLatchStatementInsertStream) + { + if (addToFront) + { + var latch = epStatementHandle.InsertIntoFrontLatchFactory.NewLatch(theEvent); + _threadWorkQueue.AddFront(latch); + } + else + { + var latch = epStatementHandle.InsertIntoBackLatchFactory.NewLatch(theEvent); + _threadWorkQueue.AddBack(latch); + } + } + else + { + if (addToFront) + { + _threadWorkQueue.AddFront(theEvent); + } + else + { + _threadWorkQueue.AddBack(theEvent); + } + } + } + finally + { + if (InstrumentationHelper.ENABLED) + InstrumentationHelper.Get().ARouteBetweenStmt(); + } + } + + /// Process an unwrapped event. + /// to process. + public void ProcessEvent(Object theEvent) + { + if (theEvent is TimerEvent) + { + ProcessTimeEvent((TimerEvent)theEvent); + return; + } + + EventBean eventBean; + + if (theEvent is EventBean) + { + eventBean = (EventBean)theEvent; + } + else + { + eventBean = WrapEvent(theEvent); + } + + ProcessWrappedEvent(eventBean); + } + + public EventBean WrapEvent(Object theEvent) + { + return _services.EventAdapterService.AdapterForObject(theEvent); + } + + public void ProcessWrappedEvent(EventBean eventBean) + { + if (InstrumentationHelper.ENABLED) + InstrumentationHelper.Get().QStimulantEvent(eventBean, _services.EngineURI); + + try + { + if (_internalEventRouter.HasPreprocessing) + { + eventBean = _internalEventRouter.Preprocess(eventBean, _engineFilterAndDispatchTimeContext); + if (eventBean == null) + { + return; + } + } + + // Acquire main processing lock which locks out statement management + if (InstrumentationHelper.ENABLED) + InstrumentationHelper.Get().QEvent(eventBean, _services.EngineURI, true); + + try + { + using (_services.EventProcessingRWLock.AcquireReadLock()) + { + try + { + ProcessMatches(eventBean); + } + catch (Exception ex) + { + ThreadData.MatchesArrayThreadLocal.Clear(); + throw new EPException(ex); + } + } + } + finally + { + if (InstrumentationHelper.ENABLED) + InstrumentationHelper.Get().AEvent(); + } + + // Dispatch results to listeners + // Done outside of the read-lock to prevent lockups when listeners create statements + Dispatch(); + + // Work off the event queue if any events accumulated in there via a Route() or insert-into + ProcessThreadWorkQueue(); + } + finally + { + if (InstrumentationHelper.ENABLED) + InstrumentationHelper.Get().AStimulantEvent(); + } + } + + private void ProcessTimeEvent(TimerEvent theEvent) + { + if (theEvent is TimerControlEvent) + { + var timerControlEvent = (TimerControlEvent)theEvent; + if (timerControlEvent.ClockType == TimerControlEvent.ClockTypeEnum.CLOCK_INTERNAL) + { + // Start internal clock which supplies CurrentTimeEvent events every 100ms + // This may be done without delay thus the write lock indeed must be reentrant. + if (_services.ConfigSnapshot.EngineDefaults.TimeSource.TimeUnit != TimeUnit.MILLISECONDS) + { + throw new EPException("Internal timer requires millisecond time resolution"); + } + + _services.TimerService.StartInternalClock(); + _isUsingExternalClocking = false; + } + else + { + // Stop internal clock, for unit testing and for external clocking + _services.TimerService.StopInternalClock(true); + _isUsingExternalClocking = true; + } + + return; + } + + if (theEvent is CurrentTimeEvent) + { + var current = (CurrentTimeEvent)theEvent; + var timeInMillis = current.Time; + + if (InstrumentationHelper.ENABLED) + InstrumentationHelper.Get().QStimulantTime(timeInMillis, _services.EngineURI); + + try + { + // Evaluation of all time events is protected from statement management + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled) && (ExecutionPathDebugLog.IsTimerDebugEnabled)) + { + Log.Debug(".processTimeEvent Setting time and evaluating schedules for time " + timeInMillis); + } + + if (_isUsingExternalClocking && (timeInMillis == _services.SchedulingService.Time)) + { + if (Log.IsWarnEnabled) + { + Log.Warn("Duplicate time event received for currentTime " + timeInMillis); + } + } + _services.SchedulingService.Time = timeInMillis; + + if (MetricReportingPath.IsMetricsEnabled) + { + _services.MetricsReportingService.ProcessTimeEvent(timeInMillis); + } + + ProcessSchedule(timeInMillis); + + // Let listeners know of results + Dispatch(); + + // Work off the event queue if any events accumulated in there via a Route() + ProcessThreadWorkQueue(); + } + finally + { + if (InstrumentationHelper.ENABLED) + InstrumentationHelper.Get().AStimulantTime(); + } + + return; + } + + // handle time span + var span = (CurrentTimeSpanEvent)theEvent; + var targetTime = span.TargetTime; + var currentTime = _services.SchedulingService.Time; + var optionalResolution = span.OptionalResolution; + + if (_isUsingExternalClocking && (targetTime < currentTime)) + { + if (Log.IsWarnEnabled) + { + Log.Warn("Past or current time event received for currentTime " + targetTime); + } + } + + // Evaluation of all time events is protected from statement management + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled) && (ExecutionPathDebugLog.IsTimerDebugEnabled)) + { + Log.Debug(".processTimeEvent Setting time span and evaluating schedules for time " + targetTime + " optional resolution " + span.OptionalResolution); + } + + while (currentTime < targetTime) + { + if ((optionalResolution != null) && (optionalResolution > 0)) + { + currentTime += optionalResolution.Value; + } + else + { + var nearest = _services.SchedulingService.NearestTimeHandle; + currentTime = nearest == null ? targetTime : nearest.Value; + } + if (currentTime > targetTime) + { + currentTime = targetTime; + } + + if (InstrumentationHelper.ENABLED) + InstrumentationHelper.Get().QStimulantTime(currentTime, _services.EngineURI); + + try + { + // Evaluation of all time events is protected from statement management + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled) && + (ExecutionPathDebugLog.IsTimerDebugEnabled)) + { + Log.Debug(".processTimeEvent Setting time and evaluating schedules for time " + currentTime); + } + + _services.SchedulingService.Time = currentTime; + + if (MetricReportingPath.IsMetricsEnabled) + { + _services.MetricsReportingService.ProcessTimeEvent(currentTime); + } + + ProcessSchedule(currentTime); + + // Let listeners know of results + Dispatch(); + + // Work off the event queue if any events accumulated in there via a Route() + ProcessThreadWorkQueue(); + } + finally + { + if (InstrumentationHelper.ENABLED) + InstrumentationHelper.Get().AStimulantTime(); + } + } + } + + private void ProcessSchedule(long time) + { + if (InstrumentationHelper.ENABLED) + InstrumentationHelper.Get().QTime(time, _services.EngineURI); + + try + { + var handles = ScheduleArray; + + // Evaluation of schedules is protected by an optional scheduling service lock and then the engine lock + // We want to stay in this order for allowing the engine lock as a second-order lock to the + // services own lock, if it has one. + using (_services.EventProcessingRWLock.AcquireReadLock()) + { + _services.SchedulingService.Evaluate(handles); + } + + using (_services.EventProcessingRWLock.AcquireReadLock()) + { + try + { + ProcessScheduleHandles(handles); + } + catch (Exception) + { + handles.Clear(); + throw; + } + } + } + finally + { + if (InstrumentationHelper.ENABLED) + InstrumentationHelper.Get().ATime(); + } + } + + public void ProcessScheduleHandles(ArrayBackedCollection handles) + { + if (ThreadLogUtil.ENABLED_TRACE) + { + ThreadLogUtil.Trace("Found schedules for", handles.Count); + } + + if (handles.Count == 0) + { + return; + } + + // handle 1 result separately for performance reasons + if (handles.Count == 1) + { + var handleArray = handles.Array; + var handle = (EPStatementHandleCallback)handleArray[0]; + + if ((MetricReportingPath.IsMetricsEnabled) && (handle.AgentInstanceHandle.StatementHandle.MetricsHandle.IsEnabled)) + { + handle.AgentInstanceHandle.StatementHandle.MetricsHandle.Call( + _services.MetricsReportingService.PerformanceCollector, + () => ProcessStatementScheduleSingle(handle, _services)); + } + else + { + if ((ThreadingOption.IsThreadingEnabledValue) && (_services.ThreadingService.IsTimerThreading)) + { + _services.ThreadingService.SubmitTimerWork( + new TimerUnitSingle(_services, this, handle).Run); + } + else + { + ProcessStatementScheduleSingle(handle, _services); + } + } + + handles.Clear(); + return; + } + + var matchArray = handles.Array; + var entryCount = handles.Count; + + // sort multiple matches for the event into statements + var stmtCallbacks = SchedulePerStmt; + stmtCallbacks.Clear(); + for (var i = 0; i < entryCount; i++) + { + var handleCallback = (EPStatementHandleCallback)matchArray[i]; + var handle = handleCallback.AgentInstanceHandle; + var callback = handleCallback.ScheduleCallback; + + var entry = stmtCallbacks.Get(handle); + + // This statement has not been encountered before + if (entry == null) + { + stmtCallbacks.Put(handle, callback); + continue; + } + + // This statement has been encountered once before + if (entry is ScheduleHandleCallback) + { + var existingCallback = (ScheduleHandleCallback)entry; + var stmtEntries = new LinkedList(); + stmtEntries.AddLast(existingCallback); + stmtEntries.AddLast(callback); + stmtCallbacks.Put(handle, stmtEntries); + continue; + } + + // This statement has been encountered more then once before + var entries = (LinkedList)entry; + entries.AddLast(callback); + } + handles.Clear(); + + foreach (var entry in stmtCallbacks) + { + var handle = entry.Key; + var callbackObject = entry.Value; + + if ((MetricReportingPath.IsMetricsEnabled) && (handle.StatementHandle.MetricsHandle.IsEnabled)) + { + var numInput = callbackObject is ICollection ? ((ICollection)callbackObject).Count : 0; + + handle.StatementHandle.MetricsHandle.Call( + _services.MetricsReportingService.PerformanceCollector, + () => ProcessStatementScheduleMultiple(handle, callbackObject, _services), + numInput); + } + else + { + if ((ThreadingOption.IsThreadingEnabledValue) && (_services.ThreadingService.IsTimerThreading)) + { + _services.ThreadingService.SubmitTimerWork( + new TimerUnitMultiple(_services, this, handle, callbackObject).Run); + } + else + { + ProcessStatementScheduleMultiple(handle, callbackObject, _services); + } + } + + if ((_isPrioritized) && (handle.IsPreemptive)) + { + break; + } + } + } + + /// + /// Works off the thread's work queue. + /// + public void ProcessThreadWorkQueue() + { + var queues = _threadWorkQueue.ThreadQueue; + + if (queues.FrontQueue.Peek() == null) + { + var haveDispatched = _services.NamedWindowDispatchService.Dispatch(); + if (haveDispatched) + { + // Dispatch results to listeners + Dispatch(); + + if (queues.FrontQueue.Peek() != null) + { + ProcessThreadWorkQueueFront(queues); + } + } + } + else + { + ProcessThreadWorkQueueFront(queues); + } + + Object item; + while ((item = queues.BackQueue.Poll()) != null) + { + if (item is InsertIntoLatchSpin) + { + ProcessThreadWorkQueueLatchedSpin((InsertIntoLatchSpin)item); + } + else if (item is InsertIntoLatchWait) + { + ProcessThreadWorkQueueLatchedWait((InsertIntoLatchWait)item); + } + else + { + ProcessThreadWorkQueueUnlatched(item); + } + + var haveDispatched = _services.NamedWindowDispatchService.Dispatch(); + if (haveDispatched) + { + Dispatch(); + } + + if (queues.FrontQueue.Peek() != null) + { + ProcessThreadWorkQueueFront(queues); + } + } + } + + private void ProcessThreadWorkQueueFront(DualWorkQueue queues) + { + Object item; + while ((item = queues.FrontQueue.Poll()) != null) + { + if (item is InsertIntoLatchSpin) + { + ProcessThreadWorkQueueLatchedSpin((InsertIntoLatchSpin)item); + } + else if (item is InsertIntoLatchWait) + { + ProcessThreadWorkQueueLatchedWait((InsertIntoLatchWait)item); + } + else + { + ProcessThreadWorkQueueUnlatched(item); + } + + var haveDispatched = _services.NamedWindowDispatchService.Dispatch(); + if (haveDispatched) + { + Dispatch(); + } + } + } + + private void ProcessThreadWorkQueueLatchedWait(InsertIntoLatchWait insertIntoLatch) + { + // wait for the latch to complete + var eventBean = insertIntoLatch.Await(); + + if (InstrumentationHelper.ENABLED) + InstrumentationHelper.Get().QEvent(eventBean, _services.EngineURI, false); + + try + { + using (_services.EventProcessingRWLock.AcquireReadLock()) + { + try + { + ProcessMatches(eventBean); + } + catch (Exception) + { + ThreadData.MatchesArrayThreadLocal.Clear(); + throw; + } + finally + { + insertIntoLatch.Done(); + } + } + } + finally + { + if (InstrumentationHelper.ENABLED) + InstrumentationHelper.Get().AEvent(); + } + + Dispatch(); + } + + private void ProcessThreadWorkQueueLatchedSpin(InsertIntoLatchSpin insertIntoLatch) + { + // wait for the latch to complete + var eventBean = insertIntoLatch.Await(); + + if (InstrumentationHelper.ENABLED) + InstrumentationHelper.Get().QEvent(eventBean, _services.EngineURI, false); + + try + { + using (_services.EventProcessingRWLock.AcquireReadLock()) + { + try + { + ProcessMatches(eventBean); + } + catch (Exception) + { + ThreadData.MatchesArrayThreadLocal.Clear(); + throw; + } + finally + { + insertIntoLatch.Done(); + } + } + } + finally + { + if (InstrumentationHelper.ENABLED) + InstrumentationHelper.Get().AEvent(); + } + + Dispatch(); + } + + private void ProcessThreadWorkQueueUnlatched(Object item) + { + EventBean eventBean; + if (item is EventBean) + { + eventBean = (EventBean)item; + } + else + { + eventBean = _services.EventAdapterService.AdapterForObject(item); + } + + if (InstrumentationHelper.ENABLED) + InstrumentationHelper.Get().QEvent(eventBean, _services.EngineURI, false); + + try + { + using (_services.EventProcessingRWLock.AcquireReadLock()) + { + try + { + ProcessMatches(eventBean); + } + catch (Exception) + { + ThreadData.MatchesArrayThreadLocal.Clear(); + throw; + } + } + } + finally + { + if (InstrumentationHelper.ENABLED) + InstrumentationHelper.Get().AEvent(); + } + + Dispatch(); + } + + protected internal void ProcessMatches(EventBean theEvent) + { + var localData = ThreadData; + + // get matching filters + var matches = localData.MatchesArrayThreadLocal; + var version = _services.FilterService.Evaluate(theEvent, matches); + + if (ThreadLogUtil.ENABLED_TRACE) + { + ThreadLogUtil.Trace("Found matches for underlying ", matches.Count, theEvent.Underlying); + } + + if (matches.Count == 0) + { + if (UnmatchedEvent != null) + { + using (_services.EventProcessingRWLock.ReadLock.ReleaseAcquire()) // Allow listener to create new statements + { + try + { + UnmatchedEvent.Invoke(this, new UnmatchedEventArgs(theEvent)); + } + catch (Exception ex) + { + Log.Error("Exception thrown by unmatched listener: " + ex.Message, ex); + } + } + } + return; + } + + var stmtCallbacks = localData.MatchesPerStmtThreadLocal; + var matchesCount = matches.Count; + + for (var i = 0; i < matchesCount; i++) + { + var handleCallback = (EPStatementHandleCallback)matches[i]; + 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) + { + object callbacks; + if (!stmtCallbacks.TryGetValue(handle, out callbacks)) + { + stmtCallbacks.Put(handle, handleCallback.FilterCallback); + } + else if (callbacks is LinkedList) + { + var q = (LinkedList)callbacks; + q.AddLast(handleCallback.FilterCallback); + } + else + { + var q = new LinkedList(); + q.AddLast((FilterHandleCallback)callbacks); + q.AddLast(handleCallback.FilterCallback); + stmtCallbacks.Put(handle, q); + } + + continue; + } + + if ((MetricReportingPath.IsMetricsEnabled) && (handle.StatementHandle.MetricsHandle.IsEnabled)) + { + handle.StatementHandle.MetricsHandle.Call( + _services.MetricsReportingService.PerformanceCollector, + () => ProcessStatementFilterSingle(handle, handleCallback, theEvent, version)); + } + else + { + if ((ThreadingOption.IsThreadingEnabledValue) && (_services.ThreadingService.IsRouteThreading)) + { + _services.ThreadingService.SubmitRoute( + new RouteUnitSingle(this, handleCallback, theEvent, version).Run); + } + else + { + ProcessStatementFilterSingle(handle, handleCallback, theEvent, version); + } + } + } + + matches.Clear(); + + if (stmtCallbacks.Count == 0) + { + return; + } + + foreach (var entry in stmtCallbacks) + { + var handle = entry.Key; + var callbackList = entry.Value; + + if ((MetricReportingPath.IsMetricsEnabled) && (handle.StatementHandle.MetricsHandle.IsEnabled)) + { + var count = 1; + if (callbackList is ICollection) + count = ((ICollection)callbackList).Count; + + handle.StatementHandle.MetricsHandle.Call( + _services.MetricsReportingService.PerformanceCollector, + () => ProcessStatementFilterMultiple(handle, callbackList, theEvent, version), + count); + } + else + { + if ((ThreadingOption.IsThreadingEnabledValue) && (_services.ThreadingService.IsRouteThreading)) + { + _services.ThreadingService.SubmitRoute( + new RouteUnitMultiple(this, callbackList, theEvent, handle, version).Run); + } + else + { + ProcessStatementFilterMultiple(handle, callbackList, theEvent, version); + } + + if ((_isPrioritized) && (handle.IsPreemptive)) + { + break; + } + } + } + stmtCallbacks.Clear(); + } + + /// + /// Processing multiple schedule matches for a statement. + /// + /// statement handle + /// object containing matches + /// engine services + public static void ProcessStatementScheduleMultiple(EPStatementAgentInstanceHandle handle, Object callbackObject, EPServicesContext services) + { + if (InstrumentationHelper.ENABLED) + InstrumentationHelper.Get().QTimeCP(handle, services.SchedulingService.Time); + + try + { + using (handle.StatementAgentInstanceLock.AcquireWriteLock()) + { + try + { + if (!handle.IsDestroyed) + { + if (handle.HasVariables) + { + services.VariableService.SetLocalVersion(); + } + + if (callbackObject is LinkedList) + { + var callbackList = (LinkedList)callbackObject; + foreach (var callback in callbackList) + { + callback.ScheduledTrigger(services.EngineLevelExtensionServicesContext); + } + } + else + { + var callback = (ScheduleHandleCallback)callbackObject; + callback.ScheduledTrigger(services.EngineLevelExtensionServicesContext); + } + + // internal join processing, if applicable + handle.InternalDispatch(); + } + } + catch (Exception ex) + { + services.ExceptionHandlingService.HandleException(ex, handle, ExceptionHandlerExceptionType.PROCESS, null); + } + finally + { + if (handle.HasTableAccess) + { + services.TableService.TableExprEvaluatorContext.ReleaseAcquiredLocks(); + } + } + } + } + finally + { + if (InstrumentationHelper.ENABLED) + InstrumentationHelper.Get().ATimeCP(); + } + } + + /// + /// Processing single schedule matche for a statement. + /// + /// statement handle + /// engine services + public static void ProcessStatementScheduleSingle(EPStatementHandleCallback handle, EPServicesContext services) + { + if (InstrumentationHelper.ENABLED) + InstrumentationHelper.Get().QTimeCP(handle.AgentInstanceHandle, services.SchedulingService.Time); + + try + { + using (handle.AgentInstanceHandle.StatementAgentInstanceLock.AcquireWriteLock()) + { + try + { + if (!handle.AgentInstanceHandle.IsDestroyed) + { + if (handle.AgentInstanceHandle.HasVariables) + { + services.VariableService.SetLocalVersion(); + } + + handle.ScheduleCallback.ScheduledTrigger(services.EngineLevelExtensionServicesContext); + handle.AgentInstanceHandle.InternalDispatch(); + } + } + catch (Exception ex) + { + services.ExceptionHandlingService.HandleException(ex, handle.AgentInstanceHandle, ExceptionHandlerExceptionType.PROCESS, null); + } + finally + { + if (handle.AgentInstanceHandle.HasTableAccess) + { + services.TableService.TableExprEvaluatorContext.ReleaseAcquiredLocks(); + } + } + } + } + finally + { + if (InstrumentationHelper.ENABLED) + InstrumentationHelper.Get().ATimeCP(); + } + } + + /// + /// Processing multiple filter matches for a statement. + /// + /// statement handle + /// object containing callbacks + /// to process + /// filter version + public void ProcessStatementFilterMultiple(EPStatementAgentInstanceHandle handle, Object callbackList, EventBean theEvent, long version) + { + if (InstrumentationHelper.ENABLED) + InstrumentationHelper.Get().QEventCP(theEvent, handle, _services.SchedulingService.Time); + + try + { + using (handle.StatementAgentInstanceLock.AcquireWriteLock()) + { + try + { + if (handle.HasVariables) + { + _services.VariableService.SetLocalVersion(); + } + if (!handle.IsCurrentFilter(version)) + { + var handled = false; + if (handle.FilterFaultHandler != null) + { + handled = handle.FilterFaultHandler.HandleFilterFault(theEvent, version); + } + if (!handled) + { + HandleFilterFault(handle, theEvent); + } + } + else + { + if (callbackList is ICollection) + { + var callbacks = (ICollection)callbackList; + handle.MultiMatchHandler.Handle(callbacks, theEvent); + } + else + { + var single = (FilterHandleCallback)callbackList; + single.MatchFound(theEvent, null); + } + + // internal join processing, if applicable + handle.InternalDispatch(); + } + } + catch (Exception ex) + { + _services.ExceptionHandlingService.HandleException(ex, handle, ExceptionHandlerExceptionType.PROCESS, theEvent); + } + finally + { + if (handle.HasTableAccess) + { + _services.TableService.TableExprEvaluatorContext.ReleaseAcquiredLocks(); + } + } + } + } + finally + { + if (InstrumentationHelper.ENABLED) + InstrumentationHelper.Get().AEventCP(); + } + } + + /// Process a single match. + /// statement + /// callback + /// event to indicate + /// filter version + public void ProcessStatementFilterSingle(EPStatementAgentInstanceHandle handle, EPStatementHandleCallback handleCallback, EventBean theEvent, long version) + { + if (InstrumentationHelper.ENABLED) + InstrumentationHelper.Get().QEventCP(theEvent, handle, _services.SchedulingService.Time); + + try + { + using (handle.StatementAgentInstanceLock.AcquireWriteLock()) + { + try + { + if (handle.HasVariables) + { + _services.VariableService.SetLocalVersion(); + } + if (!handle.IsCurrentFilter(version)) + { + var handled = false; + if (handle.FilterFaultHandler != null) + { + handled = handle.FilterFaultHandler.HandleFilterFault(theEvent, version); + } + if (!handled) + { + HandleFilterFault(handle, theEvent); + } + } + else + { + handleCallback.FilterCallback.MatchFound(theEvent, null); + } + + // internal join processing, if applicable + handle.InternalDispatch(); + } + catch (Exception ex) + { + _services.ExceptionHandlingService.HandleException(ex, handle, ExceptionHandlerExceptionType.PROCESS, theEvent); + } + finally + { + if (handle.HasTableAccess) + { + _services.TableService.TableExprEvaluatorContext.ReleaseAcquiredLocks(); + } + } + } + } + finally + { + if (InstrumentationHelper.ENABLED) + InstrumentationHelper.Get().AEventCP(); + } + } + + protected internal void HandleFilterFault(EPStatementAgentInstanceHandle faultingHandle, EventBean theEvent) + { + var callbacksForStatement = new ArrayDeque(); + var version = _services.FilterService.Evaluate(theEvent, callbacksForStatement, faultingHandle.StatementId); + + if (callbacksForStatement.Count == 1) + { + var handleCallback = (EPStatementHandleCallback)callbacksForStatement.First; + ProcessStatementFilterSingle(handleCallback.AgentInstanceHandle, handleCallback, theEvent, version); + return; + } + if (callbacksForStatement.Count == 0) + { + return; + } + + IDictionary stmtCallbacks; + if (_isPrioritized) + { + stmtCallbacks = new SortedDictionary(EPStatementAgentInstanceHandleComparator.Instance); + } + else + { + stmtCallbacks = new Dictionary(); + } + + foreach (var filterHandle in callbacksForStatement) + { + var handleCallback = (EPStatementHandleCallback)filterHandle; + var handle = handleCallback.AgentInstanceHandle; + + if (handle.CanSelfJoin || _isPrioritized) + { + var callbacks = stmtCallbacks.Get(handle); + if (callbacks == null) + { + stmtCallbacks.Put(handle, handleCallback.FilterCallback); + } + else if (callbacks is LinkedList) + { + var q = (LinkedList)callbacks; + q.AddLast(handleCallback.FilterCallback); + } + else + { + var q = new LinkedList(); + q.AddLast((FilterHandleCallback)callbacks); + q.AddLast(handleCallback.FilterCallback); + stmtCallbacks.Put(handle, q); + } + continue; + } + + ProcessStatementFilterSingle(handle, handleCallback, theEvent, version); + } + + if (stmtCallbacks.IsEmpty()) + { + return; + } + + foreach (var entry in stmtCallbacks) + { + var handle = entry.Key; + var callbackList = entry.Value; + + ProcessStatementFilterMultiple(handle, callbackList, theEvent, version); + + if ((_isPrioritized) && (handle.IsPreemptive)) + { + break; + } + } + } + + /// + /// Dispatch events. + /// + public void Dispatch() + { + try + { + _services.DispatchService.Dispatch(); + } + catch (Exception ex) + { + throw new EPException(ex); + } + } + + public bool IsExternalClockingEnabled + { + get { return _isUsingExternalClocking; } + } + + /// + /// Dispose for destroying an engine instance: sets references to null and clears thread-locals + /// + public void Dispose() + { + _services = null; + _threadLocalData.Dispose(); + _threadLocalData = null; + } + + public void Initialize() + { + InitThreadLocals(); + _threadWorkQueue = new ThreadWorkQueue(); + } + + public void ClearCaches() + { + InitThreadLocals(); + } + + public void SetVariableValue(String variableName, Object variableValue) + { + VariableMetaData metaData = _services.VariableService.GetVariableMetaData(variableName); + CheckVariable(variableName, metaData, true, false); + + using (_services.VariableService.ReadWriteLock.AcquireWriteLock()) + { + _services.VariableService.CheckAndWrite(variableName, EPStatementStartMethodConst.DEFAULT_AGENT_INSTANCE_ID, variableValue); + _services.VariableService.Commit(); + } + } + + public void SetVariableValue(IDictionary variableValues) + { + SetVariableValueInternal(variableValues, EPStatementStartMethodConst.DEFAULT_AGENT_INSTANCE_ID, false); + } + + public void SetVariableValue(IDictionary variableValues, int agentInstanceId) + { + SetVariableValueInternal(variableValues, agentInstanceId, true); + } + + public Object GetVariableValue(String variableName) + { + _services.VariableService.SetLocalVersion(); + VariableMetaData metaData = _services.VariableService.GetVariableMetaData(variableName); + if (metaData == null) + { + throw new VariableNotFoundException("Variable by name '" + variableName + "' has not been declared"); + } + if (metaData.ContextPartitionName != null) + { + throw new VariableNotFoundException("Variable by name '" + variableName + "' has been declared for context '" + metaData.ContextPartitionName + "' and cannot be read without context partition selector"); + } + VariableReader reader = _services.VariableService.GetReader(variableName, EPStatementStartMethodConst.DEFAULT_AGENT_INSTANCE_ID); + Object value = reader.Value; + if (value == null || reader.VariableMetaData.EventType == null) + { + return value; + } + return ((EventBean)value).Underlying; + } + + public IDictionary> GetVariableValue(ISet variableNames, ContextPartitionSelector contextPartitionSelector) + { + _services.VariableService.SetLocalVersion(); + String contextPartitionName = null; + foreach (String variableName in variableNames) + { + VariableMetaData metaData = _services.VariableService.GetVariableMetaData(variableName); + if (metaData == null) + { + throw new VariableNotFoundException("Variable by name '" + variableName + "' has not been declared"); + } + if (metaData.ContextPartitionName == null) + { + throw new VariableNotFoundException("Variable by name '" + variableName + "' is a global variable and not context-partitioned"); + } + if (contextPartitionName == null) + { + contextPartitionName = metaData.ContextPartitionName; + } + else + { + if (!contextPartitionName.Equals(metaData.ContextPartitionName)) + { + throw new VariableNotFoundException("Variable by name '" + variableName + "' is a declared for context '" + metaData.ContextPartitionName + "' however the expected context is '" + contextPartitionName + "'"); + } + } + } + ContextManager contextManager = _services.ContextManagementService.GetContextManager(contextPartitionName); + if (contextManager == null) + { + throw new VariableNotFoundException("Context by name '" + contextPartitionName + "' cannot be found"); + } + IDictionary contextPartitions = contextManager.ExtractPaths(contextPartitionSelector).ContextPartitionInformation; + if (contextPartitions.IsEmpty()) + { + return Collections.GetEmptyMap>(); + } + IDictionary> statesMap = new Dictionary>(); + foreach (String variableName in variableNames) + { + var states = new List(); + statesMap.Put(variableName, states); + foreach (var entry in contextPartitions) + { + VariableReader reader = _services.VariableService.GetReader(variableName, entry.Key); + Object value = reader.Value; + if (value != null && reader.VariableMetaData.EventType != null) + { + value = ((EventBean)value).Underlying; + } + states.Add(new ContextPartitionVariableState(entry.Key, entry.Value.Identifier, value)); + } + } + return statesMap; + } + + public IDictionary GetVariableValue(ICollection variableNames) + { + _services.VariableService.SetLocalVersion(); + IDictionary values = new Dictionary(); + foreach (var variableName in variableNames) + { + VariableMetaData metaData = _services.VariableService.GetVariableMetaData(variableName); + CheckVariable(variableName, metaData, false, false); + VariableReader reader = _services.VariableService.GetReader(variableName, EPStatementStartMethodConst.DEFAULT_AGENT_INSTANCE_ID); + if (reader == null) + { + throw new VariableNotFoundException("Variable by name '" + variableName + "' has not been declared"); + } + + var value = reader.Value; + if (value != null && reader.VariableMetaData.EventType != null) + { + value = ((EventBean)value).Underlying; + } + values.Put(variableName, value); + } + return values; + } + + public DataMap VariableValueAll + { + get + { + _services.VariableService.SetLocalVersion(); + IDictionary variables = _services.VariableService.VariableReadersNonCP; + var values = new Dictionary(); + foreach (var entry in variables) + { + var value = entry.Value.Value; + values.Put(entry.Value.VariableMetaData.VariableName, value); + } + return values; + } + } + + public IDictionary VariableTypeAll + { + get + { + IDictionary variables = + _services.VariableService.VariableReadersNonCP; + var values = new Dictionary(); + foreach (var entry in variables) + { + var type = entry.Value.VariableMetaData.VariableType; + values.Put(entry.Value.VariableMetaData.VariableName, type); + } + return values; + } + } + + public Type GetVariableType(String variableName) + { + VariableMetaData metaData = _services.VariableService.GetVariableMetaData(variableName); + if (metaData == null) + { + return null; + } + return metaData.VariableType; + } + + public EPOnDemandQueryResult ExecuteQuery(String epl, ContextPartitionSelector[] contextPartitionSelectors) + { + if (contextPartitionSelectors == null) + { + throw new ArgumentException("No context partition selectors provided"); + } + return ExecuteQueryInternal(epl, null, null, contextPartitionSelectors); + } + + public EPOnDemandQueryResult ExecuteQuery(String epl) + { + return ExecuteQueryInternal(epl, null, null, null); + } + + public EPOnDemandQueryResult ExecuteQuery(EPStatementObjectModel model) + { + return ExecuteQueryInternal(null, model, null, null); + } + + public EPOnDemandQueryResult ExecuteQuery(EPStatementObjectModel model, ContextPartitionSelector[] contextPartitionSelectors) + { + if (contextPartitionSelectors == null) + { + throw new ArgumentException("No context partition selectors provided"); + } + return ExecuteQueryInternal(null, model, null, contextPartitionSelectors); + } + + public EPOnDemandQueryResult ExecuteQuery(EPOnDemandPreparedQueryParameterized parameterizedQuery) + { + return ExecuteQueryInternal(null, null, parameterizedQuery, null); + } + + public EPOnDemandQueryResult ExecuteQuery(EPOnDemandPreparedQueryParameterized parameterizedQuery, ContextPartitionSelector[] contextPartitionSelectors) + { + return ExecuteQueryInternal(null, null, parameterizedQuery, contextPartitionSelectors); + } + + private EPOnDemandQueryResult ExecuteQueryInternal(String epl, EPStatementObjectModel model, EPOnDemandPreparedQueryParameterized parameterizedQuery, ContextPartitionSelector[] contextPartitionSelectors) + { + try + { + var executeMethod = GetExecuteMethod(epl, model, parameterizedQuery); + var result = executeMethod.Execute(contextPartitionSelectors); + return new EPQueryResultImpl(result); + } + catch (EPStatementException) + { + throw; + } + catch (Exception ex) + { + var message = "Error executing statement: " + ex.Message; + Log.Info(message, ex); + throw new EPStatementException(message, epl, ex); + } + } + + public EPOnDemandPreparedQuery PrepareQuery(String epl) + { + return PrepareQueryInternal(epl, null); + } + + public EPOnDemandPreparedQuery PrepareQuery(EPStatementObjectModel model) + { + return PrepareQueryInternal(null, model); + } + + public EPOnDemandPreparedQueryParameterized PrepareQueryWithParameters(String epl) + { + // compile to specification + var stmtName = UuidGenerator.Generate(); + var statementSpec = EPAdministratorHelper.CompileEPL(epl, epl, true, stmtName, _services, SelectClauseStreamSelectorEnum.ISTREAM_ONLY); + + // map to object model thus finding all substitution parameters and their indexes + var unmapped = StatementSpecMapper.Unmap(statementSpec); + + // the prepared statement is the object model plus a list of substitution parameters + // map to specification will refuse any substitution parameters that are unfilled + return new EPPreparedStatementImpl(unmapped.ObjectModel, unmapped.SubstitutionParams, epl); + } + + private EPOnDemandPreparedQuery PrepareQueryInternal(String epl, EPStatementObjectModel model) + { + try + { + var startMethod = GetExecuteMethod(epl, model, null); + return new EPPreparedQueryImpl(startMethod, epl); + } + catch (EPStatementException) + { + throw; + } + catch (Exception ex) + { + var message = "Error executing statement: " + ex.Message; + Log.Debug(message, ex); + throw new EPStatementException(message, epl); + } + } + + private EPPreparedExecuteMethod GetExecuteMethod(String epl, EPStatementObjectModel model, EPOnDemandPreparedQueryParameterized parameterizedQuery) + { + var stmtName = UuidGenerator.Generate(); + var stmtId = -1; + + try + { + StatementSpecRaw spec; + if (epl != null) + { + spec = EPAdministratorHelper.CompileEPL(epl, epl, true, stmtName, _services, SelectClauseStreamSelectorEnum.ISTREAM_ONLY); + } + else if (model != null) + { + spec = StatementSpecMapper.Map( + model, + _services.EngineImportService, + _services.VariableService, + _services.ConfigSnapshot, + _services.SchedulingService, + _services.EngineURI, + _services.PatternNodeFactory, + _services.NamedWindowMgmtService, + _services.ContextManagementService, + _services.ExprDeclaredService, + _services.TableService); + epl = model.ToEPL(); + } + else + { + var prepared = (EPPreparedStatementImpl)parameterizedQuery; + spec = StatementSpecMapper.Map( + prepared.Model, + _services.EngineImportService, + _services.VariableService, + _services.ConfigSnapshot, + _services.SchedulingService, + _services.EngineURI, + _services.PatternNodeFactory, + _services.NamedWindowMgmtService, + _services.ContextManagementService, + _services.ExprDeclaredService, + _services.TableService); + epl = prepared.OptionalEPL ?? prepared.Model.ToEPL(); + } + + var annotations = AnnotationUtil.CompileAnnotations(spec.Annotations, _services.EngineImportService, epl); + var writesToTables = StatementLifecycleSvcUtil.IsWritesToTables(spec, _services.TableService); + var statementContext = _services.StatementContextFactory.MakeContext( + stmtId, stmtName, epl, StatementType.SELECT, _services, null, true, annotations, null, true, spec, Collections.GetEmptyList(), writesToTables, null); + + // walk subselects, alias expressions, declared expressions, dot-expressions + ExprNodeSubselectDeclaredDotVisitor visitor; + try + { + visitor = StatementSpecRawAnalyzer.WalkSubselectAndDeclaredDotExpr(spec); + } + catch (ExprValidationException ex) + { + throw new EPStatementException(ex.Message, epl); + } + + var compiledSpec = StatementLifecycleSvcImpl.Compile( + spec, epl, statementContext, false, true, annotations, visitor.Subselects, + Collections.GetEmptyList(), + spec.TableExpressions, + _services); + + if (compiledSpec.InsertIntoDesc != null) + { + return new EPPreparedExecuteIUDInsertInto(compiledSpec, _services, statementContext); + } + else if (compiledSpec.FireAndForgetSpec == null) + { // null indicates a select-statement, same as continuous query + if (compiledSpec.UpdateSpec != null) + { + throw new EPStatementException("Provided EPL expression is a continuous query expression (not an on-demand query), please use the administrator createEPL API instead", epl); + } + return new EPPreparedExecuteMethodQuery(compiledSpec, _services, statementContext); + } + else if (compiledSpec.FireAndForgetSpec is FireAndForgetSpecDelete) + { + return new EPPreparedExecuteIUDSingleStreamDelete(compiledSpec, _services, statementContext); + } + else if (compiledSpec.FireAndForgetSpec is FireAndForgetSpecUpdate) + { + return new EPPreparedExecuteIUDSingleStreamUpdate(compiledSpec, _services, statementContext); + } + else + { + throw new IllegalStateException("Unrecognized FAF code " + compiledSpec.FireAndForgetSpec); + } + } + catch (EPStatementException) + { + throw; + } + catch (Exception ex) + { + var message = "Error executing statement: " + ex.Message; + Log.Debug(message, ex); + throw new EPStatementException(message, ex, epl); + } + } + + public EventSender GetEventSender(String eventTypeName) + { + return _services.EventAdapterService.GetStaticTypeEventSender(this, eventTypeName, _services.ThreadingService); + } + + public EventSender GetEventSender(Uri[] uri) + { + return _services.EventAdapterService.GetDynamicTypeEventSender(this, uri, _services.ThreadingService); + } + + public EventRenderer EventRenderer + { + get { return _eventRenderer ?? (_eventRenderer = new EventRendererImpl()); } + } + + public long CurrentTime + { + get { return _services.SchedulingService.Time; } + } + + public long? NextScheduledTime + { + get { return _services.SchedulingService.NearestTimeHandle; } + } + + public IDictionary StatementNearestSchedules + { + get { return GetStatementNearestSchedulesInternal(_services.SchedulingService, _services.StatementLifecycleSvc); } + } + + internal static IDictionary GetStatementNearestSchedulesInternal(SchedulingServiceSPI schedulingService, StatementLifecycleSvc statementLifecycleSvc) + { + var schedulePerStatementId = new Dictionary(); + schedulingService.VisitSchedules(visit => + { + if (schedulePerStatementId.ContainsKey(visit.StatementId)) + { + return; + } + schedulePerStatementId.Put(visit.StatementId, visit.Timestamp); + }); + + var result = new Dictionary(); + foreach (var schedule in schedulePerStatementId) + { + var stmtName = statementLifecycleSvc.GetStatementNameById(schedule.Key); + if (stmtName != null) + { + result.Put(stmtName, schedule.Value); + } + } + return result; + } + + public string EngineURI + { + get { return _services.EngineURI; } + } + + public EPDataFlowRuntime DataFlowRuntime + { + get { return _services.DataFlowService; } + } + + private void RemoveFromThreadLocals() + { + if (_threadLocalData != null) + { + _threadLocalData.Dispose(); + _threadLocalData = ThreadLocalManager.Create(CreateLocalData); + } + } + + private void InitThreadLocals() + { + RemoveFromThreadLocals(); + _threadLocalData = ThreadLocalManager.Create(CreateLocalData); + } + + private void CheckVariable(String variableName, VariableMetaData metaData, bool settable, bool requireContextPartitioned) + { + if (metaData == null) + { + throw new VariableNotFoundException("Variable by name '" + variableName + "' has not been declared"); + } + if (!requireContextPartitioned) + { + if (metaData.ContextPartitionName != null) + { + throw new VariableNotFoundException("Variable by name '" + variableName + "' has been declared for context '" + metaData.ContextPartitionName + "' and cannot be set without context partition selectors"); + } + } + else + { + if (metaData.ContextPartitionName == null) + { + throw new VariableNotFoundException("Variable by name '" + variableName + "' is a global variable and not context-partitioned"); + } + } + if (settable && metaData.IsConstant) + { + throw new VariableConstantValueException("Variable by name '" + variableName + "' is declared as constant and may not be assigned a new value"); + } + } + + private void SetVariableValueInternal( + IDictionary variableValues, + int agentInstanceId, + bool requireContextPartitioned) + { + // verify + foreach (var entry in variableValues) + { + String variableName = entry.Key; + VariableMetaData metaData = _services.VariableService.GetVariableMetaData(variableName); + CheckVariable(variableName, metaData, true, requireContextPartitioned); + } + + // set values + using (_services.VariableService.ReadWriteLock.AcquireWriteLock()) + { + foreach (var entry in variableValues) + { + String variableName = entry.Key; + try + { + _services.VariableService.CheckAndWrite(variableName, agentInstanceId, entry.Value); + } + catch (Exception ex) + { + _services.VariableService.Rollback(); + throw; + } + } + _services.VariableService.Commit(); + } + } + + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/EPRuntimeIsolatedFactory.cs b/NEsper.Core/NEsper.Core/core/service/EPRuntimeIsolatedFactory.cs new file mode 100755 index 000000000..b70be47a7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/EPRuntimeIsolatedFactory.cs @@ -0,0 +1,15 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.core.service +{ + public interface EPRuntimeIsolatedFactory + { + EPRuntimeIsolatedSPI Make(EPIsolationUnitServices isolatedServices, EPServicesContext unisolatedSvc); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/service/EPRuntimeIsolatedFactoryImpl.cs b/NEsper.Core/NEsper.Core/core/service/EPRuntimeIsolatedFactoryImpl.cs new file mode 100755 index 000000000..4d2c48e6e --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/EPRuntimeIsolatedFactoryImpl.cs @@ -0,0 +1,18 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.core.service +{ + public class EPRuntimeIsolatedFactoryImpl : EPRuntimeIsolatedFactory + { + public EPRuntimeIsolatedSPI Make(EPIsolationUnitServices isolatedServices, EPServicesContext unisolatedSvc) + { + return new EPRuntimeIsolatedImpl(isolatedServices, unisolatedSvc); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/service/EPRuntimeIsolatedImpl.cs b/NEsper.Core/NEsper.Core/core/service/EPRuntimeIsolatedImpl.cs new file mode 100755 index 000000000..38c6c8060 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/EPRuntimeIsolatedImpl.cs @@ -0,0 +1,902 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Xml; +using System.Xml.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.client.hook; +using com.espertech.esper.client.time; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.compat.threading; +using com.espertech.esper.core.context.util; +using com.espertech.esper.filter; +using com.espertech.esper.schedule; +using com.espertech.esper.util; + +using DataMap = System.Collections.Generic.IDictionary; +using TypeMap = System.Collections.Generic.IDictionary; + +namespace com.espertech.esper.core.service +{ + /// + /// Implementation for isolated runtime. + /// + public class EPRuntimeIsolatedImpl + : EPRuntimeIsolatedSPI + , InternalEventRouteDest + , EPRuntimeEventSender + { + private readonly EPServicesContext _unisolatedServices; + private EPIsolationUnitServices _services; + private readonly bool _isSubselectPreeval; + private readonly bool _isPrioritized; + private readonly bool _isLatchStatementInsertStream; + private readonly ThreadWorkQueue _threadWorkQueue; + + /// + /// Data that remains local to the thread. + /// + private IThreadLocal _threadLocalData; + + #region Nested type: ThreadLocalData + + /// + /// Group of data that is associated with the thread. + /// + private class ThreadLocalData + { + internal List MatchesArrayThreadLocal; + internal IDictionary> MatchesPerStmtThreadLocal; + internal ArrayBackedCollection ScheduleArrayThreadLocal; + internal IDictionary SchedulePerStmtThreadLocal; + } + + /// + /// Gets the local data. + /// + /// The local data. + private ThreadLocalData LocalData + { + get { return _threadLocalData.GetOrCreate(); } + } + + /// + /// Gets the schedule array. + /// + /// The schedule array. + private ArrayBackedCollection ScheduleArray + { + get { return LocalData.ScheduleArrayThreadLocal; } + } + + /// + /// Gets the schedule per statement. + /// + /// The schedule per statement. + private IDictionary SchedulePerStmt + { + get { return LocalData.SchedulePerStmtThreadLocal; } + } + + #endregion + + /// + /// Creates a local data object. + /// + /// + private ThreadLocalData CreateLocalData() + { + var threadLocalData = new ThreadLocalData(); + threadLocalData.MatchesArrayThreadLocal = + new List(100); + threadLocalData.ScheduleArrayThreadLocal = + new ArrayBackedCollection(100); + + if (_isPrioritized) + { + threadLocalData.MatchesPerStmtThreadLocal = + new OrderedDictionary>(new EPStatementAgentInstanceHandlePrioritySort()); + threadLocalData.SchedulePerStmtThreadLocal = + new OrderedDictionary(new EPStatementAgentInstanceHandlePrioritySort(true)); + } + else + { + threadLocalData.MatchesPerStmtThreadLocal = + new Dictionary>(10000); + threadLocalData.SchedulePerStmtThreadLocal = + new Dictionary(10000); + } + + return threadLocalData; + } + + /// Ctor. + /// isolated services + /// engine services + public EPRuntimeIsolatedImpl(EPIsolationUnitServices svc, EPServicesContext unisolatedSvc) + { + _services = svc; + _unisolatedServices = unisolatedSvc; + _threadWorkQueue = new ThreadWorkQueue(); + + _isSubselectPreeval = unisolatedSvc.EngineSettingsService.EngineSettings.Expression.IsSelfSubselectPreeval; + _isPrioritized = unisolatedSvc.EngineSettingsService.EngineSettings.Execution.IsPrioritized; + _isLatchStatementInsertStream = unisolatedSvc.EngineSettingsService.EngineSettings.Threading.IsInsertIntoDispatchPreserveOrder; + + _threadLocalData = ThreadLocalManager.Create(CreateLocalData); + } + + public void SendEvent(Object theEvent) + { + if (theEvent == null) + { + Log.Error(".SendEvent Null object supplied"); + return; + } + + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled)) + { + if ((!(theEvent is CurrentTimeEvent)) || (ExecutionPathDebugLog.IsTimerDebugEnabled)) + { + Log.Debug(".SendEvent Processing event " + theEvent); + } + } + + // Process event + ProcessEvent(theEvent); + } + + public void SendEvent(XElement element) + { + if (element == null) + { + Log.Error(".SendEvent Null object supplied"); + return; + } + + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled)) + { + Log.Debug(".SendEvent Processing DOM node event {0}", element); + } + + // Get it wrapped up, process event + EventBean eventBean = _unisolatedServices.EventAdapterService.AdapterForDOM(element); + ProcessEvent(eventBean); + } + + public void SendEvent(XmlNode document) + { + if (document == null) + { + Log.Error(".SendEvent Null object supplied"); + return; + } + + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled)) + { + Log.Debug(".SendEvent Processing DOM node event " + document); + } + + // Get it wrapped up, process event + EventBean eventBean = _unisolatedServices.EventAdapterService.AdapterForDOM(document); + ProcessEvent(eventBean); + } + + /// Route a XML docment event + /// to route + /// EPException if routing failed + public void Route(XmlNode document) + { + if (document == null) + { + Log.Error(".SendEvent Null object supplied"); + return; + } + + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled)) + { + Log.Debug(".SendEvent Processing DOM node event " + document); + } + + // Get it wrapped up, process event + EventBean eventBean = _unisolatedServices.EventAdapterService.AdapterForDOM(document); + _threadWorkQueue.AddBack(eventBean); + } + + public void SendEvent(DataMap map, String eventTypeName) + { + if (map == null) + { + throw new ArgumentException("Invalid null event object"); + } + + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled)) + { + Log.Debug(".sendMap Processing event " + map); + } + + // Process event + EventBean eventBean = _unisolatedServices.EventAdapterService.AdapterForMap(map, eventTypeName); + ProcessWrappedEvent(eventBean); + } + + public void SendEvent(Object[] objectarray, String objectArrayEventTypeName) + { + if (objectarray == null) + { + throw new ArgumentException("Invalid null event object"); + } + + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled)) + { + Log.Debug(".sendEvent Processing event {0}", objectarray); + } + + // Process event + EventBean eventBean = _unisolatedServices.EventAdapterService.AdapterForObjectArray(objectarray, objectArrayEventTypeName); + ProcessWrappedEvent(eventBean); + } + + /// Process an unwrapped event. + /// to process. + public void ProcessEvent(Object theEvent) + { + if (theEvent is TimerEvent) + { + ProcessTimeEvent((TimerEvent)theEvent); + return; + } + + EventBean eventBean; + + if (theEvent is EventBean) + { + eventBean = (EventBean)theEvent; + } + else + { + eventBean = _unisolatedServices.EventAdapterService.AdapterForObject(theEvent); + } + + ProcessWrappedEvent(eventBean); + } + + /// Process a wrapped event. + /// to process + public void ProcessWrappedEvent(EventBean eventBean) + { + // Acquire main processing lock which locks out statement management + using (_unisolatedServices.EventProcessingRWLock.AcquireReadLock()) + { + try + { + ProcessMatches(eventBean); + } + catch (EPException) + { + throw; + } + catch (Exception ex) + { + LocalData.MatchesArrayThreadLocal.Clear(); + throw new EPException(ex); + } + } + + // Dispatch results to listeners + // Done outside of the read-lock to prevent lockups when listeners create statements + Dispatch(); + + // Work off the event queue if any events accumulated in there via a Route() or insert-into + ProcessThreadWorkQueue(); + } + + private void ProcessTimeEvent(TimerEvent theEvent) + { + if (theEvent is TimerControlEvent) + { + var tce = (TimerControlEvent)theEvent; + if (tce.ClockType == TimerControlEvent.ClockTypeEnum.CLOCK_INTERNAL) + { + Log.Warn("Timer control events are not processed by the isolated runtime as the setting is always external timer."); + } + return; + } + + // Evaluation of all time events is protected from statement management + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled) && (ExecutionPathDebugLog.IsTimerDebugEnabled)) + { + Log.Debug(".processTimeEvent Setting time and evaluating schedules"); + } + + long currentTime; + + if (theEvent is CurrentTimeEvent) + { + var current = (CurrentTimeEvent)theEvent; + + currentTime = current.Time; + if (currentTime == _services.SchedulingService.Time) + { + Log.Warn("Duplicate time event received for currentTime {0}", currentTime); + } + + _services.SchedulingService.Time = currentTime; + + ProcessSchedule(); + + // Let listeners know of results + Dispatch(); + + // Work off the event queue if any events accumulated in there via a route() + ProcessThreadWorkQueue(); + + return; + } + + // handle time span + var span = (CurrentTimeSpanEvent)theEvent; + var targetTime = span.TargetTime; + var optionalResolution = span.OptionalResolution; + currentTime = _services.SchedulingService.Time; + + if (targetTime < currentTime) + { + Log.Warn("Past or current time event received for currentTime {0}", targetTime); + } + + // Evaluation of all time events is protected from statement management + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled) && (ExecutionPathDebugLog.IsTimerDebugEnabled)) + { + Log.Debug(".processTimeEvent Setting time span and evaluating schedules for time " + targetTime + " optional resolution " + span.OptionalResolution); + } + + while (currentTime < targetTime) + { + + if ((optionalResolution != null) && (optionalResolution > 0)) + { + currentTime += optionalResolution.Value; + } + else + { + var nearest = _services.SchedulingService.NearestTimeHandle; + if (nearest == null) + { + currentTime = targetTime; + } + else + { + currentTime = nearest.Value; + } + } + if (currentTime > targetTime) + { + currentTime = targetTime; + } + + // Evaluation of all time events is protected from statement management + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled) && + (ExecutionPathDebugLog.IsTimerDebugEnabled)) + { + Log.Debug(".processTimeEvent Setting time and evaluating schedules for time " + currentTime); + } + + _services.SchedulingService.Time = currentTime; + + ProcessSchedule(); + + // Let listeners know of results + Dispatch(); + + // Work off the event queue if any events accumulated in there via a route() + ProcessThreadWorkQueue(); + } + } + + private void ProcessSchedule() + { + ArrayBackedCollection handles = ScheduleArray; + + // Evaluation of schedules is protected by an optional scheduling service lock and then the engine lock + // We want to stay in this order for allowing the engine lock as a second-order lock to the + // services own lock, if it has one. + using (_unisolatedServices.EventProcessingRWLock.AcquireReadLock()) + { + _services.SchedulingService.Evaluate(handles); + } + using (_unisolatedServices.EventProcessingRWLock.AcquireReadLock()) + { + try + { + ProcessScheduleHandles(handles); + } + catch (Exception) + { + handles.Clear(); + throw; + } + } + } + + private void ProcessScheduleHandles(ArrayBackedCollection handles) + { + if (ThreadLogUtil.ENABLED_TRACE) + { + ThreadLogUtil.Trace("Found schedules for", handles.Count); + } + + if (handles.Count == 0) + { + return; + } + + // handle 1 result separatly for performance reasons + if (handles.Count == 1) + { + Object[] handleArray = handles.Array; + var handle = (EPStatementHandleCallback)handleArray[0]; + + EPRuntimeImpl.ProcessStatementScheduleSingle(handle, _unisolatedServices); + + handles.Clear(); + return; + } + + Object[] matchArray = handles.Array; + int entryCount = handles.Count; + + LinkedList entries; + + // sort multiple matches for the event into statements + var stmtCallbacks = SchedulePerStmt; + stmtCallbacks.Clear(); + for (int i = 0; i < entryCount; i++) + { + var handleCallback = (EPStatementHandleCallback)matchArray[i]; + var handle = handleCallback.AgentInstanceHandle; + var callback = handleCallback.ScheduleCallback; + + var entry = stmtCallbacks.Get(handle); + + // This statement has not been encountered before + if (entry == null) + { + stmtCallbacks.Put(handle, callback); + continue; + } + + // This statement has been encountered once before + if (entry is ScheduleHandleCallback) + { + var existingCallback = (ScheduleHandleCallback)entry; + entries = new LinkedList(); + entries.AddLast(existingCallback); + entries.AddLast(callback); + stmtCallbacks.Put(handle, entries); + continue; + } + + // This statement has been encountered more then once before + entries = (LinkedList)entry; + entries.AddLast(callback); + } + handles.Clear(); + + foreach (var entry in stmtCallbacks) + { + var handle = entry.Key; + var callbackObject = entry.Value; + + EPRuntimeImpl.ProcessStatementScheduleMultiple(handle, callbackObject, _unisolatedServices); + + if ((_isPrioritized) && (handle.IsPreemptive)) + { + break; + } + } + } + + /// Works off the thread's work queue. + public void ProcessThreadWorkQueue() + { + DualWorkQueue queues = _threadWorkQueue.ThreadQueue; + + Object item; + if (queues.FrontQueue.IsEmpty()) + { + bool haveDispatched = _unisolatedServices.NamedWindowDispatchService.Dispatch(); + if (haveDispatched) + { + // Dispatch results to listeners + Dispatch(); + if (!queues.FrontQueue.IsEmpty()) + { + ProcessThreadWorkQueueFront(queues); + } + } + } + else + { + ProcessThreadWorkQueueFront(queues); + } + + while ((item = queues.BackQueue.Poll()) != null) + { + if (item is InsertIntoLatchSpin) + { + ProcessThreadWorkQueueLatchedSpin((InsertIntoLatchSpin)item); + } + else if (item is InsertIntoLatchWait) + { + ProcessThreadWorkQueueLatchedWait((InsertIntoLatchWait)item); + } + else + { + ProcessThreadWorkQueueUnlatched(item); + } + + bool haveDispatched = _unisolatedServices.NamedWindowDispatchService.Dispatch(); + if (haveDispatched) + { + Dispatch(); + } + + if (!queues.FrontQueue.IsEmpty()) + { + ProcessThreadWorkQueueFront(queues); + } + } + } + + private void ProcessThreadWorkQueueFront(DualWorkQueue queues) + { + Object item; + + while ((item = queues.FrontQueue.Poll()) != null) + { + if (item is InsertIntoLatchSpin) + { + ProcessThreadWorkQueueLatchedSpin((InsertIntoLatchSpin)item); + } + else if (item is InsertIntoLatchWait) + { + ProcessThreadWorkQueueLatchedWait((InsertIntoLatchWait)item); + } + else + { + ProcessThreadWorkQueueUnlatched(item); + } + + bool haveDispatched = _unisolatedServices.NamedWindowDispatchService.Dispatch(); + if (haveDispatched) + { + Dispatch(); + } + } + } + + private void ProcessThreadWorkQueueLatchedWait(InsertIntoLatchWait insertIntoLatch) + { + // wait for the latch to complete + EventBean eventBean = insertIntoLatch.Await(); + + using (_unisolatedServices.EventProcessingRWLock.AcquireReadLock()) + { + try + { + ProcessMatches(eventBean); + } + catch (Exception) + { + LocalData.MatchesArrayThreadLocal.Clear(); + throw; + } + finally + { + insertIntoLatch.Done(); + } + } + + Dispatch(); + } + + private void ProcessThreadWorkQueueLatchedSpin(InsertIntoLatchSpin insertIntoLatch) + { + // wait for the latch to complete + EventBean eventBean = insertIntoLatch.Await(); + + using (_unisolatedServices.EventProcessingRWLock.AcquireReadLock()) + { + try + { + ProcessMatches(eventBean); + } + catch (Exception) + { + LocalData.MatchesArrayThreadLocal.Clear(); + throw; + } + finally + { + insertIntoLatch.Done(); + } + } + + Dispatch(); + } + + private void ProcessThreadWorkQueueUnlatched(Object item) + { + EventBean eventBean; + if (item is EventBean) + { + eventBean = (EventBean)item; + } + else + { + eventBean = _unisolatedServices.EventAdapterService.AdapterForObject(item); + } + + using (_unisolatedServices.EventProcessingRWLock.AcquireReadLock()) + { + try + { + ProcessMatches(eventBean); + } + catch (Exception) + { + LocalData.MatchesArrayThreadLocal.Clear(); + throw; + } + } + + Dispatch(); + } + + private void ProcessMatches(EventBean theEvent) + { + var localData = LocalData; + + // get matching filters + var matches = localData.MatchesArrayThreadLocal; + _services.FilterService.Evaluate(theEvent, matches); + + if (ThreadLogUtil.ENABLED_TRACE) + { + ThreadLogUtil.Trace("Found matches for underlying ", matches.Count, theEvent.Underlying); + } + + if (matches.Count == 0) + { + return; + } + + var stmtCallbacks = localData.MatchesPerStmtThreadLocal; + int entryCount = matches.Count; + + for (int i = 0; i < entryCount; i++) + { + var handleCallback = (EPStatementHandleCallback)matches[i]; + 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 callbacks = stmtCallbacks.Get(handle); + if (callbacks == null) + { + callbacks = new LinkedList(); + stmtCallbacks.Put(handle, callbacks); + } + callbacks.AddLast(handleCallback.FilterCallback); + continue; + } + + ProcessStatementFilterSingle(handle, handleCallback, theEvent); + } + matches.Clear(); + if (stmtCallbacks.IsEmpty()) + { + return; + } + + foreach (var entry in stmtCallbacks) + { + var handle = entry.Key; + var callbackList = entry.Value; + + ProcessStatementFilterMultiple(handle, callbackList, theEvent); + + if ((_isPrioritized) && (handle.IsPreemptive)) + { + break; + } + } + stmtCallbacks.Clear(); + } + + /// Processing multiple filter matches for a statement. + /// statement handle + /// object containing callbacks + /// to process + public void ProcessStatementFilterMultiple(EPStatementAgentInstanceHandle handle, ICollection callbackList, EventBean theEvent) + { + using (handle.StatementAgentInstanceLock.AcquireWriteLock()) + { + try + { + if (handle.HasVariables) + { + _unisolatedServices.VariableService.SetLocalVersion(); + } + + handle.MultiMatchHandler.Handle(callbackList, theEvent); + + // internal join processing, if applicable + handle.InternalDispatch(); + } + catch (Exception ex) + { + _unisolatedServices.ExceptionHandlingService.HandleException(ex, handle, ExceptionHandlerExceptionType.PROCESS, theEvent); + } + finally + { + if (handle.HasTableAccess) + { + _unisolatedServices.TableService.TableExprEvaluatorContext.ReleaseAcquiredLocks(); + } + } + } + } + + /// Process a single match. + /// statement + /// callback + /// event to indicate + public void ProcessStatementFilterSingle(EPStatementAgentInstanceHandle handle, EPStatementHandleCallback handleCallback, EventBean theEvent) + { + using (handle.StatementAgentInstanceLock.AcquireWriteLock()) + { + try + { + if (handle.HasVariables) + { + _unisolatedServices.VariableService.SetLocalVersion(); + } + + handleCallback.FilterCallback.MatchFound(theEvent, null); + + // internal join processing, if applicable + handle.InternalDispatch(); + } + catch (Exception ex) + { + _unisolatedServices.ExceptionHandlingService.HandleException(ex, handle, ExceptionHandlerExceptionType.PROCESS, theEvent); + } + finally + { + if (handle.HasTableAccess) + { + _unisolatedServices.TableService.TableExprEvaluatorContext.ReleaseAcquiredLocks(); + } + } + } + } + + /// Dispatch events. + public void Dispatch() + { + try + { + _unisolatedServices.DispatchService.Dispatch(); + } + catch (EPException) + { + throw; + } + catch (Exception ex) + { + throw new EPException(ex); + } + } + + /// + /// Dispose for destroying an engine instance: sets references to null and clears thread-locals + /// + public void Dispose() + { + _services = null; + _threadLocalData = null; + } + + public long CurrentTime + { + get { return _services.SchedulingService.Time; } + } + + // Internal route of events via insert-into, holds a statement lock + public void Route(EventBean theEvent, EPStatementHandle epStatementHandle, bool addToFront) + { + if (_isLatchStatementInsertStream) + { + if (addToFront) + { + Object latch = epStatementHandle.InsertIntoFrontLatchFactory.NewLatch(theEvent); + _threadWorkQueue.AddFront(latch); + } + else + { + Object latch = epStatementHandle.InsertIntoBackLatchFactory.NewLatch(theEvent); + _threadWorkQueue.AddBack(latch); + } + } + else + { + if (addToFront) + { + _threadWorkQueue.AddFront(theEvent); + } + else + { + _threadWorkQueue.AddBack(theEvent); + } + } + } + + public EventSender GetEventSender(String eventTypeName) + { + return _unisolatedServices.EventAdapterService.GetStaticTypeEventSender(this, eventTypeName, _unisolatedServices.ThreadingService); + } + + public EventSender GetEventSender(Uri[] uri) + { + return _unisolatedServices.EventAdapterService.GetDynamicTypeEventSender(this, uri, _unisolatedServices.ThreadingService); + } + + public void RouteEventBean(EventBean theEvent) + { + _threadWorkQueue.AddBack(theEvent); + } + + public InternalEventRouter InternalEventRouter + { + set { throw new UnsupportedOperationException("Isolated runtime does not route itself"); } + } + + + public long? NextScheduledTime + { + get { return _services.SchedulingService.NearestTimeHandle; } + } + + public IDictionary StatementNearestSchedules + { + get + { + return EPRuntimeImpl.GetStatementNearestSchedulesInternal( + _services.SchedulingService, + _unisolatedServices.StatementLifecycleSvc); + } + } + + public string EngineURI + { + get { return _unisolatedServices.EngineURI; } + } + + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/EPRuntimeIsolatedSPI.cs b/NEsper.Core/NEsper.Core/core/service/EPRuntimeIsolatedSPI.cs new file mode 100755 index 000000000..835356c13 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/EPRuntimeIsolatedSPI.cs @@ -0,0 +1,21 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; + +namespace com.espertech.esper.core.service +{ + public interface EPRuntimeIsolatedSPI + : EPRuntimeIsolated + , InternalEventRouteDest + { + IDictionary StatementNearestSchedules { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/EPRuntimeSPI.cs b/NEsper.Core/NEsper.Core/core/service/EPRuntimeSPI.cs new file mode 100755 index 000000000..45003481d --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/EPRuntimeSPI.cs @@ -0,0 +1,104 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Xml; +using com.espertech.esper.client; + +namespace com.espertech.esper.core.service +{ + /// + /// SPI interface of the runtime exposes fire-and-forget, non-continuous query functionality. + /// + public interface EPRuntimeSPI : EPRuntime, IDisposable + { + /// + /// Returns all declared variable names and their types. + /// + /// variable names and types + IDictionary VariableTypeAll { get; } + + /// + /// Returns a variable's type. + /// + /// type or null if the variable is not declared + /// type of variable + Type GetVariableType(String variableName); + + /// + /// Number of events routed internally. + /// + /// event count routed internally + long RoutedInternal { get; } + + /// + /// Number of events routed externally. + /// + /// event count routed externally + long RoutedExternal { get; } + + IDictionary StatementNearestSchedules { get; } + + /// + /// Send an event represented by a plain object to the event stream processing runtime. + /// + /// Use the route method for sending events into the runtime from within UpdateListener code, to avoid + /// the possibility of a stack overflow due to nested calls to sendEvent. + /// + /// is the event to sent to the runtime + /// + /// com.espertech.esper.client.EPException is thrown when the processing of the event lead to an error + EventBean WrapEvent(Object @object); + + /// + /// Send a map containing event property values to the event stream processing runtime. + /// + /// Use the route method for sending events into the runtime from within UpdateListener code. to avoid + /// the possibility of a stack overflow due to nested calls to sendEvent. + /// + /// map that contains event property values. Keys are expected to be of type String while valuescan be of any type. Keys and values should match those declared via Configuration for the given eventTypeName. + /// the name for the Map event type that was previously configured + /// + /// EPException - when the processing of the event leads to an error + EventBean WrapEvent(IDictionary map, String eventTypeName); + + /// + /// Send an event represented by a DOM node to the event stream processing runtime. + /// + /// Use the route method for sending events into the runtime from within UpdateListener code. to avoid + /// the possibility of a stack overflow due to nested calls to sendEvent. + /// + /// is the DOM node as an event + /// + /// EPException is thrown when the processing of the event lead to an error + EventBean WrapEvent(XmlNode node); + + void ProcessThreadWorkQueue(); + void Dispatch(); + void Initialize(); + + void ProcessWrappedEvent(EventBean eventBean); + + /// + /// Gets the engine URI. + /// + /// The engine URI. + string EngineURI { get; } + + /// + /// Clear short-lived memory that may temporarily retain references to stopped or destroyed statements. + /// + /// Use this method after stopping and destroying statements for the purpose of clearing thread-local + /// or other short lived storage to statement handles of deleted statements. + /// + /// NOT safe to use without first acquiring the engine lock. + /// + void ClearCaches(); + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/EPServiceProviderImpl.cs b/NEsper.Core/NEsper.Core/core/service/EPServiceProviderImpl.cs new file mode 100755 index 000000000..17a09ed58 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/EPServiceProviderImpl.cs @@ -0,0 +1,1119 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Threading; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.compat.threading; +using com.espertech.esper.core.context.mgr; +using com.espertech.esper.core.deploy; +using com.espertech.esper.core.thread; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.metric; +using com.espertech.esper.epl.named; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.epl.variable; +using com.espertech.esper.events; +using com.espertech.esper.events.vaevent; +using com.espertech.esper.filter; +using com.espertech.esper.plugin; +using com.espertech.esper.schedule; +using com.espertech.esper.script; +using com.espertech.esper.timer; +using com.espertech.esper.util; + +namespace com.espertech.esper.core.service +{ + using Directory = com.espertech.esper.client.Directory; + using TimerCallback = com.espertech.esper.timer.TimerCallback; + using Version = com.espertech.esper.util.Version; + + /// + /// Service provider encapsulates the engine's services for runtime and administration interfaces. + /// + public class EPServiceProviderImpl : EPServiceProviderSPI + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// Occurs before an is destroyed. + /// + public event EventHandler ServiceDestroyRequested; + + /// + /// Occurs after an is initialized. + /// + public event EventHandler ServiceInitialized; + + /// + /// Occurs when a statement created. + /// + public event EventHandler StatementCreate; + + /// + /// Occurs when a statement state changes. + /// + public event EventHandler StatementStateChange; + + private volatile EPServiceEngine _engine; + private ConfigurationInformation _configSnapshot; + private readonly string _engineURI; + private readonly IDictionary _runtimes; + + /// + /// Constructor - initializes services. + /// + /// is the engine configuration + /// is the engine URI or "default" (or null which it assumes as "default") if this is the default provider + /// map of URI and runtime + /// is thrown to indicate a configuraton error + public EPServiceProviderImpl( + Configuration configuration, + string engineURI, + IDictionary runtimes) + { + if (configuration == null) + { + throw new ArgumentNullException(nameof(configuration), "Unexpected null value received for configuration"); + } + + _runtimes = runtimes; + _engineURI = engineURI ?? throw new ArgumentNullException(nameof(engineURI), "Engine URI should not be null at this stage"); + VerifyConfiguration(configuration); + + _configSnapshot = TakeSnapshot(configuration); + DoInitialize(null); + } + + public EPServiceProviderIsolated GetEPServiceIsolated(string name) + { + lock (this) + { + if (_engine == null) + { + throw new EPServiceDestroyedException(_engineURI); + } + + if (!_engine.Services.ConfigSnapshot.EngineDefaults.Execution.IsAllowIsolatedService) + { + throw new EPServiceNotAllowedException( + "Isolated runtime requires execution setting to allow isolated services, please change execution settings under engine defaults"); + } + if (_engine.Services.ConfigSnapshot.EngineDefaults.ViewResources.IsShareViews) + { + throw new EPServiceNotAllowedException( + "Isolated runtime requires view sharing disabled, set engine defaults under view resources and share views to false"); + } + if (name == null) + { + throw new ArgumentException("Name parameter does not have a value provided"); + } + + return _engine.Services.StatementIsolationService.GetIsolationUnit(name, null); + } + } + + /// Invoked after an initialize operation. + public void PostInitialize() + { + // plugin-loaders + var pluginLoaders = _engine.Services.ConfigSnapshot.PluginLoaders; + // in the order configured + foreach (var config in pluginLoaders) + { + try + { + var plugin = + (PluginLoader) _engine.Services.EngineEnvContext.Lookup("plugin-loader/" + config.LoaderName); + plugin.PostInitialize(); + } + catch (Exception ex) + { + var message = "Error post-initializing plugin class " + config.TypeName + ": " + ex.Message; + Log.Error(message, ex); + throw new EPException(message, ex); + } + } + } + + /// + /// Sets engine configuration information for use in the next initialize. + /// + /// is the engine configs + public void SetConfiguration(Configuration configuration) + { + VerifyConfiguration(configuration); + _configSnapshot = TakeSnapshot(configuration); + } + + private void VerifyConfiguration(Configuration configuration) + { + if (configuration.EngineDefaults.Execution.IsPrioritized) + { + if (!configuration.EngineDefaults.ViewResources.IsShareViews) + { + Log.Info("Setting engine setting for share-views to false as execution is prioritized"); + } + configuration.EngineDefaults.ViewResources.IsShareViews = false; + } + } + + public string URI + { + get { return _engineURI; } + } + + public EPRuntime EPRuntime + { + get + { + if (_engine == null) + { + throw new EPServiceDestroyedException(_engineURI); + } + return _engine.Runtime; + } + } + + public EPAdministrator EPAdministrator + { + get + { + if (_engine == null) + { + throw new EPServiceDestroyedException(_engineURI); + } + return _engine.Admin; + } + } + + public EPServicesContext ServicesContext + { + get + { + if (_engine == null) + { + throw new EPServiceDestroyedException(_engineURI); + } + return _engine.Services; + } + } + + public ThreadingService ThreadingService + { + get + { + if (_engine == null) + { + throw new EPServiceDestroyedException(_engineURI); + } + return _engine.Services.ThreadingService; + } + } + + public EventAdapterService EventAdapterService + { + get + { + if (_engine == null) + { + throw new EPServiceDestroyedException(_engineURI); + } + return _engine.Services.EventAdapterService; + } + } + + public SchedulingService SchedulingService + { + get + { + if (_engine == null) + { + throw new EPServiceDestroyedException(_engineURI); + } + return _engine.Services.SchedulingService; + } + } + + public FilterService FilterService + { + get + { + if (_engine == null) + { + throw new EPServiceDestroyedException(_engineURI); + } + return _engine.Services.FilterService; + } + } + + public TimerService TimerService + { + get + { + if (_engine == null) + { + throw new EPServiceDestroyedException(_engineURI); + } + return _engine.Services.TimerService; + } + } + + public ConfigurationInformation ConfigurationInformation + { + get { return _configSnapshot; } + } + + public NamedWindowMgmtService NamedWindowMgmtService + { + get + { + if (_engine == null) + { + throw new EPServiceDestroyedException(_engineURI); + } + return _engine.Services.NamedWindowMgmtService; + } + } + + public TableService TableService + { + get + { + if (_engine == null) + { + throw new EPServiceDestroyedException(_engineURI); + } + return _engine.Services.TableService; + } + } + + public EngineLevelExtensionServicesContext ExtensionServicesContext + { + get + { + if (_engine == null) + { + throw new EPServiceDestroyedException(_engineURI); + } + return _engine.Services.EngineLevelExtensionServicesContext; + } + } + + public StatementLifecycleSvc StatementLifecycleSvc + { + get + { + if (_engine == null) + { + throw new EPServiceDestroyedException(_engineURI); + } + return _engine.Services.StatementLifecycleSvc; + } + } + + public MetricReportingService MetricReportingService + { + get + { + if (_engine == null) + { + throw new EPServiceDestroyedException(_engineURI); + } + return _engine.Services.MetricsReportingService; + } + } + + public ValueAddEventService ValueAddEventService + { + get + { + if (_engine == null) + { + throw new EPServiceDestroyedException(_engineURI); + } + return _engine.Services.ValueAddEventService; + } + } + + public StatementEventTypeRef StatementEventTypeRef + { + get + { + if (_engine == null) + { + throw new EPServiceDestroyedException(_engineURI); + } + return _engine.Services.StatementEventTypeRefService; + } + } + + public Directory EngineEnvContext + { + get + { + if (_engine == null) + { + throw new EPServiceDestroyedException(URI); + } + return _engine.Services.EngineEnvContext; + } + } + + public Directory Directory + { + get + { + if (_engine == null) + { + throw new EPServiceDestroyedException(URI); + } + return _engine.Services.EngineEnvContext; + } + } + + public StatementContextFactory StatementContextFactory + { + get + { + if (_engine == null) + { + throw new EPServiceDestroyedException(_engineURI); + } + return _engine.Services.StatementContextFactory; + } + } + + public StatementIsolationService StatementIsolationService + { + get + { + if (_engine == null) + { + throw new EPServiceDestroyedException(_engineURI); + } + return _engine.Services.StatementIsolationService; + } + } + + public DeploymentStateService DeploymentStateService + { + get + { + if (_engine == null) + { + throw new EPServiceDestroyedException(_engineURI); + } + return _engine.Services.DeploymentStateService; + } + } + + /// + /// Gets the scripting service. + /// + /// The scripting service. + public ScriptingService ScriptingService + { + get + { + if (_engine == null) + { + throw new EPServiceDestroyedException(URI); + } + + return _engine.Services.ScriptingService; + } + } + + public void Dispose() + { + lock (this) + { + if (_engine != null) + { + Log.Info("Destroying engine URI '" + _engineURI + "'"); + + try + { + if (ServiceDestroyRequested != null) + { + ServiceDestroyRequested(this, new ServiceProviderEventArgs(this)); + } + } + catch (Exception ex) + { + Log.Error("Exception caught during an ServiceDestroyRequested event:" + ex.Message, ex); + } + + if (_configSnapshot.EngineDefaults.MetricsReporting.IsEnableMetricsReporting) + { + DestroyEngineMetrics(_engine.Services.EngineURI); + } + + // assign null value + var engineToDestroy = _engine; + + engineToDestroy.Services.TimerService.StopInternalClock(false); + // Give the timer thread a little moment to catch up + Thread.Sleep(100); + + // plugin-loaders - destroy in opposite order + var pluginLoaders = engineToDestroy.Services.ConfigSnapshot.PluginLoaders; + if (!pluginLoaders.IsEmpty()) + { + var reversed = new List(pluginLoaders); + reversed.Reverse(); + foreach (var config in reversed) + { + try + { + using ((PluginLoader) engineToDestroy.Services.EngineEnvContext.Lookup("plugin-loader/" + config.LoaderName)) + { + } + } + catch (Exception e) + { + Log.Error("Error destroying plug-in loader: " + config.LoaderName, e); + } + } + } + + engineToDestroy.Services.ThreadingService.Dispose(); + + // assign null - making EPRuntime and EPAdministrator unobtainable + _engine = null; + + engineToDestroy.Runtime.Dispose(); + engineToDestroy.Admin.Dispose(); + engineToDestroy.Services.Dispose(); + _runtimes.Remove(_engineURI); + + engineToDestroy.Services.Initialize(); + } + } + } + + public bool IsDestroyed + { + get { return _engine == null; } + } + + public void Initialize() + { + InitializeInternal(null); + } + + public void Initialize(long? currentTime) + { + InitializeInternal(currentTime); + } + + private void InitializeInternal(long? currentTime) + { + DoInitialize(currentTime); + PostInitialize(); + } + + /// + /// Performs the initialization. + /// + /// optional start time + protected void DoInitialize(long? startTime) + { + Log.Info("Initializing engine URI '" + _engineURI + "' version " + Version.VERSION); + + // This setting applies to all engines in a given VM + ExecutionPathDebugLog.IsEnabled = _configSnapshot.EngineDefaults.Logging.IsEnableExecutionDebug; + ExecutionPathDebugLog.IsTimerDebugEnabled = _configSnapshot.EngineDefaults.Logging.IsEnableTimerDebug; + + // This setting applies to all engines in a given VM + MetricReportingPath.IsMetricsEnabled = + _configSnapshot.EngineDefaults.MetricsReporting.IsEnableMetricsReporting; + + // This setting applies to all engines in a given VM + AuditPath.AuditPattern = _configSnapshot.EngineDefaults.Logging.AuditPattern; + + // This setting applies to all engines in a given VM + ThreadingOption.IsThreadingEnabled = ( + ThreadingOption.IsThreadingEnabled || + _configSnapshot.EngineDefaults.Threading.IsThreadPoolTimerExec || + _configSnapshot.EngineDefaults.Threading.IsThreadPoolInbound || + _configSnapshot.EngineDefaults.Threading.IsThreadPoolRouteExec || + _configSnapshot.EngineDefaults.Threading.IsThreadPoolOutbound + ); + + if (_engine != null) + { + _engine.Services.TimerService.StopInternalClock(false); + // Give the timer thread a little moment to catch up + Thread.Sleep(100); + + if (_configSnapshot.EngineDefaults.MetricsReporting.IsEnableMetricsReporting) + { + DestroyEngineMetrics(_engine.Services.EngineURI); + } + + _engine.Runtime.Initialize(); + + _engine.Services.Dispose(); + } + + // Make EP services context factory + var epServicesContextFactoryClassName = _configSnapshot.EPServicesContextFactoryClassName; + EPServicesContextFactory epServicesContextFactory; + if (epServicesContextFactoryClassName == null) + { + // Check system properties + epServicesContextFactoryClassName = + Environment.GetEnvironmentVariable("ESPER_EPSERVICE_CONTEXT_FACTORY_CLASS"); + } + if (epServicesContextFactoryClassName == null) + { + epServicesContextFactory = new EPServicesContextFactoryDefault(); + } + else + { + Type clazz; + try + { + clazz = + TransientConfigurationResolver.ResolveClassForNameProvider( + _configSnapshot.TransientConfiguration).ClassForName(epServicesContextFactoryClassName); + } + catch (TypeLoadException) + { + throw new ConfigurationException( + "Type '" + epServicesContextFactoryClassName + "' cannot be loaded"); + } + + Object obj; + try + { + obj = Activator.CreateInstance(clazz); + } + catch (TypeLoadException) + { + throw new ConfigurationException("Class '" + clazz + "' cannot be instantiated"); + } + catch (MissingMethodException e) + { + throw new ConfigurationException( + "Error instantiating class - Default constructor was not found", e); + } + catch (MethodAccessException e) + { + throw new ConfigurationException( + "Error instantiating class - Caller does not have permission to use constructor", e); + } + catch (ArgumentException e) + { + throw new ConfigurationException("Error instantiating class - Type is not a RuntimeType", e); + } + + epServicesContextFactory = (EPServicesContextFactory) obj; + } + + var services = epServicesContextFactory.CreateServicesContext(this, _configSnapshot); + + // New runtime + EPRuntimeSPI runtimeSPI; + InternalEventRouteDest routeDest; + TimerCallback timerCallback; + var runtimeClassName = _configSnapshot.EngineDefaults.AlternativeContext.Runtime; + if (runtimeClassName == null) + { + // Check system properties + runtimeClassName = Environment.GetEnvironmentVariable("ESPER_EPRUNTIME_CLASS"); + } + + if (runtimeClassName == null) + { + var runtimeImpl = new EPRuntimeImpl(services); + runtimeSPI = runtimeImpl; + routeDest = runtimeImpl; + timerCallback = runtimeImpl.TimerCallback; + } + else + { + Type clazz; + try + { + clazz = TypeHelper.ResolveType(runtimeClassName, true); + } + catch (TypeLoadException) + { + throw new ConfigurationException("Class '" + runtimeClassName + "' cannot be loaded"); + } + + Object obj; + try + { + var c = clazz.GetConstructor(new[] { typeof (EPServicesContext) }); + obj = c.Invoke(new object[]{ services }); + } + catch (TypeLoadException) + { + throw new ConfigurationException("Class '" + clazz + "' cannot be instantiated"); + } + catch (MissingMethodException) + { + throw new ConfigurationException( + "Class '" + clazz + "' cannot be instantiated, constructor accepting services was not found"); + } + catch (MethodAccessException) + { + throw new ConfigurationException("Illegal access instantiating class '" + clazz + "'"); + } + + runtimeSPI = (EPRuntimeSPI) obj; + routeDest = (InternalEventRouteDest) obj; + timerCallback = (TimerCallback) obj; + } + + routeDest.InternalEventRouter = services.InternalEventRouter; + services.InternalEventEngineRouteDest = routeDest; + + // set current time, if applicable + if (startTime != null) + { + services.SchedulingService.Time = startTime.Value; + } + + // Configure services to use the new runtime + services.TimerService.Callback = timerCallback; + + // Statement lifecycle init + services.StatementLifecycleSvc.Init(); + + // Filter service init + services.FilterService.Init(); + + // Schedule service init + services.SchedulingService.Init(); + + // New admin + var configOps = new ConfigurationOperationsImpl( + services.EventAdapterService, services.EventTypeIdGenerator, services.EngineImportService, + services.VariableService, services.EngineSettingsService, services.ValueAddEventService, + services.MetricsReportingService, services.StatementEventTypeRefService, + services.StatementVariableRefService, services.PlugInViews, services.FilterService, + services.PatternSubexpressionPoolSvc, services.MatchRecognizeStatePoolEngineSvc, services.TableService, + _configSnapshot.TransientConfiguration); + var defaultStreamSelector = + SelectClauseStreamSelectorEnumExtensions.MapFromSODA( + _configSnapshot.EngineDefaults.StreamSelection.DefaultStreamSelector); + EPAdministratorSPI adminSPI; + var adminClassName = _configSnapshot.EngineDefaults.AlternativeContext.Admin; + var adminContext = new EPAdministratorContext(runtimeSPI, services, configOps, defaultStreamSelector); + if (adminClassName == null) + { + // Check system properties + adminClassName = Environment.GetEnvironmentVariable("ESPER_EPADMIN_CLASS"); + } + if (adminClassName == null) + { + adminSPI = new EPAdministratorImpl(adminContext); + } + else + { + Type clazz; + try + { + clazz = TypeHelper.ResolveType(adminClassName, true); + } + catch (TypeLoadException) + { + throw new ConfigurationException( + "Class '" + epServicesContextFactoryClassName + "' cannot be loaded"); + } + + Object obj; + try + { + var c = clazz.GetConstructor(new[] { typeof (EPAdministratorContext) }); + obj = c.Invoke(new[] { adminContext }); + } + catch (TypeLoadException) + { + throw new ConfigurationException("Class '" + clazz + "' cannot be instantiated"); + } + catch (MissingMethodException) + { + throw new ConfigurationException( + "Class '" + clazz + "' cannot be instantiated, constructor accepting context was not found"); + } + catch (MethodAccessException) + { + throw new ConfigurationException("Illegal access instantiating class '" + clazz + "'"); + } + + adminSPI = (EPAdministratorSPI) obj; + } + + // Start clocking + if (_configSnapshot.EngineDefaults.Threading.IsInternalTimerEnabled) + { + if (_configSnapshot.EngineDefaults.TimeSource.TimeUnit != TimeUnit.MILLISECONDS) + { + throw new ConfigurationException("Internal timer requires millisecond time resolution"); + } + services.TimerService.StartInternalClock(); + } + + // Give the timer thread a little moment to start up + Thread.Sleep(100); + + // Save engine instance + _engine = new EPServiceEngine(services, runtimeSPI, adminSPI); + + // Load and initialize adapter loader classes + LoadAdapters(services); + + // Initialize extension services + if (services.EngineLevelExtensionServicesContext != null) + { + services.EngineLevelExtensionServicesContext.Init(services, runtimeSPI, adminSPI); + } + + // Start metrics reporting, if any + if (_configSnapshot.EngineDefaults.MetricsReporting.IsEnableMetricsReporting) + { + services.MetricsReportingService.SetContext(runtimeSPI, services); + } + + // Start engine metrics report + if (_configSnapshot.EngineDefaults.MetricsReporting.IsEnableMetricsReporting) + { + StartEngineMetrics(services, runtimeSPI); + } + + // register with the statement lifecycle service + services.StatementLifecycleSvc.LifecycleEvent += HandleLifecycleEvent; + + // call initialize listeners + try + { + if (ServiceInitialized != null) + { + ServiceInitialized(this, new ServiceProviderEventArgs(this)); + } + } + catch (Exception ex) + { + Log.Error("Runtime exception caught during an ServiceInitialized event:" + ex.Message, ex); + } + } + + /// + /// Raises the event. + /// + /// The instance containing the event data. + protected virtual void OnStatementCreate(StatementStateEventArgs e) + { + if (StatementCreate != null) + StatementCreate(this, new StatementStateEventArgs(this, e.Statement)); + } + + /// + /// Raises the event. + /// + /// The instance containing the event data. + protected virtual void OnStatementStateChanged(StatementLifecycleEvent e) + { + if (StatementStateChange != null) + StatementStateChange(this, new StatementStateEventArgs(this, e.Statement)); + } + + /// + /// Handles the lifecycle event. + /// + /// The sender. + /// The instance containing the event data. + private void HandleLifecycleEvent(object sender, StatementLifecycleEvent e) + { + switch (e.EventType) + { + case StatementLifecycleEvent.LifecycleEventType.CREATE: + OnStatementCreate(new StatementStateEventArgs(this, e.Statement)); + break; + case StatementLifecycleEvent.LifecycleEventType.STATECHANGE: + OnStatementStateChanged(e); + break; + } + } + + private void StartEngineMetrics(EPServicesContext services, EPRuntime runtime) + { + lock (this) + { +#if false + MetricName filterName = MetricNameFactory.Name(services.EngineURI, "filter"); + CommonJMXUtil.RegisterMbean(services.FilterService, filterName); + MetricName scheduleName = MetricNameFactory.Name(services.EngineURI, "schedule"); + CommonJMXUtil.RegisterMbean(services.SchedulingService, scheduleName); + MetricName runtimeName = MetricNameFactory.Name(services.EngineURI, "runtime"); + CommonJMXUtil.RegisterMbean(runtime, runtimeName); +#endif + } + } + + private void DestroyEngineMetrics(string engineURI) + { + lock (this) + { +#if false + CommonJMXUtil.UnregisterMbean(MetricNameFactory.Name(engineURI, "filter")); + CommonJMXUtil.UnregisterMbean(MetricNameFactory.Name(engineURI, "schedule")); + CommonJMXUtil.UnregisterMbean(MetricNameFactory.Name(engineURI, "runtime")); +#endif + } + } + + /// + /// Loads and initializes adapter loaders. + /// + /// is the engine instance services + private void LoadAdapters(EPServicesContext services) + { + var pluginLoaders = _configSnapshot.PluginLoaders; + if ((pluginLoaders == null) || (pluginLoaders.Count == 0)) + { + return; + } + foreach (var config in pluginLoaders) + { + var className = config.TypeName; + Type pluginLoaderClass; + try + { + pluginLoaderClass = services.EngineImportService.GetClassForNameProvider().ClassForName(className); + } + catch (TypeLoadException ex) + { + throw new ConfigurationException("Failed to load adapter loader class '" + className + "'", ex); + } + + Object pluginLoaderObj; + try + { + pluginLoaderObj = Activator.CreateInstance(pluginLoaderClass); + } + catch (TypeLoadException) + { + throw new ConfigurationException("Class '" + pluginLoaderClass + "' cannot be instantiated"); + } + catch (MissingMethodException e) + { + throw new ConfigurationException( + "Error instantiating class - Default constructor was not found", e); + } + catch (MethodAccessException e) + { + throw new ConfigurationException( + "Error instantiating class - Caller does not have permission to use constructor", e); + } + catch (ArgumentException e) + { + throw new ConfigurationException("Error instantiating class - Type is not a RuntimeType", e); + } + + if (!(pluginLoaderObj is PluginLoader)) + { + throw new ConfigurationException( + "Failed to cast adapter loader class '" + className + "' to " + typeof (PluginLoader).FullName); + } + + var pluginLoader = (PluginLoader) pluginLoaderObj; + var context = new PluginLoaderInitContext( + config.LoaderName, config.ConfigProperties, config.ConfigurationXML, this); + pluginLoader.Init(context); + + // register adapter loader in JNDI context tree + services.EngineEnvContext.Bind("plugin-loader/" + config.LoaderName, pluginLoader); + } + } + + private ConfigurationInformation TakeSnapshot(Configuration configuration) + { + try + { + // Allow variables to have non-serializable values by copying their initial value + IDictionary variableInitialValues = null; + if (!configuration.Variables.IsEmpty()) + { + variableInitialValues = new Dictionary(); + foreach (var variable in configuration.Variables) + { + var initializationValue = variable.Value.InitializationValue; + if (initializationValue != null) + { + variableInitialValues.Put(variable.Key, initializationValue); + variable.Value.InitializationValue = null; + } + } + } + + // Avro schemas are not serializable + IDictionary avroSchemas = null; + if (!configuration.EventTypesAvro.IsEmpty()) + { + avroSchemas = new Dictionary(configuration.EventTypesAvro); + configuration.EventTypesAvro.Clear(); + } + + var copy = (Configuration) SerializableObjectCopier.Copy(configuration); + copy.TransientConfiguration = configuration.TransientConfiguration; + + // Restore variable with initial values + if (variableInitialValues != null && !variableInitialValues.IsEmpty()) + { + foreach (var entry in variableInitialValues) + { + var config = copy.Variables.Get(entry.Key); + config.InitializationValue = entry.Value; + } + } + + // Restore Avro schemas + if (avroSchemas != null) + { + copy.EventTypesAvro.PutAll(avroSchemas); + } + + return copy; + } + catch (IOException e) + { + throw new ConfigurationException( + "Failed to snapshot configuration instance through serialization : " + e.Message, e); + } + catch (TypeLoadException e) + { + throw new ConfigurationException( + "Failed to snapshot configuration instance through serialization : " + e.Message, e); + } + } + + /// + /// Clears the service state event handlers. For internal use only. + /// + public virtual void RemoveAllServiceStateEventHandlers() + { + ServiceInitialized = null; + ServiceDestroyRequested = null; + } + + public IList EPServiceIsolatedNames + { + get + { + if (_engine == null) + { + throw new EPServiceDestroyedException(_engineURI); + } + return _engine.Services.StatementIsolationService.IsolationUnitNames; + } + } + + public SchedulingMgmtService SchedulingMgmtService + { + get + { + if (_engine == null) + { + throw new EPServiceDestroyedException(_engineURI); + } + return _engine.Services.SchedulingMgmtService; + } + } + + public EngineImportService EngineImportService + { + get + { + if (_engine == null) + { + throw new EPServiceDestroyedException(_engineURI); + } + return _engine.Services.EngineImportService; + } + } + + public TimeProvider TimeProvider + { + get + { + if (_engine == null) + { + throw new EPServiceDestroyedException(_engineURI); + } + return _engine.Services.SchedulingService; + } + } + + public VariableService VariableService + { + get + { + if (_engine == null) + { + throw new EPServiceDestroyedException(_engineURI); + } + return _engine.Services.VariableService; + } + } + + public ContextManagementService ContextManagementService + { + get + { + if (_engine == null) + { + throw new EPServiceDestroyedException(_engineURI); + } + return _engine.Services.ContextManagementService; + } + } + + public IReaderWriterLock EngineInstanceWideLock + { + get + { + if (_engine == null) + { + throw new EPServiceDestroyedException(_engineURI); + } + return _engine.Services.EventProcessingRWLock; + } + } + + private class EPServiceEngine + { + public EPServiceEngine(EPServicesContext services, EPRuntimeSPI runtimeSPI, EPAdministratorSPI admin) + { + Services = services; + Runtime = runtimeSPI; + Admin = admin; + } + + public EPServicesContext Services { get; private set; } + + public EPRuntimeSPI Runtime { get; private set; } + + public EPAdministratorSPI Admin { get; private set; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/service/EPServiceProviderIsolatedImpl.cs b/NEsper.Core/NEsper.Core/core/service/EPServiceProviderIsolatedImpl.cs new file mode 100755 index 000000000..a7ed21bc4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/EPServiceProviderIsolatedImpl.cs @@ -0,0 +1,76 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.logging; + +namespace com.espertech.esper.core.service +{ + /// + /// Implementation of the isolated service provider. + /// + public class EPServiceProviderIsolatedImpl : EPServiceProviderIsolatedSPI + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly String _name; + private readonly EPRuntimeIsolatedSPI _runtime; + private readonly EPAdministratorIsolatedImpl _admin; + private readonly EPIsolationUnitServices _isolatedServices; + private readonly IDictionary _providers; + + /// Ctor. + /// name of isolated service + /// filter and scheduling service isolated + /// engine services + /// names and isolated service providers + public EPServiceProviderIsolatedImpl( + String name, + EPIsolationUnitServices isolatedServices, + EPServicesContext unisolatedSvc, + IDictionary providers) + { + _name = name; + _providers = providers; + _isolatedServices = isolatedServices; + + _runtime = unisolatedSvc.EpRuntimeIsolatedFactory.Make(isolatedServices, unisolatedSvc); + _admin = new EPAdministratorIsolatedImpl(name, isolatedServices, unisolatedSvc, _runtime); + } + + public EPIsolationUnitServices IsolatedServices + { + get { return _isolatedServices; } + } + + public EPRuntimeIsolated EPRuntime + { + get { return _runtime; } + } + + public EPAdministratorIsolated EPAdministrator + { + get { return _admin; } + } + + public string Name + { + get { return _name; } + } + + public void Dispose() + { + _providers.Remove(_name); + + _admin.RemoveAllStatements(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/EPServiceProviderIsolatedSPI.cs b/NEsper.Core/NEsper.Core/core/service/EPServiceProviderIsolatedSPI.cs new file mode 100755 index 000000000..a470ff332 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/EPServiceProviderIsolatedSPI.cs @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; + +namespace com.espertech.esper.core.service +{ + /// SPU for isolated service provider. + public interface EPServiceProviderIsolatedSPI : EPServiceProviderIsolated + { + /// Return isolated services. + /// isolated services + EPIsolationUnitServices IsolatedServices { get; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/service/EPServiceProviderSPI.cs b/NEsper.Core/NEsper.Core/core/service/EPServiceProviderSPI.cs new file mode 100755 index 000000000..e08926263 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/EPServiceProviderSPI.cs @@ -0,0 +1,141 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.core.context.mgr; +using com.espertech.esper.core.deploy; +using com.espertech.esper.core.thread; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.metric; +using com.espertech.esper.epl.named; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.epl.variable; +using com.espertech.esper.events; +using com.espertech.esper.events.vaevent; +using com.espertech.esper.filter; +using com.espertech.esper.schedule; +using com.espertech.esper.script; +using com.espertech.esper.timer; + +namespace com.espertech.esper.core.service +{ + /// + /// A service provider interface that makes available internal engine services. + /// + public interface EPServiceProviderSPI : EPServiceProvider + { + /// Returns statement management service for the engine. + /// the StatementLifecycleSvc + StatementLifecycleSvc StatementLifecycleSvc { get; } + + /// Get the EventAdapterService for this engine. + /// the EventAdapterService + EventAdapterService EventAdapterService { get; } + + /// Get the SchedulingService for this engine. + /// the SchedulingService + SchedulingService SchedulingService { get; } + + /// Get the SchedulingMgmtService for this engine. + /// the SchedulingMgmtService + SchedulingMgmtService SchedulingMgmtService { get; } + + /// Returns the filter service. + /// filter service + FilterService FilterService { get; } + + /// Returns the timer service. + /// timer service + TimerService TimerService { get; } + + /// Returns the named window service. + /// named window service + NamedWindowMgmtService NamedWindowMgmtService { get; } + + /// Returns the table service. + /// The table service. + TableService TableService { get; } + + /// Returns the current configuration. + /// configuration information + ConfigurationInformation ConfigurationInformation { get; } + + /// Returns the extension services context. + /// extension services context + EngineLevelExtensionServicesContext ExtensionServicesContext { get; } + + /// Returns metrics reporting. + /// metrics reporting + MetricReportingService MetricReportingService { get; } + + /// Returns variable services. + /// services + VariableService VariableService { get; } + + /// Returns value-added type service. + /// value types + ValueAddEventService ValueAddEventService { get; } + + /// Returns statement event type reference service. + /// statement-type reference service + StatementEventTypeRef StatementEventTypeRef { get; } + + /// Returns threading service for the engine. + /// the ThreadingService + ThreadingService ThreadingService { get; } + + /// Returns engine environment context such as plugin loader references. + /// environment context + Directory EngineEnvContext { get; } + + /// Returns services. + /// services + EPServicesContext ServicesContext { get; } + + /// Returns context factory. + /// factory + StatementContextFactory StatementContextFactory { get; } + + /// Returns engine imports. + /// engine imports + EngineImportService EngineImportService { get; } + + /// Returns time provider. + /// time provider + TimeProvider TimeProvider { get; } + + StatementIsolationService StatementIsolationService { get; } + + DeploymentStateService DeploymentStateService { get; } + + ContextManagementService ContextManagementService { get; } + + /// + /// Gets the scripting service. + /// + /// The scripting service. + ScriptingService ScriptingService { get; } + + void SetConfiguration(Configuration configuration); + + void PostInitialize(); + + void Initialize(long? currentTime); + } + + public class EPServiceProviderConstants + { + /// For the default provider instance, which carries a null provider URI, the URI value is "default". + public static readonly String DEFAULT_ENGINE_URI = "default"; + + /// For the default provider instance, which carries a "default" provider URI, the property name qualification and stream name qualification may use "default". + public static readonly String DEFAULT_ENGINE_URI_QUALIFIER = "default"; + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/EPServicesContext.cs b/NEsper.Core/NEsper.Core/core/service/EPServicesContext.cs new file mode 100755 index 000000000..338f31a17 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/EPServicesContext.cs @@ -0,0 +1,523 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat.threading; +using com.espertech.esper.core.context.activator; +using com.espertech.esper.core.context.mgr; +using com.espertech.esper.core.deploy; +using com.espertech.esper.core.service.multimatch; +using com.espertech.esper.core.thread; +using com.espertech.esper.dataflow.core; +using com.espertech.esper.dispatch; +using com.espertech.esper.epl.agg.factory; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.db; +using com.espertech.esper.epl.declexpr; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.epl.metric; +using com.espertech.esper.epl.named; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.epl.variable; +using com.espertech.esper.events; +using com.espertech.esper.events.vaevent; +using com.espertech.esper.filter; +using com.espertech.esper.pattern; +using com.espertech.esper.pattern.pool; +using com.espertech.esper.rowregex; +using com.espertech.esper.schedule; +using com.espertech.esper.script; +using com.espertech.esper.timer; +using com.espertech.esper.view; +using com.espertech.esper.view.stream; + +namespace com.espertech.esper.core.service +{ + /// + /// Convenience class to hold implementations for all services. + /// + public sealed class EPServicesContext + { + // Supplied after construction to avoid circular dependency + + /// + /// Constructor - sets up new set of services. + /// + /// is the engine URI + /// service to get time and schedule callbacks + /// service to resolve event types + /// is engine imported static func packages and aggregation functions + /// provides engine settings + /// service to resolve a database name to database connection factory and configs + /// resolves view namespace and name to view factory class + /// creates statement-level locks + /// is the engine lock for statement management + /// marker interface allows adding additional services + /// is engine environment/directory information for use with adapters and external env + /// is the factory to use to create statement context objects + /// resolves plug-in pattern objects + /// is the timer service + /// the filter service + /// is hooking up filters to streams + /// The named window MGMT service. + /// The named window dispatch service. + /// provides access to variable values + /// The table service. + /// time source provider class + /// handles Update events + /// for metric reporting + /// statement to event type reference holding + /// statement to variabke reference holding + /// configuration snapshot + /// engine-level threading services + /// routing of events + /// maintains isolation information per statement + /// schedule management for statements + /// The deployment state service. + /// The exception handling service. + /// The pattern node factory. + /// The event type id generator. + /// The statement metadata factory. + /// The context management service. + /// The pattern subexpression pool SVC. + /// The match recognize state pool engine SVC. + /// The data flow service. + /// The expr declared service. + /// The context controller factory factory SVC. + /// The context manager factory service. + /// The ep statement factory. + /// The regex handler factory. + /// The viewable activator factory. + /// The filter non property registery service. + /// The result set processor helper factory. + /// The view service previous factory. + /// The event table index service. + /// The ep runtime isolated factory. + /// The filter boolean expression factory. + /// The data cache factory. + /// The multi match handler factory. + /// The named window consumer MGMT service. + /// + /// The scripting service. + public EPServicesContext( + string engineURI, + SchedulingServiceSPI schedulingService, + EventAdapterService eventAdapterService, + EngineImportService engineImportService, + EngineSettingsService engineSettingsService, + DatabaseConfigService databaseConfigService, + PluggableObjectCollection plugInViews, + StatementLockFactory statementLockFactory, + IReaderWriterLock eventProcessingRWLock, + EngineLevelExtensionServicesContext extensionServicesContext, + Directory engineEnvContext, + StatementContextFactory statementContextFactory, + PluggableObjectCollection plugInPatternObjects, + TimerService timerService, + FilterServiceSPI filterService, + StreamFactoryService streamFactoryService, + NamedWindowMgmtService namedWindowMgmtService, + NamedWindowDispatchService namedWindowDispatchService, + VariableService variableService, + TableService tableService, + TimeSourceService timeSourceService, + ValueAddEventService valueAddEventService, + MetricReportingServiceSPI metricsReportingService, + StatementEventTypeRef statementEventTypeRef, + StatementVariableRef statementVariableRef, + ConfigurationInformation configSnapshot, + ThreadingService threadingServiceImpl, + InternalEventRouterImpl internalEventRouter, + StatementIsolationService statementIsolationService, + SchedulingMgmtService schedulingMgmtService, + DeploymentStateService deploymentStateService, + ExceptionHandlingService exceptionHandlingService, + PatternNodeFactory patternNodeFactory, + EventTypeIdGenerator eventTypeIdGenerator, + StatementMetadataFactory statementMetadataFactory, + ContextManagementService contextManagementService, + PatternSubexpressionPoolEngineSvc patternSubexpressionPoolSvc, + MatchRecognizeStatePoolEngineSvc matchRecognizeStatePoolEngineSvc, + DataFlowService dataFlowService, + ExprDeclaredService exprDeclaredService, + ContextControllerFactoryFactorySvc contextControllerFactoryFactorySvc, + ContextManagerFactoryService contextManagerFactoryService, + EPStatementFactory epStatementFactory, + RegexHandlerFactory regexHandlerFactory, + ViewableActivatorFactory viewableActivatorFactory, + FilterNonPropertyRegisteryService filterNonPropertyRegisteryService, + ResultSetProcessorHelperFactory resultSetProcessorHelperFactory, + ViewServicePreviousFactory viewServicePreviousFactory, + EventTableIndexService eventTableIndexService, + EPRuntimeIsolatedFactory epRuntimeIsolatedFactory, + FilterBooleanExpressionFactory filterBooleanExpressionFactory, + DataCacheFactory dataCacheFactory, + MultiMatchHandlerFactory multiMatchHandlerFactory, + NamedWindowConsumerMgmtService namedWindowConsumerMgmtService, + AggregationFactoryFactory aggregationFactoryFactory, + ScriptingService scriptingService) + { + EngineURI = engineURI; + SchedulingService = schedulingService; + EventAdapterService = eventAdapterService; + EngineImportService = engineImportService; + EngineSettingsService = engineSettingsService; + DatabaseRefService = databaseConfigService; + FilterService = filterService; + TimerService = timerService; + DispatchService = DispatchServiceProvider.NewService(); + ViewService = ViewServiceProvider.NewService(); + StreamService = streamFactoryService; + PlugInViews = plugInViews; + StatementLockFactory = statementLockFactory; + EventProcessingRWLock = eventProcessingRWLock; + EngineLevelExtensionServicesContext = extensionServicesContext; + EngineEnvContext = engineEnvContext; + StatementContextFactory = statementContextFactory; + PlugInPatternObjects = plugInPatternObjects; + NamedWindowMgmtService = namedWindowMgmtService; + NamedWindowDispatchService = namedWindowDispatchService; + VariableService = variableService; + TableService = tableService; + TimeSource = timeSourceService; + ValueAddEventService = valueAddEventService; + MetricsReportingService = metricsReportingService; + StatementEventTypeRefService = statementEventTypeRef; + ConfigSnapshot = configSnapshot; + ThreadingService = threadingServiceImpl; + InternalEventRouter = internalEventRouter; + StatementIsolationService = statementIsolationService; + SchedulingMgmtService = schedulingMgmtService; + StatementVariableRefService = statementVariableRef; + DeploymentStateService = deploymentStateService; + ExceptionHandlingService = exceptionHandlingService; + PatternNodeFactory = patternNodeFactory; + EventTypeIdGenerator = eventTypeIdGenerator; + StatementMetadataFactory = statementMetadataFactory; + ContextManagementService = contextManagementService; + PatternSubexpressionPoolSvc = patternSubexpressionPoolSvc; + MatchRecognizeStatePoolEngineSvc = matchRecognizeStatePoolEngineSvc; + DataFlowService = dataFlowService; + ExprDeclaredService = exprDeclaredService; + ExpressionResultCacheSharable = new ExpressionResultCacheService(configSnapshot.EngineDefaults.Execution.DeclaredExprValueCacheSize); + ContextControllerFactoryFactorySvc = contextControllerFactoryFactorySvc; + ContextManagerFactoryService = contextManagerFactoryService; + EpStatementFactory = epStatementFactory; + RegexHandlerFactory = regexHandlerFactory; + ViewableActivatorFactory = viewableActivatorFactory; + FilterNonPropertyRegisteryService = filterNonPropertyRegisteryService; + ResultSetProcessorHelperFactory = resultSetProcessorHelperFactory; + ViewServicePreviousFactory = viewServicePreviousFactory; + EventTableIndexService = eventTableIndexService; + EpRuntimeIsolatedFactory = epRuntimeIsolatedFactory; + FilterBooleanExpressionFactory = filterBooleanExpressionFactory; + DataCacheFactory = dataCacheFactory; + MultiMatchHandlerFactory = multiMatchHandlerFactory; + NamedWindowConsumerMgmtService = namedWindowConsumerMgmtService; + AggregationFactoryFactory = aggregationFactoryFactory; + ScriptingService = scriptingService; + } + + public PatternNodeFactory PatternNodeFactory { get; private set; } + + /// Returns the event routing destination. + /// event routing destination + public InternalEventRouteDest InternalEventEngineRouteDest { get; set; } + + /// Returns router for internal event processing. + /// router for internal event processing + public InternalEventRouterImpl InternalEventRouter { get; private set; } + + /// Returns filter evaluation service implementation. + /// filter evaluation service + public FilterServiceSPI FilterService { get; private set; } + + /// Returns time provider service implementation. + /// time provider service + public TimerService TimerService { get; private set; } + + /// Returns scheduling service implementation. + /// scheduling service + public SchedulingServiceSPI SchedulingService { get; private set; } + + /// Returns dispatch service responsible for dispatching events to listeners. + /// dispatch service. + public DispatchService DispatchService { get; private set; } + + /// Returns services for view creation, sharing and removal. + /// view service + public ViewService ViewService { get; private set; } + + /// Returns stream service. + /// stream service + public StreamFactoryService StreamService { get; private set; } + + /// Returns event type resolution service. + /// service resolving event type + public EventAdapterService EventAdapterService { get; private set; } + + /// Returns the import and class name resolution service. + /// import service + public EngineImportService EngineImportService { get; private set; } + + /// Returns the database settings service. + /// database info service + public DatabaseConfigService DatabaseRefService { get; private set; } + + /// Information to resolve plug-in view namespace and name. + /// plug-in view information + public PluggableObjectCollection PlugInViews { get; private set; } + + /// Information to resolve plug-in pattern object namespace and name. + /// plug-in pattern object information + public PluggableObjectCollection PlugInPatternObjects { get; private set; } + + /// Factory for statement-level locks. + /// factory + public StatementLockFactory StatementLockFactory { get; private set; } + + /// Returns the event processing lock for coordinating statement administration with event processing. + /// lock + public IReaderWriterLock EventProcessingRWLock { get; private set; } + + /// Returns statement lifecycle svc + /// service for statement start and stop + public StatementLifecycleSvc StatementLifecycleSvc { get; internal set; } + + /// Returns extension service for adding custom the services. + /// extension service context + public EngineLevelExtensionServicesContext EngineLevelExtensionServicesContext { get; private set; } + + /// Returns the engine environment context for getting access to engine-external resources, such as adapters + /// engine environment context + public Directory EngineEnvContext { get; private set; } + + /// Returns engine-level threading settings. + /// threading service + public ThreadingService ThreadingService { get; private set; } + + /// Returns the factory to use for creating a statement context. + /// statement context factory + public StatementContextFactory StatementContextFactory { get; private set; } + + /// Returns the engine URI. + /// engine URI + public string EngineURI { get; private set; } + + /// Returns engine settings. + /// settings + public EngineSettingsService EngineSettingsService { get; private set; } + + /// Returns the named window management service. + /// service for managing named windows + public NamedWindowMgmtService NamedWindowMgmtService { get; private set; } + + /// Gets the named window dispatch service. + /// The named window dispatch service. + public NamedWindowDispatchService NamedWindowDispatchService { get; private set; } + + /// Returns the variable service. + /// variable service + public VariableService VariableService { get; private set; } + + /// Returns the time source provider class. + /// time source + public TimeSourceService TimeSource { get; private set; } + + /// Returns the service for handling updates to events. + /// revision service + public ValueAddEventService ValueAddEventService { get; private set; } + + /// Returns metrics reporting. + /// metrics reporting + public MetricReportingServiceSPI MetricsReportingService { get; private set; } + + /// Returns service for statement to event type mapping. + /// statement-type mapping + public StatementEventTypeRef StatementEventTypeRefService { get; private set; } + + /// Returns the configuration. + /// configuration + public ConfigurationInformation ConfigSnapshot { get; private set; } + + /// Service for keeping track of variable-statement use. + /// svc + public StatementVariableRef StatementVariableRefService { get; private set; } + + /// Returns the schedule management service. + /// schedule management service + public SchedulingMgmtService SchedulingMgmtService { get; private set; } + + /// Returns the service for maintaining statement isolation information. + /// isolation service + public StatementIsolationService StatementIsolationService { get; set; } + + public DeploymentStateService DeploymentStateService { get; private set; } + + public ExceptionHandlingService ExceptionHandlingService { get; private set; } + + public EventTypeIdGenerator EventTypeIdGenerator { get; private set; } + + public StatementMetadataFactory StatementMetadataFactory { get; private set; } + + public ContextManagementService ContextManagementService { get; private set; } + + public PatternSubexpressionPoolEngineSvc PatternSubexpressionPoolSvc { get; private set; } + + public MatchRecognizeStatePoolEngineSvc MatchRecognizeStatePoolEngineSvc { get; private set; } + + public DataFlowService DataFlowService { get; private set; } + + public ExprDeclaredService ExprDeclaredService { get; private set; } + + public ExpressionResultCacheService ExpressionResultCacheSharable { get; private set; } + + public ScriptingService ScriptingService { get; private set; } + + public TableService TableService { get; private set; } + + public ContextControllerFactoryFactorySvc ContextControllerFactoryFactorySvc { get; private set; } + + public EPStatementFactory EpStatementFactory { get; private set; } + + public ContextManagerFactoryService ContextManagerFactoryService { get; private set; } + + public RegexHandlerFactory RegexHandlerFactory { get; private set; } + + public ViewableActivatorFactory ViewableActivatorFactory { get; private set; } + + public FilterNonPropertyRegisteryService FilterNonPropertyRegisteryService { get; private set; } + + public ResultSetProcessorHelperFactory ResultSetProcessorHelperFactory { get; private set; } + + public ViewServicePreviousFactory ViewServicePreviousFactory { get; private set; } + + public EventTableIndexService EventTableIndexService { get; private set; } + + public EPRuntimeIsolatedFactory EpRuntimeIsolatedFactory { get; private set; } + + public FilterBooleanExpressionFactory FilterBooleanExpressionFactory { get; private set; } + + public DataCacheFactory DataCacheFactory { get; private set; } + + public MultiMatchHandlerFactory MultiMatchHandlerFactory { get; private set; } + + public NamedWindowConsumerMgmtService NamedWindowConsumerMgmtService { get; private set; } + + public AggregationFactoryFactory AggregationFactoryFactory { get; private set; } + + /// Sets the service dealing with starting and stopping statements. + /// statement lifycycle svc + public void SetStatementLifecycleSvc(StatementLifecycleSvc statementLifecycleSvc) + { + StatementLifecycleSvc = statementLifecycleSvc; + } + + /// Dispose services. + public void Dispose() + { + if (ScriptingService != null) + { + ScriptingService.Dispose(); + } + if (ExprDeclaredService != null) + { + ExprDeclaredService.Dispose(); + } + if (DataFlowService != null) + { + DataFlowService.Dispose(); + } + if (VariableService != null) + { + VariableService.Dispose(); + } + if (MetricsReportingService != null) + { + MetricsReportingService.Dispose(); + } + if (ThreadingService != null) + { + ThreadingService.Dispose(); + } + if (StatementLifecycleSvc != null) + { + StatementLifecycleSvc.Dispose(); + } + if (FilterService != null) + { + FilterService.Dispose(); + } + if (SchedulingService != null) + { + SchedulingService.Dispose(); + } + if (SchedulingMgmtService != null) + { + SchedulingMgmtService.Dispose(); + } + if (StreamService != null) + { + StreamService.Destroy(); + } + if (NamedWindowMgmtService != null) + { + NamedWindowMgmtService.Dispose(); + } + if (NamedWindowDispatchService != null) + { + NamedWindowDispatchService.Dispose(); + } + if (EngineLevelExtensionServicesContext != null) + { + EngineLevelExtensionServicesContext.Dispose(); + } + if (StatementIsolationService != null) + { + StatementIsolationService.Dispose(); + } + if (DeploymentStateService != null) + { + DeploymentStateService.Dispose(); + } + } + + /// Dispose services. + public void Initialize() + { + ScriptingService = null; + StatementLifecycleSvc = null; + EngineURI = null; + SchedulingService = null; + EventAdapterService = null; + EngineImportService = null; + EngineSettingsService = null; + DatabaseRefService = null; + FilterService = null; + TimerService = null; + DispatchService = null; + ViewService = null; + StreamService = null; + PlugInViews = null; + StatementLockFactory = null; + EngineLevelExtensionServicesContext = null; + EngineEnvContext = null; + StatementContextFactory = null; + PlugInPatternObjects = null; + NamedWindowMgmtService = null; + ValueAddEventService = null; + MetricsReportingService = null; + StatementEventTypeRefService = null; + ThreadingService = null; + ExpressionResultCacheSharable = null; + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/service/EPServicesContextFactory.cs b/NEsper.Core/NEsper.Core/core/service/EPServicesContextFactory.cs new file mode 100755 index 000000000..2fd9cd4dc --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/EPServicesContextFactory.cs @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; + + +namespace com.espertech.esper.core.service +{ + /// Interface for a factory class to provide services in a services context for an engine instance. + public interface EPServicesContextFactory + { + /// Factory method for a new set of engine services. + /// is the engine instance + /// is a snapshot of configs at the time of engine creation + /// services context + EPServicesContext CreateServicesContext(EPServiceProvider epServiceProvider, ConfigurationInformation configurationSnapshot); + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/EPServicesContextFactoryDefault.cs b/NEsper.Core/NEsper.Core/core/service/EPServicesContextFactoryDefault.cs new file mode 100755 index 000000000..3d0e2a690 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/EPServicesContextFactoryDefault.cs @@ -0,0 +1,872 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.client.hook; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.compat.threading; +using com.espertech.esper.core.context.activator; +using com.espertech.esper.core.context.mgr; +using com.espertech.esper.core.deploy; +using com.espertech.esper.core.service.multimatch; +using com.espertech.esper.core.start; +using com.espertech.esper.core.thread; +using com.espertech.esper.dataflow.core; +using com.espertech.esper.epl.agg.factory; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.db; +using com.espertech.esper.epl.declexpr; +using com.espertech.esper.epl.expression.time; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.epl.metric; +using com.espertech.esper.epl.named; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.epl.variable; +using com.espertech.esper.events.avro; +using com.espertech.esper.events; +using com.espertech.esper.events.vaevent; +using com.espertech.esper.events.xml; +using com.espertech.esper.filter; +using com.espertech.esper.pattern; +using com.espertech.esper.pattern.pool; +using com.espertech.esper.plugin; +using com.espertech.esper.rowregex; +using com.espertech.esper.schedule; +using com.espertech.esper.script; +using com.espertech.esper.timer; +using com.espertech.esper.util; +using com.espertech.esper.view; +using com.espertech.esper.view.stream; + +namespace com.espertech.esper.core.service +{ + /// + /// Factory for services context. + /// + public class EPServicesContextFactoryDefault : EPServicesContextFactory + { + private static readonly ILog Log = + LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + internal static ExceptionHandlingService InitExceptionHandling( + string engineURI, + ConfigurationEngineDefaults.ExceptionHandlingConfig exceptionHandling, + ConfigurationEngineDefaults.ConditionHandlingConfig conditionHandling, + EngineImportService engineImportService) + { + IList exceptionHandlers; + if (exceptionHandling.HandlerFactories == null || exceptionHandling.HandlerFactories.IsEmpty()) + { + exceptionHandlers = Collections.GetEmptyList(); + } + else + { + exceptionHandlers = new List(); + var context = new ExceptionHandlerFactoryContext(engineURI); + foreach (var className in exceptionHandling.HandlerFactories) + { + try + { + var factory = TypeHelper.Instantiate( + className, engineImportService.GetClassForNameProvider()); + var handler = factory.GetHandler(context); + if (handler == null) + { + Log.Warn( + "Exception handler factory '" + className + + "' returned a null handler, skipping factory"); + continue; + } + exceptionHandlers.Add(handler); + } + catch (Exception ex) + { + throw new ConfigurationException( + "Exception initializing exception handler from exception handler factory '" + className + + "': " + ex.Message, ex); + } + } + } + + IList conditionHandlers; + if (conditionHandling.HandlerFactories == null || conditionHandling.HandlerFactories.IsEmpty()) + { + conditionHandlers = Collections.GetEmptyList(); + } + else + { + conditionHandlers = new List(); + var context = new ConditionHandlerFactoryContext(engineURI); + foreach (var className in conditionHandling.HandlerFactories) + { + try + { + var factory = TypeHelper.Instantiate( + className, engineImportService.GetClassForNameProvider()); + var handler = factory.GetHandler(context); + if (handler == null) + { + Log.Warn( + "Condition handler factory '" + className + + "' returned a null handler, skipping factory"); + continue; + } + conditionHandlers.Add(handler); + } + catch (Exception ex) + { + throw new ConfigurationException( + "Exception initializing exception handler from exception handler factory '" + className + + "': " + ex.Message, ex); + } + } + } + return new ExceptionHandlingService(engineURI, exceptionHandlers, conditionHandlers); + } + + /// + /// Makes the time source provider. + /// + /// the configuration + /// time source provider + internal static TimeSourceService MakeTimeSource(ConfigurationInformation configSnapshot) + { + if (configSnapshot.EngineDefaults.TimeSource.TimeSourceType == + ConfigurationEngineDefaults.TimeSourceType.NANO) + { + // this is a static variable to keep overhead down for getting a current time + TimeSourceServiceImpl.IsSystemCurrentTime = false; + } + return new TimeSourceServiceImpl(); + } + + /// + /// Adds configured variables to the variable service. + /// + /// service to add to + /// configured variables + /// engine imports + internal static void InitVariables( + VariableService variableService, + IDictionary variables, + EngineImportService engineImportService) + { + foreach (var entry in variables) + { + try + { + var arrayType = TypeHelper.IsGetArrayType(entry.Value.VariableType); + variableService.CreateNewVariable( + null, entry.Key, arrayType.First, entry.Value.IsConstant, arrayType.Second, false, + entry.Value.InitializationValue, engineImportService); + variableService.AllocateVariableState( + entry.Key, EPStatementStartMethodConst.DEFAULT_AGENT_INSTANCE_ID, null, false); + } + catch (VariableExistsException e) + { + throw new ConfigurationException("Error configuring variables: " + e.Message, e); + } + catch (VariableTypeException e) + { + throw new ConfigurationException("Error configuring variables: " + e.Message, e); + } + } + } + + /// + /// Initialize event adapter service for config snapshot. + /// + /// is events adapter + /// is the config snapshot + /// engine import service + internal static void Init( + EventAdapterService eventAdapterService, + ConfigurationInformation configSnapshot, + EngineImportService engineImportService) + { + // Extract legacy event type definitions for each event type name, if supplied. + // + // We supply this information as setup information to the event adapter service + // to allow discovery of superclasses and interfaces during event type construction for bean events, + // such that superclasses and interfaces can use the legacy type definitions. + var classLegacyInfo = new Dictionary(); + foreach (var entry in configSnapshot.EventTypeNames) + { + var typeName = entry.Key; + var className = entry.Value; + var legacyDef = configSnapshot.EventTypesLegacy.Get(typeName); + if (legacyDef != null) + { + classLegacyInfo.Put(className, legacyDef); + } + } + eventAdapterService.TypeLegacyConfigs = classLegacyInfo; + eventAdapterService.DefaultPropertyResolutionStyle = + configSnapshot.EngineDefaults.EventMeta.ClassPropertyResolutionStyle; + eventAdapterService.DefaultAccessorStyle = configSnapshot.EngineDefaults.EventMeta.DefaultAccessorStyle; + + foreach (var typeNamespace in configSnapshot.EventTypeAutoNamePackages) + { + eventAdapterService.AddAutoNamePackage(typeNamespace); + } + + // Add from the configuration the event class names + var typeNames = configSnapshot.EventTypeNames; + foreach (var entry in typeNames) + { + // Add class + try + { + var typeName = entry.Key; + eventAdapterService.AddBeanType(typeName, entry.Value, false, true, true, true); + } + catch (EventAdapterException ex) + { + throw new ConfigurationException("Error configuring engine: " + ex.Message, ex); + } + } + + // Add from the configuration the Java event class names + var avroNames = configSnapshot.EventTypesAvro; + foreach (var entry in avroNames) + { + try + { + eventAdapterService.AddAvroType(entry.Key, entry.Value, true, true, true, false, false); + } + catch (EventAdapterException ex) + { + throw new ConfigurationException("Error configuring engine: " + ex.Message, ex); + } + } + + // Add from the configuration the XML DOM names and type def + var xmlDOMNames = configSnapshot.EventTypesXMLDOM; + foreach (var entry in xmlDOMNames) + { + SchemaModel schemaModel = null; + if ((entry.Value.SchemaResource != null) || (entry.Value.SchemaText != null)) + { + try + { + schemaModel = XSDSchemaMapper.LoadAndMap( + entry.Value.SchemaResource, entry.Value.SchemaText, engineImportService); + } + catch (Exception ex) + { + throw new ConfigurationException(ex.Message, ex); + } + } + + // Add XML DOM type + try + { + eventAdapterService.AddXMLDOMType(entry.Key, entry.Value, schemaModel, true); + } + catch (EventAdapterException ex) + { + throw new ConfigurationException("Error configuring engine: " + ex.Message, ex); + } + } + + // Add maps in dependency order such that supertypes are added before subtypes + ICollection dependentMapOrder; + try + { + var typesReferences = ToTypesReferences(configSnapshot.MapTypeConfigurations); + dependentMapOrder = GraphUtil.GetTopDownOrder(typesReferences); + } + catch (GraphCircularDependencyException e) + { + throw new ConfigurationException( + "Error configuring engine, dependency graph between map type names is circular: " + e.Message, e); + } + + var mapNames = configSnapshot.EventTypesMapEvents; + var nestableMapNames = configSnapshot.EventTypesNestableMapEvents; + dependentMapOrder.AddAll(mapNames.Keys); + dependentMapOrder.AddAll(nestableMapNames.Keys); + try + { + foreach (var mapName in dependentMapOrder) + { + var mapConfig = configSnapshot.MapTypeConfigurations.Get(mapName); + var propertiesUnnested = mapNames.Get(mapName); + if (propertiesUnnested != null) + { + var propertyTypes = CreatePropertyTypes(propertiesUnnested, engineImportService); + var propertyTypesCompiled = EventTypeUtility.CompileMapTypeProperties( + propertyTypes, eventAdapterService); + eventAdapterService.AddNestableMapType( + mapName, propertyTypesCompiled, mapConfig, true, true, true, false, false); + } + + var propertiesNestable = nestableMapNames.Get(mapName); + if (propertiesNestable != null) + { + var propertiesNestableCompiled = EventTypeUtility.CompileMapTypeProperties( + propertiesNestable, eventAdapterService); + eventAdapterService.AddNestableMapType( + mapName, propertiesNestableCompiled, mapConfig, true, true, true, false, false); + } + } + } + catch (EventAdapterException ex) + { + throw new ConfigurationException("Error configuring engine: " + ex.Message, ex); + } + + // Add object-array in dependency order such that supertypes are added before subtypes + ICollection dependentObjectArrayOrder; + try + { + var typesReferences = ToTypesReferences(configSnapshot.ObjectArrayTypeConfigurations); + dependentObjectArrayOrder = GraphUtil.GetTopDownOrder(typesReferences); + } + catch (GraphCircularDependencyException e) + { + throw new ConfigurationException( + "Error configuring engine, dependency graph between object array type names is circular: " + + e.Message, e); + } + var nestableObjectArrayNames = configSnapshot.EventTypesNestableObjectArrayEvents; + dependentObjectArrayOrder.AddAll(nestableObjectArrayNames.Keys); + try + { + foreach (var objectArrayName in dependentObjectArrayOrder) + { + var objectArrayConfig = configSnapshot.ObjectArrayTypeConfigurations.Get(objectArrayName); + var propertyTypes = nestableObjectArrayNames.Get(objectArrayName); + propertyTypes = ResolveClassesForStringPropertyTypes(propertyTypes, engineImportService); + var propertyTypesCompiled = EventTypeUtility.CompileMapTypeProperties( + propertyTypes, eventAdapterService); + eventAdapterService.AddNestableObjectArrayType( + objectArrayName, propertyTypesCompiled, objectArrayConfig, true, true, true, false, false, false, + null); + } + } + catch (EventAdapterException ex) + { + throw new ConfigurationException("Error configuring engine: " + ex.Message, ex); + } + + // Add plug-in event representations + var plugInReps = configSnapshot.PlugInEventRepresentation; + foreach (var entry in plugInReps) + { + String className = entry.Value.EventRepresentationTypeName; + Type eventRepClass; + try + { + eventRepClass = TypeHelper.ResolveType(className); + } + catch (TypeLoadException ex) + { + throw new ConfigurationException( + "Failed to load plug-in event representation class '" + className + "'", ex); + } + + Object pluginEventRepObj; + try + { + pluginEventRepObj = Activator.CreateInstance(eventRepClass); + } + catch (TypeInstantiationException ex) + { + throw new ConfigurationException( + "Failed to instantiate plug-in event representation class '" + className + + "' via default constructor", ex); + } + catch (TargetInvocationException ex) + { + throw new ConfigurationException( + "Failed to instantiate plug-in event representation class '" + className + + "' via default constructor", ex); + } + catch (MethodAccessException ex) + { + throw new ConfigurationException( + "Illegal access to instantiate plug-in event representation class '" + className + + "' via default constructor", ex); + } + catch (MemberAccessException ex) + { + throw new ConfigurationException( + "Illegal access to instantiate plug-in event representation class '" + className + + "' via default constructor", ex); + } + + if (!(pluginEventRepObj is PlugInEventRepresentation)) + { + throw new ConfigurationException( + "Plug-in event representation class '" + className + + "' does not implement the required interface " + typeof (PlugInEventRepresentation).Name); + } + + var eventRepURI = entry.Key; + var pluginEventRep = (PlugInEventRepresentation) pluginEventRepObj; + var initializer = entry.Value.Initializer; + var context = new PlugInEventRepresentationContext(eventAdapterService, eventRepURI, initializer); + + try + { + pluginEventRep.Init(context); + eventAdapterService.AddEventRepresentation(eventRepURI, pluginEventRep); + } + catch (Exception e) + { + throw new ConfigurationException( + "Plug-in event representation class '" + className + "' and URI '" + eventRepURI + + "' did not initialize correctly : " + e.Message, e); + } + } + + // Add plug-in event type names + var plugInNames = configSnapshot.PlugInEventTypes; + foreach (var entry in plugInNames) + { + var name = entry.Key; + var config = entry.Value; + eventAdapterService.AddPlugInEventType( + name, config.EventRepresentationResolutionURIs, config.Initializer); + } + } + + private static IDictionary> ToTypesReferences( + IDictionary mapTypeConfigurations) + where T : ConfigurationEventTypeWithSupertype + { + var result = new Dictionary>(); + foreach (var entry in mapTypeConfigurations) + { + result[entry.Key] = entry.Value.SuperTypes; + } + return result; + } + + /// + /// Constructs the auto import service. + /// + /// config INFO + /// factory of aggregation service provider + /// service + internal static EngineImportService MakeEngineImportService( + ConfigurationInformation configSnapshot, + AggregationFactoryFactory aggregationFactoryFactory) + { + TimeUnit timeUnit = configSnapshot.EngineDefaults.TimeSource.TimeUnit; + TimeAbacus timeAbacus; + if (timeUnit == TimeUnit.MILLISECONDS) + { + timeAbacus = TimeAbacusMilliseconds.INSTANCE; + } + else if (timeUnit == TimeUnit.MICROSECONDS) + { + timeAbacus = TimeAbacusMicroseconds.INSTANCE; + } + else + { + throw new ConfigurationException( + "Invalid time-source time unit of " + timeUnit + ", expected millis or micros"); + } + + var expression = configSnapshot.EngineDefaults.Expression; + var engineImportService = new EngineImportServiceImpl( + expression.IsExtendedAggregation, + expression.IsUdfCache, expression.IsDuckTyping, + configSnapshot.EngineDefaults.Language.IsSortUsingCollator, + configSnapshot.EngineDefaults.Expression.MathContext, + configSnapshot.EngineDefaults.Expression.TimeZone, timeAbacus, + configSnapshot.EngineDefaults.Execution.ThreadingProfile, + configSnapshot.TransientConfiguration, + aggregationFactoryFactory); + engineImportService.AddMethodRefs(configSnapshot.MethodInvocationReferences); + + // Add auto-imports + try + { + foreach (var importName in configSnapshot.Imports) + { + engineImportService.AddImport(importName); + } + + foreach (var importName in configSnapshot.AnnotationImports) + { + engineImportService.AddAnnotationImport(importName); + } + + foreach (var config in configSnapshot.PlugInAggregationFunctions) + { + engineImportService.AddAggregation(config.Name, config); + } + + foreach (var config in configSnapshot.PlugInAggregationMultiFunctions) + { + engineImportService.AddAggregationMultiFunction(config); + } + + foreach (var config in configSnapshot.PlugInSingleRowFunctions) + { + engineImportService.AddSingleRow( + config.Name, config.FunctionClassName, config.FunctionMethodName, config.ValueCache, + config.FilterOptimizable, config.IsRethrowExceptions, config.EventTypeName); + } + } + catch (EngineImportException ex) + { + throw new ConfigurationException("Error configuring engine: " + ex.Message, ex); + } + + return engineImportService; + } + + /// + /// Creates the database config service. + /// + /// is the config snapshot + /// is the timer stuff + /// for statement schedule management + /// engine import service + /// database config svc + internal static DatabaseConfigService MakeDatabaseRefService( + ConfigurationInformation configSnapshot, + SchedulingService schedulingService, + SchedulingMgmtService schedulingMgmtService, + EngineImportService engineImportService) + { + DatabaseConfigService databaseConfigService; + + // Add auto-imports + try + { + var allStatementsBucket = schedulingMgmtService.AllocateBucket(); + databaseConfigService = new DatabaseConfigServiceImpl( + configSnapshot.DatabaseReferences, schedulingService, allStatementsBucket, engineImportService); + } + catch (ArgumentException ex) + { + throw new ConfigurationException("Error configuring engine: " + ex.Message, ex); + } + + return databaseConfigService; + } + + private static IDictionary CreatePropertyTypes( + Properties properties, + EngineImportService engineImportService) + { + var propertyTypes = new Dictionary(); + foreach (var entry in properties) + { + var property = entry.Key; + var className = entry.Value; + var clazz = ResolveClassForTypeName(className, engineImportService); + if (clazz != null) + { + propertyTypes.Put(property, clazz); + } + } + return propertyTypes; + } + + private static IDictionary ResolveClassesForStringPropertyTypes( + IDictionary properties, + EngineImportService engineImportService) + { + var propertyTypes = new Dictionary(); + foreach (var entry in properties) + { + var property = entry.Key; + propertyTypes.Put(property, entry.Value); + if (!(entry.Value is string)) + { + continue; + } + var className = (string) entry.Value; + var clazz = ResolveClassForTypeName(className, engineImportService); + if (clazz != null) + { + propertyTypes.Put(property, clazz); + } + } + return propertyTypes; + } + + private static Type ResolveClassForTypeName(string type, EngineImportService engineImportService) + { + var isArray = false; + if (type != null && EventTypeUtility.IsPropertyArray(type)) + { + isArray = true; + type = EventTypeUtility.GetPropertyRemoveArray(type); + } + + if (type == null) + { + throw new ConfigurationException("A null value has been provided for the type"); + } + Type clazz = TypeHelper.GetTypeForSimpleName(type); + if (clazz == null) + { + throw new ConfigurationException("The type '" + type + "' is not a recognized type"); + } + + if (isArray) + { + clazz = Array.CreateInstance(clazz, 0).GetType(); + } + return clazz; + } + + public EPServicesContext CreateServicesContext( + EPServiceProvider epServiceProvider, + ConfigurationInformation configSnapshot) + { + // Directory for binding resources + var resourceDirectory = new SimpleServiceDirectory(); + + // Engine import service + var engineImportService = MakeEngineImportService(configSnapshot, AggregationFactoryFactoryDefault.INSTANCE); + + // Event Type Id Generation + EventTypeIdGenerator eventTypeIdGenerator; + if (configSnapshot.EngineDefaults.AlternativeContext == null || + configSnapshot.EngineDefaults.AlternativeContext.EventTypeIdGeneratorFactory == null) + { + eventTypeIdGenerator = new EventTypeIdGeneratorImpl(); + } + else + { + var eventTypeIdGeneratorFactory = TypeHelper.Instantiate( + configSnapshot.EngineDefaults.AlternativeContext.EventTypeIdGeneratorFactory, + engineImportService.GetClassForNameProvider()); + eventTypeIdGenerator = eventTypeIdGeneratorFactory.Create( + new EventTypeIdGeneratorContext(epServiceProvider.URI)); + } + + // Make services that depend on snapshot config entries + EventAdapterAvroHandler avroHandler = EventAdapterAvroHandlerUnsupported.INSTANCE; + if (configSnapshot.EngineDefaults.EventMeta.AvroSettings.IsEnableAvro) + { + try + { + avroHandler = TypeHelper.Instantiate( + EventAdapterAvroHandlerConstants.HANDLER_IMPL, engineImportService.GetClassForNameProvider()); + } + catch (Exception e) + { + Log.Debug( + "Avro provider {0} not instantiated, not enabling Avro support: {1}", + EventAdapterAvroHandlerConstants.HANDLER_IMPL, e.Message); + } + try + { + avroHandler.Init(configSnapshot.EngineDefaults.EventMeta.AvroSettings, engineImportService); + } + catch (Exception e) + { + throw new ConfigurationException("Failed to initialize Esper-Avro: " + e.Message, e); + } + } + var eventAdapterService = new EventAdapterServiceImpl( + eventTypeIdGenerator, configSnapshot.EngineDefaults.EventMeta.AnonymousCacheSize, avroHandler, + engineImportService); + Init(eventAdapterService, configSnapshot, engineImportService); + + // New read-write lock for concurrent event processing + var eventProcessingRwLock = ReaderWriterLockManager.CreateLock( + MethodBase.GetCurrentMethod().DeclaringType); + + var timeSourceService = MakeTimeSource(configSnapshot); + var schedulingService = SchedulingServiceProvider.NewService(timeSourceService); + var schedulingMgmtService = new SchedulingMgmtServiceImpl(); + var engineSettingsService = new EngineSettingsService( + configSnapshot.EngineDefaults, configSnapshot.PlugInEventTypeResolutionURIs); + var databaseConfigService = MakeDatabaseRefService( + configSnapshot, schedulingService, schedulingMgmtService, engineImportService); + + var plugInViews = new PluggableObjectCollection(); + plugInViews.AddViews( + configSnapshot.PlugInViews, configSnapshot.PlugInVirtualDataWindows, engineImportService); + var plugInPatternObj = new PluggableObjectCollection(); + plugInPatternObj.AddPatternObjects(configSnapshot.PlugInPatternObjects, engineImportService); + + // exception handling + var exceptionHandlingService = InitExceptionHandling( + epServiceProvider.URI, configSnapshot.EngineDefaults.ExceptionHandling, + configSnapshot.EngineDefaults.ConditionHandling, engineImportService); + + // Statement context factory + Type systemVirtualDWViewFactory = null; + if (configSnapshot.EngineDefaults.AlternativeContext.VirtualDataWindowViewFactory != null) + { + try + { + systemVirtualDWViewFactory = + engineImportService.GetClassForNameProvider() + .ClassForName(configSnapshot.EngineDefaults.AlternativeContext.VirtualDataWindowViewFactory); + if (!TypeHelper.IsImplementsInterface(systemVirtualDWViewFactory, typeof (VirtualDataWindowFactory))) + { + throw new ConfigurationException( + "Type " + systemVirtualDWViewFactory.Name + " does not implement the interface " + + typeof (VirtualDataWindowFactory).Name); + } + } + catch (TypeLoadException e) + { + throw new ConfigurationException("Failed to look up class " + systemVirtualDWViewFactory); + } + } + var statementContextFactory = new StatementContextFactoryDefault( + plugInViews, plugInPatternObj, systemVirtualDWViewFactory); + + var msecTimerResolution = configSnapshot.EngineDefaults.Threading.InternalTimerMsecResolution; + if (msecTimerResolution <= 0) + { + throw new ConfigurationException( + "Timer resolution configuration not set to a valid value, expecting a non-zero value"); + } + var timerService = new TimerServiceImpl(epServiceProvider.URI, msecTimerResolution); + + var variableService = new VariableServiceImpl( + configSnapshot.EngineDefaults.Variables.MsecVersionRelease, schedulingService, eventAdapterService, null); + InitVariables(variableService, configSnapshot.Variables, engineImportService); + + var tableService = new TableServiceImpl(); + + var statementLockFactory = new StatementLockFactoryImpl( + configSnapshot.EngineDefaults.Execution.IsFairlock, + configSnapshot.EngineDefaults.Execution.IsDisableLocking); + var streamFactoryService = StreamFactoryServiceProvider.NewService( + epServiceProvider.URI, configSnapshot.EngineDefaults.ViewResources.IsShareViews); + var filterService = + FilterServiceProvider.NewService( + configSnapshot.EngineDefaults.Execution.FilterServiceProfile, + configSnapshot.EngineDefaults.Execution.IsAllowIsolatedService); + var metricsReporting = new MetricReportingServiceImpl( + configSnapshot.EngineDefaults.MetricsReporting, epServiceProvider.URI); + var namedWindowMgmtService = + new NamedWindowMgmtServiceImpl( + configSnapshot.EngineDefaults.Logging.IsEnableQueryPlan, metricsReporting); + var namedWindowDispatchService = new NamedWindowDispatchServiceImpl( + schedulingService, variableService, tableService, + engineSettingsService.EngineSettings.Execution.IsPrioritized, eventProcessingRwLock, + exceptionHandlingService, metricsReporting); + + var valueAddEventService = new ValueAddEventServiceImpl(); + valueAddEventService.Init( + configSnapshot.RevisionEventTypes, configSnapshot.VariantStreams, eventAdapterService, + eventTypeIdGenerator); + + var statementEventTypeRef = new StatementEventTypeRefImpl(); + var statementVariableRef = new StatementVariableRefImpl( + variableService, tableService, namedWindowMgmtService); + + var threadingService = new ThreadingServiceImpl(configSnapshot.EngineDefaults.Threading); + + var internalEventRouterImpl = new InternalEventRouterImpl(epServiceProvider.URI); + + var statementIsolationService = new StatementIsolationServiceImpl(); + + var deploymentStateService = new DeploymentStateServiceImpl(); + + StatementMetadataFactory stmtMetadataFactory; + if (configSnapshot.EngineDefaults.AlternativeContext.StatementMetadataFactory == null) + { + stmtMetadataFactory = new StatementMetadataFactoryDefault(); + } + else + { + stmtMetadataFactory = (StatementMetadataFactory) TypeHelper.Instantiate( + configSnapshot.EngineDefaults.AlternativeContext.StatementMetadataFactory, + engineImportService.GetClassForNameProvider()); + } + + var contextManagementService = new ContextManagementServiceImpl(); + + PatternSubexpressionPoolEngineSvc patternSubexpressionPoolSvc = null; + if (configSnapshot.EngineDefaults.Patterns.MaxSubexpressions != null) + { + patternSubexpressionPoolSvc = + new PatternSubexpressionPoolEngineSvc( + configSnapshot.EngineDefaults.Patterns.MaxSubexpressions.Value, + configSnapshot.EngineDefaults.Patterns.IsMaxSubexpressionPreventStart); + } + + MatchRecognizeStatePoolEngineSvc matchRecognizeStatePoolEngineSvc = null; + if (configSnapshot.EngineDefaults.MatchRecognize.MaxStates != null) + { + matchRecognizeStatePoolEngineSvc = + new MatchRecognizeStatePoolEngineSvc( + configSnapshot.EngineDefaults.MatchRecognize.MaxStates.Value, + configSnapshot.EngineDefaults.MatchRecognize.IsMaxStatesPreventStart); + } + + var scriptingService = new ScriptingServiceImpl(); + scriptingService.DiscoverEngines(); + + // New services context + var services = new EPServicesContext( + epServiceProvider.URI, schedulingService, + eventAdapterService, engineImportService, engineSettingsService, databaseConfigService, plugInViews, + statementLockFactory, eventProcessingRwLock, null, resourceDirectory, statementContextFactory, + plugInPatternObj, timerService, filterService, streamFactoryService, + namedWindowMgmtService, namedWindowDispatchService, variableService, tableService, timeSourceService, + valueAddEventService, metricsReporting, statementEventTypeRef, + statementVariableRef, configSnapshot, threadingService, internalEventRouterImpl, + statementIsolationService, schedulingMgmtService, + deploymentStateService, exceptionHandlingService, + new PatternNodeFactoryImpl(), eventTypeIdGenerator, + stmtMetadataFactory, + contextManagementService, patternSubexpressionPoolSvc, matchRecognizeStatePoolEngineSvc, + new DataFlowServiceImpl( + epServiceProvider, + new DataFlowConfigurationStateServiceImpl()), + new ExprDeclaredServiceImpl(), + new ContextControllerFactoryFactorySvcImpl(), + new ContextManagerFactoryServiceImpl(), + new EPStatementFactoryDefault(), + new RegexHandlerFactoryDefault(), + new ViewableActivatorFactoryDefault(), + new FilterNonPropertyRegisteryServiceImpl(), + new ResultSetProcessorHelperFactoryImpl(), + new ViewServicePreviousFactoryImpl(), + new EventTableIndexServiceImpl(), + new EPRuntimeIsolatedFactoryImpl(), + new FilterBooleanExpressionFactoryImpl(), + new DataCacheFactory(), + new MultiMatchHandlerFactoryImpl(), + NamedWindowConsumerMgmtServiceImpl.INSTANCE, + AggregationFactoryFactoryDefault.INSTANCE, + scriptingService + ); + + // Engine services subset available to statements + statementContextFactory.StmtEngineServices = services; + + // Circular dependency + var statementLifecycleSvc = new StatementLifecycleSvcImpl(epServiceProvider, services); + services.StatementLifecycleSvc = statementLifecycleSvc; + + // Observers to statement events + statementLifecycleSvc.LifecycleEvent += (s, theEvent) => metricsReporting.Observe(theEvent); + + // Circular dependency + statementIsolationService.SetEpServicesContext(services); + + return services; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/service/EPStatementDispatch.cs b/NEsper.Core/NEsper.Core/core/service/EPStatementDispatch.cs new file mode 100755 index 000000000..d659fc293 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/EPStatementDispatch.cs @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.core.service +{ + /// + /// Interface for statement-level dispatch. + /// + /// Relevant when a statements callbacks have completed and the join processing must take place. + /// + public interface EPStatementDispatch + { + /// + /// Execute dispatch. + /// + void Execute(); + } + + public class ProxyEPStatementDispatch : EPStatementDispatch + { + public Action ProcExecute { get; set; } + + /// + /// Execute dispatch. + /// + public void Execute() + { + ProcExecute(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/EPStatementFactory.cs b/NEsper.Core/NEsper.Core/core/service/EPStatementFactory.cs new file mode 100755 index 000000000..f0cd6131b --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/EPStatementFactory.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.core.context.factory; +using com.espertech.esper.dispatch; +using com.espertech.esper.timer; +using com.espertech.esper.util; + +namespace com.espertech.esper.core.service +{ + public interface EPStatementFactory + { + EPStatementSPI Make( + string expressionNoAnnotations, + bool isPattern, + DispatchService dispatchService, + StatementLifecycleSvcImpl statementLifecycleSvc, + long timeLastStateChange, + bool preserveDispatchOrder, + bool isSpinLocks, + long blockingTimeout, + TimeSourceService timeSource, + StatementMetadata statementMetadata, + object statementUserObject, + StatementContext statementContext, + bool isFailed, + bool nameProvided); + + StopCallback MakeStopMethod(StatementAgentInstanceFactoryResult startResult); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/service/EPStatementFactoryDefault.cs b/NEsper.Core/NEsper.Core/core/service/EPStatementFactoryDefault.cs new file mode 100755 index 000000000..a7ea90f38 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/EPStatementFactoryDefault.cs @@ -0,0 +1,46 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.core.context.factory; +using com.espertech.esper.dispatch; +using com.espertech.esper.timer; +using com.espertech.esper.util; + +namespace com.espertech.esper.core.service +{ + public class EPStatementFactoryDefault : EPStatementFactory + { + public EPStatementSPI Make( + string expressionNoAnnotations, + bool isPattern, + DispatchService dispatchService, + StatementLifecycleSvcImpl statementLifecycleSvc, + long timeLastStateChange, + bool preserveDispatchOrder, + bool isSpinLocks, + long blockingTimeout, + TimeSourceService timeSource, + StatementMetadata statementMetadata, + object statementUserObject, + StatementContext statementContext, + bool isFailed, + bool nameProvided) + { + return new EPStatementImpl( + expressionNoAnnotations, isPattern, dispatchService, statementLifecycleSvc, timeLastStateChange, + preserveDispatchOrder, isSpinLocks, blockingTimeout, timeSource, statementMetadata, statementUserObject, + statementContext, isFailed, nameProvided); + } + + public StopCallback MakeStopMethod(StatementAgentInstanceFactoryResult startResult) + { + return startResult.StopCallback; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/service/EPStatementHandle.cs b/NEsper.Core/NEsper.Core/core/service/EPStatementHandle.cs new file mode 100755 index 000000000..efb632c59 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/EPStatementHandle.cs @@ -0,0 +1,147 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.core.service.multimatch; +using com.espertech.esper.epl.metric; +using com.espertech.esper.util; + +namespace com.espertech.esper.core.service +{ + /// + /// Class exists once per statement and hold statement resource lock(s). + /// + /// Use by for determining callback-statement affinity and locking of statement + /// resources. + /// + [Serializable] + public class EPStatementHandle : MetaDefItem + { + private readonly int _hashCode; + + [NonSerialized] private InsertIntoLatchFactory _insertIntoFrontLatchFactory; + [NonSerialized] private InsertIntoLatchFactory _insertIntoBackLatchFactory; + [NonSerialized] private readonly StatementMetricHandle _metricsHandle; + + /// + /// Ctor. + /// + /// is the statement id uniquely indentifying the handle + /// Name of the statement. + /// The statement text. + /// Type of the statement. + /// is the expression + /// indicator whether the statement uses variables + /// handle for metrics reporting + /// priority, zero is default + /// true for drop after done + /// if set to true [has table access]. + /// The multi match handler. + public EPStatementHandle(int statementId, string statementName, string statementText, StatementType statementType, string expressionText, bool hasVariables, StatementMetricHandle metricsHandle, int priority, bool preemptive, bool hasTableAccess, MultiMatchHandler multiMatchHandler) + { + StatementId = statementId; + StatementName = statementName; + EPL = statementText; + StatementType = statementType; + HasVariables = hasVariables; + Priority = priority; + IsPreemptive = preemptive; + _metricsHandle = metricsHandle; + HasTableAccess = hasTableAccess; + MultiMatchHandler = multiMatchHandler; + + unchecked + { + _hashCode = (statementName != null ? statementName.GetHashCode() : 0); + _hashCode = (_hashCode*397) ^ (statementId.GetHashCode()); + _hashCode = (_hashCode*397) ^ (expressionText != null ? expressionText.GetHashCode() : 0); + } + } + + /// Returns the statement id. + /// statement id + public int StatementId { get; private set; } + + /// Returns the factory for latches in insert-into guaranteed order of delivery. + /// latch factory for the statement if it performs insert-into (route) of events + public InsertIntoLatchFactory InsertIntoFrontLatchFactory + { + get { return _insertIntoFrontLatchFactory; } + set { _insertIntoFrontLatchFactory = value; } + } + + public InsertIntoLatchFactory InsertIntoBackLatchFactory + { + get { return _insertIntoBackLatchFactory; } + set { _insertIntoBackLatchFactory = value; } + } + + /// Returns true if the statement uses variables, false if not. + /// indicator if variables are used by statement + public bool HasVariables { get; private set; } + + /// Returns the statement priority. + /// priority, default 0 + public int Priority { get; private set; } + + /// True for preemptive (drop) statements. + /// preemptive indicator + public bool IsPreemptive { get; private set; } + + public bool Equals(EPStatementHandle other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return (other.StatementId == StatementId); + } + + /// + /// Determines whether the specified is equal to the current . + /// + /// + /// true if the specified is equal to the current ; otherwise, false. + /// + /// The to compare with the current . + /// The parameter is null. + /// 2 + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != typeof (EPStatementHandle)) return false; + return Equals((EPStatementHandle) obj); + } + + public override int GetHashCode() + { + return _hashCode; + } + + /// Returns true if the statement potentially self-joins amojng the events it processes. + /// true for self-joins possible, false for not possible (most statements) + public bool IsCanSelfJoin { get; set; } + + /// Returns handle for metrics reporting. + /// handle for metrics reporting + public StatementMetricHandle MetricsHandle + { + get { return _metricsHandle; } + } + + public string StatementName { get; private set; } + + public string EPL { get; private set; } + + public StatementType StatementType { get; private set; } + + public bool HasTableAccess { get; private set; } + + public MultiMatchHandler MultiMatchHandler { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/EPStatementHandleCallback.cs b/NEsper.Core/NEsper.Core/core/service/EPStatementHandleCallback.cs new file mode 100755 index 000000000..8a686fe5c --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/EPStatementHandleCallback.cs @@ -0,0 +1,79 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.context.util; +using com.espertech.esper.filter; +using com.espertech.esper.schedule; + +namespace com.espertech.esper.core.service +{ + /// + /// Statement resource handle and callback for use with + /// and . + /// + /// Links the statement handle identifying a statement and containing the statement resource lock, with + /// the actual callback to invoke for a statement together. + /// + public class EPStatementHandleCallback : FilterHandle, ScheduleHandle + { + private EPStatementAgentInstanceHandle _agentInstanceHandle; + private FilterHandleCallback _filterCallback; + private ScheduleHandleCallback _scheduleCallback; + + /// Ctor. + /// is a statement handle + /// is a filter callback + public EPStatementHandleCallback(EPStatementAgentInstanceHandle agentInstanceHandle, FilterHandleCallback callback) + { + _agentInstanceHandle = agentInstanceHandle; + _filterCallback = callback; + } + + /// Ctor. + /// is a statement handle + /// is a schedule callback + public EPStatementHandleCallback(EPStatementAgentInstanceHandle agentInstanceHandle, ScheduleHandleCallback callback) + { + _agentInstanceHandle = agentInstanceHandle; + _scheduleCallback = callback; + } + + public int StatementId + { + get { return _agentInstanceHandle.StatementId; } + } + + public int AgentInstanceId + { + get { return _agentInstanceHandle.AgentInstanceId; } + } + + /// Returns the statement handle. + /// handle containing a statement resource lock + public EPStatementAgentInstanceHandle AgentInstanceHandle + { + get { return _agentInstanceHandle; } + } + + /// Returns the statement filter callback, or null if this is a schedule callback handle. + /// filter callback + public FilterHandleCallback FilterCallback + { + get { return _filterCallback; } + set { _filterCallback = value; } + } + + /// Returns the statement schedule callback, or null if this is a filter callback handle. + /// schedule callback + public ScheduleHandleCallback ScheduleCallback + { + get { return _scheduleCallback; } + set { _scheduleCallback = value; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/EPStatementImpl.cs b/NEsper.Core/NEsper.Core/core/service/EPStatementImpl.cs new file mode 100755 index 000000000..f39f9e46b --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/EPStatementImpl.cs @@ -0,0 +1,707 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.client.context; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.dispatch; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.timer; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.service +{ + /// + /// Statement implementation for EPL statements. + /// + public class EPStatementImpl : EPStatementSPI + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly EPStatementListenerSet _statementListenerSet; + + private UpdateDispatchViewBase _dispatchChildView; + private StatementLifecycleSvc _statementLifecycleSvc; + + private Viewable _parentView; + + /// + /// Ctor. + /// + /// expression text witout annotations + /// is true to indicate this is a pure pattern expression + /// for dispatching events to listeners to the statement + /// handles lifecycle transitions for the statement + /// the timestamp the statement was created and started + /// is true if the dispatch to listeners should block to preserve event generation order + /// true to use spin locks blocking to deliver results, as locks are usually uncontended + /// is the max number of milliseconds of block time + /// time source provider + /// statement metadata + /// the application define user object associated to each statement, if supplied + /// the statement service context + /// indicator to start in failed state + /// true to indicate a statement name has been provided and is not a system-generated name + public EPStatementImpl( + String expressionNoAnnotations, + bool isPattern, + DispatchService dispatchService, + StatementLifecycleSvc statementLifecycleSvc, + long timeLastStateChange, + bool isBlockingDispatch, + bool isSpinBlockingDispatch, + long msecBlockingTimeout, + TimeSourceService timeSourceService, + StatementMetadata statementMetadata, + Object userObject, + StatementContext statementContext, + bool isFailed, + bool nameProvided) + { + _statementLifecycleSvc = statementLifecycleSvc; + _statementListenerSet = new EPStatementListenerSet(); + + IsPattern = isPattern; + ExpressionNoAnnotations = expressionNoAnnotations; + StatementContext = statementContext; + IsNameProvided = nameProvided; + + if (isBlockingDispatch) + { + if (isSpinBlockingDispatch) + { + _dispatchChildView = new UpdateDispatchViewBlockingSpin( + statementContext.StatementResultService, + dispatchService, msecBlockingTimeout, + timeSourceService); + } + else + { + _dispatchChildView = new UpdateDispatchViewBlockingWait( + statementContext.StatementResultService, + dispatchService, msecBlockingTimeout); + } + } + else + { + _dispatchChildView = new UpdateDispatchViewNonBlocking( + statementContext.StatementResultService, + dispatchService); + } + + State = !isFailed ? EPStatementState.STOPPED : EPStatementState.FAILED; + TimeLastStateChange = timeLastStateChange; + StatementMetadata = statementMetadata; + UserObject = userObject; + statementContext.StatementResultService.SetUpdateListeners(_statementListenerSet, false); + } + + public void SetListeners(EPStatementListenerSet listenerSet, bool isRecovery) + { + _statementListenerSet.Copy(listenerSet); + StatementContext.StatementResultService.SetUpdateListeners(listenerSet, isRecovery); + } + + /// + /// Gets or sets the service provider. + /// + /// The service provider. + public EPServiceProvider ServiceProvider { get; private set; } + + #region EPStatementSPI Members + + /// + /// Occurs whenever new events are available or old events are removed. + /// + public event UpdateEventHandler Events + { + add + { + if (value == null) + { + throw new ArgumentNullException("value", "Event handler cannot be null"); + } + + if (IsDisposed) + { + throw new IllegalStateException("Statement is in disposed state"); + } + + _statementListenerSet.Events.Add(value); + StatementContext.StatementResultService.SetUpdateListeners(_statementListenerSet, false); + if (_statementLifecycleSvc != null) + { + _statementLifecycleSvc.DispatchStatementLifecycleEvent( + new StatementLifecycleEvent(this, + StatementLifecycleEvent.LifecycleEventType.LISTENER_ADD, + value)); + } + } + + remove + { + if (value == null) + { + throw new ArgumentNullException("value", "Event handler cannot be null"); + } + + _statementListenerSet.Events.Remove(value); + StatementContext.StatementResultService.SetUpdateListeners(_statementListenerSet, false); + if (_statementLifecycleSvc != null) + { + _statementLifecycleSvc.DispatchStatementLifecycleEvent( + new StatementLifecycleEvent(this, + StatementLifecycleEvent.LifecycleEventType.LISTENER_REMOVE, + value)); + } + } + } + + public bool IsDisposed + { + get { return State == EPStatementState.DESTROYED; } + } + + public ICollection Annotations + { + get { return StatementContext.Annotations; } + } + + /// + /// Returns the statement id. + /// + /// statement id + public int StatementId + { + get { return StatementContext.StatementId; } + } + + /// + /// Start the statement. + /// + public void Start() + { + if (_statementLifecycleSvc == null) + { + throw new IllegalStateException("Cannot start statement, statement is in destroyed state"); + } + _statementLifecycleSvc.Start(StatementContext.StatementId); + } + + /// + /// Stop the statement. + /// + public void Stop() + { + if (_statementLifecycleSvc == null) + { + throw new IllegalStateException("Cannot stop statement, statement is in destroyed state"); + } + _statementLifecycleSvc.Stop(StatementContext.StatementId); + + // On stop, we give the dispatch view a chance to dispatch readonly results, if any + StatementContext.StatementResultService.DispatchOnStop(); + + _dispatchChildView.Clear(); + } + + /// + /// Gets the statement's current state + /// + /// + public EPStatementState State { get; private set; } + + /// + /// Set statement state. + /// + /// new current state + /// the timestamp the statement changed state + public void SetCurrentState(EPStatementState currentState, + long timeLastStateChange) + { + State = currentState; + TimeLastStateChange = timeLastStateChange; + } + + /// + /// Sets the parent view. + /// + /// is the statement viewable + public Viewable ParentView + { + get { return _parentView; } + set + { + if (value == null) + { + if (_parentView != null) + { + _parentView.RemoveView(_dispatchChildView); + } + _parentView = null; + } + else + { + _parentView = value; + _parentView.AddView(_dispatchChildView); + EventType = _parentView.EventType; + } + } + } + + /// + /// Returns the underlying expression text or XML. + /// + /// + /// expression text + public string Text + { + get { return StatementContext.Expression; } + } + + /// + /// Returns the statement name. + /// + /// + /// statement name + public string Name + { + get { return StatementContext.StatementName; } + } + + public IEnumerator GetEnumerator(ContextPartitionSelector selector) + { + if (StatementContext.ContextDescriptor == null) + { + throw GetUnsupportedNonContextEnumerator(); + } + if (selector == null) + { + throw new ArgumentException("No selector provided"); + } + + // Return null if not started + StatementContext.VariableService.SetLocalVersion(); + if (_parentView == null) + { + return null; + } + return StatementContext.ContextDescriptor.GetEnumerator(StatementContext.StatementId, selector); + } + + public IEnumerator GetSafeEnumerator(ContextPartitionSelector selector) + { + if (StatementContext.ContextDescriptor == null) + { + throw GetUnsupportedNonContextEnumerator(); + } + if (selector == null) + { + throw new ArgumentException("No selector provided"); + } + + // Return null if not started + if (_parentView == null) + { + return null; + } + + StatementContext.VariableService.SetLocalVersion(); + return StatementContext.ContextDescriptor.GetSafeEnumerator( + StatementContext.StatementId, selector); + } + + /// + /// Returns an enumerator that iterates through a collection. + /// + /// + /// An object that can be used to iterate through the collection. + /// + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + /// + /// Returns an enumerator that iterates through the collection. + /// + /// + /// A that can be used to iterate through the collection. + /// + public IEnumerator GetEnumerator() + { + // Return null if not started + StatementContext.VariableService.SetLocalVersion(); + if (_parentView == null) + { + return EnumerationHelper.Empty(); + } + + IEnumerator theEnumerator; + + if (StatementContext.ContextDescriptor != null) + { + theEnumerator = StatementContext.ContextDescriptor.GetEnumerator(StatementContext.StatementId); + } + else + { + theEnumerator = _parentView.GetEnumerator(); + } + + if (StatementContext.EpStatementHandle.HasTableAccess) + { + return GetUnsafeEnumeratorWTableImpl( + theEnumerator, StatementContext.TableExprEvaluatorContext); + } + + return theEnumerator; + } + + private IEnumerator GetUnsafeEnumeratorWTableImpl( + IEnumerator enumerator, + TableExprEvaluatorContext tableExprEvaluatorContext) + { + try + { + while (enumerator.MoveNext()) + { + var value = enumerator.Current; + tableExprEvaluatorContext.ReleaseAcquiredLocks(); + yield return value; + } + } + finally + { + } + } + + /// + /// Returns a concurrency-safe iterator that iterates over events representing statement results (pull API) + /// in the face of concurrent event processing by further threads. + /// + /// In comparison to the regular iterator, the safe iterator guarantees correct results even + /// as events are being processed by other threads. The cost is that the iterator holds + /// one or more locks that must be released. Any locks are acquired at the time this method + /// is called. + /// + /// This method is a blocking method. It may block until statement processing locks are released + /// such that the safe iterator can acquire any required locks. + /// + /// An application MUST explicitly close the safe iterator instance using the close method, to release locks held by the + /// iterator. The call to the close method should be done in a finally block to make sure + /// the iterator gets closed. + /// + /// Multiple safe iterators may be not be used at the same time by different application threads. + /// A single application thread may hold and use multiple safe iterators however this is discouraged. + /// + /// safe iterator; + public IEnumerator GetSafeEnumerator() + { + // Return null if not started + if (_parentView == null) + { + return null; + } + + if (StatementContext.ContextDescriptor != null) + { + StatementContext.VariableService.SetLocalVersion(); + return StatementContext.ContextDescriptor.GetSafeEnumerator(StatementContext.StatementId); + } + + // Set variable version and acquire the lock first + var instanceLockHandler = StatementContext.DefaultAgentInstanceLock.AcquireReadLock(); + + try + { + StatementContext.VariableService.SetLocalVersion(); + // Provide iterator - that iterator MUST be closed else the lock is not released + if (StatementContext.EpStatementHandle.HasTableAccess) + { + return GetSafeEnumeratorWTableImpl( + instanceLockHandler, + _parentView.GetEnumerator(), + StatementContext.TableExprEvaluatorContext); + } + + return GetSafeEnumerator(instanceLockHandler, _parentView.GetEnumerator()); + } + catch + { + // Lock disposal only occurs if the lock has been acquired and the + // subsequent methods fail to return a true enumerator. If the + // enumerator is successfully created, this disposable artifact + // remains intact. + + instanceLockHandler.Dispose(); + throw; + } + } + + private IEnumerator GetSafeEnumeratorWTableImpl( + IDisposable instanceLockHandler, + IEnumerator enumerator, + TableExprEvaluatorContext tableExprEvaluatorContext) + { + try + { + while (enumerator.MoveNext()) + { + yield return enumerator.Current; + } + } + finally + { + instanceLockHandler.Dispose(); + tableExprEvaluatorContext.ReleaseAcquiredLocks(); + } + } + + private IEnumerator GetSafeEnumerator( + IDisposable instanceLockHandler, + IEnumerator enumerator) + { + try + { + while (enumerator.MoveNext()) + { + yield return enumerator.Current; + } + } + finally + { + instanceLockHandler.Dispose(); + } + } + + + public EventType EventType { get; private set; } + + /// Returns the set of listeners to the statement. + /// statement listeners + public void SetListenerSet(EPStatementListenerSet value, bool isRecovery) + { + _statementListenerSet.Copy(value); + StatementContext.StatementResultService.SetUpdateListeners(value, isRecovery); + } + + /// Returns the set of listeners to the statement. + /// statement listeners + public EPStatementListenerSet GetListenerSet() + { + return _statementListenerSet; + } + + public long TimeLastStateChange { get; private set; } + + public bool IsStarted + { + get { return State == EPStatementState.STARTED; } + } + + public bool IsStopped + { + get { return State == EPStatementState.STOPPED; } + } + + public void SetSubscriber(object subscriber, string methodName) + { + _statementListenerSet.Subscriber = new EPSubscriber(subscriber, methodName); + StatementContext.StatementResultService.SetUpdateListeners(_statementListenerSet, false); + } + + public object Subscriber + { + get { return _statementListenerSet.Subscriber; } + set + { + if (value is EPSubscriber) + { + _statementListenerSet.Subscriber = (EPSubscriber)value; + } + else + { + _statementListenerSet.Subscriber = new EPSubscriber(value); + } + + StatementContext.StatementResultService.SetUpdateListeners(_statementListenerSet, false); + } + } + + public bool IsPattern { get; private set; } + + public StatementMetadata StatementMetadata { get; private set; } + + public object UserObject { get; private set; } + + public StatementContext StatementContext { get; internal set; } + + public string ExpressionNoAnnotations { get; private set; } + + public string ServiceIsolated { get; set; } + + public bool IsNameProvided { get; private set; } + + public UpdateDispatchViewBase DispatchChildView + { + get { return _dispatchChildView; } + } + + public void Dispose() + { + if (State == EPStatementState.DESTROYED) + { + throw new IllegalStateException("Statement already destroyed"); + } + _statementLifecycleSvc.Dispose(StatementContext.StatementId); + _parentView = null; + EventType = null; + _dispatchChildView = null; + _statementLifecycleSvc = null; + } + + /// Remove all event handlers from a statement. + public void RemoveAllEventHandlers() + { + _statementListenerSet.RemoveAllEventHandlers(); + StatementContext.StatementResultService.SetUpdateListeners(_statementListenerSet, false); + if (_statementLifecycleSvc != null) + { + _statementLifecycleSvc.DispatchStatementLifecycleEvent( + new StatementLifecycleEvent(this, StatementLifecycleEvent.LifecycleEventType.LISTENER_REMOVE_ALL)); + } + } + + /// + /// Add an event handler to the current statement and replays current statement + /// results to the handler. + /// + /// The handler receives current statement results as the first call to the Update + /// method of the event handler, passing in the newEvents parameter the current statement + /// results as an array of zero or more events. Subsequent calls to the Update + /// method of the event handler are statement results. + /// + /// Current statement results are the events returned by the GetEnumerator or + /// GetSafeEnumerator methods. + /// + /// Delivery of current statement results in the first call is performed by the + /// same thread invoking this method, while subsequent calls to the event handler may + /// deliver statement results by the same or other threads. + /// + /// Note: this is a blocking call, delivery is atomic: Events occurring during + /// iteration and delivery to the event handler are guaranteed to be delivered in a separate + /// call and not lost. The event handler implementation should minimize long-running or + /// blocking operations. + /// + /// Delivery is only atomic relative to the current statement. If the same event handler + /// instance is registered with other statements it may receive other statement + /// result s simultaneously. + /// + /// If a statement is not started an therefore does not have current results, the + /// event handler receives a single invocation with a null listenerSet in newEvents. + /// + /// eventHandler that will receive events + public void AddEventHandlerWithReplay(UpdateEventHandler eventHandler) + { + if (eventHandler == null) + { + throw new ArgumentNullException("eventHandler", "Null listener reference supplied"); + } + + if (IsDisposed) + { + throw new IllegalStateException("Statement is in destroyed state"); + } + + using (StatementContext.DefaultAgentInstanceLock.AcquireReadLock()) + { + _statementListenerSet.Events.Add(eventHandler); + StatementContext.StatementResultService.SetUpdateListeners(_statementListenerSet, false); + if (_statementLifecycleSvc != null) + { + _statementLifecycleSvc.DispatchStatementLifecycleEvent( + new StatementLifecycleEvent(this, + StatementLifecycleEvent.LifecycleEventType.LISTENER_ADD, + eventHandler)); + } + + IEnumerator enumerator = GetEnumerator(); + var events = new List(); + while (enumerator.MoveNext()) + { + events.Add(enumerator.Current); + } + + try + { + if (events.IsEmpty()) + { + eventHandler.Invoke( + this, + new UpdateEventArgs( + ServiceProvider, + this, + null, + null)); + } + else + { + eventHandler.Invoke( + this, + new UpdateEventArgs( + ServiceProvider, + this, + events.ToArray(), + null)); + } + } + catch (Exception exception) + { + Log.Error( + "Unexpected exception invoking eventHandler for replay on event handler '{0}' : {1} : {2}", + eventHandler.GetType().Name, + exception.GetType().Name, + exception.Message); + } + finally + { + if (StatementContext.EpStatementHandle.HasTableAccess) + { + StatementContext.TableExprEvaluatorContext.ReleaseAcquiredLocks(); + } + } + } + } + + #endregion + + /// + /// Clears the event handlers. Should be used with caution since this clears + /// anyone who has registered a handler. + /// + public void ClearEventHandlers() + { + _statementListenerSet.Events.Clear(); + _statementLifecycleSvc.DispatchStatementLifecycleEvent( + new StatementLifecycleEvent(this, StatementLifecycleEvent.LifecycleEventType.LISTENER_REMOVE_ALL)); + } + + private static UnsupportedOperationException GetUnsupportedNonContextEnumerator() + { + return new UnsupportedOperationException("Enumerator with context selector is only supported for statements under context"); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/service/EPStatementListenerSet.cs b/NEsper.Core/NEsper.Core/core/service/EPStatementListenerSet.cs new file mode 100755 index 000000000..bfacb74f9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/EPStatementListenerSet.cs @@ -0,0 +1,63 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.core.service +{ + /// + /// Provides Update listeners for use by statement instances, and the management methods around these. + /// + /// The collection of Update listeners is based on copy-on-write: + /// When the engine dispatches events to a set of listeners, then while iterating through the set there + /// may be listeners added or removed (the listener may remove itself). + /// + /// + /// Additionally, events may be dispatched by multiple threads to the same listener. + /// + /// + public class EPStatementListenerSet + { + public CopyOnWriteArraySet Events; + + /// Ctor. + public EPStatementListenerSet() + { + Events = new CopyOnWriteArraySet(); + } + + public bool HasEventConsumers + { + get + { + return ((Events != null) && (Events.Count != 0)); + } + } + + /// Copy the update listener set to from another. + /// a collection of Update listeners + public void Copy(EPStatementListenerSet listenerSet) + { + Events = listenerSet.Events; + } + + public void RemoveAllEventHandlers() + { + Events.Clear(); + } + + /// + /// Gets or sets the subscriber instance. + /// + public EPSubscriber Subscriber { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/EPStatementObjectModelHelper.cs b/NEsper.Core/NEsper.Core/core/service/EPStatementObjectModelHelper.cs new file mode 100755 index 000000000..45e48857a --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/EPStatementObjectModelHelper.cs @@ -0,0 +1,155 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.core.service +{ + /// + /// Helper methods for use by the statement object model. + /// + public static class EPStatementObjectModelHelper + { + public static string RenderValue(this object constant) + { + if (constant == null) + { + return("null"); + } + + if ((constant is String) || + (constant is Char)) + { + return '\"' + constant.ToString() + '\"'; + } + else if (constant is Int64) + { + return string.Format("{0}L", constant); + } + else if (constant is Double) + { + double dvalue = (double)constant; + double scrubbed = Math.Floor(dvalue); + if (dvalue == scrubbed) + { + return string.Format("{0:F1}d", dvalue); + } + else + { + return string.Format("{0}", dvalue); + } + } + else if (constant is Single) + { + double dvalue = (float)constant; + double scrubbed = Math.Floor(dvalue); + if (dvalue == scrubbed) + { + return string.Format("{0:F1}f", dvalue); + } + else + { + return string.Format("{0}f", dvalue); + } + } + else if (constant is Decimal) + { + decimal dvalue = (decimal)constant; + decimal scrubbed = Math.Floor(dvalue); + if (dvalue == scrubbed) + { + return string.Format("{0:F1}m", dvalue); + } + else + { + return string.Format("{0}m", dvalue); + } + } + else if (constant is Boolean) + { + return(constant.ToString().ToLower()); + } + else + { + return(constant.ToString()); + } + } + + /// Renders a constant as an EPL. + /// to output to + /// to render + public static void RenderEPL(TextWriter writer, Object constant) + { + if (constant == null) + { + writer.Write("null"); + return; + } + + if ((constant is String) || + (constant is Char)) + { + writer.Write('\"'); + writer.Write(constant.ToString()); + writer.Write('\"'); + } + else if (constant is Int64) + { + writer.Write("{0}L", constant); + } + else if (constant is Double) + { + double dvalue = (double)constant; + double scrubbed = Math.Floor(dvalue); + if (dvalue == scrubbed) + { + writer.Write("{0:F1}d", dvalue); + } + else + { + writer.Write("{0}", dvalue); + } + } + else if (constant is Single) + { + double dvalue = (float)constant; + double scrubbed = Math.Floor(dvalue); + if (dvalue == scrubbed) + { + writer.Write("{0:F1}f", dvalue); + } + else + { + writer.Write("{0}f", dvalue); + } + } + else if (constant is Decimal) + { + decimal dvalue = (decimal)constant; + decimal scrubbed = Math.Floor(dvalue); + if (dvalue == scrubbed) + { + writer.Write("{0:F1}m", dvalue); + } + else + { + writer.Write("{0}m", dvalue); + } + } + else if (constant is Boolean) + { + writer.Write(constant.ToString().ToLower()); + } + else + { + writer.Write(constant.ToString()); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/EPStatementSPI.cs b/NEsper.Core/NEsper.Core/core/service/EPStatementSPI.cs new file mode 100755 index 000000000..c7429f90a --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/EPStatementSPI.cs @@ -0,0 +1,60 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.service +{ + /// + /// Statement SPI for statements operations for state transitions and internal management. + /// + public interface EPStatementSPI : EPStatement + { + /// Returns the statement id. + /// statement id + int StatementId { get; } + + /// Returns the statements expression without annotations. + /// expression + string ExpressionNoAnnotations { get; } + + /// + /// Returns the current set of listeners for read-only operations. + /// + /// listener set + /// if set to true [is recovery]. + void SetListenerSet(EPStatementListenerSet value, bool isRecovery); + + /// + /// Returns the current set of listeners for read-only operations. + /// + EPStatementListenerSet GetListenerSet(); + + /// Set statement state. + /// new current state + /// the timestamp the statement changed state + void SetCurrentState(EPStatementState currentState, long timeLastStateChange); + + /// Gets or sets the parent view. + /// the statement viewable + Viewable ParentView { get; set; } + + /// Returns additional metadata about a statement. + /// statement metadata + StatementMetadata StatementMetadata { get; } + + /// Returns the statement context. + /// statement context + StatementContext StatementContext { get; } + + /// True if an explicit statement name has been provided, false if the statement name is system-generated. + /// indicator if statement name exists + bool IsNameProvided { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/EngineLevelExtensionServicesContext.cs b/NEsper.Core/NEsper.Core/core/service/EngineLevelExtensionServicesContext.cs new file mode 100755 index 000000000..b54449ab9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/EngineLevelExtensionServicesContext.cs @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.core.service +{ + /// + /// Marker interface for extension services that provide additional engine or statement-level extensions, + /// such as views backed by a write-behind store. + /// + public interface EngineLevelExtensionServicesContext : IDisposable + { + /// + /// Invoked to initialize extension services after engine services initialization. + /// + void Init(EPServicesContext engine, EPRuntimeSPI runtimeSPI, EPAdministratorSPI adminSPI); + + bool IsHAEnabled { get; } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/service/ExceptionHandlingService.cs b/NEsper.Core/NEsper.Core/core/service/ExceptionHandlingService.cs new file mode 100755 index 000000000..e538a473a --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/ExceptionHandlingService.cs @@ -0,0 +1,109 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.client; +using com.espertech.esper.client.hook; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.util; + +namespace com.espertech.esper.core.service +{ + public class ExceptionHandlingService + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly String _engineURI; + + public event ExceptionHandler UnhandledException; + public event ConditionHandler UnhandledCondition; + + public ExceptionHandlingService( + string engineURI, + IEnumerable exceptionHandlers, + IEnumerable conditionHandlers) + { + _engineURI = engineURI; + foreach (var handler in exceptionHandlers) UnhandledException += handler; + foreach (var handler in conditionHandlers) UnhandledCondition += handler; + } + + public void HandleCondition(BaseCondition condition, EPStatementHandle handle) + { + if (UnhandledCondition == null) + { + Log.Info("Condition encountered processing statement '{0}' statement text '{1}' : {2}", handle.StatementName, handle.EPL, condition); + return; + } + + UnhandledCondition( + new ConditionHandlerContext( + _engineURI, handle.StatementName, handle.EPL, condition)); + } + + public void HandleException( + Exception ex, + EPStatementAgentInstanceHandle handle, + ExceptionHandlerExceptionType type, + EventBean optionalCurrentEvent) + { + HandleException(ex, handle.StatementHandle.StatementName, handle.StatementHandle.EPL, type, optionalCurrentEvent); + } + + public void HandleException( + Exception ex, + String statementName, + String epl, + ExceptionHandlerExceptionType type, + EventBean optionalCurrentEvent) + { + if (UnhandledException == null) + { + var writer = new StringWriter(); + if (type == ExceptionHandlerExceptionType.PROCESS) + { + writer.Write("Exception encountered processing "); + } + else + { + writer.Write("Exception encountered performing instance stop for "); + } + writer.Write("statement '"); + writer.Write(statementName); + writer.Write("' expression '"); + writer.Write(epl); + writer.Write("' : "); + writer.Write(ex.Message); + + var message = writer.ToString(); + + if (type == ExceptionHandlerExceptionType.PROCESS) + { + Log.Error(message, ex); + } + else + { + Log.Warn(message, ex); + } + + return; + } + + UnhandledException( + new ExceptionHandlerContext(_engineURI, ex, statementName, epl, type, optionalCurrentEvent)); + } + + public string EngineURI + { + get { return _engineURI; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/ExprEvaluatorContextStatement.cs b/NEsper.Core/NEsper.Core/core/service/ExprEvaluatorContextStatement.cs new file mode 100755 index 000000000..63eb0ddff --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/ExprEvaluatorContextStatement.cs @@ -0,0 +1,105 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat.threading; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.script; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.schedule; + +namespace com.espertech.esper.core.service +{ + /// + /// Represents a statement-level-only context for expression evaluation, not allowing for agents instances and result cache. + /// + public class ExprEvaluatorContextStatement : ExprEvaluatorContext + { + private readonly StatementContext _statementContext; + private readonly bool _allowTableAccess; + private EventBean _contextProperties; + + public ExprEvaluatorContextStatement(StatementContext statementContext, bool allowTableAccess) + { + _statementContext = statementContext; + _allowTableAccess = allowTableAccess; + } + + /// Returns the time provider. + /// time provider + public TimeProvider TimeProvider + { + get { return _statementContext.TimeProvider; } + } + + public ExpressionResultCacheService ExpressionResultCacheService + { + get { return _statementContext.ExpressionResultCacheServiceSharable; } + } + + public int AgentInstanceId + { + get { return -1; } + } + + public EventBean ContextProperties + { + get { return _contextProperties; } + set { _contextProperties = value; } + } + + public AgentInstanceScriptContext AllocateAgentInstanceScriptContext + { + get { return _statementContext.AllocateAgentInstanceScriptContext; } + } + + public String StatementName + { + get { return _statementContext.StatementName; } + } + + public String EngineURI + { + get { return _statementContext.EngineURI; } + } + + public int StatementId + { + get { return _statementContext.StatementId; } + } + + public StatementType? StatementType + { + get { return _statementContext.StatementType; } + } + + public IReaderWriterLock AgentInstanceLock + { + get { return _statementContext.DefaultAgentInstanceLock; } + } + + public TableExprEvaluatorContext TableExprEvaluatorContext + { + get + { + if (!_allowTableAccess) + { + throw new EPException("Access to tables is not allowed"); + } + return _statementContext.TableExprEvaluatorContext; + } + } + + public object StatementUserObject + { + get { return _statementContext.StatementUserObject; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/ExprEvaluatorContextWTableAccess.cs b/NEsper.Core/NEsper.Core/core/service/ExprEvaluatorContextWTableAccess.cs new file mode 100755 index 000000000..ef746f518 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/ExprEvaluatorContextWTableAccess.cs @@ -0,0 +1,89 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.compat.threading; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.script; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.schedule; + +namespace com.espertech.esper.core.service +{ + public class ExprEvaluatorContextWTableAccess : ExprEvaluatorContext + { + private readonly ExprEvaluatorContext _context; + private readonly TableService _tableService; + + public ExprEvaluatorContextWTableAccess(ExprEvaluatorContext context, TableService tableService) + { + _context = context; + _tableService = tableService; + } + + public string StatementName + { + get { return _context.StatementName; } + } + + public string EngineURI + { + get { return _context.EngineURI; } + } + + public int StatementId + { + get { return _context.StatementId; } + } + + public StatementType? StatementType + { + get { return _context.StatementType; } + } + + public TimeProvider TimeProvider + { + get { return _context.TimeProvider; } + } + + public ExpressionResultCacheService ExpressionResultCacheService + { + get { return _context.ExpressionResultCacheService; } + } + + public int AgentInstanceId + { + get { return _context.AgentInstanceId; } + } + + public EventBean ContextProperties + { + get { return _context.ContextProperties; } + } + + public AgentInstanceScriptContext AllocateAgentInstanceScriptContext + { + get { return _context.AllocateAgentInstanceScriptContext; } + } + + public IReaderWriterLock AgentInstanceLock + { + get { return _context.AgentInstanceLock; } + } + + public TableExprEvaluatorContext TableExprEvaluatorContext + { + get { return _tableService.TableExprEvaluatorContext; } + } + + public object StatementUserObject + { + get { return _context.StatementUserObject; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/ExpressionResultCacheEntry.cs b/NEsper.Core/NEsper.Core/core/service/ExpressionResultCacheEntry.cs new file mode 100755 index 000000000..889ff3f40 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/ExpressionResultCacheEntry.cs @@ -0,0 +1,22 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.core.service +{ + public class ExpressionResultCacheEntry + { + public TReference Reference { get; set; } + public TResult Result { get; set; } + + public ExpressionResultCacheEntry(TReference reference, TResult result) + { + Reference = reference; + Result = result; + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/service/ExpressionResultCacheForDeclaredExprLastColl.cs b/NEsper.Core/NEsper.Core/core/service/ExpressionResultCacheForDeclaredExprLastColl.cs new file mode 100755 index 000000000..fa3e5f1f9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/ExpressionResultCacheForDeclaredExprLastColl.cs @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; + +namespace com.espertech.esper.core.service +{ + /// + /// On the level of expression declaration: + /// a) for non-enum evaluation and for enum-evaluation a separate cache + /// b) The cache is keyed by the prototype-node and verified by a events-per-stream (EventBean[]) that is maintained or rewritten. + /// NOTE: ExpressionResultCacheEntry should not be held onto since the instance returned can be reused. + /// + public interface ExpressionResultCacheForDeclaredExprLastColl + { + ExpressionResultCacheEntry> GetDeclaredExpressionLastColl(object node, EventBean[] eventsPerStream); + void SaveDeclaredExpressionLastColl(object node, EventBean[] eventsPerStream, ICollection result); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/service/ExpressionResultCacheForDeclaredExprLastCollImpl.cs b/NEsper.Core/NEsper.Core/core/service/ExpressionResultCacheForDeclaredExprLastCollImpl.cs new file mode 100755 index 000000000..8aa4074eb --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/ExpressionResultCacheForDeclaredExprLastCollImpl.cs @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.events; + +namespace com.espertech.esper.core.service +{ + using IdentityCache = IdentityDictionary>>>; + + public class ExpressionResultCacheForDeclaredExprLastCollImpl : ExpressionResultCacheForDeclaredExprLastColl + { + private readonly IdentityCache _exprDeclCacheCollection = new IdentityCache(); + + public ExpressionResultCacheEntry> GetDeclaredExpressionLastColl(object node, EventBean[] eventsPerStream) + { + var cacheRef = _exprDeclCacheCollection.Get(node); + if (cacheRef == null) + { + return null; + } + + var entry = cacheRef.Target; + if (entry == null) + { + return null; + } + return EventBeanUtility.CompareEventReferences(entry.Reference, eventsPerStream) ? entry : null; + } + + public void SaveDeclaredExpressionLastColl(object node, EventBean[] eventsPerStream, ICollection result) + { + var copy = eventsPerStream.MaterializeArray(); + var entry = new ExpressionResultCacheEntry>(copy, result); + _exprDeclCacheCollection.Put( + node, new SoftReference>>(entry)); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/service/ExpressionResultCacheForDeclaredExprLastValue.cs b/NEsper.Core/NEsper.Core/core/service/ExpressionResultCacheForDeclaredExprLastValue.cs new file mode 100755 index 000000000..bd1cff18a --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/ExpressionResultCacheForDeclaredExprLastValue.cs @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; + +namespace com.espertech.esper.core.service +{ + /// + /// On the level of expression declaration: + /// a) for non-enum evaluation and for enum-evaluation a separate cache + /// b) The cache is keyed by the prototype-node and verified by a events-per-stream (EventBean[]) that is maintained or rewritten. + /// NOTE: ExpressionResultCacheEntry should not be held onto since the instance returned can be reused. + /// + public interface ExpressionResultCacheForDeclaredExprLastValue + { + bool CacheEnabled(); + ExpressionResultCacheEntry GetDeclaredExpressionLastValue(object node, EventBean[] eventsPerStream); + void SaveDeclaredExpressionLastValue(object node, EventBean[] eventsPerStream, object result); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/service/ExpressionResultCacheForDeclaredExprLastValueMulti.cs b/NEsper.Core/NEsper.Core/core/service/ExpressionResultCacheForDeclaredExprLastValueMulti.cs new file mode 100755 index 000000000..2992048c2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/ExpressionResultCacheForDeclaredExprLastValueMulti.cs @@ -0,0 +1,91 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.events; + +namespace com.espertech.esper.core.service +{ + using IdentityCache = IdentityDictionary>>; + + public class ExpressionResultCacheForDeclaredExprLastValueMulti : ExpressionResultCacheForDeclaredExprLastValue + { + private readonly int _cacheSize; + + private readonly ExpressionResultCacheEntry _resultCacheEntry = + new ExpressionResultCacheEntry(null, null); + + private readonly IdentityCache _cache = new IdentityCache(); + + public ExpressionResultCacheForDeclaredExprLastValueMulti(int cacheSize) + { + _cacheSize = cacheSize; + } + + public bool CacheEnabled() + { + return true; + } + + public ExpressionResultCacheEntry GetDeclaredExpressionLastValue(object node, EventBean[] eventsPerStream) + { + var cacheRef = _cache.Get(node); + if (cacheRef == null) + { + return null; + } + + var entry = cacheRef.Get(); + if (entry == null) + { + return null; + } + for (var i = 0; i < entry.BufferA.Length; i++) + { + var key = entry.BufferA[i]; + if (key != null && EventBeanUtility.CompareEventReferences(key, eventsPerStream)) + { + _resultCacheEntry.Reference = key; + _resultCacheEntry.Result = entry.BufferB[i]; + return _resultCacheEntry; + } + } + return null; + } + + public void SaveDeclaredExpressionLastValue(object node, EventBean[] eventsPerStream, object result) + { + var cacheRef = _cache.Get(node); + + RollingTwoValueBuffer buf; + if (cacheRef == null) + { + buf = new RollingTwoValueBuffer(new EventBean[_cacheSize][], new object[_cacheSize]); + _cache.Put(node, new SoftReference>(buf)); + } + else + { + buf = cacheRef.Get(); + if (buf == null) + { + buf = new RollingTwoValueBuffer(new EventBean[_cacheSize][], new object[_cacheSize]); + _cache.Put(node, new SoftReference>(buf)); + } + } + + var copy = new EventBean[eventsPerStream.Length]; + Array.Copy(eventsPerStream, 0, copy, 0, copy.Length); + buf.Add(copy, result); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/service/ExpressionResultCacheForDeclaredExprLastValueNone.cs b/NEsper.Core/NEsper.Core/core/service/ExpressionResultCacheForDeclaredExprLastValueNone.cs new file mode 100755 index 000000000..8224edd95 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/ExpressionResultCacheForDeclaredExprLastValueNone.cs @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; + +namespace com.espertech.esper.core.service +{ + public class ExpressionResultCacheForDeclaredExprLastValueNone : ExpressionResultCacheForDeclaredExprLastValue + { + public bool CacheEnabled() + { + return false; + } + + public ExpressionResultCacheEntry GetDeclaredExpressionLastValue(object node, EventBean[] eventsPerStream) + { + return null; + } + + public void SaveDeclaredExpressionLastValue(object node, EventBean[] eventsPerStream, object result) + { + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/service/ExpressionResultCacheForDeclaredExprLastValueSingle.cs b/NEsper.Core/NEsper.Core/core/service/ExpressionResultCacheForDeclaredExprLastValueSingle.cs new file mode 100755 index 000000000..746fc6a25 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/ExpressionResultCacheForDeclaredExprLastValueSingle.cs @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.events; + +namespace com.espertech.esper.core.service +{ + using IdentityCache = IdentityDictionary>>; + + public class ExpressionResultCacheForDeclaredExprLastValueSingle : ExpressionResultCacheForDeclaredExprLastValue + { + private readonly IdentityCache _exprDeclCacheObject = new IdentityCache(); + + public bool CacheEnabled() + { + return true; + } + + public ExpressionResultCacheEntry GetDeclaredExpressionLastValue(object node, EventBean[] eventsPerStream) + { + var cacheRef = this._exprDeclCacheObject.Get( + node); + if (cacheRef == null) + { + return null; + } + var entry = cacheRef.Get(); + if (entry == null) + { + return null; + } + return EventBeanUtility.CompareEventReferences(entry.Reference, eventsPerStream) ? entry : null; + } + + public void SaveDeclaredExpressionLastValue(object node, EventBean[] eventsPerStream, object result) + { + EventBean[] copy = EventBeanUtility.CopyArray(eventsPerStream); + var entry = new ExpressionResultCacheEntry(copy, result); + _exprDeclCacheObject.Put(node, new SoftReference>(entry)); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/service/ExpressionResultCacheForEnumerationMethod.cs b/NEsper.Core/NEsper.Core/core/service/ExpressionResultCacheForEnumerationMethod.cs new file mode 100755 index 000000000..466a3756f --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/ExpressionResultCacheForEnumerationMethod.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.core.service +{ + /// + /// On the level of enumeration method: + /// If a enumeration method expression is invoked within another enumeration method expression (not counting expression declarations), + /// for example "source.where(a => source.minBy(b => b.x))" the "source.minBy(b => b.x)" is not dependent on any other lambda so the result gets cached. + /// The cache is keyed by the enumeration-method-node as an IdentityHashMap and verified by a context stack (Long[]) that is built in nested evaluation calls. + /// + /// NOTE: ExpressionResultCacheEntry should not be held onto since the instance returned can be reused. + /// + public interface ExpressionResultCacheForEnumerationMethod + { + void PushStack(ExpressionResultCacheStackEntry lambda); + bool PopLambda(); + Deque GetStack(); + + ExpressionResultCacheEntry GetEnumerationMethodLastValue(object node); + void SaveEnumerationMethodLastValue(object node, object result); + + void PushContext(long contextNumber); + void PopContext(); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/service/ExpressionResultCacheForEnumerationMethodImpl.cs b/NEsper.Core/NEsper.Core/core/service/ExpressionResultCacheForEnumerationMethodImpl.cs new file mode 100755 index 000000000..8a6cc68b3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/ExpressionResultCacheForEnumerationMethodImpl.cs @@ -0,0 +1,97 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Linq; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.core.service +{ + using IdentityCache = IdentityDictionary>>; + + public class ExpressionResultCacheForEnumerationMethodImpl : ExpressionResultCacheForEnumerationMethod + { + private readonly IdentityCache enumMethodCache = new IdentityCache(); + + private Deque callStack; + private Deque lastValueCacheStack; + + public void PushStack(ExpressionResultCacheStackEntry lambda) + { + if (callStack == null) + { + callStack = new ArrayDeque(); + lastValueCacheStack = new ArrayDeque(10); + } + callStack.AddLast(lambda); + } + + public bool PopLambda() + { + callStack.RemoveLast(); + return callStack.IsEmpty(); + } + + public Deque GetStack() + { + return callStack; + } + + public ExpressionResultCacheEntry GetEnumerationMethodLastValue(object node) + { + var cacheRef = enumMethodCache.Get(node); + if (cacheRef == null) + { + return null; + } + var entry = cacheRef.Get(); + if (entry == null) + { + return null; + } + var required = entry.Reference; + if (required.Length != lastValueCacheStack.Count) + { + return null; + } + var prov = lastValueCacheStack.GetEnumerator(); + for (int i = 0; i < lastValueCacheStack.Count; i++) + { + prov.MoveNext(); + if (!required[i].Equals(prov.Current)) + { + return null; + } + } + return entry; + } + + public void SaveEnumerationMethodLastValue(object node, object result) + { + long?[] snapshot = lastValueCacheStack.ToArray(); + var entry = new ExpressionResultCacheEntry(snapshot, result); + enumMethodCache.Put(node, new SoftReference>(entry)); + } + + public void PushContext(long contextNumber) + { + if (callStack == null) + { + callStack = new ArrayDeque(); + lastValueCacheStack = new ArrayDeque(10); + } + lastValueCacheStack.AddLast(contextNumber); + } + + public void PopContext() + { + lastValueCacheStack.RemoveLast(); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/service/ExpressionResultCacheForPropUnwrap.cs b/NEsper.Core/NEsper.Core/core/service/ExpressionResultCacheForPropUnwrap.cs new file mode 100755 index 000000000..89aaec985 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/ExpressionResultCacheForPropUnwrap.cs @@ -0,0 +1,24 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; + +namespace com.espertech.esper.core.service +{ + /// + /// On the level of indexed event properties: Properties that are contained in EventBean instances, such as for Enumeration Methods, get wrapped only once for the same event. + /// NOTE: ExpressionResultCacheEntry should not be held onto since the instance returned can be reused. + /// + public interface ExpressionResultCacheForPropUnwrap + { + ExpressionResultCacheEntry> GetPropertyColl(string propertyNameFullyQualified, EventBean reference); + void SavePropertyColl(string propertyNameFullyQualified, EventBean reference, ICollection events); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/service/ExpressionResultCacheForPropUnwrapImpl.cs b/NEsper.Core/NEsper.Core/core/service/ExpressionResultCacheForPropUnwrapImpl.cs new file mode 100755 index 000000000..204e34d57 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/ExpressionResultCacheForPropUnwrapImpl.cs @@ -0,0 +1,56 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.core.service +{ + using Cache = Dictionary>>>; + + public class ExpressionResultCacheForPropUnwrapImpl : ExpressionResultCacheForPropUnwrap + { + private readonly Cache _collPropertyCache = new Cache(); + + public ExpressionResultCacheEntry> GetPropertyColl( + string propertyNameFullyQualified, + EventBean reference) + { + var cacheRef = _collPropertyCache.Get(propertyNameFullyQualified); + if (cacheRef == null) + { + return null; + } + var entry = cacheRef.Get(); + if (entry == null) + { + return null; + } + if (entry.Reference != reference) + { + return null; + } + return entry; + } + + public void SavePropertyColl( + string propertyNameFullyQualified, + EventBean reference, + ICollection events) + { + var entry = new ExpressionResultCacheEntry>(reference, events); + _collPropertyCache.Put( + propertyNameFullyQualified, + new SoftReference>>(entry)); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/service/ExpressionResultCacheService.cs b/NEsper.Core/NEsper.Core/core/service/ExpressionResultCacheService.cs new file mode 100755 index 000000000..6a9af00ae --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/ExpressionResultCacheService.cs @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.compat.threading; + +namespace com.espertech.esper.core.service +{ + public class ExpressionResultCacheService + { + private readonly int _declareExprCacheSize; + private readonly IThreadLocal _threadCache; + + public ExpressionResultCacheService(int declareExprCacheSize) + { + _declareExprCacheSize = declareExprCacheSize; + _threadCache = ThreadLocalManager.Create( + () => new ExpressionResultCacheServiceHolder(declareExprCacheSize)); + } + + public ExpressionResultCacheForPropUnwrap AllocateUnwrapProp + { + get { return _threadCache.GetOrCreate().GetAllocateUnwrapProp(); } + } + + public ExpressionResultCacheForDeclaredExprLastValue AllocateDeclaredExprLastValue + { + get { return _threadCache.GetOrCreate().GetAllocateDeclaredExprLastValue(); } + } + + public ExpressionResultCacheForDeclaredExprLastColl AllocateDeclaredExprLastColl + { + get { return _threadCache.GetOrCreate().GetAllocateDeclaredExprLastColl(); } + } + + public ExpressionResultCacheForEnumerationMethod AllocateEnumerationMethod + { + get { return _threadCache.GetOrCreate().GetAllocateEnumerationMethod(); } + } + + public bool IsDeclaredExprCacheEnabled + { + get { return _declareExprCacheSize > 0; } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/service/ExpressionResultCacheServiceHolder.cs b/NEsper.Core/NEsper.Core/core/service/ExpressionResultCacheServiceHolder.cs new file mode 100755 index 000000000..d36e789ab --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/ExpressionResultCacheServiceHolder.cs @@ -0,0 +1,60 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.core.service +{ + public class ExpressionResultCacheServiceHolder + { + private readonly int _declareExprCacheSize; + + private ExpressionResultCacheForPropUnwrap _propUnwrap; + private ExpressionResultCacheForDeclaredExprLastValue _declaredExprLastValue; + private ExpressionResultCacheForDeclaredExprLastColl _declaredExprLastColl; + private ExpressionResultCacheForEnumerationMethod _enumerationMethod; + + public ExpressionResultCacheServiceHolder(int declareExprCacheSize) + { + this._declareExprCacheSize = declareExprCacheSize; + } + + public ExpressionResultCacheForPropUnwrap GetAllocateUnwrapProp() + { + return _propUnwrap ?? (_propUnwrap = new ExpressionResultCacheForPropUnwrapImpl()); + } + + public ExpressionResultCacheForDeclaredExprLastValue GetAllocateDeclaredExprLastValue() + { + if (_declaredExprLastValue == null) + { + if (_declareExprCacheSize < 1) + { + _declaredExprLastValue = new ExpressionResultCacheForDeclaredExprLastValueNone(); + } + else if (_declareExprCacheSize < 2) + { + _declaredExprLastValue = new ExpressionResultCacheForDeclaredExprLastValueSingle(); + } + else + { + _declaredExprLastValue = new ExpressionResultCacheForDeclaredExprLastValueMulti(_declareExprCacheSize); + } + } + return _declaredExprLastValue; + } + + public ExpressionResultCacheForDeclaredExprLastColl GetAllocateDeclaredExprLastColl() + { + return _declaredExprLastColl ?? (_declaredExprLastColl = new ExpressionResultCacheForDeclaredExprLastCollImpl()); + } + + public ExpressionResultCacheForEnumerationMethod GetAllocateEnumerationMethod() + { + return _enumerationMethod ?? (_enumerationMethod = new ExpressionResultCacheForEnumerationMethodImpl()); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/service/ExpressionResultCacheStackEntry.cs b/NEsper.Core/NEsper.Core/core/service/ExpressionResultCacheStackEntry.cs new file mode 100755 index 000000000..b892462f2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/ExpressionResultCacheStackEntry.cs @@ -0,0 +1,13 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.core.service +{ + public interface ExpressionResultCacheStackEntry { + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/InsertIntoLatchFactory.cs b/NEsper.Core/NEsper.Core/core/service/InsertIntoLatchFactory.cs new file mode 100755 index 000000000..bb73e2841 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/InsertIntoLatchFactory.cs @@ -0,0 +1,102 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.timer; + +namespace com.espertech.esper.core.service +{ + /// + /// Class to hold a current latch per statement that uses an insert-into stream (per statement and insert-into stream relationship). + /// + public class InsertIntoLatchFactory + { + private readonly String _name; + private readonly bool _stateless; + private readonly bool _useSpin; + private readonly TimeSourceService _timeSourceService; + private readonly long _msecWait; + + private InsertIntoLatchSpin _currentLatchSpin; + private InsertIntoLatchWait _currentLatchWait; + + /// + /// Ctor. + /// + /// the factory name + /// if set to true [stateless]. + /// the number of milliseconds latches will await maximually + /// the blocking strategy to employ + /// time source provider + public InsertIntoLatchFactory( + String name, + bool stateless, + long msecWait, + ConfigurationEngineDefaults.ThreadingConfig.Locking locking, + TimeSourceService timeSourceService) + { + _name = name; + _msecWait = msecWait; + _timeSourceService = timeSourceService; + _stateless = stateless; + + _useSpin = (locking == ConfigurationEngineDefaults.ThreadingConfig.Locking.SPIN); + + // construct a completed latch as an initial root latch + if (_useSpin) + { + _currentLatchSpin = new InsertIntoLatchSpin(this); + } + else + { + _currentLatchWait = new InsertIntoLatchWait(this); + } + } + + /// Returns a new latch. + /// + /// Need not be synchronized as there is one per statement and execution is during statement lock. + /// + /// + /// is the object returned by the await. + /// latch + public Object NewLatch(EventBean payload) + { + if (_stateless) + { + return payload; + } + + if (_useSpin) + { + var nextLatch = new InsertIntoLatchSpin(this, _currentLatchSpin, _msecWait, payload); + _currentLatchSpin = nextLatch; + return nextLatch; + } + else + { + var nextLatch = new InsertIntoLatchWait(_currentLatchWait, _msecWait, payload); + _currentLatchWait.SetLater(nextLatch); + _currentLatchWait = nextLatch; + return nextLatch; + } + } + + public TimeSourceService TimeSourceService + { + get { return _timeSourceService; } + } + + public string Name + { + get { return _name; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/InsertIntoLatchSpin.cs b/NEsper.Core/NEsper.Core/core/service/InsertIntoLatchSpin.cs new file mode 100755 index 000000000..bf171ba33 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/InsertIntoLatchSpin.cs @@ -0,0 +1,93 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Threading; + +using com.espertech.esper.client; +using com.espertech.esper.compat.logging; + +namespace com.espertech.esper.core.service +{ + /// + /// A spin-locking implementation of a latch for use in guaranteeing delivery between a single event produced by a single statement and consumable by another statement. + /// + public class InsertIntoLatchSpin + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + // The earlier latch is the latch generated before this latch + private readonly InsertIntoLatchFactory _factory; + private InsertIntoLatchSpin _earlier; + private readonly long _msecTimeout; + private readonly EventBean _payload; + + private bool _isCompleted; + + /// + /// Ctor. + /// + /// The factory. + /// the latch before this latch that this latch should be waiting for + /// the timeout after which delivery occurs + /// the payload is an event to deliver + public InsertIntoLatchSpin(InsertIntoLatchFactory factory, InsertIntoLatchSpin earlier, long msecTimeout, EventBean payload) + { + _factory = factory; + _earlier = earlier; + _msecTimeout = msecTimeout; + _payload = payload; + } + + /// Ctor - use for the first and unused latch to indicate completion. + public InsertIntoLatchSpin(InsertIntoLatchFactory factory) + { + _factory = factory; + _isCompleted = true; + _earlier = null; + _msecTimeout = 0; + } + + /// Returns true if the dispatch completed for this future. + /// true for completed, false if not + public bool IsCompleted() + { + return _isCompleted; + } + + /// Blocking call that returns only when the earlier latch completed. + /// payload of the latch + public EventBean Await() + { + if (!_earlier._isCompleted) + { + long spinStartTime = _factory.TimeSourceService.GetTimeMillis(); + + while (!_earlier._isCompleted) + { + Thread.Yield(); + + long spinDelta = _factory.TimeSourceService.GetTimeMillis() - spinStartTime; + if (spinDelta > _msecTimeout) + { + Log.Info("Spin wait timeout exceeded in insert-into dispatch at " + _msecTimeout + "ms for " + _factory.Name + ", consider disabling insert-into between-statement latching for better performance"); + break; + } + } + } + + return _payload; + } + + /// Called to indicate that the latch completed and a later latch can start. + public void Done() + { + _isCompleted = true; + _earlier = null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/InsertIntoLatchWait.cs b/NEsper.Core/NEsper.Core/core/service/InsertIntoLatchWait.cs new file mode 100755 index 000000000..a3134253d --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/InsertIntoLatchWait.cs @@ -0,0 +1,122 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Reflection; +using System.Threading; +using com.espertech.esper.client; +using com.espertech.esper.compat.logging; + +namespace com.espertech.esper.core.service +{ + /// + /// A suspend-and-notify implementation of a latch for use in guaranteeing delivery + /// between a single event produced by a single statement and consumable by another + /// statement. + /// + public class InsertIntoLatchWait + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + // The earlier latch is the latch generated before this latch + private InsertIntoLatchWait _earlier; + private readonly int _msecTimeout; + private readonly EventBean _payload; + + // The later latch is the latch generated after this latch + private InsertIntoLatchWait _later; + private volatile bool _isCompleted; + + /// + /// Ctor. + /// + /// the latch before this latch that this latch should be waiting for + /// the timeout after which delivery occurs + /// the payload is an event to deliver + public InsertIntoLatchWait(InsertIntoLatchWait earlier, long msecTimeout, EventBean payload) + { + _earlier = earlier; + _msecTimeout = (int)msecTimeout; + _payload = payload; + } + + /// + /// Ctor - use for the first and unused latch to indicate completion. + /// + /// The factory. + public InsertIntoLatchWait(InsertIntoLatchFactory factory) + { + _isCompleted = true; + _earlier = null; + _msecTimeout = 0; + } + + /// + /// Returns true if the dispatch completed for this future. + /// + /// + /// true for completed, false if not + /// + public bool IsCompleted() + { + return _isCompleted; + } + + /// + /// Hand a later latch to use for indicating completion via notify. + /// + /// is the later latch + public void SetLater(InsertIntoLatchWait later) + { + _later = later; + } + + /// + /// Blocking call that returns only when the earlier latch completed. + /// + /// + /// payload of the latch + /// + public EventBean Await() + { + if (!_earlier._isCompleted) + { + lock (this) + { + if (!_earlier._isCompleted) + { + Monitor.Wait(this, _msecTimeout); + } + } + } + + if (!_earlier._isCompleted) + { + Log.Info("Wait timeout exceeded for insert-into dispatch with notify"); + } + + return _payload; + } + + /// + /// Called to indicate that the latch completed and a later latch can start. + /// + public void Done() + { + _isCompleted = true; + if (_later != null) + { + lock (_later) + { + Monitor.Pulse(_later); + } + } + _earlier = null; + _later = null; + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/service/InsertIntoListener.cs b/NEsper.Core/NEsper.Core/core/service/InsertIntoListener.cs new file mode 100755 index 000000000..802cd7e1d --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/InsertIntoListener.cs @@ -0,0 +1,16 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; + +namespace com.espertech.esper.core.service +{ + public interface InsertIntoListener { + bool Inserted(EventBean theEvent, EPStatementHandle statementHandle); + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/InternalEventRouteDest.cs b/NEsper.Core/NEsper.Core/core/service/InternalEventRouteDest.cs new file mode 100755 index 000000000..c586788e3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/InternalEventRouteDest.cs @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; + +namespace com.espertech.esper.core.service +{ + /// + /// Interface for a service that routes events within the engine for further processing. + /// + public interface InternalEventRouteDest + { + /// + /// Route the event such that the event is processed as required. + /// + /// to route + /// provides statement resources + /// if set to true [add to front]. + void Route(EventBean theEvent, EPStatementHandle statementHandle, bool addToFront); + + InternalEventRouter InternalEventRouter { set; } + + void ProcessThreadWorkQueue(); + + void Dispatch(); + + string EngineURI { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/InternalEventRouter.cs b/NEsper.Core/NEsper.Core/core/service/InternalEventRouter.cs new file mode 100755 index 000000000..d2c88a3fd --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/InternalEventRouter.cs @@ -0,0 +1,47 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat.threading; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.core.service +{ + /// Interface for a service that routes events within the engine for further processing. + public interface InternalEventRouter + { + InternalEventRouterDesc GetValidatePreprocessing(EventType eventType, UpdateDesc desc, Attribute[] annotations); + + void AddPreprocessing(InternalEventRouterDesc internalEventRouterDesc, InternalRoutePreprocessView outputView, IReaderWriterLock agentInstanceLock, Boolean hasSubselect); + + /// Remove preprocessing. + /// type to remove for + /// Update statement specification + void RemovePreprocessing(EventType eventType, UpdateDesc desc); + + /// + /// Route the event such that the event is processed as required. + /// + /// to route + /// provides statement resources + /// routing destination + /// context for expression evalauation + /// if set to true [add to front]. + void Route(EventBean theEvent, EPStatementHandle statementHandle, InternalEventRouteDest routeDest, ExprEvaluatorContext exprEvaluatorContext, bool addToFront); + + bool HasPreprocessing { get; } + + EventBean Preprocess(EventBean theEvent, ExprEvaluatorContext engineFilterAndDispatchTimeContext); + + InsertIntoListener InsertIntoListener { set; } + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/InternalEventRouterDesc.cs b/NEsper.Core/NEsper.Core/core/service/InternalEventRouterDesc.cs new file mode 100755 index 000000000..f9a499d4a --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/InternalEventRouterDesc.cs @@ -0,0 +1,43 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.spec; +using com.espertech.esper.events; +using com.espertech.esper.util; + +namespace com.espertech.esper.core.service +{ + public class InternalEventRouterDesc + { + public InternalEventRouterDesc(UpdateDesc updateDesc, + EventBeanCopyMethod copyMethod, + TypeWidener[] wideners, + EventType eventType, + Attribute[] annotations) + { + UpdateDesc = updateDesc; + CopyMethod = copyMethod; + Wideners = wideners; + EventType = eventType; + Annotations = annotations; + } + + public UpdateDesc UpdateDesc { get; private set; } + + public EventBeanCopyMethod CopyMethod { get; private set; } + + public TypeWidener[] Wideners { get; private set; } + + public EventType EventType { get; private set; } + + public Attribute[] Annotations { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/service/InternalEventRouterEntry.cs b/NEsper.Core/NEsper.Core/core/service/InternalEventRouterEntry.cs new file mode 100755 index 000000000..0edb3f531 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/InternalEventRouterEntry.cs @@ -0,0 +1,87 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.compat.threading; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events; +using com.espertech.esper.util; + +namespace com.espertech.esper.core.service +{ + /// + /// Pre-Processing entry for routing an event internally. + /// + public class InternalEventRouterEntry + { + /// + /// Ctor. + /// + /// priority of statement + /// whether to drop the event if matched + /// where clause, or null if none provided + /// event property assignments + /// writes values to an event + /// for widening types to write + /// for indicating output + /// The agent instance lock. + /// if set to true [has subselect]. + public InternalEventRouterEntry(int priority, + bool drop, + ExprNode optionalWhereClause, + ExprNode[] assignments, + EventBeanWriter writer, + TypeWidener[] wideners, + InternalRoutePreprocessView outputView, + IReaderWriterLock agentInstanceLock, + bool hasSubselect) + { + Priority = priority; + IsDrop = drop; + OptionalWhereClause = optionalWhereClause == null ? null : optionalWhereClause.ExprEvaluator; + Assignments = ExprNodeUtility.GetEvaluators(assignments); + Writer = writer; + Wideners = wideners; + OutputView = outputView; + AgentInstanceLock = agentInstanceLock; + HasSubselect = hasSubselect; + } + + /// Returns the execution priority. + /// priority + public int Priority { get; private set; } + + /// Returns indicator whether dropping events if the where-clause matches. + /// drop events + public bool IsDrop { get; private set; } + + /// Returns the where-clause or null if none defined + /// where-clause + public ExprEvaluator OptionalWhereClause { get; private set; } + + /// Returns the expressions providing values for assignment. + /// assignment expressions + public ExprEvaluator[] Assignments { get; private set; } + + /// Returns the writer to the event for writing property values. + /// writer + public EventBeanWriter Writer { get; private set; } + + /// Returns the type wideners to use or null if none required. + /// wideners. + public TypeWidener[] Wideners { get; private set; } + + /// Returns the output view. + /// output view + public InternalRoutePreprocessView OutputView { get; private set; } + + public IReaderWriterLock AgentInstanceLock { get; private set; } + + public bool HasSubselect { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/service/InternalEventRouterImpl.cs b/NEsper.Core/NEsper.Core/core/service/InternalEventRouterImpl.cs new file mode 100755 index 000000000..5c83de38e --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/InternalEventRouterImpl.cs @@ -0,0 +1,343 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.client.annotation; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.compat.threading; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; +using com.espertech.esper.events; +using com.espertech.esper.util; + +namespace com.espertech.esper.core.service +{ + /// + /// Routing implementation that allows to pre-process events. + /// + public class InternalEventRouterImpl : InternalEventRouter + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly string _engineURI; + private readonly IDictionary> _preprocessors; + private readonly IDictionary _descriptors; + private bool _hasPreprocessing; + private InsertIntoListener _insertIntoListener; + + /// Ctor. + public InternalEventRouterImpl(String engineURI) + { + _engineURI = engineURI; + _hasPreprocessing = false; + _preprocessors = new ConcurrentDictionary>(); + _descriptors = new Dictionary(); + } + + /// Return true to indicate that there is pre-processing to take place. + /// preprocessing indicator + public bool HasPreprocessing + { + get { return _hasPreprocessing; } + } + + /// Pre-process the event. + /// to preprocess + /// expression evaluation context + /// preprocessed event + public EventBean Preprocess(EventBean theEvent, ExprEvaluatorContext exprEvaluatorContext) + { + return GetPreprocessedEvent(theEvent, exprEvaluatorContext); + } + + public InsertIntoListener InsertIntoListener + { + set { _insertIntoListener = value; } + } + + public void Route( + EventBean theEvent, + EPStatementHandle statementHandle, + InternalEventRouteDest routeDest, + ExprEvaluatorContext exprEvaluatorContext, + bool addToFront) + { + if (!_hasPreprocessing) + { + if (_insertIntoListener != null) + { + bool route = _insertIntoListener.Inserted(theEvent, statementHandle); + if (route) + { + routeDest.Route(theEvent, statementHandle, addToFront); + } + } + else + { + routeDest.Route(theEvent, statementHandle, addToFront); + } + return; + } + + EventBean preprocessed = GetPreprocessedEvent(theEvent, exprEvaluatorContext); + if (preprocessed != null) + { + if (_insertIntoListener != null) + { + bool route = _insertIntoListener.Inserted(theEvent, statementHandle); + if (route) + { + routeDest.Route(preprocessed, statementHandle, addToFront); + } + } + else + { + routeDest.Route(preprocessed, statementHandle, addToFront); + } + } + } + + public InternalEventRouterDesc GetValidatePreprocessing(EventType eventType, UpdateDesc desc, Attribute[] annotations) + { + if (Log.IsDebugEnabled) + { + Log.Debug("Validating route preprocessing for type '" + eventType.Name + "'"); + } + + if (!(eventType is EventTypeSPI)) + { + throw new ExprValidationException("Update statements require the event type to implement the " + typeof(EventTypeSPI) + " interface"); + } + + var eventTypeSPI = (EventTypeSPI)eventType; + var wideners = new TypeWidener[desc.Assignments.Count]; + var properties = new List(); + for (int i = 0; i < desc.Assignments.Count; i++) + { + var xxx = desc.Assignments[i]; + var assignmentPair = ExprNodeUtility.CheckGetAssignmentToProp(xxx.Expression); + if (assignmentPair == null) + { + throw new ExprValidationException("Missing property assignment expression in assignment number " + i); + } + + var writableProperty = eventTypeSPI.GetWritableProperty(assignmentPair.First); + if (writableProperty == null) + { + throw new ExprValidationException("Property '" + assignmentPair.First + "' is not available for write access"); + } + + wideners[i] = TypeWidenerFactory.GetCheckPropertyAssignType( + ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(assignmentPair.Second), + assignmentPair.Second.ExprEvaluator.ReturnType, + writableProperty.PropertyType, assignmentPair.First, + false, null, null, _engineURI); + properties.Add(assignmentPair.First); + } + + // check copy-able + var copyMethod = eventTypeSPI.GetCopyMethod(properties.ToArray()); + if (copyMethod == null) + { + throw new ExprValidationException("The update-clause requires the underlying event representation to support copy (via Serializable by default)"); + } + + return new InternalEventRouterDesc(desc, copyMethod, wideners, eventType, annotations); + } + + public void AddPreprocessing(InternalEventRouterDesc internalEventRouterDesc, InternalRoutePreprocessView outputView, IReaderWriterLock agentInstanceLock, bool hasSubselect) + { + lock (this) + { + _descriptors.Put(internalEventRouterDesc.UpdateDesc, new IRDescEntry(internalEventRouterDesc, outputView, agentInstanceLock, hasSubselect)); + + // remove all preprocessors for this type as well as any known child types, forcing re-init on next use + RemovePreprocessors(internalEventRouterDesc.EventType); + + _hasPreprocessing = true; + } + } + + public void RemovePreprocessing(EventType eventType, UpdateDesc desc) + { + lock (this) + { + if (Log.IsInfoEnabled) + { + Log.Info("Removing route preprocessing for type '" + eventType.Name); + } + + // remove all preprocessors for this type as well as any known child types + RemovePreprocessors(eventType); + + _descriptors.Remove(desc); + if (_descriptors.IsEmpty()) + { + _hasPreprocessing = false; + _preprocessors.Clear(); + } + } + } + + private EventBean GetPreprocessedEvent(EventBean theEvent, ExprEvaluatorContext exprEvaluatorContext) + { + NullableObject processor = _preprocessors.Get(theEvent.EventType); + if (processor == null) + { + lock (this) + { + processor = Initialize(theEvent.EventType); + _preprocessors.Put(theEvent.EventType, processor); + } + } + + if (processor.Value == null) + { + return theEvent; + } + else + { + return processor.Value.Process(theEvent, exprEvaluatorContext); + } + } + + private void RemovePreprocessors(EventType eventType) + { + _preprocessors.Remove(eventType); + + // find each child type entry + foreach (EventType type in _preprocessors.Keys) + { + if (type.DeepSuperTypes != null) + { + // TODO: can be minimized + foreach (var dtype in type.DeepSuperTypes.Where(dtype => dtype == eventType)) + { + _preprocessors.Remove(type); + } + } + } + } + + private NullableObject Initialize(EventType eventType) + { + var eventTypeSPI = (EventTypeSPI)eventType; + var desc = new List(); + + // determine which ones to process for this types, and what priority and drop + var eventPropertiesWritten = new HashSet(); + foreach (var entry in _descriptors) + { + bool applicable = entry.Value.EventType == eventType; + if (!applicable) + { + if (eventType.DeepSuperTypes != null) + { + if (eventType.DeepSuperTypes.Where(dtype => dtype == entry.Value.EventType).Any()) + { + applicable = true; + } + } + } + + if (!applicable) + { + continue; + } + + var priority = 0; + var isDrop = false; + var annotations = entry.Value.Annotations; + for (int i = 0; i < annotations.Length; i++) + { + if (annotations[i] is PriorityAttribute) + { + priority = ((PriorityAttribute)annotations[i]).Value; + } + if (annotations[i] is DropAttribute) + { + isDrop = true; + } + } + + var properties = new List(); + var expressions = new ExprNode[entry.Key.Assignments.Count]; + for (int i = 0; i < entry.Key.Assignments.Count; i++) + { + var assignment = entry.Key.Assignments[i]; + var assignmentPair = ExprNodeUtility.CheckGetAssignmentToProp(assignment.Expression); + expressions[i] = assignmentPair.Second; + properties.Add(assignmentPair.First); + eventPropertiesWritten.Add(assignmentPair.First); + } + + var writer = eventTypeSPI.GetWriter(properties.ToArray()); + desc.Add(new InternalEventRouterEntry(priority, isDrop, entry.Key.OptionalWhereClause, expressions, writer, entry.Value.Wideners, entry.Value.OutputView, entry.Value.AgentInstanceLock, entry.Value.HasSubselect)); + } + + var copyMethod = eventTypeSPI.GetCopyMethod(eventPropertiesWritten.ToArray()); + if (copyMethod == null) + { + return new NullableObject(null); + } + return new NullableObject(new InternalEventRouterPreprocessor(copyMethod, desc)); + } + + private class IRDescEntry + { + private readonly InternalEventRouterDesc _internalEventRouterDesc; + private readonly InternalRoutePreprocessView _outputView; + private readonly IReaderWriterLock _agentInstanceLock; + private readonly bool _hasSubselect; + + internal IRDescEntry(InternalEventRouterDesc internalEventRouterDesc, InternalRoutePreprocessView outputView, IReaderWriterLock agentInstanceLock, bool hasSubselect) + { + _internalEventRouterDesc = internalEventRouterDesc; + _outputView = outputView; + _agentInstanceLock = agentInstanceLock; + _hasSubselect = hasSubselect; + } + + public EventType EventType + { + get { return _internalEventRouterDesc.EventType; } + } + + public Attribute[] Annotations + { + get { return _internalEventRouterDesc.Annotations; } + } + + public TypeWidener[] Wideners + { + get { return _internalEventRouterDesc.Wideners; } + } + + public InternalRoutePreprocessView OutputView + { + get { return _outputView; } + } + + public IReaderWriterLock AgentInstanceLock + { + get { return _agentInstanceLock; } + } + + public bool HasSubselect + { + get { return _hasSubselect; } + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/InternalEventRouterPreprocessor.cs b/NEsper.Core/NEsper.Core/core/service/InternalEventRouterPreprocessor.cs new file mode 100755 index 000000000..67d24f707 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/InternalEventRouterPreprocessor.cs @@ -0,0 +1,225 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.compat.logging; +using com.espertech.esper.collection; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.core.service +{ + /// + /// Interface for a service that routes events within the engine for further + /// processing. + /// + public class InternalEventRouterPreprocessor + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly EventBeanCopyMethod _copyMethod; + private readonly bool _empty; + private readonly InternalEventRouterEntry[] _entries; + + /// + /// Ctor. + /// + /// for copying the events to preprocess + /// descriptors for pre-processing to apply + public InternalEventRouterPreprocessor(EventBeanCopyMethod copyMethod, IEnumerable entries) + { + var tempIndex = 0; + var tempList = entries + .Select(tempEntry => new Pair(tempIndex++, tempEntry)) + .ToList(); + + tempList.Sort(DoCompare); + + _copyMethod = copyMethod; + _entries = tempList.Select(pair => pair.Second).ToArray(); + _empty = _entries.Length == 0; + } + + private int DoCompare(Pair op1, + Pair op2) + { + var o1 = op1.Second; + var o2 = op2.Second; + + if (o1.Priority > o2.Priority) + { + return 1; + } + + if (o1.Priority < o2.Priority) + { + return -1; + } + + if (o1.IsDrop) + { + return -1; + } + + if (o2.IsDrop) + { + return -1; + } + + return op1.First.CompareTo(op2.First); + } + + /// + /// Pre-proces the event. + /// + /// to pre-process + /// expression evaluation context + /// + /// processed event + /// + public EventBean Process(EventBean theEvent, ExprEvaluatorContext exprEvaluatorContext) + { + if (_empty) + { + return theEvent; + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QUpdateIStream(_entries); } + + EventBean oldEvent = theEvent; + bool haveCloned = false; + var eventsPerStream = new EventBean[1]; + eventsPerStream[0] = theEvent; + InternalEventRouterEntry lastEntry = null; + + for (int i = 0; i < _entries.Length; i++) + { + InternalEventRouterEntry entry = _entries[i]; + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QUpdateIStreamApply(i, entry); } + + ExprEvaluator whereClause = entry.OptionalWhereClause; + if (whereClause != null) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QUpdateIStreamApplyWhere(); } + + var result = whereClause.Evaluate(new EvaluateParams(eventsPerStream, true, exprEvaluatorContext)); + if ((result == null) || (false.Equals(result))) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AUpdateIStreamApplyWhere((bool?)result); } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AUpdateIStreamApply(null, false); } + + continue; + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AUpdateIStreamApplyWhere(true); } + } + + if (entry.IsDrop) + { + return null; + } + + // before applying the changes, indicate to last-entries output view + if (lastEntry != null) + { + InternalRoutePreprocessView view = lastEntry.OutputView; + if (view.IsIndicate) + { + EventBean copied = _copyMethod.Copy(theEvent); + view.Indicate(copied, oldEvent); + oldEvent = copied; + } + else + { + if (_entries[i].OutputView.IsIndicate) + { + oldEvent = _copyMethod.Copy(theEvent); + } + } + } + + // copy event for the first Update that applies + if (!haveCloned) + { + EventBean copiedEvent = _copyMethod.Copy(theEvent); + if (copiedEvent == null) + { + Log.Warn("Event of type " + theEvent.EventType.Name + " could not be copied"); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AUpdateIStreamApply(null, false); } + return null; + } + haveCloned = true; + eventsPerStream[0] = copiedEvent; + theEvent = copiedEvent; + } + + Apply(theEvent, eventsPerStream, entry, exprEvaluatorContext); + lastEntry = entry; + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AUpdateIStreamApply(theEvent, true); } + } + + if (lastEntry != null) + { + InternalRoutePreprocessView view = lastEntry.OutputView; + if (view.IsIndicate) + { + view.Indicate(theEvent, oldEvent); + } + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AUpdateIStream(theEvent, haveCloned); } + return theEvent; + } + + private void Apply(EventBean theEvent, EventBean[] eventsPerStream, InternalEventRouterEntry entry, ExprEvaluatorContext exprEvaluatorContext) + { + // evaluate + Object[] values; + if (entry.HasSubselect) + { + using (entry.AgentInstanceLock.AcquireWriteLock()) + { + values = ObtainValues(eventsPerStream, entry, exprEvaluatorContext); + } + } + else + { + values = ObtainValues(eventsPerStream, entry, exprEvaluatorContext); + } + + // apply + entry.Writer.Write(values, theEvent); + } + + private Object[] ObtainValues(EventBean[] eventsPerStream, InternalEventRouterEntry entry, ExprEvaluatorContext exprEvaluatorContext) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QUpdateIStreamApplyAssignments(entry); } + + Object[] values = new Object[entry.Assignments.Length]; + for (int i = 0; i < entry.Assignments.Length; i++) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QUpdateIStreamApplyAssignmentItem(i); } + Object value = entry.Assignments[i].Evaluate(new EvaluateParams(eventsPerStream, true, exprEvaluatorContext)); + if ((value != null) && (entry.Wideners[i] != null)) + { + value = entry.Wideners[i].Invoke(value); + } + values[i] = value; + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AUpdateIStreamApplyAssignmentItem(value); } + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AUpdateIStreamApplyAssignments(values); } + return values; + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/InternalRoutePreprocessView.cs b/NEsper.Core/NEsper.Core/core/service/InternalRoutePreprocessView.cs new file mode 100755 index 000000000..291974c12 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/InternalRoutePreprocessView.cs @@ -0,0 +1,95 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.compat.logging; +using com.espertech.esper.events; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.service +{ + /// + /// View for use with pre-processing statement such as "Update istream" for indicating previous and current event. + /// + public class InternalRoutePreprocessView : ViewSupport + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private readonly EventType _eventType; + private readonly StatementResultService _statementResultService; + + /// Ctor. + /// the type of event to indicator + /// determines whether listeners or subscribers are attached. + public InternalRoutePreprocessView(EventType eventType, StatementResultService statementResultService) + { + _eventType = eventType; + _statementResultService = statementResultService; + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled)) + { + Log.Debug(".Update Received Update, " + + " newData.Length==" + ((newData == null) ? 0 : newData.Length) + + " oldData.Length==" + ((oldData == null) ? 0 : oldData.Length)); + } + } + + public override EventType EventType + { + get { return _eventType; } + } + + public override IEnumerator GetEnumerator() + { + return CollectionUtil.NULL_EVENT_ITERATOR; + } + + /// Returns true if a subscriber or listener is attached. + /// indicator + public bool IsIndicate + { + get { return (_statementResultService.IsMakeNatural || _statementResultService.IsMakeSynthetic); } + } + + /// Indicate an modifed event and its previous version. + /// modified event + /// previous version event + public void Indicate(EventBean newEvent, EventBean oldEvent) + { + try + { + if (_statementResultService.IsMakeNatural) + { + var natural = new NaturalEventBean(_eventType, new Object[] { newEvent.Underlying }, newEvent); + var naturalOld = new NaturalEventBean(_eventType, new Object[] { oldEvent.Underlying }, oldEvent); + UpdateChildren(new NaturalEventBean[] { natural }, new NaturalEventBean[] { naturalOld }); + } + else + { + UpdateChildren(new EventBean[] { newEvent }, new EventBean[] { oldEvent }); + } + } + catch (Exception ex) + { + Log.Error("Unexpected error updating child view: " + ex.Message); + } + } + + public StatementResultService StatementResultService + { + get { return _statementResultService; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/NamedWindowSelectedProps.cs b/NEsper.Core/NEsper.Core/core/service/NamedWindowSelectedProps.cs new file mode 100755 index 000000000..073b1f4cd --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/NamedWindowSelectedProps.cs @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.core.service +{ + /// Selected properties for a create-window expression in the model-after syntax. + public class NamedWindowSelectedProps + { + /// Ctor. + /// expression result type + /// name of column + /// null if not a fragment, or event type of fragment if one was selected + public NamedWindowSelectedProps(Type selectExpressionType, String assignedName, EventType fragmentType) + { + SelectExpressionType = selectExpressionType; + AssignedName = assignedName; + FragmentType = fragmentType; + } + + /// Returns the type of the expression result. + /// type + public Type SelectExpressionType { get; private set; } + + /// Returns the assigned column name. + /// name + public string AssignedName { get; private set; } + + /// Returns the fragment type or null if not a fragment type. + /// type + public EventType FragmentType { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/PatternListenerDispatch.cs b/NEsper.Core/NEsper.Core/core/service/PatternListenerDispatch.cs new file mode 100755 index 000000000..b333c2da7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/PatternListenerDispatch.cs @@ -0,0 +1,132 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.logging; +using com.espertech.esper.dispatch; + +namespace com.espertech.esper.core.service +{ + /// Dispatchable for dispatching events to update eventHandlers. + public class PatternListenerDispatch : Dispatchable + { + private static readonly ILog Log = + LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly EPStatement _statement; + private readonly EPServiceProvider _serviceProvider; + private readonly ICollection _eventHandlers; + + private EventBean _singleEvent; + private List _eventList; + + /// + /// Constructor. + /// + /// The service provider. + /// The statement. + /// is the eventHandlers to dispatch to. + public PatternListenerDispatch( + EPServiceProvider serviceProvider, + EPStatement statement, + ICollection eventHandlers) + { + _serviceProvider = serviceProvider; + _statement = statement; + _eventHandlers = eventHandlers; + } + + /// + /// Add an event to be dispatched. + /// + /// to add + public virtual void Add(EventBean theEvent) + { + if (_singleEvent == null) + { + _singleEvent = theEvent; + } + else + { + if (_eventList == null) + { + _eventList = new List(5); + _eventList.Add(_singleEvent); + } + _eventList.Add(theEvent); + } + } + + /// + /// Fires the Update event. + /// + /// The new events. + /// The old events. + protected void FireUpdateEvent(EventBean[] newEvents, EventBean[] oldEvents) + { + if ((_eventHandlers != null) && (_eventHandlers.Count != 0)) + { + var e = new UpdateEventArgs(_serviceProvider, _statement, newEvents, oldEvents); + foreach (var eventHandler in _eventHandlers) + { + try + { + eventHandler(this, e); + } + catch (Exception ex) + { + String message = "Unexpected exception invoking listener Update method on listener class '" + + eventHandler.GetType().Name + "' : " + ex.GetType().Name + " : " + ex.Message; + Log.Error(message, ex); + } + } + } + } + + public void Execute() + { + EventBean[] eventArray; + + if (_eventList != null) + { + eventArray = _eventList.ToArray(); + _eventList = null; + _singleEvent = null; + } + else + { + eventArray = new EventBean[] + { + _singleEvent + }; + _singleEvent = null; + } + + FireUpdateEvent(eventArray, null); + } + + /// + /// Returns true if at least one event has been added. + /// + /// true if it has data, false if not + public virtual bool HasData + { + get + { + if (_singleEvent != null) + { + return true; + } + return false; + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/service/ResultDeliveryStrategy.cs b/NEsper.Core/NEsper.Core/core/service/ResultDeliveryStrategy.cs new file mode 100755 index 000000000..7a14a764d --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/ResultDeliveryStrategy.cs @@ -0,0 +1,24 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.collection; + +namespace com.espertech.esper.core.service +{ + /// + /// Strategy for use with to dispatch to + /// a statement's subscriber via method invocations. + /// + public interface ResultDeliveryStrategy + { + /// Execute the dispatch. + /// is the insert and remove stream to indicate + void Execute(UniformPair result); + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/service/ResultDeliveryStrategyFactory.cs b/NEsper.Core/NEsper.Core/core/service/ResultDeliveryStrategyFactory.cs new file mode 100755 index 000000000..65135e8cc --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/ResultDeliveryStrategyFactory.cs @@ -0,0 +1,469 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.util; + +namespace com.espertech.esper.core.service +{ + using DataMap = IDictionary; + + /// + /// Factory for creating a dispatch strategy based on the subscriber object + /// and the columns produced by a select-clause. + /// + public class ResultDeliveryStrategyFactory + { + /// + /// Creates a strategy implementation that indicates to subscribers + /// the statement results based on the select-clause columns. + /// + /// The statement. + /// to indicate to + /// are the types of each column in the select clause + /// the names of each column in the select clause + /// The engine URI. + /// The engine import service. + /// + /// strategy for dispatching naturals + /// + /// + /// + /// EPSubscriberException if the subscriber is invalid + public static ResultDeliveryStrategy Create( + EPStatement statement, + EPSubscriber subscriber, + Type[] selectClauseTypes, + string[] selectClauseColumns, + string engineURI, + EngineImportService engineImportService) + { + var subscriberObject = subscriber.Subscriber; + var subscriberMethod = subscriber.SubscriberMethod; + + if (selectClauseTypes == null) + { + selectClauseTypes = new Type[0]; + selectClauseColumns = new string[0]; + } + + var subscriberType = subscriberObject.GetType(); + if (subscriberMethod == null) + { + if (subscriberType.IsDelegate()) + { + subscriberMethod = "Invoke"; + } + else + { + subscriberMethod = "Update"; + } + } + + // Locate Update methods + MethodInfo subscriptionMethod = null; + + var updateMethods = subscriberType + .GetMethods() + .Where(method => (method.Name == subscriberMethod) && (method.IsPublic)) + .OrderBy(method => IsFirstParameterEPStatement(method) ? 0 : 1) + .ToDictionary(method => method, GetMethodParameterTypesWithoutEPStatement); + + // none found + if (updateMethods.Count == 0) + { + var message = "Subscriber object does not provide a public method by name '" + subscriberMethod + "'"; + throw new EPSubscriberException(message); + } + + // match to parameters + var isMapArrayDelivery = false; + var isObjectArrayDelivery = false; + var isSingleRowMap = false; + var isSingleRowObjectArr = false; + var isTypeArrayDelivery = false; + + // find an exact-matching method: no conversions and not even unboxing/boxing + foreach (var methodNormParameterEntry in updateMethods) + { + var normalized = methodNormParameterEntry.Value; + if (normalized.Length == selectClauseTypes.Length) + { + var fits = true; + for (var i = 0; i < normalized.Length; i++) + { + if ((selectClauseTypes[i] != null) && (selectClauseTypes[i] != normalized[i])) + { + fits = false; + break; + } + } + if (fits) + { + subscriptionMethod = methodNormParameterEntry.Key; + break; + } + } + } + + // when not yet resolved, find an exact-matching method with boxing/unboxing + if (subscriptionMethod == null) + { + foreach (var methodNormParameterEntry in updateMethods) + { + var normalized = methodNormParameterEntry.Value; + if (normalized.Length == selectClauseTypes.Length) + { + var fits = true; + for (var i = 0; i < normalized.Length; i++) + { + var boxedExpressionType = selectClauseTypes[i].GetBoxedType(); + var boxedParameterType = normalized[i].GetBoxedType(); + if ((boxedExpressionType != null) && (boxedExpressionType != boxedParameterType)) + { + fits = false; + break; + } + } + if (fits) + { + subscriptionMethod = methodNormParameterEntry.Key; + break; + } + } + } + } + + // when not yet resolved, find assignment-compatible methods that may require widening (including Integer to Long etc.) + var checkWidening = false; + if (subscriptionMethod == null) + { + foreach (var methodNormParameterEntry in updateMethods) + { + var normalized = methodNormParameterEntry.Value; + if (normalized.Length == selectClauseTypes.Length) + { + var fits = true; + for (var i = 0; i < normalized.Length; i++) + { + var boxedExpressionType = selectClauseTypes[i].GetBoxedType(); + var boxedParameterType = normalized[i].GetBoxedType(); + if ((boxedExpressionType != null) && + (!boxedExpressionType.IsAssignmentCompatible(boxedParameterType))) + { + fits = false; + break; + } + } + if (fits) + { + subscriptionMethod = methodNormParameterEntry.Key; + checkWidening = true; + break; + } + } + } + } + + // when not yet resolved, find first-fit wildcard method + if (subscriptionMethod == null) + { + foreach (var methodNormParameterEntry in updateMethods) + { + var normalized = methodNormParameterEntry.Value; + if ((normalized.Length == 1) && (normalized[0] == typeof(DataMap))) + { + isSingleRowMap = true; + subscriptionMethod = methodNormParameterEntry.Key; + break; + } + if ((normalized.Length == 1) && (normalized[0] == typeof(object[]))) + { + isSingleRowObjectArr = true; + subscriptionMethod = methodNormParameterEntry.Key; + break; + } + + if ((normalized.Length == 2) && (normalized[0] == typeof(DataMap[])) && + (normalized[1] == typeof(DataMap[]))) + { + subscriptionMethod = methodNormParameterEntry.Key; + isMapArrayDelivery = true; + break; + } + if ((normalized.Length == 2) && (normalized[0] == typeof(object[][])) && + (normalized[1] == typeof(object[][]))) + { + subscriptionMethod = methodNormParameterEntry.Key; + isObjectArrayDelivery = true; + break; + } + // Handle uniform underlying or column type array dispatch + if ((normalized.Length == 2) && (normalized[0].Equals(normalized[1])) && (normalized[0].IsArray) + && (selectClauseTypes.Length == 1)) + { + var componentType = normalized[0].GetElementType(); + if (selectClauseTypes[0].IsAssignmentCompatible(componentType)) + { + subscriptionMethod = methodNormParameterEntry.Key; + isTypeArrayDelivery = true; + break; + } + } + + if ((normalized.Length == 0) && (selectClauseTypes.Length == 1) && (selectClauseTypes[0] == null)) + { + subscriptionMethod = methodNormParameterEntry.Key; + } + } + } + + if (subscriptionMethod == null) + { + if (updateMethods.Count > 1) + { + var parametersDesc = TypeHelper.GetParameterAsString(selectClauseTypes); + var message = + "No suitable subscriber method named 'Update' found, expecting a method that takes " + + selectClauseTypes.Length + " parameter of type " + parametersDesc; + throw new EPSubscriberException(message); + } + else + { + var firstUpdateMethod = updateMethods.First(); + var parametersNormalized = firstUpdateMethod.Value; + var parametersDescNormalized = TypeHelper.GetParameterAsString(selectClauseTypes); + if (parametersNormalized.Length != selectClauseTypes.Length) + { + if (selectClauseTypes.Length > 0) + { + var message = + "No suitable subscriber method named 'Update' found, expecting a method that takes " + + selectClauseTypes.Length + " parameter of type " + parametersDescNormalized; + throw new EPSubscriberException(message); + } + else + { + var message = + "No suitable subscriber method named 'Update' found, expecting a method that takes no parameters"; + throw new EPSubscriberException(message); + } + } + for (var i = 0; i < parametersNormalized.Length; i++) + { + var boxedExpressionType = selectClauseTypes[i].GetBoxedType(); + var boxedParameterType = parametersNormalized[i].GetBoxedType(); + if ((boxedExpressionType != null) && + (!boxedExpressionType.IsAssignmentCompatible(boxedParameterType))) + { + var message = "Subscriber method named 'Update' for parameter number " + (i + 1) + + " is not assignable, " + + "expecting type '" + selectClauseTypes[i].GetParameterAsString() + + "' but found type '" + + parametersNormalized[i].GetParameterAsString() + "'"; + throw new EPSubscriberException(message); + } + } + } + } + + var parameterTypes = subscriptionMethod.GetParameterTypes(); + + // Invalid if there is a another footprint for the subscription method that does not include EPStatement if present + var firstParameterIsEPStatement = IsFirstParameterEPStatement(subscriptionMethod); + if (isMapArrayDelivery) + { + return firstParameterIsEPStatement + ? new ResultDeliveryStrategyMapWStmt(statement, subscriberObject, subscriptionMethod, selectClauseColumns, engineImportService) + : new ResultDeliveryStrategyMap(statement, subscriberObject, subscriptionMethod, selectClauseColumns, engineImportService); + } + else if (isObjectArrayDelivery) + { + return firstParameterIsEPStatement + ? new ResultDeliveryStrategyObjectArrWStmt(statement, subscriberObject, subscriptionMethod, engineImportService) + : new ResultDeliveryStrategyObjectArr(statement, subscriberObject, subscriptionMethod, engineImportService); + } + else if (isTypeArrayDelivery) + { + return firstParameterIsEPStatement + ? new ResultDeliveryStrategyTypeArrWStmt(statement, subscriberObject, subscriptionMethod, parameterTypes[1].GetElementType(), engineImportService) + : new ResultDeliveryStrategyTypeArr(statement, subscriberObject, subscriptionMethod, parameterTypes[0].GetElementType(), engineImportService); + } + + // Try to find the "start", "end" and "updateRStream" methods + MethodInfo startMethod = null; + MethodInfo endMethod = null; + MethodInfo rStreamMethod = null; + + startMethod = subscriberObject.GetType().GetMethod("UpdateStart", new Type[] { typeof(EPStatement), typeof(int), typeof(int) }); + if (startMethod == null) + { + startMethod = subscriberObject.GetType().GetMethod("UpdateStart", new Type[] { typeof(int), typeof(int) }); + } + + endMethod = subscriberObject.GetType().GetMethod("UpdateEnd", new Type[] { typeof(EPStatement) }); + if (endMethod == null) + { + endMethod = subscriberObject.GetType().GetMethod("UpdateEnd"); + } + + // must be exactly the same footprint (may include EPStatement), since delivery convertor used for both + rStreamMethod = subscriberObject.GetType().GetMethod("UpdateRStream", parameterTypes); + if (rStreamMethod == null) + { + // we don't have an "updateRStream" expected, make sure there isn't one with/without EPStatement + if (IsFirstParameterEPStatement(subscriptionMethod)) + { + var classes = updateMethods.Get(subscriptionMethod); + ValidateNonMatchUpdateRStream(subscriberObject, classes); + } + else + { + var classes = new Type[parameterTypes.Length + 1]; + classes[0] = typeof(EPStatement); + Array.Copy(parameterTypes, 0, classes, 1, parameterTypes.Length); + ValidateNonMatchUpdateRStream(subscriberObject, classes); + } + } + + DeliveryConvertor convertor; + if (isSingleRowMap) + { + convertor = firstParameterIsEPStatement + ? (DeliveryConvertor)new DeliveryConvertorMapWStatement(selectClauseColumns, statement) + : (DeliveryConvertor)new DeliveryConvertorMap(selectClauseColumns); + } + else if (isSingleRowObjectArr) + { + convertor = firstParameterIsEPStatement + ? (DeliveryConvertor)new DeliveryConvertorObjectArrWStatement(statement) + : (DeliveryConvertor)DeliveryConvertorObjectArr.INSTANCE; + } + else + { + if (checkWidening) + { + var normalizedParameters = updateMethods.Get(subscriptionMethod); + convertor = DetermineWideningDeliveryConvertor( + firstParameterIsEPStatement, statement, selectClauseTypes, normalizedParameters, + subscriptionMethod, engineURI); + } + else + { + convertor = firstParameterIsEPStatement + ? (DeliveryConvertor)new DeliveryConvertorNullWStatement(statement) + : (DeliveryConvertor)DeliveryConvertorNull.INSTANCE; + } + } + + return new ResultDeliveryStrategyImpl( + statement, subscriberObject, convertor, subscriptionMethod, startMethod, endMethod, rStreamMethod, engineImportService); + } + + private static DeliveryConvertor DetermineWideningDeliveryConvertor( + bool firstParameterIsEPStatement, + EPStatement statement, + Type[] selectClauseTypes, + Type[] parameterTypes, + MethodInfo method, + string engineURI) + { + var needWidener = false; + for (var i = 0; i < selectClauseTypes.Length; i++) + { + var optionalWidener = GetWidener(i, selectClauseTypes[i], parameterTypes[i], method, statement.Name, engineURI); + if (optionalWidener != null) + { + needWidener = true; + break; + } + } + if (!needWidener) + { + return firstParameterIsEPStatement + ? (DeliveryConvertor)new DeliveryConvertorNullWStatement(statement) + : (DeliveryConvertor)DeliveryConvertorNull.INSTANCE; + } + var wideners = new TypeWidener[selectClauseTypes.Length]; + for (var i = 0; i < selectClauseTypes.Length; i++) + { + wideners[i] = GetWidener(i, selectClauseTypes[i], parameterTypes[i], method, statement.Name, engineURI); + } + return firstParameterIsEPStatement + ? (DeliveryConvertor)new DeliveryConvertorWidenerWStatement(wideners, statement) + : (DeliveryConvertor)new DeliveryConvertorWidener(wideners); + } + + private static TypeWidener GetWidener( + int columnNum, + Type selectClauseType, + Type parameterType, + MethodInfo method, + string statementName, + string engineURI) + { + if (selectClauseType == null || parameterType == null) + { + return null; + } + if (selectClauseType == parameterType) + { + return null; + } + try + { + return TypeWidenerFactory.GetCheckPropertyAssignType( + "Select-Clause Column " + columnNum, selectClauseType, parameterType, + "Method Parameter " + columnNum, false, null, + statementName, engineURI); + } + catch (ExprValidationException e) + { + throw new EPException( + "Unexpected exception assigning select clause columns to subscriber method " + method + ": " + + e.Message, e); + } + } + + private static void ValidateNonMatchUpdateRStream(object subscriber, Type[] classes) + { + var m = subscriber.GetType().GetMethod("UpdateRStream", classes); + if (m != null) + { + throw new EPSubscriberException( + "Subscriber 'updateRStream' method footprint must match 'update' method footprint"); + } + } + + private static Type[] GetMethodParameterTypesWithoutEPStatement(MethodInfo method) + { + var parameterTypes = method.GetParameterTypes(); + if (parameterTypes.Length == 0 || parameterTypes[0] != typeof(EPStatement)) + { + return parameterTypes; + } + var normalized = new Type[parameterTypes.Length - 1]; + Array.Copy(parameterTypes, 1, normalized, 0, parameterTypes.Length - 1); + return normalized; + } + + private static bool IsFirstParameterEPStatement(MethodInfo method) + { + var parameterTypes = method.GetParameterTypes(); + return parameterTypes.Length > 0 && parameterTypes[0] == typeof(EPStatement); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/service/ResultDeliveryStrategyImpl.cs b/NEsper.Core/NEsper.Core/core/service/ResultDeliveryStrategyImpl.cs new file mode 100755 index 000000000..6b92984f7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/ResultDeliveryStrategyImpl.cs @@ -0,0 +1,275 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using com.espertech.esper.epl.core; + +using XLR8.CGLib; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.events; +using com.espertech.esper.util; + +namespace com.espertech.esper.core.service +{ + /// + /// A result delivery strategy that uses a matching "Update" method and optional start, + /// end, and updateRStream methods, to deliver column-wise to parameters of the Update method. + /// + public class ResultDeliveryStrategyImpl : ResultDeliveryStrategy + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly EPStatement _statement; + private readonly Object _subscriber; + private readonly MethodInfo _updateMethodFast; + private readonly MethodInfo _startMethodFast; + private readonly bool _startMethodHasEPStatement; + private readonly MethodInfo _endMethodFast; + private readonly bool _endMethodHasEPStatement; + private readonly MethodInfo _updateRStreamMethodFast; + private readonly DeliveryConvertor _deliveryConvertor; + + /// + /// Initializes a new instance of the class. + /// + /// The statement. + /// The subscriber. + /// The delivery convertor. + /// The method. + /// The start method. + /// The end method. + /// The r stream method. + public ResultDeliveryStrategyImpl( + EPStatement statement, + Object subscriber, + DeliveryConvertor deliveryConvertor, + MethodInfo method, + MethodInfo startMethod, + MethodInfo endMethod, + MethodInfo rStreamMethod, + EngineImportService engineImportService) + { + _statement = statement; + _subscriber = subscriber; + _deliveryConvertor = deliveryConvertor; + _updateMethodFast = FastClass.CreateMethod(method); + + if (startMethod != null) + { + _startMethodFast = FastClass.CreateMethod(startMethod); + _startMethodHasEPStatement = IsMethodAcceptsStatement(startMethod); + } + else + { + _startMethodFast = null; + _startMethodHasEPStatement = false; + } + + if (endMethod != null) + { + _endMethodFast = FastClass.CreateMethod(endMethod); + _endMethodHasEPStatement = IsMethodAcceptsStatement(endMethod); + } + else + { + _endMethodFast = null; + _endMethodHasEPStatement = false; + } + + _updateRStreamMethodFast = rStreamMethod != null ? FastClass.CreateMethod(rStreamMethod) : null; + } + + #region ResultDeliveryStrategy Members + + public void Execute(UniformPair result) + { + if (_startMethodFast != null) + { + int countNew = 0; + int countOld = 0; + if (result != null) + { + countNew = Count(result.First); + countOld = Count(result.Second); + } + + Object[] paramList; + if (!_startMethodHasEPStatement) + { + paramList = new Object[] { countNew, countOld }; + } + else + { + paramList = new Object[] { _statement, countNew, countOld }; + } + + try + { + _startMethodFast.Invoke(_subscriber, paramList); + } + catch (TargetInvocationException e) + { + Handle(_statement.Name, Log, e, paramList, _subscriber, _startMethodFast); + } + catch (Exception e) + { + HandleThrowable(Log, e, null, _subscriber, _startMethodFast); + } + } + + EventBean[] newData = null; + EventBean[] oldData = null; + if (result != null) + { + newData = result.First; + oldData = result.Second; + } + + if ((newData != null) && (newData.Length > 0)) + { + for (int i = 0; i < newData.Length; i++) + { + EventBean theEvent = newData[i]; + if (theEvent is NaturalEventBean) + { + var natural = (NaturalEventBean)theEvent; + Object[] paramList = _deliveryConvertor.ConvertRow(natural.Natural); + try + { + _updateMethodFast.Invoke(_subscriber, paramList); + } + catch (TargetInvocationException e) + { + Handle(_statement.Name, Log, e, paramList, _subscriber, _updateMethodFast); + } + catch (Exception e) + { + HandleThrowable(Log, e, paramList, _subscriber, _updateMethodFast); + } + } + } + } + + if ((_updateRStreamMethodFast != null) && (oldData != null) && (oldData.Length > 0)) + { + for (int i = 0; i < oldData.Length; i++) + { + EventBean theEvent = oldData[i]; + if (theEvent is NaturalEventBean) + { + var natural = (NaturalEventBean)theEvent; + var paramList = _deliveryConvertor.ConvertRow(natural.Natural); + try + { + _updateRStreamMethodFast.Invoke(_subscriber, paramList); + } + catch (TargetInvocationException e) + { + Handle(_statement.Name, Log, e, paramList, _subscriber, _updateRStreamMethodFast); + } + catch (Exception e) + { + HandleThrowable(Log, e, paramList, _subscriber, _updateRStreamMethodFast); + } + } + } + } + + if (_endMethodFast != null) + { + var paramList = _endMethodHasEPStatement ? new object[] { _statement } : new object[] { }; + try + { + _endMethodFast.Invoke(_subscriber, paramList); + } + catch (TargetInvocationException e) + { + Handle(_statement.Name, Log, e, null, _subscriber, _endMethodFast); + } + catch (Exception e) + { + HandleThrowable(Log, e, null, _subscriber, _endMethodFast); + } + } + } + + #endregion + + /// + /// Handle the exception, displaying a nice message and converting to . + /// + /// Name of the statement. + /// is the logger to use for error logging + /// is the exception + /// the method parameters + /// the object to deliver to + /// the method to call + /// EPException converted from the passed invocation exception + protected internal static void Handle(String statementName, + ILog logger, + TargetInvocationException e, + Object[] paramList, + Object subscriber, + MethodInfo method) + { + String message = TypeHelper.GetMessageInvocationTarget(statementName, method.Target, + subscriber.GetType().FullName, + paramList, e); + logger.Error(message, e.InnerException); + } + + /// Handle the exception, displaying a nice message and converting to . + /// is the logger to use for error logging + /// is the throwable + /// the method parameters + /// the object to deliver to + /// the method to call + /// EPException converted from the passed invocation exception + internal static void HandleThrowable(ILog logger, + Exception e, + Object[] paramList, + Object subscriber, + MethodInfo method) + { + String message = "Unexpected exception when invoking method '" + method.Name + + "' on subscriber class '" + subscriber.GetType().Name + + "' for parameters " + ((paramList == null) ? "null" : paramList.Render()) + + " : " + e.GetType().Name + " : " + e.Message; + logger.Error(message, e); + } + + private static int Count(EventBean[] events) + { + if (events == null) + { + return 0; + } + int count = 0; + for (int i = 0; i < events.Length; i++) + { + EventBean theEvent = events[i]; + if (theEvent is NaturalEventBean) + { + count++; + } + } + return count; + } + + private static bool IsMethodAcceptsStatement(MethodInfo method) + { + return method.GetParameterTypes().Length > 0 && method.GetParameterTypes()[0] == typeof(EPStatement); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/service/ResultDeliveryStrategyMap.cs b/NEsper.Core/NEsper.Core/core/service/ResultDeliveryStrategyMap.cs new file mode 100755 index 000000000..1d804c976 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/ResultDeliveryStrategyMap.cs @@ -0,0 +1,125 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.epl.core; + +using XLR8.CGLib; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.events; + +namespace com.espertech.esper.core.service +{ + using DataMap = IDictionary; + + /// + /// A result delivery strategy that uses an "Update" method that accepts a pair of map array. + /// + public class ResultDeliveryStrategyMap : ResultDeliveryStrategy + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + internal readonly EPStatement _statement; + internal readonly Object _subscriber; + internal readonly FastMethod _fastMethod; + internal readonly String[] _columnNames; + + /// + /// Ctor. + /// + /// The statement. + /// the object to deliver to + /// the delivery method + /// the column names for the map + /// The engine import service. + public ResultDeliveryStrategyMap(EPStatement statement, object subscriber, MethodInfo method, string[] columnNames, EngineImportService engineImportService) + { + _statement = statement; + _subscriber = subscriber; + _fastMethod = FastClass.CreateMethod(method); + _columnNames = columnNames; + } + + public virtual void Execute(UniformPair result) + { + DataMap[] newData; + DataMap[] oldData; + + if (result == null) + { + newData = null; + oldData = null; + } + else + { + newData = Convert(result.First); + oldData = Convert(result.Second); + } + + var paramList = new Object[] { newData, oldData }; + try + { + _fastMethod.Invoke(_subscriber, paramList); + } + catch (TargetInvocationException e) + { + ResultDeliveryStrategyImpl.Handle(_statement.Name, Log, e, paramList, _subscriber, _fastMethod); + } + } + + internal DataMap[] Convert(EventBean[] events) + { + if ((events == null) || (events.Length == 0)) + { + return null; + } + + var result = new DataMap[events.Length]; + var length = 0; + for (int i = 0; i < result.Length; i++) + { + if (events[i] is NaturalEventBean) + { + var natural = (NaturalEventBean)events[i]; + result[length] = Convert(natural); + length++; + } + } + + if (length == 0) + { + return null; + } + if (length != events.Length) + { + var reduced = new DataMap[length]; + Array.Copy(result, 0, reduced, 0, length); + result = reduced; + } + return result; + } + + private IDictionary Convert(NaturalEventBean natural) + { + var map = new Dictionary(); + var columns = natural.Natural; + for (int i = 0; i < columns.Length; i++) + { + map.Put(_columnNames[i], columns[i]); + } + return map; + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/ResultDeliveryStrategyMapWStmt.cs b/NEsper.Core/NEsper.Core/core/service/ResultDeliveryStrategyMapWStmt.cs new file mode 100755 index 000000000..16ea18421 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/ResultDeliveryStrategyMapWStmt.cs @@ -0,0 +1,55 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.core; + +namespace com.espertech.esper.core.service +{ + public class ResultDeliveryStrategyMapWStmt : ResultDeliveryStrategyMap + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + public ResultDeliveryStrategyMapWStmt(EPStatement statement, object subscriber, MethodInfo method, string[] columnNames, EngineImportService engineImportService) + : base(statement, subscriber, method, columnNames, engineImportService) + { + } + + public override void Execute(UniformPair result) + { + IDictionary[] newData; + IDictionary[] oldData; + + if (result == null) + { + newData = null; + oldData = null; + } + else + { + newData = Convert(result.First); + oldData = Convert(result.Second); + } + + var parameters = new object[] { _statement, newData, oldData }; + try + { + _fastMethod.Invoke(_subscriber, parameters); + } + catch (TargetInvocationException e) + { + ResultDeliveryStrategyImpl.Handle(_statement.Name, Log, e, parameters, _subscriber, _fastMethod); + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/service/ResultDeliveryStrategyObjectArr.cs b/NEsper.Core/NEsper.Core/core/service/ResultDeliveryStrategyObjectArr.cs new file mode 100755 index 000000000..c9bdd261e --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/ResultDeliveryStrategyObjectArr.cs @@ -0,0 +1,106 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using com.espertech.esper.epl.core; + +using XLR8.CGLib; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.logging; +using com.espertech.esper.events; + +namespace com.espertech.esper.core.service +{ + /// + /// A result delivery strategy that uses an "Update" method that accepts a pair of object array array. + /// + public class ResultDeliveryStrategyObjectArr : ResultDeliveryStrategy + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + internal readonly EPStatement _statement; + internal readonly Object _subscriber; + internal readonly MethodInfo _method; + + /// + /// Ctor. + /// + /// the statement. + /// is the subscriber to deliver to + /// the method to invoke + public ResultDeliveryStrategyObjectArr(EPStatement statement, Object subscriber, MethodInfo method, EngineImportService engineImportService) + { + _statement = statement; + _subscriber = subscriber; + _method = FastClass.CreateMethod(method); + } + + public virtual void Execute(UniformPair result) + { + Object[][] newData; + Object[][] oldData; + + if (result == null) + { + newData = null; + oldData = null; + } + else + { + newData = Convert(result.First); + oldData = Convert(result.Second); + } + + var paramList = new Object[] { newData, oldData }; + try + { + _method.Invoke(_subscriber, paramList); + } + catch (TargetInvocationException e) + { + ResultDeliveryStrategyImpl.Handle(_statement.Name, Log, e, paramList, _subscriber, _method); + } + } + + internal Object[][] Convert(EventBean[] events) + { + if ((events == null) || (events.Length == 0)) + { + return null; + } + + var result = new Object[events.Length][]; + var length = 0; + for (var i = 0; i < result.Length; i++) + { + if (events[i] is NaturalEventBean) + { + var natural = (NaturalEventBean)events[i]; + result[length] = natural.Natural; + length++; + } + } + + if (length == 0) + { + return null; + } + if (length != events.Length) + { + var reduced = new Object[length][]; + Array.Copy(result, 0, reduced, 0, length); + result = reduced; + } + return result; + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/ResultDeliveryStrategyObjectArrWStmt.cs b/NEsper.Core/NEsper.Core/core/service/ResultDeliveryStrategyObjectArrWStmt.cs new file mode 100755 index 000000000..b0bb546b6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/ResultDeliveryStrategyObjectArrWStmt.cs @@ -0,0 +1,61 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.core; +using com.espertech.esper.events; + +namespace com.espertech.esper.core.service +{ + /// + /// A result delivery strategy that uses an "update" method that accepts a pair of object array array. + /// + public class ResultDeliveryStrategyObjectArrWStmt : ResultDeliveryStrategyObjectArr + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + public ResultDeliveryStrategyObjectArrWStmt(EPStatement statement, object subscriber, MethodInfo method, EngineImportService engineImportService) + : base(statement, subscriber, method, engineImportService) + { + } + + public override void Execute(UniformPair result) + { + object[][] newData; + object[][] oldData; + + if (result == null) + { + newData = null; + oldData = null; + } + else + { + newData = Convert(result.First); + oldData = Convert(result.Second); + } + + var parameters = new object[] { _statement, newData, oldData }; + try + { + _method.Invoke(_subscriber, parameters); + } + catch (TargetInvocationException e) + { + ResultDeliveryStrategyImpl.Handle(_statement.Name, Log, e, parameters, _subscriber, _method); + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/service/ResultDeliveryStrategyTypeArr.cs b/NEsper.Core/NEsper.Core/core/service/ResultDeliveryStrategyTypeArr.cs new file mode 100755 index 000000000..845174371 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/ResultDeliveryStrategyTypeArr.cs @@ -0,0 +1,109 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using com.espertech.esper.epl.core; + +using XLR8.CGLib; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.logging; +using com.espertech.esper.events; + +namespace com.espertech.esper.core.service +{ + /// + /// A result delivery strategy that uses an "Update" method that accepts a underlying + /// array for use in wildcard selection. + /// + public class ResultDeliveryStrategyTypeArr : ResultDeliveryStrategy + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + internal readonly EPStatement _statement; + internal readonly Object _subscriber; + internal readonly FastMethod _fastMethod; + internal readonly Type _componentType; + + /// + /// Ctor. + /// + /// the statement. + /// is the receiver to method invocations + /// is the method to deliver to + public ResultDeliveryStrategyTypeArr(EPStatement statement, Object subscriber, MethodInfo method, Type componentType, EngineImportService engineImportService) + { + _statement = statement; + _subscriber = subscriber; + _fastMethod = FastClass.CreateMethod(method); + _componentType = componentType; + } + + public virtual void Execute(UniformPair result) + { + Object newData; + Object oldData; + + if (result == null) + { + newData = null; + oldData = null; + } + else + { + newData = Convert(result.First); + oldData = Convert(result.Second); + } + + var paramList = new[] { newData, oldData }; + try + { + _fastMethod.Invoke(_subscriber, paramList); + } + catch (TargetInvocationException e) + { + ResultDeliveryStrategyImpl.Handle(_statement.Name, Log, e, paramList, _subscriber, _fastMethod); + } + } + + internal Object Convert(EventBean[] events) + { + if ((events == null) || (events.Length == 0)) + { + return null; + } + + Array array = Array.CreateInstance(_componentType, events.Length); + int length = 0; + for (int i = 0; i < events.Length; i++) + { + if (events[i] is NaturalEventBean) + { + var natural = (NaturalEventBean)events[i]; + array.SetValue(natural.Natural[0], length); + length++; + } + } + + if (length == 0) + { + return null; + } + if (length != events.Length) + { + Array reduced = Array.CreateInstance(_componentType, events.Length); + Array.Copy(array, 0, reduced, 0, length); + array = reduced; + } + return array; + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/ResultDeliveryStrategyTypeArrWStmt.cs b/NEsper.Core/NEsper.Core/core/service/ResultDeliveryStrategyTypeArrWStmt.cs new file mode 100755 index 000000000..d8984abbd --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/ResultDeliveryStrategyTypeArrWStmt.cs @@ -0,0 +1,55 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.core; + +namespace com.espertech.esper.core.service +{ + public class ResultDeliveryStrategyTypeArrWStmt : ResultDeliveryStrategyTypeArr + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + public ResultDeliveryStrategyTypeArrWStmt(EPStatement statement, object subscriber, MethodInfo method, Type componentType, EngineImportService engineImportService) + : base(statement, subscriber, method, componentType, engineImportService) + { + } + + public override void Execute(UniformPair result) + { + object newData; + object oldData; + + if (result == null) + { + newData = null; + oldData = null; + } + else + { + newData = Convert(result.First); + oldData = Convert(result.Second); + } + + object[] parameters = new object[] { _statement, newData, oldData }; + try + { + _fastMethod.Invoke(_subscriber, parameters); + } + catch (TargetInvocationException e) + { + ResultDeliveryStrategyImpl.Handle(_statement.Name, Log, e, parameters, _subscriber, _fastMethod); + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/service/SchedulingServiceAudit.cs b/NEsper.Core/NEsper.Core/core/service/SchedulingServiceAudit.cs new file mode 100755 index 000000000..c0bcea1d9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/SchedulingServiceAudit.cs @@ -0,0 +1,144 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.client.annotation; +using com.espertech.esper.schedule; +using com.espertech.esper.util; + +namespace com.espertech.esper.core.service +{ + public class SchedulingServiceAudit : SchedulingServiceSPI + { + private readonly String _engineUri; + private readonly String _statementName; + private readonly SchedulingServiceSPI _spi; + + public SchedulingServiceAudit(String engineUri, String statementName, SchedulingServiceSPI spi) + { + _engineUri = engineUri; + _statementName = statementName; + _spi = spi; + } + + public bool IsScheduled(ScheduleHandle handle) + { + return _spi.IsScheduled(handle); + } + + public ScheduleSet Take(ICollection statementId) + { + return _spi.Take(statementId); + } + + public void Apply(ScheduleSet scheduleSet) + { + _spi.Apply(scheduleSet); + } + + public long? NearestTimeHandle + { + get { return _spi.NearestTimeHandle; } + } + + public void VisitSchedules(ScheduleVisitor visitor) + { + _spi.VisitSchedules(visitor); + } + + public void Add(long afterTime, ScheduleHandle handle, long slot) + { + if (AuditPath.IsInfoEnabled) { + StringWriter message = new StringWriter(); + message.Write("after "); + message.Write(afterTime); + message.Write(" handle "); + PrintHandle(message, handle); + + AuditPath.AuditLog(_engineUri, _statementName, AuditEnum.SCHEDULE, message.ToString()); + + ModifyCreateProxy(handle); + } + _spi.Add(afterTime, handle, slot); + } + + public void Remove(ScheduleHandle handle, long scheduleSlot) + { + if (AuditPath.IsInfoEnabled) { + StringWriter message = new StringWriter(); + message.Write("remove handle "); + PrintHandle(message, handle); + + AuditPath.AuditLog(_engineUri, _statementName, AuditEnum.SCHEDULE, message.ToString()); + } + _spi.Remove(handle, scheduleSlot); + } + + public long Time + { + set { _spi.Time = value; } + get { return _spi.Time; } + } + + public void Evaluate(ICollection handles) + { + _spi.Evaluate(handles); + } + + public void Dispose() + { + _spi.Dispose(); + } + + public int TimeHandleCount + { + get { return _spi.TimeHandleCount; } + } + + public long? FurthestTimeHandle + { + get { return _spi.FurthestTimeHandle; } + } + + public int ScheduleHandleCount + { + get { return _spi.ScheduleHandleCount; } + } + + public void Init() + { + // no action required + } + + private void PrintHandle(TextWriter message, ScheduleHandle handle) + { + if (handle is EPStatementHandleCallback) + { + var callback = (EPStatementHandleCallback) handle; + TypeHelper.WriteInstance(message, callback.ScheduleCallback, true); + } + else { + TypeHelper.WriteInstance(message, handle, true); + } + } + + private void ModifyCreateProxy(ScheduleHandle handle) + { + if (!(handle is EPStatementHandleCallback)) + { + return; + } + var callback = (EPStatementHandleCallback) handle; + var sc = ScheduleHandleCallbackProxy.NewInstance(_engineUri, _statementName, callback.ScheduleCallback); + callback.ScheduleCallback = sc; + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/SimpleServiceDirectory.cs b/NEsper.Core/NEsper.Core/core/service/SimpleServiceDirectory.cs new file mode 100755 index 000000000..e9ba419e0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/SimpleServiceDirectory.cs @@ -0,0 +1,141 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.threading; + +namespace com.espertech.esper.core +{ + /// + /// Simple implementation of the directory. + /// + + public class SimpleServiceDirectory : Directory + { + private IDictionary m_dataTable ; + private ILockable m_dataLock; + + /// + /// Initializes a new instance of the class. + /// + public SimpleServiceDirectory() + { + m_dataTable = new Dictionary() ; + m_dataLock = LockManager.CreateLock(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + } + + /// + /// Lookup an object by name. + /// + /// + /// + public object Lookup(string name) + { + using(m_dataLock.Acquire()) + { + return m_dataTable.Get( name ) ; + } + } + + /// + /// Bind an object to a name. Throws an exception if + /// the name is already bound. + /// + /// + /// + public void Bind(string name, object obj) + { + using(m_dataLock.Acquire()) + { +#if true + m_dataTable[name] = obj; +#else + if ( m_dataTable.ContainsKey( name ) ) + { + throw new DirectoryException( "Value '" + name + "' was already bound" ) ; + } + + m_dataTable[name] = obj ; +#endif + } + } + + /// + /// Bind an object to a name. If the object is already + /// bound, rebind it. + /// + /// + /// + public void Rebind(string name, object obj) + { + using(m_dataLock.Acquire()) + { + m_dataTable[name] = obj; + } + } + + /// + /// Unbind the object at the given name. + /// + /// + public void Unbind(string name) + { + using(m_dataLock.Acquire()) + { + m_dataTable.Remove( name ) ; + } + } + + /// + /// Rename the object at oldName with newName. + /// + /// + /// + public void Rename(string oldName, string newName) + { + using(m_dataLock.Acquire()) + { + object tempObj = m_dataTable.Get( oldName ); + if ( tempObj == null ) + { + throw new DirectoryException( "Value '" + oldName + "' was not found" ) ; + } + + if ( m_dataTable.ContainsKey( newName ) ) + { + throw new DirectoryException( "Value '" + newName + "' was already bound" ) ; + } + + m_dataTable.Remove(oldName) ; + m_dataTable[newName] = tempObj; + } + } + + /// + /// Enumerates the names bound in the named context. + /// + /// + /// + public IEnumerator List(string name) + { + return m_dataTable.Keys.GetEnumerator(); + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + m_dataTable = null ; + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/StatementAgentInstanceFilterVersion.cs b/NEsper.Core/NEsper.Core/core/service/StatementAgentInstanceFilterVersion.cs new file mode 100755 index 000000000..9ede03b46 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/StatementAgentInstanceFilterVersion.cs @@ -0,0 +1,52 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.core.service +{ + /// + /// Records minimal statement filter version required for processing. + /// + [Serializable] + public class StatementAgentInstanceFilterVersion + { + private long _stmtFilterVersion; + + /// Ctor. + public StatementAgentInstanceFilterVersion() + { + _stmtFilterVersion = long.MinValue; + } + + /// Set filter version. + /// to set + public long StmtFilterVersion + { + get { return _stmtFilterVersion; } + set { _stmtFilterVersion = value; } + } + + /// Check current filter. + /// to check + /// false if not current + public bool IsCurrentFilter(long filterVersion) + { + if (filterVersion < _stmtFilterVersion) + { + // catch-up in case of roll + if (filterVersion + 100000 < _stmtFilterVersion && _stmtFilterVersion != long.MaxValue) + { + _stmtFilterVersion = filterVersion; + } + return false; + } + return true; + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/service/StatementContext.cs b/NEsper.Core/NEsper.Core/core/service/StatementContext.cs new file mode 100755 index 000000000..4505a380f --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/StatementContext.cs @@ -0,0 +1,449 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.threading; +using com.espertech.esper.core.context.factory; +using com.espertech.esper.core.context.mgr; +using com.espertech.esper.core.context.stmt; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.declexpr; +using com.espertech.esper.epl.expression.time; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.epl.metric; +using com.espertech.esper.epl.named; +using com.espertech.esper.epl.script; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.epl.variable; +using com.espertech.esper.events; +using com.espertech.esper.events.vaevent; +using com.espertech.esper.filter; +using com.espertech.esper.pattern; +using com.espertech.esper.pattern.pool; +using com.espertech.esper.rowregex; +using com.espertech.esper.schedule; +using com.espertech.esper.script; +using com.espertech.esper.timer; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.service +{ + /// + /// Contains handles to the implementation of the the scheduling service for use in view evaluation. + /// + public sealed class StatementContext + { + private readonly StatementResultService _statementResultService; + private readonly StatementContextEngineServices _stmtEngineServices; + + // settable for view-sharing + + /// + /// Constructor. + /// + /// is the engine services for the statement + /// implementation for schedule registration + /// is for ordering scheduled callbacks within the view statements + /// is the statements-own handle for use in registering callbacks with services + /// is a service for resolving view namespace and name to a view factory + /// is the service that resolves pattern objects for the statement + /// provide extension points for custom statement resources + /// for registering a callback invoked when a statement is stopped + /// is the pattern-level services and context information factory + /// is the filtering service + /// handles awareness of listeners/subscriptions for a statement customizing output produced + /// routing destination + /// The annotations. + /// The statement agent instance registry. + /// The default agent instance lock. + /// The context descriptor. + /// The pattern subexpression pool SVC. + /// The match recognize state pool statement SVC. + /// if set to true [stateless select]. + /// The context controller factory service. + /// The default agent instance script context. + /// The aggregation service factory service. + /// The scripting service. + /// if set to true [writes to tables]. + /// The statement user object. + /// The statement semi anonymous type registry. + /// The priority. + public StatementContext( + StatementContextEngineServices stmtEngineServices, + SchedulingService schedulingService, + ScheduleBucket scheduleBucket, + EPStatementHandle epStatementHandle, + ViewResolutionService viewResultionService, + PatternObjectResolutionService patternResolutionService, + StatementExtensionSvcContext statementExtensionSvcContext, + StatementStopService statementStopService, + PatternContextFactory patternContextFactory, + FilterService filterService, + StatementResultService statementResultService, + InternalEventRouteDest internalEventEngineRouteDest, + Attribute[] annotations, + StatementAIResourceRegistry statementAgentInstanceRegistry, + IReaderWriterLock defaultAgentInstanceLock, + ContextDescriptor contextDescriptor, + PatternSubexpressionPoolStmtSvc patternSubexpressionPoolSvc, + MatchRecognizeStatePoolStmtSvc matchRecognizeStatePoolStmtSvc, + bool statelessSelect, + ContextControllerFactoryService contextControllerFactoryService, + AgentInstanceScriptContext defaultAgentInstanceScriptContext, + AggregationServiceFactoryService aggregationServiceFactoryService, + ScriptingService scriptingService, + bool writesToTables, + object statementUserObject, + StatementSemiAnonymousTypeRegistry statementSemiAnonymousTypeRegistry, + int priority) + { + _stmtEngineServices = stmtEngineServices; + SchedulingService = schedulingService; + ScheduleBucket = scheduleBucket; + EpStatementHandle = epStatementHandle; + ViewResolutionService = viewResultionService; + PatternResolutionService = patternResolutionService; + StatementExtensionServicesContext = statementExtensionSvcContext; + StatementStopService = statementStopService; + PatternContextFactory = patternContextFactory; + FilterService = filterService; + _statementResultService = statementResultService; + InternalEventEngineRouteDest = internalEventEngineRouteDest; + ScheduleAdjustmentService = stmtEngineServices.ConfigSnapshot.EngineDefaults.Execution.IsAllowIsolatedService ? new ScheduleAdjustmentService() : null; + Annotations = annotations; + StatementAgentInstanceRegistry = statementAgentInstanceRegistry; + DefaultAgentInstanceLock = defaultAgentInstanceLock; + ContextDescriptor = contextDescriptor; + PatternSubexpressionPoolSvc = patternSubexpressionPoolSvc; + MatchRecognizeStatePoolStmtSvc = matchRecognizeStatePoolStmtSvc; + IsStatelessSelect = statelessSelect; + ContextControllerFactoryService = contextControllerFactoryService; + DefaultAgentInstanceScriptContext = defaultAgentInstanceScriptContext; + AggregationServiceFactoryService = aggregationServiceFactoryService; + ScriptingService = scriptingService; + IsWritesToTables = writesToTables; + StatementUserObject = statementUserObject; + StatementSemiAnonymousTypeRegistry = statementSemiAnonymousTypeRegistry; + Priority = priority; + } + + /// Returns the statement id. + /// statement id + public int StatementId + { + get { return EpStatementHandle.StatementId; } + } + + /// Returns the statement type. + /// statement type + public StatementType StatementType + { + get { return EpStatementHandle.StatementType; } + } + + /// Returns the statement name + /// statement name + public string StatementName + { + get { return EpStatementHandle.StatementName; } + } + + /// Returns service to use for schedule evaluation. + /// schedule evaluation service implemetation + public SchedulingService SchedulingService { get; set; } + + /// Returns service for generating events and handling event types. + /// event adapter service + public EventAdapterService EventAdapterService + { + get { return _stmtEngineServices.EventAdapterService; } + } + + /// Returns the schedule bucket for ordering schedule callbacks within this pattern. + /// schedule bucket + public ScheduleBucket ScheduleBucket { get; private set; } + + /// Returns the statement's resource locks. + /// statement resource lock/handle + public EPStatementHandle EpStatementHandle { get; private set; } + + /// Returns view resolution svc. + /// view resolution + public ViewResolutionService ViewResolutionService { get; private set; } + + /// Returns extension context for statements. + /// context + public StatementExtensionSvcContext StatementExtensionServicesContext { get; private set; } + + /// Returns statement stop subscription taker. + /// stop service + public StatementStopService StatementStopService { get; private set; } + + /// Returns the pattern context factory for the statement. + /// pattern context factory + public PatternContextFactory PatternContextFactory { get; private set; } + + public MatchRecognizeStatePoolStmtSvc MatchRecognizeStatePoolStmtSvc { get; private set; } + + /// Gets or sets the compiled statement spec. + public StatementSpecCompiled StatementSpecCompiled { get; set; } + /// Gets or sets the statement agent instance factory. + public StatementAgentInstanceFactory StatementAgentInstanceFactory { get; set; } + /// Gets or sets the statement. + public EPStatementSPI Statement { get; set; } + + public EngineLevelExtensionServicesContext EngineExtensionServicesContext + { + get { return _stmtEngineServices.EngineLevelExtensionServicesContext; } + } + + public RegexHandlerFactory RegexPartitionStateRepoFactory + { + get { return _stmtEngineServices.RegexHandlerFactory; } + } + + public ViewServicePreviousFactory ViewServicePreviousFactory + { + get { return _stmtEngineServices.ViewServicePreviousFactory; } + } + + public PatternNodeFactory PatternNodeFactory + { + get { return _stmtEngineServices.PatternNodeFactory; } + } + + public EventTableIndexService EventTableIndexService + { + get { return _stmtEngineServices.EventTableIndexService; } + } + + public StatementLockFactory StatementLockFactory + { + get { return _stmtEngineServices.StatementLockFactory; } + } + + /// Returns the statement expression text + /// expression text + public string Expression + { + get { return EpStatementHandle.EPL; } + } + + /// Returns the engine URI. + /// engine URI + public string EngineURI + { + get { return _stmtEngineServices.EngineURI; } + } + + /// Returns the statement's resolution service for pattern objects. + /// service for resolving pattern objects + public PatternObjectResolutionService PatternResolutionService { get; private set; } + + /// Returns the named window management service. + /// service for managing named windows + public NamedWindowMgmtService NamedWindowMgmtService + { + get { return _stmtEngineServices.NamedWindowMgmtService; } + } + + /// Returns variable service. + /// variable service + public VariableService VariableService + { + get { return _stmtEngineServices.VariableService; } + } + + /// Returns table service. + /// The table service. + public TableService TableService + { + get { return _stmtEngineServices.TableService; } + } + + /// Returns the service that handles awareness of listeners/subscriptions for a statement customizing output produced + /// statement result svc + public StatementResultService StatementResultService + { + get { return _statementResultService; } + } + + /// Returns the URIs for resolving the event name against plug-inn event representations, if any + /// URIs + public IList PlugInTypeResolutionURIs + { + get { return _stmtEngineServices.PlugInTypeResolutionURIs; } + } + + /// Returns the Update event service. + /// revision service + public ValueAddEventService ValueAddEventService + { + get { return _stmtEngineServices.ValueAddEventService; } + } + + /// Returns the configuration. + /// configuration + public ConfigurationInformation ConfigSnapshot + { + get { return _stmtEngineServices.ConfigSnapshot; } + } + + /// Sets the filter service + /// filter service + public FilterService FilterService { get; set; } + + /// Returns the internal event router. + /// router + public InternalEventRouteDest InternalEventEngineRouteDest { get; set; } + + /// Return the service for adjusting schedules or null if not applicable. + /// service for adjusting schedules + public ScheduleAdjustmentService ScheduleAdjustmentService { get; private set; } + + /// Returns metrics svc. + /// metrics + public MetricReportingServiceSPI MetricReportingService + { + get { return _stmtEngineServices.MetricReportingService; } + } + + /// Returns the time provider. + /// time provider + public TimeProvider TimeProvider + { + get { return SchedulingService; } + } + + /// Returns view svc. + /// svc + public ViewService ViewService + { + get { return _stmtEngineServices.ViewService; } + } + + public ExceptionHandlingService ExceptionHandlingService + { + get { return _stmtEngineServices.ExceptionHandlingService; } + } + + public TableExprEvaluatorContext TableExprEvaluatorContext + { + get { return _stmtEngineServices.TableExprEvaluatorContext; } + } + + public ContextManagementService ContextManagementService + { + get { return _stmtEngineServices.ContextManagementService; } + } + + public Attribute[] Annotations { get; private set; } + + public ExpressionResultCacheService ExpressionResultCacheServiceSharable + { + get { return _stmtEngineServices.ExpressionResultCacheService; } + } + + public int AgentInstanceId + { + get { throw new Exception("Statement agent instance information is not available when providing a context"); } + } + + public StatementAIResourceRegistry StatementAgentInstanceRegistry { get; private set; } + + public IReaderWriterLock DefaultAgentInstanceLock { get; set; } + + public ContextDescriptor ContextDescriptor { get; private set; } + + public PatternSubexpressionPoolStmtSvc PatternSubexpressionPoolSvc { get; private set; } + + public bool IsStatelessSelect { get; private set; } + + public ContextControllerFactoryService ContextControllerFactoryService { get; private set; } + + public AgentInstanceScriptContext DefaultAgentInstanceScriptContext { get; private set; } + + public AggregationServiceFactoryService AggregationServiceFactoryService { get; private set; } + + public StatementSemiAnonymousTypeRegistry StatementSemiAnonymousTypeRegistry { get; private set; } + + public int Priority { get; private set; } + + public FilterFaultHandlerFactory FilterFaultHandlerFactory { get; set; } + + public FilterBooleanExpressionFactory FilterBooleanExpressionFactory + { + get { return _stmtEngineServices.FilterBooleanExpressionFactory; } + } + + public EngineSettingsService EngineSettingsService + { + get { return _stmtEngineServices.EngineSettingsService; } + } + + public ExprDeclaredService ExprDeclaredService + { + get { return _stmtEngineServices.ExprDeclaredService; } + } + + public TimeSourceService TimeSourceService + { + get { return _stmtEngineServices.TimeSourceService; } + } + + public EngineImportService EngineImportService + { + get { return _stmtEngineServices.EngineImportService; } + } + + public TimeAbacus TimeAbacus + { + get { return _stmtEngineServices.EngineImportService.TimeAbacus; } + } + + public AgentInstanceScriptContext AllocateAgentInstanceScriptContext + { + get + { + if (DefaultAgentInstanceScriptContext == null) + { + DefaultAgentInstanceScriptContext = AgentInstanceScriptContext.From(EventAdapterService); + } + return DefaultAgentInstanceScriptContext; + } + } + + public StatementEventTypeRef StatementEventTypeRef + { + get { return _stmtEngineServices.StatementEventTypeRef; } + } + + public ScriptingService ScriptingService { get; private set; } + + public string ContextName + { + get { return ContextDescriptor == null ? null : ContextDescriptor.ContextName; } + } + + public bool IsWritesToTables { get; private set; } + + public object StatementUserObject { get; private set; } + + public override String ToString() + { + return " stmtId=" + EpStatementHandle.StatementId + + " stmtName=" + EpStatementHandle.StatementName; + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/service/StatementContextCPPair.cs b/NEsper.Core/NEsper.Core/core/service/StatementContextCPPair.cs new file mode 100755 index 000000000..4c740de83 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/StatementContextCPPair.cs @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.core.service +{ + public class StatementContextCPPair + { + public StatementContextCPPair(int statementId, int agentInstanceId, StatementContext optionalStatementContext) + { + StatementId = statementId; + AgentInstanceId = agentInstanceId; + OptionalStatementContext = optionalStatementContext; + } + + public int StatementId { get; private set; } + + public int AgentInstanceId { get; private set; } + + public StatementContext OptionalStatementContext { get; private set; } + + protected bool Equals(StatementContextCPPair other) + { + return AgentInstanceId == other.AgentInstanceId && StatementId == other.StatementId; + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) + return false; + if (ReferenceEquals(this, obj)) + return true; + if (obj.GetType() != this.GetType()) + return false; + return Equals((StatementContextCPPair) obj); + } + + public override int GetHashCode() + { + unchecked + { + return (AgentInstanceId*397) ^ StatementId; + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/service/StatementContextEngineServices.cs b/NEsper.Core/NEsper.Core/core/service/StatementContextEngineServices.cs new file mode 100755 index 000000000..835326796 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/StatementContextEngineServices.cs @@ -0,0 +1,152 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.core.context.mgr; +using com.espertech.esper.epl.agg.factory; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.declexpr; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.epl.metric; +using com.espertech.esper.epl.named; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.epl.variable; +using com.espertech.esper.events; +using com.espertech.esper.events.vaevent; +using com.espertech.esper.filter; +using com.espertech.esper.pattern; +using com.espertech.esper.rowregex; +using com.espertech.esper.schedule; +using com.espertech.esper.timer; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.service +{ + public sealed class StatementContextEngineServices + { + public StatementContextEngineServices( + String engineURI, + EventAdapterService eventAdapterService, + NamedWindowMgmtService namedWindowMgmtService, + VariableService variableService, + TableService tableService, + EngineSettingsService engineSettingsService, + ValueAddEventService valueAddEventService, + ConfigurationInformation configSnapshot, + MetricReportingServiceSPI metricReportingService, + ViewService viewService, + ExceptionHandlingService exceptionHandlingService, + ExpressionResultCacheService expressionResultCacheService, + StatementEventTypeRef statementEventTypeRef, + TableExprEvaluatorContext tableExprEvaluatorContext, + EngineLevelExtensionServicesContext engineLevelExtensionServicesContext, + RegexHandlerFactory regexHandlerFactory, + StatementLockFactory statementLockFactory, + ContextManagementService contextManagementService, + ViewServicePreviousFactory viewServicePreviousFactory, + EventTableIndexService eventTableIndexService, + PatternNodeFactory patternNodeFactory, + FilterBooleanExpressionFactory filterBooleanExpressionFactory, + TimeSourceService timeSourceService, + EngineImportService engineImportService, + AggregationFactoryFactory aggregationFactoryFactory, + SchedulingService schedulingService, + ExprDeclaredService exprDeclaredService) + { + EngineURI = engineURI; + EventAdapterService = eventAdapterService; + NamedWindowMgmtService = namedWindowMgmtService; + VariableService = variableService; + TableService = tableService; + EngineSettingsService = engineSettingsService; + ValueAddEventService = valueAddEventService; + ConfigSnapshot = configSnapshot; + MetricReportingService = metricReportingService; + ViewService = viewService; + ExceptionHandlingService = exceptionHandlingService; + ExpressionResultCacheService = expressionResultCacheService; + StatementEventTypeRef = statementEventTypeRef; + TableExprEvaluatorContext = tableExprEvaluatorContext; + EngineLevelExtensionServicesContext = engineLevelExtensionServicesContext; + RegexHandlerFactory = regexHandlerFactory; + StatementLockFactory = statementLockFactory; + ContextManagementService = contextManagementService; + ViewServicePreviousFactory = viewServicePreviousFactory; + EventTableIndexService = eventTableIndexService; + PatternNodeFactory = patternNodeFactory; + FilterBooleanExpressionFactory = filterBooleanExpressionFactory; + TimeSourceService = timeSourceService; + EngineImportService = engineImportService; + AggregationFactoryFactory = aggregationFactoryFactory; + SchedulingService = schedulingService; + ExprDeclaredService = exprDeclaredService; + } + + public string EngineURI { get; private set; } + + public EventAdapterService EventAdapterService { get; private set; } + + public NamedWindowMgmtService NamedWindowMgmtService { get; private set; } + + public VariableService VariableService { get; private set; } + + public IList PlugInTypeResolutionURIs + { + get { return EngineSettingsService.PlugInEventTypeResolutionURIs; } + } + + public ValueAddEventService ValueAddEventService { get; private set; } + + public ConfigurationInformation ConfigSnapshot { get; private set; } + + public MetricReportingServiceSPI MetricReportingService { get; private set; } + + public ViewService ViewService { get; private set; } + + public ExceptionHandlingService ExceptionHandlingService { get; private set; } + + public ExpressionResultCacheService ExpressionResultCacheService { get; private set; } + + public StatementEventTypeRef StatementEventTypeRef { get; private set; } + + public TableService TableService { get; private set; } + + public EngineSettingsService EngineSettingsService { get; private set; } + + public TableExprEvaluatorContext TableExprEvaluatorContext { get; private set; } + + public EngineLevelExtensionServicesContext EngineLevelExtensionServicesContext { get; private set; } + + public RegexHandlerFactory RegexHandlerFactory { get; private set; } + + public StatementLockFactory StatementLockFactory { get; private set; } + + public ContextManagementService ContextManagementService { get; private set; } + + public ViewServicePreviousFactory ViewServicePreviousFactory { get; private set; } + + public EventTableIndexService EventTableIndexService { get; private set; } + + public PatternNodeFactory PatternNodeFactory { get; private set; } + + public FilterBooleanExpressionFactory FilterBooleanExpressionFactory { get; private set; } + + public TimeSourceService TimeSourceService { get; private set; } + + public EngineImportService EngineImportService { get; private set; } + + public AggregationFactoryFactory AggregationFactoryFactory { get; private set; } + + public SchedulingService SchedulingService { get; private set; } + + public ExprDeclaredService ExprDeclaredService { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/service/StatementContextFactory.cs b/NEsper.Core/NEsper.Core/core/service/StatementContextFactory.cs new file mode 100755 index 000000000..2008ae6ca --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/StatementContextFactory.cs @@ -0,0 +1,60 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.epl.expression.subquery; +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.core.service +{ + /// + /// Interface for a factory class that makes statement context specific to a statement. + /// + public interface StatementContextFactory + { + EPServicesContext StmtEngineServices { set; } + + /// + /// Create a new statement context consisting of statement-level services. + /// + /// is the statement is + /// is the statement name + /// is the statement expression + /// Type of the statement. + /// is engine services + /// addtional context to pass to the statement + /// if the statement context is for a fire-and-forget statement + /// The annotations. + /// for isolation units + /// if set to true [stateless]. + /// The statement spec raw. + /// The subselect nodes. + /// if set to true [write to tables]. + /// The statement user object. + /// + /// statement context + /// + StatementContext MakeContext( + int statementId, + string statementName, + string expression, + StatementType statementType, + EPServicesContext engineServices, + IDictionary optAdditionalContext, + bool isFireAndForget, + Attribute[] annotations, + EPIsolationUnitServices isolationUnitServices, + bool stateless, + StatementSpecRaw statementSpecRaw, + IList subselectNodes, + bool writeToTables, + object statementUserObject); + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/StatementContextFactoryDefault.cs b/NEsper.Core/NEsper.Core/core/service/StatementContextFactoryDefault.cs new file mode 100755 index 000000000..045c12eed --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/StatementContextFactoryDefault.cs @@ -0,0 +1,340 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.client.annotation; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.threading; +using com.espertech.esper.core.context.mgr; +using com.espertech.esper.core.context.stmt; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service.resource; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.subquery; +using com.espertech.esper.epl.metric; +using com.espertech.esper.epl.script; +using com.espertech.esper.epl.spec; +using com.espertech.esper.pattern; +using com.espertech.esper.pattern.pool; +using com.espertech.esper.rowregex; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.service +{ + /// + /// Default implementation for making a statement-specific context class. + /// + public class StatementContextFactoryDefault : StatementContextFactory + { + private readonly PluggableObjectRegistryImpl _viewRegistry; + private readonly PluggableObjectCollection _patternObjectClasses; + private readonly Type _systemVirtualDwViewFactory; + + private StatementContextEngineServices _stmtEngineServices; + + /// + /// Ctor. + /// + /// is the view plug-in object descriptions + /// is the pattern plug-in object descriptions + /// virtual DW factory + public StatementContextFactoryDefault( + PluggableObjectCollection viewPlugIns, + PluggableObjectCollection plugInPatternObj, + Type systemVirtualDWViewFactory) + { + _viewRegistry = new PluggableObjectRegistryImpl(new PluggableObjectCollection[]{ViewEnumHelper.BuiltinViews, viewPlugIns}); + + _systemVirtualDwViewFactory = systemVirtualDWViewFactory; + + _patternObjectClasses = new PluggableObjectCollection(); + _patternObjectClasses.AddObjects(plugInPatternObj); + _patternObjectClasses.AddObjects(PatternObjectHelper.BuiltinPatternObjects); + } + + public static StatementContextEngineServices GetStmtCtxEngineServices(EPServicesContext services) + { + return new StatementContextEngineServices( + services.EngineURI, + services.EventAdapterService, + services.NamedWindowMgmtService, + services.VariableService, + services.TableService, + services.EngineSettingsService, + services.ValueAddEventService, + services.ConfigSnapshot, + services.MetricsReportingService, + services.ViewService, + services.ExceptionHandlingService, + services.ExpressionResultCacheSharable, + services.StatementEventTypeRefService, + services.TableService.TableExprEvaluatorContext, + services.EngineLevelExtensionServicesContext, + services.RegexHandlerFactory, + services.StatementLockFactory, + services.ContextManagementService, + services.ViewServicePreviousFactory, + services.EventTableIndexService, + services.PatternNodeFactory, + services.FilterBooleanExpressionFactory, + services.TimeSource, + services.EngineImportService, + services.AggregationFactoryFactory, + services.SchedulingService, + services.ExprDeclaredService + ); + } + + public EPServicesContext StmtEngineServices + { + set { _stmtEngineServices = GetStmtCtxEngineServices(value); } + } + + public StatementContext MakeContext( + int statementId, + string statementName, + string expression, + StatementType statementType, + EPServicesContext engineServices, + IDictionary optAdditionalContext, + bool isFireAndForget, + Attribute[] annotations, + EPIsolationUnitServices isolationUnitServices, + bool stateless, + StatementSpecRaw statementSpecRaw, + IList subselectNodes, + bool writesToTables, + Object statementUserObject) + { + // Allocate the statement's schedule bucket which stays constant over it's lifetime. + // The bucket allows callbacks for the same time to be ordered (within and across statements) and thus deterministic. + var scheduleBucket = engineServices.SchedulingMgmtService.AllocateBucket(); + + // Create a lock for the statement + IReaderWriterLock defaultStatementAgentInstanceLock; + + // For on-delete statements, use the create-named-window statement lock + var optCreateWindowDesc = statementSpecRaw.CreateWindowDesc; + var optOnTriggerDesc = statementSpecRaw.OnTriggerDesc; + if ((optOnTriggerDesc != null) && (optOnTriggerDesc is OnTriggerWindowDesc)) + { + var windowName = ((OnTriggerWindowDesc) optOnTriggerDesc).WindowName; + if (engineServices.TableService.GetTableMetadata(windowName) == null) { + defaultStatementAgentInstanceLock = engineServices.NamedWindowMgmtService.GetNamedWindowLock(windowName); + if (defaultStatementAgentInstanceLock == null) { + throw new EPStatementException("Named window or table '" + windowName + "' has not been declared", expression); + } + } else { + defaultStatementAgentInstanceLock = engineServices.StatementLockFactory.GetStatementLock(statementName, annotations, stateless); + } + } else if (optCreateWindowDesc != null) { + // For creating a named window, save the lock for use with on-delete/on-merge/on-update etc. statements + defaultStatementAgentInstanceLock = engineServices.NamedWindowMgmtService.GetNamedWindowLock(optCreateWindowDesc.WindowName); + if (defaultStatementAgentInstanceLock == null) { + defaultStatementAgentInstanceLock = engineServices.StatementLockFactory.GetStatementLock(statementName, annotations, false); + engineServices.NamedWindowMgmtService.AddNamedWindowLock(optCreateWindowDesc.WindowName, defaultStatementAgentInstanceLock, statementName); + } + } else { + defaultStatementAgentInstanceLock = engineServices.StatementLockFactory.GetStatementLock(statementName, annotations, stateless); + } + + StatementMetricHandle stmtMetric = null; + if (!isFireAndForget) { + stmtMetric = engineServices.MetricsReportingService.GetStatementHandle(statementId, statementName); + } + + var annotationData = AnnotationAnalysisResult.AnalyzeAnnotations(annotations); + var hasVariables = statementSpecRaw.HasVariables || (statementSpecRaw.CreateContextDesc != null); + var hasTableAccess = StatementContextFactoryUtil.DetermineHasTableAccess(subselectNodes, statementSpecRaw, engineServices); + var epStatementHandle = new EPStatementHandle( + statementId, statementName, expression, statementType, expression, + hasVariables, stmtMetric, + annotationData.Priority, + annotationData.IsPremptive, + hasTableAccess, + engineServices.MultiMatchHandlerFactory.GetDefaultHandler()); + + var patternContextFactory = new PatternContextFactoryDefault(); + + var optionalCreateNamedWindowName = statementSpecRaw.CreateWindowDesc != null ? statementSpecRaw.CreateWindowDesc.WindowName : null; + var viewResolutionService = new ViewResolutionServiceImpl(_viewRegistry, optionalCreateNamedWindowName, _systemVirtualDwViewFactory); + var patternResolutionService = new PatternObjectResolutionServiceImpl(_patternObjectClasses); + + var schedulingService = engineServices.SchedulingService; + var filterService = engineServices.FilterService; + if (isolationUnitServices != null) { + filterService = isolationUnitServices.FilterService; + schedulingService = isolationUnitServices.SchedulingService; + } + + var scheduleAudit = AuditEnum.SCHEDULE.GetAudit(annotations); + if (scheduleAudit != null) { + schedulingService = new SchedulingServiceAudit(engineServices.EngineURI, statementName, schedulingService); + } + + StatementAIResourceRegistry statementAgentInstanceRegistry = null; + ContextDescriptor contextDescriptor = null; + var optionalContextName = statementSpecRaw.OptionalContextName; + if (optionalContextName != null) { + contextDescriptor = engineServices.ContextManagementService.GetContextDescriptor(optionalContextName); + + // allocate a per-instance registry of aggregations and prev/prior/subselect + if (contextDescriptor != null) { + statementAgentInstanceRegistry = contextDescriptor.AiResourceRegistryFactory.Invoke(); + } + } + + var countSubexpressions = engineServices.ConfigSnapshot.EngineDefaults.Patterns.MaxSubexpressions != null; + PatternSubexpressionPoolStmtSvc patternSubexpressionPoolStmtSvc = null; + if (countSubexpressions) { + var stmtCounter = new PatternSubexpressionPoolStmtHandler(); + patternSubexpressionPoolStmtSvc = new PatternSubexpressionPoolStmtSvc(engineServices.PatternSubexpressionPoolSvc, stmtCounter); + engineServices.PatternSubexpressionPoolSvc.AddPatternContext(statementName, stmtCounter); + } + + var countMatchRecogStates = engineServices.ConfigSnapshot.EngineDefaults.MatchRecognize.MaxStates != null; + MatchRecognizeStatePoolStmtSvc matchRecognizeStatePoolStmtSvc = null; + if (countMatchRecogStates && statementSpecRaw.MatchRecognizeSpec != null) { + var stmtCounter = new MatchRecognizeStatePoolStmtHandler(); + matchRecognizeStatePoolStmtSvc = new MatchRecognizeStatePoolStmtSvc(engineServices.MatchRecognizeStatePoolEngineSvc, stmtCounter); + engineServices.MatchRecognizeStatePoolEngineSvc.AddPatternContext(statementName, stmtCounter); + } + + AgentInstanceScriptContext defaultAgentInstanceScriptContext = null; + if (statementSpecRaw.ScriptExpressions != null && !statementSpecRaw.ScriptExpressions.IsEmpty()) { + defaultAgentInstanceScriptContext = AgentInstanceScriptContext.From(engineServices.EventAdapterService); + } + + // allow a special context controller factory for testing + var contextControllerFactoryService = GetContextControllerFactoryService(annotations, engineServices.EngineImportService); + + // may use resource tracking + var statementResourceService = new StatementResourceService(optionalContextName != null); + var extensionSvcContext = new ProxyStatementExtensionSvcContext() { + ProcStmtResources = () => statementResourceService, + ProcExtractStatementResourceHolder = (resultOfStart) => StatementResourceHolderUtil.PopulateHolder(resultOfStart), + ProcPreStartWalk = (selectDesc) => {}, + ProcPostProcessStart = (resultOfStart, isRecoveringResilient) => { }, + ProcContributeStopCallback = (selectResult, stopCallbacks) => {} + }; + + // Create statement context + return new StatementContext( + _stmtEngineServices, + schedulingService, + scheduleBucket, + epStatementHandle, + viewResolutionService, + patternResolutionService, + extensionSvcContext, + new StatementStopServiceImpl(), + patternContextFactory, + filterService, + new StatementResultServiceImpl( + statementName, engineServices.StatementLifecycleSvc, engineServices.MetricsReportingService, + engineServices.ThreadingService), + engineServices.InternalEventEngineRouteDest, + annotations, + statementAgentInstanceRegistry, + defaultStatementAgentInstanceLock, + contextDescriptor, + patternSubexpressionPoolStmtSvc, + matchRecognizeStatePoolStmtSvc, + stateless, + contextControllerFactoryService, + defaultAgentInstanceScriptContext, + AggregationServiceFactoryServiceImpl.DEFAULT_FACTORY, + engineServices.ScriptingService, + writesToTables, + statementUserObject, + StatementSemiAnonymousTypeRegistryImpl.INSTANCE, + annotationData.Priority); + } + + private ContextControllerFactoryService GetContextControllerFactoryService( + Attribute[] annotations, + EngineImportService engineImportService) + { + try + { + var replacementCache = (ContextStateCache) TypeHelper.GetAnnotationHook( + annotations, HookType.CONTEXT_STATE_CACHE, typeof (ContextStateCache), engineImportService); + if (replacementCache != null) + { + return new ContextControllerFactoryServiceImpl(replacementCache); + } + } + catch (ExprValidationException e) + { + throw new EPException("Failed to obtain hook for " + HookType.CONTEXT_STATE_CACHE); + } + return ContextControllerFactoryServiceImpl.DEFAULT_FACTORY; + } + + /// Analysis result of analysing annotations for a statement. + public class AnnotationAnalysisResult + { + /// + /// Ctor. + /// + /// priority + /// preemptive indicator + private AnnotationAnalysisResult(int priority, bool premptive) { + this.Priority = priority; + IsPremptive = premptive; + } + + /// + /// Analyze the annotations and return priority and drop settings. + /// + /// to analyze + /// analysis result + public static AnnotationAnalysisResult AnalyzeAnnotations(Attribute[] annotations) + { + var preemptive = false; + var priority = 0; + var hasPrioritySetting = false; + foreach (var annotation in annotations) + { + if (annotation is PriorityAttribute) + { + priority = ((PriorityAttribute) annotation).Value; + hasPrioritySetting = true; + } + if (annotation is DropAttribute) + { + preemptive = true; + } + } + if (!hasPrioritySetting && preemptive) + { + priority = 1; + } + return new AnnotationAnalysisResult(priority, preemptive); + } + + /// + /// Returns execution priority. + /// + /// priority. + public int Priority { get; private set; } + + /// + /// Returns preemptive indicator (drop or normal). + /// + /// true for drop + public bool IsPremptive { get; private set; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/service/StatementContextFactoryUtil.cs b/NEsper.Core/NEsper.Core/core/service/StatementContextFactoryUtil.cs new file mode 100755 index 000000000..e1f82850a --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/StatementContextFactoryUtil.cs @@ -0,0 +1,58 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.subquery; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.table.mgmt; + +namespace com.espertech.esper.core.service +{ + public class StatementContextFactoryUtil + { + public static bool DetermineHasTableAccess(IList subselectNodes, StatementSpecRaw statementSpecRaw, EPServicesContext engineServices) { + var hasTableAccess = (statementSpecRaw.TableExpressions != null && !statementSpecRaw.TableExpressions.IsEmpty()) || + statementSpecRaw.IntoTableSpec != null; + hasTableAccess = hasTableAccess || IsJoinWithTable(statementSpecRaw, engineServices.TableService) || IsSubqueryWithTable(subselectNodes, engineServices.TableService) || IsInsertIntoTable(statementSpecRaw, engineServices.TableService); + return hasTableAccess; + } + + private static bool IsInsertIntoTable(StatementSpecRaw statementSpecRaw, TableService tableService) { + if (statementSpecRaw.InsertIntoDesc == null) { + return false; + } + return tableService.GetTableMetadata(statementSpecRaw.InsertIntoDesc.EventTypeName) != null; + } + + private static bool IsSubqueryWithTable(IList subselectNodes, TableService tableService) { + foreach (var node in subselectNodes) { + var spec = (FilterStreamSpecRaw) node.StatementSpecRaw.StreamSpecs[0]; + if (tableService.GetTableMetadata(spec.RawFilterSpec.EventTypeName) != null) { + return true; + } + } + return false; + } + + private static bool IsJoinWithTable(StatementSpecRaw statementSpecRaw, TableService tableService) { + foreach (var stream in statementSpecRaw.StreamSpecs) { + if (stream is FilterStreamSpecRaw) { + var filter = (FilterStreamSpecRaw) stream; + if (tableService.GetTableMetadata(filter.RawFilterSpec.EventTypeName) != null) { + return true; + } + } + } + return false; + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/StatementEventDispatcherUnthreaded.cs b/NEsper.Core/NEsper.Core/core/service/StatementEventDispatcherUnthreaded.cs new file mode 100755 index 000000000..071fb2028 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/StatementEventDispatcherUnthreaded.cs @@ -0,0 +1,76 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.compat.logging; + +namespace com.espertech.esper.core.service +{ + /// + /// Dispatcher for statement lifecycle events to service provider statement state listeners. + /// + public class StatementEventDispatcherUnthreaded + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly EPServiceProvider _serviceProvider; + private readonly IEnumerable _statementListeners; + + /// Ctor. + /// engine instance + /// listeners to dispatch to + public StatementEventDispatcherUnthreaded(EPServiceProvider serviceProvider, + IEnumerable statementListeners) + { + _serviceProvider = serviceProvider; + _statementListeners = statementListeners; + } + + #region StatementLifecycleObserver Members + + public void Observe(StatementLifecycleEvent theEvent) + { + if (theEvent.EventType == StatementLifecycleEvent.LifecycleEventType.CREATE) + { + IEnumerator it = _statementListeners.GetEnumerator(); + for (; it.MoveNext(); ) + { + try + { + it.Current.OnStatementCreate(_serviceProvider, theEvent.Statement); + } + catch (Exception ex) + { + Log.Error("Caught runtime exception in onStatementCreate callback:" + ex.Message, ex); + } + } + } + else if (theEvent.EventType == StatementLifecycleEvent.LifecycleEventType.STATECHANGE) + { + IEnumerator it = _statementListeners.GetEnumerator(); + for (; it.MoveNext(); ) + { + try + { + it.Current.OnStatementStateChange(_serviceProvider, theEvent.Statement); + } + catch (Exception ex) + { + Log.Error("Caught runtime exception in onStatementCreate callback:" + ex.Message, ex); + } + } + } + } + + #endregion + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/service/StatementEventTypeRef.cs b/NEsper.Core/NEsper.Core/core/service/StatementEventTypeRef.cs new file mode 100755 index 000000000..c305eba23 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/StatementEventTypeRef.cs @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.core.service +{ + /// Service for maintaining references between statement name and event type. + public interface StatementEventTypeRef + { + /// Returns true if the event type is listed as in-use by any statement, or false if not + /// name + /// indicator whether type is in use + bool IsInUse(String eventTypeName); + + /// Returns the set of event types that are use by a given statement name. + /// name + /// set of event types or empty set if none found + String[] GetTypesForStatementName(String statementName); + + /// Returns the set of statement names that use a given event type name. + /// name + /// set of statements or null if none found + ICollection GetStatementNamesForType(String eventTypeName); + + /// Add a reference from a statement name to a set of event types. + /// name of statement + /// types + void AddReferences(String statementName, String[] eventTypesReferenced); + + /// Remove all references for a given statement. + /// statement name + void RemoveReferencesStatement(String statementName); + + /// Remove all references for a given event type. + /// event type name + void RemoveReferencesType(String eventTypeName); + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/StatementEventTypeRefImpl.cs b/NEsper.Core/NEsper.Core/core/service/StatementEventTypeRefImpl.cs new file mode 100755 index 000000000..655828de7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/StatementEventTypeRefImpl.cs @@ -0,0 +1,201 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.compat.threading; +using com.espertech.esper.util; + +namespace com.espertech.esper.core.service +{ + /// + /// Service for holding references between statements and their event type use. + /// + public class StatementEventTypeRefImpl : StatementEventTypeRef + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly IReaderWriterLock _mapLock; + private readonly Dictionary> _typeToStmt; + private readonly Dictionary _stmtToType; + + /// Ctor. + public StatementEventTypeRefImpl() + { + _typeToStmt = new Dictionary>(); + _stmtToType = new Dictionary(); + _mapLock = ReaderWriterLockManager.CreateLock(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + } + + public void AddReferences(String statementName, String[] eventTypesReferenced) + { + if (eventTypesReferenced.Length == 0) + { + return; + } + + using (_mapLock.AcquireWriteLock()) + { + foreach (String reference in eventTypesReferenced) + { + AddReference(statementName, reference); + } + } + } + + public void RemoveReferencesStatement(String statementName) + { + using (_mapLock.AcquireWriteLock()) + { + var types = _stmtToType.Delete(statementName); + if (types != null) + { + foreach (String type in types) + { + RemoveReference(statementName, type); + } + } + } + } + + public void RemoveReferencesType(String name) + { + using (_mapLock.AcquireWriteLock()) + { + var statementNames = _typeToStmt.Delete(name); + if (statementNames != null) + { + foreach (String statementName in statementNames) + { + RemoveReference(statementName, name); + } + } + } + } + + public bool IsInUse(String eventTypeName) + { + using (_mapLock.AcquireReadLock()) + { + return _typeToStmt.ContainsKey(eventTypeName); + } + } + + public ICollection GetStatementNamesForType(String eventTypeName) + { + using (_mapLock.AcquireReadLock()) + { + var types = _typeToStmt.Get(eventTypeName); + if (types == null) + { + return Collections.GetEmptySet(); + } + return types.AsReadOnlyCollection(); + } + } + + public String[] GetTypesForStatementName(String statementName) + { + using (_mapLock.AcquireReadLock()) + { + var types = _stmtToType.Get(statementName); + if (types == null) + { + return new String[0]; + } + return types; + } + } + + private void AddReference(String statementName, String eventTypeName) + { + // add to types + var statements = _typeToStmt.Get(eventTypeName); + if (statements == null) + { + statements = new HashSet(); + _typeToStmt.Put(eventTypeName, statements); + } + statements.Add(statementName); + + // add to statements + String[] types = _stmtToType.Get(statementName); + if (types == null) + { + types = new String[]{ eventTypeName }; + } + else + { + int index = CollectionUtil.FindItem(types, eventTypeName); + if (index == -1) + { + types = (String[])CollectionUtil.ArrayExpandAddSingle(types, eventTypeName); + } + } + _stmtToType.Put(statementName, types); + } + + private void RemoveReference(String statementName, String eventTypeName) + { + // remove from types + ICollection statements = _typeToStmt.Get(eventTypeName); + if (statements != null) + { + if (!statements.Remove(statementName)) + { + Log.Info("Failed to find statement name '" + statementName + "' in collection"); + } + + if (statements.IsEmpty()) + { + _typeToStmt.Remove(eventTypeName); + } + } + + // remove from statements + String[] types = _stmtToType.Get(statementName); + if (types != null) + { + int index = CollectionUtil.FindItem(types, eventTypeName); + if (index != -1) + { + if (types.Length == 1) + { + _stmtToType.Remove(statementName); + } + else + { + types = (String[])CollectionUtil.ArrayShrinkRemoveSingle(types, index); + _stmtToType.Put(statementName, types); + } + } + else + { + Log.Info("Failed to find type name '" + eventTypeName + "' in collection"); + } + } + } + + /// For testing, returns the mapping of event type name to statement names. + /// mapping + public Dictionary> TypeToStmt + { + get { return _typeToStmt; } + } + + /// For testing, returns the mapping of statement names to event type names. + /// mapping + public Dictionary StmtToType + { + get { return _stmtToType; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/StatementExtensionSvcContext.cs b/NEsper.Core/NEsper.Core/core/service/StatementExtensionSvcContext.cs new file mode 100755 index 000000000..c7eb333ed --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/StatementExtensionSvcContext.cs @@ -0,0 +1,75 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.core.context.factory; +using com.espertech.esper.core.service.resource; +using com.espertech.esper.core.start; +using com.espertech.esper.util; + +namespace com.espertech.esper.core.service +{ + /// + /// Statement-level extension services. + /// + public interface StatementExtensionSvcContext + { + StatementResourceService StmtResources { get; } + StatementResourceHolder ExtractStatementResourceHolder(StatementAgentInstanceFactoryResult resultOfStart); + void PreStartWalk(EPStatementStartMethodSelectDesc selectDesc); + void PostProcessStart(StatementAgentInstanceFactoryResult resultOfStart, bool isRecoveringResilient); + void ContributeStopCallback(StatementAgentInstanceFactoryResult selectResult, IList stopCallbacks); + } + + public class ProxyStatementExtensionSvcContext : StatementExtensionSvcContext + { + public Func ProcStmtResources { get; set; } + public Func ProcExtractStatementResourceHolder { get; set; } + public Action ProcPreStartWalk { get; set; } + public Action ProcPostProcessStart { get; set; } + public Action> ProcContributeStopCallback { get; set; } + + public StatementResourceService StmtResources + { + get { return ProcStmtResources.Invoke(); } + } + + public StatementResourceHolder ExtractStatementResourceHolder(StatementAgentInstanceFactoryResult resultOfStart) + { + return ProcExtractStatementResourceHolder.Invoke(resultOfStart); + } + + public void PreStartWalk(EPStatementStartMethodSelectDesc selectDesc) + { + if (ProcPreStartWalk != null) + { + ProcPreStartWalk.Invoke(selectDesc); + } + } + + public void PostProcessStart(StatementAgentInstanceFactoryResult resultOfStart, bool isRecoveringResilient) + { + if (ProcPostProcessStart != null) + { + ProcPostProcessStart.Invoke(resultOfStart, isRecoveringResilient); + } + } + + public void ContributeStopCallback( + StatementAgentInstanceFactoryResult selectResult, + IList stopCallbacks) + { + if (ProcContributeStopCallback != null) + { + ProcContributeStopCallback.Invoke(selectResult, stopCallbacks); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/StatementIsolationService.cs b/NEsper.Core/NEsper.Core/core/service/StatementIsolationService.cs new file mode 100755 index 000000000..6c328b5a1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/StatementIsolationService.cs @@ -0,0 +1,71 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; + +namespace com.espertech.esper.core.service +{ + /// Service for managing statement isolation. + public interface StatementIsolationService : IDisposable + { + /// Returns an isolated service by names, or allocates a new one if none found. + /// isolated service + /// the unique id assigned to the isolation unit + /// isolated service provider + EPServiceProviderIsolated GetIsolationUnit(string name, int? optionalUnitId); + + /// Returns all names or currently known isolation services. + /// names + string[] IsolationUnitNames { get; } + + /// Indicates statements are moved to isolation. + /// isolated service provider name. + /// isolated service provider number. + /// statements moved. + void BeginIsolatingStatements(string name, int unitId, IList stmt); + + /// Indicates statements are have moved to isolation. + /// isolated service provider name. + /// isolated service provider number. + /// statements moved. + void CommitIsolatingStatements(string name, int unitId, IList stmt); + + /// Indicates statements are have not moved to isolation. + /// isolated service provider name. + /// isolated service provider number. + /// statements moved. + void RollbackIsolatingStatements(string name, int unitId, IList stmt); + + /// Indicates statements are moved out of isolation. + /// isolated service provider name. + /// isolated service provider number. + /// statements moved. + void BeginUnisolatingStatements(string name, int unitId, IList stmt); + + /// Indicates statements have been moved out of isolation. + /// isolated service provider name. + /// isolated service provider number. + /// statements moved. + void CommitUnisolatingStatements(string name, int unitId, IList stmt); + + /// Indicates statements are not moved out of isolation. + /// isolated service provider name. + /// isolated service provider number. + /// statements moved. + void RollbackUnisolatingStatements(string name, int unitId, IList stmt); + + /// Indicates a new statement created in an isolated service. + /// statement id + /// statement name + /// isolated services + void NewStatement(int stmtId, string stmtName, EPIsolationUnitServices isolatedServices); + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/StatementIsolationServiceImpl.cs b/NEsper.Core/NEsper.Core/core/service/StatementIsolationServiceImpl.cs new file mode 100755 index 000000000..57ce86e71 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/StatementIsolationServiceImpl.cs @@ -0,0 +1,144 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.filter; +using com.espertech.esper.schedule; + +namespace com.espertech.esper.core.service +{ + /// + /// Service to maintain currently active isoalted service providers for an engine. + /// + public class StatementIsolationServiceImpl : StatementIsolationService + { + private static readonly ILog Log = + LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly IDictionary _isolatedProviders; + private EPServicesContext _epServicesContext; + private volatile int currentUnitId = 0; + + /// Ctor. + public StatementIsolationServiceImpl() + { + _isolatedProviders = new ConcurrentDictionary(); + } + + /// + /// Set the engine service context. + /// + /// services context + public void SetEpServicesContext(EPServicesContext epServicesContext) + { + _epServicesContext = epServicesContext; + } + + public EPServiceProviderIsolated GetIsolationUnit(string name, int? optionalUnitId) + { + EPServiceProviderIsolatedImpl serviceProviderIsolated = _isolatedProviders.Get(name); + if (serviceProviderIsolated != null) + { + return serviceProviderIsolated; + } + + FilterServiceSPI filterService = + FilterServiceProvider.NewService( + _epServicesContext.ConfigSnapshot.EngineDefaults.Execution.FilterServiceProfile, true); + var scheduleService = new SchedulingServiceImpl(_epServicesContext.TimeSource); + var services = new EPIsolationUnitServices(name, currentUnitId, filterService, scheduleService); + serviceProviderIsolated = new EPServiceProviderIsolatedImpl( + name, services, _epServicesContext, _isolatedProviders); + _isolatedProviders.Put(name, serviceProviderIsolated); + return serviceProviderIsolated; + } + + public void Dispose() + { + _isolatedProviders.Clear(); + } + + public string[] IsolationUnitNames + { + get { return _isolatedProviders.Keys.ToArray(); } + } + + public void BeginIsolatingStatements(string name, int unitId, IList stmt) + { + if (Log.IsInfoEnabled) + { + Log.Info("Begin isolating statements " + Print(stmt) + " unit " + name + " id " + unitId); + } + } + + public void CommitIsolatingStatements(string name, int unitId, IList stmt) + { + if (Log.IsInfoEnabled) + { + Log.Info("Completed isolating statements " + Print(stmt) + " unit " + name + " id " + unitId); + } + } + + public void RollbackIsolatingStatements(string name, int unitId, IList stmt) + { + if (Log.IsInfoEnabled) + { + Log.Info("Failed isolating statements " + Print(stmt) + " unit " + name + " id " + unitId); + } + } + + public void BeginUnisolatingStatements(string name, int unitId, IList stmt) + { + if (Log.IsInfoEnabled) + { + Log.Info("Begin un-isolating statements " + Print(stmt) + " unit " + name + " id " + unitId); + } + } + + public void CommitUnisolatingStatements(string name, int unitId, IList stmt) + { + if (Log.IsInfoEnabled) + { + Log.Info("Completed un-isolating statements " + Print(stmt) + " unit " + name + " id " + unitId); + } + } + + public void RollbackUnisolatingStatements(string name, int unitId, IList stmt) + { + if (Log.IsInfoEnabled) + { + Log.Info("Failed un-isolating statements " + Print(stmt) + " unit " + name + " id " + unitId); + } + } + + public void NewStatement(int stmtId, string stmtName, EPIsolationUnitServices isolatedServices) + { + Log.Info("New statement '" + stmtName + "' unit " + isolatedServices.Name); + } + + private string Print(IEnumerable stmts) + { + var buf = new StringBuilder(); + string delimiter = ""; + foreach (EPStatement stmt in stmts) + { + buf.Append(delimiter); + buf.Append(stmt.Name); + delimiter = ", "; + } + return buf.ToString(); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/service/StatementLifecycleEvent.cs b/NEsper.Core/NEsper.Core/core/service/StatementLifecycleEvent.cs new file mode 100755 index 000000000..fbd9e2647 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/StatementLifecycleEvent.cs @@ -0,0 +1,65 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using com.espertech.esper.client; + +namespace com.espertech.esper.core.service +{ + /// + /// Event indicating statement lifecycle management. + /// + public class StatementLifecycleEvent : EventArgs + { + #region LifecycleEventType enum + + /// Event types. + public enum LifecycleEventType + { + /// Statement created. + CREATE, + /// Statement state change. + STATECHANGE, + /// listener added + LISTENER_ADD, + /// Listener removed. + LISTENER_REMOVE, + /// All listeners removed. + LISTENER_REMOVE_ALL, + /// Statement destroyed / disposed. + DISPOSED + } + + #endregion + + /// Ctor. + /// the statement + /// the type if event + /// event parameters + public StatementLifecycleEvent(EPStatement statement, + LifecycleEventType eventType, + params Object[] parameters) + { + Statement = statement; + EventType = eventType; + Parameters = parameters; + } + + /// Returns the statement instance for the event. + /// statement + public EPStatement Statement { get; private set; } + + /// Returns the event type. + /// type of event + public LifecycleEventType EventType { get; private set; } + + /// Returns event parameters. + /// params + public object[] Parameters { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/service/StatementLifecycleStmtContextResolver.cs b/NEsper.Core/NEsper.Core/core/service/StatementLifecycleStmtContextResolver.cs new file mode 100755 index 000000000..221c11efc --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/StatementLifecycleStmtContextResolver.cs @@ -0,0 +1,15 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.core.service +{ + public interface StatementLifecycleStmtContextResolver + { + StatementContext GetStatementContextById(int statementId); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/service/StatementLifecycleSvc.cs b/NEsper.Core/NEsper.Core/core/service/StatementLifecycleSvc.cs new file mode 100755 index 000000000..639338d52 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/StatementLifecycleSvc.cs @@ -0,0 +1,113 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.client.soda; +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.core.service +{ + /// + /// Handles statement management. + /// + public interface StatementLifecycleSvc + : StatementLifecycleStmtContextResolver + , IDisposable + { + /// Initialized the service before use. + void Init(); + + /// + /// Occurs when there is a corresponding lifecycle event. + /// + event EventHandler LifecycleEvent; + + /// + /// Dispatch event to observers. + /// + /// to dispatch + void DispatchStatementLifecycleEvent(StatementLifecycleEvent theEvent); + + /// + /// Create and start the statement. + /// + /// is the statement definition in bean object form, raw unvalidated and unoptimized. + /// is the expression text + /// is an indicator on whether this is a pattern statement and thus the iterator must return the last result,versus for non-pattern statements the iterator returns view content. + /// is an optional statement name, null if none was supplied + /// the application define user object associated to each statement, if supplied + /// isolated service services + /// The statement id. + /// The optional model. + /// started statement + EPStatement CreateAndStart( + StatementSpecRaw statementSpec, + string expression, + bool isPattern, + string optStatementName, + object userObject, + EPIsolationUnitServices isolationUnitServices, + int? optionalStatementId, + EPStatementObjectModel optionalModel); + + /// Start statement by statement id. + /// of the statement to start. + void Start(int statementId); + + /// Stop statement by statement id. + /// of the statement to stop. + void Stop(int statementId); + + /// Dispose statement by statement id. + /// statementId of the statement to destroy + void Dispose(int statementId); + + /// Returns the statement by the given name, or null if no such statement exists. + /// is the statement name + /// statement for the given name, or null if no such statement existed + EPStatement GetStatementByName(string name); + + /// + /// Returns an array of statement names. If no statement has been created, an empty array is returned. + /// + /// Only returns started and stopped statements. + /// statement names + string[] StatementNames { get; } + + /// Starts all stopped statements. First statement to fail supplies the exception. + /// EPException to indicate a start error. + void StartAllStatements(); + + /// Stops all started statements. First statement to fail supplies the exception. + /// EPException to indicate a start error. + void StopAllStatements(); + + /// Destroys all started statements. First statement to fail supplies the exception. + /// EPException to indicate a start error. + void DestroyAllStatements(); + + /// + /// Statements indicate that listeners have been added through this method. + /// + /// is the statement for which listeners were added + /// is the set of listeners after adding the new listener + /// if set to true [is recovery]. + void UpdatedListeners(EPStatement stmt, EPStatementListenerSet listeners, bool isRecovery); + + string GetStatementNameById(int id); + + EPStatementSPI GetStatementById(int id); + + IDictionary StmtNameToStmt { get; } + + StatementSpecCompiled GetStatementSpec(int statementName); + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/StatementLifecycleSvcImpl.cs b/NEsper.Core/NEsper.Core/core/service/StatementLifecycleSvcImpl.cs new file mode 100755 index 000000000..52f9893fd --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/StatementLifecycleSvcImpl.cs @@ -0,0 +1,1923 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.client.annotation; +using com.espertech.esper.client.hook; +using com.espertech.esper.client.soda; +using com.espertech.esper.client.util; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.compat.threading; +using com.espertech.esper.core.service.multimatch; +using com.espertech.esper.core.start; +using com.espertech.esper.epl.agg.rollup; +using com.espertech.esper.epl.annotation; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.declexpr; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.dot; +using com.espertech.esper.epl.expression.ops; +using com.espertech.esper.epl.expression.subquery; +using com.espertech.esper.epl.expression.table; +using com.espertech.esper.epl.expression.visitor; +using com.espertech.esper.epl.named; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.spec.util; +using com.espertech.esper.events; +using com.espertech.esper.events.arr; +using com.espertech.esper.events.avro; +using com.espertech.esper.events.bean; +using com.espertech.esper.events.map; +using com.espertech.esper.filter; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.pattern; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.service +{ + /// + /// Provides statement lifecycle services. + /// + public class StatementLifecycleSvcImpl : StatementLifecycleSvc + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// Services context for statement lifecycle management. + /// + internal readonly EPServicesContext Services; + + /// + /// Maps of statement id to descriptor. + /// + internal readonly IDictionary StmtIdToDescMap; + + /// + /// Map of statement name to statement. + /// + internal readonly IDictionary StmtNameToStmtMap; + + private readonly EPServiceProviderSPI _epServiceProvider; + private readonly IReaderWriterLock _eventProcessingRWLock; + + private readonly IDictionary _stmtNameToIdMap; + + public event EventHandler LifecycleEvent; + + private int _lastStatementId; + + private readonly ILockable _lock = LockManager.CreateLock(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// Ctor. + /// + /// is the engine instance to hand to statement-aware listeners + /// is engine services + public StatementLifecycleSvcImpl(EPServiceProvider epServiceProvider, EPServicesContext services) + { + Services = services; + _epServiceProvider = (EPServiceProviderSPI)epServiceProvider; + + // lock for starting and stopping statements + _eventProcessingRWLock = services.EventProcessingRWLock; + + StmtIdToDescMap = new Dictionary(); + StmtNameToStmtMap = new Dictionary(); + _stmtNameToIdMap = new LinkedHashMap(); + } + + public void Dispose() + { + DestroyAllStatements(); + } + + public void Init() + { + // called after services are activated, to begin statement loading from store + } + + public IDictionary StmtNameToStmt + { + get { return StmtNameToStmtMap; } + } + + public EPStatement CreateAndStart( + StatementSpecRaw statementSpec, + string expression, + bool isPattern, + string optStatementName, + object userObject, + EPIsolationUnitServices isolationUnitServices, + int? optionalStatementId, + EPStatementObjectModel optionalModel) + { + using (_lock.Acquire()) + { + var assignedStatementId = optionalStatementId; + if (assignedStatementId == null) + { + do + { + _lastStatementId++; + assignedStatementId = _lastStatementId; + } while (StmtIdToDescMap.ContainsKey(assignedStatementId.Value)); + } + + var desc = CreateStoppedAssignName( + statementSpec, expression, isPattern, optStatementName, assignedStatementId.Value, null, userObject, + isolationUnitServices, optionalModel); + Start(assignedStatementId.Value, desc, true, false, false); + return desc.EpStatement; + } + } + + /// + /// Creates and starts statement. + /// + /// defines the statement + /// is the EPL + /// is true for patterns + /// is the optional statement name + /// is the statement id + /// additional context for use by the statement context + /// the application define user object associated to each statement, if supplied + /// isolated service services + /// The optional model. + /// + /// started statement + /// + protected internal EPStatementDesc CreateStoppedAssignName( + StatementSpecRaw statementSpec, + string expression, + bool isPattern, + string optStatementName, + int statementId, + IDictionary optAdditionalContext, + object userObject, + EPIsolationUnitServices isolationUnitServices, + EPStatementObjectModel optionalModel) + { + using (_lock.Acquire()) + { + var nameProvided = false; + var statementName = "stmt_" + statementId; + + // compile annotations, can produce a null array + var annotations = AnnotationUtil.CompileAnnotations(statementSpec.Annotations, Services.EngineImportService, expression); + + // find name annotation + if (optStatementName == null) + { + if (annotations != null && annotations.Length != 0) + { + foreach (var annotation in annotations) + { + if (annotation is NameAttribute) + { + var name = (NameAttribute)annotation; + if (name.Value != null) + { + optStatementName = name.Value; + } + } + } + } + } + + // Determine a statement name, i.e. use the id or use/generate one for the name passed in + if (optStatementName != null) + { + optStatementName = optStatementName.Trim(); + statementName = GetUniqueStatementName(optStatementName, statementId); + nameProvided = true; + } + + if (statementSpec.FireAndForgetSpec != null) + { + throw new EPStatementException("Provided EPL expression is an on-demand query expression (not a continuous query), please use the runtime executeQuery API instead", expression); + } + + return CreateStopped(statementSpec, annotations, expression, isPattern, statementName, nameProvided, statementId, optAdditionalContext, userObject, isolationUnitServices, false, optionalModel); + } + } + + /// + /// Create stopped statement. + /// + /// statement definition + /// The annotations. + /// is the expression text + /// is true for patterns, false for non-patterns + /// is the statement name assigned or given + /// true when an explicit statement name is provided + /// is the statement id + /// additional context for use by the statement context + /// the application define user object associated to each statement, if supplied + /// isolated service services + /// to start the statement in failed state + /// The optional model. + /// + /// stopped statement + /// + /// + protected internal EPStatementDesc CreateStopped( + StatementSpecRaw statementSpec, + Attribute[] annotations, + string expression, + bool isPattern, + string statementName, + bool nameProvided, + int statementId, + IDictionary optAdditionalContext, + object statementUserObject, + EPIsolationUnitServices isolationUnitServices, + bool isFailed, + EPStatementObjectModel optionalModel) + { + using (_lock.Acquire()) + { + EPStatementDesc statementDesc; + EPStatementStartMethod startMethod; + + // Hint annotations are often driven by variables + if (annotations != null) + { + foreach (var annotation in annotations) + { + if (annotation is HintAttribute) + { + statementSpec.HasVariables = true; + } + } + } + + // walk subselects, alias expressions, declared expressions, dot-expressions + ExprNodeSubselectDeclaredDotVisitor visitor; + try + { + visitor = StatementSpecRawAnalyzer.WalkSubselectAndDeclaredDotExpr(statementSpec); + } + catch (ExprValidationException ex) + { + throw new EPStatementException(ex.Message, expression); + } + + // Determine table access nodes + var tableAccessNodes = DetermineTableAccessNodes(statementSpec.TableExpressions, visitor); + + if (statementSpec.TableExpressions != null) + { + tableAccessNodes.AddAll(statementSpec.TableExpressions); + } + if (visitor.DeclaredExpressions != null) + { + var tableAccessVisitor = new ExprNodeTableAccessVisitor(tableAccessNodes); + foreach (var declared in visitor.DeclaredExpressions) + { + declared.Body.Accept(tableAccessVisitor); + } + } + foreach (var subselectNode in visitor.Subselects) + { + if (subselectNode.StatementSpecRaw.TableExpressions != null) + { + tableAccessNodes.AddAll(subselectNode.StatementSpecRaw.TableExpressions); + } + } + + // Determine Subselects for compilation, and lambda-expression shortcut syntax for named windows + var subselectNodes = visitor.Subselects; + if (!visitor.ChainedExpressionsDot.IsEmpty()) + { + RewriteNamedWindowSubselect(visitor.ChainedExpressionsDot, subselectNodes, Services.NamedWindowMgmtService); + } + + // compile foreign scripts + ValidateScripts(expression, statementSpec.ScriptExpressions, statementSpec.ExpressionDeclDesc); + + // Determine statement type + var statementType = StatementMetadataFactoryDefault.GetStatementType(statementSpec, isPattern); + + // Determine stateless statement + var stateless = DetermineStatelessSelect(statementType, statementSpec, !subselectNodes.IsEmpty(), isPattern); + + // Determine table use + var writesToTables = StatementLifecycleSvcUtil.IsWritesToTables(statementSpec, Services.TableService); + + // Make context + var statementContext = Services.StatementContextFactory.MakeContext(statementId, statementName, expression, statementType, Services, optAdditionalContext, false, annotations, isolationUnitServices, stateless, statementSpec, subselectNodes, writesToTables, statementUserObject); + + StatementSpecCompiled compiledSpec; + try + { + compiledSpec = Compile(statementSpec, expression, statementContext, false, false, annotations, visitor.Subselects, visitor.DeclaredExpressions, tableAccessNodes, Services); + } + catch (Exception) + { + HandleRemove(statementId, statementName); + throw; + } + + // We keep a reference of the compiled spec as part of the statement context + statementContext.StatementSpecCompiled = compiledSpec; + + // For insert-into streams, create a lock taken out as soon as an event is inserted + // Makes the processing between chained statements more predictable. + if (statementSpec.InsertIntoDesc != null || statementSpec.OnTriggerDesc is OnTriggerMergeDesc) + { + string insertIntoStreamName; + if (statementSpec.InsertIntoDesc != null) + { + insertIntoStreamName = statementSpec.InsertIntoDesc.EventTypeName; + } + else + { + insertIntoStreamName = "merge"; + } + var latchFactoryNameBack = "insert_stream_B_" + insertIntoStreamName + "_" + statementName; + var latchFactoryNameFront = "insert_stream_F_" + insertIntoStreamName + "_" + statementName; + var msecTimeout = Services.EngineSettingsService.EngineSettings.Threading.InsertIntoDispatchTimeout; + var locking = Services.EngineSettingsService.EngineSettings.Threading.InsertIntoDispatchLocking; + var latchFactoryFront = new InsertIntoLatchFactory(latchFactoryNameFront, stateless, msecTimeout, locking, Services.TimeSource); + var latchFactoryBack = new InsertIntoLatchFactory(latchFactoryNameBack, stateless, msecTimeout, locking, Services.TimeSource); + statementContext.EpStatementHandle.InsertIntoFrontLatchFactory = latchFactoryFront; + statementContext.EpStatementHandle.InsertIntoBackLatchFactory = latchFactoryBack; + } + + // determine overall filters, assign the filter spec index to filter boolean expressions + var needDedup = false; + var streamAnalysis = StatementSpecCompiledAnalyzer.AnalyzeFilters(compiledSpec); + FilterSpecCompiled[] filterSpecAll = streamAnalysis.Filters.ToArray(); + NamedWindowConsumerStreamSpec[] namedWindowConsumersAll = streamAnalysis.NamedWindowConsumers.ToArray(); + compiledSpec.FilterSpecsOverall = filterSpecAll; + compiledSpec.NamedWindowConsumersAll = namedWindowConsumersAll; + foreach (var filter in filterSpecAll) + { + if (filter.Parameters.Length > 1) + { + needDedup = true; + } + StatementLifecycleSvcUtil.AssignFilterSpecIds(filter, filterSpecAll); + RegisterNonPropertyGetters(filter, statementName, Services.FilterNonPropertyRegisteryService); + } + + MultiMatchHandler multiMatchHandler; + var isSubselectPreeval = Services.EngineSettingsService.EngineSettings.Expression.IsSelfSubselectPreeval; + if (!needDedup) + { + // no dedup + if (subselectNodes.IsEmpty()) + { + multiMatchHandler = Services.MultiMatchHandlerFactory.MakeNoDedupNoSubq(); + } + else + { + if (isSubselectPreeval) + { + multiMatchHandler = Services.MultiMatchHandlerFactory.MakeNoDedupSubselectPreval(); + } + else + { + multiMatchHandler = Services.MultiMatchHandlerFactory.MakeNoDedupSubselectPosteval(); + } + } + } + else + { + // with dedup + if (subselectNodes.IsEmpty()) + { + multiMatchHandler = Services.MultiMatchHandlerFactory.MakeDedupNoSubq(); + } + else + { + multiMatchHandler = Services.MultiMatchHandlerFactory.MakeDedupSubq(isSubselectPreeval); + } + } + statementContext.EpStatementHandle.MultiMatchHandler = multiMatchHandler; + + // In a join statements if the same event type or it's deep super types are used in the join more then once, + // then this is a self-join and the statement handle must know to dispatch the results together + var canSelfJoin = IsPotentialSelfJoin(compiledSpec) || needDedup; + statementContext.EpStatementHandle.IsCanSelfJoin = canSelfJoin; + + // add statically typed event type references: those in the from clause; Dynamic (created) types collected by statement context and added on start + Services.StatementEventTypeRefService.AddReferences(statementName, compiledSpec.EventTypeReferences); + + // add variable references + Services.StatementVariableRefService.AddReferences(statementName, compiledSpec.VariableReferences, compiledSpec.TableNodes); + + // create metadata + var statementMetadata = + Services.StatementMetadataFactory.Create( + new StatementMetadataFactoryContext( + statementName, statementId, statementContext, statementSpec, expression, isPattern, + optionalModel)); + + using (_eventProcessingRWLock.AcquireWriteLock()) + { + try + { + // create statement - may fail for parser and simple validation errors + var preserveDispatchOrder = Services.EngineSettingsService.EngineSettings.Threading.IsListenerDispatchPreserveOrder + && !stateless; + var isSpinLocks = Services.EngineSettingsService.EngineSettings.Threading.ListenerDispatchLocking == ConfigurationEngineDefaults.ThreadingConfig.Locking.SPIN; + var blockingTimeout = Services.EngineSettingsService.EngineSettings.Threading.ListenerDispatchTimeout; + var timeLastStateChange = Services.SchedulingService.Time; + var statement = Services.EpStatementFactory.Make( + statementSpec.ExpressionNoAnnotations, isPattern, + Services.DispatchService, this, timeLastStateChange, preserveDispatchOrder, isSpinLocks, blockingTimeout, + Services.TimeSource, statementMetadata, statementUserObject, statementContext, isFailed, nameProvided); + statementContext.Statement = statement; + + var isInsertInto = statementSpec.InsertIntoDesc != null; + var isDistinct = statementSpec.SelectClauseSpec.IsDistinct; + var isForClause = statementSpec.ForClauseSpec != null; + statementContext.StatementResultService.SetContext(statement, _epServiceProvider, + isInsertInto, isPattern, isDistinct, isForClause, statementContext.EpStatementHandle.MetricsHandle); + + // create start method + startMethod = EPStatementStartMethodFactory.MakeStartMethod(compiledSpec); + + statementDesc = new EPStatementDesc(statement, startMethod, statementContext); + StmtIdToDescMap.Put(statementId, statementDesc); + StmtNameToStmtMap.Put(statementName, statement); + _stmtNameToIdMap.Put(statementName, statementId); + + DispatchStatementLifecycleEvent(new StatementLifecycleEvent(statement, StatementLifecycleEvent.LifecycleEventType.CREATE)); + } + catch (Exception) + { + StmtIdToDescMap.Remove(statementId); + _stmtNameToIdMap.Remove(statementName); + StmtNameToStmtMap.Remove(statementName); + throw; + } + } + + return statementDesc; + } + } + + private ISet DetermineTableAccessNodes(IEnumerable statementDirectTableAccess, ExprNodeSubselectDeclaredDotVisitor visitor) + { + ISet tableAccessNodes = new HashSet(); + if (statementDirectTableAccess != null) + { + tableAccessNodes.AddAll(statementDirectTableAccess); + } + // include all declared expression usages + var tableAccessVisitor = new ExprNodeTableAccessVisitor(tableAccessNodes); + foreach (var declared in visitor.DeclaredExpressions) + { + declared.Body.Accept(tableAccessVisitor); + } + // include all subqueries (and their declared expressions) + // This is nested as declared expressions can have more subqueries, however all subqueries are in this list. + foreach (var subselectNode in visitor.Subselects) + { + if (subselectNode.StatementSpecRaw.TableExpressions != null) + { + tableAccessNodes.AddAll(subselectNode.StatementSpecRaw.TableExpressions); + } + } + return tableAccessNodes; + } + + // All scripts get compiled/verfied - to ensure they compile (and not just when they are referred to my an expression). + private void ValidateScripts(string epl, IList scripts, ExpressionDeclDesc expressionDeclDesc) + { + if (scripts == null) + { + return; + } + try + { + ISet scriptsSet = new HashSet(); + foreach (var script in scripts) + { + ValidateScript(script); + + var key = new NameParameterCountKey(script.Name, script.ParameterNames.Count); + if (scriptsSet.Contains(key)) + { + throw new ExprValidationException(string.Format("Script name '{0}' has already been defined with the same number of parameters", script.Name)); + } + scriptsSet.Add(key); + } + + if (expressionDeclDesc != null) + { + foreach (var declItem in expressionDeclDesc.Expressions) + { + if (scriptsSet.Contains(new NameParameterCountKey(declItem.Name, 0))) + { + throw new ExprValidationException("Script name '" + declItem.Name + "' overlaps with another expression of the same name"); + } + } + } + } + catch (ExprValidationException ex) + { + throw new EPStatementException(ex.Message, ex, epl); + } + } + + private void ValidateScript(ExpressionScriptProvided script) + { + var dialect = script.OptionalDialect ?? Services.ConfigSnapshot.EngineDefaults.Scripts.DefaultDialect; + if (dialect == null) + { + throw new ExprValidationException( + string.Format("Failed to determine script dialect for script '{0}', please configure a default dialect or provide a dialect explicitly", script.Name)); + } + + // NOTE: we have to do something here + Services.ScriptingService.VerifyScript(dialect, script); + //JSR223Helper.VerifyCompileScript(script, dialect); + + if (!script.ParameterNames.IsEmpty()) + { + var parameters = new HashSet(); + foreach (var param in script.ParameterNames) + { + if (parameters.Contains(param)) + { + throw new ExprValidationException( + string.Format( + "Invalid script parameters for script '{0}', parameter '{1}' is defined more then once", + script.Name, param)); + } + parameters.Add(param); + } + } + } + + private bool IsPotentialSelfJoin(StatementSpecCompiled spec) + { + // Include create-context as nested contexts that have pattern-initiated sub-contexts may change filters during execution + if (spec.ContextDesc != null && spec.ContextDesc.ContextDetail is ContextDetailNested) + { + return true; + } + + // if order-by is specified, ans since multiple output rows may produce, ensure dispatch + if (spec.OrderByList.Length > 0) + { + return true; + } + + if (spec.StreamSpecs.OfType().Any()) + { + return true; + } + + // not a self join + if ((spec.StreamSpecs.Length <= 1) && (spec.SubSelectExpressions.Length == 0)) + { + return false; + } + + // join - determine types joined + IList filteredTypes = new List(); + + // consider subqueryes + var optSubselectTypes = PopulateSubqueryTypes(spec.SubSelectExpressions); + + var hasFilterStream = false; + foreach (var streamSpec in spec.StreamSpecs) + { + if (streamSpec is FilterStreamSpecCompiled) + { + var type = ((FilterStreamSpecCompiled)streamSpec).FilterSpec.FilterForEventType; + filteredTypes.Add(type); + hasFilterStream = true; + } + } + + if ((filteredTypes.Count == 1) && (optSubselectTypes.IsEmpty())) + { + return false; + } + + // pattern-only streams are not self-joins + if (!hasFilterStream) + { + return false; + } + + // is type overlap in filters + for (var i = 0; i < filteredTypes.Count; i++) + { + for (var j = i + 1; j < filteredTypes.Count; j++) + { + var typeOne = filteredTypes[i]; + var typeTwo = filteredTypes[j]; + if (typeOne == typeTwo) + { + return true; + } + + if (typeOne.SuperTypes != null) + { + if (typeOne.SuperTypes.Any(typeOneSuper => typeOneSuper == typeTwo)) + { + return true; + } + } + if (typeTwo.SuperTypes != null) + { + if (typeTwo.SuperTypes.Any(typeTwoSuper => typeOne == typeTwoSuper)) + { + return true; + } + } + } + } + + // analyze subselect types + if (!optSubselectTypes.IsEmpty()) + { + foreach (var typeOne in filteredTypes) + { + if (optSubselectTypes.Contains(typeOne)) + { + return true; + } + + if (typeOne.SuperTypes != null) + { + if (typeOne.SuperTypes.Any(optSubselectTypes.Contains)) + { + return true; + } + } + } + } + + return false; + } + + private ISet PopulateSubqueryTypes(ExprSubselectNode[] subSelectExpressions) + { + ISet set = null; + foreach (var subselect in subSelectExpressions) + { + foreach (var streamSpec in subselect.StatementSpecCompiled.StreamSpecs) + { + if (streamSpec is FilterStreamSpecCompiled) + { + var type = ((FilterStreamSpecCompiled)streamSpec).FilterSpec.FilterForEventType; + if (set == null) + { + set = new HashSet(); + } + set.Add(type); + } + else if (streamSpec is PatternStreamSpecCompiled) + { + var evalNodeAnalysisResult = EvalNodeUtil.RecursiveAnalyzeChildNodes(((PatternStreamSpecCompiled)streamSpec).EvalFactoryNode); + var filterNodes = evalNodeAnalysisResult.FilterNodes; + foreach (var filterNode in filterNodes) + { + if (set == null) + { + set = new HashSet(); + } + set.Add(filterNode.FilterSpec.FilterForEventType); + } + } + } + } + if (set == null) + { + return Collections.GetEmptySet(); + } + return set; + } + + public void Start(int statementId) + { + using (_lock.Acquire()) + { + if (Log.IsDebugEnabled) + { + Log.Debug(".start Starting statement " + statementId); + } + + // Acquire a lock for event processing as threads may be in the views used by the statement + // and that could conflict with the destroy of views + using (_eventProcessingRWLock.AcquireWriteLock()) + { + var desc = StmtIdToDescMap.Get(statementId); + if (desc == null) + { + throw new IllegalStateException("Cannot start statement, statement is in destroyed state"); + } + StartInternal(statementId, desc, false, false, false); + } + } + } + + /// + /// Start the given statement. + /// + /// is the statement id + /// is the cached statement info + /// indicator whether the statement is new or a stop-restart statement + /// if the statement is recovering or new + /// true if recovering a resilient stmt + public void Start(int statementId, EPStatementDesc desc, bool isNewStatement, bool isRecoveringStatement, bool isResilient) + { + if (Log.IsDebugEnabled) + { + Log.Debug(".start Starting statement " + statementId + " from desc=" + desc); + } + + // Acquire a lock for event processing as threads may be in the views used by the statement + // and that could conflict with the destroy of views + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().QEngineManagementStmtCompileStart( + Services.EngineURI, statementId, desc.EpStatement.Name, desc.EpStatement.Text, Services.SchedulingService.Time); + } + + using (_eventProcessingRWLock.AcquireWriteLock()) + { + try + { + StartInternal(statementId, desc, isNewStatement, isRecoveringStatement, isResilient); + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().QaEngineManagementStmtStarted( + Services.EngineURI, statementId, desc.EpStatement.Name, desc.EpStatement.Text, Services.SchedulingService.Time); + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AEngineManagementStmtCompileStart(true, null); } + } + catch (Exception ex) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AEngineManagementStmtCompileStart(false, ex.Message); } + throw; + } + } + } + + private void StartInternal(int statementId, EPStatementDesc desc, bool isNewStatement, bool isRecoveringStatement, bool isResilient) + { + if (Log.IsDebugEnabled) + { + Log.Debug(".startInternal Starting statement " + statementId + " from desc=" + desc); + } + + if (desc.StartMethod == null) + { + throw new IllegalStateException("Statement start method not found for id " + statementId); + } + + var statement = desc.EpStatement; + if (statement.State == EPStatementState.STARTED) + { + Log.Debug(".startInternal - Statement already started"); + return; + } + + EPStatementStartResult startResult; + try + { + // start logically + startResult = desc.StartMethod.Start(Services, desc.StatementContext, isNewStatement, isRecoveringStatement, isResilient); + + // start named window consumers + Services.NamedWindowConsumerMgmtService.Start(desc.StatementContext.StatementName); + } + catch (EPStatementException ex) + { + HandleRemove(statementId, statement.Name); + Log.Debug(".start Error starting statement", ex); + throw; + } + catch (ExprValidationException ex) + { + HandleRemove(statementId, statement.Name); + Log.Debug(".start Error starting statement", ex); + throw new EPStatementException("Error starting statement: " + ex.Message, ex, statement.Text); + } + catch (ViewProcessingException ex) + { + HandleRemove(statementId, statement.Name); + Log.Debug(".start Error starting statement", ex); + throw new EPStatementException("Error starting statement: " + ex.Message, ex, statement.Text); + } + catch (Exception ex) + { + HandleRemove(statementId, statement.Name); + Log.Debug(".start Error starting statement", ex); + throw new EPStatementException("Unexpected exception starting statement: " + ex.Message, ex, statement.Text); + } + + // hook up + var parentView = startResult.Viewable; + desc.StopMethod = startResult.StopMethod; + desc.DestroyMethod = startResult.DestroyMethod; + statement.ParentView = parentView; + var timeLastStateChange = Services.SchedulingService.Time; + statement.SetCurrentState(EPStatementState.STARTED, timeLastStateChange); + + DispatchStatementLifecycleEvent(new StatementLifecycleEvent(statement, StatementLifecycleEvent.LifecycleEventType.STATECHANGE)); + } + + private void HandleRemove(int statementId, string statementName) + { + StmtIdToDescMap.Remove(statementId); + _stmtNameToIdMap.Remove(statementName); + StmtNameToStmtMap.Remove(statementName); + Services.StatementEventTypeRefService.RemoveReferencesStatement(statementName); + Services.StatementVariableRefService.RemoveReferencesStatement(statementName); + Services.FilterNonPropertyRegisteryService.RemoveReferencesStatement(statementName); + Services.NamedWindowConsumerMgmtService.RemoveReferences(statementName); + } + + public void Stop(int statementId) + { + using (_lock.Acquire()) + { + // Acquire a lock for event processing as threads may be in the views used by the statement + // and that could conflict with the destroy of views + try + { + using (_eventProcessingRWLock.AcquireWriteLock()) + { + var desc = StmtIdToDescMap.Get(statementId); + if (desc == null) + { + throw new IllegalStateException("Cannot stop statement, statement is in destroyed state"); + } + + var statement = desc.EpStatement; + var stopMethod = desc.StopMethod; + if (stopMethod == null) + { + throw new IllegalStateException("Stop method not found for statement " + statementId); + } + + if (statement.State == EPStatementState.STOPPED) + { + Log.Debug(".startInternal - Statement already stopped"); + return; + } + + // stop named window consumers + Services.NamedWindowConsumerMgmtService.Stop(desc.StatementContext.StatementName); + + // fire the statement stop + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QEngineManagementStmtStop(EPStatementState.STOPPED, Services.EngineURI, statementId, statement.Name, statement.Text, Services.SchedulingService.Time); } + + desc.StatementContext.StatementStopService.FireStatementStopped(); + + // invoke start-provided stop method + stopMethod.Stop(); + statement.ParentView = null; + desc.StopMethod = null; + + var timeLastStateChange = Services.SchedulingService.Time; + statement.SetCurrentState(EPStatementState.STOPPED, timeLastStateChange); + + ((EPRuntimeSPI)_epServiceProvider.EPRuntime).ClearCaches(); + + DispatchStatementLifecycleEvent(new StatementLifecycleEvent(statement, StatementLifecycleEvent.LifecycleEventType.STATECHANGE)); + } + } + finally + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AEngineManagementStmtStop(); } + } + } + } + + public void Dispose(int statementId) + { + using (_lock.Acquire()) + { + // Acquire a lock for event processing as threads may be in the views used by the statement + // and that could conflict with the destroy of views + using (_eventProcessingRWLock.AcquireWriteLock()) + { + var desc = StmtIdToDescMap.Get(statementId); + if (desc == null) + { + Log.Debug(".destroy - Statement already destroyed"); + return; + } + DestroyInternal(desc); + } + } + } + + public EPStatement GetStatementByName(string name) + { + using (_lock.Acquire()) + { + return StmtNameToStmtMap.Get(name); + } + } + + public StatementSpecCompiled GetStatementSpec(int statementId) + { + using (_lock.Acquire()) + { + var desc = StmtIdToDescMap.Get(statementId); + if (desc != null) + { + return desc.StartMethod.StatementSpec; + } + return null; + } + } + + /// + /// Returns the statement given a statement id. + /// + /// is the statement id + /// statement + public EPStatementSPI GetStatementById(int statementId) + { + var statementDesc = StmtIdToDescMap.Get(statementId); + if (statementDesc == null) + { + Log.Warn("Could not locate statement descriptor for statement id '" + statementId + "'"); + return null; + } + return statementDesc.EpStatement; + } + + public StatementContext GetStatementContextById(int statementId) + { + var statementDesc = StmtIdToDescMap.Get(statementId); + if (statementDesc == null) + { + return null; + } + return statementDesc.EpStatement.StatementContext; + } + + public string[] StatementNames + { + get + { + using (_lock.Acquire()) + { + var statements = new string[StmtNameToStmtMap.Count]; + var count = 0; + foreach (string key in StmtNameToStmtMap.Keys) + { + statements[count++] = key; + } + return statements; + } + } + } + + public void StartAllStatements() + { + using (_lock.Acquire()) + { + int[] statementIds = StatementIds; + for (var i = 0; i < statementIds.Length; i++) + { + EPStatement statement = StmtIdToDescMap.Get(statementIds[i]).EpStatement; + if (statement.State == EPStatementState.STOPPED) + { + Start(statementIds[i]); + } + } + } + } + + public void StopAllStatements() + { + using (_lock.Acquire()) + { + int[] statementIds = StatementIds; + for (var i = 0; i < statementIds.Length; i++) + { + EPStatement statement = StmtIdToDescMap.Get(statementIds[i]).EpStatement; + if (statement.State == EPStatementState.STARTED) + { + Stop(statementIds[i]); + } + } + } + } + + public void DestroyAllStatements() + { + using (_lock.Acquire()) + { + // Acquire a lock for event processing as threads may be in the views used by the statement + // and that could conflict with the destroy of views + using (_eventProcessingRWLock.AcquireWriteLock()) + { + int[] statementIds = StatementIds; + foreach (var statementId in statementIds) + { + var desc = StmtIdToDescMap.Get(statementId); + if (desc == null) + { + continue; + } + + try + { + DestroyInternal(desc); + } + catch (Exception ex) + { + Services.ExceptionHandlingService.HandleException( + ex, desc.EpStatement.Name, desc.EpStatement.Text, ExceptionHandlerExceptionType.STOP, null); + } + } + } + } + } + + private int[] StatementIds + { + get + { + var statementIds = new int[_stmtNameToIdMap.Count]; + var count = 0; + foreach (int id in _stmtNameToIdMap.Values) + { + statementIds[count++] = id; + } + return statementIds; + } + } + + private string GetUniqueStatementName(string statementName, int statementId) + { + string finalStatementName; + + if (_stmtNameToIdMap.ContainsKey(statementName)) + { + var count = 0; + while (true) + { + finalStatementName = statementName + "--" + count; + if (!(_stmtNameToIdMap.ContainsKey(finalStatementName))) + { + break; + } + if (count > int.MaxValue - 2) + { + throw new EPException("Failed to establish a unique statement name"); + } + count++; + } + } + else + { + finalStatementName = statementName; + } + + _stmtNameToIdMap.Put(finalStatementName, statementId); + return finalStatementName; + } + + public string GetStatementNameById(int statementId) + { + var desc = StmtIdToDescMap.Get(statementId); + if (desc != null) + { + return desc.EpStatement.Name; + } + return null; + } + + public void UpdatedListeners(EPStatement statement, EPStatementListenerSet listeners, bool isRecovery) + { + Log.Debug(".updatedListeners No action for base implementation"); + } + + /// + /// Compiles a statement returning the compile (verified, non-serializable) form of a statement. + /// + /// is the statement specification + /// the statement to compile + /// the statement services + /// is true for subquery compilation or false for statement compile + /// if set to true [is on demand query]. + /// statement annotations + /// The subselect nodes. + /// The declared nodes. + /// The table access nodes. + /// The services context. + /// + /// compiled statement + /// + /// EPStatementException if the statement cannot be compiled + internal static StatementSpecCompiled Compile( + StatementSpecRaw spec, + string eplStatement, + StatementContext statementContext, + bool isSubquery, + bool isOnDemandQuery, + Attribute[] annotations, + IList subselectNodes, + IList declaredNodes, + ICollection tableAccessNodes, + EPServicesContext servicesContext) + { + IList compiledStreams; + ISet eventTypeReferences = new HashSet(); + + // If not using a join and not specifying a data window, make the where-clause, if present, the filter of the stream + // if selecting using filter spec, and not subquery in where clause + if ((spec.StreamSpecs.Count == 1) && + (spec.StreamSpecs[0] is FilterStreamSpecRaw) && + (spec.StreamSpecs[0].ViewSpecs.IsEmpty()) && + (spec.FilterRootNode != null) && + (spec.OnTriggerDesc == null) && + (!isSubquery) && + (!isOnDemandQuery) && + (tableAccessNodes == null || tableAccessNodes.IsEmpty())) + { + var whereClause = spec.FilterRootNode; + + var dotVisitor = new ExprNodeSubselectDeclaredDotVisitor(); + whereClause.Accept(dotVisitor); + + var disqualified = dotVisitor.Subselects.Count > 0 || HintEnum.DISABLE_WHEREEXPR_MOVETO_FILTER.GetHint(annotations) != null; + + if (!disqualified) + { + var viewResourceVisitor = new ExprNodeViewResourceVisitor(); + whereClause.Accept(viewResourceVisitor); + disqualified = viewResourceVisitor.ExprNodes.Count > 0; + } + + if (!disqualified) + { + // If an alias is provided, find all properties to ensure the alias gets removed + string alias = spec.StreamSpecs[0].OptionalStreamName; + if (alias != null) + { + var v = new ExprNodeIdentifierCollectVisitor(); + whereClause.Accept(v); + foreach (var node in v.ExprProperties) + { + if (node.StreamOrPropertyName != null && (node.StreamOrPropertyName == alias)) + { + node.StreamOrPropertyName = null; + } + } + } + + spec.FilterExprRootNode = null; + var streamSpec = (FilterStreamSpecRaw)spec.StreamSpecs[0]; + streamSpec.RawFilterSpec.FilterExpressions.Add(whereClause); + } + } + + // compile select-clause + var selectClauseCompiled = StatementLifecycleSvcUtil.CompileSelectClause(spec.SelectClauseSpec); + + // Determine subselects in filter streams, these may need special handling for locking + var visitor = new ExprNodeSubselectDeclaredDotVisitor(); + StatementLifecycleSvcUtil.WalkStreamSpecs(spec, visitor); + foreach (var subselectNode in visitor.Subselects) + { + subselectNode.IsFilterStreamSubselect = true; + } + + // Determine subselects for compilation, and lambda-expression shortcut syntax for named windows + visitor.Reset(); + GroupByClauseExpressions groupByRollupExpressions; + try + { + StatementLifecycleSvcUtil.WalkStatement(spec, visitor); + + groupByRollupExpressions = GroupByExpressionHelper.GetGroupByRollupExpressions(spec.GroupByExpressions, + spec.SelectClauseSpec, spec.HavingExprRootNode, spec.OrderByList, visitor); + + var subselects = visitor.Subselects; + if (!visitor.ChainedExpressionsDot.IsEmpty()) + { + RewriteNamedWindowSubselect(visitor.ChainedExpressionsDot, subselects, statementContext.NamedWindowMgmtService); + } + } + catch (ExprValidationException ex) + { + throw new EPStatementException(ex.Message, eplStatement); + } + + if (isSubquery && !visitor.Subselects.IsEmpty()) + { + throw new EPStatementException("Invalid nested subquery, subquery-within-subquery is not supported", eplStatement); + } + if (isOnDemandQuery && !visitor.Subselects.IsEmpty()) + { + throw new EPStatementException("Subqueries are not a supported feature of on-demand queries", eplStatement); + } + foreach (var subselectNode in visitor.Subselects) + { + if (!subselectNodes.Contains(subselectNode)) + { + subselectNodes.Add(subselectNode); + } + } + + // Compile subselects found + var subselectNumber = 0; + foreach (var subselect in subselectNodes) + { + var raw = subselect.StatementSpecRaw; + StatementSpecCompiled compiled = Compile( + raw, eplStatement, statementContext, true, isOnDemandQuery, new Attribute[0], + Collections.GetEmptyList(), + Collections.GetEmptyList(), raw.TableExpressions, servicesContext); + subselectNumber++; + subselect.SetStatementSpecCompiled(compiled, subselectNumber); + } + + // compile each stream used + try + { + compiledStreams = new List(spec.StreamSpecs.Count); + var streamNum = 0; + foreach (var rawSpec in spec.StreamSpecs) + { + streamNum++; + var compiled = rawSpec.Compile( + statementContext, eventTypeReferences, spec.InsertIntoDesc != null, + Collections.SingletonList(streamNum), spec.StreamSpecs.Count > 1, false, spec.OnTriggerDesc != null, + rawSpec.OptionalStreamName); + compiledStreams.Add(compiled); + } + } + catch (ExprValidationException ex) + { + Log.Info("Failed to compile statement: " + ex.Message, ex); + if (ex.Message == null) + { + throw new EPStatementException("Unexpected exception compiling statement, please consult the log file and report the exception", eplStatement, ex); + } + else + { + throw new EPStatementException(ex.Message, eplStatement, ex); + } + } + catch (Exception ex) + { + const string text = "Unexpected error compiling statement"; + Log.Error(text, ex); + throw new EPStatementException(text + ": " + ex.GetType().Name + ":" + ex.Message, eplStatement, ex); + } + + // for create window statements, we switch the filter to a new event type + if (spec.CreateWindowDesc != null) + { + try + { + StreamSpecCompiled createWindowTypeSpec = compiledStreams[0]; + EventType selectFromType; + string selectFromTypeName; + if (createWindowTypeSpec is FilterStreamSpecCompiled) + { + var filterStreamSpec = (FilterStreamSpecCompiled)createWindowTypeSpec; + selectFromType = filterStreamSpec.FilterSpec.FilterForEventType; + selectFromTypeName = filterStreamSpec.FilterSpec.FilterForEventTypeName; + + if (spec.CreateWindowDesc.IsInsert || spec.CreateWindowDesc.InsertFilter != null) + { + throw new EPStatementException(string.Format("A named window by name '{0}' could not be located, use the insert-keyword with an existing named window", selectFromTypeName), eplStatement); + } + } + else + { + var consumerStreamSpec = (NamedWindowConsumerStreamSpec)createWindowTypeSpec; + selectFromType = statementContext.EventAdapterService.GetEventTypeByName(consumerStreamSpec.WindowName); + selectFromTypeName = consumerStreamSpec.WindowName; + + if (spec.CreateWindowDesc.InsertFilter != null) + { + var insertIntoFilter = spec.CreateWindowDesc.InsertFilter; + var checkMinimal = ExprNodeUtility.IsMinimalExpression(insertIntoFilter); + if (checkMinimal != null) + { + throw new ExprValidationException("Create window where-clause may not have " + checkMinimal); + } + StreamTypeService streamTypeService = new StreamTypeServiceImpl(selectFromType, selectFromTypeName, true, statementContext.EngineURI); + var evaluatorContextStmt = new ExprEvaluatorContextStatement(statementContext, false); + var validationContext = new ExprValidationContext( + streamTypeService, + statementContext.EngineImportService, + statementContext.StatementExtensionServicesContext, null, + statementContext.SchedulingService, + statementContext.VariableService, + statementContext.TableService, + evaluatorContextStmt, + statementContext.EventAdapterService, + statementContext.StatementName, + statementContext.StatementId, + statementContext.Annotations, + statementContext.ContextDescriptor, + statementContext.ScriptingService, + false, + false, + false, + false, + null, + false); + var insertFilter = ExprNodeUtility.GetValidatedSubtree(ExprNodeOrigin.CREATEWINDOWFILTER, spec.CreateWindowDesc.InsertFilter, validationContext); + spec.CreateWindowDesc.InsertFilter = insertFilter; + } + + // set the window to insert from + spec.CreateWindowDesc.InsertFromWindow = consumerStreamSpec.WindowName; + } + var newFilter = HandleCreateWindow(selectFromType, selectFromTypeName, spec.CreateWindowDesc.Columns, spec, eplStatement, statementContext, servicesContext); + eventTypeReferences.Add(((EventTypeSPI)newFilter.First.FilterForEventType).Metadata.PrimaryName); + + // view must be non-empty list + if (spec.CreateWindowDesc.ViewSpecs.IsEmpty()) + { + throw new ExprValidationException(NamedWindowMgmtServiceConstants.ERROR_MSG_DATAWINDOWS); + } + + // use the filter specification of the newly created event type and the views for the named window + compiledStreams.Clear(); + var views = spec.CreateWindowDesc.ViewSpecs.ToArray(); + compiledStreams.Add(new FilterStreamSpecCompiled(newFilter.First, views, null, spec.CreateWindowDesc.StreamSpecOptions)); + spec.SelectClauseSpec = newFilter.Second; + } + catch (ExprValidationException e) + { + throw new EPStatementException(e.Message, eplStatement); + } + } + + return new StatementSpecCompiled( + spec.OnTriggerDesc, + spec.CreateWindowDesc, + spec.CreateIndexDesc, + spec.CreateVariableDesc, + spec.CreateTableDesc, + spec.CreateSchemaDesc, + spec.InsertIntoDesc, + spec.SelectStreamSelectorEnum, + selectClauseCompiled, + compiledStreams.ToArray(), + spec.OuterJoinDescList.ToArray(), + spec.FilterRootNode, + spec.HavingExprRootNode, + spec.OutputLimitSpec, + OrderByItem.ToArray(spec.OrderByList), + ExprSubselectNode.ToArray(subselectNodes), + ExprNodeUtility.ToArray(declaredNodes), + spec.ScriptExpressions.MaterializeArray(), + spec.ReferencedVariables, + spec.RowLimitSpec, + CollectionUtil.ToArray(eventTypeReferences), + annotations, + spec.UpdateDesc, + spec.MatchRecognizeSpec, + spec.ForClauseSpec, + spec.SqlParameters, + spec.CreateContextDesc, + spec.OptionalContextName, + spec.CreateDataFlowDesc, + spec.CreateExpressionDesc, + spec.FireAndForgetSpec, + groupByRollupExpressions, + spec.IntoTableSpec, + tableAccessNodes.ToArrayOrNull()); + } + + private static bool DetermineStatelessSelect(StatementType type, StatementSpecRaw spec, bool hasSubselects, bool isPattern) + { + if (hasSubselects || isPattern) + { + return false; + } + if (type != StatementType.SELECT && type != StatementType.INSERT_INTO) + { + return false; + } + if (spec.StreamSpecs == null || spec.StreamSpecs.Count > 1 || spec.StreamSpecs.IsEmpty()) + { + return false; + } + StreamSpecRaw singleStream = spec.StreamSpecs[0]; + if (!(singleStream is FilterStreamSpecRaw) && !(singleStream is NamedWindowConsumerStreamSpec)) + { + return false; + } + if (singleStream.ViewSpecs != null && singleStream.ViewSpecs.Length > 0) + { + return false; + } + if (spec.OutputLimitSpec != null) + { + return false; + } + if (spec.MatchRecognizeSpec != null) + { + return false; + } + + var expressions = StatementSpecRawAnalyzer.CollectExpressionsShallow(spec); + if (expressions.IsEmpty()) + { + return true; + } + + var visitor = new ExprNodeSummaryVisitor(); + foreach (var expr in expressions.Where(e => e != null)) + { + expr.Accept(visitor); + } + + return !visitor.HasAggregation && !visitor.HasPreviousPrior && !visitor.HasSubselect; + } + + private static void RewriteNamedWindowSubselect( + IList chainedExpressionsDot, + IList subselects, + NamedWindowMgmtService service) + { + foreach (var dotNode in chainedExpressionsDot) + { + string proposedWindow = dotNode.ChainSpec[0].Name; + if (!service.IsNamedWindow(proposedWindow)) + { + continue; + } + + // build spec for subselect + var raw = new StatementSpecRaw(SelectClauseStreamSelectorEnum.ISTREAM_ONLY); + var filter = new FilterSpecRaw(proposedWindow, Collections.GetEmptyList(), null); + raw.StreamSpecs.Add(new FilterStreamSpecRaw(filter, ViewSpec.EMPTY_VIEWSPEC_ARRAY, proposedWindow, StreamSpecOptions.DEFAULT)); + + var firstChain = dotNode.ChainSpec.DeleteAt(0); + if (!firstChain.Parameters.IsEmpty()) + { + if (firstChain.Parameters.Count == 1) + { + raw.FilterExprRootNode = firstChain.Parameters[0]; + } + else + { + ExprAndNode andNode = new ExprAndNodeImpl(); + foreach (var node in firstChain.Parameters) + { + andNode.AddChildNode(node); + } + raw.FilterExprRootNode = andNode; + } + } + + // activate subselect + ExprSubselectNode subselect = new ExprSubselectRowNode(raw); + subselects.Add(subselect); + dotNode.ChildNodes = new[] { subselect }; + } + } + + /// + /// Compile a select clause allowing subselects. + /// + /// to compile + /// select clause compiled + /// ExprValidationException when validation fails + public static SelectClauseSpecCompiled CompileSelectAllowSubselect(SelectClauseSpecRaw spec) + { + // Look for expressions with sub-selects in select expression list and filter expression + // Recursively compile the statement within the statement. + var visitor = new ExprNodeSubselectDeclaredDotVisitor(); + IList selectElements = new List(); + foreach (var raw in spec.SelectExprList) + { + if (raw is SelectClauseExprRawSpec) + { + var rawExpr = (SelectClauseExprRawSpec)raw; + rawExpr.SelectExpression.Accept(visitor); + selectElements.Add( + new SelectClauseExprCompiledSpec( + rawExpr.SelectExpression, rawExpr.OptionalAsName, rawExpr.OptionalAsName, rawExpr.IsEvents)); + } + else if (raw is SelectClauseStreamRawSpec) + { + var rawExpr = (SelectClauseStreamRawSpec)raw; + selectElements.Add(new SelectClauseStreamCompiledSpec(rawExpr.StreamName, rawExpr.OptionalAsName)); + } + else if (raw is SelectClauseElementWildcard) + { + var wildcard = (SelectClauseElementWildcard)raw; + selectElements.Add(wildcard); + } + else + { + throw new IllegalStateException("Unexpected select clause element class : " + raw.GetType().Name); + } + } + return new SelectClauseSpecCompiled(selectElements.ToArray(), spec.IsDistinct); + } + + // The create window command: + // create window windowName[.window_view_list] as [select properties from] type + // + // This section expected s single FilterStreamSpecCompiled representing the selected type. + // It creates a new event type representing the window type and a sets the type selected on the filter stream spec. + private static Pair HandleCreateWindow( + EventType selectFromType, + string selectFromTypeName, + IList columns, + StatementSpecRaw spec, + string eplStatement, + StatementContext statementContext, + EPServicesContext servicesContext) + { + var typeName = spec.CreateWindowDesc.WindowName; + EventType targetType; + + // determine that the window name is not already in use as an event type name + var existingType = servicesContext.EventAdapterService.GetEventTypeByName(typeName); + if (existingType != null && ((EventTypeSPI)existingType).Metadata.TypeClass != TypeClass.NAMED_WINDOW) + { + throw new ExprValidationException("Error starting statement: An event type or schema by name '" + typeName + "' already exists"); + } + + // Validate the select expressions which consists of properties only + var evaluatorContextStmt = new ExprEvaluatorContextStatement(statementContext, false); + var select = CompileLimitedSelect( + spec.SelectClauseSpec, eplStatement, + selectFromType, + selectFromTypeName, + statementContext.EngineURI, + evaluatorContextStmt, + statementContext.EngineImportService, + statementContext.EventAdapterService, + statementContext.StatementName, + statementContext.StatementId, + statementContext.Annotations, + statementContext.StatementExtensionServicesContext); + + // Create Map or Wrapper event type from the select clause of the window. + // If no columns selected, simply create a wrapper type + // Build a list of properties + var newSelectClauseSpecRaw = new SelectClauseSpecRaw(); + IDictionary properties; + var hasProperties = false; + if ((columns != null) && (!columns.IsEmpty())) + { + properties = EventTypeUtility.BuildType( + columns, statementContext.EventAdapterService, null, + statementContext.EngineImportService); + hasProperties = true; + } + else + { + properties = new LinkedHashMap(); + foreach (var selectElement in select) + { + if (selectElement.FragmentType != null) + { + properties.Put(selectElement.AssignedName, selectElement.FragmentType); + } + else + { + properties.Put(selectElement.AssignedName, selectElement.SelectExpressionType); + } + + // Add any properties to the new select clause for use by consumers to the statement itself + newSelectClauseSpecRaw.Add( + new SelectClauseExprRawSpec(new ExprIdentNodeImpl(selectElement.AssignedName), null, false)); + hasProperties = true; + } + } + + // Create Map or Wrapper event type from the select clause of the window. + // If no columns selected, simply create a wrapper type + var isOnlyWildcard = spec.SelectClauseSpec.IsOnlyWildcard; + var isWildcard = spec.SelectClauseSpec.IsUsingWildcard; + if (statementContext.ValueAddEventService.IsRevisionTypeName(selectFromTypeName)) + { + targetType = statementContext.ValueAddEventService.CreateRevisionType( + typeName, selectFromTypeName, statementContext.StatementStopService, + statementContext.EventAdapterService, servicesContext.EventTypeIdGenerator); + } + else if (isWildcard && !isOnlyWildcard) + { + targetType = statementContext.EventAdapterService.AddWrapperType( + typeName, selectFromType, properties, true, false); + } + else + { + // Some columns selected, use the types of the columns + if (hasProperties && !isOnlyWildcard) + { + var compiledProperties = EventTypeUtility.CompileMapTypeProperties( + properties, statementContext.EventAdapterService); + var representation = EventRepresentationUtil.GetRepresentation( + statementContext.Annotations, servicesContext.ConfigSnapshot, AssignedType.NONE); + if (representation == EventUnderlyingType.MAP) + { + targetType = statementContext.EventAdapterService.AddNestableMapType( + typeName, compiledProperties, null, false, false, false, true, false); + } + else if (representation == EventUnderlyingType.OBJECTARRAY) + { + targetType = statementContext.EventAdapterService.AddNestableObjectArrayType( + typeName, compiledProperties, null, false, false, false, true, false, false, null); + } + else if (representation == EventUnderlyingType.AVRO) + { + targetType = statementContext.EventAdapterService.AddAvroType( + typeName, compiledProperties, false, false, false, true, false, statementContext.Annotations, + null, statementContext.StatementName, statementContext.EngineURI); + } + else + { + throw new IllegalStateException("Unrecognized representation " + representation); + } + } + else + { + // No columns selected, no wildcard, use the type as is or as a wrapped type + if (selectFromType is ObjectArrayEventType) + { + var objectArrayEventType = (ObjectArrayEventType)selectFromType; + targetType = statementContext.EventAdapterService.AddNestableObjectArrayType( + typeName, objectArrayEventType.Types, null, false, false, false, true, false, false, null); + } + else if (selectFromType is AvroSchemaEventType) + { + var avroSchemaEventType = (AvroSchemaEventType) selectFromType; + var avro = new ConfigurationEventTypeAvro(); + avro.SetAvroSchema(avroSchemaEventType.Schema); + targetType = statementContext.EventAdapterService.AddAvroType( + typeName, avro, false, false, false, true, false); + } + else if (selectFromType is MapEventType) + { + var mapType = (MapEventType)selectFromType; + targetType = statementContext.EventAdapterService.AddNestableMapType( + typeName, mapType.Types, null, false, false, false, true, false); + } + else if (selectFromType is BeanEventType) + { + var beanType = (BeanEventType)selectFromType; + targetType = statementContext.EventAdapterService.AddBeanTypeByName( + typeName, beanType.UnderlyingType, true); + } + else + { + IDictionary addOnTypes = new Dictionary(); + targetType = statementContext.EventAdapterService.AddWrapperType( + typeName, selectFromType, addOnTypes, true, false); + } + } + } + + var filter = new FilterSpecCompiled(targetType, typeName, new IList[0], null); + return new Pair(filter, newSelectClauseSpecRaw); + } + + private static IList CompileLimitedSelect( + SelectClauseSpecRaw spec, + string eplStatement, + EventType singleType, + string selectFromTypeName, + string engineURI, + ExprEvaluatorContext exprEvaluatorContext, + EngineImportService engineImportService, + EventAdapterService eventAdapterService, + string statementName, + int statementId, + Attribute[] annotations, + StatementExtensionSvcContext statementExtensionSvcContext) + { + var selectProps = new List(); + var streams = new StreamTypeServiceImpl( + new EventType[] { singleType }, + new string[] { "stream_0" }, + new bool[] { false }, + engineURI, false); + + var validationContext = new ExprValidationContext( + streams, engineImportService, + statementExtensionSvcContext, + null, null, null, null, + exprEvaluatorContext, + eventAdapterService, + statementName, statementId, annotations, + null, null, false, false, false, false, null, false); + + foreach (var raw in spec.SelectExprList) + { + if (!(raw is SelectClauseExprRawSpec)) + { + continue; + } + var exprSpec = (SelectClauseExprRawSpec)raw; + ExprNode validatedExpression; + try + { + validatedExpression = ExprNodeUtility.GetValidatedSubtree( + ExprNodeOrigin.SELECT, exprSpec.SelectExpression, validationContext); + } + catch (ExprValidationException e) + { + throw new EPStatementException(e.Message, e, eplStatement); + } + + // determine an element name if none assigned + var asName = exprSpec.OptionalAsName; + if (asName == null) + { + asName = ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(validatedExpression); + } + + // check for fragments + EventType fragmentType = null; + if ((validatedExpression is ExprIdentNode) && (!(singleType is NativeEventType))) + { + var identNode = (ExprIdentNode)validatedExpression; + var fragmentEventType = singleType.GetFragmentType(identNode.FullUnresolvedName); + if ((fragmentEventType != null) && (!fragmentEventType.IsNative)) + { + fragmentType = fragmentEventType.FragmentType; + } + } + + var validatedElement = new NamedWindowSelectedProps( + validatedExpression.ExprEvaluator.ReturnType, asName, fragmentType); + selectProps.Add(validatedElement); + } + + return selectProps; + } + + private static void RegisterNonPropertyGetters( + FilterSpecCompiled filter, + string statementName, + FilterNonPropertyRegisteryService filterNonPropertyRegisteryService) + { + foreach (var row in filter.Parameters) + { + foreach (var col in row) + { + if (col.Lookupable.IsNonPropertyGetter) + { + filterNonPropertyRegisteryService.RegisterNonPropertyExpression(statementName, filter.FilterForEventType, col.Lookupable); + } + } + } + } + + internal void DestroyInternal(EPStatementDesc desc) + { + try + { + // fire the statement stop + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QEngineManagementStmtStop(EPStatementState.DESTROYED, Services.EngineURI, desc.EpStatement.StatementId, desc.EpStatement.Name, desc.EpStatement.Text, Services.SchedulingService.Time); } + + // remove referenced event types + Services.StatementEventTypeRefService.RemoveReferencesStatement(desc.EpStatement.Name); + + // remove the named window lock + Services.NamedWindowMgmtService.RemoveNamedWindowLock(desc.EpStatement.Name); + + // remove any pattern subexpression counts + if (Services.PatternSubexpressionPoolSvc != null) + { + Services.PatternSubexpressionPoolSvc.RemoveStatement(desc.EpStatement.Name); + } + + // remove any match-recognize counts + if (Services.MatchRecognizeStatePoolEngineSvc != null) + { + Services.MatchRecognizeStatePoolEngineSvc.RemoveStatement(desc.EpStatement.Name); + } + + var statement = desc.EpStatement; + if (statement.State == EPStatementState.STARTED) + { + // fire the statement stop + desc.StatementContext.StatementStopService.FireStatementStopped(); + + // invoke start-provided stop method + var stopMethod = desc.StopMethod; + statement.ParentView = null; + stopMethod.Stop(); + } + + // call any destroy method that is registered for the statement: this destroy context partitions but not metadata + if (desc.DestroyMethod != null) + { + desc.DestroyMethod.Destroy(); + } + + // remove referenced non-property getters (after stop to allow lookup of these during stop) + Services.FilterNonPropertyRegisteryService.RemoveReferencesStatement(desc.EpStatement.Name); + + // remove referenced variables (after stop to allow lookup of these during stop) + Services.StatementVariableRefService.RemoveReferencesStatement(desc.EpStatement.Name); + + // destroy named window consumers + Services.NamedWindowConsumerMgmtService.Destroy(desc.StatementContext.StatementName); + + var timeLastStateChange = Services.SchedulingService.Time; + statement.SetCurrentState(EPStatementState.DESTROYED, timeLastStateChange); + + StmtNameToStmtMap.Remove(statement.Name); + _stmtNameToIdMap.Remove(statement.Name); + StmtIdToDescMap.Remove(statement.StatementId); + + if (!_epServiceProvider.IsDestroyed) + { + ((EPRuntimeSPI)_epServiceProvider.EPRuntime).ClearCaches(); + } + + DispatchStatementLifecycleEvent(new StatementLifecycleEvent(statement, StatementLifecycleEvent.LifecycleEventType.STATECHANGE)); + } + finally + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AEngineManagementStmtStop(); } + } + } + + public void DispatchStatementLifecycleEvent(StatementLifecycleEvent theEvent) + { + if (LifecycleEvent != null) + { + LifecycleEvent(this, theEvent); + } + } + + /// + /// Statement information. + /// + public class EPStatementDesc + { + /// + /// Ctor. + /// + /// the statement + /// the start method + /// statement context + public EPStatementDesc(EPStatementSPI epStatement, EPStatementStartMethod startMethod, StatementContext statementContext) + { + EpStatement = epStatement; + StartMethod = startMethod; + StatementContext = statementContext; + } + + /// + /// Returns the statement. + /// + /// statement. + public EPStatementSPI EpStatement { get; private set; } + + /// + /// Returns the start method. + /// + /// start method + public EPStatementStartMethod StartMethod { get; private set; } + + /// + /// Returns the stop method. + /// + /// stop method + public EPStatementStopMethod StopMethod { get; set; } + + /// + /// Returns the statement context. + /// + /// statement context + public StatementContext StatementContext { get; private set; } + + /// + /// Gets or sets method to call when destroyed. + /// + /// method + public EPStatementDestroyMethod DestroyMethod { get; set; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/service/StatementLifecycleSvcUtil.cs b/NEsper.Core/NEsper.Core/core/service/StatementLifecycleSvcUtil.cs new file mode 100755 index 000000000..fe259b171 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/StatementLifecycleSvcUtil.cs @@ -0,0 +1,307 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.compat; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.visitor; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.filter; +using com.espertech.esper.pattern; + +namespace com.espertech.esper.core.service +{ + public class StatementLifecycleSvcUtil + { + public static void AssignFilterSpecIds(FilterSpecCompiled filterSpec, FilterSpecCompiled[] filterSpecsAll) + { + for (int path = 0; path < filterSpec.Parameters.Length; path++) + { + foreach (FilterSpecParam param in filterSpec.Parameters[path]) + { + if (param is FilterSpecParamExprNode) + { + var index = filterSpec.GetFilterSpecIndexAmongAll(filterSpecsAll); + var exprNode = (FilterSpecParamExprNode) param; + exprNode.FilterSpecId = index; + exprNode.FilterSpecParamPathNum = path; + } + } + } + } + + public static void WalkStatement(StatementSpecRaw spec, ExprNodeSubselectDeclaredDotVisitor visitor) + { + // Look for expressions with sub-selects in select expression list and filter expression + // Recursively compile the statement within the statement. + foreach (SelectClauseElementRaw raw in spec.SelectClauseSpec.SelectExprList) + { + if (raw is SelectClauseExprRawSpec) + { + SelectClauseExprRawSpec rawExpr = (SelectClauseExprRawSpec) raw; + rawExpr.SelectExpression.Accept(visitor); + } + else + { + continue; + } + } + if (spec.FilterRootNode != null) + { + spec.FilterRootNode.Accept(visitor); + } + if (spec.HavingExprRootNode != null) + { + spec.HavingExprRootNode.Accept(visitor); + } + if (spec.UpdateDesc != null) + { + if (spec.UpdateDesc.OptionalWhereClause != null) + { + spec.UpdateDesc.OptionalWhereClause.Accept(visitor); + } + foreach (OnTriggerSetAssignment assignment in spec.UpdateDesc.Assignments) + { + assignment.Expression.Accept(visitor); + } + } + if (spec.OnTriggerDesc != null) { + VisitSubselectOnTrigger(spec.OnTriggerDesc, visitor); + } + // Determine pattern-filter subqueries + foreach (StreamSpecRaw streamSpecRaw in spec.StreamSpecs) { + if (streamSpecRaw is PatternStreamSpecRaw) { + PatternStreamSpecRaw patternStreamSpecRaw = (PatternStreamSpecRaw) streamSpecRaw; + EvalNodeAnalysisResult analysisResult = EvalNodeUtil.RecursiveAnalyzeChildNodes(patternStreamSpecRaw.EvalFactoryNode); + foreach (EvalFactoryNode evalNode in analysisResult.ActiveNodes) { + if (evalNode is EvalFilterFactoryNode) { + EvalFilterFactoryNode filterNode = (EvalFilterFactoryNode) evalNode; + foreach (ExprNode filterExpr in filterNode.RawFilterSpec.FilterExpressions) { + filterExpr.Accept(visitor); + } + } + else if (evalNode is EvalObserverFactoryNode) { + int beforeCount = visitor.Subselects.Count; + EvalObserverFactoryNode observerNode = (EvalObserverFactoryNode) evalNode; + foreach (ExprNode param in observerNode.PatternObserverSpec.ObjectParameters) { + param.Accept(visitor); + } + if (visitor.Subselects.Count != beforeCount) { + throw new ExprValidationException("Subselects are not allowed within pattern observer parameters, please consider using a variable instead"); + } + } + } + } + } + + // walk streams + WalkStreamSpecs(spec, visitor); + } + + public static void WalkStreamSpecs(StatementSpecRaw spec, ExprNodeSubselectDeclaredDotVisitor visitor) { + + // Determine filter streams + foreach (StreamSpecRaw rawSpec in spec.StreamSpecs) + { + if (rawSpec is FilterStreamSpecRaw) { + FilterStreamSpecRaw raw = (FilterStreamSpecRaw) rawSpec; + foreach (ExprNode filterExpr in raw.RawFilterSpec.FilterExpressions) { + filterExpr.Accept(visitor); + } + } + if (rawSpec is PatternStreamSpecRaw) { + PatternStreamSpecRaw patternStreamSpecRaw = (PatternStreamSpecRaw) rawSpec; + EvalNodeAnalysisResult analysisResult = EvalNodeUtil.RecursiveAnalyzeChildNodes(patternStreamSpecRaw.EvalFactoryNode); + foreach (EvalFactoryNode evalNode in analysisResult.ActiveNodes) { + if (evalNode is EvalFilterFactoryNode) { + EvalFilterFactoryNode filterNode = (EvalFilterFactoryNode) evalNode; + foreach (ExprNode filterExpr in filterNode.RawFilterSpec.FilterExpressions) { + filterExpr.Accept(visitor); + } + } + } + } + } + } + + private static void VisitSubselectOnTrigger(OnTriggerDesc onTriggerDesc, ExprNodeSubselectDeclaredDotVisitor visitor) { + if (onTriggerDesc is OnTriggerWindowUpdateDesc) { + OnTriggerWindowUpdateDesc updates = (OnTriggerWindowUpdateDesc) onTriggerDesc; + foreach (OnTriggerSetAssignment assignment in updates.Assignments) + { + assignment.Expression.Accept(visitor); + } + } + else if (onTriggerDesc is OnTriggerSetDesc) { + OnTriggerSetDesc sets = (OnTriggerSetDesc) onTriggerDesc; + foreach (OnTriggerSetAssignment assignment in sets.Assignments) + { + assignment.Expression.Accept(visitor); + } + } + else if (onTriggerDesc is OnTriggerSplitStreamDesc) { + OnTriggerSplitStreamDesc splits = (OnTriggerSplitStreamDesc) onTriggerDesc; + foreach (OnTriggerSplitStream split in splits.SplitStreams) + { + if (split.WhereClause != null) { + split.WhereClause.Accept(visitor); + } + if (split.SelectClause.SelectExprList != null) { + foreach (SelectClauseElementRaw element in split.SelectClause.SelectExprList) { + if (element is SelectClauseExprRawSpec) { + SelectClauseExprRawSpec selectExpr = (SelectClauseExprRawSpec) element; + selectExpr.SelectExpression.Accept(visitor); + } + } + } + } + } + else if (onTriggerDesc is OnTriggerMergeDesc) { + OnTriggerMergeDesc merge = (OnTriggerMergeDesc) onTriggerDesc; + foreach (OnTriggerMergeMatched matched in merge.Items) { + if (matched.OptionalMatchCond != null) { + matched.OptionalMatchCond.Accept(visitor); + } + foreach (OnTriggerMergeAction action in matched.Actions) + { + if (action.OptionalWhereClause != null) { + action.OptionalWhereClause.Accept(visitor); + } + + if (action is OnTriggerMergeActionUpdate) { + OnTriggerMergeActionUpdate update = (OnTriggerMergeActionUpdate) action; + foreach (OnTriggerSetAssignment assignment in update.Assignments) + { + assignment.Expression.Accept(visitor); + } + } + if (action is OnTriggerMergeActionInsert) { + OnTriggerMergeActionInsert insert = (OnTriggerMergeActionInsert) action; + foreach (SelectClauseElementRaw element in insert.SelectClause) { + if (element is SelectClauseExprRawSpec) { + SelectClauseExprRawSpec selectExpr = (SelectClauseExprRawSpec) element; + selectExpr.SelectExpression.Accept(visitor); + } + } + } + } + } + } + } + + public static SelectClauseSpecCompiled CompileSelectClause(SelectClauseSpecRaw spec) { + IList selectElements = new List(); + foreach (SelectClauseElementRaw raw in spec.SelectExprList) + { + if (raw is SelectClauseExprRawSpec) + { + SelectClauseExprRawSpec rawExpr = (SelectClauseExprRawSpec) raw; + selectElements.Add(new SelectClauseExprCompiledSpec(rawExpr.SelectExpression, rawExpr.OptionalAsName, rawExpr.OptionalAsName, rawExpr.IsEvents)); + } + else if (raw is SelectClauseStreamRawSpec) + { + SelectClauseStreamRawSpec rawExpr = (SelectClauseStreamRawSpec) raw; + selectElements.Add(new SelectClauseStreamCompiledSpec(rawExpr.StreamName, rawExpr.OptionalAsName)); + } + else if (raw is SelectClauseElementWildcard) + { + SelectClauseElementWildcard wildcard = (SelectClauseElementWildcard) raw; + selectElements.Add(wildcard); + } + else + { + throw new IllegalStateException("Unexpected select clause element class : " + raw.GetType().FullName); + } + } + return new SelectClauseSpecCompiled(selectElements.ToArray(), spec.IsDistinct); + } + + public static bool IsWritesToTables(StatementSpecRaw statementSpec, TableService tableService) + { + // determine if writing to a table: + + // insert-into (single) + if (statementSpec.InsertIntoDesc != null) { + if (IsTable(statementSpec.InsertIntoDesc.EventTypeName, tableService)) { + return true; + } + } + + // into-table + if (statementSpec.IntoTableSpec != null) { + return true; + } + + // triggers + if (statementSpec.OnTriggerDesc != null) { + OnTriggerDesc onTriggerDesc = statementSpec.OnTriggerDesc; + + // split-stream insert-into + if (onTriggerDesc.OnTriggerType == OnTriggerType.ON_SPLITSTREAM) { + OnTriggerSplitStreamDesc split = (OnTriggerSplitStreamDesc) onTriggerDesc; + foreach (OnTriggerSplitStream stream in split.SplitStreams) { + if (stream.InsertInto != null && IsTable(stream.InsertInto.EventTypeName, tableService)) { + return true; + } + } + } + + // on-delete/update/merge/on-selectdelete + if (onTriggerDesc is OnTriggerWindowDesc) { + OnTriggerWindowDesc window = (OnTriggerWindowDesc) onTriggerDesc; + if (onTriggerDesc.OnTriggerType == OnTriggerType.ON_DELETE || + onTriggerDesc.OnTriggerType == OnTriggerType.ON_UPDATE || + onTriggerDesc.OnTriggerType == OnTriggerType.ON_MERGE || + window.IsDeleteAndSelect) { + if (IsTable(window.WindowName, tableService)) { + return true; + } + } + } + + // on-merge with insert-action + if (onTriggerDesc is OnTriggerMergeDesc) { + OnTriggerMergeDesc merge = (OnTriggerMergeDesc) onTriggerDesc; + foreach (OnTriggerMergeMatched item in merge.Items) { + foreach (OnTriggerMergeAction action in item.Actions) { + if (action is OnTriggerMergeActionInsert) { + OnTriggerMergeActionInsert insert = (OnTriggerMergeActionInsert) action; + if (insert.OptionalStreamName != null && IsTable(insert.OptionalStreamName, tableService)) { + return true; + } + } + } + } + } + } // end of trigger handling + + // fire-and-forget insert/update/delete + if (statementSpec.FireAndForgetSpec != null) { + FireAndForgetSpec faf = statementSpec.FireAndForgetSpec; + if (faf is FireAndForgetSpecDelete || + faf is FireAndForgetSpecInsert || + faf is FireAndForgetSpecUpdate) { + if (statementSpec.StreamSpecs.Count == 1) { + return IsTable(((FilterStreamSpecRaw) statementSpec.StreamSpecs[0]).RawFilterSpec.EventTypeName, tableService); + } + } + } + + return false; + } + + private static bool IsTable(string name, TableService tableService) + { + return tableService.GetTableMetadata(name) != null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/StatementLockFactory.cs b/NEsper.Core/NEsper.Core/core/service/StatementLockFactory.cs new file mode 100755 index 000000000..56a6234ba --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/StatementLockFactory.cs @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using com.espertech.esper.compat.threading; + +namespace com.espertech.esper.core.service +{ + /// + /// Factory for the managed lock that provides statement resource protection. + /// + public interface StatementLockFactory + { + /// + /// Create lock for statement + /// + /// is the statement name + /// The annotations. + /// if set to true [stateless]. + /// lock + IReaderWriterLock GetStatementLock(string statementName, Attribute[] annotations, bool stateless); + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/StatementLockFactoryImpl.cs b/NEsper.Core/NEsper.Core/core/service/StatementLockFactoryImpl.cs new file mode 100755 index 000000000..4d36d07fd --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/StatementLockFactoryImpl.cs @@ -0,0 +1,47 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client.annotation; +using com.espertech.esper.compat.threading; +using com.espertech.esper.epl.annotation; + +namespace com.espertech.esper.core.service +{ + /// + /// Provides statement-level locks. + /// + public class StatementLockFactoryImpl : StatementLockFactory + { + private readonly bool _fairlocks; + private readonly bool _disableLocking; + + public StatementLockFactoryImpl(bool fairlocks, bool disableLocking) + { + _fairlocks = fairlocks; + _disableLocking = disableLocking; + } + + public IReaderWriterLock GetStatementLock(String statementName, Attribute[] annotations, bool stateless) + { + bool foundNoLock = AnnotationUtil.FindAnnotation(annotations, typeof(NoLockAttribute)) != null; + if (_disableLocking || foundNoLock || stateless) + { + return ReaderWriterLockManager.VoidLock(); + } + + if (_fairlocks) + { + return ReaderWriterLockManager.FairLock(); + } + + return ReaderWriterLockManager.CreateDefaultLock(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/StatementMetadata.cs b/NEsper.Core/NEsper.Core/core/service/StatementMetadata.cs new file mode 100755 index 000000000..844d4afba --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/StatementMetadata.cs @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + + +namespace com.espertech.esper.core.service +{ + /// Statement metadata. + [Serializable] + public class StatementMetadata + { + /// Ctor. + /// the type of statement + public StatementMetadata(StatementType statementType) + { + StatementType = statementType; + } + + /// Returns the statement type. + /// statement type. + public StatementType StatementType { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/StatementMetadataFactory.cs b/NEsper.Core/NEsper.Core/core/service/StatementMetadataFactory.cs new file mode 100755 index 000000000..9ab72a4e0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/StatementMetadataFactory.cs @@ -0,0 +1,16 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.core.service +{ + /// Statement metadata factory. + public interface StatementMetadataFactory + { + StatementMetadata Create(StatementMetadataFactoryContext context); + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/StatementMetadataFactoryContext.cs b/NEsper.Core/NEsper.Core/core/service/StatementMetadataFactoryContext.cs new file mode 100755 index 000000000..635577747 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/StatementMetadataFactoryContext.cs @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client.soda; +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.core.service +{ + /// + /// Statement metadata factory context. + /// + public class StatementMetadataFactoryContext + { + public StatementMetadataFactoryContext( + string statementName, + int statementId, + StatementContext statementContext, + StatementSpecRaw statementSpec, + string expression, + bool pattern, + EPStatementObjectModel optionalModel) + { + StatementName = statementName; + StatementId = statementId; + StatementContext = statementContext; + StatementSpec = statementSpec; + Expression = expression; + IsPattern = pattern; + OptionalModel = optionalModel; + } + + public string StatementName { get; private set; } + + public int StatementId { get; private set; } + + public StatementContext StatementContext { get; private set; } + + public StatementSpecRaw StatementSpec { get; private set; } + + public bool IsPattern { get; private set; } + + public string Expression { get; private set; } + + public EPStatementObjectModel OptionalModel { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/service/StatementMetadataFactoryDefault.cs b/NEsper.Core/NEsper.Core/core/service/StatementMetadataFactoryDefault.cs new file mode 100755 index 000000000..420be4c29 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/StatementMetadataFactoryDefault.cs @@ -0,0 +1,91 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.core.service +{ + /// + /// Statement metadata. + /// + public class StatementMetadataFactoryDefault : StatementMetadataFactory + { + public StatementMetadata Create(StatementMetadataFactoryContext context) + { + return new StatementMetadata(GetStatementType(context.StatementSpec, context.IsPattern)); + } + + public static StatementType GetStatementType(StatementSpecRaw statementSpec, bool pattern) + { + // determine statement type + StatementType? statementType = null; + if (statementSpec.CreateVariableDesc != null) { + statementType = StatementType.CREATE_VARIABLE; + } + else if (statementSpec.CreateTableDesc != null) { + statementType = StatementType.CREATE_TABLE; + } + else if (statementSpec.CreateWindowDesc != null) { + statementType = StatementType.CREATE_WINDOW; + } + else if (statementSpec.OnTriggerDesc != null) { + if (statementSpec.OnTriggerDesc.OnTriggerType == OnTriggerType.ON_DELETE) { + statementType = StatementType.ON_DELETE; + } + else if (statementSpec.OnTriggerDesc.OnTriggerType == OnTriggerType.ON_UPDATE) { + statementType = StatementType.ON_UPDATE; + } + else if (statementSpec.OnTriggerDesc.OnTriggerType == OnTriggerType.ON_SELECT) { + if (statementSpec.InsertIntoDesc != null) { + statementType = StatementType.ON_INSERT; + } + else { + statementType = StatementType.ON_SELECT; + } + } + else if (statementSpec.OnTriggerDesc.OnTriggerType == OnTriggerType.ON_SET) { + statementType = StatementType.ON_SET; + } + else if (statementSpec.OnTriggerDesc.OnTriggerType == OnTriggerType.ON_MERGE) { + statementType = StatementType.ON_MERGE; + } + else if (statementSpec.OnTriggerDesc.OnTriggerType == OnTriggerType.ON_SPLITSTREAM) { + statementType = StatementType.ON_SPLITSTREAM; + } + } + else if (statementSpec.InsertIntoDesc != null) { + statementType = StatementType.INSERT_INTO; + } + else if (pattern) { + statementType = StatementType.PATTERN; + } + else if (statementSpec.UpdateDesc != null) { + statementType = StatementType.UPDATE; + } + else if (statementSpec.CreateIndexDesc != null) { + statementType = StatementType.CREATE_INDEX; + } + else if (statementSpec.CreateContextDesc != null) { + statementType = StatementType.CREATE_CONTEXT; + } + else if (statementSpec.CreateSchemaDesc != null) { + statementType = StatementType.CREATE_SCHEMA; + } + else if (statementSpec.CreateDataFlowDesc != null) { + statementType = StatementType.CREATE_DATAFLOW; + } + else if (statementSpec.CreateExpressionDesc != null) { + statementType = StatementType.CREATE_EXPRESSION; + } + if (statementType == null) { + statementType = StatementType.SELECT; + } + return statementType.Value; + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/StatementResultListener.cs b/NEsper.Core/NEsper.Core/core/service/StatementResultListener.cs new file mode 100755 index 000000000..5b5490ab5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/StatementResultListener.cs @@ -0,0 +1,31 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using com.espertech.esper.client; + +namespace com.espertech.esper.core.service +{ + /// + /// Interface for statement result callbacks. + /// + public interface StatementResultListener + { + /// Provide statement result. + /// insert stream + /// remove stream + /// stmt name + /// stmt + /// engine + void Update(EventBean[] newEvents, + EventBean[] oldEvents, + String statementName, + EPStatementSPI statement, + EPServiceProviderSPI epServiceProvider); + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/service/StatementResultService.cs b/NEsper.Core/NEsper.Core/core/service/StatementResultService.cs new file mode 100755 index 000000000..4321fffcb --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/StatementResultService.cs @@ -0,0 +1,97 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.metric; + +namespace com.espertech.esper.core.service +{ + /// + /// Interface for a statement-level service for coordinating the insert/remove stream generation, + /// native deliver to subscribers and the presence/absence of listener or subscribers to a statement. + /// + public interface StatementResultService + { + /// + /// For initialization of the service to provide statement context. + /// + /// the statement + /// the engine instance + /// true if this is insert into + /// true if this is a pattern statement + /// true if using distinct + /// if set to true [is for clause]. + /// handle for metrics reporting + void SetContext(EPStatementSPI epStatement, + EPServiceProviderSPI epServiceProvider, + bool isInsertInto, + bool isPattern, + bool isDistinct, + bool isForClause, + StatementMetricHandle statementMetricHandle); + + /// + /// For initialize of the service providing select clause column types and names. + /// + /// types of columns in the select clause + /// column names + /// if set to true [for clause delivery]. + /// The group delivery expressions. + /// The expr evaluator context. + void SetSelectClause(Type[] selectClauseTypes, + String[] selectClauseColumnNames, + Boolean forClauseDelivery, + ExprEvaluator[] groupDeliveryExpressions, + ExprEvaluatorContext exprEvaluatorContext); + + /// Returns true to indicate that synthetic events should be produced, for use in select expression processing. + /// true to produce synthetic events + bool IsMakeSynthetic { get; } + + /// Returns true to indicate that natural events should be produced, for use in select expression processing. + /// true to produce natural (object[] column) events + bool IsMakeNatural { get; } + + /// Dispatch the remaining results, if any, to listeners as the statement is about to be stopped. + void DispatchOnStop(); + + /// Indicate a change in Update listener. + /// is the new listeners and subscriber + void SetUpdateListeners(EPStatementListenerSet updateListeners, bool isRecovery); + + /// Stores for dispatching the statement results. + /// is the insert and remove stream data + void Indicate(UniformPair results); + + /// Execution of result indication. + void Execute(); + + /// + /// Gets the name of the statement. + /// + /// The name of the statement. + string StatementName { get; } + + /// + /// Gets the statement id. + /// + /// The statement id. + int StatementId { get; } + + /// + /// Gets the statement listener set. + /// + /// The statement listener set. + EPStatementListenerSet StatementListenerSet { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/StatementResultServiceImpl.cs b/NEsper.Core/NEsper.Core/core/service/StatementResultServiceImpl.cs new file mode 100755 index 000000000..32be7cf7a --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/StatementResultServiceImpl.cs @@ -0,0 +1,455 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.compat.threading; +using com.espertech.esper.core.thread; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.metric; +using com.espertech.esper.events; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.service +{ + /// + /// Implements tracking of statement listeners and subscribers for a given statement + /// such as to efficiently dispatch in situations where 0, 1 or more listeners are attached + /// and/or 0 or 1 subscriber (such as iteration-only statement). + /// + public class StatementResultServiceImpl : StatementResultService + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly bool _isDebugEnabled; + + private readonly String _statementName; + private readonly StatementLifecycleSvc _statementLifecycleSvc; + private readonly MetricReportingService _metricReportingService; + private readonly ThreadingService _threadingService; + + // Part of the statement context + private EPStatementSPI _epStatement; + private EPServiceProviderSPI _epServiceProvider; + private bool _isInsertInto; + private bool _isPattern; + private bool _isDistinct; + private bool _isForClause; + private StatementMetricHandle _statementMetricHandle; + + private bool _forClauseDelivery; + private ExprEvaluator[] _groupDeliveryExpressions; + private ExprEvaluatorContext _exprEvaluatorContext; + + // For natural delivery derived out of select-clause expressions + private Type[] _selectClauseTypes; + private String[] _selectClauseColumnNames; + + // Listeners and subscribers and derived information + private EPStatementListenerSet _statementListenerSet; + private bool _isMakeNatural; + private bool _isMakeSynthetic; + private ResultDeliveryStrategy _statementResultNaturalStrategy; + + private readonly ICollection _statementOutputHooks; + + /// Buffer for holding dispatchable events. + private IThreadLocal>> _lastResults = + ThreadLocalManager.Create(() => new LinkedList>()); + + /// + /// Ctor. + /// + /// Name of the statement. + /// handles persistence for statements + /// for metrics reporting + /// for outbound threading + public StatementResultServiceImpl(String statementName, + StatementLifecycleSvc statementLifecycleSvc, + MetricReportingServiceSPI metricReportingService, + ThreadingService threadingService) + { + Log.Debug(".ctor"); + + _isDebugEnabled = ExecutionPathDebugLog.IsEnabled && Log.IsDebugEnabled; + _statementName = statementName; + _statementLifecycleSvc = statementLifecycleSvc; + _metricReportingService = metricReportingService; + _statementOutputHooks = metricReportingService != null ? metricReportingService.StatementOutputHooks : new List(); + _threadingService = threadingService; + } + + /// + /// Gets the name of the statement. + /// + /// The name of the statement. + public string StatementName + { + get { return _statementName; } + } + + /// + /// For initialization of the service to provide statement context. + /// + /// the statement + /// the engine instance + /// true if this is insert into + /// true if this is a pattern statement + /// true if using distinct + /// if set to true [is for clause]. + /// handle for metrics reporting + public void SetContext( + EPStatementSPI epStatement, + EPServiceProviderSPI epServiceProvider, + bool isInsertInto, + bool isPattern, + bool isDistinct, + bool isForClause, + StatementMetricHandle statementMetricHandle) + { + _epStatement = epStatement; + _epServiceProvider = epServiceProvider; + _isInsertInto = isInsertInto; + _isPattern = isPattern; + _isDistinct = isDistinct; + _isForClause = isForClause; + _isMakeSynthetic = isInsertInto || isPattern || isDistinct || isForClause; + _statementMetricHandle = statementMetricHandle; + } + + /// + /// For initialize of the service providing select clause column types and names. + /// + /// types of columns in the select clause + /// column names + /// if set to true [for clause delivery]. + /// The group delivery expressions. + /// The expr evaluator context. + public void SetSelectClause( + Type[] selectClauseTypes, + String[] selectClauseColumnNames, + bool forClauseDelivery, + ExprEvaluator[] groupDeliveryExpressions, + ExprEvaluatorContext exprEvaluatorContext) + { + if ((selectClauseTypes == null) || (selectClauseTypes.Length == 0)) + { + throw new ArgumentException("Invalid null or zero-element list of select clause expression types"); + } + if ((selectClauseColumnNames == null) || (selectClauseColumnNames.Length == 0)) + { + throw new ArgumentException("Invalid null or zero-element list of select clause column names"); + } + _selectClauseTypes = selectClauseTypes; + _selectClauseColumnNames = selectClauseColumnNames; + _forClauseDelivery = forClauseDelivery; + _exprEvaluatorContext = exprEvaluatorContext; + _groupDeliveryExpressions = groupDeliveryExpressions; + } + + public int StatementId + { + get { return _epStatement.StatementId; } + } + + public bool IsMakeSynthetic + { + get { return _isMakeSynthetic; } + } + + public bool IsMakeNatural + { + get { return _isMakeNatural; } + } + + public EPStatementListenerSet StatementListenerSet + { + get { return _statementListenerSet; } + } + + public void SetUpdateListeners(EPStatementListenerSet updateListeners, bool isRecovery) + { + // indicate that listeners were updated for potential persistence of listener set, once the statement context is known + if (_epStatement != null) + { + _statementLifecycleSvc.UpdatedListeners(_epStatement, updateListeners, isRecovery); + } + + _statementListenerSet = updateListeners; + + _isMakeNatural = _statementListenerSet.Subscriber != null; + _isMakeSynthetic = _statementListenerSet.HasEventConsumers + || _isPattern || _isInsertInto || _isDistinct | _isForClause; + + if (_statementListenerSet.Subscriber == null) + { + _statementResultNaturalStrategy = null; + _isMakeNatural = false; + return; + } + + _statementResultNaturalStrategy = ResultDeliveryStrategyFactory.Create( + _epStatement, + _statementListenerSet.Subscriber, + _selectClauseTypes, + _selectClauseColumnNames, + _epServiceProvider.URI, + _epServiceProvider.EngineImportService + ); + _isMakeNatural = true; + } + +#if NET45 + //[MethodImplOptions.AggressiveInlining] +#endif + + // Called by OutputProcessView + public void Indicate(UniformPair results) + { + if (results != null) + { + if ((MetricReportingPath.IsMetricsEnabled) && (_statementMetricHandle.IsEnabled)) + { + int numIStream = (results.First != null) ? results.First.Length : 0; + int numRStream = (results.Second != null) ? results.Second.Length : 0; + _metricReportingService.AccountOutput(_statementMetricHandle, numIStream, numRStream); + } + + var lastResults = _lastResults.GetOrCreate(); + + if ((results.First != null) && (results.First.Length != 0)) + { + lastResults.AddLast(results); + } + else if ((results.Second != null) && (results.Second.Length != 0)) + { + lastResults.AddLast(results); + } + } + } + + public void Execute() + { + var dispatches = _lastResults.GetOrCreate(); + var events = EventBeanUtility.FlattenList(dispatches); + + if (_isDebugEnabled) + { + ViewSupport.DumpUpdateParams(".execute", events); + } + + if ((ThreadingOption.IsThreadingEnabledValue) && (_threadingService.IsOutboundThreading)) + { + _threadingService.SubmitOutbound(new OutboundUnitRunnable(events, this).Run); + } + else + { + ProcessDispatch(events); + } + + dispatches.Clear(); + } + + /// Indicate an outbound result. + /// to indicate + public void ProcessDispatch(UniformPair events) + { + // Plain all-events delivery + if (!_forClauseDelivery) + { + DispatchInternal(events); + return; + } + + // Discrete delivery + if ((_groupDeliveryExpressions == null) || (_groupDeliveryExpressions.Length == 0)) + { + var todeliver = new UniformPair(null, null); + + if (events != null) + { + if (events.First != null) + { + foreach (EventBean theEvent in events.First) + { + todeliver.First = new EventBean[] { theEvent }; + DispatchInternal(todeliver); + } + todeliver.First = null; + } + if (events.Second != null) + { + foreach (EventBean theEvent in events.Second) + { + todeliver.Second = new EventBean[] { theEvent }; + DispatchInternal(todeliver); + } + todeliver.Second = null; + } + } + + return; + } + + // Grouped delivery + IDictionary> groups; + try + { + groups = GetGroupedResults(events); + } + catch (Exception ex) + { + Log.Error("Unexpected exception evaluating grouped-delivery expressions: " + ex.Message + ", delivering ungrouped", ex); + DispatchInternal(events); + return; + } + + // Deliver each group separately + foreach (var group in groups) + { + DispatchInternal(group.Value); + } + } + + private IDictionary> GetGroupedResults(UniformPair events) + { + if (events == null) + { + return new Dictionary>(); + } + + var groups = new LinkedHashMap>(); + var eventsPerStream = new EventBean[1]; + GetGroupedResults(groups, events.First, true, eventsPerStream); + GetGroupedResults(groups, events.Second, false, eventsPerStream); + return groups; + } + + private void GetGroupedResults(IDictionary> groups, IEnumerable events, bool insertStream, EventBean[] eventsPerStream) + { + if (events == null) + { + return; + } + + foreach (EventBean theEvent in events) + { + EventBean evalEvent = theEvent; + if (evalEvent is NaturalEventBean) + { + evalEvent = ((NaturalEventBean)evalEvent).OptionalSynthetic; + } + + Object key; + eventsPerStream[0] = evalEvent; + if (_groupDeliveryExpressions.Length == 1) + { + key = _groupDeliveryExpressions[0].Evaluate(new EvaluateParams(eventsPerStream, true, _exprEvaluatorContext)); + } + else + { + var keys = new Object[_groupDeliveryExpressions.Length]; + for (int i = 0; i < _groupDeliveryExpressions.Length; i++) + { + keys[i] = _groupDeliveryExpressions[i].Evaluate(new EvaluateParams(eventsPerStream, true, _exprEvaluatorContext)); + } + key = new MultiKeyUntyped(keys); + } + + UniformPair groupEntry = groups.Get(key); + if (groupEntry == null) + { + groupEntry = insertStream + ? new UniformPair(new[] { theEvent }, null) + : new UniformPair(null, new[] { theEvent }); + groups.Put(key, groupEntry); + } + else + { + if (insertStream) + { + groupEntry.First = groupEntry.First == null + ? new[] { theEvent } + : EventBeanUtility.AddToArray(groupEntry.First, theEvent); + } + else + { + groupEntry.Second = groupEntry.Second == null + ? new[] { theEvent } + : EventBeanUtility.AddToArray(groupEntry.Second, theEvent); + } + } + } + } + + private void DispatchInternal(UniformPair events) + { + if (_statementResultNaturalStrategy != null) + { + _statementResultNaturalStrategy.Execute(events); + } + + EventBean[] newEventArr = events != null ? events.First : null; + EventBean[] oldEventArr = events != null ? events.Second : null; + + var eventHandlerList = _statementListenerSet.Events; + if (eventHandlerList.Count != 0) + { + var ev = new UpdateEventArgs(_epServiceProvider, _epStatement, newEventArr, oldEventArr); + var eventList = eventHandlerList.ToArray(); + if (eventList != null) + { + var eventListLength = eventList.Length; + for (int ii = 0; ii < eventListLength; ii++) + { + var eventHandler = eventList[ii]; + try + { + eventHandler.Invoke(this, ev); + } + catch (Exception e) + { + Log.Error("Unexpected exception invoking event handler", e); + } + } + } + } + + if ((AuditPath.IsAuditEnabled) && (_statementOutputHooks.IsNotEmpty())) + { + foreach (StatementResultListener listener in _statementOutputHooks) + { + listener.Update(newEventArr, oldEventArr, _epStatement.Name, _epStatement, _epServiceProvider); + } + } + } + + /// + /// Dispatches when the statement is stopped any remaining results. + /// + public void DispatchOnStop() + { + var dispatches = _lastResults.GetOrCreate(); + if (dispatches.IsEmpty()) + { + return; + } + + Execute(); + + _lastResults = ThreadLocalManager.Create( + () => new LinkedList>()); + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/StatementSemiAnonymousTypeRegistry.cs b/NEsper.Core/NEsper.Core/core/service/StatementSemiAnonymousTypeRegistry.cs new file mode 100755 index 000000000..edf9795c0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/StatementSemiAnonymousTypeRegistry.cs @@ -0,0 +1,17 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; + +namespace com.espertech.esper.core.service +{ + public interface StatementSemiAnonymousTypeRegistry + { + void Register(EventType semiAnonymouseType); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/service/StatementSemiAnonymousTypeRegistryImpl.cs b/NEsper.Core/NEsper.Core/core/service/StatementSemiAnonymousTypeRegistryImpl.cs new file mode 100755 index 000000000..491215c32 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/StatementSemiAnonymousTypeRegistryImpl.cs @@ -0,0 +1,18 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; + +namespace com.espertech.esper.core.service +{ + public class StatementSemiAnonymousTypeRegistryImpl : StatementSemiAnonymousTypeRegistry + { + public readonly static StatementSemiAnonymousTypeRegistryImpl INSTANCE = new StatementSemiAnonymousTypeRegistryImpl(); + public void Register(EventType anonymouseType) {} + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/service/StatementType.cs b/NEsper.Core/NEsper.Core/core/service/StatementType.cs new file mode 100755 index 000000000..cea397c4b --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/StatementType.cs @@ -0,0 +1,74 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.core.service +{ + /// Type of the statement. + public enum StatementType + { + /// Pattern statement. + PATTERN, + + /// Select statement that may contain one or more patterns. + SELECT, + + /// Insert-into statement. + INSERT_INTO, + + /// Create a named window statement. + CREATE_WINDOW, + + /// Create a variable statement. + CREATE_VARIABLE, + + /// Create a table statement. + CREATE_TABLE, + + /// Create-schema statement. + CREATE_SCHEMA, + + /// Create-index statement. + CREATE_INDEX, + + /// Create-context statement. + CREATE_CONTEXT, + + /// Create-graph statement. + CREATE_DATAFLOW, + + /// Create-expression statement. + CREATE_EXPRESSION, + + /// On-merge statement. + ON_MERGE, + + /// On-merge statement. + ON_SPLITSTREAM, + + /// On-delete statement. + ON_DELETE, + + /// On-select statement. + ON_SELECT, + + /// On-insert statement. + ON_INSERT, + + /// On-set statement. + ON_SET, + + /// On-Update statement. + ON_UPDATE, + + /// Update statement. + UPDATE, + + /// EsperIO + ESPERIO + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/StatementVariableRef.cs b/NEsper.Core/NEsper.Core/core/service/StatementVariableRef.cs new file mode 100755 index 000000000..9397e0aa4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/StatementVariableRef.cs @@ -0,0 +1,58 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.epl.expression.table; + + +namespace com.espertech.esper.core.service +{ + /// + /// Service for maintaining references between statement name and variables. + /// + public interface StatementVariableRef + { + /// Returns true if the variable is listed as in-use by any statement, or false if not + /// name + /// indicator whether variable is in use + bool IsInUse(String variableName); + + /// Returns the set of statement names that use a given variable. + /// name + /// set of statements or null if none found + ICollection GetStatementNamesForVar(String variableName); + + /// Add a reference from a statement name to a set of variables. + /// name of statement + /// types + void AddReferences(String statementName, ICollection variablesReferenced, ExprTableAccessNode[] tableNodes); + + /// Add a reference from a statement name to a single variable. + /// name of statement + /// variable + void AddReferences(String statementName, String variableReferenced); + + /// Remove all references for a given statement. + /// statement name + void RemoveReferencesStatement(String statementName); + + /// Remove all references for a given event type. + /// variable name + void RemoveReferencesVariable(String variableName); + + /// Add a preconfigured variable. + /// name + void AddConfiguredVariable(String variableName); + + /// Remove a preconfigured variable. + /// var + void RemoveConfiguredVariable(String variableName); + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/StatementVariableRefImpl.cs b/NEsper.Core/NEsper.Core/core/service/StatementVariableRefImpl.cs new file mode 100755 index 000000000..3004a0b09 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/StatementVariableRefImpl.cs @@ -0,0 +1,220 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.compat.threading; +using com.espertech.esper.epl.expression.table; +using com.espertech.esper.epl.named; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.epl.variable; + +namespace com.espertech.esper.core.service +{ + /// + /// Service for holding references between statements and their variable use. + /// + public class StatementVariableRefImpl : StatementVariableRef + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly IReaderWriterLock _mapLock; + private readonly IDictionary> _variableToStmt; + private readonly IDictionary> _stmtToVariable; + private readonly VariableService _variableService; + private readonly TableService _tableService; + private readonly NamedWindowMgmtService _namedWindowMgmtService; + private readonly ICollection _configuredVariables; + + /// Ctor. + /// variables + public StatementVariableRefImpl(VariableService variableService, TableService tableService, NamedWindowMgmtService namedWindowMgmtService) + { + _variableToStmt = new Dictionary>().WithNullSupport(); + _stmtToVariable = new Dictionary>().WithNullSupport(); + _mapLock = ReaderWriterLockManager.CreateLock(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + _variableService = variableService; + _tableService = tableService; + _namedWindowMgmtService = namedWindowMgmtService; + + _configuredVariables = new HashSet(); + foreach (var entry in variableService.VariableReadersNonCP) + { + _configuredVariables.Add(entry.Key); + } + } + + public void AddConfiguredVariable(String variableName) + { + _configuredVariables.Add(variableName); + } + + public void RemoveConfiguredVariable(String variableName) + { + _configuredVariables.Remove(variableName); + } + + public void AddReferences(String statementName, ICollection variablesReferenced, ExprTableAccessNode[] tableNodes) + { + using (_mapLock.AcquireWriteLock()) + { + if (variablesReferenced != null) + { + foreach (var reference in variablesReferenced) + { + AddReference(statementName, reference); + } + } + if (tableNodes != null) + { + foreach (var tableNode in tableNodes) + { + AddReference(statementName, tableNode.TableName); + } + } + } + } + + public void AddReferences(String statementName, String variableReferenced) + { + using (_mapLock.AcquireWriteLock()) + { + AddReference(statementName, variableReferenced); + } + } + + public void RemoveReferencesStatement(String statementName) + { + using (_mapLock.AcquireWriteLock()) + { + var variables = _stmtToVariable.Delete(statementName); + if (variables != null) + { + foreach (var variable in variables) + { + RemoveReference(statementName, variable); + } + } + } + } + + public void RemoveReferencesVariable(String name) + { + using (_mapLock.AcquireWriteLock()) + { + var statementNames = _variableToStmt.Delete(name); + if (statementNames != null) + { + foreach (var statementName in statementNames) + { + RemoveReference(statementName, name); + } + } + } + } + + public bool IsInUse(String variable) + { + using (_mapLock.AcquireReadLock()) + { + return _variableToStmt.ContainsKey(variable); + } + } + + public ICollection GetStatementNamesForVar(String variableName) + { + using (_mapLock.AcquireReadLock()) + { + var variables = _variableToStmt.Get(variableName); + if (variables == null) + { + return new string[0]; + } + return variables.AsReadOnlyCollection(); + } + } + + private void AddReference(String statementName, String variableName) + { + // add to variables + var statements = _variableToStmt.Get(variableName); + if (statements == null) + { + statements = new HashSet(); + _variableToStmt.Put(variableName, statements); + } + statements.Add(statementName); + + // add to statements + var variables = _stmtToVariable.Get(statementName); + if (variables == null) + { + variables = new HashSet(); + _stmtToVariable.Put(statementName, variables); + } + + variables.Add(variableName); + } + + private void RemoveReference(String statementName, String variableName) + { + // remove from variables + var statements = _variableToStmt.Get(variableName); + if (statements != null) + { + if (!statements.Remove(statementName)) + { + Log.Info("Failed to find statement name '" + statementName + "' in collection"); + } + + if (statements.IsEmpty()) + { + _variableToStmt.Remove(variableName); + + if (!_configuredVariables.Contains(variableName)) + { + _variableService.RemoveVariableIfFound(variableName); + _tableService.RemoveTableIfFound(variableName); + _namedWindowMgmtService.RemoveNamedWindowIfFound(variableName); + } + } + } + + // remove from statements + var variables = _stmtToVariable.Get(statementName); + if (variables != null) + { + if (!variables.Remove(variableName)) + { + Log.Info("Failed to find variable '" + variableName + "' in collection"); + } + + if (variables.IsEmpty()) + { + _stmtToVariable.Remove(statementName); + } + } + } + + /// For testing, returns the mapping of variable name to statement names. + /// mapping + protected IDictionary> VariableToStmt + { + get { return _variableToStmt; } + } + + /// For testing, returns the mapping of statement names to variable names. + /// mapping + protected IDictionary> StmtToVariable + { + get { return _stmtToVariable; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/StreamJoinAnalysisResult.cs b/NEsper.Core/NEsper.Core/core/service/StreamJoinAnalysisResult.cs new file mode 100755 index 000000000..51525b151 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/StreamJoinAnalysisResult.cs @@ -0,0 +1,233 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.compat; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.epl.virtualdw; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.service +{ + /// Analysis result for joins. + public class StreamJoinAnalysisResult + { + private readonly int _numStreams; + private readonly bool[] _isUnidirectionalInd; + private readonly bool[] _isUnidirectionalNonDriving; + private bool _isPureSelfJoin; + private readonly bool[] _hasChildViews; + private readonly bool[] _isNamedWindow; + private readonly VirtualDWViewProviderForAgentInstance[] _viewExternal; + private readonly string[][][] _uniqueKeys; + private readonly TableMetadata[] _tablesPerStream; + private bool unidirectionalAll; + + /// + /// Ctor. + /// + /// number of streams + public StreamJoinAnalysisResult(int numStreams) + { + _numStreams = numStreams; + _isPureSelfJoin = false; + _isUnidirectionalInd = new bool[numStreams]; + _isUnidirectionalNonDriving = new bool[numStreams]; + _hasChildViews = new bool[numStreams]; + _isNamedWindow = new bool[numStreams]; + _viewExternal = new VirtualDWViewProviderForAgentInstance[numStreams]; + _uniqueKeys = new string[numStreams][][]; + _tablesPerStream = new TableMetadata[numStreams]; + } + + /// + /// Returns unidirection ind. + /// + /// unidirectional flags + public bool[] UnidirectionalInd + { + get { return _isUnidirectionalInd; } + } + + /// + /// Sets flag. + /// + /// index + public void SetUnidirectionalInd(int index) { + _isUnidirectionalInd[index] = true; + } + + /// + /// Returns non-driving unidirectional streams when partial self-joins. + /// + /// indicators + public bool[] UnidirectionalNonDriving + { + get { return _isUnidirectionalNonDriving; } + } + + /// + /// Sets flag. + /// + /// index + public void SetUnidirectionalNonDriving(int index) { + _isUnidirectionalNonDriving[index] = true; + } + + /// + /// True for self-join. + /// + /// self-join + public bool IsPureSelfJoin + { + get { return _isPureSelfJoin; } + set { _isPureSelfJoin = value; } + } + + /// + /// Returns child view flags. + /// + /// flags + public bool[] HasChildViews + { + get { return _hasChildViews; } + } + + /// + /// Sets child view flags. + /// + /// to set + public void SetHasChildViews(int index) { + this._hasChildViews[index] = true; + } + + /// + /// Return named window flags. + /// + /// flags + public bool[] NamedWindow + { + get { return _isNamedWindow; } + } + + /// + /// Sets named window flag + /// + /// to set + public void SetNamedWindow(int index) { + _isNamedWindow[index] = true; + } + + /// + /// Returns streams num. + /// + /// num + public int NumStreams + { + get { return _numStreams; } + } + + public VirtualDWViewProviderForAgentInstance[] ViewExternal + { + get { return _viewExternal; } + } + + public string[][][] UniqueKeys + { + get { return _uniqueKeys; } + } + + public void SetTablesForStream(int streamNum, TableMetadata metadata) { + this._tablesPerStream[streamNum] = metadata; + } + + public TableMetadata[] TablesPerStream + { + get { return _tablesPerStream; } + } + + public void AddUniquenessInfo(ViewFactoryChain[] unmaterializedViewChain, Attribute[] annotations) + { + for (int i = 0; i < unmaterializedViewChain.Length; i++) + { + if (unmaterializedViewChain[i].DataWindowViewFactoryCount > 0) + { + var uniquenessProps = + ViewServiceHelper.GetUniqueCandidateProperties( + unmaterializedViewChain[i].FactoryChain, annotations); + if (uniquenessProps != null) + { + _uniqueKeys[i] = new string[1][]; + _uniqueKeys[i][0] = uniquenessProps.ToArray(); + } + } + } + } + + public bool IsUnidirectional + { + get + { + foreach (bool ind in _isUnidirectionalInd) + { + if (ind) + { + return true; + } + } + return false; + } + } + + public int UnidirectionalStreamNumberFirst + { + get + { + for (int i = 0; i < _isUnidirectionalInd.Length; i++) + { + if (_isUnidirectionalInd[i]) + { + return i; + } + } + throw new IllegalStateException(); + } + } + + public bool IsUnidirectionalAll + { + get + { + foreach (bool ind in _isUnidirectionalInd) + { + if (!ind) + { + return false; + } + } + return true; + } + } + + public int UnidirectionalCount + { + get + { + int count = 0; + foreach (bool ind in _isUnidirectionalInd) + { + count += ind ? 1 : 0; + } + return count; + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/service/UpdateDispatchFutureSpin.cs b/NEsper.Core/NEsper.Core/core/service/UpdateDispatchFutureSpin.cs new file mode 100755 index 000000000..d0c167523 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/UpdateDispatchFutureSpin.cs @@ -0,0 +1,96 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Reflection; +using System.Threading; +using com.espertech.esper.compat.logging; +using com.espertech.esper.dispatch; +using com.espertech.esper.timer; + +namespace com.espertech.esper.core.service +{ + /// + /// UpdateDispatchFutureSpin can be added to a dispatch queue that is thread-local. It represents + /// is a stand-in for a future dispatching of a statement result to statement listeners. + /// + /// UpdateDispatchFutureSpin is aware of future and past dispatches: + /// (newest) DF3 <--> DF2 <--> DF1 (oldest), + /// + /// and uses a spin lock to block if required + /// + public class UpdateDispatchFutureSpin : Dispatchable + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private readonly long _msecTimeout; + private readonly TimeSourceService _timeSourceService; + + private readonly UpdateDispatchViewBlockingSpin _view; + private UpdateDispatchFutureSpin _earlier; + private volatile bool _isCompleted; + + /// Ctor. + /// is the blocking dispatch view through which to execute a dispatch + /// is the older future + /// is the timeout period to wait for listeners to complete a prior dispatch + /// time source provider + public UpdateDispatchFutureSpin(UpdateDispatchViewBlockingSpin view, + UpdateDispatchFutureSpin earlier, + long msecTimeout, + TimeSourceService timeSourceService) + { + _view = view; + _earlier = earlier; + _msecTimeout = msecTimeout; + _timeSourceService = timeSourceService; + } + + /// Ctor - use for the first future to indicate completion. + /// time source provider + public UpdateDispatchFutureSpin(TimeSourceService timeSourceService) + { + _isCompleted = true; + _timeSourceService = timeSourceService; + } + + #region Dispatchable Members + + public void Execute() + { + if (!_earlier._isCompleted) + { + long spinStartTime = _timeSourceService.GetTimeMillis(); + + while (!_earlier._isCompleted) + { + Thread.Yield(); + + long spinDelta = _timeSourceService.GetTimeMillis() - spinStartTime; + if (spinDelta > _msecTimeout) + { + Log.Info("Spin wait timeout exceeded in listener dispatch for statement '" + _view.StatementResultService.StatementName + "'"); + break; + } + } + } + + _view.Execute(); + _isCompleted = true; + + _earlier = null; + } + + #endregion + + /// Returns true if the dispatch completed for this future. + /// true for completed, false if not + public bool IsCompleted() + { + return _isCompleted; + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/service/UpdateDispatchFutureWait.cs b/NEsper.Core/NEsper.Core/core/service/UpdateDispatchFutureWait.cs new file mode 100755 index 000000000..e1dc067fa --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/UpdateDispatchFutureWait.cs @@ -0,0 +1,91 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Threading; +using com.espertech.esper.compat.logging; +using com.espertech.esper.dispatch; + +namespace com.espertech.esper.core.service +{ + /// + /// UpdateDispatchFutureWait can be added to a dispatch queue that is thread-local. It + /// represents is a stand-in for a future dispatching of a statement result to statement + /// listeners. + /// + /// UpdateDispatchFutureWait is aware of future and past dispatches: + /// (newest) DF3 <--> DF2 <--> DF1 (oldest) + /// + public class UpdateDispatchFutureWait : Dispatchable + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly UpdateDispatchViewBlockingWait _view; + private UpdateDispatchFutureWait _earlier; + private UpdateDispatchFutureWait _later; + private volatile bool _isCompleted; + private readonly long _msecTimeout; + + /// Ctor. + /// is the blocking dispatch view through which to execute a dispatch + /// is the older future + /// is the timeout period to wait for listeners to complete a prior dispatch + public UpdateDispatchFutureWait(UpdateDispatchViewBlockingWait view, UpdateDispatchFutureWait earlier, long msecTimeout) + { + _view = view; + _earlier = earlier; + _msecTimeout = msecTimeout; + } + + /// Ctor - use for the first future to indicate completion. + public UpdateDispatchFutureWait() + { + _isCompleted = true; + } + + /// Returns true if the dispatch completed for this future. + /// true for completed, false if not + public bool IsCompleted + { + get { return _isCompleted; } + } + + /// Hand a later future to the dispatch to use for indicating completion via notify. + /// is the later dispatch + public void SetLater(UpdateDispatchFutureWait later) + { + _later = later; + } + + public void Execute() + { + if (!_earlier._isCompleted) + { + lock (this) + { + if (!_earlier._isCompleted) + { + Monitor.Wait(this, (int)_msecTimeout); + } + } + } + + _view.Execute(); + _isCompleted = true; + + if (_later != null) + { + lock (_later) + { + Monitor.Pulse(_later); + } + } + _earlier = null; + _later = null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/UpdateDispatchView.cs b/NEsper.Core/NEsper.Core/core/service/UpdateDispatchView.cs new file mode 100755 index 000000000..65e6b72fd --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/UpdateDispatchView.cs @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.service +{ + /// + /// Update dispatch view to indicate statement results to listeners. + /// + public interface UpdateDispatchView : View + { + /// + /// Convenience method that accepts a pair of new and old data as this is the most treated unit. + /// + /// is new data (insert stream) and old data (remove stream) + void NewResult(UniformPair result); + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/UpdateDispatchViewBase.cs b/NEsper.Core/NEsper.Core/core/service/UpdateDispatchViewBase.cs new file mode 100755 index 000000000..baf03d40d --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/UpdateDispatchViewBase.cs @@ -0,0 +1,82 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.logging; +using com.espertech.esper.compat.threading; +using com.espertech.esper.dispatch; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.service +{ + /// + /// Convenience view for dispatching view updates received from a parent view to Update listeners via the dispatch service. + /// + public abstract class UpdateDispatchViewBase + : ViewSupport + , Dispatchable + , UpdateDispatchView + { + /// Handles result delivery + public StatementResultService StatementResultService { get; protected internal set; } + + /// Dispatches events to listeners. + protected readonly DispatchService DispatchService; + + /// For iteration with patterns. + protected EventBean LastIterableEvent; + + private readonly IThreadLocal> _isDispatchWaiting = + ThreadLocalManager.Create(() => new Mutable()); + + /// Flag to indicate we have registered a dispatch. + protected Mutable IsDispatchWaiting + { + get { return _isDispatchWaiting.GetOrCreate(); } + } + + /// Ctor. + /// for performing the dispatch + /// handles result delivery + protected UpdateDispatchViewBase(StatementResultService statementResultService, DispatchService dispatchService) + { + DispatchService = dispatchService; + StatementResultService = statementResultService; + } + + public abstract void NewResult(UniformPair result); + + public override EventType EventType + { + get { return null; } + } + + public override IEnumerator GetEnumerator() + { + throw new UnsupportedOperationException(); + } + + public void Execute() + { + IsDispatchWaiting.Value = false; + StatementResultService.Execute(); + } + + /// Remove event reference to last event. + public void Clear() + { + LastIterableEvent = null; + } + + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/service/UpdateDispatchViewBlockingSpin.cs b/NEsper.Core/NEsper.Core/core/service/UpdateDispatchViewBlockingSpin.cs new file mode 100755 index 000000000..7971d190d --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/UpdateDispatchViewBlockingSpin.cs @@ -0,0 +1,67 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.logging; +using com.espertech.esper.dispatch; +using com.espertech.esper.timer; + +namespace com.espertech.esper.core.service +{ + /// + /// Convenience view for dispatching view updates received from a parent view + /// to Update listeners via the dispatch service. + /// + public class UpdateDispatchViewBlockingSpin : UpdateDispatchViewBase + { + private UpdateDispatchFutureSpin _currentFutureSpin; + private readonly long _msecTimeout; + private readonly TimeSourceService _timeSourceService; + + /// Ctor. + /// for performing the dispatch + /// timeout for preserving dispatch order through blocking + /// handles result delivery + /// time source provider + public UpdateDispatchViewBlockingSpin(StatementResultService statementResultService, DispatchService dispatchService, long msecTimeout, TimeSourceService timeSourceService) + : base(statementResultService, dispatchService) + { + _currentFutureSpin = new UpdateDispatchFutureSpin(timeSourceService); // use a completed future as a start + _msecTimeout = msecTimeout; + _timeSourceService = timeSourceService; + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + NewResult(new UniformPair(newData, oldData)); + } + + public override void NewResult(UniformPair result) + { + StatementResultService.Indicate(result); + + var isDispatchWaiting = IsDispatchWaiting; + + if (!isDispatchWaiting.Value) + { + UpdateDispatchFutureSpin nextFutureSpin; + lock (this) + { + nextFutureSpin = new UpdateDispatchFutureSpin(this, _currentFutureSpin, _msecTimeout, _timeSourceService); + _currentFutureSpin = nextFutureSpin; + } + DispatchService.AddExternal(nextFutureSpin); + isDispatchWaiting.Value = true; + } + } + + private static readonly ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/UpdateDispatchViewBlockingWait.cs b/NEsper.Core/NEsper.Core/core/service/UpdateDispatchViewBlockingWait.cs new file mode 100755 index 000000000..17ad21540 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/UpdateDispatchViewBlockingWait.cs @@ -0,0 +1,58 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.dispatch; + +namespace com.espertech.esper.core.service +{ + /// + /// Convenience view for dispatching view updates received from a parent view to Update + /// listeners via the dispatch service. + /// + public class UpdateDispatchViewBlockingWait : UpdateDispatchViewBase + { + private UpdateDispatchFutureWait _currentFutureWait; + private readonly long _msecTimeout; + + /// Ctor. + /// for performing the dispatch + /// timeout for preserving dispatch order through blocking + /// handles result delivery + public UpdateDispatchViewBlockingWait(StatementResultService statementResultServiceImpl, DispatchService dispatchService, long msecTimeout) + : base(statementResultServiceImpl, dispatchService) + { + _currentFutureWait = new UpdateDispatchFutureWait(); // use a completed future as a start + _msecTimeout = msecTimeout; + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + NewResult(new UniformPair(newData, oldData)); + } + + public override void NewResult(UniformPair results) + { + StatementResultService.Indicate(results); + + if (!IsDispatchWaiting.Value) + { + UpdateDispatchFutureWait nextFutureWait; + lock (this) + { + nextFutureWait = new UpdateDispatchFutureWait(this, _currentFutureWait, _msecTimeout); + _currentFutureWait.SetLater(nextFutureWait); + _currentFutureWait = nextFutureWait; + } + DispatchService.AddExternal(nextFutureWait); + IsDispatchWaiting.Value = true; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/UpdateDispatchViewNonBlocking.cs b/NEsper.Core/NEsper.Core/core/service/UpdateDispatchViewNonBlocking.cs new file mode 100755 index 000000000..61b7a573c --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/UpdateDispatchViewNonBlocking.cs @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.logging; +using com.espertech.esper.dispatch; + +namespace com.espertech.esper.core.service +{ + /// + /// Convenience view for dispatching view updates received from a parent view to Update + /// listeners via the dispatch service. + /// + public class UpdateDispatchViewNonBlocking : UpdateDispatchViewBase + { + /// + /// Ctor. + /// + /// handles result delivery + /// for performing the dispatch + public UpdateDispatchViewNonBlocking(StatementResultService statementResultServiceImpl, + DispatchService dispatchService) + : base(statementResultServiceImpl, dispatchService) + { + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + NewResult(new UniformPair(newData, oldData)); + } + + public override void NewResult(UniformPair results) + { + StatementResultService.Indicate(results); + if (!IsDispatchWaiting.Value) + { + DispatchService.AddExternal(this); + IsDispatchWaiting.Value = true; + } + } + + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/multimatch/MultiMatchHandler.cs b/NEsper.Core/NEsper.Core/core/service/multimatch/MultiMatchHandler.cs new file mode 100755 index 000000000..8da0e5e2f --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/multimatch/MultiMatchHandler.cs @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.filter; + +namespace com.espertech.esper.core.service.multimatch +{ + public interface MultiMatchHandler + { + void Handle(ICollection callbacks, EventBean theEvent); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/service/multimatch/MultiMatchHandlerFactory.cs b/NEsper.Core/NEsper.Core/core/service/multimatch/MultiMatchHandlerFactory.cs new file mode 100755 index 000000000..e98dda558 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/multimatch/MultiMatchHandlerFactory.cs @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.core.service.multimatch +{ + public interface MultiMatchHandlerFactory + { + MultiMatchHandler GetDefaultHandler(); + MultiMatchHandler MakeNoDedupNoSubq(); + MultiMatchHandler MakeNoDedupSubselectPreval(); + MultiMatchHandler MakeNoDedupSubselectPosteval(); + MultiMatchHandler MakeDedupNoSubq(); + MultiMatchHandler MakeDedupSubq(bool isSubselectPreeval); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/service/multimatch/MultiMatchHandlerFactoryImpl.cs b/NEsper.Core/NEsper.Core/core/service/multimatch/MultiMatchHandlerFactoryImpl.cs new file mode 100755 index 000000000..50593aa7a --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/multimatch/MultiMatchHandlerFactoryImpl.cs @@ -0,0 +1,43 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.core.service.multimatch +{ + public class MultiMatchHandlerFactoryImpl : MultiMatchHandlerFactory + { + public MultiMatchHandler GetDefaultHandler() + { + return MultiMatchHandlerSubqueryPreevalNoDedup.INSTANCE; + } + + public MultiMatchHandler MakeNoDedupNoSubq() + { + return MultiMatchHandlerNoSubqueryNoDedup.INSTANCE; + } + + public MultiMatchHandler MakeNoDedupSubselectPreval() + { + return MultiMatchHandlerSubqueryPreevalNoDedup.INSTANCE; + } + + public MultiMatchHandler MakeNoDedupSubselectPosteval() + { + return MultiMatchHandlerSubqueryPostevalNoDedup.INSTANCE; + } + + public MultiMatchHandler MakeDedupNoSubq() + { + return MultiMatchHandlerNoSubqueryWDedup.INSTANCE; + } + + public MultiMatchHandler MakeDedupSubq(bool isSubselectPreeval) + { + return new MultiMatchHandlerSubqueryWDedup(isSubselectPreeval); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/service/multimatch/MultiMatchHandlerNoSubqueryNoDedup.cs b/NEsper.Core/NEsper.Core/core/service/multimatch/MultiMatchHandlerNoSubqueryNoDedup.cs new file mode 100755 index 000000000..c3747c51e --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/multimatch/MultiMatchHandlerNoSubqueryNoDedup.cs @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.filter; + +namespace com.espertech.esper.core.service.multimatch +{ + public class MultiMatchHandlerNoSubqueryNoDedup : MultiMatchHandler + { + public static readonly MultiMatchHandlerNoSubqueryNoDedup INSTANCE = new MultiMatchHandlerNoSubqueryNoDedup(); + + private MultiMatchHandlerNoSubqueryNoDedup() + { + } + + public void Handle(ICollection callbacks, EventBean theEvent) + { + foreach (FilterHandleCallback callback in callbacks) + { + callback.MatchFound(theEvent, callbacks); + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/service/multimatch/MultiMatchHandlerNoSubqueryWDedup.cs b/NEsper.Core/NEsper.Core/core/service/multimatch/MultiMatchHandlerNoSubqueryWDedup.cs new file mode 100755 index 000000000..06cda60f5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/multimatch/MultiMatchHandlerNoSubqueryWDedup.cs @@ -0,0 +1,82 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.threading; +using com.espertech.esper.filter; + +namespace com.espertech.esper.core.service.multimatch +{ + public class MultiMatchHandlerNoSubqueryWDedup : MultiMatchHandler + { + public static readonly MultiMatchHandlerNoSubqueryWDedup INSTANCE = new MultiMatchHandlerNoSubqueryWDedup(); + + private MultiMatchHandlerNoSubqueryWDedup() + { + } + + internal static readonly IThreadLocal> Dedups = ThreadLocalManager.Create( + () => new LinkedHashSet()); + + public void Handle(ICollection callbacks, EventBean theEvent) + { + if (callbacks.Count >= 8) + { + var dedup = Dedups.GetOrCreate(); + dedup.Clear(); + dedup.AddAll(callbacks); + foreach (var callback in dedup) + { + callback.MatchFound(theEvent, callbacks); + } + dedup.Clear(); + } + else + { + var count = 0; + foreach (var callback in callbacks) + { + var haveInvoked = CheckDup(callback, callbacks, count); + if (!haveInvoked) + { + callback.MatchFound(theEvent, callbacks); + } + count++; + } + } + } + + private bool CheckDup(FilterHandleCallback callback, IEnumerable callbacks, int count) + { + if (count < 1) + { + return false; + } + + var index = 0; + foreach (var candidate in callbacks) + { + if (candidate == callback) + { + return true; + } + + index++; + if (index == count) + { + break; + } + } + + return false; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/service/multimatch/MultiMatchHandlerSubqueryPostevalNoDedup.cs b/NEsper.Core/NEsper.Core/core/service/multimatch/MultiMatchHandlerSubqueryPostevalNoDedup.cs new file mode 100755 index 000000000..9a879016c --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/multimatch/MultiMatchHandlerSubqueryPostevalNoDedup.cs @@ -0,0 +1,43 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.filter; + +namespace com.espertech.esper.core.service.multimatch +{ + public class MultiMatchHandlerSubqueryPostevalNoDedup : MultiMatchHandler + { + public static readonly MultiMatchHandlerSubqueryPostevalNoDedup INSTANCE = + new MultiMatchHandlerSubqueryPostevalNoDedup(); + + private MultiMatchHandlerSubqueryPostevalNoDedup() + { + } + + public void Handle(ICollection callbacks, EventBean theEvent) + { + foreach (FilterHandleCallback callback in callbacks) + { + if (!callback.IsSubSelect) + { + callback.MatchFound(theEvent, callbacks); + } + } + foreach (FilterHandleCallback callback in callbacks) + { + if (callback.IsSubSelect) + { + callback.MatchFound(theEvent, callbacks); + } + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/service/multimatch/MultiMatchHandlerSubqueryPreevalNoDedup.cs b/NEsper.Core/NEsper.Core/core/service/multimatch/MultiMatchHandlerSubqueryPreevalNoDedup.cs new file mode 100755 index 000000000..5fbcec226 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/multimatch/MultiMatchHandlerSubqueryPreevalNoDedup.cs @@ -0,0 +1,44 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.filter; + +namespace com.espertech.esper.core.service.multimatch +{ + public class MultiMatchHandlerSubqueryPreevalNoDedup : MultiMatchHandler + { + public static readonly MultiMatchHandlerSubqueryPreevalNoDedup INSTANCE = + new MultiMatchHandlerSubqueryPreevalNoDedup(); + + private MultiMatchHandlerSubqueryPreevalNoDedup() + { + } + + public void Handle(ICollection callbacks, EventBean theEvent) + { + foreach (FilterHandleCallback callback in callbacks) + { + if (callback.IsSubSelect) + { + callback.MatchFound(theEvent, callbacks); + } + } + + foreach (FilterHandleCallback callback in callbacks) + { + if (!callback.IsSubSelect) + { + callback.MatchFound(theEvent, callbacks); + } + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/service/multimatch/MultiMatchHandlerSubqueryWDedup.cs b/NEsper.Core/NEsper.Core/core/service/multimatch/MultiMatchHandlerSubqueryWDedup.cs new file mode 100755 index 000000000..8f7b054fb --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/multimatch/MultiMatchHandlerSubqueryWDedup.cs @@ -0,0 +1,75 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.filter; + +namespace com.espertech.esper.core.service.multimatch +{ + public class MultiMatchHandlerSubqueryWDedup : MultiMatchHandler + { + private readonly bool _subselectPreeval; + + public MultiMatchHandlerSubqueryWDedup(bool subselectPreeval) + { + _subselectPreeval = subselectPreeval; + } + + public void Handle(ICollection callbacks, EventBean theEvent) + { + + var dedup = MultiMatchHandlerNoSubqueryWDedup.Dedups.GetOrCreate(); + dedup.Clear(); + dedup.AddAll(callbacks); + + if (_subselectPreeval) + { + // sub-selects always go first + foreach (var callback in dedup) + { + if (callback.IsSubSelect) + { + callback.MatchFound(theEvent, dedup); + } + } + + foreach (var callback in dedup) + { + if (!callback.IsSubSelect) + { + callback.MatchFound(theEvent, dedup); + } + } + } + else + { + // sub-selects always go last + foreach (var callback in dedup) + { + if (!callback.IsSubSelect) + { + callback.MatchFound(theEvent, dedup); + } + } + + foreach (var callback in dedup) + { + if (callback.IsSubSelect) + { + callback.MatchFound(theEvent, dedup); + } + } + } + + dedup.Clear(); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/service/resource/StatementResourceExtension.cs b/NEsper.Core/NEsper.Core/core/service/resource/StatementResourceExtension.cs new file mode 100755 index 000000000..229acdb6c --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/resource/StatementResourceExtension.cs @@ -0,0 +1,14 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.core.service.resource +{ + public interface StatementResourceExtension + { + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/service/resource/StatementResourceHolder.cs b/NEsper.Core/NEsper.Core/core/service/resource/StatementResourceHolder.cs new file mode 100755 index 000000000..d6111313c --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/resource/StatementResourceHolder.cs @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.threading; +using com.espertech.esper.core.context.factory; +using com.espertech.esper.core.context.subselect; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.subquery; +using com.espertech.esper.epl.named; +using com.espertech.esper.pattern; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.service.resource +{ + public class StatementResourceHolder + { + public StatementResourceHolder(AgentInstanceContext agentInstanceContext) + { + AgentInstanceContext = agentInstanceContext; + SubselectStrategies = Collections.GetEmptyMap(); + } + + public AgentInstanceContext AgentInstanceContext { get; internal set; } + + public Viewable[] TopViewables { get; internal set; } + + public Viewable[] EventStreamViewables { get; internal set; } + + public EvalRootState[] PatternRoots { get; internal set; } + + public AggregationService AggregationService { get; internal set; } + + public IDictionary SubselectStrategies { get; internal set; } + + public StatementAgentInstancePostLoad PostLoad { get; internal set; } + + public NamedWindowProcessorInstance NamedWindowProcessorInstance { get; internal set; } + + public StatementResourceExtension StatementResourceExtension { get; internal set; } + } +} diff --git a/NEsper.Core/NEsper.Core/core/service/resource/StatementResourceHolderUtil.cs b/NEsper.Core/NEsper.Core/core/service/resource/StatementResourceHolderUtil.cs new file mode 100755 index 000000000..47ded75e7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/resource/StatementResourceHolderUtil.cs @@ -0,0 +1,54 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.factory; +using com.espertech.esper.pattern; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.service.resource +{ + public class StatementResourceHolderUtil + { + public static StatementResourceHolder PopulateHolder(StatementAgentInstanceFactoryResult startResult) + { + StatementResourceHolder holder = new StatementResourceHolder(startResult.AgentInstanceContext); + + if (startResult is StatementAgentInstanceFactorySelectResult) { + StatementAgentInstanceFactorySelectResult selectResult = (StatementAgentInstanceFactorySelectResult) startResult; + holder.TopViewables = selectResult.TopViews; + holder.EventStreamViewables = selectResult.EventStreamViewables; + holder.PatternRoots = selectResult.PatternRoots; + holder.AggregationService = selectResult.OptionalAggegationService; + holder.SubselectStrategies = selectResult.SubselectStrategies; + holder.PostLoad = selectResult.OptionalPostLoadJoin; + } + else if (startResult is StatementAgentInstanceFactoryCreateWindowResult) { + StatementAgentInstanceFactoryCreateWindowResult createResult = (StatementAgentInstanceFactoryCreateWindowResult) startResult; + holder.TopViewables = new Viewable[] {createResult.TopView}; + holder.PostLoad = createResult.PostLoad; + holder.NamedWindowProcessorInstance = createResult.ProcessorInstance; + } + else if (startResult is StatementAgentInstanceFactoryCreateTableResult) { + StatementAgentInstanceFactoryCreateTableResult createResult = (StatementAgentInstanceFactoryCreateTableResult) startResult; + holder.TopViewables = new Viewable[] {createResult.FinalView}; + holder.AggregationService = createResult.OptionalAggegationService; + } + else if (startResult is StatementAgentInstanceFactoryOnTriggerResult) { + StatementAgentInstanceFactoryOnTriggerResult onTriggerResult = (StatementAgentInstanceFactoryOnTriggerResult) startResult; + holder.PatternRoots = new EvalRootState[] {onTriggerResult.OptPatternRoot}; + holder.AggregationService = onTriggerResult.OptionalAggegationService; + holder.SubselectStrategies = onTriggerResult.SubselectStrategies; + } + return holder; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/service/resource/StatementResourceService.cs b/NEsper.Core/NEsper.Core/core/service/resource/StatementResourceService.cs new file mode 100755 index 000000000..5d815140d --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/service/resource/StatementResourceService.cs @@ -0,0 +1,114 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.threading; +using com.espertech.esper.core.context.factory; +using com.espertech.esper.core.context.mgr; +using com.espertech.esper.pattern; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.service.resource +{ + public class StatementResourceService + { + public StatementResourceHolder ResourcesUnpartitioned { get; private set; } + + public IDictionary ResourcesPartitioned { get; private set; } + + public IDictionary ContextEndEndpoints { get; private set; } + + public IDictionary ContextStartEndpoints { get; private set; } + + public StatementResourceService(bool partitioned) + { + if (partitioned) + { + ResourcesPartitioned = new Dictionary(); + } + } + + public void StartContextPattern(EvalRootState patternStopCallback, bool startEndpoint, ContextStatePathKey path) + { + AddContextPattern(patternStopCallback, startEndpoint, path); + } + + public void StopContextPattern(bool startEndpoint, ContextStatePathKey path) + { + RemoveContextPattern(startEndpoint, path); + } + + public StatementResourceHolder GetPartitioned(int agentInstanceId) + { + return ResourcesPartitioned.Get(agentInstanceId); + } + + public StatementResourceHolder Unpartitioned + { + get { return ResourcesUnpartitioned; } + set { ResourcesUnpartitioned = value; } + } + + public void SetPartitioned(int agentInstanceId, StatementResourceHolder statementResourceHolder) + { + ResourcesPartitioned.Put(agentInstanceId, statementResourceHolder); + } + + public StatementResourceHolder DeallocatePartitioned(int agentInstanceId) + { + return ResourcesPartitioned.Delete(agentInstanceId); + } + + public StatementResourceHolder DeallocateUnpartitioned() + { + StatementResourceHolder unpartitioned = ResourcesUnpartitioned; + ResourcesUnpartitioned = null; + return unpartitioned; + } + + private void RemoveContextPattern(bool startEndpoint, ContextStatePathKey path) + { + if (startEndpoint) + { + if (ContextStartEndpoints != null) + { + ContextStartEndpoints.Remove(path); + } + } + else + { + if (ContextEndEndpoints != null) + { + ContextEndEndpoints.Remove(path); + } + } + } + + private void AddContextPattern(EvalRootState rootState, bool startEndpoint, ContextStatePathKey path) + { + if (startEndpoint) + { + if (ContextStartEndpoints == null) + { + ContextStartEndpoints = new Dictionary(); + } + ContextStartEndpoints.Put(path, rootState); + } + else + { + if (ContextEndEndpoints == null) + { + ContextEndEndpoints = new Dictionary(); + } + ContextEndEndpoints.Put(path, rootState); + } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/start/EPPreparedExecuteIUDInsertInto.cs b/NEsper.Core/NEsper.Core/core/start/EPPreparedExecuteIUDInsertInto.cs new file mode 100755 index 000000000..e4e7c980b --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/start/EPPreparedExecuteIUDInsertInto.cs @@ -0,0 +1,136 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Linq; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.named; +using com.espertech.esper.epl.spec; +using com.espertech.esper.filter; +using com.espertech.esper.util; + +namespace com.espertech.esper.core.start +{ + /// Starts and provides the stop method for EPL statements. + public class EPPreparedExecuteIUDInsertInto : EPPreparedExecuteIUDSingleStream + { + public EPPreparedExecuteIUDInsertInto(StatementSpecCompiled statementSpec, EPServicesContext services, StatementContext statementContext) + : base(AssociatedFromClause(statementSpec), services, statementContext) + { + } + + private static StatementSpecCompiled AssociatedFromClause(StatementSpecCompiled statementSpec) { + if (statementSpec.FilterRootNode != null || + statementSpec.StreamSpecs.Length > 0 || + statementSpec.HavingExprRootNode != null || + statementSpec.OutputLimitSpec != null || + statementSpec.ForClauseSpec != null || + statementSpec.MatchRecognizeSpec != null || + statementSpec.OrderByList.Length > 0 || + statementSpec.RowLimitSpec != null) { + throw new ExprValidationException("Insert-into fire-and-forget query can only consist of an insert-into clause and a select-clause"); + } + + var namedWindowName = statementSpec.InsertIntoDesc.EventTypeName; + var namedWindowStream = new NamedWindowConsumerStreamSpec(namedWindowName, null, new ViewSpec[0], Collections.GetEmptyList(), + StreamSpecOptions.DEFAULT, null); + statementSpec.StreamSpecs = new StreamSpecCompiled[]{namedWindowStream}; + return statementSpec; + } + + public override EPPreparedExecuteIUDSingleStreamExec GetExecutor(FilterSpecCompiled filter, string aliasName) + { + var statementSpec = base.StatementSpec; + var statementContext = base.StatementContext; + var selectNoWildcard = NamedWindowOnMergeHelper.CompileSelectNoWildcard(UuidGenerator.Generate(), statementSpec.SelectClauseSpec.SelectExprList); + var streamTypeService = new StreamTypeServiceImpl(statementContext.EngineURI, true); + var exprEvaluatorContextStatement = new ExprEvaluatorContextStatement(statementContext, true); + + // assign names + var validationContext = new ExprValidationContext( + streamTypeService, + statementContext.EngineImportService, + statementContext.StatementExtensionServicesContext, null, + statementContext.TimeProvider, + statementContext.VariableService, + statementContext.TableService, + exprEvaluatorContextStatement, + statementContext.EventAdapterService, + statementContext.StatementName, + statementContext.StatementId, + statementContext.Annotations, + statementContext.ContextDescriptor, + statementContext.ScriptingService, + false, false, true, false, null, false); + + var processor = base.Processor; + + // determine whether column names are provided + // if the "values" keyword was used, allow sequential automatic name assignment + string[] assignedSequentialNames = null; + if (statementSpec.InsertIntoDesc.ColumnNames.IsEmpty()) { + var insert = (FireAndForgetSpecInsert) statementSpec.FireAndForgetSpec; + if (insert.IsUseValuesKeyword) { + assignedSequentialNames = processor.EventTypePublic.PropertyNames; + } + } + + var count = -1; + foreach (var compiled in statementSpec.SelectClauseSpec.SelectExprList) { + count++; + if (compiled is SelectClauseExprCompiledSpec) { + var expr = (SelectClauseExprCompiledSpec) compiled; + var validatedExpression = ExprNodeUtility.GetValidatedSubtree(ExprNodeOrigin.SELECT, expr.SelectExpression, validationContext); + expr.SelectExpression = validatedExpression; + if (expr.AssignedName == null) { + if (expr.ProvidedName == null) { + if (assignedSequentialNames != null && count < assignedSequentialNames.Length) { + expr.AssignedName = assignedSequentialNames[count]; + } else { + expr.AssignedName = ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(expr.SelectExpression); + } + } else { + expr.AssignedName = expr.ProvidedName; + } + } + } + } + + var optionalInsertIntoEventType = processor.EventTypeResultSetProcessor; + var selectExprEventTypeRegistry = new SelectExprEventTypeRegistry(statementContext.StatementName, statementContext.StatementEventTypeRef); + var insertHelper = SelectExprProcessorFactory.GetProcessor( + Collections.SingletonList(0), + selectNoWildcard.ToArray(), false, + statementSpec.InsertIntoDesc, optionalInsertIntoEventType, null, streamTypeService, + statementContext.EventAdapterService, + statementContext.StatementResultService, + statementContext.ValueAddEventService, + selectExprEventTypeRegistry, + statementContext.EngineImportService, + exprEvaluatorContextStatement, + statementContext.VariableService, + statementContext.ScriptingService, + statementContext.TableService, + statementContext.TimeProvider, + statementContext.EngineURI, + statementContext.StatementId, + statementContext.StatementName, + statementContext.Annotations, + statementContext.ContextDescriptor, + statementContext.ConfigSnapshot, null, + statementContext.NamedWindowMgmtService, null, null, + statementContext.StatementExtensionServicesContext); + + return new EPPreparedExecuteIUDSingleStreamExecInsert(exprEvaluatorContextStatement, insertHelper, statementSpec.TableNodes, base.Services); + } + + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/start/EPPreparedExecuteIUDSingleStream.cs b/NEsper.Core/NEsper.Core/core/start/EPPreparedExecuteIUDSingleStream.cs new file mode 100755 index 000000000..2a57c3799 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/start/EPPreparedExecuteIUDSingleStream.cs @@ -0,0 +1,227 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Linq; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.client.context; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.spec; +using com.espertech.esper.filter; +using com.espertech.esper.util; + +namespace com.espertech.esper.core.start +{ + /// + /// Starts and provides the stop method for EPL statements. + /// + public abstract class EPPreparedExecuteIUDSingleStream : EPPreparedExecuteMethod + { + private static readonly ILog QueryPlanLog = LogManager.GetLogger(AuditPath.QUERYPLAN_LOG); + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + protected readonly StatementSpecCompiled StatementSpec; + protected readonly FireAndForgetProcessor Processor; + protected readonly EPServicesContext Services; + protected readonly EPPreparedExecuteIUDSingleStreamExec Executor; + protected readonly StatementContext StatementContext; + protected bool HasTableAccess; + + public abstract EPPreparedExecuteIUDSingleStreamExec GetExecutor(FilterSpecCompiled filter, string aliasName); + + /// + /// Ctor. + /// + /// is a container for the definition of all statement constructs thatmay have been used in the statement, i.e. if defines the select clauses, insert into, outer joins etc. + /// + /// is the service instances for dependency injection + /// is statement-level information and statement services + /// com.espertech.esper.epl.expression.core.ExprValidationException if the preparation failed + public EPPreparedExecuteIUDSingleStream( + StatementSpecCompiled statementSpec, + EPServicesContext services, + StatementContext statementContext) + { + var queryPlanLogging = services.ConfigSnapshot.EngineDefaults.Logging.IsEnableQueryPlan; + if (queryPlanLogging) + { + QueryPlanLog.Info("Query plans for Fire-and-forget query '" + statementContext.Expression + "'"); + } + + HasTableAccess = statementSpec.IntoTableSpec != null || + (statementSpec.TableNodes != null && statementSpec.TableNodes.Length > 0); + if (statementSpec.InsertIntoDesc != null && services.TableService.GetTableMetadata(statementSpec.InsertIntoDesc.EventTypeName) != null) + { + HasTableAccess = true; + } + if (statementSpec.FireAndForgetSpec is FireAndForgetSpecUpdate || + statementSpec.FireAndForgetSpec is FireAndForgetSpecDelete) + { + HasTableAccess |= statementSpec.StreamSpecs[0] is TableQueryStreamSpec; + } + + StatementSpec = statementSpec; + Services = services; + StatementContext = statementContext; + + // validate general FAF criteria + EPPreparedExecuteMethodHelper.ValidateFAFQuery(statementSpec); + + // obtain processor + var streamSpec = statementSpec.StreamSpecs[0]; + Processor = FireAndForgetProcessorFactory.ValidateResolveProcessor(streamSpec, services); + + // obtain name and type + var processorName = Processor.NamedWindowOrTableName; + var eventType = Processor.EventTypeResultSetProcessor; + + // determine alias + var aliasName = processorName; + if (streamSpec.OptionalStreamName != null) + { + aliasName = streamSpec.OptionalStreamName; + } + + // compile filter to optimize access to named window + var typeService = new StreamTypeServiceImpl(new EventType[] { eventType }, new string[] { aliasName }, new bool[] { true }, services.EngineURI, true); + FilterSpecCompiled filter; + if (statementSpec.FilterRootNode != null) + { + var tagged = new LinkedHashMap>(); + FilterSpecCompiled filterCompiled; + try + { + filterCompiled = FilterSpecCompiler.MakeFilterSpec(eventType, aliasName, + Collections.SingletonList(statementSpec.FilterRootNode), null, + tagged, tagged, typeService, + null, statementContext, + Collections.SingletonList(0)); + } + catch (Exception ex) + { + Log.Warn("Unexpected exception analyzing filter paths: " + ex.Message, ex); + filterCompiled = null; + } + filter = filterCompiled; + } + else + { + filter = null; + } + + // validate expressions + EPStatementStartMethodHelperValidate.ValidateNodes(statementSpec, statementContext, typeService, null); + + // get executor + Executor = GetExecutor(filter, aliasName); + } + + /// + /// Returns the event type of the prepared statement. + /// + /// event type + public EventType EventType + { + get { return Processor.EventTypeResultSetProcessor; } + } + + /// + /// Executes the prepared query. + /// + /// query results + public EPPreparedQueryResult Execute(ContextPartitionSelector[] contextPartitionSelectors) + { + try + { + if (contextPartitionSelectors != null && contextPartitionSelectors.Length != 1) + { + throw new ArgumentException("Number of context partition selectors must be one"); + } + var optionalSingleSelector = contextPartitionSelectors != null && contextPartitionSelectors.Length > 0 ? contextPartitionSelectors[0] : null; + + // validate context + if (Processor.ContextName != null && + StatementSpec.OptionalContextName != null && + !Processor.ContextName.Equals(StatementSpec.OptionalContextName)) + { + throw new EPException("Context for named window is '" + Processor.ContextName + "' and query specifies context '" + StatementSpec.OptionalContextName + "'"); + } + + // handle non-specified context + if (StatementSpec.OptionalContextName == null) + { + FireAndForgetInstance processorInstance = Processor.GetProcessorInstanceNoContext(); + if (processorInstance != null) + { + var rows = Executor.Execute(processorInstance); + if (rows != null && rows.Length > 0) + { + Dispatch(); + } + return new EPPreparedQueryResult(Processor.EventTypePublic, rows); + } + } + + // context partition runtime query + var agentInstanceIds = EPPreparedExecuteMethodHelper.GetAgentInstanceIds(Processor, optionalSingleSelector, Services.ContextManagementService, Processor.ContextName); + + // collect events and agent instances + if (agentInstanceIds.IsEmpty()) + { + return new EPPreparedQueryResult(Processor.EventTypeResultSetProcessor, CollectionUtil.EVENTBEANARRAY_EMPTY); + } + + if (agentInstanceIds.Count == 1) + { + int agentInstanceId = agentInstanceIds.First(); + var processorInstance = Processor.GetProcessorInstanceContextById(agentInstanceId); + var rows = Executor.Execute(processorInstance); + if (rows.Length > 0) + { + Dispatch(); + } + return new EPPreparedQueryResult(Processor.EventTypeResultSetProcessor, rows); + } + + var allRows = new ArrayDeque(); + foreach (int agentInstanceId in agentInstanceIds) + { + var processorInstance = Processor.GetProcessorInstanceContextById(agentInstanceId); + if (processorInstance != null) + { + var rows = Executor.Execute(processorInstance); + allRows.AddAll(rows); + } + } + if (allRows.Count > 0) + { + Dispatch(); + } + return new EPPreparedQueryResult(Processor.EventTypeResultSetProcessor, allRows.ToArray()); + } + finally + { + if (HasTableAccess) + { + Services.TableService.TableExprEvaluatorContext.ReleaseAcquiredLocks(); + } + } + } + + protected void Dispatch() + { + Services.InternalEventEngineRouteDest.ProcessThreadWorkQueue(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/start/EPPreparedExecuteIUDSingleStreamDelete.cs b/NEsper.Core/NEsper.Core/core/start/EPPreparedExecuteIUDSingleStreamDelete.cs new file mode 100755 index 000000000..0b8a8aa5d --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/start/EPPreparedExecuteIUDSingleStreamDelete.cs @@ -0,0 +1,31 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.service; +using com.espertech.esper.epl.spec; +using com.espertech.esper.filter; + +namespace com.espertech.esper.core.start +{ + /// + /// Starts and provides the stop method for EPL statements. + /// + public class EPPreparedExecuteIUDSingleStreamDelete : EPPreparedExecuteIUDSingleStream + { + public EPPreparedExecuteIUDSingleStreamDelete(StatementSpecCompiled statementSpec, EPServicesContext services, StatementContext statementContext) + : base(statementSpec, services, statementContext) + { + + } + + public override EPPreparedExecuteIUDSingleStreamExec GetExecutor(FilterSpecCompiled filter, string aliasName) + { + return new EPPreparedExecuteIUDSingleStreamExecDelete(filter, StatementSpec.FilterRootNode, StatementSpec.Annotations, StatementSpec.TableNodes, Services); + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/start/EPPreparedExecuteIUDSingleStreamExec.cs b/NEsper.Core/NEsper.Core/core/start/EPPreparedExecuteIUDSingleStreamExec.cs new file mode 100755 index 000000000..48fb61486 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/start/EPPreparedExecuteIUDSingleStreamExec.cs @@ -0,0 +1,21 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.core.start +{ + public interface EPPreparedExecuteIUDSingleStreamExec + { + EventBean[] Execute(FireAndForgetInstance instance); + } +} diff --git a/NEsper.Core/NEsper.Core/core/start/EPPreparedExecuteIUDSingleStreamExecDelete.cs b/NEsper.Core/NEsper.Core/core/start/EPPreparedExecuteIUDSingleStreamExecDelete.cs new file mode 100755 index 000000000..8a8f1b43e --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/start/EPPreparedExecuteIUDSingleStreamExecDelete.cs @@ -0,0 +1,68 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.table; +using com.espertech.esper.filter; + +namespace com.espertech.esper.core.start +{ + public class EPPreparedExecuteIUDSingleStreamExecDelete : EPPreparedExecuteIUDSingleStreamExec + { + private readonly FilterSpecCompiled _filter; + private readonly ExprNode _optionalWhereClause; + private readonly Attribute[] _annotations; + private readonly ExprTableAccessNode[] _optionalTableNodes; + private readonly EPServicesContext _services; + + public EPPreparedExecuteIUDSingleStreamExecDelete(FilterSpecCompiled filter, ExprNode optionalWhereClause, Attribute[] annotations, ExprTableAccessNode[] optionalTableNodes, EPServicesContext services) + { + _filter = filter; + _optionalWhereClause = optionalWhereClause; + _annotations = annotations; + _optionalTableNodes = optionalTableNodes; + _services = services; + } + + public EventBean[] Execute(FireAndForgetInstance fireAndForgetProcessorInstance) + { + return fireAndForgetProcessorInstance.ProcessDelete(this); + } + + public FilterSpecCompiled Filter + { + get { return _filter; } + } + + public ExprNode OptionalWhereClause + { + get { return _optionalWhereClause; } + } + + public Attribute[] Annotations + { + get { return _annotations; } + } + + public ExprTableAccessNode[] OptionalTableNodes + { + get { return _optionalTableNodes; } + } + + public EPServicesContext Services + { + get { return _services; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/start/EPPreparedExecuteIUDSingleStreamExecInsert.cs b/NEsper.Core/NEsper.Core/core/start/EPPreparedExecuteIUDSingleStreamExecInsert.cs new file mode 100755 index 000000000..95b2a5613 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/start/EPPreparedExecuteIUDSingleStreamExecInsert.cs @@ -0,0 +1,57 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.table; + +namespace com.espertech.esper.core.start +{ + public class EPPreparedExecuteIUDSingleStreamExecInsert : EPPreparedExecuteIUDSingleStreamExec + { + private readonly ExprEvaluatorContext _exprEvaluatorContext; + private readonly SelectExprProcessor _insertHelper; + private readonly ExprTableAccessNode[] _optionalTableNodes; + private readonly EPServicesContext _services; + + public EPPreparedExecuteIUDSingleStreamExecInsert(ExprEvaluatorContext exprEvaluatorContext, SelectExprProcessor insertHelper, ExprTableAccessNode[] optionalTableNodes, EPServicesContext services) + { + _exprEvaluatorContext = exprEvaluatorContext; + _insertHelper = insertHelper; + _optionalTableNodes = optionalTableNodes; + _services = services; + } + + public EventBean[] Execute(FireAndForgetInstance fireAndForgetProcessorInstance) + { + return fireAndForgetProcessorInstance.ProcessInsert(this); + } + + public ExprEvaluatorContext ExprEvaluatorContext + { + get { return _exprEvaluatorContext; } + } + + public SelectExprProcessor InsertHelper + { + get { return _insertHelper; } + } + + public ExprTableAccessNode[] OptionalTableNodes + { + get { return _optionalTableNodes; } + } + + public EPServicesContext Services + { + get { return _services; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/start/EPPreparedExecuteIUDSingleStreamExecUpdate.cs b/NEsper.Core/NEsper.Core/core/start/EPPreparedExecuteIUDSingleStreamExecUpdate.cs new file mode 100755 index 000000000..2085e9f5f --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/start/EPPreparedExecuteIUDSingleStreamExecUpdate.cs @@ -0,0 +1,55 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.table; +using com.espertech.esper.epl.table.upd; +using com.espertech.esper.epl.updatehelper; +using com.espertech.esper.filter; + +namespace com.espertech.esper.core.start +{ + public class EPPreparedExecuteIUDSingleStreamExecUpdate : EPPreparedExecuteIUDSingleStreamExec + { + public EPPreparedExecuteIUDSingleStreamExecUpdate(FilterSpecCompiled filter, ExprNode optionalWhereClause, Attribute[] annotations, EventBeanUpdateHelper updateHelper, TableUpdateStrategy tableUpdateStrategy, ExprTableAccessNode[] optionalTableNodes, EPServicesContext services) + { + Filter = filter; + OptionalWhereClause = optionalWhereClause; + Annotations = annotations; + UpdateHelper = updateHelper; + TableUpdateStrategy = tableUpdateStrategy; + OptionalTableNodes = optionalTableNodes; + Services = services; + } + + public EventBean[] Execute(FireAndForgetInstance fireAndForgetProcessorInstance) + { + return fireAndForgetProcessorInstance.ProcessUpdate(this); + } + + public FilterSpecCompiled Filter { get; private set; } + + public ExprNode OptionalWhereClause { get; private set; } + + public Attribute[] Annotations { get; private set; } + + public EventBeanUpdateHelper UpdateHelper { get; private set; } + + public TableUpdateStrategy TableUpdateStrategy { get; private set; } + + public ExprTableAccessNode[] OptionalTableNodes { get; private set; } + + public EPServicesContext Services { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/core/start/EPPreparedExecuteIUDSingleStreamUpdate.cs b/NEsper.Core/NEsper.Core/core/start/EPPreparedExecuteIUDSingleStreamUpdate.cs new file mode 100755 index 000000000..8f5d407fb --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/start/EPPreparedExecuteIUDSingleStreamUpdate.cs @@ -0,0 +1,83 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.table.upd; +using com.espertech.esper.epl.updatehelper; +using com.espertech.esper.events; +using com.espertech.esper.filter; + +namespace com.espertech.esper.core.start +{ + /// Starts and provides the stop method for EPL statements. + public class EPPreparedExecuteIUDSingleStreamUpdate : EPPreparedExecuteIUDSingleStream + { + public EPPreparedExecuteIUDSingleStreamUpdate(StatementSpecCompiled statementSpec, EPServicesContext services, StatementContext statementContext) + : base(statementSpec, services, statementContext) + { + } + + public override EPPreparedExecuteIUDSingleStreamExec GetExecutor(FilterSpecCompiled filter, string aliasName) + { + var services = base.Services; + var processor = base.Processor; + var statementContext = base.StatementContext; + var statementSpec = base.StatementSpec; + var updateSpec = (FireAndForgetSpecUpdate) statementSpec.FireAndForgetSpec; + + var assignmentTypeService = new StreamTypeServiceImpl( + new EventType[]{processor.EventTypeResultSetProcessor, null, processor.EventTypeResultSetProcessor}, + new string[]{aliasName, "", EPStatementStartMethodOnTrigger.INITIAL_VALUE_STREAM_NAME}, + new bool[]{true, true, true}, services.EngineURI, true); + assignmentTypeService.IsStreamZeroUnambigous = true; + var evaluatorContextStmt = new ExprEvaluatorContextStatement(statementContext, true); + var validationContext = new ExprValidationContext( + assignmentTypeService, statementContext.EngineImportService, + statementContext.StatementExtensionServicesContext, null, statementContext.SchedulingService, + statementContext.VariableService, statementContext.TableService, evaluatorContextStmt, + statementContext.EventAdapterService, statementContext.StatementName, statementContext.StatementId, + statementContext.Annotations, statementContext.ContextDescriptor, statementContext.ScriptingService, + false, false, true, false, null, false); + + // validate update expressions + try { + foreach (OnTriggerSetAssignment assignment in updateSpec.Assignments) { + ExprNode validated = ExprNodeUtility.GetValidatedSubtree(ExprNodeOrigin.UPDATEASSIGN, assignment.Expression, validationContext); + assignment.Expression = validated; + EPStatementStartMethodHelperValidate.ValidateNoAggregations(validated, "Aggregation functions may not be used within an update-clause"); + } + } catch (ExprValidationException e) { + throw new EPException(e.Message, e); + } + + // make updater + EventBeanUpdateHelper updateHelper; + TableUpdateStrategy tableUpdateStrategy = null; + try { + + bool copyOnWrite = !(processor is FireAndForgetProcessorTable); + updateHelper = EventBeanUpdateHelperFactory.Make(processor.NamedWindowOrTableName, + (EventTypeSPI) processor.EventTypeResultSetProcessor, updateSpec.Assignments, aliasName, null, copyOnWrite, statementContext.StatementName, services.EngineURI, services.EventAdapterService); + + if (processor is FireAndForgetProcessorTable) { + FireAndForgetProcessorTable tableProcessor = (FireAndForgetProcessorTable) processor; + tableUpdateStrategy = services.TableService.GetTableUpdateStrategy(tableProcessor.TableMetadata, updateHelper, false); + copyOnWrite = false; + } + } catch (ExprValidationException e) { + throw new EPException(e.Message, e); + } + + return new EPPreparedExecuteIUDSingleStreamExecUpdate(filter, statementSpec.FilterRootNode, statementSpec.Annotations, updateHelper, tableUpdateStrategy, statementSpec.TableNodes, services); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/start/EPPreparedExecuteMethod.cs b/NEsper.Core/NEsper.Core/core/start/EPPreparedExecuteMethod.cs new file mode 100755 index 000000000..5ad6bb257 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/start/EPPreparedExecuteMethod.cs @@ -0,0 +1,22 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.client.context; +using com.espertech.esper.core.service; + +namespace com.espertech.esper.core.start +{ + /// Starts and provides the stop method for EPL statements. + public interface EPPreparedExecuteMethod + { + EPPreparedQueryResult Execute(ContextPartitionSelector[] contextPartitionSelectors); + + EventType EventType { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/core/start/EPPreparedExecuteMethodHelper.cs b/NEsper.Core/NEsper.Core/core/start/EPPreparedExecuteMethodHelper.cs new file mode 100755 index 000000000..e0231c07c --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/start/EPPreparedExecuteMethodHelper.cs @@ -0,0 +1,69 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.client.context; +using com.espertech.esper.core.context.mgr; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.named; +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.core.start +{ + /// + /// Starts and provides the stop method for EPL statements. + /// + public class EPPreparedExecuteMethodHelper + { + internal static void ValidateFAFQuery(StatementSpecCompiled statementSpec) + { + for (int i = 0; i < statementSpec.StreamSpecs.Length; i++) + { + var streamSpec = statementSpec.StreamSpecs[i]; + if (!(streamSpec is NamedWindowConsumerStreamSpec || streamSpec is TableQueryStreamSpec)) { + throw new ExprValidationException("On-demand queries require tables or named windows and do not allow event streams or patterns"); + } + if (streamSpec.ViewSpecs.Length != 0) { + throw new ExprValidationException("Views are not a supported feature of on-demand queries"); + } + } + if (statementSpec.OutputLimitSpec != null) + { + throw new ExprValidationException( + "Output rate limiting is not a supported feature of on-demand queries"); + } + } + + public static ICollection GetAgentInstanceIds( + FireAndForgetProcessor processor, + ContextPartitionSelector selector, + ContextManagementService contextManagementService, + String contextName) + { + ICollection agentInstanceIds; + if (selector == null || selector is ContextPartitionSelectorAll) + { + agentInstanceIds = processor.GetProcessorInstancesAll(); + } + else + { + ContextManager contextManager = contextManagementService.GetContextManager(contextName); + if (contextManager == null) + { + throw new EPException("Context by name '" + contextName + "' could not be found"); + } + agentInstanceIds = contextManager.GetAgentInstanceIds(selector); + } + return agentInstanceIds; + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/start/EPPreparedExecuteMethodQuery.cs b/NEsper.Core/NEsper.Core/core/start/EPPreparedExecuteMethodQuery.cs new file mode 100755 index 000000000..33a0da4ed --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/start/EPPreparedExecuteMethodQuery.cs @@ -0,0 +1,444 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.client.context; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.mgr; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.table; +using com.espertech.esper.epl.@join.@base; +using com.espertech.esper.epl.spec; +using com.espertech.esper.events; +using com.espertech.esper.filter; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.start +{ + /// + /// Starts and provides the stop method for EPL statements. + /// + public class EPPreparedExecuteMethodQuery : EPPreparedExecuteMethod + { + private static readonly ILog QueryPlanLog = LogManager.GetLogger(AuditPath.QUERYPLAN_LOG); + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly StatementSpecCompiled _statementSpec; + private readonly ResultSetProcessor _resultSetProcessor; + private readonly FireAndForgetProcessor[] _processors; + private readonly AgentInstanceContext _agentInstanceContext; + private readonly EPServicesContext _services; + private readonly EventBeanReader _eventBeanReader; + private readonly JoinSetComposerPrototype _joinSetComposerPrototype; + private readonly FilterSpecCompiled[] _filters; + private readonly bool _hasTableAccess; + + /// + /// Ctor. + /// + /// + /// is a container for the definition of all statement constructs that may have been used in the + /// statement, i.e. if defines the select clauses, insert into, outer joins etc. + /// + /// is the service instances for dependency injection + /// is statement-level information and statement services + /// ExprValidationException if the preparation failed + public EPPreparedExecuteMethodQuery( + StatementSpecCompiled statementSpec, + EPServicesContext services, + StatementContext statementContext) + { + var queryPlanLogging = services.ConfigSnapshot.EngineDefaults.Logging.IsEnableQueryPlan; + if (queryPlanLogging) + { + QueryPlanLog.Info("Query plans for Fire-and-forget query '" + statementContext.Expression + "'"); + } + + _hasTableAccess = (statementSpec.TableNodes != null && statementSpec.TableNodes.Length > 0); + foreach (var streamSpec in statementSpec.StreamSpecs) + { + _hasTableAccess |= streamSpec is TableQueryStreamSpec; + } + + _statementSpec = statementSpec; + _services = services; + + EPPreparedExecuteMethodHelper.ValidateFAFQuery(statementSpec); + + var numStreams = statementSpec.StreamSpecs.Length; + var typesPerStream = new EventType[numStreams]; + var namesPerStream = new string[numStreams]; + _processors = new FireAndForgetProcessor[numStreams]; + _agentInstanceContext = new AgentInstanceContext(statementContext, null, -1, null, null, statementContext.DefaultAgentInstanceScriptContext); + + // resolve types and processors + for (var i = 0; i < numStreams; i++) + { + var streamSpec = statementSpec.StreamSpecs[i]; + _processors[i] = FireAndForgetProcessorFactory.ValidateResolveProcessor(streamSpec, services); + + string streamName = _processors[i].NamedWindowOrTableName; + if (streamSpec.OptionalStreamName != null) + { + streamName = streamSpec.OptionalStreamName; + } + namesPerStream[i] = streamName; + typesPerStream[i] = _processors[i].EventTypeResultSetProcessor; + } + + // compile filter to optimize access to named window + _filters = new FilterSpecCompiled[numStreams]; + if (statementSpec.FilterRootNode != null) + { + var tagged = new LinkedHashMap>(); + for (var i = 0; i < numStreams; i++) + { + try + { + var types = new StreamTypeServiceImpl(typesPerStream, namesPerStream, new bool[numStreams], services.EngineURI, false); + _filters[i] = FilterSpecCompiler.MakeFilterSpec(typesPerStream[i], namesPerStream[i], + Collections.SingletonList(statementSpec.FilterRootNode), null, + tagged, tagged, types, + null, statementContext, Collections.SingletonList(i)); + } + catch (Exception ex) + { + Log.Warn("Unexpected exception analyzing filter paths: " + ex.Message, ex); + } + } + } + + // obtain result set processor + var isIStreamOnly = new bool[namesPerStream.Length]; + CompatExtensions.Fill(isIStreamOnly, true); + StreamTypeService typeService = new StreamTypeServiceImpl(typesPerStream, namesPerStream, isIStreamOnly, services.EngineURI, true); + EPStatementStartMethodHelperValidate.ValidateNodes(statementSpec, statementContext, typeService, null); + + var resultSetProcessorPrototype = ResultSetProcessorFactoryFactory.GetProcessorPrototype(statementSpec, statementContext, typeService, null, new bool[0], true, ContextPropertyRegistryImpl.EMPTY_REGISTRY, null, services.ConfigSnapshot, services.ResultSetProcessorHelperFactory, true, false); + _resultSetProcessor = EPStatementStartMethodHelperAssignExpr.GetAssignResultSetProcessor(_agentInstanceContext, resultSetProcessorPrototype, false, null, true); + + if (statementSpec.SelectClauseSpec.IsDistinct) + { + if (_resultSetProcessor.ResultEventType is EventTypeSPI) + { + _eventBeanReader = ((EventTypeSPI)_resultSetProcessor.ResultEventType).Reader; + } + if (_eventBeanReader == null) + { + _eventBeanReader = new EventBeanReaderDefaultImpl(_resultSetProcessor.ResultEventType); + } + } + + // check context partition use + if (statementSpec.OptionalContextName != null) + { + if (numStreams > 1) + { + throw new ExprValidationException("Joins in runtime queries for context partitions are not supported"); + } + } + + // plan joins or simple queries + if (numStreams > 1) + { + var streamJoinAnalysisResult = new StreamJoinAnalysisResult(numStreams); + CompatExtensions.Fill(streamJoinAnalysisResult.NamedWindow, true); + for (var i = 0; i < numStreams; i++) + { + var processorInstance = _processors[i].GetProcessorInstance(_agentInstanceContext); + if (_processors[i].IsVirtualDataWindow) + { + streamJoinAnalysisResult.ViewExternal[i] = agentInstanceContext => processorInstance.VirtualDataWindow; + } + var uniqueIndexes = _processors[i].GetUniqueIndexes(processorInstance); + streamJoinAnalysisResult.UniqueKeys[i] = uniqueIndexes; + } + + var hasAggregations = !resultSetProcessorPrototype.AggregationServiceFactoryDesc.Expressions.IsEmpty(); + + _joinSetComposerPrototype = JoinSetComposerPrototypeFactory.MakeComposerPrototype(null, -1, + statementSpec.OuterJoinDescList, statementSpec.FilterRootNode, typesPerStream, namesPerStream, + streamJoinAnalysisResult, queryPlanLogging, statementContext, new HistoricalViewableDesc(numStreams), + _agentInstanceContext, false, hasAggregations, services.TableService, true, + services.EventTableIndexService.AllowInitIndex(false)); + } + } + + /// + /// Returns the event type of the prepared statement. + /// + /// event type + public EventType EventType + { + get { return _resultSetProcessor.ResultEventType; } + } + + /// + /// Executes the prepared query. + /// + /// query results + public EPPreparedQueryResult Execute(ContextPartitionSelector[] contextPartitionSelectors) + { + try + { + var numStreams = _processors.Length; + + if (contextPartitionSelectors != null && contextPartitionSelectors.Length != numStreams) + { + throw new ArgumentException("Number of context partition selectors does not match the number of named windows in the from-clause"); + } + + // handle non-context case + if (_statementSpec.OptionalContextName == null) + { + + ICollection[] snapshots = new ICollection[numStreams]; + for (var i = 0; i < numStreams; i++) + { + + var selector = contextPartitionSelectors == null ? null : contextPartitionSelectors[i]; + snapshots[i] = GetStreamFilterSnapshot(i, selector); + } + + _resultSetProcessor.Clear(); + return Process(snapshots); + } + + IList contextPartitionResults = new List(); + var singleSelector = contextPartitionSelectors != null && contextPartitionSelectors.Length > 0 ? contextPartitionSelectors[0] : null; + + // context partition runtime query + ICollection agentInstanceIds = EPPreparedExecuteMethodHelper.GetAgentInstanceIds( + _processors[0], singleSelector, _services.ContextManagementService, _statementSpec.OptionalContextName); + + // collect events and agent instances + foreach (int agentInstanceId in agentInstanceIds) + { + var processorInstance = _processors[0].GetProcessorInstanceContextById(agentInstanceId); + if (processorInstance != null) + { + EPPreparedExecuteTableHelper.AssignTableAccessStrategies(_services, _statementSpec.TableNodes, processorInstance.AgentInstanceContext); + var coll = processorInstance.SnapshotBestEffort(this, _filters[0], _statementSpec.Annotations); + contextPartitionResults.Add(new ContextPartitionResult(coll, processorInstance.AgentInstanceContext)); + } + } + + // process context partitions + var events = new ArrayDeque(); + foreach (var contextPartitionResult in contextPartitionResults) + { + var snapshot = contextPartitionResult.Events; + if (_statementSpec.FilterRootNode != null) + { + snapshot = GetFiltered(snapshot, Collections.SingletonList(_statementSpec.FilterRootNode)); + } + + EventBean[] rows; + + var snapshotAsArrayDeque = snapshot as ArrayDeque; + if (snapshotAsArrayDeque != null) + { + rows = snapshotAsArrayDeque.Array; + } + else + { + rows = snapshot.ToArray(); + } + + _resultSetProcessor.AgentInstanceContext = contextPartitionResult.Context; + var results = _resultSetProcessor.ProcessViewResult(rows, null, true); + if (results != null && results.First != null && results.First.Length > 0) + { + events.Add(results.First); + } + } + return new EPPreparedQueryResult(_resultSetProcessor.ResultEventType, EventBeanUtility.Flatten(events)); + } + finally + { + if (_hasTableAccess) + { + _services.TableService.TableExprEvaluatorContext.ReleaseAcquiredLocks(); + } + } + } + + private ICollection GetStreamFilterSnapshot(int streamNum, ContextPartitionSelector contextPartitionSelector) + { + var streamSpec = _statementSpec.StreamSpecs[streamNum]; + IList filterExpressions = Collections.GetEmptyList(); + if (streamSpec is NamedWindowConsumerStreamSpec) + { + var namedSpec = (NamedWindowConsumerStreamSpec)streamSpec; + filterExpressions = namedSpec.FilterExpressions; + } + else + { + var tableSpec = (TableQueryStreamSpec)streamSpec; + filterExpressions = tableSpec.FilterExpressions; + } + + var fireAndForgetProcessor = _processors[streamNum]; + + // handle the case of a single or matching agent instance + var processorInstance = fireAndForgetProcessor.GetProcessorInstance(_agentInstanceContext); + if (processorInstance != null) + { + EPPreparedExecuteTableHelper.AssignTableAccessStrategies(_services, _statementSpec.TableNodes, _agentInstanceContext); + return GetStreamSnapshotInstance(streamNum, filterExpressions, processorInstance); + } + + // context partition runtime query + var contextPartitions = EPPreparedExecuteMethodHelper.GetAgentInstanceIds(fireAndForgetProcessor, contextPartitionSelector, _services.ContextManagementService, fireAndForgetProcessor.ContextName); + + // collect events + var events = new ArrayDeque(); + foreach (int agentInstanceId in contextPartitions) + { + processorInstance = fireAndForgetProcessor.GetProcessorInstanceContextById(agentInstanceId); + if (processorInstance != null) + { + var coll = processorInstance.SnapshotBestEffort(this, _filters[streamNum], _statementSpec.Annotations); + events.AddAll(coll); + } + } + return events; + } + + private ICollection GetStreamSnapshotInstance( + int streamNum, + IList filterExpressions, + FireAndForgetInstance processorInstance) + { + var coll = processorInstance.SnapshotBestEffort(this, _filters[streamNum], _statementSpec.Annotations); + if (filterExpressions.Count != 0) + { + coll = GetFiltered(coll, filterExpressions); + } + return coll; + } + + private EPPreparedQueryResult Process(ICollection[] snapshots) + { + var numStreams = _processors.Length; + + UniformPair results; + if (numStreams == 1) + { + if (_statementSpec.FilterRootNode != null) + { + snapshots[0] = GetFiltered(snapshots[0], _statementSpec.FilterRootNode.AsSingleton()); + } + EventBean[] rows = snapshots[0].ToArray(); + results = _resultSetProcessor.ProcessViewResult(rows, null, true); + } + else + { + var viewablePerStream = new Viewable[numStreams]; + for (var i = 0; i < numStreams; i++) + { + var instance = _processors[i].GetProcessorInstance(_agentInstanceContext); + if (instance == null) + { + throw new UnsupportedOperationException("Joins against named windows that are under context are not supported"); + } + viewablePerStream[i] = instance.TailViewInstance; + } + + var joinSetComposerDesc = _joinSetComposerPrototype.Create(viewablePerStream, true, _agentInstanceContext, false); + var joinComposer = joinSetComposerDesc.JoinSetComposer; + JoinSetFilter joinFilter; + if (joinSetComposerDesc.PostJoinFilterEvaluator != null) + { + joinFilter = new JoinSetFilter(joinSetComposerDesc.PostJoinFilterEvaluator); + } + else + { + joinFilter = null; + } + + var oldDataPerStream = new EventBean[numStreams][]; + var newDataPerStream = new EventBean[numStreams][]; + for (var i = 0; i < numStreams; i++) + { + newDataPerStream[i] = snapshots[i].ToArray(); + } + var result = joinComposer.Join(newDataPerStream, oldDataPerStream, _agentInstanceContext); + if (joinFilter != null) + { + joinFilter.Process(result.First, null, _agentInstanceContext); + } + results = _resultSetProcessor.ProcessJoinResult(result.First, null, true); + } + + if (_statementSpec.SelectClauseSpec.IsDistinct) + { + results.First = EventBeanUtility.GetDistinctByProp(results.First, _eventBeanReader); + } + + return new EPPreparedQueryResult(_resultSetProcessor.ResultEventType, results.First); + } + + private ICollection GetFiltered(ICollection snapshot, IList filterExpressions) + { + var deque = new ArrayDeque(Math.Min(snapshot.Count + 1, 16)); + var visitable = snapshot as IVisitable; + ExprNodeUtility.ApplyFilterExpressionsIterable(snapshot, filterExpressions, _agentInstanceContext, deque); + return deque; + } + + public EPServicesContext Services + { + get { return _services; } + } + + public ExprTableAccessNode[] TableNodes + { + get { return _statementSpec.TableNodes; } + } + + public AgentInstanceContext AgentInstanceContext + { + get { return _agentInstanceContext; } + } + + internal class ContextPartitionResult + { + private readonly ICollection _events; + private readonly AgentInstanceContext _context; + + internal ContextPartitionResult(ICollection events, AgentInstanceContext context) + { + _events = events; + _context = context; + } + + public ICollection Events + { + get { return _events; } + } + + public AgentInstanceContext Context + { + get { return _context; } + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/start/EPPreparedExecuteTableHelper.cs b/NEsper.Core/NEsper.Core/core/start/EPPreparedExecuteTableHelper.cs new file mode 100755 index 000000000..895daabf3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/start/EPPreparedExecuteTableHelper.cs @@ -0,0 +1,35 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.table; + +namespace com.espertech.esper.core.start +{ + public class EPPreparedExecuteTableHelper + { + public static void AssignTableAccessStrategies(EPServicesContext services, ExprTableAccessNode[] optionalTableNodes, AgentInstanceContext agentInstanceContext) + { + if (optionalTableNodes == null) + { + return; + } + var strategies = EPStatementStartMethodHelperTableAccess.AttachTableAccess(services, agentInstanceContext, optionalTableNodes); + foreach (var strategyEntry in strategies) + { + strategyEntry.Key.Strategy = strategyEntry.Value; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/start/EPStatementDestroyCallbackContext.cs b/NEsper.Core/NEsper.Core/core/start/EPStatementDestroyCallbackContext.cs new file mode 100755 index 000000000..d1789ef12 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/start/EPStatementDestroyCallbackContext.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.context.mgr; +using com.espertech.esper.util; + +namespace com.espertech.esper.core.start +{ + public class EPStatementDestroyCallbackContext : DestroyCallback + { + private readonly ContextManagementService _contextManagementService; + private readonly string _contextName; + private readonly string _statementName; + private readonly int _statementId; + + public EPStatementDestroyCallbackContext( + ContextManagementService contextManagementService, + string optionalContextName, + string statementName, + int statementId) + { + _contextManagementService = contextManagementService; + _contextName = optionalContextName; + _statementName = statementName; + _statementId = statementId; + } + + public void Destroy() { + _contextManagementService.DestroyedStatement(_contextName, _statementName, _statementId); + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/start/EPStatementDestroyCallbackList.cs b/NEsper.Core/NEsper.Core/core/start/EPStatementDestroyCallbackList.cs new file mode 100755 index 000000000..dc0093031 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/start/EPStatementDestroyCallbackList.cs @@ -0,0 +1,60 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.util; + +namespace com.espertech.esper.core.start +{ + /// + /// Method to call to destroy an EPStatement. + /// + public class EPStatementDestroyCallbackList : EPStatementDestroyMethod + { + private static readonly ILog Log = + LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private Deque _callbacks; + + public void AddCallback(Action destroyAction) + { + AddCallback(new ProxyDestroyCallback(destroyAction)); + } + + public void AddCallback(DestroyCallback destroyCallback) + { + if (_callbacks == null) + { + _callbacks = new ArrayDeque(2); + } + _callbacks.Add(destroyCallback); + } + + public void Destroy() + { + if (_callbacks != null) + { + _callbacks.Visit( + destroyCallback => + { + try + { + destroyCallback.Destroy(); + } + catch (Exception ex) + { + Log.Error("Failed to destroy resource: " + ex.Message, ex); + } + }); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/start/EPStatementDestroyCallbackTableIdxRef.cs b/NEsper.Core/NEsper.Core/core/start/EPStatementDestroyCallbackTableIdxRef.cs new file mode 100755 index 000000000..6968b26c6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/start/EPStatementDestroyCallbackTableIdxRef.cs @@ -0,0 +1,30 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.util; + +namespace com.espertech.esper.core.start +{ + public class EPStatementDestroyCallbackTableIdxRef : DestroyCallback + { + private readonly TableService _tableService; + private readonly TableMetadata _tableMetadata; + private readonly string _statementName; + + public EPStatementDestroyCallbackTableIdxRef(TableService tableService, TableMetadata tableMetadata, string statementName) { + _tableService = tableService; + _tableMetadata = tableMetadata; + _statementName = statementName; + } + + public void Destroy() { + _tableService.RemoveIndexReferencesStmtMayRemoveIndex(_statementName, _tableMetadata); + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/start/EPStatementDestroyCallbackTableUpdStr.cs b/NEsper.Core/NEsper.Core/core/start/EPStatementDestroyCallbackTableUpdStr.cs new file mode 100755 index 000000000..f546b8f71 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/start/EPStatementDestroyCallbackTableUpdStr.cs @@ -0,0 +1,30 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.util; + +namespace com.espertech.esper.core.start +{ + public class EPStatementDestroyCallbackTableUpdStr : DestroyCallback + { + private readonly TableService _tableService; + private readonly TableMetadata _tableMetadata; + private readonly string _statementName; + + public EPStatementDestroyCallbackTableUpdStr(TableService tableService, TableMetadata tableMetadata, string statementName) { + _tableService = tableService; + _tableMetadata = tableMetadata; + _statementName = statementName; + } + + public void Destroy() { + _tableService.RemoveTableUpdateStrategyReceivers(_tableMetadata, _statementName); + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/start/EPStatementDestroyMethod.cs b/NEsper.Core/NEsper.Core/core/start/EPStatementDestroyMethod.cs new file mode 100755 index 000000000..d8c8c14ce --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/start/EPStatementDestroyMethod.cs @@ -0,0 +1,39 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.core.start +{ + /// + /// Method to call to destroy an EPStatement. + /// + public interface EPStatementDestroyMethod + { + /// + /// Destroys a statement + /// + void Destroy(); + } + + public sealed class ProxyEPStatementDestroyMethod : EPStatementDestroyMethod + { + public Action ProcDestroy; + + public ProxyEPStatementDestroyMethod() { } + public ProxyEPStatementDestroyMethod(Action procDestroy) + { + ProcDestroy = procDestroy; + } + + public void Destroy() + { + ProcDestroy.Invoke(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/start/EPStatementStartAuditUtil.cs b/NEsper.Core/NEsper.Core/core/start/EPStatementStartAuditUtil.cs new file mode 100755 index 000000000..766088c60 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/start/EPStatementStartAuditUtil.cs @@ -0,0 +1,19 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.core.start +{ + public class EPStatementStartAuditUtil + { + } +} diff --git a/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethod.cs b/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethod.cs new file mode 100755 index 000000000..af69357cb --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethod.cs @@ -0,0 +1,47 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.core.start +{ + /// + /// Starts and provides the stop method for EPL statements. + /// + public interface EPStatementStartMethod + { + /// + /// Starts the EPL statement. + /// + /// The services. + /// statement level services + /// indicator whether the statement is new or a stop-restart statement + /// true to indicate the statement is in the process of being recovered + /// true to indicate the statement is in the process of being recovered and that statement is resilient + /// + /// a viewable to attach to for listening to events, and a stop method to invoke to clean up + /// + /// when the expression validation fails + /// com.espertech.esper.view.ViewProcessingException when views cannot be started + EPStatementStartResult Start( + EPServicesContext services, + StatementContext statementContext, + bool isNewStatement, + bool isRecoveringStatement, + bool isRecoveringResilient); + + StatementSpecCompiled StatementSpec { get; } + } + + public class EPStatementStartMethodConst + { + public const int DEFAULT_AGENT_INSTANCE_ID = -1; + } +} diff --git a/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodBase.cs b/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodBase.cs new file mode 100755 index 000000000..b06d75134 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodBase.cs @@ -0,0 +1,79 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; +using com.espertech.esper.util; + +namespace com.espertech.esper.core.start +{ + /// + /// Starts and provides the stop method for EPL statements. + /// + public abstract class EPStatementStartMethodBase : EPStatementStartMethod + { + private static readonly ILog QUERY_PLAN_LOG = LogManager.GetLogger(AuditPath.QUERYPLAN_LOG); + + internal readonly StatementSpecCompiled _statementSpec; + + protected EPStatementStartMethodBase(StatementSpecCompiled statementSpec) + { + _statementSpec = statementSpec; + } + + public StatementSpecCompiled StatementSpec + { + get { return _statementSpec; } + } + + public abstract EPStatementStartResult StartInternal( + EPServicesContext services, + StatementContext statementContext, + bool isNewStatement, + bool isRecoveringStatement, + bool isRecoveringResilient); + + public EPStatementStartResult Start(EPServicesContext services, StatementContext statementContext, bool isNewStatement, bool isRecoveringStatement, bool isRecoveringResilient) + { + statementContext.VariableService.SetLocalVersion(); // get current version of variables + + bool queryPlanLogging = services.ConfigSnapshot.EngineDefaults.Logging.IsEnableQueryPlan; + if (queryPlanLogging && QUERY_PLAN_LOG.IsInfoEnabled) + { + QUERY_PLAN_LOG.Info("Query plans for statement '" + statementContext.StatementName + "' expression '" + statementContext.Expression + "'"); + } + + // validate context - may not exist + if (_statementSpec.OptionalContextName != null && statementContext.ContextDescriptor == null) + { + throw new ExprValidationException("Context by name '" + _statementSpec.OptionalContextName + "' has not been declared"); + } + + return StartInternal(services, statementContext, isNewStatement, isRecoveringStatement, isRecoveringResilient); + } + + protected EPStatementAgentInstanceHandle GetDefaultAgentInstanceHandle(StatementContext statementContext) + { + return new EPStatementAgentInstanceHandle(statementContext.EpStatementHandle, statementContext.DefaultAgentInstanceLock, -1, new StatementAgentInstanceFilterVersion(), statementContext.FilterFaultHandlerFactory); + } + + protected AgentInstanceContext GetDefaultAgentInstanceContext(StatementContext statementContext) + { + EPStatementAgentInstanceHandle handle = GetDefaultAgentInstanceHandle(statementContext); + return new AgentInstanceContext(statementContext, handle, EPStatementStartMethodConst.DEFAULT_AGENT_INSTANCE_ID, null, null, statementContext.DefaultAgentInstanceScriptContext); + } + + protected bool IsQueryPlanLogging(EPServicesContext services) + { + return services.ConfigSnapshot.EngineDefaults.Logging.IsEnableQueryPlan; + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodCreateContext.cs b/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodCreateContext.cs new file mode 100755 index 000000000..6d6abdc8e --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodCreateContext.cs @@ -0,0 +1,319 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.start +{ + /// + /// Starts and provides the stop method for EPL statements. + /// + public class EPStatementStartMethodCreateContext : EPStatementStartMethodBase + { + public EPStatementStartMethodCreateContext(StatementSpecCompiled statementSpec) + : base(statementSpec) + { + } + + public override EPStatementStartResult StartInternal( + EPServicesContext services, + StatementContext statementContext, + bool isNewStatement, + bool isRecoveringStatement, + bool isRecoveringResilient) + { + if (_statementSpec.OptionalContextName != null) + { + throw new ExprValidationException("A create-context statement cannot itself be associated to a context, please declare a nested context instead"); + } + var context = _statementSpec.ContextDesc; + var agentInstanceContext = GetDefaultAgentInstanceContext(statementContext); + + // compile filter specs, if any + ISet eventTypesReferenced = new HashSet(); + ValidateContextDetail(services, statementContext, eventTypesReferenced, context.ContextDetail, agentInstanceContext); + services.StatementEventTypeRefService.AddReferences(statementContext.StatementName, CollectionUtil.ToArray(eventTypesReferenced)); + + // define output event type + var typeName = "EventType_Context_" + context.ContextName; + var statementResultEventType = services.EventAdapterService.CreateAnonymousMapType(typeName, Collections.GetEmptyMap(), true); + + // add context - does not activate that context + services.ContextManagementService.AddContextSpec(services, agentInstanceContext, context, isRecoveringResilient, statementResultEventType); + + EPStatementStopMethod stopMethod = new ProxyEPStatementStopMethod(() => + { + // no action + }); + + EPStatementDestroyMethod destroyMethod = new ProxyEPStatementDestroyMethod(() => services.ContextManagementService.DestroyedContext(context.ContextName)); + return new EPStatementStartResult(new ZeroDepthStreamNoIterate(statementResultEventType), stopMethod, destroyMethod); + } + + private void ValidateContextDetail( + EPServicesContext servicesContext, + StatementContext statementContext, + ISet eventTypesReferenced, + ContextDetail contextDetail, + AgentInstanceContext agentInstanceContext) + { + if (contextDetail is ContextDetailPartitioned) + { + var segmented = (ContextDetailPartitioned)contextDetail; + foreach (var partition in segmented.Items) + { + ValidateNotTable(servicesContext, partition.FilterSpecRaw.EventTypeName); + var raw = new FilterStreamSpecRaw(partition.FilterSpecRaw, ViewSpec.EMPTY_VIEWSPEC_ARRAY, null, StreamSpecOptions.DEFAULT); + var compiled = raw.Compile(statementContext, eventTypesReferenced, false, Collections.GetEmptyList(), false, true, false, null); + if (!(compiled is FilterStreamSpecCompiled)) + { + throw new ExprValidationException("Partition criteria may not include named windows"); + } + var result = (FilterStreamSpecCompiled)compiled; + partition.FilterSpecCompiled = result.FilterSpec; + } + } + else if (contextDetail is ContextDetailCategory) + { + + // compile filter + var category = (ContextDetailCategory)contextDetail; + ValidateNotTable(servicesContext, category.FilterSpecRaw.EventTypeName); + var raw = new FilterStreamSpecRaw(category.FilterSpecRaw, ViewSpec.EMPTY_VIEWSPEC_ARRAY, null, StreamSpecOptions.DEFAULT); + var result = (FilterStreamSpecCompiled)raw.Compile(statementContext, eventTypesReferenced, false, Collections.GetEmptyList(), false, true, false, null); + category.FilterSpecCompiled = result.FilterSpec; + servicesContext.StatementEventTypeRefService.AddReferences(statementContext.StatementName, CollectionUtil.ToArray(eventTypesReferenced)); + + // compile expressions + foreach (var item in category.Items) + { + ValidateNotTable(servicesContext, category.FilterSpecRaw.EventTypeName); + var filterSpecRaw = new FilterSpecRaw(category.FilterSpecRaw.EventTypeName, Collections.SingletonList(item.Expression), null); + var rawExpr = new FilterStreamSpecRaw(filterSpecRaw, ViewSpec.EMPTY_VIEWSPEC_ARRAY, null, StreamSpecOptions.DEFAULT); + var compiled = (FilterStreamSpecCompiled)rawExpr.Compile(statementContext, eventTypesReferenced, false, Collections.GetEmptyList(), false, true, false, null); + item.SetCompiledFilter(compiled.FilterSpec, agentInstanceContext); + } + } + else if (contextDetail is ContextDetailHash) + { + var hashed = (ContextDetailHash)contextDetail; + foreach (var hashItem in hashed.Items) + { + var raw = new FilterStreamSpecRaw(hashItem.FilterSpecRaw, ViewSpec.EMPTY_VIEWSPEC_ARRAY, null, StreamSpecOptions.DEFAULT); + ValidateNotTable(servicesContext, hashItem.FilterSpecRaw.EventTypeName); + var result = (FilterStreamSpecCompiled)raw.Compile(statementContext, eventTypesReferenced, false, Collections.GetEmptyList(), false, true, false, null); + hashItem.FilterSpecCompiled = result.FilterSpec; + + // validate parameters + var streamTypes = new StreamTypeServiceImpl(result.FilterSpec.FilterForEventType, null, true, statementContext.EngineURI); + var validationContext = new ExprValidationContext( + streamTypes, + statementContext.EngineImportService, + statementContext.StatementExtensionServicesContext, null, + statementContext.SchedulingService, + statementContext.VariableService, statementContext.TableService, + GetDefaultAgentInstanceContext(statementContext), statementContext.EventAdapterService, + statementContext.StatementName, statementContext.StatementId, statementContext.Annotations, + statementContext.ContextDescriptor, statementContext.ScriptingService, false, false, false, false, + null, false); + ExprNodeUtility.Validate(ExprNodeOrigin.CONTEXT, Collections.SingletonList(hashItem.Function), validationContext); + } + } + else if (contextDetail is ContextDetailInitiatedTerminated) + { + var def = (ContextDetailInitiatedTerminated)contextDetail; + var startCondition = ValidateRewriteContextCondition(servicesContext, statementContext, def.Start, eventTypesReferenced, new MatchEventSpec(), new LinkedHashSet()); + var endCondition = ValidateRewriteContextCondition(servicesContext, statementContext, def.End, eventTypesReferenced, startCondition.Matches, startCondition.AllTags); + def.Start = startCondition.Condition; + def.End = endCondition.Condition; + + if (def.DistinctExpressions != null) + { + if (!(startCondition.Condition is ContextDetailConditionFilter)) + { + throw new ExprValidationException("Distinct-expressions require a stream as the initiated-by condition"); + } + var distinctExpressions = def.DistinctExpressions; + if (distinctExpressions.Length == 0) + { + throw new ExprValidationException("Distinct-expressions have not been provided"); + } + var filter = (ContextDetailConditionFilter)startCondition.Condition; + if (filter.OptionalFilterAsName == null) + { + throw new ExprValidationException("Distinct-expressions require that a stream name is assigned to the stream using 'as'"); + } + var types = new StreamTypeServiceImpl(filter.FilterSpecCompiled.FilterForEventType, filter.OptionalFilterAsName, true, servicesContext.EngineURI); + var validationContext = new ExprValidationContext( + types, + statementContext.EngineImportService, + statementContext.StatementExtensionServicesContext, null, + statementContext.SchedulingService, + statementContext.VariableService, statementContext.TableService, + GetDefaultAgentInstanceContext(statementContext), statementContext.EventAdapterService, + statementContext.StatementName, statementContext.StatementId, statementContext.Annotations, + statementContext.ContextDescriptor, statementContext.ScriptingService, false, false, true, false, + null, false); + for (var i = 0; i < distinctExpressions.Length; i++) + { + ExprNodeUtility.ValidatePlainExpression(ExprNodeOrigin.CONTEXTDISTINCT, ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(distinctExpressions[i]), distinctExpressions[i]); + distinctExpressions[i] = ExprNodeUtility.GetValidatedSubtree(ExprNodeOrigin.CONTEXTDISTINCT, distinctExpressions[i], validationContext); + } + } + } + else if (contextDetail is ContextDetailNested) + { + var nested = (ContextDetailNested)contextDetail; + foreach (var nestedContext in nested.Contexts) + { + ValidateContextDetail(servicesContext, statementContext, eventTypesReferenced, nestedContext.ContextDetail, agentInstanceContext); + } + } + else + { + throw new IllegalStateException("Unrecognized context detail " + contextDetail); + } + } + + private void ValidateNotTable(EPServicesContext servicesContext, string eventTypeName) + { + if (servicesContext.TableService.GetTableMetadata(eventTypeName) != null) + { + throw new ExprValidationException("Tables cannot be used in a context declaration"); + } + } + + private ContextDetailMatchPair ValidateRewriteContextCondition( + EPServicesContext servicesContext, + StatementContext statementContext, + ContextDetailCondition endpoint, + ISet eventTypesReferenced, + MatchEventSpec priorMatches, + ISet priorAllTags) + { + if (endpoint is ContextDetailConditionCrontab) + { + var crontab = (ContextDetailConditionCrontab)endpoint; + var scheduleSpecEvaluators = ExprNodeUtility.CrontabScheduleValidate(ExprNodeOrigin.CONTEXTCONDITION, crontab.Crontab, statementContext, false); + var schedule = ExprNodeUtility.CrontabScheduleBuild(scheduleSpecEvaluators, new ExprEvaluatorContextStatement(statementContext, false)); + crontab.Schedule = schedule; + return new ContextDetailMatchPair(crontab, new MatchEventSpec(), new LinkedHashSet()); + } + + if (endpoint is ContextDetailConditionTimePeriod) + { + var timePeriod = (ContextDetailConditionTimePeriod)endpoint; + var validationContext = + new ExprValidationContext( + new StreamTypeServiceImpl(servicesContext.EngineURI, false), + statementContext.EngineImportService, + statementContext.StatementExtensionServicesContext, null, + statementContext.SchedulingService, + statementContext.VariableService, statementContext.TableService, + GetDefaultAgentInstanceContext(statementContext), statementContext.EventAdapterService, + statementContext.StatementName, statementContext.StatementId, statementContext.Annotations, + statementContext.ContextDescriptor, statementContext.ScriptingService, false, false, false, false, + null, false); + ExprNodeUtility.GetValidatedSubtree(ExprNodeOrigin.CONTEXTCONDITION, timePeriod.TimePeriod, validationContext); + if (timePeriod.TimePeriod.IsConstantResult) + { + if (timePeriod.TimePeriod.EvaluateAsSeconds(null, true, null) < 0) + { + throw new ExprValidationException("Invalid negative time period expression '" + ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(timePeriod.TimePeriod) + "'"); + } + } + return new ContextDetailMatchPair(timePeriod, new MatchEventSpec(), new LinkedHashSet()); + } + + if (endpoint is ContextDetailConditionPattern) + { + var pattern = (ContextDetailConditionPattern)endpoint; + var matches = ValidatePatternContextConditionPattern(statementContext, pattern, eventTypesReferenced, priorMatches, priorAllTags); + return new ContextDetailMatchPair(pattern, matches.First, matches.Second); + } + + if (endpoint is ContextDetailConditionFilter) + { + var filter = (ContextDetailConditionFilter)endpoint; + ValidateNotTable(servicesContext, filter.FilterSpecRaw.EventTypeName); + + // compile as filter if there are no prior match to consider + if (priorMatches == null || (priorMatches.ArrayEventTypes.IsEmpty() && priorMatches.TaggedEventTypes.IsEmpty())) + { + var rawExpr = new FilterStreamSpecRaw(filter.FilterSpecRaw, ViewSpec.EMPTY_VIEWSPEC_ARRAY, null, StreamSpecOptions.DEFAULT); + var compiled = (FilterStreamSpecCompiled)rawExpr.Compile(statementContext, eventTypesReferenced, false, Collections.GetEmptyList(), false, true, false, filter.OptionalFilterAsName); + filter.FilterSpecCompiled = compiled.FilterSpec; + var matchEventSpec = new MatchEventSpec(); + var filterForType = compiled.FilterSpec.FilterForEventType; + var allTags = new LinkedHashSet(); + if (filter.OptionalFilterAsName != null) + { + matchEventSpec.TaggedEventTypes.Put(filter.OptionalFilterAsName, new Pair(filterForType, rawExpr.RawFilterSpec.EventTypeName)); + allTags.Add(filter.OptionalFilterAsName); + } + return new ContextDetailMatchPair(filter, matchEventSpec, allTags); + } + + // compile as pattern if there are prior matches to consider, since this is a type of followed-by relationship + var factoryNode = servicesContext.PatternNodeFactory.MakeFilterNode(filter.FilterSpecRaw, filter.OptionalFilterAsName, 0); + var pattern = new ContextDetailConditionPattern(factoryNode, true, false); + var matches = ValidatePatternContextConditionPattern(statementContext, pattern, eventTypesReferenced, priorMatches, priorAllTags); + return new ContextDetailMatchPair(pattern, matches.First, matches.Second); + } + else if (endpoint is ContextDetailConditionImmediate || endpoint is ContextDetailConditionNever) + { + return new ContextDetailMatchPair(endpoint, new MatchEventSpec(), new LinkedHashSet()); + } + else + { + throw new IllegalStateException("Unrecognized endpoint type " + endpoint); + } + } + + private Pair> ValidatePatternContextConditionPattern( + StatementContext statementContext, + ContextDetailConditionPattern pattern, + ISet eventTypesReferenced, + MatchEventSpec priorMatches, + ISet priorAllTags) + { + var raw = new PatternStreamSpecRaw(pattern.PatternRaw, ViewSpec.EMPTY_VIEWSPEC_ARRAY, null, StreamSpecOptions.DEFAULT, false, false); + var compiled = raw.Compile(statementContext, eventTypesReferenced, false, Collections.GetEmptyList(), priorMatches, priorAllTags, false, true, false); + pattern.PatternCompiled = compiled; + return new Pair>(new MatchEventSpec(compiled.TaggedEventTypes, compiled.ArrayEventTypes), compiled.AllTags); + } + + internal class ContextDetailMatchPair + { + internal ContextDetailMatchPair(ContextDetailCondition condition, MatchEventSpec matches, ISet allTags) + { + Condition = condition; + Matches = matches; + AllTags = allTags; + } + + public ContextDetailCondition Condition { get; private set; } + + public MatchEventSpec Matches { get; private set; } + + public ISet AllTags { get; private set; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodCreateExpression.cs b/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodCreateExpression.cs new file mode 100755 index 000000000..eeb97f618 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodCreateExpression.cs @@ -0,0 +1,58 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.factory; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.spec; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.start +{ + /// + /// Starts and provides the stop method for EPL statements. + /// + public class EPStatementStartMethodCreateExpression : EPStatementStartMethodBase + { + public EPStatementStartMethodCreateExpression(StatementSpecCompiled statementSpec) + : base(statementSpec) + { + } + + public override EPStatementStartResult StartInternal( + EPServicesContext services, + StatementContext statementContext, + bool isNewStatement, + bool isRecoveringStatement, + bool isRecoveringResilient) + { + var expressionName = + services.ExprDeclaredService.AddExpressionOrScript(StatementSpec.CreateExpressionDesc); + + // define output event type + var typeName = "EventType_Expression_" + expressionName; + var resultType = services.EventAdapterService.CreateAnonymousMapType( + typeName, Collections.GetEmptyMap(), true); + + var stopMethod = new ProxyEPStatementStopMethod( + () => + { + // no action + }); + + var destroyMethod = new ProxyEPStatementDestroyMethod( + () => services.ExprDeclaredService.DestroyedExpression(StatementSpec.CreateExpressionDesc)); + + Viewable resultView = new ZeroDepthStreamNoIterate(resultType); + statementContext.StatementAgentInstanceFactory = new StatementAgentInstanceFactoryNoAgentInstance(resultView); + return new EPStatementStartResult(resultView, stopMethod, destroyMethod); + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodCreateGraph.cs b/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodCreateGraph.cs new file mode 100755 index 000000000..50014c2c4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodCreateGraph.cs @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.factory; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.spec; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.start +{ + /// + /// Starts and provides the stop method for EPL statements. + /// + public class EPStatementStartMethodCreateGraph : EPStatementStartMethodBase + { + public EPStatementStartMethodCreateGraph(StatementSpecCompiled statementSpec) + : base(statementSpec) + { + } + + public override EPStatementStartResult StartInternal(EPServicesContext services, StatementContext statementContext, bool isNewStatement, bool isRecoveringStatement, bool isRecoveringResilient) + { + var createGraphDesc = StatementSpec.CreateGraphDesc; + var agentInstanceContext = GetDefaultAgentInstanceContext(statementContext); + + // define output event type + var typeName = "EventType_Graph_" + createGraphDesc.GraphName; + var resultType = services.EventAdapterService.CreateAnonymousMapType(typeName, Collections.GetEmptyMap(), true); + + services.DataFlowService.AddStartGraph(createGraphDesc, statementContext, services, agentInstanceContext, isNewStatement); + + var stopMethod = new ProxyEPStatementStopMethod(() => + services.DataFlowService.StopGraph(createGraphDesc.GraphName)); + + var destroyMethod = new ProxyEPStatementDestroyMethod(() => + services.DataFlowService.RemoveGraph(createGraphDesc.GraphName)); + + var resultView = new ZeroDepthStreamNoIterate(resultType); + statementContext.StatementAgentInstanceFactory = new StatementAgentInstanceFactoryNoAgentInstance(resultView); + return new EPStatementStartResult(resultView, stopMethod, destroyMethod); + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodCreateIndex.cs b/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodCreateIndex.cs new file mode 100755 index 000000000..d597eebe4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodCreateIndex.cs @@ -0,0 +1,114 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.core.context.factory; +using com.espertech.esper.core.context.mgr; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.start +{ + /// + /// Starts and provides the stop method for EPL statements. + /// + public class EPStatementStartMethodCreateIndex : EPStatementStartMethodBase + { + public EPStatementStartMethodCreateIndex(StatementSpecCompiled statementSpec) + : base(statementSpec) + { + } + + public override EPStatementStartResult StartInternal(EPServicesContext services, StatementContext statementContext, bool isNewStatement, bool isRecoveringStatement, bool isRecoveringResilient) + { + var spec = _statementSpec.CreateIndexDesc; + var namedWindowProcessor = services.NamedWindowMgmtService.GetProcessor(spec.WindowName); + var tableMetadata = services.TableService.GetTableMetadata(spec.WindowName); + if (namedWindowProcessor == null && tableMetadata == null) { + throw new ExprValidationException("A named window or table by name '" + spec.WindowName + "' does not exist"); + } + var indexedEventType = namedWindowProcessor != null ? namedWindowProcessor.NamedWindowType : tableMetadata.InternalEventType; + var infraContextName = namedWindowProcessor != null ? namedWindowProcessor.ContextName : tableMetadata.ContextName; + EPLValidationUtil.ValidateContextName(namedWindowProcessor == null, spec.WindowName, infraContextName, _statementSpec.OptionalContextName, true); + + // validate index + var validated = EventTableIndexUtil.ValidateCompileExplicitIndex(spec.IsUnique, spec.Columns, indexedEventType); + var imk = new IndexMultiKey(spec.IsUnique, validated.HashProps, validated.BtreeProps); + + // for tables we add the index to metadata + if (tableMetadata != null) { + services.TableService.ValidateAddIndex(statementContext.StatementName, tableMetadata, spec.IndexName, imk); + } + else { + namedWindowProcessor.ValidateAddIndex(statementContext.StatementName, spec.IndexName, imk); + } + + // allocate context factory + Viewable viewable = new ViewableDefaultImpl(indexedEventType); + var contextFactory = new StatementAgentInstanceFactoryCreateIndex(services, spec, viewable, namedWindowProcessor, tableMetadata == null ? null : tableMetadata.TableName, _statementSpec.OptionalContextName); + statementContext.StatementAgentInstanceFactory = contextFactory; + + // provide destroy method which de-registers interest in this index + var finalTableService = services.TableService; + var finalStatementName = statementContext.StatementName; + var destroyMethod = new EPStatementDestroyCallbackList(); + if (tableMetadata != null) { + destroyMethod.AddCallback(() => finalTableService.RemoveIndexReferencesStmtMayRemoveIndex(finalStatementName, tableMetadata)); + } + else { + destroyMethod.AddCallback(() => namedWindowProcessor.RemoveIndexReferencesStmtMayRemoveIndex(imk, finalStatementName)); + } + + EPStatementStopMethod stopMethod; + if (_statementSpec.OptionalContextName != null) { + var mergeView = new ContextMergeView(indexedEventType); + var statement = new ContextManagedStatementCreateIndexDesc(_statementSpec, statementContext, mergeView, contextFactory); + services.ContextManagementService.AddStatement(_statementSpec.OptionalContextName, statement, isRecoveringResilient); + stopMethod = new ProxyEPStatementStopMethod(() => {}); + + var contextManagementService = services.ContextManagementService; + destroyMethod.AddCallback(() => contextManagementService.DestroyedStatement(_statementSpec.OptionalContextName, statementContext.StatementName, statementContext.StatementId)); + } + else { + var defaultAgentInstanceContext = GetDefaultAgentInstanceContext(statementContext); + StatementAgentInstanceFactoryResult result; + try { + result = contextFactory.NewContext(defaultAgentInstanceContext, isRecoveringResilient); + } + catch (EPException ex) { + if (ex.InnerException is ExprValidationException) { + throw (ExprValidationException) ex.InnerException; + } + throw; + } + var stopCallback = services.EpStatementFactory.MakeStopMethod(result); + stopMethod = new ProxyEPStatementStopMethod(stopCallback.Stop); + + if (statementContext.StatementExtensionServicesContext != null && statementContext.StatementExtensionServicesContext.StmtResources != null) { + var holder = statementContext.StatementExtensionServicesContext.ExtractStatementResourceHolder(result); + statementContext.StatementExtensionServicesContext.StmtResources.Unpartitioned = holder; + statementContext.StatementExtensionServicesContext.PostProcessStart(result, isRecoveringResilient); + } + } + + if (tableMetadata != null) { + services.StatementVariableRefService.AddReferences(statementContext.StatementName, tableMetadata.TableName); + } + else { + services.StatementVariableRefService.AddReferences(statementContext.StatementName, namedWindowProcessor.NamedWindowType.Name); + } + + return new EPStatementStartResult(viewable, stopMethod, destroyMethod); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodCreateSchema.cs b/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodCreateSchema.cs new file mode 100755 index 000000000..154ee2e95 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodCreateSchema.cs @@ -0,0 +1,126 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.factory; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.util; +using com.espertech.esper.events; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.start +{ + /// + /// Starts and provides the stop method for EPL statements. + /// + public class EPStatementStartMethodCreateSchema : EPStatementStartMethodBase + { + public EPStatementStartMethodCreateSchema(StatementSpecCompiled statementSpec) + : base(statementSpec) + { + } + + public override EPStatementStartResult StartInternal( + EPServicesContext services, + StatementContext statementContext, + bool isNewStatement, + bool isRecoveringStatement, + bool isRecoveringResilient) + { + CreateSchemaDesc spec = StatementSpec.CreateSchemaDesc; + + EPLValidationUtil.ValidateTableExists(services.TableService, spec.SchemaName); + EventType eventType = HandleCreateSchema(services, statementContext, spec); + + // enter a reference + services.StatementEventTypeRefService.AddReferences( + statementContext.StatementName, new String[] + { + spec.SchemaName + }); + + EventType allocatedEventType = eventType; + EPStatementStopMethod stopMethod = new ProxyEPStatementStopMethod(() => + { + services.StatementEventTypeRefService.RemoveReferencesStatement(statementContext.StatementName); + if (services.StatementEventTypeRefService.GetStatementNamesForType(spec.SchemaName).IsEmpty()) + { + services.EventAdapterService.RemoveType(allocatedEventType.Name); + services.FilterService.RemoveType(allocatedEventType); + } + }); + + Viewable viewable = new ViewableDefaultImpl(eventType); + + // assign agent instance factory (an empty op) + statementContext.StatementAgentInstanceFactory = new StatementAgentInstanceFactoryNoAgentInstance(viewable); + + return new EPStatementStartResult(viewable, stopMethod, null); + } + + private EventType HandleCreateSchema( + EPServicesContext services, + StatementContext statementContext, + CreateSchemaDesc spec) + { + EventType eventType; + + try + { + if (spec.AssignedType != AssignedType.VARIANT) + { + eventType = EventTypeUtility.CreateNonVariantType( + false, spec, statementContext.Annotations, services.ConfigSnapshot, services.EventAdapterService, + services.EngineImportService); + } + else + { + if (spec.CopyFrom != null && !spec.CopyFrom.IsEmpty()) + { + throw new ExprValidationException("Copy-from types are not allowed with variant types"); + } + + var isAny = false; + var config = new ConfigurationVariantStream(); + foreach (var typeName in spec.Types) + { + if (typeName.Trim().Equals("*")) + { + isAny = true; + break; + } + config.AddEventTypeName(typeName); + } + if (!isAny) + { + config.TypeVariance = TypeVarianceEnum.PREDEFINED; + } + else + { + config.TypeVariance = TypeVarianceEnum.ANY; + } + services.ValueAddEventService.AddVariantStream( + spec.SchemaName, config, services.EventAdapterService, services.EventTypeIdGenerator); + eventType = services.ValueAddEventService.GetValueAddProcessor(spec.SchemaName).ValueAddEventType; + } + } + catch (Exception ex) + { + throw new ExprValidationException(ex.Message, ex); + } + + return eventType; + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodCreateTable.cs b/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodCreateTable.cs new file mode 100755 index 000000000..431ebb2c4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodCreateTable.cs @@ -0,0 +1,493 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.factory; +using com.espertech.esper.core.context.mgr; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.annotation; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.rettype; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.epl.variable; +using com.espertech.esper.events; +using com.espertech.esper.events.arr; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.start +{ + /// + /// Starts and provides the stop method for EPL statements. + /// + public class EPStatementStartMethodCreateTable : EPStatementStartMethodBase + { + public EPStatementStartMethodCreateTable(StatementSpecCompiled statementSpec) + : base(statementSpec) + { + } + + public override EPStatementStartResult StartInternal(EPServicesContext services, StatementContext statementContext, bool isNewStatement, bool isRecoveringStatement, bool isRecoveringResilient) + { + var createDesc = _statementSpec.CreateTableDesc; + + // determine whether already declared + VariableServiceUtil.CheckAlreadyDeclaredVariable(createDesc.TableName, services.VariableService); + if (isNewStatement) + { + VariableServiceUtil.CheckAlreadyDeclaredTable(createDesc.TableName, services.TableService); + } + if (services.EventAdapterService.GetEventTypeByName(createDesc.TableName) != null) + { + throw new ExprValidationException("An event type or schema by name '" + createDesc.TableName + "' already exists"); + } + + // Determine event type names + var internalTypeName = "table_" + createDesc.TableName + "__internal"; + var publicTypeName = "table_" + createDesc.TableName + "__public"; + + TableMetadata metadata; + try + { + // determine key types + var keyTypes = GetKeyTypes(createDesc.Columns, services.EngineImportService); + + // check column naming, interpret annotations + var columnDescs = ValidateExpressions(createDesc.Columns, services, statementContext); + + // analyze and plan the state holders + var plan = AnalyzePlanAggregations(createDesc.TableName, statementContext, columnDescs, services, internalTypeName, publicTypeName); + var tableStateRowFactory = plan.StateRowFactory; + + // register new table + var queryPlanLogging = services.ConfigSnapshot.EngineDefaults.Logging.IsEnableQueryPlan; + metadata = services.TableService.AddTable(createDesc.TableName, statementContext.Expression, statementContext.StatementName, keyTypes, plan.TableColumns, tableStateRowFactory, plan.NumberMethodAggregations, statementContext, plan.InternalEventType, + plan.PublicEventType, plan.EventToPublic, queryPlanLogging); + } + catch (ExprValidationException ex) + { + services.EventAdapterService.RemoveType(internalTypeName); + services.EventAdapterService.RemoveType(publicTypeName); + throw ex; + } + + // allocate context factory + var contextFactory = new StatementAgentInstanceFactoryCreateTable(metadata); + statementContext.StatementAgentInstanceFactory = contextFactory; + Viewable outputView; + EPStatementStopMethod stopStatementMethod; + EPStatementDestroyMethod destroyStatementMethod; + + if (_statementSpec.OptionalContextName != null) + { + var contextName = _statementSpec.OptionalContextName; + var mergeView = new ContextMergeView(metadata.PublicEventType); + outputView = mergeView; + var statement = new ContextManagedStatementCreateAggregationVariableDesc(_statementSpec, statementContext, mergeView, contextFactory); + services.ContextManagementService.AddStatement(_statementSpec.OptionalContextName, statement, isRecoveringResilient); + + stopStatementMethod = new ProxyEPStatementStopMethod(() => + services.ContextManagementService.StoppedStatement( + contextName, statementContext.StatementName, statementContext.StatementId, + statementContext.Expression, statementContext.ExceptionHandlingService)); + + destroyStatementMethod = new ProxyEPStatementDestroyMethod(() => + { + services.ContextManagementService.DestroyedStatement(contextName, statementContext.StatementName, statementContext.StatementId); + services.StatementVariableRefService.RemoveReferencesStatement(statementContext.StatementName); + }); + } + else + { + var defaultAgentInstanceContext = GetDefaultAgentInstanceContext(statementContext); + var result = contextFactory.NewContext(defaultAgentInstanceContext, false); + if (statementContext.StatementExtensionServicesContext != null && statementContext.StatementExtensionServicesContext.StmtResources != null) + { + var holder = statementContext.StatementExtensionServicesContext.ExtractStatementResourceHolder(result); + statementContext.StatementExtensionServicesContext.StmtResources.Unpartitioned = holder; + } + outputView = result.FinalView; + + stopStatementMethod = new ProxyEPStatementStopMethod(() => { }); + destroyStatementMethod = new ProxyEPStatementDestroyMethod(() => + services.StatementVariableRefService.RemoveReferencesStatement(statementContext.StatementName)); + } + + services.StatementVariableRefService.AddReferences(statementContext.StatementName, createDesc.TableName); + + return new EPStatementStartResult(outputView, stopStatementMethod, destroyStatementMethod); + } + + private Type[] GetKeyTypes(IList columns, EngineImportService engineImportService) + { + IList keys = new List(); + foreach (var col in columns) + { + if (col.PrimaryKey == null || !col.PrimaryKey.Value) + { + continue; + } + var msg = "Column '" + col.ColumnName + "' may not be tagged as primary key"; + if (col.OptExpression != null) + { + throw new ExprValidationException(msg + ", an expression cannot become a primary key column"); + } + if (col.OptTypeIsArray != null && col.OptTypeIsArray.Value) + { + throw new ExprValidationException(msg + ", an array-typed column cannot become a primary key column"); + } + var type = EventTypeUtility.BuildType(new ColumnDesc(col.ColumnName, col.OptTypeName, false, false), engineImportService); + if (!(type is Type)) + { + throw new ExprValidationException(msg + ", received unexpected event type '" + type + "'"); + } + keys.Add((Type)type); + } + return keys.ToArray(); + } + + private ExprAggregateNode ValidateAggregationExpr( + ExprNode columnExpressionType, + EventType optionalProvidedType, + EPServicesContext services, + StatementContext statementContext) + { + // determine validation context types and istream/irstream + EventType[] types; + string[] streamNames; + bool[] istreamOnly; + if (optionalProvidedType != null) + { + types = new EventType[] { optionalProvidedType }; + streamNames = new string[] { types[0].Name }; + istreamOnly = new bool[] { false }; // always false (expected to be bound by data window), use "ever"-aggregation functions otherwise + } + else + { + types = new EventType[0]; + streamNames = new string[0]; + istreamOnly = new bool[0]; + } + + var streamTypeService = new StreamTypeServiceImpl(types, streamNames, istreamOnly, services.EngineURI, false); + var validationContext = new ExprValidationContext( + streamTypeService, + statementContext.EngineImportService, + statementContext.StatementExtensionServicesContext, null, + statementContext.SchedulingService, + statementContext.VariableService, statementContext.TableService, + new ExprEvaluatorContextStatement(statementContext, false), statementContext.EventAdapterService, + statementContext.StatementName, statementContext.StatementId, statementContext.Annotations, + statementContext.ContextDescriptor, statementContext.ScriptingService, false, false, false, false, null, false); + + // substitute parameter nodes + foreach (var childNode in columnExpressionType.ChildNodes.ToArray()) + { + if (childNode is ExprIdentNode) + { + var identNode = (ExprIdentNode) childNode; + var propname = identNode.FullUnresolvedName.Trim(); + var clazz = TypeHelper.GetTypeForSimpleName(propname); + if (propname.ToLower().Trim() == "object") + { + clazz = typeof(object); + } + EngineImportException ex = null; + if (clazz == null) + { + try + { + clazz = services.EngineImportService.ResolveType(propname, false); + } + catch (EngineImportException e) + { + ex = e; + } + } + if (clazz != null) + { + var typeNode = new ExprTypedNoEvalNode(propname, clazz); + ExprNodeUtility.ReplaceChildNode(columnExpressionType, identNode, typeNode); + } + else + { + if (optionalProvidedType == null) + { + if (ex != null) + { + throw new ExprValidationException("Failed to resolve type '" + propname + "': " + ex.Message, ex); + } + throw new ExprValidationException("Failed to resolve type '" + propname + "'"); + } + } + } + } + + // validate + var validated = ExprNodeUtility.GetValidatedSubtree(ExprNodeOrigin.CREATETABLECOLUMN, columnExpressionType, validationContext); + if (!(validated is ExprAggregateNode)) + { + throw new ExprValidationException("Expression '" + validated.ToExpressionStringMinPrecedenceSafe() + "' is not an aggregation"); + } + + return (ExprAggregateNode)validated; + } + + private IList ValidateExpressions( + IList columns, + EPServicesContext services, + StatementContext statementContext) + { + ISet columnNames = new HashSet(); + IList descriptors = new List(); + + var positionInDeclaration = 0; + foreach (var column in columns) + { + var msgprefix = "For column '" + column.ColumnName + "'"; + + // check duplicate name + if (columnNames.Contains(column.ColumnName)) + { + throw new ExprValidationException("Column '" + column.ColumnName + "' is listed more than once"); + } + columnNames.Add(column.ColumnName); + + // determine presence of type annotation + var optionalEventType = ValidateExpressionGetEventType(msgprefix, column.Annotations, services.EventAdapterService); + + // aggregation node + TableColumnDesc descriptor; + if (column.OptExpression != null) + { + var validated = ValidateAggregationExpr(column.OptExpression, optionalEventType, services, statementContext); + descriptor = new TableColumnDescAgg(positionInDeclaration, column.ColumnName, validated, optionalEventType); + } + else + { + var unresolvedType = EventTypeUtility.BuildType( + new ColumnDesc( + column.ColumnName, + column.OptTypeName, + column.OptTypeIsArray ?? false, + column.OptTypeIsPrimitiveArray ?? false), + services.EngineImportService); + descriptor = new TableColumnDescTyped( + positionInDeclaration, column.ColumnName, unresolvedType, column.PrimaryKey ?? false); + } + descriptors.Add(descriptor); + positionInDeclaration++; + } + + return descriptors; + } + + private static EventType ValidateExpressionGetEventType( + string msgprefix, + IList annotations, + EventAdapterService eventAdapterService) + { + var annos = AnnotationUtil.MapByNameLowerCase(annotations); + + // check annotations used + var typeAnnos = annos.Delete("type"); + if (!annos.IsEmpty()) + { + throw new ExprValidationException(msgprefix + " unrecognized annotation '" + annos.Keys.First() + "'"); + } + + // type determination + EventType optionalType = null; + if (typeAnnos != null) + { + var typeName = AnnotationUtil.GetExpectSingleStringValue(msgprefix, typeAnnos); + optionalType = eventAdapterService.GetEventTypeByName(typeName); + if (optionalType == null) + { + throw new ExprValidationException(msgprefix + " failed to find event type '" + typeName + "'"); + } + } + + return optionalType; + } + + private TableAccessAnalysisResult AnalyzePlanAggregations( + string tableName, + StatementContext statementContext, + IList columns, + EPServicesContext services, + string internalTypeName, + string publicTypeName) + { + // once upfront: obtains aggregation factories for each aggregation + // we do this once as a factory may be a heavier object + IDictionary aggregationFactories = new Dictionary(); + foreach (var column in columns) + { + if (column is TableColumnDescAgg) + { + var agg = (TableColumnDescAgg)column; + var factory = agg.Aggregation.Factory; + aggregationFactories.Put(column, factory); + } + } + + // sort into these categories: + // plain / method-agg / access-agg + // compile all-column public types + IList plainColumns = new List(); + IList methodAggColumns = new List(); + IList accessAggColumns = new List(); + IDictionary allColumnsPublicTypes = new LinkedHashMap(); + foreach (var column in columns) + { + + // handle plain types + if (column is TableColumnDescTyped) + { + var typed = (TableColumnDescTyped)column; + plainColumns.Add(typed); + allColumnsPublicTypes.Put(column.ColumnName, typed.UnresolvedType); + continue; + } + + // handle aggs + var agg = (TableColumnDescAgg)column; + var aggFactory = aggregationFactories.Get(agg); + if (aggFactory.IsAccessAggregation) + { + accessAggColumns.Add(agg); + } + else + { + methodAggColumns.Add(agg); + } + allColumnsPublicTypes.Put(column.ColumnName, agg.Aggregation.ReturnType); + } + + // determine column metadata + // + IDictionary columnMetadata = new LinkedHashMap(); + + // handle typed columns + IDictionary allColumnsInternalTypes = new LinkedHashMap(); + allColumnsInternalTypes.Put(TableServiceConstants.INTERNAL_RESERVED_PROPERTY, typeof(object)); + var indexPlain = 1; + IList groupKeyIndexes = new List(); + var assignPairsPlain = new TableMetadataColumnPairPlainCol[plainColumns.Count]; + foreach (var typedColumn in plainColumns) + { + allColumnsInternalTypes.Put(typedColumn.ColumnName, typedColumn.UnresolvedType); + columnMetadata.Put(typedColumn.ColumnName, new TableMetadataColumnPlain(typedColumn.ColumnName, typedColumn.IsKey, indexPlain)); + if (typedColumn.IsKey) + { + groupKeyIndexes.Add(indexPlain); + } + assignPairsPlain[indexPlain - 1] = new TableMetadataColumnPairPlainCol(typedColumn.PositionInDeclaration, indexPlain); + indexPlain++; + } + + // determine internally-used event type + // for use by indexes and lookups + ObjectArrayEventType internalEventType; + ObjectArrayEventType publicEventType; + try + { + internalEventType = (ObjectArrayEventType)services.EventAdapterService.AddNestableObjectArrayType(internalTypeName, allColumnsInternalTypes, null, false, false, false, false, false, true, tableName); + publicEventType = (ObjectArrayEventType)services.EventAdapterService.AddNestableObjectArrayType(publicTypeName, allColumnsPublicTypes, null, false, false, false, false, false, false, null); + } + catch (EPException ex) + { + throw new ExprValidationException("Invalid type information: " + ex.Message, ex); + } + services.StatementEventTypeRefService.AddReferences(statementContext.StatementName, new string[] { internalTypeName, publicTypeName }); + + // handle aggregation-methods single-func first. + var methodFactories = new AggregationMethodFactory[methodAggColumns.Count]; + var index = 0; + var assignPairsMethod = new TableMetadataColumnPairAggMethod[methodAggColumns.Count]; + foreach (var column in methodAggColumns) + { + var factory = aggregationFactories.Get(column); + var optionalEnumerationType = EPTypeHelper.OptionalFromEnumerationExpr(statementContext.StatementId, statementContext.EventAdapterService, column.Aggregation); + methodFactories[index] = factory; + columnMetadata.Put(column.ColumnName, new TableMetadataColumnAggregation(column.ColumnName, factory, index, null, optionalEnumerationType, column.OptionalAssociatedType)); + assignPairsMethod[index] = new TableMetadataColumnPairAggMethod(column.PositionInDeclaration); + index++; + } + + // handle access-aggregation (sharable, multi-value) aggregations + var stateFactories = new AggregationStateFactory[accessAggColumns.Count]; + var assignPairsAccess = new TableMetadataColumnPairAggAccess[accessAggColumns.Count]; + index = 0; + foreach (var column in accessAggColumns) + { + var factory = aggregationFactories.Get(column); + stateFactories[index] = factory.GetAggregationStateFactory(false); + var pair = new AggregationAccessorSlotPair(index, factory.Accessor); + var optionalEnumerationType = EPTypeHelper.OptionalFromEnumerationExpr(statementContext.StatementId, statementContext.EventAdapterService, column.Aggregation); + columnMetadata.Put(column.ColumnName, new TableMetadataColumnAggregation(column.ColumnName, factory, -1, pair, optionalEnumerationType, column.OptionalAssociatedType)); + assignPairsAccess[index] = new TableMetadataColumnPairAggAccess(column.PositionInDeclaration, factory.Accessor); + index++; + } + + // create state factory + var groupKeyIndexesArr = CollectionUtil.IntArray(groupKeyIndexes); + var stateRowFactory = new TableStateRowFactory( + internalEventType, statementContext.EngineImportService, methodFactories, stateFactories, groupKeyIndexesArr, services.EventAdapterService); + + // create public event provision + var eventToPublic = new TableMetadataInternalEventToPublic(publicEventType, + assignPairsPlain, assignPairsMethod, assignPairsAccess, services.EventAdapterService); + + return new TableAccessAnalysisResult(stateRowFactory, columnMetadata, methodAggColumns.Count, internalEventType, publicEventType, eventToPublic); + } + + internal class TableAccessAnalysisResult + { + internal TableAccessAnalysisResult( + TableStateRowFactory stateRowFactory, + IDictionary tableColumns, + int numberMethodAggregations, + ObjectArrayEventType internalEventType, + ObjectArrayEventType publicEventType, + TableMetadataInternalEventToPublic eventToPublic) + { + StateRowFactory = stateRowFactory; + TableColumns = tableColumns; + NumberMethodAggregations = numberMethodAggregations; + InternalEventType = internalEventType; + PublicEventType = publicEventType; + EventToPublic = eventToPublic; + } + + public TableStateRowFactory StateRowFactory { get; private set; } + + public IDictionary TableColumns { get; private set; } + + public int NumberMethodAggregations { get; private set; } + + public ObjectArrayEventType InternalEventType { get; private set; } + + public ObjectArrayEventType PublicEventType { get; private set; } + + public TableMetadataInternalEventToPublic EventToPublic { get; private set; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodCreateVariable.cs b/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodCreateVariable.cs new file mode 100755 index 000000000..9f3d055ac --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodCreateVariable.cs @@ -0,0 +1,145 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.factory; +using com.espertech.esper.core.context.mgr; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.variable; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.start +{ + /// + /// Starts and provides the stop method for EPL statements. + /// + public class EPStatementStartMethodCreateVariable : EPStatementStartMethodBase + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + public EPStatementStartMethodCreateVariable(StatementSpecCompiled statementSpec) + : base(statementSpec) + { + } + + public override EPStatementStartResult StartInternal( + EPServicesContext services, + StatementContext statementContext, + bool isNewStatement, + bool isRecoveringStatement, + bool isRecoveringResilient) + { + var createDesc = _statementSpec.CreateVariableDesc; + + VariableServiceUtil.CheckAlreadyDeclaredTable(createDesc.VariableName, services.TableService); + + // Get assignment value + object value = null; + if (createDesc.Assignment != null) + { + // Evaluate assignment expression + StreamTypeService typeService = new StreamTypeServiceImpl( + new EventType[0], new string[0], new bool[0], services.EngineURI, false); + var evaluatorContextStmt = new ExprEvaluatorContextStatement(statementContext, false); + var validationContext = new ExprValidationContext( + typeService, + statementContext.EngineImportService, + statementContext.StatementExtensionServicesContext, + null, + statementContext.SchedulingService, + statementContext.VariableService, + statementContext.TableService, evaluatorContextStmt, + statementContext.EventAdapterService, + statementContext.StatementName, statementContext.StatementId, + statementContext.Annotations, + statementContext.ContextDescriptor, + statementContext.ScriptingService, + false, false, false, false, null, false); + var validated = ExprNodeUtility.GetValidatedSubtree( + ExprNodeOrigin.VARIABLEASSIGN, createDesc.Assignment, validationContext); + value = validated.ExprEvaluator.Evaluate(new EvaluateParams(null, true, evaluatorContextStmt)); + } + + // Create variable + try + { + services.VariableService.CreateNewVariable( + _statementSpec.OptionalContextName, createDesc.VariableName, createDesc.VariableType, createDesc.IsConstant, + createDesc.IsArray, createDesc.IsArrayOfPrimitive, value, services.EngineImportService); + } + catch (VariableExistsException ex) + { + // for new statement we don't allow creating the same variable + if (isNewStatement) + { + throw new ExprValidationException("Cannot create variable: " + ex.Message, ex); + } + } + catch (VariableDeclarationException ex) + { + throw new ExprValidationException("Cannot create variable: " + ex.Message, ex); + } + + var destroyMethod = new EPStatementDestroyCallbackList(); + var stopMethod = new ProxyEPStatementStopMethod(() => { }); + + var variableMetaData = services.VariableService.GetVariableMetaData(createDesc.VariableName); + Viewable outputView; + var eventType = CreateVariableView.GetEventType( + statementContext.StatementId, services.EventAdapterService, variableMetaData); + var contextFactory = + new StatementAgentInstanceFactoryCreateVariable( + createDesc, _statementSpec, statementContext, services, variableMetaData, eventType); + statementContext.StatementAgentInstanceFactory = contextFactory; + + if (_statementSpec.OptionalContextName != null) + { + var mergeView = new ContextMergeView(eventType); + outputView = mergeView; + var statement = + new ContextManagedStatementCreateVariableDesc(_statementSpec, statementContext, mergeView, contextFactory); + services.ContextManagementService.AddStatement( + _statementSpec.OptionalContextName, statement, isRecoveringResilient); + + var contextManagementService = services.ContextManagementService; + destroyMethod.AddCallback(new ProxyDestroyCallback(() => contextManagementService.DestroyedStatement( + _statementSpec.OptionalContextName, statementContext.StatementName, + statementContext.StatementId))); + } + else + { + var resultOfStart = + (StatementAgentInstanceFactoryCreateVariableResult) + contextFactory.NewContext(GetDefaultAgentInstanceContext(statementContext), isRecoveringResilient); + outputView = resultOfStart.FinalView; + + if (statementContext.StatementExtensionServicesContext != null && + statementContext.StatementExtensionServicesContext.StmtResources != null) + { + var holder = + statementContext.StatementExtensionServicesContext.ExtractStatementResourceHolder(resultOfStart); + statementContext.StatementExtensionServicesContext.StmtResources.Unpartitioned = holder; + statementContext.StatementExtensionServicesContext.PostProcessStart(resultOfStart, isRecoveringResilient); + } + } + + services.StatementVariableRefService.AddReferences( + statementContext.StatementName, Collections.SingletonList(createDesc.VariableName), null); + return new EPStatementStartResult(outputView, stopMethod, destroyMethod); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodCreateWindow.cs b/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodCreateWindow.cs new file mode 100755 index 000000000..230dad3cc --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodCreateWindow.cs @@ -0,0 +1,224 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.client.annotation; +using com.espertech.esper.core.context.factory; +using com.espertech.esper.core.context.mgr; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.named; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.view; +using com.espertech.esper.epl.virtualdw; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.start +{ + /// + /// Starts and provides the stop method for EPL statements. + /// + public class EPStatementStartMethodCreateWindow : EPStatementStartMethodBase + { + public EPStatementStartMethodCreateWindow(StatementSpecCompiled statementSpec) + : base(statementSpec) + { + } + + public override EPStatementStartResult StartInternal( + EPServicesContext services, + StatementContext statementContext, + bool isNewStatement, + bool isRecoveringStatement, + bool isRecoveringResilient) + { + // define stop + var stopCallbacks = new List(); + + // determine context + var contextName = _statementSpec.OptionalContextName; + + // Create view factories and parent view based on a filter specification + // Since only for non-joins we get the existing stream's lock and try to reuse it's views + var filterStreamSpec = (FilterStreamSpecCompiled)_statementSpec.StreamSpecs[0]; + InstrumentationAgent instrumentationAgentCreateWindowInsert = null; + if (InstrumentationHelper.ENABLED) + { + var eventTypeName = filterStreamSpec.FilterSpec.FilterForEventType.Name; + instrumentationAgentCreateWindowInsert = new ProxyInstrumentationAgent() + { + ProcIndicateQ = () => InstrumentationHelper.Get().QFilterActivationNamedWindowInsert(eventTypeName), + ProcIndicateA = () => InstrumentationHelper.Get().AFilterActivationNamedWindowInsert(), + }; + } + var activator = services.ViewableActivatorFactory.CreateFilterProxy(services, filterStreamSpec.FilterSpec, statementContext.Annotations, false, instrumentationAgentCreateWindowInsert, false, 0); + + // create data window view factories + var unmaterializedViewChain = services.ViewService.CreateFactories(0, filterStreamSpec.FilterSpec.ResultEventType, filterStreamSpec.ViewSpecs, filterStreamSpec.Options, statementContext, false, -1); + + // verify data window + VerifyDataWindowViewFactoryChain(unmaterializedViewChain.FactoryChain); + + // get processor for variant-streams and versioned typed + var windowName = _statementSpec.CreateWindowDesc.WindowName; + var optionalRevisionProcessor = statementContext.ValueAddEventService.GetValueAddProcessor(windowName); + + // add named window processor (one per named window for all agent instances) + var isPrioritized = services.EngineSettingsService.EngineSettings.Execution.IsPrioritized; + var isEnableSubqueryIndexShare = HintEnum.ENABLE_WINDOW_SUBQUERY_INDEXSHARE.GetHint(_statementSpec.Annotations) != null; + if (!isEnableSubqueryIndexShare && unmaterializedViewChain.FactoryChain[0] is VirtualDWViewFactory) + { + isEnableSubqueryIndexShare = true; // index share is always enabled for virtual data window (otherwise it wouldn't make sense) + } + var isBatchingDataWindow = DetermineBatchingDataWindow(unmaterializedViewChain.FactoryChain); + var virtualDataWindowFactory = DetermineVirtualDataWindow(unmaterializedViewChain.FactoryChain); + var optionalUniqueKeyProps = ViewServiceHelper.GetUniqueCandidateProperties(unmaterializedViewChain.FactoryChain, _statementSpec.Annotations); + var processor = services.NamedWindowMgmtService.AddProcessor( + windowName, contextName, filterStreamSpec.FilterSpec.ResultEventType, + statementContext.StatementResultService, optionalRevisionProcessor, statementContext.Expression, + statementContext.StatementName, isPrioritized, isEnableSubqueryIndexShare, isBatchingDataWindow, + virtualDataWindowFactory != null, optionalUniqueKeyProps, + _statementSpec.CreateWindowDesc.AsEventTypeName, + statementContext, services.NamedWindowDispatchService); + + Viewable finalViewable; + EPStatementStopMethod stopStatementMethod; + EPStatementDestroyMethod destroyStatementMethod; + + try + { + // add stop callback + stopCallbacks.Add(new ProxyStopCallback(() => + { + services.NamedWindowMgmtService.RemoveProcessor(windowName); + if (virtualDataWindowFactory != null) + { + virtualDataWindowFactory.DestroyNamedWindow(); + } + })); + + // Add a wildcard to the select clause as subscribers received the window contents + _statementSpec.SelectClauseSpec.SetSelectExprList(new SelectClauseElementWildcard()); + _statementSpec.SelectStreamDirEnum = SelectClauseStreamSelectorEnum.RSTREAM_ISTREAM_BOTH; + + // obtain result set processor factory + StreamTypeService typeService = new StreamTypeServiceImpl(new EventType[] { processor.NamedWindowType }, new string[] { windowName }, new bool[] { true }, services.EngineURI, false); + var resultSetProcessorPrototype = ResultSetProcessorFactoryFactory.GetProcessorPrototype( + _statementSpec, statementContext, typeService, null, new bool[0], true, null, null, services.ConfigSnapshot, services.ResultSetProcessorHelperFactory, false, false); + + // obtain factory for output limiting + var outputViewFactory = OutputProcessViewFactoryFactory.Make( + _statementSpec, + services.InternalEventRouter, + statementContext, + resultSetProcessorPrototype.ResultSetProcessorFactory.ResultEventType, null, + services.TableService, + resultSetProcessorPrototype.ResultSetProcessorFactory.ResultSetProcessorType, + services.ResultSetProcessorHelperFactory, + services.StatementVariableRefService); + + // create context factory + // Factory for statement-context instances + var contextFactory = new StatementAgentInstanceFactoryCreateWindow(statementContext, _statementSpec, services, activator, unmaterializedViewChain, resultSetProcessorPrototype, outputViewFactory, isRecoveringStatement); + statementContext.StatementAgentInstanceFactory = contextFactory; + + // With context - delegate instantiation to context + EPStatementStopMethod stopMethod = new EPStatementStopMethodImpl(statementContext, stopCallbacks); + if (_statementSpec.OptionalContextName != null) + { + + var mergeView = new ContextMergeView(processor.NamedWindowType); + finalViewable = mergeView; + + var statement = new ContextManagedStatementCreateWindowDesc(_statementSpec, statementContext, mergeView, contextFactory); + services.ContextManagementService.AddStatement(contextName, statement, isRecoveringResilient); + stopStatementMethod = new ProxyEPStatementStopMethod(() => + { + services.ContextManagementService.StoppedStatement(contextName, statementContext.StatementName, statementContext.StatementId, statementContext.Expression, statementContext.ExceptionHandlingService); + stopMethod.Stop(); + }); + + destroyStatementMethod = new ProxyEPStatementDestroyMethod(() => + services.ContextManagementService.DestroyedStatement(contextName, statementContext.StatementName, statementContext.StatementId)); + } + // Without context - start here + else + { + var agentInstanceContext = GetDefaultAgentInstanceContext(statementContext); + StatementAgentInstanceFactoryCreateWindowResult resultOfStart; + try + { + resultOfStart = (StatementAgentInstanceFactoryCreateWindowResult)contextFactory.NewContext(agentInstanceContext, isRecoveringResilient); + } + catch (Exception) + { + services.NamedWindowMgmtService.RemoveProcessor(windowName); + throw; + } + finalViewable = resultOfStart.FinalView; + var stopCallback = services.EpStatementFactory.MakeStopMethod(resultOfStart); + stopStatementMethod = new ProxyEPStatementStopMethod(() => + { + stopCallback.Stop(); + stopMethod.Stop(); + }); + destroyStatementMethod = null; + + if (statementContext.StatementExtensionServicesContext != null && statementContext.StatementExtensionServicesContext.StmtResources != null) + { + var holder = statementContext.StatementExtensionServicesContext.ExtractStatementResourceHolder(resultOfStart); + statementContext.StatementExtensionServicesContext.StmtResources.Unpartitioned = holder; + statementContext.StatementExtensionServicesContext.PostProcessStart(resultOfStart, isRecoveringResilient); + } + } + } + catch (ExprValidationException) + { + services.NamedWindowMgmtService.RemoveProcessor(windowName); + throw; + } + catch (Exception) + { + services.NamedWindowMgmtService.RemoveProcessor(windowName); + throw; + } + + services.StatementVariableRefService.AddReferences(statementContext.StatementName, windowName); + + return new EPStatementStartResult(finalViewable, stopStatementMethod, destroyStatementMethod); + } + + private static VirtualDWViewFactory DetermineVirtualDataWindow(IEnumerable viewFactoryChain) + { + return viewFactoryChain.OfType().FirstOrDefault(); + } + + private static bool DetermineBatchingDataWindow(IEnumerable viewFactoryChain) + { + return viewFactoryChain.OfType().Any(); + } + + private void VerifyDataWindowViewFactoryChain(IEnumerable viewFactories) + { + if (viewFactories.OfType().Any()) + { + return; + } + + throw new ExprValidationException(NamedWindowMgmtServiceConstants.ERROR_MSG_DATAWINDOWS); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodFactory.cs b/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodFactory.cs new file mode 100755 index 000000000..f96b766d5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodFactory.cs @@ -0,0 +1,75 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.core.start +{ + /// + /// Starts and provides the stop method for EPL statements. + /// + public class EPStatementStartMethodFactory + { + /// + /// Ctor. + /// + /// + /// is a container for the definition of all statement constructs that + /// may have been used in the statement, i.e. if defines the select clauses, + /// insert into, outer joins etc. + /// + /// + public static EPStatementStartMethod MakeStartMethod(StatementSpecCompiled statementSpec) + { + if (statementSpec.UpdateSpec != null) + { + return new EPStatementStartMethodUpdate(statementSpec); + } + if (statementSpec.OnTriggerDesc != null) + { + return new EPStatementStartMethodOnTrigger(statementSpec); + } + else if (statementSpec.CreateWindowDesc != null) + { + return new EPStatementStartMethodCreateWindow(statementSpec); + } + else if (statementSpec.CreateIndexDesc != null) + { + return new EPStatementStartMethodCreateIndex(statementSpec); + } + else if (statementSpec.CreateGraphDesc != null) + { + return new EPStatementStartMethodCreateGraph(statementSpec); + } + else if (statementSpec.CreateSchemaDesc != null) + { + return new EPStatementStartMethodCreateSchema(statementSpec); + } + else if (statementSpec.CreateVariableDesc != null) + { + return new EPStatementStartMethodCreateVariable(statementSpec); + } + else if (statementSpec.CreateTableDesc != null) + { + return new EPStatementStartMethodCreateTable(statementSpec); + } + else if (statementSpec.ContextDesc != null) + { + return new EPStatementStartMethodCreateContext(statementSpec); + } + else if (statementSpec.CreateExpressionDesc != null) + { + return new EPStatementStartMethodCreateExpression(statementSpec); + } + else + { + return new EPStatementStartMethodSelect(statementSpec); + } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodHelperAssignExpr.cs b/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodHelperAssignExpr.cs new file mode 100755 index 000000000..00b2d7166 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodHelperAssignExpr.cs @@ -0,0 +1,244 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.subselect; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.prev; +using com.espertech.esper.epl.expression.prior; +using com.espertech.esper.epl.expression.subquery; +using com.espertech.esper.epl.expression.table; +using com.espertech.esper.rowregex; + +namespace com.espertech.esper.core.start +{ + public class EPStatementStartMethodHelperAssignExpr + { + public static void AssignExpressionStrategies( + EPStatementStartMethodSelectDesc selectDesc, + AggregationService aggregationService, + IDictionary subselectStrategyInstances, + IDictionary priorStrategyInstances, + IDictionary previousStrategyInstances, + ICollection matchRecognizeNodes, + RegexExprPreviousEvalStrategy matchRecognizePrevEvalStrategy, + IDictionary tableAccessStrategyInstances) + { + // initialize aggregation expression nodes + if (selectDesc.ResultSetProcessorPrototypeDesc.AggregationServiceFactoryDesc != null && + aggregationService != null) + { + AssignAggregations( + aggregationService, + selectDesc.ResultSetProcessorPrototypeDesc.AggregationServiceFactoryDesc.Expressions); + } + + // assign subquery nodes + AssignSubqueryStrategies(selectDesc.SubSelectStrategyCollection, subselectStrategyInstances); + + // assign prior nodes + AssignPriorStrategies(priorStrategyInstances); + + // assign previous nodes + AssignPreviousStrategies(previousStrategyInstances); + + // assign match-recognize previous nodes + AssignMatchRecognizePreviousStrategies(matchRecognizeNodes, matchRecognizePrevEvalStrategy); + + // assign table access nodes + AssignTableAccessStrategies(tableAccessStrategyInstances); + } + + public static void AssignTableAccessStrategies( + IDictionary tableAccessStrategies) + { + foreach (var pair in tableAccessStrategies) + { + pair.Key.Strategy = pair.Value; + } + } + + public static void AssignMatchRecognizePreviousStrategies(ICollection matchRecognizeNodes, RegexExprPreviousEvalStrategy strategy) + { + if (matchRecognizeNodes != null && strategy != null) + { + foreach (var node in matchRecognizeNodes) + { + node.Strategy = strategy; + } + } + } + + public static void UnassignMatchRecognizePreviousStrategies( + ICollection matchRecognizeNodes) + { + if (matchRecognizeNodes != null) + { + foreach (var node in matchRecognizeNodes) + { + node.Strategy = null; + } + } + } + + public static void AssignAggregations( + AggregationResultFuture aggregationService, + IList aggregationExpressions) + { + foreach (var aggregation in aggregationExpressions) + { + aggregation.AssignFuture(aggregationService); + } + } + + public static void UnassignAggregations(IList aggregationExpressions) + { + foreach (var aggregation in aggregationExpressions) + { + aggregation.AssignFuture(null); + } + } + + public static void AssignPreviousStrategies( + IDictionary previousStrategyInstances) + { + foreach (var pair in previousStrategyInstances) + { + pair.Key.Evaluator = pair.Value; + } + } + + public static void UnassignPreviousStrategies(ICollection nodes) + { + foreach (var node in nodes) + { + node.Evaluator = null; + } + } + + public static void AssignPriorStrategies( + IDictionary priorStrategyInstances) + { + foreach (var pair in priorStrategyInstances) + { + pair.Key.PriorStrategy = pair.Value; + } + } + + public static void UnassignPriorStrategies(ICollection priorStrategyInstances) + { + foreach (var node in priorStrategyInstances) + { + node.PriorStrategy = null; + } + } + + public static ResultSetProcessor GetAssignResultSetProcessor( + AgentInstanceContext agentInstanceContext, + ResultSetProcessorFactoryDesc resultSetProcessorPrototype, + bool isSubquery, + int? subqueryNumber, + bool isFireAndForget) + { + AggregationService aggregationService = null; + if (resultSetProcessorPrototype.AggregationServiceFactoryDesc != null) + { + aggregationService = + resultSetProcessorPrototype.AggregationServiceFactoryDesc.AggregationServiceFactory.MakeService( + agentInstanceContext, agentInstanceContext.StatementContext.EngineImportService, isSubquery, + subqueryNumber); + } + + OrderByProcessor orderByProcessor = null; + if (resultSetProcessorPrototype.OrderByProcessorFactory != null) + { + orderByProcessor = resultSetProcessorPrototype.OrderByProcessorFactory.Instantiate( + aggregationService, agentInstanceContext); + } + + var processor = resultSetProcessorPrototype.ResultSetProcessorFactory.Instantiate( + orderByProcessor, aggregationService, agentInstanceContext); + + // initialize aggregation expression nodes + if (resultSetProcessorPrototype.AggregationServiceFactoryDesc != null) + { + foreach (var aggregation in resultSetProcessorPrototype.AggregationServiceFactoryDesc.Expressions) + { + aggregation.AssignFuture(aggregationService); + } + } + + return processor; + } + + public static void UnassignSubqueryStrategies(ICollection subselects) + { + foreach (var subselectNode in subselects) + { + subselectNode.Strategy = null; + subselectNode.SubselectAggregationService = null; + } + } + + public static void AssignSubqueryStrategies( + SubSelectStrategyCollection subSelectStrategyCollection, + IDictionary subselectStrategyInstances) + { + // initialize subselects expression nodes (strategy assignment) + foreach (var subselectEntry in subselectStrategyInstances) + { + var subselectNode = subselectEntry.Key; + var strategyInstance = subselectEntry.Value; + + subselectNode.Strategy = strategyInstance.Stategy; + subselectNode.SubselectAggregationService = strategyInstance.SubselectAggregationService; + + // initialize aggregations in the subselect + var factoryDesc = subSelectStrategyCollection.Subqueries.Get(subselectNode); + if (factoryDesc.AggregationServiceFactoryDesc != null) + { + foreach (var aggExpressionDesc in factoryDesc.AggregationServiceFactoryDesc.Expressions) + { + aggExpressionDesc.AssignFuture(subselectEntry.Value.SubselectAggregationService); + } + if (factoryDesc.AggregationServiceFactoryDesc.GroupKeyExpressions != null) + { + foreach (var groupKeyExpr in factoryDesc.AggregationServiceFactoryDesc.GroupKeyExpressions) + { + groupKeyExpr.AssignFuture(subselectEntry.Value.SubselectAggregationService); + } + } + } + + // initialize "prior" nodes in the subselect + if (strategyInstance.PriorStrategies != null) + { + foreach (var entry in strategyInstance.PriorStrategies) + { + entry.Key.PriorStrategy = entry.Value; + } + } + + // initialize "prev" nodes in the subselect + if (strategyInstance.PreviousNodeStrategies != null) + { + foreach ( + var entry in + strategyInstance.PreviousNodeStrategies) + { + entry.Key.Evaluator = entry.Value; + } + } + } + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodHelperPrevious.cs b/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodHelperPrevious.cs new file mode 100755 index 000000000..49a3d5b34 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodHelperPrevious.cs @@ -0,0 +1,116 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.prev; +using com.espertech.esper.view; +using com.espertech.esper.view.window; + +namespace com.espertech.esper.core.start +{ + /// + /// Starts and provides the stop method for EPL statements. + /// + public class EPStatementStartMethodHelperPrevious + { + private static readonly Dictionary EmptyMap = + new Dictionary(); + + public static IDictionary CompilePreviousNodeStrategies(ViewResourceDelegateVerified viewResourceDelegate, AgentInstanceViewFactoryChainContext[] contexts) + { + if (!viewResourceDelegate.HasPrevious) + { + return EmptyMap; + } + + IDictionary strategies = new Dictionary(); + + for (int streamNum = 0; streamNum < contexts.Length; streamNum++) + { + // get stream-specific INFO + ViewResourceDelegateVerifiedStream @delegate = viewResourceDelegate.PerStream[streamNum]; + + // obtain getter + HandlePrevious(@delegate.PreviousRequests, contexts[streamNum].PreviousNodeGetter, strategies); + } + + return strategies; + } + + public static DataWindowViewWithPrevious FindPreviousViewFactory(IList factories) + { + ViewFactory factoryFound = null; + foreach (ViewFactory factory in factories) + { + if (factory is DataWindowViewWithPrevious) + { + factoryFound = factory; + break; + } + } + if (factoryFound == null) + { + throw new EPException("Failed to find 'previous'-handling view factory"); // was verified earlier, should not occur + } + return (DataWindowViewWithPrevious)factoryFound; + } + + private static void HandlePrevious(IList previousRequests, Object previousNodeGetter, IDictionary strategies) + { + if (previousRequests.IsEmpty()) + { + return; + } + + RandomAccessByIndexGetter randomAccessGetter = null; + RelativeAccessByEventNIndexGetter relativeAccessGetter = null; + if (previousNodeGetter is RandomAccessByIndexGetter) + { + randomAccessGetter = (RandomAccessByIndexGetter)previousNodeGetter; + } + else if (previousNodeGetter is RelativeAccessByEventNIndexGetter) + { + relativeAccessGetter = (RelativeAccessByEventNIndexGetter)previousNodeGetter; + } + else + { + throw new EPException("Unexpected 'previous' handler: " + previousNodeGetter); + } + + foreach (ExprPreviousNode previousNode in previousRequests) + { + int streamNumber = previousNode.StreamNumber; + var previousType = previousNode.PreviousType; + ExprPreviousEvalStrategy evaluator; + + if (previousType == ExprPreviousNodePreviousType.PREVWINDOW) + { + evaluator = new ExprPreviousEvalStrategyWindow(streamNumber, previousNode.ChildNodes[1].ExprEvaluator, previousNode.ReturnType.GetElementType(), + randomAccessGetter, relativeAccessGetter); + } + else if (previousType == ExprPreviousNodePreviousType.PREVCOUNT) + { + evaluator = new ExprPreviousEvalStrategyCount(streamNumber, randomAccessGetter, relativeAccessGetter); + } + else + { + evaluator = new ExprPreviousEvalStrategyPrev(streamNumber, previousNode.ChildNodes[0].ExprEvaluator, previousNode.ChildNodes[1].ExprEvaluator, + randomAccessGetter, relativeAccessGetter, previousNode.IsConstantIndex, previousNode.ConstantIndexNumber, previousType == ExprPreviousNodePreviousType.PREVTAIL); + } + + strategies.Put(previousNode, evaluator); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodHelperPrior.cs b/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodHelperPrior.cs new file mode 100755 index 000000000..228a12d86 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodHelperPrior.cs @@ -0,0 +1,140 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.prior; +using com.espertech.esper.view; +using com.espertech.esper.view.internals; +using com.espertech.esper.view.window; + +namespace com.espertech.esper.core.start +{ + /// + /// Starts and provides the stop method for EPL statements. + /// + public class EPStatementStartMethodHelperPrior + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + public static PriorEventViewFactory FindPriorViewFactory(IList factories) + { + ViewFactory factoryFound = null; + foreach (var factory in factories) + { + if (factory is PriorEventViewFactory) + { + factoryFound = factory; + break; + } + } + if (factoryFound == null) + { + throw new EPException("Failed to find 'prior'-handling view factory"); // was verified earlier, should not occur + } + return (PriorEventViewFactory)factoryFound; + } + + public static PriorEventViewFactory GetPriorEventViewFactory( + StatementContext statementContext, + int streamNum, + bool unboundStream, + bool isSubquery, + int subqueryNumber) + { + try + { + var @namespace = ViewEnum.PRIOR_EVENT_VIEW.GetNamespace(); + var name = ViewEnum.PRIOR_EVENT_VIEW.GetName(); + var factory = statementContext.ViewResolutionService.Create(@namespace, name); + + var context = new ViewFactoryContext(statementContext, streamNum, @namespace, name, isSubquery, subqueryNumber, false); + factory.SetViewParameters(context, ((ExprNode)new ExprConstantNodeImpl(unboundStream)).AsSingleton()); + + return (PriorEventViewFactory)factory; + } + catch (ViewProcessingException ex) + { + const string text = "Exception creating prior event view factory"; + throw new EPException(text, ex); + } + catch (ViewParameterException ex) + { + var text = "Exception creating prior event view factory"; + throw new EPException(text, ex); + } + } + + public static IDictionary CompilePriorNodeStrategies(ViewResourceDelegateVerified viewResourceDelegate, AgentInstanceViewFactoryChainContext[] viewFactoryChainContexts) + { + + if (!viewResourceDelegate.HasPrior) + { + return new Dictionary(); + } + + IDictionary strategies = new Dictionary(); + + for (var streamNum = 0; streamNum < viewResourceDelegate.PerStream.Length; streamNum++) + { + var viewUpdatedCollection = viewFactoryChainContexts[streamNum].PriorViewUpdatedCollection; + var callbacksPerIndex = viewResourceDelegate.PerStream[streamNum].PriorRequests; + HandlePrior(viewUpdatedCollection, callbacksPerIndex, strategies); + } + + return strategies; + } + + private static void HandlePrior(ViewUpdatedCollection viewUpdatedCollection, IDictionary> callbacksPerIndex, IDictionary strategies) + { + + // Since an expression such as "prior(2, price), prior(8, price)" translates + // into {2, 8} the relative index is {0, 1}. + // Map the expression-supplied index to a relative viewUpdatedCollection-known index via wrapper + var relativeIndex = 0; + foreach (var reqIndex in callbacksPerIndex.Keys) + { + var priorNodes = callbacksPerIndex.Get(reqIndex); + foreach (var callback in priorNodes) + { + ExprPriorEvalStrategy strategy; + if (viewUpdatedCollection is RelativeAccessByEventNIndex) + { + var relativeAccess = (RelativeAccessByEventNIndex)viewUpdatedCollection; + var impl = new PriorEventViewRelAccess(relativeAccess, relativeIndex); + strategy = new ExprPriorEvalStrategyRelativeAccess(impl); + } + else + { + if (viewUpdatedCollection is RandomAccessByIndex) + { + strategy = new ExprPriorEvalStrategyRandomAccess((RandomAccessByIndex)viewUpdatedCollection); + } + else + { + strategy = new ExprPriorEvalStrategyRelativeAccess((RelativeAccessByEventNIndex)viewUpdatedCollection); + } + } + + strategies.Put(callback, strategy); + } + relativeIndex++; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodHelperSubselect.cs b/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodHelperSubselect.cs new file mode 100755 index 000000000..143a38be6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodHelperSubselect.cs @@ -0,0 +1,1218 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.client.annotation; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.subselect; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.declexpr; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.subquery; +using com.espertech.esper.epl.expression.visitor; +using com.espertech.esper.epl.join.hint; +using com.espertech.esper.epl.join.plan; +using com.espertech.esper.epl.join.table; +using com.espertech.esper.epl.join.util; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.epl.spec; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.start +{ + public class EPStatementStartMethodHelperSubselect + { + private static readonly ILog QUERY_PLAN_LOG = LogManager.GetLogger(AuditPath.QUERYPLAN_LOG); + + private const string MSG_SUBQUERY_REQUIRES_WINDOW = "Subqueries require one or more views to limit the stream, consider declaring a length or time window (applies to correlated or non-fully-aggregated subqueries)"; + + internal static SubSelectActivationCollection CreateSubSelectActivation( + EPServicesContext services, + StatementSpecCompiled statementSpecContainer, + StatementContext statementContext, + EPStatementDestroyCallbackList destroyCallbacks) + { + var subSelectStreamDesc = new SubSelectActivationCollection(); + var subselectStreamNumber = 1024; + + // Process all subselect expression nodes + foreach (var subselect in statementSpecContainer.SubSelectExpressions) + { + var statementSpec = subselect.StatementSpecCompiled; + var streamSpec = statementSpec.StreamSpecs[0]; + + if (streamSpec is FilterStreamSpecCompiled) + { + var filterStreamSpec = (FilterStreamSpecCompiled) statementSpec.StreamSpecs[0]; + + subselectStreamNumber++; + + InstrumentationAgent instrumentationAgentSubquery = null; + if (InstrumentationHelper.ENABLED) + { + var eventTypeName = filterStreamSpec.FilterSpec.FilterForEventType.Name; + var exprSubselectNode = subselect; + instrumentationAgentSubquery = new ProxyInstrumentationAgent + { + ProcIndicateQ = + () => + InstrumentationHelper.Get() + .QFilterActivationSubselect(eventTypeName, exprSubselectNode), + ProcIndicateA = () => InstrumentationHelper.Get().AFilterActivationSubselect() + }; + } + + // Register filter, create view factories + var activatorDeactivator = + services.ViewableActivatorFactory.CreateFilterProxy( + services, filterStreamSpec.FilterSpec, statementSpec.Annotations, true, + instrumentationAgentSubquery, false, null); + var viewFactoryChain = services.ViewService.CreateFactories( + subselectStreamNumber, filterStreamSpec.FilterSpec.ResultEventType, filterStreamSpec.ViewSpecs, + filterStreamSpec.Options, statementContext, true, subselect.SubselectNumber); + subselect.RawEventType = viewFactoryChain.EventType; + + // Add lookup to list, for later starts + subSelectStreamDesc.Add( + subselect, + new SubSelectActivationHolder( + subselectStreamNumber, filterStreamSpec.FilterSpec.ResultEventType, viewFactoryChain, + activatorDeactivator, streamSpec)); + } + else if (streamSpec is TableQueryStreamSpec) + { + var table = (TableQueryStreamSpec) streamSpec; + var metadata = services.TableService.GetTableMetadata(table.TableName); + var viewFactoryChain = ViewFactoryChain.FromTypeNoViews(metadata.InternalEventType); + var viewableActivator = services.ViewableActivatorFactory.CreateTable(metadata, null); + subSelectStreamDesc.Add( + subselect, + new SubSelectActivationHolder( + subselectStreamNumber, metadata.InternalEventType, viewFactoryChain, viewableActivator, + streamSpec)); + subselect.RawEventType = metadata.InternalEventType; + destroyCallbacks.AddCallback( + new EPStatementDestroyCallbackTableIdxRef( + services.TableService, metadata, statementContext.StatementName)); + services.StatementVariableRefService.AddReferences( + statementContext.StatementName, metadata.TableName); + } + else + { + var namedSpec = + (NamedWindowConsumerStreamSpec) statementSpec.StreamSpecs[0]; + var processor = services.NamedWindowMgmtService.GetProcessor(namedSpec.WindowName); + + var namedWindowType = processor.TailView.EventType; + if (namedSpec.OptPropertyEvaluator != null) + { + namedWindowType = namedSpec.OptPropertyEvaluator.FragmentEventType; + } + + // if named-window index sharing is disabled (the default) or filter expressions are provided then consume the insert-remove stream + var disableIndexShare = + HintEnum.DISABLE_WINDOW_SUBQUERY_INDEXSHARE.GetHint(statementSpecContainer.Annotations) != null; + if (disableIndexShare && processor.IsVirtualDataWindow) + { + disableIndexShare = false; + } + if (!namedSpec.FilterExpressions.IsEmpty() || !processor.IsEnableSubqueryIndexShare || + disableIndexShare) + { + var activatorNamedWindow = + services.ViewableActivatorFactory.CreateNamedWindow(processor, namedSpec, statementContext); + var viewFactoryChain = services.ViewService.CreateFactories( + 0, namedWindowType, namedSpec.ViewSpecs, namedSpec.Options, statementContext, true, + subselect.SubselectNumber); + subselect.RawEventType = viewFactoryChain.EventType; + subSelectStreamDesc.Add( + subselect, + new SubSelectActivationHolder( + subselectStreamNumber, namedWindowType, viewFactoryChain, activatorNamedWindow, + streamSpec)); + services.NamedWindowConsumerMgmtService.AddConsumer(statementContext, namedSpec); + } + else + { + // else if there are no named window stream filter expressions and index sharing is enabled + var viewFactoryChain = services.ViewService.CreateFactories( + 0, processor.NamedWindowType, namedSpec.ViewSpecs, namedSpec.Options, statementContext, true, + subselect.SubselectNumber); + subselect.RawEventType = processor.NamedWindowType; + var activator = services.ViewableActivatorFactory.MakeSubqueryNWIndexShare(); + subSelectStreamDesc.Add( + subselect, + new SubSelectActivationHolder( + subselectStreamNumber, namedWindowType, viewFactoryChain, activator, streamSpec)); + services.StatementVariableRefService.AddReferences( + statementContext.StatementName, processor.NamedWindowType.Name); + } + } + } + + return subSelectStreamDesc; + } + + internal static SubSelectStrategyCollection PlanSubSelect( + EPServicesContext services, + StatementContext statementContext, + bool queryPlanLogging, + SubSelectActivationCollection subSelectStreamDesc, + string[] outerStreamNames, + EventType[] outerEventTypesSelect, + string[] outerEventTypeNamees, + ExprDeclaredNode[] declaredExpressions, + ContextPropertyRegistry contextPropertyRegistry) + { + var subqueryNum = -1; + var collection = new SubSelectStrategyCollection(); + + IDictionary> declaredExpressionCallHierarchy = null; + if (declaredExpressions.Length > 0) + { + declaredExpressionCallHierarchy = ExprNodeUtility.GetDeclaredExpressionCallHierarchy( + declaredExpressions); + } + + foreach (var entry in subSelectStreamDesc.Subqueries) + { + subqueryNum++; + var subselect = entry.Key; + var subSelectActivation = entry.Value; + + try + { + var factoryDesc = PlanSubSelectInternal( + subqueryNum, subselect, subSelectActivation, + services, statementContext, queryPlanLogging, subSelectStreamDesc, + outerStreamNames, outerEventTypesSelect, outerEventTypeNamees, + declaredExpressions, contextPropertyRegistry, declaredExpressionCallHierarchy); + collection.Add(subselect, factoryDesc); + } + catch (Exception ex) + { + throw new ExprValidationException( + "Failed to plan " + GetSubqueryInfoText(subqueryNum, subselect) + ": " + ex.Message, ex); + } + } + + return collection; + } + + public static IDictionary StartSubselects( + EPServicesContext services, + SubSelectStrategyCollection subSelectStrategyCollection, + AgentInstanceContext agentInstanceContext, + IList stopCallbackList, + bool isRecoveringResilient) + { + + var subselectStrategies = new Dictionary(); + + foreach (var subselectEntry in subSelectStrategyCollection.Subqueries) + { + + var subselectNode = subselectEntry.Key; + var factoryDesc = subselectEntry.Value; + var holder = factoryDesc.SubSelectActivationHolder; + + // activate viewable + var subselectActivationResult = holder.Activator.Activate( + agentInstanceContext, true, isRecoveringResilient); + stopCallbackList.Add(subselectActivationResult.StopCallback); + + // apply returning the strategy instance + var result = factoryDesc.Factory.Instantiate( + services, subselectActivationResult.Viewable, agentInstanceContext, stopCallbackList, + factoryDesc.SubqueryNumber, isRecoveringResilient); + + // handle stoppable view + if (result.SubselectView is StoppableView) + { + stopCallbackList.Add((StoppableView) result.SubselectView); + } + if (result.SubselectAggregationService != null) + { + var subselectAggregationService = result.SubselectAggregationService; + stopCallbackList.Add( + new ProxyStopCallback + { + ProcStop = subselectAggregationService.Stop + }); + } + + // set aggregation + var lookupStrategy = result.Strategy; + var aggregationPreprocessor = result.SubselectAggregationPreprocessor; + + // determine strategy + ExprSubselectStrategy strategy; + if (aggregationPreprocessor != null) + { + strategy = new ProxyExprSubselectStrategy() + { + ProcEvaluateMatching = (eventsPerStream, exprEvaluatorContext) => + { + var matchingEvents = lookupStrategy.Lookup( + eventsPerStream, exprEvaluatorContext); + aggregationPreprocessor.Evaluate(eventsPerStream, matchingEvents, exprEvaluatorContext); + return CollectionUtil.SINGLE_NULL_ROW_EVENT_SET; + } + }; + } + else + { + strategy = new ProxyExprSubselectStrategy + { + ProcEvaluateMatching = (eventsPerStream, exprEvaluatorContext) => lookupStrategy.Lookup( + eventsPerStream, + exprEvaluatorContext) + }; + } + + var instance = new SubSelectStrategyHolder( + strategy, + result.SubselectAggregationService, + result.PriorNodeStrategies, + result.PreviousNodeStrategies, + result.SubselectView, + result.PostLoad, + subselectActivationResult); + subselectStrategies.Put(subselectNode, instance); + } + + return subselectStrategies; + } + + private static Pair DetermineSubqueryIndexFactory( + ExprNode filterExpr, + EventType viewableEventType, + EventType[] outerEventTypes, + StreamTypeService subselectTypeService, + bool fullTableScan, + bool queryPlanLogging, + ICollection optionalUniqueProps, + StatementContext statementContext, + int subqueryNum) + { + var result = + DetermineSubqueryIndexInternalFactory( + filterExpr, viewableEventType, outerEventTypes, subselectTypeService, fullTableScan, + optionalUniqueProps, statementContext); + + var hook = QueryPlanIndexHookUtil.GetHook( + statementContext.Annotations, statementContext.EngineImportService); + if (queryPlanLogging && (QUERY_PLAN_LOG.IsInfoEnabled || hook != null)) + { + QUERY_PLAN_LOG.Info("local index"); + QUERY_PLAN_LOG.Info("strategy " + result.Second.ToQueryPlan()); + QUERY_PLAN_LOG.Info("table " + result.First.ToQueryPlan()); + if (hook != null) + { + string strategyName = result.Second.GetType().Name; + hook.Subquery( + new QueryPlanIndexDescSubquery( + new IndexNameAndDescPair[] + { + new IndexNameAndDescPair(null, result.First.EventTableType.Name) + }, subqueryNum, strategyName)); + } + } + + return result; + } + + private static Pair DetermineSubqueryIndexInternalFactory( + ExprNode filterExpr, + EventType viewableEventType, + EventType[] outerEventTypes, + StreamTypeService subselectTypeService, + bool fullTableScan, + ICollection optionalUniqueProps, + StatementContext statementContext) + { + // No filter expression means full table scan + if ((filterExpr == null) || fullTableScan) + { + var tableFactory = statementContext.EventTableIndexService.CreateUnindexed(0, null, false); + var strategy = new SubordFullTableScanLookupStrategyFactory(); + return new Pair(tableFactory, strategy); + } + + // Build a list of streams and indexes + var excludePlanHint = ExcludePlanHint.GetHint( + subselectTypeService.StreamNames, statementContext); + var joinPropDesc = QueryPlanIndexBuilder.GetJoinProps( + filterExpr, outerEventTypes.Length, subselectTypeService.EventTypes, excludePlanHint); + var hashKeys = joinPropDesc.HashProps; + var rangeKeys = joinPropDesc.RangeProps; + var hashKeyList = new List(hashKeys.Values); + var rangeKeyList = new List(rangeKeys.Values); + var unique = false; + ExprNode[] inKeywordSingleIdxKeys = null; + ExprNode inKeywordMultiIdxKey = null; + + // If this is a unique-view and there are unique criteria, use these + if (optionalUniqueProps != null && !optionalUniqueProps.IsEmpty()) + { + var found = true; + foreach (var uniqueProp in optionalUniqueProps) + { + if (!hashKeys.ContainsKey(uniqueProp)) + { + found = false; + break; + } + } + if (found) + { + string[] hashKeysArray = hashKeys.Keys.ToArray(); + foreach (var hashKey in hashKeysArray) + { + if (!optionalUniqueProps.Contains(hashKey)) + { + hashKeys.Remove(hashKey); + } + } + hashKeyList = new List(hashKeys.Values); + unique = true; + rangeKeyList.Clear(); + rangeKeys.Clear(); + } + } + + // build table (local table) + EventTableFactory eventTableFactory; + CoercionDesc hashCoercionDesc; + CoercionDesc rangeCoercionDesc; + if (hashKeys.Count != 0 && rangeKeys.IsEmpty()) + { + string[] indexedProps = hashKeys.Keys.ToArray(); + hashCoercionDesc = CoercionUtil.GetCoercionTypesHash(viewableEventType, indexedProps, hashKeyList); + rangeCoercionDesc = new CoercionDesc(false, null); + + if (hashKeys.Count == 1) + { + if (!hashCoercionDesc.IsCoerce) + { + eventTableFactory = statementContext.EventTableIndexService.CreateSingle( + 0, viewableEventType, indexedProps[0], unique, null, null, false); + } + else + { + eventTableFactory = statementContext.EventTableIndexService.CreateSingleCoerceAdd( + 0, viewableEventType, indexedProps[0], hashCoercionDesc.CoercionTypes[0], null, false); + } + } + else + { + if (!hashCoercionDesc.IsCoerce) + { + eventTableFactory = statementContext.EventTableIndexService.CreateMultiKey( + 0, viewableEventType, indexedProps, unique, null, null, false); + } + else + { + eventTableFactory = statementContext.EventTableIndexService.CreateMultiKeyCoerceAdd( + 0, viewableEventType, indexedProps, hashCoercionDesc.CoercionTypes, false); + } + } + } + else if (hashKeys.IsEmpty() && rangeKeys.IsEmpty()) + { + hashCoercionDesc = new CoercionDesc(false, null); + rangeCoercionDesc = new CoercionDesc(false, null); + if (joinPropDesc.InKeywordSingleIndex != null) + { + eventTableFactory = statementContext.EventTableIndexService.CreateSingle( + 0, viewableEventType, joinPropDesc.InKeywordSingleIndex.IndexedProp, unique, null, null, false); + inKeywordSingleIdxKeys = joinPropDesc.InKeywordSingleIndex.Expressions; + } + else if (joinPropDesc.InKeywordMultiIndex != null) + { + eventTableFactory = statementContext.EventTableIndexService.CreateInArray( + 0, viewableEventType, joinPropDesc.InKeywordMultiIndex.IndexedProp, unique); + inKeywordMultiIdxKey = joinPropDesc.InKeywordMultiIndex.Expression; + } + else + { + eventTableFactory = statementContext.EventTableIndexService.CreateUnindexed(0, null, false); + } + } + else if (hashKeys.IsEmpty() && rangeKeys.Count == 1) + { + string indexedProp = rangeKeys.Keys.First(); + var coercionRangeTypes = CoercionUtil.GetCoercionTypesRange( + viewableEventType, rangeKeys, outerEventTypes); + if (!coercionRangeTypes.IsCoerce) + { + eventTableFactory = statementContext.EventTableIndexService.CreateSorted( + 0, viewableEventType, indexedProp, false); + } + else + { + eventTableFactory = statementContext.EventTableIndexService.CreateSortedCoerce( + 0, viewableEventType, indexedProp, coercionRangeTypes.CoercionTypes[0], false); + } + hashCoercionDesc = new CoercionDesc(false, null); + rangeCoercionDesc = coercionRangeTypes; + } + else + { + string[] indexedKeyProps = hashKeys.Keys.ToArray(); + var coercionKeyTypes = SubordPropUtil.GetCoercionTypes(hashKeys.Values); + string[] indexedRangeProps = rangeKeys.Keys.ToArray(); + var coercionRangeTypes = CoercionUtil.GetCoercionTypesRange( + viewableEventType, rangeKeys, outerEventTypes); + eventTableFactory = statementContext.EventTableIndexService.CreateComposite( + 0, viewableEventType, indexedKeyProps, coercionKeyTypes, indexedRangeProps, + coercionRangeTypes.CoercionTypes, false); + hashCoercionDesc = CoercionUtil.GetCoercionTypesHash(viewableEventType, indexedKeyProps, hashKeyList); + rangeCoercionDesc = coercionRangeTypes; + } + + var subqTableLookupStrategyFactory = + SubordinateTableLookupStrategyUtil.GetLookupStrategy( + outerEventTypes, + hashKeyList, hashCoercionDesc, rangeKeyList, rangeCoercionDesc, inKeywordSingleIdxKeys, + inKeywordMultiIdxKey, false); + + return new Pair( + eventTableFactory, subqTableLookupStrategyFactory); + } + + private static StreamTypeService GetDeclaredExprTypeService( + ExprDeclaredNode[] declaredExpressions, + IDictionary> declaredExpressionCallHierarchy, + string[] outerStreamNames, + EventType[] outerEventTypesSelect, + string engineURI, + ExprSubselectNode subselect, + string subexpressionStreamName, + EventType eventType) + { + // Find that subselect within that any of the expression declarations + foreach (var declaration in declaredExpressions) + { + var visitor = new ExprNodeSubselectDeclaredNoTraverseVisitor(declaration); + visitor.Reset(); + declaration.Accept(visitor); + if (!visitor.Subselects.Contains(subselect)) + { + continue; + } + + // no type service for "alias" + if (declaration.Prototype.IsAlias) + { + return null; + } + + // subselect found - compute outer stream names + // initialize from the outermost provided stream names + var outerStreamNamesMap = new LinkedHashMap(); + var count = 0; + foreach (var outerStreamName in outerStreamNames) + { + outerStreamNamesMap.Put(outerStreamName, count++); + } + + // give each declared expression a chance to change the names (unless alias expression) + IDictionary outerStreamNamesForSubselect = outerStreamNamesMap; + var callers = declaredExpressionCallHierarchy.Get(declaration); + foreach (var caller in callers) + { + outerStreamNamesForSubselect = caller.GetOuterStreamNames(outerStreamNamesForSubselect); + } + outerStreamNamesForSubselect = declaration.GetOuterStreamNames(outerStreamNamesForSubselect); + + // compile a new StreamTypeService for use in validating that particular subselect + var eventTypes = new EventType[outerStreamNamesForSubselect.Count + 1]; + var streamNames = new string[outerStreamNamesForSubselect.Count + 1]; + eventTypes[0] = eventType; + streamNames[0] = subexpressionStreamName; + count = 0; + foreach (var entry in outerStreamNamesForSubselect) + { + eventTypes[count + 1] = outerEventTypesSelect[entry.Value]; + streamNames[count + 1] = entry.Key; + count++; + } + + var availableTypes = new StreamTypeServiceImpl( + eventTypes, streamNames, new bool[eventTypes.Length], engineURI, false); + availableTypes.RequireStreamNames = true; + return availableTypes; + } + return null; + } + + private static SubSelectStrategyFactoryDesc PlanSubSelectInternal( + int subqueryNum, + ExprSubselectNode subselect, + SubSelectActivationHolder subSelectActivation, + EPServicesContext services, + StatementContext statementContext, + bool queryPlanLogging, + SubSelectActivationCollection subSelectStreamDesc, + string[] outerStreamNames, + EventType[] outerEventTypesSelect, + string[] outerEventTypeNamees, + ExprDeclaredNode[] declaredExpressions, + ContextPropertyRegistry contextPropertyRegistry, + IDictionary> declaredExpressionCallHierarchy) + { + if (queryPlanLogging && QUERY_PLAN_LOG.IsInfoEnabled) + { + QUERY_PLAN_LOG.Info("For statement '" + statementContext.StatementName + "' subquery " + subqueryNum); + } + + var annotations = statementContext.Annotations; + var indexHint = IndexHint.GetIndexHint(statementContext.Annotations); + var statementSpec = subselect.StatementSpecCompiled; + var filterStreamSpec = statementSpec.StreamSpecs[0]; + + string subselecteventTypeName = null; + if (filterStreamSpec is FilterStreamSpecCompiled) + { + subselecteventTypeName = ((FilterStreamSpecCompiled) filterStreamSpec).FilterSpec.FilterForEventTypeName; + } + else if (filterStreamSpec is NamedWindowConsumerStreamSpec) + { + subselecteventTypeName = ((NamedWindowConsumerStreamSpec) filterStreamSpec).WindowName; + } + else if (filterStreamSpec is TableQueryStreamSpec) + { + subselecteventTypeName = ((TableQueryStreamSpec) filterStreamSpec).TableName; + } + + var viewFactoryChain = subSelectStreamDesc.GetViewFactoryChain(subselect); + var eventType = viewFactoryChain.EventType; + + // determine a stream name unless one was supplied + var subexpressionStreamName = filterStreamSpec.OptionalStreamName; + var subselectStreamNumber = subSelectStreamDesc.GetStreamNumber(subselect); + if (subexpressionStreamName == null) + { + subexpressionStreamName = "$subselect_" + subselectStreamNumber; + } + var allStreamNames = new string[outerStreamNames.Length + 1]; + Array.Copy(outerStreamNames, 0, allStreamNames, 1, outerStreamNames.Length); + allStreamNames[0] = subexpressionStreamName; + + // Named windows don't allow data views + if (filterStreamSpec is NamedWindowConsumerStreamSpec || filterStreamSpec is TableQueryStreamSpec) + { + EPStatementStartMethodHelperValidate.ValidateNoDataWindowOnNamedWindow(viewFactoryChain.FactoryChain); + } + + // Expression declarations are copies of a predefined expression body with their own stream context. + // Should only be invoked if the subselect belongs to that instance. + StreamTypeService subselectTypeService = null; + EventType[] outerEventTypes = null; + + // determine subselect type information from the enclosing declared expression, if possibly enclosed + if (declaredExpressions.Length > 0) + { + subselectTypeService = GetDeclaredExprTypeService( + declaredExpressions, declaredExpressionCallHierarchy, outerStreamNames, outerEventTypesSelect, + services.EngineURI, subselect, subexpressionStreamName, eventType); + if (subselectTypeService != null) + { + outerEventTypes = new EventType[subselectTypeService.EventTypes.Length - 1]; + Array.Copy( + subselectTypeService.EventTypes, 1, outerEventTypes, 0, + subselectTypeService.EventTypes.Length - 1); + } + } + + // Use the override provided by the subselect if present + if (subselectTypeService == null) + { + if (subselect.FilterSubqueryStreamTypes != null) + { + subselectTypeService = subselect.FilterSubqueryStreamTypes; + outerEventTypes = new EventType[subselectTypeService.EventTypes.Length - 1]; + Array.Copy( + subselectTypeService.EventTypes, 1, outerEventTypes, 0, + subselectTypeService.EventTypes.Length - 1); + } + else + { + // Streams event types are the original stream types with the stream zero the subselect stream + var namesAndTypes = new LinkedHashMap>(); + namesAndTypes.Put( + subexpressionStreamName, new Pair(eventType, subselecteventTypeName)); + for (var i = 0; i < outerEventTypesSelect.Length; i++) + { + var pair = new Pair(outerEventTypesSelect[i], outerEventTypeNamees[i]); + namesAndTypes.Put(outerStreamNames[i], pair); + } + subselectTypeService = new StreamTypeServiceImpl(namesAndTypes, services.EngineURI, true, true); + outerEventTypes = outerEventTypesSelect; + } + } + + // Validate select expression + var viewResourceDelegateSubselect = new ViewResourceDelegateUnverified(); + var selectClauseSpec = subselect.StatementSpecCompiled.SelectClauseSpec; + AggregationServiceFactoryDesc aggregationServiceFactoryDesc = null; + var selectExpressions = new List(); + var assignedNames = new List(); + var isWildcard = false; + var isStreamWildcard = false; + ExprEvaluator[] groupByEvaluators = null; + bool hasNonAggregatedProperties; + + var evaluatorContextStmt = new ExprEvaluatorContextStatement(statementContext, false); + var validationContext = new ExprValidationContext( + subselectTypeService, statementContext.EngineImportService, + statementContext.StatementExtensionServicesContext, viewResourceDelegateSubselect, + statementContext.SchedulingService, statementContext.VariableService, statementContext.TableService, + evaluatorContextStmt, statementContext.EventAdapterService, statementContext.StatementName, + statementContext.StatementId, statementContext.Annotations, statementContext.ContextDescriptor, + statementContext.ScriptingService, false, false, true, false, null, false); + var aggExprNodesSelect = new List(2); + + for (var i = 0; i < selectClauseSpec.SelectExprList.Length; i++) + { + var element = selectClauseSpec.SelectExprList[i]; + + if (element is SelectClauseExprCompiledSpec) + { + // validate + var compiled = (SelectClauseExprCompiledSpec) element; + var selectExpression = compiled.SelectExpression; + selectExpression = ExprNodeUtility.GetValidatedSubtree( + ExprNodeOrigin.SELECT, selectExpression, validationContext); + + selectExpressions.Add(selectExpression); + if (compiled.AssignedName == null) + { + assignedNames.Add(ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(selectExpression)); + } + else + { + assignedNames.Add(compiled.AssignedName); + } + + // handle aggregation + ExprAggregateNodeUtil.GetAggregatesBottomUp(selectExpression, aggExprNodesSelect); + + // This stream (stream 0) properties must either all be under aggregation, or all not be. + if (aggExprNodesSelect.Count > 0) + { + var propertiesNotAggregated = ExprNodeUtility.GetExpressionProperties(selectExpression, false); + foreach (var pair in propertiesNotAggregated) + { + if (pair.First == 0) + { + throw new ExprValidationException( + "Subselect properties must all be within aggregation functions"); + } + } + } + } + else if (element is SelectClauseElementWildcard) + { + isWildcard = true; + } + else if (element is SelectClauseStreamCompiledSpec) + { + isStreamWildcard = true; + } + } // end of for loop + + // validate having-clause and collect aggregations + var aggExpressionNodesHaving = Collections.GetEmptyList(); + if (statementSpec.HavingExprRootNode != null) + { + var validatedHavingClause = ExprNodeUtility.GetValidatedSubtree( + ExprNodeOrigin.HAVING, statementSpec.HavingExprRootNode, validationContext); + if (validatedHavingClause.ExprEvaluator.ReturnType.GetBoxedType() != typeof (bool?)) + { + throw new ExprValidationException("Subselect having-clause expression must return a boolean value"); + } + aggExpressionNodesHaving = new List(); + ExprAggregateNodeUtil.GetAggregatesBottomUp(validatedHavingClause, aggExpressionNodesHaving); + ValidateAggregationPropsAndLocalGroup(aggExpressionNodesHaving); + + // if the having-clause does not have aggregations, it becomes part of the filter + if (aggExpressionNodesHaving.IsEmpty()) + { + var filter = statementSpec.FilterRootNode; + if (filter == null) + { + statementSpec.FilterExprRootNode = statementSpec.HavingExprRootNode; + } + else + { + statementSpec.FilterExprRootNode = ExprNodeUtility.ConnectExpressionsByLogicalAnd( + Collections.List(statementSpec.FilterRootNode, statementSpec.HavingExprRootNode)); + } + statementSpec.HavingExprRootNode = null; + } + else + { + subselect.HavingExpr = validatedHavingClause.ExprEvaluator; + var nonAggregatedPropsHaving = + ExprNodeUtility.GetNonAggregatedProps( + validationContext.StreamTypeService.EventTypes, + Collections.SingletonList(validatedHavingClause), contextPropertyRegistry); + foreach (var prop in nonAggregatedPropsHaving.Properties) + { + if (prop.StreamNum == 0) + { + throw new ExprValidationException( + "Subselect having-clause requires that all properties are under aggregation, consider using the 'first' aggregation function instead"); + } + } + } + } + + // Figure out all non-aggregated event properties in the select clause (props not under a sum/avg/max aggregation node) + var nonAggregatedPropsSelect = + ExprNodeUtility.GetNonAggregatedProps( + validationContext.StreamTypeService.EventTypes, selectExpressions, contextPropertyRegistry); + hasNonAggregatedProperties = !nonAggregatedPropsSelect.IsEmpty(); + + // Validate and set select-clause names and expressions + if (!selectExpressions.IsEmpty()) + { + if (isWildcard || isStreamWildcard) + { + throw new ExprValidationException( + "Subquery multi-column select does not allow wildcard or stream wildcard when selecting multiple columns."); + } + if (selectExpressions.Count > 1 && !subselect.IsAllowMultiColumnSelect) + { + throw new ExprValidationException("Subquery multi-column select is not allowed in this context."); + } + if (statementSpec.GroupByExpressions == null && selectExpressions.Count > 1 && + aggExprNodesSelect.Count > 0 && hasNonAggregatedProperties) + { + throw new ExprValidationException( + "Subquery with multi-column select requires that either all or none of the selected columns are under aggregation, unless a group-by clause is also specified"); + } + subselect.SelectClause = selectExpressions.ToArray(); + subselect.SelectAsNames = assignedNames.ToArray(); + } + + // Handle aggregation + ExprNodePropOrStreamSet propertiesGroupBy = null; + if (aggExprNodesSelect.Count > 0 || aggExpressionNodesHaving.Count > 0) + { + if (statementSpec.GroupByExpressions != null && + statementSpec.GroupByExpressions.GroupByRollupLevels != null) + { + throw new ExprValidationException("Group-by expressions in a subselect may not have rollups"); + } + var theGroupBy = statementSpec.GroupByExpressions == null + ? null + : statementSpec.GroupByExpressions.GroupByNodes; + var hasGroupBy = theGroupBy != null && theGroupBy.Length > 0; + if (hasGroupBy) + { + var groupByNodes = statementSpec.GroupByExpressions.GroupByNodes; + groupByEvaluators = new ExprEvaluator[groupByNodes.Length]; + + // validate group-by + for (var i = 0; i < groupByNodes.Length; i++) + { + groupByNodes[i] = ExprNodeUtility.GetValidatedSubtree( + ExprNodeOrigin.GROUPBY, groupByNodes[i], validationContext); + groupByEvaluators[i] = groupByNodes[i].ExprEvaluator; + var minimal = ExprNodeUtility.IsMinimalExpression(groupByNodes[i]); + if (minimal != null) + { + throw new ExprValidationException( + "Group-by expressions in a subselect may not have " + minimal); + } + } + + // Get a list of event properties being aggregated in the select clause, if any + propertiesGroupBy = ExprNodeUtility.GetGroupByPropertiesValidateHasOne(groupByNodes); + + // Validated all group-by properties come from stream itself + var firstNonZeroGroupBy = propertiesGroupBy.FirstWithStreamNumNotZero; + if (firstNonZeroGroupBy != null) + { + throw new ExprValidationException( + "Subselect with group-by requires that group-by properties are provided by the subselect stream only (" + + firstNonZeroGroupBy.Textual + " is not)"); + } + + // Validate that this is a grouped full-aggregated case + var reasonMessage = propertiesGroupBy.NotContainsAll(nonAggregatedPropsSelect); + var allInGroupBy = reasonMessage == null; + if (!allInGroupBy) + { + throw new ExprValidationException( + "Subselect with group-by requires non-aggregated properties in the select-clause to also appear in the group-by clause"); + } + } + + // Other stream properties, if there is aggregation, cannot be under aggregation. + ValidateAggregationPropsAndLocalGroup(aggExprNodesSelect); + + // determine whether select-clause has grouped-by expressions + List groupKeyExpressions = null; + var groupByExpressions = new ExprNode[0]; + if (hasGroupBy) + { + groupByExpressions = statementSpec.GroupByExpressions.GroupByNodes; + for (var i = 0; i < selectExpressions.Count; i++) + { + ExprNode selectExpression = selectExpressions[i]; + var revalidate = false; + for (var j = 0; j < groupByExpressions.Length; j++) + { + var foundPairs = ExprNodeUtility.FindExpression(selectExpression, groupByExpressions[j]); + foreach (var pair in foundPairs) + { + var replacement = new ExprAggregateNodeGroupKey(j, groupByEvaluators[j].ReturnType); + if (pair.First == null) + { + selectExpressions[i] = replacement; + } + else + { + ExprNodeUtility.ReplaceChildNode(pair.First, pair.Second, replacement); + revalidate = true; + } + if (groupKeyExpressions == null) + { + groupKeyExpressions = new List(); + } + groupKeyExpressions.Add(replacement); + } + } + + // if the select-clause expression changed, revalidate it + if (revalidate) + { + selectExpression = ExprNodeUtility.GetValidatedSubtree( + ExprNodeOrigin.SELECT, selectExpression, validationContext); + selectExpressions[i] = selectExpression; + } + } // end of for loop + } + + aggregationServiceFactoryDesc = AggregationServiceFactoryFactory.GetService( + aggExprNodesSelect, + Collections.GetEmptyMap(), + Collections.GetEmptyList(), groupByExpressions, aggExpressionNodesHaving, + Collections.GetEmptyList(), groupKeyExpressions, hasGroupBy, annotations, + statementContext.VariableService, false, true, statementSpec.FilterRootNode, + statementSpec.HavingExprRootNode, statementContext.AggregationServiceFactoryService, + subselectTypeService.EventTypes, null, statementSpec.OptionalContextName, null, null, false, false, + false, statementContext.EngineImportService); + + // assign select-clause + if (!selectExpressions.IsEmpty()) + { + subselect.SelectClause = selectExpressions.ToArray(); + subselect.SelectAsNames = assignedNames.ToArray(); + } + } + + // no aggregation functions allowed in filter + if (statementSpec.FilterRootNode != null) + { + var aggExprNodesFilter = new List(); + ExprAggregateNodeUtil.GetAggregatesBottomUp(statementSpec.FilterRootNode, aggExprNodesFilter); + if (aggExprNodesFilter.Count > 0) + { + throw new ExprValidationException( + "Aggregation functions are not supported within subquery filters, consider using a having-clause or insert-into instead"); + } + } + + // validate filter expression, if there is one + var filterExpr = statementSpec.FilterRootNode; + + // add the table filter for tables + if (filterStreamSpec is TableQueryStreamSpec) + { + var table = (TableQueryStreamSpec) filterStreamSpec; + filterExpr = ExprNodeUtility.ConnectExpressionsByLogicalAnd(table.FilterExpressions, filterExpr); + } + + // determine correlated + var correlatedSubquery = false; + if (filterExpr != null) + { + filterExpr = ExprNodeUtility.GetValidatedSubtree(ExprNodeOrigin.FILTER, filterExpr, validationContext); + if (filterExpr.ExprEvaluator.ReturnType.GetBoxedType() != typeof (bool?)) + { + throw new ExprValidationException("Subselect filter expression must return a boolean value"); + } + + // check the presence of a correlated filter, not allowed with aggregation + var visitor = new ExprNodeIdentifierVisitor(true); + filterExpr.Accept(visitor); + var propertiesNodes = visitor.ExprProperties; + foreach (var pair in propertiesNodes) + { + if (pair.First != 0) + { + correlatedSubquery = true; + break; + } + } + } + + var viewResourceDelegateVerified = + EPStatementStartMethodHelperViewResources.VerifyPreviousAndPriorRequirements( + new ViewFactoryChain[] + { + viewFactoryChain + }, viewResourceDelegateSubselect); + var priorNodes = viewResourceDelegateVerified.PerStream[0].PriorRequestsAsList; + var previousNodes = viewResourceDelegateVerified.PerStream[0].PreviousRequests; + + // Set the aggregated flag + // This must occur here as some analysis of return type depends on aggregated or not. + if (aggregationServiceFactoryDesc == null) + { + subselect.SubselectAggregationType = ExprSubselectNode.SubqueryAggregationType.NONE; + } + else + { + subselect.SubselectAggregationType = hasNonAggregatedProperties + ? ExprSubselectNode.SubqueryAggregationType.FULLY_AGGREGATED_WPROPS + : ExprSubselectNode.SubqueryAggregationType.FULLY_AGGREGATED_NOPROPS; + } + + // Set the filter. + var filterExprEval = (filterExpr == null) ? null : filterExpr.ExprEvaluator; + var assignedFilterExpr = aggregationServiceFactoryDesc != null ? null : filterExprEval; + subselect.FilterExpr = assignedFilterExpr; + + // validation for correlated subqueries against named windows contained-event syntax + if (filterStreamSpec is NamedWindowConsumerStreamSpec && correlatedSubquery) + { + var namedSpec = (NamedWindowConsumerStreamSpec) filterStreamSpec; + if (namedSpec.OptPropertyEvaluator != null) + { + throw new ExprValidationException( + "Failed to validate named window use in subquery, contained-event is only allowed for named windows when not correlated"); + } + } + + // Validate presence of a data window + ValidateSubqueryDataWindow( + subselect, correlatedSubquery, hasNonAggregatedProperties, propertiesGroupBy, nonAggregatedPropsSelect); + + // Determine strategy factories + // + + // handle named window index share first + if (filterStreamSpec is NamedWindowConsumerStreamSpec) + { + var namedSpec = (NamedWindowConsumerStreamSpec) filterStreamSpec; + if (namedSpec.FilterExpressions.IsEmpty()) + { + var processor = services.NamedWindowMgmtService.GetProcessor(namedSpec.WindowName); + if (processor == null) + { + throw new ExprValidationException( + "A named window by name '" + namedSpec.WindowName + "' does not exist"); + } + + var disableIndexShare = HintEnum.DISABLE_WINDOW_SUBQUERY_INDEXSHARE.GetHint(annotations) != null; + if (disableIndexShare && processor.IsVirtualDataWindow) + { + disableIndexShare = false; + } + + if (!disableIndexShare && processor.IsEnableSubqueryIndexShare) + { + ValidateContextAssociation( + statementContext, processor.ContextName, "named window '" + processor.NamedWindowName + "'"); + if (queryPlanLogging && QUERY_PLAN_LOG.IsInfoEnabled) + { + QUERY_PLAN_LOG.Info("prefering shared index"); + } + var fullTableScanX = HintEnum.SET_NOINDEX.GetHint(annotations) != null; + var excludePlanHint = ExcludePlanHint.GetHint(allStreamNames, statementContext); + var joinedPropPlan = QueryPlanIndexBuilder.GetJoinProps( + filterExpr, outerEventTypes.Length, subselectTypeService.EventTypes, excludePlanHint); + var factoryX = new SubSelectStrategyFactoryIndexShare( + statementContext.StatementName, statementContext.StatementId, subqueryNum, + outerEventTypesSelect, + processor, null, fullTableScanX, indexHint, joinedPropPlan, filterExprEval, + aggregationServiceFactoryDesc, groupByEvaluators, services.TableService, + statementContext.Annotations, statementContext.StatementStopService, + statementContext.EngineImportService); + return new SubSelectStrategyFactoryDesc( + subSelectActivation, factoryX, aggregationServiceFactoryDesc, priorNodes, previousNodes, + subqueryNum); + } + } + } + + // handle table-subselect + if (filterStreamSpec is TableQueryStreamSpec) + { + var tableSpec = (TableQueryStreamSpec) filterStreamSpec; + var metadata = services.TableService.GetTableMetadata(tableSpec.TableName); + if (metadata == null) + { + throw new ExprValidationException("A table by name '" + tableSpec.TableName + "' does not exist"); + } + + ValidateContextAssociation( + statementContext, metadata.ContextName, "table '" + tableSpec.TableName + "'"); + var fullTableScanX = HintEnum.SET_NOINDEX.GetHint(annotations) != null; + var excludePlanHint = ExcludePlanHint.GetHint(allStreamNames, statementContext); + var joinedPropPlan = QueryPlanIndexBuilder.GetJoinProps( + filterExpr, outerEventTypes.Length, subselectTypeService.EventTypes, excludePlanHint); + var factoryX = new SubSelectStrategyFactoryIndexShare( + statementContext.StatementName, statementContext.StatementId, subqueryNum, outerEventTypesSelect, + null, metadata, fullTableScanX, indexHint, joinedPropPlan, filterExprEval, + aggregationServiceFactoryDesc, groupByEvaluators, services.TableService, + statementContext.Annotations, statementContext.StatementStopService, + statementContext.EngineImportService); + return new SubSelectStrategyFactoryDesc( + subSelectActivation, factoryX, aggregationServiceFactoryDesc, priorNodes, previousNodes, subqueryNum); + } + + // determine unique keys, if any + ICollection optionalUniqueProps = null; + if (viewFactoryChain.DataWindowViewFactoryCount > 0) + { + optionalUniqueProps = ViewServiceHelper.GetUniqueCandidateProperties( + viewFactoryChain.FactoryChain, annotations); + } + if (filterStreamSpec is NamedWindowConsumerStreamSpec) + { + var namedSpec = (NamedWindowConsumerStreamSpec) filterStreamSpec; + var processor = services.NamedWindowMgmtService.GetProcessor(namedSpec.WindowName); + optionalUniqueProps = processor.OptionalUniqueKeyProps; + } + + // handle local stream + named-window-stream + var fullTableScan = HintEnum.SET_NOINDEX.GetHint(annotations) != null; + var indexPair = + DetermineSubqueryIndexFactory( + filterExpr, eventType, + outerEventTypes, subselectTypeService, fullTableScan, queryPlanLogging, optionalUniqueProps, + statementContext, subqueryNum); + + var factory = new SubSelectStrategyFactoryLocalViewPreloaded( + subqueryNum, subSelectActivation, indexPair, filterExpr, filterExprEval, correlatedSubquery, + aggregationServiceFactoryDesc, viewResourceDelegateVerified, groupByEvaluators); + return new SubSelectStrategyFactoryDesc( + subSelectActivation, factory, aggregationServiceFactoryDesc, priorNodes, previousNodes, subqueryNum); + } + + public static string GetSubqueryInfoText(int subqueryNum, ExprSubselectNode subselect) + { + var text = "subquery number " + (subqueryNum + 1); + var streamRaw = subselect.StatementSpecRaw.StreamSpecs[0]; + if (streamRaw is FilterStreamSpecRaw) + { + text += " querying " + ((FilterStreamSpecRaw) streamRaw).RawFilterSpec.EventTypeName; + } + return text; + } + + private static string ValidateContextAssociation( + StatementContext statementContext, + string entityDeclaredContextName, + string entityDesc) + { + var optionalProvidedContextName = statementContext.ContextDescriptor == null + ? null + : statementContext.ContextDescriptor.ContextName; + if (entityDeclaredContextName != null) + { + if (optionalProvidedContextName == null || + !optionalProvidedContextName.Equals(entityDeclaredContextName)) + { + throw new ExprValidationException( + "Mismatch in context specification, the context for the " + entityDesc + " is '" + + entityDeclaredContextName + "' and the query specifies " + + (optionalProvidedContextName == null + ? "no context " + : "context '" + optionalProvidedContextName + "'")); + } + } + return null; + } + + private static void ValidateSubqueryDataWindow( + ExprSubselectNode subselectNode, + bool correlatedSubquery, + bool hasNonAggregatedProperties, + ExprNodePropOrStreamSet propertiesGroupBy, + ExprNodePropOrStreamSet nonAggregatedPropsSelect) + { + // validation applies only to type+filter subqueries that have no data window + var streamSpec = subselectNode.StatementSpecCompiled.StreamSpecs[0]; + if (!(streamSpec is FilterStreamSpecCompiled) || streamSpec.ViewSpecs.Length > 0) + { + return; + } + + if (correlatedSubquery) + { + throw new ExprValidationException(MSG_SUBQUERY_REQUIRES_WINDOW); + } + + // we have non-aggregated properties + if (hasNonAggregatedProperties) + { + if (propertiesGroupBy == null) + { + throw new ExprValidationException(MSG_SUBQUERY_REQUIRES_WINDOW); + } + + var reason = nonAggregatedPropsSelect.NotContainsAll(propertiesGroupBy); + if (reason != null) + { + throw new ExprValidationException(MSG_SUBQUERY_REQUIRES_WINDOW); + } + } + } + + private static void ValidateAggregationPropsAndLocalGroup(IList aggregateNodes) + { + foreach (var aggNode in aggregateNodes) + { + var propertiesNodesAggregated = ExprNodeUtility.GetExpressionProperties(aggNode, true); + foreach (var pair in propertiesNodesAggregated) + { + if (pair.First != 0) + { + throw new ExprValidationException( + "Subselect aggregation functions cannot aggregate across correlated properties"); + } + } + + if (aggNode.OptionalLocalGroupBy != null) + { + throw new ExprValidationException("Subselect aggregations functions cannot specify a group-by"); + } + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodHelperTableAccess.cs b/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodHelperTableAccess.cs new file mode 100755 index 000000000..e8c934f6d --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodHelperTableAccess.cs @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.table; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.epl.table.strategy; + +namespace com.espertech.esper.core.start +{ + public class EPStatementStartMethodHelperTableAccess + { + public static IDictionary AttachTableAccess( + EPServicesContext services, + AgentInstanceContext agentInstanceContext, + ExprTableAccessNode[] tableNodes) + { + if (tableNodes == null || tableNodes.Length == 0) + { + return Collections.GetEmptyMap(); + } + + IDictionary strategies = + new Dictionary(); + foreach (var tableNode in tableNodes) + { + var writesToTables = agentInstanceContext.StatementContext.IsWritesToTables; + TableAndLockProvider provider = services.TableService.GetStateProvider(tableNode.TableName, agentInstanceContext.AgentInstanceId, writesToTables); + TableMetadata tableMetadata = services.TableService.GetTableMetadata(tableNode.TableName); + ExprTableAccessEvalStrategy strategy = ExprTableEvalStrategyFactory.GetTableAccessEvalStrategy(tableNode, provider, tableMetadata); + strategies.Put(tableNode, strategy); + } + + return strategies; + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodHelperUtil.cs b/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodHelperUtil.cs new file mode 100755 index 000000000..ca1196ccd --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodHelperUtil.cs @@ -0,0 +1,108 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.subquery; +using com.espertech.esper.epl.spec; +using com.espertech.esper.events; +using com.espertech.esper.pattern; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.start +{ + public class EPStatementStartMethodHelperUtil + { + public static Pair StartResultSetAndAggregation(ResultSetProcessorFactoryDesc resultSetProcessorPrototype, AgentInstanceContext agentInstanceContext, bool isSubquery, int? subqueryNumber) + { + AggregationService aggregationService = null; + if (resultSetProcessorPrototype.AggregationServiceFactoryDesc != null) + { + aggregationService = + resultSetProcessorPrototype.AggregationServiceFactoryDesc.AggregationServiceFactory.MakeService( + agentInstanceContext, agentInstanceContext.StatementContext.EngineImportService, isSubquery, subqueryNumber); + } + + OrderByProcessor orderByProcessor = null; + if (resultSetProcessorPrototype.OrderByProcessorFactory != null) { + orderByProcessor = resultSetProcessorPrototype.OrderByProcessorFactory.Instantiate(aggregationService, agentInstanceContext); + } + + ResultSetProcessor resultSetProcessor = resultSetProcessorPrototype.ResultSetProcessorFactory.Instantiate(orderByProcessor, aggregationService, agentInstanceContext); + + return new Pair(resultSetProcessor, aggregationService); + } + + /// + /// Returns a stream name assigned for each stream, generated if none was supplied. + /// + /// stream specifications + /// array of stream names + + internal static String[] DetermineStreamNames(StreamSpecCompiled[] streams) + { + String[] streamNames = new String[streams.Length]; + for (int i = 0; i < streams.Length; i++) + { + // Assign a stream name for joins, if not supplied + streamNames[i] = streams[i].OptionalStreamName; + if (streamNames[i] == null) + { + streamNames[i] = "stream_" + i; + } + } + return streamNames; + } + + internal static bool[] GetHasIStreamOnly(bool[] isNamedWindow, ViewFactoryChain[] unmaterializedViewChain) + { + bool[] result = new bool[unmaterializedViewChain.Length]; + for (int i = 0; i < unmaterializedViewChain.Length; i++) { + if (isNamedWindow[i]) { + continue; + } + result[i] = unmaterializedViewChain[i].DataWindowViewFactoryCount == 0; + } + return result; + } + + internal static bool DetermineSubquerySameStream(StatementSpecCompiled statementSpec, FilterStreamSpecCompiled filterStreamSpec) + { + foreach (ExprSubselectNode subselect in statementSpec.SubSelectExpressions) { + StreamSpecCompiled streamSpec = subselect.StatementSpecCompiled.StreamSpecs[0]; + if (!(streamSpec is FilterStreamSpecCompiled)) { + continue; + } + FilterStreamSpecCompiled filterStream = (FilterStreamSpecCompiled) streamSpec; + EventType typeSubselect = filterStream.FilterSpec.FilterForEventType; + EventType typeFiltered = filterStreamSpec.FilterSpec.FilterForEventType; + if (EventTypeUtility.IsTypeOrSubTypeOf(typeSubselect, typeFiltered) || EventTypeUtility.IsTypeOrSubTypeOf(typeFiltered, typeSubselect)) { + return true; + } + } + return false; + } + + internal static bool IsConsumingFilters(EvalFactoryNode evalNode) + { + if (evalNode is EvalFilterFactoryNode) { + return ((EvalFilterFactoryNode) evalNode).ConsumptionLevel != null; + } + bool consumption = false; + foreach (EvalFactoryNode child in evalNode.ChildNodes) { + consumption = consumption || IsConsumingFilters(child); + } + return consumption; + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodHelperValidate.cs b/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodHelperValidate.cs new file mode 100755 index 000000000..bbfa177e4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodHelperValidate.cs @@ -0,0 +1,364 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.ops; +using com.espertech.esper.epl.expression.visitor; +using com.espertech.esper.epl.named; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.view; +using com.espertech.esper.util; +using com.espertech.esper.view; +using com.espertech.esper.view.std; + +namespace com.espertech.esper.core.start +{ + public class EPStatementStartMethodHelperValidate + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + public static void ValidateNoDataWindowOnNamedWindow(IList viewFactories) + { + foreach (var viewFactory in viewFactories) + { + if ((viewFactory is GroupByViewFactory) || ((viewFactory is MergeViewFactory))) + { + continue; + } + if (viewFactory is DataWindowViewFactory) + { + throw new ExprValidationException(NamedWindowMgmtServiceConstants.ERROR_MSG_NO_DATAWINDOW_ALLOWED); + } + } + } + + /// + /// Validate filter and join expression nodes. + /// + /// the compiled statement + /// the statement services + /// the event types for streams + /// the delegate to verify expressions that use view resources + internal static void ValidateNodes( + StatementSpecCompiled statementSpec, + StatementContext statementContext, + StreamTypeService typeService, + ViewResourceDelegateUnverified viewResourceDelegate) + { + var engineImportService = statementContext.EngineImportService; + var evaluatorContextStmt = new ExprEvaluatorContextStatement(statementContext, false); + var intoTableName = statementSpec.IntoTableSpec == null ? null : statementSpec.IntoTableSpec.Name; + + if (statementSpec.FilterRootNode != null) + { + var optionalFilterNode = statementSpec.FilterRootNode; + + // Validate where clause, initializing nodes to the stream ids used + try + { + var validationContext = new ExprValidationContext( + typeService, engineImportService, statementContext.StatementExtensionServicesContext, + viewResourceDelegate, statementContext.SchedulingService, statementContext.VariableService, + statementContext.TableService, evaluatorContextStmt, statementContext.EventAdapterService, + statementContext.StatementName, statementContext.StatementId, statementContext.Annotations, + statementContext.ContextDescriptor, statementContext.ScriptingService, + false, false, true, false, intoTableName, false); + optionalFilterNode = ExprNodeUtility.GetValidatedSubtree( + ExprNodeOrigin.FILTER, optionalFilterNode, validationContext); + if (optionalFilterNode.ExprEvaluator.ReturnType != typeof (bool) && + optionalFilterNode.ExprEvaluator.ReturnType != typeof (bool?)) + { + throw new ExprValidationException("The where-clause filter expression must return a boolean value"); + } + statementSpec.FilterExprRootNode = optionalFilterNode; + + // Make sure there is no aggregation in the where clause + var aggregateNodes = new List(); + ExprAggregateNodeUtil.GetAggregatesBottomUp(optionalFilterNode, aggregateNodes); + if (!aggregateNodes.IsEmpty()) + { + throw new ExprValidationException( + "An aggregate function may not appear in a WHERE clause (use the HAVING clause)"); + } + } + catch (ExprValidationException ex) + { + Log.Debug( + ".validateNodes Validation exception for filter=" + + ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(optionalFilterNode), ex); + throw new EPStatementException( + "Error validating expression: " + ex.Message, ex, statementContext.Expression); + } + } + + if ((statementSpec.OutputLimitSpec != null) && + ((statementSpec.OutputLimitSpec.WhenExpressionNode != null) || + (statementSpec.OutputLimitSpec.AndAfterTerminateExpr != null))) + { + // Validate where clause, initializing nodes to the stream ids used + try + { + var outputLimitType = + OutputConditionExpressionFactory.GetBuiltInEventType(statementContext.EventAdapterService); + var typeServiceOutputWhen = new StreamTypeServiceImpl( + new EventType[] { outputLimitType }, + new string[] { null }, + new bool[] { true }, + statementContext.EngineURI, false); + var validationContext = new ExprValidationContext( + typeServiceOutputWhen, engineImportService, statementContext.StatementExtensionServicesContext, + null, statementContext.SchedulingService, statementContext.VariableService, + statementContext.TableService, evaluatorContextStmt, statementContext.EventAdapterService, + statementContext.StatementName, statementContext.StatementId, statementContext.Annotations, + statementContext.ContextDescriptor, statementContext.ScriptingService, + false, false, false, false, intoTableName, false); + + var outputLimitWhenNode = statementSpec.OutputLimitSpec.WhenExpressionNode; + if (outputLimitWhenNode != null) + { + outputLimitWhenNode = ExprNodeUtility.GetValidatedSubtree( + ExprNodeOrigin.OUTPUTLIMIT, outputLimitWhenNode, validationContext); + statementSpec.OutputLimitSpec.WhenExpressionNode = outputLimitWhenNode; + + if (outputLimitWhenNode.ExprEvaluator.ReturnType.GetBoxedType() != typeof (bool?)) + { + throw new ExprValidationException( + "The when-trigger expression in the OUTPUT WHEN clause must return a boolean-type value"); + } + EPStatementStartMethodHelperValidate.ValidateNoAggregations( + outputLimitWhenNode, "An aggregate function may not appear in a OUTPUT LIMIT clause"); + } + + // validate and-terminate expression if provided + if (statementSpec.OutputLimitSpec.AndAfterTerminateExpr != null) + { + if (statementSpec.OutputLimitSpec.RateType != OutputLimitRateType.WHEN_EXPRESSION && + statementSpec.OutputLimitSpec.RateType != OutputLimitRateType.TERM) + { + throw new ExprValidationException( + "A terminated-and expression must be used with the OUTPUT WHEN clause"); + } + var validated = ExprNodeUtility.GetValidatedSubtree( + ExprNodeOrigin.OUTPUTLIMIT, statementSpec.OutputLimitSpec.AndAfterTerminateExpr, + validationContext); + statementSpec.OutputLimitSpec.AndAfterTerminateExpr = validated; + + if (validated.ExprEvaluator.ReturnType.GetBoxedType() != typeof (bool?)) + { + throw new ExprValidationException( + "The terminated-and expression must return a boolean-type value"); + } + EPStatementStartMethodHelperValidate.ValidateNoAggregations( + validated, "An aggregate function may not appear in a terminated-and clause"); + } + + // validate then-expression + ValidateThenSetAssignments(statementSpec.OutputLimitSpec.ThenExpressions, validationContext); + + // validate after-terminated then-expression + ValidateThenSetAssignments( + statementSpec.OutputLimitSpec.AndAfterTerminateThenExpressions, validationContext); + } + catch (ExprValidationException ex) + { + throw new EPStatementException( + "Error validating expression: " + ex.Message, statementContext.Expression); + } + } + + for (var outerJoinCount = 0; outerJoinCount < statementSpec.OuterJoinDescList.Length; outerJoinCount++) + { + var outerJoinDesc = statementSpec.OuterJoinDescList[outerJoinCount]; + + // validate on-expression nodes, if provided + if (outerJoinDesc.OptLeftNode != null) + { + var streamIdPair = ValidateOuterJoinPropertyPair( + statementContext, outerJoinDesc.OptLeftNode, outerJoinDesc.OptRightNode, outerJoinCount, + typeService, viewResourceDelegate); + + if (outerJoinDesc.AdditionalLeftNodes != null) + { + var streamSet = new HashSet(); + streamSet.Add(streamIdPair.First); + streamSet.Add(streamIdPair.Second); + for (var i = 0; i < outerJoinDesc.AdditionalLeftNodes.Length; i++) + { + var streamIdPairAdd = ValidateOuterJoinPropertyPair( + statementContext, outerJoinDesc.AdditionalLeftNodes[i], + outerJoinDesc.AdditionalRightNodes[i], outerJoinCount, + typeService, viewResourceDelegate); + + // make sure all additional properties point to the same two streams + if (!streamSet.Contains(streamIdPairAdd.First) || + (!streamSet.Contains(streamIdPairAdd.Second))) + { + const string message = + "Outer join ON-clause columns must refer to properties of the same joined streams" + + " when using multiple columns in the on-clause"; + throw new EPStatementException( + "Error validating expression: " + message, statementContext.Expression); + } + } + } + } + } + } + + private static void ValidateThenSetAssignments( + IList assignments, + ExprValidationContext validationContext) + { + if (assignments == null || assignments.IsEmpty()) + { + return; + } + foreach (var assign in assignments) + { + var node = ExprNodeUtility.GetValidatedAssignment(assign, validationContext); + assign.Expression = node; + EPStatementStartMethodHelperValidate.ValidateNoAggregations( + node, "An aggregate function may not appear in a OUTPUT LIMIT clause"); + } + } + + internal static UniformPair ValidateOuterJoinPropertyPair( + StatementContext statementContext, + ExprIdentNode leftNode, + ExprIdentNode rightNode, + int outerJoinCount, + StreamTypeService typeService, + ViewResourceDelegateUnverified viewResourceDelegate) { + // Validate the outer join clause using an artificial equals-node on top. + // Thus types are checked via equals. + // Sets stream ids used for validated nodes. + var equalsNode = new ExprEqualsNodeImpl(false, false); + equalsNode.AddChildNode(leftNode); + equalsNode.AddChildNode(rightNode); + var evaluatorContextStmt = new ExprEvaluatorContextStatement(statementContext, false); + try + { + var validationContext = new ExprValidationContext( + typeService, statementContext.EngineImportService, + statementContext.StatementExtensionServicesContext, viewResourceDelegate, + statementContext.SchedulingService, statementContext.VariableService, statementContext.TableService, + evaluatorContextStmt, statementContext.EventAdapterService, statementContext.StatementName, + statementContext.StatementId, statementContext.Annotations, statementContext.ContextDescriptor, + statementContext.ScriptingService, + false, false, true, false, null, false); + ExprNodeUtility.GetValidatedSubtree(ExprNodeOrigin.JOINON, equalsNode, validationContext); + } catch (ExprValidationException ex) { + Log.Debug("Validation exception for outer join node=" + ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(equalsNode), ex); + throw new EPStatementException("Error validating expression: " + ex.Message, statementContext.Expression); + } + + // Make sure we have left-hand-side and right-hand-side refering to different streams + var streamIdLeft = leftNode.StreamId; + var streamIdRight = rightNode.StreamId; + if (streamIdLeft == streamIdRight) + { + const string message = "Outer join ON-clause cannot refer to properties of the same stream"; + throw new EPStatementException("Error validating expression: " + message, statementContext.Expression); + } + + // Make sure one of the properties refers to the acutual stream currently being joined + var expectedStreamJoined = outerJoinCount + 1; + if ((streamIdLeft != expectedStreamJoined) && (streamIdRight != expectedStreamJoined)) { + var message = "Outer join ON-clause must refer to at least one property of the joined stream" + + " for stream " + expectedStreamJoined; + throw new EPStatementException("Error validating expression: " + message, statementContext.Expression); + } + + // Make sure neither of the streams refer to a 'future' stream + string badPropertyName = null; + if (streamIdLeft > outerJoinCount + 1) { + badPropertyName = leftNode.ResolvedPropertyName; + } + if (streamIdRight > outerJoinCount + 1) { + badPropertyName = rightNode.ResolvedPropertyName; + } + if (badPropertyName != null) { + var message = "Outer join ON-clause invalid scope for property" + + " '" + badPropertyName + "', expecting the current or a prior stream scope"; + throw new EPStatementException("Error validating expression: " + message, statementContext.Expression); + } + + return new UniformPair(streamIdLeft, streamIdRight); + } + + internal static ExprNode ValidateExprNoAgg( + ExprNodeOrigin exprNodeOrigin, + ExprNode exprNode, + StreamTypeService streamTypeService, + StatementContext statementContext, + ExprEvaluatorContext exprEvaluatorContext, + string errorMsg, + bool allowTableConsumption) + { + var validationContext = new ExprValidationContext( + streamTypeService, statementContext.EngineImportService, + statementContext.StatementExtensionServicesContext, null, statementContext.SchedulingService, + statementContext.VariableService, statementContext.TableService, exprEvaluatorContext, + statementContext.EventAdapterService, statementContext.StatementName, statementContext.StatementId, + statementContext.Annotations, statementContext.ContextDescriptor, statementContext.ScriptingService, + false, false, allowTableConsumption, + false, null, false); + var validated = ExprNodeUtility.GetValidatedSubtree(exprNodeOrigin, exprNode, validationContext); + ValidateNoAggregations(validated, errorMsg); + return validated; + } + + internal static void ValidateNoAggregations(ExprNode exprNode, string errorMsg) + { + // Make sure there is no aggregation in the where clause + var aggregateNodes = new List(); + ExprAggregateNodeUtil.GetAggregatesBottomUp(exprNode, aggregateNodes); + if (!aggregateNodes.IsEmpty()) + { + throw new ExprValidationException(errorMsg); + } + } + + // Special-case validation: When an on-merge query in the not-matched clause uses a subquery then + // that subquery should not reference any of the stream's properties which are not-matched + internal static void ValidateSubqueryExcludeOuterStream(ExprNode matchCondition) + { + var visitorSubselects = new ExprNodeSubselectDeclaredDotVisitor(); + matchCondition.Accept(visitorSubselects); + if (visitorSubselects.Subselects.IsEmpty()) + { + return; + } + var visitorProps = new ExprNodeIdentifierCollectVisitor(); + foreach (var node in visitorSubselects.Subselects) + { + if (node.StatementSpecCompiled.FilterRootNode != null) + { + node.StatementSpecCompiled.FilterRootNode.Accept(visitorProps); + } + } + foreach (var node in visitorProps.ExprProperties) + { + if (node.StreamId == 1) + { + throw new ExprValidationException( + "On-Merge not-matched filter expression may not use properties that are provided by the named window event"); + } + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodHelperViewResources.cs b/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodHelperViewResources.cs new file mode 100755 index 000000000..c60abdc49 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodHelperViewResources.cs @@ -0,0 +1,125 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.prev; +using com.espertech.esper.epl.expression.prior; +using com.espertech.esper.rowregex; +using com.espertech.esper.view; +using com.espertech.esper.view.internals; +using com.espertech.esper.view.std; + +namespace com.espertech.esper.core.start +{ + public class EPStatementStartMethodHelperViewResources + { + public static ViewResourceDelegateVerified VerifyPreviousAndPriorRequirements(ViewFactoryChain[] unmaterializedViewChain, ViewResourceDelegateUnverified @delegate) + { + var hasPriorNodes = !@delegate.PriorRequests.IsEmpty(); + var hasPreviousNodes = !@delegate.PreviousRequests.IsEmpty(); + + var numStreams = unmaterializedViewChain.Length; + var perStream = new ViewResourceDelegateVerifiedStream[numStreams]; + + // verify "previous" + var previousPerStream = new IList[numStreams]; + foreach (var previousNode in @delegate.PreviousRequests) { + var stream = previousNode.StreamNumber; + var factories = unmaterializedViewChain[stream].FactoryChain; + + var pass = InspectViewFactoriesForPrevious(factories); + if (!pass) { + throw new ExprValidationException("Previous function requires a single data window view onto the stream"); + } + + var found = factories.OfType().Any(); + if (!found) { + throw new ExprValidationException("Required data window not found for the 'prev' function, specify a data window for which previous events are retained"); + } + + if (previousPerStream[stream] == null) { + previousPerStream[stream] = new List(); + } + previousPerStream[stream].Add(previousNode); + } + + // verify "prior" + var priorPerStream = new IDictionary>[numStreams]; + foreach (var priorNode in @delegate.PriorRequests) { + var stream = priorNode.StreamNumber; + + if (priorPerStream[stream] == null) { + priorPerStream[stream] = new SortedDictionary>(); + } + + var treemap = priorPerStream[stream]; + var callbackList = treemap.Get(priorNode.ConstantIndexNumber); + if (callbackList == null) + { + callbackList = new List(); + treemap.Put(priorNode.ConstantIndexNumber, callbackList); + } + callbackList.Add(priorNode); + } + + // build per-stream INFO + for (var i = 0; i < numStreams; i++) { + if (previousPerStream[i] == null) { + previousPerStream[i] = Collections.GetEmptyList(); + } + if (priorPerStream[i] == null) { + priorPerStream[i] = new OrderedDictionary>(); + } + + // determine match-recognize "prev" + var matchRecognizePrevious = Collections.GetEmptySet(); + if (i == 0) { + foreach (var viewFactory in unmaterializedViewChain[i].FactoryChain) { + if (viewFactory is EventRowRegexNFAViewFactory) { + var matchRecognize = (EventRowRegexNFAViewFactory) viewFactory; + matchRecognizePrevious = matchRecognize.PreviousExprNodes; + } + } + } + + perStream[i] = new ViewResourceDelegateVerifiedStream(previousPerStream[i], priorPerStream[i], matchRecognizePrevious); + } + + return new ViewResourceDelegateVerified(hasPriorNodes, hasPreviousNodes, perStream); + } + + private static bool InspectViewFactoriesForPrevious(IList viewFactories) + { + // We allow the capability only if + // - 1 view + // - 2 views and the first view is a group-by (for window-per-group access) + if (viewFactories.Count == 1) + { + return true; + } + if (viewFactories.Count == 2) + { + if (viewFactories[0] is GroupByViewFactory) + { + return true; + } + if (viewFactories[1] is PriorEventViewFactory) { + return true; + } + return false; + } + return true; + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodOnTrigger.cs b/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodOnTrigger.cs new file mode 100755 index 000000000..0e170c924 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodOnTrigger.cs @@ -0,0 +1,1260 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.client.soda; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.activator; +using com.espertech.esper.core.context.factory; +using com.espertech.esper.core.context.mgr; +using com.espertech.esper.core.context.subselect; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.prev; +using com.espertech.esper.epl.expression.prior; +using com.espertech.esper.epl.expression.subquery; +using com.espertech.esper.epl.expression.table; +using com.espertech.esper.epl.named; +using com.espertech.esper.epl.property; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.epl.table.onaction; +using com.espertech.esper.epl.variable; +using com.espertech.esper.epl.view; +using com.espertech.esper.events; +using com.espertech.esper.events.map; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.start +{ + /// Starts and provides the stop method for EPL statements. + public class EPStatementStartMethodOnTrigger : EPStatementStartMethodBase + { + public static readonly string INITIAL_VALUE_STREAM_NAME = "initial"; + + public EPStatementStartMethodOnTrigger(StatementSpecCompiled statementSpec) + : base(statementSpec) + { + } + + private static EPStatementStartMethodOnTriggerItem OnSplitValidate( + StatementSpecCompiled statementSpec, + StreamTypeService typeServiceTrigger, + ContextPropertyRegistry contextPropertyRegistry, + EPServicesContext services, + StatementContext statementContext, + PropertyEvaluator optionalPropertyEvaluator) + { + var isNamedWindowInsert = + statementContext.NamedWindowMgmtService.IsNamedWindow(statementSpec.InsertIntoDesc.EventTypeName); + EPStatementStartMethodHelperValidate.ValidateNodes( + statementSpec, statementContext, typeServiceTrigger, null); + var factoryDescs = ResultSetProcessorFactoryFactory.GetProcessorPrototype( + statementSpec, statementContext, typeServiceTrigger, null, new bool[0], false, contextPropertyRegistry, + null, services.ConfigSnapshot, services.ResultSetProcessorHelperFactory, false, true); + return new EPStatementStartMethodOnTriggerItem( + statementSpec.FilterRootNode, isNamedWindowInsert, + GetOptionalInsertIntoTableName(statementSpec.InsertIntoDesc, services.TableService), factoryDescs, + optionalPropertyEvaluator); + } + + private static string GetOptionalInsertIntoTableName(InsertIntoDesc insertIntoDesc, TableService tableService) + { + if (insertIntoDesc == null) + { + return null; + } + var tableMetadata = tableService.GetTableMetadata(insertIntoDesc.EventTypeName); + if (tableMetadata != null) + { + return tableMetadata.TableName; + } + return null; + } + + public override EPStatementStartResult StartInternal( + EPServicesContext services, + StatementContext statementContext, + bool isNewStatement, + bool isRecoveringStatement, + bool isRecoveringResilient) + { + // define stop and destroy + var stopCallbacks = new List(); + var destroyCallbacks = new EPStatementDestroyCallbackList(); + + // determine context + var contextName = StatementSpec.OptionalContextName; + ContextPropertyRegistry contextPropertyRegistry = (contextName != null) + ? services.ContextManagementService.GetContextDescriptor(contextName).ContextPropertyRegistry + : null; + + // create subselect information + var subSelectStreamDesc = EPStatementStartMethodHelperSubselect.CreateSubSelectActivation( + services, StatementSpec, statementContext, destroyCallbacks); + + // obtain activator + var streamSpec = StatementSpec.StreamSpecs[0]; + ActivatorResult activatorResult; + StreamSelector? optionalStreamSelector = null; + if (streamSpec is FilterStreamSpecCompiled) + { + var filterStreamSpec = (FilterStreamSpecCompiled) streamSpec; + activatorResult = ActivatorFilter(statementContext, services, filterStreamSpec); + } + else if (streamSpec is PatternStreamSpecCompiled) + { + var patternStreamSpec = (PatternStreamSpecCompiled) streamSpec; + activatorResult = ActivatorPattern(statementContext, services, patternStreamSpec); + } + else if (streamSpec is NamedWindowConsumerStreamSpec) + { + var namedSpec = (NamedWindowConsumerStreamSpec) streamSpec; + activatorResult = ActivatorNamedWindow(services, namedSpec, statementContext); + } + else if (streamSpec is TableQueryStreamSpec) + { + throw new ExprValidationException("Tables cannot be used in an on-action statement triggering stream"); + } + else + { + throw new ExprValidationException("Unknown stream specification type: " + streamSpec); + } + + // context-factory creation + // + // handle on-merge for table + ContextFactoryResult contextFactoryResult; + TableMetadata tableMetadata = null; + if (StatementSpec.OnTriggerDesc is OnTriggerWindowDesc) + { + var onTriggerDesc = (OnTriggerWindowDesc) StatementSpec.OnTriggerDesc; + tableMetadata = services.TableService.GetTableMetadata(onTriggerDesc.WindowName); + if (tableMetadata != null) + { + contextFactoryResult = HandleContextFactoryOnTriggerTable( + statementContext, services, onTriggerDesc, contextName, streamSpec, activatorResult, + contextPropertyRegistry, subSelectStreamDesc); + services.StatementVariableRefService.AddReferences( + statementContext.StatementName, tableMetadata.TableName); + } + else if (services.NamedWindowMgmtService.GetProcessor(onTriggerDesc.WindowName) != null) + { + services.StatementVariableRefService.AddReferences( + statementContext.StatementName, onTriggerDesc.WindowName); + contextFactoryResult = HandleContextFactoryOnTriggerNamedWindow( + services, statementContext, onTriggerDesc, contextName, streamSpec, contextPropertyRegistry, + subSelectStreamDesc, activatorResult, optionalStreamSelector, stopCallbacks); + } + else + { + throw new ExprValidationException( + "A named window or variable by name '" + onTriggerDesc.WindowName + "' does not exist"); + } + } + else if (StatementSpec.OnTriggerDesc is OnTriggerSetDesc) + { + // variable assignments + var desc = (OnTriggerSetDesc) StatementSpec.OnTriggerDesc; + contextFactoryResult = HandleContextFactorySetVariable( + StatementSpec, statementContext, services, desc, streamSpec, subSelectStreamDesc, + contextPropertyRegistry, activatorResult); + } + else + { + // split-stream use case + var desc = (OnTriggerSplitStreamDesc) StatementSpec.OnTriggerDesc; + contextFactoryResult = HandleContextFactorySplitStream( + StatementSpec, statementContext, services, desc, streamSpec, contextPropertyRegistry, + subSelectStreamDesc, activatorResult); + } + statementContext.StatementAgentInstanceFactory = contextFactoryResult.ContextFactory; + var resultEventType = contextFactoryResult.ResultSetProcessorPrototype == null + ? null + : contextFactoryResult.ResultSetProcessorPrototype.ResultSetProcessorFactory.ResultEventType; + + // perform start of hook-up to start + Viewable finalViewable; + EPStatementStopMethod stopStatementMethod; + IDictionary subselectStrategyInstances; + IDictionary tableAccessStrategyInstances; + AggregationService aggregationService; + + // add cleanup for table metadata, if required + if (tableMetadata != null) + { + destroyCallbacks.AddCallback( + new EPStatementDestroyCallbackTableIdxRef( + services.TableService, tableMetadata, statementContext.StatementName)); + destroyCallbacks.AddCallback( + new EPStatementDestroyCallbackTableUpdStr( + services.TableService, tableMetadata, statementContext.StatementName)); + } + + // With context - delegate instantiation to context + var stopMethod = new EPStatementStopMethodImpl(statementContext, stopCallbacks); + if (StatementSpec.OptionalContextName != null) + { + // use statement-wide agent-instance-specific aggregation service + aggregationService = statementContext.StatementAgentInstanceRegistry.AgentInstanceAggregationService; + + // use statement-wide agent-instance-specific subselects + var aiRegistryExpr = statementContext.StatementAgentInstanceRegistry.AgentInstanceExprService; + subselectStrategyInstances = new Dictionary(); + foreach (var entry in contextFactoryResult.SubSelectStrategyCollection.Subqueries) + { + var specificService = aiRegistryExpr.AllocateSubselect(entry.Key); + entry.Key.Strategy = specificService; + + var subselectPriorStrategies = new Dictionary(); + foreach (ExprPriorNode subselectPrior in entry.Value.PriorNodesList) + { + var specificSubselectPriorService = aiRegistryExpr.AllocatePrior(subselectPrior); + subselectPriorStrategies.Put(subselectPrior, specificSubselectPriorService); + } + + var subselectPreviousStrategies = new Dictionary(); + foreach (ExprPreviousNode subselectPrevious in entry.Value.PrevNodesList) + { + var specificSubselectPreviousService = aiRegistryExpr.AllocatePrevious(subselectPrevious); + subselectPreviousStrategies.Put(subselectPrevious, specificSubselectPreviousService); + } + + var subselectAggregation = aiRegistryExpr.AllocateSubselectAggregation(entry.Key); + subselectStrategyInstances.Put( + entry.Key, + new SubSelectStrategyHolder( + specificService, subselectAggregation, subselectPriorStrategies, subselectPreviousStrategies, + null, null, null)); + } + + // use statement-wide agent-instance-specific tables + tableAccessStrategyInstances = new Dictionary(); + if (StatementSpec.TableNodes != null) + { + foreach (ExprTableAccessNode tableNode in StatementSpec.TableNodes) + { + var specificService = aiRegistryExpr.AllocateTableAccess(tableNode); + tableAccessStrategyInstances.Put(tableNode, specificService); + } + } + + var mergeView = new ContextMergeViewForwarding(resultEventType); + finalViewable = mergeView; + + var statement = new ContextManagedStatementOnTriggerDesc( + StatementSpec, statementContext, mergeView, contextFactoryResult.ContextFactory); + services.ContextManagementService.AddStatement(contextName, statement, isRecoveringResilient); + stopStatementMethod = new ProxyEPStatementStopMethod( + () => + { + services.ContextManagementService.StoppedStatement( + contextName, statementContext.StatementName, statementContext.StatementId, + statementContext.Expression, statementContext.ExceptionHandlingService); + stopMethod.Stop(); + }); + + destroyCallbacks.AddCallback( + new EPStatementDestroyCallbackContext( + services.ContextManagementService, contextName, statementContext.StatementName, + statementContext.StatementId)); + } + else + { + // Without context - start here + var agentInstanceContext = GetDefaultAgentInstanceContext(statementContext); + var resultOfStart = contextFactoryResult.ContextFactory.NewContext(agentInstanceContext, isRecoveringResilient); + finalViewable = resultOfStart.FinalView; + var stopCallback = services.EpStatementFactory.MakeStopMethod(resultOfStart); + stopStatementMethod = new ProxyEPStatementStopMethod( + () => + { + stopCallback.Stop(); + stopMethod.Stop(); + }); + aggregationService = resultOfStart.OptionalAggegationService; + subselectStrategyInstances = resultOfStart.SubselectStrategies; + tableAccessStrategyInstances = resultOfStart.TableAccessEvalStrategies; + + if (statementContext.StatementExtensionServicesContext != null && + statementContext.StatementExtensionServicesContext.StmtResources != null) + { + var holder = + statementContext.StatementExtensionServicesContext.ExtractStatementResourceHolder(resultOfStart); + statementContext.StatementExtensionServicesContext.StmtResources.Unpartitioned = holder; + statementContext.StatementExtensionServicesContext.PostProcessStart( + resultOfStart, isRecoveringResilient); + } + } + + // initialize aggregation expression nodes + if (contextFactoryResult.ResultSetProcessorPrototype != null && + contextFactoryResult.ResultSetProcessorPrototype.AggregationServiceFactoryDesc != null) + { + EPStatementStartMethodHelperAssignExpr.AssignAggregations( + aggregationService, + contextFactoryResult.ResultSetProcessorPrototype.AggregationServiceFactoryDesc.Expressions); + } + + // assign subquery nodes + EPStatementStartMethodHelperAssignExpr.AssignSubqueryStrategies( + contextFactoryResult.SubSelectStrategyCollection, subselectStrategyInstances); + + // assign tables + EPStatementStartMethodHelperAssignExpr.AssignTableAccessStrategies(tableAccessStrategyInstances); + + return new EPStatementStartResult(finalViewable, stopStatementMethod, destroyCallbacks); + } + + private ActivatorResult ActivatorNamedWindow( + EPServicesContext services, + NamedWindowConsumerStreamSpec namedSpec, + StatementContext statementContext) + { + var processor = services.NamedWindowMgmtService.GetProcessor(namedSpec.WindowName); + if (processor == null) + { + throw new ExprValidationException( + "A named window by name '" + namedSpec.WindowName + "' does not exist"); + } + var triggerEventTypeName = namedSpec.WindowName; + var activator = services.ViewableActivatorFactory.CreateNamedWindow(processor, namedSpec, statementContext); + var activatorResultEventType = processor.NamedWindowType; + if (namedSpec.OptPropertyEvaluator != null) + { + activatorResultEventType = namedSpec.OptPropertyEvaluator.FragmentEventType; + } + services.NamedWindowConsumerMgmtService.AddConsumer(statementContext, namedSpec); + return new ActivatorResult(activator, triggerEventTypeName, activatorResultEventType); + } + + private ActivatorResult ActivatorPattern( + StatementContext statementContext, + EPServicesContext services, + PatternStreamSpecCompiled patternStreamSpec) + { + var usedByChildViews = patternStreamSpec.ViewSpecs.Length > 0 || (StatementSpec.InsertIntoDesc != null); + var patternTypeName = statementContext.StatementId + "_patternon"; + var eventType = services.EventAdapterService.CreateSemiAnonymousMapType( + patternTypeName, patternStreamSpec.TaggedEventTypes, patternStreamSpec.ArrayEventTypes, usedByChildViews); + + var rootNode = services.PatternNodeFactory.MakeRootNode(patternStreamSpec.EvalFactoryNode); + var patternContext = statementContext.PatternContextFactory.CreateContext( + statementContext, 0, rootNode, patternStreamSpec.MatchedEventMapMeta, true); + var activator = services.ViewableActivatorFactory.CreatePattern( + patternContext, rootNode, eventType, + EPStatementStartMethodHelperUtil.IsConsumingFilters(patternStreamSpec.EvalFactoryNode), false, false, + false); + return new ActivatorResult(activator, null, eventType); + } + + private ActivatorResult ActivatorFilter( + StatementContext statementContext, + EPServicesContext services, + FilterStreamSpecCompiled filterStreamSpec) + { + var triggerEventTypeName = filterStreamSpec.FilterSpec.FilterForEventTypeName; + InstrumentationAgent instrumentationAgentOnTrigger = null; + if (InstrumentationHelper.ENABLED) + { + var eventTypeName = filterStreamSpec.FilterSpec.FilterForEventType.Name; + instrumentationAgentOnTrigger = new ProxyInstrumentationAgent + { + ProcIndicateQ = () => InstrumentationHelper.Get().QFilterActivationOnTrigger(eventTypeName), + ProcIndicateA = () => InstrumentationHelper.Get().AFilterActivationOnTrigger() + }; + } + var activator = services.ViewableActivatorFactory.CreateFilterProxy( + services, filterStreamSpec.FilterSpec, statementContext.Annotations, false, + instrumentationAgentOnTrigger, false, 0); + var activatorResultEventType = filterStreamSpec.FilterSpec.ResultEventType; + return new ActivatorResult(activator, triggerEventTypeName, activatorResultEventType); + } + + private ContextFactoryResult HandleContextFactorySetVariable( + StatementSpecCompiled statementSpec, + StatementContext statementContext, + EPServicesContext services, + OnTriggerSetDesc desc, + StreamSpecCompiled streamSpec, + SubSelectActivationCollection subSelectStreamDesc, + ContextPropertyRegistry contextPropertyRegistry, + ActivatorResult activatorResult) + { + var typeService = new StreamTypeServiceImpl( + new EventType[] + { + activatorResult.ActivatorResultEventType + }, new string[] + { + streamSpec.OptionalStreamName + }, new bool[] + { + true + }, services.EngineURI, false); + var validationContext = new ExprValidationContext( + typeService, statementContext.EngineImportService, statementContext.StatementExtensionServicesContext, + null, statementContext.SchedulingService, statementContext.VariableService, + statementContext.TableService, GetDefaultAgentInstanceContext(statementContext), + statementContext.EventAdapterService, statementContext.StatementName, statementContext.StatementId, + statementContext.Annotations, statementContext.ContextDescriptor, statementContext.ScriptingService, + false, false, true, false, null, false); + + // Materialize sub-select views + var subSelectStrategyCollection = EPStatementStartMethodHelperSubselect.PlanSubSelect( + services, statementContext, IsQueryPlanLogging(services), subSelectStreamDesc, new string[] + { + streamSpec.OptionalStreamName + }, new EventType[] + { + activatorResult.ActivatorResultEventType + }, new string[] + { + activatorResult.TriggerEventTypeName + }, statementSpec.DeclaredExpressions, contextPropertyRegistry); + + foreach (var assignment in desc.Assignments) + { + var validated = ExprNodeUtility.GetValidatedAssignment(assignment, validationContext); + assignment.Expression = validated; + } + + OnSetVariableViewFactory onSetVariableViewFactory; + try + { + var exprEvaluatorContext = new ExprEvaluatorContextStatement(statementContext, false); + onSetVariableViewFactory = new OnSetVariableViewFactory( + statementContext.StatementId, desc, statementContext.EventAdapterService, + statementContext.VariableService, statementContext.StatementResultService, exprEvaluatorContext); + } + catch (VariableValueException ex) + { + throw new ExprValidationException("Error in variable assignment: " + ex.Message, ex); + } + + EventType outputEventType = onSetVariableViewFactory.EventType; + + // handle output format + var defaultSelectAllSpec = new StatementSpecCompiled(); + defaultSelectAllSpec.SelectClauseSpec.SetSelectExprList(new SelectClauseElementWildcard()); + var streamTypeService = new StreamTypeServiceImpl( + new EventType[] + { + outputEventType + }, new string[] + { + "trigger_stream" + }, new bool[] + { + true + }, services.EngineURI, false); + var outputResultSetProcessorPrototype = + ResultSetProcessorFactoryFactory.GetProcessorPrototype( + defaultSelectAllSpec, statementContext, streamTypeService, null, new bool[0], true, + contextPropertyRegistry, null, services.ConfigSnapshot, services.ResultSetProcessorHelperFactory, + false, true); + + var outputViewFactory = OutputProcessViewFactoryFactory.Make( + statementSpec, services.InternalEventRouter, statementContext, null, null, services.TableService, + outputResultSetProcessorPrototype.ResultSetProcessorFactory.ResultSetProcessorType, + services.ResultSetProcessorHelperFactory, services.StatementVariableRefService); + var contextFactory = new StatementAgentInstanceFactoryOnTriggerSetVariable( + statementContext, statementSpec, services, activatorResult.Activator, subSelectStrategyCollection, + onSetVariableViewFactory, outputResultSetProcessorPrototype, outputViewFactory); + return new ContextFactoryResult(contextFactory, subSelectStrategyCollection, null); + } + + private ContextFactoryResult HandleContextFactorySplitStream( + StatementSpecCompiled statementSpec, + StatementContext statementContext, + EPServicesContext services, + OnTriggerSplitStreamDesc desc, + StreamSpecCompiled streamSpec, + ContextPropertyRegistry contextPropertyRegistry, + SubSelectActivationCollection subSelectStreamDesc, + ActivatorResult activatorResult) + { + if (statementSpec.InsertIntoDesc == null) + { + throw new ExprValidationException( + "Required insert-into clause is not provided, the clause is required for split-stream syntax"); + } + if ((statementSpec.GroupByExpressions != null && statementSpec.GroupByExpressions.GroupByNodes.Length > 0) || + (statementSpec.HavingExprRootNode != null) || (statementSpec.OrderByList.Length > 0)) + { + throw new ExprValidationException( + "A group-by clause, having-clause or order-by clause is not allowed for the split stream syntax"); + } + + var streamName = streamSpec.OptionalStreamName; + if (streamName == null) + { + streamName = "stream_0"; + } + var typeServiceTrigger = new StreamTypeServiceImpl( + new EventType[] + { + activatorResult.ActivatorResultEventType + }, new string[] + { + streamName + }, new bool[] + { + true + }, services.EngineURI, false); + + // materialize sub-select views + var subSelectStrategyCollection = EPStatementStartMethodHelperSubselect.PlanSubSelect( + services, statementContext, IsQueryPlanLogging(services), subSelectStreamDesc, new string[] + { + streamSpec.OptionalStreamName + }, new EventType[] + { + activatorResult.ActivatorResultEventType + }, new string[] + { + activatorResult.TriggerEventTypeName + }, statementSpec.DeclaredExpressions, contextPropertyRegistry); + + // compile top-level split + var items = new EPStatementStartMethodOnTriggerItem[desc.SplitStreams.Count + 1]; + items[0] = OnSplitValidate( + statementSpec, typeServiceTrigger, contextPropertyRegistry, services, statementContext, null); + + // compile each additional split + var index = 1; + var assignedTypeNumberStack = new List(); + foreach (var splits in desc.SplitStreams) + { + var splitSpec = new StatementSpecCompiled(); + splitSpec.InsertIntoDesc = splits.InsertInto; + splitSpec.SelectClauseSpec = StatementLifecycleSvcImpl.CompileSelectAllowSubselect(splits.SelectClause); + splitSpec.FilterExprRootNode = splits.WhereClause; + + PropertyEvaluator optionalPropertyEvaluator = null; + StreamTypeService typeServiceProperty; + if (splits.FromClause != null) + { + optionalPropertyEvaluator = + PropertyEvaluatorFactory.MakeEvaluator( + splits.FromClause.PropertyEvalSpec, + activatorResult.ActivatorResultEventType, + streamName, + services.EventAdapterService, + services.EngineImportService, + services.SchedulingService, + services.VariableService, + services.ScriptingService, + services.TableService, + typeServiceTrigger.EngineURIQualifier, + statementContext.StatementId, + statementContext.StatementName, + statementContext.Annotations, + assignedTypeNumberStack, + services.ConfigSnapshot, + services.NamedWindowMgmtService, + statementContext.StatementExtensionServicesContext + ); + + typeServiceProperty = new StreamTypeServiceImpl( + new EventType[] + { + optionalPropertyEvaluator.FragmentEventType + }, new string[] + { + splits.FromClause.OptionalStreamName + }, new bool[] + { + true + }, services.EngineURI, false); + } + else + { + typeServiceProperty = typeServiceTrigger; + } + + items[index] = OnSplitValidate( + splitSpec, typeServiceProperty, contextPropertyRegistry, services, statementContext, + optionalPropertyEvaluator); + index++; + } + + var contextFactory = new StatementAgentInstanceFactoryOnTriggerSplit( + statementContext, statementSpec, services, activatorResult.Activator, subSelectStrategyCollection, items, + activatorResult.ActivatorResultEventType); + return new ContextFactoryResult(contextFactory, subSelectStrategyCollection, null); + } + + private ContextFactoryResult HandleContextFactoryOnTriggerNamedWindow( + EPServicesContext services, + StatementContext statementContext, + OnTriggerWindowDesc onTriggerDesc, + string contextName, + StreamSpecCompiled streamSpec, + ContextPropertyRegistry contextPropertyRegistry, + SubSelectActivationCollection subSelectStreamDesc, + ActivatorResult activatorResult, + StreamSelector? optionalStreamSelector, + IList stopCallbacks) + { + var processor = services.NamedWindowMgmtService.GetProcessor(onTriggerDesc.WindowName); + + // validate context + ValidateOnExpressionContext( + contextName, processor.ContextName, "Named window '" + onTriggerDesc.WindowName + "'"); + + var namedWindowType = processor.NamedWindowType; + services.StatementEventTypeRefService.AddReferences( + statementContext.StatementName, new string[] + { + onTriggerDesc.WindowName + }); + + // validate expressions and plan subselects + var validationResult = ValidateOnTriggerPlan( + services, statementContext, onTriggerDesc, namedWindowType, streamSpec, activatorResult, + contextPropertyRegistry, subSelectStreamDesc, null); + + InternalEventRouter routerService = null; + var addToFront = false; + string optionalInsertIntoTableName = null; + if (StatementSpec.InsertIntoDesc != null || onTriggerDesc is OnTriggerMergeDesc) + { + routerService = services.InternalEventRouter; + } + if (StatementSpec.InsertIntoDesc != null) + { + var tableMetadata = services.TableService.GetTableMetadata(StatementSpec.InsertIntoDesc.EventTypeName); + if (tableMetadata != null) + { + optionalInsertIntoTableName = tableMetadata.TableName; + routerService = null; + } + addToFront = + statementContext.NamedWindowMgmtService.IsNamedWindow(StatementSpec.InsertIntoDesc.EventTypeName); + } + bool isDistinct = StatementSpec.SelectClauseSpec.IsDistinct; + EventType selectResultEventType = + validationResult.ResultSetProcessorPrototype.ResultSetProcessorFactory.ResultEventType; + var createNamedWindowMetricsHandle = processor.CreateNamedWindowMetricsHandle; + + var onExprFactory = NamedWindowOnExprFactoryFactory.Make( + namedWindowType, onTriggerDesc.WindowName, validationResult.ZeroStreamAliasName, + onTriggerDesc, + activatorResult.ActivatorResultEventType, streamSpec.OptionalStreamName, addToFront, routerService, + selectResultEventType, + statementContext, createNamedWindowMetricsHandle, isDistinct, optionalStreamSelector, + optionalInsertIntoTableName); + + // For on-delete/set/update/merge, create an output processor that passes on as a wildcard the underlying event + ResultSetProcessorFactoryDesc outputResultSetProcessorPrototype = null; + if ((StatementSpec.OnTriggerDesc.OnTriggerType == OnTriggerType.ON_DELETE) || + (StatementSpec.OnTriggerDesc.OnTriggerType == OnTriggerType.ON_UPDATE) || + (StatementSpec.OnTriggerDesc.OnTriggerType == OnTriggerType.ON_MERGE)) + { + var defaultSelectAllSpec = new StatementSpecCompiled(); + defaultSelectAllSpec.SelectClauseSpec.SetSelectExprList(new SelectClauseElementWildcard()); + var streamTypeService = new StreamTypeServiceImpl( + new EventType[] + { + namedWindowType + }, new string[] + { + "trigger_stream" + }, new bool[] + { + true + }, services.EngineURI, false); + outputResultSetProcessorPrototype = + ResultSetProcessorFactoryFactory.GetProcessorPrototype( + defaultSelectAllSpec, statementContext, streamTypeService, null, new bool[0], true, + contextPropertyRegistry, null, services.ConfigSnapshot, services.ResultSetProcessorHelperFactory, + false, true); + } + + EventType resultEventType = + validationResult.ResultSetProcessorPrototype.ResultSetProcessorFactory.ResultEventType; + var outputViewFactory = OutputProcessViewFactoryFactory.Make( + StatementSpec, services.InternalEventRouter, statementContext, resultEventType, null, + services.TableService, + validationResult.ResultSetProcessorPrototype.ResultSetProcessorFactory.ResultSetProcessorType, + services.ResultSetProcessorHelperFactory, services.StatementVariableRefService); + + var contextFactory = new StatementAgentInstanceFactoryOnTriggerNamedWindow( + statementContext, StatementSpec, services, activatorResult.Activator, + validationResult.SubSelectStrategyCollection, validationResult.ResultSetProcessorPrototype, + validationResult.ValidatedJoin, outputResultSetProcessorPrototype, onExprFactory, outputViewFactory, + activatorResult.ActivatorResultEventType, processor, stopCallbacks); + return new ContextFactoryResult( + contextFactory, validationResult.SubSelectStrategyCollection, + validationResult.ResultSetProcessorPrototype); + } + + private TriggerValidationPlanResult ValidateOnTriggerPlan( + EPServicesContext services, + StatementContext statementContext, + OnTriggerWindowDesc onTriggerDesc, + EventType namedWindowType, + StreamSpecCompiled streamSpec, + ActivatorResult activatorResult, + ContextPropertyRegistry contextPropertyRegistry, + SubSelectActivationCollection subSelectStreamDesc, + string optionalTableName) + { + var zeroStreamAliasName = onTriggerDesc.OptionalAsName; + if (zeroStreamAliasName == null) + { + zeroStreamAliasName = "stream_0"; + } + var streamName = streamSpec.OptionalStreamName; + if (streamName == null) + { + streamName = "stream_1"; + } + var namedWindowTypeName = onTriggerDesc.WindowName; + + // Materialize sub-select views + // 0 - named window stream + // 1 - arriving stream + // 2 - initial value before update + var subSelectStrategyCollection = EPStatementStartMethodHelperSubselect.PlanSubSelect( + services, statementContext, IsQueryPlanLogging(services), subSelectStreamDesc, new string[] + { + zeroStreamAliasName, + streamSpec.OptionalStreamName + }, new EventType[] + { + namedWindowType, + activatorResult.ActivatorResultEventType + }, new string[] + { + namedWindowTypeName, + activatorResult.TriggerEventTypeName + }, StatementSpec.DeclaredExpressions, contextPropertyRegistry); + + var typeService = new StreamTypeServiceImpl( + new EventType[] + { + namedWindowType, + activatorResult.ActivatorResultEventType + }, new string[] + { + zeroStreamAliasName, + streamName + }, new bool[] + { + false, + true + }, services.EngineURI, true); + + // allow "initial" as a prefix to properties + StreamTypeServiceImpl assignmentTypeService; + if (zeroStreamAliasName.Equals(INITIAL_VALUE_STREAM_NAME) || streamName.Equals(INITIAL_VALUE_STREAM_NAME)) + { + assignmentTypeService = typeService; + } + else + { + assignmentTypeService = new StreamTypeServiceImpl( + new EventType[] + { + namedWindowType, + activatorResult.ActivatorResultEventType, + namedWindowType + }, new string[] + { + zeroStreamAliasName, + streamName, + INITIAL_VALUE_STREAM_NAME + }, new bool[] + { + false, + true, + true + }, services.EngineURI, false); + assignmentTypeService.IsStreamZeroUnambigous = true; + } + + if (onTriggerDesc is OnTriggerWindowUpdateDesc) + { + var updateDesc = (OnTriggerWindowUpdateDesc) onTriggerDesc; + var validationContext = new ExprValidationContext( + assignmentTypeService, statementContext.EngineImportService, + statementContext.StatementExtensionServicesContext, null, statementContext.SchedulingService, + statementContext.VariableService, statementContext.TableService, + GetDefaultAgentInstanceContext(statementContext), statementContext.EventAdapterService, + statementContext.StatementName, statementContext.StatementId, statementContext.Annotations, + statementContext.ContextDescriptor, statementContext.ScriptingService, false, false, true, false, + null, false); + foreach (var assignment in updateDesc.Assignments) + { + var validated = ExprNodeUtility.GetValidatedAssignment(assignment, validationContext); + assignment.Expression = validated; + EPStatementStartMethodHelperValidate.ValidateNoAggregations( + validated, "Aggregation functions may not be used within an on-update-clause"); + } + } + if (onTriggerDesc is OnTriggerMergeDesc) + { + var mergeDesc = (OnTriggerMergeDesc) onTriggerDesc; + ValidateMergeDesc( + mergeDesc, statementContext, namedWindowType, zeroStreamAliasName, + activatorResult.ActivatorResultEventType, streamName); + } + + // validate join expression + var validatedJoin = ValidateJoinNamedWindow( + services.EngineURI, statementContext, ExprNodeOrigin.WHERE, StatementSpec.FilterRootNode, + namedWindowType, zeroStreamAliasName, namedWindowTypeName, + activatorResult.ActivatorResultEventType, streamName, activatorResult.TriggerEventTypeName, + optionalTableName); + + // validate filter, output rate limiting + EPStatementStartMethodHelperValidate.ValidateNodes(StatementSpec, statementContext, typeService, null); + + // Construct a processor for results; for use in on-select to process selection results + // Use a wildcard select if the select-clause is empty, such as for on-delete. + // For on-select the select clause is not empty. + if (StatementSpec.SelectClauseSpec.SelectExprList.Length == 0) + { + StatementSpec.SelectClauseSpec.SetSelectExprList(new SelectClauseElementWildcard()); + } + var resultSetProcessorPrototype = ResultSetProcessorFactoryFactory.GetProcessorPrototype( + StatementSpec, statementContext, typeService, null, new bool[0], true, contextPropertyRegistry, null, + services.ConfigSnapshot, services.ResultSetProcessorHelperFactory, false, true); + + return new TriggerValidationPlanResult( + subSelectStrategyCollection, resultSetProcessorPrototype, validatedJoin, zeroStreamAliasName); + } + + private void ValidateOnExpressionContext(string onExprContextName, string desiredContextName, string title) + { + if (onExprContextName == null) + { + if (desiredContextName != null) + { + throw new ExprValidationException( + string.Format( + "Cannot create on-trigger expression: {0} was declared with context '{1}', please declare the same context name", + title, CompatExtensions.RenderAny(desiredContextName))); + } + return; + } + if (!onExprContextName.Equals(desiredContextName)) + { + throw new ExprValidationException( + string.Format( + "Cannot create on-trigger expression: {0} was declared with context '{1}', please use the same context instead", + title, CompatExtensions.RenderAny(desiredContextName))); + } + } + + private ContextFactoryResult HandleContextFactoryOnTriggerTable( + StatementContext statementContext, + EPServicesContext services, + OnTriggerWindowDesc onTriggerDesc, + string contextName, + StreamSpecCompiled streamSpec, + ActivatorResult activatorResult, + ContextPropertyRegistry contextPropertyRegistry, + SubSelectActivationCollection subSelectStreamDesc) + { + var metadata = services.TableService.GetTableMetadata(onTriggerDesc.WindowName); + + // validate context + ValidateOnExpressionContext(contextName, metadata.ContextName, "Table '" + onTriggerDesc.WindowName + "'"); + + InternalEventRouter routerService = null; + if (StatementSpec.InsertIntoDesc != null || onTriggerDesc is OnTriggerMergeDesc) + { + routerService = services.InternalEventRouter; + } + + // validate expressions and plan subselects + var validationResult = ValidateOnTriggerPlan( + services, statementContext, onTriggerDesc, metadata.InternalEventType, streamSpec, activatorResult, + contextPropertyRegistry, subSelectStreamDesc, metadata.TableName); + + // table on-action view factory + var onExprFactory = TableOnViewFactoryFactory.Make( + metadata, onTriggerDesc, activatorResult.ActivatorResultEventType, streamSpec.OptionalStreamName, + statementContext, statementContext.EpStatementHandle.MetricsHandle, false, routerService); + + // For on-delete/set/update/merge, create an output processor that passes on as a wildcard the underlying event + ResultSetProcessorFactoryDesc outputResultSetProcessorPrototype = null; + if ((StatementSpec.OnTriggerDesc.OnTriggerType == OnTriggerType.ON_DELETE) || + (StatementSpec.OnTriggerDesc.OnTriggerType == OnTriggerType.ON_UPDATE) || + (StatementSpec.OnTriggerDesc.OnTriggerType == OnTriggerType.ON_MERGE)) + { + var defaultSelectAllSpec = new StatementSpecCompiled(); + defaultSelectAllSpec.SelectClauseSpec.SetSelectExprList(new SelectClauseElementWildcard()); + // we'll be expecting public-type events as there is no copy op + var streamTypeService = new StreamTypeServiceImpl( + new EventType[] + { + metadata.PublicEventType + }, new string[] + { + "trigger_stream" + }, new bool[] + { + true + }, services.EngineURI, false); + outputResultSetProcessorPrototype = + ResultSetProcessorFactoryFactory.GetProcessorPrototype( + defaultSelectAllSpec, statementContext, streamTypeService, null, new bool[0], true, + contextPropertyRegistry, null, services.ConfigSnapshot, services.ResultSetProcessorHelperFactory, + false, true); + } + + EventType resultEventType = + validationResult.ResultSetProcessorPrototype.ResultSetProcessorFactory.ResultEventType; + var outputViewFactory = OutputProcessViewFactoryFactory.Make( + StatementSpec, services.InternalEventRouter, statementContext, resultEventType, null, + services.TableService, + validationResult.ResultSetProcessorPrototype.ResultSetProcessorFactory.ResultSetProcessorType, + services.ResultSetProcessorHelperFactory, services.StatementVariableRefService); + + var contextFactory = new StatementAgentInstanceFactoryOnTriggerTable( + statementContext, StatementSpec, services, activatorResult.Activator, + validationResult.SubSelectStrategyCollection, validationResult.ResultSetProcessorPrototype, + validationResult.ValidatedJoin, onExprFactory, activatorResult.ActivatorResultEventType, metadata, + outputResultSetProcessorPrototype, outputViewFactory); + + return new ContextFactoryResult( + contextFactory, validationResult.SubSelectStrategyCollection, + validationResult.ResultSetProcessorPrototype); + } + + private void ValidateMergeDesc( + OnTriggerMergeDesc mergeDesc, + StatementContext statementContext, + EventType namedWindowType, + string namedWindowName, + EventType triggerStreamType, + string triggerStreamName) + { + var exprNodeErrorMessage = "Aggregation functions may not be used within an merge-clause"; + var evaluatorContextStmt = new ExprEvaluatorContextStatement(statementContext, false); + + foreach (var matchedItem in mergeDesc.Items) + { + + var dummyTypeNoProperties = + new MapEventType( + EventTypeMetadata.CreateAnonymous("merge_named_window_insert", ApplicationType.MAP), + "merge_named_window_insert", 0, null, Collections.EmptyDataMap, null, null, null); + var twoStreamTypeSvc = new StreamTypeServiceImpl( + new EventType[] + { + namedWindowType, + triggerStreamType + }, + new string[] + { + namedWindowName, + triggerStreamName + }, new bool[] + { + true, + true + }, statementContext.EngineURI, true); + var insertOnlyTypeSvc = new StreamTypeServiceImpl( + new EventType[] + { + dummyTypeNoProperties, + triggerStreamType + }, + new string[] + { + UuidGenerator.Generate(), + triggerStreamName + }, new bool[] + { + true, + true + }, statementContext.EngineURI, true); + + // we may provide an additional stream "initial" for the prior value, unless already defined + StreamTypeServiceImpl assignmentStreamTypeSvc; + if (namedWindowName.Equals(INITIAL_VALUE_STREAM_NAME) || + triggerStreamName.Equals(INITIAL_VALUE_STREAM_NAME)) + { + assignmentStreamTypeSvc = twoStreamTypeSvc; + } + else + { + assignmentStreamTypeSvc = new StreamTypeServiceImpl( + new EventType[] + { + namedWindowType, + triggerStreamType, + namedWindowType + }, + new string[] + { + namedWindowName, + triggerStreamName, + INITIAL_VALUE_STREAM_NAME + }, new bool[] + { + true, + true, + true + }, statementContext.EngineURI, false); + assignmentStreamTypeSvc.IsStreamZeroUnambigous = true; + } + + if (matchedItem.OptionalMatchCond != null) + { + StreamTypeService matchValidStreams = matchedItem.IsMatchedUnmatched + ? twoStreamTypeSvc + : insertOnlyTypeSvc; + matchedItem.OptionalMatchCond = + EPStatementStartMethodHelperValidate.ValidateExprNoAgg( + ExprNodeOrigin.MERGEMATCHCOND, matchedItem.OptionalMatchCond, matchValidStreams, + statementContext, evaluatorContextStmt, exprNodeErrorMessage, true); + if (!matchedItem.IsMatchedUnmatched) + { + EPStatementStartMethodHelperValidate.ValidateSubqueryExcludeOuterStream( + matchedItem.OptionalMatchCond); + } + } + + foreach (var item in matchedItem.Actions) + { + if (item is OnTriggerMergeActionDelete) + { + var delete = (OnTriggerMergeActionDelete) item; + if (delete.OptionalWhereClause != null) + { + delete.OptionalWhereClause = + EPStatementStartMethodHelperValidate.ValidateExprNoAgg( + ExprNodeOrigin.MERGEMATCHWHERE, delete.OptionalWhereClause, twoStreamTypeSvc, + statementContext, evaluatorContextStmt, exprNodeErrorMessage, true); + } + } + else if (item is OnTriggerMergeActionUpdate) + { + var update = (OnTriggerMergeActionUpdate) item; + if (update.OptionalWhereClause != null) + { + update.OptionalWhereClause = + EPStatementStartMethodHelperValidate.ValidateExprNoAgg( + ExprNodeOrigin.MERGEMATCHWHERE, update.OptionalWhereClause, twoStreamTypeSvc, + statementContext, evaluatorContextStmt, exprNodeErrorMessage, true); + } + foreach (var assignment in update.Assignments) + { + assignment.Expression = + EPStatementStartMethodHelperValidate.ValidateExprNoAgg( + ExprNodeOrigin.UPDATEASSIGN, assignment.Expression, assignmentStreamTypeSvc, + statementContext, evaluatorContextStmt, exprNodeErrorMessage, true); + } + } + else if (item is OnTriggerMergeActionInsert) + { + var insert = (OnTriggerMergeActionInsert) item; + + StreamTypeService insertTypeSvc; + if (insert.OptionalStreamName == null || insert.OptionalStreamName.Equals(namedWindowName)) + { + insertTypeSvc = insertOnlyTypeSvc; + } + else + { + insertTypeSvc = twoStreamTypeSvc; + } + + var compiledSelect = new List(); + if (insert.OptionalWhereClause != null) + { + insert.OptionalWhereClause = + EPStatementStartMethodHelperValidate.ValidateExprNoAgg( + ExprNodeOrigin.MERGEMATCHWHERE, insert.OptionalWhereClause, insertTypeSvc, + statementContext, evaluatorContextStmt, exprNodeErrorMessage, true); + } + var colIndex = 0; + foreach (var raw in insert.SelectClause) + { + if (raw is SelectClauseStreamRawSpec) + { + var rawStreamSpec = (SelectClauseStreamRawSpec) raw; + int? foundStreamNum = null; + for (var s = 0; s < insertTypeSvc.StreamNames.Length; s++) + { + if (rawStreamSpec.StreamName.Equals(insertTypeSvc.StreamNames[s])) + { + foundStreamNum = s; + break; + } + } + if (foundStreamNum == null) + { + throw new ExprValidationException( + "Stream by name '" + rawStreamSpec.StreamName + "' was not found"); + } + var streamSelectSpec = new SelectClauseStreamCompiledSpec( + rawStreamSpec.StreamName, rawStreamSpec.OptionalAsName); + streamSelectSpec.StreamNumber = foundStreamNum.Value; + compiledSelect.Add(streamSelectSpec); + } + else if (raw is SelectClauseExprRawSpec) + { + var exprSpec = (SelectClauseExprRawSpec) raw; + var validationContext = new ExprValidationContext( + insertTypeSvc, statementContext.EngineImportService, + statementContext.StatementExtensionServicesContext, null, + statementContext.TimeProvider, statementContext.VariableService, + statementContext.TableService, evaluatorContextStmt, + statementContext.EventAdapterService, statementContext.StatementName, + statementContext.StatementId, statementContext.Annotations, + statementContext.ContextDescriptor, statementContext.ScriptingService, + false, false, true, false, null, false); + var exprCompiled = ExprNodeUtility.GetValidatedSubtree( + ExprNodeOrigin.SELECT, exprSpec.SelectExpression, validationContext); + var resultName = exprSpec.OptionalAsName; + if (resultName == null) + { + if (insert.Columns.Count > colIndex) + { + resultName = insert.Columns[colIndex]; + } + else + { + resultName = ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(exprCompiled); + } + } + compiledSelect.Add( + new SelectClauseExprCompiledSpec( + exprCompiled, resultName, exprSpec.OptionalAsName, exprSpec.IsEvents)); + EPStatementStartMethodHelperValidate.ValidateNoAggregations( + exprCompiled, + "Expression in a merge-selection may not utilize aggregation functions"); + } + else if (raw is SelectClauseElementWildcard) + { + compiledSelect.Add(new SelectClauseElementWildcard()); + } + else + { + throw new IllegalStateException("Unknown select clause item:" + raw); + } + colIndex++; + } + insert.SelectClauseCompiled = compiledSelect; + } + else + { + throw new ArgumentException("Unrecognized merge item '" + item.GetType().FullName + "'"); + } + } + } + } + + // For delete actions from named windows + protected ExprNode ValidateJoinNamedWindow( + string engineURI, + StatementContext statementContext, + ExprNodeOrigin exprNodeOrigin, + ExprNode deleteJoinExpr, + EventType namedWindowType, + string namedWindowStreamName, + string namedWindowName, + EventType filteredType, + string filterStreamName, + string filteredTypeName, + string optionalTableName + ) + { + if (deleteJoinExpr == null) + { + return null; + } + + var namesAndTypes = new LinkedHashMap>(); + namesAndTypes.Put(namedWindowStreamName, new Pair(namedWindowType, namedWindowName)); + namesAndTypes.Put(filterStreamName, new Pair(filteredType, filteredTypeName)); + var typeService = new StreamTypeServiceImpl(namesAndTypes, engineURI, false, false); + + var evaluatorContextStmt = new ExprEvaluatorContextStatement(statementContext, false); + var validationContext = new ExprValidationContext( + typeService, statementContext.EngineImportService, statementContext.StatementExtensionServicesContext, + null, statementContext.SchedulingService, statementContext.VariableService, + statementContext.TableService, evaluatorContextStmt, statementContext.EventAdapterService, + statementContext.StatementName, statementContext.StatementId, statementContext.Annotations, + statementContext.ContextDescriptor, statementContext.ScriptingService, + false, false, true, false, null, false); + return ExprNodeUtility.GetValidatedSubtree(exprNodeOrigin, deleteJoinExpr, validationContext); + } + + internal class ContextFactoryResult + { + internal StatementAgentInstanceFactoryOnTriggerBase ContextFactory; + internal SubSelectStrategyCollection SubSelectStrategyCollection; + internal ResultSetProcessorFactoryDesc ResultSetProcessorPrototype; + + internal ContextFactoryResult( + StatementAgentInstanceFactoryOnTriggerBase contextFactory, + SubSelectStrategyCollection subSelectStrategyCollection, + ResultSetProcessorFactoryDesc resultSetProcessorPrototype) + { + ContextFactory = contextFactory; + SubSelectStrategyCollection = subSelectStrategyCollection; + ResultSetProcessorPrototype = resultSetProcessorPrototype; + } + } + + internal class ActivatorResult + { + internal readonly ViewableActivator Activator; + internal readonly string TriggerEventTypeName; + internal readonly EventType ActivatorResultEventType; + + internal ActivatorResult( + ViewableActivator activator, + string triggerEventTypeName, + EventType activatorResultEventType) + { + Activator = activator; + TriggerEventTypeName = triggerEventTypeName; + ActivatorResultEventType = activatorResultEventType; + } + } + + internal class TriggerValidationPlanResult + { + internal SubSelectStrategyCollection SubSelectStrategyCollection; + internal ResultSetProcessorFactoryDesc ResultSetProcessorPrototype; + internal ExprNode ValidatedJoin; + internal string ZeroStreamAliasName; + + internal TriggerValidationPlanResult( + SubSelectStrategyCollection subSelectStrategyCollection, + ResultSetProcessorFactoryDesc resultSetProcessorPrototype, + ExprNode validatedJoin, + string zeroStreamAliasName) + { + SubSelectStrategyCollection = subSelectStrategyCollection; + ResultSetProcessorPrototype = resultSetProcessorPrototype; + ValidatedJoin = validatedJoin; + ZeroStreamAliasName = zeroStreamAliasName; + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodOnTriggerItem.cs b/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodOnTriggerItem.cs new file mode 100755 index 000000000..731995311 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodOnTriggerItem.cs @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.property; + +namespace com.espertech.esper.core.start +{ + public class EPStatementStartMethodOnTriggerItem + { + public EPStatementStartMethodOnTriggerItem( + ExprNode whereClause, + bool isNamedWindowInsert, + string insertIntoTableNames, + ResultSetProcessorFactoryDesc factoryDesc, + PropertyEvaluator propertyEvaluator) + { + WhereClause = whereClause; + IsNamedWindowInsert = isNamedWindowInsert; + InsertIntoTableNames = insertIntoTableNames; + FactoryDesc = factoryDesc; + PropertyEvaluator = propertyEvaluator; + } + + public ExprNode WhereClause { get; private set; } + + public bool IsNamedWindowInsert { get; private set; } + + public string InsertIntoTableNames { get; private set; } + + public ResultSetProcessorFactoryDesc FactoryDesc { get; private set; } + + public PropertyEvaluator PropertyEvaluator { get; private set; } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodSelect.cs b/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodSelect.cs new file mode 100755 index 000000000..e1ba0394d --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodSelect.cs @@ -0,0 +1,209 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.factory; +using com.espertech.esper.core.context.mgr; +using com.espertech.esper.core.context.subselect; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.prev; +using com.espertech.esper.epl.expression.prior; +using com.espertech.esper.epl.expression.subquery; +using com.espertech.esper.epl.expression.table; +using com.espertech.esper.epl.spec; +using com.espertech.esper.rowregex; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.start +{ + /// Starts and provides the stop method for EPL statements. + public class EPStatementStartMethodSelect : EPStatementStartMethodBase + { + public EPStatementStartMethodSelect(StatementSpecCompiled statementSpec) + : base(statementSpec) + { + } + + public override EPStatementStartResult StartInternal( + EPServicesContext services, + StatementContext statementContext, + bool isNewStatement, + bool isRecoveringStatement, + bool isRecoveringResilient) + { + var statementSpec = base.StatementSpec; + + // validate use of table: may not both read and write + ValidateTableAccessUse(statementSpec.IntoTableSpec, statementSpec.TableNodes); + + var contextName = statementSpec.OptionalContextName; + var defaultAgentInstanceContext = GetDefaultAgentInstanceContext(statementContext); + var selectDesc = EPStatementStartMethodSelectUtil.Prepare(statementSpec, services, statementContext, isRecoveringResilient, defaultAgentInstanceContext, IsQueryPlanLogging(services), null, null, null); + statementContext.StatementAgentInstanceFactory = selectDesc.StatementAgentInstanceFactorySelect; + + // allow extension to walk + statementContext.StatementExtensionServicesContext.PreStartWalk(selectDesc); + + // Determine context + EPStatementStopMethod stopStatementMethod; + Viewable finalViewable; + AggregationService aggregationService; + IDictionary subselectStrategyInstances; + IDictionary priorStrategyInstances; + IDictionary previousStrategyInstances; + IDictionary tableAccessStrategyInstances; + var preloadList = Collections.GetEmptyList(); + RegexExprPreviousEvalStrategy matchRecognizePrevEvalStrategy; + + // With context - delegate instantiation to context + if (statementSpec.OptionalContextName != null) { + + // use statement-wide agent-instance-specific aggregation service + aggregationService = statementContext.StatementAgentInstanceRegistry.AgentInstanceAggregationService; + + // use statement-wide agent-instance-specific subselects + var aiRegistryExpr = statementContext.StatementAgentInstanceRegistry.AgentInstanceExprService; + + subselectStrategyInstances = new Dictionary(); + foreach (var entry in selectDesc.SubSelectStrategyCollection.Subqueries) { + var specificService = aiRegistryExpr.AllocateSubselect(entry.Key); + entry.Key.Strategy = specificService; + + var subselectPriorStrategies = new Dictionary(); + foreach (var subselectPrior in entry.Value.PriorNodesList) { + var specificSubselectPriorService = aiRegistryExpr.AllocatePrior(subselectPrior); + subselectPriorStrategies.Put(subselectPrior, specificSubselectPriorService); + } + + var subselectPreviousStrategies = new Dictionary(); + foreach (var subselectPrevious in entry.Value.PrevNodesList) { + var specificSubselectPreviousService = aiRegistryExpr.AllocatePrevious(subselectPrevious); + subselectPreviousStrategies.Put(subselectPrevious, specificSubselectPreviousService); + } + + var subselectAggregation = aiRegistryExpr.AllocateSubselectAggregation(entry.Key); + var strategyHolder = new SubSelectStrategyHolder(specificService, subselectAggregation, subselectPriorStrategies, subselectPreviousStrategies, null, null, null); + subselectStrategyInstances.Put(entry.Key, strategyHolder); + } + + // use statement-wide agent-instance-specific "prior" + priorStrategyInstances = new Dictionary(); + foreach (var priorNode in selectDesc.ViewResourceDelegateUnverified.PriorRequests) { + var specificService = aiRegistryExpr.AllocatePrior(priorNode); + priorStrategyInstances.Put(priorNode, specificService); + } + + // use statement-wide agent-instance-specific "previous" + previousStrategyInstances = new Dictionary(); + foreach (var previousNode in selectDesc.ViewResourceDelegateUnverified.PreviousRequests) { + var specificService = aiRegistryExpr.AllocatePrevious(previousNode); + previousStrategyInstances.Put(previousNode, specificService); + } + + // use statement-wide agent-instance-specific match-recognize "previous" + matchRecognizePrevEvalStrategy = aiRegistryExpr.AllocateMatchRecognizePrevious(); + + // use statement-wide agent-instance-specific tables + tableAccessStrategyInstances = new Dictionary(); + if (statementSpec.TableNodes != null) { + foreach (var tableNode in statementSpec.TableNodes) { + var specificService = aiRegistryExpr.AllocateTableAccess(tableNode); + tableAccessStrategyInstances.Put(tableNode, specificService); + } + } + + var mergeView = new ContextMergeView(selectDesc.ResultSetProcessorPrototypeDesc.ResultSetProcessorFactory.ResultEventType); + finalViewable = mergeView; + + var statement = new ContextManagedStatementSelectDesc(statementSpec, statementContext, mergeView, selectDesc.StatementAgentInstanceFactorySelect, + selectDesc.ResultSetProcessorPrototypeDesc.AggregationServiceFactoryDesc.Expressions, + selectDesc.SubSelectStrategyCollection); + services.ContextManagementService.AddStatement(contextName, statement, isRecoveringResilient); + var selectStop = selectDesc.StopMethod; + stopStatementMethod = new ProxyEPStatementStopMethod() { + ProcStop = () => { + services.ContextManagementService.StoppedStatement(contextName, statementContext.StatementName, statementContext.StatementId, statementContext.Expression, statementContext.ExceptionHandlingService); + selectStop.Stop(); + } + }; + + selectDesc.DestroyCallbacks.AddCallback(new EPStatementDestroyCallbackContext(services.ContextManagementService, contextName, statementContext.StatementName, statementContext.StatementId)); + } else { + // Without context - start here + var resultOfStart = (StatementAgentInstanceFactorySelectResult) selectDesc.StatementAgentInstanceFactorySelect.NewContext(defaultAgentInstanceContext, isRecoveringResilient); + finalViewable = resultOfStart.FinalView; + + var startResultStop = services.EpStatementFactory.MakeStopMethod(resultOfStart); + var selectStop = selectDesc.StopMethod; + stopStatementMethod = new ProxyEPStatementStopMethod() { + ProcStop = () => { + StatementAgentInstanceUtil.StopSafe(startResultStop, statementContext); + selectStop.Stop(); + } + }; + + aggregationService = resultOfStart.OptionalAggegationService; + subselectStrategyInstances = resultOfStart.SubselectStrategies; + priorStrategyInstances = resultOfStart.PriorNodeStrategies; + previousStrategyInstances = resultOfStart.PreviousNodeStrategies; + tableAccessStrategyInstances = resultOfStart.TableAccessEvalStrategies; + preloadList = resultOfStart.PreloadList; + + matchRecognizePrevEvalStrategy = null; + if (resultOfStart.TopViews.Length > 0) { + var matchRecognize = EventRowRegexHelper.RecursiveFindRegexService(resultOfStart.TopViews[0]); + if (matchRecognize != null) { + matchRecognizePrevEvalStrategy = matchRecognize.PreviousEvaluationStrategy; + } + } + + if (statementContext.StatementExtensionServicesContext != null && statementContext.StatementExtensionServicesContext.StmtResources != null) { + var holder = statementContext.StatementExtensionServicesContext.ExtractStatementResourceHolder(resultOfStart); + statementContext.StatementExtensionServicesContext.StmtResources.Unpartitioned = holder; + statementContext.StatementExtensionServicesContext.PostProcessStart(resultOfStart, isRecoveringResilient); + } + } + + var matchRecognizeNodes = selectDesc.StatementAgentInstanceFactorySelect.ViewResourceDelegate.PerStream[0].MatchRecognizePreviousRequests; + + // assign strategies to expression nodes + EPStatementStartMethodHelperAssignExpr.AssignExpressionStrategies(selectDesc, aggregationService, subselectStrategyInstances, priorStrategyInstances, previousStrategyInstances, matchRecognizeNodes, matchRecognizePrevEvalStrategy, tableAccessStrategyInstances); + + // execute preload if any + foreach (var preload in preloadList) { + preload.ExecutePreload(); + } + + // handle association to table + if (statementSpec.IntoTableSpec != null) { + services.StatementVariableRefService.AddReferences(statementContext.StatementName, statementSpec.IntoTableSpec.Name); + } + + return new EPStatementStartResult(finalViewable, stopStatementMethod, selectDesc.DestroyCallbacks); + } + + private void ValidateTableAccessUse(IntoTableSpec bindingSpec, ExprTableAccessNode[] tableNodes) + { + var statementSpec = base.StatementSpec; + if (statementSpec.IntoTableSpec != null && statementSpec.TableNodes != null && statementSpec.TableNodes.Length > 0) { + foreach (var node in statementSpec.TableNodes) { + if (node.TableName.Equals(statementSpec.IntoTableSpec.Name)) { + throw new ExprValidationException("Invalid use of table '" + statementSpec.IntoTableSpec.Name + "', aggregate-into requires write-only, the expression '" + + ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(statementSpec.TableNodes[0]) + "' is not allowed"); + } + } + } + + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodSelectDesc.cs b/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodSelectDesc.cs new file mode 100755 index 000000000..19279f8b5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodSelectDesc.cs @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.context.factory; +using com.espertech.esper.core.context.subselect; +using com.espertech.esper.epl.core; + +namespace com.espertech.esper.core.start +{ + /// + /// Starts and provides the stop method for EPL statements. + /// + public class EPStatementStartMethodSelectDesc + { + public EPStatementStartMethodSelectDesc( + StatementAgentInstanceFactorySelect statementAgentInstanceFactorySelect, + SubSelectStrategyCollection subSelectStrategyCollection, + ViewResourceDelegateUnverified viewResourceDelegateUnverified, + ResultSetProcessorFactoryDesc resultSetProcessorPrototypeDesc, + EPStatementStopMethod stopMethod, + EPStatementDestroyCallbackList destroyCallbacks) + { + StatementAgentInstanceFactorySelect = statementAgentInstanceFactorySelect; + SubSelectStrategyCollection = subSelectStrategyCollection; + ViewResourceDelegateUnverified = viewResourceDelegateUnverified; + ResultSetProcessorPrototypeDesc = resultSetProcessorPrototypeDesc; + StopMethod = stopMethod; + DestroyCallbacks = destroyCallbacks; + } + + public StatementAgentInstanceFactorySelect StatementAgentInstanceFactorySelect { get; private set; } + + public SubSelectStrategyCollection SubSelectStrategyCollection { get; private set; } + + public ViewResourceDelegateUnverified ViewResourceDelegateUnverified { get; private set; } + + public ResultSetProcessorFactoryDesc ResultSetProcessorPrototypeDesc { get; private set; } + + public EPStatementStopMethod StopMethod { get; private set; } + + public EPStatementDestroyCallbackList DestroyCallbacks { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodSelectUtil.cs b/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodSelectUtil.cs new file mode 100755 index 000000000..1c156491e --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodSelectUtil.cs @@ -0,0 +1,654 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.client.annotation; +using com.espertech.esper.client.hook; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.activator; +using com.espertech.esper.core.context.factory; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.annotation; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.db; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.join.@base; +using com.espertech.esper.epl.named; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.util; +using com.espertech.esper.epl.view; +using com.espertech.esper.filter; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.type; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.start +{ + /// Starts and provides the stop method for EPL statements. + public class EPStatementStartMethodSelectUtil + { + public static EPStatementStartMethodSelectDesc Prepare( + StatementSpecCompiled statementSpec, + EPServicesContext services, + StatementContext statementContext, + bool recoveringResilient, + AgentInstanceContext defaultAgentInstanceContext, + bool queryPlanLogging, + ViewableActivatorFactory optionalViewableActivatorFactory, + OutputProcessViewCallback optionalOutputProcessViewCallback, + SelectExprProcessorDeliveryCallback selectExprProcessorDeliveryCallback) + { + + // define stop and destroy + var stopCallbacks = new List(); + var destroyCallbacks = new EPStatementDestroyCallbackList(); + + // determine context + var contextName = statementSpec.OptionalContextName; + var contextPropertyRegistry = (contextName != null) + ? services.ContextManagementService.GetContextDescriptor(contextName).ContextPropertyRegistry + : null; + + // Determine stream names for each stream - some streams may not have a name given + var streamNames = EPStatementStartMethodHelperUtil.DetermineStreamNames(statementSpec.StreamSpecs); + var numStreams = streamNames.Length; + if (numStreams == 0) + { + throw new ExprValidationException("The from-clause is required but has not been specified"); + } + var isJoin = statementSpec.StreamSpecs.Length > 1; + var hasContext = statementSpec.OptionalContextName != null; + + // First we create streams for subselects, if there are any + var subSelectStreamDesc = EPStatementStartMethodHelperSubselect.CreateSubSelectActivation( + services, statementSpec, statementContext, destroyCallbacks); + + // Create streams and views + var eventStreamParentViewableActivators = new ViewableActivator[numStreams]; + var unmaterializedViewChain = new ViewFactoryChain[numStreams]; + var eventTypeNames = new string[numStreams]; + var isNamedWindow = new bool[numStreams]; + var historicalEventViewables = new HistoricalEventViewable[numStreams]; + + // verify for joins that required views are present + var joinAnalysisResult = VerifyJoinViews(statementSpec, statementContext.NamedWindowMgmtService); + var evaluatorContextStmt = new ExprEvaluatorContextStatement(statementContext, false); + + for (var i = 0; i < statementSpec.StreamSpecs.Length; i++) + { + var streamSpec = statementSpec.StreamSpecs[i]; + + var isCanIterateUnbound = streamSpec.ViewSpecs.Length == 0 && + (services.ConfigSnapshot.EngineDefaults.ViewResources.IsIterableUnbound || + AnnotationUtil.FindAnnotation( + statementSpec.Annotations, typeof (IterableUnboundAttribute)) != null); + + // Create view factories and parent view based on a filter specification + if (streamSpec is FilterStreamSpecCompiled) + { + var filterStreamSpec = (FilterStreamSpecCompiled) streamSpec; + eventTypeNames[i] = filterStreamSpec.FilterSpec.FilterForEventTypeName; + + // Since only for non-joins we get the existing stream's lock and try to reuse it's views + var filterSubselectSameStream = + EPStatementStartMethodHelperUtil.DetermineSubquerySameStream(statementSpec, filterStreamSpec); + + // create activator + ViewableActivator activatorDeactivator; + if (optionalViewableActivatorFactory != null) + { + activatorDeactivator = optionalViewableActivatorFactory.CreateActivatorSimple(filterStreamSpec); + if (activatorDeactivator == null) + { + throw new IllegalStateException( + "Viewable activate is null for " + filterStreamSpec.FilterSpec.FilterForEventType.Name); + } + } + else + { + if (!hasContext) + { + activatorDeactivator = services.ViewableActivatorFactory.CreateStreamReuseView( + services, statementContext, statementSpec, filterStreamSpec, isJoin, + evaluatorContextStmt, filterSubselectSameStream, i, isCanIterateUnbound); + } + else + { + InstrumentationAgent instrumentationAgentFilter = null; + if (InstrumentationHelper.ENABLED) + { + var eventTypeName = filterStreamSpec.FilterSpec.FilterForEventType.Name; + var streamNumber = i; + instrumentationAgentFilter = new ProxyInstrumentationAgent() + { + ProcIndicateQ = () => + { + InstrumentationHelper.Get() + .QFilterActivationStream(eventTypeName, streamNumber); + }, + ProcIndicateA = () => { + InstrumentationHelper.Get().AFilterActivationStream(); + } + }; + } + + activatorDeactivator = services.ViewableActivatorFactory.CreateFilterProxy( + services, filterStreamSpec.FilterSpec, statementSpec.Annotations, false, + instrumentationAgentFilter, isCanIterateUnbound, i); + } + } + eventStreamParentViewableActivators[i] = activatorDeactivator; + + var resultEventType = filterStreamSpec.FilterSpec.ResultEventType; + unmaterializedViewChain[i] = services.ViewService.CreateFactories( + i, resultEventType, streamSpec.ViewSpecs, streamSpec.Options, statementContext, false, -1); + } + else if (streamSpec is PatternStreamSpecCompiled) + { + // Create view factories and parent view based on a pattern expression + var patternStreamSpec = (PatternStreamSpecCompiled) streamSpec; + var usedByChildViews = streamSpec.ViewSpecs.Length > 0 || (statementSpec.InsertIntoDesc != null); + var patternTypeName = statementContext.StatementId + "_pattern_" + i; + var eventType = services.EventAdapterService.CreateSemiAnonymousMapType( + patternTypeName, patternStreamSpec.TaggedEventTypes, patternStreamSpec.ArrayEventTypes, + usedByChildViews); + unmaterializedViewChain[i] = services.ViewService.CreateFactories( + i, eventType, streamSpec.ViewSpecs, streamSpec.Options, statementContext, false, -1); + + var rootFactoryNode = services.PatternNodeFactory.MakeRootNode(patternStreamSpec.EvalFactoryNode); + var patternContext = statementContext.PatternContextFactory.CreateContext( + statementContext, i, rootFactoryNode, patternStreamSpec.MatchedEventMapMeta, true); + + // create activator + var patternActivator = services.ViewableActivatorFactory.CreatePattern( + patternContext, rootFactoryNode, eventType, + EPStatementStartMethodHelperUtil.IsConsumingFilters(patternStreamSpec.EvalFactoryNode), + patternStreamSpec.IsSuppressSameEventMatches, patternStreamSpec.IsDiscardPartialsOnMatch, + isCanIterateUnbound); + eventStreamParentViewableActivators[i] = patternActivator; + } + else if (streamSpec is DBStatementStreamSpec) + { + // Create view factories and parent view based on a database SQL statement + ValidateNoViews(streamSpec, "Historical data"); + var sqlStreamSpec = (DBStatementStreamSpec) streamSpec; + var typeConversionHook = + (SQLColumnTypeConversion) + TypeHelper.GetAnnotationHook( + statementSpec.Annotations, HookType.SQLCOL, typeof (SQLColumnTypeConversion), + statementContext.EngineImportService); + var outputRowConversionHook = + (SQLOutputRowConversion) + TypeHelper.GetAnnotationHook( + statementSpec.Annotations, HookType.SQLROW, typeof (SQLOutputRowConversion), + statementContext.EngineImportService); + var epStatementAgentInstanceHandle = defaultAgentInstanceContext.EpStatementAgentInstanceHandle; + var historicalEventViewable = + DatabasePollingViewableFactory.CreateDBStatementView( + statementContext.StatementId, i, sqlStreamSpec, + services.DatabaseRefService, + services.EventAdapterService, + epStatementAgentInstanceHandle, + statementContext.Annotations, + typeConversionHook, + outputRowConversionHook, + statementContext.ConfigSnapshot.EngineDefaults.Logging.IsEnableADO, + services.DataCacheFactory, statementContext); + historicalEventViewables[i] = historicalEventViewable; + unmaterializedViewChain[i] = ViewFactoryChain.FromTypeNoViews(historicalEventViewable.EventType); + eventStreamParentViewableActivators[i] = + services.ViewableActivatorFactory.MakeHistorical(historicalEventViewable); + stopCallbacks.Add(historicalEventViewable); + } + else if (streamSpec is MethodStreamSpec) + { + ValidateNoViews(streamSpec, "Method data"); + var methodStreamSpec = (MethodStreamSpec) streamSpec; + var epStatementAgentInstanceHandle = defaultAgentInstanceContext.EpStatementAgentInstanceHandle; + var historicalEventViewable = MethodPollingViewableFactory.CreatePollMethodView( + i, methodStreamSpec, services.EventAdapterService, epStatementAgentInstanceHandle, + statementContext.EngineImportService, statementContext.SchedulingService, + statementContext.ScheduleBucket, evaluatorContextStmt, statementContext.VariableService, + statementContext.ContextName, services.DataCacheFactory, statementContext); + historicalEventViewables[i] = historicalEventViewable; + unmaterializedViewChain[i] = ViewFactoryChain.FromTypeNoViews(historicalEventViewable.EventType); + eventStreamParentViewableActivators[i] = + services.ViewableActivatorFactory.MakeHistorical(historicalEventViewable); + stopCallbacks.Add(historicalEventViewable); + } + else if (streamSpec is TableQueryStreamSpec) + { + ValidateNoViews(streamSpec, "Table data"); + var tableStreamSpec = (TableQueryStreamSpec) streamSpec; + if (isJoin && tableStreamSpec.FilterExpressions.Count > 0) + { + throw new ExprValidationException( + "Joins with tables do not allow table filter expressions, please add table filters to the where-clause instead"); + } + var metadata = services.TableService.GetTableMetadata(tableStreamSpec.TableName); + ExprEvaluator[] tableFilterEvals = null; + if (tableStreamSpec.FilterExpressions.Count > 0) + { + tableFilterEvals = ExprNodeUtility.GetEvaluators(tableStreamSpec.FilterExpressions); + } + EPLValidationUtil.ValidateContextName( + true, metadata.TableName, metadata.ContextName, statementSpec.OptionalContextName, false); + eventStreamParentViewableActivators[i] = services.ViewableActivatorFactory.CreateTable( + metadata, tableFilterEvals); + unmaterializedViewChain[i] = ViewFactoryChain.FromTypeNoViews(metadata.InternalEventType); + eventTypeNames[i] = tableStreamSpec.TableName; + joinAnalysisResult.SetTablesForStream(i, metadata); + if (tableStreamSpec.Options.IsUnidirectional) + { + throw new ExprValidationException("Tables cannot be marked as unidirectional"); + } + if (tableStreamSpec.Options.IsRetainIntersection || tableStreamSpec.Options.IsRetainUnion) + { + throw new ExprValidationException("Tables cannot be marked with retain"); + } + if (isJoin) + { + destroyCallbacks.AddCallback( + new EPStatementDestroyCallbackTableIdxRef( + services.TableService, metadata, statementContext.StatementName)); + } + services.StatementVariableRefService.AddReferences( + statementContext.StatementName, metadata.TableName); + } + else if (streamSpec is NamedWindowConsumerStreamSpec) + { + var namedSpec = (NamedWindowConsumerStreamSpec) streamSpec; + var processor = services.NamedWindowMgmtService.GetProcessor(namedSpec.WindowName); + var namedWindowType = processor.TailView.EventType; + if (namedSpec.OptPropertyEvaluator != null) + { + namedWindowType = namedSpec.OptPropertyEvaluator.FragmentEventType; + } + + eventStreamParentViewableActivators[i] = + services.ViewableActivatorFactory.CreateNamedWindow(processor, namedSpec, statementContext); + services.NamedWindowConsumerMgmtService.AddConsumer(statementContext, namedSpec); + unmaterializedViewChain[i] = services.ViewService.CreateFactories( + i, namedWindowType, namedSpec.ViewSpecs, namedSpec.Options, statementContext, false, -1); + joinAnalysisResult.SetNamedWindow(i); + eventTypeNames[i] = namedSpec.WindowName; + isNamedWindow[i] = true; + + // Consumers to named windows cannot declare a data window view onto the named window to avoid duplicate remove streams + EPStatementStartMethodHelperValidate.ValidateNoDataWindowOnNamedWindow( + unmaterializedViewChain[i].FactoryChain); + } + else + { + throw new ExprValidationException("Unknown stream specification type: " + streamSpec); + } + } + + // handle match-recognize pattern + if (statementSpec.MatchRecognizeSpec != null) + { + if (isJoin) + { + throw new ExprValidationException("Joins are not allowed when using match-recognize"); + } + if (joinAnalysisResult.TablesPerStream[0] != null) + { + throw new ExprValidationException("Tables cannot be used with match-recognize"); + } + var isUnbound = (unmaterializedViewChain[0].FactoryChain.IsEmpty()) && + (!(statementSpec.StreamSpecs[0] is NamedWindowConsumerStreamSpec)); + var factory = services.RegexHandlerFactory.MakeViewFactory( + unmaterializedViewChain[0], statementSpec.MatchRecognizeSpec, defaultAgentInstanceContext, isUnbound, + statementSpec.Annotations, services.ConfigSnapshot.EngineDefaults.MatchRecognize); + unmaterializedViewChain[0].FactoryChain.Add(factory); + + EPStatementStartMethodHelperAssignExpr.AssignAggregations( + factory.AggregationService, factory.AggregationExpressions); + } + + // Obtain event types from view factory chains + var streamEventTypes = new EventType[statementSpec.StreamSpecs.Length]; + for (var i = 0; i < unmaterializedViewChain.Length; i++) + { + streamEventTypes[i] = unmaterializedViewChain[i].EventType; + } + + // Add uniqueness information useful for joins + joinAnalysisResult.AddUniquenessInfo(unmaterializedViewChain, statementSpec.Annotations); + + // Validate sub-select views + var subSelectStrategyCollection = EPStatementStartMethodHelperSubselect.PlanSubSelect( + services, statementContext, queryPlanLogging, subSelectStreamDesc, streamNames, streamEventTypes, + eventTypeNames, statementSpec.DeclaredExpressions, contextPropertyRegistry); + + // Construct type information per stream + var typeService = new StreamTypeServiceImpl( + streamEventTypes, streamNames, + EPStatementStartMethodHelperUtil.GetHasIStreamOnly(isNamedWindow, unmaterializedViewChain), + services.EngineURI, false); + var viewResourceDelegateUnverified = new ViewResourceDelegateUnverified(); + + // Validate views that require validation, specifically streams that don't have + // sub-views such as DB SQL joins + var historicalViewableDesc = new HistoricalViewableDesc(numStreams); + for (var stream = 0; stream < historicalEventViewables.Length; stream++) + { + var historicalEventViewable = historicalEventViewables[stream]; + if (historicalEventViewable == null) + { + continue; + } + historicalEventViewable.Validate( + services.EngineImportService, + typeService, + statementContext.TimeProvider, + statementContext.VariableService, + statementContext.TableService, + statementContext.ScriptingService, evaluatorContextStmt, + services.ConfigSnapshot, + services.SchedulingService, + services.EngineURI, + statementSpec.SqlParameters, + statementContext.EventAdapterService, + statementContext); + historicalViewableDesc.SetHistorical(stream, historicalEventViewable.RequiredStreams); + if (historicalEventViewable.RequiredStreams.Contains(stream)) + { + throw new ExprValidationException( + "Parameters for historical stream " + stream + + " indicate that the stream is subordinate to itself as stream parameters originate in the same stream"); + } + } + + // unidirectional is not supported with into-table + if (joinAnalysisResult.IsUnidirectional && statementSpec.IntoTableSpec != null) + { + throw new ExprValidationException("Into-table does not allow unidirectional joins"); + } + + // Construct a processor for results posted by views and joins, which takes care of aggregation if required. + // May return null if we don't need to post-process results posted by views or joins. + var resultSetProcessorPrototypeDesc = ResultSetProcessorFactoryFactory.GetProcessorPrototype( + statementSpec, statementContext, typeService, viewResourceDelegateUnverified, + joinAnalysisResult.UnidirectionalInd, true, contextPropertyRegistry, selectExprProcessorDeliveryCallback, + services.ConfigSnapshot, services.ResultSetProcessorHelperFactory, false, false); + + // Validate where-clause filter tree, outer join clause and output limit expression + EPStatementStartMethodHelperValidate.ValidateNodes( + statementSpec, statementContext, typeService, viewResourceDelegateUnverified); + + // Handle 'prior' function nodes in terms of view requirements + var viewResourceDelegateVerified = + EPStatementStartMethodHelperViewResources.VerifyPreviousAndPriorRequirements( + unmaterializedViewChain, viewResourceDelegateUnverified); + + // handle join + JoinSetComposerPrototype joinSetComposerPrototype = null; + if (numStreams > 1) + { + var selectsRemoveStream = statementSpec.SelectStreamSelectorEnum.IsSelectsRStream() || + statementSpec.OutputLimitSpec != null; + var hasAggregations = + !resultSetProcessorPrototypeDesc.AggregationServiceFactoryDesc.Expressions.IsEmpty(); + joinSetComposerPrototype = JoinSetComposerPrototypeFactory.MakeComposerPrototype( + statementContext.StatementName, statementContext.StatementId, + statementSpec.OuterJoinDescList, statementSpec.FilterRootNode, typeService.EventTypes, streamNames, + joinAnalysisResult, queryPlanLogging, statementContext, historicalViewableDesc, + defaultAgentInstanceContext, + selectsRemoveStream, hasAggregations, services.TableService, false, + services.EventTableIndexService.AllowInitIndex(recoveringResilient)); + } + + // obtain factory for output limiting + var outputViewFactory = OutputProcessViewFactoryFactory.Make( + statementSpec, services.InternalEventRouter, statementContext, + resultSetProcessorPrototypeDesc.ResultSetProcessorFactory.ResultEventType, + optionalOutputProcessViewCallback, services.TableService, + resultSetProcessorPrototypeDesc.ResultSetProcessorFactory.ResultSetProcessorType, + services.ResultSetProcessorHelperFactory, services.StatementVariableRefService); + + // Factory for statement-context instances + var factoryX = new StatementAgentInstanceFactorySelect( + numStreams, eventStreamParentViewableActivators, + statementContext, statementSpec, services, + typeService, unmaterializedViewChain, resultSetProcessorPrototypeDesc, joinAnalysisResult, + recoveringResilient, + joinSetComposerPrototype, subSelectStrategyCollection, viewResourceDelegateVerified, outputViewFactory); + + var stopMethod = new EPStatementStopMethodImpl(statementContext, stopCallbacks); + return new EPStatementStartMethodSelectDesc( + factoryX, subSelectStrategyCollection, viewResourceDelegateUnverified, resultSetProcessorPrototypeDesc, + stopMethod, destroyCallbacks); + } + + private static void ValidateNoViews(StreamSpecCompiled streamSpec, string conceptName) + { + if (streamSpec.ViewSpecs.Length > 0) + { + throw new ExprValidationException( + conceptName + " joins do not allow views onto the data, view '" + + streamSpec.ViewSpecs[0].ObjectName + "' is not valid in this context"); + } + } + + private static StreamJoinAnalysisResult VerifyJoinViews( + StatementSpecCompiled statementSpec, + NamedWindowMgmtService namedWindowMgmtService) + { + var streamSpecs = statementSpec.StreamSpecs; + var analysisResult = new StreamJoinAnalysisResult(streamSpecs.Length); + if (streamSpecs.Length < 2) + { + return analysisResult; + } + + // Determine if any stream has a unidirectional keyword + + // inspect unidirectional indicator and named window flags + for (var i = 0; i < statementSpec.StreamSpecs.Length; i++) + { + var streamSpec = statementSpec.StreamSpecs[i]; + if (streamSpec.Options.IsUnidirectional) + { + analysisResult.SetUnidirectionalInd(i); + } + if (streamSpec.ViewSpecs.Length > 0) + { + analysisResult.SetHasChildViews(i); + } + if (streamSpec is NamedWindowConsumerStreamSpec) + { + var nwSpec = (NamedWindowConsumerStreamSpec) streamSpec; + if (nwSpec.OptPropertyEvaluator != null && !streamSpec.Options.IsUnidirectional) + { + throw new ExprValidationException( + "Failed to validate named window use in join, contained-event is only allowed for named windows when marked as unidirectional"); + } + analysisResult.SetNamedWindow(i); + var processor = namedWindowMgmtService.GetProcessor(nwSpec.WindowName); + var uniqueIndexes = processor.UniqueIndexes; + analysisResult.UniqueKeys[i] = uniqueIndexes; + if (processor.IsVirtualDataWindow) + { + analysisResult.ViewExternal[i] = + (agentInstanceContext) => + processor.GetProcessorInstance(agentInstanceContext).RootViewInstance.VirtualDataWindow; + } + } + } + + // non-outer-join: verify unidirectional can be on a single stream only + if (statementSpec.StreamSpecs.Length > 1 && analysisResult.IsUnidirectional) + { + VerifyJoinUnidirectional(analysisResult, statementSpec); + } + + // count streams that provide data, excluding streams that poll data (DB and method) + var countProviderNonpolling = 0; + for (var i = 0; i < statementSpec.StreamSpecs.Length; i++) + { + var streamSpec = statementSpec.StreamSpecs[i]; + if ((streamSpec is MethodStreamSpec) || + (streamSpec is DBStatementStreamSpec) || + (streamSpec is TableQueryStreamSpec)) + { + continue; + } + countProviderNonpolling++; + } + + // if there is only one stream providing data, the analysis is done + if (countProviderNonpolling == 1) + { + return analysisResult; + } + // there are multiple driving streams, verify the presence of a view for insert/remove stream + + // validation of join views works differently for unidirectional as there can be self-joins that don't require a view + // see if this is a self-join in which all streams are filters and filter specification is the same. + FilterSpecCompiled unidirectionalFilterSpec = null; + FilterSpecCompiled lastFilterSpec = null; + var pureSelfJoin = true; + foreach (var streamSpec in statementSpec.StreamSpecs) + { + if (!(streamSpec is FilterStreamSpecCompiled)) + { + pureSelfJoin = false; + continue; + } + + var filterSpec = ((FilterStreamSpecCompiled) streamSpec).FilterSpec; + if ((lastFilterSpec != null) && (!lastFilterSpec.EqualsTypeAndFilter(filterSpec))) + { + pureSelfJoin = false; + } + if (streamSpec.ViewSpecs.Length > 0) + { + pureSelfJoin = false; + } + lastFilterSpec = filterSpec; + + if (streamSpec.Options.IsUnidirectional) + { + unidirectionalFilterSpec = filterSpec; + } + } + + // self-join without views and not unidirectional + if (pureSelfJoin && (unidirectionalFilterSpec == null)) + { + analysisResult.IsPureSelfJoin = true; + return analysisResult; + } + + // weed out filter and pattern streams that don't have a view in a join + for (var i = 0; i < statementSpec.StreamSpecs.Length; i++) + { + var streamSpec = statementSpec.StreamSpecs[i]; + if (streamSpec.ViewSpecs.Length > 0) + { + continue; + } + + var name = streamSpec.OptionalStreamName; + if ((name == null) && (streamSpec is FilterStreamSpecCompiled)) + { + name = ((FilterStreamSpecCompiled) streamSpec).FilterSpec.FilterForEventTypeName; + } + if ((name == null) && (streamSpec is PatternStreamSpecCompiled)) + { + name = "pattern event stream"; + } + + if (streamSpec.Options.IsUnidirectional) + { + continue; + } + // allow a self-join without a child view, in that the filter spec is the same as the unidirection's stream filter + if ((unidirectionalFilterSpec != null) && + (streamSpec is FilterStreamSpecCompiled) && + (((FilterStreamSpecCompiled) streamSpec).FilterSpec.EqualsTypeAndFilter(unidirectionalFilterSpec))) + { + analysisResult.SetUnidirectionalNonDriving(i); + continue; + } + if ((streamSpec is FilterStreamSpecCompiled) || + (streamSpec is PatternStreamSpecCompiled)) + { + throw new ExprValidationException( + "Joins require that at least one view is specified for each stream, no view was specified for " + + name); + } + } + + return analysisResult; + } + + private static void VerifyJoinUnidirectional( + StreamJoinAnalysisResult analysisResult, + StatementSpecCompiled statementSpec) + { + var numUnidirectionalStreams = analysisResult.UnidirectionalCount; + var numStreams = statementSpec.StreamSpecs.Length; + + // only a single stream is unidirectional (applies to all but all-full-outer-join) + if (!IsFullOuterJoinAllStreams(statementSpec)) + { + if (numUnidirectionalStreams > 1) + { + throw new ExprValidationException( + "The unidirectional keyword can only apply to one stream in a join"); + } + } + else + { + // verify full-outer-join: requires unidirectional for all streams + if (numUnidirectionalStreams > 1 && numUnidirectionalStreams < numStreams) + { + throw new ExprValidationException( + "The unidirectional keyword must either apply to a single stream or all streams in a full outer join"); + } + } + + // verify no-child-view for unidirectional + for (var i = 0; i < statementSpec.StreamSpecs.Length; i++) + { + if (analysisResult.UnidirectionalInd[i]) + { + if (analysisResult.HasChildViews[i]) + { + throw new ExprValidationException( + "The unidirectional keyword requires that no views are declared onto the stream (applies to stream " + + i + ")"); + } + } + } + } + + private static bool IsFullOuterJoinAllStreams(StatementSpecCompiled statementSpec) + { + if (statementSpec.OuterJoinDescList == null || statementSpec.OuterJoinDescList.Length == 0) + { + return false; + } + for (var stream = 0; stream < statementSpec.StreamSpecs.Length - 1; stream++) + { + if (statementSpec.OuterJoinDescList[stream].OuterJoinType != OuterJoinType.FULL) + { + return false; + } + } + return true; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodUpdate.cs b/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodUpdate.cs new file mode 100755 index 000000000..b2d58299e --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/start/EPStatementStartMethodUpdate.cs @@ -0,0 +1,237 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.factory; +using com.espertech.esper.core.context.mgr; +using com.espertech.esper.core.context.subselect; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.subquery; +using com.espertech.esper.epl.spec; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.start +{ + /// Starts and provides the stop method for EPL statements. + public class EPStatementStartMethodUpdate : EPStatementStartMethodBase + { + public EPStatementStartMethodUpdate(StatementSpecCompiled statementSpec) + : base(statementSpec) + { + } + + public override EPStatementStartResult StartInternal( + EPServicesContext services, + StatementContext statementContext, + bool isNewStatement, + bool isRecoveringStatement, + bool isRecoveringResilient) + { + var statementSpec = base.StatementSpec; + + // define stop and destroy + var stopCallbacks = new List(); + var destroyCallbacks = new EPStatementDestroyCallbackList(); + + // determine context + var contextName = statementSpec.OptionalContextName; + if (contextName != null) + { + throw new ExprValidationException("Update IStream is not supported in conjunction with a context"); + } + + // First we create streams for subselects, if there are any + var subSelectStreamDesc = + EPStatementStartMethodHelperSubselect.CreateSubSelectActivation( + services, statementSpec, statementContext, destroyCallbacks); + + var streamSpec = statementSpec.StreamSpecs[0]; + var updateSpec = statementSpec.UpdateSpec; + string triggereventTypeName; + + if (streamSpec is FilterStreamSpecCompiled) + { + var filterStreamSpec = (FilterStreamSpecCompiled) streamSpec; + triggereventTypeName = filterStreamSpec.FilterSpec.FilterForEventTypeName; + } + else if (streamSpec is NamedWindowConsumerStreamSpec) + { + var namedSpec = (NamedWindowConsumerStreamSpec) streamSpec; + triggereventTypeName = namedSpec.WindowName; + } + else if (streamSpec is TableQueryStreamSpec) + { + throw new ExprValidationException("Tables cannot be used in an update-istream statement"); + } + else + { + throw new ExprValidationException("Unknown stream specification streamEventType: " + streamSpec); + } + + // determine a stream name + var streamName = triggereventTypeName; + if (updateSpec.OptionalStreamName != null) + { + streamName = updateSpec.OptionalStreamName; + } + + var streamEventType = services.EventAdapterService.GetEventTypeByName(triggereventTypeName); + var typeService = new StreamTypeServiceImpl( + new EventType[] { streamEventType }, + new string[] { streamName }, + new bool[] { true }, + services.EngineURI, false); + + // determine subscriber result types + var evaluatorContextStmt = new ExprEvaluatorContextStatement(statementContext, false); + statementContext.StatementResultService.SetSelectClause( + new Type[] { streamEventType.UnderlyingType }, + new string[] { "*" }, + false, null, evaluatorContextStmt); + + // Materialize sub-select views + var subSelectStrategyCollection = + EPStatementStartMethodHelperSubselect.PlanSubSelect( + services, statementContext, IsQueryPlanLogging(services), subSelectStreamDesc, new string[] + { + streamName + }, new EventType[] + { + streamEventType + }, new string[] + { + triggereventTypeName + }, statementSpec.DeclaredExpressions, null); + + var validationContext = new ExprValidationContext( + typeService, statementContext.EngineImportService, statementContext.StatementExtensionServicesContext, + null, statementContext.SchedulingService, statementContext.VariableService, + statementContext.TableService, evaluatorContextStmt, statementContext.EventAdapterService, + statementContext.StatementName, statementContext.StatementId, statementContext.Annotations, + statementContext.ContextDescriptor, statementContext.ScriptingService, false, false, false, false, null, + false); + foreach (var assignment in updateSpec.Assignments) + { + var validated = ExprNodeUtility.GetValidatedAssignment(assignment, validationContext); + assignment.Expression = validated; + EPStatementStartMethodHelperValidate.ValidateNoAggregations( + validated, "Aggregation functions may not be used within an update-clause"); + } + if (updateSpec.OptionalWhereClause != null) + { + var validated = ExprNodeUtility.GetValidatedSubtree( + ExprNodeOrigin.WHERE, updateSpec.OptionalWhereClause, validationContext); + updateSpec.OptionalWhereClause = validated; + EPStatementStartMethodHelperValidate.ValidateNoAggregations( + validated, "Aggregation functions may not be used within an update-clause"); + } + + // preprocessing view + var onExprView = new InternalRoutePreprocessView(streamEventType, statementContext.StatementResultService); + + // validation + var routerDesc = + services.InternalEventRouter.GetValidatePreprocessing( + onExprView.EventType, updateSpec, statementContext.Annotations); + + // create context factory + var contextFactory = new StatementAgentInstanceFactoryUpdate( + statementContext, services, streamEventType, updateSpec, onExprView, routerDesc, + subSelectStrategyCollection); + statementContext.StatementAgentInstanceFactory = contextFactory; + + // perform start of hook-up to start + Viewable finalViewable; + EPStatementStopMethod stopStatementMethod; + IDictionary subselectStrategyInstances; + + // With context - delegate instantiation to context + var stopMethod = new EPStatementStopMethodImpl(statementContext, stopCallbacks); + if (statementSpec.OptionalContextName != null) + { + + // use statement-wide agent-instance-specific subselects + var aiRegistryExpr = statementContext.StatementAgentInstanceRegistry.AgentInstanceExprService; + subselectStrategyInstances = new Dictionary(); + foreach (var node in subSelectStrategyCollection.Subqueries.Keys) + { + var specificService = aiRegistryExpr.AllocateSubselect(node); + node.Strategy = specificService; + subselectStrategyInstances.Put( + node, new SubSelectStrategyHolder(null, null, null, null, null, null, null)); + } + + var mergeView = new ContextMergeView(onExprView.EventType); + finalViewable = mergeView; + + var statement = new ContextManagedStatementOnTriggerDesc( + statementSpec, statementContext, mergeView, contextFactory); + services.ContextManagementService.AddStatement( + statementSpec.OptionalContextName, statement, isRecoveringResilient); + stopStatementMethod = new ProxyEPStatementStopMethod + { + ProcStop = () => + { + services.ContextManagementService.StoppedStatement( + contextName, statementContext.StatementName, statementContext.StatementId, + statementContext.Expression, statementContext.ExceptionHandlingService); + stopMethod.Stop(); + } + }; + + destroyCallbacks.AddCallback( + new EPStatementDestroyCallbackContext( + services.ContextManagementService, statementSpec.OptionalContextName, + statementContext.StatementName, statementContext.StatementId)); + } + else + { + // Without context - start here + var agentInstanceContext = GetDefaultAgentInstanceContext(statementContext); + var resultOfStart = + (StatementAgentInstanceFactoryUpdateResult) + contextFactory.NewContext(agentInstanceContext, isRecoveringResilient); + finalViewable = resultOfStart.FinalView; + var stopCallback = services.EpStatementFactory.MakeStopMethod(resultOfStart); + stopStatementMethod = new ProxyEPStatementStopMethod + { + ProcStop = () => + { + stopCallback.Stop(); + stopMethod.Stop(); + } + }; + subselectStrategyInstances = resultOfStart.SubselectStrategies; + + if (statementContext.StatementExtensionServicesContext != null && + statementContext.StatementExtensionServicesContext.StmtResources != null) + { + var holder = + statementContext.StatementExtensionServicesContext.ExtractStatementResourceHolder(resultOfStart); + statementContext.StatementExtensionServicesContext.StmtResources.Unpartitioned = holder; + statementContext.StatementExtensionServicesContext.PostProcessStart( + resultOfStart, isRecoveringResilient); + } + } + + // assign subquery nodes + EPStatementStartMethodHelperAssignExpr.AssignSubqueryStrategies( + subSelectStrategyCollection, subselectStrategyInstances); + + return new EPStatementStartResult(finalViewable, stopStatementMethod, destroyCallbacks); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/start/EPStatementStartResult.cs b/NEsper.Core/NEsper.Core/core/start/EPStatementStartResult.cs new file mode 100755 index 000000000..879c4b3cf --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/start/EPStatementStartResult.cs @@ -0,0 +1,52 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.view; + +namespace com.espertech.esper.core.start +{ + /// Result holder returned by @{link EPStatementStartMethod}. + public class EPStatementStartResult + { + /// Ctor. + /// last view to attach listeners to + /// method to stop + public EPStatementStartResult(Viewable viewable, + EPStatementStopMethod stopMethod) + { + Viewable = viewable; + StopMethod = stopMethod; + DestroyMethod = null; + } + + /// Ctor. + /// last view to attach listeners to + /// method to stop + /// method to call when destroying + public EPStatementStartResult(Viewable viewable, + EPStatementStopMethod stopMethod, + EPStatementDestroyMethod destroyMethod) + { + Viewable = viewable; + StopMethod = stopMethod; + DestroyMethod = destroyMethod; + } + + /// Returns last view to attached to. + /// view + public Viewable Viewable { get; private set; } + + /// Returns stop method. + /// stop method. + public EPStatementStopMethod StopMethod { get; private set; } + + /// Returns destroy method. + /// destroy method + public EPStatementDestroyMethod DestroyMethod { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/start/EPStatementStopMethod.cs b/NEsper.Core/NEsper.Core/core/start/EPStatementStopMethod.cs new file mode 100755 index 000000000..6a0cfd2c3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/start/EPStatementStopMethod.cs @@ -0,0 +1,33 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.core.start +{ + public interface EPStatementStopMethod + { + void Stop(); + } + + public class ProxyEPStatementStopMethod : EPStatementStopMethod + { + public Action ProcStop; + + public ProxyEPStatementStopMethod() { } + public ProxyEPStatementStopMethod(Action procStop) + { + ProcStop = procStop; + } + + public void Stop() + { + ProcStop.Invoke(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/start/EPStatementStopMethodImpl.cs b/NEsper.Core/NEsper.Core/core/start/EPStatementStopMethodImpl.cs new file mode 100755 index 000000000..5b55c4b23 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/start/EPStatementStopMethodImpl.cs @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.util; + +namespace com.espertech.esper.core.start +{ + /// + /// Method to call to stop an EPStatement. + /// + public class EPStatementStopMethodImpl : EPStatementStopMethod + { + private readonly StatementContext _statementContext; + private readonly StopCallback[] _stopCallbacks; + + public EPStatementStopMethodImpl(StatementContext statementContext, IList stopCallbacks) + { + _statementContext = statementContext; + _stopCallbacks = stopCallbacks.ToArray(); + } + + public void Stop() + { + foreach (StopCallback stopCallback in _stopCallbacks) + { + StatementAgentInstanceUtil.StopSafe(stopCallback, _statementContext); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/start/FireAndForgetInstance.cs b/NEsper.Core/NEsper.Core/core/start/FireAndForgetInstance.cs new file mode 100755 index 000000000..e577300ce --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/start/FireAndForgetInstance.cs @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.virtualdw; +using com.espertech.esper.filter; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.start +{ + public abstract class FireAndForgetInstance + { + public abstract EventBean[] ProcessInsert(EPPreparedExecuteIUDSingleStreamExecInsert insert); + public abstract EventBean[] ProcessDelete(EPPreparedExecuteIUDSingleStreamExecDelete delete); + public abstract EventBean[] ProcessUpdate(EPPreparedExecuteIUDSingleStreamExecUpdate update); + public abstract ICollection SnapshotBestEffort(EPPreparedExecuteMethodQuery epPreparedExecuteMethodQuery, FilterSpecCompiled filter, Attribute[] annotations); + public abstract AgentInstanceContext AgentInstanceContext { get; } + public abstract Viewable TailViewInstance { get; } + public abstract VirtualDWView VirtualDataWindow { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/core/start/FireAndForgetInstanceNamedWindow.cs b/NEsper.Core/NEsper.Core/core/start/FireAndForgetInstanceNamedWindow.cs new file mode 100755 index 000000000..9e9e64f03 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/start/FireAndForgetInstanceNamedWindow.cs @@ -0,0 +1,93 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.named; +using com.espertech.esper.epl.virtualdw; +using com.espertech.esper.filter; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.start +{ + public class FireAndForgetInstanceNamedWindow : FireAndForgetInstance + { + private readonly NamedWindowProcessorInstance _processorInstance; + + public FireAndForgetInstanceNamedWindow(NamedWindowProcessorInstance processorInstance) + { + this._processorInstance = processorInstance; + } + + public NamedWindowProcessorInstance ProcessorInstance + { + get { return _processorInstance; } + } + + public override EventBean[] ProcessInsert(EPPreparedExecuteIUDSingleStreamExecInsert insert) + { + EPPreparedExecuteTableHelper.AssignTableAccessStrategies(insert.Services, insert.OptionalTableNodes, _processorInstance.TailViewInstance.AgentInstanceContext); + try { + var @event = insert.InsertHelper.Process(new EventBean[0], true, true, insert.ExprEvaluatorContext); + var inserted = new EventBean[] {@event}; + + var ctx = _processorInstance.TailViewInstance.AgentInstanceContext; + var ailock = ctx.AgentInstanceLock; + using (ailock.AcquireWriteLock()) + { + try + { + _processorInstance.RootViewInstance.Update(inserted, null); + } + catch (EPException) + { + _processorInstance.RootViewInstance.Update(null, inserted); + } + } + + return inserted; + } + finally { + insert.Services.TableService.TableExprEvaluatorContext.ReleaseAcquiredLocks(); + } + } + + public override EventBean[] ProcessDelete(EPPreparedExecuteIUDSingleStreamExecDelete delete) { + EPPreparedExecuteTableHelper.AssignTableAccessStrategies(delete.Services, delete.OptionalTableNodes, _processorInstance.TailViewInstance.AgentInstanceContext); + return _processorInstance.TailViewInstance.SnapshotDelete(delete.Filter, delete.OptionalWhereClause, delete.Annotations); + } + + public override EventBean[] ProcessUpdate(EPPreparedExecuteIUDSingleStreamExecUpdate update) { + EPPreparedExecuteTableHelper.AssignTableAccessStrategies(update.Services, update.OptionalTableNodes, _processorInstance.TailViewInstance.AgentInstanceContext); + return _processorInstance.TailViewInstance.SnapshotUpdate(update.Filter, update.OptionalWhereClause, update.UpdateHelper, update.Annotations); + } + + public override ICollection SnapshotBestEffort(EPPreparedExecuteMethodQuery query, FilterSpecCompiled filter, Attribute[] annotations) { + EPPreparedExecuteTableHelper.AssignTableAccessStrategies(query.Services, query.TableNodes, _processorInstance.TailViewInstance.AgentInstanceContext); + return _processorInstance.TailViewInstance.Snapshot(filter, annotations); + } + + public override AgentInstanceContext AgentInstanceContext + { + get { return _processorInstance.TailViewInstance.AgentInstanceContext; } + } + + public override Viewable TailViewInstance + { + get { return _processorInstance.TailViewInstance; } + } + + public override VirtualDWView VirtualDataWindow + { + get { return _processorInstance.RootViewInstance.VirtualDataWindow; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/start/FireAndForgetInstanceTable.cs b/NEsper.Core/NEsper.Core/core/start/FireAndForgetInstanceTable.cs new file mode 100755 index 000000000..5e28cdade --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/start/FireAndForgetInstanceTable.cs @@ -0,0 +1,151 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.ComponentModel; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.fafquery; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.epl.table.strategy; +using com.espertech.esper.epl.virtualdw; +using com.espertech.esper.filter; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.start +{ + public class FireAndForgetInstanceTable : FireAndForgetInstance + { + private readonly TableStateInstance _instance; + + public FireAndForgetInstanceTable(TableStateInstance instance) + { + _instance = instance; + } + + public override EventBean[] ProcessInsert(EPPreparedExecuteIUDSingleStreamExecInsert insert) + { + ExprTableEvalLockUtil.ObtainLockUnless(_instance.TableLevelRWLock.WriteLock, insert.Services.TableService.TableExprEvaluatorContext); + var theEvent = insert.InsertHelper.Process(new EventBean[0], true, true, insert.ExprEvaluatorContext); + var aggs = _instance.TableMetadata.RowFactory.MakeAggs(insert.ExprEvaluatorContext.AgentInstanceId, null, null, _instance.AggregationServicePassThru); + ((object[]) theEvent.Underlying)[0] = aggs; + _instance.AddEvent(theEvent); + return CollectionUtil.EVENTBEANARRAY_EMPTY; + } + + public override EventBean[] ProcessDelete(EPPreparedExecuteIUDSingleStreamExecDelete delete) + { + ExprTableEvalLockUtil.ObtainLockUnless(_instance.TableLevelRWLock.WriteLock, delete.Services.TableService.TableExprEvaluatorContext); + + if (delete.OptionalWhereClause == null) { + _instance.ClearInstance(); + return CollectionUtil.EVENTBEANARRAY_EMPTY; + } + + var found = SnapshotAndApplyFilter(delete.Filter, delete.Annotations, delete.OptionalWhereClause, _instance.AgentInstanceContext); + foreach (var @event in found) { + _instance.DeleteEvent(@event); + } + return CollectionUtil.EVENTBEANARRAY_EMPTY; + } + + public override EventBean[] ProcessUpdate(EPPreparedExecuteIUDSingleStreamExecUpdate update) + { + ExprTableEvalLockUtil.ObtainLockUnless(_instance.TableLevelRWLock.WriteLock, update.Services.TableService.TableExprEvaluatorContext); + var events = SnapshotAndApplyFilter(update.Filter, update.Annotations, update.OptionalWhereClause, _instance.AgentInstanceContext); + + if (events != null && events.IsEmpty()) { + return CollectionUtil.EVENTBEANARRAY_EMPTY; + } + + var eventsPerStream = new EventBean[3]; + if (events == null) { + update.TableUpdateStrategy.UpdateTable(_instance.EventCollection, _instance, eventsPerStream, _instance.AgentInstanceContext); + } + else { + update.TableUpdateStrategy.UpdateTable(events, _instance, eventsPerStream, _instance.AgentInstanceContext); + } + return CollectionUtil.EVENTBEANARRAY_EMPTY; + } + + public override ICollection SnapshotBestEffort(EPPreparedExecuteMethodQuery query, FilterSpecCompiled filter, Attribute[] annotations) + { + ExprTableEvalLockUtil.ObtainLockUnless(_instance.TableLevelRWLock.ReadLock, query.AgentInstanceContext); + var events = SnapshotNullWhenNoIndex(filter, annotations, null, null); + if (events != null) { + return events; + } + return _instance.EventCollection; + } + + private ICollection SnapshotAndApplyFilter(FilterSpecCompiled filter, Attribute[] annotations, ExprNode filterExpr, AgentInstanceContext agentInstanceContext) + { + var indexedResult = SnapshotNullWhenNoIndex(filter, annotations, null, null); + if (indexedResult != null) { + if (indexedResult.IsEmpty() || filterExpr == null) { + return indexedResult; + } + var dequeX = new ArrayDeque(Math.Min(indexedResult.Count, 16)); + ExprNodeUtility.ApplyFilterExpressionIterable(indexedResult.GetEnumerator(), filterExpr.ExprEvaluator, agentInstanceContext, dequeX); + return dequeX; + } + + // fall back to window operator if snapshot doesn't resolve successfully + var sourceCollection = _instance.EventCollection; + var it = sourceCollection.GetEnumerator(); + if (it.MoveNext() == false) { + return Collections.GetEmptyList(); + } + var deque = new ArrayDeque(sourceCollection.Count); + if (filterExpr != null) { + ExprNodeUtility.ApplyFilterExpressionIterable(sourceCollection.GetEnumerator(), filterExpr.ExprEvaluator, agentInstanceContext, deque); + } + else + { + do + { + deque.Add(it.Current); + } while (it.MoveNext()); + } + return deque; + } + + /// + /// Returns null when a filter cannot be applied, and a collection iterator must be used instead. + /// Returns best-effort matching events otherwise which should still be run through any filter expressions. + /// + private ICollection SnapshotNullWhenNoIndex(FilterSpecCompiled filter, Attribute[] annotations, ExprNode optionalWhereClause, AgentInstanceContext agentInstanceContext) + { + // return null when filter cannot be applies + return FireAndForgetQueryExec.Snapshot(filter, annotations, null, + _instance.IndexRepository, _instance.TableMetadata.IsQueryPlanLogging, + TableServiceImpl.QueryPlanLog, _instance.TableMetadata.TableName, + _instance.AgentInstanceContext); + } + + public override AgentInstanceContext AgentInstanceContext + { + get { return _instance.AgentInstanceContext; } + } + + public override Viewable TailViewInstance + { + get { return null; } + } + + public override VirtualDWView VirtualDataWindow + { + get { return null; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/start/FireAndForgetProcessor.cs b/NEsper.Core/NEsper.Core/core/start/FireAndForgetProcessor.cs new file mode 100755 index 000000000..3ec06b371 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/start/FireAndForgetProcessor.cs @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; + +namespace com.espertech.esper.core.start +{ + public abstract class FireAndForgetProcessor + { + public abstract EventType EventTypeResultSetProcessor { get; } + public abstract string ContextName { get; } + public abstract FireAndForgetInstance GetProcessorInstance(AgentInstanceContext agentInstanceContext); + public abstract FireAndForgetInstance GetProcessorInstanceContextById(int agentInstanceId); + public abstract FireAndForgetInstance GetProcessorInstanceNoContext(); + public abstract ICollection GetProcessorInstancesAll(); + public abstract string NamedWindowOrTableName { get; } + public abstract bool IsVirtualDataWindow { get; } + public abstract string[][] GetUniqueIndexes(FireAndForgetInstance processorInstance); + public abstract EventType EventTypePublic { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/core/start/FireAndForgetProcessorFactory.cs b/NEsper.Core/NEsper.Core/core/start/FireAndForgetProcessorFactory.cs new file mode 100755 index 000000000..5d603b361 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/start/FireAndForgetProcessorFactory.cs @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.named; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.table.mgmt; + +namespace com.espertech.esper.core.start +{ + public class FireAndForgetProcessorFactory + { + public static FireAndForgetProcessor ValidateResolveProcessor(StreamSpecCompiled streamSpec, EPServicesContext services) + { + // resolve processor + string processorName; + if (streamSpec is NamedWindowConsumerStreamSpec) { + var namedSpec = (NamedWindowConsumerStreamSpec) streamSpec; + processorName = namedSpec.WindowName; + } + else { + var tableSpec = (TableQueryStreamSpec) streamSpec; + processorName = tableSpec.TableName; + } + + // get processor instance + var tableMetadata = services.TableService.GetTableMetadata(processorName); + if (tableMetadata != null) { + return new FireAndForgetProcessorTable(services.TableService, tableMetadata); + } + else { + var nwprocessor = services.NamedWindowMgmtService.GetProcessor(processorName); + if (nwprocessor == null) { + throw new ExprValidationException("A table or named window by name '" + processorName + "' does not exist"); + } + return new FireAndForgetProcessorNamedWindow(nwprocessor); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/start/FireAndForgetProcessorNamedWindow.cs b/NEsper.Core/NEsper.Core/core/start/FireAndForgetProcessorNamedWindow.cs new file mode 100755 index 000000000..55f523e71 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/start/FireAndForgetProcessorNamedWindow.cs @@ -0,0 +1,96 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.named; + +namespace com.espertech.esper.core.start +{ + public class FireAndForgetProcessorNamedWindow : FireAndForgetProcessor + { + private readonly NamedWindowProcessor _namedWindowProcessor; + + internal FireAndForgetProcessorNamedWindow(NamedWindowProcessor namedWindowProcessor) + { + this._namedWindowProcessor = namedWindowProcessor; + } + + public NamedWindowProcessor NamedWindowProcessor + { + get { return _namedWindowProcessor; } + } + + public override EventType EventTypeResultSetProcessor + { + get { return _namedWindowProcessor.NamedWindowType; } + } + + public override EventType EventTypePublic + { + get { return _namedWindowProcessor.NamedWindowType; } + } + + public override string ContextName + { + get { return _namedWindowProcessor.ContextName; } + } + + public override FireAndForgetInstance GetProcessorInstance(AgentInstanceContext agentInstanceContext) + { + NamedWindowProcessorInstance processorInstance = _namedWindowProcessor.GetProcessorInstance(agentInstanceContext); + if (processorInstance != null) { + return new FireAndForgetInstanceNamedWindow(processorInstance); + } + return null; + } + + public override FireAndForgetInstance GetProcessorInstanceContextById(int agentInstanceId) + { + NamedWindowProcessorInstance processorInstance = _namedWindowProcessor.GetProcessorInstance(agentInstanceId); + if (processorInstance != null) { + return new FireAndForgetInstanceNamedWindow(processorInstance); + } + return null; + } + + public override FireAndForgetInstance GetProcessorInstanceNoContext() + { + NamedWindowProcessorInstance processorInstance = _namedWindowProcessor.ProcessorInstanceNoContext; + if (processorInstance == null) { + return null; + } + return new FireAndForgetInstanceNamedWindow(processorInstance); + } + + public override ICollection GetProcessorInstancesAll() + { + return _namedWindowProcessor.ProcessorInstancesAll; + } + + public override string NamedWindowOrTableName + { + get { return _namedWindowProcessor.NamedWindowName; } + } + + public override bool IsVirtualDataWindow + { + get { return _namedWindowProcessor.IsVirtualDataWindow; } + } + + public override string[][] GetUniqueIndexes(FireAndForgetInstance processorInstance) + { + if (processorInstance == null) { + return new string[0][]; + } + return _namedWindowProcessor.UniqueIndexes; + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/start/FireAndForgetProcessorTable.cs b/NEsper.Core/NEsper.Core/core/start/FireAndForgetProcessorTable.cs new file mode 100755 index 000000000..dcf5e7be3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/start/FireAndForgetProcessorTable.cs @@ -0,0 +1,90 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.table.mgmt; + +namespace com.espertech.esper.core.start +{ + public class FireAndForgetProcessorTable : FireAndForgetProcessor + { + private readonly TableService _tableService; + private readonly TableMetadata _tableMetadata; + + public FireAndForgetProcessorTable(TableService tableService, TableMetadata tableMetadata) + { + this._tableService = tableService; + this._tableMetadata = tableMetadata; + } + + public TableMetadata TableMetadata + { + get { return _tableMetadata; } + } + + public override EventType EventTypeResultSetProcessor + { + get { return _tableMetadata.InternalEventType; } + } + + public override EventType EventTypePublic + { + get { return _tableMetadata.PublicEventType; } + } + + public override string ContextName + { + get { return _tableMetadata.ContextName; } + } + + public override FireAndForgetInstance GetProcessorInstanceContextById(int agentInstanceId) + { + TableStateInstance instance = _tableService.GetState(_tableMetadata.TableName, agentInstanceId); + if (instance == null) { + return null; + } + return new FireAndForgetInstanceTable(instance); + } + + public override FireAndForgetInstance GetProcessorInstanceNoContext() + { + return GetProcessorInstanceContextById(-1); + } + + public override FireAndForgetInstance GetProcessorInstance(AgentInstanceContext agentInstanceContext) + { + return GetProcessorInstanceContextById(agentInstanceContext.AgentInstanceId); + } + + public override ICollection GetProcessorInstancesAll() + { + return _tableService.GetAgentInstanceIds(_tableMetadata.TableName); + } + + public override string NamedWindowOrTableName + { + get { return _tableMetadata.TableName; } + } + + public override bool IsVirtualDataWindow + { + get { return false; } + } + + public override string[][] GetUniqueIndexes(FireAndForgetInstance processorInstance) + { + return _tableMetadata.UniqueIndexes; + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/support/SupportEngineImportServiceFactory.cs b/NEsper.Core/NEsper.Core/core/support/SupportEngineImportServiceFactory.cs new file mode 100755 index 000000000..9153d4966 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/support/SupportEngineImportServiceFactory.cs @@ -0,0 +1,30 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.agg.factory; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.time; + +namespace com.espertech.esper.core.support +{ + public class SupportEngineImportServiceFactory + { + public static EngineImportServiceImpl Make() + { + return new EngineImportServiceImpl( + true, true, true, false, null, + TimeZoneInfo.Local, + TimeAbacusMilliseconds.INSTANCE, + ConfigurationEngineDefaults.ThreadingProfile.NORMAL, null, + AggregationFactoryFactoryDefault.INSTANCE); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/support/SupportEventAdapterService.cs b/NEsper.Core/NEsper.Core/core/support/SupportEventAdapterService.cs new file mode 100755 index 000000000..fce91b704 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/support/SupportEventAdapterService.cs @@ -0,0 +1,57 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client.util; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.events.avro; +using com.espertech.esper.events; +using com.espertech.esper.util; + +namespace com.espertech.esper.core.support +{ + public class SupportEventAdapterService + { + private static EventAdapterService _eventAdapterService; + + static SupportEventAdapterService() + { + _eventAdapterService = Allocate(); + } + + public static void Reset() + { + _eventAdapterService = Allocate(); + } + + public static EventAdapterService Service + { + get { return _eventAdapterService; } + } + + private static EventAdapterService Allocate() + { + EventAdapterAvroHandler avroHandler = EventAdapterAvroHandlerUnsupported.INSTANCE; + try + { + avroHandler = + TypeHelper.Instantiate( + EventAdapterAvroHandlerConstants.HANDLER_IMPL, ClassForNameProviderDefault.INSTANCE); + } + catch + { + } + + return new EventAdapterServiceImpl( + new EventTypeIdGeneratorImpl(), 5, avroHandler, SupportEngineImportServiceFactory.Make()); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/support/SupportSchedulingServiceImpl.cs b/NEsper.Core/NEsper.Core/core/support/SupportSchedulingServiceImpl.cs new file mode 100755 index 000000000..e4e196fe8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/support/SupportSchedulingServiceImpl.cs @@ -0,0 +1,119 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.service; +using com.espertech.esper.schedule; + +namespace com.espertech.esper.core.support +{ + public class SupportSchedulingServiceImpl : SchedulingService + { + private readonly IDictionary _added = new Dictionary(); + private long _currentTime; + + public IDictionary Added + { + get { return _added; } + } + + public void EvaluateLock() + { + } + + public void EvaluateUnLock() + { + } + + public void Add(long afterTime, ScheduleHandle callback, long slot) + { + Log.Debug(".add Not implemented, afterTime=" + afterTime + " callback=" + callback.GetType().Name); + _added.Put(afterTime, callback); + } + + public void Remove(ScheduleHandle callback, long scheduleSlot) + { + Log.Debug(".remove Not implemented, callback=" + callback.GetType().Name); + } + + public long Time + { + get + { + Log.Debug(".getTime Time is " + _currentTime); + return this._currentTime; + } + set + { + Log.Debug(".setTime Setting new time, currentTime=" + value); + this._currentTime = value; + } + } + + public void Evaluate(ICollection handles) + { + Log.Debug(".evaluate Not implemented"); + } + + public ScheduleBucket AllocateBucket() + { + return new ScheduleBucket(0); + } + + public static void EvaluateSchedule(SchedulingService service) + { + ICollection handles = new LinkedList(); + service.Evaluate(handles); + + foreach (var handle in handles) + { + if (handle is EPStatementHandleCallback) + { + var callback = (EPStatementHandleCallback)handle; + callback.ScheduleCallback.ScheduledTrigger(null); + } + else + { + var cb = (ScheduleHandleCallback)handle; + cb.ScheduledTrigger(null); + } + } + } + + public void Dispose() + { + } + + public int TimeHandleCount + { + get { throw new NotImplementedException(); } + } + + public long? FurthestTimeHandle + { + get { throw new NotImplementedException(); } + } + + public int ScheduleHandleCount + { + get { throw new NotImplementedException(); } + } + + public bool IsScheduled(ScheduleHandle scheduleHandle) + { + return false; + } + + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/support/SupportStatementContextFactory.cs b/NEsper.Core/NEsper.Core/core/support/SupportStatementContextFactory.cs new file mode 100755 index 000000000..5c2421f41 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/support/SupportStatementContextFactory.cs @@ -0,0 +1,120 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.core.service.multimatch; +using com.espertech.esper.core.thread; +using com.espertech.esper.epl.agg.factory; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.named; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.events.vaevent; +using com.espertech.esper.filter; +using com.espertech.esper.pattern; +using com.espertech.esper.schedule; +using com.espertech.esper.timer; +using com.espertech.esper.view; + +namespace com.espertech.esper.core.support +{ + public class SupportStatementContextFactory { + public static ExprEvaluatorContext MakeEvaluatorContext() { + return new ExprEvaluatorContextStatement(null, false); + } + + public static AgentInstanceContext MakeAgentInstanceContext(SchedulingService stub) { + return new AgentInstanceContext(MakeContext(stub), null, -1, null, null, null); + } + + public static AgentInstanceContext MakeAgentInstanceContext() { + return new AgentInstanceContext(MakeContext(), null, -1, null, null, null); + } + + public static AgentInstanceViewFactoryChainContext MakeAgentInstanceViewFactoryContext(SchedulingService stub) { + AgentInstanceContext agentInstanceContext = MakeAgentInstanceContext(stub); + return new AgentInstanceViewFactoryChainContext(agentInstanceContext, false, null, null); + } + + public static AgentInstanceViewFactoryChainContext MakeAgentInstanceViewFactoryContext() { + AgentInstanceContext agentInstanceContext = MakeAgentInstanceContext(); + return new AgentInstanceViewFactoryChainContext(agentInstanceContext, false, null, null); + } + + public static ViewFactoryContext MakeViewContext() { + StatementContext stmtContext = MakeContext(); + return new ViewFactoryContext(stmtContext, 1, "somenamespacetest", "somenametest", false, -1, false); + } + + public static StatementContext MakeContext() { + var sched = new SupportSchedulingServiceImpl(); + return MakeContext(sched); + } + + public static StatementContext MakeContext(int statementId) { + var sched = new SupportSchedulingServiceImpl(); + return MakeContext(statementId, sched); + } + + public static StatementContext MakeContext(SchedulingService stub) { + return MakeContext(1, stub); + } + + public static StatementContext MakeContext(int statementId, SchedulingService stub) { + var config = new Configuration(); + config.EngineDefaults.ViewResources.IsAllowMultipleExpiryPolicies = true; + + var timeSourceService = new TimeSourceServiceImpl(); + var stmtEngineServices = new StatementContextEngineServices("engURI", + SupportEventAdapterService.Service, + new NamedWindowMgmtServiceImpl(false, null), + null, new TableServiceImpl(), + new EngineSettingsService(new Configuration().EngineDefaults, new Uri[0]), + new ValueAddEventServiceImpl(), + config, + null, + null, + null, + null, + new StatementEventTypeRefImpl(), null, null, null, null, null, new ViewServicePreviousFactoryImpl(), null, new PatternNodeFactoryImpl(), new FilterBooleanExpressionFactoryImpl(), timeSourceService, SupportEngineImportServiceFactory.Make(), AggregationFactoryFactoryDefault.INSTANCE, new SchedulingServiceImpl(timeSourceService), null); + + return new StatementContext( + stmtEngineServices, + stub, + new ScheduleBucket(1), + new EPStatementHandle(statementId, "name1", "epl1", StatementType.SELECT, "epl1", false, null, 0, false, false, new MultiMatchHandlerFactoryImpl().GetDefaultHandler()), + new ViewResolutionServiceImpl(new PluggableObjectRegistryImpl(new PluggableObjectCollection[]{ViewEnumHelper.BuiltinViews}), null, null), + new PatternObjectResolutionServiceImpl(null), + null, + null, + null, + null, + new StatementResultServiceImpl("name", null, null, new ThreadingServiceImpl(new ConfigurationEngineDefaults.ThreadingConfig())), // statement result svc + null, + null, + null, + null, + null, + null, + null, + false, + null, + null, + AggregationServiceFactoryServiceImpl.DEFAULT_FACTORY, + null, + false, + null, new StatementSemiAnonymousTypeRegistryImpl(), 0); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/thread/InboundUnitSendAvro.cs b/NEsper.Core/NEsper.Core/core/thread/InboundUnitSendAvro.cs new file mode 100755 index 000000000..00d2315e5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/thread/InboundUnitSendAvro.cs @@ -0,0 +1,61 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.service; + +namespace com.espertech.esper.core.thread +{ + /// Inbound work unit processing a map event. + public class InboundUnitSendAvro + { + private static readonly ILog Log = + LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly Object _genericRecordDotData; + private readonly string _eventTypeName; + private readonly EPServicesContext _services; + private readonly EPRuntimeImpl _runtime; + + /// + /// Ctor. + /// + /// to send + /// type name + /// to wrap + /// to process + public InboundUnitSendAvro( + Object genericRecordDotData, + string eventTypeName, + EPServicesContext services, + EPRuntimeImpl runtime) + { + this._eventTypeName = eventTypeName; + this._genericRecordDotData = genericRecordDotData; + this._services = services; + this._runtime = runtime; + } + + public void Run() + { + try + { + EventBean eventBean = _services.EventAdapterService.AdapterForAvro( + _genericRecordDotData, _eventTypeName); + _runtime.ProcessWrappedEvent(eventBean); + } + catch (Exception e) + { + Log.Error("Unexpected error processing Object-array event: " + e.Message, e); + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/thread/InboundUnitSendDOM.cs b/NEsper.Core/NEsper.Core/core/thread/InboundUnitSendDOM.cs new file mode 100755 index 000000000..01b053014 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/thread/InboundUnitSendDOM.cs @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; +using System.Xml; +using com.espertech.esper.client; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.service; + +namespace com.espertech.esper.core.thread +{ + /// Inbound unit for DOM events. + public class InboundUnitSendDOM + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly XmlNode _event; + private readonly EPRuntimeImpl _runtime; + private readonly EPServicesContext _services; + + /// Ctor. + /// document + /// for wrapping event + /// runtime to process + public InboundUnitSendDOM(XmlNode theEvent, + EPServicesContext services, + EPRuntimeImpl runtime) + { + _event = theEvent; + _services = services; + _runtime = runtime; + } + + public void Run() + { + try + { + EventBean eventBean = _services.EventAdapterService.AdapterForDOM(_event); + _runtime.ProcessEvent(eventBean); + } + catch (Exception e) + { + Log.Error("Unexpected error processing DOM event: " + e.Message, e); + } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/thread/InboundUnitSendEvent.cs b/NEsper.Core/NEsper.Core/core/thread/InboundUnitSendEvent.cs new file mode 100755 index 000000000..6138bd563 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/thread/InboundUnitSendEvent.cs @@ -0,0 +1,44 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.service; + +namespace com.espertech.esper.core.thread +{ + /// Inbound unit for unwrapped events. + public class InboundUnitSendEvent + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + private readonly Object _event; + private readonly EPRuntimeImpl _runtime; + + /// Ctor. + /// to process + /// to process event + public InboundUnitSendEvent(Object theEvent, EPRuntimeImpl runtime) + { + _event = theEvent; + _runtime = runtime; + } + + public void Run() + { + try + { + _runtime.ProcessEvent(_event); + } + catch (Exception e) + { + Log.Error("Unexpected error processing unwrapped event: " + e.Message, e); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/thread/InboundUnitSendLINQ.cs b/NEsper.Core/NEsper.Core/core/thread/InboundUnitSendLINQ.cs new file mode 100755 index 000000000..f13295513 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/thread/InboundUnitSendLINQ.cs @@ -0,0 +1,56 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; +using System.Xml.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.service; + +namespace com.espertech.esper.core.thread +{ + /// + /// Inbound unit for LINQ XML events. + /// + public class InboundUnitSendLINQ + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly XElement _event; + private readonly EPRuntimeImpl _runtime; + private readonly EPServicesContext _services; + + /// Ctor. + /// document + /// for wrapping event + /// runtime to process + public InboundUnitSendLINQ(XElement theEvent, + EPServicesContext services, + EPRuntimeImpl runtime) + { + _event = theEvent; + _services = services; + _runtime = runtime; + } + + public void Run() + { + try + { + EventBean eventBean = _services.EventAdapterService.AdapterForDOM(_event); + _runtime.ProcessEvent(eventBean); + } + catch (Exception e) + { + Log.Error("Unexpected error processing DOM event: " + e.Message, e); + } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/thread/InboundUnitSendMap.cs b/NEsper.Core/NEsper.Core/core/thread/InboundUnitSendMap.cs new file mode 100755 index 000000000..59ccae5e6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/thread/InboundUnitSendMap.cs @@ -0,0 +1,59 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; +using com.espertech.esper.client; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.service; + +namespace com.espertech.esper.core.thread +{ + using DataMap = IDictionary; + + /// Inbound work unit processing a map event. + public class InboundUnitSendMap + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly String _eventTypeName; + private readonly DataMap _map; + private readonly EPRuntimeImpl _runtime; + private readonly EPServicesContext _services; + + /// Ctor. + /// to send + /// type name + /// to wrap + /// to process + public InboundUnitSendMap(DataMap map, + String eventTypeName, + EPServicesContext services, + EPRuntimeImpl runtime) + { + _eventTypeName = eventTypeName; + _map = map; + _services = services; + _runtime = runtime; + } + + public void Run() + { + try + { + EventBean eventBean = _services.EventAdapterService.AdapterForMap(_map, _eventTypeName); + _runtime.ProcessWrappedEvent(eventBean); + } + catch (Exception e) + { + Log.Error("Unexpected error processing Map event: " + e.Message, e); + } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/thread/InboundUnitSendObjectArray.cs b/NEsper.Core/NEsper.Core/core/thread/InboundUnitSendObjectArray.cs new file mode 100755 index 000000000..431a7ca6b --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/thread/InboundUnitSendObjectArray.cs @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.service; + +namespace com.espertech.esper.core.thread +{ + /// Inbound work unit processing a map event. + public class InboundUnitSendObjectArray + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly Object[] _properties; + private readonly String _eventTypeName; + private readonly EPServicesContext _services; + private readonly EPRuntimeImpl _runtime; + + /// Ctor. + /// to send + /// type name + /// to wrap + /// to process + public InboundUnitSendObjectArray(Object[] properties, String eventTypeName, EPServicesContext services, EPRuntimeImpl runtime) + { + _eventTypeName = eventTypeName; + _properties = properties; + _services = services; + _runtime = runtime; + } + + public void Run() + { + try + { + EventBean eventBean = _services.EventAdapterService.AdapterForObjectArray(_properties, _eventTypeName); + _runtime.ProcessWrappedEvent(eventBean); + } + catch (Exception e) + { + Log.Error("Unexpected error processing Object-array event: " + e.Message, e); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/thread/InboundUnitSendWrapped.cs b/NEsper.Core/NEsper.Core/core/thread/InboundUnitSendWrapped.cs new file mode 100755 index 000000000..11f7d2678 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/thread/InboundUnitSendWrapped.cs @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.service; + +namespace com.espertech.esper.core.thread +{ + /// + /// Inbound unit for wrapped events. + /// + public class InboundUnitSendWrapped + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly EventBean _eventBean; + private readonly EPRuntimeEventSender _runtime; + + /// + /// Ctor. + /// + /// inbound event, wrapped + /// to process + public InboundUnitSendWrapped(EventBean theEvent, EPRuntimeEventSender runtime) + { + _eventBean = theEvent; + _runtime = runtime; + } + + public void Run() + { + try + { + _runtime.ProcessWrappedEvent(_eventBean); + } + catch (Exception e) + { + Log.Error("Unexpected error processing wrapped event: " + e.Message, e); + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/thread/OutboundUnitRunnable.cs b/NEsper.Core/NEsper.Core/core/thread/OutboundUnitRunnable.cs new file mode 100755 index 000000000..5df7f25bf --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/thread/OutboundUnitRunnable.cs @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.service; + +namespace com.espertech.esper.core.thread +{ + /// Outbound unit. + public class OutboundUnitRunnable + { + private static readonly ILog Log = + LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly UniformPair _events; + private readonly StatementResultServiceImpl _statementResultService; + + /// + /// Ctor. + /// + /// to dispatch + /// handles result indicate + public OutboundUnitRunnable(UniformPair events, StatementResultServiceImpl statementResultService) + { + _events = events; + _statementResultService = statementResultService; + } + + public void Run() + { + try + { + _statementResultService.ProcessDispatch(_events); + } + catch (Exception e) + { + Log.Error("Unexpected error processing dispatch: " + e.Message, e); + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/thread/RouteUnitMultiple.cs b/NEsper.Core/NEsper.Core/core/thread/RouteUnitMultiple.cs new file mode 100755 index 000000000..1b8b754b5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/thread/RouteUnitMultiple.cs @@ -0,0 +1,71 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; + +namespace com.espertech.esper.core.thread +{ + /// Route execution work unit. + public class RouteUnitMultiple + { + private static readonly ILog Log = + LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly EPRuntimeImpl _epRuntime; + private readonly EventBean _theEvent; + private readonly long _filterVersion; + private readonly Object _callbackList; + private readonly EPStatementAgentInstanceHandle _handle; + + /// + /// Ctor. + /// + /// runtime to process + /// callback list + /// event to pass + /// statement handle + /// version of filter + public RouteUnitMultiple( + EPRuntimeImpl epRuntime, + Object callbackList, + EventBean theEvent, + EPStatementAgentInstanceHandle handle, + long filterVersion) + { + _epRuntime = epRuntime; + _callbackList = callbackList; + _theEvent = theEvent; + _handle = handle; + _filterVersion = filterVersion; + } + + public void Run() + { + try + { + _epRuntime.ProcessStatementFilterMultiple(_handle, _callbackList, _theEvent, _filterVersion); + + _epRuntime.Dispatch(); + + _epRuntime.ProcessThreadWorkQueue(); + } + catch (Exception e) + { + Log.Error("Unexpected error processing multiple route execution: " + e.Message, e); + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/thread/RouteUnitSingle.cs b/NEsper.Core/NEsper.Core/core/thread/RouteUnitSingle.cs new file mode 100755 index 000000000..c1f9921a7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/thread/RouteUnitSingle.cs @@ -0,0 +1,64 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.service; + +namespace com.espertech.esper.core.thread +{ + /// Route unit for single match. + public class RouteUnitSingle + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly EPRuntimeImpl _epRuntime; + private readonly EventBean _theEvent; + private readonly long _filterVersion; + private readonly EPStatementHandleCallback _handleCallback; + + /// + /// Ctor. + /// + /// runtime to process + /// callback + /// event + /// version of filter + public RouteUnitSingle( + EPRuntimeImpl epRuntime, + EPStatementHandleCallback handleCallback, + EventBean theEvent, + long filterVersion) + { + _epRuntime = epRuntime; + _theEvent = theEvent; + _handleCallback = handleCallback; + _filterVersion = filterVersion; + } + + public void Run() + { + try + { + _epRuntime.ProcessStatementFilterSingle( + _handleCallback.AgentInstanceHandle, _handleCallback, _theEvent, _filterVersion); + + _epRuntime.Dispatch(); + + _epRuntime.ProcessThreadWorkQueue(); + } + catch (Exception e) + { + Log.Error("Unexpected error processing route execution: " + e.Message, e); + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/core/thread/ThreadingOption.cs b/NEsper.Core/NEsper.Core/core/thread/ThreadingOption.cs new file mode 100755 index 000000000..2911b7508 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/thread/ThreadingOption.cs @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.core.thread +{ + /// Ctor + public class ThreadingOption + { + internal static bool IsThreadingEnabledValue; + + static ThreadingOption() + { + IsThreadingEnabled = false; + } + + /// Returns true when threading is enabled + /// indicator + public static bool IsThreadingEnabled + { + get { return IsThreadingEnabledValue; } + set { IsThreadingEnabledValue = value; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/thread/ThreadingService.cs b/NEsper.Core/NEsper.Core/core/thread/ThreadingService.cs new file mode 100755 index 000000000..25e7d645b --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/thread/ThreadingService.cs @@ -0,0 +1,92 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Threading; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.threading; +using com.espertech.esper.core.service; + +namespace com.espertech.esper.core.thread +{ + /// Engine-level threading services. + public interface ThreadingService : IDisposable + { + /// Initialize thread pools. + /// engine-level service context + /// runtime + void InitThreading(EPServicesContext services, EPRuntimeImpl runtime); + + /// Returns true for timer execution threading enabled. + /// indicator + bool IsTimerThreading { get; } + + /// Submit timer execution work unit. + /// unit of work + void SubmitTimerWork(Runnable timerUnit); + + /// Returns true for inbound threading enabled. + /// indicator + bool IsInboundThreading { get; } + + /// Submit inbound work unit. + /// unit of work + void SubmitInbound(Runnable unit); + + /// Returns true for route execution threading enabled. + /// indicator + bool IsRouteThreading { get; } + + /// Submit route work unit. + /// unit of work + void SubmitRoute(Runnable unit); + + /// Returns true for outbound threading enabled. + /// indicator + bool IsOutboundThreading { get; } + + /// Submit outbound work unit. + /// unit of work + void SubmitOutbound(Runnable unit); + + /// Returns the outbound queue. + /// queue + IBlockingQueue OutboundQueue { get; } + + /// Returns the outbound thread pool + /// thread pool + IExecutorService OutboundThreadPool { get; } + + /// Returns the route queue. + /// queue + IBlockingQueue RouteQueue { get; } + + /// Returns the route thread pool + /// thread pool + IExecutorService RouteThreadPool { get; } + + /// Returns the timer queue. + /// queue + IBlockingQueue TimerQueue { get; } + + /// Returns the timer thread pool + /// thread pool + IExecutorService TimerThreadPool { get; } + + /// Returns the inbound queue. + /// queue + IBlockingQueue InboundQueue { get; } + + /// Returns the inbound thread pool + /// thread pool + IExecutorService InboundThreadPool { get; } + + Thread MakeEventSourceThread(String engineURI, String sourceName, Runnable runnable); + } +} diff --git a/NEsper.Core/NEsper.Core/core/thread/ThreadingServiceImpl.cs b/NEsper.Core/NEsper.Core/core/thread/ThreadingServiceImpl.cs new file mode 100755 index 000000000..c30592429 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/thread/ThreadingServiceImpl.cs @@ -0,0 +1,258 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Threading; +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.compat.threading; +using com.espertech.esper.core.service; + +namespace com.espertech.esper.core.thread +{ + /// + /// Implementation for engine-level threading. + /// + public class ThreadingServiceImpl : ThreadingService + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly ConfigurationEngineDefaults.ThreadingConfig _config; + private readonly bool _isTimerThreading; + private readonly bool _isInboundThreading; + private readonly bool _isRouteThreading; + private readonly bool _isOutboundThreading; + + private IBlockingQueue _timerQueue; + private IBlockingQueue _inboundQueue; + private IBlockingQueue _routeQueue; + private IBlockingQueue _outboundQueue; + + private IExecutorService _timerThreadPool; + private IExecutorService _inboundThreadPool; + private IExecutorService _routeThreadPool; + private IExecutorService _outboundThreadPool; + + /// Ctor. + /// configuration + public ThreadingServiceImpl(ConfigurationEngineDefaults.ThreadingConfig threadingConfig) + { + _config = threadingConfig; + if (ThreadingOption.IsThreadingEnabled) + { + _isTimerThreading = threadingConfig.IsThreadPoolTimerExec; + _isInboundThreading = threadingConfig.IsThreadPoolInbound; + _isRouteThreading = threadingConfig.IsThreadPoolRouteExec; + _isOutboundThreading = threadingConfig.IsThreadPoolOutbound; + } + else + { + _isTimerThreading = false; + _isInboundThreading = false; + _isRouteThreading = false; + _isOutboundThreading = false; + } + } + + public bool IsRouteThreading + { + get { return _isRouteThreading; } + } + + public bool IsInboundThreading + { + get { return _isInboundThreading; } + } + + public bool IsTimerThreading + { + get { return _isTimerThreading; } + } + + public bool IsOutboundThreading + { + get { return _isOutboundThreading; } + } + + public void InitThreading(EPServicesContext services, EPRuntimeImpl runtime) + { + if (_isInboundThreading) + { + _inboundQueue = MakeQueue(_config.ThreadPoolInboundCapacity, _config.ThreadPoolInboundBlocking); + _inboundThreadPool = GetThreadPool(services.EngineURI, "Inbound", _inboundQueue, _config.ThreadPoolInboundNumThreads); + } + + if (_isTimerThreading) + { + _timerQueue = MakeQueue(_config.ThreadPoolTimerExecCapacity, _config.ThreadPoolTimerExecBlocking); + _timerThreadPool = GetThreadPool(services.EngineURI, "TimerExec", _timerQueue, _config.ThreadPoolTimerExecNumThreads); + } + + if (_isRouteThreading) + { + _routeQueue = MakeQueue(_config.ThreadPoolRouteExecCapacity, _config.ThreadPoolRouteExecBlocking); + _routeThreadPool = GetThreadPool(services.EngineURI, "RouteExec", _routeQueue, _config.ThreadPoolRouteExecNumThreads); + } + + if (_isOutboundThreading) + { + _outboundQueue = MakeQueue(_config.ThreadPoolOutboundCapacity, _config.ThreadPoolOutboundBlocking); + _outboundThreadPool = GetThreadPool(services.EngineURI, "Outbound", _outboundQueue, _config.ThreadPoolOutboundNumThreads); + } + } + + private static IBlockingQueue MakeQueue(int? threadPoolTimerExecCapacity, ConfigurationEngineDefaults.ThreadingConfig.Locking blocking) + { + if ((threadPoolTimerExecCapacity == null) || + (threadPoolTimerExecCapacity <= 0) || + (threadPoolTimerExecCapacity == int.MaxValue)) + { + return blocking == ConfigurationEngineDefaults.ThreadingConfig.Locking.SPIN + ? (IBlockingQueue)new ImperfectBlockingQueue() + : (IBlockingQueue)new LinkedBlockingQueue(); + } + + return blocking == ConfigurationEngineDefaults.ThreadingConfig.Locking.SPIN + ? (IBlockingQueue)new ImperfectBlockingQueue(threadPoolTimerExecCapacity.Value) + : (IBlockingQueue)new BoundBlockingQueue(threadPoolTimerExecCapacity.Value); + } + + /// Submit route work unit. + /// unit of work + public void SubmitRoute(Runnable unit) + { + _routeQueue.Push(unit); + } + + /// Submit inbound work unit. + /// unit of work + public void SubmitInbound(Runnable unit) + { + _inboundQueue.Push(unit); + } + + public void SubmitOutbound(Runnable unit) + { + _outboundQueue.Push(unit); + } + + public void SubmitTimerWork(Runnable unit) + { + _timerQueue.Push(unit); + } + + public IBlockingQueue OutboundQueue + { + get { return _outboundQueue; } + } + + public IExecutorService OutboundThreadPool + { + get { return _outboundThreadPool; } + } + + public IBlockingQueue RouteQueue + { + get { return _routeQueue; } + } + + public IExecutorService RouteThreadPool + { + get { return _routeThreadPool; } + } + + public IBlockingQueue TimerQueue + { + get { return _timerQueue; } + } + + public IExecutorService TimerThreadPool + { + get { return _timerThreadPool; } + } + + public IBlockingQueue InboundQueue + { + get { return _inboundQueue; } + } + + public IExecutorService InboundThreadPool + { + get { return _inboundThreadPool; } + } + + public void Dispose() + { + lock (this) + { + if (_timerThreadPool != null) + { + StopPool(_timerThreadPool, _timerQueue, "TimerExec"); + } + if (_routeThreadPool != null) + { + StopPool(_routeThreadPool, _routeQueue, "RouteExec"); + } + if (_outboundThreadPool != null) + { + StopPool(_outboundThreadPool, _outboundQueue, "Outbound"); + } + if (_inboundThreadPool != null) + { + StopPool(_inboundThreadPool, _inboundQueue, "Inbound"); + } + + _timerThreadPool = null; + _routeThreadPool = null; + _outboundThreadPool = null; + _inboundThreadPool = null; + } + } + + private static IExecutorService GetThreadPool(String engineURI, String name, IBlockingQueue queue, int numThreads) + { + if (Log.IsInfoEnabled) + { + Log.Info("Starting pool " + name + " with " + numThreads + " threads"); + } + + if (engineURI == null) + { + engineURI = "default"; + } + + return new DedicatedExecutorService(name, numThreads, queue); + } + + public Thread MakeEventSourceThread(String engineURI, String sourceName, Runnable runnable) + { + if (engineURI == null) + { + engineURI = "default"; + } + + var threadGroupName = "com.espertech.esper." + engineURI + "-source-" + sourceName; + var thread = new Thread(() => runnable()); + return thread; + } + + private static void StopPool(IExecutorService executorService, IBlockingQueue queue, String name) + { + if (Log.IsInfoEnabled) + { + Log.Info("Shutting down pool " + name); + } + + queue.Clear(); + executorService.Shutdown(); + executorService.AwaitTermination(new TimeSpan(0, 0, 10)); + } + } +} diff --git a/NEsper.Core/NEsper.Core/core/thread/TimerUnitMultiple.cs b/NEsper.Core/NEsper.Core/core/thread/TimerUnitMultiple.cs new file mode 100755 index 000000000..3f3f4cf01 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/thread/TimerUnitMultiple.cs @@ -0,0 +1,62 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.core.thread +{ + /// + /// Timer unit for multiple callbacks for a statement. + /// + public class TimerUnitMultiple + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly Object _callbackObject; + private readonly EPStatementAgentInstanceHandle _handle; + private readonly EPRuntimeImpl _runtime; + private readonly EPServicesContext _services; + + /// Ctor. + /// engine services + /// runtime to process + /// statement handle + /// callback list + public TimerUnitMultiple(EPServicesContext services, EPRuntimeImpl runtime, EPStatementAgentInstanceHandle handle, object callbackObject) + { + _services = services; + _handle = handle; + _runtime = runtime; + _callbackObject = callbackObject; + } + + public void Run() + { + try + { + EPRuntimeImpl.ProcessStatementScheduleMultiple(_handle, _callbackObject, _services); + + // Let listeners know of results + _runtime.Dispatch(); + + // Work off the event queue if any events accumulated in there via a Route() + _runtime.ProcessThreadWorkQueue(); + } + catch (Exception e) + { + Log.Error("Unexpected error processing multiple timer execution: " + e.Message, e); + } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/core/thread/TimerUnitSingle.cs b/NEsper.Core/NEsper.Core/core/thread/TimerUnitSingle.cs new file mode 100755 index 000000000..7052973f7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/core/thread/TimerUnitSingle.cs @@ -0,0 +1,56 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.core.thread +{ + /// + /// Timer unit for a single callback for a statement. + /// + public class TimerUnitSingle + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly EPStatementHandleCallback _handleCallback; + private readonly EPRuntimeImpl _runtime; + private readonly EPServicesContext _services; + + /// Ctor. + /// engine services + /// runtime to process + /// callback + public TimerUnitSingle(EPServicesContext services, EPRuntimeImpl runtime, EPStatementHandleCallback handleCallback) + { + _services = services; + _runtime = runtime; + _handleCallback = handleCallback; + } + + public void Run() + { + try + { + EPRuntimeImpl.ProcessStatementScheduleSingle(_handleCallback, _services); + + _runtime.Dispatch(); + + _runtime.ProcessThreadWorkQueue(); + } + catch (Exception e) + { + Log.Error("Unexpected error processing timer execution: " + e.Message, e); + } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/dataflow/annotations/DataFlowContextAttribute.cs b/NEsper.Core/NEsper.Core/dataflow/annotations/DataFlowContextAttribute.cs new file mode 100755 index 000000000..c72014637 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/annotations/DataFlowContextAttribute.cs @@ -0,0 +1,17 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.dataflow.annotations +{ + [AttributeUsage(AttributeTargets.Field)] + public class DataFlowContextAttribute : Attribute + { + } +} diff --git a/NEsper.Core/NEsper.Core/dataflow/annotations/DataFlowOpParameterAttribute.cs b/NEsper.Core/NEsper.Core/dataflow/annotations/DataFlowOpParameterAttribute.cs new file mode 100755 index 000000000..7f8734b9e --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/annotations/DataFlowOpParameterAttribute.cs @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.dataflow.annotations +{ + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Method)] + public class DataFlowOpParameterAttribute : Attribute + { + public String Name { get; set; } + public bool All { get; set; } + + public DataFlowOpParameterAttribute() + { + Name = string.Empty; + All = false; + } + } +} diff --git a/NEsper.Core/NEsper.Core/dataflow/annotations/DataFlowOpPropertyHolderAttribute.cs b/NEsper.Core/NEsper.Core/dataflow/annotations/DataFlowOpPropertyHolderAttribute.cs new file mode 100755 index 000000000..2a7cf61cd --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/annotations/DataFlowOpPropertyHolderAttribute.cs @@ -0,0 +1,17 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.dataflow.annotations +{ + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Method)] + public class DataFlowOpPropertyHolderAttribute : Attribute + { + } +} diff --git a/NEsper.Core/NEsper.Core/dataflow/annotations/DataFlowOpProvideSignalAttribute.cs b/NEsper.Core/NEsper.Core/dataflow/annotations/DataFlowOpProvideSignalAttribute.cs new file mode 100755 index 000000000..fca74aea8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/annotations/DataFlowOpProvideSignalAttribute.cs @@ -0,0 +1,17 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.dataflow.annotations +{ + [AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Struct)] + public class DataFlowOpProvideSignalAttribute : Attribute + { + } +} diff --git a/NEsper.Core/NEsper.Core/dataflow/annotations/DataFlowOperatorAttribute.cs b/NEsper.Core/NEsper.Core/dataflow/annotations/DataFlowOperatorAttribute.cs new file mode 100755 index 000000000..22c80fb74 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/annotations/DataFlowOperatorAttribute.cs @@ -0,0 +1,17 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.dataflow.annotations +{ + [AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Struct)] + public class DataFlowOperatorAttribute : Attribute + { + } +} diff --git a/NEsper.Core/NEsper.Core/dataflow/annotations/OutputTypeAttribute.cs b/NEsper.Core/NEsper.Core/dataflow/annotations/OutputTypeAttribute.cs new file mode 100755 index 000000000..e72895609 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/annotations/OutputTypeAttribute.cs @@ -0,0 +1,91 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.dataflow.annotations +{ + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + public class OutputTypeAttribute : Attribute + { + public String Name { get; set; } + public Type Type { get; set; } + public String TypeName { get; set; } + public int Port { get; set; } + + public OutputTypeAttribute(string name) + { + Name = name; + } + + public OutputTypeAttribute(string name, Type type) + { + Name = name; + Type = type; + } + + public OutputTypeAttribute() + { + Name = string.Empty; + TypeName = string.Empty; + Type = typeof(OutputTypeAttribute); + } + + public override object TypeId + { + get + { + return this; + } + } + + public bool Equals(OutputTypeAttribute other) + { + if (ReferenceEquals(null, other)) + return false; + if (ReferenceEquals(this, other)) + return true; + return base.Equals(other) && Equals(other.Name, Name) && Equals(other.Type, Type) && Equals(other.TypeName, TypeName); + } + + /// + /// Returns a value that indicates whether this instance is equal to a specified object. + /// + /// + /// true if equals the type and value of this instance; otherwise, false. + /// + /// An to compare with this instance or null. 2 + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) + return false; + if (ReferenceEquals(this, obj)) + return true; + return Equals(obj as OutputTypeAttribute); + } + + /// + /// Returns the hash code for this instance. + /// + /// + /// A 32-bit signed integer hash code. + /// + /// 2 + public override int GetHashCode() + { + unchecked + { + int result = base.GetHashCode(); + result = (result*397) ^ (Name != null ? Name.GetHashCode() : 0); + result = (result*397) ^ (Type != null ? Type.GetHashCode() : 0); + result = (result*397) ^ (TypeName != null ? TypeName.GetHashCode() : 0); + return result; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/dataflow/core/DataFlowConfigurationStateService.cs b/NEsper.Core/NEsper.Core/dataflow/core/DataFlowConfigurationStateService.cs new file mode 100755 index 000000000..7c6b1a53a --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/core/DataFlowConfigurationStateService.cs @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client.dataflow; + +namespace com.espertech.esper.dataflow.core +{ + public interface DataFlowConfigurationStateService + { + bool Exists(String savedConfigName); + void Add(EPDataFlowSavedConfiguration epDataFlowSavedConfiguration); + string[] SavedConfigNames { get; } + EPDataFlowSavedConfiguration GetSavedConfig(String savedConfigName); + Object RemovePrototype(String savedConfigName); + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/dataflow/core/DataFlowConfigurationStateServiceImpl.cs b/NEsper.Core/NEsper.Core/dataflow/core/DataFlowConfigurationStateServiceImpl.cs new file mode 100755 index 000000000..b0d11a25f --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/core/DataFlowConfigurationStateServiceImpl.cs @@ -0,0 +1,52 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client.dataflow; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.dataflow.core +{ + public class DataFlowConfigurationStateServiceImpl : DataFlowConfigurationStateService + { + private readonly IDictionary _savedConfigs = + new Dictionary(); + + public bool Exists(String savedConfigName) + { + return _savedConfigs.ContainsKey(savedConfigName); + } + + public void Add(EPDataFlowSavedConfiguration savedConfiguration) + { + _savedConfigs.Put(savedConfiguration.SavedConfigurationName, savedConfiguration); + } + + public string[] SavedConfigNames + { + get + { + ICollection names = _savedConfigs.Keys; + return names.ToArray(); + } + } + + public EPDataFlowSavedConfiguration GetSavedConfig(String savedConfigName) + { + return _savedConfigs.Get(savedConfigName); + } + + public object RemovePrototype(String savedConfigName) + { + return _savedConfigs.Delete(savedConfigName); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/dataflow/core/DataFlowService.cs b/NEsper.Core/NEsper.Core/dataflow/core/DataFlowService.cs new file mode 100755 index 000000000..8a60a7014 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/core/DataFlowService.cs @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client.dataflow; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.dataflow.core +{ + public interface DataFlowService : EPDataFlowRuntime, IDisposable + { + void AddStartGraph(CreateDataFlowDesc desc, + StatementContext statementContext, + EPServicesContext servicesContext, + AgentInstanceContext agentInstanceContext, + bool newStatement); + + void RemoveGraph(String graphName); + void StopGraph(String graphName); + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/dataflow/core/DataFlowServiceEntry.cs b/NEsper.Core/NEsper.Core/dataflow/core/DataFlowServiceEntry.cs new file mode 100755 index 000000000..feb3eceb8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/core/DataFlowServiceEntry.cs @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; + +namespace com.espertech.esper.dataflow.core +{ + public class DataFlowServiceEntry + { + public DataFlowServiceEntry(DataFlowStmtDesc dataFlowDesc, EPStatementState state) + { + DataFlowDesc = dataFlowDesc; + State = state; + } + + public DataFlowStmtDesc DataFlowDesc { get; private set; } + + public EPStatementState State { get; set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/dataflow/core/DataFlowServiceImpl.cs b/NEsper.Core/NEsper.Core/dataflow/core/DataFlowServiceImpl.cs new file mode 100755 index 000000000..9bd82adfa --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/core/DataFlowServiceImpl.cs @@ -0,0 +1,1345 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.client.annotation; +using com.espertech.esper.client.dataflow; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.compat.threading; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.dataflow.annotations; +using com.espertech.esper.dataflow.interfaces; +using com.espertech.esper.dataflow.runnables; +using com.espertech.esper.dataflow.util; +using com.espertech.esper.epl.annotation; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; +using com.espertech.esper.events; +using com.espertech.esper.events.arr; +using com.espertech.esper.util; + +namespace com.espertech.esper.dataflow.core +{ + public class DataFlowServiceImpl : DataFlowService + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private const String EVENT_WRAPPED_TYPE = "eventbean"; + + private readonly IDictionary _graphs = new Dictionary(); + private readonly IDictionary _instances = new Dictionary(); + + private readonly EPServiceProvider _epService; + private readonly DataFlowConfigurationStateService _configurationState; + + private readonly ILockable _iLock = LockManager.CreateLock(MethodBase.GetCurrentMethod().DeclaringType); + + public DataFlowServiceImpl(EPServiceProvider epService, DataFlowConfigurationStateService configurationState) + { + _epService = epService; + _configurationState = configurationState; + } + + public EPDataFlowDescriptor GetDataFlow(String dataFlowName) + { + using (_iLock.Acquire()) + { + var entry = _graphs.Get(dataFlowName); + if (entry == null) + { + return null; + } + + return new EPDataFlowDescriptor(dataFlowName, entry.State, entry.DataFlowDesc.StatementContext.StatementName); + } + } + + public String[] GetDataFlows() + { + using (_iLock.Acquire()) + { + return _graphs.Keys.ToArray(); + } + } + + public void AddStartGraph(CreateDataFlowDesc desc, StatementContext statementContext, EPServicesContext servicesContext, AgentInstanceContext agentInstanceContext, bool newStatement) + { + using (_iLock.Acquire()) + { + CompileTimeValidate(desc); + + var existing = _graphs.Get(desc.GraphName); + if (existing != null && (existing.State == EPStatementState.STARTED || newStatement)) + { + throw new ExprValidationException( + "Data flow by name '" + desc.GraphName + "' has already been declared"); + } + if (existing != null) + { + existing.State = EPStatementState.STARTED; + return; + } + + // compile annotations + var operatorAnnotations = new Dictionary(); + foreach (GraphOperatorSpec spec in desc.Operators) + { + Attribute[] operatorAnnotation = AnnotationUtil.CompileAnnotations( + spec.Annotations, servicesContext.EngineImportService, null); + operatorAnnotations.Put(spec, operatorAnnotation); + } + + var stmtDesc = new DataFlowStmtDesc( + desc, statementContext, servicesContext, agentInstanceContext, operatorAnnotations); + _graphs.Put(desc.GraphName, new DataFlowServiceEntry(stmtDesc, EPStatementState.STARTED)); + } + } + + public void StopGraph(String graphName) + { + using (_iLock.Acquire()) + { + var existing = _graphs.Get(graphName); + if (existing != null && existing.State == EPStatementState.STARTED) + { + existing.State = EPStatementState.STOPPED; + } + } + } + + public void RemoveGraph(String graphName) + { + using (_iLock.Acquire()) + { + _graphs.Remove(graphName); + } + } + + public EPDataFlowInstance Instantiate(String dataFlowName) + { + return Instantiate(dataFlowName, null); + } + + public EPDataFlowInstance Instantiate(String dataFlowName, EPDataFlowInstantiationOptions options) + { + using (_iLock.Acquire()) + { + var serviceDesc = _graphs.Get(dataFlowName); + if (serviceDesc == null) + { + throw new EPDataFlowInstantiationException( + "Data flow by name '" + dataFlowName + "' has not been defined"); + } + if (serviceDesc.State != EPStatementState.STARTED) + { + throw new EPDataFlowInstantiationException( + "Data flow by name '" + dataFlowName + "' is currently in STOPPED statement state"); + } + DataFlowStmtDesc stmtDesc = serviceDesc.DataFlowDesc; + try + { + return InstantiateInternal( + dataFlowName, options, stmtDesc.GraphDesc, stmtDesc.StatementContext, stmtDesc.ServicesContext, + stmtDesc.AgentInstanceContext, stmtDesc.OperatorAnnotations); + } + catch (Exception ex) + { + String message = "Failed to instantiate data flow '" + dataFlowName + "': " + ex.Message; + Log.Debug(message, ex); + throw new EPDataFlowInstantiationException(message, ex); + } + } + } + + public void Dispose() + { + using (_iLock.Acquire()) + { + _graphs.Clear(); + } + } + + public void SaveConfiguration(String dataflowConfigName, String dataFlowName, EPDataFlowInstantiationOptions options) + { + using (_iLock.Acquire()) + { + var dataFlow = _graphs.Get(dataFlowName); + if (dataFlow == null) + { + String message = "Failed to locate data flow '" + dataFlowName + "'"; + throw new EPDataFlowNotFoundException(message); + } + if (_configurationState.Exists(dataflowConfigName)) + { + String message = "Data flow saved configuration by name '" + dataflowConfigName + "' already exists"; + throw new EPDataFlowAlreadyExistsException(message); + } + _configurationState.Add(new EPDataFlowSavedConfiguration(dataflowConfigName, dataFlowName, options)); + } + } + + public string[] SavedConfigurations + { + get + { + using (_iLock.Acquire()) + { + return _configurationState.SavedConfigNames; + } + } + } + + public EPDataFlowSavedConfiguration GetSavedConfiguration(String configurationName) + { + using (_iLock.Acquire()) + { + return _configurationState.GetSavedConfig(configurationName); + } + } + + public EPDataFlowInstance InstantiateSavedConfiguration(String configurationName) + { + using (_iLock.Acquire()) + { + var savedConfiguration = _configurationState.GetSavedConfig(configurationName); + if (savedConfiguration == null) + { + throw new EPDataFlowInstantiationException( + "Dataflow saved configuration '" + configurationName + "' could not be found"); + } + var options = savedConfiguration.Options; + if (options == null) + { + options = new EPDataFlowInstantiationOptions(); + options.DataFlowInstanceId(configurationName); + } + return Instantiate(savedConfiguration.DataflowName, options); + } + } + + public bool RemoveSavedConfiguration(String configurationName) + { + using (_iLock.Acquire()) + { + return _configurationState.RemovePrototype(configurationName) != null; + } + } + + public void SaveInstance(String instanceName, EPDataFlowInstance instance) + { + using (_iLock.Acquire()) + { + if (_instances.ContainsKey(instanceName)) + { + throw new EPDataFlowAlreadyExistsException( + "Data flow instance name '" + instanceName + "' already saved"); + } + _instances.Put(instanceName, instance); + } + } + + public string[] SavedInstances + { + get + { + using (_iLock.Acquire()) + { + ICollection instanceids = _instances.Keys; + return instanceids.ToArray(); + } + } + } + + public EPDataFlowInstance GetSavedInstance(String instanceName) + { + using (_iLock.Acquire()) + { + return _instances.Get(instanceName); + } + } + + public bool RemoveSavedInstance(String instanceName) + { + using (_iLock.Acquire()) + { + return _instances.Remove(instanceName); // != null; + } + } + + private EPDataFlowInstance InstantiateInternal(String dataFlowName, + EPDataFlowInstantiationOptions options, + CreateDataFlowDesc desc, + StatementContext statementContext, + EPServicesContext servicesContext, + AgentInstanceContext agentInstanceContext, + IDictionary operatorAnnotations) + { + if (options == null) + { + options = new EPDataFlowInstantiationOptions(); + } + + // + // Building a model. + // + + // resolve types + IDictionary declaredTypes = ResolveTypes(desc, statementContext, servicesContext); + + // resolve operator classes + IDictionary operatorMetadata = ResolveMetadata(desc, options, servicesContext.EngineImportService, operatorAnnotations); + + // build dependency graph: operator -> [input_providing_op, input_providing_op] + IDictionary operatorDependencies = AnalyzeDependencies(desc); + + // determine build order of operators + ICollection operatorBuildOrder = AnalyzeBuildOrder(operatorDependencies); + + // assure variables + servicesContext.VariableService.SetLocalVersion(); + + // instantiate operators + IDictionary operators = InstantiateOperators(operatorMetadata, desc, options, statementContext); + + // Build graph that references port numbers (port number is simply the method offset number or to-be-generated slot in the list) + var runtimeEventSender = (EPRuntimeEventSender)_epService.EPRuntime; + var operatorChannels = DetermineChannels(dataFlowName, operatorBuildOrder, operatorDependencies, operators, declaredTypes, operatorMetadata, options, servicesContext.EventAdapterService, servicesContext.EngineImportService, statementContext, servicesContext, agentInstanceContext, runtimeEventSender); + if (Log.IsDebugEnabled) + { + Log.Debug("For flow '" + dataFlowName + "' channels are: " + LogicalChannelUtil.PrintChannels(operatorChannels)); + } + + // + // Build the realization. + // + + // Determine binding of each channel to input methods (ports) + var operatorChannelBindings = new List(); + foreach (LogicalChannel channel in operatorChannels) + { + Type targetClass = operators.Get(channel.ConsumingOpNum).GetType(); + LogicalChannelBindingMethodDesc consumingMethod = FindMatchingMethod(channel.ConsumingOpPrettyPrint, targetClass, channel, false); + LogicalChannelBindingMethodDesc onSignalMethod = null; + if (channel.OutputPort.HasPunctuation) + { + onSignalMethod = FindMatchingMethod(channel.ConsumingOpPrettyPrint, targetClass, channel, true); + } + operatorChannelBindings.Add(new LogicalChannelBinding(channel, consumingMethod, onSignalMethod)); + } + + // Obtain realization + var dataFlowSignalManager = new DataFlowSignalManager(); + var startDesc = RealizationFactoryInterface.Realize( + dataFlowName, operators, operatorMetadata, operatorBuildOrder, operatorChannelBindings, + dataFlowSignalManager, options, servicesContext, statementContext); + + // For each GraphSource add runnable + var sourceRunnables = new List(); + var audit = AuditEnum.DATAFLOW_SOURCE.GetAudit(statementContext.Annotations) != null; + foreach (var operatorEntry in operators) + { + if (!(operatorEntry.Value is DataFlowSourceOperator)) + { + continue; + } + var meta = operatorMetadata.Get(operatorEntry.Key); + var graphSource = (DataFlowSourceOperator)operatorEntry.Value; + var runnable = new GraphSourceRunnable(statementContext.EngineURI, statementContext.StatementName, graphSource, dataFlowName, meta.OperatorName, operatorEntry.Key, meta.OperatorPrettyPrint, options.GetExceptionHandler(), audit); + sourceRunnables.Add(runnable); + + dataFlowSignalManager.AddSignalListener(operatorEntry.Key, runnable); + } + + bool auditStates = AuditEnum.DATAFLOW_TRANSITION.GetAudit(statementContext.Annotations) != null; + return new EPDataFlowInstanceImpl( + servicesContext.EngineURI, statementContext.StatementName, auditStates, dataFlowName, + options.GetDataFlowInstanceUserObject(), options.GetDataFlowInstanceId(), EPDataFlowState.INSTANTIATED, + sourceRunnables, operators, operatorBuildOrder, startDesc.StatisticsProvider, options.ParametersURIs, + statementContext.EngineImportService); + } + + private static IDictionary ResolveTypes( + CreateDataFlowDesc desc, + StatementContext statementContext, + EPServicesContext servicesContext) + { + var types = new Dictionary(); + foreach (CreateSchemaDesc spec in desc.Schemas) + { + EventType eventType = EventTypeUtility.CreateNonVariantType( + true, spec, statementContext.Annotations, statementContext.ConfigSnapshot, + statementContext.EventAdapterService, servicesContext.EngineImportService); + types.Put(spec.SchemaName, eventType); + } + return types; + } + + private IDictionary InstantiateOperators( + IDictionary operatorClasses, + CreateDataFlowDesc desc, + EPDataFlowInstantiationOptions options, + StatementContext statementContext) + { + var operators = new Dictionary(); + var exprValidationContext = ExprNodeUtility.GetExprValidationContextStatementOnly(statementContext); + + foreach (var operatorEntry in operatorClasses) { + var @operator = InstantiateOperator(desc.GraphName, operatorEntry.Key, operatorEntry.Value, desc.Operators[operatorEntry.Key], options, exprValidationContext); + operators.Put(operatorEntry.Key, @operator); + } + + return operators; + } + + private Object InstantiateOperator( + String dataFlowName, + int operatorNum, + OperatorMetadataDescriptor desc, + GraphOperatorSpec graphOperator, + EPDataFlowInstantiationOptions options, + ExprValidationContext exprValidationContext) + { + var operatorObject = desc.OptionalOperatorObject; + if (operatorObject == null) + { + var clazz = desc.OperatorFactoryClass ?? desc.OperatorClass; + + // use non-factory class if provided + try + { + operatorObject = Activator.CreateInstance(clazz); + } + catch (Exception e) + { + throw new ExprValidationException("Failed to instantiate: " + e.Message); + } + } + + // inject properties + var configs = graphOperator.Detail == null ? Collections.EmptyDataMap : graphOperator.Detail.Configs; + InjectObjectProperties(dataFlowName, graphOperator.OperatorName, operatorNum, configs, operatorObject, options.GetParameterProvider(), options.ParametersURIs, exprValidationContext); + + if (operatorObject is DataFlowOperatorFactory) + { + try + { + operatorObject = ((DataFlowOperatorFactory)operatorObject).Create(); + } + catch (Exception ex) + { + throw new ExprValidationException("Failed to obtain operator '" + desc.OperatorName + "', encountered an exception raised by factory class " + operatorObject.GetType().Name + ": " + ex.Message, ex); + } + } + + return operatorObject; + } + + private static void InjectObjectProperties( + String dataFlowName, + String operatorName, + int operatorNum, + IDictionary configs, + object instance, + EPDataFlowOperatorParameterProvider optionalParameterProvider, + IDictionary optionalParameterURIs, + ExprValidationContext exprValidationContext) + { + // determine if there is a property holder which holds all properties + var propertyHolderFields = TypeHelper.FindAnnotatedFields(instance.GetType(), typeof(DataFlowOpPropertyHolderAttribute)); + if (propertyHolderFields.Count > 1) + { + throw new ArgumentException("May apply " + typeof(DataFlowOpPropertyHolderAttribute).Name + " annotation only to a single field"); + } + + // determine which class to write properties to + Object propertyInstance; + if (propertyHolderFields.IsEmpty()) + { + propertyInstance = instance; + } + else + { + var propertyHolderClass = propertyHolderFields.First().FieldType; + try + { + propertyInstance = Activator.CreateInstance(propertyHolderClass); + } + catch (Exception e) + { + throw new ExprValidationException("Failed to instantiate '" + propertyHolderClass + "': " + e.Message, e); + } + } + + // populate either the instance itself or the property-holder + PopulateUtil.PopulateObject( + operatorName, operatorNum, dataFlowName, configs, propertyInstance, ExprNodeOrigin.DATAFLOW, + exprValidationContext, optionalParameterProvider, optionalParameterURIs); + + // set holder + if (propertyHolderFields.IsNotEmpty()) + { + var field = propertyHolderFields.FirstOrDefault(); + try + { + field.SetValue(instance, propertyInstance); + } + catch (Exception e) + { + throw new ExprValidationException("Failed to set field '" + field.Name + "': " + e.Message, e); + } + } + } + + private IList DetermineChannels(String dataflowName, + ICollection operatorBuildOrder, + IDictionary operatorDependencies, + IDictionary operators, + IDictionary types, + IDictionary operatorMetadata, + EPDataFlowInstantiationOptions options, + EventAdapterService eventAdapterService, + EngineImportService engineImportService, + StatementContext statementContext, + EPServicesContext servicesContext, + AgentInstanceContext agentInstanceContext, + EPRuntimeEventSender runtimeEventSender) + { + // This is a multi-step process. + // + // Step 1: find all the operators that have explicit output ports and determine the type of such + var declaredOutputPorts = new Dictionary>(); + foreach (int operatorNum in operatorBuildOrder) + { + var metadata = operatorMetadata.Get(operatorNum); + var @operator = operators.Get(operatorNum); + + var annotationPorts = DetermineAnnotatedOutputPorts(operatorNum, @operator, metadata, engineImportService, eventAdapterService); + var graphDeclaredPorts = DetermineGraphDeclaredOutputPorts(@operator, operatorNum, metadata, types, servicesContext); + + var allDeclaredPorts = new List(); + allDeclaredPorts.AddAll(annotationPorts); + allDeclaredPorts.AddAll(graphDeclaredPorts); + + declaredOutputPorts.Put(operatorNum, allDeclaredPorts); + } + + // Step 2: determine for each operator the output ports: some are determined via "prepare" and some can be implicit + // since they may not be declared or can be punctuation. + // Therefore we need to meet ends: on one end the declared types, on the other the implied and dynamically-determined types based on input. + // We do this in operator build order. + var compiledOutputPorts = new Dictionary>(); + foreach (int myOpNum in operatorBuildOrder) + { + + GraphOperatorSpec operatorSpec = operatorMetadata.Get(myOpNum).OperatorSpec; + Object @operator = operators.Get(myOpNum); + OperatorMetadataDescriptor metadata = operatorMetadata.Get(myOpNum); + + // Handle incoming first: if the operator has incoming ports, each of such should already have type information + // Compile type information, call method, obtain output types. + var incomingDependentOpNums = operatorDependencies.Get(myOpNum).Incoming; + var typesPerOutput = DetermineOutputForInput(dataflowName, myOpNum, @operator, metadata, operatorSpec, declaredOutputPorts, compiledOutputPorts, types, incomingDependentOpNums, options, statementContext, servicesContext, agentInstanceContext, runtimeEventSender); + + // Handle outgoing second: + // If there is outgoing declared, use that. + // If output types have been determined based on input, use that. + // else error + var outgoingPorts = DetermineOutgoingPorts(myOpNum, @operator, operatorSpec, metadata, compiledOutputPorts, declaredOutputPorts, typesPerOutput, incomingDependentOpNums); + compiledOutputPorts.Put(myOpNum, outgoingPorts); + } + + // Step 3: normalization and connecting input ports with output ports (logically, no methods yet) + var channels = new List(); + var channelId = 0; + foreach (int myOpNum in operatorBuildOrder) + { + var dependencies = operatorDependencies.Get(myOpNum); + var inputNames = operatorMetadata.Get(myOpNum).OperatorSpec.Input.StreamNamesAndAliases; + var descriptor = operatorMetadata.Get(myOpNum); + + // handle each (a,b,c AS d) + int streamNum = -1; + foreach (GraphOperatorInputNamesAlias inputName in inputNames) + { + streamNum++; + + // get producers + var producingPorts = LogicalChannelUtil.GetOutputPortByStreamName(dependencies.Incoming, inputName.InputStreamNames, compiledOutputPorts); + if (producingPorts.Count < inputName.InputStreamNames.Length) + { + throw new IllegalStateException("Failed to find producing ports"); + } + + // determine type compatibility + if (producingPorts.Count > 1) + { + LogicalChannelProducingPortCompiled first = producingPorts[0]; + for (int i = 1; i < producingPorts.Count; i++) + { + LogicalChannelProducingPortCompiled other = producingPorts[i]; + CompareTypeInfo(descriptor.OperatorName, first.StreamName, first.GraphTypeDesc, other.StreamName, other.GraphTypeDesc); + } + } + + String optionalAlias = inputName.OptionalAsName; + + // handle each stream name + foreach (String streamName in inputName.InputStreamNames) + { + foreach (LogicalChannelProducingPortCompiled port in producingPorts) + { + if (port.StreamName == streamName) + { + var channel = new LogicalChannel(channelId++, descriptor.OperatorName, myOpNum, streamNum, streamName, optionalAlias, descriptor.OperatorPrettyPrint, port); + channels.Add(channel); + } + } + } + } + } + + return channels; + } + + private static void CompareTypeInfo(String operatorName, String firstName, GraphTypeDesc firstType, String otherName, GraphTypeDesc otherType) + { + if (firstType.EventType != null && otherType.EventType != null && !firstType.EventType.Equals(otherType.EventType)) + { + throw new ExprValidationException("For operator '" + operatorName + "' stream '" + firstName + "'" + + " typed '" + firstType.EventType.Name + "'" + + " is not the same type as stream '" + otherName + "'" + + " typed '" + otherType.EventType.Name + "'"); + } + if (firstType.IsWildcard != otherType.IsWildcard) + { + throw new ExprValidationException("For operator '" + operatorName + "' streams '" + firstName + "'" + + " and '" + otherName + "' have differing wildcard type information"); + } + if (firstType.IsUnderlying != otherType.IsUnderlying) + { + throw new ExprValidationException("For operator '" + operatorName + "' streams '" + firstName + "'" + + " and '" + otherName + "' have differing underlying information"); + } + } + + private IList DetermineOutgoingPorts(int myOpNum, + Object @operator, + GraphOperatorSpec operatorSpec, + OperatorMetadataDescriptor metadata, + IDictionary> compiledOutputPorts, + IDictionary> declaredOutputPorts, + GraphTypeDesc[] typesPerOutput, + ICollection incomingDependentOpNums) + { + // Either + // (A) the port is explicitly declared via @OutputTypes + // (B) the port is declared via "=> ABC" + // (C) the port is implicit since there is only one input port and the operator is a functor + + var numPorts = operatorSpec.Output.Items.Count; + var result = new List(); + + // we go port-by-port: what was declared, what types were determined + var types = new Dictionary(); + for (int port = 0; port < numPorts; port++) + { + String portStreamName = operatorSpec.Output.Items[port].StreamName; + + // find declaration, if any + LogicalChannelProducingPortDeclared foundDeclared = null; + IList declaredList = declaredOutputPorts.Get(myOpNum); + foreach (LogicalChannelProducingPortDeclared declared in declaredList) + { + if (declared.StreamNumber == port) + { + if (foundDeclared != null) + { + throw new ExprValidationException("Found a declaration twice for port " + port); + } + foundDeclared = declared; + } + } + + if (foundDeclared == null && (typesPerOutput == null || typesPerOutput.Length <= port || typesPerOutput[port] == null)) + { + throw new ExprValidationException("Operator neither declares an output type nor provided by the operator itself in a 'prepare' method"); + } + if (foundDeclared != null && typesPerOutput != null && typesPerOutput.Length > port && typesPerOutput[port] != null) + { + throw new ExprValidationException("Operator both declares an output type and provided a type in the 'prepare' method"); + } + + // punctuation determined by input + bool hasPunctuationSignal = (foundDeclared != null ? foundDeclared.HasPunctuation : false) || DetermineReceivesPunctuation(incomingDependentOpNums, operatorSpec.Input, compiledOutputPorts); + + GraphTypeDesc compiledType; + if (foundDeclared != null) + { + compiledType = foundDeclared.TypeDesc; + } + else + { + compiledType = typesPerOutput[port]; + } + + var compiled = new LogicalChannelProducingPortCompiled(myOpNum, metadata.OperatorPrettyPrint, portStreamName, port, compiledType, hasPunctuationSignal); + result.Add(compiled); + + // check type compatibility + GraphTypeDesc existingType = types.Get(portStreamName); + types.Put(portStreamName, compiledType); + if (existingType != null) + { + CompareTypeInfo(operatorSpec.OperatorName, portStreamName, existingType, portStreamName, compiledType); + } + } + + return result; + } + + private static bool DetermineReceivesPunctuation(ICollection incomingDependentOpNums, GraphOperatorInput input, IDictionary> compiledOutputPorts) + { + foreach (GraphOperatorInputNamesAlias inputItem in input.StreamNamesAndAliases) + { + var list = LogicalChannelUtil.GetOutputPortByStreamName(incomingDependentOpNums, inputItem.InputStreamNames, compiledOutputPorts); + if (list.Any(port => port.HasPunctuation)) + { + return true; + } + } + + return false; + } + + private GraphTypeDesc[] DetermineOutputForInput(String dataFlowName, + int myOpNum, + Object @operator, + OperatorMetadataDescriptor meta, + GraphOperatorSpec operatorSpec, + IDictionary> declaredOutputPorts, + IDictionary> compiledOutputPorts, + IDictionary types, + ICollection incomingDependentOpNums, + EPDataFlowInstantiationOptions options, + StatementContext statementContext, + EPServicesContext servicesContext, + AgentInstanceContext agentInstanceContext, + EPRuntimeEventSender runtimeEventSender) + { + if (!(@operator is DataFlowOpLifecycle)) + { + return null; + } + + // determine input ports to build up the input port metadata + var numDeclared = operatorSpec.Input.StreamNamesAndAliases.Count; + var inputPorts = new LinkedHashMap(); + for (int inputPortNum = 0; inputPortNum < numDeclared; inputPortNum++) + { + var inputItem = operatorSpec.Input.StreamNamesAndAliases[inputPortNum]; + var producingPorts = LogicalChannelUtil.GetOutputPortByStreamName(incomingDependentOpNums, inputItem.InputStreamNames, compiledOutputPorts); + + DataFlowOpInputPort port; + if (producingPorts.IsEmpty()) + { // this can be when the operator itself is the incoming port, i.e. feedback loop + var declareds = declaredOutputPorts.Get(myOpNum); + if (declareds == null || declareds.IsEmpty()) + { + throw new ExprValidationException("Failed validation for operator '" + operatorSpec.OperatorName + "': No output ports declared"); + } + LogicalChannelProducingPortDeclared foundDeclared = null; + foreach (LogicalChannelProducingPortDeclared declared in declareds) + { + if (inputItem.InputStreamNames.Contains(declared.StreamName)) + { + foundDeclared = declared; + break; + } + } + if (foundDeclared == null) + { + throw new ExprValidationException("Failed validation for operator '" + operatorSpec.OperatorName + "': Failed to find output port declared"); + } + port = new DataFlowOpInputPort(foundDeclared.TypeDesc, new HashSet(inputItem.InputStreamNames), inputItem.OptionalAsName, false); + } + else + { + port = new DataFlowOpInputPort( + new GraphTypeDesc(false, false, producingPorts[0].GraphTypeDesc.EventType), + new HashSet(inputItem.InputStreamNames), + inputItem.OptionalAsName, + producingPorts[0].HasPunctuation); + } + inputPorts.Put(inputPortNum, port); + } + + // determine output ports to build up the output port metadata + IDictionary outputPorts = GetDeclaredOutputPorts(operatorSpec, types, servicesContext); + + // determine event sender + EPRuntimeEventSender dfRuntimeEventSender = runtimeEventSender; + if (options.SurrogateEventSender != null) + { + dfRuntimeEventSender = options.SurrogateEventSender; + } + + var preparable = (DataFlowOpLifecycle)@operator; + var context = new DataFlowOpInitializateContext(dataFlowName, options.GetDataFlowInstanceId(), options.GetDataFlowInstanceUserObject(), inputPorts, outputPorts, statementContext, servicesContext, agentInstanceContext, dfRuntimeEventSender, _epService, meta.OperatorAnnotations); + + DataFlowOpInitializeResult prepareResult; + try + { + prepareResult = preparable.Initialize(context); + } + catch (ExprValidationException e) + { + throw new ExprValidationException("Failed validation for operator '" + operatorSpec.OperatorName + "': " + e.Message, e); + } + catch (Exception e) + { + throw new ExprValidationException("Failed initialization for operator '" + operatorSpec.OperatorName + "': " + e.Message, e); + } + + if (prepareResult == null) + { + return null; + } + return prepareResult.TypeDescriptors; + } + + private static IList DetermineAnnotatedOutputPorts(int producingOpNum, object @operator, OperatorMetadataDescriptor descriptor, EngineImportService engineImportService, EventAdapterService eventAdapterService) + { + var ports = new List(); + + // See if any @OutputTypes annotations exists + var unwrapAttributes = @operator.GetType().UnwrapAttributes(); + var outputTypeAttributes = TypeHelper.GetAnnotations( + unwrapAttributes).Cast(); + var outputTypeGroups = outputTypeAttributes.GroupBy( + attribute => attribute.Port, + attribute => attribute); + + foreach (var outputTypeGroup in outputTypeGroups) + { + // create local event type for the declared type + var propertiesRaw = new LinkedHashMap(); + var outputTypeArr = outputTypeGroup.ToArray(); + var outputTypePort = outputTypeGroup.Key; + + foreach (var outputType in outputTypeArr) + { + Type clazz; + if ((outputType.Type != null) && (outputType.Type != typeof(OutputTypeAttribute))) + { + clazz = outputType.Type; + } + else + { + var typeName = outputType.TypeName; + clazz = TypeHelper.GetTypeForSimpleName(typeName); + if (clazz == null) + { + try + { + clazz = engineImportService.ResolveType(typeName, false); + } + catch (EngineImportException e) + { + throw new EPException("Failed to resolve type '" + typeName + "'"); + } + } + } + propertiesRaw.Put(outputType.Name, clazz); + } + + var propertiesCompiled = EventTypeUtility.CompileMapTypeProperties(propertiesRaw, eventAdapterService); + var eventType = eventAdapterService.CreateAnonymousObjectArrayType("TYPE_" + @operator.GetType(), propertiesCompiled); + + // determine output stream name, which must be provided + var declaredOutput = descriptor.OperatorSpec.Output.Items; + if (declaredOutput.IsEmpty()) + { + throw new ExprValidationException("No output stream declared"); + } + if (declaredOutput.Count < outputTypePort) + { + throw new ExprValidationException("No output stream declared for this port"); + } + + var streamName = declaredOutput[outputTypePort].StreamName; + var isDeclaredPunctuated = TypeHelper.IsAnnotationListed( + typeof(DataFlowOpProvideSignalAttribute), unwrapAttributes); + var port = new LogicalChannelProducingPortDeclared( + producingOpNum, descriptor.OperatorPrettyPrint, streamName, outputTypePort, + new GraphTypeDesc(false, false, eventType), isDeclaredPunctuated); + ports.Add(port); + } + + return ports; + } + + private static IList DetermineGraphDeclaredOutputPorts(Object @operator, int producingOpNum, OperatorMetadataDescriptor metadata, IDictionary types, EPServicesContext servicesContext) + { + var ports = new List(); + + int portNumber = 0; + foreach (GraphOperatorOutputItem outputItem in metadata.OperatorSpec.Output.Items) + { + if (outputItem.TypeInfo.Count > 1) + { + throw new ExprValidationException("Multiple parameter types are not supported"); + } + + if (!outputItem.TypeInfo.IsEmpty()) + { + GraphTypeDesc typeDesc = DetermineTypeOutputPort(outputItem.TypeInfo[0], types, servicesContext); + bool isDeclaredPunctuated = TypeHelper.IsAnnotationListed(typeof(DataFlowOpProvideSignalAttribute), @operator.GetType().UnwrapAttributes()); + ports.Add(new LogicalChannelProducingPortDeclared(producingOpNum, metadata.OperatorPrettyPrint, outputItem.StreamName, portNumber, typeDesc, isDeclaredPunctuated)); + } + portNumber++; + } + + return ports; + } + + private static IDictionary AnalyzeDependencies(CreateDataFlowDesc graphDesc) + { + var logicalOpDependencies = new Dictionary(); + for (int i = 0; i < graphDesc.Operators.Count; i++) + { + OperatorDependencyEntry entry = new OperatorDependencyEntry(); + logicalOpDependencies.Put(i, entry); + } + for (int consumingOpNum = 0; consumingOpNum < graphDesc.Operators.Count; consumingOpNum++) + { + OperatorDependencyEntry entry = logicalOpDependencies.Get(consumingOpNum); + GraphOperatorSpec op = graphDesc.Operators[consumingOpNum]; + + // for each input item + foreach (GraphOperatorInputNamesAlias input in op.Input.StreamNamesAndAliases) + { + + // for each stream name listed + foreach (String inputStreamName in input.InputStreamNames) + { + // find all operators providing such input stream + bool found = false; + + // for each operator + for (int providerOpNum = 0; providerOpNum < graphDesc.Operators.Count; providerOpNum++) + { + GraphOperatorSpec from = graphDesc.Operators[providerOpNum]; + foreach (GraphOperatorOutputItem outputItem in from.Output.Items) + { + if (outputItem.StreamName.Equals(inputStreamName)) + { + found = true; + entry.AddIncoming(providerOpNum); + logicalOpDependencies.Get(providerOpNum).AddOutgoing(consumingOpNum); + } + } + } + + if (!found) + { + throw new ExprValidationException("Input stream '" + inputStreamName + "' consumed by operator '" + op.OperatorName + "' could not be found"); + } + } + } + } + return logicalOpDependencies; + } + + private IDictionary ResolveMetadata(CreateDataFlowDesc graphDesc, + EPDataFlowInstantiationOptions options, + EngineImportService engineImportService, + IDictionary operatorAnnotations) + { + IDictionary operatorClasses = new Dictionary(); + for (int i = 0; i < graphDesc.Operators.Count; i++) + { + var operatorSpec = graphDesc.Operators[i]; + var operatorPrettyPrint = ToPrettyPrint(i, operatorSpec); + var operatorAnnotation = operatorAnnotations.Get(operatorSpec); + + // see if the operator is already provided by options + OperatorMetadataDescriptor descriptor; + if (options.GetOperatorProvider() != null) + { + var @operator = options.GetOperatorProvider().Provide(new EPDataFlowOperatorProviderContext(graphDesc.GraphName, operatorSpec.OperatorName, operatorSpec)); + if (@operator != null) + { + descriptor = new OperatorMetadataDescriptor(operatorSpec, i, @operator.GetType(), null, @operator, operatorPrettyPrint, operatorAnnotation); + operatorClasses.Put(i, descriptor); + continue; + } + } + + // try to find factory class with factory annotation + Type factoryClass = null; + try + { + factoryClass = engineImportService.ResolveType(StringExtensions.Capitalize(operatorSpec.OperatorName + "Factory"), false); + } + catch (EngineImportException e) + { + } + + // if the factory : the interface use that + if (factoryClass != null && factoryClass.IsImplementsInterface(typeof(DataFlowOperatorFactory))) + { + descriptor = new OperatorMetadataDescriptor(operatorSpec, i, null, factoryClass, null, operatorPrettyPrint, operatorAnnotation); + operatorClasses.Put(i, descriptor); + continue; + } + + // resolve by class name + Type clazz; + try + { + clazz = engineImportService.ResolveType(StringExtensions.Capitalize(operatorSpec.OperatorName), false); + } + catch (EngineImportException e) + { + throw new ExprValidationException("Failed to resolve operator '" + operatorSpec.OperatorName + "': " + e.Message, e); + } + + if (!TypeHelper.IsImplementsInterface(clazz, typeof(DataFlowSourceOperator)) && + !TypeHelper.IsAnnotationListed(typeof(DataFlowOperatorAttribute), clazz.UnwrapAttributes())) + { + throw new ExprValidationException( + "Failed to resolve operator '" + operatorSpec.OperatorName + "', operator class " + clazz.FullName + + " does not declare the " + typeof(DataFlowOperatorAttribute).Name + + " annotation or implement the " + typeof(DataFlowSourceOperator).Name + " interface"); + } + + descriptor = new OperatorMetadataDescriptor(operatorSpec, i, clazz, null, null, operatorPrettyPrint, operatorAnnotation); + operatorClasses.Put(i, descriptor); + } + return operatorClasses; + } + + private String ToPrettyPrint(int operatorNum, GraphOperatorSpec spec) + { + var writer = new StringWriter(); + writer.Write(spec.OperatorName); + writer.Write("#"); + writer.Write(Convert.ToString(operatorNum)); + + writer.Write("("); + String delimiter = ""; + foreach (GraphOperatorInputNamesAlias inputItem in spec.Input.StreamNamesAndAliases) + { + writer.Write(delimiter); + ToPrettyPrintInput(inputItem, writer); + if (inputItem.OptionalAsName != null) + { + writer.Write(" as "); + writer.Write(inputItem.OptionalAsName); + } + delimiter = ", "; + } + writer.Write(")"); + + if (spec.Output.Items.IsEmpty()) + { + return writer.ToString(); + } + writer.Write(" -> "); + + delimiter = ""; + foreach (GraphOperatorOutputItem outputItem in spec.Output.Items) + { + writer.Write(delimiter); + writer.Write(outputItem.StreamName); + WriteTypes(outputItem.TypeInfo, writer); + delimiter = ","; + } + + return writer.ToString(); + } + + private static void ToPrettyPrintInput(GraphOperatorInputNamesAlias inputItem, TextWriter writer) + { + if (inputItem.InputStreamNames.Length == 1) + { + writer.Write(inputItem.InputStreamNames[0]); + } + else + { + writer.Write("("); + String delimiterNames = ""; + foreach (String name in inputItem.InputStreamNames) + { + writer.Write(delimiterNames); + writer.Write(name); + delimiterNames = ","; + } + writer.Write(")"); + } + } + + private void WriteTypes(ICollection types, TextWriter writer) + { + if (types.IsEmpty()) + { + return; + } + + writer.Write("<"); + String typeDelimiter = ""; + foreach (GraphOperatorOutputItemType type in types) + { + writer.Write(typeDelimiter); + WriteType(type, writer); + typeDelimiter = ","; + } + writer.Write(">"); + } + + private void WriteType(GraphOperatorOutputItemType type, TextWriter writer) + { + if (type.IsWildcard) + { + writer.Write('?'); + return; + } + writer.Write(type.TypeOrClassname); + WriteTypes(type.TypeParameters, writer); + } + + private static ICollection AnalyzeBuildOrder(IDictionary operators) + { + var graph = new DependencyGraph(operators.Count, true); + foreach (var entry in operators) + { + var myOpNum = entry.Key; + var incomings = entry.Value.Incoming; + foreach (int incoming in incomings) + { + graph.AddDependency(myOpNum, incoming); + } + } + + ICollection topDownSet = new SortedSet(); + while (topDownSet.Count < operators.Count) + { + // secondary sort according to the order of listing + ICollection rootNodes = new SortedSet( + new ProxyComparer((o1, o2) => -1 * o1.CompareTo(o2))); + rootNodes.AddAll(graph.GetRootNodes(topDownSet)); + + if (rootNodes.IsEmpty()) // circular dependency could cause this + { + for (int i = 0; i < operators.Count; i++) + { + if (!topDownSet.Contains(i)) + { + rootNodes.Add(i); + break; + } + } + } + + topDownSet.AddAll(rootNodes); + } + + return topDownSet; + } + + private static LogicalChannelBindingMethodDesc FindMatchingMethod(String operatorName, Type target, LogicalChannel channelDesc, bool isPunctuation) + { + if (isPunctuation) + { + return target.GetMethods() + .Where(m => m.Name == "OnSignal") + .Select(method => new LogicalChannelBindingMethodDesc(method, LogicalChannelBindingTypePassAlong.INSTANCE)) + .FirstOrDefault(); + } + + LogicalChannelProducingPortCompiled outputPort = channelDesc.OutputPort; + + Type[] expectedIndividual; + Type expectedUnderlying; + EventType expectedUnderlyingType; + GraphTypeDesc typeDesc = outputPort.GraphTypeDesc; + + if (typeDesc.IsWildcard) + { + expectedIndividual = new Type[0]; + expectedUnderlying = null; + expectedUnderlyingType = null; + } + else + { + expectedIndividual = new Type[typeDesc.EventType.PropertyNames.Length]; + int i = 0; + foreach (EventPropertyDescriptor descriptor in typeDesc.EventType.PropertyDescriptors) + { + expectedIndividual[i] = descriptor.PropertyType; + i++; + } + expectedUnderlying = typeDesc.EventType.UnderlyingType; + expectedUnderlyingType = typeDesc.EventType; + } + + String channelSpecificMethodName = null; + if (channelDesc.ConsumingOptStreamAliasName != null) + { + channelSpecificMethodName = "On" + channelDesc.ConsumingOptStreamAliasName; + } + + foreach (var method in target.GetMethods()) + { + bool eligible = method.Name.Equals("OnInput"); + if (!eligible && (method.Name == channelSpecificMethodName)) + { + eligible = true; + } + + if (!eligible) + { + continue; + } + + // handle Object[] + var paramTypes = method.GetParameterTypes(); + var numParams = paramTypes.Length; + + if (expectedUnderlying != null) + { + if (numParams == 1 && TypeHelper.IsSubclassOrImplementsInterface(paramTypes[0], expectedUnderlying)) + { + return new LogicalChannelBindingMethodDesc(method, LogicalChannelBindingTypePassAlong.INSTANCE); + } + if (numParams == 2 && paramTypes[0].GetBoxedType() == typeof(int?) && TypeHelper.IsSubclassOrImplementsInterface(paramTypes[1], expectedUnderlying)) + { + return new LogicalChannelBindingMethodDesc(method, new LogicalChannelBindingTypePassAlongWStream(channelDesc.ConsumingOpStreamNum)); + } + } + + if (numParams == 1 && (paramTypes[0] == typeof(Object) || (paramTypes[0] == typeof(object[]) && method.IsVarArgs()))) + { + return new LogicalChannelBindingMethodDesc(method, LogicalChannelBindingTypePassAlong.INSTANCE); + } + if (numParams == 2 && paramTypes[0] == typeof(int) && (paramTypes[1] == typeof(Object) || (paramTypes[1] == typeof(object[]) && method.IsVarArgs()))) + { + return new LogicalChannelBindingMethodDesc(method, new LogicalChannelBindingTypePassAlongWStream(channelDesc.ConsumingOpStreamNum)); + } + + // if exposing a method that exactly matches each property type in order, use that, i.e. "onInut(String p0, int p1)" + if (expectedUnderlyingType is ObjectArrayEventType && TypeHelper.IsSignatureCompatible(expectedIndividual, paramTypes)) + { + return new LogicalChannelBindingMethodDesc(method, LogicalChannelBindingTypeUnwind.INSTANCE); + } + } + + var choices = new LinkedHashSet(); + choices.Add(typeof(Object).Name); + choices.Add("Object[]"); + if (expectedUnderlying != null) + { + choices.Add(expectedUnderlying.Name); + } + throw new ExprValidationException("Failed to find OnInput method on for operator '" + operatorName + "' class " + + target.FullName + ", expected an OnInput method that takes any of {" + CollectionUtil.ToString(choices) + "}"); + } + + private static IDictionary GetDeclaredOutputPorts(GraphOperatorSpec operatorSpec, IDictionary types, EPServicesContext servicesContext) + { + IDictionary outputPorts = new LinkedHashMap(); + for (int outputPortNum = 0; outputPortNum < operatorSpec.Output.Items.Count; outputPortNum++) + { + GraphOperatorOutputItem outputItem = operatorSpec.Output.Items[outputPortNum]; + GraphTypeDesc typeDesc = null; + if (!outputItem.TypeInfo.IsEmpty()) + { + typeDesc = DetermineTypeOutputPort(outputItem.TypeInfo[0], types, servicesContext); + } + outputPorts.Put(outputPortNum, new DataFlowOpOutputPort(outputItem.StreamName, typeDesc)); + } + + return outputPorts; + } + + private static GraphTypeDesc DetermineTypeOutputPort(GraphOperatorOutputItemType outType, IDictionary types, EPServicesContext servicesContext) + { + EventType eventType = null; + bool isWildcard = false; + bool isUnderlying = true; + + String typeOrClassname = outType.TypeOrClassname; + if (typeOrClassname != null && (typeOrClassname.ToLower() == EVENT_WRAPPED_TYPE)) + { + isUnderlying = false; + if (!outType.TypeParameters.IsEmpty() && !outType.TypeParameters[0].IsWildcard) + { + String typeName = outType.TypeParameters[0].TypeOrClassname; + eventType = ResolveType(typeName, types, servicesContext); + } + else + { + isWildcard = true; + } + } + else if (typeOrClassname != null) + { + eventType = ResolveType(typeOrClassname, types, servicesContext); + } + else + { + isWildcard = true; + } + return new GraphTypeDesc(isWildcard, isUnderlying, eventType); + } + + private static EventType ResolveType(String typeOrClassname, IDictionary types, EPServicesContext servicesContext) + { + var eventType = types.Get(typeOrClassname) ?? servicesContext.EventAdapterService.GetEventTypeByName(typeOrClassname); + if (eventType == null) + { + throw new ExprValidationException("Failed to find event type '" + typeOrClassname + "'"); + } + return eventType; + } + + private static void CompileTimeValidate(CreateDataFlowDesc desc) + { + foreach (var spec in desc.Operators) + { + foreach (var @out in spec.Output.Items) + { + if (@out.TypeInfo.Count > 1) + { + throw new ExprValidationException("Failed to validate operator '" + spec.OperatorName + "': Multiple output types for a single stream '" + @out.StreamName + "' are not supported"); + } + } + } + + var schemaNames = new HashSet(); + foreach (var schema in desc.Schemas) + { + if (schemaNames.Contains(schema.SchemaName)) + { + throw new ExprValidationException("Schema name '" + schema.SchemaName + "' is declared more then once"); + } + schemaNames.Add(schema.SchemaName); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/dataflow/core/DataFlowStmtDesc.cs b/NEsper.Core/NEsper.Core/dataflow/core/DataFlowStmtDesc.cs new file mode 100755 index 000000000..0efd548c2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/core/DataFlowStmtDesc.cs @@ -0,0 +1,43 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.dataflow.core +{ + public class DataFlowStmtDesc + { + public DataFlowStmtDesc(CreateDataFlowDesc graphDesc, + StatementContext statementContext, + EPServicesContext servicesContext, + AgentInstanceContext agentInstanceContext, + IDictionary operatorAnnotations) + { + GraphDesc = graphDesc; + StatementContext = statementContext; + ServicesContext = servicesContext; + AgentInstanceContext = agentInstanceContext; + OperatorAnnotations = operatorAnnotations; + } + + public CreateDataFlowDesc GraphDesc { get; private set; } + + public StatementContext StatementContext { get; private set; } + + public EPServicesContext ServicesContext { get; private set; } + + public AgentInstanceContext AgentInstanceContext { get; private set; } + + public IDictionary OperatorAnnotations { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/dataflow/core/DataflowStartDesc.cs b/NEsper.Core/NEsper.Core/dataflow/core/DataflowStartDesc.cs new file mode 100755 index 000000000..e3533e4c9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/core/DataflowStartDesc.cs @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.dataflow.core +{ + public class DataflowStartDesc + { + public DataflowStartDesc(OperatorStatisticsProvider statisticsProvider) + { + StatisticsProvider = statisticsProvider; + } + + public OperatorStatisticsProvider StatisticsProvider { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/dataflow/core/EPDataFlowEmitter1Stream1TargetBase.cs b/NEsper.Core/NEsper.Core/dataflow/core/EPDataFlowEmitter1Stream1TargetBase.cs new file mode 100755 index 000000000..3ccfd4614 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/core/EPDataFlowEmitter1Stream1TargetBase.cs @@ -0,0 +1,79 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client.dataflow; +using com.espertech.esper.dataflow.interfaces; +using com.espertech.esper.dataflow.util; +using com.espertech.esper.epl.core; + +using XLR8.CGLib; + +namespace com.espertech.esper.dataflow.core +{ + public abstract class EPDataFlowEmitter1Stream1TargetBase : EPDataFlowEmitter, SubmitHandler + { + protected readonly int OperatorNum; + protected readonly DataFlowSignalManager SignalManager; + protected readonly SignalHandler SignalHandler; + protected readonly EPDataFlowEmitterExceptionHandler ExceptionHandler; + + private readonly FastMethod _fastMethod; + protected readonly Object TargetObject; + + protected EPDataFlowEmitter1Stream1TargetBase( + int operatorNum, + DataFlowSignalManager signalManager, + SignalHandler signalHandler, + EPDataFlowEmitterExceptionHandler exceptionHandler, + ObjectBindingPair target, + EngineImportService engineImportService) + { + OperatorNum = operatorNum; + SignalManager = signalManager; + SignalHandler = signalHandler; + ExceptionHandler = exceptionHandler; + + var fastClass = FastClass.Create(target.Target.GetType()); + _fastMethod = fastClass.GetMethod(target.Binding.ConsumingBindingDesc.Method); + TargetObject = target.Target; + } + + public abstract void SubmitInternal(Object @object); + + public void Submit(Object @object) + { + SubmitInternal(@object); + } + + public void SubmitSignal(EPDataFlowSignal signal) + { + SignalManager.ProcessSignal(OperatorNum, signal); + SignalHandler.HandleSignal(signal); + } + + public void HandleSignal(EPDataFlowSignal signal) + { + SignalHandler.HandleSignal(signal); + } + + public void SubmitPort(int portNumber, Object @object) + { + if (portNumber == 0) + { + Submit(@object); + } + } + + public FastMethod FastMethod + { + get { return _fastMethod; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/dataflow/core/EPDataFlowEmitter1Stream1TargetPassAlong.cs b/NEsper.Core/NEsper.Core/dataflow/core/EPDataFlowEmitter1Stream1TargetPassAlong.cs new file mode 100755 index 000000000..034982afd --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/core/EPDataFlowEmitter1Stream1TargetPassAlong.cs @@ -0,0 +1,44 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.dataflow.util; +using com.espertech.esper.epl.core; + +namespace com.espertech.esper.dataflow.core +{ + public class EPDataFlowEmitter1Stream1TargetPassAlong + : EPDataFlowEmitter1Stream1TargetBase + { + public EPDataFlowEmitter1Stream1TargetPassAlong( + int operatorNum, + DataFlowSignalManager signalManager, + SignalHandler signalHandler, + EPDataFlowEmitterExceptionHandler exceptionHandler, + ObjectBindingPair target, + EngineImportService engineImportService) + : base(operatorNum, signalManager, signalHandler, exceptionHandler, target, engineImportService) + { + } + + public override void SubmitInternal(Object @object) + { + var parameters = new Object[] { @object }; + try + { + ExceptionHandler.HandleAudit(TargetObject, parameters); + FastMethod.Invoke(TargetObject, parameters); + } + catch (Exception e) + { + ExceptionHandler.HandleException(TargetObject, FastMethod, e, parameters); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/dataflow/core/EPDataFlowEmitter1Stream1TargetPassAlongWStream.cs b/NEsper.Core/NEsper.Core/dataflow/core/EPDataFlowEmitter1Stream1TargetPassAlongWStream.cs new file mode 100755 index 000000000..c4eaae356 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/core/EPDataFlowEmitter1Stream1TargetPassAlongWStream.cs @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.dataflow.util; +using com.espertech.esper.epl.core; + +namespace com.espertech.esper.dataflow.core +{ + public class EPDataFlowEmitter1Stream1TargetPassAlongWStream + : EPDataFlowEmitter1Stream1TargetPassAlong + { + private readonly int _streamNum; + + public EPDataFlowEmitter1Stream1TargetPassAlongWStream( + int operatorNum, + DataFlowSignalManager signalManager, + SignalHandler signalHandler, + EPDataFlowEmitterExceptionHandler exceptionHandler, + ObjectBindingPair target, + int streamNum, + EngineImportService engineImportService) + : base(operatorNum, signalManager, signalHandler, exceptionHandler, target, engineImportService) + { + _streamNum = streamNum; + } + + public override void SubmitInternal(Object @object) + { + var parameters = new Object[] { _streamNum, @object }; + try + { + ExceptionHandler.HandleAudit(TargetObject, parameters); + FastMethod.Invoke(TargetObject, parameters); + } + catch (Exception e) + { + ExceptionHandler.HandleException(TargetObject, FastMethod, e, parameters); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/dataflow/core/EPDataFlowEmitter1Stream1TargetUnwind.cs b/NEsper.Core/NEsper.Core/dataflow/core/EPDataFlowEmitter1Stream1TargetUnwind.cs new file mode 100755 index 000000000..d25dc967c --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/core/EPDataFlowEmitter1Stream1TargetUnwind.cs @@ -0,0 +1,44 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.dataflow.util; +using com.espertech.esper.epl.core; + +namespace com.espertech.esper.dataflow.core +{ + public class EPDataFlowEmitter1Stream1TargetUnwind + : EPDataFlowEmitter1Stream1TargetBase + { + public EPDataFlowEmitter1Stream1TargetUnwind( + int operatorNum, + DataFlowSignalManager signalManager, + SignalHandler signalHandler, + EPDataFlowEmitterExceptionHandler exceptionHandler, + ObjectBindingPair target, + EngineImportService engineImportService) + : base(operatorNum, signalManager, signalHandler, exceptionHandler, target, engineImportService) + { + } + + public override void SubmitInternal(Object @object) + { + var parameters = (Object[])@object; + try + { + ExceptionHandler.HandleAudit(TargetObject, parameters); + FastMethod.Invoke(TargetObject, parameters); + } + catch (Exception e) + { + ExceptionHandler.HandleException(TargetObject, FastMethod, e, parameters); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/dataflow/core/EPDataFlowEmitter1StreamNTarget.cs b/NEsper.Core/NEsper.Core/dataflow/core/EPDataFlowEmitter1StreamNTarget.cs new file mode 100755 index 000000000..1a3cbf3dc --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/core/EPDataFlowEmitter1StreamNTarget.cs @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client.dataflow; +using com.espertech.esper.dataflow.interfaces; +using com.espertech.esper.dataflow.util; + +namespace com.espertech.esper.dataflow.core +{ + public class EPDataFlowEmitter1StreamNTarget : EPDataFlowEmitter + { + private readonly int _operatorNum; + private readonly DataFlowSignalManager _signalManager; + private readonly SubmitHandler[] _targets; + + public EPDataFlowEmitter1StreamNTarget(int operatorNum, DataFlowSignalManager signalManager, SubmitHandler[] targets) + { + _operatorNum = operatorNum; + _signalManager = signalManager; + _targets = targets; + } + + public void Submit(Object @object) + { + foreach (SubmitHandler handler in _targets) + { + handler.SubmitInternal(@object); + } + } + + public void SubmitSignal(EPDataFlowSignal signal) + { + _signalManager.ProcessSignal(_operatorNum, signal); + foreach (SubmitHandler handler in _targets) + { + handler.HandleSignal(signal); + } + } + + public void SubmitPort(int portNumber, Object @object) + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/dataflow/core/EPDataFlowEmitterExceptionHandler.cs b/NEsper.Core/NEsper.Core/dataflow/core/EPDataFlowEmitterExceptionHandler.cs new file mode 100755 index 000000000..0d2911ffb --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/core/EPDataFlowEmitterExceptionHandler.cs @@ -0,0 +1,84 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using XLR8.CGLib; + +using com.espertech.esper.client.annotation; +using com.espertech.esper.client.dataflow; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.util; + +namespace com.espertech.esper.dataflow.core +{ + public class EPDataFlowEmitterExceptionHandler + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public EPDataFlowEmitterExceptionHandler(String engineURI, + String statementName, + bool audit, + String dataFlowName, + String operatorName, + int operatorNumber, + String operatorPrettyPrint, + EPDataFlowExceptionHandler optionalExceptionHandler) + { + EngineURI = engineURI; + StatementName = statementName; + Audit = audit; + DataFlowName = dataFlowName; + OperatorName = operatorName; + OperatorNumber = operatorNumber; + OperatorPrettyPrint = operatorPrettyPrint; + OptionalExceptionHandler = optionalExceptionHandler; + } + + public void HandleException(Object targetObject, FastMethod fastMethod, Exception ex, Object[] parameters) + { + Log.Error("Exception encountered: " + ex.Message, ex); + + if (OptionalExceptionHandler != null) + { + OptionalExceptionHandler.Handle( + new EPDataFlowExceptionContext( + DataFlowName, OperatorName, OperatorNumber, OperatorPrettyPrint, ex)); + } + } + + public bool Audit { get; private set; } + + public string EngineURI { get; private set; } + + public string StatementName { get; private set; } + + public string DataFlowName { get; private set; } + + public string OperatorName { get; private set; } + + public int OperatorNumber { get; private set; } + + public string OperatorPrettyPrint { get; private set; } + + public EPDataFlowExceptionHandler OptionalExceptionHandler { get; private set; } + + public void HandleAudit(Object targetObject, Object[] parameters) + { + if (Audit) + { + AuditPath.AuditLog( + EngineURI, StatementName, AuditEnum.DATAFLOW_OP, + "dataflow " + DataFlowName + " operator " + OperatorName + "(" + OperatorNumber + ") parameters " + + parameters.Render()); + } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/dataflow/core/EPDataFlowEmitterNStreamNTarget.cs b/NEsper.Core/NEsper.Core/dataflow/core/EPDataFlowEmitterNStreamNTarget.cs new file mode 100755 index 000000000..4e32d4ea5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/core/EPDataFlowEmitterNStreamNTarget.cs @@ -0,0 +1,57 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client.dataflow; +using com.espertech.esper.compat; +using com.espertech.esper.dataflow.interfaces; +using com.espertech.esper.dataflow.util; + +namespace com.espertech.esper.dataflow.core +{ + public class EPDataFlowEmitterNStreamNTarget : EPDataFlowEmitter + { + private readonly int _operatorNum; + private readonly DataFlowSignalManager _signalManager; + private readonly SubmitHandler[][] _handlersPerStream; + + public EPDataFlowEmitterNStreamNTarget(int operatorNum, DataFlowSignalManager signalManager, SubmitHandler[][] handlersPerStream) + { + _operatorNum = operatorNum; + _signalManager = signalManager; + _handlersPerStream = handlersPerStream; + } + + public void Submit(Object @object) + { + throw new UnsupportedOperationException("Submit to a specific port is excepted"); + } + + public void SubmitSignal(EPDataFlowSignal signal) + { + _signalManager.ProcessSignal(_operatorNum, signal); + foreach (SubmitHandler[] handlerArr in _handlersPerStream) + { + foreach (SubmitHandler handler in handlerArr) + { + handler.HandleSignal(signal); + } + } + } + + public void SubmitPort(int portNumber, Object @object) + { + SubmitHandler[] targets = _handlersPerStream[portNumber]; + foreach (SubmitHandler handler in targets) + { + handler.SubmitInternal(@object); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/dataflow/core/EPDataFlowEmitterNoTarget.cs b/NEsper.Core/NEsper.Core/dataflow/core/EPDataFlowEmitterNoTarget.cs new file mode 100755 index 000000000..b0ace76da --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/core/EPDataFlowEmitterNoTarget.cs @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client.dataflow; +using com.espertech.esper.dataflow.interfaces; +using com.espertech.esper.dataflow.util; + +namespace com.espertech.esper.dataflow.core +{ + public class EPDataFlowEmitterNoTarget : EPDataFlowEmitter + { + protected readonly int OperatorNum; + protected readonly DataFlowSignalManager DataFlowSignalManager; + + public EPDataFlowEmitterNoTarget(int operatorNum, DataFlowSignalManager dataFlowSignalManager) + { + OperatorNum = operatorNum; + DataFlowSignalManager = dataFlowSignalManager; + } + + public void Submit(Object @object) + { + } + + public void SubmitSignal(EPDataFlowSignal signal) + { + DataFlowSignalManager.ProcessSignal(OperatorNum, signal); + } + + public void SubmitPort(int portNumber, Object @object) + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/dataflow/core/EPDataFlowEmitterWrapperWStatistics.cs b/NEsper.Core/NEsper.Core/dataflow/core/EPDataFlowEmitterWrapperWStatistics.cs new file mode 100755 index 000000000..618fa2ed9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/core/EPDataFlowEmitterWrapperWStatistics.cs @@ -0,0 +1,58 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client.dataflow; +using com.espertech.esper.compat; +using com.espertech.esper.dataflow.interfaces; + +namespace com.espertech.esper.dataflow.core +{ + public class EPDataFlowEmitterWrapperWStatistics : EPDataFlowEmitter + { + private readonly EPDataFlowEmitter _facility; + private readonly int _producerOpNum; + private readonly OperatorStatisticsProvider _statisticsProvider; + private readonly bool _cpuStatistics; + + public EPDataFlowEmitterWrapperWStatistics(EPDataFlowEmitter facility, int producerOpNum, OperatorStatisticsProvider statisticsProvider, bool cpuStatistics) + { + _facility = facility; + _producerOpNum = producerOpNum; + _statisticsProvider = statisticsProvider; + _cpuStatistics = cpuStatistics; + } + + public void Submit(Object @object) + { + SubmitPort(0, @object); + } + + public void SubmitSignal(EPDataFlowSignal signal) + { + _facility.SubmitSignal(signal); + } + + public void SubmitPort(int portNumber, Object @object) + { + if (!_cpuStatistics) + { + _facility.SubmitPort(portNumber, @object); + _statisticsProvider.CountSubmitPort(_producerOpNum, portNumber); + } + else + { + long nanoTime = PerformanceObserver.NanoTime; + _facility.SubmitPort(portNumber, @object); + long nanoTimDelta = PerformanceObserver.NanoTime - nanoTime; + _statisticsProvider.CountSubmitPortWithTime(_producerOpNum, portNumber, nanoTimDelta); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/dataflow/core/EPDataFlowInstanceImpl.cs b/NEsper.Core/NEsper.Core/dataflow/core/EPDataFlowInstanceImpl.cs new file mode 100755 index 000000000..2932eca13 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/core/EPDataFlowInstanceImpl.cs @@ -0,0 +1,417 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Threading; + +using com.espertech.esper.client.annotation; +using com.espertech.esper.client.dataflow; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.compat.threading; +using com.espertech.esper.dataflow.interfaces; +using com.espertech.esper.dataflow.ops; +using com.espertech.esper.dataflow.runnables; +using com.espertech.esper.epl.core; +using com.espertech.esper.util; + +namespace com.espertech.esper.dataflow.core +{ + public class EPDataFlowInstanceImpl : EPDataFlowInstance + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly String _engineUri; + private readonly String _statementName; + private readonly bool _audit; + private readonly String _dataFlowName; + private readonly Object _userObject; + private readonly String _instanceId; + private volatile EPDataFlowState _state; + private readonly IList _sourceRunnables; + private readonly IDictionary> _operators; + private readonly ICollection _operatorBuildOrder; + private readonly EPDataFlowInstanceStatistics _statisticsProvider; + private readonly IDictionary _parameters; + private readonly EngineImportService _engineImportService; + + private IList _joinedThreadLatches; + private IList _threads; + private Thread _runCurrentThread; + + public EPDataFlowInstanceImpl( + String engineURI, + String statementName, + bool audit, + String dataFlowName, + Object userObject, + String instanceId, + EPDataFlowState state, + IList sourceRunnables, + IDictionary operators, + ICollection operatorBuildOrder, + EPDataFlowInstanceStatistics statisticsProvider, + IDictionary parameters, + EngineImportService engineImportService) + { + _engineUri = engineURI; + _statementName = statementName; + _audit = audit; + _dataFlowName = dataFlowName; + _userObject = userObject; + _instanceId = instanceId; + _sourceRunnables = sourceRunnables; + _operators = new OrderedDictionary>(); + foreach (var entry in operators) + { + _operators.Put(entry.Key, new Pair(entry.Value, false)); + } + _operatorBuildOrder = operatorBuildOrder; + _statisticsProvider = statisticsProvider; + SetState(state); + _parameters = parameters; + _engineImportService = engineImportService; + } + + public string DataFlowName + { + get { return _dataFlowName; } + } + + public EPDataFlowState State + { + get { return _state; } + } + + public object UserObject + { + get { return _userObject; } + } + + public string InstanceId + { + get { return _instanceId; } + } + + public IDictionary Parameters + { + get { return _parameters; } + } + + public EPDataFlowInstanceCaptive StartCaptive() + { + lock (this) + { + CheckExecCompleteState(); + CheckExecCancelledState(); + CheckExecRunningState(); + SetState(EPDataFlowState.RUNNING); + + CallOperatorOpen(); + + var emitters = new Dictionary(); + foreach (var operatorStatePair in _operators.Values) + { + if (operatorStatePair.First is Emitter) + { + Emitter emitter = (Emitter)operatorStatePair.First; + emitters.Put(emitter.Name, emitter); + } + } + + return new EPDataFlowInstanceCaptive(emitters, _sourceRunnables); + } + } + + public void Run() + { + lock (this) + { + CheckExecCompleteState(); + CheckExecCancelledState(); + CheckExecRunningState(); + + if (_sourceRunnables.Count != 1) + { + throw new UnsupportedOperationException( + "The data flow '" + _dataFlowName + + "' has zero or multiple sources and requires the use of the start method instead"); + } + + CallOperatorOpen(); + + GraphSourceRunnable sourceRunnable = _sourceRunnables[0]; + SetState(EPDataFlowState.RUNNING); + _runCurrentThread = Thread.CurrentThread; + try + { + sourceRunnable.RunSync(); + } + catch (ThreadInterruptedException) + { + CallOperatorClose(); + SetState(EPDataFlowState.CANCELLED); + throw new EPDataFlowCancellationException( + "Data flow '" + _dataFlowName + "' execution was cancelled", _dataFlowName); + } + catch (Exception t) + { + CallOperatorClose(); + SetState(EPDataFlowState.COMPLETE); + throw new EPDataFlowExecutionException( + "Exception encountered running data flow '" + _dataFlowName + "': " + t.Message, t, _dataFlowName); + } + CallOperatorClose(); + if (_state != EPDataFlowState.CANCELLED) + { + SetState(EPDataFlowState.COMPLETE); + } + } + } + + public void Start() + { + lock (this) + { + CheckExecCompleteState(); + CheckExecCancelledState(); + CheckExecRunningState(); + + CallOperatorOpen(); + + var countdown = new int[] + { + _sourceRunnables.Count + }; + + _threads = new List(); + for (int i = 0; i < _sourceRunnables.Count; i++) + { + var runnable = _sourceRunnables[i]; + var threadName = "esper." + _dataFlowName + "-" + i; + var thread = new Thread(runnable.Run); + thread.Name = threadName; + thread.IsBackground = true; + + runnable.AddCompletionListener( + () => + { + int remaining = Interlocked.Decrement(ref countdown[0]); + if (remaining == 0) + { + Completed(); + } + }); + _threads.Add(thread); + } + + SetState(EPDataFlowState.RUNNING); + + _threads.ForEach(t => t.Start()); + } + } + + public void Join() + { + if (_state == EPDataFlowState.INSTANTIATED) + { + throw new IllegalStateException( + "Data flow '" + _dataFlowName + + "' instance has not been executed, please use join after start or run"); + } + if (_state == EPDataFlowState.CANCELLED) + { + throw new IllegalStateException( + "Data flow '" + _dataFlowName + "' instance has been cancelled and cannot be joined"); + } + + // latch used for non-blocking start + if (_threads != null) + { + _threads.ForEach(t => t.Join()); + } + else + { + var latch = new CountDownLatch(1); + lock (this) + { + if (_joinedThreadLatches == null) + { + _joinedThreadLatches = new List(); + } + _joinedThreadLatches.Add(latch); + } + if (_state != EPDataFlowState.COMPLETE) + { + latch.Await(); + } + } + } + + public void Cancel() + { + if (_state == EPDataFlowState.COMPLETE || _state == EPDataFlowState.CANCELLED) + { + return; + } + if (_state == EPDataFlowState.INSTANTIATED) + { + SetState(EPDataFlowState.CANCELLED); + _sourceRunnables.Clear(); + CallOperatorClose(); + return; + } + + // handle async start + if (_threads != null) + { + foreach (GraphSourceRunnable runnable in _sourceRunnables) + { + runnable.Shutdown(); + } + foreach (Thread thread in _threads) + { + if (thread.IsAlive) + { + thread.Interrupt(); + } + } + } + // handle run + else + { + if (_runCurrentThread != null) + { + _runCurrentThread.Interrupt(); + } + _runCurrentThread = null; + } + + CallOperatorClose(); + + SetState(EPDataFlowState.CANCELLED); + _sourceRunnables.Clear(); + } + + public void Completed() + { + lock (this) + { + if (_state != EPDataFlowState.CANCELLED) + { + SetState(EPDataFlowState.COMPLETE); + } + + CallOperatorClose(); + + if (_joinedThreadLatches != null) + { + foreach (CountDownLatch joinedThread in _joinedThreadLatches) + { + joinedThread.CountDown(); + } + } + } + } + + public EPDataFlowInstanceStatistics Statistics + { + get { return _statisticsProvider; } + } + + private void CheckExecCompleteState() + { + if (_state == EPDataFlowState.COMPLETE) + { + throw new IllegalStateException( + "Data flow '" + _dataFlowName + + "' instance has already completed, please use instantiate to run the data flow again"); + } + } + + private void CheckExecRunningState() + { + if (_state == EPDataFlowState.RUNNING) + { + throw new IllegalStateException("Data flow '" + _dataFlowName + "' instance is already running"); + } + } + + private void CheckExecCancelledState() + { + if (_state == EPDataFlowState.CANCELLED) + { + throw new IllegalStateException( + "Data flow '" + _dataFlowName + "' instance has been cancelled and cannot be run or started"); + } + } + + private void CallOperatorClose() + { + lock (this) + { + foreach (int? opNum in _operatorBuildOrder) + { + var operatorStatePair = _operators.Get(opNum.Value); + if (operatorStatePair.First is DataFlowOpLifecycle && !operatorStatePair.Second) + { + try + { + var lf = (DataFlowOpLifecycle)operatorStatePair.First; + lf.Close(new DataFlowOpCloseContext()); + } + catch (Exception ex) + { + Log.Error( + "Exception encountered closing data flow '" + _dataFlowName + "': " + ex.Message, ex); + } + operatorStatePair.Second = true; + } + } + } + } + + private void CallOperatorOpen() + { + foreach (int? opNum in _operatorBuildOrder) + { + var operatorStatePair = _operators.Get(opNum.Value); + if (operatorStatePair.First is DataFlowOpLifecycle) + { + try + { + var lf = (DataFlowOpLifecycle)operatorStatePair.First; + lf.Open(new DataFlowOpOpenContext()); + } + catch (Exception ex) + { + throw new EPDataFlowExecutionException( + "Exception encountered opening data flow 'FlowOne' in operator " + + operatorStatePair.First.GetType().Name + ": " + ex.Message, ex, _dataFlowName); + } + } + } + } + + private void SetState(EPDataFlowState newState) + { + if (_audit) + { + AuditPath.AuditLog( + _engineUri, _statementName, AuditEnum.DATAFLOW_TRANSITION, + "dataflow " + _dataFlowName + " instance " + _instanceId + " from state " + _state + " to state " + + newState); + } + _state = newState; + } + } +} diff --git a/NEsper.Core/NEsper.Core/dataflow/core/ObjectBindingPair.cs b/NEsper.Core/NEsper.Core/dataflow/core/ObjectBindingPair.cs new file mode 100755 index 000000000..d20904fce --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/core/ObjectBindingPair.cs @@ -0,0 +1,30 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.dataflow.util; + +namespace com.espertech.esper.dataflow.core +{ + public class ObjectBindingPair + { + public ObjectBindingPair(Object target, String operatorPrettyPrint, LogicalChannelBinding binding) + { + Target = target; + OperatorPrettyPrint = operatorPrettyPrint; + Binding = binding; + } + + public string OperatorPrettyPrint { get; private set; } + + public object Target { get; private set; } + + public LogicalChannelBinding Binding { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/dataflow/core/OperatorStatisticsProvider.cs b/NEsper.Core/NEsper.Core/dataflow/core/OperatorStatisticsProvider.cs new file mode 100755 index 000000000..b7212d6c2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/core/OperatorStatisticsProvider.cs @@ -0,0 +1,74 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client.dataflow; +using com.espertech.esper.dataflow.util; + +namespace com.espertech.esper.dataflow.core +{ + public class OperatorStatisticsProvider : EPDataFlowInstanceStatistics + { + private readonly long[][] _submitCounts; + private readonly long[][] _cpuDelta; + private readonly OperatorMetadataDescriptor[] _desc; + + public OperatorStatisticsProvider(IDictionary operatorMetadata) + { + _submitCounts = new long[operatorMetadata.Count][]; + _cpuDelta = new long[operatorMetadata.Count][]; + _desc = new OperatorMetadataDescriptor[operatorMetadata.Count]; + foreach (var entry in operatorMetadata) + { + int opNum = entry.Key; + _desc[opNum] = entry.Value; + int numPorts = entry.Value.OperatorSpec.Output.Items.Count; + _submitCounts[opNum] = new long[numPorts]; + _cpuDelta[opNum] = new long[numPorts]; + } + } + + public IList OperatorStatistics + { + get + { + var result = new List(_submitCounts.Length); + for (int i = 0; i < _submitCounts.Length; i++) + { + long[] submittedPerPort = _submitCounts[i]; + long submittedOverall = submittedPerPort.Sum(); + + long[] timePerPort = _cpuDelta[i]; + long timeOverall = timePerPort.Sum(); + + var meta = _desc[i]; + var stat = new EPDataFlowInstanceOperatorStat( + meta.OperatorName, + meta.OperatorPrettyPrint, i, + submittedOverall, submittedPerPort, + timeOverall, timePerPort); + result.Add(stat); + } + return result; + } + } + + public void CountSubmitPort(int producerOpNum, int portNumber) + { + _submitCounts[producerOpNum][portNumber]++; + } + + public void CountSubmitPortWithTime(int producerOpNum, int portNumber, long nanoTimeDelta) + { + CountSubmitPort(producerOpNum, portNumber); + _cpuDelta[producerOpNum][portNumber] += nanoTimeDelta; + } + } +} diff --git a/NEsper.Core/NEsper.Core/dataflow/core/RealizationFactoryInterface.cs b/NEsper.Core/NEsper.Core/dataflow/core/RealizationFactoryInterface.cs new file mode 100755 index 000000000..f04eb8697 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/core/RealizationFactoryInterface.cs @@ -0,0 +1,288 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client.annotation; +using com.espertech.esper.client.dataflow; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.service; +using com.espertech.esper.dataflow.annotations; +using com.espertech.esper.dataflow.interfaces; +using com.espertech.esper.dataflow.util; +using com.espertech.esper.epl.core; +using com.espertech.esper.util; + +namespace com.espertech.esper.dataflow.core +{ + public class RealizationFactoryInterface + { + private static readonly ILog Log = + LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + public static DataflowStartDesc Realize( + string dataFlowName, + IDictionary operators, + IDictionary operatorMetadata, + ICollection operatorBuildOrder, + IList bindings, + DataFlowSignalManager dataFlowSignalManager, + EPDataFlowInstantiationOptions options, + EPServicesContext services, + StatementContext statementContext) + { + // First pass: inject runtime context + var runtimeContexts = new Dictionary(); + OperatorStatisticsProvider statisticsProvider = null; + if (options.IsOperatorStatistics()) + { + statisticsProvider = new OperatorStatisticsProvider(operatorMetadata); + } + + var audit = AuditEnum.DATAFLOW_OP.GetAudit(statementContext.Annotations) != null; + foreach (var producerOpNum in operatorBuildOrder) + { + var operatorPrettyPrint = operatorMetadata.Get(producerOpNum).OperatorPrettyPrint; + if (Log.IsDebugEnabled) + { + Log.Debug("Generating runtime context for " + operatorPrettyPrint); + } + + // determine the number of output streams + var producingOp = operators.Get(producerOpNum); + var numOutputStreams = operatorMetadata.Get(producerOpNum).OperatorSpec.Output.Items.Count; + var targets = GetOperatorConsumersPerStream( + numOutputStreams, producerOpNum, operators, operatorMetadata, bindings); + + var runtimeContext = GenerateRuntimeContext( + statementContext.EngineURI, statementContext.StatementName, audit, dataFlowName, producerOpNum, + operatorPrettyPrint, dataFlowSignalManager, targets, options, statementContext.EngineImportService); + + if (options.IsOperatorStatistics()) + { + runtimeContext = new EPDataFlowEmitterWrapperWStatistics( + runtimeContext, producerOpNum, statisticsProvider, options.IsCpuStatistics()); + } + + TypeHelper.SetFieldForAnnotation(producingOp, typeof (DataFlowContextAttribute), runtimeContext); + runtimeContexts.Put(producerOpNum, runtimeContext); + } + + // Second pass: hook punctuation such that it gets forwarded + foreach (var producerOpNum in operatorBuildOrder) + { + var operatorPrettyPrint = operatorMetadata.Get(producerOpNum).OperatorPrettyPrint; + if (Log.IsDebugEnabled) + { + Log.Debug("Handling signals for " + operatorPrettyPrint); + } + + // determine consumers that receive punctuation + var consumingOperatorsWithPunctuation = new HashSet(); + foreach (var binding in bindings) + { + if (!binding.LogicalChannel.OutputPort.HasPunctuation || + binding.LogicalChannel.OutputPort.ProducingOpNum != producerOpNum) + { + continue; + } + consumingOperatorsWithPunctuation.Add(binding.LogicalChannel.ConsumingOpNum); + } + + // hook up a listener for each + foreach (int consumerPunc in consumingOperatorsWithPunctuation) + { + var context = runtimeContexts.Get(consumerPunc); + if (context == null) + { + continue; + } + dataFlowSignalManager.AddSignalListener( + producerOpNum, new ProxyDataFlowSignalListener + { + ProcSignal = signal => context.SubmitSignal(signal) + }); + } + } + + return new DataflowStartDesc(statisticsProvider); + } + + private static IList[] GetOperatorConsumersPerStream( + int numOutputStreams, + int producingOperator, + IDictionary operators, + IDictionary operatorMetadata, + IList bindings) + { + var channelsForProducer = LogicalChannelUtil.GetBindingsConsuming(producingOperator, bindings); + if (channelsForProducer.IsEmpty()) + { + return null; + } + + var submitTargets = new IList[numOutputStreams]; + for (var i = 0; i < numOutputStreams; i++) + { + submitTargets[i] = new List(); + } + + foreach (var binding in channelsForProducer) + { + var consumingOp = binding.LogicalChannel.ConsumingOpNum; + var @operator = operators.Get(consumingOp); + var producingStreamNum = binding.LogicalChannel.OutputPort.StreamNumber; + var pairs = submitTargets[producingStreamNum]; + var metadata = operatorMetadata.Get(consumingOp); + pairs.Add(new ObjectBindingPair(@operator, metadata.OperatorPrettyPrint, binding)); + } + return submitTargets; + } + + private static SignalHandler GetSignalHandler( + int producerNum, + Object target, + LogicalChannelBindingMethodDesc consumingSignalBindingDesc, + EngineImportService engineImportService) + { + if (consumingSignalBindingDesc == null) + { + return SignalHandlerDefault.INSTANCE; + } + else + { + if (consumingSignalBindingDesc.BindingType is LogicalChannelBindingTypePassAlong) + { + return new SignalHandlerDefaultWInvoke( + target, consumingSignalBindingDesc.Method, engineImportService); + } + else if (consumingSignalBindingDesc.BindingType is LogicalChannelBindingTypePassAlongWStream) + { + var streamInfo = (LogicalChannelBindingTypePassAlongWStream) consumingSignalBindingDesc.BindingType; + return new SignalHandlerDefaultWInvokeStream( + target, consumingSignalBindingDesc.Method, engineImportService, streamInfo.StreamNum); + } + else + { + throw new IllegalStateException( + "Unrecognized signal binding: " + consumingSignalBindingDesc.BindingType); + } + } + } + + private static SubmitHandler GetSubmitHandler( + string engineURI, + string statementName, + bool audit, + string dataflowName, + int producerOpNum, + string operatorPrettyPrint, + DataFlowSignalManager dataFlowSignalManager, + ObjectBindingPair target, + EPDataFlowExceptionHandler optionalExceptionHandler, + EngineImportService engineImportService) + { + var signalHandler = GetSignalHandler( + producerOpNum, target.Target, target.Binding.ConsumingSignalBindingDesc, engineImportService); + + var receivingOpNum = target.Binding.LogicalChannel.ConsumingOpNum; + var receivingOpPretty = target.Binding.LogicalChannel.ConsumingOpPrettyPrint; + var receivingOpName = target.Binding.LogicalChannel.ConsumingOpName; + var exceptionHandler = new EPDataFlowEmitterExceptionHandler( + engineURI, statementName, audit, dataflowName, receivingOpName, receivingOpNum, receivingOpPretty, + optionalExceptionHandler); + + var bindingType = target.Binding.ConsumingBindingDesc.BindingType; + if (bindingType is LogicalChannelBindingTypePassAlong) + { + return new EPDataFlowEmitter1Stream1TargetPassAlong( + producerOpNum, dataFlowSignalManager, signalHandler, exceptionHandler, target, engineImportService); + } + else if (bindingType is LogicalChannelBindingTypePassAlongWStream) + { + var type = (LogicalChannelBindingTypePassAlongWStream) bindingType; + return new EPDataFlowEmitter1Stream1TargetPassAlongWStream( + producerOpNum, dataFlowSignalManager, signalHandler, exceptionHandler, target, type.StreamNum, + engineImportService); + } + else if (bindingType is LogicalChannelBindingTypeUnwind) + { + return new EPDataFlowEmitter1Stream1TargetUnwind( + producerOpNum, dataFlowSignalManager, signalHandler, exceptionHandler, target, engineImportService); + } + else + { + throw new UnsupportedOperationException("Unsupported binding type '" + bindingType + "'"); + } + } + + private static EPDataFlowEmitter GenerateRuntimeContext( + string engineURI, + string statementName, + bool audit, + string dataflowName, + int producerOpNum, + string operatorPrettyPrint, + DataFlowSignalManager dataFlowSignalManager, + IList[] targetsPerStream, + EPDataFlowInstantiationOptions options, + EngineImportService engineImportService) + { + // handle no targets + if (targetsPerStream == null) + { + return new EPDataFlowEmitterNoTarget(producerOpNum, dataFlowSignalManager); + } + + // handle single-stream case + if (targetsPerStream.Length == 1) + { + var targets = targetsPerStream[0]; + + // handle single-stream single target case + if (targets.Count == 1) + { + var target = targets[0]; + return GetSubmitHandler( + engineURI, statementName, audit, dataflowName, producerOpNum, operatorPrettyPrint, + dataFlowSignalManager, target, options.GetExceptionHandler(), engineImportService); + } + + var handlers = new SubmitHandler[targets.Count]; + for (var i = 0; i < handlers.Length; i++) + { + handlers[i] = GetSubmitHandler( + engineURI, statementName, audit, dataflowName, producerOpNum, operatorPrettyPrint, + dataFlowSignalManager, targets[i], options.GetExceptionHandler(), engineImportService); + } + return new EPDataFlowEmitter1StreamNTarget(producerOpNum, dataFlowSignalManager, handlers); + } + else + { + // handle multi-stream case + var handlersPerStream = new SubmitHandler[targetsPerStream.Length][]; + for (var streamNum = 0; streamNum < targetsPerStream.Length; streamNum++) + { + var handlers = new SubmitHandler[targetsPerStream[streamNum].Count]; + handlersPerStream[streamNum] = handlers; + for (var i = 0; i < handlers.Length; i++) + { + handlers[i] = GetSubmitHandler( + engineURI, statementName, audit, dataflowName, producerOpNum, operatorPrettyPrint, + dataFlowSignalManager, targetsPerStream[streamNum][i], options.GetExceptionHandler(), + engineImportService); + } + } + return new EPDataFlowEmitterNStreamNTarget(producerOpNum, dataFlowSignalManager, handlersPerStream); + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/dataflow/core/SignalHandler.cs b/NEsper.Core/NEsper.Core/dataflow/core/SignalHandler.cs new file mode 100755 index 000000000..770de8fff --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/core/SignalHandler.cs @@ -0,0 +1,17 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client.dataflow; + +namespace com.espertech.esper.dataflow.core +{ + public interface SignalHandler + { + void HandleSignal(EPDataFlowSignal signal); + } +} diff --git a/NEsper.Core/NEsper.Core/dataflow/core/SignalHandlerDefault.cs b/NEsper.Core/NEsper.Core/dataflow/core/SignalHandlerDefault.cs new file mode 100755 index 000000000..cdde2403b --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/core/SignalHandlerDefault.cs @@ -0,0 +1,21 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client.dataflow; + +namespace com.espertech.esper.dataflow.core +{ + public class SignalHandlerDefault : SignalHandler + { + internal static SignalHandlerDefault INSTANCE = new SignalHandlerDefault(); + + public virtual void HandleSignal(EPDataFlowSignal signal) + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/dataflow/core/SignalHandlerDefaultWInvoke.cs b/NEsper.Core/NEsper.Core/dataflow/core/SignalHandlerDefaultWInvoke.cs new file mode 100755 index 000000000..ed0adebf2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/core/SignalHandlerDefaultWInvoke.cs @@ -0,0 +1,54 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using com.espertech.esper.client.dataflow; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.core; + +namespace com.espertech.esper.dataflow.core +{ + public class SignalHandlerDefaultWInvoke : SignalHandlerDefault + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + protected readonly Object Target; + protected readonly MethodInfo Method; + + public SignalHandlerDefaultWInvoke(Object target, MethodInfo method, EngineImportService engineImportService) + { + Target = target; + + FastClass fastClass = FastClass.Create(target.GetType()); + Method = fastClass.GetMethod(method); + } + + public override void HandleSignal(EPDataFlowSignal signal) + { + try + { + HandleSignalInternal(signal); + } + catch (TargetInvocationException ex) + { + Log.Error("Failed to invoke signal handler: " + ex.Message, ex); + } + } + + protected virtual void HandleSignalInternal(EPDataFlowSignal signal) + { + Method.Invoke( + Target, new Object[] + { + signal + }); + } + } +} diff --git a/NEsper.Core/NEsper.Core/dataflow/core/SignalHandlerDefaultWInvokeStream.cs b/NEsper.Core/NEsper.Core/dataflow/core/SignalHandlerDefaultWInvokeStream.cs new file mode 100755 index 000000000..971ac2211 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/core/SignalHandlerDefaultWInvokeStream.cs @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using com.espertech.esper.client.dataflow; +using com.espertech.esper.epl.core; + +namespace com.espertech.esper.dataflow.core +{ + public class SignalHandlerDefaultWInvokeStream : SignalHandlerDefaultWInvoke + { + private readonly int _streamNum; + + public SignalHandlerDefaultWInvokeStream(Object target, MethodInfo method, EngineImportService engineImportService, int streamNum) + : base(target, method, engineImportService) + { + _streamNum = streamNum; + } + + protected override void HandleSignalInternal(EPDataFlowSignal signal) + { + Method.Invoke(Target, new Object[] { _streamNum, signal }); + } + } +} diff --git a/NEsper.Core/NEsper.Core/dataflow/core/SubmitHandler.cs b/NEsper.Core/NEsper.Core/dataflow/core/SubmitHandler.cs new file mode 100755 index 000000000..5fcacf5ae --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/core/SubmitHandler.cs @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using XLR8.CGLib; + +using com.espertech.esper.client.dataflow; +using com.espertech.esper.dataflow.interfaces; + +namespace com.espertech.esper.dataflow.core +{ + public interface SubmitHandler : EPDataFlowEmitter { + void SubmitInternal(Object @object); + void HandleSignal(EPDataFlowSignal signal); + FastMethod FastMethod { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/dataflow/interfaces/DataFlowOpCloseContext.cs b/NEsper.Core/NEsper.Core/dataflow/interfaces/DataFlowOpCloseContext.cs new file mode 100755 index 000000000..73657c1db --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/interfaces/DataFlowOpCloseContext.cs @@ -0,0 +1,14 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.dataflow.interfaces +{ + public class DataFlowOpCloseContext + { + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/dataflow/interfaces/DataFlowOpInitializateContext.cs b/NEsper.Core/NEsper.Core/dataflow/interfaces/DataFlowOpInitializateContext.cs new file mode 100755 index 000000000..efa049cef --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/interfaces/DataFlowOpInitializateContext.cs @@ -0,0 +1,57 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; + +namespace com.espertech.esper.dataflow.interfaces +{ + public class DataFlowOpInitializateContext + { + public DataFlowOpInitializateContext(String dataflowName, String dataflowInstanceId, Object dataflowInstanceUserObject, IDictionary inputPorts, IDictionary outputPorts, StatementContext statementContext, EPServicesContext servicesContext, AgentInstanceContext agentInstanceContext, EPRuntimeEventSender runtimeEventSender, EPServiceProvider engine, Attribute[] operatorAnnotations) + { + DataflowName = dataflowName; + DataflowInstanceId = dataflowInstanceId; + DataflowInstanceUserObject = dataflowInstanceUserObject; + InputPorts = inputPorts; + OutputPorts = outputPorts; + StatementContext = statementContext; + ServicesContext = servicesContext; + AgentInstanceContext = agentInstanceContext; + RuntimeEventSender = runtimeEventSender; + Engine = engine; + OperatorAnnotations = operatorAnnotations; + } + + public string DataflowName { get; private set; } + + public string DataflowInstanceId { get; private set; } + + public object DataflowInstanceUserObject { get; private set; } + + public StatementContext StatementContext { get; private set; } + + public EPServicesContext ServicesContext { get; private set; } + + public AgentInstanceContext AgentInstanceContext { get; private set; } + + public IDictionary InputPorts { get; private set; } + + public IDictionary OutputPorts { get; private set; } + + public EPRuntimeEventSender RuntimeEventSender { get; private set; } + + public EPServiceProvider Engine { get; private set; } + + public Attribute[] OperatorAnnotations { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/dataflow/interfaces/DataFlowOpInitializeContext.cs b/NEsper.Core/NEsper.Core/dataflow/interfaces/DataFlowOpInitializeContext.cs new file mode 100755 index 000000000..e52cfb957 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/interfaces/DataFlowOpInitializeContext.cs @@ -0,0 +1,22 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.service; + +namespace com.espertech.esper.dataflow.interfaces +{ + public class DataFlowOpInitializeContext + { + public DataFlowOpInitializeContext(StatementContext statementContext) + { + StatementContext = statementContext; + } + + public StatementContext StatementContext { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/dataflow/interfaces/DataFlowOpInitializeResult.cs b/NEsper.Core/NEsper.Core/dataflow/interfaces/DataFlowOpInitializeResult.cs new file mode 100755 index 000000000..865a8ec68 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/interfaces/DataFlowOpInitializeResult.cs @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.dataflow.util; + +namespace com.espertech.esper.dataflow.interfaces +{ + public class DataFlowOpInitializeResult { + public DataFlowOpInitializeResult() { + } + + public DataFlowOpInitializeResult(GraphTypeDesc[] typeDescriptors) { + TypeDescriptors = typeDescriptors; + } + + public GraphTypeDesc[] TypeDescriptors { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/dataflow/interfaces/DataFlowOpInputPort.cs b/NEsper.Core/NEsper.Core/dataflow/interfaces/DataFlowOpInputPort.cs new file mode 100755 index 000000000..4075791af --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/interfaces/DataFlowOpInputPort.cs @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.dataflow.util; + +namespace com.espertech.esper.dataflow.interfaces +{ + public class DataFlowOpInputPort + { + public DataFlowOpInputPort(GraphTypeDesc typeDesc, ICollection streamNames, String optionalAlias, bool hasPunctuationSignal) + { + TypeDesc = typeDesc; + StreamNames = streamNames; + OptionalAlias = optionalAlias; + HasPunctuationSignal = hasPunctuationSignal; + } + + public GraphTypeDesc TypeDesc { get; private set; } + + public ICollection StreamNames { get; private set; } + + public string OptionalAlias { get; private set; } + + public bool HasPunctuationSignal { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/dataflow/interfaces/DataFlowOpLifecycle.cs b/NEsper.Core/NEsper.Core/dataflow/interfaces/DataFlowOpLifecycle.cs new file mode 100755 index 000000000..426e97a98 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/interfaces/DataFlowOpLifecycle.cs @@ -0,0 +1,17 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.dataflow.interfaces +{ + public interface DataFlowOpLifecycle + { + DataFlowOpInitializeResult Initialize(DataFlowOpInitializateContext initContext); + void Open(DataFlowOpOpenContext openContext); + void Close(DataFlowOpCloseContext closeContext); + } +} diff --git a/NEsper.Core/NEsper.Core/dataflow/interfaces/DataFlowOpOpenContext.cs b/NEsper.Core/NEsper.Core/dataflow/interfaces/DataFlowOpOpenContext.cs new file mode 100755 index 000000000..91b64c746 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/interfaces/DataFlowOpOpenContext.cs @@ -0,0 +1,14 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.dataflow.interfaces +{ + public class DataFlowOpOpenContext { + + } +} diff --git a/NEsper.Core/NEsper.Core/dataflow/interfaces/DataFlowOpOutputPort.cs b/NEsper.Core/NEsper.Core/dataflow/interfaces/DataFlowOpOutputPort.cs new file mode 100755 index 000000000..4a17a1557 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/interfaces/DataFlowOpOutputPort.cs @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.dataflow.util; + +namespace com.espertech.esper.dataflow.interfaces +{ + public class DataFlowOpOutputPort + { + public DataFlowOpOutputPort(String streamName, GraphTypeDesc optionalDeclaredType) + { + StreamName = streamName; + OptionalDeclaredType = optionalDeclaredType; + } + + public string StreamName { get; private set; } + + public GraphTypeDesc OptionalDeclaredType { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/dataflow/interfaces/DataFlowOperatorFactory.cs b/NEsper.Core/NEsper.Core/dataflow/interfaces/DataFlowOperatorFactory.cs new file mode 100755 index 000000000..9cb583bf5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/interfaces/DataFlowOperatorFactory.cs @@ -0,0 +1,17 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.dataflow.interfaces +{ + public interface DataFlowOperatorFactory + { + Object Create(); + } +} diff --git a/NEsper.Core/NEsper.Core/dataflow/interfaces/DataFlowSourceOperator.cs b/NEsper.Core/NEsper.Core/dataflow/interfaces/DataFlowSourceOperator.cs new file mode 100755 index 000000000..639edbb36 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/interfaces/DataFlowSourceOperator.cs @@ -0,0 +1,15 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.dataflow.interfaces +{ + public interface DataFlowSourceOperator : DataFlowOpLifecycle + { + void Next(); + } +} diff --git a/NEsper.Core/NEsper.Core/dataflow/interfaces/EPDataFlowEmitter.cs b/NEsper.Core/NEsper.Core/dataflow/interfaces/EPDataFlowEmitter.cs new file mode 100755 index 000000000..1ffdd0447 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/interfaces/EPDataFlowEmitter.cs @@ -0,0 +1,21 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client.dataflow; + +namespace com.espertech.esper.dataflow.interfaces +{ + public interface EPDataFlowEmitter + { + void Submit(Object @object); + void SubmitSignal(EPDataFlowSignal signal); + void SubmitPort(int portNumber, Object @object); + } +} diff --git a/NEsper.Core/NEsper.Core/dataflow/interfaces/EPDataFlowSignalHandler.cs b/NEsper.Core/NEsper.Core/dataflow/interfaces/EPDataFlowSignalHandler.cs new file mode 100755 index 000000000..4f51d5720 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/interfaces/EPDataFlowSignalHandler.cs @@ -0,0 +1,17 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client.dataflow; + +namespace com.espertech.esper.dataflow.interfaces +{ + public interface EPDataFlowSignalHandler + { + void OnSignal(EPDataFlowSignal signal); + } +} diff --git a/NEsper.Core/NEsper.Core/dataflow/ops/BeaconSource.cs b/NEsper.Core/NEsper.Core/dataflow/ops/BeaconSource.cs new file mode 100755 index 000000000..ab3872ef7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/ops/BeaconSource.cs @@ -0,0 +1,304 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; + +using com.espertech.esper.client; +using com.espertech.esper.client.dataflow; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.service; +using com.espertech.esper.dataflow.annotations; +using com.espertech.esper.dataflow.interfaces; +using com.espertech.esper.dataflow.util; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events; +using com.espertech.esper.util; + +namespace com.espertech.esper.dataflow.ops +{ + [DataFlowOperator] + public class BeaconSource : DataFlowSourceOperator + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private static readonly List PARAMETER_PROPERTIES = new List + { + "interval", + "iterations", + "initialDelay" + }; + + [DataFlowContext] private EPDataFlowEmitter graphContext; + [DataFlowOpParameter] private long iterations; + [DataFlowOpParameter] private double initialDelay; + [DataFlowOpParameter] private double interval; + + private readonly IDictionary _allProperties = new LinkedHashMap(); + + private long _initialDelayMSec; + private long _periodDelayMSec; + private long _lastSendTime; + private long _iterationNumber; + private bool _produceEventBean; + + private ExprEvaluator[] _evaluators; + private EventBeanManufacturer _manufacturer; + + private static WriteablePropertyDescriptor[] SetupProperties(string[] propertyNamesOffered, EventType outputEventType, StatementContext statementContext) + { + var writeables = statementContext.EventAdapterService.GetWriteableProperties(outputEventType, false); + var writablesList = new List(); + + for (var i = 0; i < propertyNamesOffered.Length; i++) { + var propertyName = propertyNamesOffered[i]; + WriteablePropertyDescriptor writable = EventTypeUtility.FindWritable(propertyName, writeables); + if (writable == null) { + throw new ExprValidationException("Failed to find writable property '" + propertyName + "' for event type '" + outputEventType.Name + "'"); + } + writablesList.Add(writable); + } + + return writablesList.ToArray(); + } + + [DataFlowOpParameter(All=true)] + public void SetProperty(string name, Object value) + { + _allProperties.Put(name, value); + } + + public DataFlowOpInitializeResult Initialize(DataFlowOpInitializateContext context) + { + _initialDelayMSec = (long) (initialDelay*1000); + _periodDelayMSec = (long) (interval*1000); + + if (context.OutputPorts.Count != 1) + { + throw new ArgumentException( + "BeaconSource operator requires one output stream but produces " + context.OutputPorts.Count + + " streams"); + } + + // Check if a type is declared + var port = context.OutputPorts[0]; + ICollection props; + if (port.OptionalDeclaredType != null && port.OptionalDeclaredType.EventType != null) + { + var outputEventType = port.OptionalDeclaredType.EventType; + _produceEventBean = port.OptionalDeclaredType != null && !port.OptionalDeclaredType.IsUnderlying; + + // compile properties to populate + props = _allProperties.Keys; + props.RemoveAll(PARAMETER_PROPERTIES); + var writables = SetupProperties(props.ToArray(), outputEventType, context.StatementContext); + _manufacturer = context.ServicesContext.EventAdapterService.GetManufacturer( + outputEventType, writables, context.ServicesContext.EngineImportService, false); + + var index = 0; + _evaluators = new ExprEvaluator[writables.Length]; + TypeWidenerCustomizer typeWidenerCustomizer = + context.ServicesContext.EventAdapterService.GetTypeWidenerCustomizer(outputEventType); + foreach (var writeable in writables) + { + + var providedProperty = _allProperties.Get(writeable.PropertyName); + if (providedProperty is ExprNode) + { + var exprNode = (ExprNode) providedProperty; + var validated = ExprNodeUtility.ValidateSimpleGetSubtree( + ExprNodeOrigin.DATAFLOWBEACON, exprNode, context.StatementContext, null, false); + var exprEvaluator = validated.ExprEvaluator; + var widener = TypeWidenerFactory.GetCheckPropertyAssignType( + ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(validated), exprEvaluator.ReturnType, + writeable.PropertyType, writeable.PropertyName, false, typeWidenerCustomizer, + context.StatementContext.StatementName, context.Engine.URI); + if (widener != null) + { + _evaluators[index] = new ProxyExprEvaluator + { + ProcEvaluate = evaluateParams => + { + var value = exprEvaluator.Evaluate(evaluateParams); + return widener.Invoke(value); + }, + ProcReturnType = () => null + }; + } + else + { + _evaluators[index] = exprEvaluator; + } + } + else if (providedProperty == null) + { + _evaluators[index] = new ProxyExprEvaluator + { + ProcEvaluate = evaluateParams => null, + ProcReturnType = () => null + }; + } + else + { + _evaluators[index] = new ProxyExprEvaluator + { + ProcEvaluate = evaluateParams => providedProperty, + ProcReturnType = () => providedProperty.GetType() + }; + } + index++; + } + + return null; // no changing types + } + + // No type has been declared, we can create one + var anonymousTypeName = context.DataflowName + "-beacon"; + var types = new LinkedHashMap(); + props = _allProperties.Keys; + props.RemoveAll(PARAMETER_PROPERTIES); + + var count = 0; + _evaluators = new ExprEvaluator[props.Count]; + foreach (var propertyName in props) + { + var exprNode = (ExprNode) _allProperties.Get(propertyName); + var validated = ExprNodeUtility.ValidateSimpleGetSubtree(ExprNodeOrigin.DATAFLOWBEACON, exprNode, context.StatementContext, null, false); + var evaluateParamsX = new EvaluateParams(null, true, context.AgentInstanceContext); + var value = validated.ExprEvaluator.Evaluate(evaluateParamsX); + if (value == null) + { + types.Put(propertyName, null); + } + else + { + types.Put(propertyName, value.GetType()); + } + _evaluators[count] = new ProxyExprEvaluator() + { + ProcEvaluate = (evaluateParams) => value, + ProcReturnType = () => null + }; + count++; + } + + EventType type = + context.ServicesContext.EventAdapterService.CreateAnonymousObjectArrayType(anonymousTypeName, types); + return new DataFlowOpInitializeResult( + new GraphTypeDesc[] + { + new GraphTypeDesc(false, true, type) + }); + } + + public void Next() + { + if (_iterationNumber == 0 && _initialDelayMSec > 0) + { + try + { + Thread.Sleep((int) _initialDelayMSec); + } + catch (ThreadInterruptedException) + { + graphContext.SubmitSignal( + new DataFlowSignalFinalMarker()); + } + } + + if (_iterationNumber > 0 && _periodDelayMSec > 0) + { + var nsecDelta = _lastSendTime - PerformanceObserver.NanoTime; + var sleepTime = _periodDelayMSec - nsecDelta/1000000; + if (sleepTime > 0) + { + try + { + Thread.Sleep((int) sleepTime); + } + catch (ThreadInterruptedException) + { + graphContext.SubmitSignal( + new DataFlowSignalFinalMarker()); + } + } + } + + if (iterations > 0 && _iterationNumber >= iterations) + { + graphContext.SubmitSignal( + new DataFlowSignalFinalMarker()); + } + else + { + _iterationNumber++; + if (_evaluators != null) + { + var evaluateParams = new EvaluateParams(null, true, null); + var row = new Object[_evaluators.Length]; + for (var i = 0; i < row.Length; i++) + { + if (_evaluators[i] != null) + { + row[i] = _evaluators[i].Evaluate(evaluateParams); + } + } + if (Log.IsDebugEnabled) + { + Log.Debug("BeaconSource submitting row " + CompatExtensions.Render(row)); + } + + Object outputEvent = row; + if (_manufacturer != null) + { + if (!_produceEventBean) + { + outputEvent = _manufacturer.MakeUnderlying(row); + } + else + { + outputEvent = _manufacturer.Make(row); + } + } + graphContext.Submit(outputEvent); + } + else + { + if (Log.IsDebugEnabled) + { + Log.Debug("BeaconSource submitting empty row"); + } + graphContext.Submit(new Object[0]); + } + + if (interval > 0) + { + _lastSendTime = PerformanceObserver.NanoTime; + } + } + } + + public void Open(DataFlowOpOpenContext openContext) + { + // no action + } + + public void Close(DataFlowOpCloseContext openContext) + { + // no action + } + + public class DataFlowSignalFinalMarker : EPDataFlowSignalFinalMarker + { + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/dataflow/ops/EPStatementSource.cs b/NEsper.Core/NEsper.Core/dataflow/ops/EPStatementSource.cs new file mode 100755 index 000000000..da56c5644 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/ops/EPStatementSource.cs @@ -0,0 +1,334 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.client.dataflow; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.compat.threading; +using com.espertech.esper.core.service; +using com.espertech.esper.dataflow.annotations; +using com.espertech.esper.dataflow.interfaces; + +namespace com.espertech.esper.dataflow.ops +{ + [DataFlowOperator] + public class EPStatementSource + : DataFlowSourceOperator + , DataFlowOpLifecycle + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + +#pragma warning disable 649 + [DataFlowOpParameterAttribute] private String statementName; + [DataFlowOpParameterAttribute] private EPDataFlowEPStatementFilter statementFilter; + [DataFlowOpParameterAttribute] private EPDataFlowIRStreamCollector collector; + [DataFlowContextAttribute] private EPDataFlowEmitter graphContext; +#pragma warning restore 649 + + private StatementLifecycleSvc _statementLifecycleSvc; + private readonly IDictionary _listeners = + new Dictionary(); + private readonly IBlockingQueue _emittables = new LinkedBlockingQueue(); + private bool _submitEventBean; + + private readonly ILockable _iLock = + LockManager.CreateLock(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly IThreadLocal _collectorDataTL = + ThreadLocalManager.Create(() => null); + + private readonly EventHandler _lifeCycleEventHandler; + + public EPStatementSource() + { + _lifeCycleEventHandler = Observe; + } + + public DataFlowOpInitializeResult Initialize(DataFlowOpInitializateContext context) + { + if (context.OutputPorts.Count != 1) + { + throw new ArgumentException("EPStatementSource operator requires one output stream but produces " + context.OutputPorts.Count + " streams"); + } + + if (statementName == null && statementFilter == null) + { + throw new EPException("Failed to find required 'StatementName' or 'StatementFilter' parameter"); + } + if (statementName != null && statementFilter != null) + { + throw new EPException("Both 'StatementName' or 'StatementFilter' parameters were provided, only either one is expected"); + } + + DataFlowOpOutputPort portZero = context.OutputPorts[0]; + if (portZero != null && portZero.OptionalDeclaredType != null && portZero.OptionalDeclaredType.IsWildcard) + { + _submitEventBean = true; + } + + _statementLifecycleSvc = context.ServicesContext.StatementLifecycleSvc; + return null; + } + + public void Next() + { + var next = _emittables.Pop(); + if (next is EPDataFlowSignal) + { + var signal = (EPDataFlowSignal)next; + graphContext.SubmitSignal(signal); + } + else if (next is PortAndMessagePair) + { + var pair = (PortAndMessagePair)next; + graphContext.SubmitPort(pair.Port, pair.Message); + } + else + { + graphContext.Submit(next); + } + } + + public void Open(DataFlowOpOpenContext openContext) + { + using (_iLock.Acquire()) + { + // start observing statement management + _statementLifecycleSvc.LifecycleEvent += _lifeCycleEventHandler; + + if (statementName != null) + { + EPStatement stmt = _statementLifecycleSvc.GetStatementByName(statementName); + if (stmt != null) + { + AddStatement(stmt); + } + } + else + { + String[] statements = _statementLifecycleSvc.StatementNames; + foreach (String name in statements) + { + EPStatement stmt = _statementLifecycleSvc.GetStatementByName(name); + if (statementFilter.Pass(stmt)) + { + AddStatement(stmt); + } + } + } + } + } + + public void Observe(Object sender, StatementLifecycleEvent theEvent) + { + using (_iLock.Acquire()) + { + EPStatement stmt = theEvent.Statement; + if (theEvent.EventType == StatementLifecycleEvent.LifecycleEventType.STATECHANGE) + { + if (theEvent.Statement.IsStopped || theEvent.Statement.IsDisposed) + { + var listener = _listeners.Delete(stmt); + if (listener != null) + { + stmt.Events -= listener; + } + } + if (theEvent.Statement.IsStarted) + { + if (statementFilter == null) + { + if (theEvent.Statement.Name.Equals(statementName)) + { + AddStatement(stmt); + } + } + else + { + if (statementFilter.Pass(stmt)) + { + AddStatement(stmt); + } + } + } + } + } + } + + public void Close(DataFlowOpCloseContext openContext) + { + foreach (KeyValuePair entry in _listeners) + { + try + { + entry.Key.Events -= entry.Value; + } + catch (Exception ex) + { + Log.Debug("Exception encountered removing listener: " + ex.Message, ex); + // possible + } + } + _listeners.Clear(); + } + + private void AddStatement(EPStatement stmt) + { + // statement may be added already + if (_listeners.ContainsKey(stmt)) + { + return; + } + + // attach listener + UpdateEventHandler updateEventHandler; + if (collector == null) + { + updateEventHandler = new EmitterUpdateListener( + _emittables, _submitEventBean).Update; + } + else + { + var emitterForCollector = new LocalEmitter(_emittables); + updateEventHandler = new EmitterCollectorUpdateListener( + collector, emitterForCollector, _collectorDataTL, _submitEventBean).Update; + } + stmt.Events += updateEventHandler; + + // save listener instance + _listeners.Put(stmt, updateEventHandler); + } + + public class EmitterUpdateListener : StatementAwareUpdateListener + { + private readonly IBlockingQueue _queue; + private readonly bool _submitEventBean; + + public EmitterUpdateListener(IBlockingQueue queue, bool submitEventBean) + { + _queue = queue; + _submitEventBean = submitEventBean; + } + + public void Update(Object sender, UpdateEventArgs e) + { + Update( + e.NewEvents, + e.OldEvents, + e.Statement, + e.ServiceProvider); + } + + public void Update(EventBean[] newEvents, EventBean[] oldEvents, EPStatement statement, EPServiceProvider epServiceProvider) + { + if (newEvents != null) + { + foreach (EventBean newEvent in newEvents) + { + if (_submitEventBean) + { + _queue.Push(newEvent); + } + else + { + Object underlying = newEvent.Underlying; + _queue.Push(underlying); + } + } + } + } + } + + public class EmitterCollectorUpdateListener : StatementAwareUpdateListener + { + private readonly EPDataFlowIRStreamCollector _collector; + private readonly LocalEmitter _emitterForCollector; + private readonly IThreadLocal _collectorDataTL; + private readonly bool _submitEventBean; + + public EmitterCollectorUpdateListener(EPDataFlowIRStreamCollector collector, LocalEmitter emitterForCollector, IThreadLocal collectorDataTL, bool submitEventBean) + { + _collector = collector; + _emitterForCollector = emitterForCollector; + _collectorDataTL = collectorDataTL; + _submitEventBean = submitEventBean; + } + + public void Update(Object sender, UpdateEventArgs e) + { + Update( + e.NewEvents, + e.OldEvents, + e.Statement, + e.ServiceProvider); + } + + public void Update(EventBean[] newEvents, EventBean[] oldEvents, EPStatement statement, EPServiceProvider epServiceProvider) + { + + EPDataFlowIRStreamCollectorContext holder = _collectorDataTL.GetOrCreate(); + if (holder == null) + { + holder = new EPDataFlowIRStreamCollectorContext(_emitterForCollector, _submitEventBean, newEvents, oldEvents, statement, epServiceProvider); + _collectorDataTL.Value = holder; + } + else + { + holder.ServiceProvider = epServiceProvider; + holder.Statement = statement; + holder.OldEvents = oldEvents; + holder.NewEvents = newEvents; + } + + _collector.Collect(holder); + } + } + + public class LocalEmitter : EPDataFlowEmitter + { + private readonly IBlockingQueue _queue; + + public LocalEmitter(IBlockingQueue queue) + { + _queue = queue; + } + + public void Submit(Object @object) + { + _queue.Push(@object); + } + + public void SubmitSignal(EPDataFlowSignal signal) + { + _queue.Push(signal); + } + + public void SubmitPort(int portNumber, Object @object) + { + _queue.Push(@object); + } + } + + public class PortAndMessagePair + { + public PortAndMessagePair(int port, Object message) + { + Port = port; + Message = message; + } + + public int Port { get; private set; } + + public object Message { get; private set; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/dataflow/ops/Emitter.cs b/NEsper.Core/NEsper.Core/dataflow/ops/Emitter.cs new file mode 100755 index 000000000..b6b7431f9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/ops/Emitter.cs @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client.dataflow; +using com.espertech.esper.dataflow.annotations; +using com.espertech.esper.dataflow.interfaces; + +namespace com.espertech.esper.dataflow.ops +{ + [DataFlowOperator] + [DataFlowOpProvideSignal] + public class Emitter : EPDataFlowEmitter + { + [DataFlowOpParameterAttribute] private String name; + [DataFlowContextAttribute] private EPDataFlowEmitter dataFlowEmitter; + + public Emitter() + { + name = null; + dataFlowEmitter = null; + } + + public void Submit(Object @object) + { + dataFlowEmitter.Submit(@object); + } + + public void SubmitSignal(EPDataFlowSignal signal) + { + dataFlowEmitter.SubmitSignal(signal); + } + + public void SubmitPort(int portNumber, Object @object) + { + dataFlowEmitter.SubmitPort(portNumber, @object); + } + + public string Name + { + get { return name; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/dataflow/ops/EventBusSink.cs b/NEsper.Core/NEsper.Core/dataflow/ops/EventBusSink.cs new file mode 100755 index 000000000..bc052dd99 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/ops/EventBusSink.cs @@ -0,0 +1,154 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Xml; +using System.Xml.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.client.dataflow; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.threading; +using com.espertech.esper.core.service; +using com.espertech.esper.dataflow.annotations; +using com.espertech.esper.dataflow.interfaces; +using com.espertech.esper.events; + +namespace com.espertech.esper.dataflow.ops +{ + [DataFlowOperator] + public class EventBusSink : DataFlowOpLifecycle + { + private EventAdapterService _eventAdapterService; + private EPRuntimeEventSender _runtimeEventSender; + + [DataFlowOpParameter] + private EPDataFlowEventCollector collector; + + private EventBusCollector _eventBusCollector; + private EventBeanAdapterFactory[] _adapterFactories; + + private readonly IThreadLocal _collectorDataTL = + ThreadLocalManager.Create(() => null); + + public DataFlowOpInitializeResult Initialize(DataFlowOpInitializateContext context) + { + if (!context.OutputPorts.IsEmpty()) + { + throw new ArgumentException("EventBusSink operator does not provide an output stream"); + } + + var eventTypes = new EventType[context.InputPorts.Count]; + for (int i = 0; i < eventTypes.Length; i++) + { + eventTypes[i] = context.InputPorts.Get(i).TypeDesc.EventType; + } + _runtimeEventSender = context.RuntimeEventSender; + _eventAdapterService = context.StatementContext.EventAdapterService; + + if (collector != null) + { + _eventBusCollector = new EventBusCollectorImpl( + _eventAdapterService, + _runtimeEventSender); + } + else + { + _adapterFactories = new EventBeanAdapterFactory[eventTypes.Length]; + for (int i = 0; i < eventTypes.Length; i++) + { + _adapterFactories[i] = context.ServicesContext.EventAdapterService.GetAdapterFactoryForType(eventTypes[i]); + } + } + return null; + } + + public void OnInput(int port, Object data) + { + if (_eventBusCollector != null) + { + EPDataFlowEventCollectorContext holder = _collectorDataTL.GetOrCreate(); + if (holder == null) + { + holder = new EPDataFlowEventCollectorContext(_eventBusCollector, data); + _collectorDataTL.Value = holder; + } + else + { + holder.Event = data; + } + collector.Collect(holder); + } + else + { + if (data is EventBean) + { + _runtimeEventSender.ProcessWrappedEvent((EventBean)data); + } + else + { + EventBean theEvent = _adapterFactories[port].MakeAdapter(data); + _runtimeEventSender.ProcessWrappedEvent(theEvent); + } + } + } + + public void Open(DataFlowOpOpenContext openContext) + { + // no action + } + + public void Close(DataFlowOpCloseContext openContext) + { + // no action + } + + private class EventBusCollectorImpl : EventBusCollector + { + private readonly EventAdapterService _eventAdapterService; + private readonly EPRuntimeEventSender _runtimeEventSender; + + public EventBusCollectorImpl(EventAdapterService eventAdapterService, EPRuntimeEventSender runtimeEventSender) + { + _eventAdapterService = eventAdapterService; + _runtimeEventSender = runtimeEventSender; + } + + public void SendEvent(Object @object) + { + EventBean theEvent = _eventAdapterService.AdapterForObject(@object); + _runtimeEventSender.ProcessWrappedEvent(theEvent); + } + + public void SendEvent(IDictionary map, String eventTypeName) + { + EventBean theEvent = _eventAdapterService.AdapterForMap(map, eventTypeName); + _runtimeEventSender.ProcessWrappedEvent(theEvent); + } + + public void SendEvent(Object[] objectArray, String eventTypeName) + { + EventBean theEvent = _eventAdapterService.AdapterForObjectArray(objectArray, eventTypeName); + _runtimeEventSender.ProcessWrappedEvent(theEvent); + } + + public void SendEvent(XmlNode node) + { + EventBean theEvent = _eventAdapterService.AdapterForDOM(node); + _runtimeEventSender.ProcessWrappedEvent(theEvent); + } + + public void SendEvent(XElement node) + { + EventBean theEvent = _eventAdapterService.AdapterForDOM(node); + _runtimeEventSender.ProcessWrappedEvent(theEvent); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/dataflow/ops/EventBusSource.cs b/NEsper.Core/NEsper.Core/dataflow/ops/EventBusSource.cs new file mode 100755 index 000000000..780e53093 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/ops/EventBusSource.cs @@ -0,0 +1,167 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.client.dataflow; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.threading; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.dataflow.annotations; +using com.espertech.esper.dataflow.interfaces; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.filter; + +namespace com.espertech.esper.dataflow.ops +{ + [DataFlowOperator] + public class EventBusSource + : DataFlowSourceOperator + , DataFlowOpLifecycle + , FilterHandleCallback + { +#pragma warning disable 649 + [DataFlowOpParameter] protected ExprNode filter; + [DataFlowOpParameter] protected EPDataFlowEventBeanCollector collector; + + [DataFlowContext] protected EPDataFlowEmitter graphContext; +#pragma warning restore 649 + + protected EventType eventType; + protected AgentInstanceContext agentInstanceContext; + protected EPStatementHandleCallback callbackHandle; + protected FilterServiceEntry filterServiceEntry; + protected readonly IBlockingQueue emittables = new LinkedBlockingQueue(); + protected bool submitEventBean; + + private readonly ILockable _iLock = + LockManager.CreateLock(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly IThreadLocal _collectorDataTL = + ThreadLocalManager.Create(() => null); + + public DataFlowOpInitializeResult Initialize(DataFlowOpInitializateContext context) + { + if (context.OutputPorts.Count != 1) + { + throw new ArgumentException("EventBusSource operator requires one output stream but produces " + context.OutputPorts.Count + " streams"); + } + + DataFlowOpOutputPort portZero = context.OutputPorts[0]; + if (portZero.OptionalDeclaredType == null || portZero.OptionalDeclaredType.EventType == null) + { + throw new ArgumentException("EventBusSource operator requires an event type declated for the output stream"); + } + + if (!portZero.OptionalDeclaredType.IsUnderlying) + { + submitEventBean = true; + } + eventType = portZero.OptionalDeclaredType.EventType; + agentInstanceContext = context.AgentInstanceContext; + + return new DataFlowOpInitializeResult(); + } + + public void Next() + { + Object next = emittables.Pop(); + graphContext.Submit(next); + } + + public void MatchFound(EventBean theEvent, ICollection allStmtMatches) + { + if (collector != null) + { + var holder = _collectorDataTL.GetOrCreate(); + if (holder == null) + { + holder = new EPDataFlowEventBeanCollectorContext(graphContext, submitEventBean, theEvent); + _collectorDataTL.Value = holder; + } + else + { + holder.Event = theEvent; + } + collector.Collect(holder); + } + else if (submitEventBean) + { + emittables.Push(theEvent); + } + else + { + emittables.Push(theEvent.Underlying); + } + } + + public bool IsSubSelect + { + get { return false; } + } + + public int StatementId + { + get { return agentInstanceContext.StatementId; } + } + + public void Open(DataFlowOpOpenContext openContext) + { + FilterValueSet valueSet; + try + { + IList filters = new ExprNode[0]; + if (filter != null) + { + filters = new ExprNode[] { filter }; + } + + var spec = FilterSpecCompiler.MakeFilterSpec( + eventType, eventType.Name, filters, null, + null, null, + new StreamTypeServiceImpl( + eventType, + eventType.Name, true, + agentInstanceContext.EngineURI), + null, + agentInstanceContext.StatementContext, + new List()); + valueSet = spec.GetValueSet(null, agentInstanceContext, null); + } + catch (ExprValidationException ex) + { + throw new EPException("Failed to open filter: " + ex.Message, ex); + } + + var handle = new EPStatementAgentInstanceHandle( + agentInstanceContext.StatementContext.EpStatementHandle, agentInstanceContext.AgentInstanceLock, 0, + new StatementAgentInstanceFilterVersion(), + agentInstanceContext.StatementContext.FilterFaultHandlerFactory); + callbackHandle = new EPStatementHandleCallback(handle, this); + filterServiceEntry = agentInstanceContext.StatementContext.FilterService.Add(valueSet, callbackHandle); + } + + public void Close(DataFlowOpCloseContext openContext) + { + using (_iLock.Acquire()) + { + if (callbackHandle != null) + { + agentInstanceContext.StatementContext.FilterService.Remove(callbackHandle, filterServiceEntry); + callbackHandle = null; + filterServiceEntry = null; + } + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/dataflow/ops/Filter.cs b/NEsper.Core/NEsper.Core/dataflow/ops/Filter.cs new file mode 100755 index 000000000..de0bb8f6a --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/ops/Filter.cs @@ -0,0 +1,104 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.dataflow.annotations; +using com.espertech.esper.dataflow.interfaces; +using com.espertech.esper.dataflow.util; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events; + +namespace com.espertech.esper.dataflow.ops +{ + [DataFlowOperator] + public class Filter : DataFlowOpLifecycle { + + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + [DataFlowOpParameter] + private ExprNode filter; + + private ExprEvaluator _evaluator; + private EventBeanSPI _theEvent; + private readonly EventBean[] _eventsPerStream = new EventBean[1]; + private bool _singleOutputPort; + + [DataFlowContext] + private EPDataFlowEmitter graphContext; + + public DataFlowOpInitializeResult Initialize(DataFlowOpInitializateContext prepareContext) { + + if (prepareContext.InputPorts.Count != 1) { + throw new ExprValidationException("Filter requires single input port"); + } + if (filter == null) { + throw new ExprValidationException("Required parameter 'filter' providing the filter expression is not provided"); + } + if (prepareContext.OutputPorts.IsEmpty() || prepareContext.OutputPorts.Count > 2) { + throw new ArgumentException("Filter operator requires one or two output Stream(s) but produces " + prepareContext.OutputPorts.Count + " streams"); + } + + EventType eventType = prepareContext.InputPorts[0].TypeDesc.EventType; + _singleOutputPort = prepareContext.OutputPorts.Count == 1; + + ExprNode validated = ExprNodeUtility.ValidateSimpleGetSubtree(ExprNodeOrigin.DATAFLOWFILTER, filter, prepareContext.StatementContext, eventType, false); + _evaluator = validated.ExprEvaluator; + _theEvent = prepareContext.ServicesContext.EventAdapterService.GetShellForType(eventType); + _eventsPerStream[0] = _theEvent; + + var typesPerPort = new GraphTypeDesc[prepareContext.OutputPorts.Count]; + for (int i = 0; i < typesPerPort.Length; i++) { + typesPerPort[i] = new GraphTypeDesc(false, true, eventType); + } + return new DataFlowOpInitializeResult(typesPerPort); + } + + public void OnInput(Object row) { + if (Log.IsDebugEnabled) { + Log.Debug("Received row for filtering: " + CompatExtensions.Render((Object[]) row)); + } + + if (!(row is EventBeanSPI)) { + _theEvent.Underlying = row; + } else { + _theEvent = (EventBeanSPI) row; + } + + var pass = _evaluator.Evaluate(new EvaluateParams(_eventsPerStream, true, null)); + if (pass != null && true.Equals(pass)) { + if (Log.IsDebugEnabled) { + Log.Debug("Submitting row " + CompatExtensions.Render((Object[])row)); + } + + if (_singleOutputPort) { + graphContext.Submit(row); + } else { + graphContext.SubmitPort(0, row); + } + } else { + if (!_singleOutputPort) { + graphContext.SubmitPort(1, row); + } + } + } + + public void Open(DataFlowOpOpenContext openContext) { + // no action + } + + public void Close(DataFlowOpCloseContext openContext) { + // no action + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/dataflow/ops/LogSink.cs b/NEsper.Core/NEsper.Core/dataflow/ops/LogSink.cs new file mode 100755 index 000000000..bce3831b1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/ops/LogSink.cs @@ -0,0 +1,320 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.client; +using com.espertech.esper.client.util; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.dataflow.annotations; +using com.espertech.esper.dataflow.interfaces; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events; + +namespace com.espertech.esper.dataflow.ops +{ + [DataFlowOperator] + public class LogSink : DataFlowOpLifecycle + { + private static readonly ILog Logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + [DataFlowOpParameter] + private string title; + [DataFlowOpParameter] + private string layout; + [DataFlowOpParameter] + private string format; + [DataFlowOpParameter] + private bool log = true; + [DataFlowOpParameter] + private bool linefeed = true; + + private String _dataflowName; + private String _dataFlowInstanceId; + private ConsoleOpRenderer _renderer; + + private EventBeanSPI[] _shellPerStream; + + public DataFlowOpInitializeResult Initialize(DataFlowOpInitializateContext context) + { + if (!context.OutputPorts.IsEmpty()) + { + throw new ArgumentException("LogSink operator does not provide an output stream"); + } + + _dataflowName = context.DataflowName; + _dataFlowInstanceId = context.DataflowInstanceId; + + _shellPerStream = new EventBeanSPI[context.InputPorts.Count]; + foreach (KeyValuePair entry in context.InputPorts) + { + EventType eventType = entry.Value.TypeDesc.EventType; + if (eventType != null) + { + _shellPerStream[entry.Key] = context.StatementContext.EventAdapterService.GetShellForType(eventType); + } + } + + if (format == null) + { + _renderer = new ConsoleOpRendererSummary(); + } + else + { + try + { + var formatEnum = EnumHelper.Parse(format.Trim()); + if (formatEnum == LogSinkOutputFormat.summary) + { + _renderer = new ConsoleOpRendererSummary(); + } + else + { + _renderer = new ConsoleOpRendererXmlJSon(formatEnum, context.Engine.EPRuntime); + } + } + catch (Exception ex) + { + throw new ExprValidationException("Format '" + format + "' is not supported, expecting any of " + EnumHelper.GetValues().Render()); + } + } + + return null; + } + + public void Open(DataFlowOpOpenContext openContext) + { + } + + public void Close(DataFlowOpCloseContext openContext) + { + } + + public void OnInput(int port, Object theEvent) + { + + String line; + if (layout == null) + { + + var writer = new StringWriter(); + + writer.Write("["); + writer.Write(_dataflowName); + writer.Write("] "); + + if (title != null) + { + writer.Write("["); + writer.Write(title); + writer.Write("] "); + } + + if (_dataFlowInstanceId != null) + { + writer.Write("["); + writer.Write(_dataFlowInstanceId); + writer.Write("] "); + } + + writer.Write("[port "); + writer.Write(Convert.ToString(port)); + writer.Write("] "); + + GetEventOut(port, theEvent, writer); + line = writer.ToString(); + } + else + { + String result = layout.Replace("%df", _dataflowName).Replace("%p", Convert.ToString(port)); + if (_dataFlowInstanceId != null) + { + result = result.Replace("%i", _dataFlowInstanceId); + } + if (title != null) + { + result = result.Replace("%t", title); + } + + var writer = new StringWriter(); + GetEventOut(port, theEvent, writer); + result = result.Replace("%e", writer.ToString()); + + line = result; + } + + if (!linefeed) + { + line = line.Replace("\n", "").Replace("\r", ""); + } + + // output + if (log) + { + Logger.Info(line); + } + else + { + Console.Out.WriteLine(line); + } + } + + private void GetEventOut(int port, Object theEvent, TextWriter writer) + { + + if (theEvent is EventBean) + { + _renderer.Render((EventBean)theEvent, writer); + return; + } + + if (_shellPerStream[port] != null) + { + lock (this) + { + _shellPerStream[port].Underlying = theEvent; + _renderer.Render(_shellPerStream[port], writer); + } + return; + } + + writer.Write("Unrecognized underlying: "); + writer.Write(theEvent.ToString()); + } + + public enum LogSinkOutputFormat + { + json, + xml, + summary + } + + public interface ConsoleOpRenderer + { + void Render(EventBean eventBean, TextWriter writer); + } + + public class ConsoleOpRendererSummary : ConsoleOpRenderer + { + public void Render(EventBean theEvent, TextWriter writer) + { + EventBeanUtility.Summarize(theEvent, writer); + } + } + + public class ConsoleOpRendererXmlJSon : ConsoleOpRenderer + { + private readonly LogSinkOutputFormat _format; + private readonly EPRuntime _runtime; + + private readonly IDictionary _jsonRendererCache = new Dictionary(); + private readonly IDictionary _xmlRendererCache = new Dictionary(); + + public ConsoleOpRendererXmlJSon(LogSinkOutputFormat format, EPRuntime runtime) + { + _format = format; + _runtime = runtime; + } + + public void Render(EventBean theEvent, TextWriter writer) + { + String result; + if (_format == LogSinkOutputFormat.json) + { + JSONEventRenderer renderer = _jsonRendererCache.Get(theEvent.EventType); + if (renderer == null) + { + renderer = GetJsonRenderer(theEvent.EventType); + _jsonRendererCache.Put(theEvent.EventType, renderer); + } + result = renderer.Render(theEvent.EventType.Name, theEvent); + } + else + { + XMLEventRenderer renderer = _xmlRendererCache.Get(theEvent.EventType); + if (renderer == null) + { + renderer = GetXmlRenderer(theEvent.EventType); + _xmlRendererCache.Put(theEvent.EventType, renderer); + } + result = renderer.Render(theEvent.EventType.Name, theEvent); + } + writer.Write(result); + } + + protected JSONEventRenderer GetJsonRenderer(EventType eventType) + { + return _runtime.EventRenderer.GetJSONRenderer(eventType, RenderingOptions.JsonOptions); + } + + protected XMLEventRenderer GetXmlRenderer(EventType eventType) + { + return _runtime.EventRenderer.GetXMLRenderer(eventType, RenderingOptions.XmlOptions); + } + } + + public static class RenderingOptions + { + public static XMLRenderingOptions XmlOptions; + public static JSONRenderingOptions JsonOptions; + + static RenderingOptions() + { + XmlOptions = new XMLRenderingOptions(); + XmlOptions.PreventLooping = true; + XmlOptions.Renderer = ConsoleOpEventPropertyRenderer.INSTANCE; + + JsonOptions = new JSONRenderingOptions(); + JsonOptions.PreventLooping = true; + JsonOptions.Renderer = ConsoleOpEventPropertyRenderer.INSTANCE; + } + + public static XMLRenderingOptions GetXmlOptions() + { + return XmlOptions; + } + + public static void SetXmlOptions(XMLRenderingOptions xmlOptions) + { + RenderingOptions.XmlOptions = xmlOptions; + } + + public static JSONRenderingOptions GetJsonOptions() + { + return JsonOptions; + } + + public static void SetJsonOptions(JSONRenderingOptions jsonOptions) + { + RenderingOptions.JsonOptions = jsonOptions; + } + } + + public class ConsoleOpEventPropertyRenderer : EventPropertyRenderer + { + public static ConsoleOpEventPropertyRenderer INSTANCE = new ConsoleOpEventPropertyRenderer(); + + public void Render(EventPropertyRendererContext context) + { + if (context.PropertyValue is Object[]) + { + context.StringBuilder.Append(CompatExtensions.Render(((Object[])context.PropertyValue))); + } + else + { + context.DefaultRenderer.Render(context.PropertyValue, context.StringBuilder); + } + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/dataflow/ops/Select.cs b/NEsper.Core/NEsper.Core/dataflow/ops/Select.cs new file mode 100755 index 000000000..3f3c1589e --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/ops/Select.cs @@ -0,0 +1,376 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.client.dataflow; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.activator; +using com.espertech.esper.core.context.factory; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.core.start; +using com.espertech.esper.dataflow.annotations; +using com.espertech.esper.dataflow.interfaces; +using com.espertech.esper.dataflow.ops.epl; +using com.espertech.esper.dataflow.util; +using com.espertech.esper.epl.agg.rollup; +using com.espertech.esper.epl.annotation; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.subquery; +using com.espertech.esper.epl.expression.visitor; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.spec.util; +using com.espertech.esper.epl.view; +using com.espertech.esper.events; +using com.espertech.esper.filter; +using com.espertech.esper.util; + +namespace com.espertech.esper.dataflow.ops +{ + [DataFlowOperator] + public class Select : OutputProcessViewCallback, DataFlowOpLifecycle + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + [DataFlowOpParameter] + private StatementSpecRaw select; + + [DataFlowOpParameter] + private bool iterate; + + private EPLSelectViewable[] _viewablesPerPort; + private EventBeanAdapterFactory[] _adapterFactories; + private AgentInstanceContext _agentInstanceContext; + private EPLSelectDeliveryCallback _deliveryCallback; + private StatementAgentInstanceFactorySelectResult _selectResult; + private bool _isOutputLimited; + private bool _submitEventBean; + + [DataFlowContext] + private EPDataFlowEmitter graphContext; + + public DataFlowOpInitializeResult Initialize(DataFlowOpInitializateContext context) + { + if (context.InputPorts.IsEmpty()) + { + throw new ArgumentException("Select operator requires at least one input stream"); + } + if (context.OutputPorts.Count != 1) + { + throw new ArgumentException("Select operator requires one output stream but produces " + context.OutputPorts.Count + " streams"); + } + + DataFlowOpOutputPort portZero = context.OutputPorts[0]; + if (portZero.OptionalDeclaredType != null && !portZero.OptionalDeclaredType.IsUnderlying) + { + _submitEventBean = true; + } + + // determine adapter factories for each type + int numStreams = context.InputPorts.Count; + _adapterFactories = new EventBeanAdapterFactory[numStreams]; + for (int i = 0; i < numStreams; i++) + { + EventType eventType = context.InputPorts.Get(i).TypeDesc.EventType; + _adapterFactories[i] = context.StatementContext.EventAdapterService.GetAdapterFactoryForType(eventType); + } + + // Compile and prepare execution + // + StatementContext statementContext = context.StatementContext; + EPServicesContext servicesContext = context.ServicesContext; + AgentInstanceContext agentInstanceContext = context.AgentInstanceContext; + + // validate + if (select.InsertIntoDesc != null) + { + throw new ExprValidationException("Insert-into clause is not supported"); + } + if (select.SelectStreamSelectorEnum != SelectClauseStreamSelectorEnum.ISTREAM_ONLY) + { + throw new ExprValidationException("Selecting remove-stream is not supported"); + } + ExprNodeSubselectDeclaredDotVisitor visitor = StatementSpecRawAnalyzer.WalkSubselectAndDeclaredDotExpr(select); + GroupByClauseExpressions groupByExpressions = GroupByExpressionHelper.GetGroupByRollupExpressions( + select.GroupByExpressions, + select.SelectClauseSpec, + select.HavingExprRootNode, + select.OrderByList, + visitor); + if (!visitor.Subselects.IsEmpty()) + { + throw new ExprValidationException("Subselects are not supported"); + } + + IDictionary streams = new Dictionary(); + for (int streamNum = 0; streamNum < select.StreamSpecs.Count; streamNum++) + { + var rawStreamSpec = select.StreamSpecs[streamNum]; + if (!(rawStreamSpec is FilterStreamSpecRaw)) + { + throw new ExprValidationException("From-clause must contain only streams and cannot contain patterns or other constructs"); + } + streams.Put(streamNum, (FilterStreamSpecRaw)rawStreamSpec); + } + + // compile offered streams + IList streamSpecCompileds = new List(); + for (int streamNum = 0; streamNum < select.StreamSpecs.Count; streamNum++) + { + var filter = streams.Get(streamNum); + var inputPort = FindInputPort(filter.RawFilterSpec.EventTypeName, context.InputPorts); + if (inputPort == null) + { + throw new ExprValidationException( + string.Format("Failed to find stream '{0}' among input ports, input ports are {1}", filter.RawFilterSpec.EventTypeName, GetInputPortNames(context.InputPorts).Render(", ", "[]"))); + } + var eventType = inputPort.Value.Value.TypeDesc.EventType; + var streamAlias = filter.OptionalStreamName; + var filterSpecCompiled = new FilterSpecCompiled(eventType, streamAlias, new IList[] { Collections.GetEmptyList() }, null); + var filterStreamSpecCompiled = new FilterStreamSpecCompiled(filterSpecCompiled, select.StreamSpecs[0].ViewSpecs, streamAlias, StreamSpecOptions.DEFAULT); + streamSpecCompileds.Add(filterStreamSpecCompiled); + } + + // create compiled statement spec + SelectClauseSpecCompiled selectClauseCompiled = StatementLifecycleSvcUtil.CompileSelectClause(select.SelectClauseSpec); + + // determine if snapshot output is needed + OutputLimitSpec outputLimitSpec = select.OutputLimitSpec; + _isOutputLimited = outputLimitSpec != null; + if (iterate) + { + if (outputLimitSpec != null) + { + throw new ExprValidationException("Output rate limiting is not supported with 'iterate'"); + } + outputLimitSpec = new OutputLimitSpec(OutputLimitLimitType.SNAPSHOT, OutputLimitRateType.TERM); + } + + var mergedAnnotations = AnnotationUtil.MergeAnnotations(statementContext.Annotations, context.OperatorAnnotations); + var orderByArray = OrderByItem.ToArray(select.OrderByList); + var outerJoinArray = OuterJoinDesc.ToArray(select.OuterJoinDescList); + var streamSpecArray = streamSpecCompileds.ToArray(); + var compiled = new StatementSpecCompiled(null, null, null, null, null, null, null, SelectClauseStreamSelectorEnum.ISTREAM_ONLY, + selectClauseCompiled, streamSpecArray, outerJoinArray, select.FilterExprRootNode, select.HavingExprRootNode, outputLimitSpec, + orderByArray, ExprSubselectNode.EMPTY_SUBSELECT_ARRAY, ExprNodeUtility.EMPTY_DECLARED_ARR, ExprNodeUtility.EMPTY_SCRIPTS, select.ReferencedVariables, + select.RowLimitSpec, CollectionUtil.EMPTY_STRING_ARRAY, mergedAnnotations, null, null, null, null, null, null, null, null, null, groupByExpressions, null, null); + + // create viewable per port + var viewables = new EPLSelectViewable[context.InputPorts.Count]; + _viewablesPerPort = viewables; + foreach (var entry in context.InputPorts) + { + EPLSelectViewable viewable = new EPLSelectViewable(entry.Value.TypeDesc.EventType); + viewables[entry.Key] = viewable; + } + + var activatorFactory = new ProxyViewableActivatorFactory + { + ProcCreateActivatorSimple = filterStreamSpec => + { + EPLSelectViewable found = null; + foreach (EPLSelectViewable sviewable in viewables) + { + if (sviewable.EventType == filterStreamSpec.FilterSpec.FilterForEventType) + { + found = sviewable; + } + } + if (found == null) + { + throw new IllegalStateException("Failed to find viewable for filter"); + } + EPLSelectViewable viewable = found; + return new ProxyViewableActivator( + (agentInstanceContext2, isSubselect, isRecoveringResilient) => + new ViewableActivationResult( + viewable, + new ProxyStopCallback(() => { }), + null, + null, + null, + false, + false, + null)); + } + }; + + // for per-row deliver, register select expression result callback + OutputProcessViewCallback optionalOutputProcessViewCallback = null; + if (!iterate && !_isOutputLimited) + { + _deliveryCallback = new EPLSelectDeliveryCallback(); + optionalOutputProcessViewCallback = this; + } + + // prepare + EPStatementStartMethodSelectDesc selectDesc = EPStatementStartMethodSelectUtil.Prepare(compiled, servicesContext, statementContext, false, agentInstanceContext, false, activatorFactory, optionalOutputProcessViewCallback, _deliveryCallback); + + // start + _selectResult = (StatementAgentInstanceFactorySelectResult)selectDesc.StatementAgentInstanceFactorySelect.NewContext(agentInstanceContext, false); + + // for output-rate-limited, register a dispatch view + if (_isOutputLimited) + { + _selectResult.FinalView.AddView(new EPLSelectUpdateDispatchView(this)); + } + + // assign strategies to expression nodes + EPStatementStartMethodHelperAssignExpr.AssignExpressionStrategies( + selectDesc, + _selectResult.OptionalAggegationService, + _selectResult.SubselectStrategies, + _selectResult.PriorNodeStrategies, + _selectResult.PreviousNodeStrategies, + null, + null, + _selectResult.TableAccessEvalStrategies); + + EventType outputEventType = selectDesc.ResultSetProcessorPrototypeDesc.ResultSetProcessorFactory.ResultEventType; + _agentInstanceContext = agentInstanceContext; + return new DataFlowOpInitializeResult(new GraphTypeDesc[] { new GraphTypeDesc(false, true, outputEventType) }); + } + + private String[] GetInputPortNames(IDictionary inputPorts) + { + IList portNames = new List(); + foreach (var entry in inputPorts) + { + if (entry.Value.OptionalAlias != null) + { + portNames.Add(entry.Value.OptionalAlias); + continue; + } + if (entry.Value.StreamNames.Count == 1) + { + portNames.Add(entry.Value.StreamNames.FirstOrDefault()); + } + } + return portNames.ToArray(); + } + + private KeyValuePair? FindInputPort(String eventTypeName, IDictionary inputPorts) + { + foreach (var entry in inputPorts) + { + if (entry.Value.OptionalAlias != null && entry.Value.OptionalAlias.Equals(eventTypeName)) + { + return entry; + } + if (entry.Value.StreamNames.Count == 1 && (entry.Value.StreamNames.FirstOrDefault() == eventTypeName)) + { + return entry; + } + } + return null; + } + + public void OnInput(int originatingStream, Object row) + { + if (Log.IsDebugEnabled) + { + Log.Debug("Received row from stream " + originatingStream + " for select, row is " + row); + } + + EventBean theEvent = _adapterFactories[originatingStream].MakeAdapter(row); + + using (_agentInstanceContext.StatementContext.DefaultAgentInstanceLock.AcquireWriteLock()) + { + try + { + _viewablesPerPort[originatingStream].Process(theEvent); + if (_viewablesPerPort.Length > 1) + { + _agentInstanceContext.EpStatementAgentInstanceHandle.OptionalDispatchable.Execute(); + } + } + finally + { + if (_agentInstanceContext.StatementContext.EpStatementHandle.HasTableAccess) + { + _agentInstanceContext.StatementContext.TableExprEvaluatorContext.ReleaseAcquiredLocks(); + } + } + } + } + + public void OnSignal(EPDataFlowSignal signal) + { + if (iterate && signal is EPDataFlowSignalFinalMarker) + { + IEnumerator it = _selectResult.FinalView.GetEnumerator(); + if (it != null) + { + for (; it.MoveNext(); ) + { + var @event = it.Current; + if (_submitEventBean) + { + graphContext.Submit(@event); + } + else + { + graphContext.Submit(@event.Underlying); + } + } + } + } + } + + public void Open(DataFlowOpOpenContext openContext) + { + // no action + } + + public void Close(DataFlowOpCloseContext openContext) + { + if (_selectResult != null) + { + StatementAgentInstanceUtil.StopSafe(_selectResult.StopCallback, _agentInstanceContext.StatementContext); + } + } + + public void OutputViaCallback(EventBean[] events) + { + var delivered = _deliveryCallback.Delivered; + if (Log.IsDebugEnabled) + { + Log.Debug("Submitting select-output row: " + delivered.Render()); + } + graphContext.Submit(_deliveryCallback.Delivered); + _deliveryCallback.Reset(); + } + + public void OutputOutputRateLimited(UniformPair result) + { + if (result == null || result.First == null || result.First.Length == 0) + { + return; + } + foreach (EventBean item in result.First) + { + if (_submitEventBean) + { + graphContext.Submit(item); + } + else + { + graphContext.Submit(item.Underlying); + } + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/dataflow/ops/epl/EPLSelectDeliveryCallback.cs b/NEsper.Core/NEsper.Core/dataflow/ops/epl/EPLSelectDeliveryCallback.cs new file mode 100755 index 000000000..4d0120c89 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/ops/epl/EPLSelectDeliveryCallback.cs @@ -0,0 +1,31 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.core; + +namespace com.espertech.esper.dataflow.ops.epl +{ + public class EPLSelectDeliveryCallback : SelectExprProcessorDeliveryCallback + { + public EventBean Selected(Object[] result) + { + Delivered = result; + return null; + } + + public void Reset() + { + Delivered = null; + } + + public object[] Delivered { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/dataflow/ops/epl/EPLSelectUpdateDispatchView.cs b/NEsper.Core/NEsper.Core/dataflow/ops/epl/EPLSelectUpdateDispatchView.cs new file mode 100755 index 000000000..a164f4c47 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/ops/epl/EPLSelectUpdateDispatchView.cs @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.service; +using com.espertech.esper.view; + + +namespace com.espertech.esper.dataflow.ops.epl +{ + public class EPLSelectUpdateDispatchView : ViewSupport, UpdateDispatchView + { + private readonly Select _select; + + public EPLSelectUpdateDispatchView(Select select) { + _select = select; + } + + public void NewResult(UniformPair result) { + _select.OutputOutputRateLimited(result); + } + + public override void Update(EventBean[] newData, EventBean[] oldData) { + } + + public override EventType EventType + { + get { return null; } + } + + public override IEnumerator GetEnumerator() + { + return EnumerationHelper.Empty(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/dataflow/ops/epl/EPLSelectViewable.cs b/NEsper.Core/NEsper.Core/dataflow/ops/epl/EPLSelectViewable.cs new file mode 100755 index 000000000..d44e6e8b1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/ops/epl/EPLSelectViewable.cs @@ -0,0 +1,56 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.view; + + +namespace com.espertech.esper.dataflow.ops.epl +{ + public class EPLSelectViewable : ViewSupport + { + private View _childView; + private readonly EventBean[] _eventBatch = new EventBean[1]; + private readonly EventType _eventType; + + public EPLSelectViewable(EventType eventType) + { + _eventType = eventType; + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + return; + } + + public override EventType EventType + { + get { return _eventType; } + } + + public override IEnumerator GetEnumerator() + { + return EnumerationHelper.Empty(); + } + + public void Process(EventBean theEvent) + { + _eventBatch[0] = theEvent; + _childView.Update(_eventBatch, null); + } + + public override View AddView(View view) + { + _childView = view; + return base.AddView(view); + } + } +} diff --git a/NEsper.Core/NEsper.Core/dataflow/runnables/BaseRunnable.cs b/NEsper.Core/NEsper.Core/dataflow/runnables/BaseRunnable.cs new file mode 100755 index 000000000..4dd00edb4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/runnables/BaseRunnable.cs @@ -0,0 +1,16 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.dataflow.runnables +{ + public interface BaseRunnable + { + void Shutdown(); + void Run(); + } +} diff --git a/NEsper.Core/NEsper.Core/dataflow/runnables/CompletionListener.cs b/NEsper.Core/NEsper.Core/dataflow/runnables/CompletionListener.cs new file mode 100755 index 000000000..3cc4a4b05 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/runnables/CompletionListener.cs @@ -0,0 +1,12 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.dataflow.runnables +{ + public delegate void CompletionListener(); +} diff --git a/NEsper.Core/NEsper.Core/dataflow/runnables/GraphSourceRunnable.cs b/NEsper.Core/NEsper.Core/dataflow/runnables/GraphSourceRunnable.cs new file mode 100755 index 000000000..bbf6641e6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/runnables/GraphSourceRunnable.cs @@ -0,0 +1,182 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Threading; + +using com.espertech.esper.client.annotation; +using com.espertech.esper.client.dataflow; +using com.espertech.esper.compat.logging; +using com.espertech.esper.dataflow.interfaces; +using com.espertech.esper.dataflow.util; +using com.espertech.esper.util; + +namespace com.espertech.esper.dataflow.runnables +{ + public class GraphSourceRunnable + : BaseRunnable, + DataFlowSignalListener + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly String _engineUri; + private readonly String _statementName; + private readonly DataFlowSourceOperator _graphSource; + private readonly String _dataFlowName; + private readonly String _operatorName; + private readonly int _operatorNumber; + private readonly String _operatorPrettyPrint; + private readonly EPDataFlowExceptionHandler _optionalExceptionHandler; + private readonly bool _audit; + + private bool _shutdown; + private IList _completionListeners; + + public GraphSourceRunnable( + String engineURI, + String statementName, + DataFlowSourceOperator graphSource, + String dataFlowName, + String operatorName, + int operatorNumber, + String operatorPrettyPrint, + EPDataFlowExceptionHandler optionalExceptionHandler, + bool audit) + { + _engineUri = engineURI; + _statementName = statementName; + _graphSource = graphSource; + _dataFlowName = dataFlowName; + _operatorName = operatorName; + _operatorNumber = operatorNumber; + _operatorPrettyPrint = operatorPrettyPrint; + _optionalExceptionHandler = optionalExceptionHandler; + _audit = audit; + } + + public void ProcessSignal(EPDataFlowSignal signal) + { + if (signal is EPDataFlowSignalFinalMarker) + { + _shutdown = true; + } + } + + public void Run() + { + try + { + RunLoop(); + } + catch (ThreadInterruptedException ex) + { + Log.Debug("Interruped runnable: " + ex.Message, ex); + } + catch (Exception ex) + { + Log.Error("Exception encountered: " + ex.Message, ex); + HandleException(ex); + } + + InvokeCompletionListeners(); + } + + public void RunSync() + { + try + { + RunLoop(); + } + catch (ThreadInterruptedException ex) + { + Log.Debug("Interruped runnable: " + ex.Message, ex); + throw; + } + catch (Exception ex) + { + Log.Error("Exception encountered: " + ex.Message, ex); + HandleException(ex); + throw; + } + } + + private void HandleException(Exception ex) + { + if (_optionalExceptionHandler == null) + { + return; + } + + _optionalExceptionHandler.Handle( + new EPDataFlowExceptionContext(_dataFlowName, _operatorName, _operatorNumber, _operatorPrettyPrint, ex)); + } + + private void RunLoop() + { + while (true) + { + if (_audit) + { + AuditPath.AuditLog( + _engineUri, _statementName, AuditEnum.DATAFLOW_SOURCE, + "dataflow " + _dataFlowName + " operator " + _operatorName + "(" + _operatorNumber + + ") invoking source.Next()"); + } + _graphSource.Next(); + + if (_shutdown) + { + break; + } + } + } + + private void InvokeCompletionListeners() + { + lock (this) + { + if (_completionListeners != null) + { + foreach (CompletionListener listener in _completionListeners) + { + listener.Invoke(); + } + } + } + } + + public void AddCompletionListener(CompletionListener completionListener) + { + lock (this) + { + if (_completionListeners == null) + { + _completionListeners = new List(); + } + _completionListeners.Add(completionListener); + } + } + + public void Next() + { + _graphSource.Next(); + } + + public void Shutdown() + { + _shutdown = true; + } + + public bool IsShutdown() + { + return _shutdown; + } + } +} diff --git a/NEsper.Core/NEsper.Core/dataflow/util/DataFlowSignalListener.cs b/NEsper.Core/NEsper.Core/dataflow/util/DataFlowSignalListener.cs new file mode 100755 index 000000000..b6934a5f7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/util/DataFlowSignalListener.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using com.espertech.esper.client.dataflow; + +namespace com.espertech.esper.dataflow.util +{ + public interface DataFlowSignalListener + { + void ProcessSignal(EPDataFlowSignal signal); + } + + public class ProxyDataFlowSignalListener : DataFlowSignalListener + { + public Action ProcSignal { get; set; } + + public ProxyDataFlowSignalListener() + { + } + + public ProxyDataFlowSignalListener(Action procSignal) + { + ProcSignal = procSignal; + } + + public void ProcessSignal(EPDataFlowSignal signal) + { + ProcSignal.Invoke(signal); + } + } +} diff --git a/NEsper.Core/NEsper.Core/dataflow/util/DataFlowSignalManager.cs b/NEsper.Core/NEsper.Core/dataflow/util/DataFlowSignalManager.cs new file mode 100755 index 000000000..ff07ccfef --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/util/DataFlowSignalManager.cs @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client.dataflow; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.dataflow.util +{ + public class DataFlowSignalManager + { + private readonly IDictionary> _listenersPerOp = + new Dictionary>(); + + public void ProcessSignal(int operatorNum, EPDataFlowSignal signal) + { + IList listeners = _listenersPerOp.Get(operatorNum); + if (listeners == null || listeners.IsEmpty()) + { + return; + } + foreach (DataFlowSignalListener listener in listeners) + { + listener.ProcessSignal(signal); + } + } + + public void AddSignalListener(int producerOpNum, DataFlowSignalListener listener) + { + IList listeners = _listenersPerOp.Get(producerOpNum); + if (listeners == null) + { + listeners = new List(); + _listenersPerOp.Put(producerOpNum, listeners); + } + listeners.Add(listener); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/dataflow/util/DefaultSupportCaptureOp.cs b/NEsper.Core/NEsper.Core/dataflow/util/DefaultSupportCaptureOp.cs new file mode 100755 index 000000000..030f497c2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/util/DefaultSupportCaptureOp.cs @@ -0,0 +1,172 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; + +using com.espertech.esper.client.dataflow; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.threading; +using com.espertech.esper.dataflow.annotations; +using com.espertech.esper.dataflow.interfaces; + +namespace com.espertech.esper.dataflow.util +{ + [DataFlowOperator] + public class DefaultSupportCaptureOp : DefaultSupportCaptureOp + { + public DefaultSupportCaptureOp() + { + } + + public DefaultSupportCaptureOp(int latchedNumRows) : base(latchedNumRows) + { + } + } + + [DataFlowOperator] + public class DefaultSupportCaptureOp + : EPDataFlowSignalHandler + , Future + { + private IList> _received = new List>(); + private IList _current = new List(); + + private readonly CountDownLatch _numRowLatch; + + private readonly ILockable _iLock = + LockManager.CreateLock(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + public DefaultSupportCaptureOp() + : this(0) + { + } + + public DefaultSupportCaptureOp(int latchedNumRows) + { + _numRowLatch = new CountDownLatch(latchedNumRows); + } + + public void OnInput(T theEvent) + { + using (_iLock.Acquire()) + { + _current.Add(theEvent); + if (_numRowLatch != null) + { + _numRowLatch.CountDown(); + } + } + } + + public void OnSignal(EPDataFlowSignal signal) + { + _received.Add(_current); + _current = new List(); + } + + public IList> GetAndReset() + { + using (_iLock.Acquire()) + { + IList> resultEvents = _received; + _received = new List>(); + _current.Clear(); + return resultEvents; + } + } + + public Object[] GetCurrent() + { + using (_iLock.Acquire()) + { + return _current.UnwrapIntoArray(); + } + } + + public Object[] GetCurrentAndReset() + { + using (_iLock.Acquire()) + { + Object[] currentArray = _current.UnwrapIntoArray(); + _current.Clear(); + return currentArray; + } + } + + public bool HasValue + { + get { return _numRowLatch.Count <= 0; } + } + + public bool IsDone() + { + return _numRowLatch.Count <= 0; + } + + public object[] GetValueOrDefault() + { + return !IsDone() ? null : GetCurrent(); + } + + public Object[] GetValue(TimeSpan timeOut) + { + bool result = _numRowLatch.Await(timeOut); + if (!result) + { + throw new TimeoutException("latch timed out"); + } + return GetCurrent(); + } + + public Object[] GetPunctuated() + { + var result = _numRowLatch.Await(TimeSpan.FromSeconds(1)); + if (!result) { + throw new TimeoutException("latch timed out"); + } + return _received[0].Cast().ToArray(); + } + + /// Wait for the listener invocation for up to the given number of milliseconds. + /// to wait + /// in any number of separate invocations required before returning + /// RuntimeException when no results or insufficient number of events were received + public void WaitForInvocation(long msecWait, int numberOfNewEvents) + { + long startTime = DateTimeHelper.CurrentTimeMillis; + while (true) + { + using (_iLock.Acquire()) + { + if ((DateTimeHelper.CurrentTimeMillis - startTime) > msecWait) + { + throw new ApplicationException("No events or less then the number of expected events received, expected " + numberOfNewEvents + " received " + _current.Count); + } + + if (_current.Count >= numberOfNewEvents) + { + return; + } + } + + try + { + Thread.Sleep(50); + } + catch (ThreadInterruptedException e) + { + return; + } + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/dataflow/util/DefaultSupportCaptureOpStatic.cs b/NEsper.Core/NEsper.Core/dataflow/util/DefaultSupportCaptureOpStatic.cs new file mode 100755 index 000000000..26c76d89a --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/util/DefaultSupportCaptureOpStatic.cs @@ -0,0 +1,54 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client.dataflow; +using com.espertech.esper.dataflow.annotations; +using com.espertech.esper.dataflow.interfaces; + +namespace com.espertech.esper.dataflow.util +{ + [DataFlowOperator] + public class DefaultSupportCaptureOpStatic + : EPDataFlowSignalHandler + { + private readonly List _current = new List(); + + public DefaultSupportCaptureOpStatic() + { + Instances.Add(this); + } + + static DefaultSupportCaptureOpStatic() + { + Instances = new List(); + } + + public void OnInput(object theEvent) + { + lock (this) + { + _current.Add(theEvent); + } + } + + public void OnSignal(EPDataFlowSignal signal) + { + _current.Add(signal); + } + + public static List Instances { get; private set; } + + public IList GetCurrent() + { + return _current; + } + } +} diff --git a/NEsper.Core/NEsper.Core/dataflow/util/DefaultSupportGraphEventUtil.cs b/NEsper.Core/NEsper.Core/dataflow/util/DefaultSupportGraphEventUtil.cs new file mode 100755 index 000000000..cd9f5524d --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/util/DefaultSupportGraphEventUtil.cs @@ -0,0 +1,177 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; +using System.Xml; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.events; +using com.espertech.esper.events.arr; +using com.espertech.esper.events.bean; +using com.espertech.esper.events.map; +using com.espertech.esper.events.xml; + +namespace com.espertech.esper.dataflow.util +{ + public class DefaultSupportGraphEventUtil + { + public static readonly String CLASSLOADER_SCHEMA_URI = "regression/threeProperties.xsd"; + + public static void AddTypeConfiguration(EPServiceProvider epService) + { + var propertyTypes = new LinkedHashMap(); + propertyTypes.Put("myDouble", typeof(Double)); + propertyTypes.Put("myInt", typeof(int)); + propertyTypes.Put("myString", typeof(String)); + epService.EPAdministrator.Configuration.AddEventType("MyMapEvent", propertyTypes); + epService.EPAdministrator.Configuration.AddEventType("MyOAEvent", "myDouble,myInt,myString".Split(','), new Object[] { typeof(double), typeof(int), typeof(string) }); + epService.EPAdministrator.Configuration.AddEventType(typeof(MyEvent)); + epService.EPAdministrator.Configuration.AddEventType("MyXMLEvent", GetConfig()); + } + + public static SendableEvent[] XMLEventsSendable + { + get + { + Object[] xmlEvents = XMLEvents; + SendableEvent[] xmls = new SendableEvent[xmlEvents.Length]; + for (int i = 0; i < xmlEvents.Length; i++) + { + xmls[i] = new SendableEventXML((XmlNode) xmlEvents[i]); + } + return xmls; + } + } + + public static SendableEvent[] OAEventsSendable + { + get + { + Object[] oaEvents = OAEvents; + SendableEvent[] oas = new SendableEvent[oaEvents.Length]; + for (int i = 0; i < oaEvents.Length; i++) + { + oas[i] = new SendableEventObjectArray((Object[]) oaEvents[i], "MyOAEvent"); + } + return oas; + } + } + + public static SendableEvent[] MapEventsSendable + { + get + { + Object[] mapEvents = MapEvents; + SendableEvent[] sendables = new SendableEvent[mapEvents.Length]; + for (int i = 0; i < mapEvents.Length; i++) + { + sendables[i] = new SendableEventMap((IDictionary) mapEvents[i], "MyMapEvent"); + } + return sendables; + } + } + + public static SendableEvent[] PONOEventsSendable + { + get + { + Object[] ponoEvents = PONOEvents; + SendableEvent[] sendables = new SendableEvent[ponoEvents.Length]; + for (int i = 0; i < ponoEvents.Length; i++) + { + sendables[i] = new SendableEventBean(ponoEvents[i]); + } + return sendables; + } + } + + public static object[] XMLEvents + { + get { return new Object[] {MakeXMLEvent(1.1d, 1, "one"), MakeXMLEvent(2.2d, 2, "two")}; } + } + + public static object[] OAEvents + { + get { return new Object[] {new Object[] {1.1d, 1, "one"}, new Object[] {2.2d, 2, "two"}}; } + } + + public static object[] MapEvents + { + get { return new Object[] {MakeMapEvent(1.1, 1, "one"), MakeMapEvent(2.2d, 2, "two")}; } + } + + public static object[] PONOEvents + { + get { return new Object[] {new MyEvent(1.1d, 1, "one"), new MyEvent(2.2d, 2, "two")}; } + } + + private static ConfigurationEventTypeXMLDOM GetConfig() + { + ConfigurationEventTypeXMLDOM eventTypeMeta = new ConfigurationEventTypeXMLDOM(); + eventTypeMeta.RootElementName = "rootelement"; + var schemaStream = ResourceManager.GetResourceAsStream(CLASSLOADER_SCHEMA_URI); + if (schemaStream == null) + { + throw new IllegalStateException("Failed to load schema '" + CLASSLOADER_SCHEMA_URI + "'"); + } + + var reader = new StreamReader(schemaStream); + var schemaText = reader.ReadToEnd(); + eventTypeMeta.SchemaText = schemaText; + return eventTypeMeta; + } + + private static XmlNode MakeXMLEvent(double myDouble, int myInt, String myString) + { + String xml = ""; + xml = xml.Replace("VAL_DBL", Convert.ToString(myDouble)); + xml = xml.Replace("VAL_INT", Convert.ToString(myInt)); + xml = xml.Replace("VAL_STR", myString); + + try + { + XmlDocument document = new XmlDocument(); + document.LoadXml(xml); + return document.DocumentElement; + } + catch (Exception e) + { + throw new Exception("Failed to parse '" + xml + "' as XML: " + e.Message, e); + } + } + + private static IDictionary MakeMapEvent(double myDouble, int myInt, String myString) + { + IDictionary map = new Dictionary(); + map.Put("myDouble", myDouble); + map.Put("myInt", myInt); + map.Put("myString", myString); + return map; + } + + public class MyEvent + { + public int MyInt { get; private set; } + + public double MyDouble { get; private set; } + + public string MyString { get; private set; } + + public MyEvent(double myDouble, int myInt, String myString) + { + MyDouble = myDouble; + MyInt = myInt; + MyString = myString; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/dataflow/util/DefaultSupportGraphOpProvider.cs b/NEsper.Core/NEsper.Core/dataflow/util/DefaultSupportGraphOpProvider.cs new file mode 100755 index 000000000..b39019bc7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/util/DefaultSupportGraphOpProvider.cs @@ -0,0 +1,42 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Linq; +using com.espertech.esper.client.dataflow; + +namespace com.espertech.esper.dataflow.util +{ + public class DefaultSupportGraphOpProvider : EPDataFlowOperatorProvider + { + private readonly Object[] _ops; + + public DefaultSupportGraphOpProvider(Object op) + { + _ops = new Object[] { op }; + } + + public DefaultSupportGraphOpProvider(params Object[] ops) + { + _ops = ops; + } + + public Object Provide(EPDataFlowOperatorProviderContext context) + { + for (var ii = 0; ii < _ops.Length; ii++) + { + if (context.OperatorName == _ops[ii].GetType().Name) + { + return _ops[ii]; + } + } + + return null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/dataflow/util/DefaultSupportGraphOpProviderByOpName.cs b/NEsper.Core/NEsper.Core/dataflow/util/DefaultSupportGraphOpProviderByOpName.cs new file mode 100755 index 000000000..171bd03e0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/util/DefaultSupportGraphOpProviderByOpName.cs @@ -0,0 +1,31 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client.dataflow; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.dataflow.util +{ + public class DefaultSupportGraphOpProviderByOpName : EPDataFlowOperatorProvider + { + private readonly IDictionary _names; + + public DefaultSupportGraphOpProviderByOpName(IDictionary names) + { + _names = names; + } + + public Object Provide(EPDataFlowOperatorProviderContext context) + { + return _names.Get(context.OperatorName); + } + } +} diff --git a/NEsper.Core/NEsper.Core/dataflow/util/DefaultSupportGraphParamProvider.cs b/NEsper.Core/NEsper.Core/dataflow/util/DefaultSupportGraphParamProvider.cs new file mode 100755 index 000000000..0575fa4f8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/util/DefaultSupportGraphParamProvider.cs @@ -0,0 +1,31 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client.dataflow; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.dataflow.util +{ + public class DefaultSupportGraphParamProvider : EPDataFlowOperatorParameterProvider + { + private readonly IDictionary _params; + + public DefaultSupportGraphParamProvider(IDictionary @params) + { + _params = @params; + } + + public Object Provide(EPDataFlowOperatorParameterProviderContext context) + { + return _params.Get(context.ParameterName); + } + } +} diff --git a/NEsper.Core/NEsper.Core/dataflow/util/DefaultSupportSourceOp.cs b/NEsper.Core/NEsper.Core/dataflow/util/DefaultSupportSourceOp.cs new file mode 100755 index 000000000..9905572c7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/util/DefaultSupportSourceOp.cs @@ -0,0 +1,93 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Threading; +using com.espertech.esper.client.dataflow; +using com.espertech.esper.compat; +using com.espertech.esper.compat.threading; +using com.espertech.esper.dataflow.annotations; +using com.espertech.esper.dataflow.interfaces; + +namespace com.espertech.esper.dataflow.util +{ + [DataFlowOpProvideSignal] + public class DefaultSupportSourceOp : DataFlowSourceOperator + { + public Object[] Instructions; + + public DefaultSupportSourceOp() + { + Instructions = new Object[0]; + } + + public DefaultSupportSourceOp(Object[] instructions) + { + Instructions = instructions; + } + + [DataFlowContext] +#pragma warning disable 649 + private EPDataFlowEmitter _graphContext; +#pragma warning restore 649 + + private int _currentCount = -1; + + public int GetCurrentCount() + { + return _currentCount; + } + + public void Next() + { + _currentCount++; + if (Instructions.Length <= _currentCount) + { + _graphContext.SubmitSignal(new DataFlowSignalFinalMarker()); + return; + } + + Object next = Instructions[_currentCount]; + if (next is CountDownLatch) + { + CountDownLatch latch = (CountDownLatch)next; + latch.Await(); + } + else if (next.IsInt() || next.IsLong()) + { + Thread.Sleep(next.AsInt()); + } + else if (next is Exception) + { + var ex = (Exception)next; + throw new Exception("Support-graph-source generated exception: " + ex.Message, ex); + } + else + { + _graphContext.Submit(next); + } + } + + public DataFlowOpInitializeResult Initialize(DataFlowOpInitializateContext context) + { + return null; + } + + public void Open(DataFlowOpOpenContext openContext) + { + } + + public void Close(DataFlowOpCloseContext openContext) + { + } + + private class DataFlowSignalFinalMarker : EPDataFlowSignalFinalMarker + { + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/dataflow/util/GraphOperatorContext.cs b/NEsper.Core/NEsper.Core/dataflow/util/GraphOperatorContext.cs new file mode 100755 index 000000000..acd822d92 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/util/GraphOperatorContext.cs @@ -0,0 +1,19 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.dataflow.util +{ + public class GraphOperatorContext + { + public void Submit(Object[] objects) + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/dataflow/util/GraphTypeDesc.cs b/NEsper.Core/NEsper.Core/dataflow/util/GraphTypeDesc.cs new file mode 100755 index 000000000..233ec69a6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/util/GraphTypeDesc.cs @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; + +namespace com.espertech.esper.dataflow.util +{ + public class GraphTypeDesc + { + public GraphTypeDesc(bool wildcard, bool underlying, EventType eventType) + { + IsWildcard = wildcard; + IsUnderlying = underlying; + EventType = eventType; + } + + public bool IsWildcard { get; private set; } + + public bool IsUnderlying { get; private set; } + + public EventType EventType { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/dataflow/util/LogicalChannel.cs b/NEsper.Core/NEsper.Core/dataflow/util/LogicalChannel.cs new file mode 100755 index 000000000..5fa7b3811 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/util/LogicalChannel.cs @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.dataflow.util +{ + /// Models a pipe between two operators. + public class LogicalChannel + { + public LogicalChannel(int channelId, String consumingOpName, int consumingOpNum, int consumingOpStreamNum, String consumingOpStreamName, String consumingOptStreamAliasName, String consumingOpPrettyPrint, LogicalChannelProducingPortCompiled outputPort) + { + ChannelId = channelId; + ConsumingOpName = consumingOpName; + ConsumingOpNum = consumingOpNum; + ConsumingOpStreamNum = consumingOpStreamNum; + ConsumingOpStreamName = consumingOpStreamName; + ConsumingOptStreamAliasName = consumingOptStreamAliasName; + ConsumingOpPrettyPrint = consumingOpPrettyPrint; + OutputPort = outputPort; + } + + public int ChannelId { get; private set; } + + public string ConsumingOpName { get; private set; } + + public string ConsumingOpStreamName { get; private set; } + + public string ConsumingOptStreamAliasName { get; private set; } + + public int ConsumingOpStreamNum { get; private set; } + + public int ConsumingOpNum { get; private set; } + + public LogicalChannelProducingPortCompiled OutputPort { get; private set; } + + public string ConsumingOpPrettyPrint { get; private set; } + + public override String ToString() + { + return "LogicalChannel{" + + "channelId=" + ChannelId + + ", produced=" + OutputPort + + ", consumed='" + ConsumingOpPrettyPrint + '\'' + + '}'; + } + } +} diff --git a/NEsper.Core/NEsper.Core/dataflow/util/LogicalChannelBinding.cs b/NEsper.Core/NEsper.Core/dataflow/util/LogicalChannelBinding.cs new file mode 100755 index 000000000..94df8be23 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/util/LogicalChannelBinding.cs @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.dataflow.util +{ + public class LogicalChannelBinding + { + public LogicalChannelBinding(LogicalChannel logicalChannel, + LogicalChannelBindingMethodDesc consumingBindingDesc, + LogicalChannelBindingMethodDesc consumingSignalBindingDesc) + { + LogicalChannel = logicalChannel; + ConsumingBindingDesc = consumingBindingDesc; + ConsumingSignalBindingDesc = consumingSignalBindingDesc; + } + + public LogicalChannel LogicalChannel { get; private set; } + + public LogicalChannelBindingMethodDesc ConsumingBindingDesc { get; private set; } + + public LogicalChannelBindingMethodDesc ConsumingSignalBindingDesc { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/dataflow/util/LogicalChannelBindingMethodDesc.cs b/NEsper.Core/NEsper.Core/dataflow/util/LogicalChannelBindingMethodDesc.cs new file mode 100755 index 000000000..a5bbe71fa --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/util/LogicalChannelBindingMethodDesc.cs @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Reflection; + +namespace com.espertech.esper.dataflow.util +{ + public class LogicalChannelBindingMethodDesc + { + public LogicalChannelBindingMethodDesc(MethodInfo method, LogicalChannelBindingType bindingType) + { + Method = method; + BindingType = bindingType; + } + + public MethodInfo Method { get; private set; } + + public LogicalChannelBindingType BindingType { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/dataflow/util/LogicalChannelBindingType.cs b/NEsper.Core/NEsper.Core/dataflow/util/LogicalChannelBindingType.cs new file mode 100755 index 000000000..08d36684d --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/util/LogicalChannelBindingType.cs @@ -0,0 +1,14 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.dataflow.util +{ + public interface LogicalChannelBindingType + { + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/dataflow/util/LogicalChannelBindingTypeCast.cs b/NEsper.Core/NEsper.Core/dataflow/util/LogicalChannelBindingTypeCast.cs new file mode 100755 index 000000000..fb01aa094 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/util/LogicalChannelBindingTypeCast.cs @@ -0,0 +1,22 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.dataflow.util +{ + public class LogicalChannelBindingTypeCast : LogicalChannelBindingType + { + public LogicalChannelBindingTypeCast(Type target) + { + Target = target; + } + + public Type Target { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/dataflow/util/LogicalChannelBindingTypeCastWStreamNum.cs b/NEsper.Core/NEsper.Core/dataflow/util/LogicalChannelBindingTypeCastWStreamNum.cs new file mode 100755 index 000000000..f71cb3241 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/util/LogicalChannelBindingTypeCastWStreamNum.cs @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.dataflow.util +{ + public class LogicalChannelBindingTypeCastWStreamNum : LogicalChannelBindingType + { + public LogicalChannelBindingTypeCastWStreamNum(Type target, int streamNum) + { + Target = target; + StreamNum = streamNum; + } + + public int StreamNum { get; private set; } + + public Type Target { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/dataflow/util/LogicalChannelBindingTypeObject.cs b/NEsper.Core/NEsper.Core/dataflow/util/LogicalChannelBindingTypeObject.cs new file mode 100755 index 000000000..c8d656af2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/util/LogicalChannelBindingTypeObject.cs @@ -0,0 +1,22 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.dataflow.util +{ + public class LogicalChannelBindingTypeObject : LogicalChannelBindingType + { + public LogicalChannelBindingTypeObject(Type targetCast) + { + TargetCast = targetCast; + } + + public Type TargetCast { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/dataflow/util/LogicalChannelBindingTypeObjectWStreamNum.cs b/NEsper.Core/NEsper.Core/dataflow/util/LogicalChannelBindingTypeObjectWStreamNum.cs new file mode 100755 index 000000000..f42c1da10 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/util/LogicalChannelBindingTypeObjectWStreamNum.cs @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.dataflow.util +{ + public class LogicalChannelBindingTypeObjectWStreamNum : LogicalChannelBindingType + { + public LogicalChannelBindingTypeObjectWStreamNum(int streamNum) + { + StreamNum = streamNum; + } + + public int StreamNum { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/dataflow/util/LogicalChannelBindingTypePassAlong.cs b/NEsper.Core/NEsper.Core/dataflow/util/LogicalChannelBindingTypePassAlong.cs new file mode 100755 index 000000000..d7b31a4e4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/util/LogicalChannelBindingTypePassAlong.cs @@ -0,0 +1,16 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.dataflow.util +{ + public class LogicalChannelBindingTypePassAlong : LogicalChannelBindingType + { + public static readonly LogicalChannelBindingType INSTANCE = + new LogicalChannelBindingTypePassAlong(); + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/dataflow/util/LogicalChannelBindingTypePassAlongWStream.cs b/NEsper.Core/NEsper.Core/dataflow/util/LogicalChannelBindingTypePassAlongWStream.cs new file mode 100755 index 000000000..4c6dce551 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/util/LogicalChannelBindingTypePassAlongWStream.cs @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.dataflow.util +{ + public class LogicalChannelBindingTypePassAlongWStream : LogicalChannelBindingType + { + public LogicalChannelBindingTypePassAlongWStream(int streamNum) + { + StreamNum = streamNum; + } + + public int StreamNum { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/dataflow/util/LogicalChannelBindingTypeUnwind.cs b/NEsper.Core/NEsper.Core/dataflow/util/LogicalChannelBindingTypeUnwind.cs new file mode 100755 index 000000000..b6445f586 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/util/LogicalChannelBindingTypeUnwind.cs @@ -0,0 +1,16 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.dataflow.util +{ + public class LogicalChannelBindingTypeUnwind : LogicalChannelBindingType + { + public static readonly LogicalChannelBindingType INSTANCE = + new LogicalChannelBindingTypeUnwind(); + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/dataflow/util/LogicalChannelProducingPortCompiled.cs b/NEsper.Core/NEsper.Core/dataflow/util/LogicalChannelProducingPortCompiled.cs new file mode 100755 index 000000000..10a36fb4c --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/util/LogicalChannelProducingPortCompiled.cs @@ -0,0 +1,52 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.dataflow.util +{ + public class LogicalChannelProducingPortCompiled + { + public LogicalChannelProducingPortCompiled(int producingOpNum, + String producingOpPrettyPrint, + String streamName, + int streamNumber, + GraphTypeDesc graphTypeDesc, + bool hasPunctuation) + { + ProducingOpNum = producingOpNum; + ProducingOpPrettyPrint = producingOpPrettyPrint; + StreamName = streamName; + StreamNumber = streamNumber; + GraphTypeDesc = graphTypeDesc; + HasPunctuation = hasPunctuation; + } + + public string ProducingOpPrettyPrint { get; private set; } + + public int ProducingOpNum { get; private set; } + + public string StreamName { get; private set; } + + public int StreamNumber { get; private set; } + + public bool HasPunctuation { get; private set; } + + public GraphTypeDesc GraphTypeDesc { get; private set; } + + public override String ToString() + { + return "LogicalChannelProducingPort{" + + "op=" + ProducingOpPrettyPrint + '\'' + + ", streamName='" + StreamName + '\'' + + ", portNumber=" + StreamNumber + + ", hasPunctuation=" + HasPunctuation + + '}'; + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/dataflow/util/LogicalChannelProducingPortDeclared.cs b/NEsper.Core/NEsper.Core/dataflow/util/LogicalChannelProducingPortDeclared.cs new file mode 100755 index 000000000..961d58a8a --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/util/LogicalChannelProducingPortDeclared.cs @@ -0,0 +1,52 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.dataflow.util +{ + public class LogicalChannelProducingPortDeclared + { + public LogicalChannelProducingPortDeclared(int producingOpNum, + String producingOpPrettyPrint, + String streamName, + int streamNumber, + GraphTypeDesc typeDesc, + bool hasPunctuation) + { + ProducingOpNum = producingOpNum; + ProducingOpPrettyPrint = producingOpPrettyPrint; + StreamName = streamName; + StreamNumber = streamNumber; + TypeDesc = typeDesc; + HasPunctuation = hasPunctuation; + } + + public string ProducingOpPrettyPrint { get; private set; } + + public int ProducingOpNum { get; private set; } + + public string StreamName { get; private set; } + + public int StreamNumber { get; private set; } + + public bool HasPunctuation { get; private set; } + + public GraphTypeDesc TypeDesc { get; private set; } + + public override String ToString() + { + return "LogicalChannelProducingPortSpec{" + + "op=" + ProducingOpPrettyPrint + '\'' + + ", streamName='" + StreamName + '\'' + + ", portNumber=" + StreamNumber + + ", hasPunctuation=" + HasPunctuation + + '}'; + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/dataflow/util/LogicalChannelUtil.cs b/NEsper.Core/NEsper.Core/dataflow/util/LogicalChannelUtil.cs new file mode 100755 index 000000000..7111eb722 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/util/LogicalChannelUtil.cs @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.dataflow.util +{ + public class LogicalChannelUtil + { + public static List GetBindingsConsuming(int producerOpNum, IList bindings) + { + return bindings.Where(binding => binding.LogicalChannel.OutputPort.ProducingOpNum == producerOpNum).ToList(); + } + + public static String PrintChannels(IList channels) + { + var writer = new StringWriter(); + writer.Write("\n"); + foreach (LogicalChannel channel in channels) + { + writer.WriteLine(channel); + } + return writer.ToString(); + } + + public static List GetOutputPortByStreamName(ICollection incomingOpNums, String[] inputStreamNames, IDictionary> compiledOutputPorts) + { + var ports = new List(); + foreach (int @operator in incomingOpNums) + { + var opPorts = compiledOutputPorts.Get(@operator); + if (opPorts != null) + { // Can be null if referring to itself + foreach (LogicalChannelProducingPortCompiled opPort in opPorts) + { + ports.AddRange(inputStreamNames.Where(name => name == opPort.StreamName).Select(streamName => opPort)); + } + } + } + return ports; + } + } +} diff --git a/NEsper.Core/NEsper.Core/dataflow/util/OperatorDependencyEntry.cs b/NEsper.Core/NEsper.Core/dataflow/util/OperatorDependencyEntry.cs new file mode 100755 index 000000000..8762b03ad --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/util/OperatorDependencyEntry.cs @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.dataflow.util +{ + public class OperatorDependencyEntry { + public OperatorDependencyEntry() { + Incoming = new LinkedHashSet(); + Outgoing = new LinkedHashSet(); + } + + public void AddIncoming(int num) { + Incoming.Add(num); + } + + public void AddOutgoing(int num) { + Outgoing.Add(num); + } + + public ICollection Incoming { get; private set; } + + public ICollection Outgoing { get; private set; } + + public override String ToString() { + return "OperatorDependencyEntry{" + + "incoming=" + Incoming.Render() + + ", outgoing=" + Outgoing.Render() + + '}'; + } + } +} diff --git a/NEsper.Core/NEsper.Core/dataflow/util/OperatorDependencyUtil.cs b/NEsper.Core/NEsper.Core/dataflow/util/OperatorDependencyUtil.cs new file mode 100755 index 000000000..5753df642 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/util/OperatorDependencyUtil.cs @@ -0,0 +1,30 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.dataflow.util +{ + public class OperatorDependencyUtil + { + public static ICollection Roots(IDictionary dependencyEntryMap) + { + ICollection roots = new HashSet(); + foreach (KeyValuePair entry in dependencyEntryMap) + { + if (entry.Value.Incoming.IsEmpty()) + { + roots.Add(entry.Key); + } + } + return roots; + } + } +} diff --git a/NEsper.Core/NEsper.Core/dataflow/util/OperatorIncomingPipeDesc.cs b/NEsper.Core/NEsper.Core/dataflow/util/OperatorIncomingPipeDesc.cs new file mode 100755 index 000000000..04a02bd91 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/util/OperatorIncomingPipeDesc.cs @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +namespace com.espertech.esper.dataflow.util +{ + public class OperatorIncomingPipeDesc { + public OperatorIncomingPipeDesc(int incomingPortNum, IList sources) { + IncomingPortNum = incomingPortNum; + Sources = sources; + } + + public int IncomingPortNum { get; private set; } + + public IList Sources { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/dataflow/util/OperatorMetadataDescriptor.cs b/NEsper.Core/NEsper.Core/dataflow/util/OperatorMetadataDescriptor.cs new file mode 100755 index 000000000..a27c33dd6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/util/OperatorMetadataDescriptor.cs @@ -0,0 +1,52 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.dataflow.util +{ + public class OperatorMetadataDescriptor + { + public OperatorMetadataDescriptor(GraphOperatorSpec operatorSpec, + int operatorNumber, + Type operatorClass, + Type operatorFactoryClass, + Object optionalOperatorObject, + String operatorPrettyPrint, + Attribute[] operatorAnnotations) + { + OperatorSpec = operatorSpec; + OperatorNumber = operatorNumber; + OperatorClass = operatorClass; + OperatorFactoryClass = operatorFactoryClass; + OptionalOperatorObject = optionalOperatorObject; + OperatorPrettyPrint = operatorPrettyPrint; + OperatorAnnotations = operatorAnnotations; + } + + public GraphOperatorSpec OperatorSpec { get; private set; } + + public string OperatorName + { + get { return OperatorSpec.OperatorName; } + } + + public Type OperatorClass { get; private set; } + + public Type OperatorFactoryClass { get; private set; } + + public object OptionalOperatorObject { get; private set; } + + public int OperatorNumber { get; private set; } + + public string OperatorPrettyPrint { get; private set; } + + public Attribute[] OperatorAnnotations { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/dataflow/util/OperatorPortRepo.cs b/NEsper.Core/NEsper.Core/dataflow/util/OperatorPortRepo.cs new file mode 100755 index 000000000..c1e612b14 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/util/OperatorPortRepo.cs @@ -0,0 +1,35 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace com.espertech.esper.dataflow.util +{ + public class OperatorPortRepo + { + public OperatorPortRepo() + { + InputPorts = new List(); + OutputPorts = new List(); + } + + public List InputPorts { get; private set; } + + public List OutputPorts { get; private set; } + + public override String ToString() + { + return "OperatorPorts{" + + "inputPorts=" + InputPorts + + ", outputPorts=" + OutputPorts + + '}'; + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/dataflow/util/PortDesc.cs b/NEsper.Core/NEsper.Core/dataflow/util/PortDesc.cs new file mode 100755 index 000000000..5e1e79a1e --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/util/PortDesc.cs @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +namespace com.espertech.esper.dataflow.util +{ + public class PortDesc + { + public PortDesc(int @operator, MethodInfo optionalMethod) + { + Operator = @operator; + OptionalMethod = optionalMethod; + } + + public int Operator { get; private set; } + + public MethodInfo OptionalMethod { get; private set; } + + public override String ToString() + { + return "{" + + "operator=" + Operator + + ", method=" + OptionalMethod + + '}'; + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/dataflow/util/PunctuationEventListenerImpl.cs b/NEsper.Core/NEsper.Core/dataflow/util/PunctuationEventListenerImpl.cs new file mode 100755 index 000000000..6d2bf4f04 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dataflow/util/PunctuationEventListenerImpl.cs @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client.dataflow; +using com.espertech.esper.dataflow.runnables; + +namespace com.espertech.esper.dataflow.util +{ + public class PunctuationEventListenerImpl : DataFlowSignalListener + { + private readonly OperatorMetadataDescriptor myOperator; + + private BaseRunnable _runnable; + + public PunctuationEventListenerImpl(OperatorMetadataDescriptor myOperator) + { + this.myOperator = myOperator; + } + + public void SetRunnable(BaseRunnable runnable) + { + _runnable = runnable; + } + + public void ProcessSignal(EPDataFlowSignal signal) + { + if (signal is EPDataFlowSignalFinalMarker) + { + if (_runnable != null) + { + _runnable.Shutdown(); + } + } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/dispatch/DispatchService.cs b/NEsper.Core/NEsper.Core/dispatch/DispatchService.cs new file mode 100755 index 000000000..70ec4e990 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dispatch/DispatchService.cs @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +namespace com.espertech.esper.dispatch +{ + /// + /// Service for dispatching internally (for operators/views processing results of prior operators/views) + /// and externally (dispatch events to UpdateListener implementations). + /// + /// The service accepts Dispatchable implementations to its internal and external lists. + /// When a client invokes dispatch the implementation first invokes all internal Dispatchable + /// instances then all external Dispatchable instances. Dispatchables are invoked + /// in the same order they are added. Any dispatchable added twice is dispatched once. + /// + /// + /// Note: Each execution thread owns its own dispatch queue. + /// + /// + /// Note: Dispatchs could result in further call to the dispatch service. This is because listener code + /// that is invoked as a result of a dispatch may create patterns that fire as soon as they are Started + /// resulting in further dispatches within the same thread. Thus the implementation class must be careful + /// with the use of iterators to avoid ConcurrentModificationException errors. + /// + /// + public interface DispatchService + { + /// Add a Dispatchable implementation. + /// to execute later + /// + void AddExternal(Dispatchable dispatchable); + + /// Execute all Dispatchable implementations added to the service since the last invocation of this method. + void Dispatch(); + } +} diff --git a/NEsper.Core/NEsper.Core/dispatch/DispatchServiceImpl.cs b/NEsper.Core/NEsper.Core/dispatch/DispatchServiceImpl.cs new file mode 100755 index 000000000..3d1efeafd --- /dev/null +++ b/NEsper.Core/NEsper.Core/dispatch/DispatchServiceImpl.cs @@ -0,0 +1,91 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using com.espertech.esper.compat.logging; +using com.espertech.esper.compat.threading; +using com.espertech.esper.util; + +namespace com.espertech.esper.dispatch +{ + /// + /// Implements dispatch service using a thread-local linked list of Dispatchable instances. + /// + public class DispatchServiceImpl : DispatchService + { + private readonly IThreadLocal> _threadDispatchQueue; + + /// + /// Initializes a new instance of the class. + /// + public DispatchServiceImpl() + { + _threadDispatchQueue = ThreadLocalManager.Create( + () => new Queue()); + } + + /// + /// Dispatches events in the queue. + /// + + public void Dispatch() + { + DispatchFromQueue(_threadDispatchQueue.Value); + } + + public void DispatchFromQueue(Queue dispatchQueue) + { + if (dispatchQueue == null) + { + return; + } + + if (IsDebugEnabled) + { + Log.Debug(".DispatchFromQueue Dispatch queue is " + dispatchQueue.Count + " elements"); + } + + try + { + int count = dispatchQueue.Count; + while (--count >= 0) + { + dispatchQueue.Dequeue().Execute(); + } + } + catch (InvalidOperationException) + { + } + } + + /// + /// Add an item to be dispatched. The item is added to + /// the external dispatch queue. + /// + /// to execute later + public void AddExternal(Dispatchable dispatchable) + { + _threadDispatchQueue + .GetOrCreate() + .Enqueue(dispatchable); + } + + private static readonly bool IsDebugEnabled; + + static DispatchServiceImpl() + { + IsDebugEnabled = + ExecutionPathDebugLog.IsEnabled && + ExecutionPathDebugLog.IsTimerDebugEnabled && + Log.IsDebugEnabled; + } + + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + } +} diff --git a/NEsper.Core/NEsper.Core/dispatch/DispatchServiceProvider.cs b/NEsper.Core/NEsper.Core/dispatch/DispatchServiceProvider.cs new file mode 100755 index 000000000..c4505c267 --- /dev/null +++ b/NEsper.Core/NEsper.Core/dispatch/DispatchServiceProvider.cs @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +namespace com.espertech.esper.dispatch +{ + /// Provider of implementations for the dispatch service. + public class DispatchServiceProvider + { + /// Returns new service. + /// new dispatch service implementation. + /// + public static DispatchService NewService() + { + return new DispatchServiceImpl(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/dispatch/Dispatchable.cs b/NEsper.Core/NEsper.Core/dispatch/Dispatchable.cs new file mode 100755 index 000000000..a105f416a --- /dev/null +++ b/NEsper.Core/NEsper.Core/dispatch/Dispatchable.cs @@ -0,0 +1,22 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +namespace com.espertech.esper.dispatch +{ + /// + /// Implementations are executed when the DispatchService receives a dispatch invocation. + /// + public interface Dispatchable + { + /// + /// Execute dispatch. + /// + void Execute(); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/access/AggregationAccessor.cs b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationAccessor.cs new file mode 100755 index 000000000..4ba568512 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationAccessor.cs @@ -0,0 +1,66 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.agg.access +{ + /// + /// Accessor for access aggregation functions. + /// + public interface AggregationAccessor + { + /// + /// Return the aggregation state value either as a scalar value or any other object. + /// + /// For enumeration over scalar values or objects return an array or collection of + /// scalar or object values. + /// + /// Use the #getEnumerableEvents method to return a collection of events. + /// + /// Use the #getEnumerableEvent to return a single events. + /// + /// aggregation state, downcast as needed + /// the evaluation parameters. + /// + /// return value + /// + object GetValue(AggregationState state, EvaluateParams evalParams); + + /// + /// Return the aggregation state value consisting of a collection of events. + /// + /// aggregation state, downcast as needed + /// the evaluation parameters. + ICollection GetEnumerableEvents(AggregationState state, EvaluateParams evalParams); + + /// + /// Return the aggregation state value consisting of a single event. + /// + /// aggregation state, downcast as needed + /// the evaluation parameters. + /// + /// return event or null + /// + EventBean GetEnumerableEvent(AggregationState state, EvaluateParams evalParams); + + /// + /// Return the aggregation state value consisting of a collection of scalar values. + /// + /// aggregation state, downcast as needed + /// the evaluation parameters. + /// + /// return collection of scalar or null or empty collection + /// + ICollection GetEnumerableScalar(AggregationState state, EvaluateParams evalParams); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/access/AggregationAccessorFirstLastIndexNoEval.cs b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationAccessorFirstLastIndexNoEval.cs new file mode 100755 index 000000000..78a1d35b7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationAccessorFirstLastIndexNoEval.cs @@ -0,0 +1,80 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.agg.access +{ + public class AggregationAccessorFirstLastIndexNoEval : AggregationAccessor + { + private readonly ExprEvaluator _indexNode; + private readonly int _constant; + private readonly bool _isFirst; + + public AggregationAccessorFirstLastIndexNoEval(ExprEvaluator indexNode, int constant, bool first) + { + _indexNode = indexNode; + _constant = constant; + _isFirst = first; + } + + public object GetValue(AggregationState state, EvaluateParams evalParams) + { + EventBean bean = GetBean(state); + if (bean == null) { + return null; + } + return bean.Underlying; + } + + public ICollection GetEnumerableEvents(AggregationState state, EvaluateParams evalParams) + { + EventBean bean = GetBean(state); + if (bean == null) { + return null; + } + return Collections.SingletonList(bean); + } + + public ICollection GetEnumerableScalar(AggregationState state, EvaluateParams evalParams) { + object value = GetValue(state, evalParams); + if (value == null) { + return null; + } + return Collections.SingletonList(value); + } + + public EventBean GetEnumerableEvent(AggregationState state, EvaluateParams evalParams) { + return GetBean(state); + } + + private EventBean GetBean(AggregationState state) { + EventBean bean; + int index = _constant; + if (index == -1) { + object result = _indexNode.Evaluate(new EvaluateParams(null, true, null)); + if ((result == null) || (!(result is int))) { + return null; + } + index = result.AsInt(); + } + if (_isFirst) { + bean = ((AggregationStateLinear) state).GetFirstNthValue(index); + } + else { + bean = ((AggregationStateLinear) state).GetLastNthValue(index); + } + return bean; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/access/AggregationAccessorFirstLastIndexWEval.cs b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationAccessorFirstLastIndexWEval.cs new file mode 100755 index 000000000..33ca81f21 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationAccessorFirstLastIndexWEval.cs @@ -0,0 +1,97 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.agg.access +{ + /// + /// Represents the aggregation accessor that provides the result for the "first" and "last" aggregation function with index. + /// + public class AggregationAccessorFirstLastIndexWEval : AggregationAccessor + { + private readonly int _streamNum; + private readonly ExprEvaluator _childNode; + private readonly EventBean[] _eventsPerStream; + private readonly ExprEvaluator _indexNode; + private readonly int _constant; + private readonly bool _isFirst; + + /// + /// Ctor. + /// + /// stream id + /// expression + /// index expression + /// constant index + /// true if returning first, false for returning last + public AggregationAccessorFirstLastIndexWEval(int streamNum, ExprEvaluator childNode, ExprEvaluator indexNode, int constant, bool isFirst) + { + _streamNum = streamNum; + _childNode = childNode; + _indexNode = indexNode; + _eventsPerStream = new EventBean[streamNum + 1]; + _constant = constant; + _isFirst = isFirst; + } + + public object GetValue(AggregationState state, EvaluateParams evalParams) + { + var bean = GetBean(state); + if (bean == null) { + return null; + } + _eventsPerStream[_streamNum] = bean; + return _childNode.Evaluate(new EvaluateParams(_eventsPerStream, true, null)); + } + + public ICollection GetEnumerableEvents(AggregationState state, EvaluateParams evalParams) { + var bean = GetBean(state); + if (bean == null) { + return null; + } + return Collections.SingletonList(bean); + } + + public ICollection GetEnumerableScalar(AggregationState state, EvaluateParams evalParams) { + var value = GetValue(state, evalParams); + if (value == null) { + return null; + } + return Collections.SingletonList(value); + } + + public EventBean GetEnumerableEvent(AggregationState state, EvaluateParams evalParams) { + return GetBean(state); + } + + private EventBean GetBean(AggregationState state) { + EventBean bean; + var index = _constant; + if (index == -1) { + var result = _indexNode.Evaluate(new EvaluateParams(null, true, null)); + if ((result == null) || (!(result is int))) { + return null; + } + index = result.AsInt(); + } + if (_isFirst) { + bean = ((AggregationStateLinear) state).GetFirstNthValue(index); + } + else { + bean = ((AggregationStateLinear) state).GetLastNthValue(index); + } + return bean; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/access/AggregationAccessorFirstNoEval.cs b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationAccessorFirstNoEval.cs new file mode 100755 index 000000000..3274d18aa --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationAccessorFirstNoEval.cs @@ -0,0 +1,55 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.agg.access +{ + /// + /// Represents the aggregation accessor that provides the result for the "first" aggregation function without index. + /// + public class AggregationAccessorFirstNoEval : AggregationAccessor { + public readonly static AggregationAccessorFirstNoEval INSTANCE = new AggregationAccessorFirstNoEval(); + + public object GetValue(AggregationState state, EvaluateParams evalParams) + { + EventBean bean = ((AggregationStateLinear) state).FirstValue; + if (bean == null) { + return null; + } + return bean.Underlying; + } + + public ICollection GetEnumerableEvents(AggregationState state, EvaluateParams evalParams) { + EventBean bean = ((AggregationStateLinear) state).FirstValue; + if (bean == null) { + return null; + } + return Collections.SingletonList(bean); + } + + public ICollection GetEnumerableScalar(AggregationState state, EvaluateParams evalParams) + { + object value = GetValue(state, evalParams); + if (value == null) { + return null; + } + return Collections.SingletonList(value); + } + + public EventBean GetEnumerableEvent(AggregationState state, EvaluateParams evalParams) { + return ((AggregationStateLinear) state).FirstValue; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/access/AggregationAccessorFirstWEval.cs b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationAccessorFirstWEval.cs new file mode 100755 index 000000000..253101e85 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationAccessorFirstWEval.cs @@ -0,0 +1,68 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.agg.access +{ + /// + /// Represents the aggregation accessor that provides the result for the "first" aggregation function without index. + /// + public class AggregationAccessorFirstWEval : AggregationAccessor + { + private readonly int _streamNum; + private readonly ExprEvaluator _childNode; + private readonly EventBean[] _eventsPerStream; + + /// + /// Ctor. + /// + /// stream id + /// expression + public AggregationAccessorFirstWEval(int streamNum, ExprEvaluator childNode) + { + _streamNum = streamNum; + _childNode = childNode; + _eventsPerStream = new EventBean[streamNum + 1]; + } + + public object GetValue(AggregationState state, EvaluateParams evalParams) + { + EventBean bean = ((AggregationStateLinear) state).FirstValue; + if (bean == null) { + return null; + } + _eventsPerStream[_streamNum] = bean; + return _childNode.Evaluate(new EvaluateParams(_eventsPerStream, true, null)); + } + + public ICollection GetEnumerableEvents(AggregationState state, EvaluateParams evalParams) { + EventBean bean = ((AggregationStateLinear) state).FirstValue; + if (bean == null) { + return null; + } + return Collections.SingletonList(bean); + } + + public ICollection GetEnumerableScalar(AggregationState state, EvaluateParams evalParams) { + object value = GetValue(state, evalParams); + if (value == null) { + return null; + } + return Collections.SingletonList(value); + } + + public EventBean GetEnumerableEvent(AggregationState state, EvaluateParams evalParams) { + return ((AggregationStateLinear) state).FirstValue; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/access/AggregationAccessorLastNoEval.cs b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationAccessorLastNoEval.cs new file mode 100755 index 000000000..a17cc60e0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationAccessorLastNoEval.cs @@ -0,0 +1,52 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.agg.access +{ + public class AggregationAccessorLastNoEval : AggregationAccessor + { + public readonly static AggregationAccessorLastNoEval INSTANCE = new AggregationAccessorLastNoEval(); + + public object GetValue(AggregationState state, EvaluateParams evalParams) + { + EventBean bean = ((AggregationStateLinear) state).LastValue; + if (bean == null) { + return null; + } + return bean.Underlying; + } + + public ICollection GetEnumerableEvents(AggregationState state, EvaluateParams evalParams) { + EventBean bean = ((AggregationStateLinear) state).LastValue; + if (bean == null) { + return null; + } + return Collections.SingletonList(bean); + } + + public ICollection GetEnumerableScalar(AggregationState state, EvaluateParams evalParams) { + object value = GetValue(state, evalParams); + if (value == null) { + return null; + } + return Collections.SingletonList(value); + } + + public EventBean GetEnumerableEvent(AggregationState state, EvaluateParams evalParams) { + return ((AggregationStateLinear) state).LastValue; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/access/AggregationAccessorLastWEval.cs b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationAccessorLastWEval.cs new file mode 100755 index 000000000..3bbb85198 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationAccessorLastWEval.cs @@ -0,0 +1,70 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.agg.access +{ + /// + /// Represents the aggregation accessor that provides the result for the "last" aggregation function without index. + /// + public class AggregationAccessorLastWEval : AggregationAccessor + { + private readonly int _streamNum; + private readonly ExprEvaluator _childNode; + private readonly EventBean[] _eventsPerStream; + + /// + /// Ctor. + /// + /// stream id + /// expression + public AggregationAccessorLastWEval(int streamNum, ExprEvaluator childNode) + { + _streamNum = streamNum; + _childNode = childNode; + _eventsPerStream = new EventBean[streamNum + 1]; + } + + public object GetValue(AggregationState state, EvaluateParams evalParams) + { + EventBean bean = ((AggregationStateLinear) state).LastValue; + if (bean == null) { + return null; + } + _eventsPerStream[_streamNum] = bean; + return _childNode.Evaluate(new EvaluateParams(_eventsPerStream, true, null)); + } + + public ICollection GetEnumerableEvents(AggregationState state, EvaluateParams evalParams) { + EventBean bean = ((AggregationStateLinear) state).LastValue; + if (bean == null) { + return null; + } + return Collections.SingletonList(bean); + } + + public ICollection GetEnumerableScalar(AggregationState state, EvaluateParams evalParams) { + object value = GetValue(state, evalParams); + if (value == null) { + return null; + } + return Collections.SingletonList(value); + } + + public EventBean GetEnumerableEvent(AggregationState state, EvaluateParams evalParams) { + return ((AggregationStateLinear) state).LastValue; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/access/AggregationAccessorMinMaxByBase.cs b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationAccessorMinMaxByBase.cs new file mode 100755 index 000000000..4c5bdf01b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationAccessorMinMaxByBase.cs @@ -0,0 +1,60 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.agg.access +{ + /// + /// Represents the aggregation accessor that provides the result for the "maxBy" aggregation function. + /// + public abstract class AggregationAccessorMinMaxByBase : AggregationAccessor + { + private readonly bool _max; + + protected AggregationAccessorMinMaxByBase(bool max) + { + _max = max; + } + + public abstract object GetValue(AggregationState state, EvaluateParams evaluateParams); + + public ICollection GetEnumerableEvents(AggregationState state, EvaluateParams evalParams) + { + EventBean bean = GetEnumerableEvent(state, evalParams); + if (bean == null) + { + return null; + } + return Collections.SingletonList(bean); + } + + public ICollection GetEnumerableScalar(AggregationState state, EvaluateParams evalParams) + { + return null; + } + + public EventBean GetEnumerableEvent(AggregationState state, EvaluateParams evalParams) + { + if (_max) + { + return ((AggregationStateSorted) state).LastValue; + } + else + { + return ((AggregationStateSorted) state).FirstValue; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/access/AggregationAccessorMinMaxByNonTable.cs b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationAccessorMinMaxByNonTable.cs new file mode 100755 index 000000000..a3ed82624 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationAccessorMinMaxByNonTable.cs @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.agg.access +{ + /// + /// Represents the aggregation accessor that provides the result for the "maxBy" aggregation function. + /// + public class AggregationAccessorMinMaxByNonTable : AggregationAccessorMinMaxByBase + { + public AggregationAccessorMinMaxByNonTable(bool max) + : base(max) + { + } + + public override object GetValue(AggregationState state, EvaluateParams evaluateParams) + { + var @event = GetEnumerableEvent(state, evaluateParams); + if (@event == null) { + return null; + } + return @event.Underlying; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/access/AggregationAccessorMinMaxByTable.cs b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationAccessorMinMaxByTable.cs new file mode 100755 index 000000000..ebee4d1b1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationAccessorMinMaxByTable.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.table.mgmt; + +namespace com.espertech.esper.epl.agg.access +{ + /// + /// Represents the aggregation accessor that provides the result for the "maxBy" aggregation function. + /// + public class AggregationAccessorMinMaxByTable : AggregationAccessorMinMaxByBase + { + private readonly TableMetadata _tableMetadata; + + public AggregationAccessorMinMaxByTable(bool max, TableMetadata tableMetadata) + : base(max) + { + _tableMetadata = tableMetadata; + } + + public override object GetValue(AggregationState state, EvaluateParams evaluateParams) + { + EventBean @event = GetEnumerableEvent(state, evaluateParams); + if (@event == null) { + return null; + } + return _tableMetadata.EventToPublic.ConvertToUnd(@event, evaluateParams); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/access/AggregationAccessorSlotPair.cs b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationAccessorSlotPair.cs new file mode 100755 index 000000000..dbf5eb28c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationAccessorSlotPair.cs @@ -0,0 +1,33 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.agg.access +{ + /// + /// For handling access aggregation functions "first, last, window" a pair of slow and accessor. + /// + public class AggregationAccessorSlotPair + { + /// Ctor. + /// number of accessor + /// accessor + public AggregationAccessorSlotPair(int slot, AggregationAccessor accessor) + { + Slot = slot; + Accessor = accessor; + } + + /// Returns the slot. + /// slow + public int Slot { get; private set; } + + /// Returns the accessor. + /// accessor + public AggregationAccessor Accessor { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/access/AggregationAccessorSortedNonTable.cs b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationAccessorSortedNonTable.cs new file mode 100755 index 000000000..380ce1acf --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationAccessorSortedNonTable.cs @@ -0,0 +1,74 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.agg.access +{ + /// + /// Represents the aggregation accessor that provides the result for the "maxBy" aggregation function. + /// + public class AggregationAccessorSortedNonTable : AggregationAccessor + { + private readonly bool _max; + private readonly Type _componentType; + + public AggregationAccessorSortedNonTable(bool max, Type componentType) + { + _max = max; + _componentType = componentType; + } + + public object GetValue(AggregationState state, EvaluateParams evalParams) + { + var sorted = (AggregationStateSorted) state; + if (sorted.Count == 0) + { + return null; + } + var array = Array.CreateInstance(_componentType, sorted.Count); + + IEnumerator it; + if (_max) + { + it = sorted.Reverse().GetEnumerator(); + } + else + { + it = sorted.GetEnumerator(); + } + + int count = 0; + while (it.MoveNext()) + { + array.SetValue(it.Current.Underlying, count++); + } + return array; + } + + public ICollection GetEnumerableEvents(AggregationState state, EvaluateParams evalParams) + { + return ((AggregationStateSorted) state).CollectionReadOnly(); + } + + public ICollection GetEnumerableScalar(AggregationState state, EvaluateParams evalParams) + { + return null; + } + + public EventBean GetEnumerableEvent(AggregationState state, EvaluateParams evalParams) + { + return null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/access/AggregationAccessorSortedTable.cs b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationAccessorSortedTable.cs new file mode 100755 index 000000000..baca80bf0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationAccessorSortedTable.cs @@ -0,0 +1,72 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.table.mgmt; + +namespace com.espertech.esper.epl.agg.access +{ + /// + /// Represents the aggregation accessor that provides the result for the "maxBy" aggregation function. + /// + public class AggregationAccessorSortedTable : AggregationAccessor + { + private readonly bool _max; + private readonly Type _componentType; + private readonly TableMetadata _tableMetadata; + + public AggregationAccessorSortedTable(bool max, Type componentType, TableMetadata tableMetadata) { + _max = max; + _componentType = componentType; + _tableMetadata = tableMetadata; + } + + public object GetValue(AggregationState state, EvaluateParams evalParams) + { + var sorted = (AggregationStateSorted) state; + if (sorted.Count == 0) { + return null; + } + var array = Array.CreateInstance(_componentType, sorted.Count); + + IEnumerator it; + if (_max) { + it = sorted.Reverse().GetEnumerator(); + } + else { + it = sorted.GetEnumerator(); + } + + int count = 0; + while(it.MoveNext()) + { + EventBean bean = it.Current; + object und = _tableMetadata.EventToPublic.ConvertToUnd(bean, evalParams); + array.SetValue(und, count++); + } + return array; + } + + public ICollection GetEnumerableEvents(AggregationState state, EvaluateParams evalParams) { + return ((AggregationStateSorted) state).CollectionReadOnly(); + } + + public ICollection GetEnumerableScalar(AggregationState state, EvaluateParams evalParams) { + return null; + } + + public EventBean GetEnumerableEvent(AggregationState state, EvaluateParams evalParams) { + return null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/access/AggregationAccessorWindowNoEval.cs b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationAccessorWindowNoEval.cs new file mode 100755 index 000000000..92b594b04 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationAccessorWindowNoEval.cs @@ -0,0 +1,78 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.agg.access +{ + /// + /// Represents the aggregation accessor that provides the result for the "window" aggregation function. + /// + public class AggregationAccessorWindowNoEval : AggregationAccessor + { + private readonly Type _componentType; + + public AggregationAccessorWindowNoEval(Type componentType) + { + this._componentType = componentType; + } + + public object GetValue(AggregationState state, EvaluateParams evalParams) + { + var linear = ((AggregationStateLinear)state); + if (linear.Count == 0) + { + return null; + } + var count = 0; + var array = Array.CreateInstance(_componentType, linear.Count); + foreach (var bean in linear) + { + array.SetValue(bean.Underlying, count++); + } + return array; + } + + public ICollection GetEnumerableEvents(AggregationState state, EvaluateParams evalParams) + { + var linear = ((AggregationStateLinear)state); + if (linear.Count == 0) + { + return null; + } + return linear.CollectionReadOnly; + } + + public ICollection GetEnumerableScalar(AggregationState state, EvaluateParams evalParams) + { + var linear = ((AggregationStateLinear)state); + if (linear.Count == 0) + { + return null; + } + var values = new List(linear.Count); + foreach (var bean in linear) + { + values.Add(bean.Underlying); + } + return values; + } + + public EventBean GetEnumerableEvent(AggregationState state, EvaluateParams evalParams) + { + return null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/access/AggregationAccessorWindowWEval.cs b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationAccessorWindowWEval.cs new file mode 100755 index 000000000..d4413b404 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationAccessorWindowWEval.cs @@ -0,0 +1,93 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.agg.access +{ + /// + /// Represents the aggregation accessor that provides the result for the "window" aggregation function. + /// + public class AggregationAccessorWindowWEval : AggregationAccessor + { + private readonly int _streamNum; + private readonly ExprEvaluator _childNode; + private readonly EventBean[] _eventsPerStream; + private readonly Type _componentType; + + /// + /// Ctor. + /// + /// stream id + /// expression + /// type + public AggregationAccessorWindowWEval(int streamNum, ExprEvaluator childNode, Type componentType) + { + _streamNum = streamNum; + _childNode = childNode; + _eventsPerStream = new EventBean[streamNum + 1]; + _componentType = componentType; + } + + public object GetValue(AggregationState state, EvaluateParams evalParams) + { + var linear = ((AggregationStateLinear)state); + if (linear.Count == 0) + { + return null; + } + var array = Array.CreateInstance(_componentType, linear.Count); + var count = 0; + foreach (var bean in linear) + { + _eventsPerStream[_streamNum] = bean; + var value = _childNode.Evaluate(new EvaluateParams(_eventsPerStream, true, null)); + array.SetValue(value, count++); + } + + return array; + } + + public ICollection GetEnumerableEvents(AggregationState state, EvaluateParams evalParams) + { + var linear = ((AggregationStateLinear)state); + if (linear.Count == 0) + { + return null; + } + return linear.CollectionReadOnly; + } + + public ICollection GetEnumerableScalar(AggregationState state, EvaluateParams evalParams) + { + var linear = ((AggregationStateLinear)state); + if (linear.Count == 0) + { + return null; + } + var values = new List(linear.Count); + foreach (EventBean bean in linear) + { + _eventsPerStream[_streamNum] = bean; + object value = _childNode.Evaluate(new EvaluateParams(_eventsPerStream, true, null)); + values.Add(value); + } + + return values; + } + + public EventBean GetEnumerableEvent(AggregationState state, EvaluateParams evalParams) + { + return null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/access/AggregationAgent.cs b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationAgent.cs new file mode 100755 index 000000000..57a9640e3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationAgent.cs @@ -0,0 +1,19 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.agg.access +{ + public interface AggregationAgent + { + void ApplyEnter(EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext, AggregationState aggregationState); + void ApplyLeave(EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext, AggregationState aggregationState); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/access/AggregationAgentDefault.cs b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationAgentDefault.cs new file mode 100755 index 000000000..5d5a3a7e6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationAgentDefault.cs @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.agg.access +{ + public class AggregationAgentDefault : AggregationAgent { + public readonly static AggregationAgentDefault INSTANCE = new AggregationAgentDefault(); + + public void ApplyEnter(EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext, AggregationState aggregationState) { + aggregationState.ApplyEnter(eventsPerStream, exprEvaluatorContext); + } + + public void ApplyLeave(EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext, AggregationState aggregationState) { + aggregationState.ApplyLeave(eventsPerStream, exprEvaluatorContext); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/access/AggregationAgentRewriteStream.cs b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationAgentRewriteStream.cs new file mode 100755 index 000000000..6f1b2b9db --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationAgentRewriteStream.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.agg.access +{ + public class AggregationAgentRewriteStream : AggregationAgent { + + private readonly int streamNum; + + public AggregationAgentRewriteStream(int streamNum) { + this.streamNum = streamNum; + } + + public void ApplyEnter(EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext, AggregationState aggregationState) { + EventBean[] rewrite = new EventBean[] {eventsPerStream[streamNum]}; + aggregationState.ApplyEnter(rewrite, exprEvaluatorContext); + } + + public void ApplyLeave(EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext, AggregationState aggregationState) { + EventBean[] rewrite = new EventBean[] {eventsPerStream[streamNum]}; + aggregationState.ApplyLeave(rewrite, exprEvaluatorContext); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/access/AggregationServicePassThru.cs b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationServicePassThru.cs new file mode 100755 index 000000000..3c3610363 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationServicePassThru.cs @@ -0,0 +1,14 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.agg.access +{ + public interface AggregationServicePassThru + { + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/agg/access/AggregationState.cs b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationState.cs new file mode 100755 index 000000000..7373994e2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationState.cs @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.agg.access +{ + /// + /// Base interface for providing access-aggregations, i.e. aggregations that mirror a + /// data window but group by the group-by clause and that do not mirror the data windows + /// sorting policy. + /// + public interface AggregationState + { + /// + /// Enter an event. + /// + /// all events in all streams, typically implementations pick the relevant stream's events to add + /// expression eval context + void ApplyEnter(EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext); + + /// + /// Remove an event. + /// + /// all events in all streams, typically implementations pick the relevant stream's events to remove + /// expression eval context + void ApplyLeave(EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext); + + /// + /// Clear all events in the group. + /// + void Clear(); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/access/AggregationStateImpl.cs b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationStateImpl.cs new file mode 100755 index 000000000..722ed49f4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationStateImpl.cs @@ -0,0 +1,131 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.agg.access +{ + /// + /// Implementation of access function for single-stream (not joins). + /// + public class AggregationStateImpl + : AggregationStateWithSize + , AggregationStateLinear + { + protected int StreamId; + protected List Events = new List(); + + /// Ctor. + /// stream id + public AggregationStateImpl(int streamId) + { + StreamId = streamId; + } + + public void Clear() + { + Events.Clear(); + } + + public void ApplyLeave(EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext) + { + EventBean theEvent = eventsPerStream[StreamId]; + if (theEvent == null) + { + return; + } + Events.Remove(theEvent); + } + + public void ApplyEnter(EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext) + { + EventBean theEvent = eventsPerStream[StreamId]; + if (theEvent == null) + { + return; + } + Events.Add(theEvent); + } + + public EventBean GetFirstNthValue(int index) + { + if (index < 0) + { + return null; + } + if (index >= Events.Count) + { + return null; + } + return Events[index]; + } + + public EventBean GetLastNthValue(int index) + { + if (index < 0) + { + return null; + } + if (index >= Events.Count) + { + return null; + } + return Events[Events.Count - index - 1]; + } + + public EventBean FirstValue + { + get + { + if (Events.IsEmpty()) + { + return null; + } + return Events[0]; + } + } + + public EventBean LastValue + { + get + { + if (Events.IsEmpty()) + { + return null; + } + return Events[Events.Count - 1]; + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public IEnumerator GetEnumerator() + { + return Events.GetEnumerator(); + } + + public ICollection CollectionReadOnly + { + get { return Events; } + } + + public int Count + { + get { return Events.Count; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/access/AggregationStateJoinImpl.cs b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationStateJoinImpl.cs new file mode 100755 index 000000000..03f6f5eb6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationStateJoinImpl.cs @@ -0,0 +1,181 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.agg.access +{ + /// + /// Implementation of access function for joins. + /// + public class AggregationStateJoinImpl + : AggregationStateWithSize + , AggregationStateLinear + { + protected int StreamId; + protected LinkedHashMap RefSet = new LinkedHashMap(); + private EventBean[] _array; + + /// Ctor. + /// stream id + public AggregationStateJoinImpl(int streamId) + { + StreamId = streamId; + } + + public void Clear() { + RefSet.Clear(); + _array = null; + } + + public void ApplyEnter(EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext) + { + var theEvent = eventsPerStream[StreamId]; + if (theEvent == null) { + return; + } + _array = null; + var value = RefSet.Get(theEvent); + if (value == null) + { + RefSet.Put(theEvent, 1); + return; + } + + value++; + RefSet.Put(theEvent, value); + } + + public void ApplyLeave(EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext) + { + var theEvent = eventsPerStream[StreamId]; + if (theEvent == null) { + return; + } + _array = null; + + var value = RefSet.Get(theEvent); + if (value == null) + { + return; + } + + if (value == 1) + { + RefSet.Remove(theEvent); + return; + } + + value--; + RefSet.Put(theEvent, value); + } + + public EventBean GetFirstNthValue(int index) { + if (index < 0) { + return null; + } + if (RefSet.IsEmpty()) { + return null; + } + if (index >= RefSet.Count) { + return null; + } + if (_array == null) { + InitArray(); + } + return _array[index]; + } + + public EventBean GetLastNthValue(int index) { + if (index < 0) { + return null; + } + if (RefSet.IsEmpty()) { + return null; + } + if (index >= RefSet.Count) { + return null; + } + if (_array == null) { + InitArray(); + } + return _array[_array.Length - index - 1]; + } + + public EventBean FirstValue + { + get + { + if (RefSet.IsEmpty()) + { + return null; + } + return RefSet.First().Key; + } + } + + public EventBean LastValue + { + get + { + if (RefSet.IsEmpty()) + { + return null; + } + if (_array == null) + { + InitArray(); + } + return _array[_array.Length - 1]; + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public IEnumerator GetEnumerator() + { + if (_array == null) { + InitArray(); + } + return _array.Cast().GetEnumerator(); + } + + public ICollection CollectionReadOnly + { + get + { + if (_array == null) + { + InitArray(); + } + return _array; + } + } + + public int Count + { + get { return RefSet.Count; } + } + + private void InitArray() + { + var events = RefSet.Keys; + _array = events.ToArray(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/access/AggregationStateKey.cs b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationStateKey.cs new file mode 100755 index 000000000..464487684 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationStateKey.cs @@ -0,0 +1,18 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.agg.access +{ + public interface AggregationStateKey + { + } + + public class ProxyAggregationStateKey : AggregationStateKey + { + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/access/AggregationStateLinear.cs b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationStateLinear.cs new file mode 100755 index 000000000..fad5f4ce9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationStateLinear.cs @@ -0,0 +1,49 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; + +namespace com.espertech.esper.epl.agg.access +{ + public interface AggregationStateLinear : IEnumerable + { + /// Returns the first (oldest) value entered. + /// first value + EventBean FirstValue { get; } + + /// Returns the newest (last) value entered. + /// last value + EventBean LastValue { get; } + + /// Returns the number of events in the group. + /// size + int Count { get; } + + /// + /// Counting from the first element to the last, returns the oldest (first) value entered for index zero and the + /// n-th oldest value for index Count. + /// + /// index + /// last value + EventBean GetFirstNthValue(int index); + + /// + /// Counting from the last element to the first, returns the newest (last) value entered for index zero and the + /// n-th newest value for index Count. + /// + /// index + /// last value + EventBean GetLastNthValue(int index); + + /// Returns all events for the group. + /// group event iterator + ICollection CollectionReadOnly { get; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/agg/access/AggregationStateMinMaxByEver.cs b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationStateMinMaxByEver.cs new file mode 100755 index 000000000..9efc73f33 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationStateMinMaxByEver.cs @@ -0,0 +1,137 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.agg.access +{ + /// + /// Implementation of access function for single-stream (not joins). + /// + public class AggregationStateMinMaxByEver + : AggregationState + , AggregationStateSorted + { + protected readonly AggregationStateMinMaxByEverSpec Spec; + protected EventBean CurrentMinMaxBean; + protected Object CurrentMinMax; + + public AggregationStateMinMaxByEver(AggregationStateMinMaxByEverSpec spec) + { + Spec = spec; + } + + public void Clear() + { + CurrentMinMax = null; + CurrentMinMaxBean = null; + } + + public void ApplyEnter(EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext) + { + EventBean theEvent = eventsPerStream[Spec.StreamId]; + if (theEvent == null) + { + return; + } + Object comparable = AggregationStateSortedImpl.GetComparable( + Spec.Criteria, eventsPerStream, true, exprEvaluatorContext); + if (CurrentMinMax == null) + { + CurrentMinMax = comparable; + CurrentMinMaxBean = theEvent; + } + else + { + int compareResult = Spec.Comparator.Compare(CurrentMinMax, comparable); + if (Spec.IsMax) + { + if (compareResult < 0) + { + CurrentMinMax = comparable; + CurrentMinMaxBean = theEvent; + } + } + else + { + if (compareResult > 0) + { + CurrentMinMax = comparable; + CurrentMinMaxBean = theEvent; + } + } + } + } + + public void ApplyLeave(EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext) + { + // this is an ever-type aggregation + } + + public EventBean FirstValue + { + get + { + if (Spec.IsMax) + { + throw new UnsupportedOperationException("Only accepts max-value queries"); + } + return CurrentMinMaxBean; + } + } + + public EventBean LastValue + { + get + { + if (!Spec.IsMax) + { + throw new UnsupportedOperationException("Only accepts min-value queries"); + } + return CurrentMinMaxBean; + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public IEnumerator GetEnumerator() + { + throw new UnsupportedOperationException(); + } + + public IEnumerator GetReverseEnumerator() + { + throw new UnsupportedOperationException(); + } + + public ICollection CollectionReadOnly() + { + if (CurrentMinMaxBean != null) + { + return Collections.SingletonList(CurrentMinMaxBean); + } + return null; + } + + public int Count + { + get { return CurrentMinMax == null ? 0 : 1; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/access/AggregationStateMinMaxByEverSpec.cs b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationStateMinMaxByEverSpec.cs new file mode 100755 index 000000000..8e111930b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationStateMinMaxByEverSpec.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.agg.access +{ + public class AggregationStateMinMaxByEverSpec + { + public AggregationStateMinMaxByEverSpec(int streamId, ExprEvaluator[] criteria, bool max, IComparer comparator, Object criteriaKeyBinding) + { + StreamId = streamId; + Criteria = criteria; + IsMax = max; + Comparator = comparator; + CriteriaKeyBinding = criteriaKeyBinding; + } + + public int StreamId { get; private set; } + + public ExprEvaluator[] Criteria { get; private set; } + + public bool IsMax { get; private set; } + + public IComparer Comparator { get; private set; } + + public object CriteriaKeyBinding { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/access/AggregationStateSorted.cs b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationStateSorted.cs new file mode 100755 index 000000000..de121955a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationStateSorted.cs @@ -0,0 +1,35 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; + +namespace com.espertech.esper.epl.agg.access +{ + public interface AggregationStateSorted : IEnumerable + { + /// Returns the first (oldest) value entered. + /// first value + EventBean FirstValue { get; } + + /// Returns the newest (last) value entered. + /// last value + EventBean LastValue { get; } + + /// Returns the number of events in the group. + /// size + int Count { get; } + + /// Returns all events for the group. + /// group event iterator + ICollection CollectionReadOnly(); + + IEnumerator GetReverseEnumerator(); + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/agg/access/AggregationStateSortedEnumerator.cs b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationStateSortedEnumerator.cs new file mode 100755 index 000000000..d2f17b1b6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationStateSortedEnumerator.cs @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.epl.agg.access +{ + public class AggregationStateSortedEnumerator : MixedEventBeanAndCollectionEnumeratorBase + { + private readonly SortedDictionary _window; + + /// + /// Ctor. + /// + /// sorted map with events + /// if set to true [reverse]. + public AggregationStateSortedEnumerator(SortedDictionary window, bool reverse) + : base(reverse ? Enumerable.Reverse(window.Keys) : window.Keys) + { + _window = window; + } + + protected override Object GetValue(Object iteratorKeyValue) + { + return _window.Get(iteratorKeyValue); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/access/AggregationStateSortedImpl.cs b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationStateSortedImpl.cs new file mode 100755 index 000000000..4deb7b9db --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationStateSortedImpl.cs @@ -0,0 +1,192 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.agg.access +{ + /// + /// Implementation of access function for single-stream (not joins). + /// + public class AggregationStateSortedImpl : AggregationStateWithSize, AggregationStateSorted + { + protected readonly AggregationStateSortedSpec Spec; + protected readonly SortedDictionary Sorted; + protected int Size; + + /// Ctor. + /// aggregation spec + public AggregationStateSortedImpl(AggregationStateSortedSpec spec) { + Spec = spec; + Sorted = new SortedDictionary(spec.Comparator); + } + + public void Clear() { + Sorted.Clear(); + Size = 0; + } + + public void ApplyEnter(EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext) + { + var theEvent = eventsPerStream[Spec.StreamId]; + if (theEvent == null) { + return; + } + if (ReferenceEvent(theEvent)) { + Object comparable = GetComparable(Spec.Criteria, eventsPerStream, true, exprEvaluatorContext); + Object existing = Sorted.Get(comparable); + if (existing == null) { + Sorted.Put(comparable, theEvent); + } + else if (existing is EventBean) { + var coll = new LinkedList(); + coll.AddLast(existing); + coll.AddLast(theEvent); + Sorted.Put(comparable, coll); + } + else { + var q = (LinkedList)existing; + q.AddLast(theEvent); + } + Size++; + } + } + + protected virtual bool ReferenceEvent(EventBean theEvent) { + // no action + return true; + } + + protected virtual bool DereferenceEvent(EventBean theEvent) { + // no action + return true; + } + + public void ApplyLeave(EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext) + { + EventBean theEvent = eventsPerStream[Spec.StreamId]; + if (theEvent == null) { + return; + } + if (DereferenceEvent(theEvent)) { + Object comparable = GetComparable(Spec.Criteria, eventsPerStream, false, exprEvaluatorContext); + Object existing = Sorted.Get(comparable); + if (existing != null) { + if (existing.Equals(theEvent)) { + Sorted.Remove(comparable); + Size--; + } + else if (existing is LinkedList) + { + var q = (LinkedList)existing; + q.Remove(theEvent); + if (q.IsEmpty()) { + Sorted.Remove(comparable); + } + Size--; + } + } + } + } + + public EventBean FirstValue + { + get + { + if (Sorted.IsEmpty()) + { + return null; + } + var max = Sorted.Values.First(); + return CheckedPayload(max); + } + } + + public EventBean LastValue + { + get + { + if (Sorted.IsEmpty()) + { + return null; + } + var min = Sorted.Values.Last(); + return CheckedPayload(min); + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public IEnumerator GetEnumerator() + { + return new AggregationStateSortedEnumerator(Sorted, false); + } + + public IEnumerator GetReverseEnumerator() + { + return new AggregationStateSortedEnumerator(Sorted, true); + } + + public ICollection CollectionReadOnly() + { + return new AggregationStateSortedWrappingCollection(Sorted, Size); + } + + public int Count + { + get { return Size; } + } + + public static Object GetComparable(ExprEvaluator[] criteria, EventBean[] eventsPerStream, bool istream, ExprEvaluatorContext exprEvaluatorContext) + { + if (criteria.Length == 1) { + return criteria[0].Evaluate(new EvaluateParams(eventsPerStream, istream, exprEvaluatorContext)); + } + else + { + var result = new Object[criteria.Length]; + var count = 0; + foreach (ExprEvaluator expr in criteria) { + result[count++] = expr.Evaluate(new EvaluateParams(eventsPerStream, true, exprEvaluatorContext)); + } + return new MultiKeyUntyped(result); + } + } + + private EventBean CheckedPayload(Object value) + { + if (value is EventBean) { + return (EventBean) value; + } + else if (value is IEnumerable) + { + return ((IEnumerable) value).First(); + } + else if (value is IEnumerable) + { + var @enum = ((IEnumerable) value).GetEnumerator(); + @enum.MoveNext(); + return @enum.Current as EventBean; + } + + throw new ArgumentException("invalid value", "value"); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/access/AggregationStateSortedJoin.cs b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationStateSortedJoin.cs new file mode 100755 index 000000000..c0e7ef5a8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationStateSortedJoin.cs @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.collection; + +namespace com.espertech.esper.epl.agg.access +{ + /// Implementation of access function for single-stream (not joins). + public class AggregationStateSortedJoin : AggregationStateSortedImpl + { + protected readonly RefCountedSetAtomicInteger Refs; + + public AggregationStateSortedJoin(AggregationStateSortedSpec spec) : base(spec) + { + Refs = new RefCountedSetAtomicInteger(); + } + + protected override bool ReferenceEvent(EventBean theEvent) + { + return Refs.Add(theEvent); + } + + protected override bool DereferenceEvent(EventBean theEvent) + { + return Refs.Remove(theEvent); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/access/AggregationStateSortedSpec.cs b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationStateSortedSpec.cs new file mode 100755 index 000000000..71c0e9a8a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationStateSortedSpec.cs @@ -0,0 +1,35 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.agg.access +{ + public class AggregationStateSortedSpec + { + public AggregationStateSortedSpec(int streamId, ExprEvaluator[] criteria, IComparer comparator, Object criteriaKeyBinding) + { + StreamId = streamId; + Criteria = criteria; + Comparator = comparator; + CriteriaKeyBinding = criteriaKeyBinding; + } + + public int StreamId { get; private set; } + + public ExprEvaluator[] Criteria { get; private set; } + + public IComparer Comparator { get; private set; } + + public object CriteriaKeyBinding { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/access/AggregationStateSortedWrappingCollection.cs b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationStateSortedWrappingCollection.cs new file mode 100755 index 000000000..86f044d59 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationStateSortedWrappingCollection.cs @@ -0,0 +1,102 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; + +namespace com.espertech.esper.epl.agg.access +{ + public class AggregationStateSortedWrappingCollection : ICollection + { + private readonly SortedDictionary _sorted; + private readonly int _count; + + public AggregationStateSortedWrappingCollection(SortedDictionary sorted, int count) + { + _sorted = sorted; + _count = count; + } + + public bool Remove(EventBean item) + { + throw new NotImplementedException(); + } + + public int Count + { + get { return _count; } + } + + public bool IsReadOnly + { + get { return true; } + } + + public bool IsEmpty() { + return Count == 0; + } + + public IEnumerator GetEnumerator() + { + return new AggregationStateSortedEnumerator(_sorted, false); + } + + public Object[] ToArray() + { + throw new UnsupportedOperationException("Partial implementation"); + } + + public Object[] ToArray(Object[] a) + { + throw new UnsupportedOperationException("Partial implementation"); + } + + public bool Contains(Object o) + { + throw new UnsupportedOperationException("Partial implementation"); + } + + public void Add(Object o) + { + throw new UnsupportedOperationException("Read-only implementation"); + } + + public bool Remove(Object o) + { + throw new UnsupportedOperationException("Read-only implementation"); + } + + public void Add(EventBean item) + { + throw new NotImplementedException(); + } + + public void Clear() { + throw new UnsupportedOperationException("Read-only implementation"); + } + + public bool Contains(EventBean item) + { + throw new NotImplementedException(); + } + + public void CopyTo(EventBean[] array, int arrayIndex) + { + throw new NotImplementedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/access/AggregationStateType.cs b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationStateType.cs new file mode 100755 index 000000000..76e2035cf --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationStateType.cs @@ -0,0 +1,39 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; + +namespace com.espertech.esper.epl.agg.access +{ + /// Enum for aggregation multi-function state type. + public enum AggregationStateType + { + /// For "first" function. + FIRST, + /// For "last" function. + LAST, + /// For "window" function. + WINDOW + } + + public static class AggregationStateTypeExtensions + { + public static AggregationStateType? FromString(string text, bool throwException = false) + { + var value = EnumHelper.ParseBoxed(text, true); + if ((value == null) && throwException) + { + throw new ArgumentException("illegal value for enumeration"); + } + + return value; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/agg/access/AggregationStateWithSize.cs b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationStateWithSize.cs new file mode 100755 index 000000000..c6445f723 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/access/AggregationStateWithSize.cs @@ -0,0 +1,19 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.agg.access +{ + public interface AggregationStateWithSize : AggregationState + { + /// + /// Must return the number of events currently held, if applicable. Return -1 if not applicable. + /// + /// number of events + int Count { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregationMethod.cs b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregationMethod.cs new file mode 100755 index 000000000..c2b39d45d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregationMethod.cs @@ -0,0 +1,47 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.agg.aggregator +{ + /// + /// Maintains aggregation state applying values as entering and leaving the state. + /// + /// Implementations must also act as a factory for further independent copies of aggregation + /// states such that new aggregation state holders and be created from a _prototype. + /// + /// + public interface AggregationMethod + { + /// + /// Apply the value as entering aggregation (entering window). + /// + /// The value can be null since 'null' values may be counted as unique separate values. + /// + /// + /// to add to aggregate + void Enter(Object value); + + /// + /// Apply the value as leaving aggregation (leaving window). + /// + /// The value can be null since 'null' values may be counted as unique separate values. + /// + /// + /// to remove from aggregate + void Leave(Object value); + + /// Returns the current value held. + /// current value + object Value { get; } + + /// Clear out the collection. + void Clear(); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorAvedev.cs b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorAvedev.cs new file mode 100755 index 000000000..1c92c1640 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorAvedev.cs @@ -0,0 +1,84 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.collection; +using com.espertech.esper.compat; + +namespace com.espertech.esper.epl.agg.aggregator +{ + /// + /// Standard deviation always generates double-types numbers. + /// + public class AggregatorAvedev : AggregationMethod + { + /// Ctor. + public AggregatorAvedev() + { + ValueSet = new RefCountedSet(); + } + + public virtual void Enter(Object @object) + { + if (@object == null) + { + return; + } + + var value = @object.AsDouble(); + ValueSet.Add(value); + Sum += value; + } + + public virtual void Leave(Object @object) + { + if (@object == null) + { + return; + } + + var value = @object.AsDouble(); + ValueSet.Remove(value); + Sum -= value; + } + + public virtual void Clear() + { + Sum = 0; + ValueSet.Clear(); + } + + public object Value + { + get + { + int datapoints = ValueSet.Count; + + if (datapoints == 0) + { + return null; + } + + double total = 0; + double avg = Sum/datapoints; + + foreach(var entry in ValueSet) + { + total += entry.Value*Math.Abs(entry.Key - avg); + } + + return total/datapoints; + } + } + + public RefCountedSet ValueSet { get; set; } + + public double Sum { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorAvedevFilter.cs b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorAvedevFilter.cs new file mode 100755 index 000000000..934c9f41f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorAvedevFilter.cs @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.agg.service; + +namespace com.espertech.esper.epl.agg.aggregator +{ + /// + /// Standard deviation always generates double-types numbers. + /// + public class AggregatorAvedevFilter : AggregatorAvedev + { + public override void Enter(Object parameters) + { + var paramArray = (Object[])parameters; + if (!AggregatorUtil.CheckFilter(paramArray)) + { + return; + } + base.Enter(paramArray[0]); + } + + public override void Leave(Object parameters) + { + var paramArray = (Object[])parameters; + if (!AggregatorUtil.CheckFilter(paramArray)) + { + return; + } + base.Leave(paramArray[0]); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorAvg.cs b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorAvg.cs new file mode 100755 index 000000000..b4e0a265c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorAvg.cs @@ -0,0 +1,64 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; + +namespace com.espertech.esper.epl.agg.aggregator +{ + /// + /// Average that generates double-typed numbers. + /// + public class AggregatorAvg : AggregationMethod + { + protected double Sum; + protected long NumDataPoints; + + public virtual void Clear() + { + Sum = 0; + NumDataPoints = 0; + } + + public virtual void Enter(Object @object) + { + if (@object == null) { + return; + } + NumDataPoints++; + Sum += (@object).AsDouble(); + } + + public virtual void Leave(Object @object) + { + if (@object == null) { + return; + } + if (NumDataPoints <= 1) { + Clear(); + } + else { + NumDataPoints--; + Sum -= (@object).AsDouble(); + } + } + + public virtual object Value + { + get + { + if (NumDataPoints == 0) + { + return null; + } + return Sum/NumDataPoints; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorAvgBigInteger.cs b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorAvgBigInteger.cs new file mode 100755 index 000000000..91f99a0f7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorAvgBigInteger.cs @@ -0,0 +1,65 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Numerics; + +using com.espertech.esper.compat; + +namespace com.espertech.esper.epl.agg.aggregator +{ + /// + /// Average that generates double-typed numbers. + /// + public class AggregatorAvgBigInteger : AggregationMethod + { + protected BigInteger Sum; + protected long NumDataPoints; + + public virtual void Clear() + { + Sum = 0; + NumDataPoints = 0; + } + + public virtual void Enter(Object @object) + { + if (@object == null) { + return; + } + NumDataPoints++; + Sum += (@object).AsBigInteger(); + } + + public virtual void Leave(Object @object) + { + if (@object == null) { + return; + } + if (NumDataPoints <= 1) { + Clear(); + } + else { + NumDataPoints--; + Sum -= (@object).AsBigInteger(); + } + } + + public virtual object Value + { + get + { + if (NumDataPoints == 0) + { + return null; + } + return Sum / NumDataPoints; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorAvgBigIntegerFilter.cs b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorAvgBigIntegerFilter.cs new file mode 100755 index 000000000..025ca3ef3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorAvgBigIntegerFilter.cs @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.agg.service; + +namespace com.espertech.esper.epl.agg.aggregator +{ + /// + /// Average that generates a BigDecimal numbers. + /// + public class AggregatorAvgBigIntegerFilter : AggregatorAvgBigInteger + { + public AggregatorAvgBigIntegerFilter() + : base() + { + } + + public override void Enter(Object parameters) + { + var paramArray = (Object[])parameters; + if (!AggregatorUtil.CheckFilter(paramArray)) + { + return; + } + base.Enter(paramArray[0]); + } + + public override void Leave(Object parameters) + { + var paramArray = (Object[])parameters; + if (!AggregatorUtil.CheckFilter(paramArray)) + { + return; + } + base.Leave(paramArray[0]); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorAvgDecimal.cs b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorAvgDecimal.cs new file mode 100755 index 000000000..f131eea81 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorAvgDecimal.cs @@ -0,0 +1,90 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.logging; + +namespace com.espertech.esper.epl.agg.aggregator +{ + /// + /// Average that generates a BigDecimal numbers. + /// + public class AggregatorAvgDecimal : AggregationMethod + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private decimal _sum; + private long _numDataPoints; + private MathContext _optionalMathContext; + + /// Ctor. + public AggregatorAvgDecimal(MathContext optionalMathContext) + { + _sum = 0.0m; + _optionalMathContext = optionalMathContext; + } + + public virtual void Clear() + { + _sum = 0.0m; + _numDataPoints = 0; + } + + public virtual void Enter(Object @object) + { + if (@object == null) + { + return; + } + _numDataPoints++; + _sum += @object.AsDecimal(); + } + + public virtual void Leave(Object @object) + { + if (@object == null) + { + return; + } + _numDataPoints--; + _sum -= @object.AsDecimal(); + } + + public virtual object Value + { + get + { + if (_numDataPoints == 0) + { + return null; + } + try + { + if (_optionalMathContext == null) + { + return _sum/_numDataPoints; + } + else + { + return Math.Round( + _sum/_numDataPoints, + _optionalMathContext.Precision, + _optionalMathContext.RoundingMode); + } + } + catch (ArithmeticException ex) + { + Log.Error("Error computing avg aggregation result: " + ex.Message, ex); + return 0.0m; + } + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorAvgDecimalFilter.cs b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorAvgDecimalFilter.cs new file mode 100755 index 000000000..c1f8badb4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorAvgDecimalFilter.cs @@ -0,0 +1,46 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.epl.agg.service; + +namespace com.espertech.esper.epl.agg.aggregator +{ + /// + /// Average that generates a BigDecimal numbers. + /// + public class AggregatorAvgDecimalFilter : AggregatorAvgDecimal + { + public AggregatorAvgDecimalFilter(MathContext optionalMathContext) + : base(optionalMathContext) + { + } + + public override void Enter(Object parameters) + { + var paramArray = (Object[])parameters; + if (!AggregatorUtil.CheckFilter(paramArray)) + { + return; + } + base.Enter(paramArray[0]); + } + + public override void Leave(Object parameters) + { + var paramArray = (Object[])parameters; + if (!AggregatorUtil.CheckFilter(paramArray)) + { + return; + } + base.Leave(paramArray[0]); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorAvgFilter.cs b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorAvgFilter.cs new file mode 100755 index 000000000..9b4a7c7a3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorAvgFilter.cs @@ -0,0 +1,39 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; + +using com.espertech.esper.epl.agg.service; + +namespace com.espertech.esper.epl.agg.aggregator +{ + /// + /// Average that generates double-typed numbers. + /// + public class AggregatorAvgFilter : AggregatorAvg + { + public override void Enter(Object parameters) + { + var paramArray = (Object[]) parameters; + if (!AggregatorUtil.CheckFilter(paramArray)) { + return; + } + base.Enter(paramArray[0]); + } + + public override void Leave(Object parameters) + { + var paramArray = (Object[]) parameters; + if (!AggregatorUtil.CheckFilter(paramArray)) { + return; + } + base.Leave(paramArray[0]); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorCount.cs b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorCount.cs new file mode 100755 index 000000000..6654e275c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorCount.cs @@ -0,0 +1,42 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.agg.aggregator +{ + /// + /// Counts all datapoints including null values. + /// + public class AggregatorCount : AggregationMethod + { + private long _numDataPoints; + + public void Clear() + { + _numDataPoints = 0; + } + + public void Enter(Object @object) + { + _numDataPoints++; + } + + public void Leave(Object @object) + { + if (_numDataPoints > 0) { + _numDataPoints--; + } + } + + public object Value + { + get { return _numDataPoints; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorCountEver.cs b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorCountEver.cs new file mode 100755 index 000000000..8aca707c6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorCountEver.cs @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.agg.aggregator +{ + /// + /// Aggregator for count-ever value. + /// + public class AggregatorCountEver : AggregationMethod + { + private long _count; + + /// + /// Ctor. + /// + public AggregatorCountEver() + { + } + + public void Clear() + { + _count = 0; + } + + public virtual void Enter(object @object) + { + _count++; + } + + public virtual void Leave(object @object) + { + } + + public virtual object Value + { + get { return _count; } + } + + public virtual long Count + { + get { return _count; } + set { this._count = value; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorCountEverFilter.cs b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorCountEverFilter.cs new file mode 100755 index 000000000..fd260b750 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorCountEverFilter.cs @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.agg.service; + +namespace com.espertech.esper.epl.agg.aggregator +{ + /// + /// Aggregator for the count-ever value. + /// + public class AggregatorCountEverFilter : AggregatorCountEver + { + public override void Enter(object parameters) + { + var paramArray = (object[]) parameters; + if (!AggregatorUtil.CheckFilter(paramArray)) + { + return; + } + base.Enter(paramArray[0]); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorCountEverNonNull.cs b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorCountEverNonNull.cs new file mode 100755 index 000000000..aa397069c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorCountEverNonNull.cs @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.agg.aggregator +{ + /// + /// Aggregator for count-ever value. + /// + public class AggregatorCountEverNonNull : AggregationMethod + { + private long _count; + + /// + /// Ctor. + /// + public AggregatorCountEverNonNull() + { + } + + public void Clear() + { + _count = 0; + } + + public virtual void Enter(object @object) + { + if (@object == null) { + return; + } + _count++; + } + + public virtual void Leave(object @object) + { + } + + public virtual object Value + { + get { return _count; } + } + + public virtual long Count + { + get { return _count; } + set { this._count = value; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorCountEverNonNullFilter.cs b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorCountEverNonNullFilter.cs new file mode 100755 index 000000000..38b9cdd9e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorCountEverNonNullFilter.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.agg.service; + +namespace com.espertech.esper.epl.agg.aggregator +{ + /// + /// Aggregator for the count-ever value. + /// + public class AggregatorCountEverNonNullFilter : AggregatorCountEverNonNull + { + public AggregatorCountEverNonNullFilter() + { + } + + public override void Enter(object parameters) + { + var paramArray = (object[]) parameters; + if (!AggregatorUtil.CheckFilter(paramArray)) + { + return; + } + + base.Enter(paramArray[0]); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorCountFilter.cs b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorCountFilter.cs new file mode 100755 index 000000000..4961ebe3c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorCountFilter.cs @@ -0,0 +1,55 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.agg.aggregator +{ + /// + /// Counts all datapoints including null values. + /// + public class AggregatorCountFilter : AggregationMethod + { + protected long NumDataPoints; + + public void Clear() + { + NumDataPoints = 0; + } + + public void Enter(Object @object) + { + if (CheckPass(@object)) + { + NumDataPoints++; + } + } + + public void Leave(Object @object) + { + if (CheckPass(@object)) + { + if (NumDataPoints > 0) { + NumDataPoints--; + } + } + } + + public object Value + { + get { return NumDataPoints; } + } + + private bool CheckPass(Object @object) + { + if (@object == null) + return false; + return true.Equals(@object); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorCountNonNull.cs b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorCountNonNull.cs new file mode 100755 index 000000000..bc64924bb --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorCountNonNull.cs @@ -0,0 +1,55 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.agg.aggregator +{ + /// + /// Count all non-null values. + /// + public class AggregatorCountNonNull : AggregationMethod + { + protected long NumDataPoints; + + public AggregatorCountNonNull() + { + } + + public void Clear() + { + NumDataPoints = 0; + } + + public void Enter(Object @object) + { + if (@object == null) + { + return; + } + NumDataPoints++; + } + + public void Leave(Object @object) + { + if (@object == null) + { + return; + } + if (NumDataPoints > 0) + { + NumDataPoints--; + } + } + + public object Value + { + get { return NumDataPoints; } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorCountNonNullFilter.cs b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorCountNonNullFilter.cs new file mode 100755 index 000000000..319167d49 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorCountNonNullFilter.cs @@ -0,0 +1,58 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.agg.aggregator +{ + /// + /// Count all non-null values. + /// + public class AggregatorCountNonNullFilter : AggregationMethod + { + protected long NumDataPoints; + + public void Clear() + { + NumDataPoints = 0; + } + + public void Enter(Object @object) + { + if (CheckPass(@object)) + { + NumDataPoints++; + } + } + + public void Leave(Object @object) + { + if (CheckPass(@object)) + { + if (NumDataPoints > 0) { + NumDataPoints--; + } + } + } + + public object Value + { + get { return NumDataPoints; } + } + + private bool CheckPass(Object @object) + { + var array = (Array) @object; + var first = array.GetValue(1); + if (first == null) + return false; + + return true.Equals(first); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorDistinctValue.cs b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorDistinctValue.cs new file mode 100755 index 000000000..85155622e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorDistinctValue.cs @@ -0,0 +1,61 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.collection; + +namespace com.espertech.esper.epl.agg.aggregator +{ + /// + /// AggregationMethod for use on top of another aggregator that handles unique value + /// aggregation (versus all-value aggregation) for the underlying aggregator. + /// + public class AggregatorDistinctValue : AggregationMethod + { + private readonly AggregationMethod _inner; + private readonly RefCountedSet _valueSet; + + /// Ctor. + /// is the aggregator function computing aggregation values + public AggregatorDistinctValue(AggregationMethod inner) + { + _inner = inner; + _valueSet = new RefCountedSet(); + } + + public virtual void Clear() + { + _valueSet.Clear(); + _inner.Clear(); + } + + public virtual void Enter(Object value) + { + // if value not already encountered, enter into aggregate + if (_valueSet.Add(value)) + { + _inner.Enter(value); + } + } + + public virtual void Leave(Object value) + { + // if last reference to the value is removed, remove from aggregate + if (_valueSet.Remove(value)) + { + _inner.Leave(value); + } + } + + public virtual object Value + { + get { return _inner.Value; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorDistinctValueFilter.cs b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorDistinctValueFilter.cs new file mode 100755 index 000000000..9f3514ea1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorDistinctValueFilter.cs @@ -0,0 +1,77 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.collection; + +namespace com.espertech.esper.epl.agg.aggregator +{ + /// + /// AggregationMethod for use on top of another aggregator that handles unique value + /// aggregation (versus all-value aggregation) for the underlying aggregator. + /// + public class AggregatorDistinctValueFilter : AggregationMethod + { + private readonly AggregationMethod _inner; + private readonly RefCountedSet _valueSet; + + /// Ctor. + /// is the aggregator function computing aggregation values + public AggregatorDistinctValueFilter(AggregationMethod inner) + { + _inner = inner; + _valueSet = new RefCountedSet(); + } + + public virtual void Clear() + { + _valueSet.Clear(); + _inner.Clear(); + } + + public virtual void Enter(Object value) + { + var values = (Object[]) value; + if (!CheckPass(values)) { + return; + } + + // if value not already encountered, enter into aggregate + if (_valueSet.Add(values[0])) + { + _inner.Enter(value); + } + } + + public virtual void Leave(Object value) + { + var values = (Object[]) value; + if (!CheckPass(values)) { + return; + } + + // if last reference to the value is removed, remove from aggregate + if (_valueSet.Remove(values[0])) + { + _inner.Leave(value); + } + } + + public object Value + { + get { return _inner.Value; } + } + + private static bool CheckPass(Object[] @object) + { + var first = (bool?) @object[1]; + return first.GetValueOrDefault(false); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorFirstEver.cs b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorFirstEver.cs new file mode 100755 index 000000000..095036011 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorFirstEver.cs @@ -0,0 +1,46 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.agg.aggregator +{ + /// + /// Aggregator for the very first value. + /// + public class AggregatorFirstEver : AggregationMethod + { + public virtual void Clear() + { + FirstValue = null; + IsSet = false; + } + + public virtual void Enter(Object @object) + { + if (!IsSet) + { + IsSet = true; + FirstValue = @object; + } + } + + public virtual void Leave(Object @object) + { + } + + public object Value + { + get { return FirstValue; } + } + + public bool IsSet { get; set; } + + public object FirstValue { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorFirstEverFilter.cs b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorFirstEverFilter.cs new file mode 100755 index 000000000..1ad2d0215 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorFirstEverFilter.cs @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.agg.service; + +namespace com.espertech.esper.epl.agg.aggregator +{ + /// + /// Aggregator for the very first value. + /// + public class AggregatorFirstEverFilter : AggregatorFirstEver + { + public override void Enter(Object parameters) + { + var paramArray = (Object[]) parameters; + if (!AggregatorUtil.CheckFilter(paramArray)) { + return; + } + base.Enter(paramArray[0]); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorLastEver.cs b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorLastEver.cs new file mode 100755 index 000000000..8cc6daf71 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorLastEver.cs @@ -0,0 +1,39 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.agg.aggregator +{ + /// + /// Aggregator for the very last value. + /// + public class AggregatorLastEver : AggregationMethod + { + private Object _lastValue; + + public virtual void Clear() + { + _lastValue = null; + } + + public virtual void Enter(Object @object) + { + _lastValue = @object; + } + + public virtual void Leave(Object @object) + { + } + + public object Value + { + get { return _lastValue; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorLastEverFilter.cs b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorLastEverFilter.cs new file mode 100755 index 000000000..2642db9b9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorLastEverFilter.cs @@ -0,0 +1,30 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; + +using com.espertech.esper.epl.agg.service; + +namespace com.espertech.esper.epl.agg.aggregator +{ + /// + /// Aggregator for the very last value. + /// + public class AggregatorLastEverFilter : AggregatorLastEver + { + public override void Enter(Object parameters) + { + var paramArray = (Object[]) parameters; + if (!AggregatorUtil.CheckFilter(paramArray)) { + return; + } + base.Enter(paramArray[0]); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorLeaving.cs b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorLeaving.cs new file mode 100755 index 000000000..e71dda3d0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorLeaving.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.agg.aggregator +{ + /// + /// For testing if a remove stream entry has been present. + /// + public class AggregatorLeaving : AggregationMethod + { + private bool _leaving = false; + + public virtual void Enter(Object value) + { + } + + public virtual void Leave(Object value) { + _leaving = true; + } + + public virtual object Value + { + get { return _leaving; } + } + + public virtual void Clear() { + _leaving = false; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorMedian.cs b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorMedian.cs new file mode 100755 index 000000000..6192eac32 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorMedian.cs @@ -0,0 +1,77 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; + +using com.espertech.esper.collection; +using com.espertech.esper.compat; + +namespace com.espertech.esper.epl.agg.aggregator +{ + /// Median aggregation. + public class AggregatorMedian : AggregationMethod + { + private readonly SortedDoubleVector _vector; + + /// Ctor. + public AggregatorMedian() + { + _vector = new SortedDoubleVector(); + } + + public virtual void Enter(Object @object) + { + if (@object == null) + { + return; + } + + _vector.Add(@object.AsDouble()); + } + + public virtual void Leave(Object @object) + { + if (@object == null) + { + return; + } + _vector.Remove(@object.AsDouble()); + } + + public virtual void Clear() + { + _vector.Clear(); + } + + public virtual object Value + { + get + { + if (_vector.Count == 0) + { + return null; + } + if (_vector.Count == 1) + { + return _vector[0]; + } + + int middle = _vector.Count >> 1; + if (_vector.Count % 2 == 0) + { + return (_vector[middle - 1] + _vector[middle])/2; + } + else + { + return _vector[middle]; + } + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorMedianFilter.cs b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorMedianFilter.cs new file mode 100755 index 000000000..fbbd24fff --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorMedianFilter.cs @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.agg.service; + +namespace com.espertech.esper.epl.agg.aggregator +{ + /// Median aggregation. + public class AggregatorMedianFilter : AggregatorMedian + { + public override void Enter(Object parameters) + { + var paramArray = (Object[])parameters; + if (!AggregatorUtil.CheckFilter(paramArray)) + { + return; + } + base.Enter(paramArray[0]); + } + + public override void Leave(Object parameters) + { + var paramArray = (Object[])parameters; + if (!AggregatorUtil.CheckFilter(paramArray)) + { + return; + } + base.Leave(paramArray[0]); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorMinMax.cs b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorMinMax.cs new file mode 100755 index 000000000..b7a32ef80 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorMinMax.cs @@ -0,0 +1,77 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; + +using com.espertech.esper.collection; +using com.espertech.esper.type; + +namespace com.espertech.esper.epl.agg.aggregator +{ + /// Min/max aggregator for all values. + public class AggregatorMinMax : AggregationMethod + { + private readonly MinMaxTypeEnum _minMaxTypeEnum; + + private readonly SortedRefCountedSet _refSet; + + /// + /// Ctor. + /// + /// enum indicating to return minimum or maximum values + public AggregatorMinMax(MinMaxTypeEnum minMaxTypeEnum) + { + _minMaxTypeEnum = minMaxTypeEnum; + _refSet = new SortedRefCountedSet(); + } + + public virtual void Clear() + { + _refSet.Clear(); + } + + public virtual void Enter(Object @object) + { + if (@object == null) + { + return; + } + _refSet.Add(@object); + } + + public virtual void Leave(Object @object) + { + if (@object == null) + { + return; + } + _refSet.Remove(@object); + } + + public virtual object Value + { + get + { + if (_minMaxTypeEnum == MinMaxTypeEnum.MAX) + { + return _refSet.MaxValue; + } + else + { + return _refSet.MinValue; + } + } + } + + public SortedRefCountedSet RefSet + { + get { return _refSet; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorMinMaxEver.cs b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorMinMaxEver.cs new file mode 100755 index 000000000..4e10f85b6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorMinMaxEver.cs @@ -0,0 +1,86 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat.logging; +using com.espertech.esper.type; + +namespace com.espertech.esper.epl.agg.aggregator +{ + /// + /// Min/max aggregator for all values, not considering events leaving the aggregation (i.e. ever). + /// + public class AggregatorMinMaxEver : AggregationMethod + { + private static readonly ILog Log = + LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly MinMaxTypeEnum _minMaxTypeEnum; + + private IComparable _currentMinMax; + + /// + /// Ctor. + /// + /// - enum indicating to return minimum or maximum values + public AggregatorMinMaxEver(MinMaxTypeEnum minMaxTypeEnum) + { + _minMaxTypeEnum = minMaxTypeEnum; + } + + public void Clear() + { + _currentMinMax = null; + } + + public virtual void Enter(Object @object) + { + if (@object == null) + { + return; + } + if (_currentMinMax == null) + { + _currentMinMax = (IComparable) @object; + return; + } + if (_minMaxTypeEnum == MinMaxTypeEnum.MAX) + { + if (_currentMinMax.CompareTo(@object) < 0) + { + _currentMinMax = (IComparable) @object; + } + } + else + { + if (_currentMinMax.CompareTo(@object) > 0) + { + _currentMinMax = (IComparable) @object; + } + } + } + + public virtual void Leave(Object @object) + { + // no-op, this is designed to handle min-max ever + Log.Warn(".leave Received remove stream, none was expected"); + } + + public object Value + { + get { return _currentMinMax; } + } + + public IComparable CurrentMinMax + { + get { return _currentMinMax; } + set { this._currentMinMax = value; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorMinMaxEverFilter.cs b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorMinMaxEverFilter.cs new file mode 100755 index 000000000..8d9915732 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorMinMaxEverFilter.cs @@ -0,0 +1,44 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.type; + +namespace com.espertech.esper.epl.agg.aggregator +{ + /// + /// Min/max aggregator for all values, not considering events leaving the aggregation (i.e. ever). + /// + public class AggregatorMinMaxEverFilter : AggregatorMinMaxEver + { + public AggregatorMinMaxEverFilter(MinMaxTypeEnum minMaxTypeEnum) + : base(minMaxTypeEnum) + { + } + + public override void Enter(Object parameters) + { + var paramArray = (Object[]) parameters; + if (!AggregatorUtil.CheckFilter(paramArray)) { + return; + } + base.Enter(paramArray[0]); + } + + public override void Leave(Object parameters) + { + var paramArray = (Object[]) parameters; + if (!AggregatorUtil.CheckFilter(paramArray)) { + return; + } + base.Leave(paramArray[0]); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorMinMaxFilter.cs b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorMinMaxFilter.cs new file mode 100755 index 000000000..f2ada92b7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorMinMaxFilter.cs @@ -0,0 +1,44 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.type; + +namespace com.espertech.esper.epl.agg.aggregator +{ + /// Min/max aggregator for all values. + public class AggregatorMinMaxFilter : AggregatorMinMax + { + public AggregatorMinMaxFilter(MinMaxTypeEnum minMaxTypeEnum) + : base(minMaxTypeEnum) + { + } + + public override void Enter(Object parameters) + { + var paramArray = (Object[])parameters; + if (!AggregatorUtil.CheckFilter(paramArray)) + { + return; + } + base.Enter(paramArray[0]); + } + + public override void Leave(Object parameters) + { + var paramArray = (Object[])parameters; + if (!AggregatorUtil.CheckFilter(paramArray)) + { + return; + } + base.Leave(paramArray[0]); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorNth.cs b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorNth.cs new file mode 100755 index 000000000..94e3430b2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorNth.cs @@ -0,0 +1,75 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.agg.aggregator +{ + /// + /// Aggregator to return the Nth oldest element to enter, with Count=1 the most recent value + /// is returned. If Count is larger than the enter minus leave size, null is returned. A maximum + /// Count historical values are stored, so it can be safely used to compare recent values in large + /// views without incurring excessive overhead. + /// + public class AggregatorNth : AggregationMethod + { + private readonly int _sizeBuf; + + private Object[] _circularBuffer; + private int _currentBufferElementPointer; + private long _numDataPoints; + + /// Ctor. + /// size + public AggregatorNth(int sizeBuf) + { + _sizeBuf = sizeBuf; + } + + public void Enter(Object value) + { + var arr = (Object[]) value; + _numDataPoints++; + if (_circularBuffer == null) + { + Clear(); + } + _circularBuffer[_currentBufferElementPointer] = arr[0]; + _currentBufferElementPointer = (_currentBufferElementPointer + 1) % _sizeBuf; + } + + public void Leave(Object value) + { + if (_sizeBuf > _numDataPoints) + { + int diff = _sizeBuf - (int) _numDataPoints; + _circularBuffer[(_currentBufferElementPointer + diff - 1) % _sizeBuf] = null; + } + _numDataPoints--; + } + + public object Value + { + get + { + if (_circularBuffer == null) + { + return null; + } + return _circularBuffer[(_currentBufferElementPointer + _sizeBuf)%_sizeBuf]; + } + } + + public void Clear() + { + _circularBuffer = new Object[_sizeBuf]; + _numDataPoints = 0; + _currentBufferElementPointer = 0; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorRate.cs b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorRate.cs new file mode 100755 index 000000000..87a3ae6c3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorRate.cs @@ -0,0 +1,79 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; + +namespace com.espertech.esper.epl.agg.aggregator +{ + /// + /// Aggregation computing an event arrival rate for data windowed-events. + /// + public class AggregatorRate : AggregationMethod + { + private long _oneSecondTime; + private double _accumulator; + private long _latest; + private long _oldest; + private bool _isSet = false; + + public AggregatorRate(long oneSecondTime) + { + _oneSecondTime = oneSecondTime; + } + + public virtual void Enter(Object value) + { + if (value.GetType().IsArray) + { + var parameters = (Object[])value; + _accumulator += parameters[1].AsDouble(); + _latest = parameters[0].AsLong(); + } + else + { + _accumulator += 1; + _latest = value.AsLong(); + } + } + + public virtual void Leave(Object value) + { + if (value.GetType().IsArray) + { + var parameters = (Object[])value; + _accumulator -= parameters[1].AsDouble(); + _oldest = parameters[0].AsLong(); + } + else + { + _accumulator -= 1; + _oldest = value.AsLong(); + } + if (!_isSet) _isSet = true; + } + + public virtual object Value + { + get + { + if (!_isSet) + return null; + return (_accumulator * _oneSecondTime) / (_latest - _oldest); + } + } + + public virtual void Clear() + { + _accumulator = 0; + _latest = 0; + _oldest = 0; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorRateEver.cs b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorRateEver.cs new file mode 100755 index 000000000..08b1a502a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorRateEver.cs @@ -0,0 +1,110 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.schedule; + +namespace com.espertech.esper.epl.agg.aggregator +{ + /// + /// Aggregation computing an event arrival rate for with and without data window. + /// + public class AggregatorRateEver : AggregationMethod + { + private readonly long _interval; + private readonly LinkedList _points; + private bool _hasLeave = false; + private readonly TimeProvider _timeProvider; + private readonly long _oneSecondTime; + + /// + /// Ctor. + /// + /// rate interval + /// The one second time. + /// time + public AggregatorRateEver(long interval, long oneSecondTime, TimeProvider timeProvider) + { + _interval = interval; + _oneSecondTime = oneSecondTime; + _timeProvider = timeProvider; + _points = new LinkedList(); + } + + public virtual void Clear() + { + _points.Clear(); + } + + public virtual void Enter(Object @object) + { + long timestamp = _timeProvider.Time; + _points.AddLast(timestamp); + RemoveFromHead(timestamp); + } + + public virtual void Leave(Object @object) + { + // This is an "ever" aggregator and is designed for use in non-window env + } + + public object Value + { + get + { + if (_points.IsNotEmpty()) + { + long newest = _points.Last.Value; + RemoveFromHead(newest); + } + if (!_hasLeave) + { + return null; + } + if (_points.IsEmpty()) + { + return 0d; + } + return (_points.Count * _oneSecondTime * 1.0d) / _interval; + } + } + + private void RemoveFromHead(long timestamp) + { + if (_points.Count > 1) + { + while (true) + { + long first = _points.First.Value; + long delta = timestamp - first; + if (delta >= _interval) + { + _points.RemoveFirst(); + _hasLeave = true; + } + else if (delta == _interval) + { + _hasLeave = true; + break; + } + else + { + break; + } + if (_points.IsEmpty()) + { + break; + } + } + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorStddev.cs b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorStddev.cs new file mode 100755 index 000000000..4b2dbf935 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorStddev.cs @@ -0,0 +1,87 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; + +namespace com.espertech.esper.epl.agg.aggregator +{ + /// + /// Standard deviation always generates double-typed numbers. + /// + public class AggregatorStddev : AggregationMethod + { + private double _mean; + private double _qn; + private long _numDataPoints; + + public virtual void Clear() + { + _mean = 0; + _numDataPoints = 0; + _qn = 0; + } + + public virtual void Enter(Object @object) + { + if (@object == null) + { + return; + } + + double p = (@object).AsDouble(); + + // compute running variance per Knuth's method + if (_numDataPoints == 0) { + _mean = p; + _qn = 0; + _numDataPoints = 1; + } + else { + _numDataPoints++; + double oldmean = _mean; + _mean += (p - _mean)/_numDataPoints; + _qn += (p - oldmean)*(p - _mean); + } + } + + public virtual void Leave(Object @object) + { + if (@object == null) + { + return; + } + + double p = (@object).AsDouble(); + + // compute running variance per Knuth's method + if (_numDataPoints <= 1) { + Clear(); + } + else { + _numDataPoints--; + double oldmean = _mean; + _mean -= (p - _mean)/_numDataPoints; + _qn -= (p - oldmean)*(p - _mean); + } + } + + public virtual object Value + { + get + { + if (_numDataPoints < 2) + { + return null; + } + return Math.Sqrt(_qn/(_numDataPoints - 1)); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorStddevFilter.cs b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorStddevFilter.cs new file mode 100755 index 000000000..467003c3d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorStddevFilter.cs @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; + +using com.espertech.esper.epl.agg.service; + +namespace com.espertech.esper.epl.agg.aggregator +{ + /// + /// Standard deviation always generates double-typed numbers. + /// + public class AggregatorStddevFilter : AggregatorStddev + { + public override void Enter(Object parameters) + { + var paramArray = (Object[]) parameters; + if (!AggregatorUtil.CheckFilter(paramArray)) + { + return; + } + base.Enter(paramArray[0]); + } + + public override void Leave(Object parameters) + { + var paramArray = (Object[]) parameters; + if (!AggregatorUtil.CheckFilter(paramArray)) + { + return; + } + base.Leave(paramArray[0]); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorSumBigInteger.cs b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorSumBigInteger.cs new file mode 100755 index 000000000..ea8149830 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorSumBigInteger.cs @@ -0,0 +1,67 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Numerics; + +using com.espertech.esper.compat; + +namespace com.espertech.esper.epl.agg.aggregator +{ + /// Sum for big integer values. + public class AggregatorSumBigInteger : AggregationMethod + { + private BigInteger _sum; + private long _numDataPoints; + + public virtual void Clear() + { + _sum = 0; + _numDataPoints = 0; + } + + public virtual void Enter(Object @object) + { + if (@object == null) + { + return; + } + _numDataPoints++; + _sum += @object.AsBigInteger(); + } + + public virtual void Leave(Object @object) + { + if (@object == null) + { + return; + } + if (_numDataPoints <= 1) + { + Clear(); + } + else + { + _numDataPoints--; + _sum -= @object.AsBigInteger(); + } + } + + public virtual object Value + { + get + { + if (_numDataPoints == 0) + { + return null; + } + return _sum; + } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorSumBigIntegerFilter.cs b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorSumBigIntegerFilter.cs new file mode 100755 index 000000000..c0e73bcc2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorSumBigIntegerFilter.cs @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.agg.service; + +namespace com.espertech.esper.epl.agg.aggregator +{ + /// + /// Sum for big integer values. + /// + public class AggregatorSumBigIntegerFilter : AggregatorSumBigInteger + { + public override void Enter(Object parameters) + { + var paramArray = (Object[]) parameters; + if (!AggregatorUtil.CheckFilter(paramArray)) + { + return; + } + base.Enter(paramArray[0]); + } + + public override void Leave(Object parameters) + { + var paramArray = (Object[]) parameters; + if (!AggregatorUtil.CheckFilter(paramArray)) + { + return; + } + base.Leave(paramArray[0]); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorSumDecimal.cs b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorSumDecimal.cs new file mode 100755 index 000000000..1969a76e9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorSumDecimal.cs @@ -0,0 +1,65 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; + +namespace com.espertech.esper.epl.agg.aggregator +{ + /// Sum for decimal values. + public class AggregatorSumDecimal : AggregationMethod + { + private decimal _sum; + private long _numDataPoints; + + /// Ctor. + public AggregatorSumDecimal() + { + _sum = 0.0m; + } + + public virtual void Clear() + { + _sum = 0.0m; + _numDataPoints = 0; + } + + public virtual void Enter(Object @object) + { + if (@object == null) + { + return; + } + _numDataPoints++; + _sum += @object.AsDecimal(); + } + + public virtual void Leave(Object @object) + { + if (@object == null) + { + return; + } + _numDataPoints--; + _sum -= @object.AsDecimal(); + } + + public virtual object Value + { + get + { + if (_numDataPoints == 0) + { + return null; + } + return _sum; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorSumDecimalFilter.cs b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorSumDecimalFilter.cs new file mode 100755 index 000000000..fedc570d9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorSumDecimalFilter.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; + +using com.espertech.esper.epl.agg.service; + +namespace com.espertech.esper.epl.agg.aggregator +{ + /// Sum for Bigint? values. + public class AggregatorSumDecimalFilter : AggregatorSumDecimal + { + public override void Enter(Object parameters) + { + var paramArray = (Object[]) parameters; + if (!AggregatorUtil.CheckFilter(paramArray)) { + return; + } + base.Enter(paramArray[0]); + } + + public override void Leave(Object parameters) + { + var paramArray = (Object[]) parameters; + if (!AggregatorUtil.CheckFilter(paramArray)) { + return; + } + base.Leave(paramArray[0]); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorSumDouble.cs b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorSumDouble.cs new file mode 100755 index 000000000..203e67ec1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorSumDouble.cs @@ -0,0 +1,67 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; + +namespace com.espertech.esper.epl.agg.aggregator +{ + /// + /// Sum for double values. + /// + public class AggregatorSumDouble : AggregationMethod + { + private double _sum; + private long _numDataPoints; + + public virtual void Clear() + { + _sum = 0; + _numDataPoints = 0; + } + + public virtual void Enter(Object @object) + { + if (@object == null) + { + return; + } + _numDataPoints++; + _sum += @object.AsDouble(); + } + + public virtual void Leave(Object @object) + { + if (@object == null) + { + return; + } + if (_numDataPoints <= 1) { + Clear(); + } + else { + _numDataPoints--; + _sum -= @object.AsDouble(); + } + } + + public virtual object Value + { + get + { + if (_numDataPoints == 0) + { + return null; + } + return _sum; + } + } + } + +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorSumDoubleFilter.cs b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorSumDoubleFilter.cs new file mode 100755 index 000000000..d856d1299 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorSumDoubleFilter.cs @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.agg.service; + +namespace com.espertech.esper.epl.agg.aggregator +{ + /// Sum for double values. + public class AggregatorSumDoubleFilter : AggregatorSumDouble + { + public override void Enter(Object parameters) + { + var paramArray = (Object[]) parameters; + if (!AggregatorUtil.CheckFilter(paramArray)) + { + return; + } + base.Enter(paramArray[0]); + } + + public override void Leave(Object parameters) + { + var paramArray = (Object[]) parameters; + if (!AggregatorUtil.CheckFilter(paramArray)) + { + return; + } + base.Leave(paramArray[0]); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorSumFloat.cs b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorSumFloat.cs new file mode 100755 index 000000000..a6432ff57 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorSumFloat.cs @@ -0,0 +1,66 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; + +namespace com.espertech.esper.epl.agg.aggregator +{ + /// Sum for float values. + public class AggregatorSumFloat : AggregationMethod + { + private float _sum; + private long _numDataPoints; + + public virtual void Clear() + { + _sum = 0; + _numDataPoints = 0; + } + + public virtual void Enter(Object @object) + { + if (@object == null) + { + return; + } + _numDataPoints++; + _sum += @object.AsFloat(); + } + + public virtual void Leave(Object @object) + { + if (@object == null) + { + return; + } + if (_numDataPoints <= 1) + { + Clear(); + } + else + { + _numDataPoints--; + _sum -= @object.AsFloat(); + } + } + + public virtual object Value + { + get + { + if (_numDataPoints == 0) + { + return null; + } + return _sum; + } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorSumFloatFilter.cs b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorSumFloatFilter.cs new file mode 100755 index 000000000..c1272697a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorSumFloatFilter.cs @@ -0,0 +1,39 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; + +using com.espertech.esper.epl.agg.service; + +namespace com.espertech.esper.epl.agg.aggregator +{ + /// Sum for float values. + public class AggregatorSumFloatFilter : AggregatorSumFloat + { + public override void Enter(Object parameters) + { + var paramArray = (Object[]) parameters; + if (!AggregatorUtil.CheckFilter(paramArray)) + { + return; + } + base.Enter(paramArray[0]); + } + + public override void Leave(Object parameters) + { + var paramArray = (Object[]) parameters; + if (!AggregatorUtil.CheckFilter(paramArray)) + { + return; + } + base.Leave(paramArray[0]); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorSumInteger.cs b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorSumInteger.cs new file mode 100755 index 000000000..29c211748 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorSumInteger.cs @@ -0,0 +1,64 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; + +namespace com.espertech.esper.epl.agg.aggregator +{ + /// Sum for integer values. + public class AggregatorSumInteger : AggregationMethod + { + private int _sum; + private long _numDataPoints; + + public virtual void Clear() + { + _sum = 0; + _numDataPoints = 0; + } + + public virtual void Enter(Object @object) + { + if (@object == null) + { + return; + } + _numDataPoints++; + _sum += @object.AsInt(); + } + + public virtual void Leave(Object @object) + { + if (@object == null) + { + return; + } + if (_numDataPoints <= 1) { + Clear(); + } + else { + _numDataPoints--; + _sum -= @object.AsInt(); + } + } + + public virtual object Value + { + get + { + if (_numDataPoints == 0) + { + return null; + } + return _sum; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorSumIntegerFilter.cs b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorSumIntegerFilter.cs new file mode 100755 index 000000000..715e2312e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorSumIntegerFilter.cs @@ -0,0 +1,39 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; + +using com.espertech.esper.epl.agg.service; + +namespace com.espertech.esper.epl.agg.aggregator +{ + /// Sum for integer values. + public class AggregatorSumIntegerFilter : AggregatorSumInteger + { + public override void Enter(Object parameters) + { + var paramArray = (Object[]) parameters; + if (!AggregatorUtil.CheckFilter(paramArray)) + { + return; + } + base.Enter(paramArray[0]); + } + + public override void Leave(Object parameters) + { + var paramArray = (Object[]) parameters; + if (!AggregatorUtil.CheckFilter(paramArray)) + { + return; + } + base.Leave(paramArray[0]); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorSumLong.cs b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorSumLong.cs new file mode 100755 index 000000000..90ab6a9e4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorSumLong.cs @@ -0,0 +1,66 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; + +namespace com.espertech.esper.epl.agg.aggregator +{ + /// Sum for long values. + public class AggregatorSumLong : AggregationMethod + { + private long _sum; + private long _numDataPoints; + + public virtual void Clear() + { + _sum = 0; + _numDataPoints = 0; + } + + public virtual void Enter(Object @object) + { + if (@object == null) + { + return; + } + _numDataPoints++; + _sum += @object.AsLong(); + } + + public virtual void Leave(Object @object) + { + if (@object == null) + { + return; + } + if (_numDataPoints <= 1) + { + Clear(); + } + else + { + _numDataPoints--; + _sum -= @object.AsLong(); + } + } + + public virtual object Value + { + get + { + if (_numDataPoints == 0) + { + return null; + } + return _sum; + } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorSumLongFilter.cs b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorSumLongFilter.cs new file mode 100755 index 000000000..5023807c1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorSumLongFilter.cs @@ -0,0 +1,39 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; + +using com.espertech.esper.epl.agg.service; + +namespace com.espertech.esper.epl.agg.aggregator +{ + /// Sum for long values. + public class AggregatorSumLongFilter : AggregatorSumLong + { + public override void Enter(Object parameters) + { + var paramArray = (Object[]) parameters; + if (!AggregatorUtil.CheckFilter(paramArray)) + { + return; + } + base.Enter(paramArray[0]); + } + + public override void Leave(Object parameters) + { + var paramArray = (Object[]) parameters; + if (!AggregatorUtil.CheckFilter(paramArray)) + { + return; + } + base.Leave(paramArray[0]); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorSumNumInteger.cs b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorSumNumInteger.cs new file mode 100755 index 000000000..6b5a27a25 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorSumNumInteger.cs @@ -0,0 +1,66 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; + +namespace com.espertech.esper.epl.agg.aggregator +{ + /// Sum for any number value. + public class AggregatorSumNumInteger : AggregationMethod + { + private int _sum; + private long _numDataPoints; + + public virtual void Clear() + { + _sum = 0; + _numDataPoints = 0; + } + + public virtual void Enter(Object @object) + { + if (@object == null) + { + return; + } + _numDataPoints++; + _sum += @object.AsInt(); + } + + public virtual void Leave(Object @object) + { + if (@object == null) + { + return; + } + if (_numDataPoints <= 1) + { + Clear(); + } + else + { + _numDataPoints--; + _sum -= @object.AsInt(); + } + } + + public virtual object Value + { + get + { + if (_numDataPoints == 0) + { + return null; + } + return _sum; + } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorSumNumIntegerFilter.cs b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorSumNumIntegerFilter.cs new file mode 100755 index 000000000..6344324ca --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/aggregator/AggregatorSumNumIntegerFilter.cs @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.agg.service; + +namespace com.espertech.esper.epl.agg.aggregator +{ + /// Sum for any number value. + public class AggregatorSumNumIntegerFilter : AggregatorSumNumInteger + { + public override void Enter(Object parameters) + { + var paramArray = (Object[]) parameters; + if (!AggregatorUtil.CheckFilter(paramArray)) + { + return; + } + base.Enter(paramArray[0]); + } + + public override void Leave(Object parameters) + { + var paramArray = (Object[]) parameters; + if (!AggregatorUtil.CheckFilter(paramArray)) + { + return; + } + base.Leave(paramArray[0]); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationFactoryFactory.cs b/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationFactoryFactory.cs new file mode 100755 index 000000000..083a60708 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationFactoryFactory.cs @@ -0,0 +1,139 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.client.hook; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.approx; +using com.espertech.esper.epl.expression.accessagg; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.methodagg; +using com.espertech.esper.epl.expression.time; +using com.espertech.esper.schedule; + +namespace com.espertech.esper.epl.agg.factory +{ + public interface AggregationFactoryFactory + { + AggregationMethodFactory MakeCount( + StatementExtensionSvcContext statementExtensionSvcContext, + ExprCountNode exprCountNode, + bool ignoreNulls, + Type countedValueType); + + AggregationMethodFactory MakeSum( + StatementExtensionSvcContext statementExtensionSvcContext, + ExprSumNode exprSumNode, + Type childType); + + AggregationMethodFactory MakeAvedev( + StatementExtensionSvcContext statementExtensionSvcContext, + ExprAvedevNode exprAvedevNode, + Type childType, + ExprNode[] positionalParams); + + AggregationMethodFactory MakeAvg( + StatementExtensionSvcContext statementExtensionSvcContext, + ExprAvgNode exprAvgNode, + Type childType, + MathContext optionalMathContext); + + AggregationMethodFactory MakeCountEver( + StatementExtensionSvcContext statementExtensionSvcContext, + ExprCountEverNode exprCountEverNode, + bool ignoreNulls); + + AggregationMethodFactory MakeFirstEver( + StatementExtensionSvcContext statementExtensionSvcContext, + ExprFirstEverNode exprFirstEverNode, + Type type); + + AggregationMethodFactory MakeLastEver( + StatementExtensionSvcContext statementExtensionSvcContext, + ExprLastEverNode exprLastEverNode, + Type type); + + AggregationMethodFactory MakeLeaving( + StatementExtensionSvcContext statementExtensionSvcContext, + ExprLeavingAggNode exprLeavingAggNode); + + AggregationMethodFactory MakeMedian( + StatementExtensionSvcContext statementExtensionSvcContext, + ExprMedianNode exprMedianNode, + Type childType); + + AggregationMethodFactory MakeMinMax( + StatementExtensionSvcContext statementExtensionSvcContext, + ExprMinMaxAggrNode exprMinMaxAggrNode, + Type type, + bool hasDataWindows); + + AggregationMethodFactory MakeNth( + StatementExtensionSvcContext statementExtensionSvcContext, + ExprNthAggNode exprNthAggNode, + Type type, + int size); + + AggregationMethodFactory MakePlugInMethod( + StatementExtensionSvcContext statementExtensionSvcContext, + ExprPlugInAggNode expr, + AggregationFunctionFactory factory, + Type childType); + + AggregationMethodFactory MakeRate( + StatementExtensionSvcContext statementExtensionSvcContext, + ExprRateAggNode exprRateAggNode, + bool isEver, + long intervalMsec, + TimeProvider timeProvider, + TimeAbacus timeAbacus); + + AggregationMethodFactory MakeStddev( + StatementExtensionSvcContext statementExtensionSvcContext, + ExprStddevNode exprStddevNode, + Type childType); + + AggregationMethodFactory MakeLinearUnbounded( + StatementExtensionSvcContext statementExtensionSvcContext, + ExprAggMultiFunctionLinearAccessNode exprAggMultiFunctionLinearAccessNode, + EventType containedType, + Type accessorResultType, + int streamNum); + + AggregationStateFactory MakeLinear( + StatementExtensionSvcContext statementExtensionSvcContext, + ExprAggMultiFunctionLinearAccessNode expr, + int streamNum); + + AggregationStateFactoryCountMinSketch MakeCountMinSketch( + StatementExtensionSvcContext statementExtensionSvcContext, + ExprAggCountMinSketchNode expr, + CountMinSketchSpec specification); + + AggregationStateFactory MakeMinMaxEver( + StatementExtensionSvcContext statementExtensionSvcContext, + ExprAggMultiFunctionSortedMinMaxByNode expr, + AggregationStateMinMaxByEverSpec spec); + + AggregationStateFactory MakePlugInAccess( + StatementExtensionSvcContext statementExtensionSvcContext, + ExprPlugInAggMultiFunctionNodeFactory factory); + + AggregationStateFactory MakeSorted( + StatementExtensionSvcContext statementExtensionSvcContext, + ExprAggMultiFunctionSortedMinMaxByNode expr, + AggregationStateSortedSpec spec); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationFactoryFactoryDefault.cs b/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationFactoryFactoryDefault.cs new file mode 100755 index 000000000..992bf5e97 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationFactoryFactoryDefault.cs @@ -0,0 +1,203 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.client.hook; +using com.espertech.esper.compat; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.approx; +using com.espertech.esper.epl.expression.accessagg; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.methodagg; +using com.espertech.esper.epl.expression.time; +using com.espertech.esper.schedule; + +namespace com.espertech.esper.epl.agg.factory +{ + public class AggregationFactoryFactoryDefault : AggregationFactoryFactory + { + public static readonly AggregationFactoryFactoryDefault INSTANCE = new AggregationFactoryFactoryDefault(); + + private AggregationFactoryFactoryDefault() + { + } + + public AggregationMethodFactory MakeCount( + StatementExtensionSvcContext statementExtensionSvcContext, + ExprCountNode exprCountNode, + bool ignoreNulls, + Type countedValueType) + { + return new AggregationMethodFactoryCount(exprCountNode, ignoreNulls, countedValueType); + } + + public AggregationMethodFactory MakeSum( + StatementExtensionSvcContext statementExtensionSvcContext, + ExprSumNode exprSumNode, + Type childType) + { + return new AggregationMethodFactorySum(exprSumNode, childType); + } + + public AggregationMethodFactory MakeAvedev( + StatementExtensionSvcContext statementExtensionSvcContext, + ExprAvedevNode exprAvedevNode, + Type childType, + ExprNode[] positionalParams) + { + return new AggregationMethodFactoryAvedev(exprAvedevNode, childType, positionalParams); + } + + public AggregationMethodFactory MakeAvg( + StatementExtensionSvcContext statementExtensionSvcContext, + ExprAvgNode exprAvgNode, + Type childType, + MathContext optionalMathContext) + { + return new AggregationMethodFactoryAvg(exprAvgNode, childType, optionalMathContext); + } + + public AggregationMethodFactory MakeCountEver( + StatementExtensionSvcContext statementExtensionSvcContext, + ExprCountEverNode exprCountEverNode, + bool ignoreNulls) + { + return new AggregationMethodFactoryCountEver(exprCountEverNode, ignoreNulls); + } + + public AggregationMethodFactory MakeFirstEver( + StatementExtensionSvcContext statementExtensionSvcContext, + ExprFirstEverNode exprFirstEverNode, + Type type) + { + return new AggregationMethodFactoryFirstEver(exprFirstEverNode, type); + } + + public AggregationMethodFactory MakeLastEver( + StatementExtensionSvcContext statementExtensionSvcContext, + ExprLastEverNode exprLastEverNode, + Type type) + { + return new AggregationMethodFactoryLastEver(exprLastEverNode, type); + } + + public AggregationMethodFactory MakeLeaving( + StatementExtensionSvcContext statementExtensionSvcContext, + ExprLeavingAggNode exprLeavingAggNode) + { + return new AggregationMethodFactoryLeaving(exprLeavingAggNode); + } + + public AggregationMethodFactory MakeMedian( + StatementExtensionSvcContext statementExtensionSvcContext, + ExprMedianNode exprMedianNode, + Type childType) + { + return new AggregationMethodFactoryMedian(exprMedianNode, childType); + } + + public AggregationMethodFactory MakeMinMax( + StatementExtensionSvcContext statementExtensionSvcContext, + ExprMinMaxAggrNode exprMinMaxAggrNode, + Type type, + bool hasDataWindows) + { + return new AggregationMethodFactoryMinMax(exprMinMaxAggrNode, type, hasDataWindows); + } + + public AggregationMethodFactory MakeNth( + StatementExtensionSvcContext statementExtensionSvcContext, + ExprNthAggNode exprNthAggNode, + Type type, + int size) + { + return new AggregationMethodFactoryNth(exprNthAggNode, type, size); + } + + public AggregationMethodFactory MakePlugInMethod( + StatementExtensionSvcContext statementExtensionSvcContext, + ExprPlugInAggNode expr, + AggregationFunctionFactory factory, + Type childType) + { + return new AggregationMethodFactoryPlugIn(expr, factory, childType); + } + + public AggregationMethodFactory MakeRate( + StatementExtensionSvcContext statementExtensionSvcContext, + ExprRateAggNode exprRateAggNode, + bool isEver, + long intervalTime, + TimeProvider timeProvider, + TimeAbacus timeAbacus) + { + return new AggregationMethodFactoryRate(exprRateAggNode, isEver, intervalTime, timeProvider, timeAbacus); + } + + public AggregationMethodFactory MakeStddev( + StatementExtensionSvcContext statementExtensionSvcContext, + ExprStddevNode exprStddevNode, + Type childType) + { + return new AggregationMethodFactoryStddev(exprStddevNode, childType); + } + + public AggregationMethodFactory MakeLinearUnbounded( + StatementExtensionSvcContext statementExtensionSvcContext, + ExprAggMultiFunctionLinearAccessNode parent, + EventType containedType, + Type accessorResultType, + int streamNum) + { + return new AggregationMethodFactoryFirstLastUnbound(parent, containedType, accessorResultType, streamNum); + } + + public AggregationStateFactory MakeLinear( + StatementExtensionSvcContext statementExtensionSvcContext, + ExprAggMultiFunctionLinearAccessNode expr, + int streamNum) + { + return new AggregationStateFactoryLinear(expr, streamNum); + } + + public AggregationStateFactoryCountMinSketch MakeCountMinSketch( + StatementExtensionSvcContext statementExtensionSvcContext, + ExprAggCountMinSketchNode expr, + CountMinSketchSpec specification) + { + return new AggregationStateFactoryCountMinSketch(expr, specification); + } + + public AggregationStateFactory MakeMinMaxEver( + StatementExtensionSvcContext statementExtensionSvcContext, + ExprAggMultiFunctionSortedMinMaxByNode expr, + AggregationStateMinMaxByEverSpec spec) + { + return new AggregationStateFactoryMinMaxByEver(expr, spec); + } + + public AggregationStateFactory MakePlugInAccess( + StatementExtensionSvcContext statementExtensionSvcContext, + ExprPlugInAggMultiFunctionNodeFactory factory) + { + return new AggregationStateFactoryPlugin(factory); + } + + public AggregationStateFactory MakeSorted( + StatementExtensionSvcContext statementExtensionSvcContext, + ExprAggMultiFunctionSortedMinMaxByNode expr, + AggregationStateSortedSpec spec) + { + return new AggregationStateFactorySorted(expr, spec); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationMethodFactoryAvedev.cs b/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationMethodFactoryAvedev.cs new file mode 100755 index 000000000..210a83a3e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationMethodFactoryAvedev.cs @@ -0,0 +1,105 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.aggregator; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.methodagg; + +namespace com.espertech.esper.epl.agg.factory +{ + public class AggregationMethodFactoryAvedev : AggregationMethodFactory + { + protected internal readonly ExprAvedevNode Parent; + protected internal readonly Type AggregatedValueType; + protected internal readonly ExprNode[] PositionalParameters; + + public AggregationMethodFactoryAvedev(ExprAvedevNode parent, Type aggregatedValueType, ExprNode[] positionalParameters) + { + Parent = parent; + AggregatedValueType = aggregatedValueType; + PositionalParameters = positionalParameters; + } + + public bool IsAccessAggregation + { + get { return false; } + } + + public Type ResultType + { + get { return typeof (double?); } + } + + public AggregationStateKey GetAggregationStateKey(bool isMatchRecognize) + { + throw new IllegalStateException("Not an access aggregation function"); + } + + public AggregationStateFactory GetAggregationStateFactory(bool isMatchRecognize) + { + throw new IllegalStateException("Not an access aggregation function"); + } + + public AggregationAccessor Accessor + { + get { throw new IllegalStateException("Not an access aggregation function"); } + } + + public AggregationMethod Make() + { + AggregationMethod method = MakeAvedevAggregator(Parent.HasFilter); + if (!Parent.IsDistinct) + { + return method; + } + return AggregationMethodFactoryUtil.MakeDistinctAggregator(method, Parent.HasFilter); + } + + public ExprAggregateNodeBase AggregationExpression + { + get { return Parent; } + } + + public void ValidateIntoTableCompatible(AggregationMethodFactory intoTableAgg) + { + service.AggregationMethodFactoryUtil.ValidateAggregationType(this, intoTableAgg); + AggregationMethodFactoryAvedev that = (AggregationMethodFactoryAvedev) intoTableAgg; + service.AggregationMethodFactoryUtil.ValidateAggregationInputType(AggregatedValueType, that.AggregatedValueType); + service.AggregationMethodFactoryUtil.ValidateAggregationFilter(Parent.HasFilter, that.Parent.HasFilter); + } + + public AggregationAgent AggregationStateAgent + { + get { return null; } + } + + public ExprEvaluator GetMethodAggregationEvaluator(bool join, EventType[] typesPerStream) + { + return ExprMethodAggUtil.GetDefaultEvaluator(PositionalParameters, join, typesPerStream); + } + + private AggregationMethod MakeAvedevAggregator(bool hasFilter) + { + if (!hasFilter) + { + return new AggregatorAvedev(); + } + else + { + return new AggregatorAvedevFilter(); + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationMethodFactoryAvg.cs b/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationMethodFactoryAvg.cs new file mode 100755 index 000000000..30017e3f9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationMethodFactoryAvg.cs @@ -0,0 +1,119 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Numerics; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.aggregator; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.methodagg; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.agg.factory +{ + public class AggregationMethodFactoryAvg : AggregationMethodFactory + { + protected internal readonly ExprAvgNode Parent; + protected internal readonly Type ChildType; + protected internal readonly MathContext OptionalMathContext; + + public AggregationMethodFactoryAvg(ExprAvgNode parent, Type childType, MathContext optionalMathContext) + { + Parent = parent; + ChildType = childType; + ResultType = GetAvgAggregatorType(childType); + OptionalMathContext = optionalMathContext; + } + + public bool IsAccessAggregation + { + get { return false; } + } + + public Type ResultType { get; private set; } + + public AggregationStateKey GetAggregationStateKey(bool isMatchRecognize) + { + throw new IllegalStateException("Not an access aggregation function"); + } + + public AggregationStateFactory GetAggregationStateFactory(bool isMatchRecognize) + { + throw new IllegalStateException("Not an access aggregation function"); + } + + public AggregationAccessor Accessor + { + get { throw new IllegalStateException("Not an access aggregation function"); } + } + + public AggregationMethod Make() + { + AggregationMethod method = MakeAvgAggregator(ChildType, Parent.HasFilter, OptionalMathContext); + if (!Parent.IsDistinct) + { + return method; + } + return AggregationMethodFactoryUtil.MakeDistinctAggregator(method, Parent.HasFilter); + } + + public ExprAggregateNodeBase AggregationExpression + { + get { return Parent; } + } + + public void ValidateIntoTableCompatible(AggregationMethodFactory intoTableAgg) + { + service.AggregationMethodFactoryUtil.ValidateAggregationType(this, intoTableAgg); + AggregationMethodFactoryAvg that = (AggregationMethodFactoryAvg) intoTableAgg; + service.AggregationMethodFactoryUtil.ValidateAggregationInputType(ChildType, that.ChildType); + service.AggregationMethodFactoryUtil.ValidateAggregationFilter(Parent.HasFilter, that.Parent.HasFilter); + } + + public AggregationAgent AggregationStateAgent + { + get { return null; } + } + + public ExprEvaluator GetMethodAggregationEvaluator(bool join, EventType[] typesPerStream) + { + return ExprMethodAggUtil.GetDefaultEvaluator(Parent.PositionalParams, join, typesPerStream); + } + + private Type GetAvgAggregatorType(Type type) + { + if (type.IsDecimal() || type.IsBigInteger()) + { + return typeof(decimal?); + } + return typeof(double?); + } + + private AggregationMethod MakeAvgAggregator(Type type, bool hasFilter, MathContext optionalMathContext) + { + if (hasFilter) + { + if (type.IsDecimal() || type.IsBigInteger()) + { + return new AggregatorAvgDecimalFilter(optionalMathContext); + } + return new AggregatorAvgFilter(); + } + if (type.IsDecimal() || type.IsBigInteger()) + { + return new AggregatorAvgDecimal(optionalMathContext); + } + return new AggregatorAvg(); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationMethodFactoryCount.cs b/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationMethodFactoryCount.cs new file mode 100755 index 000000000..9a7885f70 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationMethodFactoryCount.cs @@ -0,0 +1,135 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.aggregator; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.methodagg; + +namespace com.espertech.esper.epl.agg.factory +{ + public class AggregationMethodFactoryCount : AggregationMethodFactory + { + protected internal readonly ExprCountNode Parent; + protected internal readonly bool IgnoreNulls; + protected internal readonly Type CountedValueType; + + public AggregationMethodFactoryCount(ExprCountNode parent, bool ignoreNulls, Type countedValueType) + { + Parent = parent; + IgnoreNulls = ignoreNulls; + CountedValueType = countedValueType; + } + + public bool IsAccessAggregation + { + get { return false; } + } + + public Type ResultType + { + get { return typeof (long?); } + } + + public AggregationStateKey GetAggregationStateKey(bool isMatchRecognize) + { + throw new IllegalStateException("Not an access aggregation function"); + } + + public AggregationStateFactory GetAggregationStateFactory(bool isMatchRecognize) + { + throw new IllegalStateException("Not an access aggregation function"); + } + + public AggregationAccessor Accessor + { + get { throw new IllegalStateException("Not an access aggregation function"); } + } + + public AggregationMethod Make() + { + AggregationMethod method = MakeCountAggregator(IgnoreNulls, Parent.HasFilter); + if (!Parent.IsDistinct) + { + return method; + } + return AggregationMethodFactoryUtil.MakeDistinctAggregator(method, Parent.HasFilter); + } + + public ExprAggregateNodeBase AggregationExpression + { + get { return Parent; } + } + + public void ValidateIntoTableCompatible(AggregationMethodFactory intoTableAgg) + { + service.AggregationMethodFactoryUtil.ValidateAggregationType(this, intoTableAgg); + AggregationMethodFactoryCount that = (AggregationMethodFactoryCount) intoTableAgg; + service.AggregationMethodFactoryUtil.ValidateAggregationFilter(Parent.HasFilter, that.Parent.HasFilter); + if (Parent.IsDistinct) { + service.AggregationMethodFactoryUtil.ValidateAggregationInputType(CountedValueType, that.CountedValueType); + } + if (IgnoreNulls != that.IgnoreNulls) { + throw new ExprValidationException("The aggregation declares" + + (IgnoreNulls ? "" : " no") + + " ignore nulls and provided is" + + (that.IgnoreNulls ? "" : " no") + + " ignore nulls"); + } + } + + public AggregationAgent AggregationStateAgent + { + get { return null; } + } + + public ExprEvaluator GetMethodAggregationEvaluator(bool join, EventType[] typesPerStream) + { + return GetMethodAggregationEvaluatorCountBy(Parent.PositionalParams, join, typesPerStream); + } + + public static ExprEvaluator GetMethodAggregationEvaluatorCountBy(ExprNode[] childNodes, bool join, EventType[] typesPerStream) + { + if (childNodes[0] is ExprWildcard && childNodes.Length == 2) + { + return ExprMethodAggUtil.GetDefaultEvaluator(new ExprNode[] {childNodes[1]}, join, typesPerStream); + } + if (childNodes[0] is ExprWildcard && childNodes.Length == 1) + { + return ExprMethodAggUtil.GetDefaultEvaluator(new ExprNode[0], join, typesPerStream); + } + return ExprMethodAggUtil.GetDefaultEvaluator(childNodes, join, typesPerStream); + } + + private AggregationMethod MakeCountAggregator(bool isIgnoreNull, bool hasFilter) + { + if (!hasFilter) + { + if (isIgnoreNull) + { + return new AggregatorCountNonNull(); + } + return new AggregatorCount(); + } + else + { + if (isIgnoreNull) + { + return new AggregatorCountNonNullFilter(); + } + return new AggregatorCountFilter(); + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationMethodFactoryCountEver.cs b/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationMethodFactoryCountEver.cs new file mode 100755 index 000000000..cf58dcb6f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationMethodFactoryCountEver.cs @@ -0,0 +1,113 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.aggregator; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.methodagg; + +namespace com.espertech.esper.epl.agg.factory +{ + public class AggregationMethodFactoryCountEver : AggregationMethodFactory + { + protected internal readonly ExprCountEverNode Parent; + protected internal readonly bool IgnoreNulls; + + public AggregationMethodFactoryCountEver(ExprCountEverNode parent, bool ignoreNulls) + { + Parent = parent; + IgnoreNulls = ignoreNulls; + } + + public bool IsAccessAggregation + { + get { return false; } + } + + public Type ResultType + { + get { return typeof (long); } + } + + public AggregationStateKey GetAggregationStateKey(bool isMatchRecognize) + { + throw new IllegalStateException("Not an access aggregation function"); + } + + public AggregationStateFactory GetAggregationStateFactory(bool isMatchRecognize) + { + throw new IllegalStateException("Not an access aggregation function"); + } + + public AggregationAccessor Accessor + { + get { throw new IllegalStateException("Not an access aggregation function"); } + } + + public AggregationMethod Make() + { + return MakeCountEverValueAggregator(Parent.HasFilter, IgnoreNulls); + } + + public ExprAggregateNodeBase AggregationExpression + { + get { return Parent; } + } + + public void ValidateIntoTableCompatible(AggregationMethodFactory intoTableAgg) + { + service.AggregationMethodFactoryUtil.ValidateAggregationType(this, intoTableAgg); + AggregationMethodFactoryCountEver that = (AggregationMethodFactoryCountEver) intoTableAgg; + if (that.IgnoreNulls != IgnoreNulls) + { + throw new ExprValidationException("The aggregation declares " + + (IgnoreNulls ? "ignore-nulls" : "no-ignore-nulls") + + " and provided is " + + (that.IgnoreNulls ? "ignore-nulls" : "no-ignore-nulls")); + } + service.AggregationMethodFactoryUtil.ValidateAggregationFilter(Parent.HasFilter, that.Parent.HasFilter); + } + + public AggregationAgent AggregationStateAgent + { + get { return null; } + } + + public ExprEvaluator GetMethodAggregationEvaluator(bool join, EventType[] typesPerStream) + { + return ExprMethodAggUtil.GetDefaultEvaluator(Parent.PositionalParams, join, typesPerStream); + } + + private AggregationMethod MakeCountEverValueAggregator(bool hasFilter, bool ignoreNulls) + { + if (!hasFilter) + { + if (ignoreNulls) + { + return new AggregatorCountEverNonNull(); + } + return new AggregatorCountEver(); + } + else + { + if (ignoreNulls) + { + return new AggregatorCountEverNonNullFilter(); + } + return new AggregatorCountEverFilter(); + } + } + } + +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationMethodFactoryFirstEver.cs b/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationMethodFactoryFirstEver.cs new file mode 100755 index 000000000..bc93a801a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationMethodFactoryFirstEver.cs @@ -0,0 +1,87 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.aggregator; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.methodagg; + +namespace com.espertech.esper.epl.agg.factory +{ + public class AggregationMethodFactoryFirstEver : AggregationMethodFactory + { + protected internal readonly ExprFirstEverNode Parent; + protected internal readonly Type ChildType; + + public AggregationMethodFactoryFirstEver(ExprFirstEverNode parent, Type childType) + { + Parent = parent; + ChildType = childType; + } + + public bool IsAccessAggregation + { + get { return false; } + } + + public Type ResultType + { + get { return ChildType; } + } + + public AggregationStateKey GetAggregationStateKey(bool isMatchRecognize) + { + throw new IllegalStateException("Not an access aggregation function"); + } + + public AggregationStateFactory GetAggregationStateFactory(bool isMatchRecognize) + { + throw new IllegalStateException("Not an access aggregation function"); + } + + public AggregationAccessor Accessor + { + get { throw new IllegalStateException("Not an access aggregation function"); } + } + + public AggregationMethod Make() + { + return AggregationMethodFactoryUtil.MakeFirstEver(Parent.HasFilter); + } + + public ExprAggregateNodeBase AggregationExpression + { + get { return Parent; } + } + + public void ValidateIntoTableCompatible(AggregationMethodFactory intoTableAgg) + { + service.AggregationMethodFactoryUtil.ValidateAggregationType(this, intoTableAgg); + AggregationMethodFactoryFirstEver that = (AggregationMethodFactoryFirstEver) intoTableAgg; + service.AggregationMethodFactoryUtil.ValidateAggregationInputType(ChildType, that.ChildType); + service.AggregationMethodFactoryUtil.ValidateAggregationFilter(Parent.HasFilter, that.Parent.HasFilter); + } + + public AggregationAgent AggregationStateAgent + { + get { return null; } + } + + public ExprEvaluator GetMethodAggregationEvaluator(bool join, EventType[] typesPerStream) + { + return ExprMethodAggUtil.GetDefaultEvaluator(Parent.PositionalParams, join, typesPerStream); + } + } + +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationMethodFactoryLastEver.cs b/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationMethodFactoryLastEver.cs new file mode 100755 index 000000000..9c523e2fe --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationMethodFactoryLastEver.cs @@ -0,0 +1,86 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.aggregator; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.methodagg; + +namespace com.espertech.esper.epl.agg.factory +{ + public class AggregationMethodFactoryLastEver : AggregationMethodFactory + { + protected internal readonly ExprLastEverNode Parent; + protected internal readonly Type ChildType; + + public AggregationMethodFactoryLastEver(ExprLastEverNode parent, Type childType) + { + Parent = parent; + ChildType = childType; + } + + public bool IsAccessAggregation + { + get { return false; } + } + + public Type ResultType + { + get { return ChildType; } + } + + public AggregationStateKey GetAggregationStateKey(bool isMatchRecognize) + { + throw new IllegalStateException("Not an access aggregation function"); + } + + public AggregationStateFactory GetAggregationStateFactory(bool isMatchRecognize) + { + throw new IllegalStateException("Not an access aggregation function"); + } + + public AggregationAccessor Accessor + { + get { throw new IllegalStateException("Not an access aggregation function"); } + } + + public AggregationMethod Make() + { + return AggregationMethodFactoryUtil.MakeLastEver(Parent.HasFilter); + } + + public ExprAggregateNodeBase AggregationExpression + { + get { return Parent; } + } + + public void ValidateIntoTableCompatible(AggregationMethodFactory intoTableAgg) + { + service.AggregationMethodFactoryUtil.ValidateAggregationType(this, intoTableAgg); + AggregationMethodFactoryLastEver that = (AggregationMethodFactoryLastEver) intoTableAgg; + service.AggregationMethodFactoryUtil.ValidateAggregationInputType(ChildType, that.ChildType); + service.AggregationMethodFactoryUtil.ValidateAggregationFilter(Parent.HasFilter, that.Parent.HasFilter); + } + + public AggregationAgent AggregationStateAgent + { + get { return null; } + } + + public ExprEvaluator GetMethodAggregationEvaluator(bool join, EventType[] typesPerStream) + { + return ExprMethodAggUtil.GetDefaultEvaluator(Parent.PositionalParams, join, typesPerStream); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationMethodFactoryLeaving.cs b/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationMethodFactoryLeaving.cs new file mode 100755 index 000000000..9b409ab3d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationMethodFactoryLeaving.cs @@ -0,0 +1,81 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.aggregator; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.methodagg; + +namespace com.espertech.esper.epl.agg.factory +{ + public class AggregationMethodFactoryLeaving : AggregationMethodFactory + { + protected internal readonly ExprLeavingAggNode Parent; + + public AggregationMethodFactoryLeaving(ExprLeavingAggNode parent) + { + Parent = parent; + } + + public bool IsAccessAggregation + { + get { return false; } + } + + public Type ResultType + { + get { return typeof (bool?); } + } + + public AggregationStateKey GetAggregationStateKey(bool isMatchRecognize) + { + throw new IllegalStateException("Not an access aggregation function"); + } + + public AggregationStateFactory GetAggregationStateFactory(bool isMatchRecognize) + { + throw new IllegalStateException("Not an access aggregation function"); + } + + public AggregationAccessor Accessor + { + get { throw new IllegalStateException("Not an access aggregation function"); } + } + + public AggregationMethod Make() + { + return new AggregatorLeaving(); + } + + public ExprAggregateNodeBase AggregationExpression + { + get { return Parent; } + } + + public void ValidateIntoTableCompatible(AggregationMethodFactory intoTableAgg) + { + service.AggregationMethodFactoryUtil.ValidateAggregationType(this, intoTableAgg); + } + + public AggregationAgent AggregationStateAgent + { + get { return null; } + } + + public ExprEvaluator GetMethodAggregationEvaluator(bool join, EventType[] typesPerStream) + { + return ExprMethodAggUtil.GetDefaultEvaluator(Parent.PositionalParams, join, typesPerStream); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationMethodFactoryMedian.cs b/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationMethodFactoryMedian.cs new file mode 100755 index 000000000..c86125774 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationMethodFactoryMedian.cs @@ -0,0 +1,98 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.aggregator; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.methodagg; + +namespace com.espertech.esper.epl.agg.factory +{ + public class AggregationMethodFactoryMedian : AggregationMethodFactory + { + protected internal readonly ExprMedianNode Parent; + protected internal readonly Type AggregatedValueType; + + public AggregationMethodFactoryMedian(ExprMedianNode parent, Type aggregatedValueType) + { + Parent = parent; + AggregatedValueType = aggregatedValueType; + } + + public bool IsAccessAggregation + { + get { return false; } + } + + public Type ResultType + { + get { return typeof (double?); } + } + + public AggregationStateKey GetAggregationStateKey(bool isMatchRecognize) + { + throw new IllegalStateException("Not an access aggregation function"); + } + + public AggregationStateFactory GetAggregationStateFactory(bool isMatchRecognize) + { + throw new IllegalStateException("Not an access aggregation function"); + } + + public AggregationAccessor Accessor + { + get { throw new IllegalStateException("Not an access aggregation function"); } + } + + public AggregationMethod Make() + { + AggregationMethod method = MakeMedianAggregator(Parent.HasFilter); + if (!Parent.IsDistinct) { + return method; + } + return AggregationMethodFactoryUtil.MakeDistinctAggregator(method, Parent.HasFilter); + } + + public ExprAggregateNodeBase AggregationExpression + { + get { return Parent; } + } + + public void ValidateIntoTableCompatible(AggregationMethodFactory intoTableAgg) + { + service.AggregationMethodFactoryUtil.ValidateAggregationType(this, intoTableAgg); + AggregationMethodFactoryMedian that = (AggregationMethodFactoryMedian) intoTableAgg; + service.AggregationMethodFactoryUtil.ValidateAggregationInputType(AggregatedValueType, that.AggregatedValueType); + service.AggregationMethodFactoryUtil.ValidateAggregationFilter(Parent.HasFilter, that.Parent.HasFilter); + } + + public AggregationAgent AggregationStateAgent + { + get { return null; } + } + + public ExprEvaluator GetMethodAggregationEvaluator(bool join, EventType[] typesPerStream) + { + return ExprMethodAggUtil.GetDefaultEvaluator(Parent.PositionalParams, join, typesPerStream); + } + + private AggregationMethod MakeMedianAggregator(bool hasFilter) + { + if (!hasFilter) { + return new AggregatorMedian(); + } + return new AggregatorMedianFilter(); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationMethodFactoryMinMax.cs b/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationMethodFactoryMinMax.cs new file mode 100755 index 000000000..e5e9cb7f7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationMethodFactoryMinMax.cs @@ -0,0 +1,126 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.aggregator; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.methodagg; +using com.espertech.esper.type; + +namespace com.espertech.esper.epl.agg.factory +{ + public class AggregationMethodFactoryMinMax : AggregationMethodFactory + { + protected internal readonly ExprMinMaxAggrNode Parent; + protected internal readonly Type Type; + protected internal readonly bool HasDataWindows; + + public AggregationMethodFactoryMinMax(ExprMinMaxAggrNode parent, Type type, bool hasDataWindows) + { + Parent = parent; + Type = type; + HasDataWindows = hasDataWindows; + } + + public bool IsAccessAggregation + { + get { return false; } + } + + public AggregationStateKey GetAggregationStateKey(bool isMatchRecognize) + { + throw new IllegalStateException("Not an access aggregation function"); + } + + public AggregationStateFactory GetAggregationStateFactory(bool isMatchRecognize) + { + throw new IllegalStateException("Not an access aggregation function"); + } + + public AggregationAccessor Accessor + { + get { throw new IllegalStateException("Not an access aggregation function"); } + } + + public Type ResultType + { + get { return Type; } + } + + public AggregationMethod Make() + { + AggregationMethod method = MakeMinMaxAggregator(Parent.MinMaxTypeEnum, Type, HasDataWindows, Parent.HasFilter); + if (!Parent.IsDistinct) { + return method; + } + return AggregationMethodFactoryUtil.MakeDistinctAggregator(method, Parent.HasFilter); + } + + public ExprAggregateNodeBase AggregationExpression + { + get { return Parent; } + } + + public void ValidateIntoTableCompatible(AggregationMethodFactory intoTableAgg) + { + service.AggregationMethodFactoryUtil.ValidateAggregationType(this, intoTableAgg); + AggregationMethodFactoryMinMax that = (AggregationMethodFactoryMinMax) intoTableAgg; + service.AggregationMethodFactoryUtil.ValidateAggregationInputType(Type, that.Type); + service.AggregationMethodFactoryUtil.ValidateAggregationFilter(Parent.HasFilter, that.Parent.HasFilter); + if (Parent.MinMaxTypeEnum != that.Parent.MinMaxTypeEnum) + { + throw new ExprValidationException("The aggregation declares " + + Parent.MinMaxTypeEnum.GetExpressionText() + + " and provided is " + + that.Parent.MinMaxTypeEnum.GetExpressionText()); + } + service.AggregationMethodFactoryUtil.ValidateAggregationUnbound(HasDataWindows, that.HasDataWindows); + } + + public AggregationAgent AggregationStateAgent + { + get { return null; } + } + + public ExprEvaluator GetMethodAggregationEvaluator(bool join, EventType[] typesPerStream) + { + return ExprMethodAggUtil.GetDefaultEvaluator(Parent.PositionalParams, join, typesPerStream); + } + + private AggregationMethod MakeMinMaxAggregator( + MinMaxTypeEnum minMaxTypeEnum, + Type targetType, + bool isHasDataWindows, + bool hasFilter) + { + if (!hasFilter) + { + if (!isHasDataWindows) + { + return new AggregatorMinMaxEver(minMaxTypeEnum); + } + return new AggregatorMinMax(minMaxTypeEnum); + } + else + { + if (!isHasDataWindows) + { + return new AggregatorMinMaxEverFilter(minMaxTypeEnum); + } + return new AggregatorMinMaxFilter(minMaxTypeEnum); + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationMethodFactoryNth.cs b/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationMethodFactoryNth.cs new file mode 100755 index 000000000..46d51e049 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationMethodFactoryNth.cs @@ -0,0 +1,95 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.aggregator; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.methodagg; + +namespace com.espertech.esper.epl.agg.factory +{ + public class AggregationMethodFactoryNth : AggregationMethodFactory + { + protected internal readonly ExprNthAggNode Parent; + protected internal readonly Type ChildType; + protected internal readonly int Size; + + public AggregationMethodFactoryNth(ExprNthAggNode parent, Type childType, int size) + { + Parent = parent; + ChildType = childType; + Size = size; + } + + public bool IsAccessAggregation + { + get { return false; } + } + + public Type ResultType + { + get { return ChildType; } + } + + public AggregationStateKey GetAggregationStateKey(bool isMatchRecognize) + { + throw new IllegalStateException("Not an access aggregation function"); + } + + public AggregationStateFactory GetAggregationStateFactory(bool isMatchRecognize) + { + throw new IllegalStateException("Not an access aggregation function"); + } + + public AggregationAccessor Accessor + { + get { throw new IllegalStateException("Not an access aggregation function"); } + } + + public AggregationMethod Make() + { + AggregationMethod method = new AggregatorNth(Size + 1); + if (!Parent.IsDistinct) + { + return method; + } + return AggregationMethodFactoryUtil.MakeDistinctAggregator(method, false); + } + + public ExprAggregateNodeBase AggregationExpression + { + get { return Parent; } + } + + public void ValidateIntoTableCompatible(AggregationMethodFactory intoTableAgg) + { + service.AggregationMethodFactoryUtil.ValidateAggregationType(this, intoTableAgg); + AggregationMethodFactoryNth that = (AggregationMethodFactoryNth) intoTableAgg; + service.AggregationMethodFactoryUtil.ValidateAggregationInputType(ChildType, that.ChildType); + if (Size != that.Size) { + throw new ExprValidationException(string.Format("The size is {0} and provided is {1}", Size, that.Size)); + } + } + + public AggregationAgent AggregationStateAgent + { + get { return null; } + } + + public ExprEvaluator GetMethodAggregationEvaluator(bool join, EventType[] typesPerStream) + { + return ExprMethodAggUtil.GetDefaultEvaluator(Parent.PositionalParams, join, typesPerStream); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationMethodFactoryPlugIn.cs b/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationMethodFactoryPlugIn.cs new file mode 100755 index 000000000..c90f1b15d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationMethodFactoryPlugIn.cs @@ -0,0 +1,91 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.client.hook; +using com.espertech.esper.compat; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.aggregator; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.methodagg; + +namespace com.espertech.esper.epl.agg.factory +{ + public class AggregationMethodFactoryPlugIn : AggregationMethodFactory + { + protected internal readonly ExprPlugInAggNode Parent; + protected internal readonly AggregationFunctionFactory AggregationFunctionFactory; + protected internal readonly Type AggregatedValueType; + + public AggregationMethodFactoryPlugIn(ExprPlugInAggNode parent, AggregationFunctionFactory aggregationFunctionFactory, Type aggregatedValueType) + { + Parent = parent; + AggregationFunctionFactory = aggregationFunctionFactory; + AggregatedValueType = aggregatedValueType; + } + + public Type ResultType + { + get { return AggregationFunctionFactory.ValueType; } + } + + public bool IsAccessAggregation + { + get { return false; } + } + + public AggregationStateKey GetAggregationStateKey(bool isMatchRecognize) + { + throw new IllegalStateException("Not an access aggregation function"); + } + + public AggregationStateFactory GetAggregationStateFactory(bool isMatchRecognize) + { + throw new IllegalStateException("Not an access aggregation function"); + } + + public AggregationAccessor Accessor + { + get { throw new IllegalStateException("Not an access aggregation function"); } + } + + public AggregationMethod Make() + { + AggregationMethod method = AggregationFunctionFactory.NewAggregator(); + if (!Parent.IsDistinct) + { + return method; + } + return AggregationMethodFactoryUtil.MakeDistinctAggregator(method, false); + } + + public ExprAggregateNodeBase AggregationExpression + { + get { return Parent; } + } + + public void ValidateIntoTableCompatible(AggregationMethodFactory intoTableAgg) + { + service.AggregationMethodFactoryUtil.ValidateAggregationType(this, intoTableAgg); + } + + public AggregationAgent AggregationStateAgent + { + get { return null; } + } + + public ExprEvaluator GetMethodAggregationEvaluator(bool join, EventType[] typesPerStream) + { + return ExprMethodAggUtil.GetDefaultEvaluator(Parent.PositionalParams, join, typesPerStream); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationMethodFactoryRate.cs b/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationMethodFactoryRate.cs new file mode 100755 index 000000000..849855768 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationMethodFactoryRate.cs @@ -0,0 +1,113 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.aggregator; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.methodagg; +using com.espertech.esper.epl.expression.time; +using com.espertech.esper.schedule; + +namespace com.espertech.esper.epl.agg.factory +{ + public class AggregationMethodFactoryRate : AggregationMethodFactory + { + private readonly ExprRateAggNode _parent; + private readonly bool _isEver; + private readonly long _intervalTime; + private readonly TimeProvider _timeProvider; + private readonly TimeAbacus _timeAbacus; + + public AggregationMethodFactoryRate( + ExprRateAggNode parent, + bool isEver, + long intervalTime, + TimeProvider timeProvider, + TimeAbacus timeAbacus) + { + _parent = parent; + _isEver = isEver; + _intervalTime = intervalTime; + _timeProvider = timeProvider; + _timeAbacus = timeAbacus; + } + + public bool IsAccessAggregation + { + get { return false; } + } + + public Type ResultType + { + get { return typeof (double?); } + } + + public AggregationStateKey GetAggregationStateKey(bool isMatchRecognize) + { + throw new IllegalStateException("Not an access aggregation function"); + } + + public AggregationStateFactory GetAggregationStateFactory(bool isMatchRecognize) + { + throw new IllegalStateException("Not an access aggregation function"); + } + + public AggregationAccessor Accessor + { + get { throw new IllegalStateException("Not an access aggregation function"); } + } + + public AggregationMethod Make() + { + if (_isEver) + { + return new AggregatorRateEver(_intervalTime, _timeAbacus.GetOneSecond(), _timeProvider); + } + else + { + return new AggregatorRate(_timeAbacus.GetOneSecond()); + } + } + + public ExprAggregateNodeBase AggregationExpression + { + get { return _parent; } + } + + public void ValidateIntoTableCompatible(AggregationMethodFactory intoTableAgg) + { + service.AggregationMethodFactoryUtil.ValidateAggregationType(this, intoTableAgg); + var that = (AggregationMethodFactoryRate) intoTableAgg; + if (_intervalTime != that._intervalTime) + { + throw new ExprValidationException( + "The size is " + + _intervalTime + + " and provided is " + + that._intervalTime); + } + service.AggregationMethodFactoryUtil.ValidateAggregationUnbound(!_isEver, !that._isEver); + } + + public AggregationAgent AggregationStateAgent + { + get { return null; } + } + + public ExprEvaluator GetMethodAggregationEvaluator(bool join, EventType[] typesPerStream) + { + return ExprMethodAggUtil.GetDefaultEvaluator(_parent.PositionalParams, join, typesPerStream); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationMethodFactoryStddev.cs b/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationMethodFactoryStddev.cs new file mode 100755 index 000000000..c6e179527 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationMethodFactoryStddev.cs @@ -0,0 +1,100 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.aggregator; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.methodagg; + +namespace com.espertech.esper.epl.agg.factory +{ + public class AggregationMethodFactoryStddev : AggregationMethodFactory + { + protected internal readonly ExprStddevNode Parent; + protected internal readonly Type AggregatedValueType; + + public AggregationMethodFactoryStddev(ExprStddevNode parent, Type aggregatedValueType) + { + Parent = parent; + AggregatedValueType = aggregatedValueType; + } + + public bool IsAccessAggregation + { + get { return false; } + } + + public Type ResultType + { + get { return typeof (double?); } + } + + public AggregationStateKey GetAggregationStateKey(bool isMatchRecognize) + { + throw new IllegalStateException("Not an access aggregation function"); + } + + public AggregationStateFactory GetAggregationStateFactory(bool isMatchRecognize) + { + throw new IllegalStateException("Not an access aggregation function"); + } + + public AggregationAccessor Accessor + { + get { throw new IllegalStateException("Not an access aggregation function"); } + } + + public AggregationMethod Make() + { + AggregationMethod method = MakeStddevAggregator(Parent.HasFilter); + if (!Parent.IsDistinct) + { + return method; + } + return AggregationMethodFactoryUtil.MakeDistinctAggregator(method, Parent.HasFilter); + } + + public ExprAggregateNodeBase AggregationExpression + { + get { return Parent; } + } + + public void ValidateIntoTableCompatible(AggregationMethodFactory intoTableAgg) + { + service.AggregationMethodFactoryUtil.ValidateAggregationType(this, intoTableAgg); + AggregationMethodFactoryStddev that = (AggregationMethodFactoryStddev) intoTableAgg; + service.AggregationMethodFactoryUtil.ValidateAggregationInputType(AggregatedValueType, that.AggregatedValueType); + service.AggregationMethodFactoryUtil.ValidateAggregationFilter(Parent.HasFilter, that.Parent.HasFilter); + } + + public AggregationAgent AggregationStateAgent + { + get { return null; } + } + + public ExprEvaluator GetMethodAggregationEvaluator(bool join, EventType[] typesPerStream) + { + return ExprMethodAggUtil.GetDefaultEvaluator(Parent.PositionalParams, join, typesPerStream); + } + + private AggregationMethod MakeStddevAggregator(bool hasFilter) + { + if (!hasFilter) + { + return new AggregatorStddev(); + } + return new AggregatorStddevFilter(); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationMethodFactorySum.cs b/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationMethodFactorySum.cs new file mode 100755 index 000000000..f300481c6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationMethodFactorySum.cs @@ -0,0 +1,180 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Numerics; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.aggregator; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.methodagg; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.agg.factory +{ + public class AggregationMethodFactorySum : AggregationMethodFactory + { + protected internal readonly ExprSumNode Parent; + protected internal readonly Type InputValueType; + + public AggregationMethodFactorySum(ExprSumNode parent, Type inputValueType) + { + Parent = parent; + InputValueType = inputValueType; + ResultType = GetSumAggregatorType(inputValueType); + } + + public bool IsAccessAggregation + { + get { return false; } + } + + public AggregationStateKey GetAggregationStateKey(bool isMatchRecognize) + { + throw new IllegalStateException("Not an access aggregation function"); + } + + public AggregationStateFactory GetAggregationStateFactory(bool isMatchRecognize) + { + throw new IllegalStateException("Not an access aggregation function"); + } + + public AggregationAccessor Accessor + { + get { throw new IllegalStateException("Not an access aggregation function"); } + } + + public Type ResultType { get; private set; } + + public AggregationMethod Make() + { + AggregationMethod method = MakeSumAggregator(InputValueType, Parent.HasFilter); + if (!Parent.IsDistinct) + { + return method; + } + return AggregationMethodFactoryUtil.MakeDistinctAggregator(method, Parent.HasFilter); + } + + public ExprAggregateNodeBase AggregationExpression + { + get { return Parent; } + } + + public void ValidateIntoTableCompatible(AggregationMethodFactory intoTableAgg) + { + service.AggregationMethodFactoryUtil.ValidateAggregationType(this, intoTableAgg); + AggregationMethodFactorySum that = (AggregationMethodFactorySum) intoTableAgg; + service.AggregationMethodFactoryUtil.ValidateAggregationInputType(InputValueType, that.InputValueType); + service.AggregationMethodFactoryUtil.ValidateAggregationFilter(Parent.HasFilter, that.Parent.HasFilter); + } + + public AggregationAgent AggregationStateAgent + { + get { return null; } + } + + public ExprEvaluator GetMethodAggregationEvaluator(bool join, EventType[] typesPerStream) + { + return ExprMethodAggUtil.GetDefaultEvaluator(Parent.PositionalParams, join, typesPerStream); + } + + private Type GetSumAggregatorType(Type type) + { + if (type.IsBigInteger()) + { + return typeof(BigInteger); + } + if (type.IsDecimal()) + { + return typeof(decimal?); + } + if ((type == typeof(long?)) || (type == typeof(long))) + { + return typeof(long?); + } + if ((type == typeof(int?)) || (type == typeof(int))) + { + return typeof(int?); + } + if ((type == typeof(double?)) || (type == typeof(double))) + { + return typeof(double?); + } + if ((type == typeof(float?)) || (type == typeof(float))) + { + return typeof(float?); + } + return typeof(int?); + } + + private AggregationMethod MakeSumAggregator(Type type, bool hasFilter) + { + if (!hasFilter) + { + if (type.IsBigInteger()) + { + return new AggregatorSumBigInteger(); + } + if (type.IsDecimal()) + { + return new AggregatorSumDecimal(); + } + if ((type == typeof(long?)) || (type == typeof(long))) + { + return new AggregatorSumLong(); + } + if ((type == typeof(int?)) || (type == typeof(int))) + { + return new AggregatorSumInteger(); + } + if ((type == typeof(double?)) || (type == typeof(double))) + { + return new AggregatorSumDouble(); + } + if ((type == typeof(float?)) || (type == typeof(float))) + { + return new AggregatorSumFloat(); + } + return new AggregatorSumNumInteger(); + } + else + { + if (type.IsBigInteger()) + { + return new AggregatorSumBigIntegerFilter(); + } + if (type.IsDecimal()) + { + return new AggregatorSumDecimalFilter(); + } + if ((type == typeof(long?)) || (type == typeof(long))) + { + return new AggregatorSumLongFilter(); + } + if ((type == typeof(int?)) || (type == typeof(int))) + { + return new AggregatorSumIntegerFilter(); + } + if ((type == typeof(double?)) || (type == typeof(double))) + { + return new AggregatorSumDoubleFilter(); + } + if ((type == typeof(float?)) || (type == typeof(float))) + { + return new AggregatorSumFloatFilter(); + } + return new AggregatorSumNumIntegerFilter(); + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationMethodFactoryUtil.cs b/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationMethodFactoryUtil.cs new file mode 100755 index 000000000..6a94675a4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationMethodFactoryUtil.cs @@ -0,0 +1,42 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.agg.aggregator; + +namespace com.espertech.esper.epl.agg.factory +{ + public class AggregationMethodFactoryUtil + { + public static AggregationMethod MakeDistinctAggregator(AggregationMethod aggregationMethod, bool hasFilter) + { + if (hasFilter) + { + return new AggregatorDistinctValueFilter(aggregationMethod); + } + return new AggregatorDistinctValue(aggregationMethod); + } + + public static AggregationMethod MakeFirstEver(bool hasFilter) + { + if (hasFilter) + { + return new AggregatorFirstEverFilter(); + } + return new AggregatorFirstEver(); + } + + public static AggregationMethod MakeLastEver(bool hasFilter) + { + if (hasFilter) + { + return new AggregatorLastEverFilter(); + } + return new AggregatorLastEver(); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationStateFactoryCountMinSketch.cs b/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationStateFactoryCountMinSketch.cs new file mode 100755 index 000000000..167d7ff66 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationStateFactoryCountMinSketch.cs @@ -0,0 +1,39 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.approx; +using com.espertech.esper.epl.expression.accessagg; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.agg.factory +{ + public class AggregationStateFactoryCountMinSketch : AggregationStateFactory + { + public AggregationStateFactoryCountMinSketch(ExprAggCountMinSketchNode parent, CountMinSketchSpec specification) + { + Parent = parent; + Specification = specification; + } + + public AggregationState CreateAccess(int agentInstanceId, bool join, object groupKey, AggregationServicePassThru passThru) + { + return new CountMinSketchAggState(CountMinSketchState.MakeState(Specification), Specification.Agent); + } + + public ExprNode AggregationExpression + { + get { return Parent; } + } + + public CountMinSketchSpec Specification { get; private set; } + + public ExprAggCountMinSketchNode Parent { get; private set; } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationStateFactoryLinear.cs b/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationStateFactoryLinear.cs new file mode 100755 index 000000000..947fdaf38 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationStateFactoryLinear.cs @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.accessagg; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.agg.factory +{ + public class AggregationStateFactoryLinear : AggregationStateFactory + { + protected internal readonly ExprAggMultiFunctionLinearAccessNode Expr; + protected internal readonly int StreamNum; + + public AggregationStateFactoryLinear(ExprAggMultiFunctionLinearAccessNode expr, int streamNum) + { + Expr = expr; + StreamNum = streamNum; + } + + public AggregationState CreateAccess(int agentInstanceId, bool join, object groupKey, AggregationServicePassThru passThru) + { + if (join) + { + return new AggregationStateJoinImpl(StreamNum); + } + return new AggregationStateImpl(StreamNum); + } + + public ExprNode AggregationExpression + { + get { return Expr; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationStateFactoryMinMaxByEver.cs b/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationStateFactoryMinMaxByEver.cs new file mode 100755 index 000000000..482dd4bc6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationStateFactoryMinMaxByEver.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.accessagg; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.agg.factory +{ + public class AggregationStateFactoryMinMaxByEver : AggregationStateFactory + { + protected internal readonly ExprAggMultiFunctionSortedMinMaxByNode Expr; + protected internal readonly AggregationStateMinMaxByEverSpec Spec; + + public AggregationStateFactoryMinMaxByEver(ExprAggMultiFunctionSortedMinMaxByNode expr, AggregationStateMinMaxByEverSpec spec) + { + Expr = expr; + Spec = spec; + } + + public AggregationState CreateAccess(int agentInstanceId, bool join, object groupKey, AggregationServicePassThru passThru) + { + return new AggregationStateMinMaxByEver(Spec); + } + + public ExprNode AggregationExpression + { + get { return Expr; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationStateFactoryPlugin.cs b/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationStateFactoryPlugin.cs new file mode 100755 index 000000000..4eb007c87 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationStateFactoryPlugin.cs @@ -0,0 +1,39 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.accessagg; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.plugin; + +namespace com.espertech.esper.epl.agg.factory +{ + public class AggregationStateFactoryPlugin : AggregationStateFactory + { + protected internal readonly ExprPlugInAggMultiFunctionNodeFactory Parent; + protected internal readonly PlugInAggregationMultiFunctionStateFactory StateFactory; + + public AggregationStateFactoryPlugin(ExprPlugInAggMultiFunctionNodeFactory parent) + { + Parent = parent; + StateFactory = parent.HandlerPlugin.StateFactory; + } + + public AggregationState CreateAccess(int agentInstanceId, bool join, object groupBy, AggregationServicePassThru passThru) + { + var context = new PlugInAggregationMultiFunctionStateContext(agentInstanceId, groupBy); + return StateFactory.MakeAggregationState(context); + } + + public ExprNode AggregationExpression + { + get { return Parent.AggregationExpression; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationStateFactorySorted.cs b/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationStateFactorySorted.cs new file mode 100755 index 000000000..2aa798c98 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/factory/AggregationStateFactorySorted.cs @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.accessagg; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.agg.factory +{ + public class AggregationStateFactorySorted : AggregationStateFactory + { + protected internal readonly ExprAggMultiFunctionSortedMinMaxByNode Expr; + protected internal readonly AggregationStateSortedSpec Spec; + + public AggregationStateFactorySorted(ExprAggMultiFunctionSortedMinMaxByNode expr, AggregationStateSortedSpec spec) + { + Expr = expr; + Spec = spec; + } + + public AggregationState CreateAccess(int agentInstanceId, bool join, object groupKey, AggregationServicePassThru passThru) + { + if (join) + { + return new AggregationStateSortedJoin(Spec); + } + return new AggregationStateSortedImpl(Spec); + } + + public ExprNode AggregationExpression + { + get { return Expr; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/agg/rollup/GroupByExpressionHelper.cs b/NEsper.Core/NEsper.Core/epl/agg/rollup/GroupByExpressionHelper.cs new file mode 100755 index 000000000..768371b68 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/rollup/GroupByExpressionHelper.cs @@ -0,0 +1,266 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.visitor; +using com.espertech.esper.epl.spec; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.agg.rollup +{ + public class GroupByExpressionHelper + { + public static GroupByClauseExpressions GetGroupByRollupExpressions( + IList groupByElements, + SelectClauseSpecRaw selectClauseSpec, + ExprNode optionalHavingNode, + IList orderByList, + ExprNodeSubselectDeclaredDotVisitor visitor) + { + if (groupByElements == null || groupByElements.Count == 0) { + return null; + } + + // walk group-by-elements, determine group-by expressions and rollup nodes + var groupByExpressionInfo = GroupByToRollupNodes(groupByElements); + + // obtain expression nodes, collect unique nodes and assign index + var distinctGroupByExpressions = new List(); + var expressionToIndex = new Dictionary(); + foreach (ExprNode exprNode in groupByExpressionInfo.Expressions) { + var found = false; + for (var i = 0; i < distinctGroupByExpressions.Count; i++) { + ExprNode other = distinctGroupByExpressions[i]; + // find same expression + if (ExprNodeUtility.DeepEquals(exprNode, other)) { + expressionToIndex.Put(exprNode, i); + found = true; + break; + } + } + + // not seen before + if (!found) { + expressionToIndex.Put(exprNode, distinctGroupByExpressions.Count); + distinctGroupByExpressions.Add(exprNode); + } + } + + // determine rollup, validate it is either (not both) + var hasGroupingSet = false; + var hasRollup = false; + foreach (var element in groupByElements) { + if (element is GroupByClauseElementGroupingSet) { + hasGroupingSet = true; + } + if (element is GroupByClauseElementRollupOrCube) { + hasRollup = true; + } + } + + // no-rollup or grouping-sets means simply validate + var groupByExpressions = distinctGroupByExpressions.ToArray(); + if (!hasRollup && !hasGroupingSet) { + return new GroupByClauseExpressions(groupByExpressions); + } + + // evaluate rollup node roots + IList nodes = groupByExpressionInfo.Nodes; + var perNodeCombinations = new Object[nodes.Count][]; + var context = new GroupByRollupEvalContext(expressionToIndex); + try { + for (var i = 0; i < nodes.Count; i++) { + var node = nodes[i]; + var combinations = node.Evaluate(context); + perNodeCombinations[i] = new Object[combinations.Count]; + for (var j = 0; j < combinations.Count; j++) { + perNodeCombinations[i][j] = combinations[j]; + } + } + } + catch (GroupByRollupDuplicateException ex) { + if (ex.Indexes.Length == 0) { + throw new ExprValidationException("Failed to validate the group-by clause, found duplicate specification of the overall grouping '()'"); + } + else { + var writer = new StringWriter(); + var delimiter = ""; + for (var i = 0; i < ex.Indexes.Length; i++) { + writer.Write(delimiter); + writer.Write(groupByExpressions[ex.Indexes[i]].ToExpressionStringMinPrecedenceSafe()); + delimiter = ", "; + } + throw new ExprValidationException("Failed to validate the group-by clause, found duplicate specification of expressions (" + writer.ToString() + ")"); + } + } + + // enumerate combinations building an index list + var combinationEnumeration = new CombinationEnumeration(perNodeCombinations); + ICollection combination = new SortedSet(); + ICollection indexList = new LinkedHashSet(); + while(combinationEnumeration.MoveNext()) + { + combination.Clear(); + Object[] combinationOA = combinationEnumeration.Current; + foreach (var indexes in combinationOA) { + var indexarr = (int[]) indexes; + foreach (var anIndex in indexarr) { + combination.Add(anIndex); + } + } + var indexArr = CollectionUtil.IntArray(combination); + indexList.Add(new MultiKeyInt(indexArr)); + } + + // obtain rollup levels + var rollupLevels = new int[indexList.Count][]; + var count = 0; + foreach (var mk in indexList) { + rollupLevels[count++] = mk.Keys; + } + var numberOfLevels = rollupLevels.Length; + if (numberOfLevels == 1 && rollupLevels[0].Length == 0) { + throw new ExprValidationException("Failed to validate the group-by clause, the overall grouping '()' cannot be the only grouping"); + } + + // obtain select-expression copies for rewrite + var expressions = selectClauseSpec.SelectExprList; + var selects = new ExprNode[numberOfLevels][]; + for (var i = 0; i < numberOfLevels; i++) { + selects[i] = new ExprNode[expressions.Count]; + for (var j = 0; j < expressions.Count; j++) { + SelectClauseElementRaw selectRaw = expressions[j]; + if (!(selectRaw is SelectClauseExprRawSpec)) { + throw new ExprValidationException("Group-by with rollup requires that the select-clause does not use wildcard"); + } + var compiled = (SelectClauseExprRawSpec) selectRaw; + selects[i][j] = CopyVisitExpression(compiled.SelectExpression, visitor); + } + } + + // obtain having-expression copies for rewrite + ExprNode[] optHavingNodeCopy = null; + if (optionalHavingNode != null) { + optHavingNodeCopy = new ExprNode[numberOfLevels]; + for (var i = 0; i < numberOfLevels; i++) { + optHavingNodeCopy[i] = CopyVisitExpression(optionalHavingNode, visitor); + } + } + + // obtain orderby-expression copies for rewrite + ExprNode[][] optOrderByCopy = null; + if (orderByList != null && orderByList.Count > 0) { + optOrderByCopy = new ExprNode[numberOfLevels][]; + for (var i = 0; i < numberOfLevels; i++) { + optOrderByCopy[i] = new ExprNode[orderByList.Count]; + for (var j = 0; j < orderByList.Count; j++) + { + OrderByItem element = orderByList[j]; + optOrderByCopy[i][j] = CopyVisitExpression(element.ExprNode, visitor); + } + } + } + + return new GroupByClauseExpressions(groupByExpressions, rollupLevels, selects, optHavingNodeCopy, optOrderByCopy); + } + + private static GroupByExpressionInfo GroupByToRollupNodes(IList groupByExpressions) { + IList parents = new List(groupByExpressions.Count); + IList exprNodes = new List(); + + foreach (var element in groupByExpressions) { + + GroupByRollupNodeBase parent; + if (element is GroupByClauseElementExpr) { + var expr = (GroupByClauseElementExpr) element; + exprNodes.Add(expr.Expr); + parent = new GroupByRollupNodeSingleExpr(expr.Expr); + } + else if (element is GroupByClauseElementRollupOrCube) { + var spec = (GroupByClauseElementRollupOrCube) element; + parent = new GroupByRollupNodeRollupOrCube(spec.IsCube); + GroupByAddRollup(spec, parent, exprNodes); + } + else if (element is GroupByClauseElementGroupingSet) { + var spec = (GroupByClauseElementGroupingSet) element; + parent = new GroupByRollupNodeGroupingSet(); + foreach (var groupElement in spec.Elements) { + if (groupElement is GroupByClauseElementExpr) { + var single = (GroupByClauseElementExpr) groupElement; + exprNodes.Add(single.Expr); + parent.Add(new GroupByRollupNodeSingleExpr(single.Expr)); + } + if (groupElement is GroupByClauseElementCombinedExpr) { + var combined = (GroupByClauseElementCombinedExpr) groupElement; + exprNodes.AddAll(combined.Expressions); + parent.Add(new GroupByRollupNodeCombinedExpr(combined.Expressions)); + } + if (groupElement is GroupByClauseElementRollupOrCube) { + var rollup = (GroupByClauseElementRollupOrCube) groupElement; + var node = new GroupByRollupNodeRollupOrCube(rollup.IsCube); + GroupByAddRollup(rollup, node, exprNodes); + parent.Add(node); + } + } + } + else { + throw new IllegalStateException("Unexpected group-by clause element " + element); + } + parents.Add(parent); + } + + return new GroupByExpressionInfo(exprNodes, parents); + } + + private static void GroupByAddRollup(GroupByClauseElementRollupOrCube spec, GroupByRollupNodeBase parent, IList exprNodes) { + foreach (var rolledUp in spec.RollupExpressions) { + if (rolledUp is GroupByClauseElementExpr) { + var expr = (GroupByClauseElementExpr) rolledUp; + exprNodes.Add(expr.Expr); + parent.Add(new GroupByRollupNodeSingleExpr(expr.Expr)); + } + else { + var combined = (GroupByClauseElementCombinedExpr) rolledUp; + exprNodes.AddAll(combined.Expressions); + parent.Add(new GroupByRollupNodeCombinedExpr(combined.Expressions)); + } + } + } + + private static ExprNode CopyVisitExpression(ExprNode expression, ExprNodeSubselectDeclaredDotVisitor visitor) { + try { + var node = (ExprNode) SerializableObjectCopier.Copy(expression); + node.Accept(visitor); + return node; + } catch (Exception e) { + throw new Exception("Internal error providing expression tree: " + e.Message, e); + } + } + + internal class GroupByExpressionInfo + { + internal GroupByExpressionInfo(IList expressions, IList nodes) + { + Expressions = expressions; + Nodes = nodes; + } + + public IList Expressions { get; private set; } + + public IList Nodes { get; private set; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/rollup/GroupByRollupDuplicateException.cs b/NEsper.Core/NEsper.Core/epl/agg/rollup/GroupByRollupDuplicateException.cs new file mode 100755 index 000000000..87e0f4758 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/rollup/GroupByRollupDuplicateException.cs @@ -0,0 +1,22 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.agg.rollup +{ + public class GroupByRollupDuplicateException : Exception + { + public GroupByRollupDuplicateException(int[] indexes) + { + Indexes = indexes; + } + + public int[] Indexes { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/agg/rollup/GroupByRollupEvalContext.cs b/NEsper.Core/NEsper.Core/epl/agg/rollup/GroupByRollupEvalContext.cs new file mode 100755 index 000000000..49651c531 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/rollup/GroupByRollupEvalContext.cs @@ -0,0 +1,31 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.agg.rollup +{ + public class GroupByRollupEvalContext + { + private readonly IDictionary _expressionToIndex; + + public GroupByRollupEvalContext(IDictionary expressionToIndex) + { + _expressionToIndex = expressionToIndex; + } + + public int GetIndex(ExprNode node) + { + return _expressionToIndex.Get(node); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/agg/rollup/GroupByRollupKey.cs b/NEsper.Core/NEsper.Core/epl/agg/rollup/GroupByRollupKey.cs new file mode 100755 index 000000000..d96c2061f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/rollup/GroupByRollupKey.cs @@ -0,0 +1,52 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.agg.service; + +namespace com.espertech.esper.epl.agg.rollup +{ + public class GroupByRollupKey + { + private readonly EventBean[] _generator; + private readonly Object _groupKey; + private readonly AggregationGroupByRollupLevel _level; + + public GroupByRollupKey(EventBean[] generator, AggregationGroupByRollupLevel level, Object groupKey) + { + _generator = generator; + _level = level; + _groupKey = groupKey; + } + + public EventBean[] Generator + { + get { return _generator; } + } + + public AggregationGroupByRollupLevel Level + { + get { return _level; } + } + + public object GroupKey + { + get { return _groupKey; } + } + + public override String ToString() + { + return "GroupRollupKey{" + + "level=" + _level + + ", groupKey=" + _groupKey + + '}'; + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/agg/rollup/GroupByRollupLevelEventPair.cs b/NEsper.Core/NEsper.Core/epl/agg/rollup/GroupByRollupLevelEventPair.cs new file mode 100755 index 000000000..9b4b5e2bf --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/rollup/GroupByRollupLevelEventPair.cs @@ -0,0 +1,35 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.epl.agg.service; + +namespace com.espertech.esper.epl.agg.rollup +{ + public class GroupByRollupLevelEventPair + { + private readonly AggregationGroupByRollupLevel _level; + private readonly EventBean _event; + + public GroupByRollupLevelEventPair(AggregationGroupByRollupLevel level, EventBean @event) + { + _level = level; + _event = @event; + } + + public AggregationGroupByRollupLevel Level + { + get { return _level; } + } + + public EventBean Event + { + get { return _event; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/rollup/GroupByRollupLevelKeyPair.cs b/NEsper.Core/NEsper.Core/epl/agg/rollup/GroupByRollupLevelKeyPair.cs new file mode 100755 index 000000000..5f8578d5f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/rollup/GroupByRollupLevelKeyPair.cs @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.agg.service; + +namespace com.espertech.esper.epl.agg.rollup +{ + public class GroupByRollupLevelKeyPair + { + public GroupByRollupLevelKeyPair(AggregationGroupByRollupLevel level, Object key) + { + Level = level; + Key = key; + } + + public AggregationGroupByRollupLevel Level { get; private set; } + + public object Key { get; private set; } + + public override bool Equals(Object o) + { + if (this == o) + return true; + if (o == null || GetType() != o.GetType()) + return false; + + var that = (GroupByRollupLevelKeyPair) o; + + if (Key != null ? !Key.Equals(that.Key) : that.Key != null) + return false; + if (!Level.Equals(that.Level)) + return false; + + return true; + } + + public override int GetHashCode() + { + int result = Level.GetHashCode(); + result = 31*result + (Key != null ? Key.GetHashCode() : 0); + return result; + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/agg/rollup/GroupByRollupNodeBase.cs b/NEsper.Core/NEsper.Core/epl/agg/rollup/GroupByRollupNodeBase.cs new file mode 100755 index 000000000..adcb7029a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/rollup/GroupByRollupNodeBase.cs @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +namespace com.espertech.esper.epl.agg.rollup +{ + public abstract class GroupByRollupNodeBase + { + private readonly IList _childNodes = new List(); + + public abstract IList Evaluate(GroupByRollupEvalContext context); + + public IList ChildNodes + { + get { return _childNodes; } + } + + public void Add(GroupByRollupNodeBase child) + { + _childNodes.Add(child); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/rollup/GroupByRollupNodeCombinedExpr.cs b/NEsper.Core/NEsper.Core/epl/agg/rollup/GroupByRollupNodeCombinedExpr.cs new file mode 100755 index 000000000..75deb3f03 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/rollup/GroupByRollupNodeCombinedExpr.cs @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.agg.rollup +{ + public class GroupByRollupNodeCombinedExpr : GroupByRollupNodeBase + { + private readonly IList _expressions; + + public GroupByRollupNodeCombinedExpr(IList expressions) + { + _expressions = expressions; + } + + public override IList Evaluate(GroupByRollupEvalContext context) + { + var result = new int[_expressions.Count]; + for (int i = 0; i < _expressions.Count; i++) + { + int index = context.GetIndex(_expressions[i]); + result[i] = index; + } + + result.SortInPlace(); + + // find dups + for (int i = 0; i < result.Length - 1; i++) + { + if (result[i] == result[i + 1]) + { + throw new GroupByRollupDuplicateException( + new int[] + { + result[i] + }); + } + } + + return Collections.SingletonList(result); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/agg/rollup/GroupByRollupNodeGroupingSet.cs b/NEsper.Core/NEsper.Core/epl/agg/rollup/GroupByRollupNodeGroupingSet.cs new file mode 100755 index 000000000..45c225d5e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/rollup/GroupByRollupNodeGroupingSet.cs @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.epl.agg.rollup +{ + public class GroupByRollupNodeGroupingSet : GroupByRollupNodeBase + { + public override IList Evaluate(GroupByRollupEvalContext context) + { + IList rollup = new List(); + foreach (GroupByRollupNodeBase node in this.ChildNodes) + { + IList result = node.Evaluate(context); + + // find dups + foreach (var row in result) + { + foreach (var existing in rollup) + { + if (Collections.AreEqual(row, existing)) + { + throw new GroupByRollupDuplicateException(row); + } + } + } + + rollup.AddAll(result); + } + return rollup; + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/agg/rollup/GroupByRollupNodeRollupOrCube.cs b/NEsper.Core/NEsper.Core/epl/agg/rollup/GroupByRollupNodeRollupOrCube.cs new file mode 100755 index 000000000..dc4cdb175 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/rollup/GroupByRollupNodeRollupOrCube.cs @@ -0,0 +1,156 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.agg.rollup +{ + public class GroupByRollupNodeRollupOrCube : GroupByRollupNodeBase + { + private readonly bool _cube; + + public GroupByRollupNodeRollupOrCube(bool cube) + { + _cube = cube; + } + + public override IList Evaluate(GroupByRollupEvalContext context) + { + int[][] childIndexes = EvaluateChildNodes(context); + + // find duplicate entries among child expressions + for (int i = 0; i < childIndexes.Length; i++) + { + for (int j = i + 1; j < childIndexes.Length; j++) + { + ValidateCompare(childIndexes[i], childIndexes[j]); + } + } + + IList rollup; + if (_cube) + { + rollup = HandleCube(childIndexes); + } + else + { + rollup = HandleRollup(childIndexes); + } + rollup.Add(new int[0]); + return rollup; + } + + private static void ValidateCompare(int[] one, int[] other) + { + if (Collections.AreEqual(one, other)) + { + throw new GroupByRollupDuplicateException(one); + } + } + + private IList HandleCube(int[][] childIndexes) + { + var enumerationSorted = new List(); + var size = ChildNodes.Count; + var e = new NumberAscCombinationEnumeration(size); + while (e.MoveNext()) + { + enumerationSorted.Add(e.Current); + } + + + enumerationSorted.SortInPlace( + (o1, o2) => + { + int shared = Math.Min(o1.Length, o2.Length); + for (int i = 0; i < shared; i++) + { + if (o1[i] < o2[i]) + { + return -1; + } + if (o1[i] > o2[i]) + { + return 1; + } + } + if (o1.Length > o2.Length) + { + return -1; + } + if (o1.Length < o2.Length) + { + return 1; + } + return 0; + }); + + var rollup = new List(enumerationSorted.Count + 1); + var keys = new LinkedHashSet(); + foreach (var item in enumerationSorted) + { + keys.Clear(); + foreach (int index in item) + { + int[] childIndex = childIndexes[index]; + foreach (int childIndexItem in childIndex) + { + keys.Add(childIndexItem); + } + } + rollup.Add(CollectionUtil.IntArray(keys)); + } + return rollup; + } + + private IList HandleRollup(int[][] childIndexes) + { + var size = ChildNodes.Count; + var rollup = new List(size + 1); + var keyset = new LinkedHashSet(); + + for (int i = 0; i < size; i++) + { + keyset.Clear(); + + for (int j = 0; j < size - i; j++) + { + int[] childIndex = childIndexes[j]; + foreach (int aChildIndex in childIndex) + { + keyset.Add(aChildIndex); + } + } + rollup.Add(CollectionUtil.IntArray(keyset)); + } + return rollup; + } + + private int[][] EvaluateChildNodes(GroupByRollupEvalContext context) + { + int size = ChildNodes.Count; + var childIndexes = new int[size][]; + for (int i = 0; i < size; i++) + { + var childIndex = ChildNodes[i].Evaluate(context); + if (childIndex.Count != 1) + { + throw new IllegalStateException(); + } + childIndexes[i] = childIndex[0]; + } + return childIndexes; + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/agg/rollup/GroupByRollupNodeSingleExpr.cs b/NEsper.Core/NEsper.Core/epl/agg/rollup/GroupByRollupNodeSingleExpr.cs new file mode 100755 index 000000000..840aebad2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/rollup/GroupByRollupNodeSingleExpr.cs @@ -0,0 +1,36 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.agg.rollup +{ + public class GroupByRollupNodeSingleExpr : GroupByRollupNodeBase + { + private readonly ExprNode _expression; + + public GroupByRollupNodeSingleExpr(ExprNode expression) + { + _expression = expression; + } + + public override IList Evaluate(GroupByRollupEvalContext context) + { + int index = context.GetIndex(_expression); + return Collections.SingletonList( + new int[] + { + index + }); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/agg/rollup/GroupByRollupPerLevelExpression.cs b/NEsper.Core/NEsper.Core/epl/agg/rollup/GroupByRollupPerLevelExpression.cs new file mode 100755 index 000000000..279bd1dfd --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/rollup/GroupByRollupPerLevelExpression.cs @@ -0,0 +1,46 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.agg.rollup +{ + public class GroupByRollupPerLevelExpression + { + private readonly ExprEvaluator[] _optionalHavingNodes; + private readonly OrderByElement[][] _optionalOrderByElements; + private readonly SelectExprProcessor[] _selectExprProcessor; + + public GroupByRollupPerLevelExpression( + SelectExprProcessor[] selectExprProcessor, + ExprEvaluator[] optionalHavingNodes, + OrderByElement[][] optionalOrderByElements) + { + _selectExprProcessor = selectExprProcessor; + _optionalHavingNodes = optionalHavingNodes; + _optionalOrderByElements = optionalOrderByElements; + } + + public SelectExprProcessor[] SelectExprProcessor + { + get { return _selectExprProcessor; } + } + + public ExprEvaluator[] OptionalHavingNodes + { + get { return _optionalHavingNodes; } + } + + public OrderByElement[][] OptionalOrderByElements + { + get { return _optionalOrderByElements; } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/agg/rollup/GroupByRollupPlanDesc.cs b/NEsper.Core/NEsper.Core/epl/agg/rollup/GroupByRollupPlanDesc.cs new file mode 100755 index 000000000..6e9837de4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/rollup/GroupByRollupPlanDesc.cs @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.agg.rollup +{ + public class GroupByRollupPlanDesc + { + public GroupByRollupPlanDesc(ExprNode[] expressions, AggregationGroupByRollupDesc rollupDesc) + { + Expressions = expressions; + RollupDesc = rollupDesc; + } + + public ExprNode[] Expressions { get; private set; } + + public AggregationGroupByRollupDesc RollupDesc { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/agg/rollup/GroupByRollupPlanHook.cs b/NEsper.Core/NEsper.Core/epl/agg/rollup/GroupByRollupPlanHook.cs new file mode 100755 index 000000000..fa967827c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/rollup/GroupByRollupPlanHook.cs @@ -0,0 +1,15 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.agg.rollup +{ + public interface GroupByRollupPlanHook + { + void Query(GroupByRollupPlanDesc desc); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupAllAccessOnlyFactory.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupAllAccessOnlyFactory.cs new file mode 100755 index 000000000..b416616f8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupAllAccessOnlyFactory.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.core; + +namespace com.espertech.esper.epl.agg.service +{ + /// + /// Aggregation service for use when only first/last/window aggregation functions are used an none other. + /// + public class AggSvcGroupAllAccessOnlyFactory : AggregationServiceFactory + { + protected internal readonly AggregationAccessorSlotPair[] Accessors; + protected internal readonly AggregationStateFactory[] AccessAggSpecs; + protected internal readonly bool IsJoin; + + public AggSvcGroupAllAccessOnlyFactory(AggregationAccessorSlotPair[] accessors, AggregationStateFactory[] accessAggSpecs, bool join) + { + Accessors = accessors; + AccessAggSpecs = accessAggSpecs; + IsJoin = join; + } + + public AggregationService MakeService(AgentInstanceContext agentInstanceContext, EngineImportService engineImportService, bool isSubquery, int? subqueryNumber) + { + AggregationState[] states = AggSvcGroupByUtil.NewAccesses(agentInstanceContext.AgentInstanceId, IsJoin, AccessAggSpecs, null, null); + return new AggSvcGroupAllAccessOnlyImpl(Accessors, states, AccessAggSpecs); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupAllAccessOnlyImpl.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupAllAccessOnlyImpl.cs new file mode 100755 index 000000000..9a4b192d7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupAllAccessOnlyImpl.cs @@ -0,0 +1,165 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.agg.service +{ + /// + /// Aggregation service for use when only first/last/window aggregation functions are used an none other. + /// + public class AggSvcGroupAllAccessOnlyImpl + : AggregationService + , AggregationResultFuture + { + private readonly AggregationState[] _states; + private readonly AggregationAccessorSlotPair[] _accessors; + private readonly AggregationStateFactory[] _accessAggSpecs; + + public AggSvcGroupAllAccessOnlyImpl( + AggregationAccessorSlotPair[] accessors, + AggregationState[] states, + AggregationStateFactory[] accessAggSpecs) + { + _accessors = accessors; + _states = states; + _accessAggSpecs = accessAggSpecs; + } + + public void ApplyEnter(EventBean[] eventsPerStream, Object groupKey, ExprEvaluatorContext exprEvaluatorContext) + { + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().QAggregationUngroupedApplyEnterLeave(true, 0, _accessAggSpecs.Length); + } + for (int i = 0; i < _states.Length; i++) + { + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get() + .QAggAccessEnterLeave(true, i, _states[i], _accessAggSpecs[i].AggregationExpression); + } + _states[i].ApplyEnter(eventsPerStream, exprEvaluatorContext); + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().AAggAccessEnterLeave(true, i, _states[i]); + } + } + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().AAggregationUngroupedApplyEnterLeave(true); + } + } + + public void ApplyLeave(EventBean[] eventsPerStream, Object groupKey, ExprEvaluatorContext exprEvaluatorContext) + { + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().QAggregationUngroupedApplyEnterLeave(false, 0, _accessAggSpecs.Length); + } + for (int i = 0; i < _states.Length; i++) + { + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get() + .QAggAccessEnterLeave(false, i, _states[i], _accessAggSpecs[i].AggregationExpression); + } + _states[i].ApplyLeave(eventsPerStream, exprEvaluatorContext); + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().AAggAccessEnterLeave(false, i, _states[i]); + } + } + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().AAggregationUngroupedApplyEnterLeave(false); + } + } + + public void SetCurrentAccess(Object groupKey, int agentInstanceId, AggregationGroupByRollupLevel rollupLevel) + { + // no implementation required + } + + public object GetValue(int column, int agentInstanceId, EvaluateParams evaluateParams) + { + AggregationAccessorSlotPair pair = _accessors[column]; + return pair.Accessor.GetValue(_states[pair.Slot], evaluateParams); + } + + public EventBean GetEventBean(int column, EvaluateParams evaluateParams) + { + AggregationAccessorSlotPair pair = _accessors[column]; + return pair.Accessor.GetEnumerableEvent(_states[pair.Slot], evaluateParams); + } + + public ICollection GetCollectionOfEvents(int column, EvaluateParams evaluateParams) + { + AggregationAccessorSlotPair pair = _accessors[column]; + return pair.Accessor.GetEnumerableEvents(_states[pair.Slot], evaluateParams); + } + + public ICollection GetCollectionScalar(int column, EvaluateParams evaluateParams) + { + AggregationAccessorSlotPair pair = _accessors[column]; + return pair.Accessor.GetEnumerableScalar(_states[pair.Slot], evaluateParams); + } + + public void ClearResults(ExprEvaluatorContext exprEvaluatorContext) + { + foreach (AggregationState state in _states) + { + state.Clear(); + } + } + + public void SetRemovedCallback(AggregationRowRemovedCallback callback) + { + // not applicable + } + + public void Accept(AggregationServiceVisitor visitor) + { + visitor.VisitAggregations(1, _states); + } + + public void AcceptGroupDetail(AggregationServiceVisitorWGroupDetail visitor) + { + } + + public bool IsGrouped + { + get { return false; } + } + + public Object GetGroupKey(int agentInstanceId) + { + return null; + } + + public ICollection GetGroupKeys(ExprEvaluatorContext exprEvaluatorContext) + { + return null; + } + + public void Stop() + { + } + + public AggregationService GetContextPartitionAggregationService(int agentInstanceId) + { + return this; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupAllLocalGroupBy.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupAllLocalGroupBy.cs new file mode 100755 index 000000000..356d9dcb8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupAllLocalGroupBy.cs @@ -0,0 +1,55 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.agg.util; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.agg.service +{ + /// + /// Implementation for handling aggregation with grouping by group-keys. + /// + public class AggSvcGroupAllLocalGroupBy : AggSvcGroupLocalGroupByBase + { + public AggSvcGroupAllLocalGroupBy(bool isJoin, AggregationLocalGroupByPlan localGroupByPlan) + : base(isJoin, localGroupByPlan) + { + } + + protected override object ComputeGroupKey(AggregationLocalGroupByLevel level, object groupKey, ExprEvaluator[] partitionEval, EventBean[] eventsPerStream, bool newData, ExprEvaluatorContext exprEvaluatorContext) + { + return AggSvcGroupLocalGroupByBase.ComputeGroupKey(partitionEval, eventsPerStream, newData, exprEvaluatorContext); + } + + public override void SetCurrentAccess(object groupByKey, int agentInstanceId, AggregationGroupByRollupLevel rollupLevel) + { + } + + public override object GetValue(int column, int agentInstanceId, EvaluateParams evaluateParams) + { + AggregationLocalGroupByColumn col = LocalGroupByPlan.Columns[column]; + + if (col.PartitionEvaluators.Length == 0) { + if (col.IsMethodAgg) { + return AggregatorsTopLevel[col.MethodOffset].Value; + } + return col.Pair.Accessor.GetValue(StatesTopLevel[col.Pair.Slot], evaluateParams); + } + + var groupByKey = ComputeGroupKey(col.PartitionEvaluators, evaluateParams.EventsPerStream, true, evaluateParams.ExprEvaluatorContext); + AggregationMethodPairRow row = AggregatorsPerLevelAndGroup[col.LevelNum].Get(groupByKey); + if (col.IsMethodAgg) { + return row.Methods[col.MethodOffset].Value; + } + return col.Pair.Accessor.GetValue(row.States[col.Pair.Slot], evaluateParams); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupAllLocalGroupByFactory.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupAllLocalGroupByFactory.cs new file mode 100755 index 000000000..844627730 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupAllLocalGroupByFactory.cs @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.agg.util; +using com.espertech.esper.epl.core; + +namespace com.espertech.esper.epl.agg.service +{ + /// + /// Implementation for handling aggregation with grouping by group-keys. + /// + public class AggSvcGroupAllLocalGroupByFactory : AggregationServiceFactory + { + protected internal readonly bool IsJoin; + private readonly AggregationLocalGroupByPlan _localGroupByPlan; + + public AggSvcGroupAllLocalGroupByFactory(bool @join, AggregationLocalGroupByPlan localGroupByPlan) + { + IsJoin = join; + _localGroupByPlan = localGroupByPlan; + } + + public AggregationService MakeService(AgentInstanceContext agentInstanceContext, EngineImportService engineImportService, bool isSubquery, int? subqueryNumber) + { + return new AggSvcGroupAllLocalGroupBy(IsJoin, _localGroupByPlan); + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupAllMixedAccessFactory.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupAllMixedAccessFactory.cs new file mode 100755 index 000000000..239fb0019 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupAllMixedAccessFactory.cs @@ -0,0 +1,46 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.aggregator; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.agg.service +{ + /// + /// Implementation for handling aggregation without any grouping (no group-by). + /// + public class AggSvcGroupAllMixedAccessFactory : AggregationServiceFactoryBase + { + protected readonly AggregationAccessorSlotPair[] Accessors; + protected readonly AggregationStateFactory[] AccessAggregations; + protected readonly bool IsJoin; + + public AggSvcGroupAllMixedAccessFactory(ExprEvaluator[] evaluators, AggregationMethodFactory[] aggregators, AggregationAccessorSlotPair[] accessors, AggregationStateFactory[] accessAggregations, bool @join) + : base(evaluators, aggregators) + { + Accessors = accessors; + AccessAggregations = accessAggregations; + IsJoin = join; + } + + public override AggregationService MakeService(AgentInstanceContext agentInstanceContext, EngineImportService engineImportService, bool isSubquery, int? subqueryNumber) + { + AggregationState[] states = AggSvcGroupByUtil.NewAccesses( + agentInstanceContext.AgentInstanceId, IsJoin, AccessAggregations, null, null); + AggregationMethod[] aggregatorsAgentInstance = AggSvcGroupByUtil.NewAggregators( + base.Aggregators); + return new AggSvcGroupAllMixedAccessImpl( + Evaluators, aggregatorsAgentInstance, Accessors, states, base.Aggregators, AccessAggregations); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupAllMixedAccessImpl.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupAllMixedAccessImpl.cs new file mode 100755 index 000000000..7757908b8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupAllMixedAccessImpl.cs @@ -0,0 +1,202 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.aggregator; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.agg.service +{ + /// + /// Implementation for handling aggregation without any grouping (no group-by). + /// + public class AggSvcGroupAllMixedAccessImpl : AggregationServiceBaseUngrouped + { + private readonly AggregationAccessorSlotPair[] _accessors; + protected AggregationState[] States; + + public AggSvcGroupAllMixedAccessImpl( + ExprEvaluator[] evaluators, + AggregationMethod[] aggregators, + AggregationAccessorSlotPair[] accessors, + AggregationState[] states, + AggregationMethodFactory[] aggregatorFactories, + AggregationStateFactory[] accessAggregations) + : base(evaluators, aggregators, aggregatorFactories, accessAggregations) + { + _accessors = accessors; + States = states; + } + + public override void ApplyEnter( + EventBean[] eventsPerStream, + Object optionalGroupKeyPerRow, + ExprEvaluatorContext exprEvaluatorContext) + { + var aggregators = base.Aggregators; + var accessAggregations = base.AccessAggregations; + var aggregatorFactories = base.AggregatorFactories; + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggregationUngroupedApplyEnterLeave(true, Evaluators.Length, accessAggregations.Length); } + + for (int i = 0; i < Evaluators.Length; i++) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggNoAccessEnterLeave(true, i, aggregators[i], aggregatorFactories[i].AggregationExpression); } + Object columnResult = + Evaluators[i].Evaluate(new EvaluateParams(eventsPerStream, true, exprEvaluatorContext)); + aggregators[i].Enter(columnResult); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggNoAccessEnterLeave(true, i, aggregators[i]); } + } + + for (int i = 0; i < States.Length; i++) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggAccessEnterLeave(true, i, States[i], accessAggregations[i].AggregationExpression); } + States[i].ApplyEnter(eventsPerStream, exprEvaluatorContext); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggAccessEnterLeave(true, i, States[i]); } + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggregationUngroupedApplyEnterLeave(true); } + } + + public override void ApplyLeave( + EventBean[] eventsPerStream, + Object optionalGroupKeyPerRow, + ExprEvaluatorContext exprEvaluatorContext) + { + var aggregators = base.Aggregators; + var accessAggregations = base.AccessAggregations; + var aggregatorFactories = base.AggregatorFactories; + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggregationUngroupedApplyEnterLeave(false, Evaluators.Length, accessAggregations.Length); } + + var evaluateParams = new EvaluateParams(eventsPerStream, false, exprEvaluatorContext); + for (int i = 0; i < Evaluators.Length; i++) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggNoAccessEnterLeave(false, i, aggregators[i], aggregatorFactories[i].AggregationExpression); } + var columnResult = Evaluators[i].Evaluate(evaluateParams); + aggregators[i].Leave(columnResult); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggNoAccessEnterLeave(false, i, aggregators[i]); } + } + + for (int i = 0; i < States.Length; i++) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggAccessEnterLeave(false, i, States[i], accessAggregations[i].AggregationExpression); } + States[i].ApplyLeave(eventsPerStream, exprEvaluatorContext); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggAccessEnterLeave(false, i, States[i]); } + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggregationUngroupedApplyEnterLeave(false); } + } + + public override void SetCurrentAccess(Object groupKey, int agentInstanceId, AggregationGroupByRollupLevel rollupLevel) + { + // no action needed - this implementation does not group and the current row is the single group + } + + public override object GetValue(int column, int agentInstanceId, EvaluateParams evaluateParams) + { + var aggregators = base.Aggregators; + if (column < aggregators.Length) + { + return aggregators[column].Value; + } + else + { + AggregationAccessorSlotPair pair = _accessors[column - aggregators.Length]; + return pair.Accessor.GetValue(States[pair.Slot], evaluateParams); + } + } + + public override ICollection GetCollectionOfEvents(int column, EvaluateParams evaluateParams) + { + var aggregators = base.Aggregators; + if (column < aggregators.Length) + { + return null; + } + else + { + AggregationAccessorSlotPair pair = _accessors[column - aggregators.Length]; + return pair.Accessor.GetEnumerableEvents(States[pair.Slot], evaluateParams); + } + } + + public override ICollection GetCollectionScalar(int column, EvaluateParams evaluateParams) + { + if (column < Aggregators.Length) + { + return null; + } + else + { + AggregationAccessorSlotPair pair = _accessors[column - Aggregators.Length]; + return pair.Accessor.GetEnumerableScalar(States[pair.Slot], evaluateParams); + } + } + + public override EventBean GetEventBean(int column, EvaluateParams evaluateParams) + { + if (column < Aggregators.Length) + { + return null; + } + else + { + AggregationAccessorSlotPair pair = _accessors[column - Aggregators.Length]; + return pair.Accessor.GetEnumerableEvent(States[pair.Slot], evaluateParams); + } + } + + public override void ClearResults(ExprEvaluatorContext exprEvaluatorContext) + { + foreach (AggregationState state in States) + { + state.Clear(); + } + foreach (AggregationMethod aggregator in Aggregators) + { + aggregator.Clear(); + } + } + + public override void SetRemovedCallback(AggregationRowRemovedCallback callback) + { + // not applicable + } + + public override void Accept(AggregationServiceVisitor visitor) + { + visitor.VisitAggregations(1, States, base.Aggregators); + } + + public override void AcceptGroupDetail(AggregationServiceVisitorWGroupDetail visitor) + { + } + + public override bool IsGrouped + { + get { return false; } + } + + public override Object GetGroupKey(int agentInstanceId) + { + return null; + } + + public override ICollection GetGroupKeys(ExprEvaluatorContext exprEvaluatorContext) + { + return null; + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupAllMixedAccessWTableFactory.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupAllMixedAccessWTableFactory.cs new file mode 100755 index 000000000..a9718e9bc --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupAllMixedAccessWTableFactory.cs @@ -0,0 +1,58 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.table.mgmt; + +namespace com.espertech.esper.epl.agg.service +{ + /// + /// Implementation for handling aggregation without any grouping (no group-by). + /// + public class AggSvcGroupAllMixedAccessWTableFactory : AggregationServiceFactory + { + protected internal readonly AggregationAccessorSlotPair[] Accessors; + protected internal readonly bool IsJoin; + + private readonly TableColumnMethodPair[] _methodPairs; + private readonly string _tableName; + private readonly int[] _targetStates; + private readonly ExprNode[] _accessStateExpr; + private readonly AggregationAgent[] _agents; + + public AggSvcGroupAllMixedAccessWTableFactory( + AggregationAccessorSlotPair[] accessors, + bool join, + TableColumnMethodPair[] methodPairs, + string tableName, + int[] targetStates, + ExprNode[] accessStateExpr, + AggregationAgent[] agents) + { + Accessors = accessors; + IsJoin = join; + _methodPairs = methodPairs; + _tableName = tableName; + _targetStates = targetStates; + _accessStateExpr = accessStateExpr; + _agents = agents; + } + + public AggregationService MakeService(AgentInstanceContext agentInstanceContext, EngineImportService engineImportService, bool isSubquery, int? subqueryNumber) + { + var tableState = (TableStateInstanceUngrouped) agentInstanceContext.StatementContext.TableService.GetState( + _tableName, agentInstanceContext.AgentInstanceId); + return new AggSvcGroupAllMixedAccessWTableImpl( + tableState, _methodPairs, + Accessors, _targetStates, _accessStateExpr, _agents); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupAllMixedAccessWTableImpl.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupAllMixedAccessWTableImpl.cs new file mode 100755 index 000000000..a87006a6e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupAllMixedAccessWTableImpl.cs @@ -0,0 +1,266 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.epl.table.strategy; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.agg.service +{ + /// + /// Implementation for handling aggregation without any grouping (no group-by). + /// + public class AggSvcGroupAllMixedAccessWTableImpl : AggregationService + { + private readonly TableStateInstanceUngrouped _tableStateInstance; + private readonly TableColumnMethodPair[] _methodPairs; + private readonly AggregationAccessorSlotPair[] _accessors; + private readonly int[] _targetStates; + private readonly ExprNode[] _accessStateExpr; + private readonly AggregationAgent[] _agents; + + public AggSvcGroupAllMixedAccessWTableImpl(TableStateInstanceUngrouped tableStateInstance, TableColumnMethodPair[] methodPairs, AggregationAccessorSlotPair[] accessors, int[] targetStates, ExprNode[] accessStateExpr, AggregationAgent[] agents) + { + _tableStateInstance = tableStateInstance; + _methodPairs = methodPairs; + _accessors = accessors; + _targetStates = targetStates; + _accessStateExpr = accessStateExpr; + _agents = agents; + } + + public void ApplyEnter(EventBean[] eventsPerStream, object optionalGroupKeyPerRow, ExprEvaluatorContext exprEvaluatorContext) + { + // acquire table-level write lock + ExprTableEvalLockUtil.ObtainLockUnless(_tableStateInstance.TableLevelRWLock.WriteLock, exprEvaluatorContext); + + var @event = _tableStateInstance.GetCreateRowIntoTable(null, exprEvaluatorContext); + var row = ExprTableEvalStrategyUtil.GetRow(@event); + var evaluateParams = new EvaluateParams(eventsPerStream, true, exprEvaluatorContext); + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggregationUngroupedApplyEnterLeave(true, row.Methods.Length, row.States.Length); } + for (var i = 0; i < _methodPairs.Length; i++) + { + var methodPair = _methodPairs[i]; + var method = row.Methods[methodPair.TargetIndex]; + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggNoAccessEnterLeave(true, i, method, methodPair.AggregationNode); } + var columnResult = methodPair.Evaluator.Evaluate(evaluateParams); + method.Enter(columnResult); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggNoAccessEnterLeave(true, i, method); } + } + + for (var i = 0; i < _targetStates.Length; i++) + { + var state = row.States[_targetStates[i]]; + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggAccessEnterLeave(true, i, state, _accessStateExpr[i]); } + _agents[i].ApplyEnter(eventsPerStream, exprEvaluatorContext, state); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggAccessEnterLeave(true, i, state); } + } + + _tableStateInstance.HandleRowUpdated(@event); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggregationUngroupedApplyEnterLeave(true); } + } + + public void ApplyLeave(EventBean[] eventsPerStream, object optionalGroupKeyPerRow, ExprEvaluatorContext exprEvaluatorContext) + { + // acquire table-level write lock + ExprTableEvalLockUtil.ObtainLockUnless(_tableStateInstance.TableLevelRWLock.WriteLock, exprEvaluatorContext); + + var @event = _tableStateInstance.GetCreateRowIntoTable(null, exprEvaluatorContext); + var row = ExprTableEvalStrategyUtil.GetRow(@event); + var evaluateParams = new EvaluateParams(eventsPerStream, false, exprEvaluatorContext); + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggregationUngroupedApplyEnterLeave(false, row.Methods.Length, row.States.Length); } + + for (var i = 0; i < _methodPairs.Length; i++) + { + var methodPair = _methodPairs[i]; + var method = row.Methods[methodPair.TargetIndex]; + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggNoAccessEnterLeave(false, i, method, methodPair.AggregationNode); } + var columnResult = methodPair.Evaluator.Evaluate(evaluateParams); + method.Leave(columnResult); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggNoAccessEnterLeave(false, i, method); } + } + + for (var i = 0; i < _targetStates.Length; i++) + { + var state = row.States[_targetStates[i]]; + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggAccessEnterLeave(false, i, state, _accessStateExpr[i]); } + _agents[i].ApplyLeave(eventsPerStream, exprEvaluatorContext, state); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggAccessEnterLeave(false, i, state); } + } + + _tableStateInstance.HandleRowUpdated(@event); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggregationUngroupedApplyEnterLeave(false); } + } + + public void SetCurrentAccess(object groupKey, int agentInstanceId, AggregationGroupByRollupLevel rollupLevel) + { + // no action needed - this implementation does not group and the current row is the single group + } + + public object GetValue(int column, int agentInstanceId, EvaluateParams evaluateParams) + { + // acquire table-level write lock + ExprTableEvalLockUtil.ObtainLockUnless(_tableStateInstance.TableLevelRWLock.WriteLock, evaluateParams.ExprEvaluatorContext); + + var @event = _tableStateInstance.EventUngrouped; + if (@event == null) + { + return null; + } + var row = ExprTableEvalStrategyUtil.GetRow(@event); + var aggregators = row.Methods; + if (column < aggregators.Length) + { + return aggregators[column].Value; + } + else + { + var pair = _accessors[column - aggregators.Length]; + return pair.Accessor.GetValue(row.States[pair.Slot], evaluateParams); + } + } + + public ICollection GetCollectionOfEvents(int column, EvaluateParams evaluateParams) + { + // acquire table-level write lock + ExprTableEvalLockUtil.ObtainLockUnless(_tableStateInstance.TableLevelRWLock.WriteLock, evaluateParams.ExprEvaluatorContext); + + var @event = _tableStateInstance.EventUngrouped; + if (@event == null) + { + return null; + } + var row = ExprTableEvalStrategyUtil.GetRow(@event); + var aggregators = row.Methods; + if (column < aggregators.Length) + { + return null; + } + else + { + var pair = _accessors[column - aggregators.Length]; + return pair.Accessor.GetEnumerableEvents(row.States[pair.Slot], evaluateParams); + } + } + + public ICollection GetCollectionScalar(int column, EvaluateParams evaluateParams) + { + // acquire table-level write lock + ExprTableEvalLockUtil.ObtainLockUnless(_tableStateInstance.TableLevelRWLock.WriteLock, evaluateParams.ExprEvaluatorContext); + + var @event = _tableStateInstance.EventUngrouped; + if (@event == null) + { + return null; + } + var row = ExprTableEvalStrategyUtil.GetRow(@event); + var aggregators = row.Methods; + if (column < aggregators.Length) + { + return null; + } + else + { + var pair = _accessors[column - aggregators.Length]; + return pair.Accessor.GetEnumerableScalar(row.States[pair.Slot], evaluateParams); + } + } + + public EventBean GetEventBean(int column, EvaluateParams evaluateParams) + { + // acquire table-level write lock + ExprTableEvalLockUtil.ObtainLockUnless(_tableStateInstance.TableLevelRWLock.WriteLock, evaluateParams.ExprEvaluatorContext); + + var @event = _tableStateInstance.EventUngrouped; + if (@event == null) + { + return null; + } + var row = ExprTableEvalStrategyUtil.GetRow(@event); + var aggregators = row.Methods; + + if (column < aggregators.Length) + { + return null; + } + else + { + var pair = _accessors[column - aggregators.Length]; + return pair.Accessor.GetEnumerableEvent(row.States[pair.Slot], evaluateParams); + } + } + + public void ClearResults(ExprEvaluatorContext exprEvaluatorContext) + { + // acquire table-level write lock + ExprTableEvalLockUtil.ObtainLockUnless(_tableStateInstance.TableLevelRWLock.WriteLock, exprEvaluatorContext); + + var @event = _tableStateInstance.EventUngrouped; + if (@event == null) + { + return; + } + var row = ExprTableEvalStrategyUtil.GetRow(@event); + var aggregators = row.Methods; + + foreach (var state in row.States) + { + state.Clear(); + } + foreach (var aggregator in aggregators) + { + aggregator.Clear(); + } + } + + public void SetRemovedCallback(AggregationRowRemovedCallback callback) + { + // not applicable + } + + public void Accept(AggregationServiceVisitor visitor) + { + // not applicable + } + + public void AcceptGroupDetail(AggregationServiceVisitorWGroupDetail visitor) + { + } + + public bool IsGrouped + { + get { return false; } + } + + public object GetGroupKey(int agentInstanceId) + { + return null; + } + + public ICollection GetGroupKeys(ExprEvaluatorContext exprEvaluatorContext) + { + return null; + } + + public void Stop() + { + } + + public AggregationService GetContextPartitionAggregationService(int agentInstanceId) + { + return this; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupAllNoAccessFactory.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupAllNoAccessFactory.cs new file mode 100755 index 000000000..99d01f4b7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupAllNoAccessFactory.cs @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.agg.aggregator; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.agg.service +{ + /// + /// Implementation for handling aggregation without any grouping (no group-by). + /// + public class AggSvcGroupAllNoAccessFactory : AggregationServiceFactoryBase + { + /// + /// Initializes a new instance of the class. + /// + /// are the child node of each aggregation function used for computing the value to be aggregated + /// aggregation states/factories + public AggSvcGroupAllNoAccessFactory(ExprEvaluator[] evaluators, AggregationMethodFactory[] aggregators) + : base(evaluators, aggregators) + { + } + + public override AggregationService MakeService(AgentInstanceContext agentInstanceContext, EngineImportService engineImportService, bool isSubquery, int? subqueryNumber) + { + AggregationMethod[] aggregatorsAgentInstance = AggSvcGroupByUtil.NewAggregators( + base.Aggregators); + return new AggSvcGroupAllNoAccessImpl(Evaluators, aggregatorsAgentInstance, Aggregators); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupAllNoAccessImpl.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupAllNoAccessImpl.cs new file mode 100755 index 000000000..ea47e617b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupAllNoAccessImpl.cs @@ -0,0 +1,142 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.agg.aggregator; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.agg.service +{ + /// + /// Implementation for handling aggregation without any grouping (no group-by). + /// + public class AggSvcGroupAllNoAccessImpl : AggregationServiceBaseUngrouped + { + /// + /// Ctor. + /// + /// evaluate the sub-expression within the aggregate function (ie. Sum(4*myNum)) + /// collect the aggregation state that evaluators evaluate to + /// The aggregator factories. + public AggSvcGroupAllNoAccessImpl( + ExprEvaluator[] evaluators, + AggregationMethod[] aggregators, + AggregationMethodFactory[] aggregatorFactories) + : base(evaluators, aggregators, aggregatorFactories, new AggregationStateFactory[0]) + { + } + + public override void ApplyEnter( + EventBean[] eventsPerStream, + Object optionalGroupKeyPerRow, + ExprEvaluatorContext exprEvaluatorContext) + { + var aggregators = base.Aggregators; + var aggregatorFactories = base.AggregatorFactories; + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggregationUngroupedApplyEnterLeave(true, Evaluators.Length, 0); } + var evaluateParams = new EvaluateParams(eventsPerStream, true, exprEvaluatorContext); + for (int j = 0; j < Evaluators.Length; j++) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggNoAccessEnterLeave(true, j, aggregators[j], aggregatorFactories[j].AggregationExpression); } + var columnResult = Evaluators[j].Evaluate(evaluateParams); + aggregators[j].Enter(columnResult); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggNoAccessEnterLeave(true, j, aggregators[j]); } + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggregationUngroupedApplyEnterLeave(true); } + } + + public override void ApplyLeave( + EventBean[] eventsPerStream, + Object optionalGroupKeyPerRow, + ExprEvaluatorContext exprEvaluatorContext) + { + var aggregators = base.Aggregators; + var aggregatorFactories = base.AggregatorFactories; + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggregationUngroupedApplyEnterLeave(false, Evaluators.Length, 0); } + + var evaluateParams = new EvaluateParams(eventsPerStream, false, exprEvaluatorContext); + for (int j = 0; j < Evaluators.Length; j++) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggNoAccessEnterLeave(false, j, aggregators[j], aggregatorFactories[j].AggregationExpression); } + var columnResult = Evaluators[j].Evaluate(evaluateParams); + aggregators[j].Leave(columnResult); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggNoAccessEnterLeave(false, j, aggregators[j]); } + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggregationUngroupedApplyEnterLeave(false); } + } + + public override void SetCurrentAccess(Object groupKey, int agentInstanceId, AggregationGroupByRollupLevel rollupLevel) + { + // no action needed - this implementation does not group and the current row is the single group + } + + public override object GetValue(int column, int agentInstanceId, EvaluateParams evaluateParams) + { + return base.Aggregators[column].Value; + } + + public override ICollection GetCollectionOfEvents(int column, EvaluateParams evaluateParams) + { + return null; + } + + public override ICollection GetCollectionScalar(int column, EvaluateParams evaluateParams) + { + return null; + } + + public override EventBean GetEventBean(int column, EvaluateParams evaluateParams) + { + return null; + } + + public override void ClearResults(ExprEvaluatorContext exprEvaluatorContext) + { + foreach (AggregationMethod aggregator in base.Aggregators) + { + aggregator.Clear(); + } + } + + public override void SetRemovedCallback(AggregationRowRemovedCallback callback) + { + // not applicable + } + + public override void Accept(AggregationServiceVisitor visitor) + { + visitor.VisitAggregations(1, base.Aggregators); + } + + public override void AcceptGroupDetail(AggregationServiceVisitorWGroupDetail visitor) + { + } + + public override bool IsGrouped + { + get { return false; } + } + + public override Object GetGroupKey(int agentInstanceId) + { + return null; + } + + public override ICollection GetGroupKeys(ExprEvaluatorContext exprEvaluatorContext) + { + return null; + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByAccessOnlyFactory.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByAccessOnlyFactory.cs new file mode 100755 index 000000000..c11455b79 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByAccessOnlyFactory.cs @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.core; + +namespace com.espertech.esper.epl.agg.service +{ + /// + /// Aggregation service for use when only first/last/window aggregation functions are used an none other. + /// + public class AggSvcGroupByAccessOnlyFactory : AggregationServiceFactory + { + private readonly AggregationAccessorSlotPair[] _accessors; + private readonly AggregationStateFactory[] _accessAggSpecs; + private readonly bool _isJoin; + + /// + /// Ctor. + /// + /// accessor definitions + /// access aggregations + /// true for join, false for single-stream + public AggSvcGroupByAccessOnlyFactory(AggregationAccessorSlotPair[] accessors, AggregationStateFactory[] accessAggSpecs, bool isJoin) + { + _accessors = accessors; + _accessAggSpecs = accessAggSpecs; + _isJoin = isJoin; + } + + public AggregationService MakeService(AgentInstanceContext agentInstanceContext, EngineImportService engineImportService, bool isSubquery, int? subqueryNumber) + { + return new AggSvcGroupByAccessOnlyImpl( + _accessors, _accessAggSpecs, _isJoin); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByAccessOnlyImpl.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByAccessOnlyImpl.cs new file mode 100755 index 000000000..8990b7ec7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByAccessOnlyImpl.cs @@ -0,0 +1,164 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.agg.service +{ + /// + /// Aggregation service for use when only first/last/window aggregation functions are used an none other. + /// + public class AggSvcGroupByAccessOnlyImpl : AggregationService, AggregationResultFuture + { + private readonly IDictionary _accessMap; + private readonly AggregationAccessorSlotPair[] _accessors; + private readonly AggregationStateFactory[] _accessAggSpecs; + private readonly bool _isJoin; + + private AggregationState[] _currentAccesses; + private Object _currentGroupKey; + + /// + /// Ctor. + /// + /// accessor definitions + /// access agg specs + /// true for join, false for single-stream + public AggSvcGroupByAccessOnlyImpl(AggregationAccessorSlotPair[] accessors, AggregationStateFactory[] accessAggSpecs, bool isJoin) + { + _accessMap = new Dictionary(); + _accessors = accessors; + _accessAggSpecs = accessAggSpecs; + _isJoin = isJoin; + } + + public void ApplyEnter(EventBean[] eventsPerStream, Object groupKey, ExprEvaluatorContext exprEvaluatorContext) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggregationGroupedApplyEnterLeave(true, 0, _accessAggSpecs.Length, groupKey); } + AggregationState[] row = GetAssertRow(exprEvaluatorContext.AgentInstanceId, groupKey); + for (int i = 0; i < row.Length; i++) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggAccessEnterLeave(true, i, row[i], _accessAggSpecs[i].AggregationExpression); } + row[i].ApplyEnter(eventsPerStream, exprEvaluatorContext); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggAccessEnterLeave(true, i, row[i]); } + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggregationGroupedApplyEnterLeave(true); } + } + + public void ApplyLeave(EventBean[] eventsPerStream, Object groupKey, ExprEvaluatorContext exprEvaluatorContext) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggregationGroupedApplyEnterLeave(false, 0, _accessAggSpecs.Length, groupKey); } + AggregationState[] row = GetAssertRow(exprEvaluatorContext.AgentInstanceId, groupKey); + for (int i = 0; i < row.Length; i++) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggAccessEnterLeave(false, i, row[i], _accessAggSpecs[i].AggregationExpression); } + row[i].ApplyLeave(eventsPerStream, exprEvaluatorContext); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggAccessEnterLeave(false, i, row[i]); } + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggregationGroupedApplyEnterLeave(false); } + } + + public void SetCurrentAccess(Object groupKey, int agentInstanceId, AggregationGroupByRollupLevel rollupLevel) + { + _currentAccesses = GetAssertRow(agentInstanceId, groupKey); + _currentGroupKey = groupKey; + } + + public object GetValue(int column, int agentInstanceId, EvaluateParams evaluateParams) + { + AggregationAccessorSlotPair pair = _accessors[column]; + return pair.Accessor.GetValue(_currentAccesses[pair.Slot], evaluateParams); + } + + public ICollection GetCollectionOfEvents(int column, EvaluateParams evaluateParams) + { + AggregationAccessorSlotPair pair = _accessors[column]; + return pair.Accessor.GetEnumerableEvents(_currentAccesses[pair.Slot], evaluateParams); + } + + public ICollection GetCollectionScalar(int column, EvaluateParams evaluateParams) + { + AggregationAccessorSlotPair pair = _accessors[column]; + return pair.Accessor.GetEnumerableScalar(_currentAccesses[pair.Slot], evaluateParams); + } + + public EventBean GetEventBean(int column, EvaluateParams evaluateParams) + { + AggregationAccessorSlotPair pair = _accessors[column]; + return pair.Accessor.GetEnumerableEvent(_currentAccesses[pair.Slot], evaluateParams); + } + + public void ClearResults(ExprEvaluatorContext exprEvaluatorContext) + { + _accessMap.Clear(); + } + + private AggregationState[] GetAssertRow(int agentInstanceId, Object groupKey) + { + AggregationState[] row = _accessMap.Get(groupKey); + if (row != null) + { + return row; + } + + row = AggSvcGroupByUtil.NewAccesses(agentInstanceId, _isJoin, _accessAggSpecs, groupKey, null); + _accessMap.Put(groupKey, row); + return row; + } + + public void SetRemovedCallback(AggregationRowRemovedCallback callback) + { + // not applicable + } + + public void Accept(AggregationServiceVisitor visitor) + { + visitor.VisitAggregations(_accessMap.Count, _accessMap); + } + + public void AcceptGroupDetail(AggregationServiceVisitorWGroupDetail visitor) + { + visitor.VisitGrouped(_accessMap.Count); + foreach (var entry in _accessMap) + { + visitor.VisitGroup(entry.Key, entry.Value); + } + } + + public bool IsGrouped + { + get { return true; } + } + + public Object GetGroupKey(int agentInstanceId) + { + return _currentGroupKey; + } + + public ICollection GetGroupKeys(ExprEvaluatorContext exprEvaluatorContext) + { + return _accessMap.Keys; + } + + public void Stop() + { + } + + public AggregationService GetContextPartitionAggregationService(int agentInstanceId) + { + return this; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByLocalGroupBy.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByLocalGroupBy.cs new file mode 100755 index 000000000..2fe26350f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByLocalGroupBy.cs @@ -0,0 +1,83 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.aggregator; +using com.espertech.esper.epl.agg.util; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.agg.service +{ + /// + /// Implementation for handling aggregation with grouping by group-keys. + /// + public class AggSvcGroupByLocalGroupBy : AggSvcGroupLocalGroupByBase + { + private AggregationMethod[] _currentAggregatorMethods; + private AggregationState[] _currentAggregatorStates; + + public AggSvcGroupByLocalGroupBy(bool isJoin, AggregationLocalGroupByPlan localGroupByPlan) + : base (isJoin, localGroupByPlan) + { + } + + protected override object ComputeGroupKey(AggregationLocalGroupByLevel level, object groupKey, ExprEvaluator[] partitionEval, EventBean[] eventsPerStream, bool newData, ExprEvaluatorContext exprEvaluatorContext) { + if (level.IsDefaultLevel) { + return groupKey; + } + return AggSvcGroupAllLocalGroupBy.ComputeGroupKey(partitionEval, eventsPerStream, true, exprEvaluatorContext); + } + + public override void SetCurrentAccess(object groupByKey, int agentInstanceId, AggregationGroupByRollupLevel rollupLevel) + { + if (!LocalGroupByPlan.AllLevels[0].IsDefaultLevel) { + return; + } + var row = AggregatorsPerLevelAndGroup[0].Get(groupByKey); + + if (row != null) { + _currentAggregatorMethods = row.Methods; + _currentAggregatorStates = row.States; + } + else { + _currentAggregatorMethods = null; + } + + if (_currentAggregatorMethods == null) { + _currentAggregatorMethods = AggSvcGroupByUtil.NewAggregators(LocalGroupByPlan.AllLevels[0].MethodFactories); + _currentAggregatorStates = AggSvcGroupByUtil.NewAccesses(agentInstanceId, IsJoin, LocalGroupByPlan.AllLevels[0].StateFactories, groupByKey, null); + } + } + + public override object GetValue(int column, int agentInstanceId, EvaluateParams evaluateParams) + { + var col = LocalGroupByPlan.Columns[column]; + if (col.IsDefaultGroupLevel) { + if (col.IsMethodAgg) { + return _currentAggregatorMethods[col.MethodOffset].Value; + } + return col.Pair.Accessor.GetValue(_currentAggregatorStates[col.Pair.Slot], evaluateParams); + } + if (col.PartitionEvaluators.Length == 0) { + if (col.IsMethodAgg) { + return AggregatorsTopLevel[col.MethodOffset].Value; + } + return col.Pair.Accessor.GetValue(StatesTopLevel[col.Pair.Slot], evaluateParams); + } + var groupByKey = AggSvcGroupAllLocalGroupBy.ComputeGroupKey(col.PartitionEvaluators, evaluateParams.EventsPerStream, true, evaluateParams.ExprEvaluatorContext); + var row = AggregatorsPerLevelAndGroup[col.LevelNum].Get(groupByKey); + if (col.IsMethodAgg) { + return row.Methods[col.MethodOffset].Value; + } + return col.Pair.Accessor.GetValue(row.States[col.Pair.Slot], evaluateParams); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByLocalGroupByFactory.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByLocalGroupByFactory.cs new file mode 100755 index 000000000..ca7739996 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByLocalGroupByFactory.cs @@ -0,0 +1,31 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.agg.util; +using com.espertech.esper.epl.core; + +namespace com.espertech.esper.epl.agg.service +{ + public class AggSvcGroupByLocalGroupByFactory : AggregationServiceFactory + { + protected internal readonly bool Join; + protected internal readonly AggregationLocalGroupByPlan LocalGroupByPlan; + + public AggSvcGroupByLocalGroupByFactory(bool @join, AggregationLocalGroupByPlan localGroupByPlan) + { + Join = join; + LocalGroupByPlan = localGroupByPlan; + } + + public AggregationService MakeService(AgentInstanceContext agentInstanceContext, EngineImportService engineImportService, bool isSubquery, int? subqueryNumber) + { + return new AggSvcGroupByLocalGroupBy(Join, LocalGroupByPlan); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByMixedAccessFactory.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByMixedAccessFactory.cs new file mode 100755 index 000000000..d2f61e8c5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByMixedAccessFactory.cs @@ -0,0 +1,49 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.agg.service +{ + /// + /// Implementation for handling aggregation with grouping by group-keys. + /// + public class AggSvcGroupByMixedAccessFactory : AggregationServiceFactoryBase + { + protected readonly AggregationAccessorSlotPair[] Accessors; + protected readonly AggregationStateFactory[] AccessAggregations; + protected readonly bool IsJoin; + + /// + /// Ctor. + /// + /// evaluate the sub-expression within the aggregate function (ie. Sum(4*myNum)) + /// collect the aggregation state that evaluators evaluate to, act as prototypes for new aggregationsaggregation states for each group + /// accessor definitions + /// access aggs + /// true for join, false for single-stream + public AggSvcGroupByMixedAccessFactory(ExprEvaluator[] evaluators, AggregationMethodFactory[] prototypes, AggregationAccessorSlotPair[] accessors, AggregationStateFactory[] accessAggregations, bool isJoin) + : base(evaluators, prototypes) + { + Accessors = accessors; + AccessAggregations = accessAggregations; + IsJoin = isJoin; + } + + public override AggregationService MakeService(AgentInstanceContext agentInstanceContext, EngineImportService engineImportService, bool isSubquery, int? subqueryNumber) + { + return new AggSvcGroupByMixedAccessImpl( + Evaluators, Aggregators, Accessors, AccessAggregations, IsJoin); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByMixedAccessImpl.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByMixedAccessImpl.cs new file mode 100755 index 000000000..33f0de3c5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByMixedAccessImpl.cs @@ -0,0 +1,246 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.aggregator; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.agg.service +{ + /// + /// Implementation for handling aggregation with grouping by group-keys. + /// + public class AggSvcGroupByMixedAccessImpl : AggregationServiceBaseGrouped + { + private readonly AggregationAccessorSlotPair[] _accessorsFactory; + protected readonly AggregationStateFactory[] AccessAggregations; + protected readonly bool IsJoin; + + // maintain for each group a row of aggregator states that the expression node canb pull the data from via index + protected IDictionary AggregatorsPerGroup; + + // maintain a current row for random access into the aggregator state table + // (row=groups, columns=expression nodes that have aggregation functions) + private AggregationRowPair _currentAggregatorRow; + private Object _currentGroupKey; + + /// + /// Ctor. + /// + /// evaluate the sub-expression within the aggregate function (ie. Sum(4*myNum)) + /// collect the aggregation state that evaluators evaluate to, act as prototypes for new aggregationsaggregation states for each group + /// accessor definitions + /// access aggs + /// true for join, false for single-stream + public AggSvcGroupByMixedAccessImpl(ExprEvaluator[] evaluators, AggregationMethodFactory[] prototypes, AggregationAccessorSlotPair[] accessorsFactory, AggregationStateFactory[] accessAggregations, bool isJoin) + : base(evaluators, prototypes) + { + _accessorsFactory = accessorsFactory; + AccessAggregations = accessAggregations; + IsJoin = isJoin; + AggregatorsPerGroup = new Dictionary(); + } + + public override void ClearResults(ExprEvaluatorContext exprEvaluatorContext) + { + AggregatorsPerGroup.Clear(); + } + + public override void ApplyEnter(EventBean[] eventsPerStream, Object groupByKey, ExprEvaluatorContext exprEvaluatorContext) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggregationGroupedApplyEnterLeave(true, Aggregators.Length, AccessAggregations.Length, groupByKey); } + AggregationRowPair groupAggregators = AggregatorsPerGroup.Get(groupByKey); + + // The aggregators for this group do not exist, need to create them from the prototypes + AggregationState[] states; + + if (groupAggregators == null) + { + AggregationMethod[] methods = AggSvcGroupByUtil.NewAggregators(Aggregators); + states = AggSvcGroupByUtil.NewAccesses(exprEvaluatorContext.AgentInstanceId, IsJoin, AccessAggregations, groupByKey, null); + groupAggregators = new AggregationRowPair(methods, states); + AggregatorsPerGroup.Put(groupByKey, groupAggregators); + } + + var evaluateParams = new EvaluateParams(eventsPerStream, true, exprEvaluatorContext); + // For this row, evaluate sub-expressions, enter result + _currentAggregatorRow = groupAggregators; + AggregationMethod[] groupAggMethods = groupAggregators.Methods; + for (int i = 0; i < Evaluators.Length; i++) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggNoAccessEnterLeave(true, i, groupAggMethods[i], Aggregators[i].AggregationExpression); } + Object columnResult = Evaluators[i].Evaluate(evaluateParams); + groupAggMethods[i].Enter(columnResult); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggNoAccessEnterLeave(true, i, groupAggMethods[i]); } + } + + states = _currentAggregatorRow.States; + for (int i = 0; i < states.Length; i++) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggAccessEnterLeave(true, i, states[i], AccessAggregations[i].AggregationExpression); } + states[i].ApplyEnter(eventsPerStream, exprEvaluatorContext); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggAccessEnterLeave(true, i, states[i]); } + } + + InternalHandleUpdated(groupByKey, groupAggregators); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggregationGroupedApplyEnterLeave(true); } + } + + public override void ApplyLeave(EventBean[] eventsPerStream, Object groupByKey, ExprEvaluatorContext exprEvaluatorContext) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggregationGroupedApplyEnterLeave(false, Aggregators.Length, AccessAggregations.Length, groupByKey); } + AggregationRowPair groupAggregators = AggregatorsPerGroup.Get(groupByKey); + + // The aggregators for this group do not exist, need to create them from the prototypes + AggregationState[] states; + + if (groupAggregators == null) + { + AggregationMethod[] methods = AggSvcGroupByUtil.NewAggregators(Aggregators); + states = AggSvcGroupByUtil.NewAccesses(exprEvaluatorContext.AgentInstanceId, IsJoin, AccessAggregations, groupByKey, null); + groupAggregators = new AggregationRowPair(methods, states); + AggregatorsPerGroup.Put(groupByKey, groupAggregators); + } + + var evaluateParams = new EvaluateParams(eventsPerStream, false, exprEvaluatorContext); + + // For this row, evaluate sub-expressions, enter result + _currentAggregatorRow = groupAggregators; + AggregationMethod[] groupAggMethods = groupAggregators.Methods; + for (int i = 0; i < Evaluators.Length; i++) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggNoAccessEnterLeave(false, i, groupAggMethods[i], Aggregators[i].AggregationExpression); } + Object columnResult = Evaluators[i].Evaluate(evaluateParams); + groupAggMethods[i].Leave(columnResult); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggNoAccessEnterLeave(false, i, groupAggMethods[i]); } + } + + states = _currentAggregatorRow.States; + for (int i = 0; i < states.Length; i++) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggAccessEnterLeave(false, i, states[i], AccessAggregations[i].AggregationExpression); } + states[i].ApplyLeave(eventsPerStream, exprEvaluatorContext); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggAccessEnterLeave(false, i, states[i]); } + } + + InternalHandleUpdated(groupByKey, groupAggregators); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggregationGroupedApplyEnterLeave(false); } + } + + public override void SetCurrentAccess(Object groupByKey, int agentInstanceId, AggregationGroupByRollupLevel rollupLevel) + { + _currentAggregatorRow = AggregatorsPerGroup.Get(groupByKey); + _currentGroupKey = groupByKey; + + if (_currentAggregatorRow == null) + { + AggregationMethod[] methods = AggSvcGroupByUtil.NewAggregators(Aggregators); + AggregationState[] states = AggSvcGroupByUtil.NewAccesses(agentInstanceId, IsJoin, AccessAggregations, groupByKey, null); + _currentAggregatorRow = new AggregationRowPair(methods, states); + AggregatorsPerGroup.Put(groupByKey, _currentAggregatorRow); + } + } + + public override object GetValue(int column, int agentInstanceId, EvaluateParams evaluateParams) + { + if (column < Aggregators.Length) + { + return _currentAggregatorRow.Methods[column].Value; + } + else + { + AggregationAccessorSlotPair pair = _accessorsFactory[column - Aggregators.Length]; + return pair.Accessor.GetValue(_currentAggregatorRow.States[pair.Slot], evaluateParams); + } + } + + public override ICollection GetCollectionOfEvents(int column, EvaluateParams evaluateParams) + { + if (column < Aggregators.Length) + { + return null; + } + else + { + AggregationAccessorSlotPair pair = _accessorsFactory[column - Aggregators.Length]; + return pair.Accessor.GetEnumerableEvents(_currentAggregatorRow.States[pair.Slot], evaluateParams); + } + } + + public override ICollection GetCollectionScalar(int column, EvaluateParams evaluateParams) + { + if (column < Aggregators.Length) + { + return null; + } + else + { + AggregationAccessorSlotPair pair = _accessorsFactory[column - Aggregators.Length]; + return pair.Accessor.GetEnumerableScalar(_currentAggregatorRow.States[pair.Slot], evaluateParams); + } + } + + public override EventBean GetEventBean(int column, EvaluateParams evaluateParams) + { + if (column < Aggregators.Length) + { + return null; + } + else + { + AggregationAccessorSlotPair pair = _accessorsFactory[column - Aggregators.Length]; + return pair.Accessor.GetEnumerableEvent(_currentAggregatorRow.States[pair.Slot], evaluateParams); + } + } + + public override void SetRemovedCallback(AggregationRowRemovedCallback callback) + { + // not applicable + } + + public void InternalHandleUpdated(Object groupByKey, AggregationRowPair groupAggregators) + { + // no action required + } + + public override void Accept(AggregationServiceVisitor visitor) + { + visitor.VisitAggregations(AggregatorsPerGroup.Count, AggregatorsPerGroup); + } + + public override void AcceptGroupDetail(AggregationServiceVisitorWGroupDetail visitor) + { + visitor.VisitGrouped(AggregatorsPerGroup.Count); + foreach (var entry in AggregatorsPerGroup) + { + visitor.VisitGroup(entry.Key, entry.Value); + } + } + + public override bool IsGrouped + { + get { return true; } + } + + public override Object GetGroupKey(int agentInstanceId) + { + return _currentGroupKey; + } + + public override ICollection GetGroupKeys(ExprEvaluatorContext exprEvaluatorContext) + { + return AggregatorsPerGroup.Keys; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByNoAccessFactory.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByNoAccessFactory.cs new file mode 100755 index 000000000..96ffc5164 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByNoAccessFactory.cs @@ -0,0 +1,39 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.agg.service +{ + /// + /// Implementation for handling aggregation with grouping by group-keys. + /// + public class AggSvcGroupByNoAccessFactory : AggregationServiceFactoryBase + { + /// + /// Ctor. + /// + /// - evaluate the sub-expression within the aggregate function (ie. Sum(4*myNum)) + /// - collect the aggregation state that evaluators evaluate to, act as prototypes for new aggregations + public AggSvcGroupByNoAccessFactory(ExprEvaluator[] evaluators, AggregationMethodFactory[] prototypes) + : base(evaluators, prototypes) + { + } + + public override AggregationService MakeService( + AgentInstanceContext agentInstanceContext, + EngineImportService engineImportService, + bool isSubquery, + int? subqueryNumber) + { + return new AggSvcGroupByNoAccessImpl(Evaluators, Aggregators); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByNoAccessImpl.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByNoAccessImpl.cs new file mode 100755 index 000000000..d122a0637 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByNoAccessImpl.cs @@ -0,0 +1,172 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.agg.aggregator; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.agg.service +{ + /// + /// Implementation for handling aggregation with grouping by group-keys. + /// + public class AggSvcGroupByNoAccessImpl : AggregationServiceBaseGrouped + { + // maintain for each group a row of aggregator states that the expression node canb pull the data from via index + private readonly IDictionary _aggregatorsPerGroup; + + // maintain a current row for random access into the aggregator state table + // (row=groups, columns=expression nodes that have aggregation functions) + private AggregationMethod[] _currentAggregatorRow; + private Object _currentGroupKey; + + /// + /// Ctor. + /// + /// evaluate the sub-expression within the aggregate function (ie. Sum(4*myNum)) + /// collect the aggregation state that evaluators evaluate to, act as prototypes for new aggregationsaggregation states for each group + public AggSvcGroupByNoAccessImpl(ExprEvaluator[] evaluators, AggregationMethodFactory[] prototypes) + : base(evaluators, prototypes) + { + _aggregatorsPerGroup = new Dictionary(); + } + + public override void ClearResults(ExprEvaluatorContext exprEvaluatorContext) + { + _aggregatorsPerGroup.Clear(); + } + + public override void ApplyEnter( + EventBean[] eventsPerStream, + Object groupByKey, + ExprEvaluatorContext exprEvaluatorContext) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggregationGroupedApplyEnterLeave(true, Aggregators.Length, 0, groupByKey); } + var groupAggregators = _aggregatorsPerGroup.Get(groupByKey); + + // The aggregators for this group do not exist, need to create them from the prototypes + if (groupAggregators == null) + { + groupAggregators = AggSvcGroupByUtil.NewAggregators(Aggregators); + _aggregatorsPerGroup.Put(groupByKey, groupAggregators); + } + + var evaluateParams = new EvaluateParams(eventsPerStream, true, exprEvaluatorContext); + // For this row, evaluate sub-expressions, enter result + _currentAggregatorRow = groupAggregators; + for (var i = 0; i < Evaluators.Length; i++) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggNoAccessEnterLeave(true, i, groupAggregators[i], Aggregators[i].AggregationExpression); } + var columnResult = Evaluators[i].Evaluate(evaluateParams); + groupAggregators[i].Enter(columnResult); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggNoAccessEnterLeave(true, i, groupAggregators[i]); } + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggregationGroupedApplyEnterLeave(true); } + } + + public override void ApplyLeave( + EventBean[] eventsPerStream, + Object groupByKey, + ExprEvaluatorContext exprEvaluatorContext) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggregationGroupedApplyEnterLeave(false, Aggregators.Length, 0, groupByKey); } + var groupAggregators = _aggregatorsPerGroup.Get(groupByKey); + + // The aggregators for this group do not exist, need to create them from the prototypes + if (groupAggregators == null) + { + groupAggregators = AggSvcGroupByUtil.NewAggregators(Aggregators); + _aggregatorsPerGroup.Put(groupByKey, groupAggregators); + } + + var evaluateParams = new EvaluateParams(eventsPerStream, false, exprEvaluatorContext); + + // For this row, evaluate sub-expressions, enter result + _currentAggregatorRow = groupAggregators; + for (var i = 0; i < Evaluators.Length; i++) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggNoAccessEnterLeave(false, i, groupAggregators[i], Aggregators[i].AggregationExpression); } + var columnResult = Evaluators[i].Evaluate(evaluateParams); + groupAggregators[i].Leave(columnResult); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggNoAccessEnterLeave(false, i, groupAggregators[i]); } + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggregationGroupedApplyEnterLeave(false); } + } + + public override void SetCurrentAccess(Object groupByKey, int agentInstanceId, AggregationGroupByRollupLevel rollupLevel) + { + _currentAggregatorRow = _aggregatorsPerGroup.Get(groupByKey); + _currentGroupKey = groupByKey; + + if (_currentAggregatorRow == null) + { + _currentAggregatorRow = AggSvcGroupByUtil.NewAggregators(Aggregators); + _aggregatorsPerGroup.Put(groupByKey, _currentAggregatorRow); + } + } + + public override object GetValue(int column, int agentInstanceId, EvaluateParams evaluateParams) + { + return _currentAggregatorRow[column].Value; + } + + public override ICollection GetCollectionOfEvents(int column, EvaluateParams evaluateParams) + { + return null; + } + + public override EventBean GetEventBean(int column, EvaluateParams evaluateParams) + { + return null; + } + + public override ICollection GetCollectionScalar(int column, EvaluateParams evaluateParams) + { + return null; + } + + public override void SetRemovedCallback(AggregationRowRemovedCallback callback) + { + // not applicable + } + + public override void Accept(AggregationServiceVisitor visitor) + { + visitor.VisitAggregations(_aggregatorsPerGroup.Count, _aggregatorsPerGroup); + } + + public override void AcceptGroupDetail(AggregationServiceVisitorWGroupDetail visitor) + { + visitor.VisitGrouped(_aggregatorsPerGroup.Count); + foreach (var entry in _aggregatorsPerGroup) + { + visitor.VisitGroup(entry.Key, entry.Value); + } + } + + public override bool IsGrouped + { + get { return true; } + } + + public override Object GetGroupKey(int agentInstanceId) + { + return _currentGroupKey; + } + + public override ICollection GetGroupKeys(ExprEvaluatorContext exprEvaluatorContext) + { + return _aggregatorsPerGroup.Keys; + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByReclaimAgedEvalFunc.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByReclaimAgedEvalFunc.cs new file mode 100755 index 000000000..d17dcdf81 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByReclaimAgedEvalFunc.cs @@ -0,0 +1,18 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.agg.service +{ + /// + /// Implementation for handling aggregation with grouping by group-keys. + /// + public interface AggSvcGroupByReclaimAgedEvalFunc + { + double? LongValue { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByReclaimAgedEvalFuncConstant.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByReclaimAgedEvalFuncConstant.cs new file mode 100755 index 000000000..643368548 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByReclaimAgedEvalFuncConstant.cs @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.agg.service +{ + /// + /// Implementation for handling aggregation with grouping by group-keys. + /// + public class AggSvcGroupByReclaimAgedEvalFuncConstant : AggSvcGroupByReclaimAgedEvalFunc + { + private readonly double _longValue; + + public AggSvcGroupByReclaimAgedEvalFuncConstant(double longValue) + { + _longValue = longValue; + } + + public double? LongValue + { + get { return _longValue; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByReclaimAgedEvalFuncFactory.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByReclaimAgedEvalFuncFactory.cs new file mode 100755 index 000000000..07a00418b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByReclaimAgedEvalFuncFactory.cs @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.core.context.util; + +namespace com.espertech.esper.epl.agg.service +{ + public interface AggSvcGroupByReclaimAgedEvalFuncFactory + { + AggSvcGroupByReclaimAgedEvalFunc Make(AgentInstanceContext agentInstanceContext); + } + + public class ProxyAggSvcGroupByReclaimAgedEvalFuncFactory : AggSvcGroupByReclaimAgedEvalFuncFactory + { + public Func ProcMake { get; set; } + + public AggSvcGroupByReclaimAgedEvalFunc Make(AgentInstanceContext agentInstanceContext) + { + return ProcMake(agentInstanceContext); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByReclaimAgedEvalFuncVariable.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByReclaimAgedEvalFuncVariable.cs new file mode 100755 index 000000000..1090cade6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByReclaimAgedEvalFuncVariable.cs @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.compat; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.variable; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.agg.service +{ + /// + /// Implementation for handling aggregation with grouping by group-keys. + /// + public class AggSvcGroupByReclaimAgedEvalFuncVariable : AggSvcGroupByReclaimAgedEvalFunc + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly VariableReader _variableReader; + + public AggSvcGroupByReclaimAgedEvalFuncVariable(VariableReader variableReader) + { + _variableReader = variableReader; + } + + public double? LongValue + { + get + { + var val = _variableReader.Value; + if (val.IsNumber()) + { + return val.AsDouble(); + } + + Log.Warn("Variable '{0} returned a null value, using last valid value", _variableReader.VariableMetaData.VariableName); + return null; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByReclaimAgedFactory.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByReclaimAgedFactory.cs new file mode 100755 index 000000000..f2bffd01d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByReclaimAgedFactory.cs @@ -0,0 +1,124 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client.annotation; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.variable; +using com.espertech.esper.type; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.agg.service +{ + /// + /// Implementation for handling aggregation with grouping by group-keys. + /// + public class AggSvcGroupByReclaimAgedFactory : AggregationServiceFactoryBase + { + protected readonly AggregationAccessorSlotPair[] Accessors; + protected readonly AggregationStateFactory[] AccessAggregations; + protected readonly bool IsJoin; + + protected readonly AggSvcGroupByReclaimAgedEvalFuncFactory EvaluationFunctionMaxAge; + protected readonly AggSvcGroupByReclaimAgedEvalFuncFactory EvaluationFunctionFrequency; + + /// + /// Ctor. + /// + /// evaluate the sub-expression within the aggregate function (ie. sum(4*myNum)) + /// collect the aggregation state that evaluators evaluate to, act as prototypes for new aggregationsaggregation states for each group + /// hint to reclaim + /// hint to reclaim + /// variables + /// accessor definitions + /// access aggs + /// true for join, false for single-stream + /// Name of the optional context. + /// Required hint value for hint ' + HintEnum.RECLAIM_GROUP_AGED + ' has not been provided + /// when validation fails + public AggSvcGroupByReclaimAgedFactory(ExprEvaluator[] evaluators, AggregationMethodFactory[] prototypes, HintAttribute reclaimGroupAged, HintAttribute reclaimGroupFrequency, VariableService variableService, AggregationAccessorSlotPair[] accessors, AggregationStateFactory[] accessAggregations, bool isJoin, string optionalContextName) + : base(evaluators, prototypes) + { + Accessors = accessors; + AccessAggregations = accessAggregations; + IsJoin = isJoin; + + String hintValueMaxAge = HintEnum.RECLAIM_GROUP_AGED.GetHintAssignedValue(reclaimGroupAged); + if (hintValueMaxAge == null) + { + throw new ExprValidationException("Required hint value for hint '" + HintEnum.RECLAIM_GROUP_AGED + "' has not been provided"); + } + EvaluationFunctionMaxAge = GetEvaluationFunction(variableService, hintValueMaxAge, optionalContextName); + + String hintValueFrequency = HintEnum.RECLAIM_GROUP_FREQ.GetHintAssignedValue(reclaimGroupAged); + if ((reclaimGroupFrequency == null) || (hintValueFrequency == null)) + { + EvaluationFunctionFrequency = EvaluationFunctionMaxAge; + } + else + { + EvaluationFunctionFrequency = GetEvaluationFunction(variableService, hintValueFrequency, optionalContextName); + } + } + + public override AggregationService MakeService(AgentInstanceContext agentInstanceContext, EngineImportService engineImportService, bool isSubquery, int? subqueryNumber) + { + AggSvcGroupByReclaimAgedEvalFunc max = EvaluationFunctionMaxAge.Make(agentInstanceContext); + AggSvcGroupByReclaimAgedEvalFunc freq = EvaluationFunctionFrequency.Make(agentInstanceContext); + return new AggSvcGroupByReclaimAgedImpl( + Evaluators, Aggregators, Accessors, AccessAggregations, IsJoin, max, freq, + agentInstanceContext.StatementContext.TimeAbacus); + } + + private AggSvcGroupByReclaimAgedEvalFuncFactory GetEvaluationFunction(VariableService variableService, String hintValue, String optionalContextName) + { + VariableMetaData variableMetaData = variableService.GetVariableMetaData(hintValue); + if (variableMetaData != null) + { + if (!variableMetaData.VariableType.IsNumeric()) + { + throw new ExprValidationException("Variable type of variable '" + variableMetaData.VariableName + "' is not numeric"); + } + String message = VariableServiceUtil.CheckVariableContextName(optionalContextName, variableMetaData); + if (message != null) { + throw new ExprValidationException(message); + } + return new ProxyAggSvcGroupByReclaimAgedEvalFuncFactory + { + ProcMake = agentInstanceContext => + { + VariableReader reader = variableService.GetReader( + variableMetaData.VariableName, agentInstanceContext.AgentInstanceId); + return new AggSvcGroupByReclaimAgedEvalFuncVariable(reader); + } + }; + } + else + { + double valueDouble; + try { + valueDouble = DoubleValue.ParseString(hintValue); + } + catch (Exception) { + throw new ExprValidationException("Failed to parse hint parameter value '" + hintValue + "' as a double-typed seconds value or variable name"); + } + if (valueDouble <= 0) { + throw new ExprValidationException("Hint parameter value '" + hintValue + "' is an invalid value, expecting a double-typed seconds value or variable name"); + } + return new ProxyAggSvcGroupByReclaimAgedEvalFuncFactory + { + ProcMake = agentInstanceContext => new AggSvcGroupByReclaimAgedEvalFuncConstant(valueDouble) + }; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByReclaimAgedImpl.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByReclaimAgedImpl.cs new file mode 100755 index 000000000..49fe9e522 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByReclaimAgedImpl.cs @@ -0,0 +1,420 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.aggregator; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.time; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.agg.service +{ + /// + /// Implementation for handling aggregation with grouping by group-keys. + /// + public class AggSvcGroupByReclaimAgedImpl : AggregationServiceBaseGrouped + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public static readonly long DEFAULT_MAX_AGE_MSEC = 60000L; + + private readonly AggregationStateFactory[] _accessAggregations; + private readonly bool _isJoin; + private readonly AggregationAccessorSlotPair[] _accessors; + private readonly TimeAbacus _timeAbacus; + + private readonly AggSvcGroupByReclaimAgedEvalFunc _evaluationFunctionMaxAge; + private readonly AggSvcGroupByReclaimAgedEvalFunc _evaluationFunctionFrequency; + + // maintain for each group a row of aggregator states that the expression node canb pull the data from via index + private readonly IDictionary _aggregatorsPerGroup; + + // maintain a current row for random access into the aggregator state table + // (row=groups, columns=expression nodes that have aggregation functions) + private AggregationMethod[] _currentAggregatorMethods; + private AggregationState[] _currentAggregatorStates; + private Object _currentGroupKey; + + private readonly IList _removedKeys; + private long? _nextSweepTime = null; + private AggregationRowRemovedCallback _removedCallback; + private long _currentMaxAge = DEFAULT_MAX_AGE_MSEC; + private long _currentReclaimFrequency = DEFAULT_MAX_AGE_MSEC; + + public AggSvcGroupByReclaimAgedImpl( + ExprEvaluator[] evaluators, + AggregationMethodFactory[] aggregators, + AggregationAccessorSlotPair[] accessors, + AggregationStateFactory[] accessAggregations, + bool join, + AggSvcGroupByReclaimAgedEvalFunc evaluationFunctionMaxAge, + AggSvcGroupByReclaimAgedEvalFunc evaluationFunctionFrequency, + TimeAbacus timeAbacus) + : base(evaluators, aggregators) + { + _accessors = accessors; + _accessAggregations = accessAggregations; + _isJoin = join; + _evaluationFunctionMaxAge = evaluationFunctionMaxAge; + _evaluationFunctionFrequency = evaluationFunctionFrequency; + _aggregatorsPerGroup = new Dictionary(); + _timeAbacus = timeAbacus; + _removedKeys = new List(); + } + + public override void ClearResults(ExprEvaluatorContext exprEvaluatorContext) + { + _aggregatorsPerGroup.Clear(); + } + + public override void ApplyEnter( + EventBean[] eventsPerStream, + Object groupByKey, + ExprEvaluatorContext exprEvaluatorContext) + { + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().QAggregationGroupedApplyEnterLeave(true, base.Aggregators.Length, _accessAggregations.Length, groupByKey); + } + var currentTime = exprEvaluatorContext.TimeProvider.Time; + if ((_nextSweepTime == null) || (_nextSweepTime <= currentTime)) + { + _currentMaxAge = GetMaxAge(_currentMaxAge); + _currentReclaimFrequency = GetReclaimFrequency(_currentReclaimFrequency); + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled)) + { + Log.Debug( + "Reclaiming groups older then " + _currentMaxAge + " msec and every " + _currentReclaimFrequency + + "msec in frequency"); + } + _nextSweepTime = currentTime + _currentReclaimFrequency; + Sweep(currentTime, _currentMaxAge); + } + + HandleRemovedKeys(); + // we collect removed keys lazily on the next enter to reduce the chance of empty-group queries creating empty aggregators temporarily + + var row = _aggregatorsPerGroup.Get(groupByKey); + + // The aggregators for this group do not exist, need to create them from the prototypes + AggregationMethod[] groupAggregators; + AggregationState[] groupStates; + if (row == null) + { + groupAggregators = AggSvcGroupByUtil.NewAggregators(base.Aggregators); + groupStates = AggSvcGroupByUtil.NewAccesses( + exprEvaluatorContext.AgentInstanceId, _isJoin, _accessAggregations, groupByKey, null); + row = new AggregationMethodRowAged(1, currentTime, groupAggregators, groupStates); + _aggregatorsPerGroup.Put(groupByKey, row); + } + else + { + groupAggregators = row.Methods; + groupStates = row.States; + row.IncreaseRefcount(); + row.LastUpdateTime = currentTime; + } + + // For this row, evaluate sub-expressions, enter result + _currentAggregatorMethods = groupAggregators; + _currentAggregatorStates = groupStates; + var evaluateParams = new EvaluateParams(eventsPerStream, true, exprEvaluatorContext); + for (var i = 0; i < base.Evaluators.Length; i++) + { + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().QAggNoAccessEnterLeave(true, i, _currentAggregatorMethods[i], base.Aggregators[i].AggregationExpression); + } + var columnResult = base.Evaluators[i].Evaluate(evaluateParams); + groupAggregators[i].Enter(columnResult); + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().AAggNoAccessEnterLeave(true, i, _currentAggregatorMethods[i]); + } + } + + for (var i = 0; i < _currentAggregatorStates.Length; i++) + { + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().QAggAccessEnterLeave(true, i, _currentAggregatorStates[i], _accessAggregations[i].AggregationExpression); + } + _currentAggregatorStates[i].ApplyEnter(eventsPerStream, exprEvaluatorContext); + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().AAggAccessEnterLeave(true, i, _currentAggregatorStates[i]); + } + } + + InternalHandleUpdated(groupByKey, row); + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().AAggregationGroupedApplyEnterLeave(true); + } + } + + private void Sweep(long currentTime, long currentMaxAge) + { + var removed = new ArrayDeque(); + foreach (var entry in _aggregatorsPerGroup) + { + var age = currentTime - entry.Value.LastUpdateTime; + if (age > currentMaxAge) + { + removed.Add(entry.Key); + } + } + + foreach (var key in removed) + { + _aggregatorsPerGroup.Remove(key); + InternalHandleRemoved(key); + _removedCallback.Removed(key); + } + } + + private long GetMaxAge(long currentMaxAge) + { + var maxAge = _evaluationFunctionMaxAge.LongValue; + if ((maxAge == null) || (maxAge <= 0)) + { + return currentMaxAge; + } + return _timeAbacus.DeltaForSecondsDouble(maxAge.Value); + } + + private long GetReclaimFrequency(long currentReclaimFrequency) + { + var frequency = _evaluationFunctionFrequency.LongValue; + if ((frequency == null) || (frequency <= 0)) + { + return currentReclaimFrequency; + } + return _timeAbacus.DeltaForSecondsDouble(frequency.Value); + } + + public override void ApplyLeave( + EventBean[] eventsPerStream, + Object groupByKey, + ExprEvaluatorContext exprEvaluatorContext) + { + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().QAggregationGroupedApplyEnterLeave(false, base.Aggregators.Length, _accessAggregations.Length, groupByKey); + } + var row = _aggregatorsPerGroup.Get(groupByKey); + var currentTime = exprEvaluatorContext.TimeProvider.Time; + + // The aggregators for this group do not exist, need to create them from the prototypes + AggregationMethod[] groupAggregators; + AggregationState[] groupStates; + if (row != null) + { + groupAggregators = row.Methods; + groupStates = row.States; + } + else + { + groupAggregators = AggSvcGroupByUtil.NewAggregators(base.Aggregators); + groupStates = AggSvcGroupByUtil.NewAccesses( + exprEvaluatorContext.AgentInstanceId, _isJoin, _accessAggregations, groupByKey, null); + row = new AggregationMethodRowAged(1, currentTime, groupAggregators, groupStates); + _aggregatorsPerGroup.Put(groupByKey, row); + } + + // For this row, evaluate sub-expressions, enter result + _currentAggregatorMethods = groupAggregators; + _currentAggregatorStates = groupStates; + var evaluateParams = new EvaluateParams(eventsPerStream, false, exprEvaluatorContext); + for (var i = 0; i < base.Evaluators.Length; i++) + { + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().QAggNoAccessEnterLeave(false, i, _currentAggregatorMethods[i], base.Aggregators[i].AggregationExpression); + } + var columnResult = base.Evaluators[i].Evaluate(evaluateParams); + groupAggregators[i].Leave(columnResult); + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().AAggNoAccessEnterLeave(false, i, _currentAggregatorMethods[i]); + } + } + + for (var i = 0; i < _currentAggregatorStates.Length; i++) + { + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().QAggAccessEnterLeave(false, i, _currentAggregatorStates[i], _accessAggregations[i].AggregationExpression); + } + _currentAggregatorStates[i].ApplyLeave(eventsPerStream, exprEvaluatorContext); + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().AAggAccessEnterLeave(false, i, _currentAggregatorStates[i]); + } + } + + row.DecreaseRefcount(); + row.LastUpdateTime = currentTime; + if (row.Refcount <= 0) + { + _removedKeys.Add(groupByKey); + } + InternalHandleUpdated(groupByKey, row); + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().AAggregationGroupedApplyEnterLeave(false); + } + } + + public override void SetCurrentAccess(Object groupByKey, int agentInstanceId, AggregationGroupByRollupLevel rollupLevel) + { + var row = _aggregatorsPerGroup.Get(groupByKey); + + if (row != null) + { + _currentAggregatorMethods = row.Methods; + _currentAggregatorStates = row.States; + } + else + { + _currentAggregatorMethods = null; + } + + if (_currentAggregatorMethods == null) + { + _currentAggregatorMethods = AggSvcGroupByUtil.NewAggregators(base.Aggregators); + _currentAggregatorStates = AggSvcGroupByUtil.NewAccesses( + agentInstanceId, _isJoin, _accessAggregations, groupByKey, null); + } + + _currentGroupKey = groupByKey; + } + + public override object GetValue(int column, int agentInstanceId, EvaluateParams evaluateParams) + { + if (column < base.Aggregators.Length) + { + return _currentAggregatorMethods[column].Value; + } + else + { + var pair = _accessors[column - base.Aggregators.Length]; + return pair.Accessor.GetValue( + _currentAggregatorStates[pair.Slot], evaluateParams); + } + } + + public override ICollection GetCollectionOfEvents(int column, EvaluateParams evaluateParams) + { + if (column < base.Aggregators.Length) + { + return null; + } + else + { + var pair = _accessors[column - base.Aggregators.Length]; + return pair.Accessor.GetEnumerableEvents( + _currentAggregatorStates[pair.Slot], evaluateParams); + } + } + + public override ICollection GetCollectionScalar(int column, EvaluateParams evaluateParams) + { + if (column < base.Aggregators.Length) + { + return null; + } + else + { + var pair = _accessors[column - base.Aggregators.Length]; + return pair.Accessor.GetEnumerableScalar( + _currentAggregatorStates[pair.Slot], evaluateParams); + } + } + + public override EventBean GetEventBean(int column, EvaluateParams evaluateParams) + { + if (column < Aggregators.Length) + { + return null; + } + else + { + var pair = _accessors[column - Aggregators.Length]; + return pair.Accessor.GetEnumerableEvent( + _currentAggregatorStates[pair.Slot], evaluateParams); + } + } + + public override void SetRemovedCallback(AggregationRowRemovedCallback value) + { + _removedCallback = value; + } + + public void InternalHandleUpdated(Object groupByKey, AggregationMethodRowAged row) + { + // no action required + } + + public void InternalHandleRemoved(Object key) + { + // no action required + } + + public override void Accept(AggregationServiceVisitor visitor) + { + visitor.VisitAggregations(_aggregatorsPerGroup.Count, _aggregatorsPerGroup); + } + + public override void AcceptGroupDetail(AggregationServiceVisitorWGroupDetail visitor) + { + visitor.VisitGrouped(_aggregatorsPerGroup.Count); + foreach (var entry in _aggregatorsPerGroup) + { + visitor.VisitGroup(entry.Key, entry.Value); + } + } + + public override bool IsGrouped + { + get { return true; } + } + + protected void HandleRemovedKeys() + { + if (!_removedKeys.IsEmpty()) + { + // we collect removed keys lazily on the next enter to reduce the chance of empty-group queries creating empty aggregators temporarily + foreach (var removedKey in _removedKeys) + { + _aggregatorsPerGroup.Remove(removedKey); + InternalHandleRemoved(removedKey); + } + _removedKeys.Clear(); + } + } + + public override Object GetGroupKey(int agentInstanceId) + { + return _currentGroupKey; + } + + public override ICollection GetGroupKeys(ExprEvaluatorContext exprEvaluatorContext) + { + return _aggregatorsPerGroup.Keys; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByRefcountedNoAccessFactory.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByRefcountedNoAccessFactory.cs new file mode 100755 index 000000000..2de00ec59 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByRefcountedNoAccessFactory.cs @@ -0,0 +1,42 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.agg.service +{ + /// + /// Implementation for handling aggregation with grouping by group-keys. + /// + public class AggSvcGroupByRefcountedNoAccessFactory : AggregationServiceFactoryBase + { + /// + /// Ctor. + /// + /// - evaluate the sub-expression within the aggregate function (ie. Sum(4*myNum)) + /// + /// - collect the aggregation state that evaluators evaluate to, act as prototypes for new aggregations + /// aggregation states for each group + /// + public AggSvcGroupByRefcountedNoAccessFactory(ExprEvaluator[] evaluators, AggregationMethodFactory[] prototypes) + : base(evaluators, prototypes) + { + } + + public override AggregationService MakeService( + AgentInstanceContext agentInstanceContext, + EngineImportService engineImportService, + bool isSubquery, + int? subqueryNumber) + { + return new AggSvcGroupByRefcountedNoAccessImpl(Evaluators, Aggregators); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByRefcountedNoAccessImpl.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByRefcountedNoAccessImpl.cs new file mode 100755 index 000000000..83b2ee113 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByRefcountedNoAccessImpl.cs @@ -0,0 +1,220 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.agg.aggregator; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.agg.service +{ + /// + /// Implementation for handling aggregation with grouping by group-keys. + /// + public class AggSvcGroupByRefcountedNoAccessImpl : AggregationServiceBaseGrouped + { + // maintain for each group a row of aggregator states that the expression node canb pull the data from via index + private readonly IDictionary _aggregatorsPerGroup; + + // maintain a current row for random access into the aggregator state table + // (row=groups, columns=expression nodes that have aggregation functions) + private AggregationMethod[] _currentAggregatorRow; + private Object _currentGroupKey; + + private readonly IList _removedKeys; + + /// + /// Ctor. + /// + /// evaluate the sub-expression within the aggregate function (ie. Sum(4*myNum)) + /// collect the aggregation state that evaluators evaluate to, act as prototypes for new aggregationsaggregation states for each group + public AggSvcGroupByRefcountedNoAccessImpl(ExprEvaluator[] evaluators, AggregationMethodFactory[] prototypes) + : base(evaluators, prototypes) + { + _aggregatorsPerGroup = new NullableDictionary(); + _removedKeys = new List(); + } + + public override void ClearResults(ExprEvaluatorContext exprEvaluatorContext) + { + _aggregatorsPerGroup.Clear(); + } + + public override void ApplyEnter(EventBean[] eventsPerStream, Object groupByKey, ExprEvaluatorContext exprEvaluatorContext) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggregationGroupedApplyEnterLeave(true, Aggregators.Length, 0, groupByKey); } + HandleRemovedKeys(); + + // The aggregators for this group do not exist, need to create them from the prototypes + AggregationMethodRow row; + AggregationMethod[] groupAggregators; + if (!_aggregatorsPerGroup.TryGetValue(groupByKey, out row)) + { + groupAggregators = AggSvcGroupByUtil.NewAggregators(Aggregators); + row = new AggregationMethodRow(1, groupAggregators); + _aggregatorsPerGroup[groupByKey] = row; + } + else + { + groupAggregators = row.Methods; + row.IncreaseRefcount(); + } + + var evaluateParams = new EvaluateParams(eventsPerStream, true, exprEvaluatorContext); + + // For this row, evaluate sub-expressions, enter result + _currentAggregatorRow = groupAggregators; + + var evaluators = Evaluators; + var evaluatorsLength = evaluators.Length; + + for (var ii = 0; ii < evaluatorsLength; ii++) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggNoAccessEnterLeave(true, ii, groupAggregators[ii], Aggregators[ii].AggregationExpression); } + groupAggregators[ii].Enter(evaluators[ii].Evaluate(evaluateParams)); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggNoAccessEnterLeave(true, ii, groupAggregators[ii]); } + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggregationGroupedApplyEnterLeave(true); } + } + + public override void ApplyLeave(EventBean[] eventsPerStream, Object groupByKey, ExprEvaluatorContext exprEvaluatorContext) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggregationGroupedApplyEnterLeave(false, Aggregators.Length, 0, groupByKey); } + var row = _aggregatorsPerGroup.Get(groupByKey); + + // The aggregators for this group do not exist, need to create them from the prototypes + AggregationMethod[] groupAggregators; + if (row != null) + { + groupAggregators = row.Methods; + } + else + { + groupAggregators = AggSvcGroupByUtil.NewAggregators(Aggregators); + row = new AggregationMethodRow(1, groupAggregators); + _aggregatorsPerGroup[groupByKey] = row; + } + + var evaluateParams = new EvaluateParams(eventsPerStream, false, exprEvaluatorContext); + + // For this row, evaluate sub-expressions, enter result + _currentAggregatorRow = groupAggregators; + + var evaluators = Evaluators; + var evaluatorsLength = evaluators.Length; + + for (var ii = 0; ii < evaluatorsLength; ii++) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggNoAccessEnterLeave(false, ii, groupAggregators[ii], Aggregators[ii].AggregationExpression); } + groupAggregators[ii].Leave(evaluators[ii].Evaluate(evaluateParams)); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggNoAccessEnterLeave(false, ii, groupAggregators[ii]); } + } + + row.DecreaseRefcount(); + if (row.Refcount <= 0) + { + _removedKeys.Add(groupByKey); + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggregationGroupedApplyEnterLeave(false); } + } + + public override void SetCurrentAccess(Object groupByKey, int agentInstanceId, AggregationGroupByRollupLevel rollupLevel) + { + var row = _aggregatorsPerGroup.Get(groupByKey); + + if (row != null) + { + _currentAggregatorRow = row.Methods; + } + else + { + _currentAggregatorRow = null; + } + + if (_currentAggregatorRow == null) + { + _currentAggregatorRow = AggSvcGroupByUtil.NewAggregators(Aggregators); + } + _currentGroupKey = groupByKey; + } + + public override object GetValue(int column, int agentInstanceId, EvaluateParams evaluateParams) + { + return _currentAggregatorRow[column].Value; + } + + public override ICollection GetCollectionOfEvents(int column, EvaluateParams evaluateParams) + { + return null; + } + + public override ICollection GetCollectionScalar(int column, EvaluateParams evaluateParams) + { + return null; + } + + public override EventBean GetEventBean(int column, EvaluateParams evaluateParams) + { + return null; + } + + public override void SetRemovedCallback(AggregationRowRemovedCallback callback) + { + // not applicable + } + + public override void Accept(AggregationServiceVisitor visitor) + { + visitor.VisitAggregations(_aggregatorsPerGroup.Count, _aggregatorsPerGroup); + } + + public override void AcceptGroupDetail(AggregationServiceVisitorWGroupDetail visitor) + { + visitor.VisitGrouped(_aggregatorsPerGroup.Count); + foreach (var entry in _aggregatorsPerGroup) + { + visitor.VisitGroup(entry.Key, entry.Value); + } + } + + public override bool IsGrouped + { + get { return true; } + } + + public override Object GetGroupKey(int agentInstanceId) + { + return _currentGroupKey; + } + + protected void HandleRemovedKeys() + { + // we collect removed keys lazily on the next enter to reduce the chance of empty-group queries creating empty aggregators temporarily + if (_removedKeys.IsNotEmpty()) + { + foreach (var removedKey in _removedKeys) + { + _aggregatorsPerGroup.Remove(removedKey); + } + _removedKeys.Clear(); + } + } + + public override ICollection GetGroupKeys(ExprEvaluatorContext exprEvaluatorContext) + { + HandleRemovedKeys(); + return _aggregatorsPerGroup.Keys; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByRefcountedWAccessFactory.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByRefcountedWAccessFactory.cs new file mode 100755 index 000000000..3f1c82994 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByRefcountedWAccessFactory.cs @@ -0,0 +1,58 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.agg.service +{ + /// + /// Implementation for handling aggregation with grouping by group-keys. + /// + public class AggSvcGroupByRefcountedWAccessFactory : AggregationServiceFactoryBase + { + private readonly AggregationAccessorSlotPair[] _accessors; + private readonly AggregationStateFactory[] _accessAggregations; + private readonly bool _isJoin; + + /// + /// Ctor. + /// + /// - evaluate the sub-expression within the aggregate function (ie. Sum(4*myNum)) + /// + /// - collect the aggregation state that evaluators evaluate to, act as prototypes for new aggregations + /// aggregation states for each group + /// + /// accessor definitions + /// access aggs + /// true for join, false for single-stream + public AggSvcGroupByRefcountedWAccessFactory( + ExprEvaluator[] evaluators, + AggregationMethodFactory[] prototypes, + AggregationAccessorSlotPair[] accessors, + AggregationStateFactory[] accessAggregations, + bool isJoin) + : base(evaluators, prototypes) + { + _accessors = accessors; + _accessAggregations = accessAggregations; + _isJoin = isJoin; + } + + public override AggregationService MakeService( + AgentInstanceContext agentInstanceContext, + EngineImportService engineImportService, + bool isSubquery, + int? subqueryNumber) + { + return new AggSvcGroupByRefcountedWAccessImpl(Evaluators, Aggregators, _accessors, _accessAggregations, _isJoin); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByRefcountedWAccessImpl.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByRefcountedWAccessImpl.cs new file mode 100755 index 000000000..5c281bfc5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByRefcountedWAccessImpl.cs @@ -0,0 +1,298 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.aggregator; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.agg.service +{ + /// + /// Implementation for handling aggregation with grouping by group-keys. + /// + public class AggSvcGroupByRefcountedWAccessImpl : AggregationServiceBaseGrouped + { + protected readonly AggregationAccessorSlotPair[] Accessors; + protected readonly AggregationStateFactory[] AccessAggregations; + protected readonly bool IsJoin; + + // maintain for each group a row of aggregator states that the expression node canb pull the data from via index + protected IDictionary AggregatorsPerGroup; + + // maintain a current row for random access into the aggregator state table + // (row=groups, columns=expression nodes that have aggregation functions) + private AggregationMethod[] _currentAggregatorMethods; + private AggregationState[] _currentAggregatorStates; + private Object _currentGroupKey; + + protected IList RemovedKeys; + + /// + /// Ctor. + /// + /// evaluate the sub-expression within the aggregate function (ie. Sum(4*myNum)) + /// collect the aggregation state that evaluators evaluate to, act as prototypes for new aggregationsaggregation states for each group + /// accessor definitions + /// access aggs + /// true for join, false for single-stream + public AggSvcGroupByRefcountedWAccessImpl(ExprEvaluator[] evaluators, AggregationMethodFactory[] prototypes, AggregationAccessorSlotPair[] accessors, AggregationStateFactory[] accessAggregations, bool isJoin) + : base(evaluators, prototypes) + { + AggregatorsPerGroup = new Dictionary(); + Accessors = accessors; + AccessAggregations = accessAggregations; + IsJoin = isJoin; + RemovedKeys = new List(); + } + + public override void ClearResults(ExprEvaluatorContext exprEvaluatorContext) + { + AggregatorsPerGroup.Clear(); + } + + public override void ApplyEnter(EventBean[] eventsPerStream, Object groupByKey, ExprEvaluatorContext exprEvaluatorContext) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggregationGroupedApplyEnterLeave(true, Aggregators.Length, AccessAggregations.Length, groupByKey); } + HandleRemovedKeys(); + + var row = AggregatorsPerGroup.Get(groupByKey); + + // The aggregators for this group do not exist, need to create them from the prototypes + AggregationMethod[] groupAggregators; + AggregationState[] groupStates; + if (row == null) + { + groupAggregators = AggSvcGroupByUtil.NewAggregators(Aggregators); + groupStates = AggSvcGroupByUtil.NewAccesses(exprEvaluatorContext.AgentInstanceId, IsJoin, AccessAggregations, groupByKey, null); + row = new AggregationMethodPairRow(1, groupAggregators, groupStates); + AggregatorsPerGroup.Put(groupByKey, row); + } + else + { + groupAggregators = row.Methods; + groupStates = row.States; + row.IncreaseRefcount(); + } + + var evaluateParams = new EvaluateParams(eventsPerStream, true, exprEvaluatorContext); + + // For this row, evaluate sub-expressions, enter result + _currentAggregatorMethods = groupAggregators; + _currentAggregatorStates = groupStates; + for (var j = 0; j < Evaluators.Length; j++) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggNoAccessEnterLeave(true, j, groupAggregators[j], Aggregators[j].AggregationExpression); } + var columnResult = Evaluators[j].Evaluate(evaluateParams); + groupAggregators[j].Enter(columnResult); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggNoAccessEnterLeave(true, j, groupAggregators[j]); } + } + + for (var i = 0; i < _currentAggregatorStates.Length; i++) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggAccessEnterLeave(true, i, _currentAggregatorStates[i], AccessAggregations[i].AggregationExpression); } + _currentAggregatorStates[i].ApplyEnter(eventsPerStream, exprEvaluatorContext); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggAccessEnterLeave(true, i, _currentAggregatorStates[i]); } + } + + InternalHandleGroupUpdate(groupByKey, row); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggregationGroupedApplyEnterLeave(true); } + } + + public override void ApplyLeave(EventBean[] eventsPerStream, Object groupByKey, ExprEvaluatorContext exprEvaluatorContext) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggregationGroupedApplyEnterLeave(false, Aggregators.Length, AccessAggregations.Length, groupByKey); } + var row = AggregatorsPerGroup.Get(groupByKey); + + // The aggregators for this group do not exist, need to create them from the prototypes + AggregationMethod[] groupAggregators; + AggregationState[] groupStates; + if (row != null) + { + groupAggregators = row.Methods; + groupStates = row.States; + } + else + { + groupAggregators = AggSvcGroupByUtil.NewAggregators(Aggregators); + groupStates = AggSvcGroupByUtil.NewAccesses(exprEvaluatorContext.AgentInstanceId, IsJoin, AccessAggregations, groupByKey, null); + row = new AggregationMethodPairRow(1, groupAggregators, groupStates); + AggregatorsPerGroup.Put(groupByKey, row); + } + + var evaluateParams = new EvaluateParams(eventsPerStream, false, exprEvaluatorContext); + + // For this row, evaluate sub-expressions, enter result + _currentAggregatorMethods = groupAggregators; + _currentAggregatorStates = groupStates; + for (var j = 0; j < Evaluators.Length; j++) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggNoAccessEnterLeave(false, j, groupAggregators[j], Aggregators[j].AggregationExpression); } + var columnResult = Evaluators[j].Evaluate(evaluateParams); + groupAggregators[j].Leave(columnResult); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggNoAccessEnterLeave(false, j, groupAggregators[j]); } + } + + for (var i = 0; i < _currentAggregatorStates.Length; i++) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggAccessEnterLeave(false, i, _currentAggregatorStates[i], AccessAggregations[i].AggregationExpression); } + _currentAggregatorStates[i].ApplyLeave(eventsPerStream, exprEvaluatorContext); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggAccessEnterLeave(false, i, _currentAggregatorStates[i]); } + } + + row.DecreaseRefcount(); + if (row.Refcount <= 0) + { + RemovedKeys.Add(groupByKey); + } + + InternalHandleGroupUpdate(groupByKey, row); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggregationGroupedApplyEnterLeave(false); } + } + + public override void SetCurrentAccess(Object groupByKey, int agentInstanceId, AggregationGroupByRollupLevel rollupLevel) + { + var row = AggregatorsPerGroup.Get(groupByKey); + + if (row != null) + { + _currentAggregatorMethods = row.Methods; + _currentAggregatorStates = row.States; + } + else + { + _currentAggregatorMethods = null; + } + + if (_currentAggregatorMethods == null) + { + _currentAggregatorMethods = AggSvcGroupByUtil.NewAggregators(Aggregators); + _currentAggregatorStates = AggSvcGroupByUtil.NewAccesses(agentInstanceId, IsJoin, AccessAggregations, groupByKey, null); + } + + _currentGroupKey = groupByKey; + } + + public override object GetValue(int column, int agentInstanceId, EvaluateParams evaluateParams) + { + if (column < Aggregators.Length) + { + return _currentAggregatorMethods[column].Value; + } + else + { + var pair = Accessors[column - Aggregators.Length]; + return pair.Accessor.GetValue(_currentAggregatorStates[pair.Slot], evaluateParams); + } + } + + public override ICollection GetCollectionOfEvents(int column, EvaluateParams evaluateParams) + { + if (column < Aggregators.Length) + { + return null; + } + else + { + var pair = Accessors[column - Aggregators.Length]; + return pair.Accessor.GetEnumerableEvents(_currentAggregatorStates[pair.Slot], evaluateParams); + } + } + + public override ICollection GetCollectionScalar(int column, EvaluateParams evaluateParams) + { + if (column < Aggregators.Length) + { + return null; + } + else + { + AggregationAccessorSlotPair pair = Accessors[column - Aggregators.Length]; + return pair.Accessor.GetEnumerableScalar(_currentAggregatorStates[pair.Slot], evaluateParams); + } + } + + public override EventBean GetEventBean(int column, EvaluateParams evaluateParams) + { + if (column < Aggregators.Length) + { + return null; + } + else + { + var pair = Accessors[column - Aggregators.Length]; + return pair.Accessor.GetEnumerableEvent(_currentAggregatorStates[pair.Slot], evaluateParams); + } + } + + public override void SetRemovedCallback(AggregationRowRemovedCallback callback) + { + // not applicable + } + + public void InternalHandleGroupUpdate(Object groupByKey, AggregationMethodPairRow row) + { + // no action required + } + + public void InternalHandleGroupRemove(Object groupByKey) + { + // no action required + } + + public override void Accept(AggregationServiceVisitor visitor) + { + visitor.VisitAggregations(AggregatorsPerGroup.Count, AggregatorsPerGroup); + } + + public override void AcceptGroupDetail(AggregationServiceVisitorWGroupDetail visitor) + { + visitor.VisitGrouped(AggregatorsPerGroup.Count); + foreach (var entry in AggregatorsPerGroup) + { + visitor.VisitGroup(entry.Key, entry.Value); + } + } + + public override bool IsGrouped + { + get { return true; } + } + + protected void HandleRemovedKeys() + { + // we collect removed keys lazily on the next enter to reduce the chance of empty-group queries creating empty aggregators temporarily + if (RemovedKeys.IsNotEmpty()) + { + foreach (var removedKey in RemovedKeys) + { + AggregatorsPerGroup.Remove(removedKey); + InternalHandleGroupRemove(removedKey); + } + RemovedKeys.Clear(); + } + } + + public override Object GetGroupKey(int agentInstanceId) + { + return _currentGroupKey; + } + + public override ICollection GetGroupKeys(ExprEvaluatorContext exprEvaluatorContext) + { + return AggregatorsPerGroup.Keys; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByRefcountedWAccessRollupFactory.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByRefcountedWAccessRollupFactory.cs new file mode 100755 index 000000000..3130a1e4e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByRefcountedWAccessRollupFactory.cs @@ -0,0 +1,67 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.aggregator; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.agg.service +{ + /// + /// Implementation for handling aggregation with grouping by group-keys. + /// + public class AggSvcGroupByRefcountedWAccessRollupFactory : AggregationServiceFactoryBase + { + private readonly AggregationAccessorSlotPair[] _accessors; + private readonly AggregationStateFactory[] _accessAggregations; + private readonly bool _isJoin; + private readonly AggregationGroupByRollupDesc _groupByRollupDesc; + + /// + /// Ctor. + /// + /// - evaluate the sub-expression within the aggregate function (ie. Sum(4*myNum)) + /// + /// - collect the aggregation state that evaluators evaluate to, act as prototypes for new aggregations + /// aggregation states for each group + /// + /// accessor definitions + /// access aggs + /// true for join, false for single-stream + /// rollups if any + public AggSvcGroupByRefcountedWAccessRollupFactory( + ExprEvaluator[] evaluators, + AggregationMethodFactory[] prototypes, + AggregationAccessorSlotPair[] accessors, + AggregationStateFactory[] accessAggregations, + bool isJoin, + AggregationGroupByRollupDesc groupByRollupDesc) + : base(evaluators, prototypes) + { + _accessors = accessors; + _accessAggregations = accessAggregations; + _isJoin = isJoin; + _groupByRollupDesc = groupByRollupDesc; + } + + public override AggregationService MakeService( + AgentInstanceContext agentInstanceContext, + EngineImportService engineImportService, + bool isSubquery, + int? subqueryNumber) + { + AggregationState[] topStates = AggSvcGroupByUtil.NewAccesses( + agentInstanceContext.AgentInstanceId, _isJoin, _accessAggregations, null, null); + AggregationMethod[] topMethods = AggSvcGroupByUtil.NewAggregators(base.Aggregators); + return new AggSvcGroupByRefcountedWAccessRollupImpl( + Evaluators, Aggregators, _accessors, _accessAggregations, _isJoin, _groupByRollupDesc, topMethods, topStates); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByRefcountedWAccessRollupImpl.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByRefcountedWAccessRollupImpl.cs new file mode 100755 index 000000000..5ab1f9886 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByRefcountedWAccessRollupImpl.cs @@ -0,0 +1,402 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.aggregator; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.agg.service +{ + /// + /// Implementation for handling aggregation with grouping by group-keys. + /// + public class AggSvcGroupByRefcountedWAccessRollupImpl : AggregationServiceBaseGrouped + { + private readonly AggregationAccessorSlotPair[] _accessors; + private readonly AggregationStateFactory[] _accessAggregations; + private readonly bool _isJoin; + private readonly AggregationGroupByRollupDesc _rollupLevelDesc; + + // maintain for each group a row of aggregator states that the expression node can pull the data from via index + private readonly IDictionary[] _aggregatorsPerGroup; + private readonly AggregationMethodPairRow _aggregatorTopGroup; + + // maintain a current row for random access into the aggregator state table + // (row=groups, columns=expression nodes that have aggregation functions) + private AggregationMethod[] _currentAggregatorMethods; + private AggregationState[] _currentAggregatorStates; + private Object _currentGroupKey; + + private readonly Object[] _methodParameterValues; + private bool _hasRemovedKey; + private readonly IList[] _removedKeys; + + /// + /// Ctor. + /// + /// evaluate the sub-expression within the aggregate function (ie. Sum(4*myNum)) + /// collect the aggregation state that evaluators evaluate to, act as prototypes for new aggregationsaggregation states for each group + /// accessor definitions + /// access aggs + /// true for join, false for single-stream + /// The rollup level desc. + /// The top group aggregators. + /// The top group states. + public AggSvcGroupByRefcountedWAccessRollupImpl(ExprEvaluator[] evaluators, AggregationMethodFactory[] prototypes, AggregationAccessorSlotPair[] accessors, AggregationStateFactory[] accessAggregations, bool isJoin, AggregationGroupByRollupDesc rollupLevelDesc, AggregationMethod[] topGroupAggregators, AggregationState[] topGroupStates) + : base(evaluators, prototypes) + { + _aggregatorsPerGroup = new IDictionary[rollupLevelDesc.NumLevelsAggregation]; + _removedKeys = new List[rollupLevelDesc.NumLevelsAggregation]; + for (var i = 0; i < rollupLevelDesc.NumLevelsAggregation; i++) + { + _aggregatorsPerGroup[i] = new Dictionary(); + _removedKeys[i] = new List(2); + } + _accessors = accessors; + _accessAggregations = accessAggregations; + _isJoin = isJoin; + _rollupLevelDesc = rollupLevelDesc; + _aggregatorTopGroup = new AggregationMethodPairRow(0, topGroupAggregators, topGroupStates); + _methodParameterValues = new Object[evaluators.Length]; + } + + public override void ClearResults(ExprEvaluatorContext exprEvaluatorContext) + { + foreach (var state in _aggregatorTopGroup.States) + { + state.Clear(); + } + foreach (var aggregator in _aggregatorTopGroup.Methods) + { + aggregator.Clear(); + } + for (var i = 0; i < _rollupLevelDesc.NumLevelsAggregation; i++) + { + _aggregatorsPerGroup[i].Clear(); + } + } + + public override void ApplyEnter(EventBean[] eventsPerStream, Object compositeGroupKey, ExprEvaluatorContext exprEvaluatorContext) + { + HandleRemovedKeys(); + + var evaluateParams = new EvaluateParams(eventsPerStream, true, exprEvaluatorContext); + + for (var i = 0; i < Evaluators.Length; i++) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggregationGroupedRollupEvalParam(true, _methodParameterValues.Length); } + _methodParameterValues[i] = Evaluators[i].Evaluate(evaluateParams); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggregationGroupedRollupEvalParam(_methodParameterValues[i]); } + } + + var groupKeyPerLevel = (Object[])compositeGroupKey; + for (var i = 0; i < groupKeyPerLevel.Length; i++) + { + var level = _rollupLevelDesc.Levels[i]; + var groupKey = groupKeyPerLevel[i]; + + AggregationMethodPairRow row; + if (!level.IsAggregationTop) + { + row = _aggregatorsPerGroup[level.AggregationOffset].Get(groupKey); + } + else + { + row = _aggregatorTopGroup; + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggregationGroupedApplyEnterLeave(true, Aggregators.Length, _accessAggregations.Length, groupKey); } + + // The aggregators for this group do not exist, need to create them from the prototypes + AggregationMethod[] groupAggregators; + AggregationState[] groupStates; + if (row == null) + { + groupAggregators = AggSvcGroupByUtil.NewAggregators(Aggregators); + groupStates = AggSvcGroupByUtil.NewAccesses(exprEvaluatorContext.AgentInstanceId, _isJoin, _accessAggregations, groupKey, null); + row = new AggregationMethodPairRow(1, groupAggregators, groupStates); + if (!level.IsAggregationTop) + { + _aggregatorsPerGroup[level.AggregationOffset].Put(groupKey, row); + } + } + else + { + groupAggregators = row.Methods; + groupStates = row.States; + row.IncreaseRefcount(); + } + + // For this row, evaluate sub-expressions, enter result + _currentAggregatorMethods = groupAggregators; + _currentAggregatorStates = groupStates; + for (var j = 0; j < Evaluators.Length; j++) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggNoAccessEnterLeave(true, j, groupAggregators[j], Aggregators[j].AggregationExpression); } + groupAggregators[j].Enter(_methodParameterValues[j]); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggNoAccessEnterLeave(true, j, groupAggregators[j]); } + } + + for (var j = 0; j < _currentAggregatorStates.Length; j++) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggAccessEnterLeave(true, j, _currentAggregatorStates[j], _accessAggregations[j].AggregationExpression); } + _currentAggregatorStates[j].ApplyEnter(eventsPerStream, exprEvaluatorContext); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggAccessEnterLeave(true, j, _currentAggregatorStates[j]); } + } + + InternalHandleGroupUpdate(groupKey, row, level); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggregationGroupedApplyEnterLeave(true); } + } + } + + public override void ApplyLeave(EventBean[] eventsPerStream, Object compositeGroupKey, ExprEvaluatorContext exprEvaluatorContext) + { + var evaluateParams = new EvaluateParams(eventsPerStream, false, exprEvaluatorContext); + + for (var i = 0; i < Evaluators.Length; i++) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggregationGroupedRollupEvalParam(false, _methodParameterValues.Length); } + _methodParameterValues[i] = Evaluators[i].Evaluate(evaluateParams); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggregationGroupedRollupEvalParam(_methodParameterValues[i]); } + } + + var groupKeyPerLevel = (Object[])compositeGroupKey; + for (var i = 0; i < groupKeyPerLevel.Length; i++) + { + var level = _rollupLevelDesc.Levels[i]; + var groupKey = groupKeyPerLevel[i]; + + AggregationMethodPairRow row; + if (!level.IsAggregationTop) + { + row = _aggregatorsPerGroup[level.AggregationOffset].Get(groupKey); + } + else + { + row = _aggregatorTopGroup; + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggregationGroupedApplyEnterLeave(false, Aggregators.Length, _accessAggregations.Length, groupKey); } + + // The aggregators for this group do not exist, need to create them from the prototypes + AggregationMethod[] groupAggregators; + AggregationState[] groupStates; + if (row != null) + { + groupAggregators = row.Methods; + groupStates = row.States; + } + else + { + groupAggregators = AggSvcGroupByUtil.NewAggregators(Aggregators); + groupStates = AggSvcGroupByUtil.NewAccesses(exprEvaluatorContext.AgentInstanceId, _isJoin, _accessAggregations, groupKey, null); + row = new AggregationMethodPairRow(1, groupAggregators, groupStates); + if (!level.IsAggregationTop) + { + _aggregatorsPerGroup[level.AggregationOffset].Put(groupKey, row); + } + } + + // For this row, evaluate sub-expressions, enter result + _currentAggregatorMethods = groupAggregators; + _currentAggregatorStates = groupStates; + for (var j = 0; j < Evaluators.Length; j++) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggNoAccessEnterLeave(false, j, groupAggregators[j], Aggregators[j].AggregationExpression); } + groupAggregators[j].Leave(_methodParameterValues[j]); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggNoAccessEnterLeave(false, j, groupAggregators[j]); } + } + + for (var j = 0; j < _currentAggregatorStates.Length; j++) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggAccessEnterLeave(false, j, _currentAggregatorStates[j], _accessAggregations[j].AggregationExpression); } + _currentAggregatorStates[j].ApplyLeave(eventsPerStream, exprEvaluatorContext); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggAccessEnterLeave(false, j, _currentAggregatorStates[j]); } + } + + row.DecreaseRefcount(); + if (row.Refcount <= 0) + { + _hasRemovedKey = true; + if (!level.IsAggregationTop) + { + _removedKeys[level.AggregationOffset].Add(groupKey); + } + } + + InternalHandleGroupUpdate(groupKey, row, level); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggregationGroupedApplyEnterLeave(false); } + } + } + + public override void SetCurrentAccess(Object groupByKey, int agentInstanceId, AggregationGroupByRollupLevel rollupLevel) + { + AggregationMethodPairRow row; + if (rollupLevel.IsAggregationTop) + { + row = _aggregatorTopGroup; + } + else + { + row = _aggregatorsPerGroup[rollupLevel.AggregationOffset].Get(groupByKey); + } + + if (row != null) + { + _currentAggregatorMethods = row.Methods; + _currentAggregatorStates = row.States; + } + else + { + _currentAggregatorMethods = null; + } + + if (_currentAggregatorMethods == null) + { + _currentAggregatorMethods = AggSvcGroupByUtil.NewAggregators(Aggregators); + _currentAggregatorStates = AggSvcGroupByUtil.NewAccesses(agentInstanceId, _isJoin, _accessAggregations, groupByKey, null); + } + + _currentGroupKey = groupByKey; + } + + public override object GetValue(int column, int agentInstanceId, EvaluateParams evaluateParams) + { + if (column < Aggregators.Length) + { + return _currentAggregatorMethods[column].Value; + } + else + { + var pair = _accessors[column - Aggregators.Length]; + return pair.Accessor.GetValue(_currentAggregatorStates[pair.Slot], evaluateParams); + } + } + + public override ICollection GetCollectionOfEvents(int column, EvaluateParams evaluateParams) + { + if (column < Aggregators.Length) + { + return null; + } + else + { + var pair = _accessors[column - Aggregators.Length]; + return pair.Accessor.GetEnumerableEvents(_currentAggregatorStates[pair.Slot], evaluateParams); + } + } + + public override ICollection GetCollectionScalar(int column, EvaluateParams evaluateParams) + { + if (column < Aggregators.Length) + { + return null; + } + else + { + AggregationAccessorSlotPair pair = _accessors[column - Aggregators.Length]; + return pair.Accessor.GetEnumerableScalar(_currentAggregatorStates[pair.Slot], evaluateParams); + } + } + + public override EventBean GetEventBean(int column, EvaluateParams evaluateParams) + { + if (column < Aggregators.Length) + { + return null; + } + else + { + var pair = _accessors[column - Aggregators.Length]; + return pair.Accessor.GetEnumerableEvent(_currentAggregatorStates[pair.Slot], evaluateParams); + } + } + + public override void SetRemovedCallback(AggregationRowRemovedCallback callback) + { + // not applicable + } + + public void InternalHandleGroupUpdate(Object groupByKey, AggregationMethodPairRow row, AggregationGroupByRollupLevel groupByRollupLevel) + { + // no action required + } + + public void InternalHandleGroupRemove(Object groupByKey, AggregationGroupByRollupLevel groupByRollupLevel) + { + // no action required + } + + public override void Accept(AggregationServiceVisitor visitor) + { + visitor.VisitAggregations(GetGroupKeyCount(), _aggregatorsPerGroup); + } + + public override void AcceptGroupDetail(AggregationServiceVisitorWGroupDetail visitor) + { + visitor.VisitGrouped(GetGroupKeyCount()); + foreach (var anAggregatorsPerGroup in _aggregatorsPerGroup) + { + foreach (var entry in anAggregatorsPerGroup) + { + visitor.VisitGroup(entry.Key, entry.Value); + } + } + visitor.VisitGroup(new Object[0], _aggregatorTopGroup); + } + + public override bool IsGrouped + { + get { return true; } + } + + protected void HandleRemovedKeys() + { + if (!_hasRemovedKey) + { + return; + } + _hasRemovedKey = false; + for (var i = 0; i < _removedKeys.Length; i++) + { + if (_removedKeys[i].IsEmpty()) + { + continue; + } + foreach (var removedKey in _removedKeys[i]) + { + _aggregatorsPerGroup[i].Remove(removedKey); + InternalHandleGroupRemove(removedKey, _rollupLevelDesc.Levels[i]); + } + _removedKeys[i].Clear(); + } + } + + public override Object GetGroupKey(int agentInstanceId) + { + return _currentGroupKey; + } + + public override ICollection GetGroupKeys(ExprEvaluatorContext exprEvaluatorContext) + { + throw new NotSupportedException(); + } + + private int GetGroupKeyCount() + { + return 1 + _aggregatorsPerGroup.Sum(anAggregatorsPerGroup => anAggregatorsPerGroup.Count); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByUtil.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByUtil.cs new file mode 100755 index 000000000..d339a0750 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByUtil.cs @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.aggregator; + +namespace com.espertech.esper.epl.agg.service +{ + public class AggSvcGroupByUtil { + public static AggregationMethod[] NewAggregators(AggregationMethodFactory[] prototypes) { + var row = new AggregationMethod[prototypes.Length]; + for (int i = 0; i < prototypes.Length; i++) { + row[i] = prototypes[i].Make(); + } + return row; + } + + public static AggregationState[] NewAccesses(int agentInstanceId, bool isJoin, AggregationStateFactory[] accessAggSpecs, Object groupKey, AggregationServicePassThru passThru) { + var row = new AggregationState[accessAggSpecs.Length]; + int i = 0; + foreach (AggregationStateFactory spec in accessAggSpecs) { + row[i] = spec.CreateAccess(agentInstanceId, isJoin, groupKey, passThru); // no group id assigned + i++; + } + return row; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByWTableBase.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByWTableBase.cs new file mode 100755 index 000000000..bfa86980d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByWTableBase.cs @@ -0,0 +1,260 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.aggregator; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.epl.table.strategy; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.agg.service +{ + /// + /// Implementation for handling aggregation with grouping by group-keys. + /// + public abstract class AggSvcGroupByWTableBase : AggregationService + { + protected readonly TableMetadata TableMetadata; + protected readonly TableColumnMethodPair[] MethodPairs; + protected readonly AggregationAccessorSlotPair[] Accessors; + protected readonly bool IsJoin; + protected readonly TableStateInstanceGrouped TableStateInstance; + protected readonly int[] TargetStates; + protected readonly ExprNode[] AccessStateExpr; + private readonly AggregationAgent[] _agents; + + // maintain a current row for random access into the aggregator state table + // (row=groups, columns=expression nodes that have aggregation functions) + protected AggregationMethod[] CurrentAggregatorMethods; + protected AggregationState[] CurrentAggregatorStates; + protected object CurrentGroupKey; + + protected AggSvcGroupByWTableBase( + TableMetadata tableMetadata, + TableColumnMethodPair[] methodPairs, + AggregationAccessorSlotPair[] accessors, + bool join, + TableStateInstanceGrouped tableStateInstance, + int[] targetStates, + ExprNode[] accessStateExpr, + AggregationAgent[] agents) + { + TableMetadata = tableMetadata; + MethodPairs = methodPairs; + Accessors = accessors; + IsJoin = join; + TableStateInstance = tableStateInstance; + TargetStates = targetStates; + AccessStateExpr = accessStateExpr; + _agents = agents; + } + + public abstract void ApplyEnterInternal(EventBean[] eventsPerStream, object groupByKey, ExprEvaluatorContext exprEvaluatorContext); + public abstract void ApplyLeaveInternal(EventBean[] eventsPerStream, object groupByKey, ExprEvaluatorContext exprEvaluatorContext); + + public void ApplyEnter(EventBean[] eventsPerStream, object groupByKey, ExprEvaluatorContext exprEvaluatorContext) + { + // acquire table-level write lock + ExprTableEvalLockUtil.ObtainLockUnless(TableStateInstance.TableLevelRWLock.WriteLock, exprEvaluatorContext); + ApplyEnterInternal(eventsPerStream, groupByKey, exprEvaluatorContext); + } + + public void ApplyLeave(EventBean[] eventsPerStream, object groupByKey, ExprEvaluatorContext exprEvaluatorContext) + { + // acquire table-level write lock + ExprTableEvalLockUtil.ObtainLockUnless(TableStateInstance.TableLevelRWLock.WriteLock, exprEvaluatorContext); + ApplyLeaveInternal(eventsPerStream, groupByKey, exprEvaluatorContext); + } + + protected void ApplyEnterGroupKey(EventBean[] eventsPerStream, object groupByKey, ExprEvaluatorContext exprEvaluatorContext) + { + var bean = TableStateInstance.GetCreateRowIntoTable(groupByKey, exprEvaluatorContext); + var row = (AggregationRowPair)bean.Properties[0]; + + CurrentAggregatorMethods = row.Methods; + CurrentAggregatorStates = row.States; + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggregationGroupedApplyEnterLeave(true, MethodPairs.Length, TargetStates.Length, groupByKey); } + + var evaluateParams = new EvaluateParams(eventsPerStream, true, exprEvaluatorContext); + + for (var j = 0; j < MethodPairs.Length; j++) + { + var methodPair = MethodPairs[j]; + var method = CurrentAggregatorMethods[methodPair.TargetIndex]; + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggNoAccessEnterLeave(true, j, method, methodPair.AggregationNode); } + var columnResult = methodPair.Evaluator.Evaluate(evaluateParams); + method.Enter(columnResult); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggNoAccessEnterLeave(true, j, method); } + } + + for (var i = 0; i < TargetStates.Length; i++) + { + var state = CurrentAggregatorStates[TargetStates[i]]; + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggAccessEnterLeave(true, i, state, AccessStateExpr[i]); } + _agents[i].ApplyEnter(eventsPerStream, exprEvaluatorContext, state); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggAccessEnterLeave(true, i, state); } + } + + TableStateInstance.HandleRowUpdated(bean); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggregationGroupedApplyEnterLeave(true); } + } + + protected void ApplyLeaveGroupKey(EventBean[] eventsPerStream, object groupByKey, ExprEvaluatorContext exprEvaluatorContext) + { + var bean = TableStateInstance.GetCreateRowIntoTable(groupByKey, exprEvaluatorContext); + var row = (AggregationRowPair)bean.Properties[0]; + + CurrentAggregatorMethods = row.Methods; + CurrentAggregatorStates = row.States; + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggregationGroupedApplyEnterLeave(false, MethodPairs.Length, TargetStates.Length, groupByKey); } + + var evaluateParams = new EvaluateParams(eventsPerStream, false, exprEvaluatorContext); + + for (var j = 0; j < MethodPairs.Length; j++) + { + var methodPair = MethodPairs[j]; + var method = CurrentAggregatorMethods[methodPair.TargetIndex]; + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggNoAccessEnterLeave(false, j, method, methodPair.AggregationNode); } + var columnResult = methodPair.Evaluator.Evaluate(evaluateParams); + method.Leave(columnResult); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggNoAccessEnterLeave(false, j, method); } + } + + for (var i = 0; i < TargetStates.Length; i++) + { + var state = CurrentAggregatorStates[TargetStates[i]]; + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggAccessEnterLeave(false, i, state, AccessStateExpr[i]); } + _agents[i].ApplyLeave(eventsPerStream, exprEvaluatorContext, state); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggAccessEnterLeave(false, i, state); } + } + + TableStateInstance.HandleRowUpdated(bean); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggregationGroupedApplyEnterLeave(false); } + } + + public virtual void SetCurrentAccess(object groupByKey, int agentInstanceId, AggregationGroupByRollupLevel rollupLevel) + { + var bean = TableStateInstance.GetRowForGroupKey(groupByKey); + + if (bean != null) + { + var row = (AggregationRowPair)bean.Properties[0]; + CurrentAggregatorMethods = row.Methods; + CurrentAggregatorStates = row.States; + } + else + { + CurrentAggregatorMethods = null; + } + + CurrentGroupKey = groupByKey; + } + + public object GetValue(int column, int agentInstanceId, EvaluateParams evaluateParams) + { + if (column < CurrentAggregatorMethods.Length) + { + return CurrentAggregatorMethods[column].Value; + } + else + { + var pair = Accessors[column - CurrentAggregatorMethods.Length]; + return pair.Accessor.GetValue(CurrentAggregatorStates[pair.Slot], evaluateParams); + } + } + + public ICollection GetCollectionOfEvents(int column, EvaluateParams evaluateParams) + { + if (column < CurrentAggregatorMethods.Length) + { + return null; + } + else + { + var pair = Accessors[column - CurrentAggregatorMethods.Length]; + return pair.Accessor.GetEnumerableEvents(CurrentAggregatorStates[pair.Slot], evaluateParams); + } + } + + public ICollection GetCollectionScalar(int column, EvaluateParams evaluateParams) + { + if (column < CurrentAggregatorMethods.Length) + { + return null; + } + else + { + var pair = Accessors[column - CurrentAggregatorMethods.Length]; + return pair.Accessor.GetEnumerableScalar(CurrentAggregatorStates[pair.Slot], evaluateParams); + } + } + + public EventBean GetEventBean(int column, EvaluateParams evaluateParams) + { + if (column < CurrentAggregatorMethods.Length) + { + return null; + } + else + { + var pair = Accessors[column - CurrentAggregatorMethods.Length]; + return pair.Accessor.GetEnumerableEvent(CurrentAggregatorStates[pair.Slot], evaluateParams); + } + } + + public void SetRemovedCallback(AggregationRowRemovedCallback callback) + { + // not applicable + } + + public void Accept(AggregationServiceVisitor visitor) + { + // not applicable + } + + public void AcceptGroupDetail(AggregationServiceVisitorWGroupDetail visitor) + { + // not applicable + } + + public bool IsGrouped + { + get { return true; } + } + + public object GetGroupKey(int agentInstanceId) + { + return CurrentGroupKey; + } + + public ICollection GetGroupKeys(ExprEvaluatorContext exprEvaluatorContext) + { + return TableStateInstance.GroupKeys; + } + + public void ClearResults(ExprEvaluatorContext exprEvaluatorContext) + { + TableStateInstance.Clear(); + } + + public void Stop() + { + } + + public AggregationService GetContextPartitionAggregationService(int agentInstanceId) + { + return this; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByWTableFactory.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByWTableFactory.cs new file mode 100755 index 000000000..0ee951196 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByWTableFactory.cs @@ -0,0 +1,74 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.table.mgmt; + +namespace com.espertech.esper.epl.agg.service +{ + /// + /// Implementation for handling aggregation with grouping by group-keys. + /// + public class AggSvcGroupByWTableFactory : AggregationServiceFactory + { + private readonly TableMetadata _tableMetadata; + private readonly TableColumnMethodPair[] _methodPairs; + private readonly AggregationAccessorSlotPair[] _accessors; + private readonly bool _isJoin; + private readonly int[] _targetStates; + private readonly ExprNode[] _accessStateExpr; + private readonly AggregationAgent[] _agents; + private readonly AggregationGroupByRollupDesc _groupByRollupDesc; + + public AggSvcGroupByWTableFactory( + TableMetadata tableMetadata, + TableColumnMethodPair[] methodPairs, + AggregationAccessorSlotPair[] accessors, + bool join, + int[] targetStates, + ExprNode[] accessStateExpr, + AggregationAgent[] agents, + AggregationGroupByRollupDesc groupByRollupDesc) + { + _tableMetadata = tableMetadata; + _methodPairs = methodPairs; + _accessors = accessors; + _isJoin = join; + _targetStates = targetStates; + _accessStateExpr = accessStateExpr; + _agents = agents; + _groupByRollupDesc = groupByRollupDesc; + } + + public AggregationService MakeService(AgentInstanceContext agentInstanceContext, EngineImportService engineImportService, bool isSubquery, int? subqueryNumber) + { + var tableState = (TableStateInstanceGrouped) agentInstanceContext.StatementContext.TableService.GetState(_tableMetadata.TableName, agentInstanceContext.AgentInstanceId); + if (_groupByRollupDesc == null) + { + return new AggSvcGroupByWTableImpl( + _tableMetadata, _methodPairs, _accessors, _isJoin, + tableState, _targetStates, _accessStateExpr, _agents); + } + if (_tableMetadata.KeyTypes.Length > 1) + { + return new AggSvcGroupByWTableRollupMultiKeyImpl( + _tableMetadata, _methodPairs, _accessors, _isJoin, + tableState, _targetStates, _accessStateExpr, _agents, _groupByRollupDesc); + } + else + { + return new AggSvcGroupByWTableRollupSingleKeyImpl( + _tableMetadata, _methodPairs, _accessors, _isJoin, + tableState, _targetStates, _accessStateExpr, _agents); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByWTableImpl.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByWTableImpl.cs new file mode 100755 index 000000000..02d914ed8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByWTableImpl.cs @@ -0,0 +1,59 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.aggregator; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.epl.table.strategy; +using com.espertech.esper.events; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.agg.service +{ + /// + /// Implementation for handling aggregation with grouping by group-keys. + /// + public class AggSvcGroupByWTableImpl : AggSvcGroupByWTableBase + { + public AggSvcGroupByWTableImpl( + TableMetadata tableMetadata, + TableColumnMethodPair[] methodPairs, + AggregationAccessorSlotPair[] accessors, + bool join, + TableStateInstanceGrouped tableStateInstance, + int[] targetStates, + ExprNode[] accessStateExpr, + AggregationAgent[] agents) + : base(tableMetadata, methodPairs, accessors, join, tableStateInstance, targetStates, accessStateExpr, agents) + { + } + + public override void ApplyEnterInternal( + EventBean[] eventsPerStream, + Object groupByKey, + ExprEvaluatorContext exprEvaluatorContext) + { + ApplyEnterGroupKey(eventsPerStream, groupByKey, exprEvaluatorContext); + } + + public override void ApplyLeaveInternal( + EventBean[] eventsPerStream, + Object groupByKey, + ExprEvaluatorContext exprEvaluatorContext) + { + ApplyLeaveGroupKey(eventsPerStream, groupByKey, exprEvaluatorContext); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByWTableRollupMultiKeyImpl.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByWTableRollupMultiKeyImpl.cs new file mode 100755 index 000000000..defe3cb7f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByWTableRollupMultiKeyImpl.cs @@ -0,0 +1,76 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.table.mgmt; + +namespace com.espertech.esper.epl.agg.service +{ + /// + /// Implementation for handling aggregation with grouping by group-keys. + /// + public class AggSvcGroupByWTableRollupMultiKeyImpl : AggSvcGroupByWTableBase + { + private readonly AggregationGroupByRollupDesc groupByRollupDesc; + + public AggSvcGroupByWTableRollupMultiKeyImpl( + TableMetadata tableMetadata, + TableColumnMethodPair[] methodPairs, + AggregationAccessorSlotPair[] accessors, + bool join, + TableStateInstanceGrouped tableStateInstance, + int[] targetStates, + ExprNode[] accessStateExpr, + AggregationAgent[] agents, + AggregationGroupByRollupDesc groupByRollupDesc) + : base(tableMetadata, methodPairs, accessors, join, tableStateInstance, targetStates, accessStateExpr, agents) + { + this.groupByRollupDesc = groupByRollupDesc; + } + + public override void ApplyEnterInternal(EventBean[] eventsPerStream, object compositeGroupByKey, ExprEvaluatorContext exprEvaluatorContext) + { + var groupKeyPerLevel = (object[]) compositeGroupByKey; + for (var i = 0; i < groupKeyPerLevel.Length; i++) { + var level = groupByRollupDesc.Levels[i]; + object groupByKey = level.ComputeMultiKey(groupKeyPerLevel[i], TableMetadata.KeyTypes.Length); + ApplyEnterGroupKey(eventsPerStream, groupByKey, exprEvaluatorContext); + } + } + + public override void ApplyLeaveInternal(EventBean[] eventsPerStream, object compositeGroupByKey, ExprEvaluatorContext exprEvaluatorContext) + { + var groupKeyPerLevel = (object[]) compositeGroupByKey; + for (var i = 0; i < groupKeyPerLevel.Length; i++) { + var level = groupByRollupDesc.Levels[i]; + object groupByKey = level.ComputeMultiKey(groupKeyPerLevel[i], TableMetadata.KeyTypes.Length); + ApplyLeaveGroupKey(eventsPerStream, groupByKey, exprEvaluatorContext); + } + } + + public override void SetCurrentAccess(object groupByKey, int agentInstanceId, AggregationGroupByRollupLevel rollupLevel) + { + var key = rollupLevel.ComputeMultiKey(groupByKey, TableMetadata.KeyTypes.Length); + var bean = TableStateInstance.GetRowForGroupKey(key); + + if (bean != null) { + var row = (AggregationRowPair) bean.Properties[0]; + CurrentAggregatorMethods = row.Methods; + CurrentAggregatorStates = row.States; + } + else { + CurrentAggregatorMethods = null; + } + + this.CurrentGroupKey = key; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByWTableRollupSingleKeyImpl.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByWTableRollupSingleKeyImpl.cs new file mode 100755 index 000000000..64f912fce --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupByWTableRollupSingleKeyImpl.cs @@ -0,0 +1,42 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.table.mgmt; + +namespace com.espertech.esper.epl.agg.service +{ + /// + /// Implementation for handling aggregation with grouping by group-keys. + /// + public class AggSvcGroupByWTableRollupSingleKeyImpl : AggSvcGroupByWTableBase + { + public AggSvcGroupByWTableRollupSingleKeyImpl(TableMetadata tableMetadata, TableColumnMethodPair[] methodPairs, AggregationAccessorSlotPair[] accessors, bool join, TableStateInstanceGrouped tableStateInstance, int[] targetStates, ExprNode[] accessStateExpr, AggregationAgent[] agents) + : base(tableMetadata, methodPairs, accessors, join, tableStateInstance, targetStates, accessStateExpr, agents) + { + } + + public override void ApplyEnterInternal(EventBean[] eventsPerStream, object compositeGroupByKey, ExprEvaluatorContext exprEvaluatorContext) + { + var groupKeyPerLevel = (object[]) compositeGroupByKey; + foreach (var groupByKey in groupKeyPerLevel) { + ApplyEnterGroupKey(eventsPerStream, groupByKey, exprEvaluatorContext); + } + } + + public override void ApplyLeaveInternal(EventBean[] eventsPerStream, object compositeGroupByKey, ExprEvaluatorContext exprEvaluatorContext) + { + var groupKeyPerLevel = (object[]) compositeGroupByKey; + foreach (var groupByKey in groupKeyPerLevel) { + ApplyLeaveGroupKey(eventsPerStream, groupByKey, exprEvaluatorContext); + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupLocalGroupByBase.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupLocalGroupByBase.cs new file mode 100755 index 000000000..73195575b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggSvcGroupLocalGroupByBase.cs @@ -0,0 +1,384 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.aggregator; +using com.espertech.esper.epl.agg.util; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.agg.service +{ + /// + /// Implementation for handling aggregation with grouping by group-keys. + /// + public abstract class AggSvcGroupLocalGroupByBase : AggregationService + { + protected internal readonly bool IsJoin; + protected internal readonly AggregationLocalGroupByPlan LocalGroupByPlan; + + public abstract object GetValue(int column, int agentInstanceId, EvaluateParams evaluateParams); + + public abstract void SetCurrentAccess(object groupKey, int agentInstanceId, AggregationGroupByRollupLevel rollupLevel); + + // state + + protected abstract object ComputeGroupKey( + AggregationLocalGroupByLevel level, + object groupKey, + ExprEvaluator[] partitionEval, + EventBean[] eventsPerStream, + bool newData, + ExprEvaluatorContext exprEvaluatorContext); + + protected AggSvcGroupLocalGroupByBase(bool isJoin, AggregationLocalGroupByPlan localGroupByPlan) + { + IsJoin = isJoin; + LocalGroupByPlan = localGroupByPlan; + + AggregatorsPerLevelAndGroup = new IDictionary[localGroupByPlan.AllLevels.Length]; + for (var i = 0; i < localGroupByPlan.AllLevels.Length; i++) + { + AggregatorsPerLevelAndGroup[i] = new Dictionary(); + } + RemovedKeys = new List>(); + } + + public void ClearResults(ExprEvaluatorContext exprEvaluatorContext) + { + ClearResults(AggregatorsPerLevelAndGroup, AggregatorsTopLevel, StatesTopLevel); + } + + public void ApplyEnter( + EventBean[] eventsPerStream, + object groupByKeyProvided, + ExprEvaluatorContext exprEvaluatorContext) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggregationGroupedApplyEnterLeave(true, LocalGroupByPlan.NumMethods, LocalGroupByPlan.NumAccess, groupByKeyProvided); } + + HandleRemovedKeys(); + + if (LocalGroupByPlan.OptionalLevelTop != null) + { + if (AggregatorsTopLevel == null) + { + AggregatorsTopLevel = AggSvcGroupByUtil.NewAggregators(LocalGroupByPlan.OptionalLevelTop.MethodFactories); + StatesTopLevel = AggSvcGroupByUtil.NewAccesses(exprEvaluatorContext.AgentInstanceId, IsJoin, LocalGroupByPlan.OptionalLevelTop.StateFactories, null, null); + } + AggregateIntoEnter( + LocalGroupByPlan.OptionalLevelTop, AggregatorsTopLevel, StatesTopLevel, eventsPerStream, + exprEvaluatorContext); + InternalHandleUpdatedTop(); + } + + for (var levelNum = 0; levelNum < LocalGroupByPlan.AllLevels.Length; levelNum++) + { + var level = LocalGroupByPlan.AllLevels[levelNum]; + var partitionEval = level.PartitionEvaluators; + var groupByKey = ComputeGroupKey( + level, groupByKeyProvided, partitionEval, eventsPerStream, true, exprEvaluatorContext); + var row = AggregatorsPerLevelAndGroup[levelNum].Get(groupByKey); + if (row == null) + { + var rowAggregators = AggSvcGroupByUtil.NewAggregators(level.MethodFactories); + AggregationState[] rowStates = AggSvcGroupByUtil.NewAccesses(exprEvaluatorContext.AgentInstanceId, IsJoin, level.StateFactories, groupByKey, null); + row = new AggregationMethodPairRow(1, rowAggregators, rowStates); + AggregatorsPerLevelAndGroup[levelNum].Put(groupByKey, row); + } + else + { + row.IncreaseRefcount(); + } + + AggregateIntoEnter(level, row.Methods, row.States, eventsPerStream, exprEvaluatorContext); + InternalHandleUpdatedGroup(levelNum, groupByKey, row); + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggregationGroupedApplyEnterLeave(true); } + } + + public void ApplyLeave( + EventBean[] eventsPerStream, + object groupByKeyProvided, + ExprEvaluatorContext exprEvaluatorContext) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggregationGroupedApplyEnterLeave(false, LocalGroupByPlan.NumMethods, LocalGroupByPlan.NumAccess, groupByKeyProvided); } + + if (LocalGroupByPlan.OptionalLevelTop != null) + { + if (AggregatorsTopLevel == null) + { + AggregatorsTopLevel = AggSvcGroupByUtil.NewAggregators(LocalGroupByPlan.OptionalLevelTop.MethodFactories); + StatesTopLevel = AggSvcGroupByUtil.NewAccesses(exprEvaluatorContext.AgentInstanceId, IsJoin, LocalGroupByPlan.OptionalLevelTop.StateFactories, null, null); + } + AggregateIntoLeave( + LocalGroupByPlan.OptionalLevelTop, AggregatorsTopLevel, StatesTopLevel, eventsPerStream, + exprEvaluatorContext); + InternalHandleUpdatedTop(); + } + + for (var levelNum = 0; levelNum < LocalGroupByPlan.AllLevels.Length; levelNum++) + { + var level = LocalGroupByPlan.AllLevels[levelNum]; + var partitionEval = level.PartitionEvaluators; + var groupByKey = ComputeGroupKey( + level, groupByKeyProvided, partitionEval, eventsPerStream, true, exprEvaluatorContext); + var row = AggregatorsPerLevelAndGroup[levelNum].Get(groupByKey); + if (row == null) + { + var rowAggregators = AggSvcGroupByUtil.NewAggregators(level.MethodFactories); + AggregationState[] rowStates = AggSvcGroupByUtil.NewAccesses(exprEvaluatorContext.AgentInstanceId, IsJoin, level.StateFactories, groupByKey, null); + row = new AggregationMethodPairRow(1, rowAggregators, rowStates); + AggregatorsPerLevelAndGroup[levelNum].Put(groupByKey, row); + } + else + { + row.DecreaseRefcount(); + if (row.Refcount <= 0) + { + RemovedKeys.Add(new Pair(levelNum, groupByKey)); + } + } + AggregateIntoLeave(level, row.Methods, row.States, eventsPerStream, exprEvaluatorContext); + InternalHandleUpdatedGroup(levelNum, groupByKey, row); + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggregationGroupedApplyEnterLeave(false); } + } + + public ICollection GetCollectionOfEvents(int column, EvaluateParams evaluateParams) + { + var col = LocalGroupByPlan.Columns[column]; + if (col.PartitionEvaluators.Length == 0) + { + return col.Pair.Accessor.GetEnumerableEvents( + StatesTopLevel[col.Pair.Slot], evaluateParams); + } + var groupByKey = ComputeGroupKey(col.PartitionEvaluators, evaluateParams.EventsPerStream, evaluateParams.IsNewData, evaluateParams.ExprEvaluatorContext); + var row = AggregatorsPerLevelAndGroup[col.LevelNum].Get(groupByKey); + return col.Pair.Accessor.GetEnumerableEvents(row.States[col.Pair.Slot], evaluateParams); + } + + public ICollection GetCollectionScalar(int column, EvaluateParams evaluateParams) + { + var col = LocalGroupByPlan.Columns[column]; + if (col.PartitionEvaluators.Length == 0) + { + return col.Pair.Accessor.GetEnumerableScalar( + StatesTopLevel[col.Pair.Slot], evaluateParams); + } + var groupByKey = ComputeGroupKey(col.PartitionEvaluators, evaluateParams.EventsPerStream, evaluateParams.IsNewData, evaluateParams.ExprEvaluatorContext); + var row = AggregatorsPerLevelAndGroup[col.LevelNum].Get(groupByKey); + return col.Pair.Accessor.GetEnumerableScalar(row.States[col.Pair.Slot], evaluateParams); + } + + public EventBean GetEventBean(int column, EvaluateParams evaluateParams) + { + var col = LocalGroupByPlan.Columns[column]; + if (col.PartitionEvaluators.Length == 0) + { + return col.Pair.Accessor.GetEnumerableEvent( + StatesTopLevel[col.Pair.Slot], evaluateParams); + } + var groupByKey = ComputeGroupKey(col.PartitionEvaluators, evaluateParams.EventsPerStream, evaluateParams.IsNewData, evaluateParams.ExprEvaluatorContext); + var row = AggregatorsPerLevelAndGroup[col.LevelNum].Get(groupByKey); + return col.Pair.Accessor.GetEnumerableEvent(row.States[col.Pair.Slot], evaluateParams); + } + + public virtual bool IsGrouped + { + get { return true; } + } + + public virtual void SetRemovedCallback(AggregationRowRemovedCallback callback) + { + // not applicable + } + + public virtual void Accept(AggregationServiceVisitor visitor) + { + visitor.VisitAggregations(NumGroups, AggregatorsTopLevel, StatesTopLevel, AggregatorsPerLevelAndGroup); + } + + public void AcceptGroupDetail(AggregationServiceVisitorWGroupDetail visitor) + { + visitor.VisitGrouped(NumGroups); + if (AggregatorsTopLevel != null) + { + visitor.VisitGroup(null, AggregatorsTopLevel, StatesTopLevel); + } + for (var i = 0; i < LocalGroupByPlan.AllLevels.Length; i++) + { + foreach (var entry in AggregatorsPerLevelAndGroup[i]) + { + visitor.VisitGroup(entry.Key, entry.Value); + } + } + } + + public void InternalHandleUpdatedGroup(int level, object groupByKey, AggregationMethodPairRow row) + { + // no action required + } + + public void InternalHandleUpdatedTop() + { + // no action required + } + + public void InternalHandleGroupRemove(Pair groupByKey) + { + // no action required + } + + public void HandleRemovedKeys() + { + if (!RemovedKeys.IsEmpty()) + // we collect removed keys lazily on the next enter to reduce the chance of empty-group queries creating empty aggregators temporarily + { + foreach (var removedKey in RemovedKeys) + { + AggregatorsPerLevelAndGroup[removedKey.First].Remove(removedKey.Second); + InternalHandleGroupRemove(removedKey); + } + RemovedKeys.Clear(); + } + } + + public object GetGroupKey(int agentInstanceId) + { + return null; + } + + public ICollection GetGroupKeys(ExprEvaluatorContext exprEvaluatorContext) + { + throw new UnsupportedOperationException(); + } + + public static object ComputeGroupKey( + ExprEvaluator[] partitionEval, + EventBean[] eventsPerStream, + bool b, + ExprEvaluatorContext exprEvaluatorContext) + { + var evaluateParams = new EvaluateParams(eventsPerStream, true, exprEvaluatorContext); + if (partitionEval.Length == 1) + { + return partitionEval[0].Evaluate(evaluateParams); + } + var keys = new object[partitionEval.Length]; + for (var i = 0; i < keys.Length; i++) + { + keys[i] = partitionEval[i].Evaluate(evaluateParams); + } + return new MultiKeyUntyped(keys); + } + + public static void AggregateIntoEnter( + AggregationLocalGroupByLevel level, + AggregationMethod[] methods, + AggregationState[] states, + EventBean[] eventsPerStream, + ExprEvaluatorContext exprEvaluatorContext) + { + var evaluateParams = new EvaluateParams(eventsPerStream, true, exprEvaluatorContext); + for (var i = 0; i < level.MethodEvaluators.Length; i++) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggNoAccessEnterLeave(true, i, methods[i], level.MethodFactories[i].AggregationExpression); } + var value = level.MethodEvaluators[i].Evaluate(evaluateParams); + methods[i].Enter(value); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggNoAccessEnterLeave(true, i, methods[i]); } + } + for (var i = 0; i < states.Length; i++) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggAccessEnterLeave(true, i, states[i], level.StateFactories[i].AggregationExpression); } + states[i].ApplyEnter(eventsPerStream, exprEvaluatorContext); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggAccessEnterLeave(true, i, states[i]); } + } + } + + public static void AggregateIntoLeave( + AggregationLocalGroupByLevel level, + AggregationMethod[] methods, + AggregationState[] states, + EventBean[] eventsPerStream, + ExprEvaluatorContext exprEvaluatorContext) + { + var evaluateParams = new EvaluateParams(eventsPerStream, false, exprEvaluatorContext); + for (var i = 0; i < level.MethodEvaluators.Length; i++) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggNoAccessEnterLeave(false, i, methods[i], level.MethodFactories[i].AggregationExpression); } + var value = level.MethodEvaluators[i].Evaluate(evaluateParams); + methods[i].Leave(value); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggNoAccessEnterLeave(false, i, methods[i]); } + } + for (var i = 0; i < states.Length; i++) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QAggAccessEnterLeave(false, i, states[i], level.StateFactories[i].AggregationExpression); } + states[i].ApplyLeave(eventsPerStream, exprEvaluatorContext); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AAggAccessEnterLeave(false, i, states[i]); } + } + } + + public static void ClearResults( + IDictionary[] aggregatorsPerLevelAndGroup, + AggregationMethod[] aggregatorsTopLevel, + AggregationState[] statesTopLevel) + { + foreach (var aggregatorsPerGroup in aggregatorsPerLevelAndGroup) + { + aggregatorsPerGroup.Clear(); + } + if (aggregatorsTopLevel != null) + { + foreach (var method in aggregatorsTopLevel) + { + method.Clear(); + } + foreach (var state in statesTopLevel) + { + state.Clear(); + } + } + } + + public AggregationMethod[] AggregatorsTopLevel { get; set; } + + public AggregationState[] StatesTopLevel { get; set; } + + public IDictionary[] AggregatorsPerLevelAndGroup { get; set; } + + public IList> RemovedKeys { get; set; } + + public void Stop() + { + } + + public AggregationService GetContextPartitionAggregationService(int agentInstanceId) + { + return this; + } + + private int NumGroups + { + get + { + var size = AggregatorsTopLevel != null ? 1 : 0; + for (var i = 0; i < LocalGroupByPlan.AllLevels.Length; i++) + { + size += AggregatorsPerLevelAndGroup[i].Count; + } + return size; + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggregationGroupByRollupDesc.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationGroupByRollupDesc.cs new file mode 100755 index 000000000..9575ce1c1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationGroupByRollupDesc.cs @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.Linq; + +namespace com.espertech.esper.epl.agg.service +{ + public class AggregationGroupByRollupDesc + { + public AggregationGroupByRollupDesc(AggregationGroupByRollupLevel[] levels) + { + Levels = levels; + NumLevelsAggregation = levels.Count(level => !level.IsAggregationTop); + } + + public AggregationGroupByRollupLevel[] Levels { get; private set; } + + public int NumLevelsAggregation { get; private set; } + + public int NumLevels + { + get { return Levels.Length; } + } + + public static AggregationGroupByRollupDesc Make(int[][] indexes) + { + var levels = new List(); + var countOffset = 0; + var countNumber = -1; + foreach (var mki in indexes) + { + countNumber++; + if (mki.Length == 0) + { + levels.Add(new AggregationGroupByRollupLevel(countNumber, -1, null)); + } + else + { + levels.Add(new AggregationGroupByRollupLevel(countNumber, countOffset, mki)); + countOffset++; + } + } + AggregationGroupByRollupLevel[] levelsarr = levels.ToArray(); + return new AggregationGroupByRollupDesc(levelsarr); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggregationGroupByRollupLevel.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationGroupByRollupLevel.cs new file mode 100755 index 000000000..a6b960af5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationGroupByRollupLevel.cs @@ -0,0 +1,116 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.epl.agg.service +{ + [Serializable] + public class AggregationGroupByRollupLevel + { + private readonly int _levelOffset; + + public AggregationGroupByRollupLevel(int levelNumber, int levelOffset, int[] rollupKeys) + { + LevelNumber = levelNumber; + _levelOffset = levelOffset; + RollupKeys = rollupKeys; + } + + public int LevelNumber { get; private set; } + + public int AggregationOffset + { + get + { + if (IsAggregationTop) + { + throw new ArgumentException(); + } + return _levelOffset; + } + } + + public bool IsAggregationTop + { + get { return _levelOffset == -1; } + } + + public int[] RollupKeys { get; private set; } + + public Object ComputeSubkey(Object groupKey) + { + if (IsAggregationTop) + { + return null; + } + if (groupKey is MultiKeyUntyped) + { + var mk = (MultiKeyUntyped) groupKey; + var keys = mk.Keys; + if (RollupKeys.Length == keys.Length) + { + return mk; + } + else if (RollupKeys.Length == 1) + { + return keys[RollupKeys[0]]; + } + else + { + var subkeys = new Object[RollupKeys.Length]; + var count = 0; + foreach (var rollupKey in RollupKeys) + { + subkeys[count++] = keys[rollupKey]; + } + return new MultiKeyUntyped(subkeys); + } + } + else + { + return groupKey; + } + } + + public override String ToString() + { + return "GroupByRollupLevel{" + + "levelOffset=" + _levelOffset + + ", rollupKeys=" + RollupKeys.Render() + + '}'; + } + + public MultiKeyUntyped ComputeMultiKey(Object subkey, int numExpected) + { + var rollupKeys = RollupKeys; + + if (subkey is MultiKeyUntyped) + { + var mk = (MultiKeyUntyped) subkey; + if (mk.Keys.Length == numExpected) { + return mk; + } + var keysInner = new Object[] {numExpected}; + for (var i = 0; i < rollupKeys.Length; i++) { + keysInner[rollupKeys[i]] = mk.Keys[i]; + } + return new MultiKeyUntyped(keysInner); + } + var keys = new Object[numExpected]; + if (subkey == null) { + return new MultiKeyUntyped(keys); + } + keys[rollupKeys[0]] = subkey; + return new MultiKeyUntyped(keys); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggregationMethodFactory.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationMethodFactory.cs new file mode 100755 index 000000000..90c1f3c66 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationMethodFactory.cs @@ -0,0 +1,44 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.aggregator; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.agg.service +{ + /// + /// Factory for aggregation methods. + /// + public interface AggregationMethodFactory + { + bool IsAccessAggregation { get; } + + AggregationMethod Make(); + + Type ResultType { get; } + + AggregationStateKey GetAggregationStateKey(bool isMatchRecognize); + + AggregationStateFactory GetAggregationStateFactory(bool isMatchRecognize); + + AggregationAccessor Accessor { get; } + + ExprAggregateNodeBase AggregationExpression { get; } + + void ValidateIntoTableCompatible(AggregationMethodFactory intoTableAgg); + + AggregationAgent AggregationStateAgent { get; } + + ExprEvaluator GetMethodAggregationEvaluator(bool join, EventType[] typesPerStream); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggregationMethodFactoryUtil.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationMethodFactoryUtil.cs new file mode 100755 index 000000000..0f0e4fac6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationMethodFactoryUtil.cs @@ -0,0 +1,102 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.agg.service +{ + public class AggregationMethodFactoryUtil + { + public static void ValidateAggregationType(AggregationMethodFactory requiredFactory, AggregationMethodFactory providedFactory) + { + if (!TypeHelper.IsSubclassOrImplementsInterface(providedFactory.GetType(), requiredFactory.GetType())) + { + throw new ExprValidationException("Not a '" + requiredFactory.AggregationExpression.AggregationFunctionName + "' aggregation"); + } + ExprAggregateNode aggNodeRequired = requiredFactory.AggregationExpression; + ExprAggregateNode aggNodeProvided = providedFactory.AggregationExpression; + if (aggNodeRequired.IsDistinct != aggNodeProvided.IsDistinct) + { + throw new ExprValidationException("The aggregation declares " + + (aggNodeRequired.IsDistinct ? "a" : "no") + + " distinct and provided is " + + (aggNodeProvided.IsDistinct ? "a" : "no") + + " distinct"); + } + } + + public static void ValidateAggregationInputType(Type requiredParam, Type providedParam) + { + var boxedRequired = requiredParam.GetBoxedType(); + var boxedProvided = providedParam.GetBoxedType(); + if (boxedRequired != boxedProvided && !TypeHelper.IsSubclassOrImplementsInterface(boxedProvided, boxedRequired)) + { + throw new ExprValidationException("The required parameter type is " + + requiredParam.GetTypeNameFullyQualPretty() + + " and provided is " + + providedParam.GetTypeNameFullyQualPretty()); + } + } + + public static void ValidateAggregationFilter(bool requireFilter, bool provideFilter) + { + if (requireFilter != provideFilter) + { + throw new ExprValidationException("The aggregation declares " + + (requireFilter ? "a" : "no") + + " filter expression and provided is " + + (provideFilter ? "a" : "no") + + " filter expression"); + } + } + + public static void ValidateAggregationUnbound(bool requiredHasDataWindows, bool providedHasDataWindows) + { + if (requiredHasDataWindows != providedHasDataWindows) + { + throw new ExprValidationException("The aggregation declares " + + (requiredHasDataWindows ? "use with data windows" : "unbound") + + " and provided is " + + (providedHasDataWindows ? "use with data windows" : "unbound")); + } + } + + public static void ValidateEventType(EventType requiredType, EventType providedType) + { + if (!EventTypeUtility.IsTypeOrSubTypeOf(providedType, requiredType)) + { + throw new ExprValidationException("The required event type is '" + + requiredType.Name + + "' and provided is '" + providedType.Name + "'"); + } + } + + public static void ValidateAggFuncName(string requiredName, string providedName) + { + if (!requiredName.ToLowerInvariant().Equals(providedName)) + { + throw new ExprValidationException("The required aggregation function name is '" + + requiredName + "' and provided is '" + providedName + "'"); + } + } + + public static void ValidateStreamNumZero(int streamNum) + { + if (streamNum != 0) + { + throw new ExprValidationException("The from-clause order requires the stream in position zero"); + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggregationMethodPairRow.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationMethodPairRow.cs new file mode 100755 index 000000000..470e79905 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationMethodPairRow.cs @@ -0,0 +1,65 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.aggregator; + +namespace com.espertech.esper.epl.agg.service +{ + /// A row in aggregation state. + public class AggregationMethodPairRow + { + private long _refcount; + private readonly AggregationMethod[] _methods; + private readonly AggregationState[] _states; + + /// Ctor. + /// number of items in state + /// aggregations + /// for first/last/window type access + public AggregationMethodPairRow(long refcount, AggregationMethod[] methods, AggregationState[] states) + { + _refcount = refcount; + _methods = methods; + _states = states; + } + + /// Returns number of data points. + /// data points + public long Refcount + { + get { return _refcount; } + } + + /// Returns aggregation state. + /// state + public AggregationMethod[] Methods + { + get { return _methods; } + } + + /// Increase number of data points by one. + public void IncreaseRefcount() + { + _refcount++; + } + + /// Decrease number of data points by one. + public void DecreaseRefcount() + { + _refcount--; + } + + /// Returns the states for first/last/window aggregation functions. + /// states + public AggregationState[] States + { + get { return _states; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggregationMethodRow.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationMethodRow.cs new file mode 100755 index 000000000..c45860098 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationMethodRow.cs @@ -0,0 +1,54 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.agg.aggregator; + +namespace com.espertech.esper.epl.agg.service +{ + /// A row in aggregation state. + public class AggregationMethodRow + { + private long _refcount; + private readonly AggregationMethod[] _methods; + + /// Ctor. + /// number of items in state + /// aggregations + public AggregationMethodRow(long refcount, AggregationMethod[] methods) + { + _refcount = refcount; + _methods = methods; + } + + /// Returns number of data points. + /// data points + public long Refcount + { + get { return _refcount; } + } + + /// Returns aggregation state. + /// state + public AggregationMethod[] Methods + { + get { return _methods; } + } + + /// Increase number of data points by one. + public void IncreaseRefcount() + { + _refcount++; + } + + /// Decrease number of data points by one. + public void DecreaseRefcount() + { + _refcount--; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggregationMethodRowAged.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationMethodRowAged.cs new file mode 100755 index 000000000..f0a97a160 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationMethodRowAged.cs @@ -0,0 +1,78 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.aggregator; + +namespace com.espertech.esper.epl.agg.service +{ + /// + /// A row in aggregation state, with aging information. + /// + public class AggregationMethodRowAged + { + private long _refcount; + private long _lastUpdateTime; + private readonly AggregationMethod[] _methods; + private readonly AggregationState[] _states; + + /// Ctor. + /// time of creation + /// number of items in state + /// aggregations + /// for first/last/window type access + public AggregationMethodRowAged(long refcount, long lastUpdateTime, AggregationMethod[] methods, AggregationState[] states) + { + _refcount = refcount; + _lastUpdateTime = lastUpdateTime; + _methods = methods; + _states = states; + } + + /// Returns number of data points. + /// data points + public long Refcount + { + get { return _refcount; } + } + + /// Returns last upd time. + /// time + public long LastUpdateTime + { + get { return _lastUpdateTime; } + set { _lastUpdateTime = value; } + } + + /// Returns aggregation state. + /// state + public AggregationMethod[] Methods + { + get { return _methods; } + } + + /// Increase number of data points by one. + public void IncreaseRefcount() + { + _refcount++; + } + + /// Decrease number of data points by one. + public void DecreaseRefcount() + { + _refcount--; + } + + /// Returns the states for first/last/window aggregation functions. + /// states + public AggregationState[] States + { + get { return _states; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggregationMultiFunctionAnalysisHelper.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationMultiFunctionAnalysisHelper.cs new file mode 100755 index 000000000..7f8616da6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationMultiFunctionAnalysisHelper.cs @@ -0,0 +1,95 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.agg.service +{ + public class AggregationMultiFunctionAnalysisHelper + { + // handle accessor aggregation (direct data window by-group access to properties) + public static AggregationMultiFunctionAnalysisResult AnalyzeAccessAggregations(IList aggregations) + { + var currentSlot = 0; + var accessProviderSlots = new ArrayDeque(); + var accessorPairs = new List(); + var stateFactories = new List(); + + foreach (var aggregation in aggregations) + { + var aggregateNode = aggregation.AggregationNode; + if (!aggregateNode.Factory.IsAccessAggregation) { + continue; + } + + var providerKey = aggregateNode.Factory.GetAggregationStateKey(false); + var existing = FindExisting(accessProviderSlots, providerKey, aggregateNode.OptionalLocalGroupBy); + + int slot; + if (existing == null) + { + accessProviderSlots.Add(new AggregationMFIdentifier(providerKey, aggregateNode.OptionalLocalGroupBy, currentSlot)); + slot = currentSlot++; + AggregationStateFactory providerFactory = aggregateNode.Factory.GetAggregationStateFactory(false); + stateFactories.Add(providerFactory); + } + else + { + slot = existing.Slot; + } + + var accessor = aggregateNode.Factory.Accessor; + accessorPairs.Add(new AggregationAccessorSlotPair(slot, accessor)); + } + + var pairs = accessorPairs.ToArray(); + var accessAggregations = stateFactories.ToArray(); + return new AggregationMultiFunctionAnalysisResult(pairs, accessAggregations); + } + + internal static AggregationMFIdentifier FindExisting(IEnumerable accessProviderSlots, AggregationStateKey providerKey, ExprAggregateLocalGroupByDesc optionalOver) + { + foreach (AggregationMFIdentifier ident in accessProviderSlots) + { + if (!Equals(providerKey, ident.AggregationStateKey)) { + continue; + } + if (optionalOver == null && ident.OptionalLocalGroupBy == null) { + return ident; + } + if (optionalOver != null && + ident.OptionalLocalGroupBy != null && + ExprNodeUtility.DeepEqualsIgnoreDupAndOrder(optionalOver.PartitionExpressions, ident.OptionalLocalGroupBy.PartitionExpressions)) { + return ident; + } + } + return null; + } + + internal class AggregationMFIdentifier + { + internal readonly AggregationStateKey AggregationStateKey; + internal readonly ExprAggregateLocalGroupByDesc OptionalLocalGroupBy; + internal readonly int Slot; + + internal AggregationMFIdentifier(AggregationStateKey aggregationStateKey, ExprAggregateLocalGroupByDesc optionalLocalGroupBy, int slot) + { + AggregationStateKey = aggregationStateKey; + OptionalLocalGroupBy = optionalLocalGroupBy; + Slot = slot; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggregationMultiFunctionAnalysisResult.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationMultiFunctionAnalysisResult.cs new file mode 100755 index 000000000..4d044d277 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationMultiFunctionAnalysisResult.cs @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.agg.access; + +namespace com.espertech.esper.epl.agg.service +{ + public class AggregationMultiFunctionAnalysisResult + { + public AggregationMultiFunctionAnalysisResult(AggregationAccessorSlotPair[] accessorPairs, AggregationStateFactory[] stateFactories) + { + AccessorPairs = accessorPairs; + StateFactories = stateFactories; + } + + public AggregationAccessorSlotPair[] AccessorPairs { get; private set; } + + public AggregationStateFactory[] StateFactories { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggregationResultFuture.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationResultFuture.cs new file mode 100755 index 000000000..7d248a48d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationResultFuture.cs @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.agg.service +{ + /// + /// Interface for use by aggregate expression nodes representing aggregate functions such as 'sum' or 'avg' to + /// use to obtain the current value for the function at time of expression evaluation. + /// + public interface AggregationResultFuture + { + /// + /// Returns current aggregation state, for use by expression node representing an aggregation function. + /// + /// is assigned to the aggregation expression node and passed as an column (index) into a row + /// the context partition id + /// + /// + /// current aggragation state + /// + object GetValue(int column, int agentInstanceId, EvaluateParams evaluateParams); + + ICollection GetCollectionOfEvents(int column, EvaluateParams evaluateParams); + + EventBean GetEventBean(int column, EvaluateParams evaluateParams); + + Object GetGroupKey(int agentInstanceId); + + ICollection GetGroupKeys(ExprEvaluatorContext exprEvaluatorContext); + + ICollection GetCollectionScalar(int column, EvaluateParams evaluateParams); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggregationRowPair.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationRowPair.cs new file mode 100755 index 000000000..885e097cf --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationRowPair.cs @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.aggregator; + +namespace com.espertech.esper.epl.agg.service +{ + /// Pair of aggregation methods and states (first/last/window) data window representations. + public class AggregationRowPair + { + /// Ctor. + /// aggregation methods/state + /// access is data window representations + public AggregationRowPair(AggregationMethod[] methods, AggregationState[] states) + { + Methods = methods; + States = states; + } + + /// Returns aggregation methods. + /// aggregation methods + public AggregationMethod[] Methods { get; private set; } + + /// Returns states to data window state. + /// states + public AggregationState[] States { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggregationRowRemovedCallback.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationRowRemovedCallback.cs new file mode 100755 index 000000000..458d05b44 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationRowRemovedCallback.cs @@ -0,0 +1,17 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.agg.service +{ + public interface AggregationRowRemovedCallback + { + void Removed(Object groupRowKey); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggregationService.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationService.cs new file mode 100755 index 000000000..a8d5d5afd --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationService.cs @@ -0,0 +1,62 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.agg.service +{ + /// + /// Service for maintaining aggregation state. Processes events entering (a window, a join etc,) + /// and events leaving. Answers questions about current aggregation state for a given row. + /// + public interface AggregationService : AggregationResultFuture, StopCallback + { + /// + /// Apply events as entering a window (new events). + /// + /// events for each stream entering window + /// can be null if grouping without keys is desired, else the keys or array of keys to use for grouping, each distinct key value results in a new row of aggregation state. + /// context for expression evaluatiom + void ApplyEnter(EventBean[] eventsPerStream, Object optionalGroupKeyPerRow, ExprEvaluatorContext exprEvaluatorContext); + + /// + /// Apply events as leaving a window (old events). + /// + /// events for each stream entering window + /// can be null if grouping without keys is desired, else the keys or array of keys to use for grouping, each distinct key value results in a new row of aggregation state. + /// context for expression evaluatiom + void ApplyLeave(EventBean[] eventsPerStream, Object optionalGroupKeyPerRow, ExprEvaluatorContext exprEvaluatorContext); + + /// + /// Set the current aggregation state row - for use when evaluation nodes are asked to evaluate. + /// + /// single key identifying the row of aggregation states + /// context partition id + /// The rollup level. + void SetCurrentAccess(Object groupKey, int agentInstanceId, AggregationGroupByRollupLevel rollupLevel); + + /// + /// Clear current aggregation state. + /// + /// The expr evaluator context. + void ClearResults(ExprEvaluatorContext exprEvaluatorContext); + + void SetRemovedCallback(AggregationRowRemovedCallback callback); + + void Accept(AggregationServiceVisitor visitor); + void AcceptGroupDetail(AggregationServiceVisitorWGroupDetail visitor); + bool IsGrouped { get; } + + AggregationService GetContextPartitionAggregationService(int agentInstanceId); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceAggExpressionDesc.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceAggExpressionDesc.cs new file mode 100755 index 000000000..2edeec8f7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceAggExpressionDesc.cs @@ -0,0 +1,69 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.baseagg; + +namespace com.espertech.esper.epl.agg.service +{ + public class AggregationServiceAggExpressionDesc + { + /// Ctor. + /// expression + /// method factory + public AggregationServiceAggExpressionDesc(ExprAggregateNode aggregationNode, AggregationMethodFactory factory) + { + AggregationNode = aggregationNode; + Factory = factory; + } + + /// Returns the equivalent aggregation functions. + /// list of agg nodes + public IList EquivalentNodes { get; private set; } + + /// Returns the expression. + /// expression + public ExprAggregateNode AggregationNode { get; private set; } + + /// Returns the method factory. + /// factory + public AggregationMethodFactory Factory { get; private set; } + + /// Returns the column number assigned. + /// column number + public int? ColumnNum { get; set; } + + /// Add an equivalent aggregation function node + /// node to add + public void AddEquivalent(ExprAggregateNode aggNodeToAdd) + { + if (EquivalentNodes == null) { + EquivalentNodes = new List(); + } + EquivalentNodes.Add(aggNodeToAdd); + } + + /// Assigns a future to the expression + /// the future + public void AssignFuture(AggregationResultFuture service) + { + var columnNum = ColumnNum.GetValueOrDefault(); + + AggregationNode.SetAggregationResultFuture(service, columnNum); + if (EquivalentNodes == null) { + return; + } + foreach (ExprAggregateNode equivalentAggNode in EquivalentNodes) + { + equivalentAggNode.SetAggregationResultFuture(service, columnNum); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceBase.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceBase.cs new file mode 100755 index 000000000..b0aaaf3e0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceBase.cs @@ -0,0 +1,55 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.agg.service +{ + /// + /// All aggregation services require evaluation nodes which supply the value to be + /// aggregated (summed, averaged, etc.) and aggregation state factories to make new + /// aggregation states. + /// + public abstract class AggregationServiceBase : AggregationService + { + /// Ctor. + /// are the child node of each aggregation function used for computing the value to be aggregated + protected AggregationServiceBase(ExprEvaluator[] evaluators) + { + Evaluators = evaluators; + } + + public ExprEvaluator[] Evaluators { get; protected set; } + + public abstract AggregationService GetContextPartitionAggregationService(int agentInstanceId); + + public abstract object GetValue(int column, int agentInstanceId, EvaluateParams evaluateParams); + public abstract ICollection GetCollectionOfEvents(int column, EvaluateParams evaluateParams); + public abstract EventBean GetEventBean(int column, EvaluateParams evaluateParams); + public abstract ICollection GetCollectionScalar(int column, EvaluateParams evaluateParams); + + public abstract void ApplyEnter(EventBean[] eventsPerStream, object optionalGroupKeyPerRow, ExprEvaluatorContext exprEvaluatorContext); + public abstract void ApplyLeave(EventBean[] eventsPerStream, object optionalGroupKeyPerRow, ExprEvaluatorContext exprEvaluatorContext); + public abstract void SetCurrentAccess(object groupKey, int agentInstanceId, AggregationGroupByRollupLevel rollupLevel); + public abstract void ClearResults(ExprEvaluatorContext exprEvaluatorContext); + public abstract void SetRemovedCallback(AggregationRowRemovedCallback callback); + + public abstract object GetGroupKey(int agentInstanceId); + public abstract ICollection GetGroupKeys(ExprEvaluatorContext exprEvaluatorContext); + public abstract void Accept(AggregationServiceVisitor visitor); + public abstract void AcceptGroupDetail(AggregationServiceVisitorWGroupDetail visitor); + public abstract bool IsGrouped { get; } + + public abstract void Stop(); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceBaseGrouped.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceBaseGrouped.cs new file mode 100755 index 000000000..cd0eb6b84 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceBaseGrouped.cs @@ -0,0 +1,70 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.agg.service +{ + /// + /// All aggregation services require evaluation nodes which supply the value to be + /// aggregated (summed, averaged, etc.) and aggregation state factories to make new + /// aggregation states. + /// + public abstract class AggregationServiceBaseGrouped : AggregationService + { + /// Evaluation nodes under. + protected internal ExprEvaluator[] Evaluators; + + /// Aggregation states and factories. + protected internal AggregationMethodFactory[] Aggregators; + + /// + /// Ctor. + /// + /// are the child node of each aggregation function used for computing the value to be aggregated + /// aggregation states/factories + protected AggregationServiceBaseGrouped(ExprEvaluator[] evaluators, AggregationMethodFactory[] aggregators) + { + Evaluators = evaluators; + Aggregators = aggregators; + + if (evaluators.Length != aggregators.Length) + { + throw new ArgumentException("Expected the same number of evaluates as computer prototypes"); + } + } + + public void Stop() + { + } + + public AggregationService GetContextPartitionAggregationService(int agentInstanceId) + { + return this; + } + + public abstract object GetValue(int column, int agentInstanceId, EvaluateParams evaluateParams); + public abstract ICollection GetCollectionOfEvents(int column, EvaluateParams evaluateParams); + public abstract ICollection GetCollectionScalar(int column, EvaluateParams evaluateParams); + public abstract EventBean GetEventBean(int column, EvaluateParams evaluateParams); + public abstract object GetGroupKey(int agentInstanceId); + public abstract ICollection GetGroupKeys(ExprEvaluatorContext exprEvaluatorContext); + public abstract void ApplyEnter(EventBean[] eventsPerStream, object optionalGroupKeyPerRow, ExprEvaluatorContext exprEvaluatorContext); + public abstract void ApplyLeave(EventBean[] eventsPerStream, object optionalGroupKeyPerRow, ExprEvaluatorContext exprEvaluatorContext); + public abstract void SetCurrentAccess(object groupKey, int agentInstanceId, AggregationGroupByRollupLevel rollupLevel); + public abstract void ClearResults(ExprEvaluatorContext exprEvaluatorContext); + public abstract void SetRemovedCallback(AggregationRowRemovedCallback callback); + public abstract void Accept(AggregationServiceVisitor visitor); + public abstract void AcceptGroupDetail(AggregationServiceVisitorWGroupDetail visitor); + public abstract bool IsGrouped { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceBaseUngrouped.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceBaseUngrouped.cs new file mode 100755 index 000000000..c7a2e12b6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceBaseUngrouped.cs @@ -0,0 +1,81 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.agg.aggregator; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.agg.service +{ + /// + /// All aggregation services require evaluation nodes which supply the value to be aggregated + /// (summed, averaged, etc.) and aggregation state factories to make new aggregation states. + /// + public abstract class AggregationServiceBaseUngrouped : AggregationService + { + /// Evaluation nodes under. + protected ExprEvaluator[] Evaluators; + + /// Aggregation states. + protected AggregationMethod[] Aggregators; + + protected AggregationMethodFactory[] AggregatorFactories; + protected AggregationStateFactory[] AccessAggregations; + + /// + /// Ctor. + /// + /// are the child node of each aggregation function used for computing the value to be aggregated + /// aggregation states/factories + /// The aggregator factories. + /// The access aggregations. + protected AggregationServiceBaseUngrouped( + ExprEvaluator[] evaluators, + AggregationMethod[] aggregators, + AggregationMethodFactory[] aggregatorFactories, + AggregationStateFactory[] accessAggregations) + { + Evaluators = evaluators; + Aggregators = aggregators; + AggregatorFactories = aggregatorFactories; + AccessAggregations = accessAggregations; + + if (evaluators.Length != aggregators.Length) + { + throw new ArgumentException("Expected the same number of evaluates as aggregation methods"); + } + } + + public void Stop() + { + } + + public AggregationService GetContextPartitionAggregationService(int agentInstanceId) + { + return this; + } + + public abstract object GetValue(int column, int agentInstanceId, EvaluateParams evaluateParams); + public abstract ICollection GetCollectionOfEvents(int column, EvaluateParams evaluateParams); + public abstract ICollection GetCollectionScalar(int column, EvaluateParams evaluateParams); + public abstract EventBean GetEventBean(int column, EvaluateParams evaluateParams); + public abstract object GetGroupKey(int agentInstanceId); + public abstract ICollection GetGroupKeys(ExprEvaluatorContext exprEvaluatorContext); + public abstract void ApplyEnter(EventBean[] eventsPerStream, object optionalGroupKeyPerRow, ExprEvaluatorContext exprEvaluatorContext); + public abstract void ApplyLeave(EventBean[] eventsPerStream, object optionalGroupKeyPerRow, ExprEvaluatorContext exprEvaluatorContext); + public abstract void SetCurrentAccess(object groupKey, int agentInstanceId, AggregationGroupByRollupLevel rollupLevel); + public abstract void ClearResults(ExprEvaluatorContext exprEvaluatorContext); + public abstract void SetRemovedCallback(AggregationRowRemovedCallback callback); + public abstract void Accept(AggregationServiceVisitor visitor); + public abstract void AcceptGroupDetail(AggregationServiceVisitorWGroupDetail visitor); + public abstract bool IsGrouped { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceFactory.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceFactory.cs new file mode 100755 index 000000000..7b51b2aac --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceFactory.cs @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.core; + +namespace com.espertech.esper.epl.agg.service +{ + /// + /// Factory for aggregation service instances. + /// + /// Consolidates aggregation nodes such that result futures point to a single instance + /// and no re-evaluation of the same result occurs. + /// + /// + public interface AggregationServiceFactory + { + AggregationService MakeService(AgentInstanceContext agentInstanceContext, EngineImportService engineImportService, bool isSubquery, int? subqueryNumber); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceFactoryBase.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceFactoryBase.cs new file mode 100755 index 000000000..4739ae78c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceFactoryBase.cs @@ -0,0 +1,54 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.agg.service +{ + /// + /// All aggregation services require evaluation nodes which supply the value to be aggregated (summed, averaged, etc.) + /// and aggregation state factories to make new aggregation states. + /// + public abstract class AggregationServiceFactoryBase : AggregationServiceFactory + { + /// Evaluation nodes under. + protected ExprEvaluator[] Evaluators; + + /// Prototype aggregation states and factories. + protected AggregationMethodFactory[] Aggregators; + + /// + /// Ctor. + /// + /// - are the child node of each aggregation function used for computing the value to be aggregated + /// - aggregation states/factories + protected AggregationServiceFactoryBase(ExprEvaluator[] evaluators, AggregationMethodFactory[] aggregators) + { + Evaluators = evaluators; + Aggregators = aggregators; + + if (evaluators.Length != aggregators.Length) + { + throw new ArgumentException("Expected the same number of evaluates as computer prototypes"); + } + } + + public abstract AggregationService MakeService( + AgentInstanceContext agentInstanceContext, + EngineImportService engineImportService, + bool isSubquery, + int? subqueryNumber); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceFactoryDesc.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceFactoryDesc.cs new file mode 100755 index 000000000..503d34e3d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceFactoryDesc.cs @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.baseagg; + +namespace com.espertech.esper.epl.agg.service +{ + public class AggregationServiceFactoryDesc + { + public AggregationServiceFactoryDesc( + AggregationServiceFactory aggregationServiceFactory, + IList expressions, + IList groupKeyExpressions) + { + AggregationServiceFactory = aggregationServiceFactory; + Expressions = expressions; + GroupKeyExpressions = groupKeyExpressions; + } + + public AggregationServiceFactory AggregationServiceFactory { get; private set; } + + public IList Expressions { get; private set; } + + public IList GroupKeyExpressions { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceFactoryFactory.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceFactoryFactory.cs new file mode 100755 index 000000000..d98017133 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceFactoryFactory.cs @@ -0,0 +1,721 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.client.annotation; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.util; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.declexpr; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.methodagg; +using com.espertech.esper.epl.expression.table; +using com.espertech.esper.epl.expression.visitor; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.epl.util; +using com.espertech.esper.epl.variable; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.agg.service +{ + /// + /// Factory for aggregation service instances. + /// + /// Consolidates aggregation nodes such that result futures point to a single instance and + /// no re-evaluation of the same result occurs. + /// + /// + public class AggregationServiceFactoryFactory + { + /// + /// Produces an aggregation service for use with match-recognice. + /// + /// number of streams + /// measure nodes + /// type information + /// for validation errors + /// service + public static AggregationServiceMatchRecognizeFactoryDesc GetServiceMatchRecognize( + int numStreams, + IDictionary> measureExprNodesPerStream, + EventType[] typesPerStream) + { + var equivalencyListPerStream = new OrderedDictionary>(); + + foreach (var entry in measureExprNodesPerStream) + { + var equivalencyList = new List(); + equivalencyListPerStream.Put(entry.Key, equivalencyList); + foreach (ExprAggregateNode selectAggNode in entry.Value) + { + AddEquivalent(selectAggNode, equivalencyList); + } + } + + var aggregatorsPerStream = new LinkedHashMap(); + var evaluatorsPerStream = new Dictionary(); + + foreach (var equivalencyPerStream in equivalencyListPerStream) + { + int index = 0; + int stream = equivalencyPerStream.Key; + + var aggregators = new AggregationMethodFactory[equivalencyPerStream.Value.Count]; + aggregatorsPerStream.Put(stream, aggregators); + + var evaluators = new ExprEvaluator[equivalencyPerStream.Value.Count]; + evaluatorsPerStream.Put(stream, evaluators); + + foreach (AggregationServiceAggExpressionDesc aggregation in equivalencyPerStream.Value) + { + ExprAggregateNode aggregateNode = aggregation.AggregationNode; + if (aggregateNode.ChildNodes.Count > 1) + { + evaluators[index] = ExprMethodAggUtil.GetMultiNodeEvaluator( + aggregateNode.ChildNodes, typesPerStream.Length > 1, typesPerStream); + } + else if (aggregateNode.ChildNodes.Count > 0) + { + // Use the evaluation node under the aggregation node to obtain the aggregation value + evaluators[index] = aggregateNode.ChildNodes[0].ExprEvaluator; + } + else + { + // For aggregation that doesn't evaluate any particular sub-expression, return null on evaluation + evaluators[index] = new ProxyExprEvaluator + { + ProcEvaluate = eventParams => null, + ProcReturnType = () => null + }; + } + + aggregators[index] = aggregateNode.Factory; + index++; + } + } + + // Assign a column number to each aggregation node. The regular aggregation goes first followed by access-aggregation. + int columnNumber = 0; + var allExpressions = new List(); + foreach (var equivalencyPerStream in equivalencyListPerStream) + { + foreach (AggregationServiceAggExpressionDesc entry in equivalencyPerStream.Value) + { + entry.ColumnNum = columnNumber++; + } + allExpressions.AddAll(equivalencyPerStream.Value); + } + + var factory = new AggregationServiceMatchRecognizeFactoryImpl( + numStreams, aggregatorsPerStream, evaluatorsPerStream); + return new AggregationServiceMatchRecognizeFactoryDesc(factory, allExpressions); + } + + public static AggregationServiceFactoryDesc GetService( + IList selectAggregateExprNodes, + IDictionary selectClauseNamedNodes, + IList declaredExpressions, + ExprNode[] groupByNodes, + IList havingAggregateExprNodes, + IList orderByAggregateExprNodes, + IList groupKeyExpressions, + bool hasGroupByClause, + Attribute[] annotations, + VariableService variableService, + bool isJoin, + bool isDisallowNoReclaim, + ExprNode whereClause, + ExprNode havingClause, + AggregationServiceFactoryService factoryService, + EventType[] typesPerStream, + AggregationGroupByRollupDesc groupByRollupDesc, + string optionalContextName, + IntoTableSpec intoTableSpec, + TableService tableService, + bool isUnidirectional, + bool isFireAndForget, + bool isOnSelect, + EngineImportService engineImportService) + { + // No aggregates used, we do not need this service + if ((selectAggregateExprNodes.IsEmpty()) && (havingAggregateExprNodes.IsEmpty())) + { + if (intoTableSpec != null) + { + throw new ExprValidationException("Into-table requires at least one aggregation function"); + } + return new AggregationServiceFactoryDesc( + factoryService.GetNullAggregationService(), + Collections.GetEmptyList(), + Collections.GetEmptyList()); + } + + // Validate the absence of "prev" function in where-clause: + // Since the "previous" function does not post remove stream results, disallow when used with aggregations. + if ((whereClause != null) || (havingClause != null)) + { + var visitor = new ExprNodePreviousVisitorWParent(); + if (whereClause != null) + { + whereClause.Accept(visitor); + } + if (havingClause != null) + { + havingClause.Accept(visitor); + } + if ((visitor.Previous != null) && (!visitor.Previous.IsEmpty())) + { + string funcname = visitor.Previous[0].Second.PreviousType.ToString().ToLowerInvariant(); + throw new ExprValidationException( + "The '" + funcname + + "' function may not occur in the where-clause or having-clause of a statement with aggregations as 'previous' does not provide remove stream data; Use the 'first','last','window' or 'count' aggregation functions instead"); + } + } + + // Compile a map of aggregation nodes and equivalent-to aggregation nodes. + // Equivalent-to functions are for example "select Sum(a*b), 5*Sum(a*b)". + // Reducing the total number of aggregation functions. + var aggregations = new List(); + foreach (ExprAggregateNode selectAggNode in selectAggregateExprNodes) + { + AddEquivalent(selectAggNode, aggregations); + } + foreach (ExprAggregateNode havingAggNode in havingAggregateExprNodes) + { + AddEquivalent(havingAggNode, aggregations); + } + foreach (ExprAggregateNode orderByAggNode in orderByAggregateExprNodes) + { + AddEquivalent(orderByAggNode, aggregations); + } + + // Construct a list of evaluation node for the aggregation functions (regular agg). + // For example "Sum(2 * 3)" would make the sum an evaluation node. + var methodAggEvaluatorsList = new List(); + foreach (AggregationServiceAggExpressionDesc aggregation in aggregations) + { + ExprAggregateNode aggregateNode = aggregation.AggregationNode; + if (!aggregateNode.Factory.IsAccessAggregation) + { + ExprEvaluator evaluator = + aggregateNode.Factory.GetMethodAggregationEvaluator(typesPerStream.Length > 1, typesPerStream); + methodAggEvaluatorsList.Add(evaluator); + } + } + + // determine local group-by, report when hook provided + AggregationGroupByLocalGroupDesc localGroupDesc = AnalyzeLocalGroupBy( + aggregations, groupByNodes, groupByRollupDesc, intoTableSpec); + + // determine binding + AggregationServiceFactory serviceFactory; + if (intoTableSpec != null) + { + // obtain metadata + TableMetadata metadata = tableService.GetTableMetadata(intoTableSpec.Name); + if (metadata == null) + { + throw new ExprValidationException( + "Invalid into-table clause: Failed to find table by name '" + intoTableSpec.Name + "'"); + } + + EPLValidationUtil.ValidateContextName( + true, intoTableSpec.Name, metadata.ContextName, optionalContextName, false); + + // validate group keys + Type[] groupByTypes = ExprNodeUtility.GetExprResultTypes(groupByNodes); + ExprTableNodeUtil.ValidateExpressions( + intoTableSpec.Name, groupByTypes, "group-by", groupByNodes, + metadata.KeyTypes, "group-by"); + + // determine how this binds to existing aggregations, assign column numbers + BindingMatchResult bindingMatchResult = MatchBindingsAssignColumnNumbers( + intoTableSpec, metadata, aggregations, selectClauseNamedNodes, methodAggEvaluatorsList, + declaredExpressions); + + // return factory + if (!hasGroupByClause) + { + serviceFactory = factoryService.GetNoGroupWBinding( + bindingMatchResult.Accessors, isJoin, bindingMatchResult.MethodPairs, intoTableSpec.Name, + bindingMatchResult.TargetStates, bindingMatchResult.AccessStateExpr, bindingMatchResult.Agents); + } + else + { + serviceFactory = factoryService.GetGroupWBinding( + metadata, bindingMatchResult.MethodPairs, bindingMatchResult.Accessors, isJoin, intoTableSpec, + bindingMatchResult.TargetStates, bindingMatchResult.AccessStateExpr, bindingMatchResult.Agents, + groupByRollupDesc); + } + return new AggregationServiceFactoryDesc(serviceFactory, aggregations, groupKeyExpressions); + } + + // Assign a column number to each aggregation node. The regular aggregation goes first followed by access-aggregation. + int columnNumber = 0; + foreach (AggregationServiceAggExpressionDesc entry in aggregations) + { + if (!entry.Factory.IsAccessAggregation) + { + entry.ColumnNum = columnNumber++; + } + } + foreach (AggregationServiceAggExpressionDesc entry in aggregations) + { + if (entry.Factory.IsAccessAggregation) + { + entry.ColumnNum = columnNumber++; + } + } + + // determine method aggregation factories and Evaluators(non-access) + ExprEvaluator[] methodAggEvaluators = methodAggEvaluatorsList.ToArray(); + var methodAggFactories = new AggregationMethodFactory[methodAggEvaluators.Length]; + int count = 0; + foreach (AggregationServiceAggExpressionDesc aggregation in aggregations) + { + ExprAggregateNode aggregateNode = aggregation.AggregationNode; + if (!aggregateNode.Factory.IsAccessAggregation) + { + methodAggFactories[count] = aggregateNode.Factory; + count++; + } + } + + // handle access aggregations + AggregationMultiFunctionAnalysisResult multiFunctionAggPlan = + AggregationMultiFunctionAnalysisHelper.AnalyzeAccessAggregations(aggregations); + AggregationAccessorSlotPair[] accessorPairs = multiFunctionAggPlan.AccessorPairs; + AggregationStateFactory[] accessAggregations = multiFunctionAggPlan.StateFactories; + + // analyze local group by + AggregationLocalGroupByPlan localGroupByPlan = null; + if (localGroupDesc != null) + { + localGroupByPlan = AggregationGroupByLocalGroupByAnalyzer.Analyze( + methodAggEvaluators, methodAggFactories, accessAggregations, localGroupDesc, groupByNodes, + accessorPairs); + try + { + var hook = + (AggregationLocalLevelHook) + TypeHelper.GetAnnotationHook( + annotations, HookType.INTERNAL_AGGLOCALLEVEL, typeof (AggregationLocalLevelHook), + engineImportService); + if (hook != null) + { + hook.Planned(localGroupDesc, localGroupByPlan); + } + } + catch (ExprValidationException) + { + throw new EPException("Failed to obtain hook for " + HookType.INTERNAL_AGGLOCALLEVEL); + } + } + + // Handle without a group-by clause: we group all into the same pot + if (!hasGroupByClause) + { + if (localGroupByPlan != null) + { + serviceFactory = factoryService.GetNoGroupLocalGroupBy( + isJoin, localGroupByPlan, isUnidirectional, isFireAndForget, isOnSelect); + } + else if ((methodAggEvaluators.Length > 0) && (accessorPairs.Length == 0)) + { + serviceFactory = factoryService.GetNoGroupNoAccess( + methodAggEvaluators, methodAggFactories, isUnidirectional, isFireAndForget, isOnSelect); + } + else if ((methodAggEvaluators.Length == 0) && (accessorPairs.Length > 0)) + { + serviceFactory = factoryService.GetNoGroupAccessOnly( + accessorPairs, accessAggregations, isJoin, isUnidirectional, isFireAndForget, isOnSelect); + } + else + { + serviceFactory = factoryService.GetNoGroupAccessMixed( + methodAggEvaluators, methodAggFactories, accessorPairs, accessAggregations, isJoin, + isUnidirectional, isFireAndForget, isOnSelect); + } + } + else + { + bool hasNoReclaim = HintEnum.DISABLE_RECLAIM_GROUP.GetHint(annotations) != null; + HintAttribute reclaimGroupAged = HintEnum.RECLAIM_GROUP_AGED.GetHint(annotations); + HintAttribute reclaimGroupFrequency = HintEnum.RECLAIM_GROUP_AGED.GetHint(annotations); + if (localGroupByPlan != null) + { + serviceFactory = factoryService.GetGroupLocalGroupBy( + isJoin, localGroupByPlan, isUnidirectional, isFireAndForget, isOnSelect); + } + else + { + if (!isDisallowNoReclaim && hasNoReclaim) + { + if (groupByRollupDesc != null) + { + throw GetRollupReclaimEx(); + } + if ((methodAggEvaluators.Length > 0) && (accessorPairs.Length == 0)) + { + serviceFactory = factoryService.GetGroupedNoReclaimNoAccess( + groupByNodes, methodAggEvaluators, methodAggFactories, isUnidirectional, isFireAndForget, + isOnSelect); + } + else if ((methodAggEvaluators.Length == 0) && (accessorPairs.Length > 0)) + { + serviceFactory = factoryService.GetGroupNoReclaimAccessOnly( + groupByNodes, accessorPairs, accessAggregations, isJoin, isUnidirectional, + isFireAndForget, isOnSelect); + } + else + { + serviceFactory = factoryService.GetGroupNoReclaimMixed( + groupByNodes, methodAggEvaluators, methodAggFactories, accessorPairs, accessAggregations, + isJoin, isUnidirectional, isFireAndForget, isOnSelect); + } + } + else if (!isDisallowNoReclaim && reclaimGroupAged != null) + { + if (groupByRollupDesc != null) + { + throw GetRollupReclaimEx(); + } + serviceFactory = factoryService.GetGroupReclaimAged( + groupByNodes, methodAggEvaluators, methodAggFactories, reclaimGroupAged, + reclaimGroupFrequency, variableService, accessorPairs, accessAggregations, isJoin, + optionalContextName, isUnidirectional, isFireAndForget, isOnSelect); + } + else if (groupByRollupDesc != null) + { + serviceFactory = factoryService.GetGroupReclaimMixableRollup( + groupByNodes, groupByRollupDesc, methodAggEvaluators, methodAggFactories, accessorPairs, + accessAggregations, isJoin, groupByRollupDesc, isUnidirectional, isFireAndForget, isOnSelect); + } + else + { + if ((methodAggEvaluators.Length > 0) && (accessorPairs.Length == 0)) + { + serviceFactory = factoryService.GetGroupReclaimNoAccess( + groupByNodes, methodAggEvaluators, methodAggFactories, accessorPairs, accessAggregations, + isJoin, isUnidirectional, isFireAndForget, isOnSelect); + } + else + { + serviceFactory = factoryService.GetGroupReclaimMixable( + groupByNodes, methodAggEvaluators, methodAggFactories, accessorPairs, accessAggregations, + isJoin, isUnidirectional, isFireAndForget, isOnSelect); + } + } + } + } + + return new AggregationServiceFactoryDesc(serviceFactory, aggregations, groupKeyExpressions); + } + + private static AggregationGroupByLocalGroupDesc AnalyzeLocalGroupBy( + IList aggregations, + ExprNode[] groupByNodes, + AggregationGroupByRollupDesc groupByRollupDesc, + IntoTableSpec intoTableSpec) + { + bool hasOver = false; + foreach (AggregationServiceAggExpressionDesc desc in aggregations) + { + if (desc.AggregationNode.OptionalLocalGroupBy != null) + { + hasOver = true; + break; + } + } + if (!hasOver) + { + return null; + } + if (groupByRollupDesc != null) + { + throw new ExprValidationException("Roll-up and group-by parameters cannot be combined"); + } + if (intoTableSpec != null) + { + throw new ExprValidationException("Into-table and group-by parameters cannot be combined"); + } + + var partitions = new List(); + foreach (AggregationServiceAggExpressionDesc desc in aggregations) + { + ExprAggregateLocalGroupByDesc localGroupBy = desc.AggregationNode.OptionalLocalGroupBy; + + IList partitionExpressions = localGroupBy == null + ? groupByNodes + : localGroupBy.PartitionExpressions; + IList found = FindPartition(partitions, partitionExpressions); + if (found == null) + { + found = new List(); + var level = new AggregationGroupByLocalGroupLevel(partitionExpressions, found); + partitions.Add(level); + } + found.Add(desc); + } + + // check single group-by partition and it matches the group-by clause + if (partitions.Count == 1 && + ExprNodeUtility.DeepEqualsIgnoreDupAndOrder(partitions[0].PartitionExpr, groupByNodes)) + { + return null; + } + return new AggregationGroupByLocalGroupDesc(aggregations.Count, partitions.ToArray()); + } + + private static IList FindPartition( + IList partitions, + IList partitionExpressions) + { + foreach (AggregationGroupByLocalGroupLevel level in partitions) + { + if (ExprNodeUtility.DeepEqualsIgnoreDupAndOrder(level.PartitionExpr, partitionExpressions)) + { + return level.Expressions; + } + } + return null; + } + + private static BindingMatchResult MatchBindingsAssignColumnNumbers( + IntoTableSpec bindings, + TableMetadata metadata, + IList aggregations, + IDictionary selectClauseNamedNodes, + IList methodAggEvaluatorsList, + IList declaredExpressions) + { + var methodAggs = new LinkedHashMap(); + var accessAggs = new LinkedHashMap(); + foreach (AggregationServiceAggExpressionDesc aggDesc in aggregations) + { + // determine assigned name + string columnName = FindColumnNameForAggregation( + selectClauseNamedNodes, declaredExpressions, aggDesc.AggregationNode); + if (columnName == null) + { + throw new ExprValidationException( + "Failed to find an expression among the select-clause expressions for expression '" + + ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(aggDesc.AggregationNode) + "'"); + } + + // determine binding metadata + var columnMetadata = (TableMetadataColumnAggregation) metadata.TableColumns.Get(columnName); + if (columnMetadata == null) + { + throw new ExprValidationException( + "Failed to find name '" + columnName + "' among the columns for table '" + bindings.Name + "'"); + } + + // validate compatible + ValidateIntoTableCompatible(bindings.Name, columnName, columnMetadata, aggDesc); + + if (!columnMetadata.Factory.IsAccessAggregation) + { + methodAggs.Put(aggDesc, columnMetadata); + } + else + { + accessAggs.Put(aggDesc, columnMetadata); + } + } + + // handle method-aggs + var methodPairs = new TableColumnMethodPair[methodAggEvaluatorsList.Count]; + int methodIndex = -1; + foreach (var methodEntry in methodAggs) + { + methodIndex++; + int targetIndex = methodEntry.Value.MethodOffset; + methodPairs[methodIndex] = new TableColumnMethodPair( + methodAggEvaluatorsList[methodIndex], targetIndex, methodEntry.Key.AggregationNode); + methodEntry.Key.ColumnNum = targetIndex; + } + + // handle access-aggs + var accessSlots = new LinkedHashMap(); + var accessReadPairs = new List(); + int accessIndex = -1; + var agents = new List(); + foreach (var accessEntry in accessAggs) + { + accessIndex++; + int slot = accessEntry.Value.AccessAccessorSlotPair.Slot; + AggregationMethodFactory aggregationMethodFactory = accessEntry.Key.Factory; + AggregationAccessor accessor = aggregationMethodFactory.Accessor; + accessSlots.Put(slot, accessEntry.Key.AggregationNode); + accessReadPairs.Add(new AggregationAccessorSlotPair(slot, accessor)); + accessEntry.Key.ColumnNum = metadata.NumberMethodAggregations + accessIndex; + agents.Add(aggregationMethodFactory.AggregationStateAgent); + } + AggregationAgent[] agentArr = agents.ToArray(); + AggregationAccessorSlotPair[] accessReads = accessReadPairs.ToArray(); + + var targetStates = new int[accessSlots.Count]; + var accessStateExpr = new ExprNode[accessSlots.Count]; + int count = 0; + foreach (var entry in accessSlots) + { + targetStates[count] = entry.Key; + accessStateExpr[count] = entry.Value; + count++; + } + + return new BindingMatchResult(methodPairs, accessReads, targetStates, accessStateExpr, agentArr); + } + + private static string FindColumnNameForAggregation( + IDictionary selectClauseNamedNodes, + IList declaredExpressions, + ExprAggregateNode aggregationNode) + { + if (selectClauseNamedNodes.ContainsKey(aggregationNode)) + { + return selectClauseNamedNodes.Get(aggregationNode); + } + + foreach (ExprDeclaredNode node in declaredExpressions) + { + if (node.Body == aggregationNode) + { + return node.Prototype.Name; + } + } + + return null; + } + + private static void ValidateIntoTableCompatible( + string tableName, + string columnName, + TableMetadataColumnAggregation columnMetadata, + AggregationServiceAggExpressionDesc aggDesc) + { + AggregationMethodFactory factoryProvided = aggDesc.Factory; + AggregationMethodFactory factoryRequired = columnMetadata.Factory; + + try + { + factoryRequired.ValidateIntoTableCompatible(factoryProvided); + } + catch (ExprValidationException ex) + { + string text = GetMessage( + tableName, columnName, factoryRequired.AggregationExpression, factoryProvided.AggregationExpression); + throw new ExprValidationException(text + ": " + ex.Message, ex); + } + } + + private static string GetMessage( + string tableName, + string columnName, + ExprAggregateNodeBase aggregationRequired, + ExprAggregateNodeBase aggregationProvided) + { + return "Incompatible aggregation function for table '" + + tableName + + "' column '" + + columnName + "', expecting '" + + ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(aggregationRequired) + + "' and received '" + + ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(aggregationProvided) + + "'"; + } + + private static void AddEquivalent( + ExprAggregateNode aggNodeToAdd, + List equivalencyList) + { + // Check any same aggregation nodes among all aggregation clauses + bool foundEquivalent = false; + foreach (AggregationServiceAggExpressionDesc existing in equivalencyList) + { + ExprAggregateNode aggNode = existing.AggregationNode; + + // we have equivalence when: + // (a) equals on node returns true + // (b) positional parameters are the same + // (c) non-positional (group-by over, if present, are the same ignoring duplicates) + if (!aggNode.EqualsNode(aggNodeToAdd)) + { + continue; + } + if (!ExprNodeUtility.DeepEquals(aggNode.PositionalParams, aggNodeToAdd.PositionalParams)) + { + continue; + } + if (aggNode.OptionalLocalGroupBy != null || aggNodeToAdd.OptionalLocalGroupBy != null) + { + if ((aggNode.OptionalLocalGroupBy == null && aggNodeToAdd.OptionalLocalGroupBy != null) || + (aggNode.OptionalLocalGroupBy != null && aggNodeToAdd.OptionalLocalGroupBy == null)) + { + continue; + } + if ( + !ExprNodeUtility.DeepEqualsIgnoreDupAndOrder( + aggNode.OptionalLocalGroupBy.PartitionExpressions, + aggNodeToAdd.OptionalLocalGroupBy.PartitionExpressions)) + { + continue; + } + } + + existing.AddEquivalent(aggNodeToAdd); + foundEquivalent = true; + break; + } + + if (!foundEquivalent) + { + equivalencyList.Add(new AggregationServiceAggExpressionDesc(aggNodeToAdd, aggNodeToAdd.Factory)); + } + } + + public static ExprValidationException GetRollupReclaimEx() + { + return new ExprValidationException("Reclaim hints are not available with rollup"); + } + + private class BindingMatchResult + { + internal BindingMatchResult( + TableColumnMethodPair[] methodPairs, + AggregationAccessorSlotPair[] accessors, + int[] targetStates, + ExprNode[] accessStateExpr, + AggregationAgent[] agents) + { + MethodPairs = methodPairs; + Accessors = accessors; + TargetStates = targetStates; + AccessStateExpr = accessStateExpr; + Agents = agents; + } + + public TableColumnMethodPair[] MethodPairs { get; private set; } + + public AggregationAccessorSlotPair[] Accessors { get; private set; } + + public int[] TargetStates { get; private set; } + + public AggregationAgent[] Agents { get; private set; } + + public ExprNode[] AccessStateExpr { get; private set; } + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceFactoryService.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceFactoryService.cs new file mode 100755 index 000000000..db266564b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceFactoryService.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client.annotation; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.util; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.epl.variable; + +namespace com.espertech.esper.epl.agg.service +{ + public interface AggregationServiceFactoryService + { + AggregationServiceFactory GetNullAggregationService(); + AggregationServiceFactory GetNoGroupNoAccess(ExprEvaluator[] evaluatorsArr, AggregationMethodFactory[] aggregatorsArr, bool isUnidirectional, bool isFireAndForget, bool isOnSelect); + AggregationServiceFactory GetNoGroupAccessOnly(AggregationAccessorSlotPair[] pairs, AggregationStateFactory[] accessAggSpecs, bool join, bool isUnidirectional, bool isFireAndForget, bool isOnSelect); + AggregationServiceFactory GetNoGroupAccessMixed(ExprEvaluator[] evaluatorsArr, AggregationMethodFactory[] aggregatorsArr, AggregationAccessorSlotPair[] pairs, AggregationStateFactory[] accessAggregations, bool join, bool isUnidirectional, bool isFireAndForget, bool isOnSelect); + AggregationServiceFactory GetGroupedNoReclaimNoAccess(ExprNode[] groupByNodes, ExprEvaluator[] evaluatorsArr, AggregationMethodFactory[] aggregatorsArr, bool isUnidirectional, bool isFireAndForget, bool isOnSelect); + AggregationServiceFactory GetGroupNoReclaimAccessOnly(ExprNode[] groupByNodes, AggregationAccessorSlotPair[] pairs, AggregationStateFactory[] accessAggSpecs, bool @join, bool isUnidirectional, bool isFireAndForget, bool isOnSelect); + AggregationServiceFactory GetGroupNoReclaimMixed(ExprNode[] groupByNodes, ExprEvaluator[] evaluatorsArr, AggregationMethodFactory[] aggregatorsArr, AggregationAccessorSlotPair[] pairs, AggregationStateFactory[] accessAggregations, bool @join, bool isUnidirectional, bool isFireAndForget, bool isOnSelect); + AggregationServiceFactory GetGroupReclaimAged(ExprNode[] groupByNodes, ExprEvaluator[] evaluatorsArr, AggregationMethodFactory[] aggregatorsArr, HintAttribute reclaimGroupAged, HintAttribute reclaimGroupFrequency, VariableService variableService, AggregationAccessorSlotPair[] pairs, AggregationStateFactory[] accessAggregations, bool @join, string optionalContextName, bool isUnidirectional, bool isFireAndForget, bool isOnSelect) ; + AggregationServiceFactory GetGroupReclaimNoAccess(ExprNode[] groupByNodes, ExprEvaluator[] evaluatorsArr, AggregationMethodFactory[] aggregatorsArr, AggregationAccessorSlotPair[] pairs, AggregationStateFactory[] accessAggregations, bool @join, bool isUnidirectional, bool isFireAndForget, bool isOnSelect); + AggregationServiceFactory GetGroupReclaimMixable(ExprNode[] groupByNodes, ExprEvaluator[] evaluatorsArr, AggregationMethodFactory[] aggregatorsArr, AggregationAccessorSlotPair[] pairs, AggregationStateFactory[] accessAggregations, bool @join, bool isUnidirectional, bool isFireAndForget, bool isOnSelect); + AggregationServiceFactory GetGroupReclaimMixableRollup(ExprNode[] groupByNodes, AggregationGroupByRollupDesc byRollupDesc, ExprEvaluator[] evaluatorsArr, AggregationMethodFactory[] aggregatorsArr, AggregationAccessorSlotPair[] pairs, AggregationStateFactory[] accessAggregations, bool join, AggregationGroupByRollupDesc groupByRollupDesc, bool isUnidirectional, bool isFireAndForget, bool isOnSelect); + AggregationServiceFactory GetGroupWBinding(TableMetadata tableMetadata, TableColumnMethodPair[] methodPairs, AggregationAccessorSlotPair[] accessorPairs, bool join, IntoTableSpec bindings, int[] targetStates, ExprNode[] accessStateExpr, AggregationAgent[] agents, AggregationGroupByRollupDesc groupByRollupDesc); + AggregationServiceFactory GetNoGroupWBinding(AggregationAccessorSlotPair[] accessors, bool join, TableColumnMethodPair[] methodPairs, string tableName, int[] targetStates, ExprNode[] accessStateExpr, AggregationAgent[] agents); + AggregationServiceFactory GetNoGroupLocalGroupBy(bool @join, AggregationLocalGroupByPlan localGroupByPlan, bool isUnidirectional, bool isFireAndForget, bool isOnSelect); + AggregationServiceFactory GetGroupLocalGroupBy(bool @join, AggregationLocalGroupByPlan localGroupByPlan, bool isUnidirectional, bool isFireAndForget, bool isOnSelect); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceFactoryServiceImpl.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceFactoryServiceImpl.cs new file mode 100755 index 000000000..3131f07e3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceFactoryServiceImpl.cs @@ -0,0 +1,217 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client.annotation; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.util; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.epl.variable; + +namespace com.espertech.esper.epl.agg.service +{ + public class AggregationServiceFactoryServiceImpl : AggregationServiceFactoryService + { + public static readonly AggregationServiceFactoryService DEFAULT_FACTORY = + new AggregationServiceFactoryServiceImpl(); + + public AggregationServiceFactory GetNullAggregationService() + { + return AggregationServiceNullFactory.AGGREGATION_SERVICE_NULL_FACTORY; + } + + public AggregationServiceFactory GetNoGroupNoAccess( + ExprEvaluator[] evaluatorsArr, + AggregationMethodFactory[] aggregatorsArr, + bool isUnidirectional, + bool isFireAndForget, + bool isOnSelect) + { + return new AggSvcGroupAllNoAccessFactory(evaluatorsArr, aggregatorsArr); + } + + public AggregationServiceFactory GetNoGroupAccessOnly( + AggregationAccessorSlotPair[] pairs, + AggregationStateFactory[] accessAggSpecs, + bool join, + bool isUnidirectional, + bool isFireAndForget, + bool isOnSelect) + { + return new AggSvcGroupAllAccessOnlyFactory(pairs, accessAggSpecs, join); + } + + public AggregationServiceFactory GetNoGroupAccessMixed( + ExprEvaluator[] evaluatorsArr, + AggregationMethodFactory[] aggregatorsArr, + AggregationAccessorSlotPair[] pairs, + AggregationStateFactory[] accessAggregations, + bool join, + bool isUnidirectional, + bool isFireAndForget, + bool isOnSelect) + { + return new AggSvcGroupAllMixedAccessFactory( + evaluatorsArr, aggregatorsArr, pairs, accessAggregations, join); + } + + public AggregationServiceFactory GetGroupedNoReclaimNoAccess( + ExprNode[] groupByNodes, + ExprEvaluator[] evaluatorsArr, + AggregationMethodFactory[] aggregatorsArr, + bool isUnidirectional, + bool isFireAndForget, + bool isOnSelect) + { + return new AggSvcGroupByNoAccessFactory(evaluatorsArr, aggregatorsArr); + } + + public AggregationServiceFactory GetGroupNoReclaimAccessOnly( + ExprNode[] groupByNodes, + AggregationAccessorSlotPair[] pairs, + AggregationStateFactory[] accessAggSpecs, + bool @join, + bool isUnidirectional, + bool isFireAndForget, + bool isOnSelect) + { + return new AggSvcGroupByAccessOnlyFactory(pairs, accessAggSpecs, join); + } + + public AggregationServiceFactory GetGroupNoReclaimMixed( + ExprNode[] groupByNodes, + ExprEvaluator[] evaluatorsArr, + AggregationMethodFactory[] aggregatorsArr, + AggregationAccessorSlotPair[] pairs, + AggregationStateFactory[] accessAggregations, + bool @join, + bool isUnidirectional, + bool isFireAndForget, + bool isOnSelect) + { + return new AggSvcGroupByMixedAccessFactory( + evaluatorsArr, aggregatorsArr, pairs, accessAggregations, join); + } + + public AggregationServiceFactory GetGroupReclaimAged( + ExprNode[] groupByNodes, + ExprEvaluator[] evaluatorsArr, + AggregationMethodFactory[] aggregatorsArr, + HintAttribute reclaimGroupAged, + HintAttribute reclaimGroupFrequency, + VariableService variableService, + AggregationAccessorSlotPair[] pairs, + AggregationStateFactory[] accessAggregations, + bool @join, + string optionalContextName, + bool isUnidirectional, + bool isFireAndForget, + bool isOnSelect) + { + return new AggSvcGroupByReclaimAgedFactory( + evaluatorsArr, aggregatorsArr, reclaimGroupAged, reclaimGroupFrequency, variableService, + pairs, accessAggregations, join, optionalContextName); + } + + public AggregationServiceFactory GetGroupReclaimNoAccess( + ExprNode[] groupByNodes, + ExprEvaluator[] evaluatorsArr, + AggregationMethodFactory[] aggregatorsArr, + AggregationAccessorSlotPair[] pairs, + AggregationStateFactory[] accessAggregations, + bool @join, + bool isUnidirectional, + bool isFireAndForget, + bool isOnSelect) + { + return new AggSvcGroupByRefcountedNoAccessFactory(evaluatorsArr, aggregatorsArr); + } + + public AggregationServiceFactory GetGroupReclaimMixable( + ExprNode[] groupByNodes, + ExprEvaluator[] evaluatorsArr, + AggregationMethodFactory[] aggregatorsArr, + AggregationAccessorSlotPair[] pairs, + AggregationStateFactory[] accessAggregations, + bool @join, + bool isUnidirectional, + bool isFireAndForget, + bool isOnSelect) + { + return new AggSvcGroupByRefcountedWAccessFactory( + evaluatorsArr, aggregatorsArr, pairs, accessAggregations, join); + } + + public AggregationServiceFactory GetGroupReclaimMixableRollup( + ExprNode[] groupByNodes, + AggregationGroupByRollupDesc byRollupDesc, + ExprEvaluator[] evaluatorsArr, + AggregationMethodFactory[] aggregatorsArr, + AggregationAccessorSlotPair[] pairs, + AggregationStateFactory[] accessAggregations, + bool join, + AggregationGroupByRollupDesc groupByRollupDesc, + bool isUnidirectional, + bool isFireAndForget, + bool isOnSelect) + { + return new AggSvcGroupByRefcountedWAccessRollupFactory( + evaluatorsArr, aggregatorsArr, pairs, accessAggregations, join, groupByRollupDesc); + } + + public AggregationServiceFactory GetGroupWBinding( + TableMetadata tableMetadata, + TableColumnMethodPair[] methodPairs, + AggregationAccessorSlotPair[] accessorPairs, + bool join, + IntoTableSpec bindings, + int[] targetStates, + ExprNode[] accessStateExpr, + AggregationAgent[] agents, + AggregationGroupByRollupDesc groupByRollupDesc) + { + return new AggSvcGroupByWTableFactory( + tableMetadata, methodPairs, accessorPairs, join, targetStates, accessStateExpr, agents, + groupByRollupDesc); + } + + public AggregationServiceFactory GetNoGroupWBinding( + AggregationAccessorSlotPair[] accessors, + bool join, + TableColumnMethodPair[] methodPairs, + string tableName, + int[] targetStates, + ExprNode[] accessStateExpr, + AggregationAgent[] agents) + { + return new AggSvcGroupAllMixedAccessWTableFactory( + accessors, join, methodPairs, tableName, targetStates, accessStateExpr, agents); + } + + public AggregationServiceFactory GetNoGroupLocalGroupBy( + bool @join, + AggregationLocalGroupByPlan localGroupByPlan, + bool isUnidirectional, + bool isFireAndForget, + bool isOnSelect) + { + return new AggSvcGroupAllLocalGroupByFactory(join, localGroupByPlan); + } + + public AggregationServiceFactory GetGroupLocalGroupBy( + bool @join, + AggregationLocalGroupByPlan localGroupByPlan, + bool isUnidirectional, + bool isFireAndForget, + bool isOnSelect) + { + return new AggSvcGroupByLocalGroupByFactory(join, localGroupByPlan); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceMatchRecognize.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceMatchRecognize.cs new file mode 100755 index 000000000..e899fb334 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceMatchRecognize.cs @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.agg.service +{ + /// Aggregation result future for use with match recognize. + public interface AggregationServiceMatchRecognize : AggregationResultFuture + { + /// Enter a single event row consisting of one or more events per stream (each stream representing a variable). + /// events per stream + /// variable number that is the base + /// context for expression evaluatiom + void ApplyEnter(EventBean[] eventsPerStream, int streamId, ExprEvaluatorContext exprEvaluatorContext); + + /// Clear current aggregation state. + void ClearResults(); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceMatchRecognizeFactory.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceMatchRecognizeFactory.cs new file mode 100755 index 000000000..b647dbed9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceMatchRecognizeFactory.cs @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.context.util; + +namespace com.espertech.esper.epl.agg.service +{ + /// + /// Factory for aggregation service instances. + /// + /// Consolidates aggregation nodes such that result futures point to a single instance + /// and no re-evaluation of the same result occurs. + /// + public interface AggregationServiceMatchRecognizeFactory + { + AggregationServiceMatchRecognize MakeService(AgentInstanceContext agentInstanceContext); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceMatchRecognizeFactoryDesc.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceMatchRecognizeFactoryDesc.cs new file mode 100755 index 000000000..9d3d35e0e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceMatchRecognizeFactoryDesc.cs @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +namespace com.espertech.esper.epl.agg.service +{ + public class AggregationServiceMatchRecognizeFactoryDesc + { + public AggregationServiceMatchRecognizeFactoryDesc(AggregationServiceMatchRecognizeFactory aggregationServiceFactory, IList expressions) + { + AggregationServiceFactory = aggregationServiceFactory; + Expressions = expressions; + } + + public AggregationServiceMatchRecognizeFactory AggregationServiceFactory { get; private set; } + + public IList Expressions { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceMatchRecognizeFactoryImpl.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceMatchRecognizeFactoryImpl.cs new file mode 100755 index 000000000..cba80d835 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceMatchRecognizeFactoryImpl.cs @@ -0,0 +1,76 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.agg.aggregator; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.agg.service +{ + /// Implements an aggregation service for match recognize. + public class AggregationServiceMatchRecognizeFactoryImpl : AggregationServiceMatchRecognizeFactory + { + private readonly ExprEvaluator[][] _evaluatorsEachStream; + private readonly AggregationMethodFactory[][] _factoryEachStream; + + /// + /// Ctor. + /// + /// number of streams/variables + /// aggregation methods per stream + /// evaluation functions per stream + public AggregationServiceMatchRecognizeFactoryImpl( + int countStreams, + IEnumerable> aggregatorsPerStream, + IEnumerable> evaluatorsPerStream) + { + _evaluatorsEachStream = new ExprEvaluator[countStreams][]; + _factoryEachStream = new AggregationMethodFactory[countStreams][]; + + foreach (var agg in aggregatorsPerStream) { + _factoryEachStream[agg.Key] = agg.Value; + } + + foreach (var eval in evaluatorsPerStream) { + _evaluatorsEachStream[eval.Key] = eval.Value; + } + } + + public AggregationServiceMatchRecognize MakeService(AgentInstanceContext agentInstanceContext) { + + var aggregatorsEachStream = new AggregationMethod[_factoryEachStream.Length][]; + + int count = 0; + for (int stream = 0; stream < _factoryEachStream.Length; stream++) { + AggregationMethodFactory[] thatStream = _factoryEachStream[stream]; + if (thatStream != null) { + aggregatorsEachStream[stream] = new AggregationMethod[thatStream.Length]; + for (int aggId = 0; aggId < thatStream.Length; aggId++) { + aggregatorsEachStream[stream][aggId] = _factoryEachStream[stream][aggId].Make(); + count++; + } + } + } + + var aggregatorsAll = new AggregationMethod[count]; + count = 0; + for (int stream = 0; stream < _factoryEachStream.Length; stream++) { + if (_factoryEachStream[stream] != null) { + for (int aggId = 0; aggId < _factoryEachStream[stream].Length; aggId++) { + aggregatorsAll[count] = aggregatorsEachStream[stream][aggId]; + count++; + } + } + } + + return new AggregationServiceMatchRecognizeImpl(_evaluatorsEachStream, aggregatorsEachStream, aggregatorsAll); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceMatchRecognizeImpl.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceMatchRecognizeImpl.cs new file mode 100755 index 000000000..1ae954fcf --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceMatchRecognizeImpl.cs @@ -0,0 +1,90 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.agg.aggregator; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.agg.service +{ + /// Implements an aggregation service for match recognize. + public class AggregationServiceMatchRecognizeImpl : AggregationServiceMatchRecognize + { + private readonly AggregationMethod[] _aggregatorsAll; + private readonly AggregationMethod[][] _aggregatorsEachStream; + private readonly ExprEvaluator[][] _evaluatorsEachStream; + + public AggregationServiceMatchRecognizeImpl( + ExprEvaluator[][] evaluatorsEachStream, + AggregationMethod[][] aggregatorsEachStream, + AggregationMethod[] aggregatorsAll) + { + _evaluatorsEachStream = evaluatorsEachStream; + _aggregatorsEachStream = aggregatorsEachStream; + _aggregatorsAll = aggregatorsAll; + } + + public void ApplyEnter(EventBean[] eventsPerStream, int streamId, ExprEvaluatorContext exprEvaluatorContext) + { + ExprEvaluator[] evaluatorsStream = _evaluatorsEachStream[streamId]; + if (evaluatorsStream == null) + { + return; + } + + var evaluateParams = new EvaluateParams(eventsPerStream, true, exprEvaluatorContext); + AggregationMethod[] aggregatorsStream = _aggregatorsEachStream[streamId]; + for (int j = 0; j < evaluatorsStream.Length; j++) + { + object columnResult = evaluatorsStream[j].Evaluate(evaluateParams); + aggregatorsStream[j].Enter(columnResult); + } + } + + public object GetValue(int column, int agentInstanceId, EvaluateParams evaluateParams) + { + return _aggregatorsAll[column].Value; + } + + public ICollection GetCollectionOfEvents(int column, EvaluateParams evaluateParams) + { + return null; + } + + public ICollection GetCollectionScalar(int column, EvaluateParams evaluateParams) + { + return null; + } + + public EventBean GetEventBean(int column, EvaluateParams evaluateParams) + { + return null; + } + + public void ClearResults() + { + foreach (AggregationMethod aggregator in _aggregatorsAll) + { + aggregator.Clear(); + } + } + + public Object GetGroupKey(int agentInstanceId) + { + return null; + } + + public ICollection GetGroupKeys(ExprEvaluatorContext exprEvaluatorContext) + { + return null; + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceNull.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceNull.cs new file mode 100755 index 000000000..f4db05b33 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceNull.cs @@ -0,0 +1,108 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.agg.service +{ + /// + /// A null object implementation of the AggregationService + /// interface. + /// + public class AggregationServiceNull : AggregationService + { + + public AggregationServiceNull() + { + } + + public void ApplyEnter( + EventBean[] eventsPerStream, + Object optionalGroupKeyPerRow, + ExprEvaluatorContext exprEvaluatorContext) + { + } + + public void ApplyLeave( + EventBean[] eventsPerStream, + Object optionalGroupKeyPerRow, + ExprEvaluatorContext exprEvaluatorContext) + { + } + + public void SetCurrentAccess(Object groupKey, int agentInstanceId, AggregationGroupByRollupLevel rollupLevel) + { + } + + public object GetValue(int column, int agentInstanceId, EvaluateParams evaluateParams) + { + return null; + } + + public ICollection GetCollectionOfEvents(int column, EvaluateParams evaluateParams) + { + return null; + } + + public ICollection GetCollectionScalar(int column, EvaluateParams evaluateParams) + { + return null; + } + + public EventBean GetEventBean(int column, EvaluateParams evaluateParams) + { + return null; + } + + public void ClearResults(ExprEvaluatorContext exprEvaluatorContext) + { + // no state to clear + } + + public void SetRemovedCallback(AggregationRowRemovedCallback callback) + { + // not applicable + } + + public void Accept(AggregationServiceVisitor visitor) + { + } + + public void AcceptGroupDetail(AggregationServiceVisitorWGroupDetail visitor) + { + } + + public bool IsGrouped + { + get { return false; } + } + + public Object GetGroupKey(int agentInstanceId) + { + return null; + } + + public ICollection GetGroupKeys(ExprEvaluatorContext exprEvaluatorContext) + { + return null; + } + + public void Stop() + { + } + + public AggregationService GetContextPartitionAggregationService(int agentInstanceId) + { + return this; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceNullFactory.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceNullFactory.cs new file mode 100755 index 000000000..9845b967d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceNullFactory.cs @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.core; + +namespace com.espertech.esper.epl.agg.service +{ + /// + /// A null object implementation of the AggregationService interface. + /// + public class AggregationServiceNullFactory : AggregationServiceFactory + { + public static AggregationServiceNullFactory AGGREGATION_SERVICE_NULL_FACTORY = new AggregationServiceNullFactory(); + + private static readonly AggregationServiceNull AGGREGATION_SERVICE_NULL = new AggregationServiceNull(); + + public AggregationService MakeService(AgentInstanceContext agentInstanceContext, EngineImportService engineImportService, bool isSubquery, int? subqueryNumber) + { + return AGGREGATION_SERVICE_NULL; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceTable.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceTable.cs new file mode 100755 index 000000000..cb32248ff --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceTable.cs @@ -0,0 +1,123 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.table.mgmt; + +namespace com.espertech.esper.epl.agg.service +{ + public class AggregationServiceTable : AggregationService + { + private readonly TableStateInstance _tableState; + + public AggregationServiceTable(TableStateInstance tableState) + { + _tableState = tableState; + } + + public TableStateInstance TableState + { + get { return _tableState; } + } + + public void ApplyEnter( + EventBean[] eventsPerStream, + Object optionalGroupKeyPerRow, + ExprEvaluatorContext exprEvaluatorContext) + { + throw HandleNotSupported(); + } + + public void ApplyLeave( + EventBean[] eventsPerStream, + Object optionalGroupKeyPerRow, + ExprEvaluatorContext exprEvaluatorContext) + { + throw HandleNotSupported(); + } + + public void SetCurrentAccess(Object groupKey, int agentInstanceId, AggregationGroupByRollupLevel rollupLevel) + { + throw HandleNotSupported(); + } + + public void ClearResults(ExprEvaluatorContext exprEvaluatorContext) + { + throw HandleNotSupported(); + } + + public void SetRemovedCallback(AggregationRowRemovedCallback callback) + { + throw HandleNotSupported(); + } + + public void Accept(AggregationServiceVisitor visitor) + { + // no action + } + + public void AcceptGroupDetail(AggregationServiceVisitorWGroupDetail visitor) + { + // no action + } + + public bool IsGrouped + { + get { return false; } + } + + public object GetValue(int column, int agentInstanceId, EvaluateParams evaluateParams) + { + throw HandleNotSupported(); + } + + public ICollection GetCollectionOfEvents(int column, EvaluateParams evaluateParams) + { + throw HandleNotSupported(); + } + + public EventBean GetEventBean(int column, EvaluateParams evaluateParams) + { + throw HandleNotSupported(); + } + + public Object GetGroupKey(int agentInstanceId) + { + throw HandleNotSupported(); + } + + public ICollection GetGroupKeys(ExprEvaluatorContext exprEvaluatorContext) + { + throw HandleNotSupported(); + } + + public ICollection GetCollectionScalar(int column, EvaluateParams evaluateParams) + { + throw HandleNotSupported(); + } + + private UnsupportedOperationException HandleNotSupported() + { + return new UnsupportedOperationException("Operation not supported, aggregation server for reporting only"); + } + + public void Stop() + { + } + + public AggregationService GetContextPartitionAggregationService(int agentInstanceId) + { + return this; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceVisitor.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceVisitor.cs new file mode 100755 index 000000000..57435bb67 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceVisitor.cs @@ -0,0 +1,15 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.agg.service +{ + public interface AggregationServiceVisitor + { + void VisitAggregations(int numGroups, params object[] state); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceVisitorWGroupDetail.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceVisitorWGroupDetail.cs new file mode 100755 index 000000000..966c4e64d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationServiceVisitorWGroupDetail.cs @@ -0,0 +1,18 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.agg.service +{ + public interface AggregationServiceVisitorWGroupDetail + { + void VisitGrouped(int numGroups); + void VisitGroup(Object groupKey, params object[] state); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggregationStateFactory.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationStateFactory.cs new file mode 100755 index 000000000..2ebd506e7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationStateFactory.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.agg.service +{ + public interface AggregationStateFactory + { + AggregationState CreateAccess(int agentInstanceId, bool @join, object groupKey, AggregationServicePassThru passThru); + ExprNode AggregationExpression { get; } + } + + public class ProxyAggregationStateFactory : AggregationStateFactory + { + public Func ProcCreateAccess { get; set; } + public Func ProcAggregationExpression { get; set; } + + public AggregationState CreateAccess(int agentInstanceId, bool @join, object groupKey, AggregationServicePassThru passThru) + { + return ProcCreateAccess(agentInstanceId, join, groupKey, passThru); + } + + public ExprNode AggregationExpression + { + get { return ProcAggregationExpression(); } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggregationStateKeyWStream.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationStateKeyWStream.cs new file mode 100755 index 000000000..170c808ef --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationStateKeyWStream.cs @@ -0,0 +1,63 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.agg.service +{ + public class AggregationStateKeyWStream : AggregationStateKey + { + private readonly int _streamNum; + private readonly EventType _eventType; + private readonly AggregationStateTypeWStream _stateType; + private readonly ExprNode[] _exprNodes; + + public AggregationStateKeyWStream(int streamNum, EventType eventType, AggregationStateTypeWStream stateType, ExprNode[] exprNodes) + { + _streamNum = streamNum; + _eventType = eventType; + _stateType = stateType; + _exprNodes = exprNodes; + } + + public override bool Equals(Object o) + { + if (this == o) return true; + if (o == null || GetType() != o.GetType()) return false; + + AggregationStateKeyWStream that = (AggregationStateKeyWStream) o; + + if (_streamNum != that._streamNum) return false; + if (_stateType != that._stateType) return false; + if (!ExprNodeUtility.DeepEquals(_exprNodes, that._exprNodes)) return false; + if (_eventType != null) + { + if (that._eventType == null) + return false; + + if (!EventTypeUtility.IsTypeOrSubTypeOf(that._eventType, _eventType)) + return false; + } + + return true; + } + + public override int GetHashCode() + { + int result = _streamNum; + result = 31 * result + _stateType.GetHashCode(); + return result; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggregationStateTypeWStream.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationStateTypeWStream.cs new file mode 100755 index 000000000..d9aaa4ecb --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationStateTypeWStream.cs @@ -0,0 +1,18 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.agg.service +{ + public enum AggregationStateTypeWStream + { + DATAWINDOWACCESS_LINEAR, + SORTED, + MAXEVER, + MINEVER + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggregationValidationContext.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationValidationContext.cs new file mode 100755 index 000000000..a136d5da0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggregationValidationContext.cs @@ -0,0 +1,66 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.agg.service +{ + /// + /// Context for use with plug-in custom aggregation functions that implement + /// . + /// + /// This context object provides access to the parameter expressions themselves as + /// well as information compiled from the parameter expressions for your convenience. + /// + public class AggregationValidationContext + { + /// Ctor. + /// the type of each parameter expression. + /// for each parameter expression an indicator whether the expression returns a constant result + /// for each parameter expression that returns a constant result this array contains the constant value + /// true if 'distinct' keyword was provided + /// true if all event properties references by parameter expressions are from streams that have data windows declared onto the stream or are from named windows + /// the parameter expressions themselves + public AggregationValidationContext(Type[] parameterTypes, bool[] constantValue, Object[] constantValues, bool distinct, bool windowed, ExprNode[] expressions) + { + ParameterTypes = parameterTypes; + IsConstantValue = constantValue; + ConstantValues = constantValues; + IsDistinct = distinct; + IsWindowed = windowed; + Expressions = expressions; + } + + /// The return type of each parameter expression. This information can also be obtained by calling getType on each parameter expression. + /// array providing result type of each parameter expression + public Type[] ParameterTypes { get; private set; } + + /// A bool indicator for each parameter expression that is true if the expression returns a constant result or false if the expression result is not a constant value. This information can also be obtained by calling isConstantResult on each parameter expression. + /// array providing an indicator per parameter expression that the result is a constant value + public bool[] IsConstantValue { get; private set; } + + /// If a parameter expression returns a constant value, the value of the constant it returns is provided in this array. This information can also be obtained by calling evaluate on each parameter expression providing a constant value. + /// array providing the constant return value per parameter expression that has constant result value, or nullif a parameter expression is deemded to not provide a constant result value + public object[] ConstantValues { get; private set; } + + /// Returns true to indicate that the 'distinct' keyword was specified for this aggregation function. + /// distinct value indicator + public bool IsDistinct { get; private set; } + + /// Returns true to indicate that all parameter expressions return event properties that originate from a stream that provides a remove stream. + /// windowed indicator + public bool IsWindowed { get; private set; } + + /// Returns the parameter expressions themselves for interrogation. + /// parameter expressions + public ExprNode[] Expressions { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/service/AggregatorUtil.cs b/NEsper.Core/NEsper.Core/epl/agg/service/AggregatorUtil.cs new file mode 100755 index 000000000..ed04db8d8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/service/AggregatorUtil.cs @@ -0,0 +1,22 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.agg.service +{ + public class AggregatorUtil + { + public static bool CheckFilter(Object[] @object) + { + if (@object[1] == null) + return false; + return true.Equals(@object[1]); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/agg/util/AggregationGroupByLocalGroupByAnalyzer.cs b/NEsper.Core/NEsper.Core/epl/agg/util/AggregationGroupByLocalGroupByAnalyzer.cs new file mode 100755 index 000000000..820c95f2e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/util/AggregationGroupByLocalGroupByAnalyzer.cs @@ -0,0 +1,170 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.agg.util +{ + /// + /// - Each local-group-by gets its own access state factory, shared between same-local-group-by for compatible states + /// + public class AggregationGroupByLocalGroupByAnalyzer + { + public static AggregationLocalGroupByPlan Analyze( + ExprEvaluator[] evaluators, + AggregationMethodFactory[] prototypes, + AggregationStateFactory[] accessAggregations, + AggregationGroupByLocalGroupDesc localGroupDesc, + ExprNode[] groupByExpressions, + AggregationAccessorSlotPair[] accessors) + { + if (groupByExpressions == null) + { + groupByExpressions = ExprNodeUtility.EMPTY_EXPR_ARRAY; + } + + var columns = new AggregationLocalGroupByColumn[localGroupDesc.NumColumns]; + IList levelsList = new List(); + AggregationLocalGroupByLevel optionalTopLevel = null; + + // determine optional top level (level number is -1) + for (int i = 0; i < localGroupDesc.Levels.Length; i++) + { + AggregationGroupByLocalGroupLevel levelDesc = localGroupDesc.Levels[i]; + if (levelDesc.PartitionExpr.Count == 0) + { + optionalTopLevel = GetLevel( + -1, levelDesc, evaluators, prototypes, accessAggregations, columns, + groupByExpressions.Length == 0, accessors); + } + } + + // determine default (same as group-by) level, if any, assign level number 0 + int levelNumber = 0; + for (int i = 0; i < localGroupDesc.Levels.Length; i++) + { + AggregationGroupByLocalGroupLevel levelDesc = localGroupDesc.Levels[i]; + if (levelDesc.PartitionExpr.Count == 0) + { + continue; + } + bool isDefaultLevel = groupByExpressions != null && + ExprNodeUtility.DeepEqualsIgnoreDupAndOrder( + groupByExpressions, levelDesc.PartitionExpr); + if (isDefaultLevel) + { + AggregationLocalGroupByLevel level = GetLevel( + 0, levelDesc, evaluators, prototypes, accessAggregations, columns, isDefaultLevel, accessors); + levelsList.Add(level); + levelNumber++; + break; + } + } + + // determine all other levels, assign level numbers + for (int i = 0; i < localGroupDesc.Levels.Length; i++) + { + AggregationGroupByLocalGroupLevel levelDesc = localGroupDesc.Levels[i]; + if (levelDesc.PartitionExpr.Count == 0) + { + continue; + } + bool isDefaultLevel = groupByExpressions != null && + ExprNodeUtility.DeepEqualsIgnoreDupAndOrder( + groupByExpressions, levelDesc.PartitionExpr); + if (isDefaultLevel) + { + continue; + } + AggregationLocalGroupByLevel level = GetLevel( + levelNumber, levelDesc, evaluators, prototypes, accessAggregations, columns, isDefaultLevel, + accessors); + levelsList.Add(level); + levelNumber++; + } + + // totals + int numMethods = 0; + int numAccesses = 0; + if (optionalTopLevel != null) + { + numMethods += optionalTopLevel.MethodFactories.Length; + numAccesses += optionalTopLevel.StateFactories.Length; + } + foreach (AggregationLocalGroupByLevel level in levelsList) + { + numMethods += level.MethodFactories.Length; + numAccesses += level.StateFactories.Length; + } + + AggregationLocalGroupByLevel[] levels = levelsList.ToArray(); + return new AggregationLocalGroupByPlan(numMethods, numAccesses, columns, optionalTopLevel, levels); + } + + // Obtain those method and state factories for each level + private static AggregationLocalGroupByLevel GetLevel( + int levelNumber, + AggregationGroupByLocalGroupLevel level, + ExprEvaluator[] methodEvaluatorsAll, + AggregationMethodFactory[] methodFactoriesAll, + AggregationStateFactory[] stateFactoriesAll, + AggregationLocalGroupByColumn[] columns, + bool defaultLevel, + AggregationAccessorSlotPair[] accessors) + { + var partitionExpr = level.PartitionExpr; + var partitionEvaluators = ExprNodeUtility.GetEvaluators(partitionExpr); + + IList methodEvaluators = new List(); + IList methodFactories = new List(); + IList stateFactories = new List(); + + foreach (AggregationServiceAggExpressionDesc expr in level.Expressions) + { + int column = expr.ColumnNum.Value; + int methodOffset = -1; + bool methodAgg = true; + AggregationAccessorSlotPair pair = null; + + if (column < methodEvaluatorsAll.Length) + { + methodEvaluators.Add(methodEvaluatorsAll[column]); + methodFactories.Add(methodFactoriesAll[column]); + methodOffset = methodFactories.Count - 1; + } + else + { + // slot gives us the number of the state factory + int absoluteSlot = accessors[column - methodEvaluatorsAll.Length].Slot; + AggregationAccessor accessor = accessors[column - methodEvaluatorsAll.Length].Accessor; + AggregationStateFactory factory = stateFactoriesAll[absoluteSlot]; + int relativeSlot = stateFactories.IndexOf(factory); + if (relativeSlot == -1) + { + stateFactories.Add(factory); + relativeSlot = stateFactories.Count - 1; + } + methodAgg = false; + pair = new AggregationAccessorSlotPair(relativeSlot, accessor); + } + columns[column] = new AggregationLocalGroupByColumn( + defaultLevel, partitionEvaluators, methodOffset, methodAgg, pair, levelNumber); + } + + return new AggregationLocalGroupByLevel( + methodEvaluators.ToArray(), + methodFactories.ToArray(), + stateFactories.ToArray(), partitionEvaluators, defaultLevel); + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/agg/util/AggregationGroupByLocalGroupDesc.cs b/NEsper.Core/NEsper.Core/epl/agg/util/AggregationGroupByLocalGroupDesc.cs new file mode 100755 index 000000000..64197f19a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/util/AggregationGroupByLocalGroupDesc.cs @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.agg.util +{ + public class AggregationGroupByLocalGroupDesc + { + public AggregationGroupByLocalGroupDesc(int numColumns, AggregationGroupByLocalGroupLevel[] levels) + { + NumColumns = numColumns; + Levels = levels; + } + + public AggregationGroupByLocalGroupLevel[] Levels { get; private set; } + + public int NumColumns { get; private set; } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/agg/util/AggregationGroupByLocalGroupLevel.cs b/NEsper.Core/NEsper.Core/epl/agg/util/AggregationGroupByLocalGroupLevel.cs new file mode 100755 index 000000000..55a45e459 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/util/AggregationGroupByLocalGroupLevel.cs @@ -0,0 +1,30 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.agg.util +{ + public class AggregationGroupByLocalGroupLevel + { + public AggregationGroupByLocalGroupLevel( + IList partitionExpr, + IList expressions) + { + PartitionExpr = partitionExpr; + Expressions = expressions; + } + + public IList PartitionExpr { get; private set; } + + public IList Expressions { get; private set; } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/agg/util/AggregationLocalGroupByColumn.cs b/NEsper.Core/NEsper.Core/epl/agg/util/AggregationLocalGroupByColumn.cs new file mode 100755 index 000000000..92ad0bdfb --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/util/AggregationLocalGroupByColumn.cs @@ -0,0 +1,44 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.agg.util +{ + public class AggregationLocalGroupByColumn + { + public AggregationLocalGroupByColumn( + bool defaultGroupLevel, + ExprEvaluator[] partitionEvaluators, + int methodOffset, + bool methodAgg, + AggregationAccessorSlotPair pair, + int levelNum) + { + IsDefaultGroupLevel = defaultGroupLevel; + PartitionEvaluators = partitionEvaluators; + MethodOffset = methodOffset; + IsMethodAgg = methodAgg; + Pair = pair; + LevelNum = levelNum; + } + + public ExprEvaluator[] PartitionEvaluators { get; private set; } + + public int MethodOffset { get; private set; } + + public bool IsDefaultGroupLevel { get; private set; } + + public bool IsMethodAgg { get; private set; } + + public AggregationAccessorSlotPair Pair { get; private set; } + + public int LevelNum { get; private set; } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/agg/util/AggregationLocalGroupByLevel.cs b/NEsper.Core/NEsper.Core/epl/agg/util/AggregationLocalGroupByLevel.cs new file mode 100755 index 000000000..0eca8a767 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/util/AggregationLocalGroupByLevel.cs @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.agg.util +{ + public class AggregationLocalGroupByLevel + { + public AggregationLocalGroupByLevel( + ExprEvaluator[] methodEvaluators, + AggregationMethodFactory[] methodFactories, + AggregationStateFactory[] stateFactories, + ExprEvaluator[] partitionEvaluators, + bool defaultLevel) + { + MethodEvaluators = methodEvaluators; + MethodFactories = methodFactories; + StateFactories = stateFactories; + PartitionEvaluators = partitionEvaluators; + IsDefaultLevel = defaultLevel; + } + + public ExprEvaluator[] MethodEvaluators { get; private set; } + + public AggregationMethodFactory[] MethodFactories { get; private set; } + + public AggregationStateFactory[] StateFactories { get; private set; } + + public ExprEvaluator[] PartitionEvaluators { get; private set; } + + public bool IsDefaultLevel { get; private set; } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/agg/util/AggregationLocalGroupByPlan.cs b/NEsper.Core/NEsper.Core/epl/agg/util/AggregationLocalGroupByPlan.cs new file mode 100755 index 000000000..1dbf70af2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/util/AggregationLocalGroupByPlan.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.agg.util +{ + public class AggregationLocalGroupByPlan + { + public AggregationLocalGroupByPlan( + int numMethods, + int numAccess, + AggregationLocalGroupByColumn[] columns, + AggregationLocalGroupByLevel optionalLevelTop, + AggregationLocalGroupByLevel[] allLevels) + { + NumMethods = numMethods; + NumAccess = numAccess; + Columns = columns; + OptionalLevelTop = optionalLevelTop; + AllLevels = allLevels; + } + + public AggregationLocalGroupByColumn[] Columns { get; private set; } + + public AggregationLocalGroupByLevel OptionalLevelTop { get; private set; } + + public AggregationLocalGroupByLevel[] AllLevels { get; private set; } + + public int NumMethods { get; private set; } + + public int NumAccess { get; private set; } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/agg/util/AggregationLocalLevelHook.cs b/NEsper.Core/NEsper.Core/epl/agg/util/AggregationLocalLevelHook.cs new file mode 100755 index 000000000..d4dce5fdb --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/agg/util/AggregationLocalLevelHook.cs @@ -0,0 +1,15 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.agg.util +{ + public interface AggregationLocalLevelHook + { + void Planned(AggregationGroupByLocalGroupDesc localGroupDesc, AggregationLocalGroupByPlan localGroupByPlan); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/annotation/AnnotationAttribute.cs b/NEsper.Core/NEsper.Core/epl/annotation/AnnotationAttribute.cs new file mode 100755 index 000000000..006d567a0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/annotation/AnnotationAttribute.cs @@ -0,0 +1,54 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.annotation +{ + /// Represents a attribute of an annotation. + public class AnnotationAttribute + { + private readonly Object _defaultValue; + private readonly String _name; + private readonly Type _type; + + /// Ctor. + /// name of attribute + /// attribute type + /// default value, if any is specified + public AnnotationAttribute(String name, + Type type, + Object defaultValue) + { + _name = name; + _type = type; + _defaultValue = defaultValue; + } + + /// Returns attribute name. + /// attribute name + public string Name + { + get { return _name; } + } + + /// Returns attribute type. + /// attribute type + public Type AnnotationType + { + get { return _type; } + } + + /// Returns default value of annotation. + /// default value + public object DefaultValue + { + get { return _defaultValue; } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/annotation/AnnotationUtil.cs b/NEsper.Core/NEsper.Core/epl/annotation/AnnotationUtil.cs new file mode 100755 index 000000000..42a51b693 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/annotation/AnnotationUtil.cs @@ -0,0 +1,456 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.client.annotation; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.compat.magic; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.annotation +{ + /// + /// Utility to handle EPL statement annotations. + /// + public class AnnotationUtil + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + public static IDictionary> MapByNameLowerCase(IList annotations) + { + var map = new Dictionary>(); + foreach (AnnotationDesc desc in annotations) + { + var key = desc.Name.ToLower(); + if (map.ContainsKey(key)) + { + map.Get(key).Add(desc); + continue; + } + + var annos = new List(2) { desc }; + map.Put(key, annos); + } + return map; + } + + public static Object GetValue(AnnotationDesc desc) + { + foreach (var pair in desc.Attributes) + { + if (pair.First.ToLower() == "value") + { + return pair.Second; + } + } + return null; + } + + /// + /// Compile annotation objects from descriptors. + /// + /// spec for annotations + /// engine imports + /// statement expression + /// + /// annotations + /// + public static Attribute[] CompileAnnotations(IList annotationSpec, EngineImportService engineImportService, String eplStatement) + { + try + { + var attributes = CompileAnnotations(annotationSpec, engineImportService); + return attributes; + } + catch (AttributeException ex) + { + throw new EPStatementException("Failed to process statement annotations: " + ex.Message, eplStatement, ex); + } + catch (Exception ex) + { + var message = "Unexpected exception compiling annotations in statement, please consult the log file and report the exception: " + ex.Message; + Log.Error(message, ex); + throw new EPStatementException(message, eplStatement, ex); + } + } + + /// + /// Compiles attributes / annotations to an array. + /// + /// a list of descriptors + /// for resolving the annotation/attribute class + /// + /// attributes / annotations or empty array if none + /// + private static Attribute[] CompileAnnotations(IList desc, EngineImportService engineImportService) + { + if (desc == null) + return new Attribute[0]; + + var attributes = new Attribute[desc.Count]; + for (int ii = 0; ii < desc.Count; ii++) + { + attributes[ii] = CompileAnnotation(desc[ii], engineImportService); + } + return attributes; + } + + /// + /// Resolves the type of the attribute. + /// + /// The desc. + /// The engine import service. + /// + public static Type ResolveAttributeType(AnnotationDesc desc, EngineImportService engineImportService) + { + // CLR attributes use a different notation that Java annotations. Format + // the attribute according to CLR conventions. + var attributeTypeName = desc.Name; + var attributeTypeNameForCLR = + (attributeTypeName.EndsWith("Attribute")) + ? attributeTypeName + : String.Format("{0}Attribute", attributeTypeName); + + // resolve attribute type + try + { + engineImportService.GetClassLoader(); // Currently unused + return engineImportService.ResolveAnnotation(attributeTypeNameForCLR); + } + catch (EngineImportException e) + { + throw new AttributeException("Failed to resolve @-annotation class: " + e.Message); + } + + return null; + } + + /// + /// Compiles the attribute. + /// + /// The desc. + /// The engine import service. + /// + public static Attribute CompileAnnotation(AnnotationDesc desc, EngineImportService engineImportService) + { + // CLR attributes use a different notation that Java annotations. Format + // the attribute according to CLR conventions. + var attributeTypeName = desc.Name; + var attributeType = ResolveAttributeType(desc, engineImportService); + + // Get the magic type for the attribute + var attributeMagic = MagicType.GetCachedType(attributeType); + if (!attributeMagic.ExtendsType(typeof(Attribute))) + { + throw new AttributeException(String.Format("Annotation '{0}' does not extends System.Attribute", + attributeTypeName)); + } + + // Search for a constructor that matches the constructor parameters + var theConstructor = attributeType.GetConstructor(new Type[0]); + if (theConstructor == null) + { + throw new AttributeException("Failed to find constructor for @-annotation class"); + } + + // Create the attribute + var attributeInstance = (Attribute)theConstructor.Invoke(null); + // Create a collection of properties that have been explicitly set for later + var explicitPropertyTable = new HashSet(); + + // Set properties on the attribute + foreach (var attributeValuePair in desc.Attributes) + { + var propertyName = attributeValuePair.First; + + // Check the explicitPropertyTable to see if we have already set + // this property. If we have, then throw an exception as we do not + // allow a property to be set twice (i.e. duplicated) + if (explicitPropertyTable.Contains(propertyName)) + { + throw new AttributeException( + "Annotation '" + attributeTypeName + "' has duplicate attribute values for attribute '" + propertyName + "'"); + } + + var magicProperty = attributeMagic.ResolveProperty(propertyName, PropertyResolutionStyle.CASE_SENSITIVE); + if (magicProperty == null) + { + throw new AttributeException( + String.Format("Failed to find property {0} in annotation type {1}", propertyName, attributeTypeName)); + } + + var propertyValue = attributeValuePair.Second; + if (propertyValue is AnnotationDesc) + { + propertyValue = CompileAnnotation((AnnotationDesc)propertyValue, engineImportService); + } + + var magicPropertyType = magicProperty.PropertyType; + if (magicPropertyType.IsArray) + { + propertyValue = CheckArray(attributeTypeName, magicProperty, propertyName, propertyValue); + } + + propertyValue = CheckTypeMismatch(attributeTypeName, magicPropertyType, propertyName, propertyValue); + + magicProperty.SetFunction(attributeInstance, propertyValue); + + explicitPropertyTable.Add(propertyName); + } + + // Make sure all required attributes were set + foreach (var property in attributeMagic.GetSimpleProperties(true)) + { +#if EXPLICIT_ATTRIBUTE_PROPERTIES + // Explicit properties make this relatively painless if the value + // was set through the property model. + if (explicitPropertyTable.Contains(property.Name)) + continue; +#endif + + // Not set explicitly which makes this somewhat ... painful + if (property.Member is PropertyInfo) + { + var propertyInfo = (PropertyInfo)property.Member; + var requiredAttributes = propertyInfo.GetCustomAttributes(typeof(RequiredAttribute), true); + if ((requiredAttributes != null) && (requiredAttributes.Length > 0)) + { + var defaultValue = GetDefaultValue(property.PropertyType); + // Property is required ... unfortunately, properties can be set through + // constructors or late-bound properties. It's honestly impossible for us + // to accurately determine if we have really bound the property. + var currentValue = property.GetMethod.Invoke(attributeInstance, new object[0]); + // Here's where it gets fuzzy, we're going to compare the currentValue against + // the defaultValue. This is an inexact science ... + if (Equals(defaultValue, currentValue)) + { + var propertyName = property.Name; + throw new AttributeException("Annotation '" + attributeTypeName + "' requires a value for attribute '" + propertyName + "'"); + } + } + } + } + + if (attributeInstance is HintAttribute) + { + HintEnumExtensions.ValidateGetListed(attributeInstance); + } + + return attributeInstance; + } + + /// + /// Checks the property type for a type mismatch. + /// + /// Name of the attribute type. + /// Type of the magic property. + /// Name of the property. + /// The property value. + private static object CheckTypeMismatch(string attributeTypeName, Type magicPropertyType, string propertyName, object propertyValue) + { + var isTypeMismatch = false; + if (magicPropertyType.IsValueType) + { + if (propertyValue == null) + { + isTypeMismatch = true; + } + else if (magicPropertyType.IsEnum && (propertyValue is string)) + { + return Enum.Parse(magicPropertyType, (string)propertyValue, true); + } + else if (!propertyValue.GetType().IsAssignableFrom(magicPropertyType)) + { + var typeCaster = CastHelper.GetCastConverter(magicPropertyType); + var newValue = typeCaster.Invoke(propertyValue); + if (newValue != null) + { + propertyValue = newValue; + } + else + { + isTypeMismatch = true; + } + } + } + else if (propertyValue != null) + { + if (!propertyValue.GetType().IsAssignableFrom(magicPropertyType)) + { + isTypeMismatch = true; + } + } + + if (isTypeMismatch) + { + var propertyValueText = + propertyValue != null ? propertyValue.GetType().FullName : "null"; + + throw new AttributeException( + "Annotation '" + attributeTypeName + "' requires a " + + magicPropertyType.Name + "-typed value for attribute '" + + propertyName + "' but received " + + "a " + propertyValueText + "-typed value"); + } + + return propertyValue; + } + + /// + /// Checks the property type for array semantics. + /// + /// Name of the attribute type. + /// The property info. + /// Name of the property. + /// The property value. + /// + private static object CheckArray(string attributeTypeName, MagicPropertyInfo propertyInfo, string propertyName, object propertyValue) + { + if (propertyValue != null) + { + var actualElementType = propertyValue.GetType().GetElementType(); + var expectElementType = propertyInfo.PropertyType.GetElementType(); + + // Did we actually receive an array as source? + if (actualElementType == null) + { + throw new AttributeException( + "Annotation '" + attributeTypeName + "' requires a " + + propertyInfo.PropertyType.FullName + "-typed value for attribute '" + + propertyName + "' but received a " + + propertyValue.GetType().FullName + "-typed value"); + } + + var array = (Array)propertyValue; + var length = array.Length; + for (var ii = 0; ii < length; ii++) + { + if (array.GetValue(ii) == null) + { + throw new AttributeException( + "Annotation '" + attributeTypeName + "' requires a " + + "non-null value for array elements for attribute '" + propertyName + "'"); + } + } + + if (!Equals(actualElementType, expectElementType)) + { + var typeCaster = CastHelper.GetCastConverter(expectElementType); + var expectedArray = Array.CreateInstance(expectElementType, length); + for (var ii = 0; ii < length; ii++) + { + var oldValue = array.GetValue(ii); + var newValue = typeCaster.Invoke(oldValue); + if ((newValue == null) && (oldValue != null)) + { + throw new AttributeException( + "Annotation '" + attributeTypeName + "' requires a " + + expectElementType.FullName + "-typed value for array elements for attribute '" + + propertyName + "' but received a " + + oldValue.GetType().FullName + "-typed value"); + } + + expectedArray.SetValue(newValue, ii); + } + + propertyValue = expectedArray; + } + } + + return propertyValue; + } + + /// + /// Gets the default value. + /// + /// The t. + /// + public static object GetDefaultValue(Type t) + { + return t.IsValueType ? Activator.CreateInstance(t) : null; + } + + public static Attribute FindAnnotation(IEnumerable attributes, Type attributeType) + { + if (!attributeType.IsSubclassOrImplementsInterface()) + { + throw new ArgumentException("Type " + attributeType.FullName + " is not an attribute type"); + } + if (attributes == null) + { + return null; + } + + return attributes.FirstOrDefault( + attr => TypeHelper.IsSubclassOrImplementsInterface(attr.GetType(), (attributeType))); + } + + public static Attribute FindAnnotation(Attribute[] annotations, Type annotationClass) + { + if (annotations == null || annotations.Length == 0) + { + return null; + } + + return annotations.FirstOrDefault(anno => + anno.GetType() == annotationClass || + anno.GetType().IsSubclassOf(annotationClass)); + } + + public static List FindAttributes(Attribute[] annotations, Type annotationClass) + { + if (!TypeHelper.IsSubclassOrImplementsInterface(annotationClass, typeof(Attribute))) + { + throw new ArgumentException("Class " + annotationClass.FullName + " is not an attribute class"); + } + + if (annotations == null || annotations.Length == 0) + { + return null; + } + + return annotations + .Where(anno => TypeHelper.IsSubclassOrImplementsInterface(anno.GetType(), annotationClass)) + .ToList(); + } + + public static Attribute[] MergeAnnotations(Attribute[] first, Attribute[] second) + { + return first.Concat(second).ToArray(); + } + + public static String GetExpectSingleStringValue(String msgPrefix, IList annotationsSameName) + { + if (annotationsSameName.Count > 1) + { + throw new ExprValidationException(msgPrefix + " multiple annotations provided named '" + annotationsSameName[0].Name + "'"); + } + var annotation = annotationsSameName[0]; + var value = AnnotationUtil.GetValue(annotation); + if (value == null) + { + throw new ExprValidationException(msgPrefix + " no value provided for annotation '" + annotation.Name + "', expected a value"); + } + if (!(value is String)) + { + throw new ExprValidationException(msgPrefix + " string value expected for annotation '" + annotation.Name + "'"); + } + return (String)value; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/annotation/AttributeException.cs b/NEsper.Core/NEsper.Core/epl/annotation/AttributeException.cs new file mode 100755 index 000000000..55753a0de --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/annotation/AttributeException.cs @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.annotation +{ + /// + /// Thrown to indicate a problem processing an EPL statement annotation. + /// + public class AttributeException : Exception + { + /// + /// Ctor. + /// + /// error message + public AttributeException(String message) + : base(message) + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/approx/CountMinSketchAggAccessorDefault.cs b/NEsper.Core/NEsper.Core/epl/approx/CountMinSketchAggAccessorDefault.cs new file mode 100755 index 000000000..886651688 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/approx/CountMinSketchAggAccessorDefault.cs @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.approx +{ + public class CountMinSketchAggAccessorDefault : AggregationAccessor { + + public static readonly CountMinSketchAggAccessorDefault INSTANCE = new CountMinSketchAggAccessorDefault(); + + private CountMinSketchAggAccessorDefault() { + } + + public object GetValue(AggregationState state, EvaluateParams evalParams) + { + return null; + } + + public ICollection GetEnumerableEvents(AggregationState state, EvaluateParams evalParams) { + return null; + } + + public EventBean GetEnumerableEvent(AggregationState state, EvaluateParams evalParams) { + return null; + } + + public ICollection GetEnumerableScalar(AggregationState state, EvaluateParams evalParams) { + return null; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/approx/CountMinSketchAggAccessorFrequency.cs b/NEsper.Core/NEsper.Core/epl/approx/CountMinSketchAggAccessorFrequency.cs new file mode 100755 index 000000000..712bf7b66 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/approx/CountMinSketchAggAccessorFrequency.cs @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.approx +{ + public class CountMinSketchAggAccessorFrequency : AggregationAccessor + { + private readonly ExprEvaluator _evaluator; + + public CountMinSketchAggAccessorFrequency(ExprEvaluator evaluator) + { + this._evaluator = evaluator; + } + + public object GetValue(AggregationState aggregationState, EvaluateParams evalParams) + { + object value = _evaluator.Evaluate(new EvaluateParams(evalParams.EventsPerStream, true, evalParams.ExprEvaluatorContext)); + CountMinSketchAggState state = (CountMinSketchAggState) aggregationState; + return state.Frequency(value); + } + + public ICollection GetEnumerableEvents(AggregationState state, EvaluateParams evalParams) { + return null; + } + + public EventBean GetEnumerableEvent(AggregationState state, EvaluateParams evalParams) { + return null; + } + + public ICollection GetEnumerableScalar(AggregationState state, EvaluateParams evalParams) { + return null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/approx/CountMinSketchAggAccessorTopk.cs b/NEsper.Core/NEsper.Core/epl/approx/CountMinSketchAggAccessorTopk.cs new file mode 100755 index 000000000..791fd9026 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/approx/CountMinSketchAggAccessorTopk.cs @@ -0,0 +1,47 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.approx +{ + public class CountMinSketchAggAccessorTopk : AggregationAccessor + { + + public static readonly CountMinSketchAggAccessorTopk INSTANCE = new CountMinSketchAggAccessorTopk(); + + private CountMinSketchAggAccessorTopk() + { + } + + public object GetValue(AggregationState aggregationState, EvaluateParams evalParams) + { + var state = (CountMinSketchAggState) aggregationState; + return state.GetFromBytes(); + } + + public ICollection GetEnumerableEvents(AggregationState state, EvaluateParams evalParams) + { + return null; + } + + public EventBean GetEnumerableEvent(AggregationState state, EvaluateParams evalParams) + { + return null; + } + + public ICollection GetEnumerableScalar(AggregationState state, EvaluateParams evalParams) + { + return null; + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/approx/CountMinSketchAggAgentAdd.cs b/NEsper.Core/NEsper.Core/epl/approx/CountMinSketchAggAgentAdd.cs new file mode 100755 index 000000000..3d6f89929 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/approx/CountMinSketchAggAgentAdd.cs @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.approx +{ + public class CountMinSketchAggAgentAdd : AggregationAgent + { + private readonly ExprEvaluator _stringEvaluator; + + public CountMinSketchAggAgentAdd(ExprEvaluator stringEvaluator) { + _stringEvaluator = stringEvaluator; + } + + public void ApplyEnter(EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext, AggregationState aggregationState) + { + var value = _stringEvaluator.Evaluate(new EvaluateParams(eventsPerStream, true, exprEvaluatorContext)); + var state = (CountMinSketchAggState) aggregationState; + state.Add(value); + } + + public void ApplyLeave(EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext, AggregationState aggregationState) { + + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/approx/CountMinSketchAggState.cs b/NEsper.Core/NEsper.Core/epl/approx/CountMinSketchAggState.cs new file mode 100755 index 000000000..c1c966f97 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/approx/CountMinSketchAggState.cs @@ -0,0 +1,83 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.client.util; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.approx +{ + public class CountMinSketchAggState : AggregationState + { + private readonly CountMinSketchState _state; + private readonly CountMinSketchAgent _agent; + + private readonly CountMinSketchAgentContextAdd _add; + private readonly CountMinSketchAgentContextEstimate _estimate; + private readonly CountMinSketchAgentContextFromBytes _fromBytes; + + public CountMinSketchAggState(CountMinSketchState state, CountMinSketchAgent agent) + { + _state = state; + _agent = agent; + _add = new CountMinSketchAgentContextAdd(state); + _estimate = new CountMinSketchAgentContextEstimate(state); + _fromBytes = new CountMinSketchAgentContextFromBytes(state); + } + + public void ApplyEnter(EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext) + { + throw new UnsupportedOperationException("values are added through the add method"); + } + + public void ApplyLeave(EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext) + { + throw new UnsupportedOperationException(); + } + + public void Add(object value) + { + _add.Value = value; + _agent.Add(_add); + } + + public long? Frequency(object value) + { + _estimate.Value = value; + return _agent.Estimate(_estimate); + } + + public void Clear() + { + throw new UnsupportedOperationException(); + } + + public CountMinSketchTopK[] GetFromBytes() + { + var bytes = _state.TopKValues; + if (bytes.IsEmpty()) { + return new CountMinSketchTopK[0]; + } + var arr = new CountMinSketchTopK[bytes.Count]; + var index = 0; + foreach (var buf in bytes) { + long? frequency = _state.Frequency(buf.Data); + _fromBytes.Bytes = buf.Data; + var value = _agent.FromBytes(_fromBytes); + if (frequency == null) { + continue; + } + arr[index++] = new CountMinSketchTopK(frequency.Value, value); + } + return arr; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/approx/CountMinSketchAggType.cs b/NEsper.Core/NEsper.Core/epl/approx/CountMinSketchAggType.cs new file mode 100755 index 000000000..7e00aa721 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/approx/CountMinSketchAggType.cs @@ -0,0 +1,60 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.epl.approx +{ + public enum CountMinSketchAggType + { + STATE, + ADD, + FREQ, + TOPK + } + + public static class CountMinSketchAggTypeExtensions + { + public static string GetFuncName(this CountMinSketchAggType enumValue) + { + switch (enumValue) + { + case CountMinSketchAggType.ADD: + return "countMinSketchAdd"; + case CountMinSketchAggType.FREQ: + return "countMinSketchFrequency"; + case CountMinSketchAggType.STATE: + return "countMinSketch"; + case CountMinSketchAggType.TOPK: + return "countMinSketchTopk"; + } + + throw new ArgumentException("invalid value for enum value", "enumValue"); + } + + public static CountMinSketchAggType? FromNameMayMatch(string name) + { + switch (name.ToLowerInvariant()) + { + case "countminsketchadd": + return CountMinSketchAggType.ADD; + case "countminsketchfrequency": + return CountMinSketchAggType.FREQ; + case "countminsketch": + return CountMinSketchAggType.STATE; + case "countminsketchtopk": + return CountMinSketchAggType.TOPK; + } + + return null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/approx/CountMinSketchSpec.cs b/NEsper.Core/NEsper.Core/epl/approx/CountMinSketchSpec.cs new file mode 100755 index 000000000..0786462ef --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/approx/CountMinSketchSpec.cs @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client.util; + +namespace com.espertech.esper.epl.approx +{ + public class CountMinSketchSpec + { + public CountMinSketchSpec(CountMinSketchSpecHashes hashesSpec, int? topkSpec, CountMinSketchAgent agent) + { + HashesSpec = hashesSpec; + TopkSpec = topkSpec; + Agent = agent; + } + + public CountMinSketchSpecHashes HashesSpec { get; private set; } + + public int? TopkSpec { get; set; } + + public CountMinSketchAgent Agent { get; set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/approx/CountMinSketchSpecHashes.cs b/NEsper.Core/NEsper.Core/epl/approx/CountMinSketchSpecHashes.cs new file mode 100755 index 000000000..4ab8856cd --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/approx/CountMinSketchSpecHashes.cs @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.approx +{ + public class CountMinSketchSpecHashes + { + public CountMinSketchSpecHashes(double epsOfTotalCount, double confidence, int seed) + { + EpsOfTotalCount = epsOfTotalCount; + Confidence = confidence; + Seed = seed; + } + + public double EpsOfTotalCount { get; set; } + + public double Confidence { get; set; } + + public int Seed { get; set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/approx/CountMinSketchState.cs b/NEsper.Core/NEsper.Core/epl/approx/CountMinSketchState.cs new file mode 100755 index 000000000..7f3d0988e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/approx/CountMinSketchState.cs @@ -0,0 +1,66 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.epl.approx +{ + public class CountMinSketchState + { + public static CountMinSketchState MakeState(CountMinSketchSpec spec) + { + CountMinSketchStateHashes hashes = CountMinSketchStateHashes.MakeState(spec.HashesSpec); + CountMinSketchStateTopk topk = null; + if (spec.TopkSpec != null && spec.TopkSpec > 0) { + topk = new CountMinSketchStateTopk(spec.TopkSpec.Value); + } + return new CountMinSketchState(hashes, topk); + } + + public CountMinSketchState(CountMinSketchStateHashes hashes, CountMinSketchStateTopk topk) + { + Hashes = hashes; + Topk = topk; + } + + public void Add(byte[] bytes, int count) + { + Hashes.Add(bytes, count); + if (Topk != null) + { + long frequency = Hashes.EstimateCount(bytes); + Topk.UpdateExpectIncreasing(bytes, frequency); + } + } + + public long Frequency(byte[] bytes) + { + return Hashes.EstimateCount(bytes); + } + + public ICollection TopKValues + { + get + { + if (Topk == null) + { + return Collections.GetEmptyList(); + } + return Topk.TopKValues; + } + } + + public CountMinSketchStateHashes Hashes { get; set; } + + public CountMinSketchStateTopk Topk { get; set; } + } + +} diff --git a/NEsper.Core/NEsper.Core/epl/approx/CountMinSketchStateHashes.cs b/NEsper.Core/NEsper.Core/epl/approx/CountMinSketchStateHashes.cs new file mode 100755 index 000000000..bbaf8902f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/approx/CountMinSketchStateHashes.cs @@ -0,0 +1,118 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.approx +{ + /// + /// + /// Count-min sketch (or CM sketch) is a probabilistic sub-linear space streaming algorithm + /// (source: Wikipedia, see http://en.wikipedia.org/wiki/Count%E2%80%93min_sketch) + /// + /// Count-min sketch computes an approximate frequency and thereby top-k or heavy-hitters. + /// + /// Paper: + /// Graham Cormode and S. Muthukrishnan. An improved data stream summary: + /// The Count-Min sketch and its applications. 2004. 10.1016/j.jalgor.2003.12.001 + /// http://dl.acm.org/citation.cfm?id=1073718 + /// + public class CountMinSketchStateHashes + { + private long _total; + private long[][] _table; + + public static CountMinSketchStateHashes MakeState(CountMinSketchSpecHashes spec) + { + var width = (int)Math.Ceiling(2 / spec.EpsOfTotalCount); + var depth = (int)Math.Ceiling(-Math.Log(1 - spec.Confidence) / Math.Log(2)); + var table = new long[depth][]; + for (var ii = 0; ii < depth; ii++) + table[ii] = new long[width]; + + var hash = new long[depth]; + var r = new Random(spec.Seed); + for (var i = 0; i < depth; ++i) + { + hash[i] = r.Next(int.MaxValue); + } + return new CountMinSketchStateHashes(depth, width, table, hash, 0); + } + + public CountMinSketchStateHashes(int depth, int width, long[][] table, long[] hash, long total) + { + this.Depth = depth; + this.Width = width; + this.Hash = hash; + this._table = table; + this._total = total; + } + + public long[][] Table + { + get { return _table; } + } + + public long[] Hash { get; private set; } + + public int Depth { get; private set; } + + public int Width { get; private set; } + + public void IncTotal(long count) + { + _total += count; + } + + public long Total + { + get { return _total; } + } + + public long EstimateCount(byte[] item) + { + var res = long.MaxValue; + var buckets = GetHashBuckets(item, Depth, Width); + for (var i = 0; i < Depth; ++i) + { + res = Math.Min(res, _table[i][buckets[i]]); + } + return res; + } + + public void Add(byte[] item, long count) + { + if (count < 0) + { + throw new ArgumentException("Negative increments not implemented"); + } + var buckets = GetHashBuckets(item, Depth, Width); + for (var i = 0; i < Depth; ++i) + { + _table[i][buckets[i]] += count; + } + _total += count; + } + + private int[] GetHashBuckets(byte[] b, int hashCount, int max) + { + var result = new int[hashCount]; + var hash1 = MurmurHash.Hash(b, 0, b.Length, 0); + var hash2 = MurmurHash.Hash(b, 0, b.Length, hash1); + for (var i = 0; i < hashCount; i++) + { + var tempMod = (int)(hash1 + i * hash2) % max; + result[i] = Math.Abs(tempMod); + } + return result; + } + } + +} diff --git a/NEsper.Core/NEsper.Core/epl/approx/CountMinSketchStateTopk.cs b/NEsper.Core/NEsper.Core/epl/approx/CountMinSketchStateTopk.cs new file mode 100755 index 000000000..1414cc359 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/approx/CountMinSketchStateTopk.cs @@ -0,0 +1,175 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.epl.approx +{ + public class CountMinSketchStateTopk + { + private readonly int _topkMax; + + // Wherein: Object either is Blob or Deque + private readonly SortedDictionary _topk; + private readonly IDictionary _lastFreqForItem; + + public CountMinSketchStateTopk(int topkMax) + { + _topkMax = topkMax; + _lastFreqForItem = new Dictionary(); + _topk = new SortedDictionary(SimpleComparer.Reverse); + } + + public CountMinSketchStateTopk( + int topkMax, + SortedDictionary topk, + IDictionary lastFreqForItem) + { + _topkMax = topkMax; + _topk = topk; + _lastFreqForItem = lastFreqForItem; + } + + public SortedDictionary Topk + { + get { return _topk; } + } + + public void UpdateExpectIncreasing(byte[] value, long frequency) + { + var filled = _lastFreqForItem.Count == _topkMax; + if (!filled) + { + var valueBuffer = new Blob(value); + UpdateInternal(valueBuffer, frequency); + } + else + { + var lastKey = _topk.Last().Key; + if (frequency > lastKey) + { + var valueBuffer = new Blob(value); + UpdateInternal(valueBuffer, frequency); + } + } + + TrimItems(); + } + + private void UpdateInternal(Blob valueBuffer, long frequency) + { + var beforeUpdateFrequency = _lastFreqForItem.Push(valueBuffer, frequency); + if (beforeUpdateFrequency != null) + { + RemoveItemFromSorted(beforeUpdateFrequency.Value, valueBuffer); + } + AddItemToSorted(frequency, valueBuffer); + } + + private void RemoveItemFromSorted(long frequency, Blob value) + { + var existing = _topk.Get(frequency); + if (existing is Deque) + { + var deque = (Deque)existing; + deque.Remove(value); + if (deque.IsEmpty()) + { + _topk.Remove(frequency); + } + } + else if (existing != null) + { + _topk.Remove(frequency); + } + } + + private void AddItemToSorted(long frequency, Blob value) + { + var existing = _topk.Get(frequency); + if (existing == null) + { + _topk.Put(frequency, value); + } + else if (existing is Deque) + { + var deque = (Deque)existing; + deque.Add(value); + } + else + { + Deque deque = new ArrayDeque(2); + deque.Add((Blob)existing); + deque.Add(value); + _topk.Put(frequency, deque); + } + } + + private void TrimItems() + { + while (_lastFreqForItem.Count > _topkMax) + { + if (_topk.Count == 0) + { + break; + } + + var last = _topk.LastOrDefault(); + if (last.Value is Deque) + { + var deque = (Deque)last.Value; + var valueRemoved = deque.RemoveLast(); + _lastFreqForItem.Remove(valueRemoved); + if (deque.IsEmpty()) + { + _topk.Remove(last.Key); + } + } + else + { + _topk.Remove(last.Key); + _lastFreqForItem.Remove((Blob)last.Value); + } + } + } + + public IList TopKValues + { + get + { + IList values = new List(); + foreach (var entry in _topk) + { + if (entry.Value is Deque) + { + var set = (Deque)entry.Value; + foreach (var o in set) + { + values.Add(o); + } + } + else + { + values.Add((Blob)entry.Value); + } + } + return values; + } + } + + public int TopkMax + { + get { return _topkMax; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/BindProcessor.cs b/NEsper.Core/NEsper.Core/epl/core/BindProcessor.cs new file mode 100755 index 000000000..3df12f7ff --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/BindProcessor.cs @@ -0,0 +1,160 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.table.mgmt; + +namespace com.espertech.esper.epl.core +{ + /// + /// Works in conjunction with to present + /// a result as an object array for 'natural' delivery. + /// + public class BindProcessor + { + private readonly string[] _columnNamesAssigned; + private readonly ExprEvaluator[] _expressionNodes; + private readonly Type[] _expressionTypes; + + /// + /// Ctor. + /// + /// the select clause + /// the event types per stream + /// the stream names + /// table service + /// when the validation of the select clause failed + public BindProcessor( + IEnumerable selectionList, + EventType[] typesPerStream, + string[] streamNames, + TableService tableService) + { + var expressions = new List(); + var types = new List(); + var columnNames = new List(); + + foreach (var element in selectionList) + { + // handle wildcards by outputting each stream's underlying event + if (element is SelectClauseElementWildcard) + { + for (var i = 0; i < typesPerStream.Length; i++) + { + var returnType = typesPerStream[i].UnderlyingType; + var tableMetadata = tableService.GetTableMetadataFromEventType(typesPerStream[i]); + ExprEvaluator evaluator; + if (tableMetadata != null) + { + evaluator = new BindProcessorEvaluatorStreamTable(i, returnType, tableMetadata); + } + else + { + evaluator = new BindProcessorEvaluatorStream(i, returnType); + } + expressions.Add(evaluator); + types.Add(returnType); + columnNames.Add(streamNames[i]); + } + } + else if (element is SelectClauseStreamCompiledSpec) + { + // handle stream wildcards by outputting the stream underlying event + var streamSpec = (SelectClauseStreamCompiledSpec) element; + var type = typesPerStream[streamSpec.StreamNumber]; + var returnType = type.UnderlyingType; + + var tableMetadata = tableService.GetTableMetadataFromEventType(type); + ExprEvaluator evaluator; + if (tableMetadata != null) + { + evaluator = new BindProcessorEvaluatorStreamTable( + streamSpec.StreamNumber, returnType, tableMetadata); + } + else + { + evaluator = new BindProcessorEvaluatorStream(streamSpec.StreamNumber, returnType); + } + expressions.Add(evaluator); + types.Add(returnType); + columnNames.Add(streamNames[streamSpec.StreamNumber]); + } + else if (element is SelectClauseExprCompiledSpec) + { + // handle expressions + var expr = (SelectClauseExprCompiledSpec) element; + var evaluator = expr.SelectExpression.ExprEvaluator; + expressions.Add(evaluator); + types.Add(evaluator.ReturnType); + if (expr.AssignedName != null) + { + columnNames.Add(expr.AssignedName); + } + else + { + columnNames.Add(ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(expr.SelectExpression)); + } + } + else + { + throw new IllegalStateException( + "Unrecognized select expression element of type " + element.GetType()); + } + } + + _expressionNodes = expressions.ToArray(); + _expressionTypes = types.ToArray(); + _columnNamesAssigned = columnNames.ToArray(); + } + + /// + /// Process select expressions into columns for native dispatch. + /// + /// each stream's events + /// true for new events + /// context for expression evaluatiom + /// object array with select-clause results + public Object[] Process(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext exprEvaluatorContext) + { + var parameters = new Object[_expressionNodes.Length]; + + var evaluateParams = new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext); + for (var i = 0; i < parameters.Length; i++) + { + var result = _expressionNodes[i].Evaluate(evaluateParams); + parameters[i] = result; + } + + return parameters; + } + + /// + /// Returns the expression types generated by the select-clause expressions. + /// + /// types + public Type[] ExpressionTypes + { + get { return _expressionTypes; } + } + + /// + /// Returns the column names of select-clause expressions. + /// + /// column names + public string[] ColumnNamesAssigned + { + get { return _columnNamesAssigned; } + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/core/BindProcessorEvaluatorStream.cs b/NEsper.Core/NEsper.Core/epl/core/BindProcessorEvaluatorStream.cs new file mode 100755 index 000000000..73e5b3c5c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/BindProcessorEvaluatorStream.cs @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.core +{ + public class BindProcessorEvaluatorStream : ExprEvaluator + { + private readonly int _streamNum; + private readonly Type _returnType; + + public BindProcessorEvaluatorStream(int streamNum, Type returnType) + { + _streamNum = streamNum; + _returnType = returnType; + } + + public object Evaluate(EvaluateParams evaluateParams) + { + EventBean theEvent = evaluateParams.EventsPerStream[_streamNum]; + if (theEvent != null) { + return theEvent.Underlying; + } + return null; + } + + public Type ReturnType + { + get { return _returnType; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/BindProcessorEvaluatorStreamTable.cs b/NEsper.Core/NEsper.Core/epl/core/BindProcessorEvaluatorStreamTable.cs new file mode 100755 index 000000000..02866a587 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/BindProcessorEvaluatorStreamTable.cs @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.table.mgmt; + +namespace com.espertech.esper.epl.core +{ + public class BindProcessorEvaluatorStreamTable : ExprEvaluator + { + private readonly int _streamNum; + private readonly Type _returnType; + private readonly TableMetadata _tableMetadata; + + public BindProcessorEvaluatorStreamTable(int streamNum, Type returnType, TableMetadata tableMetadata) + { + _streamNum = streamNum; + _returnType = returnType; + _tableMetadata = tableMetadata; + } + + public object Evaluate(EvaluateParams evaluateParams) + { + return Evaluate( + evaluateParams.EventsPerStream, + evaluateParams.IsNewData, + evaluateParams.ExprEvaluatorContext); + } + + public object Evaluate(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext exprEvaluatorContext) { + EventBean theEvent = eventsPerStream[_streamNum]; + if (theEvent != null) { + return _tableMetadata.EventToPublic.ConvertToUnd(theEvent, new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext)); + } + return null; + } + + public Type ReturnType + { + get { return _returnType; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/ColumnNamedNodeSwapper.cs b/NEsper.Core/NEsper.Core/epl/core/ColumnNamedNodeSwapper.cs new file mode 100755 index 000000000..f11f8fefb --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ColumnNamedNodeSwapper.cs @@ -0,0 +1,83 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.core +{ + /// A utility class for replacing select-clause column names with their definitions in expression node trees. + public class ColumnNamedNodeSwapper + { + /// Replace all instances of the node representing the colum name with the full expression. + /// the expression node tree to make the changes in + /// the select-clause name that is to be expanded + /// the full expression that the column name represents + /// exprTree with the appropriate swaps performed, or fullExpr,if all of exprTree needed to be swapped + public static ExprNode Swap(ExprNode exprTree, String columnName, ExprNode fullExpr) + { + if(fullExpr == null) + { + throw new ArgumentNullException(); + } + + if(IsColumnNameNode(exprTree, columnName)) + { + return fullExpr; + } + else + { + VisitChildren(exprTree, columnName, fullExpr); + } + + return exprTree; + } + + /// A recursive function that works on the child nodes of a given node, replacing any instances of the node representing the name, and visiting the children of all other nodes. + /// the node whose children are to be examined for names + /// the name to replace + /// the full expression corresponding to the name + private static void VisitChildren(ExprNode node, String name, ExprNode fullExpr) + { + var childNodes = node.ChildNodes; + + for (int i = 0; i < childNodes.Count; i++) + { + ExprNode childNode = childNodes[i]; + if(IsColumnNameNode(childNode, name)) + { + node.SetChildNode(i, fullExpr); + } + else + { + VisitChildren(childNode, name, fullExpr); + } + } + } + + private static bool IsColumnNameNode(ExprNode node, String name) + { + if(node is ExprIdentNode) + { + if(node.ChildNodes.Count > 0) + { + throw new IllegalStateException("Ident node has unexpected child nodes"); + } + ExprIdentNode identNode = (ExprIdentNode) node; + return identNode.UnresolvedPropertyName.Equals(name) && identNode.StreamOrPropertyName == null; + } + else + { + return false; + } + } + } +} diff --git a/NEsper/NEsper/client/dataflow/io/ObjectToDataOutputCollector.cs b/NEsper.Core/NEsper.Core/epl/core/DuplicatePropertyException.cs old mode 100644 new mode 100755 similarity index 51% rename from NEsper/NEsper/client/dataflow/io/ObjectToDataOutputCollector.cs rename to NEsper.Core/NEsper.Core/epl/core/DuplicatePropertyException.cs index e382904cc..3091f9ec2 --- a/NEsper/NEsper/client/dataflow/io/ObjectToDataOutputCollector.cs +++ b/NEsper.Core/NEsper.Core/epl/core/DuplicatePropertyException.cs @@ -1,21 +1,28 @@ /////////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2006-2015 Esper Team. All rights reserved. / +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / // http://esper.codehaus.org / // ---------------------------------------------------------------------------------- / // The software in this package is published under the terms of the GPL license / // a copy of which has been included with this distribution in the license.txt file. / /////////////////////////////////////////////////////////////////////////////////////// + using System; -using System.IO; -namespace com.espertech.esper.client.dataflow.io +namespace com.espertech.esper.epl.core { - /// Receives an object and writes to {@link java.io.DataOutput}. - public interface ObjectToDataOutputCollector { - /// Write the received object to {@link java.io.DataOutput}. - /// the object and output - /// IOException when the write operation failed - void Collect(ObjectToDataOutputCollectorContext context) + /// + /// Indicates a property exists in multiple streams. + /// + [Serializable] + public class DuplicatePropertyException : StreamTypesException + { + /// Ctor. + /// exception message + /// + public DuplicatePropertyException(String msg) + : base(msg, null) + { + } } } diff --git a/NEsper.Core/NEsper.Core/epl/core/EngineImportException.cs b/NEsper.Core/NEsper.Core/epl/core/EngineImportException.cs new file mode 100755 index 000000000..02605e819 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/EngineImportException.cs @@ -0,0 +1,33 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.core +{ + /// + /// Indicates a problem importing classes, aggregation functions and the like. + /// + public class EngineImportException : Exception + { + /// Ctor. + /// exception message + public EngineImportException(String msg) + : base(msg) + { + } + + /// Ctor. + /// exception message + /// inner exception + public EngineImportException(String msg, Exception ex) + : base(msg, ex) + { + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/EngineImportService.cs b/NEsper.Core/NEsper.Core/epl/core/EngineImportService.cs new file mode 100755 index 000000000..bf50186b6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/EngineImportService.cs @@ -0,0 +1,228 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.client.hook; +using com.espertech.esper.client.util; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.epl.agg.factory; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.time; + +namespace com.espertech.esper.epl.core +{ + /// + /// Service for engine-level resolution of static methods and aggregation methods. + /// + public interface EngineImportService + { + /// + /// Returns the method invocation caches for the from-clause for a class. + /// + /// the class name providing the method + /// cache configs + ConfigurationMethodRef GetConfigurationMethodRef(string className); + + /// + /// Add an import, such as "com.mypackage.*" or "com.mypackage.MyClass". + /// + /// Type of the namespace or. + /// if the information or format is invalid + void AddImport(String namespaceOrType); + + /// + /// Add an import, such as "com.mypackage.*" or "com.mypackage.MyClass". + /// + /// The automatic import desc. + /// if the information or format is invalid + void AddImport(AutoImportDesc import); + + /// + /// Add an import annotation-only use, such as "com.mypackage.*" or "com.mypackage.MyClass". + /// + /// Type of the namespace or. + /// if the information or format is invalid + void AddAnnotationImport(String namespaceOrType); + + /// + /// Add an import for annotation-only use, such as "com.mypackage.*" or "com.mypackage.MyClass". + /// + /// The automatic import desc. + /// if the information or format is invalid + void AddAnnotationImport(AutoImportDesc autoImportDesc); + + /// + /// Add an aggregation function. + /// + /// is the name of the function to make known. + /// is the descriptor for the aggregation function + /// throw if format or information is invalid + void AddAggregation(string functionName, ConfigurationPlugInAggregationFunction aggregationDesc) ; + + /// + /// Add an single-row function. + /// + /// is the name of the function to make known. + /// is the class that provides the single row function + /// is the name of the static method provided by the class that provides the single row function + /// setting to control value cache behavior which may cache a result value when constant parameters are passed + /// filter behavior setting + /// for whether to rethrow + /// event type name when provided + /// throw if format or information is invalid + void AddSingleRow(string functionName, string singleRowFuncClass, string methodName, ValueCacheEnum valueCache, FilterOptimizableEnum filterOptimizable, bool rethrowExceptions, string optionalEventTypeName) ; + + /// + /// Used at statement compile-time to try and resolve a given function name into an + /// aggregation method. Matches function name case-neutral. + /// + /// is the function name + /// if the function is not a configured aggregation function + /// if the aggregation providing class could not be loaded or doesn't match + /// aggregation provider + AggregationFunctionFactory ResolveAggregationFactory(string functionName) ; + + ConfigurationPlugInAggregationMultiFunction ResolveAggregationMultiFunction(string name); + + /// + /// Used at statement compile-time to try and resolve a given function name into an + /// single-row function. Matches function name case-neutral. + /// + /// is the function name + /// if the function is not a configured single-row function + /// if the function providing class could not be loaded or doesn't match + /// class name and method name pair + Pair ResolveSingleRow(string functionName) ; + + /// + /// Resolves a given class, method and list of parameter types to a static method. + /// + /// is the class name to use + /// is the method name + /// is parameter types match expression sub-nodes + /// flag for whether event bean is allowed + /// flag for whether event bean array is allowed + /// if the method cannot be resolved to a visible static method + /// method this resolves to + MethodInfo ResolveMethodOverloadChecked(string className, string methodName, Type[] paramTypes, bool[] allowEventBeanType, bool[] allowEventBeanCollType); + + MethodInfo ResolveMethodOverloadChecked(Type clazz, string methodName); + + /// + /// Resolves a constructor matching list of parameter types. + /// + /// is the class to use + /// is parameter types match expression sub-nodes + /// if the ctor cannot be resolved + /// method this resolves to + ConstructorInfo ResolveCtor(Type clazz, Type[] paramTypes) ; + + /// + /// Resolves a given class name, either fully qualified and simple and imported to a class. + /// + /// is the class name to use + /// whether we are resolving an annotation + /// if there was an error resolving the class + /// class this resolves to + Type ResolveType(string className, bool forAnnotation) ; + + /// + /// Resolves a given class name, either fully qualified and simple and imported to a annotation. + /// + /// is the class name to use + /// if there was an error resolving the class + /// annotation class this resolves to + Type ResolveAnnotation(string className) ; + + /// + /// Resolves a given class and method name to a static method, expecting the method to exist + /// exactly once and not be overloaded, with any parameters. + /// + /// is the class name to use + /// is the method name + /// + /// if the method cannot be resolved to a visible static method, or + /// if the method is overloaded + /// + /// method this resolves to + MethodInfo ResolveMethodOverloadChecked(string className, string methodName); + + /// + /// Resolves a given class and method name to a non-static method, expecting the method to exist + /// exactly once and not be overloaded, with any parameters. + /// + /// is the class + /// is the method name + /// + /// if the method cannot be resolved to a visible static method, or + /// if the method is overloaded + /// + /// method this resolves to + MethodInfo ResolveNonStaticMethodOverloadChecked(Type clazz, string methodName) ; + + /// + /// Resolves a given method name and list of parameter types to an instance or static method exposed by the given class. + /// + /// is the class to look for a fitting method + /// is the method name + /// is parameter types match expression sub-nodes + /// whether EventBean footprint is allowed + /// whether EventBean array footprint is allowed + /// if the method cannot be resolved to a visible static or instance method + /// method this resolves to + MethodInfo ResolveMethod(Type clazz, string methodName, Type[] paramTypes, bool[] allowEventBeanType, bool[] allowEventBeanCollType); + + /// + /// Resolve an extended (non-SQL std) builtin aggregation. + /// + /// of func + /// indicator + /// aggregation func node + ExprNode ResolveAggExtendedBuiltin(string name, bool isDistinct); + + /// + /// Resolve an extended (non-SQL std) single-row function. + /// + /// of func + /// node or null + ExprNode ResolveSingleRowExtendedBuiltin(string name); + + bool IsDuckType { get; } + + bool IsUdfCache { get; } + + bool IsSortUsingCollator { get; } + + void AddAggregationMultiFunction(ConfigurationPlugInAggregationMultiFunction desc) ; + + MathContext DefaultMathContext { get; } + + TimeZoneInfo TimeZone { get; } + + TimeAbacus TimeAbacus { get; } + + ConfigurationEngineDefaults.ThreadingProfile ThreadingProfile { get; } + + AggregationFactoryFactory AggregationFactoryFactory { get; } + + ClassForNameProvider GetClassForNameProvider(); + + ClassLoader GetFastClassClassLoader(Type clazz); + + ClassLoader GetClassLoader(); + } + + public class EngineImportServiceConstants + { + public const string EXT_SINGLEROW_FUNCTION_TRANSPOSE = "transpose"; + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/EngineImportServiceImpl.cs b/NEsper.Core/NEsper.Core/epl/core/EngineImportServiceImpl.cs new file mode 100755 index 000000000..96896b56d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/EngineImportServiceImpl.cs @@ -0,0 +1,854 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text.RegularExpressions; + +using com.espertech.esper.client; +using com.espertech.esper.client.annotation; +using com.espertech.esper.client.hook; +using com.espertech.esper.client.util; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.factory; +using com.espertech.esper.epl.approx; +using com.espertech.esper.epl.expression.accessagg; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.methodagg; +using com.espertech.esper.epl.expression.time; +using com.espertech.esper.type; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.core +{ + /// Implementation for engine-level imports. + public class EngineImportServiceImpl : EngineImportService + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly IList _imports; + private readonly IList _annotationImports; + private readonly IDictionary _aggregationFunctions; + private readonly IList, ConfigurationPlugInAggregationMultiFunction>> _aggregationAccess; + private readonly IDictionary _singleRowFunctions; + private readonly IDictionary _methodInvocationRef; + private readonly bool _allowExtendedAggregationFunc; + private readonly bool _isUdfCache; + private readonly bool _isDuckType; + private readonly bool _sortUsingCollator; + private readonly MathContext _optionalDefaultMathContext; + private readonly TimeZoneInfo _timeZone; + private readonly TimeAbacus _timeAbacus; + private readonly ConfigurationEngineDefaults.ThreadingProfile _threadingProfile; + private readonly IDictionary _transientConfiguration; + private readonly AggregationFactoryFactory _aggregationFactoryFactory; + + public EngineImportServiceImpl( + bool allowExtendedAggregationFunc, + bool isUdfCache, + bool isDuckType, + bool sortUsingCollator, + MathContext optionalDefaultMathContext, + TimeZoneInfo timeZone, + TimeAbacus timeAbacus, + ConfigurationEngineDefaults.ThreadingProfile threadingProfile, + IDictionary transientConfiguration, + AggregationFactoryFactory aggregationFactoryFactory) + { + _imports = new List(); + _annotationImports = new List(2); + _aggregationFunctions = new Dictionary(); + _aggregationAccess = new List, ConfigurationPlugInAggregationMultiFunction>>(); + _singleRowFunctions = new Dictionary(); + _methodInvocationRef = new Dictionary(); + _allowExtendedAggregationFunc = allowExtendedAggregationFunc; + _isUdfCache = isUdfCache; + _isDuckType = isDuckType; + _sortUsingCollator = sortUsingCollator; + _optionalDefaultMathContext = optionalDefaultMathContext; + _timeZone = timeZone; + _timeAbacus = timeAbacus; + _threadingProfile = threadingProfile; + _transientConfiguration = transientConfiguration; + _aggregationFactoryFactory = aggregationFactoryFactory; + } + + public bool IsUdfCache + { + get { return _isUdfCache; } + } + + public bool IsDuckType + { + get { return _isDuckType; } + } + + public ConfigurationMethodRef GetConfigurationMethodRef(string className) { + return _methodInvocationRef.Get(className); + } + + public ClassForNameProvider GetClassForNameProvider() { + return TransientConfigurationResolver.ResolveClassForNameProvider(_transientConfiguration); + } + + public ClassLoader GetFastClassClassLoader(Type clazz) { + return TransientConfigurationResolver.ResolveFastClassClassLoaderProvider(_transientConfiguration).Classloader(clazz); + } + + public ClassLoader GetClassLoader() { + return TransientConfigurationResolver.ResolveClassLoader(_transientConfiguration).Classloader(); + } + + /// + /// Adds cache configs for method invocations for from-clause. + /// + /// cache configs + public void AddMethodRefs(IDictionary configs) + { + _methodInvocationRef.PutAll(configs); + } + + public void AddImport(String namespaceOrType) + { + ValidateImportAndAdd(new AutoImportDesc(namespaceOrType), _imports); + } + + public void AddImport(AutoImportDesc importDesc) + { + ValidateImportAndAdd(importDesc, _imports); + } + + public void AddAnnotationImport(String namespaceOrType) + { + ValidateImportAndAdd(new AutoImportDesc(namespaceOrType), _annotationImports); + } + + public void AddAnnotationImport(AutoImportDesc importDesc) + { + ValidateImportAndAdd(importDesc, _annotationImports); + } + + public void AddAggregation(string functionName, ConfigurationPlugInAggregationFunction aggregationDesc) + { + ValidateFunctionName("aggregation function", functionName); + if (aggregationDesc.FactoryClassName == null || !IsTypeName(aggregationDesc.FactoryClassName)) + { + throw new EngineImportException( + "Invalid class name for aggregation factory '" + aggregationDesc.FactoryClassName + "'"); + } + _aggregationFunctions.Put(functionName.ToLowerInvariant(), aggregationDesc); + } + + public void AddSingleRow( + string functionName, + string singleRowFuncClass, + string methodName, + ValueCacheEnum valueCache, + FilterOptimizableEnum filterOptimizable, + bool rethrowExceptions, + string optionalEventTypeName) + { + ValidateFunctionName("single-row", functionName); + + if (!IsTypeName(singleRowFuncClass)) + { + throw new EngineImportException("Invalid class name for aggregation '" + singleRowFuncClass + "'"); + } + _singleRowFunctions.Put( + functionName.ToLowerInvariant(), + new EngineImportSingleRowDesc( + singleRowFuncClass, methodName, valueCache, filterOptimizable, rethrowExceptions, + optionalEventTypeName)); + } + + public AggregationFunctionFactory ResolveAggregationFactory(string name) + { + var desc = _aggregationFunctions.Get(name); + if (desc == null) { + desc = _aggregationFunctions.Get(name.ToLowerInvariant()); + } + if (desc == null || desc.FactoryClassName == null) { + throw new EngineImportUndefinedException("A function named '" + name + "' is not defined"); + } + + var className = desc.FactoryClassName; + Type clazz; + try { + clazz = GetClassForNameProvider().ClassForName(className); + } catch (TypeLoadException ex) { + throw new EngineImportException("Could not load aggregation factory class by name '" + className + "'", ex); + } + + Object @object; + try { + @object = Activator.CreateInstance(clazz); + } + catch (TypeLoadException e) + { + throw new EngineImportException("Error instantiating aggregation class", e); + } + catch (MissingMethodException e) + { + throw new EngineImportException("Error instantiating aggregation class - Default constructor was not found", e); + } + catch (MethodAccessException e) + { + throw new EngineImportException("Error instantiating aggregation class - Caller does not have permission to use constructor", e); + } + catch (ArgumentException e) + { + throw new EngineImportException("Error instantiating aggregation class - Type is not a RuntimeType", e); + } + + if (!(@object is AggregationFunctionFactory)) { + throw new EngineImportException("Aggregation class by name '" + className + "' does not implement AggregationFunctionFactory"); + } + return (AggregationFunctionFactory) @object; + } + + public void AddAggregationMultiFunction(ConfigurationPlugInAggregationMultiFunction desc) { + var orderedImmutableFunctionNames = new LinkedHashSet(); + foreach (var functionName in desc.FunctionNames) { + orderedImmutableFunctionNames.Add(functionName.ToLowerInvariant()); + ValidateFunctionName("aggregation multi-function", functionName.ToLowerInvariant()); + } + if (!IsTypeName(desc.MultiFunctionFactoryClassName)) { + throw new EngineImportException("Invalid class name for aggregation multi-function factory '" + desc.MultiFunctionFactoryClassName + "'"); + } + _aggregationAccess.Add(new Pair, ConfigurationPlugInAggregationMultiFunction>(orderedImmutableFunctionNames, desc)); + } + + public ConfigurationPlugInAggregationMultiFunction ResolveAggregationMultiFunction(string name) + { + foreach (var config in _aggregationAccess) + { + if (config.First.Contains(name.ToLowerInvariant())) + { + return config.Second; + } + } + return null; + } + + public Pair ResolveSingleRow(string name) + { + var pair = _singleRowFunctions.Get(name); + if (pair == null) + { + pair = _singleRowFunctions.Get(name.ToLowerInvariant()); + } + if (pair == null) + { + throw new EngineImportUndefinedException("A function named '" + name + "' is not defined"); + } + + Type clazz; + try + { + clazz = GetClassForNameProvider().ClassForName(pair.ClassName); + } + catch (TypeLoadException ex) + { + throw new EngineImportException( + "Could not load single-row function class by name '" + pair.ClassName + "'", ex); + } + return new Pair(clazz, pair); + } + + public MethodInfo ResolveMethodOverloadChecked(string className, string methodName, Type[] paramTypes, bool[] allowEventBeanType, bool[] allowEventBeanCollType) + { + Type clazz; + try { + clazz = ResolveTypeInternal(className, false, false); + } + catch (TypeLoadException e) + { + throw new EngineImportException("Could not load class by name '" + className + "', please check imports", e); + } + + try { + return MethodResolver.ResolveMethod(clazz, methodName, paramTypes, false, allowEventBeanType, allowEventBeanCollType); + } catch (EngineNoSuchMethodException e) { + throw Convert(clazz, methodName, paramTypes, e, false); + } + } + + public ConstructorInfo ResolveCtor(Type clazz, Type[] paramTypes) { + try { + return MethodResolver.ResolveCtor(clazz, paramTypes); + } catch (EngineNoSuchCtorException e) { + throw Convert(clazz, paramTypes, e); + } + } + + public MethodInfo ResolveMethodOverloadChecked(string className, string methodName) { + Type clazz; + try { + clazz = ResolveTypeInternal(className, false, false); + } + catch (TypeLoadException e) + { + throw new EngineImportException("Could not load class by name '" + className + "', please check imports", e); + } + return ResolveMethodInternalCheckOverloads(clazz, methodName, MethodModifiers.REQUIRE_STATIC_AND_PUBLIC); + } + + public MethodInfo ResolveMethodOverloadChecked(Type clazz, string methodName) { + return ResolveMethodInternalCheckOverloads(clazz, methodName, MethodModifiers.REQUIRE_STATIC_AND_PUBLIC); + } + + public MethodInfo ResolveNonStaticMethodOverloadChecked(Type clazz, string methodName) { + return ResolveMethodInternalCheckOverloads(clazz, methodName, MethodModifiers.REQUIRE_NONSTATIC_AND_PUBLIC); + } + + public Type ResolveType(string className, bool forAnnotation) { + Type clazz; + try { + clazz = ResolveTypeInternal(className, false, forAnnotation); + } catch (TypeLoadException e) { + throw new EngineImportException("Could not load class by name '" + className + "', please check imports", e); + } + + return clazz; + } + + public Type ResolveAnnotation(string className) { + Type clazz; + try { + clazz = ResolveTypeInternal(className, true, true); + } + catch (TypeLoadException e) + { + throw new EngineImportException("Could not load annotation class by name '" + className + "', please check imports", e); + } + + return clazz; + } + + /// + /// Finds a class by class name using the auto-import information provided. + /// + /// is the class name to find + /// whether the class must be an annotation + /// whether resolving class for use with annotations + /// class + public Type ResolveTypeInternal(string className, bool requireAnnotation, bool forAnnotationUse) + { + // Attempt to retrieve the class with the name as-is + try { + return GetClassForNameProvider().ClassForName(className); + } + catch (TypeLoadException) + { + if (Log.IsDebugEnabled) { + Log.Debug("Type not found for resolving from name as-is '" + className + "'"); + } + } + + // check annotation-specific imports first + if (forAnnotationUse) { + var clazzInner = CheckImports(_annotationImports, requireAnnotation, className); + if (clazzInner != null) { + return clazzInner; + } + } + + // check all imports + var clazz = CheckImports(_imports, requireAnnotation, className); + if (clazz != null) { + return clazz; + } + + if (!forAnnotationUse) { + // try to resolve from method references + foreach (var name in _methodInvocationRef.Keys) { + if (TypeHelper.IsSimpleNameFullyQualfied(className, name)) { + try { + var found = GetClassForNameProvider().ClassForName(name); + if (!requireAnnotation || found.IsAttribute()) { + return found; + } + } + catch (TypeLoadException) + { + if (Log.IsDebugEnabled) { + Log.Debug("Type not found for resolving from method invocation ref:" + name); + } + } + } + } + } + + // No import worked, the class isn't resolved + throw new TypeLoadException("Unknown class " + className); + } + + public MethodInfo ResolveMethod( + Type clazz, + string methodName, + Type[] paramTypes, + bool[] allowEventBeanType, + bool[] allowEventBeanCollType) + { + try + { + return MethodResolver.ResolveMethod( + clazz, methodName, paramTypes, true, allowEventBeanType, allowEventBeanType); + } + catch (EngineNoSuchMethodException e) + { + var method = MethodResolver.ResolveExtensionMethod( + clazz, methodName, paramTypes, true, allowEventBeanType, allowEventBeanType); + if (method == null) + { + throw Convert(clazz, methodName, paramTypes, e, true); + } + + return method; + } + } + + private EngineImportException Convert( + Type clazz, + string methodName, + Type[] paramTypes, + EngineNoSuchMethodException e, + bool isInstance) + { + var expected = TypeHelper.GetParameterAsString(paramTypes); + var message = "Could not find "; + if (!isInstance) + { + message += "static "; + } + else + { + message += "enumeration method, date-time method or instance "; + } + + if (paramTypes.Length > 0) + { + message += string.Format("method named '{0}' in class '{1}' with matching parameter number and expected parameter type(s) '{2}'", methodName, clazz.GetTypeNameFullyQualPretty(), expected); + } + else + { + message += string.Format("method named '{0}' in class '{1}' taking no parameters", methodName, clazz.GetTypeNameFullyQualPretty()); + } + + if (e.NearestMissMethod != null) + { + message += " (nearest match found was '" + e.NearestMissMethod.Name; + if (e.NearestMissMethod.GetParameterTypes().Length == 0) + { + message += "' taking no parameters"; + } + else + { + message += "' taking type(s) '" + + TypeHelper.GetParameterAsString(e.NearestMissMethod.GetParameterTypes()) + "'"; + } + message += ")"; + } + return new EngineImportException(message, e); + } + + private EngineImportException Convert(Type clazz, Type[] paramTypes, EngineNoSuchCtorException e) + { + var expected = TypeHelper.GetParameterAsString(paramTypes); + var message = "Could not find constructor "; + if (paramTypes.Length > 0) + { + message += "in class '" + clazz.GetTypeNameFullyQualPretty() + + "' with matching parameter number and expected parameter type(s) '" + expected + "'"; + } + else + { + message += "in class '" + clazz.GetTypeNameFullyQualPretty() + "' taking no parameters"; + } + + if (e.NearestMissCtor != null) + { + message += " (nearest matching constructor "; + if (e.NearestMissCtor.GetParameterTypes().Length == 0) + { + message += "taking no parameters"; + } + else + { + message += "taking type(s) '" + + TypeHelper.GetParameterAsString(e.NearestMissCtor.GetParameterTypes()) + "'"; + } + message += ")"; + } + return new EngineImportException(message, e); + } + + public ExprNode ResolveSingleRowExtendedBuiltin(string name) + { + var nameLowerCase = name.ToLowerInvariant(); + if (nameLowerCase.Equals("current_evaluation_context")) + { + return new ExprCurrentEvaluationContextNode(); + } + return null; + } + + public ExprNode ResolveAggExtendedBuiltin(string name, bool isDistinct) + { + if (!_allowExtendedAggregationFunc) + { + return null; + } + + var nameLowerCase = name.ToLowerInvariant(); + switch (nameLowerCase) + { + case ("first"): + return new ExprAggMultiFunctionLinearAccessNode(AggregationStateType.FIRST); + case ("last"): + return new ExprAggMultiFunctionLinearAccessNode(AggregationStateType.LAST); + case ("window"): + return new ExprAggMultiFunctionLinearAccessNode(AggregationStateType.WINDOW); + case ("firstever"): + return new ExprFirstEverNode(isDistinct); + case ("lastever"): + return new ExprLastEverNode(isDistinct); + case ("countever"): + return new ExprCountEverNode(isDistinct); + case ("minever"): + return new ExprMinMaxAggrNode(isDistinct, MinMaxTypeEnum.MIN, false, true); + case ("maxever"): + return new ExprMinMaxAggrNode(isDistinct, MinMaxTypeEnum.MAX, false, true); + case ("fminever"): + return new ExprMinMaxAggrNode(isDistinct, MinMaxTypeEnum.MIN, true, true); + case ("fmaxever"): + return new ExprMinMaxAggrNode(isDistinct, MinMaxTypeEnum.MAX, true, true); + case ("rate"): + return new ExprRateAggNode(isDistinct); + case ("nth"): + return new ExprNthAggNode(isDistinct); + case ("leaving"): + return new ExprLeavingAggNode(isDistinct); + case ("maxby"): + return new ExprAggMultiFunctionSortedMinMaxByNode(true, false, false); + case ("maxbyever"): + return new ExprAggMultiFunctionSortedMinMaxByNode(true, true, false); + case ("minby"): + return new ExprAggMultiFunctionSortedMinMaxByNode(false, false, false); + case ("minbyever"): + return new ExprAggMultiFunctionSortedMinMaxByNode(false, true, false); + case ("sorted"): + return new ExprAggMultiFunctionSortedMinMaxByNode(false, false, true); + } + + var cmsType = CountMinSketchAggTypeExtensions.FromNameMayMatch(nameLowerCase); + if (cmsType != null) + { + return new ExprAggCountMinSketchNode(isDistinct, cmsType.Value); + } + return null; + } + + public MathContext DefaultMathContext + { + get { return _optionalDefaultMathContext; } + } + + public TimeZoneInfo TimeZone + { + get { return _timeZone; } + } + + public TimeAbacus TimeAbacus + { + get { return _timeAbacus; } + } + + public ConfigurationEngineDefaults.ThreadingProfile ThreadingProfile + { + get { return _threadingProfile; } + } + + public bool IsSortUsingCollator + { + get { return _sortUsingCollator; } + } + + public AggregationFactoryFactory AggregationFactoryFactory + { + get { return _aggregationFactoryFactory; } + } + + /// + /// For testing, returns imports. + /// + /// returns auto-import list as array + public AutoImportDesc[] Imports + { + get { return _imports.ToArray(); } + } + + private void ValidateFunctionName(string functionType, string functionName) + { + var functionNameLower = functionName.ToLowerInvariant(); + if (_aggregationFunctions.ContainsKey(functionNameLower)) + { + throw new EngineImportException( + "Aggregation function by name '" + functionName + "' is already defined"); + } + if (_singleRowFunctions.ContainsKey(functionNameLower)) + { + throw new EngineImportException("Single-row function by name '" + functionName + "' is already defined"); + } + if (_aggregationAccess.Any(pairs => pairs.First.Contains(functionNameLower))) + { + throw new EngineImportException( + "Aggregation multi-function by name '" + functionName + "' is already defined"); + } + if (!IsFunctionName(functionName)) + { + throw new EngineImportException("Invalid " + functionType + " name '" + functionName + "'"); + } + } + + private static readonly Regex FunctionRegEx1 = new Regex(@"^\w+$", RegexOptions.None); + + private static bool IsFunctionName(String functionName) + { + return FunctionRegEx1.IsMatch(functionName); + } + + private static readonly Regex TypeNameRegEx1 = new Regex(@"^(\w+\.)*\w+$", RegexOptions.None); + private static readonly Regex TypeNameRegEx2 = new Regex(@"^(\w+\.)*\w+\+(\w+|)$", RegexOptions.None); + + private static bool IsTypeName(String importName) + { + if (TypeNameRegEx1.IsMatch(importName) || TypeNameRegEx2.IsMatch(importName)) + return true; + + return TypeHelper.ResolveType(importName, false) != null; + } + + private static bool IsTypeNameOrNamespace(String importName) + { + if (TypeNameRegEx1.IsMatch(importName) || TypeNameRegEx2.IsMatch(importName)) + return true; + + return TypeHelper.ResolveType(importName, false) != null; + } + + public bool IsStrictTypeMatch(AutoImportDesc importDesc, String typeName) + { + var importName = importDesc.TypeOrNamespace; + if (importName == typeName) + { + return true; + } + + var lastIndex = importName.LastIndexOf(typeName); + if (lastIndex == -1) + { + return false; + } + + if ((importName[lastIndex - 1] == '.') || + (importName[lastIndex - 1] == '+')) + { + return true; + } + + return false; + } + + + private MethodInfo ResolveMethodInternalCheckOverloads(Type clazz, string methodName, MethodModifiers methodModifiers) + { + MethodInfo[] methods = clazz.GetMethods(); + ISet overloadeds = null; + MethodInfo methodByName = null; + + // check each method by name, add to overloads when multiple methods for the same name + foreach (var method in methods) + { + if (method.Name == methodName) + { + var isPublic = method.IsPublic; + var isStatic = method.IsStatic; + if (methodModifiers.AcceptsPublicFlag(isPublic) && + methodModifiers.AcceptsStaticFlag(isStatic)) + { + if (methodByName != null) + { + if (overloadeds == null) + { + overloadeds = new HashSet(); + } + overloadeds.Add(method); + } + else + { + methodByName = method; + } + } + } + } + if (methodByName == null) { + throw new EngineImportException("Could not find " + methodModifiers.Text + " method named '" + methodName + "' in class '" + clazz.FullName + "'"); + } + if (overloadeds == null) { + return methodByName; + } + + // determine that all overloads have the same result type + if (overloadeds.Any(overloaded => overloaded.ReturnType != methodByName.ReturnType)) + { + throw new EngineImportException("Method by name '" + methodName + "' is overloaded in class '" + clazz.FullName + "' and overloaded methods do not return the same type"); + } + + return methodByName; + } + + private Type CheckImports(IList imports, bool requireAnnotation, String typeName) + { + foreach (var importDesc in imports) + { + var importName = importDesc.TypeOrNamespace; + + // Test as a class name + if (IsStrictTypeMatch(importDesc, typeName)) + { + var type = TypeHelper.ResolveType(importName, importDesc.AssemblyNameOrFile); + if (type != null) + { + return type; + } + + Log.Warn("Type not found for resolving from name as-is: '{0}'", typeName); + } + else + { + if (requireAnnotation && (importName == Configuration.ANNOTATION_IMPORT)) + { + var clazz = BuiltinAnnotation.BUILTIN.Get(typeName.ToLower()); + if (clazz != null) + { + return clazz; + } + } + + if (importDesc.TypeOrNamespace.EndsWith("." + typeName) || + importDesc.TypeOrNamespace.EndsWith("+" + typeName) || + importDesc.TypeOrNamespace.Equals(typeName)) + { + try + { + var type = TypeHelper.ResolveType(importDesc.TypeOrNamespace, importDesc.AssemblyNameOrFile); + if (type != null) + { + return type; + } + + Log.Warn("Type not found for resolving from name as-is: {0}", typeName); + } + catch (TypeLoadException) + { + } + } + + // Import is a namespace + var prefixedClassName = importDesc.TypeOrNamespace + '.' + typeName; + + try + { + var type = TypeHelper.ResolveType(prefixedClassName, importDesc.AssemblyNameOrFile); + if (type != null) + { + return type; + } + + Log.Warn("Type not found for resolving from name as-is: {0}", typeName); + } + catch (TypeLoadException) + { + } + + prefixedClassName = importDesc.TypeOrNamespace + '+' + typeName; + + try + { + var type = TypeHelper.ResolveType(prefixedClassName, importDesc.AssemblyNameOrFile); + if (type != null) + { + return type; + } + + Log.Warn("Type not found for resolving from name as-is: {0}", typeName); + } + catch (TypeLoadException) + { + } + + // Import is a type + } + } + + return null; + } + + private void ValidateImportAndAdd(AutoImportDesc autoImportDesc, ICollection imports) + { + if (!IsTypeNameOrNamespace(autoImportDesc.TypeOrNamespace)) + { + throw new EngineImportException("Invalid import name '" + autoImportDesc + "'"); + } + + Log.Debug("Adding import {0}", autoImportDesc); + + imports.Add(autoImportDesc); + } + + internal class MethodModifiers + { + public static readonly MethodModifiers REQUIRE_STATIC_AND_PUBLIC = + new MethodModifiers("public static", true); + + public static readonly MethodModifiers REQUIRE_NONSTATIC_AND_PUBLIC = + new MethodModifiers("public non-static", false); + + private readonly String _text; + private readonly bool _requiredStaticFlag; + + MethodModifiers(String text, bool requiredStaticFlag) + { + _text = text; + _requiredStaticFlag = requiredStaticFlag; + } + + public bool AcceptsPublicFlag(bool isPublic) + { + return isPublic; + } + + public bool AcceptsStaticFlag(bool isStatic) + { + return _requiredStaticFlag == isStatic; + } + + public string Text + { + get { return _text; } + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/EngineImportSingleRowDesc.cs b/NEsper.Core/NEsper.Core/epl/core/EngineImportSingleRowDesc.cs new file mode 100755 index 000000000..a9d14f0a7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/EngineImportSingleRowDesc.cs @@ -0,0 +1,47 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.epl.core +{ + /// Provides information about a single-row function. + [Serializable] + public class EngineImportSingleRowDesc + { + public EngineImportSingleRowDesc( + string className, + string methodName, + ValueCacheEnum valueCache, + FilterOptimizableEnum filterOptimizable, + bool rethrowExceptions, + string optionalEventTypeName) + { + ClassName = className; + MethodName = methodName; + ValueCache = valueCache; + FilterOptimizable = filterOptimizable; + IsRethrowExceptions = rethrowExceptions; + OptionalEventTypeName = optionalEventTypeName; + } + + public string ClassName { get; private set; } + + public string MethodName { get; private set; } + + public ValueCacheEnum ValueCache { get; private set; } + + public FilterOptimizableEnum FilterOptimizable { get; private set; } + + public bool IsRethrowExceptions { get; private set; } + + public string OptionalEventTypeName { get; private set; } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/EngineImportUndefinedException.cs b/NEsper.Core/NEsper.Core/epl/core/EngineImportUndefinedException.cs new file mode 100755 index 000000000..5da19b9f2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/EngineImportUndefinedException.cs @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.core +{ + + /// + /// Indicates a problem importing classes, aggregation functions and the like. + /// + public class EngineImportUndefinedException : Exception + { + /// Ctor. + /// exception message + public EngineImportUndefinedException(String msg) + : base(msg) + { + } + + /// Ctor. + /// exception message + /// inner exception + public EngineImportUndefinedException(String msg, Exception ex) + : base(msg, ex) + { + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/EngineNoSuchCtorException.cs b/NEsper.Core/NEsper.Core/epl/core/EngineNoSuchCtorException.cs new file mode 100755 index 000000000..1c56fc3b8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/EngineNoSuchCtorException.cs @@ -0,0 +1,39 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +namespace com.espertech.esper.epl.core +{ + /// + /// Exception for resolution of a method failed. + /// + [Serializable] + public class EngineNoSuchCtorException : Exception + { + [NonSerialized] + private readonly ConstructorInfo _nearestMissCtor; + + /// Ctor. + /// message + /// best-match method + public EngineNoSuchCtorException(String message, ConstructorInfo nearestMissCtor) + : base(message) + { + _nearestMissCtor = nearestMissCtor; + } + + /// Returns the best-match ctor. + /// ctor + public ConstructorInfo NearestMissCtor + { + get { return _nearestMissCtor; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/EngineNoSuchMethodException.cs b/NEsper.Core/NEsper.Core/epl/core/EngineNoSuchMethodException.cs new file mode 100755 index 000000000..2552b4396 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/EngineNoSuchMethodException.cs @@ -0,0 +1,36 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +namespace com.espertech.esper.epl.core +{ + /// Exception for resolution of a method failed. + public class EngineNoSuchMethodException : Exception + { + [NonSerialized] + private readonly MethodInfo _nearestMissMethod; + /// Ctor. + /// message + /// best-match method + public EngineNoSuchMethodException(String message, MethodInfo nearestMissMethod) + + : base(message) + { + _nearestMissMethod = nearestMissMethod; + } + + /// Returns the best-match method. + /// method + public MethodInfo NearestMissMethod + { + get { return _nearestMissMethod; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/EngineSettingsService.cs b/NEsper.Core/NEsper.Core/epl/core/EngineSettingsService.cs new file mode 100755 index 000000000..6f07b72ad --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/EngineSettingsService.cs @@ -0,0 +1,43 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; + +namespace com.espertech.esper.epl.core +{ + /// + /// Service for engine-level settings around threading and concurrency. + /// + public class EngineSettingsService + { + /// Ctor. + /// is the configured defaults + /// is URIs for resolving the event name against plug-inn event representations, if any + public EngineSettingsService(ConfigurationEngineDefaults config, IList plugInEventTypeResolutionURIs) + { + EngineSettings = config; + PlugInEventTypeResolutionURIs = plugInEventTypeResolutionURIs; + } + + /// + /// Gets the engine settings. + /// + /// The engine settings. + public ConfigurationEngineDefaults EngineSettings { get; private set; } + + /// + /// Gets or sets the URIs for resolving the event name against plug-in event representations, if any. + /// + /// The plug in event type resolution UR is. + /// URIs + public IList PlugInEventTypeResolutionURIs { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/GroupByRollupInfo.cs b/NEsper.Core/NEsper.Core/epl/core/GroupByRollupInfo.cs new file mode 100755 index 000000000..db200087d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/GroupByRollupInfo.cs @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.core +{ + public class GroupByRollupInfo + { + public ExprNode[] ExprNodes { get; private set; } + + public AggregationGroupByRollupDesc RollupDesc { get; private set; } + + public GroupByRollupInfo(ExprNode[] exprNodes, AggregationGroupByRollupDesc rollupDesc) + { + ExprNodes = exprNodes; + RollupDesc = rollupDesc; + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/core/MethodPollingExecStrategyBase.cs b/NEsper.Core/NEsper.Core/epl/core/MethodPollingExecStrategyBase.cs new file mode 100755 index 000000000..76a1ee511 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/MethodPollingExecStrategyBase.cs @@ -0,0 +1,135 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.variable; + +using com.espertech.esper.client; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.db; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.core +{ + /// + /// Viewable providing historical data from a database. + /// + public abstract class MethodPollingExecStrategyBase : PollExecStrategy + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + protected readonly EventAdapterService EventAdapterService; + protected readonly MethodInfo Method; + protected readonly EventType EventType; + protected readonly Object InvocationTarget; + protected readonly MethodPollingExecStrategyEnum Strategy; + protected readonly VariableReader VariableReader; + protected readonly String VariableName; + protected readonly VariableService VariableService; + + protected MethodPollingExecStrategyBase( + EventAdapterService eventAdapterService, + MethodInfo method, + EventType eventType, + Object invocationTarget, + MethodPollingExecStrategyEnum strategy, + VariableReader variableReader, + String variableName, + VariableService variableService) + { + EventAdapterService = eventAdapterService; + Method = method; + EventType = eventType; + InvocationTarget = invocationTarget; + Strategy = strategy; + VariableReader = variableReader; + VariableName = variableName; + VariableService = variableService; + } + + protected abstract IList HandleResult(Object invocationResult); + + protected bool CheckNonNullArrayValue(Object value) + { + if (value == null) + { + Log.Warn("Expected non-null return result from method '" + Method.Name + "', but received null array element value"); + return false; + } + return true; + } + + public void Start() + { + } + + public void Done() + { + } + + public void Dispose() + { + } + + public IList Poll(Object[] lookupValues, ExprEvaluatorContext exprEvaluatorContext) + { + switch (Strategy) + { + case MethodPollingExecStrategyEnum.TARGET_CONST: + return InvokeInternal(lookupValues, InvocationTarget); + case MethodPollingExecStrategyEnum.TARGET_VAR: + return InvokeInternalVariable(lookupValues, VariableReader); + case MethodPollingExecStrategyEnum.TARGET_VAR_CONTEXT: + var reader = VariableService.GetReader(VariableName, exprEvaluatorContext.AgentInstanceId); + if (reader == null) + { + return null; + } + return InvokeInternalVariable(lookupValues, reader); + default: + throw new NotSupportedException("unrecognized strategy " + Strategy); + } + } + + private IList InvokeInternalVariable(Object[] lookupValues, VariableReader variableReader) + { + var target = variableReader.Value; + if (target == null) + { + return null; + } + if (target is EventBean) + { + target = ((EventBean)target).Underlying; + } + return InvokeInternal(lookupValues, target); + } + + private IList InvokeInternal(Object[] lookupValues, Object invocationTarget) + { + try + { + var invocationResult = Method.Invoke(invocationTarget, lookupValues); + if (invocationResult != null) + { + return HandleResult(invocationResult); + } + return null; + } + catch (TargetException ex) + { + throw new EPException("Method '" + Method.Name + "' of class '" + Method.DeclaringType.FullName + + "' reported an exception: " + ex.InnerException, ex.InnerException); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/MethodPollingExecStrategyBaseArray.cs b/NEsper.Core/NEsper.Core/epl/core/MethodPollingExecStrategyBaseArray.cs new file mode 100755 index 000000000..bdf00432f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/MethodPollingExecStrategyBaseArray.cs @@ -0,0 +1,61 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.epl.variable; + +using XLR8.CGLib; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.core +{ + public abstract class MethodPollingExecStrategyBaseArray : MethodPollingExecStrategyBase + { + protected MethodPollingExecStrategyBaseArray(EventAdapterService eventAdapterService, FastMethod method, EventType eventType, Object invocationTarget, MethodPollingExecStrategyEnum strategy, VariableReader variableReader, String variableName, VariableService variableService) + : base(eventAdapterService, method, eventType, invocationTarget, strategy, variableReader, variableName, variableService) + { + } + + + protected abstract EventBean GetEventBean(Object value); + + protected override IList HandleResult(Object invocationResult) + { + var array = (Array) invocationResult; + + int length = array.Length; + if (length == 0) { + return Collections.GetEmptyList(); + } + if (length == 1) { + var value = array.GetValue(0); + if (CheckNonNullArrayValue(value)) { + var @event = GetEventBean(value); + return Collections.SingletonList(@event); + } + return Collections.GetEmptyList(); + } + + var rowResult = new List(length); + for (int i = 0; i < length; i++) + { + var value = array.GetValue(i); + if (CheckNonNullArrayValue(value)) { + EventBean @event = GetEventBean(value); + rowResult.Add(@event); + } + } + return rowResult; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/MethodPollingExecStrategyBaseCollection.cs b/NEsper.Core/NEsper.Core/epl/core/MethodPollingExecStrategyBaseCollection.cs new file mode 100755 index 000000000..68601a49f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/MethodPollingExecStrategyBaseCollection.cs @@ -0,0 +1,58 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.variable; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.core +{ + public abstract class MethodPollingExecStrategyBaseCollection : MethodPollingExecStrategyBase + { + public MethodPollingExecStrategyBaseCollection(EventAdapterService eventAdapterService, MethodInfo method, EventType eventType, object invocationTarget, MethodPollingExecStrategyEnum strategy, VariableReader variableReader, string variableName, VariableService variableService) + : base(eventAdapterService, method, eventType, invocationTarget, strategy, variableReader, variableName, variableService) + { + } + + protected abstract EventBean GetEventBean(object value); + + protected override IList HandleResult(object invocationResult) + { + var collection = invocationResult.Unwrap(); + var length = collection.Count; + if (length == 0) { + return Collections.GetEmptyList(); + } + if (length == 1) { + var value = collection.First(); + if (CheckNonNullArrayValue(value)) { + var @event = GetEventBean(value); + return Collections.SingletonList(@event); + } + return Collections.GetEmptyList(); + } + var rowResult = new List(length); + var it = collection.GetEnumerator(); + while (it.MoveNext()) { + object value = it.Current; + if (CheckNonNullArrayValue(value)) { + var @event = GetEventBean(value); + rowResult.Add(@event); + } + } + return rowResult; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/MethodPollingExecStrategyBaseIterator.cs b/NEsper.Core/NEsper.Core/epl/core/MethodPollingExecStrategyBaseIterator.cs new file mode 100755 index 000000000..d1127f2a2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/MethodPollingExecStrategyBaseIterator.cs @@ -0,0 +1,57 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.variable; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.core +{ + public abstract class MethodPollingExecStrategyBaseIterator : MethodPollingExecStrategyBase + { + public MethodPollingExecStrategyBaseIterator(EventAdapterService eventAdapterService, MethodInfo method, EventType eventType, object invocationTarget, MethodPollingExecStrategyEnum strategy, VariableReader variableReader, string variableName, VariableService variableService) + : base(eventAdapterService, method, eventType, invocationTarget, strategy, variableReader, variableName, variableService) + { + } + + protected abstract EventBean GetEventBean(object value); + + protected override IList HandleResult(object invocationResult) + { + var enumerator = invocationResult as System.Collections.IEnumerator; + if (enumerator == null) + { + var enumerable = invocationResult as System.Collections.IEnumerable; + if (enumerable == null) + { + return Collections.GetEmptyList(); + //throw new ArgumentException("invalid input - not enumerable", "invocationResult"); + } + + enumerator = enumerable.GetEnumerator(); + } + + var rowResult = new List(2); + while (enumerator.MoveNext()) + { + var value = enumerator.Current; + if (CheckNonNullArrayValue(value)) + { + var @event = GetEventBean(value); + rowResult.Add(@event); + } + } + return rowResult; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/MethodPollingExecStrategyEnum.cs b/NEsper.Core/NEsper.Core/epl/core/MethodPollingExecStrategyEnum.cs new file mode 100755 index 000000000..4c51e5967 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/MethodPollingExecStrategyEnum.cs @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; + +namespace com.espertech.esper.epl.core +{ + public enum MethodPollingExecStrategyEnum { + TARGET_CONST, + TARGET_VAR, + TARGET_VAR_CONTEXT, + TARGET_SCRIPT + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/MethodPollingExecStrategyEventBeans.cs b/NEsper.Core/NEsper.Core/epl/core/MethodPollingExecStrategyEventBeans.cs new file mode 100755 index 000000000..5472efe30 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/MethodPollingExecStrategyEventBeans.cs @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.variable; +using com.espertech.esper.events; + +using XLR8.CGLib; + +namespace com.espertech.esper.epl.core +{ + public class MethodPollingExecStrategyEventBeans : MethodPollingExecStrategyBase + { + public MethodPollingExecStrategyEventBeans( + EventAdapterService eventAdapterService, + FastMethod method, + EventType eventType, + Object invocationTarget, + MethodPollingExecStrategyEnum strategy, + VariableReader variableReader, + string variableName, + VariableService variableService) + : base(eventAdapterService, method, eventType, invocationTarget, strategy, variableReader, variableName, variableService) + { + } + + protected override IList HandleResult(Object invocationResult) + { + if (invocationResult == null) { + return Collections.GetEmptyList(); + } + + return invocationResult + .UnwrapIntoList(includeNullValues: false); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/MethodPollingExecStrategyMapArray.cs b/NEsper.Core/NEsper.Core/epl/core/MethodPollingExecStrategyMapArray.cs new file mode 100755 index 000000000..1d33c6ca5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/MethodPollingExecStrategyMapArray.cs @@ -0,0 +1,46 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.variable; + +using XLR8.CGLib; + +using com.espertech.esper.client; +using com.espertech.esper.compat.logging; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.core +{ + public class MethodPollingExecStrategyMapArray : MethodPollingExecStrategyBaseArray + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public MethodPollingExecStrategyMapArray(EventAdapterService eventAdapterService, FastMethod method, EventType eventType, Object invocationTarget, MethodPollingExecStrategyEnum strategy, VariableReader variableReader, String variableName, VariableService variableService) + : base(eventAdapterService, method, eventType, invocationTarget, strategy, variableReader, variableName, variableService) + { + } + + protected override EventBean GetEventBean(Object value) + { + try + { + var valueDataMap = value.UnwrapDictionary(); + return EventAdapterService.AdapterForTypedMap(valueDataMap, EventType); + } + catch (ArgumentException) + { + Log.Warn("Expected map-type for value, but received type '" + value.GetType() + "'"); + throw new EPException("Expected map-type for value, but received type '" + value.GetType() + "'"); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/MethodPollingExecStrategyMapCollection.cs b/NEsper.Core/NEsper.Core/epl/core/MethodPollingExecStrategyMapCollection.cs new file mode 100755 index 000000000..72764a23c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/MethodPollingExecStrategyMapCollection.cs @@ -0,0 +1,31 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.variable; +using com.espertech.esper.events; + +using XLR8.CGLib; + +namespace com.espertech.esper.epl.core +{ + public class MethodPollingExecStrategyMapCollection : MethodPollingExecStrategyBaseCollection + { + public MethodPollingExecStrategyMapCollection(EventAdapterService eventAdapterService, FastMethod method, EventType eventType, object invocationTarget, MethodPollingExecStrategyEnum strategy, VariableReader variableReader, string variableName, VariableService variableService) + : base(eventAdapterService, method, eventType, invocationTarget, strategy, variableReader, variableName, variableService) + { + } + + protected override EventBean GetEventBean(object value) + { + var valueDataMap = value.UnwrapDictionary(); + return EventAdapterService.AdapterForTypedMap(valueDataMap, EventType); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/MethodPollingExecStrategyMapIterator.cs b/NEsper.Core/NEsper.Core/epl/core/MethodPollingExecStrategyMapIterator.cs new file mode 100755 index 000000000..050f0f81c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/MethodPollingExecStrategyMapIterator.cs @@ -0,0 +1,31 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.variable; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.core +{ + public class MethodPollingExecStrategyMapIterator : MethodPollingExecStrategyBaseIterator + { + public MethodPollingExecStrategyMapIterator(EventAdapterService eventAdapterService, MethodInfo method, EventType eventType, object invocationTarget, MethodPollingExecStrategyEnum strategy, VariableReader variableReader, string variableName, VariableService variableService) + : base(eventAdapterService, method, eventType, invocationTarget, strategy, variableReader, variableName, variableService) + { + } + + protected override EventBean GetEventBean(object value) + { + var valueDataMap = value.UnwrapDictionary(); + return EventAdapterService.AdapterForTypedMap(valueDataMap, EventType); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/MethodPollingExecStrategyMapPlain.cs b/NEsper.Core/NEsper.Core/epl/core/MethodPollingExecStrategyMapPlain.cs new file mode 100755 index 000000000..666c1ae8f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/MethodPollingExecStrategyMapPlain.cs @@ -0,0 +1,33 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.variable; +using com.espertech.esper.events; + +using XLR8.CGLib; + +namespace com.espertech.esper.epl.core +{ + public class MethodPollingExecStrategyMapPlain : MethodPollingExecStrategyBase + { + public MethodPollingExecStrategyMapPlain(EventAdapterService eventAdapterService, FastMethod method, EventType eventType, Object invocationTarget, MethodPollingExecStrategyEnum strategy, VariableReader variableReader, String variableName, VariableService variableService) + : base(eventAdapterService, method, eventType, invocationTarget, strategy, variableReader, variableName, variableService) + { + } + + protected override IList HandleResult(Object invocationResult) + { + return Collections.SingletonList(EventAdapterService.AdapterForTypedMap((IDictionary) invocationResult, EventType)); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/MethodPollingExecStrategyOAArray.cs b/NEsper.Core/NEsper.Core/epl/core/MethodPollingExecStrategyOAArray.cs new file mode 100755 index 000000000..57c31c48f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/MethodPollingExecStrategyOAArray.cs @@ -0,0 +1,31 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.variable; +using com.espertech.esper.events; + +using XLR8.CGLib; + +namespace com.espertech.esper.epl.core +{ + public class MethodPollingExecStrategyOAArray : MethodPollingExecStrategyBaseArray + { + public MethodPollingExecStrategyOAArray(EventAdapterService eventAdapterService, FastMethod method, EventType eventType, Object invocationTarget, MethodPollingExecStrategyEnum strategy, VariableReader variableReader, String variableName, VariableService variableService) + : base(eventAdapterService, method, eventType, invocationTarget, strategy, variableReader, variableName, variableService) + { + } + + protected override EventBean GetEventBean(Object value) + { + return EventAdapterService.AdapterForTypedObjectArray((Object[]) value, EventType); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/MethodPollingExecStrategyOACollection.cs b/NEsper.Core/NEsper.Core/epl/core/MethodPollingExecStrategyOACollection.cs new file mode 100755 index 000000000..1c0aadbd3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/MethodPollingExecStrategyOACollection.cs @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.epl.variable; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.core +{ + public class MethodPollingExecStrategyOACollection : MethodPollingExecStrategyBaseCollection + { + public MethodPollingExecStrategyOACollection(EventAdapterService eventAdapterService, MethodInfo method, EventType eventType, object invocationTarget, MethodPollingExecStrategyEnum strategy, VariableReader variableReader, string variableName, VariableService variableService) + : base(eventAdapterService, method, eventType, invocationTarget, strategy, variableReader, variableName, variableService) + { + } + + protected override EventBean GetEventBean(object value) + { + return EventAdapterService.AdapterForTypedObjectArray((object[]) value, EventType); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/MethodPollingExecStrategyOAIterator.cs b/NEsper.Core/NEsper.Core/epl/core/MethodPollingExecStrategyOAIterator.cs new file mode 100755 index 000000000..f4cab4b39 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/MethodPollingExecStrategyOAIterator.cs @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.epl.variable; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.core +{ + public class MethodPollingExecStrategyOAIterator : MethodPollingExecStrategyBaseIterator + { + public MethodPollingExecStrategyOAIterator(EventAdapterService eventAdapterService, MethodInfo method, EventType eventType, object invocationTarget, MethodPollingExecStrategyEnum strategy, VariableReader variableReader, string variableName, VariableService variableService) + : base(eventAdapterService, method, eventType, invocationTarget, strategy, variableReader, variableName, variableService) + { + } + + protected override EventBean GetEventBean(object value) + { + return EventAdapterService.AdapterForTypedObjectArray((object[]) value, EventType); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/MethodPollingExecStrategyOAPlain.cs b/NEsper.Core/NEsper.Core/epl/core/MethodPollingExecStrategyOAPlain.cs new file mode 100755 index 000000000..ee8d00e15 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/MethodPollingExecStrategyOAPlain.cs @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.variable; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.core +{ + public class MethodPollingExecStrategyOAPlain : MethodPollingExecStrategyBase + { + public MethodPollingExecStrategyOAPlain(EventAdapterService eventAdapterService, MethodInfo method, EventType eventType, Object invocationTarget, MethodPollingExecStrategyEnum strategy, VariableReader variableReader, String variableName, VariableService variableService) + : base(eventAdapterService, method, eventType, invocationTarget, strategy, variableReader, variableName, variableService) + { + } + + protected override IList HandleResult(Object invocationResult) + { + return Collections.SingletonList(EventAdapterService.AdapterForTypedObjectArray((Object[]) invocationResult, EventType)); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/MethodPollingExecStrategyPONOArray.cs b/NEsper.Core/NEsper.Core/epl/core/MethodPollingExecStrategyPONOArray.cs new file mode 100755 index 000000000..e224c2e80 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/MethodPollingExecStrategyPONOArray.cs @@ -0,0 +1,30 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.epl.variable; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.core +{ + public class MethodPollingExecStrategyPONOArray : MethodPollingExecStrategyBaseArray + { + public MethodPollingExecStrategyPONOArray(EventAdapterService eventAdapterService, MethodInfo method, EventType eventType, Object invocationTarget, MethodPollingExecStrategyEnum strategy, VariableReader variableReader, String variableName, VariableService variableService) + : base(eventAdapterService, method, eventType, invocationTarget, strategy, variableReader, variableName, variableService) + { + } + + protected override EventBean GetEventBean(Object value) + { + return EventAdapterService.AdapterForObject(value); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/MethodPollingExecStrategyPONOCollection.cs b/NEsper.Core/NEsper.Core/epl/core/MethodPollingExecStrategyPONOCollection.cs new file mode 100755 index 000000000..ef0864353 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/MethodPollingExecStrategyPONOCollection.cs @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.epl.variable; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.core +{ + public class MethodPollingExecStrategyPONOCollection : MethodPollingExecStrategyBaseCollection + { + public MethodPollingExecStrategyPONOCollection(EventAdapterService eventAdapterService, MethodInfo method, EventType eventType, object invocationTarget, MethodPollingExecStrategyEnum strategy, VariableReader variableReader, string variableName, VariableService variableService) + : base(eventAdapterService, method, eventType, invocationTarget, strategy, variableReader, variableName, variableService) + { + } + + protected override EventBean GetEventBean(object value) + { + return EventAdapterService.AdapterForObject(value); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/MethodPollingExecStrategyPONOIterator.cs b/NEsper.Core/NEsper.Core/epl/core/MethodPollingExecStrategyPONOIterator.cs new file mode 100755 index 000000000..b54458a49 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/MethodPollingExecStrategyPONOIterator.cs @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.epl.variable; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.core +{ + public class MethodPollingExecStrategyPONOIterator : MethodPollingExecStrategyBaseIterator + { + public MethodPollingExecStrategyPONOIterator(EventAdapterService eventAdapterService, MethodInfo method, EventType eventType, object invocationTarget, MethodPollingExecStrategyEnum strategy, VariableReader variableReader, string variableName, VariableService variableService) + : base(eventAdapterService, method, eventType, invocationTarget, strategy, variableReader, variableName, variableService) + { + } + + protected override EventBean GetEventBean(object value) + { + return EventAdapterService.AdapterForObject(value); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/MethodPollingExecStrategyPONOPlain.cs b/NEsper.Core/NEsper.Core/epl/core/MethodPollingExecStrategyPONOPlain.cs new file mode 100755 index 000000000..d568aa2a5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/MethodPollingExecStrategyPONOPlain.cs @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.variable; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.core +{ + public class MethodPollingExecStrategyPONOPlain : MethodPollingExecStrategyBase + { + public MethodPollingExecStrategyPONOPlain(EventAdapterService eventAdapterService, MethodInfo method, EventType eventType, Object invocationTarget, MethodPollingExecStrategyEnum strategy, VariableReader variableReader, String variableName, VariableService variableService) + : base(eventAdapterService, method, eventType, invocationTarget, strategy, variableReader, variableName, variableService) + { + } + + protected override IList HandleResult(Object invocationResult) + { + return Collections.SingletonList(EventAdapterService.AdapterForObject(invocationResult)); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/MethodPollingExecStrategyScript.cs b/NEsper.Core/NEsper.Core/epl/core/MethodPollingExecStrategyScript.cs new file mode 100755 index 000000000..041a3137c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/MethodPollingExecStrategyScript.cs @@ -0,0 +1,58 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.db; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.script; + +namespace com.espertech.esper.epl.core +{ + public class MethodPollingExecStrategyScript : PollExecStrategy + { + private static readonly ILog Log = + LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly ExprNodeScriptEvaluator _eval; + + public MethodPollingExecStrategyScript(ExprNodeScript scriptExpression, EventType eventTypeEventBeanArray) + { + _eval = (ExprNodeScriptEvaluator) scriptExpression.ExprEvaluator; + } + + public void Start() + { + } + + public IList Poll(Object[] lookupValues, ExprEvaluatorContext exprEvaluatorContext) + { + var result = _eval.Evaluate(lookupValues, exprEvaluatorContext); + if (!(result is EventBean[])) + { + Log.Warn( + "Script expected return type EventBean[] does not match result {0}", + (result == null ? "null" : result.GetType().FullName)); + return Collections.GetEmptyList(); + } + return result.UnwrapIntoList(); + } + + public void Done() + { + } + + public void Dispose() + { + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/MethodPollingViewable.cs b/NEsper.Core/NEsper.Core/epl/core/MethodPollingViewable.cs new file mode 100755 index 000000000..729c65985 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/MethodPollingViewable.cs @@ -0,0 +1,326 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.threading; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.db; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.visitor; +using com.espertech.esper.epl.@join.pollindex; +using com.espertech.esper.epl.@join.table; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.epl.variable; +using com.espertech.esper.events; +using com.espertech.esper.schedule; +using com.espertech.esper.script; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.epl.core +{ + /// + /// Polling-data provider that calls a static method on a class and passed parameters, and wraps the + /// results as events. + /// + public class MethodPollingViewable : HistoricalEventViewable + { + private static readonly EventBean[][] NULL_ROWS; + private static readonly PollResultIndexingStrategy ITERATOR_INDEXING_STRATEGY = new ProxyPollResultIndexingStrategy + { + ProcIndex = (pollResult, isActiveCache, statementContext) => new EventTable[]{ new UnindexedEventTableList(pollResult, -1) }, + ProcToQueryPlan = () => typeof (MethodPollingViewable).Name + " unindexed" + }; + + static MethodPollingViewable() + { + NULL_ROWS = new EventBean[1][]; + NULL_ROWS[0] = new EventBean[1]; + } + + private readonly MethodStreamSpec _methodStreamSpec; + private readonly DataCache _dataCache; + private readonly EventType _eventType; + private readonly IThreadLocal _dataCacheThreadLocal = ThreadLocalManager.Create(() => null); + private readonly ExprEvaluatorContext _exprEvaluatorContext; + private readonly MethodPollingViewableMeta _metadata; + private PollExecStrategy _pollExecStrategy; + private SortedSet _requiredStreams; + private ExprEvaluator[] _validatedExprNodes; + private StatementContext _statementContext; + + public MethodPollingViewable( + MethodStreamSpec methodStreamSpec, + DataCache dataCache, + EventType eventType, + ExprEvaluatorContext exprEvaluatorContext, + MethodPollingViewableMeta metadata) + { + _methodStreamSpec = methodStreamSpec; + _dataCache = dataCache; + _eventType = eventType; + _exprEvaluatorContext = exprEvaluatorContext; + _metadata = metadata; + } + + public void Stop() { + _pollExecStrategy.Dispose(); + _dataCache.Dispose(); + } + + public IThreadLocal DataCacheThreadLocal + { + get { return _dataCacheThreadLocal; } + } + + public DataCache OptionalDataCache + { + get { return _dataCache; } + } + + public void Validate( + EngineImportService engineImportService, + StreamTypeService streamTypeService, + TimeProvider timeProvider, + VariableService variableService, + TableService tableService, + ScriptingService scriptingService, + ExprEvaluatorContext exprEvaluatorContext, + ConfigurationInformation configSnapshot, + SchedulingService schedulingService, + string engineURI, + IDictionary> sqlParameters, + EventAdapterService eventAdapterService, + StatementContext statementContext) + { + _statementContext = statementContext; + + // validate and visit + var validationContext = new ExprValidationContext(streamTypeService, engineImportService, statementContext.StatementExtensionServicesContext, null, timeProvider, variableService, tableService, exprEvaluatorContext, eventAdapterService, statementContext.StatementName, statementContext.StatementId, statementContext.Annotations, null, statementContext.ScriptingService, false, false, true, false, null, false); + var visitor = new ExprNodeIdentifierVisitor(true); + var validatedInputParameters = new List(); + foreach (var exprNode in _methodStreamSpec.Expressions) { + var validated = ExprNodeUtility.GetValidatedSubtree(ExprNodeOrigin.METHODINVJOIN, exprNode, validationContext); + validatedInputParameters.Add(validated); + validated.Accept(visitor); + } + + // determine required streams + _requiredStreams = new SortedSet(); + foreach (var identifier in visitor.ExprProperties) { + _requiredStreams.Add(identifier.First); + } + + // class-based evaluation + if (_metadata.MethodProviderClass != null) { + // resolve actual method to use + var handler = new ProxyExprNodeUtilResolveExceptionHandler { + ProcHandle = e => { + if (_methodStreamSpec.Expressions.Count == 0) { + return new ExprValidationException("Method footprint does not match the number or type of expression parameters, expecting no parameters in method: " + e.Message, e); + } + var resultTypes = ExprNodeUtility.GetExprResultTypes(validatedInputParameters); + return new ExprValidationException( + string.Format("Method footprint does not match the number or type of expression parameters, expecting a method where parameters are typed '{0}': {1}", TypeHelper.GetParameterAsString(resultTypes), e.Message), e); + } + }; + var desc = ExprNodeUtility.ResolveMethodAllowWildcardAndStream( + _metadata.MethodProviderClass.FullName, _metadata.IsStaticMethod ? null : _metadata.MethodProviderClass, + _methodStreamSpec.MethodName, validatedInputParameters, engineImportService, eventAdapterService, statementContext.StatementId, + false, null, handler, _methodStreamSpec.MethodName, tableService, statementContext.EngineURI); + _validatedExprNodes = desc.ChildEvals; + + // Construct polling strategy as a method invocation + var invocationTarget = _metadata.InvocationTarget; + var strategy = _metadata.Strategy; + var variableReader = _metadata.VariableReader; + var variableName = _metadata.VariableName; + var methodFastClass = desc.FastMethod; + if (_metadata.EventTypeEventBeanArray != null) { + _pollExecStrategy = new MethodPollingExecStrategyEventBeans(eventAdapterService, methodFastClass, _eventType, invocationTarget, strategy, variableReader, variableName, variableService); + } else if (_metadata.OptionalMapType != null) { + if (desc.FastMethod.ReturnType.IsArray) { + _pollExecStrategy = new MethodPollingExecStrategyMapArray(eventAdapterService, methodFastClass, _eventType, invocationTarget, strategy, variableReader, variableName, variableService); + } else if (_metadata.IsCollection) { + _pollExecStrategy = new MethodPollingExecStrategyMapCollection(eventAdapterService, methodFastClass, _eventType, invocationTarget, strategy, variableReader, variableName, variableService); + } else if (_metadata.IsEnumerator) { + _pollExecStrategy = new MethodPollingExecStrategyMapIterator(eventAdapterService, methodFastClass, _eventType, invocationTarget, strategy, variableReader, variableName, variableService); + } else { + _pollExecStrategy = new MethodPollingExecStrategyMapPlain(eventAdapterService, methodFastClass, _eventType, invocationTarget, strategy, variableReader, variableName, variableService); + } + } else if (_metadata.OptionalOaType != null) { + if (desc.FastMethod.ReturnType == typeof(Object[][])) { + _pollExecStrategy = new MethodPollingExecStrategyOAArray(eventAdapterService, methodFastClass, _eventType, invocationTarget, strategy, variableReader, variableName, variableService); + } else if (_metadata.IsCollection) { + _pollExecStrategy = new MethodPollingExecStrategyOACollection(eventAdapterService, methodFastClass, _eventType, invocationTarget, strategy, variableReader, variableName, variableService); + } else if (_metadata.IsEnumerator) { + _pollExecStrategy = new MethodPollingExecStrategyOAIterator(eventAdapterService, methodFastClass, _eventType, invocationTarget, strategy, variableReader, variableName, variableService); + } else { + _pollExecStrategy = new MethodPollingExecStrategyOAPlain(eventAdapterService, methodFastClass, _eventType, invocationTarget, strategy, variableReader, variableName, variableService); + } + } else { + if (desc.FastMethod.ReturnType.IsArray) { + _pollExecStrategy = new MethodPollingExecStrategyPONOArray(eventAdapterService, methodFastClass, _eventType, invocationTarget, strategy, variableReader, variableName, variableService); + } else if (_metadata.IsCollection) { + _pollExecStrategy = new MethodPollingExecStrategyPONOCollection(eventAdapterService, methodFastClass, _eventType, invocationTarget, strategy, variableReader, variableName, variableService); + } else if (_metadata.IsEnumerator) { + _pollExecStrategy = new MethodPollingExecStrategyPONOIterator(eventAdapterService, methodFastClass, _eventType, invocationTarget, strategy, variableReader, variableName, variableService); + } else { + _pollExecStrategy = new MethodPollingExecStrategyPONOPlain(eventAdapterService, methodFastClass, _eventType, invocationTarget, strategy, variableReader, variableName, variableService); + } + } + } else { + // script-based evaluation + _pollExecStrategy = new MethodPollingExecStrategyScript(_metadata.ScriptExpression, _metadata.EventTypeEventBeanArray); + _validatedExprNodes = ExprNodeUtility.GetEvaluators(validatedInputParameters); + } + } + + public EventTable[][] Poll(EventBean[][] lookupEventsPerStream, PollResultIndexingStrategy indexingStrategy, ExprEvaluatorContext exprEvaluatorContext) + { + var localDataCache = _dataCacheThreadLocal.GetOrCreate(); + var strategyStarted = false; + + var resultPerInputRow = new EventTable[lookupEventsPerStream.Length][]; + + // Get input parameters for each row + for (var row = 0; row < lookupEventsPerStream.Length; row++) + { + var methodParams = new Object[_validatedExprNodes.Length]; + var evaluateParams = new EvaluateParams(lookupEventsPerStream[row], true, exprEvaluatorContext); + + // Build lookup keys + for (var valueNum = 0; valueNum < _validatedExprNodes.Length; valueNum++) + { + var parameterValue = _validatedExprNodes[valueNum].Evaluate(evaluateParams); + methodParams[valueNum] = parameterValue; + } + + EventTable[] result = null; + + // try the threadlocal iteration cache, if set + if (localDataCache != null) { + result = localDataCache.GetCached(methodParams, _methodStreamSpec.Expressions.Count); + } + + // try the connection cache + if (result == null) { + result = _dataCache.GetCached(methodParams, _methodStreamSpec.Expressions.Count); + if ((result != null) && (localDataCache != null)) { + localDataCache.PutCached(methodParams, _methodStreamSpec.Expressions.Count, result); + } + } + + if (result != null) { + // found in cache + resultPerInputRow[row] = result; + } else { + // not found in cache, get from actual polling (db query) + try { + if (!strategyStarted) { + _pollExecStrategy.Start(); + strategyStarted = true; + } + + // Poll using the polling execution strategy and lookup values + var pollResult = _pollExecStrategy.Poll(methodParams, exprEvaluatorContext); + + // index the result, if required, using an indexing strategy + var indexTable = indexingStrategy.Index(pollResult, _dataCache.IsActive, _statementContext); + + // assign to row + resultPerInputRow[row] = indexTable; + + // save in cache + _dataCache.PutCached(methodParams, _methodStreamSpec.Expressions.Count, indexTable); + + if (localDataCache != null) { + localDataCache.PutCached(methodParams, _methodStreamSpec.Expressions.Count, indexTable); + } + } catch (EPException ex) { + if (strategyStarted) { + _pollExecStrategy.Done(); + } + throw; + } + } + } + + if (strategyStarted) { + _pollExecStrategy.Done(); + } + + return resultPerInputRow; + } + + public View AddView(View view) { + view.Parent = this; + return view; + } + + public View[] Views + { + get { return ViewSupport.EMPTY_VIEW_ARRAY; } + } + + public bool RemoveView(View view) { + throw new UnsupportedOperationException("Subviews not supported"); + } + + public void RemoveAllViews() { + throw new UnsupportedOperationException("Subviews not supported"); + } + + public bool HasViews + { + get { return false; } + } + + public EventType EventType + { + get { return _eventType; } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public IEnumerator GetEnumerator() + { + var result = Poll(NULL_ROWS, ITERATOR_INDEXING_STRATEGY, _exprEvaluatorContext); + foreach (EventTable[] eventTableList in result) + { + foreach (EventTable eventTable in eventTableList) + { + foreach (EventBean eventBean in eventTable) + { + yield return eventBean; + } + } + } + } + + public ICollection RequiredStreams + { + get { return _requiredStreams; } + } + + public bool HasRequiredStreams + { + get { return !_requiredStreams.IsEmpty(); } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/MethodPollingViewableFactory.cs b/NEsper.Core/NEsper.Core/epl/core/MethodPollingViewableFactory.cs new file mode 100755 index 000000000..fb7a6dc4e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/MethodPollingViewableFactory.cs @@ -0,0 +1,477 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.core.start; +using com.espertech.esper.epl.db; +using com.espertech.esper.epl.declexpr; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.script; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.variable; +using com.espertech.esper.events; +using com.espertech.esper.schedule; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.epl.core +{ + using Map = IDictionary; + + /// Factory for method-invocation data provider streams. + public class MethodPollingViewableFactory + { + /// + /// Creates a method-invocation polling view for use as a stream that calls a method, or pulls results from cache. + /// + /// the stream number + /// defines the class and method to call + /// for creating event types and events + /// for time-based callbacks + /// for resolving configurations + /// for scheduling callbacks in expiry-time based caches + /// for schedules within the statement + /// expression evaluation context + /// variable service + /// statement context + /// context name + /// factory for cache + /// + /// if the expressions cannot be validated or the method descriptor + /// has incorrect class and method names, or parameter number and types don't match + /// + /// pollable view + public static HistoricalEventViewable CreatePollMethodView( + int streamNumber, + MethodStreamSpec methodStreamSpec, + EventAdapterService eventAdapterService, + EPStatementAgentInstanceHandle epStatementAgentInstanceHandle, + EngineImportService engineImportService, + SchedulingService schedulingService, + ScheduleBucket scheduleBucket, + ExprEvaluatorContext exprEvaluatorContext, + VariableService variableService, + string contextName, + DataCacheFactory dataCacheFactory, + StatementContext statementContext) + { + VariableMetaData variableMetaData = variableService.GetVariableMetaData(methodStreamSpec.ClassName); + MethodPollingExecStrategyEnum strategy; + VariableReader variableReader = null; + string variableName = null; + MethodInfo methodReflection = null; + object invocationTarget = null; + string eventTypeNameProvidedUDFOrScript = null; + + // see if this is a script in the from-clause + ExprNodeScript scriptExpression = null; + if (methodStreamSpec.ClassName == null && methodStreamSpec.MethodName != null) + { + var scriptsByName = statementContext.ExprDeclaredService.GetScriptsByName(methodStreamSpec.MethodName); + if (scriptsByName != null) + { + scriptExpression = + ExprDeclaredHelper.GetExistsScript( + statementContext.ConfigSnapshot.EngineDefaults.Scripts.DefaultDialect, + methodStreamSpec.MethodName, methodStreamSpec.Expressions, scriptsByName, + statementContext.ExprDeclaredService); + } + } + + try + { + if (scriptExpression != null) + { + eventTypeNameProvidedUDFOrScript = scriptExpression.EventTypeNameAnnotation; + strategy = MethodPollingExecStrategyEnum.TARGET_SCRIPT; + ExprNodeUtility.ValidateSimpleGetSubtree( + ExprNodeOrigin.METHODINVJOIN, scriptExpression, statementContext, null, false); + } + else if (variableMetaData != null) + { + variableName = variableMetaData.VariableName; + if (variableMetaData.ContextPartitionName != null) + { + if (contextName == null || !contextName.Equals(variableMetaData.ContextPartitionName)) + { + throw new ExprValidationException( + "Variable by name '" + variableMetaData.VariableName + + "' has been declared for context '" + variableMetaData.ContextPartitionName + + "' and can only be used within the same context"); + } + strategy = MethodPollingExecStrategyEnum.TARGET_VAR_CONTEXT; + variableReader = null; + invocationTarget = null; + } + else + { + variableReader = variableService.GetReader( + methodStreamSpec.ClassName, EPStatementStartMethodConst.DEFAULT_AGENT_INSTANCE_ID); + if (variableMetaData.IsConstant) + { + invocationTarget = variableReader.Value; + if (invocationTarget is EventBean) + { + invocationTarget = ((EventBean) invocationTarget).Underlying; + } + strategy = MethodPollingExecStrategyEnum.TARGET_CONST; + } + else + { + invocationTarget = null; + strategy = MethodPollingExecStrategyEnum.TARGET_VAR; + } + } + methodReflection = engineImportService.ResolveNonStaticMethodOverloadChecked( + variableMetaData.VariableType, methodStreamSpec.MethodName); + } + else if (methodStreamSpec.ClassName == null) + { + // must be either UDF or script + Pair udf = null; + try + { + udf = engineImportService.ResolveSingleRow(methodStreamSpec.MethodName); + } + catch (EngineImportException ex) + { + throw new ExprValidationException( + "Failed to find user-defined function '" + methodStreamSpec.MethodName + "': " + ex.Message, + ex); + } + methodReflection = engineImportService.ResolveMethodOverloadChecked(udf.First, methodStreamSpec.MethodName); + invocationTarget = null; + variableReader = null; + variableName = null; + strategy = MethodPollingExecStrategyEnum.TARGET_CONST; + eventTypeNameProvidedUDFOrScript = udf.Second.OptionalEventTypeName; + } + else + { + methodReflection = engineImportService.ResolveMethodOverloadChecked(methodStreamSpec.ClassName, methodStreamSpec.MethodName); + invocationTarget = null; + variableReader = null; + variableName = null; + strategy = MethodPollingExecStrategyEnum.TARGET_CONST; + } + } + catch (ExprValidationException) + { + throw; + } + catch (Exception e) + { + throw new ExprValidationException(e.Message, e); + } + + Type methodProviderClass = null; + Type beanClass; + IDictionary oaType = null; + IDictionary mapType = null; + bool isCollection = false; + bool isIterator = false; + EventType eventType; + EventType eventTypeWhenMethodReturnsEventBeans = null; + bool isStaticMethod = false; + + if (methodReflection != null) + { + methodProviderClass = methodReflection.DeclaringType; + isStaticMethod = variableMetaData == null; + + // Determine object type returned by method + beanClass = methodReflection.ReturnType; + if ((beanClass == typeof (void)) || (beanClass.IsBuiltinDataType())) + { + throw new ExprValidationException( + "Invalid return type for static method '" + methodReflection.Name + "' of class '" + + methodStreamSpec.ClassName + "', expecting a class"); + } + + if (methodReflection.ReturnType.IsArray && + methodReflection.ReturnType.GetElementType() != typeof (EventBean)) + { + beanClass = methodReflection.ReturnType.GetElementType(); + } + + Type collectionClass = null; + Type iteratorClass = null; + + if (!beanClass.IsGenericDictionary()) + { + isCollection = beanClass.IsGenericCollection(); + if (isCollection) + { + collectionClass = TypeHelper.GetGenericReturnType(methodReflection, true); + beanClass = collectionClass; + } + + isIterator = beanClass.IsGenericEnumerator() && !beanClass.IsGenericDictionary(); + if (isIterator) + { + iteratorClass = TypeHelper.GetGenericReturnType(methodReflection, true); + beanClass = iteratorClass; + } + } + + // If the method returns a Map, look up the map type + string mapTypeName = null; + + if ((methodReflection.ReturnType.IsGenericStringDictionary()) || + (methodReflection.ReturnType.IsArray && methodReflection.ReturnType.GetElementType().IsGenericStringDictionary()) || + (isCollection && collectionClass.IsImplementsInterface(typeof (Map))) || + (isIterator && iteratorClass.IsImplementsInterface(typeof (Map)))) + { + MethodMetadataDesc metadata; + if (variableMetaData != null) + { + metadata = GetCheckMetadataVariable( + methodStreamSpec.MethodName, variableMetaData, variableReader, engineImportService, + typeof (Map)); + } + else + { + metadata = GetCheckMetadataNonVariable( + methodStreamSpec.MethodName, methodStreamSpec.ClassName, engineImportService, typeof (Map)); + } + mapTypeName = metadata.TypeName; + mapType = (IDictionary) metadata.TypeMetadata; + } + + // If the method returns an object[] or object[][], look up the type information + string oaTypeName = null; + if (methodReflection.ReturnType == typeof (object[]) || + methodReflection.ReturnType == typeof (object[][]) || + (isCollection && collectionClass == typeof (object[])) || + (isIterator && iteratorClass == typeof (object[]))) + { + MethodMetadataDesc metadata; + if (variableMetaData != null) + { + metadata = GetCheckMetadataVariable( + methodStreamSpec.MethodName, variableMetaData, variableReader, engineImportService, + typeof (IDictionary)); + } + else + { + metadata = GetCheckMetadataNonVariable( + methodStreamSpec.MethodName, methodStreamSpec.ClassName, engineImportService, + typeof (IDictionary)); + } + oaTypeName = metadata.TypeName; + oaType = (IDictionary) metadata.TypeMetadata; + } + + // Determine event type from class and method name + // If the method returns EventBean[], require the event type + if ((methodReflection.ReturnType.IsArray && + methodReflection.ReturnType.GetElementType() == typeof (EventBean)) || + (isCollection && collectionClass == typeof (EventBean)) || + (isIterator && iteratorClass == typeof (EventBean))) + { + string typeName = methodStreamSpec.EventTypeName == null + ? eventTypeNameProvidedUDFOrScript + : methodStreamSpec.EventTypeName; + eventType = EventTypeUtility.RequireEventType( + "Method", methodReflection.Name, eventAdapterService, typeName); + eventTypeWhenMethodReturnsEventBeans = eventType; + } + else if (mapType != null) + { + eventType = eventAdapterService.AddNestableMapType( + mapTypeName, mapType, null, false, true, true, false, false); + } + else if (oaType != null) + { + eventType = eventAdapterService.AddNestableObjectArrayType( + oaTypeName, oaType, null, false, true, true, false, false, false, null); + } + else + { + eventType = eventAdapterService.AddBeanType(beanClass.GetDefaultTypeName(), beanClass, false, true, true); + } + + // the @type is only allowed in conjunction with EventBean return types + if (methodStreamSpec.EventTypeName != null && eventTypeWhenMethodReturnsEventBeans == null) + { + throw new ExprValidationException(EventTypeUtility.DisallowedAtTypeMessage()); + } + } + else + { + string eventTypeName = methodStreamSpec.EventTypeName == null + ? scriptExpression.EventTypeNameAnnotation + : methodStreamSpec.EventTypeName; + eventType = EventTypeUtility.RequireEventType( + "Script", scriptExpression.Script.Name, eventAdapterService, eventTypeName); + } + + // get configuration for cache + string configName = methodProviderClass != null ? methodProviderClass.FullName : methodStreamSpec.MethodName; + ConfigurationMethodRef configCache = engineImportService.GetConfigurationMethodRef(configName); + if (configCache == null) + { + configCache = engineImportService.GetConfigurationMethodRef(configName); + } + ConfigurationDataCache dataCacheDesc = (configCache != null) ? configCache.DataCacheDesc : null; + DataCache dataCache = dataCacheFactory.GetDataCache( + dataCacheDesc, statementContext, epStatementAgentInstanceHandle, schedulingService, scheduleBucket, + streamNumber); + + // metadata + var meta = new MethodPollingViewableMeta( + methodProviderClass, isStaticMethod, mapType, oaType, invocationTarget, strategy, isCollection, + isIterator, variableReader, variableName, eventTypeWhenMethodReturnsEventBeans, scriptExpression); + return new MethodPollingViewable(methodStreamSpec, dataCache, eventType, exprEvaluatorContext, meta); + } + + private static MethodMetadataDesc GetCheckMetadataVariable( + string methodName, + VariableMetaData variableMetaData, + VariableReader variableReader, + EngineImportService engineImportService, + Type metadataClass) + { + MethodInfo typeGetterMethod = GetRequiredTypeGetterMethodCanNonStatic( + methodName, null, variableMetaData.VariableType, engineImportService, metadataClass); + + if (typeGetterMethod.IsStatic) + { + return InvokeMetadataMethod(null, variableMetaData.GetType().Name, typeGetterMethod); + } + + // if the metadata is not a static method and we don't have an instance this is a problem + const string messagePrefix = "Failed to access variable method invocation metadata: "; + if (variableReader == null) + { + throw new ExprValidationException( + messagePrefix + + "The metadata method is an instance method however the variable is contextual, please declare the metadata method as static or remove the context declaration for the variable"); + } + + object value = variableReader.Value; + if (value == null) + { + throw new ExprValidationException( + messagePrefix + "The variable value is null and the metadata method is an instance method"); + } + + if (value is EventBean) + { + value = ((EventBean) value).Underlying; + } + return InvokeMetadataMethod(value, variableMetaData.GetType().Name, typeGetterMethod); + } + + + private static MethodMetadataDesc GetCheckMetadataNonVariable( + string methodName, + string className, + EngineImportService engineImportService, + Type metadataClass) + { + MethodInfo typeGetterMethod = GetRequiredTypeGetterMethodCanNonStatic( + methodName, className, null, engineImportService, metadataClass); + return InvokeMetadataMethod(null, className, typeGetterMethod); + } + + private static MethodInfo GetRequiredTypeGetterMethodCanNonStatic( + string methodName, + string classNameWhenNoClass, + Type clazzWhenAvailable, + EngineImportService engineImportService, + Type metadataClass) + { + MethodInfo typeGetterMethod; + string getterMethodName = methodName + "Metadata"; + try + { + if (clazzWhenAvailable != null) + { + typeGetterMethod = engineImportService.ResolveMethod( + clazzWhenAvailable, getterMethodName, new Type[0], new bool[0], new bool[0]); + } + else + { + typeGetterMethod = engineImportService.ResolveMethodOverloadChecked( + classNameWhenNoClass, getterMethodName, new Type[0], new bool[0], new bool[0]); + } + } + catch (Exception) + { + throw new ExprValidationException( + "Could not find getter method for method invocation, expected a method by name '" + getterMethodName + + "' accepting no parameters"); + } + + bool fail; + if (metadataClass.IsInterface) + { + fail = !typeGetterMethod.ReturnType.IsImplementsInterface(metadataClass); + } + else + { + fail = typeGetterMethod.ReturnType != metadataClass; + } + if (fail) + { + throw new ExprValidationException( + "Getter method '" + typeGetterMethod.Name + "' does not return " + + metadataClass.GetTypeNameFullyQualPretty()); + } + + return typeGetterMethod; + } + + private static MethodMetadataDesc InvokeMetadataMethod( + object target, + string className, + MethodInfo typeGetterMethod) + { + object resultType; + try + { + resultType = typeGetterMethod.Invoke(target, new object[0]); + } + catch (Exception e) + { + throw new ExprValidationException( + "Error invoking metadata getter method for method invocation, for method by name '" + + typeGetterMethod.Name + "' accepting no parameters: " + e.Message, e); + } + if (resultType == null) + { + throw new ExprValidationException( + "Error invoking metadata getter method for method invocation, method returned a null value"); + } + + return new MethodMetadataDesc(className + "." + typeGetterMethod.Name, resultType); + } + + public class MethodMetadataDesc + { + public MethodMetadataDesc(string typeName, object typeMetadata) + { + TypeName = typeName; + TypeMetadata = typeMetadata; + } + + public string TypeName { get; private set; } + + public object TypeMetadata { get; private set; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/MethodPollingViewableMeta.cs b/NEsper.Core/NEsper.Core/epl/core/MethodPollingViewableMeta.cs new file mode 100755 index 000000000..84503995f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/MethodPollingViewableMeta.cs @@ -0,0 +1,72 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.script; +using com.espertech.esper.epl.variable; + +namespace com.espertech.esper.epl.core +{ + public class MethodPollingViewableMeta + { + public MethodPollingViewableMeta( + Type methodProviderClass, + bool isStaticMethod, + IDictionary optionalMapType, + IDictionary optionalOaType, + Object invocationTarget, + MethodPollingExecStrategyEnum strategy, + bool isCollection, + bool isIterator, + VariableReader variableReader, + string variableName, + EventType eventTypeEventBeanArray, + ExprNodeScript scriptExpression) + { + MethodProviderClass = methodProviderClass; + IsStaticMethod = isStaticMethod; + OptionalMapType = optionalMapType; + OptionalOaType = optionalOaType; + InvocationTarget = invocationTarget; + Strategy = strategy; + IsCollection = isCollection; + IsEnumerator = isIterator; + VariableReader = variableReader; + VariableName = variableName; + EventTypeEventBeanArray = eventTypeEventBeanArray; + ScriptExpression = scriptExpression; + } + + public IDictionary OptionalMapType { get; private set; } + + public IDictionary OptionalOaType { get; private set; } + + public object InvocationTarget { get; private set; } + + public MethodPollingExecStrategyEnum Strategy { get; private set; } + + public bool IsCollection { get; private set; } + + public bool IsEnumerator { get; private set; } + + public VariableReader VariableReader { get; private set; } + + public string VariableName { get; private set; } + + public EventType EventTypeEventBeanArray { get; private set; } + + public ExprNodeScript ScriptExpression { get; private set; } + + public Type MethodProviderClass { get; private set; } + + public bool IsStaticMethod { get; private set; } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/OrderByElement.cs b/NEsper.Core/NEsper.Core/epl/core/OrderByElement.cs new file mode 100755 index 000000000..aa253f274 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/OrderByElement.cs @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.core +{ + public class OrderByElement + { + public OrderByElement(ExprNode exprNode, ExprEvaluator expr, bool descending) + { + ExprNode = exprNode; + Expr = expr; + IsDescending = descending; + } + + public ExprNode ExprNode { get; private set; } + + public ExprEvaluator Expr { get; private set; } + + public bool IsDescending { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/core/OrderByProcessor.cs b/NEsper.Core/NEsper.Core/epl/core/OrderByProcessor.cs new file mode 100755 index 000000000..d424a4a24 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/OrderByProcessor.cs @@ -0,0 +1,81 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.agg.rollup; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.core +{ + /// + /// A processor for ordering output events according to the order specified in the order-by clause. + /// + public interface OrderByProcessor + { + /// + /// Sort the output events. If the order-by processor needs group-by + /// keys to evaluate the expressions in the order-by clause, these will + /// be computed from the generating events. + /// + /// the events to be sorted + /// the events that generated the output events (each event has a corresponding array of generating events per different event streams) + /// indicates whether we are dealing with new data (istream) or old data (rstream) + /// context for expression evalauation + /// an array containing the output events in sorted order + EventBean[] Sort(EventBean[] outgoingEvents, EventBean[][] generatingEvents, bool isNewData, ExprEvaluatorContext exprEvaluatorContext); + + /// + /// Sort the output events, using the provided group-by keys for + /// evaluating grouped aggregation functions, and avoiding the cost of + /// recomputing the keys. + /// + /// the events to sort + /// the events that generated the output events (each event has a corresponding array of generating events per different event streams) + /// the keys to use for determining the group-by group of output events + /// indicates whether we are dealing with new data (istream) or old data (rstream) + /// context for expression evalauation + /// an array containing the output events in sorted order + EventBean[] Sort(EventBean[] outgoingEvents, EventBean[][] generatingEvents, Object[] groupByKeys, bool isNewData, ExprEvaluatorContext exprEvaluatorContext); + + EventBean[] Sort(EventBean[] outgoingEvents, IList currentGenerators, bool newData, AgentInstanceContext agentInstanceContext, OrderByElement[][] elementsPerLevel); + + /// + /// Returns the sort key for a given row. + /// + /// is the row consisting of one event per stream + /// is true for new data + /// context for expression evalauation + /// sort key + Object GetSortKey(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext exprEvaluatorContext); + + Object GetSortKey(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext exprEvaluatorContext, OrderByElement[] elementsForLevel); + + /// + /// Returns the sort key for a each row where a row is a single event (no join, single stream). + /// + /// is the rows consisting of one event per row + /// is true for new data + /// context for expression evalauation + /// sort key for each row + Object[] GetSortKeyPerRow(EventBean[] generatingEvents, bool isNewData, ExprEvaluatorContext exprEvaluatorContext); + + /// + /// Sort a given array of outgoing events using the sort keys returning a sorted outgoing event array. + /// + /// is the events to sort + /// is the keys to sort by + /// context for expression evalauation + /// sorted events + EventBean[] Sort(EventBean[] outgoingEvents, Object[] orderKeys, ExprEvaluatorContext exprEvaluatorContext); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/OrderByProcessorFactory.cs b/NEsper.Core/NEsper.Core/epl/core/OrderByProcessorFactory.cs new file mode 100755 index 000000000..b91bc3b68 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/OrderByProcessorFactory.cs @@ -0,0 +1,21 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.agg.service; + +namespace com.espertech.esper.epl.core +{ + /// + /// A processor for ordering output events according to the order specified in the order-by clause. + /// + public interface OrderByProcessorFactory + { + OrderByProcessor Instantiate(AggregationService aggregationService, AgentInstanceContext agentInstanceContext); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/OrderByProcessorFactoryFactory.cs b/NEsper.Core/NEsper.Core/epl/core/OrderByProcessorFactoryFactory.cs new file mode 100755 index 000000000..afb52f155 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/OrderByProcessorFactoryFactory.cs @@ -0,0 +1,108 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.variable; + +namespace com.espertech.esper.epl.core +{ + /// + /// Factory for processors. + /// + public class OrderByProcessorFactoryFactory + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// Returns processor for order-by clauses. + /// + /// is a list of select expressions + /// is a list of group-by expressions + /// is a list of order-by expressions + /// specification for row limit, or null if no row limit is defined + /// for retrieving variable state for use with row limiting + /// for string value sorting using compare or Collator + /// context name + /// when validation of expressions fails + /// ordering processor instance + public static OrderByProcessorFactory GetProcessor( + IList selectionList, + ExprNode[] groupByNodes, + IList orderByList, + RowLimitSpec rowLimitSpec, + VariableService variableService, + bool isSortUsingCollator, + string optionalContextName) + { + // Get the order by expression nodes + var orderByNodes = orderByList.Select(element => element.ExprNode).ToList(); + + // No order-by clause + if (orderByList.IsEmpty()) { + Log.Debug(".GetProcessor Using no OrderByProcessor"); + if (rowLimitSpec != null) { + var rowLimitProcessorFactory = new RowLimitProcessorFactory(rowLimitSpec, variableService, optionalContextName); + return new OrderByProcessorRowLimitOnlyFactory(rowLimitProcessorFactory); + } + return null; + } + + // Determine aggregate functions used in select, if any + var selectAggNodes = new List(); + foreach (SelectClauseExprCompiledSpec element in selectionList) { + ExprAggregateNodeUtil.GetAggregatesBottomUp(element.SelectExpression, selectAggNodes); + } + + // Get all the aggregate functions occuring in the order-by clause + var orderAggNodes = new List(); + foreach (ExprNode orderByNode in orderByNodes) { + ExprAggregateNodeUtil.GetAggregatesBottomUp(orderByNode, orderAggNodes); + } + + ValidateOrderByAggregates(selectAggNodes, orderAggNodes); + + // Tell the order-by processor whether to compute group-by + // keys if they are not present + bool needsGroupByKeys = !selectionList.IsEmpty() && !orderAggNodes.IsEmpty(); + + Log.Debug(".getProcessor Using OrderByProcessorImpl"); + var orderByProcessorFactory = new OrderByProcessorFactoryImpl(orderByList, groupByNodes, needsGroupByKeys, isSortUsingCollator); + if (rowLimitSpec == null) { + return orderByProcessorFactory; + } else { + var rowLimitProcessorFactory = new RowLimitProcessorFactory(rowLimitSpec, variableService, optionalContextName); + return new OrderByProcessorOrderedLimitFactory(orderByProcessorFactory, rowLimitProcessorFactory); + } + } + + private static void ValidateOrderByAggregates(IList selectAggNodes, IList orderAggNodes) + { + // Check that the order-by clause doesn't contain + // any aggregate functions not in the select expression + foreach (ExprAggregateNode orderAgg in orderAggNodes) { + bool inSelect = false; + foreach (ExprAggregateNode selectAgg in selectAggNodes) { + if (ExprNodeUtility.DeepEquals(selectAgg, orderAgg)) { + inSelect = true; + break; + } + } + if (!inSelect) { + throw new ExprValidationException("Aggregate functions in the order-by clause must also occur in the select expression"); + } + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/OrderByProcessorFactoryImpl.cs b/NEsper.Core/NEsper.Core/epl/core/OrderByProcessorFactoryImpl.cs new file mode 100755 index 000000000..ea33f0b0f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/OrderByProcessorFactoryImpl.cs @@ -0,0 +1,108 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.spec; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.core +{ + /// + /// An order-by processor that sorts events according to the expressions + /// in the order_by clause. + /// + public class OrderByProcessorFactoryImpl : OrderByProcessorFactory + { + private readonly OrderByElement[] _orderBy; + private readonly ExprEvaluator[] _groupByNodes; + private readonly bool _needsGroupByKeys; + private readonly IComparer _comparator; + + /// + /// Ctor. + /// + /// the nodes that generate the keys to sort events on + /// + /// generate the keys for determining aggregation groups + /// + /// indicates whether this processor needs to have individual + /// group by keys to evaluate the sort condition successfully + /// + /// for string value sorting using compare or Collator + /// when order-by items don't divulge a type + public OrderByProcessorFactoryImpl( + IList orderByList, + ExprNode[] groupByNodes, + bool needsGroupByKeys, + bool isSortUsingCollator) + { + _orderBy = ToElementArray(orderByList); + _groupByNodes = ExprNodeUtility.GetEvaluators(groupByNodes); + _needsGroupByKeys = needsGroupByKeys; + _comparator = GetComparator(_orderBy, isSortUsingCollator); + } + + public OrderByProcessor Instantiate(AggregationService aggregationService, AgentInstanceContext agentInstanceContext) { + return new OrderByProcessorImpl(this, aggregationService); + } + + public OrderByElement[] OrderBy + { + get { return _orderBy; } + } + + public ExprEvaluator[] GroupByNodes + { + get { return _groupByNodes; } + } + + public bool IsNeedsGroupByKeys + { + get { return _needsGroupByKeys; } + } + + public IComparer Comparator + { + get { return _comparator; } + } + + /// + /// Returns a comparator for order items that may sort string values using Collator. + /// + /// order-by items + /// true for Collator string sorting + /// comparator + /// if the return type of order items cannot be determined + internal static IComparer GetComparator(OrderByElement[] orderBy, bool isSortUsingCollator) + { + var evaluators = new ExprEvaluator[orderBy.Length]; + var descending = new bool[orderBy.Length]; + for (int i = 0; i < orderBy.Length; i++) { + evaluators[i] = orderBy[i].Expr; + descending[i] = orderBy[i].IsDescending; + } + return CollectionUtil.GetComparator(evaluators, isSortUsingCollator, descending); + } + + private OrderByElement[] ToElementArray(IList orderByList) + { + var elements = new OrderByElement[orderByList.Count]; + var count = 0; + foreach (var item in orderByList) { + elements[count++] = new OrderByElement(item.ExprNode, item.ExprNode.ExprEvaluator, item.IsDescending); + } + return elements; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/OrderByProcessorImpl.cs b/NEsper.Core/NEsper.Core/epl/core/OrderByProcessorImpl.cs new file mode 100755 index 000000000..3dd3e3c53 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/OrderByProcessorImpl.cs @@ -0,0 +1,478 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.agg.rollup; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.core +{ + /// + /// An order-by processor that sorts events according to the expressions + /// in the order_by clause. + /// + public class OrderByProcessorImpl : OrderByProcessor + { + private readonly OrderByProcessorFactoryImpl _factory; + private readonly AggregationService _aggregationService; + + public OrderByProcessorImpl(OrderByProcessorFactoryImpl factory, AggregationService aggregationService) + { + _factory = factory; + _aggregationService = aggregationService; + } + + public Object GetSortKey(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext exprEvaluatorContext) + { + return GetSortKeyInternal(eventsPerStream, isNewData, exprEvaluatorContext, _factory.OrderBy); + } + + public Object GetSortKey(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext exprEvaluatorContext, OrderByElement[] elementsForLevel) + { + return GetSortKeyInternal(eventsPerStream, isNewData, exprEvaluatorContext, elementsForLevel); + } + + private static Object GetSortKeyInternal(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext exprEvaluatorContext, OrderByElement[] elements) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QOrderBy(eventsPerStream, elements); } + + var evaluateParams = new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext); + if (elements.Length == 1) + { + if (InstrumentationHelper.ENABLED) + { + var value = elements[0].Expr.Evaluate(evaluateParams); + InstrumentationHelper.Get().AOrderBy(value); + return value; + } + return elements[0].Expr.Evaluate(evaluateParams); + } + + var values = new Object[elements.Length]; + var count = 0; + foreach (var sortPair in elements) + { + values[count++] = sortPair.Expr.Evaluate(evaluateParams); + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AOrderBy(values); } + return new MultiKeyUntyped(values); + } + + public Object[] GetSortKeyPerRow(EventBean[] generatingEvents, bool isNewData, ExprEvaluatorContext exprEvaluatorContext) + { + if (generatingEvents == null) + { + return null; + } + + var sortProperties = new Object[generatingEvents.Length]; + + var count = 0; + var evalEventsPerStream = new EventBean[1]; + var evaluateParams = new EvaluateParams(evalEventsPerStream, isNewData, exprEvaluatorContext); + + if (_factory.OrderBy.Length == 1) + { + var singleEval = _factory.OrderBy[0].Expr; + foreach (var theEvent in generatingEvents) + { + evalEventsPerStream[0] = theEvent; + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QOrderBy(evalEventsPerStream, _factory.OrderBy); } + sortProperties[count] = singleEval.Evaluate(evaluateParams); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AOrderBy(sortProperties[count]); } + count++; + } + } + else + { + foreach (var theEvent in generatingEvents) + { + var values = new Object[_factory.OrderBy.Length]; + var countTwo = 0; + evalEventsPerStream[0] = theEvent; + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QOrderBy(evalEventsPerStream, _factory.OrderBy); } + foreach (var sortPair in _factory.OrderBy) + { + values[countTwo++] = sortPair.Expr.Evaluate(evaluateParams); + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AOrderBy(values); } + + sortProperties[count] = new MultiKeyUntyped(values); + count++; + } + } + + return sortProperties; + } + + public EventBean[] Sort(EventBean[] outgoingEvents, EventBean[][] generatingEvents, bool isNewData, ExprEvaluatorContext exprEvaluatorContext) + { + if (outgoingEvents == null || outgoingEvents.Length < 2) + { + return outgoingEvents; + } + + // Get the group by keys if needed + Object[] groupByKeys = null; + if (_factory.IsNeedsGroupByKeys) + { + groupByKeys = GenerateGroupKeys(generatingEvents, isNewData, exprEvaluatorContext); + } + + return Sort(outgoingEvents, generatingEvents, groupByKeys, isNewData, exprEvaluatorContext); + } + + public EventBean[] Sort(EventBean[] outgoingEvents, IList currentGenerators, bool isNewData, AgentInstanceContext exprEvaluatorContext, OrderByElement[][] elementsPerLevel) + { + var sortValuesMultiKeys = CreateSortPropertiesWRollup(currentGenerators, elementsPerLevel, isNewData, exprEvaluatorContext); + return SortInternal(outgoingEvents, sortValuesMultiKeys, _factory.Comparator); + } + + public EventBean[] Sort(EventBean[] outgoingEvents, EventBean[][] generatingEvents, Object[] groupByKeys, bool isNewData, ExprEvaluatorContext exprEvaluatorContext) + { + if (outgoingEvents == null || outgoingEvents.Length < 2) + { + return outgoingEvents; + } + + // Create the multikeys of sort values + var sortValuesMultiKeys = CreateSortProperties(generatingEvents, groupByKeys, isNewData, exprEvaluatorContext); + + return SortInternal(outgoingEvents, sortValuesMultiKeys, _factory.Comparator); + } + + private IList CreateSortProperties(EventBean[][] generatingEvents, Object[] groupByKeys, bool isNewData, ExprEvaluatorContext exprEvaluatorContext) + { + var sortProperties = new Object[generatingEvents.Length]; + + var elements = _factory.OrderBy; + if (elements.Length == 1) + { + var count = 0; + foreach (var eventsPerStream in generatingEvents) + { + var evaluateParams = new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext); + + // Make a new multikey that contains the sort-by values. + if (_factory.IsNeedsGroupByKeys) + { + _aggregationService.SetCurrentAccess(groupByKeys[count], exprEvaluatorContext.AgentInstanceId, null); + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QOrderBy(eventsPerStream, _factory.OrderBy); } + sortProperties[count] = elements[0].Expr.Evaluate(evaluateParams); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AOrderBy(sortProperties[count]); } + count++; + } + } + else + { + var count = 0; + foreach (var eventsPerStream in generatingEvents) + { + var evaluateParams = new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext); + + // Make a new multikey that contains the sort-by values. + if (_factory.IsNeedsGroupByKeys) + { + _aggregationService.SetCurrentAccess(groupByKeys[count], exprEvaluatorContext.AgentInstanceId, null); + } + + var values = new Object[_factory.OrderBy.Length]; + var countTwo = 0; + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QOrderBy(eventsPerStream, _factory.OrderBy); } + foreach (var sortPair in _factory.OrderBy) + { + values[countTwo++] = sortPair.Expr.Evaluate(evaluateParams); + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AOrderBy(values); } + + sortProperties[count] = new MultiKeyUntyped(values); + count++; + } + } + + return sortProperties; + } + + public EventBean[] Sort(EventBean[] outgoingEvents, Object[] orderKeys, ExprEvaluatorContext exprEvaluatorContext) + { + var sort = new SortedDictionary(_factory.Comparator).WithNullSupport(); + + if (outgoingEvents == null || outgoingEvents.Length < 2) + { + return outgoingEvents; + } + + for (var i = 0; i < outgoingEvents.Length; i++) + { + var entry = sort.Get(orderKeys[i]); + if (entry == null) + { + sort.Put(orderKeys[i], outgoingEvents[i]); + } + else if (entry is EventBean) + { + IList list = new List(); + list.Add((EventBean)entry); + list.Add(outgoingEvents[i]); + sort.Put(orderKeys[i], list); + } + else + { + var list = (IList)entry; + list.Add(outgoingEvents[i]); + } + } + + var result = new EventBean[outgoingEvents.Length]; + var count = 0; + foreach (Object entry in sort.Values) + { + if (entry is IList) + { + var output = (IList)entry; + foreach (var theEvent in output) + { + result[count++] = theEvent; + } + } + else + { + result[count++] = (EventBean)entry; + } + } + return result; + } + + private Object[] GenerateGroupKeys(EventBean[][] generatingEvents, bool isNewData, ExprEvaluatorContext exprEvaluatorContext) + { + var keys = new Object[generatingEvents.Length]; + + var count = 0; + foreach (var eventsPerStream in generatingEvents) + { + keys[count++] = GenerateGroupKey(eventsPerStream, isNewData, exprEvaluatorContext); + } + + return keys; + } + + private Object GenerateGroupKey(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext exprEvaluatorContext) + { + var evaluateParams = new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext); + var evals = _factory.GroupByNodes; + if (evals.Length == 1) + { + return evals[0].Evaluate(evaluateParams); + } + + var keys = new Object[evals.Length]; + var count = 0; + foreach (var exprNode in evals) + { + keys[count] = exprNode.Evaluate(evaluateParams); + count++; + } + + return new MultiKeyUntyped(keys); + } + + private IList CreateSortPropertiesWRollup(IList currentGenerators, OrderByElement[][] elementsPerLevel, bool isNewData, AgentInstanceContext exprEvaluatorContext) + { + var sortProperties = new Object[currentGenerators.Count]; + + var elements = _factory.OrderBy; + if (elements.Length == 1) + { + var count = 0; + foreach (var rollup in currentGenerators) + { + + // Make a new multikey that contains the sort-by values. + if (_factory.IsNeedsGroupByKeys) + { + _aggregationService.SetCurrentAccess(rollup.GroupKey, exprEvaluatorContext.AgentInstanceId, rollup.Level); + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QOrderBy(rollup.Generator, _factory.OrderBy); } + sortProperties[count] = elementsPerLevel[rollup.Level.LevelNumber][0].Expr.Evaluate( + new EvaluateParams(rollup.Generator, isNewData, exprEvaluatorContext)); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AOrderBy(sortProperties[count]); } + + count++; + } + } + else + { + var count = 0; + foreach (var rollup in currentGenerators) + { + var evaluateParams = new EvaluateParams(rollup.Generator, isNewData, exprEvaluatorContext); + + // Make a new multikey that contains the sort-by values. + if (_factory.IsNeedsGroupByKeys) + { + _aggregationService.SetCurrentAccess(rollup.GroupKey, exprEvaluatorContext.AgentInstanceId, rollup.Level); + } + + var values = new Object[_factory.OrderBy.Length]; + var countTwo = 0; + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QOrderBy(rollup.Generator, _factory.OrderBy); } + foreach (var sortPair in elementsPerLevel[rollup.Level.LevelNumber]) + { + values[countTwo++] = sortPair.Expr.Evaluate(evaluateParams); + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AOrderBy(values); } + + sortProperties[count] = new MultiKeyUntyped(values); + count++; + } + } + return sortProperties; + } + + private static EventBean[] SortInternal(EventBean[] outgoingEvents, IList sortValuesMultiKeys, IComparer comparator) + { + // Map the sort values to the corresponding outgoing events + var sortToOutgoing = new Dictionary>().WithNullSupport(); + var countOne = 0; + foreach (var sortValues in sortValuesMultiKeys) + { + var list = sortToOutgoing.Get(sortValues); + if (list == null) + { + list = new List(); + } + list.Add(outgoingEvents[countOne++]); + sortToOutgoing.Put(sortValues, list); + } + + // Sort the sort values + sortValuesMultiKeys.SortInPlace(comparator); + + // Sort the outgoing events in the same order + var sortSet = new LinkedHashSet(sortValuesMultiKeys); + var result = new EventBean[outgoingEvents.Length]; + var countTwo = 0; + foreach (var sortValues in sortSet) + { + ICollection output = sortToOutgoing.Get(sortValues); + foreach (var theEvent in output) + { + result[countTwo++] = theEvent; + } + } + + return result; + } + + public EventBean DetermineLocalMinMax(EventBean[] outgoingEvents, EventBean[][] generatingEvents, bool isNewData, ExprEvaluatorContext exprEvaluatorContext) + { + // Get the group by keys if needed + Object[] groupByKeys = null; + if (_factory.IsNeedsGroupByKeys) + { + groupByKeys = GenerateGroupKeys(generatingEvents, isNewData, exprEvaluatorContext); + } + + OrderByElement[] elements = _factory.OrderBy; + Object localMinMax = null; + EventBean outgoingMinMaxBean = null; + + if (elements.Length == 1) + { + int count = 0; + foreach (EventBean[] eventsPerStream in generatingEvents) + { + if (_factory.IsNeedsGroupByKeys) + { + _aggregationService.SetCurrentAccess(groupByKeys[count], exprEvaluatorContext.AgentInstanceId, null); + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QOrderBy(eventsPerStream, _factory.OrderBy); } + var evaluateParams = new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext); + var sortKey = elements[0].Expr.Evaluate(evaluateParams); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AOrderBy(localMinMax); } + + var newMinMax = localMinMax == null || _factory.Comparator.Compare(localMinMax, sortKey) > 0; + if (newMinMax) + { + localMinMax = sortKey; + outgoingMinMaxBean = outgoingEvents[count]; + } + + count++; + } + } + else + { + var count = 0; + var values = new Object[_factory.OrderBy.Length]; + var valuesMk = new MultiKeyUntyped(values); + + foreach (var eventsPerStream in generatingEvents) + { + if (_factory.IsNeedsGroupByKeys) + { + _aggregationService.SetCurrentAccess(groupByKeys[count], exprEvaluatorContext.AgentInstanceId, null); + } + + var countTwo = 0; + var evaluateParams = new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QOrderBy(eventsPerStream, _factory.OrderBy); } + foreach (var sortPair in _factory.OrderBy) + { + values[countTwo++] = sortPair.Expr.Evaluate(evaluateParams); + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AOrderBy(values); } + + var newMinMax = localMinMax == null || _factory.Comparator.Compare(localMinMax, valuesMk) > 0; + if (newMinMax) + { + localMinMax = valuesMk; + values = new Object[_factory.OrderBy.Length]; + valuesMk = new MultiKeyUntyped(values); + outgoingMinMaxBean = outgoingEvents[count]; + } + + count++; + } + } + + return outgoingMinMaxBean; + } + + public EventBean DetermineLocalMinMax(EventBean[] outgoingEvents, Object[] orderKeys) + { + Object localMinMax = null; + EventBean outgoingMinMaxBean = null; + + for (int i = 0; i < outgoingEvents.Length; i++) + { + var newMinMax = localMinMax == null || _factory.Comparator.Compare(localMinMax, orderKeys[i]) > 0; + if (newMinMax) + { + localMinMax = orderKeys[i]; + outgoingMinMaxBean = outgoingEvents[i]; + } + } + + return outgoingMinMaxBean; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/OrderByProcessorOrderedLimit.cs b/NEsper.Core/NEsper.Core/epl/core/OrderByProcessorOrderedLimit.cs new file mode 100755 index 000000000..a0d861082 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/OrderByProcessorOrderedLimit.cs @@ -0,0 +1,97 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.agg.rollup; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.core +{ + /// + /// Sorter and row limiter in one: sorts using a sorter and row limits + /// + public class OrderByProcessorOrderedLimit : OrderByProcessor + { + private readonly OrderByProcessorImpl _orderByProcessor; + private readonly RowLimitProcessor _rowLimitProcessor; + + /// + /// Ctor. + /// + /// the sorter + /// the row limiter + public OrderByProcessorOrderedLimit(OrderByProcessorImpl orderByProcessor, RowLimitProcessor rowLimitProcessor) + { + _orderByProcessor = orderByProcessor; + _rowLimitProcessor = rowLimitProcessor; + } + + public EventBean[] Sort(EventBean[] outgoingEvents, EventBean[][] generatingEvents, bool isNewData, ExprEvaluatorContext exprEvaluatorContext) + { + _rowLimitProcessor.DetermineCurrentLimit(); + + if (_rowLimitProcessor.CurrentRowLimit == 1 && + _rowLimitProcessor.CurrentOffset == 0 && + outgoingEvents != null && + outgoingEvents.Length > 1) + { + EventBean minmax = _orderByProcessor.DetermineLocalMinMax(outgoingEvents, generatingEvents, isNewData, exprEvaluatorContext); + return new EventBean[] { minmax }; + } + + EventBean[] sorted = _orderByProcessor.Sort(outgoingEvents, generatingEvents, isNewData, exprEvaluatorContext); + return _rowLimitProcessor.ApplyLimit(sorted); + } + + public EventBean[] Sort(EventBean[] outgoingEvents, IList currentGenerators, bool newData, AgentInstanceContext agentInstanceContext, OrderByElement[][] elementsPerLevel) { + EventBean[] sorted = _orderByProcessor.Sort(outgoingEvents, currentGenerators, newData, agentInstanceContext, elementsPerLevel); + return _rowLimitProcessor.ApplyLimit(sorted); + } + + public EventBean[] Sort(EventBean[] outgoingEvents, EventBean[][] generatingEvents, Object[] groupByKeys, bool isNewData, ExprEvaluatorContext exprEvaluatorContext) + { + EventBean[] sorted = _orderByProcessor.Sort(outgoingEvents, generatingEvents, groupByKeys, isNewData, exprEvaluatorContext); + return _rowLimitProcessor.ApplyLimit(sorted); + } + + public Object GetSortKey(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext exprEvaluatorContext) + { + return _orderByProcessor.GetSortKey(eventsPerStream, isNewData, exprEvaluatorContext); + } + + public Object GetSortKey(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext exprEvaluatorContext, OrderByElement[] elementsForLevel) { + return _orderByProcessor.GetSortKey(eventsPerStream, isNewData, exprEvaluatorContext, elementsForLevel); + } + + public Object[] GetSortKeyPerRow(EventBean[] generatingEvents, bool isNewData, ExprEvaluatorContext exprEvaluatorContext) + { + return _orderByProcessor.GetSortKeyPerRow(generatingEvents, isNewData, exprEvaluatorContext); + } + + public EventBean[] Sort(EventBean[] outgoingEvents, Object[] orderKeys, ExprEvaluatorContext exprEvaluatorContext) + { + _rowLimitProcessor.DetermineCurrentLimit(); + + if (_rowLimitProcessor.CurrentRowLimit == 1 && + _rowLimitProcessor.CurrentOffset == 0 && + outgoingEvents != null && + outgoingEvents.Length > 1) + { + EventBean minmax = _orderByProcessor.DetermineLocalMinMax(outgoingEvents, orderKeys); + return new EventBean[] { minmax }; + } + + EventBean[] sorted = _orderByProcessor.Sort(outgoingEvents, orderKeys, exprEvaluatorContext); + return _rowLimitProcessor.ApplyLimit(sorted); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/OrderByProcessorOrderedLimitFactory.cs b/NEsper.Core/NEsper.Core/epl/core/OrderByProcessorOrderedLimitFactory.cs new file mode 100755 index 000000000..c510c9dfc --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/OrderByProcessorOrderedLimitFactory.cs @@ -0,0 +1,35 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.agg.service; + +namespace com.espertech.esper.epl.core +{ + /// + /// Sorter and row limiter in one: sorts using a sorter and row limits + /// + public class OrderByProcessorOrderedLimitFactory : OrderByProcessorFactory + { + private readonly OrderByProcessorFactoryImpl _orderByProcessorFactory; + private readonly RowLimitProcessorFactory _rowLimitProcessorFactory; + + public OrderByProcessorOrderedLimitFactory(OrderByProcessorFactoryImpl orderByProcessorFactory, RowLimitProcessorFactory rowLimitProcessorFactory) + { + _orderByProcessorFactory = orderByProcessorFactory; + _rowLimitProcessorFactory = rowLimitProcessorFactory; + } + + public OrderByProcessor Instantiate(AggregationService aggregationService, AgentInstanceContext agentInstanceContext) + { + var orderByProcessor = (OrderByProcessorImpl) _orderByProcessorFactory.Instantiate(aggregationService, agentInstanceContext); + var rowLimitProcessor = _rowLimitProcessorFactory.Instantiate(agentInstanceContext); + return new OrderByProcessorOrderedLimit(orderByProcessor, rowLimitProcessor); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/OrderByProcessorRowLimitOnly.cs b/NEsper.Core/NEsper.Core/epl/core/OrderByProcessorRowLimitOnly.cs new file mode 100755 index 000000000..665455189 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/OrderByProcessorRowLimitOnly.cs @@ -0,0 +1,64 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.agg.rollup; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.core +{ + /// + /// An order-by processor that sorts events according to the expressions + /// in the order_by clause. + /// + public class OrderByProcessorRowLimitOnly : OrderByProcessor + { + private readonly RowLimitProcessor _rowLimitProcessor; + + public OrderByProcessorRowLimitOnly(RowLimitProcessor rowLimitProcessor) + { + _rowLimitProcessor = rowLimitProcessor; + } + + public EventBean[] Sort(EventBean[] outgoingEvents, EventBean[][] generatingEvents, bool isNewData, ExprEvaluatorContext exprEvaluatorContext) + { + return _rowLimitProcessor.DetermineLimitAndApply(outgoingEvents); + } + + public EventBean[] Sort(EventBean[] outgoingEvents, EventBean[][] generatingEvents, object[] groupByKeys, bool isNewData, ExprEvaluatorContext exprEvaluatorContext) + { + return _rowLimitProcessor.DetermineLimitAndApply(outgoingEvents); + } + + public EventBean[] Sort(EventBean[] outgoingEvents, IList currentGenerators, bool newData, AgentInstanceContext agentInstanceContext, OrderByElement[][] elementsPerLevel) { + return _rowLimitProcessor.DetermineLimitAndApply(outgoingEvents); + } + + public object GetSortKey(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext exprEvaluatorContext) + { + return null; + } + + public object GetSortKey(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext exprEvaluatorContext, OrderByElement[] elementsForLevel) { + return null; + } + + public object[] GetSortKeyPerRow(EventBean[] generatingEvents, bool isNewData, ExprEvaluatorContext exprEvaluatorContext) + { + return null; + } + + public EventBean[] Sort(EventBean[] outgoingEvents, object[] orderKeys, ExprEvaluatorContext exprEvaluatorContext) + { + return _rowLimitProcessor.DetermineLimitAndApply(outgoingEvents); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/OrderByProcessorRowLimitOnlyFactory.cs b/NEsper.Core/NEsper.Core/epl/core/OrderByProcessorRowLimitOnlyFactory.cs new file mode 100755 index 000000000..e5ec1092f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/OrderByProcessorRowLimitOnlyFactory.cs @@ -0,0 +1,33 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.agg.service; + +namespace com.espertech.esper.epl.core +{ + /// + /// An order-by processor that sorts events according to the expressions + /// in the order_by clause. + /// + public class OrderByProcessorRowLimitOnlyFactory : OrderByProcessorFactory + { + private readonly RowLimitProcessorFactory _rowLimitProcessorFactory; + + public OrderByProcessorRowLimitOnlyFactory(RowLimitProcessorFactory rowLimitProcessorFactory) + { + _rowLimitProcessorFactory = rowLimitProcessorFactory; + } + + public OrderByProcessor Instantiate(AggregationService aggregationService, AgentInstanceContext agentInstanceContext) + { + RowLimitProcessor rowLimitProcessor = _rowLimitProcessorFactory.Instantiate(agentInstanceContext); + return new OrderByProcessorRowLimitOnly(rowLimitProcessor); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/PropertyNotFoundException.cs b/NEsper.Core/NEsper.Core/epl/core/PropertyNotFoundException.cs new file mode 100755 index 000000000..c8c9c7b8f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/PropertyNotFoundException.cs @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.core +{ + /// Exception to indicate that a property name used in a filter doesn't resolve. + [Serializable] + public class PropertyNotFoundException : StreamTypesException + { + /// + /// Ctor. + /// + /// The message without detail. + /// The MSG gen. + public PropertyNotFoundException(String messageWithoutDetail, StreamTypesExceptionSuggestionGen msgGen) + : base(messageWithoutDetail, msgGen) + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/PropertyResolutionDescriptor.cs b/NEsper.Core/NEsper.Core/epl/core/PropertyResolutionDescriptor.cs new file mode 100755 index 000000000..673288ce8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/PropertyResolutionDescriptor.cs @@ -0,0 +1,70 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.epl.core +{ + /// Encapsulates the result of resolving a property and optional stream name against a supplied list of streams + /// . + /// + public class PropertyResolutionDescriptor + { + /// Returns stream name. + /// stream name + /// + public String StreamName { get; private set; } + + /// Returns event type of the stream that the property was found in. + /// stream's event type + /// + public EventType StreamEventType { get; private set; } + + /// Returns resolved property name of the property as it exists in a stream. + /// property name as resolved in a stream + /// + public String PropertyName { get; private set; } + + /// Returns the number of the stream the property was found in. + /// stream offset number Starting at zero to Count-1 where Count is the number of streams + /// + public int StreamNum { get; private set; } + + /// Returns the property type of the resolved property. + /// class of property + /// + public Type PropertyType { get; private set; } + + /// + /// Gets or sets the type of the fragment event. + /// + /// The type of the fragment event. + public FragmentEventType FragmentEventType { get; private set; } + + /// + /// Ctor. + /// + /// is the stream name + /// is the event type of the stream where the property was found + /// is the regular name of property + /// is the number offset of the stream + /// is the type of the property + /// Type of the fragment event. + public PropertyResolutionDescriptor(String streamName, EventType streamEventType, String propertyName, int streamNum, Type propertyType, FragmentEventType fragmentEventType) + { + StreamNum = streamNum; + StreamName = streamName; + StreamEventType = streamEventType; + PropertyName = propertyName; + PropertyType = propertyType; + FragmentEventType = fragmentEventType; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetAggregateAllEnumerator.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetAggregateAllEnumerator.cs new file mode 100755 index 000000000..0410ecf8d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetAggregateAllEnumerator.cs @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.core +{ + /// + /// Enumerator for aggregation results that aggregate all rows. + /// + + public class ResultSetAggregateAllEnumerator + { + public static IEnumerator New( + IEnumerable source, + ResultSetProcessorAggregateAll resultSetProcessor, + ExprEvaluatorContext exprEvaluatorContext) + { + var eventsPerStream = new EventBean[1]; + var evaluateParams = new EvaluateParams(eventsPerStream, true, exprEvaluatorContext); + var optionHavingNode = resultSetProcessor.OptionalHavingNode; + var selectExprProcessor = resultSetProcessor.SelectExprProcessor; + + foreach (EventBean eventBean in source) + { + eventsPerStream[0] = eventBean; + + if (optionHavingNode != null) + { + var pass = optionHavingNode.Evaluate( + evaluateParams); + if (false.Equals(pass)) + continue; + } + + yield return selectExprProcessor.Process( + eventsPerStream, true, true, exprEvaluatorContext); + } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetAggregateGroupedIterator.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetAggregateGroupedIterator.cs new file mode 100755 index 000000000..89726c3e5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetAggregateGroupedIterator.cs @@ -0,0 +1,126 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.core +{ + /// + /// GetEnumerator for group-by with aggregation. + /// + public class ResultSetAggregateGroupedIterator : IEnumerator + { + private readonly IEnumerator _sourceIterator; + private readonly ResultSetProcessorAggregateGrouped _resultSetProcessor; + private readonly AggregationService _aggregationService; + private EventBean _currResult; + private bool _iterate; + private readonly EventBean[] _eventsPerStream; + private readonly ExprEvaluatorContext _exprEvaluatorContext; + + /// + /// Ctor. + /// + /// is the parent iterator + /// for constructing result rows + /// for pointing to the right aggregation row + /// context for expression evalauation + public ResultSetAggregateGroupedIterator( + IEnumerator sourceIterator, + ResultSetProcessorAggregateGrouped resultSetProcessor, + AggregationService aggregationService, + ExprEvaluatorContext exprEvaluatorContext) + { + _sourceIterator = sourceIterator; + _resultSetProcessor = resultSetProcessor; + _aggregationService = aggregationService; + _eventsPerStream = new EventBean[1]; + _exprEvaluatorContext = exprEvaluatorContext; + _currResult = null; + _iterate = true; + } + + /// + /// Gets the element in the collection at the current position of the enumerator. + /// + object IEnumerator.Current + { + get { return Current; } + } + + /// + /// Gets the element in the collection at the current position of the enumerator. + /// + public EventBean Current + { + get { return _currResult; } + } + + /// + /// Advances the enumerator to the next element of the collection. + /// + /// + /// true if the enumerator was successfully advanced to the next element; false if the enumerator has passed the end of the collection. + /// + public bool MoveNext() + { + while (_iterate && _sourceIterator.MoveNext()) + { + var candidate = _sourceIterator.Current; + _eventsPerStream[0] = candidate; + + var groupKey = _resultSetProcessor.GenerateGroupKey(_eventsPerStream, true); + _aggregationService.SetCurrentAccess(groupKey, _exprEvaluatorContext.AgentInstanceId, null); + + bool? pass = true; + if (_resultSetProcessor.OptionalHavingNode != null) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QHavingClauseJoin(_eventsPerStream);} + pass = _resultSetProcessor.OptionalHavingNode.Evaluate(new EvaluateParams(_eventsPerStream, true, _exprEvaluatorContext)).AsBoxedBoolean(); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AHavingClauseJoin(pass);} + } + + if (false.Equals(pass)) + { + continue; + } + + _currResult = _resultSetProcessor.SelectExprProcessor.Process(_eventsPerStream, true, true, _exprEvaluatorContext); + return true; + } + + return _iterate = false; + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + } + + /// + /// Sets the enumerator to its initial position, which is before the first element in the collection. + /// + /// + public void Reset() + { + throw new NotSupportedException(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessor.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessor.cs new file mode 100755 index 000000000..138048781 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessor.cs @@ -0,0 +1,118 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.spec; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.epl.core +{ + /// + /// Processor for result sets coming from 2 sources. First, out of a simple view (no join). + /// And second, out of a join of event streams. The processor must apply the select-clause, + /// group-by-clause and having-clauses as supplied. It must state what the event type of + /// the result rows is. + /// + public interface ResultSetProcessor : StopCallback + { + /// + /// Returns the event type of processed results. + /// + /// event type of the resulting events posted by the processor. + EventType ResultEventType { get; } + + /// + /// For use by views posting their result, process the event rows that are entered and removed + /// (new and old events). Processes according to select-clauses, group-by clauses and having-clauses + /// and returns new events and old events as specified. + /// + /// new events posted by view + /// old events posted by view + /// set to true to indicate that synthetic events are required for an iterator result set + /// pair of new events and old events + UniformPair ProcessViewResult( + EventBean[] newData, + EventBean[] oldData, + bool isSynthesize); + + /// + /// For use by joins posting their result, process the event rows that are entered and removed + /// (new and old events). Processes according to select-clauses, group-by clauses and having-clauses + /// and returns new events and old events as specified. + /// + /// new events posted by join + /// old events posted by join + /// set to true to indicate that synthetic events are required for an iterator result set + /// pair of new events and old events + UniformPair ProcessJoinResult( + ISet> newEvents, + ISet> oldEvents, + bool isSynthesize); + + /// + /// Returns the enumerator implementing the group-by and aggregation and order-by logic + /// specific to each case of use of these construct. + /// + /// is the parent view iterator + /// event iterator + IEnumerator GetEnumerator(Viewable parent); + + /// Returns the iterator for iterating over a join-result. + /// is the join result set + /// iterator over join results + IEnumerator GetEnumerator(ISet> joinSet); + + /// Clear out current state. + void Clear(); + + /// Processes batched events in case of output-rate limiting. + /// the join results + /// flag to indicate whether synthetic events must be generated + /// the type of output rate limiting + /// results for dispatch + UniformPair ProcessOutputLimitedJoin(IList>>> joinEventsSet, + bool generateSynthetic, + OutputLimitLimitType outputLimitLimitType); + + /// Processes batched events in case of output-rate limiting. + /// the view results + /// flag to indicate whether synthetic events must be generated + /// the type of output rate limiting + /// results for dispatch + UniformPair ProcessOutputLimitedView(IList> viewEventsList, + bool generateSynthetic, + OutputLimitLimitType outputLimitLimitType); + + bool HasAggregation { get; } + + /// + /// Sets the agent instance context. + /// + /// The context. + AgentInstanceContext AgentInstanceContext { set; } + + void ApplyViewResult(EventBean[] newData, EventBean[] oldData); + + void ApplyJoinResult(ISet> newEvents, ISet> oldEvents); + + void ProcessOutputLimitedLastAllNonBufferedView(EventBean[] newData, EventBean[] oldData, bool isGenerateSynthetic, bool isAll); + + void ProcessOutputLimitedLastAllNonBufferedJoin(ISet> newEvents, ISet> oldEvents, bool isGenerateSynthetic, bool isAll); + + UniformPair ContinueOutputLimitedLastAllNonBufferedView(bool isSynthesize, bool isAll); + + UniformPair ContinueOutputLimitedLastAllNonBufferedJoin(bool isSynthesize, bool isAll); + + void AcceptHelperVisitor(ResultSetProcessorOutputHelperVisitor visitor); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorAggregateAll.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorAggregateAll.cs new file mode 100755 index 000000000..be0bfbd56 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorAggregateAll.cs @@ -0,0 +1,812 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.view; + +namespace com.espertech.esper.epl.core +{ + /// + /// Result set processor for the case: aggregation functions used in the select clause, and no group-by, + /// and not all of the properties in the select clause are under an aggregation function. + /// This processor does not perform grouping, every event entering and leaving is in the same group. + /// The processor generates one row for each event entering (new event) and one row for each event leaving (old event). + /// Aggregation state is simply one row holding all the state. + /// + public class ResultSetProcessorAggregateAll : ResultSetProcessor + { + private readonly ResultSetProcessorAggregateAllFactory _prototype; + private readonly SelectExprProcessor _selectExprProcessor; + private readonly OrderByProcessor _orderByProcessor; + private readonly AggregationService _aggregationService; + private ExprEvaluatorContext _exprEvaluatorContext; + private readonly ResultSetProcessorAggregateAllOutputLastHelper _outputLastUnordHelper; + private readonly ResultSetProcessorAggregateAllOutputAllHelper _outputAllUnordHelper; + + public ResultSetProcessorAggregateAll( + ResultSetProcessorAggregateAllFactory prototype, + SelectExprProcessor selectExprProcessor, + OrderByProcessor orderByProcessor, + AggregationService aggregationService, + AgentInstanceContext agentInstanceContext) + { + _prototype = prototype; + _selectExprProcessor = selectExprProcessor; + _orderByProcessor = orderByProcessor; + _aggregationService = aggregationService; + _exprEvaluatorContext = agentInstanceContext; + _outputLastUnordHelper = prototype.IsEnableOutputLimitOpt && prototype.IsOutputLast ? prototype.ResultSetProcessorHelperFactory.MakeRSAggregateAllOutputLast(this, agentInstanceContext) : null; + _outputAllUnordHelper = prototype.IsEnableOutputLimitOpt && prototype.IsOutputAll ? prototype.ResultSetProcessorHelperFactory.MakeRSAggregateAllOutputAll(this, agentInstanceContext) : null; + } + + public AgentInstanceContext AgentInstanceContext + { + set { _exprEvaluatorContext = value; } + } + + public EventType ResultEventType + { + get { return _prototype.ResultEventType; } + } + + public void ApplyViewResult(EventBean[] newData, EventBean[] oldData) + { + var eventsPerStream = new EventBean[1]; + ResultSetProcessorUtil.ApplyAggViewResult(_aggregationService, _exprEvaluatorContext, newData, oldData, eventsPerStream); + } + + public void ApplyJoinResult(ISet> newEvents, ISet> oldEvents) + { + ResultSetProcessorUtil.ApplyAggJoinResult(_aggregationService, _exprEvaluatorContext, newEvents, oldEvents); + } + + public UniformPair ProcessJoinResult(ISet> newEvents, ISet> oldEvents, bool isSynthesize) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QResultSetProcessUngroupedNonfullyAgg(); } + EventBean[] selectOldEvents = null; + EventBean[] selectNewEvents; + + if (_prototype.IsUnidirectional) + { + Clear(); + } + + ResultSetProcessorUtil.ApplyAggJoinResult(_aggregationService, _exprEvaluatorContext, newEvents, oldEvents); + + if (_prototype.OptionalHavingNode == null) + { + if (_prototype.IsSelectRStream) + { + if (_orderByProcessor == null) + { + selectOldEvents = ResultSetProcessorUtil.GetSelectJoinEventsNoHaving(_selectExprProcessor, oldEvents, false, isSynthesize, _exprEvaluatorContext); + } + else + { + selectOldEvents = ResultSetProcessorUtil.GetSelectJoinEventsNoHavingWithOrderBy(_selectExprProcessor, _orderByProcessor, oldEvents, false, isSynthesize, _exprEvaluatorContext); + } + } + + if (_orderByProcessor == null) + { + selectNewEvents = ResultSetProcessorUtil.GetSelectJoinEventsNoHaving(_selectExprProcessor, newEvents, true, isSynthesize, _exprEvaluatorContext); + } + else + { + selectNewEvents = ResultSetProcessorUtil.GetSelectJoinEventsNoHavingWithOrderBy(_selectExprProcessor, _orderByProcessor, newEvents, true, isSynthesize, _exprEvaluatorContext); + } + } + else + { + if (_prototype.IsSelectRStream) + { + if (_orderByProcessor == null) + { + selectOldEvents = ResultSetProcessorUtil.GetSelectJoinEventsHaving(_selectExprProcessor, oldEvents, _prototype.OptionalHavingNode, false, isSynthesize, _exprEvaluatorContext); + } + else + { + selectOldEvents = ResultSetProcessorUtil.GetSelectJoinEventsHavingWithOrderBy(_selectExprProcessor, _orderByProcessor, oldEvents, _prototype.OptionalHavingNode, false, isSynthesize, _exprEvaluatorContext); + } + } + + if (_orderByProcessor == null) + { + selectNewEvents = ResultSetProcessorUtil.GetSelectJoinEventsHaving(_selectExprProcessor, newEvents, _prototype.OptionalHavingNode, true, isSynthesize, _exprEvaluatorContext); + } + else + { + selectNewEvents = ResultSetProcessorUtil.GetSelectJoinEventsHavingWithOrderBy(_selectExprProcessor, _orderByProcessor, newEvents, _prototype.OptionalHavingNode, true, isSynthesize, _exprEvaluatorContext); + } + } + + if ((selectNewEvents == null) && (selectOldEvents == null)) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AResultSetProcessUngroupedNonfullyAgg(null, null); } + return null; + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AResultSetProcessUngroupedNonfullyAgg(selectNewEvents, selectOldEvents); } + return new UniformPair(selectNewEvents, selectOldEvents); + } + + public UniformPair ProcessViewResult(EventBean[] newData, EventBean[] oldData, bool isSynthesize) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QResultSetProcessUngroupedNonfullyAgg(); } + EventBean[] selectOldEvents = null; + EventBean[] selectNewEvents; + + var eventsPerStream = new EventBean[1]; + ResultSetProcessorUtil.ApplyAggViewResult(_aggregationService, _exprEvaluatorContext, newData, oldData, eventsPerStream); + + // generate new events using select expressions + if (_prototype.OptionalHavingNode == null) + { + if (_prototype.IsSelectRStream) + { + if (_orderByProcessor == null) + { + selectOldEvents = ResultSetProcessorUtil.GetSelectEventsNoHaving(_selectExprProcessor, oldData, false, isSynthesize, _exprEvaluatorContext); + } + else + { + selectOldEvents = ResultSetProcessorUtil.GetSelectEventsNoHavingWithOrderBy(_selectExprProcessor, _orderByProcessor, oldData, false, isSynthesize, _exprEvaluatorContext); + } + } + + if (_orderByProcessor == null) + { + selectNewEvents = ResultSetProcessorUtil.GetSelectEventsNoHaving(_selectExprProcessor, newData, true, isSynthesize, _exprEvaluatorContext); + } + else + { + selectNewEvents = ResultSetProcessorUtil.GetSelectEventsNoHavingWithOrderBy(_selectExprProcessor, _orderByProcessor, newData, true, isSynthesize, _exprEvaluatorContext); + } + } + else + { + if (_prototype.IsSelectRStream) + { + if (_orderByProcessor == null) + { + selectOldEvents = ResultSetProcessorUtil.GetSelectEventsHaving(_selectExprProcessor, oldData, _prototype.OptionalHavingNode, false, isSynthesize, _exprEvaluatorContext); + } + else + { + selectOldEvents = ResultSetProcessorUtil.GetSelectEventsHavingWithOrderBy(_selectExprProcessor, _orderByProcessor, oldData, _prototype.OptionalHavingNode, false, isSynthesize, _exprEvaluatorContext); + } + } + + if (_orderByProcessor == null) + { + selectNewEvents = ResultSetProcessorUtil.GetSelectEventsHaving(_selectExprProcessor, newData, _prototype.OptionalHavingNode, true, isSynthesize, _exprEvaluatorContext); + } + else + { + selectNewEvents = ResultSetProcessorUtil.GetSelectEventsHavingWithOrderBy(_selectExprProcessor, _orderByProcessor, newData, _prototype.OptionalHavingNode, true, isSynthesize, _exprEvaluatorContext); + } + } + + if ((selectNewEvents == null) && (selectOldEvents == null)) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AResultSetProcessUngroupedNonfullyAgg(null, null); } + return null; + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AResultSetProcessUngroupedNonfullyAgg(selectNewEvents, selectOldEvents); } + return new UniformPair(selectNewEvents, selectOldEvents); + } + + public static readonly IList EMPTY_EVENT_BEAN_LIST = new EventBean[0]; + + public IEnumerator GetEnumerator(Viewable parent) + { + if (!_prototype.IsHistoricalOnly) + { + return ObtainEnumerator(parent); + } + + ResultSetProcessorUtil.ClearAndAggregateUngrouped(_exprEvaluatorContext, _aggregationService, parent); + ArrayDeque deque = ResultSetProcessorUtil.EnumeratorToDeque(ObtainEnumerator(parent)); + _aggregationService.ClearResults(_exprEvaluatorContext); + return deque.GetEnumerator(); + } + + public IEnumerator ObtainEnumerator(Viewable parent) + { + if (_orderByProcessor == null) + { + return ResultSetAggregateAllEnumerator.New(parent, this, _exprEvaluatorContext); + } + + // Pull all parent events, generate order keys + var eventsPerStream = new EventBean[1]; + var outgoingEvents = new List(); + var orderKeys = new List(); + + var evaluateParams = new EvaluateParams(eventsPerStream, true, _exprEvaluatorContext); + + foreach (var candidate in parent) + { + eventsPerStream[0] = candidate; + + object pass = true; + if (_prototype.OptionalHavingNode != null) + { + pass = _prototype.OptionalHavingNode.Evaluate(evaluateParams); + } + if ((pass == null) || (false.Equals(pass))) + { + continue; + } + + outgoingEvents.Add(_selectExprProcessor.Process(eventsPerStream, true, true, _exprEvaluatorContext)); + + var orderKey = _orderByProcessor.GetSortKey(eventsPerStream, true, _exprEvaluatorContext); + orderKeys.Add(orderKey); + } + + // sort + var outgoingEventsArr = outgoingEvents.ToArray(); + var orderKeysArr = orderKeys.ToArray(); + var orderedEvents = _orderByProcessor.Sort(outgoingEventsArr, orderKeysArr, _exprEvaluatorContext) ?? EMPTY_EVENT_BEAN_LIST; + + return orderedEvents.GetEnumerator(); + } + + /// + /// Returns the select expression processor + /// + /// select processor. + public SelectExprProcessor SelectExprProcessor + { + get { return _selectExprProcessor; } + } + + /// + /// Returns the optional having expression. + /// + /// having expression node + public ExprEvaluator OptionalHavingNode + { + get { return _prototype.OptionalHavingNode; } + } + + public IEnumerator GetEnumerator(ISet> joinSet) + { + IList result; + if (_prototype.OptionalHavingNode == null) + { + if (_orderByProcessor == null) + { + result = ResultSetProcessorUtil.GetSelectJoinEventsNoHaving(_selectExprProcessor, joinSet, true, true, _exprEvaluatorContext); + } + else + { + result = ResultSetProcessorUtil.GetSelectJoinEventsNoHavingWithOrderBy(_selectExprProcessor, _orderByProcessor, joinSet, true, true, _exprEvaluatorContext); + } + } + else + { + if (_orderByProcessor == null) + { + result = ResultSetProcessorUtil.GetSelectJoinEventsHaving(_selectExprProcessor, joinSet, _prototype.OptionalHavingNode, true, true, _exprEvaluatorContext); + } + else + { + result = ResultSetProcessorUtil.GetSelectJoinEventsHavingWithOrderBy(_selectExprProcessor, _orderByProcessor, joinSet, _prototype.OptionalHavingNode, true, true, _exprEvaluatorContext); + } + } + return result != null ? result.GetEnumerator() : EnumerationHelper.Empty(); + } + + public void Clear() + { + _aggregationService.ClearResults(_exprEvaluatorContext); + } + + public UniformPair ProcessOutputLimitedJoin(IList>>> joinEventsSet, bool generateSynthetic, OutputLimitLimitType outputLimitLimitType) + { + if (outputLimitLimitType == OutputLimitLimitType.LAST) + { + return ProcessOutputLimitedJoinLast(joinEventsSet, generateSynthetic); + } + else + { + return ProcessOutputLimitedJoinDefault(joinEventsSet, generateSynthetic); + } + } + + public UniformPair ProcessOutputLimitedView(IList> viewEventsList, bool generateSynthetic, OutputLimitLimitType outputLimitLimitType) + { + if (outputLimitLimitType == OutputLimitLimitType.LAST) + { + return ProcessOutputLimitedViewLast(viewEventsList, generateSynthetic); + } + else + { + return ProcessOutputLimitedViewDefault(viewEventsList, generateSynthetic); + } + } + + public bool HasAggregation + { + get { return true; } + } + + public void ProcessOutputLimitedLastAllNonBufferedView(EventBean[] newData, EventBean[] oldData, bool isGenerateSynthetic, bool isAll) + { + if (isAll) + { + _outputAllUnordHelper.ProcessView(newData, oldData, isGenerateSynthetic); + } + else + { + _outputLastUnordHelper.ProcessView(newData, oldData, isGenerateSynthetic); + } + } + + public void ProcessOutputLimitedLastAllNonBufferedJoin(ISet> newEvents, ISet> oldEvents, bool isGenerateSynthetic, bool isAll) + { + if (isAll) + { + _outputAllUnordHelper.ProcessJoin(newEvents, oldEvents, isGenerateSynthetic); + } + else + { + _outputLastUnordHelper.ProcessJoin(newEvents, oldEvents, isGenerateSynthetic); + } + } + + public UniformPair ContinueOutputLimitedLastAllNonBufferedView(bool isSynthesize, bool isAll) + { + if (isAll) + { + return _outputAllUnordHelper.Output(); + } + return _outputLastUnordHelper.Output(); + } + + public UniformPair ContinueOutputLimitedLastAllNonBufferedJoin(bool isSynthesize, bool isAll) + { + if (isAll) + { + return _outputAllUnordHelper.Output(); + } + return _outputLastUnordHelper.Output(); + } + + public void Stop() + { + if (_outputLastUnordHelper != null) + { + _outputLastUnordHelper.Destroy(); + } + if (_outputAllUnordHelper != null) + { + _outputAllUnordHelper.Destroy(); + } + } + + private UniformPair ProcessOutputLimitedJoinDefault(IList>>> joinEventsSet, bool generateSynthetic) + { + IList newEvents = new List(); + IList oldEvents = null; + if (_prototype.IsSelectRStream) + { + oldEvents = new List(); + } + + IList newEventsSortKey = null; + IList oldEventsSortKey = null; + if (_orderByProcessor != null) + { + newEventsSortKey = new List(); + if (_prototype.IsSelectRStream) + { + oldEventsSortKey = new List(); + } + } + + foreach (var pair in joinEventsSet) + { + var newData = pair.First; + var oldData = pair.Second; + + if (_prototype.IsUnidirectional) + { + Clear(); + } + + if (newData != null) + { + // apply new data to aggregates + foreach (var row in newData) + { + _aggregationService.ApplyEnter(row.Array, null, _exprEvaluatorContext); + } + } + if (oldData != null) + { + // apply old data to aggregates + foreach (var row in oldData) + { + _aggregationService.ApplyLeave(row.Array, null, _exprEvaluatorContext); + } + } + + // generate old events using select expressions + if (_prototype.IsSelectRStream) + { + if (_prototype.OptionalHavingNode == null) + { + if (_orderByProcessor == null) + { + ResultSetProcessorUtil.PopulateSelectJoinEventsNoHaving(_selectExprProcessor, oldData, false, generateSynthetic, oldEvents, _exprEvaluatorContext); + } + else + { + ResultSetProcessorUtil.PopulateSelectJoinEventsNoHavingWithOrderBy(_selectExprProcessor, _orderByProcessor, oldData, false, generateSynthetic, oldEvents, oldEventsSortKey, _exprEvaluatorContext); + } + } + // generate old events using having then select + else + { + if (_orderByProcessor == null) + { + ResultSetProcessorUtil.PopulateSelectJoinEventsHaving(_selectExprProcessor, oldData, _prototype.OptionalHavingNode, false, generateSynthetic, oldEvents, _exprEvaluatorContext); + } + else + { + ResultSetProcessorUtil.PopulateSelectJoinEventsHavingWithOrderBy(_selectExprProcessor, _orderByProcessor, oldData, _prototype.OptionalHavingNode, false, generateSynthetic, oldEvents, oldEventsSortKey, _exprEvaluatorContext); + } + } + } + + // generate new events using select expressions + if (_prototype.OptionalHavingNode == null) + { + if (_orderByProcessor == null) + { + ResultSetProcessorUtil.PopulateSelectJoinEventsNoHaving(_selectExprProcessor, newData, true, generateSynthetic, newEvents, _exprEvaluatorContext); + } + else + { + ResultSetProcessorUtil.PopulateSelectJoinEventsNoHavingWithOrderBy(_selectExprProcessor, _orderByProcessor, newData, true, generateSynthetic, newEvents, newEventsSortKey, _exprEvaluatorContext); + } + } + else + { + if (_orderByProcessor == null) + { + ResultSetProcessorUtil.PopulateSelectJoinEventsHaving(_selectExprProcessor, newData, _prototype.OptionalHavingNode, true, generateSynthetic, newEvents, _exprEvaluatorContext); + } + else + { + ResultSetProcessorUtil.PopulateSelectJoinEventsHavingWithOrderBy(_selectExprProcessor, _orderByProcessor, newData, _prototype.OptionalHavingNode, true, generateSynthetic, newEvents, newEventsSortKey, _exprEvaluatorContext); + } + } + } + + var newEventsArr = (newEvents.IsEmpty()) ? null : newEvents.ToArray(); + EventBean[] oldEventsArr = null; + if (_prototype.IsSelectRStream) + { + oldEventsArr = (oldEvents.IsEmpty()) ? null : oldEvents.ToArray(); + } + + if (_orderByProcessor != null) + { + var sortKeysNew = (newEventsSortKey.IsEmpty()) ? null : newEventsSortKey.ToArray(); + newEventsArr = _orderByProcessor.Sort(newEventsArr, sortKeysNew, _exprEvaluatorContext); + if (_prototype.IsSelectRStream) + { + var sortKeysOld = (oldEventsSortKey.IsEmpty()) ? null : oldEventsSortKey.ToArray(); + oldEventsArr = _orderByProcessor.Sort(oldEventsArr, sortKeysOld, _exprEvaluatorContext); + } + } + + if ((newEventsArr == null) && (oldEventsArr == null)) + { + return null; + } + return new UniformPair(newEventsArr, oldEventsArr); + } + + private UniformPair ProcessOutputLimitedJoinLast(IList>>> joinEventsSet, bool generateSynthetic) + { + EventBean lastOldEvent = null; + EventBean lastNewEvent = null; + + foreach (var pair in joinEventsSet) + { + var newData = pair.First; + var oldData = pair.Second; + + if (_prototype.IsUnidirectional) + { + Clear(); + } + + if (newData != null) + { + // apply new data to aggregates + foreach (var eventsPerStream in newData) + { + _aggregationService.ApplyEnter(eventsPerStream.Array, null, _exprEvaluatorContext); + } + } + if (oldData != null) + { + // apply old data to aggregates + foreach (var eventsPerStream in oldData) + { + _aggregationService.ApplyLeave(eventsPerStream.Array, null, _exprEvaluatorContext); + } + } + + EventBean[] selectOldEvents; + if (_prototype.IsSelectRStream) + { + if (_prototype.OptionalHavingNode == null) + { + selectOldEvents = ResultSetProcessorUtil.GetSelectJoinEventsNoHaving(_selectExprProcessor, oldData, false, generateSynthetic, _exprEvaluatorContext); + } + else + { + selectOldEvents = ResultSetProcessorUtil.GetSelectJoinEventsHaving(_selectExprProcessor, oldData, _prototype.OptionalHavingNode, false, generateSynthetic, _exprEvaluatorContext); + } + if ((selectOldEvents != null) && (selectOldEvents.Length > 0)) + { + lastOldEvent = selectOldEvents[selectOldEvents.Length - 1]; + } + } + + // generate new events using select expressions + EventBean[] selectNewEvents; + if (_prototype.OptionalHavingNode == null) + { + selectNewEvents = ResultSetProcessorUtil.GetSelectJoinEventsNoHaving(_selectExprProcessor, newData, true, generateSynthetic, _exprEvaluatorContext); + } + else + { + selectNewEvents = ResultSetProcessorUtil.GetSelectJoinEventsHaving(_selectExprProcessor, newData, _prototype.OptionalHavingNode, true, generateSynthetic, _exprEvaluatorContext); + } + if ((selectNewEvents != null) && (selectNewEvents.Length > 0)) + { + lastNewEvent = selectNewEvents[selectNewEvents.Length - 1]; + } + } + + var lastNew = (lastNewEvent != null) ? new EventBean[] { lastNewEvent } : null; + var lastOld = (lastOldEvent != null) ? new EventBean[] { lastOldEvent } : null; + + if ((lastNew == null) && (lastOld == null)) + { + return null; + } + return new UniformPair(lastNew, lastOld); + } + + private UniformPair ProcessOutputLimitedViewDefault(IList> viewEventsList, bool generateSynthetic) + { + IList newEvents = new List(); + IList oldEvents = null; + if (_prototype.IsSelectRStream) + { + oldEvents = new List(); + } + IList newEventsSortKey = null; + IList oldEventsSortKey = null; + if (_orderByProcessor != null) + { + newEventsSortKey = new List(); + if (_prototype.IsSelectRStream) + { + oldEventsSortKey = new List(); + } + } + + foreach (var pair in viewEventsList) + { + var newData = pair.First; + var oldData = pair.Second; + + var eventsPerStream = new EventBean[1]; + if (newData != null) + { + // apply new data to aggregates + foreach (var aNewData in newData) + { + eventsPerStream[0] = aNewData; + _aggregationService.ApplyEnter(eventsPerStream, null, _exprEvaluatorContext); + } + } + if (oldData != null) + { + // apply old data to aggregates + foreach (var anOldData in oldData) + { + eventsPerStream[0] = anOldData; + _aggregationService.ApplyLeave(eventsPerStream, null, _exprEvaluatorContext); + } + } + + // generate old events using select expressions + if (_prototype.IsSelectRStream) + { + if (_prototype.OptionalHavingNode == null) + { + if (_orderByProcessor == null) + { + ResultSetProcessorUtil.PopulateSelectEventsNoHaving(_selectExprProcessor, oldData, false, generateSynthetic, oldEvents, _exprEvaluatorContext); + } + else + { + ResultSetProcessorUtil.PopulateSelectEventsNoHavingWithOrderBy(_selectExprProcessor, _orderByProcessor, oldData, false, generateSynthetic, oldEvents, oldEventsSortKey, _exprEvaluatorContext); + } + } + // generate old events using having then select + else + { + if (_orderByProcessor == null) + { + ResultSetProcessorUtil.PopulateSelectEventsHaving(_selectExprProcessor, oldData, _prototype.OptionalHavingNode, false, generateSynthetic, oldEvents, _exprEvaluatorContext); + } + else + { + ResultSetProcessorUtil.PopulateSelectEventsHavingWithOrderBy(_selectExprProcessor, _orderByProcessor, oldData, _prototype.OptionalHavingNode, false, generateSynthetic, oldEvents, oldEventsSortKey, _exprEvaluatorContext); + } + } + } + + // generate new events using select expressions + if (_prototype.OptionalHavingNode == null) + { + if (_orderByProcessor == null) + { + ResultSetProcessorUtil.PopulateSelectEventsNoHaving(_selectExprProcessor, newData, true, generateSynthetic, newEvents, _exprEvaluatorContext); + } + else + { + ResultSetProcessorUtil.PopulateSelectEventsNoHavingWithOrderBy(_selectExprProcessor, _orderByProcessor, newData, true, generateSynthetic, newEvents, newEventsSortKey, _exprEvaluatorContext); + } + } + else + { + if (_orderByProcessor == null) + { + ResultSetProcessorUtil.PopulateSelectEventsHaving(_selectExprProcessor, newData, _prototype.OptionalHavingNode, true, generateSynthetic, newEvents, _exprEvaluatorContext); + } + else + { + ResultSetProcessorUtil.PopulateSelectEventsHavingWithOrderBy(_selectExprProcessor, _orderByProcessor, newData, _prototype.OptionalHavingNode, true, generateSynthetic, newEvents, newEventsSortKey, _exprEvaluatorContext); + } + } + } + + var newEventsArr = (newEvents.IsEmpty()) ? null : newEvents.ToArray(); + EventBean[] oldEventsArr = null; + if (_prototype.IsSelectRStream) + { + oldEventsArr = (oldEvents.IsEmpty()) ? null : oldEvents.ToArray(); + } + if (_orderByProcessor != null) + { + var sortKeysNew = (newEventsSortKey.IsEmpty()) ? null : newEventsSortKey.ToArray(); + newEventsArr = _orderByProcessor.Sort(newEventsArr, sortKeysNew, _exprEvaluatorContext); + + if (_prototype.IsSelectRStream) + { + var sortKeysOld = (oldEventsSortKey.IsEmpty()) ? null : oldEventsSortKey.ToArray(); + oldEventsArr = _orderByProcessor.Sort(oldEventsArr, sortKeysOld, _exprEvaluatorContext); + } + } + + if ((newEventsArr == null) && (oldEventsArr == null)) + { + return null; + } + return new UniformPair(newEventsArr, oldEventsArr); + } + + private UniformPair ProcessOutputLimitedViewLast(IList> viewEventsList, bool generateSynthetic) + { + EventBean lastOldEvent = null; + EventBean lastNewEvent = null; + var eventsPerStream = new EventBean[1]; + + foreach (var pair in viewEventsList) + { + var newData = pair.First; + var oldData = pair.Second; + + if (newData != null) + { + // apply new data to aggregates + foreach (var aNewData in newData) + { + eventsPerStream[0] = aNewData; + _aggregationService.ApplyEnter(eventsPerStream, null, _exprEvaluatorContext); + } + } + if (oldData != null) + { + // apply old data to aggregates + foreach (var anOldData in oldData) + { + eventsPerStream[0] = anOldData; + _aggregationService.ApplyLeave(eventsPerStream, null, _exprEvaluatorContext); + } + } + + EventBean[] selectOldEvents; + if (_prototype.IsSelectRStream) + { + if (_prototype.OptionalHavingNode == null) + { + selectOldEvents = ResultSetProcessorUtil.GetSelectEventsNoHaving(_selectExprProcessor, oldData, false, generateSynthetic, _exprEvaluatorContext); + } + else + { + selectOldEvents = ResultSetProcessorUtil.GetSelectEventsHaving(_selectExprProcessor, oldData, _prototype.OptionalHavingNode, false, generateSynthetic, _exprEvaluatorContext); + } + if ((selectOldEvents != null) && (selectOldEvents.Length > 0)) + { + lastOldEvent = selectOldEvents[selectOldEvents.Length - 1]; + } + } + + // generate new events using select expressions + EventBean[] selectNewEvents; + if (_prototype.OptionalHavingNode == null) + { + selectNewEvents = ResultSetProcessorUtil.GetSelectEventsNoHaving(_selectExprProcessor, newData, true, generateSynthetic, _exprEvaluatorContext); + } + else + { + selectNewEvents = ResultSetProcessorUtil.GetSelectEventsHaving(_selectExprProcessor, newData, _prototype.OptionalHavingNode, true, generateSynthetic, _exprEvaluatorContext); + } + if ((selectNewEvents != null) && (selectNewEvents.Length > 0)) + { + lastNewEvent = selectNewEvents[selectNewEvents.Length - 1]; + } + } + + var lastNew = (lastNewEvent != null) ? new EventBean[] { lastNewEvent } : null; + var lastOld = (lastOldEvent != null) ? new EventBean[] { lastOldEvent } : null; + + if ((lastNew == null) && (lastOld == null)) + { + return null; + } + return new UniformPair(lastNew, lastOld); + } + + public void AcceptHelperVisitor(ResultSetProcessorOutputHelperVisitor visitor) + { + if (_outputLastUnordHelper != null) + { + visitor.Visit(_outputLastUnordHelper); + } + if (_outputAllUnordHelper != null) + { + visitor.Visit(_outputAllUnordHelper); + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorAggregateAllFactory.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorAggregateAllFactory.cs new file mode 100755 index 000000000..4e7180edf --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorAggregateAllFactory.cs @@ -0,0 +1,109 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.epl.core +{ + /// + /// Result set processor _prototype for the case: aggregation functions used in the select + /// clause, and no group-by, and not all of the properties in the select clause are under + /// an aggregation function. + /// + public class ResultSetProcessorAggregateAllFactory : ResultSetProcessorFactory + { + private readonly SelectExprProcessor _selectExprProcessor; + + /// + /// Ctor. + /// + /// for processing the select expression and generting the readonly output rows + /// having clause expression node + /// true if remove stream events should be generated + /// true if unidirectional join + /// if set to true [is historical only]. + /// The output limit spec. + /// if set to true [enable output limit opt]. + /// The result set processor helper factory. + public ResultSetProcessorAggregateAllFactory( + SelectExprProcessor selectExprProcessor, + ExprEvaluator optionalHavingNode, + bool isSelectRStream, + bool isUnidirectional, + bool isHistoricalOnly, + OutputLimitSpec outputLimitSpec, + bool enableOutputLimitOpt, + ResultSetProcessorHelperFactory resultSetProcessorHelperFactory) + { + _selectExprProcessor = selectExprProcessor; + OutputLimitSpec = outputLimitSpec; + OptionalHavingNode = optionalHavingNode; + IsSelectRStream = isSelectRStream; + IsUnidirectional = isUnidirectional; + IsHistoricalOnly = isHistoricalOnly; + IsEnableOutputLimitOpt = enableOutputLimitOpt; + ResultSetProcessorHelperFactory = resultSetProcessorHelperFactory; + } + + public ExprEvaluator OptionalHavingNode { get; private set; } + + public bool IsSelectRStream { get; private set; } + + public bool IsUnidirectional { get; private set; } + + public bool IsHistoricalOnly { get; private set; } + + public bool IsOutputLast + { + get { return OutputLimitSpec != null && OutputLimitSpec.DisplayLimit == OutputLimitLimitType.LAST; } + } + + public bool IsOutputAll + { + get { return OutputLimitSpec != null && OutputLimitSpec.DisplayLimit == OutputLimitLimitType.ALL; } + } + + public bool IsEnableOutputLimitOpt { get; private set; } + + public ResultSetProcessorType ResultSetProcessorType + { + get { return ResultSetProcessorType.AGGREGATED_UNGROUPED; } + } + + public OutputLimitSpec OutputLimitSpec { get; private set; } + + public ResultSetProcessorHelperFactory ResultSetProcessorHelperFactory { get; private set; } + + #region ResultSetProcessorFactory Members + + public ResultSetProcessor Instantiate(OrderByProcessor orderByProcessor, + AggregationService aggregationService, + AgentInstanceContext agentInstanceContext) + { + return new ResultSetProcessorAggregateAll(this, _selectExprProcessor, orderByProcessor, aggregationService, + agentInstanceContext); + } + + public EventType ResultEventType + { + get { return _selectExprProcessor.ResultEventType; } + } + + public bool HasAggregation + { + get { return true; } + } + + #endregion + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorAggregateAllOutputAllHelper.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorAggregateAllOutputAllHelper.cs new file mode 100755 index 000000000..062823c10 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorAggregateAllOutputAllHelper.cs @@ -0,0 +1,24 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; + +namespace com.espertech.esper.epl.core +{ + public interface ResultSetProcessorAggregateAllOutputAllHelper + : ResultSetProcessorOutputHelper + { + void ProcessView(EventBean[] newData, EventBean[] oldData, bool isGenerateSynthetic); + void ProcessJoin(ISet> newEvents, ISet> oldEvents, bool isGenerateSynthetic); + UniformPair Output(); + void Destroy(); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorAggregateAllOutputAllHelperImpl.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorAggregateAllOutputAllHelperImpl.cs new file mode 100755 index 000000000..1598e97fc --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorAggregateAllOutputAllHelperImpl.cs @@ -0,0 +1,66 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.core +{ + public class ResultSetProcessorAggregateAllOutputAllHelperImpl : ResultSetProcessorAggregateAllOutputAllHelper + { + private readonly ResultSetProcessorAggregateAll _processor; + private readonly Deque _eventsOld = new ArrayDeque(2); + private readonly Deque _eventsNew = new ArrayDeque(2); + + public ResultSetProcessorAggregateAllOutputAllHelperImpl(ResultSetProcessorAggregateAll processor) { + this._processor = processor; + } + + public void ProcessView(EventBean[] newData, EventBean[] oldData, bool isGenerateSynthetic) { + UniformPair pair = _processor.ProcessViewResult(newData, oldData, isGenerateSynthetic); + Apply(pair); + } + + public void ProcessJoin(ISet> newEvents, ISet> oldEvents, bool isGenerateSynthetic) { + UniformPair pair = _processor.ProcessJoinResult(newEvents, oldEvents, isGenerateSynthetic); + Apply(pair); + } + + public UniformPair Output() { + EventBean[] oldEvents = EventBeanUtility.ToArrayNullIfEmpty(_eventsOld); + EventBean[] newEvents = EventBeanUtility.ToArrayNullIfEmpty(_eventsNew); + + UniformPair result = null; + if (oldEvents != null || newEvents != null) { + result = new UniformPair(newEvents, oldEvents); + } + + _eventsOld.Clear(); + _eventsNew.Clear(); + return result; + } + + public void Destroy() { + // no action required + } + + private void Apply(UniformPair pair) { + if (pair == null) { + return; + } + EventBeanUtility.AddToCollection(pair.First, _eventsNew); + EventBeanUtility.AddToCollection(pair.Second, _eventsOld); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorAggregateAllOutputLastHelper.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorAggregateAllOutputLastHelper.cs new file mode 100755 index 000000000..1fe36733b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorAggregateAllOutputLastHelper.cs @@ -0,0 +1,24 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; + +namespace com.espertech.esper.epl.core +{ + public interface ResultSetProcessorAggregateAllOutputLastHelper + : ResultSetProcessorOutputHelper + { + void ProcessView(EventBean[] newData, EventBean[] oldData, bool isGenerateSynthetic); + void ProcessJoin(ISet> newEvents, ISet> oldEvents, bool isGenerateSynthetic); + UniformPair Output(); + void Destroy(); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorAggregateAllOutputLastHelperImpl.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorAggregateAllOutputLastHelperImpl.cs new file mode 100755 index 000000000..1aa0deb16 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorAggregateAllOutputLastHelperImpl.cs @@ -0,0 +1,77 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.epl.core +{ + public class ResultSetProcessorAggregateAllOutputLastHelperImpl : ResultSetProcessorAggregateAllOutputLastHelper + { + private readonly ResultSetProcessorAggregateAll _processor; + + private EventBean _lastEventIStreamForOutputLast; + private EventBean _lastEventRStreamForOutputLast; + + public ResultSetProcessorAggregateAllOutputLastHelperImpl(ResultSetProcessorAggregateAll processor) { + this._processor = processor; + } + + public void ProcessView(EventBean[] newData, EventBean[] oldData, bool isGenerateSynthetic) { + var pair = _processor.ProcessViewResult(newData, oldData, isGenerateSynthetic); + Apply(pair); + } + + public void ProcessJoin(ISet> newEvents, ISet> oldEvents, bool isGenerateSynthetic) { + var pair = _processor.ProcessJoinResult(newEvents, oldEvents, isGenerateSynthetic); + Apply(pair); + } + + public UniformPair Output() { + UniformPair newOldEvents = null; + if (_lastEventIStreamForOutputLast != null) { + var istream = new EventBean[] {_lastEventIStreamForOutputLast}; + newOldEvents = new UniformPair(istream, null); + } + if (_lastEventRStreamForOutputLast != null) { + var rstream = new EventBean[] {_lastEventRStreamForOutputLast}; + if (newOldEvents == null) { + newOldEvents = new UniformPair(null, rstream); + } + else { + newOldEvents.Second = rstream; + } + } + + _lastEventIStreamForOutputLast = null; + _lastEventRStreamForOutputLast = null; + return newOldEvents; + } + + public void Destroy() { + // no action required + } + + private void Apply(UniformPair pair) { + if (pair == null) { + return; + } + if (pair.First != null && pair.First.Length > 0) { + _lastEventIStreamForOutputLast = pair.First[pair.First.Length - 1]; + } + if (pair.Second != null && pair.Second.Length > 0) { + _lastEventRStreamForOutputLast = pair.Second[pair.Second.Length - 1]; + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorAggregateGrouped.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorAggregateGrouped.cs new file mode 100755 index 000000000..a132207a0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorAggregateGrouped.cs @@ -0,0 +1,1885 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.view; + +namespace com.espertech.esper.epl.core +{ + /// + /// Result-set processor for the aggregate-grouped case: + /// there is a group-by and one or more non-aggregation event properties in the select clause are not listed in the group by, + /// and there are aggregation functions. + /// This processor does perform grouping by computing MultiKey group-by keys for each row. + /// The processor generates one row for each event entering (new event) and one row for each event leaving (old event). + /// Aggregation state is a table of rows held by where the row key is the group-by MultiKey. + /// + public class ResultSetProcessorAggregateGrouped + : ResultSetProcessor + , AggregationRowRemovedCallback + { + protected internal readonly ResultSetProcessorAggregateGroupedFactory Prototype; + private readonly SelectExprProcessor _selectExprProcessor; + private readonly OrderByProcessor _orderByProcessor; + + protected internal AggregationService _aggregationService; + + protected readonly EventBean[] _eventsPerStreamOneStream = new EventBean[1]; + + // For output limiting, keep a representative of each group-by group + private readonly ResultSetProcessorGroupedOutputAllGroupReps _outputAllGroupReps; + private readonly IDictionary _workCollection = new Dictionary(); + private readonly IDictionary _workCollectionTwo = new Dictionary(); + + private readonly ResultSetProcessorAggregateGroupedOutputLastHelper _outputLastHelper; + private readonly ResultSetProcessorAggregateGroupedOutputAllHelper _outputAllHelper; + private readonly ResultSetProcessorGroupedOutputFirstHelper _outputFirstHelper; + + public ResultSetProcessorAggregateGrouped( + ResultSetProcessorAggregateGroupedFactory prototype, + SelectExprProcessor selectExprProcessor, + OrderByProcessor orderByProcessor, + AggregationService aggregationService, + AgentInstanceContext agentInstanceContext) + { + Prototype = prototype; + _selectExprProcessor = selectExprProcessor; + _orderByProcessor = orderByProcessor; + _aggregationService = aggregationService; + AgentInstanceContext = agentInstanceContext; + + aggregationService.SetRemovedCallback(this); + + if (prototype.IsOutputLast && prototype.IsEnableOutputLimitOpt) + { + _outputLastHelper = prototype.ResultSetProcessorHelperFactory.MakeRSAggregateGroupedOutputLastOpt(agentInstanceContext, this, prototype); + } + else if (prototype.IsOutputAll) + { + if (!prototype.IsEnableOutputLimitOpt) + { + _outputAllGroupReps = prototype.ResultSetProcessorHelperFactory.MakeRSGroupedOutputAllNoOpt(agentInstanceContext, prototype.GroupKeyNodes, prototype.NumStreams); + } + else + { + _outputAllHelper = prototype.ResultSetProcessorHelperFactory.MakeRSAggregateGroupedOutputAll(agentInstanceContext, this, prototype); + } + } + else if (prototype.IsOutputFirst) + { + _outputFirstHelper = prototype.ResultSetProcessorHelperFactory.MakeRSGroupedOutputFirst(agentInstanceContext, prototype.GroupKeyNodes, prototype.OptionalOutputFirstConditionFactory, null, -1); + } + } + + public AgentInstanceContext AgentInstanceContext { get; set; } + + public EventType ResultEventType + { + get { return Prototype.ResultEventType; } + } + + public EventBean[] EventsPerStreamOneStream + { + get { return _eventsPerStreamOneStream; } + } + + public AggregationService AggregationService + { + get { return _aggregationService; } + } + + public void ApplyViewResult(EventBean[] newData, EventBean[] oldData) + { + var eventsPerStream = new EventBean[1]; + if (newData != null) + { + // apply new data to aggregates + foreach (var aNewData in newData) + { + eventsPerStream[0] = aNewData; + var mk = GenerateGroupKey(eventsPerStream, true); + _aggregationService.ApplyEnter(eventsPerStream, mk, AgentInstanceContext); + } + } + if (oldData != null) + { + // apply old data to aggregates + foreach (var anOldData in oldData) + { + eventsPerStream[0] = anOldData; + var mk = GenerateGroupKey(eventsPerStream, false); + _aggregationService.ApplyLeave(eventsPerStream, mk, AgentInstanceContext); + } + } + } + + public void ApplyJoinResult(ISet> newEvents, ISet> oldEvents) + { + if (!newEvents.IsEmpty()) + { + // apply old data to aggregates + foreach (var eventsPerStream in newEvents) + { + var mk = GenerateGroupKey(eventsPerStream.Array, true); + _aggregationService.ApplyEnter(eventsPerStream.Array, mk, AgentInstanceContext); + } + } + if (oldEvents != null && !oldEvents.IsEmpty()) + { + // apply old data to aggregates + foreach (var eventsPerStream in oldEvents) + { + var mk = GenerateGroupKey(eventsPerStream.Array, false); + _aggregationService.ApplyLeave(eventsPerStream.Array, mk, AgentInstanceContext); + } + } + } + + public UniformPair ProcessJoinResult(ISet> newEvents, ISet> oldEvents, bool isSynthesize) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QResultSetProcessGroupedRowPerEvent(); } + // Generate group-by keys for all events + var newDataGroupByKeys = GenerateGroupKeys(newEvents, true); + var oldDataGroupByKeys = GenerateGroupKeys(oldEvents, false); + + // generate old events + if (Prototype.IsUnidirectional) + { + Clear(); + } + + // update aggregates + if (!newEvents.IsEmpty()) + { + // apply old data to aggregates + var count = 0; + foreach (var eventsPerStream in newEvents) + { + _aggregationService.ApplyEnter(eventsPerStream.Array, newDataGroupByKeys[count], AgentInstanceContext); + count++; + } + } + if (!oldEvents.IsEmpty()) + { + // apply old data to aggregates + var count = 0; + foreach (var eventsPerStream in oldEvents) + { + _aggregationService.ApplyLeave(eventsPerStream.Array, oldDataGroupByKeys[count], AgentInstanceContext); + count++; + } + } + + EventBean[] selectOldEvents = null; + if (Prototype.IsSelectRStream) + { + selectOldEvents = GenerateOutputEventsJoin(oldEvents, oldDataGroupByKeys, false, isSynthesize); + } + var selectNewEvents = GenerateOutputEventsJoin(newEvents, newDataGroupByKeys, true, isSynthesize); + + if ((selectNewEvents != null) || (selectOldEvents != null)) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AResultSetProcessGroupedRowPerEvent(selectNewEvents, selectOldEvents); } + return new UniformPair(selectNewEvents, selectOldEvents); + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AResultSetProcessGroupedRowPerEvent(null, null); } + return null; + } + + public UniformPair ProcessViewResult(EventBean[] newData, EventBean[] oldData, bool isSynthesize) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QResultSetProcessGroupedRowPerEvent(); } + + // Generate group-by keys for all events + var newDataGroupByKeys = GenerateGroupKeys(newData, true); + var oldDataGroupByKeys = GenerateGroupKeys(oldData, false); + + // update aggregates + var eventsPerStream = new EventBean[1]; + if (newData != null) + { + // apply new data to aggregates + for (var i = 0; i < newData.Length; i++) + { + eventsPerStream[0] = newData[i]; + _aggregationService.ApplyEnter(eventsPerStream, newDataGroupByKeys[i], AgentInstanceContext); + } + } + if (oldData != null) + { + // apply old data to aggregates + for (var i = 0; i < oldData.Length; i++) + { + eventsPerStream[0] = oldData[i]; + _aggregationService.ApplyLeave(eventsPerStream, oldDataGroupByKeys[i], AgentInstanceContext); + } + } + + EventBean[] selectOldEvents = null; + if (Prototype.IsSelectRStream) + { + selectOldEvents = GenerateOutputEventsView(oldData, oldDataGroupByKeys, false, isSynthesize); + } + var selectNewEvents = GenerateOutputEventsView(newData, newDataGroupByKeys, true, isSynthesize); + + if ((selectNewEvents != null) || (selectOldEvents != null)) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AResultSetProcessGroupedRowPerEvent(selectNewEvents, selectOldEvents); } + return new UniformPair(selectNewEvents, selectOldEvents); + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AResultSetProcessGroupedRowPerEvent(null, null); } + return null; + } + + private EventBean[] GenerateOutputEventsView(EventBean[] outputEvents, object[] groupByKeys, bool isNewData, bool isSynthesize) + { + if (outputEvents == null) + { + return null; + } + + var eventsPerStream = new EventBean[1]; + var events = new EventBean[outputEvents.Length]; + var keys = new object[outputEvents.Length]; + EventBean[][] currentGenerators = null; + if (Prototype.IsSorting) + { + currentGenerators = new EventBean[outputEvents.Length][]; + } + + var evaluateParams = new EvaluateParams(eventsPerStream, isNewData, AgentInstanceContext); + var countOutputRows = 0; + for (var countInputRows = 0; countInputRows < outputEvents.Length; countInputRows++) + { + _aggregationService.SetCurrentAccess(groupByKeys[countInputRows], AgentInstanceContext.AgentInstanceId, null); + eventsPerStream[0] = outputEvents[countInputRows]; + + // Filter the having clause + if (Prototype.OptionalHavingNode != null) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QHavingClauseNonJoin(outputEvents[countInputRows]); } + var result = Prototype.OptionalHavingNode.Evaluate(evaluateParams); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AHavingClauseNonJoin(result.AsBoxedBoolean()); } + if ((result == null) || (false.Equals(result))) + { + continue; + } + } + + events[countOutputRows] = _selectExprProcessor.Process(eventsPerStream, isNewData, isSynthesize, AgentInstanceContext); + keys[countOutputRows] = groupByKeys[countInputRows]; + if (Prototype.IsSorting) + { + var currentEventsPerStream = new EventBean[] { outputEvents[countInputRows] }; + currentGenerators[countOutputRows] = currentEventsPerStream; + } + + countOutputRows++; + } + + // Resize if some rows were filtered out + if (countOutputRows != events.Length) + { + if (countOutputRows == 0) + { + return null; + } + var outEvents = new EventBean[countOutputRows]; + Array.Copy(events, 0, outEvents, 0, countOutputRows); + events = outEvents; + + if (Prototype.IsSorting) + { + var outKeys = new object[countOutputRows]; + Array.Copy(keys, 0, outKeys, 0, countOutputRows); + keys = outKeys; + + var outGens = new EventBean[countOutputRows][]; + Array.Copy(currentGenerators, 0, outGens, 0, countOutputRows); + currentGenerators = outGens; + } + } + + if (Prototype.IsSorting) + { + events = _orderByProcessor.Sort(events, currentGenerators, keys, isNewData, AgentInstanceContext); + } + + return events; + } + + public void AcceptHelperVisitor(ResultSetProcessorOutputHelperVisitor visitor) + { + if (_outputAllGroupReps != null) + { + visitor.Visit(_outputAllGroupReps); + } + if (_outputLastHelper != null) + { + visitor.Visit(_outputLastHelper); + } + if (_outputAllHelper != null) + { + visitor.Visit(_outputAllHelper); + } + if (_outputFirstHelper != null) + { + visitor.Visit(_outputFirstHelper); + } + } + + public object[] GenerateGroupKeys(ISet> resultSet, bool isNewData) + { + if (resultSet.IsEmpty()) + { + return null; + } + + var keys = new object[resultSet.Count]; + + var count = 0; + foreach (var eventsPerStream in resultSet) + { + keys[count] = GenerateGroupKey(eventsPerStream.Array, isNewData); + count++; + } + + return keys; + } + + public object[] GenerateGroupKeys(EventBean[] events, bool isNewData) + { + if (events == null) + { + return null; + } + + var eventsPerStream = new EventBean[1]; + var keys = new object[events.Length]; + + for (var i = 0; i < events.Length; i++) + { + eventsPerStream[0] = events[i]; + keys[i] = GenerateGroupKey(eventsPerStream, isNewData); + } + + return keys; + } + + /// + /// Generates the group-by key for the row + /// + /// is the row of events + /// is true for new data + /// grouping keys + protected internal object GenerateGroupKey(EventBean[] eventsPerStream, bool isNewData) + { + var evaluateParams = new EvaluateParams(eventsPerStream, isNewData, AgentInstanceContext); + + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().QResultSetProcessComputeGroupKeys(isNewData, Prototype.GroupKeyNodeExpressions, eventsPerStream); + + object keyObject; + if (Prototype.GroupKeyNode != null) + { + keyObject = Prototype.GroupKeyNode.Evaluate(evaluateParams); + } + else + { + var keysX = new object[Prototype.GroupKeyNodes.Length]; + var countX = 0; + foreach (var exprNode in Prototype.GroupKeyNodes) + { + keysX[countX] = exprNode.Evaluate(evaluateParams); + countX++; + } + keyObject = new MultiKeyUntyped(keysX); + } + InstrumentationHelper.Get().AResultSetProcessComputeGroupKeys(isNewData, keyObject); + return keyObject; + } + + if (Prototype.GroupKeyNode != null) + { + return Prototype.GroupKeyNode.Evaluate(evaluateParams); + } + + var keys = new object[Prototype.GroupKeyNodes.Length]; + var count = 0; + foreach (var exprNode in Prototype.GroupKeyNodes) + { + keys[count] = exprNode.Evaluate(evaluateParams); + count++; + } + return new MultiKeyUntyped(keys); + } + + private EventBean[] GenerateOutputEventsJoin(ISet> resultSet, object[] groupByKeys, bool isNewData, bool isSynthesize) + { + if (resultSet.IsEmpty()) + { + return null; + } + + var events = new EventBean[resultSet.Count]; + var keys = new object[resultSet.Count]; + EventBean[][] currentGenerators = null; + if (Prototype.IsSorting) + { + currentGenerators = new EventBean[resultSet.Count][]; + } + + var countOutputRows = 0; + var countInputRows = -1; + foreach (var row in resultSet) + { + countInputRows++; + var eventsPerStream = row.Array; + var evaluateParams = new EvaluateParams(eventsPerStream, isNewData, AgentInstanceContext); + + _aggregationService.SetCurrentAccess(groupByKeys[countInputRows], AgentInstanceContext.AgentInstanceId, null); + + // Filter the having clause + if (Prototype.OptionalHavingNode != null) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QHavingClauseJoin(eventsPerStream); } + var result = Prototype.OptionalHavingNode.Evaluate(evaluateParams); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AHavingClauseJoin(result.AsBoolean()); } + if ((result == null) || (false.Equals(result))) + { + continue; + } + } + + events[countOutputRows] = _selectExprProcessor.Process(eventsPerStream, isNewData, isSynthesize, AgentInstanceContext); + keys[countOutputRows] = groupByKeys[countInputRows]; + if (Prototype.IsSorting) + { + currentGenerators[countOutputRows] = eventsPerStream; + } + + countOutputRows++; + } + + // Resize if some rows were filtered out + if (countOutputRows != events.Length) + { + if (countOutputRows == 0) + { + return null; + } + var outEvents = new EventBean[countOutputRows]; + Array.Copy(events, 0, outEvents, 0, countOutputRows); + events = outEvents; + + if (Prototype.IsSorting) + { + var outKeys = new object[countOutputRows]; + Array.Copy(keys, 0, outKeys, 0, countOutputRows); + keys = outKeys; + + var outGens = new EventBean[countOutputRows][]; + Array.Copy(currentGenerators, 0, outGens, 0, countOutputRows); + currentGenerators = outGens; + } + } + + if (Prototype.IsSorting) + { + events = _orderByProcessor.Sort(events, currentGenerators, keys, isNewData, AgentInstanceContext); + } + return events; + } + + public static readonly IList EMPTY_EVENT_BEAN_LIST = new EventBean[0]; + + public IEnumerator GetEnumerator(Viewable parent) + { + if (!Prototype.IsHistoricalOnly) + { + return ObtainEnumerator(parent); + } + + _aggregationService.ClearResults(AgentInstanceContext); + var it = parent.GetEnumerator(); + var eventsPerStream = new EventBean[1]; + while (it.MoveNext()) + { + eventsPerStream[0] = it.Current; + var groupKey = GenerateGroupKey(eventsPerStream, true); + _aggregationService.ApplyEnter(eventsPerStream, groupKey, AgentInstanceContext); + } + + var deque = ResultSetProcessorUtil.EnumeratorToDeque(ObtainEnumerator(parent)); + _aggregationService.ClearResults(AgentInstanceContext); + return deque.GetEnumerator(); + } + + private IEnumerator ObtainEnumerator(Viewable parent) + { + if (_orderByProcessor == null) + { + return new ResultSetAggregateGroupedIterator(parent.GetEnumerator(), this, _aggregationService, AgentInstanceContext); + } + + // Pull all parent events, generate order keys + var eventsPerStream = new EventBean[1]; + IList outgoingEvents = new List(); + IList orderKeys = new List(); + + var evaluateParams = new EvaluateParams(eventsPerStream, true, AgentInstanceContext); + + foreach (var candidate in parent) + { + eventsPerStream[0] = candidate; + + var groupKey = GenerateGroupKey(eventsPerStream, true); + _aggregationService.SetCurrentAccess(groupKey, AgentInstanceContext.AgentInstanceId, null); + + if (Prototype.OptionalHavingNode != null) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QHavingClauseNonJoin(candidate); } + var pass = Prototype.OptionalHavingNode.Evaluate(evaluateParams); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AHavingClauseNonJoin(pass.AsBoolean()); } + if ((pass == null) || (false.Equals(pass))) + { + continue; + } + } + + outgoingEvents.Add(_selectExprProcessor.Process(eventsPerStream, true, true, AgentInstanceContext)); + + var orderKey = _orderByProcessor.GetSortKey(eventsPerStream, true, AgentInstanceContext); + orderKeys.Add(orderKey); + } + + // sort + var outgoingEventsArr = outgoingEvents.ToArray(); + var orderKeysArr = orderKeys.ToArray(); + var orderedEvents = _orderByProcessor.Sort(outgoingEventsArr, orderKeysArr, AgentInstanceContext) ?? EMPTY_EVENT_BEAN_LIST; + + return orderedEvents.GetEnumerator(); + } + + /// + /// Returns the select expression processor + /// + /// select processor. + public SelectExprProcessor SelectExprProcessor + { + get { return _selectExprProcessor; } + } + + /// + /// Returns the having node. + /// + /// having expression + public ExprEvaluator OptionalHavingNode + { + get { return Prototype.OptionalHavingNode; } + } + + public IEnumerator GetEnumerator(ISet> joinSet) + { + // Generate group-by keys for all events + var groupByKeys = GenerateGroupKeys(joinSet, true); + var result = GenerateOutputEventsJoin(joinSet, groupByKeys, true, true) ?? EMPTY_EVENT_BEAN_LIST; + return result.GetEnumerator(); + } + + public void Clear() + { + _aggregationService.ClearResults(AgentInstanceContext); + } + + public UniformPair ProcessOutputLimitedJoin(IList>>> joinEventsSet, bool generateSynthetic, OutputLimitLimitType outputLimitLimitType) + { + if (outputLimitLimitType == OutputLimitLimitType.DEFAULT) + { + return ProcessOutputLimitedJoinDefault(joinEventsSet, generateSynthetic); + } + else if (outputLimitLimitType == OutputLimitLimitType.ALL) + { + return ProcessOutputLimitedJoinAll(joinEventsSet, generateSynthetic); + } + else if (outputLimitLimitType == OutputLimitLimitType.FIRST) + { + return ProcessOutputLimitedJoinFirst(joinEventsSet, generateSynthetic); + } + else if (outputLimitLimitType == OutputLimitLimitType.LAST) + { + return ProcessOutputLimitedJoinLast(joinEventsSet, generateSynthetic); + } + else + { + throw new IllegalStateException("Unrecognized output limit " + outputLimitLimitType); + } + } + + public UniformPair ProcessOutputLimitedView(IList> viewEventsList, bool generateSynthetic, OutputLimitLimitType outputLimitLimitType) + { + if (outputLimitLimitType == OutputLimitLimitType.DEFAULT) + { + return ProcessOutputLimitedViewDefault(viewEventsList, generateSynthetic); + } + else if (outputLimitLimitType == OutputLimitLimitType.ALL) + { + return ProcessOutputLimitedViewAll(viewEventsList, generateSynthetic); + } + else if (outputLimitLimitType == OutputLimitLimitType.FIRST) + { + return ProcessOutputLimitedViewFirst(viewEventsList, generateSynthetic); + } + else if (outputLimitLimitType == OutputLimitLimitType.LAST) + { + return ProcessOutputLimitedViewLast(viewEventsList, generateSynthetic); + } + else + { + throw new IllegalStateException("Unrecognized output limited type " + outputLimitLimitType); + } + } + + public void Stop() + { + if (_outputAllGroupReps != null) + { + _outputAllGroupReps.Destroy(); + } + if (_outputAllHelper != null) + { + _outputAllHelper.Destroy(); + } + if (_outputLastHelper != null) + { + _outputLastHelper.Destroy(); + } + if (_outputFirstHelper != null) + { + _outputFirstHelper.Destroy(); + } + } + + public void GenerateOutputBatchedJoinUnkeyed(ISet> outputEvents, object[] groupByKeys, bool isNewData, bool isSynthesize, ICollection resultEvents, IList optSortKeys) + { + if (outputEvents == null) + { + return; + } + + EventBean[] eventsPerStream; + + var count = 0; + foreach (var row in outputEvents) + { + _aggregationService.SetCurrentAccess(groupByKeys[count], AgentInstanceContext.AgentInstanceId, null); + eventsPerStream = row.Array; + + var evaluateParams = new EvaluateParams(eventsPerStream, isNewData, AgentInstanceContext); + // Filter the having clause + if (Prototype.OptionalHavingNode != null) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QHavingClauseJoin(eventsPerStream); } + var result = Prototype.OptionalHavingNode.Evaluate(evaluateParams); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AHavingClauseJoin(result.AsBoolean()); } + if ((result == null) || (false.Equals(result))) + { + continue; + } + } + + resultEvents.Add(_selectExprProcessor.Process(eventsPerStream, isNewData, isSynthesize, AgentInstanceContext)); + if (Prototype.IsSorting) + { + optSortKeys.Add(_orderByProcessor.GetSortKey(eventsPerStream, isNewData, AgentInstanceContext)); + } + + count++; + } + } + + public EventBean GenerateOutputBatchedSingle(object groupByKey, EventBean[] eventsPerStream, bool isNewData, bool isSynthesize) + { + _aggregationService.SetCurrentAccess(groupByKey, AgentInstanceContext.AgentInstanceId, null); + + // Filter the having clause + if (Prototype.OptionalHavingNode != null) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QHavingClauseJoin(eventsPerStream); } + var evaluateParams = new EvaluateParams(eventsPerStream, isNewData, AgentInstanceContext); + var result = Prototype.OptionalHavingNode.Evaluate(evaluateParams); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AHavingClauseJoin(result.AsBoolean()); } + if ((result == null) || (false.Equals(result))) + { + return null; + } + } + + return _selectExprProcessor.Process(eventsPerStream, isNewData, isSynthesize, AgentInstanceContext); + } + + public void GenerateOutputBatchedViewPerKey(EventBean[] outputEvents, object[] groupByKeys, bool isNewData, bool isSynthesize, IDictionary resultEvents, IDictionary optSortKeys) + { + if (outputEvents == null) + { + return; + } + + var eventsPerStream = new EventBean[1]; + + var count = 0; + for (var i = 0; i < outputEvents.Length; i++) + { + var groupKey = groupByKeys[count]; + _aggregationService.SetCurrentAccess(groupKey, AgentInstanceContext.AgentInstanceId, null); + eventsPerStream[0] = outputEvents[count]; + + // Filter the having clause + if (Prototype.OptionalHavingNode != null) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QHavingClauseNonJoin(outputEvents[count]); } + var evaluateParams = new EvaluateParams(eventsPerStream, isNewData, AgentInstanceContext); + var result = Prototype.OptionalHavingNode.Evaluate(evaluateParams); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AHavingClauseNonJoin(result.AsBoolean()); } + if ((result == null) || (false.Equals(result))) + { + continue; + } + } + + resultEvents.Put(groupKey, _selectExprProcessor.Process(eventsPerStream, isNewData, isSynthesize, AgentInstanceContext)); + if (Prototype.IsSorting) + { + optSortKeys.Put(groupKey, _orderByProcessor.GetSortKey(eventsPerStream, isNewData, AgentInstanceContext)); + } + + count++; + } + } + + public void GenerateOutputBatchedJoinPerKey(ISet> outputEvents, object[] groupByKeys, bool isNewData, bool isSynthesize, IDictionary resultEvents, IDictionary optSortKeys) + { + if (outputEvents == null) + { + return; + } + + var count = 0; + foreach (var row in outputEvents) + { + var groupKey = groupByKeys[count]; + _aggregationService.SetCurrentAccess(groupKey, AgentInstanceContext.AgentInstanceId, null); + + // Filter the having clause + if (Prototype.OptionalHavingNode != null) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QHavingClauseJoin(row.Array); } + var evaluateParams = new EvaluateParams(row.Array, isNewData, AgentInstanceContext); + var result = Prototype.OptionalHavingNode.Evaluate(evaluateParams); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AHavingClauseJoin(result.AsBoolean()); } + if ((result == null) || (false.Equals(result))) + { + continue; + } + } + + resultEvents.Put(groupKey, _selectExprProcessor.Process(row.Array, isNewData, isSynthesize, AgentInstanceContext)); + if (Prototype.IsSorting) + { + optSortKeys.Put(groupKey, _orderByProcessor.GetSortKey(row.Array, isNewData, AgentInstanceContext)); + } + + count++; + } + } + + public bool HasAggregation + { + get { return true; } + } + + public void Removed(object key) + { + if (_outputAllGroupReps != null) + { + _outputAllGroupReps.Remove(key); + } + if (_outputAllHelper != null) + { + _outputAllHelper.Remove(key); + } + if (_outputLastHelper != null) + { + _outputLastHelper.Remove(key); + } + if (_outputFirstHelper != null) + { + _outputFirstHelper.Remove(key); + } + } + + public void ProcessOutputLimitedLastAllNonBufferedView(EventBean[] newData, EventBean[] oldData, bool isGenerateSynthetic, bool isAll) + { + if (isAll) + { + _outputAllHelper.ProcessView(newData, oldData, isGenerateSynthetic); + } + else + { + _outputLastHelper.ProcessView(newData, oldData, isGenerateSynthetic); + } + } + + public void ProcessOutputLimitedLastAllNonBufferedJoin(ISet> newData, ISet> oldData, bool isGenerateSynthetic, bool isAll) + { + if (isAll) + { + _outputAllHelper.ProcessJoin(newData, oldData, isGenerateSynthetic); + } + else + { + _outputLastHelper.ProcessJoin(newData, oldData, isGenerateSynthetic); + } + } + + public UniformPair ContinueOutputLimitedLastAllNonBufferedView(bool isSynthesize, bool isAll) + { + if (isAll) + { + return _outputAllHelper.OutputView(isSynthesize); + } + return _outputLastHelper.OutputView(isSynthesize); + } + + public UniformPair ContinueOutputLimitedLastAllNonBufferedJoin(bool isSynthesize, bool isAll) + { + if (isAll) + { + return _outputAllHelper.OutputJoin(isSynthesize); + } + return _outputLastHelper.OutputJoin(isSynthesize); + } + + private UniformPair ProcessOutputLimitedJoinLast(IList>>> joinEventsSet, bool generateSynthetic) + { + IDictionary lastPerGroupNew = new Dictionary(); + IDictionary lastPerGroupOld = null; + if (Prototype.IsSelectRStream) + { + lastPerGroupOld = new Dictionary(); + } + + IDictionary newEventsSortKey = null; // group key to sort key + IDictionary oldEventsSortKey = null; + if (_orderByProcessor != null) + { + newEventsSortKey = new Dictionary(); + if (Prototype.IsSelectRStream) + { + oldEventsSortKey = new Dictionary(); + } + } + + foreach (var pair in joinEventsSet) + { + var newData = pair.First; + var oldData = pair.Second; + + var newDataMultiKey = GenerateGroupKeys(newData, true); + var oldDataMultiKey = GenerateGroupKeys(oldData, false); + + if (Prototype.IsUnidirectional) + { + Clear(); + } + + if (newData != null) + { + // apply new data to aggregates + var count = 0; + foreach (var aNewData in newData) + { + var mk = newDataMultiKey[count]; + _aggregationService.ApplyEnter(aNewData.Array, mk, AgentInstanceContext); + count++; + } + } + if (oldData != null) + { + // apply old data to aggregates + var count = 0; + foreach (var anOldData in oldData) + { + _aggregationService.ApplyLeave(anOldData.Array, oldDataMultiKey[count], AgentInstanceContext); + count++; + } + } + + if (Prototype.IsSelectRStream) + { + GenerateOutputBatchedJoinPerKey(oldData, oldDataMultiKey, false, generateSynthetic, lastPerGroupOld, oldEventsSortKey); + } + GenerateOutputBatchedJoinPerKey(newData, newDataMultiKey, false, generateSynthetic, lastPerGroupNew, newEventsSortKey); + } + + var newEventsArr = (lastPerGroupNew.IsEmpty()) ? null : lastPerGroupNew.Values.ToArray(); + EventBean[] oldEventsArr = null; + if (Prototype.IsSelectRStream) + { + oldEventsArr = (lastPerGroupOld.IsEmpty()) ? null : lastPerGroupOld.Values.ToArray(); + } + + if (_orderByProcessor != null) + { + var sortKeysNew = (newEventsSortKey.IsEmpty()) ? null : newEventsSortKey.Values.ToArray(); + newEventsArr = _orderByProcessor.Sort(newEventsArr, sortKeysNew, AgentInstanceContext); + if (Prototype.IsSelectRStream) + { + var sortKeysOld = (oldEventsSortKey.IsEmpty()) ? null : oldEventsSortKey.Values.ToArray(); + oldEventsArr = _orderByProcessor.Sort(oldEventsArr, sortKeysOld, AgentInstanceContext); + } + } + + if ((newEventsArr == null) && (oldEventsArr == null)) + { + return null; + } + return new UniformPair(newEventsArr, oldEventsArr); + } + + private UniformPair ProcessOutputLimitedJoinFirst(IList>>> joinEventsSet, bool generateSynthetic) + { + IList resultNewEvents = new List(); + IList resultNewSortKeys = null; + IList resultOldEvents = null; + IList resultOldSortKeys = null; + + if (_orderByProcessor != null) + { + resultNewSortKeys = new List(); + } + if (Prototype.IsSelectRStream) + { + resultOldEvents = new List(); + resultOldSortKeys = new List(); + } + + _workCollection.Clear(); + + if (Prototype.OptionalHavingNode == null) + { + foreach (var pair in joinEventsSet) + { + var newData = pair.First; + var oldData = pair.Second; + + var newDataMultiKey = GenerateGroupKeys(newData, true); + var oldDataMultiKey = GenerateGroupKeys(oldData, false); + + if (newData != null) + { + // apply new data to aggregates + var count = 0; + foreach (var aNewData in newData) + { + var mk = newDataMultiKey[count]; + var outputStateGroup = _outputFirstHelper.GetOrAllocate(mk, AgentInstanceContext, Prototype.OptionalOutputFirstConditionFactory); + var pass = outputStateGroup.UpdateOutputCondition(1, 0); + if (pass) + { + _workCollection.Put(mk, aNewData.Array); + } + _aggregationService.ApplyEnter(aNewData.Array, mk, AgentInstanceContext); + count++; + } + } + + if (oldData != null) + { + // apply new data to aggregates + var count = 0; + foreach (var aOldData in oldData) + { + var mk = oldDataMultiKey[count]; + var outputStateGroup = _outputFirstHelper.GetOrAllocate(mk, AgentInstanceContext, Prototype.OptionalOutputFirstConditionFactory); + var pass = outputStateGroup.UpdateOutputCondition(0, 1); + if (pass) + { + _workCollection.Put(mk, aOldData.Array); + } + _aggregationService.ApplyLeave(aOldData.Array, mk, AgentInstanceContext); + count++; + } + } + + // there is no remove stream currently for output first + GenerateOutputBatchedArr(_workCollection, false, generateSynthetic, resultNewEvents, resultNewSortKeys); + } + } + else + {// there is a having-clause, apply after aggregations + foreach (var pair in joinEventsSet) + { + var newData = pair.First; + var oldData = pair.Second; + + var newDataMultiKey = GenerateGroupKeys(newData, true); + var oldDataMultiKey = GenerateGroupKeys(oldData, false); + + if (newData != null) + { + // apply new data to aggregates + var count = 0; + foreach (var aNewData in newData) + { + var mk = newDataMultiKey[count]; + _aggregationService.ApplyEnter(aNewData.Array, mk, AgentInstanceContext); + count++; + } + } + + if (oldData != null) + { + var count = 0; + foreach (var aOldData in oldData) + { + var mk = oldDataMultiKey[count]; + _aggregationService.ApplyLeave(aOldData.Array, mk, AgentInstanceContext); + count++; + } + } + + if (newData != null) + { + // check having clause and first-condition + var count = 0; + foreach (var aNewData in newData) + { + var mk = newDataMultiKey[count]; + _aggregationService.SetCurrentAccess(mk, AgentInstanceContext.AgentInstanceId, null); + + // Filter the having clause + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QHavingClauseJoin(aNewData.Array); } + var evaluateParams = new EvaluateParams(aNewData.Array, true, AgentInstanceContext); + var result = Prototype.OptionalHavingNode.Evaluate(evaluateParams); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AHavingClauseJoin(result.AsBoolean()); } + if ((result == null) || (false.Equals(result))) + { + count++; + continue; + } + + var outputStateGroup = _outputFirstHelper.GetOrAllocate(mk, AgentInstanceContext, Prototype.OptionalOutputFirstConditionFactory); + var pass = outputStateGroup.UpdateOutputCondition(1, 0); + if (pass) + { + _workCollection.Put(mk, aNewData.Array); + } + count++; + } + } + + if (oldData != null) + { + // apply new data to aggregates + var count = 0; + foreach (var aOldData in oldData) + { + var mk = oldDataMultiKey[count]; + _aggregationService.SetCurrentAccess(mk, AgentInstanceContext.AgentInstanceId, null); + + // Filter the having clause + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QHavingClauseJoin(aOldData.Array); } + var evaluateParams = new EvaluateParams(aOldData.Array, true, AgentInstanceContext); + var result = Prototype.OptionalHavingNode.Evaluate(evaluateParams); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AHavingClauseJoin(result.AsBoolean()); } + if ((result == null) || (false.Equals(result))) + { + count++; + continue; + } + + var outputStateGroup = _outputFirstHelper.GetOrAllocate(mk, AgentInstanceContext, Prototype.OptionalOutputFirstConditionFactory); + var pass = outputStateGroup.UpdateOutputCondition(0, 1); + if (pass) + { + _workCollection.Put(mk, aOldData.Array); + } + } + } + + // there is no remove stream currently for output first + GenerateOutputBatchedArr(_workCollection, false, generateSynthetic, resultNewEvents, resultNewSortKeys); + } + } + + EventBean[] newEventsArr = null; + EventBean[] oldEventsArr = null; + if (!resultNewEvents.IsEmpty()) + { + newEventsArr = resultNewEvents.ToArray(); + } + if ((resultOldEvents != null) && (!resultOldEvents.IsEmpty())) + { + oldEventsArr = resultOldEvents.ToArray(); + } + + if (_orderByProcessor != null) + { + var sortKeysNew = (resultNewSortKeys.IsEmpty()) ? null : resultNewSortKeys.ToArray(); + newEventsArr = _orderByProcessor.Sort(newEventsArr, sortKeysNew, AgentInstanceContext); + if (Prototype.IsSelectRStream) + { + var sortKeysOld = (resultOldSortKeys.IsEmpty()) ? null : resultOldSortKeys.ToArray(); + oldEventsArr = _orderByProcessor.Sort(oldEventsArr, sortKeysOld, AgentInstanceContext); + } + } + + if ((newEventsArr == null) && (oldEventsArr == null)) + { + return null; + } + return new UniformPair(newEventsArr, oldEventsArr); + } + + private UniformPair ProcessOutputLimitedJoinAll(IList>>> joinEventsSet, bool generateSynthetic) + { + IList newEvents = new List(); + IList oldEvents = null; + if (Prototype.IsSelectRStream) + { + oldEvents = new List(); + } + + IList newEventsSortKey = null; + IList oldEventsSortKey = null; + if (_orderByProcessor != null) + { + newEventsSortKey = new List(); + if (Prototype.IsSelectRStream) + { + oldEventsSortKey = new List(); + } + } + + _workCollection.Clear(); + + foreach (var pair in joinEventsSet) + { + var newData = pair.First; + var oldData = pair.Second; + + var newDataMultiKey = GenerateGroupKeys(newData, true); + var oldDataMultiKey = GenerateGroupKeys(oldData, false); + + if (Prototype.IsUnidirectional) + { + Clear(); + } + + if (newData != null) + { + // apply new data to aggregates + var count = 0; + foreach (var aNewData in newData) + { + var mk = newDataMultiKey[count]; + _aggregationService.ApplyEnter(aNewData.Array, mk, AgentInstanceContext); + count++; + + // keep the new event as a representative for the group + _workCollection.Put(mk, aNewData.Array); + _outputAllGroupReps.Put(mk, aNewData.Array); + } + } + if (oldData != null) + { + // apply old data to aggregates + var count = 0; + foreach (var anOldData in oldData) + { + _aggregationService.ApplyLeave(anOldData.Array, oldDataMultiKey[count], AgentInstanceContext); + count++; + } + } + + if (Prototype.IsSelectRStream) + { + GenerateOutputBatchedJoinUnkeyed(oldData, oldDataMultiKey, false, generateSynthetic, oldEvents, oldEventsSortKey); + } + GenerateOutputBatchedJoinUnkeyed(newData, newDataMultiKey, true, generateSynthetic, newEvents, newEventsSortKey); + } + + // For any group representatives not in the work collection, generate a row + var entryIterator = _outputAllGroupReps.EntryIterator(); + while (entryIterator.MoveNext()) + { + var entry = entryIterator.Current; + if (!_workCollection.ContainsKey(entry.Key)) + { + _workCollectionTwo.Put(entry.Key, entry.Value); + GenerateOutputBatchedArr(_workCollectionTwo, true, generateSynthetic, newEvents, newEventsSortKey); + _workCollectionTwo.Clear(); + } + } + + var newEventsArr = (newEvents.IsEmpty()) ? null : newEvents.ToArray(); + EventBean[] oldEventsArr = null; + if (Prototype.IsSelectRStream) + { + oldEventsArr = (oldEvents.IsEmpty()) ? null : oldEvents.ToArray(); + } + + if (_orderByProcessor != null) + { + var sortKeysNew = (newEventsSortKey.IsEmpty()) ? null : newEventsSortKey.ToArray(); + newEventsArr = _orderByProcessor.Sort(newEventsArr, sortKeysNew, AgentInstanceContext); + if (Prototype.IsSelectRStream) + { + var sortKeysOld = (oldEventsSortKey.IsEmpty()) ? null : oldEventsSortKey.ToArray(); + oldEventsArr = _orderByProcessor.Sort(oldEventsArr, sortKeysOld, AgentInstanceContext); + } + } + + if ((newEventsArr == null) && (oldEventsArr == null)) + { + return null; + } + return new UniformPair(newEventsArr, oldEventsArr); + } + + private UniformPair ProcessOutputLimitedJoinDefault(IList>>> joinEventsSet, bool generateSynthetic) + { + IList newEvents = new List(); + IList oldEvents = null; + if (Prototype.IsSelectRStream) + { + oldEvents = new List(); + } + + IList newEventsSortKey = null; + IList oldEventsSortKey = null; + if (_orderByProcessor != null) + { + newEventsSortKey = new List(); + if (Prototype.IsSelectRStream) + { + oldEventsSortKey = new List(); + } + } + + foreach (var pair in joinEventsSet) + { + var newData = pair.First; + var oldData = pair.Second; + + var newDataMultiKey = GenerateGroupKeys(newData, true); + var oldDataMultiKey = GenerateGroupKeys(oldData, false); + + if (Prototype.IsUnidirectional) + { + Clear(); + } + + if (newData != null) + { + // apply new data to aggregates + var count = 0; + foreach (var aNewData in newData) + { + _aggregationService.ApplyEnter(aNewData.Array, newDataMultiKey[count], AgentInstanceContext); + count++; + } + } + if (oldData != null) + { + // apply old data to aggregates + var count = 0; + foreach (var anOldData in oldData) + { + _aggregationService.ApplyLeave(anOldData.Array, oldDataMultiKey[count], AgentInstanceContext); + count++; + } + } + + if (Prototype.IsSelectRStream) + { + GenerateOutputBatchedJoinUnkeyed(oldData, oldDataMultiKey, false, generateSynthetic, oldEvents, oldEventsSortKey); + } + GenerateOutputBatchedJoinUnkeyed(newData, newDataMultiKey, true, generateSynthetic, newEvents, newEventsSortKey); + } + + var newEventsArr = (newEvents.IsEmpty()) ? null : newEvents.ToArray(); + EventBean[] oldEventsArr = null; + if (Prototype.IsSelectRStream) + { + oldEventsArr = (oldEvents.IsEmpty()) ? null : oldEvents.ToArray(); + } + + if (_orderByProcessor != null) + { + var sortKeysNew = (newEventsSortKey.IsEmpty()) ? null : newEventsSortKey.ToArray(); + newEventsArr = _orderByProcessor.Sort(newEventsArr, sortKeysNew, AgentInstanceContext); + if (Prototype.IsSelectRStream) + { + var sortKeysOld = (oldEventsSortKey.IsEmpty()) ? null : oldEventsSortKey.ToArray(); + oldEventsArr = _orderByProcessor.Sort(oldEventsArr, sortKeysOld, AgentInstanceContext); + } + } + + if ((newEventsArr == null) && (oldEventsArr == null)) + { + return null; + } + return new UniformPair(newEventsArr, oldEventsArr); + } + + private UniformPair ProcessOutputLimitedViewLast(IList> viewEventsList, bool generateSynthetic) + { + IDictionary lastPerGroupNew = new Dictionary(); + IDictionary lastPerGroupOld = null; + if (Prototype.IsSelectRStream) + { + lastPerGroupOld = new Dictionary(); + } + + IDictionary newEventsSortKey = null; // group key to sort key + IDictionary oldEventsSortKey = null; + if (_orderByProcessor != null) + { + newEventsSortKey = new Dictionary(); + if (Prototype.IsSelectRStream) + { + oldEventsSortKey = new Dictionary(); + } + } + + foreach (var pair in viewEventsList) + { + var newData = pair.First; + var oldData = pair.Second; + + var newDataMultiKey = GenerateGroupKeys(newData, true); + var oldDataMultiKey = GenerateGroupKeys(oldData, false); + + if (newData != null) + { + // apply new data to aggregates + var count = 0; + foreach (var aNewData in newData) + { + var mk = newDataMultiKey[count]; + _eventsPerStreamOneStream[0] = aNewData; + _aggregationService.ApplyEnter(_eventsPerStreamOneStream, mk, AgentInstanceContext); + count++; + } + } + if (oldData != null) + { + // apply old data to aggregates + var count = 0; + foreach (var anOldData in oldData) + { + _eventsPerStreamOneStream[0] = anOldData; + _aggregationService.ApplyLeave(_eventsPerStreamOneStream, oldDataMultiKey[count], AgentInstanceContext); + count++; + } + } + + if (Prototype.IsSelectRStream) + { + GenerateOutputBatchedViewPerKey(oldData, oldDataMultiKey, false, generateSynthetic, lastPerGroupOld, oldEventsSortKey); + } + GenerateOutputBatchedViewPerKey(newData, newDataMultiKey, false, generateSynthetic, lastPerGroupNew, newEventsSortKey); + } + + var newEventsArr = (lastPerGroupNew.IsEmpty()) ? null : lastPerGroupNew.Values.ToArray(); + EventBean[] oldEventsArr = null; + if (Prototype.IsSelectRStream) + { + oldEventsArr = (lastPerGroupOld.IsEmpty()) ? null : lastPerGroupOld.Values.ToArray(); + } + + if (_orderByProcessor != null) + { + var sortKeysNew = (newEventsSortKey.IsEmpty()) ? null : newEventsSortKey.Values.ToArray(); + newEventsArr = _orderByProcessor.Sort(newEventsArr, sortKeysNew, AgentInstanceContext); + if (Prototype.IsSelectRStream) + { + var sortKeysOld = (oldEventsSortKey.IsEmpty()) ? null : oldEventsSortKey.Values.ToArray(); + oldEventsArr = _orderByProcessor.Sort(oldEventsArr, sortKeysOld, AgentInstanceContext); + } + } + + if ((newEventsArr == null) && (oldEventsArr == null)) + { + return null; + } + return new UniformPair(newEventsArr, oldEventsArr); + } + + private UniformPair ProcessOutputLimitedViewFirst(IList> viewEventsList, bool generateSynthetic) + { + IList resultNewEvents = new List(); + IList resultNewSortKeys = null; + IList resultOldEvents = null; + IList resultOldSortKeys = null; + + if (_orderByProcessor != null) + { + resultNewSortKeys = new List(); + } + if (Prototype.IsSelectRStream) + { + resultOldEvents = new List(); + resultOldSortKeys = new List(); + } + + _workCollection.Clear(); + if (Prototype.OptionalHavingNode == null) + { + foreach (var pair in viewEventsList) + { + var newData = pair.First; + var oldData = pair.Second; + + var newDataMultiKey = GenerateGroupKeys(newData, true); + var oldDataMultiKey = GenerateGroupKeys(oldData, false); + + if (newData != null) + { + // apply new data to aggregates + for (var i = 0; i < newData.Length; i++) + { + _eventsPerStreamOneStream[0] = newData[i]; + var mk = newDataMultiKey[i]; + var outputStateGroup = _outputFirstHelper.GetOrAllocate(mk, AgentInstanceContext, Prototype.OptionalOutputFirstConditionFactory); + var pass = outputStateGroup.UpdateOutputCondition(1, 0); + if (pass) + { + _workCollection.Put(mk, new EventBean[] { newData[i] }); + } + _aggregationService.ApplyEnter(_eventsPerStreamOneStream, mk, AgentInstanceContext); + } + } + + if (oldData != null) + { + // apply new data to aggregates + for (var i = 0; i < oldData.Length; i++) + { + _eventsPerStreamOneStream[0] = oldData[i]; + var mk = oldDataMultiKey[i]; + var outputStateGroup = _outputFirstHelper.GetOrAllocate(mk, AgentInstanceContext, Prototype.OptionalOutputFirstConditionFactory); + var pass = outputStateGroup.UpdateOutputCondition(0, 1); + if (pass) + { + _workCollection.Put(mk, new EventBean[] { oldData[i] }); + } + _aggregationService.ApplyLeave(_eventsPerStreamOneStream, mk, AgentInstanceContext); + } + } + + // there is no remove stream currently for output first + GenerateOutputBatchedArr(_workCollection, false, generateSynthetic, resultNewEvents, resultNewSortKeys); + } + } + else + { // has a having-clause + foreach (var pair in viewEventsList) + { + var newData = pair.First; + var oldData = pair.Second; + + var newDataMultiKey = GenerateGroupKeys(newData, true); + var oldDataMultiKey = GenerateGroupKeys(oldData, false); + + if (newData != null) + { + // apply new data to aggregates + for (var i = 0; i < newData.Length; i++) + { + _eventsPerStreamOneStream[0] = newData[i]; + var mk = newDataMultiKey[i]; + _aggregationService.ApplyEnter(_eventsPerStreamOneStream, mk, AgentInstanceContext); + } + } + + if (oldData != null) + { + for (var i = 0; i < oldData.Length; i++) + { + _eventsPerStreamOneStream[0] = oldData[i]; + var mk = oldDataMultiKey[i]; + _aggregationService.ApplyLeave(_eventsPerStreamOneStream, mk, AgentInstanceContext); + } + } + + if (newData != null) + { + var evaluateParams = new EvaluateParams(_eventsPerStreamOneStream, true, AgentInstanceContext); + + // check having clause and first-condition + for (var i = 0; i < newData.Length; i++) + { + _eventsPerStreamOneStream[0] = newData[i]; + var mk = newDataMultiKey[i]; + _aggregationService.SetCurrentAccess(mk, AgentInstanceContext.AgentInstanceId, null); + + // Filter the having clause + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QHavingClauseNonJoin(newData[i]); } + var result = Prototype.OptionalHavingNode.Evaluate(evaluateParams); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AHavingClauseNonJoin(result.AsBoolean()); } + if ((result == null) || (false.Equals(result))) + { + continue; + } + + var outputStateGroup = _outputFirstHelper.GetOrAllocate(mk, AgentInstanceContext, Prototype.OptionalOutputFirstConditionFactory); + var pass = outputStateGroup.UpdateOutputCondition(1, 0); + if (pass) + { + _workCollection.Put(mk, new EventBean[] { newData[i] }); + } + } + } + + if (oldData != null) + { + var evaluateParams = new EvaluateParams(_eventsPerStreamOneStream, true, AgentInstanceContext); + + // apply new data to aggregates + for (var i = 0; i < oldData.Length; i++) + { + _eventsPerStreamOneStream[0] = oldData[i]; + var mk = oldDataMultiKey[i]; + _aggregationService.SetCurrentAccess(mk, AgentInstanceContext.AgentInstanceId, null); + + // Filter the having clause + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QHavingClauseNonJoin(oldData[i]); } + var result = Prototype.OptionalHavingNode.Evaluate(evaluateParams); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AHavingClauseNonJoin(result.AsBoolean()); } + if ((result == null) || (false.Equals(result))) + { + continue; + } + + var outputStateGroup = _outputFirstHelper.GetOrAllocate(mk, AgentInstanceContext, Prototype.OptionalOutputFirstConditionFactory); + var pass = outputStateGroup.UpdateOutputCondition(0, 1); + if (pass) + { + _workCollection.Put(mk, new EventBean[] { oldData[i] }); + } + } + } + + // there is no remove stream currently for output first + GenerateOutputBatchedArr(_workCollection, false, generateSynthetic, resultNewEvents, resultNewSortKeys); + } + } + + EventBean[] newEventsArr = null; + EventBean[] oldEventsArr = null; + if (!resultNewEvents.IsEmpty()) + { + newEventsArr = resultNewEvents.ToArray(); + } + if ((resultOldEvents != null) && (!resultOldEvents.IsEmpty())) + { + oldEventsArr = resultOldEvents.ToArray(); + } + + if (_orderByProcessor != null) + { + var sortKeysNew = (resultNewSortKeys.IsEmpty()) ? null : resultNewSortKeys.ToArray(); + newEventsArr = _orderByProcessor.Sort(newEventsArr, sortKeysNew, AgentInstanceContext); + if (Prototype.IsSelectRStream) + { + var sortKeysOld = (resultOldSortKeys.IsEmpty()) ? null : resultOldSortKeys.ToArray(); + oldEventsArr = _orderByProcessor.Sort(oldEventsArr, sortKeysOld, AgentInstanceContext); + } + } + + if ((newEventsArr == null) && (oldEventsArr == null)) + { + return null; + } + return new UniformPair(newEventsArr, oldEventsArr); + } + + private UniformPair ProcessOutputLimitedViewAll(IList> viewEventsList, bool generateSynthetic) + { + IList newEvents = new List(); + IList oldEvents = null; + if (Prototype.IsSelectRStream) + { + oldEvents = new List(); + } + + IList newEventsSortKey = null; + IList oldEventsSortKey = null; + if (_orderByProcessor != null) + { + newEventsSortKey = new List(); + if (Prototype.IsSelectRStream) + { + oldEventsSortKey = new List(); + } + } + + _workCollection.Clear(); + + foreach (var pair in viewEventsList) + { + var newData = pair.First; + var oldData = pair.Second; + + var newDataMultiKey = GenerateGroupKeys(newData, true); + var oldDataMultiKey = GenerateGroupKeys(oldData, false); + + var eventsPerStream = new EventBean[1]; + if (newData != null) + { + // apply new data to aggregates + var count = 0; + foreach (var aNewData in newData) + { + var mk = newDataMultiKey[count]; + eventsPerStream[0] = aNewData; + _aggregationService.ApplyEnter(eventsPerStream, mk, AgentInstanceContext); + count++; + + // keep the new event as a representative for the group + _workCollection.Put(mk, eventsPerStream); + _outputAllGroupReps.Put(mk, new EventBean[] { aNewData }); + } + } + if (oldData != null) + { + // apply old data to aggregates + var count = 0; + foreach (var anOldData in oldData) + { + eventsPerStream[0] = anOldData; + _aggregationService.ApplyLeave(eventsPerStream, oldDataMultiKey[count], AgentInstanceContext); + count++; + } + } + + if (Prototype.IsSelectRStream) + { + GenerateOutputBatchedViewUnkeyed(oldData, oldDataMultiKey, false, generateSynthetic, oldEvents, oldEventsSortKey); + } + GenerateOutputBatchedViewUnkeyed(newData, newDataMultiKey, true, generateSynthetic, newEvents, newEventsSortKey); + } + + // For any group representatives not in the work collection, generate a row + var entryIterator = _outputAllGroupReps.EntryIterator(); + while (entryIterator.MoveNext()) + { + var entry = entryIterator.Current; + if (!_workCollection.ContainsKey(entry.Key)) + { + _workCollectionTwo.Put(entry.Key, entry.Value); + GenerateOutputBatchedArr(_workCollectionTwo, true, generateSynthetic, newEvents, newEventsSortKey); + _workCollectionTwo.Clear(); + } + } + + var newEventsArr = (newEvents.IsEmpty()) ? null : newEvents.ToArray(); + EventBean[] oldEventsArr = null; + if (Prototype.IsSelectRStream) + { + oldEventsArr = (oldEvents.IsEmpty()) ? null : oldEvents.ToArray(); + } + + if (_orderByProcessor != null) + { + var sortKeysNew = (newEventsSortKey.IsEmpty()) ? null : newEventsSortKey.ToArray(); + newEventsArr = _orderByProcessor.Sort(newEventsArr, sortKeysNew, AgentInstanceContext); + if (Prototype.IsSelectRStream) + { + var sortKeysOld = (oldEventsSortKey.IsEmpty()) ? null : oldEventsSortKey.ToArray(); + oldEventsArr = _orderByProcessor.Sort(oldEventsArr, sortKeysOld, AgentInstanceContext); + } + } + + if ((newEventsArr == null) && (oldEventsArr == null)) + { + return null; + } + return new UniformPair(newEventsArr, oldEventsArr); + } + + private UniformPair ProcessOutputLimitedViewDefault(IList> viewEventsList, bool generateSynthetic) + { + IList newEvents = new List(); + IList oldEvents = null; + if (Prototype.IsSelectRStream) + { + oldEvents = new List(); + } + + IList newEventsSortKey = null; + IList oldEventsSortKey = null; + if (_orderByProcessor != null) + { + newEventsSortKey = new List(); + if (Prototype.IsSelectRStream) + { + oldEventsSortKey = new List(); + } + } + + foreach (var pair in viewEventsList) + { + var newData = pair.First; + var oldData = pair.Second; + + var newDataMultiKey = GenerateGroupKeys(newData, true); + var oldDataMultiKey = GenerateGroupKeys(oldData, false); + + if (newData != null) + { + // apply new data to aggregates + var count = 0; + foreach (var aNewData in newData) + { + _eventsPerStreamOneStream[0] = aNewData; + _aggregationService.ApplyEnter(_eventsPerStreamOneStream, newDataMultiKey[count], AgentInstanceContext); + count++; + } + } + if (oldData != null) + { + // apply old data to aggregates + var count = 0; + foreach (var anOldData in oldData) + { + _eventsPerStreamOneStream[0] = anOldData; + _aggregationService.ApplyLeave(_eventsPerStreamOneStream, oldDataMultiKey[count], AgentInstanceContext); + count++; + } + } + + if (Prototype.IsSelectRStream) + { + GenerateOutputBatchedViewUnkeyed(oldData, oldDataMultiKey, false, generateSynthetic, oldEvents, oldEventsSortKey); + } + GenerateOutputBatchedViewUnkeyed(newData, newDataMultiKey, true, generateSynthetic, newEvents, newEventsSortKey); + } + + var newEventsArr = (newEvents.IsEmpty()) ? null : newEvents.ToArray(); + EventBean[] oldEventsArr = null; + if (Prototype.IsSelectRStream) + { + oldEventsArr = (oldEvents.IsEmpty()) ? null : oldEvents.ToArray(); + } + + if (_orderByProcessor != null) + { + var sortKeysNew = (newEventsSortKey.IsEmpty()) ? null : newEventsSortKey.ToArray(); + newEventsArr = _orderByProcessor.Sort(newEventsArr, sortKeysNew, AgentInstanceContext); + if (Prototype.IsSelectRStream) + { + var sortKeysOld = (oldEventsSortKey.IsEmpty()) ? null : oldEventsSortKey.ToArray(); + oldEventsArr = _orderByProcessor.Sort(oldEventsArr, sortKeysOld, AgentInstanceContext); + } + } + + if ((newEventsArr == null) && (oldEventsArr == null)) + { + return null; + } + return new UniformPair(newEventsArr, oldEventsArr); + } + + private void GenerateOutputBatchedArr(IDictionary keysAndEvents, bool isNewData, bool isSynthesize, IList resultEvents, IList optSortKeys) + { + foreach (var entry in keysAndEvents) + { + var eventsPerStream = entry.Value; + + // Set the current row of aggregation states + _aggregationService.SetCurrentAccess(entry.Key, AgentInstanceContext.AgentInstanceId, null); + + // Filter the having clause + if (Prototype.OptionalHavingNode != null) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QHavingClauseJoin(entry.Value); } + var evaluateParams = new EvaluateParams(eventsPerStream, isNewData, AgentInstanceContext); + var result = Prototype.OptionalHavingNode.Evaluate(evaluateParams); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AHavingClauseJoin(result.AsBoolean()); } + if ((result == null) || (false.Equals(result))) + { + continue; + } + } + + resultEvents.Add(_selectExprProcessor.Process(eventsPerStream, isNewData, isSynthesize, AgentInstanceContext)); + + if (Prototype.IsSorting) + { + optSortKeys.Add(_orderByProcessor.GetSortKey(eventsPerStream, isNewData, AgentInstanceContext)); + } + } + } + + public void GenerateOutputBatchedViewUnkeyed(EventBean[] outputEvents, object[] groupByKeys, bool isNewData, bool isSynthesize, ICollection resultEvents, IList optSortKeys) + { + if (outputEvents == null) + { + return; + } + + var eventsPerStream = new EventBean[1]; + + var evaluateParams = new EvaluateParams(eventsPerStream, isNewData, AgentInstanceContext); + + var count = 0; + for (var i = 0; i < outputEvents.Length; i++) + { + _aggregationService.SetCurrentAccess(groupByKeys[count], AgentInstanceContext.AgentInstanceId, null); + eventsPerStream[0] = outputEvents[count]; + + // Filter the having clause + if (Prototype.OptionalHavingNode != null) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QHavingClauseNonJoin(outputEvents[count]); } + var result = Prototype.OptionalHavingNode.Evaluate(evaluateParams); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AHavingClauseNonJoin(result.AsBoolean()); } + if ((result == null) || (false.Equals(result))) + { + continue; + } + } + + resultEvents.Add(_selectExprProcessor.Process(eventsPerStream, isNewData, isSynthesize, AgentInstanceContext)); + if (Prototype.IsSorting) + { + optSortKeys.Add(_orderByProcessor.GetSortKey(eventsPerStream, isNewData, AgentInstanceContext)); + } + + count++; + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorAggregateGroupedFactory.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorAggregateGroupedFactory.cs new file mode 100755 index 000000000..5e80b0af2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorAggregateGroupedFactory.cs @@ -0,0 +1,136 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.view; + +namespace com.espertech.esper.epl.core +{ + /// + /// Result-set processor _prototype for the aggregate-grouped case: there is a group-by and + /// one or more non-aggregation event properties in the select clause are not listed in the + /// group by, and there are aggregation functions. + /// + public class ResultSetProcessorAggregateGroupedFactory : ResultSetProcessorFactory + { + private readonly SelectExprProcessor _selectExprProcessor; + + /// + /// Ctor. + /// + /// for processing the select expression and generting the readonly output rows + /// The group key node expressions. + /// list of group-by expression nodes needed for building the group-by keys + /// expression node representing validated HAVING clause, or null if none given.Aggregation functions in the having node must have been pointed to the AggregationService for evaluation. + /// true if remove stream events should be generated + /// true if unidirectional join + /// The output limit spec. + /// if set to true [is sorting]. + /// if set to true [is historical only]. + /// The result set processor helper factory. + /// The optional output first condition factory. + /// if set to true [enable output limit opt]. + /// The number streams. + public ResultSetProcessorAggregateGroupedFactory( + SelectExprProcessor selectExprProcessor, + ExprNode[] groupKeyNodeExpressions, + ExprEvaluator[] groupKeyNodes, + ExprEvaluator optionalHavingNode, + bool isSelectRStream, + bool isUnidirectional, + OutputLimitSpec outputLimitSpec, + bool isSorting, + bool isHistoricalOnly, + ResultSetProcessorHelperFactory resultSetProcessorHelperFactory, + OutputConditionPolledFactory optionalOutputFirstConditionFactory, + bool enableOutputLimitOpt, + int numStreams) + { + _selectExprProcessor = selectExprProcessor; + GroupKeyNodeExpressions = groupKeyNodeExpressions; + GroupKeyNode = groupKeyNodes.Length == 1 ? groupKeyNodes[0] : null; + GroupKeyNodes = groupKeyNodes; + OptionalHavingNode = optionalHavingNode; + IsSorting = isSorting; + IsSelectRStream = isSelectRStream; + IsUnidirectional = isUnidirectional; + IsHistoricalOnly = isHistoricalOnly; + OutputLimitSpec = outputLimitSpec; + ResultSetProcessorHelperFactory = resultSetProcessorHelperFactory; + OptionalOutputFirstConditionFactory = optionalOutputFirstConditionFactory; + IsEnableOutputLimitOpt = enableOutputLimitOpt; + NumStreams = numStreams; + } + + public ResultSetProcessor Instantiate(OrderByProcessor orderByProcessor, AggregationService aggregationService, AgentInstanceContext agentInstanceContext) + { + return new ResultSetProcessorAggregateGrouped(this, _selectExprProcessor, orderByProcessor, aggregationService, agentInstanceContext); + } + + public EventType ResultEventType + { + get { return _selectExprProcessor.ResultEventType; } + } + + public bool HasAggregation + { + get { return true; } + } + + public ExprEvaluator[] GroupKeyNodes { get; private set; } + + public ExprEvaluator OptionalHavingNode { get; private set; } + + public bool IsSorting { get; private set; } + + public bool IsSelectRStream { get; private set; } + + public bool IsUnidirectional { get; private set; } + + public bool IsHistoricalOnly { get; private set; } + + public OutputLimitSpec OutputLimitSpec { get; private set; } + + public ExprEvaluator GroupKeyNode { get; private set; } + + public ExprNode[] GroupKeyNodeExpressions { get; private set; } + + public bool IsOutputLast + { + get { return OutputLimitSpec != null && OutputLimitSpec.DisplayLimit == OutputLimitLimitType.LAST; } + } + + public bool IsOutputAll + { + get { return OutputLimitSpec != null && OutputLimitSpec.DisplayLimit == OutputLimitLimitType.ALL; } + } + + public ResultSetProcessorType ResultSetProcessorType + { + get { return ResultSetProcessorType.AGGREGATED_GROUPED; } + } + + public OutputConditionPolledFactory OptionalOutputFirstConditionFactory { get; private set; } + + public bool IsEnableOutputLimitOpt { get; private set; } + + public int NumStreams { get; private set; } + + public bool IsOutputFirst + { + get { return OutputLimitSpec != null && OutputLimitSpec.DisplayLimit == OutputLimitLimitType.FIRST; } + } + + public ResultSetProcessorHelperFactory ResultSetProcessorHelperFactory { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorAggregateGroupedOutputAllHelper.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorAggregateGroupedOutputAllHelper.cs new file mode 100755 index 000000000..9e61a1b7f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorAggregateGroupedOutputAllHelper.cs @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; + +namespace com.espertech.esper.epl.core +{ + public interface ResultSetProcessorAggregateGroupedOutputAllHelper + : ResultSetProcessorOutputHelper + { + void ProcessView(EventBean[] newData, EventBean[] oldData, bool isGenerateSynthetic); + void ProcessJoin(ISet> newData, ISet> oldData, bool isGenerateSynthetic); + UniformPair OutputView(bool isSynthesize); + UniformPair OutputJoin(bool isSynthesize); + void Remove(object key); + void Destroy(); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorAggregateGroupedOutputAllHelperImpl.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorAggregateGroupedOutputAllHelperImpl.cs new file mode 100755 index 000000000..9678029eb --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorAggregateGroupedOutputAllHelperImpl.cs @@ -0,0 +1,168 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.core +{ + public class ResultSetProcessorAggregateGroupedOutputAllHelperImpl : ResultSetProcessorAggregateGroupedOutputAllHelper + { + private readonly ResultSetProcessorAggregateGrouped _processor; + + private readonly IList _eventsOld = new List(2); + private readonly IList _eventsNew = new List(2); + private readonly IDictionary _repsPerGroup = new Dictionary(); + private readonly ISet _lastSeenKeys = new HashSet(); + + public ResultSetProcessorAggregateGroupedOutputAllHelperImpl(ResultSetProcessorAggregateGrouped processor) { + _processor = processor; + } + + public void ProcessView(EventBean[] newData, EventBean[] oldData, bool isGenerateSynthetic) { + object[] newDataMultiKey = _processor.GenerateGroupKeys(newData, true); + object[] oldDataMultiKey = _processor.GenerateGroupKeys(oldData, false); + ISet keysSeenRemoved = new HashSet(); + + if (newData != null) + { + // apply new data to aggregates + int count = 0; + foreach (EventBean aNewData in newData) + { + EventBean[] eventsPerStream = new EventBean[] {aNewData}; + object mk = newDataMultiKey[count]; + _repsPerGroup.Put(mk, eventsPerStream); + _lastSeenKeys.Add(mk); + _processor.EventsPerStreamOneStream[0] = aNewData; + _processor.AggregationService.ApplyEnter(eventsPerStream, mk, _processor.AgentInstanceContext); + count++; + } + } + if (oldData != null) + { + // apply old data to aggregates + int count = 0; + foreach (EventBean anOldData in oldData) + { + object mk = oldDataMultiKey[count]; + _lastSeenKeys.Add(mk); + keysSeenRemoved.Add(mk); + _processor.EventsPerStreamOneStream[0] = anOldData; + _processor.AggregationService.ApplyLeave(_processor.EventsPerStreamOneStream, oldDataMultiKey[count], _processor.AgentInstanceContext); + count++; + } + } + + if (_processor.Prototype.IsSelectRStream) { + _processor.GenerateOutputBatchedViewUnkeyed(oldData, oldDataMultiKey, false, isGenerateSynthetic, _eventsOld, null); + } + _processor.GenerateOutputBatchedViewUnkeyed(newData, newDataMultiKey, true, isGenerateSynthetic, _eventsNew, null); + + foreach (object keySeen in keysSeenRemoved) { + EventBean newEvent = _processor.GenerateOutputBatchedSingle(keySeen, _repsPerGroup.Get(keySeen), true, isGenerateSynthetic); + if (newEvent != null) { + _eventsNew.Add(newEvent); + } + } + } + + public void ProcessJoin(ISet> newData, ISet> oldData, bool isGenerateSynthetic) { + object[] newDataMultiKey = _processor.GenerateGroupKeys(newData, true); + object[] oldDataMultiKey = _processor.GenerateGroupKeys(oldData, false); + ISet keysSeenRemoved = new HashSet(); + + if (newData != null) { + // apply new data to aggregates + int count = 0; + foreach (MultiKey aNewData in newData) + { + object mk = newDataMultiKey[count]; + _repsPerGroup.Put(mk, aNewData.Array); + _lastSeenKeys.Add(mk); + _processor.AggregationService.ApplyEnter(aNewData.Array, mk, _processor.AgentInstanceContext); + count++; + } + } + if (oldData != null) + { + // apply old data to aggregates + int count = 0; + foreach (MultiKey anOldData in oldData) + { + object mk = oldDataMultiKey[count]; + _lastSeenKeys.Add(mk); + keysSeenRemoved.Add(mk); + _processor.AggregationService.ApplyLeave(anOldData.Array, oldDataMultiKey[count], _processor.AgentInstanceContext); + count++; + } + } + + if (_processor.Prototype.IsSelectRStream) { + _processor.GenerateOutputBatchedJoinUnkeyed(oldData, oldDataMultiKey, false, isGenerateSynthetic, _eventsOld, null); + } + _processor.GenerateOutputBatchedJoinUnkeyed(newData, newDataMultiKey, false, isGenerateSynthetic, _eventsNew, null); + + foreach (object keySeen in keysSeenRemoved) { + EventBean newEvent = _processor.GenerateOutputBatchedSingle(keySeen, _repsPerGroup.Get(keySeen), true, isGenerateSynthetic); + if (newEvent != null) { + _eventsNew.Add(newEvent); + } + } + } + + public UniformPair OutputView(bool isSynthesize) { + return Output(isSynthesize); + } + + public UniformPair OutputJoin(bool isSynthesize) { + return Output(isSynthesize); + } + + public void Remove(object key) { + _repsPerGroup.Remove(key); + } + + public void Destroy() { + // no action required + } + + private UniformPair Output(bool isSynthesize) { + // generate remaining key events + foreach (KeyValuePair entry in _repsPerGroup) { + if (_lastSeenKeys.Contains(entry.Key)) { + continue; + } + EventBean newEvent = _processor.GenerateOutputBatchedSingle(entry.Key, entry.Value, true, isSynthesize); + if (newEvent != null) { + _eventsNew.Add(newEvent); + } + } + _lastSeenKeys.Clear(); + + EventBean[] newEventsArr = _eventsNew.ToArrayOrNull(); + EventBean[] oldEventsArr = null; + if (_processor.Prototype.IsSelectRStream) + { + oldEventsArr = _eventsOld.ToArrayOrNull(); + } + _eventsNew.Clear(); + _eventsOld.Clear(); + if ((newEventsArr == null) && (oldEventsArr == null)) { + return null; + } + return new UniformPair(newEventsArr, oldEventsArr); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorAggregateGroupedOutputLastHelper.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorAggregateGroupedOutputLastHelper.cs new file mode 100755 index 000000000..d85ad2afb --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorAggregateGroupedOutputLastHelper.cs @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; + +namespace com.espertech.esper.epl.core +{ + public interface ResultSetProcessorAggregateGroupedOutputLastHelper + : ResultSetProcessorOutputHelper + { + void ProcessView(EventBean[] newData, EventBean[] oldData, bool isGenerateSynthetic); + void ProcessJoin(ISet> newData, ISet> oldData, bool isGenerateSynthetic); + UniformPair OutputView(bool isSynthesize); + UniformPair OutputJoin(bool isSynthesize); + void Destroy(); + void Remove(object key); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorAggregateGroupedOutputLastHelperImpl.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorAggregateGroupedOutputLastHelperImpl.cs new file mode 100755 index 000000000..89a5872b9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorAggregateGroupedOutputLastHelperImpl.cs @@ -0,0 +1,125 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.epl.core +{ + public class ResultSetProcessorAggregateGroupedOutputLastHelperImpl : ResultSetProcessorAggregateGroupedOutputLastHelper + { + private readonly ResultSetProcessorAggregateGrouped _processor; + + private readonly IDictionary _outputLastUnordGroupNew; + private readonly IDictionary _outputLastUnordGroupOld; + + public ResultSetProcessorAggregateGroupedOutputLastHelperImpl(ResultSetProcessorAggregateGrouped processor) { + _processor = processor; + _outputLastUnordGroupNew = new Dictionary(); + _outputLastUnordGroupOld = new Dictionary(); + } + + public void ProcessView(EventBean[] newData, EventBean[] oldData, bool isGenerateSynthetic) { + object[] newDataMultiKey = _processor.GenerateGroupKeys(newData, true); + object[] oldDataMultiKey = _processor.GenerateGroupKeys(oldData, false); + + if (newData != null) + { + // apply new data to aggregates + int count = 0; + foreach (EventBean aNewData in newData) + { + object mk = newDataMultiKey[count]; + _processor.EventsPerStreamOneStream[0] = aNewData; + _processor.AggregationService.ApplyEnter(_processor.EventsPerStreamOneStream, mk, _processor.AgentInstanceContext); + count++; + } + } + if (oldData != null) + { + // apply old data to aggregates + int count = 0; + foreach (EventBean anOldData in oldData) + { + _processor.EventsPerStreamOneStream[0] = anOldData; + _processor.AggregationService.ApplyLeave(_processor.EventsPerStreamOneStream, oldDataMultiKey[count], _processor.AgentInstanceContext); + count++; + } + } + + if (_processor.Prototype.IsSelectRStream) { + _processor.GenerateOutputBatchedViewPerKey(oldData, oldDataMultiKey, false, isGenerateSynthetic, _outputLastUnordGroupOld, null); + } + _processor.GenerateOutputBatchedViewPerKey(newData, newDataMultiKey, false, isGenerateSynthetic, _outputLastUnordGroupNew, null); + } + + public void ProcessJoin(ISet> newData, ISet> oldData, bool isGenerateSynthetic) { + object[] newDataMultiKey = _processor.GenerateGroupKeys(newData, true); + object[] oldDataMultiKey = _processor.GenerateGroupKeys(oldData, false); + + if (newData != null) { + // apply new data to aggregates + int count = 0; + foreach (MultiKey aNewData in newData) + { + object mk = newDataMultiKey[count]; + _processor.AggregationService.ApplyEnter(aNewData.Array, mk, _processor.AgentInstanceContext); + count++; + } + } + if (oldData != null) + { + // apply old data to aggregates + int count = 0; + foreach (MultiKey anOldData in oldData) + { + _processor.AggregationService.ApplyLeave(anOldData.Array, oldDataMultiKey[count], _processor.AgentInstanceContext); + count++; + } + } + + if (_processor.Prototype.IsSelectRStream) { + _processor.GenerateOutputBatchedJoinPerKey(oldData, oldDataMultiKey, false, isGenerateSynthetic, _outputLastUnordGroupOld, null); + } + _processor.GenerateOutputBatchedJoinPerKey(newData, newDataMultiKey, false, isGenerateSynthetic, _outputLastUnordGroupNew, null); + } + + public UniformPair OutputView(bool isSynthesize) { + return ContinueOutputLimitedLastNonBuffered(); + } + + public UniformPair OutputJoin(bool isSynthesize) { + return ContinueOutputLimitedLastNonBuffered(); + } + + public void Remove(object key) { + // no action required + } + + public void Destroy() { + // no action required + } + + private UniformPair ContinueOutputLimitedLastNonBuffered() { + EventBean[] newEventsArr = (_outputLastUnordGroupNew.IsEmpty()) ? null : _outputLastUnordGroupNew.Values.ToArrayOrNull(); + EventBean[] oldEventsArr = null; + if (_processor.Prototype.IsSelectRStream) { + oldEventsArr = (_outputLastUnordGroupOld.IsEmpty()) ? null : _outputLastUnordGroupOld.Values.ToArrayOrNull(); + } + if ((newEventsArr == null) && (oldEventsArr == null)) { + return null; + } + _outputLastUnordGroupNew.Clear(); + _outputLastUnordGroupOld.Clear(); + return new UniformPair(newEventsArr, oldEventsArr); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorBaseSimple.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorBaseSimple.cs new file mode 100755 index 000000000..755568d9b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorBaseSimple.cs @@ -0,0 +1,153 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.view; +using com.espertech.esper.events; +using com.espertech.esper.view; + +namespace com.espertech.esper.epl.core +{ + /// + /// Result set processor for the simplest case: no aggregation functions used in the select + /// clause, and no group-by. The processor generates one row for each event entering + /// (new event) and one row for each event leaving (old event). + /// + public abstract class ResultSetProcessorBaseSimple : ResultSetProcessor + { + /// Clear out current state. + public virtual void Clear() + { + // No need to clear state, there is no state held + } + + #region "Abstract Methods" + + /// + /// Gets a value indicating whether this instance has aggregation. + /// + /// + /// true if this instance has aggregation; otherwise, false. + /// + public abstract bool HasAggregation { get; } + + /// + /// Returns the event type of processed results. + /// + /// The type of the result event. + /// event type of the resulting events posted by the processor. + /// + public abstract EventType ResultEventType { get; } + + /// + /// For use by views posting their result, process the event rows that are entered and removed (new and old events). + /// Processes according to select-clauses, group-by clauses and having-clauses and returns new events and + /// old events as specified. + /// + /// new events posted by view + /// old events posted by view + /// set to true to indicate that synthetic events are required for an iterator result set + /// pair of new events and old events + public abstract UniformPair ProcessViewResult( + EventBean[] newData, + EventBean[] oldData, + bool isSynthesize); + + /// + /// For use by joins posting their result, process the event rows that are entered and removed (new and old events). + /// Processes according to select-clauses, group-by clauses and having-clauses and returns new events and + /// old events as specified. + /// + /// new events posted by join + /// old events posted by join + /// set to true to indicate that synthetic events are required for an iterator result set + /// pair of new events and old events + public abstract UniformPair ProcessJoinResult( + ISet> newEvents, + ISet> oldEvents, + bool isSynthesize); + + /// + /// Returns the iterator implementing the group-by and aggregation and order-by logic + /// specific to each case of use of these construct. + /// + /// is the parent view iterator + /// event iterator + public abstract IEnumerator GetEnumerator(Viewable parent); + + /// Returns the iterator for iterating over a join-result. + /// is the join result set + /// iterator over join results + public abstract IEnumerator GetEnumerator(ISet> joinSet); + + /// + /// Sets the agent instance context. + /// + /// The context. + public abstract AgentInstanceContext AgentInstanceContext { get; set; } + + public abstract void ApplyViewResult(EventBean[] newData, EventBean[] oldData); + public abstract void ApplyJoinResult(ISet> newEvents, ISet> oldEvents); + + public abstract void ProcessOutputLimitedLastAllNonBufferedView( + EventBean[] newData, + EventBean[] oldData, + bool isGenerateSynthetic, + bool isAll); + + public abstract void ProcessOutputLimitedLastAllNonBufferedJoin(ISet> newEvents, ISet> oldEvents, bool isGenerateSynthetic, bool isAll); + public abstract UniformPair ContinueOutputLimitedLastAllNonBufferedView(bool isSynthesize, bool isAll); + public abstract UniformPair ContinueOutputLimitedLastAllNonBufferedJoin(bool isSynthesize, bool isAll); + + public abstract void AcceptHelperVisitor(ResultSetProcessorOutputHelperVisitor visitor); + + #endregion + + /// Processes batched events in case of output-rate limiting. + /// the join results + /// flag to indicate whether synthetic events must be generated + /// the type of output rate limiting + /// results for dispatch + public virtual UniformPair ProcessOutputLimitedJoin(IList>>> joinEventsSet, bool generateSynthetic, OutputLimitLimitType outputLimitLimitType) + { + if (outputLimitLimitType != OutputLimitLimitType.LAST) + { + var flattened = EventBeanUtility.FlattenBatchJoin(joinEventsSet); + return ProcessJoinResult(flattened.First, flattened.Second, generateSynthetic); + } + + throw new IllegalStateException("Output last is provided by " + typeof(OutputProcessViewConditionLastAllUnord).Name); + } + + /// Processes batched events in case of output-rate limiting. + /// the view results + /// flag to indicate whether synthetic events must be generated + /// the type of output rate limiting + /// results for dispatch + public virtual UniformPair ProcessOutputLimitedView(IList> viewEventsList, + bool generateSynthetic, + OutputLimitLimitType outputLimitLimitType) + { + if (outputLimitLimitType != OutputLimitLimitType.LAST) + { + UniformPair pair = EventBeanUtility.FlattenBatchStream(viewEventsList); + return ProcessViewResult(pair.First, pair.Second, generateSynthetic); + } + + throw new IllegalStateException("Output last is provided by " + typeof(OutputProcessViewConditionLastAllUnord).Name); + } + + public abstract void Stop(); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorFactory.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorFactory.cs new file mode 100755 index 000000000..3a589db62 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorFactory.cs @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.agg.service; + +namespace com.espertech.esper.epl.core +{ + /// + /// Processor _prototype for result sets for instances that apply the select-clause, group-by-clause and having-clauses as supplied. + /// + public interface ResultSetProcessorFactory + { + /// + /// Returns the event type of processed results. + /// + /// event type of the resulting events posted by the processor. + EventType ResultEventType { get; } + + /// + /// Gets a value indicating whether this instance has aggregation. + /// + /// + /// true if this instance has aggregation; otherwise, false. + /// + bool HasAggregation { get; } + + /// + /// Instantiates the specified order by processor. + /// + /// The order by processor. + /// The aggregation service. + /// The agent instance context. + /// + ResultSetProcessor Instantiate(OrderByProcessor orderByProcessor, AggregationService aggregationService, AgentInstanceContext agentInstanceContext); + + /// + /// Gets the type of the result set processor. + /// + /// + /// The type of the result set processor. + /// + ResultSetProcessorType ResultSetProcessorType { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorFactoryDesc.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorFactoryDesc.cs new file mode 100755 index 000000000..1e74ae9ac --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorFactoryDesc.cs @@ -0,0 +1,31 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.agg.service; + +namespace com.espertech.esper.epl.core +{ + /// + /// Processor _prototype for result sets for instances that apply the select-clause, group-by-clause and having-clauses as supplied. + /// + public class ResultSetProcessorFactoryDesc + { + public ResultSetProcessorFactoryDesc(ResultSetProcessorFactory resultSetProcessorFactory, OrderByProcessorFactory orderByProcessorFactory, AggregationServiceFactoryDesc aggregationServiceFactoryDesc) + { + ResultSetProcessorFactory = resultSetProcessorFactory; + OrderByProcessorFactory = orderByProcessorFactory; + AggregationServiceFactoryDesc = aggregationServiceFactoryDesc; + } + + public ResultSetProcessorFactory ResultSetProcessorFactory { get; private set; } + + public OrderByProcessorFactory OrderByProcessorFactory { get; private set; } + + public AggregationServiceFactoryDesc AggregationServiceFactoryDesc { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorFactoryFactory.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorFactoryFactory.cs new file mode 100755 index 000000000..830875352 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorFactoryFactory.cs @@ -0,0 +1,1079 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.client.annotation; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.agg.rollup; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.annotation; +using com.espertech.esper.epl.declexpr; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.prev; +using com.espertech.esper.epl.expression.prior; +using com.espertech.esper.epl.expression.time; +using com.espertech.esper.epl.expression.visitor; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.view; +using com.espertech.esper.events; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.core +{ + /// + /// Factory for output processors. Output processors process the result set of a join or of a view + /// and apply aggregation/grouping, having and some output limiting logic. + /// + /// The instance produced by the factory depends on the presence of aggregation functions in the select list, + /// the presence and nature of the group-by clause. + /// + /// In case (1) and (2) there are no aggregation functions in the select clause. + /// + /// Case (3) is without group-by and with aggregation functions and without non-aggregated properties + /// in the select list:
    select sum(volume) 
    . + /// Always produces one row for new and old data, aggregates without grouping. + /// + /// Case (4) is without group-by and with aggregation functions but with non-aggregated properties + /// in the select list:
    select price, sum(volume) 
    . + /// Produces a row for each event, aggregates without grouping. + /// + /// Case (5) is with group-by and with aggregation functions and all selected properties are grouped-by. + /// in the select list:
    select customerId, sum(volume) group by customerId
    . + /// Produces a old and new data row for each group changed, aggregates with grouping, see + /// + /// + /// Case (6) is with group-by and with aggregation functions and only some selected properties are grouped-by. + /// in the select list:
    select customerId, supplierId, sum(volume) group by customerId
    . + /// Produces row for each event, aggregates with grouping. + ///
    + public class ResultSetProcessorFactoryFactory + { + /// + /// Returns the result set process for the given select expression, group-by clause and + /// having clause given a set of types describing each stream in the from-clause. + /// + /// a subset of the statement specification + /// engine and statement and agent-instance level services + /// for information about the streams in the from clause + /// delegates views resource factory to expression resources requirements + /// true if unidirectional join for any of the streams + /// indicator whether to allow aggregation functions in any expressions + /// The context property registry. + /// The select expr processor callback. + /// The configuration information. + /// The result set processor helper factory. + /// if set to true [is fire and forget]. + /// if set to true [is on select]. + /// + /// result set processor instance + /// + /// ExprValidationException when any of the expressions is invalid + public static ResultSetProcessorFactoryDesc GetProcessorPrototype( + StatementSpecCompiled statementSpec, + StatementContext stmtContext, + StreamTypeService typeService, + ViewResourceDelegateUnverified viewResourceDelegate, + bool[] isUnidirectionalStream, + bool allowAggregation, + ContextPropertyRegistry contextPropertyRegistry, + SelectExprProcessorDeliveryCallback selectExprProcessorCallback, + ConfigurationInformation configurationInformation, + ResultSetProcessorHelperFactory resultSetProcessorHelperFactory, + bool isFireAndForget, + bool isOnSelect) + { + var orderByListUnexpanded = statementSpec.OrderByList; + var selectClauseSpec = statementSpec.SelectClauseSpec; + var insertIntoDesc = statementSpec.InsertIntoDesc; + var optionalHavingNode = statementSpec.HavingExprRootNode; + var outputLimitSpec = statementSpec.OutputLimitSpec; + IList declaredNodes = new List(); + + // validate output limit spec + ValidateOutputLimit(outputLimitSpec, stmtContext); + + // determine unidirectional + var isUnidirectional = false; + for (var i = 0; i < isUnidirectionalStream.Length; i++) + { + isUnidirectional |= isUnidirectionalStream[i]; + } + + // determine single-stream historical + var isHistoricalOnly = false; + if (statementSpec.StreamSpecs.Length == 1) + { + var spec = statementSpec.StreamSpecs[0]; + if (spec is DBStatementStreamSpec || spec is MethodStreamSpec || spec is TableQueryStreamSpec) + { + isHistoricalOnly = true; + } + } + + // determine join or number of streams + var numStreams = typeService.EventTypes.Length; + + // Expand any instances of select-clause names in the + // order-by clause with the full expression + var orderByList = ExpandColumnNames(selectClauseSpec.SelectExprList, orderByListUnexpanded); + + // Validate selection expressions, if any (could be wildcard i.e. empty list) + var namedSelectionList = new List(); + var evaluatorContextStmt = new ExprEvaluatorContextStatement(stmtContext, false); + var allowRollup = statementSpec.GroupByExpressions != null && statementSpec.GroupByExpressions.GroupByRollupLevels != null; + var resettableAggs = isUnidirectional || statementSpec.OnTriggerDesc != null; + var intoTableName = statementSpec.IntoTableSpec == null ? null : statementSpec.IntoTableSpec.Name; + var validationContext = new ExprValidationContext( + typeService, + stmtContext.EngineImportService, + stmtContext.StatementExtensionServicesContext, + viewResourceDelegate, + stmtContext.SchedulingService, + stmtContext.VariableService, + stmtContext.TableService, + evaluatorContextStmt, + stmtContext.EventAdapterService, + stmtContext.StatementName, + stmtContext.StatementId, + stmtContext.Annotations, + stmtContext.ContextDescriptor, + stmtContext.ScriptingService, + false, allowRollup, true, resettableAggs, + intoTableName, false); + + ValidateSelectAssignColNames(selectClauseSpec, namedSelectionList, validationContext); + if (statementSpec.GroupByExpressions != null && statementSpec.GroupByExpressions.SelectClausePerLevel != null) + { + ExprNodeUtility.GetValidatedSubtree(ExprNodeOrigin.GROUPBY, statementSpec.GroupByExpressions.SelectClausePerLevel, validationContext); + } + var isUsingWildcard = selectClauseSpec.IsUsingWildcard; + + // Validate stream selections, if any (such as stream.*) + var isUsingStreamSelect = false; + foreach (var compiled in selectClauseSpec.SelectExprList) + { + if (!(compiled is SelectClauseStreamCompiledSpec)) + { + continue; + } + var streamSelectSpec = (SelectClauseStreamCompiledSpec)compiled; + var streamNum = int.MinValue; + var isFragmentEvent = false; + var isProperty = false; + Type propertyType = null; + isUsingStreamSelect = true; + for (var i = 0; i < typeService.StreamNames.Length; i++) + { + var streamName = streamSelectSpec.StreamName; + if (typeService.StreamNames[i].Equals(streamName)) + { + streamNum = i; + break; + } + + // see if the stream name is known as a nested event type + var candidateProviderOfFragments = typeService.EventTypes[i]; + // for the native event type we don't need to fragment, we simply use the property itself since all wrappers understand Java objects + if (!(candidateProviderOfFragments is NativeEventType) && (candidateProviderOfFragments.GetFragmentType(streamName) != null)) + { + streamNum = i; + isFragmentEvent = true; + break; + } + } + + // stream name not found + if (streamNum == int.MinValue) + { + // see if the stream name specified resolves as a property + PropertyResolutionDescriptor desc = null; + try + { + desc = typeService.ResolveByPropertyName(streamSelectSpec.StreamName, false); + } + catch (StreamTypesException) + { + // not handled + } + + if (desc == null) + { + throw new ExprValidationException("Stream selector '" + streamSelectSpec.StreamName + ".*' does not match any stream name in the from clause"); + } + isProperty = true; + propertyType = desc.PropertyType; + streamNum = desc.StreamNum; + } + + streamSelectSpec.StreamNumber = streamNum; + streamSelectSpec.IsFragmentEvent = isFragmentEvent; + streamSelectSpec.SetProperty(isProperty, propertyType); + + if (streamNum >= 0) + { + var tableMetadata = stmtContext.TableService.GetTableMetadataFromEventType(typeService.EventTypes[streamNum]); + streamSelectSpec.TableMetadata = tableMetadata; + } + } + + // Validate having clause, if present + if (optionalHavingNode != null) + { + optionalHavingNode = ExprNodeUtility.GetValidatedSubtree(ExprNodeOrigin.HAVING, optionalHavingNode, validationContext); + if (statementSpec.GroupByExpressions != null) + { + ExprNodeUtility.GetValidatedSubtree(ExprNodeOrigin.GROUPBY, statementSpec.GroupByExpressions.OptHavingNodePerLevel, validationContext); + } + } + + // Validate order-by expressions, if any (could be empty list for no order-by) + for (var i = 0; i < orderByList.Count; i++) + { + var orderByNode = orderByList[i].ExprNode; + + // Ensure there is no subselects + var visitor = new ExprNodeSubselectDeclaredDotVisitor(); + orderByNode.Accept(visitor); + if (visitor.Subselects.Count > 0) + { + throw new ExprValidationException("Subselects not allowed within order-by clause"); + } + + var isDescending = orderByList[i].IsDescending; + var validatedOrderBy = new OrderByItem(ExprNodeUtility.GetValidatedSubtree(ExprNodeOrigin.ORDERBY, orderByNode, validationContext), isDescending); + orderByList[i] = validatedOrderBy; + + if (statementSpec.GroupByExpressions != null && statementSpec.GroupByExpressions.OptOrderByPerLevel != null) + { + ExprNodeUtility.GetValidatedSubtree(ExprNodeOrigin.GROUPBY, statementSpec.GroupByExpressions.OptOrderByPerLevel, validationContext); + } + } + + // Get the select expression nodes + IList selectNodes = namedSelectionList.Select(element => element.SelectExpression).ToList(); + + // Get the order-by expression nodes + IList orderByNodes = orderByList.Select(element => element.ExprNode).ToList(); + + // Determine aggregate functions used in select, if any + IList selectAggregateExprNodes = new List(); + IDictionary selectAggregationNodesNamed = new Dictionary(); + var declaredNodeVisitor = new ExprNodeDeclaredVisitor(); + foreach (var element in namedSelectionList) + { + ExprAggregateNodeUtil.GetAggregatesBottomUp(element.SelectExpression, selectAggregateExprNodes); + if (element.ProvidedName != null) + { + selectAggregationNodesNamed.Put(element.SelectExpression, element.ProvidedName); + } + element.SelectExpression.Accept(declaredNodeVisitor); + declaredNodes.AddAll(declaredNodeVisitor.DeclaredExpressions); + declaredNodeVisitor.Clear(); + } + if (statementSpec.GroupByExpressions != null) + { + ExprAggregateNodeUtil.GetAggregatesBottomUp(statementSpec.GroupByExpressions.SelectClausePerLevel, selectAggregateExprNodes); + } + if (!allowAggregation && !selectAggregateExprNodes.IsEmpty()) + { + throw new ExprValidationException("Aggregation functions are not allowed in this context"); + } + + // Determine if we have a having clause with aggregation + IList havingAggregateExprNodes = new List(); + var propertiesAggregatedHaving = new ExprNodePropOrStreamSet(); + if (optionalHavingNode != null) + { + ExprAggregateNodeUtil.GetAggregatesBottomUp(optionalHavingNode, havingAggregateExprNodes); + if (statementSpec.GroupByExpressions != null) + { + ExprAggregateNodeUtil.GetAggregatesBottomUp(statementSpec.GroupByExpressions.OptHavingNodePerLevel, havingAggregateExprNodes); + } + propertiesAggregatedHaving = ExprNodeUtility.GetAggregatedProperties(havingAggregateExprNodes); + } + if (!allowAggregation && !havingAggregateExprNodes.IsEmpty()) + { + throw new ExprValidationException("Aggregation functions are not allowed in this context"); + } + + // Determine if we have a order-by clause with aggregation + IList orderByAggregateExprNodes = new List(); + if (orderByNodes != null && !orderByNodes.IsEmpty()) + { + foreach (var orderByNode in orderByNodes) + { + ExprAggregateNodeUtil.GetAggregatesBottomUp(orderByNode, orderByAggregateExprNodes); + } + if (statementSpec.GroupByExpressions != null) + { + ExprAggregateNodeUtil.GetAggregatesBottomUp(statementSpec.GroupByExpressions.OptOrderByPerLevel, orderByAggregateExprNodes); + } + if (!allowAggregation && !orderByAggregateExprNodes.IsEmpty()) + { + throw new ExprValidationException("Aggregation functions are not allowed in this context"); + } + } + + // Analyze rollup + var groupByRollupInfo = AnalyzeValidateGroupBy(statementSpec.GroupByExpressions, validationContext); + var groupByNodesValidated = groupByRollupInfo == null ? new ExprNode[0] : groupByRollupInfo.ExprNodes; + var groupByRollupDesc = groupByRollupInfo == null ? null : groupByRollupInfo.RollupDesc; + + // Construct the appropriate aggregation service + var hasGroupBy = groupByNodesValidated.Length > 0; + var aggregationServiceFactory = AggregationServiceFactoryFactory.GetService( + selectAggregateExprNodes, selectAggregationNodesNamed, declaredNodes, groupByNodesValidated, + havingAggregateExprNodes, orderByAggregateExprNodes, Collections.GetEmptyList(), + hasGroupBy, statementSpec.Annotations, stmtContext.VariableService, typeService.EventTypes.Length > 1, + false, + statementSpec.FilterRootNode, statementSpec.HavingExprRootNode, + stmtContext.AggregationServiceFactoryService, typeService.EventTypes, + groupByRollupDesc, + statementSpec.OptionalContextName, + statementSpec.IntoTableSpec, + stmtContext.TableService, isUnidirectional, + isFireAndForget, isOnSelect, + stmtContext.EngineImportService); + + // Compare local-aggregation versus group-by + var localGroupByMatchesGroupBy = AnalyzeLocalGroupBy(groupByNodesValidated, selectAggregateExprNodes, havingAggregateExprNodes, orderByAggregateExprNodes); + + var useCollatorSort = false; + if (stmtContext.ConfigSnapshot != null) + { + useCollatorSort = stmtContext.ConfigSnapshot.EngineDefaults.Language.IsSortUsingCollator; + } + + // Construct the processor for sorting output events + var orderByProcessorFactory = OrderByProcessorFactoryFactory.GetProcessor(namedSelectionList, + groupByNodesValidated, orderByList, statementSpec.RowLimitSpec, stmtContext.VariableService, useCollatorSort, statementSpec.OptionalContextName); + + // Construct the processor for evaluating the select clause + var selectExprEventTypeRegistry = new SelectExprEventTypeRegistry(stmtContext.StatementName, stmtContext.StatementEventTypeRef); + var selectExprProcessor = + SelectExprProcessorFactory.GetProcessor( + Collections.GetEmptyList(), + selectClauseSpec.SelectExprList, isUsingWildcard, insertIntoDesc, null, + statementSpec.ForClauseSpec, typeService, + stmtContext.EventAdapterService, + stmtContext.StatementResultService, + stmtContext.ValueAddEventService, + selectExprEventTypeRegistry, + stmtContext.EngineImportService, + evaluatorContextStmt, + stmtContext.VariableService, + stmtContext.ScriptingService, + stmtContext.TableService, + stmtContext.TimeProvider, + stmtContext.EngineURI, + stmtContext.StatementId, + stmtContext.StatementName, + stmtContext.Annotations, + stmtContext.ContextDescriptor, + stmtContext.ConfigSnapshot, + selectExprProcessorCallback, + stmtContext.NamedWindowMgmtService, + statementSpec.IntoTableSpec, + groupByRollupInfo, + stmtContext.StatementExtensionServicesContext); + + // Get a list of event properties being aggregated in the select clause, if any + var propertiesGroupBy = ExprNodeUtility.GetGroupByPropertiesValidateHasOne(groupByNodesValidated); + // Figure out all non-aggregated event properties in the select clause (props not under a sum/avg/max aggregation node) + var nonAggregatedPropsSelect = ExprNodeUtility.GetNonAggregatedProps(typeService.EventTypes, selectNodes, contextPropertyRegistry); + if (optionalHavingNode != null) + { + ExprNodeUtility.AddNonAggregatedProps(optionalHavingNode, nonAggregatedPropsSelect, typeService.EventTypes, contextPropertyRegistry); + } + + // Validate the having-clause (selected aggregate nodes and all in group-by are allowed) + var hasAggregation = (!selectAggregateExprNodes.IsEmpty()) || (!havingAggregateExprNodes.IsEmpty()) || (!orderByAggregateExprNodes.IsEmpty()) || (!propertiesAggregatedHaving.IsEmpty()); + if (optionalHavingNode != null && hasAggregation) + { + ValidateHaving(propertiesGroupBy, optionalHavingNode); + } + + // We only generate Remove-Stream events if they are explicitly selected, or the insert-into requires them + var isSelectRStream = (statementSpec.SelectStreamSelectorEnum == SelectClauseStreamSelectorEnum.RSTREAM_ISTREAM_BOTH + || statementSpec.SelectStreamSelectorEnum == SelectClauseStreamSelectorEnum.RSTREAM_ONLY); + if ((statementSpec.InsertIntoDesc != null) && (statementSpec.InsertIntoDesc.StreamSelector.IsSelectsRStream())) + { + isSelectRStream = true; + } + + var optionHavingEval = optionalHavingNode == null ? null : optionalHavingNode.ExprEvaluator; + var hasOutputLimitOptHint = HintEnum.ENABLE_OUTPUTLIMIT_OPT.GetHint(statementSpec.Annotations) != null; + + // Determine output-first condition factory + OutputConditionPolledFactory optionalOutputFirstConditionFactory = null; + if (outputLimitSpec != null && outputLimitSpec.DisplayLimit == OutputLimitLimitType.FIRST) + { + optionalOutputFirstConditionFactory = OutputConditionPolledFactoryFactory.CreateConditionFactory(outputLimitSpec, stmtContext); + } + + // (1) + // There is no group-by clause and no aggregate functions with event properties in the select clause and having clause (simplest case) + if ((groupByNodesValidated.Length == 0) && (selectAggregateExprNodes.IsEmpty()) && (havingAggregateExprNodes.IsEmpty())) + { + // Determine if any output rate limiting must be performed early while processing results + // Snapshot output does not count in terms of limiting output for grouping/aggregation purposes + var isOutputLimitingNoSnapshot = (outputLimitSpec != null) && (outputLimitSpec.DisplayLimit != OutputLimitLimitType.SNAPSHOT); + + // (1a) + // There is no need to perform select expression processing, the single view itself (no join) generates + // events in the desired format, therefore there is no output processor. There are no order-by expressions. + if (orderByNodes.IsEmpty() && optionalHavingNode == null && !isOutputLimitingNoSnapshot && statementSpec.RowLimitSpec == null) + { + Log.Debug(".getProcessor Using no result processor"); + var factoryX = new ResultSetProcessorHandThroughFactory(selectExprProcessor, isSelectRStream); + return new ResultSetProcessorFactoryDesc(factoryX, orderByProcessorFactory, aggregationServiceFactory); + } + + // (1b) + // We need to process the select expression in a simple fashion, with each event (old and new) + // directly generating one row, and no need to update aggregate state since there is no aggregate function. + // There might be some order-by expressions. + Log.Debug(".getProcessor Using ResultSetProcessorSimple"); + var factoryY = new ResultSetProcessorSimpleFactory(selectExprProcessor, optionHavingEval, isSelectRStream, outputLimitSpec, hasOutputLimitOptHint, resultSetProcessorHelperFactory, numStreams); + return new ResultSetProcessorFactoryDesc(factoryY, orderByProcessorFactory, aggregationServiceFactory); + } + + // (2) + // A wildcard select-clause has been specified and the group-by is ignored since no aggregation functions are used, and no having clause + var isLast = statementSpec.OutputLimitSpec != null && statementSpec.OutputLimitSpec.DisplayLimit == OutputLimitLimitType.LAST; + var isFirst = statementSpec.OutputLimitSpec != null && statementSpec.OutputLimitSpec.DisplayLimit == OutputLimitLimitType.FIRST; + if ((namedSelectionList.IsEmpty()) && (propertiesAggregatedHaving.IsEmpty()) && (havingAggregateExprNodes.IsEmpty()) && !isLast && !isFirst) + { + Log.Debug(".getProcessor Using ResultSetProcessorSimple"); + var factoryX = new ResultSetProcessorSimpleFactory(selectExprProcessor, optionHavingEval, isSelectRStream, outputLimitSpec, hasOutputLimitOptHint, resultSetProcessorHelperFactory, numStreams); + return new ResultSetProcessorFactoryDesc(factoryX, orderByProcessorFactory, aggregationServiceFactory); + } + + if ((groupByNodesValidated.Length == 0) && hasAggregation) + { + // (3) + // There is no group-by clause and there are aggregate functions with event properties in the select clause (aggregation case) + // or having class, and all event properties are aggregated (all properties are under aggregation functions). + var hasStreamSelect = ExprNodeUtility.HasStreamSelect(selectNodes); + if ((nonAggregatedPropsSelect.IsEmpty()) && !hasStreamSelect && !isUsingWildcard && !isUsingStreamSelect && localGroupByMatchesGroupBy && (viewResourceDelegate == null || viewResourceDelegate.PreviousRequests.IsEmpty())) + { + Log.Debug(".getProcessor Using ResultSetProcessorRowForAll"); + var factoryZ = new ResultSetProcessorRowForAllFactory(selectExprProcessor, optionHavingEval, isSelectRStream, isUnidirectional, isHistoricalOnly, outputLimitSpec, resultSetProcessorHelperFactory); + return new ResultSetProcessorFactoryDesc(factoryZ, orderByProcessorFactory, aggregationServiceFactory); + } + + // (4) + // There is no group-by clause but there are aggregate functions with event properties in the select clause (aggregation case) + // or having clause and not all event properties are aggregated (some properties are not under aggregation functions). + Log.Debug(".getProcessor Using ResultSetProcessorAggregateAll"); + var factoryY = new ResultSetProcessorAggregateAllFactory(selectExprProcessor, optionHavingEval, isSelectRStream, isUnidirectional, isHistoricalOnly, outputLimitSpec, hasOutputLimitOptHint, resultSetProcessorHelperFactory); + return new ResultSetProcessorFactoryDesc(factoryY, orderByProcessorFactory, aggregationServiceFactory); + } + + // Handle group-by cases + if (groupByNodesValidated.Length == 0) + { + throw new IllegalStateException("Unexpected empty group-by expression list"); + } + + // Figure out if all non-aggregated event properties in the select clause are listed in the group by + var allInGroupBy = true; + string notInGroupByReason = null; + if (isUsingStreamSelect) + { + allInGroupBy = false; + notInGroupByReason = "stream select"; + } + + var reasonMessage = propertiesGroupBy.NotContainsAll(nonAggregatedPropsSelect); + if (reasonMessage != null) + { + notInGroupByReason = reasonMessage; + allInGroupBy = false; + } + + // Wildcard select-clause means we do not have all selected properties in the group + if (isUsingWildcard) + { + allInGroupBy = false; + notInGroupByReason = "wildcard select"; + } + + // Figure out if all non-aggregated event properties in the order-by clause are listed in the select expression + var nonAggregatedPropsOrderBy = ExprNodeUtility.GetNonAggregatedProps(typeService.EventTypes, orderByNodes, contextPropertyRegistry); + + reasonMessage = nonAggregatedPropsSelect.NotContainsAll(nonAggregatedPropsOrderBy); + var allInSelect = reasonMessage == null; + + // Wildcard select-clause means that all order-by props in the select expression + if (isUsingWildcard) + { + allInSelect = true; + } + + // (4) + // There is a group-by clause, and all event properties in the select clause that are not under an aggregation + // function are listed in the group-by clause, and if there is an order-by clause, all non-aggregated properties + // referred to in the order-by clause also appear in the select (output one row per group, not one row per event) + var groupByEval = ExprNodeUtility.GetEvaluators(groupByNodesValidated); + if (allInGroupBy && allInSelect && localGroupByMatchesGroupBy) + { + var noDataWindowSingleStream = typeService.IsIStreamOnly[0] && typeService.EventTypes.Length < 2; + var iterableUnboundConfig = configurationInformation.EngineDefaults.ViewResources.IsIterableUnbound; + var iterateUnbounded = noDataWindowSingleStream && (iterableUnboundConfig || AnnotationUtil.FindAnnotation(statementSpec.Annotations, typeof(IterableUnboundAttribute)) != null); + + Log.Debug(".getProcessor Using ResultSetProcessorRowPerGroup"); + ResultSetProcessorFactory factoryX; + if (groupByRollupDesc != null) + { + var perLevelExpression = GetRollUpPerLevelExpressions(statementSpec, groupByNodesValidated, groupByRollupDesc, stmtContext, selectExprEventTypeRegistry, evaluatorContextStmt, insertIntoDesc, typeService, validationContext, groupByRollupInfo); + factoryX = new ResultSetProcessorRowPerGroupRollupFactory(perLevelExpression, groupByNodesValidated, groupByEval, isSelectRStream, isUnidirectional, outputLimitSpec, orderByProcessorFactory != null, noDataWindowSingleStream, groupByRollupDesc, typeService.EventTypes.Length > 1, isHistoricalOnly, iterateUnbounded, optionalOutputFirstConditionFactory, resultSetProcessorHelperFactory, hasOutputLimitOptHint, numStreams); + } + else + { + factoryX = new ResultSetProcessorRowPerGroupFactory(selectExprProcessor, groupByNodesValidated, groupByEval, optionHavingEval, isSelectRStream, isUnidirectional, outputLimitSpec, orderByProcessorFactory != null, noDataWindowSingleStream, isHistoricalOnly, iterateUnbounded, resultSetProcessorHelperFactory, hasOutputLimitOptHint, numStreams, optionalOutputFirstConditionFactory); + } + return new ResultSetProcessorFactoryDesc(factoryX, orderByProcessorFactory, aggregationServiceFactory); + } + + if (groupByRollupDesc != null) + { + throw new ExprValidationException("Group-by with rollup requires a fully-aggregated query, the query is not full-aggregated because of " + notInGroupByReason); + } + + // (6) + // There is a group-by clause, and one or more event properties in the select clause that are not under an aggregation + // function are not listed in the group-by clause (output one row per event, not one row per group) + Log.Debug(".getProcessor Using ResultSetProcessorAggregateGrouped"); + var factory = new ResultSetProcessorAggregateGroupedFactory(selectExprProcessor, groupByNodesValidated, groupByEval, optionHavingEval, isSelectRStream, isUnidirectional, outputLimitSpec, orderByProcessorFactory != null, isHistoricalOnly, resultSetProcessorHelperFactory, optionalOutputFirstConditionFactory, hasOutputLimitOptHint, numStreams); + return new ResultSetProcessorFactoryDesc(factory, orderByProcessorFactory, aggregationServiceFactory); + } + + private static void ValidateOutputLimit(OutputLimitSpec outputLimitSpec, StatementContext statementContext) + { + if (outputLimitSpec == null) + { + return; + } + var evaluatorContextStmt = new ExprEvaluatorContextStatement(statementContext, false); + var validationContext = new ExprValidationContext( + new StreamTypeServiceImpl(statementContext.EngineURI, false), + statementContext.EngineImportService, + statementContext.StatementExtensionServicesContext, null, + statementContext.TimeProvider, + statementContext.VariableService, + statementContext.TableService, + evaluatorContextStmt, + statementContext.EventAdapterService, + statementContext.StatementName, + statementContext.StatementId, + statementContext.Annotations, + statementContext.ContextDescriptor, + statementContext.ScriptingService, + false, false, false, false, null, false); + if (outputLimitSpec.AfterTimePeriodExpr != null) + { + var timePeriodExpr = (ExprTimePeriod)ExprNodeUtility.GetValidatedSubtree(ExprNodeOrigin.OUTPUTLIMIT, outputLimitSpec.AfterTimePeriodExpr, validationContext); + outputLimitSpec.AfterTimePeriodExpr = timePeriodExpr; + } + if (outputLimitSpec.TimePeriodExpr != null) + { + var timePeriodExpr = (ExprTimePeriod)ExprNodeUtility.GetValidatedSubtree(ExprNodeOrigin.OUTPUTLIMIT, outputLimitSpec.TimePeriodExpr, validationContext); + outputLimitSpec.TimePeriodExpr = timePeriodExpr; + if (timePeriodExpr.IsConstantResult && timePeriodExpr.EvaluateAsSeconds(null, true, new ExprEvaluatorContextStatement(statementContext, false)) <= 0) + { + throw new ExprValidationException("Invalid time period expression returns a zero or negative time interval"); + } + } + } + + private static bool AnalyzeLocalGroupBy( + ExprNode[] groupByNodesValidated, + IList selectAggregateExprNodes, + IList havingAggregateExprNodes, + IList orderByAggregateExprNodes) + { + var localGroupByMatchesGroupBy = AnalyzeLocalGroupBy(groupByNodesValidated, selectAggregateExprNodes); + localGroupByMatchesGroupBy = localGroupByMatchesGroupBy && AnalyzeLocalGroupBy(groupByNodesValidated, havingAggregateExprNodes); + localGroupByMatchesGroupBy = localGroupByMatchesGroupBy && AnalyzeLocalGroupBy(groupByNodesValidated, orderByAggregateExprNodes); + return localGroupByMatchesGroupBy; + } + + private static bool AnalyzeLocalGroupBy(ExprNode[] groupByNodesValidated, IList aggNodes) + { + foreach (var agg in aggNodes) + { + if (agg.OptionalLocalGroupBy != null) + { + if (!ExprNodeUtility.DeepEqualsIsSubset(agg.OptionalLocalGroupBy.PartitionExpressions, groupByNodesValidated)) + { + return false; + } + } + } + return true; + } + + private static GroupByRollupInfo AnalyzeValidateGroupBy(GroupByClauseExpressions groupBy, ExprValidationContext validationContext) + { + if (groupBy == null) + { + return null; + } + + // validate that group-by expressions are somewhat-plain expressions + ExprNodeUtility.ValidateNoSpecialsGroupByExpressions(groupBy.GroupByNodes); + + // validate each expression + var validated = new ExprNode[groupBy.GroupByNodes.Length]; + for (var i = 0; i < validated.Length; i++) + { + validated[i] = ExprNodeUtility.GetValidatedSubtree(ExprNodeOrigin.GROUPBY, groupBy.GroupByNodes[i], validationContext); + } + + if (groupBy.GroupByRollupLevels == null) + { + return new GroupByRollupInfo(validated, null); + } + + var rollup = AggregationGroupByRollupDesc.Make(groupBy.GroupByRollupLevels); + + // callback when hook reporting enabled + try + { + var hook = (GroupByRollupPlanHook) TypeHelper.GetAnnotationHook( + validationContext.Annotations, HookType.INTERNAL_GROUPROLLUP_PLAN, + typeof (GroupByRollupPlanHook), validationContext.EngineImportService); + if (hook != null) + { + hook.Query(new GroupByRollupPlanDesc(validated, rollup)); + } + } + catch (ExprValidationException e) + { + throw new EPException("Failed to obtain hook for " + HookType.INTERNAL_QUERY_PLAN); + } + + return new GroupByRollupInfo(validated, rollup); + } + + private static GroupByRollupPerLevelExpression GetRollUpPerLevelExpressions( + StatementSpecCompiled statementSpec, + ExprNode[] groupByNodesValidated, + AggregationGroupByRollupDesc groupByRollupDesc, + StatementContext stmtContext, + SelectExprEventTypeRegistry selectExprEventTypeRegistry, + ExprEvaluatorContextStatement evaluatorContextStmt, + InsertIntoDesc insertIntoDesc, + StreamTypeService typeService, + ExprValidationContext validationContext, + GroupByRollupInfo groupByRollupInfo) + { + var numLevels = groupByRollupDesc.Levels.Length; + var groupByExpressions = statementSpec.GroupByExpressions; + + // allocate + var processors = new SelectExprProcessor[numLevels]; + ExprEvaluator[] havingClauses = null; + if (groupByExpressions.OptHavingNodePerLevel != null) + { + havingClauses = new ExprEvaluator[numLevels]; + } + OrderByElement[][] orderByElements = null; + if (groupByExpressions.OptOrderByPerLevel != null) + { + orderByElements = new OrderByElement[numLevels][]; + } + + // for each expression in the group-by clause determine which properties it refers to + var propsPerGroupByExpr = new ExprNodePropOrStreamSet[groupByNodesValidated.Length]; + for (var i = 0; i < groupByNodesValidated.Length; i++) + { + propsPerGroupByExpr[i] = ExprNodeUtility.GetGroupByPropertiesValidateHasOne(new ExprNode[] { groupByNodesValidated[i] }); + } + + // for each level obtain a separate select expression processor + for (var i = 0; i < numLevels; i++) + { + var level = groupByRollupDesc.Levels[i]; + + // determine properties rolled up for this level + var rolledupProps = GetRollupProperties(level, propsPerGroupByExpr); + + var selectClauseLevel = groupByExpressions.SelectClausePerLevel[i]; + var selectClause = GetRollUpSelectClause(statementSpec.SelectClauseSpec, selectClauseLevel, level, rolledupProps, groupByNodesValidated, validationContext); + processors[i] = SelectExprProcessorFactory.GetProcessor( + Collections.GetEmptyList(), selectClause, false, insertIntoDesc, null, + statementSpec.ForClauseSpec, + typeService, + stmtContext.EventAdapterService, + stmtContext.StatementResultService, + stmtContext.ValueAddEventService, + selectExprEventTypeRegistry, + stmtContext.EngineImportService, + evaluatorContextStmt, + stmtContext.VariableService, + stmtContext.ScriptingService, + stmtContext.TableService, + stmtContext.TimeProvider, + stmtContext.EngineURI, + stmtContext.StatementId, + stmtContext.StatementName, + stmtContext.Annotations, + stmtContext.ContextDescriptor, + stmtContext.ConfigSnapshot, null, + stmtContext.NamedWindowMgmtService, + statementSpec.IntoTableSpec, + groupByRollupInfo, + stmtContext.StatementExtensionServicesContext); + + if (havingClauses != null) + { + havingClauses[i] = RewriteRollupValidateExpression(ExprNodeOrigin.HAVING, groupByExpressions.OptHavingNodePerLevel[i], validationContext, rolledupProps, groupByNodesValidated, level).ExprEvaluator; + } + + if (orderByElements != null) + { + orderByElements[i] = RewriteRollupOrderBy(statementSpec.OrderByList, groupByExpressions.OptOrderByPerLevel[i], validationContext, rolledupProps, groupByNodesValidated, level); + } + } + + return new GroupByRollupPerLevelExpression(processors, havingClauses, orderByElements); + } + + private static OrderByElement[] RewriteRollupOrderBy(OrderByItem[] items, ExprNode[] orderByList, ExprValidationContext validationContext, ExprNodePropOrStreamSet rolledupProps, ExprNode[] groupByNodes, AggregationGroupByRollupLevel level) + { + var elements = new OrderByElement[orderByList.Length]; + for (var i = 0; i < orderByList.Length; i++) + { + var validated = RewriteRollupValidateExpression(ExprNodeOrigin.ORDERBY, orderByList[i], validationContext, rolledupProps, groupByNodes, level); + elements[i] = new OrderByElement(validated, validated.ExprEvaluator, items[i].IsDescending); + } + return elements; + } + + private static ExprNodePropOrStreamSet GetRollupProperties(AggregationGroupByRollupLevel level, ExprNodePropOrStreamSet[] propsPerGroupByExpr) + { + // determine properties rolled up for this level + var rolledupProps = new ExprNodePropOrStreamSet(); + for (var i = 0; i < propsPerGroupByExpr.Length; i++) + { + if (level.IsAggregationTop) + { + rolledupProps.AddAll(propsPerGroupByExpr[i]); + } + else + { + var rollupContainsGroupExpr = false; + foreach (var num in level.RollupKeys) + { + if (num == i) + { + rollupContainsGroupExpr = true; + break; + } + } + if (!rollupContainsGroupExpr) + { + rolledupProps.AddAll(propsPerGroupByExpr[i]); + } + } + } + return rolledupProps; + } + + private static SelectClauseElementCompiled[] GetRollUpSelectClause( + SelectClauseSpecCompiled selectClauseSpec, + ExprNode[] selectClauseLevel, + AggregationGroupByRollupLevel level, + ExprNodePropOrStreamSet rolledupProps, + ExprNode[] groupByNodesValidated, + ExprValidationContext validationContext) + { + var rewritten = new SelectClauseElementCompiled[selectClauseSpec.SelectExprList.Length]; + for (var i = 0; i < rewritten.Length; i++) + { + var spec = selectClauseSpec.SelectExprList[i]; + if (!(spec is SelectClauseExprCompiledSpec)) + { + throw new ExprValidationException("Group-by clause with roll-up does not allow wildcard"); + } + + var exprSpec = (SelectClauseExprCompiledSpec)spec; + var validated = RewriteRollupValidateExpression(ExprNodeOrigin.SELECT, selectClauseLevel[i], validationContext, rolledupProps, groupByNodesValidated, level); + rewritten[i] = new SelectClauseExprCompiledSpec(validated, exprSpec.AssignedName, exprSpec.ProvidedName, exprSpec.IsEvents); + } + return rewritten; + } + + private static ExprNode RewriteRollupValidateExpression( + ExprNodeOrigin exprNodeOrigin, + ExprNode exprNode, + ExprValidationContext validationContext, + ExprNodePropOrStreamSet rolledupProps, + ExprNode[] groupByNodes, + AggregationGroupByRollupLevel level) + { + // rewrite grouping expressions + var groupingVisitor = new ExprNodeGroupingVisitorWParent(); + exprNode.Accept(groupingVisitor); + foreach (var groupingNodePair in groupingVisitor.GroupingNodes) + { + // obtain combination - always a single one as grouping nodes cannot have + var combination = GetGroupExprCombination(groupByNodes, groupingNodePair.Second.ChildNodes); + + var found = false; + var rollupIndexes = level.IsAggregationTop ? new int[0] : level.RollupKeys; + foreach (var index in rollupIndexes) + { + if (index == combination[0]) + { + found = true; + break; + } + } + + var result = found ? 0 : 1; + var constant = new ExprConstantNodeImpl(result, typeof(int?)); + if (groupingNodePair.First != null) + { + ExprNodeUtility.ReplaceChildNode(groupingNodePair.First, groupingNodePair.Second, constant); + } + else + { + exprNode = constant; + } + } + + // rewrite grouping id expressions + foreach (var groupingIdNodePair in groupingVisitor.GroupingIdNodes) + { + var combination = GetGroupExprCombination(groupByNodes, groupingIdNodePair.Second.ChildNodes); + + var result = 0; + for (var i = 0; i < combination.Length; i++) + { + var index = combination[i]; + + var found = false; + var rollupIndexes = level.IsAggregationTop ? new int[0] : level.RollupKeys; + foreach (var rollupIndex in rollupIndexes) + { + if (index == rollupIndex) + { + found = true; + break; + } + } + if (!found) + { + result = result + Pow2(combination.Length - i - 1); + } + } + + var constant = new ExprConstantNodeImpl(result, typeof(int?)); + if (groupingIdNodePair.First != null) + { + ExprNodeUtility.ReplaceChildNode(groupingIdNodePair.First, groupingIdNodePair.Second, constant); + } + else + { + exprNode = constant; + } + } + + // rewrite properties + var identVisitor = new ExprNodeIdentifierCollectVisitorWContainer(); + exprNode.Accept(identVisitor); + foreach (var node in identVisitor.ExprProperties) + { + var rewrite = false; + + var firstRollupNonPropExpr = rolledupProps.FirstExpression; + if (firstRollupNonPropExpr != null) + { + throw new ExprValidationException("Invalid rollup expression " + firstRollupNonPropExpr.Textual); + } + + foreach (ExprNodePropOrStreamDesc rolledupProp in rolledupProps.Properties) + { + var prop = (ExprNodePropOrStreamPropDesc)rolledupProp; + if (rolledupProp.StreamNum == node.Second.StreamId && prop.PropertyName.Equals(node.Second.ResolvedPropertyName)) + { + rewrite = true; + break; + } + } + if (node.First != null && (node.First is ExprPreviousNode || node.First is ExprPriorNode)) + { + rewrite = false; + } + if (!rewrite) + { + continue; + } + + var constant = new ExprConstantNodeImpl(null, node.Second.ExprEvaluator.ReturnType); + if (node.First != null) + { + ExprNodeUtility.ReplaceChildNode(node.First, node.Second, constant); + } + else + { + exprNode = constant; + } + } + + return ExprNodeUtility.GetValidatedSubtree(exprNodeOrigin, exprNode, validationContext); + } + + private static int[] GetGroupExprCombination(IList groupByNodes, IEnumerable childNodes) + { + ISet indexes = new SortedSet(); + foreach (var child in childNodes) + { + var found = false; + + for (var i = 0; i < groupByNodes.Count; i++) + { + if (ExprNodeUtility.DeepEquals(child, groupByNodes[i])) + { + if (indexes.Contains(i)) + { + throw new ExprValidationException("Duplicate expression '" + ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(child) + "' among grouping function parameters"); + } + indexes.Add(i); + found = true; + } + } + + if (!found) + { + throw new ExprValidationException("Failed to find expression '" + ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(child) + "' among group-by expressions"); + } + } + return CollectionUtil.IntArray(indexes); + } + + private static void ValidateSelectAssignColNames(SelectClauseSpecCompiled selectClauseSpec, IList namedSelectionList, ExprValidationContext validationContext) + { + for (var i = 0; i < selectClauseSpec.SelectExprList.Length; i++) + { + // validate element + var element = selectClauseSpec.SelectExprList[i]; + if (element is SelectClauseExprCompiledSpec) + { + var expr = (SelectClauseExprCompiledSpec)element; + var validatedExpression = ExprNodeUtility.GetValidatedSubtree(ExprNodeOrigin.SELECT, expr.SelectExpression, validationContext); + + // determine an element name if none assigned + var asName = expr.AssignedName; + if (asName == null) + { + asName = ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(validatedExpression); + } + + expr.AssignedName = asName; + expr.SelectExpression = validatedExpression; + namedSelectionList.Add(expr); + } + } + } + + private static void ValidateHaving(ExprNodePropOrStreamSet propertiesGroupedBy, + ExprNode havingNode) + { + IList aggregateNodesHaving = new List(); + ExprAggregateNodeUtil.GetAggregatesBottomUp(havingNode, aggregateNodesHaving); + + // Any non-aggregated properties must occur in the group-by clause (if there is one) + if (!propertiesGroupedBy.IsEmpty()) + { + var visitor = new ExprNodeIdentifierAndStreamRefVisitor(true); + havingNode.Accept(visitor); + var allPropertiesHaving = visitor.GetRefs(); + var aggPropertiesHaving = ExprNodeUtility.GetAggregatedProperties(aggregateNodesHaving); + + aggPropertiesHaving.RemoveFromList(allPropertiesHaving); + propertiesGroupedBy.RemoveFromList(allPropertiesHaving); + + if (!allPropertiesHaving.IsEmpty()) + { + var desc = allPropertiesHaving.FirstOrDefault(); + throw new ExprValidationException("Non-aggregated " + desc.Textual + " in the HAVING clause must occur in the group-by clause"); + } + } + } + + private static IList ExpandColumnNames(IEnumerable selectionList, OrderByItem[] orderByUnexpanded) + { + if (orderByUnexpanded.Length == 0) + { + return Collections.GetEmptyList(); + } + + // copy list to modify + IList expanded = new List(); + foreach (var item in orderByUnexpanded) + { + expanded.Add(item.Copy()); + } + + // expand + foreach (var selectElement in selectionList) + { + // process only expressions + if (!(selectElement is SelectClauseExprCompiledSpec)) + { + continue; + } + var selectExpr = (SelectClauseExprCompiledSpec)selectElement; + + var name = selectExpr.AssignedName; + if (name != null) + { + var fullExpr = selectExpr.SelectExpression; + for (var ii = 0; ii < expanded.Count; ii++) + { + var orderByElement = expanded[ii]; + var swapped = ColumnNamedNodeSwapper.Swap(orderByElement.ExprNode, name, fullExpr); + var newOrderByElement = new OrderByItem(swapped, orderByElement.IsDescending); + expanded[ii] = newOrderByElement; + } + } + } + + return expanded; + } + + private static int Pow2(int exponent) + { + if (exponent == 0) + { + return 1; + } + var result = 2; + for (var i = 0; i < exponent - 1; i++) + { + result = 2 * result; + } + return result; + } + + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorGroupedOutputAllGroupReps.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorGroupedOutputAllGroupReps.cs new file mode 100755 index 000000000..79aa9d4a0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorGroupedOutputAllGroupReps.cs @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; + +namespace com.espertech.esper.epl.core +{ + public interface ResultSetProcessorGroupedOutputAllGroupReps + : ResultSetProcessorOutputHelper + { + object Put(object mk, EventBean[] array); + void Remove(object key); + IEnumerator> EntryIterator(); + void Destroy(); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorGroupedOutputAllGroupRepsImpl.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorGroupedOutputAllGroupRepsImpl.cs new file mode 100755 index 000000000..ef7c966bd --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorGroupedOutputAllGroupRepsImpl.cs @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.epl.core +{ + public class ResultSetProcessorGroupedOutputAllGroupRepsImpl : ResultSetProcessorGroupedOutputAllGroupReps { + + private readonly IDictionary groupRepsView = new LinkedHashMap(); + + public object Put(object mk, EventBean[] array) { + return groupRepsView.Push(mk, array); + } + + public void Remove(object key) { + groupRepsView.Remove(key); + } + + public IEnumerator> EntryIterator() { + return groupRepsView.GetEnumerator(); + } + + public void Destroy() { + // no action required + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorGroupedOutputFirstHelper.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorGroupedOutputFirstHelper.cs new file mode 100755 index 000000000..24d4cd50a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorGroupedOutputFirstHelper.cs @@ -0,0 +1,21 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.view; + +namespace com.espertech.esper.epl.core +{ + public interface ResultSetProcessorGroupedOutputFirstHelper + : ResultSetProcessorOutputHelper + { + OutputConditionPolled GetOrAllocate(object mk, AgentInstanceContext agentInstanceContext, OutputConditionPolledFactory optionalOutputFirstConditionFactory); + void Remove(object key); + void Destroy(); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorGroupedOutputFirstHelperImpl.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorGroupedOutputFirstHelperImpl.cs new file mode 100755 index 000000000..ba976de73 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorGroupedOutputFirstHelperImpl.cs @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.view; + +namespace com.espertech.esper.epl.core +{ + public class ResultSetProcessorGroupedOutputFirstHelperImpl : ResultSetProcessorGroupedOutputFirstHelper + { + private readonly IDictionary outputState = + new Dictionary().WithNullSupport(); + + public void Remove(object key) { + outputState.Remove(key); + } + + public OutputConditionPolled GetOrAllocate(object mk, AgentInstanceContext agentInstanceContext, OutputConditionPolledFactory factory) + { + OutputConditionPolled outputStateGroup = outputState.Get(mk); + if (outputStateGroup == null) { + outputStateGroup = factory.MakeNew(agentInstanceContext); + outputState.Put(mk, outputStateGroup); + } + return outputStateGroup; + } + + public OutputConditionPolled Get(object mk) { + return outputState.Get(mk); + } + + public void Put(object mk, OutputConditionPolled outputStateGroup) { + outputState.Put(mk, outputStateGroup); + } + + public void Destroy() { + // no action required + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorHandThrough.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorHandThrough.cs new file mode 100755 index 000000000..334bd51a4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorHandThrough.cs @@ -0,0 +1,195 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.epl.core +{ + /// + /// Result set processor for the hand-through case: no aggregation functions used + /// in the select clause, and no group-by, no having and ordering. + /// + public class ResultSetProcessorHandThrough : ResultSetProcessorBaseSimple + { + private readonly ResultSetProcessorHandThroughFactory _prototype; + private readonly SelectExprProcessor _selectExprProcessor; + private AgentInstanceContext _agentInstanceContext; + + public ResultSetProcessorHandThrough(ResultSetProcessorHandThroughFactory prototype, SelectExprProcessor selectExprProcessor, AgentInstanceContext agentInstanceContext) + { + _prototype = prototype; + _selectExprProcessor = selectExprProcessor; + _agentInstanceContext = agentInstanceContext; + } + + public override AgentInstanceContext AgentInstanceContext + { + set { _agentInstanceContext = value; } + get { return _agentInstanceContext; } + } + + public override EventType ResultEventType + { + get { return _prototype.ResultEventType; } + } + + public override UniformPair ProcessJoinResult(ISet> newEvents, ISet> oldEvents, bool isSynthesize) + { + EventBean[] selectOldEvents = null; + EventBean[] selectNewEvents; + + if (_prototype.IsSelectRStream) + { + selectOldEvents = GetSelectEventsNoHaving(_selectExprProcessor, oldEvents, false, isSynthesize, _agentInstanceContext); + } + selectNewEvents = GetSelectEventsNoHaving(_selectExprProcessor, newEvents, true, isSynthesize, _agentInstanceContext); + + return new UniformPair(selectNewEvents, selectOldEvents); + } + + public override UniformPair ProcessViewResult(EventBean[] newData, EventBean[] oldData, bool isSynthesize) + { + EventBean[] selectOldEvents = null; + + if (_prototype.IsSelectRStream) + { + selectOldEvents = GetSelectEventsNoHaving(_selectExprProcessor, oldData, false, isSynthesize, _agentInstanceContext); + } + EventBean[] selectNewEvents = GetSelectEventsNoHaving(_selectExprProcessor, newData, true, isSynthesize, _agentInstanceContext); + + return new UniformPair(selectNewEvents, selectOldEvents); + } + + /// + /// Applies the select-clause to the given events returning the selected events. The number of events stays the same, i.e. this method does not filter it just transforms the result set. + /// + /// processes each input event and returns output event + /// input events + /// indicates whether we are dealing with new data (istream) or old data (rstream) + /// set to true to indicate that synthetic events are required for an iterator result set + /// The agent instance context. + /// output events, one for each input event + internal static EventBean[] GetSelectEventsNoHaving(SelectExprProcessor exprProcessor, EventBean[] events, bool isNewData, bool isSynthesize, ExprEvaluatorContext agentInstanceContext) + { + if (events == null) + { + return null; + } + + EventBean[] result = new EventBean[events.Length]; + + EventBean[] eventsPerStream = new EventBean[1]; + for (int i = 0; i < events.Length; i++) + { + eventsPerStream[0] = events[i]; + result[i] = exprProcessor.Process(eventsPerStream, isNewData, isSynthesize, agentInstanceContext); + } + + return result; + } + + /// Applies the select-clause to the given events returning the selected events. The number of events stays the same, i.e. this method does not filter it just transforms the result set. + /// processes each input event and returns output event + /// input events + /// indicates whether we are dealing with new data (istream) or old data (rstream) + /// set to true to indicate that synthetic events are required for an iterator result set + /// output events, one for each input event + internal static EventBean[] GetSelectEventsNoHaving(SelectExprProcessor exprProcessor, ICollection> events, bool isNewData, bool isSynthesize, ExprEvaluatorContext agentInstanceContext) + { + int length = events.Count; + if (length == 0) + { + return null; + } + + EventBean[] result = new EventBean[length]; + int count = 0; + foreach (MultiKey key in events) + { + EventBean[] eventsPerStream = key.Array; + result[count] = exprProcessor.Process(eventsPerStream, isNewData, isSynthesize, agentInstanceContext); + count++; + } + + return result; + } + + public override void Clear() + { + // No need to clear state, there is no state held + } + + public override IEnumerator GetEnumerator(Viewable parent) + { + // Return an iterator that gives row-by-row a result + var transform = new ResultSetProcessorSimpleTransform(this); + return parent.Select(transform.Transform).GetEnumerator(); + } + + public override IEnumerator GetEnumerator(ISet> joinSet) + { + // Process join results set as a regular join, includes sorting and having-clause filter + UniformPair result = ProcessJoinResult(joinSet, CollectionUtil.EMPTY_ROW_SET, true); + if ((result == null) || (result.First == null)) + return EnumerationHelper.Empty(); + return ((IEnumerable)result.First).GetEnumerator(); + } + + public override bool HasAggregation + { + get { return false; } + } + + + public override void ApplyViewResult(EventBean[] newData, EventBean[] oldData) + { + } + + public override void ApplyJoinResult(ISet> newEvents, ISet> oldEvents) + { + } + + + public override void ProcessOutputLimitedLastAllNonBufferedView(EventBean[] newData, EventBean[] oldData, bool isGenerateSynthetic, bool isAll) + { + } + + public override void ProcessOutputLimitedLastAllNonBufferedJoin(ISet> newEvents, ISet> oldEvents, bool isGenerateSynthetic, bool isAll) + { + } + + public override UniformPair ContinueOutputLimitedLastAllNonBufferedView(bool isSynthesize, bool isAll) + { + return null; + } + + public override UniformPair ContinueOutputLimitedLastAllNonBufferedJoin(bool isSynthesize, bool isAll) + { + return null; + } + + public override void Stop() + { + // no action required + } + + public override void AcceptHelperVisitor(ResultSetProcessorOutputHelperVisitor visitor) + { + // nothing to visit + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorHandThroughFactory.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorHandThroughFactory.cs new file mode 100755 index 000000000..dec0e739e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorHandThroughFactory.cs @@ -0,0 +1,61 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.agg.service; + +namespace com.espertech.esper.epl.core +{ + /// + /// Result set processor prototye for the hand-through case: + /// no aggregation functions used in the select clause, and no group-by, no having and ordering. + /// + public class ResultSetProcessorHandThroughFactory : ResultSetProcessorFactory + { + private readonly SelectExprProcessor _selectExprProcessor; + private readonly bool _isSelectRStream; + + /// + /// Ctor. + /// + /// for processing the select expression and generting the final output rowsa row per group even if groups didn't change + /// + /// true if remove stream events should be generated + public ResultSetProcessorHandThroughFactory(SelectExprProcessor selectExprProcessor, bool selectRStream) + { + _selectExprProcessor = selectExprProcessor; + _isSelectRStream = selectRStream; + } + + public ResultSetProcessor Instantiate(OrderByProcessor orderByProcessor, AggregationService aggregationService, AgentInstanceContext agentInstanceContext) + { + return new ResultSetProcessorHandThrough(this, _selectExprProcessor, agentInstanceContext); + } + + public EventType ResultEventType + { + get { return _selectExprProcessor.ResultEventType; } + } + + public bool HasAggregation + { + get { return false; } + } + + public bool IsSelectRStream + { + get { return _isSelectRStream; } + } + + public ResultSetProcessorType ResultSetProcessorType + { + get { return ResultSetProcessorType.HANDTHROUGH; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorHelperFactory.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorHelperFactory.cs new file mode 100755 index 000000000..0ba3c4740 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorHelperFactory.cs @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.time; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.variable; +using com.espertech.esper.epl.view; + +namespace com.espertech.esper.epl.core +{ + public interface ResultSetProcessorHelperFactory + { + OutputProcessViewConditionDeltaSet MakeOutputConditionChangeSet(bool isJoin, AgentInstanceContext agentInstanceContext); + OutputConditionFactory MakeOutputConditionTime(ExprTimePeriod timePeriodExpr, bool isStartConditionOnCreation); + OutputConditionFactory MakeOutputConditionExpression(ExprNode whenExpressionNode, IList thenExpressions, StatementContext statementContext, ExprNode andAfterTerminateExpr, IList andAfterTerminateThenExpressions, bool isStartConditionOnCreation) ; + OutputConditionFactory MakeOutputConditionCrontab(IList crontabAtSchedule, StatementContext statementContext, bool isStartConditionOnCreation) ; + OutputConditionFactory MakeOutputConditionCount(int rate, VariableMetaData variableMetaData, StatementContext statementContext); + OutputProcessViewAfterState MakeOutputConditionAfter(long? afterConditionTime, int? afterConditionNumberOfEvents, bool afterConditionSatisfied, AgentInstanceContext agentInstanceContext); + ResultSetProcessorSimpleOutputLastHelper MakeRSSimpleOutputLast(ResultSetProcessorSimpleFactory prototype, ResultSetProcessorSimple simple, AgentInstanceContext agentInstanceContext); + ResultSetProcessorSimpleOutputAllHelper MakeRSSimpleOutputAll(ResultSetProcessorSimpleFactory prototype, ResultSetProcessorSimple resultSetProcessorSimple, AgentInstanceContext agentInstanceContext); + ResultSetProcessorAggregateAllOutputLastHelper MakeRSAggregateAllOutputLast(ResultSetProcessorAggregateAll processor, AgentInstanceContext agentInstanceContext); + ResultSetProcessorAggregateAllOutputAllHelper MakeRSAggregateAllOutputAll(ResultSetProcessorAggregateAll processor, AgentInstanceContext agentInstanceContext); + ResultSetProcessorRowForAllOutputLastHelper MakeRSRowForAllOutputLast(ResultSetProcessorRowForAll processor, ResultSetProcessorRowForAllFactory prototype, AgentInstanceContext agentInstanceContext); + ResultSetProcessorRowForAllOutputAllHelper MakeRSRowForAllOutputAll(ResultSetProcessorRowForAll processor, ResultSetProcessorRowForAllFactory prototype, AgentInstanceContext agentInstanceContext); + ResultSetProcessorGroupedOutputAllGroupReps MakeRSGroupedOutputAllNoOpt(AgentInstanceContext agentInstanceContext, ExprEvaluator[] groupKeyExpressions, int numStreams); + ResultSetProcessorRowPerGroupOutputAllHelper MakeRSRowPerGroupOutputAllOpt(AgentInstanceContext agentInstanceContext, ResultSetProcessorRowPerGroup resultSetProcessorRowPerGroup, ResultSetProcessorRowPerGroupFactory prototype); + ResultSetProcessorRowPerGroupOutputLastHelper MakeRSRowPerGroupOutputLastOpt(AgentInstanceContext agentInstanceContext, ResultSetProcessorRowPerGroup resultSetProcessorRowPerGroup, ResultSetProcessorRowPerGroupFactory prototype); + ResultSetProcessorRowPerGroupUnboundGroupRep MakeRSRowPerGroupUnboundGroupRep(AgentInstanceContext agentInstanceContext, ResultSetProcessorRowPerGroupFactory prototype); + ResultSetProcessorAggregateGroupedOutputAllHelper MakeRSAggregateGroupedOutputAll(AgentInstanceContext agentInstanceContext, ResultSetProcessorAggregateGrouped resultSetProcessorAggregateGrouped, ResultSetProcessorAggregateGroupedFactory prototype); + ResultSetProcessorAggregateGroupedOutputLastHelper MakeRSAggregateGroupedOutputLastOpt(AgentInstanceContext agentInstanceContext, ResultSetProcessorAggregateGrouped resultSetProcessorAggregateGrouped, ResultSetProcessorAggregateGroupedFactory prototype); + ResultSetProcessorGroupedOutputFirstHelper MakeRSGroupedOutputFirst(AgentInstanceContext agentInstanceContext, ExprEvaluator[] groupKeyNodes, OutputConditionPolledFactory optionalOutputFirstConditionFactory, AggregationGroupByRollupDesc optionalGroupByRollupDesc, int optionalRollupLevel); + ResultSetProcessorRowPerGroupRollupOutputLastHelper MakeRSRowPerGroupRollupLast(AgentInstanceContext agentInstanceContext, ResultSetProcessorRowPerGroupRollup resultSetProcessorRowPerGroupRollup, ResultSetProcessorRowPerGroupRollupFactory prototype); + ResultSetProcessorRowPerGroupRollupOutputAllHelper MakeRSRowPerGroupRollupAll(AgentInstanceContext agentInstanceContext, ResultSetProcessorRowPerGroupRollup resultSetProcessorRowPerGroupRollup, ResultSetProcessorRowPerGroupRollupFactory prototype); + ResultSetProcessorRowPerGroupRollupUnboundHelper MakeRSRowPerGroupRollupSnapshotUnbound(AgentInstanceContext agentInstanceContext, ResultSetProcessorRowPerGroupRollupFactory prototype); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorHelperFactoryImpl.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorHelperFactoryImpl.cs new file mode 100755 index 000000000..f15d7500b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorHelperFactoryImpl.cs @@ -0,0 +1,116 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.time; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.variable; +using com.espertech.esper.epl.view; + +namespace com.espertech.esper.epl.core +{ + public class ResultSetProcessorHelperFactoryImpl : ResultSetProcessorHelperFactory + { + public ResultSetProcessorSimpleOutputLastHelper MakeRSSimpleOutputLast(ResultSetProcessorSimpleFactory prototype, ResultSetProcessorSimple simple, AgentInstanceContext agentInstanceContext) { + return new ResultSetProcessorSimpleOutputLastHelperImpl(simple); + } + + public ResultSetProcessorSimpleOutputAllHelper MakeRSSimpleOutputAll(ResultSetProcessorSimpleFactory prototype, ResultSetProcessorSimple resultSetProcessorSimple, AgentInstanceContext agentInstanceContext) { + return new ResultSetProcessorSimpleOutputAllHelperImpl(resultSetProcessorSimple); + } + + public OutputProcessViewConditionDeltaSet MakeOutputConditionChangeSet(bool isJoin, AgentInstanceContext agentInstanceContext) { + return new OutputProcessViewConditionDeltaSetImpl(isJoin); + } + + public OutputConditionFactory MakeOutputConditionTime(ExprTimePeriod timePeriodExpr, bool isStartConditionOnCreation) { + return new OutputConditionTimeFactory(timePeriodExpr, isStartConditionOnCreation); + } + + public OutputConditionFactory MakeOutputConditionExpression(ExprNode whenExpressionNode, IList thenExpressions, StatementContext statementContext, ExprNode andAfterTerminateExpr, IList andAfterTerminateThenExpressions, bool isStartConditionOnCreation) { + return new OutputConditionExpressionFactory(whenExpressionNode, thenExpressions, statementContext, andAfterTerminateExpr, andAfterTerminateThenExpressions, isStartConditionOnCreation); + } + + public OutputConditionFactory MakeOutputConditionCrontab(IList crontabAtSchedule, StatementContext statementContext, bool isStartConditionOnCreation) { + return new OutputConditionCrontabFactory(crontabAtSchedule, statementContext, isStartConditionOnCreation); + } + + public OutputConditionFactory MakeOutputConditionCount(int rate, VariableMetaData variableMetaData, StatementContext statementContext) { + return new OutputConditionCountFactory(rate, variableMetaData); + } + + public OutputProcessViewAfterState MakeOutputConditionAfter(long? afterConditionTime, int? afterConditionNumberOfEvents, bool afterConditionSatisfied, AgentInstanceContext agentInstanceContext) { + if (afterConditionSatisfied) { + return OutputProcessViewAfterStateNone.INSTANCE; + } + return new OutputProcessViewAfterStateImpl(afterConditionTime, afterConditionNumberOfEvents); + } + + public ResultSetProcessorAggregateAllOutputLastHelper MakeRSAggregateAllOutputLast(ResultSetProcessorAggregateAll processor, AgentInstanceContext agentInstanceContext) { + return new ResultSetProcessorAggregateAllOutputLastHelperImpl(processor); + } + + public ResultSetProcessorAggregateAllOutputAllHelper MakeRSAggregateAllOutputAll(ResultSetProcessorAggregateAll processor, AgentInstanceContext agentInstanceContext) { + return new ResultSetProcessorAggregateAllOutputAllHelperImpl(processor); + } + + public ResultSetProcessorRowForAllOutputLastHelper MakeRSRowForAllOutputLast(ResultSetProcessorRowForAll processor, ResultSetProcessorRowForAllFactory prototype, AgentInstanceContext agentInstanceContext) { + return new ResultSetProcessorRowForAllOutputLastHelperImpl(processor); + } + + public ResultSetProcessorRowForAllOutputAllHelper MakeRSRowForAllOutputAll(ResultSetProcessorRowForAll processor, ResultSetProcessorRowForAllFactory prototype, AgentInstanceContext agentInstanceContext) { + return new ResultSetProcessorRowForAllOutputAllHelperImpl(processor); + } + + public ResultSetProcessorGroupedOutputAllGroupReps MakeRSGroupedOutputAllNoOpt(AgentInstanceContext agentInstanceContext, ExprEvaluator[] groupKeyExpressions, int numStreams) { + return new ResultSetProcessorGroupedOutputAllGroupRepsImpl(); + } + + public ResultSetProcessorRowPerGroupOutputAllHelper MakeRSRowPerGroupOutputAllOpt(AgentInstanceContext agentInstanceContext, ResultSetProcessorRowPerGroup resultSetProcessorRowPerGroup, ResultSetProcessorRowPerGroupFactory prototype) { + return new ResultSetProcessorRowPerGroupOutputAllHelperImpl(resultSetProcessorRowPerGroup); + } + + public ResultSetProcessorRowPerGroupOutputLastHelper MakeRSRowPerGroupOutputLastOpt(AgentInstanceContext agentInstanceContext, ResultSetProcessorRowPerGroup resultSetProcessorRowPerGroup, ResultSetProcessorRowPerGroupFactory prototype) { + return new ResultSetProcessorRowPerGroupOutputLastHelperImpl(resultSetProcessorRowPerGroup); + } + + public ResultSetProcessorGroupedOutputFirstHelper MakeRSGroupedOutputFirst(AgentInstanceContext agentInstanceContext, ExprEvaluator[] groupKeyNodes, OutputConditionPolledFactory optionalOutputFirstConditionFactory, AggregationGroupByRollupDesc optionalGroupByRollupDesc, int optionalRollupLevel) { + return new ResultSetProcessorGroupedOutputFirstHelperImpl(); + } + + public ResultSetProcessorRowPerGroupUnboundGroupRep MakeRSRowPerGroupUnboundGroupRep(AgentInstanceContext agentInstanceContext, ResultSetProcessorRowPerGroupFactory prototype) { + return new ResultSetProcessorRowPerGroupUnboundGroupRepImpl(); + } + + public ResultSetProcessorAggregateGroupedOutputAllHelper MakeRSAggregateGroupedOutputAll(AgentInstanceContext agentInstanceContext, ResultSetProcessorAggregateGrouped processor, ResultSetProcessorAggregateGroupedFactory prototype) { + return new ResultSetProcessorAggregateGroupedOutputAllHelperImpl(processor); + } + + public ResultSetProcessorAggregateGroupedOutputLastHelper MakeRSAggregateGroupedOutputLastOpt(AgentInstanceContext agentInstanceContext, ResultSetProcessorAggregateGrouped resultSetProcessorAggregateGrouped, ResultSetProcessorAggregateGroupedFactory prototype) { + return new ResultSetProcessorAggregateGroupedOutputLastHelperImpl(resultSetProcessorAggregateGrouped); + } + + public ResultSetProcessorRowPerGroupRollupOutputLastHelper MakeRSRowPerGroupRollupLast(AgentInstanceContext agentInstanceContext, ResultSetProcessorRowPerGroupRollup resultSetProcessorRowPerGroupRollup, ResultSetProcessorRowPerGroupRollupFactory prototype) { + return new ResultSetProcessorRowPerGroupRollupOutputLastHelperImpl(resultSetProcessorRowPerGroupRollup, prototype.GroupByRollupDesc.Levels.Length); + } + + public ResultSetProcessorRowPerGroupRollupOutputAllHelper MakeRSRowPerGroupRollupAll(AgentInstanceContext agentInstanceContext, ResultSetProcessorRowPerGroupRollup resultSetProcessorRowPerGroupRollup, ResultSetProcessorRowPerGroupRollupFactory prototype) { + return new ResultSetProcessorRowPerGroupRollupOutputAllHelperImpl(resultSetProcessorRowPerGroupRollup, prototype.GroupByRollupDesc.Levels.Length); + } + + public ResultSetProcessorRowPerGroupRollupUnboundHelper MakeRSRowPerGroupRollupSnapshotUnbound(AgentInstanceContext agentInstanceContext, ResultSetProcessorRowPerGroupRollupFactory prototype) { + int levelCount = prototype.GroupByRollupDesc.Levels.Length; + return new ResultSetProcessorRowPerGroupRollupUnboundHelperImpl(levelCount); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorOutputHelper.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorOutputHelper.cs new file mode 100755 index 000000000..ca270616c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorOutputHelper.cs @@ -0,0 +1,14 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.core +{ + public interface ResultSetProcessorOutputHelper + { + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorOutputHelperVisitor.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorOutputHelperVisitor.cs new file mode 100755 index 000000000..ae1e06326 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorOutputHelperVisitor.cs @@ -0,0 +1,15 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.core +{ + public interface ResultSetProcessorOutputHelperVisitor + { + void Visit(ResultSetProcessorOutputHelper helper); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowForAll.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowForAll.cs new file mode 100755 index 000000000..b49b62fa9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowForAll.cs @@ -0,0 +1,633 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.epl.core +{ + /// + /// Result set processor for the case: aggregation functions used in the select clause, and no group-by, + /// and all properties in the select clause are under an aggregation function. + /// This processor does not perform grouping, every event entering and leaving is in the same group. + /// Produces one old event and one new event row every time either at least one old or new event is received. + /// Aggregation state is simply one row holding all the state. + /// + public class ResultSetProcessorRowForAll : ResultSetProcessor + { + protected internal readonly ResultSetProcessorRowForAllFactory _prototype; + private readonly SelectExprProcessor _selectExprProcessor; + private readonly OrderByProcessor _orderByProcessor; + protected internal readonly AggregationService _aggregationService; + protected internal ExprEvaluatorContext _exprEvaluatorContext; + private readonly ResultSetProcessorRowForAllOutputLastHelper _outputLastHelper; + private readonly ResultSetProcessorRowForAllOutputAllHelper _outputAllHelper; + + public ResultSetProcessorRowForAll( + ResultSetProcessorRowForAllFactory prototype, + SelectExprProcessor selectExprProcessor, + OrderByProcessor orderByProcessor, + AggregationService aggregationService, + AgentInstanceContext agentInstanceContext) + { + _prototype = prototype; + _selectExprProcessor = selectExprProcessor; + _orderByProcessor = orderByProcessor; + _aggregationService = aggregationService; + _exprEvaluatorContext = agentInstanceContext; + if (prototype.IsOutputLast) { + _outputLastHelper = prototype.ResultSetProcessorHelperFactory.MakeRSRowForAllOutputLast(this, prototype, agentInstanceContext); + } + else if (prototype.IsOutputAll) { + _outputAllHelper = prototype.ResultSetProcessorHelperFactory.MakeRSRowForAllOutputAll(this, prototype, agentInstanceContext); + } + } + + public ResultSetProcessorRowForAllFactory Prototype + { + get { return _prototype; } + } + + public AgentInstanceContext AgentInstanceContext + { + set { _exprEvaluatorContext = value; } + } + + public EventType ResultEventType + { + get { return _prototype.ResultEventType; } + } + + public UniformPair ProcessJoinResult(ISet> newEvents, ISet> oldEvents, bool isSynthesize) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QResultSetProcessUngroupedFullyAgg();} + EventBean[] selectOldEvents = null; + EventBean[] selectNewEvents; + + if (_prototype.IsUnidirectional) + { + Clear(); + } + + if (_prototype.IsSelectRStream) + { + selectOldEvents = GetSelectListEvents(false, isSynthesize, true); + } + + ResultSetProcessorUtil.ApplyAggJoinResult(_aggregationService, _exprEvaluatorContext, newEvents, oldEvents); + + selectNewEvents = GetSelectListEvents(true, isSynthesize, true); + + if ((selectNewEvents == null) && (selectOldEvents == null)) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AResultSetProcessUngroupedFullyAgg(null, null);} + return null; + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AResultSetProcessUngroupedFullyAgg(selectNewEvents, selectOldEvents);} + return new UniformPair(selectNewEvents, selectOldEvents); + } + + public UniformPair ProcessViewResult(EventBean[] newData, EventBean[] oldData, bool isSynthesize) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QResultSetProcessUngroupedFullyAgg();} + EventBean[] selectOldEvents = null; + EventBean[] selectNewEvents; + + if (_prototype.IsSelectRStream) + { + selectOldEvents = GetSelectListEvents(false, isSynthesize, false); + } + + var eventsPerStream = new EventBean[1]; + ResultSetProcessorUtil.ApplyAggViewResult(_aggregationService, _exprEvaluatorContext, newData, oldData, eventsPerStream); + + // generate new events using select expressions + selectNewEvents = GetSelectListEvents(true, isSynthesize, false); + + if ((selectNewEvents == null) && (selectOldEvents == null)) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AResultSetProcessUngroupedFullyAgg(null, null);} + return null; + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AResultSetProcessUngroupedFullyAgg(selectNewEvents, selectOldEvents);} + return new UniformPair(selectNewEvents, selectOldEvents); + } + + public EventBean[] GetSelectListEvents(bool isNewData, bool isSynthesize, bool join) + { + if (_prototype.OptionalHavingNode != null) + { + if (InstrumentationHelper.ENABLED) { if (!join) InstrumentationHelper.Get().QHavingClauseNonJoin(null); else InstrumentationHelper.Get().QHavingClauseJoin(null);} + var result = _prototype.OptionalHavingNode.Evaluate(new EvaluateParams(null, isNewData, _exprEvaluatorContext)); + if (InstrumentationHelper.ENABLED) { if (!join) InstrumentationHelper.Get().AHavingClauseNonJoin(result.AsBoolean()); else InstrumentationHelper.Get().AHavingClauseJoin(result.AsBoolean()); } + if ((result == null) || (false.Equals(result))) + { + return null; + } + } + + // Since we are dealing with strictly aggregation nodes, there are no events required for evaluating + var theEvent = _selectExprProcessor.Process(CollectionUtil.EVENTBEANARRAY_EMPTY, isNewData, isSynthesize, _exprEvaluatorContext); + + // The result is always a single row + return new EventBean[] {theEvent}; + } + + private EventBean GetSelectListEvent(bool isNewData, bool isSynthesize, bool join) + { + if (_prototype.OptionalHavingNode != null) + { + if (InstrumentationHelper.ENABLED) { if (!join) InstrumentationHelper.Get().QHavingClauseNonJoin(null); else InstrumentationHelper.Get().QHavingClauseJoin(null);} + var result = _prototype.OptionalHavingNode.Evaluate(new EvaluateParams(null, isNewData, _exprEvaluatorContext)); + if (InstrumentationHelper.ENABLED) { if (!join) InstrumentationHelper.Get().AHavingClauseNonJoin(result.AsBoolean()); else InstrumentationHelper.Get().AHavingClauseJoin(result.AsBoolean());} + + if ((result == null) || (false.Equals(result))) + { + return null; + } + } + + // Since we are dealing with strictly aggregation nodes, there are no events required for evaluating + var theEvent = _selectExprProcessor.Process(CollectionUtil.EVENTBEANARRAY_EMPTY, isNewData, isSynthesize, _exprEvaluatorContext); + + // The result is always a single row + return theEvent; + } + + public static readonly IList EMPTY_EVENT_BEAN_LIST = new EventBean[0]; + + public IEnumerator GetEnumerator(Viewable parent) + { + if (!_prototype.IsHistoricalOnly) { + return ObtainEnumerator(); + } + + ResultSetProcessorUtil.ClearAndAggregateUngrouped(_exprEvaluatorContext, _aggregationService, parent); + + var iterator = ObtainEnumerator(); + _aggregationService.ClearResults(_exprEvaluatorContext); + return iterator; + } + + public IEnumerator GetEnumerator(ISet> joinSet) + { + var result = GetSelectListEvents(true, true, true) ?? EMPTY_EVENT_BEAN_LIST; + return result.GetEnumerator(); + } + + public void Clear() + { + _aggregationService.ClearResults(_exprEvaluatorContext); + } + + public UniformPair ProcessOutputLimitedJoin(IList>>> joinEventsSet, bool generateSynthetic, OutputLimitLimitType outputLimitLimitType) + { + if (outputLimitLimitType == OutputLimitLimitType.LAST) { + return ProcessOutputLimitedJoinLast(joinEventsSet, generateSynthetic); + } + else { + return ProcessOutputLimitedJoinDefault(joinEventsSet, generateSynthetic); + } + } + + public UniformPair ProcessOutputLimitedView(IList> viewEventsList, bool generateSynthetic, OutputLimitLimitType outputLimitLimitType) + { + if (outputLimitLimitType == OutputLimitLimitType.LAST) { + return ProcessOutputLimitedViewLast(viewEventsList, generateSynthetic); + } + else { + return ProcessOutputLimitedViewDefault(viewEventsList, generateSynthetic); + } + } + + public bool HasAggregation + { + get { return true; } + } + + public void ApplyViewResult(EventBean[] newData, EventBean[] oldData) + { + var events = new EventBean[1]; + ResultSetProcessorUtil.ApplyAggViewResult(_aggregationService, _exprEvaluatorContext, newData, oldData, events); + } + + public void ApplyJoinResult(ISet> newEvents, ISet> oldEvents) + { + ResultSetProcessorUtil.ApplyAggJoinResult(_aggregationService, _exprEvaluatorContext, newEvents, oldEvents); + } + + public AggregationService AggregationService + { + get { return _aggregationService; } + } + + public void Stop() { + if (_outputLastHelper != null) { + _outputLastHelper.Destroy(); + } + if (_outputAllHelper != null) { + _outputAllHelper.Destroy(); + } + } + + public void ProcessOutputLimitedLastAllNonBufferedView(EventBean[] newData, EventBean[] oldData, bool isGenerateSynthetic, bool isAll) { + if (isAll) { + _outputAllHelper.ProcessView(newData, oldData, isGenerateSynthetic); + } + else { + _outputLastHelper.ProcessView(newData, oldData, isGenerateSynthetic); + } + } + + public void ProcessOutputLimitedLastAllNonBufferedJoin(ISet> newEvents, ISet> oldEvents, bool isGenerateSynthetic, bool isAll) { + if (isAll) { + _outputAllHelper.ProcessJoin(newEvents, oldEvents, isGenerateSynthetic); + } + else { + _outputLastHelper.ProcessJoin(newEvents, oldEvents, isGenerateSynthetic); + } + } + + public UniformPair ContinueOutputLimitedLastAllNonBufferedView(bool isSynthesize, bool isAll) + { + if (isAll) { + return _outputAllHelper.OutputView(isSynthesize); + } + return _outputLastHelper.OutputView(isSynthesize); + } + + public UniformPair ContinueOutputLimitedLastAllNonBufferedJoin(bool isSynthesize, bool isAll) + { + if (isAll) { + return _outputAllHelper.OutputJoin(isSynthesize); + } + return _outputLastHelper.OutputJoin(isSynthesize); + } + + public void AcceptHelperVisitor(ResultSetProcessorOutputHelperVisitor visitor) + { + if (_outputLastHelper != null) + { + visitor.Visit(_outputLastHelper); + } + if (_outputAllHelper != null) + { + visitor.Visit(_outputAllHelper); + } + } + + private void GetSelectListEvent(bool isNewData, bool isSynthesize, IList resultEvents, bool join) + { + if (_prototype.OptionalHavingNode != null) + { + if (InstrumentationHelper.ENABLED) { if (!join) InstrumentationHelper.Get().QHavingClauseNonJoin(null); else InstrumentationHelper.Get().QHavingClauseJoin(null);} + var result = _prototype.OptionalHavingNode.Evaluate(new EvaluateParams(null, isNewData, _exprEvaluatorContext)); + if (InstrumentationHelper.ENABLED) { if (!join) InstrumentationHelper.Get().AHavingClauseNonJoin(result.AsBoolean()); else InstrumentationHelper.Get().AHavingClauseJoin(result.AsBoolean()); } + if ((result == null) || (false.Equals(result))) + { + return; + } + } + + // Since we are dealing with strictly aggregation nodes, there are no events required for evaluating + var theEvent = _selectExprProcessor.Process(CollectionUtil.EVENTBEANARRAY_EMPTY, isNewData, isSynthesize, _exprEvaluatorContext); + + resultEvents.Add(theEvent); + } + + private UniformPair ProcessOutputLimitedJoinDefault(IList>>> joinEventsSet, bool generateSynthetic) + { + IList newEvents = new List(); + IList oldEvents = null; + if (_prototype.IsSelectRStream) + { + oldEvents = new List(); + } + + IList newEventsSortKey = null; + IList oldEventsSortKey = null; + if (_orderByProcessor != null) + { + newEventsSortKey = new List(); + if (_prototype.IsSelectRStream) + { + oldEventsSortKey = new List(); + } + } + + foreach (var pair in joinEventsSet) + { + if (_prototype.IsUnidirectional) + { + Clear(); + } + + var newData = pair.First; + var oldData = pair.Second; + + if (_prototype.IsSelectRStream) + { + GetSelectListEvent(false, generateSynthetic, oldEvents, true); + } + + if (newData != null) + { + // apply new data to aggregates + foreach (var row in newData) + { + _aggregationService.ApplyEnter(row.Array, null, _exprEvaluatorContext); + } + } + if (oldData != null) + { + // apply old data to aggregates + foreach (var row in oldData) + { + _aggregationService.ApplyLeave(row.Array, null, _exprEvaluatorContext); + } + } + + GetSelectListEvent(false, generateSynthetic, newEvents, true); + } + + var newEventsArr = (newEvents.IsEmpty()) ? null : newEvents.ToArray(); + EventBean[] oldEventsArr = null; + if (_prototype.IsSelectRStream) + { + oldEventsArr = (oldEvents.IsEmpty()) ? null : oldEvents.ToArray(); + } + + if (_orderByProcessor != null) + { + var sortKeysNew = (newEventsSortKey.IsEmpty()) ? null : newEventsSortKey.ToArray(); + newEventsArr = _orderByProcessor.Sort(newEventsArr, sortKeysNew, _exprEvaluatorContext); + if (_prototype.IsSelectRStream) + { + var sortKeysOld = (oldEventsSortKey.IsEmpty()) ? null : oldEventsSortKey.ToArray(); + oldEventsArr = _orderByProcessor.Sort(oldEventsArr, sortKeysOld, _exprEvaluatorContext); + } + } + + if (joinEventsSet.IsEmpty()) + { + if (_prototype.IsSelectRStream) + { + oldEventsArr = GetSelectListEvents(false, generateSynthetic, true); + } + newEventsArr = GetSelectListEvents(true, generateSynthetic, true); + } + + if ((newEventsArr == null) && (oldEventsArr == null)) + { + return null; + } + return new UniformPair(newEventsArr, oldEventsArr); + } + + private UniformPair ProcessOutputLimitedJoinLast(IList>>> joinEventsSet, bool generateSynthetic) + { + EventBean lastOldEvent = null; + EventBean lastNewEvent = null; + + // if empty (nothing to post) + if (joinEventsSet.IsEmpty()) + { + if (_prototype.IsSelectRStream) + { + lastOldEvent = GetSelectListEvent(false, generateSynthetic, true); + lastNewEvent = lastOldEvent; + } + else + { + lastNewEvent = GetSelectListEvent(false, generateSynthetic, true); + } + } + + foreach (var pair in joinEventsSet) + { + if (_prototype.IsUnidirectional) + { + Clear(); + } + + var newData = pair.First; + var oldData = pair.Second; + + if ((lastOldEvent == null) && (_prototype.IsSelectRStream)) + { + lastOldEvent = GetSelectListEvent(false, generateSynthetic, true); + } + + if (newData != null) + { + // apply new data to aggregates + foreach (var eventsPerStream in newData) + { + _aggregationService.ApplyEnter(eventsPerStream.Array, null, _exprEvaluatorContext); + } + } + if (oldData != null) + { + // apply old data to aggregates + foreach (var eventsPerStream in oldData) + { + _aggregationService.ApplyLeave(eventsPerStream.Array, null, _exprEvaluatorContext); + } + } + + lastNewEvent = GetSelectListEvent(true, generateSynthetic, true); + } + + var lastNew = (lastNewEvent != null) ? new EventBean[] {lastNewEvent} : null; + var lastOld = (lastOldEvent != null) ? new EventBean[] {lastOldEvent} : null; + + if ((lastNew == null) && (lastOld == null)) + { + return null; + } + return new UniformPair(lastNew, lastOld); + } + + private UniformPair ProcessOutputLimitedViewDefault(IList> viewEventsList, bool generateSynthetic) + { + IList newEvents = new List(); + IList oldEvents = null; + if (_prototype.IsSelectRStream) + { + oldEvents = new List(); + } + + IList newEventsSortKey = null; + IList oldEventsSortKey = null; + if (_orderByProcessor != null) + { + newEventsSortKey = new List(); + if (_prototype.IsSelectRStream) + { + oldEventsSortKey = new List(); + } + } + + foreach (var pair in viewEventsList) + { + var newData = pair.First; + var oldData = pair.Second; + + if (_prototype.IsSelectRStream) + { + GetSelectListEvent(false, generateSynthetic, oldEvents, false); + } + + var eventsPerStream = new EventBean[1]; + if (newData != null) + { + // apply new data to aggregates + foreach (var aNewData in newData) + { + eventsPerStream[0] = aNewData; + _aggregationService.ApplyEnter(eventsPerStream, null, _exprEvaluatorContext); + } + } + if (oldData != null) + { + // apply old data to aggregates + foreach (var anOldData in oldData) + { + eventsPerStream[0] = anOldData; + _aggregationService.ApplyLeave(eventsPerStream, null, _exprEvaluatorContext); + } + } + + GetSelectListEvent(true, generateSynthetic, newEvents, false); + } + + var newEventsArr = (newEvents.IsEmpty()) ? null : newEvents.ToArray(); + EventBean[] oldEventsArr = null; + if (_prototype.IsSelectRStream) + { + oldEventsArr = (oldEvents.IsEmpty()) ? null : oldEvents.ToArray(); + } + if (_orderByProcessor != null) + { + var sortKeysNew = (newEventsSortKey.IsEmpty()) ? null : newEventsSortKey.ToArray(); + newEventsArr = _orderByProcessor.Sort(newEventsArr, sortKeysNew, _exprEvaluatorContext); + if (_prototype.IsSelectRStream) + { + var sortKeysOld = (oldEventsSortKey.IsEmpty()) ? null : oldEventsSortKey.ToArray(); + oldEventsArr = _orderByProcessor.Sort(oldEventsArr, sortKeysOld, _exprEvaluatorContext); + } + } + + if (viewEventsList.IsEmpty()) + { + if (_prototype.IsSelectRStream) + { + oldEventsArr = GetSelectListEvents(false, generateSynthetic, false); + } + newEventsArr = GetSelectListEvents(true, generateSynthetic, false); + } + + if ((newEventsArr == null) && (oldEventsArr == null)) + { + return null; + } + return new UniformPair(newEventsArr, oldEventsArr); + } + + private UniformPair ProcessOutputLimitedViewLast(IList> viewEventsList, bool generateSynthetic) + { + // For last, if there are no events: + // As insert stream, return the current value, if matching the having clause + // As remove stream, return the current value, if matching the having clause + // For last, if there are events in the batch: + // As insert stream, return the newest value that is matching the having clause + // As remove stream, return the oldest value that is matching the having clause + + EventBean lastOldEvent = null; + EventBean lastNewEvent = null; + var eventsPerStream = new EventBean[1]; + + // if empty (nothing to post) + if (viewEventsList.IsEmpty()) + { + if (_prototype.IsSelectRStream) + { + lastOldEvent = GetSelectListEvent(false, generateSynthetic, false); + lastNewEvent = lastOldEvent; + } + else + { + lastNewEvent = GetSelectListEvent(false, generateSynthetic, false); + } + } + + foreach (var pair in viewEventsList) + { + var newData = pair.First; + var oldData = pair.Second; + + if ((lastOldEvent == null) && (_prototype.IsSelectRStream)) + { + lastOldEvent = GetSelectListEvent(false, generateSynthetic, false); + } + + if (newData != null) + { + // apply new data to aggregates + foreach (var aNewData in newData) + { + eventsPerStream[0] = aNewData; + _aggregationService.ApplyEnter(eventsPerStream, null, _exprEvaluatorContext); + } + } + if (oldData != null) + { + // apply old data to aggregates + foreach (var anOldData in oldData) + { + eventsPerStream[0] = anOldData; + _aggregationService.ApplyLeave(eventsPerStream, null, _exprEvaluatorContext); + } + } + + lastNewEvent = GetSelectListEvent(false, generateSynthetic, false); + } + + var lastNew = (lastNewEvent != null) ? new EventBean[] {lastNewEvent} : null; + var lastOld = (lastOldEvent != null) ? new EventBean[] {lastOldEvent} : null; + + if ((lastNew == null) && (lastOld == null)) + { + return null; + } + return new UniformPair(lastNew, lastOld); + } + + private IEnumerator ObtainEnumerator() + { + var selectNewEvents = GetSelectListEvents(true, true, false); + return selectNewEvents != null + ? EnumerationHelper.Singleton(selectNewEvents[0]) + : EnumerationHelper.Empty(); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowForAllFactory.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowForAllFactory.cs new file mode 100755 index 000000000..8b9a2f41a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowForAllFactory.cs @@ -0,0 +1,97 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.epl.core +{ + /// + /// Result set processor _prototype for the case: aggregation functions used in the + /// select clause, and no group-by, and all properties in the select clause are under + /// an aggregation function. + /// + public class ResultSetProcessorRowForAllFactory : ResultSetProcessorFactory + { + private readonly SelectExprProcessor _selectExprProcessor; + + /// + /// Ctor. + /// + /// for processing the select expression and generting the readonly output rows + /// having clause expression node + /// true if remove stream events should be generated + /// true if unidirectional join + /// if set to true [is historical only]. + /// The output limit spec. + /// The result set processor helper factory. + public ResultSetProcessorRowForAllFactory( + SelectExprProcessor selectExprProcessor, + ExprEvaluator optionalHavingNode, + bool isSelectRStream, + bool isUnidirectional, + bool isHistoricalOnly, + OutputLimitSpec outputLimitSpec, + ResultSetProcessorHelperFactory resultSetProcessorHelperFactory) + { + _selectExprProcessor = selectExprProcessor; + OutputLimitSpec = outputLimitSpec; + OptionalHavingNode = optionalHavingNode; + IsSelectRStream = isSelectRStream; + IsUnidirectional = isUnidirectional; + IsHistoricalOnly = isHistoricalOnly; + ResultSetProcessorHelperFactory = resultSetProcessorHelperFactory; + } + + public ResultSetProcessor Instantiate(OrderByProcessor orderByProcessor, AggregationService aggregationService, AgentInstanceContext agentInstanceContext) + { + return new ResultSetProcessorRowForAll(this, _selectExprProcessor, orderByProcessor, aggregationService, agentInstanceContext); + } + + public EventType ResultEventType + { + get { return _selectExprProcessor.ResultEventType; } + } + + public bool HasAggregation + { + get { return true; } + } + + public bool IsSelectRStream { get; private set; } + + public bool IsUnidirectional { get; private set; } + + public bool IsHistoricalOnly { get; private set; } + + public ExprEvaluator OptionalHavingNode { get; private set; } + + public OutputLimitSpec OutputLimitSpec { get; private set; } + + public ResultSetProcessorType ResultSetProcessorType + { + get { return ResultSetProcessorType.FULLYAGGREGATED_UNGROUPED; } + } + + public bool IsOutputLast + { + get { return OutputLimitSpec != null && OutputLimitSpec.DisplayLimit == OutputLimitLimitType.LAST; } + } + + public bool IsOutputAll + { + get { return OutputLimitSpec != null && OutputLimitSpec.DisplayLimit == OutputLimitLimitType.ALL; } + } + + public ResultSetProcessorHelperFactory ResultSetProcessorHelperFactory { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowForAllOutputAllHelper.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowForAllOutputAllHelper.cs new file mode 100755 index 000000000..2bd36c9d0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowForAllOutputAllHelper.cs @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; + +namespace com.espertech.esper.epl.core +{ + public interface ResultSetProcessorRowForAllOutputAllHelper + : ResultSetProcessorOutputHelper + { + void ProcessView(EventBean[] newData, EventBean[] oldData, bool isGenerateSynthetic); + void ProcessJoin(ISet> newEvents, ISet> oldEvents, bool isGenerateSynthetic); + UniformPair OutputView(bool isGenerateSynthetic); + UniformPair OutputJoin(bool isGenerateSynthetic); + void Destroy(); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowForAllOutputAllHelperImpl.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowForAllOutputAllHelperImpl.cs new file mode 100755 index 000000000..44be97265 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowForAllOutputAllHelperImpl.cs @@ -0,0 +1,88 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.core +{ + public class ResultSetProcessorRowForAllOutputAllHelperImpl : ResultSetProcessorRowForAllOutputAllHelper + { + private readonly ResultSetProcessorRowForAll processor; + private readonly Deque eventsOld = new ArrayDeque(2); + private readonly Deque eventsNew = new ArrayDeque(2); + + public ResultSetProcessorRowForAllOutputAllHelperImpl(ResultSetProcessorRowForAll processor) { + this.processor = processor; + } + + public void ProcessView(EventBean[] newData, EventBean[] oldData, bool isGenerateSynthetic) { + if (processor.Prototype.IsSelectRStream) { + EventBean[] eventsX = processor.GetSelectListEvents(false, isGenerateSynthetic, false); + EventBeanUtility.AddToCollection(eventsX, eventsOld); + } + + EventBean[] eventsPerStream = new EventBean[1]; + ResultSetProcessorUtil.ApplyAggViewResult(processor.AggregationService, processor._exprEvaluatorContext, newData, oldData, eventsPerStream); + + EventBean[] events = processor.GetSelectListEvents(true, isGenerateSynthetic, false); + EventBeanUtility.AddToCollection(events, eventsNew); + } + + public void ProcessJoin(ISet> newEvents, ISet> oldEvents, bool isGenerateSynthetic) { + if (processor.Prototype.IsSelectRStream) { + EventBean[] eventsX = processor.GetSelectListEvents(false, isGenerateSynthetic, true); + EventBeanUtility.AddToCollection(eventsX, eventsOld); + } + + ResultSetProcessorUtil.ApplyAggJoinResult(processor.AggregationService, processor._exprEvaluatorContext, newEvents, oldEvents); + + EventBean[] events = processor.GetSelectListEvents(true, isGenerateSynthetic, true); + EventBeanUtility.AddToCollection(events, eventsNew); + } + + public UniformPair OutputView(bool isGenerateSynthetic) { + return Output(isGenerateSynthetic, false); + } + + public UniformPair OutputJoin(bool isGenerateSynthetic) { + return Output(isGenerateSynthetic, true); + } + + public void Destroy() { + // no action required + } + + private UniformPair Output(bool isGenerateSynthetic, bool isJoin) { + EventBean[] oldEvents = EventBeanUtility.ToArrayNullIfEmpty(eventsOld); + EventBean[] newEvents = EventBeanUtility.ToArrayNullIfEmpty(eventsNew); + + if (newEvents == null) { + newEvents = processor.GetSelectListEvents(true, isGenerateSynthetic, isJoin); + } + if (oldEvents == null && processor.Prototype.IsSelectRStream) { + oldEvents = processor.GetSelectListEvents(false, isGenerateSynthetic, isJoin); + } + + UniformPair result = null; + if (oldEvents != null || newEvents != null) { + result = new UniformPair(newEvents, oldEvents); + } + + eventsOld.Clear(); + eventsNew.Clear(); + return result; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowForAllOutputLastHelper.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowForAllOutputLastHelper.cs new file mode 100755 index 000000000..99e4f21ca --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowForAllOutputLastHelper.cs @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; + +namespace com.espertech.esper.epl.core +{ + public interface ResultSetProcessorRowForAllOutputLastHelper + : ResultSetProcessorOutputHelper + { + void ProcessView(EventBean[] newData, EventBean[] oldData, bool isGenerateSynthetic); + void ProcessJoin(ISet> newEvents, ISet> oldEvents, bool isGenerateSynthetic); + UniformPair OutputView(bool isSynthesize); + UniformPair OutputJoin(bool isSynthesize); + void Destroy(); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowForAllOutputLastHelperImpl.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowForAllOutputLastHelperImpl.cs new file mode 100755 index 000000000..0f57d5d5c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowForAllOutputLastHelperImpl.cs @@ -0,0 +1,72 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.epl.core +{ + public class ResultSetProcessorRowForAllOutputLastHelperImpl : ResultSetProcessorRowForAllOutputLastHelper + { + private readonly ResultSetProcessorRowForAll processor; + private EventBean[] lastEventRStreamForOutputLast; + + public ResultSetProcessorRowForAllOutputLastHelperImpl(ResultSetProcessorRowForAll processor) { + this.processor = processor; + } + + public void ProcessView(EventBean[] newData, EventBean[] oldData, bool isGenerateSynthetic) { + if (processor.Prototype.IsSelectRStream && lastEventRStreamForOutputLast == null) { + lastEventRStreamForOutputLast = processor.GetSelectListEvents(false, isGenerateSynthetic, false); + } + + EventBean[] eventsPerStream = new EventBean[1]; + ResultSetProcessorUtil.ApplyAggViewResult(processor.AggregationService, processor._exprEvaluatorContext, newData, oldData, eventsPerStream); + } + + public void ProcessJoin(ISet> newEvents, ISet> oldEvents, bool isGenerateSynthetic) { + if (processor.Prototype.IsSelectRStream && lastEventRStreamForOutputLast == null) { + lastEventRStreamForOutputLast = processor.GetSelectListEvents(false, isGenerateSynthetic, true); + } + + ResultSetProcessorUtil.ApplyAggJoinResult(processor.AggregationService, processor._exprEvaluatorContext, newEvents, oldEvents); + } + + public UniformPair OutputView(bool isSynthesize) { + return ContinueOutputLimitedLastNonBuffered(isSynthesize); + } + + public UniformPair OutputJoin(bool isSynthesize) { + return ContinueOutputLimitedLastNonBuffered(isSynthesize); + } + + public void Destroy() { + // no action required + } + + private UniformPair ContinueOutputLimitedLastNonBuffered(bool isSynthesize) { + EventBean[] events = processor.GetSelectListEvents(true, isSynthesize, false); + UniformPair result = new UniformPair(events, null); + + if (processor.Prototype.IsSelectRStream && lastEventRStreamForOutputLast == null) { + lastEventRStreamForOutputLast = processor.GetSelectListEvents(false, isSynthesize, false); + } + if (lastEventRStreamForOutputLast != null) { + result.Second = lastEventRStreamForOutputLast; + lastEventRStreamForOutputLast = null; + } + + return result; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowPerGroup.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowPerGroup.cs new file mode 100755 index 000000000..8567af138 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowPerGroup.cs @@ -0,0 +1,1859 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.view; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.view; + +namespace com.espertech.esper.epl.core +{ + /// + /// Result set processor for the fully-grouped case: + /// there is a group-by and all non-aggregation event properties in the select clause are listed in the group by, + /// and there are aggregation functions. + /// Produces one row for each group that changed (and not one row per event). Computes MultiKey group-by keys for + /// each event and uses a set of the group-by keys to generate the result rows, using the first (old or new, anyone) event + /// for each distinct group-by key. + /// + public class ResultSetProcessorRowPerGroup + : ResultSetProcessor + , AggregationRowRemovedCallback + { + private readonly ResultSetProcessorRowPerGroupFactory _prototype; + private readonly SelectExprProcessor _selectExprProcessor; + private readonly OrderByProcessor _orderByProcessor; + private readonly AggregationService _aggregationService; + private AgentInstanceContext _agentInstanceContext; + + // For output rate limiting, keep a representative event for each group for + // representing each group in an output limit clause + private ResultSetProcessorGroupedOutputAllGroupReps _outputAllGroupReps; + + private readonly ResultSetProcessorGroupedOutputFirstHelper _outputFirstHelper; + private readonly ResultSetProcessorRowPerGroupOutputLastHelper _outputLastHelper; + private readonly ResultSetProcessorRowPerGroupOutputAllHelper _outputAllHelper; + + public ResultSetProcessorRowPerGroup( + ResultSetProcessorRowPerGroupFactory prototype, + SelectExprProcessor selectExprProcessor, + OrderByProcessor orderByProcessor, + AggregationService aggregationService, + AgentInstanceContext agentInstanceContext) + { + _prototype = prototype; + _selectExprProcessor = selectExprProcessor; + _orderByProcessor = orderByProcessor; + _aggregationService = aggregationService; + _agentInstanceContext = agentInstanceContext; + + aggregationService.SetRemovedCallback(this); + + if (prototype.IsOutputLast) + { + _outputLastHelper = prototype.ResultSetProcessorHelperFactory.MakeRSRowPerGroupOutputLastOpt(agentInstanceContext, this, prototype); + } + else if (prototype.IsOutputAll) + { + if (!prototype.IsEnableOutputLimitOpt) + { + _outputAllGroupReps = prototype.ResultSetProcessorHelperFactory.MakeRSGroupedOutputAllNoOpt(agentInstanceContext, prototype.GroupKeyNodes, prototype.NumStreams); + } + else + { + _outputAllHelper = prototype.ResultSetProcessorHelperFactory.MakeRSRowPerGroupOutputAllOpt(agentInstanceContext, this, prototype); + } + } + else if (prototype.IsOutputFirst) + { + _outputFirstHelper = prototype.ResultSetProcessorHelperFactory.MakeRSGroupedOutputFirst(agentInstanceContext, prototype.GroupKeyNodes, prototype.OptionalOutputFirstConditionFactory, null, -1); + } + } + + public OrderByProcessor OrderByProcessor + { + get { return _orderByProcessor; } + } + + public ResultSetProcessorRowPerGroupFactory Prototype + { + get { return _prototype; } + } + + public AgentInstanceContext AgentInstanceContext + { + get { return _agentInstanceContext; } + set { _agentInstanceContext = value; } + } + + public EventType ResultEventType + { + get { return _prototype.ResultEventType; } + } + + public virtual void ApplyViewResult(EventBean[] newData, EventBean[] oldData) + { + var eventsPerStream = new EventBean[1]; + if (newData != null) + { + // apply new data to aggregates + foreach (var aNewData in newData) + { + eventsPerStream[0] = aNewData; + var mk = GenerateGroupKey(eventsPerStream, true); + _aggregationService.ApplyEnter(eventsPerStream, mk, _agentInstanceContext); + } + } + if (oldData != null) + { + // apply old data to aggregates + foreach (var anOldData in oldData) + { + eventsPerStream[0] = anOldData; + var mk = GenerateGroupKey(eventsPerStream, false); + _aggregationService.ApplyLeave(eventsPerStream, mk, _agentInstanceContext); + } + } + } + + public void ApplyJoinResult(ISet> newEvents, ISet> oldEvents) + { + if (!newEvents.IsEmpty()) + { + // apply old data to aggregates + foreach (var eventsPerStream in newEvents) + { + var mk = GenerateGroupKey(eventsPerStream.Array, true); + _aggregationService.ApplyEnter(eventsPerStream.Array, mk, _agentInstanceContext); + } + } + if (oldEvents != null && !oldEvents.IsEmpty()) + { + // apply old data to aggregates + foreach (var eventsPerStream in oldEvents) + { + var mk = GenerateGroupKey(eventsPerStream.Array, false); + _aggregationService.ApplyLeave(eventsPerStream.Array, mk, _agentInstanceContext); + } + } + } + + public UniformPair ProcessJoinResult(ISet> newEvents, ISet> oldEvents, bool isSynthesize) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QResultSetProcessGroupedRowPerGroup(); } + // Generate group-by keys for all events, collect all keys in a set for later event generation + IDictionary keysAndEvents = new NullableDictionary(); + var newDataMultiKey = GenerateGroupKeys(newEvents, keysAndEvents, true); + var oldDataMultiKey = GenerateGroupKeys(oldEvents, keysAndEvents, false); + + if (_prototype.IsUnidirectional) + { + Clear(); + } + + // generate old events + EventBean[] selectOldEvents = null; + if (_prototype.IsSelectRStream) + { + selectOldEvents = GenerateOutputEventsJoin(keysAndEvents, false, isSynthesize); + } + + // update aggregates + if (!newEvents.IsEmpty()) + { + // apply old data to aggregates + var count = 0; + foreach (var eventsPerStream in newEvents) + { + _aggregationService.ApplyEnter(eventsPerStream.Array, newDataMultiKey[count], _agentInstanceContext); + count++; + } + } + if (oldEvents != null && !oldEvents.IsEmpty()) + { + // apply old data to aggregates + var count = 0; + foreach (var eventsPerStream in oldEvents) + { + _aggregationService.ApplyLeave(eventsPerStream.Array, oldDataMultiKey[count], _agentInstanceContext); + count++; + } + } + + // generate new events using select expressions + var selectNewEvents = GenerateOutputEventsJoin(keysAndEvents, true, isSynthesize); + + if ((selectNewEvents != null) || (selectOldEvents != null)) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AResultSetProcessGroupedRowPerGroup(selectNewEvents, selectOldEvents); } + return new UniformPair(selectNewEvents, selectOldEvents); + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AResultSetProcessGroupedRowPerGroup(null, null); } + return null; + } + + public virtual UniformPair ProcessViewResult(EventBean[] newData, EventBean[] oldData, bool isSynthesize) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QResultSetProcessGroupedRowPerGroup(); } + // Generate group-by keys for all events, collect all keys in a set for later event generation + IDictionary keysAndEvents = new NullableDictionary(); + + var newDataMultiKey = GenerateGroupKeys(newData, keysAndEvents, true); + var oldDataMultiKey = GenerateGroupKeys(oldData, keysAndEvents, false); + + EventBean[] selectOldEvents = null; + if (_prototype.IsSelectRStream) + { + selectOldEvents = GenerateOutputEventsView(keysAndEvents, false, isSynthesize); + } + + // update aggregates + var eventsPerStream = new EventBean[1]; + if (newData != null) + { + // apply new data to aggregates + for (var i = 0; i < newData.Length; i++) + { + eventsPerStream[0] = newData[i]; + _aggregationService.ApplyEnter(eventsPerStream, newDataMultiKey[i], _agentInstanceContext); + } + } + if (oldData != null) + { + // apply old data to aggregates + for (var i = 0; i < oldData.Length; i++) + { + eventsPerStream[0] = oldData[i]; + _aggregationService.ApplyLeave(eventsPerStream, oldDataMultiKey[i], _agentInstanceContext); + } + } + + // generate new events using select expressions + var selectNewEvents = GenerateOutputEventsView(keysAndEvents, true, isSynthesize); + if ((selectNewEvents != null) || (selectOldEvents != null)) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AResultSetProcessGroupedRowPerGroup(selectNewEvents, selectOldEvents); } + return new UniformPair(selectNewEvents, selectOldEvents); + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AResultSetProcessGroupedRowPerGroup(null, null); } + return null; + } + + protected EventBean[] GenerateOutputEventsView(IDictionary keysAndEvents, bool isNewData, bool isSynthesize) + { + var eventsPerStream = new EventBean[1]; + var events = new EventBean[keysAndEvents.Count]; + var keys = new object[keysAndEvents.Count]; + EventBean[][] currentGenerators = null; + if (_prototype.IsSorting) + { + currentGenerators = new EventBean[keysAndEvents.Count][]; + } + + var evaluateParams = new EvaluateParams(eventsPerStream, isNewData, _agentInstanceContext); + var count = 0; + foreach (var entry in keysAndEvents) + { + // Set the current row of aggregation states + _aggregationService.SetCurrentAccess(entry.Key, _agentInstanceContext.AgentInstanceId, null); + + eventsPerStream[0] = entry.Value; + + // Filter the having clause + if (_prototype.OptionalHavingNode != null) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QHavingClauseNonJoin(entry.Value); } + var result = _prototype.OptionalHavingNode.Evaluate(evaluateParams); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AHavingClauseNonJoin(result.AsBoolean()); } + if ((result == null) || (false.Equals(result))) + { + continue; + } + } + + events[count] = _selectExprProcessor.Process(eventsPerStream, isNewData, isSynthesize, _agentInstanceContext); + keys[count] = entry.Key; + if (_prototype.IsSorting) + { + var currentEventsPerStream = new EventBean[] { entry.Value }; + currentGenerators[count] = currentEventsPerStream; + } + + count++; + } + + // Resize if some rows were filtered out + if (count != events.Length) + { + if (count == 0) + { + return null; + } + var outEvents = new EventBean[count]; + Array.Copy(events, 0, outEvents, 0, count); + events = outEvents; + + if (_prototype.IsSorting) + { + var outKeys = new object[count]; + Array.Copy(keys, 0, outKeys, 0, count); + keys = outKeys; + + var outGens = new EventBean[count][]; + Array.Copy(currentGenerators, 0, outGens, 0, count); + currentGenerators = outGens; + } + } + + if (_prototype.IsSorting) + { + events = _orderByProcessor.Sort(events, currentGenerators, keys, isNewData, _agentInstanceContext); + } + + return events; + } + + private void GenerateOutputBatchedRow(IDictionary keysAndEvents, bool isNewData, bool isSynthesize, IList resultEvents, IList optSortKeys, AgentInstanceContext agentInstanceContext) + { + var eventsPerStream = new EventBean[1]; + var evaluateParams = new EvaluateParams(eventsPerStream, isNewData, agentInstanceContext); + + foreach (var entry in keysAndEvents) + { + // Set the current row of aggregation states + _aggregationService.SetCurrentAccess(entry.Key, agentInstanceContext.AgentInstanceId, null); + + eventsPerStream[0] = entry.Value; + + // Filter the having clause + if (_prototype.OptionalHavingNode != null) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QHavingClauseNonJoin(entry.Value); } + var result = _prototype.OptionalHavingNode.Evaluate(evaluateParams); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AHavingClauseNonJoin(result.AsBoolean()); } + if ((result == null) || (false.Equals(result))) + { + continue; + } + } + + resultEvents.Add(_selectExprProcessor.Process(eventsPerStream, isNewData, isSynthesize, agentInstanceContext)); + + if (_prototype.IsSorting) + { + optSortKeys.Add(_orderByProcessor.GetSortKey(eventsPerStream, isNewData, agentInstanceContext)); + } + } + } + + public void GenerateOutputBatchedArr(bool join, IEnumerator> keysAndEvents, bool isNewData, bool isSynthesize, IList resultEvents, IList optSortKeys) + { + while (keysAndEvents.MoveNext()) + { + var entry = keysAndEvents.Current; + GenerateOutputBatchedRow(join, entry.Key, entry.Value, isNewData, isSynthesize, resultEvents, optSortKeys); + } + } + + private void GenerateOutputBatchedRow(bool join, object mk, EventBean[] eventsPerStream, bool isNewData, bool isSynthesize, IList resultEvents, IList optSortKeys) + { + // Set the current row of aggregation states + _aggregationService.SetCurrentAccess(mk, _agentInstanceContext.AgentInstanceId, null); + + // Filter the having clause + if (_prototype.OptionalHavingNode != null) + { + if (InstrumentationHelper.ENABLED) { if (!join) InstrumentationHelper.Get().QHavingClauseNonJoin(eventsPerStream[0]); else InstrumentationHelper.Get().QHavingClauseJoin(eventsPerStream); } + var evaluateParams = new EvaluateParams(eventsPerStream, isNewData, _agentInstanceContext); + var result = _prototype.OptionalHavingNode.Evaluate(evaluateParams); + if (InstrumentationHelper.ENABLED) { if (!join) InstrumentationHelper.Get().AHavingClauseNonJoin(result.AsBoolean()); else InstrumentationHelper.Get().AHavingClauseJoin(result.AsBoolean()); } + if ((result == null) || (false.Equals(result))) + { + return; + } + } + + resultEvents.Add(_selectExprProcessor.Process(eventsPerStream, isNewData, isSynthesize, _agentInstanceContext)); + + if (_prototype.IsSorting) + { + optSortKeys.Add(_orderByProcessor.GetSortKey(eventsPerStream, isNewData, _agentInstanceContext)); + } + } + + public EventBean GenerateOutputBatchedNoSortWMap(bool join, object mk, EventBean[] eventsPerStream, bool isNewData, bool isSynthesize) + { + // Set the current row of aggregation states + _aggregationService.SetCurrentAccess(mk, _agentInstanceContext.AgentInstanceId, null); + + // Filter the having clause + if (_prototype.OptionalHavingNode != null) + { + if (InstrumentationHelper.ENABLED) { if (!join) InstrumentationHelper.Get().QHavingClauseNonJoin(eventsPerStream[0]); else InstrumentationHelper.Get().QHavingClauseJoin(eventsPerStream); } + var evaluateParams = new EvaluateParams(eventsPerStream, isNewData, _agentInstanceContext); + var result = _prototype.OptionalHavingNode.Evaluate(evaluateParams); + if (InstrumentationHelper.ENABLED) { if (!join) InstrumentationHelper.Get().AHavingClauseNonJoin(result.AsBoolean()); else InstrumentationHelper.Get().AHavingClauseJoin(result.AsBoolean()); } + if ((result == null) || (false.Equals(result))) + { + return null; + } + } + + return _selectExprProcessor.Process(eventsPerStream, isNewData, isSynthesize, _agentInstanceContext); + } + + private EventBean[] GenerateOutputEventsJoin(IDictionary keysAndEvents, bool isNewData, bool isSynthesize) + { + var events = new EventBean[keysAndEvents.Count]; + var keys = new object[keysAndEvents.Count]; + EventBean[][] currentGenerators = null; + if (_prototype.IsSorting) + { + currentGenerators = new EventBean[keysAndEvents.Count][]; + } + + var count = 0; + foreach (var entry in keysAndEvents) + { + _aggregationService.SetCurrentAccess(entry.Key, _agentInstanceContext.AgentInstanceId, null); + var eventsPerStream = entry.Value; + + // Filter the having clause + if (_prototype.OptionalHavingNode != null) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QHavingClauseJoin(eventsPerStream); } + var evaluateParams = new EvaluateParams(eventsPerStream, isNewData, _agentInstanceContext); + var result = _prototype.OptionalHavingNode.Evaluate(evaluateParams); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AHavingClauseJoin(result.AsBoolean()); } + if ((result == null) || (false.Equals(result))) + { + continue; + } + } + + events[count] = _selectExprProcessor.Process(eventsPerStream, isNewData, isSynthesize, _agentInstanceContext); + keys[count] = entry.Key; + if (_prototype.IsSorting) + { + currentGenerators[count] = eventsPerStream; + } + + count++; + } + + // Resize if some rows were filtered out + if (count != events.Length) + { + if (count == 0) + { + return null; + } + var outEvents = new EventBean[count]; + Array.Copy(events, 0, outEvents, 0, count); + events = outEvents; + + if (_prototype.IsSorting) + { + var outKeys = new object[count]; + Array.Copy(keys, 0, outKeys, 0, count); + keys = outKeys; + + var outGens = new EventBean[count][]; + Array.Copy(currentGenerators, 0, outGens, 0, count); + currentGenerators = outGens; + } + } + + if (_prototype.IsSorting) + { + events = _orderByProcessor.Sort(events, currentGenerators, keys, isNewData, _agentInstanceContext); + } + + return events; + } + + private object[] GenerateGroupKeys(EventBean[] events, bool isNewData) + { + if (events == null) + { + return null; + } + + var eventsPerStream = new EventBean[1]; + var keys = new object[events.Length]; + + for (var i = 0; i < events.Length; i++) + { + eventsPerStream[0] = events[i]; + keys[i] = GenerateGroupKey(eventsPerStream, isNewData); + } + + return keys; + } + + protected object[] GenerateGroupKeys(EventBean[] events, IDictionary eventPerKey, bool isNewData) + { + if (events == null) + { + return null; + } + + var eventsPerStream = new EventBean[1]; + var keys = new object[events.Length]; + + for (var i = 0; i < events.Length; i++) + { + eventsPerStream[0] = events[i]; + keys[i] = GenerateGroupKey(eventsPerStream, isNewData); + eventPerKey.Put(keys[i], events[i]); + } + + return keys; + } + + private object[] GenerateGroupKeys(ISet> resultSet, IDictionary eventPerKey, bool isNewData) + { + if (resultSet == null || resultSet.IsEmpty()) + { + return null; + } + + var keys = new object[resultSet.Count]; + + var count = 0; + foreach (var eventsPerStream in resultSet) + { + keys[count] = GenerateGroupKey(eventsPerStream.Array, isNewData); + eventPerKey.Put(keys[count], eventsPerStream.Array); + + count++; + } + + return keys; + } + + /// + /// Returns the optional having expression. + /// + /// having expression node + public ExprEvaluator OptionalHavingNode + { + get { return _prototype.OptionalHavingNode; } + } + + /// + /// Returns the select expression processor + /// + /// select processor. + public SelectExprProcessor SelectExprProcessor + { + get { return _selectExprProcessor; } + } + + public virtual IEnumerator GetEnumerator(Viewable parent) + { + if (!_prototype.IsHistoricalOnly) + { + return ObtainEnumerator(parent); + } + + _aggregationService.ClearResults(_agentInstanceContext); + var it = parent.GetEnumerator(); + var eventsPerStream = new EventBean[1]; + while (it.MoveNext()) + { + eventsPerStream[0] = it.Current; + var groupKey = GenerateGroupKey(eventsPerStream, true); + _aggregationService.ApplyEnter(eventsPerStream, groupKey, _agentInstanceContext); + } + + ArrayDeque deque = ResultSetProcessorUtil.EnumeratorToDeque(ObtainEnumerator(parent)); + _aggregationService.ClearResults(_agentInstanceContext); + return deque.GetEnumerator(); + } + + public IEnumerator ObtainEnumerator(Viewable parent) + { + if (_orderByProcessor == null) + { + return ResultSetRowPerGroupEnumerator.New(parent, this, _aggregationService, _agentInstanceContext); + } + return GetEnumeratorSorted(parent.GetEnumerator()); + } + + private static readonly IList EMPTY_EVENT_BEAN_LIST = new EventBean[0]; + + protected IEnumerator GetEnumeratorSorted(IEnumerator parentIter) + { + // Pull all parent events, generate order keys + var eventsPerStream = new EventBean[1]; + IList outgoingEvents = new List(); + IList orderKeys = new List(); + ISet priorSeenGroups = new HashSet(); + + var evaluateParams = new EvaluateParams(eventsPerStream, true, _agentInstanceContext); + while (parentIter.MoveNext()) + { + var candidate = parentIter.Current; + eventsPerStream[0] = candidate; + + var groupKey = GenerateGroupKey(eventsPerStream, true); + _aggregationService.SetCurrentAccess(groupKey, _agentInstanceContext.AgentInstanceId, null); + + if (_prototype.OptionalHavingNode != null) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QHavingClauseNonJoin(candidate); } + var pass = _prototype.OptionalHavingNode.Evaluate(evaluateParams); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AHavingClauseNonJoin(pass.AsBoolean()); } + if ((pass == null) || (false.Equals(pass))) + { + continue; + } + } + if (priorSeenGroups.Contains(groupKey)) + { + continue; + } + priorSeenGroups.Add(groupKey); + + outgoingEvents.Add(_selectExprProcessor.Process(eventsPerStream, true, true, _agentInstanceContext)); + + var orderKey = _orderByProcessor.GetSortKey(eventsPerStream, true, _agentInstanceContext); + orderKeys.Add(orderKey); + } + + // sort + var outgoingEventsArr = outgoingEvents.ToArray(); + var orderKeysArr = orderKeys.ToArray(); + var orderedEvents = _orderByProcessor.Sort(outgoingEventsArr, orderKeysArr, _agentInstanceContext) ?? EMPTY_EVENT_BEAN_LIST; + + return orderedEvents.GetEnumerator(); + } + + public IEnumerator GetEnumerator(ISet> joinSet) + { + IDictionary keysAndEvents = new NullableDictionary(); + GenerateGroupKeys(joinSet, keysAndEvents, true); + var selectNewEvents = GenerateOutputEventsJoin(keysAndEvents, true, true) ?? EMPTY_EVENT_BEAN_LIST; + return selectNewEvents.GetEnumerator(); + } + + public void Clear() + { + _aggregationService.ClearResults(_agentInstanceContext); + } + + public UniformPair ProcessOutputLimitedJoin(IList>>> joinEventsSet, bool generateSynthetic, OutputLimitLimitType outputLimitLimitType) + { + if (outputLimitLimitType == OutputLimitLimitType.DEFAULT) + { + return ProcessOutputLimitedJoinDefault(joinEventsSet, generateSynthetic); + } + else if (outputLimitLimitType == OutputLimitLimitType.ALL) + { + return ProcessOutputLimitedJoinAll(joinEventsSet, generateSynthetic); + } + else if (outputLimitLimitType == OutputLimitLimitType.FIRST) + { + return ProcessOutputLimitedJoinFirst(joinEventsSet, generateSynthetic); + } + else if (outputLimitLimitType == OutputLimitLimitType.LAST) + { + return ProcessOutputLimitedJoinLast(joinEventsSet, generateSynthetic); + } + throw new IllegalStateException("Unrecognized output limit type " + outputLimitLimitType); + } + + public UniformPair ProcessOutputLimitedView(IList> viewEventsList, bool generateSynthetic, OutputLimitLimitType outputLimitLimitType) + { + if (outputLimitLimitType == OutputLimitLimitType.DEFAULT) + { + return ProcessOutputLimitedViewDefault(viewEventsList, generateSynthetic); + } + else if (outputLimitLimitType == OutputLimitLimitType.ALL) + { + return ProcessOutputLimitedViewAll(viewEventsList, generateSynthetic); + } + else if (outputLimitLimitType == OutputLimitLimitType.FIRST) + { + return ProcessOutputLimitedViewFirst(viewEventsList, generateSynthetic); + } + else if (outputLimitLimitType == OutputLimitLimitType.LAST) + { + return ProcessOutputLimitedViewLast(viewEventsList, generateSynthetic); + } + throw new IllegalStateException("Unrecognized output limit type " + outputLimitLimitType); + } + + public bool HasAggregation + { + get { return true; } + } + + public void Removed(object key) + { + if (_outputAllGroupReps != null) + { + _outputAllGroupReps.Remove(key); + } + if (_outputLastHelper != null) + { + _outputLastHelper.Remove(key); + } + if (_outputAllGroupReps != null) + { + _outputAllGroupReps.Remove(key); + } + if (_outputFirstHelper != null) + { + _outputFirstHelper.Remove(key); + } + } + + public object GenerateGroupKey(EventBean[] eventsPerStream, bool isNewData) + { + var evaluateParams = new EvaluateParams(eventsPerStream, isNewData, _agentInstanceContext); + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().QResultSetProcessComputeGroupKeys(isNewData, _prototype.GroupKeyNodeExpressions, eventsPerStream); + object keyObject; + if (_prototype.GroupKeyNode != null) + { + keyObject = _prototype.GroupKeyNode.Evaluate(evaluateParams); + } + else + { + var evals = _prototype.GroupKeyNodes; + var keys = new object[evals.Length]; + for (var i = 0; i < evals.Length; i++) + { + keys[i] = evals[i].Evaluate(evaluateParams); + } + keyObject = new MultiKeyUntyped(keys); + } + + InstrumentationHelper.Get().AResultSetProcessComputeGroupKeys(isNewData, keyObject); + return keyObject; + } + + if (_prototype.GroupKeyNode != null) + { + return _prototype.GroupKeyNode.Evaluate(evaluateParams); + } + else + { + var evals = _prototype.GroupKeyNodes; + var keys = new object[evals.Length]; + for (var i = 0; i < evals.Length; i++) + { + keys[i] = evals[i].Evaluate(evaluateParams); + } + return new MultiKeyUntyped(keys); + } + } + + public void ProcessOutputLimitedLastAllNonBufferedView(EventBean[] newData, EventBean[] oldData, bool isGenerateSynthetic, bool isAll) + { + if (isAll) + { + _outputAllHelper.ProcessView(newData, oldData, isGenerateSynthetic); + } + else + { + _outputLastHelper.ProcessView(newData, oldData, isGenerateSynthetic); + } + } + + public void ProcessOutputLimitedLastAllNonBufferedJoin(ISet> newData, ISet> oldData, bool isGenerateSynthetic, bool isAll) + { + if (isAll) + { + _outputAllHelper.ProcessJoin(newData, oldData, isGenerateSynthetic); + } + else + { + _outputLastHelper.ProcessJoin(newData, oldData, isGenerateSynthetic); + } + } + + public UniformPair ContinueOutputLimitedLastAllNonBufferedView(bool isSynthesize, bool isAll) + { + if (isAll) + { + return _outputAllHelper.OutputView(isSynthesize); + } + return _outputLastHelper.OutputView(isSynthesize); + } + + public UniformPair ContinueOutputLimitedLastAllNonBufferedJoin(bool isSynthesize, bool isAll) + { + if (isAll) + { + return _outputAllHelper.OutputJoin(isSynthesize); + } + return _outputLastHelper.OutputJoin(isSynthesize); + } + + public virtual void Stop() + { + if (_outputAllGroupReps != null) + { + _outputAllGroupReps.Destroy(); + } + if (_outputAllHelper != null) + { + _outputAllHelper.Destroy(); + } + if (_outputLastHelper != null) + { + _outputLastHelper.Destroy(); + } + if (_outputFirstHelper != null) + { + _outputFirstHelper.Destroy(); + } + } + + public AggregationService AggregationService + { + get { return _aggregationService; } + } + + public void AcceptHelperVisitor(ResultSetProcessorOutputHelperVisitor visitor) + { + if (_outputAllGroupReps != null) + { + visitor.Visit(_outputAllGroupReps); + } + if (_outputFirstHelper != null) + { + visitor.Visit(_outputFirstHelper); + } + if (_outputLastHelper != null) + { + visitor.Visit(_outputLastHelper); + } + if (_outputAllHelper != null) + { + visitor.Visit(_outputAllHelper); + } + } + + private UniformPair ProcessOutputLimitedJoinLast(IList>>> joinEventsSet, bool generateSynthetic) + { + IList newEvents = new List(); + IList oldEvents = null; + if (_prototype.IsSelectRStream) + { + oldEvents = new List(); + } + + IList newEventsSortKey = null; + IList oldEventsSortKey = null; + if (_orderByProcessor != null) + { + newEventsSortKey = new List(); + if (_prototype.IsSelectRStream) + { + oldEventsSortKey = new List(); + } + } + + IDictionary groupRepsView = new LinkedHashMap(); + foreach (var pair in joinEventsSet) + { + var newData = pair.First; + var oldData = pair.Second; + + if (_prototype.IsUnidirectional) + { + Clear(); + } + + if (newData != null) + { + // apply new data to aggregates + foreach (var aNewData in newData) + { + var mk = GenerateGroupKey(aNewData.Array, true); + + // if this is a newly encountered group, generate the remove stream event + if (groupRepsView.Push(mk, aNewData.Array) == null) + { + if (_prototype.IsSelectRStream) + { + GenerateOutputBatchedRow(true, mk, aNewData.Array, false, generateSynthetic, oldEvents, oldEventsSortKey); + } + } + _aggregationService.ApplyEnter(aNewData.Array, mk, _agentInstanceContext); + } + } + if (oldData != null) + { + // apply old data to aggregates + foreach (var anOldData in oldData) + { + var mk = GenerateGroupKey(anOldData.Array, true); + + if (groupRepsView.Push(mk, anOldData.Array) == null) + { + if (_prototype.IsSelectRStream) + { + GenerateOutputBatchedRow(true, mk, anOldData.Array, false, generateSynthetic, oldEvents, oldEventsSortKey); + } + } + + _aggregationService.ApplyLeave(anOldData.Array, mk, _agentInstanceContext); + } + } + } + + GenerateOutputBatchedArr(true, groupRepsView.GetEnumerator(), true, generateSynthetic, newEvents, newEventsSortKey); + + var newEventsArr = (newEvents.IsEmpty()) ? null : newEvents.ToArray(); + EventBean[] oldEventsArr = null; + if (_prototype.IsSelectRStream) + { + oldEventsArr = (oldEvents.IsEmpty()) ? null : oldEvents.ToArray(); + } + + if (_orderByProcessor != null) + { + var sortKeysNew = (newEventsSortKey.IsEmpty()) ? null : newEventsSortKey.ToArray(); + newEventsArr = _orderByProcessor.Sort(newEventsArr, sortKeysNew, _agentInstanceContext); + + if (_prototype.IsSelectRStream) + { + var sortKeysOld = (oldEventsSortKey.IsEmpty()) ? null : oldEventsSortKey.ToArray(); + oldEventsArr = _orderByProcessor.Sort(oldEventsArr, sortKeysOld, _agentInstanceContext); + } + } + + if ((newEventsArr == null) && (oldEventsArr == null)) + { + return null; + } + return new UniformPair(newEventsArr, oldEventsArr); + } + + private UniformPair ProcessOutputLimitedJoinFirst(IList>>> joinEventsSet, bool generateSynthetic) + { + IList newEvents = new List(); + IList oldEvents = null; + if (_prototype.IsSelectRStream) + { + oldEvents = new List(); + } + + IList newEventsSortKey = null; + IList oldEventsSortKey = null; + if (_orderByProcessor != null) + { + newEventsSortKey = new List(); + if (_prototype.IsSelectRStream) + { + oldEventsSortKey = new List(); + } + } + + IDictionary groupRepsView = new LinkedHashMap(); + if (_prototype.OptionalHavingNode == null) + { + foreach (var pair in joinEventsSet) + { + var newData = pair.First; + var oldData = pair.Second; + + if (newData != null) + { + // apply new data to aggregates + foreach (var aNewData in newData) + { + var mk = GenerateGroupKey(aNewData.Array, true); + var outputStateGroup = _outputFirstHelper.GetOrAllocate(mk, _agentInstanceContext, _prototype.OptionalOutputFirstConditionFactory); + var pass = outputStateGroup.UpdateOutputCondition(1, 0); + if (pass) + { + // if this is a newly encountered group, generate the remove stream event + if (groupRepsView.Push(mk, aNewData.Array) == null) + { + if (_prototype.IsSelectRStream) + { + GenerateOutputBatchedRow(true, mk, aNewData.Array, false, generateSynthetic, oldEvents, oldEventsSortKey); + } + } + } + _aggregationService.ApplyEnter(aNewData.Array, mk, _agentInstanceContext); + } + } + if (oldData != null) + { + // apply old data to aggregates + foreach (var anOldData in oldData) + { + var mk = GenerateGroupKey(anOldData.Array, true); + var outputStateGroup = _outputFirstHelper.GetOrAllocate(mk, _agentInstanceContext, _prototype.OptionalOutputFirstConditionFactory); + var pass = outputStateGroup.UpdateOutputCondition(0, 1); + if (pass) + { + if (groupRepsView.Push(mk, anOldData.Array) == null) + { + if (_prototype.IsSelectRStream) + { + GenerateOutputBatchedRow(true, mk, anOldData.Array, false, generateSynthetic, oldEvents, oldEventsSortKey); + } + } + } + + _aggregationService.ApplyLeave(anOldData.Array, mk, _agentInstanceContext); + } + } + } + } + else + { + groupRepsView.Clear(); + foreach (var pair in joinEventsSet) + { + var newData = pair.First; + var oldData = pair.Second; + + var newDataMultiKey = GenerateGroupKeys(newData, true); + var oldDataMultiKey = GenerateGroupKeys(oldData, false); + + if (newData != null) + { + // apply new data to aggregates + var count = 0; + foreach (var aNewData in newData) + { + _aggregationService.ApplyEnter(aNewData.Array, newDataMultiKey[count], _agentInstanceContext); + count++; + } + } + if (oldData != null) + { + // apply old data to aggregates + var count = 0; + foreach (var anOldData in oldData) + { + _aggregationService.ApplyLeave(anOldData.Array, oldDataMultiKey[count], _agentInstanceContext); + count++; + } + } + + // evaluate having-clause + if (newData != null) + { + var count = 0; + foreach (var aNewData in newData) + { + var mk = newDataMultiKey[count]; + _aggregationService.SetCurrentAccess(mk, _agentInstanceContext.AgentInstanceId, null); + + // Filter the having clause + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QHavingClauseJoin(aNewData.Array); } + var evaluateParams = new EvaluateParams(aNewData.Array, true, _agentInstanceContext); + var result = _prototype.OptionalHavingNode.Evaluate(evaluateParams); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AHavingClauseJoin(result.AsBoolean()); } + if ((result == null) || (false.Equals(result))) + { + count++; + continue; + } + + var outputStateGroup = _outputFirstHelper.GetOrAllocate(mk, _agentInstanceContext, _prototype.OptionalOutputFirstConditionFactory); + var pass = outputStateGroup.UpdateOutputCondition(1, 0); + if (pass) + { + if (groupRepsView.Push(mk, aNewData.Array) == null) + { + if (_prototype.IsSelectRStream) + { + GenerateOutputBatchedRow(true, mk, aNewData.Array, false, generateSynthetic, oldEvents, oldEventsSortKey); + } + } + } + count++; + } + } + + // evaluate having-clause + if (oldData != null) + { + var count = 0; + foreach (var anOldData in oldData) + { + var mk = oldDataMultiKey[count]; + _aggregationService.SetCurrentAccess(mk, _agentInstanceContext.AgentInstanceId, null); + + // Filter the having clause + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QHavingClauseJoin(anOldData.Array); } + var evaluateParams = new EvaluateParams(anOldData.Array, false, _agentInstanceContext); + var result = _prototype.OptionalHavingNode.Evaluate(evaluateParams); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AHavingClauseJoin(result.AsBoolean()); } + if ((result == null) || (false.Equals(result))) + { + count++; + continue; + } + + var outputStateGroup = _outputFirstHelper.GetOrAllocate(mk, _agentInstanceContext, _prototype.OptionalOutputFirstConditionFactory); + var pass = outputStateGroup.UpdateOutputCondition(0, 1); + if (pass) + { + if (groupRepsView.Push(mk, anOldData.Array) == null) + { + if (_prototype.IsSelectRStream) + { + GenerateOutputBatchedRow(true, mk, anOldData.Array, false, generateSynthetic, oldEvents, oldEventsSortKey); + } + } + } + count++; + } + } + } + } + + GenerateOutputBatchedArr(true, groupRepsView.GetEnumerator(), true, generateSynthetic, newEvents, newEventsSortKey); + + var newEventsArr = (newEvents.IsEmpty()) ? null : newEvents.ToArray(); + EventBean[] oldEventsArr = null; + if (_prototype.IsSelectRStream) + { + oldEventsArr = (oldEvents.IsEmpty()) ? null : oldEvents.ToArray(); + } + + if (_orderByProcessor != null) + { + var sortKeysNew = (newEventsSortKey.IsEmpty()) ? null : newEventsSortKey.ToArray(); + newEventsArr = _orderByProcessor.Sort(newEventsArr, sortKeysNew, _agentInstanceContext); + if (_prototype.IsSelectRStream) + { + var sortKeysOld = (oldEventsSortKey.IsEmpty()) ? null : oldEventsSortKey.ToArray(); + oldEventsArr = _orderByProcessor.Sort(oldEventsArr, sortKeysOld, _agentInstanceContext); + } + } + + if ((newEventsArr == null) && (oldEventsArr == null)) + { + return null; + } + return new UniformPair(newEventsArr, oldEventsArr); + } + + private UniformPair ProcessOutputLimitedJoinAll(IList>>> joinEventsSet, bool generateSynthetic) + { + IList newEvents = new List(); + IList oldEvents = null; + if (_prototype.IsSelectRStream) + { + oldEvents = new List(); + } + + IList newEventsSortKey = null; + IList oldEventsSortKey = null; + if (_orderByProcessor != null) + { + newEventsSortKey = new List(); + if (_prototype.IsSelectRStream) + { + oldEventsSortKey = new List(); + } + } + + if (_prototype.IsSelectRStream) + { + GenerateOutputBatchedArr(true, _outputAllGroupReps.EntryIterator(), false, generateSynthetic, oldEvents, oldEventsSortKey); + } + + foreach (var pair in joinEventsSet) + { + var newData = pair.First; + var oldData = pair.Second; + + if (_prototype.IsUnidirectional) + { + Clear(); + } + + if (newData != null) + { + // apply new data to aggregates + foreach (var aNewData in newData) + { + var mk = GenerateGroupKey(aNewData.Array, true); + + // if this is a newly encountered group, generate the remove stream event + if (_outputAllGroupReps.Put(mk, aNewData.Array) == null) + { + if (_prototype.IsSelectRStream) + { + GenerateOutputBatchedRow(true, mk, aNewData.Array, false, generateSynthetic, oldEvents, oldEventsSortKey); + } + } + _aggregationService.ApplyEnter(aNewData.Array, mk, _agentInstanceContext); + } + } + if (oldData != null) + { + // apply old data to aggregates + foreach (var anOldData in oldData) + { + var mk = GenerateGroupKey(anOldData.Array, true); + + if (_outputAllGroupReps.Put(mk, anOldData.Array) == null) + { + if (_prototype.IsSelectRStream) + { + GenerateOutputBatchedRow(true, mk, anOldData.Array, false, generateSynthetic, oldEvents, oldEventsSortKey); + } + } + + _aggregationService.ApplyLeave(anOldData.Array, mk, _agentInstanceContext); + } + } + } + + GenerateOutputBatchedArr(true, _outputAllGroupReps.EntryIterator(), true, generateSynthetic, newEvents, newEventsSortKey); + + var newEventsArr = (newEvents.IsEmpty()) ? null : newEvents.ToArray(); + EventBean[] oldEventsArr = null; + if (_prototype.IsSelectRStream) + { + oldEventsArr = (oldEvents.IsEmpty()) ? null : oldEvents.ToArray(); + } + + if (_orderByProcessor != null) + { + var sortKeysNew = (newEventsSortKey.IsEmpty()) ? null : newEventsSortKey.ToArray(); + newEventsArr = _orderByProcessor.Sort(newEventsArr, sortKeysNew, _agentInstanceContext); + if (_prototype.IsSelectRStream) + { + var sortKeysOld = (oldEventsSortKey.IsEmpty()) ? null : oldEventsSortKey.ToArray(); + oldEventsArr = _orderByProcessor.Sort(oldEventsArr, sortKeysOld, _agentInstanceContext); + } + } + + if ((newEventsArr == null) && (oldEventsArr == null)) + { + return null; + } + return new UniformPair(newEventsArr, oldEventsArr); + } + + private UniformPair ProcessOutputLimitedJoinDefault(IList>>> joinEventsSet, bool generateSynthetic) + { + IList newEvents = new List(); + IList oldEvents = null; + if (_prototype.IsSelectRStream) + { + oldEvents = new List(); + } + + IList newEventsSortKey = null; + IList oldEventsSortKey = null; + + if (_orderByProcessor != null) + { + newEventsSortKey = new List(); + if (_prototype.IsSelectRStream) + { + oldEventsSortKey = new List(); + } + } + + IDictionary keysAndEvents = new NullableDictionary(); + + foreach (var pair in joinEventsSet) + { + var newData = pair.First; + var oldData = pair.Second; + + if (_prototype.IsUnidirectional) + { + Clear(); + } + + var newDataMultiKey = GenerateGroupKeys(newData, keysAndEvents, true); + var oldDataMultiKey = GenerateGroupKeys(oldData, keysAndEvents, false); + + if (_prototype.IsSelectRStream) + { + GenerateOutputBatchedArr(true, keysAndEvents.GetEnumerator(), false, generateSynthetic, oldEvents, oldEventsSortKey); + } + + if (newData != null) + { + // apply new data to aggregates + var count = 0; + foreach (var aNewData in newData) + { + _aggregationService.ApplyEnter(aNewData.Array, newDataMultiKey[count], _agentInstanceContext); + count++; + } + } + if (oldData != null) + { + // apply old data to aggregates + var count = 0; + foreach (var anOldData in oldData) + { + _aggregationService.ApplyLeave(anOldData.Array, oldDataMultiKey[count], _agentInstanceContext); + count++; + } + } + + GenerateOutputBatchedArr(true, keysAndEvents.GetEnumerator(), true, generateSynthetic, newEvents, newEventsSortKey); + + keysAndEvents.Clear(); + } + + var newEventsArr = (newEvents.IsEmpty()) ? null : newEvents.ToArray(); + EventBean[] oldEventsArr = null; + if (_prototype.IsSelectRStream) + { + oldEventsArr = (oldEvents.IsEmpty()) ? null : oldEvents.ToArray(); + } + + if (_orderByProcessor != null) + { + var sortKeysNew = (newEventsSortKey.IsEmpty()) ? null : newEventsSortKey.ToArray(); + newEventsArr = _orderByProcessor.Sort(newEventsArr, sortKeysNew, _agentInstanceContext); + if (_prototype.IsSelectRStream) + { + var sortKeysOld = (oldEventsSortKey.IsEmpty()) ? null : oldEventsSortKey.ToArray(); + oldEventsArr = _orderByProcessor.Sort(oldEventsArr, sortKeysOld, _agentInstanceContext); + } + } + + if ((newEventsArr == null) && (oldEventsArr == null)) + { + return null; + } + return new UniformPair(newEventsArr, oldEventsArr); + } + + private UniformPair ProcessOutputLimitedViewLast(IList> viewEventsList, bool generateSynthetic) + { + IList newEvents = new List(); + IList oldEvents = null; + if (_prototype.IsSelectRStream) + { + oldEvents = new List(); + } + + IList newEventsSortKey = null; + IList oldEventsSortKey = null; + if (_orderByProcessor != null) + { + newEventsSortKey = new List(); + if (_prototype.IsSelectRStream) + { + oldEventsSortKey = new List(); + } + } + + IDictionary groupRepsView = new LinkedHashMap(); + foreach (var pair in viewEventsList) + { + var newData = pair.First; + var oldData = pair.Second; + + if (newData != null) + { + // apply new data to aggregates + foreach (var aNewData in newData) + { + var eventsPerStream = new EventBean[] { aNewData }; + var mk = GenerateGroupKey(eventsPerStream, true); + + // if this is a newly encountered group, generate the remove stream event + if (groupRepsView.Push(mk, eventsPerStream) == null) + { + if (_prototype.IsSelectRStream) + { + GenerateOutputBatchedRow(false, mk, eventsPerStream, false, generateSynthetic, oldEvents, oldEventsSortKey); + } + } + _aggregationService.ApplyEnter(eventsPerStream, mk, _agentInstanceContext); + } + } + if (oldData != null) + { + // apply old data to aggregates + foreach (var anOldData in oldData) + { + var eventsPerStream = new EventBean[] { anOldData }; + var mk = GenerateGroupKey(eventsPerStream, true); + + if (groupRepsView.Push(mk, eventsPerStream) == null) + { + if (_prototype.IsSelectRStream) + { + GenerateOutputBatchedRow(false, mk, eventsPerStream, false, generateSynthetic, oldEvents, oldEventsSortKey); + } + } + + _aggregationService.ApplyLeave(eventsPerStream, mk, _agentInstanceContext); + } + } + } + + GenerateOutputBatchedArr(false, groupRepsView.GetEnumerator(), true, generateSynthetic, newEvents, newEventsSortKey); + + var newEventsArr = (newEvents.IsEmpty()) ? null : newEvents.ToArray(); + EventBean[] oldEventsArr = null; + if (_prototype.IsSelectRStream) + { + oldEventsArr = (oldEvents.IsEmpty()) ? null : oldEvents.ToArray(); + } + + if (_orderByProcessor != null) + { + var sortKeysNew = (newEventsSortKey.IsEmpty()) ? null : newEventsSortKey.ToArray(); + newEventsArr = _orderByProcessor.Sort(newEventsArr, sortKeysNew, _agentInstanceContext); + if (_prototype.IsSelectRStream) + { + var sortKeysOld = (oldEventsSortKey.IsEmpty()) ? null : oldEventsSortKey.ToArray(); + oldEventsArr = _orderByProcessor.Sort(oldEventsArr, sortKeysOld, _agentInstanceContext); + } + } + + if ((newEventsArr == null) && (oldEventsArr == null)) + { + return null; + } + return new UniformPair(newEventsArr, oldEventsArr); + } + + private UniformPair ProcessOutputLimitedViewFirst(IList> viewEventsList, bool generateSynthetic) + { + IList newEvents = new List(); + IList oldEvents = null; + if (_prototype.IsSelectRStream) + { + oldEvents = new List(); + } + + IList newEventsSortKey = null; + IList oldEventsSortKey = null; + if (_orderByProcessor != null) + { + newEventsSortKey = new List(); + if (_prototype.IsSelectRStream) + { + oldEventsSortKey = new List(); + } + } + + IDictionary groupRepsView = new LinkedHashMap(); + if (_prototype.OptionalHavingNode == null) + { + foreach (var pair in viewEventsList) + { + var newData = pair.First; + var oldData = pair.Second; + + if (newData != null) + { + // apply new data to aggregates + foreach (var aNewData in newData) + { + var eventsPerStream = new EventBean[] { aNewData }; + var mk = GenerateGroupKey(eventsPerStream, true); + var outputStateGroup = _outputFirstHelper.GetOrAllocate(mk, _agentInstanceContext, _prototype.OptionalOutputFirstConditionFactory); + var pass = outputStateGroup.UpdateOutputCondition(1, 0); + if (pass) + { + // if this is a newly encountered group, generate the remove stream event + if (groupRepsView.Push(mk, eventsPerStream) == null) + { + if (_prototype.IsSelectRStream) + { + GenerateOutputBatchedRow(false, mk, eventsPerStream, false, generateSynthetic, oldEvents, oldEventsSortKey); + } + } + } + _aggregationService.ApplyEnter(eventsPerStream, mk, _agentInstanceContext); + } + } + if (oldData != null) + { + // apply old data to aggregates + foreach (var anOldData in oldData) + { + var eventsPerStream = new EventBean[] { anOldData }; + var mk = GenerateGroupKey(eventsPerStream, true); + var outputStateGroup = _outputFirstHelper.GetOrAllocate(mk, _agentInstanceContext, _prototype.OptionalOutputFirstConditionFactory); + var pass = outputStateGroup.UpdateOutputCondition(0, 1); + if (pass) + { + if (groupRepsView.Push(mk, eventsPerStream) == null) + { + if (_prototype.IsSelectRStream) + { + GenerateOutputBatchedRow(false, mk, eventsPerStream, false, generateSynthetic, oldEvents, oldEventsSortKey); + } + } + } + + _aggregationService.ApplyLeave(eventsPerStream, mk, _agentInstanceContext); + } + } + } + } + else + { // having clause present, having clause evaluates at the level of individual posts + var eventsPerStreamOneStream = new EventBean[1]; + foreach (var pair in viewEventsList) + { + var newData = pair.First; + var oldData = pair.Second; + + var newDataMultiKey = GenerateGroupKeys(newData, true); + var oldDataMultiKey = GenerateGroupKeys(oldData, false); + + if (newData != null) + { + // apply new data to aggregates + for (var i = 0; i < newData.Length; i++) + { + eventsPerStreamOneStream[0] = newData[i]; + _aggregationService.ApplyEnter(eventsPerStreamOneStream, newDataMultiKey[i], _agentInstanceContext); + } + } + if (oldData != null) + { + // apply old data to aggregates + for (var i = 0; i < oldData.Length; i++) + { + eventsPerStreamOneStream[0] = oldData[i]; + _aggregationService.ApplyLeave(eventsPerStreamOneStream, oldDataMultiKey[i], _agentInstanceContext); + } + } + + // evaluate having-clause + if (newData != null) + { + var evaluateParams = new EvaluateParams(eventsPerStreamOneStream, true, _agentInstanceContext); + for (var i = 0; i < newData.Length; i++) + { + var mk = newDataMultiKey[i]; + eventsPerStreamOneStream[0] = newData[i]; + _aggregationService.SetCurrentAccess(mk, _agentInstanceContext.AgentInstanceId, null); + + // Filter the having clause + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QHavingClauseNonJoin(newData[i]); } + var result = _prototype.OptionalHavingNode.Evaluate(evaluateParams); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AHavingClauseNonJoin(result.AsBoolean()); } + if ((result == null) || (false.Equals(result))) + { + continue; + } + + var outputStateGroup = _outputFirstHelper.GetOrAllocate(mk, _agentInstanceContext, _prototype.OptionalOutputFirstConditionFactory); + var pass = outputStateGroup.UpdateOutputCondition(0, 1); + if (pass) + { + var eventsPerStream = new EventBean[] { newData[i] }; + if (groupRepsView.Push(mk, eventsPerStream) == null) + { + if (_prototype.IsSelectRStream) + { + GenerateOutputBatchedRow(false, mk, eventsPerStream, true, generateSynthetic, oldEvents, oldEventsSortKey); + } + } + } + } + } + + // evaluate having-clause + if (oldData != null) + { + var evaluateParams = new EvaluateParams(eventsPerStreamOneStream, false, _agentInstanceContext); + for (var i = 0; i < oldData.Length; i++) + { + var mk = oldDataMultiKey[i]; + eventsPerStreamOneStream[0] = oldData[i]; + _aggregationService.SetCurrentAccess(mk, _agentInstanceContext.AgentInstanceId, null); + + // Filter the having clause + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QHavingClauseNonJoin(oldData[i]); } + var result = _prototype.OptionalHavingNode.Evaluate(evaluateParams); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AHavingClauseNonJoin(result.AsBoolean()); } + if ((result == null) || (false.Equals(result))) + { + continue; + } + + var outputStateGroup = _outputFirstHelper.GetOrAllocate(mk, _agentInstanceContext, _prototype.OptionalOutputFirstConditionFactory); + var pass = outputStateGroup.UpdateOutputCondition(0, 1); + if (pass) + { + var eventsPerStream = new EventBean[] { oldData[i] }; + if (groupRepsView.Push(mk, eventsPerStream) == null) + { + if (_prototype.IsSelectRStream) + { + GenerateOutputBatchedRow(false, mk, eventsPerStream, false, generateSynthetic, oldEvents, oldEventsSortKey); + } + } + } + } + } + } + } + + GenerateOutputBatchedArr(false, groupRepsView.GetEnumerator(), true, generateSynthetic, newEvents, newEventsSortKey); + + var newEventsArr = (newEvents.IsEmpty()) ? null : newEvents.ToArray(); + EventBean[] oldEventsArr = null; + if (_prototype.IsSelectRStream) + { + oldEventsArr = (oldEvents.IsEmpty()) ? null : oldEvents.ToArray(); + } + + if (_orderByProcessor != null) + { + var sortKeysNew = (newEventsSortKey.IsEmpty()) ? null : newEventsSortKey.ToArray(); + newEventsArr = _orderByProcessor.Sort(newEventsArr, sortKeysNew, _agentInstanceContext); + if (_prototype.IsSelectRStream) + { + var sortKeysOld = (oldEventsSortKey.IsEmpty()) ? null : oldEventsSortKey.ToArray(); + oldEventsArr = _orderByProcessor.Sort(oldEventsArr, sortKeysOld, _agentInstanceContext); + } + } + + if ((newEventsArr == null) && (oldEventsArr == null)) + { + return null; + } + return new UniformPair(newEventsArr, oldEventsArr); + } + + private UniformPair ProcessOutputLimitedViewAll(IList> viewEventsList, bool generateSynthetic) + { + var eventsPerStream = new EventBean[1]; + + IList newEvents = new List(); + IList oldEvents = null; + if (_prototype.IsSelectRStream) + { + oldEvents = new List(); + } + + IList newEventsSortKey = null; + IList oldEventsSortKey = null; + if (_orderByProcessor != null) + { + newEventsSortKey = new List(); + if (_prototype.IsSelectRStream) + { + oldEventsSortKey = new List(); + } + } + + if (_prototype.IsSelectRStream) + { + GenerateOutputBatchedArr(false, _outputAllGroupReps.EntryIterator(), false, generateSynthetic, oldEvents, oldEventsSortKey); + } + + foreach (var pair in viewEventsList) + { + var newData = pair.First; + var oldData = pair.Second; + + if (newData != null) + { + // apply new data to aggregates + foreach (var aNewData in newData) + { + eventsPerStream[0] = aNewData; + var mk = GenerateGroupKey(eventsPerStream, true); + + // if this is a newly encountered group, generate the remove stream event + if (_outputAllGroupReps.Put(mk, new EventBean[] { aNewData }) == null) + { + if (_prototype.IsSelectRStream) + { + GenerateOutputBatchedRow(false, mk, eventsPerStream, false, generateSynthetic, oldEvents, oldEventsSortKey); + } + } + _aggregationService.ApplyEnter(eventsPerStream, mk, _agentInstanceContext); + } + } + if (oldData != null) + { + // apply old data to aggregates + foreach (var anOldData in oldData) + { + eventsPerStream[0] = anOldData; + var mk = GenerateGroupKey(eventsPerStream, true); + + if (_outputAllGroupReps.Put(mk, new EventBean[] { anOldData }) == null) + { + if (_prototype.IsSelectRStream) + { + GenerateOutputBatchedRow(false, mk, eventsPerStream, false, generateSynthetic, oldEvents, oldEventsSortKey); + } + } + + _aggregationService.ApplyLeave(eventsPerStream, mk, _agentInstanceContext); + } + } + } + + GenerateOutputBatchedArr(false, _outputAllGroupReps.EntryIterator(), true, generateSynthetic, newEvents, newEventsSortKey); + + var newEventsArr = (newEvents.IsEmpty()) ? null : newEvents.ToArray(); + EventBean[] oldEventsArr = null; + if (_prototype.IsSelectRStream) + { + oldEventsArr = (oldEvents.IsEmpty()) ? null : oldEvents.ToArray(); + } + + if (_orderByProcessor != null) + { + var sortKeysNew = (newEventsSortKey.IsEmpty()) ? null : newEventsSortKey.ToArray(); + newEventsArr = _orderByProcessor.Sort(newEventsArr, sortKeysNew, _agentInstanceContext); + if (_prototype.IsSelectRStream) + { + var sortKeysOld = (oldEventsSortKey.IsEmpty()) ? null : oldEventsSortKey.ToArray(); + oldEventsArr = _orderByProcessor.Sort(oldEventsArr, sortKeysOld, _agentInstanceContext); + } + } + + if ((newEventsArr == null) && (oldEventsArr == null)) + { + return null; + } + return new UniformPair(newEventsArr, oldEventsArr); + } + + private UniformPair ProcessOutputLimitedViewDefault(IList> viewEventsList, bool generateSynthetic) + { + IList newEvents = new List(); + IList oldEvents = null; + if (_prototype.IsSelectRStream) + { + oldEvents = new List(); + } + + IList newEventsSortKey = null; + IList oldEventsSortKey = null; + if (_orderByProcessor != null) + { + newEventsSortKey = new List(); + if (_prototype.IsSelectRStream) + { + oldEventsSortKey = new List(); + } + } + + IDictionary keysAndEvents = new NullableDictionary(); + + foreach (var pair in viewEventsList) + { + var newData = pair.First; + var oldData = pair.Second; + + var newDataMultiKey = GenerateGroupKeys(newData, keysAndEvents, true); + var oldDataMultiKey = GenerateGroupKeys(oldData, keysAndEvents, false); + + if (_prototype.IsSelectRStream) + { + GenerateOutputBatchedRow(keysAndEvents, false, generateSynthetic, oldEvents, oldEventsSortKey, _agentInstanceContext); + } + + var eventsPerStream = new EventBean[1]; + if (newData != null) + { + // apply new data to aggregates + var count = 0; + foreach (var aNewData in newData) + { + eventsPerStream[0] = aNewData; + _aggregationService.ApplyEnter(eventsPerStream, newDataMultiKey[count], _agentInstanceContext); + count++; + } + } + if (oldData != null) + { + // apply old data to aggregates + var count = 0; + foreach (var anOldData in oldData) + { + eventsPerStream[0] = anOldData; + _aggregationService.ApplyLeave(eventsPerStream, oldDataMultiKey[count], _agentInstanceContext); + count++; + } + } + + GenerateOutputBatchedRow(keysAndEvents, true, generateSynthetic, newEvents, newEventsSortKey, _agentInstanceContext); + + keysAndEvents.Clear(); + } + + var newEventsArr = (newEvents.IsEmpty()) ? null : newEvents.ToArray(); + EventBean[] oldEventsArr = null; + if (_prototype.IsSelectRStream) + { + oldEventsArr = (oldEvents.IsEmpty()) ? null : oldEvents.ToArray(); + } + + if (_orderByProcessor != null) + { + var sortKeysNew = (newEventsSortKey.IsEmpty()) ? null : newEventsSortKey.ToArray(); + newEventsArr = _orderByProcessor.Sort(newEventsArr, sortKeysNew, _agentInstanceContext); + if (_prototype.IsSelectRStream) + { + var sortKeysOld = (oldEventsSortKey.IsEmpty()) ? null : oldEventsSortKey.ToArray(); + oldEventsArr = _orderByProcessor.Sort(oldEventsArr, sortKeysOld, _agentInstanceContext); + } + } + + if ((newEventsArr == null) && (oldEventsArr == null)) + { + return null; + } + return new UniformPair(newEventsArr, oldEventsArr); + } + + private object[] GenerateGroupKeys(ISet> resultSet, bool isNewData) + { + if (resultSet.IsEmpty()) + { + return null; + } + + var keys = new object[resultSet.Count]; + + var count = 0; + foreach (var eventsPerStream in resultSet) + { + keys[count] = GenerateGroupKey(eventsPerStream.Array, isNewData); + count++; + } + + return keys; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowPerGroupFactory.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowPerGroupFactory.cs new file mode 100755 index 000000000..6878b5bf1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowPerGroupFactory.cs @@ -0,0 +1,187 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.view; + +namespace com.espertech.esper.epl.core +{ + /// + /// Result set processor _prototype for the fully-grouped case: + /// there is a group-by and all non-aggregation event properties in the select clause are listed in the group by, + /// and there are aggregation functions. + /// + public class ResultSetProcessorRowPerGroupFactory : ResultSetProcessorFactory + { + private readonly SelectExprProcessor _selectExprProcessor; + private readonly ExprNode[] _groupKeyNodeExpressions; + private readonly ExprEvaluator _groupKeyNode; + private readonly ExprEvaluator[] _groupKeyNodes; + private readonly ExprEvaluator _optionalHavingNode; + private readonly bool _isSorting; + private readonly bool _isSelectRStream; + private readonly bool _isUnidirectional; + private readonly OutputLimitSpec _outputLimitSpec; + private readonly bool _noDataWindowSingleSnapshot; + private readonly bool _isHistoricalOnly; + + /// + /// Ctor. + /// + /// for processing the select expression and generting the final output rows + /// The group key node expressions. + /// list of group-by expression nodes needed for building the group-by keys + /// expression node representing validated HAVING clause, or null if none given.Aggregation functions in the having node must have been pointed to the AggregationService for evaluation. + /// true if remove stream events should be generated + /// true if unidirectional join + /// The output limit spec. + /// if set to true [is sorting]. + /// if set to true [no data window single stream]. + /// if set to true [is historical only]. + /// if set to true [iterate unbounded]. + /// The result set processor helper factory. + /// if set to true [enable output limit opt]. + /// The number streams. + /// The optional output first condition factory. + public ResultSetProcessorRowPerGroupFactory( + SelectExprProcessor selectExprProcessor, + ExprNode[] groupKeyNodeExpressions, + ExprEvaluator[] groupKeyNodes, + ExprEvaluator optionalHavingNode, + bool isSelectRStream, + bool isUnidirectional, + OutputLimitSpec outputLimitSpec, + bool isSorting, + bool noDataWindowSingleStream, + bool isHistoricalOnly, + bool iterateUnbounded, + ResultSetProcessorHelperFactory resultSetProcessorHelperFactory, + bool enableOutputLimitOpt, + int numStreams, + OutputConditionPolledFactory optionalOutputFirstConditionFactory) + { + _groupKeyNodeExpressions = groupKeyNodeExpressions; + _selectExprProcessor = selectExprProcessor; + _groupKeyNodes = groupKeyNodes; + _groupKeyNode = groupKeyNodes.Length == 1 ? groupKeyNodes[0] : null; + _optionalHavingNode = optionalHavingNode; + _isSorting = isSorting; + _isSelectRStream = isSelectRStream; + _isUnidirectional = isUnidirectional; + _outputLimitSpec = outputLimitSpec; + _noDataWindowSingleSnapshot = iterateUnbounded || (outputLimitSpec != null && outputLimitSpec.DisplayLimit == OutputLimitLimitType.SNAPSHOT && noDataWindowSingleStream); + _isHistoricalOnly = isHistoricalOnly; + ResultSetProcessorHelperFactory = resultSetProcessorHelperFactory; + IsEnableOutputLimitOpt = enableOutputLimitOpt; + NumStreams = numStreams; + OptionalOutputFirstConditionFactory = optionalOutputFirstConditionFactory; + } + + public ResultSetProcessor Instantiate( + OrderByProcessor orderByProcessor, + AggregationService aggregationService, + AgentInstanceContext agentInstanceContext) + { + if (_noDataWindowSingleSnapshot && !_isHistoricalOnly) + { + return new ResultSetProcessorRowPerGroupUnbound( + this, _selectExprProcessor, orderByProcessor, aggregationService, agentInstanceContext); + } + return new ResultSetProcessorRowPerGroup( + this, _selectExprProcessor, orderByProcessor, aggregationService, agentInstanceContext); + } + + public EventType ResultEventType + { + get { return _selectExprProcessor.ResultEventType; } + } + + public bool HasAggregation + { + get { return true; } + } + + public ExprEvaluator[] GroupKeyNodes + { + get { return _groupKeyNodes; } + } + + public ExprEvaluator GroupKeyNode + { + get { return _groupKeyNode; } + } + + public ExprEvaluator OptionalHavingNode + { + get { return _optionalHavingNode; } + } + + public bool IsSorting + { + get { return _isSorting; } + } + + public bool IsSelectRStream + { + get { return _isSelectRStream; } + } + + public bool IsUnidirectional + { + get { return _isUnidirectional; } + } + + public OutputLimitSpec OutputLimitSpec + { + get { return _outputLimitSpec; } + } + + public ExprNode[] GroupKeyNodeExpressions + { + get { return _groupKeyNodeExpressions; } + } + + public bool IsHistoricalOnly + { + get { return _isHistoricalOnly; } + } + + public ResultSetProcessorType ResultSetProcessorType + { + get { return ResultSetProcessorType.FULLYAGGREGATED_GROUPED; } + } + + public bool IsOutputLast + { + get { return _outputLimitSpec != null && _outputLimitSpec.DisplayLimit == OutputLimitLimitType.LAST; } + } + + public bool IsOutputAll + { + get { return _outputLimitSpec != null && _outputLimitSpec.DisplayLimit == OutputLimitLimitType.ALL; } + } + + public bool IsEnableOutputLimitOpt { get; private set; } + + public int NumStreams { get; private set; } + + public bool IsOutputFirst + { + get { return OutputLimitSpec != null && OutputLimitSpec.DisplayLimit == OutputLimitLimitType.FIRST; } + } + + public OutputConditionPolledFactory OptionalOutputFirstConditionFactory { get; private set; } + + public ResultSetProcessorHelperFactory ResultSetProcessorHelperFactory { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowPerGroupOutputAllHelper.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowPerGroupOutputAllHelper.cs new file mode 100755 index 000000000..da9521e7f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowPerGroupOutputAllHelper.cs @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; + +namespace com.espertech.esper.epl.core +{ + public interface ResultSetProcessorRowPerGroupOutputAllHelper : ResultSetProcessorOutputHelper{ + + void ProcessView(EventBean[] newData, EventBean[] oldData, bool isGenerateSynthetic); + + void ProcessJoin(ISet> newData, ISet> oldData, bool isGenerateSynthetic); + + UniformPair OutputView(bool isSynthesize); + + UniformPair OutputJoin(bool isSynthesize); + + void Destroy(); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowPerGroupOutputAllHelperImpl.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowPerGroupOutputAllHelperImpl.cs new file mode 100755 index 000000000..7a4a6db80 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowPerGroupOutputAllHelperImpl.cs @@ -0,0 +1,142 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.epl.core +{ + public class ResultSetProcessorRowPerGroupOutputAllHelperImpl : ResultSetProcessorRowPerGroupOutputAllHelper + { + private readonly ResultSetProcessorRowPerGroup _processor; + + private readonly IDictionary _groupReps = new LinkedHashMap(); + private readonly IDictionary _groupRepsOutputLastUnordRStream = new LinkedHashMap(); + private bool _first; + + public ResultSetProcessorRowPerGroupOutputAllHelperImpl(ResultSetProcessorRowPerGroup processor) { + _processor = processor; + } + + public void ProcessView(EventBean[] newData, EventBean[] oldData, bool isGenerateSynthetic) { + GenerateRemoveStreamJustOnce(isGenerateSynthetic, false); + + if (newData != null) { + foreach (var aNewData in newData) { + var eventsPerStream = new EventBean[] {aNewData}; + var mk = _processor.GenerateGroupKey(eventsPerStream, true); + _groupReps.Put(mk, eventsPerStream); + + if (_processor.Prototype.IsSelectRStream && !_groupRepsOutputLastUnordRStream.ContainsKey(mk)) { + EventBean @event = _processor.GenerateOutputBatchedNoSortWMap(false, mk, eventsPerStream, true, isGenerateSynthetic); + if (@event != null) { + _groupRepsOutputLastUnordRStream.Put(mk, @event); + } + } + _processor.AggregationService.ApplyEnter(eventsPerStream, mk, _processor.AgentInstanceContext); + } + } + if (oldData != null) { + foreach (var anOldData in oldData) { + var eventsPerStream = new EventBean[] {anOldData}; + var mk = _processor.GenerateGroupKey(eventsPerStream, true); + + if (_processor.Prototype.IsSelectRStream && !_groupRepsOutputLastUnordRStream.ContainsKey(mk)) { + EventBean @event = _processor.GenerateOutputBatchedNoSortWMap(false, mk, eventsPerStream, false, isGenerateSynthetic); + if (@event != null) { + _groupRepsOutputLastUnordRStream.Put(mk, @event); + } + } + _processor.AggregationService.ApplyLeave(eventsPerStream, mk, _processor.AgentInstanceContext); + } + } + } + + public void ProcessJoin(ISet> newData, ISet> oldData, bool isGenerateSynthetic) { + GenerateRemoveStreamJustOnce(isGenerateSynthetic, true); + + if (newData != null) { + foreach (var aNewData in newData) { + var mk = _processor.GenerateGroupKey(aNewData.Array, true); + _groupReps.Put(mk, aNewData.Array); + + if (_processor.Prototype.IsSelectRStream && !_groupRepsOutputLastUnordRStream.ContainsKey(mk)) { + EventBean @event = _processor.GenerateOutputBatchedNoSortWMap(true, mk, aNewData.Array, true, isGenerateSynthetic); + if (@event != null) { + _groupRepsOutputLastUnordRStream.Put(mk, @event); + } + } + _processor.AggregationService.ApplyEnter(aNewData.Array, mk, _processor.AgentInstanceContext); + } + } + if (oldData != null) { + foreach (var anOldData in oldData) { + var mk = _processor.GenerateGroupKey(anOldData.Array, false); + if (_processor.Prototype.IsSelectRStream && !_groupRepsOutputLastUnordRStream.ContainsKey(mk)) { + EventBean @event = _processor.GenerateOutputBatchedNoSortWMap(true, mk, anOldData.Array, false, isGenerateSynthetic); + if (@event != null) { + _groupRepsOutputLastUnordRStream.Put(mk, @event); + } + } + _processor.AggregationService.ApplyLeave(anOldData.Array, mk, _processor.AgentInstanceContext); + } + } + } + + public UniformPair OutputView(bool isSynthesize) { + GenerateRemoveStreamJustOnce(isSynthesize, false); + return Output(isSynthesize, false); + } + + public UniformPair OutputJoin(bool isSynthesize) { + GenerateRemoveStreamJustOnce(isSynthesize, true); + return Output(isSynthesize, true); + } + + public void Destroy() { + // no action required + } + + private UniformPair Output(bool isSynthesize, bool join) { + // generate latest new-events from group representatives + IList newEvents = new List(4); + _processor.GenerateOutputBatchedArr(join, _groupReps.GetEnumerator(), true, isSynthesize, newEvents, null); + var newEventsArr = (newEvents.IsEmpty()) ? null : newEvents.ToArrayOrNull(); + + // use old-events as retained, if any + EventBean[] oldEventsArr = null; + if (!_groupRepsOutputLastUnordRStream.IsEmpty()) { + var oldEvents = _groupRepsOutputLastUnordRStream.Values; + oldEventsArr = oldEvents.ToArrayOrNull(); + _groupRepsOutputLastUnordRStream.Clear(); + } + _first = true; + + if (newEventsArr == null && oldEventsArr == null) { + return null; + } + return new UniformPair(newEventsArr, oldEventsArr); + } + + private void GenerateRemoveStreamJustOnce(bool isSynthesize, bool join) { + if (_first && _processor.Prototype.IsSelectRStream) { + foreach (var groupRep in _groupReps) { + var mk = _processor.GenerateGroupKey(groupRep.Value, false); + EventBean @event = _processor.GenerateOutputBatchedNoSortWMap(join, mk, groupRep.Value, false, isSynthesize); + if (@event != null) { + _groupRepsOutputLastUnordRStream.Put(mk, @event); + } + } + } + _first = false; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowPerGroupOutputLastHelper.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowPerGroupOutputLastHelper.cs new file mode 100755 index 000000000..40ce8a3ae --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowPerGroupOutputLastHelper.cs @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; + +namespace com.espertech.esper.epl.core +{ + public interface ResultSetProcessorRowPerGroupOutputLastHelper + : ResultSetProcessorOutputHelper + { + void ProcessView(EventBean[] newData, EventBean[] oldData, bool isGenerateSynthetic); + void ProcessJoin(ISet> newData, ISet> oldData, bool isGenerateSynthetic); + UniformPair OutputView(bool isSynthesize); + UniformPair OutputJoin(bool isSynthesize); + void Remove(object key); + void Destroy(); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowPerGroupOutputLastHelperImpl.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowPerGroupOutputLastHelperImpl.cs new file mode 100755 index 000000000..d5ad4deae --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowPerGroupOutputLastHelperImpl.cs @@ -0,0 +1,129 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.epl.core +{ + public class ResultSetProcessorRowPerGroupOutputLastHelperImpl : ResultSetProcessorRowPerGroupOutputLastHelper + { + private readonly ResultSetProcessorRowPerGroup _processor; + private readonly IDictionary _groupReps = new LinkedHashMap(); + private readonly IDictionary _groupRepsOutputLastUnordRStream = new LinkedHashMap(); + + public ResultSetProcessorRowPerGroupOutputLastHelperImpl(ResultSetProcessorRowPerGroup processor) { + _processor = processor; + } + + public void ProcessView(EventBean[] newData, EventBean[] oldData, bool isGenerateSynthetic) { + if (newData != null) { + foreach (EventBean aNewData in newData) { + EventBean[] eventsPerStream = new EventBean[] {aNewData}; + object mk = _processor.GenerateGroupKey(eventsPerStream, true); + + // if this is a newly encountered group, generate the remove stream event + if (_groupReps.Push(mk, eventsPerStream) == null) { + if (_processor.Prototype.IsSelectRStream) { + EventBean @event = _processor.GenerateOutputBatchedNoSortWMap(false, mk, eventsPerStream, true, isGenerateSynthetic); + if (@event != null) { + _groupRepsOutputLastUnordRStream.Put(mk, @event); + } + } + } + _processor.AggregationService.ApplyEnter(eventsPerStream, mk, _processor.AgentInstanceContext); + } + } + if (oldData != null) { + foreach (EventBean anOldData in oldData) { + EventBean[] eventsPerStream = new EventBean[] {anOldData}; + object mk = _processor.GenerateGroupKey(eventsPerStream, true); + + if (_groupReps.Push(mk, eventsPerStream) == null) { + if (_processor.Prototype.IsSelectRStream) { + EventBean @event = _processor.GenerateOutputBatchedNoSortWMap(false, mk, eventsPerStream, false, isGenerateSynthetic); + if (@event != null) { + _groupRepsOutputLastUnordRStream.Put(mk, @event); + } + } + } + + _processor.AggregationService.ApplyLeave(eventsPerStream, mk, _processor.AgentInstanceContext); + } + } + } + + public void ProcessJoin(ISet> newData, ISet> oldData, bool isGenerateSynthetic) { + if (newData != null) { + foreach (MultiKey aNewData in newData) { + object mk = _processor.GenerateGroupKey(aNewData.Array, true); + if (_groupReps.Push(mk, aNewData.Array) == null) { + if (_processor.Prototype.IsSelectRStream) { + EventBean @event = _processor.GenerateOutputBatchedNoSortWMap(true, mk, aNewData.Array, false, isGenerateSynthetic); + if (@event != null) { + _groupRepsOutputLastUnordRStream.Put(mk, @event); + } + } + } + _processor.AggregationService.ApplyEnter(aNewData.Array, mk, _processor.AgentInstanceContext); + } + } + if (oldData != null) { + foreach (MultiKey anOldData in oldData) { + object mk = _processor.GenerateGroupKey(anOldData.Array, false); + if (_groupReps.Push(mk, anOldData.Array) == null) { + if (_processor.Prototype.IsSelectRStream) { + EventBean @event = _processor.GenerateOutputBatchedNoSortWMap(true, mk, anOldData.Array, false, isGenerateSynthetic); + if (@event != null) { + _groupRepsOutputLastUnordRStream.Put(mk, @event); + } + } + } + _processor.AggregationService.ApplyLeave(anOldData.Array, mk, _processor.AgentInstanceContext); + } + } + } + + public UniformPair OutputView(bool isSynthesize) { + return Output(isSynthesize, false); + } + + public UniformPair OutputJoin(bool isSynthesize) { + return Output(isSynthesize, true); + } + + public void Destroy() { + // no action required + } + + public void Remove(object key) { + _groupReps.Remove(key); + } + + private UniformPair Output(bool isSynthesize, bool join) { + IList newEvents = new List(4); + _processor.GenerateOutputBatchedArr(join, _groupReps.GetEnumerator(), true, isSynthesize, newEvents, null); + _groupReps.Clear(); + EventBean[] newEventsArr = (newEvents.IsEmpty()) ? null : newEvents.ToArrayOrNull(); + + EventBean[] oldEventsArr = null; + if (_groupRepsOutputLastUnordRStream != null && !_groupRepsOutputLastUnordRStream.IsEmpty()) { + ICollection oldEvents = _groupRepsOutputLastUnordRStream.Values; + oldEventsArr = oldEvents.ToArrayOrNull(); + } + + if (newEventsArr == null && oldEventsArr == null) { + return null; + } + return new UniformPair(newEventsArr, oldEventsArr); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowPerGroupRollup.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowPerGroupRollup.cs new file mode 100755 index 000000000..f89d85fa7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowPerGroupRollup.cs @@ -0,0 +1,1535 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.client.scopetest; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.agg.rollup; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.view; + +namespace com.espertech.esper.epl.core +{ + public class ResultSetProcessorRowPerGroupRollup + : ResultSetProcessor + , AggregationRowRemovedCallback + { + private readonly ResultSetProcessorRowPerGroupRollupFactory _prototype; + private readonly OrderByProcessor _orderByProcessor; + private readonly AggregationService _aggregationService; + private AgentInstanceContext _agentInstanceContext; + + private readonly IDictionary[] _groupRepsPerLevelBuf; + private readonly IDictionary[] _eventPerGroupBuf; + private readonly IDictionary[] _eventPerGroupJoinBuf; + private readonly EventArrayAndSortKeyArray _rstreamEventSortArrayBuf; + + private readonly ResultSetProcessorRowPerGroupRollupOutputLastHelper _outputLastHelper; + private readonly ResultSetProcessorRowPerGroupRollupOutputAllHelper _outputAllHelper; + private readonly ResultSetProcessorGroupedOutputFirstHelper[] _outputFirstHelpers; + + public ResultSetProcessorRowPerGroupRollup( + ResultSetProcessorRowPerGroupRollupFactory prototype, + OrderByProcessor orderByProcessor, + AggregationService aggregationService, + AgentInstanceContext agentInstanceContext) + { + _prototype = prototype; + _orderByProcessor = orderByProcessor; + _aggregationService = aggregationService; + _agentInstanceContext = agentInstanceContext; + aggregationService.SetRemovedCallback(this); + + var levelCount = prototype.GroupByRollupDesc.Levels.Length; + + if (prototype.IsJoin) { + _eventPerGroupJoinBuf = new IDictionary[levelCount]; + for (var i = 0; i < levelCount; i++) { + _eventPerGroupJoinBuf[i] = new LinkedHashMap(); + } + _eventPerGroupBuf = null; + } + else { + _eventPerGroupBuf = new IDictionary[levelCount]; + for (var i = 0; i < levelCount; i++) { + _eventPerGroupBuf[i] = new LinkedHashMap(); + } + _eventPerGroupJoinBuf = null; + } + + if (prototype.OutputLimitSpec != null) { + _groupRepsPerLevelBuf = new IDictionary[levelCount]; + for (var i = 0; i < levelCount; i++) { + _groupRepsPerLevelBuf[i] = new LinkedHashMap(); + } + + if (prototype.OutputLimitSpec.DisplayLimit == OutputLimitLimitType.LAST) { + _outputLastHelper = prototype.ResultSetProcessorHelperFactory.MakeRSRowPerGroupRollupLast(agentInstanceContext, this, prototype); + _outputAllHelper = null; + } + else if (prototype.OutputLimitSpec.DisplayLimit == OutputLimitLimitType.ALL) { + _outputAllHelper = prototype.ResultSetProcessorHelperFactory.MakeRSRowPerGroupRollupAll(agentInstanceContext, this, prototype); + _outputLastHelper = null; + } + else { + _outputLastHelper = null; + _outputAllHelper = null; + } + } + else { + _groupRepsPerLevelBuf = null; + _outputLastHelper = null; + _outputAllHelper = null; + } + + // Allocate output state for output-first + if (prototype.OutputLimitSpec != null && prototype.OutputLimitSpec.DisplayLimit == OutputLimitLimitType.FIRST) { + _outputFirstHelpers = new ResultSetProcessorGroupedOutputFirstHelper[levelCount]; + for (var i = 0; i < levelCount; i++) { + _outputFirstHelpers[i] = prototype.ResultSetProcessorHelperFactory.MakeRSGroupedOutputFirst(agentInstanceContext, prototype.GroupKeyNodes, prototype.OptionalOutputFirstConditionFactory, prototype.GroupByRollupDesc, i); + } + } + else { + _outputFirstHelpers = null; + } + + if (prototype.OutputLimitSpec != null && (prototype.IsSelectRStream || prototype.OutputLimitSpec.DisplayLimit == OutputLimitLimitType.FIRST)) { + var eventsPerLevel = new IList[prototype.GroupByRollupDesc.Levels.Length]; + IList[] sortKeyPerLevel = null; + if (orderByProcessor != null) { + sortKeyPerLevel = new IList[prototype.GroupByRollupDesc.Levels.Length]; + } + foreach (var level in prototype.GroupByRollupDesc.Levels) { + eventsPerLevel[level.LevelNumber] = new List(); + if (orderByProcessor != null) { + sortKeyPerLevel[level.LevelNumber] = new List(); + } + } + _rstreamEventSortArrayBuf = new EventArrayAndSortKeyArray(eventsPerLevel, sortKeyPerLevel); + } + else { + _rstreamEventSortArrayBuf = null; + } + } + + public ResultSetProcessorRowPerGroupRollupFactory Prototype + { + get { return _prototype; } + } + + public OrderByProcessor OrderByProcessor + { + get { return _orderByProcessor; } + } + + public AgentInstanceContext AgentInstanceContext + { + get { return _agentInstanceContext; } + set { _agentInstanceContext = value; } + } + + public AggregationService AggregationService + { + get { return _aggregationService; } + } + + public EventType ResultEventType + { + get { return _prototype.ResultEventType; } + } + + public UniformPair ProcessJoinResult(ISet> newEvents, ISet> oldEvents, bool isSynthesize) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QResultSetProcessGroupedRowPerGroup();} + + if (_prototype.IsUnidirectional) { + Clear(); + } + + ResetEventPerGroupJoinBuf(); + var newDataMultiKey = GenerateGroupKeysJoin(newEvents, _eventPerGroupJoinBuf, true); + var oldDataMultiKey = GenerateGroupKeysJoin(oldEvents, _eventPerGroupJoinBuf, false); + + EventBean[] selectOldEvents = null; + if (_prototype.IsSelectRStream) { + selectOldEvents = GenerateOutputEventsJoin(_eventPerGroupJoinBuf, false, isSynthesize); + } + + // update aggregates + if (newEvents != null) { + var count = 0; + foreach (var mk in newEvents) { + _aggregationService.ApplyEnter(mk.Array, newDataMultiKey[count++], _agentInstanceContext); + } + } + if (oldEvents != null) { + var count = 0; + foreach (var mk in oldEvents) { + _aggregationService.ApplyLeave(mk.Array, oldDataMultiKey[count++], _agentInstanceContext); + } + } + + // generate new events using select expressions + var selectNewEvents = GenerateOutputEventsJoin(_eventPerGroupJoinBuf, true, isSynthesize); + + if ((selectNewEvents != null) || (selectOldEvents != null)) { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AResultSetProcessGroupedRowPerGroup(selectNewEvents, selectOldEvents);} + return new UniformPair(selectNewEvents, selectOldEvents); + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AResultSetProcessGroupedRowPerGroup(null, null);} + return null; + } + + public virtual UniformPair ProcessViewResult(EventBean[] newData, EventBean[] oldData, bool isSynthesize) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QResultSetProcessGroupedRowPerGroup();} + + ResetEventPerGroupBuf(); + var newDataMultiKey = GenerateGroupKeysView(newData, _eventPerGroupBuf, true); + var oldDataMultiKey = GenerateGroupKeysView(oldData, _eventPerGroupBuf, false); + + EventBean[] selectOldEvents = null; + if (_prototype.IsSelectRStream) { + selectOldEvents = GenerateOutputEventsView(_eventPerGroupBuf, false, isSynthesize); + } + + // update aggregates + var eventsPerStream = new EventBean[1]; + if (newData != null) { + for (var i = 0; i < newData.Length; i++) { + eventsPerStream[0] = newData[i]; + _aggregationService.ApplyEnter(eventsPerStream, newDataMultiKey[i], _agentInstanceContext); + } + } + if (oldData != null) { + for (var i = 0; i < oldData.Length; i++) { + eventsPerStream[0] = oldData[i]; + _aggregationService.ApplyLeave(eventsPerStream, oldDataMultiKey[i], _agentInstanceContext); + } + } + + // generate new events using select expressions + var selectNewEvents = GenerateOutputEventsView(_eventPerGroupBuf, true, isSynthesize); + + if ((selectNewEvents != null) || (selectOldEvents != null)) { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AResultSetProcessGroupedRowPerGroup(selectNewEvents, selectOldEvents);} + return new UniformPair(selectNewEvents, selectOldEvents); + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AResultSetProcessGroupedRowPerGroup(null, null);} + return null; + } + + protected EventBean[] GenerateOutputEventsView(IDictionary[] keysAndEvents, bool isNewData, bool isSynthesize) + { + var eventsPerStream = new EventBean[1]; + var events = new List(1); + IList currentGenerators = null; + if(_prototype.IsSorting) { + currentGenerators = new List(4); + } + + var levels = _prototype.GroupByRollupDesc.Levels; + var selectExprProcessors = _prototype.PerLevelExpression.SelectExprProcessor; + var optionalHavingClauses = _prototype.PerLevelExpression.OptionalHavingNodes; + var evaluateParams = new EvaluateParams(eventsPerStream, isNewData, _agentInstanceContext); + foreach (var level in levels) + { + foreach (var entry in keysAndEvents[level.LevelNumber]) { + var groupKey = entry.Key; + + // Set the current row of aggregation states + _aggregationService.SetCurrentAccess(groupKey, _agentInstanceContext.AgentInstanceId, level); + eventsPerStream[0] = entry.Value; + + // Filter the having clause + if (optionalHavingClauses != null) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QHavingClauseNonJoin(entry.Value);} + var result = optionalHavingClauses[level.LevelNumber].Evaluate(evaluateParams); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AHavingClauseNonJoin(result.AsBoxedBoolean());} + if ((result == null) || (false.Equals(result))) { + continue; + } + } + events.Add(selectExprProcessors[level.LevelNumber].Process(eventsPerStream, isNewData, isSynthesize, _agentInstanceContext)); + + if(_prototype.IsSorting) { + var currentEventsPerStream = new EventBean[] { entry.Value }; + currentGenerators.Add(new GroupByRollupKey(currentEventsPerStream, level, groupKey)); + } + } + } + + if (events.IsEmpty()) { + return null; + } + var outgoing = events.ToArray(); + if (outgoing.Length > 1 && _prototype.IsSorting) { + return _orderByProcessor.Sort(outgoing, currentGenerators, isNewData, _agentInstanceContext, _prototype.PerLevelExpression.OptionalOrderByElements); + } + return outgoing; + } + + private EventBean[] GenerateOutputEventsJoin(IDictionary[] eventPairs, bool isNewData, bool synthesize) { + var events = new List(1); + IList currentGenerators = null; + if(_prototype.IsSorting) { + currentGenerators = new List(4); + } + + var levels = _prototype.GroupByRollupDesc.Levels; + var selectExprProcessors = _prototype.PerLevelExpression.SelectExprProcessor; + var optionalHavingClauses = _prototype.PerLevelExpression.OptionalHavingNodes; + foreach (var level in levels) { + foreach (var entry in eventPairs[level.LevelNumber]) { + var groupKey = entry.Key; + + // Set the current row of aggregation states + _aggregationService.SetCurrentAccess(groupKey, _agentInstanceContext.AgentInstanceId, level); + + // Filter the having clause + if (optionalHavingClauses != null) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QHavingClauseJoin(entry.Value);} + var evaluateParams = new EvaluateParams(entry.Value, isNewData, _agentInstanceContext); + var result = optionalHavingClauses[level.LevelNumber].Evaluate(evaluateParams); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AHavingClauseJoin(result.AsBoolean());} + if ((result == null) || (false.Equals(result))) { + continue; + } + } + events.Add(selectExprProcessors[level.LevelNumber].Process(entry.Value, isNewData, synthesize, _agentInstanceContext)); + + if(_prototype.IsSorting) { + currentGenerators.Add(new GroupByRollupKey(entry.Value, level, groupKey)); + } + } + } + + if (events.IsEmpty()) { + return null; + } + var outgoing = events.ToArray(); + if (outgoing.Length > 1 && _prototype.IsSorting) { + return _orderByProcessor.Sort(outgoing, currentGenerators, isNewData, _agentInstanceContext, _prototype.PerLevelExpression.OptionalOrderByElements); + } + return outgoing; + } + + public virtual IEnumerator GetEnumerator(Viewable parent) + { + if (!_prototype.IsHistoricalOnly) { + return ObtainEnumerator(parent); + } + + _aggregationService.ClearResults(_agentInstanceContext); + var it = parent.GetEnumerator(); + var eventsPerStream = new EventBean[1]; + var groupKeys = new object[_prototype.GroupByRollupDesc.Levels.Length]; + var levels = _prototype.GroupByRollupDesc.Levels; + while (it.MoveNext()) { + eventsPerStream[0] = it.Current; + var groupKeyComplete = GenerateGroupKey(eventsPerStream, true); + for (var j = 0; j < levels.Length; j++) { + var subkey = levels[j].ComputeSubkey(groupKeyComplete); + groupKeys[j] = subkey; + } + _aggregationService.ApplyEnter(eventsPerStream, groupKeys, _agentInstanceContext); + } + + ArrayDeque deque = ResultSetProcessorUtil.EnumeratorToDeque(ObtainEnumerator(parent)); + _aggregationService.ClearResults(_agentInstanceContext); + return deque.GetEnumerator(); + } + + private static readonly IList EMPTY_BEAN_ARRAY = new EventBean[0]; + + private IEnumerator ObtainEnumerator(Viewable parent) + { + ResetEventPerGroupBuf(); + EventBean[] events = EPAssertionUtil.EnumeratorToArray(parent.GetEnumerator()); + GenerateGroupKeysView(events, _eventPerGroupBuf, true); + var output = GenerateOutputEventsView(_eventPerGroupBuf, true, true) ?? EMPTY_BEAN_ARRAY; + return output.GetEnumerator(); + } + + public IEnumerator GetEnumerator(ISet> joinSet) + { + ResetEventPerGroupJoinBuf(); + GenerateGroupKeysJoin(joinSet, _eventPerGroupJoinBuf, true); + var output = GenerateOutputEventsJoin(_eventPerGroupJoinBuf, true, true) ?? EMPTY_BEAN_ARRAY; + return output.GetEnumerator(); + } + + public void Clear() + { + _aggregationService.ClearResults(_agentInstanceContext); + } + + public UniformPair ProcessOutputLimitedJoin(IList>>> joinEventsSet, bool generateSynthetic, OutputLimitLimitType outputLimitLimitType) + { + if (outputLimitLimitType == OutputLimitLimitType.DEFAULT) { + return HandleOutputLimitDefaultJoin(joinEventsSet, generateSynthetic); + } + else if (outputLimitLimitType == OutputLimitLimitType.ALL) { + return HandleOutputLimitAllJoin(joinEventsSet, generateSynthetic); + } + else if (outputLimitLimitType == OutputLimitLimitType.FIRST) { + return HandleOutputLimitFirstJoin(joinEventsSet, generateSynthetic); + } + // (outputLimitLimitType == OutputLimitLimitType.LAST) { + return HandleOutputLimitLastJoin(joinEventsSet, generateSynthetic); + } + + public UniformPair ProcessOutputLimitedView(IList> viewEventsList, bool generateSynthetic, OutputLimitLimitType outputLimitLimitType) + { + if (outputLimitLimitType == OutputLimitLimitType.DEFAULT) { + return HandleOutputLimitDefaultView(viewEventsList, generateSynthetic); + } + else if (outputLimitLimitType == OutputLimitLimitType.ALL) { + return HandleOutputLimitAllView(viewEventsList, generateSynthetic); + } + else if (outputLimitLimitType == OutputLimitLimitType.FIRST) { + return HandleOutputLimitFirstView(viewEventsList, generateSynthetic); + } + // (outputLimitLimitType == OutputLimitLimitType.LAST) { + return HandleOutputLimitLastView(viewEventsList, generateSynthetic); + } + + public void AcceptHelperVisitor(ResultSetProcessorOutputHelperVisitor visitor) + { + if (_outputLastHelper != null) { + visitor.Visit(_outputLastHelper); + } + if (_outputAllHelper != null) { + visitor.Visit(_outputAllHelper); + } + if (_outputFirstHelpers != null) { + foreach (ResultSetProcessorGroupedOutputFirstHelper helper in _outputFirstHelpers) { + visitor.Visit(helper); + } + } + } + + private UniformPair HandleOutputLimitFirstView(IList> viewEventsList, bool generateSynthetic) { + + foreach (var aGroupRepsView in _groupRepsPerLevelBuf) { + aGroupRepsView.Clear(); + } + + _rstreamEventSortArrayBuf.Reset(); + + int oldEventCount; + if (_prototype.PerLevelExpression.OptionalHavingNodes == null) { + oldEventCount = HandleOutputLimitFirstViewNoHaving(viewEventsList, generateSynthetic, _rstreamEventSortArrayBuf.EventsPerLevel, _rstreamEventSortArrayBuf.SortKeyPerLevel); + } + else { + oldEventCount = HandleOutputLimitFirstViewHaving(viewEventsList, generateSynthetic, _rstreamEventSortArrayBuf.EventsPerLevel, _rstreamEventSortArrayBuf.SortKeyPerLevel); + } + + return GenerateAndSort(_groupRepsPerLevelBuf, generateSynthetic, oldEventCount); + } + + private UniformPair HandleOutputLimitFirstJoin(IList>>> joinEventsSet, bool generateSynthetic) { + + foreach (var aGroupRepsView in _groupRepsPerLevelBuf) { + aGroupRepsView.Clear(); + } + + _rstreamEventSortArrayBuf.Reset(); + + int oldEventCount; + if (_prototype.PerLevelExpression.OptionalHavingNodes == null) { + oldEventCount = HandleOutputLimitFirstJoinNoHaving(joinEventsSet, generateSynthetic, _rstreamEventSortArrayBuf.EventsPerLevel, _rstreamEventSortArrayBuf.SortKeyPerLevel); + } + else { + oldEventCount = HandleOutputLimitFirstJoinHaving(joinEventsSet, generateSynthetic, _rstreamEventSortArrayBuf.EventsPerLevel, _rstreamEventSortArrayBuf.SortKeyPerLevel); + } + + return GenerateAndSort(_groupRepsPerLevelBuf, generateSynthetic, oldEventCount); + } + + private int HandleOutputLimitFirstViewHaving( + IList> viewEventsList, + bool generateSynthetic, + IList[] oldEventsPerLevel, + IList[] oldEventsSortKeyPerLevel) + { + var oldEventCount = 0; + + var havingPerLevel = _prototype.PerLevelExpression.OptionalHavingNodes; + + foreach (var pair in viewEventsList) + { + var newData = pair.First; + var oldData = pair.Second; + + // apply to aggregates + var groupKeysPerLevel = new object[_prototype.GroupByRollupDesc.Levels.Length]; + EventBean[] eventsPerStream; + if (newData != null) { + foreach (var aNewData in newData) { + eventsPerStream = new EventBean[] {aNewData}; + var groupKeyComplete = GenerateGroupKey(eventsPerStream, true); + foreach (var level in _prototype.GroupByRollupDesc.Levels) { + var groupKey = level.ComputeSubkey(groupKeyComplete); + groupKeysPerLevel[level.LevelNumber] = groupKey; + } + _aggregationService.ApplyEnter(eventsPerStream, groupKeysPerLevel, _agentInstanceContext); + } + } + if (oldData != null) { + foreach (var anOldData in oldData) { + eventsPerStream = new EventBean[] {anOldData}; + var groupKeyComplete = GenerateGroupKey(eventsPerStream, false); + foreach (var level in _prototype.GroupByRollupDesc.Levels) { + var groupKey = level.ComputeSubkey(groupKeyComplete); + groupKeysPerLevel[level.LevelNumber] = groupKey; + } + _aggregationService.ApplyLeave(eventsPerStream, groupKeysPerLevel, _agentInstanceContext); + } + } + + if (newData != null) { + foreach (var aNewData in newData) { + eventsPerStream = new EventBean[] {aNewData}; + var evaluateParams = new EvaluateParams(eventsPerStream, true, _agentInstanceContext); + var groupKeyComplete = GenerateGroupKey(eventsPerStream, true); + foreach (var level in _prototype.GroupByRollupDesc.Levels) { + var groupKey = level.ComputeSubkey(groupKeyComplete); + + _aggregationService.SetCurrentAccess(groupKey, _agentInstanceContext.AgentInstanceId, level); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QHavingClauseNonJoin(aNewData);} + var result = havingPerLevel[level.LevelNumber].Evaluate(evaluateParams); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AHavingClauseNonJoin(result.AsBoolean());} + if ((result == null) || (false.Equals(result))) { + continue; + } + + var outputStateGroup = _outputFirstHelpers[level.LevelNumber].GetOrAllocate(groupKey, _agentInstanceContext, _prototype.OptionalOutputFirstConditionFactory); + var pass = outputStateGroup.UpdateOutputCondition(1, 0); + if (pass) { + if (_groupRepsPerLevelBuf[level.LevelNumber].Push(groupKey, eventsPerStream) == null) + { + if (_prototype.IsSelectRStream) { + GenerateOutputBatched(false, groupKey, level, eventsPerStream, true, generateSynthetic, oldEventsPerLevel, oldEventsSortKeyPerLevel); + oldEventCount++; + } + } + } + } + } + } + if (oldData != null) { + foreach (var anOldData in oldData) { + eventsPerStream = new EventBean[] {anOldData}; + var evaluateParams = new EvaluateParams(eventsPerStream, false, _agentInstanceContext); + var groupKeyComplete = GenerateGroupKey(eventsPerStream, false); + foreach (var level in _prototype.GroupByRollupDesc.Levels) { + var groupKey = level.ComputeSubkey(groupKeyComplete); + + _aggregationService.SetCurrentAccess(groupKey, _agentInstanceContext.AgentInstanceId, level); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QHavingClauseNonJoin(anOldData);} + var result = havingPerLevel[level.LevelNumber].Evaluate(evaluateParams); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AHavingClauseNonJoin(result.AsBoxedBoolean());} + if ((result == null) || (false.Equals(result))) { + continue; + } + + var outputStateGroup = _outputFirstHelpers[level.LevelNumber].GetOrAllocate(groupKey, _agentInstanceContext, _prototype.OptionalOutputFirstConditionFactory); + var pass = outputStateGroup.UpdateOutputCondition(1, 0); + if (pass) { + if (_groupRepsPerLevelBuf[level.LevelNumber].Push(groupKey, eventsPerStream) == null) + { + if (_prototype.IsSelectRStream) { + GenerateOutputBatched(false, groupKey, level, eventsPerStream, false, generateSynthetic, oldEventsPerLevel, oldEventsSortKeyPerLevel); + oldEventCount++; + } + } + } + } + } + } + } + return oldEventCount; + } + + private int HandleOutputLimitFirstJoinNoHaving( + IList>>> joinEventSet, + bool generateSynthetic, + IList[] oldEventsPerLevel, + IList[] oldEventsSortKeyPerLevel) + { + var oldEventCount = 0; + + // outer loop is the events + foreach (var pair in joinEventSet) + { + var newData = pair.First; + var oldData = pair.Second; + + // apply to aggregates + var groupKeysPerLevel = new object[_prototype.GroupByRollupDesc.Levels.Length]; + if (newData != null) { + foreach (var aNewData in newData) { + var groupKeyComplete = GenerateGroupKey(aNewData.Array, true); + foreach (var level in _prototype.GroupByRollupDesc.Levels) { + var groupKey = level.ComputeSubkey(groupKeyComplete); + groupKeysPerLevel[level.LevelNumber] = groupKey; + + var outputStateGroup = _outputFirstHelpers[level.LevelNumber].GetOrAllocate(groupKey, _agentInstanceContext, _prototype.OptionalOutputFirstConditionFactory); + var pass = outputStateGroup.UpdateOutputCondition(1, 0); + if (pass) { + if (_groupRepsPerLevelBuf[level.LevelNumber].Push(groupKey, aNewData.Array) == null) + { + if (_prototype.IsSelectRStream) { + GenerateOutputBatched(false, groupKey, level, aNewData.Array, true, generateSynthetic, oldEventsPerLevel, oldEventsSortKeyPerLevel); + oldEventCount++; + } + } + } + } + _aggregationService.ApplyEnter(aNewData.Array, groupKeysPerLevel, _agentInstanceContext); + } + } + if (oldData != null) { + foreach (var anOldData in oldData) { + var groupKeyComplete = GenerateGroupKey(anOldData.Array, false); + foreach (var level in _prototype.GroupByRollupDesc.Levels) { + var groupKey = level.ComputeSubkey(groupKeyComplete); + groupKeysPerLevel[level.LevelNumber] = groupKey; + + var outputStateGroup = _outputFirstHelpers[level.LevelNumber].GetOrAllocate(groupKey, _agentInstanceContext, _prototype.OptionalOutputFirstConditionFactory); + var pass = outputStateGroup.UpdateOutputCondition(1, 0); + if (pass) { + if (_groupRepsPerLevelBuf[level.LevelNumber].Push(groupKey, anOldData.Array) == null) + { + if (_prototype.IsSelectRStream) { + GenerateOutputBatched(false, groupKey, level, anOldData.Array, false, generateSynthetic, oldEventsPerLevel, oldEventsSortKeyPerLevel); + oldEventCount++; + } + } + } + } + _aggregationService.ApplyLeave(anOldData.Array, groupKeysPerLevel, _agentInstanceContext); + } + } + } + return oldEventCount; + } + + private int HandleOutputLimitFirstJoinHaving( + IList>>> joinEventSet, + bool generateSynthetic, + IList[] oldEventsPerLevel, + IList[] oldEventsSortKeyPerLevel) + { + var oldEventCount = 0; + + var havingPerLevel = _prototype.PerLevelExpression.OptionalHavingNodes; + + foreach (var pair in joinEventSet) + { + var newData = pair.First; + var oldData = pair.Second; + + // apply to aggregates + var groupKeysPerLevel = new object[_prototype.GroupByRollupDesc.Levels.Length]; + if (newData != null) { + foreach (var aNewData in newData) { + var groupKeyComplete = GenerateGroupKey(aNewData.Array, true); + foreach (var level in _prototype.GroupByRollupDesc.Levels) { + var groupKey = level.ComputeSubkey(groupKeyComplete); + groupKeysPerLevel[level.LevelNumber] = groupKey; + } + _aggregationService.ApplyEnter(aNewData.Array, groupKeysPerLevel, _agentInstanceContext); + } + } + if (oldData != null) { + foreach (var anOldData in oldData) { + var groupKeyComplete = GenerateGroupKey(anOldData.Array, false); + foreach (var level in _prototype.GroupByRollupDesc.Levels) { + var groupKey = level.ComputeSubkey(groupKeyComplete); + groupKeysPerLevel[level.LevelNumber] = groupKey; + } + _aggregationService.ApplyLeave(anOldData.Array, groupKeysPerLevel, _agentInstanceContext); + } + } + + if (newData != null) { + foreach (var aNewData in newData) { + var groupKeyComplete = GenerateGroupKey(aNewData.Array, true); + var evaluateParams = new EvaluateParams(aNewData.Array, true, _agentInstanceContext); + foreach (var level in _prototype.GroupByRollupDesc.Levels) + { + var groupKey = level.ComputeSubkey(groupKeyComplete); + + _aggregationService.SetCurrentAccess(groupKey, _agentInstanceContext.AgentInstanceId, level); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QHavingClauseJoin(aNewData.Array);} + var result = havingPerLevel[level.LevelNumber].Evaluate(evaluateParams); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AHavingClauseJoin(result.AsBoolean());} + if ((result == null) || (false.Equals(result))) { + continue; + } + + var outputStateGroup = _outputFirstHelpers[level.LevelNumber].GetOrAllocate(groupKey, _agentInstanceContext, _prototype.OptionalOutputFirstConditionFactory); + var pass = outputStateGroup.UpdateOutputCondition(1, 0); + if (pass) { + if (_groupRepsPerLevelBuf[level.LevelNumber].Push(groupKey, aNewData.Array) == null) + { + if (_prototype.IsSelectRStream) { + GenerateOutputBatched(false, groupKey, level, aNewData.Array, true, generateSynthetic, oldEventsPerLevel, oldEventsSortKeyPerLevel); + oldEventCount++; + } + } + } + } + } + } + if (oldData != null) { + foreach (var anOldData in oldData) { + var groupKeyComplete = GenerateGroupKey(anOldData.Array, false); + var evaluateParams = new EvaluateParams(anOldData.Array, false, _agentInstanceContext); + foreach (var level in _prototype.GroupByRollupDesc.Levels) + { + var groupKey = level.ComputeSubkey(groupKeyComplete); + + _aggregationService.SetCurrentAccess(groupKey, _agentInstanceContext.AgentInstanceId, level); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QHavingClauseJoin(anOldData.Array);} + var result = havingPerLevel[level.LevelNumber].Evaluate(evaluateParams); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AHavingClauseJoin(result.AsBoxedBoolean());} + if ((result == null) || (false.Equals(result))) { + continue; + } + + var outputStateGroup = _outputFirstHelpers[level.LevelNumber].GetOrAllocate(groupKey, _agentInstanceContext, _prototype.OptionalOutputFirstConditionFactory); + var pass = outputStateGroup.UpdateOutputCondition(1, 0); + if (pass) { + if (_groupRepsPerLevelBuf[level.LevelNumber].Push(groupKey, anOldData.Array) == null) + { + if (_prototype.IsSelectRStream) { + GenerateOutputBatched(false, groupKey, level, anOldData.Array, false, generateSynthetic, oldEventsPerLevel, oldEventsSortKeyPerLevel); + oldEventCount++; + } + } + } + } + } + } + } + return oldEventCount; + } + + private int HandleOutputLimitFirstViewNoHaving( + IList> viewEventsList, + bool generateSynthetic, + IList[] oldEventsPerLevel, + IList[] oldEventsSortKeyPerLevel) + { + var oldEventCount = 0; + + // outer loop is the events + foreach (var pair in viewEventsList) + { + var newData = pair.First; + var oldData = pair.Second; + + // apply to aggregates + var groupKeysPerLevel = new object[_prototype.GroupByRollupDesc.Levels.Length]; + EventBean[] eventsPerStream; + if (newData != null) { + foreach (var aNewData in newData) { + eventsPerStream = new EventBean[] {aNewData}; + var groupKeyComplete = GenerateGroupKey(eventsPerStream, true); + foreach (var level in _prototype.GroupByRollupDesc.Levels) { + var groupKey = level.ComputeSubkey(groupKeyComplete); + groupKeysPerLevel[level.LevelNumber] = groupKey; + + var outputStateGroup = _outputFirstHelpers[level.LevelNumber].GetOrAllocate(groupKey, _agentInstanceContext, _prototype.OptionalOutputFirstConditionFactory); + var pass = outputStateGroup.UpdateOutputCondition(1, 0); + if (pass) { + if (_groupRepsPerLevelBuf[level.LevelNumber].Push(groupKey, eventsPerStream) == null) + { + if (_prototype.IsSelectRStream) { + GenerateOutputBatched(false, groupKey, level, eventsPerStream, true, generateSynthetic, oldEventsPerLevel, oldEventsSortKeyPerLevel); + oldEventCount++; + } + } + } + } + _aggregationService.ApplyEnter(eventsPerStream, groupKeysPerLevel, _agentInstanceContext); + } + } + if (oldData != null) { + foreach (var anOldData in oldData) { + eventsPerStream = new EventBean[] {anOldData}; + var groupKeyComplete = GenerateGroupKey(eventsPerStream, false); + foreach (var level in _prototype.GroupByRollupDesc.Levels) { + var groupKey = level.ComputeSubkey(groupKeyComplete); + groupKeysPerLevel[level.LevelNumber] = groupKey; + + var outputStateGroup = _outputFirstHelpers[level.LevelNumber].GetOrAllocate(groupKey, _agentInstanceContext, _prototype.OptionalOutputFirstConditionFactory); + var pass = outputStateGroup.UpdateOutputCondition(1, 0); + if (pass) { + if (_groupRepsPerLevelBuf[level.LevelNumber].Push(groupKey, eventsPerStream) == null) + { + if (_prototype.IsSelectRStream) { + GenerateOutputBatched(false, groupKey, level, eventsPerStream, false, generateSynthetic, oldEventsPerLevel, oldEventsSortKeyPerLevel); + oldEventCount++; + } + } + } + } + _aggregationService.ApplyLeave(eventsPerStream, groupKeysPerLevel, _agentInstanceContext); + } + } + } + return oldEventCount; + } + + private UniformPair HandleOutputLimitDefaultView( + IList> viewEventsList, + bool generateSynthetic) + { + IList newEvents = new List(); + IList newEventsSortKey = null; + if (_orderByProcessor != null) { + newEventsSortKey = new List(); + } + + IList oldEvents = null; + IList oldEventsSortKey = null; + if (_prototype.IsSelectRStream) { + oldEvents = new List(); + if (_orderByProcessor != null) { + oldEventsSortKey = new List(); + } + } + + foreach (var pair in viewEventsList) { + var newData = pair.First; + var oldData = pair.Second; + + ResetEventPerGroupBuf(); + var newDataMultiKey = GenerateGroupKeysView(newData, _eventPerGroupBuf, true); + var oldDataMultiKey = GenerateGroupKeysView(oldData, _eventPerGroupBuf, false); + + if (_prototype.IsSelectRStream) { + GenerateOutputBatchedCollectNonJoin(_eventPerGroupBuf, false, generateSynthetic, oldEvents, oldEventsSortKey); + } + + // update aggregates + var eventsPerStream = new EventBean[1]; + if (newData != null) { + for (var i = 0; i < newData.Length; i++) { + eventsPerStream[0] = newData[i]; + _aggregationService.ApplyEnter(eventsPerStream, newDataMultiKey[i], _agentInstanceContext); + } + } + if (oldData != null) { + for (var i = 0; i < oldData.Length; i++) { + eventsPerStream[0] = oldData[i]; + _aggregationService.ApplyLeave(eventsPerStream, oldDataMultiKey[i], _agentInstanceContext); + } + } + + GenerateOutputBatchedCollectNonJoin(_eventPerGroupBuf, true, generateSynthetic, newEvents, newEventsSortKey); + } + + return ConvertToArrayMaySort(newEvents, newEventsSortKey, oldEvents, oldEventsSortKey); + } + + private UniformPair HandleOutputLimitDefaultJoin( + IList>>> viewEventsList, + bool generateSynthetic) + { + IList newEvents = new List(); + IList newEventsSortKey = null; + if (_orderByProcessor != null) { + newEventsSortKey = new List(); + } + + IList oldEvents = null; + IList oldEventsSortKey = null; + if (_prototype.IsSelectRStream) { + oldEvents = new List(); + if (_orderByProcessor != null) { + oldEventsSortKey = new List(); + } + } + + foreach (var pair in viewEventsList) { + var newData = pair.First; + var oldData = pair.Second; + + ResetEventPerGroupJoinBuf(); + var newDataMultiKey = GenerateGroupKeysJoin(newData, _eventPerGroupJoinBuf, true); + var oldDataMultiKey = GenerateGroupKeysJoin(oldData, _eventPerGroupJoinBuf, false); + + if (_prototype.IsSelectRStream) { + GenerateOutputBatchedCollectJoin(_eventPerGroupJoinBuf, false, generateSynthetic, oldEvents, oldEventsSortKey); + } + + // update aggregates + if (newData != null) { + var count = 0; + foreach (var newEvent in newData) { + _aggregationService.ApplyEnter(newEvent.Array, newDataMultiKey[count++], _agentInstanceContext); + } + } + if (oldData != null) { + var count = 0; + foreach (var oldEvent in oldData) { + _aggregationService.ApplyLeave(oldEvent.Array, oldDataMultiKey[count++], _agentInstanceContext); + } + } + + GenerateOutputBatchedCollectJoin(_eventPerGroupJoinBuf, true, generateSynthetic, newEvents, newEventsSortKey); + } + + return ConvertToArrayMaySort(newEvents, newEventsSortKey, oldEvents, oldEventsSortKey); + } + + public bool HasAggregation + { + get { return true; } + } + + public void Removed(object key) { + throw new UnsupportedOperationException(); + } + + public object GenerateGroupKey(EventBean[] eventsPerStream, bool isNewData) + { + var evaluateParams = new EvaluateParams(eventsPerStream, isNewData, _agentInstanceContext); + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().QResultSetProcessComputeGroupKeys(isNewData, _prototype.GroupKeyNodeExpressions, eventsPerStream); + object keyObject; + if (_prototype.GroupKeyNode != null) { + keyObject = _prototype.GroupKeyNode.Evaluate(evaluateParams); + } + else { + var evals = _prototype.GroupKeyNodes; + var keys = new object[evals.Length]; + for (var i = 0; i < evals.Length; i++) { + keys[i] = evals[i].Evaluate(evaluateParams); + } + keyObject = new MultiKeyUntyped(keys); + } + + InstrumentationHelper.Get().AResultSetProcessComputeGroupKeys(isNewData, keyObject); + return keyObject; + } + + if (_prototype.GroupKeyNode != null) { + return _prototype.GroupKeyNode.Evaluate(evaluateParams); + } + else { + var evals = _prototype.GroupKeyNodes; + var keys = new object[evals.Length]; + for (var i = 0; i < evals.Length; i++) { + keys[i] = evals[i].Evaluate(evaluateParams); + } + return new MultiKeyUntyped(keys); + } + } + + private void GenerateOutputBatched(bool join, object mk, AggregationGroupByRollupLevel level, EventBean[] eventsPerStream, bool isNewData, bool isSynthesize, IList[] resultEvents, IList[] optSortKeys) { + var resultList = resultEvents[level.LevelNumber]; + var sortKeys = optSortKeys == null ? null : optSortKeys[level.LevelNumber]; + GenerateOutputBatched(join, mk, level, eventsPerStream, isNewData, isSynthesize, resultList, sortKeys); + } + + public void GenerateOutputBatched(bool join, object mk, AggregationGroupByRollupLevel level, EventBean[] eventsPerStream, bool isNewData, bool isSynthesize, IList resultEvents, IList optSortKeys) { + _aggregationService.SetCurrentAccess(mk, _agentInstanceContext.AgentInstanceId, level); + + if (_prototype.PerLevelExpression.OptionalHavingNodes != null) + { + if (InstrumentationHelper.ENABLED) { if (!join) InstrumentationHelper.Get().QHavingClauseNonJoin(eventsPerStream[0]); else InstrumentationHelper.Get().QHavingClauseJoin(eventsPerStream);} + var evaluateParams = new EvaluateParams(eventsPerStream, isNewData, _agentInstanceContext); + var result = _prototype.PerLevelExpression.OptionalHavingNodes[level.LevelNumber].Evaluate(evaluateParams); + if (InstrumentationHelper.ENABLED) { if (!join) InstrumentationHelper.Get().AHavingClauseNonJoin(result.AsBoolean()); else InstrumentationHelper.Get().AHavingClauseJoin(result.AsBoolean()); } + if ((result == null) || (false.Equals(result))) { + return; + } + } + + resultEvents.Add(_prototype.PerLevelExpression.SelectExprProcessor[level.LevelNumber].Process(eventsPerStream, isNewData, isSynthesize, _agentInstanceContext)); + + if (_prototype.IsSorting) { + optSortKeys.Add(_orderByProcessor.GetSortKey(eventsPerStream, isNewData, _agentInstanceContext, _prototype.PerLevelExpression.OptionalOrderByElements[level.LevelNumber])); + } + } + + public void GenerateOutputBatchedMapUnsorted(bool join, object mk, AggregationGroupByRollupLevel level, EventBean[] eventsPerStream, bool isNewData, bool isSynthesize, IDictionary resultEvents) { + _aggregationService.SetCurrentAccess(mk, _agentInstanceContext.AgentInstanceId, level); + + if (_prototype.PerLevelExpression.OptionalHavingNodes != null) + { + if (InstrumentationHelper.ENABLED) { if (!join) InstrumentationHelper.Get().QHavingClauseNonJoin(eventsPerStream[0]); else InstrumentationHelper.Get().QHavingClauseJoin(eventsPerStream);} + var evaluateParams = new EvaluateParams(eventsPerStream, isNewData, _agentInstanceContext); + var result = _prototype.PerLevelExpression.OptionalHavingNodes[level.LevelNumber].Evaluate(evaluateParams); + if (InstrumentationHelper.ENABLED) { if (!join) InstrumentationHelper.Get().AHavingClauseNonJoin(result.AsBoolean()); else InstrumentationHelper.Get().AHavingClauseJoin(result.AsBoolean()); } + if ((result == null) || (false.Equals(result))) { + return; + } + } + + resultEvents.Put(mk, _prototype.PerLevelExpression.SelectExprProcessor[level.LevelNumber].Process(eventsPerStream, isNewData, isSynthesize, _agentInstanceContext)); + } + + private UniformPair HandleOutputLimitLastView(IList> viewEventsList, bool generateSynthetic) + { + var oldEventCount = 0; + if (_prototype.IsSelectRStream) { + _rstreamEventSortArrayBuf.Reset(); + } + + foreach (var aGroupRepsView in _groupRepsPerLevelBuf) { + aGroupRepsView.Clear(); + } + + // outer loop is the events + foreach (var pair in viewEventsList) + { + var newData = pair.First; + var oldData = pair.Second; + + // apply to aggregates + var groupKeysPerLevel = new object[_prototype.GroupByRollupDesc.Levels.Length]; + EventBean[] eventsPerStream; + if (newData != null) { + foreach (var aNewData in newData) { + eventsPerStream = new EventBean[] {aNewData}; + var groupKeyComplete = GenerateGroupKey(eventsPerStream, true); + foreach (var level in _prototype.GroupByRollupDesc.Levels) { + var groupKey = level.ComputeSubkey(groupKeyComplete); + groupKeysPerLevel[level.LevelNumber] = groupKey; + if (_groupRepsPerLevelBuf[level.LevelNumber].Push(groupKey, eventsPerStream) == null) + { + if (_prototype.IsSelectRStream) { + GenerateOutputBatched(false, groupKey, level, eventsPerStream, true, generateSynthetic, _rstreamEventSortArrayBuf.EventsPerLevel, _rstreamEventSortArrayBuf.SortKeyPerLevel); + oldEventCount++; + } + } + } + _aggregationService.ApplyEnter(eventsPerStream, groupKeysPerLevel, _agentInstanceContext); + } + } + if (oldData != null) { + foreach (var anOldData in oldData) { + eventsPerStream = new EventBean[] {anOldData}; + var groupKeyComplete = GenerateGroupKey(eventsPerStream, false); + foreach (var level in _prototype.GroupByRollupDesc.Levels) { + var groupKey = level.ComputeSubkey(groupKeyComplete); + groupKeysPerLevel[level.LevelNumber] = groupKey; + if (_groupRepsPerLevelBuf[level.LevelNumber].Push(groupKey, eventsPerStream) == null) + { + if (_prototype.IsSelectRStream) { + GenerateOutputBatched(true, groupKey, level, eventsPerStream, true, generateSynthetic, _rstreamEventSortArrayBuf.EventsPerLevel, _rstreamEventSortArrayBuf.SortKeyPerLevel); + oldEventCount++; + } + } + } + _aggregationService.ApplyLeave(eventsPerStream, groupKeysPerLevel, _agentInstanceContext); + } + } + } + + return GenerateAndSort(_groupRepsPerLevelBuf, generateSynthetic, oldEventCount); + } + + private UniformPair HandleOutputLimitLastJoin(IList>>> viewEventsList, bool generateSynthetic) + { + var oldEventCount = 0; + if (_prototype.IsSelectRStream) { + _rstreamEventSortArrayBuf.Reset(); + } + + foreach (var aGroupRepsView in _groupRepsPerLevelBuf) { + aGroupRepsView.Clear(); + } + + // outer loop is the events + foreach (var pair in viewEventsList) + { + var newData = pair.First; + var oldData = pair.Second; + + // apply to aggregates + var groupKeysPerLevel = new object[_prototype.GroupByRollupDesc.Levels.Length]; + if (newData != null) { + foreach (var aNewData in newData) { + var groupKeyComplete = GenerateGroupKey(aNewData.Array, true); + foreach (var level in _prototype.GroupByRollupDesc.Levels) { + var groupKey = level.ComputeSubkey(groupKeyComplete); + groupKeysPerLevel[level.LevelNumber] = groupKey; + if (_groupRepsPerLevelBuf[level.LevelNumber].Push(groupKey, aNewData.Array) == null) + { + if (_prototype.IsSelectRStream) { + GenerateOutputBatched(false, groupKey, level, aNewData.Array, true, generateSynthetic, _rstreamEventSortArrayBuf.EventsPerLevel, _rstreamEventSortArrayBuf.SortKeyPerLevel); + oldEventCount++; + } + } + } + _aggregationService.ApplyEnter(aNewData.Array, groupKeysPerLevel, _agentInstanceContext); + } + } + if (oldData != null) { + foreach (var anOldData in oldData) { + var groupKeyComplete = GenerateGroupKey(anOldData.Array, false); + foreach (var level in _prototype.GroupByRollupDesc.Levels) { + var groupKey = level.ComputeSubkey(groupKeyComplete); + groupKeysPerLevel[level.LevelNumber] = groupKey; + if (_groupRepsPerLevelBuf[level.LevelNumber].Push(groupKey, anOldData.Array) == null) + { + if (_prototype.IsSelectRStream) { + GenerateOutputBatched(true, groupKey, level, anOldData.Array, true, generateSynthetic, _rstreamEventSortArrayBuf.EventsPerLevel, _rstreamEventSortArrayBuf.SortKeyPerLevel); + oldEventCount++; + } + } + } + _aggregationService.ApplyLeave(anOldData.Array, groupKeysPerLevel, _agentInstanceContext); + } + } + } + + return GenerateAndSort(_groupRepsPerLevelBuf, generateSynthetic, oldEventCount); + } + + private UniformPair HandleOutputLimitAllView(IList> viewEventsList, bool generateSynthetic) { + + var oldEventCount = 0; + if (_prototype.IsSelectRStream) { + _rstreamEventSortArrayBuf.Reset(); + + foreach (var level in _prototype.GroupByRollupDesc.Levels) { + var groupGenerators = _groupRepsPerLevelBuf[level.LevelNumber]; + foreach (var entry in groupGenerators) { + GenerateOutputBatched(false, entry.Key, level, entry.Value, false, generateSynthetic, _rstreamEventSortArrayBuf.EventsPerLevel, _rstreamEventSortArrayBuf.SortKeyPerLevel); + oldEventCount++; + } + } + } + + // outer loop is the events + foreach (var pair in viewEventsList) + { + var newData = pair.First; + var oldData = pair.Second; + + // apply to aggregates + var groupKeysPerLevel = new object[_prototype.GroupByRollupDesc.Levels.Length]; + if (newData != null) { + foreach (var aNewData in newData) { + var eventsPerStream = new EventBean[] {aNewData}; + var groupKeyComplete = GenerateGroupKey(eventsPerStream, true); + foreach (var level in _prototype.GroupByRollupDesc.Levels) { + var groupKey = level.ComputeSubkey(groupKeyComplete); + groupKeysPerLevel[level.LevelNumber] = groupKey; + var existing = _groupRepsPerLevelBuf[level.LevelNumber].Push(groupKey, eventsPerStream); + + if (existing == null && _prototype.IsSelectRStream) { + GenerateOutputBatched(false, groupKey, level, eventsPerStream, true, generateSynthetic, _rstreamEventSortArrayBuf.EventsPerLevel, _rstreamEventSortArrayBuf.SortKeyPerLevel); + oldEventCount++; + } + } + _aggregationService.ApplyEnter(eventsPerStream, groupKeysPerLevel, _agentInstanceContext); + } + } + if (oldData != null) { + foreach (var anOldData in oldData) { + var eventsPerStream = new EventBean[] {anOldData}; + var groupKeyComplete = GenerateGroupKey(eventsPerStream, false); + foreach (var level in _prototype.GroupByRollupDesc.Levels) { + var groupKey = level.ComputeSubkey(groupKeyComplete); + groupKeysPerLevel[level.LevelNumber] = groupKey; + var existing = _groupRepsPerLevelBuf[level.LevelNumber].Push(groupKey, eventsPerStream); + + if (existing == null && _prototype.IsSelectRStream) { + GenerateOutputBatched(false, groupKey, level, eventsPerStream, false, generateSynthetic, _rstreamEventSortArrayBuf.EventsPerLevel, _rstreamEventSortArrayBuf.SortKeyPerLevel); + oldEventCount++; + } + } + _aggregationService.ApplyLeave(eventsPerStream, groupKeysPerLevel, _agentInstanceContext); + } + } + } + + return GenerateAndSort(_groupRepsPerLevelBuf, generateSynthetic, oldEventCount); + } + + private UniformPair HandleOutputLimitAllJoin(IList>>> joinEventsSet, bool generateSynthetic) + { + var oldEventCount = 0; + if (_prototype.IsSelectRStream) { + _rstreamEventSortArrayBuf.Reset(); + + foreach (var level in _prototype.GroupByRollupDesc.Levels) { + var groupGenerators = _groupRepsPerLevelBuf[level.LevelNumber]; + foreach (var entry in groupGenerators) { + GenerateOutputBatched(false, entry.Key, level, entry.Value, false, generateSynthetic, _rstreamEventSortArrayBuf.EventsPerLevel, _rstreamEventSortArrayBuf.SortKeyPerLevel); + oldEventCount++; + } + } + } + + // outer loop is the events + foreach (var pair in joinEventsSet) + { + var newData = pair.First; + var oldData = pair.Second; + + // apply to aggregates + var groupKeysPerLevel = new object[_prototype.GroupByRollupDesc.Levels.Length]; + if (newData != null) { + foreach (var aNewData in newData) { + var groupKeyComplete = GenerateGroupKey(aNewData.Array, true); + foreach (var level in _prototype.GroupByRollupDesc.Levels) { + var groupKey = level.ComputeSubkey(groupKeyComplete); + groupKeysPerLevel[level.LevelNumber] = groupKey; + var existing = _groupRepsPerLevelBuf[level.LevelNumber].Push(groupKey, aNewData.Array); + + if (existing == null && _prototype.IsSelectRStream) { + GenerateOutputBatched(false, groupKey, level, aNewData.Array, true, generateSynthetic, _rstreamEventSortArrayBuf.EventsPerLevel, _rstreamEventSortArrayBuf.SortKeyPerLevel); + oldEventCount++; + } + } + _aggregationService.ApplyEnter(aNewData.Array, groupKeysPerLevel, _agentInstanceContext); + } + } + if (oldData != null) { + foreach (var anOldData in oldData) { + var groupKeyComplete = GenerateGroupKey(anOldData.Array, false); + foreach (var level in _prototype.GroupByRollupDesc.Levels) { + var groupKey = level.ComputeSubkey(groupKeyComplete); + groupKeysPerLevel[level.LevelNumber] = groupKey; + var existing = _groupRepsPerLevelBuf[level.LevelNumber].Push(groupKey, anOldData.Array); + + if (existing == null && _prototype.IsSelectRStream) { + GenerateOutputBatched(false, groupKey, level, anOldData.Array, false, generateSynthetic, _rstreamEventSortArrayBuf.EventsPerLevel, _rstreamEventSortArrayBuf.SortKeyPerLevel); + oldEventCount++; + } + } + _aggregationService.ApplyLeave(anOldData.Array, groupKeysPerLevel, _agentInstanceContext); + } + } + } + + return GenerateAndSort(_groupRepsPerLevelBuf, generateSynthetic, oldEventCount); + } + + private void GenerateOutputBatchedCollectNonJoin(IDictionary[] eventPairs, bool isNewData, bool generateSynthetic, IList events, IList sortKey) + { + var levels = _prototype.GroupByRollupDesc.Levels; + var eventsPerStream = new EventBean[1]; + + foreach (var level in levels) { + var eventsForLevel = eventPairs[level.LevelNumber]; + foreach (var pair in eventsForLevel) { + eventsPerStream[0] = pair.Value; + GenerateOutputBatched(false, pair.Key, level, eventsPerStream, isNewData, generateSynthetic, events, sortKey); + } + } + } + + private void GenerateOutputBatchedCollectJoin(IDictionary[] eventPairs, bool isNewData, bool generateSynthetic, IList events, IList sortKey) + { + var levels = _prototype.GroupByRollupDesc.Levels; + + foreach (var level in levels) { + var eventsForLevel = eventPairs[level.LevelNumber]; + foreach (var pair in eventsForLevel) { + GenerateOutputBatched(false, pair.Key, level, pair.Value, isNewData, generateSynthetic, events, sortKey); + } + } + } + + private void ResetEventPerGroupBuf() { + foreach (var anEventPerGroupBuf in _eventPerGroupBuf) { + anEventPerGroupBuf.Clear(); + } + } + + private void ResetEventPerGroupJoinBuf() { + foreach (var anEventPerGroupBuf in _eventPerGroupJoinBuf) { + anEventPerGroupBuf.Clear(); + } + } + + private EventsAndSortKeysPair GetOldEventsSortKeys(int oldEventCount, IList[] oldEventsPerLevel, IList[] oldEventsSortKeyPerLevel) { + var oldEventsArr = new EventBean[oldEventCount]; + object[] oldEventsSortKeys = null; + if (_orderByProcessor != null) { + oldEventsSortKeys = new object[oldEventCount]; + } + var countEvents = 0; + var countSortKeys = 0; + foreach (var level in _prototype.GroupByRollupDesc.Levels) { + var events = oldEventsPerLevel[level.LevelNumber]; + foreach (var @event in events) { + oldEventsArr[countEvents++] = @event; + } + if (_orderByProcessor != null) { + var sortKeys = oldEventsSortKeyPerLevel[level.LevelNumber]; + foreach (var sortKey in sortKeys) { + oldEventsSortKeys[countSortKeys++] = sortKey; + } + } + } + return new EventsAndSortKeysPair(oldEventsArr, oldEventsSortKeys); + } + + protected object[][] GenerateGroupKeysView(EventBean[] events, IDictionary[] eventPerKey, bool isNewData) + { + if (events == null) { + return null; + } + + var result = new object[events.Length][]; + var eventsPerStream = new EventBean[1]; + + for (var i = 0; i < events.Length; i++) { + eventsPerStream[0] = events[i]; + var groupKeyComplete = GenerateGroupKey(eventsPerStream, isNewData); + var levels = _prototype.GroupByRollupDesc.Levels; + result[i] = new object[levels.Length]; + for (var j = 0; j < levels.Length; j++) { + var subkey = levels[j].ComputeSubkey(groupKeyComplete); + result[i][j] = subkey; + eventPerKey[levels[j].LevelNumber].Put(subkey, events[i]); + } + } + + return result; + } + + private object[][] GenerateGroupKeysJoin(ISet> events, IDictionary[] eventPerKey, bool isNewData) + { + if (events == null || events.IsEmpty()) { + return null; + } + + var result = new object[events.Count][]; + + var count = -1; + foreach (var eventrow in events) { + count++; + var groupKeyComplete = GenerateGroupKey(eventrow.Array, isNewData); + var levels = _prototype.GroupByRollupDesc.Levels; + result[count] = new object[levels.Length]; + for (var j = 0; j < levels.Length; j++) { + var subkey = levels[j].ComputeSubkey(groupKeyComplete); + result[count][j] = subkey; + eventPerKey[levels[j].LevelNumber].Put(subkey, eventrow.Array); + } + } + + return result; + } + + private UniformPair GenerateAndSort(IDictionary[] outputLimitGroupRepsPerLevel, bool generateSynthetic, int oldEventCount) { + // generate old events: ordered by level by default + EventBean[] oldEventsArr = null; + object[] oldEventSortKeys = null; + if (_prototype.IsSelectRStream && oldEventCount > 0) { + var pair = GetOldEventsSortKeys(oldEventCount, _rstreamEventSortArrayBuf.EventsPerLevel, _rstreamEventSortArrayBuf.SortKeyPerLevel); + oldEventsArr = pair.Events; + oldEventSortKeys = pair.SortKeys; + } + + IList newEvents = new List(); + IList newEventsSortKey = null; + if (_orderByProcessor != null) { + newEventsSortKey = new List(); + } + + foreach (var level in _prototype.GroupByRollupDesc.Levels) { + var groupGenerators = outputLimitGroupRepsPerLevel[level.LevelNumber]; + foreach (KeyValuePair entry in groupGenerators) { + GenerateOutputBatched(false, entry.Key, level, entry.Value, true, generateSynthetic, newEvents, newEventsSortKey); + } + } + + var newEventsArr = (newEvents.IsEmpty()) ? null : newEvents.ToArray(); + if (_orderByProcessor != null) { + var sortKeysNew = (newEventsSortKey.IsEmpty()) ? null : newEventsSortKey.ToArray(); + newEventsArr = _orderByProcessor.Sort(newEventsArr, sortKeysNew, _agentInstanceContext); + if (_prototype.IsSelectRStream) { + oldEventsArr = _orderByProcessor.Sort(oldEventsArr, oldEventSortKeys, _agentInstanceContext); + } + } + + if ((newEventsArr == null) && (oldEventsArr == null)) { + return null; + } + return new UniformPair(newEventsArr, oldEventsArr); + } + + public virtual void ApplyViewResult(EventBean[] newData, EventBean[] oldData) { + var eventsPerStream = new EventBean[1]; + if (newData != null) { + foreach (var aNewData in newData) { + eventsPerStream[0] = aNewData; + var keys = GenerateGroupKeysNonJoin(eventsPerStream, true); + _aggregationService.ApplyEnter(eventsPerStream, keys, _agentInstanceContext); + } + } + if (oldData != null) { + foreach (var anOldData in oldData) { + eventsPerStream[0] = anOldData; + var keys = GenerateGroupKeysNonJoin(eventsPerStream, false); + _aggregationService.ApplyLeave(eventsPerStream, keys, _agentInstanceContext); + } + } + } + + public void ApplyJoinResult(ISet> newEvents, ISet> oldEvents) { + if (newEvents != null) { + foreach (var mk in newEvents) { + var keys = GenerateGroupKeysNonJoin(mk.Array, true); + _aggregationService.ApplyEnter(mk.Array, keys, _agentInstanceContext); + } + } + if (oldEvents != null) { + foreach (var mk in oldEvents) { + var keys = GenerateGroupKeysNonJoin(mk.Array, false); + _aggregationService.ApplyLeave(mk.Array, keys, _agentInstanceContext); + } + } + } + + public void ProcessOutputLimitedLastAllNonBufferedView(EventBean[] newData, EventBean[] oldData, bool isGenerateSynthetic, bool isAll) { + if (isAll) { + _outputAllHelper.ProcessView(newData, oldData, isGenerateSynthetic); + } + else { + _outputLastHelper.ProcessView(newData, oldData, isGenerateSynthetic); + } + } + + public void ProcessOutputLimitedLastAllNonBufferedJoin(ISet> newEvents, ISet> oldEvents, bool isGenerateSynthetic, bool isAll) { + if (isAll) { + _outputAllHelper.ProcessJoin(newEvents, oldEvents, isGenerateSynthetic); + } + else { + _outputLastHelper.ProcessJoin(newEvents, oldEvents, isGenerateSynthetic); + } + } + + public UniformPair ContinueOutputLimitedLastAllNonBufferedView(bool isSynthesize, bool isAll) { + if (isAll) { + return _outputAllHelper.OutputView(isSynthesize); + } + return _outputLastHelper.OutputView(isSynthesize); + } + + public UniformPair ContinueOutputLimitedLastAllNonBufferedJoin(bool isSynthesize, bool isAll) { + if (isAll) { + return _outputAllHelper.OutputJoin(isSynthesize); + } + return _outputLastHelper.OutputJoin(isSynthesize); + } + + public virtual void Stop() { + if (_outputLastHelper != null) { + _outputLastHelper.Destroy(); + } + if (_outputFirstHelpers != null) { + foreach (var helper in _outputFirstHelpers) { + helper.Destroy(); + } + } + if (_outputAllHelper != null) { + _outputAllHelper.Destroy(); + } + } + + private UniformPair ConvertToArrayMaySort(IList newEvents, IList newEventsSortKey, IList oldEvents, IList oldEventsSortKey) { + var newEventsArr = (newEvents.IsEmpty()) ? null : newEvents.ToArray(); + EventBean[] oldEventsArr = null; + if (_prototype.IsSelectRStream) { + oldEventsArr = (oldEvents.IsEmpty()) ? null : oldEvents.ToArray(); + } + + if (_orderByProcessor != null) { + var sortKeysNew = (newEventsSortKey.IsEmpty()) ? null : newEventsSortKey.ToArray(); + newEventsArr = _orderByProcessor.Sort(newEventsArr, sortKeysNew, _agentInstanceContext); + if (_prototype.IsSelectRStream) { + var sortKeysOld = (oldEventsSortKey.IsEmpty()) ? null : oldEventsSortKey.ToArray(); + oldEventsArr = _orderByProcessor.Sort(oldEventsArr, sortKeysOld, _agentInstanceContext); + } + } + + if ((newEventsArr == null) && (oldEventsArr == null)) { + return null; + } + return new UniformPair(newEventsArr, oldEventsArr); + } + + private object[] GenerateGroupKeysNonJoin(EventBean[] eventsPerStream, bool isNewData) { + var groupKeyComplete = GenerateGroupKey(eventsPerStream, true); + var levels = _prototype.GroupByRollupDesc.Levels; + var result = new object[levels.Length]; + for (var j = 0; j < levels.Length; j++) { + var subkey = levels[j].ComputeSubkey(groupKeyComplete); + result[j] = subkey; + } + return result; + } + + internal class EventArrayAndSortKeyArray + { + internal EventArrayAndSortKeyArray(IList[] eventsPerLevel, IList[] sortKeyPerLevel) + { + EventsPerLevel = eventsPerLevel; + SortKeyPerLevel = sortKeyPerLevel; + } + + public readonly IList[] EventsPerLevel; + public readonly IList[] SortKeyPerLevel; + + public void Reset() + { + foreach (var anEventsPerLevel in EventsPerLevel) + { + anEventsPerLevel.Clear(); + } + if (SortKeyPerLevel != null) + { + foreach (var anSortKeyPerLevel in SortKeyPerLevel) + { + anSortKeyPerLevel.Clear(); + } + } + } + } + + internal class EventsAndSortKeysPair + { + public readonly EventBean[] Events; + public readonly object[] SortKeys; + + internal EventsAndSortKeysPair(EventBean[] events, object[] sortKeys) + { + Events = events; + SortKeys = sortKeys; + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowPerGroupRollupFactory.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowPerGroupRollupFactory.cs new file mode 100755 index 000000000..b317f0f88 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowPerGroupRollupFactory.cs @@ -0,0 +1,138 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.agg.rollup; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.view; + +namespace com.espertech.esper.epl.core +{ + /// + /// Result set processor _prototype for the fully-grouped case: there is a group-by and + /// all non-aggregation event properties in the select clause are listed in the group by, + /// and there are aggregation functions. + /// + public class ResultSetProcessorRowPerGroupRollupFactory : ResultSetProcessorFactory + { + private readonly bool _noDataWindowSingleSnapshot; + + /// + /// Ctor. + /// + /// The per level expression. + /// The group key node expressions. + /// list of group-by expression nodes needed for building the group-by keysAggregation functions in the having node must have been pointed to the AggregationService for evaluation. + /// true if remove stream events should be generated + /// true if unidirectional join + /// The output limit spec. + /// if set to true [is sorting]. + /// if set to true [no data window single stream]. + /// The group by rollup desc. + /// if set to true [is join]. + /// if set to true [is historical only]. + /// if set to true [iterate unbounded]. + /// The optional output first condition factory. + /// The result set processor helper factory. + /// if set to true [enable output limit opt]. + /// The number streams. + public ResultSetProcessorRowPerGroupRollupFactory( + GroupByRollupPerLevelExpression perLevelExpression, + ExprNode[] groupKeyNodeExpressions, + ExprEvaluator[] groupKeyNodes, + bool isSelectRStream, + bool isUnidirectional, + OutputLimitSpec outputLimitSpec, + bool isSorting, + bool noDataWindowSingleStream, + AggregationGroupByRollupDesc groupByRollupDesc, + bool isJoin, + bool isHistoricalOnly, + bool iterateUnbounded, + OutputConditionPolledFactory optionalOutputFirstConditionFactory, + ResultSetProcessorHelperFactory resultSetProcessorHelperFactory, + bool enableOutputLimitOpt, + int numStreams) + { + GroupKeyNodeExpressions = groupKeyNodeExpressions; + PerLevelExpression = perLevelExpression; + GroupKeyNodes = groupKeyNodes; + GroupKeyNode = groupKeyNodes.Length == 1 ? groupKeyNodes[0] : null; + IsSorting = isSorting; + IsSelectRStream = isSelectRStream; + IsUnidirectional = isUnidirectional; + OutputLimitSpec = outputLimitSpec; + _noDataWindowSingleSnapshot = iterateUnbounded || (outputLimitSpec != null && outputLimitSpec.DisplayLimit == OutputLimitLimitType.SNAPSHOT && noDataWindowSingleStream); + GroupByRollupDesc = groupByRollupDesc; + IsJoin = isJoin; + IsHistoricalOnly = isHistoricalOnly; + OptionalOutputFirstConditionFactory = optionalOutputFirstConditionFactory; + ResultSetProcessorHelperFactory = resultSetProcessorHelperFactory; + IsEnableOutputLimitOpt = enableOutputLimitOpt; + NumStreams = numStreams; + } + + public ResultSetProcessor Instantiate(OrderByProcessor orderByProcessor, AggregationService aggregationService, AgentInstanceContext agentInstanceContext) + { + if (_noDataWindowSingleSnapshot && !IsHistoricalOnly) + { + return new ResultSetProcessorRowPerGroupRollupUnbound(this, orderByProcessor, aggregationService, agentInstanceContext); + } + return new ResultSetProcessorRowPerGroupRollup(this, orderByProcessor, aggregationService, agentInstanceContext); + } + + public EventType ResultEventType + { + get { return PerLevelExpression.SelectExprProcessor[0].ResultEventType; } + } + + public bool HasAggregation + { + get { return true; } + } + + public ExprEvaluator[] GroupKeyNodes { get; private set; } + + public ExprEvaluator GroupKeyNode { get; private set; } + + public bool IsSorting { get; private set; } + + public bool IsSelectRStream { get; private set; } + + public bool IsUnidirectional { get; private set; } + + public OutputLimitSpec OutputLimitSpec { get; private set; } + + public ExprNode[] GroupKeyNodeExpressions { get; private set; } + + public AggregationGroupByRollupDesc GroupByRollupDesc { get; private set; } + + public GroupByRollupPerLevelExpression PerLevelExpression { get; private set; } + + public bool IsJoin { get; private set; } + + public bool IsHistoricalOnly { get; private set; } + + public ResultSetProcessorType ResultSetProcessorType + { + get { return ResultSetProcessorType.FULLYAGGREGATED_GROUPED_ROLLUP; } + } + + public OutputConditionPolledFactory OptionalOutputFirstConditionFactory { get; private set; } + + public bool IsEnableOutputLimitOpt { get; private set; } + + public ResultSetProcessorHelperFactory ResultSetProcessorHelperFactory { get; private set; } + + public int NumStreams { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowPerGroupRollupOutputAllHelper.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowPerGroupRollupOutputAllHelper.cs new file mode 100755 index 000000000..90e97f905 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowPerGroupRollupOutputAllHelper.cs @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; + +namespace com.espertech.esper.epl.core +{ + public interface ResultSetProcessorRowPerGroupRollupOutputAllHelper + : ResultSetProcessorOutputHelper + { + void ProcessView(EventBean[] newData, EventBean[] oldData, bool isGenerateSynthetic); + void ProcessJoin(ISet> newEvents, ISet> oldEvents, bool isGenerateSynthetic); + UniformPair OutputView(bool isSynthesize); + UniformPair OutputJoin(bool isSynthesize); + void Destroy(); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowPerGroupRollupOutputAllHelperImpl.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowPerGroupRollupOutputAllHelperImpl.cs new file mode 100755 index 000000000..d65ea391d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowPerGroupRollupOutputAllHelperImpl.cs @@ -0,0 +1,199 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.epl.core +{ + public class ResultSetProcessorRowPerGroupRollupOutputAllHelperImpl : ResultSetProcessorRowPerGroupRollupOutputAllHelper + { + private readonly ResultSetProcessorRowPerGroupRollup _processor; + private readonly IDictionary[] _outputLimitGroupRepsPerLevel; + private readonly IDictionary[] _groupRepsOutputLastUnordRStream; + private bool _first; + + public ResultSetProcessorRowPerGroupRollupOutputAllHelperImpl(ResultSetProcessorRowPerGroupRollup processor, int levelCount) + { + _processor = processor; + + _outputLimitGroupRepsPerLevel = new IDictionary[levelCount]; + for (var i = 0; i < levelCount; i++) { + _outputLimitGroupRepsPerLevel[i] = new LinkedHashMap(); + } + + if (processor.Prototype.IsSelectRStream) { + _groupRepsOutputLastUnordRStream = new IDictionary[levelCount]; + for (var i = 0; i < levelCount; i++) { + _groupRepsOutputLastUnordRStream[i] = new LinkedHashMap(); + } + } + else { + _groupRepsOutputLastUnordRStream = null; + } + } + + public void ProcessView(EventBean[] newData, EventBean[] oldData, bool isGenerateSynthetic) + { + GenerateRemoveStreamJustOnce(isGenerateSynthetic, false); + + // apply to aggregates + var groupKeysPerLevel = new object[_processor.Prototype.GroupByRollupDesc.Levels.Length]; + EventBean[] eventsPerStream; + if (newData != null) { + foreach (var aNewData in newData) { + eventsPerStream = new EventBean[] {aNewData}; + var groupKeyComplete = _processor.GenerateGroupKey(eventsPerStream, true); + foreach (var level in _processor.Prototype.GroupByRollupDesc.Levels) + { + var groupKey = level.ComputeSubkey(groupKeyComplete); + groupKeysPerLevel[level.LevelNumber] = groupKey; + if (_outputLimitGroupRepsPerLevel[level.LevelNumber].Push(groupKey, eventsPerStream) == null) + { + if (_processor.Prototype.IsSelectRStream) + { + _processor.GenerateOutputBatchedMapUnsorted(false, groupKey, level, eventsPerStream, true, isGenerateSynthetic, _groupRepsOutputLastUnordRStream[level.LevelNumber]); + } + } + } + _processor.AggregationService.ApplyEnter(eventsPerStream, groupKeysPerLevel, _processor.AgentInstanceContext); + } + } + if (oldData != null) { + foreach (var anOldData in oldData) { + eventsPerStream = new EventBean[] {anOldData}; + var groupKeyComplete = _processor.GenerateGroupKey(eventsPerStream, false); + foreach (var level in _processor.Prototype.GroupByRollupDesc.Levels) { + var groupKey = level.ComputeSubkey(groupKeyComplete); + groupKeysPerLevel[level.LevelNumber] = groupKey; + if (_outputLimitGroupRepsPerLevel[level.LevelNumber].Push(groupKey, eventsPerStream) == null) { + if (_processor.Prototype.IsSelectRStream) + { + _processor.GenerateOutputBatchedMapUnsorted(true, groupKey, level, eventsPerStream, false, isGenerateSynthetic, _groupRepsOutputLastUnordRStream[level.LevelNumber]); + } + } + } + _processor.AggregationService.ApplyLeave(eventsPerStream, groupKeysPerLevel, _processor.AgentInstanceContext); + } + } + } + + public void ProcessJoin(ISet> newEvents, ISet> oldEvents, bool isGenerateSynthetic) + { + GenerateRemoveStreamJustOnce(isGenerateSynthetic, true); + + // apply to aggregates + var groupKeysPerLevel = new object[_processor.Prototype.GroupByRollupDesc.Levels.Length]; + if (newEvents != null) { + foreach (var newEvent in newEvents) { + var aNewData = newEvent.Array; + var groupKeyComplete = _processor.GenerateGroupKey(aNewData, true); + foreach (var level in _processor.Prototype.GroupByRollupDesc.Levels) + { + var groupKey = level.ComputeSubkey(groupKeyComplete); + groupKeysPerLevel[level.LevelNumber] = groupKey; + if (_outputLimitGroupRepsPerLevel[level.LevelNumber].Push(groupKey, aNewData) == null) { + if (_processor.Prototype.IsSelectRStream) + { + _processor.GenerateOutputBatchedMapUnsorted(false, groupKey, level, aNewData, true, isGenerateSynthetic, _groupRepsOutputLastUnordRStream[level.LevelNumber]); + } + } + } + _processor.AggregationService.ApplyEnter(aNewData, groupKeysPerLevel, _processor.AgentInstanceContext); + } + } + if (oldEvents != null) { + foreach (var oldEvent in oldEvents) { + var aOldData = oldEvent.Array; + var groupKeyComplete = _processor.GenerateGroupKey(aOldData, false); + foreach (var level in _processor.Prototype.GroupByRollupDesc.Levels) + { + var groupKey = level.ComputeSubkey(groupKeyComplete); + groupKeysPerLevel[level.LevelNumber] = groupKey; + if (_outputLimitGroupRepsPerLevel[level.LevelNumber].Push(groupKey, aOldData) == null) { + if (_processor.Prototype.IsSelectRStream) + { + _processor.GenerateOutputBatchedMapUnsorted(true, groupKey, level, aOldData, false, isGenerateSynthetic, _groupRepsOutputLastUnordRStream[level.LevelNumber]); + } + } + } + _processor.AggregationService.ApplyLeave(aOldData, groupKeysPerLevel, _processor.AgentInstanceContext); + } + } + } + + public UniformPair OutputView(bool isSynthesize) + { + GenerateRemoveStreamJustOnce(isSynthesize, false); + return Output(isSynthesize, false); + } + + public UniformPair OutputJoin(bool isSynthesize) + { + GenerateRemoveStreamJustOnce(isSynthesize, true); + return Output(isSynthesize, true); + } + + public void Destroy() + { + // no action required + } + + private UniformPair Output(bool isSynthesize, bool isJoin) + { + IList newEvents = new List(); + foreach (var level in _processor.Prototype.GroupByRollupDesc.Levels) + { + var groupGenerators = _outputLimitGroupRepsPerLevel[level.LevelNumber]; + foreach (var entry in groupGenerators) { + _processor.GenerateOutputBatched(isJoin, entry.Key, level, entry.Value, true, isSynthesize, newEvents, null); + } + } + var newEventsArr = (newEvents.IsEmpty()) ? null : newEvents.ToArrayOrNull(); + + EventBean[] oldEventsArr = null; + if (_processor.Prototype.IsSelectRStream) + { + IList oldEventList = new List(); + foreach (var entry in _groupRepsOutputLastUnordRStream) { + oldEventList.AddAll(entry.Values); + entry.Clear(); + } + if (!oldEventList.IsEmpty()) { + oldEventsArr = oldEventList.ToArrayOrNull(); + } + } + + _first = true; + + if (newEventsArr == null && oldEventsArr == null) { + return null; + } + return new UniformPair(newEventsArr, oldEventsArr); + } + + private void GenerateRemoveStreamJustOnce(bool isSynthesize, bool join) + { + if (_first && _processor.Prototype.IsSelectRStream) + { + foreach (var level in _processor.Prototype.GroupByRollupDesc.Levels) + { + foreach (var groupRep in _outputLimitGroupRepsPerLevel[level.LevelNumber]) { + var groupKeyPartial = _processor.GenerateGroupKey(groupRep.Value, false); + var groupKey = level.ComputeSubkey(groupKeyPartial); + _processor.GenerateOutputBatchedMapUnsorted(join, groupKey, level, groupRep.Value, false, isSynthesize, _groupRepsOutputLastUnordRStream[level.LevelNumber]); + } + } + } + _first = false; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowPerGroupRollupOutputLastHelper.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowPerGroupRollupOutputLastHelper.cs new file mode 100755 index 000000000..789480b0e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowPerGroupRollupOutputLastHelper.cs @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; + +namespace com.espertech.esper.epl.core +{ + public interface ResultSetProcessorRowPerGroupRollupOutputLastHelper + : ResultSetProcessorOutputHelper + { + void ProcessView(EventBean[] newData, EventBean[] oldData, bool isGenerateSynthetic); + void ProcessJoin(ISet> newEvents, ISet> oldEvents, bool isGenerateSynthetic); + UniformPair OutputView(bool isSynthesize); + UniformPair OutputJoin(bool isSynthesize); + void Destroy(); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowPerGroupRollupOutputLastHelperImpl.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowPerGroupRollupOutputLastHelperImpl.cs new file mode 100755 index 000000000..9d4566c61 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowPerGroupRollupOutputLastHelperImpl.cs @@ -0,0 +1,164 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.epl.core +{ + public class ResultSetProcessorRowPerGroupRollupOutputLastHelperImpl : ResultSetProcessorRowPerGroupRollupOutputLastHelper + { + private readonly ResultSetProcessorRowPerGroupRollup _processor; + private readonly IDictionary[] _outputLimitGroupRepsPerLevel; + private readonly IDictionary[] _groupRepsOutputLastUnordRStream; + + public ResultSetProcessorRowPerGroupRollupOutputLastHelperImpl(ResultSetProcessorRowPerGroupRollup processor, int levelCount) + { + _processor = processor; + + _outputLimitGroupRepsPerLevel = new IDictionary[levelCount]; + for (var i = 0; i < levelCount; i++) { + _outputLimitGroupRepsPerLevel[i] = new Dictionary().WithNullSupport(); + } + + if (processor.Prototype.IsSelectRStream) { + _groupRepsOutputLastUnordRStream = new IDictionary[levelCount]; + for (var i = 0; i < levelCount; i++) { + _groupRepsOutputLastUnordRStream[i] = new Dictionary().WithNullSupport(); + } + } + else { + _groupRepsOutputLastUnordRStream = null; + } + } + + public void ProcessView(EventBean[] newData, EventBean[] oldData, bool isGenerateSynthetic) { + // apply to aggregates + var groupKeysPerLevel = new object[_processor.Prototype.GroupByRollupDesc.Levels.Length]; + EventBean[] eventsPerStream; + if (newData != null) { + foreach (var aNewData in newData) { + eventsPerStream = new EventBean[] {aNewData}; + var groupKeyComplete = _processor.GenerateGroupKey(eventsPerStream, true); + foreach (var level in _processor.Prototype.GroupByRollupDesc.Levels) { + var groupKey = level.ComputeSubkey(groupKeyComplete); + groupKeysPerLevel[level.LevelNumber] = groupKey; + + _outputLimitGroupRepsPerLevel[level.LevelNumber].Put(groupKey, eventsPerStream); + if (_processor.Prototype.IsSelectRStream && !_groupRepsOutputLastUnordRStream[level.LevelNumber].ContainsKey(groupKey)) { + _processor.GenerateOutputBatchedMapUnsorted(false, groupKey, level, eventsPerStream, true, isGenerateSynthetic, _groupRepsOutputLastUnordRStream[level.LevelNumber]); + } + } + _processor.AggregationService.ApplyEnter(eventsPerStream, groupKeysPerLevel, _processor.AgentInstanceContext); + } + } + if (oldData != null) { + foreach (var anOldData in oldData) { + eventsPerStream = new EventBean[] {anOldData}; + var groupKeyComplete = _processor.GenerateGroupKey(eventsPerStream, false); + foreach (var level in _processor.Prototype.GroupByRollupDesc.Levels) { + var groupKey = level.ComputeSubkey(groupKeyComplete); + groupKeysPerLevel[level.LevelNumber] = groupKey; + + _outputLimitGroupRepsPerLevel[level.LevelNumber].Put(groupKey, eventsPerStream); + if (_processor.Prototype.IsSelectRStream && !_groupRepsOutputLastUnordRStream[level.LevelNumber].ContainsKey(groupKey)) { + _processor.GenerateOutputBatchedMapUnsorted(false, groupKey, level, eventsPerStream, false, isGenerateSynthetic, _groupRepsOutputLastUnordRStream[level.LevelNumber]); + } + } + _processor.AggregationService.ApplyLeave(eventsPerStream, groupKeysPerLevel, _processor.AgentInstanceContext); + } + } + } + + public void ProcessJoin(ISet> newEvents, ISet> oldEvents, bool isGenerateSynthetic) { + // apply to aggregates + var groupKeysPerLevel = new object[_processor.Prototype.GroupByRollupDesc.Levels.Length]; + if (newEvents != null) { + foreach (var newEvent in newEvents) { + var aNewData = newEvent.Array; + var groupKeyComplete = _processor.GenerateGroupKey(aNewData, true); + foreach (var level in _processor.Prototype.GroupByRollupDesc.Levels) { + var groupKey = level.ComputeSubkey(groupKeyComplete); + groupKeysPerLevel[level.LevelNumber] = groupKey; + + _outputLimitGroupRepsPerLevel[level.LevelNumber].Put(groupKey, aNewData); + if (_processor.Prototype.IsSelectRStream && !_groupRepsOutputLastUnordRStream[level.LevelNumber].ContainsKey(groupKey)) { + _processor.GenerateOutputBatchedMapUnsorted(false, groupKey, level, aNewData, true, isGenerateSynthetic, _groupRepsOutputLastUnordRStream[level.LevelNumber]); + } + } + _processor.AggregationService.ApplyEnter(aNewData, groupKeysPerLevel, _processor.AgentInstanceContext); + } + } + if (oldEvents != null) { + foreach (var oldEvent in oldEvents) { + var aOldData = oldEvent.Array; + var groupKeyComplete = _processor.GenerateGroupKey(aOldData, false); + foreach (var level in _processor.Prototype.GroupByRollupDesc.Levels) { + var groupKey = level.ComputeSubkey(groupKeyComplete); + groupKeysPerLevel[level.LevelNumber] = groupKey; + + _outputLimitGroupRepsPerLevel[level.LevelNumber].Put(groupKey, aOldData); + if (_processor.Prototype.IsSelectRStream && !_groupRepsOutputLastUnordRStream[level.LevelNumber].ContainsKey(groupKey)) { + _processor.GenerateOutputBatchedMapUnsorted(false, groupKey, level, aOldData, false, isGenerateSynthetic, _groupRepsOutputLastUnordRStream[level.LevelNumber]); + } + } + _processor.AggregationService.ApplyLeave(aOldData, groupKeysPerLevel, _processor.AgentInstanceContext); + } + } + } + + public UniformPair OutputView(bool isSynthesize) { + return Output(isSynthesize, false); + } + + public UniformPair OutputJoin(bool isSynthesize) { + return Output(isSynthesize, true); + } + + public void Destroy() { + // no action required + } + + private UniformPair Output(bool isSynthesize, bool isJoin) { + + IList newEvents = new List(4); + foreach (var level in _processor.Prototype.GroupByRollupDesc.Levels) { + var groupGenerators = _outputLimitGroupRepsPerLevel[level.LevelNumber]; + foreach (var entry in groupGenerators) { + _processor.GenerateOutputBatched(isJoin, entry.Key, level, entry.Value, true, isSynthesize, newEvents, null); + } + } + var newEventsArr = (newEvents.IsEmpty()) ? null : newEvents.ToArrayOrNull(); + foreach (var outputLimitGroupRepsPerLevelItem in _outputLimitGroupRepsPerLevel) { + outputLimitGroupRepsPerLevelItem.Clear(); + } + + EventBean[] oldEventsArr = null; + if (_groupRepsOutputLastUnordRStream != null) { + IList oldEventList = new List(4); + foreach (var entry in _groupRepsOutputLastUnordRStream) { + oldEventList.AddAll(entry.Values); + } + if (!oldEventList.IsEmpty()) { + oldEventsArr = oldEventList.ToArrayOrNull(); + foreach (var groupRepsOutputLastUnordRStreamItem in _groupRepsOutputLastUnordRStream) { + groupRepsOutputLastUnordRStreamItem.Clear(); + } + } + } + + if (newEventsArr == null && oldEventsArr == null) { + return null; + } + return new UniformPair(newEventsArr, oldEventsArr); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowPerGroupRollupUnbound.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowPerGroupRollupUnbound.cs new file mode 100755 index 000000000..4470b2e2e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowPerGroupRollupUnbound.cs @@ -0,0 +1,115 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.view; + +namespace com.espertech.esper.epl.core +{ + public class ResultSetProcessorRowPerGroupRollupUnbound : ResultSetProcessorRowPerGroupRollup + { + private readonly ResultSetProcessorRowPerGroupRollupUnboundHelper _unboundHelper; + + public ResultSetProcessorRowPerGroupRollupUnbound(ResultSetProcessorRowPerGroupRollupFactory prototype, OrderByProcessor orderByProcessor, AggregationService aggregationService, AgentInstanceContext agentInstanceContext) + : base(prototype, orderByProcessor, aggregationService, agentInstanceContext) + { + _unboundHelper = prototype.ResultSetProcessorHelperFactory.MakeRSRowPerGroupRollupSnapshotUnbound(agentInstanceContext, prototype); + } + + public override void Stop() + { + base.Stop(); + _unboundHelper.Destroy(); + } + + public override void ApplyViewResult(EventBean[] newData, EventBean[] oldData) + { + var newDataMultiKey = GenerateGroupKeysView(newData, _unboundHelper.Buffer, true); + var oldDataMultiKey = GenerateGroupKeysView(oldData, _unboundHelper.Buffer, false); + + var aggregationService = AggregationService; + var agentInstanceContext = AgentInstanceContext; + + // update aggregates + var eventsPerStream = new EventBean[1]; + if (newData != null) + { + for (var i = 0; i < newData.Length; i++) + { + eventsPerStream[0] = newData[i]; + aggregationService.ApplyEnter(eventsPerStream, newDataMultiKey[i], agentInstanceContext); + } + } + if (oldData != null) + { + for (var i = 0; i < oldData.Length; i++) + { + eventsPerStream[0] = oldData[i]; + aggregationService.ApplyLeave(eventsPerStream, oldDataMultiKey[i], agentInstanceContext); + } + } + } + + public override UniformPair ProcessViewResult(EventBean[] newData, EventBean[] oldData, bool isSynthesize) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QResultSetProcessGroupedRowPerGroup();} + + var newDataMultiKey = GenerateGroupKeysView(newData, _unboundHelper.Buffer, true); + var oldDataMultiKey = GenerateGroupKeysView(oldData, _unboundHelper.Buffer, false); + + EventBean[] selectOldEvents = null; + if (Prototype.IsSelectRStream) { + selectOldEvents = GenerateOutputEventsView(_unboundHelper.Buffer, false, isSynthesize); + } + + var aggregationService = AggregationService; + var agentInstanceContext = AgentInstanceContext; + + // update aggregates + var eventsPerStream = new EventBean[1]; + if (newData != null) { + for (var i = 0; i < newData.Length; i++) { + eventsPerStream[0] = newData[i]; + aggregationService.ApplyEnter(eventsPerStream, newDataMultiKey[i], agentInstanceContext); + } + } + if (oldData != null) { + for (var i = 0; i < oldData.Length; i++) { + eventsPerStream[0] = oldData[i]; + aggregationService.ApplyLeave(eventsPerStream, oldDataMultiKey[i], agentInstanceContext); + } + } + + // generate new events using select expressions + var selectNewEvents = GenerateOutputEventsView(_unboundHelper.Buffer, true, isSynthesize); + + if ((selectNewEvents != null) || (selectOldEvents != null)) { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AResultSetProcessGroupedRowPerGroup(selectNewEvents, selectOldEvents);} + return new UniformPair(selectNewEvents, selectOldEvents); + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AResultSetProcessGroupedRowPerGroup(null, null);} + return null; + } + + public override IEnumerator GetEnumerator(Viewable parent) + { + var output = GenerateOutputEventsView(_unboundHelper.Buffer, true, true); + if (output == null) + return EnumerationHelper.Empty(); + return ((IEnumerable) output).GetEnumerator(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowPerGroupRollupUnboundHelper.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowPerGroupRollupUnboundHelper.cs new file mode 100755 index 000000000..80f51d8f8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowPerGroupRollupUnboundHelper.cs @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; + +namespace com.espertech.esper.epl.core +{ + public interface ResultSetProcessorRowPerGroupRollupUnboundHelper + { + IDictionary[] Buffer { get; } + void Destroy(); + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowPerGroupRollupUnboundHelperImpl.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowPerGroupRollupUnboundHelperImpl.cs new file mode 100755 index 000000000..8863a3cd8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowPerGroupRollupUnboundHelperImpl.cs @@ -0,0 +1,39 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.epl.core +{ + public class ResultSetProcessorRowPerGroupRollupUnboundHelperImpl : ResultSetProcessorRowPerGroupRollupUnboundHelper + { + private readonly LinkedHashMap[] eventPerGroupBuf; + + public ResultSetProcessorRowPerGroupRollupUnboundHelperImpl(int levelCount) + { + eventPerGroupBuf = new LinkedHashMap[levelCount]; + for (int i = 0; i < levelCount; i++) + { + eventPerGroupBuf[i] = new LinkedHashMap(); + } + } + + public IDictionary[] Buffer + { + get { return eventPerGroupBuf; } + } + + public void Destroy() + { + // no action required + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowPerGroupUnbound.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowPerGroupUnbound.cs new file mode 100755 index 000000000..88414a650 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowPerGroupUnbound.cs @@ -0,0 +1,118 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.view; + +namespace com.espertech.esper.epl.core +{ + public class ResultSetProcessorRowPerGroupUnbound : ResultSetProcessorRowPerGroup, AggregationRowRemovedCallback + { + protected readonly ResultSetProcessorRowPerGroupUnboundGroupRep _groupReps; + + public ResultSetProcessorRowPerGroupUnbound(ResultSetProcessorRowPerGroupFactory prototype, SelectExprProcessor selectExprProcessor, OrderByProcessor orderByProcessor, AggregationService aggregationService, AgentInstanceContext agentInstanceContext) + : base(prototype, selectExprProcessor, orderByProcessor, aggregationService, agentInstanceContext) + { + _groupReps = prototype.ResultSetProcessorHelperFactory.MakeRSRowPerGroupUnboundGroupRep(agentInstanceContext, prototype); + aggregationService.SetRemovedCallback(_groupReps); + } + + public override void ApplyViewResult(EventBean[] newData, EventBean[] oldData) + { + var eventsPerStream = new EventBean[1]; + if (newData != null) + { + var groupReps = _groupReps; + var newDataLength = newData.Length; + for(var ii = 0 ; ii < newDataLength ; ii++) + { + eventsPerStream[0] = newData[ii]; + var mk = GenerateGroupKey(eventsPerStream, true); + groupReps.Put(mk, eventsPerStream[0]); + AggregationService.ApplyEnter(eventsPerStream, mk, AgentInstanceContext); + } + } + if (oldData != null) + { + var oldDataLength = oldData.Length; + for (var ii = 0; ii < oldDataLength; ii++) + { + eventsPerStream[0] = oldData[ii]; + var mk = GenerateGroupKey(eventsPerStream, false); + AggregationService.ApplyLeave(eventsPerStream, mk, AgentInstanceContext); + } + } + } + + public override UniformPair ProcessViewResult(EventBean[] newData, EventBean[] oldData, bool isSynthesize) + { + // Generate group-by keys for all events, collect all keys in a set for later event generation + IDictionary keysAndEvents = new Dictionary(); + var newDataMultiKey = GenerateGroupKeys(newData, keysAndEvents, true); + var oldDataMultiKey = GenerateGroupKeys(oldData, keysAndEvents, false); + + EventBean[] selectOldEvents = null; + if (Prototype.IsSelectRStream) + { + selectOldEvents = GenerateOutputEventsView(keysAndEvents, false, isSynthesize); + } + + // update aggregates + var eventsPerStream = new EventBean[1]; + if (newData != null) + { + // apply new data to aggregates + for (var i = 0; i < newData.Length; i++) + { + eventsPerStream[0] = newData[i]; + _groupReps.Put(newDataMultiKey[i], eventsPerStream[0]); + AggregationService.ApplyEnter(eventsPerStream, newDataMultiKey[i], AgentInstanceContext); + } + } + if (oldData != null) + { + // apply old data to aggregates + for (var i = 0; i < oldData.Length; i++) + { + eventsPerStream[0] = oldData[i]; + AggregationService.ApplyLeave(eventsPerStream, oldDataMultiKey[i], AgentInstanceContext); + } + } + + // generate new events using select expressions + var selectNewEvents = GenerateOutputEventsView(keysAndEvents, true, isSynthesize); + + if ((selectNewEvents != null) || (selectOldEvents != null)) + { + return new UniformPair(selectNewEvents, selectOldEvents); + } + return null; + } + + public override IEnumerator GetEnumerator(Viewable parent) + { + if (OrderByProcessor == null) + { + return ResultSetRowPerGroupEnumerator.New(_groupReps.Values, this, AggregationService, AgentInstanceContext); + } + return GetEnumeratorSorted(_groupReps.Values.GetEnumerator()); + } + + public override void Stop() + { + base.Stop(); + _groupReps.Destroy(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowPerGroupUnboundGroupRep.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowPerGroupUnboundGroupRep.cs new file mode 100755 index 000000000..ec0fe0149 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowPerGroupUnboundGroupRep.cs @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.agg.service; + +namespace com.espertech.esper.epl.core +{ + public interface ResultSetProcessorRowPerGroupUnboundGroupRep : AggregationRowRemovedCallback + { + void Put(object key, EventBean @event); + //void Removed(object key); + IEnumerable Values { get; } + void Destroy(); + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowPerGroupUnboundGroupRepImpl.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowPerGroupUnboundGroupRepImpl.cs new file mode 100755 index 000000000..1519b0175 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorRowPerGroupUnboundGroupRepImpl.cs @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.epl.core +{ + public class ResultSetProcessorRowPerGroupUnboundGroupRepImpl : ResultSetProcessorRowPerGroupUnboundGroupRep + { + private readonly IDictionary _groupReps = new LinkedHashMap(); + + public void Put(object key, EventBean @event) + { + _groupReps.Put(key, @event); + } + + public IEnumerable Values + { + get { return _groupReps.Values; } + } + + public void Removed(object key) + { + _groupReps.Remove(key); + } + + public void Destroy() + { + // no action required + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorSimple.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorSimple.cs new file mode 100755 index 000000000..64cdf94c8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorSimple.cs @@ -0,0 +1,333 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.epl.core +{ + /// + /// Result set processor for the simplest case: no aggregation functions used in the + /// select clause, and no group-by. + /// + /// The processor generates one row for each event entering (new event) and one row + /// for each event leaving (old event). + /// + public class ResultSetProcessorSimple : ResultSetProcessorBaseSimple + { + internal readonly ResultSetProcessorSimpleFactory Prototype; + private readonly SelectExprProcessor _selectExprProcessor; + private readonly OrderByProcessor _orderByProcessor; + internal ExprEvaluatorContext ExprEvaluatorContext; + private ResultSetProcessorSimpleOutputLastHelper _outputLastHelper; + private ResultSetProcessorSimpleOutputAllHelper _outputAllHelper; + + public ResultSetProcessorSimple( + ResultSetProcessorSimpleFactory prototype, + SelectExprProcessor selectExprProcessor, + OrderByProcessor orderByProcessor, + AgentInstanceContext agentInstanceContext) + { + Prototype = prototype; + _selectExprProcessor = selectExprProcessor; + _orderByProcessor = orderByProcessor; + ExprEvaluatorContext = agentInstanceContext; + if (prototype.IsOutputLast) + { + // output-last always uses this mechanism + _outputLastHelper = prototype.ResultSetProcessorHelperFactory + .MakeRSSimpleOutputLast(prototype, this, agentInstanceContext); + } + else if (prototype.IsOutputAll && prototype.IsEnableOutputLimitOpt) + { + _outputAllHelper = prototype.ResultSetProcessorHelperFactory + .MakeRSSimpleOutputAll(prototype, this, agentInstanceContext); + } + } + + + public override AgentInstanceContext AgentInstanceContext + { + set { ExprEvaluatorContext = value; } + get { return (AgentInstanceContext) ExprEvaluatorContext; } + } + + public override EventType ResultEventType + { + get { return Prototype.ResultEventType; } + } + + public override UniformPair ProcessJoinResult(ISet> newEvents, ISet> oldEvents, bool isSynthesize) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QResultSetProcessSimple();} + + EventBean[] selectOldEvents = null; + EventBean[] selectNewEvents; + + if (Prototype.OptionalHavingExpr == null) + { + if (Prototype.IsSelectRStream) + { + if (_orderByProcessor == null) { + selectOldEvents = ResultSetProcessorUtil.GetSelectJoinEventsNoHaving(_selectExprProcessor, oldEvents, false, isSynthesize, ExprEvaluatorContext); + } + else { + selectOldEvents = ResultSetProcessorUtil.GetSelectJoinEventsNoHavingWithOrderBy(_selectExprProcessor, _orderByProcessor, oldEvents, false, isSynthesize, ExprEvaluatorContext); + } + } + + if (_orderByProcessor == null) { + selectNewEvents = ResultSetProcessorUtil.GetSelectJoinEventsNoHaving(_selectExprProcessor, newEvents, true, isSynthesize, ExprEvaluatorContext); + } + else { + selectNewEvents = ResultSetProcessorUtil.GetSelectJoinEventsNoHavingWithOrderBy(_selectExprProcessor, _orderByProcessor, newEvents, true, isSynthesize, ExprEvaluatorContext); + } + } + else + { + if (Prototype.IsSelectRStream) + { + if (_orderByProcessor == null) { + selectOldEvents = ResultSetProcessorUtil.GetSelectJoinEventsHaving(_selectExprProcessor, oldEvents, Prototype.OptionalHavingExpr, false, isSynthesize, ExprEvaluatorContext); + } + else { + selectOldEvents = ResultSetProcessorUtil.GetSelectJoinEventsHavingWithOrderBy(_selectExprProcessor, _orderByProcessor, oldEvents, Prototype.OptionalHavingExpr, false, isSynthesize, ExprEvaluatorContext); + } + } + + if (_orderByProcessor == null) { + selectNewEvents = ResultSetProcessorUtil.GetSelectJoinEventsHaving(_selectExprProcessor, newEvents, Prototype.OptionalHavingExpr, true, isSynthesize, ExprEvaluatorContext); + } + else { + selectNewEvents = ResultSetProcessorUtil.GetSelectJoinEventsHavingWithOrderBy(_selectExprProcessor, _orderByProcessor, newEvents, Prototype.OptionalHavingExpr, true, isSynthesize, ExprEvaluatorContext); + } + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AResultSetProcessSimple(selectNewEvents, selectOldEvents);} + return new UniformPair(selectNewEvents, selectOldEvents); + } + + public override UniformPair ProcessViewResult(EventBean[] newData, EventBean[] oldData, bool isSynthesize) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QResultSetProcessSimple();} + + EventBean[] selectOldEvents = null; + EventBean[] selectNewEvents; + if (Prototype.OptionalHavingExpr == null) + { + if (Prototype.IsSelectRStream) + { + if (_orderByProcessor == null) { + selectOldEvents = ResultSetProcessorUtil.GetSelectEventsNoHaving(_selectExprProcessor, oldData, false, isSynthesize, ExprEvaluatorContext); + } + else { + selectOldEvents = ResultSetProcessorUtil.GetSelectEventsNoHavingWithOrderBy(_selectExprProcessor, _orderByProcessor, oldData, false, isSynthesize, ExprEvaluatorContext); + } + } + + if (_orderByProcessor == null) { + selectNewEvents = ResultSetProcessorUtil.GetSelectEventsNoHaving(_selectExprProcessor, newData, true, isSynthesize, ExprEvaluatorContext); + } + else { + selectNewEvents = ResultSetProcessorUtil.GetSelectEventsNoHavingWithOrderBy(_selectExprProcessor, _orderByProcessor, newData, true, isSynthesize, ExprEvaluatorContext); + } + } + else + { + if (Prototype.IsSelectRStream) + { + if (_orderByProcessor == null) { + selectOldEvents = ResultSetProcessorUtil.GetSelectEventsHaving(_selectExprProcessor, oldData, Prototype.OptionalHavingExpr, false, isSynthesize, ExprEvaluatorContext); + } + else { + selectOldEvents = ResultSetProcessorUtil.GetSelectEventsHavingWithOrderBy(_selectExprProcessor, _orderByProcessor, oldData, Prototype.OptionalHavingExpr, false, isSynthesize, ExprEvaluatorContext); + } + } + if (_orderByProcessor == null) { + selectNewEvents = ResultSetProcessorUtil.GetSelectEventsHaving(_selectExprProcessor, newData, Prototype.OptionalHavingExpr, true, isSynthesize, ExprEvaluatorContext); + } + else { + selectNewEvents = ResultSetProcessorUtil.GetSelectEventsHavingWithOrderBy(_selectExprProcessor, _orderByProcessor, newData, Prototype.OptionalHavingExpr, true, isSynthesize, ExprEvaluatorContext); + } + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AResultSetProcessSimple(selectNewEvents, selectOldEvents);} + return new UniformPair(selectNewEvents, selectOldEvents); + } + + /// Process view results for the iterator. + /// new events + /// pair of insert and remove stream + public UniformPair ProcessViewResultIterator(EventBean[] newData) + { + EventBean[] selectNewEvents; + if (Prototype.OptionalHavingExpr == null) + { + // ignore _orderByProcessor + selectNewEvents = ResultSetProcessorUtil.GetSelectEventsNoHaving(_selectExprProcessor, newData, true, true, ExprEvaluatorContext); + } + else + { + // ignore _orderByProcessor + selectNewEvents = ResultSetProcessorUtil.GetSelectEventsHaving(_selectExprProcessor, newData, Prototype.OptionalHavingExpr, true, true, ExprEvaluatorContext); + } + + return new UniformPair(selectNewEvents, null); + } + + public override IEnumerator GetEnumerator(Viewable parent) + { + if (_orderByProcessor != null) + { + // Pull all events, generate order keys + var eventsPerStream = new EventBean[1]; + var events = new List(); + var orderKeys = new List(); + + var parentEnumerator = parent.GetEnumerator(); + if (parentEnumerator.MoveNext() == false) + { + return CollectionUtil.NULL_EVENT_ITERATOR; + } + + do + { + var aParent = parentEnumerator.Current; + eventsPerStream[0] = aParent; + var orderKey = _orderByProcessor.GetSortKey(eventsPerStream, true, ExprEvaluatorContext); + var pair = ProcessViewResultIterator(eventsPerStream); + var result = pair.First; + if (result != null && result.Length != 0) + { + events.Add(result[0]); + } + orderKeys.Add(orderKey); + } while (parentEnumerator.MoveNext()); + + // sort + var outgoingEvents = events.ToArray(); + var orderKeysArr = orderKeys.ToArray(); + var orderedEvents = _orderByProcessor.Sort(outgoingEvents, orderKeysArr, ExprEvaluatorContext); + if (orderedEvents == null) + return EnumerationHelper.Empty(); + + return ((IEnumerable)orderedEvents).GetEnumerator(); + } + // Return an iterator that gives row-by-row a result + + var transform = new ResultSetProcessorSimpleTransform(this); + return parent.Select(transform.Transform).GetEnumerator(); + } + + public override IEnumerator GetEnumerator(ISet> joinSet) + { + // Process join results set as a regular join, includes sorting and having-clause filter + var result = ProcessJoinResult(joinSet, CollectionUtil.EMPTY_ROW_SET, true); + var first = result.First; + if (first == null) + return EnumerationHelper.Empty(); + return ((IEnumerable)first).GetEnumerator(); + } + + public override void Clear() + { + // No need to clear state, there is no state held + } + + public override bool HasAggregation + { + get { return false; } + } + + public override void ApplyViewResult(EventBean[] newData, EventBean[] oldData) + { + } + + public override void ApplyJoinResult(ISet> newEvents, ISet> oldEvents) + { + } + + public override void ProcessOutputLimitedLastAllNonBufferedView(EventBean[] newData, EventBean[] oldData, bool isGenerateSynthetic, bool isAll) + { + if (isAll) + { + _outputAllHelper.ProcessView(newData, oldData); + } + else + { + _outputLastHelper.ProcessView(newData, oldData); + } + } + + public override void ProcessOutputLimitedLastAllNonBufferedJoin(ISet> newEvents, ISet> oldEvents, bool isGenerateSynthetic, bool isAll) + { + if (isAll) + { + _outputAllHelper.ProcessJoin(newEvents, oldEvents); + } + else + { + _outputLastHelper.ProcessJoin(newEvents, oldEvents); + } + } + + public override UniformPair ContinueOutputLimitedLastAllNonBufferedView(bool isSynthesize, bool isAll) + { + if (isAll) + { + return _outputAllHelper.OutputView(isSynthesize); + } + return _outputLastHelper.OutputView(isSynthesize); + } + + public override UniformPair ContinueOutputLimitedLastAllNonBufferedJoin(bool isSynthesize, bool isAll) + { + if (isAll) + { + return _outputAllHelper.OutputJoin(isSynthesize); + } + return _outputLastHelper.OutputJoin(isSynthesize); + } + + public override void Stop() + { + if (_outputLastHelper != null) + { + _outputLastHelper.Destroy(); + } + if (_outputAllHelper != null) + { + _outputAllHelper.Destroy(); + } + } + + public override void AcceptHelperVisitor(ResultSetProcessorOutputHelperVisitor visitor) + { + if (_outputLastHelper != null) + { + visitor.Visit(_outputLastHelper); + } + if (_outputAllHelper != null) + { + visitor.Visit(_outputAllHelper); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorSimpleFactory.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorSimpleFactory.cs new file mode 100755 index 000000000..b23fb9dc8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorSimpleFactory.cs @@ -0,0 +1,96 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.epl.core +{ + /// + /// Result set processor _prototype for the simplest case: no aggregation functions used in the select clause, and no group-by. + /// + public class ResultSetProcessorSimpleFactory : ResultSetProcessorFactory + { + private readonly bool _isSelectRStream; + private readonly SelectExprProcessor _selectExprProcessor; + private readonly ExprEvaluator _optionalHavingExpr; + private readonly OutputLimitSpec _outputLimitSpec; + + /// Ctor. + /// for processing the select expression and generting the readonly output rows + /// having clause expression node + /// true if remove stream events should be generated + public ResultSetProcessorSimpleFactory( + SelectExprProcessor selectExprProcessor, + ExprEvaluator optionalHavingNode, + bool isSelectRStream, + OutputLimitSpec outputLimitSpec, + bool enableOutputLimitOpt, + ResultSetProcessorHelperFactory resultSetProcessorHelperFactory, + int numStreams) + { + _selectExprProcessor = selectExprProcessor; + _optionalHavingExpr = optionalHavingNode; + _isSelectRStream = isSelectRStream; + _outputLimitSpec = outputLimitSpec; + this.IsEnableOutputLimitOpt = enableOutputLimitOpt; + this.ResultSetProcessorHelperFactory = resultSetProcessorHelperFactory; + this.NumStreams = numStreams; + } + + public ResultSetProcessorType ResultSetProcessorType + { + get { return ResultSetProcessorType.UNAGGREGATED_UNGROUPED; } + } + + public ResultSetProcessor Instantiate(OrderByProcessor orderByProcessor, AggregationService aggregationService, AgentInstanceContext agentInstanceContext) + { + return new ResultSetProcessorSimple(this, _selectExprProcessor, orderByProcessor, agentInstanceContext); + } + + public EventType ResultEventType + { + get { return _selectExprProcessor.ResultEventType; } + } + + public bool HasAggregation + { + get { return false; } + } + + public bool IsSelectRStream + { + get { return _isSelectRStream; } + } + + public ExprEvaluator OptionalHavingExpr + { + get { return _optionalHavingExpr; } + } + + public bool IsOutputLast + { + get { return _outputLimitSpec != null && _outputLimitSpec.DisplayLimit == OutputLimitLimitType.LAST; } + } + + public bool IsOutputAll + { + get { return _outputLimitSpec != null && _outputLimitSpec.DisplayLimit == OutputLimitLimitType.ALL; } + } + + public bool IsEnableOutputLimitOpt { get; private set; } + + public ResultSetProcessorHelperFactory ResultSetProcessorHelperFactory { get; private set; } + + public int NumStreams { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorSimpleOutputAllHelper.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorSimpleOutputAllHelper.cs new file mode 100755 index 000000000..8e412ba1e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorSimpleOutputAllHelper.cs @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; + +namespace com.espertech.esper.epl.core +{ + public interface ResultSetProcessorSimpleOutputAllHelper + : ResultSetProcessorOutputHelper + { + void ProcessView(EventBean[] newData, EventBean[] oldData); + void ProcessJoin(ISet> newEvents, ISet> oldEvents); + UniformPair OutputView(bool isSynthesize); + UniformPair OutputJoin(bool isSynthesize); + void Destroy(); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorSimpleOutputAllHelperImpl.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorSimpleOutputAllHelperImpl.cs new file mode 100755 index 000000000..656f4fb52 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorSimpleOutputAllHelperImpl.cs @@ -0,0 +1,132 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.core +{ + public class ResultSetProcessorSimpleOutputAllHelperImpl : ResultSetProcessorSimpleOutputAllHelper + { + private readonly ResultSetProcessorSimple processor; + + private readonly Deque eventsNewView = new ArrayDeque(2); + private readonly Deque eventsOldView = new ArrayDeque(2); + private readonly Deque> eventsNewJoin = new ArrayDeque>(2); + private readonly Deque> eventsOldJoin = new ArrayDeque>(2); + + public ResultSetProcessorSimpleOutputAllHelperImpl(ResultSetProcessorSimple processor) { + this.processor = processor; + } + + public void ProcessView(EventBean[] newData, EventBean[] oldData) { + if (processor.Prototype.OptionalHavingExpr == null) { + AddToView(newData, oldData); + return; + } + + var eventsPerStream = new EventBean[1]; + if (newData != null && newData.Length > 0) { + var evaluateParams = new EvaluateParams(eventsPerStream, true, processor.ExprEvaluatorContext); + foreach (var theEvent in newData) { + eventsPerStream[0] = theEvent; + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QHavingClauseNonJoin(theEvent);} + var passesHaving = processor.Prototype.OptionalHavingExpr.Evaluate(evaluateParams); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AHavingClauseNonJoin(passesHaving.AsBoxedBoolean());} + if ((passesHaving == null) || (false.Equals(passesHaving))) { + continue; + } + eventsNewView.Add(theEvent); + } + } + if (oldData != null && oldData.Length > 0) { + var evaluateParams = new EvaluateParams(eventsPerStream, false, processor.ExprEvaluatorContext); + foreach (var theEvent in oldData) { + eventsPerStream[0] = theEvent; + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QHavingClauseNonJoin(theEvent);} + var passesHaving = processor.Prototype.OptionalHavingExpr.Evaluate(evaluateParams); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AHavingClauseNonJoin(passesHaving.AsBoxedBoolean()); } + if ((passesHaving == null) || (false.Equals(passesHaving))) { + continue; + } + eventsOldView.Add(theEvent); + } + } + } + + public void ProcessJoin(ISet> newEvents, ISet> oldEvents) { + if (processor.Prototype.OptionalHavingExpr == null) { + AddToJoin(newEvents, oldEvents); + return; + } + + if (newEvents != null && newEvents.Count > 0) { + foreach (var theEvent in newEvents) { + var evaluateParams = new EvaluateParams(theEvent.Array, true, processor.ExprEvaluatorContext); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QHavingClauseJoin(theEvent.Array); } + var passesHaving = processor.Prototype.OptionalHavingExpr.Evaluate(evaluateParams); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AHavingClauseJoin(passesHaving.AsBoxedBoolean()); } + if ((passesHaving == null) || (false.Equals(passesHaving))) { + continue; + } + eventsNewJoin.Add(theEvent); + } + } + if (oldEvents != null && oldEvents.Count > 0) { + foreach (var theEvent in oldEvents) { + var evaluateParams = new EvaluateParams(theEvent.Array, false, processor.ExprEvaluatorContext); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QHavingClauseJoin(theEvent.Array);} + var passesHaving = processor.Prototype.OptionalHavingExpr.Evaluate(evaluateParams); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AHavingClauseJoin(passesHaving.AsBoxedBoolean()); } + if ((passesHaving == null) || (false.Equals(passesHaving))) { + continue; + } + eventsOldJoin.Add(theEvent); + } + } + } + + public UniformPair OutputView(bool isSynthesize) { + var pair = processor.ProcessViewResult(EventBeanUtility.ToArrayNullIfEmpty(eventsNewView), EventBeanUtility.ToArrayNullIfEmpty(eventsOldView), isSynthesize); + eventsNewView.Clear(); + eventsOldView.Clear(); + return pair; + } + + public UniformPair OutputJoin(bool isSynthesize) { + var pair = processor.ProcessJoinResult(EventBeanUtility.ToLinkedHashSetNullIfEmpty(eventsNewJoin), EventBeanUtility.ToLinkedHashSetNullIfEmpty(eventsOldJoin), isSynthesize); + eventsNewJoin.Clear(); + eventsOldJoin.Clear(); + return pair; + } + + public void Destroy() { + // no action required + } + + private void AddToView(EventBean[] newData, EventBean[] oldData) { + EventBeanUtility.AddToCollection(newData, eventsNewView); + EventBeanUtility.AddToCollection(oldData, eventsOldView); + } + + private void AddToJoin(ISet> newEvents, ISet> oldEvents) { + EventBeanUtility.AddToCollection(newEvents, eventsNewJoin); + EventBeanUtility.AddToCollection(oldEvents, eventsOldJoin); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorSimpleOutputLastHelper.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorSimpleOutputLastHelper.cs new file mode 100755 index 000000000..ef845b181 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorSimpleOutputLastHelper.cs @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; + +namespace com.espertech.esper.epl.core +{ + public interface ResultSetProcessorSimpleOutputLastHelper + : ResultSetProcessorOutputHelper + { + void ProcessView(EventBean[] newData, EventBean[] oldData); + void ProcessJoin(ISet> newEvents, ISet> oldEvents); + UniformPair OutputView(bool isSynthesize); + UniformPair OutputJoin(bool isSynthesize); + void Destroy(); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorSimpleOutputLastHelperImpl.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorSimpleOutputLastHelperImpl.cs new file mode 100755 index 000000000..3a6dafa12 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorSimpleOutputLastHelperImpl.cs @@ -0,0 +1,140 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.core +{ + public class ResultSetProcessorSimpleOutputLastHelperImpl : ResultSetProcessorSimpleOutputLastHelper + { + private readonly ResultSetProcessorSimple _processor; + + private EventBean _outputLastIStreamBufView; + private EventBean _outputLastRStreamBufView; + private MultiKey _outputLastIStreamBufJoin; + private MultiKey _outputLastRStreamBufJoin; + + public ResultSetProcessorSimpleOutputLastHelperImpl(ResultSetProcessorSimple processor) { + _processor = processor; + } + + public void ProcessView(EventBean[] newData, EventBean[] oldData) { + if (_processor.Prototype.OptionalHavingExpr == null) { + if (newData != null && newData.Length > 0) { + _outputLastIStreamBufView = newData[newData.Length - 1]; + } + if (oldData != null && oldData.Length > 0) { + _outputLastRStreamBufView = oldData[oldData.Length - 1]; + } + } + else { + EventBean[] eventsPerStream = new EventBean[1]; + if (newData != null && newData.Length > 0) { + var evaluateParams = new EvaluateParams(eventsPerStream, true, _processor.ExprEvaluatorContext); + foreach (EventBean theEvent in newData) + { + eventsPerStream[0] = theEvent; + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QHavingClauseNonJoin(theEvent);} + var passesHaving = _processor.Prototype.OptionalHavingExpr.Evaluate(evaluateParams); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AHavingClauseNonJoin(passesHaving.AsBoxedBoolean()); } + if ((passesHaving == null) || (false.Equals(passesHaving))) { + continue; + } + _outputLastIStreamBufView = theEvent; + } + } + if (oldData != null && oldData.Length > 0) { + var evaluateParams = new EvaluateParams(eventsPerStream, false, _processor.ExprEvaluatorContext); + foreach (EventBean theEvent in oldData) + { + eventsPerStream[0] = theEvent; + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QHavingClauseNonJoin(theEvent);} + var passesHaving = _processor.Prototype.OptionalHavingExpr.Evaluate(evaluateParams); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AHavingClauseNonJoin(passesHaving.AsBoxedBoolean()); } + if ((passesHaving == null) || (false.Equals(passesHaving))) { + continue; + } + _outputLastRStreamBufView = theEvent; + } + } + } + } + + public void ProcessJoin(ISet> newEvents, ISet> oldEvents) { + if (_processor.Prototype.OptionalHavingExpr == null) { + if (newEvents != null && !newEvents.IsEmpty()) { + _outputLastIStreamBufJoin = EventBeanUtility.GetLastInSet(newEvents); + } + if (oldEvents != null && !oldEvents.IsEmpty()) { + _outputLastRStreamBufJoin = EventBeanUtility.GetLastInSet(oldEvents); + } + } + else { + if (newEvents != null && newEvents.Count > 0) { + foreach (MultiKey theEvent in newEvents) { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QHavingClauseJoin(theEvent.Array);} + var evaluateParams = new EvaluateParams(theEvent.Array, true, _processor.ExprEvaluatorContext); + var passesHaving = _processor.Prototype.OptionalHavingExpr.Evaluate(evaluateParams); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AHavingClauseJoin(passesHaving.AsBoxedBoolean()); } + if ((passesHaving == null) || (false.Equals(passesHaving))) { + continue; + } + _outputLastIStreamBufJoin = theEvent; + } + } + if (oldEvents != null && oldEvents.Count > 0) { + foreach (MultiKey theEvent in oldEvents) { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QHavingClauseJoin(theEvent.Array);} + var evaluateParams = new EvaluateParams(theEvent.Array, false, _processor.ExprEvaluatorContext); + var passesHaving = _processor.Prototype.OptionalHavingExpr.Evaluate(evaluateParams); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AHavingClauseJoin(passesHaving.AsBoxedBoolean()); } + if ((passesHaving == null) || (false.Equals(passesHaving))) { + continue; + } + _outputLastRStreamBufJoin = theEvent; + } + } + } + } + + public UniformPair OutputView(bool isSynthesize) { + if (_outputLastIStreamBufView == null && _outputLastRStreamBufView == null) { + return null; + } + UniformPair pair = _processor.ProcessViewResult(EventBeanUtility.ToArrayIfNotNull(_outputLastIStreamBufView), EventBeanUtility.ToArrayIfNotNull(_outputLastRStreamBufView), isSynthesize); + _outputLastIStreamBufView = null; + _outputLastRStreamBufView = null; + return pair; + } + + public UniformPair OutputJoin(bool isSynthesize) { + if (_outputLastIStreamBufJoin == null && _outputLastRStreamBufJoin == null) { + return null; + } + UniformPair pair = _processor.ProcessJoinResult(EventBeanUtility.ToSingletonSetIfNotNull(_outputLastIStreamBufJoin), EventBeanUtility.ToSingletonSetIfNotNull(_outputLastRStreamBufJoin), isSynthesize); + _outputLastIStreamBufJoin = null; + _outputLastRStreamBufJoin = null; + return pair; + } + + public void Destroy() { + // no action required + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorSimpleTransform.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorSimpleTransform.cs new file mode 100755 index 000000000..d8e718320 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorSimpleTransform.cs @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.collection; + +namespace com.espertech.esper.epl.core +{ + /// + /// Method to transform an event based on the select expression. + /// + public class ResultSetProcessorSimpleTransform + { + private readonly ResultSetProcessorBaseSimple _resultSetProcessor; + private readonly EventBean[] _newData; + + /// Ctor. + /// is applying the select expressions to the events for the transformation + public ResultSetProcessorSimpleTransform(ResultSetProcessorBaseSimple resultSetProcessor) { + _resultSetProcessor = resultSetProcessor; + _newData = new EventBean[1]; + } + + public EventBean Transform(EventBean theEvent) + { + _newData[0] = theEvent; + var pair = _resultSetProcessor.ProcessViewResult(_newData, null, true); + if (pair == null) + return null; + if (pair.First == null) + return null; + return pair.First[0]; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorType.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorType.cs new file mode 100755 index 000000000..2cec80130 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorType.cs @@ -0,0 +1,21 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.core +{ + public enum ResultSetProcessorType + { + HANDTHROUGH, + UNAGGREGATED_UNGROUPED, + FULLYAGGREGATED_UNGROUPED, + AGGREGATED_UNGROUPED, + FULLYAGGREGATED_GROUPED, + FULLYAGGREGATED_GROUPED_ROLLUP, + AGGREGATED_GROUPED, + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorUtil.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorUtil.cs new file mode 100755 index 000000000..377339f13 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetProcessorUtil.cs @@ -0,0 +1,592 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.view; + +namespace com.espertech.esper.epl.core +{ + public class ResultSetProcessorUtil + { + public static void ApplyAggViewResult( + AggregationService aggregationService, + ExprEvaluatorContext exprEvaluatorContext, + EventBean[] newData, + EventBean[] oldData, + EventBean[] eventsPerStream) + { + if (newData != null) + { + // apply new data to aggregates + for (int i = 0; i < newData.Length; i++) + { + eventsPerStream[0] = newData[i]; + aggregationService.ApplyEnter(eventsPerStream, null, exprEvaluatorContext); + } + } + if (oldData != null) + { + // apply old data to aggregates + for (int i = 0; i < oldData.Length; i++) + { + eventsPerStream[0] = oldData[i]; + aggregationService.ApplyLeave(eventsPerStream, null, exprEvaluatorContext); + } + } + } + + public static void ApplyAggJoinResult( + AggregationService aggregationService, + ExprEvaluatorContext exprEvaluatorContext, + ISet> newEvents, + ISet> oldEvents) + { + if (!newEvents.IsEmpty()) + { + // apply new data to aggregates + foreach (MultiKey events in newEvents) + { + aggregationService.ApplyEnter(events.Array, null, exprEvaluatorContext); + } + } + if (!oldEvents.IsEmpty()) + { + // apply old data to aggregates + foreach (MultiKey events in oldEvents) + { + aggregationService.ApplyLeave(events.Array, null, exprEvaluatorContext); + } + } + } + + /// + /// Applies the select-clause to the given events returning the selected events. The number of + /// events stays the same, i.e. this method does not filter e just transforms the result set. + /// + /// processes each input event and returns output event + /// input events + /// indicates whether we are dealing with new data (istream) or old data (rstream) + /// set to true to indicate that synthetic events are required for an iterator result set + /// context for expression evalauation + /// output events, one for each input event + internal static EventBean[] GetSelectEventsNoHaving(SelectExprProcessor exprProcessor, EventBean[] events, bool isNewData, bool isSynthesize, ExprEvaluatorContext exprEvaluatorContext) + { + if (events == null) { + return null; + } + + var result = new EventBean[events.Length]; + var eventsPerStream = new EventBean[1]; + for (var i = 0; i < events.Length; i++) { + eventsPerStream[0] = events[i]; + result[i] = exprProcessor.Process(eventsPerStream, isNewData, isSynthesize, exprEvaluatorContext); + } + return result; + } + + /// + /// Applies the select-clause to the given events returning the selected events. The number of events stays + /// the same, i.e. this method does not filter e just transforms the result set. + /// + /// processes each input event and returns output event + /// orders the outgoing events according to the order-by clause + /// input events + /// indicates whether we are dealing with new data (istream) or old data (rstream) + /// set to true to indicate that synthetic events are required for an iterator result set + /// context for expression evalauation + /// output events, one for each input event + internal static EventBean[] GetSelectEventsNoHavingWithOrderBy(SelectExprProcessor exprProcessor, OrderByProcessor orderByProcessor, EventBean[] events, bool isNewData, bool isSynthesize, ExprEvaluatorContext exprEvaluatorContext) + { + if (events == null) { + return null; + } + + var result = new EventBean[events.Length]; + var eventGenerators = new EventBean[events.Length][]; + + var eventsPerStream = new EventBean[1]; + for (var i = 0; i < events.Length; i++) { + eventsPerStream[0] = events[i]; + result[i] = exprProcessor.Process(eventsPerStream, isNewData, isSynthesize, exprEvaluatorContext); + eventGenerators[i] = new EventBean[] {events[i]}; + } + + return orderByProcessor.Sort(result, eventGenerators, isNewData, exprEvaluatorContext); + } + + /// + /// Applies the select-clause to the given events returning the selected events. The number of events stays + /// the same, i.e. this method does not filter e just transforms the result set. + /// + /// Also applies a having clause. + /// + /// processes each input event and returns output event + /// for sorting output events according to the order-by clause + /// input events + /// supplies the having-clause expression + /// indicates whether we are dealing with new data (istream) or old data (rstream) + /// set to true to indicate that synthetic events are required for an iterator result set + /// context for expression evalauation + /// output events, one for each input event + internal static EventBean[] GetSelectEventsHavingWithOrderBy(SelectExprProcessor exprProcessor, OrderByProcessor orderByProcessor, EventBean[] events, ExprEvaluator havingNode, bool isNewData, bool isSynthesize, ExprEvaluatorContext exprEvaluatorContext) + { + if (events == null) { + return null; + } + + ArrayDeque result = null; + ArrayDeque eventGenerators = null; + + var eventsPerStream = new EventBean[1]; + var evaluateParams = new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext); + + foreach (var theEvent in events) + { + eventsPerStream[0] = theEvent; + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QHavingClauseNonJoin(theEvent);} + var passesHaving = havingNode.Evaluate(evaluateParams); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AHavingClauseNonJoin(passesHaving.AsBoxedBoolean());} + if ((passesHaving == null) || (false.Equals(passesHaving))) { + continue; + } + + var generated = exprProcessor.Process(eventsPerStream, isNewData, isSynthesize, exprEvaluatorContext); + if (generated != null) { + if (result == null) { + result = new ArrayDeque(events.Length); + eventGenerators = new ArrayDeque(events.Length); + } + result.Add(generated); + eventGenerators.Add(new EventBean[]{theEvent}); + } + } + + if (result != null) { + return orderByProcessor.Sort(result.ToArray(), eventGenerators.ToArray(), isNewData, exprEvaluatorContext); + } + return null; + } + + /// + /// Applies the select-clause to the given events returning the selected events. The number of events stays + /// the same, i.e. this method does not filter e just transforms the result set. + /// + /// Also applies a having clause. + /// + /// processes each input event and returns output event + /// input events + /// supplies the having-clause expression + /// indicates whether we are dealing with new data (istream) or old data (rstream) + /// set to true to indicate that synthetic events are required for an iterator result set + /// context for expression evalauation + /// output events, one for each input event + internal static EventBean[] GetSelectEventsHaving(SelectExprProcessor exprProcessor, + EventBean[] events, + ExprEvaluator havingNode, + bool isNewData, + bool isSynthesize, + ExprEvaluatorContext exprEvaluatorContext) + { + if (events == null) { + return null; + } + + ArrayDeque result = null; + var eventsPerStream = new EventBean[1]; + var evaluateParams = new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext); + + foreach (var theEvent in events) { + eventsPerStream[0] = theEvent; + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QHavingClauseNonJoin(theEvent);} + var passesHaving = havingNode.Evaluate(evaluateParams); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AHavingClauseNonJoin(passesHaving.AsBoxedBoolean()); } + if ((passesHaving == null) || (false.Equals(passesHaving))) { + continue; + } + + var generated = exprProcessor.Process(eventsPerStream, isNewData, isSynthesize, exprEvaluatorContext); + if (generated != null) { + if (result == null) { + result = new ArrayDeque(events.Length); + } + result.Add(generated); + } + } + + if (result != null) { + return result.ToArray(); + } + return null; + } + + /// + /// Applies the select-clause to the given events returning the selected events. The number of events stays the + /// same, i.e. this method does not filter e just transforms the result set. + /// + /// processes each input event and returns output event + /// for sorting output events according to the order-by clause + /// input events + /// indicates whether we are dealing with new data (istream) or old data (rstream) + /// set to true to indicate that synthetic events are required for an iterator result set + /// context for expression evalauation + /// output events, one for each input event + internal static EventBean[] GetSelectJoinEventsNoHavingWithOrderBy(SelectExprProcessor exprProcessor, OrderByProcessor orderByProcessor, ICollection> events, bool isNewData, bool isSynthesize, ExprEvaluatorContext exprEvaluatorContext) + { + if ((events == null) || (events.IsEmpty())) { + return null; + } + + var result = new EventBean[events.Count]; + var eventGenerators = new EventBean[events.Count][]; + + var count = 0; + foreach (var key in events) { + var eventsPerStream = key.Array; + result[count] = exprProcessor.Process(eventsPerStream, isNewData, isSynthesize, exprEvaluatorContext); + eventGenerators[count] = eventsPerStream; + count++; + } + + return orderByProcessor.Sort(result, eventGenerators, isNewData, exprEvaluatorContext); + } + + /// + /// Applies the select-clause to the given events returning the selected events. The number of events stays the + /// same, i.e. this method does not filter e just transforms the result set. + /// + /// processes each input event and returns output event + /// input events + /// indicates whether we are dealing with new data (istream) or old data (rstream) + /// set to true to indicate that synthetic events are required for an iterator result set + /// context for expression evalauation + /// output events, one for each input event + internal static EventBean[] GetSelectJoinEventsNoHaving(SelectExprProcessor exprProcessor, ICollection> events, bool isNewData, bool isSynthesize, ExprEvaluatorContext exprEvaluatorContext) + { + if ((events == null) || (events.IsEmpty())) { + return null; + } + + var result = new EventBean[events.Count]; + var count = 0; + + foreach (var key in events) { + var eventsPerStream = key.Array; + result[count] = exprProcessor.Process(eventsPerStream, isNewData, isSynthesize, exprEvaluatorContext); + count++; + } + + return result; + } + + /// + /// Applies the select-clause to the given events returning the selected events. The number of events stays the + /// same, i.e. this method does not filter e just transforms the result set. + /// + /// Also applies a having clause. + /// + /// processes each input event and returns output event + /// input events + /// supplies the having-clause expression + /// indicates whether we are dealing with new data (istream) or old data (rstream) + /// set to true to indicate that synthetic events are required for an iterator result set + /// context for expression evalauation + /// output events, one for each input event + internal static EventBean[] GetSelectJoinEventsHaving(SelectExprProcessor exprProcessor, ICollection> events, ExprEvaluator havingNode, bool isNewData, bool isSynthesize, ExprEvaluatorContext exprEvaluatorContext) + { + if ((events == null) || (events.IsEmpty())) { + return null; + } + + ArrayDeque result = null; + + foreach (var key in events) { + var eventsPerStream = key.Array; + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QHavingClauseJoin(eventsPerStream);} + var evaluateParams = new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext); + var passesHaving = havingNode.Evaluate(evaluateParams); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AHavingClauseJoin(passesHaving.AsBoxedBoolean()); } + if ((passesHaving == null) || (false.Equals(passesHaving))) + { + continue; + } + + var resultEvent = exprProcessor.Process(eventsPerStream, isNewData, isSynthesize, exprEvaluatorContext); + if (resultEvent != null) { + if (result == null) { + result = new ArrayDeque(events.Count); + } + result.Add(resultEvent); + } + } + + if (result != null) { + return result.ToArray(); + } + return null; + } + + /// + /// Applies the select-clause to the given events returning the selected events. The number of events stays the + /// same, i.e. this method does not filter e just transforms the result set. Also applies a having clause. + /// + /// processes each input event and returns output event + /// for sorting output events according to the order-by clause + /// input events + /// supplies the having-clause expression + /// indicates whether we are dealing with new data (istream) or old data (rstream) + /// set to true to indicate that synthetic events are required for an iterator result set + /// context for expression evalauation + /// output events, one for each input event + internal static EventBean[] GetSelectJoinEventsHavingWithOrderBy(SelectExprProcessor exprProcessor, OrderByProcessor orderByProcessor, ICollection> events, ExprEvaluator havingNode, bool isNewData, bool isSynthesize, ExprEvaluatorContext exprEvaluatorContext) + { + if ((events == null) || (events.IsEmpty())) { + return null; + } + + ArrayDeque result = null; + ArrayDeque eventGenerators = null; + + foreach (var key in events) { + var eventsPerStream = key.Array; + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QHavingClauseJoin(eventsPerStream);} + var evaluateParams = new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext); + var passesHaving = havingNode.Evaluate(evaluateParams); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AHavingClauseJoin(passesHaving.AsBoxedBoolean()); } + if ((passesHaving == null) || (false.Equals(passesHaving))) + { + continue; + } + + var resultEvent = exprProcessor.Process(eventsPerStream, isNewData, isSynthesize, exprEvaluatorContext); + if (resultEvent != null) { + if (result == null) { + result = new ArrayDeque(events.Count); + eventGenerators = new ArrayDeque(events.Count); + } + result.Add(resultEvent); + eventGenerators.Add(eventsPerStream); + } + } + + if (result != null) { + return orderByProcessor.Sort(result.ToArray(), eventGenerators.ToArray(), isNewData, exprEvaluatorContext); + } + return null; + } + + internal static void PopulateSelectEventsNoHaving(SelectExprProcessor exprProcessor, EventBean[] events, bool isNewData, bool isSynthesize, ICollection result, ExprEvaluatorContext exprEvaluatorContext) + { + if (events == null) { + return; + } + + var eventsPerStream = new EventBean[1]; + foreach (var theEvent in events) { + eventsPerStream[0] = theEvent; + + var resultEvent = exprProcessor.Process(eventsPerStream, isNewData, isSynthesize, exprEvaluatorContext); + if (resultEvent != null) { + result.Add(resultEvent); + } + } + } + + internal static void PopulateSelectEventsNoHavingWithOrderBy(SelectExprProcessor exprProcessor, OrderByProcessor orderByProcessor, EventBean[] events, bool isNewData, bool isSynthesize, ICollection result, ICollection sortKeys, ExprEvaluatorContext exprEvaluatorContext) + { + if (events == null) { + return; + } + + var eventsPerStream = new EventBean[1]; + foreach (var theEvent in events) { + eventsPerStream[0] = theEvent; + + var resultEvent = exprProcessor.Process(eventsPerStream, isNewData, isSynthesize, exprEvaluatorContext); + if (resultEvent != null) { + result.Add(resultEvent); + sortKeys.Add(orderByProcessor.GetSortKey(eventsPerStream, isNewData, exprEvaluatorContext)); + } + } + } + + internal static void PopulateSelectEventsHaving(SelectExprProcessor exprProcessor, EventBean[] events, ExprEvaluator havingNode, bool isNewData, bool isSynthesize, ICollection result, ExprEvaluatorContext exprEvaluatorContext) + { + if (events == null) { + return; + } + + var eventsPerStream = new EventBean[1]; + var evaluateParams = new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext); + foreach (var theEvent in events) + { + eventsPerStream[0] = theEvent; + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QHavingClauseNonJoin(theEvent);} + var passesHaving = havingNode.Evaluate(evaluateParams); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AHavingClauseNonJoin(passesHaving.AsBoxedBoolean()); } + if ((passesHaving == null) || (false.Equals(passesHaving))) + { + continue; + } + + var resultEvent = exprProcessor.Process(eventsPerStream, isNewData, isSynthesize, exprEvaluatorContext); + if (resultEvent != null) { + result.Add(resultEvent); + } + } + } + + internal static void PopulateSelectEventsHavingWithOrderBy(SelectExprProcessor exprProcessor, OrderByProcessor orderByProcessor, EventBean[] events, ExprEvaluator havingNode, bool isNewData, bool isSynthesize, ICollection result, ICollection optSortKeys, ExprEvaluatorContext exprEvaluatorContext) + { + if (events == null) { + return; + } + + var eventsPerStream = new EventBean[1]; + var evaluateParams = new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext); + foreach (var theEvent in events) + { + eventsPerStream[0] = theEvent; + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QHavingClauseNonJoin(theEvent);} + var passesHaving = havingNode.Evaluate(evaluateParams); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AHavingClauseNonJoin(passesHaving.AsBoxedBoolean()); } + if ((passesHaving == null) || (false.Equals(passesHaving))) + { + continue; + } + + var resultEvent = exprProcessor.Process(eventsPerStream, isNewData, isSynthesize, exprEvaluatorContext); + if (resultEvent != null) { + result.Add(resultEvent); + optSortKeys.Add(orderByProcessor.GetSortKey(eventsPerStream, isNewData, exprEvaluatorContext)); + } + } + } + + internal static void PopulateSelectJoinEventsHaving(SelectExprProcessor exprProcessor, ICollection> events, ExprEvaluator havingNode, bool isNewData, bool isSynthesize, ICollection result, ExprEvaluatorContext exprEvaluatorContext) + { + if (events == null) { + return; + } + + foreach (var key in events) { + var eventsPerStream = key.Array; + var evaluateParams = new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext); + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QHavingClauseJoin(eventsPerStream);} + var passesHaving = havingNode.Evaluate(evaluateParams); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AHavingClauseJoin(passesHaving.AsBoxedBoolean()); } + if ((passesHaving == null) || (false.Equals(passesHaving))) { + continue; + } + + var resultEvent = exprProcessor.Process(eventsPerStream, isNewData, isSynthesize, exprEvaluatorContext); + if (resultEvent != null) { + result.Add(resultEvent); + } + } + } + + internal static void PopulateSelectJoinEventsHavingWithOrderBy(SelectExprProcessor exprProcessor, OrderByProcessor orderByProcessor, ICollection> events, ExprEvaluator havingNode, bool isNewData, bool isSynthesize, ICollection result, ICollection sortKeys, ExprEvaluatorContext exprEvaluatorContext) + { + if (events == null) { + return; + } + + foreach (var key in events) { + var eventsPerStream = key.Array; + var evaluateParams = new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext); + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QHavingClauseJoin(eventsPerStream);} + var passesHaving = havingNode.Evaluate(evaluateParams); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AHavingClauseJoin(passesHaving.AsBoxedBoolean()); } + if ((passesHaving == null) || (false.Equals(passesHaving))) { + continue; + } + + var resultEvent = exprProcessor.Process(eventsPerStream, isNewData, isSynthesize, exprEvaluatorContext); + if (resultEvent != null) { + result.Add(resultEvent); + sortKeys.Add(orderByProcessor.GetSortKey(eventsPerStream, isNewData, exprEvaluatorContext)); + } + } + } + + internal static void PopulateSelectJoinEventsNoHaving(SelectExprProcessor exprProcessor, ICollection> events, bool isNewData, bool isSynthesize, ICollection result, ExprEvaluatorContext exprEvaluatorContext) + { + var length = (events != null) ? events.Count : 0; + if (length == 0) { + return; + } + + foreach (var key in events) { + var eventsPerStream = key.Array; + var resultEvent = exprProcessor.Process(eventsPerStream, isNewData, isSynthesize, exprEvaluatorContext); + if (resultEvent != null) { + result.Add(resultEvent); + } + } + } + + internal static void PopulateSelectJoinEventsNoHavingWithOrderBy(SelectExprProcessor exprProcessor, OrderByProcessor orderByProcessor, ICollection> events, bool isNewData, bool isSynthesize, ICollection result, ICollection optSortKeys, ExprEvaluatorContext exprEvaluatorContext) + { + var length = (events != null) ? events.Count : 0; + if (length == 0) { + return; + } + + foreach (var key in events) + { + var eventsPerStream = key.Array; + var resultEvent = exprProcessor.Process(eventsPerStream, isNewData, isSynthesize, exprEvaluatorContext); + if (resultEvent != null) { + result.Add(resultEvent); + optSortKeys.Add(orderByProcessor.GetSortKey(eventsPerStream, isNewData, exprEvaluatorContext)); + } + } + } + + public static void ClearAndAggregateUngrouped(ExprEvaluatorContext exprEvaluatorContext, AggregationService aggregationService, Viewable parent) + { + aggregationService.ClearResults(exprEvaluatorContext); + var ee = parent.GetEnumerator(); + var eventsPerStream = new EventBean[1]; + while(ee.MoveNext()) + { + eventsPerStream[0] = ee.Current; + aggregationService.ApplyEnter(eventsPerStream, null, exprEvaluatorContext); + } + } + + public static ArrayDeque EnumeratorToDeque(IEnumerator e) + { + var deque = new ArrayDeque(); + while(e.MoveNext()) + { + deque.Add(e.Current); + } + return deque; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/ResultSetRowPerGroupEnumerator.cs b/NEsper.Core/NEsper.Core/epl/core/ResultSetRowPerGroupEnumerator.cs new file mode 100755 index 000000000..e3ea871e3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ResultSetRowPerGroupEnumerator.cs @@ -0,0 +1,92 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.core +{ + public class ResultSetRowPerGroupEnumerator + { + /// + /// Creates a new enumeration wrapper. + /// + /// The source. + /// The result set processor. + /// The aggregation service. + /// The expr evaluator context. + /// + public static IEnumerator New( + IEnumerable source, + ResultSetProcessorRowPerGroup resultSetProcessor, + AggregationService aggregationService, + ExprEvaluatorContext exprEvaluatorContext) + { + return New( + source.GetEnumerator(), + resultSetProcessor, + aggregationService, + exprEvaluatorContext); + } + + /// + /// Creates a new enumeration wrapper. + /// + /// The source. + /// The result set processor. + /// The aggregation service. + /// The expr evaluator context. + /// + public static IEnumerator New( + IEnumerator source, + ResultSetProcessorRowPerGroup resultSetProcessor, + AggregationService aggregationService, + ExprEvaluatorContext exprEvaluatorContext) + { + var eventsPerStream = new EventBean[1]; + var priorSeenGroups = new HashSet(); + var evaluateParams = new EvaluateParams(eventsPerStream, true, exprEvaluatorContext); + var optionHavingNode = resultSetProcessor.OptionalHavingNode; + var selectExprProcessor = resultSetProcessor.SelectExprProcessor; + + while(source.MoveNext()) + { + var candidate = source.Current; + eventsPerStream[0] = candidate; + var groupKey = resultSetProcessor.GenerateGroupKey(eventsPerStream, true); + aggregationService.SetCurrentAccess(groupKey, exprEvaluatorContext.AgentInstanceId, null); + + if (optionHavingNode != null) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QHavingClauseNonJoin(candidate); } + var pass = resultSetProcessor.OptionalHavingNode.Evaluate(evaluateParams); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AHavingClauseJoin(pass.AsBoxedBoolean()); } + if ((pass != null) && false.Equals(pass)) + { + continue; + } + } + + if (priorSeenGroups.Contains(groupKey)) + { + continue; + } + + priorSeenGroups.Add(groupKey); + + yield return selectExprProcessor.Process(eventsPerStream, true, true, exprEvaluatorContext); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/RowLimitProcessor.cs b/NEsper.Core/NEsper.Core/epl/core/RowLimitProcessor.cs new file mode 100755 index 000000000..6f6b0800b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/RowLimitProcessor.cs @@ -0,0 +1,148 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.variable; + +namespace com.espertech.esper.epl.core +{ + /// An limit-processor for use with "limit" and "offset". + public class RowLimitProcessor + { + private readonly VariableReader _numRowsVariableReader; + private readonly VariableReader _offsetVariableReader; + private int _currentRowLimit; + private int _currentOffset; + + public RowLimitProcessor( + VariableReader numRowsVariableReader, + VariableReader offsetVariableReader, + int currentRowLimit, + int currentOffset) + { + _numRowsVariableReader = numRowsVariableReader; + _offsetVariableReader = offsetVariableReader; + _currentRowLimit = currentRowLimit; + _currentOffset = currentOffset; + } + + public int CurrentRowLimit + { + get { return _currentRowLimit; } + } + + public int CurrentOffset + { + get { return _currentOffset; } + } + + /// + /// Determine the current limit and applies the limiting function to outgoing events. + /// + /// unlimited + /// limited + internal EventBean[] DetermineLimitAndApply(EventBean[] outgoingEvents) + { + if (outgoingEvents == null) + { + return null; + } + DetermineCurrentLimit(); + return ApplyLimit(outgoingEvents); + } + + internal void DetermineCurrentLimit() + { + if (_numRowsVariableReader != null) + { + var varValue = _numRowsVariableReader.Value; + if (varValue != null) + { + _currentRowLimit = varValue.AsInt(); + } + else + { + _currentRowLimit = Int32.MaxValue; + } + if (_currentRowLimit < 0) + { + _currentRowLimit = Int32.MaxValue; + } + } + + if (_offsetVariableReader != null) + { + var varValue = _offsetVariableReader.Value; + if (varValue != null) + { + _currentOffset = varValue.AsInt(); + } + else + { + _currentOffset = 0; + } + if (_currentOffset < 0) + { + _currentOffset = 0; + } + } + } + + internal EventBean[] ApplyLimit(EventBean[] outgoingEvents) + { + // no offset + if (_currentOffset == 0) + { + if (outgoingEvents.Length <= _currentRowLimit) + { + return outgoingEvents; + } + + if (_currentRowLimit == 0) + { + return null; + } + + var limited = new EventBean[_currentRowLimit]; + Array.Copy(outgoingEvents, 0, limited, 0, _currentRowLimit); + return limited; + } + else + { + // with offset + int maxInterested = _currentRowLimit + _currentOffset; + if (_currentRowLimit == Int32.MaxValue) + { + maxInterested = Int32.MaxValue; + } + + // more rows then requested + if (outgoingEvents.Length > maxInterested) + { + var limitedX = new EventBean[_currentRowLimit]; + Array.Copy(outgoingEvents, _currentOffset, limitedX, 0, _currentRowLimit); + return limitedX; + } + + // less or equal rows to offset + if (outgoingEvents.Length <= _currentOffset) + { + return null; + } + + int size = outgoingEvents.Length - _currentOffset; + var limited = new EventBean[size]; + Array.Copy(outgoingEvents, _currentOffset, limited, 0, size); + return limited; + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/RowLimitProcessorFactory.cs b/NEsper.Core/NEsper.Core/epl/core/RowLimitProcessorFactory.cs new file mode 100755 index 000000000..611a3b99f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/RowLimitProcessorFactory.cs @@ -0,0 +1,110 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.variable; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.core +{ + /// + /// A factory for row-limit processor instances. + /// + public class RowLimitProcessorFactory + { + private readonly VariableMetaData _numRowsVariableMetaData; + private readonly VariableMetaData _offsetVariableMetaData; + private readonly int _currentRowLimit; + private readonly int _currentOffset; + + /// + /// Ctor. + /// + /// specification for row limit, or null if no row limit is defined + /// for retrieving variable state for use with row limiting + /// com.espertech.esper.epl.expression.core.ExprValidationException if row limit specification validation fails + public RowLimitProcessorFactory(RowLimitSpec rowLimitSpec, VariableService variableService, string optionalContextName) + { + if (rowLimitSpec.NumRowsVariable != null) + { + _numRowsVariableMetaData = variableService.GetVariableMetaData(rowLimitSpec.NumRowsVariable); + if (_numRowsVariableMetaData == null) { + throw new ExprValidationException("Limit clause variable by name '" + rowLimitSpec.NumRowsVariable + "' has not been declared"); + } + string message = VariableServiceUtil.CheckVariableContextName(optionalContextName, _numRowsVariableMetaData); + if (message != null) { + throw new ExprValidationException(message); + } + if (!TypeHelper.IsNumeric(_numRowsVariableMetaData.VariableType)) + { + throw new ExprValidationException("Limit clause requires a variable of numeric type"); + } + } + else + { + _numRowsVariableMetaData = null; + _currentRowLimit = rowLimitSpec.NumRows.GetValueOrDefault(); + + if (_currentRowLimit < 0) + { + _currentRowLimit = int.MaxValue; + } + } + + if (rowLimitSpec.OptionalOffsetVariable != null) + { + _offsetVariableMetaData = variableService.GetVariableMetaData(rowLimitSpec.OptionalOffsetVariable); + if (_offsetVariableMetaData == null) { + throw new ExprValidationException("Limit clause variable by name '" + rowLimitSpec.OptionalOffsetVariable + "' has not been declared"); + } + string message = VariableServiceUtil.CheckVariableContextName(optionalContextName, _offsetVariableMetaData); + if (message != null) { + throw new ExprValidationException(message); + } + if (!TypeHelper.IsNumeric(_offsetVariableMetaData.VariableType)) + { + throw new ExprValidationException("Limit clause requires a variable of numeric type"); + } + } + else + { + _offsetVariableMetaData = null; + if (rowLimitSpec.OptionalOffset != null) + { + _currentOffset = rowLimitSpec.OptionalOffset.GetValueOrDefault(); + + if (_currentOffset <= 0) + { + throw new ExprValidationException("Limit clause requires a positive offset"); + } + } + else + { + _currentOffset = 0; + } + } + } + + public RowLimitProcessor Instantiate(AgentInstanceContext agentInstanceContext) { + VariableReader numRowsVariableReader = null; + if (_numRowsVariableMetaData != null) { + numRowsVariableReader = agentInstanceContext.StatementContext.VariableService.GetReader(_numRowsVariableMetaData.VariableName, agentInstanceContext.AgentInstanceId); + } + + VariableReader offsetVariableReader = null; + if (_offsetVariableMetaData != null) { + offsetVariableReader = agentInstanceContext.StatementContext.VariableService.GetReader(_offsetVariableMetaData.VariableName, agentInstanceContext.AgentInstanceId); + } + + return new RowLimitProcessor(numRowsVariableReader, offsetVariableReader, + _currentRowLimit, _currentOffset); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/SelectExprEventTypeRegistry.cs b/NEsper.Core/NEsper.Core/epl/core/SelectExprEventTypeRegistry.cs new file mode 100755 index 000000000..8fda527ba --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/SelectExprEventTypeRegistry.cs @@ -0,0 +1,39 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.core.service; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.core +{ + /// Registry for event types creates as part of the select expression analysis. + public class SelectExprEventTypeRegistry + { + private readonly String statementName; + private readonly StatementEventTypeRef statementEventTypeRef; + + public SelectExprEventTypeRegistry(String statementName, StatementEventTypeRef statementEventTypeRef) { + this.statementName = statementName; + this.statementEventTypeRef = statementEventTypeRef; + } + + /// Adds an event type. + /// to add + public void Add(EventType eventType) + { + if (!(eventType is EventTypeSPI)) + { + return; + } + statementEventTypeRef.AddReferences(statementName, new String[]{((EventTypeSPI) eventType).Metadata.PrimaryName}); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/SelectExprInsertEventBeanFactory.cs b/NEsper.Core/NEsper.Core/epl/core/SelectExprInsertEventBeanFactory.cs new file mode 100755 index 000000000..fd15fa15b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/SelectExprInsertEventBeanFactory.cs @@ -0,0 +1,832 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; +using com.espertech.esper.events.avro; +using com.espertech.esper.events; +using com.espertech.esper.events.arr; +using com.espertech.esper.events.bean; +using com.espertech.esper.events.map; +using com.espertech.esper.util; + +using XLR8.CGLib; + +namespace com.espertech.esper.epl.core +{ + public class SelectExprInsertEventBeanFactory + { + public static SelectExprProcessor GetInsertUnderlyingNonJoin( + EventAdapterService eventAdapterService, + EventType eventType, + bool isUsingWildcard, + StreamTypeService typeService, + ExprEvaluator[] expressionNodes, + string[] columnNames, + Object[] expressionReturnTypes, + EngineImportService engineImportService, + InsertIntoDesc insertIntoDesc, + string[] columnNamesAsProvided, + bool allowNestableTargetFragmentTypes, + string statementName) + { + // handle single-column coercion to underlying, i.e. "insert into MapDefinedEvent select DoSomethingReturnMap() from MyEvent" + if (expressionReturnTypes.Length == 1 && + expressionReturnTypes[0] is Type && + (eventType is BaseNestableEventType || eventType is AvroSchemaEventType) && + TypeHelper.IsSubclassOrImplementsInterface((Type) expressionReturnTypes[0], eventType.UnderlyingType) && + insertIntoDesc.ColumnNames.IsEmpty() && + columnNamesAsProvided[0] == null) + { + + if (eventType is MapEventType) + { + return new SelectExprInsertNativeExpressionCoerceMap( + eventType, expressionNodes[0], eventAdapterService); + } + else if (eventType is ObjectArrayEventType) + { + return new SelectExprInsertNativeExpressionCoerceObjectArray( + eventType, expressionNodes[0], eventAdapterService); + } + else if (eventType is AvroSchemaEventType) + { + return new SelectExprInsertNativeExpressionCoerceAvro( + eventType, expressionNodes[0], eventAdapterService); + } + else + { + throw new IllegalStateException("Unrecognied event type " + eventType); + } + } + + // handle special case where the target type has no properties and there is a single "null" value selected + if (eventType.PropertyDescriptors.Count == 0 && + columnNames.Length == 1 && + columnNames[0].Equals("null") && + expressionReturnTypes[0] == null && + !isUsingWildcard) + { + + EventBeanManufacturer eventManufacturer; + try + { + eventManufacturer = eventAdapterService.GetManufacturer( + eventType, new WriteablePropertyDescriptor[0], engineImportService, true); + } + catch (EventBeanManufactureException e) + { + throw new ExprValidationException(e.Message, e); + } + return new SelectExprInsertNativeNoEval(eventType, eventManufacturer); + } + + // handle writing to defined columns + var writableProps = eventAdapterService.GetWriteableProperties(eventType, false); + var isEligible = CheckEligible(eventType, writableProps, allowNestableTargetFragmentTypes); + if (!isEligible) + { + return null; + } + + try + { + return InitializeSetterManufactor( + eventType, writableProps, isUsingWildcard, typeService, expressionNodes, columnNames, + expressionReturnTypes, engineImportService, eventAdapterService, statementName); + } + catch (ExprValidationException) + { + if (!(eventType is BeanEventType)) + { + throw; + } + // Try constructor injection + try + { + return InitializeCtorInjection( + (BeanEventType) eventType, expressionNodes, expressionReturnTypes, engineImportService, + eventAdapterService); + } + catch (ExprValidationException) + { + if (writableProps.IsEmpty()) + { + throw; + } + } + + throw; + } + } + + public static SelectExprProcessor GetInsertUnderlyingJoinWildcard( + EventAdapterService eventAdapterService, + EventType eventType, + string[] streamNames, + EventType[] streamTypes, + EngineImportService engineImportService, + string statementName, + string engineURI) + { + var writableProps = eventAdapterService.GetWriteableProperties(eventType, false); + var isEligible = CheckEligible(eventType, writableProps, false); + if (!isEligible) + { + return null; + } + + try + { + return InitializeJoinWildcardInternal( + eventType, writableProps, streamNames, streamTypes, engineImportService, eventAdapterService, + statementName, engineURI); + } + catch (ExprValidationException) + { + if (!(eventType is BeanEventType)) + { + throw; + } + // Try constructor injection + try + { + var evaluators = new ExprEvaluator[streamTypes.Length]; + var resultTypes = new Object[streamTypes.Length]; + for (var i = 0; i < streamTypes.Length; i++) + { + evaluators[i] = new ExprEvaluatorJoinWildcard(i, streamTypes[i].UnderlyingType); + resultTypes[i] = evaluators[i].ReturnType; + } + + return InitializeCtorInjection( + (BeanEventType) eventType, evaluators, resultTypes, engineImportService, eventAdapterService); + } + catch (ExprValidationException) + { + if (writableProps.IsEmpty()) + { + throw; + } + } + + throw; + } + } + + private static bool CheckEligible( + EventType eventType, + ICollection writableProps, + bool allowNestableTargetFragmentTypes) + { + if (writableProps == null) + { + return false; // no writable properties, not a writable type, proceed + } + + // For map event types this class does not handle fragment inserts; all fragments are required however and must be explicit + if (!allowNestableTargetFragmentTypes && + (eventType is BaseNestableEventType || eventType is AvroSchemaEventType)) + { + foreach (var prop in eventType.PropertyDescriptors) + { + if (prop.IsFragment) + { + return false; + } + } + } + + return true; + } + + private static SelectExprProcessor InitializeSetterManufactor( + EventType eventType, + ICollection writables, + bool isUsingWildcard, + StreamTypeService typeService, + ExprEvaluator[] expressionNodes, + string[] columnNames, + Object[] expressionReturnTypes, + EngineImportService engineImportService, + EventAdapterService eventAdapterService, + string statementName) + { + var typeWidenerCustomizer = eventAdapterService.GetTypeWidenerCustomizer(eventType); + var writablePropertiesList = new List(); + var evaluatorsList = new List(); + var widenersList = new List(); + + // loop over all columns selected, if any + for (var i = 0; i < columnNames.Length; i++) + { + WriteablePropertyDescriptor selectedWritable = null; + TypeWidener widener = null; + var evaluator = expressionNodes[i]; + + foreach (var desc in writables) + { + if (!desc.PropertyName.Equals(columnNames[i])) + { + continue; + } + + var columnType = expressionReturnTypes[i]; + if (columnType == null) + { + TypeWidenerFactory.GetCheckPropertyAssignType( + columnNames[i], null, desc.PropertyType, desc.PropertyName, false, typeWidenerCustomizer, + statementName, typeService.EngineURIQualifier); + } + else if (columnType is EventType) + { + var columnEventType = (EventType) columnType; + var returnType = columnEventType.UnderlyingType; + widener = TypeWidenerFactory.GetCheckPropertyAssignType( + columnNames[i], columnEventType.UnderlyingType, desc.PropertyType, desc.PropertyName, false, + typeWidenerCustomizer, statementName, typeService.EngineURIQualifier); + + // handle evaluator returning an event + if (TypeHelper.IsSubclassOrImplementsInterface(returnType, desc.PropertyType)) + { + selectedWritable = desc; + widener = input => + { + var eventBean = input as EventBean; + if (eventBean != null) + { + return eventBean.Underlying; + } + return input; + }; + continue; + } + + // find stream + var streamNum = 0; + for (var j = 0; j < typeService.EventTypes.Length; j++) + { + if (Equals(typeService.EventTypes[j], columnEventType)) + { + streamNum = j; + break; + } + } + var streamNumEval = streamNum; + evaluator = new ProxyExprEvaluator + { + ProcEvaluate = evaluateParams => + { + EventBean theEvent = evaluateParams.EventsPerStream[streamNumEval]; + if (theEvent != null) + { + return theEvent.Underlying; + } + return null; + }, + + ProcReturnType = () => returnType + }; + } + else if (columnType is EventType[]) + { + // handle case where the select-clause contains an fragment array + var columnEventType = ((EventType[]) columnType)[0]; + var componentReturnType = columnEventType.UnderlyingType; + var arrayReturnType = Array.CreateInstance(componentReturnType, 0).GetType(); + + var allowObjectArrayToCollectionConversion = eventType is AvroSchemaEventType; + widener = TypeWidenerFactory.GetCheckPropertyAssignType( + columnNames[i], arrayReturnType, desc.PropertyType, desc.PropertyName, + allowObjectArrayToCollectionConversion, typeWidenerCustomizer, statementName, + typeService.EngineURIQualifier); + var inner = evaluator; + evaluator = new ProxyExprEvaluator + { + ProcEvaluate = evaluateParams => + { + var result = inner.Evaluate(evaluateParams); + if (!(result is EventBean[])) + { + return null; + } + var events = (EventBean[]) result; + var values = Array.CreateInstance(componentReturnType, events.Length); + for (var ii = 0; ii < events.Length; ii++) + { + values.SetValue(events[ii].Underlying, ii); + } + return values; + }, + + ProcReturnType = () => componentReturnType + }; + } + else if (!(columnType is Type)) + { + var message = "Invalid assignment of column '" + columnNames[i] + + "' of type '" + columnType + + "' to event property '" + desc.PropertyName + + "' typed as '" + desc.PropertyType.FullName + + "', column and parameter types mismatch"; + throw new ExprValidationException(message); + } + else + { + widener = TypeWidenerFactory.GetCheckPropertyAssignType( + columnNames[i], (Type) columnType, desc.PropertyType, desc.PropertyName, false, + typeWidenerCustomizer, statementName, typeService.EngineURIQualifier); + } + + selectedWritable = desc; + break; + } + + if (selectedWritable == null) + { + var message = "Column '" + columnNames[i] + + "' could not be assigned to any of the properties of the underlying type (missing column names, event property, setter method or constructor?)"; + throw new ExprValidationException(message); + } + + // add + writablePropertiesList.Add(selectedWritable); + evaluatorsList.Add(evaluator); + widenersList.Add(widener); + } + + // handle wildcard + if (isUsingWildcard) + { + var sourceType = typeService.EventTypes[0]; + foreach (var eventPropDescriptor in sourceType.PropertyDescriptors) + { + if (eventPropDescriptor.RequiresIndex || (eventPropDescriptor.RequiresMapKey)) + { + continue; + } + + WriteablePropertyDescriptor selectedWritable = null; + TypeWidener widener = null; + ExprEvaluator evaluator = null; + + foreach (var writableDesc in writables) + { + if (!writableDesc.PropertyName.Equals(eventPropDescriptor.PropertyName)) + { + continue; + } + + widener = TypeWidenerFactory.GetCheckPropertyAssignType( + eventPropDescriptor.PropertyName, eventPropDescriptor.PropertyType, writableDesc.PropertyType, + writableDesc.PropertyName, false, typeWidenerCustomizer, statementName, + typeService.EngineURIQualifier); + selectedWritable = writableDesc; + + var propertyName = eventPropDescriptor.PropertyName; + var propertyType = eventPropDescriptor.PropertyType; + evaluator = new ProxyExprEvaluator + { + ProcEvaluate = evaluateParams => + { + EventBean theEvent = evaluateParams.EventsPerStream[0]; + if (theEvent != null) + { + return theEvent.Get(propertyName); + } + return null; + }, + + ProcReturnType = () => propertyType + }; + break; + } + + if (selectedWritable == null) + { + var message = "Event property '" + eventPropDescriptor.PropertyName + + "' could not be assigned to any of the properties of the underlying type (missing column names, event property, setter method or constructor?)"; + throw new ExprValidationException(message); + } + + writablePropertiesList.Add(selectedWritable); + evaluatorsList.Add(evaluator); + widenersList.Add(widener); + } + } + + // assign + var writableProperties = writablePropertiesList.ToArray(); + var exprEvaluators = evaluatorsList.ToArray(); + var wideners = widenersList.ToArray(); + + EventBeanManufacturer eventManufacturer; + try + { + eventManufacturer = eventAdapterService.GetManufacturer( + eventType, writableProperties, engineImportService, false); + } + catch (EventBeanManufactureException e) + { + throw new ExprValidationException(e.Message, e); + } + + return new SelectExprInsertNativeWidening(eventType, eventManufacturer, exprEvaluators, wideners); + } + + private static SelectExprProcessor InitializeCtorInjection( + BeanEventType beanEventType, + ExprEvaluator[] exprEvaluators, + Object[] expressionReturnTypes, + EngineImportService engineImportService, + EventAdapterService eventAdapterService) + { + Pair pair = + InstanceManufacturerUtil.GetManufacturer( + beanEventType.UnderlyingType, engineImportService, exprEvaluators, expressionReturnTypes); + var eventManufacturer = new EventBeanManufacturerCtor(pair.First, beanEventType, eventAdapterService); + return new SelectExprInsertNativeNoWiden(beanEventType, eventManufacturer, pair.Second); + } + + private static SelectExprProcessor InitializeJoinWildcardInternal( + EventType eventType, + ICollection writables, + string[] streamNames, + EventType[] streamTypes, + EngineImportService engineImportService, + EventAdapterService eventAdapterService, + string statementName, + string engineURI) + { + var typeWidenerCustomizer = eventAdapterService.GetTypeWidenerCustomizer(eventType); + var writablePropertiesList = new List(); + var evaluatorsList = new List(); + var widenersList = new List(); + + // loop over all columns selected, if any + for (var i = 0; i < streamNames.Length; i++) + { + WriteablePropertyDescriptor selectedWritable = null; + TypeWidener widener = null; + + foreach (var desc in writables) + { + if (!desc.PropertyName.Equals(streamNames[i])) + { + continue; + } + + widener = TypeWidenerFactory.GetCheckPropertyAssignType( + streamNames[i], streamTypes[i].UnderlyingType, desc.PropertyType, desc.PropertyName, false, + typeWidenerCustomizer, statementName, engineURI); + selectedWritable = desc; + break; + } + + if (selectedWritable == null) + { + var message = "Stream underlying object for stream '" + streamNames[i] + + "' could not be assigned to any of the properties of the underlying type (missing column names, event property or setter method?)"; + throw new ExprValidationException(message); + } + + var streamNum = i; + var returnType = streamTypes[streamNum].UnderlyingType; + var evaluator = new ProxyExprEvaluator + { + ProcEvaluate = evaluateParams => + { + EventBean theEvent = evaluateParams.EventsPerStream[streamNum]; + if (theEvent != null) + { + return theEvent.Underlying; + } + return null; + }, + + ProcReturnType = () => returnType + }; + + // add + writablePropertiesList.Add(selectedWritable); + evaluatorsList.Add(evaluator); + widenersList.Add(widener); + } + + // assign + var writableProperties = writablePropertiesList.ToArray(); + var exprEvaluators = evaluatorsList.ToArray(); + var wideners = widenersList.ToArray(); + + EventBeanManufacturer eventManufacturer; + try + { + eventManufacturer = eventAdapterService.GetManufacturer( + eventType, writableProperties, engineImportService, false); + } + catch (EventBeanManufactureException e) + { + throw new ExprValidationException(e.Message, e); + } + + return new SelectExprInsertNativeWidening(eventType, eventManufacturer, exprEvaluators, wideners); + } + + public abstract class SelectExprInsertNativeExpressionCoerceBase : SelectExprProcessor + { + protected readonly EventType EventType; + protected readonly ExprEvaluator ExprEvaluator; + protected readonly EventAdapterService EventAdapterService; + + internal SelectExprInsertNativeExpressionCoerceBase( + EventType eventType, + ExprEvaluator exprEvaluator, + EventAdapterService eventAdapterService) + { + EventType = eventType; + ExprEvaluator = exprEvaluator; + EventAdapterService = eventAdapterService; + } + + public EventType ResultEventType + { + get { return EventType; } + } + + public abstract EventBean Process( + EventBean[] eventsPerStream, + bool isNewData, + bool isSynthesize, + ExprEvaluatorContext exprEvaluatorContext); + } + + public class SelectExprInsertNativeExpressionCoerceMap : SelectExprInsertNativeExpressionCoerceBase + { + internal SelectExprInsertNativeExpressionCoerceMap( + EventType eventType, + ExprEvaluator exprEvaluator, + EventAdapterService eventAdapterService) + : base(eventType, exprEvaluator, eventAdapterService) + { + } + + public override EventBean Process( + EventBean[] eventsPerStream, + bool isNewData, + bool isSynthesize, + ExprEvaluatorContext exprEvaluatorContext) + { + var result = ExprEvaluator.Evaluate(new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext)); + if (result == null) + { + return null; + } + return EventAdapterService.AdapterForTypedMap((IDictionary) result, EventType); + } + } + + public class SelectExprInsertNativeExpressionCoerceAvro : SelectExprInsertNativeExpressionCoerceBase + { + internal SelectExprInsertNativeExpressionCoerceAvro( + EventType eventType, + ExprEvaluator exprEvaluator, + EventAdapterService eventAdapterService) + : base(eventType, exprEvaluator, eventAdapterService) + { + } + + public override EventBean Process( + EventBean[] eventsPerStream, + bool isNewData, + bool isSynthesize, + ExprEvaluatorContext exprEvaluatorContext) + { + var result = ExprEvaluator.Evaluate(new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext)); + if (result == null) + { + return null; + } + return EventAdapterService.AdapterForTypedAvro(result, EventType); + } + } + + public class SelectExprInsertNativeExpressionCoerceObjectArray : SelectExprInsertNativeExpressionCoerceBase + { + internal SelectExprInsertNativeExpressionCoerceObjectArray( + EventType eventType, + ExprEvaluator exprEvaluator, + EventAdapterService eventAdapterService) + : base(eventType, exprEvaluator, eventAdapterService) + { + } + + public override EventBean Process( + EventBean[] eventsPerStream, + bool isNewData, + bool isSynthesize, + ExprEvaluatorContext exprEvaluatorContext) + { + var result = ExprEvaluator.Evaluate(new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext)); + if (result == null) + { + return null; + } + return EventAdapterService.AdapterForTypedObjectArray((Object[]) result, EventType); + } + } + + public class SelectExprInsertNativeExpressionCoerceNative : SelectExprInsertNativeExpressionCoerceBase + { + internal SelectExprInsertNativeExpressionCoerceNative( + EventType eventType, + ExprEvaluator exprEvaluator, + EventAdapterService eventAdapterService) + : base(eventType, exprEvaluator, eventAdapterService) + { + } + + public override EventBean Process( + EventBean[] eventsPerStream, + bool isNewData, + bool isSynthesize, + ExprEvaluatorContext exprEvaluatorContext) + { + var result = ExprEvaluator.Evaluate(new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext)); + if (result == null) + { + return null; + } + return EventAdapterService.AdapterForTypedObject(result, EventType); + } + } + + public abstract class SelectExprInsertNativeBase : SelectExprProcessor + { + protected readonly EventBeanManufacturer EventManufacturer; + protected readonly ExprEvaluator[] ExprEvaluators; + private readonly EventType _eventType; + + internal SelectExprInsertNativeBase( + EventType eventType, + EventBeanManufacturer eventManufacturer, + ExprEvaluator[] exprEvaluators) + { + _eventType = eventType; + EventManufacturer = eventManufacturer; + ExprEvaluators = exprEvaluators; + } + + public EventType ResultEventType + { + get { return _eventType; } + } + + public abstract EventBean Process( + EventBean[] eventsPerStream, + bool isNewData, + bool isSynthesize, + ExprEvaluatorContext exprEvaluatorContext); + } + + public class SelectExprInsertNativeWidening : SelectExprInsertNativeBase + { + private readonly TypeWidener[] _wideners; + + internal SelectExprInsertNativeWidening( + EventType eventType, + EventBeanManufacturer eventManufacturer, + ExprEvaluator[] exprEvaluators, + TypeWidener[] wideners) + : base(eventType, eventManufacturer, exprEvaluators) + { + _wideners = wideners; + } + + public override EventBean Process( + EventBean[] eventsPerStream, + bool isNewData, + bool isSynthesize, + ExprEvaluatorContext exprEvaluatorContext) + { + var values = new Object[ExprEvaluators.Length]; + var evaluateParams = new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext); + + for (var i = 0; i < ExprEvaluators.Length; i++) + { + var evalResult = ExprEvaluators[i].Evaluate(evaluateParams); + if ((evalResult != null) && (_wideners[i] != null)) + { + evalResult = _wideners[i].Invoke(evalResult); + } + values[i] = evalResult; + } + + return EventManufacturer.Make(values); + } + } + + public class SelectExprInsertNativeNoWiden : SelectExprInsertNativeBase + { + internal SelectExprInsertNativeNoWiden( + EventType eventType, + EventBeanManufacturer eventManufacturer, + ExprEvaluator[] exprEvaluators) + : base(eventType, eventManufacturer, exprEvaluators) + { + } + + public override EventBean Process( + EventBean[] eventsPerStream, + bool isNewData, + bool isSynthesize, + ExprEvaluatorContext exprEvaluatorContext) + { + var values = new Object[ExprEvaluators.Length]; + + for (var i = 0; i < ExprEvaluators.Length; i++) + { + var evalResult = ExprEvaluators[i].Evaluate(new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext)); + values[i] = evalResult; + } + + return EventManufacturer.Make(values); + } + } + + public class SelectExprInsertNativeNoEval : SelectExprProcessor + { + private static readonly Object[] EMPTY_PROPS = new Object[0]; + + private readonly EventType _eventType; + private readonly EventBeanManufacturer _eventManufacturer; + + internal SelectExprInsertNativeNoEval(EventType eventType, EventBeanManufacturer eventManufacturer) + { + _eventType = eventType; + _eventManufacturer = eventManufacturer; + } + + public EventBean Process( + EventBean[] eventsPerStream, + bool isNewData, + bool isSynthesize, + ExprEvaluatorContext exprEvaluatorContext) + { + return _eventManufacturer.Make(EMPTY_PROPS); + } + + public EventType ResultEventType + { + get { return _eventType; } + } + } + + public class ExprEvaluatorJoinWildcard : ExprEvaluator + { + private readonly int _streamNum; + private readonly Type _returnType; + + internal ExprEvaluatorJoinWildcard(int streamNum, Type returnType) + { + _streamNum = streamNum; + _returnType = returnType; + } + + public object Evaluate(EvaluateParams evaluateParams) + { + return Evaluate( + evaluateParams.EventsPerStream, + evaluateParams.IsNewData, + evaluateParams.ExprEvaluatorContext + ); + } + + public Object Evaluate(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + var bean = eventsPerStream[_streamNum]; + if (bean == null) + { + return null; + } + return bean.Underlying; + } + + public Type ReturnType + { + get { return _returnType; } + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/SelectExprJoinWildcardProcessorFactory.cs b/NEsper.Core/NEsper.Core/epl/core/SelectExprJoinWildcardProcessorFactory.cs new file mode 100755 index 000000000..f0e3fb8bb --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/SelectExprJoinWildcardProcessorFactory.cs @@ -0,0 +1,170 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.client.util; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.events.avro; +using com.espertech.esper.events; +using com.espertech.esper.events.arr; +using com.espertech.esper.events.map; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.core +{ + public class SelectExprJoinWildcardProcessorFactory + { + public static SelectExprProcessor Create( + ICollection assignedTypeNumberStack, + int statementId, + string statementName, + string[] streamNames, + EventType[] streamTypes, + EventAdapterService eventAdapterService, + InsertIntoDesc insertIntoDesc, + SelectExprEventTypeRegistry selectExprEventTypeRegistry, + EngineImportService engineImportService, + Attribute[] annotations, + ConfigurationInformation configuration, + TableService tableService, + string engineURI) + + { + if ((streamNames.Length < 2) || (streamTypes.Length < 2) || (streamNames.Length != streamTypes.Length)) + { + throw new ArgumentException( + "Stream names and types parameter length is invalid, expected use of this class is for join statements"); + } + + // Create EventType of result join events + var selectProperties = new LinkedHashMap(); + var streamTypesWTables = new EventType[streamTypes.Length]; + bool hasTables = false; + for (int i = 0; i < streamTypes.Length; i++) + { + streamTypesWTables[i] = streamTypes[i]; + string tableName = TableServiceUtil.GetTableNameFromEventType(streamTypesWTables[i]); + if (tableName != null) + { + hasTables = true; + streamTypesWTables[i] = tableService.GetTableMetadata(tableName).PublicEventType; + } + selectProperties.Put(streamNames[i], streamTypesWTables[i]); + } + + // If we have a name for this type, add it + EventUnderlyingType representation = EventRepresentationUtil.GetRepresentation( + annotations, configuration, AssignedType.NONE); + EventType resultEventType; + + SelectExprProcessor processor = null; + if (insertIntoDesc != null) + { + EventType existingType = eventAdapterService.GetEventTypeByName(insertIntoDesc.EventTypeName); + if (existingType != null) + { + processor = SelectExprInsertEventBeanFactory.GetInsertUnderlyingJoinWildcard( + eventAdapterService, existingType, streamNames, streamTypesWTables, engineImportService, + statementName, engineURI); + } + } + + if (processor == null) + { + if (insertIntoDesc != null) + { + try + { + if (representation == EventUnderlyingType.MAP) + { + resultEventType = eventAdapterService.AddNestableMapType( + insertIntoDesc.EventTypeName, selectProperties, null, false, false, false, false, true); + } + else if (representation == EventUnderlyingType.OBJECTARRAY) + { + resultEventType = eventAdapterService.AddNestableObjectArrayType( + insertIntoDesc.EventTypeName, selectProperties, null, false, false, false, false, true, + false, null); + } + else if (representation == EventUnderlyingType.AVRO) + { + resultEventType = eventAdapterService.AddAvroType( + insertIntoDesc.EventTypeName, selectProperties, false, false, false, false, true, + annotations, null, statementName, engineURI); + } + else + { + throw new IllegalStateException("Unrecognized code " + representation); + } + selectExprEventTypeRegistry.Add(resultEventType); + } + catch (EventAdapterException ex) + { + throw new ExprValidationException(ex.Message, ex); + } + } + else + { + if (representation == EventUnderlyingType.MAP) + { + resultEventType = + eventAdapterService.CreateAnonymousMapType( + statementId + "_join_" + CollectionUtil.ToString(assignedTypeNumberStack, "_"), + selectProperties, true); + } + else if (representation == EventUnderlyingType.OBJECTARRAY) + { + resultEventType = + eventAdapterService.CreateAnonymousObjectArrayType( + statementId + "_join_" + CollectionUtil.ToString(assignedTypeNumberStack, "_"), + selectProperties); + } + else if (representation == EventUnderlyingType.AVRO) + { + resultEventType = + eventAdapterService.CreateAnonymousAvroType( + statementId + "_join_" + CollectionUtil.ToString(assignedTypeNumberStack, "_"), + selectProperties, annotations, statementName, engineURI); + } + else + { + throw new IllegalStateException("Unrecognized enum " + representation); + } + } + if (resultEventType is ObjectArrayEventType) + { + processor = new SelectExprJoinWildcardProcessorObjectArray( + streamNames, resultEventType, eventAdapterService); + } + else if (resultEventType is MapEventType) + { + processor = new SelectExprJoinWildcardProcessorMap( + streamNames, resultEventType, eventAdapterService); + } + else if (resultEventType is AvroSchemaEventType) + { + processor = eventAdapterService.EventAdapterAvroHandler.GetOutputFactory().MakeJoinWildcard( + streamNames, resultEventType, eventAdapterService); + } + } + + if (!hasTables) + { + return processor; + } + return new SelectExprJoinWildcardProcessorTableRows(streamTypes, processor, tableService); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/SelectExprJoinWildcardProcessorMap.cs b/NEsper.Core/NEsper.Core/epl/core/SelectExprJoinWildcardProcessorMap.cs new file mode 100755 index 000000000..6e32ba16c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/SelectExprJoinWildcardProcessorMap.cs @@ -0,0 +1,65 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.core +{ + /// + /// Processor for select-clause expressions that handles wildcards. Computes results based on matching events. + /// + public class SelectExprJoinWildcardProcessorMap : SelectExprProcessor + { + private readonly EventAdapterService _eventAdapterService; + private readonly EventType _resultEventType; + private readonly String[] _streamNames; + + public SelectExprJoinWildcardProcessorMap(String[] streamNames, + EventType resultEventType, + EventAdapterService eventAdapterService) + { + _streamNames = streamNames; + _resultEventType = resultEventType; + _eventAdapterService = eventAdapterService; + } + + #region SelectExprProcessor Members + + public EventBean Process(EventBean[] eventsPerStream, + bool isNewData, + bool isSynthesize, + ExprEvaluatorContext exprEvaluatorContext) + { + IDictionary tuple = new Dictionary(); + for (int i = 0; i < _streamNames.Length; i++) + { + if (_streamNames[i] == null) + { + throw new IllegalStateException("Event name for stream " + i + " is null"); + } + tuple.Put(_streamNames[i], eventsPerStream[i]); + } + + return _eventAdapterService.AdapterForTypedMap(tuple, _resultEventType); + } + + #endregion + + public EventType ResultEventType + { + get { return _resultEventType; } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/core/SelectExprJoinWildcardProcessorObjectArray.cs b/NEsper.Core/NEsper.Core/epl/core/SelectExprJoinWildcardProcessorObjectArray.cs new file mode 100755 index 000000000..63b23014f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/SelectExprJoinWildcardProcessorObjectArray.cs @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.core +{ + /// Processor for select-clause expressions that handles wildcards. Computes results based on matching events. + public class SelectExprJoinWildcardProcessorObjectArray : SelectExprProcessor + { + private readonly EventAdapterService _eventAdapterService; + private readonly EventType _resultEventType; + private readonly String[] _streamNames; + + public SelectExprJoinWildcardProcessorObjectArray(String[] streamNames, + EventType resultEventType, + EventAdapterService eventAdapterService) + { + _streamNames = streamNames; + _resultEventType = resultEventType; + _eventAdapterService = eventAdapterService; + } + + #region SelectExprProcessor Members + + public EventBean Process(EventBean[] eventsPerStream, + bool isNewData, + bool isSynthesize, + ExprEvaluatorContext exprEvaluatorContext) + { + var tuple = new Object[_streamNames.Length]; + Array.Copy(eventsPerStream, 0, tuple, 0, _streamNames.Length); + return _eventAdapterService.AdapterForTypedObjectArray(tuple, _resultEventType); + } + + public EventType ResultEventType + { + get { return _resultEventType; } + } + + #endregion + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/core/SelectExprJoinWildcardProcessorTableRows.cs b/NEsper.Core/NEsper.Core/epl/core/SelectExprJoinWildcardProcessorTableRows.cs new file mode 100755 index 000000000..2fd3808ca --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/SelectExprJoinWildcardProcessorTableRows.cs @@ -0,0 +1,60 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.table.mgmt; + +namespace com.espertech.esper.epl.core +{ + /// + /// Processor for select-clause expressions that handles wildcards. Computes results based on matching events. + /// + public class SelectExprJoinWildcardProcessorTableRows : SelectExprProcessor + { + private readonly SelectExprProcessor _inner; + private readonly EventBean[] _eventsPerStreamWTableRows; + private readonly TableMetadata[] _tables; + + public SelectExprJoinWildcardProcessorTableRows(EventType[] types, SelectExprProcessor inner, TableService tableService) + { + _inner = inner; + _eventsPerStreamWTableRows = new EventBean[types.Length]; + _tables = new TableMetadata[types.Length]; + for (int i = 0; i < types.Length; i++) { + _tables[i] = tableService.GetTableMetadataFromEventType(types[i]); + } + } + + public EventBean Process( + EventBean[] eventsPerStream, + bool isNewData, + bool isSynthesize, + ExprEvaluatorContext exprEvaluatorContext) + { + var evaluateParams = new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext); + for (int i = 0; i < _eventsPerStreamWTableRows.Length; i++) + { + if (_tables[i] != null && eventsPerStream[i] != null) + { + _eventsPerStreamWTableRows[i] = _tables[i].EventToPublic.Convert(eventsPerStream[i], evaluateParams); + } + else + { + _eventsPerStreamWTableRows[i] = eventsPerStream[i]; + } + } + return _inner.Process(_eventsPerStreamWTableRows, isNewData, isSynthesize, exprEvaluatorContext); + } + + public EventType ResultEventType + { + get { return _inner.ResultEventType; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/SelectExprProcessor.cs b/NEsper.Core/NEsper.Core/epl/core/SelectExprProcessor.cs new file mode 100755 index 000000000..d361ccbf8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/SelectExprProcessor.cs @@ -0,0 +1,43 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.core +{ + /// + /// Interface for processors of select-clause items, implementors are computing results based on matching events. + /// + public interface SelectExprProcessor + { + /// + /// Returns the event type that represents the select-clause items. + /// + /// + /// event type representing select-clause items + /// + EventType ResultEventType { get; } + + /// + /// Computes the select-clause results and returns an event of the result event type that contains, in it's properties, the selected items. + /// + /// is per stream the event + /// indicates whether we are dealing with new data (istream) or old data (rstream) + /// set to true to indicate that synthetic events are required for an iterator result set + /// The expr evaluator context. + /// + /// event with properties containing selected items + /// + EventBean Process(EventBean[] eventsPerStream, + bool isNewData, + bool isSynthesize, + ExprEvaluatorContext exprEvaluatorContext); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/SelectExprProcessorDeliveryCallback.cs b/NEsper.Core/NEsper.Core/epl/core/SelectExprProcessorDeliveryCallback.cs new file mode 100755 index 000000000..bda7e0cd2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/SelectExprProcessorDeliveryCallback.cs @@ -0,0 +1,19 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.epl.core +{ + public interface SelectExprProcessorDeliveryCallback + { + EventBean Selected(Object[] result); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/SelectExprProcessorEvalByGetter.cs b/NEsper.Core/NEsper.Core/epl/core/SelectExprProcessorEvalByGetter.cs new file mode 100755 index 000000000..7ec946f37 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/SelectExprProcessorEvalByGetter.cs @@ -0,0 +1,44 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.core +{ + public class SelectExprProcessorEvalByGetter : ExprEvaluator + { + private readonly EventPropertyGetter _getter; + private readonly Type _returnType; + private readonly int _streamNum; + + public SelectExprProcessorEvalByGetter(int streamNum, EventPropertyGetter getter, Type returnType) + { + _streamNum = streamNum; + _getter = getter; + _returnType = returnType; + } + + public Object Evaluate(EvaluateParams evaluateParams) + { + EventBean streamEvent = evaluateParams.EventsPerStream[_streamNum]; + if (streamEvent == null) + { + return null; + } + return _getter.Get(streamEvent); + } + + public Type ReturnType + { + get { return _returnType; } + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/core/SelectExprProcessorEvalByGetterFragment.cs b/NEsper.Core/NEsper.Core/epl/core/SelectExprProcessorEvalByGetterFragment.cs new file mode 100755 index 000000000..cc8452694 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/SelectExprProcessorEvalByGetterFragment.cs @@ -0,0 +1,54 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.core +{ + public class SelectExprProcessorEvalByGetterFragment : ExprEvaluator + { + private readonly EventPropertyGetter _getter; + private readonly Type _returnType; + private readonly int _streamNum; + + public SelectExprProcessorEvalByGetterFragment(int streamNum, EventPropertyGetter getter, Type returnType) + { + _streamNum = streamNum; + _getter = getter; + _returnType = returnType; + } + + public EventPropertyGetter Getter + { + get { return _getter; } + } + + public int StreamNum + { + get { return _streamNum; } + } + + public Type ReturnType + { + get { return _returnType; } + } + + public Object Evaluate(EvaluateParams evaluateParams) + { + EventBean streamEvent = evaluateParams.EventsPerStream[_streamNum]; + if (streamEvent == null) + { + return null; + } + return _getter.GetFragment(streamEvent); + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/core/SelectExprProcessorEvalStreamInsertNamedWindow.cs b/NEsper.Core/NEsper.Core/epl/core/SelectExprProcessorEvalStreamInsertNamedWindow.cs new file mode 100755 index 000000000..b6e4e6df4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/SelectExprProcessorEvalStreamInsertNamedWindow.cs @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.core +{ + public class SelectExprProcessorEvalStreamInsertNamedWindow : ExprEvaluator + { + private readonly EventType _namedWindowAsType; + private readonly EventAdapterService _eventAdapterService; + + public SelectExprProcessorEvalStreamInsertNamedWindow( + int streamNum, + EventType namedWindowAsType, + Type returnType, + EventAdapterService eventAdapterService) + { + StreamNum = streamNum; + _namedWindowAsType = namedWindowAsType; + ReturnType = returnType; + _eventAdapterService = eventAdapterService; + } + + public object Evaluate(EvaluateParams evaluateParams) + { + return Evaluate( + evaluateParams.EventsPerStream, + evaluateParams.IsNewData, + evaluateParams.ExprEvaluatorContext + ); + } + + public Object Evaluate(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext exprEvaluatorContext) + { + var @event = eventsPerStream[StreamNum]; + return @event == null ? null : _eventAdapterService.AdapterForType(@event.Underlying, _namedWindowAsType); + } + + public Type ReturnType { get; private set; } + + public int StreamNum { get; private set; } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/SelectExprProcessorEvalStreamInsertTable.cs b/NEsper.Core/NEsper.Core/epl/core/SelectExprProcessorEvalStreamInsertTable.cs new file mode 100755 index 000000000..efae08352 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/SelectExprProcessorEvalStreamInsertTable.cs @@ -0,0 +1,63 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.core +{ + public class SelectExprProcessorEvalStreamInsertTable : ExprEvaluator + { + private readonly int _streamNum; + private readonly ExprStreamUnderlyingNode _undNode; + private readonly TableMetadata _tableMetadata; + private readonly Type _returnType; + + public SelectExprProcessorEvalStreamInsertTable(int streamNum, ExprStreamUnderlyingNode undNode, TableMetadata tableMetadata, Type returnType) + { + _streamNum = streamNum; + _undNode = undNode; + _tableMetadata = tableMetadata; + _returnType = returnType; + } + + public object Evaluate(EvaluateParams evaluateParams) + { + return this.Evaluate( + evaluateParams.EventsPerStream, + evaluateParams.IsNewData, + evaluateParams.ExprEvaluatorContext + ); + } + + public object Evaluate(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext exprEvaluatorContext) + { + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().QExprStreamUndSelectClause(_undNode); + } + + var @event = eventsPerStream == null ? null : eventsPerStream[_streamNum]; + if (@event != null) { + @event = _tableMetadata.EventToPublic.Convert(@event, new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext)); + } + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().AExprStreamUndSelectClause(@event); + } + return @event; + } + + public Type ReturnType + { + get { return _returnType; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/SelectExprProcessorEvalStreamInsertUnd.cs b/NEsper.Core/NEsper.Core/epl/core/SelectExprProcessorEvalStreamInsertUnd.cs new file mode 100755 index 000000000..1b4d20e34 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/SelectExprProcessorEvalStreamInsertUnd.cs @@ -0,0 +1,55 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.core +{ + public class SelectExprProcessorEvalStreamInsertUnd : ExprEvaluator + { + private readonly Type _returnType; + private readonly int _streamNum; + private readonly ExprStreamUnderlyingNode _undNode; + + public SelectExprProcessorEvalStreamInsertUnd(ExprStreamUnderlyingNode undNode, int streamNum, Type returnType) + { + _undNode = undNode; + _streamNum = streamNum; + _returnType = returnType; + } + + public int StreamNum + { + get { return _streamNum; } + } + + public object Evaluate(EvaluateParams evaluateParams) + { + var eventsPerStream = evaluateParams.EventsPerStream; + + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().QExprStreamUndSelectClause(_undNode); + var @event = eventsPerStream == null ? null : eventsPerStream[_streamNum]; + InstrumentationHelper.Get().AExprStreamUndSelectClause(@event); + return @event; + } + + return eventsPerStream == null ? null : eventsPerStream[_streamNum]; + } + + public Type ReturnType + { + get { return _returnType; } + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/core/SelectExprProcessorEvalTypableMap.cs b/NEsper.Core/NEsper.Core/epl/core/SelectExprProcessorEvalTypableMap.cs new file mode 100755 index 000000000..ea4d5a983 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/SelectExprProcessorEvalTypableMap.cs @@ -0,0 +1,55 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.core +{ + public class SelectExprProcessorEvalTypableMap : ExprEvaluator + { + private readonly EventAdapterService _eventAdapterService; + private readonly ExprEvaluator _innerEvaluator; + private readonly EventType _mapType; + + public SelectExprProcessorEvalTypableMap( + EventType mapType, + ExprEvaluator innerEvaluator, + EventAdapterService eventAdapterService) + { + _mapType = mapType; + _innerEvaluator = innerEvaluator; + _eventAdapterService = eventAdapterService; + } + + public ExprEvaluator InnerEvaluator + { + get { return _innerEvaluator; } + } + + public Object Evaluate(EvaluateParams evaluateParams) + { + var values = (IDictionary) _innerEvaluator.Evaluate(evaluateParams); + if (values == null) + { + return _eventAdapterService.AdapterForTypedMap(Collections.EmptyDataMap, _mapType); + } + return _eventAdapterService.AdapterForTypedMap(values, _mapType); + } + + public Type ReturnType + { + get { return typeof (IDictionary); } + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/core/SelectExprProcessorFactory.cs b/NEsper.Core/NEsper.Core/epl/core/SelectExprProcessorFactory.cs new file mode 100755 index 000000000..5e15c0c0e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/SelectExprProcessorFactory.cs @@ -0,0 +1,378 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.client.soda; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.core.eval; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.dot; +using com.espertech.esper.epl.named; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.epl.variable; +using com.espertech.esper.events; +using com.espertech.esper.events.vaevent; +using com.espertech.esper.schedule; +using com.espertech.esper.script; + +using ContextDescriptor = com.espertech.esper.core.context.util.ContextDescriptor; + +namespace com.espertech.esper.epl.core +{ + /// + /// Factory for select expression processors. + /// + public class SelectExprProcessorFactory + { + private static readonly ILog Log = LogManager.GetLogger(typeof(SelectExprProcessorFactory)); + + /// + /// Returns the processor to use for a given select-clause. + /// + /// The assigned type number stack. + /// the list of select clause elements/items, which are expected to have been validated + /// true if the wildcard (*) occurs in the select clause + /// contains column names for the optional insert-into clause (if supplied) + /// Type of the optional insert into event. + /// For clause spec. + /// serves stream type information + /// for generating wrapper instances for events + /// handles listeners/subscriptions awareness to reduce output result generation + /// service that handles update events and variant events + /// registry for event type to statements + /// + /// context for expression evalauation + /// The variable service. + /// The scripting service. + /// The table service. + /// The time provider. + /// The engine URI. + /// The statement identifier. + /// Name of the statement. + /// The annotations. + /// The context descriptor. + /// The configuration. + /// The select expr processor callback. + /// The named window service. + /// The into table clause. + /// + /// + /// + /// select-clause expression processor + /// + /// Expected any of the + Arrays.ToString(ForClauseKeyword.Values()).ToLowerCase() + for-clause keywords after reserved keyword 'for' + /// or + /// The for-clause with the + ForClauseKeyword.GROUPED_DELIVERY.Name + keyword requires one or more grouping expressions + /// or + /// The for-clause with the + ForClauseKeyword.DISCRETE_DELIVERY.Name + keyword does not allow grouping expressions + /// or + /// The for-clause with delivery keywords may only occur once in a statement + /// or + /// Expected any of the + Arrays.ToString(ForClauseKeyword.Values()).ToLowerCase() + for-clause keywords after reserved keyword 'for' + /// ExprValidationException to indicate the select expression cannot be validated + public static SelectExprProcessor GetProcessor( + ICollection assignedTypeNumberStack, + SelectClauseElementCompiled[] selectionList, + bool isUsingWildcard, + InsertIntoDesc insertIntoDesc, + EventType optionalInsertIntoEventType, + ForClauseSpec forClauseSpec, + StreamTypeService typeService, + EventAdapterService eventAdapterService, + StatementResultService statementResultService, + ValueAddEventService valueAddEventService, + SelectExprEventTypeRegistry selectExprEventTypeRegistry, + EngineImportService engineImportService, + ExprEvaluatorContext exprEvaluatorContext, + VariableService variableService, + ScriptingService scriptingService, + TableService tableService, + TimeProvider timeProvider, + string engineURI, + int statementId, + string statementName, + Attribute[] annotations, + ContextDescriptor contextDescriptor, + ConfigurationInformation configuration, + SelectExprProcessorDeliveryCallback selectExprProcessorCallback, + NamedWindowMgmtService namedWindowMgmtService, + IntoTableSpec intoTableClause, + GroupByRollupInfo groupByRollupInfo, + StatementExtensionSvcContext statementExtensionSvcContext) + { + if (selectExprProcessorCallback != null) + { + var bindProcessor = new BindProcessor(selectionList, typeService.EventTypes, typeService.StreamNames, tableService); + IDictionary properties = new LinkedHashMap(); + for (var i = 0; i < bindProcessor.ColumnNamesAssigned.Length; i++) + { + properties.Put(bindProcessor.ColumnNamesAssigned[i], bindProcessor.ExpressionTypes[i]); + } + var eventType = eventAdapterService.CreateAnonymousObjectArrayType("Output_" + statementName, properties); + return new SelectExprProcessorWDeliveryCallback(eventType, bindProcessor, selectExprProcessorCallback); + } + + var synthetic = GetProcessorInternal( + assignedTypeNumberStack, selectionList, isUsingWildcard, insertIntoDesc, optionalInsertIntoEventType, + typeService, eventAdapterService, valueAddEventService, selectExprEventTypeRegistry, + engineImportService, statementId, statementName, annotations, configuration, namedWindowMgmtService, tableService, + groupByRollupInfo); + + // Handle table as an optional service + if (statementResultService != null) + { + // Handle for-clause delivery contract checking + ExprNode[] groupedDeliveryExpr = null; + var forDelivery = false; + if (forClauseSpec != null) + { + foreach (var item in forClauseSpec.Clauses) + { + if (item.Keyword == null) + { + throw new ExprValidationException("Expected any of the " + EnumHelper.GetValues().Render().ToLower() + " for-clause keywords after reserved keyword 'for'"); + } + try + { + ForClauseKeyword keyword = EnumHelper.Parse(item.Keyword); + if ((keyword == ForClauseKeyword.GROUPED_DELIVERY) && (item.Expressions.IsEmpty())) + { + throw new ExprValidationException( + "The for-clause with the " + ForClauseKeyword.GROUPED_DELIVERY.GetName() + + " keyword requires one or more grouping expressions"); + } + if ((keyword == ForClauseKeyword.DISCRETE_DELIVERY) && (!item.Expressions.IsEmpty())) + { + throw new ExprValidationException( + "The for-clause with the " + ForClauseKeyword.DISCRETE_DELIVERY.GetName() + + " keyword does not allow grouping expressions"); + } + if (forDelivery) + { + throw new ExprValidationException( + "The for-clause with delivery keywords may only occur once in a statement"); + } + } + catch (ExprValidationException) + { + throw; + } + catch (EPException) + { + throw; + } + catch (Exception ex) + { + throw new ExprValidationException("Expected any of the " + EnumHelper.GetValues().Render().ToLower() + " for-clause keywords after reserved keyword 'for'", ex); + } + + StreamTypeService type = new StreamTypeServiceImpl(synthetic.ResultEventType, null, false, engineURI); + groupedDeliveryExpr = new ExprNode[item.Expressions.Count]; + var validationContext = new ExprValidationContext(type, engineImportService, statementExtensionSvcContext, null, timeProvider, variableService, tableService, exprEvaluatorContext, eventAdapterService, statementName, statementId, annotations, null, scriptingService, false, false, true, false, intoTableClause == null ? null : intoTableClause.Name, false); // no context descriptor available + for (var i = 0; i < item.Expressions.Count; i++) + { + groupedDeliveryExpr[i] = ExprNodeUtility.GetValidatedSubtree(ExprNodeOrigin.FORCLAUSE, item.Expressions[i], validationContext); + } + forDelivery = true; + } + } + + var bindProcessor = new BindProcessor(selectionList, typeService.EventTypes, typeService.StreamNames, tableService); + statementResultService.SetSelectClause(bindProcessor.ExpressionTypes, bindProcessor.ColumnNamesAssigned, forDelivery, ExprNodeUtility.GetEvaluators(groupedDeliveryExpr), exprEvaluatorContext); + return new SelectExprResultProcessor(statementResultService, synthetic, bindProcessor); + } + + return synthetic; + } + + private static SelectExprProcessor GetProcessorInternal( + ICollection assignedTypeNumberStack, + SelectClauseElementCompiled[] selectionList, + bool isUsingWildcard, + InsertIntoDesc insertIntoDesc, + EventType optionalInsertIntoEventType, + StreamTypeService typeService, + EventAdapterService eventAdapterService, + ValueAddEventService valueAddEventService, + SelectExprEventTypeRegistry selectExprEventTypeRegistry, + EngineImportService engineImportService, + int statementId, + string statementName, + Attribute[] annotations, + ConfigurationInformation configuration, + NamedWindowMgmtService namedWindowMgmtService, + TableService tableService, + GroupByRollupInfo groupByRollupInfo) + { + // Wildcard not allowed when insert into specifies column order + if (isUsingWildcard && insertIntoDesc != null && !insertIntoDesc.ColumnNames.IsEmpty()) + { + throw new ExprValidationException("Wildcard not allowed when insert-into specifies column order"); + } + + // Determine wildcard processor (select *) + if (IsWildcardsOnly(selectionList)) + { + // For joins + if (typeService.StreamNames.Length > 1) + { + Log.Debug(".getProcessor Using SelectExprJoinWildcardProcessor"); + return SelectExprJoinWildcardProcessorFactory.Create( + assignedTypeNumberStack, statementId, statementName, + typeService.StreamNames, typeService.EventTypes, + eventAdapterService, insertIntoDesc, selectExprEventTypeRegistry, engineImportService, + annotations, configuration, tableService, typeService.EngineURIQualifier); + } + // Single-table selects with no insert-into + // don't need extra processing + else if (insertIntoDesc == null) + { + Log.Debug(".getProcessor Using wildcard processor"); + if (typeService.HasTableTypes) + { + var tableName = TableServiceUtil.GetTableNameFromEventType(typeService.EventTypes[0]); + return new SelectExprWildcardTableProcessor(tableName, tableService); + } + return new SelectExprWildcardProcessor(typeService.EventTypes[0]); + } + } + + // Verify the assigned or name used is unique + if (insertIntoDesc == null) + { + VerifyNameUniqueness(selectionList); + } + + // Construct processor + var buckets = GetSelectExpressionBuckets(selectionList); + + var factory = new SelectExprProcessorHelper( + assignedTypeNumberStack, buckets.Expressions, buckets.SelectedStreams, insertIntoDesc, + optionalInsertIntoEventType, isUsingWildcard, typeService, eventAdapterService, valueAddEventService, + selectExprEventTypeRegistry, engineImportService, statementId, statementName, annotations, configuration, + namedWindowMgmtService, tableService, groupByRollupInfo); + SelectExprProcessor processor = factory.Evaluator; + + // add reference to the type obtained + var type = (EventTypeSPI)processor.ResultEventType; + if (!typeService.IsOnDemandStreams && type.Metadata.TypeClass != TypeClass.ANONYMOUS) + { + selectExprEventTypeRegistry.Add(processor.ResultEventType); + } + return processor; + } + + /// + /// Verify that each given name occurs exactly one. + /// + /// is the list of select items to verify names + /// com.espertech.esper.epl.expression.core.ExprValidationException thrown if a name occured more then once + internal static void VerifyNameUniqueness(SelectClauseElementCompiled[] selectionList) + { + ISet names = new HashSet(); + foreach (var element in selectionList) + { + if (element is SelectClauseExprCompiledSpec) + { + var expr = (SelectClauseExprCompiledSpec)element; + if (names.Contains(expr.AssignedName)) + { + throw new ExprValidationException("Column name '" + expr.AssignedName + "' appears more then once in select clause"); + } + names.Add(expr.AssignedName); + } + else if (element is SelectClauseStreamCompiledSpec) + { + var stream = (SelectClauseStreamCompiledSpec)element; + if (stream.OptionalName == null) + { + continue; // ignore no-name stream selectors + } + if (names.Contains(stream.OptionalName)) + { + throw new ExprValidationException("Column name '" + stream.OptionalName + "' appears more then once in select clause"); + } + names.Add(stream.OptionalName); + } + } + } + + private static bool IsWildcardsOnly(IEnumerable elements) + { + foreach (var element in elements) + { + if (!(element is SelectClauseElementWildcard)) + { + return false; + } + } + return true; + } + + private static SelectExprBuckets GetSelectExpressionBuckets(IEnumerable elements) + { + var expressions = new List(); + var selectedStreams = new List(); + + foreach (var element in elements) + { + if (element is SelectClauseExprCompiledSpec) + { + var expr = (SelectClauseExprCompiledSpec)element; + if (!IsTransposingFunction(expr.SelectExpression)) + { + expressions.Add(expr); + } + else + { + selectedStreams.Add(new SelectExprStreamDesc(expr)); + } + } + else if (element is SelectClauseStreamCompiledSpec) + { + selectedStreams.Add(new SelectExprStreamDesc((SelectClauseStreamCompiledSpec)element)); + } + } + return new SelectExprBuckets(expressions, selectedStreams); + } + + private static bool IsTransposingFunction(ExprNode selectExpression) + { + if (!(selectExpression is ExprDotNode)) + { + return false; + } + var dotNode = (ExprDotNode)selectExpression; + if (dotNode.ChainSpec[0].Name.ToLower() == EngineImportServiceConstants.EXT_SINGLEROW_FUNCTION_TRANSPOSE) + { + return true; + } + return false; + } + + public class SelectExprBuckets + { + public SelectExprBuckets(IList expressions, IList selectedStreams) + { + Expressions = expressions; + SelectedStreams = selectedStreams; + } + + public IList SelectedStreams { get; private set; } + + public IList Expressions { get; private set; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/SelectExprProcessorHelper.cs b/NEsper.Core/NEsper.Core/epl/core/SelectExprProcessorHelper.cs new file mode 100755 index 000000000..bb8baf97e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/SelectExprProcessorHelper.cs @@ -0,0 +1,1961 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.client.util; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.core.eval; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.named; +using com.espertech.esper.epl.rettype; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.events; +using com.espertech.esper.events.arr; +using com.espertech.esper.events.avro; +using com.espertech.esper.events.bean; +using com.espertech.esper.events.map; +using com.espertech.esper.events.vaevent; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.core +{ + /// + /// Processor for select-clause expressions that handles a list of selection items represented by + /// expression nodes. Computes results based on matching events. + /// + public class SelectExprProcessorHelper + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private readonly Attribute[] _annotations; + + private readonly ICollection _assignedTypeNumberStack; + private readonly ConfigurationInformation _configuration; + private readonly EngineImportService _engineImportService; + private readonly EventAdapterService _eventAdapterService; + private readonly GroupByRollupInfo _groupByRollupInfo; + private readonly InsertIntoDesc _insertIntoDesc; + private readonly bool _isUsingWildcard; + private readonly NamedWindowMgmtService _namedWindowMgmtService; + private readonly SelectExprEventTypeRegistry _selectExprEventTypeRegistry; + private readonly IList _selectedStreams; + private readonly IList _selectionList; + private readonly int _statementId; + private readonly string _statementName; + private readonly TableService _tableService; + private readonly StreamTypeService _typeService; + private readonly ValueAddEventService _valueAddEventService; + private EventType _optionalInsertIntoOverrideType; + + public SelectExprProcessorHelper( + ICollection assignedTypeNumberStack, + IList selectionList, + IList selectedStreams, + InsertIntoDesc insertIntoDesc, + EventType optionalInsertIntoOverrideType, + bool isUsingWildcard, + StreamTypeService typeService, + EventAdapterService eventAdapterService, + ValueAddEventService valueAddEventService, + SelectExprEventTypeRegistry selectExprEventTypeRegistry, + EngineImportService engineImportService, + int statementId, + string statementName, + Attribute[] annotations, + ConfigurationInformation configuration, + NamedWindowMgmtService namedWindowMgmtService, + TableService tableService, + GroupByRollupInfo groupByRollupInfo) + { + _assignedTypeNumberStack = assignedTypeNumberStack; + _selectionList = selectionList; + _selectedStreams = selectedStreams; + _insertIntoDesc = insertIntoDesc; + _optionalInsertIntoOverrideType = optionalInsertIntoOverrideType; + _eventAdapterService = eventAdapterService; + _isUsingWildcard = isUsingWildcard; + _typeService = typeService; + _valueAddEventService = valueAddEventService; + _selectExprEventTypeRegistry = selectExprEventTypeRegistry; + _engineImportService = engineImportService; + _statementId = statementId; + _statementName = statementName; + _annotations = annotations; + _configuration = configuration; + _namedWindowMgmtService = namedWindowMgmtService; + _tableService = tableService; + _groupByRollupInfo = groupByRollupInfo; + } + + private static EPType[] DetermineInsertedEventTypeTargets( + EventType targetType, + IList selectionList) + { + var targets = new EPType[selectionList.Count]; + if (targetType == null) + { + return targets; + } + + for (int i = 0; i < selectionList.Count; i++) + { + SelectClauseExprCompiledSpec expr = selectionList[i]; + if (expr.ProvidedName == null) + { + continue; + } + + EventPropertyDescriptor desc = targetType.GetPropertyDescriptor(expr.ProvidedName); + if (desc == null) + { + continue; + } + + if (!desc.IsFragment) + { + continue; + } + + FragmentEventType fragmentEventType = targetType.GetFragmentType(expr.ProvidedName); + if (fragmentEventType == null) + { + continue; + } + + if (fragmentEventType.IsIndexed) + { + targets[i] = EPTypeHelper.CollectionOfEvents(fragmentEventType.FragmentType); + } + else + { + targets[i] = EPTypeHelper.SingleEvent(fragmentEventType.FragmentType); + } + } + + return targets; + } + + // Determine which properties provided by the Map must be downcast from EventBean to Object + private static ISet GetEventBeanToObjectProps( + IDictionary selPropertyTypes, + EventType resultEventType) + { + if (!(resultEventType is BaseNestableEventType)) + { + return Collections.GetEmptySet(); + } + var mapEventType = (BaseNestableEventType) resultEventType; + ISet props = null; + foreach (var entry in selPropertyTypes) + { + if (entry.Value is BeanEventType && mapEventType.Types.Get(entry.Key) is Type) + { + if (props == null) + { + props = new HashSet(); + } + props.Add(entry.Key); + } + } + if (props == null) + { + return Collections.GetEmptySet(); + } + return props; + } + + private static void VerifyInsertInto( + InsertIntoDesc insertIntoDesc, + IList selectionList) + { + // Verify all column names are unique + var names = new HashSet(); + foreach (string element in insertIntoDesc.ColumnNames) + { + if (names.Contains(element)) + { + throw new ExprValidationException( + "Property name '" + element + "' appears more then once in insert-into clause"); + } + names.Add(element); + } + + // Verify number of columns matches the select clause + if ((!insertIntoDesc.ColumnNames.IsEmpty()) && + (insertIntoDesc.ColumnNames.Count != selectionList.Count)) + { + throw new ExprValidationException( + "Number of supplied values in the select or values clause does not match insert-into clause"); + } + } + + public SelectExprProcessor Evaluator + { + get + { + // Get the named and un-named stream selectors (i.e. select s0.* from S0 as s0), if any + var namedStreams = new List(); + var unnamedStreams = new List(); + foreach (SelectExprStreamDesc spec in _selectedStreams) + { + // handle special "Transpose(...)" function + if ((spec.StreamSelected != null && spec.StreamSelected.OptionalName == null) + || + (spec.ExpressionSelectedAsStream != null)) + { + unnamedStreams.Add(spec); + } + else + { + namedStreams.Add(spec.StreamSelected); + if (spec.StreamSelected.IsProperty) + { + throw new ExprValidationException( + "The property wildcard syntax must be used without column name"); + } + } + } + + // Error if there are more then one un-named streams (i.e. select s0.*, s1.* from S0 as s0, S1 as s1) + // Thus there is only 1 unnamed stream selector maximum. + if (unnamedStreams.Count > 1) + { + throw new ExprValidationException( + "A column name must be supplied for all but one stream if multiple streams are selected via the stream.* notation"); + } + + if (_selectedStreams.IsEmpty() && _selectionList.IsEmpty() && !_isUsingWildcard) + { + throw new ArgumentException("EmptyFalse selection list not supported"); + } + + foreach (SelectClauseExprCompiledSpec entry in _selectionList) + { + if (entry.AssignedName == null) + { + throw new ArgumentException("Expected name for each expression has not been supplied"); + } + } + + // Verify insert into clause + if (_insertIntoDesc != null) + { + VerifyInsertInto(_insertIntoDesc, _selectionList); + } + + // Build a subordinate wildcard processor for joins + SelectExprProcessor joinWildcardProcessor = null; + if (_typeService.StreamNames.Length > 1 && _isUsingWildcard) + { + joinWildcardProcessor = SelectExprJoinWildcardProcessorFactory.Create( + _assignedTypeNumberStack, _statementId, _statementName, _typeService.StreamNames, + _typeService.EventTypes, _eventAdapterService, null, _selectExprEventTypeRegistry, + _engineImportService, _annotations, _configuration, _tableService, _typeService.EngineURIQualifier); + } + + // Resolve underlying event type in the case of wildcard select + EventType eventType = null; + bool singleStreamWrapper = false; + if (_isUsingWildcard) + { + if (joinWildcardProcessor != null) + { + eventType = joinWildcardProcessor.ResultEventType; + } + else + { + eventType = _typeService.EventTypes[0]; + if (eventType is WrapperEventType) + { + singleStreamWrapper = true; + } + } + } + + // Find if there is any fragments selected + EventType insertIntoTargetType = null; + if (_insertIntoDesc != null) + { + if (_optionalInsertIntoOverrideType != null) + { + insertIntoTargetType = _optionalInsertIntoOverrideType; + } + else + { + insertIntoTargetType = _eventAdapterService.GetEventTypeByName(_insertIntoDesc.EventTypeName); + TableMetadata tableMetadata = _tableService.GetTableMetadata(_insertIntoDesc.EventTypeName); + if (tableMetadata != null) + { + insertIntoTargetType = tableMetadata.InternalEventType; + _optionalInsertIntoOverrideType = insertIntoTargetType; + } + } + } + + // Obtain insert-into per-column type information, when available + EPType[] insertIntoTargetsPerCol = DetermineInsertedEventTypeTargets(insertIntoTargetType, _selectionList); + + // Get expression nodes + var exprEvaluators = new ExprEvaluator[_selectionList.Count]; + var exprNodes = new ExprNode[_selectionList.Count]; + var expressionReturnTypes = new Object[_selectionList.Count]; + for (int i = 0; i < _selectionList.Count; i++) + { + SelectClauseExprCompiledSpec spec = _selectionList[i]; + ExprNode expr = spec.SelectExpression; + ExprEvaluator evaluator = expr.ExprEvaluator; + exprNodes[i] = expr; + + // if there is insert-into specification, use that + if (_insertIntoDesc != null) + { + // handle insert-into, with well-defined target event-typed column, and enumeration + TypeAndFunctionPair pairX = HandleInsertIntoEnumeration( + spec.ProvidedName, insertIntoTargetsPerCol[i], evaluator, _engineImportService); + if (pairX != null) + { + expressionReturnTypes[i] = pairX.Type; + exprEvaluators[i] = pairX.Function; + continue; + } + + // handle insert-into with well-defined target event-typed column, and typable expression + pairX = HandleInsertIntoTypableExpression( + insertIntoTargetsPerCol[i], evaluator, _engineImportService); + if (pairX != null) + { + expressionReturnTypes[i] = pairX.Type; + exprEvaluators[i] = pairX.Function; + continue; + } + } + + // handle @eventbean annotation, i.e. well-defined type through enumeration + TypeAndFunctionPair pair = HandleAtEventbeanEnumeration(spec.IsEvents, evaluator); + if (pair != null) + { + expressionReturnTypes[i] = pair.Type; + exprEvaluators[i] = pair.Function; + continue; + } + + // handle typeable return, i.e. typable multi-column return without provided target type + pair = HandleTypableExpression(evaluator, i); + if (pair != null) + { + expressionReturnTypes[i] = pair.Type; + exprEvaluators[i] = pair.Function; + continue; + } + + // handle select-clause expressions that match group-by expressions with rollup and therefore should be boxed types as rollup can produce a null value + if (_groupByRollupInfo != null && _groupByRollupInfo.RollupDesc != null) + { + Type returnType = evaluator.ReturnType; + Type returnTypeBoxed = returnType.GetBoxedType(); + if (returnType != returnTypeBoxed && IsGroupByRollupNullableExpression(expr, _groupByRollupInfo)) + { + exprEvaluators[i] = evaluator; + expressionReturnTypes[i] = returnTypeBoxed; + continue; + } + } + + // assign normal expected return type + exprEvaluators[i] = evaluator; + expressionReturnTypes[i] = exprEvaluators[i].ReturnType; + } + + // Get column names + string[] columnNames; + string[] columnNamesAsProvided; + if ((_insertIntoDesc != null) && (!_insertIntoDesc.ColumnNames.IsEmpty())) + { + columnNames = _insertIntoDesc.ColumnNames.ToArray(); + columnNamesAsProvided = columnNames; + } + else if (!_selectedStreams.IsEmpty()) + { + // handle stream selection column names + int numStreamColumnsJoin = 0; + if (_isUsingWildcard && _typeService.EventTypes.Length > 1) + { + numStreamColumnsJoin = _typeService.EventTypes.Length; + } + columnNames = new string[_selectionList.Count + namedStreams.Count + numStreamColumnsJoin]; + columnNamesAsProvided = new string[columnNames.Length]; + int countX = 0; + foreach (SelectClauseExprCompiledSpec aSelectionList in _selectionList) + { + columnNames[countX] = aSelectionList.AssignedName; + columnNamesAsProvided[countX] = aSelectionList.ProvidedName; + countX++; + } + foreach (SelectClauseStreamCompiledSpec aSelectionList in namedStreams) + { + columnNames[countX] = aSelectionList.OptionalName; + columnNamesAsProvided[countX] = aSelectionList.OptionalName; + countX++; + } + // for wildcard joins, add the streams themselves + if (_isUsingWildcard && _typeService.EventTypes.Length > 1) + { + foreach (string streamName in _typeService.StreamNames) + { + columnNames[countX] = streamName; + columnNamesAsProvided[countX] = streamName; + countX++; + } + } + } + else + { + // handle regular column names + columnNames = new string[_selectionList.Count]; + columnNamesAsProvided = new string[_selectionList.Count]; + for (int i = 0; i < _selectionList.Count; i++) + { + columnNames[i] = _selectionList[i].AssignedName; + columnNamesAsProvided[i] = _selectionList[i].ProvidedName; + } + } + + // Find if there is any fragment event types: + // This is a special case for fragments: select a, b from pattern [a=A -> b=B] + // We'd like to maintain 'A' and 'B' EventType in the Map type, and 'a' and 'b' EventBeans in the event bean + for (int i = 0; i < _selectionList.Count; i++) + { + if (!(exprNodes[i] is ExprIdentNode)) + { + continue; + } + + var identNode = (ExprIdentNode) exprNodes[i]; + string propertyName = identNode.ResolvedPropertyName; + int streamNum = identNode.StreamId; + + EventType eventTypeStream = _typeService.EventTypes[streamNum]; + if (eventTypeStream is NativeEventType) + { + continue; // we do not transpose the native type for performance reasons + } + + FragmentEventType fragmentType = eventTypeStream.GetFragmentType(propertyName); + if ((fragmentType == null) || (fragmentType.IsNative)) + { + continue; // we also ignore native classes as fragments for performance reasons + } + + // may need to unwrap the fragment if the target type has this underlying type + FragmentEventType targetFragment = null; + if (insertIntoTargetType != null) + { + targetFragment = insertIntoTargetType.GetFragmentType(columnNames[i]); + } + if ((insertIntoTargetType != null) && + (fragmentType.FragmentType.UnderlyingType == expressionReturnTypes[i]) && + ((targetFragment == null) || (targetFragment != null && targetFragment.IsNative))) + { + EventPropertyGetter getter = eventTypeStream.GetGetter(propertyName); + Type returnType = eventTypeStream.GetPropertyType(propertyName); + exprEvaluators[i] = new SelectExprProcessorEvalByGetter(streamNum, getter, returnType); + } + else if ((insertIntoTargetType != null) && expressionReturnTypes[i] is Type && + (fragmentType.FragmentType.UnderlyingType == ((Type) expressionReturnTypes[i]).GetElementType()) && + ((targetFragment == null) || (targetFragment != null && targetFragment.IsNative))) + { + // same for arrays: may need to unwrap the fragment if the target type has this underlying type + EventPropertyGetter getter = eventTypeStream.GetGetter(propertyName); + Type returnType = TypeHelper.GetArrayType(eventTypeStream.GetPropertyType(propertyName)); + exprEvaluators[i] = new SelectExprProcessorEvalByGetter(streamNum, getter, returnType); + } + else + { + EventPropertyGetter getter = eventTypeStream.GetGetter(propertyName); + FragmentEventType fragType = eventTypeStream.GetFragmentType(propertyName); + Type undType = fragType.FragmentType.UnderlyingType; + Type returnType = fragType.IsIndexed ? TypeHelper.GetArrayType(undType) : undType; + exprEvaluators[i] = new SelectExprProcessorEvalByGetterFragment(streamNum, getter, returnType); + if (!fragmentType.IsIndexed) + { + expressionReturnTypes[i] = fragmentType.FragmentType; + } + else + { + expressionReturnTypes[i] = new EventType[] + { + fragmentType.FragmentType + }; + } + } + } + + // Find if there is any stream expression (ExprStreamNode) : + // This is a special case for stream selection: select a, b from A as a, B as b + // We'd like to maintain 'A' and 'B' EventType in the Map type, and 'a' and 'b' EventBeans in the event bean + for (int i = 0; i < _selectionList.Count; i++) + { + Pair pair = HandleUnderlyingStreamInsert( + exprEvaluators[i], _namedWindowMgmtService, _eventAdapterService); + if (pair != null) + { + exprEvaluators[i] = pair.First; + expressionReturnTypes[i] = pair.Second; + } + } + + // Build event type that reflects all selected properties + var selPropertyTypes = new LinkedHashMap(); + int count = 0; + for (int i = 0; i < exprEvaluators.Length; i++) + { + object expressionReturnType = expressionReturnTypes[count]; + selPropertyTypes.Put(columnNames[count], expressionReturnType); + count++; + } + if (!_selectedStreams.IsEmpty()) + { + foreach (SelectClauseStreamCompiledSpec element in namedStreams) + { + EventType eventTypeStream; + if (element.TableMetadata != null) + { + eventTypeStream = element.TableMetadata.PublicEventType; + } + else + { + eventTypeStream = _typeService.EventTypes[element.StreamNumber]; + } + selPropertyTypes.Put(columnNames[count], eventTypeStream); + count++; + } + if (_isUsingWildcard && _typeService.EventTypes.Length > 1) + { + for (int i = 0; i < _typeService.EventTypes.Length; i++) + { + EventType eventTypeStream = _typeService.EventTypes[i]; + selPropertyTypes.Put(columnNames[count], eventTypeStream); + count++; + } + } + } + + // Handle stream selection + EventType underlyingEventType = null; + int underlyingStreamNumber = 0; + bool underlyingIsFragmentEvent = false; + EventPropertyGetter underlyingPropertyEventGetter = null; + ExprEvaluator underlyingExprEvaluator = null; + var representation = EventRepresentationUtil.GetRepresentation( + _annotations, _configuration, AssignedType.NONE); + + if (!_selectedStreams.IsEmpty()) + { + // Resolve underlying event type in the case of wildcard or non-named stream select. + // Determine if the we are considering a tagged event or a stream name. + if (_isUsingWildcard || (!unnamedStreams.IsEmpty())) + { + if (!unnamedStreams.IsEmpty()) + { + if (unnamedStreams[0].StreamSelected != null) + { + SelectClauseStreamCompiledSpec streamSpec = unnamedStreams[0].StreamSelected; + + // the tag.* syntax for : select tag.* from pattern [tag = A] + underlyingStreamNumber = streamSpec.StreamNumber; + if (streamSpec.IsFragmentEvent) + { + EventType compositeMap = _typeService.EventTypes[underlyingStreamNumber]; + FragmentEventType fragment = compositeMap.GetFragmentType(streamSpec.StreamName); + underlyingEventType = fragment.FragmentType; + underlyingIsFragmentEvent = true; + } + else if (streamSpec.IsProperty) + { + // the property.* syntax for : select property.* from A + string propertyName = streamSpec.StreamName; + Type propertyType = streamSpec.PropertyType; + int streamNumber = streamSpec.StreamNumber; + + if (streamSpec.PropertyType.IsBuiltinDataType()) + { + throw new ExprValidationException( + "The property wildcard syntax cannot be used on built-in types as returned by property '" + + propertyName + "'"); + } + + // create or get an underlying type for that Class + underlyingEventType = _eventAdapterService.AddBeanType( + propertyType.GetDefaultTypeName(), propertyType, false, false, false); + _selectExprEventTypeRegistry.Add(underlyingEventType); + underlyingPropertyEventGetter = + _typeService.EventTypes[streamNumber].GetGetter(propertyName); + if (underlyingPropertyEventGetter == null) + { + throw new ExprValidationException( + "Unexpected error resolving property getter for property " + propertyName); + } + } + else + { + // the stream.* syntax for: select a.* from A as a + underlyingEventType = _typeService.EventTypes[underlyingStreamNumber]; + } + } + else + { + // handle case where the unnamed stream is a "transpose" function, for non-insert-into + if (_insertIntoDesc == null || insertIntoTargetType == null) + { + ExprNode expression = unnamedStreams[0].ExpressionSelectedAsStream.SelectExpression; + Type returnType = expression.ExprEvaluator.ReturnType; + if (returnType == typeof (Object[]) || + returnType.IsImplementsInterface(typeof (IDictionary)) || + returnType.IsBuiltinDataType()) + { + throw new ExprValidationException( + "Invalid expression return type '" + Name.Clean(returnType) + + "' for transpose function"); + } + underlyingEventType = _eventAdapterService.AddBeanType( + returnType.GetDefaultTypeName(), returnType, false, false, false); + _selectExprEventTypeRegistry.Add(underlyingEventType); + underlyingExprEvaluator = expression.ExprEvaluator; + } + } + } + else + { + // no un-named stream selectors, but a wildcard was specified + if (_typeService.EventTypes.Length == 1) + { + // not a join, we are using the selected event + underlyingEventType = _typeService.EventTypes[0]; + if (underlyingEventType is WrapperEventType) + { + singleStreamWrapper = true; + } + } + else + { + // For joins, all results are placed in a map with properties for each stream + underlyingEventType = null; + } + } + } + } + + var selectExprContext = new SelectExprContext(exprEvaluators, columnNames, _eventAdapterService); + + if (_insertIntoDesc == null) + { + if (!_selectedStreams.IsEmpty()) + { + EventType resultEventTypeX; + if (underlyingEventType != null) + { + TableMetadata tableMetadata = _tableService.GetTableMetadataFromEventType(underlyingEventType); + if (tableMetadata != null) + { + underlyingEventType = tableMetadata.PublicEventType; + } + resultEventTypeX = + _eventAdapterService.CreateAnonymousWrapperType( + _statementId + "_wrapout_" + CollectionUtil.ToString(_assignedTypeNumberStack, "_"), + underlyingEventType, selPropertyTypes); + return new EvalSelectStreamWUnderlying( + selectExprContext, resultEventTypeX, namedStreams, _isUsingWildcard, + unnamedStreams, singleStreamWrapper, underlyingIsFragmentEvent, underlyingStreamNumber, + underlyingPropertyEventGetter, underlyingExprEvaluator, tableMetadata); + } + else + { + resultEventTypeX = + _eventAdapterService.CreateAnonymousMapType( + _statementId + "_mapout_" + CollectionUtil.ToString(_assignedTypeNumberStack, "_"), + selPropertyTypes, true); + return new EvalSelectStreamNoUnderlyingMap( + selectExprContext, resultEventTypeX, namedStreams, _isUsingWildcard); + } + } + + if (_isUsingWildcard) + { + EventType resultEventTypeX = + _eventAdapterService.CreateAnonymousWrapperType( + _statementId + "_wrapoutwild_" + CollectionUtil.ToString(_assignedTypeNumberStack, "_"), + eventType, selPropertyTypes); + if (singleStreamWrapper) + { + return new EvalSelectWildcardSSWrapper(selectExprContext, resultEventTypeX); + } + if (joinWildcardProcessor == null) + { + return new EvalSelectWildcard(selectExprContext, resultEventTypeX); + } + return new EvalSelectWildcardJoin(selectExprContext, resultEventTypeX, joinWildcardProcessor); + } + + EventType resultEventType; + if (representation == EventUnderlyingType.OBJECTARRAY) + { + resultEventType = + _eventAdapterService.CreateAnonymousObjectArrayType( + _statementId + "_result_" + CollectionUtil.ToString(_assignedTypeNumberStack, "_"), + selPropertyTypes); + } + else if (representation == EventUnderlyingType.AVRO) + { + resultEventType = + _eventAdapterService.CreateAnonymousAvroType( + _statementId + "_result_" + CollectionUtil.ToString(_assignedTypeNumberStack, "_"), + selPropertyTypes, _annotations, _statementName, _typeService.EngineURIQualifier); + } + else + { + resultEventType = + _eventAdapterService.CreateAnonymousMapType( + _statementId + "_result_" + CollectionUtil.ToString(_assignedTypeNumberStack, "_"), + selPropertyTypes, true); + } + + if (selectExprContext.ExpressionNodes.Length == 0) + { + return new EvalSelectNoWildcardEmptyProps(selectExprContext, resultEventType); + } + else + { + if (representation == EventUnderlyingType.OBJECTARRAY) + { + return new EvalSelectNoWildcardObjectArray(selectExprContext, resultEventType); + } + else if (representation == EventUnderlyingType.AVRO) + { + return + _eventAdapterService.EventAdapterAvroHandler.GetOutputFactory().MakeSelectNoWildcard( + selectExprContext, resultEventType, _tableService, _statementName, + _typeService.EngineURIQualifier); + } + return new EvalSelectNoWildcardMap(selectExprContext, resultEventType); + } + } + + EventType vaeInnerEventType = null; + bool singleColumnWrapOrBeanCoercion = false; + // Additional single-column coercion for non-wrapped type done by SelectExprInsertEventBeanFactory + bool isRevisionEvent = false; + + try + { + if (!_selectedStreams.IsEmpty()) + { + EventType resultEventTypeX; + + // handle "transpose" special function with predefined target type + if (insertIntoTargetType != null && _selectedStreams[0].ExpressionSelectedAsStream != null) + { + if (exprEvaluators.Length != 0) + { + throw new ExprValidationException( + "Cannot transpose additional properties in the select-clause to target event type '" + + insertIntoTargetType.Name + + "' with underlying type '" + insertIntoTargetType.UnderlyingType.FullName + "', the " + + EngineImportServiceConstants.EXT_SINGLEROW_FUNCTION_TRANSPOSE + + " function must occur alone in the select clause"); + } + ExprNode expression = unnamedStreams[0].ExpressionSelectedAsStream.SelectExpression; + Type returnType = expression.ExprEvaluator.ReturnType; + if (insertIntoTargetType is ObjectArrayEventType && returnType == typeof (Object[])) + { + return + new SelectExprInsertEventBeanFactory.SelectExprInsertNativeExpressionCoerceObjectArray( + insertIntoTargetType, expression.ExprEvaluator, _eventAdapterService); + } + else if (insertIntoTargetType is MapEventType && + returnType.IsImplementsInterface(typeof (IDictionary))) + { + return + new SelectExprInsertEventBeanFactory.SelectExprInsertNativeExpressionCoerceMap( + insertIntoTargetType, expression.ExprEvaluator, _eventAdapterService); + } + else if (insertIntoTargetType is BeanEventType && + TypeHelper.IsSubclassOrImplementsInterface( + returnType, insertIntoTargetType.UnderlyingType)) + { + return + new SelectExprInsertEventBeanFactory.SelectExprInsertNativeExpressionCoerceNative( + insertIntoTargetType, expression.ExprEvaluator, _eventAdapterService); + } + else if (insertIntoTargetType is AvroSchemaEventType && + returnType.FullName.Equals(AvroConstantsNoDep.GENERIC_RECORD_CLASSNAME)) + { + return + new SelectExprInsertEventBeanFactory.SelectExprInsertNativeExpressionCoerceAvro( + insertIntoTargetType, expression.ExprEvaluator, _eventAdapterService); + } + else if (insertIntoTargetType is WrapperEventType) + { + // for native event types as they got renamed, they become wrappers + // check if the proposed wrapper is compatible with the existing wrapper + var existing = (WrapperEventType) insertIntoTargetType; + if (existing.UnderlyingEventType is BeanEventType) + { + var innerType = (BeanEventType) existing.UnderlyingEventType; + ExprEvaluator evalExprEvaluator = + unnamedStreams[0].ExpressionSelectedAsStream.SelectExpression.ExprEvaluator; + if ( + !TypeHelper.IsSubclassOrImplementsInterface( + evalExprEvaluator.ReturnType, innerType.UnderlyingType)) + { + throw new ExprValidationException( + "Invalid expression return type '" + Name.Clean(evalExprEvaluator.ReturnType) + + "' for transpose function, expected '" + + innerType.UnderlyingType.Name + "'"); + } + resultEventTypeX = _eventAdapterService.AddWrapperType( + insertIntoTargetType.Name, existing.UnderlyingEventType, selPropertyTypes, + false, true); + return new EvalSelectStreamWUnderlying( + selectExprContext, resultEventTypeX, namedStreams, _isUsingWildcard, + unnamedStreams, false, false, underlyingStreamNumber, null, + evalExprEvaluator, null); + } + } + throw EvalInsertUtil.MakeEventTypeCastException(returnType, insertIntoTargetType); + } + + if (underlyingEventType != null) + { + // a single stream was selected via "stream.*" and there is no column name + // recast as a Map-type + if (underlyingEventType is MapEventType && insertIntoTargetType is MapEventType) + { + return EvalSelectStreamWUndRecastMapFactory.Make( + _typeService.EventTypes, selectExprContext, + _selectedStreams[0].StreamSelected.StreamNumber, insertIntoTargetType, exprNodes, + _engineImportService, _statementName, _typeService.EngineURIQualifier); + } + + // recast as a Object-array-type + if (underlyingEventType is ObjectArrayEventType && insertIntoTargetType is ObjectArrayEventType) + { + return EvalSelectStreamWUndRecastObjectArrayFactory.Make( + _typeService.EventTypes, selectExprContext, + _selectedStreams[0].StreamSelected.StreamNumber, insertIntoTargetType, exprNodes, + _engineImportService, _statementName, _typeService.EngineURIQualifier); + } + + // recast as a Avro-type + if (underlyingEventType is AvroSchemaEventType && insertIntoTargetType is AvroSchemaEventType) + { + return + _eventAdapterService.EventAdapterAvroHandler.GetOutputFactory().MakeRecast( + _typeService.EventTypes, selectExprContext, + _selectedStreams[0].StreamSelected.StreamNumber, + (AvroSchemaEventType) insertIntoTargetType, exprNodes, _statementName, + _typeService.EngineURIQualifier); + } + + // recast as a Bean-type + if (underlyingEventType is BeanEventType && insertIntoTargetType is BeanEventType) + { + return new EvalInsertBeanRecast( + insertIntoTargetType, _eventAdapterService, + _selectedStreams[0].StreamSelected.StreamNumber, _typeService.EventTypes); + } + + // wrap if no recast possible + TableMetadata tableMetadata = _tableService.GetTableMetadataFromEventType(underlyingEventType); + if (tableMetadata != null) + { + underlyingEventType = tableMetadata.PublicEventType; + } + resultEventTypeX = _eventAdapterService.AddWrapperType( + _insertIntoDesc.EventTypeName, underlyingEventType, selPropertyTypes, false, true); + return new EvalSelectStreamWUnderlying( + selectExprContext, resultEventTypeX, namedStreams, _isUsingWildcard, + unnamedStreams, singleStreamWrapper, underlyingIsFragmentEvent, underlyingStreamNumber, + underlyingPropertyEventGetter, underlyingExprEvaluator, tableMetadata); + } + else + { + // there are one or more streams selected with column name such as "stream.* as columnOne" + if (insertIntoTargetType is BeanEventType) + { + string name = _selectedStreams[0].StreamSelected.StreamName; + string alias = _selectedStreams[0].StreamSelected.OptionalName; + string syntaxUsed = name + ".*" + (alias != null ? " as " + alias : ""); + string syntaxInstead = name + (alias != null ? " as " + alias : ""); + throw new ExprValidationException( + "The '" + syntaxUsed + + "' syntax is not allowed when inserting into an existing bean event type, use the '" + + syntaxInstead + "' syntax instead"); + } + if (insertIntoTargetType == null || insertIntoTargetType is MapEventType) + { + resultEventTypeX = _eventAdapterService.AddNestableMapType( + _insertIntoDesc.EventTypeName, selPropertyTypes, null, false, false, false, false, true); + ISet propertiesToUnwrap = GetEventBeanToObjectProps( + selPropertyTypes, resultEventTypeX); + if (propertiesToUnwrap.IsEmpty()) + { + return new EvalSelectStreamNoUnderlyingMap( + selectExprContext, resultEventTypeX, namedStreams, _isUsingWildcard); + } + else + { + return new EvalSelectStreamNoUndWEventBeanToObj( + selectExprContext, resultEventTypeX, namedStreams, _isUsingWildcard, + propertiesToUnwrap); + } + } + else if (insertIntoTargetType is ObjectArrayEventType) + { + ISet propertiesToUnwrap = GetEventBeanToObjectProps( + selPropertyTypes, insertIntoTargetType); + if (propertiesToUnwrap.IsEmpty()) + { + return new EvalSelectStreamNoUnderlyingObjectArray( + selectExprContext, insertIntoTargetType, namedStreams, _isUsingWildcard); + } + else + { + return new EvalSelectStreamNoUndWEventBeanToObjObjArray( + selectExprContext, insertIntoTargetType, namedStreams, _isUsingWildcard, + propertiesToUnwrap); + } + } + else if (insertIntoTargetType is AvroSchemaEventType) + { + throw new ExprValidationException("Avro event type does not allow contained beans"); + } + else + { + throw new IllegalStateException("Unrecognized event type " + insertIntoTargetType); + } + } + } + + ValueAddEventProcessor vaeProcessor = + _valueAddEventService.GetValueAddProcessor(_insertIntoDesc.EventTypeName); + EventType resultEventType; + if (_isUsingWildcard) + { + if (vaeProcessor != null) + { + resultEventType = vaeProcessor.ValueAddEventType; + isRevisionEvent = true; + vaeProcessor.ValidateEventType(eventType); + } + else + { + if (insertIntoTargetType != null) + { + // handle insert-into with fast coercion (no additional properties selected) + if (selPropertyTypes.IsEmpty()) + { + if (insertIntoTargetType is BeanEventType && eventType is BeanEventType) + { + return new EvalInsertBeanRecast( + insertIntoTargetType, _eventAdapterService, 0, _typeService.EventTypes); + } + if (insertIntoTargetType is ObjectArrayEventType && eventType is ObjectArrayEventType) + { + var target = (ObjectArrayEventType) insertIntoTargetType; + var source = (ObjectArrayEventType) eventType; + string msg = BaseNestableEventType.IsDeepEqualsProperties( + eventType.Name, source.Types, target.Types); + if (msg == null) + { + return new EvalInsertCoercionObjectArray( + insertIntoTargetType, _eventAdapterService); + } + } + if (insertIntoTargetType is MapEventType && eventType is MapEventType) + { + return new EvalInsertCoercionMap(insertIntoTargetType, _eventAdapterService); + } + if (insertIntoTargetType is AvroSchemaEventType && eventType is AvroSchemaEventType) + { + return new EvalInsertCoercionAvro(insertIntoTargetType, _eventAdapterService); + } + if (insertIntoTargetType is WrapperEventType && eventType is BeanEventType) + { + var wrapperType = (WrapperEventType) insertIntoTargetType; + if (wrapperType.UnderlyingEventType is BeanEventType) + { + return new EvalInsertBeanWrapRecast( + wrapperType, _eventAdapterService, 0, _typeService.EventTypes); + } + } + } + + // handle insert-into by generating the writer with possible additional properties + SelectExprProcessor existingTypeProcessor = + SelectExprInsertEventBeanFactory.GetInsertUnderlyingNonJoin( + _eventAdapterService, insertIntoTargetType, _isUsingWildcard, _typeService, + exprEvaluators, columnNames, expressionReturnTypes, _engineImportService, + _insertIntoDesc, columnNamesAsProvided, true, _statementName); + if (existingTypeProcessor != null) + { + return existingTypeProcessor; + } + } + + if (selPropertyTypes.IsEmpty() && eventType is BeanEventType) + { + var beanEventType = (BeanEventType) eventType; + resultEventType = _eventAdapterService.AddBeanTypeByName( + _insertIntoDesc.EventTypeName, beanEventType.UnderlyingType, false); + } + else + { + resultEventType = _eventAdapterService.AddWrapperType( + _insertIntoDesc.EventTypeName, eventType, selPropertyTypes, false, true); + } + } + + if (singleStreamWrapper) + { + if (!isRevisionEvent) + { + return new EvalInsertWildcardSSWrapper(selectExprContext, resultEventType); + } + else + { + return new EvalInsertWildcardSSWrapperRevision( + selectExprContext, resultEventType, vaeProcessor); + } + } + if (joinWildcardProcessor == null) + { + if (!isRevisionEvent) + { + if (resultEventType is WrapperEventType) + { + return new EvalInsertWildcardWrapper(selectExprContext, resultEventType); + } + else + { + return new EvalInsertWildcardBean(selectExprContext, resultEventType); + } + } + else + { + if (exprEvaluators.Length == 0) + { + return new EvalInsertWildcardRevision(selectExprContext, resultEventType, vaeProcessor); + } + else + { + EventType wrappingEventType = + _eventAdapterService.AddWrapperType( + _insertIntoDesc.EventTypeName + "_wrapped", eventType, selPropertyTypes, false, + true); + return new EvalInsertWildcardRevisionWrapper( + selectExprContext, resultEventType, vaeProcessor, wrappingEventType); + } + } + } + else + { + if (!isRevisionEvent) + { + return new EvalInsertWildcardJoin(selectExprContext, resultEventType, joinWildcardProcessor); + } + else + { + return new EvalInsertWildcardJoinRevision( + selectExprContext, resultEventType, joinWildcardProcessor, vaeProcessor); + } + } + } + + // not using wildcard + resultEventType = null; + if ((columnNames.Length == 1) && (_insertIntoDesc.ColumnNames.Count == 0)) + { + if (insertIntoTargetType != null) + { + // check if the existing type and new type are compatible + object columnOneType = expressionReturnTypes[0]; + if (insertIntoTargetType is WrapperEventType) + { + var wrapperType = (WrapperEventType) insertIntoTargetType; + // Map and Object both supported + if (wrapperType.UnderlyingEventType.UnderlyingType == columnOneType) + { + singleColumnWrapOrBeanCoercion = true; + resultEventType = insertIntoTargetType; + } + } + if ((insertIntoTargetType is BeanEventType) && (columnOneType is Type)) + { + var beanType = (BeanEventType) insertIntoTargetType; + // Map and Object both supported + if (TypeHelper.IsSubclassOrImplementsInterface( + (Type) columnOneType, beanType.UnderlyingType)) + { + singleColumnWrapOrBeanCoercion = true; + resultEventType = insertIntoTargetType; + } + } + } + } + if (singleColumnWrapOrBeanCoercion) + { + if (!isRevisionEvent) + { + if (resultEventType is WrapperEventType) + { + var wrapper = (WrapperEventType) resultEventType; + if (wrapper.UnderlyingEventType is MapEventType) + { + return new EvalInsertNoWildcardSingleColCoercionMapWrap(selectExprContext, wrapper); + } + else if (wrapper.UnderlyingEventType is ObjectArrayEventType) + { + return new EvalInsertNoWildcardSingleColCoercionObjectArrayWrap( + selectExprContext, wrapper); + } + else if (wrapper.UnderlyingEventType is AvroSchemaEventType) + { + return new EvalInsertNoWildcardSingleColCoercionAvroWrap(selectExprContext, wrapper); + } + else if (wrapper.UnderlyingEventType is VariantEventType) + { + var variantEventType = (VariantEventType) wrapper.UnderlyingEventType; + vaeProcessor = _valueAddEventService.GetValueAddProcessor(variantEventType.Name); + return new EvalInsertNoWildcardSingleColCoercionBeanWrapVariant( + selectExprContext, wrapper, vaeProcessor); + } + else + { + return new EvalInsertNoWildcardSingleColCoercionBeanWrap(selectExprContext, wrapper); + } + } + else + { + if (resultEventType is BeanEventType) + { + return new EvalInsertNoWildcardSingleColCoercionBean(selectExprContext, resultEventType); + } + } + } + else + { + if (resultEventType is BeanEventType) + { + return new EvalInsertNoWildcardSingleColCoercionRevisionBean( + selectExprContext, resultEventType, vaeProcessor, vaeInnerEventType); + } + else + { + Func func; + if (resultEventType is MapEventType) + { + func = + (eventAdapterService, und, type) => + eventAdapterService.AdapterForTypedMap((IDictionary) und, type); + } + else if (resultEventType is ObjectArrayEventType) + { + func = + (eventAdapterService, und, type) => + eventAdapterService.AdapterForTypedObjectArray((Object[]) und, type); + } + else if (resultEventType is AvroSchemaEventType) + { + func = + (eventAdapterService, und, type) => + eventAdapterService.AdapterForTypedAvro(und, type); + } + else + { + func = + (eventAdapterService, und, type) => + eventAdapterService.AdapterForTypedObject(und, type); + } + return new EvalInsertNoWildcardSingleColCoercionRevisionFunc( + selectExprContext, resultEventType, vaeProcessor, vaeInnerEventType, func); + } + } + } + if (resultEventType == null) + { + if (vaeProcessor != null) + { + // Use an anonymous type if the target is not a variant stream + if (_valueAddEventService.GetValueAddProcessor(_insertIntoDesc.EventTypeName) == null) + { + resultEventType = + _eventAdapterService.CreateAnonymousMapType( + _statementId + "_vae_" + CollectionUtil.ToString(_assignedTypeNumberStack, "_"), + selPropertyTypes, true); + } + else + { + string statementName = "stmt_" + _statementId + "_insert"; + resultEventType = _eventAdapterService.AddNestableMapType( + statementName, selPropertyTypes, null, false, false, false, false, true); + } + } + else + { + EventType existingType = insertIntoTargetType; + if (existingType == null) + { + // The type may however be an auto-import or fully-qualified class name + Type clazz = null; + try + { + clazz = _engineImportService.ResolveType(_insertIntoDesc.EventTypeName, false); + } + catch (EngineImportException) + { + Log.Debug( + "Target stream name '" + _insertIntoDesc.EventTypeName + + "' is not resolved as a class name"); + } + if (clazz != null) + { + existingType = _eventAdapterService.AddBeanType(clazz.GetDefaultTypeName(), clazz, false, false, false); + } + } + + SelectExprProcessor selectExprInsertEventBean = null; + if (existingType != null) + { + selectExprInsertEventBean = + SelectExprInsertEventBeanFactory.GetInsertUnderlyingNonJoin( + _eventAdapterService, existingType, _isUsingWildcard, _typeService, exprEvaluators, + columnNames, expressionReturnTypes, _engineImportService, _insertIntoDesc, + columnNamesAsProvided, false, _statementName); + } + if (selectExprInsertEventBean != null) + { + return selectExprInsertEventBean; + } + else + { + // use the provided override-type if there is one + if (_optionalInsertIntoOverrideType != null) + { + resultEventType = insertIntoTargetType; + } + else if (existingType is AvroSchemaEventType) + { + _eventAdapterService.EventAdapterAvroHandler.AvroCompat(existingType, selPropertyTypes); + resultEventType = existingType; + } + else + { + var @out = EventRepresentationUtil.GetRepresentation( + _annotations, _configuration, AssignedType.NONE); + if (@out == EventUnderlyingType.MAP) + { + resultEventType = + _eventAdapterService.AddNestableMapType( + _insertIntoDesc.EventTypeName, selPropertyTypes, null, false, false, false, + false, true); + } + else if (@out == EventUnderlyingType.OBJECTARRAY) + { + resultEventType = + _eventAdapterService.AddNestableObjectArrayType( + _insertIntoDesc.EventTypeName, selPropertyTypes, null, false, false, false, + false, true, false, null); + } + else if (@out == EventUnderlyingType.AVRO) + { + resultEventType = _eventAdapterService.AddAvroType( + _insertIntoDesc.EventTypeName, selPropertyTypes, false, false, false, false, + true, _annotations, null, _statementName, _typeService.EngineURIQualifier); + } + else + { + throw new IllegalStateException("Unrecognized code " + @out); + } + } + } + } + } + if (vaeProcessor != null) + { + vaeProcessor.ValidateEventType(resultEventType); + vaeInnerEventType = resultEventType; + resultEventType = vaeProcessor.ValueAddEventType; + isRevisionEvent = true; + } + + if (!isRevisionEvent) + { + if (resultEventType is MapEventType) + { + return new EvalInsertNoWildcardMap(selectExprContext, resultEventType); + } + else if (resultEventType is ObjectArrayEventType) + { + return MakeObjectArrayConsiderReorder( + selectExprContext, (ObjectArrayEventType) resultEventType, _statementName, + _typeService.EngineURIQualifier); + } + else if (resultEventType is AvroSchemaEventType) + { + return + _eventAdapterService.EventAdapterAvroHandler.GetOutputFactory().MakeSelectNoWildcard( + selectExprContext, resultEventType, _tableService, _statementName, + _typeService.EngineURIQualifier); + } + else + { + throw new IllegalStateException("Unrecognized output type " + resultEventType); + } + } + else + { + return new EvalInsertNoWildcardRevision( + selectExprContext, resultEventType, vaeProcessor, vaeInnerEventType); + } + } + catch (EventAdapterException ex) + { + Log.Debug("Exception provided by event adapter: " + ex.Message, ex); + throw new ExprValidationException(ex.Message, ex); + } + } + } + + private bool IsGroupByRollupNullableExpression(ExprNode expr, GroupByRollupInfo groupByRollupInfo) + { + // if all levels include this key, we are fine + foreach (AggregationGroupByRollupLevel level in groupByRollupInfo.RollupDesc.Levels) + { + if (level.IsAggregationTop) + { + return true; + } + bool found = false; + foreach (int rollupKeyIndex in level.RollupKeys) + { + ExprNode groupExpression = groupByRollupInfo.ExprNodes[rollupKeyIndex]; + if (ExprNodeUtility.DeepEquals(groupExpression, expr)) + { + found = true; + break; + } + } + if (!found) + { + return true; + } + } + return false; + } + + private SelectExprProcessor MakeObjectArrayConsiderReorder( + SelectExprContext selectExprContext, + ObjectArrayEventType resultEventType, + string statementName, + string engineURI) + { + var wideners = new TypeWidener[selectExprContext.ColumnNames.Length]; + var remapped = new int[selectExprContext.ColumnNames.Length]; + bool needRemap = false; + for (int i = 0; i < selectExprContext.ColumnNames.Length; i++) + { + string colName = selectExprContext.ColumnNames[i]; + int index = CollectionUtil.FindItem(resultEventType.PropertyNames, colName); + if (index == -1) + { + throw new ExprValidationException( + "Could not find property '" + colName + "' in " + + GetTypeNameConsiderTable(resultEventType, _tableService)); + } + remapped[i] = index; + if (index != i) + { + needRemap = true; + } + Type sourceColumnType = selectExprContext.ExpressionNodes[i].ReturnType; + Type targetPropType = resultEventType.GetPropertyType(colName); + wideners[i] = TypeWidenerFactory.GetCheckPropertyAssignType( + colName, sourceColumnType, targetPropType, colName, false, + _eventAdapterService.GetTypeWidenerCustomizer(resultEventType), statementName, engineURI); + } + + if (!needRemap) + { + return new EvalInsertNoWildcardObjectArray(selectExprContext, resultEventType); + } + if (CollectionUtil.IsAllNullArray(wideners)) + { + return new EvalInsertNoWildcardObjectArrayRemap(selectExprContext, resultEventType, remapped); + } + return new EvalInsertNoWildcardObjectArrayRemapWWiden( + selectExprContext, resultEventType, remapped, wideners); + } + + private string GetTypeNameConsiderTable(ObjectArrayEventType resultEventType, TableService tableService) + { + TableMetadata metadata = tableService.GetTableMetadataFromEventType(resultEventType); + if (metadata != null) + { + return "table '" + metadata.TableName + "'"; + } + return "type '" + resultEventType.Name + "'"; + } + + private Pair HandleUnderlyingStreamInsert( + ExprEvaluator exprEvaluator, + NamedWindowMgmtService namedWindowMgmtService, + EventAdapterService eventAdapterService) + { + if (!(exprEvaluator is ExprStreamUnderlyingNode)) + { + return null; + } + var undNode = (ExprStreamUnderlyingNode) exprEvaluator; + int streamNum = undNode.StreamId; + Type returnType = undNode.ExprEvaluator.ReturnType; + EventType namedWindowAsType = GetNamedWindowUnderlyingType( + namedWindowMgmtService, eventAdapterService, _typeService.EventTypes[streamNum]); + TableMetadata tableMetadata = _tableService.GetTableMetadataFromEventType( + _typeService.EventTypes[streamNum]); + + EventType eventTypeStream; + ExprEvaluator evaluator; + if (tableMetadata != null) + { + eventTypeStream = tableMetadata.PublicEventType; + evaluator = new SelectExprProcessorEvalStreamInsertTable(streamNum, undNode, tableMetadata, returnType); + } + else if (namedWindowAsType == null) + { + eventTypeStream = _typeService.EventTypes[streamNum]; + evaluator = new SelectExprProcessorEvalStreamInsertUnd(undNode, streamNum, returnType); + } + else + { + eventTypeStream = namedWindowAsType; + evaluator = new SelectExprProcessorEvalStreamInsertNamedWindow( + streamNum, namedWindowAsType, returnType, eventAdapterService); + } + + return new Pair(evaluator, eventTypeStream); + } + + private EventType GetNamedWindowUnderlyingType( + NamedWindowMgmtService namedWindowMgmtService, + EventAdapterService eventAdapterService, + EventType eventType) + { + if (!namedWindowMgmtService.IsNamedWindow(eventType.Name)) + { + return null; + } + NamedWindowProcessor processor = namedWindowMgmtService.GetProcessor(eventType.Name); + if (processor.EventTypeAsName == null) + { + return null; + } + return eventAdapterService.GetEventTypeByName(processor.EventTypeAsName); + } + + private TypeAndFunctionPair HandleTypableExpression(ExprEvaluator exprEvaluator, int expressionNum) + { + if (!(exprEvaluator is ExprEvaluatorTypableReturn)) + { + return null; + } + + var typable = (ExprEvaluatorTypableReturn) exprEvaluator; + var eventTypeExpr = typable.RowProperties; + if (eventTypeExpr == null) + { + return null; + } + + EventType mapType = + _eventAdapterService.CreateAnonymousMapType( + _statementId + "_innereval_" + CollectionUtil.ToString(_assignedTypeNumberStack, "_") + "_" + + expressionNum, eventTypeExpr, true); + var evaluatorFragment = new SelectExprProcessorEvalTypableMap(mapType, exprEvaluator, _eventAdapterService); + + return new TypeAndFunctionPair(mapType, evaluatorFragment); + } + + private TypeAndFunctionPair HandleInsertIntoEnumeration( + string insertIntoColName, + EPType insertIntoTarget, + ExprEvaluator exprEvaluator, + EngineImportService engineImportService) + { + if (!(exprEvaluator is ExprEvaluatorEnumeration) || insertIntoTarget == null + || (!EPTypeHelper.IsCarryEvent(insertIntoTarget))) + { + return null; + } + + var enumeration = (ExprEvaluatorEnumeration) exprEvaluator; + EventType eventTypeSingle = enumeration.GetEventTypeSingle(_eventAdapterService, _statementId); + EventType eventTypeColl = enumeration.GetEventTypeCollection(_eventAdapterService, _statementId); + EventType sourceType = eventTypeSingle ?? eventTypeColl; + if (eventTypeColl == null && eventTypeSingle == null) + { + return null; // enumeration is untyped events (select-clause provided to subquery or 'new' operator) + } + if (((EventTypeSPI) sourceType).Metadata.TypeClass == TypeClass.ANONYMOUS) + { + return null; // we don't allow anonymous types here, thus excluding subquery multi-column selection + } + + // check type INFO + EventType targetType = EPTypeHelper.GetEventType(insertIntoTarget); + CheckTypeCompatible(insertIntoColName, targetType, sourceType); + + // handle collection target - produce EventBean[] + if (insertIntoTarget is EventMultiValuedEPType) + { + if (eventTypeColl != null) + { + var evaluatorFragmentX = new ProxyExprEvaluator + { + ProcEvaluate = eventsParams => + { + ICollection events = + enumeration.EvaluateGetROCollectionEvents(eventsParams); + if (events == null) + { + return null; + } + return events.ToArray(); + }, + ProcReturnType = () => TypeHelper.GetArrayType(targetType.UnderlyingType) + }; + return new TypeAndFunctionPair( + new EventType[] + { + targetType + }, evaluatorFragmentX); + } + var evaluatorFragmentY = new ProxyExprEvaluator + { + ProcEvaluate = eventsParams => + { + EventBean @event = enumeration.EvaluateGetEventBean(eventsParams); + if (@event == null) + { + return null; + } + return new EventBean[] + { + @event + }; + }, + ProcReturnType = () => TypeHelper.GetArrayType(targetType.UnderlyingType) + }; + return new TypeAndFunctionPair( + new EventType[] + { + targetType + }, evaluatorFragmentY); + } + + // handle single-bean target + // handle single-source + if (eventTypeSingle != null) + { + var evaluatorFragmentX = new ProxyExprEvaluator + { + ProcEvaluate = eventsParams => enumeration.EvaluateGetEventBean(eventsParams), + ProcReturnType = () => targetType.UnderlyingType + }; + return new TypeAndFunctionPair(targetType, evaluatorFragmentX); + } + + // handle collection-source by taking the first + var evaluatorFragment = new ProxyExprEvaluator + { + ProcEvaluate = eventsParams => + { + ICollection events = + enumeration.EvaluateGetROCollectionEvents(eventsParams); + if (events == null || events.Count == 0) + { + return null; + } + return EventBeanUtility.GetNonemptyFirstEvent(events); + }, + ProcReturnType = () => targetType.UnderlyingType + }; + return new TypeAndFunctionPair(targetType, evaluatorFragment); + } + + private void CheckTypeCompatible(string insertIntoCol, EventType targetType, EventType selectedType) + { + if (!EventTypeUtility.IsTypeOrSubTypeOf(targetType, selectedType)) + { + throw new ExprValidationException( + "Incompatible type detected attempting to insert into column '" + + insertIntoCol + "' type '" + targetType.Name + "' compared to selected type '" + selectedType.Name + + "'"); + } + } + + private TypeAndFunctionPair HandleInsertIntoTypableExpression( + EPType insertIntoTarget, + ExprEvaluator exprEvaluator, + EngineImportService engineImportService) + { + if (!(exprEvaluator is ExprEvaluatorTypableReturn) + || insertIntoTarget == null + || (!EPTypeHelper.IsCarryEvent(insertIntoTarget))) + { + return null; + } + + EventType targetType = EPTypeHelper.GetEventType(insertIntoTarget); + var typable = (ExprEvaluatorTypableReturn) exprEvaluator; + if (typable.IsMultirow == null) + { + // not typable after all + return null; + } + IDictionary eventTypeExpr = typable.RowProperties; + if (eventTypeExpr == null) + { + return null; + } + + ICollection writables = _eventAdapterService.GetWriteableProperties( + targetType, false); + var written = new List(); + var writtenOffered = new List>(); + + // from IDictionary determine properties and type widening that may be required + foreach (var offeredProperty in eventTypeExpr) + { + WriteablePropertyDescriptor writable = EventTypeUtility.FindWritable(offeredProperty.Key, writables); + if (writable == null) + { + throw new ExprValidationException( + "Failed to find property '" + offeredProperty.Key + "' among properties for target event type '" + + targetType.Name + "'"); + } + written.Add(writable); + writtenOffered.Add(offeredProperty); + } + + // determine widening and column type compatibility + var wideners = new TypeWidener[written.Count]; + var typeWidenerCustomizer = _eventAdapterService.GetTypeWidenerCustomizer(targetType); + for (int i = 0; i < written.Count; i++) + { + Type expected = written[i].PropertyType; + var provided = writtenOffered[i]; + if (provided.Value is Type) + { + wideners[i] = TypeWidenerFactory.GetCheckPropertyAssignType( + provided.Key, (Type) provided.Value, + expected, written[i].PropertyName, false, typeWidenerCustomizer, _statementName, + _typeService.EngineURIQualifier); + } + } + bool hasWideners = !CollectionUtil.IsAllNullArray(wideners); + + // obtain factory + WriteablePropertyDescriptor[] writtenArray = written.ToArray(); + EventBeanManufacturer manufacturer; + try + { + manufacturer = _eventAdapterService.GetManufacturer( + targetType, writtenArray, engineImportService, false); + } + catch (EventBeanManufactureException e) + { + throw new ExprValidationException("Failed to obtain eventbean factory: " + e.Message, e); + } + + // handle collection + EventBeanManufacturer factory = manufacturer; + if (insertIntoTarget is EventMultiValuedEPType && typable.IsMultirow.GetValueOrDefault()) + { + var evaluatorFragmentX = new ProxyExprEvaluator + { + ProcEvaluate = eventsParams => + { + object[][] rows = typable.EvaluateTypableMulti( + eventsParams.EventsPerStream, + eventsParams.IsNewData, + eventsParams.ExprEvaluatorContext + ); + if (rows == null) + { + return null; + } + if (rows.Length == 0) + { + return new EventBean[0]; + } + if (hasWideners) + { + ApplyWideners(rows, wideners); + } + var events = new EventBean[rows.Length]; + for (int i = 0; i < events.Length; i++) + { + events[i] = factory.Make(rows[i]); + } + return events; + }, + ProcReturnType = () => TypeHelper.GetArrayType(targetType.UnderlyingType) + }; + + return new TypeAndFunctionPair( + new EventType[] + { + targetType + }, evaluatorFragmentX); + } + else if (insertIntoTarget is EventMultiValuedEPType && !typable.IsMultirow.GetValueOrDefault()) + { + var evaluatorFragmentX = new ProxyExprEvaluator + { + ProcEvaluate = eventsParams => + { + object[] row = typable.EvaluateTypableSingle( + eventsParams.EventsPerStream, + eventsParams.IsNewData, + eventsParams.ExprEvaluatorContext + ); + if (row == null) + { + return null; + } + if (hasWideners) + { + ApplyWideners(row, wideners); + } + return new EventBean[] + { + factory.Make(row) + }; + }, + ProcReturnType = () => TypeHelper.GetArrayType(targetType.UnderlyingType) + }; + return new TypeAndFunctionPair( + new EventType[] + { + targetType + }, evaluatorFragmentX); + } + else if (insertIntoTarget is EventEPType && !typable.IsMultirow.GetValueOrDefault()) + { + var evaluatorFragmentX = new ProxyExprEvaluator + { + ProcEvaluate = eventsParams => + { + object[] row = typable.EvaluateTypableSingle( + eventsParams.EventsPerStream, + eventsParams.IsNewData, + eventsParams.ExprEvaluatorContext + ); + if (row == null) + { + return null; + } + if (hasWideners) + { + ApplyWideners(row, wideners); + } + return factory.Make(row); + }, + ProcReturnType = () => TypeHelper.GetArrayType(targetType.UnderlyingType) + }; + return new TypeAndFunctionPair(targetType, evaluatorFragmentX); + } + + // we are discarding all but the first row + var evaluatorFragment = new ProxyExprEvaluator + { + ProcEvaluate = eventsParams => + { + object[][] rows = typable.EvaluateTypableMulti( + eventsParams.EventsPerStream, + eventsParams.IsNewData, + eventsParams.ExprEvaluatorContext + ); + if (rows == null) + { + return null; + } + if (rows.Length == 0) + { + return new EventBean[0]; + } + if (hasWideners) + { + ApplyWideners(rows[0], wideners); + } + return factory.Make(rows[0]); + }, + ProcReturnType = () => TypeHelper.GetArrayType(targetType.UnderlyingType) + }; + return new TypeAndFunctionPair(targetType, evaluatorFragment); + } + + private void ApplyWideners(Object[] row, TypeWidener[] wideners) + { + for (int i = 0; i < wideners.Length; i++) + { + if (wideners[i] != null) + { + row[i] = wideners[i].Invoke(row[i]); + } + } + } + + private void ApplyWideners(Object[][] rows, TypeWidener[] wideners) + { + foreach (var row in rows) + { + ApplyWideners(row, wideners); + } + } + + private TypeAndFunctionPair HandleAtEventbeanEnumeration(bool isEventBeans, ExprEvaluator evaluator) + { + if (!(evaluator is ExprEvaluatorEnumeration) || !isEventBeans) + { + return null; + } + + var enumEval = (ExprEvaluatorEnumeration) evaluator; + EventType eventTypeSingle = enumEval.GetEventTypeSingle(_eventAdapterService, _statementId); + if (eventTypeSingle != null) + { + TableMetadata tableMetadata = _tableService.GetTableMetadataFromEventType(eventTypeSingle); + if (tableMetadata == null) + { + var evaluatorFragmentX = new ProxyExprEvaluator + { + ProcEvaluate = eventsParams => enumEval.EvaluateGetEventBean(eventsParams), + ProcReturnType = () => eventTypeSingle.UnderlyingType + }; + return new TypeAndFunctionPair(eventTypeSingle, evaluatorFragmentX); + } + var evaluatorFragment = new ProxyExprEvaluator + { + ProcEvaluate = eventsParams => + { + EventBean @event = enumEval.EvaluateGetEventBean(eventsParams); + if (@event == null) + { + return null; + } + return tableMetadata.EventToPublic.Convert(@event, eventsParams); + }, + ProcReturnType = () => tableMetadata.PublicEventType.UnderlyingType + }; + return new TypeAndFunctionPair(tableMetadata.PublicEventType, evaluatorFragment); + } + + EventType eventTypeColl = enumEval.GetEventTypeCollection(_eventAdapterService, _statementId); + if (eventTypeColl != null) + { + TableMetadata tableMetadata = _tableService.GetTableMetadataFromEventType(eventTypeColl); + if (tableMetadata == null) + { + var evaluatorFragmentX = new ProxyExprEvaluator + { + ProcEvaluate = eventsParams => + { + // the protocol is EventBean[] + Object result = enumEval.EvaluateGetROCollectionEvents(eventsParams); + if (result is ICollection) + { + var events = (ICollection) result; + return events.ToArray(); + } + return result; + }, + ProcReturnType = () => TypeHelper.GetArrayType(eventTypeColl.UnderlyingType) + }; + return new TypeAndFunctionPair( + new EventType[] + { + eventTypeColl + }, evaluatorFragmentX); + } + var evaluatorFragment = new ProxyExprEvaluator + { + ProcEvaluate = eventsParams => + { + // the protocol is EventBean[] + Object result = enumEval.EvaluateGetROCollectionEvents(eventsParams); + if (result == null) + { + return null; + } + if (result is ICollection) + { + var eventsX = (ICollection) result; + var @out = new EventBean[eventsX.Count]; + int index = 0; + foreach (EventBean @event in eventsX) + { + @out[index++] = tableMetadata.EventToPublic.Convert( + @event, eventsParams); + } + return @out; + } + var events = (EventBean[]) result; + for (int i = 0; i < events.Length; i++) + { + events[i] = tableMetadata.EventToPublic.Convert( + events[i], eventsParams); + } + return events; + }, + ProcReturnType = + () => TypeHelper.GetArrayType(tableMetadata.PublicEventType.UnderlyingType) + }; + return new TypeAndFunctionPair( + new EventType[] + { + tableMetadata.PublicEventType + }, evaluatorFragment); + } + + return null; + } + + private class TypeAndFunctionPair + { + internal TypeAndFunctionPair(Object type, ExprEvaluator function) + { + Type = type; + Function = function; + } + + public Object Type { get; private set; } + + public ExprEvaluator Function { get; private set; } + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/core/SelectExprProcessorRepresentationFactory.cs b/NEsper.Core/NEsper.Core/epl/core/SelectExprProcessorRepresentationFactory.cs new file mode 100755 index 000000000..bb220a0aa --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/SelectExprProcessorRepresentationFactory.cs @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.epl.core.eval; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.events; +using com.espertech.esper.events.avro; + +namespace com.espertech.esper.epl.core +{ + public interface SelectExprProcessorRepresentationFactory + { + SelectExprProcessor MakeSelectNoWildcard( + SelectExprContext selectExprContext, + EventType resultEventType, + TableService tableService, + string statementName, + string engineURI); + + SelectExprProcessor MakeRecast( + EventType[] eventTypes, + SelectExprContext selectExprContext, + int streamNumber, + AvroSchemaEventType insertIntoTargetType, + ExprNode[] exprNodes, + string statementName, + string engineURI); + + SelectExprProcessor MakeJoinWildcard( + string[] streamNames, + EventType resultEventType, + EventAdapterService eventAdapterService); + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/core/SelectExprProcessorWDeliveryCallback.cs b/NEsper.Core/NEsper.Core/epl/core/SelectExprProcessorWDeliveryCallback.cs new file mode 100755 index 000000000..0e94a281d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/SelectExprProcessorWDeliveryCallback.cs @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.core +{ + /// Interface for processors of select-clause items, implementors are computing results based on matching events. + public class SelectExprProcessorWDeliveryCallback : SelectExprProcessor + { + private readonly BindProcessor _bindProcessor; + private readonly EventType _eventType; + private readonly SelectExprProcessorDeliveryCallback _selectExprProcessorCallback; + + public SelectExprProcessorWDeliveryCallback(EventType eventType, + BindProcessor bindProcessor, + SelectExprProcessorDeliveryCallback selectExprProcessorCallback) + { + _eventType = eventType; + _bindProcessor = bindProcessor; + _selectExprProcessorCallback = selectExprProcessorCallback; + } + + #region SelectExprProcessor Members + + public EventType ResultEventType + { + get { return _eventType; } + } + + public EventBean Process(EventBean[] eventsPerStream, + bool isNewData, + bool isSynthesize, + ExprEvaluatorContext exprEvaluatorContext) + { + Object[] columns = _bindProcessor.Process(eventsPerStream, isNewData, exprEvaluatorContext); + return _selectExprProcessorCallback.Selected(columns); + } + + #endregion + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/core/SelectExprResultProcessor.cs b/NEsper.Core/NEsper.Core/epl/core/SelectExprResultProcessor.cs new file mode 100755 index 000000000..22836c558 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/SelectExprResultProcessor.cs @@ -0,0 +1,91 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.core +{ + /// + /// A select expression processor that check what type of result (synthetic and + /// natural) event is expected and produces. + /// + public class SelectExprResultProcessor : SelectExprProcessor + { + private readonly StatementResultService _statementResultService; + private readonly SelectExprProcessor _syntheticProcessor; + private readonly BindProcessor _bindProcessor; + + private static int seq = 0; + private readonly int id = seq++; + + /// Ctor. + /// for awareness of listeners and subscribers handles output results + /// is the processor generating synthetic events according to the select clause + /// for generating natural object column results + public SelectExprResultProcessor(StatementResultService statementResultService, + SelectExprProcessor syntheticProcessor, + BindProcessor bindProcessor) + { + _statementResultService = statementResultService; + _syntheticProcessor = syntheticProcessor; + _bindProcessor = bindProcessor; + } + + public EventType ResultEventType + { + get { return _syntheticProcessor.ResultEventType; } + } + + public EventBean Process(EventBean[] eventsPerStream, bool isNewData, bool isSynthesize, ExprEvaluatorContext exprEvaluatorContext) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QSelectClause(eventsPerStream, isNewData, isSynthesize, exprEvaluatorContext); } + if ((isSynthesize) && (!_statementResultService.IsMakeNatural)) + { + if (InstrumentationHelper.ENABLED) + { + EventBean result = _syntheticProcessor.Process(eventsPerStream, isNewData, isSynthesize, exprEvaluatorContext); + InstrumentationHelper.Get().ASelectClause(isNewData, result, null); + return result; + } + return _syntheticProcessor.Process(eventsPerStream, isNewData, isSynthesize, exprEvaluatorContext); + } + + EventBean syntheticEvent = null; + EventType syntheticEventType = null; + if (_statementResultService.IsMakeSynthetic || isSynthesize) + { + syntheticEvent = _syntheticProcessor.Process(eventsPerStream, isNewData, isSynthesize, exprEvaluatorContext); + + if (!_statementResultService.IsMakeNatural) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().ASelectClause(isNewData, syntheticEvent, null); } + return syntheticEvent; + } + + syntheticEventType = _syntheticProcessor.ResultEventType; + } + + if (!_statementResultService.IsMakeNatural) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().ASelectClause(isNewData, null, null); } + return null; // neither synthetic nor natural required, be cheap and generate no output event + } + + Object[] parameters = _bindProcessor.Process(eventsPerStream, isNewData, exprEvaluatorContext); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().ASelectClause(isNewData, null, parameters); } + return new NaturalEventBean(syntheticEventType, parameters, syntheticEvent); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/SelectExprWildcardProcessor.cs b/NEsper.Core/NEsper.Core/epl/core/SelectExprWildcardProcessor.cs new file mode 100755 index 000000000..8b5577ba1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/SelectExprWildcardProcessor.cs @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; + + +namespace com.espertech.esper.epl.core +{ + /// + /// Processor for select-clause expressions that handles wildcards for single streams with no insert-into. + /// + public class SelectExprWildcardProcessor : SelectExprProcessor + { + private readonly EventType eventType; + + /// Ctor. + /// is the type of event this processor produces + /// if the expression validation failed + public SelectExprWildcardProcessor(EventType eventType) + { + this.eventType = eventType; + } + + public EventBean Process(EventBean[] eventsPerStream, bool isNewData, bool isSynthesize, ExprEvaluatorContext exprEvaluatorContext) + { + return eventsPerStream[0]; + } + + public EventType ResultEventType + { + get { return eventType; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/SelectExprWildcardTableProcessor.cs b/NEsper.Core/NEsper.Core/epl/core/SelectExprWildcardTableProcessor.cs new file mode 100755 index 000000000..b34f544c4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/SelectExprWildcardTableProcessor.cs @@ -0,0 +1,44 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.table.mgmt; + +namespace com.espertech.esper.epl.core +{ + /// + /// Processor for select-clause expressions that handles wildcards for single streams with no insert-into. + /// + public class SelectExprWildcardTableProcessor : SelectExprProcessor + { + private readonly TableMetadata metadata; + + public SelectExprWildcardTableProcessor(string tableName, TableService tableService) { + metadata = tableService.GetTableMetadata(tableName); + } + + public EventBean Process(EventBean[] eventsPerStream, bool isNewData, bool isSynthesize, ExprEvaluatorContext exprEvaluatorContext) + { + EventBean @event = eventsPerStream[0]; + if (@event == null) { + return null; + } + return metadata.GetPublicEventBean(@event, eventsPerStream, isNewData, exprEvaluatorContext); + } + + public EventType ResultEventType + { + get { return metadata.PublicEventType; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/StreamNotFoundException.cs b/NEsper.Core/NEsper.Core/epl/core/StreamNotFoundException.cs new file mode 100755 index 000000000..285ab08b0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/StreamNotFoundException.cs @@ -0,0 +1,30 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; + +using com.espertech.esper.collection; + +namespace com.espertech.esper.epl.core +{ + /// Exception to indicate that a stream name could not be resolved. + [Serializable] + public class StreamNotFoundException : StreamTypesException + { + /// + /// Ctor. + /// + /// The message. + /// The MSG gen. + public StreamNotFoundException(String message, StreamTypesExceptionSuggestionGen msgGen) + : base(message, msgGen) + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/StreamTypeService.cs b/NEsper.Core/NEsper.Core/epl/core/StreamTypeService.cs new file mode 100755 index 000000000..bb95292aa --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/StreamTypeService.cs @@ -0,0 +1,130 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.epl.core +{ + /// + /// Service supplying stream number and property type information. + /// + public interface StreamTypeService + { + /// + /// Returns the offset of the stream and the type of the property for the given property name, by looking through the types offered and matching up. + /// + /// This method considers only a property name and looks at all streams to resolve the property name. + /// + /// + /// property name in event + /// if set to true [obtain fragment]. + /// + /// descriptor with stream number, property type and property name + /// + /// DuplicatePropertyException to indicate property was found twice + /// PropertyNotFoundException to indicate property could not be resolved + PropertyResolutionDescriptor ResolveByPropertyName(String propertyName, bool obtainFragment); + + /// + /// Returns the offset of the stream and the type of the property for the given property name, + /// by looking through the types offered considering only explicitly listed properties and matching up. + /// + /// This method considers only a property name and looks at all streams to resolve the property name. + /// + /// + /// property name in event + /// if set to true [obtain fragment]. + /// + /// descriptor with stream number, property type and property name + /// + /// DuplicatePropertyException to indicate property was found twice + /// PropertyNotFoundException to indicate property could not be resolved + PropertyResolutionDescriptor ResolveByPropertyNameExplicitProps(String propertyName, bool obtainFragment); + + /// + /// Returns the offset of the stream and the type of the property for the given property name, + /// by using the specified stream name to resolve the property. + /// + /// This method considers and explicit stream name and property name, both parameters are required. + /// + /// + /// name of stream, required + /// property name in event, , required + /// if set to true [obtain fragment]. + /// + /// descriptor with stream number, property type and property name + /// + /// PropertyNotFoundException to indicate property could not be resolved + /// StreamNotFoundException to indicate stream name could not be resolved + PropertyResolutionDescriptor ResolveByStreamAndPropName(String streamName, String propertyName, bool obtainFragment); + + /// + /// Returns the offset of the stream and the type of the property for the given property name, by + /// using the specified stream name to resolve the property and considering only explicitly listed + /// properties. + /// + /// This method considers and explicit stream name and property name, both parameters are required. + /// + /// + /// name of stream, required + /// property name in event, , required + /// if set to true [obtain fragment]. + /// + /// descriptor with stream number, property type and property name + /// + /// PropertyNotFoundException to indicate property could not be resolved + /// StreamNotFoundException to indicate stream name could not be resolved + PropertyResolutionDescriptor ResolveByStreamAndPropNameExplicitProps(String streamName, String propertyName, bool obtainFragment); + + /// + /// Returns the offset of the stream and the type of the property for the given property name, by looking through the types offered and matching up. + /// + /// This method considers a single property name that may or may not be prefixed by a stream name. The resolution first attempts to find the property + /// name itself, then attempts to consider a stream name that may be part of the property name. + /// + /// + /// stream name and property name (e.g. s0.p0) or just a property name (p0) + /// + /// descriptor with stream number, property type and property name + /// DuplicatePropertyException to indicate property was found twice + /// PropertyNotFoundException to indicate property could not be resolved + PropertyResolutionDescriptor ResolveByStreamAndPropName(String streamAndPropertyName, bool obtainFragment); + + /// + /// Returns an array of event stream names in the order declared. + /// + /// stream names + string[] StreamNames { get; } + + /// + /// Returns an array of event types for each event stream in the order declared. + /// + /// event types + EventType[] EventTypes { get; } + + /// + /// Returns true for each stream without a data window. + /// + /// true for non-windowed streams. + bool[] IsIStreamOnly { get; } + + int GetStreamNumForStreamName(String streamWildcard); + + bool IsOnDemandStreams { get; } + + string EngineURIQualifier { get; } + + bool HasPropertyAgnosticType { get; } + + bool HasTableTypes { get; } + + bool IsStreamZeroUnambigous { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/StreamTypeServiceImpl.cs b/NEsper.Core/NEsper.Core/epl/core/StreamTypeServiceImpl.cs new file mode 100755 index 000000000..e9cbf02c4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/StreamTypeServiceImpl.cs @@ -0,0 +1,666 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.parse; +using com.espertech.esper.events; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.core +{ + /// + /// Implementation that provides stream number and property type information. + /// + [Serializable] + public class StreamTypeServiceImpl : StreamTypeService + { + private readonly EventType[] _eventTypes; + private readonly String[] _streamNames; + private readonly bool[] _isIStreamOnly; + private readonly String _engineURIQualifier; + private bool _isStreamZeroUnambigous; + private bool _requireStreamNames; + private readonly bool _isOnDemandStreams; + private bool _hasTableTypes; + + /// + /// Ctor. + /// + /// engine URI + /// + public StreamTypeServiceImpl(String engineURI, bool isOnDemandStreams) + : this(new EventType[0], new String[0], new bool[0], engineURI, isOnDemandStreams) + { + } + + /// + /// Ctor. + /// + /// a single event type for a single stream + /// the stream name of the single stream + /// engine URI + /// true for no datawindow for stream + public StreamTypeServiceImpl(EventType eventType, String streamName, bool isIStreamOnly, String engineURI) + : this(new EventType[] { eventType }, new String[] { streamName }, new bool[] { isIStreamOnly }, engineURI, false) + { + } + + /// + /// Ctor. + /// + /// array of event types, one for each stream + /// array of stream names, one for each stream + /// true for no datawindow for stream + /// engine URI + /// true to indicate that all streams are on-demand pull-based + public StreamTypeServiceImpl(EventType[] eventTypes, String[] streamNames, bool[] isIStreamOnly, String engineURI, bool isOnDemandStreams) + { + _eventTypes = eventTypes; + _streamNames = streamNames; + _isIStreamOnly = isIStreamOnly; + _isOnDemandStreams = isOnDemandStreams; + + if (engineURI == null || EPServiceProviderConstants.DEFAULT_ENGINE_URI.Equals(engineURI)) + { + _engineURIQualifier = EPServiceProviderConstants.DEFAULT_ENGINE_URI_QUALIFIER; + } + else + { + _engineURIQualifier = engineURI; + } + + if (eventTypes.Length != streamNames.Length) + { + throw new ArgumentException("Number of entries for event types and stream names differs"); + } + + _hasTableTypes = DetermineHasTableTypes(); + } + + /// + /// Ctor. + /// + /// is the ordered list of stream names and event types available (stream zero to Count) + /// indicates whether when a property is found in stream zero and another stream an exception should bethrown or the stream zero should be assumed + /// + /// uri of the engine + /// is true to indicate that stream names are required for any non-zero streams (for subqueries) + public StreamTypeServiceImpl(LinkedHashMap> namesAndTypes, String engineURI, bool isStreamZeroUnambigous, bool requireStreamNames) + { + _isStreamZeroUnambigous = isStreamZeroUnambigous; + _requireStreamNames = requireStreamNames; + _engineURIQualifier = engineURI; + _isIStreamOnly = new bool[namesAndTypes.Count]; + _eventTypes = new EventType[namesAndTypes.Count]; + _streamNames = new String[namesAndTypes.Count]; + + var count = 0; + foreach (var entry in namesAndTypes) + { + _streamNames[count] = entry.Key; + _eventTypes[count] = entry.Value.First; + count++; + } + + _hasTableTypes = DetermineHasTableTypes(); + } + + private bool DetermineHasTableTypes() + { + return _eventTypes.OfType().Any(typeSPI => typeSPI.Metadata.TypeClass == TypeClass.TABLE); + } + + public bool RequireStreamNames + { + get { return _requireStreamNames; } + set { _requireStreamNames = value; } + } + + public bool IsOnDemandStreams + { + get { return _isOnDemandStreams; } + } + + public EventType[] EventTypes + { + get { return _eventTypes; } + } + + public string[] StreamNames + { + get { return _streamNames; } + } + + public bool[] IsIStreamOnly + { + get { return _isIStreamOnly; } + } + + public int GetStreamNumForStreamName(String streamWildcard) + { + for (var i = 0; i < _streamNames.Length; i++) + { + if (streamWildcard.Equals(_streamNames[i])) + { + return i; + } + } + return -1; + } + + public PropertyResolutionDescriptor ResolveByPropertyName(String propertyName, bool obtainFragment) + { + if (propertyName == null) + { + throw new ArgumentException("Null property name"); + } + var desc = FindByPropertyName(propertyName, obtainFragment); + if ((_requireStreamNames) && (desc.StreamNum != 0)) + { + throw new PropertyNotFoundException("Property named '" + propertyName + "' must be prefixed by a stream name, use the stream name itself or use the as-clause to name the stream with the property in the format \"stream.property\"", null); + } + return desc; + } + + public PropertyResolutionDescriptor ResolveByPropertyNameExplicitProps(String propertyName, bool obtainFragment) + { + if (propertyName == null) + { + throw new ArgumentException("Null property name"); + } + var desc = FindByPropertyNameExplicitProps(propertyName, obtainFragment); + if ((_requireStreamNames) && (desc.StreamNum != 0)) + { + throw new PropertyNotFoundException("Property named '" + propertyName + "' must be prefixed by a stream name, use the stream name itself or use the as-clause to name the stream with the property in the format \"stream.property\"", null); + } + return desc; + } + + public PropertyResolutionDescriptor ResolveByStreamAndPropName(String streamName, String propertyName, bool obtainFragment) + { + if (streamName == null) + { + throw new ArgumentException("Null property name"); + } + if (propertyName == null) + { + throw new ArgumentException("Null property name"); + } + return FindByStreamAndEngineName(propertyName, streamName, false, obtainFragment); + } + + public PropertyResolutionDescriptor ResolveByStreamAndPropNameExplicitProps(String streamName, String propertyName, bool obtainFragment) + { + if (streamName == null) + { + throw new ArgumentException("Null property name"); + } + if (propertyName == null) + { + throw new ArgumentException("Null property name"); + } + return FindByStreamAndEngineName(propertyName, streamName, true, obtainFragment); + } + + public PropertyResolutionDescriptor ResolveByStreamAndPropName(String streamAndPropertyName, bool obtainFragment) + { + if (streamAndPropertyName == null) + { + throw new ArgumentException("Null stream and property name"); + } + + PropertyResolutionDescriptor desc; + try + { + // first try to resolve as a property name + desc = FindByPropertyName(streamAndPropertyName, obtainFragment); + } + catch (PropertyNotFoundException ex) + { + // Attempt to resolve by extracting a stream name + var index = ASTUtil.UnescapedIndexOfDot(streamAndPropertyName); + if (index == -1) + { + throw; + } + var streamName = streamAndPropertyName.Substring(0, index); + var propertyName = streamAndPropertyName.Substring(index + 1); + try + { + // try to resolve a stream and property name + desc = FindByStreamAndEngineName(propertyName, streamName, false, obtainFragment); + } + catch (StreamNotFoundException) + { + // Consider the engine URI as a further prefix + var propertyNoEnginePair = GetIsEngineQualified(propertyName, streamName); + if (propertyNoEnginePair == null) + { + throw ex; + } + try + { + return FindByStreamNameOnly(propertyNoEnginePair.First, propertyNoEnginePair.Second, false, obtainFragment); + } + catch (StreamNotFoundException) + { + throw ex; + } + } + return desc; + } + + return desc; + } + + private PropertyResolutionDescriptor FindByPropertyName(String propertyName, bool obtainFragment) + { + var index = 0; + var foundIndex = 0; + var foundCount = 0; + EventType streamType = null; + + for (var i = 0; i < _eventTypes.Length; i++) + { + if (_eventTypes[i] != null) + { + Type propertyType = null; + var found = false; + FragmentEventType fragmentEventTypeX = null; + + if (_eventTypes[i].IsProperty(propertyName)) + { + propertyType = _eventTypes[i].GetPropertyType(propertyName); + if (obtainFragment) + { + fragmentEventTypeX = _eventTypes[i].GetFragmentType(propertyName); + } + found = true; + } + else + { + // mapped(expression) or array(expression) are not property names but expressions against a property by name "mapped" or "array" + var descriptor = _eventTypes[i].GetPropertyDescriptor(propertyName); + if (descriptor != null) + { + found = true; + propertyType = descriptor.PropertyType; + if (descriptor.IsFragment && obtainFragment) + { + fragmentEventTypeX = _eventTypes[i].GetFragmentType(propertyName); + } + } + } + + if (found) + { + streamType = _eventTypes[i]; + foundCount++; + foundIndex = index; + + // If the property could be resolved from stream 0 then we don't need to look further + if ((i == 0) && _isStreamZeroUnambigous) + { + return new PropertyResolutionDescriptor(_streamNames[0], _eventTypes[0], propertyName, 0, propertyType, fragmentEventTypeX); + } + } + } + index++; + } + + HandleFindExceptions(propertyName, foundCount, streamType); + + FragmentEventType fragmentEventType = null; + if (obtainFragment) + { + fragmentEventType = streamType.GetFragmentType(propertyName); + } + + return new PropertyResolutionDescriptor(_streamNames[foundIndex], _eventTypes[foundIndex], propertyName, foundIndex, streamType.GetPropertyType(propertyName), fragmentEventType); + } + + private PropertyResolutionDescriptor FindByPropertyNameExplicitProps(String propertyName, bool obtainFragment) + { + var index = 0; + var foundIndex = 0; + var foundCount = 0; + EventType streamType = null; + + for (var i = 0; i < _eventTypes.Length; i++) + { + if (_eventTypes[i] != null) + { + var descriptors = _eventTypes[i].PropertyDescriptors; + Type propertyType = null; + var found = false; + FragmentEventType fragmentEventTypeX = null; + + foreach (var desc in descriptors) + { + if (desc.PropertyName.Equals(propertyName)) + { + propertyType = desc.PropertyType; + found = true; + if (obtainFragment && desc.IsFragment) + { + fragmentEventTypeX = _eventTypes[i].GetFragmentType(propertyName); + } + } + } + + if (found) + { + streamType = _eventTypes[i]; + foundCount++; + foundIndex = index; + + // If the property could be resolved from stream 0 then we don't need to look further + if ((i == 0) && _isStreamZeroUnambigous) + { + return new PropertyResolutionDescriptor(_streamNames[0], _eventTypes[0], propertyName, 0, propertyType, fragmentEventTypeX); + } + } + } + index++; + } + + HandleFindExceptions(propertyName, foundCount, streamType); + + FragmentEventType fragmentEventType = null; + if (obtainFragment) + { + fragmentEventType = streamType.GetFragmentType(propertyName); + } + + return new PropertyResolutionDescriptor(_streamNames[foundIndex], _eventTypes[foundIndex], propertyName, foundIndex, streamType.GetPropertyType(propertyName), fragmentEventType); + } + + private void HandleFindExceptions(String propertyName, int foundCount, EventType streamType) + { + if (foundCount > 1) + { + throw new DuplicatePropertyException("Property named '" + propertyName + "' is ambiguous as is valid for more then one stream"); + } + + if (streamType == null) + { + var message = "Property named '" + propertyName + "' is not valid in any stream"; + var msgGen = new PropertyNotFoundExceptionSuggestionGenMultiTyped(_eventTypes, propertyName); + throw new PropertyNotFoundException(message, msgGen.GetSuggestion); + } + } + + private PropertyResolutionDescriptor FindByStreamAndEngineName(String propertyName, String streamName, bool explicitPropertiesOnly, bool obtainFragment) + { + PropertyResolutionDescriptor desc; + try + { + desc = FindByStreamNameOnly(propertyName, streamName, explicitPropertiesOnly, obtainFragment); + } + catch (PropertyNotFoundException) + { + var propertyNoEnginePair = GetIsEngineQualified(propertyName, streamName); + if (propertyNoEnginePair == null) + { + throw; + } + return FindByStreamNameOnly(propertyNoEnginePair.First, propertyNoEnginePair.Second, explicitPropertiesOnly, obtainFragment); + } + catch (StreamNotFoundException) + { + var propertyNoEnginePair = GetIsEngineQualified(propertyName, streamName); + if (propertyNoEnginePair == null) + { + throw; + } + return FindByStreamNameOnly(propertyNoEnginePair.First, propertyNoEnginePair.Second, explicitPropertiesOnly, obtainFragment); + } + return desc; + } + + private Pair GetIsEngineQualified(String propertyName, String streamName) + { + + // If still not found, test for the stream name to contain the engine URI + if (!streamName.Equals(_engineURIQualifier)) + { + return null; + } + + var index = ASTUtil.UnescapedIndexOfDot(propertyName); + if (index == -1) + { + return null; + } + + var streamNameNoEngine = propertyName.Substring(0, index); + var propertyNameNoEngine = propertyName.Substring(index + 1); + return new Pair(propertyNameNoEngine, streamNameNoEngine); + } + + private PropertyResolutionDescriptor FindByStreamNameOnly(String propertyName, String streamName, bool explicitPropertiesOnly, bool obtainFragment) + { + var index = 0; + EventType streamType = null; + + // Stream name resultion examples: + // A) select A1.price from Event.price as A2 => mismatch stream name, cannot resolve + // B) select Event1.price from Event2.price => mismatch event type name, cannot resolve + // C) select default.Event2.price from Event2.price => possible prefix of engine name + for (var i = 0; i < _eventTypes.Length; i++) + { + if (_eventTypes[i] == null) + { + index++; + continue; + } + if ((_streamNames[i] != null) && (_streamNames[i].Equals(streamName))) + { + streamType = _eventTypes[i]; + break; + } + + // If the stream name is the event type name, that is also acceptable + if ((_eventTypes[i].Name != null) && (_eventTypes[i].Name.Equals(streamName))) + { + streamType = _eventTypes[i]; + break; + } + + index++; + } + + if (streamType == null) + { + var message = "Failed to find a stream named '" + streamName + "'"; + var msgGen = new StreamNotFoundExceptionSuggestionGen(_eventTypes, _streamNames, streamName); + throw new StreamNotFoundException(message, msgGen.GetSuggestion); + } + + Type propertyType = null; + FragmentEventType fragmentEventType = null; + + if (!explicitPropertiesOnly) + { + propertyType = streamType.GetPropertyType(propertyName); + if (propertyType == null) + { + var desc = streamType.GetPropertyDescriptor(propertyName); + if (desc == null) + { + throw HandlePropertyNotFound(propertyName, streamName, streamType); + } + propertyType = desc.PropertyType; + if (obtainFragment && desc.IsFragment) + { + fragmentEventType = streamType.GetFragmentType(propertyName); + } + } + else + { + if (obtainFragment) + { + fragmentEventType = streamType.GetFragmentType(propertyName); + } + } + } + else + { + var explicitProps = streamType.PropertyDescriptors; + var found = false; + foreach (var prop in explicitProps) + { + if (prop.PropertyName.Equals(propertyName)) + { + propertyType = prop.PropertyType; + if (obtainFragment && prop.IsFragment) + { + fragmentEventType = streamType.GetFragmentType(propertyName); + } + found = true; + break; + } + } + if (!found) + { + throw HandlePropertyNotFound(propertyName, streamName, streamType); + } + } + + return new PropertyResolutionDescriptor(streamName, streamType, propertyName, index, propertyType, fragmentEventType); + } + + private PropertyNotFoundException HandlePropertyNotFound(String propertyName, String streamName, EventType streamType) + { + var message = "Property named '" + propertyName + "' is not valid in stream '" + streamName + "'"; + var msgGen = new PropertyNotFoundExceptionSuggestionGenSingleTyped(streamType, propertyName); + return new PropertyNotFoundException(message, msgGen.GetSuggestion); + } + + public string EngineURIQualifier + { + get { return _engineURIQualifier; } + } + + public bool HasPropertyAgnosticType + { + get + { + return _eventTypes.OfType().Any(spi => spi.Metadata.IsPropertyAgnostic); + } + } + + public bool IsStreamZeroUnambigous + { + get { return _isStreamZeroUnambigous; } + set { _isStreamZeroUnambigous = value; } + } + + public bool HasTableTypes + { + get { return _hasTableTypes; } + } + + internal class PropertyNotFoundExceptionSuggestionGenMultiTyped + { + private readonly EventType[] _eventTypes; + private readonly String _propertyName; + + internal PropertyNotFoundExceptionSuggestionGenMultiTyped(EventType[] eventTypes, String propertyName) + { + _eventTypes = eventTypes; + _propertyName = propertyName; + } + + public Pair GetSuggestion() + { + return StreamTypeServiceUtil.FindLevMatch(_eventTypes, _propertyName); + } + } + + internal class PropertyNotFoundExceptionSuggestionGenSingleTyped + { + private readonly EventType _eventType; + private readonly String _propertyName; + + internal PropertyNotFoundExceptionSuggestionGenSingleTyped(EventType eventType, String propertyName) + { + _eventType = eventType; + _propertyName = propertyName; + } + + public Pair GetSuggestion() + { + return StreamTypeServiceUtil.FindLevMatch(_propertyName, _eventType); + } + } + + internal class StreamNotFoundExceptionSuggestionGen + { + private readonly EventType[] _eventTypes; + private readonly String[] _streamNames; + private readonly String _streamName; + + public StreamNotFoundExceptionSuggestionGen(EventType[] eventTypes, String[] streamNames, String streamName) + { + _eventTypes = eventTypes; + _streamNames = streamNames; + _streamName = streamName; + } + + public Pair GetSuggestion() + { + // find a near match, textually + String bestMatch = null; + var bestMatchDiff = int.MaxValue; + + for (var i = 0; i < _eventTypes.Length; i++) + { + if (_streamNames[i] != null) + { + var diff = LevenshteinDistance.ComputeLevenshteinDistance(_streamNames[i], _streamName); + if (diff < bestMatchDiff) + { + bestMatchDiff = diff; + bestMatch = _streamNames[i]; + } + } + + if (_eventTypes[i] == null) + { + continue; + } + + // If the stream name is the event type name, that is also acceptable + if (_eventTypes[i].Name != null) + { + var diff = LevenshteinDistance.ComputeLevenshteinDistance(_eventTypes[i].Name, _streamName); + if (diff < bestMatchDiff) + { + bestMatchDiff = diff; + bestMatch = _eventTypes[i].Name; + } + } + } + + Pair suggestion = null; + if (bestMatchDiff < int.MaxValue) + { + suggestion = new Pair(bestMatchDiff, bestMatch); + } + return suggestion; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/StreamTypeServiceUtil.cs b/NEsper.Core/NEsper.Core/epl/core/StreamTypeServiceUtil.cs new file mode 100755 index 000000000..be06dda1e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/StreamTypeServiceUtil.cs @@ -0,0 +1,68 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.core +{ + public class StreamTypeServiceUtil + { + internal static Pair FindLevMatch(EventType[] eventTypes, string propertyName) + { + string bestMatch = null; + int bestMatchDiff = int.MaxValue; + for (int i = 0; i < eventTypes.Length; i++) + { + if (eventTypes[i] == null) + { + continue; + } + var props = eventTypes[i].PropertyDescriptors; + for (int j = 0; j < props.Count; j++) + { + int diff = LevenshteinDistance.ComputeLevenshteinDistance(propertyName, props[j].PropertyName); + if (diff < bestMatchDiff) + { + bestMatchDiff = diff; + bestMatch = props[j].PropertyName; + } + } + } + + if (bestMatchDiff < int.MaxValue) + { + return new Pair(bestMatchDiff, bestMatch); + } + return null; + } + + internal static Pair FindLevMatch(string propertyName, EventType eventType) + { + string bestMatch = null; + int bestMatchDiff = int.MaxValue; + var props = eventType.PropertyDescriptors; + for (int j = 0; j < props.Count; j++) + { + int diff = LevenshteinDistance.ComputeLevenshteinDistance(propertyName, props[j].PropertyName); + if (diff < bestMatchDiff) + { + bestMatchDiff = diff; + bestMatch = props[j].PropertyName; + } + } + + if (bestMatchDiff < int.MaxValue) + { + return new Pair(bestMatchDiff, bestMatch); + } + return null; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/StreamTypesException.cs b/NEsper.Core/NEsper.Core/epl/core/StreamTypesException.cs new file mode 100755 index 000000000..2fbc3ffcc --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/StreamTypesException.cs @@ -0,0 +1,35 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.collection; + +namespace com.espertech.esper.epl.core +{ + /// Base class for stream and property name resolution errors. + public abstract class StreamTypesException : Exception + { + private readonly StreamTypesExceptionSuggestionGen _optionalSuggestionGenerator; + + public StreamTypesException(string message, StreamTypesExceptionSuggestionGen optionalSuggestionGenerator) + : base(message) + { + _optionalSuggestionGenerator = optionalSuggestionGenerator; + } + + /// + /// Returns the optional suggestion for a matching name. + /// + /// suggested match + public Pair OptionalSuggestion + { + get { return _optionalSuggestionGenerator != null ? _optionalSuggestionGenerator.Invoke() : null; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/StreamTypesExceptionSuggestionGen.cs b/NEsper.Core/NEsper.Core/epl/core/StreamTypesExceptionSuggestionGen.cs new file mode 100755 index 000000000..db52a31a9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/StreamTypesExceptionSuggestionGen.cs @@ -0,0 +1,14 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.collection; + +namespace com.espertech.esper.epl.core +{ + public delegate Pair StreamTypesExceptionSuggestionGen(); +} diff --git a/NEsper.Core/NEsper.Core/epl/core/ViewResourceDelegateUnverified.cs b/NEsper.Core/NEsper.Core/epl/core/ViewResourceDelegateUnverified.cs new file mode 100755 index 000000000..6b674b868 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ViewResourceDelegateUnverified.cs @@ -0,0 +1,49 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.prev; +using com.espertech.esper.epl.expression.prior; + +namespace com.espertech.esper.epl.core +{ + /// + /// Coordinates between view factories and requested resource (by expressions) the availability of view resources to expressions. + /// + public class ViewResourceDelegateUnverified + { + private readonly IList _priorRequests; + private readonly IList _previousRequests; + + public ViewResourceDelegateUnverified() + { + _priorRequests = new List(); + _previousRequests = new List(); + } + + public IList PriorRequests + { + get { return _priorRequests; } + } + + public void AddPriorNodeRequest(ExprPriorNode priorNode) { + _priorRequests.Add(priorNode); + } + + public void AddPreviousRequest(ExprPreviousNode previousNode) { + _previousRequests.Add(previousNode); + } + + public IList PreviousRequests + { + get { return _previousRequests; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/ViewResourceDelegateVerified.cs b/NEsper.Core/NEsper.Core/epl/core/ViewResourceDelegateVerified.cs new file mode 100755 index 000000000..6d119d653 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ViewResourceDelegateVerified.cs @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.core +{ + /// + /// Coordinates between view factories and requested resource (by expressions) the availability of view resources to expressions. + /// + public class ViewResourceDelegateVerified + { + public ViewResourceDelegateVerified(bool hasPrior, bool hasPrevious, ViewResourceDelegateVerifiedStream[] perStream) { + HasPrior = hasPrior; + HasPrevious = hasPrevious; + PerStream = perStream; + } + + public ViewResourceDelegateVerifiedStream[] PerStream { get; private set; } + + public bool HasPrior { get; private set; } + + public bool HasPrevious { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/ViewResourceDelegateVerifiedStream.cs b/NEsper.Core/NEsper.Core/epl/core/ViewResourceDelegateVerifiedStream.cs new file mode 100755 index 000000000..a1386a347 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/ViewResourceDelegateVerifiedStream.cs @@ -0,0 +1,76 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.prev; +using com.espertech.esper.epl.expression.prior; + +namespace com.espertech.esper.epl.core +{ + /// + /// Coordinates between view factories and requested resource (by expressions) the availability of view resources to expressions. + /// + public class ViewResourceDelegateVerifiedStream + { + private readonly IList _previousRequests; + private readonly IDictionary> _priorRequests; + private readonly ICollection _matchRecognizePreviousRequests; + + /// + /// Initializes a new instance of the class. + /// + /// The previous requests. + /// The prior requests. + /// The match recognize previous requests. + public ViewResourceDelegateVerifiedStream( + IList previousRequests, + IDictionary> priorRequests, + ISet matchRecognizePreviousRequests) + { + _previousRequests = previousRequests; + _priorRequests = priorRequests; + _matchRecognizePreviousRequests = matchRecognizePreviousRequests; + } + + public IList PreviousRequests + { + get { return _previousRequests; } + } + + public IDictionary> PriorRequests + { + get { return _priorRequests; } + } + + public ICollection MatchRecognizePreviousRequests + { + get { return _matchRecognizePreviousRequests; } + } + + public IList PriorRequestsAsList + { + get + { + if (_priorRequests.IsEmpty()) + { + return Collections.GetEmptyList(); + } + + var nodes = new List(); + foreach (IList priorNodes in _priorRequests.Values) + { + nodes.AddAll(priorNodes); + } + return nodes; + } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/core/eval/EvalBase.cs b/NEsper.Core/NEsper.Core/epl/core/eval/EvalBase.cs new file mode 100755 index 000000000..26e0c97a5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/eval/EvalBase.cs @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.core.eval +{ + public abstract class EvalBase + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly EventType _resultEventType; + private readonly SelectExprContext _selectExprContext; + + protected EvalBase(SelectExprContext selectExprContext, EventType resultEventType) + { + _selectExprContext = selectExprContext; + _resultEventType = resultEventType; + } + + internal SelectExprContext SelectExprContext + { + get { return _selectExprContext; } + } + + public EventAdapterService EventAdapterService + { + get { return _selectExprContext.EventAdapterService; } + } + + public EventType ResultEventType + { + get { return _resultEventType; } + } + + public ExprEvaluator[] ExprNodes + { + get { return _selectExprContext.ExpressionNodes; } + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/core/eval/EvalBaseFirstProp.cs b/NEsper.Core/NEsper.Core/epl/core/eval/EvalBaseFirstProp.cs new file mode 100755 index 000000000..5776ebfe2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/eval/EvalBaseFirstProp.cs @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.core.eval +{ + public abstract class EvalBaseFirstProp : SelectExprProcessor + { + private readonly SelectExprContext _selectExprContext; + + protected EvalBaseFirstProp(SelectExprContext selectExprContext, EventType resultEventType) + { + _selectExprContext = selectExprContext; + ResultEventType = resultEventType; + } + + public abstract EventBean ProcessFirstCol(Object result); + + public EventBean Process(EventBean[] eventsPerStream, bool isNewData, bool isSynthesize, ExprEvaluatorContext exprEvaluatorContext) + { + ExprEvaluator[] expressionNodes = _selectExprContext.ExpressionNodes; + + Object first = expressionNodes[0].Evaluate(new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext)); + return ProcessFirstCol(first); + } + + public EventAdapterService EventAdapterService + { + get { return _selectExprContext.EventAdapterService; } + } + + public EventType ResultEventType { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/eval/EvalBaseFirstPropFromWrap.cs b/NEsper.Core/NEsper.Core/epl/core/eval/EvalBaseFirstPropFromWrap.cs new file mode 100755 index 000000000..63ebc2fa7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/eval/EvalBaseFirstPropFromWrap.cs @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.core.eval +{ + public abstract class EvalBaseFirstPropFromWrap : EvalBaseFirstProp + { + protected readonly WrapperEventType Wrapper; + + protected EvalBaseFirstPropFromWrap(SelectExprContext selectExprContext, WrapperEventType wrapper) + : base(selectExprContext, wrapper) + { + Wrapper = wrapper; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/eval/EvalBaseMap.cs b/NEsper.Core/NEsper.Core/epl/core/eval/EvalBaseMap.cs new file mode 100755 index 000000000..6d44061dd --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/eval/EvalBaseMap.cs @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.core.eval +{ + public abstract class EvalBaseMap : EvalBase, SelectExprProcessor + { + protected EvalBaseMap(SelectExprContext selectExprContext, EventType resultEventType) + : base(selectExprContext, resultEventType) + { + } + + public abstract EventBean ProcessSpecific(IDictionary props, EventBean[] eventsPerStream, bool isNewData, bool isSynthesize, ExprEvaluatorContext exprEvaluatorContext); + + public EventBean Process(EventBean[] eventsPerStream, bool isNewData, bool isSynthesize, ExprEvaluatorContext exprEvaluatorContext) + { + ExprEvaluator[] expressionNodes = SelectExprContext.ExpressionNodes; + String[] columnNames = SelectExprContext.ColumnNames; + + // Evaluate all expressions and build a map of name-value pairs + IDictionary props; + if (expressionNodes.Length == 0) + { + props = Collections.EmptyDataMap; + } + else + { + props = new Dictionary(); + for (int i = 0; i < expressionNodes.Length; i++) + { + Object evalResult = expressionNodes[i].Evaluate(new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext)); + props.Put(columnNames[i], evalResult); + } + } + + return ProcessSpecific(props, eventsPerStream, isNewData, isSynthesize, exprEvaluatorContext); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertBeanRecast.cs b/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertBeanRecast.cs new file mode 100755 index 000000000..4a9ff9d2d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertBeanRecast.cs @@ -0,0 +1,52 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.core.eval +{ + public class EvalInsertBeanRecast : SelectExprProcessor + { + + private readonly EventType _eventType; + private readonly EventAdapterService _eventAdapterService; + private readonly int _streamNumber; + + public EvalInsertBeanRecast(EventType targetType, EventAdapterService eventAdapterService, int streamNumber, EventType[] typesPerStream) + { + _eventType = targetType; + _eventAdapterService = eventAdapterService; + _streamNumber = streamNumber; + + EventType sourceType = typesPerStream[streamNumber]; + Type sourceClass = sourceType.UnderlyingType; + Type targetClass = targetType.UnderlyingType; + if (!TypeHelper.IsSubclassOrImplementsInterface(sourceClass, targetClass)) + { + throw EvalInsertUtil.MakeEventTypeCastException(sourceType, targetType); + } + } + + public EventBean Process(EventBean[] eventsPerStream, bool isNewData, bool isSynthesize, ExprEvaluatorContext exprEvaluatorContext) + { + EventBean theEvent = eventsPerStream[_streamNumber]; + return _eventAdapterService.AdapterForTypedObject(theEvent.Underlying, _eventType); + } + + public EventType ResultEventType + { + get { return _eventType; } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertBeanWrapRecast.cs b/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertBeanWrapRecast.cs new file mode 100755 index 000000000..3a44467f7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertBeanWrapRecast.cs @@ -0,0 +1,64 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.core.eval +{ + public class EvalInsertBeanWrapRecast : SelectExprProcessor + { + private readonly EventAdapterService _eventAdapterService; + private readonly WrapperEventType _eventType; + private readonly int _streamNumber; + + public EvalInsertBeanWrapRecast(WrapperEventType targetType, + EventAdapterService eventAdapterService, + int streamNumber, + EventType[] typesPerStream) + { + _eventType = targetType; + _eventAdapterService = eventAdapterService; + _streamNumber = streamNumber; + + EventType sourceType = typesPerStream[streamNumber]; + Type sourceClass = sourceType.UnderlyingType; + Type targetClass = targetType.UnderlyingEventType.UnderlyingType; + if (!TypeHelper.IsSubclassOrImplementsInterface(sourceClass, targetClass)) + { + throw EvalInsertUtil.MakeEventTypeCastException(sourceType, targetType); + } + } + + #region SelectExprProcessor Members + + public EventBean Process(EventBean[] eventsPerStream, + bool isNewData, + bool isSynthesize, + ExprEvaluatorContext exprEvaluatorContext) + { + EventBean theEvent = eventsPerStream[_streamNumber]; + EventBean recast = _eventAdapterService.AdapterForTypedObject(theEvent.Underlying, + _eventType.UnderlyingEventType); + return _eventAdapterService.AdapterForTypedWrapper(recast, new Dictionary(), _eventType); + } + + public EventType ResultEventType + { + get { return _eventType; } + } + + #endregion + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertCoercionAvro.cs b/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertCoercionAvro.cs new file mode 100755 index 000000000..365b00687 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertCoercionAvro.cs @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.core.eval +{ + public class EvalInsertCoercionAvro : SelectExprProcessor + { + private readonly EventAdapterService _eventAdapterService; + private readonly EventType _resultEventType; + + public EvalInsertCoercionAvro(EventType resultEventType, EventAdapterService eventAdapterService) + { + _resultEventType = resultEventType; + _eventAdapterService = eventAdapterService; + } + + public EventBean Process( + EventBean[] eventsPerStream, + bool isNewData, + bool isSynthesize, + ExprEvaluatorContext exprEvaluatorContext) + { + return _eventAdapterService.AdapterForTypedAvro(eventsPerStream[0].Underlying, _resultEventType); + } + + public EventType ResultEventType + { + get { return _resultEventType; } + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertCoercionMap.cs b/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertCoercionMap.cs new file mode 100755 index 000000000..dc9f696fa --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertCoercionMap.cs @@ -0,0 +1,36 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.core.eval +{ + public class EvalInsertCoercionMap : SelectExprProcessor + { + private readonly EventType _resultEventType; + private readonly EventAdapterService _eventAdapterService; + + public EvalInsertCoercionMap(EventType resultEventType, EventAdapterService eventAdapterService) { + _resultEventType = resultEventType; + _eventAdapterService = eventAdapterService; + } + + public EventBean Process(EventBean[] eventsPerStream, bool isNewData, bool isSynthesize, ExprEvaluatorContext exprEvaluatorContext) { + var theEvent = (MappedEventBean) eventsPerStream[0]; + return _eventAdapterService.AdapterForTypedMap(theEvent.Properties, _resultEventType); + } + + public EventType ResultEventType + { + get { return _resultEventType; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertCoercionObjectArray.cs b/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertCoercionObjectArray.cs new file mode 100755 index 000000000..9a2376ee4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertCoercionObjectArray.cs @@ -0,0 +1,36 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.core.eval +{ + public class EvalInsertCoercionObjectArray : SelectExprProcessor + { + private readonly EventType _resultEventType; + private readonly EventAdapterService _eventAdapterService; + + public EvalInsertCoercionObjectArray(EventType resultEventType, EventAdapterService eventAdapterService) { + _resultEventType = resultEventType; + _eventAdapterService = eventAdapterService; + } + + public EventBean Process(EventBean[] eventsPerStream, bool isNewData, bool isSynthesize, ExprEvaluatorContext exprEvaluatorContext) { + ObjectArrayBackedEventBean theEvent = (ObjectArrayBackedEventBean) eventsPerStream[0]; + return _eventAdapterService.AdapterForTypedObjectArray(theEvent.Properties, _resultEventType); + } + + public EventType ResultEventType + { + get { return _resultEventType; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertNoWildcardMap.cs b/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertNoWildcardMap.cs new file mode 100755 index 000000000..3d996d042 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertNoWildcardMap.cs @@ -0,0 +1,30 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.core.eval +{ + public class EvalInsertNoWildcardMap : EvalBaseMap, SelectExprProcessor + { + public EvalInsertNoWildcardMap(SelectExprContext selectExprContext, EventType resultEventType) + : base(selectExprContext, resultEventType) + { + } + + public override EventBean ProcessSpecific(IDictionary props, EventBean[] eventsPerStream, bool isNewData, bool isSynthesize, ExprEvaluatorContext exprEvaluatorContext) + { + return base.EventAdapterService.AdapterForTypedMap(props, base.ResultEventType); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertNoWildcardObjectArray.cs b/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertNoWildcardObjectArray.cs new file mode 100755 index 000000000..fbb206cb7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertNoWildcardObjectArray.cs @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.core.eval +{ + public class EvalInsertNoWildcardObjectArray + : EvalBase, + SelectExprProcessor + { + public EvalInsertNoWildcardObjectArray(SelectExprContext selectExprContext, EventType resultEventType) + : base(selectExprContext, resultEventType) + { + } + + public EventBean Process( + EventBean[] eventsPerStream, + bool isNewData, + bool isSynthesize, + ExprEvaluatorContext exprEvaluatorContext) + { + var evaluateParams = new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext); + var result = new Object[base.ExprNodes.Length]; + for (int i = 0; i < base.ExprNodes.Length; i++) + { + result[i] = base.ExprNodes[i].Evaluate(evaluateParams); + } + + return base.EventAdapterService.AdapterForTypedObjectArray(result, base.ResultEventType); + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertNoWildcardObjectArrayRemap.cs b/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertNoWildcardObjectArrayRemap.cs new file mode 100755 index 000000000..0a3446659 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertNoWildcardObjectArrayRemap.cs @@ -0,0 +1,61 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.core.eval +{ + public class EvalInsertNoWildcardObjectArrayRemap : SelectExprProcessor + { + private readonly SelectExprContext _selectExprContext; + private readonly EventType _resultEventType; + private readonly int[] _remapped; + + public EvalInsertNoWildcardObjectArrayRemap(SelectExprContext selectExprContext, EventType resultEventType, int[] remapped) + { + _selectExprContext = selectExprContext; + _resultEventType = resultEventType; + _remapped = remapped; + } + + public EventType ResultEventType + { + get { return _resultEventType; } + } + + public int[] Remapped + { + get { return _remapped; } + } + + public SelectExprContext SelectExprContext + { + get { return _selectExprContext; } + } + + public virtual EventBean Process(EventBean[] eventsPerStream, bool isNewData, bool isSynthesize, ExprEvaluatorContext exprEvaluatorContext) + { + var expressionNodes = _selectExprContext.ExpressionNodes; + + var result = new object[_resultEventType.PropertyNames.Length]; + var evaluateParams = new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext); + for (int i = 0; i < expressionNodes.Length; i++) + { + result[_remapped[i]] = expressionNodes[i].Evaluate(evaluateParams); + } + + return _selectExprContext.EventAdapterService.AdapterForTypedObjectArray(result, _resultEventType); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertNoWildcardObjectArrayRemapWWiden.cs b/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertNoWildcardObjectArrayRemapWWiden.cs new file mode 100755 index 000000000..e5d112e04 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertNoWildcardObjectArrayRemapWWiden.cs @@ -0,0 +1,44 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.core.eval +{ + public class EvalInsertNoWildcardObjectArrayRemapWWiden : EvalInsertNoWildcardObjectArrayRemap + { + private readonly TypeWidener[] _wideners; + + public EvalInsertNoWildcardObjectArrayRemapWWiden(SelectExprContext selectExprContext, EventType resultEventType, int[] remapped, TypeWidener[] wideners) + : base(selectExprContext, resultEventType, remapped) + { + this._wideners = wideners; + } + + public override EventBean Process(EventBean[] eventsPerStream, bool isNewData, bool isSynthesize, ExprEvaluatorContext exprEvaluatorContext) + { + ExprEvaluator[] expressionNodes = SelectExprContext.ExpressionNodes; + + var evaluateParams = new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext); + var result = new object[ResultEventType.PropertyNames.Length]; + for (var i = 0; i < expressionNodes.Length; i++) { + var value = expressionNodes[i].Evaluate(evaluateParams); + if (_wideners[i] != null) { + value = _wideners[i].Invoke(value); + } + result[Remapped[i]] = value; + } + + return SelectExprContext.EventAdapterService.AdapterForTypedObjectArray(result, ResultEventType); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertNoWildcardRevision.cs b/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertNoWildcardRevision.cs new file mode 100755 index 000000000..ee420f8ae --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertNoWildcardRevision.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events.vaevent; + +namespace com.espertech.esper.epl.core.eval +{ + public class EvalInsertNoWildcardRevision + : EvalBaseMap + , SelectExprProcessor + { + private readonly ValueAddEventProcessor _vaeProcessor; + private readonly EventType _vaeInnerEventType; + + public EvalInsertNoWildcardRevision(SelectExprContext selectExprContext, EventType resultEventType, ValueAddEventProcessor vaeProcessor, EventType vaeInnerEventType) + : base(selectExprContext, resultEventType) + { + _vaeProcessor = vaeProcessor; + _vaeInnerEventType = vaeInnerEventType; + } + + public override EventBean ProcessSpecific(IDictionary props, EventBean[] eventsPerStream, bool isNewData, bool isSynthesize, ExprEvaluatorContext exprEvaluatorContext) + { + return _vaeProcessor.GetValueAddEventBean(base.EventAdapterService.AdapterForTypedMap(props, _vaeInnerEventType)); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertNoWildcardSingleColCoercionAvroWrap.cs b/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertNoWildcardSingleColCoercionAvroWrap.cs new file mode 100755 index 000000000..89a07d494 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertNoWildcardSingleColCoercionAvroWrap.cs @@ -0,0 +1,30 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.core.eval +{ + public class EvalInsertNoWildcardSingleColCoercionAvroWrap : EvalBaseFirstPropFromWrap, SelectExprProcessor + { + public EvalInsertNoWildcardSingleColCoercionAvroWrap(SelectExprContext selectExprContext, WrapperEventType wrapper) + : base(selectExprContext, wrapper) + { + } + + public override EventBean ProcessFirstCol(Object result) + { + EventBean wrappedEvent = base.EventAdapterService.AdapterForTypedAvro(result, Wrapper.UnderlyingEventType); + return base.EventAdapterService.AdapterForTypedWrapper(wrappedEvent, Collections.EmptyDataMap, Wrapper); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertNoWildcardSingleColCoercionBean.cs b/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertNoWildcardSingleColCoercionBean.cs new file mode 100755 index 000000000..1f270db23 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertNoWildcardSingleColCoercionBean.cs @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.compat.logging; + +namespace com.espertech.esper.epl.core.eval +{ + public class EvalInsertNoWildcardSingleColCoercionBean + : EvalBaseFirstProp + , SelectExprProcessor + { + private static readonly ILog Log = + LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public EvalInsertNoWildcardSingleColCoercionBean(SelectExprContext selectExprContext, EventType resultEventType) + : base(selectExprContext, resultEventType) + { + } + + public override EventBean ProcessFirstCol(Object result) + { + return base.EventAdapterService.AdapterForTypedObject(result, base.ResultEventType); + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertNoWildcardSingleColCoercionBeanWrap.cs b/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertNoWildcardSingleColCoercionBeanWrap.cs new file mode 100755 index 000000000..1e1b00dc8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertNoWildcardSingleColCoercionBeanWrap.cs @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.core.eval +{ + public class EvalInsertNoWildcardSingleColCoercionBeanWrap : EvalBaseFirstPropFromWrap, SelectExprProcessor + { + public EvalInsertNoWildcardSingleColCoercionBeanWrap(SelectExprContext selectExprContext, WrapperEventType wrapper) + : base(selectExprContext, wrapper) + { + } + + public override EventBean ProcessFirstCol(Object result) { + EventBean wrappedEvent = base.EventAdapterService.AdapterForTypedObject(result, Wrapper.UnderlyingEventType); + return base.EventAdapterService.AdapterForTypedWrapper(wrappedEvent, Collections.EmptyDataMap, Wrapper); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertNoWildcardSingleColCoercionBeanWrapVariant.cs b/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertNoWildcardSingleColCoercionBeanWrapVariant.cs new file mode 100755 index 000000000..a4fea4c44 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertNoWildcardSingleColCoercionBeanWrapVariant.cs @@ -0,0 +1,44 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.events.vaevent; + +namespace com.espertech.esper.epl.core.eval +{ + public class EvalInsertNoWildcardSingleColCoercionBeanWrapVariant + : EvalBaseFirstProp + , SelectExprProcessor + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly ValueAddEventProcessor _vaeProcessor; + + public EvalInsertNoWildcardSingleColCoercionBeanWrapVariant( + SelectExprContext selectExprContext, + EventType resultEventType, + ValueAddEventProcessor vaeProcessor) + : base(selectExprContext, resultEventType) + { + _vaeProcessor = vaeProcessor; + } + + public override EventBean ProcessFirstCol(Object result) + { + EventBean wrappedEvent = base.EventAdapterService.AdapterForObject(result); + EventBean variant = _vaeProcessor.GetValueAddEventBean(wrappedEvent); + return base.EventAdapterService.AdapterForTypedWrapper( + variant, Collections.EmptyDataMap, base.ResultEventType); + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertNoWildcardSingleColCoercionMapWrap.cs b/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertNoWildcardSingleColCoercionMapWrap.cs new file mode 100755 index 000000000..3127384a2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertNoWildcardSingleColCoercionMapWrap.cs @@ -0,0 +1,36 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.core.eval +{ + public class EvalInsertNoWildcardSingleColCoercionMapWrap + : EvalBaseFirstPropFromWrap + , SelectExprProcessor + { + public EvalInsertNoWildcardSingleColCoercionMapWrap( + SelectExprContext selectExprContext, + WrapperEventType wrapper) + : base(selectExprContext, wrapper) + { + } + + public override EventBean ProcessFirstCol(Object result) + { + EventBean wrappedEvent = base.EventAdapterService.AdapterForTypedMap( + (IDictionary) result, Wrapper.UnderlyingEventType); + return base.EventAdapterService.AdapterForTypedWrapper(wrappedEvent, Collections.EmptyDataMap, Wrapper); + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertNoWildcardSingleColCoercionObjectArrayWrap.cs b/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertNoWildcardSingleColCoercionObjectArrayWrap.cs new file mode 100755 index 000000000..833ebbdf8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertNoWildcardSingleColCoercionObjectArrayWrap.cs @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.core.eval +{ + public class EvalInsertNoWildcardSingleColCoercionObjectArrayWrap + : EvalBaseFirstPropFromWrap + , SelectExprProcessor + { + public EvalInsertNoWildcardSingleColCoercionObjectArrayWrap(SelectExprContext selectExprContext, WrapperEventType wrapper) + : base(selectExprContext, wrapper) + { + } + + public override EventBean ProcessFirstCol(Object result) + { + EventBean wrappedEvent = base.EventAdapterService.AdapterForTypedObjectArray((Object[]) result, Wrapper.UnderlyingEventType); + return base.EventAdapterService.AdapterForTypedWrapper(wrappedEvent, Collections.EmptyDataMap, Wrapper); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertNoWildcardSingleColCoercionRevisionBean.cs b/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertNoWildcardSingleColCoercionRevisionBean.cs new file mode 100755 index 000000000..e1a28323e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertNoWildcardSingleColCoercionRevisionBean.cs @@ -0,0 +1,36 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat.logging; +using com.espertech.esper.events.vaevent; + +namespace com.espertech.esper.epl.core.eval +{ + public class EvalInsertNoWildcardSingleColCoercionRevisionBean : EvalBaseFirstProp, SelectExprProcessor + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly ValueAddEventProcessor _vaeProcessor; + private readonly EventType _vaeInnerEventType; + + public EvalInsertNoWildcardSingleColCoercionRevisionBean(SelectExprContext selectExprContext, EventType resultEventType, ValueAddEventProcessor vaeProcessor, EventType vaeInnerEventType) + : base(selectExprContext, resultEventType) + { + _vaeProcessor = vaeProcessor; + _vaeInnerEventType = vaeInnerEventType; + } + + public override EventBean ProcessFirstCol(Object result) + { + return _vaeProcessor.GetValueAddEventBean(base.EventAdapterService.AdapterForTypedObject(result, _vaeInnerEventType)); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertNoWildcardSingleColCoercionRevisionFunc.cs b/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertNoWildcardSingleColCoercionRevisionFunc.cs new file mode 100755 index 000000000..051fba142 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertNoWildcardSingleColCoercionRevisionFunc.cs @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.events; +using com.espertech.esper.events.vaevent; + +namespace com.espertech.esper.epl.core.eval +{ + public class EvalInsertNoWildcardSingleColCoercionRevisionFunc + : EvalBaseFirstProp + , SelectExprProcessor + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly ValueAddEventProcessor _vaeProcessor; + private readonly EventType _vaeInnerEventType; + private readonly Func _func; + + public EvalInsertNoWildcardSingleColCoercionRevisionFunc( + SelectExprContext selectExprContext, + EventType resultEventType, + ValueAddEventProcessor vaeProcessor, + EventType vaeInnerEventType, + Func func) + : base(selectExprContext, resultEventType) + { + _vaeProcessor = vaeProcessor; + _vaeInnerEventType = vaeInnerEventType; + _func = func; + } + + public override EventBean ProcessFirstCol(Object result) + { + EventBean wrappedEvent = _func.Invoke(base.EventAdapterService, result, base.ResultEventType); + return _vaeProcessor.GetValueAddEventBean(base.EventAdapterService.AdapterForTypedWrapper(wrappedEvent, Collections.EmptyDataMap, _vaeInnerEventType)); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertUtil.cs b/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertUtil.cs new file mode 100755 index 000000000..02ba61a31 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertUtil.cs @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.core.eval +{ + public class EvalInsertUtil + { + public static ExprValidationException MakeEventTypeCastException(EventType sourceType, EventType targetType) { + return new ExprValidationException("Expression-returned event type '" + sourceType.Name + + "' with underlying type '" + sourceType.UnderlyingType.FullName + + "' cannot be converted to target event type '" + targetType.Name + + "' with underlying type '" + targetType.UnderlyingType.FullName + "'"); + } + + public static ExprValidationException MakeEventTypeCastException(Type sourceType, EventType targetType) { + return new ExprValidationException("Expression-returned value of type '" + sourceType.FullName + + "' cannot be converted to target event type '" + targetType.Name + + "' with underlying type '" + targetType.UnderlyingType.FullName + "'"); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertWildcardBean.cs b/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertWildcardBean.cs new file mode 100755 index 000000000..1346b5475 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertWildcardBean.cs @@ -0,0 +1,30 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.core.eval +{ + public class EvalInsertWildcardBean + : EvalBase + , SelectExprProcessor + { + public EvalInsertWildcardBean(SelectExprContext selectExprContext, EventType resultEventType) + : base(selectExprContext, resultEventType) + { + } + + public EventBean Process(EventBean[] eventsPerStream, bool isNewData, bool isSynthesize, ExprEvaluatorContext exprEvaluatorContext) + { + EventBean theEvent = eventsPerStream[0]; + return EventAdapterService.AdapterForTypedObject(theEvent.Underlying, ResultEventType); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertWildcardJoin.cs b/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertWildcardJoin.cs new file mode 100755 index 000000000..c8afc844e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertWildcardJoin.cs @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.core.eval +{ + public class EvalInsertWildcardJoin + : EvalBaseMap + , SelectExprProcessor + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly SelectExprProcessor _joinWildcardProcessor; + + public EvalInsertWildcardJoin( + SelectExprContext selectExprContext, + EventType resultEventType, + SelectExprProcessor joinWildcardProcessor) + : base(selectExprContext, resultEventType) + { + _joinWildcardProcessor = joinWildcardProcessor; + } + + public override EventBean ProcessSpecific( + IDictionary props, + EventBean[] eventsPerStream, + bool isNewData, + bool isSynthesize, + ExprEvaluatorContext exprEvaluatorContext) + { + EventBean theEvent = _joinWildcardProcessor.Process( + eventsPerStream, isNewData, isSynthesize, exprEvaluatorContext); + // Using a wrapper bean since we cannot use the same event type else same-type filters match. + // Wrapping it even when not adding properties is very inexpensive. + return base.EventAdapterService.AdapterForTypedWrapper(theEvent, props, base.ResultEventType); + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertWildcardJoinRevision.cs b/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertWildcardJoinRevision.cs new file mode 100755 index 000000000..5d679a575 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertWildcardJoinRevision.cs @@ -0,0 +1,39 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events.vaevent; + +namespace com.espertech.esper.epl.core.eval +{ + public class EvalInsertWildcardJoinRevision + : EvalBase + , SelectExprProcessor + { + private readonly SelectExprProcessor _joinWildcardProcessor; + private readonly ValueAddEventProcessor _vaeProcessor; + + public EvalInsertWildcardJoinRevision(SelectExprContext selectExprContext, + EventType resultEventType, + SelectExprProcessor joinWildcardProcessor, + ValueAddEventProcessor vaeProcessor) + : base(selectExprContext, resultEventType) + { + _joinWildcardProcessor = joinWildcardProcessor; + _vaeProcessor = vaeProcessor; + } + + public EventBean Process(EventBean[] eventsPerStream, bool isNewData, bool isSynthesize, ExprEvaluatorContext exprEvaluatorContext) + { + EventBean theEvent = _joinWildcardProcessor.Process(eventsPerStream, isNewData, isSynthesize, exprEvaluatorContext); + return _vaeProcessor.GetValueAddEventBean(theEvent); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertWildcardRevision.cs b/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertWildcardRevision.cs new file mode 100755 index 000000000..d081d5e02 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertWildcardRevision.cs @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events.vaevent; + +namespace com.espertech.esper.epl.core.eval +{ + public class EvalInsertWildcardRevision + : EvalBase + , SelectExprProcessor + { + private readonly ValueAddEventProcessor _vaeProcessor; + + public EvalInsertWildcardRevision(SelectExprContext selectExprContext, EventType resultEventType, ValueAddEventProcessor vaeProcessor) + : base(selectExprContext, resultEventType) + { + _vaeProcessor = vaeProcessor; + } + + public EventBean Process(EventBean[] eventsPerStream, bool isNewData, bool isSynthesize, ExprEvaluatorContext exprEvaluatorContext) + { + EventBean theEvent = eventsPerStream[0]; + return _vaeProcessor.GetValueAddEventBean(theEvent); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertWildcardRevisionWrapper.cs b/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertWildcardRevisionWrapper.cs new file mode 100755 index 000000000..f5dfcc43e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertWildcardRevisionWrapper.cs @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events.vaevent; + +namespace com.espertech.esper.epl.core.eval +{ + public class EvalInsertWildcardRevisionWrapper : EvalBaseMap, SelectExprProcessor + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly ValueAddEventProcessor _vaeProcessor; + private readonly EventType _wrappingEventType; + + public EvalInsertWildcardRevisionWrapper(SelectExprContext selectExprContext, EventType resultEventType, ValueAddEventProcessor vaeProcessor, EventType wrappingEventType) + : base(selectExprContext, resultEventType) + { + _vaeProcessor = vaeProcessor; + _wrappingEventType = wrappingEventType; + } + + public override EventBean ProcessSpecific( + IDictionary props, + EventBean[] eventsPerStream, + bool isNewData, + bool isSynthesize, + ExprEvaluatorContext exprEvaluatorContext) + { + EventBean underlying = eventsPerStream[0]; + EventBean wrapped = base.EventAdapterService.AdapterForTypedWrapper(underlying, props, _wrappingEventType); + return _vaeProcessor.GetValueAddEventBean(wrapped); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertWildcardSSWrapper.cs b/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertWildcardSSWrapper.cs new file mode 100755 index 000000000..39e5cdf3c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertWildcardSSWrapper.cs @@ -0,0 +1,59 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.core.eval +{ + public class EvalInsertWildcardSSWrapper : EvalBaseMap, SelectExprProcessor + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + public EvalInsertWildcardSSWrapper(SelectExprContext selectExprContext, EventType resultEventType) + : base(selectExprContext, resultEventType) + { + } + + // In case of a wildcard and single stream that is itself a + // wrapper bean, we also need to add the map properties + public override EventBean ProcessSpecific( + IDictionary props, + EventBean[] eventsPerStream, + bool isNewData, + bool isSynthesize, + ExprEvaluatorContext exprEvaluatorContext) + { + var wrapper = (DecoratingEventBean) eventsPerStream[0]; + if (wrapper != null) + { + IDictionary map = wrapper.DecoratingProperties; + if ((base.ExprNodes.Length == 0) && (!map.IsEmpty())) + { + props = new Dictionary(map); + } + else + { + props.PutAll(map); + } + } + + EventBean theEvent = eventsPerStream[0]; + + // Using a wrapper bean since we cannot use the same event type else same-type filters match. + // Wrapping it even when not adding properties is very inexpensive. + return base.EventAdapterService.AdapterForTypedWrapper(theEvent, props, base.ResultEventType); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertWildcardSSWrapperRevision.cs b/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertWildcardSSWrapperRevision.cs new file mode 100755 index 000000000..25e72704f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertWildcardSSWrapperRevision.cs @@ -0,0 +1,60 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events; +using com.espertech.esper.events.vaevent; + +namespace com.espertech.esper.epl.core.eval +{ + public class EvalInsertWildcardSSWrapperRevision : EvalBaseMap, SelectExprProcessor { + + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly ValueAddEventProcessor _vaeProcessor; + + public EvalInsertWildcardSSWrapperRevision(SelectExprContext selectExprContext, EventType resultEventType, ValueAddEventProcessor vaeProcessor) + : base(selectExprContext, resultEventType) + { + _vaeProcessor = vaeProcessor; + } + + // In case of a wildcard and single stream that is itself a + // wrapper bean, we also need to add the map properties + public override EventBean ProcessSpecific( + IDictionary props, + EventBean[] eventsPerStream, + bool isNewData, + bool isSynthesize, + ExprEvaluatorContext exprEvaluatorContext) + { + var wrapper = (DecoratingEventBean) eventsPerStream[0]; + if (wrapper != null) + { + IDictionary map = wrapper.DecoratingProperties; + if ((base.ExprNodes.Length == 0) && (!map.IsEmpty())) + { + // no action + } + else + { + props.PutAll(map); + } + } + + EventBean theEvent = eventsPerStream[0]; + return _vaeProcessor.GetValueAddEventBean(theEvent); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertWildcardWrapper.cs b/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertWildcardWrapper.cs new file mode 100755 index 000000000..91c17787b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/eval/EvalInsertWildcardWrapper.cs @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.core.eval +{ + public class EvalInsertWildcardWrapper : EvalBaseMap, SelectExprProcessor + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + public EvalInsertWildcardWrapper(SelectExprContext selectExprContext, EventType resultEventType) + : base(selectExprContext, resultEventType) + { + } + + public override EventBean ProcessSpecific( + IDictionary props, + EventBean[] eventsPerStream, + bool isNewData, + bool isSynthesize, + ExprEvaluatorContext exprEvaluatorContext) + { + EventBean theEvent = eventsPerStream[0]; + // Using a wrapper bean since we cannot use the same event type else same-type filters match. + // Wrapping it even when not adding properties is very inexpensive. + return base.EventAdapterService.AdapterForTypedWrapper(theEvent, props, base.ResultEventType); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/eval/EvalSelectNoWildcardEmptyProps.cs b/NEsper.Core/NEsper.Core/epl/core/eval/EvalSelectNoWildcardEmptyProps.cs new file mode 100755 index 000000000..f19eedaa2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/eval/EvalSelectNoWildcardEmptyProps.cs @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.core.eval +{ + public class EvalSelectNoWildcardEmptyProps : SelectExprProcessor + { + private readonly SelectExprContext _selectExprContext; + private readonly EventType _resultEventType; + + public EvalSelectNoWildcardEmptyProps(SelectExprContext selectExprContext, EventType resultEventType) + { + this._selectExprContext = selectExprContext; + this._resultEventType = resultEventType; + } + + public EventBean Process(EventBean[] eventsPerStream, bool isNewData, bool isSynthesize, ExprEvaluatorContext exprEvaluatorContext) + { + return _selectExprContext.EventAdapterService.AdapterForTypedMap(new Dictionary(), _resultEventType); + } + + public EventType ResultEventType + { + get { return _resultEventType; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/eval/EvalSelectNoWildcardMap.cs b/NEsper.Core/NEsper.Core/epl/core/eval/EvalSelectNoWildcardMap.cs new file mode 100755 index 000000000..be4e7d5ce --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/eval/EvalSelectNoWildcardMap.cs @@ -0,0 +1,68 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.core.eval +{ + public class EvalSelectNoWildcardMap : SelectExprProcessor + { + private readonly EventType _resultEventType; + private readonly SelectExprContext _selectExprContext; + + private static int seq = 0; + private readonly int id = seq++; + + public EvalSelectNoWildcardMap(SelectExprContext selectExprContext, EventType resultEventType) + { + _selectExprContext = selectExprContext; + _resultEventType = resultEventType; + } + + #region SelectExprProcessor Members + + public EventBean Process(EventBean[] eventsPerStream, + bool isNewData, + bool isSynthesize, + ExprEvaluatorContext exprEvaluatorContext) + { + var expressionNodes = _selectExprContext.ExpressionNodes; + var columnNames = _selectExprContext.ColumnNames; + var eventAdapterService = _selectExprContext.EventAdapterService; + + var evaluateParams = new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext); + + // Evaluate all expressions and build a map of name-value pairs + var props = new Dictionary(); + + unchecked + { + for (int ii = 0; ii < expressionNodes.Length; ii++) + { + var evalResult = expressionNodes[ii].Evaluate(evaluateParams); + props.Put(columnNames[ii], evalResult); + } + } + + return eventAdapterService.AdapterForTypedMap(props, _resultEventType); + } + + public EventType ResultEventType + { + get { return _resultEventType; } + } + + #endregion + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/core/eval/EvalSelectNoWildcardObjectArray.cs b/NEsper.Core/NEsper.Core/epl/core/eval/EvalSelectNoWildcardObjectArray.cs new file mode 100755 index 000000000..1f35ec498 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/eval/EvalSelectNoWildcardObjectArray.cs @@ -0,0 +1,64 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.core.eval +{ + public class EvalSelectNoWildcardObjectArray + : SelectExprProcessor + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly EventType _resultEventType; + private readonly SelectExprContext _selectExprContext; + + public EvalSelectNoWildcardObjectArray(SelectExprContext selectExprContext, EventType resultEventType) + { + _selectExprContext = selectExprContext; + _resultEventType = resultEventType; + } + + #region SelectExprProcessor Members + + public EventBean Process(EventBean[] eventsPerStream, + bool isNewData, + bool isSynthesize, + ExprEvaluatorContext exprEvaluatorContext) + { + ExprEvaluator[] expressionNodes = _selectExprContext.ExpressionNodes; + EventAdapterService eventAdapterService = _selectExprContext.EventAdapterService; + + // Evaluate all expressions and build a map of name-value pairs + var props = new Object[expressionNodes.Length]; + var evaluateParams = new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext); + + for (int i = 0; i < expressionNodes.Length; i++) + { + Object evalResult = expressionNodes[i].Evaluate(evaluateParams); + props[i] = evalResult; + } + + return eventAdapterService.AdapterForTypedObjectArray(props, _resultEventType); + } + + public EventType ResultEventType + { + get { return _resultEventType; } + } + + #endregion + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/core/eval/EvalSelectStreamBase.cs b/NEsper.Core/NEsper.Core/epl/core/eval/EvalSelectStreamBase.cs new file mode 100755 index 000000000..879093acc --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/eval/EvalSelectStreamBase.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.epl.core.eval +{ + public abstract class EvalSelectStreamBase : SelectExprProcessor + { + protected readonly IList NamedStreams; + protected readonly bool IsUsingWildcard; + + protected EvalSelectStreamBase(SelectExprContext selectExprContext, EventType resultEventType, IList namedStreams, bool usingWildcard) + { + SelectExprContext = selectExprContext; + ResultEventType = resultEventType; + NamedStreams = namedStreams; + IsUsingWildcard = usingWildcard; + } + + public EventType ResultEventType { get; protected set; } + + public SelectExprContext SelectExprContext { get; protected set; } + + public abstract EventBean Process(EventBean[] eventsPerStream, bool isNewData, bool isSynthesize, ExprEvaluatorContext exprEvaluatorContext); + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/core/eval/EvalSelectStreamBaseMap.cs b/NEsper.Core/NEsper.Core/epl/core/eval/EvalSelectStreamBaseMap.cs new file mode 100755 index 000000000..d3d24fdb3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/eval/EvalSelectStreamBaseMap.cs @@ -0,0 +1,68 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.epl.core.eval +{ + public abstract class EvalSelectStreamBaseMap + : EvalSelectStreamBase + , SelectExprProcessor + { + protected EvalSelectStreamBaseMap(SelectExprContext selectExprContext, EventType resultEventType, IList namedStreams, bool usingWildcard) + : base(selectExprContext, resultEventType, namedStreams, usingWildcard) + { + } + + public abstract EventBean ProcessSpecific(IDictionary props, EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext exprEvaluatorContext); + + public override EventBean Process(EventBean[] eventsPerStream, bool isNewData, bool isSynthesize, ExprEvaluatorContext exprEvaluatorContext) + { + // Evaluate all expressions and build a map of name-value pairs + IDictionary props = new Dictionary(); + int count = 0; + var evaluateParams = new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext); + foreach (ExprEvaluator expressionNode in SelectExprContext.ExpressionNodes) + { + Object evalResult = expressionNode.Evaluate(evaluateParams); + props.Put(SelectExprContext.ColumnNames[count], evalResult); + count++; + } + foreach (SelectClauseStreamCompiledSpec element in NamedStreams) + { + EventBean theEvent = eventsPerStream[element.StreamNumber]; + if (element.TableMetadata != null) + { + if (theEvent != null) + { + theEvent = element.TableMetadata.EventToPublic.Convert(theEvent, evaluateParams); + } + } + props.Put(SelectExprContext.ColumnNames[count], theEvent); + count++; + } + if (IsUsingWildcard && eventsPerStream.Length > 1) + { + foreach (EventBean anEventsPerStream in eventsPerStream) + { + props.Put(SelectExprContext.ColumnNames[count], anEventsPerStream); + count++; + } + } + + return ProcessSpecific(props, eventsPerStream, isNewData, exprEvaluatorContext); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/eval/EvalSelectStreamBaseObjectArray.cs b/NEsper.Core/NEsper.Core/epl/core/eval/EvalSelectStreamBaseObjectArray.cs new file mode 100755 index 000000000..0f1989099 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/eval/EvalSelectStreamBaseObjectArray.cs @@ -0,0 +1,72 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.epl.core.eval +{ + public abstract class EvalSelectStreamBaseObjectArray : EvalSelectStreamBase, SelectExprProcessor + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + public EvalSelectStreamBaseObjectArray( + SelectExprContext selectExprContext, + EventType resultEventType, + IList namedStreams, + bool usingWildcard) + : base(selectExprContext, resultEventType, namedStreams, usingWildcard) + { + } + + public abstract EventBean ProcessSpecific(Object[] props, EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext); + + public override EventBean Process( + EventBean[] eventsPerStream, + bool isNewData, + bool isSynthesize, + ExprEvaluatorContext exprEvaluatorContext) + { + // Evaluate all expressions and build a map of name-value pairs + int size = (base.IsUsingWildcard && eventsPerStream.Length > 1) ? eventsPerStream.Length : 0; + size += base.SelectExprContext.ExpressionNodes.Length + base.NamedStreams.Count; + var props = new Object[size]; + int count = 0; + foreach (ExprEvaluator expressionNode in base.SelectExprContext.ExpressionNodes) + { + var evalResult = expressionNode.Evaluate(new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext)); + props[count] = evalResult; + count++; + } + foreach (SelectClauseStreamCompiledSpec element in base.NamedStreams) + { + EventBean theEvent = eventsPerStream[element.StreamNumber]; + props[count] = theEvent; + count++; + } + if (base.IsUsingWildcard && eventsPerStream.Length > 1) + { + foreach (EventBean anEventsPerStream in eventsPerStream) + { + props[count] = anEventsPerStream; + count++; + } + } + + return ProcessSpecific(props, eventsPerStream, exprEvaluatorContext); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/eval/EvalSelectStreamNoUndWEventBeanToObj.cs b/NEsper.Core/NEsper.Core/epl/core/eval/EvalSelectStreamNoUndWEventBeanToObj.cs new file mode 100755 index 000000000..e910de0ae --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/eval/EvalSelectStreamNoUndWEventBeanToObj.cs @@ -0,0 +1,57 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.epl.core.eval +{ + public class EvalSelectStreamNoUndWEventBeanToObj + : EvalSelectStreamBaseMap + , SelectExprProcessor + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly ISet _eventBeanToObjectProps; + + public EvalSelectStreamNoUndWEventBeanToObj( + SelectExprContext selectExprContext, + EventType resultEventType, + IList namedStreams, + bool usingWildcard, + ISet eventBeanToObjectProps) + : base(selectExprContext, resultEventType, namedStreams, usingWildcard) + { + _eventBeanToObjectProps = eventBeanToObjectProps; + } + + public override EventBean ProcessSpecific( + IDictionary props, + EventBean[] eventsPerStream, + bool isNewData, + ExprEvaluatorContext exprEvaluatorContext) + { + foreach (string property in _eventBeanToObjectProps) + { + Object value = props.Get(property); + if (value is EventBean) + { + props.Put(property, ((EventBean) value).Underlying); + } + } + return base.SelectExprContext.EventAdapterService.AdapterForTypedMap(props, base.ResultEventType); + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/core/eval/EvalSelectStreamNoUndWEventBeanToObjObjArray.cs b/NEsper.Core/NEsper.Core/epl/core/eval/EvalSelectStreamNoUndWEventBeanToObjObjArray.cs new file mode 100755 index 000000000..09dd60a08 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/eval/EvalSelectStreamNoUndWEventBeanToObjObjArray.cs @@ -0,0 +1,63 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.epl.core.eval +{ + public class EvalSelectStreamNoUndWEventBeanToObjObjArray : EvalSelectStreamBaseObjectArray, SelectExprProcessor + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly ISet _eventBeanToObjectIndexes; + + public EvalSelectStreamNoUndWEventBeanToObjObjArray( + SelectExprContext selectExprContext, + EventType resultEventType, + IList namedStreams, + bool usingWildcard, + IEnumerable eventBeanToObjectProps) + : base(selectExprContext, resultEventType, namedStreams, usingWildcard) + { + _eventBeanToObjectIndexes = new HashSet(); + var type = (ObjectArrayEventType) resultEventType; + foreach (var name in eventBeanToObjectProps) { + int index; + if (type.PropertiesIndexes.TryGetValue(name, out index)) { + _eventBeanToObjectIndexes.Add(index); + } + } + } + + public override EventBean ProcessSpecific( + Object[] props, + EventBean[] eventsPerStream, + ExprEvaluatorContext exprEvaluatorContext) + { + foreach (var propertyIndex in _eventBeanToObjectIndexes) + { + var value = props[propertyIndex]; + if (value is EventBean) + { + props[propertyIndex] = ((EventBean) value).Underlying; + } + } + return base.SelectExprContext.EventAdapterService.AdapterForTypedObjectArray(props, base.ResultEventType); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/eval/EvalSelectStreamNoUnderlyingMap.cs b/NEsper.Core/NEsper.Core/epl/core/eval/EvalSelectStreamNoUnderlyingMap.cs new file mode 100755 index 000000000..71a4f4de6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/eval/EvalSelectStreamNoUnderlyingMap.cs @@ -0,0 +1,44 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.epl.core.eval +{ + public class EvalSelectStreamNoUnderlyingMap : EvalSelectStreamBaseMap, SelectExprProcessor { + + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + public EvalSelectStreamNoUnderlyingMap( + SelectExprContext selectExprContext, + EventType resultEventType, + List namedStreams, + bool usingWildcard) + : base(selectExprContext, resultEventType, namedStreams, usingWildcard) + { + } + + public override EventBean ProcessSpecific( + IDictionary props, + EventBean[] eventsPerStream, + bool isNewData, + ExprEvaluatorContext exprEvaluatorContext) + { + return base.SelectExprContext.EventAdapterService.AdapterForTypedMap(props, base.ResultEventType); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/eval/EvalSelectStreamNoUnderlyingObjectArray.cs b/NEsper.Core/NEsper.Core/epl/core/eval/EvalSelectStreamNoUnderlyingObjectArray.cs new file mode 100755 index 000000000..1fd97a6dc --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/eval/EvalSelectStreamNoUnderlyingObjectArray.cs @@ -0,0 +1,63 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.epl.core.eval +{ + public class EvalSelectStreamNoUnderlyingObjectArray + : EvalSelectStreamBase + , SelectExprProcessor + { + public EvalSelectStreamNoUnderlyingObjectArray(SelectExprContext selectExprContext, EventType resultEventType, IList namedStreams, bool usingWildcard) + : base(selectExprContext, resultEventType, namedStreams, usingWildcard) + { + } + + public EventBean ProcessSpecific(Object[] props, EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext) { + return SelectExprContext.EventAdapterService.AdapterForTypedObjectArray(props, base.ResultEventType); + } + + public override EventBean Process(EventBean[] eventsPerStream, bool isNewData, bool isSynthesize, ExprEvaluatorContext exprEvaluatorContext) + { + // Evaluate all expressions and build a map of name-value pairs + int size = (IsUsingWildcard && eventsPerStream.Length > 1) ? eventsPerStream.Length : 0; + size += SelectExprContext.ExpressionNodes.Length + NamedStreams.Count; + Object[] props = new Object[size]; + int count = 0; + foreach (ExprEvaluator expressionNode in SelectExprContext.ExpressionNodes) + { + Object evalResult = expressionNode.Evaluate(new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext)); + props[count] = evalResult; + count++; + } + foreach (SelectClauseStreamCompiledSpec element in NamedStreams) + { + EventBean theEvent = eventsPerStream[element.StreamNumber]; + props[count] = theEvent; + count++; + } + if (IsUsingWildcard && eventsPerStream.Length > 1) + { + foreach (EventBean anEventsPerStream in eventsPerStream) + { + props[count] = anEventsPerStream; + count++; + } + } + + return ProcessSpecific(props, eventsPerStream, exprEvaluatorContext); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/eval/EvalSelectStreamWUndRecastMapFactory.cs b/NEsper.Core/NEsper.Core/epl/core/eval/EvalSelectStreamWUndRecastMapFactory.cs new file mode 100755 index 000000000..900e7ffc6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/eval/EvalSelectStreamWUndRecastMapFactory.cs @@ -0,0 +1,205 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events; +using com.espertech.esper.events.map; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.core.eval +{ + public class EvalSelectStreamWUndRecastMapFactory + { + public static SelectExprProcessor Make( + EventType[] eventTypes, + SelectExprContext selectExprContext, + int streamNumber, + EventType targetType, + ExprNode[] exprNodes, + EngineImportService engineImportService, + string statementName, + string engineURI) + { + var mapResultType = (MapEventType)targetType; + var mapStreamType = (MapEventType)eventTypes[streamNumber]; + + // (A) fully assignment-compatible: same number, name and type of fields, no additional expressions: Straight repackage + String typeSameMssage = BaseNestableEventType.IsDeepEqualsProperties(mapResultType.Name, mapResultType.Types, mapStreamType.Types); + if (typeSameMssage == null && selectExprContext.ExpressionNodes.Length == 0) + { + return new MapInsertProcessorSimpleRepackage(selectExprContext, streamNumber, targetType); + } + + // (B) not completely assignable: find matching properties + ICollection writables = selectExprContext.EventAdapterService.GetWriteableProperties(mapResultType, true); + IList items = new List(); + IList written = new List(); + + // find the properties coming from the providing source stream + int count = 0; + foreach (WriteablePropertyDescriptor writeable in writables) + { + String propertyName = writeable.PropertyName; + + if (mapStreamType.Types.ContainsKey(propertyName)) + { + Object setOneType = mapStreamType.Types.Get(propertyName); + Object setTwoType = mapResultType.Types.Get(propertyName); + bool setTwoTypeFound = mapResultType.Types.ContainsKey(propertyName); + String message = BaseNestableEventUtil.ComparePropType(propertyName, setOneType, setTwoType, setTwoTypeFound, mapResultType.Name); + if (message != null) + { + throw new ExprValidationException(message); + } + items.Add(new Item(count, propertyName, null, null)); + written.Add(writeable); + count++; + } + } + + // find the properties coming from the expressions of the select clause + for (int i = 0; i < selectExprContext.ExpressionNodes.Length; i++) + { + String columnName = selectExprContext.ColumnNames[i]; + ExprEvaluator evaluator = selectExprContext.ExpressionNodes[i]; + ExprNode exprNode = exprNodes[i]; + + WriteablePropertyDescriptor writable = FindWritable(columnName, writables); + if (writable == null) + { + throw new ExprValidationException("Failed to find column '" + columnName + "' in target type '" + mapResultType.Name + "'"); + } + + TypeWidener widener = TypeWidenerFactory.GetCheckPropertyAssignType( + ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(exprNode), + exprNode.ExprEvaluator.ReturnType, + writable.PropertyType, columnName, + false, null, statementName, engineURI); + items.Add(new Item(count, null, evaluator, widener)); + written.Add(writable); + count++; + } + + // make manufacturer + Item[] itemsArr = items.ToArray(); + EventBeanManufacturer manufacturer; + try + { + manufacturer = selectExprContext.EventAdapterService.GetManufacturer(mapResultType, + written.ToArray(), engineImportService, true); + } + catch (EventBeanManufactureException e) + { + throw new ExprValidationException("Failed to write to type: " + e.Message, e); + } + + return new MapInsertProcessorAllocate(streamNumber, itemsArr, manufacturer, targetType); + } + + private static WriteablePropertyDescriptor FindWritable(String columnName, ICollection writables) + { + return writables.FirstOrDefault(writable => writable.PropertyName.Equals(columnName)); + } + + private class MapInsertProcessorSimpleRepackage : SelectExprProcessor + { + private readonly SelectExprContext _selectExprContext; + private readonly int _underlyingStreamNumber; + private readonly EventType _resultType; + + internal MapInsertProcessorSimpleRepackage(SelectExprContext selectExprContext, int underlyingStreamNumber, EventType resultType) + { + _selectExprContext = selectExprContext; + _underlyingStreamNumber = underlyingStreamNumber; + _resultType = resultType; + } + + public EventType ResultEventType + { + get { return _resultType; } + } + + public EventBean Process(EventBean[] eventsPerStream, bool isNewData, bool isSynthesize, ExprEvaluatorContext exprEvaluatorContext) + { + var theEvent = (MappedEventBean)eventsPerStream[_underlyingStreamNumber]; + return _selectExprContext.EventAdapterService.AdapterForTypedMap(theEvent.Properties, _resultType); + } + } + + private class MapInsertProcessorAllocate : SelectExprProcessor + { + private readonly int _underlyingStreamNumber; + private readonly Item[] _items; + private readonly EventBeanManufacturer _manufacturer; + private readonly EventType _resultType; + + internal MapInsertProcessorAllocate(int underlyingStreamNumber, Item[] items, EventBeanManufacturer manufacturer, EventType resultType) + { + _underlyingStreamNumber = underlyingStreamNumber; + _items = items; + _manufacturer = manufacturer; + _resultType = resultType; + } + + public EventType ResultEventType + { + get { return _resultType; } + } + + public EventBean Process(EventBean[] eventsPerStream, bool isNewData, bool isSynthesize, ExprEvaluatorContext exprEvaluatorContext) + { + var theEvent = (MappedEventBean)eventsPerStream[_underlyingStreamNumber]; + var props = new Object[_items.Length]; + foreach (Item item in _items) + { + Object value; + + if (item.OptionalPropertyName != null) + { + value = theEvent.Properties.Get(item.OptionalPropertyName); + } + else + { + value = item.Evaluator.Evaluate(new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext)); + if (item.OptionalWidener != null) + { + value = item.OptionalWidener.Invoke(value); + } + } + + props[item.ToIndex] = value; + } + + return _manufacturer.Make(props); + } + } + + private class Item + { + internal Item(int toIndex, String optionalPropertyName, ExprEvaluator evaluator, TypeWidener optionalWidener) + { + ToIndex = toIndex; + OptionalPropertyName = optionalPropertyName; + Evaluator = evaluator; + OptionalWidener = optionalWidener; + } + + public readonly int ToIndex; + public readonly string OptionalPropertyName; + public readonly ExprEvaluator Evaluator; + public readonly TypeWidener OptionalWidener; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/eval/EvalSelectStreamWUndRecastObjectArrayFactory.cs b/NEsper.Core/NEsper.Core/epl/core/eval/EvalSelectStreamWUndRecastObjectArrayFactory.cs new file mode 100755 index 000000000..3384d8502 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/eval/EvalSelectStreamWUndRecastObjectArrayFactory.cs @@ -0,0 +1,210 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events; +using com.espertech.esper.events.arr; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.core.eval +{ + public class EvalSelectStreamWUndRecastObjectArrayFactory + { + public static SelectExprProcessor Make( + EventType[] eventTypes, + SelectExprContext selectExprContext, + int streamNumber, + EventType targetType, + ExprNode[] exprNodes, + EngineImportService engineImportService, + string statementName, + string engineURI) + { + var oaResultType = (ObjectArrayEventType)targetType; + var oaStreamType = (ObjectArrayEventType)eventTypes[streamNumber]; + + // (A) fully assignment-compatible: same number, name and type of fields, no additional expressions: Straight repackage + if (oaResultType.IsDeepEqualsConsiderOrder(oaStreamType) && selectExprContext.ExpressionNodes.Length == 0) + { + return new OAInsertProcessorSimpleRepackage(selectExprContext, streamNumber, targetType); + } + + // (B) not completely assignable: find matching properties + ICollection writables = selectExprContext.EventAdapterService.GetWriteableProperties(oaResultType, true); + IList items = new List(); + IList written = new List(); + + // find the properties coming from the providing source stream + foreach (WriteablePropertyDescriptor writeable in writables) + { + String propertyName = writeable.PropertyName; + + int indexSource; + int indexTarget = oaResultType.PropertiesIndexes.Get(propertyName); + + if (oaStreamType.PropertiesIndexes.TryGetValue(propertyName, out indexSource)) + { + var setOneType = oaStreamType.Types.Get(propertyName); + var setTwoType = oaResultType.Types.Get(propertyName); + var setTwoTypeFound = oaResultType.Types.ContainsKey(propertyName); + var message = BaseNestableEventUtil.ComparePropType(propertyName, setOneType, setTwoType, setTwoTypeFound, oaResultType.Name); + if (message != null) + { + throw new ExprValidationException(message); + } + items.Add(new Item(indexTarget, indexSource, null, null)); + written.Add(writeable); + } + } + + // find the properties coming from the expressions of the select clause + int count = written.Count; + for (int i = 0; i < selectExprContext.ExpressionNodes.Length; i++) + { + String columnName = selectExprContext.ColumnNames[i]; + ExprEvaluator evaluator = selectExprContext.ExpressionNodes[i]; + ExprNode exprNode = exprNodes[i]; + + WriteablePropertyDescriptor writable = FindWritable(columnName, writables); + if (writable == null) + { + throw new ExprValidationException("Failed to find column '" + columnName + "' in target type '" + oaResultType.Name + "'"); + } + + TypeWidener widener = TypeWidenerFactory.GetCheckPropertyAssignType( + ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(exprNode), + exprNode.ExprEvaluator.ReturnType, + writable.PropertyType, columnName, + false, null, statementName, engineURI); + items.Add(new Item(count, -1, evaluator, widener)); + written.Add(writable); + count++; + } + + // make manufacturer + Item[] itemsArr = items.ToArray(); + EventBeanManufacturer manufacturer; + try + { + manufacturer = selectExprContext.EventAdapterService.GetManufacturer(oaResultType, + written.ToArray(), engineImportService, true); + } + catch (EventBeanManufactureException e) + { + throw new ExprValidationException("Failed to write to type: " + e.Message, e); + } + + return new OAInsertProcessorAllocate(streamNumber, itemsArr, manufacturer, targetType); + } + + private static WriteablePropertyDescriptor FindWritable(String columnName, ICollection writables) + { + return writables.FirstOrDefault(writable => writable.PropertyName.Equals(columnName)); + } + + private class OAInsertProcessorSimpleRepackage : SelectExprProcessor + { + private readonly SelectExprContext _selectExprContext; + private readonly int _underlyingStreamNumber; + private readonly EventType _resultType; + + internal OAInsertProcessorSimpleRepackage(SelectExprContext selectExprContext, int underlyingStreamNumber, EventType resultType) + { + _selectExprContext = selectExprContext; + _underlyingStreamNumber = underlyingStreamNumber; + _resultType = resultType; + } + + public EventType ResultEventType + { + get { return _resultType; } + } + + public EventBean Process(EventBean[] eventsPerStream, bool isNewData, bool isSynthesize, ExprEvaluatorContext exprEvaluatorContext) + { + ObjectArrayBackedEventBean theEvent = (ObjectArrayBackedEventBean)eventsPerStream[_underlyingStreamNumber]; + return _selectExprContext.EventAdapterService.AdapterForTypedObjectArray(theEvent.Properties, _resultType); + } + } + + private class OAInsertProcessorAllocate : SelectExprProcessor + { + private readonly int _underlyingStreamNumber; + private readonly Item[] _items; + private readonly EventBeanManufacturer _manufacturer; + private readonly EventType _resultType; + + internal OAInsertProcessorAllocate(int underlyingStreamNumber, Item[] items, EventBeanManufacturer manufacturer, EventType resultType) + { + _underlyingStreamNumber = underlyingStreamNumber; + _items = items; + _manufacturer = manufacturer; + _resultType = resultType; + } + + public EventType ResultEventType + { + get { return _resultType; } + } + + public EventBean Process(EventBean[] eventsPerStream, bool isNewData, bool isSynthesize, ExprEvaluatorContext exprEvaluatorContext) + { + ObjectArrayBackedEventBean theEvent = (ObjectArrayBackedEventBean)eventsPerStream[_underlyingStreamNumber]; + + Object[] props = new Object[_items.Length]; + foreach (Item item in _items) + { + Object value; + + if (item.OptionalFromIndex != -1) + { + value = theEvent.Properties[item.OptionalFromIndex]; + } + else + { + value = item.Evaluator.Evaluate(new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext)); + if (item.OptionalWidener != null) + { + value = item.OptionalWidener.Invoke(value); + } + } + + props[item.ToIndex] = value; + } + + return _manufacturer.Make(props); + } + } + + private class Item + { + internal Item(int toIndex, int optionalFromIndex, ExprEvaluator evaluator, TypeWidener optionalWidener) + { + ToIndex = toIndex; + OptionalFromIndex = optionalFromIndex; + Evaluator = evaluator; + OptionalWidener = optionalWidener; + } + + public readonly int ToIndex; + + public readonly int OptionalFromIndex; + + public readonly ExprEvaluator Evaluator; + + public readonly TypeWidener OptionalWidener; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/core/eval/EvalSelectStreamWUnderlying.cs b/NEsper.Core/NEsper.Core/epl/core/eval/EvalSelectStreamWUnderlying.cs new file mode 100755 index 000000000..f47a3e9ce --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/eval/EvalSelectStreamWUnderlying.cs @@ -0,0 +1,109 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.core.eval +{ + public class EvalSelectStreamWUnderlying + : EvalSelectStreamBaseMap + , SelectExprProcessor + { + private readonly bool _singleStreamWrapper; + private readonly ExprEvaluator _underlyingExprEvaluator; + private readonly bool _underlyingIsFragmentEvent; + private readonly EventPropertyGetter _underlyingPropertyEventGetter; + private readonly int _underlyingStreamNumber; + private readonly IList _unnamedStreams; + private readonly TableMetadata _tableMetadata; + + public EvalSelectStreamWUnderlying(SelectExprContext selectExprContext, + EventType resultEventType, + IList namedStreams, + bool usingWildcard, + IList unnamedStreams, + bool singleStreamWrapper, + bool underlyingIsFragmentEvent, + int underlyingStreamNumber, + EventPropertyGetter underlyingPropertyEventGetter, + ExprEvaluator underlyingExprEvaluator, + TableMetadata tableMetadata) + : base(selectExprContext, resultEventType, namedStreams, usingWildcard) + { + _unnamedStreams = unnamedStreams; + _singleStreamWrapper = singleStreamWrapper; + _underlyingIsFragmentEvent = underlyingIsFragmentEvent; + _underlyingStreamNumber = underlyingStreamNumber; + _underlyingPropertyEventGetter = underlyingPropertyEventGetter; + _underlyingExprEvaluator = underlyingExprEvaluator; + _tableMetadata = tableMetadata; + } + + public override EventBean ProcessSpecific(IDictionary props, + EventBean[] eventsPerStream, + bool isNewData, + ExprEvaluatorContext exprEvaluatorContext) + { + // In case of a wildcard and single stream that is itself a + // wrapper bean, we also need to add the map properties + if (_singleStreamWrapper) + { + var wrapper = (DecoratingEventBean)eventsPerStream[0]; + if (wrapper != null) + { + IDictionary map = wrapper.DecoratingProperties; + props.PutAll(map); + } + } + + EventBean theEvent = null; + if (_underlyingIsFragmentEvent) + { + EventBean eventBean = eventsPerStream[_underlyingStreamNumber]; + theEvent = (EventBean)eventBean.GetFragment(_unnamedStreams[0].StreamSelected.StreamName); + } + else if (_underlyingPropertyEventGetter != null) + { + object value = _underlyingPropertyEventGetter.Get(eventsPerStream[_underlyingStreamNumber]); + if (value != null) + { + theEvent = SelectExprContext.EventAdapterService.AdapterForObject(value); + } + } + else if (_underlyingExprEvaluator != null) + { + object value = _underlyingExprEvaluator.Evaluate(new EvaluateParams(eventsPerStream, true, exprEvaluatorContext)); + if (value != null) + { + theEvent = SelectExprContext.EventAdapterService.AdapterForObject(value); + } + } + else + { + theEvent = eventsPerStream[_underlyingStreamNumber]; + if (_tableMetadata != null && theEvent != null) + { + theEvent = _tableMetadata.EventToPublic.Convert(theEvent, new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext)); + } + } + + // Using a wrapper bean since we cannot use the same event type else same-type filters match. + // Wrapping it even when not adding properties is very inexpensive. + return base.SelectExprContext.EventAdapterService.AdapterForTypedWrapper(theEvent, props, base.ResultEventType); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/core/eval/EvalSelectStreamWUnderlyingRecastMap.cs b/NEsper.Core/NEsper.Core/epl/core/eval/EvalSelectStreamWUnderlyingRecastMap.cs new file mode 100755 index 000000000..0d485fb0b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/eval/EvalSelectStreamWUnderlyingRecastMap.cs @@ -0,0 +1,49 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.core.eval +{ + public class EvalSelectStreamWUnderlyingRecastMap : SelectExprProcessor + { + private readonly EventType _resultType; + private readonly SelectExprContext _selectExprContext; + private readonly int _underlyingStreamNumber; + + public EvalSelectStreamWUnderlyingRecastMap(SelectExprContext selectExprContext, + int underlyingStreamNumber, + EventType resultType) + { + _selectExprContext = selectExprContext; + _underlyingStreamNumber = underlyingStreamNumber; + _resultType = resultType; + } + + #region SelectExprProcessor Members + + public EventType ResultEventType + { + get { return _resultType; } + } + + public EventBean Process(EventBean[] eventsPerStream, + bool isNewData, + bool isSynthesize, + ExprEvaluatorContext exprEvaluatorContext) + { + var theEvent = (MappedEventBean) eventsPerStream[_underlyingStreamNumber]; + return _selectExprContext.EventAdapterService.AdapterForTypedMap(theEvent.Properties, _resultType); + } + + #endregion + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/core/eval/EvalSelectStreamWUnderlyingRecastObjectArray.cs b/NEsper.Core/NEsper.Core/epl/core/eval/EvalSelectStreamWUnderlyingRecastObjectArray.cs new file mode 100755 index 000000000..2e32c9397 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/eval/EvalSelectStreamWUnderlyingRecastObjectArray.cs @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.core.eval +{ + public class EvalSelectStreamWUnderlyingRecastObjectArray : SelectExprProcessor + { + private readonly EventType _resultType; + private readonly SelectExprContext _selectExprContext; + private readonly int _underlyingStreamNumber; + + public EvalSelectStreamWUnderlyingRecastObjectArray(SelectExprContext selectExprContext, + int underlyingStreamNumber, + EventType resultType) + { + _selectExprContext = selectExprContext; + _underlyingStreamNumber = underlyingStreamNumber; + _resultType = resultType; + } + + public EventType ResultEventType + { + get { return _resultType; } + } + + public EventBean Process(EventBean[] eventsPerStream, + bool isNewData, + bool isSynthesize, + ExprEvaluatorContext exprEvaluatorContext) + { + var theEvent = (ObjectArrayBackedEventBean) eventsPerStream[_underlyingStreamNumber]; + return _selectExprContext.EventAdapterService.AdapterForTypedObjectArray(theEvent.Properties, _resultType); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/core/eval/EvalSelectWildcard.cs b/NEsper.Core/NEsper.Core/epl/core/eval/EvalSelectWildcard.cs new file mode 100755 index 000000000..a77355574 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/eval/EvalSelectWildcard.cs @@ -0,0 +1,44 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.core.eval +{ + public class EvalSelectWildcard : EvalBaseMap, SelectExprProcessor + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + public EvalSelectWildcard(SelectExprContext selectExprContext, EventType resultEventType) + : base(selectExprContext, resultEventType) + { + } + + public override EventBean ProcessSpecific( + IDictionary props, + EventBean[] eventsPerStream, + bool isNewData, + bool isSynthesize, + ExprEvaluatorContext exprEvaluatorContext) + { + EventBean theEvent = eventsPerStream[0]; + + // Using a wrapper bean since we cannot use the same event type else same-type filters match. + // Wrapping it even when not adding properties is very inexpensive. + return base.EventAdapterService.AdapterForTypedWrapper(theEvent, props, base.ResultEventType); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/eval/EvalSelectWildcardJoin.cs b/NEsper.Core/NEsper.Core/epl/core/eval/EvalSelectWildcardJoin.cs new file mode 100755 index 000000000..7209cdff9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/eval/EvalSelectWildcardJoin.cs @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.core.eval +{ + public class EvalSelectWildcardJoin : EvalBaseMap, SelectExprProcessor + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly SelectExprProcessor _joinWildcardProcessor; + + public EvalSelectWildcardJoin(SelectExprContext selectExprContext, EventType resultEventType, SelectExprProcessor joinWildcardProcessor) + : base(selectExprContext, resultEventType) + { + _joinWildcardProcessor = joinWildcardProcessor; + } + + public override EventBean ProcessSpecific( + IDictionary props, + EventBean[] eventsPerStream, + bool isNewData, + bool isSynthesize, + ExprEvaluatorContext exprEvaluatorContext) + { + EventBean theEvent = _joinWildcardProcessor.Process( + eventsPerStream, isNewData, isSynthesize, exprEvaluatorContext); + + // Using a wrapper bean since we cannot use the same event type else same-type filters match. + // Wrapping it even when not adding properties is very inexpensive. + return base.EventAdapterService.AdapterForTypedWrapper(theEvent, props, base.ResultEventType); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/eval/EvalSelectWildcardSSWrapper.cs b/NEsper.Core/NEsper.Core/epl/core/eval/EvalSelectWildcardSSWrapper.cs new file mode 100755 index 000000000..1fbc012d8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/eval/EvalSelectWildcardSSWrapper.cs @@ -0,0 +1,59 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.core.eval +{ + public class EvalSelectWildcardSSWrapper + : EvalBaseMap + , SelectExprProcessor + { + public EvalSelectWildcardSSWrapper(SelectExprContext selectExprContext, + EventType resultEventType) + : base(selectExprContext, resultEventType) + { + } + + public override EventBean ProcessSpecific(IDictionary props, + EventBean[] eventsPerStream, + bool isNewData, + bool isSynthesize, + ExprEvaluatorContext exprEvaluatorContext) + { + // In case of a wildcard and single stream that is itself a + // wrapper bean, we also need to add the map properties + var wrapper = (DecoratingEventBean) eventsPerStream[0]; + if (wrapper != null) + { + IDictionary map = wrapper.DecoratingProperties; + if ((ExprNodes.Length == 0) && (map.IsNotEmpty())) + { + props = new Dictionary(map); + } + else + { + props.PutAll(map); + } + } + + EventBean theEvent = eventsPerStream[0]; + + // Using a wrapper bean since we cannot use the same event type else same-type filters match. + // Wrapping it even when not adding properties is very inexpensive. + return EventAdapterService.AdapterForTypedWrapper(theEvent, props, ResultEventType); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/core/eval/SelectExprContext.cs b/NEsper.Core/NEsper.Core/epl/core/eval/SelectExprContext.cs new file mode 100755 index 000000000..837218a5f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/eval/SelectExprContext.cs @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.core.eval +{ + public class SelectExprContext + { + public SelectExprContext(ExprEvaluator[] expressionNodes, string[] columnNames, EventAdapterService eventAdapterService) + { + ExpressionNodes = expressionNodes; + ColumnNames = columnNames; + EventAdapterService = eventAdapterService; + } + + public ExprEvaluator[] ExpressionNodes { get; set; } + + public string[] ColumnNames { get; private set; } + + public EventAdapterService EventAdapterService { get; private set; } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/core/eval/SelectExprStreamDesc.cs b/NEsper.Core/NEsper.Core/epl/core/eval/SelectExprStreamDesc.cs new file mode 100755 index 000000000..12a7f0bb9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/core/eval/SelectExprStreamDesc.cs @@ -0,0 +1,31 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.epl.core.eval +{ + public class SelectExprStreamDesc + { + public SelectExprStreamDesc(SelectClauseStreamCompiledSpec streamSelected) + { + StreamSelected = streamSelected; + ExpressionSelectedAsStream = null; + } + + public SelectExprStreamDesc(SelectClauseExprCompiledSpec expressionSelectedAsStream) + { + ExpressionSelectedAsStream = expressionSelectedAsStream; + StreamSelected = null; + } + + public SelectClauseStreamCompiledSpec StreamSelected { get; private set; } + + public SelectClauseExprCompiledSpec ExpressionSelectedAsStream { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/datetime/calop/ApacheCommonsDateUtils.cs b/NEsper.Core/NEsper.Core/epl/datetime/calop/ApacheCommonsDateUtils.cs new file mode 100755 index 000000000..3bd0de08b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/calop/ApacheCommonsDateUtils.cs @@ -0,0 +1,405 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; + +using com.espertech.esper.compat; + +namespace com.espertech.esper.epl.datetime.calop +{ + public class ApacheCommonsDateUtils + { + /// + /// Number of milliseconds in a standard second. + /// + public const long MILLIS_PER_SECOND = 1000; + /// + /// Number of milliseconds in a standard minute. + /// + public const long MILLIS_PER_MINUTE = 60 * MILLIS_PER_SECOND; + /// + /// Number of milliseconds in a standard hour. + /// + public const long MILLIS_PER_HOUR = 60 * MILLIS_PER_MINUTE; + /// + /// Number of milliseconds in a standard day. + /// + public const long MILLIS_PER_DAY = 24 * MILLIS_PER_HOUR; + + /// + /// This is half a month, so this represents whether a date is in the top + /// or bottom half of the month. + /// + public const int SEMI_MONTH = 1001; + + /// + /// Constant marker for truncating + /// + public const int MODIFY_TRUNCATE = 0; + + /// + /// Constant marker for rounding + /// + public const int MODIFY_ROUND = 1; + + /// + /// Constant marker for ceiling + /// + public const int MODIFY_CEILING = 2; + + private static readonly int[][] Fields = + { + new[] {DateTimeFieldEnum.MILLISEC}, + new[] {DateTimeFieldEnum.SECOND}, + new[] {DateTimeFieldEnum.MINUTE}, + new[] {DateTimeFieldEnum.HOUR_OF_DAY, DateTimeFieldEnum.HOUR}, + new[] {DateTimeFieldEnum.DATE, DateTimeFieldEnum.DAY_OF_MONTH, DateTimeFieldEnum.AM_PM}, + new[] {DateTimeFieldEnum.MONTH, ApacheCommonsDateUtils.SEMI_MONTH}, + new[] {DateTimeFieldEnum.YEAR} + }; + + public static DateTime Modify(DateTime val, int field, int modType) + { + if (val.Year > 280000000) + { + throw new ArithmeticException("Calendar value too large for accurate calculations"); + } + + if (field == DateTimeFieldEnum.MILLISEC) + { + return val; + } + + // ----------------- Fix for LANG-59 ---------------------- START --------------- + // see http://issues.apache.org/jira/browse/LANG-59 + // + // Manually truncate milliseconds, seconds and minutes, rather than using + // Calendar methods. + + var time = val.UtcMillis(); + var done = false; + + // truncate milliseconds + int millisecs = val.Millisecond; + if (MODIFY_TRUNCATE == modType || millisecs < 500) + { + time = time - millisecs; + } + if (field == DateTimeFieldEnum.SECOND) + { + done = true; + } + + // truncate seconds + int seconds = val.Second; + if (!done && (MODIFY_TRUNCATE == modType || seconds < 30)) + { + time = time - (seconds*1000L); + } + if (field == DateTimeFieldEnum.MINUTE) + { + done = true; + } + + // truncate minutes + int minutes = val.Minute; + if (!done && (MODIFY_TRUNCATE == modType || minutes < 30)) + { + time = time - (minutes*60000L); + } + + // reset time + if (val.UtcMillis() != time) + { + val = DateTimeHelper.FromMillis(time); + } + // ----------------- Fix for LANG-59 ----------------------- END ---------------- + + var roundUp = false; + for (int i = 0; i < Fields.Length; i++) + { + for (int j = 0; j < Fields[i].Length; j++) + { + if (Fields[i][j] == field) + { + //This is our field... we stop looping + if (modType == MODIFY_CEILING || (modType == MODIFY_ROUND && roundUp)) + { + if (field == ApacheCommonsDateUtils.SEMI_MONTH) + { + //This is a special case that's hard to generalize + //If the date is 1, we round up to 16, otherwise + // we subtract 15 days and add 1 month + if (val.Day == 1) + { + val = val.AddDays(15); + } + else + { + val = val.AddDays(-15).AddMonths(1); + } + // ----------------- Fix for LANG-440 ---------------------- START --------------- + } + else if (field == DateTimeFieldEnum.AM_PM) + { + // This is a special case + // If the time is 0, we round up to 12, otherwise + // we subtract 12 hours and add 1 day + if (val.Hour == 0) + { + val = val.AddHours(12); + } + else + { + val = val.AddHours(-12).AddDays(1); + } + // ----------------- Fix for LANG-440 ---------------------- END --------------- + } + else + { + //We need at add one to this field since the + // last number causes us to round up + val = val.AddUsingField(Fields[i][0], 1); + } + } + return val; + } + } + //We have various fields that are not easy roundings + var offset = 0; + var offsetSet = false; + //These are special types of fields that require different rounding rules + switch (field) + { + case ApacheCommonsDateUtils.SEMI_MONTH: + if (Fields[i][0] == DateTimeFieldEnum.DATE) + { + //If we're going to drop the DATE field's value, + // we want to do this our own way. + //We need to subtrace 1 since the date has a minimum of 1 + offset = val.Day - 1; + //If we're above 15 days adjustment, that means we're in the + // bottom half of the month and should stay accordingly. + if (offset >= 15) + { + offset -= 15; + } + //Record whether we're in the top or bottom half of that range + roundUp = offset > 7; + offsetSet = true; + } + break; + case DateTimeFieldEnum.AM_PM: + if (Fields[i][0] == DateTimeFieldEnum.HOUR_OF_DAY) + { + //If we're going to drop the HOUR field's value, + // we want to do this our own way. + offset = val.Hour; + if (offset >= 12) + { + offset -= 12; + } + roundUp = offset >= 6; + offsetSet = true; + } + break; + } + if (!offsetSet) + { + int min = val.GetActualMinimum(Fields[i][0]); + int max = val.GetActualMaximum(Fields[i][0]); + //Calculate the offset from the minimum allowed value + offset = val.GetFieldValue(Fields[i][0]) - min; + //Set roundUp if this is more than half way between the minimum and maximum + roundUp = offset > ((max - min)/2); + } + //We need to remove this field + if (offset != 0) + { + val = val.SetFieldValue(Fields[i][0], val.GetFieldValue(Fields[i][0]) - offset); + } + } + + throw new ArgumentException("The field " + field + " is not supported"); + } + + public static DateTimeOffset Modify(DateTimeOffset val, int field, int modType, TimeZoneInfo timeZone) + { + if (val.Year > 280000000) + { + throw new ArithmeticException("Calendar value too large for accurate calculations"); + } + + if (field == DateTimeFieldEnum.MILLISEC) + { + return val; + } + + // ----------------- Fix for LANG-59 ---------------------- START --------------- + // see http://issues.apache.org/jira/browse/LANG-59 + // + // Manually truncate milliseconds, seconds and minutes, rather than using + // Calendar methods. + + var time = val.TimeInMillis(); + var done = false; + + // truncate milliseconds + int millisecs = val.Millisecond; + if (MODIFY_TRUNCATE == modType || millisecs < 500) + { + time = time - millisecs; + } + if (field == DateTimeFieldEnum.SECOND) + { + done = true; + } + + // truncate seconds + int seconds = val.Second; + if (!done && (MODIFY_TRUNCATE == modType || seconds < 30)) + { + time = time - (seconds * 1000L); + } + if (field == DateTimeFieldEnum.MINUTE) + { + done = true; + } + + // truncate minutes + int minutes = val.Minute; + if (!done && (MODIFY_TRUNCATE == modType || minutes < 30)) + { + time = time - (minutes * 60000L); + } + + // reset time + if (val.TimeInMillis() != time) + { + val = DateTimeOffsetHelper.TimeFromMillis(time, timeZone); + } + // ----------------- Fix for LANG-59 ----------------------- END ---------------- + + var roundUp = false; + for (int i = 0; i < Fields.Length; i++) + { + for (int j = 0; j < Fields[i].Length; j++) + { + if (Fields[i][j] == field) + { + //This is our field... we stop looping + if (modType == MODIFY_CEILING || (modType == MODIFY_ROUND && roundUp)) + { + if (field == ApacheCommonsDateUtils.SEMI_MONTH) + { + //This is a special case that's hard to generalize + //If the date is 1, we round up to 16, otherwise + // we subtract 15 days and add 1 month + if (val.Day == 1) + { + val = val.AddDays(15); + } + else + { + val = val.AddDays(-15).AddMonthsLikeJava(1); + } + // ----------------- Fix for LANG-440 ---------------------- START --------------- + } + else if (field == DateTimeFieldEnum.AM_PM) + { + // This is a special case + // If the time is 0, we round up to 12, otherwise + // we subtract 12 hours and add 1 day + if (val.Hour == 0) + { + val = val.AddHours(12); + } + else + { + val = val.AddHours(-12).AddDays(1); + } + // ----------------- Fix for LANG-440 ---------------------- END --------------- + } + else + { + //We need at add one to this field since the + // last number causes us to round up + val = val.AddUsingField(Fields[i][0], 1); + } + } + return val; + } + } + //We have various fields that are not easy roundings + var offset = 0; + var offsetSet = false; + //These are special types of fields that require different rounding rules + switch (field) + { + case ApacheCommonsDateUtils.SEMI_MONTH: + if (Fields[i][0] == DateTimeFieldEnum.DATE) + { + //If we're going to drop the DATE field's value, + // we want to do this our own way. + //We need to subtrace 1 since the date has a minimum of 1 + offset = val.Day - 1; + //If we're above 15 days adjustment, that means we're in the + // bottom half of the month and should stay accordingly. + if (offset >= 15) + { + offset -= 15; + } + //Record whether we're in the top or bottom half of that range + roundUp = offset > 7; + offsetSet = true; + } + break; + case DateTimeFieldEnum.AM_PM: + if (Fields[i][0] == DateTimeFieldEnum.HOUR_OF_DAY) + { + //If we're going to drop the HOUR field's value, + // we want to do this our own way. + offset = val.Hour; + if (offset >= 12) + { + offset -= 12; + } + roundUp = offset >= 6; + offsetSet = true; + } + break; + } + if (!offsetSet) + { + int min = val.GetActualMinimum(Fields[i][0]); + int max = val.GetActualMaximum(Fields[i][0]); + //Calculate the offset from the minimum allowed value + offset = val.GetFieldValue(Fields[i][0]) - min; + //Set roundUp if this is more than half way between the minimum and maximum + roundUp = offset > ((max - min) / 2); + } + //We need to remove this field + if (offset != 0) + { + val = val.SetFieldValue(Fields[i][0], val.GetFieldValue(Fields[i][0]) - offset, timeZone); + } + } + + throw new ArgumentException("The field " + field + " is not supported"); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/calop/CalendarFieldEnum.cs b/NEsper.Core/NEsper.Core/epl/datetime/calop/CalendarFieldEnum.cs new file mode 100755 index 000000000..1d57fd399 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/calop/CalendarFieldEnum.cs @@ -0,0 +1,166 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Linq; +using System.Text; + +using com.espertech.esper.compat; + +namespace com.espertech.esper.epl.datetime.calop +{ + public enum CalendarFieldEnum + { + MILLISEC, + SECOND, + MINUTE, + HOUR, + DAY, + MONTH, + WEEK, + YEAR + } + + public static class CalendarFieldEnumExtensions + { + public static string[] GetNames(this CalendarFieldEnum @enum) + { + switch (@enum) + { + case CalendarFieldEnum.MILLISEC: + return new[] { "msec", "millisecond", "milliseconds" }; + case CalendarFieldEnum.SECOND: + return new[] { "sec", "second", "seconds" }; + case CalendarFieldEnum.MINUTE: + return new[] { "min", "minute", "minutes" }; + case CalendarFieldEnum.HOUR: + return new[] { "hour", "hours" }; + case CalendarFieldEnum.DAY: + return new[] { "day", "days" }; + case CalendarFieldEnum.MONTH: + return new[] { "month", "months" }; + case CalendarFieldEnum.WEEK: + return new[] { "week", "weeks" }; + case CalendarFieldEnum.YEAR: + return new[] { "year", "years" }; + } + + throw new ArgumentException("invalid value", "enum"); + } + + public static string GetValidList(this CalendarFieldEnum @enum) + { + var builder = new StringBuilder(); + var delimiter = ""; + + var values = Enum.GetValues(typeof(CalendarFieldEnum)); + foreach (CalendarFieldEnum value in values) + { + foreach (String name in GetNames(value)) + { + builder.Append(delimiter); + builder.Append(name); + delimiter = ","; + } + } + + return builder.ToString(); + } + + public static CalendarFieldEnum FromString(string field) + { + string compareTo = field.Trim().ToLower(); + + var values = Enum.GetValues(typeof(CalendarFieldEnum)); + foreach (CalendarFieldEnum value in values) + { + if (GetNames(value).Any(name => name == field)) + { + return value; + } + } + + throw new ArgumentException("value not found", "field"); + } + + public static int ToDateTimeFieldEnum(this CalendarFieldEnum @enum) + { + switch (@enum) + { + case CalendarFieldEnum.MILLISEC: + return DateTimeFieldEnum.MILLISEC; + case CalendarFieldEnum.SECOND: + return DateTimeFieldEnum.SECOND; + case CalendarFieldEnum.MINUTE: + return DateTimeFieldEnum.MINUTE; + case CalendarFieldEnum.HOUR: + return DateTimeFieldEnum.HOUR; + case CalendarFieldEnum.DAY: + return DateTimeFieldEnum.DAY; + case CalendarFieldEnum.MONTH: + return DateTimeFieldEnum.MONTH; + case CalendarFieldEnum.WEEK: + return DateTimeFieldEnum.WEEK; + case CalendarFieldEnum.YEAR: + return DateTimeFieldEnum.YEAR; + } + + throw new ArgumentException("invalid value", "enum"); + } + + public static ChronoUnit GetChronoUnit(this CalendarFieldEnum @enum) + { + switch (@enum) + { + case CalendarFieldEnum.MILLISEC: + return ChronoUnit.MILLIS; + case CalendarFieldEnum.SECOND: + return ChronoUnit.SECONDS; + case CalendarFieldEnum.MINUTE: + return ChronoUnit.MINUTES; + case CalendarFieldEnum.HOUR: + return ChronoUnit.HOURS; + case CalendarFieldEnum.DAY: + return ChronoUnit.DAYS; + case CalendarFieldEnum.MONTH: + return ChronoUnit.MONTHS; + case CalendarFieldEnum.WEEK: + return ChronoUnit.WEEKS; + case CalendarFieldEnum.YEAR: + return ChronoUnit.YEARS; + } + + throw new ArgumentException("invalid value", "enum"); + } + + public static ChronoField GetChronoField(this CalendarFieldEnum @enum) + { + switch (@enum) + { + case CalendarFieldEnum.MILLISEC: + return ChronoField.MILLI_OF_SECOND; + case CalendarFieldEnum.SECOND: + return ChronoField.SECOND_OF_MINUTE; + case CalendarFieldEnum.MINUTE: + return ChronoField.MINUTE_OF_HOUR; + case CalendarFieldEnum.HOUR: + return ChronoField.HOUR_OF_DAY; + case CalendarFieldEnum.DAY: + return ChronoField.DAY_OF_MONTH; + case CalendarFieldEnum.MONTH: + return ChronoField.MONTH_OF_YEAR; + case CalendarFieldEnum.WEEK: + return ChronoField.ALIGNED_WEEK_OF_YEAR; + case CalendarFieldEnum.YEAR: + return ChronoField.YEAR; + } + + throw new ArgumentException("invalid value", "enum"); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/calop/CalendarOp.cs b/NEsper.Core/NEsper.Core/epl/datetime/calop/CalendarOp.cs new file mode 100755 index 000000000..82e60f4d3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/calop/CalendarOp.cs @@ -0,0 +1,19 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.datetime.calop +{ + public interface CalendarOp + { + void Evaluate(DateTimeEx cal, EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/datetime/calop/CalendarOpFactory.cs b/NEsper.Core/NEsper.Core/epl/datetime/calop/CalendarOpFactory.cs new file mode 100755 index 000000000..4a03be565 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/calop/CalendarOpFactory.cs @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.compat; +using com.espertech.esper.epl.datetime.eval; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.datetime.calop +{ + public class CalendarOpFactory : OpFactory + { + public CalendarOp GetOp(DatetimeMethodEnum method, String methodNameUsed, IList parameters, ExprEvaluator[] evaluators) + { + if (method == DatetimeMethodEnum.WITHTIME) { + return new CalendarOpWithTime(evaluators[0], evaluators[1], evaluators[2], evaluators[3]); + } + if (method == DatetimeMethodEnum.WITHDATE) { + return new CalendarOpWithDate(evaluators[0], evaluators[1], evaluators[2]); + } + if (method == DatetimeMethodEnum.PLUS || method == DatetimeMethodEnum.MINUS) { + return new CalendarOpPlusMinus(evaluators[0], method == DatetimeMethodEnum.MINUS ? -1 : 1); + } + if (method == DatetimeMethodEnum.WITHMAX || + method == DatetimeMethodEnum.WITHMIN || + method == DatetimeMethodEnum.ROUNDCEILING || + method == DatetimeMethodEnum.ROUNDFLOOR || + method == DatetimeMethodEnum.ROUNDHALF || + method == DatetimeMethodEnum.SET) { + CalendarFieldEnum fieldNum = CalendarOpUtil.GetEnum(methodNameUsed, parameters[0]); + if (method == DatetimeMethodEnum.WITHMIN) { + return new CalendarOpWithMin(fieldNum); + } + if (method == DatetimeMethodEnum.ROUNDCEILING || method == DatetimeMethodEnum.ROUNDFLOOR || method == DatetimeMethodEnum.ROUNDHALF) { + return new CalendarOpRound(fieldNum, method); + } + if (method == DatetimeMethodEnum.SET) { + return new CalendarOpSet(fieldNum, evaluators[1]); + } + return new CalendarOpWithMax(fieldNum); + } + throw new IllegalStateException("Unrecognized calendar-op code '" + method + "'"); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/calop/CalendarOpPlusFastAddHelper.cs b/NEsper.Core/NEsper.Core/epl/datetime/calop/CalendarOpPlusFastAddHelper.cs new file mode 100755 index 000000000..d0dc100c7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/calop/CalendarOpPlusFastAddHelper.cs @@ -0,0 +1,114 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using com.espertech.esper.client.util; +using com.espertech.esper.compat; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.expression.time; + +namespace com.espertech.esper.epl.datetime.calop +{ + public class CalendarOpPlusFastAddHelper + { + private const bool DEBUG = false; + + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public static CalendarOpPlusFastAddResult ComputeNextDue( + long currentTime, + TimePeriod timePeriod, + DateTimeEx reference, + TimeAbacus timeAbacus, + long remainder) + { + if (timeAbacus.CalendarGet(reference, remainder) > currentTime) + { + return new CalendarOpPlusFastAddResult(0, reference); + } + + // add one time period + DateTimeEx work = reference.Clone(); + if (DEBUG && Log.IsDebugEnabled) + { + Log.Debug("Work date is " + work); + } + + CalendarOpPlusMinus.ActionSafeOverflow(work, 1, timePeriod); + long inMillis = timeAbacus.CalendarGet(work, remainder); + if (inMillis > currentTime) + { + return new CalendarOpPlusFastAddResult(1, work); + } + if (DEBUG && Log.IsDebugEnabled) + { + Log.Debug("Work date is {0}", work); + } + + long factor = 1; + + // determine multiplier + long refTime = timeAbacus.CalendarGet(reference, remainder); + long deltaCurrentToStart = currentTime - refTime; + long deltaAddedOne = timeAbacus.CalendarGet(work, remainder) - refTime; + double multiplierDbl = (deltaCurrentToStart/deltaAddedOne) - 1; + var multiplierRoundedLong = (long) multiplierDbl; + + // handle integer max + while (multiplierRoundedLong > Int32.MaxValue) + { + CalendarOpPlusMinus.ActionSafeOverflow(work, Int32.MaxValue, timePeriod); + factor += Int32.MaxValue; + multiplierRoundedLong -= Int32.MaxValue; + if (DEBUG && Log.IsDebugEnabled) + { + Log.Debug("Work date is {0} factor {1}", work, factor); + } + } + + // add + var multiplierRoundedInt = (int) multiplierRoundedLong; + CalendarOpPlusMinus.ActionSafeOverflow(work, multiplierRoundedInt, timePeriod); + factor += multiplierRoundedInt; + + // if below, add more + if (timeAbacus.CalendarGet(work, remainder) <= currentTime) + { + while (timeAbacus.CalendarGet(work, remainder) <= currentTime) + { + CalendarOpPlusMinus.ActionSafeOverflow(work, 1, timePeriod); + factor += 1; + if (DEBUG && Log.IsDebugEnabled) + { + Log.Debug("Work date is {0} factor {1}", work, factor); + } + } + return new CalendarOpPlusFastAddResult(factor, work); + } + + // we are over + while (timeAbacus.CalendarGet(work, remainder) > currentTime) + { + CalendarOpPlusMinus.ActionSafeOverflow(work, -1, timePeriod); + factor -= 1; + if (DEBUG && Log.IsDebugEnabled) + { + Log.Debug("Work date is {0} factor {1}", work, factor); + } + } + CalendarOpPlusMinus.ActionSafeOverflow(work, 1, timePeriod); + if (DEBUG && Log.IsDebugEnabled) + { + Log.Debug("Work date is {0} factor {1}", work, factor); + } + return new CalendarOpPlusFastAddResult(factor + 1, work); + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/datetime/calop/CalendarOpPlusFastAddResult.cs b/NEsper.Core/NEsper.Core/epl/datetime/calop/CalendarOpPlusFastAddResult.cs new file mode 100755 index 000000000..85d0087e9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/calop/CalendarOpPlusFastAddResult.cs @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.compat; + +namespace com.espertech.esper.epl.datetime.calop +{ + public class CalendarOpPlusFastAddResult + { + public CalendarOpPlusFastAddResult(long factor, DateTimeEx scheduled) + { + Factor = factor; + Scheduled = scheduled; + } + + public long Factor { get; private set; } + + public DateTimeEx Scheduled { get; private set; } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/datetime/calop/CalendarOpPlusMinus.cs b/NEsper.Core/NEsper.Core/epl/datetime/calop/CalendarOpPlusMinus.cs new file mode 100755 index 000000000..8c793f77b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/calop/CalendarOpPlusMinus.cs @@ -0,0 +1,139 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.client.util; +using com.espertech.esper.compat; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.datetime.calop +{ + public class CalendarOpPlusMinus : CalendarOp + { + private readonly ExprEvaluator _param; + private readonly int _factor; + + public CalendarOpPlusMinus(ExprEvaluator param, int factor) + { + _param = param; + _factor = factor; + } + + public void Evaluate(DateTimeEx dateTime, EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + var value = _param.Evaluate(new EvaluateParams(eventsPerStream, isNewData, context)); + if (value.IsNumber()) + { + Action(dateTime, _factor, value.AsLong()); + } + else + { + Action(dateTime, _factor, (TimePeriod) value); + } + } + + internal static DateTimeEx Action(DateTimeEx dateTime, int factor, long? duration) + { + if (duration == null) + { + return dateTime; + } + + if (duration < Int32.MaxValue) + { + return dateTime.AddMilliseconds((int)(factor * duration)); + } + + var days = (int)(duration / (1000L * 60 * 60 * 24)); + var msec = (int)(duration - days * (1000L * 60 * 60 * 24)); + var newDate = dateTime + .AddMilliseconds(factor * msec) + .AddDays(factor * days, DateTimeMathStyle.Java); + + return newDate; + } + + public static void ActionSafeOverflow(DateTimeEx dateTime, int factor, TimePeriod tp) + { + if (Math.Abs(factor) == 1) + { + Action(dateTime, factor, tp); + return; + } + + var max = tp.LargestAbsoluteValue(); + if (max == null || max == 0) + { + return; + } + + ActionHandleOverflow(dateTime, factor, tp, max.Value); + } + + public static void Action(DateTimeEx dateTime, int factor, TimePeriod tp) + { + if (tp == null) + { + return; + } + + if (tp.Years != null) + { + dateTime.AddYears(tp.Years.Value * factor); + } + if (tp.Months != null) + { + dateTime.AddMonths(tp.Months.Value * factor, DateTimeMathStyle.Java); + } + if (tp.Weeks != null) + { + dateTime.AddDays(tp.Weeks.Value * 7 * factor, DateTimeMathStyle.Java); + } + if (tp.Days != null) + { + dateTime.AddDays(tp.Days.Value * factor, DateTimeMathStyle.Java); + } + if (tp.Hours != null) + { + dateTime.AddHours(tp.Hours.Value * factor, DateTimeMathStyle.Java); + } + if (tp.Minutes != null) + { + dateTime.AddMinutes(tp.Minutes.Value * factor, DateTimeMathStyle.Java); + } + if (tp.Seconds != null) + { + dateTime.AddSeconds(tp.Seconds.Value * factor, DateTimeMathStyle.Java); + } + if (tp.Milliseconds != null) + { + dateTime.AddMilliseconds(tp.Milliseconds.Value * factor, DateTimeMathStyle.Java); + } + } + + private static void ActionHandleOverflow(DateTimeEx dateTime, int factor, TimePeriod tp, int max) + { + if (max != 0 && factor > int.MaxValue / max) + { + // overflow + int first = factor / 2; + int second = (factor - first * 2) + first; + ActionHandleOverflow(dateTime, first, tp, max); + ActionHandleOverflow(dateTime, second, tp, max); + } + else + { + // no overflow + Action(dateTime, factor, tp); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/calop/CalendarOpRound.cs b/NEsper.Core/NEsper.Core/epl/datetime/calop/CalendarOpRound.cs new file mode 100755 index 000000000..dee03fe5c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/calop/CalendarOpRound.cs @@ -0,0 +1,47 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.datetime.eval; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.datetime.calop +{ + public class CalendarOpRound : CalendarOp + { + private readonly CalendarFieldEnum _fieldName; + private readonly int _code; + + public CalendarOpRound(CalendarFieldEnum fieldName, DatetimeMethodEnum method) { + _fieldName = fieldName; + if (method == DatetimeMethodEnum.ROUNDCEILING) { + _code = ApacheCommonsDateUtils.MODIFY_CEILING; + } + else if (method == DatetimeMethodEnum.ROUNDFLOOR) { + _code = ApacheCommonsDateUtils.MODIFY_TRUNCATE; + } + else if (method == DatetimeMethodEnum.ROUNDHALF) { + _code = ApacheCommonsDateUtils.MODIFY_ROUND; + } + else { + throw new ArgumentException("Unrecognized method '" + method + "'"); + } + } + + public void Evaluate(DateTimeEx dateTime, EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + dateTime.Set( + ApacheCommonsDateUtils.Modify( + dateTime.DateTime, _fieldName.ToDateTimeFieldEnum(), _code, dateTime.TimeZone)); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/calop/CalendarOpSet.cs b/NEsper.Core/NEsper.Core/epl/datetime/calop/CalendarOpSet.cs new file mode 100755 index 000000000..591586edd --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/calop/CalendarOpSet.cs @@ -0,0 +1,65 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.datetime.calop +{ + public class CalendarOpSet : CalendarOp + { + private readonly CalendarFieldEnum _fieldName; + private readonly ExprEvaluator _valueExpr; + + public CalendarOpSet(CalendarFieldEnum fieldName, ExprEvaluator valueExpr) + { + _fieldName = fieldName; + _valueExpr = valueExpr; + } + + public void Evaluate(DateTimeEx dateTime, EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + int? ovalue = CalendarOpUtil.GetInt(_valueExpr, eventsPerStream, isNewData, context); + if (ovalue == null) + { + return; + } + + var value = ovalue.Value; + + switch (_fieldName) + { + case CalendarFieldEnum.MILLISEC: + dateTime.Set(dateTime.Year, dateTime.Month, dateTime.Day, dateTime.Hour, dateTime.Minute, dateTime.Second, value); + break; + case CalendarFieldEnum.SECOND: + dateTime.Set(dateTime.Year, dateTime.Month, dateTime.Day, dateTime.Hour, dateTime.Minute, value, dateTime.Millisecond); + break; + case CalendarFieldEnum.MINUTE: + dateTime.Set(dateTime.Year, dateTime.Month, dateTime.Day, dateTime.Hour, value, dateTime.Second, dateTime.Millisecond); + break; + case CalendarFieldEnum.HOUR: + dateTime.Set(dateTime.Year, dateTime.Month, dateTime.Day, value, dateTime.Minute, dateTime.Second, dateTime.Millisecond); + break; + case CalendarFieldEnum.DAY: + dateTime.Set(dateTime.Year, dateTime.Month, value, dateTime.Hour, dateTime.Minute, dateTime.Second, dateTime.Millisecond); + break; + case CalendarFieldEnum.MONTH: + dateTime.Set(dateTime.Year, value, dateTime.Day, dateTime.Hour, dateTime.Minute, dateTime.Second, dateTime.Millisecond); + break; + case CalendarFieldEnum.YEAR: + dateTime.Set(value, dateTime.Month, dateTime.Day, dateTime.Hour, dateTime.Minute, dateTime.Second, dateTime.Millisecond); + break; + case CalendarFieldEnum.WEEK: + dateTime.MoveToWeek(value); + break; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/calop/CalendarOpUtil.cs b/NEsper.Core/NEsper.Core/epl/datetime/calop/CalendarOpUtil.cs new file mode 100755 index 000000000..675a5e0c8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/calop/CalendarOpUtil.cs @@ -0,0 +1,49 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.datetime.calop +{ + public class CalendarOpUtil + { + public static int? GetInt(ExprEvaluator expr, EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + Object result = expr.Evaluate(new EvaluateParams(eventsPerStream, isNewData, context)); + if (result == null) { + return null; + } + return (int?) result; + } + + public static CalendarFieldEnum GetEnum(String methodName, ExprNode exprNode) + { + var message = "Date-time enumeration method '" + methodName + "'"; + var validFieldNames = "valid field names are '" + Enum.GetNames(typeof(CalendarFieldEnum)) + "'"; + + if (!ExprNodeUtility.IsConstantValueExpr(exprNode)) { + throw new ExprValidationException(message + " requires a constant string-type parameter as its first parameter, " + validFieldNames); + } + + var fieldname = (String) exprNode.ExprEvaluator.Evaluate(new EvaluateParams(null, true, null)); + try + { + return CalendarFieldEnumExtensions.FromString(fieldname); + } + catch (ArgumentException) + { + throw new ExprValidationException( + message + " datetime-field name '" + fieldname + "' is not recognized, " + validFieldNames); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/calop/CalendarOpWithDate.cs b/NEsper.Core/NEsper.Core/epl/datetime/calop/CalendarOpWithDate.cs new file mode 100755 index 000000000..4f554e50b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/calop/CalendarOpWithDate.cs @@ -0,0 +1,61 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.datetime.calop +{ + public class CalendarOpWithDate : CalendarOp + { + private readonly ExprEvaluator _year; + private readonly ExprEvaluator _month; + private readonly ExprEvaluator _day; + + public CalendarOpWithDate(ExprEvaluator year, ExprEvaluator month, ExprEvaluator day) + { + _year = year; + _month = month; + _day = day; + } + + public void Evaluate(DateTimeEx dateTime, EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + int? yearNum = GetInt(_year, eventsPerStream, isNewData, context); + int? monthNum = GetInt(_month, eventsPerStream, isNewData, context); + int? dayNum = GetInt(_day, eventsPerStream, isNewData, context); + Action(dateTime, yearNum, monthNum, dayNum); + } + + public static int? GetInt(ExprEvaluator expr, EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + var result = expr.Evaluate(new EvaluateParams(eventsPerStream, isNewData, context)); + if (result == null) + { + return null; + } + return (int?)result; + } + + private static DateTimeEx Action(DateTimeEx dateTime, int? year, int? month, int? day) + { + return dateTime.Set( + year ?? dateTime.Year, + month ?? dateTime.Month, + day ?? dateTime.Day, + dateTime.Hour, + dateTime.Minute, + dateTime.Second, + dateTime.Millisecond); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/calop/CalendarOpWithMax.cs b/NEsper.Core/NEsper.Core/epl/datetime/calop/CalendarOpWithMax.cs new file mode 100755 index 000000000..de2317a4d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/calop/CalendarOpWithMax.cs @@ -0,0 +1,55 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.datetime.calop +{ + public class CalendarOpWithMax : CalendarOp + { + private readonly CalendarFieldEnum _fieldName; + + public CalendarOpWithMax(CalendarFieldEnum fieldName) + { + _fieldName = fieldName; + } + + public void Evaluate(DateTimeEx dateTime, EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + switch (_fieldName) + { + case CalendarFieldEnum.MILLISEC: + dateTime.Set(dateTime.Year, dateTime.Month, dateTime.Day, dateTime.Hour, dateTime.Minute, dateTime.Second, 999); + break; + case CalendarFieldEnum.SECOND: + dateTime.Set(dateTime.Year, dateTime.Month, dateTime.Day, dateTime.Hour, dateTime.Minute, 59, dateTime.Millisecond); + break; + case CalendarFieldEnum.MINUTE: + dateTime.Set(dateTime.Year, dateTime.Month, dateTime.Day, dateTime.Hour, 59, dateTime.Second, dateTime.Millisecond); + break; + case CalendarFieldEnum.HOUR: + dateTime.Set(dateTime.Year, dateTime.Month, dateTime.Day, 23, dateTime.Minute, dateTime.Second, dateTime.Millisecond); + break; + case CalendarFieldEnum.DAY: + dateTime = dateTime.SetMaximumDay(); + break; + case CalendarFieldEnum.MONTH: + dateTime = dateTime.SetMaximumMonth(); + break; + case CalendarFieldEnum.YEAR: + dateTime.Set(9999, dateTime.Month, dateTime.Day, dateTime.Hour, dateTime.Minute, dateTime.Second, dateTime.Millisecond); + break; + case CalendarFieldEnum.WEEK: + dateTime = dateTime.SetMaximumWeek(); + break; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/calop/CalendarOpWithMin.cs b/NEsper.Core/NEsper.Core/epl/datetime/calop/CalendarOpWithMin.cs new file mode 100755 index 000000000..87453d725 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/calop/CalendarOpWithMin.cs @@ -0,0 +1,57 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.datetime.calop +{ + public class CalendarOpWithMin : CalendarOp + { + private readonly CalendarFieldEnum _fieldName; + + public CalendarOpWithMin(CalendarFieldEnum fieldName) + { + _fieldName = fieldName; + } + + public void Evaluate(DateTimeEx dateTime, EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + switch (_fieldName) + { + case CalendarFieldEnum.MILLISEC: + dateTime.Set(dateTime.Year, dateTime.Month, dateTime.Day, dateTime.Hour, dateTime.Minute, dateTime.Second, 0); + break; + case CalendarFieldEnum.SECOND: + dateTime.Set(dateTime.Year, dateTime.Month, dateTime.Day, dateTime.Hour, dateTime.Minute, 0, dateTime.Millisecond); + break; + case CalendarFieldEnum.MINUTE: + dateTime.Set(dateTime.Year, dateTime.Month, dateTime.Day, dateTime.Hour, 0, dateTime.Second, dateTime.Millisecond); + break; + case CalendarFieldEnum.HOUR: + dateTime.Set(dateTime.Year, dateTime.Month, dateTime.Day, 0, dateTime.Minute, dateTime.Second, dateTime.Millisecond); + break; + case CalendarFieldEnum.DAY: + dateTime.Set(dateTime.Year, dateTime.Month, 1, dateTime.Hour, dateTime.Minute, dateTime.Second, dateTime.Millisecond); + break; + case CalendarFieldEnum.MONTH: + dateTime.Set(dateTime.Year, 1, dateTime.Day, dateTime.Hour, dateTime.Minute, dateTime.Second, dateTime.Millisecond); + break; + case CalendarFieldEnum.YEAR: + dateTime.Set(DateTimeOffset.MinValue.Year, dateTime.Month, dateTime.Day, dateTime.Hour, dateTime.Minute, dateTime.Second, dateTime.Millisecond); + break; + case CalendarFieldEnum.WEEK: + dateTime.SetMinimumWeek(); + break; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/calop/CalendarOpWithTime.cs b/NEsper.Core/NEsper.Core/epl/datetime/calop/CalendarOpWithTime.cs new file mode 100755 index 000000000..ecbaf7dcd --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/calop/CalendarOpWithTime.cs @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.datetime.calop +{ + public class CalendarOpWithTime : CalendarOp + { + private readonly ExprEvaluator _hour; + private readonly ExprEvaluator _min; + private readonly ExprEvaluator _sec; + private readonly ExprEvaluator _msec; + + public CalendarOpWithTime(ExprEvaluator hour, ExprEvaluator min, ExprEvaluator sec, ExprEvaluator msec) + { + _hour = hour; + _min = min; + _sec = sec; + _msec = msec; + } + + public void Evaluate(DateTimeEx dateTime, EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + int? hourNum = CalendarOpWithDate.GetInt(_hour, eventsPerStream, isNewData, context); + int? minNum = CalendarOpWithDate.GetInt(_min, eventsPerStream, isNewData, context); + int? secNum = CalendarOpWithDate.GetInt(_sec, eventsPerStream, isNewData, context); + int? msecNum = CalendarOpWithDate.GetInt(_msec, eventsPerStream, isNewData, context); + Action(dateTime, hourNum, minNum, secNum, msecNum); + } + + private static DateTimeEx Action(DateTimeEx dateTime, int? hour, int? minute, int? second, int? msec) + { + return dateTime.Set( + dateTime.Year, + dateTime.Month, + dateTime.Day, + hour ?? dateTime.Hour, + minute ?? dateTime.Minute, + second ?? dateTime.Second, + msec ?? dateTime.Millisecond); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/calop/DateTimeFieldEnum.cs b/NEsper.Core/NEsper.Core/epl/datetime/calop/DateTimeFieldEnum.cs new file mode 100755 index 000000000..45f5efa38 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/calop/DateTimeFieldEnum.cs @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.datetime.calop +{ + public class DateTimeFieldEnum + { + public const int MILLISEC = 0; + public const int SECOND = 1; + public const int MINUTE = 2; + public const int HOUR = 3; + public const int DAY = 4; + public const int MONTH = 5; + public const int WEEK = 6; + public const int YEAR = 7; + + public const int HOUR_OF_DAY = 3; + public const int DATE = 9; + public const int DAY_OF_MONTH = 4; + public const int AM_PM = 11; + } +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/calop/DateTimeFieldMath.cs b/NEsper.Core/NEsper.Core/epl/datetime/calop/DateTimeFieldMath.cs new file mode 100755 index 000000000..eed5cbeee --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/calop/DateTimeFieldMath.cs @@ -0,0 +1,425 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using com.espertech.esper.compat; + +namespace com.espertech.esper.epl.datetime.calop +{ + public static class DateTimeFieldMath + { + /// + /// Gets the actual minimum. + /// + /// The date time. + /// The field. + /// + public static int GetActualMinimum(this DateTime dateTime, int field) + { + switch (field) + { + case DateTimeFieldEnum.MILLISEC: + return 0; + case DateTimeFieldEnum.SECOND: + return 0; + case DateTimeFieldEnum.MINUTE: + return 0; + case DateTimeFieldEnum.HOUR_OF_DAY: + return 0; + case DateTimeFieldEnum.DATE: + case DateTimeFieldEnum.DAY_OF_MONTH: + return 1; + case DateTimeFieldEnum.MONTH: + return 1; + case DateTimeFieldEnum.YEAR: + return DateTime.MinValue.Year; + default: + throw new ArgumentException("invalid datetime"); + } + } + + /// + /// Gets the actual minimum. + /// + /// The date time. + /// The field. + /// + public static int GetActualMinimum(this DateTimeOffset dateTime, int field) + { + switch (field) + { + case DateTimeFieldEnum.MILLISEC: + return 0; + case DateTimeFieldEnum.SECOND: + return 0; + case DateTimeFieldEnum.MINUTE: + return 0; + case DateTimeFieldEnum.HOUR_OF_DAY: + return 0; + case DateTimeFieldEnum.DATE: + case DateTimeFieldEnum.DAY_OF_MONTH: + return 1; + case DateTimeFieldEnum.MONTH: + return 1; + case DateTimeFieldEnum.YEAR: + return DateTime.MinValue.Year; + default: + throw new ArgumentException("invalid datetime"); + } + } + + /// + /// Gets the actual maximum. + /// + /// The date time. + /// The field. + /// + public static int GetActualMaximum(this DateTime dateTime, int field) + { + switch (field) + { + case DateTimeFieldEnum.MILLISEC: + return 999; + case DateTimeFieldEnum.SECOND: + return 59; + case DateTimeFieldEnum.MINUTE: + return 59; + case DateTimeFieldEnum.HOUR_OF_DAY: + return 23; + case DateTimeFieldEnum.DATE: + case DateTimeFieldEnum.DAY_OF_MONTH: + return dateTime.GetWithMaximumDay().Day; + case DateTimeFieldEnum.MONTH: + return dateTime.GetWithMaximumMonth().Month; + case DateTimeFieldEnum.YEAR: + return DateTime.MaxValue.Year; + default: + throw new ArgumentException("invalid datetime"); + } + } + + /// + /// Gets the actual maximum. + /// + /// The date time. + /// The field. + /// + public static int GetActualMaximum(this DateTimeOffset dateTime, int field) + { + switch (field) + { + case DateTimeFieldEnum.MILLISEC: + return 999; + case DateTimeFieldEnum.SECOND: + return 59; + case DateTimeFieldEnum.MINUTE: + return 59; + case DateTimeFieldEnum.HOUR_OF_DAY: + return 23; + case DateTimeFieldEnum.DATE: + case DateTimeFieldEnum.DAY_OF_MONTH: + return dateTime.GetWithMaximumDay().Day; + case DateTimeFieldEnum.MONTH: + return dateTime.GetWithMaximumMonth().Month; + case DateTimeFieldEnum.YEAR: + return DateTime.MaxValue.Year; + default: + throw new ArgumentException("invalid datetime"); + } + } + + /// + /// Sets the field value. + /// + /// The date time. + /// The field. + /// The value. + /// + public static DateTime SetFieldValue(this DateTime dateTime, int field, int value) + { + switch (field) + { + case DateTimeFieldEnum.MILLISEC: + return new DateTime( + dateTime.Year, + dateTime.Month, + dateTime.Day, + dateTime.Hour, + dateTime.Minute, + dateTime.Second, + value); + case DateTimeFieldEnum.SECOND: + return new DateTime( + dateTime.Year, + dateTime.Month, + dateTime.Day, + dateTime.Hour, + dateTime.Minute, + value, + dateTime.Millisecond); + case DateTimeFieldEnum.MINUTE: + return new DateTime( + dateTime.Year, + dateTime.Month, + dateTime.Day, + dateTime.Hour, + value, + dateTime.Second, + dateTime.Millisecond); + case DateTimeFieldEnum.HOUR_OF_DAY: + return new DateTime( + dateTime.Year, + dateTime.Month, + dateTime.Day, + value, + dateTime.Minute, + dateTime.Second, + dateTime.Millisecond); + case DateTimeFieldEnum.DATE: + case DateTimeFieldEnum.DAY_OF_MONTH: + return new DateTime( + dateTime.Year, + dateTime.Month, + value, + dateTime.Hour, + dateTime.Minute, + dateTime.Second, + dateTime.Millisecond); + case DateTimeFieldEnum.MONTH: + return new DateTime( + dateTime.Year, + value, + dateTime.Day, + dateTime.Hour, + dateTime.Minute, + dateTime.Second, + dateTime.Millisecond); + case DateTimeFieldEnum.YEAR: + return new DateTime( + value, + dateTime.Month, + dateTime.Day, + dateTime.Hour, + dateTime.Minute, + dateTime.Second, + dateTime.Millisecond); + default: + throw new ArgumentException("invalid datetime"); + } + } + + /// + /// Sets the field value. + /// + /// The date time. + /// The field. + /// The value. + /// + public static DateTimeOffset SetFieldValue(this DateTimeOffset dateTime, int field, int value, TimeZoneInfo timeZone) + { + switch (field) + { + case DateTimeFieldEnum.MILLISEC: + return DateTimeOffsetHelper.CreateDateTime( + dateTime.Year, + dateTime.Month, + dateTime.Day, + dateTime.Hour, + dateTime.Minute, + dateTime.Second, + value, + timeZone); + case DateTimeFieldEnum.SECOND: + return DateTimeOffsetHelper.CreateDateTime( + dateTime.Year, + dateTime.Month, + dateTime.Day, + dateTime.Hour, + dateTime.Minute, + value, + dateTime.Millisecond, + timeZone); + case DateTimeFieldEnum.MINUTE: + return DateTimeOffsetHelper.CreateDateTime( + dateTime.Year, + dateTime.Month, + dateTime.Day, + dateTime.Hour, + value, + dateTime.Second, + dateTime.Millisecond, + timeZone); + case DateTimeFieldEnum.HOUR_OF_DAY: + return DateTimeOffsetHelper.CreateDateTime( + dateTime.Year, + dateTime.Month, + dateTime.Day, + value, + dateTime.Minute, + dateTime.Second, + dateTime.Millisecond, + timeZone); + case DateTimeFieldEnum.DATE: + case DateTimeFieldEnum.DAY_OF_MONTH: + return DateTimeOffsetHelper.CreateDateTime( + dateTime.Year, + dateTime.Month, + value, + dateTime.Hour, + dateTime.Minute, + dateTime.Second, + dateTime.Millisecond, + timeZone); + case DateTimeFieldEnum.MONTH: + return DateTimeOffsetHelper.CreateDateTime( + dateTime.Year, + value, + dateTime.Day, + dateTime.Hour, + dateTime.Minute, + dateTime.Second, + dateTime.Millisecond, + timeZone); + case DateTimeFieldEnum.YEAR: + return DateTimeOffsetHelper.CreateDateTime( + value, + dateTime.Month, + dateTime.Day, + dateTime.Hour, + dateTime.Minute, + dateTime.Second, + dateTime.Millisecond, + timeZone); + default: + throw new ArgumentException("invalid datetime"); + } + } + + /// + /// Gets the field value. + /// + /// The date time. + /// The field. + /// + public static int GetFieldValue(this DateTime dateTime, int field) + { + switch (field) + { + case DateTimeFieldEnum.MILLISEC: + return dateTime.Millisecond; + case DateTimeFieldEnum.SECOND: + return dateTime.Second; + case DateTimeFieldEnum.MINUTE: + return dateTime.Minute; + case DateTimeFieldEnum.HOUR_OF_DAY: + return dateTime.Hour; + case DateTimeFieldEnum.DATE: + case DateTimeFieldEnum.DAY_OF_MONTH: + return dateTime.Day; + case DateTimeFieldEnum.MONTH: + return dateTime.Month; + case DateTimeFieldEnum.YEAR: + return dateTime.Year; + default: + throw new ArgumentException("invalid datetime"); + } + } + + /// + /// Gets the field value. + /// + /// The date time. + /// The field. + /// + public static int GetFieldValue(this DateTimeOffset dateTime, int field) + { + switch (field) + { + case DateTimeFieldEnum.MILLISEC: + return dateTime.Millisecond; + case DateTimeFieldEnum.SECOND: + return dateTime.Second; + case DateTimeFieldEnum.MINUTE: + return dateTime.Minute; + case DateTimeFieldEnum.HOUR_OF_DAY: + return dateTime.Hour; + case DateTimeFieldEnum.DATE: + case DateTimeFieldEnum.DAY_OF_MONTH: + return dateTime.Day; + case DateTimeFieldEnum.MONTH: + return dateTime.Month; + case DateTimeFieldEnum.YEAR: + return dateTime.Year; + default: + throw new ArgumentException("invalid datetime"); + } + } + + /// + /// Adds using field to indicate which datetime field to add to. + /// + /// The date time. + /// The field. + /// The amount. + /// + public static DateTime AddUsingField(this DateTime dateTime, int field, int amount) + { + switch (field) + { + case DateTimeFieldEnum.MILLISEC: + return dateTime.AddMilliseconds(amount); + case DateTimeFieldEnum.SECOND: + return dateTime.AddSeconds(amount); + case DateTimeFieldEnum.MINUTE: + return dateTime.AddMinutes(amount); + case DateTimeFieldEnum.HOUR_OF_DAY: + return dateTime.AddHours(amount); + case DateTimeFieldEnum.DATE: + case DateTimeFieldEnum.DAY_OF_MONTH: + return dateTime.AddDays(amount); + case DateTimeFieldEnum.MONTH: + return dateTime.AddMonths(amount); + case DateTimeFieldEnum.YEAR: + return dateTime.AddYears(amount); + default: + throw new ArgumentException("invalid datetime"); + } + } + + /// + /// Adds using field to indicate which datetime field to add to. + /// + /// The date time. + /// The field. + /// The amount. + /// + public static DateTimeOffset AddUsingField(this DateTimeOffset dateTime, int field, int amount) + { + switch (field) + { + case DateTimeFieldEnum.MILLISEC: + return dateTime.AddMilliseconds(amount); + case DateTimeFieldEnum.SECOND: + return dateTime.AddSeconds(amount); + case DateTimeFieldEnum.MINUTE: + return dateTime.AddMinutes(amount); + case DateTimeFieldEnum.HOUR_OF_DAY: + return dateTime.AddHours(amount); + case DateTimeFieldEnum.DATE: + case DateTimeFieldEnum.DAY_OF_MONTH: + return dateTime.AddDays(amount); + case DateTimeFieldEnum.MONTH: + return dateTime.AddMonthsLikeJava(amount); + case DateTimeFieldEnum.YEAR: + return dateTime.AddYears(amount); + default: + throw new ArgumentException("invalid datetime"); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/eval/DatetimeLongCoercer.cs b/NEsper.Core/NEsper.Core/epl/datetime/eval/DatetimeLongCoercer.cs new file mode 100755 index 000000000..cdd42053d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/eval/DatetimeLongCoercer.cs @@ -0,0 +1,16 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.datetime.eval +{ + public interface DatetimeLongCoercer { + long Coerce(Object value); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/eval/DatetimeLongCoercerDateTime.cs b/NEsper.Core/NEsper.Core/epl/datetime/eval/DatetimeLongCoercerDateTime.cs new file mode 100755 index 000000000..cdf268435 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/eval/DatetimeLongCoercerDateTime.cs @@ -0,0 +1,22 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; + +namespace com.espertech.esper.epl.datetime.eval +{ + public class DatetimeLongCoercerDateTime : DatetimeLongCoercer + { + public long Coerce(Object value) + { + return value.AsDateTimeOffset().TimeInMillis(); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/datetime/eval/DatetimeLongCoercerDateTimeEx.cs b/NEsper.Core/NEsper.Core/epl/datetime/eval/DatetimeLongCoercerDateTimeEx.cs new file mode 100755 index 000000000..a7a040a7e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/eval/DatetimeLongCoercerDateTimeEx.cs @@ -0,0 +1,22 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; + +namespace com.espertech.esper.epl.datetime.eval +{ + public class DatetimeLongCoercerDateTimeEx : DatetimeLongCoercer + { + public long Coerce(Object value) + { + return ((DateTimeEx) value).TimeInMillis; + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/datetime/eval/DatetimeLongCoercerFactory.cs b/NEsper.Core/NEsper.Core/epl/datetime/eval/DatetimeLongCoercerFactory.cs new file mode 100755 index 000000000..3a370fab2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/eval/DatetimeLongCoercerFactory.cs @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.datetime.eval +{ + public class DatetimeLongCoercerFactory + { + private static readonly DatetimeLongCoercerLong DATETIME_LONG_COERCER_LONG = new DatetimeLongCoercerLong(); + private static readonly DatetimeLongCoercerDateTime DATETIME_LONG_COERCER_DATETIME = new DatetimeLongCoercerDateTime(); + private static readonly DatetimeLongCoercerDateTime DATETIME_LONG_COERCER_DATETIME_OFFSET = new DatetimeLongCoercerDateTime(); + private static readonly DatetimeLongCoercerDateTimeEx DATETIME_LONG_COERCER_DTX = new DatetimeLongCoercerDateTimeEx(); + + public static DatetimeLongCoercer GetCoercer(Type clazz, TimeZoneInfo timeZone) + { + if (TypeHelper.IsSubclassOrImplementsInterface(clazz, typeof (DateTime))) + { + return DATETIME_LONG_COERCER_DATETIME; + } + if (TypeHelper.IsSubclassOrImplementsInterface(clazz, typeof (DateTimeOffset))) + { + return DATETIME_LONG_COERCER_DATETIME_OFFSET; + } + if (TypeHelper.IsSubclassOrImplementsInterface(clazz, typeof (DateTimeEx))) + { + return DATETIME_LONG_COERCER_DTX; + } + return DATETIME_LONG_COERCER_LONG; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/datetime/eval/DatetimeLongCoercerLong.cs b/NEsper.Core/NEsper.Core/epl/datetime/eval/DatetimeLongCoercerLong.cs new file mode 100755 index 000000000..1dc1410a0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/eval/DatetimeLongCoercerLong.cs @@ -0,0 +1,33 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; + +namespace com.espertech.esper.epl.datetime.eval +{ + public class DatetimeLongCoercerLong : DatetimeLongCoercer + { + public long Coerce(Object value) + { + if (value is long) + return ((long) value); + if (value is int) + return ((int) value); + if (value is DateTime) + return ((DateTime) value).UtcMillis(); + if (value is DateTimeOffset) + return ((DateTimeOffset) value).TimeInMillis(); + if (value is DateTimeEx) + return ((DateTimeEx) value).TimeInMillis; + + throw new ArgumentException("invalid value for datetime", "value"); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/eval/DatetimeMethodEnum.cs b/NEsper.Core/NEsper.Core/epl/datetime/eval/DatetimeMethodEnum.cs new file mode 100755 index 000000000..b7719849d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/eval/DatetimeMethodEnum.cs @@ -0,0 +1,300 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.methodbase; + +namespace com.espertech.esper.epl.datetime.eval +{ + public enum DatetimeMethodEnum + { + // datetime ops + WITHTIME, + WITHDATE, + PLUS, + MINUS, + WITHMAX, + WITHMIN, + SET, + ROUNDCEILING, + ROUNDFLOOR, + ROUNDHALF, + + // reformat ops + GET, + FORMAT, + TOCALENDAR, + TODATE, + TOMILLISEC, + GETMINUTEOFHOUR, + GETMONTHOFYEAR, + GETDAYOFMONTH, + GETDAYOFWEEK, + GETDAYOFYEAR, + GETERA, + GETHOUROFDAY, + GETMILLISOFSECOND, + GETSECONDOFMINUTE, + GETWEEKYEAR, + GETYEAR, + BETWEEN, + + // interval ops + BEFORE, + AFTER, + COINCIDES, + DURING, + INCLUDES, + FINISHES, + FINISHEDBY, + MEETS, + METBY, + OVERLAPS, + OVERLAPPEDBY, + STARTS, + STARTEDBY + } + + public class DatetimeMethodEnumMetaData + { + + public string NameCamel { get; private set; } + + public OpFactory OpFactory { get; private set; } + + public DotMethodFP[] Footprints { get; private set; } + + internal DatetimeMethodEnumMetaData(string nameCamel, OpFactory opFactory, DotMethodFP[] footprints) + { + NameCamel = nameCamel; + OpFactory = opFactory; + Footprints = footprints; + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + /// 2 + public override string ToString() + { + return string.Format("NameCamel: {0}, OpFactory: {1}, Footprints: {2}", NameCamel, OpFactory, Footprints); + } + } + + public static class DatetimeMethodEnumExtensions + { + private static readonly IDictionary MetaDataTable; + + static DatetimeMethodEnumExtensions() + { + MetaDataTable = new Dictionary(); + MetaDataTable.Put( + DatetimeMethodEnum.WITHTIME, + new DatetimeMethodEnumMetaData("withTime", DatetimeMethodEnumStatics.CALENDAR_OP_FACTORY, + DatetimeMethodEnumParams.WITHTIME)); + MetaDataTable.Put( + DatetimeMethodEnum.WITHDATE, + new DatetimeMethodEnumMetaData("withDate", DatetimeMethodEnumStatics.CALENDAR_OP_FACTORY, + DatetimeMethodEnumParams.WITHDATE)); + MetaDataTable.Put( + DatetimeMethodEnum.PLUS, + new DatetimeMethodEnumMetaData("plus", DatetimeMethodEnumStatics.CALENDAR_OP_FACTORY, + DatetimeMethodEnumParams.PLUSMINUS)); + MetaDataTable.Put( + DatetimeMethodEnum.MINUS, + new DatetimeMethodEnumMetaData("minus", DatetimeMethodEnumStatics.CALENDAR_OP_FACTORY, + DatetimeMethodEnumParams.PLUSMINUS)); + MetaDataTable.Put( + DatetimeMethodEnum.WITHMAX, + new DatetimeMethodEnumMetaData("withMax", DatetimeMethodEnumStatics.CALENDAR_OP_FACTORY, + DatetimeMethodEnumParams.CALFIELD)); + MetaDataTable.Put( + DatetimeMethodEnum.WITHMIN, + new DatetimeMethodEnumMetaData("withMin", DatetimeMethodEnumStatics.CALENDAR_OP_FACTORY, + DatetimeMethodEnumParams.CALFIELD)); + MetaDataTable.Put( + DatetimeMethodEnum.SET, + new DatetimeMethodEnumMetaData("set", DatetimeMethodEnumStatics.CALENDAR_OP_FACTORY, + DatetimeMethodEnumParams.CALFIELD_PLUS_INT)); + MetaDataTable.Put( + DatetimeMethodEnum.ROUNDCEILING, + new DatetimeMethodEnumMetaData("roundCeiling", DatetimeMethodEnumStatics.CALENDAR_OP_FACTORY, + DatetimeMethodEnumParams.CALFIELD)); + MetaDataTable.Put( + DatetimeMethodEnum.ROUNDFLOOR, + new DatetimeMethodEnumMetaData("roundFloor", DatetimeMethodEnumStatics.CALENDAR_OP_FACTORY, + DatetimeMethodEnumParams.CALFIELD)); + MetaDataTable.Put( + DatetimeMethodEnum.ROUNDHALF, + new DatetimeMethodEnumMetaData("roundHalf", DatetimeMethodEnumStatics.CALENDAR_OP_FACTORY, + DatetimeMethodEnumParams.CALFIELD)); + MetaDataTable.Put( + DatetimeMethodEnum.GET, + new DatetimeMethodEnumMetaData("get", DatetimeMethodEnumStatics.REFORMAT_OP_FACTORY, + DatetimeMethodEnumParams.CALFIELD)); + MetaDataTable.Put( + DatetimeMethodEnum.FORMAT, + new DatetimeMethodEnumMetaData("format", DatetimeMethodEnumStatics.REFORMAT_OP_FACTORY, + DatetimeMethodEnumParams.NOPARAM)); + MetaDataTable.Put( + DatetimeMethodEnum.TOCALENDAR, + new DatetimeMethodEnumMetaData("toCalendar", DatetimeMethodEnumStatics.REFORMAT_OP_FACTORY, + DatetimeMethodEnumParams.NOPARAM)); + MetaDataTable.Put( + DatetimeMethodEnum.TODATE, + new DatetimeMethodEnumMetaData("toDate", DatetimeMethodEnumStatics.REFORMAT_OP_FACTORY, + DatetimeMethodEnumParams.NOPARAM)); + MetaDataTable.Put( + DatetimeMethodEnum.TOMILLISEC, + new DatetimeMethodEnumMetaData("toMillisec", DatetimeMethodEnumStatics.REFORMAT_OP_FACTORY, + DatetimeMethodEnumParams.NOPARAM)); + MetaDataTable.Put( + DatetimeMethodEnum.GETMINUTEOFHOUR, + new DatetimeMethodEnumMetaData("getMinuteOfHour", DatetimeMethodEnumStatics.REFORMAT_OP_FACTORY, + DatetimeMethodEnumParams.NOPARAM)); + MetaDataTable.Put( + DatetimeMethodEnum.GETMONTHOFYEAR, + new DatetimeMethodEnumMetaData("getMonthOfYear", DatetimeMethodEnumStatics.REFORMAT_OP_FACTORY, + DatetimeMethodEnumParams.NOPARAM)); + MetaDataTable.Put( + DatetimeMethodEnum.GETDAYOFMONTH, + new DatetimeMethodEnumMetaData("getDayOfMonth", DatetimeMethodEnumStatics.REFORMAT_OP_FACTORY, + DatetimeMethodEnumParams.NOPARAM)); + MetaDataTable.Put( + DatetimeMethodEnum.GETDAYOFWEEK, + new DatetimeMethodEnumMetaData("getDayOfWeek", DatetimeMethodEnumStatics.REFORMAT_OP_FACTORY, + DatetimeMethodEnumParams.NOPARAM)); + MetaDataTable.Put( + DatetimeMethodEnum.GETDAYOFYEAR, + new DatetimeMethodEnumMetaData("getDayOfYear", DatetimeMethodEnumStatics.REFORMAT_OP_FACTORY, + DatetimeMethodEnumParams.NOPARAM)); + MetaDataTable.Put( + DatetimeMethodEnum.GETERA, + new DatetimeMethodEnumMetaData("getEra", DatetimeMethodEnumStatics.REFORMAT_OP_FACTORY, + DatetimeMethodEnumParams.NOPARAM)); + MetaDataTable.Put( + DatetimeMethodEnum.GETHOUROFDAY, + new DatetimeMethodEnumMetaData("getHourOfDay", DatetimeMethodEnumStatics.REFORMAT_OP_FACTORY, + DatetimeMethodEnumParams.NOPARAM)); + MetaDataTable.Put( + DatetimeMethodEnum.GETMILLISOFSECOND, + new DatetimeMethodEnumMetaData("getMillisOfSecond", DatetimeMethodEnumStatics.REFORMAT_OP_FACTORY, + DatetimeMethodEnumParams.NOPARAM)); + MetaDataTable.Put( + DatetimeMethodEnum.GETSECONDOFMINUTE, + new DatetimeMethodEnumMetaData("getSecondOfMinute", DatetimeMethodEnumStatics.REFORMAT_OP_FACTORY, + DatetimeMethodEnumParams.NOPARAM)); + MetaDataTable.Put( + DatetimeMethodEnum.GETWEEKYEAR, + new DatetimeMethodEnumMetaData("getWeekyear", DatetimeMethodEnumStatics.REFORMAT_OP_FACTORY, + DatetimeMethodEnumParams.NOPARAM)); + MetaDataTable.Put( + DatetimeMethodEnum.GETYEAR, + new DatetimeMethodEnumMetaData("getYear", DatetimeMethodEnumStatics.REFORMAT_OP_FACTORY, + DatetimeMethodEnumParams.NOPARAM)); + MetaDataTable.Put( + DatetimeMethodEnum.BETWEEN, + new DatetimeMethodEnumMetaData("between", DatetimeMethodEnumStatics.REFORMAT_OP_FACTORY, + DatetimeMethodEnumParams.BETWEEN)); + MetaDataTable.Put( + DatetimeMethodEnum.BEFORE, + new DatetimeMethodEnumMetaData("before", DatetimeMethodEnumStatics.INTERVAL_OP_FACTORY, + DatetimeMethodEnumParams.INTERVAL_BEFORE_AFTER)); + MetaDataTable.Put( + DatetimeMethodEnum.AFTER, + new DatetimeMethodEnumMetaData("after", DatetimeMethodEnumStatics.INTERVAL_OP_FACTORY, + DatetimeMethodEnumParams.INTERVAL_BEFORE_AFTER)); + MetaDataTable.Put( + DatetimeMethodEnum.COINCIDES, + new DatetimeMethodEnumMetaData("coincides", DatetimeMethodEnumStatics.INTERVAL_OP_FACTORY, + DatetimeMethodEnumParams.INTERVAL_COINCIDES)); + MetaDataTable.Put( + DatetimeMethodEnum.DURING, + new DatetimeMethodEnumMetaData("during", DatetimeMethodEnumStatics.INTERVAL_OP_FACTORY, + DatetimeMethodEnumParams.INTERVAL_DURING_INCLUDES)); + MetaDataTable.Put( + DatetimeMethodEnum.INCLUDES, + new DatetimeMethodEnumMetaData("includes", DatetimeMethodEnumStatics.INTERVAL_OP_FACTORY, + DatetimeMethodEnumParams.INTERVAL_DURING_INCLUDES)); + MetaDataTable.Put( + DatetimeMethodEnum.FINISHES, + new DatetimeMethodEnumMetaData("finishes", DatetimeMethodEnumStatics.INTERVAL_OP_FACTORY, + DatetimeMethodEnumParams.INTERVAL_FINISHES_FINISHEDBY)); + MetaDataTable.Put( + DatetimeMethodEnum.FINISHEDBY, + new DatetimeMethodEnumMetaData("finishedBy", DatetimeMethodEnumStatics.INTERVAL_OP_FACTORY, + DatetimeMethodEnumParams.INTERVAL_FINISHES_FINISHEDBY)); + MetaDataTable.Put( + DatetimeMethodEnum.MEETS, + new DatetimeMethodEnumMetaData("meets", DatetimeMethodEnumStatics.INTERVAL_OP_FACTORY, + DatetimeMethodEnumParams.INTERVAL_MEETS_METBY)); + MetaDataTable.Put( + DatetimeMethodEnum.METBY, + new DatetimeMethodEnumMetaData("metBy", DatetimeMethodEnumStatics.INTERVAL_OP_FACTORY, + DatetimeMethodEnumParams.INTERVAL_MEETS_METBY)); + MetaDataTable.Put( + DatetimeMethodEnum.OVERLAPS, + new DatetimeMethodEnumMetaData("overlaps", DatetimeMethodEnumStatics.INTERVAL_OP_FACTORY, + DatetimeMethodEnumParams.INTERVAL_DURING_OVERLAPS_OVERLAPBY)); + MetaDataTable.Put( + DatetimeMethodEnum.OVERLAPPEDBY, + new DatetimeMethodEnumMetaData("overlappedBy", DatetimeMethodEnumStatics.INTERVAL_OP_FACTORY, + DatetimeMethodEnumParams.INTERVAL_DURING_OVERLAPS_OVERLAPBY)); + MetaDataTable.Put( + DatetimeMethodEnum.STARTS, + new DatetimeMethodEnumMetaData("starts", DatetimeMethodEnumStatics.INTERVAL_OP_FACTORY, + DatetimeMethodEnumParams.INTERVAL_STARTS_STARTEDBY)); + MetaDataTable.Put( + DatetimeMethodEnum.STARTEDBY, + new DatetimeMethodEnumMetaData("startedBy", DatetimeMethodEnumStatics.INTERVAL_OP_FACTORY, + DatetimeMethodEnumParams.INTERVAL_STARTS_STARTEDBY)); + } + + public static DotMethodFP[] Footprints(this DatetimeMethodEnum datetimeMethodEnum) + { + var metaData = MetaData(datetimeMethodEnum); + if (metaData != null) + return metaData.Footprints; + + throw new ArgumentException(); + } + + public static DatetimeMethodEnumMetaData MetaData(this DatetimeMethodEnum datetimeMethodEnum) + { + return MetaDataTable.Get(datetimeMethodEnum); + } + + public static bool IsDateTimeMethod(this string name) + { + name = name.ToLower(); + return MetaDataTable.Values.Any(metaData => metaData.NameCamel.ToLower() == name); + } + + public static DatetimeMethodEnum FromName(string name) + { + name = name.ToLower(); + + foreach (var keyValuePair in MetaDataTable) + { + if (keyValuePair.Value.NameCamel.ToLower() == name) + { + return keyValuePair.Key; + } + } + + throw new ArgumentException("Enumeration identified by name \"" + name + "\" was not found"); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/eval/DatetimeMethodEnumParams.cs b/NEsper.Core/NEsper.Core/epl/datetime/eval/DatetimeMethodEnumParams.cs new file mode 100755 index 000000000..3be9caff5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/eval/DatetimeMethodEnumParams.cs @@ -0,0 +1,150 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client.util; +using com.espertech.esper.epl.methodbase; + +namespace com.espertech.esper.epl.datetime.eval +{ + public class DatetimeMethodEnumParams { + + public static readonly DotMethodFP[] WITHTIME = new DotMethodFP[] { + new DotMethodFP(DotMethodFPInputEnum.SCALAR_ANY, + new DotMethodFPParam("an integer-type hour", DotMethodFPParamTypeEnum.SPECIFIC, typeof(int)), + new DotMethodFPParam("an integer-type minute", DotMethodFPParamTypeEnum.SPECIFIC, typeof(int)), + new DotMethodFPParam("an integer-type second", DotMethodFPParamTypeEnum.SPECIFIC, typeof(int)), + new DotMethodFPParam("an integer-type millis", DotMethodFPParamTypeEnum.SPECIFIC, typeof(int))) + }; + + public static readonly DotMethodFP[] WITHDATE = new DotMethodFP[] { + new DotMethodFP(DotMethodFPInputEnum.SCALAR_ANY, + new DotMethodFPParam("an integer-type year", DotMethodFPParamTypeEnum.SPECIFIC, typeof(int)), + new DotMethodFPParam("an integer-type month", DotMethodFPParamTypeEnum.SPECIFIC, typeof(int)), + new DotMethodFPParam("an integer-type day", DotMethodFPParamTypeEnum.SPECIFIC, typeof(int))) + }; + + public static readonly DotMethodFP[] PLUSMINUS = new DotMethodFP[] { + new DotMethodFP(DotMethodFPInputEnum.SCALAR_ANY, + new DotMethodFPParam(0, "a numeric-type millisecond", DotMethodFPParamTypeEnum.NUMERIC)), + new DotMethodFP(DotMethodFPInputEnum.SCALAR_ANY, + new DotMethodFPParam("a time period", DotMethodFPParamTypeEnum.SPECIFIC, typeof(TimePeriod))) + }; + + public static readonly DotMethodFP[] CALFIELD = new DotMethodFP[] { + new DotMethodFP(DotMethodFPInputEnum.SCALAR_ANY, + new DotMethodFPParam("a string-type calendar field name", DotMethodFPParamTypeEnum.SPECIFIC, typeof(String))), + }; + + public static readonly DotMethodFP[] CALFIELD_PLUS_INT = new DotMethodFP[] { + new DotMethodFP(DotMethodFPInputEnum.SCALAR_ANY, + new DotMethodFPParam("a string-type calendar field name", DotMethodFPParamTypeEnum.SPECIFIC, typeof(String)), + new DotMethodFPParam("an integer-type value", DotMethodFPParamTypeEnum.SPECIFIC, typeof(int))), + }; + + public static readonly DotMethodFP[] NOPARAM = new DotMethodFP[] { + new DotMethodFP(DotMethodFPInputEnum.SCALAR_ANY) + }; + + public static readonly DotMethodFP[] BETWEEN = new DotMethodFP[] { + new DotMethodFP(DotMethodFPInputEnum.SCALAR_ANY, + new DotMethodFPParam("a date-time type", DotMethodFPParamTypeEnum.DATETIME, null), + new DotMethodFPParam("a date-time type", DotMethodFPParamTypeEnum.DATETIME, null)), + new DotMethodFP(DotMethodFPInputEnum.SCALAR_ANY, + new DotMethodFPParam("a date-time type", DotMethodFPParamTypeEnum.DATETIME, null), + new DotMethodFPParam("a date-time type", DotMethodFPParamTypeEnum.DATETIME, null), + new DotMethodFPParam("bool", DotMethodFPParamTypeEnum.BOOLEAN, null), + new DotMethodFPParam("bool", DotMethodFPParamTypeEnum.BOOLEAN, null)), + }; + + /// Interval. + + public static readonly String INPUT_INTERVAL = "timestamp or timestamped-event"; + public static readonly String INPUT_INTERVAL_START = "interval start value"; + public static readonly String INPUT_INTERVAL_FINISHES = "interval finishes value"; + + public static readonly DotMethodFP[] INTERVAL_BEFORE_AFTER = new DotMethodFP[] { + new DotMethodFP(DotMethodFPInputEnum.SCALAR_ANY, + new DotMethodFPParam(INPUT_INTERVAL, DotMethodFPParamTypeEnum.ANY, null)), + new DotMethodFP(DotMethodFPInputEnum.SCALAR_ANY, + new DotMethodFPParam(INPUT_INTERVAL, DotMethodFPParamTypeEnum.ANY, null), + new DotMethodFPParam(INPUT_INTERVAL_START, DotMethodFPParamTypeEnum.TIME_PERIOD_OR_SEC, null)), + new DotMethodFP(DotMethodFPInputEnum.SCALAR_ANY, + new DotMethodFPParam(INPUT_INTERVAL, DotMethodFPParamTypeEnum.ANY, null), + new DotMethodFPParam(INPUT_INTERVAL_START, DotMethodFPParamTypeEnum.TIME_PERIOD_OR_SEC, null), + new DotMethodFPParam(INPUT_INTERVAL_FINISHES, DotMethodFPParamTypeEnum.TIME_PERIOD_OR_SEC, null)) + }; + + public static readonly DotMethodFP[] INTERVAL_COINCIDES = new DotMethodFP[] { + new DotMethodFP(DotMethodFPInputEnum.SCALAR_ANY, + new DotMethodFPParam(INPUT_INTERVAL, DotMethodFPParamTypeEnum.ANY, null)), + new DotMethodFP(DotMethodFPInputEnum.SCALAR_ANY, + new DotMethodFPParam(INPUT_INTERVAL, DotMethodFPParamTypeEnum.ANY, null), + new DotMethodFPParam("threshold for start and end value", DotMethodFPParamTypeEnum.TIME_PERIOD_OR_SEC, null)), + new DotMethodFP(DotMethodFPInputEnum.SCALAR_ANY, + new DotMethodFPParam(INPUT_INTERVAL, DotMethodFPParamTypeEnum.ANY, null), + new DotMethodFPParam("threshold for start value", DotMethodFPParamTypeEnum.TIME_PERIOD_OR_SEC, null), + new DotMethodFPParam("threshold for end value", DotMethodFPParamTypeEnum.TIME_PERIOD_OR_SEC, null)) + }; + + public static readonly DotMethodFP[] INTERVAL_DURING_INCLUDES = new DotMethodFP[] { + new DotMethodFP(DotMethodFPInputEnum.SCALAR_ANY, + new DotMethodFPParam(INPUT_INTERVAL, DotMethodFPParamTypeEnum.ANY, null)), + new DotMethodFP(DotMethodFPInputEnum.SCALAR_ANY, + new DotMethodFPParam(INPUT_INTERVAL, DotMethodFPParamTypeEnum.ANY, null), + new DotMethodFPParam("maximum distance interval both start and end", DotMethodFPParamTypeEnum.TIME_PERIOD_OR_SEC, null)), + new DotMethodFP(DotMethodFPInputEnum.SCALAR_ANY, + new DotMethodFPParam(INPUT_INTERVAL, DotMethodFPParamTypeEnum.ANY, null), + new DotMethodFPParam("minimum distance interval both start and end", DotMethodFPParamTypeEnum.TIME_PERIOD_OR_SEC, null), + new DotMethodFPParam("maximum distance interval both start and end", DotMethodFPParamTypeEnum.TIME_PERIOD_OR_SEC, null)), + new DotMethodFP(DotMethodFPInputEnum.SCALAR_ANY, + new DotMethodFPParam(INPUT_INTERVAL, DotMethodFPParamTypeEnum.ANY, null), + new DotMethodFPParam("minimum distance start", DotMethodFPParamTypeEnum.TIME_PERIOD_OR_SEC, null), + new DotMethodFPParam("maximum distance start", DotMethodFPParamTypeEnum.TIME_PERIOD_OR_SEC, null), + new DotMethodFPParam("minimum distance end", DotMethodFPParamTypeEnum.TIME_PERIOD_OR_SEC, null), + new DotMethodFPParam("maximum distance end", DotMethodFPParamTypeEnum.TIME_PERIOD_OR_SEC, null)), + }; + + public static readonly DotMethodFP[] INTERVAL_DURING_OVERLAPS_OVERLAPBY = new DotMethodFP[] { + new DotMethodFP(DotMethodFPInputEnum.SCALAR_ANY, + new DotMethodFPParam(INPUT_INTERVAL, DotMethodFPParamTypeEnum.ANY, null)), + new DotMethodFP(DotMethodFPInputEnum.SCALAR_ANY, + new DotMethodFPParam(INPUT_INTERVAL, DotMethodFPParamTypeEnum.ANY, null), + new DotMethodFPParam("maximum distance interval both start and end", DotMethodFPParamTypeEnum.TIME_PERIOD_OR_SEC, null)), + new DotMethodFP(DotMethodFPInputEnum.SCALAR_ANY, + new DotMethodFPParam(INPUT_INTERVAL, DotMethodFPParamTypeEnum.ANY, null), + new DotMethodFPParam("minimum distance interval both start and end", DotMethodFPParamTypeEnum.TIME_PERIOD_OR_SEC, null), + new DotMethodFPParam("maximum distance interval both start and end", DotMethodFPParamTypeEnum.TIME_PERIOD_OR_SEC, null)), + }; + + public static readonly DotMethodFP[] INTERVAL_FINISHES_FINISHEDBY = new DotMethodFP[] { + new DotMethodFP(DotMethodFPInputEnum.SCALAR_ANY, + new DotMethodFPParam(INPUT_INTERVAL, DotMethodFPParamTypeEnum.ANY, null)), + new DotMethodFP(DotMethodFPInputEnum.SCALAR_ANY, + new DotMethodFPParam(INPUT_INTERVAL, DotMethodFPParamTypeEnum.ANY, null), + new DotMethodFPParam("maximum distance between end timestamps", DotMethodFPParamTypeEnum.TIME_PERIOD_OR_SEC, null)), + }; + + public static readonly DotMethodFP[] INTERVAL_STARTS_STARTEDBY = new DotMethodFP[] { + new DotMethodFP(DotMethodFPInputEnum.SCALAR_ANY, + new DotMethodFPParam(INPUT_INTERVAL, DotMethodFPParamTypeEnum.ANY, null)), + new DotMethodFP(DotMethodFPInputEnum.SCALAR_ANY, + new DotMethodFPParam(INPUT_INTERVAL, DotMethodFPParamTypeEnum.ANY, null), + new DotMethodFPParam("maximum distance between start timestamps", DotMethodFPParamTypeEnum.TIME_PERIOD_OR_SEC, null)), + }; + + public static readonly DotMethodFP[] INTERVAL_MEETS_METBY = new DotMethodFP[] { + new DotMethodFP(DotMethodFPInputEnum.SCALAR_ANY, + new DotMethodFPParam(INPUT_INTERVAL, DotMethodFPParamTypeEnum.ANY, null)), + new DotMethodFP(DotMethodFPInputEnum.SCALAR_ANY, + new DotMethodFPParam(INPUT_INTERVAL, DotMethodFPParamTypeEnum.ANY, null), + new DotMethodFPParam("maximum distance between start and end timestamps", DotMethodFPParamTypeEnum.TIME_PERIOD_OR_SEC, null)), + }; + } +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/eval/DatetimeMethodEnumStatics.cs b/NEsper.Core/NEsper.Core/epl/datetime/eval/DatetimeMethodEnumStatics.cs new file mode 100755 index 000000000..304edc8fd --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/eval/DatetimeMethodEnumStatics.cs @@ -0,0 +1,21 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.datetime.calop; +using com.espertech.esper.epl.datetime.interval; +using com.espertech.esper.epl.datetime.reformatop; + +namespace com.espertech.esper.epl.datetime.eval +{ + public class DatetimeMethodEnumStatics + { + public static readonly OpFactory CALENDAR_OP_FACTORY = new CalendarOpFactory(); + public static readonly OpFactory REFORMAT_OP_FACTORY = new ReformatOpFactory(); + public static readonly OpFactory INTERVAL_OP_FACTORY = new IntervalOpFactory(); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/datetime/eval/ExprDotEvalDT.cs b/NEsper.Core/NEsper.Core/epl/datetime/eval/ExprDotEvalDT.cs new file mode 100755 index 000000000..78b312ac3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/eval/ExprDotEvalDT.cs @@ -0,0 +1,209 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.datetime.calop; +using com.espertech.esper.epl.datetime.eval.reformat; +using com.espertech.esper.epl.datetime.interval; +using com.espertech.esper.epl.datetime.reformatop; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.dot; +using com.espertech.esper.epl.expression.time; +using com.espertech.esper.epl.rettype; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.datetime.eval +{ + public class ExprDotEvalDT : ExprDotEval + { + private readonly EPType _returnType; + private readonly DTLocalEvaluator _evaluator; + + public ExprDotEvalDT( + IList calendarOps, + TimeZoneInfo timeZone, + TimeAbacus timeAbacus, + ReformatOp reformatOp, + IntervalOp intervalOp, + Type inputType, + EventType inputEventType) + { + this._evaluator = GetEvaluator( + calendarOps, timeZone, timeAbacus, inputType, inputEventType, reformatOp, intervalOp); + + if (intervalOp != null) + { + _returnType = EPTypeHelper.SingleValue(typeof (bool?)); + } + else if (reformatOp != null) + { + _returnType = EPTypeHelper.SingleValue(reformatOp.ReturnType); + } + else + { + // only calendar ops + if (inputEventType != null) + { + _returnType = EPTypeHelper.SingleValue( + inputEventType.GetPropertyType(inputEventType.StartTimestampPropertyName)); + } + else + { + _returnType = EPTypeHelper.SingleValue(inputType); + } + } + } + + public EPType TypeInfo + { + get { return _returnType; } + } + + public void Visit(ExprDotEvalVisitor visitor) + { + visitor.VisitDateTime(); + } + + internal DTLocalEvaluator GetEvaluator( + IList calendarOps, + TimeZoneInfo timeZone, + TimeAbacus timeAbacus, + Type inputType, + EventType inputEventType, + ReformatOp reformatOp, + IntervalOp intervalOp) + { + inputType = TypeHelper.GetBoxedType(inputType); + + if (inputEventType == null) + { + if (reformatOp != null) + { + if (TypeHelper.IsSubclassOrImplementsInterface(inputType, typeof (DateTimeEx))) + { + return calendarOps.IsEmpty() + ? (DTLocalEvaluator) new DTLocalEvaluatorDtxReformat(reformatOp) + : (DTLocalEvaluator) new DTLocalEvaluatorDtxOpsReformat(calendarOps, reformatOp); + } + else if (TypeHelper.IsSubclassOrImplementsInterface(inputType, typeof (DateTime?))) + { + return calendarOps.IsEmpty() + ? (DTLocalEvaluator) new DTLocalEvaluatorDateTimeReformat(reformatOp) + : (DTLocalEvaluator) new DTLocalEvaluatorDateTimeOpsReformat(calendarOps, reformatOp, timeZone); + } + else if (TypeHelper.IsSubclassOrImplementsInterface(inputType, typeof (DateTimeOffset?))) + { + return calendarOps.IsEmpty() + ? (DTLocalEvaluator) new DTLocalEvaluatorDtoReformat(reformatOp) + : (DTLocalEvaluator) new DTLocalEvaluatorDtoOpsReformat(calendarOps, reformatOp, timeZone); + } + else if (inputType == typeof(long?)) + { + return calendarOps.IsEmpty() + ? (DTLocalEvaluator) new DTLocalEvaluatorLongReformat(reformatOp) + : (DTLocalEvaluator) new DTLocalEvaluatorLongOpsReformat(calendarOps, reformatOp, timeZone, timeAbacus); + } + } + else if (intervalOp != null) + { + if (TypeHelper.IsSubclassOrImplementsInterface(inputType, typeof (DateTimeEx))) + { + return calendarOps.IsEmpty() + ? (DTLocalEvaluator) new DTLocalEvaluatorDtxInterval(intervalOp) + : (DTLocalEvaluator) new DTLocalEvaluatorDtxOpsInterval(calendarOps, intervalOp, timeZone); + } + else if (TypeHelper.IsSubclassOrImplementsInterface(inputType, typeof (DateTime?))) + { + return calendarOps.IsEmpty() + ? (DTLocalEvaluator) new DTLocalEvaluatorDateTimeInterval(intervalOp) + : (DTLocalEvaluator) new DTLocalEvaluatorDateTimeOpsInterval(calendarOps, intervalOp, timeZone); + } + else if (TypeHelper.IsSubclassOrImplementsInterface(inputType, typeof (DateTimeOffset?))) + { + return calendarOps.IsEmpty() + ? (DTLocalEvaluator) new DTLocalEvaluatorDtoInterval(intervalOp) + : (DTLocalEvaluator) new DTLocalEvaluatorDtoOpsInterval(calendarOps, intervalOp, timeZone); + } + else if (inputType == typeof(long?)) + { + return calendarOps.IsEmpty() + ? (DTLocalEvaluator) new DTLocalEvaluatorLongInterval(intervalOp) + : (DTLocalEvaluator) new DTLocalEvaluatorLongOpsInterval(calendarOps, intervalOp, timeZone, timeAbacus); + } + } + else + { + // only calendar ops, nothing else + if (TypeHelper.IsSubclassOrImplementsInterface(inputType, typeof (DateTimeEx))) + { + return new DTLocalEvaluatorDtxOpsDtx(calendarOps); + } + else if (TypeHelper.IsSubclassOrImplementsInterface(inputType, typeof (DateTime?))) + { + return new DTLocalEvaluatorDtxOpsDateTime(calendarOps, timeZone); + } + else if (TypeHelper.IsSubclassOrImplementsInterface(inputType, typeof (DateTimeOffset?))) + { + return new DTLocalEvaluatorDtxOpsDateTimeOffset(calendarOps, timeZone); + } + else if (inputType == typeof(long?)) + { + return new DTLocalEvaluatorDtxOpsLong(calendarOps, timeZone, timeAbacus); + } + } + throw new ArgumentException("Invalid input type '" + inputType + "'"); + } + + var getter = inputEventType.GetGetter(inputEventType.StartTimestampPropertyName); + var getterResultType = inputEventType.GetPropertyType(inputEventType.StartTimestampPropertyName); + + if (reformatOp != null) + { + var inner = GetEvaluator( + calendarOps, timeZone, timeAbacus, getterResultType, null, reformatOp, null); + return new DTLocalEvaluatorBeanReformat(getter, inner); + } + if (intervalOp == null) + { + // only calendar ops + var inner = GetEvaluator( + calendarOps, timeZone, timeAbacus, getterResultType, null, null, null); + return new DTLocalEvaluatorBeanCalOps(getter, inner); + } + + // have interval ops but no end timestamp + if (inputEventType.EndTimestampPropertyName == null) + { + var inner = GetEvaluator( + calendarOps, timeZone, timeAbacus, getterResultType, null, null, intervalOp); + return new DTLocalEvaluatorBeanIntervalNoEndTS(getter, inner); + } + + // interval ops and have end timestamp + var getterEndTimestamp = inputEventType.GetGetter(inputEventType.EndTimestampPropertyName); + var innerX = + (DTLocalEvaluatorIntervalComp) + GetEvaluator(calendarOps, timeZone, timeAbacus, getterResultType, null, null, intervalOp); + return new DTLocalEvaluatorBeanIntervalWithEnd(getter, getterEndTimestamp, innerX); + } + + public object Evaluate(object target, EvaluateParams evaluateParams) + { + if (target == null) + { + return null; + } + return _evaluator.Evaluate(target, evaluateParams); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/datetime/eval/ExprDotEvalDTFactory.cs b/NEsper.Core/NEsper.Core/epl/datetime/eval/ExprDotEvalDTFactory.cs new file mode 100755 index 000000000..a861d9f5f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/eval/ExprDotEvalDTFactory.cs @@ -0,0 +1,189 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client.util; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.datetime.calop; +using com.espertech.esper.epl.datetime.interval; +using com.espertech.esper.epl.datetime.reformatop; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.dot; +using com.espertech.esper.epl.expression.time; +using com.espertech.esper.epl.methodbase; +using com.espertech.esper.epl.rettype; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.datetime.eval +{ + public class ExprDotEvalDTFactory + { + public static ExprDotEvalDTMethodDesc ValidateMake( + StreamTypeService streamTypeService, + Deque chainSpecStack, + DatetimeMethodEnum dtMethod, + String dtMethodName, + EPType inputType, + IList parameters, + ExprDotNodeFilterAnalyzerInput inputDesc, + TimeZoneInfo timeZone, + TimeAbacus timeAbacus) + { + // verify input + String message = "Date-time enumeration method '" + dtMethodName + + "' requires either a DateTime, DateTimeEx or long value as input or events of an event type that declares a timestamp property"; + if (inputType is EventEPType) + { + if (((EventEPType)inputType).EventType.StartTimestampPropertyName == null) + { + throw new ExprValidationException(message); + } + } + else + { + if (!(inputType is ClassEPType || inputType is NullEPType)) + { + throw new ExprValidationException(message + " but received " + EPTypeHelper.ToTypeDescriptive(inputType)); + } + if (inputType is ClassEPType) + { + ClassEPType classEPType = (ClassEPType)inputType; + if (!TypeHelper.IsDateTime(classEPType.Clazz)) + { + throw new ExprValidationException( + message + " but received " + classEPType.Clazz.GetTypeNameFullyQualPretty()); + } + } + } + + IList calendarOps = new List(); + ReformatOp reformatOp = null; + IntervalOp intervalOp = null; + DatetimeMethodEnum currentMethod = dtMethod; + IList currentParameters = parameters; + String currentMethodName = dtMethodName; + + // drain all calendar ops + ExprDotNodeFilterAnalyzerDesc filterAnalyzerDesc = null; + while (true) + { + + // handle the first one only if its a calendar op + var evaluators = GetEvaluators(currentParameters); + var opFactory = currentMethod.MetaData().OpFactory; + + // compile parameter abstract for validation against available footprints + var footprintProvided = DotMethodUtil.GetProvidedFootprint(currentParameters); + + // validate parameters + DotMethodUtil.ValidateParametersDetermineFootprint( + currentMethod.Footprints(), + DotMethodTypeEnum.DATETIME, + currentMethodName, footprintProvided, + DotMethodInputTypeMatcherImpl.DEFAULT_ALL); + + if (opFactory is CalendarOpFactory) + { + CalendarOp calendarOp = ((CalendarOpFactory)opFactory).GetOp(currentMethod, currentMethodName, currentParameters, evaluators); + calendarOps.Add(calendarOp); + } + else if (opFactory is ReformatOpFactory) + { + reformatOp = ((ReformatOpFactory)opFactory).GetOp(timeZone, timeAbacus, currentMethod, currentMethodName, currentParameters); + + // compile filter analyzer information if there are no calendar ops in the chain + if (calendarOps.IsEmpty()) + { + filterAnalyzerDesc = reformatOp.GetFilterDesc(streamTypeService.EventTypes, currentMethod, currentParameters, inputDesc); + } + else + { + filterAnalyzerDesc = null; + } + } + else if (opFactory is IntervalOpFactory) + { + intervalOp = ((IntervalOpFactory)opFactory).GetOp(streamTypeService, currentMethod, currentMethodName, currentParameters, timeZone, timeAbacus); + + // compile filter analyzer information if there are no calendar ops in the chain + if (calendarOps.IsEmpty()) + { + filterAnalyzerDesc = intervalOp.GetFilterDesc(streamTypeService.EventTypes, currentMethod, currentParameters, inputDesc); + } + else + { + filterAnalyzerDesc = null; + } + } + else + { + throw new IllegalStateException("Invalid op factory class " + opFactory); + } + + // see if there is more + if (chainSpecStack.IsEmpty() || !DatetimeMethodEnumExtensions.IsDateTimeMethod(chainSpecStack.First.Name)) + { + break; + } + + // pull next + var next = chainSpecStack.RemoveFirst(); + currentMethod = DatetimeMethodEnumExtensions.FromName(next.Name); + currentParameters = next.Parameters; + currentMethodName = next.Name; + + if ((reformatOp != null || intervalOp != null)) + { + throw new ExprValidationException("Invalid input for date-time method '" + next.Name + "'"); + } + } + + ExprDotEval dotEval; + EPType returnType; + + dotEval = new ExprDotEvalDT( + calendarOps, timeZone, timeAbacus, reformatOp, intervalOp, + EPTypeHelper.GetClassSingleValued(inputType), + EPTypeHelper.GetEventTypeSingleValued(inputType)); + returnType = dotEval.TypeInfo; + return new ExprDotEvalDTMethodDesc(dotEval, returnType, filterAnalyzerDesc); + } + + private static ExprEvaluator[] GetEvaluators(IList parameters) + { + var inputExpr = new ExprEvaluator[parameters.Count]; + for (int i = 0; i < parameters.Count; i++) + { + ExprNode innerExpr = parameters[i]; + ExprEvaluator inner = innerExpr.ExprEvaluator; + + // Time periods get special attention + if (innerExpr is ExprTimePeriod) + { + + var timePeriod = (ExprTimePeriod)innerExpr; + inputExpr[i] = new ProxyExprEvaluator + { + ProcEvaluate = evaluateParams => timePeriod.EvaluateGetTimePeriod(evaluateParams), + ReturnType = typeof(TimePeriod), + }; + } + else + { + inputExpr[i] = inner; + } + } + return inputExpr; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/eval/ExprDotEvalDTMethodDesc.cs b/NEsper.Core/NEsper.Core/epl/datetime/eval/ExprDotEvalDTMethodDesc.cs new file mode 100755 index 000000000..babcf5f9f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/eval/ExprDotEvalDTMethodDesc.cs @@ -0,0 +1,30 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.dot; +using com.espertech.esper.epl.rettype; + +namespace com.espertech.esper.epl.datetime.eval +{ + public class ExprDotEvalDTMethodDesc + { + public ExprDotEvalDTMethodDesc(ExprDotEval eval, EPType returnType, ExprDotNodeFilterAnalyzerDesc intervalFilterDesc) + { + Eval = eval; + ReturnType = returnType; + IntervalFilterDesc = intervalFilterDesc; + } + + public ExprDotEval Eval { get; private set; } + + public EPType ReturnType { get; private set; } + + public ExprDotNodeFilterAnalyzerDesc IntervalFilterDesc { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/eval/ExprDotNodeFilterAnalyzerDTBetweenDesc.cs b/NEsper.Core/NEsper.Core/epl/datetime/eval/ExprDotNodeFilterAnalyzerDTBetweenDesc.cs new file mode 100755 index 000000000..3e72850bb --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/eval/ExprDotNodeFilterAnalyzerDTBetweenDesc.cs @@ -0,0 +1,46 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.join.plan; +using com.espertech.esper.epl.join.util; + + +namespace com.espertech.esper.epl.datetime.eval +{ + public class ExprDotNodeFilterAnalyzerDTBetweenDesc : ExprDotNodeFilterAnalyzerDesc + { + private readonly EventType[] typesPerStream; + private readonly int targetStreamNum; + private readonly String targetPropertyName; + private readonly ExprNode start; + private readonly ExprNode end; + private readonly bool includeLow; + private readonly bool includeHigh; + + public ExprDotNodeFilterAnalyzerDTBetweenDesc(EventType[] typesPerStream, int targetStreamNum, String targetPropertyName, ExprNode start, ExprNode end, bool includeLow, bool includeHigh) { + this.typesPerStream = typesPerStream; + this.targetStreamNum = targetStreamNum; + this.targetPropertyName = targetPropertyName; + this.start = start; + this.end = end; + this.includeLow = includeLow; + this.includeHigh = includeHigh; + } + + public void Apply(QueryGraph queryGraph) { + ExprIdentNode targetExpr = ExprNodeUtility.GetExprIdentNode(typesPerStream, targetStreamNum, targetPropertyName); + RangeFilterAnalyzer.Apply(targetExpr, start, end, includeLow, includeHigh, false, queryGraph); + } + } + +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/eval/ExprDotNodeFilterAnalyzerDTIntervalDesc.cs b/NEsper.Core/NEsper.Core/epl/datetime/eval/ExprDotNodeFilterAnalyzerDTIntervalDesc.cs new file mode 100755 index 000000000..3af964138 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/eval/ExprDotNodeFilterAnalyzerDTIntervalDesc.cs @@ -0,0 +1,193 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using com.espertech.esper.client; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.join.plan; +using com.espertech.esper.type; + +namespace com.espertech.esper.epl.datetime.eval +{ + public class ExprDotNodeFilterAnalyzerDTIntervalDesc : ExprDotNodeFilterAnalyzerDesc + { + private readonly DatetimeMethodEnum _currentMethod; + private readonly String _parameterEndProp; + private readonly String _parameterStartProp; + private readonly int _parameterStreamNum; + private readonly String _targetEndProp; + private readonly String _targetStartProp; + private readonly int _targetStreamNum; + private readonly EventType[] _typesPerStream; + + public ExprDotNodeFilterAnalyzerDTIntervalDesc(DatetimeMethodEnum currentMethod, + EventType[] typesPerStream, + int targetStreamNum, + String targetStartProp, + String targetEndProp, + int parameterStreamNum, + String parameterStartProp, + String parameterEndProp) + { + _currentMethod = currentMethod; + _typesPerStream = typesPerStream; + _targetStreamNum = targetStreamNum; + _targetStartProp = targetStartProp; + _targetEndProp = targetEndProp; + _parameterStreamNum = parameterStreamNum; + _parameterStartProp = parameterStartProp; + _parameterEndProp = parameterEndProp; + } + + #region ExprDotNodeFilterAnalyzerDesc Members + + public void Apply(QueryGraph queryGraph) + { + if (_targetStreamNum == _parameterStreamNum) + { + return; + } + + ExprIdentNode targetStartExpr = ExprNodeUtility.GetExprIdentNode( + _typesPerStream, _targetStreamNum, _targetStartProp); + ExprIdentNode targetEndExpr = ExprNodeUtility.GetExprIdentNode( + _typesPerStream, _targetStreamNum, _targetEndProp); + ExprIdentNode parameterStartExpr = ExprNodeUtility.GetExprIdentNode( + _typesPerStream, _parameterStreamNum, _parameterStartProp); + ExprIdentNode parameterEndExpr = ExprNodeUtility.GetExprIdentNode( + _typesPerStream, _parameterStreamNum, _parameterEndProp); + + if (targetStartExpr.ExprEvaluator.ReturnType != parameterStartExpr.ExprEvaluator.ReturnType) + { + return; + } + + if (_currentMethod == DatetimeMethodEnum.BEFORE) + { + // a.end < b.start + queryGraph.AddRelationalOpStrict( + _targetStreamNum, targetEndExpr, + _parameterStreamNum, parameterStartExpr, + RelationalOpEnum.LT); + } + else if (_currentMethod == DatetimeMethodEnum.AFTER) + { + // a.start > b.end + queryGraph.AddRelationalOpStrict( + _targetStreamNum, targetStartExpr, + _parameterStreamNum, parameterEndExpr, + RelationalOpEnum.GT); + } + else if (_currentMethod == DatetimeMethodEnum.COINCIDES) + { + // a.startTimestamp = b.startTimestamp and a.endTimestamp = b.endTimestamp + queryGraph.AddStrictEquals(_targetStreamNum, _targetStartProp, targetStartExpr, + _parameterStreamNum, _parameterStartProp, parameterStartExpr); + + var noDuration = _parameterEndProp.Equals(_parameterStartProp) && + _targetEndProp.Equals(_targetStartProp); + if (!noDuration) + { + ExprIdentNode leftEndExpr = ExprNodeUtility.GetExprIdentNode( + _typesPerStream, _targetStreamNum, _targetEndProp); + ExprIdentNode rightEndExpr = ExprNodeUtility.GetExprIdentNode( + _typesPerStream, _parameterStreamNum, _parameterEndProp); + queryGraph.AddStrictEquals( + _targetStreamNum, _targetEndProp, leftEndExpr, _parameterStreamNum, _parameterEndProp, rightEndExpr); + } + } + else if (_currentMethod == DatetimeMethodEnum.DURING || _currentMethod == DatetimeMethodEnum.INCLUDES) + { + // DURING: b.startTimestamp < a.startTimestamp <= a.endTimestamp < b.endTimestamp + // INCLUDES: a.startTimestamp < b.startTimestamp <= b.endTimestamp < a.endTimestamp + RelationalOpEnum relop = _currentMethod == DatetimeMethodEnum.DURING + ? RelationalOpEnum.LT + : RelationalOpEnum.GT; + queryGraph.AddRelationalOpStrict( + _parameterStreamNum, parameterStartExpr, + _targetStreamNum, targetStartExpr, relop); + + queryGraph.AddRelationalOpStrict( + _targetStreamNum, targetEndExpr, + _parameterStreamNum, parameterEndExpr, relop); + } + else if (_currentMethod == DatetimeMethodEnum.FINISHES || _currentMethod == DatetimeMethodEnum.FINISHEDBY) + { + // FINISHES: b.startTimestamp < a.startTimestamp and a.endTimestamp = b.endTimestamp + // FINISHEDBY: a.startTimestamp < b.startTimestamp and a.endTimestamp = b.endTimestamp + RelationalOpEnum relop = _currentMethod == DatetimeMethodEnum.FINISHES + ? RelationalOpEnum.LT + : RelationalOpEnum.GT; + queryGraph.AddRelationalOpStrict( + _parameterStreamNum, parameterStartExpr, + _targetStreamNum, targetStartExpr, relop); + + queryGraph.AddStrictEquals( + _targetStreamNum, _targetEndProp, targetEndExpr, + _parameterStreamNum, _parameterEndProp, parameterEndExpr); + } + else if (_currentMethod == DatetimeMethodEnum.MEETS) + { + // a.endTimestamp = b.startTimestamp + queryGraph.AddStrictEquals(_targetStreamNum, _targetEndProp, targetEndExpr, + _parameterStreamNum, _parameterStartProp, parameterStartExpr); + } + else if (_currentMethod == DatetimeMethodEnum.METBY) + { + // a.startTimestamp = b.endTimestamp + queryGraph.AddStrictEquals(_targetStreamNum, _targetStartProp, targetStartExpr, + _parameterStreamNum, _parameterEndProp, parameterEndExpr); + } + else if (_currentMethod == DatetimeMethodEnum.OVERLAPS || _currentMethod == DatetimeMethodEnum.OVERLAPPEDBY) + { + // OVERLAPS: a.startTimestamp < b.startTimestamp < a.endTimestamp < b.endTimestamp + // OVERLAPPEDBY: b.startTimestamp < a.startTimestamp < b.endTimestamp < a.endTimestamp + RelationalOpEnum relop = _currentMethod == DatetimeMethodEnum.OVERLAPS + ? RelationalOpEnum.LT + : RelationalOpEnum.GT; + queryGraph.AddRelationalOpStrict( + _targetStreamNum, targetStartExpr, + _parameterStreamNum, parameterStartExpr, relop); + + queryGraph.AddRelationalOpStrict( + _targetStreamNum, targetEndExpr, + _parameterStreamNum, parameterEndExpr, relop); + + if (_currentMethod == DatetimeMethodEnum.OVERLAPS) + { + queryGraph.AddRelationalOpStrict( + _parameterStreamNum, parameterStartExpr, + _targetStreamNum, targetEndExpr, RelationalOpEnum.LT); + } + else + { + queryGraph.AddRelationalOpStrict( + _targetStreamNum, targetStartExpr, + _parameterStreamNum, parameterEndExpr, RelationalOpEnum.LT); + } + } + else if (_currentMethod == DatetimeMethodEnum.STARTS || _currentMethod == DatetimeMethodEnum.STARTEDBY) + { + // STARTS: a.startTimestamp = b.startTimestamp and a.endTimestamp < b.endTimestamp + // STARTEDBY: a.startTimestamp = b.startTimestamp and b.endTimestamp < a.endTimestamp + queryGraph.AddStrictEquals(_targetStreamNum, _targetStartProp, targetStartExpr, + _parameterStreamNum, _parameterStartProp, parameterStartExpr); + + RelationalOpEnum relop = _currentMethod == DatetimeMethodEnum.STARTS + ? RelationalOpEnum.LT + : RelationalOpEnum.GT; + queryGraph.AddRelationalOpStrict( + _targetStreamNum, targetEndExpr, + _parameterStreamNum, parameterEndExpr, relop); + } + } + + #endregion + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/datetime/eval/ExprDotNodeFilterAnalyzerDesc.cs b/NEsper.Core/NEsper.Core/epl/datetime/eval/ExprDotNodeFilterAnalyzerDesc.cs new file mode 100755 index 000000000..56b33529e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/eval/ExprDotNodeFilterAnalyzerDesc.cs @@ -0,0 +1,17 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.join.plan; + +namespace com.espertech.esper.epl.datetime.eval +{ + public interface ExprDotNodeFilterAnalyzerDesc + { + void Apply(QueryGraph queryGraph); + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/datetime/eval/OpFactory.cs b/NEsper.Core/NEsper.Core/epl/datetime/eval/OpFactory.cs new file mode 100755 index 000000000..7ed6db364 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/eval/OpFactory.cs @@ -0,0 +1,13 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.datetime.eval +{ + public interface OpFactory { + } +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluator.cs b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluator.cs new file mode 100755 index 000000000..0a04e1315 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluator.cs @@ -0,0 +1,17 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.datetime.eval.reformat +{ + internal interface DTLocalEvaluator + { + object Evaluate(object target, EvaluateParams evaluateParams); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorBase.cs b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorBase.cs new file mode 100755 index 000000000..cba2cf0df --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorBase.cs @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.compat; +using com.espertech.esper.epl.datetime.calop; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.datetime.eval.reformat +{ + internal abstract class DTLocalEvaluatorBase : DTLocalEvaluator + { + public abstract object Evaluate(object target, EvaluateParams evaluateParams); + + internal static void EvaluateDtxOps(IList calendarOps, DateTimeEx cal, EvaluateParams evaluateParams) + { + foreach (var calendarOp in calendarOps) + { + calendarOp.Evaluate(cal, evaluateParams.EventsPerStream, evaluateParams.IsNewData, evaluateParams.ExprEvaluatorContext); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorBeanCalOps.cs b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorBeanCalOps.cs new file mode 100755 index 000000000..dbe4c7ea0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorBeanCalOps.cs @@ -0,0 +1,35 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.datetime.eval.reformat +{ + internal class DTLocalEvaluatorBeanCalOps : DTLocalEvaluator + { + private readonly EventPropertyGetter _getter; + private readonly DTLocalEvaluator _inner; + + internal DTLocalEvaluatorBeanCalOps(EventPropertyGetter getter, DTLocalEvaluator inner) + { + _getter = getter; + _inner = inner; + } + + public object Evaluate(object target, EvaluateParams evaluateParams) + { + var timestamp = _getter.Get((EventBean)target); + if (timestamp == null) + { + return null; + } + return _inner.Evaluate(timestamp, evaluateParams); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorBeanIntervalNoEndTS.cs b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorBeanIntervalNoEndTS.cs new file mode 100755 index 000000000..4c9f523ca --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorBeanIntervalNoEndTS.cs @@ -0,0 +1,35 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.datetime.eval.reformat +{ + internal class DTLocalEvaluatorBeanIntervalNoEndTS : DTLocalEvaluator + { + private readonly EventPropertyGetter _getter; + private readonly DTLocalEvaluator _inner; + + internal DTLocalEvaluatorBeanIntervalNoEndTS(EventPropertyGetter getter, DTLocalEvaluator inner) + { + _getter = getter; + _inner = inner; + } + + public object Evaluate(object target, EvaluateParams evaluateParams) + { + var timestamp = _getter.Get((EventBean)target); + if (timestamp == null) + { + return null; + } + return _inner.Evaluate(timestamp, evaluateParams); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorBeanIntervalWithEnd.cs b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorBeanIntervalWithEnd.cs new file mode 100755 index 000000000..22acba113 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorBeanIntervalWithEnd.cs @@ -0,0 +1,46 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.datetime.eval.reformat +{ + internal class DTLocalEvaluatorBeanIntervalWithEnd : DTLocalEvaluator + { + private readonly EventPropertyGetter _getterStartTimestamp; + private readonly EventPropertyGetter _getterEndTimestamp; + private readonly DTLocalEvaluatorIntervalComp _inner; + + internal DTLocalEvaluatorBeanIntervalWithEnd( + EventPropertyGetter getterStartTimestamp, + EventPropertyGetter getterEndTimestamp, + DTLocalEvaluatorIntervalComp inner) + { + _getterStartTimestamp = getterStartTimestamp; + _getterEndTimestamp = getterEndTimestamp; + _inner = inner; + } + + public object Evaluate(object target, EvaluateParams evaluateParams) + { + var startTimestamp = _getterStartTimestamp.Get((EventBean)target); + if (startTimestamp == null) + { + return null; + } + var endTimestamp = _getterEndTimestamp.Get((EventBean)target); + if (endTimestamp == null) + { + return null; + } + + return _inner.Evaluate(startTimestamp, endTimestamp, evaluateParams); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorBeanReformat.cs b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorBeanReformat.cs new file mode 100755 index 000000000..03f84002f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorBeanReformat.cs @@ -0,0 +1,35 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.datetime.eval.reformat +{ + internal class DTLocalEvaluatorBeanReformat : DTLocalEvaluator + { + private readonly EventPropertyGetter _getter; + private readonly DTLocalEvaluator _inner; + + internal DTLocalEvaluatorBeanReformat(EventPropertyGetter getter, DTLocalEvaluator inner) + { + _getter = getter; + _inner = inner; + } + + public object Evaluate(object target, EvaluateParams evaluateParams) + { + var timestamp = _getter.Get((EventBean)target); + if (timestamp == null) + { + return null; + } + return _inner.Evaluate(timestamp, evaluateParams); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorCalOpsIntervalBase.cs b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorCalOpsIntervalBase.cs new file mode 100755 index 000000000..21833fd60 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorCalOpsIntervalBase.cs @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.epl.datetime.calop; +using com.espertech.esper.epl.datetime.interval; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.datetime.eval.reformat +{ + internal abstract class DTLocalEvaluatorCalOpsIntervalBase + : DTLocalEvaluatorBase + , DTLocalEvaluatorIntervalComp + { + protected readonly IList CalendarOps; + protected readonly IntervalOp IntervalOp; + + protected DTLocalEvaluatorCalOpsIntervalBase(IList calendarOps, IntervalOp intervalOp) + { + CalendarOps = calendarOps; + IntervalOp = intervalOp; + } + + public abstract object Evaluate(object startTimestamp, object endTimestamp, EvaluateParams evaluateParams); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorCalopReformatBase.cs b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorCalopReformatBase.cs new file mode 100755 index 000000000..0dc9238f4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorCalopReformatBase.cs @@ -0,0 +1,30 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.epl.datetime.calop; +using com.espertech.esper.epl.datetime.reformatop; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.datetime.eval.reformat +{ + internal abstract class DTLocalEvaluatorCalopReformatBase : DTLocalEvaluator + { + protected readonly IList CalendarOps; + protected readonly ReformatOp ReformatOp; + + protected DTLocalEvaluatorCalopReformatBase(IList calendarOps, ReformatOp reformatOp) + { + CalendarOps = calendarOps; + ReformatOp = reformatOp; + } + + public abstract object Evaluate(object target, EvaluateParams evaluateParams); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorDateTimeInterval.cs b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorDateTimeInterval.cs new file mode 100755 index 000000000..7643f3be7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorDateTimeInterval.cs @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.datetime.interval; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.datetime.eval.reformat +{ + internal class DTLocalEvaluatorDateTimeInterval : DTLocalEvaluatorIntervalBase + { + internal DTLocalEvaluatorDateTimeInterval(IntervalOp intervalOp) + : base(intervalOp) + { + } + + public override object Evaluate(object target, EvaluateParams evaluateParams) + { + var time = ((DateTime)target).UtcMillis(); + return IntervalOp.Evaluate(time, time, evaluateParams); + } + + public override object Evaluate(object startTimestamp, object endTimestamp, EvaluateParams evaluateParams) + { + var start = ((DateTime)startTimestamp).UtcMillis(); + var end = ((DateTime)endTimestamp).UtcMillis(); + return IntervalOp.Evaluate(start, end, evaluateParams); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorDateTimeOpsInterval.cs b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorDateTimeOpsInterval.cs new file mode 100755 index 000000000..0139f863e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorDateTimeOpsInterval.cs @@ -0,0 +1,58 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.datetime.calop; +using com.espertech.esper.epl.datetime.interval; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.datetime.eval.reformat +{ + internal class DTLocalEvaluatorDateTimeOpsInterval : DTLocalEvaluatorCalOpsIntervalBase + { + private readonly TimeZoneInfo _timeZone; + + internal DTLocalEvaluatorDateTimeOpsInterval( + IList calendarOps, + IntervalOp intervalOp, + TimeZoneInfo timeZone) + : base(calendarOps, intervalOp) + { + _timeZone = timeZone; + } + + public override object Evaluate(object target, EvaluateParams evaluateParams) + { + var dt = (DateTime) target; + var dtx = DateTimeEx.GetInstance(_timeZone); + dtx.SetUtcMillis(dt.UtcMillis()); + EvaluateDtxOps(CalendarOps, dtx, evaluateParams); + var time = dtx.TimeInMillis; + return IntervalOp.Evaluate(time, time, evaluateParams); + } + + public override object Evaluate( + object startTimestamp, + object endTimestamp, + EvaluateParams evaluateParams) + { + var startLong = ((DateTime)startTimestamp).UtcMillis(); + var endLong = ((DateTime)endTimestamp).UtcMillis(); + var dtx = DateTimeEx.GetInstance(_timeZone); + dtx.SetUtcMillis(startLong); + EvaluateDtxOps(CalendarOps, dtx, evaluateParams); + var startTime = dtx.TimeInMillis; + var endTime = startTime + (endLong - startLong); + return IntervalOp.Evaluate(startTime, endTime, evaluateParams); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorDateTimeOpsReformat.cs b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorDateTimeOpsReformat.cs new file mode 100755 index 000000000..d92ea25ac --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorDateTimeOpsReformat.cs @@ -0,0 +1,42 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.datetime.calop; +using com.espertech.esper.epl.datetime.reformatop; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.datetime.eval.reformat +{ + internal class DTLocalEvaluatorDateTimeOpsReformat : DTLocalEvaluatorCalopReformatBase + { + private readonly TimeZoneInfo _timeZone; + + internal DTLocalEvaluatorDateTimeOpsReformat( + IList calendarOps, + ReformatOp reformatOp, + TimeZoneInfo timeZone) + : base(calendarOps, reformatOp) + { + _timeZone = timeZone; + } + + public override object Evaluate(object target, EvaluateParams evaluateParams) + { + var dt = (DateTime) target; + var dtx = DateTimeEx.GetInstance(_timeZone); + dtx.SetUtcMillis(dt.UtcMillis()); + DTLocalEvaluatorBase.EvaluateDtxOps(CalendarOps, dtx, evaluateParams); + return ReformatOp.Evaluate(dtx, evaluateParams.EventsPerStream, evaluateParams.IsNewData, evaluateParams.ExprEvaluatorContext); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorDateTimeReformat.cs b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorDateTimeReformat.cs new file mode 100755 index 000000000..2433d6531 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorDateTimeReformat.cs @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.datetime.reformatop; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.datetime.eval.reformat +{ + internal class DTLocalEvaluatorDateTimeReformat : DTLocalEvaluatorReformatBase + { + internal DTLocalEvaluatorDateTimeReformat(ReformatOp reformatOp) + : base(reformatOp) + { + } + + public override object Evaluate(object target, EvaluateParams evaluateParams) + { + return ReformatOp.Evaluate((DateTime)target, evaluateParams.EventsPerStream, evaluateParams.IsNewData, evaluateParams.ExprEvaluatorContext); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorDtoInterval.cs b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorDtoInterval.cs new file mode 100755 index 000000000..684dce837 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorDtoInterval.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.epl.datetime.interval; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.datetime.eval.reformat +{ + internal class DTLocalEvaluatorDtoInterval : DTLocalEvaluatorIntervalBase + { + internal DTLocalEvaluatorDtoInterval(IntervalOp intervalOp) + : base(intervalOp) + { + } + + public override object Evaluate(object target, EvaluateParams evaluateParams) + { + var time = ((DateTimeOffset)target).TimeInMillis(); + return IntervalOp.Evaluate(time, time, evaluateParams); + } + + public override object Evaluate(object startTimestamp, object endTimestamp, EvaluateParams evaluateParams) + { + var start = ((DateTimeOffset)startTimestamp).TimeInMillis(); + var end = ((DateTimeOffset)endTimestamp).TimeInMillis(); + return IntervalOp.Evaluate(start, end, evaluateParams); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorDtoOpsInterval.cs b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorDtoOpsInterval.cs new file mode 100755 index 000000000..f0990eda6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorDtoOpsInterval.cs @@ -0,0 +1,58 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.datetime.calop; +using com.espertech.esper.epl.datetime.interval; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.datetime.eval.reformat +{ + internal class DTLocalEvaluatorDtoOpsInterval : DTLocalEvaluatorCalOpsIntervalBase + { + private readonly TimeZoneInfo _timeZone; + + internal DTLocalEvaluatorDtoOpsInterval( + IList calendarOps, + IntervalOp intervalOp, + TimeZoneInfo timeZone) + : base(calendarOps, intervalOp) + { + _timeZone = timeZone; + } + + public override object Evaluate(object target, EvaluateParams evaluateParams) + { + var dto = (DateTimeOffset) target; + var dtx = DateTimeEx.GetInstance(_timeZone, dto); + EvaluateDtxOps(CalendarOps, dtx, evaluateParams); + var time = dtx.TimeInMillis; + return IntervalOp.Evaluate(time, time, evaluateParams); + } + + public override object Evaluate( + object startTimestamp, + object endTimestamp, + EvaluateParams evaluateParams) + { + var startLong = ((DateTimeOffset)startTimestamp).TimeInMillis(); + var endLong = ((DateTimeOffset)endTimestamp).TimeInMillis(); + var dtx = DateTimeEx.GetInstance(_timeZone); + dtx.SetUtcMillis(startLong); + EvaluateDtxOps(CalendarOps, dtx, evaluateParams); + var startTime = dtx.TimeInMillis; + var endTime = startTime + (endLong - startLong); + return IntervalOp.Evaluate(startTime, endTime, evaluateParams); + } + } + +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorDtoOpsReformat.cs b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorDtoOpsReformat.cs new file mode 100755 index 000000000..9be0bf914 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorDtoOpsReformat.cs @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.datetime.calop; +using com.espertech.esper.epl.datetime.reformatop; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.datetime.eval.reformat +{ + internal class DTLocalEvaluatorDtoOpsReformat : DTLocalEvaluatorCalopReformatBase + { + private readonly TimeZoneInfo _timeZone; + + internal DTLocalEvaluatorDtoOpsReformat(IList calendarOps, ReformatOp reformatOp, TimeZoneInfo timeZoneInfo) + : base(calendarOps, reformatOp) + { + _timeZone = timeZoneInfo; + } + + public override object Evaluate(object target, EvaluateParams evaluateParams) + { + var dt = (DateTimeOffset) target; + var dtx = DateTimeEx.GetInstance(_timeZone, dt); + DTLocalEvaluatorBase.EvaluateDtxOps(CalendarOps, dtx, evaluateParams); + return ReformatOp.Evaluate(dtx, evaluateParams.EventsPerStream, evaluateParams.IsNewData, evaluateParams.ExprEvaluatorContext); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorDtoReformat.cs b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorDtoReformat.cs new file mode 100755 index 000000000..5429f3eed --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorDtoReformat.cs @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.datetime.reformatop; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.datetime.eval.reformat +{ + internal class DTLocalEvaluatorDtoReformat : DTLocalEvaluatorReformatBase + { + internal DTLocalEvaluatorDtoReformat(ReformatOp reformatOp) + : base(reformatOp) + { + } + + public override object Evaluate(object target, EvaluateParams evaluateParams) + { + return ReformatOp.Evaluate((DateTimeOffset)target, evaluateParams.EventsPerStream, evaluateParams.IsNewData, evaluateParams.ExprEvaluatorContext); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorDtxInterval.cs b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorDtxInterval.cs new file mode 100755 index 000000000..ece17ab79 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorDtxInterval.cs @@ -0,0 +1,36 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.datetime.interval; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.datetime.eval.reformat +{ + internal class DTLocalEvaluatorDtxInterval : DTLocalEvaluatorIntervalBase + { + internal DTLocalEvaluatorDtxInterval(IntervalOp intervalOp) + : base(intervalOp) + { + } + + public override object Evaluate(object target, EvaluateParams evaluateParams) + { + var time = ((DateTimeEx)target).TimeInMillis; + return IntervalOp.Evaluate(time, time, evaluateParams); + } + + public override object Evaluate(object startTimestamp, object endTimestamp, EvaluateParams evaluateParams) + { + var start = ((DateTimeEx)startTimestamp).TimeInMillis; + var end = ((DateTimeEx)endTimestamp).TimeInMillis; + return IntervalOp.Evaluate(start, end, evaluateParams); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorDtxOpsDateTime.cs b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorDtxOpsDateTime.cs new file mode 100755 index 000000000..93725d8d7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorDtxOpsDateTime.cs @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.compat; +using com.espertech.esper.epl.datetime.calop; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.datetime.eval.reformat +{ + internal class DTLocalEvaluatorDtxOpsDateTime + : DTLocalEvaluatorDtxOpsDtxBase + , DTLocalEvaluator + { + private readonly TimeZoneInfo _timeZone; + + internal DTLocalEvaluatorDtxOpsDateTime(IList calendarOps, TimeZoneInfo timeZone) + : base(calendarOps) + { + _timeZone = timeZone; + } + + public object Evaluate(object target, EvaluateParams evaluateParams) + { + var dateValue = (DateTime) target; + var dtx = DateTimeEx.GetInstance(_timeZone); + dtx.SetUtcMillis(dateValue.UtcMillis()); + + DTLocalEvaluatorBase.EvaluateDtxOps(CalendarOps, dtx, evaluateParams); + + return dtx.DateTime.DateTime; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorDtxOpsDateTimeOffset.cs b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorDtxOpsDateTimeOffset.cs new file mode 100755 index 000000000..a6b30264b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorDtxOpsDateTimeOffset.cs @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.compat; +using com.espertech.esper.epl.datetime.calop; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.datetime.eval.reformat +{ + internal class DTLocalEvaluatorDtxOpsDateTimeOffset + : DTLocalEvaluatorDtxOpsDtxBase + , DTLocalEvaluator + { + private readonly TimeZoneInfo _timeZone; + + internal DTLocalEvaluatorDtxOpsDateTimeOffset(IList calendarOps, TimeZoneInfo timeZone) + : base(calendarOps) + { + _timeZone = timeZone; + } + + public object Evaluate(object target, EvaluateParams evaluateParams) + { + var dateValue = (DateTimeOffset) target; + var dtx = new DateTimeEx(dateValue, _timeZone); + + DTLocalEvaluatorBase.EvaluateDtxOps(CalendarOps, dtx, evaluateParams); + + return dtx.DateTime; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorDtxOpsDtx.cs b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorDtxOpsDtx.cs new file mode 100755 index 000000000..939464d58 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorDtxOpsDtx.cs @@ -0,0 +1,36 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.compat; +using com.espertech.esper.epl.datetime.calop; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.datetime.eval.reformat +{ + internal class DTLocalEvaluatorDtxOpsDtx + : DTLocalEvaluatorDtxOpsDtxBase + , DTLocalEvaluator + { + internal DTLocalEvaluatorDtxOpsDtx(IList calendarOps) + : base(calendarOps) + { + } + + public object Evaluate(object target, EvaluateParams evaluateParams) + { + var dtxValue = (DateTimeEx) target; + DateTimeEx dtx = dtxValue.Clone(); + + DTLocalEvaluatorBase.EvaluateDtxOps(CalendarOps, dtx, evaluateParams); + + return dtx; + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorDtxOpsDtxBase.cs b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorDtxOpsDtxBase.cs new file mode 100755 index 000000000..a50140b19 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorDtxOpsDtxBase.cs @@ -0,0 +1,24 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.epl.datetime.calop; + +namespace com.espertech.esper.epl.datetime.eval.reformat +{ + internal abstract class DTLocalEvaluatorDtxOpsDtxBase + { + protected readonly IList CalendarOps; + + protected DTLocalEvaluatorDtxOpsDtxBase(IList calendarOps) + { + CalendarOps = calendarOps; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorDtxOpsInterval.cs b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorDtxOpsInterval.cs new file mode 100755 index 000000000..eb239f153 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorDtxOpsInterval.cs @@ -0,0 +1,57 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.datetime.calop; +using com.espertech.esper.epl.datetime.interval; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.datetime.eval.reformat +{ + internal class DTLocalEvaluatorDtxOpsInterval : DTLocalEvaluatorCalOpsIntervalBase + { + private readonly TimeZoneInfo _timeZone; + + internal DTLocalEvaluatorDtxOpsInterval( + IList calendarOps, + IntervalOp intervalOp, + TimeZoneInfo timeZone) + : base(calendarOps, intervalOp) + { + _timeZone = timeZone; + } + + public override object Evaluate(object target, EvaluateParams evaluateParams) + { + var dtx = ((DateTimeEx)target).Clone(); + EvaluateDtxOps(CalendarOps, dtx, evaluateParams); + var time = dtx.TimeInMillis; + return IntervalOp.Evaluate(time, time, evaluateParams); + } + + public override object Evaluate( + object startTimestamp, + object endTimestamp, + EvaluateParams evaluateParams) + { + var startLong = ((DateTimeEx)startTimestamp).TimeInMillis; + var endLong = ((DateTimeEx)endTimestamp).TimeInMillis; + var dtx = DateTimeEx.GetInstance(_timeZone); + dtx.SetUtcMillis(startLong); + EvaluateDtxOps(CalendarOps, dtx, evaluateParams); + var startTime = dtx.TimeInMillis; + var endTime = startTime + (endLong - startLong); + return IntervalOp.Evaluate(startTime, endTime, evaluateParams); + } + } + +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorDtxOpsLong.cs b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorDtxOpsLong.cs new file mode 100755 index 000000000..5764209c3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorDtxOpsLong.cs @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.datetime.calop; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.time; + +namespace com.espertech.esper.epl.datetime.eval.reformat +{ + internal class DTLocalEvaluatorDtxOpsLong + : DTLocalEvaluatorDtxOpsDtxBase + , DTLocalEvaluator + { + private readonly TimeZoneInfo _timeZone; + private readonly TimeAbacus _timeAbacus; + + internal DTLocalEvaluatorDtxOpsLong(IList calendarOps, TimeZoneInfo timeZone, TimeAbacus timeAbacus) + : base(calendarOps) + { + _timeZone = timeZone; + _timeAbacus = timeAbacus; + } + + public object Evaluate(object target, EvaluateParams evaluateParams) + { + var longValue = (long)target; + var dtx = DateTimeEx.GetInstance(_timeZone); + var remainder = _timeAbacus.CalendarSet(longValue, dtx); + + DTLocalEvaluatorBase.EvaluateDtxOps(CalendarOps, dtx, evaluateParams); + + return _timeAbacus.CalendarGet(dtx, remainder); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorDtxOpsReformat.cs b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorDtxOpsReformat.cs new file mode 100755 index 000000000..a5145544b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorDtxOpsReformat.cs @@ -0,0 +1,33 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.datetime.calop; +using com.espertech.esper.epl.datetime.reformatop; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.datetime.eval.reformat +{ + internal class DTLocalEvaluatorDtxOpsReformat : DTLocalEvaluatorCalopReformatBase + { + internal DTLocalEvaluatorDtxOpsReformat(IList calendarOps, ReformatOp reformatOp) + : base(calendarOps, reformatOp) + { + } + + public override object Evaluate(object target, EvaluateParams evaluateParams) + { + var dtx = ((DateTimeEx)target).Clone(); + DTLocalEvaluatorBase.EvaluateDtxOps(CalendarOps, dtx, evaluateParams); + return ReformatOp.Evaluate(dtx, evaluateParams.EventsPerStream, evaluateParams.IsNewData, evaluateParams.ExprEvaluatorContext); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorDtxReformat.cs b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorDtxReformat.cs new file mode 100755 index 000000000..b93218987 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorDtxReformat.cs @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.datetime.reformatop; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.datetime.eval.reformat +{ + internal class DTLocalEvaluatorDtxReformat : DTLocalEvaluatorReformatBase + { + internal DTLocalEvaluatorDtxReformat(ReformatOp reformatOp) + : base(reformatOp) + { + } + + public override object Evaluate(object target, EvaluateParams evaluateParams) + { + return ReformatOp.Evaluate((DateTimeEx)target, evaluateParams.EventsPerStream, evaluateParams.IsNewData, evaluateParams.ExprEvaluatorContext); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorIntervalBase.cs b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorIntervalBase.cs new file mode 100755 index 000000000..d8f2a0cd1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorIntervalBase.cs @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.datetime.interval; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.datetime.eval.reformat +{ + internal abstract class DTLocalEvaluatorIntervalBase + : DTLocalEvaluator + , DTLocalEvaluatorIntervalComp + { + protected readonly IntervalOp IntervalOp; + + protected DTLocalEvaluatorIntervalBase(IntervalOp intervalOp) + { + IntervalOp = intervalOp; + } + + public abstract object Evaluate(object target, EvaluateParams evaluateParams); + public abstract object Evaluate(object startTimestamp, object endTimestamp, EvaluateParams evaluateParams); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorIntervalComp.cs b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorIntervalComp.cs new file mode 100755 index 000000000..cf440e754 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorIntervalComp.cs @@ -0,0 +1,18 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.datetime.eval.reformat +{ + /// Interval methods. + internal interface DTLocalEvaluatorIntervalComp + { + object Evaluate(object startTimestamp, object endTimestamp, EvaluateParams evaluateParams); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorLongInterval.cs b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorLongInterval.cs new file mode 100755 index 000000000..f7d22b2cb --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorLongInterval.cs @@ -0,0 +1,35 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.epl.datetime.interval; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.datetime.eval.reformat +{ + internal class DTLocalEvaluatorLongInterval : DTLocalEvaluatorIntervalBase + { + internal DTLocalEvaluatorLongInterval(IntervalOp intervalOp) + : base(intervalOp) + { + } + + public override object Evaluate(object target, EvaluateParams evaluateParams) + { + var time = (long)target; + return IntervalOp.Evaluate(time, time, evaluateParams); + } + + public override object Evaluate(object startTimestamp, object endTimestamp, EvaluateParams evaluateParams) + { + var startTime = (long)startTimestamp; + var endTime = (long)endTimestamp; + return IntervalOp.Evaluate(startTime, endTime, evaluateParams); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorLongOpsInterval.cs b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorLongOpsInterval.cs new file mode 100755 index 000000000..432e72d7b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorLongOpsInterval.cs @@ -0,0 +1,58 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.datetime.calop; +using com.espertech.esper.epl.datetime.interval; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.time; + +namespace com.espertech.esper.epl.datetime.eval.reformat +{ + internal class DTLocalEvaluatorLongOpsInterval : DTLocalEvaluatorCalOpsIntervalBase + { + private readonly TimeZoneInfo _timeZone; + private readonly TimeAbacus _timeAbacus; + + internal DTLocalEvaluatorLongOpsInterval( + IList calendarOps, + IntervalOp intervalOp, + TimeZoneInfo timeZone, + TimeAbacus timeAbacus) + : base(calendarOps, intervalOp) + { + _timeZone = timeZone; + _timeAbacus = timeAbacus; + } + + public override object Evaluate(object target, EvaluateParams evaluateParams) + { + var dtx = DateTimeEx.GetInstance(_timeZone); + var startRemainder = _timeAbacus.CalendarSet((long)target, dtx); + EvaluateDtxOps(CalendarOps, dtx, evaluateParams); + var time = _timeAbacus.CalendarGet(dtx, startRemainder); + return IntervalOp.Evaluate(time, time, evaluateParams); + } + + public override object Evaluate(object startTimestamp, object endTimestamp, EvaluateParams evaluateParams) + { + var startLong = (long)startTimestamp; + var endLong = (long)endTimestamp; + var dtx = DateTimeEx.GetInstance(_timeZone); + var startRemainder = _timeAbacus.CalendarSet(startLong, dtx); + EvaluateDtxOps(CalendarOps, dtx, evaluateParams); + var startTime = _timeAbacus.CalendarGet(dtx, startRemainder); + var endTime = startTime + (endLong - startLong); + return IntervalOp.Evaluate(startTime, endTime, evaluateParams); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorLongOpsReformat.cs b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorLongOpsReformat.cs new file mode 100755 index 000000000..43984ba1f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorLongOpsReformat.cs @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.datetime.calop; +using com.espertech.esper.epl.datetime.reformatop; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.time; + +namespace com.espertech.esper.epl.datetime.eval.reformat +{ + internal class DTLocalEvaluatorLongOpsReformat : DTLocalEvaluatorCalopReformatBase + { + private readonly TimeZoneInfo _timeZone; + private readonly TimeAbacus _timeAbacus; + + internal DTLocalEvaluatorLongOpsReformat( + IList calendarOps, + ReformatOp reformatOp, + TimeZoneInfo timeZone, + TimeAbacus timeAbacus) + : base(calendarOps, reformatOp) + { + _timeZone = timeZone; + _timeAbacus = timeAbacus; + } + + public override object Evaluate(object target, EvaluateParams evaluateParams) + { + var dtx = DateTimeEx.GetInstance(_timeZone); + _timeAbacus.CalendarSet((long)target, dtx); + DTLocalEvaluatorBase.EvaluateDtxOps(CalendarOps, dtx, evaluateParams); + return ReformatOp.Evaluate(dtx, evaluateParams.EventsPerStream, evaluateParams.IsNewData, evaluateParams.ExprEvaluatorContext); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorLongReformat.cs b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorLongReformat.cs new file mode 100755 index 000000000..7daf99986 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorLongReformat.cs @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.epl.datetime.reformatop; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.datetime.eval.reformat +{ + internal class DTLocalEvaluatorLongReformat : DTLocalEvaluatorReformatBase + { + internal DTLocalEvaluatorLongReformat(ReformatOp reformatOp) + : base(reformatOp) + { + } + + public override object Evaluate(object target, EvaluateParams evaluateParams) + { + return ReformatOp.Evaluate((long)target, evaluateParams.EventsPerStream, evaluateParams.IsNewData, evaluateParams.ExprEvaluatorContext); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorReformatBase.cs b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorReformatBase.cs new file mode 100755 index 000000000..de5bce958 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/eval/reformat/DTLocalEvaluatorReformatBase.cs @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.datetime.reformatop; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.datetime.eval.reformat +{ + internal abstract class DTLocalEvaluatorReformatBase : DTLocalEvaluator + { + protected readonly ReformatOp ReformatOp; + + protected DTLocalEvaluatorReformatBase(ReformatOp reformatOp) + { + ReformatOp = reformatOp; + } + + public abstract object Evaluate(object target, EvaluateParams evaluateParams); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/interval/ExprEvaluatorStreamLongProp.cs b/NEsper.Core/NEsper.Core/epl/datetime/interval/ExprEvaluatorStreamLongProp.cs new file mode 100755 index 000000000..f2c3f236f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/interval/ExprEvaluatorStreamLongProp.cs @@ -0,0 +1,44 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.datetime.interval +{ + [Serializable] + public class ExprEvaluatorStreamLongProp : ExprEvaluator + { + private readonly int _streamId; + private readonly EventPropertyGetter _getter; + + public ExprEvaluatorStreamLongProp(int streamId, EventPropertyGetter getter) + { + _streamId = streamId; + _getter = getter; + } + + public object Evaluate(EvaluateParams evaluateParams) + { + var theEvent = evaluateParams.EventsPerStream[_streamId]; + if (theEvent == null) + { + return null; + } + return _getter.Get(theEvent); + } + + public Type ReturnType + { + get { return typeof (long); } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/interval/ExprEvaluatorStreamLongPropFragment.cs b/NEsper.Core/NEsper.Core/epl/datetime/interval/ExprEvaluatorStreamLongPropFragment.cs new file mode 100755 index 000000000..58f539efe --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/interval/ExprEvaluatorStreamLongPropFragment.cs @@ -0,0 +1,54 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.datetime.interval +{ + public class ExprEvaluatorStreamLongPropFragment : ExprEvaluator + { + private readonly EventPropertyGetter _getterFragment; + private readonly EventPropertyGetter _getterTimestamp; + private readonly int _streamId; + + public ExprEvaluatorStreamLongPropFragment( + int streamId, + EventPropertyGetter getterFragment, + EventPropertyGetter getterTimestamp) + { + _streamId = streamId; + _getterFragment = getterFragment; + _getterTimestamp = getterTimestamp; + } + + public Type ReturnType + { + get { return typeof (long); } + } + + public object Evaluate(EvaluateParams evaluateParams) + { + var theEvent = evaluateParams.EventsPerStream[_streamId]; + if (theEvent == null) + { + return null; + } + + var @event = _getterFragment.GetFragment(theEvent); + if (!(@event is EventBean)) + { + return null; + } + return _getterTimestamp.Get((EventBean) @event); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/datetime/interval/ExprOptionalConstant.cs b/NEsper.Core/NEsper.Core/epl/datetime/interval/ExprOptionalConstant.cs new file mode 100755 index 000000000..91137e887 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/interval/ExprOptionalConstant.cs @@ -0,0 +1,52 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.datetime.interval +{ + public class ExprOptionalConstant + { + public readonly static IntervalDeltaExprEvaluatorMax MAXEVAL = + new IntervalDeltaExprEvaluatorMax(); + + private readonly IntervalDeltaExprEvaluator _evaluator; + private readonly long? _optionalConstant; + + public ExprOptionalConstant(IntervalDeltaExprEvaluator evaluator, long? optionalConstant) + { + _evaluator = evaluator; + _optionalConstant = optionalConstant; + } + + public long? OptionalConstant + { + get { return _optionalConstant; } + } + + public IntervalDeltaExprEvaluator Evaluator + { + get { return _evaluator; } + } + + public static ExprOptionalConstant Make(long maxValue) + { + return new ExprOptionalConstant(MAXEVAL, maxValue); + } + + public class IntervalDeltaExprEvaluatorMax : IntervalDeltaExprEvaluator + { + public long Evaluate(long reference, EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + return long.MaxValue; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/interval/IntervalComputer.cs b/NEsper.Core/NEsper.Core/epl/datetime/interval/IntervalComputer.cs new file mode 100755 index 000000000..b5eb7c836 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/interval/IntervalComputer.cs @@ -0,0 +1,24 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.datetime.interval +{ + public interface IntervalComputer { + bool? Compute(long leftStart, + long leftEnd, + long rightStart, + long rightEnd, + EventBean[] eventsPerStream, + bool newData, + ExprEvaluatorContext context); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/interval/IntervalComputerConstantBase.cs b/NEsper.Core/NEsper.Core/epl/datetime/interval/IntervalComputerConstantBase.cs new file mode 100755 index 000000000..ae95a1471 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/interval/IntervalComputerConstantBase.cs @@ -0,0 +1,33 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.datetime.interval +{ + public abstract class IntervalComputerConstantBase + { + protected readonly long Start; + protected readonly long End; + + protected IntervalComputerConstantBase(IntervalStartEndParameterPair pair, bool allowSwitch) + { + var startVal = pair.Start.OptionalConstant.Value; + var endVal = pair.End.OptionalConstant.Value; + + if (startVal > endVal && allowSwitch) + { + Start = endVal; + End = startVal; + } + else + { + Start = startVal; + End = endVal; + } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/datetime/interval/IntervalComputerFactory.cs b/NEsper.Core/NEsper.Core/epl/datetime/interval/IntervalComputerFactory.cs new file mode 100755 index 000000000..d759d120b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/interval/IntervalComputerFactory.cs @@ -0,0 +1,1214 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.datetime.eval; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.time; + +namespace com.espertech.esper.epl.datetime.interval +{ + public class IntervalComputerFactory + { + public static IntervalComputer Make(DatetimeMethodEnum method, IList expressions, TimeAbacus timeAbacus) + { + var parameters = GetParameters(expressions, timeAbacus); + + if (method == DatetimeMethodEnum.BEFORE) + { + if (parameters.Length == 0) + { + return new IntervalComputerBeforeNoParam(); + } + var pair = IntervalStartEndParameterPair.FromParamsWithLongMaxEnd(parameters); + if (pair.IsConstant) + { + return new IntervalComputerConstantBefore(pair); + } + return new IntervalComputerBeforeWithDeltaExpr(pair); + } + else if (method == DatetimeMethodEnum.AFTER) + { + if (parameters.Length == 0) + { + return new IntervalComputerAfterNoParam(); + } + var pair = IntervalStartEndParameterPair.FromParamsWithLongMaxEnd(parameters); + if (pair.IsConstant) + { + return new IntervalComputerConstantAfter(pair); + } + return new IntervalComputerAfterWithDeltaExpr(pair); + } + else if (method == DatetimeMethodEnum.COINCIDES) + { + if (parameters.Length == 0) + { + return new IntervalComputerCoincidesNoParam(); + } + var pair = IntervalStartEndParameterPair.FromParamsWithSameEnd(parameters); + if (pair.IsConstant) + { + return new IntervalComputerConstantCoincides(pair); + } + return new IntervalComputerCoincidesWithDeltaExpr(pair); + } + else if (method == DatetimeMethodEnum.DURING || method == DatetimeMethodEnum.INCLUDES) + { + if (parameters.Length == 0) + { + if (method == DatetimeMethodEnum.DURING) + { + return new IntervalComputerDuringNoParam(); + } + return new IntervalComputerIncludesNoParam(); + } + var pair = IntervalStartEndParameterPair.FromParamsWithSameEnd(parameters); + if (parameters.Length == 1) + { + return new IntervalComputerDuringAndIncludesThreshold( + method == DatetimeMethodEnum.DURING, pair.Start.Evaluator); + } + if (parameters.Length == 2) + { + return new IntervalComputerDuringAndIncludesMinMax( + method == DatetimeMethodEnum.DURING, pair.Start.Evaluator, pair.End.Evaluator); + } + return new IntervalComputerDuringMinMaxStartEnd( + method == DatetimeMethodEnum.DURING, GetEvaluators(expressions, timeAbacus)); + } + else if (method == DatetimeMethodEnum.FINISHES) + { + if (parameters.Length == 0) + { + return new IntervalComputerFinishesNoParam(); + } + ValidateConstantThreshold("finishes", parameters[0]); + return new IntervalComputerFinishesThreshold(parameters[0].Evaluator); + } + else if (method == DatetimeMethodEnum.FINISHEDBY) + { + if (parameters.Length == 0) + { + return new IntervalComputerFinishedByNoParam(); + } + ValidateConstantThreshold("finishedby", parameters[0]); + return new IntervalComputerFinishedByThreshold(parameters[0].Evaluator); + } + else if (method == DatetimeMethodEnum.MEETS) + { + if (parameters.Length == 0) + { + return new IntervalComputerMeetsNoParam(); + } + ValidateConstantThreshold("meets", parameters[0]); + return new IntervalComputerMeetsThreshold(parameters[0].Evaluator); + } + else if (method == DatetimeMethodEnum.METBY) + { + if (parameters.Length == 0) + { + return new IntervalComputerMetByNoParam(); + } + ValidateConstantThreshold("metBy", parameters[0]); + return new IntervalComputerMetByThreshold(parameters[0].Evaluator); + } + else if (method == DatetimeMethodEnum.OVERLAPS || method == DatetimeMethodEnum.OVERLAPPEDBY) + { + if (parameters.Length == 0) + { + if (method == DatetimeMethodEnum.OVERLAPS) + { + return new IntervalComputerOverlapsNoParam(); + } + return new IntervalComputerOverlappedByNoParam(); + } + if (parameters.Length == 1) + { + return new IntervalComputerOverlapsAndByThreshold( + method == DatetimeMethodEnum.OVERLAPS, parameters[0].Evaluator); + } + return new IntervalComputerOverlapsAndByMinMax( + method == DatetimeMethodEnum.OVERLAPS, parameters[0].Evaluator, parameters[1].Evaluator); + } + else if (method == DatetimeMethodEnum.STARTS) + { + if (parameters.Length == 0) + { + return new IntervalComputerStartsNoParam(); + } + ValidateConstantThreshold("starts", parameters[0]); + return new IntervalComputerStartsThreshold(parameters[0].Evaluator); + } + else if (method == DatetimeMethodEnum.STARTEDBY) + { + if (parameters.Length == 0) + { + return new IntervalComputerStartedByNoParam(); + } + ValidateConstantThreshold("startedBy", parameters[0]); + return new IntervalComputerStartedByThreshold(parameters[0].Evaluator); + } + throw new ArgumentException("Unknown datetime method '" + method + "'"); + } + + private static void ValidateConstantThreshold(String method, ExprOptionalConstant param) + { + if (param.OptionalConstant != null && (param.OptionalConstant).AsLong() < 0) + { + throw new ExprValidationException( + "The " + method + " date-time method does not allow negative threshold value"); + } + } + + private static ExprOptionalConstant[] GetParameters(IList expressions, TimeAbacus timeAbacus) + { + var parameters = new ExprOptionalConstant[expressions.Count - 1]; + for (var i = 1; i < expressions.Count; i++) + { + parameters[i - 1] = GetExprOrConstant(expressions[i], timeAbacus); + } + return parameters; + } + + private static IntervalDeltaExprEvaluator[] GetEvaluators(IList expressions, TimeAbacus timeAbacus) + { + var parameters = new IntervalDeltaExprEvaluator[expressions.Count - 1]; + for (var i = 1; i < expressions.Count; i++) + { + parameters[i - 1] = GetExprOrConstant(expressions[i], timeAbacus).Evaluator; + } + return parameters; + } + + private static ExprOptionalConstant GetExprOrConstant(ExprNode exprNode, TimeAbacus timeAbacus) + { + if (exprNode is ExprTimePeriod) + { + var timePeriod = (ExprTimePeriod)exprNode; + if (!timePeriod.HasMonth && !timePeriod.HasYear) + { + // no-month and constant + if (exprNode.IsConstantResult) + { + var sec = timePeriod.EvaluateAsSeconds(null, true, null); + var l = timeAbacus.DeltaForSecondsDouble(sec); + IntervalDeltaExprEvaluator eval = new ProxyIntervalDeltaExprEvaluator + { + ProcEvaluate = (reference, eventsPerStream, isNewData, context) => l, + }; + return new ExprOptionalConstant(eval, l); + } + // no-month and not constant + else + { + IntervalDeltaExprEvaluator eval = new ProxyIntervalDeltaExprEvaluator + { + ProcEvaluate = (reference, eventsPerStream, isNewData, context) => + { + double sec = timePeriod.EvaluateAsSeconds(eventsPerStream, isNewData, context); + return timeAbacus.DeltaForSecondsDouble(sec); + }, + }; + return new ExprOptionalConstant(eval, null); + } + } + // has-month + else + { + // has-month and constant + if (exprNode.IsConstantResult) + { + ExprTimePeriodEvalDeltaConst timerPeriodConst = timePeriod.ConstEvaluator(null); + IntervalDeltaExprEvaluator eval = new ProxyIntervalDeltaExprEvaluator + { + ProcEvaluate = (reference, eventsPerStream, isNewData, context) => + { + return timerPeriodConst.DeltaAdd(reference); + }, + }; + return new ExprOptionalConstant(eval, null); + } + // has-month and not constant + else + { + ExprTimePeriodEvalDeltaNonConst timerPeriodNonConst = timePeriod.NonconstEvaluator(); + IntervalDeltaExprEvaluator eval = new ProxyIntervalDeltaExprEvaluator + { + ProcEvaluate = (reference, eventsPerStream, isNewData, context) => timerPeriodNonConst.DeltaAdd( + reference, eventsPerStream, isNewData, context), + }; + return new ExprOptionalConstant(eval, null); + } + } + } + else if (ExprNodeUtility.IsConstantValueExpr(exprNode)) + { + var constantNode = (ExprConstantNode)exprNode; + long l = constantNode.GetConstantValue(null).AsLong(); + IntervalDeltaExprEvaluator eval = new ProxyIntervalDeltaExprEvaluator + { + ProcEvaluate = (reference, eventsPerStream, isNewData, context) => l, + }; + return new ExprOptionalConstant(eval, l); + } + else + { + var evaluator = exprNode.ExprEvaluator; + IntervalDeltaExprEvaluator eval = new ProxyIntervalDeltaExprEvaluator + { + ProcEvaluate = (reference, eventsPerStream, isNewData, context) => evaluator.Evaluate( + new EvaluateParams(eventsPerStream, isNewData, context)).AsLong(), + }; + return new ExprOptionalConstant(eval, null); + } + } + + /// + /// After. + /// + public class IntervalComputerConstantAfter + : IntervalComputerConstantBase + , IntervalComputer + { + public IntervalComputerConstantAfter(IntervalStartEndParameterPair pair) + : base(pair, true) + { + } + + public bool? Compute( + long leftStart, + long leftEnd, + long rightStart, + long rightEnd, + EventBean[] eventsPerStream, + bool newData, + ExprEvaluatorContext context) + { + return ComputeInternal(leftStart, leftEnd, rightStart, rightEnd, Start, End); + } + + public static Boolean ComputeInternal( + long leftStart, + long leftEnd, + long rightStart, + long rightEnd, + long start, + long end) + { + var delta = leftStart - rightEnd; + return start <= delta && delta <= end; + } + } + + public class IntervalComputerAfterWithDeltaExpr : IntervalComputer + { + private readonly IntervalDeltaExprEvaluator _start; + private readonly IntervalDeltaExprEvaluator _finish; + + public IntervalComputerAfterWithDeltaExpr(IntervalStartEndParameterPair pair) + { + _start = pair.Start.Evaluator; + _finish = pair.End.Evaluator; + } + + public bool? Compute( + long leftStart, + long leftEnd, + long rightStart, + long rightEnd, + EventBean[] eventsPerStream, + bool newData, + ExprEvaluatorContext context) + { + var rangeStartDelta = _start.Evaluate(rightStart, eventsPerStream, newData, context); + var rangeEndDelta = _finish.Evaluate(rightStart, eventsPerStream, newData, context); + if (rangeStartDelta > rangeEndDelta) + { + return IntervalComputerConstantAfter.ComputeInternal( + leftStart, leftEnd, rightStart, rightEnd, rangeEndDelta, rangeStartDelta); + } + else + { + return IntervalComputerConstantAfter.ComputeInternal( + leftStart, leftEnd, rightStart, rightEnd, rangeStartDelta, rangeEndDelta); + } + } + } + + public class IntervalComputerAfterNoParam : IntervalComputer + { + public bool? Compute( + long leftStart, + long leftEnd, + long rightStart, + long rightEnd, + EventBean[] eventsPerStream, + bool newData, + ExprEvaluatorContext context) + { + return leftStart > rightEnd; + } + } + + /// + /// Before. + /// + public class IntervalComputerConstantBefore + : IntervalComputerConstantBase + , IntervalComputer + { + public IntervalComputerConstantBefore(IntervalStartEndParameterPair pair) + : base(pair, true) + { + } + + public bool? Compute( + long leftStart, + long leftEnd, + long rightStart, + long rightEnd, + EventBean[] eventsPerStream, + bool newData, + ExprEvaluatorContext context) + { + return ComputeInternal(leftStart, leftEnd, rightStart, Start, End); + } + + public static Boolean ComputeInternal(long left, long leftEnd, long right, long start, long end) + { + var delta = right - leftEnd; + return start <= delta && delta <= end; + } + } + + public class IntervalComputerBeforeWithDeltaExpr : IntervalComputer + { + private readonly IntervalDeltaExprEvaluator _start; + private readonly IntervalDeltaExprEvaluator _finish; + + public IntervalComputerBeforeWithDeltaExpr(IntervalStartEndParameterPair pair) + { + _start = pair.Start.Evaluator; + _finish = pair.End.Evaluator; + } + + public bool? Compute( + long leftStart, + long leftEnd, + long rightStart, + long rightEnd, + EventBean[] eventsPerStream, + bool newData, + ExprEvaluatorContext context) + { + var rangeStartDelta = _start.Evaluate(leftEnd, eventsPerStream, newData, context); + var rangeEndDelta = _finish.Evaluate(leftEnd, eventsPerStream, newData, context); + if (rangeStartDelta > rangeEndDelta) + { + return IntervalComputerConstantBefore.ComputeInternal( + leftStart, leftEnd, rightStart, rangeEndDelta, rangeStartDelta); + } + else + { + return IntervalComputerConstantBefore.ComputeInternal( + leftStart, leftEnd, rightStart, rangeStartDelta, rangeEndDelta); + } + } + } + + public class IntervalComputerBeforeNoParam : IntervalComputer + { + public bool? Compute( + long leftStart, + long leftEnd, + long rightStart, + long rightEnd, + EventBean[] eventsPerStream, + bool newData, + ExprEvaluatorContext context) + { + return leftEnd < rightStart; + } + } + + /// + /// Coincides. + /// + public class IntervalComputerConstantCoincides : IntervalComputer + { + protected readonly long Start; + protected readonly long End; + + public IntervalComputerConstantCoincides(IntervalStartEndParameterPair pair) + { + Start = pair.Start.OptionalConstant.GetValueOrDefault(); + End = pair.End.OptionalConstant.GetValueOrDefault(); + if (Start < 0 || End < 0) + { + throw new ExprValidationException( + "The coincides date-time method does not allow negative start and end values"); + } + } + + public bool? Compute( + long leftStart, + long leftEnd, + long rightStart, + long rightEnd, + EventBean[] eventsPerStream, + bool newData, + ExprEvaluatorContext context) + { + return ComputeInternal(leftStart, leftEnd, rightStart, rightEnd, Start, End); + } + + public static Boolean ComputeInternal( + long left, + long leftEnd, + long right, + long rightEnd, + long startThreshold, + long endThreshold) + { + return Math.Abs(left - right) <= startThreshold && + Math.Abs(leftEnd - rightEnd) <= endThreshold; + } + } + + public class IntervalComputerCoincidesWithDeltaExpr : IntervalComputer + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly IntervalDeltaExprEvaluator _start; + private readonly IntervalDeltaExprEvaluator _finish; + + public IntervalComputerCoincidesWithDeltaExpr(IntervalStartEndParameterPair pair) + { + _start = pair.Start.Evaluator; + _finish = pair.End.Evaluator; + } + + public bool? Compute( + long leftStart, + long leftEnd, + long rightStart, + long rightEnd, + EventBean[] eventsPerStream, + bool newData, + ExprEvaluatorContext context) + { + var startValue = _start.Evaluate(Math.Min(leftStart, rightStart), eventsPerStream, newData, context); + var endValue = _finish.Evaluate(Math.Min(leftEnd, rightEnd), eventsPerStream, newData, context); + + if (startValue < 0 || endValue < 0) + { + Log.Warn("The coincides date-time method does not allow negative start and end values"); + return null; + } + + return IntervalComputerConstantCoincides.ComputeInternal( + leftStart, leftEnd, rightStart, rightEnd, startValue, endValue); + } + } + + public class IntervalComputerCoincidesNoParam : IntervalComputer + { + public bool? Compute( + long leftStart, + long leftEnd, + long rightStart, + long rightEnd, + EventBean[] eventsPerStream, + bool newData, + ExprEvaluatorContext context) + { + return leftStart == rightStart && leftEnd == rightEnd; + } + } + + /// + /// During And Includes. + /// + public class IntervalComputerDuringNoParam : IntervalComputer + { + public bool? Compute( + long leftStart, + long leftEnd, + long rightStart, + long rightEnd, + EventBean[] eventsPerStream, + bool newData, + ExprEvaluatorContext context) + { + return rightStart < leftStart && leftEnd < rightEnd; + } + } + + public class IntervalComputerIncludesNoParam : IntervalComputer + { + + public bool? Compute( + long leftStart, + long leftEnd, + long rightStart, + long rightEnd, + EventBean[] eventsPerStream, + bool newData, + ExprEvaluatorContext context) + { + return leftStart < rightStart && rightEnd < leftEnd; + } + } + + public class IntervalComputerDuringAndIncludesThreshold : IntervalComputer + { + private readonly bool _during; + private readonly IntervalDeltaExprEvaluator _threshold; + + public IntervalComputerDuringAndIncludesThreshold(bool during, IntervalDeltaExprEvaluator threshold) + { + _during = during; + _threshold = threshold; + } + + public bool? Compute( + long leftStart, + long leftEnd, + long rightStart, + long rightEnd, + EventBean[] eventsPerStream, + bool newData, + ExprEvaluatorContext context) + { + + var thresholdValue = _threshold.Evaluate(leftStart, eventsPerStream, newData, context); + + if (_during) + { + var deltaStart = leftStart - rightStart; + if (deltaStart <= 0 || deltaStart > thresholdValue) + { + return false; + } + + var deltaEnd = rightEnd - leftEnd; + return !(deltaEnd <= 0 || deltaEnd > thresholdValue); + } + else + { + var deltaStart = rightStart - leftStart; + if (deltaStart <= 0 || deltaStart > thresholdValue) + { + return false; + } + + var deltaEnd = leftEnd - rightEnd; + return !(deltaEnd <= 0 || deltaEnd > thresholdValue); + } + } + } + + public class IntervalComputerDuringAndIncludesMinMax : IntervalComputer + { + private readonly bool _during; + private readonly IntervalDeltaExprEvaluator _minEval; + private readonly IntervalDeltaExprEvaluator _maxEval; + + public IntervalComputerDuringAndIncludesMinMax( + bool during, + IntervalDeltaExprEvaluator minEval, + IntervalDeltaExprEvaluator maxEval) + { + _during = during; + _minEval = minEval; + _maxEval = maxEval; + } + + public bool? Compute( + long leftStart, + long leftEnd, + long rightStart, + long rightEnd, + EventBean[] eventsPerStream, + bool newData, + ExprEvaluatorContext context) + { + var min = _minEval.Evaluate(leftStart, eventsPerStream, newData, context); + var max = _maxEval.Evaluate(rightEnd, eventsPerStream, newData, context); + if (_during) + { + return ComputeInternalDuring(leftStart, leftEnd, rightStart, rightEnd, min, max, min, max); + } + else + { + return ComputeInternalIncludes(leftStart, leftEnd, rightStart, rightEnd, min, max, min, max); + } + } + + public static bool ComputeInternalDuring( + long left, + long leftEnd, + long right, + long rightEnd, + long startMin, + long startMax, + long endMin, + long endMax) + { + if (startMin <= 0) + { + startMin = 1; + } + var deltaStart = left - right; + if (deltaStart < startMin || deltaStart > startMax) + { + return false; + } + + var deltaEnd = rightEnd - leftEnd; + return !(deltaEnd < endMin || deltaEnd > endMax); + } + + public static bool ComputeInternalIncludes( + long left, + long leftEnd, + long right, + long rightEnd, + long startMin, + long startMax, + long endMin, + long endMax) + { + if (startMin <= 0) + { + startMin = 1; + } + var deltaStart = right - left; + if (deltaStart < startMin || deltaStart > startMax) + { + return false; + } + + var deltaEnd = leftEnd - rightEnd; + return !(deltaEnd < endMin || deltaEnd > endMax); + } + } + + public class IntervalComputerDuringMinMaxStartEnd : IntervalComputer + { + private readonly bool _during; + private readonly IntervalDeltaExprEvaluator _minStartEval; + private readonly IntervalDeltaExprEvaluator _maxStartEval; + private readonly IntervalDeltaExprEvaluator _minEndEval; + private readonly IntervalDeltaExprEvaluator _maxEndEval; + + public IntervalComputerDuringMinMaxStartEnd(bool during, IntervalDeltaExprEvaluator[] parameters) + { + _during = during; + _minStartEval = parameters[0]; + _maxStartEval = parameters[1]; + _minEndEval = parameters[2]; + _maxEndEval = parameters[3]; + } + + public bool? Compute( + long leftStart, + long leftEnd, + long rightStart, + long rightEnd, + EventBean[] eventsPerStream, + bool newData, + ExprEvaluatorContext context) + { + + var minStart = _minStartEval.Evaluate(rightStart, eventsPerStream, newData, context); + var maxStart = _maxStartEval.Evaluate(rightStart, eventsPerStream, newData, context); + var minEnd = _minEndEval.Evaluate(rightEnd, eventsPerStream, newData, context); + var maxEnd = _maxEndEval.Evaluate(rightEnd, eventsPerStream, newData, context); + + if (_during) + { + return IntervalComputerDuringAndIncludesMinMax.ComputeInternalDuring( + leftStart, leftEnd, rightStart, rightEnd, minStart, maxStart, minEnd, maxEnd); + } + else + { + return IntervalComputerDuringAndIncludesMinMax.ComputeInternalIncludes( + leftStart, leftEnd, rightStart, rightEnd, minStart, maxStart, minEnd, maxEnd); + } + } + } + + /// + /// Finishes. + /// + public class IntervalComputerFinishesNoParam : IntervalComputer + { + public bool? Compute( + long leftStart, + long leftEnd, + long rightStart, + long rightEnd, + EventBean[] eventsPerStream, + bool newData, + ExprEvaluatorContext context) + { + return rightStart < leftStart && (leftEnd == rightEnd); + } + } + + public class IntervalComputerFinishesThreshold : IntervalComputer + { + private static readonly ILog Log = + LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly IntervalDeltaExprEvaluator _thresholdExpr; + + public IntervalComputerFinishesThreshold(IntervalDeltaExprEvaluator thresholdExpr) + { + _thresholdExpr = thresholdExpr; + } + + public bool? Compute( + long leftStart, + long leftEnd, + long rightStart, + long rightEnd, + EventBean[] eventsPerStream, + bool newData, + ExprEvaluatorContext context) + { + + var threshold = _thresholdExpr.Evaluate(Math.Min(leftEnd, rightEnd), eventsPerStream, newData, context); + + if (threshold < 0) + { + Log.Warn("The 'finishes' date-time method does not allow negative threshold"); + return null; + } + + if (rightStart >= leftStart) + { + return false; + } + var delta = Math.Abs(leftEnd - rightEnd); + return delta <= threshold; + } + } + + /// + /// Finishes-By. + /// + public class IntervalComputerFinishedByNoParam : IntervalComputer + { + public bool? Compute( + long leftStart, + long leftEnd, + long rightStart, + long rightEnd, + EventBean[] eventsPerStream, + bool newData, + ExprEvaluatorContext context) + { + return leftStart < rightStart && (leftEnd == rightEnd); + } + } + + public class IntervalComputerFinishedByThreshold : IntervalComputer + { + private static readonly ILog Log = + LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly IntervalDeltaExprEvaluator _thresholdExpr; + + public IntervalComputerFinishedByThreshold(IntervalDeltaExprEvaluator thresholdExpr) + { + _thresholdExpr = thresholdExpr; + } + + public bool? Compute( + long leftStart, + long leftEnd, + long rightStart, + long rightEnd, + EventBean[] eventsPerStream, + bool newData, + ExprEvaluatorContext context) + { + + var threshold = _thresholdExpr.Evaluate(Math.Min(rightEnd, leftEnd), eventsPerStream, newData, context); + if (threshold < 0) + { + Log.Warn("The 'finishes' date-time method does not allow negative threshold"); + return null; + } + + if (leftStart >= rightStart) + { + return false; + } + var delta = Math.Abs(leftEnd - rightEnd); + return delta <= threshold; + } + } + + /// + /// Meets. + /// + public class IntervalComputerMeetsNoParam : IntervalComputer + { + public bool? Compute( + long leftStart, + long leftEnd, + long rightStart, + long rightEnd, + EventBean[] eventsPerStream, + bool newData, + ExprEvaluatorContext context) + { + return leftEnd == rightStart; + } + } + + public class IntervalComputerMeetsThreshold : IntervalComputer + { + private static readonly ILog Log = + LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly IntervalDeltaExprEvaluator _thresholdExpr; + + public IntervalComputerMeetsThreshold(IntervalDeltaExprEvaluator thresholdExpr) + { + _thresholdExpr = thresholdExpr; + } + + public bool? Compute( + long leftStart, + long leftEnd, + long rightStart, + long rightEnd, + EventBean[] eventsPerStream, + bool newData, + ExprEvaluatorContext context) + { + var threshold = _thresholdExpr.Evaluate( + Math.Min(leftEnd, rightStart), eventsPerStream, newData, context); + if (threshold < 0) + { + Log.Warn("The 'finishes' date-time method does not allow negative threshold"); + return null; + } + + var delta = Math.Abs(rightStart - leftEnd); + return delta <= threshold; + } + } + + /// + /// Met-By. + /// + public class IntervalComputerMetByNoParam : IntervalComputer + { + public bool? Compute( + long leftStart, + long leftEnd, + long rightStart, + long rightEnd, + EventBean[] eventsPerStream, + bool newData, + ExprEvaluatorContext context) + { + return rightEnd == leftStart; + } + } + + public class IntervalComputerMetByThreshold : IntervalComputer + { + private static readonly ILog Log = + LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly IntervalDeltaExprEvaluator _thresholdExpr; + + public IntervalComputerMetByThreshold(IntervalDeltaExprEvaluator thresholdExpr) + { + _thresholdExpr = thresholdExpr; + } + + public bool? Compute( + long leftStart, + long leftEnd, + long rightStart, + long rightEnd, + EventBean[] eventsPerStream, + bool newData, + ExprEvaluatorContext context) + { + + var threshold = _thresholdExpr.Evaluate( + Math.Min(leftStart, rightEnd), eventsPerStream, newData, context); + + if (threshold < 0) + { + Log.Warn("The 'finishes' date-time method does not allow negative threshold"); + return null; + } + + var delta = Math.Abs(leftStart - rightEnd); + return delta <= threshold; + } + } + + /// + /// Overlaps. + /// + public class IntervalComputerOverlapsNoParam : IntervalComputer + { + public bool? Compute( + long leftStart, + long leftEnd, + long rightStart, + long rightEnd, + EventBean[] eventsPerStream, + bool newData, + ExprEvaluatorContext context) + { + return (leftStart < rightStart) && + (rightStart < leftEnd) && + (leftEnd < rightEnd); + } + } + + public class IntervalComputerOverlapsAndByThreshold : IntervalComputer + { + private readonly bool _overlaps; + private readonly IntervalDeltaExprEvaluator _thresholdExpr; + + public IntervalComputerOverlapsAndByThreshold(bool overlaps, IntervalDeltaExprEvaluator thresholdExpr) + { + _overlaps = overlaps; + _thresholdExpr = thresholdExpr; + } + + public bool? Compute( + long leftStart, + long leftEnd, + long rightStart, + long rightEnd, + EventBean[] eventsPerStream, + bool newData, + ExprEvaluatorContext context) + { + + if (_overlaps) + { + var threshold = _thresholdExpr.Evaluate(leftStart, eventsPerStream, newData, context); + return ComputeInternalOverlaps(leftStart, leftEnd, rightStart, rightEnd, 0, threshold); + } + else + { + var threshold = _thresholdExpr.Evaluate(rightStart, eventsPerStream, newData, context); + return ComputeInternalOverlaps(rightStart, rightEnd, leftStart, leftEnd, 0, threshold); + } + } + + public static bool ComputeInternalOverlaps( + long left, + long leftEnd, + long right, + long rightEnd, + long min, + long max) + { + var match = ((left < right) && + (right < leftEnd) && + (leftEnd < rightEnd)); + if (!match) + { + return false; + } + var delta = leftEnd - right; + return min <= delta && delta <= max; + } + } + + public class IntervalComputerOverlapsAndByMinMax : IntervalComputer + { + private readonly bool _overlaps; + private readonly IntervalDeltaExprEvaluator _minEval; + private readonly IntervalDeltaExprEvaluator _maxEval; + + public IntervalComputerOverlapsAndByMinMax( + bool overlaps, + IntervalDeltaExprEvaluator minEval, + IntervalDeltaExprEvaluator maxEval) + { + _overlaps = overlaps; + _minEval = minEval; + _maxEval = maxEval; + } + + public bool? Compute( + long leftStart, + long leftEnd, + long rightStart, + long rightEnd, + EventBean[] eventsPerStream, + bool newData, + ExprEvaluatorContext context) + { + + if (_overlaps) + { + var minThreshold = _minEval.Evaluate(leftStart, eventsPerStream, newData, context); + var maxThreshold = _maxEval.Evaluate(leftEnd, eventsPerStream, newData, context); + return IntervalComputerOverlapsAndByThreshold.ComputeInternalOverlaps( + leftStart, leftEnd, rightStart, rightEnd, minThreshold, maxThreshold); + } + else + { + var minThreshold = _minEval.Evaluate(rightStart, eventsPerStream, newData, context); + var maxThreshold = _maxEval.Evaluate(rightEnd, eventsPerStream, newData, context); + return IntervalComputerOverlapsAndByThreshold.ComputeInternalOverlaps( + rightStart, rightEnd, leftStart, leftEnd, minThreshold, maxThreshold); + } + } + } + + /// + /// OverlappedBy. + /// + public class IntervalComputerOverlappedByNoParam : IntervalComputer + { + public bool? Compute( + long leftStart, + long leftEnd, + long rightStart, + long rightEnd, + EventBean[] eventsPerStream, + bool newData, + ExprEvaluatorContext context) + { + return (rightStart < leftStart) && + (leftStart < rightEnd) && + (rightEnd < leftEnd); + } + } + + /// + /// Starts. + /// + public class IntervalComputerStartsNoParam : IntervalComputer + { + public bool? Compute( + long leftStart, + long leftEnd, + long rightStart, + long rightEnd, + EventBean[] eventsPerStream, + bool newData, + ExprEvaluatorContext context) + { + return (leftStart == rightStart) && (leftEnd < rightEnd); + } + } + + public class IntervalComputerStartsThreshold : IntervalComputer + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly IntervalDeltaExprEvaluator _thresholdExpr; + + public IntervalComputerStartsThreshold(IntervalDeltaExprEvaluator thresholdExpr) + { + _thresholdExpr = thresholdExpr; + } + + public bool? Compute( + long leftStart, + long leftEnd, + long rightStart, + long rightEnd, + EventBean[] eventsPerStream, + bool newData, + ExprEvaluatorContext context) + { + + var threshold = _thresholdExpr.Evaluate( + Math.Min(leftStart, rightStart), eventsPerStream, newData, context); + if (threshold < 0) + { + Log.Warn("The 'finishes' date-time method does not allow negative threshold"); + return null; + } + + var delta = Math.Abs(leftStart - rightStart); + return delta <= threshold && (leftEnd < rightEnd); + } + } + + /// + /// Started-by. + /// + public class IntervalComputerStartedByNoParam : IntervalComputer + { + + public bool? Compute( + long leftStart, + long leftEnd, + long rightStart, + long rightEnd, + EventBean[] eventsPerStream, + bool newData, + ExprEvaluatorContext context) + { + return (leftStart == rightStart) && (leftEnd > rightEnd); + } + } + + public class IntervalComputerStartedByThreshold : IntervalComputer + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly IntervalDeltaExprEvaluator _thresholdExpr; + + public IntervalComputerStartedByThreshold(IntervalDeltaExprEvaluator thresholdExpr) + { + _thresholdExpr = thresholdExpr; + } + + public bool? Compute( + long leftStart, + long leftEnd, + long rightStart, + long rightEnd, + EventBean[] eventsPerStream, + bool newData, + ExprEvaluatorContext context) + { + + var threshold = _thresholdExpr.Evaluate( + Math.Min(leftStart, rightStart), eventsPerStream, newData, context); + if (threshold < 0) + { + Log.Warn("The 'finishes' date-time method does not allow negative threshold"); + return null; + } + + var delta = Math.Abs(leftStart - rightStart); + return delta <= threshold && (leftEnd > rightEnd); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/interval/IntervalDeltaExprEvaluator.cs b/NEsper.Core/NEsper.Core/epl/datetime/interval/IntervalDeltaExprEvaluator.cs new file mode 100755 index 000000000..101b926d1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/interval/IntervalDeltaExprEvaluator.cs @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.datetime.interval +{ + public interface IntervalDeltaExprEvaluator + { + long Evaluate(long reference, EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context); + } + + public class ProxyIntervalDeltaExprEvaluator : IntervalDeltaExprEvaluator + { + public Func ProcEvaluate { get; set; } + + public long Evaluate(long reference, EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + return ProcEvaluate.Invoke( + reference, eventsPerStream, isNewData, context); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/interval/IntervalOp.cs b/NEsper.Core/NEsper.Core/epl/datetime/interval/IntervalOp.cs new file mode 100755 index 000000000..747112965 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/interval/IntervalOp.cs @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.datetime.eval; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.dot; + +namespace com.espertech.esper.epl.datetime.interval +{ + public interface IntervalOp + { + Object Evaluate(long startTs, long endTs, EvaluateParams evaluateParams); + + ExprDotNodeFilterAnalyzerDTIntervalDesc GetFilterDesc( + EventType[] typesPerStream, + DatetimeMethodEnum currentMethod, + IList currentParameters, + ExprDotNodeFilterAnalyzerInput inputDesc); + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/datetime/interval/IntervalOpFactory.cs b/NEsper.Core/NEsper.Core/epl/datetime/interval/IntervalOpFactory.cs new file mode 100755 index 000000000..5ee61752f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/interval/IntervalOpFactory.cs @@ -0,0 +1,33 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.datetime.eval; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.time; + +namespace com.espertech.esper.epl.datetime.interval +{ + public class IntervalOpFactory : OpFactory + { + public IntervalOp GetOp( + StreamTypeService streamTypeService, + DatetimeMethodEnum method, + string methodNameUsed, + IList parameters, + TimeZoneInfo timeZone, + TimeAbacus timeAbacus) + { + + return new IntervalOpImpl(method, methodNameUsed, streamTypeService, parameters, timeZone, timeAbacus); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/datetime/interval/IntervalOpImpl.cs b/NEsper.Core/NEsper.Core/epl/datetime/interval/IntervalOpImpl.cs new file mode 100755 index 000000000..c5b6a5f52 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/interval/IntervalOpImpl.cs @@ -0,0 +1,371 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.datetime.eval; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.dot; +using com.espertech.esper.epl.expression.time; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.datetime.interval +{ + public class IntervalOpImpl : IntervalOp + { + private readonly ExprEvaluator _evaluatorTimestamp; + + private readonly int _parameterStreamNum; + private readonly String _parameterPropertyStart; + private readonly String _parameterPropertyEnd; + + private readonly IntervalOpEval _intervalOpEval; + + public IntervalOpImpl( + DatetimeMethodEnum method, + String methodNameUse, + StreamTypeService streamTypeService, + IList expressions, + TimeZoneInfo timeZone, + TimeAbacus timeAbacus) + { + ExprEvaluator evaluatorEndTimestamp = null; + Type timestampType; + + if (expressions[0] is ExprStreamUnderlyingNode) + { + var und = (ExprStreamUnderlyingNode)expressions[0]; + _parameterStreamNum = und.StreamId; + EventType type = streamTypeService.EventTypes[_parameterStreamNum]; + _parameterPropertyStart = type.StartTimestampPropertyName; + if (_parameterPropertyStart == null) + { + throw new ExprValidationException("For date-time method '" + methodNameUse + "' the first parameter is event type '" + type.Name + "', however no timestamp property has been defined for this event type"); + } + + timestampType = type.GetPropertyType(_parameterPropertyStart); + EventPropertyGetter getter = type.GetGetter(_parameterPropertyStart); + _evaluatorTimestamp = new ExprEvaluatorStreamLongProp(_parameterStreamNum, getter); + + if (type.EndTimestampPropertyName != null) + { + _parameterPropertyEnd = type.EndTimestampPropertyName; + EventPropertyGetter getterEndTimestamp = type.GetGetter(type.EndTimestampPropertyName); + evaluatorEndTimestamp = new ExprEvaluatorStreamLongProp(_parameterStreamNum, getterEndTimestamp); + } + else + { + _parameterPropertyEnd = _parameterPropertyStart; + } + } + else + { + _evaluatorTimestamp = expressions[0].ExprEvaluator; + timestampType = _evaluatorTimestamp.ReturnType; + + String unresolvedPropertyName = null; + if (expressions[0] is ExprIdentNode) + { + var identNode = (ExprIdentNode)expressions[0]; + _parameterStreamNum = identNode.StreamId; + _parameterPropertyStart = identNode.ResolvedPropertyName; + _parameterPropertyEnd = _parameterPropertyStart; + unresolvedPropertyName = identNode.UnresolvedPropertyName; + } + + if (!_evaluatorTimestamp.ReturnType.IsDateTime()) + { + // ident node may represent a fragment + if (unresolvedPropertyName != null) + { + var propertyDesc = ExprIdentNodeUtil.GetTypeFromStream( + streamTypeService, unresolvedPropertyName, false, true); + if (propertyDesc.First.FragmentEventType != null) + { + EventType type = propertyDesc.First.FragmentEventType.FragmentType; + _parameterPropertyStart = type.StartTimestampPropertyName; + if (_parameterPropertyStart == null) + { + throw new ExprValidationException("For date-time method '" + methodNameUse + "' the first parameter is event type '" + type.Name + "', however no timestamp property has been defined for this event type"); + } + + timestampType = type.GetPropertyType(_parameterPropertyStart); + EventPropertyGetter getterFragment = streamTypeService.EventTypes[_parameterStreamNum].GetGetter(unresolvedPropertyName); + EventPropertyGetter getterStartTimestamp = type.GetGetter(_parameterPropertyStart); + _evaluatorTimestamp = new ExprEvaluatorStreamLongPropFragment(_parameterStreamNum, getterFragment, getterStartTimestamp); + + if (type.EndTimestampPropertyName != null) + { + _parameterPropertyEnd = type.EndTimestampPropertyName; + EventPropertyGetter getterEndTimestamp = type.GetGetter(type.EndTimestampPropertyName); + evaluatorEndTimestamp = new ExprEvaluatorStreamLongPropFragment(_parameterStreamNum, getterFragment, getterEndTimestamp); + } + else + { + _parameterPropertyEnd = _parameterPropertyStart; + } + } + } + else + { + throw new ExprValidationException("For date-time method '" + methodNameUse + "' the first parameter expression returns '" + _evaluatorTimestamp.ReturnType.FullName + "', however requires a DateTime or Long-type return value or event (with timestamp)"); + } + } + } + + IntervalComputer intervalComputer = IntervalComputerFactory.Make(method, expressions, timeAbacus); + + // evaluation without end timestamp + var timestampTypeBoxed = timestampType != null ? timestampType.GetBoxedType() : timestampType; + if (evaluatorEndTimestamp == null) + { + if (timestampTypeBoxed == typeof(DateTime?) || timestampTypeBoxed == typeof(DateTimeOffset?)) + { + _intervalOpEval = new IntervalOpEvalCal(intervalComputer); + } + else if (timestampTypeBoxed == typeof(DateTimeEx)) + { + _intervalOpEval = new IntervalOpEvalDateTimeEx(intervalComputer); + } + else if (timestampTypeBoxed == typeof(long?)) + { + _intervalOpEval = new IntervalOpEvalLong(intervalComputer); + } + else + { + throw new ArgumentException("Invalid interval first parameter type '" + timestampType + "'"); + } + } + else + { + if (timestampTypeBoxed == typeof(DateTime?) || timestampTypeBoxed == typeof(DateTimeOffset?)) + { + _intervalOpEval = new IntervalOpEvalCalWithEnd(intervalComputer, evaluatorEndTimestamp); + } + else if (timestampTypeBoxed == typeof(DateTimeEx)) + { + _intervalOpEval = new IntervalOpEvalDateTimeExWithEnd(intervalComputer, evaluatorEndTimestamp); + } + else if (timestampTypeBoxed == typeof(long?)) + { + _intervalOpEval = new IntervalOpEvalLongWithEnd(intervalComputer, evaluatorEndTimestamp); + } + else + { + throw new ArgumentException("Invalid interval first parameter type '" + timestampType + "'"); + } + } + } + + /// + /// Obtain information used by filter analyzer to handle this dot-method invocation as part of query planning/indexing. + /// + /// The types per stream. + /// The current method. + /// The current parameters. + /// The input desc. + /// + public ExprDotNodeFilterAnalyzerDTIntervalDesc GetFilterDesc( + EventType[] typesPerStream, + DatetimeMethodEnum currentMethod, + IList currentParameters, + ExprDotNodeFilterAnalyzerInput inputDesc) + { + // with intervals is not currently query planned + if (currentParameters.Count > 1) + { + return null; + } + + // Get input (target) + int targetStreamNum; + String targetPropertyStart; + String targetPropertyEnd; + if (inputDesc is ExprDotNodeFilterAnalyzerInputStream) + { + var targetStream = (ExprDotNodeFilterAnalyzerInputStream)inputDesc; + targetStreamNum = targetStream.StreamNum; + EventType targetType = typesPerStream[targetStreamNum]; + targetPropertyStart = targetType.StartTimestampPropertyName; + targetPropertyEnd = targetType.EndTimestampPropertyName ?? targetPropertyStart; + } + else if (inputDesc is ExprDotNodeFilterAnalyzerInputProp) + { + var targetStream = (ExprDotNodeFilterAnalyzerInputProp)inputDesc; + targetStreamNum = targetStream.StreamNum; + targetPropertyStart = targetStream.PropertyName; + targetPropertyEnd = targetStream.PropertyName; + } + else + { + return null; + } + + // check parameter info + if (_parameterPropertyStart == null) + { + return null; + } + + return new ExprDotNodeFilterAnalyzerDTIntervalDesc(currentMethod, typesPerStream, + targetStreamNum, targetPropertyStart, targetPropertyEnd, + _parameterStreamNum, _parameterPropertyStart, _parameterPropertyEnd); + } + + public object Evaluate(long startTs, long endTs, EvaluateParams evaluateParams) + { + var parameter = _evaluatorTimestamp.Evaluate(evaluateParams); + if (parameter == null) + { + return parameter; + } + + return _intervalOpEval.Evaluate(startTs, endTs, parameter, evaluateParams.EventsPerStream, evaluateParams.IsNewData, evaluateParams.ExprEvaluatorContext); + } + + public interface IntervalOpEval + { + Object Evaluate(long startTs, long endTs, Object parameter, EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context); + } + + public abstract class IntervalOpEvalDateBase : IntervalOpEval + { + protected readonly IntervalComputer IntervalComputer; + + public abstract object Evaluate(long startTs, + long endTs, + object parameter, + EventBean[] eventsPerStream, + bool isNewData, + ExprEvaluatorContext context); + + public IntervalOpEvalDateBase(IntervalComputer intervalComputer) + { + IntervalComputer = intervalComputer; + } + } + + public class IntervalOpEvalLong : IntervalOpEvalDateBase + { + public IntervalOpEvalLong(IntervalComputer intervalComputer) + : base(intervalComputer) + { + } + + public override Object Evaluate(long startTs, long endTs, Object parameter, EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + var time = ((long?)parameter).GetValueOrDefault(); + return IntervalComputer.Compute(startTs, endTs, time, time, eventsPerStream, isNewData, context); + } + } + + public class IntervalOpEvalCal : IntervalOpEvalDateBase + { + public IntervalOpEvalCal(IntervalComputer intervalComputer) + : base(intervalComputer) + { + } + + public override Object Evaluate(long startTs, long endTs, Object parameter, EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + long time = parameter.AsDateTimeOffset().TimeInMillis(); + return IntervalComputer.Compute(startTs, endTs, time, time, eventsPerStream, isNewData, context); + } + } + + public class IntervalOpEvalDateTimeEx : IntervalOpEvalDateBase + { + public IntervalOpEvalDateTimeEx(IntervalComputer intervalComputer) + : base(intervalComputer) + { + } + + public override Object Evaluate(long startTs, long endTs, Object parameter, EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + var time = ((DateTimeEx) parameter).TimeInMillis; + return IntervalComputer.Compute(startTs, endTs, time, time, eventsPerStream, isNewData, context); + } + } + + public abstract class IntervalOpEvalDateWithEndBase : IntervalOpEval + { + protected readonly IntervalComputer IntervalComputer; + private readonly ExprEvaluator _evaluatorEndTimestamp; + + protected IntervalOpEvalDateWithEndBase(IntervalComputer intervalComputer, ExprEvaluator evaluatorEndTimestamp) + { + IntervalComputer = intervalComputer; + _evaluatorEndTimestamp = evaluatorEndTimestamp; + } + + public abstract Object Evaluate(long startTs, long endTs, Object parameterStartTs, Object parameterEndTs, EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context); + + public Object Evaluate(long startTs, long endTs, Object parameterStartTs, EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + Object paramEndTs = _evaluatorEndTimestamp.Evaluate(new EvaluateParams(eventsPerStream, isNewData, context)); + if (paramEndTs == null) + { + return null; + } + return Evaluate(startTs, endTs, parameterStartTs, paramEndTs, eventsPerStream, isNewData, context); + } + } + + public class IntervalOpEvalLongWithEnd : IntervalOpEvalDateWithEndBase + { + + public IntervalOpEvalLongWithEnd(IntervalComputer intervalComputer, ExprEvaluator evaluatorEndTimestamp) + : base(intervalComputer, evaluatorEndTimestamp) + { + } + + public override Object Evaluate(long startTs, long endTs, Object parameterStartTs, Object parameterEndTs, EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + return IntervalComputer.Compute(startTs, endTs, parameterStartTs.AsLong(), parameterEndTs.AsLong(), eventsPerStream, isNewData, context); + } + } + + public class IntervalOpEvalCalWithEnd : IntervalOpEvalDateWithEndBase + { + public IntervalOpEvalCalWithEnd(IntervalComputer intervalComputer, ExprEvaluator evaluatorEndTimestamp) + : base(intervalComputer, evaluatorEndTimestamp) + { + } + + public override Object Evaluate(long startTs, long endTs, Object parameterStartTs, Object parameterEndTs, EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + return IntervalComputer.Compute( + startTs, endTs, + parameterStartTs.AsDateTimeOffset().TimeInMillis(), + parameterEndTs.AsDateTimeOffset().TimeInMillis(), + eventsPerStream, isNewData, context); + } + } + + public class IntervalOpEvalDateTimeExWithEnd : IntervalOpEvalDateWithEndBase + { + public IntervalOpEvalDateTimeExWithEnd(IntervalComputer intervalComputer, ExprEvaluator evaluatorEndTimestamp) + : base(intervalComputer, evaluatorEndTimestamp) + { + } + + public override Object Evaluate(long startTs, long endTs, Object parameterStartTs, Object parameterEndTs, EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + return IntervalComputer.Compute( + startTs, endTs, + ((DateTimeEx) parameterStartTs).TimeInMillis, + ((DateTimeEx) parameterEndTs).TimeInMillis, + eventsPerStream, isNewData, context); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/interval/IntervalStartEndParameterPair.cs b/NEsper.Core/NEsper.Core/epl/datetime/interval/IntervalStartEndParameterPair.cs new file mode 100755 index 000000000..683a26e91 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/interval/IntervalStartEndParameterPair.cs @@ -0,0 +1,47 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.datetime.interval +{ + public class IntervalStartEndParameterPair { + private readonly ExprOptionalConstant _start; + private readonly ExprOptionalConstant _end; + + private IntervalStartEndParameterPair(ExprOptionalConstant start, ExprOptionalConstant end) { + _start = start; + _end = end; + } + + public static IntervalStartEndParameterPair FromParamsWithSameEnd(ExprOptionalConstant[] paramList) { + ExprOptionalConstant start = paramList[0]; + ExprOptionalConstant end = paramList.Length == 1 ? start : paramList[1]; + return new IntervalStartEndParameterPair(start, end); + } + + public static IntervalStartEndParameterPair FromParamsWithLongMaxEnd(ExprOptionalConstant[] paramList) { + ExprOptionalConstant start = paramList[0]; + ExprOptionalConstant end = paramList.Length == 1 ? ExprOptionalConstant.Make(long.MaxValue) : paramList[1]; + return new IntervalStartEndParameterPair(start, end); + } + + public ExprOptionalConstant Start + { + get { return _start; } + } + + public ExprOptionalConstant End + { + get { return _end; } + } + + public bool IsConstant + { + get { return _start.OptionalConstant != null && _end.OptionalConstant != null; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/reformatop/CalendarEvalStatics.cs b/NEsper.Core/NEsper.Core/epl/datetime/reformatop/CalendarEvalStatics.cs new file mode 100755 index 000000000..9ccc6203c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/reformatop/CalendarEvalStatics.cs @@ -0,0 +1,44 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; + +namespace com.espertech.esper.epl.datetime.reformatop +{ + using CalendarIntEval = Func; + using CalendarDowEval = Func; + + public class CalendarEvalStatics + { + public static readonly CalendarIntEval MINUTE_OF_HOUR = dtx => dtx.Minute; + + public static readonly CalendarIntEval MONTH_OF_YEAR = dtx => dtx.Month; + + public static readonly CalendarIntEval DAY_OF_MONTH = dtx => dtx.Day; + + public static readonly CalendarDowEval DAY_OF_WEEK = dtx => dtx.DayOfWeek; + + public static readonly CalendarIntEval DAY_OF_YEAR = dtx => dtx.DayOfYear; + + public static readonly CalendarIntEval ERA = dtx => { + throw new NotSupportedException(); + }; + + public static readonly CalendarIntEval HOUR_OF_DAY = dtx => dtx.Hour; + + public static readonly CalendarIntEval MILLIS_OF_SECOND = dtx => dtx.Millisecond; + + public static readonly CalendarIntEval SECOND_OF_MINUTE = dtx => dtx.Second; + + public static readonly CalendarIntEval WEEKYEAR = dtx => dtx.DateTime.GetWeekOfYear(); + + public static readonly CalendarIntEval YEAR = dtx => dtx.Year; + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/datetime/reformatop/DateTimeEval.cs b/NEsper.Core/NEsper.Core/epl/datetime/reformatop/DateTimeEval.cs new file mode 100755 index 000000000..3a7a0529a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/reformatop/DateTimeEval.cs @@ -0,0 +1,14 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.datetime.reformatop +{ + public delegate Object DateTimeEval(DateTimeOffset cal); +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/reformatop/DateTimeEvalStatics.cs b/NEsper.Core/NEsper.Core/epl/datetime/reformatop/DateTimeEvalStatics.cs new file mode 100755 index 000000000..018aeff25 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/reformatop/DateTimeEvalStatics.cs @@ -0,0 +1,36 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.compat; + +namespace com.espertech.esper.epl.datetime.reformatop +{ + public class DateTimeEvalStatics + { + public static DateTimeEval MinuteOfHour = + dateTime => dateTime.Minute; + public static DateTimeEval MonthOfYear = + dateTime => dateTime.Month; + public static DateTimeEval DayOfMonth = + dateTime => dateTime.Day; + public static DateTimeEval DayOfWeek = + dateTime => dateTime.DayOfWeek; + public static DateTimeEval DayOfYear = + dateTime => dateTime.DayOfYear; + public static DateTimeEval HourOfDay = + dateTime => dateTime.Hour; + public static DateTimeEval MillisOfSecond = + dateTime => dateTime.Millisecond; + public static DateTimeEval SecondOfMinute = + dateTime => dateTime.Second; + public static DateTimeEval Year = + dateTime => dateTime.Year; + public static DateTimeEval Weekyear = + dateTime => dateTime.GetWeekOfYear(); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/reformatop/LocalDateTimeEval.cs b/NEsper.Core/NEsper.Core/epl/datetime/reformatop/LocalDateTimeEval.cs new file mode 100755 index 000000000..df1dcd173 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/reformatop/LocalDateTimeEval.cs @@ -0,0 +1,14 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.compat; + +namespace com.espertech.esper.epl.datetime.reformatop +{ + public delegate object LocalDateTimeEval(DateTimeEx dateTimeEx); +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/datetime/reformatop/ReformatOp.cs b/NEsper.Core/NEsper.Core/epl/datetime/reformatop/ReformatOp.cs new file mode 100755 index 000000000..41f473091 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/reformatop/ReformatOp.cs @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.datetime.eval; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.dot; + +namespace com.espertech.esper.epl.datetime.reformatop +{ + public interface ReformatOp + { + Object Evaluate(long ts, EventBean[] eventsPerStream, bool newData, ExprEvaluatorContext exprEvaluatorContext); + + Object Evaluate( + DateTime d, + EventBean[] eventsPerStream, + bool newData, + ExprEvaluatorContext exprEvaluatorContext); + + Object Evaluate( + DateTimeOffset d, + EventBean[] eventsPerStream, + bool newData, + ExprEvaluatorContext exprEvaluatorContext); + + Object Evaluate( + DateTimeEx dtx, + EventBean[] eventsPerStream, + bool newData, + ExprEvaluatorContext exprEvaluatorContext); + + Type ReturnType { get; } + + ExprDotNodeFilterAnalyzerDesc GetFilterDesc( + EventType[] typesPerStream, + DatetimeMethodEnum currentMethod, + IList currentParameters, + ExprDotNodeFilterAnalyzerInput inputDesc); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/datetime/reformatop/ReformatOpBetweenConstantParams.cs b/NEsper.Core/NEsper.Core/epl/datetime/reformatop/ReformatOpBetweenConstantParams.cs new file mode 100755 index 000000000..db8751043 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/reformatop/ReformatOpBetweenConstantParams.cs @@ -0,0 +1,135 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.datetime.eval; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.dot; + +namespace com.espertech.esper.epl.datetime.reformatop +{ + public class ReformatOpBetweenConstantParams : ReformatOp + { + private readonly long _first; + private readonly long _second; + private readonly TimeZoneInfo _timeZone; + + public ReformatOpBetweenConstantParams(IList parameters, TimeZoneInfo timeZone) + { + this._timeZone = timeZone; + + long paramFirst = GetLongValue(parameters[0]); + long paramSecond = GetLongValue(parameters[1]); + + if (paramFirst > paramSecond) + { + _second = paramFirst; + _first = paramSecond; + } + else + { + _first = paramFirst; + _second = paramSecond; + } + if (parameters.Count > 2) + { + if (!GetBooleanValue(parameters[2])) + { + _first = _first + 1; + } + if (!GetBooleanValue(parameters[3])) + { + _second = _second - 1; + } + } + } + + private long GetLongValue(ExprNode exprNode) + { + var value = exprNode.ExprEvaluator.Evaluate(new EvaluateParams(null, true, null)); + if (value == null) + { + throw new ExprValidationException("Date-time method 'between' requires non-null parameter values"); + } + return DatetimeLongCoercerFactory.GetCoercer(value.GetType(), _timeZone).Coerce(value); + } + + private bool GetBooleanValue(ExprNode exprNode) + { + var value = exprNode.ExprEvaluator.Evaluate(new EvaluateParams(null, true, null)); + if (value == null) + { + throw new ExprValidationException("Date-time method 'between' requires non-null parameter values"); + } + return (bool) value; + } + + public Object Evaluate( + long ts, + EventBean[] eventsPerStream, + bool newData, + ExprEvaluatorContext exprEvaluatorContext) + { + return EvaluateInternal(ts); + } + + public Object Evaluate( + DateTime d, + EventBean[] eventsPerStream, + bool newData, + ExprEvaluatorContext exprEvaluatorContext) + { + return EvaluateInternal(d.UtcMillis()); + } + + public Object Evaluate( + DateTimeOffset d, + EventBean[] eventsPerStream, + bool newData, + ExprEvaluatorContext exprEvaluatorContext) + { + return EvaluateInternal(d.TimeInMillis()); + } + + public Object Evaluate( + DateTimeEx dtx, + EventBean[] eventsPerStream, + bool newData, + ExprEvaluatorContext exprEvaluatorContext) + { + if (dtx == null) + { + return null; + } + return EvaluateInternal(dtx.TimeInMillis); + } + + public Object EvaluateInternal(long ts) + { + return _first <= ts && ts <= _second; + } + + public Type ReturnType + { + get { return typeof (bool?); } + } + + public ExprDotNodeFilterAnalyzerDesc GetFilterDesc( + EventType[] typesPerStream, + DatetimeMethodEnum currentMethod, + IList currentParameters, + ExprDotNodeFilterAnalyzerInput inputDesc) + { + return null; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/datetime/reformatop/ReformatOpBetweenNonConstantParams.cs b/NEsper.Core/NEsper.Core/epl/datetime/reformatop/ReformatOpBetweenNonConstantParams.cs new file mode 100755 index 000000000..b6f1a8aa8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/reformatop/ReformatOpBetweenNonConstantParams.cs @@ -0,0 +1,266 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.datetime.eval; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.dot; + +namespace com.espertech.esper.epl.datetime.reformatop +{ + public class ReformatOpBetweenNonConstantParams : ReformatOp + { + private readonly ExprNode _start; + private readonly ExprEvaluator _startEval; + private readonly DatetimeLongCoercer _startCoercer; + private readonly ExprNode _end; + private readonly ExprEvaluator _endEval; + private readonly DatetimeLongCoercer _secondCoercer; + private readonly TimeZoneInfo _timeZone; + + private readonly bool _includeBoth; + private readonly bool? _includeLow; + private readonly bool? _includeHigh; + private readonly ExprEvaluator _evalIncludeLow; + private readonly ExprEvaluator _evalIncludeHigh; + + public ReformatOpBetweenNonConstantParams(IList parameters, TimeZoneInfo timeZone) + { + _timeZone = timeZone; + _start = parameters[0]; + _startEval = _start.ExprEvaluator; + _startCoercer = DatetimeLongCoercerFactory.GetCoercer(_startEval.ReturnType, timeZone); + _end = parameters[1]; + _endEval = _end.ExprEvaluator; + _secondCoercer = DatetimeLongCoercerFactory.GetCoercer(_endEval.ReturnType, timeZone); + + if (parameters.Count == 2) + { + _includeBoth = true; + _includeLow = true; + _includeHigh = true; + } + else + { + if (parameters[2].IsConstantResult) + { + _includeLow = GetBooleanValue(parameters[2]); + } + else + { + _evalIncludeLow = parameters[2].ExprEvaluator; + } + if (parameters[3].IsConstantResult) + { + _includeHigh = GetBooleanValue(parameters[3]); + } + else + { + _evalIncludeHigh = parameters[3].ExprEvaluator; + } + if (_includeLow != null && _includeHigh != null && _includeLow.Value && _includeHigh.Value) + { + _includeBoth = true; + } + } + } + + private bool GetBooleanValue(ExprNode exprNode) + { + var value = exprNode.ExprEvaluator.Evaluate(new EvaluateParams(null, true, null)); + if (value == null) + { + throw new ExprValidationException("Date-time method 'between' requires non-null parameter values"); + } + return (bool) value; + } + + public Object Evaluate( + long ts, + EventBean[] eventsPerStream, + bool newData, + ExprEvaluatorContext exprEvaluatorContext) + { + return EvaluateInternal(ts, eventsPerStream, newData, exprEvaluatorContext); + } + + public Object Evaluate( + DateTime d, + EventBean[] eventsPerStream, + bool newData, + ExprEvaluatorContext exprEvaluatorContext) + { + return EvaluateInternal(d.UtcMillis(), eventsPerStream, newData, exprEvaluatorContext); + } + + public Object Evaluate( + DateTimeOffset d, + EventBean[] eventsPerStream, + bool newData, + ExprEvaluatorContext exprEvaluatorContext) + { + return EvaluateInternal(d.TimeInMillis(), eventsPerStream, newData, exprEvaluatorContext); + } + + public Object Evaluate( + DateTimeEx dtx, + EventBean[] eventsPerStream, + bool newData, + ExprEvaluatorContext exprEvaluatorContext) + { + if (dtx == null) + { + return null; + } + return EvaluateInternal(dtx.TimeInMillis, eventsPerStream, newData, exprEvaluatorContext); + } + + public Type ReturnType + { + get { return typeof (bool?); } + } + + public Object EvaluateInternal( + long ts, + EventBean[] eventsPerStream, + bool newData, + ExprEvaluatorContext exprEvaluatorContext) + { + var evaluateParams = new EvaluateParams(eventsPerStream, newData, exprEvaluatorContext); + var firstObj = _startEval.Evaluate(evaluateParams); + if (firstObj == null) + { + return null; + } + var secondObj = _endEval.Evaluate(evaluateParams); + if (secondObj == null) + { + return null; + } + var first = _startCoercer.Coerce(firstObj); + var second = _secondCoercer.Coerce(secondObj); + if (_includeBoth) + { + if (first <= second) + { + return first <= ts && ts <= second; + } + else + { + return second <= ts && ts <= first; + } + } + else + { + + bool includeLowEndpoint; + if (_includeLow != null) + { + includeLowEndpoint = _includeLow.Value; + } + else + { + var value = _evalIncludeLow.Evaluate(evaluateParams); + if (value == null) + { + return null; + } + includeLowEndpoint = (bool) value; + + } + + bool includeHighEndpoint; + if (_includeHigh != null) + { + includeHighEndpoint = _includeHigh.Value; + } + else + { + var value = _evalIncludeHigh.Evaluate(evaluateParams); + if (value == null) + { + return null; + } + includeHighEndpoint = (bool) value; + + } + + if (includeLowEndpoint) + { + if (ts < first) + { + return false; + } + } + else + { + if (ts <= first) + { + return false; + } + } + + if (includeHighEndpoint) + { + if (ts > second) + { + return false; + } + } + else + { + if (ts >= second) + { + return false; + } + } + + return true; + } + } + + public ExprDotNodeFilterAnalyzerDesc GetFilterDesc( + EventType[] typesPerStream, + DatetimeMethodEnum currentMethod, + IList currentParameters, + ExprDotNodeFilterAnalyzerInput inputDesc) + { + if (_includeLow == null || _includeHigh == null) + { + return null; + } + + int targetStreamNum; + string targetProperty; + if (inputDesc is ExprDotNodeFilterAnalyzerInputStream) + { + var targetStream = (ExprDotNodeFilterAnalyzerInputStream) inputDesc; + targetStreamNum = targetStream.StreamNum; + var targetType = typesPerStream[targetStreamNum]; + targetProperty = targetType.StartTimestampPropertyName; + } + else if (inputDesc is ExprDotNodeFilterAnalyzerInputProp) + { + var targetStream = (ExprDotNodeFilterAnalyzerInputProp) inputDesc; + targetStreamNum = targetStream.StreamNum; + targetProperty = targetStream.PropertyName; + } + else + { + return null; + } + + return new ExprDotNodeFilterAnalyzerDTBetweenDesc( + typesPerStream, targetStreamNum, targetProperty, _start, _end, _includeLow.Value, _includeHigh.Value); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/datetime/reformatop/ReformatOpDateTimeEval.cs b/NEsper.Core/NEsper.Core/epl/datetime/reformatop/ReformatOpDateTimeEval.cs new file mode 100755 index 000000000..64056a399 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/reformatop/ReformatOpDateTimeEval.cs @@ -0,0 +1,67 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.datetime.eval; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.dot; + +namespace com.espertech.esper.epl.datetime.reformatop +{ + public class ReformatOpDateTimeEval : ReformatOp + { + private readonly Type _returnType; + private readonly DateTimeEval _dateTimeEval; + private readonly TimeZoneInfo _timeZone; + + public ReformatOpDateTimeEval(DateTimeEval dateTimeEval, Type returnType, TimeZoneInfo timeZone) + { + _dateTimeEval = dateTimeEval; + _returnType = returnType; + _timeZone = timeZone; + } + + public Object Evaluate(long ts, EventBean[] eventsPerStream, bool newData, ExprEvaluatorContext exprEvaluatorContext) + { + return _dateTimeEval.Invoke(ts.TimeFromMillis(_timeZone)); + } + + public object Evaluate(DateTime dt, EventBean[] eventsPerStream, bool newData, ExprEvaluatorContext exprEvaluatorContext) + { + return _dateTimeEval.Invoke(dt.TranslateTo(_timeZone)); + } + + public object Evaluate(DateTimeOffset dateTime, EventBean[] eventsPerStream, bool newData, ExprEvaluatorContext exprEvaluatorContext) + { + return _dateTimeEval.Invoke(dateTime.TranslateTo(_timeZone)); + } + + public object Evaluate(DateTimeEx dtx, EventBean[] eventsPerStream, bool newData, ExprEvaluatorContext exprEvaluatorContext) + { + return _dateTimeEval.Invoke(dtx.DateTime.TranslateTo(_timeZone)); + } + + public Type ReturnType + { + get { return _returnType; } + } + + public ExprDotNodeFilterAnalyzerDesc GetFilterDesc( + EventType[] typesPerStream, + DatetimeMethodEnum currentMethod, + IList currentParameters, + ExprDotNodeFilterAnalyzerInput inputDesc) + { + return null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/reformatop/ReformatOpEval.cs b/NEsper.Core/NEsper.Core/epl/datetime/reformatop/ReformatOpEval.cs new file mode 100755 index 000000000..951759bbc --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/reformatop/ReformatOpEval.cs @@ -0,0 +1,90 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.datetime.eval; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.dot; +using com.espertech.esper.epl.expression.time; + +namespace com.espertech.esper.epl.datetime.reformatop +{ + using DateTimeExEval = Func; + + public class ReformatOpEval : ReformatOp + { + private readonly Func _dtxEval; + private readonly TimeZoneInfo _timeZone; + private readonly TimeAbacus _timeAbacus; + + public ReformatOpEval( + Func dtxEval, + TimeZoneInfo timeZone, + TimeAbacus timeAbacus) + { + _dtxEval = dtxEval; + _timeZone = timeZone; + _timeAbacus = timeAbacus; + } + + public Object Evaluate( + long ts, + EventBean[] eventsPerStream, + bool newData, + ExprEvaluatorContext exprEvaluatorContext) + { + DateTimeEx dtx = DateTimeEx.GetInstance(_timeZone); + _timeAbacus.CalendarSet(ts, dtx); + return _dtxEval.Invoke(dtx); + } + + public Object Evaluate( + DateTime d, + EventBean[] eventsPerStream, + bool newData, + ExprEvaluatorContext exprEvaluatorContext) + { + DateTimeEx dtx = DateTimeEx.GetInstance(_timeZone, d); + return _dtxEval.Invoke(dtx); + } + + public Object Evaluate( + DateTimeOffset d, + EventBean[] eventsPerStream, + bool newData, + ExprEvaluatorContext exprEvaluatorContext) + { + DateTimeEx dtx = DateTimeEx.GetInstance(_timeZone, d); + return _dtxEval.Invoke(dtx); + } + + public Object Evaluate( + DateTimeEx dtx, + EventBean[] eventsPerStream, + bool newData, + ExprEvaluatorContext exprEvaluatorContext) + { + return _dtxEval.Invoke(dtx); + } + + public Type ReturnType => typeof(TValue); + + public ExprDotNodeFilterAnalyzerDesc GetFilterDesc( + EventType[] typesPerStream, + DatetimeMethodEnum currentMethod, + IList currentParameters, + ExprDotNodeFilterAnalyzerInput inputDesc) + { + return null; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/datetime/reformatop/ReformatOpFactory.cs b/NEsper.Core/NEsper.Core/epl/datetime/reformatop/ReformatOpFactory.cs new file mode 100755 index 000000000..05272619f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/reformatop/ReformatOpFactory.cs @@ -0,0 +1,107 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.compat; +using com.espertech.esper.epl.datetime.calop; +using com.espertech.esper.epl.datetime.eval; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.time; + +namespace com.espertech.esper.epl.datetime.reformatop +{ + public class ReformatOpFactory : OpFactory + { + private static readonly ReformatOp FORMAT_STRING = new ReformatOpStringFormat(); + + public ReformatOp GetOp( + TimeZoneInfo timeZone, + TimeAbacus timeAbacus, + DatetimeMethodEnum method, + string methodNameUsed, + IList parameters) + { + if (method == DatetimeMethodEnum.GET) + { + CalendarFieldEnum fieldNum = CalendarOpUtil.GetEnum(methodNameUsed, parameters[0]); + return new ReformatOpGetField(fieldNum, timeZone, timeAbacus); + } + if (method == DatetimeMethodEnum.FORMAT) + { + return FORMAT_STRING; + } + if (method == DatetimeMethodEnum.TOCALENDAR) + { + return new ReformatOpToCalendar(timeZone, timeAbacus); + } + if (method == DatetimeMethodEnum.TOMILLISEC) + { + return new ReformatOpToMillisec(timeZone); + } + if (method == DatetimeMethodEnum.TODATE) + { + return new ReformatOpToDateTimeOffset(timeZone, timeAbacus); + } + if (method == DatetimeMethodEnum.GETDAYOFMONTH) + { + return new ReformatOpEval(CalendarEvalStatics.DAY_OF_MONTH, timeZone, timeAbacus); + } + if (method == DatetimeMethodEnum.GETMINUTEOFHOUR) + { + return new ReformatOpEval(CalendarEvalStatics.MINUTE_OF_HOUR, timeZone, timeAbacus); + } + if (method == DatetimeMethodEnum.GETMONTHOFYEAR) + { + return new ReformatOpEval(CalendarEvalStatics.MONTH_OF_YEAR, timeZone, timeAbacus); + } + if (method == DatetimeMethodEnum.GETDAYOFWEEK) + { + return new ReformatOpEval(CalendarEvalStatics.DAY_OF_WEEK, timeZone, timeAbacus); + } + if (method == DatetimeMethodEnum.GETDAYOFYEAR) + { + return new ReformatOpEval(CalendarEvalStatics.DAY_OF_YEAR, timeZone, timeAbacus); + } + if (method == DatetimeMethodEnum.GETERA) + { + return new ReformatOpEval(CalendarEvalStatics.ERA, timeZone, timeAbacus); + } + if (method == DatetimeMethodEnum.GETHOUROFDAY) + { + return new ReformatOpEval(CalendarEvalStatics.HOUR_OF_DAY, timeZone, timeAbacus); + } + if (method == DatetimeMethodEnum.GETMILLISOFSECOND) + { + return new ReformatOpEval(CalendarEvalStatics.MILLIS_OF_SECOND, timeZone, timeAbacus); + } + if (method == DatetimeMethodEnum.GETSECONDOFMINUTE) + { + return new ReformatOpEval(CalendarEvalStatics.SECOND_OF_MINUTE, timeZone, timeAbacus); + } + if (method == DatetimeMethodEnum.GETWEEKYEAR) + { + return new ReformatOpEval(CalendarEvalStatics.WEEKYEAR, timeZone, timeAbacus); + } + if (method == DatetimeMethodEnum.GETYEAR) + { + return new ReformatOpEval(CalendarEvalStatics.YEAR, timeZone, timeAbacus); + } + if (method == DatetimeMethodEnum.BETWEEN) + { + if (ExprNodeUtility.IsAllConstants(parameters)) + { + return new ReformatOpBetweenConstantParams(parameters, timeZone); + } + return new ReformatOpBetweenNonConstantParams(parameters, timeZone); + } + throw new IllegalStateException("Unrecognized date-time method code '" + method + "'"); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/datetime/reformatop/ReformatOpGetField.cs b/NEsper.Core/NEsper.Core/epl/datetime/reformatop/ReformatOpGetField.cs new file mode 100755 index 000000000..925d5f515 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/reformatop/ReformatOpGetField.cs @@ -0,0 +1,90 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.datetime.calop; +using com.espertech.esper.epl.datetime.eval; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.dot; +using com.espertech.esper.epl.expression.time; + +namespace com.espertech.esper.epl.datetime.reformatop +{ + public class ReformatOpGetField : ReformatOp + { + private readonly CalendarFieldEnum _fieldNum; + private readonly TimeZoneInfo _timeZone; + private readonly TimeAbacus _timeAbacus; + + public ReformatOpGetField(CalendarFieldEnum fieldNum, TimeZoneInfo timeZone, TimeAbacus timeAbacus) { + _fieldNum = fieldNum; + _timeZone = timeZone; + _timeAbacus = timeAbacus; + } + + public Object Evaluate(long ts, EventBean[] eventsPerStream, bool newData, ExprEvaluatorContext exprEvaluatorContext) { + DateTimeEx cal = DateTimeEx.GetInstance(_timeZone); + _timeAbacus.CalendarSet(ts, cal); + return Action(cal); + } + + public Object Evaluate(DateTime d, EventBean[] eventsPerStream, bool newData, ExprEvaluatorContext exprEvaluatorContext) { + DateTimeEx cal = DateTimeEx.GetInstance(_timeZone, d); + return Action(cal); + } + + public Object Evaluate(DateTimeOffset d, EventBean[] eventsPerStream, bool newData, ExprEvaluatorContext exprEvaluatorContext) + { + DateTimeEx cal = DateTimeEx.GetInstance(_timeZone, d); + return Action(cal); + } + + public Object Evaluate(DateTimeEx dtx, EventBean[] eventsPerStream, bool newData, ExprEvaluatorContext exprEvaluatorContext) + { + return Action(dtx); + } + + private int Action(DateTimeEx dtx) + { + switch (_fieldNum) + { + case CalendarFieldEnum.WEEK: + return dtx.DateTime.GetWeekOfYear(); + case CalendarFieldEnum.YEAR: + return dtx.Year; + case CalendarFieldEnum.MONTH: + return dtx.Month; + case CalendarFieldEnum.DAY: + return dtx.Day; + case CalendarFieldEnum.HOUR: + return dtx.Hour; + case CalendarFieldEnum.MINUTE: + return dtx.Minute; + case CalendarFieldEnum.SECOND: + return dtx.Second; + case CalendarFieldEnum.MILLISEC: + return dtx.Millisecond; + default: + throw new ArgumentException("invalid value for field num"); + } + } + + public Type ReturnType + { + get { return typeof (int?); } + } + + public ExprDotNodeFilterAnalyzerDesc GetFilterDesc(EventType[] typesPerStream, DatetimeMethodEnum currentMethod, IList currentParameters, ExprDotNodeFilterAnalyzerInput inputDesc) { + return null; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/datetime/reformatop/ReformatOpStringFormat.cs b/NEsper.Core/NEsper.Core/epl/datetime/reformatop/ReformatOpStringFormat.cs new file mode 100755 index 000000000..58b7e4022 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/reformatop/ReformatOpStringFormat.cs @@ -0,0 +1,61 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.datetime.eval; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.dot; + +namespace com.espertech.esper.epl.datetime.reformatop +{ + public class ReformatOpStringFormat : ReformatOp + { + private static string Action(DateTimeOffset d) + { + return d.ToString(); + } + + public Object Evaluate(long ts, EventBean[] eventsPerStream, bool newData, ExprEvaluatorContext exprEvaluatorContext) + { + return Action(DateTimeOffsetHelper.MillisToDateTimeOffset(ts)); + } + + public Object Evaluate(DateTime d, EventBean[] eventsPerStream, bool newData, ExprEvaluatorContext exprEvaluatorContext) + { + return Action(d); + } + + public Object Evaluate(DateTimeOffset d, EventBean[] eventsPerStream, bool newData, ExprEvaluatorContext exprEvaluatorContext) + { + return Action(d); + } + + public Object Evaluate(DateTimeEx dtx, EventBean[] eventsPerStream, bool newData, ExprEvaluatorContext exprEvaluatorContext) + { + return Action(dtx.DateTime); + } + + public Type ReturnType + { + get { return typeof (string); } + } + + public ExprDotNodeFilterAnalyzerDesc GetFilterDesc( + EventType[] typesPerStream, + DatetimeMethodEnum currentMethod, + IList currentParameters, + ExprDotNodeFilterAnalyzerInput inputDesc) + { + return null; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/datetime/reformatop/ReformatOpToCalendar.cs b/NEsper.Core/NEsper.Core/epl/datetime/reformatop/ReformatOpToCalendar.cs new file mode 100755 index 000000000..92bc18be3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/reformatop/ReformatOpToCalendar.cs @@ -0,0 +1,69 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.datetime.eval; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.dot; +using com.espertech.esper.epl.expression.time; + +namespace com.espertech.esper.epl.datetime.reformatop +{ + public class ReformatOpToCalendar : ReformatOp + { + private readonly TimeZoneInfo _timeZone; + private readonly TimeAbacus _timeAbacus; + + public ReformatOpToCalendar(TimeZoneInfo timeZone, TimeAbacus timeAbacus) + { + _timeZone = timeZone; + _timeAbacus = timeAbacus; + } + + public Object Evaluate(long ts, EventBean[] eventsPerStream, bool newData, ExprEvaluatorContext exprEvaluatorContext) + { + DateTimeEx dtx = DateTimeEx.GetInstance(_timeZone); + _timeAbacus.CalendarSet(ts, dtx); + return dtx; + } + + public Object Evaluate(DateTime d, EventBean[] eventsPerStream, bool newData, ExprEvaluatorContext exprEvaluatorContext) + { + DateTimeEx dtx = DateTimeEx.GetInstance(_timeZone, d); + return dtx; + } + + public Object Evaluate(DateTimeOffset d, EventBean[] eventsPerStream, bool newData, ExprEvaluatorContext exprEvaluatorContext) + { + DateTimeEx dtx = DateTimeEx.GetInstance(_timeZone, d); + return dtx; + } + + public Object Evaluate(DateTimeEx dtx, EventBean[] eventsPerStream, bool newData, ExprEvaluatorContext exprEvaluatorContext) { + return dtx; + } + + public Type ReturnType + { + get { return typeof(DateTimeEx); } + } + + public ExprDotNodeFilterAnalyzerDesc GetFilterDesc( + EventType[] typesPerStream, + DatetimeMethodEnum currentMethod, + IList currentParameters, + ExprDotNodeFilterAnalyzerInput inputDesc) + { + return null; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/datetime/reformatop/ReformatOpToDateTime.cs b/NEsper.Core/NEsper.Core/epl/datetime/reformatop/ReformatOpToDateTime.cs new file mode 100755 index 000000000..389315354 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/reformatop/ReformatOpToDateTime.cs @@ -0,0 +1,66 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.datetime.eval; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.dot; +using com.espertech.esper.epl.expression.time; + +namespace com.espertech.esper.epl.datetime.reformatop +{ + public class ReformatOpToDateTime : ReformatOp + { + private readonly TimeZoneInfo _timeZone; + private readonly TimeAbacus _timeAbacus; + + public ReformatOpToDateTime(TimeZoneInfo timeZone, TimeAbacus timeAbacus) + { + _timeZone = timeZone; + _timeAbacus = timeAbacus; + } + + public Object Evaluate(long ts, EventBean[] eventsPerStream, bool newData, ExprEvaluatorContext exprEvaluatorContext) { + return _timeAbacus.ToDate(ts); + } + + public Object Evaluate(DateTime d, EventBean[] eventsPerStream, bool newData, ExprEvaluatorContext exprEvaluatorContext) { + return d; + } + + public Object Evaluate(DateTimeOffset d, EventBean[] eventsPerStream, bool newData, ExprEvaluatorContext exprEvaluatorContext) + { + return d.DateTime; + } + + public Object Evaluate(DateTimeEx dtx, EventBean[] eventsPerStream, bool newData, ExprEvaluatorContext exprEvaluatorContext) + { + return dtx.DateTime.DateTime; + } + + public Type ReturnType + { + get { return typeof (DateTime); } + } + + public ExprDotNodeFilterAnalyzerDesc GetFilterDesc( + EventType[] typesPerStream, + DatetimeMethodEnum currentMethod, + IList currentParameters, + ExprDotNodeFilterAnalyzerInput inputDesc) + { + return null; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/datetime/reformatop/ReformatOpToDateTimeOffset.cs b/NEsper.Core/NEsper.Core/epl/datetime/reformatop/ReformatOpToDateTimeOffset.cs new file mode 100755 index 000000000..d07553a37 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/reformatop/ReformatOpToDateTimeOffset.cs @@ -0,0 +1,68 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.datetime.eval; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.dot; +using com.espertech.esper.epl.expression.time; + +namespace com.espertech.esper.epl.datetime.reformatop +{ + public class ReformatOpToDateTimeOffset : ReformatOp + { + private readonly TimeZoneInfo _timeZone; + private readonly TimeAbacus _timeAbacus; + + public ReformatOpToDateTimeOffset(TimeZoneInfo timeZone, TimeAbacus timeAbacus) + { + _timeZone = timeZone; + _timeAbacus = timeAbacus; + } + + public Object Evaluate(long ts, EventBean[] eventsPerStream, bool newData, ExprEvaluatorContext exprEvaluatorContext) + { + return _timeAbacus.ToDate(ts) + .DateTime + .TranslateTo(_timeZone); + } + + public object Evaluate(DateTime d, EventBean[] eventsPerStream, bool newData, ExprEvaluatorContext exprEvaluatorContext) + { + return d.TranslateTo(_timeZone); + } + + public object Evaluate(DateTimeOffset d, EventBean[] eventsPerStream, bool newData, ExprEvaluatorContext exprEvaluatorContext) + { + return d.TranslateTo(_timeZone); + } + + public object Evaluate(DateTimeEx d, EventBean[] eventsPerStream, bool newData, ExprEvaluatorContext exprEvaluatorContext) + { + return d.DateTime.TranslateTo(_timeZone); + } + + public Type ReturnType + { + get { return typeof(DateTimeOffset); } + } + + public ExprDotNodeFilterAnalyzerDesc GetFilterDesc( + EventType[] typesPerStream, + DatetimeMethodEnum currentMethod, + IList currentParameters, + ExprDotNodeFilterAnalyzerInput inputDesc) + { + return null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/datetime/reformatop/ReformatOpToMillisec.cs b/NEsper.Core/NEsper.Core/epl/datetime/reformatop/ReformatOpToMillisec.cs new file mode 100755 index 000000000..bcb2cd9b7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/datetime/reformatop/ReformatOpToMillisec.cs @@ -0,0 +1,65 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.datetime.eval; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.dot; + +namespace com.espertech.esper.epl.datetime.reformatop +{ + public class ReformatOpToMillisec : ReformatOp + { + private readonly TimeZoneInfo timeZone; + + public ReformatOpToMillisec(TimeZoneInfo timeZone) + { + this.timeZone = timeZone; + } + + public Object Evaluate(long ts, EventBean[] eventsPerStream, bool newData, ExprEvaluatorContext exprEvaluatorContext) + { + return ts; + } + + public Object Evaluate(DateTime d, EventBean[] eventsPerStream, bool newData, ExprEvaluatorContext exprEvaluatorContext) + { + return d.UtcMillis(); + } + + public Object Evaluate(DateTimeOffset d, EventBean[] eventsPerStream, bool newData, ExprEvaluatorContext exprEvaluatorContext) + { + return d.TimeInMillis(); + } + + public Object Evaluate(DateTimeEx dtx, EventBean[] eventsPerStream, bool newData, ExprEvaluatorContext exprEvaluatorContext) + { + return dtx.TimeInMillis; + } + + public Type ReturnType + { + get { return typeof (long); } + } + + public ExprDotNodeFilterAnalyzerDesc GetFilterDesc( + EventType[] typesPerStream, + DatetimeMethodEnum currentMethod, + IList currentParameters, + ExprDotNodeFilterAnalyzerInput inputDesc) + { + return null; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/db/ColumnSettings.cs b/NEsper.Core/NEsper.Core/epl/db/ColumnSettings.cs new file mode 100755 index 000000000..01d812f2b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/db/ColumnSettings.cs @@ -0,0 +1,63 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; + +namespace com.espertech.esper.epl.db +{ + /// + /// Column-level configuration settings are held in this immutable descriptor. + /// + public class ColumnSettings + { + private readonly ConfigurationDBRef.MetadataOriginEnum metadataOriginEnum; + private readonly ConfigurationDBRef.ColumnChangeCaseEnum columnCaseConversionEnum; + private readonly IDictionary dataTypesMapping; + + /// + /// Ctor. + /// + /// defines how to obtain output columnn metadata + /// defines if to change case on output columns + /// The data types mapping. + public ColumnSettings(ConfigurationDBRef.MetadataOriginEnum metadataOriginEnum, + ConfigurationDBRef.ColumnChangeCaseEnum columnCaseConversionEnum, + IDictionary dataTypesMapping) + { + this.metadataOriginEnum = metadataOriginEnum; + this.columnCaseConversionEnum = columnCaseConversionEnum; + this.dataTypesMapping = dataTypesMapping; + } + + /// Returns the metadata orgin. + /// indicator how the engine obtains output column metadata + public ConfigurationDBRef.MetadataOriginEnum MetadataRetrievalEnum + { + get { return metadataOriginEnum; } + } + + /// Returns the change case policy. + /// indicator how the engine should change case on output columns + public ConfigurationDBRef.ColumnChangeCaseEnum ColumnCaseConversionEnum + { + get { return columnCaseConversionEnum; } + } + + /// + /// Gets the data types mapping. + /// + /// The data types mapping. + public IDictionary DataTypesMapping + { + get { return dataTypesMapping; } + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/epl/db/ConnectionCache.cs b/NEsper.Core/NEsper.Core/epl/db/ConnectionCache.cs new file mode 100755 index 000000000..47e7eafe2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/db/ConnectionCache.cs @@ -0,0 +1,176 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; +using System.Data.Common; +using System.Text; + +using com.espertech.esper.collection; +using com.espertech.esper.client; +using com.espertech.esper.compat.logging; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.db +{ + /// + /// Base class for a Connection and DbCommand cache. + /// + /// Implementations control the lifecycle via lifecycle methods, or + /// may simple obtain new resources and close new resources every time. + /// + /// + /// This is not a pool - a cache is associated with one client class and that + /// class is expected to use cache methods in well-defined order of get, done-with and destroy. + /// + /// + public abstract class ConnectionCache : IDisposable + { + private readonly DatabaseConnectionFactory _databaseConnectionFactory; + private readonly String _sql; + private readonly IList _sqlFragments; + private readonly IEnumerable _contextAttributes; + + /// Returns a cached or new connection and statement pair. + /// connection and statement pair + /// + public abstract Pair GetConnection(); + + /// Indicate to return the connection and statement pair after use. + /// is the resources to return + /// + public abstract void DoneWith(Pair pair); + + /// Destroys cache closing all resources cached, if any. + public abstract void Dispose(); + + /// + /// Ctor. + /// + /// connection factory + /// statement sql + /// The context attributes. + + internal ConnectionCache(DatabaseConnectionFactory databaseConnectionFactory, String sql, IEnumerable contextAttributes) + { + _databaseConnectionFactory = databaseConnectionFactory; + _sql = sql; + _sqlFragments = GetSqlFragments(sql); + _contextAttributes = contextAttributes; + } + + /// Close resources. + /// is the resources to close. + /// + + internal static void Close(Pair pair) + { + Log.Info(".Close Closing statement and connection"); + try + { + pair.Second.Dispose(); + } + catch (DbException ex) + { + throw new EPException("Error closing statement", ex); + } + } + + /// Make a new pair of resources. + /// pair of resources + /// + + protected Pair MakeNew() + { + Log.Info(".MakeNew Obtaining new connection and statement"); + + try + { + // Get the driver + DbDriver dbDriver = _databaseConnectionFactory.Driver; + // Get the command + DbDriverCommand dbCommand = dbDriver.CreateCommand(_sqlFragments, null, _contextAttributes); + + return new Pair(dbDriver, dbCommand); + } + catch (DatabaseConfigException ex) + { + throw new EPException("Error obtaining connection", ex); + } + + } + + /// + /// Gets the SQL fragments. + /// + /// The SQL. + /// + public static IList GetSqlFragments(string sql) + { + List sqlFragments = new List(); + + bool inQuote = false; + bool inEscape = false; + // Keeps track of the current token + StringBuilder currToken = new StringBuilder(); + String token; + + foreach (char ch in sql) + { + switch (ch) + { + case '\\': + inEscape = !inEscape; // Addresses the case of double backslash + currToken.Append(ch); + break; + case '"': + case '\'': + if (!inEscape) + { + inQuote = !inQuote; + } + currToken.Append(ch); + break; + case '?': + if (!inEscape && !inQuote) + { + // Get the current token + token = currToken.ToString(); + // Reset the current token + currToken.Length = 0; + // Add the current token to the sql fragments if it has any value + if (!String.IsNullOrEmpty(token)) + { + sqlFragments.Add(new PlaceholderParser.TextFragment(token)); + } + sqlFragments.Add(new PlaceholderParser.ParameterFragment(null)); + } + break; + default: + inEscape = false; + currToken.Append(ch); + break; + } + } + + // Get the current token + token = currToken.ToString(); + // Add the current token to the sql fragments if it has any value + if (!String.IsNullOrEmpty(token)) + { + sqlFragments.Add(new PlaceholderParser.TextFragment(token)); + } + + // Return the fragments + return sqlFragments; + } + + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/db/ConnectionCacheImpl.cs b/NEsper.Core/NEsper.Core/epl/db/ConnectionCacheImpl.cs new file mode 100755 index 000000000..e69e3fcb7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/db/ConnectionCacheImpl.cs @@ -0,0 +1,72 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; + +using com.espertech.esper.collection; + + +namespace com.espertech.esper.epl.db +{ + /// + /// Caches the Connection and DbCommand instance for reuse. + /// + + public class ConnectionCacheImpl : ConnectionCache + { + private Pair cache; + + /// + /// Ctor. + /// + /// connection factory + /// statement sql + /// The context attributes. + + public ConnectionCacheImpl(DatabaseConnectionFactory databaseConnectionFactory, String sql, IEnumerable contextAttributes) + : base(databaseConnectionFactory, sql, contextAttributes) + { + } + + /// + /// Returns a cached or new connection and statement pair. + /// + /// connection and statement pair + public override Pair GetConnection() + { + if (cache == null) + { + cache = MakeNew(); + } + return cache; + } + + /// + /// Indicate to return the connection and statement pair after use. + /// + /// is the resources to return + public override void DoneWith(Pair pair) + { + // no need to implement + } + + /// + /// Destroys cache closing all resources cached, if any. + /// + public override void Dispose() + { + if (cache != null) + { + Close(cache); + } + cache = null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/db/ConnectionNoCacheImpl.cs b/NEsper.Core/NEsper.Core/epl/db/ConnectionNoCacheImpl.cs new file mode 100755 index 000000000..3645dca83 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/db/ConnectionNoCacheImpl.cs @@ -0,0 +1,63 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; + +using com.espertech.esper.collection; + +namespace com.espertech.esper.epl.db +{ + + /// + /// Implementation of a connection cache that simply doesn't cache but gets + /// a new connection and statement every request, and closes these every time + /// a client indicates done. + /// + + public class ConnectionNoCacheImpl : ConnectionCache + { + /// + /// Ctor. + /// + /// is the connection factory + /// is the statement sql + /// The context attributes. + public ConnectionNoCacheImpl(DatabaseConnectionFactory databaseConnectionFactory, String sql, IEnumerable contextAttributes) + : base(databaseConnectionFactory, sql, contextAttributes) + { + } + + /// + /// Returns a cached or new connection and statement pair. + /// + /// connection and statement pair + public override Pair GetConnection() + { + return MakeNew(); + } + + /// + /// Indicate to return the connection and statement pair after use. + /// + /// is the resources to return + public override void DoneWith(Pair pair) + { + Close(pair); + } + + /// + /// Destroys cache closing all resources cached, if any. + /// + public override void Dispose() + { + // no resources held + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/db/DBOutputTypeDesc.cs b/NEsper.Core/NEsper.Core/epl/db/DBOutputTypeDesc.cs new file mode 100755 index 000000000..a871f462c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/db/DBOutputTypeDesc.cs @@ -0,0 +1,62 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; + +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.db +{ + /// + /// Descriptor for SQL output columns. + /// + + public sealed class DBOutputTypeDesc + { + /// Returns the SQL type of the output column. + /// sql type + /// + public string SqlType { get; private set; } + + /// Returns the type that getObject on the output column produces. + /// type from statement metadata + /// + public Type DataType { get; private set; } + + /// + /// Gets the optional mapping from output column type to built-in. + /// + public DatabaseTypeBinding OptionalBinding { get; private set; } + + /// + /// Ctor. + /// + /// the type of the column + /// the type reflecting column type + /// the optional mapping from output column type to built-in + + public DBOutputTypeDesc(string sqlType, Type dataType, DatabaseTypeBinding optionalBinding) + { + SqlType = sqlType; + DataType = dataType; + OptionalBinding = optionalBinding; + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override String ToString() + { + return "sqlType=" + SqlType + " dataType=" + DataType; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/db/DataCache.cs b/NEsper.Core/NEsper.Core/epl/db/DataCache.cs new file mode 100755 index 000000000..d9b0e767a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/db/DataCache.cs @@ -0,0 +1,55 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.join.table; + +namespace com.espertech.esper.epl.db +{ + /// + /// Implementations serve as caches for historical or reference data retrieved + /// via lookup keys consisting or one or more rows represented by a list of events. + /// + public interface DataCache : IDisposable + { + /// + /// Ask the cache if the keyed value is cached, returning a list or rows if the key is in the cache, + /// or returning null to indicate no such key cached. Zero rows may also be cached. + /// + /// is the keys to look up in the cache + /// number of method param keys, from the start, that are for cache lookup + /// + /// a list of rows that can be empty is the key was found in the cache, or null if + /// the key is not found in the cache + /// + EventTable[] GetCached(Object[] methodParams, int numLookupKeys); + + /// + /// Puts into the cache a key and a list of rows, or an empty list if zero rows. + /// + /// The put method is designed to be called when the cache does not contain a key as + /// determined by the get method. Implementations typically simply overwrite + /// any keys put into the cache that already existed in the cache. + /// + /// + /// is the keys to the cache entry + /// number of method param keys, from the start, that are for cache lookup + /// is a number of rows + void PutCached(Object[] methodParams, int numLookupKeys, EventTable[] rows); + + /// + /// Returns true if the cache is active and currently caching, or false if the cache is inactive and not currently caching + /// + /// true for caching enabled, false for no caching taking place + bool IsActive { get; } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/db/DataCacheClearableMap.cs b/NEsper.Core/NEsper.Core/epl/db/DataCacheClearableMap.cs new file mode 100755 index 000000000..218ef2877 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/db/DataCacheClearableMap.cs @@ -0,0 +1,59 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.join.table; + +namespace com.espertech.esper.epl.db +{ + /// + /// For use in iteration over historical joins, a implementation + /// that serves to hold EventBean rows generated during a join evaluation + /// involving historical streams stable for the same cache lookup keys. + /// + public class DataCacheClearableMap : DataCache + { + private readonly IDictionary _cache; + + /// Ctor. + public DataCacheClearableMap() + { + _cache = new Dictionary(); + } + + public EventTable[] GetCached(Object[] methodParams, int numLookupKeys) + { + var key = DataCacheUtil.GetLookupKey(methodParams, numLookupKeys); + return _cache.Get(key); + } + + public void PutCached(Object[] methodParams, int numLookupKeys, EventTable[] rows) + { + var key = DataCacheUtil.GetLookupKey(methodParams, numLookupKeys); + _cache.Put(key, rows); + } + + public bool IsActive + { + get { return false; } + } + + /// Clears the cache. + public void Clear() + { + _cache.Clear(); + } + + public void Dispose() + { + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/db/DataCacheExpiringImpl.cs b/NEsper.Core/NEsper.Core/epl/db/DataCacheExpiringImpl.cs new file mode 100755 index 000000000..1b6fc83eb --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/db/DataCacheExpiringImpl.cs @@ -0,0 +1,190 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.time; +using com.espertech.esper.epl.@join.table; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.schedule; + +namespace com.espertech.esper.epl.db +{ + /// + /// Implements an expiry-time cache that evicts data when data becomes stale + /// after a given number of seconds. + /// + /// The cache reference type indicates which backing Map is used: Weak type uses the WeakHashMap, + /// Soft type uses the apache commons ReferenceMap, and Hard type simply uses a HashMap. + /// + /// + public class DataCacheExpiringImpl + : DataCache + , ScheduleHandleCallback + { + private readonly double _maxAgeSec; + private readonly double _purgeIntervalSec; + private readonly SchedulingService _schedulingService; + private readonly long _scheduleSlot; + private readonly IDictionary _cache; + private readonly EPStatementAgentInstanceHandle _epStatementAgentInstanceHandle; + private readonly TimeAbacus _timeAbacus; + + private bool _isScheduled; + + /// + /// Ctor. + /// + /// is the maximum age in seconds + /// is the purge interval in seconds + /// indicates whether hard, soft or weak references are used in the cache + /// is a service for call backs at a scheduled time, for purging + /// slot for scheduling callbacks for this cache + /// is the statements-own handle for use in registering callbacks with services + /// time abacus + public DataCacheExpiringImpl( + double maxAgeSec, + double purgeIntervalSec, + ConfigurationCacheReferenceType cacheReferenceType, + SchedulingService schedulingService, + long scheduleSlot, + EPStatementAgentInstanceHandle epStatementAgentInstanceHandle, + TimeAbacus timeAbacus) + { + _maxAgeSec = maxAgeSec; + _purgeIntervalSec = purgeIntervalSec; + _schedulingService = schedulingService; + _scheduleSlot = scheduleSlot; + _timeAbacus = timeAbacus; + + if (cacheReferenceType == ConfigurationCacheReferenceType.HARD) + { + _cache = new Dictionary(); + } + else if (cacheReferenceType == ConfigurationCacheReferenceType.SOFT) + { + _cache = new ReferenceMap(ReferenceType.SOFT, ReferenceType.SOFT); + } + else + { + _cache = new WeakDictionary(); + } + + _epStatementAgentInstanceHandle = epStatementAgentInstanceHandle; + } + + public EventTable[] GetCached(Object[] methodParams, int numLookupKeys) + { + var key = DataCacheUtil.GetLookupKey(methodParams, numLookupKeys); + var item = _cache.Get(key); + if (item == null) + { + return null; + } + + var now = _schedulingService.Time; + long maxAgeMSec = _timeAbacus.DeltaForSecondsDouble(_maxAgeSec); + if ((now - item.Time) > maxAgeMSec) + { + _cache.Remove(key); + return null; + } + + return item.Data; + } + + public void PutCached(Object[] methodParams, int numLookupKeys, EventTable[] rows) + { + var key = DataCacheUtil.GetLookupKey(methodParams, numLookupKeys); + var now = _schedulingService.Time; + var item = new Item(rows, now); + _cache.Put(key, item); + + if (!_isScheduled) + { + var callback = new EPStatementHandleCallback(_epStatementAgentInstanceHandle, this); + _schedulingService.Add(_timeAbacus.DeltaForSecondsDouble(_purgeIntervalSec), callback, _scheduleSlot); + _isScheduled = true; + } + } + + /// + /// Returns the maximum age in milliseconds. + /// + /// millisecon max age + public double MaxAgeSec + { + get { return _maxAgeSec; } + } + + public double PurgeIntervalSec + { + get { return _purgeIntervalSec; } + } + + public bool IsActive + { + get { return true; } + } + + /// + /// Returns the current cache size. + /// + /// cache size + public long Count + { + get { return _cache.Count; } + } + + public void ScheduledTrigger(EngineLevelExtensionServicesContext engineLevelExtensionServicesContext) + { + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().QHistoricalScheduledEval(); + } + // purge expired + var now = _schedulingService.Time; + var maxAgeMSec = _timeAbacus.DeltaForSecondsDouble(_maxAgeSec); + + _cache + .Where(entry => ((now - entry.Value.Time) > maxAgeMSec)) + .Select(entry => entry.Key) + .ToList() + .ForEach(itemKey => _cache.Remove(itemKey)); + + _isScheduled = false; + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().AHistoricalScheduledEval(); + } + } + + public void Dispose() + { + } + + private class Item + { + public Item(EventTable[] data, long time) + { + Data = data; + Time = time; + } + + public EventTable[] Data { get; private set; } + public long Time { get; private set; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/db/DataCacheFactory.cs b/NEsper.Core/NEsper.Core/epl/db/DataCacheFactory.cs new file mode 100755 index 000000000..caf65adbe --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/db/DataCacheFactory.cs @@ -0,0 +1,63 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.schedule; + +namespace com.espertech.esper.epl.db +{ + /// + /// Factory for data caches for use caching database query results and method invocation results. + /// + public class DataCacheFactory { + /// + /// Creates a cache implementation for the strategy as defined by the cache descriptor. + /// + /// cache descriptor + /// statement handle for timer invocations + /// scheduling service for time-based caches + /// for ordered timer invokation + /// statement context + /// stream number + /// data cache implementation + public DataCache GetDataCache(ConfigurationDataCache cacheDesc, + StatementContext statementContext, + EPStatementAgentInstanceHandle epStatementAgentInstanceHandle, + SchedulingService schedulingService, + ScheduleBucket scheduleBucket, + int streamNum) { + if (cacheDesc == null) { + return new DataCacheNullImpl(); + } + + if (cacheDesc is ConfigurationLRUCache) { + ConfigurationLRUCache lruCache = (ConfigurationLRUCache) cacheDesc; + return new DataCacheLRUImpl(lruCache.Size); + } + + if (cacheDesc is ConfigurationExpiryTimeCache) { + ConfigurationExpiryTimeCache expCache = (ConfigurationExpiryTimeCache) cacheDesc; + return MakeTimeCache(expCache, statementContext, epStatementAgentInstanceHandle, schedulingService, scheduleBucket, streamNum); + } + + throw new IllegalStateException("Cache implementation class not configured"); + } + + protected DataCache MakeTimeCache(ConfigurationExpiryTimeCache expCache, StatementContext statementContext, EPStatementAgentInstanceHandle epStatementAgentInstanceHandle, SchedulingService schedulingService, ScheduleBucket scheduleBucket, int streamNum) { + return new DataCacheExpiringImpl(expCache.MaxAgeSeconds, expCache.PurgeIntervalSeconds, expCache.CacheReferenceType, + schedulingService, scheduleBucket.AllocateSlot(), epStatementAgentInstanceHandle, statementContext.TimeAbacus); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/db/DataCacheLRUImpl.cs b/NEsper.Core/NEsper.Core/epl/db/DataCacheLRUImpl.cs new file mode 100755 index 000000000..06ff48c23 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/db/DataCacheLRUImpl.cs @@ -0,0 +1,87 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.join.table; + +namespace com.espertech.esper.epl.db +{ + /// + /// Query result data _cache implementation that uses a least-recently-used algorithm + /// to store and evict query results. + /// + public class DataCacheLRUImpl : DataCache + { + private static readonly float HASH_TABLE_LOAD_FACTOR = 0.75f; + private readonly int _cacheSize; + private readonly LinkedHashMap _cache; + private readonly object _iLock = new object(); + + /// + /// Ctor. + /// + /// is the maximum _cache size + public DataCacheLRUImpl(int cacheSize) { + _cacheSize = cacheSize; + int hashTableCapacity = (int)Math.Ceiling(cacheSize / HASH_TABLE_LOAD_FACTOR) + 1; + _cache = new LinkedHashMap(); // hashTableCapacity + _cache.ShuffleOnAccess = true; + _cache.RemoveEldest += delegate { return _cache.Count > _cacheSize; }; + } + + /// + /// Retrieves an entry from the _cache. + /// The retrieved entry becomes the MRU (most recently used) entry. + /// + /// the key whose associated value is to be returned. + /// The number input parameters. + /// + /// the value associated to this key, or null if no value with this key exists in the _cache. + /// + public EventTable[] GetCached(Object[] methodParams, int numInputParameters) { + var key = DataCacheUtil.GetLookupKey(methodParams, numInputParameters); + return _cache.Get(key); + } + + /// + /// Adds an entry to this _cache. + /// If the _cache is full, the LRU (least recently used) entry is dropped. + /// + /// the keys with which the specified value is to be associated. + /// number of method param keys, from the start, that are for cache lookup + /// a value to be associated with the specified key. + public void PutCached(Object[] methodParams, int numLookupKeys, EventTable[] rows) + { + lock (_iLock) + { + var key = DataCacheUtil.GetLookupKey(methodParams, numLookupKeys); + _cache.Put(key, rows); + } + } + + /// + /// Returns the maximum _cache size. + /// + /// maximum _cache size + public int CacheSize + { + get { return _cacheSize; } + } + + public bool IsActive + { + get { return true; } + } + + public void Dispose() + { + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/db/DataCacheNullImpl.cs b/NEsper.Core/NEsper.Core/epl/db/DataCacheNullImpl.cs new file mode 100755 index 000000000..836e1e2be --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/db/DataCacheNullImpl.cs @@ -0,0 +1,36 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.@join.table; + +namespace com.espertech.esper.epl.db +{ + /// Null implementation for a data cache that doesn't ever hit. + public class DataCacheNullImpl : DataCache + { + public EventTable[] GetCached(Object[] methodParams, int numLookupKeys) + { + return null; + } + + public void PutCached(Object[] methodParams, int numLookupKeys, EventTable[] rows) + { + } + + public void Dispose() + { + } + + public bool IsActive + { + get { return false; } + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/db/DataCacheUtil.cs b/NEsper.Core/NEsper.Core/epl/db/DataCacheUtil.cs new file mode 100755 index 000000000..64a7ecc5c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/db/DataCacheUtil.cs @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; + +namespace com.espertech.esper.epl.db +{ + public class DataCacheUtil { + public static Object GetLookupKey(Object[] methodParams, int numInputParameters) { + if (numInputParameters == 0) { + return typeof(Object); + } else if (numInputParameters == 1) { + return methodParams[0]; + } else { + if (methodParams.Length == numInputParameters) { + return new MultiKeyUntyped(methodParams); + } + var lookupKeys = new Object[numInputParameters]; + Array.Copy(methodParams, 0, lookupKeys, 0, numInputParameters); + return new MultiKeyUntyped(lookupKeys); + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/db/DatabaseConfigException.cs b/NEsper.Core/NEsper.Core/epl/db/DatabaseConfigException.cs new file mode 100755 index 000000000..f359371c1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/db/DatabaseConfigException.cs @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +namespace com.espertech.esper.epl.db +{ + /// + /// Exception to indicate that a stream name could not be resolved. + /// + + [Serializable] + public class DatabaseConfigException : System.Exception + { + /// Ctor. + /// message + /// + + public DatabaseConfigException(String msg):base(msg) + { + } + + /// Ctor. + /// error message + /// + /// cause is the inner exception + /// + + public DatabaseConfigException(String message, System.Exception cause):base(message, cause) + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/db/DatabaseConfigService.cs b/NEsper.Core/NEsper.Core/epl/db/DatabaseConfigService.cs new file mode 100755 index 000000000..e3415fc4a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/db/DatabaseConfigService.cs @@ -0,0 +1,62 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; + +namespace com.espertech.esper.epl.db +{ + /// Service providing database connection factory and configuration information + /// for use with historical data polling. + /// + public interface DatabaseConfigService + { + /// Returns a connection factory for a configured database. + /// is the name of the database + /// + /// is a connection factory to use to get connections to the database + /// + /// DatabaseConfigException is thrown to indicate database configuration errors + DatabaseConnectionFactory GetConnectionFactory(String databaseName); + + /// + /// Returns the column metadata settings for the database. + /// + /// the database name + /// indicators for change case, metadata retrieval strategy and others + ColumnSettings GetQuerySetting(String databaseName); + + /// + /// Returns true to indicate a setting to retain connections between lookups. + /// + /// is the name of the database + /// is the sql text + /// The context attributes. + /// + /// a cache implementation to cache connection and prepared statements + /// + /// DatabaseConfigException is thrown to indicate database configuration errors + ConnectionCache GetConnectionCache(String databaseName, String preparedStatementText, IEnumerable contextAttributes); + + /// + /// Returns a new cache implementation for this database. + /// + /// is the name of the database to return a new cache implementation for for + /// The statement context. + /// is the statements-own handle for use in registering callbacks with services + /// The data cache factory. + /// The stream number. + /// + /// cache implementation + /// + /// DatabaseConfigException is thrown to indicate database configuration errors + DataCache GetDataCache(String databaseName, StatementContext statementContext, EPStatementAgentInstanceHandle epStatementAgentInstanceHandle, DataCacheFactory dataCacheFactory, int streamNumber); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/db/DatabaseConfigServiceImpl.cs b/NEsper.Core/NEsper.Core/epl/db/DatabaseConfigServiceImpl.cs new file mode 100755 index 000000000..2818bb659 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/db/DatabaseConfigServiceImpl.cs @@ -0,0 +1,161 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.schedule; + +namespace com.espertech.esper.epl.db +{ + /// Implementation provides database instance services such as connection factory and + /// connection settings. + /// + + public class DatabaseConfigServiceImpl : DatabaseConfigService + { + private readonly IDictionary _mapDatabaseRef; + private readonly IDictionary _connectionFactories; + private readonly SchedulingService _schedulingService; + private readonly ScheduleBucket _scheduleBucket; + private readonly EngineImportService _engineImportService; + + /// Ctor. + /// is a map of database name and database configuration entries + /// + /// is for scheduling callbacks for a cache + /// + /// is a system bucket for all scheduling callbacks for caches + /// + public DatabaseConfigServiceImpl( + IDictionary mapDatabaseRef, + SchedulingService schedulingService, + ScheduleBucket scheduleBucket, + EngineImportService engineImportService) + { + _mapDatabaseRef = mapDatabaseRef; + _connectionFactories = new Dictionary(); + _schedulingService = schedulingService; + _scheduleBucket = scheduleBucket; + _engineImportService = engineImportService; + } + + /// + /// Returns true to indicate a setting to retain connections between lookups. + /// + /// is the name of the database + /// is the sql text + /// The context attributes. + /// + /// a cache implementation to cache connection and prepared statements + /// + /// DatabaseConfigException is thrown to indicate database configuration errors + public virtual ConnectionCache GetConnectionCache(String databaseName, + String preparedStatementText, + IEnumerable contextAttributes) + { + ConfigurationDBRef config; + if (!_mapDatabaseRef.TryGetValue(databaseName, out config)) + { + throw new DatabaseConfigException("Cannot locate configuration information for database '" + databaseName + "'"); + } + + DatabaseConnectionFactory connectionFactory = GetConnectionFactory(databaseName); + + if (config.ConnectionLifecycle == ConnectionLifecycleEnum.RETAIN) + { + return new ConnectionCacheImpl(connectionFactory, preparedStatementText, contextAttributes); + } + else + { + return new ConnectionNoCacheImpl(connectionFactory, preparedStatementText, contextAttributes); + } + } + + /// + /// Returns a connection factory for a configured database. + /// + /// is the name of the database + /// + /// is a connection factory to use to get connections to the database + /// + /// DatabaseConfigException is thrown to indicate database configuration errors + public virtual DatabaseConnectionFactory GetConnectionFactory(String databaseName) + { + // check if we already have a reference + DatabaseConnectionFactory factory; + if (_connectionFactories.TryGetValue(databaseName, out factory)) + { + return factory; + } + + ConfigurationDBRef config; + if (!_mapDatabaseRef.TryGetValue(databaseName, out config)) + { + throw new DatabaseConfigException("Cannot locate configuration information for database '" + databaseName + "'"); + } + + ConnectionSettings settings = config.ConnectionSettings; + if (config.ConnectionFactoryDesc is DbDriverFactoryConnection) + { + DbDriverFactoryConnection dbConfig = (DbDriverFactoryConnection)config.ConnectionFactoryDesc; + factory = new DatabaseDriverConnFactory(dbConfig, settings); + } + else if (config.ConnectionFactoryDesc == null) + { + throw new DatabaseConfigException("No connection factory setting provided in configuration"); + } + else + { + throw new DatabaseConfigException("Unknown connection factory setting provided in configuration"); + } + + _connectionFactories[databaseName] = factory; + + return factory; + } + + /// + /// Returns a new cache implementation for this database. + /// + /// the name of the database to return a new cache implementation for for + /// is the statements-own handle for use in registering callbacks with services + /// cache implementation + /// DatabaseConfigException is thrown to indicate database configuration errors + public virtual DataCache GetDataCache(String databaseName, StatementContext statementContext, EPStatementAgentInstanceHandle epStatementAgentInstanceHandle, DataCacheFactory dataCacheFactory, int streamNumber) + { + ConfigurationDBRef config; + if (!_mapDatabaseRef.TryGetValue(databaseName, out config)) + { + throw new DatabaseConfigException("Cannot locate configuration information for database '" + databaseName + "'"); + } + + ConfigurationDataCache dataCacheDesc = config.DataCacheDesc; + return dataCacheFactory.GetDataCache(dataCacheDesc, statementContext, epStatementAgentInstanceHandle, _schedulingService, _scheduleBucket, streamNumber); + } + + public ColumnSettings GetQuerySetting(String databaseName) + { + ConfigurationDBRef config = _mapDatabaseRef.Get(databaseName); + if (config == null) + { + throw new DatabaseConfigException("Cannot locate configuration information for database '" + databaseName + '\''); + } + return new ColumnSettings( + config.MetadataRetrievalEnum, + config.ColumnChangeCase, + config.DataTypeMapping); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/db/DatabaseConnectionFactory.cs b/NEsper.Core/NEsper.Core/epl/db/DatabaseConnectionFactory.cs new file mode 100755 index 000000000..101943267 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/db/DatabaseConnectionFactory.cs @@ -0,0 +1,24 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +namespace com.espertech.esper.epl.db +{ + /// + /// Factory for new database connections. + /// + + public interface DatabaseConnectionFactory + { + /// + /// Gets the database driver. + /// + + DbDriver Driver { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/db/DatabaseDriverConnFactory.cs b/NEsper.Core/NEsper.Core/epl/db/DatabaseDriverConnFactory.cs new file mode 100755 index 000000000..af67baf67 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/db/DatabaseDriverConnFactory.cs @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using com.espertech.esper.client; + +namespace com.espertech.esper.epl.db +{ + /// + /// Database connection factory using DbProviderFactory to obtain connections. + /// + + public class DatabaseDriverConnFactory : DatabaseConnectionFactory + { + private readonly DbDriver dbDriver; + private readonly ConnectionSettings connectionSettings; + + /// + /// Gets the database driver. + /// + /// + public virtual DbDriver Driver + { + get { return dbDriver; } + } + + /// + /// Ctor. + /// + /// is the database provider configuration + /// are connection-level settings + /// DatabaseConfigException thrown if the driver class cannot be loaded + + public DatabaseDriverConnFactory(DbDriverFactoryConnection dbConfig, ConnectionSettings connectionSettings) + { + this.dbDriver = dbConfig.Driver; + this.connectionSettings = connectionSettings; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/db/DatabasePollingViewable.cs b/NEsper.Core/NEsper.Core/epl/db/DatabasePollingViewable.cs new file mode 100755 index 000000000..025106e6e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/db/DatabasePollingViewable.cs @@ -0,0 +1,435 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.threading; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.visitor; +using com.espertech.esper.epl.join.pollindex; +using com.espertech.esper.epl.join.table; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.epl.variable; +using com.espertech.esper.events; +using com.espertech.esper.schedule; +using com.espertech.esper.script; +using com.espertech.esper.view; + +namespace com.espertech.esper.epl.db +{ + /// + /// Implements a poller viewable that uses a polling strategy, a cache and + /// some input parameters extracted from event streams to perform the polling. + /// + + public class DatabasePollingViewable : HistoricalEventViewable + { + private readonly int _myStreamNumber; + private readonly PollExecStrategy _pollExecStrategy; + private readonly IList _inputParameters; + private readonly DataCache _dataCache; + private readonly EventType _eventType; + + private readonly IThreadLocal _dataCacheThreadLocal = ThreadLocalManager.Create(() => null); + + private ExprEvaluator[] _evaluators; + private ICollection _subordinateStreams; + private ExprEvaluatorContext _exprEvaluatorContext; + private StatementContext _statementContext; + + private static readonly EventBean[][] NULL_ROWS; + + static DatabasePollingViewable() + { + NULL_ROWS = new EventBean[1][]; + NULL_ROWS[0] = new EventBean[1]; + } + + private class IteratorIndexingStrategy : PollResultIndexingStrategy + { + public EventTable[] Index(IList pollResult, bool isActiveCache, StatementContext statementContext) + { + return new EventTable[] + { + new UnindexedEventTableList(pollResult, -1) + }; + } + + public String ToQueryPlan() + { + return GetType().Name + " unindexed"; + } + } + + private static readonly PollResultIndexingStrategy ITERATOR_INDEXING_STRATEGY = new IteratorIndexingStrategy(); + + /// + /// Returns the a set of stream numbers of all streams that provide + /// property values in any of the parameter expressions to the stream. + /// + /// + /// set of stream numbers + public ICollection RequiredStreams + { + get { return _subordinateStreams; } + } + + /// + /// Returns true if the parameters expressions to the historical require + /// other stream's data, or false if there are no parameters or all + /// parameter expressions are only contants and variables without + /// properties of other stream events. + /// + /// + /// indicator whether properties are required for parameter evaluation + public bool HasRequiredStreams + { + get { return _subordinateStreams.IsNotEmpty(); } + } + + /// + /// Historical views are expected to provide a thread-local data cache + /// for use in keeping row ( references) returned during + /// iteration stable, since the concept of a primary key does not exist. + /// + /// + /// thread-local cache, can be null for any thread to indicate no caching + public IThreadLocal DataCacheThreadLocal + { + get { return _dataCacheThreadLocal; } + } + + /// + /// Returns the optional data cache. + /// + /// + /// The optional data cache. + /// + public DataCache OptionalDataCache + { + get { return _dataCache; } + } + + /// + /// Ctor. + /// + /// is the stream number of the view + /// are the event property names providing input parameter keys + /// is the strategy to use for retrieving results + /// is looked up before using the strategy + /// is the type of events generated by the view + + public DatabasePollingViewable( + int myStreamNumber, + IList inputParameters, + PollExecStrategy pollExecStrategy, + DataCache dataCache, + EventType eventType) + { + _myStreamNumber = myStreamNumber; + _inputParameters = inputParameters; + _pollExecStrategy = pollExecStrategy; + _dataCache = dataCache; + _eventType = eventType; + } + + /// + /// Stops the view + /// + public virtual void Stop() + { + _pollExecStrategy.Dispose(); + _dataCache.Dispose(); + } + + /// + /// Validate the view. + /// + /// The engine import service. + /// supplies the types of streams against which to validate + /// for providing current time + /// for access to variables + /// + /// The scripting service. + /// The expression evaluator context. + /// The config snapshot. + /// The scheduling service. + /// The engine URI. + /// The SQL parameters. + /// The event adapter service. + /// The statement context + /// ExprValidationException is thrown to indicate an exception in validating the view + public void Validate(EngineImportService engineImportService, StreamTypeService streamTypeService, TimeProvider timeProvider, VariableService variableService, TableService tableService, ScriptingService scriptingService, ExprEvaluatorContext exprEvaluatorContext, ConfigurationInformation configSnapshot, SchedulingService schedulingService, string engineURI, IDictionary> sqlParameters, EventAdapterService eventAdapterService, StatementContext statementContext) + { + _statementContext = statementContext; + _evaluators = new ExprEvaluator[_inputParameters.Count]; + _subordinateStreams = new SortedSet(); + _exprEvaluatorContext = exprEvaluatorContext; + + var count = 0; + var validationContext = new ExprValidationContext( + streamTypeService, engineImportService, + statementContext.StatementExtensionServicesContext, null, + timeProvider, variableService, tableService, + exprEvaluatorContext, eventAdapterService, + statementContext.StatementName, + statementContext.StatementId, + statementContext.Annotations, null, + scriptingService, false, false, true, false, null, false); + + foreach (var inputParam in _inputParameters) + { + var raw = FindSQLExpressionNode(_myStreamNumber, count, sqlParameters); + if (raw == null) + { + throw new ExprValidationException( + "Internal error find expression for historical stream parameter " + count + " stream " + + _myStreamNumber); + } + var evaluator = ExprNodeUtility.GetValidatedSubtree(ExprNodeOrigin.DATABASEPOLL, raw, validationContext); + _evaluators[count++] = evaluator.ExprEvaluator; + + var visitor = new ExprNodeIdentifierCollectVisitor(); + visitor.Visit(evaluator); + foreach (var identNode in visitor.ExprProperties) + { + if (identNode.StreamId == _myStreamNumber) + { + throw new ExprValidationException("Invalid expression '" + inputParam + + "' resolves to the historical data itself"); + } + _subordinateStreams.Add(identNode.StreamId); + } + } + } + + /// + /// Poll for stored historical or reference data using events per stream and + /// returing for each event-per-stream row a separate list with events + /// representing the poll result. + /// + /// is the events per stream where the + /// first dimension is a number of rows (often 1 depending on windows used) and + /// the second dimension is the number of streams participating in a join. + /// the strategy to use for converting poll results into a indexed table for fast lookup + /// The expression evaluator context. + /// + /// array of lists with one list for each event-per-stream row + /// + public EventTable[][] Poll(EventBean[][] lookupEventsPerStream, PollResultIndexingStrategy indexingStrategy, ExprEvaluatorContext exprEvaluatorContext) + { + var localDataCache = _dataCacheThreadLocal.GetOrCreate(); + var strategyStarted = false; + + var resultPerInputRow = new EventTable[lookupEventsPerStream.Length][]; + + // Get input parameters for each row + for (var row = 0; row < lookupEventsPerStream.Length; row++) + { + var lookupValues = new Object[_inputParameters.Count]; + + // Build lookup keys + for (var valueNum = 0; valueNum < _inputParameters.Count; valueNum++) + { + var eventsPerStream = lookupEventsPerStream[row]; + var lookupValue = _evaluators[valueNum].Evaluate(new EvaluateParams(eventsPerStream, true, exprEvaluatorContext)); + lookupValues[valueNum] = lookupValue; + } + + EventTable[] result = null; + + // try the threadlocal iteration cache, if set + if (localDataCache != null) + { + result = localDataCache.GetCached(lookupValues, lookupValues.Length); + } + + // try the connection cache + if (result == null) + { + var multi = _dataCache.GetCached(lookupValues, lookupValues.Length); + if (multi != null) + { + result = multi; + if (localDataCache != null) + { + localDataCache.PutCached(lookupValues, lookupValues.Length, result); + } + } + } + + // use the result from cache + if (result != null) + // found in cache + { + resultPerInputRow[row] = result; + } + // not found in cache, get from actual polling (db query) + else + { + try + { + if (!strategyStarted) + { + _pollExecStrategy.Start(); + strategyStarted = true; + } + + // Poll using the polling execution strategy and lookup values + var pollResult = _pollExecStrategy.Poll(lookupValues, exprEvaluatorContext); + + // index the result, if required, using an indexing strategy + var indexTable = indexingStrategy.Index(pollResult, _dataCache.IsActive, _statementContext); + + // assign to row + resultPerInputRow[row] = indexTable; + + // save in cache + _dataCache.PutCached(lookupValues, lookupValues.Length, indexTable); + + if (localDataCache != null) + { + localDataCache.PutCached(lookupValues, lookupValues.Length, indexTable); + } + } + catch (EPException) + { + if (strategyStarted) + { + _pollExecStrategy.Done(); + } + + throw; + } + } + } + + + if (strategyStarted) + { + _pollExecStrategy.Done(); + } + + return resultPerInputRow; + } + + /// + /// Add a view to the viewable object. + /// + /// to add + /// view to add + public virtual View AddView(View view) + { + view.Parent = this; + return view; + } + + /// + /// Returns all added views. + /// + /// list of added views + public View[] Views + { + get { return EmptyList; } + } + + private static readonly View[] EmptyList = { }; + + /// + /// Remove a view. + /// + /// to remove + /// + /// true to indicate that the view to be removed existed within this view, false if the view to + /// remove could not be found + /// + public virtual bool RemoveView(View view) + { + throw new NotSupportedException("Subviews not supported"); + } + + /// + /// Test is there are any views to the Viewable. + /// + /// + /// true indicating there are child views, false indicating there are no child views + /// + public virtual bool HasViews + { + get { return false; } + } + + /// + /// Provides metadata information about the type of object the event collection contains. + /// + /// + /// metadata for the objects in the collection + /// + public virtual EventType EventType + { + get { return _eventType; } + } + + /// + /// Returns an enumerator that iterates through the collection. + /// + /// + /// A that can be used to iterate through the collection. + /// + public IEnumerator GetEnumerator() + { + var result = Poll(NULL_ROWS, ITERATOR_INDEXING_STRATEGY, _exprEvaluatorContext); + + foreach (var tableList in result) + { + foreach (var table in tableList) + { + foreach (var e in table) + { + yield return e; + } + } + } + } + + #region IEnumerable Members + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + #endregion + + public void RemoveAllViews() + { + throw new UnsupportedOperationException("Subviews not supported"); + } + + private static ExprNode FindSQLExpressionNode(int myStreamNumber, int count, IDictionary> sqlParameters) + { + if ((sqlParameters == null) || (sqlParameters.IsEmpty())) + { + return null; + } + var paramList = sqlParameters.Get(myStreamNumber); + if ((paramList == null) || (paramList.IsEmpty()) || (paramList.Count < (count + 1))) + { + return null; + } + return paramList[count]; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/db/DatabasePollingViewableFactory.cs b/NEsper.Core/NEsper.Core/epl/db/DatabasePollingViewableFactory.cs new file mode 100755 index 000000000..ff0468aae --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/db/DatabasePollingViewableFactory.cs @@ -0,0 +1,614 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Text.RegularExpressions; + +using Antlr4.Runtime; + +using com.espertech.esper.client; +using com.espertech.esper.compat.logging; +using com.espertech.esper.client.hook; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.parse; +using com.espertech.esper.epl.spec; +using com.espertech.esper.events; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.epl.db +{ + /// + /// Factory for a view onto historical data via SQL statement. + /// + + public class DatabasePollingViewableFactory + { + public const String SAMPLE_WHERECLAUSE_PLACEHOLDER = "$ESPER-SAMPLE-WHERE"; + + /// + /// Creates the viewable for polling via database SQL query. + /// + /// The statement id. + /// is the stream number of the view + /// provides the SQL statement, database name and additional info + /// for getting database connection and settings + /// for generating event beans from database information + /// The ep statement agent instance handle. + /// The db attributes. + /// The column type conversion hook. + /// The output row conversion hook. + /// if set to true [enable JDBC logging]. + /// The data cache factory. + /// The statement context. + /// + /// viewable providing poll functionality + /// + /// the validation failed + public static HistoricalEventViewable CreateDBStatementView( + int statementId, + int streamNumber, + DBStatementStreamSpec databaseStreamSpec, + DatabaseConfigService databaseConfigService, + EventAdapterService eventAdapterService, + EPStatementAgentInstanceHandle epStatementAgentInstanceHandle, + IEnumerable contextAttributes, + SQLColumnTypeConversion columnTypeConversionHook, + SQLOutputRowConversion outputRowConversionHook, + bool enableAdoLogging, + DataCacheFactory dataCacheFactory, + StatementContext statementContext) + { + // Parse the SQL for placeholders and text fragments + var sqlFragments = GetSqlFragments(databaseStreamSpec); + IList invocationInputParameters = new List(); + foreach (var fragment in sqlFragments) + { + if ((fragment.IsParameter) && (fragment.Value != SAMPLE_WHERECLAUSE_PLACEHOLDER)) + { + invocationInputParameters.Add(fragment.Value); + } + } + + // Get the database information + var databaseName = databaseStreamSpec.DatabaseName; + var dbDriver = GetDatabaseConnectionFactory(databaseConfigService, databaseName).Driver; + var dbCommand = dbDriver.CreateCommand( + sqlFragments, + GetMetaDataSettings(databaseConfigService, databaseName), + contextAttributes); + + if (Log.IsDebugEnabled) + { + Log.Debug(".CreateDBStatementView dbCommand=" + dbCommand); + } + + var queryMetaData = GetQueryMetaData( + databaseStreamSpec, + databaseConfigService, + dbCommand, + contextAttributes); + + Func columnTypeConversionFunc = null; + if (columnTypeConversionHook != null) + { + columnTypeConversionFunc = columnTypeConversionHook.GetColumnType; + } + + Func outputRowConversionFunc = null; + if (outputRowConversionHook != null) + { + outputRowConversionFunc = outputRowConversionHook.GetOutputRowType; + } + + // Construct an event type from SQL query result metadata + var eventType = CreateEventType( + statementId, + streamNumber, + queryMetaData, + eventAdapterService, + databaseStreamSpec, + columnTypeConversionFunc, + outputRowConversionFunc); + + // Get a proper connection and data cache + ConnectionCache connectionCache; + DataCache dataCache; + try + { + connectionCache = databaseConfigService.GetConnectionCache( + databaseName, dbCommand.PseudoText, contextAttributes); + dataCache = databaseConfigService.GetDataCache( + databaseName, statementContext, epStatementAgentInstanceHandle, dataCacheFactory, streamNumber); + } + catch (DatabaseConfigException e) + { + const string text = "Error obtaining cache configuration"; + Log.Error(text, e); + throw new ExprValidationException(text + ", reason: " + e.Message, e); + } + + var dbPollStrategy = new PollExecStrategyDBQuery( + eventAdapterService, + eventType, + connectionCache, + dbCommand.CommandText, + queryMetaData.OutputParameters, + columnTypeConversionHook, + outputRowConversionHook); + + return new DatabasePollingViewable( + streamNumber, + invocationInputParameters, + dbPollStrategy, + dataCache, + eventType); + } + + /// + /// Gets the meta data settings from the database configuration service for the specified + /// database name. + /// + /// + /// + /// + + private static ColumnSettings GetMetaDataSettings( + DatabaseConfigService databaseConfigService, + String databaseName) + { + try + { + return databaseConfigService.GetQuerySetting(databaseName); + } + catch (DatabaseConfigException ex) + { + var text = "Error connecting to database '" + databaseName + '\''; + Log.Error(text, ex); + throw new ExprValidationException(text + ", reason: " + ex.Message, ex); + } + } + + /// + /// Creates an event type from the query meta data. + /// + /// The statement id. + /// The stream number. + /// The query meta data. + /// The event adapter service. + /// The database stream spec. + /// The column type conversion hook. + /// The output row conversion hook. + /// + private static EventType CreateEventType( + int statementId, + int streamNumber, + QueryMetaData queryMetaData, + EventAdapterService eventAdapterService, + DBStatementStreamSpec databaseStreamSpec, + Func columnTypeConversionHook, + Func outputRowConversionHook) + { + var columnNum = 1; + var eventTypeFields = new Dictionary(); + foreach (var entry in queryMetaData.OutputParameters) + { + var name = entry.Key; + var dbOutputDesc = entry.Value; + + Type clazz; + if (dbOutputDesc.OptionalBinding != null) + { + clazz = dbOutputDesc.OptionalBinding.DataType; + } + else + { + clazz = dbOutputDesc.DataType; + } + + if (columnTypeConversionHook != null) + { + + var newValue = columnTypeConversionHook.Invoke( + new SQLColumnTypeContext( + databaseStreamSpec.DatabaseName, + databaseStreamSpec.SqlWithSubsParams, + name, clazz, + dbOutputDesc.SqlType, + columnNum)); + if (newValue != null) + { + clazz = newValue; + } + + } + + eventTypeFields[name] = clazz.GetBoxedType(); + columnNum++; + } + + EventType eventType; + if (outputRowConversionHook == null) + { + var outputEventType = statementId + "_dbpoll_" + streamNumber; + eventType = eventAdapterService.CreateAnonymousMapType(outputEventType, eventTypeFields, true); + } + else + { + var carrierClass = outputRowConversionHook.Invoke( + new SQLOutputRowTypeContext( + databaseStreamSpec.DatabaseName, + databaseStreamSpec.SqlWithSubsParams, + eventTypeFields)); + if (carrierClass == null) + { + throw new ExprValidationException("Output row conversion hook returned no type"); + } + + eventType = eventAdapterService.AddBeanType(carrierClass.FullName, carrierClass, false, false, false); + } + + return eventType; + } + + /// + /// Gets the database connection factory. + /// + /// The database config service. + /// Name of the database. + /// + private static DatabaseConnectionFactory GetDatabaseConnectionFactory( + DatabaseConfigService databaseConfigService, + string databaseName) + { + DatabaseConnectionFactory databaseConnectionFactory; + try + { + databaseConnectionFactory = databaseConfigService.GetConnectionFactory(databaseName); + } + catch (DatabaseConfigException ex) + { + var text = "Error connecting to database '" + databaseName + "'"; + Log.Error(text, ex); + throw new ExprValidationException(text + ", reason: " + ex.Message, ex); + } + return databaseConnectionFactory; + } + + /// + /// Gets the SQL fragments. + /// + /// The database stream spec. + /// + private static IList GetSqlFragments(DBStatementStreamSpec databaseStreamSpec) + { + IList sqlFragments; + try + { + sqlFragments = PlaceholderParser.ParsePlaceholder(databaseStreamSpec.SqlWithSubsParams); + } + catch (PlaceholderParseException ex) + { + const string text = "Error parsing SQL"; + throw new ExprValidationException(text + ", reason: " + ex.Message, ex); + } + return sqlFragments; + } + + /// + /// Gets the query meta data. + /// + /// The database stream spec. + /// The database config service. + /// The database command. + /// The context attributes. + /// + private static QueryMetaData GetQueryMetaData( + DBStatementStreamSpec databaseStreamSpec, + DatabaseConfigService databaseConfigService, + DbDriverCommand dbCommand, + IEnumerable contextAttributes) + { + // Get a database connection + var databaseName = databaseStreamSpec.DatabaseName; + //DatabaseConnectionFactory databaseConnectionFactory = GetDatabaseConnectionFactory(databaseConfigService, databaseName); + var metadataSetting = dbCommand.MetaDataSettings; + + QueryMetaData queryMetaData; + try + { + // On default setting, if we detect Oracle in the connection then don't query metadata from prepared statement + var metaOriginPolicy = metadataSetting.MetadataRetrievalEnum; + if (metaOriginPolicy == ConfigurationDBRef.MetadataOriginEnum.DEFAULT) + { + // Ask the driver how it interprets the default meta origin policy; the + // esper code has a specific hook for Oracle. We have moved this into + // the driver to avoid specifically coding behavior to a driver. + metaOriginPolicy = dbCommand.Driver.DefaultMetaOriginPolicy; + } + + switch (metaOriginPolicy) + { + case ConfigurationDBRef.MetadataOriginEnum.METADATA: + case ConfigurationDBRef.MetadataOriginEnum.DEFAULT: + queryMetaData = dbCommand.MetaData; + break; + case ConfigurationDBRef.MetadataOriginEnum.SAMPLE: + { + var parameterDesc = dbCommand.ParameterDescription; + + String sampleSQL; + if (databaseStreamSpec.MetadataSQL != null) + { + sampleSQL = databaseStreamSpec.MetadataSQL; + if (Log.IsInfoEnabled) + { + Log.Info(".GetQueryMetaData Using provided sample SQL '" + sampleSQL + "'"); + } + } + else + { + // Create the sample SQL by replacing placeholders with null and + // SAMPLE_WHERECLAUSE_PLACEHOLDER with a "where 1=0" clause + sampleSQL = CreateSamplePlaceholderStatement(dbCommand.Fragments); + + if (Log.IsInfoEnabled) + { + Log.Info(".GetQueryMetaData Using un-lexed sample SQL '" + sampleSQL + "'"); + } + + // If there is no SAMPLE_WHERECLAUSE_PLACEHOLDER, lexical analyse the SQL + // adding a "where 1=0" clause. + if (parameterDesc.BuiltinIdentifiers.Count != 1) + { + sampleSQL = LexSampleSQL(sampleSQL); + if (Log.IsInfoEnabled) + { + Log.Info(".GetQueryMetaData Using lexed sample SQL '" + sampleSQL + "'"); + } + } + } + + // finally get the metadata by firing the sample SQL + queryMetaData = GetExampleQueryMetaData( + dbCommand.Driver, sampleSQL, metadataSetting, contextAttributes); + } + break; + default: + throw new ArgumentException( + "MetaOriginPolicy contained an unhandled value: #" + metaOriginPolicy); + } + } + catch (DatabaseConfigException ex) + { + var text = "Error connecting to database '" + databaseName + '\''; + Log.Error(text, ex); + throw new ExprValidationException(text + ", reason: " + ex.Message, ex); + } + + return queryMetaData; + } + + /// + /// Lexes the sample SQL and inserts a "where 1=0" where-clause. + /// + /// to inspect using lexer + /// sample SQL with where-clause inserted + /// indicates a lexer problem + public static String LexSampleSQL(String querySQL) + { + querySQL = Regex.Replace(querySQL, "\\s\\s+|\\n|\\r", m => " "); + + ICharStream input; + try + { + input = new NoCaseSensitiveStream(querySQL); + } + catch (IOException ex) + { + throw new ExprValidationException("IOException lexing query SQL '" + querySQL + '\'', ex); + } + + var whereIndex = -1; + var groupbyIndex = -1; + var havingIndex = -1; + var orderByIndex = -1; + var unionIndexes = new List(); + + var lex = ParseHelper.NewLexer(input); + var tokens = new CommonTokenStream(lex); + tokens.Fill(); + + IList tokenList = tokens.GetTokens(); + + for (var i = 0; i < tokenList.Count; i++) + { + var token = (IToken) tokenList[i]; + if ((token == null) || token.Text == null) + { + break; + } + var text = token.Text.ToLower().Trim(); + if (text == "") + { + continue; + } + + switch (text) + { + case "where": + whereIndex = token.Column + 1; + break; + case "group": + groupbyIndex = token.Column + 1; + break; + case "having": + havingIndex = token.Column + 1; + break; + case "order": + orderByIndex = token.Column + 1; + break; + case "union": + unionIndexes.Add(token.Column + 1); + break; + } + } + + // If we have a union, break string into subselects and process each + if (unionIndexes.Count != 0) + { + String fragment; + String lexedFragment; + var changedSQL = new StringBuilder(); + var lastIndex = 0; + for (var i = 0; i < unionIndexes.Count; i++) + { + var index = unionIndexes[i]; + if (i > 0) + { + fragment = querySQL.Substring(lastIndex + 5, index - 6 - lastIndex); + } + else + { + fragment = querySQL.Substring(lastIndex, index - 1 - lastIndex); + } + + lexedFragment = LexSampleSQL(fragment); + + if (i > 0) + { + changedSQL.Append("union "); + } + changedSQL.Append(lexedFragment); + lastIndex = index - 1; + } + + // last part after last union + fragment = querySQL.Substring(lastIndex + 5); + lexedFragment = LexSampleSQL(fragment); + changedSQL.Append("union "); + changedSQL.Append(lexedFragment); + + return changedSQL.ToString(); + } + + // Found a where clause, simplest cases + if (whereIndex != -1) + { + var changedSQL = new StringBuilder(); + var prefix = querySQL.Substring(0, whereIndex + 5); + var suffix = querySQL.Substring(whereIndex + 5); + changedSQL.Append(prefix); + changedSQL.Append("1=0 and "); + changedSQL.Append(suffix); + return changedSQL.ToString(); + } + + // No where clause, find group-by + int insertIndex; + if (groupbyIndex != -1) + { + insertIndex = groupbyIndex; + } + else if (havingIndex != -1) + { + insertIndex = havingIndex; + } + else if (orderByIndex != -1) + { + insertIndex = orderByIndex; + } + else + { + var changedSQL = new StringBuilder(); + changedSQL.Append(querySQL); + changedSQL.Append(" where 1=0 "); + return changedSQL.ToString(); + } + + try + { + var changedSQL = new StringBuilder(); + var prefix = querySQL.Substring(0, insertIndex - 1); + changedSQL.Append(prefix); + changedSQL.Append("where 1=0 "); + var suffix = querySQL.Substring(insertIndex - 1); + changedSQL.Append(suffix); + return changedSQL.ToString(); + } + catch (Exception ex) + { + const string text = + "Error constructing sample SQL to retrieve metadata for JDBC-drivers that don't support metadata, consider using the " + + SAMPLE_WHERECLAUSE_PLACEHOLDER + " placeholder or providing a sample SQL"; + Log.Error(text, ex); + throw new ExprValidationException(text, ex); + } + } + + /// + /// Gets the example query meta data. + /// + /// The driver. + /// The sample SQL. + /// The metadata setting. + /// The context attributes. + /// + private static QueryMetaData GetExampleQueryMetaData( + DbDriver dbDriver, + String sampleSQL, + ColumnSettings metadataSetting, + IEnumerable contextAttributes) + { + var sampleSQLFragments = PlaceholderParser.ParsePlaceholder(sampleSQL); + using (var dbCommand = dbDriver.CreateCommand(sampleSQLFragments, metadataSetting, contextAttributes)) + { + return dbCommand.MetaData; + } + } + + /// + /// Creates the sample placeholder statement. + /// + /// The parse fragements. + /// + private static String CreateSamplePlaceholderStatement(IEnumerable parseFragements) + { + var buffer = new StringBuilder(); + foreach (var fragment in parseFragements) + { + if (!fragment.IsParameter) + { + buffer.Append(fragment.Value); + } + else + { + if (fragment.Value.Equals(SAMPLE_WHERECLAUSE_PLACEHOLDER)) + { + buffer.Append(" where 1=0 "); + break; + } + else + { + buffer.Append("null"); + } + } + } + return buffer.ToString(); + } + + private static readonly ILog Log = + LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/db/DbColumn.cs b/NEsper.Core/NEsper.Core/epl/db/DbColumn.cs new file mode 100755 index 000000000..b009fad00 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/db/DbColumn.cs @@ -0,0 +1,19 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; + +namespace com.espertech.esper.epl.db +{ + public struct DbColumn + { + public String Name; + public int Ordinal; + } +} diff --git a/NEsper.Core/NEsper.Core/epl/db/DbDriver.cs b/NEsper.Core/NEsper.Core/epl/db/DbDriver.cs new file mode 100755 index 000000000..2ab97cfe9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/db/DbDriver.cs @@ -0,0 +1,65 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; +using System.Data.Common; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.db +{ + /// + /// Database driver semantics are captured in the DbDriver. Each + /// driver instance is completely separate from other instances. + /// Drivers encapsulate management of the connection, so specific + /// properties are given to it so that it can build its connection + /// string. + /// + + public interface DbDriver + { + /// + /// Gets the default meta origin policy. + /// + /// The default meta origin policy. + ConfigurationDBRef.MetadataOriginEnum DefaultMetaOriginPolicy { get; } + + /// + /// Creates a database driver command from a collection of fragments. + /// + /// The SQL fragments. + /// The metadata settings. + /// The context attributes. + /// + DbDriverCommand CreateCommand(IEnumerable sqlFragments, + ColumnSettings metadataSettings, + IEnumerable contextAttributes); + + /// + /// Gets or sets the properties for the driver. + /// + /// The properties. + Properties Properties { get; set; } + + /// + /// Gets the connection string associated with this driver. + /// + String ConnectionString { get; } + + /// + /// Creates a database connection; this should be used sparingly if possible. + /// + /// + + DbConnection CreateConnection(); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/db/DbDriverCommand.cs b/NEsper.Core/NEsper.Core/epl/db/DbDriverCommand.cs new file mode 100755 index 000000000..7cf580c83 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/db/DbDriverCommand.cs @@ -0,0 +1,80 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; +using System.Data.Common; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.db +{ + /// + /// Minor abstraction on top of the IDbCommand. The DbDriverCommand + /// provides callers (above the driver command) to obtain information + /// about the command using a notation that is similar to JDBC (i.e. it + /// uses ? for parameters); below it ensures that the underlying + /// connection uses proper ADO.NET conventions for the driver. It also + /// handles certain other nuances that differ between ADO.NET driver + /// implementations, encapsulating that behavior within the driver. + /// + + public interface DbDriverCommand : IDisposable + { + /// + /// Clones the driver command. + /// + /// + DbDriverCommand Clone(); + + /// + /// Gets the driver associated with this command. + /// + DbDriver Driver { get; } + + /// + /// Gets the actual database command. + /// + /// The command. + DbCommand Command { get; } + + /// + /// Gets the meta data. + /// + /// The meta data. + QueryMetaData MetaData { get; } + + /// + /// Gets the meta data settings associated with this command. + /// + ColumnSettings MetaDataSettings { get; } + + /// + /// Gets a list of parameters. + /// + /// The parameters. + SQLParameterDesc ParameterDescription { get; } + + /// + /// Gets the fragments. If the command was not created through + /// supplied fragments, this method will throw an exception. + /// + /// The fragments. + IEnumerable Fragments { get;} + + /// + /// Gets the actual SQL that is sent to the driver. + /// + String CommandText { get; } + + /// + /// Gets the pseudo SQL that is provided to and from the client. + /// + String PseudoText { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/db/DbDriverSchema.cs b/NEsper.Core/NEsper.Core/epl/db/DbDriverSchema.cs new file mode 100755 index 000000000..ff911d5ad --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/db/DbDriverSchema.cs @@ -0,0 +1,39 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System.Collections.Generic; + +namespace com.espertech.esper.epl.db +{ + /// + /// Provides the schema associated with a command. + /// + + public interface DbDriverSchema + { + /// + /// Gets the column names. + /// + /// The column names. + IEnumerable ColumnNames { get; } + + /// + /// Gets the column INFO. + /// + /// The column INFO. + IEnumerable> ColumnInfo { get; } + + /// + /// Gets the associated + /// with the given column name. + /// + /// + DBOutputTypeDesc this[string index] { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/db/PollExecStrategy.cs b/NEsper.Core/NEsper.Core/epl/db/PollExecStrategy.cs new file mode 100755 index 000000000..9dfb2cf2a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/db/PollExecStrategy.cs @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.db +{ + /// + /// Interface for polling data from a data source such as a relational database. + /// Lifecycle methods are for managing connection resources. + /// + public interface PollExecStrategy : IDisposable + { + /// Start the poll, called before any poll operation. + void Start(); + + /// + /// Poll events using the keys provided. + /// + /// is keys for exeuting a query or such + /// The expression evaluator context. + /// + /// a list of events for the keys + /// + + IList Poll(Object[] lookupValues, ExprEvaluatorContext exprEvaluatorContext); + + /// Indicate we are done polling and can release resources. + void Done(); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/db/PollExecStrategyDBQuery.cs b/NEsper.Core/NEsper.Core/epl/db/PollExecStrategyDBQuery.cs new file mode 100755 index 000000000..5027ff2b8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/db/PollExecStrategyDBQuery.cs @@ -0,0 +1,318 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; +using System.Data.Common; + +using com.espertech.esper.client; +using com.espertech.esper.compat.logging; +using com.espertech.esper.client.hook; +using com.espertech.esper.collection; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events; +using com.espertech.esper.events.bean; +using com.espertech.esper.util; + +using DataMap = System.Collections.Generic.IDictionary; + +namespace com.espertech.esper.epl.db +{ + /// + /// Viewable providing historical data from a database. + /// + + public class PollExecStrategyDBQuery : PollExecStrategy + { + private readonly EventAdapterService _eventAdapterService; + private readonly String _preparedStatementText; + private readonly IDictionary _outputTypes; + private readonly ConnectionCache _connectionCache; + private readonly EventType _eventType; + private readonly SQLColumnTypeConversion _columnTypeConversionHook; + private readonly SQLOutputRowConversion _outputRowConversionHook; + + private Pair _resources; + + /// Ctor. + /// for generating event beans + /// is the event type that this poll generates + /// caches Connection and PreparedStatement + /// is the SQL to use for polling + /// describe columns selected by the SQL + /// hook to convert rows, if any hook is registered + /// hook to convert columns, if any hook is registered + public PollExecStrategyDBQuery( + EventAdapterService eventAdapterService, + EventType eventType, + ConnectionCache connectionCache, + String preparedStatementText, + IDictionary outputTypes, + SQLColumnTypeConversion columnTypeConversionHook, + SQLOutputRowConversion outputRowConversionHook) + { + _eventAdapterService = eventAdapterService; + _eventType = eventType; + _connectionCache = connectionCache; + _preparedStatementText = preparedStatementText; + _outputTypes = outputTypes; + _columnTypeConversionHook = columnTypeConversionHook; + _outputRowConversionHook = outputRowConversionHook; + } + + /// + /// Start the poll, called before any poll operation. + /// + public virtual void Start() + { + _resources = _connectionCache.GetConnection(); + } + + /// + /// Indicate we are done polling and can release resources. + /// + public virtual void Done() + { + _connectionCache.DoneWith(_resources); + } + + /// + /// Indicate we are no going to use this object again. + /// + public virtual void Dispose() + { + _connectionCache.Dispose(); + } + + /// + /// Poll events using the keys provided. + /// + /// is keys for exeuting a query or such + /// The expression evaluator context. + /// + /// a list of events for the keys + /// + public IList Poll(Object[] lookupValues, ExprEvaluatorContext exprEvaluatorContext) + { + IList result; + try + { + result = Execute(_resources.Second, lookupValues); + } + catch (EPException) + { + _connectionCache.DoneWith(_resources); + throw; + } + + return result; + } + + private List _dbInfoList = null; + + private IList Execute(DbDriverCommand driverCommand, Object[] lookupValuePerStream) + { + using (DbDriverCommand myDriverCommand = driverCommand.Clone()) + { + DbCommand dbCommand = myDriverCommand.Command; + + if (ExecutionPathDebugLog.IsEnabled && Log.IsInfoEnabled) + { + Log.Info(".execute Executing prepared statement '{0}'", dbCommand.CommandText); + } + + int dbParamCount = dbCommand.Parameters.Count; + if (dbParamCount != lookupValuePerStream.Length) + { + throw new ArgumentException("Only those parameters that have been prepared may be used here"); + } + + DbParameter dbParam; + + // set parameters + SQLInputParameterContext inputParameterContext = null; + if (_columnTypeConversionHook != null) + { + inputParameterContext = new SQLInputParameterContext(); + } + + for (int i = 0; i < lookupValuePerStream.Length; i++) + { + try + { + Object parameter = lookupValuePerStream[i]; + if (ExecutionPathDebugLog.IsEnabled && Log.IsInfoEnabled) + { + Log.Info(".Execute Setting parameter " + " to " + parameter + " typed " + + ((parameter == null) ? "null" : parameter.GetType().Name)); + } + + if (_columnTypeConversionHook != null) + { + inputParameterContext.ParameterNumber = i + 1; + inputParameterContext.ParameterValue = parameter; + parameter = _columnTypeConversionHook.GetParameterValue(inputParameterContext); + } + + dbParam = dbCommand.Parameters[i]; + dbParam.Value = parameter ?? DBNull.Value; + } + catch (DbException ex) + { + throw new EPException("Error setting parameter " + i, ex); + } + } + + // execute + try + { + // generate events for result set + IList rows = new List(); + + using (DbDataReader dataReader = dbCommand.ExecuteReader()) + { + try + { + SQLColumnValueContext valueContext = null; + if (_columnTypeConversionHook != null) + { + valueContext = new SQLColumnValueContext(); + } + + SQLOutputRowValueContext rowContext = null; + if (_outputRowConversionHook != null) + { + rowContext = new SQLOutputRowValueContext(); + } + + int rowNum = 0; + + if (dataReader.HasRows) + { + // Determine how many fields we will be receiving + int fieldCount = dataReader.FieldCount; + // Allocate a buffer to hold the results of the row + Object[] rawData = new object[fieldCount]; + // Convert the names of columns into ordinal indices and prepare + // them so that we only have to incur this cost when we first notice + // the reader has rows. + if (_dbInfoList == null) + { + _dbInfoList = new List(); + foreach (KeyValuePair entry in _outputTypes) + { + DbInfo dbInfo = new DbInfo(); + dbInfo.Name = entry.Key; + dbInfo.Ordinal = dataReader.GetOrdinal(dbInfo.Name); + dbInfo.OutputTypeDesc = entry.Value; + dbInfo.Binding = entry.Value.OptionalBinding; + _dbInfoList.Add(dbInfo); + } + } + + var fieldNames = new string[fieldCount]; + for (int ii = 0; ii < fieldCount; ii++) + { + fieldNames[ii] = dataReader.GetName(ii); + } + + // Anyone know if the ordinal will always be the same every time + // the query is executed; if so, we could certainly cache this + // dbInfoList so that we only have to do that once for the lifetime + // of the statement. + while (dataReader.Read()) + { + int colNum = 1; + + DataMap row = new Dictionary(); + // Get all of the values for the row in one shot + dataReader.GetValues(rawData); + // Convert the items into raw row objects + foreach (DbInfo dbInfo in _dbInfoList) + { + Object value = rawData[dbInfo.Ordinal]; + if (value == DBNull.Value) + { + value = null; + } + else if (dbInfo.Binding != null) + { + value = dbInfo.Binding.GetValue(value, dbInfo.Name); + } + else if (value.GetType() != dbInfo.OutputTypeDesc.DataType) + { + value = Convert.ChangeType(value, dbInfo.OutputTypeDesc.DataType); + } + + if (_columnTypeConversionHook != null) + { + valueContext.ColumnName = fieldNames[colNum - 1]; + valueContext.ColumnNumber = colNum; + valueContext.ColumnValue = value; + + value = _columnTypeConversionHook.GetColumnValue(valueContext); + } + + row[dbInfo.Name] = value; + + colNum++; + } + + EventBean eventBeanRow = null; + if (_outputRowConversionHook == null) + { + eventBeanRow = _eventAdapterService.AdapterForTypedMap(row, _eventType); + } + else + { + rowContext.Values = row; + rowContext.RowNum = rowNum; + Object rowData = _outputRowConversionHook.GetOutputRow(rowContext); + if (rowData != null) + { + eventBeanRow = _eventAdapterService.AdapterForTypedObject(rowData, (BeanEventType)_eventType); + } + } + + if (eventBeanRow != null) + { + rows.Add(eventBeanRow); + rowNum++; + } + } + } + } + catch (DbException ex) + { + throw new EPException( + "Error reading results for statement '" + _preparedStatementText + "'", ex); + } + } + + + return rows; + } + catch (DbException ex) + { + throw new EPException("Error executing statement '" + _preparedStatementText + "'", ex); + } + } + } + + struct DbInfo + { + public string Name; + public int Ordinal; + public DBOutputTypeDesc OutputTypeDesc; + public DatabaseTypeBinding Binding; + } + + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/db/QueryMetaData.cs b/NEsper.Core/NEsper.Core/epl/db/QueryMetaData.cs new file mode 100755 index 000000000..e8c9d557f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/db/QueryMetaData.cs @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.epl.db +{ + /// + /// Holder for query meta data information obtained from interrogating statements. + /// + public class QueryMetaData + { + private readonly IList inputParameters; + private readonly IDictionary outputParameters; + + /// Ctor. + /// is the input parameter names + /// is the output column names and types + public QueryMetaData(IList inputParameters, IDictionary outputParameters) + { + this.inputParameters = inputParameters; + this.outputParameters = outputParameters; + } + + /// Return the input parameters. + /// input parameter names + public IList InputParameters + { + get { return inputParameters; } + } + + /// Returns a map of output column name and type descriptor. + /// column names and types + public IDictionary OutputParameters + { + get { return outputParameters; } + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/epl/db/SQLParameterDesc.cs b/NEsper.Core/NEsper.Core/epl/db/SQLParameterDesc.cs new file mode 100755 index 000000000..913ee510e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/db/SQLParameterDesc.cs @@ -0,0 +1,54 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.epl.db +{ + /// + /// Hold a raw SQL-statements parameter information that were specified in the form ${name}. + /// + + public class SQLParameterDesc + { + private readonly IList parameters; + private readonly IList builtinIdentifiers; + + /// Ctor. + /// is the name of parameters + /// is the names of built-in predefined values + public SQLParameterDesc(IList parameters, IList builtinIdentifiers) + { + this.parameters = parameters; + this.builtinIdentifiers = builtinIdentifiers; + } + + /// Returns parameter names. + /// parameter names + public IList Parameters + { + get {return parameters;} + } + + /// Returns built-in identifiers. + /// built-in identifiers + public IList BuiltinIdentifiers + { + get {return builtinIdentifiers;} + } + + public override String ToString() + { + return "params=" + parameters.Render() + + " builtin=" + builtinIdentifiers.Render(); + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/epl/db/drivers/BaseDbDriver.cs b/NEsper.Core/NEsper.Core/epl/db/drivers/BaseDbDriver.cs new file mode 100755 index 000000000..125c4ef64 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/db/drivers/BaseDbDriver.cs @@ -0,0 +1,471 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Data; +using System.Data.Common; +using System.Linq; +using System.Text; +using System.Threading; + +using com.espertech.esper.client; +using com.espertech.esper.client.annotation; +using com.espertech.esper.compat; +using com.espertech.esper.compat.threading; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.db.drivers +{ + /// + /// An abstract base driver that provides some of the functionality + /// that is common to all ADO.NET based drivers, but leaves the specifics + /// of the database to the driver implementation. ADO.NET leaves some + /// wholes in its implementation and advises that for maximum performance + /// that you use driver specific semantics. This code exists to allow + /// developers to integrate their own database models. + /// + [Serializable] + abstract public class BaseDbDriver : DbDriver + { + private const String SAMPLE_WHERECLAUSE_PLACEHOLDER = "$ESPER-SAMPLE-WHERE"; + + /// + /// Connection name + /// + private String _name; + + /// + /// Connection properties + /// + private Properties _connectionProperties; + + /// + /// Connection string + /// + private String _connectionString; + + /// + /// Creates a connection string builder. + /// + /// + protected abstract DbConnectionStringBuilder CreateConnectionStringBuilder(); + + /// + /// Gets the default meta origin policy. + /// + /// The default meta origin policy. + public virtual ConfigurationDBRef.MetadataOriginEnum DefaultMetaOriginPolicy + { + get { return ConfigurationDBRef.MetadataOriginEnum.DEFAULT; } + } + + /// + /// Gets the parameter prefix. + /// + /// The param prefix. + abstract protected String ParamPrefix { get; } + + /// + /// Gets or sets the connection string. + /// + /// The connection string. + public virtual String ConnectionString + { + get { return _connectionString; } + protected set { _connectionString = value; } + } + + /// + /// Gets a value indicating whether [use position parameters]. + /// + /// + /// true if [use position parameters]; otherwise, false. + /// + protected virtual bool UsePositionalParameters + { + get { return false; } + } + + #region "PositionalToTextConversion" + + /// + /// Converts a positional parameter into text that can be embedded + /// into the command text. + /// + /// + /// + + protected delegate String PositionalToTextConverter(int parameterIndex); + + /// + /// Gets the text for the parameter at the given index. + /// + /// MapIndex of the parameter. + /// + protected String PositionalToNamedTextConverter(int parameterIndex) + { + return String.Format("{0}arg{1}", ParamPrefix, parameterIndex); + } + + /// + /// Gets the text for the parameter at the given index. + /// + /// MapIndex of the parameter. + /// + protected String PositionalToPositionalTextConverter(int parameterIndex) + { + return ParamPrefix; + } + + /// + /// Gets the positional to text converter. + /// + protected PositionalToTextConverter ParamToTextConverter + { + get + { + if (UsePositionalParameters) + { + return PositionalToPositionalTextConverter; + } + else + { + return PositionalToNamedTextConverter; + } + } + } + #endregion + + /// + /// Weak reference to the database connection. Allows a thread to + /// reuse an existing connection rather than opening a new one as + /// opening a new connection can be considerably expensive with some + /// drivers. The reference is weak which means that after it is no + /// longer is use, the weak reference will go out of scope. To + /// prevent the database connection from going out of scope prematurely + /// we keep around a strong reference that is swept on a regular + /// interval. + /// + + private readonly FastThreadStore> wdbConnection = + new FastThreadStore>(); + + /// + /// Collects connections across threads and stores them in a strongly + /// referenced table. The table allows us to reuse connections to + /// database that are continually accessed on the same thread. + /// + + [NonSerialized] + private static readonly Dictionary sdbConnectionTable = new Dictionary(); + + /// + /// Periodically removes unused connections from the sdbConnectionTable + /// and allows them to be reclaimed. + /// + + [NonSerialized] + private static Timer releaseTimer = null; + + /// + /// Releases the connections. + /// + /// The user object. + private static void ReleaseConnections(Object userObject) + { + long touchPoint = Environment.TickCount - 15000; + + lock (((ICollection) sdbConnectionTable).SyncRoot) + { + IList termList = new List(); + foreach( KeyValuePair entry in sdbConnectionTable ) + { + if ( entry.Value < touchPoint ) + { + termList.Add(entry.Key); + } + } + + // Remove strong references to any databases that have not + // been active since the touchPoint (expiry time). + + foreach( DbConnection termPoint in termList ) + { + sdbConnectionTable.Remove(termPoint); + } + } + } + + + /// + /// Factory method that is used to create instance of a connection. + /// + /// + + public abstract DbConnection CreateConnection(); + + /// + /// Creates a connection using the internal mechanism. Avoids having + /// to make CreateConnection protected internal. Its primary use is + /// by the BaseDbDriverCommand. + /// + /// + + protected internal DbConnection CreateConnectionInternal() + { + DbConnection _dbConnection; + var dbConnection = wdbConnection.Value; + if ((dbConnection == null) || + (dbConnection.IsDead) || + (dbConnection.Target == null)) + { + _dbConnection = CreateConnection(); + dbConnection = new compat.WeakReference(_dbConnection); + ApplyConnectionOptions(_dbConnection); + wdbConnection.Value = dbConnection; + } + else + { + _dbConnection = dbConnection.Target; + } + + // Enter the time of the last activity on the database + // connection... i.e. touch the connection. + lock (((ICollection)sdbConnectionTable).SyncRoot) + { + sdbConnectionTable[_dbConnection] = Environment.TickCount; + if (releaseTimer == null) + { + releaseTimer = new Timer(ReleaseConnections, null, 0L, 5000L); + } + } + + return _dbConnection; + } + + /// + /// Sets the transaction isolation. + /// + /// The connection. + /// The isolation level. + protected virtual void SetTransactionIsolation( DbConnection connection, IsolationLevel? isolationLevel ) + { + try + { + if (isolationLevel != null) + { + // Begin a transaction to provide the proper isolation. Need to ensure + // that the transaction is properly committed upon completion since we + // do not have auto-commit handled. + connection.BeginTransaction(isolationLevel.Value); + } + } + catch (DbException ex) + { + throw new DatabaseConfigException( + "Error setting transaction isolation level to " + isolationLevel + + " on connection with detail " + GetDetail(ex), ex); + } + } + + /// + /// Sets the catalog. + /// + /// The connection. + /// The catalog. + protected virtual void SetCatalog( DbConnection connection, String catalog ) + { + try + { + if (catalog != null) + { + connection.ChangeDatabase(catalog); + } + } + catch (DbException ex) + { + throw new DatabaseConfigException( + "Error setting catalog to '" + catalog + + "' on connection with detail " + GetDetail(ex), ex); + } + } + + /// + /// Sets the automatic commits. + /// + /// The connection. + /// The use auto commit. + protected virtual void SetAutoCommit( DbConnection connection, bool? useAutoCommit ) + { + try + { + if (useAutoCommit ?? false) + { + throw new NotSupportedException("AutoCommit semantics not yet supported in this version"); + } + } + catch (DbException ex) + { + throw new DatabaseConfigException( + "Error setting auto-commit to " + useAutoCommit + + " on connection with detail " + GetDetail(ex), ex); + } + } + + /// Method to set connection-level configuration settings. + /// is the connection to set on + /// + /// are the settings to apply + /// + /// DatabaseConfigException is thrown if an DbException is thrown + protected virtual void ApplyConnectionOptions(DbConnection connection, ConnectionSettings connectionSettings) + { + SetTransactionIsolation(connection, connectionSettings.TransactionIsolation); + SetCatalog(connection, connectionSettings.Catalog); + SetAutoCommit(connection, connectionSettings.AutoCommit); + } + + /// + /// Sets the connection options using the default connection options. + /// + /// The connection. + protected virtual void ApplyConnectionOptions(DbConnection connection) + { + } + + /// + /// Gets the detail. + /// + /// The ex. + /// + public static String GetDetail(DbException ex) + { + return + "DbException: " + ex.Message + + " VendorError: " + ex.ErrorCode; + } + + #region DbDriver Members + + /// + /// Creates a database driver command from a collection of fragments. + /// + /// The SQL fragments. + /// The metadata settings. + /// The context attributes. + /// + public virtual DbDriverCommand CreateCommand(IEnumerable sqlFragments, + ColumnSettings metadataSettings, + IEnumerable contextAttributes) + { + int? dbCommandTimeout = null; + + // Determine if we have a SQLTimeoutAttribute specified within this context. If so, + // it must be applied to the command timeout. + if (contextAttributes != null) { + var timeoutAttribute = (SQLTimeoutAttribute) contextAttributes.FirstOrDefault( + contextAttribute => contextAttribute is SQLTimeoutAttribute); + if (timeoutAttribute != null) { + dbCommandTimeout = timeoutAttribute.Value; + } + } + + // How do we convert from positional to underlying + PositionalToTextConverter paramConverter = ParamToTextConverter; + // List of parameters + List parameters = new List(); + // Command text builder + StringBuilder buffer = new StringBuilder(); + // Counter for parameters + int parameterCount = 0; + foreach (PlaceholderParser.Fragment fragment in sqlFragments) + { + if (!fragment.IsParameter) + { + buffer.Append(fragment.Value); + } + else if (fragment.Value == SAMPLE_WHERECLAUSE_PLACEHOLDER) + { + continue; + } + else + { + String parameter = paramConverter.Invoke(parameterCount++); + // Add the parameter to the parameter list + parameters.Add(parameter); + // Add the parameter to the command text + buffer.Append(parameter); + } + } + + String dbCommandText = buffer.ToString(); + return new BaseDbDriverCommand( + this, + sqlFragments, + parameters, + dbCommandText, + dbCommandTimeout, + metadataSettings); + } + + /// + /// Gets or sets the properties for the driver. + /// + /// The properties. + public virtual Properties Properties + { + get { return _connectionProperties; } + set + { + _connectionProperties = value; + + // Look for the term "connectionString" in the properties. If it is not specified + // then use the other items in the properties to drive a connection string builder. + + if (!_connectionProperties.TryGetValue("connectionString", out _connectionString) && + !_connectionProperties.TryGetValue("connection-string", out _connectionString)) + { + // Create the connection string; to do so, we require a connection + // string builder. These are native to every connection class and + // ADO.NET providers will provide them to you natively. We require + // that the implementation class provide us with one of these. + DbConnectionStringBuilder builder = CreateConnectionStringBuilder(); + + foreach (KeyValuePair entry in _connectionProperties) + { + String ekey = entry.Key; + String evalue = entry.Value; + if (String.Equals(ekey, "name", StringComparison.CurrentCultureIgnoreCase)) + { + _name = evalue; + } + else + { + builder.Add(ekey, evalue); + } + } + + _connectionString = builder.ConnectionString; + } + } + } + + /// + /// Connection name + /// + public virtual string Name + { + get { return _name; } + } + + #endregion + } +} diff --git a/NEsper.Core/NEsper.Core/epl/db/drivers/BaseDbDriverCommand.cs b/NEsper.Core/NEsper.Core/epl/db/drivers/BaseDbDriverCommand.cs new file mode 100755 index 000000000..91cf2b22c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/db/drivers/BaseDbDriverCommand.cs @@ -0,0 +1,527 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; +using System.Data; +using System.Data.Common; +using System.Text; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.db.drivers +{ + /// + /// Companion to the BaseDbDriver that provides command support in + /// accordance to ADO.NET and the DbDriverCommand. + /// + + public class BaseDbDriverCommand : DbDriverCommand + { + private const String SAMPLE_WHERECLAUSE_PLACEHOLDER = "$ESPER-SAMPLE-WHERE"; + + /// + /// Underlying driver. + /// + private readonly BaseDbDriver _driver; + + /// + /// Fragments that were used to build the command. + /// + private readonly List _fragments; + + /// + /// List of input parameters + /// + private readonly List _inputParameters; + + /// + /// Output parameters; cached upon creation + /// + private IDictionary _outputParameters; + + /// + /// Command text that needs to be associated with the command. + /// + private readonly String _dbCommandText; + + /// + /// Command timeout + /// + private readonly int? _dbCommandTimeout; + + /// + /// Column settings + /// + private readonly ColumnSettings _metadataSettings; + + /// + /// Private lock for connection and command. + /// + private Object _allocLock; + + /// + /// Connection allocated to this instance + /// + private DbConnection _theConnection; + + /// + /// Command allocated to this instance + /// + private DbCommand _theCommand; + + /// + /// Initializes a new instance of the class. + /// + /// The driver. + /// The fragments. + /// The input parameters. + /// The command text. + /// The db command timeout. + /// The metadata settings. + protected internal BaseDbDriverCommand( + BaseDbDriver driver, + IEnumerable fragments, + IEnumerable inputParameters, + String dbCommandText, + int? dbCommandTimeout, + ColumnSettings metadataSettings) + { + _driver = driver; + _metadataSettings = metadataSettings; + _fragments = new List(fragments); + _inputParameters = new List(inputParameters); + _dbCommandText = dbCommandText; + _dbCommandTimeout = dbCommandTimeout; + + _allocLock = new Object(); + _theConnection = null; + _theCommand = null; + } + + /// + /// Initializes a new instance of the class. + /// Used for cloning. + /// + protected internal BaseDbDriverCommand() + { + } + + /// + /// Clones the driver command. + /// + /// + public virtual DbDriverCommand Clone() + { + BaseDbDriverCommand dbClone = (BaseDbDriverCommand) MemberwiseClone(); + // Create an independent lock + dbClone._allocLock = new object(); + // Ensure theConnection and theCommand are not copied + dbClone._theConnection = null; + dbClone._theCommand = null; + // Return the clone + return dbClone; + } + + #region IDisposable Members + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + lock (_allocLock) + { + // Clean up the command + if (_theCommand != null) + { + _theCommand.Dispose(); + _theCommand = null; + } + + // Clean up the connection + //if (theConnection != null) + //{ + // theConnection.Dispose(); + // theConnection = null; + //} + } + } + + #endregion + + private DbCommand CreateCommand(DbConnection dbConnection, bool honorTimeout) + { + DbCommand myCommand; + // Create the command + myCommand = dbConnection.CreateCommand(); + myCommand.CommandType = CommandType.Text; + myCommand.CommandText = _dbCommandText; + if (_dbCommandTimeout.HasValue && honorTimeout) { + myCommand.CommandTimeout = _dbCommandTimeout.Value; + } + + // Bind the parameters + myCommand.Parameters.Clear(); + foreach (string parameterName in _inputParameters) + { + DbParameter myParam = myCommand.CreateParameter(); + myParam.IsNullable = true; + myParam.ParameterName = parameterName; + myCommand.Parameters.Add(myParam); + } + + return myCommand; + } + + /// + /// Ensures that the command is allocated. + /// + protected virtual void AllocateCommand() + { + lock( _allocLock ) + { + if (_theCommand == null) + { + // Create the connection + _theConnection = _driver.CreateConnectionInternal(); + // Create the command + _theCommand = CreateCommand(_theConnection, true); + } + } + } + + /// + /// Gets the actual database command. + /// + /// The command. + public virtual DbCommand Command + { + get + { + AllocateCommand(); + return _theCommand; + } + } + + /// + /// Gets the parameters. + /// + /// The parse fragements. + /// + private static SQLParameterDesc GetParameters(IEnumerable parseFragements) + { + List eventPropertyParams = new List(); + List builtinParams = new List(); + foreach (PlaceholderParser.Fragment fragment in parseFragements) + { + if (fragment.IsParameter) + { + if (fragment.Value == SAMPLE_WHERECLAUSE_PLACEHOLDER) + { + builtinParams.Add(fragment.Value); + } + else + { + eventPropertyParams.Add(fragment.Value); + } + } + } + + IList paramList = eventPropertyParams; + IList builtin = eventPropertyParams; + return new SQLParameterDesc(paramList, builtin); + } + + /// + /// Gets the fragments. + /// + /// The fragments. + public virtual IEnumerable Fragments + { + get { return _fragments; } + } + + /// + /// Gets the pseudo text. + /// + /// The pseudo text. + public virtual String PseudoText + { + get + { + StringBuilder builder = new StringBuilder(); + foreach (PlaceholderParser.Fragment fragment in Fragments) + { + if (fragment.IsParameter) + { + if (fragment.Value != SAMPLE_WHERECLAUSE_PLACEHOLDER) + { + builder.Append('?'); + } + } + else + { + builder.Append(fragment.Value); + } + } + + return builder.ToString(); + } + } + + /// + /// Gets the command text. + /// + /// The command text. + public virtual String CommandText + { + get { return _dbCommandText; } + } + + #region DbDriverCommand Members + + /// + /// Gets the driver associated with this command. + /// + /// + public virtual DbDriver Driver + { + get { return _driver; } + } + + /// + /// Gets the meta data. + /// + /// The meta data. + public virtual QueryMetaData MetaData + { + get { return new QueryMetaData(InputParameters, OutputParameters); } + } + + /// + /// Gets the meta data settings associated with this command. + /// + public ColumnSettings MetaDataSettings + { + get { return _metadataSettings; } + } + + /// + /// Gets a list of parameters. + /// + /// The parameters. + public virtual SQLParameterDesc ParameterDescription + { + get { return GetParameters(_fragments); } + } + + /// + /// Gets the input parameters. + /// + /// The input parameters. + public virtual IList InputParameters + { + get { return _inputParameters; } + } + + /// + /// Gets the output parameters. + /// + /// The output parameters. + public virtual IDictionary OutputParameters + { + get + { + if (_outputParameters == null) + { + CreateOutputParameters(); + } + + return _outputParameters; + } + + protected set + { + _outputParameters = value; + } + } + + /// + /// Creates and sets the output parameters + /// + + protected virtual void CreateOutputParameters() + { + try + { + if (Log.IsInfoEnabled) + { + Log.Info(".OutputParameters - dbCommandText = '" + _dbCommandText + "'"); + } + + // This embodies the default behavior of the BaseDbDriver and how it + // handles the analysis of a query and the schema that is associated + // with it. If this handling is incorrect, you can (a) subclass and + // provide your implementation or (b) submit the points you need + // interceptors to be added so that we can provide you with the + // right hooks. ADO.NET can often be difficult to navigate. + + DataTable schemaTable; + + DbConnection dbConnection = _driver.CreateConnectionInternal(); + + using (DbCommand dbCommand = CreateCommand(dbConnection, false)) + { + try + { + using (IDataReader reader = dbCommand.ExecuteReader(CommandBehavior.SchemaOnly)) + { + // Get the schema table + schemaTable = reader.GetSchemaTable(); + } + } + catch (DbException ex) + { + String text = "Error in statement '" + _dbCommandText + + "', failed to obtain result metadata, consider turning off metadata interrogation via configuration"; + Log.Error(text, ex); + throw new ExprValidationException(text + ", please check the statement, reason: " + + ex.Message); + } + + if (Log.IsDebugEnabled) + { + Log.Debug(".OutputParameters value = " + _outputParameters); + } + } + + // Analyze the schemaTable + _outputParameters = CompileSchemaTable(schemaTable, _metadataSettings); + } + catch (DbException ex) + { + String text = "Error preparing statement '" + _dbCommandText + '\''; + Log.Error(text, ex); + throw new ExprValidationException(text + ", reason: " + ex.Message); + } + } + + /// + /// Gets the type of the column associated with the row in the + /// table schema. + /// + /// The schema data row. + /// + protected virtual Type GetColumnType( DataRow schemaDataRow ) + { + Type columnType = (Type)schemaDataRow["DataType"]; + Int32 columnSize = (Int32)schemaDataRow["ColumnSize"]; + + // Some providers (read MySQL) provide bools as an integer + // with a size of 1. We should probably convert these to bool + // to make client integration easier. + if ((columnType == typeof(sbyte)) && (columnSize == 1)) + { + columnType = typeof(bool); + } + + return columnType; + } + + /// + /// Gets the SQL type of the column associated with the row in the + /// table schema. + /// + /// The schema data row. + /// + protected virtual String GetColumnSqlType( DataRow schemaDataRow ) + { +#if TODO + var providerType = (Int32)schemaDataRow["ProviderType"]; + var providerTypeAsEnum = (OleDbType) providerType; + var sqlTypeName = EnumHelper.GetName(providerTypeAsEnum); + return sqlTypeName; +#else + throw new NotSupportedException(); +#endif + } + + /// + /// Compiles the schema table. + /// + /// The schema table. + /// The column settings. + /// + protected virtual IDictionary CompileSchemaTable( + DataTable schemaTable, + ColumnSettings columnSettings) + { + IDictionary outputProperties = new Dictionary(); + foreach (DataRow dataRow in schemaTable.Rows) + { + var canBeNull = (Boolean) dataRow["AllowDBNull"]; + var columnName = (String)dataRow["ColumnName"]; + var columnType = GetColumnType(dataRow); + var sqlTypeName = GetColumnSqlType(dataRow); + //if (canBeNull) { + // columnType = columnType.GetBoxedType(); + //} + + // Address column case management + ConfigurationDBRef.ColumnChangeCaseEnum caseEnum = columnSettings.ColumnCaseConversionEnum; + switch (caseEnum) + { + case ConfigurationDBRef.ColumnChangeCaseEnum.LOWERCASE: + columnName = columnName.ToLower(); + break; + case ConfigurationDBRef.ColumnChangeCaseEnum.UPPERCASE: + columnName = columnName.ToUpper(); + break; + } + + // Setup type binding + DatabaseTypeBinding binding = null; + + // Check the typeBinding; the typeBinding tells us if we are + // converting the resultant type from the dataType that has been + // provided to us into a different type. + + //if (columnSettings.SqlTypeBinding != null) + //{ + // String typeBinding = columnSettings.SqlTypeBinding.Get(columnType); + // if (typeBinding != null) + // { + // binding = DatabaseTypeEnum.GetEnum(typeBinding).Binding; + // } + //} + + DBOutputTypeDesc outputDesc = new DBOutputTypeDesc(sqlTypeName, columnType, binding); + outputProperties[columnName] = outputDesc; + } + + return outputProperties; + } + +#endregion + + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + } + + /// + /// Creates database command objects + /// + /// + + public delegate IDbCommand DbCommandFactory(); +} diff --git a/NEsper.Core/NEsper.Core/epl/db/drivers/DbDriverGeneric.cs b/NEsper.Core/NEsper.Core/epl/db/drivers/DbDriverGeneric.cs new file mode 100755 index 000000000..a0db9dd02 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/db/drivers/DbDriverGeneric.cs @@ -0,0 +1,86 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Data.Common; + +namespace com.espertech.esper.epl.db.drivers +{ + /// + /// A generic database driver. + /// + [Serializable] + public class DbDriverGeneric : BaseDbDriver + { + private readonly DbProviderFactory dbProviderFactory; + private readonly bool isPositional; + private readonly String paramPrefix; + + /// + /// Initializes the class. + /// + public DbDriverGeneric() + { + dbProviderFactory = DbProviderFactories.GetFactory("MySql.Data.MySqlClient"); + isPositional = false; + paramPrefix = "@"; + } + + /// + /// Factory method that is used to create instance of a connection. + /// + /// + public override DbConnection CreateConnection() + { + try + { + DbConnection dbConnection = dbProviderFactory.CreateConnection(); + dbConnection.ConnectionString = ConnectionString; + dbConnection.Open(); + return dbConnection; + } + catch (DbException ex) + { + String detail = "DbException: " + ex.Message + " VendorError: " + ex.ErrorCode; + throw new DatabaseConfigException( + "Error obtaining database connection using connection-string '" + ConnectionString + + "' with detail " + detail, ex); + } + } + + /// + /// Gets a value indicating whether [use position parameters]. + /// + /// + /// true if [use position parameters]; otherwise, false. + /// + protected override bool UsePositionalParameters + { + get { return isPositional; } + } + + /// + /// Gets the parameter prefix. + /// + /// The param prefix. + protected override string ParamPrefix + { + get { return paramPrefix; } + } + + /// + /// Creates a connection string builder. + /// + /// + protected override DbConnectionStringBuilder CreateConnectionStringBuilder() + { + return dbProviderFactory.CreateConnectionStringBuilder(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/db/drivers/DbDriverMySQL.cs b/NEsper.Core/NEsper.Core/epl/db/drivers/DbDriverMySQL.cs new file mode 100755 index 000000000..7a743ee47 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/db/drivers/DbDriverMySQL.cs @@ -0,0 +1,78 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Data.Common; + +namespace com.espertech.esper.epl.db.drivers +{ + /// + /// A database driver specific to the MySQL driver. The MySQL driver + /// is a named positional driver. + /// + [Serializable] + public class DbDriverMySQL : BaseDbDriver + { + private static readonly DbProviderFactory dbProviderFactory; + + /// + /// Initializes the class. + /// + static DbDriverMySQL() + { + // MySQL needs to be installed on the host box in order for us + // to make use of it. Additionally we don't want to bind anything + // to the library that does not need to artifically be bound. + + + dbProviderFactory = DbProviderFactories.GetFactory("MySql.Data.MySqlClient"); + } + + /// + /// Factory method that is used to create instance of a connection. + /// + /// + public override DbConnection CreateConnection() + { + DbConnection dbConnection = dbProviderFactory.CreateConnection(); + dbConnection.ConnectionString = ConnectionString; + dbConnection.Open(); + return dbConnection; + } + + /// + /// Gets a value indicating whether [use position parameters]. + /// + /// + /// true if [use position parameters]; otherwise, false. + /// + protected override bool UsePositionalParameters + { + get { return false; } + } + + /// + /// Gets the parameter prefix. + /// + /// The param prefix. + protected override string ParamPrefix + { + get { return "?"; } + } + + /// + /// Creates a connection string builder. + /// + /// + protected override DbConnectionStringBuilder CreateConnectionStringBuilder() + { + return dbProviderFactory.CreateConnectionStringBuilder(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/declexpr/ExprDeclaredEvalBase.cs b/NEsper.Core/NEsper.Core/epl/declexpr/ExprDeclaredEvalBase.cs new file mode 100755 index 000000000..ee2217fca --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/declexpr/ExprDeclaredEvalBase.cs @@ -0,0 +1,200 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.spec; +using com.espertech.esper.events; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.declexpr +{ + public abstract class ExprDeclaredEvalBase : ExprEvaluatorTypableReturn, ExprEvaluatorEnumeration + { + private readonly ExprEvaluator _innerEvaluator; + private readonly ExprEvaluatorEnumeration _innerEvaluatorLambda; + private readonly ExpressionDeclItem _prototype; + private readonly bool _isCache; + + public abstract EventBean[] GetEventsPerStreamRewritten(EventBean[] eventsPerStream); + + protected ExprDeclaredEvalBase(ExprEvaluator innerEvaluator, ExpressionDeclItem prototype, bool isCache) + { + _innerEvaluator = innerEvaluator; + _prototype = prototype; + if (innerEvaluator is ExprEvaluatorEnumeration) { + _innerEvaluatorLambda = (ExprEvaluatorEnumeration) innerEvaluator; + } + else { + _innerEvaluatorLambda = null; + } + _isCache = isCache; + } + + public ExprEvaluator InnerEvaluator + { + get { return _innerEvaluator; } + } + + public Type ReturnType + { + get { return _innerEvaluator.ReturnType; } + } + + public IDictionary RowProperties + { + get + { + if (_innerEvaluator is ExprEvaluatorTypableReturn) + { + return ((ExprEvaluatorTypableReturn) _innerEvaluator).RowProperties; + } + return null; + } + } + + public bool? IsMultirow + { + get + { + if (_innerEvaluator is ExprEvaluatorTypableReturn) + { + return ((ExprEvaluatorTypableReturn) _innerEvaluator).IsMultirow; + } + return null; + } + } + + public Object[] EvaluateTypableSingle(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + return ((ExprEvaluatorTypableReturn) _innerEvaluator).EvaluateTypableSingle( + eventsPerStream, isNewData, context); + } + + public Object[][] EvaluateTypableMulti(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + return ((ExprEvaluatorTypableReturn) _innerEvaluator).EvaluateTypableMulti( + eventsPerStream, isNewData, context); + } + + public object Evaluate(EvaluateParams evaluateParams) + { + object[] result = { null }; + + using (Instrument.With( + i => i.QExprDeclared(_prototype), + i => i.AExprDeclared(result[0]))) + { + // rewrite streams + var events = GetEventsPerStreamRewritten(evaluateParams.EventsPerStream); + var evaluateParamsX = new EvaluateParams(events, evaluateParams.IsNewData, evaluateParams.ExprEvaluatorContext); + var context = evaluateParams.ExprEvaluatorContext; + + if (_isCache) + { + // no the same cache as for iterator + var cache = context.ExpressionResultCacheService.AllocateDeclaredExprLastValue; + var entry = cache.GetDeclaredExpressionLastValue(_prototype, events); + if (entry != null) + { + return entry.Result; + } + result[0] = _innerEvaluator.Evaluate(evaluateParamsX); + cache.SaveDeclaredExpressionLastValue(_prototype, events, result[0]); + } + else + { + result[0] = _innerEvaluator.Evaluate(evaluateParamsX); + } + + return result[0]; + } + } + + public ICollection EvaluateGetROCollectionEvents(EvaluateParams evaluateParams) + { + // rewrite streams + EventBean[] events = GetEventsPerStreamRewritten(evaluateParams.EventsPerStream); + + ICollection result; + if (_isCache) + { + var cache = evaluateParams.ExprEvaluatorContext.ExpressionResultCacheService.AllocateDeclaredExprLastColl; + var entry = cache.GetDeclaredExpressionLastColl(_prototype, events); + if (entry != null) + { + return entry.Result; + } + + result = _innerEvaluatorLambda.EvaluateGetROCollectionEvents(evaluateParams); + cache.SaveDeclaredExpressionLastColl(_prototype, events, result); + return result; + } + else + { + result = _innerEvaluatorLambda.EvaluateGetROCollectionEvents(evaluateParams); + } + + return result; + } + + public ICollection EvaluateGetROCollectionScalar(EvaluateParams evaluateParams) + { + // rewrite streams + EventBean[] events = GetEventsPerStreamRewritten(evaluateParams.EventsPerStream); + + ICollection result; + if (_isCache) + { + var cache = evaluateParams.ExprEvaluatorContext.ExpressionResultCacheService.AllocateDeclaredExprLastColl; + var entry = cache.GetDeclaredExpressionLastColl(_prototype, events); + if (entry != null) + { + return entry.Result.Unwrap(); + } + + result = _innerEvaluatorLambda.EvaluateGetROCollectionScalar(evaluateParams); + cache.SaveDeclaredExpressionLastColl(_prototype, events, result.UnwrapSafe()); + return result; + } + else { + result = _innerEvaluatorLambda.EvaluateGetROCollectionScalar(evaluateParams); + } + + return result; + } + + public Type ComponentTypeCollection + { + get + { + return _innerEvaluatorLambda != null ? _innerEvaluatorLambda.ComponentTypeCollection : null; + } + } + + public EventType GetEventTypeCollection(EventAdapterService eventAdapterService, int statementId) + { + return _innerEvaluatorLambda != null ? _innerEvaluatorLambda.GetEventTypeCollection(eventAdapterService, statementId) : null; + } + + public EventType GetEventTypeSingle(EventAdapterService eventAdapterService, int statementId) + { + return _innerEvaluatorLambda != null ? _innerEvaluatorLambda.GetEventTypeSingle(eventAdapterService, statementId) : null; + } + + public EventBean EvaluateGetEventBean(EvaluateParams evaluateParams) + { + return _innerEvaluatorLambda.EvaluateGetEventBean(evaluateParams); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/declexpr/ExprDeclaredEvalConstant.cs b/NEsper.Core/NEsper.Core/epl/declexpr/ExprDeclaredEvalConstant.cs new file mode 100755 index 000000000..969c66c23 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/declexpr/ExprDeclaredEvalConstant.cs @@ -0,0 +1,43 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.spec; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.declexpr +{ + public class ExprDeclaredEvalConstant : ExprEvaluator + { + private readonly Type _returnType; + private readonly ExpressionDeclItem _prototype; + private readonly Object _value; + + public ExprDeclaredEvalConstant(Type returnType, ExpressionDeclItem prototype, Object value) + { + _returnType = returnType; + _prototype = prototype; + _value = value; + } + + public Type ReturnType + { + get { return _returnType; } + } + + public object Evaluate(EvaluateParams evaluateParams) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QExprDeclared(_prototype);} + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprDeclared(_value);} + return _value; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/declexpr/ExprDeclaredEvalNoRewrite.cs b/NEsper.Core/NEsper.Core/epl/declexpr/ExprDeclaredEvalNoRewrite.cs new file mode 100755 index 000000000..5b1264b78 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/declexpr/ExprDeclaredEvalNoRewrite.cs @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.epl.declexpr +{ + public class ExprDeclaredEvalNoRewrite + : ExprDeclaredEvalBase + { + public ExprDeclaredEvalNoRewrite(ExprEvaluator innerEvaluator, ExpressionDeclItem prototype, bool isCache) + : base(innerEvaluator, prototype, isCache) + { + } + + public override EventBean[] GetEventsPerStreamRewritten(EventBean[] eventsPerStream) + { + return eventsPerStream; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/declexpr/ExprDeclaredEvalRewrite.cs b/NEsper.Core/NEsper.Core/epl/declexpr/ExprDeclaredEvalRewrite.cs new file mode 100755 index 000000000..16c986597 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/declexpr/ExprDeclaredEvalRewrite.cs @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.epl.declexpr +{ + public class ExprDeclaredEvalRewrite + : ExprDeclaredEvalBase + { + private readonly int[] _streamAssignments; + + public ExprDeclaredEvalRewrite(ExprEvaluator innerEvaluator, ExpressionDeclItem prototype, bool isCache, int[] streamAssignments) + : base(innerEvaluator, prototype, isCache) + { + _streamAssignments = streamAssignments; + } + + public override EventBean[] GetEventsPerStreamRewritten(EventBean[] eventsPerStream) + { + + // rewrite streams + EventBean[] events = new EventBean[_streamAssignments.Length]; + for (int i = 0; i < _streamAssignments.Length; i++) + { + events[i] = eventsPerStream[_streamAssignments[i]]; + } + + return events; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/declexpr/ExprDeclaredHelper.cs b/NEsper.Core/NEsper.Core/epl/declexpr/ExprDeclaredHelper.cs new file mode 100755 index 000000000..7007e1ff8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/declexpr/ExprDeclaredHelper.cs @@ -0,0 +1,87 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.script; +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.epl.declexpr +{ + public class ExprDeclaredHelper + { + public static ExprDeclaredNodeImpl GetExistsDeclaredExpr(String name, IList parameters, ICollection expressionDeclarations, ExprDeclaredService exprDeclaredService, ContextDescriptor contextDescriptor) + { + // Find among local expressions + if (expressionDeclarations.IsNotEmpty()) + { + foreach (ExpressionDeclItem declNode in expressionDeclarations) + { + if (declNode.Name.Equals(name)) + { + return new ExprDeclaredNodeImpl(declNode, parameters, contextDescriptor); + } + } + } + + // find among global expressions + ExpressionDeclItem found = exprDeclaredService.GetExpression(name); + if (found != null) + { + return new ExprDeclaredNodeImpl(found, parameters, contextDescriptor); + } + return null; + } + + public static ExprNodeScript GetExistsScript(String defaultDialect, String expressionName, IList parameters, ICollection scriptExpressions, ExprDeclaredService exprDeclaredService) + { + if (scriptExpressions.IsNotEmpty()) + { + var scriptProvided = FindScript(expressionName, parameters.Count, scriptExpressions); + if (scriptProvided != null) + { + return new ExprNodeScript(defaultDialect, scriptProvided, parameters); + } + } + + var globalScripts = exprDeclaredService.GetScriptsByName(expressionName); + var script = FindScript(expressionName, parameters.Count, globalScripts); + if (script != null) + { + return new ExprNodeScript(defaultDialect, script, parameters); + } + return null; + } + + private static ExpressionScriptProvided FindScript(String name, int parameterCount, ICollection scriptsByName) + { + if (scriptsByName == null || scriptsByName.IsEmpty()) + { + return null; + } + ExpressionScriptProvided nameMatchedScript = null; + foreach (ExpressionScriptProvided script in scriptsByName) + { + if (script.Name.Equals(name) && script.ParameterNames.Count == parameterCount) + { + return script; + } + if (script.Name.Equals(name)) + { + nameMatchedScript = script; + } + } + return nameMatchedScript; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/declexpr/ExprDeclaredNode.cs b/NEsper.Core/NEsper.Core/epl/declexpr/ExprDeclaredNode.cs new file mode 100755 index 000000000..462bce252 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/declexpr/ExprDeclaredNode.cs @@ -0,0 +1,36 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System.Collections.Generic; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.epl.declexpr +{ + /// + /// Expression instance as declared elsewhere. + /// (1) Statement parse: Expression tree from expression body gets deep-copied. + /// (2) Statement create (lifecyle event): Subselect visitor compiles Subselect-list + /// (3) Statement start: + /// a) event types of each stream determined + /// b) subselects filter expressions get validated and subselect started + /// (4) Remaining expressions get validated + /// + public interface ExprDeclaredNode + : ExprNode + { + IList ChainParameters { get; } + ExpressionDeclItem Prototype { get; } + LinkedHashMap GetOuterStreamNames(IDictionary outerStreamNames); + ExprNode Body { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/declexpr/ExprDeclaredNodeImpl.cs b/NEsper.Core/NEsper.Core/epl/declexpr/ExprDeclaredNodeImpl.cs new file mode 100755 index 000000000..2148c45e5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/declexpr/ExprDeclaredNodeImpl.cs @@ -0,0 +1,380 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.client.annotation; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.enummethod.dot; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.visitor; +using com.espertech.esper.epl.spec; +using com.espertech.esper.filter; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.declexpr +{ + /// + /// Expression instance as declared elsewhere. + /// + [Serializable] + public class ExprDeclaredNodeImpl + : ExprNodeBase + , ExprDeclaredNode + , ExprDeclaredOrLambdaNode + , ExprFilterOptimizableNode + , ExprNodeInnerNodeProvider + , ExprConstantNode + { + private readonly ExpressionDeclItem _prototype; + private IList _chainParameters; + [NonSerialized] + private ExprEvaluator _exprEvaluator; + private ExprNode _expressionBodyCopy; + + public ExprDeclaredNodeImpl(ExpressionDeclItem prototype, IList chainParameters, ContextDescriptor contextDescriptor) + { + _prototype = prototype; + _chainParameters = chainParameters; + + // copy expression - we do it at this time and not later + try { + _expressionBodyCopy = (ExprNode) SerializableObjectCopier.Copy(prototype.Inner); + } catch (Exception e) { + throw new Exception("Internal error providing expression tree: " + e.Message, e); + } + + // replace context-properties where they are currently identifiers + if (contextDescriptor == null) { + return; + } + var visitorWParent = new ExprNodeIdentVisitorWParent(); + _expressionBodyCopy.Accept(visitorWParent); + foreach (var pair in visitorWParent.IdentNodes) { + var streamOrProp = pair.Second.StreamOrPropertyName; + if (streamOrProp != null && contextDescriptor.ContextPropertyRegistry.IsContextPropertyPrefix(streamOrProp)) { + var context = new ExprContextPropertyNode(pair.Second.UnresolvedPropertyName); + if (pair.First == null) { + _expressionBodyCopy = context; + } + else { + ExprNodeUtility.ReplaceChildNode(pair.First, pair.Second, context); + } + } + } + } + + public ExprNode Body + { + get { return _expressionBodyCopy; } + } + + public IList AdditionalNodes + { + get { return _chainParameters; } + } + + public bool IsValidated + { + get { return _exprEvaluator != null; } + } + + public Type ConstantType + { + get { return _exprEvaluator.ReturnType; } + } + + public Object GetConstantValue(ExprEvaluatorContext context) + { + return _exprEvaluator.Evaluate(new EvaluateParams(null, true, context)); + } + + public bool IsConstantValue + { + get { return _expressionBodyCopy.IsConstantResult; } + } + + public LinkedHashMap GetOuterStreamNames(IDictionary outerStreamNames) + { + CheckParameterCount(); + + // determine stream ids for each parameter + var streamParameters = new LinkedHashMap(); + for (var param = 0; param < _chainParameters.Count; param++) { + if (!(_chainParameters[param] is ExprIdentNode)) { + throw new ExprValidationException("Sub-selects in an expression declaration require passing only stream names as parameters"); + } + + var parameterName = ((ExprIdentNode) _chainParameters[param]).UnresolvedPropertyName; + + int streamIdFound; + if (!outerStreamNames.TryGetValue(parameterName, out streamIdFound)) + { + throw new ExprValidationException("Failed validation of expression declaration '" + _prototype.Name + "': Invalid parameter to expression declaration, parameter " + param + " is not the name of a stream in the query"); + } + var prototypeName = _prototype.ParametersNames[param]; + streamParameters.Put(prototypeName, streamIdFound); + } + + return streamParameters; + } + + public override ExprNode Validate(ExprValidationContext validationContext) + { + if (_prototype.IsAlias) + { + try + { + _expressionBodyCopy = ExprNodeUtility.GetValidatedSubtree(ExprNodeOrigin.ALIASEXPRBODY, _expressionBodyCopy, validationContext); + } + catch (ExprValidationException ex) + { + var message = "Error validating expression alias '" + _prototype.Name + "': " + ex.Message; + throw new ExprValidationException(message, ex); + } + + _exprEvaluator = _expressionBodyCopy.ExprEvaluator; + return null; + } + + if (_exprEvaluator != null) + { + return null; // already evaluated + } + + if (ChildNodes.Count > 0) { + throw new IllegalStateException("Execution node has its own child nodes"); + } + + // validate chain + var validated = _chainParameters + .Select(expr => ExprNodeUtility.GetValidatedSubtree(ExprNodeOrigin.DECLAREDEXPRPARAM, expr, validationContext)) + .ToList(); + _chainParameters = validated; + + // validate parameter count + CheckParameterCount(); + + // create context for expression body + var eventTypes = new EventType[_prototype.ParametersNames.Count]; + var streamNames = new String[_prototype.ParametersNames.Count]; + var isIStreamOnly = new bool[_prototype.ParametersNames.Count]; + var streamsIdsPerStream = new int[_prototype.ParametersNames.Count]; + var allStreamIdsMatch = true; + + for (var i = 0; i < _prototype.ParametersNames.Count; i++) { + var parameter = _chainParameters[i]; + streamNames[i] = _prototype.ParametersNames[i]; + + if (parameter is ExprStreamUnderlyingNode) { + var und = (ExprStreamUnderlyingNode) parameter; + eventTypes[i] = validationContext.StreamTypeService.EventTypes[und.StreamId]; + isIStreamOnly[i] = validationContext.StreamTypeService.IsIStreamOnly[und.StreamId]; + streamsIdsPerStream[i] = und.StreamId; + } + else if (parameter is ExprWildcard) + { + if (validationContext.StreamTypeService.EventTypes.Length != 1) { + throw new ExprValidationException("Expression '" + _prototype.Name + "' only allows a wildcard parameter if there is a single stream available, please use a stream or tag name instead"); + } + eventTypes[i] = validationContext.StreamTypeService.EventTypes[0]; + isIStreamOnly[i] = validationContext.StreamTypeService.IsIStreamOnly[0]; + streamsIdsPerStream[i] = 0; + } + else { + throw new ExprValidationException("Expression '" + _prototype.Name + "' requires a stream name as a parameter"); + } + + if (streamsIdsPerStream[i] != i) { + allStreamIdsMatch = false; + } + } + + var streamTypeService = validationContext.StreamTypeService; + var copyTypes = new StreamTypeServiceImpl(eventTypes, streamNames, isIStreamOnly, streamTypeService.EngineURIQualifier, streamTypeService.IsOnDemandStreams); + copyTypes.RequireStreamNames = true; + + // validate expression body in this context + try { + var expressionBodyContext = new ExprValidationContext(copyTypes, validationContext); + _expressionBodyCopy = ExprNodeUtility.GetValidatedSubtree(ExprNodeOrigin.DECLAREDEXPRBODY, _expressionBodyCopy, expressionBodyContext); + } + catch (ExprValidationException ex) { + var message = "Error validating expression declaration '" + _prototype.Name + "': " + ex.Message; + throw new ExprValidationException(message, ex); + } + + // analyze child node + var summaryVisitor = new ExprNodeSummaryVisitor(); + _expressionBodyCopy.Accept(summaryVisitor); + + var isCache = !(summaryVisitor.HasAggregation || summaryVisitor.HasPreviousPrior); + isCache &= validationContext.ExprEvaluatorContext.ExpressionResultCacheService.IsDeclaredExprCacheEnabled; + + // determine a suitable evaluation + if (_expressionBodyCopy.IsConstantResult) { + // pre-evaluated + _exprEvaluator = new ExprDeclaredEvalConstant( + _expressionBodyCopy.ExprEvaluator.ReturnType, _prototype, + _expressionBodyCopy.ExprEvaluator.Evaluate(new EvaluateParams(null, true, null))); + } + else if (_prototype.ParametersNames.IsEmpty() || + (allStreamIdsMatch && _prototype.ParametersNames.Count == streamTypeService.EventTypes.Length)) { + _exprEvaluator = new ExprDeclaredEvalNoRewrite(_expressionBodyCopy.ExprEvaluator, _prototype, isCache); + } + else { + _exprEvaluator = new ExprDeclaredEvalRewrite(_expressionBodyCopy.ExprEvaluator, _prototype, isCache, streamsIdsPerStream); + } + + var audit = AuditEnum.EXPRDEF.GetAudit(validationContext.Annotations); + if (audit != null) { + _exprEvaluator = (ExprEvaluator) ExprEvaluatorProxy.NewInstance(validationContext.StreamTypeService.EngineURIQualifier, validationContext.StatementName, _prototype.Name, _exprEvaluator); + } + + return null; + } + + public bool IsFilterLookupEligible + { + get { return true; } + } + + public FilterSpecLookupable FilterLookupable + { + get + { + return new FilterSpecLookupable( + this.ToExpressionStringMinPrecedenceSafe(), + new DeclaredNodeEventPropertyGetter(_exprEvaluator), + _exprEvaluator.ReturnType, true); + } + } + + public override bool IsConstantResult + { + get { return false; } + } + + public override bool EqualsNode(ExprNode node) + { + var otherExprCaseNode = node as ExprDeclaredNodeImpl; + if (otherExprCaseNode == null) + return false; + + return ExprNodeUtility.DeepEquals(_expressionBodyCopy, otherExprCaseNode._expressionBodyCopy); + } + + public override void Accept(ExprNodeVisitor visitor) + { + base.Accept(visitor); + if (ChildNodes.Count == 0) { + _expressionBodyCopy.Accept(visitor); + } + } + + public override void Accept(ExprNodeVisitorWithParent visitor) + { + base.Accept(visitor); + if (ChildNodes.Count == 0) { + _expressionBodyCopy.Accept(visitor); + } + } + + public override void AcceptChildnodes(ExprNodeVisitorWithParent visitor, ExprNode parent) + { + base.AcceptChildnodes(visitor, parent); + if (visitor.IsVisit(this) && ChildNodes.Count == 0) { + _expressionBodyCopy.Accept(visitor); + } + } + + public ExprNode ExpressionBodyCopy + { + get { return _expressionBodyCopy; } + } + + public ExpressionDeclItem Prototype + { + get { return _prototype; } + } + + public IList ChainParameters + { + get { return _chainParameters; } + } + + public override ExprEvaluator ExprEvaluator + { + get { return _exprEvaluator; } + } + + private void CheckParameterCount() { + if (_chainParameters.Count != _prototype.ParametersNames.Count) { + throw new ExprValidationException("Parameter count mismatches for declared expression '" + _prototype.Name + "', expected " + + _prototype.ParametersNames.Count + " parameters but received " + _chainParameters.Count + " parameters"); + } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + writer.Write(_prototype.Name); + + if (_prototype.IsAlias) + return; + + writer.Write("("); + var delimiter = ""; + foreach (var parameter in _chainParameters) + { + writer.Write(delimiter); + parameter.ToEPL(writer, ExprPrecedenceEnum.MINIMUM); + delimiter = ","; + } + writer.Write(")"); + } + + public override ExprPrecedenceEnum Precedence + { + get { return ExprPrecedenceEnum.UNARY; } + } + + internal sealed class DeclaredNodeEventPropertyGetter : EventPropertyGetter + { + private readonly ExprEvaluator _exprEvaluator; + + internal DeclaredNodeEventPropertyGetter(ExprEvaluator exprEvaluator) + { + _exprEvaluator = ((ExprDeclaredEvalBase) exprEvaluator).InnerEvaluator; + } + + public Object Get(EventBean eventBean) + { + var events = new EventBean[1]; + events[0] = eventBean; + return _exprEvaluator.Evaluate(new EvaluateParams(events, true, null)); + } + + public bool IsExistsProperty(EventBean eventBean) + { + return false; + } + + public Object GetFragment(EventBean eventBean) + { + return null; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/declexpr/ExprDeclaredService.cs b/NEsper.Core/NEsper.Core/epl/declexpr/ExprDeclaredService.cs new file mode 100755 index 000000000..2a31f7ae7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/declexpr/ExprDeclaredService.cs @@ -0,0 +1,24 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.epl.declexpr +{ + public interface ExprDeclaredService + { + ExpressionDeclItem GetExpression(String name); + IList GetScriptsByName(String expressionName); + String AddExpressionOrScript(CreateExpressionDesc expression); + void DestroyedExpression(CreateExpressionDesc expression); + void Dispose(); + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/declexpr/ExprDeclaredServiceImpl.cs b/NEsper.Core/NEsper.Core/epl/declexpr/ExprDeclaredServiceImpl.cs new file mode 100755 index 000000000..dc08a37c1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/declexpr/ExprDeclaredServiceImpl.cs @@ -0,0 +1,113 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.threading; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.epl.declexpr +{ + public class ExprDeclaredServiceImpl : ExprDeclaredService + { + private readonly ILockable _iLock = LockManager.CreateLock(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly IDictionary _globalExpressions; + private readonly IDictionary> _globalScripts; + + public ExprDeclaredServiceImpl() + { + _globalExpressions = new Dictionary(); + _globalScripts = new Dictionary>(); + } + + #region ExprDeclaredService Members + + public String AddExpressionOrScript(CreateExpressionDesc expressionDesc) + { + using (_iLock.Acquire()) + { + if (expressionDesc.Expression != null) + { + ExpressionDeclItem expression = expressionDesc.Expression; + String name = expression.Name; + if (_globalExpressions.ContainsKey(name)) + { + throw new ExprValidationException("Expression '" + name + "' has already been declared"); + } + _globalExpressions.Put(name, expression); + return name; + } + else + { + ExpressionScriptProvided newScript = expressionDesc.Script; + String name = newScript.Name; + + List scripts = _globalScripts.Get(name); + if (scripts != null) + { + foreach (ExpressionScriptProvided script in scripts) + { + if (script.ParameterNames.Count == newScript.ParameterNames.Count) + { + throw new ExprValidationException( + "Script '" + name + + "' that takes the same number of parameters has already been declared"); + } + } + } + else + { + scripts = new List(2); + _globalScripts.Put(name, scripts); + } + scripts.Add(newScript); + + return name; + } + } + } + + public ExpressionDeclItem GetExpression(String name) + { + return _globalExpressions.Get(name); + } + + public IList GetScriptsByName(String name) + { + return _globalScripts.Get(name); + } + + public void DestroyedExpression(CreateExpressionDesc expressionDesc) + { + using (_iLock.Acquire()) + { + if (expressionDesc.Expression != null) + { + _globalExpressions.Remove(expressionDesc.Expression.Name); + } + else + { + _globalScripts.Remove(expressionDesc.Script.Name); + } + } + } + + public void Dispose() + { + _globalExpressions.Clear(); + } + + #endregion + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/dot/EnumMethodEnum.cs b/NEsper.Core/NEsper.Core/epl/enummethod/dot/EnumMethodEnum.cs new file mode 100755 index 000000000..f36d5c574 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/dot/EnumMethodEnum.cs @@ -0,0 +1,282 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Linq; + +using com.espertech.esper.compat; +using com.espertech.esper.epl.enummethod.eval; +using com.espertech.esper.epl.methodbase; + +namespace com.espertech.esper.epl.enummethod.dot +{ + public enum EnumMethodEnum + { + AGGREGATE, + ALLOF, + ANYOF, + TOMAP, + GROUPBY, + COUNTOF, + MIN, + MAX, + AVERAGE, + SUMOF, + MOSTFREQUENT, + LEASTFREQUENT, + SELECTFROM, + FIRST, + LAST, + MINBY, + MAXBY, + TAKE, + TAKELAST, + TAKEWHILE, + TAKEWHILELAST, + ORDERBY, + ORDERBYDESC, + DISTINCT, + WHERE, + UNION, + EXCEPT, + INTERSECT, + REVERSE, + NOOP, + SEQUENCE_EQUAL + } ; + + public static class EnumMethodEnumExtensions + { + public static string GetNameCamel(this EnumMethodEnum value) + { + switch (value) + { + case EnumMethodEnum.AGGREGATE: + return "aggregate"; + case EnumMethodEnum.ALLOF: + return "allOf"; + case EnumMethodEnum.ANYOF: + return "anyOf"; + case EnumMethodEnum.TOMAP: + return "toMap"; + case EnumMethodEnum.GROUPBY: + return "groupBy"; + case EnumMethodEnum.COUNTOF: + return "countOf"; + case EnumMethodEnum.MIN: + return "min"; + case EnumMethodEnum.MAX: + return "max"; + case EnumMethodEnum.AVERAGE: + return "average"; + case EnumMethodEnum.SUMOF: + return "sumOf"; + case EnumMethodEnum.MOSTFREQUENT: + return "mostFrequent"; + case EnumMethodEnum.LEASTFREQUENT: + return "leastFrequent"; + case EnumMethodEnum.SELECTFROM: + return "selectFrom"; + case EnumMethodEnum.FIRST: + return "firstOf"; + case EnumMethodEnum.LAST: + return "lastOf"; + case EnumMethodEnum.MINBY: + return "minBy"; + case EnumMethodEnum.MAXBY: + return "maxBy"; + case EnumMethodEnum.TAKE: + return "take"; + case EnumMethodEnum.TAKELAST: + return "takeLast"; + case EnumMethodEnum.TAKEWHILE: + return "takeWhile"; + case EnumMethodEnum.TAKEWHILELAST: + return "takeWhileLast"; + case EnumMethodEnum.ORDERBY: + return "orderBy"; + case EnumMethodEnum.ORDERBYDESC: + return "orderByDesc"; + case EnumMethodEnum.DISTINCT: + return "distinctOf"; + case EnumMethodEnum.WHERE: + return "where"; + case EnumMethodEnum.UNION: + return "union"; + case EnumMethodEnum.EXCEPT: + return "except"; + case EnumMethodEnum.INTERSECT: + return "intersect"; + case EnumMethodEnum.REVERSE: + return "reverse"; + case EnumMethodEnum.NOOP: + return "esperInternalNoop"; + case EnumMethodEnum.SEQUENCE_EQUAL: + return "sequenceequal"; + } + + throw new ArgumentException(); + } + + public static Type GetImplementation(this EnumMethodEnum value) + { + switch (value) + { + case EnumMethodEnum.AGGREGATE: + return typeof(ExprDotEvalAggregate); + case EnumMethodEnum.ALLOF: + return typeof(ExprDotEvalAllOfAnyOf); + case EnumMethodEnum.ANYOF: + return typeof(ExprDotEvalAllOfAnyOf); + case EnumMethodEnum.TOMAP: + return typeof(ExprDotEvalToMap); + case EnumMethodEnum.GROUPBY: + return typeof(ExprDotEvalGroupBy); + case EnumMethodEnum.COUNTOF: + return typeof(ExprDotEvalCountOf); + case EnumMethodEnum.MIN: + return typeof(ExprDotEvalMinMax); + case EnumMethodEnum.MAX: + return typeof(ExprDotEvalMinMax); + case EnumMethodEnum.AVERAGE: + return typeof(ExprDotEvalAverage); + case EnumMethodEnum.SUMOF: + return typeof(ExprDotEvalSumOf); + case EnumMethodEnum.MOSTFREQUENT: + return typeof(ExprDotEvalMostLeastFrequent); + case EnumMethodEnum.LEASTFREQUENT: + return typeof(ExprDotEvalMostLeastFrequent); + case EnumMethodEnum.SELECTFROM: + return typeof(ExprDotEvalSelectFrom); + case EnumMethodEnum.FIRST: + return typeof(ExprDotEvalFirstLastOf); + case EnumMethodEnum.LAST: + return typeof(ExprDotEvalFirstLastOf); + case EnumMethodEnum.MINBY: + return typeof(ExprDotEvalMinByMaxBy); + case EnumMethodEnum.MAXBY: + return typeof(ExprDotEvalMinByMaxBy); + case EnumMethodEnum.TAKE: + return typeof(ExprDotEvalTakeAndTakeLast); + case EnumMethodEnum.TAKELAST: + return typeof(ExprDotEvalTakeAndTakeLast); + case EnumMethodEnum.TAKEWHILE: + return typeof(ExprDotEvalTakeWhileAndLast); + case EnumMethodEnum.TAKEWHILELAST: + return typeof(ExprDotEvalTakeWhileAndLast); + case EnumMethodEnum.ORDERBY: + return typeof(ExprDotEvalOrderByAscDesc); + case EnumMethodEnum.ORDERBYDESC: + return typeof(ExprDotEvalOrderByAscDesc); + case EnumMethodEnum.DISTINCT: + return typeof(ExprDotEvalDistinct); + case EnumMethodEnum.WHERE: + return typeof(ExprDotEvalWhere); + case EnumMethodEnum.UNION: + return typeof(ExprDotEvalSetExceptUnionIntersect); + case EnumMethodEnum.EXCEPT: + return typeof(ExprDotEvalSetExceptUnionIntersect); + case EnumMethodEnum.INTERSECT: + return typeof(ExprDotEvalSetExceptUnionIntersect); + case EnumMethodEnum.REVERSE: + return typeof(ExprDotEvalReverse); + case EnumMethodEnum.NOOP: + return typeof(ExprDotEvalNoOp); + case EnumMethodEnum.SEQUENCE_EQUAL: + return typeof(ExprDotEvalSequenceEqual); + } + + throw new ArgumentException(); + } + + public static DotMethodFP[] GetFootprints(this EnumMethodEnum value) + { + switch (value) + { + case EnumMethodEnum.AGGREGATE: + return EnumMethodEnumParams.AGGREGATE_FP; + case EnumMethodEnum.ALLOF: + return EnumMethodEnumParams.ALLOF_ANYOF; + case EnumMethodEnum.ANYOF: + return EnumMethodEnumParams.ALLOF_ANYOF; + case EnumMethodEnum.TOMAP: + return EnumMethodEnumParams.MAP; + case EnumMethodEnum.GROUPBY: + return EnumMethodEnumParams.GROUP; + case EnumMethodEnum.COUNTOF: + return EnumMethodEnumParams.COUNTOF_FIRST_LAST; + case EnumMethodEnum.MIN: + return EnumMethodEnumParams.MIN_MAX; + case EnumMethodEnum.MAX: + return EnumMethodEnumParams.MIN_MAX; + case EnumMethodEnum.AVERAGE: + return EnumMethodEnumParams.AVERAGE_SUMOF; + case EnumMethodEnum.SUMOF: + return EnumMethodEnumParams.AVERAGE_SUMOF; + case EnumMethodEnum.MOSTFREQUENT: + return EnumMethodEnumParams.MOST_LEAST_FREQ; + case EnumMethodEnum.LEASTFREQUENT: + return EnumMethodEnumParams.MOST_LEAST_FREQ; + case EnumMethodEnum.SELECTFROM: + return EnumMethodEnumParams.SELECTFROM_MINBY_MAXBY; + case EnumMethodEnum.FIRST: + return EnumMethodEnumParams.COUNTOF_FIRST_LAST; + case EnumMethodEnum.LAST: + return EnumMethodEnumParams.COUNTOF_FIRST_LAST; + case EnumMethodEnum.MINBY: + return EnumMethodEnumParams.SELECTFROM_MINBY_MAXBY; + case EnumMethodEnum.MAXBY: + return EnumMethodEnumParams.SELECTFROM_MINBY_MAXBY; + case EnumMethodEnum.TAKE: + return EnumMethodEnumParams.TAKE; + case EnumMethodEnum.TAKELAST: + return EnumMethodEnumParams.TAKELAST; + case EnumMethodEnum.TAKEWHILE: + return EnumMethodEnumParams.WHERE_FP; + case EnumMethodEnum.TAKEWHILELAST: + return EnumMethodEnumParams.WHERE_FP; + case EnumMethodEnum.ORDERBY: + return EnumMethodEnumParams.ORDERBY_DISTINCT; + case EnumMethodEnum.ORDERBYDESC: + return EnumMethodEnumParams.ORDERBY_DISTINCT; + case EnumMethodEnum.DISTINCT: + return EnumMethodEnumParams.ORDERBY_DISTINCT; + case EnumMethodEnum.WHERE: + return EnumMethodEnumParams.WHERE_FP; + case EnumMethodEnum.UNION: + return EnumMethodEnumParams.SET_LOGIC_FP; + case EnumMethodEnum.EXCEPT: + return EnumMethodEnumParams.SET_LOGIC_FP; + case EnumMethodEnum.INTERSECT: + return EnumMethodEnumParams.SET_LOGIC_FP; + case EnumMethodEnum.REVERSE: + return EnumMethodEnumParams.NOOP_REVERSE; + case EnumMethodEnum.NOOP: + return EnumMethodEnumParams.NOOP_REVERSE; + case EnumMethodEnum.SEQUENCE_EQUAL: + return EnumMethodEnumParams.SEQ_EQUALS_FP; + } + + throw new ArgumentException(); + } + + public static bool IsEnumerationMethod(this String name) + { + return EnumHelper + .GetValues() + .Any(e => String.Equals(name, e.GetNameCamel(), StringComparison.InvariantCultureIgnoreCase)); + } + + public static EnumMethodEnum FromName(this String name) + { + return EnumHelper + .GetValues() + .FirstOrDefault(e => String.Equals(name, e.GetNameCamel(), StringComparison.InvariantCultureIgnoreCase)); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/dot/EnumMethodEnumParams.cs b/NEsper.Core/NEsper.Core/epl/enummethod/dot/EnumMethodEnumParams.cs new file mode 100755 index 000000000..c9af61725 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/dot/EnumMethodEnumParams.cs @@ -0,0 +1,104 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.methodbase; + +namespace com.espertech.esper.epl.enummethod.dot +{ + public class EnumMethodEnumParams { + + public static readonly DotMethodFP[] NOOP_REVERSE = new DotMethodFP[] { + new DotMethodFP(DotMethodFPInputEnum.ANY), + }; + + public static readonly DotMethodFP[] COUNTOF_FIRST_LAST = new DotMethodFP[] { + new DotMethodFP(DotMethodFPInputEnum.ANY), + new DotMethodFP(DotMethodFPInputEnum.ANY, new DotMethodFPParam(1, "predicate", DotMethodFPParamTypeEnum.BOOLEAN)), + }; + + public static readonly DotMethodFP[] TAKELAST = new DotMethodFP[] { + new DotMethodFP(DotMethodFPInputEnum.ANY, new DotMethodFPParam(0, "count", DotMethodFPParamTypeEnum.NUMERIC)), + }; + + public static readonly DotMethodFP[] TAKE = new DotMethodFP[] { + new DotMethodFP(DotMethodFPInputEnum.ANY, new DotMethodFPParam(0, "count", DotMethodFPParamTypeEnum.NUMERIC)), + }; + + public static readonly DotMethodFP[] AGGREGATE_FP = new DotMethodFP[] { + new DotMethodFP(DotMethodFPInputEnum.ANY, + new DotMethodFPParam(0, "initialization-value", DotMethodFPParamTypeEnum.ANY), + new DotMethodFPParam(2, "(result, next)", DotMethodFPParamTypeEnum.ANY)), + }; + + public static readonly DotMethodFP[] ALLOF_ANYOF = new DotMethodFP[] { + new DotMethodFP(DotMethodFPInputEnum.ANY, new DotMethodFPParam(1, "predicate", DotMethodFPParamTypeEnum.BOOLEAN)), + }; + + public static readonly DotMethodFP[] MIN_MAX = new DotMethodFP[] { + new DotMethodFP(DotMethodFPInputEnum.SCALAR_ANY), + new DotMethodFP(DotMethodFPInputEnum.SCALAR_ANY, new DotMethodFPParam(1, "value-selector", DotMethodFPParamTypeEnum.ANY)), + new DotMethodFP(DotMethodFPInputEnum.EVENTCOLL, new DotMethodFPParam(1, "value-selector", DotMethodFPParamTypeEnum.ANY)), + }; + + public static readonly DotMethodFP[] SELECTFROM_MINBY_MAXBY = new DotMethodFP[] { + new DotMethodFP(DotMethodFPInputEnum.SCALAR_ANY, new DotMethodFPParam(1, "value-selector", DotMethodFPParamTypeEnum.ANY)), + new DotMethodFP(DotMethodFPInputEnum.EVENTCOLL, new DotMethodFPParam(1, "value-selector", DotMethodFPParamTypeEnum.ANY)), + }; + + public static readonly DotMethodFP[] AVERAGE_SUMOF = new DotMethodFP[] { + new DotMethodFP(DotMethodFPInputEnum.SCALAR_NUMERIC), + new DotMethodFP(DotMethodFPInputEnum.SCALAR_ANY, new DotMethodFPParam(1, "value-selector", DotMethodFPParamTypeEnum.NUMERIC)), + new DotMethodFP(DotMethodFPInputEnum.EVENTCOLL, new DotMethodFPParam(1, "value-selector", DotMethodFPParamTypeEnum.NUMERIC)) + }; + + public static readonly DotMethodFP[] MOST_LEAST_FREQ = new DotMethodFP[] { + new DotMethodFP(DotMethodFPInputEnum.SCALAR_ANY), + new DotMethodFP(DotMethodFPInputEnum.SCALAR_ANY, new DotMethodFPParam(1, "value-selector", DotMethodFPParamTypeEnum.ANY)), + new DotMethodFP(DotMethodFPInputEnum.EVENTCOLL, new DotMethodFPParam(1, "value-selector", DotMethodFPParamTypeEnum.ANY)) + }; + + public static readonly DotMethodFP[] MAP = new DotMethodFP[] { + new DotMethodFP(DotMethodFPInputEnum.SCALAR_ANY, + new DotMethodFPParam(1, "key-selector", DotMethodFPParamTypeEnum.ANY), + new DotMethodFPParam(1, "value-selector", DotMethodFPParamTypeEnum.ANY)), + new DotMethodFP(DotMethodFPInputEnum.EVENTCOLL, + new DotMethodFPParam(1, "key-selector", DotMethodFPParamTypeEnum.ANY), + new DotMethodFPParam(1, "value-selector", DotMethodFPParamTypeEnum.ANY)), + }; + + public static readonly DotMethodFP[] GROUP = new DotMethodFP[] { + new DotMethodFP(DotMethodFPInputEnum.SCALAR_ANY, new DotMethodFPParam(1, "key-selector", DotMethodFPParamTypeEnum.ANY)), + new DotMethodFP(DotMethodFPInputEnum.EVENTCOLL, new DotMethodFPParam(1, "key-selector", DotMethodFPParamTypeEnum.ANY)), + new DotMethodFP(DotMethodFPInputEnum.SCALAR_ANY, + new DotMethodFPParam(1, "key-selector", DotMethodFPParamTypeEnum.ANY), + new DotMethodFPParam(1, "value-selector", DotMethodFPParamTypeEnum.ANY)), + new DotMethodFP(DotMethodFPInputEnum.EVENTCOLL, + new DotMethodFPParam(1, "key-selector", DotMethodFPParamTypeEnum.ANY), + new DotMethodFPParam(1, "value-selector", DotMethodFPParamTypeEnum.ANY)), + }; + + public static readonly DotMethodFP[] ORDERBY_DISTINCT = new DotMethodFP[] { + new DotMethodFP(DotMethodFPInputEnum.SCALAR_ANY), + new DotMethodFP(DotMethodFPInputEnum.SCALAR_ANY, new DotMethodFPParam(1, "compare-selector", DotMethodFPParamTypeEnum.ANY)), + new DotMethodFP(DotMethodFPInputEnum.EVENTCOLL, new DotMethodFPParam(1, "compare-selector", DotMethodFPParamTypeEnum.ANY)), + }; + + public static readonly DotMethodFP[] WHERE_FP = new DotMethodFP[] { + new DotMethodFP(DotMethodFPInputEnum.ANY, new DotMethodFPParam(1, "predicate", DotMethodFPParamTypeEnum.BOOLEAN)), + new DotMethodFP(DotMethodFPInputEnum.ANY, new DotMethodFPParam(2, "(predicate, index)", DotMethodFPParamTypeEnum.BOOLEAN)) + }; + + public static readonly DotMethodFP[] SET_LOGIC_FP = new DotMethodFP[] { + new DotMethodFP(DotMethodFPInputEnum.ANY, new DotMethodFPParam(0, "collection", DotMethodFPParamTypeEnum.ANY)), + }; + + public static readonly DotMethodFP[] SEQ_EQUALS_FP = new DotMethodFP[] { + new DotMethodFP(DotMethodFPInputEnum.SCALAR_ANY, new DotMethodFPParam(0, "sequence", DotMethodFPParamTypeEnum.ANY)), + }; + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/dot/EnumMethodReturnType.cs b/NEsper.Core/NEsper.Core/epl/enummethod/dot/EnumMethodReturnType.cs new file mode 100755 index 000000000..0af92ba6c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/dot/EnumMethodReturnType.cs @@ -0,0 +1,18 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.enummethod.dot +{ + public enum EnumMethodReturnType { + + ITERATOR_BEAN, + BEAN, + ITERATOR_VALUE, + VALUE + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDeclaredOrLambdaNode.cs b/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDeclaredOrLambdaNode.cs new file mode 100755 index 000000000..7d7ad9a44 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDeclaredOrLambdaNode.cs @@ -0,0 +1,15 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.enummethod.dot +{ + public interface ExprDeclaredOrLambdaNode + { + bool IsValidated { get; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotEvalEnumMethod.cs b/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotEvalEnumMethod.cs new file mode 100755 index 000000000..599f2f4d3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotEvalEnumMethod.cs @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.dot; +using com.espertech.esper.epl.rettype; + +namespace com.espertech.esper.epl.enummethod.dot +{ + public interface ExprDotEvalEnumMethod : ExprDotEval + { + void Init( + int? streamOfProviderIfApplicable, + EnumMethodEnum lambda, + String lambdaUsedName, + EPType currentInputType, + IList parameters, + ExprValidationContext validationContext); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotEvalEnumMethodBase.cs b/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotEvalEnumMethodBase.cs new file mode 100755 index 000000000..7e9e94b40 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotEvalEnumMethodBase.cs @@ -0,0 +1,336 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.enummethod.eval; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.dot; +using com.espertech.esper.epl.expression.visitor; +using com.espertech.esper.epl.methodbase; +using com.espertech.esper.epl.rettype; +using com.espertech.esper.events; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.enummethod.dot +{ + public abstract class ExprDotEvalEnumMethodBase + : ExprDotEvalEnumMethod + , ExpressionResultCacheStackEntry + { + private EnumMethodEnum _enumMethodEnum; + private String _enumMethodUsedName; + private int _streamCountIncoming; + private EnumEval _enumEval; + private int _enumEvalNumRequiredEvents; + private EPType _typeInfo; + + private bool _cache; + private long _contextNumber = 0; + + protected ExprDotEvalEnumMethodBase() + { + } + + public abstract EventType[] GetAddStreamTypes(string enumMethodUsedName, IList goesToNames, EventType inputEventType, Type collectionComponentType, IList bodiesAndParameters, EventAdapterService eventAdapterService); + + public abstract EnumEval GetEnumEval(EngineImportService engineImportService, EventAdapterService eventAdapterService, StreamTypeService streamTypeService, int statementId, string enumMethodUsedName, IList bodiesAndParameters, EventType inputEventType, Type collectionComponentType, int numStreamsIncoming, bool disablePropertyExpressionEventCollCache); + + public EnumMethodEnum EnumMethodEnum + { + get { return _enumMethodEnum; } + } + + public void Visit(ExprDotEvalVisitor visitor) + { + visitor.VisitEnumeration(_enumMethodEnum.GetNameCamel()); + } + + public void Init( + int? streamOfProviderIfApplicable, + EnumMethodEnum enumMethodEnum, + String enumMethodUsedName, + EPType typeInfo, + IList parameters, + ExprValidationContext validationContext) + { + var eventTypeColl = EPTypeHelper.GetEventTypeMultiValued(typeInfo); + var eventTypeBean = EPTypeHelper.GetEventTypeSingleValued(typeInfo); + var collectionComponentType = EPTypeHelper.GetClassMultiValued(typeInfo); + + _enumMethodEnum = enumMethodEnum; + _enumMethodUsedName = enumMethodUsedName; + _streamCountIncoming = validationContext.StreamTypeService.EventTypes.Length; + + if (eventTypeColl == null && collectionComponentType == null && eventTypeBean == null) + { + throw new ExprValidationException( + "Invalid input for built-in enumeration method '" + enumMethodUsedName + + "', expecting collection of event-type or scalar values as input, received " + + EPTypeHelper.ToTypeDescriptive(typeInfo)); + } + + // compile parameter abstract for validation against available footprints + var footprintProvided = DotMethodUtil.GetProvidedFootprint(parameters); + + // validate parameters + DotMethodInputTypeMatcher inputTypeMatcher = new ProxyDotMethodInputTypeMatcher + { + ProcMatches = fp => + { + if (fp.Input == DotMethodFPInputEnum.EVENTCOLL && eventTypeBean == null && eventTypeColl == null) + { + return false; + } + if (fp.Input == DotMethodFPInputEnum.SCALAR_ANY && collectionComponentType == null) + { + return false; + } + return true; + } + }; + + var footprint = DotMethodUtil.ValidateParametersDetermineFootprint( + enumMethodEnum.GetFootprints(), DotMethodTypeEnum.ENUM, enumMethodUsedName, footprintProvided, + inputTypeMatcher); + + // validate input criteria met for this footprint + if (footprint.Input != DotMethodFPInputEnum.ANY) + { + var message = "Invalid input for built-in enumeration method '" + enumMethodUsedName + "' and " + + footprint.Parameters.Length + "-parameter footprint, expecting collection of "; + var received = " as input, received " + EPTypeHelper.ToTypeDescriptive(typeInfo); + if (footprint.Input == DotMethodFPInputEnum.EVENTCOLL && eventTypeColl == null) + { + throw new ExprValidationException(message + "events" + received); + } + if (footprint.Input.IsScalar() && collectionComponentType == null) + { + throw new ExprValidationException(message + "values (typically scalar values)" + received); + } + if (footprint.Input == DotMethodFPInputEnum.SCALAR_NUMERIC && !collectionComponentType.IsNumeric()) + { + throw new ExprValidationException(message + "numeric values" + received); + } + } + + // manage context of this lambda-expression in regards to outer lambda-expression that may call this one. + ExpressionResultCacheForEnumerationMethod enumerationMethodCache = validationContext.ExprEvaluatorContext.ExpressionResultCacheService.AllocateEnumerationMethod; + enumerationMethodCache.PushStack(this); + + var bodiesAndParameters = new List(); + var count = 0; + var inputEventType = eventTypeBean ?? eventTypeColl; + foreach (var node in parameters) + { + var bodyAndParameter = GetBodyAndParameter( + enumMethodUsedName, count++, node, inputEventType, collectionComponentType, validationContext, + bodiesAndParameters, footprint); + bodiesAndParameters.Add(bodyAndParameter); + } + + _enumEval = GetEnumEval( + validationContext.EngineImportService, validationContext.EventAdapterService, + validationContext.StreamTypeService, validationContext.StatementId, enumMethodUsedName, + bodiesAndParameters, inputEventType, collectionComponentType, _streamCountIncoming, + validationContext.IsDisablePropertyExpressionEventCollCache); + _enumEvalNumRequiredEvents = _enumEval.StreamNumSize; + + // determine the stream ids of event properties asked for in the Evaluator(s) + var streamsRequired = new HashSet(); + var visitor = new ExprNodeIdentifierCollectVisitor(); + foreach (var desc in bodiesAndParameters) + { + desc.Body.Accept(visitor); + foreach (var ident in visitor.ExprProperties) + { + streamsRequired.Add(ident.StreamId); + } + } + if (streamOfProviderIfApplicable != null) + { + streamsRequired.Add(streamOfProviderIfApplicable.Value); + } + + // We turn on caching if the stack is not empty (we are an inner lambda) and the dependency does not include the stream. + var isInner = !enumerationMethodCache.PopLambda(); + if (isInner) + { + // If none of the properties that the current lambda uses comes from the ultimate Parent(s) or subsequent streams, then cache. + var parents = enumerationMethodCache.GetStack(); + var found = false; + foreach (var req in streamsRequired) + { + var first = (ExprDotEvalEnumMethodBase) parents.First; + var parentIncoming = first._streamCountIncoming - 1; + var selfAdded = _streamCountIncoming; // the one we use ourselfs + if (req > parentIncoming && req < selfAdded) + { + found = true; + } + } + _cache = !found; + } + } + + public EPType TypeInfo + { + get { return _typeInfo; } + set { _typeInfo = value; } + } + + public object Evaluate(object target, EvaluateParams evalParams) + { + if (target is EventBean) + { + target = Collections.SingletonList((EventBean) target); + } + + var exprEvaluatorContext = evalParams.ExprEvaluatorContext; + var enumerationMethodCache = exprEvaluatorContext.ExpressionResultCacheService.AllocateEnumerationMethod; + if (_cache) + { + var cacheValue = enumerationMethodCache.GetEnumerationMethodLastValue(this); + if (cacheValue != null) + { + return cacheValue.Result; + } + var coll = target.Unwrap(); + if (coll == null) + { + return null; + } + var eventsLambda = AllocateCopyEventLambda(evalParams.EventsPerStream); + var result = _enumEval.EvaluateEnumMethod(eventsLambda, coll, evalParams.IsNewData, exprEvaluatorContext); + enumerationMethodCache.SaveEnumerationMethodLastValue(this, result); + return result; + } + + _contextNumber++; + try + { + enumerationMethodCache.PushContext(_contextNumber); + var coll = target.Unwrap(); + if (coll == null) + { + return null; + } + var eventsLambda = AllocateCopyEventLambda(evalParams.EventsPerStream); + return _enumEval.EvaluateEnumMethod(eventsLambda, coll, evalParams.IsNewData, exprEvaluatorContext); + } + finally + { + enumerationMethodCache.PopContext(); + } + } + + private EventBean[] AllocateCopyEventLambda(EventBean[] eventsPerStream) + { + var eventsLambda = new EventBean[_enumEvalNumRequiredEvents]; + EventBeanUtility.SafeArrayCopy(eventsPerStream, eventsLambda); + return eventsLambda; + } + + private ExprDotEvalParam GetBodyAndParameter( + String enumMethodUsedName, + int parameterNum, + ExprNode parameterNode, + EventType inputEventType, + Type collectionComponentType, + ExprValidationContext validationContext, + IList priorParameters, + DotMethodFP footprint) + { + // handle an expression that is a constant or other (not =>) + if (!(parameterNode is ExprLambdaGoesNode)) + { + // no node subtree validation is required here, the chain parameter validation has taken place in ExprDotNode.validate + // validation of parameter types has taken place in footprint matching + return new ExprDotEvalParamExpr(parameterNum, parameterNode, parameterNode.ExprEvaluator); + } + + var goesNode = (ExprLambdaGoesNode) parameterNode; + + // Get secondary + var additionalTypes = GetAddStreamTypes( + enumMethodUsedName, goesNode.GoesToNames, inputEventType, collectionComponentType, priorParameters, validationContext.EventAdapterService); + var additionalStreamNames = goesNode.GoesToNames.ToArray(); + + ValidateDuplicateStreamNames(validationContext.StreamTypeService.StreamNames, additionalStreamNames); + + // add name and type to list of known types + var addTypes = + (EventType[]) + CollectionUtil.ArrayExpandAddElements( + validationContext.StreamTypeService.EventTypes, additionalTypes); + var addNames = + (String[]) + CollectionUtil.ArrayExpandAddElements( + validationContext.StreamTypeService.StreamNames, additionalStreamNames); + + var types = new StreamTypeServiceImpl( + addTypes, addNames, new bool[addTypes.Length], null, false); + + // validate expression body + var filter = goesNode.ChildNodes[0]; + try + { + var filterValidationContext = new ExprValidationContext(types, validationContext); + filter = ExprNodeUtility.GetValidatedSubtree(ExprNodeOrigin.DECLAREDEXPRBODY, filter, filterValidationContext); + } + catch (ExprValidationException ex) + { + throw new ExprValidationException( + "Error validating enumeration method '" + enumMethodUsedName + "' parameter " + parameterNum + ": " + + ex.Message, ex); + } + + var filterEvaluator = filter.ExprEvaluator; + var expectedType = footprint.Parameters[parameterNum].ParamType; + // Lambda-methods don't use a specific expected return-type, so passing null for type is fine. + DotMethodUtil.ValidateSpecificType( + enumMethodUsedName, DotMethodTypeEnum.ENUM, expectedType, null, filterEvaluator.ReturnType, parameterNum, + filter); + + var numStreamsIncoming = validationContext.StreamTypeService.EventTypes.Length; + return new ExprDotEvalParamLambda( + parameterNum, filter, filterEvaluator, + numStreamsIncoming, goesNode.GoesToNames, additionalTypes); + } + + private void ValidateDuplicateStreamNames(String[] streamNames, String[] additionalStreamNames) + { + for (var added = 0; added < additionalStreamNames.Length; added++) + { + for (var exist = 0; exist < streamNames.Length; exist++) + { + if (streamNames[exist] != null && + string.Equals( + streamNames[exist], additionalStreamNames[added], StringComparison.CurrentCultureIgnoreCase)) + { + var message = "Error validating enumeration method '" + _enumMethodUsedName + + "', the lambda-parameter name '" + additionalStreamNames[added] + + "' has already been declared in this context"; + throw new ExprValidationException(message); + } + } + } + } + + public override String ToString() + { + return GetType().Name + " lambda=" + _enumMethodEnum; + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotEvalParam.cs b/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotEvalParam.cs new file mode 100755 index 000000000..ca79b4b6e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotEvalParam.cs @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.enummethod.dot +{ + public abstract class ExprDotEvalParam + { + protected ExprDotEvalParam(int parameterNum, ExprNode body, ExprEvaluator bodyEvaluator) + { + ParameterNum = parameterNum; + Body = body; + BodyEvaluator = bodyEvaluator; + } + + public int ParameterNum { get; private set; } + + public ExprNode Body { get; private set; } + + public ExprEvaluator BodyEvaluator { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotEvalParamExpr.cs b/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotEvalParamExpr.cs new file mode 100755 index 000000000..f55c4f80d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotEvalParamExpr.cs @@ -0,0 +1,21 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.enummethod.dot +{ + public class ExprDotEvalParamExpr : ExprDotEvalParam + { + public ExprDotEvalParamExpr(int parameterNum, ExprNode body, ExprEvaluator bodyEvaluator) + : base(parameterNum, body, bodyEvaluator) + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotEvalParamLambda.cs b/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotEvalParamLambda.cs new file mode 100755 index 000000000..23dc9d35b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotEvalParamLambda.cs @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.enummethod.dot +{ + public class ExprDotEvalParamLambda : ExprDotEvalParam + { + public ExprDotEvalParamLambda(int parameterNum, ExprNode body, ExprEvaluator bodyEvaluator, int streamCountIncoming, IList goesToNames, EventType[] goesToTypes) + : base(parameterNum, body, bodyEvaluator) + { + StreamCountIncoming = streamCountIncoming; + GoesToNames = goesToNames; + GoesToTypes = goesToTypes; + } + + public int StreamCountIncoming { get; private set; } + + public IList GoesToNames { get; private set; } + + public EventType[] GoesToTypes { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotEvalProperty.cs b/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotEvalProperty.cs new file mode 100755 index 000000000..1f6443e8a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotEvalProperty.cs @@ -0,0 +1,42 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.dot; +using com.espertech.esper.epl.rettype; + +namespace com.espertech.esper.epl.enummethod.dot +{ + public class ExprDotEvalProperty : ExprDotEval + { + private readonly EventPropertyGetter _getter; + private readonly EPType _returnType; + + public ExprDotEvalProperty(EventPropertyGetter getter, EPType returnType) + { + _getter = getter; + _returnType = returnType; + } + + public object Evaluate(object target, EvaluateParams evalParams) + { + return target is EventBean ? _getter.Get((EventBean) target) : null; + } + + public EPType TypeInfo + { + get { return _returnType; } + } + + public void Visit(ExprDotEvalVisitor visitor) + { + visitor.VisitPropertySource(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotEvalTypeInfo.cs b/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotEvalTypeInfo.cs new file mode 100755 index 000000000..8206fb901 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotEvalTypeInfo.cs @@ -0,0 +1,115 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.enummethod.dot +{ + public class ExprDotEvalTypeInfo + { + public Type Scalar { get; private set; } + + public Type Component { get; private set; } + + public EventType EventType { get; private set; } + + public EventType EventTypeColl { get; private set; } + + private ExprDotEvalTypeInfo(Type scalar, Type component, EventType eventType, EventType eventTypeColl) + { + Scalar = scalar; + Component = component; + EventType = eventType; + EventTypeColl = eventTypeColl; + } + + public static ExprDotEvalTypeInfo From(Type inputType, Type collectionComponentType, EventType lambdaType) + { + var info = new ExprDotEvalTypeInfo(null, null, null, null); + if (lambdaType != null) + { + info.EventTypeColl = lambdaType; + } + else if (collectionComponentType != null) + { + info.Scalar = typeof(ICollection); + info.Component = collectionComponentType; + } + else + { + info.Scalar = inputType; + } + return info; + } + + public bool IsScalar + { + get { return Scalar != null; } + } + + public static ExprDotEvalTypeInfo FromMethod(MethodInfo method) + { + var returnType = method.ReturnType; + if (returnType.IsImplementsInterface(typeof(ICollection))) + { + var componentType = TypeHelper.GetGenericReturnType(method, true); + return ComponentColl(componentType); + } + return ScalarOrUnderlying(method.ReturnType); + } + + public static ExprDotEvalTypeInfo ScalarOrUnderlying(Type scalar) + { + return new ExprDotEvalTypeInfo(scalar, null, null, null); + } + + public static ExprDotEvalTypeInfo ComponentColl(Type component) + { + return new ExprDotEvalTypeInfo(typeof(ICollection), component, null, null); + } + + public static ExprDotEvalTypeInfo EventColl(EventType eventColl) + { + return new ExprDotEvalTypeInfo(null, null, null, eventColl); + } + + public static ExprDotEvalTypeInfo Event(EventType theEvent) + { + return new ExprDotEvalTypeInfo(null, null, theEvent, null); + } + + public String ToTypeName() + { + if (Component != null) + { + return "collection of " + Component.Name; + } + else if (EventType != null) + { + return "event type '" + EventType.Name + "'"; + } + else if (EventTypeColl != null) + { + return "collection of events of type '" + EventTypeColl.Name + "'"; + } + else if (Scalar != null) + { + return "class " + TypeHelper.GetTypeNameFullyQualPretty(Scalar); + } + else + { + return "an incompatible type"; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotEvalUnpackBean.cs b/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotEvalUnpackBean.cs new file mode 100755 index 000000000..551e362e5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotEvalUnpackBean.cs @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.dot; +using com.espertech.esper.epl.rettype; + +namespace com.espertech.esper.epl.enummethod.dot +{ + public class ExprDotEvalUnpackBean : ExprDotEval + { + private readonly EPType _returnType; + + public ExprDotEvalUnpackBean(EventType lambdaType) + { + _returnType = EPTypeHelper.SingleValue(lambdaType.UnderlyingType); + } + + public object Evaluate(object target, EvaluateParams evalParams) + { + var theEvent = target as EventBean; + return theEvent != null ? theEvent.Underlying : null; + } + + public EPType TypeInfo + { + get { return _returnType; } + } + + public void Visit(ExprDotEvalVisitor visitor) + { + visitor.VisitUnderlyingEvent(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotEvalUnpackBeanTable.cs b/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotEvalUnpackBeanTable.cs new file mode 100755 index 000000000..f943406fe --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotEvalUnpackBeanTable.cs @@ -0,0 +1,57 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.dot; +using com.espertech.esper.epl.rettype; +using com.espertech.esper.epl.table.mgmt; + +namespace com.espertech.esper.epl.enummethod.dot +{ + public class ExprDotEvalUnpackBeanTable : ExprDotEval + { + private readonly EPType _returnType; + private readonly TableMetadata _tableMetadata; + + public ExprDotEvalUnpackBeanTable(EventType lambdaType, TableMetadata tableMetadata) + { + _tableMetadata = tableMetadata; + _returnType = EPTypeHelper.SingleValue(tableMetadata.PublicEventType.UnderlyingType); + } + + public object Evaluate(object target, EvaluateParams evalParams) + { + if (target == null) + { + return null; + } + + var theEvent = target as EventBean; + if (theEvent == null) + { + return null; + } + return _tableMetadata.EventToPublic.ConvertToUnd(theEvent, evalParams); + } + + public EPType TypeInfo + { + get { return _returnType; } + } + + public void Visit(ExprDotEvalVisitor visitor) + { + visitor.VisitUnderlyingEvent(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotEvalUnpackCollEventBean.cs b/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotEvalUnpackCollEventBean.cs new file mode 100755 index 000000000..98249a209 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotEvalUnpackCollEventBean.cs @@ -0,0 +1,57 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.dot; +using com.espertech.esper.epl.rettype; + +namespace com.espertech.esper.epl.enummethod.dot +{ + public class ExprDotEvalUnpackCollEventBean : ExprDotEval + { + private readonly EPType _typeInfo; + + public ExprDotEvalUnpackCollEventBean(EventType type) { + _typeInfo = EPTypeHelper.CollectionOfSingleValue(type.UnderlyingType); + } + + public object Evaluate(object target, EvaluateParams evalParams) + { + if (target == null) + { + return null; + } + + ICollection baseCollection; + if (target is ICollection) + return ((ICollection)target).Select(e => e.Underlying).ToList(); + else if (target is ICollection) + return ((ICollection)target).OfType().Select(e => e.Underlying).ToList(); + else if (target is ICollection) + return ((ICollection)target).Cast().Select(e => e.Underlying).ToList(); + + throw new ArgumentException("invalid value for target"); + } + + public EPType TypeInfo + { + get { return _typeInfo; } + } + + public void Visit(ExprDotEvalVisitor visitor) + { + visitor.VisitUnderlyingEventColl(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotEvalUnpackCollEventBeanTable.cs b/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotEvalUnpackCollEventBeanTable.cs new file mode 100755 index 000000000..632ccd566 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotEvalUnpackCollEventBeanTable.cs @@ -0,0 +1,58 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.dot; +using com.espertech.esper.epl.rettype; +using com.espertech.esper.epl.table.mgmt; + +namespace com.espertech.esper.epl.enummethod.dot +{ + public class ExprDotEvalUnpackCollEventBeanTable : ExprDotEval + { + private readonly EPType _typeInfo; + private readonly TableMetadata _tableMetadata; + + public ExprDotEvalUnpackCollEventBeanTable(EventType type, TableMetadata tableMetadata) + { + this._typeInfo = EPTypeHelper.CollectionOfSingleValue(tableMetadata.PublicEventType.UnderlyingType); + this._tableMetadata = tableMetadata; + } + + public object Evaluate(object target, EvaluateParams evalParams) + { + if (target == null) + { + return null; + } + var events = (ICollection) target; + var underlyings = new ArrayDeque(events.Count); + foreach (var @event in events) + { + underlyings.Add(_tableMetadata.EventToPublic.ConvertToUnd(@event, evalParams)); + } + return underlyings; + } + + public EPType TypeInfo + { + get { return _typeInfo; } + } + + public void Visit(ExprDotEvalVisitor visitor) + { + visitor.VisitUnderlyingEventColl(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotStaticMethodWrap.cs b/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotStaticMethodWrap.cs new file mode 100755 index 000000000..f0c9c88d0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotStaticMethodWrap.cs @@ -0,0 +1,21 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.epl.rettype; + +namespace com.espertech.esper.epl.enummethod.dot +{ + public interface ExprDotStaticMethodWrap + { + EPType TypeInfo { get; } + ICollection Convert(Object result); + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotStaticMethodWrapArrayEvents.cs b/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotStaticMethodWrapArrayEvents.cs new file mode 100755 index 000000000..fed59b3e6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotStaticMethodWrapArrayEvents.cs @@ -0,0 +1,54 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.epl.rettype; +using com.espertech.esper.events; +using com.espertech.esper.events.bean; + +namespace com.espertech.esper.epl.enummethod.dot +{ + public class ExprDotStaticMethodWrapArrayEvents : ExprDotStaticMethodWrap + { + private readonly EventAdapterService _eventAdapterService; + private readonly BeanEventType _type; + + public ExprDotStaticMethodWrapArrayEvents(EventAdapterService eventAdapterService, BeanEventType type) + { + _eventAdapterService = eventAdapterService; + _type = type; + } + + public EPType TypeInfo + { + get { return EPTypeHelper.CollectionOfEvents(_type); } + } + + public ICollection Convert(Object result) + { + if (result == null) + { + return null; + } + + var asArray = result as Array; + if (asArray == null) + { + return null; + } + + return asArray.Cast() + .Select(item => _eventAdapterService.AdapterForTypedObject(item, _type)) + .Cast() + .ToList(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotStaticMethodWrapArrayScalar.cs b/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotStaticMethodWrapArrayScalar.cs new file mode 100755 index 000000000..5fe807447 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotStaticMethodWrapArrayScalar.cs @@ -0,0 +1,52 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.rettype; + +namespace com.espertech.esper.epl.enummethod.dot +{ + public class ExprDotStaticMethodWrapArrayScalar : ExprDotStaticMethodWrap + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly String _methodName; + private readonly Type _componentType; + + public ExprDotStaticMethodWrapArrayScalar(String methodName, Type componentType) + { + _methodName = methodName; + _componentType = componentType; + } + + public EPType TypeInfo + { + get { return EPTypeHelper.CollectionOfSingleValue(_componentType); } + } + + public ICollection Convert(Object result) + { + if (result == null) + { + return null; + } + + var asArray = result as Array; + if (asArray == null) + { + Log.Warn(string.Format("Expected array-type input from method '{0}' but received {1}", _methodName, result.GetType().FullName)); + return null; + } + return asArray.Cast().ToList(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotStaticMethodWrapCollection.cs b/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotStaticMethodWrapCollection.cs new file mode 100755 index 000000000..fcf78b510 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotStaticMethodWrapCollection.cs @@ -0,0 +1,59 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.compat.magic; +using com.espertech.esper.epl.rettype; + +namespace com.espertech.esper.epl.enummethod.dot +{ + public class ExprDotStaticMethodWrapCollection : ExprDotStaticMethodWrap + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly String _methodName; + private readonly Type _componentType; + + public ExprDotStaticMethodWrapCollection(String methodName, Type componentType) + { + _methodName = methodName; + _componentType = componentType; + } + + public EPType TypeInfo + { + get { return EPTypeHelper.CollectionOfSingleValue(_componentType); } + } + + public ICollection Convert(Object result) + { + if (result == null) + { + return null; + } + + if (result is ICollection) + return ((ICollection)result); + if (result.GetType().IsGenericCollection()) + return MagicMarker.GetCollectionFactory(result.GetType()).Invoke(result); + if (result is ICollection) + return ((ICollection)result).Cast().ToList(); + + Log.Warn("Expected collection-type input from method '{0}' but received {1}", _methodName, + result.GetType().FullName); + return null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotStaticMethodWrapEventBeanArr.cs b/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotStaticMethodWrapEventBeanArr.cs new file mode 100755 index 000000000..95eccc9d4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotStaticMethodWrapEventBeanArr.cs @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.rettype; + +namespace com.espertech.esper.epl.enummethod.dot +{ + public class ExprDotStaticMethodWrapEventBeanArr : ExprDotStaticMethodWrap + { + private readonly EventType _type; + + public ExprDotStaticMethodWrapEventBeanArr(EventType type) + { + _type = type; + } + + public EPType TypeInfo + { + get { return EPTypeHelper.CollectionOfEvents(_type); } + } + + public ICollection Convert(Object result) + { + if (result == null) + { + return null; + } + return result.Unwrap(); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotStaticMethodWrapEventBeanColl.cs b/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotStaticMethodWrapEventBeanColl.cs new file mode 100755 index 000000000..742a06fc1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotStaticMethodWrapEventBeanColl.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.rettype; + +namespace com.espertech.esper.epl.enummethod.dot +{ + public class ExprDotStaticMethodWrapEventBeanColl : ExprDotStaticMethodWrap + { + private readonly EventType _type; + + public ExprDotStaticMethodWrapEventBeanColl(EventType type) + { + _type = type; + } + + public EPType TypeInfo + { + get { return EPTypeHelper.CollectionOfEvents(_type); } + } + + public ICollection Convert(Object result) + { + return result.Unwrap(); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotStaticMethodWrapFactory.cs b/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotStaticMethodWrapFactory.cs new file mode 100755 index 000000000..f26f11330 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotStaticMethodWrapFactory.cs @@ -0,0 +1,87 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events; +using com.espertech.esper.events.bean; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.enummethod.dot +{ + public class ExprDotStaticMethodWrapFactory + { + public static ExprDotStaticMethodWrap Make( + MethodInfo method, + EventAdapterService eventAdapterService, + IList modifiedChain, + string optionalEventTypeName) + { + if (modifiedChain.IsEmpty() || (!modifiedChain[0].Name.IsEnumerationMethod())) + { + return null; + } + + + if (method.ReturnType.IsArray) + { + var componentType = method.ReturnType.GetElementType(); + if (componentType == typeof(EventBean)) + { + EventType eventType = RequireEventType(method, eventAdapterService, optionalEventTypeName); + return new ExprDotStaticMethodWrapEventBeanArr(eventType); + } + if (componentType == null || componentType.IsBuiltinDataType()) + { + return new ExprDotStaticMethodWrapArrayScalar(method.Name, componentType); + } + var type = (BeanEventType)eventAdapterService.AddBeanType(componentType.GetDefaultTypeName(), componentType, false, false, false); + return new ExprDotStaticMethodWrapArrayEvents(eventAdapterService, type); + } + +#if DEFUNCT + if (method.ReturnType.IsGenericCollection()) + { + var genericType = TypeHelper.GetGenericReturnType(method, true); + if (genericType == null || genericType.IsBuiltinDataType()) + { + return new ExprDotStaticMethodWrapCollection(method.Name, genericType); + } + } +#endif + + if (method.ReturnType.IsGenericEnumerable()) + { + var genericType = TypeHelper.GetGenericReturnType(method, true); + if (genericType == typeof(EventBean)) + { + EventType eventType = RequireEventType(method, eventAdapterService, optionalEventTypeName); + return new ExprDotStaticMethodWrapEventBeanColl(eventType); + } + if (genericType == null || genericType.IsBuiltinDataType()) + { + return new ExprDotStaticMethodWrapIterableScalar(method.Name, genericType); + } + var type = (BeanEventType)eventAdapterService.AddBeanType(genericType.GetDefaultTypeName(), genericType, false, false, false); + return new ExprDotStaticMethodWrapIterableEvents(eventAdapterService, type); + } + return null; + } + + private static EventType RequireEventType(MethodInfo method, EventAdapterService eventAdapterService, string optionalEventTypeName) + { + return EventTypeUtility.RequireEventType("Method", method.Name, eventAdapterService, optionalEventTypeName); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotStaticMethodWrapIterableEvents.cs b/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotStaticMethodWrapIterableEvents.cs new file mode 100755 index 000000000..277e21b1a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotStaticMethodWrapIterableEvents.cs @@ -0,0 +1,55 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.rettype; +using com.espertech.esper.events; +using com.espertech.esper.events.bean; + +namespace com.espertech.esper.epl.enummethod.dot +{ + [Serializable] + public class ExprDotStaticMethodWrapIterableEvents : ExprDotStaticMethodWrap + { + private readonly EventAdapterService _eventAdapterService; + private readonly BeanEventType _type; + + public ExprDotStaticMethodWrapIterableEvents(EventAdapterService eventAdapterService, BeanEventType type) + { + _eventAdapterService = eventAdapterService; + _type = type; + } + + public EPType TypeInfo + { + get { return EPTypeHelper.CollectionOfEvents(_type); } + } + + public ICollection Convert(Object result) + { + if (result == null) + { + return null; + } + + var asEnum = result.Unwrap(); + if (asEnum == null) + { + return null; + } + + return asEnum + .Select(item => _eventAdapterService.AdapterForTypedObject(item, _type)) + .ToArray(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotStaticMethodWrapIterableScalar.cs b/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotStaticMethodWrapIterableScalar.cs new file mode 100755 index 000000000..ba29a5b57 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprDotStaticMethodWrapIterableScalar.cs @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.rettype; + +namespace com.espertech.esper.epl.enummethod.dot +{ + public class ExprDotStaticMethodWrapIterableScalar : ExprDotStaticMethodWrap + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly String _methodName; + private readonly Type _componentType; + + public ExprDotStaticMethodWrapIterableScalar(String methodName, Type componentType) + { + _methodName = methodName; + _componentType = componentType; + } + + public EPType TypeInfo + { + get { return EPTypeHelper.CollectionOfSingleValue(_componentType); } + } + + public ICollection Convert(Object result) + { + if (result == null) + { + return null; + } + if (!(result is IEnumerable)) + { + Log.Warn("Expected iterable-type input from method '" + _methodName + "' but received " + result.GetType()); + return null; + } + + var asEnumerable = (IEnumerable)result; + return asEnumerable.Cast().ToList(); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprLambdaGoesNode.cs b/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprLambdaGoesNode.cs new file mode 100755 index 000000000..f3ebab4ca --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/dot/ExprLambdaGoesNode.cs @@ -0,0 +1,90 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.enummethod.dot +{ + /// + /// Represents the case-when-then-else control flow function is an expression tree. + /// + [Serializable] + public class ExprLambdaGoesNode + : ExprNodeBase + , ExprEvaluator + , ExprDeclaredOrLambdaNode + { + private readonly IList _goesToNames; + + public ExprLambdaGoesNode(IList goesToNames) + { + this._goesToNames = goesToNames; + } + + public bool IsValidated + { + get { return true; } + } + + public IList GoesToNames + { + get { return _goesToNames; } + } + + public override ExprEvaluator ExprEvaluator + { + get { return this; } + } + + public override ExprNode Validate(ExprValidationContext validationContext) { + throw new UnsupportedOperationException(); + } + + public override bool IsConstantResult + { + get { return false; } + } + + public Type ReturnType + { + get { return null; } + } + + public object Evaluate(EvaluateParams evaluateParams) + { + throw new UnsupportedOperationException(); + } + + public Object Evaluate(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext exprEvaluatorContext) + { + throw new UnsupportedOperationException(); + } + + public override bool EqualsNode(ExprNode node) { + return false; + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) { + } + + public override ExprPrecedenceEnum Precedence + { + get { return ExprPrecedenceEnum.MINIMUM; } + } + } + + +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/dot/PropertyExprEvaluatorEventCollection.cs b/NEsper.Core/NEsper.Core/epl/enummethod/dot/PropertyExprEvaluatorEventCollection.cs new file mode 100755 index 000000000..c919aa256 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/dot/PropertyExprEvaluatorEventCollection.cs @@ -0,0 +1,118 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.enummethod.dot +{ + public class PropertyExprEvaluatorEventCollection + : ExprEvaluatorEnumeration + , ExprEvaluatorEnumerationGivenEvent + { + private readonly string _propertyNameCache; + private readonly int _streamId; + private readonly EventType _fragmentType; + private readonly EventPropertyGetter _getter; + private readonly bool _disablePropertyExpressionEventCollCache; + + public PropertyExprEvaluatorEventCollection( + string propertyNameCache, + int streamId, + EventType fragmentType, + EventPropertyGetter getter, + bool disablePropertyExpressionEventCollCache) + { + _propertyNameCache = propertyNameCache; + _streamId = streamId; + _fragmentType = fragmentType; + _getter = getter; + _disablePropertyExpressionEventCollCache = disablePropertyExpressionEventCollCache; + } + + public ICollection EvaluateGetROCollectionEvents(EvaluateParams evaluateParams) + { + var eventInQuestion = evaluateParams.EventsPerStream[_streamId]; + if (eventInQuestion == null) + { + return null; + } + return EvaluateInternal(eventInQuestion, evaluateParams.ExprEvaluatorContext); + } + + public ICollection EvaluateEventGetROCollectionEvents(EventBean @event, ExprEvaluatorContext context) + { + if (@event == null) + { + return null; + } + return EvaluateInternal(@event, context); + } + + private ICollection EvaluateInternal(EventBean eventInQuestion, ExprEvaluatorContext context) + { + if (_disablePropertyExpressionEventCollCache) + { + var eventsX = _getter.GetFragment(eventInQuestion).UnwrapIntoArray(); + return (ICollection) eventsX; + } + + var cache = context.ExpressionResultCacheService.AllocateUnwrapProp; + var cacheEntry = cache.GetPropertyColl(_propertyNameCache, eventInQuestion); + if (cacheEntry != null) + { + return cacheEntry.Result; + } + + var events = _getter.GetFragment(eventInQuestion).UnwrapIntoArray(); + var coll = (ICollection) events; + cache.SavePropertyColl(_propertyNameCache, eventInQuestion, coll); + return coll; + } + + public EventType GetEventTypeCollection(EventAdapterService eventAdapterService, int statementId) + { + return _fragmentType; + } + + public ICollection EvaluateGetROCollectionScalar(EvaluateParams evaluateParams) + { + return null; + } + + public Type ComponentTypeCollection + { + get { return null; } + } + + public EventType GetEventTypeSingle(EventAdapterService eventAdapterService, int statementId) + { + return null; + } + + public EventBean EvaluateGetEventBean(EvaluateParams evaluateParams) + { + return null; + } + + public ICollection EvaluateEventGetROCollectionScalar(EventBean @event, ExprEvaluatorContext context) + { + return null; + } + + public EventBean EvaluateEventGetEventBean(EventBean @event, ExprEvaluatorContext context) + { + return null; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/dot/PropertyExprEvaluatorEventSingle.cs b/NEsper.Core/NEsper.Core/epl/enummethod/dot/PropertyExprEvaluatorEventSingle.cs new file mode 100755 index 000000000..ac7301bc8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/dot/PropertyExprEvaluatorEventSingle.cs @@ -0,0 +1,79 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.enummethod.dot +{ + public class PropertyExprEvaluatorEventSingle + : ExprEvaluatorEnumeration + , ExprEvaluatorEnumerationGivenEvent + { + private readonly int _streamId; + private readonly EventType _fragmentType; + private readonly EventPropertyGetter _getter; + + public PropertyExprEvaluatorEventSingle(int streamId, EventType fragmentType, EventPropertyGetter getter) + { + _streamId = streamId; + _fragmentType = fragmentType; + _getter = getter; + } + + public EventBean EvaluateGetEventBean(EvaluateParams evaluateParams) { + EventBean eventInQuestion = evaluateParams.EventsPerStream[_streamId]; + if (eventInQuestion == null) { + return null; + } + return (EventBean) _getter.GetFragment(eventInQuestion); + } + + public EventBean EvaluateEventGetEventBean(EventBean @event, ExprEvaluatorContext context) { + if (@event == null) { + return null; + } + return (EventBean) _getter.GetFragment(@event); + } + + public EventType GetEventTypeCollection(EventAdapterService eventAdapterService, int statementId) { + return _fragmentType; + } + + public Type ComponentTypeCollection + { + get { return null; } + } + + public EventType GetEventTypeSingle(EventAdapterService eventAdapterService, int statementId) { + return null; + } + + public ICollection EvaluateEventGetROCollectionEvents(EventBean @event, ExprEvaluatorContext context) { + return null; + } + + public ICollection EvaluateEventGetROCollectionScalar(EventBean @event, ExprEvaluatorContext context) + { + return null; + } + + public ICollection EvaluateGetROCollectionEvents(EvaluateParams evaluateParams) { + return null; + } + + public ICollection EvaluateGetROCollectionScalar(EvaluateParams evaluateParams) + { + return null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/dot/PropertyExprEvaluatorNonLambda.cs b/NEsper.Core/NEsper.Core/epl/enummethod/dot/PropertyExprEvaluatorNonLambda.cs new file mode 100755 index 000000000..0d7eb8248 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/dot/PropertyExprEvaluatorNonLambda.cs @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.enummethod.dot +{ + [Serializable] + public class PropertyExprEvaluatorNonLambda : ExprEvaluator + { + private readonly int _streamId; + private readonly EventPropertyGetter _getter; + private readonly Type _returnType; + + public PropertyExprEvaluatorNonLambda(int streamId, EventPropertyGetter getter, Type returnType) + { + _streamId = streamId; + _getter = getter; + _returnType = returnType; + } + + public object Evaluate(EvaluateParams evaluateParams) + { + var eventInQuestion = evaluateParams.EventsPerStream[_streamId]; + if (eventInQuestion == null) { + return null; + } + return _getter.Get(eventInQuestion); + } + + public Type ReturnType + { + get { return _returnType; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/dot/PropertyExprEvaluatorNonLambdaFragment.cs b/NEsper.Core/NEsper.Core/epl/enummethod/dot/PropertyExprEvaluatorNonLambdaFragment.cs new file mode 100755 index 000000000..38826c032 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/dot/PropertyExprEvaluatorNonLambdaFragment.cs @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.enummethod.dot +{ + [Serializable] + public class PropertyExprEvaluatorNonLambdaFragment : ExprEvaluator + { + private readonly int _streamId; + private readonly EventPropertyGetter _getter; + private readonly Type _returnType; + + public PropertyExprEvaluatorNonLambdaFragment(int streamId, EventPropertyGetter getter, Type returnType) + { + _streamId = streamId; + _getter = getter; + _returnType = returnType; + } + + public object Evaluate(EvaluateParams evaluateParams) + { + var eventInQuestion = evaluateParams.EventsPerStream[_streamId]; + if (eventInQuestion == null) { + return null; + } + return _getter.GetFragment(eventInQuestion); + } + + public Type ReturnType + { + get { return _returnType; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/dot/PropertyExprEvaluatorNonLambdaIndexed.cs b/NEsper.Core/NEsper.Core/epl/enummethod/dot/PropertyExprEvaluatorNonLambdaIndexed.cs new file mode 100755 index 000000000..3ccc0bdeb --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/dot/PropertyExprEvaluatorNonLambdaIndexed.cs @@ -0,0 +1,49 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.enummethod.dot +{ + [Serializable] + public class PropertyExprEvaluatorNonLambdaIndexed : ExprEvaluator + { + private readonly int _streamId; + private readonly EventPropertyGetterIndexed _indexedGetter; + private readonly ExprEvaluator _paramEval; + private readonly Type _returnType; + + public PropertyExprEvaluatorNonLambdaIndexed(int streamId, EventPropertyGetterIndexed indexedGetter, ExprEvaluator paramEval, Type returnType) + { + _streamId = streamId; + _indexedGetter = indexedGetter; + _paramEval = paramEval; + _returnType = returnType; + } + + public object Evaluate(EvaluateParams evaluateParams) + { + var key = _paramEval.Evaluate(evaluateParams).AsInt(); + var eventInQuestion = evaluateParams.EventsPerStream[_streamId]; + if (eventInQuestion == null) { + return null; + } + return _indexedGetter.Get(eventInQuestion, key); + } + + public Type ReturnType + { + get { return _returnType; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/dot/PropertyExprEvaluatorNonLambdaMapped.cs b/NEsper.Core/NEsper.Core/epl/enummethod/dot/PropertyExprEvaluatorNonLambdaMapped.cs new file mode 100755 index 000000000..f4d23e1c6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/dot/PropertyExprEvaluatorNonLambdaMapped.cs @@ -0,0 +1,49 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.enummethod.dot +{ + [Serializable] + public class PropertyExprEvaluatorNonLambdaMapped : ExprEvaluator + { + private readonly int _streamId; + private readonly EventPropertyGetterMapped _mappedGetter; + private readonly ExprEvaluator _paramEval; + private readonly Type _returnType; + + public PropertyExprEvaluatorNonLambdaMapped( + int streamId, + EventPropertyGetterMapped mappedGetter, + ExprEvaluator paramEval, + Type returnType) + { + _streamId = streamId; + _mappedGetter = mappedGetter; + _paramEval = paramEval; + _returnType = returnType; + } + + public object Evaluate(EvaluateParams evaluateParams) + { + var key = (String) _paramEval.Evaluate(evaluateParams); + var eventInQuestion = evaluateParams.EventsPerStream[_streamId]; + return eventInQuestion == null ? null : _mappedGetter.Get(eventInQuestion, key); + } + + public Type ReturnType + { + get { return _returnType; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/dot/PropertyExprEvaluatorScalarArray.cs b/NEsper.Core/NEsper.Core/epl/enummethod/dot/PropertyExprEvaluatorScalarArray.cs new file mode 100755 index 000000000..925f349b6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/dot/PropertyExprEvaluatorScalarArray.cs @@ -0,0 +1,106 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.enummethod.dot +{ + public class PropertyExprEvaluatorScalarArray + : ExprEvaluatorEnumeration + , ExprEvaluatorEnumerationGivenEvent + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly string _propertyName; + private readonly int _streamId; + private readonly EventPropertyGetter _getter; + private readonly Type _componentType; + + public PropertyExprEvaluatorScalarArray(string propertyName, int streamId, EventPropertyGetter getter, Type componentType) + { + _propertyName = propertyName; + _streamId = streamId; + _getter = getter; + _componentType = componentType; + } + + public ICollection EvaluateGetROCollectionScalar(EvaluateParams evaluateParams) + { + EventBean eventInQuestion = evaluateParams.EventsPerStream[_streamId]; + return EvaluateEventGetROCollectionScalar(eventInQuestion, evaluateParams.ExprEvaluatorContext); + } + + public ICollection EvaluateEventGetROCollectionScalar(EventBean @event, ExprEvaluatorContext context) + { + if (@event == null) + { + return null; + } + return EvaluateGetInterval(@event); + } + + public Type ComponentTypeCollection + { + get { return _componentType; } + } + + public EventType GetEventTypeCollection(EventAdapterService eventAdapterService, int statementId) + { + return null; + } + + public EventType GetEventTypeSingle(EventAdapterService eventAdapterService, int statementId) + { + return null; + } + + public EventBean EvaluateGetEventBean(EvaluateParams evaluateParams) + { + return null; + } + + public ICollection EvaluateEventGetROCollectionEvents(EventBean @event, ExprEvaluatorContext context) + { + return null; + } + + public EventBean EvaluateEventGetEventBean(EventBean @event, ExprEvaluatorContext context) + { + return null; + } + + public ICollection EvaluateGetROCollectionEvents(EvaluateParams evaluateParams) + { + return null; + } + + private ICollection EvaluateGetInterval(EventBean @event) + { + var value = _getter.Get(@event); + if (value == null) + { + return null; + } + if (!(value.GetType().IsArray)) + { + Log.Warn("Expected array-type input from property '" + _propertyName + "' but received " + value.GetType()); + return null; + } + + return value.Unwrap(); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/dot/PropertyExprEvaluatorScalarCollection.cs b/NEsper.Core/NEsper.Core/epl/enummethod/dot/PropertyExprEvaluatorScalarCollection.cs new file mode 100755 index 000000000..8f51e8014 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/dot/PropertyExprEvaluatorScalarCollection.cs @@ -0,0 +1,102 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.enummethod.dot +{ + public class PropertyExprEvaluatorScalarCollection + : ExprEvaluatorEnumeration + , ExprEvaluatorEnumerationGivenEvent + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly string _propertyName; + private readonly int _streamId; + private readonly EventPropertyGetter _getter; + private readonly Type _componentType; + + public PropertyExprEvaluatorScalarCollection(string propertyName, int streamId, EventPropertyGetter getter, Type componentType) + { + _propertyName = propertyName; + _streamId = streamId; + _getter = getter; + _componentType = componentType; + } + + public ICollection EvaluateGetROCollectionEvents(EvaluateParams evaluateParams) + { + return null; + } + + public ICollection EvaluateGetROCollectionScalar(EvaluateParams evaluateParams) + { + return EvaluateInternal(evaluateParams.EventsPerStream[_streamId]); + } + + public ICollection EvaluateEventGetROCollectionEvents(EventBean @event, ExprEvaluatorContext context) + { + return EvaluateInternal(@event); + } + + private ICollection EvaluateInternal(EventBean @event) + { + var result = _getter.Get(@event); + if (result == null) + { + return null; + } + + if (!result.GetType().IsGenericCollection()) + { + Log.Warn("Expected collection-type input from property '" + _propertyName + "' but received " + result.GetType()); + return null; + } + + return result.Unwrap(); + } + + public Type ComponentTypeCollection + { + get { return _componentType; } + } + + public ICollection EvaluateEventGetROCollectionScalar(EventBean @event, ExprEvaluatorContext context) + { + return null; + } + + public EventBean EvaluateEventGetEventBean(EventBean @event, ExprEvaluatorContext context) + { + return null; + } + + public EventType GetEventTypeCollection(EventAdapterService eventAdapterService, int statementId) + { + return null; + } + + public EventType GetEventTypeSingle(EventAdapterService eventAdapterService, int statementId) + { + return null; + } + + public EventBean EvaluateGetEventBean(EvaluateParams evaluateParams) + { + return null; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/dot/PropertyExprEvaluatorScalarIterable.cs b/NEsper.Core/NEsper.Core/epl/enummethod/dot/PropertyExprEvaluatorScalarIterable.cs new file mode 100755 index 000000000..b076731ab --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/dot/PropertyExprEvaluatorScalarIterable.cs @@ -0,0 +1,112 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.enummethod.dot +{ + public class PropertyExprEvaluatorScalarIterable + : ExprEvaluatorEnumeration + , ExprEvaluatorEnumerationGivenEvent + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly String _propertyName; + private readonly int _streamId; + private readonly EventPropertyGetter _getter; + private readonly Type _componentType; + + public PropertyExprEvaluatorScalarIterable( + String propertyName, + int streamId, + EventPropertyGetter getter, + Type componentType) + { + _propertyName = propertyName; + _streamId = streamId; + _getter = getter; + _componentType = componentType; + } + + public ICollection EvaluateGetROCollectionScalar(EvaluateParams evaluateParams) + { + return EvaluateInternal(evaluateParams.EventsPerStream[_streamId]); + } + + public ICollection EvaluateEventGetROCollectionScalar(EventBean @event, ExprEvaluatorContext context) + { + return EvaluateInternal(@event); + } + + private ICollection EvaluateInternal(EventBean eventInQuestion) + { + var result = _getter.Get(eventInQuestion); + if (result == null) + { + return null; + } + + try + { + return result.Unwrap(true); + } + catch (ArgumentException e) + { + var resultType = result.GetType(); + Log.Warn("Expected iterable-type input from property '" + _propertyName + "' but received " + resultType.FullName); + throw; + //return null; + } + } + + public EventType GetEventTypeCollection(EventAdapterService eventAdapterService, int statementId) + { + return null; + } + + public Type ComponentTypeCollection + { + get { return _componentType; } + } + + public EventType GetEventTypeSingle(EventAdapterService eventAdapterService, int statementId) + { + return null; + } + + public EventBean EvaluateGetEventBean(EvaluateParams evaluateParams) + { + return null; + } + + public ICollection EvaluateEventGetROCollectionEvents(EventBean @event, ExprEvaluatorContext context) + { + return null; + } + + public EventBean EvaluateEventGetEventBean(EventBean @event, ExprEvaluatorContext context) + { + return null; + } + + public ICollection EvaluateGetROCollectionEvents(EvaluateParams evaluateParams) + { + return null; + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEval.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEval.cs new file mode 100755 index 000000000..b4a4b3533 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEval.cs @@ -0,0 +1,22 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public interface EnumEval + { + int StreamNumSize { get; } + object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalAggregateBase.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalAggregateBase.cs new file mode 100755 index 000000000..cd7c924ea --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalAggregateBase.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalAggregateBase + { + protected ExprEvaluator Initialization; + protected ExprEvaluator InnerExpression; + protected int StreamNumLambda; + protected ObjectArrayEventType ResultEventType; + + public EnumEvalAggregateBase(ExprEvaluator initialization, + ExprEvaluator innerExpression, int streamNumLambda, + ObjectArrayEventType resultEventType) + { + Initialization = initialization; + InnerExpression = innerExpression; + StreamNumLambda = streamNumLambda; + ResultEventType = resultEventType; + } + + public int StreamNumSize + { + get { return StreamNumLambda + 2; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalAggregateEvents.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalAggregateEvents.cs new file mode 100755 index 000000000..1d135d4bb --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalAggregateEvents.cs @@ -0,0 +1,52 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalAggregateEvents + : EnumEvalAggregateBase + , EnumEval + { + public EnumEvalAggregateEvents(ExprEvaluator initialization, ExprEvaluator innerExpression, int streamNumLambda, ObjectArrayEventType resultEventType) + : base(initialization, innerExpression, streamNumLambda, resultEventType) + { + } + + public Object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) + { + var initializationValue = Initialization.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + + if (target.IsEmpty()) + { + return initializationValue; + } + + var resultEvent = new ObjectArrayEventBean(new Object[1], ResultEventType); + + foreach (EventBean next in target) + { + resultEvent.Properties[0] = initializationValue; + eventsLambda[StreamNumLambda + 1] = next; + eventsLambda[StreamNumLambda] = resultEvent; + + initializationValue = InnerExpression.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + } + + return initializationValue; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalAggregateScalar.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalAggregateScalar.cs new file mode 100755 index 000000000..10b2c46c3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalAggregateScalar.cs @@ -0,0 +1,54 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalAggregateScalar : EnumEvalAggregateBase, EnumEval { + + private readonly ObjectArrayEventType _evalEventType; + + public EnumEvalAggregateScalar(ExprEvaluator initialization, ExprEvaluator innerExpression, int streamNumLambda, ObjectArrayEventType resultEventType, ObjectArrayEventType evalEventType) + : base(initialization, innerExpression, streamNumLambda, resultEventType) + { + _evalEventType = evalEventType; + } + + public Object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) + { + var initializationValue = Initialization.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + + if (target.IsEmpty()) { + return initializationValue; + } + + var resultEvent = new ObjectArrayEventBean(new Object[1], ResultEventType); + var evalEvent = new ObjectArrayEventBean(new Object[1], _evalEventType); + + foreach (Object next in target) { + + resultEvent.Properties[0] = initializationValue; + evalEvent.Properties[0] = next; + eventsLambda[StreamNumLambda] = resultEvent; + eventsLambda[StreamNumLambda + 1] = evalEvent; + + initializationValue = InnerExpression.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + } + + return initializationValue; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalAllOfEvents.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalAllOfEvents.cs new file mode 100755 index 000000000..b2a79417c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalAllOfEvents.cs @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalAllOfEvents + : EnumEvalBase + , EnumEval + { + public EnumEvalAllOfEvents(ExprEvaluator innerExpression, int streamCountIncoming) + : base(innerExpression, streamCountIncoming) + { + } + + public object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) + { + if (target.IsEmpty()) { + return true; + } + + foreach (EventBean next in target) { + eventsLambda[StreamNumLambda] = next; + + var result = InnerExpression.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + if (! result.AsBoolean() ) { + return false; + } + } + + return true; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalAllOfScalar.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalAllOfScalar.cs new file mode 100755 index 000000000..d59984a60 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalAllOfScalar.cs @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalAllOfScalar + : EnumEvalBaseScalar + , EnumEval + { + + public EnumEvalAllOfScalar(ExprEvaluator innerExpression, int streamCountIncoming, ObjectArrayEventType type) + : base(innerExpression, streamCountIncoming, type) + { + } + + public override Object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) + { + if (target.IsEmpty()) + { + return true; + } + + var evalEvent = new ObjectArrayEventBean(new Object[1], Type); + foreach (Object next in target) + { + evalEvent.Properties[0] = next; + eventsLambda[StreamNumLambda] = evalEvent; + + var pass = (bool?)InnerExpression.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + if (pass.HasValue && (!pass.Value)) + { + return false; + } + } + + return true; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalAnyOfEvents.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalAnyOfEvents.cs new file mode 100755 index 000000000..c92b27ab5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalAnyOfEvents.cs @@ -0,0 +1,47 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalAnyOfEvents + : EnumEvalBase + , EnumEval + { + public EnumEvalAnyOfEvents(ExprEvaluator innerExpression, int streamCountIncoming) + : base(innerExpression, streamCountIncoming) + { + } + + public object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) + { + if (target.IsEmpty()) { + return false; + } + + foreach (EventBean next in target) { + eventsLambda[StreamNumLambda] = next; + + Object pass = InnerExpression.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + if (pass.AsBoolean()) { + return true; + } + } + + return false; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalAnyOfScalar.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalAnyOfScalar.cs new file mode 100755 index 000000000..137132224 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalAnyOfScalar.cs @@ -0,0 +1,54 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalAnyOfScalar + : EnumEvalBaseScalar + , EnumEval + { + public EnumEvalAnyOfScalar(ExprEvaluator innerExpression, int streamCountIncoming, ObjectArrayEventType type) + : base(innerExpression, streamCountIncoming, type) + { + } + + public override Object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) + { + if (target.IsEmpty()) + { + return false; + } + + var evalEvent = new ObjectArrayEventBean(new Object[1], Type); + foreach (Object next in target) + { + + evalEvent.Properties[0] = next; + eventsLambda[StreamNumLambda] = evalEvent; + + var pass = InnerExpression.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)).AsBoolean(); + if (pass) + { + return true; + } + } + + return false; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalAverageDecimalEvents.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalAverageDecimalEvents.cs new file mode 100755 index 000000000..14a1936b3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalAverageDecimalEvents.cs @@ -0,0 +1,47 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.agg.aggregator; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalAverageDecimalEvents + : EnumEvalBase + , EnumEval + { + private readonly MathContext _optionalMathContext; + + public EnumEvalAverageDecimalEvents(ExprEvaluator innerExpression, int streamCountIncoming, MathContext optionalMathContext) + : base(innerExpression, streamCountIncoming) + { + _optionalMathContext = optionalMathContext; + } + + public object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) + { + var agg = new AggregatorAvgDecimal(_optionalMathContext); + foreach (EventBean next in target) { + eventsLambda[StreamNumLambda] = next; + + var num = InnerExpression.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + if (num == null) { + continue; + } + agg.Enter(num); + } + + return agg.Value; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalAverageDecimalScalar.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalAverageDecimalScalar.cs new file mode 100755 index 000000000..9d660816a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalAverageDecimalScalar.cs @@ -0,0 +1,47 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.agg.aggregator; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalAverageDecimalScalar + : EnumEvalBase + , EnumEval + { + private readonly MathContext _optionalMathContext; + + public EnumEvalAverageDecimalScalar(int streamCountIncoming, MathContext optionalMathContext) + : base(streamCountIncoming) + { + _optionalMathContext = optionalMathContext; + } + + public object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) { + var agg = new AggregatorAvgDecimal(_optionalMathContext); + + foreach (Object next in target) { + if (next == null) + { + continue; + } + agg.Enter(next); + } + + return agg.Value; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalAverageDecimalScalarLambda.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalAverageDecimalScalarLambda.cs new file mode 100755 index 000000000..0bdf8ac0a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalAverageDecimalScalarLambda.cs @@ -0,0 +1,57 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.agg.aggregator; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalAverageDecimalScalarLambda + : EnumEvalBase + , EnumEval + { + private readonly ObjectArrayEventType _resultEventType; + private readonly MathContext _optionalMathContext; + + public EnumEvalAverageDecimalScalarLambda(ExprEvaluator innerExpression, int streamCountIncoming, ObjectArrayEventType resultEventType, MathContext optionalMathContext) + : base(innerExpression, streamCountIncoming) + { + _resultEventType = resultEventType; + _optionalMathContext = optionalMathContext; + } + + public Object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) + { + var agg = new AggregatorAvgDecimal(_optionalMathContext); + var resultEvent = new ObjectArrayEventBean(new Object[1], _resultEventType); + + var values = target; + foreach (Object next in values) + { + resultEvent.Properties[0] = next; + eventsLambda[StreamNumLambda] = resultEvent; + + var num = InnerExpression.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + if (num == null) + { + continue; + } + agg.Enter(num); + } + + return agg.Value; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalAverageEvents.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalAverageEvents.cs new file mode 100755 index 000000000..f1e1938bd --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalAverageEvents.cs @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalAverageEvents + : EnumEvalBase + , EnumEval + { + public EnumEvalAverageEvents(ExprEvaluator innerExpression, int streamCountIncoming) + : base(innerExpression, streamCountIncoming) + { + } + + public object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) + { + double sum = 0d; + int count = 0; + + var beans = target; + foreach (EventBean next in target) { + eventsLambda[StreamNumLambda] = next; + + var num = InnerExpression.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + if (num == null) { + continue; + } + count++; + sum += num.AsDouble(); + } + + if (count == 0) { + return null; + } + return sum / count; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalAverageScalar.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalAverageScalar.cs new file mode 100755 index 000000000..ddca971e4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalAverageScalar.cs @@ -0,0 +1,49 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalAverageScalar + : EnumEvalBase + , EnumEval + { + public EnumEvalAverageScalar(int streamCountIncoming) + : base(streamCountIncoming) + { + } + + public object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) + { + double sum = 0d; + int count = 0; + + foreach (Object next in target) { + var num = next; + if (num == null) { + continue; + } + count++; + sum += num.AsDouble(); + } + + if (count == 0) { + return null; + } + return sum / count; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalAverageScalarLambda.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalAverageScalarLambda.cs new file mode 100755 index 000000000..9f6d2c13b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalAverageScalarLambda.cs @@ -0,0 +1,64 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalAverageScalarLambda + : EnumEvalBase + , EnumEval + { + private readonly ObjectArrayEventType _resultEventType; + + public EnumEvalAverageScalarLambda(ExprEvaluator innerExpression, + int streamCountIncoming, + ObjectArrayEventType resultEventType) + : base(innerExpression, streamCountIncoming) + { + _resultEventType = resultEventType; + } + + public Object EvaluateEnumMethod(EventBean[] eventsLambda, + ICollection target, + bool isNewData, + ExprEvaluatorContext context) + { + double sum = 0d; + int count = 0; + var resultEvent = new ObjectArrayEventBean(new Object[1], _resultEventType); + ICollection values = target; + foreach (object next in values) + { + resultEvent.Properties[0] = next; + eventsLambda[StreamNumLambda] = resultEvent; + + object num = InnerExpression.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + if (num == null) + { + continue; + } + count++; + sum += num.AsDouble(); + } + + if (count == 0) + { + return null; + } + return sum/count; + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalBase.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalBase.cs new file mode 100755 index 000000000..2a096b339 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalBase.cs @@ -0,0 +1,36 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalBase + { + protected int StreamNumLambda; + + public EnumEvalBase(ExprEvaluator innerExpression, int streamCountIncoming) + : this(streamCountIncoming) + { + InnerExpression = innerExpression; + } + + public EnumEvalBase(int streamCountIncoming) + { + StreamNumLambda = streamCountIncoming; + } + + public int StreamNumSize + { + get { return StreamNumLambda + 1; } + } + + protected internal ExprEvaluator InnerExpression; + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalBaseIndex.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalBaseIndex.cs new file mode 100755 index 000000000..ecbdf2ecc --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalBaseIndex.cs @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public abstract class EnumEvalBaseIndex : EnumEval + { + protected ExprEvaluator InnerExpression; + protected int StreamNumLambda; + protected ObjectArrayEventType IndexEventType; + + protected EnumEvalBaseIndex(ExprEvaluator innerExpression, int streamNumLambda, ObjectArrayEventType indexEventType) + { + InnerExpression = innerExpression; + StreamNumLambda = streamNumLambda; + IndexEventType = indexEventType; + } + + public int StreamNumSize + { + get { return StreamNumLambda + 2; } + } + + public abstract object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalBaseScalar.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalBaseScalar.cs new file mode 100755 index 000000000..a78b3e0b8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalBaseScalar.cs @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public abstract class EnumEvalBaseScalar + : EnumEvalBase + , EnumEval + { + protected readonly ObjectArrayEventType Type; + + protected EnumEvalBaseScalar(ExprEvaluator innerExpression, int streamCountIncoming, ObjectArrayEventType type) + : base(innerExpression, streamCountIncoming) + { + Type = type; + } + + public abstract object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalBaseScalarIndex.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalBaseScalarIndex.cs new file mode 100755 index 000000000..3c84e4cba --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalBaseScalarIndex.cs @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public abstract class EnumEvalBaseScalarIndex : EnumEval + { + protected readonly ExprEvaluator InnerExpression; + protected readonly int StreamNumLambda; + protected readonly ObjectArrayEventType EvalEventType; + protected readonly ObjectArrayEventType IndexEventType; + + protected EnumEvalBaseScalarIndex(ExprEvaluator innerExpression, int streamNumLambda, ObjectArrayEventType evalEventType, ObjectArrayEventType indexEventType) + { + InnerExpression = innerExpression; + StreamNumLambda = streamNumLambda; + EvalEventType = evalEventType; + IndexEventType = indexEventType; + } + + public int StreamNumSize + { + get { return StreamNumLambda + 2; } + } + + public abstract object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalCountOf.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalCountOf.cs new file mode 100755 index 000000000..ba93f83b2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalCountOf.cs @@ -0,0 +1,31 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalCountOf : EnumEval + { + public EnumEvalCountOf(int numStreams) + { + StreamNumSize = numStreams; + } + + public int StreamNumSize { get; private set; } + + public object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) + { + return target.Count; + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalCountOfSelectorEvents.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalCountOfSelectorEvents.cs new file mode 100755 index 000000000..967e58646 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalCountOfSelectorEvents.cs @@ -0,0 +1,47 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalCountOfSelectorEvents + : EnumEvalBase + , EnumEval + { + public EnumEvalCountOfSelectorEvents(ExprEvaluator innerExpression, int streamCountIncoming) + : base(innerExpression, streamCountIncoming) + { + } + + public object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) + { + int count = 0; + + var evaluateParams = new EvaluateParams(eventsLambda, isNewData, context); + foreach (EventBean next in target) + { + eventsLambda[StreamNumLambda] = next; + + var pass = (bool?)InnerExpression.Evaluate(evaluateParams); + if (!pass.GetValueOrDefault(false)) { + continue; + } + count++; + } + + return count; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalCountOfSelectorScalar.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalCountOfSelectorScalar.cs new file mode 100755 index 000000000..3eec9ce51 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalCountOfSelectorScalar.cs @@ -0,0 +1,49 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalCountOfSelectorScalar + : EnumEvalBaseScalar + , EnumEval + { + public EnumEvalCountOfSelectorScalar(ExprEvaluator innerExpression, int streamCountIncoming, ObjectArrayEventType type) + : base(innerExpression, streamCountIncoming, type) + { + } + + public override Object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) + { + int count = 0; + + var evalEvent = new ObjectArrayEventBean(new Object[1], Type); + foreach (Object next in target) + { + evalEvent.Properties[0] = next; + eventsLambda[StreamNumLambda] = evalEvent; + + var pass = (bool?)InnerExpression.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + if (!pass.GetValueOrDefault(false)) + { + continue; + } + count++; + } + + return count; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalDistinctEvents.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalDistinctEvents.cs new file mode 100755 index 000000000..856f79120 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalDistinctEvents.cs @@ -0,0 +1,56 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalDistinctEvents + : EnumEvalBase + , EnumEval + { + public EnumEvalDistinctEvents(ExprEvaluator innerExpression, int streamCountIncoming) + : base(innerExpression, streamCountIncoming) + { + } + + public Object EvaluateEnumMethod( + EventBean[] eventsLambda, + ICollection target, + bool isNewData, + ExprEvaluatorContext context) + { + var beans = (ICollection) target; + if (beans.IsEmpty() || beans.Count == 1) + { + return beans; + } + + var evaluateParams = new EvaluateParams(eventsLambda, isNewData, context); + var distinct = new LinkedHashMap(); + foreach (var next in beans) + { + eventsLambda[StreamNumLambda] = next; + + var comparable = (IComparable) InnerExpression.Evaluate(evaluateParams); + if (!distinct.ContainsKey(comparable)) + { + distinct.Put(comparable, next); + } + } + + return distinct.Values; + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalDistinctScalar.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalDistinctScalar.cs new file mode 100755 index 000000000..392dcf629 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalDistinctScalar.cs @@ -0,0 +1,52 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalDistinctScalar + : EnumEvalBase + , EnumEval + { + public EnumEvalDistinctScalar(int streamCountIncoming) + : base(streamCountIncoming) + { + } + + public Object EvaluateEnumMethod( + EventBean[] eventsLambda, + ICollection target, + bool isNewData, + ExprEvaluatorContext context) + { + if (target == null || target.Count < 2) + { + return target; + } + + if (target is ISet) + { + return target; + } + + var set = new LinkedHashSet(); + foreach (Object entry in target) + { + set.Add(entry); + } + return set; + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalDistinctScalarLambda.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalDistinctScalarLambda.cs new file mode 100755 index 000000000..4c5e5ff2b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalDistinctScalarLambda.cs @@ -0,0 +1,64 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalDistinctScalarLambda + : EnumEvalBase + , EnumEval + { + private readonly ObjectArrayEventType _resultEventType; + + public EnumEvalDistinctScalarLambda( + ExprEvaluator innerExpression, + int streamCountIncoming, + ObjectArrayEventType resultEventType) + : base(innerExpression, streamCountIncoming) + { + _resultEventType = resultEventType; + } + + public Object EvaluateEnumMethod( + EventBean[] eventsLambda, + ICollection target, + bool isNewData, + ExprEvaluatorContext context) + { + if (target == null || target.Count < 2) + { + return target; + } + + var evaluateParams = new EvaluateParams(eventsLambda, isNewData, context); + var set = new LinkedHashMap(); + var resultEvent = new ObjectArrayEventBean(new Object[1], _resultEventType); + var values = target; + foreach (Object next in values) + { + resultEvent.Properties[0] = next; + eventsLambda[StreamNumLambda] = resultEvent; + + var comparable = (IComparable) InnerExpression.Evaluate(evaluateParams); + if (!set.ContainsKey(comparable)) + { + set.Put(comparable, next); + } + } + return set.Values; + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalExcept.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalExcept.cs new file mode 100755 index 000000000..f3c4f94e4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalExcept.cs @@ -0,0 +1,109 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalExcept : EnumEval + { + private readonly int _numStreams; + private readonly ExprEvaluatorEnumeration _evaluator; + private readonly bool _scalar; + + public EnumEvalExcept(int numStreams, ExprEvaluatorEnumeration evaluator, bool scalar) + { + _numStreams = numStreams; + _evaluator = evaluator; + _scalar = scalar; + } + + public int StreamNumSize + { + get { return _numStreams; } + } + + public Object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) + { + if (target == null) + { + return null; + } + + ICollection set; + if (_scalar) + { + set = _evaluator.EvaluateGetROCollectionScalar(new EvaluateParams(eventsLambda, isNewData, context)); + } + else + { + set = _evaluator + .EvaluateGetROCollectionEvents(new EvaluateParams(eventsLambda, isNewData, context)) + .TransformInto(o => (EventBean) o, e => (Object) e); + } + + if (set == null || set.IsEmpty() || target.IsEmpty()) + { + return target; + } + + if (_scalar) + { + var resultX = new List(target); + resultX.RemoveAll(set); + return resultX; + } + + var targetEvents = target.OfType(); + var sourceEvents = set.OfType(); + var result = new List(); + + // we compare event underlying + foreach (EventBean targetEvent in targetEvents) + { + if (targetEvent == null) + { + result.Add(null); + continue; + } + + bool found = false; + foreach (EventBean sourceEvent in sourceEvents) + { + if (targetEvent == sourceEvent) + { + found = true; + break; + } + if (sourceEvent == null) + { + continue; + } + if (targetEvent.Underlying.Equals(sourceEvent.Underlying)) + { + found = true; + break; + } + } + + if (!found) + { + result.Add(targetEvent); + } + } + return result; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalFirstOfNoPredicate.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalFirstOfNoPredicate.cs new file mode 100755 index 000000000..cfc351618 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalFirstOfNoPredicate.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalFirstOfNoPredicate + : EnumEvalBase + , EnumEval + { + public EnumEvalFirstOfNoPredicate(int streamCountIncoming) + : base(streamCountIncoming) + { + } + + public object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) + { + if (target == null || target.IsEmpty()) + { + return null; + } + return target.FirstOrDefault(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalFirstOfPredicateEvents.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalFirstOfPredicateEvents.cs new file mode 100755 index 000000000..8fd00e6cc --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalFirstOfPredicateEvents.cs @@ -0,0 +1,44 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalFirstOfPredicateEvents + : EnumEvalBase + , EnumEval + { + public EnumEvalFirstOfPredicateEvents(ExprEvaluator innerExpression, int streamCountIncoming) + : base(innerExpression, streamCountIncoming) + { + } + + public object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) + { + foreach (EventBean next in target) { + eventsLambda[StreamNumLambda] = next; + + Object pass = InnerExpression.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + if (!pass.AsBoolean()) { + continue; + } + + return next; + } + + return null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalFirstOfPredicateScalar.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalFirstOfPredicateScalar.cs new file mode 100755 index 000000000..726a80467 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalFirstOfPredicateScalar.cs @@ -0,0 +1,49 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalFirstOfPredicateScalar + : EnumEvalBaseScalar + , EnumEval + { + public EnumEvalFirstOfPredicateScalar(ExprEvaluator innerExpression, int streamCountIncoming, ObjectArrayEventType type) + : base(innerExpression, streamCountIncoming, type) + { + } + + public override object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) + { + var evalEvent = new ObjectArrayEventBean(new Object[1], Type); + foreach (var next in target) + { + evalEvent.Properties[0] = next; + eventsLambda[StreamNumLambda] = evalEvent; + + Object pass = InnerExpression.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + if (!pass.AsBoolean()) + { + continue; + } + + return next; + } + + return null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalGroupByKeySelectorEvents.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalGroupByKeySelectorEvents.cs new file mode 100755 index 000000000..1e6c8d319 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalGroupByKeySelectorEvents.cs @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalGroupByKeySelectorEvents + : EnumEvalBase + , EnumEval + { + public EnumEvalGroupByKeySelectorEvents(ExprEvaluator innerExpression, int streamCountIncoming) + : base(innerExpression, streamCountIncoming) + { + } + + public Object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) + { + var result = new LinkedHashMap>(); + foreach (EventBean next in target) + { + eventsLambda[StreamNumLambda] = next; + + var key = InnerExpression.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + var value = result.Get(key); + if (value == null) + { + value = new List(); + result.Put(key, value); + } + value.Add(next.Underlying); + } + + return result; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalGroupByKeySelectorScalarLambda.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalGroupByKeySelectorScalarLambda.cs new file mode 100755 index 000000000..83f675e2b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalGroupByKeySelectorScalarLambda.cs @@ -0,0 +1,55 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalGroupByKeySelectorScalarLambda + : EnumEvalBase + , EnumEval + { + private readonly ObjectArrayEventType _resultEventType; + + public EnumEvalGroupByKeySelectorScalarLambda(ExprEvaluator innerExpression, int streamCountIncoming, ObjectArrayEventType resultEventType) + : base(innerExpression, streamCountIncoming) + { + _resultEventType = resultEventType; + } + + public Object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) + { + var result = new LinkedHashMap>(); + var values = (ICollection)target; + var resultEvent = new ObjectArrayEventBean(new Object[1], _resultEventType); + + foreach (Object next in values) { + + resultEvent.Properties[0] = next; + eventsLambda[StreamNumLambda] = resultEvent; + + var key = InnerExpression.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + var value = result.Get(key); + if (value == null) { + value = new List(); + result.Put(key, value); + } + value.Add(next); + } + + return result; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalGroupByKeyValueSelectorEvents.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalGroupByKeyValueSelectorEvents.cs new file mode 100755 index 000000000..ea49ba8d4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalGroupByKeyValueSelectorEvents.cs @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalGroupByKeyValueSelectorEvents + : EnumEvalBase + , EnumEval + { + private readonly ExprEvaluator _secondExpression; + + public EnumEvalGroupByKeyValueSelectorEvents(ExprEvaluator innerExpression, int streamCountIncoming, ExprEvaluator secondExpression) + : base(innerExpression, streamCountIncoming) + { + _secondExpression = secondExpression; + } + + public Object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) + { + var result = new LinkedHashMap>(); + foreach (EventBean next in target) + { + eventsLambda[StreamNumLambda] = next; + + var key = InnerExpression.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + var entry = _secondExpression.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + + var value = result.Get(key); + if (value == null) + { + value = new List(); + result.Put(key, value); + } + value.Add(entry); + } + + return result; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalGroupByKeyValueSelectorScalarLambda.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalGroupByKeyValueSelectorScalarLambda.cs new file mode 100755 index 000000000..42d1df573 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalGroupByKeyValueSelectorScalarLambda.cs @@ -0,0 +1,59 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalGroupByKeyValueSelectorScalarLambda + : EnumEvalBase + , EnumEval + { + private readonly ExprEvaluator _secondExpression; + private readonly ObjectArrayEventType _resultEventType; + + public EnumEvalGroupByKeyValueSelectorScalarLambda(ExprEvaluator innerExpression, int streamCountIncoming, ExprEvaluator secondExpression, ObjectArrayEventType resultEventType) + : base(innerExpression, streamCountIncoming) + { + _secondExpression = secondExpression; + _resultEventType = resultEventType; + } + + public Object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) { + var result = new LinkedHashMap>(); + + var resultEvent = new ObjectArrayEventBean(new Object[1], _resultEventType); + var values = target; + foreach (Object next in values) { + + resultEvent.Properties[0] = next; + eventsLambda[StreamNumLambda] = resultEvent; + var key = InnerExpression.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + + resultEvent.Properties[0] = next; + var entry = _secondExpression.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + + var value = result.Get(key); + if (value == null) { + value = new List(); + result.Put(key, value); + } + value.Add(entry); + } + + return result; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalIntersect.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalIntersect.cs new file mode 100755 index 000000000..22ef836e0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalIntersect.cs @@ -0,0 +1,109 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +///////////////////////////////////////////////////////////////////////////////////////w + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalIntersect : EnumEval + { + private readonly int _numStreams; + private readonly ExprEvaluatorEnumeration _evaluator; + private readonly bool _scalar; + + public EnumEvalIntersect(int numStreams, ExprEvaluatorEnumeration evaluator, bool scalar) + { + _numStreams = numStreams; + _evaluator = evaluator; + _scalar = scalar; + } + + public int StreamNumSize + { + get { return _numStreams; } + } + + public Object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) + { + if (target == null) + { + return null; + } + + ICollection set; + if (_scalar) + { + set = _evaluator.EvaluateGetROCollectionScalar(new EvaluateParams(eventsLambda, isNewData, context)); + } + else + { + set = _evaluator + .EvaluateGetROCollectionEvents(new EvaluateParams(eventsLambda, isNewData, context)) + .TransformInto(o => (EventBean) o, e => (Object) e); + } + + if (set == null || set.IsEmpty() || target.IsEmpty()) + { + return target; + } + + if (_scalar) + { + var resultX = new HashSet(target); + resultX.IntersectWith(set); + return resultX.ToList(); + } + + var targetEvents = target.OfType(); + var sourceEvents = set.OfType(); + var result = new List(); + + // we compare event underlying + foreach (EventBean targetEvent in targetEvents) + { + if (targetEvent == null) + { + result.Add(null); + continue; + } + + bool found = false; + foreach (EventBean sourceEvent in sourceEvents) + { + if (targetEvent == sourceEvent) + { + found = true; + break; + } + if (sourceEvent == null) + { + continue; + } + if (targetEvent.Underlying.Equals(sourceEvent.Underlying)) + { + found = true; + break; + } + } + + if (found) + { + result.Add(targetEvent); + } + } + return result; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalLastOfNoPredicate.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalLastOfNoPredicate.cs new file mode 100755 index 000000000..ac285d741 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalLastOfNoPredicate.cs @@ -0,0 +1,36 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalLastOfNoPredicate + : EnumEvalBase + , EnumEval + { + public EnumEvalLastOfNoPredicate(int streamCountIncoming) + : base(streamCountIncoming) + { + } + + public object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) + { + Object result = null; + foreach (var next in target) { + result = next; + } + return result; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalLastOfPredicateEvents.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalLastOfPredicateEvents.cs new file mode 100755 index 000000000..15c556afa --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalLastOfPredicateEvents.cs @@ -0,0 +1,47 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalLastOfPredicateEvents + : EnumEvalBase + , EnumEval + { + public EnumEvalLastOfPredicateEvents(ExprEvaluator innerExpression, int streamCountIncoming) + : base(innerExpression, streamCountIncoming) + { + } + + public object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) + { + Object result = null; + foreach (EventBean next in target) + { + eventsLambda[StreamNumLambda] = next; + + Object pass = InnerExpression.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + if (!pass.AsBoolean()) + { + continue; + } + + result = next; + } + + return result; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalLastOfPredicateScalar.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalLastOfPredicateScalar.cs new file mode 100755 index 000000000..bb1af03dc --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalLastOfPredicateScalar.cs @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalLastOfPredicateScalar + : EnumEvalBaseScalar + , EnumEval + { + public EnumEvalLastOfPredicateScalar(ExprEvaluator innerExpression, int streamCountIncoming, ObjectArrayEventType type) + : base(innerExpression, streamCountIncoming, type) + { + } + + public override object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) + { + Object result = null; + ObjectArrayEventBean evalEvent = new ObjectArrayEventBean(new Object[1], Type); + + foreach (var next in target) + { + evalEvent.Properties[0] = next; + eventsLambda[StreamNumLambda] = evalEvent; + + Object pass = InnerExpression.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + if (!pass.AsBoolean()) + { + continue; + } + + result = next; + } + + return result; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalMinMaxByEvents.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalMinMaxByEvents.cs new file mode 100755 index 000000000..27d6082f7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalMinMaxByEvents.cs @@ -0,0 +1,65 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalMinMaxByEvents + : EnumEvalBase + , EnumEval + { + private readonly bool _max; + + public EnumEvalMinMaxByEvents(ExprEvaluator innerExpression, int streamCountIncoming, bool max) + : base(innerExpression, streamCountIncoming) + { + _max = max; + } + + public Object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) { + IComparable minKey = null; + EventBean result = null; + + foreach (EventBean next in target) { + eventsLambda[StreamNumLambda] = next; + + Object comparable = InnerExpression.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + if (comparable == null) { + continue; + } + + if (minKey == null) { + minKey = (IComparable)comparable; + result = next; + } + else { + if (_max) { + if (minKey.CompareTo(comparable) < 0) { + minKey = (IComparable)comparable; + result = next; + } + } + else { + if (minKey.CompareTo(comparable) > 0) { + minKey = (IComparable)comparable; + result = next; + } + } + } + } + + return result; // unpack of EventBean to underlying performed at another stage + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalMinMaxByScalarLambda.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalMinMaxByScalarLambda.cs new file mode 100755 index 000000000..f2f51fa72 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalMinMaxByScalarLambda.cs @@ -0,0 +1,71 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalMinMaxByScalarLambda + : EnumEvalBase + , EnumEval + { + private readonly bool _max; + private readonly ObjectArrayEventType _resultEventType; + + public EnumEvalMinMaxByScalarLambda(ExprEvaluator innerExpression, int streamCountIncoming, bool max, ObjectArrayEventType resultEventType) + : base(innerExpression, streamCountIncoming) + { + _max = max; + _resultEventType = resultEventType; + } + + public Object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) { + IComparable minKey = null; + Object result = null; + ObjectArrayEventBean resultEvent = new ObjectArrayEventBean(new Object[1], _resultEventType); + ICollection values = (ICollection) target; + foreach (Object next in values) { + + resultEvent.Properties[0] = next; + eventsLambda[StreamNumLambda] = resultEvent; + + Object comparable = InnerExpression.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + if (comparable == null) { + continue; + } + + if (minKey == null) { + minKey = (IComparable)comparable; + result = next; + } + else { + if (_max) { + if (minKey.CompareTo(comparable) < 0) { + minKey = (IComparable)comparable; + result = next; + } + } + else { + if (minKey.CompareTo(comparable) > 0) { + minKey = (IComparable)comparable; + result = next; + } + } + } + } + + return result; // unpack of EventBean to underlying performed at another stage + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalMinMaxEvents.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalMinMaxEvents.cs new file mode 100755 index 000000000..3ad3c0be9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalMinMaxEvents.cs @@ -0,0 +1,71 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalMinMaxEvents + : EnumEvalBase + , EnumEval + { + private readonly bool _max; + + public EnumEvalMinMaxEvents(ExprEvaluator innerExpression, int streamCountIncoming, bool max) + : base(innerExpression, streamCountIncoming) + { + _max = max; + } + + public Object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) + { + IComparable minKey = null; + + var beans = target; + foreach (EventBean next in beans) + { + eventsLambda[StreamNumLambda] = next; + + Object comparable = InnerExpression.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + if (comparable == null) + { + continue; + } + + if (minKey == null) + { + minKey = (IComparable)comparable; + } + else + { + if (_max) + { + if (minKey.CompareTo(comparable) < 0) + { + minKey = (IComparable)comparable; + } + } + else + { + if (minKey.CompareTo(comparable) > 0) + { + minKey = (IComparable)comparable; + } + } + } + } + + return minKey; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalMinMaxScalar.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalMinMaxScalar.cs new file mode 100755 index 000000000..d9fab1b36 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalMinMaxScalar.cs @@ -0,0 +1,68 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalMinMaxScalar + : EnumEvalBase + , EnumEval + { + private readonly bool _max; + + public EnumEvalMinMaxScalar(int streamCountIncoming, bool max) + : base(streamCountIncoming) + { + _max = max; + } + + public Object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) + { + IComparable minKey = null; + + foreach (Object next in target) + { + var comparable = next; + if (comparable == null) + { + continue; + } + + if (minKey == null) + { + minKey = (IComparable)comparable; + } + else + { + if (_max) + { + if (minKey.CompareTo(comparable) < 0) + { + minKey = (IComparable)comparable; + } + } + else + { + if (minKey.CompareTo(comparable) > 0) + { + minKey = (IComparable)comparable; + } + } + } + } + + return minKey; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalMinMaxScalarLambda.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalMinMaxScalarLambda.cs new file mode 100755 index 000000000..a989b802b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalMinMaxScalarLambda.cs @@ -0,0 +1,77 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalMinMaxScalarLambda + : EnumEvalBase + , EnumEval + { + private readonly bool _max; + private readonly ObjectArrayEventType _resultEventType; + + public EnumEvalMinMaxScalarLambda(ExprEvaluator innerExpression, int streamCountIncoming, bool max, ObjectArrayEventType resultEventType) + : base(innerExpression, streamCountIncoming) + { + _max = max; + _resultEventType = resultEventType; + } + + public Object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) + { + IComparable minKey = null; + + var resultEvent = new ObjectArrayEventBean(new Object[1], _resultEventType); + var coll = (ICollection)target; + foreach (Object next in coll) + { + + resultEvent.Properties[0] = next; + eventsLambda[StreamNumLambda] = resultEvent; + + Object comparable = InnerExpression.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + if (comparable == null) + { + continue; + } + + if (minKey == null) + { + minKey = (IComparable)comparable; + } + else + { + if (_max) + { + if (minKey.CompareTo(comparable) < 0) + { + minKey = (IComparable)comparable; + } + } + else + { + if (minKey.CompareTo(comparable) > 0) + { + minKey = (IComparable)comparable; + } + } + } + } + + return minKey; + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalMostLeastFrequentEvent.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalMostLeastFrequentEvent.cs new file mode 100755 index 000000000..cf099dd56 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalMostLeastFrequentEvent.cs @@ -0,0 +1,88 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalMostLeastFrequentEvent + : EnumEvalBase + , EnumEval + { + private readonly bool _isMostFrequent; + + public EnumEvalMostLeastFrequentEvent(ExprEvaluator innerExpression, int streamCountIncoming, bool mostFrequent) + : base(innerExpression, streamCountIncoming) + { + _isMostFrequent = mostFrequent; + } + + public object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) + { + + var items = new LinkedHashMap(); + + foreach (EventBean next in target) + { + eventsLambda[StreamNumLambda] = next; + + Object item = InnerExpression.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + int? existing = items.Get(item); + if (!existing.HasValue) + { + existing = 1; + } + else + { + existing++; + } + items[item] = existing; + } + + return GetResult(items, _isMostFrequent); + } + + internal static Object GetResult(IDictionary items, bool mostFrequent) + { + if (mostFrequent) + { + Object maxKey = null; + int max = Int32.MinValue; + foreach (var entry in items) + { + if (entry.Value > max) + { + maxKey = entry.Key; + max = entry.Value.Value; + } + } + return maxKey; + } + + int min = Int32.MaxValue; + Object minKey = null; + foreach (var entry in items) + { + if (entry.Value < min) + { + minKey = entry.Key; + min = entry.Value.Value; + } + } + return minKey; + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalMostLeastFrequentScalar.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalMostLeastFrequentScalar.cs new file mode 100755 index 000000000..76fdf6774 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalMostLeastFrequentScalar.cs @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalMostLeastFrequentScalar + : EnumEvalBase + , EnumEval + { + private readonly bool _isMostFrequent; + + public EnumEvalMostLeastFrequentScalar(int streamCountIncoming, bool isMostFrequent) + : base(streamCountIncoming) + { + _isMostFrequent = isMostFrequent; + } + + public object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) + { + IDictionary items = new LinkedHashMap(); + + foreach (Object next in target) { + int? existing = items.Get(next); + if (existing == null) { + existing = 1; + } + else { + existing++; + } + items.Put(next, existing); + } + + return EnumEvalMostLeastFrequentEvent.GetResult(items, _isMostFrequent); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalMostLeastFrequentScalarLamda.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalMostLeastFrequentScalarLamda.cs new file mode 100755 index 000000000..d64126a29 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalMostLeastFrequentScalarLamda.cs @@ -0,0 +1,65 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalMostLeastFrequentScalarLamda + : EnumEvalBase + , EnumEval + { + private readonly bool _isMostFrequent; + private readonly ObjectArrayEventType _resultEventType; + + public EnumEvalMostLeastFrequentScalarLamda(ExprEvaluator innerExpression, int streamCountIncoming, bool mostFrequent, ObjectArrayEventType resultEventType) + : base(innerExpression, streamCountIncoming) + { + _isMostFrequent = mostFrequent; + _resultEventType = resultEventType; + } + + public Object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) + { + + var items = new LinkedHashMap(); + var values = target; + + var resultEvent = new ObjectArrayEventBean(new Object[1], _resultEventType); + + foreach (Object next in values) + { + + resultEvent.Properties[0] = next; + eventsLambda[StreamNumLambda] = resultEvent; + + var item = InnerExpression.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + + int? existing; + if (!items.TryGetValue(item, out existing)) + { + existing = 1; + } + else + { + existing++; + } + items.Put(item, existing); + } + + return EnumEvalMostLeastFrequentEvent.GetResult(items, _isMostFrequent); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalNoOp.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalNoOp.cs new file mode 100755 index 000000000..95de917c7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalNoOp.cs @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalNoOp + : EnumEval + { + public EnumEvalNoOp(int numEvents) { + } + + public int StreamNumSize + { + get { return 0; } + } + + public object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) { + return target; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalOrderByAscDescEvents.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalOrderByAscDescEvents.cs new file mode 100755 index 000000000..24f040264 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalOrderByAscDescEvents.cs @@ -0,0 +1,97 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalOrderByAscDescEvents + : EnumEvalBase + , EnumEval + { + private readonly bool _descending; + + public EnumEvalOrderByAscDescEvents(ExprEvaluator innerExpression, int streamCountIncoming, bool descending) + : base(innerExpression, streamCountIncoming) + { + this._descending = descending; + } + + public object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) + { + var sort = new OrderedDictionary(); + var hasColl = false; + + foreach (EventBean next in target) + { + eventsLambda[StreamNumLambda] = next; + + var comparable = (IComparable)InnerExpression.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + var entry = sort.Get(comparable); + + if (entry == null) + { + sort.Put(comparable, next); + continue; + } + + if (entry is LinkedList) + { + ((LinkedList)entry).AddLast(next); + continue; + } + + var linkedList = new LinkedList(); + linkedList.AddLast(entry); + linkedList.AddLast(next); + sort.Put(comparable, linkedList); + hasColl = true; + } + + IDictionary sorted; + if (_descending) + { + sorted = sort.Invert(); + } + else + { + sorted = sort; + } + + if (!hasColl) + { + return sorted.Values; + } + + var coll = new LinkedList(); + foreach (var entry in sorted) + { + if (entry.Value is LinkedList) + { + foreach (var value in (LinkedList)entry.Value) + { + coll.AddLast(value); + } + } + else + { + coll.AddLast(entry.Value); + } + } + return coll; + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalOrderByAscDescScalar.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalOrderByAscDescScalar.cs new file mode 100755 index 000000000..f1ad83892 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalOrderByAscDescScalar.cs @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalOrderByAscDescScalar + : EnumEvalBase + , EnumEval + { + private readonly bool _descending; + + public EnumEvalOrderByAscDescScalar(int streamCountIncoming, bool descending) + : base(streamCountIncoming) + { + _descending = descending; + } + + public object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) + { + if (target == null || target.IsEmpty()) + { + return target; + } + + var list = new List(target); + if (_descending) + { + list.Sort(); + list.Reverse(); + //Collections.Sort(list, Collections.ReverseOrder()); + } + else + { + list.Sort(); + } + return list; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalOrderByAscDescScalarLambda.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalOrderByAscDescScalarLambda.cs new file mode 100755 index 000000000..60353e97a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalOrderByAscDescScalarLambda.cs @@ -0,0 +1,98 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalOrderByAscDescScalarLambda + : EnumEvalBase + , EnumEval + { + private readonly bool _descending; + private readonly ObjectArrayEventType _resultEventType; + + public EnumEvalOrderByAscDescScalarLambda(ExprEvaluator innerExpression, int streamCountIncoming, bool descending, ObjectArrayEventType resultEventType) + : base(innerExpression, streamCountIncoming) + { + _descending = descending; + _resultEventType = resultEventType; + } + + public Object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) + { + var sort = new OrderedDictionary(); + var hasColl = false; + + var resultEvent = new ObjectArrayEventBean(new Object[1], _resultEventType); + var values = (ICollection)target; + foreach (Object next in values) + { + resultEvent.Properties[0] = next; + eventsLambda[StreamNumLambda] = resultEvent; + + var comparable = (IComparable)InnerExpression.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + var entry = sort.Get(comparable); + + if (entry == null) + { + sort.Put(comparable, next); + continue; + } + + if (entry is ICollection) + { + ((ICollection)entry).Add(next); + continue; + } + + var mcoll = new LinkedList(); + mcoll.AddLast(entry); + mcoll.AddLast(next); + sort.Put(comparable, mcoll); + hasColl = true; + } + + IDictionary sorted; + if (_descending) + { + sorted = sort.Invert(); + } + else + { + sorted = sort; + } + + if (!hasColl) + { + return sorted.Values; + } + + var coll = new LinkedList(); + foreach (var entry in sorted) + { + if (entry.Value is ICollection) + { + coll.AddAll((ICollection)entry.Value); + } + else + { + coll.AddLast(entry.Value); + } + } + return coll; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalReverse.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalReverse.cs new file mode 100755 index 000000000..7a50ac3d8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalReverse.cs @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalReverse + : EnumEval + { + private readonly int _numStreams; + + public EnumEvalReverse(int numStreams) + { + _numStreams = numStreams; + } + + public int StreamNumSize + { + get { return _numStreams; } + } + + public object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) + { + if (target.IsEmpty()) + { + return target; + } + + var result = new List(target); + result.Reverse(); + return result; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalSelectFromEvents.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalSelectFromEvents.cs new file mode 100755 index 000000000..5002d78d1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalSelectFromEvents.cs @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalSelectFromEvents + : EnumEvalBase + , EnumEval + { + public EnumEvalSelectFromEvents(ExprEvaluator innerExpression, int streamCountIncoming) + : base(innerExpression, streamCountIncoming) + { + } + + public Object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) + { + if (target.IsEmpty()) + { + return target; + } + + var queue = new LinkedList(); + foreach (EventBean next in target) + { + eventsLambda[StreamNumLambda] = next; + + var item = InnerExpression.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + if (item != null) + { + queue.AddLast(item); + } + } + + return queue; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalSelectFromScalarLambda.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalSelectFromScalarLambda.cs new file mode 100755 index 000000000..081e29374 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalSelectFromScalarLambda.cs @@ -0,0 +1,58 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalSelectFromScalarLambda + : EnumEvalBase + , EnumEval + { + private readonly ObjectArrayEventType _resultEventType; + + public EnumEvalSelectFromScalarLambda(ExprEvaluator innerExpression, int streamCountIncoming, ObjectArrayEventType resultEventType) + : base(innerExpression, streamCountIncoming) + { + _resultEventType = resultEventType; + } + + public Object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) + { + if (target.IsEmpty()) + { + return target; + } + + var resultEvent = new ObjectArrayEventBean(new Object[1], _resultEventType); + var values = (ICollection)target; + var queue = new LinkedList(); + foreach (Object next in values) + { + + resultEvent.Properties[0] = next; + eventsLambda[StreamNumLambda] = resultEvent; + + var item = InnerExpression.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + if (item != null) + { + queue.AddLast(item); + } + } + + return queue; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalSequenceEqual.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalSequenceEqual.cs new file mode 100755 index 000000000..de7bd3563 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalSequenceEqual.cs @@ -0,0 +1,88 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalSequenceEqual + : EnumEvalBase + , EnumEval + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + public EnumEvalSequenceEqual(ExprEvaluator innerExpression, int streamCountIncoming) + : base(innerExpression, streamCountIncoming) + { + } + + public object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) + { + var otherObj = InnerExpression.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + if (otherObj == null) + { + return false; + } + + var other = otherObj.Unwrap(true); + if (other == null) + { + Log.Warn("Enumeration method 'sequenceEqual' expected a Collection-type return value from its parameter but received '" + otherObj.GetType().FullName + "'"); + return false; + } + + if (target.Count != other.Count) + { + return false; + } + + if (target.IsEmpty()) + { + return true; + } + + var oneit = target.GetEnumerator(); + var twoit = other.GetEnumerator(); + for (int i = 0; i < target.Count; i++) + { + if (!oneit.MoveNext()) { throw new InvalidOperationException(); } + if (!twoit.MoveNext()) { throw new InvalidOperationException(); } + + var one = oneit.Current; + var two = twoit.Current; + + if (one == null) + { + if (two != null) + { + return false; + } + continue; + } + if (two == null) + { + return false; + } + + if (!Equals(one, two)) + { + return false; + } + } + + return true; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalSumEvents.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalSumEvents.cs new file mode 100755 index 000000000..4cb1e0aa7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalSumEvents.cs @@ -0,0 +1,42 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalSumEvents + : EnumEvalBase + , EnumEval + { + private readonly ExprDotEvalSumMethodFactory _sumMethodFactory; + + public EnumEvalSumEvents(ExprEvaluator innerExpression, int streamCountIncoming, ExprDotEvalSumMethodFactory sumMethodFactory) + : base(innerExpression, streamCountIncoming) + { + _sumMethodFactory = sumMethodFactory; + } + + public Object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) + { + var method = _sumMethodFactory.SumAggregator; + foreach (EventBean next in target) { + eventsLambda[StreamNumLambda] = next; + var value = InnerExpression.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + method.Enter(value); + } + + return method.Value; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalSumScalar.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalSumScalar.cs new file mode 100755 index 000000000..42d37dc6f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalSumScalar.cs @@ -0,0 +1,39 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalSumScalar + : EnumEvalBase + , EnumEval + { + private readonly ExprDotEvalSumMethodFactory _sumMethodFactory; + + public EnumEvalSumScalar(int streamCountIncoming, ExprDotEvalSumMethodFactory sumMethodFactory) + : base(streamCountIncoming) + { + _sumMethodFactory = sumMethodFactory; + } + + public Object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) + { + var method = _sumMethodFactory.SumAggregator; + foreach (Object next in target) { + method.Enter(next); + } + return method.Value; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalSumScalarLambda.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalSumScalarLambda.cs new file mode 100755 index 000000000..b993f45c1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalSumScalarLambda.cs @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalSumScalarLambda + : EnumEvalBase + , EnumEval + { + private readonly ExprDotEvalSumMethodFactory _sumMethodFactory; + private readonly ObjectArrayEventType _resultEventType; + + public EnumEvalSumScalarLambda(ExprEvaluator innerExpression, int streamCountIncoming, ExprDotEvalSumMethodFactory sumMethodFactory, ObjectArrayEventType resultEventType) + : base(innerExpression, streamCountIncoming) + { + _sumMethodFactory = sumMethodFactory; + _resultEventType = resultEventType; + } + + public Object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) + { + var method = _sumMethodFactory.SumAggregator; + var resultEvent = new ObjectArrayEventBean(new Object[1], _resultEventType); + var values = (ICollection)target; + foreach (Object next in values) + { + resultEvent.Properties[0] = next; + eventsLambda[StreamNumLambda] = resultEvent; + + var value = InnerExpression.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + method.Enter(value); + } + + return method.Value; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalTake.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalTake.cs new file mode 100755 index 000000000..0f1fa7b73 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalTake.cs @@ -0,0 +1,80 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; +using System.Linq; +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalTake + : EnumEval + { + private readonly ExprEvaluator _sizeEval; + private readonly int _numStreams; + + public EnumEvalTake(ExprEvaluator sizeEval, int numStreams) + { + _sizeEval = sizeEval; + _numStreams = numStreams; + } + + public int StreamNumSize + { + get { return _numStreams; } + } + + public object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) + { + Object sizeObj = _sizeEval.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + if (sizeObj == null) + { + return null; + } + + if (target.IsEmpty()) + { + return target; + } + + int size = sizeObj.AsInt(); + if (size <= 0) + { + return new object[0]; + } + + if (target.Count < size) + { + return target; + } + + if (size == 1) + { + return new object[] { target.FirstOrDefault() }; + } + + var result = new List(size); + foreach (var next in target) + { + if (result.Count >= size) + { + break; + } + result.Add(next); + } + + return result; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalTakeLast.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalTakeLast.cs new file mode 100755 index 000000000..31b62c8af --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalTakeLast.cs @@ -0,0 +1,78 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalTakeLast + : EnumEval + { + private readonly ExprEvaluator _sizeEval; + private readonly int _numStreams; + + public EnumEvalTakeLast(ExprEvaluator sizeEval, int numStreams) { + _sizeEval = sizeEval; + _numStreams = numStreams; + } + + public int StreamNumSize + { + get { return _numStreams; } + } + + public object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) { + + Object sizeObj = _sizeEval.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + if (sizeObj == null) { + return null; + } + + if (target.IsEmpty()) { + return target; + } + + int size = sizeObj.AsInt(); + if (size <= 0) { + return new object[0]; + } + + if (target.Count < size) { + return target; + } + + if (size == 1) { + object last = null; + foreach (object next in target) + { + last = next; + } + return new object[] { last }; + } + + var result = new List(); + foreach (object next in target) + { + result.Add(next); + if (result.Count > size) { + result.RemoveAt(0); + } + } + return result; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalTakeWhileEvents.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalTakeWhileEvents.cs new file mode 100755 index 000000000..199fca097 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalTakeWhileEvents.cs @@ -0,0 +1,64 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalTakeWhileEvents + : EnumEvalBase + , EnumEval + { + public EnumEvalTakeWhileEvents(ExprEvaluator innerExpression, int streamCountIncoming) + : base(innerExpression, streamCountIncoming) + { + } + + public object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) + { + if (target.IsEmpty()) { + return target; + } + + if (target.Count == 1) { + EventBean item = target.OfType().FirstOrDefault(); + eventsLambda[StreamNumLambda] = item; + + Object pass = InnerExpression.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + if (!pass.AsBoolean()) { + return new EventBean[0]; + } + return new[] {item}; + } + + var result = new List(); + + foreach (EventBean next in target) { + eventsLambda[StreamNumLambda] = next; + + Object pass = InnerExpression.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + if (!pass.AsBoolean()) { + break; + } + + result.Add(next); + } + + return result; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalTakeWhileIndexEvents.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalTakeWhileIndexEvents.cs new file mode 100755 index 000000000..11571027a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalTakeWhileIndexEvents.cs @@ -0,0 +1,84 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalTakeWhileIndexEvents : EnumEval + { + private readonly ExprEvaluator _innerExpression; + private readonly int _streamNumLambda; + private readonly ObjectArrayEventType _indexEventType; + + public EnumEvalTakeWhileIndexEvents(ExprEvaluator innerExpression, int streamNumLambda, ObjectArrayEventType indexEventType) { + _innerExpression = innerExpression; + _streamNumLambda = streamNumLambda; + _indexEventType = indexEventType; + } + + public int StreamNumSize + { + get { return _streamNumLambda + 2; } + } + + public Object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) + { + if (target.IsEmpty()) { + return target; + } + + var indexEvent = new ObjectArrayEventBean(new Object[1], _indexEventType); + + if (target.Count == 1) + { + EventBean item = (EventBean) target.First(); + indexEvent.Properties[0] = 0; + eventsLambda[_streamNumLambda] = item; + eventsLambda[_streamNumLambda + 1] = indexEvent; + + var pass = (bool?) _innerExpression.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + if (!pass.GetValueOrDefault(false)) + { + return Collections.GetEmptyList(); + } + return Collections.SingletonList(item); + } + + var result = new LinkedList(); + + int count = -1; + foreach (EventBean next in target) { + + count++; + + indexEvent.Properties[0] = count; + eventsLambda[_streamNumLambda] = next; + eventsLambda[_streamNumLambda + 1] = indexEvent; + + var pass = (bool?)_innerExpression.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + if (!pass.GetValueOrDefault(false)) + { + break; + } + + result.AddLast(next); + } + + return result; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalTakeWhileIndexScalar.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalTakeWhileIndexScalar.cs new file mode 100755 index 000000000..ce393b320 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalTakeWhileIndexScalar.cs @@ -0,0 +1,84 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalTakeWhileIndexScalar + : EnumEvalBaseScalarIndex + , EnumEval + { + public EnumEvalTakeWhileIndexScalar(ExprEvaluator innerExpression, int streamNumLambda, ObjectArrayEventType evalEventType, ObjectArrayEventType indexEventType) + : base(innerExpression, streamNumLambda, evalEventType, indexEventType) + { + } + + public override Object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) + { + if (target.IsEmpty()) + { + return target; + } + + var evalEvent = new ObjectArrayEventBean(new Object[1], EvalEventType); + var indexEvent = new ObjectArrayEventBean(new Object[1], IndexEventType); + + if (target.Count == 1) + { + Object item = target.First(); + + evalEvent.Properties[0] = item; + eventsLambda[StreamNumLambda] = evalEvent; + + indexEvent.Properties[0] = 0; + eventsLambda[StreamNumLambda + 1] = indexEvent; + + var pass = (bool?)InnerExpression.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + if (!pass.GetValueOrDefault(false)) + { + return Collections.GetEmptyList(); + } + return Collections.SingletonList(item); + } + + var result = new LinkedList(); + + var count = -1; + foreach (Object next in target) + { + + count++; + + evalEvent.Properties[0] = next; + eventsLambda[StreamNumLambda] = evalEvent; + + indexEvent.Properties[0] = count; + eventsLambda[StreamNumLambda + 1] = indexEvent; + + var pass = (bool?)InnerExpression.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + if (!pass.GetValueOrDefault(false)) + { + break; + } + + result.AddLast(next); + } + + return result; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalTakeWhileLastEvents.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalTakeWhileLastEvents.cs new file mode 100755 index 000000000..451977f3f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalTakeWhileLastEvents.cs @@ -0,0 +1,68 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; +using System.Linq; +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalTakeWhileLastEvents + : EnumEvalBase + , EnumEval + { + + public EnumEvalTakeWhileLastEvents(ExprEvaluator innerExpression, int streamCountIncoming) + : base(innerExpression, streamCountIncoming) + { + } + + public object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) + { + if (target.IsEmpty()) { + return target; + } + + if (target.Count == 1) { + EventBean item = target.OfType().FirstOrDefault(); + eventsLambda[StreamNumLambda] = item; + + Object pass = InnerExpression.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + if (!pass.AsBoolean()) + { + return new EventBean[0]; + } + return new[] {item}; + } + + var all = target.OfType().ToArray(); + var result = new LinkedList(); + + for (int i = all.Length - 1; i >= 0; i--) { + eventsLambda[StreamNumLambda] = all[i]; + + Object pass = InnerExpression.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + if (!pass.AsBoolean()) + { + break; + } + + result.AddFirst(all[i]); + } + + return result; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalTakeWhileLastIndexEvents.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalTakeWhileLastIndexEvents.cs new file mode 100755 index 000000000..9a292d14a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalTakeWhileLastIndexEvents.cs @@ -0,0 +1,78 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalTakeWhileLastIndexEvents + : EnumEvalBaseIndex + , EnumEval + { + public EnumEvalTakeWhileLastIndexEvents(ExprEvaluator innerExpression, int streamNumLambda, ObjectArrayEventType indexEventType) + : base(innerExpression, streamNumLambda, indexEventType) + { + } + + public override Object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) + { + if (target.IsEmpty()) { + return target; + } + var indexEvent = new ObjectArrayEventBean(new Object[1], IndexEventType); + if (target.Count == 1) + { + EventBean item = (EventBean) target.First(); + indexEvent.Properties[0] = 0; + eventsLambda[StreamNumLambda] = item; + eventsLambda[StreamNumLambda + 1] = indexEvent; + + var pass = (bool?)InnerExpression.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + if (!pass.GetValueOrDefault(false)) + { + return Collections.GetEmptyList(); + } + return Collections.SingletonList(item); + } + + var size = target.Count; + var all = new EventBean[size]; + var count = 0; + foreach (EventBean item in target) { + all[count++] = item; + } + + var result = new LinkedList(); + var index = 0; + for (int i = all.Length - 1; i >= 0; i--) { + + indexEvent.Properties[0] = index++; + eventsLambda[StreamNumLambda] = all[i]; + eventsLambda[StreamNumLambda + 1] = indexEvent; + + var pass = (bool?)InnerExpression.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + if (!pass.GetValueOrDefault(false)) + { + break; + } + + result.AddFirst(all[i]); + } + + return result; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalTakeWhileLastIndexScalar.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalTakeWhileLastIndexScalar.cs new file mode 100755 index 000000000..91dc54978 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalTakeWhileLastIndexScalar.cs @@ -0,0 +1,88 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalTakeWhileLastIndexScalar + : EnumEvalBaseScalarIndex + , EnumEval + { + public EnumEvalTakeWhileLastIndexScalar(ExprEvaluator innerExpression, int streamNumLambda, ObjectArrayEventType evalEventType, ObjectArrayEventType indexEventType) + : base(innerExpression, streamNumLambda, evalEventType, indexEventType) + { + } + + public override Object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) + { + if (target.IsEmpty()) + { + return target; + } + + var evalEvent = new ObjectArrayEventBean(new Object[1], EvalEventType); + var indexEvent = new ObjectArrayEventBean(new Object[1], IndexEventType); + + if (target.Count == 1) + { + Object item = target.First(); + + evalEvent.Properties[0] = item; + eventsLambda[StreamNumLambda] = evalEvent; + + indexEvent.Properties[0] = 0; + eventsLambda[StreamNumLambda + 1] = indexEvent; + + var pass = (bool?) InnerExpression.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + if (!pass.GetValueOrDefault(false)) + { + return Collections.GetEmptyList(); + } + return Collections.SingletonList(item); + } + + var size = target.Count; + var all = new Object[size]; + var count = 0; + foreach (Object item in target) + { + all[count++] = item; + } + + var result = new LinkedList(); + int index = 0; + for (int i = all.Length - 1; i >= 0; i--) + { + + evalEvent.Properties[0] = all[i]; + eventsLambda[StreamNumLambda] = evalEvent; + + indexEvent.Properties[0] = index++; + eventsLambda[StreamNumLambda + 1] = indexEvent; + + var pass = (bool?) InnerExpression.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + if (!pass.GetValueOrDefault(false)) + { + break; + } + result.AddFirst(all[i]); + } + + return result; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalTakeWhileLastScalar.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalTakeWhileLastScalar.cs new file mode 100755 index 000000000..74a45a603 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalTakeWhileLastScalar.cs @@ -0,0 +1,79 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalTakeWhileLastScalar + : EnumEvalBaseScalar + , EnumEval + { + public EnumEvalTakeWhileLastScalar(ExprEvaluator innerExpression, int streamCountIncoming, ObjectArrayEventType type) + : base(innerExpression, streamCountIncoming, type) + { + } + + public override Object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) + { + if (target.IsEmpty()) + { + return target; + } + + var evalEvent = new ObjectArrayEventBean(new Object[1], Type); + if (target.Count == 1) + { + var item = target.First(); + evalEvent.Properties[0] = item; + eventsLambda[StreamNumLambda] = evalEvent; + + var pass = (bool?) InnerExpression.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + if (!pass.GetValueOrDefault(false)) + { + return Collections.GetEmptyList(); + } + return Collections.SingletonList(item); + } + + var size = target.Count; + var all = new Object[size]; + var count = 0; + foreach (Object item in target) + { + all[count++] = item; + } + + var result = new LinkedList(); + + for (int i = all.Length - 1; i >= 0; i--) + { + evalEvent.Properties[0] = all[i]; + eventsLambda[StreamNumLambda] = evalEvent; + + var pass = (bool?) InnerExpression.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + if (!pass.GetValueOrDefault(false)) + { + break; + } + + result.AddFirst(all[i]); + } + + return result; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalTakeWhileScalar.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalTakeWhileScalar.cs new file mode 100755 index 000000000..a026377e5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalTakeWhileScalar.cs @@ -0,0 +1,72 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalTakeWhileScalar + : EnumEvalBaseScalar + , EnumEval + { + public EnumEvalTakeWhileScalar(ExprEvaluator innerExpression, int streamCountIncoming, ObjectArrayEventType type) + : base(innerExpression, streamCountIncoming, type) + { + } + + public override Object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) + { + if (target.IsEmpty()) + { + return target; + } + + var evalEvent = new ObjectArrayEventBean(new Object[1], Type); + if (target.Count == 1) + { + var item = target.First(); + evalEvent.Properties[0] = item; + eventsLambda[StreamNumLambda] = evalEvent; + + var pass = (bool?) InnerExpression.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + if (!pass.GetValueOrDefault(false)) + { + return Collections.GetEmptyList(); + } + return Collections.SingletonList(item); + } + + var result = new LinkedList(); + + foreach (Object next in target) + { + + evalEvent.Properties[0] = next; + eventsLambda[StreamNumLambda] = evalEvent; + + var pass = (bool?) InnerExpression.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + if (!pass.GetValueOrDefault(false)) + { + break; + } + + result.AddLast(next); + } + + return result; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalToMapEvents.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalToMapEvents.cs new file mode 100755 index 000000000..267ba90b2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalToMapEvents.cs @@ -0,0 +1,47 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalToMapEvents + : EnumEvalBase + , EnumEval + { + private readonly ExprEvaluator _secondExpression; + + public EnumEvalToMapEvents(ExprEvaluator innerExpression, int streamCountIncoming, ExprEvaluator secondExpression) + : base(innerExpression, streamCountIncoming) + { + _secondExpression = secondExpression; + } + + public Object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) + { + var map = new Dictionary().WithNullSupport(); + + foreach (EventBean next in target) + { + eventsLambda[StreamNumLambda] = next; + + var key = InnerExpression.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + var value = _secondExpression.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + map.Put(key, value); + } + + return map; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalToMapScalarLambda.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalToMapScalarLambda.cs new file mode 100755 index 000000000..731d12ac0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalToMapScalarLambda.cs @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalToMapScalarLambda + : EnumEvalBase + , EnumEval + { + private readonly ExprEvaluator _secondExpression; + private readonly ObjectArrayEventType _resultEventType; + + public EnumEvalToMapScalarLambda(ExprEvaluator innerExpression, int streamCountIncoming, ExprEvaluator secondExpression, ObjectArrayEventType resultEventType) + : base(innerExpression, streamCountIncoming) + { + _secondExpression = secondExpression; + _resultEventType = resultEventType; + } + + public Object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) { + var map = new Dictionary(); + var resultEvent = new ObjectArrayEventBean(new Object[1], _resultEventType); + var values = (ICollection)target; + foreach (Object next in values) + { + resultEvent.Properties[0] = next; + eventsLambda[StreamNumLambda] = resultEvent; + + var key = InnerExpression.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + var value = _secondExpression.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + map.Put(key, value); + } + + return map; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalUnion.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalUnion.cs new file mode 100755 index 000000000..4ceca8415 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalUnion.cs @@ -0,0 +1,67 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalUnion : EnumEval + { + private readonly int _numStreams; + private readonly ExprEvaluatorEnumeration _evaluator; + private readonly bool _scalar; + + public EnumEvalUnion(int numStreams, ExprEvaluatorEnumeration evaluator, bool scalar) + { + _numStreams = numStreams; + _evaluator = evaluator; + _scalar = scalar; + } + + public int StreamNumSize + { + get { return _numStreams; } + } + + public Object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) + { + if (target == null) + { + return null; + } + + ICollection set; + if (_scalar) + { + set = _evaluator.EvaluateGetROCollectionScalar(new EvaluateParams(eventsLambda, isNewData, context)); + } + else + { + set = _evaluator + .EvaluateGetROCollectionEvents(new EvaluateParams(eventsLambda, isNewData, context)) + .TransformInto(o => (EventBean) o, e => (Object) e); + } + + if (set == null || set.IsEmpty()) + { + return target; + } + + var result = new List(target); + result.AddAll(set); + + return result; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalWhereEvents.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalWhereEvents.cs new file mode 100755 index 000000000..20cc1a96c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalWhereEvents.cs @@ -0,0 +1,49 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalWhereEvents + : EnumEvalBase + , EnumEval + { + public EnumEvalWhereEvents(ExprEvaluator innerExpression, int streamCountIncoming) + : base(innerExpression, streamCountIncoming) + { + } + + public object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) { + if (target.IsEmpty()) { + return target; + } + + var result = new List(); + + foreach (EventBean next in target) { + eventsLambda[StreamNumLambda] = next; + + var pass = InnerExpression.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + if (!pass.AsBoolean()) { + continue; + } + + result.Add(next); + } + + return result; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalWhereIndexEvents.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalWhereIndexEvents.cs new file mode 100755 index 000000000..a2775421e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalWhereIndexEvents.cs @@ -0,0 +1,54 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalWhereIndexEvents : EnumEvalBaseIndex, EnumEval { + + public EnumEvalWhereIndexEvents(ExprEvaluator innerExpression, int streamNumLambda, ObjectArrayEventType indexEventType) + : base(innerExpression, streamNumLambda, indexEventType) + { + } + + public override Object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) { + if (target.IsEmpty()) { + return target; + } + + var result = new LinkedList(); + var indexEvent = new ObjectArrayEventBean(new Object[1], IndexEventType); + + int count = -1; + foreach (EventBean next in target) { + count++; + + indexEvent.Properties[0] = count; + eventsLambda[StreamNumLambda] = next; + eventsLambda[StreamNumLambda + 1] = indexEvent; + + var pass = (bool?) InnerExpression.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + if (!pass.HasValue || !pass.Value) { + continue; + } + + result.AddLast(next); + } + + return result; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalWhereScalar.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalWhereScalar.cs new file mode 100755 index 000000000..5c067e54b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalWhereScalar.cs @@ -0,0 +1,54 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalWhereScalar + : EnumEvalBaseScalar + , EnumEval + { + public EnumEvalWhereScalar(ExprEvaluator innerExpression, int streamCountIncoming, ObjectArrayEventType type) + : base(innerExpression, streamCountIncoming, type) + { + } + + public override Object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) + { + if (target.IsEmpty()) { + return target; + } + + var result = new LinkedList(); + var evalEvent = new ObjectArrayEventBean(new Object[1], Type); + + foreach (Object next in target) { + + evalEvent.Properties[0] = next; + eventsLambda[StreamNumLambda] = evalEvent; + + var pass = (bool?) InnerExpression.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + if (!pass.GetValueOrDefault(false)) { + continue; + } + + result.AddLast(next); + } + + return result; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalWhereScalarIndex.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalWhereScalarIndex.cs new file mode 100755 index 000000000..0fe47db23 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/EnumEvalWhereScalarIndex.cs @@ -0,0 +1,64 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class EnumEvalWhereScalarIndex + : EnumEvalBaseScalarIndex + , EnumEval + { + public EnumEvalWhereScalarIndex(ExprEvaluator innerExpression, int streamNumLambda, ObjectArrayEventType evalEventType, ObjectArrayEventType indexEventType) + : base(innerExpression, streamNumLambda, evalEventType, indexEventType) + { + } + + public override Object EvaluateEnumMethod(EventBean[] eventsLambda, ICollection target, bool isNewData, ExprEvaluatorContext context) + { + if (target.IsEmpty()) + { + return target; + } + + var result = new LinkedList(); + var evalEvent = new ObjectArrayEventBean(new Object[1], EvalEventType); + var indexEvent = new ObjectArrayEventBean(new Object[1], IndexEventType); + + int count = -1; + foreach (Object next in target) + { + + count++; + + evalEvent.Properties[0] = next; + eventsLambda[StreamNumLambda] = evalEvent; + + indexEvent.Properties[0] = count; + eventsLambda[StreamNumLambda + 1] = indexEvent; + + var pass = (bool?) InnerExpression.Evaluate(new EvaluateParams(eventsLambda, isNewData, context)); + if (!pass.GetValueOrDefault(false)) + { + continue; + } + + result.AddLast(next); + } + + return result; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalAggregate.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalAggregate.cs new file mode 100755 index 000000000..2cf1d359a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalAggregate.cs @@ -0,0 +1,82 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.enummethod.dot; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.dot; +using com.espertech.esper.epl.rettype; +using com.espertech.esper.events; +using com.espertech.esper.events.arr; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class ExprDotEvalAggregate : ExprDotEvalEnumMethodBase + { + public override EventType[] GetAddStreamTypes( + string enumMethodUsedName, + IList goesToNames, + EventType inputEventType, + Type collectionComponentType, + IList bodiesAndParameters, + EventAdapterService eventAdapterService) + { + EventType evalEventType; + if (inputEventType == null) + { + evalEventType = ExprDotNodeUtility.MakeTransientOAType( + enumMethodUsedName, goesToNames[1], collectionComponentType, eventAdapterService); + } + else + { + evalEventType = inputEventType; + } + + Type initializationType = bodiesAndParameters[0].BodyEvaluator.ReturnType; + EventType typeResult = ExprDotNodeUtility.MakeTransientOAType( + enumMethodUsedName, goesToNames[0], initializationType, eventAdapterService); + + return new EventType[] + { + typeResult, + evalEventType + }; + } + + public override EnumEval GetEnumEval(EngineImportService engineImportService, EventAdapterService eventAdapterService, StreamTypeService streamTypeService, int statementId, string enumMethodUsedName, IList bodiesAndParameters, EventType inputEventType, Type collectionComponentType, int numStreamsIncoming, bool disablePropertyExpressionEventCollCache) + { + ExprDotEvalParam initValueParam = bodiesAndParameters[0]; + ExprEvaluator initValueEval = initValueParam.BodyEvaluator; + base.TypeInfo = EPTypeHelper.SingleValue(initValueEval.ReturnType.GetBoxedType()); + + var resultAndAdd = (ExprDotEvalParamLambda) bodiesAndParameters[1]; + + if (inputEventType != null) + { + return new EnumEvalAggregateEvents( + initValueEval, + resultAndAdd.BodyEvaluator, resultAndAdd.StreamCountIncoming, + (ObjectArrayEventType) resultAndAdd.GoesToTypes[0]); + } + else + { + return new EnumEvalAggregateScalar( + initValueEval, + resultAndAdd.BodyEvaluator, resultAndAdd.StreamCountIncoming, + (ObjectArrayEventType) resultAndAdd.GoesToTypes[0], + (ObjectArrayEventType) resultAndAdd.GoesToTypes[1]); + } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalAllOfAnyOf.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalAllOfAnyOf.cs new file mode 100755 index 000000000..623fd1db3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalAllOfAnyOf.cs @@ -0,0 +1,62 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.enummethod.dot; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.dot; +using com.espertech.esper.epl.rettype; +using com.espertech.esper.events; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class ExprDotEvalAllOfAnyOf : ExprDotEvalEnumMethodBase + { + public override EventType[] GetAddStreamTypes( + string enumMethodUsedName, + IList goesToNames, + EventType inputEventType, + Type collectionComponentType, + IList bodiesAndParameters, + EventAdapterService eventAdapterService) + { + return ExprDotNodeUtility.GetSingleLambdaParamEventType( + enumMethodUsedName, goesToNames, inputEventType, collectionComponentType, + eventAdapterService); + } + + + public override EnumEval GetEnumEval(EngineImportService engineImportService, EventAdapterService eventAdapterService, StreamTypeService streamTypeService, int statementId, string enumMethodUsedName, IList bodiesAndParameters, EventType inputEventType, Type collectionComponentType, int numStreamsIncoming, bool disablePropertyExpressionEventCollCache) + { + var first = (ExprDotEvalParamLambda) bodiesAndParameters[0]; + + TypeInfo = EPTypeHelper.SingleValue(typeof(bool)); + if (inputEventType != null) + { + if (EnumMethodEnum == EnumMethodEnum.ALLOF) + { + return new EnumEvalAllOfEvents(first.BodyEvaluator, first.StreamCountIncoming); + } + return new EnumEvalAnyOfEvents(first.BodyEvaluator, first.StreamCountIncoming); + } + + if (EnumMethodEnum == EnumMethodEnum.ALLOF) + { + return new EnumEvalAllOfScalar( + first.BodyEvaluator, first.StreamCountIncoming, (ObjectArrayEventType) first.GoesToTypes[0]); + } + return new EnumEvalAnyOfScalar( + first.BodyEvaluator, first.StreamCountIncoming, (ObjectArrayEventType) first.GoesToTypes[0]); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalAverage.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalAverage.cs new file mode 100755 index 000000000..0803c9ea2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalAverage.cs @@ -0,0 +1,82 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.enummethod.dot; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.dot; +using com.espertech.esper.epl.rettype; +using com.espertech.esper.events; +using com.espertech.esper.events.arr; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class ExprDotEvalAverage : ExprDotEvalEnumMethodBase + { + public override EventType[] GetAddStreamTypes( + string enumMethodUsedName, + IList goesToNames, + EventType inputEventType, + Type collectionComponentType, + IList bodiesAndParameters, + EventAdapterService eventAdapterService) + { + return ExprDotNodeUtility.GetSingleLambdaParamEventType( + enumMethodUsedName, goesToNames, inputEventType, collectionComponentType, + eventAdapterService); + } + + + public override EnumEval GetEnumEval(EngineImportService engineImportService, EventAdapterService eventAdapterService, StreamTypeService streamTypeService, int statementId, string enumMethodUsedName, IList bodiesAndParameters, EventType inputEventType, Type collectionComponentType, int numStreamsIncoming, bool disablePropertyExpressionEventCollCache) + { + if (bodiesAndParameters.IsEmpty()) + { + if (collectionComponentType == typeof (decimal?)) + { + TypeInfo = EPTypeHelper.SingleValue(typeof(decimal?)); + return new EnumEvalAverageDecimalScalar( + numStreamsIncoming, engineImportService.DefaultMathContext); + } + TypeInfo = EPTypeHelper.SingleValue(typeof(double?)); + return new EnumEvalAverageScalar(numStreamsIncoming); + } + + var first = (ExprDotEvalParamLambda) bodiesAndParameters[0]; + var returnType = first.BodyEvaluator.ReturnType.GetBoxedType(); + + if (returnType == typeof (decimal?)) + { + TypeInfo = EPTypeHelper.SingleValue(typeof(decimal?)); + if (inputEventType == null) + { + return new EnumEvalAverageDecimalScalarLambda( + first.BodyEvaluator, first.StreamCountIncoming, + (ObjectArrayEventType) first.GoesToTypes[0], + engineImportService.DefaultMathContext); + } + return new EnumEvalAverageDecimalEvents( + first.BodyEvaluator, first.StreamCountIncoming, + engineImportService.DefaultMathContext); + } + TypeInfo = EPTypeHelper.SingleValue(typeof(double?)); + if (inputEventType == null) + { + return new EnumEvalAverageScalarLambda( + first.BodyEvaluator, first.StreamCountIncoming, + (ObjectArrayEventType) first.GoesToTypes[0]); + } + return new EnumEvalAverageEvents(first.BodyEvaluator, first.StreamCountIncoming); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalCountOf.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalCountOf.cs new file mode 100755 index 000000000..aaa7c39c5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalCountOf.cs @@ -0,0 +1,60 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.enummethod.dot; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.dot; +using com.espertech.esper.epl.rettype; +using com.espertech.esper.events; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class ExprDotEvalCountOf : ExprDotEvalEnumMethodBase + { + public override EventType[] GetAddStreamTypes( + string enumMethodUsedName, + IList goesToNames, + EventType inputEventType, + Type collectionComponentType, + IList bodiesAndParameters, + EventAdapterService eventAdapterService) + { + return ExprDotNodeUtility.GetSingleLambdaParamEventType( + enumMethodUsedName, goesToNames, inputEventType, collectionComponentType, + eventAdapterService); + } + + + public override EnumEval GetEnumEval(EngineImportService engineImportService, EventAdapterService eventAdapterService, StreamTypeService streamTypeService, int statementId, string enumMethodUsedName, IList bodiesAndParameters, EventType inputEventType, Type collectionComponentType, int numStreamsIncoming, bool disablePropertyExpressionEventCollCache) + { + base.TypeInfo = EPTypeHelper.SingleValue(typeof(int)); + if (bodiesAndParameters.IsEmpty()) + { + return new EnumEvalCountOf(numStreamsIncoming); + } + + var first = (ExprDotEvalParamLambda) bodiesAndParameters[0]; + if (inputEventType != null) + { + return new EnumEvalCountOfSelectorEvents(first.BodyEvaluator, first.StreamCountIncoming); + } + else + { + return new EnumEvalCountOfSelectorScalar( + first.BodyEvaluator, first.StreamCountIncoming, (ObjectArrayEventType) first.GoesToTypes[0]); + } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalDistinct.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalDistinct.cs new file mode 100755 index 000000000..dea7be502 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalDistinct.cs @@ -0,0 +1,56 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.enummethod.dot; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.dot; +using com.espertech.esper.epl.rettype; +using com.espertech.esper.events; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class ExprDotEvalDistinct : ExprDotEvalEnumMethodBase + { + public override EventType[] GetAddStreamTypes( + string enumMethodUsedName, + IList goesToNames, + EventType inputEventType, + Type collectionComponentType, + IList bodiesAndParameters, + EventAdapterService eventAdapterService) + { + return ExprDotNodeUtility.GetSingleLambdaParamEventType( + enumMethodUsedName, goesToNames, inputEventType, collectionComponentType, + eventAdapterService); + } + + public override EnumEval GetEnumEval(EngineImportService engineImportService, EventAdapterService eventAdapterService, StreamTypeService streamTypeService, int statementId, string enumMethodUsedName, IList bodiesAndParameters, EventType inputEventType, Type collectionComponentType, int numStreamsIncoming, bool disablePropertyExpressionEventCollCache) + { + if (bodiesAndParameters.IsEmpty()) { + base.TypeInfo = EPTypeHelper.CollectionOfSingleValue(collectionComponentType); + return new EnumEvalDistinctScalar(numStreamsIncoming); + } + + ExprDotEvalParamLambda first = (ExprDotEvalParamLambda) bodiesAndParameters[0]; + if (inputEventType == null) { + base.TypeInfo = EPTypeHelper.CollectionOfSingleValue(collectionComponentType); + return new EnumEvalDistinctScalarLambda(first.BodyEvaluator, first.StreamCountIncoming, + (ObjectArrayEventType) first.GoesToTypes[0]); + } + base.TypeInfo = EPTypeHelper.CollectionOfEvents(inputEventType); + return new EnumEvalDistinctEvents(first.BodyEvaluator, first.StreamCountIncoming); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalFirstLastOf.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalFirstLastOf.cs new file mode 100755 index 000000000..a1237fb72 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalFirstLastOf.cs @@ -0,0 +1,88 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.enummethod.dot; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.dot; +using com.espertech.esper.epl.rettype; +using com.espertech.esper.events; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class ExprDotEvalFirstLastOf : ExprDotEvalEnumMethodBase + { + public override EventType[] GetAddStreamTypes( + string enumMethodUsedName, + IList goesToNames, + EventType inputEventType, + Type collectionComponentType, + IList bodiesAndParameters, + EventAdapterService eventAdapterService) + { + return ExprDotNodeUtility.GetSingleLambdaParamEventType( + enumMethodUsedName, goesToNames, inputEventType, collectionComponentType, + eventAdapterService); + } + + + public override EnumEval GetEnumEval(EngineImportService engineImportService, EventAdapterService eventAdapterService, StreamTypeService streamTypeService, int statementId, string enumMethodUsedName, IList bodiesAndParameters, EventType inputEventType, Type collectionComponentType, int numStreamsIncoming, bool disablePropertyExpressionEventCollCache) + { + if (bodiesAndParameters.IsEmpty()) + { + if (inputEventType != null) + { + base.TypeInfo = EPTypeHelper.SingleEvent(inputEventType); + } + else + { + base.TypeInfo = EPTypeHelper.SingleValue(collectionComponentType); + } + if (EnumMethodEnum == EnumMethodEnum.FIRST) + { + return new EnumEvalFirstOfNoPredicate(numStreamsIncoming); + } + else + { + return new EnumEvalLastOfNoPredicate(numStreamsIncoming); + } + } + + var first = (ExprDotEvalParamLambda) bodiesAndParameters[0]; + if (inputEventType != null) + { + base.TypeInfo = EPTypeHelper.SingleEvent(inputEventType); + if (EnumMethodEnum == EnumMethodEnum.FIRST) + { + return new EnumEvalFirstOfPredicateEvents(first.BodyEvaluator, first.StreamCountIncoming); + } + else + { + return new EnumEvalLastOfPredicateEvents(first.BodyEvaluator, first.StreamCountIncoming); + } + } + base.TypeInfo = EPTypeHelper.SingleValue(collectionComponentType); + if (EnumMethodEnum == EnumMethodEnum.FIRST) + { + return new EnumEvalFirstOfPredicateScalar( + first.BodyEvaluator, first.StreamCountIncoming, (ObjectArrayEventType) first.GoesToTypes[0]); + } + else + { + return new EnumEvalLastOfPredicateScalar( + first.BodyEvaluator, first.StreamCountIncoming, (ObjectArrayEventType) first.GoesToTypes[0]); + } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalGroupBy.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalGroupBy.cs new file mode 100755 index 000000000..e2b1c7aab --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalGroupBy.cs @@ -0,0 +1,66 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.enummethod.dot; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.dot; +using com.espertech.esper.epl.rettype; +using com.espertech.esper.events; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.epl.enummethod.eval +{ + using GroupMap = IDictionary>; + + public class ExprDotEvalGroupBy : ExprDotEvalEnumMethodBase + { + public override EventType[] GetAddStreamTypes( + string enumMethodUsedName, + IList goesToNames, + EventType inputEventType, + Type collectionComponentType, + IList bodiesAndParameters, + EventAdapterService eventAdapterService) + { + return ExprDotNodeUtility.GetSingleLambdaParamEventType( + enumMethodUsedName, goesToNames, inputEventType, collectionComponentType, + eventAdapterService); + } + + + public override EnumEval GetEnumEval(EngineImportService engineImportService, EventAdapterService eventAdapterService, StreamTypeService streamTypeService, int statementId, string enumMethodUsedName, IList bodiesAndParameters, EventType inputEventType, Type collectionComponentType, int numStreamsIncoming, bool disablePropertyExpressionEventCollCache) + { + base.TypeInfo = EPTypeHelper.SingleValue(typeof(GroupMap)); + var first = (ExprDotEvalParamLambda) bodiesAndParameters[0]; + if (bodiesAndParameters.Count == 2) + { + var second = (ExprDotEvalParamLambda) bodiesAndParameters[1]; + if (inputEventType == null) + { + return new EnumEvalGroupByKeyValueSelectorScalarLambda( + first.BodyEvaluator, first.StreamCountIncoming, second.BodyEvaluator, + (ObjectArrayEventType) first.GoesToTypes[0]); + } + return new EnumEvalGroupByKeyValueSelectorEvents( + first.BodyEvaluator, first.StreamCountIncoming, second.BodyEvaluator); + } + if (inputEventType == null) + { + return new EnumEvalGroupByKeySelectorScalarLambda( + first.BodyEvaluator, first.StreamCountIncoming, + (ObjectArrayEventType) first.GoesToTypes[0]); + } + return new EnumEvalGroupByKeySelectorEvents(first.BodyEvaluator, first.StreamCountIncoming); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalMinByMaxBy.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalMinByMaxBy.cs new file mode 100755 index 000000000..3b3a4fb72 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalMinByMaxBy.cs @@ -0,0 +1,55 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.enummethod.dot; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.dot; +using com.espertech.esper.epl.rettype; +using com.espertech.esper.events; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class ExprDotEvalMinByMaxBy : ExprDotEvalEnumMethodBase + { + public override EventType[] GetAddStreamTypes( + string enumMethodUsedName, + IList goesToNames, + EventType inputEventType, + Type collectionComponentType, + IList bodiesAndParameters, + EventAdapterService eventAdapterService) + { + return ExprDotNodeUtility.GetSingleLambdaParamEventType( + enumMethodUsedName, goesToNames, inputEventType, collectionComponentType, + eventAdapterService); + } + + + public override EnumEval GetEnumEval(EngineImportService engineImportService, EventAdapterService eventAdapterService, StreamTypeService streamTypeService, int statementId, string enumMethodUsedName, IList bodiesAndParameters, EventType inputEventType, Type collectionComponentType, int numStreamsIncoming, bool disablePropertyExpressionEventCollCache) + { + var first = (ExprDotEvalParamLambda) bodiesAndParameters[0]; + + var max = EnumMethodEnum == EnumMethodEnum.MAXBY; + if (inputEventType == null) + { + base.TypeInfo = EPTypeHelper.SingleValue(collectionComponentType); + return new EnumEvalMinMaxByScalarLambda( + first.BodyEvaluator, first.StreamCountIncoming, max, + (ObjectArrayEventType) first.GoesToTypes[0]); + } + base.TypeInfo = EPTypeHelper.SingleEvent(inputEventType); + return new EnumEvalMinMaxByEvents(first.BodyEvaluator, first.StreamCountIncoming, max); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalMinMax.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalMinMax.cs new file mode 100755 index 000000000..ea141a84f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalMinMax.cs @@ -0,0 +1,65 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.enummethod.dot; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.dot; +using com.espertech.esper.epl.rettype; +using com.espertech.esper.events; +using com.espertech.esper.events.arr; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class ExprDotEvalMinMax : ExprDotEvalEnumMethodBase + { + public override EventType[] GetAddStreamTypes( + string enumMethodUsedName, + IList goesToNames, + EventType inputEventType, + Type collectionComponentType, + IList bodiesAndParameters, + EventAdapterService eventAdapterService) + { + return ExprDotNodeUtility.GetSingleLambdaParamEventType( + enumMethodUsedName, goesToNames, inputEventType, collectionComponentType, + eventAdapterService); + } + + public override EnumEval GetEnumEval(EngineImportService engineImportService, EventAdapterService eventAdapterService, StreamTypeService streamTypeService, int statementId, string enumMethodUsedName, IList bodiesAndParameters, EventType inputEventType, Type collectionComponentType, int numStreamsIncoming, bool disablePropertyExpressionEventCollCache) + { + bool max = this.EnumMethodEnum == EnumMethodEnum.MAX; + + Type returnType; + if (bodiesAndParameters.IsEmpty()) + { + returnType = collectionComponentType.GetBoxedType(); + base.TypeInfo = EPTypeHelper.SingleValue(returnType); + return new EnumEvalMinMaxScalar(numStreamsIncoming, max); + } + + var first = (ExprDotEvalParamLambda) bodiesAndParameters[0]; + returnType = first.BodyEvaluator.ReturnType.GetBoxedType(); + base.TypeInfo = EPTypeHelper.SingleValue(returnType); + + if (inputEventType == null) + { + return new EnumEvalMinMaxScalarLambda( + first.BodyEvaluator, first.StreamCountIncoming, max, + (ObjectArrayEventType) first.GoesToTypes[0]); + } + return new EnumEvalMinMaxEvents(first.BodyEvaluator, first.StreamCountIncoming, max); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalMostLeastFrequent.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalMostLeastFrequent.cs new file mode 100755 index 000000000..bc16b7e98 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalMostLeastFrequent.cs @@ -0,0 +1,65 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.enummethod.dot; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.dot; +using com.espertech.esper.epl.rettype; +using com.espertech.esper.events; +using com.espertech.esper.events.arr; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class ExprDotEvalMostLeastFrequent : ExprDotEvalEnumMethodBase + { + public override EventType[] GetAddStreamTypes( + string enumMethodUsedName, + IList goesToNames, + EventType inputEventType, + Type collectionComponentType, + IList bodiesAndParameters, + EventAdapterService eventAdapterService) + { + return ExprDotNodeUtility.GetSingleLambdaParamEventType( + enumMethodUsedName, goesToNames, inputEventType, collectionComponentType, + eventAdapterService); + } + + public override EnumEval GetEnumEval(EngineImportService engineImportService, EventAdapterService eventAdapterService, StreamTypeService streamTypeService, int statementId, string enumMethodUsedName, IList bodiesAndParameters, EventType inputEventType, Type collectionComponentType, int numStreamsIncoming, bool disablePropertyExpressionEventCollCache) + { + Type returnType; + if (bodiesAndParameters.IsEmpty()) + { + returnType = collectionComponentType.GetBoxedType(); + base.TypeInfo = EPTypeHelper.SingleValue(returnType); + return new EnumEvalMostLeastFrequentScalar( + numStreamsIncoming, EnumMethodEnum == EnumMethodEnum.MOSTFREQUENT); + } + + var first = (ExprDotEvalParamLambda) bodiesAndParameters[0]; + returnType = first.BodyEvaluator.ReturnType.GetBoxedType(); + base.TypeInfo = EPTypeHelper.SingleValue(returnType); + + var mostFrequent = EnumMethodEnum == EnumMethodEnum.MOSTFREQUENT; + if (inputEventType == null) + { + return new EnumEvalMostLeastFrequentScalarLamda( + first.BodyEvaluator, first.StreamCountIncoming, mostFrequent, + (ObjectArrayEventType) first.GoesToTypes[0]); + } + return new EnumEvalMostLeastFrequentEvent(first.BodyEvaluator, numStreamsIncoming, mostFrequent); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalNoOp.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalNoOp.cs new file mode 100755 index 000000000..5ef7bc95a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalNoOp.cs @@ -0,0 +1,33 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.enummethod.dot; +using com.espertech.esper.epl.rettype; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class ExprDotEvalNoOp : ExprDotEvalEnumMethodBase + { + public override EventType[] GetAddStreamTypes(String enumMethodUsedName, IList goesToNames, EventType inputEventType, Type collectionComponentType, IList bodiesAndParameters, EventAdapterService eventAdapterService) + { + return new EventType[] {}; + } + + public override EnumEval GetEnumEval(EngineImportService engineImportService, EventAdapterService eventAdapterService, StreamTypeService streamTypeService, int statementId, string enumMethodUsedName, IList bodiesAndParameters, EventType inputEventType, Type collectionComponentType, int numStreamsIncoming, bool disablePropertyExpressionEventCollCache) + { + TypeInfo = EPTypeHelper.CollectionOfEvents(inputEventType); + return new EnumEvalNoOp(numStreamsIncoming); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalOrderByAscDesc.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalOrderByAscDesc.cs new file mode 100755 index 000000000..777796d50 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalOrderByAscDesc.cs @@ -0,0 +1,55 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.enummethod.dot; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.dot; +using com.espertech.esper.epl.rettype; +using com.espertech.esper.events; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class ExprDotEvalOrderByAscDesc : ExprDotEvalEnumMethodBase + { + public override EventType[] GetAddStreamTypes(string enumMethodUsedName, IList goesToNames, EventType inputEventType, Type collectionComponentType, IList bodiesAndParameters, EventAdapterService eventAdapterService) + { + return ExprDotNodeUtility.GetSingleLambdaParamEventType( + enumMethodUsedName, goesToNames, inputEventType, collectionComponentType, + eventAdapterService); + } + + public override EnumEval GetEnumEval(EngineImportService engineImportService, EventAdapterService eventAdapterService, StreamTypeService streamTypeService, int statementId, string enumMethodUsedName, IList bodiesAndParameters, EventType inputEventType, Type collectionComponentType, int numStreamsIncoming, bool disablePropertyExpressionEventCollCache) + { + bool isDescending = EnumMethodEnum == EnumMethodEnum.ORDERBYDESC; + + if (bodiesAndParameters.IsEmpty()) + { + base.TypeInfo = EPTypeHelper.CollectionOfSingleValue(collectionComponentType); + return new EnumEvalOrderByAscDescScalar(numStreamsIncoming, isDescending); + } + + var first = (ExprDotEvalParamLambda) bodiesAndParameters[0]; + if (inputEventType == null) + { + base.TypeInfo = EPTypeHelper.CollectionOfSingleValue(collectionComponentType); + return new EnumEvalOrderByAscDescScalarLambda( + first.BodyEvaluator, first.StreamCountIncoming, isDescending, + (ObjectArrayEventType) first.GoesToTypes[0]); + } + base.TypeInfo = EPTypeHelper.CollectionOfEvents(inputEventType); + return new EnumEvalOrderByAscDescEvents(first.BodyEvaluator, first.StreamCountIncoming, isDescending); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalReverse.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalReverse.cs new file mode 100755 index 000000000..8c51a21d5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalReverse.cs @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.enummethod.dot; +using com.espertech.esper.epl.rettype; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class ExprDotEvalReverse : ExprDotEvalEnumMethodBase + { + public override EventType[] GetAddStreamTypes( + String enumMethodUsedName, + IList goesToNames, + EventType inputEventType, + Type collectionComponentType, + IList bodiesAndParameters, + EventAdapterService eventAdapterService) + { + return new EventType[] + { + }; + } + + public override EnumEval GetEnumEval(EngineImportService engineImportService, EventAdapterService eventAdapterService, StreamTypeService streamTypeService, int statementId, string enumMethodUsedName, IList bodiesAndParameters, EventType inputEventType, Type collectionComponentType, int numStreamsIncoming, bool disablePropertyExpressionEventCollCache) + { + if (inputEventType != null) + { + base.TypeInfo = EPTypeHelper.CollectionOfEvents(inputEventType); + } + else + { + base.TypeInfo = EPTypeHelper.CollectionOfSingleValue(collectionComponentType); + } + return new EnumEvalReverse(numStreamsIncoming); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalSelectFrom.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalSelectFrom.cs new file mode 100755 index 000000000..03ec606ea --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalSelectFrom.cs @@ -0,0 +1,52 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.enummethod.dot; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.dot; +using com.espertech.esper.epl.rettype; +using com.espertech.esper.events; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class ExprDotEvalSelectFrom : ExprDotEvalEnumMethodBase + { + public override EventType[] GetAddStreamTypes( + string enumMethodUsedName, + IList goesToNames, + EventType inputEventType, + Type collectionComponentType, + IList bodiesAndParameters, + EventAdapterService eventAdapterService) + { + return ExprDotNodeUtility.GetSingleLambdaParamEventType( + enumMethodUsedName, goesToNames, inputEventType, collectionComponentType, + eventAdapterService); + } + + public override EnumEval GetEnumEval(EngineImportService engineImportService, EventAdapterService eventAdapterService, StreamTypeService streamTypeService, int statementId, string enumMethodUsedName, IList bodiesAndParameters, EventType inputEventType, Type collectionComponentType, int numStreamsIncoming, bool disablePropertyExpressionEventCollCache) + { + var first = (ExprDotEvalParamLambda) bodiesAndParameters[0]; + var returnType = first.BodyEvaluator.ReturnType; + base.TypeInfo = EPTypeHelper.CollectionOfSingleValue(returnType); + if (inputEventType == null) + { + return new EnumEvalSelectFromScalarLambda( + first.BodyEvaluator, first.StreamCountIncoming, + (ObjectArrayEventType) first.GoesToTypes[0]); + } + return new EnumEvalSelectFromEvents(first.BodyEvaluator, first.StreamCountIncoming); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalSequenceEqual.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalSequenceEqual.cs new file mode 100755 index 000000000..82797f3fc --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalSequenceEqual.cs @@ -0,0 +1,42 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.enummethod.dot; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.rettype; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class ExprDotEvalSequenceEqual : ExprDotEvalEnumMethodBase + { + public override EventType[] GetAddStreamTypes( + string enumMethodUsedName, + IList goesToNames, + EventType inputEventType, + Type collectionComponentType, + IList bodiesAndParameters, + EventAdapterService eventAdapterService) + { + return new EventType[0]; + } + + public override EnumEval GetEnumEval(EngineImportService engineImportService, EventAdapterService eventAdapterService, StreamTypeService streamTypeService, int statementId, string enumMethodUsedName, IList bodiesAndParameters, EventType inputEventType, Type collectionComponentType, int numStreamsIncoming, bool disablePropertyExpressionEventCollCache) + { + base.TypeInfo = EPTypeHelper.SingleValue(typeof(Boolean)); + ExprEvaluator body = bodiesAndParameters[0].BodyEvaluator; + return new EnumEvalSequenceEqual(body, numStreamsIncoming); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalSetExceptUnionIntersect.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalSetExceptUnionIntersect.cs new file mode 100755 index 000000000..5e84f31cf --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalSetExceptUnionIntersect.cs @@ -0,0 +1,93 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.enummethod.dot; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.dot; +using com.espertech.esper.epl.rettype; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class ExprDotEvalSetExceptUnionIntersect : ExprDotEvalEnumMethodBase + { + public override EventType[] GetAddStreamTypes( + string enumMethodUsedName, + IList goesToNames, + EventType inputEventType, + Type collectionComponentType, + IList bodiesAndParameters, + EventAdapterService eventAdapterService) + { + return new EventType[] + { + }; + } + + public override EnumEval GetEnumEval(EngineImportService engineImportService, EventAdapterService eventAdapterService, StreamTypeService streamTypeService, int statementId, string enumMethodUsedName, IList bodiesAndParameters, EventType inputEventType, Type collectionComponentType, int numStreamsIncoming, bool disablePropertyExpressionEventCollCache) + { + ExprDotEvalParam first = bodiesAndParameters[0]; + + ExprDotEnumerationSource enumSrc = ExprDotNodeUtility.GetEnumerationSource( + first.Body, streamTypeService, eventAdapterService, statementId, true, + disablePropertyExpressionEventCollCache); + if (inputEventType != null) + { + base.TypeInfo = EPTypeHelper.CollectionOfEvents(inputEventType); + } + else + { + base.TypeInfo = EPTypeHelper.CollectionOfSingleValue(collectionComponentType); + } + + if (enumSrc.Enumeration == null) + { + String message = "Enumeration method '" + enumMethodUsedName + + "' requires an expression yielding an event-collection as input paramater"; + throw new ExprValidationException(message); + } + + var setType = enumSrc.Enumeration.GetEventTypeCollection(eventAdapterService, statementId); + if (!Equals(setType, inputEventType)) + { + bool isSubtype = EventTypeUtility.IsTypeOrSubTypeOf(setType, inputEventType); + if (!isSubtype) + { + String message = "Enumeration method '" + enumMethodUsedName + "' expects event type '" + + inputEventType.Name + "' but receives event type '" + + enumSrc.Enumeration.GetEventTypeCollection(eventAdapterService, statementId).Name + + "'"; + throw new ExprValidationException(message); + } + } + + if (EnumMethodEnum == EnumMethodEnum.UNION) + { + return new EnumEvalUnion(numStreamsIncoming, enumSrc.Enumeration, inputEventType == null); + } + else if (EnumMethodEnum == EnumMethodEnum.INTERSECT) + { + return new EnumEvalIntersect(numStreamsIncoming, enumSrc.Enumeration, inputEventType == null); + } + else if (EnumMethodEnum == EnumMethodEnum.EXCEPT) + { + return new EnumEvalExcept(numStreamsIncoming, enumSrc.Enumeration, inputEventType == null); + } + else + { + throw new ArgumentException("Invalid enumeration method for this factory: " + EnumMethodEnum); + } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalSumMethod.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalSumMethod.cs new file mode 100755 index 000000000..fa16c60b9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalSumMethod.cs @@ -0,0 +1,18 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public interface ExprDotEvalSumMethod + { + void Enter(Object o); + object Value { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalSumMethodFactory.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalSumMethodFactory.cs new file mode 100755 index 000000000..0263b29e6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalSumMethodFactory.cs @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public interface ExprDotEvalSumMethodFactory + { + ExprDotEvalSumMethod SumAggregator { get; } + Type ValueType { get; } + } + + public class ProxyExprDotEvalSumMethodFactory : ExprDotEvalSumMethodFactory + { + public Func ProcSumAggregator { get; set; } + public Func ProcValueType { get; set; } + + public ExprDotEvalSumMethod SumAggregator + { + get { return ProcSumAggregator(); } + } + + public Type ValueType + { + get { return ProcValueType(); } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalSumOf.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalSumOf.cs new file mode 100755 index 000000000..ed5626cff --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalSumOf.cs @@ -0,0 +1,187 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.enummethod.dot; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.dot; +using com.espertech.esper.epl.rettype; +using com.espertech.esper.events; +using com.espertech.esper.events.arr; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.enummethod.eval +{ + [Serializable] + public class ExprDotEvalSumOf : ExprDotEvalEnumMethodBase + { + public override EventType[] GetAddStreamTypes( + string enumMethodUsedName, + IList goesToNames, + EventType inputEventType, + Type collectionComponentType, + IList bodiesAndParameters, + EventAdapterService eventAdapterService) + { + return ExprDotNodeUtility.GetSingleLambdaParamEventType(enumMethodUsedName, goesToNames, inputEventType, collectionComponentType, eventAdapterService); + } + + public override EnumEval GetEnumEval(EngineImportService engineImportService, EventAdapterService eventAdapterService, StreamTypeService streamTypeService, int statementId, string enumMethodUsedName, IList bodiesAndParameters, EventType inputEventType, Type collectionComponentType, int numStreamsIncoming, bool disablePropertyExpressionEventCollCache) + { + if (bodiesAndParameters.IsEmpty()) + { + var aggMethodFactoryX = GetAggregatorFactory(collectionComponentType); + TypeInfo = EPTypeHelper.SingleValue(aggMethodFactoryX.ValueType.GetBoxedType()); + return new EnumEvalSumScalar(numStreamsIncoming, aggMethodFactoryX); + } + + var first = (ExprDotEvalParamLambda) bodiesAndParameters[0]; + var aggMethodFactory = GetAggregatorFactory(first.BodyEvaluator.ReturnType); + var returnType = aggMethodFactory.ValueType.GetBoxedType(); + TypeInfo = EPTypeHelper.SingleValue(returnType); + if (inputEventType == null) + { + return new EnumEvalSumScalarLambda( + first.BodyEvaluator, first.StreamCountIncoming, aggMethodFactory, + (ObjectArrayEventType) first.GoesToTypes[0]); + } + return new EnumEvalSumEvents(first.BodyEvaluator, first.StreamCountIncoming, aggMethodFactory); + } + + private static ExprDotEvalSumMethodFactory GetAggregatorFactory(Type evalType) + { + if (evalType.IsFloatingPointClass()) + { + return new ProxyExprDotEvalSumMethodFactory + { + ProcSumAggregator = () => new ExprDotEvalSumMethodDouble(), + ProcValueType = () => typeof(double?) + }; + } + else if (evalType.GetBoxedType() == typeof(decimal?)) + { + return new ProxyExprDotEvalSumMethodFactory + { + ProcSumAggregator = () => new ExprDotEvalSumMethodBigDecimal(), + ProcValueType = () => typeof(decimal?) + }; + } + else if (evalType.GetBoxedType() == typeof(long?)) + { + return new ProxyExprDotEvalSumMethodFactory + { + ProcSumAggregator = () => new ExprDotEvalSumMethodLong(), + ProcValueType = () => typeof(long?) + }; + } + else + { + return new ProxyExprDotEvalSumMethodFactory + { + ProcSumAggregator = () => new ExprDotEvalSumMethodInteger(), + ProcValueType = () => typeof(int?) + }; + } + } + + private class ExprDotEvalSumMethodDouble : ExprDotEvalSumMethod + { + private double _sum; + private long _numDataPoints; + + public void Enter(Object @object) + { + if (@object == null) + { + return; + } + _numDataPoints++; + _sum += @object.AsDouble(); + } + + public object Value + { + get { return _numDataPoints == 0 ? (object) null : _sum; } + } + } + + private class ExprDotEvalSumMethodBigDecimal : ExprDotEvalSumMethod + { + private decimal _sum; + private long _numDataPoints; + + public ExprDotEvalSumMethodBigDecimal() + { + _sum = 0.0m; + } + + public void Enter(Object @object) + { + if (@object == null) + { + return; + } + _numDataPoints++; + _sum += @object.AsDecimal(); + } + + public object Value + { + get { return _numDataPoints == 0 ? (object) null : _sum; } + } + } + + private class ExprDotEvalSumMethodLong : ExprDotEvalSumMethod + { + private long _sum; + private long _numDataPoints; + + public void Enter(Object @object) + { + if (@object == null) + { + return; + } + _numDataPoints++; + _sum += @object.AsLong(); + } + + public object Value + { + get { return _numDataPoints == 0 ? (object) null : _sum; } + } + } + + private class ExprDotEvalSumMethodInteger : ExprDotEvalSumMethod + { + private int _sum; + private long _numDataPoints; + + public void Enter(Object @object) + { + if (@object == null) + { + return; + } + _numDataPoints++; + _sum += @object.AsInt(); + } + + public object Value + { + get { return _numDataPoints == 0 ? (object) null : _sum; } + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalTakeAndTakeLast.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalTakeAndTakeLast.cs new file mode 100755 index 000000000..a173133ee --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalTakeAndTakeLast.cs @@ -0,0 +1,60 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.enummethod.dot; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.rettype; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class ExprDotEvalTakeAndTakeLast : ExprDotEvalEnumMethodBase + { + public override EventType[] GetAddStreamTypes( + string enumMethodUsedName, + IList goesToNames, + EventType inputEventType, + Type collectionComponentType, + IList bodiesAndParameters, + EventAdapterService eventAdapterService) + { + return new EventType[] + { + }; + } + + public override EnumEval GetEnumEval(EngineImportService engineImportService, EventAdapterService eventAdapterService, StreamTypeService streamTypeService, int statementId, string enumMethodUsedName, IList bodiesAndParameters, EventType inputEventType, Type collectionComponentType, int numStreamsIncoming, bool disablePropertyExpressionEventCollCache) + { + ExprEvaluator sizeEval = bodiesAndParameters[0].BodyEvaluator; + + if (inputEventType != null) + { + base.TypeInfo = EPTypeHelper.CollectionOfEvents(inputEventType); + } + else + { + base.TypeInfo = EPTypeHelper.CollectionOfSingleValue(collectionComponentType); + } + + if (EnumMethodEnum == EnumMethodEnum.TAKE) + { + return new EnumEvalTake(sizeEval, numStreamsIncoming); + } + else + { + return new EnumEvalTakeLast(sizeEval, numStreamsIncoming); + } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalTakeWhileAndLast.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalTakeWhileAndLast.cs new file mode 100755 index 000000000..59f6f36ed --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalTakeWhileAndLast.cs @@ -0,0 +1,109 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.enummethod.dot; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.dot; +using com.espertech.esper.epl.rettype; +using com.espertech.esper.events; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class ExprDotEvalTakeWhileAndLast : ExprDotEvalEnumMethodBase + { + public override EventType[] GetAddStreamTypes( + string enumMethodUsedName, + IList goesToNames, + EventType inputEventType, + Type collectionComponentType, + IList bodiesAndParameters, + EventAdapterService eventAdapterService) + { + EventType firstParamType; + if (inputEventType == null) + { + firstParamType = ExprDotNodeUtility.MakeTransientOAType( + enumMethodUsedName, goesToNames[0], collectionComponentType, eventAdapterService); + } + else + { + firstParamType = inputEventType; + } + + if (goesToNames.Count == 1) + { + return new EventType[] + { + firstParamType + }; + } + + ObjectArrayEventType indexEventType = ExprDotNodeUtility.MakeTransientOAType( + enumMethodUsedName, goesToNames[1], typeof(int), eventAdapterService); + return new EventType[] + { + firstParamType, + indexEventType + }; + } + + public override EnumEval GetEnumEval(EngineImportService engineImportService, EventAdapterService eventAdapterService, StreamTypeService streamTypeService, int statementId, string enumMethodUsedName, IList bodiesAndParameters, EventType inputEventType, Type collectionComponentType, int numStreamsIncoming, bool disablePropertyExpressionEventCollCache) + { + var first = (ExprDotEvalParamLambda) bodiesAndParameters[0]; + + if (inputEventType != null) + { + base.TypeInfo = EPTypeHelper.CollectionOfEvents(inputEventType); + if (first.GoesToNames.Count == 1) + { + if (EnumMethodEnum == EnumMethodEnum.TAKEWHILELAST) + { + return new EnumEvalTakeWhileLastEvents(first.BodyEvaluator, first.StreamCountIncoming); + } + return new EnumEvalTakeWhileEvents(first.BodyEvaluator, first.StreamCountIncoming); + } + + if (EnumMethodEnum == EnumMethodEnum.TAKEWHILELAST) + { + return new EnumEvalTakeWhileLastIndexEvents( + first.BodyEvaluator, first.StreamCountIncoming, (ObjectArrayEventType) first.GoesToTypes[1]); + } + return new EnumEvalTakeWhileIndexEvents( + first.BodyEvaluator, first.StreamCountIncoming, (ObjectArrayEventType) first.GoesToTypes[1]); + } + + base.TypeInfo = EPTypeHelper.CollectionOfSingleValue(collectionComponentType); + if (first.GoesToNames.Count == 1) + { + if (EnumMethodEnum == EnumMethodEnum.TAKEWHILELAST) + { + return new EnumEvalTakeWhileLastScalar( + first.BodyEvaluator, first.StreamCountIncoming, (ObjectArrayEventType) first.GoesToTypes[0]); + } + return new EnumEvalTakeWhileScalar( + first.BodyEvaluator, first.StreamCountIncoming, (ObjectArrayEventType) first.GoesToTypes[0]); + } + + if (EnumMethodEnum == EnumMethodEnum.TAKEWHILELAST) + { + return new EnumEvalTakeWhileLastIndexScalar( + first.BodyEvaluator, first.StreamCountIncoming, (ObjectArrayEventType) first.GoesToTypes[0], + (ObjectArrayEventType) first.GoesToTypes[1]); + } + return new EnumEvalTakeWhileIndexScalar( + first.BodyEvaluator, first.StreamCountIncoming, (ObjectArrayEventType) first.GoesToTypes[0], + (ObjectArrayEventType) first.GoesToTypes[1]); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalToMap.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalToMap.cs new file mode 100755 index 000000000..1be83bba1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalToMap.cs @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.enummethod.dot; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.dot; +using com.espertech.esper.epl.rettype; +using com.espertech.esper.events; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class ExprDotEvalToMap : ExprDotEvalEnumMethodBase + { + public override EventType[] GetAddStreamTypes(string enumMethodUsedName, IList goesToNames, EventType inputEventType, Type collectionComponentType, IList bodiesAndParameters, EventAdapterService eventAdapterService) + { + return ExprDotNodeUtility.GetSingleLambdaParamEventType( + enumMethodUsedName, goesToNames, inputEventType, collectionComponentType, eventAdapterService); + } + + public override EnumEval GetEnumEval(EngineImportService engineImportService, EventAdapterService eventAdapterService, StreamTypeService streamTypeService, int statementId, string enumMethodUsedName, IList bodiesAndParameters, EventType inputEventType, Type collectionComponentType, int numStreamsIncoming, bool disablePropertyExpressionEventCollCache) + { + TypeInfo = EPTypeHelper.SingleValue(typeof(IDictionary)); + var first = (ExprDotEvalParamLambda) bodiesAndParameters[0]; + var second = (ExprDotEvalParamLambda) bodiesAndParameters[1]; + if (inputEventType == null) + { + return new EnumEvalToMapScalarLambda( + first.BodyEvaluator, first.StreamCountIncoming, second.BodyEvaluator, + (ObjectArrayEventType) first.GoesToTypes[0]); + } + return new EnumEvalToMapEvents(first.BodyEvaluator, first.StreamCountIncoming, second.BodyEvaluator); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalWhere.cs b/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalWhere.cs new file mode 100755 index 000000000..69e365609 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/enummethod/eval/ExprDotEvalWhere.cs @@ -0,0 +1,87 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.enummethod.dot; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.dot; +using com.espertech.esper.epl.rettype; +using com.espertech.esper.events; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.epl.enummethod.eval +{ + public class ExprDotEvalWhere : ExprDotEvalEnumMethodBase + { + public override EventType[] GetAddStreamTypes( + string enumMethodUsedName, + IList goesToNames, + EventType inputEventType, + Type collectionComponentType, + IList bodiesAndParameters, + EventAdapterService eventAdapterService) + { + EventType firstParamType; + if (inputEventType == null) + { + firstParamType = ExprDotNodeUtility.MakeTransientOAType( + enumMethodUsedName, goesToNames[0], collectionComponentType, eventAdapterService); + } + else + { + firstParamType = inputEventType; + } + + if (goesToNames.Count == 1) + { + return new EventType[] + { + firstParamType + }; + } + + ObjectArrayEventType indexEventType = ExprDotNodeUtility.MakeTransientOAType( + enumMethodUsedName, goesToNames[1], typeof (int), eventAdapterService); + return new EventType[] + { + firstParamType, + indexEventType + }; + } + + public override EnumEval GetEnumEval(EngineImportService engineImportService, EventAdapterService eventAdapterService, StreamTypeService streamTypeService, int statementId, string enumMethodUsedName, IList bodiesAndParameters, EventType inputEventType, Type collectionComponentType, int numStreamsIncoming, bool disablePropertyExpressionEventCollCache) + { + var first = (ExprDotEvalParamLambda) bodiesAndParameters[0]; + + if (inputEventType != null) + { + TypeInfo = EPTypeHelper.CollectionOfEvents(inputEventType); + if (first.GoesToNames.Count == 1) + { + return new EnumEvalWhereEvents(first.BodyEvaluator, first.StreamCountIncoming); + } + return new EnumEvalWhereIndexEvents( + first.BodyEvaluator, first.StreamCountIncoming, (ObjectArrayEventType) first.GoesToTypes[1]); + } + + TypeInfo = EPTypeHelper.CollectionOfSingleValue(collectionComponentType); + if (first.GoesToNames.Count == 1) + { + return new EnumEvalWhereScalar( + first.BodyEvaluator, first.StreamCountIncoming, (ObjectArrayEventType) first.GoesToTypes[0]); + } + return new EnumEvalWhereScalarIndex( + first.BodyEvaluator, first.StreamCountIncoming, (ObjectArrayEventType) first.GoesToTypes[0], + (ObjectArrayEventType) first.GoesToTypes[1]); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/expression/accessagg/AggregationMethodFactoryFirstLastUnbound.cs b/NEsper.Core/NEsper.Core/epl/expression/accessagg/AggregationMethodFactoryFirstLastUnbound.cs new file mode 100755 index 000000000..bac159955 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/accessagg/AggregationMethodFactoryFirstLastUnbound.cs @@ -0,0 +1,112 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.aggregator; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.methodagg; + +using AggregationMethodFactoryUtil = com.espertech.esper.epl.agg.factory.AggregationMethodFactoryUtil; + +namespace com.espertech.esper.epl.expression.accessagg +{ + public class AggregationMethodFactoryFirstLastUnbound : AggregationMethodFactory + { + private readonly ExprAggMultiFunctionLinearAccessNode _parent; + private readonly EventType _collectionEventType; + private readonly Type _resultType; + private readonly int _streamNum; + + public AggregationMethodFactoryFirstLastUnbound( + ExprAggMultiFunctionLinearAccessNode parent, + EventType collectionEventType, + Type resultType, + int streamNum) + { + _parent = parent; + _collectionEventType = collectionEventType; + _resultType = resultType; + _streamNum = streamNum; + } + + public Type ResultType + { + get { return _resultType; } + } + + public AggregationStateKey GetAggregationStateKey(bool isMatchRecognize) + { + throw new UnsupportedOperationException(); + } + + public bool IsAccessAggregation + { + get { return false; } + } + + public AggregationStateFactory GetAggregationStateFactory(bool isMatchRecognize) + { + throw new UnsupportedOperationException(); + } + + public AggregationAccessor Accessor + { + get { throw new UnsupportedOperationException(); } + } + + public AggregationMethod Make() + { + if (_parent.StateType == AggregationStateType.FIRST) + { + return AggregationMethodFactoryUtil.MakeFirstEver(false); + } + else if (_parent.StateType == AggregationStateType.LAST) + { + return AggregationMethodFactoryUtil.MakeLastEver(false); + } + throw new EPException("Window aggregation function is not available"); + } + + public ExprAggregateNodeBase AggregationExpression + { + get { return _parent; } + } + + public void ValidateIntoTableCompatible(AggregationMethodFactory intoTableAgg) + { + agg.service.AggregationMethodFactoryUtil.ValidateAggregationType(this, intoTableAgg); + var that = (AggregationMethodFactoryFirstLastUnbound) intoTableAgg; + agg.service.AggregationMethodFactoryUtil.ValidateStreamNumZero(that._streamNum); + if (_collectionEventType != null) + { + agg.service.AggregationMethodFactoryUtil.ValidateEventType( + _collectionEventType, that._collectionEventType); + } + else + { + agg.service.AggregationMethodFactoryUtil.ValidateAggregationInputType(_resultType, that._resultType); + } + } + + public AggregationAgent AggregationStateAgent + { + get { throw new UnsupportedOperationException(); } + } + + public ExprEvaluator GetMethodAggregationEvaluator(bool join, EventType[] typesPerStream) + { + return ExprMethodAggUtil.GetDefaultEvaluator(_parent.PositionalParams, join, typesPerStream); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/accessagg/ExprAggCountMinSketchNode.cs b/NEsper.Core/NEsper.Core/epl/expression/accessagg/ExprAggCountMinSketchNode.cs new file mode 100755 index 000000000..fe3ef8f7b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/accessagg/ExprAggCountMinSketchNode.cs @@ -0,0 +1,253 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.client.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.approx; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.events; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.accessagg +{ + /// + /// Represents the Count-min sketch aggregate function. + /// + [Serializable] + public class ExprAggCountMinSketchNode + : ExprAggregateNodeBase + , ExprAggregateAccessMultiValueNode + { + private const double DEFAULT_EPS_OF_TOTAL_COUNT = 0.0001; + private const double DEFAULT_CONFIDENCE = 0.99; + private const int DEFAULT_SEED = 1234567; + + private static readonly CountMinSketchAgentStringUTF16 DEFAULT_AGENT = new CountMinSketchAgentStringUTF16(); + + private const string MSG_NAME = "Count-min-sketch"; + private const string NAME_EPS_OF_TOTAL_COUNT = "epsOfTotalCount"; + private const string NAME_CONFIDENCE = "confidence"; + private const string NAME_SEED = "seed"; + private const string NAME_TOPK = "topk"; + private const string NAME_AGENT = "agent"; + + private readonly CountMinSketchAggType _aggType; + + /// + /// Ctor. + /// + /// flag indicating unique or non-unique value aggregation + /// Type of the aggregate. + public ExprAggCountMinSketchNode(bool distinct, CountMinSketchAggType aggType) + : base(distinct) + { + _aggType = aggType; + } + + public override AggregationMethodFactory ValidateAggregationChild(ExprValidationContext validationContext) + { + return ValidateAggregationInternal(validationContext, null); + } + + public AggregationMethodFactory ValidateAggregationParamsWBinding(ExprValidationContext context, TableMetadataColumnAggregation tableAccessColumn) + { + return ValidateAggregationInternal(context, tableAccessColumn); + } + + public override string AggregationFunctionName + { + get { return _aggType.GetFuncName(); } + } + + protected override bool EqualsNodeAggregateMethodOnly(ExprAggregateNode node) + { + return false; + } + + public CountMinSketchAggType AggType + { + get { return _aggType; } + } + + public EventType GetEventTypeCollection(EventAdapterService eventAdapterService, int statementId) + { + return null; + } + + public ICollection EvaluateGetROCollectionEvents(EvaluateParams evaluateParams) + { + return null; + } + + public Type ComponentTypeCollection + { + get { return null; } + } + + public ICollection EvaluateGetROCollectionScalar(EvaluateParams evaluateParams) + { + return null; + } + + public EventType GetEventTypeSingle(EventAdapterService eventAdapterService, int statementId) + { + return null; + } + + public EventBean EvaluateGetEventBean(EvaluateParams evaluateParams) + { + return null; + } + + protected override bool IsExprTextWildcardWhenNoParams + { + get { return false; } + } + + private AggregationMethodFactory ValidateAggregationInternal(ExprValidationContext context, TableMetadataColumnAggregation tableAccessColumn) + { + if (IsDistinct) + { + throw new ExprValidationException(MessagePrefix + "is not supported with distinct"); + } + + // for declaration, validate the specification and return the state factory + if (_aggType == CountMinSketchAggType.STATE) + { + if (context.ExprEvaluatorContext.StatementType != StatementType.CREATE_TABLE) + { + throw new ExprValidationException(MessagePrefix + "can only be used in create-table statements"); + } + var specification = ValidateSpecification(context); + var stateFactory = context.EngineImportService.AggregationFactoryFactory.MakeCountMinSketch(context.StatementExtensionSvcContext, this, specification); + return new ExprAggCountMinSketchNodeFactoryState(stateFactory); + } + + // validate number of parameters + if (_aggType == CountMinSketchAggType.ADD || _aggType == CountMinSketchAggType.FREQ) + { + if (ChildNodes.Count == 0 || ChildNodes.Count > 1) + { + throw new ExprValidationException(MessagePrefix + "requires a single parameter expression"); + } + } + else + { + if (ChildNodes.Count != 0) + { + throw new ExprValidationException(MessagePrefix + "requires a no parameter expressions"); + } + } + + // validate into-table and table-access + if (_aggType == CountMinSketchAggType.ADD) + { + if (context.IntoTableName == null) + { + throw new ExprValidationException(MessagePrefix + "can only be used with into-table"); + } + } + else + { + if (tableAccessColumn == null) + { + throw new ExprValidationException(MessagePrefix + "requires the use of a table-access expression"); + } + ExprNodeUtility.GetValidatedSubtree(ExprNodeOrigin.AGGPARAM, ChildNodes, context); + } + + // obtain evaluator + ExprEvaluator addOrFrequencyEvaluator = null; + if (_aggType == CountMinSketchAggType.ADD || _aggType == CountMinSketchAggType.FREQ) + { + addOrFrequencyEvaluator = ChildNodes[0].ExprEvaluator; + } + + return new ExprAggCountMinSketchNodeFactoryUse(this, addOrFrequencyEvaluator); + } + + private CountMinSketchSpec ValidateSpecification(ExprValidationContext exprValidationContext) + { + // default specification + var spec = new CountMinSketchSpec(new CountMinSketchSpecHashes(DEFAULT_EPS_OF_TOTAL_COUNT, DEFAULT_CONFIDENCE, DEFAULT_SEED), null, DEFAULT_AGENT); + + // no parameters + if (ChildNodes.Count == 0) + { + return spec; + } + + // check expected parameter type: a json object + if (ChildNodes.Count > 1 || !(ChildNodes[0] is ExprConstantNode)) + { + throw DeclaredWrongParameterExpr; + } + var constantNode = (ExprConstantNode)ChildNodes[0]; + var value = constantNode.GetConstantValue(exprValidationContext.ExprEvaluatorContext); + if (!(value is IDictionary)) + { + throw DeclaredWrongParameterExpr; + } + + // define what to populate + var descriptors = new PopulateFieldWValueDescriptor[] { + new PopulateFieldWValueDescriptor(NAME_EPS_OF_TOTAL_COUNT, typeof(double), spec.HashesSpec.GetType(), vv => { + if (vv != null) {spec.HashesSpec.EpsOfTotalCount = (double) vv;} + }, true), + new PopulateFieldWValueDescriptor(NAME_CONFIDENCE, typeof(double), spec.HashesSpec.GetType(), vv => { + if (vv != null) {spec.HashesSpec.Confidence = (double) vv;} + }, true), + new PopulateFieldWValueDescriptor(NAME_SEED, typeof(int), spec.HashesSpec.GetType(), vv => { + if (vv != null) {spec.HashesSpec.Seed = (int) vv;} + }, true), + new PopulateFieldWValueDescriptor(NAME_TOPK, typeof(int), spec.GetType(), vv => { + if (vv != null) {spec.TopkSpec = (int) vv;} + }, true), + new PopulateFieldWValueDescriptor(NAME_AGENT, typeof(string), spec.GetType(), vv => { + if (vv != null) { + CountMinSketchAgent transform; + try { + var transformClass = exprValidationContext.EngineImportService.ResolveType((string)vv, false); + transform = TypeHelper.Instantiate(transformClass); + } + catch (Exception e) { + throw new ExprValidationException("Failed to instantiate agent provider: " + e.Message, e); + } + spec.Agent = transform; + } + }, true) + }; + + // populate from json, validates incorrect names, coerces types, instantiates transform + PopulateUtil.PopulateSpecCheckParameters(descriptors, (IDictionary)value, spec, ExprNodeOrigin.AGGPARAM, exprValidationContext); + + return spec; + } + + public ExprValidationException DeclaredWrongParameterExpr + { + get + { + return new ExprValidationException( + MessagePrefix + " expects either no parameter or a single json parameter object"); + } + } + + private string MessagePrefix + { + get { return MSG_NAME + " aggregation function '" + _aggType.GetFuncName() + "' "; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/accessagg/ExprAggCountMinSketchNodeFactoryBase.cs b/NEsper.Core/NEsper.Core/epl/expression/accessagg/ExprAggCountMinSketchNodeFactoryBase.cs new file mode 100755 index 000000000..c68283b9c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/accessagg/ExprAggCountMinSketchNodeFactoryBase.cs @@ -0,0 +1,64 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.aggregator; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.accessagg +{ + public abstract class ExprAggCountMinSketchNodeFactoryBase : AggregationMethodFactory + { + private readonly ExprAggCountMinSketchNode _parent; + + public abstract Type ResultType { get; } + public abstract AggregationStateFactory GetAggregationStateFactory(bool isMatchRecognize); + public abstract AggregationAccessor Accessor { get; } + public abstract void ValidateIntoTableCompatible(AggregationMethodFactory intoTableAgg); + public abstract AggregationAgent AggregationStateAgent { get; } + public abstract ExprEvaluator GetMethodAggregationEvaluator(bool @join, EventType[] typesPerStream); + + protected ExprAggCountMinSketchNodeFactoryBase(ExprAggCountMinSketchNode parent) + { + _parent = parent; + } + + public virtual bool IsAccessAggregation + { + get { return true; } + } + + public virtual AggregationStateKey GetAggregationStateKey(bool isMatchRecognize) + { + throw new UnsupportedOperationException(); + } + + public virtual AggregationMethod Make() + { + throw new UnsupportedOperationException(); + } + + public virtual ExprAggregateNodeBase AggregationExpression + { + get { return _parent; } + } + + public virtual ExprAggCountMinSketchNode Parent + { + get { return _parent; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/accessagg/ExprAggCountMinSketchNodeFactoryState.cs b/NEsper.Core/NEsper.Core/epl/expression/accessagg/ExprAggCountMinSketchNodeFactoryState.cs new file mode 100755 index 000000000..faf994497 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/accessagg/ExprAggCountMinSketchNodeFactoryState.cs @@ -0,0 +1,87 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.factory; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.approx; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.accessagg +{ + public class ExprAggCountMinSketchNodeFactoryState : ExprAggCountMinSketchNodeFactoryBase + { + private readonly AggregationStateFactoryCountMinSketch _stateFactory; + + public ExprAggCountMinSketchNodeFactoryState(AggregationStateFactoryCountMinSketch stateFactory) + : base(stateFactory.Parent) + { + _stateFactory = stateFactory; + } + + public override Type ResultType + { + get { return null; } + } + + public override AggregationAccessor Accessor + { + get { return CountMinSketchAggAccessorDefault.INSTANCE; } + } + + public override AggregationStateFactory GetAggregationStateFactory(bool isMatchRecognize) + { + // For match-recognize we don't allow + if (isMatchRecognize) { + throw new IllegalStateException("Count-min-sketch is not supported for match-recognize"); + } + return _stateFactory; + } + + public override AggregationAgent AggregationStateAgent + { + get { throw new UnsupportedOperationException(); } + } + + public override void ValidateIntoTableCompatible(AggregationMethodFactory intoTableAgg) + { + var use = (ExprAggCountMinSketchNodeFactoryUse) intoTableAgg; + var aggType = use.Parent.AggType; + if (aggType == CountMinSketchAggType.FREQ || aggType == CountMinSketchAggType.ADD) + { + Type clazz = use.AddOrFrequencyEvaluator.ReturnType; + var foundMatch = false; + foreach (var allowed in _stateFactory.Specification.Agent.AcceptableValueTypes) + { + if (TypeHelper.IsSubclassOrImplementsInterface(clazz, allowed)) + { + foundMatch = true; + } + } + if (!foundMatch) + { + throw new ExprValidationException( + "Mismatching parameter return type, expected any of " + + _stateFactory.Specification.Agent.AcceptableValueTypes.Render() + " but received " + + clazz.GetTypeNameFullyQualPretty()); + } + } + } + + public override ExprEvaluator GetMethodAggregationEvaluator(Boolean join, EventType[] typesPerStream) + { + return null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/accessagg/ExprAggCountMinSketchNodeFactoryUse.cs b/NEsper.Core/NEsper.Core/epl/expression/accessagg/ExprAggCountMinSketchNodeFactoryUse.cs new file mode 100755 index 000000000..21e615cd7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/accessagg/ExprAggCountMinSketchNodeFactoryUse.cs @@ -0,0 +1,117 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.client.util; +using com.espertech.esper.compat; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.approx; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.accessagg +{ + public class ExprAggCountMinSketchNodeFactoryUse : ExprAggCountMinSketchNodeFactoryBase + { + private readonly ExprEvaluator _addOrFrequencyEvaluator; + + public ExprAggCountMinSketchNodeFactoryUse(ExprAggCountMinSketchNode parent, ExprEvaluator addOrFrequencyEvaluator) + : base(parent) + { + _addOrFrequencyEvaluator = addOrFrequencyEvaluator; + } + + public override Type ResultType + { + get + { + var parent = Parent; + if (parent.AggType == CountMinSketchAggType.ADD) + { + return null; + } + else if (parent.AggType == CountMinSketchAggType.FREQ) + { + return typeof (long?); + } + else if (parent.AggType == CountMinSketchAggType.TOPK) + { + return typeof (CountMinSketchTopK[]); + } + else + { + throw new UnsupportedOperationException("Unrecognized code " + parent.AggType); + } + } + } + + public override AggregationStateKey GetAggregationStateKey(bool isMatchRecognize) + { + throw new UnsupportedOperationException(); + } + + public override AggregationStateFactory GetAggregationStateFactory(bool isMatchRecognize) + { + throw new UnsupportedOperationException(); + } + + public override AggregationAccessor Accessor + { + get + { + var parent = Parent; + if (parent.AggType == CountMinSketchAggType.ADD) + { + // modifications handled by agent + return CountMinSketchAggAccessorDefault.INSTANCE; + } + else if (parent.AggType == CountMinSketchAggType.FREQ) + { + return new CountMinSketchAggAccessorFrequency(_addOrFrequencyEvaluator); + } + else if (parent.AggType == CountMinSketchAggType.TOPK) + { + return CountMinSketchAggAccessorTopk.INSTANCE; + } + throw new IllegalStateException( + "Aggregation accessor not available for this function '" + parent.AggregationFunctionName + "'"); + } + } + + public override AggregationAgent AggregationStateAgent + { + get + { + var parent = Parent; + if (parent.AggType == CountMinSketchAggType.ADD) + { + return new CountMinSketchAggAgentAdd(_addOrFrequencyEvaluator); + } + throw new IllegalStateException( + "Aggregation agent not available for this function '" + parent.AggregationFunctionName + "'"); + } + } + + public override void ValidateIntoTableCompatible(AggregationMethodFactory intoTableAgg) + { + throw new IllegalStateException("Aggregation not compatible"); + } + + public ExprEvaluator AddOrFrequencyEvaluator + { + get { return _addOrFrequencyEvaluator; } + } + + public override ExprEvaluator GetMethodAggregationEvaluator(bool join, EventType[] typesPerStream) + { + return null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/accessagg/ExprAggMultiFunctionLinearAccessNode.cs b/NEsper.Core/NEsper.Core/epl/expression/accessagg/ExprAggMultiFunctionLinearAccessNode.cs new file mode 100755 index 000000000..b04324fc1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/accessagg/ExprAggMultiFunctionLinearAccessNode.cs @@ -0,0 +1,544 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.events; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.accessagg +{ + [Serializable] + public class ExprAggMultiFunctionLinearAccessNode + : ExprAggregateNodeBase + , ExprEvaluatorEnumeration + , ExprAggregateAccessMultiValueNode + { + private readonly AggregationStateType _stateType; + [NonSerialized] private EventType _containedType; + [NonSerialized] private Type _scalarCollectionComponentType; + + public ExprAggMultiFunctionLinearAccessNode(AggregationStateType stateType) + : base(false) + { + _stateType = stateType; + } + + internal static bool GetIstreamOnly(StreamTypeService streamTypeService, int streamNum) + { + if (streamNum < streamTypeService.EventTypes.Length) + { + return streamTypeService.IsIStreamOnly[streamNum]; + } + // this could happen for match-recognize which has different stream types for selection and for aggregation + return streamTypeService.IsIStreamOnly[0]; + } + + private static ExprValidationException MakeUnboundValidationEx(AggregationStateType stateType) + { + return + new ExprValidationException( + GetErrorPrefix(stateType) + + " requires that the aggregated events provide a remove stream; Please define a data window onto the stream or use 'firstever', 'lastever' or 'nth' instead"); + } + + private static string GetErrorPrefix(AggregationStateType stateType) + { + return ExprAggMultiFunctionUtil.GetErrorPrefix(stateType.ToString().ToLowerInvariant()); + } + + public override AggregationMethodFactory ValidateAggregationChild(ExprValidationContext validationContext) + { + return ValidateAggregationInternal(validationContext, null); + } + + public AggregationMethodFactory ValidateAggregationParamsWBinding( + ExprValidationContext validationContext, + TableMetadataColumnAggregation tableAccessColumn) + { + return ValidateAggregationInternal(validationContext, tableAccessColumn); + } + + private AggregationMethodFactory ValidateAggregationInternal( + ExprValidationContext validationContext, + TableMetadataColumnAggregation optionalBinding) + { + + LinearAggregationFactoryDesc desc; + + var positionalParams = PositionalParams; + // handle table-access expression (state provided, accessor needed) + if (optionalBinding != null) + { + desc = HandleTableAccess(positionalParams, _stateType, validationContext, optionalBinding); + } + else if (validationContext.ExprEvaluatorContext.StatementType == StatementType.CREATE_TABLE) + { + // handle create-table statements (state creator and default accessor, limited to certain options) + desc = HandleCreateTable(positionalParams, _stateType, validationContext); + } + else if (validationContext.IntoTableName != null) + { + // handle into-table (state provided, accessor and agent needed, validation done by factory) + desc = HandleIntoTable(positionalParams, _stateType, validationContext); + } + else + { + // handle standalone + desc = HandleNonIntoTable(positionalParams, _stateType, validationContext); + } + + _containedType = desc.EnumerationEventType; + _scalarCollectionComponentType = desc.ScalarCollectionType; + + return desc.Factory; + } + + private LinearAggregationFactoryDesc HandleNonIntoTable( + ExprNode[] childNodes, + AggregationStateType stateType, + ExprValidationContext validationContext) + { + + var streamTypeService = validationContext.StreamTypeService; + int streamNum; + Type resultType; + ExprEvaluator evaluator; + ExprNode evaluatorIndex = null; + bool istreamOnly; + EventType containedType; + Type scalarCollectionComponentType = null; + + // validate wildcard use + var isWildcard = childNodes.Length == 0 || childNodes.Length > 0 && childNodes[0] is ExprWildcard; + if (isWildcard) + { + ExprAggMultiFunctionUtil.ValidateWildcardStreamNumbers( + validationContext.StreamTypeService, stateType.ToString().ToLowerInvariant()); + streamNum = 0; + containedType = streamTypeService.EventTypes[0]; + resultType = containedType.UnderlyingType; + var tableMetadata = validationContext.TableService.GetTableMetadataFromEventType(containedType); + evaluator = ExprNodeUtility.MakeUnderlyingEvaluator(0, resultType, tableMetadata); + istreamOnly = GetIstreamOnly(streamTypeService, 0); + if ((stateType == AggregationStateType.WINDOW) && istreamOnly && !streamTypeService.IsOnDemandStreams) + { + throw MakeUnboundValidationEx(stateType); + } + } + else if (childNodes.Length > 0 && childNodes[0] is ExprStreamUnderlyingNode) + { + // validate "stream.*" + streamNum = ExprAggMultiFunctionUtil.ValidateStreamWildcardGetStreamNum(childNodes[0]); + istreamOnly = GetIstreamOnly(streamTypeService, streamNum); + if ((stateType == AggregationStateType.WINDOW) && istreamOnly && !streamTypeService.IsOnDemandStreams) + { + throw MakeUnboundValidationEx(stateType); + } + var type = streamTypeService.EventTypes[streamNum]; + containedType = type; + resultType = type.UnderlyingType; + var tableMetadata = validationContext.TableService.GetTableMetadataFromEventType(type); + evaluator = ExprNodeUtility.MakeUnderlyingEvaluator(streamNum, resultType, tableMetadata); + } + else + { + // validate when neither wildcard nor "stream.*" + var child = childNodes[0]; + var streams = ExprNodeUtility.GetIdentStreamNumbers(child); + if (streams.IsEmpty() || (streams.Count > 1)) + { + throw new ExprValidationException( + GetErrorPrefix(stateType) + + " requires that any child expressions evaluate properties of the same stream; Use 'firstever' or 'lastever' or 'nth' instead"); + } + streamNum = streams.First(); + istreamOnly = GetIstreamOnly(streamTypeService, streamNum); + if ((stateType == AggregationStateType.WINDOW) && istreamOnly && !streamTypeService.IsOnDemandStreams) + { + throw MakeUnboundValidationEx(stateType); + } + resultType = childNodes[0].ExprEvaluator.ReturnType; + evaluator = childNodes[0].ExprEvaluator; + if (streamNum >= streamTypeService.EventTypes.Length) + { + containedType = streamTypeService.EventTypes[0]; + } + else + { + containedType = streamTypeService.EventTypes[streamNum]; + } + scalarCollectionComponentType = resultType; + } + + if (childNodes.Length > 1) + { + if (stateType == AggregationStateType.WINDOW) + { + throw new ExprValidationException( + GetErrorPrefix(stateType) + + " does not accept an index expression; Use 'first' or 'last' instead"); + } + evaluatorIndex = childNodes[1]; + if (evaluatorIndex.ExprEvaluator.ReturnType != typeof (int?)) + { + throw new ExprValidationException( + GetErrorPrefix(stateType) + " requires an index expression that returns an integer value"); + } + } + + // determine accessor + AggregationAccessor accessor; + if (evaluatorIndex != null) + { + var isFirst = stateType == AggregationStateType.FIRST; + int constant = -1; + if (evaluatorIndex.IsConstantResult) + { + constant = (int) evaluatorIndex.ExprEvaluator.Evaluate(new EvaluateParams(null, true, null)); + } + accessor = new AggregationAccessorFirstLastIndexWEval( + streamNum, evaluator, evaluatorIndex.ExprEvaluator, constant, isFirst); + } + else + { + if (stateType == AggregationStateType.FIRST) + { + accessor = new AggregationAccessorFirstWEval(streamNum, evaluator); + } + else if (stateType == AggregationStateType.LAST) + { + accessor = new AggregationAccessorLastWEval(streamNum, evaluator); + } + else if (stateType == AggregationStateType.WINDOW) + { + accessor = new AggregationAccessorWindowWEval(streamNum, evaluator, resultType); + } + else + { + throw new IllegalStateException("Access type is undefined or not known as code '" + stateType + "'"); + } + } + + var accessorResultType = resultType; + if (stateType == AggregationStateType.WINDOW) + { + accessorResultType = TypeHelper.GetArrayType(resultType); + } + + var isFafWindow = streamTypeService.IsOnDemandStreams && stateType == AggregationStateType.WINDOW; + var tableMetadataX = validationContext.TableService.GetTableMetadataFromEventType(containedType); + if (tableMetadataX == null && !isFafWindow && (istreamOnly || streamTypeService.IsOnDemandStreams)) + { + var factory = + validationContext.EngineImportService.AggregationFactoryFactory.MakeLinearUnbounded( + validationContext.StatementExtensionSvcContext, this, containedType, accessorResultType, + streamNum); + return new LinearAggregationFactoryDesc(factory, containedType, scalarCollectionComponentType); + } + + var stateKey = new AggregationStateKeyWStream( + streamNum, containedType, AggregationStateTypeWStream.DATAWINDOWACCESS_LINEAR, new ExprNode[0]); + + var stateFactory = + validationContext.EngineImportService.AggregationFactoryFactory.MakeLinear( + validationContext.StatementExtensionSvcContext, this, streamNum); + var factoryX = new ExprAggMultiFunctionLinearAccessNodeFactoryAccess( + this, accessor, accessorResultType, containedType, + stateKey, stateFactory, AggregationAgentDefault.INSTANCE); + var enumerationType = scalarCollectionComponentType == null ? containedType : null; + return new LinearAggregationFactoryDesc(factoryX, enumerationType, scalarCollectionComponentType); + } + + private LinearAggregationFactoryDesc HandleCreateTable( + ExprNode[] childNodes, + AggregationStateType stateType, + ExprValidationContext validationContext) + { + var message = "For tables columns, the " + stateType.GetName().ToLowerInvariant() + + " aggregation function requires the 'window(*)' declaration"; + if (stateType != AggregationStateType.WINDOW) + { + throw new ExprValidationException(message); + } + if (childNodes.Length == 0 || childNodes.Length > 1 || !(childNodes[0] is ExprWildcard)) + { + throw new ExprValidationException(message); + } + if (validationContext.StreamTypeService.StreamNames.Length == 0) + { + throw new ExprValidationException( + GetErrorPrefix(stateType) + " requires that the event type is provided"); + } + var containedType = validationContext.StreamTypeService.EventTypes[0]; + var componentType = containedType.UnderlyingType; + var accessor = new AggregationAccessorWindowNoEval(componentType); + var stateFactory = + validationContext.EngineImportService.AggregationFactoryFactory.MakeLinear( + validationContext.StatementExtensionSvcContext, this, 0); + var factory = new ExprAggMultiFunctionLinearAccessNodeFactoryAccess( + this, accessor, TypeHelper.GetArrayType(componentType), containedType, null, stateFactory, null); + return new LinearAggregationFactoryDesc(factory, factory.ContainedEventType, null); + } + + private LinearAggregationFactoryDesc HandleIntoTable( + ExprNode[] childNodes, + AggregationStateType stateType, + ExprValidationContext validationContext) + { + var message = "For into-table use 'window(*)' or ''window(stream.*)' instead"; + if (stateType != AggregationStateType.WINDOW) + { + throw new ExprValidationException(message); + } + if (childNodes.Length == 0 || childNodes.Length > 1) + { + throw new ExprValidationException(message); + } + if (validationContext.StreamTypeService.StreamNames.Length == 0) + { + throw new ExprValidationException( + GetErrorPrefix(stateType) + " requires that at least one stream is provided"); + } + int streamNum; + if (childNodes[0] is ExprWildcard) + { + if (validationContext.StreamTypeService.StreamNames.Length != 1) + { + throw new ExprValidationException( + GetErrorPrefix(stateType) + " with wildcard requires a single stream"); + } + streamNum = 0; + } + else if (childNodes[0] is ExprStreamUnderlyingNode) + { + var und = (ExprStreamUnderlyingNode) childNodes[0]; + streamNum = und.StreamId; + } + else + { + throw new ExprValidationException(message); + } + var containedType = validationContext.StreamTypeService.EventTypes[streamNum]; + var componentType = containedType.UnderlyingType; + var accessor = new AggregationAccessorWindowNoEval(componentType); + AggregationAgent agent; + if (streamNum == 0) + { + agent = AggregationAgentDefault.INSTANCE; + } + else + { + agent = new AggregationAgentRewriteStream(streamNum); + } + var factory = new ExprAggMultiFunctionLinearAccessNodeFactoryAccess( + this, accessor, TypeHelper.GetArrayType(componentType), containedType, null, null, agent); + return new LinearAggregationFactoryDesc(factory, factory.ContainedEventType, null); + } + + private LinearAggregationFactoryDesc HandleTableAccess( + ExprNode[] childNodes, + AggregationStateType stateType, + ExprValidationContext validationContext, + TableMetadataColumnAggregation tableAccess) + { + if (stateType == AggregationStateType.FIRST || stateType == AggregationStateType.LAST) + { + return HandleTableAccessFirstLast(childNodes, stateType, validationContext, tableAccess); + } + else if (stateType == AggregationStateType.WINDOW) + { + return HandleTableAccessWindow(childNodes, stateType, validationContext, tableAccess); + } + throw new IllegalStateException("Unrecognized type " + stateType); + } + + private LinearAggregationFactoryDesc HandleTableAccessFirstLast( + ExprNode[] childNodes, + AggregationStateType stateType, + ExprValidationContext validationContext, + TableMetadataColumnAggregation tableAccess) + { + var original = (ExprAggMultiFunctionLinearAccessNodeFactoryAccess) tableAccess.Factory; + var resultType = original.ContainedEventType.UnderlyingType; + AggregationAccessor defaultAccessor = + stateType == AggregationStateType.FIRST + ? (AggregationAccessor) AggregationAccessorFirstNoEval.INSTANCE + : (AggregationAccessor) AggregationAccessorLastNoEval.INSTANCE; + if (childNodes.Length == 0) + { + var factoryAccess = new ExprAggMultiFunctionLinearAccessNodeFactoryAccess( + this, defaultAccessor, resultType, original.ContainedEventType, null, null, null); + return new LinearAggregationFactoryDesc(factoryAccess, factoryAccess.ContainedEventType, null); + } + if (childNodes.Length == 1) + { + if (childNodes[0] is ExprWildcard) + { + var factoryAccess = new ExprAggMultiFunctionLinearAccessNodeFactoryAccess( + this, defaultAccessor, resultType, original.ContainedEventType, null, null, null); + return new LinearAggregationFactoryDesc(factoryAccess, factoryAccess.ContainedEventType, null); + } + if (childNodes[0] is ExprStreamUnderlyingNode) + { + throw new ExprValidationException("Stream-wildcard is not allowed for table column access"); + } + // Expressions apply to events held, thereby validate in terms of event value expressions + var paramNode = childNodes[0]; + var streams = TableServiceUtil.StreamTypeFromTableColumn( + tableAccess, validationContext.StreamTypeService.EngineURIQualifier); + var localValidationContext = new ExprValidationContext(streams, validationContext); + paramNode = ExprNodeUtility.GetValidatedSubtree( + ExprNodeOrigin.AGGPARAM, paramNode, localValidationContext); + var paramNodeEval = paramNode.ExprEvaluator; + AggregationAccessor accessor; + if (stateType == AggregationStateType.FIRST) + { + accessor = new AggregationAccessorFirstWEval(0, paramNodeEval); + } + else + { + accessor = new AggregationAccessorLastWEval(0, paramNodeEval); + } + var factory = new ExprAggMultiFunctionLinearAccessNodeFactoryAccess( + this, accessor, paramNodeEval.ReturnType, original.ContainedEventType, null, null, null); + return new LinearAggregationFactoryDesc(factory, factory.ContainedEventType, null); + } + if (childNodes.Length == 2) + { + var isFirst = stateType == AggregationStateType.FIRST; + int constant = -1; + var indexEvalNode = childNodes[1]; + if (indexEvalNode.IsConstantResult) + { + constant = (int) indexEvalNode.ExprEvaluator.Evaluate(new EvaluateParams(null, true, null)); + } + var evaluatorIndex = indexEvalNode.ExprEvaluator; + if (evaluatorIndex.ReturnType != typeof (int?)) + { + throw new ExprValidationException( + GetErrorPrefix(stateType) + + " requires a constant index expression that returns an integer value"); + } + var accessor = new AggregationAccessorFirstLastIndexNoEval(evaluatorIndex, constant, isFirst); + var factory = new ExprAggMultiFunctionLinearAccessNodeFactoryAccess( + this, accessor, resultType, original.ContainedEventType, null, null, null); + return new LinearAggregationFactoryDesc(factory, factory.ContainedEventType, null); + } + throw new ExprValidationException("Invalid number of parameters"); + } + + private LinearAggregationFactoryDesc HandleTableAccessWindow( + ExprNode[] childNodes, + AggregationStateType stateType, + ExprValidationContext validationContext, + TableMetadataColumnAggregation tableAccess) + { + var original = (ExprAggMultiFunctionLinearAccessNodeFactoryAccess) tableAccess.Factory; + if (childNodes.Length == 0 || + (childNodes.Length == 1 && childNodes[0] is ExprWildcard)) + { + var componentType = original.ContainedEventType.UnderlyingType; + var accessor = new AggregationAccessorWindowNoEval(componentType); + var factory = new ExprAggMultiFunctionLinearAccessNodeFactoryAccess( + this, accessor, TypeHelper.GetArrayType(componentType), original.ContainedEventType, null, null, + null); + return new LinearAggregationFactoryDesc(factory, factory.ContainedEventType, null); + } + if (childNodes.Length == 1) + { + // Expressions apply to events held, thereby validate in terms of event value expressions + var paramNode = childNodes[0]; + var streams = TableServiceUtil.StreamTypeFromTableColumn( + tableAccess, validationContext.StreamTypeService.EngineURIQualifier); + var localValidationContext = new ExprValidationContext(streams, validationContext); + paramNode = ExprNodeUtility.GetValidatedSubtree( + ExprNodeOrigin.AGGPARAM, paramNode, localValidationContext); + var paramNodeEval = paramNode.ExprEvaluator; + var factory = new ExprAggMultiFunctionLinearAccessNodeFactoryAccess( + this, + new AggregationAccessorWindowWEval(0, paramNodeEval, paramNodeEval.ReturnType), + TypeHelper.GetArrayType(paramNodeEval.ReturnType), original.ContainedEventType, null, null, null); + return new LinearAggregationFactoryDesc(factory, null, paramNodeEval.ReturnType); + } + throw new ExprValidationException("Invalid number of parameters"); + } + + public override string AggregationFunctionName + { + get { return _stateType.ToString().ToLowerInvariant(); } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + writer.Write(_stateType.ToString().ToLowerInvariant()); + ExprNodeUtility.ToExpressionStringParams(writer, ChildNodes); + } + + public AggregationStateType StateType + { + get { return _stateType; } + } + + public ICollection EvaluateGetROCollectionEvents(EvaluateParams evaluateParams) + { + return base.AggregationResultFuture.GetCollectionOfEvents(base.Column, evaluateParams); + } + + public ICollection EvaluateGetROCollectionScalar(EvaluateParams evaluateParams) + { + return base.AggregationResultFuture.GetCollectionScalar(base.Column, evaluateParams); + } + + public EventType GetEventTypeCollection(EventAdapterService eventAdapterService, int statementId) + { + if (_stateType == AggregationStateType.FIRST || _stateType == AggregationStateType.LAST) + { + return null; + } + return _containedType; + } + + public Type ComponentTypeCollection + { + get { return _scalarCollectionComponentType; } + } + + public EventType GetEventTypeSingle(EventAdapterService eventAdapterService, int statementId) + { + if (_stateType == AggregationStateType.FIRST || _stateType == AggregationStateType.LAST) + { + return _containedType; + } + return null; + } + + public EventBean EvaluateGetEventBean(EvaluateParams evaluateParams) + { + return base.AggregationResultFuture.GetEventBean(base.Column, evaluateParams); + } + + protected override bool EqualsNodeAggregateMethodOnly(ExprAggregateNode node) + { + return false; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/accessagg/ExprAggMultiFunctionLinearAccessNodeFactoryAccess.cs b/NEsper.Core/NEsper.Core/epl/expression/accessagg/ExprAggMultiFunctionLinearAccessNodeFactoryAccess.cs new file mode 100755 index 000000000..630e50063 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/accessagg/ExprAggMultiFunctionLinearAccessNodeFactoryAccess.cs @@ -0,0 +1,102 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.aggregator; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.accessagg +{ + public class ExprAggMultiFunctionLinearAccessNodeFactoryAccess : AggregationMethodFactory + { + private readonly ExprAggMultiFunctionLinearAccessNode _parent; + private readonly AggregationAccessor _accessor; + private readonly Type _accessorResultType; + private readonly EventType _containedEventType; + + private readonly AggregationStateKey _optionalStateKey; + private readonly AggregationStateFactory _optionalStateFactory; + private readonly AggregationAgent _optionalAgent; + + public ExprAggMultiFunctionLinearAccessNodeFactoryAccess(ExprAggMultiFunctionLinearAccessNode parent, AggregationAccessor accessor, Type accessorResultType, EventType containedEventType, AggregationStateKey optionalStateKey, AggregationStateFactory optionalStateFactory, AggregationAgent optionalAgent) + { + _parent = parent; + _accessor = accessor; + _accessorResultType = accessorResultType; + _containedEventType = containedEventType; + _optionalStateKey = optionalStateKey; + _optionalStateFactory = optionalStateFactory; + _optionalAgent = optionalAgent; + } + + public bool IsAccessAggregation + { + get { return true; } + } + + public AggregationMethod Make() + { + throw new UnsupportedOperationException(); + } + + public Type ResultType + { + get { return _accessorResultType; } + } + + public AggregationStateKey GetAggregationStateKey(bool isMatchRecognize) + { + return _optionalStateKey; + } + + public AggregationStateFactory GetAggregationStateFactory(bool isMatchRecognize) + { + return _optionalStateFactory; + } + + public AggregationAccessor Accessor + { + get { return _accessor; } + } + + public ExprAggregateNodeBase AggregationExpression + { + get { return _parent; } + } + + public void ValidateIntoTableCompatible(AggregationMethodFactory intoTableAgg) + { + AggregationMethodFactoryUtil.ValidateAggregationType(this, intoTableAgg); + var other = (ExprAggMultiFunctionLinearAccessNodeFactoryAccess) intoTableAgg; + AggregationMethodFactoryUtil.ValidateEventType(_containedEventType, other.ContainedEventType); + } + + public AggregationAgent AggregationStateAgent + { + get { return _optionalAgent; } + } + + public EventType ContainedEventType + { + get { return _containedEventType; } + } + + public ExprEvaluator GetMethodAggregationEvaluator(Boolean join, EventType[] typesPerStream) + { + return null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/accessagg/ExprAggMultiFunctionSortedMinMaxByNode.cs b/NEsper.Core/NEsper.Core/epl/expression/accessagg/ExprAggMultiFunctionSortedMinMaxByNode.cs new file mode 100755 index 000000000..f19393a4c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/accessagg/ExprAggMultiFunctionSortedMinMaxByNode.cs @@ -0,0 +1,384 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.events; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.accessagg +{ + [Serializable] + public class ExprAggMultiFunctionSortedMinMaxByNode + : ExprAggregateNodeBase + , ExprEvaluatorEnumeration + , ExprAggregateAccessMultiValueNode + { + private readonly bool _max; + private readonly bool _ever; + private readonly bool _sortedwin; + + [NonSerialized] private EventType _containedType; + + public ExprAggMultiFunctionSortedMinMaxByNode(bool max, bool ever, bool sortedwin) + : base(false) + { + _max = max; + _ever = ever; + _sortedwin = sortedwin; + } + + public AggregationMethodFactory ValidateAggregationParamsWBinding( + ExprValidationContext validationContext, + TableMetadataColumnAggregation tableAccessColumn) + { + return ValidateAggregationInternal(validationContext, tableAccessColumn); + } + + public override AggregationMethodFactory ValidateAggregationChild(ExprValidationContext validationContext) + { + return ValidateAggregationInternal(validationContext, null); + } + + private AggregationMethodFactory ValidateAggregationInternal( + ExprValidationContext validationContext, + TableMetadataColumnAggregation optionalBinding) + { + ExprAggMultiFunctionSortedMinMaxByNodeFactory factory; + + // handle table-access expression (state provided, accessor needed) + if (optionalBinding != null) + { + factory = HandleTableAccess(optionalBinding); + } + else if (validationContext.ExprEvaluatorContext.StatementType == StatementType.CREATE_TABLE) + { + // handle create-table statements (state creator and default accessor, limited to certain options) + factory = HandleCreateTable(validationContext); + } + else if (validationContext.IntoTableName != null) + { + // handle into-table (state provided, accessor and agent needed, validation done by factory) + factory = HandleIntoTable(validationContext); + } + else + { + // handle standalone + factory = HandleNonTable(validationContext); + } + + _containedType = factory.ContainedEventType; + return factory; + } + + private ExprAggMultiFunctionSortedMinMaxByNodeFactory HandleNonTable(ExprValidationContext validationContext) + { + var positionalParams = PositionalParams; + if (positionalParams.Length == 0) + { + throw new ExprValidationException("Missing the sort criteria expression"); + } + + // validate that the streams referenced in the criteria are a single stream's + var streams = ExprNodeUtility.GetIdentStreamNumbers(positionalParams[0]); + if (streams.Count > 1 || streams.IsEmpty()) + { + throw new ExprValidationException( + ErrorPrefix + " requires that any parameter expressions evaluate properties of the same stream"); + } + var streamNum = streams.First(); + + // validate that there is a remove stream, use "ever" if not + var forceEver = false; + if (!_ever && ExprAggMultiFunctionLinearAccessNode.GetIstreamOnly(validationContext.StreamTypeService, streamNum)) + { + if (_sortedwin) + { + throw new ExprValidationException( + ErrorPrefix + " requires that a data window is declared for the stream"); + } + forceEver = true; + } + + // determine typing and evaluation + _containedType = validationContext.StreamTypeService.EventTypes[streamNum]; + + var componentType = _containedType.UnderlyingType; + var accessorResultType = componentType; + AggregationAccessor accessor; + var tableMetadata = validationContext.TableService.GetTableMetadataFromEventType(_containedType); + if (!_sortedwin) + { + if (tableMetadata != null) + { + accessor = new AggregationAccessorMinMaxByTable(_max, tableMetadata); + } + else + { + accessor = new AggregationAccessorMinMaxByNonTable(_max); + } + } + else + { + if (tableMetadata != null) + { + accessor = new AggregationAccessorSortedTable(_max, componentType, tableMetadata); + } + else + { + accessor = new AggregationAccessorSortedNonTable(_max, componentType); + } + accessorResultType = TypeHelper.GetArrayType(accessorResultType); + } + + var criteriaExpressions = CriteriaExpressions; + + AggregationStateTypeWStream type; + if (_ever) + { + type = _max ? AggregationStateTypeWStream.MAXEVER : AggregationStateTypeWStream.MINEVER; + } + else + { + type = AggregationStateTypeWStream.SORTED; + } + var stateKey = new AggregationStateKeyWStream(streamNum, _containedType, type, criteriaExpressions.First); + + var stateFactoryFactory = new + SortedAggregationStateFactoryFactory( + validationContext.EngineImportService, validationContext.StatementExtensionSvcContext, + ExprNodeUtility.GetEvaluators(criteriaExpressions.First), + criteriaExpressions.Second, _ever, streamNum, this); + + return new ExprAggMultiFunctionSortedMinMaxByNodeFactory( + this, accessor, accessorResultType, _containedType, stateKey, stateFactoryFactory, + AggregationAgentDefault.INSTANCE); + } + + private ExprAggMultiFunctionSortedMinMaxByNodeFactory HandleIntoTable(ExprValidationContext validationContext) + { + int streamNum; + var positionalParams = PositionalParams; + if (positionalParams.Length == 0 || + (positionalParams.Length == 1 && positionalParams[0] is ExprWildcard)) + { + ExprAggMultiFunctionUtil.ValidateWildcardStreamNumbers( + validationContext.StreamTypeService, AggregationFunctionName); + streamNum = 0; + } + else if (positionalParams.Length == 1 && positionalParams[0] is ExprStreamUnderlyingNode) + { + streamNum = ExprAggMultiFunctionUtil.ValidateStreamWildcardGetStreamNum(positionalParams[0]); + } + else if (positionalParams.Length > 0) + { + throw new ExprValidationException("When specifying into-table a sort expression cannot be provided"); + } + else + { + streamNum = 0; + } + + var containedType = validationContext.StreamTypeService.EventTypes[streamNum]; + var componentType = containedType.UnderlyingType; + var accessorResultType = componentType; + AggregationAccessor accessor; + if (!_sortedwin) + { + accessor = new AggregationAccessorMinMaxByNonTable(_max); + } + else + { + accessor = new AggregationAccessorSortedNonTable(_max, componentType); + accessorResultType = TypeHelper.GetArrayType(accessorResultType); + } + + AggregationAgent agent = AggregationAgentDefault.INSTANCE; + if (streamNum != 0) + { + agent = new AggregationAgentRewriteStream(streamNum); + } + + return new ExprAggMultiFunctionSortedMinMaxByNodeFactory( + this, accessor, accessorResultType, containedType, null, null, agent); + } + + private ExprAggMultiFunctionSortedMinMaxByNodeFactory HandleCreateTable(ExprValidationContext validationContext) + { + var positionalParams = PositionalParams; + if (positionalParams.Length == 0) + { + throw new ExprValidationException("Missing the sort criteria expression"); + } + + var message = "For tables columns, the aggregation function requires the 'sorted(*)' declaration"; + if (!_sortedwin && !_ever) + { + throw new ExprValidationException(message); + } + if (validationContext.StreamTypeService.StreamNames.Length == 0) + { + throw new ExprValidationException("'Sorted' requires that the event type is provided"); + } + var containedType = validationContext.StreamTypeService.EventTypes[0]; + var componentType = containedType.UnderlyingType; + var criteriaExpressions = CriteriaExpressions; + var accessorResultType = componentType; + AggregationAccessor accessor; + if (!_sortedwin) + { + accessor = new AggregationAccessorMinMaxByNonTable(_max); + } + else + { + accessor = new AggregationAccessorSortedNonTable(_max, componentType); + accessorResultType = TypeHelper.GetArrayType(accessorResultType); + } + var stateFactoryFactory = new + SortedAggregationStateFactoryFactory( + validationContext.EngineImportService, validationContext.StatementExtensionSvcContext, + ExprNodeUtility.GetEvaluators(criteriaExpressions.First), + criteriaExpressions.Second, _ever, 0, this); + return new ExprAggMultiFunctionSortedMinMaxByNodeFactory( + this, accessor, accessorResultType, containedType, null, stateFactoryFactory, null); + } + + private Pair CriteriaExpressions + { + get + { + // determine ordering ascending/descending and build criteria expression without "asc" marker + var positionalParams = PositionalParams; + var criteriaExpressions = new ExprNode[positionalParams.Length]; + var sortDescending = new bool[positionalParams.Length]; + for (var i = 0; i < positionalParams.Length; i++) + { + var parameter = positionalParams[i]; + criteriaExpressions[i] = parameter; + if (parameter is ExprOrderedExpr) + { + var ordered = (ExprOrderedExpr) parameter; + sortDescending[i] = ordered.IsDescending; + if (!ordered.IsDescending) + { + criteriaExpressions[i] = ordered.ChildNodes[0]; + } + } + } + return new Pair(criteriaExpressions, sortDescending); + } + } + + private ExprAggMultiFunctionSortedMinMaxByNodeFactory HandleTableAccess( + TableMetadataColumnAggregation tableAccess) + { + var factory = (ExprAggMultiFunctionSortedMinMaxByNodeFactory) tableAccess.Factory; + AggregationAccessor accessor; + var componentType = factory.ContainedEventType.UnderlyingType; + var accessorResultType = componentType; + if (!_sortedwin) + { + accessor = new AggregationAccessorMinMaxByNonTable(_max); + } + else + { + accessor = new AggregationAccessorSortedNonTable(_max, componentType); + accessorResultType = TypeHelper.GetArrayType(accessorResultType); + } + return new ExprAggMultiFunctionSortedMinMaxByNodeFactory( + this, accessor, accessorResultType, factory.ContainedEventType, null, null, null); + } + + public override string AggregationFunctionName + { + get + { + if (_sortedwin) + { + return "sorted"; + } + if (_ever) + { + return _max ? "maxbyever" : "minbyever"; + } + return _max ? "maxby" : "minby"; + } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + writer.Write(AggregationFunctionName); + ExprNodeUtility.ToExpressionStringParams(writer, PositionalParams); + } + + public ICollection EvaluateGetROCollectionEvents(EvaluateParams evaluateParams) + { + return base.AggregationResultFuture.GetCollectionOfEvents(base.Column, evaluateParams); + } + + public ICollection EvaluateGetROCollectionScalar(EvaluateParams evaluateParams) + { + return null; + } + + public EventType GetEventTypeCollection(EventAdapterService eventAdapterService, int statementId) + { + if (!_sortedwin) + { + return null; + } + return _containedType; + } + + public Type ComponentTypeCollection + { + get { return null; } + } + + public EventType GetEventTypeSingle(EventAdapterService eventAdapterService, int statementId) + { + if (_sortedwin) + { + return null; + } + return _containedType; + } + + public EventBean EvaluateGetEventBean(EvaluateParams evaluateParams) + { + return base.AggregationResultFuture.GetEventBean(base.Column, evaluateParams); + } + + public bool IsMax + { + get { return _max; } + } + + protected override bool EqualsNodeAggregateMethodOnly(ExprAggregateNode node) + { + return false; + } + + private string ErrorPrefix + { + get { return "The '" + AggregationFunctionName + "' aggregation function"; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/accessagg/ExprAggMultiFunctionSortedMinMaxByNodeFactory.cs b/NEsper.Core/NEsper.Core/epl/expression/accessagg/ExprAggMultiFunctionSortedMinMaxByNodeFactory.cs new file mode 100755 index 000000000..5e9f02ca6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/accessagg/ExprAggMultiFunctionSortedMinMaxByNodeFactory.cs @@ -0,0 +1,111 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.aggregator; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.accessagg +{ + public class ExprAggMultiFunctionSortedMinMaxByNodeFactory : AggregationMethodFactory + { + private readonly ExprAggMultiFunctionSortedMinMaxByNode _parent; + private readonly AggregationAccessor _accessor; + private readonly Type _accessorResultType; + private readonly EventType _containedEventType; + + private readonly AggregationStateKey _optionalStateKey; + private readonly SortedAggregationStateFactoryFactory _optionalStateFactory; + private readonly AggregationAgent _optionalAgent; + + public ExprAggMultiFunctionSortedMinMaxByNodeFactory(ExprAggMultiFunctionSortedMinMaxByNode parent, AggregationAccessor accessor, Type accessorResultType, EventType containedEventType, AggregationStateKey optionalStateKey, SortedAggregationStateFactoryFactory optionalStateFactory, AggregationAgent optionalAgent) + { + _parent = parent; + _accessor = accessor; + _accessorResultType = accessorResultType; + _containedEventType = containedEventType; + _optionalStateKey = optionalStateKey; + _optionalStateFactory = optionalStateFactory; + _optionalAgent = optionalAgent; + } + + public bool IsAccessAggregation + { + get { return true; } + } + + public AggregationMethod Make() + { + throw new UnsupportedOperationException(); + } + + public Type ResultType + { + get { return _accessorResultType; } + } + + public AggregationStateKey GetAggregationStateKey(bool isMatchRecognize) + { + return _optionalStateKey; + } + + public AggregationStateFactory GetAggregationStateFactory(bool isMatchRecognize) + { + if (isMatchRecognize || _optionalStateFactory == null) + { + return null; + } + return _optionalStateFactory.MakeFactory(); + } + + public AggregationAccessor Accessor + { + get { return _accessor; } + } + + public ExprAggregateNodeBase AggregationExpression + { + get { return _parent; } + } + + public void ValidateIntoTableCompatible(AggregationMethodFactory intoTableAgg) + { + AggregationMethodFactoryUtil.ValidateAggregationType(this, intoTableAgg); + var other = (ExprAggMultiFunctionSortedMinMaxByNodeFactory) intoTableAgg; + AggregationMethodFactoryUtil.ValidateEventType(_containedEventType, other.ContainedEventType); + AggregationMethodFactoryUtil.ValidateAggFuncName(_parent.AggregationFunctionName, other.Parent.AggregationFunctionName); + } + + public AggregationAgent AggregationStateAgent + { + get { return _optionalAgent; } + } + + public EventType ContainedEventType + { + get { return _containedEventType; } + } + + public ExprAggMultiFunctionSortedMinMaxByNode Parent + { + get { return _parent; } + } + + public ExprEvaluator GetMethodAggregationEvaluator(Boolean join, EventType[] typesPerStream) + { + return null; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/accessagg/ExprAggMultiFunctionUtil.cs b/NEsper.Core/NEsper.Core/epl/expression/accessagg/ExprAggMultiFunctionUtil.cs new file mode 100755 index 000000000..ae5c9ece4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/accessagg/ExprAggMultiFunctionUtil.cs @@ -0,0 +1,60 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.accessagg +{ + public class ExprAggMultiFunctionUtil + { + public static int ValidateStreamWildcardGetStreamNum(ExprNode node) + + { + if (!(node is ExprStreamUnderlyingNode)) + { + throw new IllegalStateException("Expression not stream-wildcard"); + } + + var und = (ExprStreamUnderlyingNode) node; + if (und.StreamId == -1) { + throw new ExprValidationException("The expression does not resolve to a stream"); + } + return und.StreamId; + } + + public static void ValidateWildcardStreamNumbers(StreamTypeService streamTypeService, string aggFuncName) + { + CheckWildcardNotJoinOrSubquery(streamTypeService, aggFuncName); + CheckWildcardHasStream(streamTypeService, aggFuncName); + } + + public static void CheckWildcardNotJoinOrSubquery(StreamTypeService streamTypeService, string aggFuncName) + { + if (streamTypeService.StreamNames.Length > 1) { + throw new ExprValidationException(GetErrorPrefix(aggFuncName) + " requires that in joins or subqueries the stream-wildcard (stream-alias.*) syntax is used instead"); + } + } + + private static void CheckWildcardHasStream(StreamTypeService streamTypeService, string aggFuncName) + { + if (streamTypeService.StreamNames.Length == 0) { // could be the case for + throw new ExprValidationException(GetErrorPrefix(aggFuncName) + " requires that at least one stream is provided"); + } + } + + public static string GetErrorPrefix(string aggFuncName) + { + return "The '" + aggFuncName + "' aggregation function"; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/accessagg/ExprAggregateAccessMultiValueNode.cs b/NEsper.Core/NEsper.Core/epl/expression/accessagg/ExprAggregateAccessMultiValueNode.cs new file mode 100755 index 000000000..2e62e5aea --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/accessagg/ExprAggregateAccessMultiValueNode.cs @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.table.mgmt; + +namespace com.espertech.esper.epl.expression.accessagg +{ + public interface ExprAggregateAccessMultiValueNode : ExprEvaluatorEnumeration + { + void ValidatePositionals(); + AggregationMethodFactory ValidateAggregationParamsWBinding(ExprValidationContext context, TableMetadataColumnAggregation tableAccessColumn); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/accessagg/ExprPlugInAggMultiFunctionNode.cs b/NEsper.Core/NEsper.Core/epl/expression/accessagg/ExprPlugInAggMultiFunctionNode.cs new file mode 100755 index 000000000..96f046336 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/accessagg/ExprPlugInAggMultiFunctionNode.cs @@ -0,0 +1,128 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.events; +using com.espertech.esper.plugin; + +namespace com.espertech.esper.epl.expression.accessagg +{ + /// + /// Represents a custom aggregation function in an expresson tree. + /// + [Serializable] + public class ExprPlugInAggMultiFunctionNode + : ExprAggregateNodeBase + , ExprEvaluatorEnumeration + , ExprAggregateAccessMultiValueNode + , ExprAggregationPlugInNodeMarker + { + private readonly PlugInAggregationMultiFunctionFactory _pluginAggregationMultiFunctionFactory; + private readonly string _functionName; + private readonly ConfigurationPlugInAggregationMultiFunction _config; + [NonSerialized] + private ExprPlugInAggMultiFunctionNodeFactory _factory; + + /// + /// Ctor. + /// + /// flag indicating unique or non-unique value aggregation + /// The configuration. + /// the factory + /// is the aggregation function name + public ExprPlugInAggMultiFunctionNode(bool distinct, ConfigurationPlugInAggregationMultiFunction config, PlugInAggregationMultiFunctionFactory pluginAggregationMultiFunctionFactory, string functionName) + : base(distinct) + { + _pluginAggregationMultiFunctionFactory = pluginAggregationMultiFunctionFactory; + _functionName = functionName; + _config = config; + } + + public override AggregationMethodFactory ValidateAggregationChild(ExprValidationContext validationContext) + { + ValidatePositionals(); + return ValidateAggregationParamsWBinding(validationContext, null); + } + + public AggregationMethodFactory ValidateAggregationParamsWBinding(ExprValidationContext validationContext, TableMetadataColumnAggregation tableAccessColumn) + { + // validate using the context provided by the 'outside' streams to determine parameters + // at this time 'inside' expressions like 'window(intPrimitive)' are not handled + ExprNodeUtility.GetValidatedSubtree(ExprNodeOrigin.AGGPARAM, ChildNodes, validationContext); + return ValidateAggregationInternal(validationContext, tableAccessColumn); + } + + private AggregationMethodFactory ValidateAggregationInternal(ExprValidationContext validationContext, TableMetadataColumnAggregation optionalTableColumn) + { + var ctx = new PlugInAggregationMultiFunctionValidationContext( + _functionName, + validationContext.StreamTypeService.EventTypes, PositionalParams, + validationContext.StreamTypeService.EngineURIQualifier, + validationContext.StatementName, + validationContext, _config, optionalTableColumn, ChildNodes); + + var handlerPlugin = _pluginAggregationMultiFunctionFactory.ValidateGetHandler(ctx); + _factory = new ExprPlugInAggMultiFunctionNodeFactory(this, handlerPlugin, validationContext.EngineImportService.AggregationFactoryFactory, validationContext.StatementExtensionSvcContext); + return _factory; + } + + public override string AggregationFunctionName + { + get { return _functionName; } + } + + public ICollection EvaluateGetROCollectionEvents(EvaluateParams evaluateParams) + { + return AggregationResultFuture.GetCollectionOfEvents(Column, evaluateParams); + } + + public ICollection EvaluateGetROCollectionScalar(EvaluateParams evaluateParams) + { + var result = AggregationResultFuture.GetValue(Column, evaluateParams.ExprEvaluatorContext.AgentInstanceId, evaluateParams); + if (result == null) + { + return null; + } + + return result.Unwrap(); + } + + public EventType GetEventTypeCollection(EventAdapterService eventAdapterService, int statementId) + { + return _factory.EventTypeCollection; + } + + public Type ComponentTypeCollection + { + get { return _factory.ComponentTypeCollection; } + } + + public EventType GetEventTypeSingle(EventAdapterService eventAdapterService, int statementId) + { + return _factory.EventTypeSingle; + } + + public EventBean EvaluateGetEventBean(EvaluateParams evaluateParams) + { + return AggregationResultFuture.GetEventBean(Column, evaluateParams); + } + + protected override bool EqualsNodeAggregateMethodOnly(ExprAggregateNode node) + { + return false; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/accessagg/ExprPlugInAggMultiFunctionNodeFactory.cs b/NEsper.Core/NEsper.Core/epl/expression/accessagg/ExprPlugInAggMultiFunctionNodeFactory.cs new file mode 100755 index 000000000..6b649de59 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/accessagg/ExprPlugInAggMultiFunctionNodeFactory.cs @@ -0,0 +1,144 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.aggregator; +using com.espertech.esper.epl.agg.factory; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.rettype; +using com.espertech.esper.plugin; + +using AggregationMethodFactoryUtil = com.espertech.esper.epl.agg.service.AggregationMethodFactoryUtil; + +namespace com.espertech.esper.epl.expression.accessagg +{ + public class ExprPlugInAggMultiFunctionNodeFactory : AggregationMethodFactory + { + private readonly ExprPlugInAggMultiFunctionNode _parent; + private readonly PlugInAggregationMultiFunctionHandler _handlerPlugin; + private readonly AggregationFactoryFactory _aggregationFactoryFactory; + private readonly StatementExtensionSvcContext _statementExtensionSvcContext; + + private EPType _returnType; + + public ExprPlugInAggMultiFunctionNodeFactory(ExprPlugInAggMultiFunctionNode parent, PlugInAggregationMultiFunctionHandler handlerPlugin, AggregationFactoryFactory aggregationFactoryFactory, StatementExtensionSvcContext statementExtensionSvcContext) + { + _handlerPlugin = handlerPlugin; + _parent = parent; + _aggregationFactoryFactory = aggregationFactoryFactory; + _statementExtensionSvcContext = statementExtensionSvcContext; + } + + public bool IsAccessAggregation + { + get { return true; } + } + + public AggregationMethod Make() + { + return null; + } + + public AggregationStateKey GetAggregationStateKey(bool isMatchRecognize) + { + return _handlerPlugin.AggregationStateUniqueKey; + } + + public AggregationStateFactory GetAggregationStateFactory(bool isMatchRecognize) + { + return _aggregationFactoryFactory.MakePlugInAccess(_statementExtensionSvcContext, this); + } + + public AggregationAccessor Accessor + { + get { return _handlerPlugin.Accessor; } + } + + public Type ResultType + { + get + { + ObtainReturnType(); + return _returnType.GetNormalizedClass(); + } + } + + public PlugInAggregationMultiFunctionHandler HandlerPlugin + { + get { return _handlerPlugin; } + } + + public Type ComponentTypeCollection + { + get + { + ObtainReturnType(); + return EPTypeHelper.GetClassMultiValued(_returnType); + } + } + + public EventType EventTypeSingle + { + get + { + ObtainReturnType(); + return EPTypeHelper.GetEventTypeSingleValued(_returnType); + } + } + + public EventType EventTypeCollection + { + get + { + ObtainReturnType(); + return EPTypeHelper.GetEventTypeMultiValued(_returnType); + } + } + + public ExprAggregateNodeBase AggregationExpression + { + get { return _parent; } + } + + private void ObtainReturnType() + { + if (_returnType == null) { + _returnType = _handlerPlugin.ReturnType; + } + } + + public void ValidateIntoTableCompatible(AggregationMethodFactory intoTableAgg) + { + AggregationMethodFactoryUtil.ValidateAggregationType(this, intoTableAgg); + var that = (ExprPlugInAggMultiFunctionNodeFactory) intoTableAgg; + if (!GetAggregationStateKey(false).Equals(that.GetAggregationStateKey(false))) { + throw new ExprValidationException("Mismatched state key"); + } + } + + public AggregationAgent AggregationStateAgent + { + get + { + var ctx = new PlugInAggregationMultiFunctionAgentContext(_parent.ChildNodes); + return _handlerPlugin.GetAggregationAgent(ctx); + } + } + + public ExprEvaluator GetMethodAggregationEvaluator(bool join, EventType[] typesPerStream) + { + return null; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/accessagg/LinearAggregationFactoryDesc.cs b/NEsper.Core/NEsper.Core/epl/expression/accessagg/LinearAggregationFactoryDesc.cs new file mode 100755 index 000000000..c35db82df --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/accessagg/LinearAggregationFactoryDesc.cs @@ -0,0 +1,31 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.agg.service; + +namespace com.espertech.esper.epl.expression.accessagg +{ + public class LinearAggregationFactoryDesc + { + public LinearAggregationFactoryDesc(AggregationMethodFactory factory, EventType enumerationEventType, Type scalarCollectionType) + { + Factory = factory; + EnumerationEventType = enumerationEventType; + ScalarCollectionType = scalarCollectionType; + } + + public AggregationMethodFactory Factory { get; private set; } + + public EventType EnumerationEventType { get; private set; } + + public Type ScalarCollectionType { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/accessagg/SortedAggregationStateFactoryFactory.cs b/NEsper.Core/NEsper.Core/epl/expression/accessagg/SortedAggregationStateFactoryFactory.cs new file mode 100755 index 000000000..74e3f6481 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/accessagg/SortedAggregationStateFactoryFactory.cs @@ -0,0 +1,62 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.service; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.accessagg +{ + public class SortedAggregationStateFactoryFactory + { + private readonly EngineImportService _engineImportService; + private readonly StatementExtensionSvcContext _statementExtensionSvcContext; + private readonly ExprEvaluator[] _evaluators; + private readonly bool[] _sortDescending; + private readonly bool _ever; + private readonly int _streamNum; + private readonly ExprAggMultiFunctionSortedMinMaxByNode _parent; + + public SortedAggregationStateFactoryFactory( + EngineImportService engineImportService, + StatementExtensionSvcContext statementExtensionSvcContext, + ExprEvaluator[] evaluators, + bool[] sortDescending, + bool ever, + int streamNum, + ExprAggMultiFunctionSortedMinMaxByNode parent) + { + _engineImportService = engineImportService; + _statementExtensionSvcContext = statementExtensionSvcContext; + _evaluators = evaluators; + _sortDescending = sortDescending; + _ever = ever; + _streamNum = streamNum; + _parent = parent; + } + + public AggregationStateFactory MakeFactory() + { + var sortUsingCollator = _engineImportService.IsSortUsingCollator; + var comparator = CollectionUtil.GetComparator(_evaluators, sortUsingCollator, _sortDescending); + + if (_ever) + { + var specX = new AggregationStateMinMaxByEverSpec( + _streamNum, _evaluators, _parent.IsMax, comparator, null); + return _engineImportService.AggregationFactoryFactory.MakeMinMaxEver(_statementExtensionSvcContext, _parent, specX); + } + + var spec = new AggregationStateSortedSpec(_streamNum, _evaluators, comparator, null); + return _engineImportService.AggregationFactoryFactory.MakeSorted(_statementExtensionSvcContext, _parent, spec); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/baseagg/ExprAggregateLocalGroupByDesc.cs b/NEsper.Core/NEsper.Core/epl/expression/baseagg/ExprAggregateLocalGroupByDesc.cs new file mode 100755 index 000000000..394ad2c61 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/baseagg/ExprAggregateLocalGroupByDesc.cs @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.baseagg +{ + [Serializable] + public class ExprAggregateLocalGroupByDesc + { + public ExprAggregateLocalGroupByDesc(IList partitionExpressions) + { + PartitionExpressions = partitionExpressions; + } + + public IList PartitionExpressions { get; private set; } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/baseagg/ExprAggregateNode.cs b/NEsper.Core/NEsper.Core/epl/expression/baseagg/ExprAggregateNode.cs new file mode 100755 index 000000000..60b54faf6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/baseagg/ExprAggregateNode.cs @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.baseagg +{ + /// + /// Base expression node that represents an aggregation function such as 'sum' or 'count'. + /// + public interface ExprAggregateNode : ExprEvaluator, ExprNode + { + AggregationMethodFactory Factory { get; } + void SetAggregationResultFuture(AggregationResultFuture aggregationResultFuture, int column); + bool IsDistinct { get; } + ExprAggregateLocalGroupByDesc OptionalLocalGroupBy { get; } + void ValidatePositionals(); + ExprNode[] PositionalParams { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/baseagg/ExprAggregateNodeBase.cs b/NEsper.Core/NEsper.Core/epl/expression/baseagg/ExprAggregateNodeBase.cs new file mode 100755 index 000000000..539313cb2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/baseagg/ExprAggregateNodeBase.cs @@ -0,0 +1,306 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +using com.espertech.esper.compat; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.agg.aggregator; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.baseagg +{ + /// + /// Base expression node that represents an aggregation function such as 'sum' or 'count'. + /// + /// In terms of validation each concrete aggregation node must implement it's own validation. + /// + /// In terms of evaluation this base class will ask the assigned for the current state, + /// using a column number assigned to the node. + /// + /// Concrete subclasses must supply an aggregation state _prototype node that reflects + /// each group's (there may be group-by critera) current aggregation state. + /// + [Serializable] + public abstract class ExprAggregateNodeBase + : ExprNodeBase + , ExprEvaluator + , ExprAggregateNode + { + [NonSerialized] protected AggregationResultFuture AggregationResultFuture; + protected int Column; + [NonSerialized] private AggregationMethodFactory _aggregationMethodFactory; + + /// + /// Indicator for whether the aggregation is distinct - i.e. only unique values are considered. + /// + private bool _isDistinct; + + private ExprAggregateLocalGroupByDesc _optionalAggregateLocalGroupByDesc; + private ExprNode[] _positionalParams; + + /// + /// Returns the aggregation function name for representation in a generate expression string. + /// + /// aggregation function name + public abstract string AggregationFunctionName { get; } + + /// + /// Return true if a expression aggregate node semantically equals the current node, or false if not. + /// + /// For use by the equalsNode implementation which compares the distinct flag. + /// + /// to compare to + /// true if semantically equal, or false if not equals + protected abstract bool EqualsNodeAggregateMethodOnly(ExprAggregateNode node); + + /// + /// Gives the aggregation node a chance to validate the sub-expression types. + /// + /// validation information + /// aggregation function factory to use + /// com.espertech.esper.epl.expression.core.ExprValidationException when expression validation failed + public abstract AggregationMethodFactory ValidateAggregationChild(ExprValidationContext validationContext); + + /// + /// Ctor. + /// + /// sets the flag indicatating whether only unique values should be aggregated + protected ExprAggregateNodeBase(bool distinct) + { + _isDistinct = distinct; + } + + /// + /// Gets the positional parameters. + /// + /// + /// The positional parameters. + /// + public ExprNode[] PositionalParams + { + get { return _positionalParams; } + private set { _positionalParams = value; } + } + + public override ExprEvaluator ExprEvaluator + { + get { return this; } + } + + public override bool IsConstantResult + { + get { return false; } + } + + public override ExprNode Validate(ExprValidationContext validationContext) + { + ValidatePositionals(); + _aggregationMethodFactory = ValidateAggregationChild(validationContext); + if (validationContext.ExprEvaluatorContext.StatementType == StatementType.CREATE_TABLE && _optionalAggregateLocalGroupByDesc != null) + { + throw new ExprValidationException("The 'group_by' parameter is not allowed in create-table statements"); + } + return null; + } + + public void ValidatePositionals() + { + var paramDesc = ExprAggregateNodeUtil.GetValidatePositionalParams(ChildNodes, !(this is ExprAggregationPlugInNodeMarker)); + _optionalAggregateLocalGroupByDesc = paramDesc.OptLocalGroupBy; + if (_optionalAggregateLocalGroupByDesc != null) { + ExprNodeUtility.ValidateNoSpecialsGroupByExpressions(_optionalAggregateLocalGroupByDesc.PartitionExpressions); + } + + _positionalParams = paramDesc.PositionalParams; + } + + public virtual Type ReturnType + { + get + { + if (_aggregationMethodFactory == null) + { + throw new IllegalStateException("Aggregation method has not been set"); + } + return _aggregationMethodFactory.ResultType; + } + } + + /// + /// Returns the aggregation state factory for use in grouping aggregation states per group-by keys. + /// + /// _prototype aggregation state as a factory for aggregation states per group-by key value + public AggregationMethodFactory Factory + { + get + { + if (_aggregationMethodFactory == null) + { + throw new IllegalStateException("Aggregation method has not been set"); + } + return _aggregationMethodFactory; + } + } + + /// + /// Assigns to the node the future which can be queried for the current aggregation state at evaluation time. + /// + /// future containing state + /// column to hand to future for easy access + public void SetAggregationResultFuture(AggregationResultFuture aggregationResultFuture, int column) + { + AggregationResultFuture = aggregationResultFuture; + Column = column; + } + + public virtual object Evaluate(EvaluateParams evaluateParams) + { + var exprEvaluatorContext = evaluateParams.ExprEvaluatorContext; + + if (InstrumentationHelper.ENABLED) { + var value = AggregationResultFuture.GetValue(Column, exprEvaluatorContext.AgentInstanceId, evaluateParams); + InstrumentationHelper.Get().QaExprAggValue(this, value); + return value; + } + return AggregationResultFuture.GetValue(Column, exprEvaluatorContext.AgentInstanceId, evaluateParams); + } + + /// + /// Returns true if the aggregation node is only aggregatig distinct values, or false if + /// aggregating all values. + /// + /// true if 'distinct' keyword was given, false if not + public bool IsDistinct + { + get { return _isDistinct; } + } + + public override bool EqualsNode(ExprNode node) + { + var other = node as ExprAggregateNode; + if (other == null) + { + return false; + } + + return other.IsDistinct == _isDistinct && EqualsNodeAggregateMethodOnly(other); + } + + /// + /// For use by implementing classes, validates the aggregation node expecting + /// a single numeric-type child node. + /// + /// if set to true [has filter]. + /// + /// numeric type of single child + /// + protected Type ValidateNumericChildAllowFilter(bool hasFilter) + { + if (_positionalParams.Length == 0 || _positionalParams.Length > 2) + { + throw MakeExceptionExpectedParamNum(1, 2); + } + + // validate child expression (filter expression is actually always the first expression) + var child = _positionalParams[0]; + if (hasFilter) + { + ValidateFilter(_positionalParams[1].ExprEvaluator); + } + + var childType = child.ExprEvaluator.ReturnType; + if (childType.IsNumeric() == false) + { + throw new ExprValidationException(string.Format( + "Implicit conversion from datatype '{0}' to numeric is not allowed for aggregation function '{1}'", + childType == null ? "null" : childType.Name, + AggregationFunctionName)); + } + + return childType; + } + + protected ExprValidationException MakeExceptionExpectedParamNum(int lower, int upper) + { + var message = "The '" + AggregationFunctionName + "' function expects "; + if (lower == 0 && upper == 0) + { + message += "no parameters"; + } + else if (lower == upper) + { + message += lower + " parameters"; + } + else + { + message += "at least " + lower + " and up to " + upper + " parameters"; + } + return new ExprValidationException(message); + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + writer.Write(AggregationFunctionName); + writer.Write('('); + + if (_isDistinct) { + writer.Write("distinct "); + } + + if (ChildNodes.Count> 0) { + ChildNodes[0].ToEPL(writer, Precedence); + + var delimiter = ","; + for (var i = 1 ; i < ChildNodes.Count; i++) { + writer.Write(delimiter); + delimiter = ","; + ChildNodes[i].ToEPL(writer, Precedence); + } + } + else { + if (IsExprTextWildcardWhenNoParams) { + writer.Write('*'); + } + } + + writer.Write(')'); + } + + public override ExprPrecedenceEnum Precedence + { + get { return ExprPrecedenceEnum.MINIMUM; } + } + + public void ValidateFilter(ExprEvaluator filterEvaluator) + { + if (filterEvaluator.ReturnType.GetBoxedType() != typeof(bool?)) + { + throw new ExprValidationException("Invalid filter expression parameter to the aggregation function '" + + AggregationFunctionName + + "' is expected to return a boolean value but returns " + TypeHelper.GetTypeNameFullyQualPretty(filterEvaluator.ReturnType)); + } + } + + public ExprAggregateLocalGroupByDesc OptionalLocalGroupBy + { + get { return _optionalAggregateLocalGroupByDesc; } + internal set { _optionalAggregateLocalGroupByDesc = value; } + } + + protected virtual bool IsExprTextWildcardWhenNoParams + { + get { return true; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/baseagg/ExprAggregateNodeGroupKey.cs b/NEsper.Core/NEsper.Core/epl/expression/baseagg/ExprAggregateNodeGroupKey.cs new file mode 100755 index 000000000..40c1f9fc0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/baseagg/ExprAggregateNodeGroupKey.cs @@ -0,0 +1,89 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +using com.espertech.esper.collection; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.baseagg +{ + [Serializable] + public class ExprAggregateNodeGroupKey + : ExprNodeBase + , ExprEvaluator + { + private readonly int _groupKeyIndex; + private readonly Type _returnType; + + private AggregationResultFuture _future; + + public ExprAggregateNodeGroupKey(int groupKeyIndex, Type returnType) + { + _groupKeyIndex = groupKeyIndex; + _returnType = returnType; + } + + public void AssignFuture(AggregationResultFuture future) + { + _future = future; + } + + public object Evaluate(EvaluateParams evaluateParams) + { + var groupKey = _future.GetGroupKey(evaluateParams.ExprEvaluatorContext.AgentInstanceId); + if (groupKey is MultiKeyUntyped) + { + return ((MultiKeyUntyped) groupKey).Keys[_groupKeyIndex]; + } + return groupKey; + } + + public Type ReturnType + { + get { return _returnType; } + } + + public override ExprEvaluator ExprEvaluator + { + get { return this; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + } + + public override ExprPrecedenceEnum Precedence + { + get { return ExprPrecedenceEnum.UNARY; } + } + + public String ToExpressionString(ExprPrecedenceEnum precedence) + { + return null; + } + + public override bool IsConstantResult + { + get { return false; } + } + + public override bool EqualsNode(ExprNode node) + { + return false; + } + + public override ExprNode Validate(ExprValidationContext validationContext) + { + // not required + return null; + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/expression/baseagg/ExprAggregateNodeParamDesc.cs b/NEsper.Core/NEsper.Core/epl/expression/baseagg/ExprAggregateNodeParamDesc.cs new file mode 100755 index 000000000..bb5091b49 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/baseagg/ExprAggregateNodeParamDesc.cs @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.baseagg +{ + public class ExprAggregateNodeParamDesc + { + public ExprAggregateNodeParamDesc(ExprNode[] positionalParams, ExprAggregateLocalGroupByDesc optLocalGroupBy) + { + PositionalParams = positionalParams; + OptLocalGroupBy = optLocalGroupBy; + } + + public ExprNode[] PositionalParams { get; private set; } + + public ExprAggregateLocalGroupByDesc OptLocalGroupBy { get; private set; } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/baseagg/ExprAggregateNodeUtil.cs b/NEsper.Core/NEsper.Core/epl/expression/baseagg/ExprAggregateNodeUtil.cs new file mode 100755 index 000000000..3d2f74242 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/baseagg/ExprAggregateNodeUtil.cs @@ -0,0 +1,167 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.declexpr; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.baseagg +{ + public class ExprAggregateNodeUtil + { + public static ExprAggregateNodeParamDesc GetValidatePositionalParams(IList childNodes, bool allowOnlyGroupBy) + { + ExprAggregateLocalGroupByDesc optionalLocalGroupBy = null; + var count = 0; + foreach (var node in childNodes) + { + if (!IsNonPositionalParameter(node)) + { + count++; + } + else + { + var namedParameterNode = (ExprNamedParameterNodeImpl)node; + if (allowOnlyGroupBy && namedParameterNode.ParameterName.ToLower() != "group_by") + { + throw new ExprValidationException("Invalid named parameter '" + namedParameterNode.ParameterName + "' (did you mean 'group_by'?)"); + } + optionalLocalGroupBy = new ExprAggregateLocalGroupByDesc(namedParameterNode.ChildNodes); + } + } + var positionals = new ExprNode[count]; + count = 0; + foreach (var node in childNodes) + { + if (!IsNonPositionalParameter(node)) + { + positionals[count++] = node; + } + } + return new ExprAggregateNodeParamDesc(positionals, optionalLocalGroupBy); + } + + public static bool IsNonPositionalParameter(ExprNode node) + { + return node is ExprNamedParameterNode; + } + + public static void GetAggregatesBottomUp(ExprNode[][] nodes, IList aggregateNodes) + { + if (nodes == null) + { + return; + } + foreach (var node in nodes) + { + GetAggregatesBottomUp(node, aggregateNodes); + } + } + + public static void GetAggregatesBottomUp(ExprNode[] nodes, IList aggregateNodes) + { + if (nodes == null) + { + return; + } + foreach (var node in nodes) + { + GetAggregatesBottomUp(node, aggregateNodes); + } + } + + /// Populates into the supplied list all aggregation functions within this expression, if any. + /// + /// Populates by going bottom-up such that nested aggregates appear first. + /// + /// i.e. sum(volume * sum(price)) would put first A then B into the list with A=sum(price) and B=sum(volume * A) + /// + /// is the expression node to deep inspect + /// is a list of node to populate into + public static void GetAggregatesBottomUp(ExprNode topNode, IList aggregateNodes) + { + // Map to hold per level of the node (1 to Count depth) of expression node a list of aggregation expr nodes, if any + // exist at that level + var aggregateExprPerLevel = new OrderedDictionary>(); + + RecursiveAggregateHandleSpecial(topNode, aggregateExprPerLevel, 1); + + // Recursively enter all aggregate functions and their level into map + RecursiveAggregateEnter(topNode, aggregateExprPerLevel, 1); + + // Done if none found + if (aggregateExprPerLevel.IsEmpty()) + { + return; + } + + // From the deepest (highest) level to the lowest, add aggregates to list + var deepLevel = aggregateExprPerLevel.Keys.Last(); + for (var i = deepLevel; i >= 1; i--) + { + var list = aggregateExprPerLevel.Get(i); + if (list == null) + { + continue; + } + aggregateNodes.AddAll(list); + } + } + + private static void RecursiveAggregateHandleSpecial(ExprNode topNode, IDictionary> aggregateExprPerLevel, int level) + { + if (topNode is ExprNodeInnerNodeProvider) + { + var parameterized = (ExprNodeInnerNodeProvider)topNode; + var additionalNodes = parameterized.AdditionalNodes; + foreach (var additionalNode in additionalNodes) + { + RecursiveAggregateEnter(additionalNode, aggregateExprPerLevel, level); + } + } + + if (topNode is ExprDeclaredNode) + { + var declared = (ExprDeclaredNode)topNode; + RecursiveAggregateEnter(declared.Body, aggregateExprPerLevel, level); + } + } + + private static void RecursiveAggregateEnter(ExprNode currentNode, IDictionary> aggregateExprPerLevel, int currentLevel) + { + // ask all child nodes to enter themselves + foreach (var node in currentNode.ChildNodes) + { + RecursiveAggregateHandleSpecial(node, aggregateExprPerLevel, currentLevel + 1); + RecursiveAggregateEnter(node, aggregateExprPerLevel, currentLevel + 1); + } + + if (!(currentNode is ExprAggregateNode)) + { + return; + } + + // Add myself to list, I'm an aggregate function + var aggregates = aggregateExprPerLevel.Get(currentLevel); + if (aggregates == null) + { + aggregates = new List(); + aggregateExprPerLevel.Put(currentLevel, aggregates); + } + aggregates.Add((ExprAggregateNode)currentNode); + } + + public static int CountPositionalArgs(IList args) + { + return args.Count(expr => !IsNonPositionalParameter(expr)); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/baseagg/ExprAggregationPlugInNodeMarker.cs b/NEsper.Core/NEsper.Core/epl/expression/baseagg/ExprAggregationPlugInNodeMarker.cs new file mode 100755 index 000000000..75a27f0ce --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/baseagg/ExprAggregationPlugInNodeMarker.cs @@ -0,0 +1,14 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.expression.baseagg +{ + interface ExprAggregationPlugInNodeMarker + { + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/EvaluateParams.cs b/NEsper.Core/NEsper.Core/epl/expression/core/EvaluateParams.cs new file mode 100755 index 000000000..7faa57405 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/EvaluateParams.cs @@ -0,0 +1,47 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; + +namespace com.espertech.esper.epl.expression.core +{ + public class EvaluateParams + { + public static readonly EvaluateParams EmptyTrue = new EvaluateParams(null, true, null); + public static readonly EvaluateParams EmptyFalse = new EvaluateParams(null, false, null); + + private readonly EventBean[] _eventsPerStream; + + private readonly ExprEvaluatorContext _exprEvaluatorContext; + private readonly bool _isNewData; + + public EvaluateParams(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext exprEvaluatorContext) + { + _eventsPerStream = eventsPerStream; + + _isNewData = isNewData; + + _exprEvaluatorContext = exprEvaluatorContext; + } + + public EventBean[] EventsPerStream + { + get { return _eventsPerStream; } + } + + public bool IsNewData + { + get { return _isNewData; } + } + + public ExprEvaluatorContext ExprEvaluatorContext + { + get { return _exprEvaluatorContext; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprChainedSpec.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprChainedSpec.cs new file mode 100755 index 000000000..ec7a90016 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprChainedSpec.cs @@ -0,0 +1,76 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.epl.expression.core +{ + [Serializable] + public class ExprChainedSpec + { + public ExprChainedSpec(String name, IList parameters, bool property) + { + Name = name; + Parameters = parameters; + IsProperty = property; + } + + public string Name { get; set; } + + public IList Parameters { get; set; } + + public bool IsProperty { get; private set; } + + public bool Equals(ExprChainedSpec other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return Equals(other.Name, Name) && ExprNodeUtility.DeepEquals(other.Parameters, Parameters); + } + + /// + /// Determines whether the specified is equal to the current . + /// + /// + /// true if the specified is equal to the current ; otherwise, false. + /// + /// The to compare with the current . + /// The parameter is null. + /// 2 + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != typeof (ExprChainedSpec)) return false; + return Equals((ExprChainedSpec) obj); + } + + /// + /// Serves as a hash function for a particular type. + /// + /// + /// A hash code for the current . + /// + /// 2 + public override int GetHashCode() + { + unchecked + { + return ((Name != null ? Name.GetHashCode() : 0)*397) ^ (Parameters != null ? Parameters.GetHashCode() : 0); + } + } + + public override String ToString() { + return "ExprChainedSpec{" + + "name='" + Name + '\'' + + ", parameters=" + Parameters + + '}'; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprConstantNode.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprConstantNode.cs new file mode 100755 index 000000000..b1c3aa7c2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprConstantNode.cs @@ -0,0 +1,22 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.expression.core +{ + /// + /// Represents a constant in an expressiun tree. + /// + public interface ExprConstantNode : ExprNode + { + Type ConstantType { get; } + object GetConstantValue(ExprEvaluatorContext context); + bool IsConstantValue { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprConstantNodeImpl.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprConstantNodeImpl.cs new file mode 100755 index 000000000..c27d4af26 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprConstantNodeImpl.cs @@ -0,0 +1,139 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +using com.espertech.esper.core.service; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.core +{ + /// + /// Represents a constant in an expressiun tree. + /// + [Serializable] + public class ExprConstantNodeImpl + : ExprNodeBase + , ExprConstantNode + , ExprEvaluator + { + private Object _constantValue; + private readonly Type _clazz; + + /// Ctor. + /// is the constant's ConstantValue. + public ExprConstantNodeImpl(Object constantValue) + { + _constantValue = constantValue; + if (constantValue == null) + { + _clazz = null; + } + else + { + _clazz = constantValue.GetType().GetBoxedType(); + } + } + + /// Ctor. + /// is the constant's ConstantValue. + /// is the constant's ConstantValue type. + public ExprConstantNodeImpl(Object constantValue, Type valueType) + { + _constantValue = constantValue; + if (constantValue == null) + { + _clazz = valueType; + } + else + { + _clazz = constantValue.GetType().GetBoxedType(); + } + } + + /// Ctor - for use when the constant should return a given type and the actual ConstantValue is always null. + /// the type of the constant null. + public ExprConstantNodeImpl(Type clazz) + { + _clazz = clazz; + _constantValue = null; + } + + public bool IsConstantValue + { + get { return true; } + } + + public override ExprNode Validate(ExprValidationContext validationContext) + { + return null; + } + + public override bool IsConstantResult + { + get { return true; } + } + + /// Returns the constant's ConstantValue. + /// ConstantValue of constant + public object ConstantValue + { + set { _constantValue = value; } + } + + public object GetConstantValue(ExprEvaluatorContext context) + { + return _constantValue; + } + + public Type ConstantType + { + get { return _clazz; } + } + + public Type ReturnType + { + get { return _clazz; } + } + + public object Evaluate(EvaluateParams evaluateParams) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QaExprConst(_constantValue); } + return _constantValue; + } + + public override ExprEvaluator ExprEvaluator + { + get { return this; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + writer.Write( + EPStatementObjectModelHelper.RenderValue(_constantValue)); + } + + public override ExprPrecedenceEnum Precedence + { + get { return ExprPrecedenceEnum.UNARY; } + } + + public override bool EqualsNode(ExprNode node) + { + var other = node as ExprConstantNodeImpl; + + if (ReferenceEquals(null, other)) + return false; + if (ReferenceEquals(this, other)) + return true; + return Equals(_constantValue, other._constantValue); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprContextPropertyNode.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprContextPropertyNode.cs new file mode 100755 index 000000000..3e279858a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprContextPropertyNode.cs @@ -0,0 +1,125 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.expression.core +{ + /// + /// Represents an stream property identifier in a filter expressiun tree. + /// + [Serializable] + public class ExprContextPropertyNode : ExprNodeBase, ExprEvaluator + { + private readonly string _propertyName; + private Type _returnType; + [NonSerialized] private EventPropertyGetter _getter; + + public ExprContextPropertyNode(String propertyName) + { + _propertyName = propertyName; + } + + public override ExprEvaluator ExprEvaluator + { + get { return this; } + } + + public string PropertyName + { + get { return _propertyName; } + } + + public override ExprNode Validate(ExprValidationContext validationContext) + { + if (validationContext.ContextDescriptor == null) + { + throw new ExprValidationException( + "Context property '" + _propertyName + "' cannot be used in the expression as provided"); + } + EventType eventType = validationContext.ContextDescriptor.ContextPropertyRegistry.ContextEventType; + if (eventType == null) + { + throw new ExprValidationException( + "Context property '" + _propertyName + "' cannot be used in the expression as provided"); + } + _getter = eventType.GetGetter(_propertyName); + if (_getter == null) + { + throw new ExprValidationException( + "Context property '" + _propertyName + "' is not a known property, known properties are " + + eventType.PropertyNames.Render()); + } + _returnType = eventType.GetPropertyType(_propertyName); + + return null; + } + + public override bool IsConstantResult + { + get { return false; } + } + + public object Evaluate(EvaluateParams evaluateParams) + { + var context = evaluateParams.ExprEvaluatorContext; + + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().QExprContextProp(this); + + Object result = null; + if (context.ContextProperties != null) + { + result = _getter.Get(context.ContextProperties); + } + InstrumentationHelper.Get().AExprContextProp(result); + return result; + } + + if (context.ContextProperties != null) + { + return _getter.Get(context.ContextProperties); + } + return null; + } + + public Type ReturnType + { + get { return _returnType; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + writer.Write(_propertyName); + } + + public override ExprPrecedenceEnum Precedence + { + get { return ExprPrecedenceEnum.UNARY; } + } + + public EventPropertyGetter Getter + { + get { return _getter; } + } + + public override bool EqualsNode(ExprNode node) { + if (this == node) return true; + if (node == null || GetType() != node.GetType()) return false; + + var that = (ExprContextPropertyNode) node; + return _propertyName.Equals(that._propertyName); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprCurrentEvaluationContextNode.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprCurrentEvaluationContextNode.cs new file mode 100755 index 000000000..ce4194c8e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprCurrentEvaluationContextNode.cs @@ -0,0 +1,80 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +using com.espertech.esper.client; +using com.espertech.esper.client.hook; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.expression.core +{ + /// + /// Represents the "current_evaluation_context" function in an expression tree. + /// + [Serializable] + public class ExprCurrentEvaluationContextNode + : ExprNodeBase + , ExprEvaluator + { + /// + /// Ctor. + /// + public ExprCurrentEvaluationContextNode() + { + } + + public override ExprEvaluator ExprEvaluator + { + get { return this; } + } + + public override ExprNode Validate(ExprValidationContext validationContext) + { + if (this.ChildNodes.Count != 0) + { + throw new ExprValidationException("current_evaluation_context function node cannot have a child node"); + } + return null; + } + + public override bool IsConstantResult + { + get { return false; } + } + + public Type ReturnType + { + get { return typeof (EPLExpressionEvaluationContext); } + } + + public object Evaluate(EvaluateParams evaluateParams) + { + var exprEvaluatorContext = evaluateParams.ExprEvaluatorContext; + var ctx = new EPLExpressionEvaluationContext(exprEvaluatorContext.StatementName, exprEvaluatorContext.AgentInstanceId, exprEvaluatorContext.EngineURI, exprEvaluatorContext.StatementUserObject); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QaExprConst(ctx);} + return ctx; + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + writer.Write("current_evaluation_context()"); + } + + public override ExprPrecedenceEnum Precedence + { + get { return ExprPrecedenceEnum.UNARY; } + } + + public override bool EqualsNode(ExprNode node) + { + return node is ExprCurrentEvaluationContextNode; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprEvaluator.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprEvaluator.cs new file mode 100755 index 000000000..144d5aa9b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprEvaluator.cs @@ -0,0 +1,36 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.epl.expression.core +{ + /// + /// Interface for evaluating of an event tuple. + /// + public interface ExprEvaluator + { + /// + /// Evaluate event tuple and return result. + /// + /// The evaluate params. + /// + /// evaluation result, a bool value for OR/AND-type evalution nodes. + /// + Object Evaluate(EvaluateParams evaluateParams); + + /// + /// Returns the type that the node's evaluate method returns an instance of. + /// + /// The type of the return. + /// type returned when evaluated + Type ReturnType { get; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprEvaluatorContext.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprEvaluatorContext.cs new file mode 100755 index 000000000..141e3ce8d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprEvaluatorContext.cs @@ -0,0 +1,141 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat.threading; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.script; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.schedule; + +namespace com.espertech.esper.epl.expression.core +{ + /// + /// Returns the context for expression evaluation. + /// + public interface ExprEvaluatorContext + { + string StatementName { get; } + + object StatementUserObject { get; } + + string EngineURI { get; } + + int StatementId { get; } + + StatementType? StatementType { get; } + + TimeProvider TimeProvider { get; } + + ExpressionResultCacheService ExpressionResultCacheService { get; } + + int AgentInstanceId { get; } + + EventBean ContextProperties { get; } + + AgentInstanceScriptContext AllocateAgentInstanceScriptContext { get; } + + IReaderWriterLock AgentInstanceLock { get; } + + TableExprEvaluatorContext TableExprEvaluatorContext { get; } + } + + public class ProxyExprEvaluatorContext : ExprEvaluatorContext + { + public Func ProcStatementUserObject { get; set; } + public Func ProcTimeProvider { get; set; } + public Func ProcExpressionResultCacheService { get; set; } + public Func ProcAgentInstanceId { get; set; } + public Func ProcContextProperties { get; set; } + public Func ProcStatementName { get; set; } + public Func ProcEngineURI { get; set; } + public Func ProcStatementId { get; set; } + public Func ProcStatementType { get; set; } + public Func ProcAllocateAgentInstanceScriptContext { get; set; } + public Func ProcAgentInstanceLock { get; set; } + public Func ProcTableExprEvaluatorContext { get; set; } + + public ProxyExprEvaluatorContext() + { + ProcTimeProvider = () => null; + ProcExpressionResultCacheService = () => null; + ProcAgentInstanceId = () => -1; + ProcContextProperties = () => null; + ProcStatementName = () => null; + ProcEngineURI = () => null; + ProcStatementId = () => -1; + ProcStatementType = () => null; + ProcAllocateAgentInstanceScriptContext = () => null; + ProcAgentInstanceLock = () => null; + ProcTableExprEvaluatorContext = () => null; + } + + public object StatementUserObject + { + get { return ProcStatementUserObject(); } + } + + public TimeProvider TimeProvider + { + get { return ProcTimeProvider(); } + } + + public ExpressionResultCacheService ExpressionResultCacheService + { + get { return ProcExpressionResultCacheService(); } + } + + public int AgentInstanceId + { + get { return ProcAgentInstanceId(); } + } + + public EventBean ContextProperties + { + get { return ProcContextProperties(); } + } + + public string StatementName + { + get { return ProcStatementName(); } + } + + public string EngineURI + { + get { return ProcEngineURI(); } + } + + public int StatementId + { + get { return ProcStatementId(); } + } + + public StatementType? StatementType + { + get { return ProcStatementType(); } + } + + public AgentInstanceScriptContext AllocateAgentInstanceScriptContext + { + get { return ProcAllocateAgentInstanceScriptContext(); } + } + + public IReaderWriterLock AgentInstanceLock + { + get { return ProcAgentInstanceLock(); } + } + + public TableExprEvaluatorContext TableExprEvaluatorContext + { + get { return ProcTableExprEvaluatorContext(); } + } + + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprEvaluatorContextTimeOnly.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprEvaluatorContextTimeOnly.cs new file mode 100755 index 000000000..d68659d32 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprEvaluatorContextTimeOnly.cs @@ -0,0 +1,96 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat.threading; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.script; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.schedule; + +namespace com.espertech.esper.epl.expression.core +{ + /// + /// Represents a minimal engine-level context for expression evaluation, not allowing for agents instances and result cache. + /// + public class ExprEvaluatorContextTimeOnly : ExprEvaluatorContext + { + private readonly TimeProvider _timeProvider; + private readonly ExpressionResultCacheService _expressionResultCacheService; + + public ExprEvaluatorContextTimeOnly(TimeProvider timeProvider) + { + _timeProvider = timeProvider; + _expressionResultCacheService = new ExpressionResultCacheService(1); + } + + /// Returns the time provider. + /// time provider + public TimeProvider TimeProvider + { + get { return _timeProvider; } + } + + public ExpressionResultCacheService ExpressionResultCacheService + { + get { return _expressionResultCacheService; } + } + + public int AgentInstanceId + { + get { return -1; } + } + + public EventBean ContextProperties + { + get { return null; } + } + + public AgentInstanceScriptContext AllocateAgentInstanceScriptContext + { + get { return null; } + } + + public String StatementName + { + get { return null; } + } + + public String EngineURI + { + get { return null; } + } + + public int StatementId + { + get { return -1; } + } + + public IReaderWriterLock AgentInstanceLock + { + get { return null; } + } + + public StatementType? StatementType + { + get { return null; } + } + + public TableExprEvaluatorContext TableExprEvaluatorContext + { + get { throw new EPException("Access to tables is not allowed"); } + } + + public object StatementUserObject + { + get { return null; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprEvaluatorEnumeration.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprEvaluatorEnumeration.cs new file mode 100755 index 000000000..61e49d724 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprEvaluatorEnumeration.cs @@ -0,0 +1,31 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.expression.core +{ + /// + /// Interface for evaluating of an event tuple. + /// + public interface ExprEvaluatorEnumeration + { + EventType GetEventTypeCollection(EventAdapterService eventAdapterService, int statementId); + ICollection EvaluateGetROCollectionEvents(EvaluateParams evaluateParams); + + Type ComponentTypeCollection { get; } + ICollection EvaluateGetROCollectionScalar(EvaluateParams evaluateParams); + + EventType GetEventTypeSingle(EventAdapterService eventAdapterService, int statementId); + EventBean EvaluateGetEventBean(EvaluateParams evaluateParams); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprEvaluatorEnumerationGivenEvent.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprEvaluatorEnumerationGivenEvent.cs new file mode 100755 index 000000000..b62c08019 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprEvaluatorEnumerationGivenEvent.cs @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; + +namespace com.espertech.esper.epl.expression.core +{ + /// + /// Interface for evaluating of an event re. enumeration. + /// + public interface ExprEvaluatorEnumerationGivenEvent + { + ICollection EvaluateEventGetROCollectionEvents(EventBean @event, ExprEvaluatorContext context); + + ICollection EvaluateEventGetROCollectionScalar(EventBean @event, ExprEvaluatorContext context); + + EventBean EvaluateEventGetEventBean(EventBean @event, ExprEvaluatorContext context); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprEvaluatorProxy.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprEvaluatorProxy.cs new file mode 100755 index 000000000..cf2d6a274 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprEvaluatorProxy.cs @@ -0,0 +1,131 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; + +using Castle.DynamicProxy; + +using com.espertech.esper.client; +using com.espertech.esper.client.annotation; +using com.espertech.esper.compat.collections; +using com.espertech.esper.events; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.core +{ + public class ExprEvaluatorProxy : IInterceptor + { + private static readonly MethodInfo TargetEvaluate = typeof(ExprEvaluator).GetMethod("Evaluate"); + private static readonly MethodInfo TargetEvaluateCollEvents = typeof(ExprEvaluatorEnumeration).GetMethod("EvaluateGetROCollectionEvents"); + private static readonly MethodInfo TargetEvaluateCollScalar = typeof(ExprEvaluatorEnumeration).GetMethod("EvaluateGetROCollectionScalar"); + private static readonly MethodInfo TargetEvaluateBean = typeof(ExprEvaluatorEnumeration).GetMethod("EvaluateGetEventBean"); + + private static readonly Assembly CastleAssembly = + typeof (ProxyGenerator).Assembly; + + private readonly String _engineURI; + private readonly String _statementName; + private readonly String _expressionToString; + private readonly ExprEvaluator _evaluator; + + public static Object NewInstance(String engineURI, String statementName, String expressionToString, ExprEvaluator evaluator) + { + var generator = new ProxyGenerator(); + var interfaces = evaluator.GetType() + .GetInterfaces() + .Where(inf => inf.Assembly != CastleAssembly) + .ToArray(); + + return generator.CreateInterfaceProxyWithoutTarget( + typeof(ExprEvaluator), interfaces, + new ExprEvaluatorProxy(engineURI, statementName, expressionToString, evaluator)); + } + + public ExprEvaluatorProxy(String engineURI, String statementName, String expressionToString, ExprEvaluator evaluator) + { + _engineURI = engineURI; + _statementName = statementName; + _expressionToString = expressionToString; + _evaluator = evaluator; + } + + /// + /// Intercepts the specified invocation. + /// + /// The invocation. + public void Intercept(IInvocation invocation) + { + var result = invocation.Method.Invoke(_evaluator, invocation.Arguments); + invocation.ReturnValue = result; + + if (invocation.Method == TargetEvaluate) + { + if (AuditPath.IsAuditEnabled) + { + AuditPath.AuditLog(_engineURI, _statementName, AuditEnum.EXPRESSION, + "expression " + _expressionToString + " result " + result); + } + } + else if (invocation.Method == TargetEvaluateCollEvents) + { + if (AuditPath.IsAuditEnabled) + { + var resultBeans = (ICollection)result; + var @out = "null"; + if (resultBeans != null) + { + if (resultBeans.IsEmpty()) + { + @out = "{}"; + } + else + { + var buf = new StringWriter(); + var count = 0; + foreach (EventBean theEvent in resultBeans) + { + buf.Write(" Event "); + buf.Write(Convert.ToString(count++)); + buf.Write(":"); + EventBeanUtility.AppendEvent(buf, theEvent); + } + @out = buf.ToString(); + } + } + + AuditPath.AuditLog(_engineURI, _statementName, AuditEnum.EXPRESSION, "expression " + _expressionToString + " result " + @out); + } + } + else if (invocation.Method == TargetEvaluateCollScalar) + { + if (AuditPath.IsAuditEnabled) + { + AuditPath.AuditLog(_engineURI, _statementName, AuditEnum.EXPRESSION, "expression " + _expressionToString + " result " + result); + } + } + else if (invocation.Method == TargetEvaluateBean) + { + if (AuditPath.IsAuditEnabled) + { + var @out = "null"; + if (result != null) + { + var buf = new StringWriter(); + EventBeanUtility.AppendEvent(buf, (EventBean)result); + @out = buf.ToString(); + } + AuditPath.AuditLog(_engineURI, _statementName, AuditEnum.EXPRESSION, "expression " + _expressionToString + " result " + @out); + } + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprEvaluatorTypableReturn.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprEvaluatorTypableReturn.cs new file mode 100755 index 000000000..13390ddbd --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprEvaluatorTypableReturn.cs @@ -0,0 +1,43 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; + +namespace com.espertech.esper.epl.expression.core +{ + /// + /// Interface for evaluators that select possible multi-valued results in a single select column, + /// such as subqueries and "new" and case+new combined. When returning non-null results + /// from , the + /// + /// When returning non-null results, the the evaluator must also return either Object[] results + /// or Object[][], each object-array following the same exact order as provided by the map, matching + /// the multi-row flag. + /// + public interface ExprEvaluatorTypableReturn : ExprEvaluator + { + /// + /// Return null to indicate no row-type result available, or a map of property names and types to indicate a row-type result is available. + /// + /// map of property names and types or null + /// ExprValidationException if the expression is invalid + IDictionary RowProperties { get; } + + /// + /// Return true for multi-row return, return false for return of single row only + /// + /// multi-row flag + bool? IsMultirow { get; } + + Object[] EvaluateTypableSingle(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context); + Object[][] EvaluateTypableMulti(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprFilterOptimizableNode.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprFilterOptimizableNode.cs new file mode 100755 index 000000000..e63ce0e45 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprFilterOptimizableNode.cs @@ -0,0 +1,19 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.filter; + + +namespace com.espertech.esper.epl.expression.core +{ + public interface ExprFilterOptimizableNode + { + bool IsFilterLookupEligible { get; } + FilterSpecLookupable FilterLookupable { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprGroupingIdNode.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprGroupingIdNode.cs new file mode 100755 index 000000000..e41b6feee --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprGroupingIdNode.cs @@ -0,0 +1,75 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.epl.expression.core +{ + [Serializable] + public class ExprGroupingIdNode : ExprNodeBase, ExprEvaluator + { + private int _id; + + public int Id + { + set { _id = value; } + get { return _id; } + } + + public override ExprNode Validate(ExprValidationContext validationContext) + { + if (!validationContext.IsAllowRollupFunctions) + { + throw MakeException("grouping_id"); + } + + return null; + } + + public override ExprEvaluator ExprEvaluator + { + get { return this; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + ExprNodeUtility.ToExpressionStringWFunctionName("grouping_id", ChildNodes, writer); + } + + public override ExprPrecedenceEnum Precedence + { + get { return ExprPrecedenceEnum.UNARY; } + } + + public override bool IsConstantResult + { + get { return false; } + } + + public override bool EqualsNode(ExprNode node) + { + return false; + } + + public object Evaluate(EvaluateParams evaluateParams) + { + return _id; + } + + public Type ReturnType + { + get { return typeof (int?); } + } + + public static ExprValidationException MakeException(String functionName) + { + return new ExprValidationException("The " + functionName + " function requires the group-by clause to specify rollup, cube or grouping sets, and may only be used in the select-clause, having-clause or order-by-clause"); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprGroupingNode.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprGroupingNode.cs new file mode 100755 index 000000000..da6b04bfa --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprGroupingNode.cs @@ -0,0 +1,61 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.epl.expression.core +{ + [Serializable] + public class ExprGroupingNode : ExprNodeBase, ExprEvaluator + { + public override ExprNode Validate(ExprValidationContext validationContext) + { + if (!validationContext.IsAllowRollupFunctions) { + throw ExprGroupingIdNode.MakeException("grouping"); + } + + return null; + } + + public override ExprEvaluator ExprEvaluator + { + get { return this; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + ExprNodeUtility.ToExpressionStringWFunctionName("grouping", this.ChildNodes, writer); + } + + public override ExprPrecedenceEnum Precedence + { + get { return ExprPrecedenceEnum.UNARY; } + } + + public override bool IsConstantResult + { + get { return false; } + } + + public override bool EqualsNode(ExprNode node) + { + return false; + } + + public object Evaluate(EvaluateParams evaluateParams) + { + return null; + } + + public Type ReturnType + { + get { return typeof (int?); } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprIdentNode.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprIdentNode.cs new file mode 100755 index 000000000..10518e712 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprIdentNode.cs @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.expression.core +{ + /// + /// Represents an stream property identifier in a filter expressiun tree. + /// + public interface ExprIdentNode + : ExprNode + , ExprFilterOptimizableNode + , ExprStreamRefNode + { + string UnresolvedPropertyName { get; } + string FullUnresolvedName { get; } + int StreamId { get; } + string ResolvedPropertyNameRoot { get; } + string ResolvedPropertyName { get; } + string StreamOrPropertyName { get; set; } + string ResolvedStreamName { get; } + ExprIdentNodeEvaluator ExprEvaluatorIdent { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprIdentNodeEvaluator.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprIdentNodeEvaluator.cs new file mode 100755 index 000000000..e69aeec78 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprIdentNodeEvaluator.cs @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; + +namespace com.espertech.esper.epl.expression.core +{ + public interface ExprIdentNodeEvaluator : ExprEvaluator + { + bool EvaluatePropertyExists(EventBean[] eventsPerStream, bool isNewData); + int StreamNum { get; } + EventPropertyGetter Getter { get; } + bool IsContextEvaluated { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprIdentNodeEvaluatorContext.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprIdentNodeEvaluatorContext.cs new file mode 100755 index 000000000..e8cc36c90 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprIdentNodeEvaluatorContext.cs @@ -0,0 +1,64 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.epl.expression.core +{ + [Serializable] + public class ExprIdentNodeEvaluatorContext : ExprIdentNodeEvaluator + { + private readonly int _streamNum; + private readonly Type _resultType; + private readonly EventPropertyGetter _getter; + + public ExprIdentNodeEvaluatorContext(int streamNum, Type resultType, EventPropertyGetter getter) + { + _streamNum = streamNum; + _resultType = resultType; + _getter = getter; + } + + public bool EvaluatePropertyExists(EventBean[] eventsPerStream, bool isNewData) + { + return true; + } + + public int StreamNum + { + get { return _streamNum; } + } + + public object Evaluate(EvaluateParams evaluateParams) + { + var context = evaluateParams.ExprEvaluatorContext; + if (context.ContextProperties != null) + { + return _getter.Get(context.ContextProperties); + } + return null; + } + + public Type ReturnType + { + get { return _resultType; } + } + + public EventPropertyGetter Getter + { + get { return _getter; } + } + + public bool IsContextEvaluated + { + get { return true; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprIdentNodeEvaluatorImpl.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprIdentNodeEvaluatorImpl.cs new file mode 100755 index 000000000..cce0a76e2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprIdentNodeEvaluatorImpl.cs @@ -0,0 +1,85 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.core +{ + [Serializable] + public class ExprIdentNodeEvaluatorImpl : ExprIdentNodeEvaluator + { + private readonly int _streamNum; + private readonly EventPropertyGetter _propertyGetter; + private readonly Type _propertyType; + private readonly ExprIdentNode _identNode; + + public ExprIdentNodeEvaluatorImpl(int streamNum, EventPropertyGetter propertyGetter, Type propertyType, ExprIdentNode identNode) + { + _streamNum = streamNum; + _propertyGetter = propertyGetter; + _propertyType = propertyType; // .GetBoxedType(); + _identNode = identNode; + } + + public virtual object Evaluate(EvaluateParams evaluateParams) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QExprIdent(_identNode.FullUnresolvedName);} + EventBean theEvent = evaluateParams.EventsPerStream[_streamNum]; + if (theEvent == null) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprIdent(null);} + return null; + } + if (InstrumentationHelper.ENABLED) { + Object result = _propertyGetter.Get(theEvent); + InstrumentationHelper.Get().AExprIdent(result); + return result; + } + + return _propertyGetter.Get(theEvent); + } + + public Type ReturnType + { + get { return _propertyType; } + } + + public EventPropertyGetter Getter + { + get { return _propertyGetter; } + } + + /// Returns true if the property exists, or false if not. + /// each stream's events + /// if the stream represents insert or remove stream + /// true if the property exists, false if not + public bool EvaluatePropertyExists(EventBean[] eventsPerStream, bool isNewData) + { + EventBean theEvent = eventsPerStream[_streamNum]; + if (theEvent == null) + { + return false; + } + return _propertyGetter.IsExistsProperty(theEvent); + } + + public int StreamNum + { + get { return _streamNum; } + } + + public bool IsContextEvaluated + { + get { return false; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprIdentNodeEvaluatorLogging.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprIdentNodeEvaluatorLogging.cs new file mode 100755 index 000000000..535f9936c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprIdentNodeEvaluatorLogging.cs @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.client.annotation; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.core +{ + [Serializable] + public class ExprIdentNodeEvaluatorLogging : ExprIdentNodeEvaluatorImpl + { + private readonly String _engineURI; + private readonly String _propertyName; + private readonly String _statementName; + + public ExprIdentNodeEvaluatorLogging(int streamNum, EventPropertyGetter propertyGetter, Type propertyType, ExprIdentNode identNode, String propertyName, String statementName, String engineURI) + : base(streamNum, propertyGetter, propertyType, identNode) + { + _propertyName = propertyName; + _statementName = statementName; + _engineURI = engineURI; + } + + public override object Evaluate(EvaluateParams evaluateParams) + { + Object result = base.Evaluate(evaluateParams); + if (AuditPath.IsInfoEnabled) { + AuditPath.AuditLog(_engineURI, _statementName, AuditEnum.PROPERTY, _propertyName + " value " + result); + } + return result; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprIdentNodeImpl.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprIdentNodeImpl.cs new file mode 100755 index 000000000..f17d2b9c5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprIdentNodeImpl.cs @@ -0,0 +1,346 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +using com.espertech.esper.client; +using com.espertech.esper.client.annotation; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.table; +using com.espertech.esper.epl.parse; +using com.espertech.esper.events.property; +using com.espertech.esper.filter; + +namespace com.espertech.esper.epl.expression.core +{ + /// + /// Represents an stream property identifier in a filter expressiun tree. + /// + [Serializable] + public class ExprIdentNodeImpl + : ExprNodeBase + , ExprIdentNode + { + // select myprop from... is a simple property, no stream supplied + // select s0.myprop from... is a simple property with a stream supplied, or a nested property (cannot tell until resolved) + // select indexed[1] from ... is a indexed property + + private readonly string _unresolvedPropertyName; + private string _streamOrPropertyName; + + private string _resolvedStreamName; + private string _resolvedPropertyName; + + [NonSerialized] + private ExprIdentNodeEvaluator _evaluator; + + /// + /// Ctor. + /// + /// is the event property name in unresolved form, ie. unvalidated against streams + public ExprIdentNodeImpl(string unresolvedPropertyName) + { + if (unresolvedPropertyName == null) + { + throw new ArgumentException("Property name is null"); + } + _unresolvedPropertyName = unresolvedPropertyName; + _streamOrPropertyName = null; + } + + /// + /// Ctor. + /// + /// is the event property name in unresolved form, ie. unvalidated against streams + /// is the stream name, or if not a valid stream name a possible nested property namein one of the streams. + /// + public ExprIdentNodeImpl(string unresolvedPropertyName, string streamOrPropertyName) + { + if (unresolvedPropertyName == null) + { + throw new ArgumentException("Property name is null"); + } + if (streamOrPropertyName == null) + { + throw new ArgumentException("Stream (or property name) name is null"); + } + _unresolvedPropertyName = unresolvedPropertyName; + _streamOrPropertyName = streamOrPropertyName; + } + + public ExprIdentNodeImpl(EventType eventType, string propertyName, int streamNumber) + { + _unresolvedPropertyName = propertyName; + _resolvedPropertyName = propertyName; + EventPropertyGetter propertyGetter = eventType.GetGetter(propertyName); + if (propertyGetter == null) { + throw new ArgumentException("Ident-node constructor could not locate property " + propertyName); + } + Type propertyType = eventType.GetPropertyType(propertyName); + _evaluator = new ExprIdentNodeEvaluatorImpl(streamNumber, propertyGetter, propertyType, this); + } + + public override ExprEvaluator ExprEvaluator + { + get { return _evaluator; } + } + + /// + /// For unit testing, returns unresolved property name. + /// + /// property name + public virtual string UnresolvedPropertyName + { + get { return _unresolvedPropertyName; } + } + + /// + /// For unit testing, returns stream or property name candidate. + /// + /// stream name, or property name of a nested property of one of the streams + public virtual string StreamOrPropertyName + { + get { return _streamOrPropertyName; } + set { _streamOrPropertyName = value; } + } + + /// + /// Returns the unresolved property name in it's complete form, including + /// the stream name if there is one. + /// + /// property name + public string FullUnresolvedName + { + get + { + if (_streamOrPropertyName == null) + { + return _unresolvedPropertyName; + } + else + { + return _streamOrPropertyName + "." + _unresolvedPropertyName; + } + } + } + + public bool IsFilterLookupEligible + { + get { return _evaluator.StreamNum == 0 && !(_evaluator.IsContextEvaluated); } + } + + public FilterSpecLookupable FilterLookupable + { + get { return new FilterSpecLookupable(_resolvedPropertyName, _evaluator.Getter, _evaluator.ReturnType, false); } + } + + public override ExprNode Validate(ExprValidationContext validationContext) + { + // rewrite expression into a table-access expression + if (validationContext.StreamTypeService.HasTableTypes) { + ExprTableIdentNode tableIdentNode = validationContext.TableService.GetTableIdentNode(validationContext.StreamTypeService, _unresolvedPropertyName, _streamOrPropertyName); + if (tableIdentNode != null) { + return tableIdentNode; + } + } + + string unescapedPropertyName = PropertyParser.UnescapeBacktick(_unresolvedPropertyName); + Pair propertyInfoPair = ExprIdentNodeUtil.GetTypeFromStream(validationContext.StreamTypeService, unescapedPropertyName, _streamOrPropertyName, false); + _resolvedStreamName = propertyInfoPair.Second; + int streamNum = propertyInfoPair.First.StreamNum; + Type propertyType = propertyInfoPair.First.PropertyType; + _resolvedPropertyName = propertyInfoPair.First.PropertyName; + EventPropertyGetter propertyGetter; + try { + propertyGetter = propertyInfoPair.First.StreamEventType.GetGetter(_resolvedPropertyName); + } + catch (PropertyAccessException ex) { + throw new ExprValidationException("Property '" + _unresolvedPropertyName + "' is not valid: " + ex.Message, ex); + } + + if (propertyGetter == null) + { + throw new ExprValidationException("Property getter returned was invalid for property '" + _unresolvedPropertyName + "'"); + } + + var audit = AuditEnum.PROPERTY.GetAudit(validationContext.Annotations); + if (audit != null) { + _evaluator = new ExprIdentNodeEvaluatorLogging(streamNum, propertyGetter, propertyType, this, _resolvedPropertyName, validationContext.StatementName, validationContext.StreamTypeService.EngineURIQualifier); + } + else { + _evaluator = new ExprIdentNodeEvaluatorImpl(streamNum, propertyGetter, propertyType, this); + } + + // if running in a context, take the property value from context + if (validationContext.ContextDescriptor != null && !validationContext.IsFilterExpression) + { + EventType fromType = validationContext.StreamTypeService.EventTypes[streamNum]; + string contextPropertyName = validationContext.ContextDescriptor.ContextPropertyRegistry.GetPartitionContextPropertyName(fromType, _resolvedPropertyName); + if (contextPropertyName != null) { + EventType contextType = validationContext.ContextDescriptor.ContextPropertyRegistry.ContextEventType; + _evaluator = new ExprIdentNodeEvaluatorContext(streamNum, contextType.GetPropertyType(contextPropertyName), contextType.GetGetter(contextPropertyName)); + } + } + return null; + } + + public override bool IsConstantResult + { + get { return false; } + } + + /// + /// Returns stream id supplying the property value. + /// + /// stream number + public int StreamId + { + get + { + if (_evaluator == null) + { + throw new IllegalStateException("Identifier expression has not been validated"); + } + return _evaluator.StreamNum; + } + } + + public int? StreamReferencedIfAny + { + get { return StreamId; } + } + + public string RootPropertyNameIfAny + { + get { return ResolvedPropertyNameRoot; } + } + + public virtual Type ReturnType + { + get + { + if (_evaluator == null) + { + throw new IllegalStateException("Identifier expression has not been validated"); + } + return _evaluator.ReturnType; + } + } + + /// + /// Returns stream name as resolved by lookup of property in streams. + /// + /// stream name + public string ResolvedStreamName + { + get + { + if (_resolvedStreamName == null) + { + throw new IllegalStateException("Identifier node has not been validated"); + } + return _resolvedStreamName; + } + } + + /// + /// Return property name as resolved by lookup in streams. + /// + /// property name + public string ResolvedPropertyName + { + get + { + if (_resolvedPropertyName == null) + { + throw new IllegalStateException("Identifier node has not been validated"); + } + return _resolvedPropertyName; + } + } + + /// + /// Returns the root of the resolved property name, if any. + /// + /// root + public string ResolvedPropertyNameRoot + { + get + { + if (_resolvedPropertyName == null) + { + throw new IllegalStateException("Identifier node has not been validated"); + } + if (_resolvedPropertyName.IndexOf('[') != -1) + { + return _resolvedPropertyName.Substring(0, _resolvedPropertyName.IndexOf('[')); + } + if (_resolvedPropertyName.IndexOf('(') != -1) + { + return _resolvedPropertyName.Substring(0, _resolvedPropertyName.IndexOf('(')); + } + if (_resolvedPropertyName.IndexOf('.') != -1) + { + return _resolvedPropertyName.Substring(0, _resolvedPropertyName.IndexOf('.')); + } + return _resolvedPropertyName; + } + } + + public override string ToString() + { + return "unresolvedPropertyName=" + (_unresolvedPropertyName ?? "null") + + " streamOrPropertyName=" + (_streamOrPropertyName ?? "null") + + " resolvedPropertyName=" + (_resolvedPropertyName ?? "null"); + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + ToPrecedenceFreeEPL(writer, _streamOrPropertyName, _unresolvedPropertyName); + } + + public static void ToPrecedenceFreeEPL(TextWriter writer, string streamOrPropertyName, string unresolvedPropertyName) + { + if (streamOrPropertyName != null) { + writer.Write(ASTUtil.UnescapeDot(streamOrPropertyName)); + writer.Write('.'); + } + writer.Write(ASTUtil.UnescapeDot(PropertyParser.UnescapeBacktick(unresolvedPropertyName))); + } + + public override ExprPrecedenceEnum Precedence + { + get { return ExprPrecedenceEnum.UNARY; } + } + + public override bool EqualsNode(ExprNode node) + { + if (!(node is ExprIdentNode)) + { + return false; + } + + ExprIdentNode other = (ExprIdentNode) node; + + if (_streamOrPropertyName != null ? !_streamOrPropertyName.Equals(other.StreamOrPropertyName) : other.StreamOrPropertyName != null) + return false; + if (_unresolvedPropertyName != null ? !_unresolvedPropertyName.Equals(other.UnresolvedPropertyName) : other.UnresolvedPropertyName != null) + return false; + return true; + } + + public ExprIdentNodeEvaluator ExprEvaluatorIdent + { + get { return _evaluator; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprIdentNodeUtil.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprIdentNodeUtil.cs new file mode 100755 index 000000000..b08543dfc --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprIdentNodeUtil.cs @@ -0,0 +1,275 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.core +{ + public class ExprIdentNodeUtil + { + public static Pair GetTypeFromStream( + StreamTypeService streamTypeService, + string propertyNameNestable, + bool explicitPropertiesOnly, + bool obtainFragment) + { + string streamOrProp = null; + var prop = propertyNameNestable; + if (propertyNameNestable.IndexOf('.') != -1) + { + prop = propertyNameNestable.Substring(propertyNameNestable.IndexOf('.') + 1); + streamOrProp = propertyNameNestable.Substring(0, propertyNameNestable.IndexOf('.')); + } + if (explicitPropertiesOnly) + { + return GetTypeFromStreamExplicitProperties(streamTypeService, prop, streamOrProp, obtainFragment); + } + return GetTypeFromStream(streamTypeService, prop, streamOrProp, obtainFragment); + } + + internal static Pair GetTypeFromStream( + StreamTypeService streamTypeService, + string unresolvedPropertyName, + string streamOrPropertyName, + bool obtainFragment) + { + PropertyResolutionDescriptor propertyInfo = null; + + // no stream/property name supplied + if (streamOrPropertyName == null) + { + try + { + propertyInfo = streamTypeService.ResolveByPropertyName(unresolvedPropertyName, obtainFragment); + } + catch (StreamTypesException ex) + { + throw GetSuggestionException(ex); + } + catch (PropertyAccessException ex) + { + throw new ExprValidationPropertyException( + "Failed to find property '" + unresolvedPropertyName + + "', the property name does not parse (are you sure?): " + ex.Message, ex); + } + + // resolves without a stream name, return descriptor and null stream name + return new Pair(propertyInfo, propertyInfo.StreamName); + } + + // try to resolve the property name and stream name as it is (ie. stream name as a stream name) + StreamTypesException typeExceptionOne; + try + { + propertyInfo = streamTypeService.ResolveByStreamAndPropName( + streamOrPropertyName, unresolvedPropertyName, obtainFragment); + // resolves with a stream name, return descriptor and stream name + return new Pair(propertyInfo, streamOrPropertyName); + } + catch (StreamTypesException ex) + { + typeExceptionOne = ex; + } + + // try to resolve the property name to a nested property 's0.p0' + StreamTypesException typeExceptionTwo; + var propertyNameCandidate = streamOrPropertyName + '.' + unresolvedPropertyName; + try + { + propertyInfo = streamTypeService.ResolveByPropertyName(propertyNameCandidate, obtainFragment); + // resolves without a stream name, return null for stream name + return new Pair(propertyInfo, null); + } + catch (StreamTypesException ex) + { + typeExceptionTwo = ex; + } + + // not resolved yet, perhaps the table name did not match an event type + if (streamTypeService.HasTableTypes && streamOrPropertyName != null) + { + for (var i = 0; i < streamTypeService.EventTypes.Length; i++) + { + var eventType = streamTypeService.EventTypes[i]; + var tableName = TableServiceUtil.GetTableNameFromEventType(eventType); + if (tableName != null && tableName.Equals(streamOrPropertyName)) + { + try + { + propertyInfo = streamTypeService.ResolveByStreamAndPropName( + eventType.Name, unresolvedPropertyName, obtainFragment); + } + catch (Exception ex) + { + } + if (propertyInfo != null) + { + return new Pair(propertyInfo, streamOrPropertyName); + } + } + } + } + + // see if the stream or property name (the prefix) can be resolved by itself, without suffix + // the property available may be indexed or mapped + try + { + var desc = streamTypeService.ResolveByPropertyName(streamOrPropertyName, false); + if (desc != null) + { + var d2 = desc.StreamEventType.GetPropertyDescriptor(streamOrPropertyName); + if (d2 != null) + { + string text = null; + if (d2.IsIndexed) + { + text = "an indexed property and requires an index or enumeration method to access values"; + } + if (d2.IsMapped) + { + text = "a mapped property and requires keyed access"; + } + if (text != null) + { + throw new ExprValidationPropertyException( + "Failed to resolve property '" + propertyNameCandidate + "' (property '" + + streamOrPropertyName + "' is " + text + ")"); + } + } + } + } + catch (StreamTypesException e) + { + // need not be handled + } + + throw GetSuggestionExceptionSecondStep(propertyNameCandidate, typeExceptionOne, typeExceptionTwo); + } + + internal static Pair GetTypeFromStreamExplicitProperties( + StreamTypeService streamTypeService, + string unresolvedPropertyName, + string streamOrPropertyName, + bool obtainFragment) + { + PropertyResolutionDescriptor propertyInfo; + + // no stream/property name supplied + if (streamOrPropertyName == null) + { + try + { + propertyInfo = streamTypeService.ResolveByPropertyNameExplicitProps( + unresolvedPropertyName, obtainFragment); + } + catch (StreamTypesException ex) + { + throw GetSuggestionException(ex); + } + catch (PropertyAccessException ex) + { + throw new ExprValidationPropertyException(ex.Message); + } + + // resolves without a stream name, return descriptor and null stream name + return new Pair(propertyInfo, propertyInfo.StreamName); + } + + // try to resolve the property name and stream name as it is (ie. stream name as a stream name) + StreamTypesException typeExceptionOne; + try + { + propertyInfo = streamTypeService.ResolveByStreamAndPropNameExplicitProps( + streamOrPropertyName, unresolvedPropertyName, obtainFragment); + // resolves with a stream name, return descriptor and stream name + return new Pair(propertyInfo, streamOrPropertyName); + } + catch (StreamTypesException ex) + { + typeExceptionOne = ex; + } + + // try to resolve the property name to a nested property 's0.p0' + StreamTypesException typeExceptionTwo; + var propertyNameCandidate = streamOrPropertyName + '.' + unresolvedPropertyName; + try + { + propertyInfo = streamTypeService.ResolveByPropertyNameExplicitProps( + propertyNameCandidate, obtainFragment); + // resolves without a stream name, return null for stream name + return new Pair(propertyInfo, null); + } + catch (StreamTypesException ex) + { + typeExceptionTwo = ex; + } + + throw GetSuggestionExceptionSecondStep(propertyNameCandidate, typeExceptionOne, typeExceptionTwo); + } + + private static ExprValidationPropertyException GetSuggestionExceptionSecondStep( + string propertyNameCandidate, + StreamTypesException typeExceptionOne, + StreamTypesException typeExceptionTwo) + { + var suggestionOne = GetSuggestion(typeExceptionOne); + var suggestionTwo = GetSuggestion(typeExceptionTwo); + if (suggestionOne != null) + { + return new ExprValidationPropertyException(typeExceptionOne.Message + suggestionOne); + } + if (suggestionTwo != null) + { + return new ExprValidationPropertyException(typeExceptionTwo.Message + suggestionTwo); + } + + // fail to resolve + return + new ExprValidationPropertyException( + "Failed to resolve property '" + propertyNameCandidate + + "' to a stream or nested property in a stream"); + } + + private static ExprValidationPropertyException GetSuggestionException(StreamTypesException ex) + { + var suggestion = GetSuggestion(ex); + if (suggestion != null) + { + return new ExprValidationPropertyException(ex.Message + suggestion); + } + else + { + return new ExprValidationPropertyException(ex.Message); + } + } + + private static string GetSuggestion(StreamTypesException ex) + { + if (ex == null) + { + return null; + } + var suggestion = ex.OptionalSuggestion; + if (suggestion == null) + { + return null; + } + if (suggestion.First > LevenshteinDistance.ACCEPTABLE_DISTANCE) + { + return null; + } + return " (did you mean '" + ex.OptionalSuggestion.Second + "'?)"; + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprNamedParameterNode.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprNamedParameterNode.cs new file mode 100755 index 000000000..00a7a1336 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprNamedParameterNode.cs @@ -0,0 +1,18 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +namespace com.espertech.esper.epl.expression.core +{ + public interface ExprNamedParameterNode + { + string ParameterName { get; } + IList ChildNodes { get; } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprNamedParameterNodeImpl.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprNamedParameterNodeImpl.cs new file mode 100755 index 000000000..17fb3e853 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprNamedParameterNodeImpl.cs @@ -0,0 +1,85 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.epl.expression.core +{ + [Serializable] + public class ExprNamedParameterNodeImpl + : ExprNodeBase + , ExprNamedParameterNode + , ExprEvaluator + { + private readonly string _parameterName; + + public ExprNamedParameterNodeImpl(string parameterName) + { + _parameterName = parameterName; + } + + public string ParameterName + { + get { return _parameterName; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + writer.Write(_parameterName); + writer.Write(":"); + if (ChildNodes.Count > 1) { + writer.Write("("); + } + ExprNodeUtility.ToExpressionStringParameterList(ChildNodes, writer); + if (ChildNodes.Count > 1) + { + writer.Write(")"); + } + } + + public override ExprEvaluator ExprEvaluator + { + get { return this; } + } + + public override ExprPrecedenceEnum Precedence + { + get { return ExprPrecedenceEnum.UNARY; } + } + + public override bool IsConstantResult + { + get { return false; } + } + + public override bool EqualsNode(ExprNode other) + { + if (!(other is ExprNamedParameterNode)) { + return false; + } + var otherNamed = (ExprNamedParameterNode) other; + return otherNamed.ParameterName.Equals(_parameterName); + } + + public override ExprNode Validate(ExprValidationContext validationContext) + { + return null; + } + + public object Evaluate(EvaluateParams evaluateParams) + { + return null; + } + + public Type ReturnType + { + get { return null; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprNode.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprNode.cs new file mode 100755 index 000000000..26958baac --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprNode.cs @@ -0,0 +1,88 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.epl.expression.visitor; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.core +{ + public interface ExprNode + : ExprValidator + , MetaDefItem + { + ExprEvaluator ExprEvaluator { get; } + + /// + /// Writes the expression node to the writer using the specified precedence. + /// + void ToEPL(TextWriter writer, ExprPrecedenceEnum parentPrecedence); + + /// + /// Returns precedence + /// + ExprPrecedenceEnum Precedence { get; } + + /// + /// Returns true if the expression node's evaluation value doesn't depend on any events data, as must be determined at validation time, + /// which is bottom-up and therefore reliably allows each node to determine constant value. + /// + /// + /// true for constant evaluation value, false for non-constant evaluation value + /// + bool IsConstantResult { get; } + + /// + /// Return true if a expression node semantically equals the current node, or false if not. Concrete implementations should compare + /// the type and any additional information that impact the evaluation of a node. + /// + /// to compare to + /// + /// true if semantically equal, or false if not equals + /// + bool EqualsNode(ExprNode node); + + /// + /// Accept the visitor. The visitor will first visit the parent then visit all child nodes, then their child nodes. The visitor can + /// decide to skip child nodes by returning false in isVisit. + /// + /// to visit each node and each child node. + void Accept(ExprNodeVisitor visitor); + + /// + /// Accept the visitor. The visitor will first visit the parent then visit all child nodes, then their child nodes. The visitor can + /// decide to skip child nodes by returning false in isVisit. + /// + /// to visit each node and each child node. + void Accept(ExprNodeVisitorWithParent visitor); + + /// Accept a visitor that receives both parent and child node. + /// to apply + /// node + void AcceptChildnodes(ExprNodeVisitorWithParent visitor, ExprNode parent); + + /// Adds a child node. + /// is the child evaluation tree node to add + void AddChildNode(ExprNode childNode); + + /// Adds child nodes. + /// are the child evaluation tree node to add + void AddChildNodes(ICollection childNodes); + + /// Returns list of child nodes. + /// list of child nodes + IList ChildNodes { get; set; } + + void ReplaceUnlistedChildNode(ExprNode nodeToReplace, ExprNode newNode); + + void SetChildNodes(params ExprNode[] nodes); + void SetChildNode(int index, ExprNode newNode); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodeBase.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodeBase.cs new file mode 100755 index 000000000..d0621ff1e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodeBase.cs @@ -0,0 +1,141 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.visitor; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.core +{ + /// + /// Superclass for filter nodes in a filter expression tree. Allow validation against + /// stream event types and evaluation of events against filter tree. + /// + [Serializable] + public abstract class ExprNodeBase : ExprNode + { + private IList _childNodes; + + /// Constructor creates a list of child nodes. + protected ExprNodeBase() + { + _childNodes = ExprNodeUtility.EMPTY_EXPR_ARRAY; + } + + public abstract void ToPrecedenceFreeEPL(TextWriter writer); + + public virtual void Accept(ExprNodeVisitor visitor) + { + if (visitor.IsVisit(this)) + { + visitor.Visit(this); + + foreach (ExprNode childNode in _childNodes) + { + childNode.Accept(visitor); + } + } + } + + public virtual void Accept(ExprNodeVisitorWithParent visitor) + { + if (visitor.IsVisit(this)) + { + visitor.Visit(this, null); + + foreach (ExprNode childNode in _childNodes) + { + childNode.AcceptChildnodes(visitor, this); + } + } + } + + public virtual void AcceptChildnodes(ExprNodeVisitorWithParent visitor, ExprNode parent) + { + if (visitor.IsVisit(this)) + { + visitor.Visit(this, parent); + + foreach (ExprNode childNode in _childNodes) + { + childNode.AcceptChildnodes(visitor, this); + } + } + } + + public virtual void AddChildNode(ExprNode childNode) + { + if (_childNodes.IsReadOnly) + { + _childNodes = new List(_childNodes); + } + + _childNodes.Add(childNode); + } + + public virtual void AddChildNodes(ICollection childNodeColl) + { + if (_childNodes.IsReadOnly) + { + _childNodes = new List(_childNodes); + } + + _childNodes.AddAll(childNodeColl); + } + + public virtual IList ChildNodes + { + get { return _childNodes; } + set { _childNodes = value; } + } + + public void SetChildNodes(params ExprNode[] nodes) + { + _childNodes = nodes; + } + + public virtual void ReplaceUnlistedChildNode(ExprNode nodeToReplace, ExprNode newNode) + { + // Override to replace child expression nodes that are chained or otherwise not listed as child nodes + } + + public virtual void AddChildNodeToFront(ExprNode childNode) + { + _childNodes = (ExprNode[]) CollectionUtil.ArrayExpandAddElements(new ExprNode[] {childNode}, _childNodes); + } + + public virtual void SetChildNode(int index, ExprNode newNode) + { + _childNodes[index] = newNode; + } + + public virtual void ToEPL(TextWriter writer, ExprPrecedenceEnum parentPrecedence) + { + if (Precedence.GetLevel() < parentPrecedence.GetLevel()) + { + writer.Write("("); + ToPrecedenceFreeEPL(writer); + writer.Write(")"); + } + else + { + ToPrecedenceFreeEPL(writer); + } + } + + public abstract ExprNode Validate(ExprValidationContext validationContext); + public abstract ExprEvaluator ExprEvaluator { get; } + public abstract bool IsConstantResult { get; } + public abstract bool EqualsNode(ExprNode node); + public abstract ExprPrecedenceEnum Precedence { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodeInnerNodeProvider.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodeInnerNodeProvider.cs new file mode 100755 index 000000000..cbea40595 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodeInnerNodeProvider.cs @@ -0,0 +1,17 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +namespace com.espertech.esper.epl.expression.core +{ + public interface ExprNodeInnerNodeProvider + { + IList AdditionalNodes { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodeOrigin.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodeOrigin.cs new file mode 100755 index 000000000..d3972135c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodeOrigin.cs @@ -0,0 +1,163 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.expression.core +{ + public enum ExprNodeOrigin + { + SELECT, + WHERE, + GROUPBY, + HAVING, + METHODINVJOIN, + DATABASEPOLL, + CONTEXT, + CONTEXTDISTINCT, + CONTEXTCONDITION, + VARIABLEASSIGN, + DATAFLOW, + DATAFLOWBEACON, + DATAFLOWFILTER, + UPDATEASSIGN, + PLUGINSINGLEROWPARAM, + AGGPARAM, + OUTPUTLIMIT, + DECLAREDEXPRPARAM, + DECLAREDEXPRBODY, + ALIASEXPRBODY, + ORDERBY, + SCRIPTPARAMS, + FOLLOWEDBYMAX, + PATTERNMATCHUNTILBOUNDS, + PATTERNGUARD, + PATTERNEVERYDISTINCT, + PATTERNOBSERVER, + DOTNODEPARAMETER, + DOTNODE, + CONTAINEDEVENT, + CREATEWINDOWFILTER, + CREATETABLECOLUMN, + SUBQUERYSELECT, + FILTER, + FORCLAUSE, + VIEWPARAMETER, + MATCHRECOGDEFINE, + MATCHRECOGMEASURE, + MATCHRECOGPARTITION, + MATCHRECOGINTERVAL, + MATCHRECOGPATTERN, + JOINON, + MERGEMATCHCOND, + MERGEMATCHWHERE, + HINT + } + + public static class ExprNodeOriginExtensions + { + public static string GetClauseName(this ExprNodeOrigin enumValue) + { + switch (enumValue) + { + case ExprNodeOrigin.SELECT: + return ("select-clause"); + case ExprNodeOrigin.WHERE: + return ("where-clause"); + case ExprNodeOrigin.GROUPBY: + return ("group-by-clause"); + case ExprNodeOrigin.HAVING: + return ("having-clause"); + case ExprNodeOrigin.METHODINVJOIN: + return ("from-clause method-invocation"); + case ExprNodeOrigin.DATABASEPOLL: + return ("from-clause database-access parameter"); + case ExprNodeOrigin.CONTEXT: + return ("context declaration"); + case ExprNodeOrigin.CONTEXTDISTINCT: + return ("context distinct-clause"); + case ExprNodeOrigin.CONTEXTCONDITION: + return ("context condition"); + case ExprNodeOrigin.VARIABLEASSIGN: + return ("variable-assignment"); + case ExprNodeOrigin.DATAFLOW: + return ("dataflow operator"); + case ExprNodeOrigin.DATAFLOWBEACON: + return ("beacon dataflow operator"); + case ExprNodeOrigin.DATAFLOWFILTER: + return ("filter dataflow operator"); + case ExprNodeOrigin.UPDATEASSIGN: + return ("update assignment"); + case ExprNodeOrigin.PLUGINSINGLEROWPARAM: + return ("single-row function parameter"); + case ExprNodeOrigin.AGGPARAM: + return ("aggregation function parameter"); + case ExprNodeOrigin.OUTPUTLIMIT: + return ("output limit"); + case ExprNodeOrigin.DECLAREDEXPRPARAM: + return ("declared expression parameter"); + case ExprNodeOrigin.DECLAREDEXPRBODY: + return ("declared expression body"); + case ExprNodeOrigin.ALIASEXPRBODY: + return ("alias expression body"); + case ExprNodeOrigin.ORDERBY: + return ("order-by-clause"); + case ExprNodeOrigin.SCRIPTPARAMS: + return ("script parameter"); + case ExprNodeOrigin.FOLLOWEDBYMAX: + return ("pattern followed-by max"); + case ExprNodeOrigin.PATTERNMATCHUNTILBOUNDS: + return ("pattern match-until bounds"); + case ExprNodeOrigin.PATTERNGUARD: + return ("pattern guard"); + case ExprNodeOrigin.PATTERNEVERYDISTINCT: + return ("pattern every-distinct"); + case ExprNodeOrigin.PATTERNOBSERVER: + return ("pattern observer"); + case ExprNodeOrigin.DOTNODEPARAMETER: + return ("method-chain parameter"); + case ExprNodeOrigin.DOTNODE: + return ("method-chain"); + case ExprNodeOrigin.CONTAINEDEVENT: + return ("contained-event"); + case ExprNodeOrigin.CREATEWINDOWFILTER: + return ("create-window filter"); + case ExprNodeOrigin.CREATETABLECOLUMN: + return ("table-column"); + case ExprNodeOrigin.SUBQUERYSELECT: + return ("subquery select-clause"); + case ExprNodeOrigin.FILTER: + return ("filter"); + case ExprNodeOrigin.FORCLAUSE: + return ("for-clause"); + case ExprNodeOrigin.VIEWPARAMETER: + return ("view parameter"); + case ExprNodeOrigin.MATCHRECOGDEFINE: + return ("match-recognize define"); + case ExprNodeOrigin.MATCHRECOGMEASURE: + return ("match-recognize measure"); + case ExprNodeOrigin.MATCHRECOGPARTITION: + return ("match-recognize partition"); + case ExprNodeOrigin.MATCHRECOGINTERVAL: + return ("match-recognize interval"); + case ExprNodeOrigin.MATCHRECOGPATTERN: + return ("match-recognize pattern"); + case ExprNodeOrigin.JOINON: + return ("on-clause join"); + case ExprNodeOrigin.MERGEMATCHCOND: + return ("match condition"); + case ExprNodeOrigin.MERGEMATCHWHERE: + return ("match where-clause"); + case ExprNodeOrigin.HINT: + return ("hint"); + } + + throw new ArgumentException("invalid value for enumValue", "enumValue"); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodePropOrStreamDesc.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodePropOrStreamDesc.cs new file mode 100755 index 000000000..559902b55 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodePropOrStreamDesc.cs @@ -0,0 +1,17 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.expression.core +{ + public interface ExprNodePropOrStreamDesc + { + int StreamNum { get; } + string Textual { get; } + } + +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodePropOrStreamExprDesc.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodePropOrStreamExprDesc.cs new file mode 100755 index 000000000..643c426e4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodePropOrStreamExprDesc.cs @@ -0,0 +1,31 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.expression.core +{ + public class ExprNodePropOrStreamExprDesc : ExprNodePropOrStreamDesc + { + public ExprNodePropOrStreamExprDesc(int streamNum, ExprNode originator) + { + StreamNum = streamNum; + Originator = originator; + } + + public int StreamNum { get; private set; } + + public ExprNode Originator { get; private set; } + + public string Textual + { + get + { + return "expression '" + ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(Originator) + "' against stream " + StreamNum; + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodePropOrStreamPropDesc.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodePropOrStreamPropDesc.cs new file mode 100755 index 000000000..279059d05 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodePropOrStreamPropDesc.cs @@ -0,0 +1,56 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.expression.core +{ + public class ExprNodePropOrStreamPropDesc : ExprNodePropOrStreamDesc + { + public ExprNodePropOrStreamPropDesc(int streamNum, string propertyName) { + this.StreamNum = streamNum; + this.PropertyName = propertyName; + if (propertyName == null) { + throw new ArgumentException("Property name is null"); + } + } + + public string PropertyName { get; private set; } + + public int StreamNum { get; private set; } + + public string Textual + { + get { return "property '" + PropertyName + "'"; } + } + + protected bool Equals(ExprNodePropOrStreamPropDesc other) + { + return StreamNum == other.StreamNum && string.Equals(PropertyName, other.PropertyName); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) + return false; + if (ReferenceEquals(this, obj)) + return true; + if (obj.GetType() != this.GetType()) + return false; + return Equals((ExprNodePropOrStreamPropDesc) obj); + } + + public override int GetHashCode() + { + unchecked + { + return ((PropertyName != null ? PropertyName.GetHashCode() : 0)*397) ^ StreamNum; + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodePropOrStreamSet.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodePropOrStreamSet.cs new file mode 100755 index 000000000..45f562121 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodePropOrStreamSet.cs @@ -0,0 +1,168 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.epl.expression.core +{ + public class ExprNodePropOrStreamSet + { + private ISet _properties; + private IList _expressions; + + public ExprNodePropOrStreamSet() + { + } + + public void Add(ExprNodePropOrStreamDesc desc) + { + if (desc is ExprNodePropOrStreamPropDesc) { + AllocateProperties(); + _properties.Add((ExprNodePropOrStreamPropDesc) desc); + } + else if (desc is ExprNodePropOrStreamExprDesc) { + AllocateExpressions(); + _expressions.Add((ExprNodePropOrStreamExprDesc) desc); + } + } + + public void AddAll(IList propertiesNode) + { + foreach (ExprNodePropOrStreamDesc desc in propertiesNode) { + Add(desc); + } + } + + public void AddAll(ExprNodePropOrStreamSet other) { + if (other._properties != null) { + AllocateProperties(); + _properties.AddAll(other._properties); + } + if (other._expressions != null) { + AllocateExpressions(); + _expressions.AddAll(other._expressions); + } + } + + public bool IsEmpty() { + return (_properties == null || _properties.IsEmpty()) && + (_expressions == null || _expressions.IsEmpty()); + } + + /// + /// Remove from the provided list those that are matching any of the contained-herein + /// + /// target list + public void RemoveFromList(IList items) + { + items.RemoveWhere(FindItem); + } + + public string NotContainsAll(ExprNodePropOrStreamSet other) { + if (other._properties != null) { + foreach (ExprNodePropOrStreamPropDesc otherProp in other._properties) { + bool found = FindItem(otherProp); + if (!found) { + return otherProp.Textual; + } + } + } + if (other._expressions != null) { + foreach (ExprNodePropOrStreamExprDesc otherExpr in other._expressions) { + bool found = FindItem(otherExpr); + if (!found) { + return otherExpr.Textual; + } + } + } + return null; + } + + public ICollection Properties + { + get + { + if (_properties == null) + { + return Collections.GetEmptyList(); + } + return _properties; + } + } + + public ExprNodePropOrStreamExprDesc FirstExpression + { + get { return _expressions != null ? _expressions.FirstOrDefault() : null; } + } + + public ExprNodePropOrStreamDesc FirstWithStreamNumNotZero + { + get + { + if (_properties != null) + { + foreach (ExprNodePropOrStreamPropDesc prop in _properties) + { + if (prop.StreamNum != 0) + { + return prop; + } + } + } + if (_expressions != null) + { + foreach (ExprNodePropOrStreamExprDesc expr in _expressions) + { + if (expr.StreamNum != 0) + { + return expr; + } + } + } + return null; + } + } + + private void AllocateProperties() + { + if (_properties == null) { + _properties = new HashSet(); + } + } + + private void AllocateExpressions() + { + if (_expressions == null) { + _expressions = new List(4); + } + } + + private bool FindItem(ExprNodePropOrStreamDesc item) + { + if (item is ExprNodePropOrStreamPropDesc) { + return _properties != null && _properties.Contains(item); + } + if (_expressions == null) { + return false; + } + var exprItem = (ExprNodePropOrStreamExprDesc) item; + foreach (ExprNodePropOrStreamExprDesc expression in _expressions) { + if (expression.StreamNum != exprItem.StreamNum) { + continue; + } + if (ExprNodeUtility.DeepEquals(expression.Originator, exprItem.Originator)) { + return true; + } + } + return false; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodeProxy.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodeProxy.cs new file mode 100755 index 000000000..c1c0cf40c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodeProxy.cs @@ -0,0 +1,77 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Linq; +using System.Reflection; + +using Castle.DynamicProxy; + +namespace com.espertech.esper.epl.expression.core +{ + public class ExprNodeProxy : IInterceptor + { + private static readonly MethodInfo ExprEvaluatorGetterMethod = + typeof(ExprNode).GetProperty("ExprEvaluator").GetGetMethod(); + + private static readonly Assembly CastleAssembly = + typeof (ProxyGenerator).Assembly; + + private readonly String _engineURI; + private readonly String _statementName; + private readonly ExprNode _exprNode; + + public static Object NewInstance(String engineURI, String statementName, ExprNode exprNode) + { + var generator = new ProxyGenerator(); + var interfaces = exprNode.GetType() + .GetInterfaces() + .Where(inf => inf.Assembly != CastleAssembly) + .ToArray(); + + return generator.CreateInterfaceProxyWithoutTarget( + typeof(ExprNode), interfaces, + new ExprNodeProxy(engineURI, statementName, exprNode)); + } + + /// + /// Intercepts the specified invocation. + /// + /// The invocation. + public void Intercept(IInvocation invocation) + { + if (invocation.Method != ExprEvaluatorGetterMethod) + { + invocation.ReturnValue = invocation.Method.Invoke(_exprNode, invocation.Arguments); + } + else + { + String expressionToString = "undefined"; + try + { + expressionToString = ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(_exprNode); + } + catch + { + // no action + } + + var evaluator = (ExprEvaluator)invocation.Method.Invoke(_exprNode, invocation.Arguments); + invocation.ReturnValue = ExprEvaluatorProxy.NewInstance( + _engineURI, _statementName, expressionToString, evaluator); + } + } + + public ExprNodeProxy(String engineURI, String statementName, ExprNode exprNode) + { + _engineURI = engineURI; + _statementName = statementName; + _exprNode = exprNode; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodeUtilExprEvalMethodContext.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodeUtilExprEvalMethodContext.cs new file mode 100755 index 000000000..b9d63952b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodeUtilExprEvalMethodContext.cs @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client.hook; + +namespace com.espertech.esper.epl.expression.core +{ + public class ExprNodeUtilExprEvalMethodContext : ExprEvaluator + { + private readonly EPLMethodInvocationContext _defaultContextForFilters; + + public ExprNodeUtilExprEvalMethodContext( + string engineURI, + string functionName, + EventBeanService eventBeanService) + { + _defaultContextForFilters = new EPLMethodInvocationContext( + null, -1, engineURI, functionName, null, eventBeanService); + } + + public object Evaluate(EvaluateParams evaluateParams) + { + var context = evaluateParams.ExprEvaluatorContext; + if (context == null) + { + return _defaultContextForFilters; + } + return new EPLMethodInvocationContext( + context.StatementName, + context.AgentInstanceId, + _defaultContextForFilters.EngineURI, + _defaultContextForFilters.FunctionName, + context.StatementUserObject, + _defaultContextForFilters.EventBeanService + ); + } + + public Type ReturnType + { + get { return typeof (EPLMethodInvocationContext); } + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodeUtilExprEvalStreamNumEnumColl.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodeUtilExprEvalStreamNumEnumColl.cs new file mode 100755 index 000000000..62ec9c2c5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodeUtilExprEvalStreamNumEnumColl.cs @@ -0,0 +1,35 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.epl.expression.core +{ + [Serializable] + public class ExprNodeUtilExprEvalStreamNumEnumColl : ExprEvaluator + { + private readonly ExprEvaluatorEnumeration _enumeration; + + public ExprNodeUtilExprEvalStreamNumEnumColl(ExprEvaluatorEnumeration enumeration) + { + _enumeration = enumeration; + } + + public object Evaluate(EvaluateParams evaluateParams) + { + return _enumeration.EvaluateGetROCollectionEvents(evaluateParams); + } + + public Type ReturnType + { + get { return typeof (EventBean); } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodeUtilExprEvalStreamNumEnumSingle.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodeUtilExprEvalStreamNumEnumSingle.cs new file mode 100755 index 000000000..90d736bb4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodeUtilExprEvalStreamNumEnumSingle.cs @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.epl.expression.core +{ + [Serializable] + public class ExprNodeUtilExprEvalStreamNumEnumSingle : ExprEvaluator + { + private readonly ExprEvaluatorEnumeration _enumeration; + + public ExprNodeUtilExprEvalStreamNumEnumSingle(ExprEvaluatorEnumeration enumeration) + { + _enumeration = enumeration; + } + + public object Evaluate(EvaluateParams evaluateParams) + { + return _enumeration.EvaluateGetEventBean(evaluateParams); + } + + public Type ReturnType + { + get { return typeof (ICollection); } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodeUtilExprEvalStreamNumEvent.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodeUtilExprEvalStreamNumEvent.cs new file mode 100755 index 000000000..b627af2e4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodeUtilExprEvalStreamNumEvent.cs @@ -0,0 +1,36 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.epl.expression.core +{ + [Serializable] + public class ExprNodeUtilExprEvalStreamNumEvent + : ExprEvaluator + { + private readonly int _streamNum; + + public ExprNodeUtilExprEvalStreamNumEvent(int streamNum) + { + _streamNum = streamNum; + } + + public object Evaluate(EvaluateParams evaluateParams) + { + return evaluateParams.EventsPerStream[_streamNum]; + } + + public Type ReturnType + { + get { return typeof (EventBean); } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodeUtilExprEvalStreamNumEventTable.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodeUtilExprEvalStreamNumEventTable.cs new file mode 100755 index 000000000..4f09b9fef --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodeUtilExprEvalStreamNumEventTable.cs @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.table.mgmt; + +namespace com.espertech.esper.epl.expression.core +{ + public class ExprNodeUtilExprEvalStreamNumEventTable : ExprEvaluator + { + private readonly int _streamNum; + private readonly TableMetadata _tableMetadata; + + public ExprNodeUtilExprEvalStreamNumEventTable(int streamNum, TableMetadata tableMetadata) + { + _streamNum = streamNum; + _tableMetadata = tableMetadata; + } + + public object Evaluate(EvaluateParams evaluateParams) + { + var @event = evaluateParams.EventsPerStream[_streamNum]; + if (@event == null) { + return null; + } + return _tableMetadata.EventToPublic.Convert(@event, evaluateParams); + } + + public Type ReturnType + { + get { return typeof (EventBean); } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodeUtilExprEvalStreamNumUnd.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodeUtilExprEvalStreamNumUnd.cs new file mode 100755 index 000000000..c5d8e276a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodeUtilExprEvalStreamNumUnd.cs @@ -0,0 +1,35 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.expression.core +{ + [Serializable] + public class ExprNodeUtilExprEvalStreamNumUnd : ExprEvaluator + { + private readonly int _streamNum; + private readonly Type _returnType; + + public ExprNodeUtilExprEvalStreamNumUnd(int streamNum, Type returnType) + { + _streamNum = streamNum; + _returnType = returnType; + } + + public object Evaluate(EvaluateParams evaluateParams) + { + return evaluateParams.EventsPerStream[_streamNum].Underlying; + } + + public Type ReturnType + { + get { return _returnType; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodeUtilMethodDesc.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodeUtilMethodDesc.cs new file mode 100755 index 000000000..1700a7b04 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodeUtilMethodDesc.cs @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; + +using XLR8.CGLib; + +namespace com.espertech.esper.epl.expression.core +{ + public class ExprNodeUtilMethodDesc + { + public ExprNodeUtilMethodDesc( + bool allConstants, + Type[] paramTypes, + ExprEvaluator[] childEvals, + MethodInfo reflectionMethod, + FastMethod fastMethod, + EventType optionalEventType) + { + IsAllConstants = allConstants; + ParamTypes = paramTypes; + ChildEvals = childEvals; + ReflectionMethod = reflectionMethod; + FastMethod = fastMethod; + OptionalEventType = optionalEventType; + } + + public bool IsAllConstants { get; private set; } + + public Type[] ParamTypes { get; private set; } + + public ExprEvaluator[] ChildEvals { get; private set; } + + public MethodInfo ReflectionMethod { get; private set; } + + public FastMethod FastMethod { get; private set; } + + public EventType OptionalEventType { get; private set; } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodeUtilResolveExceptionHandler.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodeUtilResolveExceptionHandler.cs new file mode 100755 index 000000000..838d3d35a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodeUtilResolveExceptionHandler.cs @@ -0,0 +1,35 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.expression.core +{ + public interface ExprNodeUtilResolveExceptionHandler { + ExprValidationException Handle(Exception e); + } + + public class ProxyExprNodeUtilResolveExceptionHandler : ExprNodeUtilResolveExceptionHandler + { + public Func ProcHandle { get; set; } + + public ProxyExprNodeUtilResolveExceptionHandler() + { + } + + public ProxyExprNodeUtilResolveExceptionHandler(Func procHandle) + { + ProcHandle = procHandle; + } + + public ExprValidationException Handle(Exception e) + { + return ProcHandle.Invoke(e); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodeUtilResolveExceptionHandlerDefault.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodeUtilResolveExceptionHandlerDefault.cs new file mode 100755 index 000000000..a2c798f1b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodeUtilResolveExceptionHandlerDefault.cs @@ -0,0 +1,33 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.expression.core +{ + public class ExprNodeUtilResolveExceptionHandlerDefault : ExprNodeUtilResolveExceptionHandler { + private readonly String resolvedExpression; + private readonly bool configuredAsSingleRow; + + public ExprNodeUtilResolveExceptionHandlerDefault(String resolvedExpression, bool configuredAsSingleRow) { + this.resolvedExpression = resolvedExpression; + this.configuredAsSingleRow = configuredAsSingleRow; + } + + public ExprValidationException Handle(Exception e) { + String message; + if (configuredAsSingleRow) { + message = e.Message; + } + else { + message = "Failed to resolve '" + resolvedExpression + "' to a property, single-row function, aggregation function, script, stream or class name"; + } + return new ExprValidationException(message, e); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodeUtilUnderlyingEvaluator.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodeUtilUnderlyingEvaluator.cs new file mode 100755 index 000000000..952f3ed6d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodeUtilUnderlyingEvaluator.cs @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.epl.expression.core +{ + public class ExprNodeUtilUnderlyingEvaluator : ExprEvaluator { + private readonly int streamNum; + private readonly Type resultType; + + public ExprNodeUtilUnderlyingEvaluator(int streamNum, Type resultType) { + this.streamNum = streamNum; + this.resultType = resultType; + } + + public object Evaluate(EvaluateParams evaluateParams) + { + return Evaluate( + evaluateParams.EventsPerStream, + evaluateParams.IsNewData, + evaluateParams.ExprEvaluatorContext); + } + + public object Evaluate(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + if ((eventsPerStream == null) || (eventsPerStream[streamNum] == null)) { + return null; + } + return eventsPerStream[streamNum].Underlying; + } + + public Type ReturnType + { + get { return resultType; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodeUtilUnderlyingEvaluatorTable.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodeUtilUnderlyingEvaluatorTable.cs new file mode 100755 index 000000000..dc0002e98 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodeUtilUnderlyingEvaluatorTable.cs @@ -0,0 +1,49 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.table.mgmt; + +namespace com.espertech.esper.epl.expression.core +{ + public class ExprNodeUtilUnderlyingEvaluatorTable : ExprEvaluator + { + private readonly int _streamNum; + private readonly Type _resultType; + private readonly TableMetadata _tableMetadata; + + public ExprNodeUtilUnderlyingEvaluatorTable(int streamNum, Type resultType, TableMetadata tableMetadata) + { + _streamNum = streamNum; + _resultType = resultType; + _tableMetadata = tableMetadata; + } + + public object Evaluate(EvaluateParams evaluateParams) + { + if (evaluateParams.EventsPerStream == null) + { + return null; + } + var @event = evaluateParams.EventsPerStream[_streamNum]; + if (@event == null) { + return null; + } + return _tableMetadata.EventToPublic.ConvertToUnd(@event, evaluateParams); + } + + public Type ReturnType + { + get { return _resultType; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodeUtility.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodeUtility.cs new file mode 100755 index 000000000..f10c63020 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodeUtility.cs @@ -0,0 +1,1886 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; + +using com.espertech.esper.client; +using com.espertech.esper.client.hook; +using com.espertech.esper.client.util; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.core.start; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.declexpr; +using com.espertech.esper.epl.enummethod.dot; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.dot; +using com.espertech.esper.epl.expression.funcs; +using com.espertech.esper.epl.expression.methodagg; +using com.espertech.esper.epl.expression.ops; +using com.espertech.esper.epl.expression.subquery; +using com.espertech.esper.epl.expression.table; +using com.espertech.esper.epl.expression.time; +using com.espertech.esper.epl.expression.visitor; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.events; +using com.espertech.esper.schedule; +using com.espertech.esper.util; + +using XLR8.CGLib; + +namespace com.espertech.esper.epl.expression.core +{ + public static class ExprNodeUtility + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public static readonly ExprNode[] EMPTY_EXPR_ARRAY = new ExprNode[0]; + public static readonly ExprDeclaredNode[] EMPTY_DECLARED_ARR = new ExprDeclaredNode[0]; + public static readonly ExpressionScriptProvided[] EMPTY_SCRIPTS = new ExpressionScriptProvided[0]; + + public static bool DeepEqualsIsSubset(IList subset, ExprNode[] superset) { + foreach (var subsetNode in subset) { + var found = false; + foreach (var supersetNode in superset) { + if (DeepEquals(subsetNode, supersetNode)) { + found = true; + break; + } + } + if (!found) { + return false; + } + } + return true; + } + + public static bool DeepEqualsIgnoreDupAndOrder(IList setOne, IList setTwo) { + if ((setOne.Count == 0 && setTwo.Count != 0) || (setOne.Count != 0 && setTwo.Count == 0)) + { + return false; + } + + // find set-one expressions in set two + var foundTwo = new bool[setTwo.Count]; + foreach (var one in setOne) { + var found = false; + for (var i = 0; i < setTwo.Count; i++) + { + if (DeepEquals(one, setTwo[i])) { + found = true; + foundTwo[i] = true; + } + } + if (!found) { + return false; + } + } + + // find any remaining set-two expressions in set one + for (var i = 0; i < foundTwo.Length; i++) { + if (foundTwo[i]) { + continue; + } + foreach (var one in setOne) { + if (DeepEquals(one, setTwo[i])) { + break; + } + } + return false; + } + return true; + } + + public static IDictionary> GetDeclaredExpressionCallHierarchy( + ExprDeclaredNode[] declaredExpressions) + { + var visitor = new ExprNodeSubselectDeclaredDotVisitor(); + var calledToCallerMap = new Dictionary>(); + foreach (var node in declaredExpressions) + { + visitor.Reset(); + node.Accept(visitor); + foreach (var called in visitor.DeclaredExpressions) + { + if (called == node) + { + continue; + } + var callers = calledToCallerMap.Get(called); + if (callers == null) + { + callers = new List(2); + calledToCallerMap.Put(called, callers); + } + callers.Add(node); + } + if (!calledToCallerMap.ContainsKey(node)) + { + calledToCallerMap.Put(node, Collections.GetEmptyList()); + } + } + return calledToCallerMap; + } + + public static string ToExpressionStringMinPrecedenceSafe(this ExprNode node) + { + try + { + var writer = new StringWriter(); + node.ToEPL(writer, ExprPrecedenceEnum.MINIMUM); + return writer.ToString(); + } + catch (Exception ex) + { + Log.Debug("Failed to render expression text: " + ex.Message, ex); + return ""; + } + } + + public static string ToExpressionStringMinPrecedence(IList nodes) + { + var writer = new StringWriter(); + var delimiter = ""; + foreach (var node in nodes) + { + writer.Write(delimiter); + node.ToEPL(writer, ExprPrecedenceEnum.MINIMUM); + delimiter = ","; + } + return writer.ToString(); + } + + public static Pair CheckGetAssignmentToProp(ExprNode node) + { + if (!(node is ExprEqualsNode)) + { + return null; + } + var equals = (ExprEqualsNode) node; + if (!(equals.ChildNodes[0] is ExprIdentNode)) + { + return null; + } + var identNode = (ExprIdentNode) equals.ChildNodes[0]; + return new Pair(identNode.FullUnresolvedName, equals.ChildNodes[1]); + } + + public static Pair CheckGetAssignmentToVariableOrProp(ExprNode node) + { + var prop = CheckGetAssignmentToProp(node); + if (prop != null) + { + return prop; + } + if (!(node is ExprEqualsNode)) + { + return null; + } + var equals = (ExprEqualsNode) node; + + if (equals.ChildNodes[0] is ExprVariableNode) + { + var variableNode = (ExprVariableNode) equals.ChildNodes[0]; + return new Pair(variableNode.VariableNameWithSubProp, equals.ChildNodes[1]); + } + if (equals.ChildNodes[0] is ExprTableAccessNode) + { + throw new ExprValidationException( + "Table access expression not allowed on the left hand side, please remove the table prefix"); + } + return null; + } + + public static void ApplyFilterExpressionsIterable( + IEnumerable iterable, + IList filterExpressions, + ExprEvaluatorContext exprEvaluatorContext, + ICollection eventsInWindow) + { + ExprEvaluator[] evaluators = GetEvaluators(filterExpressions); + var events = new EventBean[1]; + var evaluateParams = new EvaluateParams(events, true, exprEvaluatorContext); + foreach (var theEvent in iterable) + { + events[0] = theEvent; + var add = true; + foreach (var filter in evaluators) + { + var result = filter.Evaluate(evaluateParams); + if ((result == null) || (false.Equals(result))) + { + add = false; + break; + } + } + if (add) + { + eventsInWindow.Add(events[0]); + } + } + } + + public static void ApplyFilterExpressionIterable( + IEnumerator enumerator, + ExprEvaluator filterExpression, + ExprEvaluatorContext exprEvaluatorContext, + ICollection eventsInWindow) + { + var events = new EventBean[1]; + var evaluateParams = new EvaluateParams(events, true, exprEvaluatorContext); + while (enumerator.MoveNext()) + { + events[0] = enumerator.Current; + var result = filterExpression.Evaluate(evaluateParams); + if ((result == null) || (false.Equals(result))) + { + continue; + } + eventsInWindow.Add(events[0]); + } + } + + public static ExprNode ConnectExpressionsByLogicalAnd(IList nodes, ExprNode optionalAdditionalFilter) + { + if (nodes.IsEmpty()) + { + return optionalAdditionalFilter; + } + if (optionalAdditionalFilter == null) + { + if (nodes.Count == 1) + { + return nodes[0]; + } + return ConnectExpressionsByLogicalAnd(nodes); + } + if (nodes.Count == 1) + { + return ConnectExpressionsByLogicalAnd(Collections.List(nodes[0], optionalAdditionalFilter)); + } + var andNode = ConnectExpressionsByLogicalAnd(nodes); + andNode.AddChildNode(optionalAdditionalFilter); + return andNode; + } + + public static ExprAndNode ConnectExpressionsByLogicalAnd(ICollection nodes) + { + if (nodes.Count < 2) + { + throw new ArgumentException("Invalid empty or 1-element list of nodes"); + } + var andNode = new ExprAndNodeImpl(); + foreach (var node in nodes) + { + andNode.AddChildNode(node); + } + return andNode; + } + + /// + /// Walk expression returning properties used. + /// + /// to walk + /// true to visit aggregation nodes + /// list of props + public static IList> GetExpressionProperties(ExprNode exprNode, bool visitAggregateNodes) + { + var visitor = new ExprNodeIdentifierVisitor(visitAggregateNodes); + exprNode.Accept(visitor); + return visitor.ExprProperties; + } + + public static bool IsConstantValueExpr(ExprNode exprNode) + { + var constantNode = exprNode as ExprConstantNode; + if (constantNode == null) + { + return false; + } + + return constantNode.IsConstantValue; + } + + /// + /// Validates the expression node subtree that has this + /// node as root. Some of the nodes of the tree, including the + /// root, might be replaced in the process. + /// + /// validate origin + /// node + /// context + /// when the validation fails + /// + /// the root node of the validated subtree, possibly + /// different than the root node of the unvalidated subtree + /// + public static ExprNode GetValidatedSubtree( + ExprNodeOrigin origin, + ExprNode exprNode, + ExprValidationContext validationContext) + { + if (exprNode is ExprLambdaGoesNode) + { + return exprNode; + } + + try + { + return GetValidatedSubtreeInternal(exprNode, validationContext, true); + } + catch (ExprValidationException ex) + { + try + { + string text; + if (exprNode is ExprSubselectNode) + { + var subselect = (ExprSubselectNode) exprNode; + text = EPStatementStartMethodHelperSubselect.GetSubqueryInfoText( + subselect.SubselectNumber - 1, subselect); + } + else + { + text = ToExpressionStringMinPrecedenceSafe(exprNode); + if (text.Length > 40) + { + var shortened = text.Substring(0, 35); + text = shortened + "...(" + text.Length + " chars)"; + } + text = "'" + text + "'"; + } + throw new ExprValidationException( + "Failed to validate " + + origin.GetClauseName() + + " expression " + + text + ": " + + ex.Message, ex); + } + catch (ExprValidationException) + { + throw; + } + catch (Exception rtex) + { + Log.Debug("Failed to render nice validation message text: " + rtex.Message, rtex); + } + + throw; + } + } + + public static void GetValidatedSubtree( + ExprNodeOrigin origin, + IList exprNode, + ExprValidationContext validationContext) + { + if (exprNode == null) + { + return; + } + for (var i = 0; i < exprNode.Count; i++) + { + exprNode[i] = GetValidatedSubtree(origin, exprNode[i], validationContext); + } + } + + public static void GetValidatedSubtree( + ExprNodeOrigin origin, + ExprNode[][] exprNode, + ExprValidationContext validationContext) + { + if (exprNode == null) + { + return; + } + foreach (var anExprNode in exprNode) + { + GetValidatedSubtree(origin, anExprNode, validationContext); + } + } + + public static ExprNode GetValidatedAssignment(OnTriggerSetAssignment assignment, ExprValidationContext validationContext) { + var strictAssignment = CheckGetAssignmentToVariableOrProp(assignment.Expression); + if (strictAssignment != null) { + var validatedRightSide = GetValidatedSubtreeInternal(strictAssignment.Second, validationContext, true); + assignment.Expression.SetChildNode(1, validatedRightSide); + return assignment.Expression; + } else { + return GetValidatedSubtreeInternal(assignment.Expression, validationContext, true); + } + } + + private static ExprNode GetValidatedSubtreeInternal( + ExprNode exprNode, + ExprValidationContext validationContext, + bool isTopLevel) + { + var result = exprNode; + if (exprNode is ExprLambdaGoesNode) + { + return exprNode; + } + + for (var i = 0; i < exprNode.ChildNodes.Count; i++) + { + var childNode = exprNode.ChildNodes[i]; + if (childNode is ExprDeclaredOrLambdaNode) + { + var node = (ExprDeclaredOrLambdaNode) childNode; + if (node.IsValidated) + { + continue; + } + } + var childNodeValidated = GetValidatedSubtreeInternal(childNode, validationContext, false); + exprNode.SetChildNode(i, childNodeValidated); + } + + try + { + var optionalReplacement = exprNode.Validate(validationContext); + if (optionalReplacement != null) + { + return GetValidatedSubtreeInternal(optionalReplacement, validationContext, isTopLevel); + } + } + catch (ExprValidationException e) + { + if (exprNode is ExprIdentNode) + { + var identNode = (ExprIdentNode) exprNode; + try + { + result = ResolveStaticMethodOrField(identNode, e, validationContext); + } + catch (ExprValidationException ex) + { + e = ex; + result = ResolveAsStreamName(identNode, e, validationContext); + } + } + else + { + throw; + } + } + + // For top-level expressions check if we perform audit + if (isTopLevel) + { + if (validationContext.IsExpressionAudit) + { + return + (ExprNode) + ExprNodeProxy.NewInstance( + validationContext.StreamTypeService.EngineURIQualifier, validationContext.StatementName, + result); + } + } + else + { + if (validationContext.IsExpressionNestedAudit && !(result is ExprIdentNode) && !(IsConstantValueExpr(result))) + { + return + (ExprNode) + ExprNodeProxy.NewInstance( + validationContext.StreamTypeService.EngineURIQualifier, validationContext.StatementName, + result); + } + } + + return result; + } + + private static ExprNode ResolveAsStreamName( + ExprIdentNode identNode, + ExprValidationException existingException, + ExprValidationContext validationContext) + { + var exprStream = new ExprStreamUnderlyingNodeImpl(identNode.UnresolvedPropertyName, false); + + try + { + exprStream.Validate(validationContext); + } + catch (ExprValidationException) + { + throw existingException; + } + + return exprStream; + } + + // Since static method calls such as "Type.Method('a')" and mapped properties "Stream.Property('key')" + // look the same, however as the validation could not resolve "Stream.Property('key')" before calling this method, + // this method tries to resolve the mapped property as a static method. + // Assumes that this is an ExprIdentNode. + private static ExprNode ResolveStaticMethodOrField( + ExprIdentNode identNode, + ExprValidationException propertyException, + ExprValidationContext validationContext) + { + // Reconstruct the original string + var mappedProperty = new StringBuilder(identNode.UnresolvedPropertyName); + if (identNode.StreamOrPropertyName != null) + { + mappedProperty.Insert(0, identNode.StreamOrPropertyName + '.'); + } + + // Parse the mapped property format into a class name, method and single string parameter + var parse = ParseMappedProperty(mappedProperty.ToString()); + if (parse == null) + { + var constNode = ResolveIdentAsEnumConst( + mappedProperty.ToString(), validationContext.EngineImportService); + if (constNode == null) + { + throw propertyException; + } + else + { + return constNode; + } + } + + // If there is a class name, assume a static method is possible. + if (parse.ClassName != null) + { + var parameters = Collections.SingletonList((ExprNode) new ExprConstantNodeImpl(parse.ArgString)); + var chain = new List(); + chain.Add(new ExprChainedSpec(parse.ClassName, Collections.GetEmptyList(), false)); + chain.Add(new ExprChainedSpec(parse.MethodName, parameters, false)); + var result = new ExprDotNodeImpl( + chain, validationContext.EngineImportService.IsDuckType, + validationContext.EngineImportService.IsUdfCache); + + // Validate + try + { + result.Validate(validationContext); + } + catch (ExprValidationException e) + { + throw new ExprValidationException( + "Failed to resolve enumeration method, date-time method or mapped property '" + mappedProperty + + "': " + e.Message); + } + + return result; + } + + // There is no class name, try a single-row function + var functionName = parse.MethodName; + try + { + var classMethodPair = validationContext.EngineImportService.ResolveSingleRow(functionName); + IList parameters = + Collections.SingletonList((ExprNode) new ExprConstantNodeImpl(parse.ArgString)); + IList chain = + Collections.SingletonList(new ExprChainedSpec(classMethodPair.Second.MethodName, parameters, false)); + var result = new ExprPlugInSingleRowNode( + functionName, classMethodPair.First, chain, classMethodPair.Second); + + // Validate + try + { + result.Validate(validationContext); + } + catch (Exception e) + { + throw new ExprValidationException( + "Plug-in aggregation function '" + parse.MethodName + "' failed validation: " + e.Message); + } + + return result; + } + catch (EngineImportUndefinedException) + { + // Not an single-row function + } + catch (EngineImportException e) + { + throw new IllegalStateException("Error resolving single-row function: " + e.Message, e); + } + + // Try an aggregation function factory + try + { + var aggregationFactory = + validationContext.EngineImportService.ResolveAggregationFactory(parse.MethodName); + var result = new ExprPlugInAggNode(false, aggregationFactory, parse.MethodName); + result.AddChildNode(new ExprConstantNodeImpl(parse.ArgString)); + + // Validate + try + { + result.Validate(validationContext); + } + catch (Exception e) + { + throw new ExprValidationException( + "Plug-in aggregation function '" + parse.MethodName + "' failed validation: " + e.Message); + } + + return result; + } + catch (EngineImportUndefinedException) + { + // Not an aggregation function + } + catch (EngineImportException e) + { + throw new IllegalStateException("Error resolving aggregation: " + e.Message, e); + } + + // absolutely cannot be resolved + throw propertyException; + } + + private static ExprConstantNode ResolveIdentAsEnumConst(string constant, EngineImportService engineImportService) + { + var enumValue = TypeHelper.ResolveIdentAsEnumConst(constant, engineImportService, false); + if (enumValue != null) { + return new ExprConstantNodeImpl(enumValue); + } + return null; + } + + /// + /// Parse the mapped property into classname, method and string argument. + /// Mind this has been parsed already and is a valid mapped property. + /// + /// is the string property to be passed as a static method invocation + /// descriptor object + public static MappedPropertyParseResult ParseMappedProperty(string property) { + // get argument + var indexFirstDoubleQuote = property.IndexOf('"'); + var indexFirstSingleQuote = property.IndexOf('\''); + int startArg; + if ((indexFirstSingleQuote == -1) && (indexFirstDoubleQuote == -1)) { + return null; + } + if ((indexFirstSingleQuote != -1) && (indexFirstDoubleQuote != -1)) { + if (indexFirstSingleQuote < indexFirstDoubleQuote) { + startArg = indexFirstSingleQuote; + } else { + startArg = indexFirstDoubleQuote; + } + } else if (indexFirstSingleQuote != -1) { + startArg = indexFirstSingleQuote; + } else { + startArg = indexFirstDoubleQuote; + } + + var indexLastDoubleQuote = property.LastIndexOf('"'); + var indexLastSingleQuote = property.LastIndexOf('\''); + int endArg; + if ((indexLastSingleQuote == -1) && (indexLastDoubleQuote == -1)) { + return null; + } + if ((indexLastSingleQuote != -1) && (indexLastDoubleQuote != -1)) { + if (indexLastSingleQuote > indexLastDoubleQuote) { + endArg = indexLastSingleQuote; + } else { + endArg = indexLastDoubleQuote; + } + } else if (indexLastSingleQuote != -1) { + if (indexLastSingleQuote == indexFirstSingleQuote) { + return null; + } + endArg = indexLastSingleQuote; + } else { + if (indexLastDoubleQuote == indexFirstDoubleQuote) { + return null; + } + endArg = indexLastDoubleQuote; + } + var argument = property.Between(startArg + 1, endArg); + + // get method + var splitDots = property.RegexSplit("[\\.]"); + if (splitDots.Length == 0) { + return null; + } + + // find which element represents the method, its the element with the parenthesis + var indexMethod = -1; + for (var i = 0; i < splitDots.Length; i++) { + if (splitDots[i].Contains('(')) { + indexMethod = i; + break; + } + } + if (indexMethod == -1) { + return null; + } + + var method = splitDots[indexMethod]; + var indexParan = method.IndexOf('('); + method = method.Substring(0, indexParan); + if (method.Length == 0) { + return null; + } + + if (splitDots.Length == 1) { + // no class name + return new MappedPropertyParseResult(null, method, argument); + } + + + // get class + var clazz = new StringBuilder(); + for (var i = 0; i < indexMethod; i++) { + if (i > 0) { + clazz.Append('.'); + } + clazz.Append(splitDots[i]); + } + + return new MappedPropertyParseResult(clazz.ToString(), method, argument); + } + + public static bool IsAllConstants(IEnumerable parameters) + { + return parameters.All(node => node.IsConstantResult); + } + + public static ExprIdentNode GetExprIdentNode(EventType[] typesPerStream, int streamId, string property) + { + return new ExprIdentNodeImpl(typesPerStream[streamId], property, streamId); + } + + public static Type[] GetExprResultTypes(ExprEvaluator[] evaluators) { + var returnTypes = new Type[evaluators.Length]; + for (var i = 0; i < evaluators.Length; i++) { + returnTypes[i] = evaluators[i].ReturnType; + } + return returnTypes; + } + + public static Type[] GetExprResultTypes(IList expressions) { + var returnTypes = new Type[expressions.Count]; + for (var i = 0; i < expressions.Count; i++) { + returnTypes[i] = expressions[i].ExprEvaluator.ReturnType; + } + return returnTypes; + } + + public static ExprNodeUtilMethodDesc ResolveMethodAllowWildcardAndStream( + string className, + Type optionalClass, + string methodName, + IList parameters, + EngineImportService engineImportService, + EventAdapterService eventAdapterService, + int statementId, + bool allowWildcard, + EventType wildcardType, + ExprNodeUtilResolveExceptionHandler exceptionHandler, + string functionName, + TableService tableService, + string engineURI) + { + var paramTypes = new Type[parameters.Count]; + var childEvals = new ExprEvaluator[parameters.Count]; + var count = 0; + var allowEventBeanType = new bool[parameters.Count]; + var allowEventBeanCollType = new bool[parameters.Count]; + var childEvalsEventBeanReturnTypes = new ExprEvaluator[parameters.Count]; + var allConstants = true; + foreach (var childNode in parameters) { + if (!methodName.IsEnumerationMethod() && childNode is ExprLambdaGoesNode) { + throw new ExprValidationException("Unexpected lambda-expression encountered as parameter to UDF or static method '" + methodName + "'"); + } + if (childNode is ExprWildcard) { + if (wildcardType == null || !allowWildcard) { + throw new ExprValidationException("Failed to resolve wildcard parameter to a given event type"); + } + childEvals[count] = new ExprNodeUtilExprEvalStreamNumUnd(0, wildcardType.UnderlyingType); + childEvalsEventBeanReturnTypes[count] = new ExprNodeUtilExprEvalStreamNumEvent(0); + paramTypes[count] = wildcardType.UnderlyingType; + allowEventBeanType[count] = true; + allConstants = false; + count++; + continue; + } + if (childNode is ExprStreamUnderlyingNode) { + var und = (ExprStreamUnderlyingNode) childNode; + var tableMetadata = tableService.GetTableMetadataFromEventType(und.EventType); + if (tableMetadata == null) { + childEvals[count] = childNode.ExprEvaluator; + childEvalsEventBeanReturnTypes[count] = new ExprNodeUtilExprEvalStreamNumEvent(und.StreamId); + } else { + childEvals[count] = new BindProcessorEvaluatorStreamTable(und.StreamId, und.EventType.UnderlyingType, tableMetadata); + childEvalsEventBeanReturnTypes[count] = new ExprNodeUtilExprEvalStreamNumEventTable(und.StreamId, tableMetadata); + } + paramTypes[count] = childEvals[count].ReturnType; + allowEventBeanType[count] = true; + allConstants = false; + count++; + continue; + } + if (childNode is ExprEvaluatorEnumeration) { + var enumeration = (ExprEvaluatorEnumeration) childNode; + var eventType = enumeration.GetEventTypeSingle(eventAdapterService, statementId); + childEvals[count] = childNode.ExprEvaluator; + paramTypes[count] = childEvals[count].ReturnType; + allConstants = false; + if (eventType != null) { + childEvalsEventBeanReturnTypes[count] = new ExprNodeUtilExprEvalStreamNumEnumSingle(enumeration); + allowEventBeanType[count] = true; + count++; + continue; + } + var eventTypeColl = enumeration.GetEventTypeCollection(eventAdapterService, statementId); + if (eventTypeColl != null) { + childEvalsEventBeanReturnTypes[count] = new ExprNodeUtilExprEvalStreamNumEnumColl(enumeration); + allowEventBeanCollType[count] = true; + count++; + continue; + } + } + var eval = childNode.ExprEvaluator; + childEvals[count] = eval; + paramTypes[count] = eval.ReturnType; + count++; + if (!(childNode.IsConstantResult)) { + allConstants = false; + } + } + + // Try to resolve the method + FastMethod staticMethod; + MethodInfo method; + try { + if (optionalClass != null) { + method = engineImportService.ResolveMethod(optionalClass, methodName, paramTypes, allowEventBeanType, allowEventBeanCollType); + } else { + method = engineImportService.ResolveMethodOverloadChecked(className, methodName, paramTypes, allowEventBeanType, allowEventBeanCollType); + } + var declaringClass = FastClass.Create(method.DeclaringType); + staticMethod = declaringClass.GetMethod(method); + } catch (Exception e) { + throw exceptionHandler.Handle(e); + } + + // rewrite those evaluator that should return the event itself + var parameterTypes = method.IsExtensionMethod() + ? method.GetParameterTypes().Skip(1).ToArray() + : method.GetParameterTypes(); + + if (CollectionUtil.IsAnySet(allowEventBeanType)) { + for (var i = 0; i < parameters.Count; i++) { + if (allowEventBeanType[i] && parameterTypes[i] == typeof(EventBean)) { + childEvals[i] = childEvalsEventBeanReturnTypes[i]; + } + } + } + + // rewrite those evaluators that should return the event collection + if (CollectionUtil.IsAnySet(allowEventBeanCollType)) { + for (var i = 0; i < parameters.Count; i++) { + if (allowEventBeanCollType[i] && parameterTypes[i].IsGenericCollection()) { + childEvals[i] = childEvalsEventBeanReturnTypes[i]; + } + } + } + + // add an evaluator if the method expects a context object + if (!method.IsVarArgs() && parameterTypes.Length > 0 && + parameterTypes[parameterTypes.Length - 1] == typeof(EPLMethodInvocationContext)) + { + childEvals = (ExprEvaluator[]) CollectionUtil.ArrayExpandAddSingle( + childEvals, + new ExprNodeUtilExprEvalMethodContext(engineURI, functionName, eventAdapterService)); + } + + // handle varargs + if (method.IsVarArgs()) { + // handle context parameter + int numMethodParams = parameterTypes.Length; + if (numMethodParams > 1 && parameterTypes[numMethodParams - 2] == typeof(EPLMethodInvocationContext)) + { + var rewritten = new ExprEvaluator[childEvals.Length + 1]; + Array.Copy(childEvals, 0, rewritten, 0, numMethodParams - 2); + rewritten[numMethodParams - 2] = new ExprNodeUtilExprEvalMethodContext(engineURI, functionName, eventAdapterService); + Array.Copy(childEvals, numMethodParams - 2, rewritten, numMethodParams - 1, childEvals.Length - (numMethodParams - 2)); + childEvals = rewritten; + } + + childEvals = MakeVarargArrayEval(method, childEvals); + } + + return new ExprNodeUtilMethodDesc(allConstants, paramTypes, childEvals, method, staticMethod, null); + } + + public static void ValidatePlainExpression( + ExprNodeOrigin origin, + string expressionTextualName, + ExprNode expression) + { + var summaryVisitor = new ExprNodeSummaryVisitor(); + expression.Accept(summaryVisitor); + if (summaryVisitor.HasAggregation || summaryVisitor.HasSubselect || + summaryVisitor.HasStreamSelect || summaryVisitor.HasPreviousPrior) + { + throw new ExprValidationException( + string.Format( + "Invalid {0} expression '{1}': Aggregation, sub-select, previous or prior functions are not supported in this context", + origin.GetClauseName(), expressionTextualName)); + } + } + + public static ExprNode ValidateSimpleGetSubtree( + ExprNodeOrigin origin, + ExprNode expression, + StatementContext statementContext, + EventType optionalEventType, + bool allowBindingConsumption) + { + ValidatePlainExpression(origin, ToExpressionStringMinPrecedenceSafe(expression), expression); + + StreamTypeServiceImpl streamTypes; + if (optionalEventType != null) + { + streamTypes = new StreamTypeServiceImpl(optionalEventType, null, true, statementContext.EngineURI); + } + else + { + streamTypes = new StreamTypeServiceImpl(statementContext.EngineURI, false); + } + + var validationContext = new ExprValidationContext( + streamTypes, statementContext.EngineImportService, statementContext.StatementExtensionServicesContext, + null, statementContext.SchedulingService, statementContext.VariableService, + statementContext.TableService, new ExprEvaluatorContextStatement(statementContext, false), + statementContext.EventAdapterService, statementContext.StatementName, statementContext.StatementId, + statementContext.Annotations, statementContext.ContextDescriptor, statementContext.ScriptingService, + false, false, allowBindingConsumption, + false, null, false); + return GetValidatedSubtree(origin, expression, validationContext); + } + + public static ExprValidationContext GetExprValidationContextStatementOnly(StatementContext statementContext) + { + return new ExprValidationContext( + new StreamTypeServiceImpl(statementContext.EngineURI, false), statementContext.EngineImportService, + statementContext.StatementExtensionServicesContext, null, statementContext.SchedulingService, + statementContext.VariableService, statementContext.TableService, + new ExprEvaluatorContextStatement(statementContext, false), statementContext.EventAdapterService, + statementContext.StatementName, statementContext.StatementId, statementContext.Annotations, + statementContext.ContextDescriptor, statementContext.ScriptingService, false, false, false, false, null, false); + } + + public static ISet GetPropertyNamesIfAllProps(ExprNode[] expressions) + { + foreach (var expression in expressions) + { + if (!(expression is ExprIdentNode)) + { + return null; + } + } + var uniquePropertyNames = new HashSet(); + foreach (var expression in expressions) + { + var identNode = (ExprIdentNode) expression; + uniquePropertyNames.Add(identNode.UnresolvedPropertyName); + } + return uniquePropertyNames; + } + + public static string[] ToExpressionStringsMinPrecedence(ExprNode[] expressions) + { + var texts = new string[expressions.Length]; + for (var i = 0; i < expressions.Length; i++) + { + texts[i] = ToExpressionStringMinPrecedenceSafe(expressions[i]); + } + return texts; + } + + public static IList> FindExpression(ExprNode selectExpression, ExprNode searchExpression) { + var pairs = new List>(); + if (DeepEquals(selectExpression, searchExpression)) { + pairs.Add(new Pair(null, selectExpression)); + return pairs; + } + FindExpressionChildRecursive(selectExpression, searchExpression, pairs); + return pairs; + } + + private static void FindExpressionChildRecursive(ExprNode parent, ExprNode searchExpression, ICollection> pairs) { + foreach (var child in parent.ChildNodes) { + if (DeepEquals(child, searchExpression)) { + pairs.Add(new Pair(parent, child)); + continue; + } + FindExpressionChildRecursive(child, searchExpression, pairs); + } + } + + public static void ToExpressionStringParameterList(ExprNode[] childNodes, TextWriter buffer) + { + var delimiter = ""; + foreach (var childNode in childNodes) { + buffer.Write(delimiter); + buffer.Write(ToExpressionStringMinPrecedenceSafe(childNode)); + delimiter = ","; + } + } + + public static void ToExpressionStringWFunctionName(string functionName, IList childNodes, TextWriter writer) { + writer.Write(functionName); + writer.Write("("); + ToExpressionStringParameterList(childNodes, writer); + writer.Write(')'); + } + + public static string[] GetIdentResolvedPropertyNames(ExprNode[] nodes) { + var propertyNames = new string[nodes.Length]; + for (var i = 0; i < propertyNames.Length; i++) { + if (!(nodes[i] is ExprIdentNode)) { + throw new ArgumentException("Expressions are not ident nodes"); + } + propertyNames[i] = ((ExprIdentNode) nodes[i]).ResolvedPropertyName; + } + return propertyNames; + } + + public static Type[] GetExprResultTypes(ExprNode[] groupByNodes) { + var types = new Type[groupByNodes.Length]; + for (var i = 0; i < types.Length; i++) { + types[i] = groupByNodes[i].ExprEvaluator.ReturnType; + } + return types; + } + + public static ExprEvaluator MakeUnderlyingEvaluator(int streamNum, Type resultType, TableMetadata tableMetadata) { + if (tableMetadata != null) { + return new ExprNodeUtilUnderlyingEvaluatorTable(streamNum, resultType, tableMetadata); + } + return new ExprNodeUtilUnderlyingEvaluator(streamNum, resultType); + } + + public static bool HasStreamSelect(IList exprNodes) + { + var visitor = new ExprNodeStreamSelectVisitor(false); + foreach (var node in exprNodes) { + node.Accept(visitor); + if (visitor.HasStreamSelect()) { + return true; + } + } + return false; + } + + public static void ValidateNoSpecialsGroupByExpressions(IList groupByNodes) { + var visitorSubselects = new ExprNodeSubselectDeclaredDotVisitor(); + var visitorGrouping = new ExprNodeGroupingVisitorWParent(); + var aggNodesInGroupBy = new List(1); + + foreach (var groupByNode in groupByNodes) { + + // no subselects + groupByNode.Accept(visitorSubselects); + if (visitorSubselects.Subselects.Count > 0) { + throw new ExprValidationException("Subselects not allowed within group-by"); + } + + // no special grouping-clauses + groupByNode.Accept(visitorGrouping); + if (!visitorGrouping.GroupingIdNodes.IsEmpty()) { + throw ExprGroupingIdNode.MakeException("grouping_id"); + } + if (!visitorGrouping.GroupingNodes.IsEmpty()) { + throw ExprGroupingIdNode.MakeException("grouping"); + } + + // no aggregations allowed + ExprAggregateNodeUtil.GetAggregatesBottomUp(groupByNode, aggNodesInGroupBy); + if (!aggNodesInGroupBy.IsEmpty()) { + throw new ExprValidationException("Group-by expressions cannot contain aggregate functions"); + } + } + } + + public static IDictionary GetNamedExpressionsHandleDups(IList parameters) { + IDictionary nameds = null; + + foreach (var node in parameters) { + if (node is ExprNamedParameterNode) { + var named = (ExprNamedParameterNode) node; + if (nameds == null) { + nameds = new Dictionary(); + } + var lowerCaseName = named.ParameterName.ToLowerInvariant(); + if (nameds.ContainsKey(lowerCaseName)) { + throw new ExprValidationException("Duplicate parameter '" + lowerCaseName + "'"); + } + nameds.Put(lowerCaseName, named); + } + } + if (nameds == null) { + return Collections.GetEmptyMap(); + } + return nameds; + } + + public static void ValidateNamed(IDictionary namedExpressions, string[] namedParameters) { + foreach (var entry in namedExpressions) { + var found = false; + foreach (var named in namedParameters) { + if (named.Equals(entry.Key)) { + found = true; + break; + } + } + if (!found) { + throw new ExprValidationException("Unexpected named parameter '" + entry.Key + "', expecting any of the following: " + CollectionUtil.ToStringArray(namedParameters)); + } + } + } + + public static bool ValidateNamedExpectType(ExprNamedParameterNode namedParameterNode, Type[] expectedTypes) { + if (namedParameterNode.ChildNodes.Count != 1) { + throw GetNamedValidationException(namedParameterNode.ParameterName, expectedTypes); + } + + var childNode = namedParameterNode.ChildNodes[0]; + Type returnType = childNode.ExprEvaluator.ReturnType.GetBoxedType(); + + var found = false; + foreach (var expectedType in expectedTypes) { + if (expectedType == typeof(TimePeriod) && childNode is ExprTimePeriod) { + found = true; + break; + } + if (returnType == expectedType.GetBoxedType()) { + found = true; + break; + } + } + + if (found) { + return namedParameterNode.ChildNodes[0].IsConstantResult; + } + throw GetNamedValidationException(namedParameterNode.ParameterName, expectedTypes); + } + + private static ExprValidationException GetNamedValidationException(string parameterName, Type[] expected) { + string expectedType; + if (expected.Length == 1) { + expectedType = "a " + TypeHelper.GetSimpleNameForType(expected[0]) + "-typed value"; + } else { + var buf = new StringWriter(); + buf.Write("any of the following types: "); + var delimiter = ""; + foreach (var clazz in expected) { + buf.Write(delimiter); + buf.Write(TypeHelper.GetSimpleNameForType(clazz)); + delimiter = ","; + } + expectedType = buf.ToString(); + } + var message = "Failed to validate named parameter '" + parameterName + "', expected a single expression returning " + expectedType; + return new ExprValidationException(message); + } + + public static void AcceptChain(ExprNodeVisitor visitor, IList chainSpec) + { + foreach (var chain in chainSpec) { + foreach (var param in chain.Parameters) { + param.Accept(visitor); + } + } + } + + public static void AcceptChain(ExprNodeVisitorWithParent visitor, IList chainSpec) + { + foreach (var chain in chainSpec) { + foreach (var param in chain.Parameters) { + param.Accept(visitor); + } + } + } + + public static void AcceptChain(ExprNodeVisitorWithParent visitor, IList chainSpec, ExprNode parent) + { + foreach (var chain in chainSpec) { + foreach (var param in chain.Parameters) { + param.AcceptChildnodes(visitor, parent); + } + } + } + + public static void ReplaceChildNode(ExprNode parentNode, ExprNode nodeToReplace, ExprNode newNode) { + var index = FindChildNode(parentNode, nodeToReplace); + if (index == -1) { + parentNode.ReplaceUnlistedChildNode(nodeToReplace, newNode); + } else { + parentNode.SetChildNode(index, newNode); + } + } + + private static int FindChildNode(ExprNode parentNode, ExprNode childNode) { + for (var i = 0; i < parentNode.ChildNodes.Count; i++) { + if (parentNode.ChildNodes[i] == childNode) { + return i; + } + } + return -1; + } + + public static void ReplaceChainChildNode(ExprNode nodeToReplace, ExprNode newNode, IList chainSpec) + { + foreach (var chained in chainSpec) { + var index = chained.Parameters.IndexOf(nodeToReplace); + if (index != -1) + { + chained.Parameters[index] = newNode; + } + } + } + + public static ExprNodePropOrStreamSet GetNonAggregatedProps(EventType[] types, IList exprNodes, ContextPropertyRegistry contextPropertyRegistry) + { + // Determine all event properties in the clause + var nonAggProps = new ExprNodePropOrStreamSet(); + var visitor = new ExprNodeIdentifierAndStreamRefVisitor(false); + foreach (var node in exprNodes) { + visitor.Reset(); + node.Accept(visitor); + AddNonAggregatedProps(nonAggProps, visitor.GetRefs(), types, contextPropertyRegistry); + } + + return nonAggProps; + } + + private static void AddNonAggregatedProps( + ExprNodePropOrStreamSet nonAggProps, + IEnumerable refs, + EventType[] types, + ContextPropertyRegistry contextPropertyRegistry) + { + foreach (var pair in refs) + { + if (pair is ExprNodePropOrStreamPropDesc) + { + var propDesc = (ExprNodePropOrStreamPropDesc) pair; + var originType = types.Length > pair.StreamNum ? types[pair.StreamNum] : null; + if (originType == null || contextPropertyRegistry == null || + !contextPropertyRegistry.IsPartitionProperty(originType, propDesc.PropertyName)) + { + nonAggProps.Add(pair); + } + } + else + { + nonAggProps.Add(pair); + } + } + } + + public static void AddNonAggregatedProps( + ExprNode exprNode, + ExprNodePropOrStreamSet set, + EventType[] types, + ContextPropertyRegistry contextPropertyRegistry) + { + var visitor = new ExprNodeIdentifierAndStreamRefVisitor(false); + exprNode.Accept(visitor); + AddNonAggregatedProps(set, visitor.GetRefs(), types, contextPropertyRegistry); + } + + public static ExprNodePropOrStreamSet GetAggregatedProperties(IList aggregateNodes) + { + // Get a list of properties being aggregated in the clause. + var propertiesAggregated = new ExprNodePropOrStreamSet(); + var visitor = new ExprNodeIdentifierAndStreamRefVisitor(true); + foreach (ExprNode selectAggExprNode in aggregateNodes) + { + visitor.Reset(); + selectAggExprNode.Accept(visitor); + IList properties = visitor.GetRefs(); + propertiesAggregated.AddAll(properties); + } + + return propertiesAggregated; + } + + public static ExprEvaluator[] GetEvaluators(ExprNode[] exprNodes) + { + if (exprNodes == null) + { + return null; + } + var eval = new ExprEvaluator[exprNodes.Length]; + for (var i = 0; i < exprNodes.Length; i++) + { + var node = exprNodes[i]; + if (node != null) + { + eval[i] = node.ExprEvaluator; + } + } + return eval; + } + + public static ExprEvaluator[] GetEvaluators(IList childNodes) + { + var eval = new ExprEvaluator[childNodes.Count]; + for (var i = 0; i < childNodes.Count; i++) + { + eval[i] = childNodes[i].ExprEvaluator; + } + return eval; + } + + public static ISet GetIdentStreamNumbers(ExprNode child) + { + var streams = new HashSet(); + var visitor = new ExprNodeIdentifierCollectVisitor(); + child.Accept(visitor); + foreach (var node in visitor.ExprProperties) { + streams.Add(node.StreamId); + } + return streams; + } + + /// + /// Returns true if all properties within the expression are witin data window'd streams. + /// + /// expression to interrogate + /// streams + /// indicator unidirection join + /// indicator + public static bool HasRemoveStreamForAggregations(ExprNode child, StreamTypeService streamTypeService, bool unidirectionalJoin) + { + // Determine whether all streams are istream-only or irstream + bool[] isIStreamOnly = streamTypeService.IsIStreamOnly; + var isAllIStream = true; // all true? + var isAllIRStream = true; // all false? + foreach (var anIsIStreamOnly in isIStreamOnly) { + if (!anIsIStreamOnly) { + isAllIStream = false; + } else { + isAllIRStream = false; + } + } + + // determine if a data-window applies to this max function + var hasDataWindows = true; + if (isAllIStream) { + hasDataWindows = false; + } else if (!isAllIRStream) { + if (streamTypeService.EventTypes.Length > 1) { + if (unidirectionalJoin) { + return false; + } + // In a join we assume that a data window is present or implicit via unidirectional + } else { + hasDataWindows = false; + // get all aggregated properties to determine if any is from a windowed stream + var visitor = new ExprNodeIdentifierCollectVisitor(); + child.Accept(visitor); + foreach (var node in visitor.ExprProperties) { + if (!isIStreamOnly[node.StreamId]) { + hasDataWindows = true; + break; + } + } + } + } + + return hasDataWindows; + } + + /// + /// Apply a filter expression. + /// + /// expression + /// the event that represents stream zero + /// all events thate are stream one events + /// context for expression evaluation + /// filtered stream one events + public static EventBean[] ApplyFilterExpression( + ExprEvaluator filter, + EventBean streamZeroEvent, + EventBean[] streamOneEvents, + ExprEvaluatorContext exprEvaluatorContext) + { + var eventsPerStream = new EventBean[2]; + eventsPerStream[0] = streamZeroEvent; + + var filtered = new EventBean[streamOneEvents.Length]; + var countPass = 0; + + var evaluateParams = new EvaluateParams(eventsPerStream, true, exprEvaluatorContext); + foreach (var eventBean in streamOneEvents) + { + eventsPerStream[1] = eventBean; + + var result = filter.Evaluate(evaluateParams); + if ((result != null) && true.Equals(result)) { + filtered[countPass] = eventBean; + countPass++; + } + } + + if (countPass == streamOneEvents.Length) { + return streamOneEvents; + } + return EventBeanUtility.ResizeArray(filtered, countPass); + } + + /// + /// Apply a filter expression returning a pass indicator. + /// + /// to apply + /// events per stream + /// context for expression evaluation + /// pass indicator + public static bool ApplyFilterExpression(ExprEvaluator filter, EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext) + { + var result = filter.Evaluate(new EvaluateParams(eventsPerStream, true, exprEvaluatorContext)); + return true.Equals(result); + } + + /// + /// Compare two expression nodes and their children in exact child-node sequence, + /// returning true if the 2 expression nodes trees are equals, or false if they are not equals. + /// + /// Recursive call since it uses this method to compare child nodes in the same exact sequence. + /// Nodes are compared using the equalsNode method. + /// + /// + /// - first expression top node of the tree to compare + /// - second expression top node of the tree to compare + /// + /// false if this or all child nodes are not equal, true if equal + /// + public static bool DeepEquals(ExprNode nodeOne, ExprNode nodeTwo) { + if (nodeOne.ChildNodes.Count != nodeTwo.ChildNodes.Count) + { + return false; + } + if (!nodeOne.EqualsNode(nodeTwo)) { + return false; + } + for (var i = 0; i < nodeOne.ChildNodes.Count; i++) + { + var childNodeOne = nodeOne.ChildNodes[i]; + var childNodeTwo = nodeTwo.ChildNodes[i]; + + if (!DeepEquals(childNodeOne, childNodeTwo)) { + return false; + } + } + return true; + } + + /// + /// Compares two expression nodes via deep comparison, considering all + /// child nodes of either side. + /// + /// array of expressions + /// array of expressions + /// true if the expressions are equal, false if not + public static bool DeepEquals(ExprNode[] one, ExprNode[] two) { + if (one.Length != two.Length) { + return false; + } + for (var i = 0; i < one.Length; i++) { + if (!DeepEquals(one[i], two[i])) { + return false; + } + } + return true; + } + + public static bool DeepEquals(IList one, IList two) + { + if (one.Count != two.Count) { + return false; + } + for (var i = 0; i < one.Count; i++) { + if (!DeepEquals(one[i], two[i])) { + return false; + } + } + return true; + } + + /// + /// Check if the expression is minimal: does not have a subselect, aggregation and does not need view resources + /// + /// to inspect + /// null if minimal, otherwise name of offending sub-expression + public static string IsMinimalExpression(ExprNode expression) { + var subselectVisitor = new ExprNodeSubselectDeclaredDotVisitor(); + expression.Accept(subselectVisitor); + if (subselectVisitor.Subselects.Count > 0) { + return "a subselect"; + } + + var viewResourceVisitor = new ExprNodeViewResourceVisitor(); + expression.Accept(viewResourceVisitor); + if (viewResourceVisitor.ExprNodes.Count > 0) { + return "a function that requires view resources (prior, prev)"; + } + + var aggregateNodes = new List(); + ExprAggregateNodeUtil.GetAggregatesBottomUp(expression, aggregateNodes); + if (!aggregateNodes.IsEmpty()) { + return "an aggregation function"; + } + return null; + } + + public static void ToExpressionString(IList chainSpec, TextWriter buffer, bool prefixDot, string functionName) { + var delimiterOuter = ""; + if (prefixDot) { + delimiterOuter = "."; + } + var isFirst = true; + foreach (var element in chainSpec) { + buffer.Write(delimiterOuter); + if (functionName != null) { + buffer.Write(functionName); + } else { + buffer.Write(element.Name); + } + + // the first item without dot-prefix and empty parameters should not be appended with parenthesis + if (!isFirst || prefixDot || !element.Parameters.IsEmpty()) { + ToExpressionStringIncludeParen(element.Parameters, buffer); + } + + delimiterOuter = "."; + isFirst = false; + } + } + + public static void ToExpressionStringParameterList(IList parameters, TextWriter buffer) + { + var delimiter = ""; + foreach (var param in parameters) { + buffer.Write(delimiter); + delimiter = ","; + buffer.Write(ToExpressionStringMinPrecedenceSafe(param)); + } + } + + public static void ToExpressionStringIncludeParen(IList parameters, TextWriter buffer) { + buffer.Write("("); + ToExpressionStringParameterList(parameters, buffer); + buffer.Write(")"); + } + + public static void Validate(ExprNodeOrigin origin, IList chainSpec, ExprValidationContext validationContext) + { + + // validate all parameters + foreach (var chainElement in chainSpec) { + var validated = new List(); + foreach (var expr in chainElement.Parameters) { + validated.Add(GetValidatedSubtree(origin, expr, validationContext)); + if (expr is ExprNamedParameterNode) { + throw new ExprValidationException("Named parameters are not allowed"); + } + } + chainElement.Parameters = validated; + } + } + + public static IList CollectChainParameters(IList chainSpec) + { + var result = new List(); + foreach (var chainElement in chainSpec) { + result.AddAll(chainElement.Parameters); + } + return result; + } + + public static void ToExpressionStringParams(TextWriter writer, IList @params) { + writer.Write('('); + var delimiter = ""; + foreach (var childNode in @params) { + writer.Write(delimiter); + delimiter = ","; + writer.Write(ToExpressionStringMinPrecedenceSafe(childNode)); + } + writer.Write(')'); + } + + public static string PrintEvaluators(ExprEvaluator[] evaluators) { + var writer = new StringWriter(); + var delimiter = ""; + foreach (var evaluator in evaluators) { + writer.Write(delimiter); + writer.Write(evaluator.GetType().Name); + delimiter = ", "; + } + return writer.ToString(); + } + + public static ExprEvaluator[] CrontabScheduleValidate( + ExprNodeOrigin origin, + IList scheduleSpecExpressionList, + StatementContext context, + bool allowBindingConsumption) + { + + // Validate the expressions + var expressions = new ExprEvaluator[scheduleSpecExpressionList.Count]; + var count = 0; + var evaluatorContextStmt = new ExprEvaluatorContextStatement(context, false); + foreach (var parameters in scheduleSpecExpressionList) + { + var validationContext = new ExprValidationContext( + new StreamTypeServiceImpl(context.EngineURI, false), context.EngineImportService, + context.StatementExtensionServicesContext, null, context.SchedulingService, context.VariableService, + context.TableService, evaluatorContextStmt, context.EventAdapterService, context.StatementName, + context.StatementId, context.Annotations, context.ContextDescriptor, context.ScriptingService, + false, false, allowBindingConsumption, false, null, false); + var node = GetValidatedSubtree(origin, parameters, validationContext); + expressions[count++] = node.ExprEvaluator; + } + + if (expressions.Length <= 4 || expressions.Length >= 8) { + throw new ExprValidationException("Invalid schedule specification: " + ScheduleSpecUtil.GetExpressionCountException(expressions.Length)); + } + + return expressions; + } + + public static ScheduleSpec CrontabScheduleBuild(ExprEvaluator[] scheduleSpecEvaluators, ExprEvaluatorContext context) { + + // Build a schedule + try { + var scheduleSpecParameterList = EvaluateExpressions(scheduleSpecEvaluators, context); + return ScheduleSpecUtil.ComputeValues(scheduleSpecParameterList); + } catch (ScheduleParameterException e) { + throw new EPException("Invalid schedule specification: " + e.Message, e); + } + } + + public static Object[] EvaluateExpressions(ExprEvaluator[] parameters, ExprEvaluatorContext exprEvaluatorContext) { + var evaluateParams = new EvaluateParams(null, true, exprEvaluatorContext); + var results = new Object[parameters.Length]; + var count = 0; + foreach (var expr in parameters) { + try { + results[count] = expr.Evaluate(evaluateParams); + count++; + } catch (Exception ex) { + var message = "Failed expression evaluation in crontab timer-at for parameter " + count + ": " + ex.Message; + Log.Error(message, ex); + throw new ArgumentException(message); + } + } + return results; + } + + public static ExprNode[] ToArray(ICollection expressions) { + if (expressions.IsEmpty()) { + return EMPTY_EXPR_ARRAY; + } + return expressions.ToArray(); + } + + public static ExprDeclaredNode[] ToArray(IList declaredNodes) + { + if (declaredNodes.IsEmpty()) { + return EMPTY_DECLARED_ARR; + } + return declaredNodes.ToArray(); + } + + public static ExprNodePropOrStreamSet GetGroupByPropertiesValidateHasOne(ExprNode[] groupByNodes) + { + // Get the set of properties refered to by all group-by expression nodes. + var propertiesGroupBy = new ExprNodePropOrStreamSet(); + var visitor = new ExprNodeIdentifierAndStreamRefVisitor(true); + + foreach (var groupByNode in groupByNodes) { + visitor.Reset(); + groupByNode.Accept(visitor); + var propertiesNode = visitor.GetRefs(); + propertiesGroupBy.AddAll(propertiesNode); + + // For each group-by expression node, require at least one property. + if (propertiesNode.IsEmpty()) { + throw new ExprValidationException("Group-by expressions must refer to property names"); + } + } + + return propertiesGroupBy; + } + + private static ExprEvaluator[] MakeVarargArrayEval(MethodInfo method, ExprEvaluator[] childEvals) { + var parameterTypes = method.GetParameterTypes(); + var evals = new ExprEvaluator[parameterTypes.Length]; + var varargClass = parameterTypes[parameterTypes.Length - 1].GetElementType(); + var varargClassBoxed = varargClass.GetBoxedType(); + if (parameterTypes.Length > 1) + { + Array.Copy(childEvals, 0, evals, 0, evals.Length - 1); + } + var varargArrayLength = childEvals.Length - parameterTypes.Length + 1; + + // handle passing array along + if (varargArrayLength == 1) { + var last = childEvals[parameterTypes.Length - 1]; + Type lastReturns = last.ReturnType; + if (lastReturns != null && lastReturns.IsArray) { + evals[parameterTypes.Length - 1] = last; + return evals; + } + } + + // handle parameter conversion to vararg parameter + var varargEvals = new ExprEvaluator[varargArrayLength]; + var coercers = new Coercer[varargEvals.Length]; + var needCoercion = false; + for (var i = 0; i < varargArrayLength; i++) { + var childEvalIndex = i + parameterTypes.Length - 1; + Type resultType = childEvals[childEvalIndex].ReturnType; + varargEvals[i] = childEvals[childEvalIndex]; + + if (TypeHelper.IsSubclassOrImplementsInterface(resultType, varargClass)) { + // no need to coerce + continue; + } + + if (resultType.GetBoxedType() != varargClassBoxed) { + needCoercion = true; + coercers[i] = CoercerFactory.GetCoercer(resultType, varargClassBoxed); + } + } + + ExprEvaluator varargEval; + if (!needCoercion) { + varargEval = new VarargOnlyArrayEvalNoCoerce(varargEvals, varargClass); + } else { + varargEval = new VarargOnlyArrayEvalWithCoerce(varargEvals, varargClass, coercers); + } + evals[parameterTypes.Length - 1] = varargEval; + return evals; + } + + /// + /// Encapsulates the parse result parsing a mapped property as a class and method name with args. + /// + public class MappedPropertyParseResult + { + /// + /// Returns the parse result of the mapped property. + /// + /// is the class name, or null if there isn't one + /// is the method name + /// is the argument + public MappedPropertyParseResult(string className, string methodName, string argString) { + ClassName = className; + MethodName = methodName; + ArgString = argString; + } + + /// + /// Returns class name. + /// + /// name of class + public string ClassName { get; private set; } + + /// + /// Returns the method name. + /// + /// method name + public string MethodName { get; private set; } + + /// + /// Returns the method argument. + /// + /// arg + public string ArgString { get; private set; } + } + + private class VarargOnlyArrayEvalNoCoerce : ExprEvaluator + { + private readonly ExprEvaluator[] _evals; + private readonly Type _varargClass; + + public VarargOnlyArrayEvalNoCoerce(ExprEvaluator[] evals, Type varargClass) + { + _evals = evals; + _varargClass = varargClass; + } + + public object Evaluate(EvaluateParams evaluateParams) + { + var array = Array.CreateInstance(_varargClass, _evals.Length); + for (var i = 0; i < _evals.Length; i++) + { + var value = _evals[i].Evaluate(evaluateParams); + array.SetValue(value, i); + } + return array; + } + + public Type ReturnType + { + get { return TypeHelper.GetArrayType(_varargClass); } + } + } + + private class VarargOnlyArrayEvalWithCoerce : ExprEvaluator + { + private readonly ExprEvaluator[] _evals; + private readonly Type _varargClass; + private readonly Coercer[] _coercers; + + public VarargOnlyArrayEvalWithCoerce(ExprEvaluator[] evals, Type varargClass, Coercer[] coercers) + { + _evals = evals; + _varargClass = varargClass; + _coercers = coercers; + } + + public object Evaluate(EvaluateParams evaluateParams) + { + var array = Array.CreateInstance(_varargClass, _evals.Length); + for (var i = 0; i < _evals.Length; i++) + { + var value = _evals[i].Evaluate(evaluateParams); + if (_coercers[i] != null) + { + value = _coercers[i].Invoke(value); + } + array.SetValue(value, i); + } + return array; + } + + public Type ReturnType + { + get { return TypeHelper.GetArrayType(_varargClass); } + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodeValidated.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodeValidated.cs new file mode 100755 index 000000000..2486afd50 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprNodeValidated.cs @@ -0,0 +1,91 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +using com.espertech.esper.epl.expression.visitor; + +namespace com.espertech.esper.epl.expression.core +{ + /// + /// A placeholder for another expression node that has been validated already. + /// + [Serializable] + public class ExprNodeValidated + : ExprNodeBase + , ExprEvaluator + { + private readonly ExprNode _inner; + + /// Ctor. + /// nested expression node + public ExprNodeValidated(ExprNode inner) + { + _inner = inner; + } + + public override ExprEvaluator ExprEvaluator + { + get { return this; } + } + + public override ExprPrecedenceEnum Precedence + { + get { return _inner.Precedence; } + } + + public override void ToEPL(TextWriter writer, ExprPrecedenceEnum parentPrecedence) + { + _inner.ToEPL(writer, parentPrecedence); + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + _inner.ToEPL(writer, ExprPrecedenceEnum.MINIMUM); + } + + public override bool IsConstantResult + { + get { return _inner.IsConstantResult; } + } + + public override bool EqualsNode(ExprNode node) + { + if (node is ExprNodeValidated) + { + return _inner.EqualsNode(((ExprNodeValidated) node)._inner); + } + return _inner.EqualsNode(node); + } + + public override ExprNode Validate(ExprValidationContext validationContext) + { + return null; + } + + public override void Accept(ExprNodeVisitor visitor) + { + if (visitor.IsVisit(this)) + { + visitor.Visit(this); + _inner.Accept(visitor); + } + } + + public Type ReturnType + { + get { return _inner.ExprEvaluator.ReturnType; } + } + + public object Evaluate(EvaluateParams evaluateParams) + { + return _inner.ExprEvaluator.Evaluate(evaluateParams); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprNumberSetCronParam.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprNumberSetCronParam.cs new file mode 100755 index 000000000..99f469f1f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprNumberSetCronParam.cs @@ -0,0 +1,132 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; +using System.Reflection; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.logging; +using com.espertech.esper.type; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.core +{ + /// + /// Expression for a parameter within a crontab. + /// + /// May have one subnode depending on the cron parameter type. + /// + [Serializable] + public class ExprNumberSetCronParam + : ExprNodeBase + , ExprEvaluator + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly CronOperatorEnum _cronOperator; + [NonSerialized] + private ExprEvaluator _evaluator; + + /// Ctor. + /// type of cron parameter + public ExprNumberSetCronParam(CronOperatorEnum cronOperator) + { + _cronOperator = cronOperator; + } + + public override ExprEvaluator ExprEvaluator + { + get { return this; } + } + + /// Returns the cron parameter type. + /// type of cron parameter + public CronOperatorEnum CronOperator + { + get { return _cronOperator; } + } + + public override bool IsConstantResult + { + get + { + if (ChildNodes.Count == 0) + { + return true; + } + return ChildNodes[0].IsConstantResult; + } + } + + public Type ReturnType + { + get { return typeof(CronParameter); } + } + + public object Evaluate(EvaluateParams evaluateParams) + { + if (ChildNodes.Count == 0) + { + return new CronParameter(_cronOperator, null); + } + object value = _evaluator.Evaluate(evaluateParams); + if (value == null) + { + Log.Warn("Null value returned for cron parameter"); + return new CronParameter(_cronOperator, null); + } + else + { + int intValue = value.AsInt(); + return new CronParameter(_cronOperator, intValue); + } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + if (ChildNodes.Count != 0) + { + ChildNodes[0].ToEPL(writer, Precedence); + writer.Write(" "); + } + writer.Write(_cronOperator.GetSyntax()); + } + + public override ExprPrecedenceEnum Precedence + { + get { return ExprPrecedenceEnum.UNARY; } + } + + public override bool EqualsNode(ExprNode node) + { + if (!(node is ExprNumberSetCronParam)) + { + return false; + } + var other = (ExprNumberSetCronParam)node; + return other._cronOperator.Equals(_cronOperator); + } + + public override ExprNode Validate(ExprValidationContext validationContext) + { + if (ChildNodes.Count == 0) + { + return null; + } + _evaluator = ChildNodes[0].ExprEvaluator; + Type type = _evaluator.ReturnType; + if (!(type.IsNumericNonFP())) + { + throw new ExprValidationException("Frequency operator requires an integer-type parameter"); + } + + return null; + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprNumberSetFrequency.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprNumberSetFrequency.cs new file mode 100755 index 000000000..1e2bfbd74 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprNumberSetFrequency.cs @@ -0,0 +1,86 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.logging; +using com.espertech.esper.type; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.core +{ + /// Expression for use within crontab to specify a frequency. + [Serializable] + public class ExprNumberSetFrequency + : ExprNodeBase + , ExprEvaluator + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + [NonSerialized] private ExprEvaluator _evaluator; + + public override ExprEvaluator ExprEvaluator + { + get { return this; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + writer.Write("*/"); + this.ChildNodes[0].ToEPL(writer, ExprPrecedenceEnum.MINIMUM); + } + + public override ExprPrecedenceEnum Precedence + { + get { return ExprPrecedenceEnum.MINIMUM; } + } + + public override bool IsConstantResult + { + get { return this.ChildNodes[0].IsConstantResult; } + } + + public override bool EqualsNode(ExprNode node) + { + return node is ExprNumberSetFrequency; + } + + public override ExprNode Validate(ExprValidationContext validationContext) + { + _evaluator = ChildNodes[0].ExprEvaluator; + var type = _evaluator.ReturnType; + if (!type.IsNumericNonFP()) + { + throw new ExprValidationException("Frequency operator requires an integer-type parameter"); + } + return null; + } + + public Type ReturnType + { + get { return typeof (FrequencyParameter); } + } + + public Object Evaluate(EvaluateParams evaluateParams) + { + var value = _evaluator.Evaluate(evaluateParams); + if (value == null) + { + Log.Warn("Null value returned for frequency parameter"); + return new FrequencyParameter(Int32.MaxValue); + } + else + { + var intValue = value.AsInt(); + return new FrequencyParameter(intValue); + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprNumberSetList.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprNumberSetList.cs new file mode 100755 index 000000000..10210fa62 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprNumberSetList.cs @@ -0,0 +1,123 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.type; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.core +{ + /// + /// Expression for use within crontab to specify a list of values. + /// + [Serializable] + public class ExprNumberSetList + : ExprNodeBase + , ExprEvaluator + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + [NonSerialized] + private ExprEvaluator[] _evaluators; + + public override ExprEvaluator ExprEvaluator + { + get { return this; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + String delimiter = ""; + + writer.Write('['); + for (int ii = 0; ii < ChildNodes.Count; ii++) + { + ExprNode expr = ChildNodes[ii]; + writer.Write(delimiter); + expr.ToEPL(writer, ExprPrecedenceEnum.MINIMUM); + delimiter = ","; + } + writer.Write(']'); + } + + public override ExprPrecedenceEnum Precedence + { + get { return ExprPrecedenceEnum.UNARY; } + } + + public override bool IsConstantResult + { + get { return ChildNodes.All(child => child.IsConstantResult); } + } + + public override bool EqualsNode(ExprNode node) + { + return (node is ExprNumberSetList); + } + + public override ExprNode Validate(ExprValidationContext validationContext) + { + // all nodes must either be int, frequency or range + _evaluators = ExprNodeUtility.GetEvaluators(ChildNodes); + foreach (ExprEvaluator child in _evaluators) + { + Type type = child.ReturnType; + if ((type == typeof(FrequencyParameter)) || (type == typeof(RangeParameter))) + { + continue; + } + if (!(type.IsNumericNonFP())) + { + throw new ExprValidationException("Frequency operator requires an integer-type parameter"); + } + } + + return null; + } + + public Type ReturnType + { + get { return typeof(ListParameter); } + } + + public object Evaluate(EvaluateParams evaluateParams) + { + IList parameters = new List(); + foreach (ExprEvaluator child in _evaluators) + { + Object value = child.Evaluate(evaluateParams); + if (value == null) + { + Log.Info("Null value returned for lower bounds value in list parameter, skipping parameter"); + continue; + } + if ((value is FrequencyParameter) || (value is RangeParameter)) + { + parameters.Add((NumberSetParameter)value); + continue; + } + + int intValue = value.AsInt(); + parameters.Add(new IntParameter(intValue)); + } + if (parameters.IsEmpty()) + { + Log.Warn("EmptyFalse list of values in list parameter, using upper bounds"); + parameters.Add(new IntParameter(int.MaxValue)); + } + return new ListParameter(parameters); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprNumberSetRange.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprNumberSetRange.cs new file mode 100755 index 000000000..debccf42a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprNumberSetRange.cs @@ -0,0 +1,98 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.logging; +using com.espertech.esper.type; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.core +{ + /// + /// Expression for use within crontab to specify a range. + /// + /// Differs from the between-expression since the value returned by evaluating is a cron-value object. + /// + [Serializable] + public class ExprNumberSetRange + : ExprNodeBase + , ExprEvaluator + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + [NonSerialized] + private ExprEvaluator[] _evaluators; + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + ChildNodes[0].ToEPL(writer, ExprPrecedenceEnum.MINIMUM); + writer.Write(":"); + ChildNodes[1].ToEPL(writer, ExprPrecedenceEnum.MINIMUM); + } + + public override ExprPrecedenceEnum Precedence + { + get { return ExprPrecedenceEnum.UNARY; } + } + + public override ExprEvaluator ExprEvaluator + { + get { return this; } + } + + public override bool IsConstantResult + { + get { return ChildNodes[0].IsConstantResult && ChildNodes[1].IsConstantResult; } + } + + public override bool EqualsNode(ExprNode node) + { + return node is ExprNumberSetRange; + } + + public override ExprNode Validate(ExprValidationContext validationContext) + { + _evaluators = ExprNodeUtility.GetEvaluators(ChildNodes); + Type typeOne = _evaluators[0].ReturnType; + Type typeTwo = _evaluators[1].ReturnType; + if ((!(typeOne.IsNumericNonFP())) || (!(typeTwo.IsNumericNonFP()))) + { + throw new ExprValidationException("Range operator requires integer-type parameters"); + } + + return null; + } + + public Type ReturnType + { + get { return typeof(RangeParameter); } + } + + public object Evaluate(EvaluateParams evaluateParams) + { + Object valueLower = _evaluators[0].Evaluate(evaluateParams); + Object valueUpper = _evaluators[1].Evaluate(evaluateParams); + if (valueLower == null) + { + Log.Warn("Null value returned for lower bounds value in range parameter, using zero as lower bounds"); + valueLower = 0; + } + if (valueUpper == null) + { + Log.Warn("Null value returned for upper bounds value in range parameter, using max as upper bounds"); + valueUpper = int.MaxValue; + } + int intValueLower = valueLower.AsInt(); + int intValueUpper = valueUpper.AsInt(); + return new RangeParameter(intValueLower, intValueUpper); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprOrderedExpr.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprOrderedExpr.cs new file mode 100755 index 000000000..3f30aae57 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprOrderedExpr.cs @@ -0,0 +1,90 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.epl.expression.core +{ + /// + /// A placeholder expression for view/pattern object parameters that allow sorting expression values ascending or descending. + /// + [Serializable] + public class ExprOrderedExpr + : ExprNodeBase + , ExprEvaluator + { + private readonly bool _isDescending; + [NonSerialized] private ExprEvaluator _evaluator; + + /// Ctor. + /// is true for descending sorts + public ExprOrderedExpr(bool descending) + { + _isDescending = descending; + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + ChildNodes[0].ToEPL(writer, ExprPrecedenceEnum.MINIMUM); + if (_isDescending) + { + writer.Write(" desc"); + } + } + + public override ExprPrecedenceEnum Precedence + { + get { return ExprPrecedenceEnum.UNARY; } + } + + public override ExprEvaluator ExprEvaluator + { + get { return this; } + } + + public override bool IsConstantResult + { + get { return ChildNodes[0].IsConstantResult; } + } + + public override bool EqualsNode(ExprNode node) + { + var other = node as ExprOrderedExpr; + if (other == null) + { + return false; + } + + return other._isDescending == _isDescending; + } + + public override ExprNode Validate(ExprValidationContext validationContext) + { + _evaluator = ChildNodes[0].ExprEvaluator; // always valid + return null; + } + + public Type ReturnType + { + get { return ChildNodes[0].ExprEvaluator.ReturnType; } + } + + public object Evaluate(EvaluateParams evaluateParams) + { + return _evaluator.Evaluate(evaluateParams); + } + + /// Returns true for descending sort. + /// indicator for ascending or descending sort + public bool IsDescending + { + get { return _isDescending; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprPrecedenceEnum.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprPrecedenceEnum.cs new file mode 100755 index 000000000..52829af02 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprPrecedenceEnum.cs @@ -0,0 +1,77 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.expression.core +{ + /// Precendence levels for expressions. + public enum ExprPrecedenceEnum + { + /// Precedence. + UNARY, + /// Precedence. + MULTIPLY, + /// Precedence. + ADDITIVE, + /// Precedence. + CONCAT, + /// Precedence. + RELATIONAL_BETWEEN_IN, + /// Precedence. + EQUALS, + /// Precedence. + NEGATED, + /// Precedence. + BITWISE, + /// Precedence. + AND, + /// Precedence. + OR, + /// Precedence. + CASE, + /// Precedence. + MINIMUM + } + + public static class ExprPrecedenceEnumExtensions + { + public static int GetLevel(this ExprPrecedenceEnum value) + { + switch (value) + { + case ExprPrecedenceEnum.UNARY: + return (11); + case ExprPrecedenceEnum.MULTIPLY: + return (10); + case ExprPrecedenceEnum.ADDITIVE: + return (9); + case ExprPrecedenceEnum.CONCAT: + return (8); + case ExprPrecedenceEnum.RELATIONAL_BETWEEN_IN: + return (7); + case ExprPrecedenceEnum.EQUALS: + return (6); + case ExprPrecedenceEnum.NEGATED: + return (5); + case ExprPrecedenceEnum.BITWISE: + return (4); + case ExprPrecedenceEnum.AND: + return (3); + case ExprPrecedenceEnum.OR: + return (2); + case ExprPrecedenceEnum.CASE: + return (1); + case ExprPrecedenceEnum.MINIMUM: + return (Int32.MinValue); + } + + throw new ArgumentException(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprStreamRefNode.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprStreamRefNode.cs new file mode 100755 index 000000000..7293d125e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprStreamRefNode.cs @@ -0,0 +1,19 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.expression.core +{ + /// + /// Represents a stream-reference. + /// + public interface ExprStreamRefNode : ExprNode + { + int? StreamReferencedIfAny { get; } + string RootPropertyNameIfAny { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprStreamUnderlyingNode.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprStreamUnderlyingNode.cs new file mode 100755 index 000000000..d343c3ec9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprStreamUnderlyingNode.cs @@ -0,0 +1,21 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; + +namespace com.espertech.esper.epl.expression.core +{ + /// + /// Represents an stream selector that returns the streams underlying event, or null if undefined. + /// + public interface ExprStreamUnderlyingNode : ExprNode, ExprStreamRefNode + { + int StreamId { get; } + EventType EventType { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprStreamUnderlyingNodeImpl.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprStreamUnderlyingNodeImpl.cs new file mode 100755 index 000000000..de2c59cc1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprStreamUnderlyingNodeImpl.cs @@ -0,0 +1,180 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.expression.core +{ + /// + /// Represents an stream selector that returns the streams underlying event, or null if undefined. + /// + [Serializable] + public class ExprStreamUnderlyingNodeImpl + : ExprNodeBase + , ExprEvaluator + , ExprStreamUnderlyingNode + { + private readonly bool _isWildcard; + private int _streamNum = -1; + private Type _type; + + [NonSerialized] + private EventType _eventType; + + /// + /// Ctor. + /// + /// is the name of the stream for which to return the underlying event + public ExprStreamUnderlyingNodeImpl(string streamName, bool isWildcard) + { + if ((streamName == null) && (!isWildcard)) + { + throw new ArgumentException("Stream name is null"); + } + StreamName = streamName; + _isWildcard = isWildcard; + } + + public override ExprEvaluator ExprEvaluator + { + get { return this; } + } + + /// + /// Returns the stream name. + /// + /// stream name + public string StreamName { get; private set; } + + public int? StreamReferencedIfAny + { + get { return StreamId; } + } + + public string RootPropertyNameIfAny + { + get { return null; } + } + + public override ExprNode Validate(ExprValidationContext validationContext) + { + if (StreamName == null && _isWildcard) + { + if (validationContext.StreamTypeService.StreamNames.Length > 1) + { + throw new ExprValidationException("Wildcard must be stream wildcard if specifying multiple streams, use the 'streamname.*' syntax instead"); + } + _streamNum = 0; + } + else + { + _streamNum = validationContext.StreamTypeService.GetStreamNumForStreamName(StreamName); + } + + if (_streamNum == -1) + { + throw new ExprValidationException("Stream by name '" + StreamName + "' could not be found among all streams"); + } + + _eventType = validationContext.StreamTypeService.EventTypes[_streamNum]; + _type = _eventType.UnderlyingType; + return null; + } + + public virtual Type ReturnType + { + get + { + if (_streamNum == -1) + { + throw new IllegalStateException("Stream underlying node has not been validated"); + } + return _type; + } + } + + public override bool IsConstantResult + { + get { return false; } + } + + /// + /// Returns stream id supplying the property value. + /// + /// stream number + public int StreamId + { + get + { + if (_streamNum == -1) + { + throw new IllegalStateException("Stream underlying node has not been validated"); + } + return _streamNum; + } + } + + public override string ToString() + { + return "streamName=" + StreamName + + " streamNum=" + _streamNum; + } + + public virtual object Evaluate(EvaluateParams evaluateParams) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QExprStreamUnd(this); } + EventBean theEvent = evaluateParams.EventsPerStream[_streamNum]; + if (theEvent == null) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprStreamUnd(null); } + return null; + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprStreamUnd(theEvent.Underlying); } + return theEvent.Underlying; + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + writer.Write(StreamName); + if (_isWildcard) + { + writer.Write(".*"); + } + } + + public override ExprPrecedenceEnum Precedence + { + get { return ExprPrecedenceEnum.UNARY; } + } + + public EventType EventType + { + get { return _eventType; } + } + + public override bool EqualsNode(ExprNode node) + { + var other = node as ExprStreamUnderlyingNodeImpl; + if (other == null) + { + return false; + } + + if (_isWildcard != other._isWildcard) + { + return false; + } + return _isWildcard || StreamName.Equals(other.StreamName); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprSubstitutionNode.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprSubstitutionNode.cs new file mode 100755 index 000000000..da89c096f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprSubstitutionNode.cs @@ -0,0 +1,93 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +using com.espertech.esper.client; +using com.espertech.esper.compat; + +namespace com.espertech.esper.epl.expression.core +{ + /// + /// Represents a substitution value to be substituted in an expression tree, not valid for any purpose of use as an expression, however can take a place in an expression tree. + /// + [Serializable] + public class ExprSubstitutionNode : ExprNodeBase + { + private const String ERROR_MSG = "Invalid use of substitution parameters marked by '?' in statement, use the prepare method to prepare statements with substitution parameters"; + + /// + /// Initializes a new instance of the class. + /// + /// the index of the substitution parameter + public ExprSubstitutionNode(int index) + { + Index = index; + } + + /// + /// Initializes a new instance of the class. + /// + /// the name of the substitution parameter + public ExprSubstitutionNode(string name) + { + Name = name; + } + + public override ExprNode Validate(ExprValidationContext validationContext) + { + throw new ExprValidationException(ERROR_MSG); + } + + /// + /// Returns the substitution parameter index (or null if by-name). + /// + public int? Index { get; set; } + + /// + /// Returns the substitution parameter name (or null if by-index). + /// + public String Name { get; set; } + + public override bool IsConstantResult + { + get { return false; } + } + + public Type ReturnType + { + get { throw new IllegalStateException(ERROR_MSG); } + } + + public Object Evaluate(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext exprEvaluatorContext) + { + throw new EPException(ERROR_MSG); + } + + public override ExprEvaluator ExprEvaluator + { + get { throw new EPException(ERROR_MSG); } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + writer.Write('?'); + } + + public override ExprPrecedenceEnum Precedence + { + get { return ExprPrecedenceEnum.UNARY; } + } + + public override bool EqualsNode(ExprNode node) + { + return (node is ExprSubstitutionNode); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprTypedNoEvalNode.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprTypedNoEvalNode.cs new file mode 100755 index 000000000..2f5f2cb0a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprTypedNoEvalNode.cs @@ -0,0 +1,73 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +using com.espertech.esper.client; + +namespace com.espertech.esper.epl.expression.core +{ + /// + /// Represents an expression node that returns the predefined type and + /// that cannot be evaluated. + /// + [Serializable]public class ExprTypedNoEvalNode + : ExprNodeBase + , ExprEvaluator + { + private readonly string _returnTypeName; + private readonly Type _returnType; + + public ExprTypedNoEvalNode(string returnTypeName, Type returnType) + { + _returnTypeName = returnTypeName; + _returnType = returnType; + } + + public override ExprEvaluator ExprEvaluator + { + get { return this; } + } + + public override ExprNode Validate(ExprValidationContext validationContext) + { + return null; + } + + public override bool IsConstantResult + { + get { return false; } + } + + public Type ReturnType + { + get { return _returnType; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + writer.Write(_returnTypeName); + } + + public override ExprPrecedenceEnum Precedence + { + get { return ExprPrecedenceEnum.UNARY; } + } + + public override bool EqualsNode(ExprNode node) + { + return false; + } + + public object Evaluate(EvaluateParams evaluateParams) + { + throw new EPException(ReturnType.Name + " cannot be evaluated"); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprValidationContext.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprValidationContext.cs new file mode 100755 index 000000000..e8345e8b3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprValidationContext.cs @@ -0,0 +1,120 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client.annotation; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.epl.variable; +using com.espertech.esper.events; +using com.espertech.esper.schedule; +using com.espertech.esper.script; + +namespace com.espertech.esper.epl.expression.core +{ + public class ExprValidationContext + { + public ExprValidationContext(StreamTypeService streamTypeService, EngineImportService engineImportService, StatementExtensionSvcContext statementExtensionSvcContext, ViewResourceDelegateUnverified viewResourceDelegate, TimeProvider timeProvider, VariableService variableService, TableService tableService, ExprEvaluatorContext exprEvaluatorContext, EventAdapterService eventAdapterService, string statementName, int statementId, Attribute[] annotations, ContextDescriptor contextDescriptor, ScriptingService scriptingService, bool disablePropertyExpressionEventCollCache, bool allowRollupFunctions, bool allowBindingConsumption, bool isUnidirectionalJoin, string intoTableName, bool isFilterExpression) + { + StreamTypeService = streamTypeService; + EngineImportService = engineImportService; + StatementExtensionSvcContext = statementExtensionSvcContext; + ViewResourceDelegate = viewResourceDelegate; + TimeProvider = timeProvider; + VariableService = variableService; + TableService = tableService; + ExprEvaluatorContext = exprEvaluatorContext; + EventAdapterService = eventAdapterService; + StatementName = statementName; + StatementId = statementId; + Annotations = annotations; + ContextDescriptor = contextDescriptor; + ScriptingService = scriptingService; + IsDisablePropertyExpressionEventCollCache = disablePropertyExpressionEventCollCache; + IsAllowRollupFunctions = allowRollupFunctions; + IsAllowBindingConsumption = allowBindingConsumption; + IsResettingAggregations = isUnidirectionalJoin; + IntoTableName = intoTableName; + IsFilterExpression = isFilterExpression; + + IsExpressionAudit = AuditEnum.EXPRESSION.GetAudit(annotations) != null; + IsExpressionNestedAudit = AuditEnum.EXPRESSION_NESTED.GetAudit(annotations) != null; + } + + public ExprValidationContext(StreamTypeServiceImpl types, ExprValidationContext ctx) + : this( + types, + ctx.EngineImportService, + ctx.StatementExtensionSvcContext, + ctx.ViewResourceDelegate, + ctx.TimeProvider, + ctx.VariableService, + ctx.TableService, + ctx.ExprEvaluatorContext, + ctx.EventAdapterService, + ctx.StatementName, + ctx.StatementId, + ctx.Annotations, + ctx.ContextDescriptor, + ctx.ScriptingService, + ctx.IsDisablePropertyExpressionEventCollCache, false, + ctx.IsAllowBindingConsumption, + ctx.IsResettingAggregations, + ctx.IntoTableName, + false) + { + } + + public StreamTypeService StreamTypeService { get; private set; } + + public EngineImportService EngineImportService { get; private set; } + + public StatementExtensionSvcContext StatementExtensionSvcContext { get; private set; } + + public ViewResourceDelegateUnverified ViewResourceDelegate { get; private set; } + + public TimeProvider TimeProvider { get; private set; } + + public VariableService VariableService { get; private set; } + + public ExprEvaluatorContext ExprEvaluatorContext { get; private set; } + + public EventAdapterService EventAdapterService { get; private set; } + + public string StatementName { get; private set; } + + public Attribute[] Annotations { get; private set; } + + public bool IsExpressionNestedAudit { get; private set; } + + public bool IsExpressionAudit { get; private set; } + + public int StatementId { get; private set; } + + public ContextDescriptor ContextDescriptor { get; private set; } + + public bool IsDisablePropertyExpressionEventCollCache { get; private set; } + + public bool IsAllowRollupFunctions { get; private set; } + + public TableService TableService { get; private set; } + + public bool IsAllowBindingConsumption { get; private set; } + + public bool IsResettingAggregations { get; private set; } + + public string IntoTableName { get; private set; } + + public ScriptingService ScriptingService { get; private set; } + + public bool IsFilterExpression { get; private set; } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprValidationContextUseEnum.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprValidationContextUseEnum.cs new file mode 100755 index 000000000..d29a25638 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprValidationContextUseEnum.cs @@ -0,0 +1,19 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.expression.core +{ + public enum ExprValidationContextUseEnum + { + SELECT, + HAVING, + ORDERBY, + ROLLUP, + OTHER + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprValidationException.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprValidationException.cs new file mode 100755 index 000000000..9ef937b18 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprValidationException.cs @@ -0,0 +1,35 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.expression.core +{ + /// Thrown to indicate a validation error in a filter expression. + [Serializable] + public class ExprValidationException : Exception + { + /// Ctor. + /// validation error message + /// + public ExprValidationException(String message) + : base(message) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The message. + /// The cause. + public ExprValidationException(String message, Exception cause) + : base(message, cause) + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprValidationPropertyException.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprValidationPropertyException.cs new file mode 100755 index 000000000..6aa8b7cdd --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprValidationPropertyException.cs @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.expression.core +{ + /// + /// Thrown to indicate a validation error in an expression originating from a property resolution error. + /// + public class ExprValidationPropertyException : ExprValidationException + { + public ExprValidationPropertyException(string message) : base(message) + { + } + + public ExprValidationPropertyException(string message, + Exception cause) : base(message, cause) + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprValidator.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprValidator.cs new file mode 100755 index 000000000..f6bcaa35c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprValidator.cs @@ -0,0 +1,16 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.expression.core +{ + /// Validation interface for expression nodes. + public interface ExprValidator + { + ExprNode Validate(ExprValidationContext validationContext); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprVariableNode.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprVariableNode.cs new file mode 100755 index 000000000..7f238b6ba --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprVariableNode.cs @@ -0,0 +1,21 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.expression.core +{ + /// + /// Represents a variable in an expression tree. + /// + public interface ExprVariableNode : ExprConstantNode + { + String VariableNameWithSubProp { get; } + String VariableName { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprVariableNodeImpl.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprVariableNodeImpl.cs new file mode 100755 index 000000000..2b5bf404a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprVariableNodeImpl.cs @@ -0,0 +1,241 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.start; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.variable; +using com.espertech.esper.events; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.expression.core +{ + /// + /// Represents a variable in an expression tree. + /// + [Serializable] + public class ExprVariableNodeImpl + : ExprNodeBase + , ExprEvaluator + , ExprVariableNode + { + private readonly String _variableName; + private readonly String _optSubPropName; + private readonly bool _isConstant; + private readonly Object _valueIfConstant; + + private Type _variableType; + private bool _isPrimitive; + [NonSerialized] + private EventPropertyGetter _eventTypeGetter; + [NonSerialized] + private IDictionary _readersPerCp; + [NonSerialized] + private VariableReader _readerNonCP; + + /// + /// Ctor. + /// + /// The variable meta data. + /// Name of the opt sub property. + /// Variables metadata is null + public ExprVariableNodeImpl(VariableMetaData variableMetaData, String optSubPropName) + { + if (variableMetaData == null) + { + throw new ArgumentException("Variables metadata is null"); + } + _variableName = variableMetaData.VariableName; + _optSubPropName = optSubPropName; + _isConstant = variableMetaData.IsConstant; + _valueIfConstant = _isConstant ? variableMetaData.VariableStateFactory.InitialState : null; + } + + public bool IsConstantValue + { + get { return _isConstant; } + } + + public override ExprEvaluator ExprEvaluator + { + get { return this; } + } + + /// Returns the name of the variable. + /// variable name + public string VariableName + { + get { return _variableName; } + } + + public object GetConstantValue(ExprEvaluatorContext context) + { + return _isConstant ? _valueIfConstant : null; + } + + public override bool IsConstantResult + { + get { return _isConstant; } + } + + public override ExprNode Validate(ExprValidationContext validationContext) + { + // determine if any types are property agnostic; If yes, resolve to variable + var hasPropertyAgnosticType = false; + var types = validationContext.StreamTypeService.EventTypes; + for (var i = 0; i < validationContext.StreamTypeService.EventTypes.Length; i++) + { + if (types[i] is EventTypeSPI) + { + hasPropertyAgnosticType |= ((EventTypeSPI)types[i]).Metadata.IsPropertyAgnostic; + } + } + + if (!hasPropertyAgnosticType) + { + // the variable name should not overlap with a property name + try + { + validationContext.StreamTypeService.ResolveByPropertyName(_variableName, false); + throw new ExprValidationException("The variable by name '" + _variableName + "' is ambigous to a property of the same name"); + } + catch (DuplicatePropertyException e) + { + throw new ExprValidationException("The variable by name '" + _variableName + "' is ambigous to a property of the same name"); + } + catch (PropertyNotFoundException e) + { + // expected + } + } + + VariableMetaData variableMetadata = validationContext.VariableService.GetVariableMetaData(_variableName); + if (variableMetadata == null) + { + throw new ExprValidationException("Failed to find variable by name '" + _variableName + "'"); + } + _isPrimitive = variableMetadata.EventType == null; + _variableType = variableMetadata.VariableType; + + if (_optSubPropName != null) + { + if (variableMetadata.EventType == null) + { + throw new ExprValidationException("Property '" + _optSubPropName + "' is not valid for variable '" + _variableName + "'"); + } + _eventTypeGetter = variableMetadata.EventType.GetGetter(_optSubPropName); + if (_eventTypeGetter == null) + { + throw new ExprValidationException("Property '" + _optSubPropName + "' is not valid for variable '" + _variableName + "'"); + } + _variableType = variableMetadata.EventType.GetPropertyType(_optSubPropName); + } + + _readersPerCp = validationContext.VariableService.GetReadersPerCP(_variableName); + if (variableMetadata.ContextPartitionName == null) + { + _readerNonCP = _readersPerCp.Get(EPStatementStartMethodConst.DEFAULT_AGENT_INSTANCE_ID); + } + + return null; + } + + public Type ConstantType + { + get { return _variableType; } + } + + public Type ReturnType + { + get { return _variableType; } + } + + public override String ToString() + { + return "variableName=" + _variableName; + } + + public object Evaluate(EvaluateParams evaluateParams) + { + VariableReader reader; + if (_readerNonCP != null) + { + reader = _readerNonCP; + } + else + { + reader = _readersPerCp.Get(evaluateParams.ExprEvaluatorContext.AgentInstanceId); + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QExprVariable(this); } + var value = reader.Value; + if (_isPrimitive || value == null) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprVariable(value); } + return value; + } + var theEvent = (EventBean)value; + if (_optSubPropName == null) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprVariable(theEvent.Underlying); } + return theEvent.Underlying; + } + var result = _eventTypeGetter.Get(theEvent); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprVariable(result); } + return result; + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + writer.Write(_variableName); + if (_optSubPropName != null) + { + writer.Write("."); + writer.Write(_optSubPropName); + } + } + + public override ExprPrecedenceEnum Precedence + { + get { return ExprPrecedenceEnum.UNARY; } + } + + public override bool EqualsNode(ExprNode node) + { + if (!(node is ExprVariableNodeImpl)) + { + return false; + } + + var that = (ExprVariableNodeImpl)node; + + if (_optSubPropName != null ? !_optSubPropName.Equals(that._optSubPropName) : that._optSubPropName != null) + { + return false; + } + return that._variableName.Equals(_variableName); + } + + public string VariableNameWithSubProp + { + get + { + if (_optSubPropName == null) + { + return _variableName; + } + return _variableName + "." + _optSubPropName; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprWildcard.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprWildcard.cs new file mode 100755 index 000000000..da26d1524 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprWildcard.cs @@ -0,0 +1,17 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.expression.core +{ + /// + /// Expression for use as wildcard (*). + /// + public interface ExprWildcard + { + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ExprWildcardImpl.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ExprWildcardImpl.cs new file mode 100755 index 000000000..07196e125 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ExprWildcardImpl.cs @@ -0,0 +1,67 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +using com.espertech.esper.type; + +namespace com.espertech.esper.epl.expression.core +{ + /// + /// Expression for use within crontab to specify a wildcard. + /// + [Serializable] + public class ExprWildcardImpl + : ExprNodeBase + , ExprEvaluator + , ExprWildcard + { + private static readonly WildcardParameter wildcardParameter = new WildcardParameter(); + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + writer.Write("*"); + } + + public override ExprPrecedenceEnum Precedence + { + get { return ExprPrecedenceEnum.UNARY; } + } + + public override ExprEvaluator ExprEvaluator + { + get { return this; } + } + + public override bool IsConstantResult + { + get { return true; } + } + + public override bool EqualsNode(ExprNode node) + { + return node is ExprWildcardImpl; + } + + public override ExprNode Validate(ExprValidationContext validationContext) + { + return null; + } + + public Type ReturnType + { + get { return typeof(WildcardParameter); } + } + + public object Evaluate(EvaluateParams evaluateParams) + { + return wildcardParameter; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/NullExprEvaluator.cs b/NEsper.Core/NEsper.Core/epl/expression/core/NullExprEvaluator.cs new file mode 100755 index 000000000..164289675 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/NullExprEvaluator.cs @@ -0,0 +1,39 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.expression.core +{ + public class NullExprEvaluator : ExprEvaluator + { + /// + /// Returns the type that the node's evaluate method returns an instance of. + /// + /// + /// + /// type returned when evaluated + /// + public Type ReturnType + { + get { return null; } + } + + /// + /// Evaluate event tuple and return result. + /// + /// The evaluate params. + /// + /// evaluation result, a bool value for OR/AND-type evalution nodes. + /// + public object Evaluate(EvaluateParams evaluateParams) + { + return null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/core/ProxyExprEvaluator.cs b/NEsper.Core/NEsper.Core/epl/expression/core/ProxyExprEvaluator.cs new file mode 100755 index 000000000..95abf21aa --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/core/ProxyExprEvaluator.cs @@ -0,0 +1,77 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.expression.core +{ + public class ProxyExprEvaluator : ExprEvaluator + { + /// + /// Initializes a new instance of the class. + /// + /// The @delegate. + /// Type of the return. + public ProxyExprEvaluator(Func procEvaluate, Type returnType) + { + ProcEvaluate = procEvaluate; + ReturnType = returnType; + } + + /// + /// Initializes a new instance of the class. + /// + /// The @delegate. + public ProxyExprEvaluator(Func procEvaluate) + { + ProcEvaluate = procEvaluate; + ReturnType = null; + } + + /// + /// Initializes a new instance of the class. + /// + public ProxyExprEvaluator() + { + ProcEvaluate = evaluateParams => null; + ProcReturnType = () => null; + } + + public Func ProcEvaluate { get; set; } + + /// + /// Proxy method for handling the return type. + /// + public Func ProcReturnType { get; set; } + + /// + /// Returns the type that the node's evaluate method returns an instance of. + /// + /// + /// + /// type returned when evaluated + /// + public Type ReturnType + { + get { return ProcReturnType.Invoke(); } + set { ProcReturnType = () => value; } + } + + /// + /// Evaluate event tuple and return result. + /// + /// The evaluate params. + /// + /// evaluation result, a bool value for OR/AND-type evalution nodes. + /// + public object Evaluate(EvaluateParams evaluateParams) + { + return ProcEvaluate.Invoke(evaluateParams); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotEnumerationSource.cs b/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotEnumerationSource.cs new file mode 100755 index 000000000..5a466f878 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotEnumerationSource.cs @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.rettype; + +namespace com.espertech.esper.epl.expression.dot +{ + public class ExprDotEnumerationSource + { + public ExprDotEnumerationSource(EPType returnType, int? streamOfProviderIfApplicable, ExprEvaluatorEnumeration enumeration) + { + ReturnType = returnType; + StreamOfProviderIfApplicable = streamOfProviderIfApplicable; + Enumeration = enumeration; + } + + public ExprEvaluatorEnumeration Enumeration { get; private set; } + + public EPType ReturnType { get; private set; } + + public int? StreamOfProviderIfApplicable { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotEnumerationSourceForProps.cs b/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotEnumerationSourceForProps.cs new file mode 100755 index 000000000..cbb34ef2f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotEnumerationSourceForProps.cs @@ -0,0 +1,24 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.rettype; + +namespace com.espertech.esper.epl.expression.dot +{ + public class ExprDotEnumerationSourceForProps : ExprDotEnumerationSource + { + public ExprDotEnumerationSourceForProps(ExprEvaluatorEnumeration enumeration, EPType returnType, int? streamOfProviderIfApplicable, ExprEvaluatorEnumerationGivenEvent enumerationGivenEvent) + : base(returnType, streamOfProviderIfApplicable, enumeration) + { + EnumerationGivenEvent = enumerationGivenEvent; + } + + public ExprEvaluatorEnumerationGivenEvent EnumerationGivenEvent { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotEval.cs b/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotEval.cs new file mode 100755 index 000000000..1553097fb --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotEval.cs @@ -0,0 +1,24 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.rettype; + +namespace com.espertech.esper.epl.expression.dot +{ + public interface ExprDotEval + { + object Evaluate(object target, EvaluateParams evaluateParams); + //Object Evaluate(Object target, EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext exprEvaluatorContext); + EPType TypeInfo { get; } + void Visit(ExprDotEvalVisitor visitor); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotEvalArrayGet.cs b/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotEvalArrayGet.cs new file mode 100755 index 000000000..aaf0494d3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotEvalArrayGet.cs @@ -0,0 +1,69 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.rettype; + +namespace com.espertech.esper.epl.expression.dot +{ + public class ExprDotEvalArrayGet : ExprDotEval + { + private static readonly ILog Log = + LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly EPType _typeInfo; + private readonly ExprEvaluator _indexExpression; + + public ExprDotEvalArrayGet(ExprEvaluator index, Type componentType) + { + _indexExpression = index; + _typeInfo = EPTypeHelper.SingleValue(componentType); + } + + public object Evaluate(object target, EvaluateParams evalParams) + { + if (target == null) + { + return null; + } + + var index = _indexExpression.Evaluate(evalParams); + if (index == null) + { + return null; + } + if (!index.IsInt()) + { + return null; + } + + var indexNum = index.AsInt(); + var targetArray = (Array) target; + if (targetArray.Length <= indexNum) + { + return null; + } + return targetArray.GetValue(indexNum); + } + + public EPType TypeInfo + { + get { return _typeInfo; } + } + + public void Visit(ExprDotEvalVisitor visitor) + { + visitor.VisitArraySingleItemSource(); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotEvalArraySize.cs b/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotEvalArraySize.cs new file mode 100755 index 000000000..280bf3a1a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotEvalArraySize.cs @@ -0,0 +1,43 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.rettype; + +namespace com.espertech.esper.epl.expression.dot +{ + public class ExprDotEvalArraySize : ExprDotEval + { + private static readonly ILog Log = + LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + public object Evaluate(object target, EvaluateParams evalParams) + { + var targetArray = target as Array; + if (targetArray == null) + { + return null; + } + + return targetArray.Length; + } + + public EPType TypeInfo + { + get { return EPTypeHelper.SingleValue(typeof (int?)); } + } + + public void Visit(ExprDotEvalVisitor visitor) + { + visitor.VisitArrayLength(); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotEvalPropertyExprBase.cs b/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotEvalPropertyExprBase.cs new file mode 100755 index 000000000..756d5c326 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotEvalPropertyExprBase.cs @@ -0,0 +1,52 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.dot +{ + [Serializable] + public abstract class ExprDotEvalPropertyExprBase : ExprEvaluator + { + protected internal readonly String StatementName; + protected internal readonly String PropertyName; + protected internal readonly int StreamNum; + protected internal readonly ExprEvaluator ExprEvaluator; + private readonly Type _propertyType; + + protected ExprDotEvalPropertyExprBase(string statementName, string propertyName, int streamNum, ExprEvaluator exprEvaluator, Type propertyType) + { + StatementName = statementName; + PropertyName = propertyName; + StreamNum = streamNum; + ExprEvaluator = exprEvaluator; + _propertyType = propertyType; + } + + public Type ReturnType + { + get { return _propertyType; } + } + + public abstract object Evaluate(EvaluateParams evaluateParams); + + protected String GetWarningText(String expectedType, Object received) + { + return string.Format( + "Statement '{0}' property {1} parameter expression expected a value of {2} but received {3}", + StatementName, + PropertyName, + expectedType, + received == null ? "null" : TypeHelper.GetTypeNameFullyQualPretty(received.GetType()) + ); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotEvalPropertyExprIndexed.cs b/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotEvalPropertyExprIndexed.cs new file mode 100755 index 000000000..aafd57a77 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotEvalPropertyExprIndexed.cs @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.dot +{ + public class ExprDotEvalPropertyExprIndexed : ExprDotEvalPropertyExprBase + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly EventPropertyGetterIndexed _indexedGetter; + + public ExprDotEvalPropertyExprIndexed(string statementName, string propertyName, int streamNum, ExprEvaluator exprEvaluator, Type propertyType, EventPropertyGetterIndexed indexedGetter) + : base(statementName, propertyName, streamNum, exprEvaluator, propertyType) + { + _indexedGetter = indexedGetter; + } + + + public override object Evaluate(EvaluateParams evaluateParams) + { + var eventInQuestion = evaluateParams.EventsPerStream[base.StreamNum]; + if (eventInQuestion == null) + { + return null; + } + var index = base.ExprEvaluator.Evaluate(evaluateParams); + if (index == null || !index.IsInt()) + { + Log.Warn(base.GetWarningText("integer", index)); + return null; + } + return _indexedGetter.Get(eventInQuestion, index.AsInt()); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotEvalPropertyExprMapped.cs b/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotEvalPropertyExprMapped.cs new file mode 100755 index 000000000..591193cc7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotEvalPropertyExprMapped.cs @@ -0,0 +1,47 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.dot +{ + public class ExprDotEvalPropertyExprMapped : ExprDotEvalPropertyExprBase + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly EventPropertyGetterMapped _mappedGetter; + + public ExprDotEvalPropertyExprMapped(string statementName, string propertyName, int streamNum, ExprEvaluator exprEvaluator, Type propertyType, EventPropertyGetterMapped mappedGetter) + : base(statementName, propertyName, streamNum, exprEvaluator, propertyType) + { + this._mappedGetter = mappedGetter; + } + + public override object Evaluate(EvaluateParams evaluateParams) + { + var eventInQuestion = evaluateParams.EventsPerStream[base.StreamNum]; + if (eventInQuestion == null) + { + return null; + } + var result = base.ExprEvaluator.Evaluate(evaluateParams); + if (result != null && (!(result is string))) + { + Log.Warn(base.GetWarningText("string", result)); + return null; + } + return _mappedGetter.Get(eventInQuestion, (string) result); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotEvalRootChild.cs b/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotEvalRootChild.cs new file mode 100755 index 000000000..e1d32c968 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotEvalRootChild.cs @@ -0,0 +1,154 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.dot.inner; +using com.espertech.esper.epl.rettype; +using com.espertech.esper.events; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.dot +{ + [Serializable] + public class ExprDotEvalRootChild + : ExprEvaluator + , ExprEvaluatorEnumeration + { + private readonly ExprDotNode _dotNode; + private readonly ExprDotEvalRootChildInnerEval _innerEvaluator; + private readonly ExprDotEval[] _evalIteratorEventBean; + private readonly ExprDotEval[] _evalUnpacking; + + public ExprDotEvalRootChild( + bool hasEnumerationMethod, + ExprDotNode dotNode, + ExprEvaluator rootNodeEvaluator, + ExprEvaluatorEnumeration rootLambdaEvaluator, + EPType typeInfo, + ExprDotEval[] evalIteratorEventBean, + ExprDotEval[] evalUnpacking, + bool checkedUnpackEvent) + { + _dotNode = dotNode; + if (rootLambdaEvaluator != null) + { + if (typeInfo is EventMultiValuedEPType) + { + _innerEvaluator = new InnerEvaluatorEnumerableEventCollection( + rootLambdaEvaluator, ((EventMultiValuedEPType) typeInfo).Component); + } + else if (typeInfo is EventEPType) { + _innerEvaluator = new InnerEvaluatorEnumerableEventBean( + rootLambdaEvaluator, ((EventEPType) typeInfo).EventType); + } + else { + _innerEvaluator = new InnerEvaluatorEnumerableScalarCollection( + rootLambdaEvaluator, ((ClassMultiValuedEPType) typeInfo).Component); + } + } + else { + if (checkedUnpackEvent) { + _innerEvaluator = new InnerEvaluatorScalarUnpackEvent(rootNodeEvaluator); + } + else { + var returnType = rootNodeEvaluator.ReturnType; + if (hasEnumerationMethod && returnType.IsArray) + { + if (returnType.GetElementType().IsPrimitive) + { + _innerEvaluator = new InnerEvaluatorArrPrimitiveToColl(rootNodeEvaluator); + } + else + { + _innerEvaluator = new InnerEvaluatorArrObjectToColl(rootNodeEvaluator); + } + } + else if (hasEnumerationMethod && TypeHelper.IsImplementsInterface(returnType, typeof (ICollection))) + { + _innerEvaluator = new InnerEvaluatorColl(rootNodeEvaluator); + } + else + { + _innerEvaluator = new InnerEvaluatorScalar(rootNodeEvaluator); + } + } + } + _evalUnpacking = evalUnpacking; + _evalIteratorEventBean = evalIteratorEventBean; + } + + public virtual Type ReturnType + { + get { return _evalUnpacking[_evalUnpacking.Length - 1].TypeInfo.GetNormalizedClass(); } + } + + public object Evaluate(EvaluateParams evaluateParams) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QExprDot(_dotNode);} + var inner = _innerEvaluator.Evaluate(evaluateParams); + if (inner != null) { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QExprDotChain(_innerEvaluator.TypeInfo, inner, _evalUnpacking);} + inner = ExprDotNodeUtility.EvaluateChain(_evalUnpacking, inner, evaluateParams.EventsPerStream, evaluateParams.IsNewData, evaluateParams.ExprEvaluatorContext); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprDotChain(); } + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprDot(inner); } + return inner; + } + + public ICollection EvaluateGetROCollectionEvents(EvaluateParams evaluateParams) + { + var inner = _innerEvaluator.EvaluateGetROCollectionEvents(evaluateParams); + if (inner != null) { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QExprDotChain(_innerEvaluator.TypeInfo, inner, _evalUnpacking);} + inner = ExprDotNodeUtility.EvaluateChain(_evalIteratorEventBean, inner, evaluateParams.EventsPerStream, evaluateParams.IsNewData, evaluateParams.ExprEvaluatorContext).Unwrap(true); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprDotChain();} + return inner; + } + return null; + } + + public ICollection EvaluateGetROCollectionScalar(EvaluateParams evaluateParams) + { + var inner = _innerEvaluator.EvaluateGetROCollectionScalar(evaluateParams); + if (inner != null) { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QExprDotChain(_innerEvaluator.TypeInfo, inner, _evalUnpacking);} + inner = ExprDotNodeUtility.EvaluateChain(_evalIteratorEventBean, inner, evaluateParams.EventsPerStream, evaluateParams.IsNewData, evaluateParams.ExprEvaluatorContext).Unwrap(true); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprDotChain();} + return inner; + } + return null; + } + + public EventType GetEventTypeCollection(EventAdapterService eventAdapterService, int statementId) + { + return _innerEvaluator.EventTypeCollection; + } + + public Type ComponentTypeCollection + { + get { return _innerEvaluator.ComponentTypeCollection; } + } + + public EventType GetEventTypeSingle(EventAdapterService eventAdapterService, int statementId) + { + return null; + } + + public EventBean EvaluateGetEventBean(EvaluateParams evaluateParams) + { + return null; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotEvalRootChildInnerEval.cs b/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotEvalRootChildInnerEval.cs new file mode 100755 index 000000000..2fb7b5a1c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotEvalRootChildInnerEval.cs @@ -0,0 +1,31 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.rettype; + +namespace com.espertech.esper.epl.expression.dot +{ + public interface ExprDotEvalRootChildInnerEval + { + object Evaluate(EvaluateParams evaluateParams); + ICollection EvaluateGetROCollectionEvents(EvaluateParams evaluateParams); + ICollection EvaluateGetROCollectionScalar(EvaluateParams evaluateParams); + EventBean EvaluateGetEventBean(EvaluateParams evaluateParams); + + EventType EventTypeCollection { get; } + EventType EventTypeSingle { get; } + Type ComponentTypeCollection { get; } + + EPType TypeInfo { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotEvalStaticMethod.cs b/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotEvalStaticMethod.cs new file mode 100755 index 000000000..36b0533d0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotEvalStaticMethod.cs @@ -0,0 +1,225 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; + +using XLR8.CGLib; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.enummethod.dot; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.rettype; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.dot +{ + public class ExprDotEvalStaticMethod : ExprEvaluator, EventPropertyGetter + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private static MethodInfo UnwrapCollection; + private static MethodInfo UnwrapList; + + static ExprDotEvalStaticMethod() + { + UnwrapCollection = typeof(CompatExtensions).GetMethod("Unwrap", new Type[] { typeof(object), typeof(bool) }); + UnwrapList = typeof(CompatExtensions).GetMethod("UnwrapIntoList", new Type[] { typeof(object), typeof(bool) }); + } + + private readonly String _statementName; + private readonly String _classOrPropertyName; + private readonly FastMethod _staticMethod; + private readonly ExprEvaluator[] _childEvals; + private readonly bool _isConstantParameters; + private readonly ExprDotEval[] _chainEval; + private readonly ExprDotStaticMethodWrap _resultWrapLambda; + private readonly bool _rethrowExceptions; + private readonly Object _targetObject; + + private bool _isCachedResult; + private Object _cachedResult; + + public ExprDotEvalStaticMethod(String statementName, + String classOrPropertyName, + FastMethod staticMethod, + ExprEvaluator[] childEvals, + bool constantParameters, + ExprDotStaticMethodWrap resultWrapLambda, + ExprDotEval[] chainEval, + bool rethrowExceptions, + Object targetObject) + { + _statementName = statementName; + _classOrPropertyName = classOrPropertyName; + _staticMethod = staticMethod; + _childEvals = childEvals; + _targetObject = targetObject; + _isConstantParameters = chainEval.Length <= 0 && constantParameters; + _resultWrapLambda = resultWrapLambda; + _chainEval = chainEval; + _rethrowExceptions = rethrowExceptions; + } + + public Type ReturnType + { + get + { + if (_chainEval.Length == 0) + { + return _staticMethod.ReturnType; + } + else + { + return EPTypeHelper.GetNormalizedClass(_chainEval[_chainEval.Length - 1].TypeInfo); + } + } + } + + private object RewriteArgument(object value, Type parameterType) + { + if (value == null) + return null; + if (value.GetType() == parameterType) + return value; + if (TypeHelper.IsSubclassOrImplementsInterface(value.GetType(), parameterType)) + return value; + + // ICollection gets passed around a lot internally rather than + // the actual type required for final targeting. If this is a collection + // to collection target, then we can rewrite the values. + if (value is ICollection) + { + if (parameterType.IsGenericCollection()) + { + var genericType = parameterType.GetGenericArguments()[0]; + return UnwrapCollection + .MakeGenericMethod(new Type[] { genericType }) + .Invoke(null, new object[] { value, true }); + } + else if (parameterType.IsGenericList()) + { + var genericType = parameterType.GetGenericArguments()[0]; + return UnwrapList + .MakeGenericMethod(new Type[] { genericType }) + .Invoke(null, new object[] { value, true }); + } + } + + return value; + } + + public object Evaluate(EvaluateParams evaluateParams) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QExprPlugInSingleRow(_staticMethod.Target); } + if ((_isConstantParameters) && (_isCachedResult)) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprPlugInSingleRow(_cachedResult); } + return _cachedResult; + } + + var methodParameters = _staticMethod.Target.GetParameters(); + + var args = new Object[_childEvals.Length]; + for (var i = 0; i < args.Length; i++) + { + var arg = _childEvals[i].Evaluate(evaluateParams); + args[i] = RewriteArgument(arg, methodParameters[i].ParameterType); + } + + // The method is static so the object it is invoked on + // can be null + try + { + var result = _staticMethod.Invoke(_targetObject, args); + + result = ExprDotNodeUtility.EvaluateChainWithWrap( + _resultWrapLambda, result, null, _staticMethod.ReturnType, _chainEval, + evaluateParams.EventsPerStream, + evaluateParams.IsNewData, + evaluateParams.ExprEvaluatorContext); + + if (_isConstantParameters) + { + _cachedResult = result; + _isCachedResult = true; + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprPlugInSingleRow(result); } + return result; + } + catch (EPException) + { + throw; + } + catch (TargetInvocationException e) + { + var message = TypeHelper.GetMessageInvocationTarget( + _statementName, _staticMethod.Target, _classOrPropertyName, args, e); + Log.Error(message, e.InnerException); + if (_rethrowExceptions) + { + throw new EPException(message, e.InnerException); + } + } + catch (Exception e) + { + var message = TypeHelper.GetMessageInvocationTarget( + _statementName, _staticMethod.Target, _classOrPropertyName, args, e); + Log.Error(message, e); + if (_rethrowExceptions) + { + throw new EPException(message, e); + } + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprPlugInSingleRow(null); } + return null; + } + + public Object Get(EventBean eventBean) + { + var args = new Object[_childEvals.Length]; + for (var i = 0; i < args.Length; i++) + { + args[i] = _childEvals[i].Evaluate(new EvaluateParams(new EventBean[] { eventBean }, false, null)); + } + + // The method is static so the object it is invoked on + // can be null + try + { + return _staticMethod.Invoke(_targetObject, args); + } + catch (TargetInvocationException e) + { + var message = TypeHelper.GetMessageInvocationTarget(_statementName, _staticMethod.Target, _classOrPropertyName, args, e); + Log.Error(message, e.InnerException); + if (_rethrowExceptions) + { + throw new EPException(message, e.InnerException); + } + } + return null; + } + + public bool IsExistsProperty(EventBean eventBean) + { + return false; + } + + public Object GetFragment(EventBean eventBean) + { + return null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotEvalStreamEventBean.cs b/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotEvalStreamEventBean.cs new file mode 100755 index 000000000..410e39ac6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotEvalStreamEventBean.cs @@ -0,0 +1,57 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.rettype; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.expression.dot +{ + public class ExprDotEvalStreamEventBean : ExprEvaluator + { + private readonly ExprDotNode _exprDotNode; + private readonly int _streamNumber; + private readonly ExprDotEval[] _evaluators; + + public ExprDotEvalStreamEventBean(ExprDotNode exprDotNode, int streamNumber, ExprDotEval[] evaluators) + { + _exprDotNode = exprDotNode; + _streamNumber = streamNumber; + _evaluators = evaluators; + } + + public Type ReturnType + { + get + { + return EPTypeHelper.GetClassSingleValued(_evaluators[_evaluators.Length - 1].TypeInfo); + } + } + + public object Evaluate(EvaluateParams evaluateParams) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QExprStreamEventMethod(_exprDotNode);} + + EventBean theEvent = evaluateParams.EventsPerStream[_streamNumber]; + if (theEvent == null) { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprStreamEventMethod(null);} + return null; + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QExprDotChain(EPTypeHelper.SingleEvent(theEvent.EventType), theEvent, _evaluators); } + Object inner = ExprDotNodeUtility.EvaluateChain(_evaluators, theEvent, evaluateParams.EventsPerStream, evaluateParams.IsNewData, evaluateParams.ExprEvaluatorContext); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprDotChain();} + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprStreamEventMethod(inner);} + return inner; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotEvalStreamMethod.cs b/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotEvalStreamMethod.cs new file mode 100755 index 000000000..07cc027f1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotEvalStreamMethod.cs @@ -0,0 +1,59 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.rettype; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.expression.dot +{ + public class ExprDotEvalStreamMethod : ExprEvaluator + { + private readonly ExprDotNode _dotNode; + private readonly int _streamNumber; + private readonly ExprDotEval[] _evaluators; + + public ExprDotEvalStreamMethod(ExprDotNode dotNode, int streamNumber, ExprDotEval[] evaluators) + { + _dotNode = dotNode; + _streamNumber = streamNumber; + _evaluators = evaluators; + } + + public Type ReturnType + { + get + { + return EPTypeHelper.GetNormalizedClass(_evaluators[_evaluators.Length - 1].TypeInfo); + } + } + + public object Evaluate(EvaluateParams evaluateParams) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QExprStreamUndMethod(_dotNode);} + + // get underlying event + EventBean theEvent = evaluateParams.EventsPerStream[_streamNumber]; + if (theEvent == null) { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprStreamUndMethod(null);} + return null; + } + Object inner = theEvent.Underlying; + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QExprDotChain(EPTypeHelper.SingleValue(theEvent.EventType.UnderlyingType), inner, _evaluators);} + inner = ExprDotNodeUtility.EvaluateChain(_evaluators, inner, evaluateParams.EventsPerStream, evaluateParams.IsNewData, evaluateParams.ExprEvaluatorContext); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprDotChain();} + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprStreamUndMethod(inner);} + return inner; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotEvalTransposeAsStream.cs b/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotEvalTransposeAsStream.cs new file mode 100755 index 000000000..c84db3b1c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotEvalTransposeAsStream.cs @@ -0,0 +1,35 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.dot +{ + [Serializable] + public class ExprDotEvalTransposeAsStream : ExprEvaluator + { + private readonly ExprEvaluator _inner; + + public ExprDotEvalTransposeAsStream(ExprEvaluator inner) + { + _inner = inner; + } + + public Type ReturnType + { + get { return _inner.ReturnType; } + } + + public object Evaluate(EvaluateParams evaluateParams) + { + return _inner.Evaluate(evaluateParams); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotEvalVariable.cs b/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotEvalVariable.cs new file mode 100755 index 000000000..d00123ab9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotEvalVariable.cs @@ -0,0 +1,71 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.enummethod.dot; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.rettype; +using com.espertech.esper.epl.variable; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.expression.dot +{ + public class ExprDotEvalVariable : ExprEvaluator + { + private readonly ExprDotNode _dotNode; + private readonly VariableReader _variableReader; + private readonly ExprDotStaticMethodWrap _resultWrapLambda; + private readonly ExprDotEval[] _chainEval; + + public ExprDotEvalVariable(ExprDotNode dotNode, VariableReader variableReader, ExprDotStaticMethodWrap resultWrapLambda, ExprDotEval[] chainEval) + { + _dotNode = dotNode; + _variableReader = variableReader; + _resultWrapLambda = resultWrapLambda; + _chainEval = chainEval; + } + + public string VariableName + { + get { return _variableReader.VariableMetaData.VariableName; } + } + + public Type ReturnType + { + get + { + if (_chainEval.Length == 0) + { + return _variableReader.VariableMetaData.VariableType; + } + else + { + return _chainEval[_chainEval.Length - 1].TypeInfo.GetClassSingleValued(); + } + } + } + + public object Evaluate(EvaluateParams evaluateParams) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QExprDot(_dotNode); } + + Object result = _variableReader.Value; + result = ExprDotNodeUtility.EvaluateChainWithWrap( + _resultWrapLambda, result, + _variableReader.VariableMetaData.EventType, + _variableReader.VariableMetaData.VariableType, _chainEval, + evaluateParams.EventsPerStream, + evaluateParams.IsNewData, + evaluateParams.ExprEvaluatorContext); + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprDot(result); } + return result; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotEvalVisitor.cs b/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotEvalVisitor.cs new file mode 100755 index 000000000..3d3788ea3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotEvalVisitor.cs @@ -0,0 +1,24 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.expression.dot +{ + public interface ExprDotEvalVisitor + { + void VisitPropertySource(); + void VisitEnumeration(String name); + void VisitMethod(String methodName); + void VisitDateTime(); + void VisitUnderlyingEvent(); + void VisitUnderlyingEventColl(); + void VisitArraySingleItemSource(); + void VisitArrayLength(); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotMethodEvalDuck.cs b/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotMethodEvalDuck.cs new file mode 100755 index 000000000..742072072 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotMethodEvalDuck.cs @@ -0,0 +1,115 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.epl.expression.core; + +using XLR8.CGLib; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.rettype; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.dot +{ + public class ExprDotMethodEvalDuck : ExprDotEval + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly String _statementName; + private readonly EngineImportService _engineImportService; + private readonly String _methodName; + private readonly Type[] _parameterTypes; + private readonly ExprEvaluator[] _parameters; + + private readonly IDictionary _cache; + + public ExprDotMethodEvalDuck(string statementName, EngineImportService engineImportService, string methodName, Type[] parameterTypes, ExprEvaluator[] parameters) + { + _statementName = statementName; + _engineImportService = engineImportService; + _methodName = methodName; + _parameterTypes = parameterTypes; + _parameters = parameters; + _cache = new Dictionary(); + } + + public void Visit(ExprDotEvalVisitor visitor) + { + visitor.VisitMethod(_methodName); + } + + public object Evaluate(object target, EvaluateParams evalParams) + { + if (target == null) + { + return null; + } + + FastMethod method; + var targetType = target.GetType(); + if (_cache.ContainsKey(targetType)) + { + method = _cache.Get(targetType); + } + else + { + method = GetFastMethod(targetType); + _cache.Put(targetType, method); + } + + if (method == null) + { + return null; + } + + var args = new Object[_parameters.Length]; + for (int i = 0; i < args.Length; i++) + { + args[i] = _parameters[i].Evaluate(evalParams); + } + + try + { + return method.Invoke(target, args); + } + catch (TargetInvocationException e) + { + String message = TypeHelper.GetMessageInvocationTarget(_statementName, method.Target, targetType.FullName, args, e); + Log.Error(message, e.InnerException); + } + return null; + } + + private FastMethod GetFastMethod(Type clazz) + { + try + { + MethodInfo method = _engineImportService.ResolveMethod(clazz, _methodName, _parameterTypes, new bool[_parameterTypes.Length], new bool[_parameterTypes.Length]); + FastClass declaringClass = FastClass.Create(method.DeclaringType); + return declaringClass.GetMethod(method); + } + catch (Exception) + { + Log.Debug("Not resolved for class '" + clazz.Name + "' method '" + _methodName + "'"); + } + return null; + } + + public EPType TypeInfo + { + get { return EPTypeHelper.SingleValue(typeof(Object)); } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotMethodEvalNoDuck.cs b/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotMethodEvalNoDuck.cs new file mode 100755 index 000000000..1b9d3c39d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotMethodEvalNoDuck.cs @@ -0,0 +1,72 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.rettype; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.util; + +using XLR8.CGLib; + +namespace com.espertech.esper.epl.expression.dot +{ + [Serializable] + public class ExprDotMethodEvalNoDuck : ExprDotEval + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + protected readonly String StatementName; + protected readonly FastMethod Method; + private readonly ExprEvaluator[] _parameters; + + public ExprDotMethodEvalNoDuck(String statementName, FastMethod method, ExprEvaluator[] parameters) + { + StatementName = statementName; + Method = method; + _parameters = parameters; + } + + public void Visit(ExprDotEvalVisitor visitor) + { + visitor.VisitMethod(Method.Name); + } + + public virtual object Evaluate(object target, EvaluateParams evalParams) + { + if (target == null) + { + return null; + } + + var args = new Object[_parameters.Length]; + for (int i = 0; i < args.Length; i++) + { + args[i] = _parameters[i].Evaluate(evalParams); + } + + try + { + return Method.Invoke(target, args); + } + catch (TargetInvocationException e) + { + String message = TypeHelper.GetMessageInvocationTarget(StatementName, Method.Target, target.GetType().FullName, args, e); + Log.Error(message, e.InnerException); + } + return null; + } + + public virtual EPType TypeInfo + { + get { return EPTypeHelper.FromMethod(Method.Target); } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotMethodEvalNoDuckUnderlying.cs b/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotMethodEvalNoDuckUnderlying.cs new file mode 100755 index 000000000..350b421c3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotMethodEvalNoDuckUnderlying.cs @@ -0,0 +1,43 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.expression.core; + +using XLR8.CGLib; + +namespace com.espertech.esper.epl.expression.dot +{ + public class ExprDotMethodEvalNoDuckUnderlying : ExprDotMethodEvalNoDuck + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + public ExprDotMethodEvalNoDuckUnderlying(String statementName, FastMethod method, ExprEvaluator[] parameters) + : base(statementName, method, parameters) + { + } + + public override Object Evaluate(Object target, EvaluateParams evaluateParams) + { + if (target == null) + { + return null; + } + if (!(target is EventBean)) + { + Log.Warn("Expected EventBean return value but received '" + target.GetType().FullName + "' for statement " + base.StatementName); + return null; + } + var bean = (EventBean)target; + return base.Evaluate(bean.Underlying, evaluateParams); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotMethodEvalNoDuckWrapArray.cs b/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotMethodEvalNoDuckWrapArray.cs new file mode 100755 index 000000000..2384672b9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotMethodEvalNoDuckWrapArray.cs @@ -0,0 +1,39 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.rettype; +using com.espertech.esper.epl.expression.core; + +using XLR8.CGLib; + +namespace com.espertech.esper.epl.expression.dot +{ + public class ExprDotMethodEvalNoDuckWrapArray : ExprDotMethodEvalNoDuck + { + public ExprDotMethodEvalNoDuckWrapArray(String statementName, FastMethod method, ExprEvaluator[] parameters) + : base(statementName, method, parameters) + { + } + + public override Object Evaluate(Object target, EvaluateParams evaluateParams) + { + var result = base.Evaluate(target, evaluateParams); + if (result == null || !result.GetType().IsArray) { + return null; + } + return (Array) result; // doesn't need to be wrapped for CLR + } + + public override EPType TypeInfo + { + get { return EPTypeHelper.CollectionOfSingleValue(Method.ReturnType.GetElementType()); } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotNode.cs b/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotNode.cs new file mode 100755 index 000000000..49bfb2f06 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotNode.cs @@ -0,0 +1,31 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.epl.datetime.eval; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.variable; + +namespace com.espertech.esper.epl.expression.dot +{ + /// + /// Represents an Dot-operator expression, for use when "(expression).Method(...).Method(...)" + /// + public interface ExprDotNode : ExprNode + { + int? StreamReferencedIfAny { get; } + + IList ChainSpec { get; } + + string IsVariableOpGetName(VariableService variableService); + + ExprDotNodeFilterAnalyzerDesc ExprDotNodeFilterAnalyzerDesc { get; } + } + +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotNodeFilterAnalyzerInput.cs b/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotNodeFilterAnalyzerInput.cs new file mode 100755 index 000000000..d19f8373d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotNodeFilterAnalyzerInput.cs @@ -0,0 +1,14 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.expression.dot +{ + public interface ExprDotNodeFilterAnalyzerInput + { + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotNodeFilterAnalyzerInputExpr.cs b/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotNodeFilterAnalyzerInputExpr.cs new file mode 100755 index 000000000..abc4d7490 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotNodeFilterAnalyzerInputExpr.cs @@ -0,0 +1,14 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.expression.dot +{ + public class ExprDotNodeFilterAnalyzerInputExpr : ExprDotNodeFilterAnalyzerInput + { + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotNodeFilterAnalyzerInputProp.cs b/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotNodeFilterAnalyzerInputProp.cs new file mode 100755 index 000000000..301a93871 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotNodeFilterAnalyzerInputProp.cs @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.expression.dot +{ + public class ExprDotNodeFilterAnalyzerInputProp : ExprDotNodeFilterAnalyzerInput + { + public ExprDotNodeFilterAnalyzerInputProp(int streamNum, string propertyName) + { + StreamNum = streamNum; + PropertyName = propertyName; + } + + public int StreamNum { get; private set; } + + public string PropertyName { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotNodeFilterAnalyzerInputStatic.cs b/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotNodeFilterAnalyzerInputStatic.cs new file mode 100755 index 000000000..4feafdfe8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotNodeFilterAnalyzerInputStatic.cs @@ -0,0 +1,14 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.expression.dot +{ + public class ExprDotNodeFilterAnalyzerInputStatic : ExprDotNodeFilterAnalyzerInput + { + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotNodeFilterAnalyzerInputStream.cs b/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotNodeFilterAnalyzerInputStream.cs new file mode 100755 index 000000000..f93f0983e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotNodeFilterAnalyzerInputStream.cs @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.expression.dot +{ + public class ExprDotNodeFilterAnalyzerInputStream : ExprDotNodeFilterAnalyzerInput + { + public ExprDotNodeFilterAnalyzerInputStream(int streamNum) + { + StreamNum = streamNum; + } + + public int StreamNum { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotNodeImpl.cs b/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotNodeImpl.cs new file mode 100755 index 000000000..9ee51591c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotNodeImpl.cs @@ -0,0 +1,768 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.start; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.datetime.eval; +using com.espertech.esper.epl.enummethod.dot; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.visitor; +using com.espertech.esper.epl.rettype; +using com.espertech.esper.epl.variable; +using com.espertech.esper.events; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.dot +{ + /// + /// Represents an Dot-operator expression, for use when "(expression).Method(...).Method(...)" + /// + [Serializable] + public class ExprDotNodeImpl + : ExprNodeBase + , ExprDotNode + , ExprNodeInnerNodeProvider + , ExprStreamRefNode + { + private readonly IList _chainSpec; + private readonly bool _isDuckTyping; + private readonly bool _isUdfCache; + + [NonSerialized] private ExprEvaluator _exprEvaluator; + private bool _isReturnsConstantResult; + + [NonSerialized] private ExprDotNodeFilterAnalyzerDesc _exprDotNodeFilterAnalyzerDesc; + private int? _streamNumReferenced; + private string _rootPropertyName; + + public ExprDotNodeImpl(IEnumerable chainSpec, bool isDuckTyping, bool isUDFCache) + { + _chainSpec = new List(chainSpec); // for safety, copy the list + _isDuckTyping = isDuckTyping; + _isUdfCache = isUDFCache; + } + + public int? StreamReferencedIfAny + { + get + { + if (_exprEvaluator == null) + { + throw new IllegalStateException("Identifier expression has not been validated"); + } + return _streamNumReferenced; + } + } + + public string RootPropertyNameIfAny + { + get + { + if (_exprEvaluator == null) + { + throw new IllegalStateException("Identifier expression has not been validated"); + } + return _rootPropertyName; + } + } + + public override ExprNode Validate(ExprValidationContext validationContext) + { + // validate all parameters + ExprNodeUtility.Validate(ExprNodeOrigin.DOTNODEPARAMETER, _chainSpec, validationContext); + + // determine if there are enumeration method expressions in the chain + var hasEnumerationMethod = _chainSpec + .Any(chain => chain.Name.IsEnumerationMethod()); + + // determine if there is an implied binding, replace first chain element with evaluation node if there is + if (validationContext.StreamTypeService.HasTableTypes && + validationContext.TableService != null && + _chainSpec.Count > 1 && _chainSpec[0].IsProperty) + { + var tableNode = validationContext.TableService.GetTableNodeChainable( + validationContext.StreamTypeService, _chainSpec, validationContext.EngineImportService); + if (tableNode != null) + { + var node = ExprNodeUtility.GetValidatedSubtree( + ExprNodeOrigin.DOTNODE, tableNode.First, validationContext); + if (tableNode.Second.IsEmpty()) + { + return node; + } + _chainSpec.Clear(); + _chainSpec.AddAll(tableNode.Second); + AddChildNode(node); + } + } + + // The root node expression may provide the input value: + // Such as "Window(*).DoIt(...)" or "(select * from Window).DoIt()" or "Prevwindow(sb).DoIt(...)", in which case the expression to act on is a child expression + // + var streamTypeService = validationContext.StreamTypeService; + if (ChildNodes.Count != 0) + { + // the root expression is the first child node + var rootNode = ChildNodes[0]; + var rootNodeEvaluator = rootNode.ExprEvaluator; + + // the root expression may also provide a lambda-function input (Iterator) + // Determine collection-type and evaluator if any for root node + var enumSrc = ExprDotNodeUtility.GetEnumerationSource( + rootNode, validationContext.StreamTypeService, validationContext.EventAdapterService, + validationContext.StatementId, hasEnumerationMethod, + validationContext.IsDisablePropertyExpressionEventCollCache); + + EPType typeInfoX; + if (enumSrc.ReturnType == null) + { + typeInfoX = EPTypeHelper.SingleValue(rootNodeEvaluator.ReturnType); + // not a collection type, treat as scalar + } + else + { + typeInfoX = enumSrc.ReturnType; + } + + var evalsX = + ExprDotNodeUtility.GetChainEvaluators( + enumSrc.StreamOfProviderIfApplicable, typeInfoX, _chainSpec, validationContext, _isDuckTyping, + new ExprDotNodeFilterAnalyzerInputExpr()); + _exprEvaluator = new ExprDotEvalRootChild( + hasEnumerationMethod, this, rootNodeEvaluator, enumSrc.Enumeration, typeInfoX, evalsX.Chain, + evalsX.ChainWithUnpack, false); + return null; + } + + // No root node, and this is a 1-element chain i.e. "Something(param,...)". + // Plug-in single-row methods are not handled here. + // Plug-in aggregation methods are not handled here. + Pair propertyInfoPair; + if (_chainSpec.Count == 1) + { + var spec = _chainSpec[0]; + if (spec.Parameters.IsEmpty()) + { + throw HandleNotFound(spec.Name); + } + + // single-parameter can resolve to a property + propertyInfoPair = null; + try + { + propertyInfoPair = ExprIdentNodeUtil.GetTypeFromStream( + streamTypeService, spec.Name, streamTypeService.HasPropertyAgnosticType, false); + } + catch (ExprValidationPropertyException) + { + // fine + } + + // if not a property then try built-in single-row non-grammar functions + if (propertyInfoPair == null && + String.Equals( + spec.Name, EngineImportServiceConstants.EXT_SINGLEROW_FUNCTION_TRANSPOSE, + StringComparison.InvariantCultureIgnoreCase)) + { + if (spec.Parameters.Count != 1) + { + throw new ExprValidationException( + "The " + EngineImportServiceConstants.EXT_SINGLEROW_FUNCTION_TRANSPOSE + + " function requires a single parameter expression"); + } + _exprEvaluator = new ExprDotEvalTransposeAsStream(_chainSpec[0].Parameters[0].ExprEvaluator); + } + else if (spec.Parameters.Count != 1) + { + throw HandleNotFound(spec.Name); + } + else + { + if (propertyInfoPair == null) + { + throw new ExprValidationException( + "Unknown single-row function, aggregation function or mapped or indexed property named '" + + spec.Name + "' could not be resolved"); + } + _exprEvaluator = GetPropertyPairEvaluator( + spec.Parameters[0].ExprEvaluator, propertyInfoPair, validationContext); + _streamNumReferenced = propertyInfoPair.First.StreamNum; + } + return null; + } + + // handle the case where the first chain spec element is a stream name. + ExprValidationException prefixedStreamNumException = null; + var prefixedStreamNumber = PrefixedStreamName(_chainSpec, validationContext.StreamTypeService); + EPType typeInfo; + if (prefixedStreamNumber != -1) + { + + var specAfterStreamName = _chainSpec[1]; + + // Attempt to resolve as property + propertyInfoPair = null; + try + { + var propName = _chainSpec[0].Name + "." + specAfterStreamName.Name; + propertyInfoPair = ExprIdentNodeUtil.GetTypeFromStream( + streamTypeService, propName, streamTypeService.HasPropertyAgnosticType, false); + } + catch (ExprValidationPropertyException) + { + // fine + } + if (propertyInfoPair != null) + { + if (specAfterStreamName.Parameters.Count != 1) + { + throw HandleNotFound(specAfterStreamName.Name); + } + _exprEvaluator = GetPropertyPairEvaluator( + specAfterStreamName.Parameters[0].ExprEvaluator, propertyInfoPair, validationContext); + _streamNumReferenced = propertyInfoPair.First.StreamNum; + return null; + } + + // Attempt to resolve as event-underlying object instance method + var eventType = validationContext.StreamTypeService.EventTypes[prefixedStreamNumber]; + var type = eventType.UnderlyingType; + + var remainderChain = new List(_chainSpec); + remainderChain.RemoveAt(0); + + ExprValidationException methodEx = null; + ExprDotEval[] underlyingMethodChain = null; + try + { + typeInfo = EPTypeHelper.SingleValue(type); + underlyingMethodChain = + ExprDotNodeUtility.GetChainEvaluators( + prefixedStreamNumber, typeInfo, remainderChain, validationContext, false, + new ExprDotNodeFilterAnalyzerInputStream(prefixedStreamNumber)).ChainWithUnpack; + } + catch (ExprValidationException ex) + { + methodEx = ex; + // expected - may not be able to find the methods on the underlying + } + + ExprDotEval[] eventTypeMethodChain = null; + ExprValidationException enumDatetimeEx = null; + try + { + typeInfo = EPTypeHelper.SingleEvent(eventType); + var chain = ExprDotNodeUtility.GetChainEvaluators( + prefixedStreamNumber, typeInfo, remainderChain, validationContext, false, + new ExprDotNodeFilterAnalyzerInputStream(prefixedStreamNumber)); + eventTypeMethodChain = chain.ChainWithUnpack; + _exprDotNodeFilterAnalyzerDesc = chain.FilterAnalyzerDesc; + } + catch (ExprValidationException ex) + { + enumDatetimeEx = ex; + // expected - may not be able to find the methods on the underlying + } + + if (underlyingMethodChain != null) + { + _exprEvaluator = new ExprDotEvalStreamMethod(this, prefixedStreamNumber, underlyingMethodChain); + _streamNumReferenced = prefixedStreamNumber; + } + else if (eventTypeMethodChain != null) + { + _exprEvaluator = new ExprDotEvalStreamEventBean(this, prefixedStreamNumber, eventTypeMethodChain); + _streamNumReferenced = prefixedStreamNumber; + } + + if (_exprEvaluator != null) + { + return null; + } + else + { + if (ExprDotNodeUtility.IsDatetimeOrEnumMethod(remainderChain[0].Name)) + { + prefixedStreamNumException = enumDatetimeEx; + } + else + { + prefixedStreamNumException = + new ExprValidationException( + "Failed to solve '" + remainderChain[0].Name + + "' to either a date-time or enumeration method, an event property or a method on the event underlying object: " + + methodEx.Message, methodEx); + } + } + } + + // There no root node, in this case the classname or property name is provided as part of the chain. + // Such as "MyClass.MyStaticLib(...)" or "mycollectionproperty.DoIt(...)" + // + var modifiedChain = new List(_chainSpec); + var firstItem = modifiedChain.DeleteAt(0); + + propertyInfoPair = null; + try + { + propertyInfoPair = ExprIdentNodeUtil.GetTypeFromStream( + streamTypeService, firstItem.Name, streamTypeService.HasPropertyAgnosticType, true); + } + catch (ExprValidationPropertyException) + { + // not a property + } + + // If property then treat it as such + ExprDotNodeRealizedChain evals; + if (propertyInfoPair != null) + { + + var propertyName = propertyInfoPair.First.PropertyName; + var streamId = propertyInfoPair.First.StreamNum; + var streamType = streamTypeService.EventTypes[streamId]; + ExprEvaluatorEnumeration enumerationEval = null; + EPType inputType; + ExprEvaluator rootNodeEvaluator = null; + EventPropertyGetter getter; + + if (firstItem.Parameters.IsEmpty()) + { + getter = streamType.GetGetter(propertyInfoPair.First.PropertyName); + + var propertyEval = + ExprDotNodeUtility.GetPropertyEnumerationSource( + propertyInfoPair.First.PropertyName, streamId, streamType, hasEnumerationMethod, + validationContext.IsDisablePropertyExpressionEventCollCache); + typeInfo = propertyEval.ReturnType; + enumerationEval = propertyEval.Enumeration; + inputType = propertyEval.ReturnType; + rootNodeEvaluator = new PropertyExprEvaluatorNonLambda( + streamId, getter, propertyInfoPair.First.PropertyType); + } + else + { + // property with parameter - mapped or indexed property + var desc = + EventTypeUtility.GetNestablePropertyDescriptor( + streamTypeService.EventTypes[propertyInfoPair.First.StreamNum], firstItem.Name); + if (firstItem.Parameters.Count > 1) + { + throw new ExprValidationException( + "Property '" + firstItem.Name + "' may not be accessed passing 2 or more parameters"); + } + var paramEval = firstItem.Parameters[0].ExprEvaluator; + typeInfo = EPTypeHelper.SingleValue(desc.PropertyComponentType); + inputType = typeInfo; + getter = null; + if (desc.IsMapped) + { + if (paramEval.ReturnType != typeof (string)) + { + throw new ExprValidationException( + "Parameter expression to mapped property '" + propertyName + + "' is expected to return a string-type value but returns " + + paramEval.ReturnType.GetTypeNameFullyQualPretty()); + } + var mappedGetter = + propertyInfoPair.First.StreamEventType.GetGetterMapped(propertyInfoPair.First.PropertyName); + if (mappedGetter == null) + { + throw new ExprValidationException( + "Mapped property named '" + propertyName + "' failed to obtain getter-object"); + } + rootNodeEvaluator = new PropertyExprEvaluatorNonLambdaMapped( + streamId, mappedGetter, paramEval, desc.PropertyComponentType); + } + if (desc.IsIndexed) + { + if (paramEval.ReturnType.GetBoxedType() != typeof (int?)) + { + throw new ExprValidationException( + "Parameter expression to mapped property '" + propertyName + + "' is expected to return a int?-type value but returns " + + paramEval.ReturnType.GetTypeNameFullyQualPretty()); + } + var indexedGetter = + propertyInfoPair.First.StreamEventType.GetGetterIndexed(propertyInfoPair.First.PropertyName); + if (indexedGetter == null) + { + throw new ExprValidationException( + "Mapped property named '" + propertyName + "' failed to obtain getter-object"); + } + rootNodeEvaluator = new PropertyExprEvaluatorNonLambdaIndexed( + streamId, indexedGetter, paramEval, desc.PropertyComponentType); + } + } + if (typeInfo == null) + { + throw new ExprValidationException( + "Property '" + propertyName + "' is not a mapped or indexed property"); + } + + // try to build chain based on the input (non-fragment) + var filterAnalyzerInputProp = new ExprDotNodeFilterAnalyzerInputProp( + propertyInfoPair.First.StreamNum, propertyInfoPair.First.PropertyName); + var rootIsEventBean = false; + try + { + evals = ExprDotNodeUtility.GetChainEvaluators( + streamId, inputType, modifiedChain, validationContext, _isDuckTyping, filterAnalyzerInputProp); + } + catch (ExprValidationException) + { + + // try building the chain based on the fragment event type (i.e. A.After(B) based on A-configured start time where A is a fragment) + var fragment = propertyInfoPair.First.FragmentEventType; + if (fragment == null) + { + throw; + } + + EPType fragmentTypeInfo; + if (fragment.IsIndexed) + { + fragmentTypeInfo = EPTypeHelper.CollectionOfEvents(fragment.FragmentType); + } + else + { + fragmentTypeInfo = EPTypeHelper.SingleEvent(fragment.FragmentType); + } + + rootIsEventBean = true; + evals = ExprDotNodeUtility.GetChainEvaluators( + propertyInfoPair.First.StreamNum, fragmentTypeInfo, modifiedChain, validationContext, + _isDuckTyping, filterAnalyzerInputProp); + rootNodeEvaluator = new PropertyExprEvaluatorNonLambdaFragment( + streamId, getter, fragment.FragmentType.UnderlyingType); + } + + _exprEvaluator = new ExprDotEvalRootChild( + hasEnumerationMethod, this, rootNodeEvaluator, enumerationEval, inputType, evals.Chain, + evals.ChainWithUnpack, !rootIsEventBean); + _exprDotNodeFilterAnalyzerDesc = evals.FilterAnalyzerDesc; + _streamNumReferenced = propertyInfoPair.First.StreamNum; + _rootPropertyName = propertyInfoPair.First.PropertyName; + return null; + } + + // If variable then resolve as such + var contextNameVariable = validationContext.VariableService.IsContextVariable(firstItem.Name); + if (contextNameVariable != null) + { + throw new ExprValidationException("Method invocation on context-specific variable is not supported"); + } + var variableReader = validationContext.VariableService.GetReader( + firstItem.Name, EPStatementStartMethodConst.DEFAULT_AGENT_INSTANCE_ID); + if (variableReader != null) + { + EPType typeInfoX; + ExprDotStaticMethodWrap wrap; + if (variableReader.VariableMetaData.VariableType.IsArray) + { + typeInfoX = + EPTypeHelper.CollectionOfSingleValue(variableReader.VariableMetaData.VariableType.GetElementType()); + wrap = new ExprDotStaticMethodWrapArrayScalar( + variableReader.VariableMetaData.VariableName, + variableReader.VariableMetaData.VariableType.GetElementType()); + } + else if (variableReader.VariableMetaData.EventType != null) + { + typeInfoX = EPTypeHelper.SingleEvent(variableReader.VariableMetaData.EventType); + wrap = null; + } + else + { + typeInfoX = EPTypeHelper.SingleValue(variableReader.VariableMetaData.VariableType); + wrap = null; + } + + var evalsX = ExprDotNodeUtility.GetChainEvaluators( + null, typeInfoX, modifiedChain, validationContext, false, new ExprDotNodeFilterAnalyzerInputStatic()); + _exprEvaluator = new ExprDotEvalVariable(this, variableReader, wrap, evalsX.ChainWithUnpack); + return null; + } + + // try resolve as enumeration class with value + var enumconstant = TypeHelper.ResolveIdentAsEnumConst( + firstItem.Name, validationContext.EngineImportService, false); + if (enumconstant != null) + { + + // try resolve method + var methodSpec = modifiedChain[0]; + var enumvalue = firstItem.Name; + var handler = new ProxyExprNodeUtilResolveExceptionHandler + { + ProcHandle = ex => new ExprValidationException( + "Failed to resolve method '" + methodSpec.Name + + "' on enumeration value '" + enumvalue + "': " + ex.Message) + }; + var wildcardType = validationContext.StreamTypeService.EventTypes.Length != 1 + ? null + : validationContext.StreamTypeService.EventTypes[0]; + var methodDesc = + ExprNodeUtility.ResolveMethodAllowWildcardAndStream( + enumconstant.GetType().FullName, + enumconstant.GetType(), + methodSpec.Name, + methodSpec.Parameters, + validationContext.EngineImportService, validationContext.EventAdapterService, + validationContext.StatementId, wildcardType != null, wildcardType, handler, methodSpec.Name, + validationContext.TableService, streamTypeService.EngineURIQualifier); + + // method resolved, hook up + modifiedChain.RemoveAt(0); // we identified this piece + var optionalLambdaWrapX = + ExprDotStaticMethodWrapFactory.Make( + methodDesc.ReflectionMethod, validationContext.EventAdapterService, modifiedChain, null); + var typeInfoX = optionalLambdaWrapX != null + ? optionalLambdaWrapX.TypeInfo + : EPTypeHelper.SingleValue(methodDesc.FastMethod.ReturnType); + + var evalsX = ExprDotNodeUtility.GetChainEvaluators( + null, typeInfoX, modifiedChain, validationContext, false, new ExprDotNodeFilterAnalyzerInputStatic()); + _exprEvaluator = new ExprDotEvalStaticMethod( + validationContext.StatementName, firstItem.Name, + methodDesc.FastMethod, + methodDesc.ChildEvals, + false, optionalLambdaWrapX, evalsX.ChainWithUnpack, + false, enumconstant); + return null; + } + + // if prefixed by a stream name, we are giving up + if (prefixedStreamNumException != null) + { + throw prefixedStreamNumException; + } + + // If class then resolve as class + var secondItem = modifiedChain.DeleteAt(0); + + var allowWildcard = validationContext.StreamTypeService.EventTypes.Length == 1; + EventType streamZeroType = null; + if (validationContext.StreamTypeService.EventTypes.Length > 0) + { + streamZeroType = validationContext.StreamTypeService.EventTypes[0]; + } + + var method = ExprNodeUtility.ResolveMethodAllowWildcardAndStream( + firstItem.Name, null, secondItem.Name, secondItem.Parameters, validationContext.EngineImportService, + validationContext.EventAdapterService, validationContext.StatementId, allowWildcard, streamZeroType, + new ExprNodeUtilResolveExceptionHandlerDefault(firstItem.Name + "." + secondItem.Name, false), + secondItem.Name, validationContext.TableService, streamTypeService.EngineURIQualifier); + + var isConstantParameters = method.IsAllConstants && _isUdfCache; + _isReturnsConstantResult = isConstantParameters && modifiedChain.IsEmpty(); + + // this may return a pair of null if there is no lambda or the result cannot be wrapped for lambda-function use + var optionalLambdaWrap = ExprDotStaticMethodWrapFactory.Make( + method.ReflectionMethod, validationContext.EventAdapterService, modifiedChain, null); + typeInfo = optionalLambdaWrap != null + ? optionalLambdaWrap.TypeInfo + : EPTypeHelper.SingleValue(method.ReflectionMethod.ReturnType); + + evals = ExprDotNodeUtility.GetChainEvaluators( + null, typeInfo, modifiedChain, validationContext, false, new ExprDotNodeFilterAnalyzerInputStatic()); + _exprEvaluator = new ExprDotEvalStaticMethod( + validationContext.StatementName, firstItem.Name, method.FastMethod, method.ChildEvals, + isConstantParameters, optionalLambdaWrap, evals.ChainWithUnpack, false, null); + return null; + } + + public ExprDotNodeFilterAnalyzerDesc ExprDotNodeFilterAnalyzerDesc + { + get { return _exprDotNodeFilterAnalyzerDesc; } + } + + private ExprEvaluator GetPropertyPairEvaluator( + ExprEvaluator parameterEval, + Pair propertyInfoPair, + ExprValidationContext validationContext) + { + var propertyName = propertyInfoPair.First.PropertyName; + var propertyDesc = + EventTypeUtility.GetNestablePropertyDescriptor(propertyInfoPair.First.StreamEventType, propertyName); + if (propertyDesc == null || (!propertyDesc.IsMapped && !propertyDesc.IsIndexed)) + { + throw new ExprValidationException( + "Unknown single-row function, aggregation function or mapped or indexed property named '" + + propertyName + "' could not be resolved"); + } + + var streamNum = propertyInfoPair.First.StreamNum; + if (propertyDesc.IsMapped) + { + if (parameterEval.ReturnType != typeof (string)) + { + throw new ExprValidationException( + "Parameter expression to mapped property '" + propertyDesc.PropertyName + + "' is expected to return a string-type value but returns " + + parameterEval.ReturnType.GetTypeNameFullyQualPretty()); + } + var mappedGetter = + propertyInfoPair.First.StreamEventType.GetGetterMapped(propertyInfoPair.First.PropertyName); + if (mappedGetter == null) + { + throw new ExprValidationException( + "Mapped property named '" + propertyName + "' failed to obtain getter-object"); + } + return new ExprDotEvalPropertyExprMapped( + validationContext.StatementName, propertyDesc.PropertyName, streamNum, parameterEval, + propertyDesc.PropertyComponentType, mappedGetter); + } + else + { + if (parameterEval.ReturnType.GetBoxedType() != typeof(int?)) + { + throw new ExprValidationException( + "Parameter expression to indexed property '" + propertyDesc.PropertyName + + "' is expected to return a int-type value but returns " + + parameterEval.ReturnType.GetTypeNameFullyQualPretty()); + } + var indexedGetter = + propertyInfoPair.First.StreamEventType.GetGetterIndexed(propertyInfoPair.First.PropertyName); + if (indexedGetter == null) + { + throw new ExprValidationException( + "Indexed property named '" + propertyName + "' failed to obtain getter-object"); + } + return new ExprDotEvalPropertyExprIndexed( + validationContext.StatementName, propertyDesc.PropertyName, streamNum, parameterEval, + propertyDesc.PropertyComponentType, indexedGetter); + } + } + + private int PrefixedStreamName(IList chainSpec, StreamTypeService streamTypeService) + { + if (chainSpec.Count < 1) + { + return -1; + } + var spec = chainSpec[0]; + if (spec.Parameters.Count > 0 && !spec.IsProperty) + { + return -1; + } + return streamTypeService.GetStreamNumForStreamName(spec.Name); + } + + public override void Accept(ExprNodeVisitor visitor) + { + base.Accept(visitor); + ExprNodeUtility.AcceptChain(visitor, _chainSpec); + } + + public override void Accept(ExprNodeVisitorWithParent visitor) + { + base.Accept(visitor); + ExprNodeUtility.AcceptChain(visitor, _chainSpec); + } + + public override void AcceptChildnodes(ExprNodeVisitorWithParent visitor, ExprNode parent) + { + base.AcceptChildnodes(visitor, parent); + ExprNodeUtility.AcceptChain(visitor, _chainSpec, this); + } + + public override void ReplaceUnlistedChildNode(ExprNode nodeToReplace, ExprNode newNode) + { + ExprNodeUtility.ReplaceChainChildNode(nodeToReplace, newNode, _chainSpec); + } + + public IList ChainSpec + { + get { return _chainSpec; } + } + + public override ExprEvaluator ExprEvaluator + { + get { return _exprEvaluator; } + } + + public override bool IsConstantResult + { + get { return _isReturnsConstantResult; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + if (ChildNodes.Count != 0) + { + writer.Write(ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(ChildNodes[0])); + } + ExprNodeUtility.ToExpressionString(_chainSpec, writer, ChildNodes.Count != 0, null); + } + + public override ExprPrecedenceEnum Precedence + { + get { return ExprPrecedenceEnum.MINIMUM; } + } + + public IDictionary EventType + { + get { return null; } + } + + public override bool EqualsNode(ExprNode node) + { + var other = node as ExprDotNodeImpl; + if (other == null) + { + return false; + } + + if (other._chainSpec.Count != _chainSpec.Count) + { + return false; + } + for (var i = 0; i < _chainSpec.Count; i++) + { + if (!Equals(_chainSpec[i], other._chainSpec[i])) + { + return false; + } + } + return true; + } + + public IList AdditionalNodes + { + get { return ExprNodeUtility.CollectChainParameters(_chainSpec); } + } + + public string IsVariableOpGetName(VariableService variableService) + { + VariableMetaData metaData = null; + if (_chainSpec.Count > 0 && _chainSpec[0].IsProperty) + { + metaData = variableService.GetVariableMetaData(_chainSpec[0].Name); + } + return metaData == null ? null : metaData.VariableName; + } + + private ExprValidationException HandleNotFound(string name) + { + return + new ExprValidationException( + "Unknown single-row function, expression declaration, script or aggregation function named '" + name + + "' could not be resolved"); + } + } + +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotNodeRealizedChain.cs b/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotNodeRealizedChain.cs new file mode 100755 index 000000000..c0af68f83 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotNodeRealizedChain.cs @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.datetime.eval; + +namespace com.espertech.esper.epl.expression.dot +{ + public class ExprDotNodeRealizedChain + { + public ExprDotEval[] Chain { get; set; } + public ExprDotEval[] ChainWithUnpack { get; set; } + public ExprDotNodeFilterAnalyzerDesc FilterAnalyzerDesc { get; set; } + + /// + /// Initializes a new instance of the class. + /// + /// The chain. + /// The chain with unpack. + /// The filter analyzer desc. + public ExprDotNodeRealizedChain(ExprDotEval[] chain, ExprDotEval[] chainWithUnpack, ExprDotNodeFilterAnalyzerDesc filterAnalyzerDesc) + { + Chain = chain; + ChainWithUnpack = chainWithUnpack; + FilterAnalyzerDesc = filterAnalyzerDesc; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotNodeUtility.cs b/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotNodeUtility.cs new file mode 100755 index 000000000..febc7aaa6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/dot/ExprDotNodeUtility.cs @@ -0,0 +1,490 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.datetime.eval; +using com.espertech.esper.epl.enummethod.dot; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.rettype; +using com.espertech.esper.events; +using com.espertech.esper.events.arr; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.dot +{ + public class ExprDotNodeUtility + { + public static bool IsDatetimeOrEnumMethod(string name) + { + return + name.IsEnumerationMethod() || + name.IsDateTimeMethod(); + } + + public static ExprDotNodeRealizedChain GetChainEvaluators( + int? streamOfProviderIfApplicable, + EPType inputType, + IList chainSpec, + ExprValidationContext validationContext, + bool isDuckTyping, + ExprDotNodeFilterAnalyzerInput inputDesc) + { + var methodEvals = new List(); + var currentInputType = inputType; + EnumMethodEnum? lastLambdaFunc = null; + var lastElement = chainSpec.IsEmpty() ? null : chainSpec[chainSpec.Count - 1]; + ExprDotNodeFilterAnalyzerDesc filterAnalyzerDesc = null; + + var chainSpecStack = new ArrayDeque(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)))) + { + var enumerationMethod = EnumMethodEnumExtensions.FromName(chainElement.Name); + var eval = TypeHelper.Instantiate(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); + } + + private static Type GetMethodTarget(EPType currentInputType) + { + if (currentInputType is ClassEPType) + { + return ((ClassEPType)currentInputType).Clazz; + } + else if (currentInputType is EventEPType) + { + return ((EventEPType)currentInputType).EventType.UnderlyingType; + } + return null; + } + + public static ObjectArrayEventType MakeTransientOAType(string enumMethod, string propertyName, Type type, EventAdapterService eventAdapterService) + { + var propsResult = new Dictionary(); + propsResult.Put(propertyName, type); + var typeName = enumMethod + "__" + propertyName; + return new ObjectArrayEventType( + EventTypeMetadata.CreateAnonymous(typeName, ApplicationType.OBJECTARR), typeName, 0, eventAdapterService, + propsResult, + null, + null, + null); + } + + public static EventType[] GetSingleLambdaParamEventType(string enumMethodUsedName, IList goesToNames, EventType inputEventType, Type collectionComponentType, EventAdapterService eventAdapterService) + { + if (inputEventType != null) + { + return new EventType[] { inputEventType }; + } + else + { + return new EventType[] { ExprDotNodeUtility.MakeTransientOAType(enumMethodUsedName, goesToNames[0], collectionComponentType, eventAdapterService) }; + } + } + + public static object EvaluateChain(ExprDotEval[] evaluators, object inner, EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + if (InstrumentationHelper.ENABLED) + { + var i = -1; + foreach (var methodEval in evaluators) + { + i++; + InstrumentationHelper.Get().QExprDotChainElement(i, methodEval); + inner = methodEval.Evaluate(inner, new EvaluateParams(eventsPerStream, isNewData, context)); + InstrumentationHelper.Get().AExprDotChainElement(methodEval.TypeInfo, inner); + if (inner == null) + { + break; + } + } + return inner; + } + else + { + foreach (var methodEval in evaluators) + { + inner = methodEval.Evaluate(inner, new EvaluateParams(eventsPerStream, isNewData, context)); + if (inner == null) + { + break; + } + } + return inner; + } + } + + public static object EvaluateChainWithWrap(ExprDotStaticMethodWrap resultWrapLambda, + object result, + EventType optionalResultSingleEventType, + Type resultType, + ExprDotEval[] chainEval, + EventBean[] eventsPerStream, + bool newData, + ExprEvaluatorContext exprEvaluatorContext) + { + if (result == null) + { + return null; + } + + if (resultWrapLambda != null) + { + result = resultWrapLambda.Convert(result); + } + + var evaluateParams = new EvaluateParams(eventsPerStream, newData, exprEvaluatorContext); + + if (InstrumentationHelper.ENABLED) + { + EPType typeInfo; + if (resultWrapLambda != null) + { + typeInfo = resultWrapLambda.TypeInfo; + } + else + { + if (optionalResultSingleEventType != null) + { + typeInfo = EPTypeHelper.SingleEvent(optionalResultSingleEventType); + } + else + { + typeInfo = EPTypeHelper.SingleValue(resultType); + } + } + InstrumentationHelper.Get().QExprDotChain(typeInfo, result, chainEval); + + var i = -1; + foreach (var aChainEval in chainEval) + { + i++; + InstrumentationHelper.Get().QExprDotChainElement(i, aChainEval); + result = aChainEval.Evaluate(result, evaluateParams); + InstrumentationHelper.Get().AExprDotChainElement(aChainEval.TypeInfo, result); + if (result == null) + { + break; + } + } + + InstrumentationHelper.Get().AExprDotChain(); + return result; + } + + foreach (var aChainEval in chainEval) + { + result = aChainEval.Evaluate(result, evaluateParams); + if (result == null) + { + return result; + } + } + return result; + } + + public static ExprDotEnumerationSource GetEnumerationSource(ExprNode inputExpression, StreamTypeService streamTypeService, EventAdapterService eventAdapterService, int statementId, bool hasEnumerationMethod, bool disablePropertyExpressionEventCollCache) + { + var rootNodeEvaluator = inputExpression.ExprEvaluator; + ExprEvaluatorEnumeration rootLambdaEvaluator = null; + EPType info = null; + + if (rootNodeEvaluator is ExprEvaluatorEnumeration) + { + rootLambdaEvaluator = (ExprEvaluatorEnumeration)rootNodeEvaluator; + + if (rootLambdaEvaluator.GetEventTypeCollection(eventAdapterService, statementId) != null) + { + info = EPTypeHelper.CollectionOfEvents(rootLambdaEvaluator.GetEventTypeCollection(eventAdapterService, statementId)); + } + else if (rootLambdaEvaluator.GetEventTypeSingle(eventAdapterService, statementId) != null) + { + info = EPTypeHelper.SingleEvent(rootLambdaEvaluator.GetEventTypeSingle(eventAdapterService, statementId)); + } + else if (rootLambdaEvaluator.ComponentTypeCollection != null) + { + info = EPTypeHelper.CollectionOfSingleValue(rootLambdaEvaluator.ComponentTypeCollection); + } + else + { + rootLambdaEvaluator = null; // not a lambda evaluator + } + } + else if (inputExpression is ExprIdentNode) + { + var identNode = (ExprIdentNode)inputExpression; + var streamId = identNode.StreamId; + var streamType = streamTypeService.EventTypes[streamId]; + return GetPropertyEnumerationSource(identNode.ResolvedPropertyName, streamId, streamType, hasEnumerationMethod, disablePropertyExpressionEventCollCache); + } + return new ExprDotEnumerationSource(info, null, rootLambdaEvaluator); + } + + public static ExprDotEnumerationSourceForProps GetPropertyEnumerationSource(string propertyName, int streamId, EventType streamType, bool allowEnumType, bool disablePropertyExpressionEventCollCache) + { + var propertyType = streamType.GetPropertyType(propertyName); + var typeInfo = EPTypeHelper.SingleValue(propertyType); // assume scalar for now + + // no enumeration methods, no need to expose as an enumeration + if (!allowEnumType) + { + return new ExprDotEnumerationSourceForProps(null, typeInfo, streamId, null); + } + + var fragmentEventType = streamType.GetFragmentType(propertyName); + var getter = streamType.GetGetter(propertyName); + + ExprEvaluatorEnumeration enumEvaluator = null; + if (getter != null && fragmentEventType != null) + { + if (fragmentEventType.IsIndexed) + { + enumEvaluator = new PropertyExprEvaluatorEventCollection(propertyName, streamId, fragmentEventType.FragmentType, getter, disablePropertyExpressionEventCollCache); + typeInfo = EPTypeHelper.CollectionOfEvents(fragmentEventType.FragmentType); + } + else + { // we don't want native to require an eventbean instance + enumEvaluator = new PropertyExprEvaluatorEventSingle(streamId, fragmentEventType.FragmentType, getter); + typeInfo = EPTypeHelper.SingleEvent(fragmentEventType.FragmentType); + } + } + else + { + var desc = EventTypeUtility.GetNestablePropertyDescriptor(streamType, propertyName); + if (desc != null && desc.IsIndexed && !desc.RequiresIndex && desc.PropertyComponentType != null) + { + if (propertyType.IsGenericCollection()) + { + enumEvaluator = new PropertyExprEvaluatorScalarCollection(propertyName, streamId, getter, desc.PropertyComponentType); + } + else if (propertyType.IsImplementsInterface(typeof(System.Collections.IEnumerable))) + { + enumEvaluator = new PropertyExprEvaluatorScalarIterable(propertyName, streamId, getter, desc.PropertyComponentType); + } + else if (propertyType.IsArray) + { + enumEvaluator = new PropertyExprEvaluatorScalarArray(propertyName, streamId, getter, desc.PropertyComponentType); + } + else + { + throw new IllegalStateException("Property indicated indexed-type but failed to find proper collection adapter for use with enumeration methods"); + } + typeInfo = EPTypeHelper.CollectionOfSingleValue(desc.PropertyComponentType); + } + } + var enumEvaluatorGivenEvent = (ExprEvaluatorEnumerationGivenEvent)enumEvaluator; + return new ExprDotEnumerationSourceForProps(enumEvaluator, typeInfo, streamId, enumEvaluatorGivenEvent); + } + + private static ExprNodeUtilMethodDesc GetValidateMethodDescriptor(Type methodTarget, string methodName, IList parameters, ExprValidationContext validationContext) + { + ExprNodeUtilResolveExceptionHandler exceptionHandler = new ProxyExprNodeUtilResolveExceptionHandler + { + ProcHandle = e => new ExprValidationException("Failed to resolve method '" + methodName + "': " + e.Message, e), + }; + var wildcardType = validationContext.StreamTypeService.EventTypes.Length != 1 ? null : validationContext.StreamTypeService.EventTypes[0]; + return ExprNodeUtility.ResolveMethodAllowWildcardAndStream( + methodTarget.Name, methodTarget, methodName, parameters, validationContext.EngineImportService, + validationContext.EventAdapterService, validationContext.StatementId, wildcardType != null, wildcardType, + exceptionHandler, methodName, validationContext.TableService, + validationContext.StreamTypeService.EngineURIQualifier); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/dot/inner/InnerEvaluatorArrObjectToColl.cs b/NEsper.Core/NEsper.Core/epl/expression/dot/inner/InnerEvaluatorArrObjectToColl.cs new file mode 100755 index 000000000..443e492af --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/dot/inner/InnerEvaluatorArrObjectToColl.cs @@ -0,0 +1,69 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.rettype; + +namespace com.espertech.esper.epl.expression.dot.inner +{ + public class InnerEvaluatorArrObjectToColl : ExprDotEvalRootChildInnerEval + { + private readonly ExprEvaluator _rootEvaluator; + + public InnerEvaluatorArrObjectToColl(ExprEvaluator rootEvaluator) + { + _rootEvaluator = rootEvaluator; + } + + public object Evaluate(EvaluateParams evaluateParams) + { + var array = _rootEvaluator.Evaluate(evaluateParams); + if (array == null) { + return null; + } + return array.UnwrapIntoArray(); + } + + public ICollection EvaluateGetROCollectionEvents(EvaluateParams evaluateParams) { + return null; + } + + public ICollection EvaluateGetROCollectionScalar(EvaluateParams evaluateParams) { + return null; + } + + public EventType EventTypeCollection + { + get { return null; } + } + + public Type ComponentTypeCollection + { + get { return null; } + } + + public EventBean EvaluateGetEventBean(EvaluateParams evaluateParams) { + return null; + } + + public EventType EventTypeSingle + { + get { return null; } + } + + public EPType TypeInfo + { + get { return EPTypeHelper.CollectionOfSingleValue(_rootEvaluator.ReturnType.GetElementType()); } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/dot/inner/InnerEvaluatorArrPrimitiveToColl.cs b/NEsper.Core/NEsper.Core/epl/expression/dot/inner/InnerEvaluatorArrPrimitiveToColl.cs new file mode 100755 index 000000000..632436ab2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/dot/inner/InnerEvaluatorArrPrimitiveToColl.cs @@ -0,0 +1,79 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.rettype; + +namespace com.espertech.esper.epl.expression.dot.inner +{ + public class InnerEvaluatorArrPrimitiveToColl : ExprDotEvalRootChildInnerEval + { + private readonly ExprEvaluator _rootEvaluator; + + public InnerEvaluatorArrPrimitiveToColl(ExprEvaluator rootEvaluator) + { + _rootEvaluator = rootEvaluator; + } + + public object Evaluate(EvaluateParams evaluateParams) + { + var array = _rootEvaluator.Evaluate(evaluateParams) as Array; + if (array == null) { + return null; + } + + var len = array.Length; + if (len == 0) { + return Collections.GetEmptyList(); + } + if (len == 1) + { + return Collections.SingletonList(array.GetValue(0)); + } + + return array.Unwrap(); + } + + public ICollection EvaluateGetROCollectionEvents(EvaluateParams evaluateParams) { + return null; + } + + public ICollection EvaluateGetROCollectionScalar(EvaluateParams evaluateParams) { + return null; + } + + public EventType EventTypeCollection + { + get { return null; } + } + + public Type ComponentTypeCollection + { + get { return null; } + } + + public EventBean EvaluateGetEventBean(EvaluateParams evaluateParams) { + return null; + } + + public EventType EventTypeSingle + { + get { return null; } + } + + public EPType TypeInfo + { + get { return EPTypeHelper.CollectionOfSingleValue(_rootEvaluator.ReturnType.GetElementType()); } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/dot/inner/InnerEvaluatorColl.cs b/NEsper.Core/NEsper.Core/epl/expression/dot/inner/InnerEvaluatorColl.cs new file mode 100755 index 000000000..de459a498 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/dot/inner/InnerEvaluatorColl.cs @@ -0,0 +1,65 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.rettype; + +namespace com.espertech.esper.epl.expression.dot.inner +{ + public class InnerEvaluatorColl : ExprDotEvalRootChildInnerEval + { + private readonly ExprEvaluator _rootEvaluator; + + public InnerEvaluatorColl(ExprEvaluator rootEvaluator) + { + _rootEvaluator = rootEvaluator; + } + + public object Evaluate(EvaluateParams evaluateParams) + { + return _rootEvaluator.Evaluate(evaluateParams); + } + + public ICollection EvaluateGetROCollectionEvents(EvaluateParams evaluateParams) { + return null; + } + + public ICollection EvaluateGetROCollectionScalar(EvaluateParams evaluateParams) + { + return null; + } + + public EventType EventTypeCollection + { + get { return null; } + } + + public Type ComponentTypeCollection + { + get { return null; } + } + + public EventBean EvaluateGetEventBean(EvaluateParams evaluateParams) { + return null; + } + + public EventType EventTypeSingle + { + get { return null; } + } + + public EPType TypeInfo + { + get { return EPTypeHelper.CollectionOfSingleValue(typeof (object)); } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/dot/inner/InnerEvaluatorEnumerableEventBean.cs b/NEsper.Core/NEsper.Core/epl/expression/dot/inner/InnerEvaluatorEnumerableEventBean.cs new file mode 100755 index 000000000..1a9c80c6e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/dot/inner/InnerEvaluatorEnumerableEventBean.cs @@ -0,0 +1,69 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.rettype; + +namespace com.espertech.esper.epl.expression.dot.inner +{ + public class InnerEvaluatorEnumerableEventBean : ExprDotEvalRootChildInnerEval + { + private readonly ExprEvaluatorEnumeration _rootLambdaEvaluator; + private readonly EventType _eventType; + + public InnerEvaluatorEnumerableEventBean(ExprEvaluatorEnumeration rootLambdaEvaluator, EventType eventType) + { + _rootLambdaEvaluator = rootLambdaEvaluator; + _eventType = eventType; + } + + public object Evaluate(EvaluateParams evaluateParams) + { + return _rootLambdaEvaluator.EvaluateGetEventBean(evaluateParams); + } + + public ICollection EvaluateGetROCollectionEvents(EvaluateParams evaluateParams) + { + return _rootLambdaEvaluator.EvaluateGetROCollectionEvents(evaluateParams); + } + + public ICollection EvaluateGetROCollectionScalar(EvaluateParams evaluateParams) + { + return _rootLambdaEvaluator.EvaluateGetROCollectionScalar(evaluateParams); + } + + public EventType EventTypeCollection + { + get { return null; } + } + + public Type ComponentTypeCollection + { + get { return null; } + } + + public EventBean EvaluateGetEventBean(EvaluateParams evaluateParams) + { + return _rootLambdaEvaluator.EvaluateGetEventBean(evaluateParams); + } + + public EventType EventTypeSingle + { + get { return _eventType; } + } + + public EPType TypeInfo + { + get { return EPTypeHelper.SingleEvent(_eventType); } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/dot/inner/InnerEvaluatorEnumerableEventCollection.cs b/NEsper.Core/NEsper.Core/epl/expression/dot/inner/InnerEvaluatorEnumerableEventCollection.cs new file mode 100755 index 000000000..09191ede7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/dot/inner/InnerEvaluatorEnumerableEventCollection.cs @@ -0,0 +1,70 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.rettype; + +namespace com.espertech.esper.epl.expression.dot.inner +{ + public class InnerEvaluatorEnumerableEventCollection : ExprDotEvalRootChildInnerEval + { + private readonly ExprEvaluatorEnumeration _rootLambdaEvaluator; + private readonly EventType _eventType; + + public InnerEvaluatorEnumerableEventCollection(ExprEvaluatorEnumeration rootLambdaEvaluator, EventType eventType) + { + _rootLambdaEvaluator = rootLambdaEvaluator; + _eventType = eventType; + } + + public object Evaluate(EvaluateParams evaluateParams) + { + return _rootLambdaEvaluator.EvaluateGetROCollectionEvents(evaluateParams); + } + + public ICollection EvaluateGetROCollectionEvents(EvaluateParams evaluateParams) + { + return _rootLambdaEvaluator.EvaluateGetROCollectionEvents(evaluateParams); + } + + public ICollection EvaluateGetROCollectionScalar(EvaluateParams evaluateParams) + { + return _rootLambdaEvaluator.EvaluateGetROCollectionEvents(evaluateParams).Unwrap(); + } + + public EventType EventTypeCollection + { + get { return _eventType; } + } + + public Type ComponentTypeCollection + { + get { return null; } + } + + public EventBean EvaluateGetEventBean(EvaluateParams evaluateParams) + { + return null; + } + + public EventType EventTypeSingle + { + get { return null; } + } + + public EPType TypeInfo + { + get { return EPTypeHelper.CollectionOfEvents(_eventType); } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/dot/inner/InnerEvaluatorEnumerableScalarCollection.cs b/NEsper.Core/NEsper.Core/epl/expression/dot/inner/InnerEvaluatorEnumerableScalarCollection.cs new file mode 100755 index 000000000..a38d79bf5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/dot/inner/InnerEvaluatorEnumerableScalarCollection.cs @@ -0,0 +1,67 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.rettype; + +namespace com.espertech.esper.epl.expression.dot.inner +{ + public class InnerEvaluatorEnumerableScalarCollection : ExprDotEvalRootChildInnerEval + { + private readonly ExprEvaluatorEnumeration _rootLambdaEvaluator; + private readonly Type _componentType; + + public InnerEvaluatorEnumerableScalarCollection(ExprEvaluatorEnumeration rootLambdaEvaluator, Type componentType) + { + _rootLambdaEvaluator = rootLambdaEvaluator; + _componentType = componentType; + } + + public object Evaluate(EvaluateParams evaluateParams) + { + return _rootLambdaEvaluator.EvaluateGetROCollectionScalar(evaluateParams); + } + + public ICollection EvaluateGetROCollectionEvents(EvaluateParams evaluateParams) { + return _rootLambdaEvaluator.EvaluateGetROCollectionEvents(evaluateParams); + } + + public ICollection EvaluateGetROCollectionScalar(EvaluateParams evaluateParams) + { + return _rootLambdaEvaluator.EvaluateGetROCollectionScalar(evaluateParams); + } + + public EventType EventTypeCollection + { + get { return null; } + } + + public Type ComponentTypeCollection + { + get { return _componentType; } + } + + public EventBean EvaluateGetEventBean(EvaluateParams evaluateParams) { + return null; + } + + public EventType EventTypeSingle + { + get { return null; } + } + + public EPType TypeInfo + { + get { return EPTypeHelper.CollectionOfSingleValue(_componentType); } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/dot/inner/InnerEvaluatorScalar.cs b/NEsper.Core/NEsper.Core/epl/expression/dot/inner/InnerEvaluatorScalar.cs new file mode 100755 index 000000000..e5679eef1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/dot/inner/InnerEvaluatorScalar.cs @@ -0,0 +1,64 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.rettype; + +namespace com.espertech.esper.epl.expression.dot.inner +{ + public class InnerEvaluatorScalar : ExprDotEvalRootChildInnerEval + { + private readonly ExprEvaluator _rootEvaluator; + + public InnerEvaluatorScalar(ExprEvaluator rootEvaluator) { + _rootEvaluator = rootEvaluator; + } + + public object Evaluate(EvaluateParams evaluateParams) + { + return _rootEvaluator.Evaluate(evaluateParams); + } + + public ICollection EvaluateGetROCollectionEvents(EvaluateParams evaluateParams) { + return null; + } + + public ICollection EvaluateGetROCollectionScalar(EvaluateParams evaluateParams) + { + return null; + } + + public EventType EventTypeCollection + { + get { return null; } + } + + public Type ComponentTypeCollection + { + get { return null; } + } + + public EventBean EvaluateGetEventBean(EvaluateParams evaluateParams) { + return null; + } + + public EventType EventTypeSingle + { + get { return null; } + } + + public EPType TypeInfo + { + get { return EPTypeHelper.SingleValue(_rootEvaluator.ReturnType); } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/dot/inner/InnerEvaluatorScalarUnpackEvent.cs b/NEsper.Core/NEsper.Core/epl/expression/dot/inner/InnerEvaluatorScalarUnpackEvent.cs new file mode 100755 index 000000000..0cd6fb85f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/dot/inner/InnerEvaluatorScalarUnpackEvent.cs @@ -0,0 +1,68 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.rettype; + +namespace com.espertech.esper.epl.expression.dot.inner +{ + public class InnerEvaluatorScalarUnpackEvent : ExprDotEvalRootChildInnerEval + { + private readonly ExprEvaluator _rootEvaluator; + + public InnerEvaluatorScalarUnpackEvent(ExprEvaluator rootEvaluator) { + _rootEvaluator = rootEvaluator; + } + + public object Evaluate(EvaluateParams evaluateParams) + { + object target = _rootEvaluator.Evaluate(evaluateParams); + if (target is EventBean) { + return ((EventBean) target).Underlying; + } + return target; + } + + public ICollection EvaluateGetROCollectionEvents(EvaluateParams evaluateParams) { + return null; + } + + public ICollection EvaluateGetROCollectionScalar(EvaluateParams evaluateParams) + { + return null; + } + + public EventType EventTypeCollection + { + get { return null; } + } + + public Type ComponentTypeCollection + { + get { return null; } + } + + public EventBean EvaluateGetEventBean(EvaluateParams evaluateParams) { + return null; + } + + public EventType EventTypeSingle + { + get { return null; } + } + + public EPType TypeInfo + { + get { return EPTypeHelper.SingleValue(_rootEvaluator.ReturnType); } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/funcs/ExprCaseNode.cs b/NEsper.Core/NEsper.Core/epl/expression/funcs/ExprCaseNode.cs new file mode 100755 index 000000000..a9a779db3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/funcs/ExprCaseNode.cs @@ -0,0 +1,542 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events.map; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.funcs +{ + using DataMap = IDictionary; + + /// + /// Represents the case-when-then-else control flow function is an expression tree. + /// + [Serializable] + public class ExprCaseNode + : ExprNodeBase + , ExprEvaluator + , ExprEvaluatorTypableReturn + { + private readonly bool _isCase2; + private Type _resultType; + [NonSerialized] + private DataMap _mapResultType; + private bool _isNumericResult; + private bool _mustCoerce; + + [NonSerialized] + private Coercer _coercer; + [NonSerialized] + private IList> _whenThenNodeList; + [NonSerialized] + private ExprEvaluator _optionalCompareExprNode; + [NonSerialized] + private ExprEvaluator _optionalElseExprNode; + + /// + /// Ctor. + /// + /// is an indicator of which Case statement we are working on. + /// True indicates a 'Case2' statement with syntax "case a when a1 then b1 else b2". + /// False indicates a 'Case1' statement with syntax "case when a=a1 then b1 else b2". + /// + public ExprCaseNode(bool isCase2) + { + _isCase2 = isCase2; + } + + public override ExprEvaluator ExprEvaluator + { + get { return this; } + } + + /// Returns true if this is a switch-type case. + /// true for switch-type case, or false for when-then type + public bool IsCase2 + { + get { return _isCase2; } + } + + public override ExprNode Validate(ExprValidationContext validationContext) + { + CaseAnalysis analysis = AnalyzeCase(); + + _whenThenNodeList = new List>(); + foreach (UniformPair pair in analysis.WhenThenNodeList) + { + if (!_isCase2) + { + if (pair.First.ExprEvaluator.ReturnType.GetBoxedType() != typeof(bool?)) + { + throw new ExprValidationException("Case node 'when' expressions must return a boolean value"); + } + } + _whenThenNodeList.Add(new UniformPair(pair.First.ExprEvaluator, pair.Second.ExprEvaluator)); + } + if (analysis.OptionalCompareExprNode != null) + { + _optionalCompareExprNode = analysis.OptionalCompareExprNode.ExprEvaluator; + } + if (analysis.OptionalElseExprNode != null) + { + _optionalElseExprNode = analysis.OptionalElseExprNode.ExprEvaluator; + } + + if (_isCase2) + { + ValidateCaseTwo(); + } + + // Determine type of each result (then-node and else node) child node expression + IList childTypes = new List(); + IList> childMapTypes = new List>(); + foreach (UniformPair pair in _whenThenNodeList) + { + if (pair.Second is ExprEvaluatorTypableReturn) + { + var typableReturn = (ExprEvaluatorTypableReturn)pair.Second; + var rowProps = typableReturn.RowProperties; + if (rowProps != null) + { + childMapTypes.Add(rowProps); + continue; + } + } + childTypes.Add(pair.Second.ReturnType); + + } + if (_optionalElseExprNode != null) + { + if (_optionalElseExprNode is ExprEvaluatorTypableReturn) + { + var typableReturn = (ExprEvaluatorTypableReturn)_optionalElseExprNode; + var rowProps = typableReturn.RowProperties; + if (rowProps != null) + { + childMapTypes.Add(rowProps); + } + else + { + childTypes.Add(_optionalElseExprNode.ReturnType); + } + } + else + { + childTypes.Add(_optionalElseExprNode.ReturnType); + } + } + + if (!childMapTypes.IsEmpty() && !childTypes.IsEmpty()) + { + String message = "Case node 'when' expressions require that all results either return a single value or a Map-type (new-operator) value"; + String check; + int count = -1; + foreach (UniformPair pair in _whenThenNodeList) + { + count++; + if (pair.Second.ReturnType != typeof(DataMap) && pair.Second.ReturnType != null) + { + check = ", check when-condition number " + count; + throw new ExprValidationException(message + check); + } + } + if (_optionalElseExprNode != null) + { + if (_optionalElseExprNode.ReturnType != typeof(DataMap) && _optionalElseExprNode.ReturnType != null) + { + check = ", check the else-condition"; + throw new ExprValidationException(message + check); + } + } + throw new ExprValidationException(message); + } + + if (childMapTypes.IsEmpty()) + { + // Determine common denominator type + try + { + _resultType = TypeHelper.GetCommonCoercionType(childTypes); + if (_resultType.IsNumeric()) + { + _isNumericResult = true; + } + } + catch (CoercionException ex) + { + throw new ExprValidationException("Implicit conversion not allowed: " + ex.Message); + } + } + else + { + _mapResultType = childMapTypes[0]; + for (int i = 1; i < childMapTypes.Count; i++) + { + DataMap other = childMapTypes[i]; + String messageEquals = MapEventType.IsDeepEqualsProperties("Case-when number " + i, _mapResultType, other); + if (messageEquals != null) + { + throw new ExprValidationException("Incompatible case-when return types by new-operator in case-when number " + i + ": " + messageEquals); + } + } + } + + return null; + } + + public override bool IsConstantResult + { + get { return false; } + } + + public Type ReturnType + { + get { return _resultType; } + } + + public IDictionary RowProperties + { + get { return _mapResultType; } + } + + public object Evaluate(EvaluateParams evaluateParams) + { + var result = new Mutable(); + + Instrument.With( + i => i.QExprCase(this), + i => i.AExprCase(result.Value), + () => + { + if (!_isCase2) + { + result.Value = EvaluateCaseSyntax1( + evaluateParams.EventsPerStream, + evaluateParams.IsNewData, + evaluateParams.ExprEvaluatorContext); + } + else + { + result.Value = EvaluateCaseSyntax2( + evaluateParams.EventsPerStream, + evaluateParams.IsNewData, + evaluateParams.ExprEvaluatorContext); + } + }); + + return result.Value; + } + + public bool? IsMultirow + { + get + { + if (_mapResultType == null) + return null; + return false; + } + } + + public Object[] EvaluateTypableSingle(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + var map = (IDictionary)Evaluate(new EvaluateParams(eventsPerStream, isNewData, context)); + var row = new Object[map.Count]; + int index = -1; + foreach (var entry in _mapResultType) + { + index++; + row[index] = map.Get(entry.Key); + } + return row; + } + + public Object[][] EvaluateTypableMulti(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + return null; // always single-row + } + + public override bool EqualsNode(ExprNode node) + { + var otherExprCaseNode = node as ExprCaseNode; + if (otherExprCaseNode == null) + { + return false; + } + + return _isCase2 == otherExprCaseNode._isCase2; + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + CaseAnalysis analysis; + try + { + analysis = AnalyzeCase(); + } + catch (ExprValidationException e) + { + throw new Exception(e.Message, e); + } + + writer.Write("case"); + if (_isCase2) + { + writer.Write(' '); + analysis.OptionalCompareExprNode.ToEPL(writer, Precedence); + } + foreach (UniformPair p in analysis.WhenThenNodeList) + { + writer.Write(" when "); + p.First.ToEPL(writer, Precedence); + writer.Write(" then "); + p.Second.ToEPL(writer, Precedence); + } + if (analysis.OptionalElseExprNode != null) + { + writer.Write(" else "); + analysis.OptionalElseExprNode.ToEPL(writer, Precedence); + } + writer.Write(" end"); + } + + public override ExprPrecedenceEnum Precedence + { + get { return ExprPrecedenceEnum.CASE; } + } + + private CaseAnalysis AnalyzeCaseOne() + { + // Case 1 expression example: + // case when a=b then x [when c=d then y...] [else y] + // + var children = ChildNodes; + if (children.Count < 2) + { + throw new ExprValidationException("Case node must have at least 2 parameters"); + } + + IList> whenThenNodeList = new List>(); + int numWhenThen = children.Count >> 1; + for (int i = 0; i < numWhenThen; i++) + { + ExprNode whenExpr = children[(i << 1)]; + ExprNode thenExpr = children[(i << 1) + 1]; + whenThenNodeList.Add(new UniformPair(whenExpr, thenExpr)); + } + ExprNode optionalElseExprNode = null; + if (children.Count % 2 != 0) + { + optionalElseExprNode = children[children.Count - 1]; + } + return new CaseAnalysis(whenThenNodeList, null, optionalElseExprNode); + } + + private CaseAnalysis AnalyzeCaseTwo() + { + // Case 2 expression example: + // case p when p1 then x [when p2 then y...] [else z] + // + var children = ChildNodes; + if (children.Count < 3) + { + throw new ExprValidationException("Case node must have at least 3 parameters"); + } + + ExprNode optionalCompareExprNode = children[0]; + + IList> whenThenNodeList = new List>(); + int numWhenThen = (children.Count - 1) / 2; + for (int i = 0; i < numWhenThen; i++) + { + whenThenNodeList.Add(new UniformPair(children[i * 2 + 1], children[i * 2 + 2])); + } + ExprNode optionalElseExprNode = null; + if (numWhenThen * 2 + 1 < children.Count) + { + optionalElseExprNode = children[children.Count - 1]; + } + return new CaseAnalysis(whenThenNodeList, optionalCompareExprNode, optionalElseExprNode); + } + + private void ValidateCaseTwo() + { + // validate we can compare result types + IList comparedTypes = new List(); + comparedTypes.Add(_optionalCompareExprNode.ReturnType); + foreach (UniformPair pair in _whenThenNodeList) + { + comparedTypes.Add(pair.First.ReturnType); + } + + // Determine common denominator type + try + { + Type coercionType = TypeHelper.GetCommonCoercionType(comparedTypes); + + // Determine if we need to coerce numbers when one type doesn't match any other type + if (coercionType.IsNumeric()) + { + _mustCoerce = false; + foreach (Type comparedType in comparedTypes) + { + if (comparedType != coercionType) + { + _mustCoerce = true; + } + } + if (_mustCoerce) + { + _coercer = CoercerFactory.GetCoercer(null, coercionType); + } + } + } + catch (CoercionException ex) + { + throw new ExprValidationException("Implicit conversion not allowed: " + ex.Message); + } + } + + private Object EvaluateCaseSyntax1(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext exprEvaluatorContext) + { + // Case 1 expression example: + // case when a=b then x [when c=d then y...] [else y] + + Object caseResult = null; + bool matched = false; + foreach (UniformPair p in _whenThenNodeList) + { + var whenResult = (bool?)p.First.Evaluate(new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext)); + + // If the 'when'-expression returns true + if (whenResult ?? false) + { + caseResult = p.Second.Evaluate(new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext)); + matched = true; + break; + } + } + + if ((!matched) && (_optionalElseExprNode != null)) + { + caseResult = _optionalElseExprNode.Evaluate(new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext)); + } + + if (caseResult == null) + { + return null; + } + + if ((caseResult.GetType() != _resultType) && (_isNumericResult)) + { + return CoercerFactory.CoerceBoxed(caseResult, _resultType); + } + return caseResult; + } + + private Object EvaluateCaseSyntax2(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext exprEvaluatorContext) + { + var evaluateParams = new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext); + + // Case 2 expression example: + // case p when p1 then x [when p2 then y...] [else z] + + Object checkResult = _optionalCompareExprNode.Evaluate(evaluateParams); + Object caseResult = null; + bool matched = false; + + foreach (UniformPair p in _whenThenNodeList) + { + var whenResult = p.First.Evaluate(evaluateParams); + if (Compare(checkResult, whenResult)) + { + caseResult = p.Second.Evaluate(evaluateParams); + matched = true; + break; + } + } + + if ((!matched) && (_optionalElseExprNode != null)) + { + caseResult = _optionalElseExprNode.Evaluate(evaluateParams); + } + + if (caseResult == null) + { + return null; + } + + if ((caseResult.GetType() != _resultType) && (_isNumericResult)) + { + return CoercerFactory.CoerceBoxed(caseResult, _resultType); + } + return caseResult; + } + + private bool Compare(Object leftResult, Object rightResult) + { + if (leftResult == null) + { + return (rightResult == null); + } + if (rightResult == null) + { + return false; + } + + if (!_mustCoerce) + { + return leftResult.Equals(rightResult); + } + else + { + var left = _coercer.Invoke(leftResult); + var right = _coercer.Invoke(rightResult); + return left.Equals(right); + } + } + + private CaseAnalysis AnalyzeCase() + { + if (_isCase2) + { + return AnalyzeCaseTwo(); + } + else + { + return AnalyzeCaseOne(); + } + } + + public class CaseAnalysis + { + public CaseAnalysis(IList> whenThenNodeList, ExprNode optionalCompareExprNode, ExprNode optionalElseExprNode) + { + WhenThenNodeList = whenThenNodeList; + OptionalCompareExprNode = optionalCompareExprNode; + OptionalElseExprNode = optionalElseExprNode; + } + + public IList> WhenThenNodeList { get; private set; } + + public ExprNode OptionalCompareExprNode { get; private set; } + + public ExprNode OptionalElseExprNode { get; private set; } + } + } + +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/funcs/ExprCastNode.cs b/NEsper.Core/NEsper.Core/epl/expression/funcs/ExprCastNode.cs new file mode 100755 index 000000000..9f0a13175 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/funcs/ExprCastNode.cs @@ -0,0 +1,417 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Globalization; +using System.IO; +using System.Numerics; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.funcs.cast; +using com.espertech.esper.schedule; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.funcs +{ + /// + /// Represents the CAST(expression, type) function is an expression tree. + /// + [Serializable] + public class ExprCastNode : ExprNodeBase + { + private readonly string _classIdentifier; + private Type _targetType; + private bool _isConstant; + [NonSerialized] private ExprEvaluator _exprEvaluator; + + /// + /// Ctor. + /// + /// the the name of the type to cast to + public ExprCastNode(string classIdentifier) + { + _classIdentifier = classIdentifier; + } + + public static void HandleParseException(string formatString, string date, Exception ex) + { + throw new EPException( + "Exception parsing date '" + date + "' format '" + formatString + "': " + ex.Message, ex); + } + + public static void HandleParseISOException(string formatString, string date, Exception ex) + { + if (ex is ScheduleParameterException) + { + throw new EPException("Exception parsing iso8601 date '" + date + "': " + ex.Message, ex); + } + else + { + HandleParseException(formatString, date, ex); + } + } + + public override ExprEvaluator ExprEvaluator + { + get { return _exprEvaluator; } + } + + /// + /// Returns the name of the type of cast to. + /// + /// type name + public string ClassIdentifier + { + get { return _classIdentifier; } + } + + public override ExprNode Validate(ExprValidationContext validationContext) + { + if (ChildNodes.Count == 0 || ChildNodes.Count > 2) + { + throw new ExprValidationException("Cast function node must have one or two child expressions"); + } + + var valueEvaluator = ChildNodes[0].ExprEvaluator; + var fromType = valueEvaluator.ReturnType; + + // determine date format parameter + var namedParams = + ExprNodeUtility.GetNamedExpressionsHandleDups(ChildNodes); + ExprNodeUtility.ValidateNamed(namedParams, new string[] { "dateformat" }); + var dateFormatParameter = namedParams.Get("dateformat"); + if (dateFormatParameter != null) + { + ExprNodeUtility.ValidateNamedExpectType( + dateFormatParameter, new Type[]{ typeof (string) }); + } + + // identify target type + // try the primitive names including "string" + _targetType = TypeHelper.GetPrimitiveTypeForName(_classIdentifier.Trim()).GetBoxedType(); + + SimpleTypeCaster caster; + bool numeric; + CasterParserComputer casterParserComputer = null; + + var classIdentifierInvariant = _classIdentifier.Trim().ToLowerInvariant(); + if (dateFormatParameter != null) + { + if (fromType != typeof (string)) + { + throw new ExprValidationException( + string.Format("Use of the '{0}' named parameter requires a string-type input", dateFormatParameter.ParameterName)); + } + + if (_targetType == null) + { + try + { + _targetType = TypeHelper.GetClassForName( + _classIdentifier.Trim(), validationContext.EngineImportService.GetClassForNameProvider()); + } + catch (TypeLoadException) + { + // expected + } + } + + // dynamic or static date format + numeric = false; + caster = null; + + + StringToDateTimeBaseComputer dateTimeComputer; + + if (_targetType == typeof (DateTime) || + _targetType == typeof (DateTime?) || + classIdentifierInvariant.Equals("date")) + { + _targetType = typeof (DateTime); + + var desc = ValidateDateFormat(dateFormatParameter, validationContext); + if (desc.StaticDateFormat != null) + { + if (desc.Iso8601Format) + { + dateTimeComputer = new StringToDateTimeWStaticISOFormatComputer(validationContext.EngineImportService.TimeZone); + dateTimeComputer.HandleParseException += HandleParseISOException; + } + else + { + dateTimeComputer = new StringToDateTimeWStaticFormatComputer(desc.StaticDateFormat); + dateTimeComputer.HandleParseException += HandleParseException; + } + } + else + { + dateTimeComputer = new StringToDateTimeWDynamicFormatComputer(desc.DynamicDateFormat); + dateTimeComputer.HandleParseException += HandleParseException; + } + } + else if (_targetType == typeof (DateTimeOffset) || + _targetType == typeof (DateTimeOffset?) || + classIdentifierInvariant.Equals("dto") || + classIdentifierInvariant.Equals("datetimeoffset")) + { + _targetType = typeof(DateTimeOffset); + + var desc = ValidateDateFormat(dateFormatParameter, validationContext); + if (desc.StaticDateFormat != null) + { + if (desc.Iso8601Format) + { + dateTimeComputer = new StringToDtoWStaticISOFormatComputer(validationContext.EngineImportService.TimeZone); + dateTimeComputer.HandleParseException += HandleParseISOException; + } + else + { + dateTimeComputer = new StringToDtoWStaticFormatComputer(desc.StaticDateFormat, validationContext.EngineImportService.TimeZone); + dateTimeComputer.HandleParseException += HandleParseException; + } + } + else + { + dateTimeComputer = new StringToDtoWDynamicFormatComputer(desc.DynamicDateFormat, validationContext.EngineImportService.TimeZone); + dateTimeComputer.HandleParseException += HandleParseException; + } + } + else if (_targetType == typeof (DateTimeEx) || + classIdentifierInvariant.Equals("dtx") || + classIdentifierInvariant.Equals("datetimeex") || + classIdentifierInvariant.Equals("calendar")) + { + _targetType = typeof(DateTimeEx); + + var desc = ValidateDateFormat(dateFormatParameter, validationContext); + if (desc.StaticDateFormat != null) + { + if (desc.Iso8601Format) + { + dateTimeComputer = new StringToDtxWStaticISOFormatComputer(validationContext.EngineImportService.TimeZone); + dateTimeComputer.HandleParseException += HandleParseISOException; + } + else + { + dateTimeComputer = new StringToDtxWStaticFormatComputer(desc.StaticDateFormat, validationContext.EngineImportService.TimeZone); + dateTimeComputer.HandleParseException += HandleParseException; + } + } + else + { + dateTimeComputer = new StringToDtxWDynamicFormatComputer(desc.DynamicDateFormat, validationContext.EngineImportService.TimeZone); + dateTimeComputer.HandleParseException += HandleParseException; + } + } + else if (_targetType == typeof (long) || _targetType == typeof(long?)) + { + _targetType = typeof (long); + + var desc = ValidateDateFormat(dateFormatParameter, validationContext); + if (desc.StaticDateFormat != null) + { + if (desc.Iso8601Format) + { + dateTimeComputer = new StringToDateTimeLongWStaticISOFormatComputer(validationContext.EngineImportService.TimeZone); + dateTimeComputer.HandleParseException += HandleParseISOException; + } + else + { + dateTimeComputer = new StringToDateTimeLongWStaticFormatComputer(desc.StaticDateFormat); + dateTimeComputer.HandleParseException += HandleParseException; + } + } + else + { + dateTimeComputer = new StringToDateTimeLongWDynamicFormatComputer(desc.DynamicDateFormat); + dateTimeComputer.HandleParseException += HandleParseException; + } + } + else + { + throw new ExprValidationException( + "Use of the '" + dateFormatParameter.ParameterName + + "' named parameter requires a target type of long or datetime"); + } + + casterParserComputer = dateTimeComputer; + } + else if (_targetType != null) + { + _targetType = _targetType.GetBoxedType(); + caster = SimpleTypeCasterFactory.GetCaster(fromType, _targetType); + numeric = _targetType.IsNumeric(); + } + else if (String.Equals(classIdentifierInvariant, "bigint", StringComparison.InvariantCultureIgnoreCase) || + String.Equals(classIdentifierInvariant, "biginteger", StringComparison.InvariantCultureIgnoreCase)) + { + _targetType = typeof (BigInteger); + caster = SimpleTypeCasterFactory.GetCaster(fromType, _targetType); + numeric = true; + } + else if (classIdentifierInvariant.Equals("decimal".ToLowerInvariant())) + { + _targetType = typeof (decimal); + caster = SimpleTypeCasterFactory.GetCaster(fromType, _targetType); + numeric = true; + } + else + { + try + { + _targetType = TypeHelper.GetClassForName( + _classIdentifier.Trim(), validationContext.EngineImportService.GetClassForNameProvider()); + } + catch (TypeLoadException e) + { + throw new ExprValidationException( + "Type as listed in cast function by name '" + _classIdentifier + "' cannot be loaded", e); + } + numeric = _targetType.IsNumeric(); + caster = numeric + ? SimpleTypeCasterFactory.GetCaster(fromType, _targetType) + : SimpleTypeCasterFactory.GetCaster(_targetType); + } + + // assign a computer unless already assigned + if (casterParserComputer == null) + { + // to-string + if (_targetType == typeof (string)) + { + casterParserComputer = new StringXFormComputer(); + } + else if (fromType == typeof (string)) + { + // parse + var parser = SimpleTypeParserFactory.GetParser(_targetType.GetBoxedType()); + casterParserComputer = new StringParserComputer(parser); + } + else if (numeric) + { + // numeric cast with check + casterParserComputer = new NumericCasterComputer(caster); + } + else + { + // non-numeric cast + casterParserComputer = new NonnumericCasterComputer(caster); + } + } + + // determine constant or not + Object theConstant = null; + if (ChildNodes[0].IsConstantResult) + { + _isConstant = casterParserComputer.IsConstantForConstInput; + if (_isConstant) + { + var evaluateParams = new EvaluateParams(null, true, validationContext.ExprEvaluatorContext); + var @in = valueEvaluator.Evaluate(evaluateParams); + theConstant = @in == null ? null : casterParserComputer.Compute(@in, evaluateParams); + } + } + + // determine evaluator + if (_isConstant) + { + _exprEvaluator = new ExprCastNodeConstEval(this, theConstant); + } + else + { + _exprEvaluator = new ExprCastNodeNonConstEval(this, valueEvaluator, casterParserComputer); + } + return null; + } + + public override bool IsConstantResult + { + get { return _isConstant; } + } + + public Type TargetType + { + get { return _targetType; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) { + writer.Write("cast("); + ChildNodes[0].ToEPL(writer, ExprPrecedenceEnum.MINIMUM); + writer.Write(","); + writer.Write(_classIdentifier); + for (var i = 1; i < ChildNodes.Count; i++) { + writer.Write(","); + ChildNodes[i].ToEPL(writer, ExprPrecedenceEnum.MINIMUM); + } + writer.Write(')'); + } + + public override ExprPrecedenceEnum Precedence + { + get { return ExprPrecedenceEnum.UNARY; } + } + + public override bool EqualsNode(ExprNode node) { + var other = node as ExprCastNode; + if (other == null) + { + return false; + } + + return other._classIdentifier.Equals(_classIdentifier); + } + + /// + /// Validates the date format. + /// + /// The date format parameter. + /// The validation context. + /// + + private ExprCastNodeDateDesc ValidateDateFormat( + ExprNamedParameterNode dateFormatParameter, + ExprValidationContext validationContext) + { + string staticDateFormat = null; + ExprEvaluator dynamicDateFormat = null; + var iso8601Format = false; + + if (!dateFormatParameter.ChildNodes[0].IsConstantResult) + { + dynamicDateFormat = dateFormatParameter.ChildNodes[0].ExprEvaluator; + } + else + { + staticDateFormat = (string) dateFormatParameter.ChildNodes[0].ExprEvaluator.Evaluate( + new EvaluateParams(null, true, validationContext.ExprEvaluatorContext)); + if (staticDateFormat.ToLowerInvariant().Trim().Equals("iso")) + { + iso8601Format = true; + } + else + { + try + { + DateTime dateTimeTemp; + DateTime.TryParseExact("", staticDateFormat, null, DateTimeStyles.None, out dateTimeTemp); + //new SimpleDateFormat(staticDateFormat); + } + catch (Exception ex) + { + throw new ExprValidationException( + "Invalid date format '" + staticDateFormat + "': " + ex.Message, ex); + } + } + } + return new ExprCastNodeDateDesc(staticDateFormat, dynamicDateFormat, iso8601Format); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/funcs/ExprCastNodeConstEval.cs b/NEsper.Core/NEsper.Core/epl/expression/funcs/ExprCastNodeConstEval.cs new file mode 100755 index 000000000..2bff06bc2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/funcs/ExprCastNodeConstEval.cs @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.expression.funcs +{ + public class ExprCastNodeConstEval : ExprEvaluator + { + private readonly ExprCastNode _parent; + private readonly object _theConstant; + + public ExprCastNodeConstEval(ExprCastNode parent, object theConstant) { + _parent = parent; + _theConstant = theConstant; + } + + public Type ReturnType + { + get { return _parent.TargetType; } + } + + public object Evaluate(EvaluateParams evaluateParams) + { + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().QExprCast(_parent); + InstrumentationHelper.Get().AExprCast(_theConstant); + } + return _theConstant; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/funcs/ExprCastNodeDateDesc.cs b/NEsper.Core/NEsper.Core/epl/expression/funcs/ExprCastNodeDateDesc.cs new file mode 100755 index 000000000..7f247abbe --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/funcs/ExprCastNodeDateDesc.cs @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.funcs +{ + public class ExprCastNodeDateDesc + { + public ExprCastNodeDateDesc(string staticDateFormat, ExprEvaluator dynamicDateFormat, bool iso8601Format) + { + StaticDateFormat = staticDateFormat; + DynamicDateFormat = dynamicDateFormat; + Iso8601Format = iso8601Format; + } + + public string StaticDateFormat { get; private set; } + + public ExprEvaluator DynamicDateFormat { get; private set; } + + public bool Iso8601Format { get; private set; } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/funcs/ExprCastNodeNonConstEval.cs b/NEsper.Core/NEsper.Core/epl/expression/funcs/ExprCastNodeNonConstEval.cs new file mode 100755 index 000000000..701aa200a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/funcs/ExprCastNodeNonConstEval.cs @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.funcs.cast; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.expression.funcs +{ + public class ExprCastNodeNonConstEval : ExprEvaluator + { + private readonly ExprCastNode _parent; + private readonly ExprEvaluator _evaluator; + private readonly CasterParserComputer _casterParserComputer; + + internal ExprCastNodeNonConstEval(ExprCastNode parent, ExprEvaluator evaluator, CasterParserComputer casterParserComputer) + { + _parent = parent; + _evaluator = evaluator; + _casterParserComputer = casterParserComputer; + } + + public object Evaluate(EvaluateParams evaluateParams) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QExprCast(_parent);} + + var result = _evaluator.Evaluate(evaluateParams); + if (result != null) { + result = _casterParserComputer.Compute(result, evaluateParams); + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprCast(result);} + return result; + } + + public Type ReturnType + { + get { return _parent.TargetType; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/funcs/ExprCoalesceNode.cs b/NEsper.Core/NEsper.Core/epl/expression/funcs/ExprCoalesceNode.cs new file mode 100755 index 000000000..0937042a1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/funcs/ExprCoalesceNode.cs @@ -0,0 +1,128 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +using com.espertech.esper.compat; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.funcs +{ + /// + /// Represents the COALESCE(a,b,...) function is an expression tree. + /// + [Serializable] + public class ExprCoalesceNode : ExprNodeBase, ExprEvaluator + { + private Type _resultType; + private bool[] _isNumericCoercion; + + [NonSerialized] private ExprEvaluator[] _evaluators; + + public override ExprEvaluator ExprEvaluator + { + get { return this; } + } + + public override ExprNode Validate(ExprValidationContext validationContext) + { + if (ChildNodes.Count < 2) + { + throw new ExprValidationException("Coalesce node must have at least 2 parameters"); + } + _evaluators = ExprNodeUtility.GetEvaluators(ChildNodes); + + // get child expression types + var childTypes = new Type[ChildNodes.Count]; + for (var i = 0; i < _evaluators.Length; i++) + { + childTypes[i] = _evaluators[i].ReturnType; + } + + // determine coercion type + try { + _resultType = TypeHelper.GetCommonCoercionType(childTypes); + } + catch (CoercionException ex) + { + throw new ExprValidationException("Implicit conversion not allowed: " + ex.Message); + } + + // determine which child nodes need numeric coercion + _isNumericCoercion = new bool[ChildNodes.Count]; + for (var i = 0; i < _evaluators.Length; i++) + { + if ((_evaluators[i].ReturnType.GetBoxedType() != _resultType) && + (_evaluators[i].ReturnType != null) && (_resultType != null)) + { + if (!_resultType.IsNumeric()) + { + throw new ExprValidationException(string.Format("Implicit conversion from datatype '{0}' to {1} is not allowed", Name.Of(_resultType), Name.Of(_evaluators[i].ReturnType))); + } + _isNumericCoercion[i] = true; + } + } + + return null; + } + + public override bool IsConstantResult + { + get { return false; } + } + + public Type ReturnType + { + get { return _resultType; } + } + + public object Evaluate(EvaluateParams evaluateParams) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QExprCoalesce(this);} + Object value; + + // Look for the first non-null return value + for (var i = 0; i < _evaluators.Length; i++) + { + value = _evaluators[i].Evaluate(evaluateParams); + + if (value != null) + { + // Check if we need to coerce + if (_isNumericCoercion[i]) + { + value = CoercerFactory.CoerceBoxed(value, _resultType); + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprCoalesce(value);} + return value; + } + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprCoalesce(null);} + return null; + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + ExprNodeUtility.ToExpressionStringWFunctionName("coalesce", ChildNodes, writer); + } + + public override ExprPrecedenceEnum Precedence + { + get { return ExprPrecedenceEnum.UNARY; } + } + + public override bool EqualsNode(ExprNode node) + { + return node is ExprCoalesceNode; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/funcs/ExprIStreamNode.cs b/NEsper.Core/NEsper.Core/epl/expression/funcs/ExprIStreamNode.cs new file mode 100755 index 000000000..aea7c0542 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/funcs/ExprIStreamNode.cs @@ -0,0 +1,76 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.expression.funcs +{ + /// + /// Represents the RSTREAM() function in an expression tree. + /// + [Serializable] + public class ExprIStreamNode + : ExprNodeBase + , ExprEvaluator + { + /// Ctor. + public ExprIStreamNode() + { + } + + public override ExprEvaluator ExprEvaluator + { + get { return this; } + } + + public override ExprNode Validate(ExprValidationContext validationContext) + { + if (ChildNodes.Count != 0) + { + throw new ExprValidationException("current_timestamp function node must have exactly 1 child node"); + } + + return null; + } + + public override bool IsConstantResult + { + get { return false; } + } + + public Type ReturnType + { + get { return typeof (bool); } + } + + public object Evaluate(EvaluateParams evaluateParams) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QaExprIStream(this, evaluateParams.IsNewData); } + return evaluateParams.IsNewData; + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + writer.Write("istream()"); + } + + public override ExprPrecedenceEnum Precedence + { + get { return ExprPrecedenceEnum.UNARY; } + } + + public override bool EqualsNode(ExprNode node) + { + return node is ExprIStreamNode; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/funcs/ExprInstanceofNode.cs b/NEsper.Core/NEsper.Core/epl/expression/funcs/ExprInstanceofNode.cs new file mode 100755 index 000000000..18e9f1658 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/funcs/ExprInstanceofNode.cs @@ -0,0 +1,210 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.threading; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.funcs +{ + /// + /// Represents the INSTANCEOF(a,b,...) function is an expression tree. + /// + [Serializable] + public class ExprInstanceofNode : ExprNodeBase, ExprEvaluator + { + private readonly String[] _classIdentifiers; + + private Type[] _classes; + private readonly CopyOnWriteList> _resultCache = new CopyOnWriteList>(); + [NonSerialized] + private ExprEvaluator _evaluator; + + private readonly ILockable _oLock; + + /// Ctor. + /// is a list of type names to check type for + public ExprInstanceofNode(String[] classIdentifiers) + { + _oLock = LockManager.CreateLock(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + _classIdentifiers = classIdentifiers; + } + + public override ExprEvaluator ExprEvaluator + { + get { return this; } + } + + public override ExprNode Validate(ExprValidationContext validationContext) + { + if (ChildNodes.Count != 1) + { + throw new ExprValidationException("Instanceof node must have 1 child expression node supplying the expression to test"); + } + if ((_classIdentifiers == null) || (_classIdentifiers.Length == 0)) + { + throw new ExprValidationException("Instanceof node must have 1 or more class identifiers to verify type against"); + } + + _evaluator = ChildNodes[0].ExprEvaluator; + + var classList = GetClassSet(_classIdentifiers, validationContext.EngineImportService); + using (_oLock.Acquire()) + { + _classes = classList.ToArray(); + } + + return null; + } + + public override bool IsConstantResult + { + get { return false; } + } + + public Type ReturnType + { + get { return typeof(bool?); } + } + + public object Evaluate(EvaluateParams evaluateParams) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QExprInstanceof(this); } + + var result = _evaluator.Evaluate(evaluateParams); + if (result == null) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprInstanceof(false); } + return false; + } + + // return cached value + foreach (var pair in _resultCache) + { + if (pair.First == result.GetType()) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprInstanceof(pair.Second); } + return pair.Second; + } + } + + var @out = CheckAddType(result.GetType()); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprInstanceof(@out); } + return @out; + } + + // Checks type and adds to cache + private bool? CheckAddType(Type type) + { + using (_oLock.Acquire()) + { + // check again in synchronized block + foreach (Pair pair in _resultCache) + { + if (pair.First == type) + { + return pair.Second; + } + } + + // get the types superclasses and interfaces, and their superclasses and interfaces + ICollection classesToCheck = new HashSet(); + TypeHelper.GetBase(type, classesToCheck); + classesToCheck.Add(type); + + // check type against each class + bool fits = false; + foreach (Type clazz in _classes) + { + if (classesToCheck.Contains(clazz)) + { + fits = true; + break; + } + } + + _resultCache.Add(new Pair(type, fits)); + return fits; + } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + writer.Write("instanceof("); + ChildNodes[0].ToEPL(writer, ExprPrecedenceEnum.MINIMUM); + writer.Write(","); + + String delimiter = ""; + for (int i = 0; i < _classIdentifiers.Length; i++) + { + writer.Write(delimiter); + writer.Write(_classIdentifiers[i]); + delimiter = ","; + } + writer.Write(')'); + } + + public override ExprPrecedenceEnum Precedence + { + get { return ExprPrecedenceEnum.UNARY; } + } + + public override bool EqualsNode(ExprNode node) + { + var other = node as ExprInstanceofNode; + return other != null && Collections.AreEqual(other._classIdentifiers, _classIdentifiers); + } + + /// Returns the list of class names or types to check instance of. + /// class names + public string[] ClassIdentifiers + { + get { return _classIdentifiers; } + } + + private ICollection GetClassSet(string[] classIdentifiers, EngineImportService engineImportService) + { + var classList = new HashSet(); + foreach (String className in classIdentifiers) + { + // try the primitive names including "string" + var clazz = TypeHelper.GetPrimitiveTypeForName(className.Trim()); + if (clazz != null) + { + classList.Add(clazz); + classList.Add(clazz.GetBoxedType()); + continue; + } + + // try to look up the class, not a primitive type name + try + { + clazz = TypeHelper.GetClassForName(className.Trim(), engineImportService.GetClassForNameProvider()); + } + catch (TypeLoadException e) + { + throw new ExprValidationException("Class as listed in is function by name '" + className + "' cannot be loaded", e); + } + + // Add primitive and boxed types, or type itself if not built-in + classList.Add(clazz.GetPrimitiveType()); + classList.Add(clazz.GetBoxedType()); + } + return classList; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/funcs/ExprMinMaxRowNode.cs b/NEsper.Core/NEsper.Core/epl/expression/funcs/ExprMinMaxRowNode.cs new file mode 100755 index 000000000..c25e7ad86 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/funcs/ExprMinMaxRowNode.cs @@ -0,0 +1,167 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; +using System.Numerics; + +using com.espertech.esper.compat; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.type; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.funcs +{ + /// + /// Represents the MAX(a,b) and MIN(a,b) functions is an expression tree. + /// + [Serializable] + public class ExprMinMaxRowNode : ExprNodeBase, ExprEvaluator + { + private readonly MinMaxTypeEnum _minMaxTypeEnum; + private Type _resultType; + [NonSerialized] private MinMaxTypeEnumExtensions.Computer _computer; + [NonSerialized] private ExprEvaluator[] _evaluators; + + /// + /// Ctor. + /// + /// type of compare + public ExprMinMaxRowNode(MinMaxTypeEnum minMaxTypeEnum) + { + _minMaxTypeEnum = minMaxTypeEnum; + } + + public override ExprEvaluator ExprEvaluator + { + get { return this; } + } + + /// Returns the indicator for minimum or maximum. + /// min/max indicator + public MinMaxTypeEnum MinMaxTypeEnum + { + get { return _minMaxTypeEnum; } + } + + public override ExprNode Validate(ExprValidationContext validationContext) + { + if (ChildNodes.Count < 2) + { + throw new ExprValidationException("MinMax node must have at least 2 parameters"); + } + _evaluators = ExprNodeUtility.GetEvaluators(ChildNodes); + + foreach (ExprEvaluator child in _evaluators) + { + var childType = child.ReturnType; + if (!childType.IsNumeric()) + { + throw new ExprValidationException(string.Format("Implicit conversion from datatype '{0}' to numeric is not allowed", childType.FullName)); + } + } + + // Determine result type, set up compute function + var childTypeOne = _evaluators[0].ReturnType; + var childTypeTwo = _evaluators[1].ReturnType; + _resultType = childTypeOne.GetArithmaticCoercionType(childTypeTwo); + + for (int i = 2; i < ChildNodes.Count; i++) + { + _resultType = _resultType.GetArithmaticCoercionType(_evaluators[i].ReturnType); + } + + if (_resultType == typeof(decimal) || _resultType == typeof(decimal?)) + { + _computer = Equals(_minMaxTypeEnum, MinMaxTypeEnum.MAX) + ? MinMaxTypeEnumExtensions.CreateMaxDecimalComputer(_evaluators) + : MinMaxTypeEnumExtensions.CreateMinDecimalComputer(_evaluators); + } + else if (_resultType == typeof(BigInteger) || _resultType == typeof(BigInteger?)) + { + _computer = Equals(_minMaxTypeEnum, MinMaxTypeEnum.MAX) + ? MinMaxTypeEnumExtensions.CreateMaxBigIntComputer(_evaluators) + : MinMaxTypeEnumExtensions.CreateMinBigIntComputer(_evaluators); + } + else + { + _computer = Equals(_minMaxTypeEnum, MinMaxTypeEnum.MAX) + ? MinMaxTypeEnumExtensions.CreateMaxDoubleComputer(_evaluators) + : MinMaxTypeEnumExtensions.CreateMinDoubleComputer(_evaluators); + } + + return null; + } + + public Type ReturnType + { + get { return _resultType; } + } + + public override bool IsConstantResult + { + get { return false; } + } + + public object Evaluate(EvaluateParams evaluateParams) + { + var result = new Mutable(); + + using (Instrument.With( + i => i.QExprMinMaxRow(this), + i => i.AExprMinMaxRow(result.Value))) + { + result.Value = _computer.Invoke( + evaluateParams.EventsPerStream, + evaluateParams.IsNewData, + evaluateParams.ExprEvaluatorContext); + if (result.Value != null) + { + result.Value = CoercerFactory.CoerceBoxed(result.Value, _resultType); + } + + return result.Value; + } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + writer.Write(_minMaxTypeEnum.GetExpressionText()); + writer.Write('('); + + ChildNodes[0].ToEPL(writer, ExprPrecedenceEnum.MINIMUM); + writer.Write(','); + ChildNodes[1].ToEPL(writer, ExprPrecedenceEnum.MINIMUM); + + for (int i = 2; i < ChildNodes.Count; i++) + { + writer.Write(','); + ChildNodes[i].ToEPL(writer, ExprPrecedenceEnum.MINIMUM); + } + + writer.Write(')'); + } + + public override ExprPrecedenceEnum Precedence + { + get { return ExprPrecedenceEnum.UNARY; } + } + + public override bool EqualsNode(ExprNode node) + { + var other = node as ExprMinMaxRowNode; + if (other != null) + { + return other._minMaxTypeEnum == _minMaxTypeEnum; + } + + return false; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/funcs/ExprPlugInSingleRowNode.cs b/NEsper.Core/NEsper.Core/epl/expression/funcs/ExprPlugInSingleRowNode.cs new file mode 100755 index 000000000..86603324b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/funcs/ExprPlugInSingleRowNode.cs @@ -0,0 +1,259 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.enummethod.dot; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.dot; +using com.espertech.esper.epl.expression.visitor; +using com.espertech.esper.epl.rettype; +using com.espertech.esper.filter; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.expression.funcs +{ + /// + /// Represents an invocation of a plug-in single-row function in the expression tree. + /// + [Serializable] + public class ExprPlugInSingleRowNode + : ExprNodeBase + , ExprNodeInnerNodeProvider + , ExprFilterOptimizableNode + { + private readonly String _functionName; + private readonly Type _clazz; + private readonly IList _chainSpec; + private readonly EngineImportSingleRowDesc _config; + + [NonSerialized] + private bool _isReturnsConstantResult; + [NonSerialized] + private ExprEvaluator _evaluator; + + /// + /// Ctor. + /// + /// Name of the function. + /// The clazz. + /// the class and name of the method that this node will invoke plus parameters + /// The config. + public ExprPlugInSingleRowNode(String functionName, Type clazz, IList chainSpec, EngineImportSingleRowDesc config) + { + _functionName = functionName; + _clazz = clazz; + _chainSpec = chainSpec; + _config = config; + } + + public override ExprEvaluator ExprEvaluator + { + get { return _evaluator; } + } + + public IList ChainSpec + { + get { return _chainSpec; } + } + + public override bool IsConstantResult + { + get { return _isReturnsConstantResult; } + } + + public string FunctionName + { + get { return _functionName; } + } + + public bool IsFilterLookupEligible + { + get + { + var eligible = !_isReturnsConstantResult; + if (eligible) + { + eligible = _chainSpec.Count == 1; + } + if (eligible) + { + eligible = _config.FilterOptimizable == FilterOptimizableEnum.ENABLED; + } + if (eligible) + { + // We disallow context properties in a filter-optimizable expression if they are passed in since + // the evaluation is context-free and shared. + ExprNodeContextPropertiesVisitor visitor = new ExprNodeContextPropertiesVisitor(); + ExprNodeUtility.AcceptChain(visitor, _chainSpec); + eligible = !visitor.IsFound; + } + if (eligible) + { + ExprNodeStreamRequiredVisitor visitor = new ExprNodeStreamRequiredVisitor(); + ExprNodeUtility.AcceptChain(visitor, _chainSpec); + foreach (int stream in visitor.StreamsRequired.Where(stream => stream != 0)) + { + eligible = false; + } + } + return eligible; + } + } + + public FilterSpecLookupable FilterLookupable + { + get + { + var eval = (ExprDotEvalStaticMethod)_evaluator; + return new FilterSpecLookupable(this.ToExpressionStringMinPrecedenceSafe(), eval, _evaluator.ReturnType, true); + } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + ExprNodeUtility.ToExpressionString(_chainSpec, writer, false, _functionName); + } + + public override ExprPrecedenceEnum Precedence + { + get { return ExprPrecedenceEnum.UNARY; } + } + + public override bool EqualsNode(ExprNode node) + { + if (!(node is ExprPlugInSingleRowNode)) + { + return false; + } + + var other = (ExprPlugInSingleRowNode)node; + if (other._chainSpec.Count != _chainSpec.Count) + { + return false; + } + for (var i = 0; i < _chainSpec.Count; i++) + { + if (!Equals(_chainSpec[i], other._chainSpec[i])) + { + return false; + } + } + return other._clazz == _clazz && other._functionName.EndsWith(_functionName); + } + + public override ExprNode Validate(ExprValidationContext validationContext) + { + ExprNodeUtility.Validate(ExprNodeOrigin.PLUGINSINGLEROWPARAM, _chainSpec, validationContext); + + // get first chain item + var chainList = new List(_chainSpec); + var firstItem = chainList.DeleteAt(0); + + // Get the types of the parameters for the first invocation + var allowWildcard = validationContext.StreamTypeService.EventTypes.Length == 1; + EventType streamZeroType = null; + if (validationContext.StreamTypeService.EventTypes.Length > 0) + { + streamZeroType = validationContext.StreamTypeService.EventTypes[0]; + } + var staticMethodDesc = ExprNodeUtility.ResolveMethodAllowWildcardAndStream( + _clazz.FullName, null, firstItem.Name, firstItem.Parameters, validationContext.EngineImportService, + validationContext.EventAdapterService, validationContext.StatementId, allowWildcard, streamZeroType, + new ExprNodeUtilResolveExceptionHandlerDefault(firstItem.Name, true), _functionName, + validationContext.TableService, + validationContext.StreamTypeService.EngineURIQualifier); + + var allowValueCache = true; + switch (_config.ValueCache) + { + case ValueCacheEnum.DISABLED: + _isReturnsConstantResult = false; + allowValueCache = false; + break; + case ValueCacheEnum.CONFIGURED: + _isReturnsConstantResult = validationContext.EngineImportService.IsUdfCache && staticMethodDesc.IsAllConstants && chainList.IsEmpty(); + allowValueCache = validationContext.EngineImportService.IsUdfCache; + break; + case ValueCacheEnum.ENABLED: + _isReturnsConstantResult = staticMethodDesc.IsAllConstants && chainList.IsEmpty(); + break; + default: + throw new IllegalStateException("Invalid value cache code " + _config.ValueCache); + } + + // this may return a pair of null if there is no lambda or the result cannot be wrapped for lambda-function use + var optionalLambdaWrap = ExprDotStaticMethodWrapFactory.Make( + staticMethodDesc.ReflectionMethod, validationContext.EventAdapterService, chainList, + _config.OptionalEventTypeName); + var typeInfo = optionalLambdaWrap != null ? optionalLambdaWrap.TypeInfo : EPTypeHelper.SingleValue(staticMethodDesc.ReflectionMethod.ReturnType); + + var eval = ExprDotNodeUtility.GetChainEvaluators(-1, typeInfo, chainList, validationContext, false, new ExprDotNodeFilterAnalyzerInputStatic()).ChainWithUnpack; + _evaluator = new ExprDotEvalStaticMethod( + validationContext.StatementName, _clazz.FullName, staticMethodDesc.FastMethod, + staticMethodDesc.ChildEvals, allowValueCache && staticMethodDesc.IsAllConstants, optionalLambdaWrap, + eval, _config.IsRethrowExceptions, null); + + // If caching the result, evaluate now and return the result. + if (_isReturnsConstantResult) + { + var result = _evaluator.Evaluate(new EvaluateParams(null, true, null)); + _evaluator = new ProxyExprEvaluator + { + ProcEvaluate = args => + { + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().QExprPlugInSingleRow(staticMethodDesc.ReflectionMethod); + InstrumentationHelper.Get().AExprPlugInSingleRow(result); + } + return result; + }, + ReturnType = staticMethodDesc.FastMethod.ReturnType + }; + } + + return null; + } + + public override void Accept(ExprNodeVisitor visitor) + { + base.Accept(visitor); + ExprNodeUtility.AcceptChain(visitor, _chainSpec); + } + + public override void Accept(ExprNodeVisitorWithParent visitor) + { + base.Accept(visitor); + ExprNodeUtility.AcceptChain(visitor, _chainSpec, this); + } + + public override void AcceptChildnodes(ExprNodeVisitorWithParent visitor, ExprNode parent) + { + base.AcceptChildnodes(visitor, parent); + ExprNodeUtility.AcceptChain(visitor, _chainSpec, this); + } + + public override void ReplaceUnlistedChildNode(ExprNode nodeToReplace, ExprNode newNode) + { + ExprNodeUtility.ReplaceChainChildNode(nodeToReplace, newNode, _chainSpec); + } + + public IList AdditionalNodes + { + get { return ExprNodeUtility.CollectChainParameters(_chainSpec); } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/funcs/ExprPropertyExistsNode.cs b/NEsper.Core/NEsper.Core/epl/expression/funcs/ExprPropertyExistsNode.cs new file mode 100755 index 000000000..04aa3a46a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/funcs/ExprPropertyExistsNode.cs @@ -0,0 +1,83 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.expression.funcs +{ + /// + /// Represents the EXISTS(property) function in an expression tree. + /// + [Serializable] + public class ExprPropertyExistsNode : ExprNodeBase, ExprEvaluator + { + private ExprIdentNode _identNode; + + public override ExprEvaluator ExprEvaluator + { + get { return this; } + } + + public override ExprNode Validate(ExprValidationContext validationContext) + { + if (ChildNodes.Count != 1) + { + throw new ExprValidationException("Exists function node must have exactly 1 child node"); + } + + if (!(ChildNodes[0] is ExprIdentNode)) + { + throw new ExprValidationException("Exists function expects an property value expression as the child node"); + } + + _identNode = (ExprIdentNode) ChildNodes[0]; + return null; + } + + public override bool IsConstantResult + { + get { return false; } + } + + public Type ReturnType + { + get { return typeof (bool?); } + } + + public object Evaluate(EvaluateParams evaluateParams) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QExprPropExists(this);} + bool exists = _identNode.ExprEvaluatorIdent.EvaluatePropertyExists( + evaluateParams.EventsPerStream, + evaluateParams.IsNewData); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprPropExists(exists);} + return exists; + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + writer.Write("exists("); + ChildNodes[0].ToEPL(writer, ExprPrecedenceEnum.MINIMUM); + writer.Write(')'); + } + + public override ExprPrecedenceEnum Precedence + { + get { return ExprPrecedenceEnum.UNARY; } + } + + public override bool EqualsNode(ExprNode node) + { + return node is ExprPropertyExistsNode; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/funcs/ExprTypeofNode.cs b/NEsper.Core/NEsper.Core/epl/expression/funcs/ExprTypeofNode.cs new file mode 100755 index 000000000..d113699ad --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/funcs/ExprTypeofNode.cs @@ -0,0 +1,242 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events.vaevent; +using com.espertech.esper.filter; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.expression.funcs +{ + /// + /// Represents the TYPEOF(a) function is an expression tree. + /// + [Serializable] + public class ExprTypeofNode + : ExprNodeBase + , ExprFilterOptimizableNode + { + [NonSerialized] + private ExprEvaluator _evaluator; + + public override ExprEvaluator ExprEvaluator + { + get { return _evaluator; } + } + + public IDictionary EventType + { + get { return null; } + } + + public override ExprNode Validate(ExprValidationContext validationContext) + { + if (ChildNodes.Count != 1) + { + throw new ExprValidationException("Typeof node must have 1 child expression node supplying the expression to test"); + } + + if (ChildNodes[0] is ExprStreamUnderlyingNode) + { + var stream = (ExprStreamUnderlyingNode)ChildNodes[0]; + _evaluator = new StreamEventTypeEval(stream.StreamId); + return null; + } + + if (ChildNodes[0] is ExprIdentNode) + { + var ident = (ExprIdentNode)ChildNodes[0]; + var streamNum = validationContext.StreamTypeService.GetStreamNumForStreamName(ident.FullUnresolvedName); + if (streamNum != -1) + { + _evaluator = new StreamEventTypeEval(streamNum); + return null; + } + + var eventType = validationContext.StreamTypeService.EventTypes[ident.StreamId]; + if (eventType.GetFragmentType(ident.ResolvedPropertyName) != null) + { + _evaluator = new FragmentTypeEval(ident.StreamId, eventType, ident.ResolvedPropertyName); + return null; + } + } + + _evaluator = new InnerEvaluator(ChildNodes[0].ExprEvaluator); + return null; + } + + public override bool IsConstantResult + { + get { return false; } + } + + public Type ReturnType + { + get { return typeof(string); } + } + + public bool IsFilterLookupEligible + { + get { return true; } + } + + public FilterSpecLookupable FilterLookupable + { + get + { + EventPropertyGetter getter = new ProxyEventPropertyGetter + { + ProcGet = eventBean => eventBean.EventType.Name, + ProcIsExistsProperty = eventBean => true, + ProcGetFragment = eventBean => null + }; + return new FilterSpecLookupable( + this.ToExpressionStringMinPrecedenceSafe(), getter, typeof(string), true); + } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + writer.Write("typeof("); + ChildNodes[0].ToEPL(writer, ExprPrecedenceEnum.MINIMUM); + writer.Write(')'); + } + + public override ExprPrecedenceEnum Precedence + { + get { return ExprPrecedenceEnum.UNARY; } + } + + public override bool EqualsNode(ExprNode node) + { + return node is ExprTypeofNode; + } + + public class StreamEventTypeEval : ExprEvaluator + { + private readonly int _streamNum; + + public StreamEventTypeEval(int streamNum) + { + _streamNum = streamNum; + } + + public object Evaluate(EvaluateParams evaluateParams) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QExprTypeof(); } + var theEvent = evaluateParams.EventsPerStream[_streamNum]; + if (theEvent == null) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprTypeof(null); } + return null; + } + if (theEvent is VariantEvent) + { + var typeName = ((VariantEvent)theEvent).UnderlyingEventBean.EventType.Name; + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprTypeof(typeName); } + return typeName; + } + if (InstrumentationHelper.ENABLED) + { + var typeName = theEvent.EventType.Name; + InstrumentationHelper.Get().AExprTypeof(typeName); + return typeName; + } + return theEvent.EventType.Name; + } + + public Type ReturnType + { + get { return typeof(string); } + } + } + + public class FragmentTypeEval : ExprEvaluator + { + private readonly int _streamId; + private readonly EventPropertyGetter _getter; + private readonly string _fragmentType; + + public FragmentTypeEval(int streamId, EventType eventType, string resolvedPropertyName) + { + _streamId = streamId; + _getter = eventType.GetGetter(resolvedPropertyName); + _fragmentType = eventType.GetFragmentType(resolvedPropertyName).FragmentType.Name; + } + + public object Evaluate(EvaluateParams evaluateParams) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QExprTypeof(); } + var theEvent = evaluateParams.EventsPerStream[_streamId]; + if (theEvent == null) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprTypeof(null); } + return null; + } + var fragment = _getter.GetFragment(theEvent); + if (fragment == null) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprTypeof(null); } + return null; + } + if (fragment is EventBean) + { + var bean = ((EventBean)fragment); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprTypeof(bean.EventType.Name); } + return bean.EventType.Name; + } + if (fragment.GetType().IsArray) + { + var type = _fragmentType + "[]"; + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprTypeof(type); } + return type; + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprTypeof(null); } + return null; + } + + public Type ReturnType + { + get { return typeof(string); } + } + } + + private class InnerEvaluator : ExprEvaluator + { + private readonly ExprEvaluator _evaluator; + + public InnerEvaluator(ExprEvaluator evaluator) + { + _evaluator = evaluator; + } + + public Type ReturnType + { + get { return typeof(string); } + } + + public object Evaluate(EvaluateParams evaluateParams) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QExprTypeof(); } + var result = _evaluator.Evaluate(evaluateParams); + if (result == null) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprTypeof(null); } + return null; + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprTypeof(result.GetType().Name); } + return result.GetType().Name; + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/CastParserComputer.cs b/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/CastParserComputer.cs new file mode 100755 index 000000000..ce86256ff --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/CastParserComputer.cs @@ -0,0 +1,30 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.funcs.cast +{ + /// Casting and parsing computer. + public interface CasterParserComputer + { + /// + /// Compute an result performing casting and parsing. + /// + /// to process + /// The evaluate parameters. + /// + /// cast or parse result + /// + Object Compute(Object input, EvaluateParams evaluateParams); + + bool IsConstantForConstInput { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/NonumericCasterComputer.cs b/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/NonumericCasterComputer.cs new file mode 100755 index 000000000..4a4cd4cc2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/NonumericCasterComputer.cs @@ -0,0 +1,36 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.funcs.cast +{ + /// Casting and parsing computer. + public class NonnumericCasterComputer : CasterParserComputer + { + private readonly SimpleTypeCaster _caster; + + public NonnumericCasterComputer(SimpleTypeCaster numericTypeCaster) + { + _caster = numericTypeCaster; + } + + public Object Compute(Object input, EvaluateParams evaluateParams) + { + return _caster.Invoke(input); + } + + public bool IsConstantForConstInput + { + get { return true; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/NumericCasterComputer.cs b/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/NumericCasterComputer.cs new file mode 100755 index 000000000..9df646f6e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/NumericCasterComputer.cs @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.funcs.cast +{ + /// Casting and parsing computer. + public class NumericCasterComputer : CasterParserComputer + { + private readonly SimpleTypeCaster _numericTypeCaster; + + public NumericCasterComputer(SimpleTypeCaster numericTypeCaster) + { + _numericTypeCaster = numericTypeCaster; + } + + public Object Compute(Object input, EvaluateParams evaluateParams) + { + if (input.IsNumber()) + { + return _numericTypeCaster.Invoke(input); + } + + return null; + } + + public bool IsConstantForConstInput + { + get { return true; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/StringParserComputer.cs b/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/StringParserComputer.cs new file mode 100755 index 000000000..87345c2dd --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/StringParserComputer.cs @@ -0,0 +1,36 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.funcs.cast +{ + /// Casting and parsing computer. + public class StringParserComputer : CasterParserComputer + { + private readonly SimpleTypeParser _parser; + + public StringParserComputer(SimpleTypeParser parser) + { + _parser = parser; + } + + public Object Compute(Object input, EvaluateParams evaluateParams) + { + return _parser.Invoke(input.ToString()); + } + + public bool IsConstantForConstInput + { + get { return true; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/StringToDateTimeBaseComputer.cs b/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/StringToDateTimeBaseComputer.cs new file mode 100755 index 000000000..60d91325e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/StringToDateTimeBaseComputer.cs @@ -0,0 +1,123 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Globalization; + +using com.espertech.esper.compat; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.funcs.cast +{ + public abstract class StringToDateTimeBaseComputer : CasterParserComputer + { + public event Action HandleParseException; + + /// + /// Gets a value indicating whether this instance is constant for constant input. + /// + /// + /// true if this instance is constant for constant input; otherwise, false. + /// + public abstract bool IsConstantForConstInput { get; } + + /// + /// Returns the date format that should be used for a given invocation. + /// + /// The evaluate parameters. + /// + protected abstract string GetDateFormat(EvaluateParams evaluateParams); + + /// + /// Parses a date time using a standard algorithm given inputs, a format and a timezone. + /// + /// The input. + /// The date format. + /// The time zone information. + /// + protected DateTimeOffset ParseDateTime(string input, string dateFormat, TimeZoneInfo timeZoneInfo) + { + DateTimeOffset dateTime; + + if (DateTimeOffset.TryParseExact(input, dateFormat, null, DateTimeStyles.None, out dateTime) || + DateTimeOffset.TryParseExact(input, dateFormat, null, DateTimeStyles.AssumeLocal, out dateTime) || + DateTimeOffset.TryParseExact(input, dateFormat, null, DateTimeStyles.AssumeUniversal, out dateTime) || + DateTimeOffset.TryParseExact(input, dateFormat, null, DateTimeStyles.AdjustToUniversal, out dateTime)) + { + return dateTime; + } + + // this will cause a FormatException + return DateTimeOffset.ParseExact(input, dateFormat, null, DateTimeStyles.None); + } + + public abstract object Compute(object input, EvaluateParams evaluateParams); + + /// + /// Called when a format exception occurs. + /// + /// The date time format. + /// The input. + /// The format exception. + protected void OnHandleParseException(string dateTimeFormat, string input, FormatException formatException) + { + if (HandleParseException != null) + { + HandleParseException(dateTimeFormat, input, formatException); + } + } + } + + public abstract class StringToDateTimeBaseComputer : StringToDateTimeBaseComputer + { + /// + /// Computes the value from the date format and input. + /// + /// The date format. + /// The input. + /// + protected abstract T ComputeFromFormat(string dateFormat, string input); + + /// + /// Compute an result performing casting and parsing. + /// + /// to process + /// The evaluate parameters. + /// + /// cast or parse result + /// + /// + public override Object Compute(Object input, EvaluateParams evaluateParams) + { + // Get the format that should be used + var dateTimeFormat = GetDateFormat(evaluateParams); + + // Compute the date from the format + try + { + return ComputeFromFormat(dateTimeFormat, (string) input); + } + catch (FormatException ex) + { + OnHandleParseException(dateTimeFormat, input.ToString(), ex); + throw new IllegalStateException(); + } + } + + /// + /// Adds the parse handler. + /// + /// The handler. + /// + public StringToDateTimeBaseComputer AddParseHandler(Action handler) + { + HandleParseException += handler; + return this; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/StringToDateTimeBaseWDynamicFormatComputer.cs b/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/StringToDateTimeBaseWDynamicFormatComputer.cs new file mode 100755 index 000000000..0881d9495 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/StringToDateTimeBaseWDynamicFormatComputer.cs @@ -0,0 +1,64 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.funcs.cast +{ + public abstract class StringToDateTimeBaseWDynamicFormat : StringToDateTimeBaseComputer + { + private readonly ExprEvaluator _dateFormatEval; + + /// + /// Initializes a new instance of the class. + /// + /// The date format eval. + protected StringToDateTimeBaseWDynamicFormat(ExprEvaluator dateFormatEval) + { + _dateFormatEval = dateFormatEval; + } + + /// + /// Gets a value indicating whether this instance is constant for constant input. + /// + /// + /// true if this instance is constant for constant input; otherwise, false. + /// + public override bool IsConstantForConstInput + { + get { return false; } + } + + /// + /// Returns the date format that should be used for a given invocation. + /// + /// The evaluate parameters. + /// + /// + /// Null date format returned by 'dateformat' expression + /// or + /// DateFormat returned by expression was of incorrect type + /// + protected override string GetDateFormat(EvaluateParams evaluateParams) + { + var dateFormat = _dateFormatEval.Evaluate(evaluateParams); + if (dateFormat == null) + { + throw new EPException("Null date format returned by 'dateformat' expression"); + } + + if (dateFormat is string) + { + return (string) dateFormat; + } + + throw new EPException("DateFormat returned by expression was of incorrect type"); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/StringToDateTimeBaseWStaticFormatComputer.cs b/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/StringToDateTimeBaseWStaticFormatComputer.cs new file mode 100755 index 000000000..71eb43841 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/StringToDateTimeBaseWStaticFormatComputer.cs @@ -0,0 +1,47 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.funcs.cast +{ + public abstract class StringToDateTimeBaseWStaticFormatComputer : StringToDateTimeBaseComputer + { + private readonly string _dateFormat; + + /// + /// Initializes a new instance of the class. + /// + /// The date format. + protected StringToDateTimeBaseWStaticFormatComputer(string dateFormat) + { + _dateFormat = dateFormat; + } + + /// + /// Gets a value indicating whether this instance is constant for constant input. + /// + /// + /// true if this instance is constant for constant input; otherwise, false. + /// + public override bool IsConstantForConstInput + { + get { return true; } + } + + /// + /// Returns the date format that should be used for a given invocation. + /// + /// The evaluate parameters. + /// + protected override string GetDateFormat(EvaluateParams evaluateParams) + { + return _dateFormat; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/StringToDateTimeBaseWStaticISOFormatComputer.cs b/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/StringToDateTimeBaseWStaticISOFormatComputer.cs new file mode 100755 index 000000000..942105a4f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/StringToDateTimeBaseWStaticISOFormatComputer.cs @@ -0,0 +1,68 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.pattern.observer; + +namespace com.espertech.esper.epl.expression.funcs.cast +{ + public abstract class StringToDateTimeBaseWStaticISOFormatComputer : StringToDateTimeBaseComputer + { + private readonly TimeZoneInfo _timeZoneInfo; + + /// + /// Initializes a new instance of the class. + /// + /// The time zone information. + protected StringToDateTimeBaseWStaticISOFormatComputer(TimeZoneInfo timeZoneInfo) + { + _timeZoneInfo = timeZoneInfo; + } + + /// + /// Gets a value indicating whether this instance is constant for constant input. + /// + /// + /// true if this instance is constant for constant input; otherwise, false. + /// + public override bool IsConstantForConstInput + { + get { return true; } + } + + /// + /// Returns the date format that should be used for a given invocation. + /// + /// The evaluate parameters. + /// + protected override string GetDateFormat(EvaluateParams evaluateParams) + { + return null; + } + + /// + /// Parses the date using an ISO8601 parser. + /// + /// The input. + /// if set to true [translate to target time zone]. + /// + protected DateTimeEx ParseISO(string input, bool translateToTargetTimeZone = true) + { + var dateTimeEx = TimerScheduleISO8601Parser.ParseDate(input); + if (translateToTargetTimeZone && (_timeZoneInfo != null)) + { + dateTimeEx = DateTimeEx.GetInstance(_timeZoneInfo, dateTimeEx.DateTime.TranslateTo(_timeZoneInfo)); + } + + return dateTimeEx; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/StringToDateTimeLongWDynamicFormatComputer.cs b/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/StringToDateTimeLongWDynamicFormatComputer.cs new file mode 100755 index 000000000..ac044b512 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/StringToDateTimeLongWDynamicFormatComputer.cs @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.funcs.cast +{ + public class StringToDateTimeLongWDynamicFormatComputer : StringToDateTimeBaseWDynamicFormat + { + /// + /// Initializes a new instance of the class. + /// + /// The date format evaluator. + public StringToDateTimeLongWDynamicFormatComputer(ExprEvaluator dateFormatEval) + : base(dateFormatEval) + { + } + + /// + /// Computes the value from the date format and input. + /// + /// The date format. + /// The input. + /// + protected override long ComputeFromFormat(string dateFormat, string input) + { + return ParseDateTime(input, dateFormat, TimeZoneInfo.Utc).TimeInMillis(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/StringToDateTimeLongWStaticFormatComputer.cs b/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/StringToDateTimeLongWStaticFormatComputer.cs new file mode 100755 index 000000000..7aabbef6f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/StringToDateTimeLongWStaticFormatComputer.cs @@ -0,0 +1,36 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; + +namespace com.espertech.esper.epl.expression.funcs.cast +{ + public class StringToDateTimeLongWStaticFormatComputer : StringToDateTimeBaseWStaticFormatComputer + { + /// + /// Initializes a new instance of the class. + /// + /// The date format. + public StringToDateTimeLongWStaticFormatComputer(string dateFormat) : base(dateFormat) + { + } + + /// + /// Computes the value from the date format and input. + /// + /// The date format. + /// The input. + /// + protected override long ComputeFromFormat(string dateFormat, string input) + { + return ParseDateTime(input, dateFormat, TimeZoneInfo.Utc).TimeInMillis(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/StringToDateTimeLongWStaticISOFormatComputer.cs b/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/StringToDateTimeLongWStaticISOFormatComputer.cs new file mode 100755 index 000000000..6f22d47ad --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/StringToDateTimeLongWStaticISOFormatComputer.cs @@ -0,0 +1,36 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; + +namespace com.espertech.esper.epl.expression.funcs.cast +{ + public class StringToDateTimeLongWStaticISOFormatComputer : StringToDateTimeBaseWStaticISOFormatComputer + { + /// + /// Initializes a new instance of the class. + /// + /// The time zone information. + public StringToDateTimeLongWStaticISOFormatComputer(TimeZoneInfo timeZoneInfo) : base(timeZoneInfo) + { + } + + /// + /// Computes datetime from the dateFormat and input. + /// + /// The date format. + /// The input. + /// + protected override long ComputeFromFormat(string dateFormat, string input) + { + return ParseISO(input).TimeInMillis; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/StringToDateTimeWDynamicFormatComputer.cs b/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/StringToDateTimeWDynamicFormatComputer.cs new file mode 100755 index 000000000..9d999423e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/StringToDateTimeWDynamicFormatComputer.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.funcs.cast +{ + public class StringToDateTimeWDynamicFormatComputer : StringToDateTimeBaseWDynamicFormat + { + /// + /// Initializes a new instance of the class. + /// + /// The date format evaluator. + public StringToDateTimeWDynamicFormatComputer(ExprEvaluator dateFormatEval) + : base(dateFormatEval) + { + } + + /// + /// Computes the value from the date format and input. + /// + /// The date format. + /// The input. + /// + protected override DateTime ComputeFromFormat(string dateFormat, string input) + { + return ParseDateTime(input, dateFormat, TimeZoneInfo.Utc).DateTime; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/StringToDateTimeWStaticFormatComputer.cs b/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/StringToDateTimeWStaticFormatComputer.cs new file mode 100755 index 000000000..faa34b041 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/StringToDateTimeWStaticFormatComputer.cs @@ -0,0 +1,35 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.expression.funcs.cast +{ + public class StringToDateTimeWStaticFormatComputer : StringToDateTimeBaseWStaticFormatComputer + { + /// + /// Initializes a new instance of the class. + /// + /// The date format. + public StringToDateTimeWStaticFormatComputer(string dateFormat) + : base(dateFormat) + { + } + + /// + /// Computes from format. + /// + /// The date format. + /// The input. + /// + protected override DateTime ComputeFromFormat(string dateFormat, string input) + { + return ParseDateTime(input, dateFormat, TimeZoneInfo.Utc).DateTime; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/StringToDateTimeWStaticISOFormatComputer.cs b/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/StringToDateTimeWStaticISOFormatComputer.cs new file mode 100755 index 000000000..b87de5c8f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/StringToDateTimeWStaticISOFormatComputer.cs @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.expression.funcs.cast +{ + public class StringToDateTimeWStaticISOFormatComputer : StringToDateTimeBaseWStaticISOFormatComputer + { + /// + /// Initializes a new instance of the class. + /// + public StringToDateTimeWStaticISOFormatComputer() : base(null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The time zone information. + public StringToDateTimeWStaticISOFormatComputer(TimeZoneInfo timeZoneInfo) : base(timeZoneInfo) + { + } + + /// + /// Computes datetime from the dateFormat and input. + /// + /// The date format. + /// The input. + /// + protected override DateTime ComputeFromFormat(string dateFormat, string input) + { + return ParseISO(input).DateTime.DateTime; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/StringToDtoWDynamicFormatComputer.cs b/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/StringToDtoWDynamicFormatComputer.cs new file mode 100755 index 000000000..b9f4398f0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/StringToDtoWDynamicFormatComputer.cs @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.funcs.cast +{ + public class StringToDtoWDynamicFormatComputer : StringToDateTimeBaseWDynamicFormat + { + private readonly TimeZoneInfo _timeZoneInfo; + + /// + /// Initializes a new instance of the class. + /// + /// The date format eval. + public StringToDtoWDynamicFormatComputer(ExprEvaluator dateFormatEval, TimeZoneInfo timeZoneInfo) + : base(dateFormatEval) + { + _timeZoneInfo = timeZoneInfo; + } + + /// + /// Computes the value from the date format and input. + /// + /// The date format. + /// The input. + /// + protected override DateTimeOffset ComputeFromFormat(string dateFormat, string input) + { + return ParseDateTime(input, dateFormat, _timeZoneInfo); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/StringToDtoWStaticFormatComputer.cs b/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/StringToDtoWStaticFormatComputer.cs new file mode 100755 index 000000000..c4306cdaa --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/StringToDtoWStaticFormatComputer.cs @@ -0,0 +1,39 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.expression.funcs.cast +{ + public class StringToDtoWStaticFormatComputer : StringToDateTimeBaseWStaticFormatComputer + { + private readonly TimeZoneInfo _timeZoneInfo; + + /// + /// Initializes a new instance of the class. + /// + /// The date format. + /// The time zone information. + public StringToDtoWStaticFormatComputer(string dateFormat, TimeZoneInfo timeZoneInfo) + : base(dateFormat) + { + _timeZoneInfo = timeZoneInfo; + } + + /// + /// Computes result from the dateFormat and input. + /// + /// The date format. + /// The input. + /// + protected override DateTimeOffset ComputeFromFormat(string dateFormat, string input) + { + return ParseDateTime(input, dateFormat, _timeZoneInfo); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/StringToDtoWStaticISOFormatComputer.cs b/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/StringToDtoWStaticISOFormatComputer.cs new file mode 100755 index 000000000..75e5e4dc8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/StringToDtoWStaticISOFormatComputer.cs @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.expression.funcs.cast +{ + public class StringToDtoWStaticISOFormatComputer : StringToDateTimeBaseWStaticISOFormatComputer + { + /// + /// Initializes a new instance of the class. + /// + /// The time zone information. + public StringToDtoWStaticISOFormatComputer(TimeZoneInfo timeZoneInfo) : base(timeZoneInfo) + { + } + + /// + /// Computes the value from the format and input. + /// + /// The date format. + /// The input. + /// + protected override DateTimeOffset ComputeFromFormat(string dateFormat, string input) + { + return ParseISO(input).DateTime; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/StringToDtxWDynamicFormatComputer.cs b/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/StringToDtxWDynamicFormatComputer.cs new file mode 100755 index 000000000..a6071b082 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/StringToDtxWDynamicFormatComputer.cs @@ -0,0 +1,42 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.funcs.cast +{ + public class StringToDtxWDynamicFormatComputer : StringToDateTimeBaseWDynamicFormat + { + private readonly TimeZoneInfo _timeZoneInfo; + + /// + /// Initializes a new instance of the class. + /// + /// The date format eval. + /// The time zone information. + public StringToDtxWDynamicFormatComputer(ExprEvaluator dateFormatEval, TimeZoneInfo timeZoneInfo) + : base(dateFormatEval) + { + _timeZoneInfo = timeZoneInfo; + } + + /// + /// Computes from format. + /// + /// The date format. + /// The input. + /// + protected override DateTimeEx ComputeFromFormat(string dateFormat, string input) + { + return DateTimeEx.GetInstance(_timeZoneInfo, ParseDateTime(input, dateFormat, _timeZoneInfo)); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/StringToDtxWStaticFormatComputer.cs b/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/StringToDtxWStaticFormatComputer.cs new file mode 100755 index 000000000..a758ebdf4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/StringToDtxWStaticFormatComputer.cs @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; + +namespace com.espertech.esper.epl.expression.funcs.cast +{ + public class StringToDtxWStaticFormatComputer : StringToDateTimeBaseWStaticFormatComputer + { + private readonly TimeZoneInfo _timeZoneInfo; + + /// + /// Initializes a new instance of the class. + /// + /// The date format eval. + /// The time zone information. + public StringToDtxWStaticFormatComputer(string dateFormat, TimeZoneInfo timeZoneInfo) + : base(dateFormat) + { + _timeZoneInfo = timeZoneInfo; + } + + /// + /// Computes from format. + /// + /// The date format. + /// The input. + /// + protected override DateTimeEx ComputeFromFormat(string dateFormat, string input) + { + return DateTimeEx.GetInstance(_timeZoneInfo, ParseDateTime(input, dateFormat, _timeZoneInfo)); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/StringToDtxWStaticISOFormatComputer.cs b/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/StringToDtxWStaticISOFormatComputer.cs new file mode 100755 index 000000000..e770ee36b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/StringToDtxWStaticISOFormatComputer.cs @@ -0,0 +1,36 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; + +namespace com.espertech.esper.epl.expression.funcs.cast +{ + public class StringToDtxWStaticISOFormatComputer : StringToDateTimeBaseWStaticISOFormatComputer + { + /// + /// Initializes a new instance of the class. + /// + /// The time zone information. + public StringToDtxWStaticISOFormatComputer(TimeZoneInfo timeZoneInfo) : base(timeZoneInfo) + { + } + + /// + /// Computes the value from the format and input. + /// + /// The date format. + /// The input. + /// + protected override DateTimeEx ComputeFromFormat(string dateFormat, string input) + { + return ParseISO(input); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/StringXFormComputer.cs b/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/StringXFormComputer.cs new file mode 100755 index 000000000..d4cfc62d2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/funcs/cast/StringXFormComputer.cs @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.funcs.cast +{ + /// Casting and parsing computer. + public class StringXFormComputer : CasterParserComputer + { + public Object Compute(Object input, EvaluateParams evaluateParams) + { + return input.ToString(); + } + + public bool IsConstantForConstInput + { + get { return true; } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/expression/methodagg/ExprAvedevNode.cs b/NEsper.Core/NEsper.Core/epl/expression/methodagg/ExprAvedevNode.cs new file mode 100755 index 000000000..9bec15c75 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/methodagg/ExprAvedevNode.cs @@ -0,0 +1,57 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.methodagg +{ + /// + /// Represents the Avedev(...) aggregate function is an expression tree. + /// + [Serializable] + public class ExprAvedevNode : ExprAggregateNodeBase + { + private bool _hasFilter; + + /// + /// Ctor. + /// + /// flag indicating unique or non-unique value aggregation + public ExprAvedevNode(bool distinct) + : base(distinct) + { + } + + public override AggregationMethodFactory ValidateAggregationChild(ExprValidationContext validationContext) + { + _hasFilter = PositionalParams.Length > 1; + var childType = base.ValidateNumericChildAllowFilter(_hasFilter); + return validationContext.EngineImportService.AggregationFactoryFactory.MakeAvedev( + validationContext.StatementExtensionSvcContext, this, childType, PositionalParams); + } + + protected override bool EqualsNodeAggregateMethodOnly(ExprAggregateNode node) + { + return node is ExprAvedevNode; + } + + public override string AggregationFunctionName + { + get { return "avedev"; } + } + + public bool HasFilter + { + get { return _hasFilter; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/methodagg/ExprAvgNode.cs b/NEsper.Core/NEsper.Core/epl/expression/methodagg/ExprAvgNode.cs new file mode 100755 index 000000000..7a7e0d218 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/methodagg/ExprAvgNode.cs @@ -0,0 +1,56 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.methodagg +{ + /// + /// Represents the Avg(...) aggregate function is an expression tree. + /// + [Serializable] + public class ExprAvgNode : ExprAggregateNodeBase + { + private bool _hasFilter; + + /// + /// Ctor. + /// + /// flag indicating unique or non-unique value aggregation + public ExprAvgNode(bool distinct) + : base(distinct) + { + } + + public override AggregationMethodFactory ValidateAggregationChild(ExprValidationContext validationContext) + { + _hasFilter = PositionalParams.Length > 1; + var childType = base.ValidateNumericChildAllowFilter(_hasFilter); + return validationContext.EngineImportService.AggregationFactoryFactory.MakeAvg(validationContext.StatementExtensionSvcContext, this, childType, validationContext.EngineImportService.DefaultMathContext); + } + + public override string AggregationFunctionName + { + get { return "avg"; } + } + + protected override bool EqualsNodeAggregateMethodOnly(ExprAggregateNode node) + { + return node is ExprAvgNode; + } + + public bool HasFilter + { + get { return _hasFilter; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/methodagg/ExprCountEverNode.cs b/NEsper.Core/NEsper.Core/epl/expression/methodagg/ExprCountEverNode.cs new file mode 100755 index 000000000..4275596ac --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/methodagg/ExprCountEverNode.cs @@ -0,0 +1,72 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.methodagg +{ + /// + /// Represents the "countever" aggregate function is an expression tree. + /// + [Serializable] + public class ExprCountEverNode : ExprAggregateNodeBase + { + /// + /// Ctor. + /// + /// flag indicating unique or non-unique value aggregation + public ExprCountEverNode(bool distinct) + : base(distinct) + { + } + + public override AggregationMethodFactory ValidateAggregationChild(ExprValidationContext validationContext) + { + if (PositionalParams.Length > 2) + { + throw MakeExceptionExpectedParamNum(0, 2); + } + if (base.IsDistinct) { + throw new ExprValidationException("Aggregation function '" + AggregationFunctionName + "' does now allow distinct"); + } + + bool ignoreNulls = false; + if (PositionalParams.Length == 0) { + // no parameters is allowed + } + else { + ignoreNulls = !(PositionalParams[0] is ExprWildcard); + if (PositionalParams.Length == 2) + { + base.ValidateFilter(PositionalParams[1].ExprEvaluator); + } + } + + return validationContext.EngineImportService.AggregationFactoryFactory.MakeCountEver(validationContext.StatementExtensionSvcContext, this, ignoreNulls); + } + + public bool HasFilter + { + get { return PositionalParams.Length == 2; } + } + + public override string AggregationFunctionName + { + get { return "countever"; } + } + + protected override bool EqualsNodeAggregateMethodOnly(ExprAggregateNode node) + { + return node is ExprCountEverNode; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/methodagg/ExprCountNode.cs b/NEsper.Core/NEsper.Core/epl/expression/methodagg/ExprCountNode.cs new file mode 100755 index 000000000..d07788986 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/methodagg/ExprCountNode.cs @@ -0,0 +1,91 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.methodagg +{ + /// + /// Represents the Count(...) and Count(*) and Count(distinct ...) aggregate function is an expression tree. + /// + [Serializable] + public class ExprCountNode : ExprAggregateNodeBase + { + private bool _hasFilter; + + /// + /// Ctor. + /// + /// flag indicating unique or non-unique value aggregation + public ExprCountNode(bool distinct) + : base(distinct) + { + } + + public override AggregationMethodFactory ValidateAggregationChild(ExprValidationContext validationContext) + { + if (PositionalParams.Length > 2 || PositionalParams.Length == 0) + { + throw MakeExceptionExpectedParamNum(1, 2); + } + + Type childType = null; + bool ignoreNulls = false; + + if (PositionalParams.Length == 1 && PositionalParams[0] is ExprWildcard) + { + ValidateNotDistinct(); + // defaults + } + else if (PositionalParams.Length == 1) { + childType = PositionalParams[0].ExprEvaluator.ReturnType; + ignoreNulls = true; + } + else if (PositionalParams.Length == 2) { + _hasFilter = true; + if (!(PositionalParams[0] is ExprWildcard)) { + childType = PositionalParams[0].ExprEvaluator.ReturnType; + ignoreNulls = true; + } + else { + ValidateNotDistinct(); + } + base.ValidateFilter(PositionalParams[1].ExprEvaluator); + } + + return validationContext.EngineImportService.AggregationFactoryFactory.MakeCount(validationContext.StatementExtensionSvcContext, this, ignoreNulls, childType); + } + + public override string AggregationFunctionName + { + get { return "count"; } + } + + public bool HasFilter + { + get { return _hasFilter; } + } + + protected override bool EqualsNodeAggregateMethodOnly(ExprAggregateNode node) + { + return node is ExprCountNode; + } + + private void ValidateNotDistinct() + { + if (base.IsDistinct) + { + throw new ExprValidationException("Invalid use of the 'distinct' keyword with count and wildcard"); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/methodagg/ExprFirstEverNode.cs b/NEsper.Core/NEsper.Core/epl/expression/methodagg/ExprFirstEverNode.cs new file mode 100755 index 000000000..762565d36 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/methodagg/ExprFirstEverNode.cs @@ -0,0 +1,59 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.methodagg +{ + /// + /// Represents the "firstever" aggregate function is an expression tree. + /// + [Serializable] + public class ExprFirstEverNode : ExprAggregateNodeBase + { + /// Ctor. + /// flag indicating unique or non-unique value aggregation + public ExprFirstEverNode(bool distinct) + : base(distinct) + { + } + + public override AggregationMethodFactory ValidateAggregationChild(ExprValidationContext validationContext) + { + if (PositionalParams.Length > 2) + { + throw MakeExceptionExpectedParamNum(0, 2); + } + if (PositionalParams.Length == 2) + { + base.ValidateFilter(PositionalParams[1].ExprEvaluator); + } + + return validationContext.EngineImportService.AggregationFactoryFactory.MakeFirstEver(validationContext.StatementExtensionSvcContext, this, PositionalParams[0].ExprEvaluator.ReturnType); + } + + public bool HasFilter + { + get { return PositionalParams.Length == 2; } + } + + public override string AggregationFunctionName + { + get { return "firstever"; } + } + + protected override bool EqualsNodeAggregateMethodOnly(ExprAggregateNode node) + { + return node is ExprFirstEverNode; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/methodagg/ExprLastEverNode.cs b/NEsper.Core/NEsper.Core/epl/expression/methodagg/ExprLastEverNode.cs new file mode 100755 index 000000000..9ab5b8a7e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/methodagg/ExprLastEverNode.cs @@ -0,0 +1,62 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.methodagg +{ + /// + /// Represents the "lastever" aggregate function is an expression tree. + /// + [Serializable] + public class ExprLastEverNode : ExprAggregateNodeBase + { + /// + /// Ctor. + /// + /// flag indicating unique or non-unique value aggregation + public ExprLastEverNode(bool distinct) + : base(distinct) + { + } + + public override AggregationMethodFactory ValidateAggregationChild(ExprValidationContext validationContext) + { + if (PositionalParams.Length == 0 || PositionalParams.Length > 2) + { + throw MakeExceptionExpectedParamNum(0, 2); + } + if (PositionalParams.Length == 2) + { + base.ValidateFilter(PositionalParams[1].ExprEvaluator); + } + return validationContext.EngineImportService.AggregationFactoryFactory.MakeLastEver(validationContext.StatementExtensionSvcContext, this, PositionalParams[0].ExprEvaluator.ReturnType); + } + + public bool HasFilter + { + get { return PositionalParams.Length == 2; } + } + + public override string AggregationFunctionName + { + get { return "lastever"; } + } + + protected override bool EqualsNodeAggregateMethodOnly(ExprAggregateNode node) + { + return node is ExprLastEverNode; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/methodagg/ExprLeavingAggNode.cs b/NEsper.Core/NEsper.Core/epl/expression/methodagg/ExprLeavingAggNode.cs new file mode 100755 index 000000000..124d44b98 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/methodagg/ExprLeavingAggNode.cs @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.methodagg +{ + /// + /// Represents the Leaving() aggregate function is an expression tree. + /// + [Serializable] + public class ExprLeavingAggNode : ExprAggregateNodeBase + { + /// Ctor. + /// flag indicating unique or non-unique value aggregation + public ExprLeavingAggNode(bool distinct) + : base(distinct) + { + } + + public override AggregationMethodFactory ValidateAggregationChild(ExprValidationContext validationContext) + { + if (PositionalParams.Length > 0) + { + throw MakeExceptionExpectedParamNum(0, 0); + } + + return validationContext.EngineImportService.AggregationFactoryFactory.MakeLeaving(validationContext.StatementExtensionSvcContext, this); + } + + public override string AggregationFunctionName + { + get { return "leaving"; } + } + + protected override bool EqualsNodeAggregateMethodOnly(ExprAggregateNode node) + { + return node is ExprLeavingAggNode; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/methodagg/ExprMedianNode.cs b/NEsper.Core/NEsper.Core/epl/expression/methodagg/ExprMedianNode.cs new file mode 100755 index 000000000..b363222d5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/methodagg/ExprMedianNode.cs @@ -0,0 +1,61 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.methodagg +{ + /// + /// Represents the Median(...) aggregate function is an expression tree. + /// + [Serializable] + public class ExprMedianNode : ExprAggregateNodeBase + { + private bool _hasFilter; + + /// + /// Ctor. + /// + /// flag indicating unique or non-unique value aggregation + public ExprMedianNode(bool distinct) + : base(distinct) + { + } + + public override AggregationMethodFactory ValidateAggregationChild(ExprValidationContext validationContext) + { + _hasFilter = PositionalParams.Length > 1; + var childType = base.ValidateNumericChildAllowFilter(_hasFilter); + return validationContext.EngineImportService.AggregationFactoryFactory.MakeMedian(validationContext.StatementExtensionSvcContext, this, childType); + } + + public override string AggregationFunctionName + { + get { return "median"; } + } + + protected override bool EqualsNodeAggregateMethodOnly(ExprAggregateNode node) + { + if (!(node is ExprMedianNode)) + { + return false; + } + + return true; + } + + public bool HasFilter + { + get { return _hasFilter; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/methodagg/ExprMethodAggUtil.cs b/NEsper.Core/NEsper.Core/epl/expression/methodagg/ExprMethodAggUtil.cs new file mode 100755 index 000000000..f1c343303 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/methodagg/ExprMethodAggUtil.cs @@ -0,0 +1,109 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.methodagg +{ + public class ExprMethodAggUtil + { + public static ExprEvaluator GetDefaultEvaluator(ExprNode[] childNodes, bool join, EventType[] typesPerStream) + { + ExprEvaluator evaluator; + if (childNodes.Length > 1) + { + evaluator = GetMultiNodeEvaluator(childNodes, join, typesPerStream); + } + else if (childNodes.Length > 0) + { + if (childNodes[0] is ExprWildcard) + { + evaluator = GetWildcardEvaluator(typesPerStream, join); + } + else + { + // Use the evaluation node under the aggregation node to obtain the aggregation value + evaluator = childNodes[0].ExprEvaluator; + } + } + else + { + // For aggregation that doesn't evaluate any particular sub-expression, return null on evaluation + evaluator = new ProxyExprEvaluator + { + ProcEvaluate = evaluateParams => null, + ProcReturnType = null + }; + } + return evaluator; + } + + public static ExprEvaluator GetMultiNodeEvaluator(IList childNodes, bool join, EventType[] typesPerStream) + { + var evaluators = new ExprEvaluator[childNodes.Count]; + + // determine constant nodes + int count = 0; + foreach (ExprNode node in childNodes) + { + if (node is ExprWildcard) + { + evaluators[count] = GetWildcardEvaluator(typesPerStream, join); + } + else + { + evaluators[count] = node.ExprEvaluator; + } + count++; + } + + return new ProxyExprEvaluator + { + ProcEvaluate = evaluateParams => + { + var values = new Object[evaluators.Length]; + for (int i = 0; i < evaluators.Length; i++) + { + values[i] = evaluators[i].Evaluate(evaluateParams); + } + return values; + }, + ProcReturnType = () => typeof (Object[]) + }; + } + + private static ExprEvaluator GetWildcardEvaluator(EventType[] typesPerStream, bool isJoin) + { + Type returnType = typesPerStream != null && typesPerStream.Length > 0 + ? typesPerStream[0].UnderlyingType + : null; + if (isJoin || returnType == null) + { + throw new ExprValidationException( + "Invalid use of wildcard (*) for stream selection in a join or an empty from-clause, please use the stream-alias syntax to select a specific stream instead"); + } + return new ProxyExprEvaluator + { + ProcEvaluate = evaluateParams => + { + EventBean @event = evaluateParams.EventsPerStream[0]; + if (@event == null) + { + return null; + } + return @event.Underlying; + }, + ProcReturnType = () => returnType + }; + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/expression/methodagg/ExprMinMaxAggrNode.cs b/NEsper.Core/NEsper.Core/epl/expression/methodagg/ExprMinMaxAggrNode.cs new file mode 100755 index 000000000..4a25dd2a3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/methodagg/ExprMinMaxAggrNode.cs @@ -0,0 +1,98 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.core.service; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.type; + +namespace com.espertech.esper.epl.expression.methodagg +{ + /// + /// Represents the min/Max(distinct? ...) aggregate function is an expression tree. + /// + [Serializable] + public class ExprMinMaxAggrNode : ExprAggregateNodeBase + { + private readonly MinMaxTypeEnum _minMaxTypeEnum; + private readonly bool _hasFilter; + private readonly bool _isEver; + + public ExprMinMaxAggrNode(bool distinct, MinMaxTypeEnum minMaxTypeEnum, bool hasFilter, bool isEver) + : base(distinct) + { + _minMaxTypeEnum = minMaxTypeEnum; + _hasFilter = hasFilter; + _isEver = isEver; + } + + public override AggregationMethodFactory ValidateAggregationChild(ExprValidationContext validationContext) + { + var positionalParams = PositionalParams; + if (positionalParams.Length == 0 || positionalParams.Length > 2) { + throw new ExprValidationException(_minMaxTypeEnum.ToString() + " node must have either 1 or 2 parameters"); + } + + var child = positionalParams[0]; + bool hasDataWindows; + if (_isEver) { + hasDataWindows = false; + } else { + if (validationContext.ExprEvaluatorContext.StatementType == StatementType.CREATE_TABLE) { + hasDataWindows = true; + } else { + hasDataWindows = ExprNodeUtility.HasRemoveStreamForAggregations(child, validationContext.StreamTypeService, validationContext.IsResettingAggregations); + } + } + + if (_hasFilter) { + if (positionalParams.Length < 2) { + throw new ExprValidationException(_minMaxTypeEnum.ToString() + "-filtered aggregation function must have a filter expression as a second parameter"); + } + base.ValidateFilter(positionalParams[1].ExprEvaluator); + } + return validationContext.EngineImportService.AggregationFactoryFactory.MakeMinMax(validationContext.StatementExtensionSvcContext, this, child.ExprEvaluator.ReturnType, hasDataWindows); + } + + protected override bool EqualsNodeAggregateMethodOnly(ExprAggregateNode node) { + var other = node as ExprMinMaxAggrNode; + if (other == null) + { + return false; + } + return other._minMaxTypeEnum == this._minMaxTypeEnum && other._isEver == this._isEver; + } + + /// + /// Returns the indicator for minimum or maximum. + /// + /// min/max indicator + public MinMaxTypeEnum MinMaxTypeEnum + { + get { return _minMaxTypeEnum; } + } + + public bool HasFilter + { + get { return _hasFilter; } + } + + public override string AggregationFunctionName + { + get { return _minMaxTypeEnum.GetExpressionText(); } + } + + public bool IsEver + { + get { return _isEver; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/methodagg/ExprNthAggNode.cs b/NEsper.Core/NEsper.Core/epl/expression/methodagg/ExprNthAggNode.cs new file mode 100755 index 000000000..fbf7ec2c0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/methodagg/ExprNthAggNode.cs @@ -0,0 +1,61 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.methodagg +{ + /// + /// Represents the Nth(...) and aggregate function is an expression tree. + /// + [Serializable] + public class ExprNthAggNode : ExprAggregateNodeBase + { + /// Ctor. + /// flag indicating unique or non-unique value aggregation + public ExprNthAggNode(bool distinct) + : base(distinct) + { + } + + public override AggregationMethodFactory ValidateAggregationChild(ExprValidationContext validationContext) + { + const string message = "The nth aggregation function requires two parameters, an expression returning aggregation values and a numeric index constant"; + var positionalParams = PositionalParams; + if (positionalParams.Length != 2) { + throw new ExprValidationException(message); + } + + ExprNode first = positionalParams[0]; + ExprNode second = positionalParams[1]; + if (!second.IsConstantResult) { + throw new ExprValidationException(message); + } + + var num = second.ExprEvaluator.Evaluate(new EvaluateParams(null, true, validationContext.ExprEvaluatorContext)); + int size = num.AsInt(); + + return validationContext.EngineImportService.AggregationFactoryFactory.MakeNth(validationContext.StatementExtensionSvcContext, this, first.ExprEvaluator.ReturnType, size); + } + + public override string AggregationFunctionName + { + get { return "nth"; } + } + + protected override bool EqualsNodeAggregateMethodOnly(ExprAggregateNode node) + { + return node is ExprNthAggNode; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/methodagg/ExprPlugInAggNode.cs b/NEsper.Core/NEsper.Core/epl/expression/methodagg/ExprPlugInAggNode.cs new file mode 100755 index 000000000..06f8dc31a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/methodagg/ExprPlugInAggNode.cs @@ -0,0 +1,121 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client.hook; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.accessagg; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.methodagg +{ + /// + /// Represents a custom aggregation function in an expresson tree. + /// + [Serializable] + public class ExprPlugInAggNode + : ExprAggregateNodeBase + , ExprAggregationPlugInNodeMarker + { + [NonSerialized] + private AggregationFunctionFactory _aggregationFunctionFactory; + private readonly string _functionName; + + /// + /// Ctor. + /// + /// flag indicating unique or non-unique value aggregation + /// is the base class for plug-in aggregation functions + /// is the aggregation function name + public ExprPlugInAggNode(bool distinct, AggregationFunctionFactory aggregationFunctionFactory, string functionName) + : base(distinct) + { + _aggregationFunctionFactory = aggregationFunctionFactory; + _functionName = functionName; + aggregationFunctionFactory.FunctionName = functionName; + } + + public override AggregationMethodFactory ValidateAggregationChild(ExprValidationContext validationContext) + { + var positionalParams = PositionalParams; + var parameterTypes = new Type[positionalParams.Length]; + var constant = new object[positionalParams.Length]; + var isConstant = new bool[positionalParams.Length]; + var expressions = new ExprNode[positionalParams.Length]; + + var count = 0; + var hasDataWindows = true; + var evaluateParams = new EvaluateParams(null, true, validationContext.ExprEvaluatorContext); + + foreach (var child in positionalParams) + { + if (child.IsConstantResult) + { + isConstant[count] = true; + constant[count] = child.ExprEvaluator.Evaluate(evaluateParams); + } + parameterTypes[count] = child.ExprEvaluator.ReturnType; + expressions[count] = child; + + if (!ExprNodeUtility.HasRemoveStreamForAggregations(child, validationContext.StreamTypeService, validationContext.IsResettingAggregations)) { + hasDataWindows = false; + } + + if (child is ExprWildcard) { + ExprAggMultiFunctionUtil.CheckWildcardNotJoinOrSubquery(validationContext.StreamTypeService, _functionName); + parameterTypes[count] = validationContext.StreamTypeService.EventTypes[0].UnderlyingType; + isConstant[count] = false; + constant[count] = null; + } + + count++; + } + + var context = new AggregationValidationContext(parameterTypes, isConstant, constant, base.IsDistinct, hasDataWindows, expressions); + try + { + // the aggregation function factory is transient, obtain if not provided + if (_aggregationFunctionFactory == null) { + _aggregationFunctionFactory = validationContext.EngineImportService.ResolveAggregationFactory(_functionName); + } + + _aggregationFunctionFactory.Validate(context); + } + catch (Exception ex) + { + throw new ExprValidationException("Plug-in aggregation function '" + _functionName + "' failed validation: " + ex.Message, ex); + } + + Type childType = null; + if (positionalParams.Length > 0) + { + childType = positionalParams[0].ExprEvaluator.ReturnType; + } + + return validationContext.EngineImportService.AggregationFactoryFactory.MakePlugInMethod(validationContext.StatementExtensionSvcContext, this, _aggregationFunctionFactory, childType); + } + + public override string AggregationFunctionName + { + get { return _functionName; } + } + + protected override bool EqualsNodeAggregateMethodOnly(ExprAggregateNode node) + { + var other = node as ExprPlugInAggNode; + if (other == null) + { + return false; + } + + return ((ExprAggregateNodeBase) other).AggregationFunctionName.Equals(AggregationFunctionName); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/methodagg/ExprRateAggNode.cs b/NEsper.Core/NEsper.Core/epl/expression/methodagg/ExprRateAggNode.cs new file mode 100755 index 000000000..7c90b283d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/methodagg/ExprRateAggNode.cs @@ -0,0 +1,124 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.time; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.methodagg +{ + /// + /// Represents the Rate(...) and aggregate function is an expression tree. + /// + [Serializable] + public class ExprRateAggNode : ExprAggregateNodeBase + { + /// + /// Ctor. + /// + /// - flag indicating unique or non-unique value aggregation + public ExprRateAggNode(bool distinct) + : base(distinct) + { + } + + public override AggregationMethodFactory ValidateAggregationChild(ExprValidationContext validationContext) + { + var positionalParams = base.PositionalParams; + if (positionalParams.Length == 0) + { + throw new ExprValidationException( + "The rate aggregation function minimally requires a numeric constant or expression as a parameter."); + } + + ExprNode first = positionalParams[0]; + if (first.IsConstantResult) + { + const string message = + "The rate aggregation function requires a numeric constant or time period as the first parameter in the constant-value notation"; + long intervalTime; + if (first is ExprTimePeriod) + { + double secInterval = ((ExprTimePeriod) first).EvaluateAsSeconds( + null, true, validationContext.ExprEvaluatorContext); + intervalTime = validationContext.EngineImportService.TimeAbacus.DeltaForSecondsDouble(secInterval); + } + else if (ExprNodeUtility.IsConstantValueExpr(first)) + { + if (!first.ExprEvaluator.ReturnType.IsNumeric()) + { + throw new ExprValidationException(message); + } + var num = + first.ExprEvaluator.Evaluate( + new EvaluateParams(null, true, validationContext.ExprEvaluatorContext)); + intervalTime = validationContext.EngineImportService.TimeAbacus.DeltaForSecondsNumber(num); + } + else + { + throw new ExprValidationException(message); + } + + return + validationContext.EngineImportService.AggregationFactoryFactory.MakeRate( + validationContext.StatementExtensionSvcContext, this, true, intervalTime, + validationContext.TimeProvider, validationContext.EngineImportService.TimeAbacus); + } + + const string messageX = + "The rate aggregation function requires a property or expression returning a non-constant long-type value as the first parameter in the timestamp-property notation"; + Type boxedParamOne = first.ExprEvaluator.ReturnType.GetBoxedType(); + if (boxedParamOne != typeof (long?)) + { + throw new ExprValidationException(messageX); + } + if (first.IsConstantResult) + { + throw new ExprValidationException(messageX); + } + if (first is ExprTimestampNode) + { + throw new ExprValidationException( + "The rate aggregation function does not allow the current engine timestamp as a parameter"); + } + if (positionalParams.Length > 1) + { + if (!TypeHelper.IsNumeric(positionalParams[1].ExprEvaluator.ReturnType)) + { + throw new ExprValidationException( + "The rate aggregation function accepts an expression returning a numeric value to accumulate as an optional second parameter"); + } + } + bool hasDataWindows = ExprNodeUtility.HasRemoveStreamForAggregations( + first, validationContext.StreamTypeService, validationContext.IsResettingAggregations); + if (!hasDataWindows) + { + throw new ExprValidationException( + "The rate aggregation function in the timestamp-property notation requires data windows"); + } + return + validationContext.EngineImportService.AggregationFactoryFactory.MakeRate( + validationContext.StatementExtensionSvcContext, this, false, -1, validationContext.TimeProvider, + validationContext.EngineImportService.TimeAbacus); + } + + public override string AggregationFunctionName + { + get { return "rate"; } + } + + protected override bool EqualsNodeAggregateMethodOnly(ExprAggregateNode node) + { + return node is ExprRateAggNode; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/methodagg/ExprStddevNode.cs b/NEsper.Core/NEsper.Core/epl/expression/methodagg/ExprStddevNode.cs new file mode 100755 index 000000000..925a06fe5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/methodagg/ExprStddevNode.cs @@ -0,0 +1,56 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.methodagg +{ + /// + /// Represents the Stddev(...) aggregate function is an expression tree. + /// + [Serializable] + public class ExprStddevNode : ExprAggregateNodeBase + { + private bool _hasFilter; + + /// + /// Ctor. + /// + /// flag indicating unique or non-unique value aggregation + public ExprStddevNode(bool distinct) + : base(distinct) + { + } + + public override AggregationMethodFactory ValidateAggregationChild(ExprValidationContext validationContext) + { + _hasFilter = PositionalParams.Length > 1; + var childType = ValidateNumericChildAllowFilter(_hasFilter); + return validationContext.EngineImportService.AggregationFactoryFactory.MakeStddev(validationContext.StatementExtensionSvcContext, this, childType); + } + + public bool HasFilter + { + get { return _hasFilter; } + } + + protected override bool EqualsNodeAggregateMethodOnly(ExprAggregateNode node) + { + return node is ExprStddevNode; + } + + public override string AggregationFunctionName + { + get { return "stddev"; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/methodagg/ExprSumNode.cs b/NEsper.Core/NEsper.Core/epl/expression/methodagg/ExprSumNode.cs new file mode 100755 index 000000000..561e4e29e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/methodagg/ExprSumNode.cs @@ -0,0 +1,56 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.methodagg +{ + /// + /// Represents the Sum(...) aggregate function is an expression tree. + /// + [Serializable] + public class ExprSumNode : ExprAggregateNodeBase + { + private bool _hasFilter; + + /// + /// Ctor. + /// + /// flag indicating unique or non-unique value aggregation + public ExprSumNode(bool distinct) + : base(distinct) + { + } + + public override AggregationMethodFactory ValidateAggregationChild(ExprValidationContext validationContext) + { + _hasFilter = PositionalParams.Length > 1; + Type childType = ValidateNumericChildAllowFilter(_hasFilter); + return validationContext.EngineImportService.AggregationFactoryFactory.MakeSum(validationContext.StatementExtensionSvcContext, this, childType); + } + + public override string AggregationFunctionName + { + get { return "sum"; } + } + + protected override bool EqualsNodeAggregateMethodOnly(ExprAggregateNode node) + { + return node is ExprSumNode; + } + + public bool HasFilter + { + get { return _hasFilter; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/ops/ExprAndNode.cs b/NEsper.Core/NEsper.Core/epl/expression/ops/ExprAndNode.cs new file mode 100755 index 000000000..e2db39262 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/ops/ExprAndNode.cs @@ -0,0 +1,17 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.ops +{ + /// Represents an And-condition. + public interface ExprAndNode : ExprNode, ExprEvaluator + { + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/ops/ExprAndNodeImpl.cs b/NEsper.Core/NEsper.Core/epl/expression/ops/ExprAndNodeImpl.cs new file mode 100755 index 000000000..ab5704a0b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/ops/ExprAndNodeImpl.cs @@ -0,0 +1,114 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; +using System.Linq; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.ops +{ + /// Represents an And-condition. + [Serializable] + public class ExprAndNodeImpl : ExprNodeBase, ExprEvaluator, ExprAndNode + { + [NonSerialized] private ExprEvaluator[] _evaluators; + + public ExprAndNodeImpl() + { + } + + public override ExprNode Validate(ExprValidationContext validationContext) + { + _evaluators = ExprNodeUtility.GetEvaluators(ChildNodes); + + // Sub-nodes must be returning bool + if (_evaluators.Select(child => child.ReturnType).Any(childType => !childType.IsBoolean())) + { + throw new ExprValidationException("Incorrect use of AND clause, sub-expressions do not return bool"); + } + + if (ChildNodes.Count <= 1) + { + throw new ExprValidationException("The AND operator requires at least 2 child expressions"); + } + + return null; + } + + public override ExprEvaluator ExprEvaluator + { + get { return this; } + } + + public override bool IsConstantResult + { + get { return false; } + } + + public Type ReturnType + { + get { return typeof (bool?); } + } + + public object Evaluate(EvaluateParams evaluateParams) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QExprAnd(this); } + + bool? result = true; + + unchecked + { + var evaluators = _evaluators; + var evaluatorsLength = evaluators.Length; + + for (int ii = 0; ii < evaluatorsLength; ii++) + { + var evaluated = evaluators[ii].Evaluate(evaluateParams); + if (evaluated == null) + { + result = null; + } + else if (false.Equals(evaluated)) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprAnd(false); } + return false; + } + } + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprAnd(result); } + + return result; + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + String appendStr = ""; + foreach (ExprNode child in ChildNodes) + { + writer.Write(appendStr); + child.ToEPL(writer, Precedence); + appendStr = " and "; + } + } + + public override ExprPrecedenceEnum Precedence + { + get { return ExprPrecedenceEnum.AND; } + } + + public override bool EqualsNode(ExprNode node) + { + return node is ExprAndNodeImpl; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/ops/ExprArrayNode.cs b/NEsper.Core/NEsper.Core/epl/expression/ops/ExprArrayNode.cs new file mode 100755 index 000000000..58c53a382 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/ops/ExprArrayNode.cs @@ -0,0 +1,284 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.ops +{ + /// + /// Represents an array in a filter expressiun tree. + /// + [Serializable] + public class ExprArrayNode + : ExprNodeBase + , ExprEvaluator + , ExprEvaluatorEnumeration + { + private Type _arrayReturnType; + private bool _mustCoerce; + private int _length; + + [NonSerialized] private Coercer _coercer; + [NonSerialized] private Object _constantResult; + [NonSerialized] private ExprEvaluator[] _evaluators; + [NonSerialized] private volatile ICollection _constantResultList; + + /// Ctor. + public ExprArrayNode() + { + } + + public override ExprEvaluator ExprEvaluator + { + get { return this; } + } + + public override ExprNode Validate(ExprValidationContext validationContext) + { + _length = ChildNodes.Count; + _evaluators = ExprNodeUtility.GetEvaluators(ChildNodes); + + // Can be an empty array with no content + if (ChildNodes.Count == 0) + { + _arrayReturnType = typeof(Object); + _constantResult = new Object[0]; + return null; + } + + var comparedTypes = new List(); + for (int i = 0; i < _length; i++) + { + comparedTypes.Add(_evaluators[i].ReturnType); + } + + // Determine common denominator type + try { + _arrayReturnType = TypeHelper.GetCommonCoercionType(comparedTypes.ToArray()); + + // Determine if we need to coerce numbers when one type doesn't match any other type + if (_arrayReturnType.IsNumeric()) + { + _mustCoerce = false; + foreach (var comparedType in comparedTypes) + { + if (comparedType != _arrayReturnType) + { + _mustCoerce = true; + } + } + if (_mustCoerce) + { + _coercer = CoercerFactory.GetCoercer(null, _arrayReturnType); + } + } + } + catch (CoercionException) + { + // expected, such as mixing String and int values, or classes (not boxed) and primitives + // use Object[] in such cases + } + if (_arrayReturnType == null) + { + _arrayReturnType = typeof(Object); + } + + // Determine if we are dealing with constants only + var results = new Object[_length]; + int index = 0; + foreach (ExprNode child in ChildNodes) + { + if (!child.IsConstantResult) + { + results = null; // not using a constant result + break; + } + results[index] = _evaluators[index].Evaluate(new EvaluateParams(null, false, validationContext.ExprEvaluatorContext)); + index++; + } + + // Copy constants into array and coerce, if required + if (results != null) + { + var asArray = Array.CreateInstance(_arrayReturnType, _length); + _constantResult = asArray; + + for (int i = 0; i < _length; i++) + { + if (_mustCoerce) + { + var boxed = results[i]; + if (boxed != null) + { + Object coercedResult = _coercer.Invoke(boxed); + asArray.SetValue(coercedResult, i); + } + } + else + { + asArray.SetValue(results[i], i); + } + } + } + + return null; + } + + public override bool IsConstantResult + { + get { return _constantResult != null; } + } + + public Type ReturnType + { + get { return Array.CreateInstance(_arrayReturnType, 0).GetType(); } + } + + public Object Evaluate(EvaluateParams evaluateParams) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QExprArray(this); } + if (_constantResult != null) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprArray(_constantResult); } + return _constantResult; + } + + Array array = Array.CreateInstance(_arrayReturnType, _length); + + if (_length == 0) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprArray(array); } + return array; + } + + int index = 0; + foreach (ExprEvaluator child in _evaluators) + { + var result = child.Evaluate(evaluateParams); + if (result != null) + { + if (_mustCoerce) + { + Object coercedResult = _coercer.Invoke(result); + array.SetValue(coercedResult, index); + } + else + { + array.SetValue(result, index); + } + } + index++; + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprArray(array); } + return array; + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + String delimiter = ""; + writer.Write("{"); + foreach (ExprNode expr in ChildNodes) + { + writer.Write(delimiter); + expr.ToEPL(writer, ExprPrecedenceEnum.MINIMUM); + delimiter = ","; + } + writer.Write('}'); + } + + public override ExprPrecedenceEnum Precedence + { + get { return ExprPrecedenceEnum.UNARY; } + } + + public Type ComponentTypeCollection + { + get { return _arrayReturnType; } + } + + public ICollection EvaluateGetROCollectionScalar(EvaluateParams evaluateParams) + { + if (_constantResult != null) + { + if (_constantResultList != null) { + return _constantResultList; + } + var list = new List(); + var array = (Array)_constantResult; + for (int i = 0; i < _length; i++) + { + list.Add(array.GetValue(i)); + } + _constantResultList = list; + return list; + } + + if (_length == 0) + { + return new List(); + } + + var resultList = new List(); + + int index = 0; + foreach (ExprEvaluator child in _evaluators) + { + var result = child.Evaluate(evaluateParams); + if (result != null) + { + if (_mustCoerce) + { + Object coercedResult = _coercer.Invoke(result); + resultList.Add(coercedResult); + } + else + { + resultList.Add(result); + } + } + index++; + } + + return resultList; + } + + public EventType GetEventTypeCollection(EventAdapterService eventAdapterService, int statementId) + { + return null; + } + + public ICollection EvaluateGetROCollectionEvents(EvaluateParams evaluateParams) + { + return null; + } + + public EventType GetEventTypeSingle(EventAdapterService eventAdapterService, int statementId) + { + return null; + } + + public EventBean EvaluateGetEventBean(EvaluateParams evaluateParams) + { + return null; + } + + public override bool EqualsNode(ExprNode node) + { + return node is ExprArrayNode; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/ops/ExprBetweenNode.cs b/NEsper.Core/NEsper.Core/epl/expression/ops/ExprBetweenNode.cs new file mode 100755 index 000000000..60eac2eca --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/ops/ExprBetweenNode.cs @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.ops +{ + /// Represents the between-clause function in an expression tree. + public interface ExprBetweenNode : ExprNode, ExprEvaluator + { + /// Returns true if the low endpoint is included, false if not + /// indicator if endppoint is included + bool IsLowEndpointIncluded { get; } + + /// Returns true if the high endpoint is included, false if not + /// indicator if endppoint is included + bool IsHighEndpointIncluded { get; } + + /// Returns true for inverted range, or false for regular (openn/close/half-open/half-closed) ranges. + /// true for not betwene, false for between + bool IsNotBetween { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/ops/ExprBetweenNodeImpl.cs b/NEsper.Core/NEsper.Core/epl/expression/ops/ExprBetweenNodeImpl.cs new file mode 100755 index 000000000..35086c8a5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/ops/ExprBetweenNodeImpl.cs @@ -0,0 +1,685 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +using com.espertech.esper.compat; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.ops +{ + /// + /// Represents the between-clause function in an expression tree. + /// + [Serializable] + public class ExprBetweenNodeImpl + : ExprNodeBase + , ExprEvaluator + , ExprBetweenNode + { + private readonly bool _isLowEndpointIncluded; + private readonly bool _isHighEndpointIncluded; + private readonly bool _isNotBetween; + + private bool _isAlwaysFalse; + [NonSerialized] + private IExprBetweenComp _computer; + [NonSerialized] + private ExprEvaluator[] _evaluators; + + /// Ctor. + /// is true for the regular 'between' or false for "val in (a:b)" (open range), orfalse if the endpoint is not included + /// indicates whether the high endpoint is included + /// is true for 'not between' or 'not in (a:b), or false for a regular between + public ExprBetweenNodeImpl(bool lowEndpointIncluded, bool highEndpointIncluded, bool notBetween) + { + _isLowEndpointIncluded = lowEndpointIncluded; + _isHighEndpointIncluded = highEndpointIncluded; + _isNotBetween = notBetween; + } + + public override ExprEvaluator ExprEvaluator + { + get { return this; } + } + + public override bool IsConstantResult + { + get { return false; } + } + + /// Returns true if the low endpoint is included, false if not + /// indicator if endppoint is included + public bool IsLowEndpointIncluded + { + get { return _isLowEndpointIncluded; } + } + + /// Returns true if the high endpoint is included, false if not + /// indicator if endppoint is included + public bool IsHighEndpointIncluded + { + get { return _isHighEndpointIncluded; } + } + + /// Returns true for inverted range, or false for regular (openn/close/half-open/half-closed) ranges. + /// true for not betwene, false for between + public bool IsNotBetween + { + get { return _isNotBetween; } + } + + public override ExprNode Validate(ExprValidationContext validationContext) + { + if (((ExprNode)this).ChildNodes.Count != 3) + { + throw new ExprValidationException("The Between operator requires exactly 3 child expressions"); + } + + // Must be either numeric or string + _evaluators = ExprNodeUtility.GetEvaluators(((ExprNode)this).ChildNodes); + Type typeOne = _evaluators[0].ReturnType.GetBoxedType(); + Type typeTwo = _evaluators[1].ReturnType.GetBoxedType(); + Type typeThree = _evaluators[2].ReturnType.GetBoxedType(); + + if (typeOne == null) + { + throw new ExprValidationException("Null value not allowed in between-clause"); + } + + Type compareType; + if ((typeTwo == null) || (typeThree == null)) + { + _isAlwaysFalse = true; + } + else + { + if ((typeOne != typeof(String)) || (typeTwo != typeof(String)) || (typeThree != typeof(String))) + { + if (!typeOne.IsNumeric()) + { + throw new ExprValidationException(string.Format("Implicit conversion from datatype '{0}' to numeric is not allowed", typeOne.FullName)); + } + if (!typeTwo.IsNumeric()) + { + throw new ExprValidationException(string.Format("Implicit conversion from datatype '{0}' to numeric is not allowed", typeTwo.FullName)); + } + if (!typeThree.IsNumeric()) + { + throw new ExprValidationException(string.Format("Implicit conversion from datatype '{0}' to numeric is not allowed", typeThree.FullName)); + } + } + + Type intermedType = typeOne.GetCompareToCoercionType(typeTwo); + compareType = intermedType.GetCompareToCoercionType(typeThree); + _computer = MakeComputer(compareType, typeOne, typeTwo, typeThree); + } + + return null; + } + + public Type ReturnType + { + get { return typeof(bool?); } + } + + public Object Evaluate(EvaluateParams evaluateParams) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QExprBetween(this); } + + if (!_isAlwaysFalse) + { + // Evaluate first child which is the base value to compare to + var value = _evaluators[0].Evaluate(evaluateParams); + if (value != null) + { + var lower = _evaluators[1].Evaluate(evaluateParams); + var higher = _evaluators[2].Evaluate(evaluateParams); + var result = _computer.IsBetween(value, lower, higher); + result = _isNotBetween ? result == false : result; + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprBetween(result); } + return result; + } + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprBetween(false); } + + return false; + } + + public override bool EqualsNode(ExprNode node) + { + var other = node as ExprBetweenNodeImpl; + if (other == null) + { + return false; + } + + return other._isNotBetween == _isNotBetween; + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + IEnumerator it = ((ExprNode)this).ChildNodes.Cast().GetEnumerator(); + it.MoveNext(); + it.Current.ToEPL(writer, Precedence); + if (_isNotBetween) + { + writer.Write(" not between "); + } + else + { + writer.Write(" between "); + } + + it.MoveNext(); + it.Current.ToEPL(writer, Precedence); + writer.Write(" and "); + it.MoveNext(); + it.Current.ToEPL(writer, Precedence); + } + + public override ExprPrecedenceEnum Precedence + { + get { return ExprPrecedenceEnum.RELATIONAL_BETWEEN_IN; } + } + + private IExprBetweenComp MakeComputer(Type compareType, Type valueType, Type lowType, Type highType) + { + IExprBetweenComp computer; + + if (compareType == typeof(String)) + { + return new ExprBetweenCompString(_isLowEndpointIncluded, _isHighEndpointIncluded); + } + + if ((compareType == valueType) && (compareType == lowType) && (compareType == highType)) + { + if (compareType == typeof(double?)) + { + return new FastExprBetweenCompDouble(_isLowEndpointIncluded, _isHighEndpointIncluded); + } + else if (compareType == typeof(decimal?)) + { + return new FastExprBetweenCompDecimal(_isLowEndpointIncluded, _isHighEndpointIncluded); + } + else if (compareType == typeof(short?)) + { + return new FastExprBetweenCompInt16(_isLowEndpointIncluded, _isHighEndpointIncluded); + } + else if (compareType == typeof(int?)) + { + return new FastExprBetweenCompInt32(_isLowEndpointIncluded, _isHighEndpointIncluded); + } + else if (compareType == typeof(long?)) + { + return new FastExprBetweenCompInt64(_isLowEndpointIncluded, _isHighEndpointIncluded); + } + } + + if (compareType == typeof(double?)) + { + return new ExprBetweenCompDouble(_isLowEndpointIncluded, _isHighEndpointIncluded); + } + if (compareType == typeof(decimal?)) + { + return new ExprBetweenCompDecimal(_isLowEndpointIncluded, _isHighEndpointIncluded); + } + if (compareType == typeof(short?)) + { + return new ExprBetweenCompInt16(_isLowEndpointIncluded, _isHighEndpointIncluded); + } + if (compareType == typeof(int?)) + { + return new ExprBetweenCompInt32(_isLowEndpointIncluded, _isHighEndpointIncluded); + } + if (compareType == typeof(long?)) + { + return new ExprBetweenCompInt64(_isLowEndpointIncluded, _isHighEndpointIncluded); + } + + return new ExprBetweenCompDouble(_isLowEndpointIncluded, _isHighEndpointIncluded); + } + + private interface IExprBetweenComp + { + bool IsBetween(Object value, Object lower, Object upper); + } + + private class ExprBetweenCompString : IExprBetweenComp + { + private readonly bool _isLowIncluded; + private readonly bool _isHighIncluded; + + public ExprBetweenCompString(bool lowIncluded, bool isHighIncluded) + { + _isLowIncluded = lowIncluded; + _isHighIncluded = isHighIncluded; + } + + public bool IsBetween(Object value, Object lower, Object upper) + { + if ((value == null) || (lower == null) || ((upper == null))) + { + return false; + } + + String valueStr = (String)value; + String lowerStr = (String)lower; + String upperStr = (String)upper; + + if (upperStr.CompareTo(lowerStr) < 0) + { + String temp = upperStr; + upperStr = lowerStr; + lowerStr = temp; + } + + if (valueStr.CompareTo(lowerStr) < 0) + { + return false; + } + if (valueStr.CompareTo(upperStr) > 0) + { + return false; + } + if (!(_isLowIncluded) && valueStr.Equals(lowerStr)) + { + return false; + } + + return (_isHighIncluded) || !valueStr.Equals(upperStr); + } + } + + private class ExprBetweenCompDecimal : IExprBetweenComp + { + private readonly bool _isLowIncluded; + private readonly bool _isHighIncluded; + + public ExprBetweenCompDecimal(bool lowIncluded, bool highIncluded) + { + _isLowIncluded = lowIncluded; + _isHighIncluded = highIncluded; + } + + public bool IsBetween(Object value, Object lower, Object upper) + { + if ((value == null) || (lower == null) || ((upper == null))) + { + return false; + } + + var valueD = value.AsDecimal(); + var lowerD = lower.AsDecimal(); + var upperD = upper.AsDecimal(); + + if (lowerD > upperD) + { + var temp = upperD; + upperD = lowerD; + lowerD = temp; + } + + if (valueD > lowerD) + { + return valueD < upperD || _isHighIncluded && valueD == upperD; + } + + return (_isLowIncluded) && (valueD == lowerD); + } + } + + private class FastExprBetweenCompDecimal : IExprBetweenComp + { + private readonly bool _isLowIncluded; + private readonly bool _isHighIncluded; + + public FastExprBetweenCompDecimal(bool lowIncluded, bool highIncluded) + { + _isLowIncluded = lowIncluded; + _isHighIncluded = highIncluded; + } + + public bool IsBetween(Object value, Object lower, Object upper) + { + if ((value == null) || (lower == null) || ((upper == null))) + { + return false; + } + + var valueD = (decimal)value; + var lowerD = (decimal)lower; + var upperD = (decimal)upper; + + if (lowerD > upperD) + { + var temp = upperD; + upperD = lowerD; + lowerD = temp; + } + + if (valueD > lowerD) + { + return valueD < upperD || _isHighIncluded && valueD == upperD; + } + + return (_isLowIncluded) && (valueD == lowerD); + } + } + + private class FastExprBetweenCompDouble : IExprBetweenComp + { + private readonly bool _isLowIncluded; + private readonly bool _isHighIncluded; + + public FastExprBetweenCompDouble(bool lowIncluded, bool highIncluded) + { + _isLowIncluded = lowIncluded; + _isHighIncluded = highIncluded; + } + + public bool IsBetween(Object value, Object lower, Object upper) + { + if ((value == null) || (lower == null) || ((upper == null))) + { + return false; + } + + double valueD = (double)value; + double lowerD = (double)lower; + double upperD = (double)upper; + + if (lowerD > upperD) + { + double temp = upperD; + upperD = lowerD; + lowerD = temp; + } + + if (valueD > lowerD) + { + return valueD < upperD || _isHighIncluded && valueD == upperD; + } + + return (_isLowIncluded) && (valueD == lowerD); + } + } + + private class ExprBetweenCompDouble : IExprBetweenComp + { + private readonly bool _isLowIncluded; + private readonly bool _isHighIncluded; + + public ExprBetweenCompDouble(bool lowIncluded, bool highIncluded) + { + _isLowIncluded = lowIncluded; + _isHighIncluded = highIncluded; + } + + public bool IsBetween(Object value, Object lower, Object upper) + { + if ((value == null) || (lower == null) || ((upper == null))) + { + return false; + } + + double valueD = value.AsDouble(); + double lowerD = lower.AsDouble(); + double upperD = upper.AsDouble(); + + if (lowerD > upperD) + { + double temp = upperD; + upperD = lowerD; + lowerD = temp; + } + + if (valueD > lowerD) + { + return valueD < upperD || _isHighIncluded && valueD == upperD; + } + + return (_isLowIncluded) && (valueD == lowerD); + } + } + + private class FastExprBetweenCompInt64 : IExprBetweenComp + { + private readonly bool _isLowIncluded; + private readonly bool _isHighIncluded; + + public FastExprBetweenCompInt64(bool lowIncluded, bool highIncluded) + { + _isLowIncluded = lowIncluded; + _isHighIncluded = highIncluded; + } + + public bool IsBetween(Object value, Object lower, Object upper) + { + if ((value == null) || (lower == null) || ((upper == null))) + { + return false; + } + + long valueD = (long)value; + long lowerD = (long)lower; + long upperD = (long)upper; + + if (lowerD > upperD) + { + long temp = upperD; + upperD = lowerD; + lowerD = temp; + } + + if (valueD > lowerD) + { + return valueD < upperD || _isHighIncluded && valueD == upperD; + } + + return (_isLowIncluded) && (valueD == lowerD); + } + } + + private class ExprBetweenCompInt64 : IExprBetweenComp + { + private readonly bool _isLowIncluded; + private readonly bool _isHighIncluded; + + public ExprBetweenCompInt64(bool lowIncluded, bool highIncluded) + { + _isLowIncluded = lowIncluded; + _isHighIncluded = highIncluded; + } + + public bool IsBetween(Object value, Object lower, Object upper) + { + if ((value == null) || (lower == null) || ((upper == null))) + { + return false; + } + + long valueD = value.AsLong(); + long lowerD = lower.AsLong(); + long upperD = upper.AsLong(); + + if (lowerD > upperD) + { + var temp = upperD; + upperD = lowerD; + lowerD = temp; + } + + if (valueD > lowerD) + { + return valueD < upperD || _isHighIncluded && valueD == upperD; + } + + return (_isLowIncluded) && (valueD == lowerD); + } + } + + private class FastExprBetweenCompInt32 : IExprBetweenComp + { + private readonly bool _isLowIncluded; + private readonly bool _isHighIncluded; + + public FastExprBetweenCompInt32(bool lowIncluded, bool highIncluded) + { + _isLowIncluded = lowIncluded; + _isHighIncluded = highIncluded; + } + + public bool IsBetween(Object value, Object lower, Object upper) + { + if ((value == null) || (lower == null) || ((upper == null))) + { + return false; + } + + int valueD = (int)value; + int lowerD = (int)lower; + int upperD = (int)upper; + + if (lowerD > upperD) + { + int temp = upperD; + upperD = lowerD; + lowerD = temp; + } + + if (valueD > lowerD) + { + return valueD < upperD || _isHighIncluded && valueD == upperD; + } + + return (_isLowIncluded) && (valueD == lowerD); + } + } + + private class ExprBetweenCompInt32 : IExprBetweenComp + { + private readonly bool _isLowIncluded; + private readonly bool _isHighIncluded; + + public ExprBetweenCompInt32(bool lowIncluded, bool highIncluded) + { + _isLowIncluded = lowIncluded; + _isHighIncluded = highIncluded; + } + + public bool IsBetween(Object value, Object lower, Object upper) + { + if ((value == null) || (lower == null) || ((upper == null))) + { + return false; + } + + int valueD = value.AsInt(); + int lowerD = lower.AsInt(); + int upperD = upper.AsInt(); + + if (lowerD > upperD) + { + int temp = upperD; + upperD = lowerD; + lowerD = temp; + } + + if (valueD > lowerD) + { + return valueD < upperD || _isHighIncluded && valueD == upperD; + } + + return (_isLowIncluded) && (valueD == lowerD); + } + } + + private class FastExprBetweenCompInt16 : IExprBetweenComp + { + private readonly bool _isLowIncluded; + private readonly bool _isHighIncluded; + + public FastExprBetweenCompInt16(bool lowIncluded, bool highIncluded) + { + _isLowIncluded = lowIncluded; + _isHighIncluded = highIncluded; + } + + public bool IsBetween(Object value, Object lower, Object upper) + { + if ((value == null) || (lower == null) || ((upper == null))) + { + return false; + } + + short valueD = (short)value; + short lowerD = (short)lower; + short upperD = (short)upper; + + if (lowerD > upperD) + { + short temp = upperD; + upperD = lowerD; + lowerD = temp; + } + + if (valueD > lowerD) + { + return valueD < upperD || _isHighIncluded && valueD == upperD; + } + + return (_isLowIncluded) && (valueD == lowerD); + } + } + + private class ExprBetweenCompInt16 : IExprBetweenComp + { + private readonly bool _isLowIncluded; + private readonly bool _isHighIncluded; + + public ExprBetweenCompInt16(bool lowIncluded, bool highIncluded) + { + _isLowIncluded = lowIncluded; + _isHighIncluded = highIncluded; + } + + public bool IsBetween(Object value, Object lower, Object upper) + { + if ((value == null) || (lower == null) || ((upper == null))) + { + return false; + } + + short valueD = value.AsShort(); + short lowerD = lower.AsShort(); + short upperD = upper.AsShort(); + + if (lowerD > upperD) + { + short temp = upperD; + upperD = lowerD; + lowerD = temp; + } + + if (valueD > lowerD) + { + return valueD < upperD || _isHighIncluded && valueD == upperD; + } + + return (_isLowIncluded) && (valueD == lowerD); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/ops/ExprBitWiseNode.cs b/NEsper.Core/NEsper.Core/epl/expression/ops/ExprBitWiseNode.cs new file mode 100755 index 000000000..acaaa111e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/ops/ExprBitWiseNode.cs @@ -0,0 +1,157 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.type; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.ops +{ + /// + /// Represents the bit-wise operators in an expression tree. + /// + [Serializable] + public class ExprBitWiseNode + : ExprNodeBase + , ExprEvaluator + { + private readonly BitWiseOpEnum _bitWiseOpEnum; + [NonSerialized] + private BitWiseOpEnumExtensions.Computer _bitWiseOpEnumComputer; + private Type _returnType; + + [NonSerialized] + private ExprEvaluator[] _evaluators; + + /// Ctor. + /// type of math + public ExprBitWiseNode(BitWiseOpEnum bitWiseOpEnum) + { + _bitWiseOpEnum = bitWiseOpEnum; + } + + public override ExprEvaluator ExprEvaluator + { + get { return this; } + } + + /// Returns the bitwise operator. + /// operator + public BitWiseOpEnum BitWiseOpEnum + { + get { return _bitWiseOpEnum; } + } + + public override ExprNode Validate(ExprValidationContext validationContext) + { + if (ChildNodes.Count != 2) + { + throw new ExprValidationException("BitWise node must have 2 parameters"); + } + + _evaluators = ExprNodeUtility.GetEvaluators(ChildNodes); + foreach (var child in _evaluators) + { + var childType = child.ReturnType; + if ((!childType.IsBoolean()) && (!childType.IsNumeric())) + { + throw new ExprValidationException("Invalid datatype for bitwise " + + childType.Name + " is not allowed"); + } + } + + // Determine result type, set up compute function + var childTypeOne = _evaluators[0].ReturnType; + var childTypeTwo = _evaluators[1].ReturnType; + if ((childTypeOne.IsFloatingPointClass()) || (childTypeTwo.IsFloatingPointClass())) + { + throw new ExprValidationException("Invalid type for bitwise " + _bitWiseOpEnum.GetComputeDescription() + " operator"); + } + else + { + var childBoxedTypeOne = childTypeOne.GetBoxedType(); + var childBoxedTypeTwo = childTypeTwo.GetBoxedType(); + if (childBoxedTypeOne == childBoxedTypeTwo) + { + _returnType = childBoxedTypeOne; + _bitWiseOpEnumComputer = _bitWiseOpEnum.GetComputer(_returnType); + } + else + { + throw new ExprValidationException("Bitwise expressions must be of the same type for bitwise " + _bitWiseOpEnum.GetComputeDescription() + " operator"); + } + } + + return null; + } + + public override bool IsConstantResult + { + get { return false; } + } + + public Type ReturnType + { + get { return _returnType; } + } + + public object Evaluate(EvaluateParams evaluateParams) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QExprBitwise(this, _bitWiseOpEnum); } + var valueChildOne = _evaluators[0].Evaluate(evaluateParams); + var valueChildTwo = _evaluators[1].Evaluate(evaluateParams); + + if ((valueChildOne == null) || (valueChildTwo == null)) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprBitwise(null); } + return null; + } + + // bitWiseOpEnumComputer is initialized by validation + if (InstrumentationHelper.ENABLED) + { + var result = _bitWiseOpEnumComputer.Invoke(valueChildOne, valueChildTwo); + InstrumentationHelper.Get().AExprBitwise(result); + return result; + } + return _bitWiseOpEnumComputer.Invoke(valueChildOne, valueChildTwo); + } + + public override bool EqualsNode(ExprNode node) + { + if (!(node is ExprBitWiseNode)) + { + return false; + } + + var other = (ExprBitWiseNode)node; + if (other._bitWiseOpEnum != _bitWiseOpEnum) + { + return false; + } + + return true; + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + ChildNodes[0].ToEPL(writer, Precedence); + writer.Write(_bitWiseOpEnum.GetComputeDescription()); + ChildNodes[1].ToEPL(writer, Precedence); + } + + public override ExprPrecedenceEnum Precedence + { + get { return ExprPrecedenceEnum.BITWISE; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/ops/ExprConcatNode.cs b/NEsper.Core/NEsper.Core/epl/expression/ops/ExprConcatNode.cs new file mode 100755 index 000000000..c72c713b6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/ops/ExprConcatNode.cs @@ -0,0 +1,91 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; +using System.Text; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.expression.ops +{ + /// + /// Represents a string concatenation. + /// + [Serializable] + public class ExprConcatNode : ExprNodeBase + { + private ExprEvaluator _evaluator; + + public override ExprNode Validate(ExprValidationContext validationContext) + { + if (ChildNodes.Count < 2) + { + throw new ExprValidationException("Concat node must have at least 2 parameters"); + } + + ExprEvaluator[] evaluators = ExprNodeUtility.GetEvaluators(this.ChildNodes); + + for (var i = 0; i < evaluators.Length; i++) + { + var childType = evaluators[i].ReturnType; + var childTypeName = childType == null ? "null" : Name.Of(childType); + if (childType != typeof(String)) + { + throw new ExprValidationException(string.Format("Implicit conversion from datatype '{0}' to string is not allowed", childTypeName)); + } + } + + ConfigurationEngineDefaults.ThreadingProfile threadingProfile = validationContext.EngineImportService.ThreadingProfile; + if (threadingProfile == ConfigurationEngineDefaults.ThreadingProfile.LARGE) + { + _evaluator = new ExprConcatNodeEvalWNew(this, evaluators); + } + else + { + _evaluator = new ExprConcatNodeEvalThreadLocal(this, evaluators); + } + + return null; + } + + public override ExprEvaluator ExprEvaluator + { + get { return _evaluator; } + } + + public override bool IsConstantResult + { + get { return false; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + String delimiter = ""; + foreach (ExprNode child in ChildNodes) + { + writer.Write(delimiter); + child.ToEPL(writer, Precedence); + delimiter = "||"; + } + } + + public override ExprPrecedenceEnum Precedence + { + get { return ExprPrecedenceEnum.CONCAT; } + } + + public override bool EqualsNode(ExprNode node) + { + return node is ExprConcatNode; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/ops/ExprConcatNodeEvalThreadLocal.cs b/NEsper.Core/NEsper.Core/epl/expression/ops/ExprConcatNodeEvalThreadLocal.cs new file mode 100755 index 000000000..b18d7df66 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/ops/ExprConcatNodeEvalThreadLocal.cs @@ -0,0 +1,42 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Text; + +using com.espertech.esper.compat.threading; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.ops +{ + public class ExprConcatNodeEvalThreadLocal : ExprEvaluator + { + private readonly ExprConcatNode _parent; + private readonly ExprEvaluator[] _evaluators; + + private readonly IThreadLocal _localBuffer = ThreadLocalManager.Create( + () => new StringBuilder()); + + public ExprConcatNodeEvalThreadLocal(ExprConcatNode parent, ExprEvaluator[] evaluators) { + _parent = parent; + _evaluators = evaluators; + } + + public object Evaluate(EvaluateParams evaluateParams) + { + var buffer = _localBuffer.GetOrCreate(); + buffer.Length = 0; + return ExprConcatNodeEvalWNew.Evaluate(evaluateParams, buffer, _evaluators, _parent); + } + + public Type ReturnType + { + get { return typeof (string); } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/ops/ExprConcatNodeEvalWNew.cs b/NEsper.Core/NEsper.Core/epl/expression/ops/ExprConcatNodeEvalWNew.cs new file mode 100755 index 000000000..d7b798134 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/ops/ExprConcatNodeEvalWNew.cs @@ -0,0 +1,57 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Text; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.expression.ops +{ + public class ExprConcatNodeEvalWNew : ExprEvaluator + { + private readonly ExprConcatNode _parent; + private readonly ExprEvaluator[] _evaluators; + + public ExprConcatNodeEvalWNew(ExprConcatNode parent, ExprEvaluator[] evaluators) + { + _parent = parent; + _evaluators = evaluators; + } + + public object Evaluate(EvaluateParams evaluateParams) + { + var buffer = new StringBuilder(); + return Evaluate(evaluateParams, buffer, _evaluators, _parent); + } + + internal static string Evaluate(EvaluateParams evaluateParams, StringBuilder buffer, ExprEvaluator[] evaluators, ExprConcatNode parent) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QExprConcat(parent);} + foreach (ExprEvaluator child in evaluators) + { + var resultX = (string) child.Evaluate(evaluateParams); + if (resultX == null) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprConcat(null);} + return null; + } + buffer.Append(resultX); + } + var result = buffer.ToString(); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprConcat(result);} + return result; + } + + public Type ReturnType + { + get { return typeof (string); } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/ops/ExprEqualsAllAnyNode.cs b/NEsper.Core/NEsper.Core/epl/expression/ops/ExprEqualsAllAnyNode.cs new file mode 100755 index 000000000..8cbf7dfa5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/ops/ExprEqualsAllAnyNode.cs @@ -0,0 +1,940 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.magic; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.util; + +using DataCollection = System.Collections.Generic.ICollection; +using DataMap = System.Collections.Generic.IDictionary; +using AnyMap = System.Collections.Generic.IDictionary; + +namespace com.espertech.esper.epl.expression.ops +{ + /// + /// Represents an equals-for-group (= ANY/ALL/SOME (expression list)) comparator in + /// a expression tree. + /// + [Serializable] + public class ExprEqualsAllAnyNode + : ExprNodeBase + , ExprEvaluator + { + [NonSerialized] + private Coercer _coercer; + private bool _hasCollectionOrArray; + private bool _mustCoerce; + + private Func[] _transformList; + + [NonSerialized] + private ExprEvaluator[] _evaluators; + + /// + /// Ctor. + /// + /// true if this is a (!=) not equals rather then equals, false if its a '=' equals + /// true if all, false for any + public ExprEqualsAllAnyNode(bool isNotEquals, bool isAll) + { + IsNot = isNotEquals; + IsAll = isAll; + } + + /// + /// Gets the expression evaluator. + /// + /// The expression evaluator. + public override ExprEvaluator ExprEvaluator + { + get { return this; } + } + + /// + /// Returns true if this is a NOT EQUALS node, false if this is a EQUALS node. + /// + /// + /// true for !=, false for = + /// + public bool IsNot { get; private set; } + + /// + /// True if all. + /// + /// + /// all-flag + /// + public bool IsAll { get; private set; } + + public override bool IsConstantResult + { + get { return false; } + } + + public Type ReturnType + { + get { return typeof(bool?); } + } + + public override ExprNode Validate(ExprValidationContext validationContext) + { + // Must have 2 child nodes + var childNodes = ChildNodes; + if (childNodes.Count < 1) + { + throw new IllegalStateException("Equals group node does not have 1 or more parameters"); + } + + _evaluators = ExprNodeUtility.GetEvaluators(ChildNodes); + + // Must be the same boxed type returned by expressions under this + Type typeOne = _evaluators[0].ReturnType.GetBoxedType(); + + // collections, array or map not supported + if ((typeOne.IsArray) || + (typeOne.IsGenericCollection()) || + (typeOne.IsGenericDictionary())) + { + throw new ExprValidationException( + "Collection or array comparison is not allowed for the IN, ANY, SOME or ALL keywords"); + } + + _transformList = new Func[childNodes.Count]; + + var comparedTypes = new List(); + comparedTypes.Add(typeOne); + _hasCollectionOrArray = false; + for (int i = 1; i < childNodes.Count; i++) + { + Type propType = _evaluators[i].ReturnType; + if (propType.IsArray) + { + _hasCollectionOrArray = true; + if (propType.GetElementType() != typeof(Object)) + { + comparedTypes.Add(propType.GetElementType()); + } + } + else if (propType.IsGenericDictionary()) + { + var baseTransform = MagicMarker.GetDictionaryFactory(propType); + _transformList[i] = o => baseTransform(o); + _hasCollectionOrArray = true; + } + else if (propType.IsGenericCollection()) + { + var baseTransform = MagicMarker.GetCollectionFactory(propType); + _transformList[i] = o => baseTransform(o); + _hasCollectionOrArray = true; + } + else + { + comparedTypes.Add(propType); + } + } + + // Determine common denominator type + Type coercionType; + try + { + coercionType = TypeHelper.GetCommonCoercionType(comparedTypes); + } + catch (CoercionException ex) + { + throw new ExprValidationException("Implicit conversion not allowed: " + ex.Message); + } + + // Check if we need to coerce + _mustCoerce = false; + if (coercionType.IsNumeric()) + { + foreach (Type compareType in comparedTypes) + { + if (coercionType != compareType.GetBoxedType()) + { + _mustCoerce = true; + } + } + if (_mustCoerce) + { + _coercer = CoercerFactory.GetCoercer(null, coercionType.GetBoxedType()); + } + } + + return null; + } + + public object Evaluate(EvaluateParams evaluateParams) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QExprEqualsAnyOrAll(this); } + var result = (bool?)EvaluateInternal(evaluateParams); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprEqualsAnyOrAll(result); } + return result; + } + + public object EvaluateInternal(EvaluateParams evaluateParams) + { + Object leftResult = _evaluators[0].Evaluate(evaluateParams); + if (_transformList[0] != null) + leftResult = _transformList[0](leftResult); + + if (_hasCollectionOrArray) + { + if (IsAll) + { + return CompareAllColl(leftResult, evaluateParams.EventsPerStream, evaluateParams.IsNewData, evaluateParams.ExprEvaluatorContext); + } + + return CompareAnyColl(leftResult, evaluateParams.EventsPerStream, evaluateParams.IsNewData, evaluateParams.ExprEvaluatorContext); + } + + // coerce early if testing without collections + if ((_mustCoerce) && (leftResult != null)) + { + leftResult = _coercer.Invoke(leftResult); + } + + if (IsAll) + { + return CompareAll(leftResult, evaluateParams.EventsPerStream, evaluateParams.IsNewData, evaluateParams.ExprEvaluatorContext); + } + + return CompareAny(leftResult, evaluateParams.EventsPerStream, evaluateParams.IsNewData, evaluateParams.ExprEvaluatorContext); + } + + private Object CompareAll(Object leftResult, EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext exprEvaluatorContext) + { + var children = ChildNodes; + if (IsNot) + { + int len = children.Count - 1; + if ((len > 0) && (leftResult == null)) + { + return null; + } + bool hasNonNullRow = false; + bool hasNullRow = false; + for (int i = 1; i <= len; i++) + { + var rightResult = _evaluators[i].Evaluate(new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext)); + if (_transformList[i] != null) + rightResult = _transformList[i](rightResult); + + if (rightResult != null) + { + hasNonNullRow = true; + if (!_mustCoerce) + { + if (leftResult.Equals(rightResult)) + { + return false; + } + } + else + { + object right = _coercer.Invoke(rightResult); + if (leftResult.Equals(right)) + { + return false; + } + } + } + else + { + hasNullRow = true; + } + } + + if ((!hasNonNullRow) || (hasNullRow)) + { + return null; + } + return true; + } + else + { + int len = children.Count - 1; + if ((len > 0) && (leftResult == null)) + { + return null; + } + bool hasNonNullRow = false; + bool hasNullRow = false; + for (int i = 1; i <= len; i++) + { + var rightResult = _evaluators[i].Evaluate(new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext)); + if (_transformList[i] != null) + rightResult = _transformList[i](rightResult); + + if (rightResult != null) + { + hasNonNullRow = true; + if (!_mustCoerce) + { + if (!leftResult.Equals(rightResult)) + { + return false; + } + } + else + { + object right = _coercer.Invoke(rightResult); + if (!leftResult.Equals(right)) + { + return false; + } + } + } + else + { + hasNullRow = true; + } + } + + if ((!hasNonNullRow) || (hasNullRow)) + { + return null; + } + return true; + } + } + + private Object CompareAllColl(Object leftResult, EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext exprEvaluatorContext) + { + var children = ChildNodes; + if (IsNot) + { + int len = children.Count - 1; + bool hasNonNullRow = false; + bool hasNullRow = false; + for (int i = 1; i <= len; i++) + { + var rightResult = _evaluators[i].Evaluate(new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext)); + if (_transformList[i] != null) + rightResult = _transformList[i](rightResult); + + if (rightResult == null) + { + hasNullRow = true; + continue; + } + + if (rightResult is AnyMap) + { + if (leftResult == null) + { + return null; + } + var coll = (AnyMap)rightResult; + if (coll.ContainsKey(leftResult)) + { + return false; + } + hasNonNullRow = true; + } + else if (rightResult is DataCollection) + { + if (leftResult == null) + { + return null; + } + var coll = (DataCollection)rightResult; + if (coll.Contains(leftResult)) + { + return false; + } + hasNonNullRow = true; + } + else if (rightResult.GetType().IsArray) + { + var array = (Array)rightResult; + int arrayLength = array.Length; + for (int index = 0; index < arrayLength; index++) + { + var item = array.GetValue(index); + if (item == null) + { + hasNullRow = true; + continue; + } + if (leftResult == null) + { + return null; + } + hasNonNullRow = true; + if (!_mustCoerce) + { + if (leftResult.Equals(item)) + { + return false; + } + } + else + { + if (!item.IsNumber()) + { + continue; + } + + object left = _coercer.Invoke(leftResult); + object right = _coercer.Invoke(item); + if (Equals(left, right)) + { + return false; + } + } + } + } + else + { + if (leftResult == null) + { + return null; + } + if (!_mustCoerce) + { + if (leftResult.Equals(rightResult)) + { + return false; + } + } + else + { + object left = _coercer.Invoke(leftResult); + object right = _coercer.Invoke(rightResult); + if (Equals(left, right)) + { + return false; + } + } + } + } + + if ((!hasNonNullRow) || (hasNullRow)) + { + return null; + } + return true; + } + else + { + int len = children.Count - 1; + bool hasNonNullRow = false; + bool hasNullRow = false; + for (int i = 1; i <= len; i++) + { + var rightResult = _evaluators[i].Evaluate(new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext)); + if (_transformList[i] != null) + rightResult = _transformList[i](rightResult); + + if (rightResult == null) + { + hasNullRow = true; + continue; + } + + if (rightResult is AnyMap) + { + if (leftResult == null) + { + return null; + } + var coll = (AnyMap)rightResult; + if (!coll.ContainsKey(leftResult)) + { + return false; + } + hasNonNullRow = true; + } + else if (rightResult is DataCollection) + { + hasNonNullRow = true; + if (leftResult == null) + { + return null; + } + var coll = (DataCollection)rightResult; + if (!coll.Contains(leftResult)) + { + return false; + } + } + else if (rightResult.GetType().IsArray) + { + var array = (Array)rightResult; + var arrayLength = array.Length; + for (int index = 0; index < arrayLength; index++) + { + Object item = array.GetValue(index); + if (item == null) + { + hasNullRow = true; + continue; + } + if (leftResult == null) + { + return null; + } + hasNonNullRow = true; + if (!_mustCoerce) + { + if (!leftResult.Equals(item)) + { + return false; + } + } + else + { + if (!item.IsNumber()) + { + continue; + } + + object left = _coercer.Invoke(leftResult); + object right = _coercer.Invoke(item); + if (!Equals(left, right)) + { + return false; + } + } + } + } + else + { + if (leftResult == null) + { + return null; + } + if (!_mustCoerce) + { + if (!leftResult.Equals(rightResult)) + { + return false; + } + } + else + { + object left = _coercer.Invoke(leftResult); + object right = _coercer.Invoke(rightResult); + if (!Equals(left, right)) + { + return false; + } + } + } + } + + if ((!hasNonNullRow) || (hasNullRow)) + { + return null; + } + return true; + } + } + + private Object CompareAny(Object leftResult, EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext exprEvaluatorContext) + { + // Return true on the first not-equal. + var children = ChildNodes; + if (IsNot) + { + bool hasNonNullRow = false; + bool hasNullRow = false; + int len = children.Count - 1; + for (int i = 1; i <= len; i++) + { + Object rightResult = _evaluators[i].Evaluate(new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext)); + if (_transformList[i] != null) + rightResult = _transformList[i](rightResult); + + if (leftResult == null) + { + return null; + } + if (rightResult == null) + { + hasNullRow = true; + continue; + } + + hasNonNullRow = true; + if (!_mustCoerce) + { + if (!leftResult.Equals(rightResult)) + { + return true; + } + } + else + { + object right = _coercer.Invoke(rightResult); + if (!leftResult.Equals(right)) + { + return true; + } + } + } + + if ((!hasNonNullRow) || (hasNullRow)) + { + return null; + } + return false; + } + // Return true on the first equal. + else + { + int len = children.Count - 1; + if ((len > 0) && (leftResult == null)) + { + return null; + } + bool hasNonNullRow = false; + bool hasNullRow = false; + for (int i = 1; i <= len; i++) + { + Object rightResult = _evaluators[i].Evaluate(new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext)); + if (_transformList[i] != null) + rightResult = _transformList[i](rightResult); + + if (rightResult == null) + { + hasNullRow = true; + continue; + } + + hasNonNullRow = true; + if (!_mustCoerce) + { + if (leftResult.Equals(rightResult)) + { + return true; + } + } + else + { + object right = _coercer.Invoke(rightResult); + if (leftResult.Equals(right)) + { + return true; + } + } + } + + if ((!hasNonNullRow) || (hasNullRow)) + { + return null; + } + return false; + } + } + + private Object CompareAnyColl(Object leftResult, EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext exprEvaluatorContext) + { + var childNodes = ChildNodes; + + // Return true on the first not-equal. + if (IsNot) + { + int len = childNodes.Count - 1; + bool hasNonNullRow = false; + bool hasNullRow = false; + for (int i = 1; i <= len; i++) + { + Object rightResult = _evaluators[i].Evaluate(new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext)); + if (_transformList[i] != null) + rightResult = _transformList[i](rightResult); + + if (rightResult == null) + { + hasNullRow = true; + continue; + } + + if (rightResult is AnyMap) + { + if (leftResult == null) + { + return null; + } + var coll = (AnyMap)rightResult; + if (!coll.ContainsKey(leftResult)) + { + return true; + } + hasNonNullRow = true; + } + else if (rightResult is DataCollection) + { + if (leftResult == null) + { + return null; + } + var coll = (DataCollection)rightResult; + if (!coll.Contains(leftResult)) + { + return true; + } + hasNonNullRow = true; + } + + else if (rightResult.GetType().IsArray) + { + var array = (Array)rightResult; + var arrayLength = array.Length; + if ((arrayLength > 0) && (leftResult == null)) + { + return null; + } + + for (int index = 0; index < arrayLength; index++) + { + var item = array.GetValue(index); + if (item == null) + { + hasNullRow = true; + continue; + } + hasNonNullRow = true; + if (!_mustCoerce) + { + if (!leftResult.Equals(item)) + { + return true; + } + } + else + { + if (!item.IsNumber()) + { + continue; + } + + object left = _coercer.Invoke(leftResult); + object right = _coercer.Invoke(item); + if (!Equals(left, right)) + { + return true; + } + } + } + } + else + { + if (leftResult == null) + { + return null; + } + hasNonNullRow = true; + if (!_mustCoerce) + { + if (!leftResult.Equals(rightResult)) + { + return true; + } + } + else + { + object left = _coercer.Invoke(leftResult); + object right = _coercer.Invoke(rightResult); + if (!Equals(left, right)) + { + return true; + } + } + } + } + + if ((!hasNonNullRow) || (hasNullRow)) + { + return null; + } + return false; + } + // Return true on the first equal. + else + { + int len = childNodes.Count - 1; + bool hasNonNullRow = false; + bool hasNullRow = false; + for (int i = 1; i <= len; i++) + { + Object rightResult = _evaluators[i].Evaluate(new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext)); + if (_transformList[i] != null) + rightResult = _transformList[i](rightResult); + + if (rightResult == null) + { + hasNonNullRow = true; + continue; + } + + if (rightResult is AnyMap) + { + if (leftResult == null) + { + return null; + } + var coll = (AnyMap)rightResult; + if (coll.ContainsKey(leftResult)) + { + return true; + } + hasNonNullRow = true; + } + else if (rightResult is DataCollection) + { + if (leftResult == null) + { + return null; + } + hasNonNullRow = true; + var coll = (DataCollection)rightResult; + if (coll.Contains(leftResult)) + { + return true; + } + } + else if (rightResult.GetType().IsArray) + { + var array = (Array)rightResult; + var arrayLength = array.Length; + if ((arrayLength > 0) && (leftResult == null)) + { + return null; + } + for (int index = 0; index < arrayLength; index++) + { + Object item = array.GetValue(index); + if (item == null) + { + hasNullRow = true; + continue; + } + hasNonNullRow = true; + if (!_mustCoerce) + { + if (leftResult.Equals(item)) + { + return true; + } + } + else + { + if (!item.IsNumber()) + { + continue; + } + + object left = _coercer.Invoke(leftResult); + object right = _coercer.Invoke(item); + if (Equals(left, right)) + { + return true; + } + } + } + } + else + { + if (leftResult == null) + { + return null; + } + hasNonNullRow = true; + if (!_mustCoerce) + { + if (leftResult.Equals(rightResult)) + { + return true; + } + } + else + { + object left = _coercer.Invoke(leftResult); + object right = _coercer.Invoke(rightResult); + if (Equals(left, right)) + { + return true; + } + } + } + } + + if ((!hasNonNullRow) || (hasNullRow)) + { + return null; + } + return false; + } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + var children = ChildNodes; + + children[0].ToEPL(writer, Precedence); + if (IsAll) + { + if (IsNot) + { + writer.Write("!=all"); + } + else + { + writer.Write("=all"); + } + } + else + { + if (IsNot) + { + writer.Write("!=any"); + } + else + { + writer.Write("=any"); + } + } + writer.Write("("); + + String delimiter = ""; + for (int i = 0; i < children.Count - 1; i++) + { + writer.Write(delimiter); + children[i + 1].ToEPL(writer, Precedence); + delimiter = ","; + } + writer.Write(")"); + } + + public override ExprPrecedenceEnum Precedence + { + get { return ExprPrecedenceEnum.EQUALS; } + } + + public override bool EqualsNode(ExprNode node) + { + var other = node as ExprEqualsAllAnyNode; + if (other == null) + { + return false; + } + + return (other.IsNot == IsNot) && (other.IsAll == IsAll); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/ops/ExprEqualsNode.cs b/NEsper.Core/NEsper.Core/epl/expression/ops/ExprEqualsNode.cs new file mode 100755 index 000000000..d602e62b6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/ops/ExprEqualsNode.cs @@ -0,0 +1,30 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.ops +{ + /// + /// Represents an equals (=, !=, <>, is, is not) comparator in a filter expressiun tree. + /// + public interface ExprEqualsNode : ExprNode + { + /// + /// Returns true if this is a NOT EQUALS node, false if this is a EQUALS node. + /// + /// true for !=, false for = + bool IsNotEquals { get; } + + /// + /// Returns true if this is a "IS" or "IS NOT" node, false if this is a EQUALS or NOT EQUALS node. + /// + /// true for !=, false for = + bool IsIs { get; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/expression/ops/ExprEqualsNodeImpl.cs b/NEsper.Core/NEsper.Core/epl/expression/ops/ExprEqualsNodeImpl.cs new file mode 100755 index 000000000..d218df50f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/ops/ExprEqualsNodeImpl.cs @@ -0,0 +1,438 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +using Castle.Components.DictionaryAdapter.Xml; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.ops +{ + /// + /// Represents an equals (=) comparator in a filter expressiun tree. + /// + [Serializable] + public class ExprEqualsNodeImpl + : ExprNodeBase + , ExprEqualsNode + { + internal readonly bool vIsNotEquals; + internal readonly bool vIsIs; + [NonSerialized] + private ExprEvaluator _evaluator; + + /// + /// Ctor. + /// + /// true if this is a (!=) not equals rather then equals, false if its a '=' equals + /// true when "is" or "is not" (instead of = or <>) + public ExprEqualsNodeImpl(bool isNotEquals, bool isIs) + { + vIsNotEquals = isNotEquals; + vIsIs = isIs; + } + + public override ExprEvaluator ExprEvaluator + { + get { return _evaluator; } + } + + public bool IsNotEquals + { + get { return vIsNotEquals; } + } + + public bool IsIs + { + get { return vIsIs; } + } + + public override ExprNode Validate(ExprValidationContext validationContext) + { + // Must have 2 child nodes + if (ChildNodes.Count != 2) + { + throw new ExprValidationException("Invalid use of equals, expecting left-hand side and right-hand side but received " + ChildNodes.Count + " expressions"); + } + var evaluators = ExprNodeUtility.GetEvaluators(ChildNodes); + + // Must be the same boxed type returned by expressions under this + var typeOne = evaluators[0].ReturnType.GetBoxedType(); + var typeTwo = evaluators[1].ReturnType.GetBoxedType(); + + // Null constants can be compared for any type + if ((typeOne == null) || (typeTwo == null)) + { + _evaluator = GetEvaluator(evaluators[0], evaluators[1]); + return null; + } + + if (typeOne == typeTwo || typeOne.IsAssignableFrom(typeTwo)) + { + _evaluator = GetEvaluator(evaluators[0], evaluators[1]); + return null; + } + + // Get the common type such as Bool, String or Double and Long + Type coercionType; + try + { + coercionType = typeOne.GetCompareToCoercionType(typeTwo); + } + catch (CoercionException) + { + throw new ExprValidationException(string.Format("Implicit conversion from datatype '{0}' to '{1}' is not allowed", typeTwo.FullName, typeOne.FullName)); + } + + // Check if we need to coerce + if ((coercionType == typeOne.GetBoxedType()) && + (coercionType == typeTwo.GetBoxedType())) + { + _evaluator = GetEvaluator(evaluators[0], evaluators[1]); + } + else if ((typeOne.IsArray) && (typeTwo.IsArray) && (typeOne.GetElementType().GetBoxedType() == typeTwo.GetElementType().GetBoxedType())) + { + coercionType = typeOne.GetElementType().GetCompareToCoercionType(typeTwo.GetElementType()); + _evaluator = new ExprEqualsEvaluatorCoercingArray( + this, evaluators[0], evaluators[1], + CoercerFactory.GetCoercer(typeOne.GetComponentType(), coercionType), + CoercerFactory.GetCoercer(typeTwo.GetComponentType(), coercionType)); + } + else + { + if (!coercionType.IsNumeric()) + { + throw new ExprValidationException("Cannot convert datatype '" + coercionType.Name + "' to a numeric value"); + } + _evaluator = new ExprEqualsEvaluatorCoercing( + this, evaluators[0], evaluators[1], + CoercerFactory.GetCoercer(typeOne, coercionType), + CoercerFactory.GetCoercer(typeTwo, coercionType)); + } + return null; + } + + public override bool IsConstantResult + { + get { return false; } + } + + public IDictionary EventType + { + get { return null; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + ChildNodes[0].ToEPL(writer, Precedence); + if (vIsIs) + { + writer.Write(" is "); + if (vIsNotEquals) + { + writer.Write("not "); + } + } + else + { + if (!vIsNotEquals) + { + writer.Write("="); + } + else + { + writer.Write("!="); + } + } + ChildNodes[1].ToEPL(writer, Precedence); + } + + public override ExprPrecedenceEnum Precedence + { + get { return ExprPrecedenceEnum.EQUALS; } + } + + public override bool EqualsNode(ExprNode node) + { + var other = node as ExprEqualsNodeImpl; + return other != null && other.vIsNotEquals == vIsNotEquals; + } + + private ExprEvaluator GetEvaluator(ExprEvaluator lhs, ExprEvaluator rhs) + { + if (vIsIs) + { + return new ExprEqualsEvaluatorIs(this, lhs, rhs); + } + else + { + return new ExprEqualsEvaluatorEquals(this, lhs, rhs); + } + } + + [Serializable] + public class ExprEqualsEvaluatorCoercingArray : ExprEvaluator + { + [NonSerialized] + private readonly ExprEqualsNodeImpl _parent; + [NonSerialized] + private readonly ExprEvaluator _lhs; + [NonSerialized] + private readonly ExprEvaluator _rhs; + [NonSerialized] + private readonly Coercer _coercerLHS; + [NonSerialized] + private readonly Coercer _coercerRHS; + + public ExprEqualsEvaluatorCoercingArray(ExprEqualsNodeImpl parent, ExprEvaluator lhs, ExprEvaluator rhs, Coercer coercerLHS, Coercer coercerRHS) + { + _parent = parent; + _lhs = lhs; + _rhs = rhs; + _coercerLHS = coercerLHS; + _coercerRHS = coercerRHS; + } + + public object Evaluate(EvaluateParams evaluateParams) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QExprEquals(_parent); } + var result = EvaluateInternal(evaluateParams); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprEquals(result); } + return result; + } + + private bool? EvaluateInternal(EvaluateParams evaluateParams) + { + var leftResult = _lhs.Evaluate(evaluateParams); + var rightResult = _rhs.Evaluate(evaluateParams); + + if (!_parent.IsIs) + { + if (leftResult == null || rightResult == null) // null comparison + { + return null; + } + } + else if (leftResult == null) + { + return rightResult == null; + } + if (rightResult == null) + { + return false; + } + + var leftArray = (Array)leftResult; + var rightArray = (Array)rightResult; + + if (leftArray.Length != rightArray.Length) + { + return !_parent.vIsNotEquals; + } + + var isEquals = true; + + for (int ii = 0; isEquals && ii < leftArray.Length; ii++) + { + var valueL = _coercerLHS.Invoke(leftArray.GetValue(ii)); + var valueR = _coercerRHS.Invoke(rightArray.GetValue(ii)); + isEquals &= Equals(valueL, valueR); + } + + return isEquals ^ _parent.vIsNotEquals; + } + + public Type ReturnType + { + get { return typeof(bool?); } + } + } + + [Serializable] + public class ExprEqualsEvaluatorCoercing : ExprEvaluator + { + [NonSerialized] + private readonly ExprEqualsNodeImpl _parent; + [NonSerialized] + private readonly ExprEvaluator _lhs; + [NonSerialized] + private readonly ExprEvaluator _rhs; + [NonSerialized] + private readonly Coercer _numberCoercerLHS; + [NonSerialized] + private readonly Coercer _numberCoercerRHS; + + public ExprEqualsEvaluatorCoercing(ExprEqualsNodeImpl parent, ExprEvaluator lhs, ExprEvaluator rhs, Coercer numberCoercerLHS, Coercer numberCoercerRHS) + { + _parent = parent; + _lhs = lhs; + _rhs = rhs; + _numberCoercerLHS = numberCoercerLHS; + _numberCoercerRHS = numberCoercerRHS; + } + + public object Evaluate(EvaluateParams evaluateParams) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QExprEquals(_parent); } + var result = EvaluateInternal(evaluateParams); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprEquals(result); } + return result; + } + + private bool? EvaluateInternal(EvaluateParams evaluateParams) + { + var leftResult = _lhs.Evaluate(evaluateParams); + var rightResult = _rhs.Evaluate(evaluateParams); + + if (!_parent.vIsIs) + { + if (leftResult == null || rightResult == null) // null comparison + { + return null; + } + } + else + { + if (leftResult == null) + { + return rightResult == null; + } + if (rightResult == null) + { + return false; + } + } + + var left = _numberCoercerLHS.Invoke(leftResult); + var right = _numberCoercerRHS.Invoke(rightResult); + return left.Equals(right) ^ _parent.vIsNotEquals; + } + + public Type ReturnType + { + get { return typeof(bool?); } + } + } + + [Serializable] + public class ExprEqualsEvaluatorEquals : ExprEvaluator + { + [NonSerialized] + private readonly ExprEqualsNodeImpl _parent; + [NonSerialized] + private readonly ExprEvaluator _lhs; + [NonSerialized] + private readonly ExprEvaluator _rhs; + + public ExprEqualsEvaluatorEquals(ExprEqualsNodeImpl parent, ExprEvaluator lhs, ExprEvaluator rhs) + { + _parent = parent; + _lhs = lhs; + _rhs = rhs; + } + + public object Evaluate(EvaluateParams evaluateParams) + { + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().QExprEquals(_parent); + var leftResultX = _lhs.Evaluate(evaluateParams); + var rightResultX = _rhs.Evaluate(evaluateParams); + if (leftResultX == null || rightResultX == null) + { // null comparison + InstrumentationHelper.Get().AExprEquals(null); + return null; + } + var result = leftResultX.Equals(rightResultX) ^ _parent.IsNotEquals; + InstrumentationHelper.Get().AExprEquals(result); + return result; + } + + var leftResult = _lhs.Evaluate(evaluateParams); + if (leftResult == null) + { // null comparison + return null; + } + + var rightResult = _rhs.Evaluate(evaluateParams); + if (rightResult == null) + { // null comparison + return null; + } + + return leftResult.Equals(rightResult) ^ _parent.vIsNotEquals; + } + + public Type ReturnType + { + get { return typeof(bool?); } + } + } + + [Serializable] + public class ExprEqualsEvaluatorIs : ExprEvaluator + { + [NonSerialized] + private readonly ExprEqualsNodeImpl _parent; + [NonSerialized] + private readonly ExprEvaluator _lhs; + [NonSerialized] + private readonly ExprEvaluator _rhs; + + public ExprEqualsEvaluatorIs(ExprEqualsNodeImpl parent, ExprEvaluator lhs, ExprEvaluator rhs) + { + _parent = parent; + _lhs = lhs; + _rhs = rhs; + } + + public object Evaluate(EvaluateParams evaluateParams) + { + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().QExprIs(_parent); + var leftResultX = _lhs.Evaluate(evaluateParams); + var rightResultX = _rhs.Evaluate(evaluateParams); + + bool result; + if (leftResultX == null) + { + result = rightResultX == null ^ _parent.IsNotEquals; + } + else + { + result = (rightResultX != null && leftResultX.Equals(rightResultX)) ^ _parent.IsNotEquals; + } + InstrumentationHelper.Get().AExprIs(result); + return result; + } + + var leftResult = _lhs.Evaluate(evaluateParams); + var rightResult = _rhs.Evaluate(evaluateParams); + + if (leftResult == null) + { + return rightResult == null ^ _parent.vIsNotEquals; + } + return (rightResult != null && leftResult.Equals(rightResult)) ^ _parent.vIsNotEquals; + } + + public Type ReturnType + { + get { return typeof(bool?); } + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/ops/ExprInNode.cs b/NEsper.Core/NEsper.Core/epl/expression/ops/ExprInNode.cs new file mode 100755 index 000000000..271036fa3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/ops/ExprInNode.cs @@ -0,0 +1,21 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.ops +{ + /// + /// Represents the in-clause (set check) function in an expression tree. + /// + public interface ExprInNode : ExprNode, ExprEvaluator + { + bool IsNotIn { get; } + void ValidateWithoutContext(); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/ops/ExprInNodeImpl.cs b/NEsper.Core/NEsper.Core/epl/expression/ops/ExprInNodeImpl.cs new file mode 100755 index 000000000..8e0bfcd7c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/ops/ExprInNodeImpl.cs @@ -0,0 +1,387 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.magic; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.ops +{ + using DataCollection = ICollection; + using AnyMap = IDictionary; + + /// + /// Represents the in-clause (set check) function in an expression tree. + /// + [Serializable] + public class ExprInNodeImpl + : ExprNodeBase + , ExprEvaluator + , ExprInNode + { + private readonly bool _isNotIn; + + private bool _mustCoerce; + private bool _hasCollectionOrArray; + + [NonSerialized] + private Coercer _coercer; + [NonSerialized] + private ExprEvaluator[] _evaluators; + [NonSerialized] + private Func[] _transformList; + + /// Ctor. + /// is true for "not in" and false for "in" + public ExprInNodeImpl(bool isNotIn) + { + _isNotIn = isNotIn; + } + + public override ExprEvaluator ExprEvaluator + { + get { return this; } + } + + /// Returns true for not-in, false for regular in + /// false for "val in (a,b,c)" or true for "val not in (a,b,c)" + public bool IsNotIn + { + get { return _isNotIn; } + } + + public override ExprNode Validate(ExprValidationContext validationContext) + { + ValidateWithoutContext(); + + return null; + } + + public void ValidateWithoutContext() + { + if (ChildNodes.Count < 2) + { + throw new ExprValidationException("The IN operator requires at least 2 child expressions"); + } + _evaluators = ExprNodeUtility.GetEvaluators(ChildNodes); + + // Must be the same boxed type returned by expressions under this + var typeOne = _evaluators[0].ReturnType.GetBoxedType(); + + // collections, array or map not supported + if ((typeOne.IsArray) || + (typeOne.IsGenericCollection()) || + (typeOne.IsGenericDictionary())) + { + throw new ExprValidationException("Collection or array comparison is not allowed for the IN, ANY, SOME or ALL keywords"); + } + + _transformList = new Func[_evaluators.Length]; + + var comparedTypes = new List { typeOne }; + _hasCollectionOrArray = false; + + var length = ChildNodes.Count - 1; + for (int i = 1; i <= length; i++) + { + var propType = _evaluators[i].ReturnType; + if (propType == null) + { + continue; + } + if (propType.IsArray) + { + _hasCollectionOrArray = true; + if (propType.GetElementType() != typeof(Object)) + { + comparedTypes.Add(propType.GetElementType()); + } + } + else if (propType.IsGenericDictionary()) + { + var baseTransform = MagicMarker.GetDictionaryFactory(propType); + _transformList[i] = o => baseTransform(o); + _hasCollectionOrArray = true; + } + else if (propType.IsGenericCollection()) + { + var baseTransform = MagicMarker.GetCollectionFactory(propType); + _transformList[i] = o => baseTransform(o); + _hasCollectionOrArray = true; + } + else + { + comparedTypes.Add(propType); + } + } + + // Determine common denominator type + Type coercionType; + try + { + coercionType = TypeHelper.GetCommonCoercionType(comparedTypes); + } + catch (CoercionException ex) + { + throw new ExprValidationException("Implicit conversion not allowed: " + ex.Message); + } + + // Check if we need to coerce + _mustCoerce = false; + if (coercionType.IsNumeric()) + { + foreach (Type compareType in comparedTypes) + { + if (coercionType != compareType.GetBoxedType()) + { + _mustCoerce = true; + } + } + if (_mustCoerce) + { + _coercer = CoercerFactory.GetCoercer(null, coercionType.GetBoxedType()); + } + } + } + + public Type ReturnType + { + get { return typeof(bool?); } + } + + public object Evaluate(EvaluateParams evaluateParams) + { + return Evaluate( + evaluateParams.EventsPerStream, + evaluateParams.IsNewData, + evaluateParams.ExprEvaluatorContext + ); + } + + public Object Evaluate(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext exprEvaluatorContext) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QExprIn(this); } + var result = EvaluateInternal(eventsPerStream, isNewData, exprEvaluatorContext); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprIn(result); } + return result; + } + + private bool? EvaluateInternal(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext exprEvaluatorContext) + { + var inPropResult = _evaluators[0].Evaluate(new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext)); + + if (!_hasCollectionOrArray) + { + if ((_mustCoerce) && (inPropResult != null)) + { + inPropResult = _coercer.Invoke(inPropResult); + } + + int len = this.ChildNodes.Count - 1; + if ((len > 0) && (inPropResult == null)) + { + return null; + } + bool hasNullRow = false; + for (int i = 1; i <= len; i++) + { + var rightResult = _evaluators[i].Evaluate(new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext)); + if (_transformList[i] != null) + rightResult = _transformList[i](rightResult); + + if (rightResult == null) + { + hasNullRow = true; + continue; + } + + if (!_mustCoerce) + { + if (rightResult.Equals(inPropResult)) + { + return !_isNotIn; + } + } + else + { + var right = _coercer.Invoke(rightResult); + if (right.Equals(inPropResult)) + { + return !_isNotIn; + } + } + } + + if (hasNullRow) + { + return null; + } + return _isNotIn; + } + else + { + var len = ChildNodes.Count - 1; + var hasNullRow = false; + var evaluateParams = new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext); + for (int i = 1; i <= len; i++) + { + var rightResult = _evaluators[i].Evaluate(evaluateParams); + if (_transformList[i] != null) + rightResult = _transformList[i](rightResult); + + if (rightResult == null) + { + continue; + } + + if (rightResult is AnyMap) + { + if (inPropResult == null) + { + return null; + } + var coll = (AnyMap)rightResult; + if (coll.ContainsKey(inPropResult)) + { + return !_isNotIn; + } + } + else if (rightResult.GetType().IsArray) + { + var array = (Array)rightResult; + int arrayLength = array.Length; + if ((arrayLength > 0) && (inPropResult == null)) + { + return null; + } + for (int index = 0; index < arrayLength; index++) + { + var item = array.GetValue(index); + if (item == null) + { + hasNullRow = true; + continue; + } + if (!_mustCoerce) + { + if (inPropResult.Equals(item)) + { + return !_isNotIn; + } + } + else + { + if (!item.IsNumber()) + { + continue; + } + var left = _coercer.Invoke(inPropResult); + var right = _coercer.Invoke(item); + if (left.Equals(right)) + { + return !_isNotIn; + } + } + } + } + else if (rightResult is DataCollection) + { + if (inPropResult == null) + { + return null; + } + var coll = (DataCollection)rightResult; + if (coll.Contains(inPropResult)) + { + return !_isNotIn; + } + } + else + { + if (inPropResult == null) + { + return null; + } + if (!_mustCoerce) + { + if (inPropResult.Equals(rightResult)) + { + return !_isNotIn; + } + } + else + { + var left = _coercer.Invoke(inPropResult); + var right = _coercer.Invoke(rightResult); + if (left.Equals(right)) + { + return !_isNotIn; + } + } + } + } + + if (hasNullRow) + { + return null; + } + return _isNotIn; + } + } + + public override bool IsConstantResult + { + get { return false; } + } + + public override bool EqualsNode(ExprNode node) + { + if (!(node is ExprInNodeImpl)) + { + return false; + } + + var other = (ExprInNodeImpl)node; + return other._isNotIn == _isNotIn; + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + var delimiter = ""; + + IEnumerator it = ChildNodes.Cast().GetEnumerator(); + it.MoveNext(); + it.Current.ToEPL(writer, Precedence); + writer.Write(_isNotIn ? " not in (" : " in ("); + + while (it.MoveNext()) + { + ExprNode inSetValueExpr = it.Current; + writer.Write(delimiter); + inSetValueExpr.ToEPL(writer, Precedence); + delimiter = ","; + } + + writer.Write(')'); + } + + public override ExprPrecedenceEnum Precedence + { + get { return ExprPrecedenceEnum.RELATIONAL_BETWEEN_IN; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/ops/ExprLikeNode.cs b/NEsper.Core/NEsper.Core/epl/expression/ops/ExprLikeNode.cs new file mode 100755 index 000000000..be2c335f7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/ops/ExprLikeNode.cs @@ -0,0 +1,201 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.ops +{ + /// + /// Represents the like-clause in an expression tree. + /// + [Serializable] + public class ExprLikeNode : ExprNodeBase, ExprEvaluator + { + private readonly bool _isNot; + + private bool _isNumericValue; + private bool _isConstantPattern; + + [NonSerialized] + private LikeUtil _likeUtil; + [NonSerialized] + private ExprEvaluator[] _evaluators; + + /// Ctor. + /// is true if this is a "not like", or false if just a like + public ExprLikeNode(bool not) + { + _isNot = not; + } + + public override ExprEvaluator ExprEvaluator + { + get { return this; } + } + + public override ExprNode Validate(ExprValidationContext validationContext) + { + if ((ChildNodes.Count != 2) && (ChildNodes.Count != 3)) + { + throw new ExprValidationException("The 'like' operator requires 2 (no escape) or 3 (with escape) child expressions"); + } + _evaluators = ExprNodeUtility.GetEvaluators(ChildNodes); + + // check eval child node - can be String or numeric + Type evalChildType = _evaluators[0].ReturnType; + _isNumericValue = evalChildType.IsNumeric(); + if ((evalChildType != typeof(String)) && (!_isNumericValue)) + { + throw new ExprValidationException("The 'like' operator requires a String or numeric type left-hand expression"); + } + + // check pattern child node + ExprEvaluator patternChildNode = _evaluators[1]; + Type patternChildType = patternChildNode.ReturnType; + if (patternChildType != typeof(String)) + { + throw new ExprValidationException("The 'like' operator requires a String-type pattern expression"); + } + if (ChildNodes[1].IsConstantResult) + { + _isConstantPattern = true; + } + + // check escape character node + if (ChildNodes.Count == 3) + { + ExprEvaluator escapeChildNode = _evaluators[2]; + if (escapeChildNode.ReturnType != typeof(String)) + { + throw new ExprValidationException("The 'like' operator escape parameter requires a character-type value"); + } + } + + return null; + } + + public Type ReturnType + { + get { return typeof(bool?); } + } + + public override bool IsConstantResult + { + get { return false; } + } + + public object Evaluate(EvaluateParams evaluateParams) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QExprLike(this); } + if (_likeUtil == null) + { + var patternVal = (string)_evaluators[1].Evaluate(evaluateParams); + if (patternVal == null) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprLike(null); } + return null; + } + string escape = "\\"; + char? escapeCharacter = null; + if (ChildNodes.Count == 3) + { + escape = (String)_evaluators[2].Evaluate(evaluateParams); + } + if (escape.Length > 0) + { + escapeCharacter = escape[0]; + } + _likeUtil = new LikeUtil(patternVal, escapeCharacter, false); + } + else + { + if (!_isConstantPattern) + { + var patternVal = (string)_evaluators[1].Evaluate(evaluateParams); + if (patternVal == null) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprLike(null); } + return null; + } + _likeUtil.ResetPattern(patternVal); + } + } + + var evalValue = _evaluators[0].Evaluate(evaluateParams); + if (evalValue == null) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprLike(null); } + return null; + } + + if (_isNumericValue) + { + evalValue = evalValue.ToString(); + } + + var result = _likeUtil.Compare((String)evalValue); + + if (_isNot) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprLike(!result); } + return !result; + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprLike(result); } + return result; + } + + public override bool EqualsNode(ExprNode node) + { + if (!(node is ExprLikeNode)) + { + return false; + } + + var other = (ExprLikeNode)node; + return _isNot == other._isNot; + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + var childNodes = ChildNodes; + childNodes[0].ToEPL(writer, Precedence); + + if (_isNot) + { + writer.Write(" not"); + } + + writer.Write(" like "); + childNodes[1].ToEPL(writer, Precedence); + + if (childNodes.Count == 3) + { + writer.Write(" escape "); + childNodes[2].ToEPL(writer, Precedence); + } + } + + public override ExprPrecedenceEnum Precedence + { + get { return ExprPrecedenceEnum.RELATIONAL_BETWEEN_IN; } + } + + /// Returns true if this is a "not like", or false if just a like + /// indicator whether negated or not + public bool IsNot + { + get { return _isNot; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/ops/ExprMathNode.cs b/NEsper.Core/NEsper.Core/epl/expression/ops/ExprMathNode.cs new file mode 100755 index 000000000..c6fe8c898 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/ops/ExprMathNode.cs @@ -0,0 +1,189 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +using com.espertech.esper.compat; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.type; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.ops +{ + /// + /// Represents a simple Math (+/-/divide/*) in a filter expression tree. + /// + [Serializable] + public class ExprMathNode : ExprNodeBase, ExprEvaluator + { + private readonly MathArithTypeEnum _mathArithTypeEnum; + private readonly bool _isIntegerDivision; + private readonly bool _isDivisionByZeroReturnsNull; + + [NonSerialized] private MathArithTypeEnumExtensions.Computer _arithTypeEnumComputer; + private Type _resultType; + [NonSerialized] private ExprEvaluator _evaluatorLeft; + [NonSerialized] private ExprEvaluator _evaluatorRight; + /// Ctor. + /// type of math + /// false for division returns double, true for using Java-standard integer division + /// false for division-by-zero returns infinity, true for null + public ExprMathNode(MathArithTypeEnum mathArithTypeEnum, bool isIntegerDivision, bool isDivisionByZeroReturnsNull) + { + _mathArithTypeEnum = mathArithTypeEnum; + _isIntegerDivision = isIntegerDivision; + _isDivisionByZeroReturnsNull = isDivisionByZeroReturnsNull; + } + + public override ExprEvaluator ExprEvaluator + { + get { return this; } + } + + public override ExprNode Validate(ExprValidationContext validationContext) + { + if (ChildNodes.Count != 2) + { + throw new ExprValidationException("Arithmatic node must have 2 parameters"); + } + + foreach (ExprNode child in ChildNodes) + { + var childType = child.ExprEvaluator.ReturnType; + if (!childType.IsNumeric()) + { + throw new ExprValidationException( + string.Format("Implicit conversion from datatype '{0}' to numeric is not allowed", childType.FullName)); + } + } + + // Determine result type, set up compute function + _evaluatorLeft = ChildNodes[0].ExprEvaluator; + _evaluatorRight = ChildNodes[1].ExprEvaluator; + + var childTypeOne = _evaluatorLeft.ReturnType; + var childTypeTwo = _evaluatorRight.ReturnType; + + if ((childTypeOne == typeof(short) || childTypeOne == typeof(short?)) && + (childTypeTwo == typeof(short) || childTypeTwo == typeof(short?))) + { + _resultType = typeof (int?); + } + else if ((childTypeOne == typeof(byte) || childTypeOne == typeof(byte?)) && + (childTypeTwo == typeof(byte) || childTypeTwo == typeof(byte?))) + { + _resultType = typeof (int?); + } + else if (childTypeOne == childTypeTwo) + { + _resultType = childTypeTwo.GetBoxedType(); + } + else + { + _resultType = childTypeOne.GetArithmaticCoercionType(childTypeTwo); + } + + if ((_mathArithTypeEnum == MathArithTypeEnum.DIVIDE) && (!_isIntegerDivision)) + { + if (_resultType != typeof(decimal?)) + { + _resultType = typeof(double?); + } + } + + _arithTypeEnumComputer = _mathArithTypeEnum.GetComputer(_resultType, childTypeOne, childTypeTwo, _isIntegerDivision, _isDivisionByZeroReturnsNull, validationContext.EngineImportService.DefaultMathContext); + + return null; + } + + public Type ReturnType + { + get { return _resultType; } + } + + public override bool IsConstantResult + { + get { return false; } + } + + public object Evaluate(EvaluateParams evaluateParams) + { + var result = new Mutable(); + + using (Instrument.With( + i => i.QExprMath(this, _mathArithTypeEnum.GetExpressionText()), + i => i.AExprMath(result.Value))) + { + result.Value = EvaluateInternal(evaluateParams); + return result.Value; + } + } + + private object EvaluateInternal(EvaluateParams evaluateParams) + { + var valueChildOne = _evaluatorLeft.Evaluate(evaluateParams); + if (valueChildOne == null) + { + return null; + } + + var valueChildTwo = _evaluatorRight.Evaluate(evaluateParams); + if (valueChildTwo == null) + { + return null; + } + + // arithTypeEnumComputer is initialized by validation + return _arithTypeEnumComputer.Invoke(valueChildOne, valueChildTwo); + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + ChildNodes[0].ToEPL(writer, Precedence); + writer.Write(_mathArithTypeEnum.GetExpressionText()); + ChildNodes[1].ToEPL(writer, Precedence); + } + + public override ExprPrecedenceEnum Precedence + { + get + { + if (_mathArithTypeEnum == MathArithTypeEnum.MULTIPLY || + _mathArithTypeEnum == MathArithTypeEnum.DIVIDE || + _mathArithTypeEnum == MathArithTypeEnum.MODULO) + { + return ExprPrecedenceEnum.MULTIPLY; + } + else + { + return ExprPrecedenceEnum.ADDITIVE; + } + } + } + + public override bool EqualsNode(ExprNode node) + { + var other = node as ExprMathNode; + if (other == null) + { + return false; + } + + return other._mathArithTypeEnum == _mathArithTypeEnum; + } + + /// Returns the type of math. + /// math type + public MathArithTypeEnum MathArithTypeEnum + { + get { return _mathArithTypeEnum; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/ops/ExprNewInstanceNode.cs b/NEsper.Core/NEsper.Core/epl/expression/ops/ExprNewInstanceNode.cs new file mode 100755 index 000000000..bc6ba0378 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/ops/ExprNewInstanceNode.cs @@ -0,0 +1,93 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events.bean; + +namespace com.espertech.esper.epl.expression.ops +{ + /// + /// Represents the "new Class(...)" operator in an expression tree. + /// + [Serializable] + public class ExprNewInstanceNode + : ExprNodeBase + , ExprEvaluator + { + private readonly string _classIdent; + [NonSerialized] private Type _targetClass; + [NonSerialized] private InstanceManufacturer _manufacturer; + + public ExprNewInstanceNode(string classIdent) + { + _classIdent = classIdent; + } + + public override ExprEvaluator ExprEvaluator + { + get { return this; } + } + + public override ExprNode Validate(ExprValidationContext validationContext) + { + try + { + _targetClass = validationContext.EngineImportService.ResolveType(_classIdent, false); + } + catch (EngineImportException e) + { + throw new ExprValidationException("Failed to resolve new-operator class name '" + _classIdent + "'"); + } + _manufacturer = InstanceManufacturerFactory.GetManufacturer( + _targetClass, validationContext.EngineImportService, this.ChildNodes); + return null; + } + + public object Evaluate(EvaluateParams evaluateParams) + { + return _manufacturer.Make(evaluateParams); + } + + public Type ReturnType + { + get { return _targetClass; } + } + + public override bool IsConstantResult + { + get { return false; } + } + + public string ClassIdent + { + get { return _classIdent; } + } + + public override bool EqualsNode(ExprNode node) + { + var other = node as ExprNewInstanceNode; + return other != null && other._classIdent.Equals(this._classIdent); + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + writer.Write("new "); + writer.Write(_classIdent); + ExprNodeUtility.ToExpressionStringParams(writer, this.ChildNodes); + } + + public override ExprPrecedenceEnum Precedence + { + get { return ExprPrecedenceEnum.UNARY; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/ops/ExprNewStructNode.cs b/NEsper.Core/NEsper.Core/epl/expression/ops/ExprNewStructNode.cs new file mode 100755 index 000000000..f29d319ee --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/ops/ExprNewStructNode.cs @@ -0,0 +1,176 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.ops +{ + /// Represents the "new {...}" operator in an expression tree. + [Serializable] + public class ExprNewStructNode : ExprNodeBase, ExprEvaluatorTypableReturn + { + private readonly string[] _columnNames; + [NonSerialized] private LinkedHashMap _eventType; + [NonSerialized] private ExprEvaluator[] _evaluators; + private bool _isAllConstants; + + public ExprNewStructNode(string[] columnNames) + { + _columnNames = columnNames; + } + + public override ExprEvaluator ExprEvaluator + { + get { return this; } + } + + public override ExprNode Validate(ExprValidationContext validationContext) + { + _eventType = new LinkedHashMap(); + _evaluators = ExprNodeUtility.GetEvaluators(this.ChildNodes); + + for (var i = 0; i < _columnNames.Length; i++) + { + _isAllConstants = _isAllConstants && this.ChildNodes[i].IsConstantResult; + if (_eventType.ContainsKey(_columnNames[i])) + { + throw new ExprValidationException("Failed to validate new-keyword property names, property '" + _columnNames[i] + "' has already been declared"); + } + + IDictionary eventTypeResult = null; + if (_evaluators[i] is ExprEvaluatorTypableReturn) + { + eventTypeResult = ((ExprEvaluatorTypableReturn) _evaluators[i]).RowProperties; + } + if (eventTypeResult != null) + { + _eventType.Put(_columnNames[i], eventTypeResult); + } + else + { + var classResult = _evaluators[i].ReturnType.GetBoxedType(); + _eventType.Put(_columnNames[i], classResult); + } + } + return null; + } + + public string[] ColumnNames + { + get { return _columnNames; } + } + + public override bool IsConstantResult + { + get { return _isAllConstants; } + } + + public Type ReturnType + { + get { return typeof (IDictionary); } + } + + public IDictionary RowProperties + { + get { return _eventType; } + } + + public Object Evaluate(EvaluateParams evaluateParams) + { + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().QExprNew(this); + } + var props = new Dictionary(); + for (var i = 0; i < _evaluators.Length; i++) { + props.Put(_columnNames[i], _evaluators[i].Evaluate(evaluateParams)); + } + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().AExprNew(props); + } + return props; + } + + public bool? IsMultirow + { + get { return false; } // New itself can only return a single row + } + + public Object[] EvaluateTypableSingle(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + var evaluateParams = new EvaluateParams(eventsPerStream, isNewData, context); + var rows = new Object[_columnNames.Length]; + for (var i = 0; i < _columnNames.Length; i++) + { + rows[i] = _evaluators[i].Evaluate(evaluateParams); + } + return rows; + } + + public Object[][] EvaluateTypableMulti( + EventBean[] eventsPerStream, + bool isNewData, + ExprEvaluatorContext context) + { + return null; + } + + public override bool EqualsNode(ExprNode node) + { + if (!(node is ExprNewStructNode)) + { + return false; + } + + var other = (ExprNewStructNode) node; + return CompatExtensions.DeepEquals(other._columnNames, _columnNames); + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + writer.Write("new{"); + var delimiter = ""; + for (var i = 0; i < this.ChildNodes.Count; i++) + { + writer.Write(delimiter); + writer.Write(_columnNames[i]); + var expr = this.ChildNodes[i]; + + var outputexpr = true; + if (expr is ExprIdentNode) + { + var prop = (ExprIdentNode) expr; + if (prop.ResolvedPropertyName.Equals(_columnNames[i])) + { + outputexpr = false; + } + } + + if (outputexpr) + { + writer.Write("="); + expr.ToEPL(writer, ExprPrecedenceEnum.MINIMUM); + } + delimiter = ","; + } + writer.Write("}"); + } + + public override ExprPrecedenceEnum Precedence + { + get { return ExprPrecedenceEnum.UNARY; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/ops/ExprNotNode.cs b/NEsper.Core/NEsper.Core/epl/expression/ops/ExprNotNode.cs new file mode 100755 index 000000000..4392cb674 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/ops/ExprNotNode.cs @@ -0,0 +1,88 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.ops +{ + /// + /// Represents a NOT expression in an expression tree. + /// + [Serializable] + public class ExprNotNode : ExprNodeBase, ExprEvaluator + { + [NonSerialized] private ExprEvaluator _evaluator; + + public override ExprNode Validate(ExprValidationContext validationContext) + { + // Must have a single child node + if (ChildNodes.Count != 1) + { + throw new ExprValidationException("The NOT node requires exactly 1 child node"); + } + + _evaluator = ChildNodes[0].ExprEvaluator; + Type childType = _evaluator.ReturnType; + if (!childType.IsBoolean()) + { + throw new ExprValidationException("Incorrect use of NOT clause, sub-expressions do not return bool"); + } + + return null; + } + + public override ExprEvaluator ExprEvaluator + { + get { return this; } + } + + public Type ReturnType + { + get { return typeof (bool?); } + } + + public override bool IsConstantResult + { + get { return false; } + } + + public object Evaluate(EvaluateParams evaluateParams) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QExprNot(this);} + var evaluated = (bool?) _evaluator.Evaluate(evaluateParams); + if (evaluated == null) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprNot(null);} + return null; + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprNot(!evaluated);} + return !evaluated; + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + writer.Write("not "); + ChildNodes[0].ToEPL(writer, Precedence); + } + + public override ExprPrecedenceEnum Precedence + { + get { return ExprPrecedenceEnum.NEGATED; } + } + + public override bool EqualsNode(ExprNode node) + { + return node is ExprNotNode; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/ops/ExprOrNode.cs b/NEsper.Core/NEsper.Core/epl/expression/ops/ExprOrNode.cs new file mode 100755 index 000000000..19c1ca041 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/ops/ExprOrNode.cs @@ -0,0 +1,106 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; +using System.Linq; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.ops +{ + /// Represents an OR expression in a filter expression tree. + [Serializable] + public class ExprOrNode + : ExprNodeBase + , ExprEvaluator + { + [NonSerialized] private ExprEvaluator[] _evaluators; + + public override ExprEvaluator ExprEvaluator + { + get { return this; } + } + + public override ExprNode Validate(ExprValidationContext validationContext) + { + _evaluators = ExprNodeUtility.GetEvaluators(ChildNodes); + + // Sub-nodes must be returning bool + if (_evaluators.Select(child => child.ReturnType).Any(childType => !childType.IsBoolean())) + { + throw new ExprValidationException("Incorrect use of OR clause, sub-expressions do not return bool"); + } + + if (ChildNodes.Count <= 1) + { + throw new ExprValidationException("The OR operator requires at least 2 child expressions"); + } + + return null; + } + + public Type ReturnType + { + get { return typeof (bool?); } + } + + public override bool IsConstantResult + { + get { return false; } + } + + public object Evaluate(EvaluateParams evaluateParams) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QExprOr(this);} + bool? result = false; + // At least one child must evaluate to true + foreach (var child in _evaluators) + { + var evaluated = (bool?) child.Evaluate(evaluateParams); + if (evaluated == null) + { + result = null; + } + else + { + if (evaluated.Value) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprOr(true);} + return true; + } + } + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprOr(result);} + return result; + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + String appendStr = ""; + foreach (ExprNode child in ChildNodes) + { + writer.Write(appendStr); + child.ToEPL(writer, Precedence); + appendStr = " or "; + } + } + + public override ExprPrecedenceEnum Precedence + { + get { return ExprPrecedenceEnum.OR; } + } + + public override bool EqualsNode(ExprNode node) + { + return node is ExprOrNode; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/ops/ExprRegexpNode.cs b/NEsper.Core/NEsper.Core/epl/expression/ops/ExprRegexpNode.cs new file mode 100755 index 000000000..34290ff22 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/ops/ExprRegexpNode.cs @@ -0,0 +1,210 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; +using System.Text.RegularExpressions; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.ops +{ + /// + /// Represents the regexp-clause in an expression tree. + /// + [Serializable] + public class ExprRegexpNode : ExprNodeBase, ExprEvaluator + { + private readonly bool _isNot; + + private Regex _pattern; + private bool _isNumericValue; + private bool _isConstantPattern; + [NonSerialized] + private ExprEvaluator[] _evaluators; + + /// Ctor. + /// is true if the it's a "not regexp" expression, of false for regular regexp + public ExprRegexpNode(bool not) + { + _isNot = not; + } + + public override ExprEvaluator ExprEvaluator + { + get { return this; } + } + + public override ExprNode Validate(ExprValidationContext validationContext) + { + if (ChildNodes.Count != 2) + { + throw new ExprValidationException("The regexp operator requires 2 child expressions"); + } + _evaluators = ExprNodeUtility.GetEvaluators(ChildNodes); + + // check pattern child node + var patternChildType = _evaluators[1].ReturnType; + if (patternChildType != typeof(String)) + { + throw new ExprValidationException("The regexp operator requires a String-type pattern expression"); + } + if (ChildNodes[1].IsConstantResult) + { + _isConstantPattern = true; + } + + // check eval child node - can be String or numeric + Type evalChildType = _evaluators[0].ReturnType; + _isNumericValue = evalChildType.IsNumeric(); + if ((evalChildType != typeof(String)) && (!_isNumericValue)) + { + throw new ExprValidationException("The regexp operator requires a String or numeric type left-hand expression"); + } + + return null; + } + + public Type ReturnType + { + get { return typeof(bool?); } + } + + public override bool IsConstantResult + { + get { return false; } + } + + public object Evaluate(EvaluateParams evaluateParams) + { + var result = new Mutable(null); + + using (Instrument.With( + i => i.QExprRegexp(this), + i => i.AExprRegexp(result.Value))) + { + if (_pattern == null) + { + var patternText = (String)_evaluators[1].Evaluate(evaluateParams); + if (patternText == null) + { + return null; + } + try + { + _pattern = new Regex(String.Format("^{0}$", patternText)); + } + catch (ArgumentException ex) + { + throw new EPException("Error compiling regex pattern '" + patternText + '\'', ex); + } + } + else + { + if (!_isConstantPattern) + { + var patternText = (String)_evaluators[1].Evaluate(evaluateParams); + if (patternText == null) + { + return null; + } + try + { + _pattern = new Regex(String.Format("^{0}$", patternText)); + + } + catch (ArgumentException ex) + { + throw new EPException("Error compiling regex pattern '" + patternText + '\'', ex); + } + } + } + + var evalValue = _evaluators[0].Evaluate(evaluateParams); + if (evalValue == null) + { + return null; + } + + if (_isNumericValue) + { + if (evalValue is double) + { + var tempValue = (double)evalValue; + evalValue = tempValue.ToString("F"); + } + else if (evalValue is float) + { + var tempValue = (float)evalValue; + evalValue = tempValue.ToString("F"); + } + else if (evalValue is decimal) + { + var tempValue = (decimal)evalValue; + evalValue = tempValue.ToString("F"); + } + else + { + evalValue = evalValue.ToString(); + } + } + + result.Value = _pattern.IsMatch((String)evalValue); + + if (_isNot) + { + result.Value = !result.Value; + } + + return result.Value; + } + } + + public override bool EqualsNode(ExprNode node) + { + if (!(node is ExprRegexpNode)) + { + return false; + } + + var other = (ExprRegexpNode)node; + if (_isNot != other._isNot) + { + return false; + } + return true; + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + ChildNodes[0].ToEPL(writer, Precedence); + if (_isNot) + { + writer.Write(" not"); + } + writer.Write(" regexp "); + ChildNodes[1].ToEPL(writer, Precedence); + } + + public override ExprPrecedenceEnum Precedence + { + get { return ExprPrecedenceEnum.RELATIONAL_BETWEEN_IN; } + } + + /// Returns true if this is a "not regexp", or false if just a regexp + /// indicator whether negated or not + public bool IsNot + { + get { return _isNot; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/ops/ExprRelationalOpAllAnyNode.cs b/NEsper.Core/NEsper.Core/epl/expression/ops/ExprRelationalOpAllAnyNode.cs new file mode 100755 index 000000000..cd5be5c47 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/ops/ExprRelationalOpAllAnyNode.cs @@ -0,0 +1,480 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.magic; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.type; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.ops +{ + using DataCollection = ICollection; + using DataMap = IDictionary; + using AnyMap = IDictionary; + using RelationalComputer = Func; + + /// + /// Represents a lesser or greater then (</<=/>/>=) expression in a filter + /// expression tree. + /// + [Serializable] + public class ExprRelationalOpAllAnyNode + : ExprNodeBase + , ExprEvaluator + { + private readonly bool _isAll; + private readonly RelationalOpEnum _relationalOp; + + private bool _hasCollectionOrArray; + + [NonSerialized] private Func[] _transformList; + [NonSerialized] private RelationalComputer _computer; + [NonSerialized] private ExprEvaluator[] _evaluators; + + /// + /// Ctor. + /// + /// type of compare, ie. lt, gt, le, ge + /// true if all, false for any + public ExprRelationalOpAllAnyNode(RelationalOpEnum relationalOpEnum, bool isAll) + { + _relationalOp = relationalOpEnum; + _isAll = isAll; + } + + public override bool IsConstantResult + { + get { return false; } + } + + /// + /// Returns true for ALL, false for ANY. + /// + /// + /// indicator all or any + /// + public bool IsAll + { + get { return _isAll; } + } + + public Type ReturnType + { + get { return typeof(bool?); } + } + + /// + /// Gets the expression evaluator. + /// + public override ExprEvaluator ExprEvaluator + { + get { return this; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + ChildNodes[0].ToEPL(writer, Precedence); + writer.Write(_relationalOp.GetExpressionText()); + if (_isAll) + { + writer.Write("all"); + } + else + { + writer.Write("any"); + } + + writer.Write("("); + String delimiter = ""; + + for (int i = 0; i < ChildNodes.Count - 1; i++) + { + writer.Write(delimiter); + ChildNodes[i + 1].ToEPL(writer, Precedence); + delimiter = ","; + } + writer.Write(")"); + } + + public override ExprPrecedenceEnum Precedence + { + get { return ExprPrecedenceEnum.RELATIONAL_BETWEEN_IN; } + } + + /// + /// Returns the type of relational op used. + /// + /// + /// enum with relational op type + /// + public RelationalOpEnum RelationalOp + { + get { return _relationalOp; } + } + + public override ExprNode Validate(ExprValidationContext validationContext) + { + // Must have 2 child nodes + var childNodes = ChildNodes; + if (childNodes.Count < 1) + { + throw new IllegalStateException("Group relational op node must have 1 or more parameters"); + } + + _evaluators = ExprNodeUtility.GetEvaluators(childNodes); + + var typeOne = _evaluators[0].ReturnType.GetBoxedType(); + + // collections, array or map not supported + if ((typeOne.IsArray) || + (typeOne.IsGenericCollection()) || + (typeOne.IsGenericDictionary())) + { + throw new ExprValidationException( + "Collection or array comparison is not allowed for the IN, ANY, SOME or ALL keywords"); + } + + _transformList = new Func[childNodes.Count]; + + var comparedTypes = new List(); + comparedTypes.Add(typeOne); + _hasCollectionOrArray = false; + for (int i = 1; i < childNodes.Count; i++) + { + var propType = _evaluators[i].ReturnType; + if (propType.IsArray) + { + _hasCollectionOrArray = true; + if (propType.GetElementType() != typeof(Object)) + { + comparedTypes.Add(propType.GetElementType()); + } + } + else if (propType.IsGenericDictionary()) + { + var baseTransform = MagicMarker.GetDictionaryFactory(propType); + _transformList[i] = o => baseTransform(o); + _hasCollectionOrArray = true; + } + else if (propType.IsGenericCollection()) + { + var baseTransform = MagicMarker.GetCollectionFactory(propType); + _transformList[i] = o => baseTransform(o); + _hasCollectionOrArray = true; + } + else + { + comparedTypes.Add(propType); + } + } + + // Determine common denominator type + Type coercionType; + try + { + coercionType = TypeHelper.GetCommonCoercionType(comparedTypes.ToArray()); + } + catch (CoercionException ex) + { + throw new ExprValidationException("Implicit conversion not allowed: " + ex.Message); + } + + // Must be either numeric or string + if (coercionType != typeof(String)) + { + if (!coercionType.IsNumeric()) + { + throw new ExprValidationException(string.Format("Implicit conversion from datatype '{0}' to numeric is not allowed", coercionType.FullName)); + } + } + + _computer = _relationalOp.GetComputer(coercionType, coercionType, coercionType); + + return null; + } + + public object Evaluate(EvaluateParams evaluateParams) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QExprRelOpAnyOrAll(this, _relationalOp.GetExpressionText()); } + var result = EvaluateInternal(evaluateParams); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprRelOpAnyOrAll(result); } + return result; + } + + private bool? EvaluateInternal(EvaluateParams evaluateParams) + { + if (_evaluators.Length == 1) + { + return false; + } + + Object valueLeft = _evaluators[0].Evaluate(evaluateParams); + int len = _evaluators.Length - 1; + + if (_hasCollectionOrArray) + { + bool hasNonNullRow = false; + bool hasRows = false; + for (int i = 1; i <= len; i++) + { + Object valueRight = _evaluators[i].Evaluate(evaluateParams); + if (_transformList[i] != null) + valueRight = _transformList[i](valueRight); + + if (valueRight == null) + { + continue; + } + + if (valueRight is AnyMap) + { + var coll = (AnyMap)valueRight; + hasRows = true; + foreach (var item in coll.Keys) + { + if (!item.IsNumber()) + { + if (_isAll && item == null) + { + return null; + } + continue; + } + hasNonNullRow = true; + if (valueLeft != null) + { + if (_isAll) + { + if (!_computer.Invoke(valueLeft, item)) + { + return false; + } + } + else + { + if (_computer.Invoke(valueLeft, item)) + { + return true; + } + } + } + } + } + else if (valueRight is DataCollection) + { + var coll = (DataCollection)valueRight; + hasRows = true; + foreach (Object item in coll) + { + if (!item.IsNumber()) + { + if (_isAll && item == null) + { + return null; + } + continue; + } + hasNonNullRow = true; + if (valueLeft != null) + { + if (_isAll) + { + if (!_computer.Invoke(valueLeft, item)) + { + return false; + } + } + else + { + if (_computer.Invoke(valueLeft, item)) + { + return true; + } + } + } + } + } + else if (valueRight.GetType().IsArray) + { + hasRows = true; + var array = (Array)valueRight; + var arrayLength = array.Length; + for (int index = 0; index < arrayLength; index++) + { + Object item = array.GetValue(index); + if (item == null) + { + if (_isAll) + { + return null; + } + continue; + } + hasNonNullRow = true; + if (valueLeft != null) + { + if (_isAll) + { + if (!_computer.Invoke(valueLeft, item)) + { + return false; + } + } + else + { + if (_computer.Invoke(valueLeft, item)) + { + return true; + } + } + } + } + } + else if (!valueRight.IsNumber()) + { + if (_isAll) + { + return null; + } + } + else + { + hasNonNullRow = true; + if (_isAll) + { + if (!_computer.Invoke(valueLeft, valueRight)) + { + return false; + } + } + else + { + if (_computer.Invoke(valueLeft, valueRight)) + { + return true; + } + } + } + } + + if (_isAll) + { + if (!hasRows) + { + return true; + } + if ((!hasNonNullRow) || (valueLeft == null)) + { + return null; + } + return true; + } + + if (!hasRows) + { + return false; + } + if ((!hasNonNullRow) || (valueLeft == null)) + { + return null; + } + return false; + } + else + { + bool hasNonNullRow = false; + bool hasRows = false; + for (int i = 1; i <= len; i++) + { + Object valueRight = _evaluators[i].Evaluate(evaluateParams); + if (_transformList[i] != null) + valueRight = _transformList[i](valueRight); + + hasRows = true; + + if (valueRight != null) + { + hasNonNullRow = true; + } + else + { + if (_isAll) + { + return null; + } + } + + if ((valueRight != null) && (valueLeft != null)) + { + if (_isAll) + { + if (!_computer.Invoke(valueLeft, valueRight)) + { + return false; + } + } + else + { + if (_computer.Invoke(valueLeft, valueRight)) + { + return true; + } + } + } + } + + if (_isAll) + { + if (!hasRows) + { + return true; + } + if ((!hasNonNullRow) || (valueLeft == null)) + { + return null; + } + return true; + } + if (!hasRows) + { + return false; + } + if ((!hasNonNullRow) || (valueLeft == null)) + { + return null; + } + return false; + } + } + + public override bool EqualsNode(ExprNode node) + { + if (!(node is ExprRelationalOpAllAnyNode)) + { + return false; + } + + var other = (ExprRelationalOpAllAnyNode)node; + + if ((other._relationalOp != _relationalOp) || + (other._isAll != _isAll)) + { + return false; + } + + return true; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/ops/ExprRelationalOpNode.cs b/NEsper.Core/NEsper.Core/epl/expression/ops/ExprRelationalOpNode.cs new file mode 100755 index 000000000..9198190bc --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/ops/ExprRelationalOpNode.cs @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.type; + +namespace com.espertech.esper.epl.expression.ops +{ + /// + /// Represents a lesser or greater then (</<=/>/>=) expression in a filter expression tree. + /// + public interface ExprRelationalOpNode + : ExprNode, + ExprEvaluator + { + RelationalOpEnum RelationalOpEnum { get; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/expression/ops/ExprRelationalOpNodeImpl.cs b/NEsper.Core/NEsper.Core/epl/expression/ops/ExprRelationalOpNodeImpl.cs new file mode 100755 index 000000000..78ec11102 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/ops/ExprRelationalOpNodeImpl.cs @@ -0,0 +1,160 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +using com.espertech.esper.compat; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.type; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.ops +{ + using RelationalComputer = Func; + + [Serializable] + public class ExprRelationalOpNodeImpl + : ExprNodeBase + , ExprEvaluator + , ExprRelationalOpNode + { + private readonly RelationalOpEnum _relationalOpEnum; + [NonSerialized] private RelationalComputer _computer; + [NonSerialized] private ExprEvaluator[] _evaluators; + /// Ctor. + /// type of compare, ie. lt, gt, le, ge + public ExprRelationalOpNodeImpl(RelationalOpEnum relationalOpEnum) + { + _relationalOpEnum = relationalOpEnum; + } + + public override ExprEvaluator ExprEvaluator + { + get { return this; } + } + + public override bool IsConstantResult + { + get { return false; } + } + + /// Returns the type of relational op used. + /// enum with relational op type + public RelationalOpEnum RelationalOpEnum + { + get { return _relationalOpEnum; } + } + + public override ExprNode Validate(ExprValidationContext validationContext) + { + // Must have 2 child nodes + if (ChildNodes.Count != 2) + { + throw new IllegalStateException("Relational op node does not have exactly 2 parameters"); + } + _evaluators = ExprNodeUtility.GetEvaluators(ChildNodes); + + // Must be either numeric or string + Type typeOne = _evaluators[0].ReturnType.GetBoxedType(); + Type typeTwo = _evaluators[1].ReturnType.GetBoxedType(); + Type compareType; + + if (typeOne == typeTwo) + { + compareType = typeOne; + } + else if ((typeOne == typeof (string)) && (typeTwo == typeof (string))) + { + compareType = typeof (string); + } + else + { + try + { + compareType = typeOne.GetCompareToCoercionType(typeTwo); + } + catch (CoercionException) + { + if (!typeOne.IsNumeric()) + { + throw new ExprValidationException( + string.Format("Implicit conversion from datatype '{0}' to numeric is not allowed", typeOne.FullName)); + } + else if (!typeTwo.IsNumeric()) + { + throw new ExprValidationException( + string.Format("Implicit conversion from datatype '{0}' to numeric is not allowed", typeTwo.FullName)); + } + else + { + throw; + } + } + } + + _computer = _relationalOpEnum.GetComputer(compareType, typeOne, typeTwo); + + return null; + } + + public Type ReturnType + { + get { return typeof (bool?); } + } + + public object Evaluate(EvaluateParams evaluateParams) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QExprRelOp(this, _relationalOpEnum.GetExpressionText());} + Object valueLeft = _evaluators[0].Evaluate(evaluateParams); + if (valueLeft == null) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprRelOp(null);} + return null; + } + + Object valueRight = _evaluators[1].Evaluate(evaluateParams); + if (valueRight == null) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprRelOp(null);} + return null; + } + + if (InstrumentationHelper.ENABLED) { + var result = _computer.Invoke(valueLeft, valueRight); + InstrumentationHelper.Get().AExprRelOp(result); + return result; + } + return _computer.Invoke(valueLeft, valueRight); + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + ChildNodes[0].ToEPL(writer, Precedence); + writer.Write(_relationalOpEnum.GetExpressionText()); + ChildNodes[1].ToEPL(writer, Precedence); + } + + public override ExprPrecedenceEnum Precedence + { + get { return ExprPrecedenceEnum.RELATIONAL_BETWEEN_IN; } + } + + public override bool EqualsNode(ExprNode node) + { + var other = node as ExprRelationalOpNodeImpl; + if (other == null) + { + return false; + } + + return other._relationalOpEnum == _relationalOpEnum; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/prev/ExprPreviousEvalStrategy.cs b/NEsper.Core/NEsper.Core/epl/expression/prev/ExprPreviousEvalStrategy.cs new file mode 100755 index 000000000..f37a5d480 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/prev/ExprPreviousEvalStrategy.cs @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.prev +{ + public interface ExprPreviousEvalStrategy + { + Object Evaluate( + EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext); + ICollection EvaluateGetCollEvents( + EventBean[] eventsPerStream, ExprEvaluatorContext context); + ICollection EvaluateGetCollScalar( + EventBean[] eventsPerStream, ExprEvaluatorContext context); + EventBean EvaluateGetEventBean( + EventBean[] eventsPerStream, ExprEvaluatorContext context); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/prev/ExprPreviousEvalStrategyCount.cs b/NEsper.Core/NEsper.Core/epl/expression/prev/ExprPreviousEvalStrategyCount.cs new file mode 100755 index 000000000..4475e54d7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/prev/ExprPreviousEvalStrategyCount.cs @@ -0,0 +1,75 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.view.window; + +namespace com.espertech.esper.epl.expression.prev +{ + public class ExprPreviousEvalStrategyCount : ExprPreviousEvalStrategy + { + private readonly RandomAccessByIndexGetter _randomAccessGetter; + private readonly RelativeAccessByEventNIndexGetter _relativeAccessGetter; + private readonly int _streamNumber; + + public ExprPreviousEvalStrategyCount( + int streamNumber, + RandomAccessByIndexGetter randomAccessGetter, + RelativeAccessByEventNIndexGetter relativeAccessGetter) + { + _streamNumber = streamNumber; + _randomAccessGetter = randomAccessGetter; + _relativeAccessGetter = relativeAccessGetter; + } + + #region ExprPreviousEvalStrategy Members + + public Object Evaluate(EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext) + { + long size; + if (_randomAccessGetter != null) + { + var randomAccess = _randomAccessGetter.Accessor; + size = randomAccess.WindowCount; + } + else + { + var evalEvent = eventsPerStream[_streamNumber]; + var relativeAccess = _relativeAccessGetter.GetAccessor(evalEvent); + if (relativeAccess == null) + { + return null; + } + size = relativeAccess.GetWindowToEventCount(); + } + + return size; + } + + public ICollection EvaluateGetCollEvents(EventBean[] eventsPerStream, ExprEvaluatorContext context) + { + return null; + } + + public ICollection EvaluateGetCollScalar(EventBean[] eventsPerStream, ExprEvaluatorContext context) + { + return null; + } + + public EventBean EvaluateGetEventBean(EventBean[] eventsPerStream, ExprEvaluatorContext context) + { + return null; + } + + #endregion + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/expression/prev/ExprPreviousEvalStrategyPrev.cs b/NEsper.Core/NEsper.Core/epl/expression/prev/ExprPreviousEvalStrategyPrev.cs new file mode 100755 index 000000000..d98df2090 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/prev/ExprPreviousEvalStrategyPrev.cs @@ -0,0 +1,144 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.view.window; + +namespace com.espertech.esper.epl.expression.prev +{ + public class ExprPreviousEvalStrategyPrev : ExprPreviousEvalStrategy + { + private readonly int? _constantIndexNumber; + private readonly ExprEvaluator _evalNode; + private readonly ExprEvaluator _indexNode; + private readonly bool _isConstantIndex; + private readonly bool _isTail; + private readonly RandomAccessByIndexGetter _randomAccessGetter; + private readonly RelativeAccessByEventNIndexGetter _relativeAccessGetter; + private readonly int _streamNumber; + + public ExprPreviousEvalStrategyPrev( + int streamNumber, + ExprEvaluator indexNode, + ExprEvaluator evalNode, + RandomAccessByIndexGetter randomAccessGetter, + RelativeAccessByEventNIndexGetter relativeAccessGetter, + bool constantIndex, + int? constantIndexNumber, + bool tail) + { + _streamNumber = streamNumber; + _indexNode = indexNode; + _evalNode = evalNode; + _randomAccessGetter = randomAccessGetter; + _relativeAccessGetter = relativeAccessGetter; + _isConstantIndex = constantIndex; + _constantIndexNumber = constantIndexNumber; + _isTail = tail; + } + + #region ExprPreviousEvalStrategy Members + + public Object Evaluate(EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext) + { + EventBean substituteEvent = GetSubstitute(eventsPerStream, exprEvaluatorContext); + if (substituteEvent == null) + { + return null; + } + + // Substitute original event with prior event, evaluate inner expression + EventBean originalEvent = eventsPerStream[_streamNumber]; + eventsPerStream[_streamNumber] = substituteEvent; + Object evalResult = _evalNode.Evaluate(new EvaluateParams(eventsPerStream, true, exprEvaluatorContext)); + eventsPerStream[_streamNumber] = originalEvent; + + return evalResult; + } + + public EventBean EvaluateGetEventBean(EventBean[] eventsPerStream, ExprEvaluatorContext context) + { + return GetSubstitute(eventsPerStream, context); + } + + public ICollection EvaluateGetCollEvents(EventBean[] eventsPerStream, ExprEvaluatorContext context) + { + return null; + } + + public ICollection EvaluateGetCollScalar(EventBean[] eventsPerStream, ExprEvaluatorContext context) + { + Object result = Evaluate(eventsPerStream, context); + if (result == null) + { + return null; + } + return result.AsSingleton(); + } + + #endregion + + private EventBean GetSubstitute(EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext) + { + // Use constant if supplied + int? index; + if (_isConstantIndex) + { + index = _constantIndexNumber; + } + else + { + // evaluate first child, which returns the index + var indexResult = _indexNode.Evaluate(new EvaluateParams(eventsPerStream, true, exprEvaluatorContext)); + if (indexResult == null) + { + return null; + } + index = indexResult.AsInt(); + } + + // access based on index returned + EventBean substituteEvent; + if (_randomAccessGetter != null) + { + RandomAccessByIndex randomAccess = _randomAccessGetter.Accessor; + if (!_isTail) + { + substituteEvent = randomAccess.GetNewData(index.Value); + } + else + { + substituteEvent = randomAccess.GetNewDataTail(index.Value); + } + } + else + { + var evalEvent = eventsPerStream[_streamNumber]; + var relativeAccess = _relativeAccessGetter.GetAccessor(evalEvent); + if (relativeAccess == null) + { + return null; + } + if (!_isTail) + { + substituteEvent = relativeAccess.GetRelativeToEvent(evalEvent, index.Value); + } + else + { + substituteEvent = relativeAccess.GetRelativeToEnd(index.Value); + } + } + return substituteEvent; + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/expression/prev/ExprPreviousEvalStrategyWindow.cs b/NEsper.Core/NEsper.Core/epl/expression/prev/ExprPreviousEvalStrategyWindow.cs new file mode 100755 index 000000000..f83a6209f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/prev/ExprPreviousEvalStrategyWindow.cs @@ -0,0 +1,145 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.view.window; + +namespace com.espertech.esper.epl.expression.prev +{ + public class ExprPreviousEvalStrategyWindow : ExprPreviousEvalStrategy + { + private readonly int _streamNumber; + private readonly ExprEvaluator _evalNode; + private readonly Type _componentType; + private readonly RandomAccessByIndexGetter _randomAccessGetter; + private readonly RelativeAccessByEventNIndexGetter _relativeAccessGetter; + + public ExprPreviousEvalStrategyWindow(int streamNumber, ExprEvaluator evalNode, Type componentType, RandomAccessByIndexGetter randomAccessGetter, RelativeAccessByEventNIndexGetter relativeAccessGetter) + { + _streamNumber = streamNumber; + _evalNode = evalNode; + _componentType = componentType; + _randomAccessGetter = randomAccessGetter; + _relativeAccessGetter = relativeAccessGetter; + } + + public Object Evaluate(EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext) + { + IEnumerator events; + int size; + if (_randomAccessGetter != null) + { + var randomAccess = _randomAccessGetter.Accessor; + events = randomAccess.GetWindowEnumerator(); + size = randomAccess.WindowCount; + } + else + { + var evalEvent = eventsPerStream[_streamNumber]; + var relativeAccess = _relativeAccessGetter.GetAccessor(evalEvent); + if (relativeAccess == null) + { + return null; + } + + size = relativeAccess.GetWindowToEventCount(); + events = relativeAccess.GetWindowToEvent(); + } + + if (size <= 0) + { + return null; + } + + var originalEvent = eventsPerStream[_streamNumber]; + var result = Array.CreateInstance(_componentType, size); + + for (int i = 0; i < size; i++) + { + events.MoveNext(); + eventsPerStream[_streamNumber] = events.Current; + var evalResult = _evalNode.Evaluate(new EvaluateParams(eventsPerStream, true, exprEvaluatorContext)); + result.SetValue(evalResult, i); + } + + eventsPerStream[_streamNumber] = originalEvent; + return result; + } + + public ICollection EvaluateGetCollEvents(EventBean[] eventsPerStream, ExprEvaluatorContext context) + { + ICollection events; + if (_randomAccessGetter != null) + { + RandomAccessByIndex randomAccess = _randomAccessGetter.Accessor; + events = randomAccess.WindowCollectionReadOnly; + } + else + { + EventBean evalEvent = eventsPerStream[_streamNumber]; + RelativeAccessByEventNIndex relativeAccess = _relativeAccessGetter.GetAccessor(evalEvent); + if (relativeAccess == null) + { + return null; + } + + events = relativeAccess.GetWindowToEventCollReadOnly(); + } + return events; + } + + public ICollection EvaluateGetCollScalar(EventBean[] eventsPerStream, ExprEvaluatorContext context) + { + IEnumerator events; + int size; + if (_randomAccessGetter != null) + { + var randomAccess = _randomAccessGetter.Accessor; + events = randomAccess.GetWindowEnumerator(); + size = randomAccess.WindowCount; + } + else + { + var evalEvent = eventsPerStream[_streamNumber]; + var relativeAccess = _relativeAccessGetter.GetAccessor(evalEvent); + if (relativeAccess == null) + { + return null; + } + size = relativeAccess.GetWindowToEventCount(); + events = relativeAccess.GetWindowToEvent(); + } + + if (size <= 0) + { + return new object[0]; + } + + var originalEvent = eventsPerStream[_streamNumber]; + var deque = new LinkedList(); + for (int i = 0; i < size; i++) + { + events.MoveNext(); + eventsPerStream[_streamNumber] = events.Current; + var evalResult = _evalNode.Evaluate(new EvaluateParams(eventsPerStream, true, context)); + deque.AddLast(evalResult); + } + eventsPerStream[_streamNumber] = originalEvent; + return deque; + } + + public EventBean EvaluateGetEventBean(EventBean[] eventsPerStream, ExprEvaluatorContext context) + { + return null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/prev/ExprPreviousMatchRecognizeEvalStrategy.cs b/NEsper.Core/NEsper.Core/epl/expression/prev/ExprPreviousMatchRecognizeEvalStrategy.cs new file mode 100755 index 000000000..13391c99e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/prev/ExprPreviousMatchRecognizeEvalStrategy.cs @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.prev +{ + public interface ExprPreviousMatchRecognizeEvalStrategy + { + Object Evaluate(EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/prev/ExprPreviousMatchRecognizeNode.cs b/NEsper.Core/NEsper.Core/epl/expression/prev/ExprPreviousMatchRecognizeNode.cs new file mode 100755 index 000000000..7fc142658 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/prev/ExprPreviousMatchRecognizeNode.cs @@ -0,0 +1,151 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +using com.espertech.esper.compat; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.rowregex; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.prev +{ + /// + /// Represents the 'prev' previous event function in match-recognize "define" item. + /// + [Serializable] + public class ExprPreviousMatchRecognizeNode : ExprNodeBase, ExprEvaluator + { + private Type _resultType; + private int _streamNumber; + private int? _constantIndexNumber; + + [NonSerialized] private RegexExprPreviousEvalStrategy _strategy; + [NonSerialized] private ExprEvaluator _evaluator; + private int _assignedIndex; + + public override ExprNode Validate(ExprValidationContext validationContext) + { + if (ChildNodes.Count != 2) + { + throw new ExprValidationException("Match-Recognize Previous expression must have 2 parameters"); + } + + if (!(ChildNodes[0] is ExprIdentNode)) + { + throw new ExprValidationException("Match-Recognize Previous expression requires an property identifier as the first parameter"); + } + + if (!ChildNodes[1].IsConstantResult || (!ChildNodes[1].ExprEvaluator.ReturnType.IsNumericNonFP())) + { + throw new ExprValidationException("Match-Recognize Previous expression requires an integer index parameter or expression as the second parameter"); + } + + var constantNode = ChildNodes[1]; + var value = constantNode.ExprEvaluator.Evaluate(new EvaluateParams(null, false, validationContext.ExprEvaluatorContext)); + if (!value.IsNumber()) + { + throw new ExprValidationException("Match-Recognize Previous expression requires an integer index parameter or expression as the second parameter"); + } + _constantIndexNumber = value.AsInt(); + + // Determine stream number + var identNode = (ExprIdentNode) ChildNodes[0]; + _streamNumber = identNode.StreamId; + _evaluator = ChildNodes[0].ExprEvaluator; + _resultType = _evaluator.ReturnType; + + return null; + } + + public override ExprEvaluator ExprEvaluator + { + get { return this; } + } + + /// Returns the index number. + /// index number + public int ConstantIndexNumber + { + get + { + if (_constantIndexNumber == null) + { + var constantNode = ChildNodes[1]; + var value = constantNode.ExprEvaluator.Evaluate(new EvaluateParams(null, false, null)); + _constantIndexNumber = value.AsInt(); + } + return _constantIndexNumber.Value; + } + } + + public Type ReturnType + { + get { return _resultType; } + } + + public override bool IsConstantResult + { + get { return false; } + } + + public object Evaluate(EvaluateParams evaluateParams) + { + var access = _strategy.GetAccess(evaluateParams.ExprEvaluatorContext); + var substituteEvent = access.GetPreviousEvent(_assignedIndex); + + if (substituteEvent == null) + { + return null; + } + + // Substitute original event with prior event, evaluate inner expression + var eventsPerStream = evaluateParams.EventsPerStream; + var originalEvent = eventsPerStream[_streamNumber]; + eventsPerStream[_streamNumber] = substituteEvent; + var evalResult = _evaluator.Evaluate(evaluateParams); + eventsPerStream[_streamNumber] = originalEvent; + + return evalResult; + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + writer.Write("prev("); + ChildNodes[0].ToEPL(writer, ExprPrecedenceEnum.MINIMUM); + writer.Write(','); + ChildNodes[1].ToEPL(writer, ExprPrecedenceEnum.MINIMUM); + writer.Write(')'); + } + + public override ExprPrecedenceEnum Precedence + { + get { return ExprPrecedenceEnum.UNARY; } + } + + public override bool EqualsNode(ExprNode node) + { + return node is ExprPreviousMatchRecognizeNode; + } + + /// Sets the index to use when accessing via getter + /// index + public int AssignedIndex + { + get { return _assignedIndex; } + set { _assignedIndex = value; } + } + + public RegexExprPreviousEvalStrategy Strategy + { + get { return _strategy; } + set { _strategy = value; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/prev/ExprPreviousNode.cs b/NEsper.Core/NEsper.Core/epl/expression/prev/ExprPreviousNode.cs new file mode 100755 index 000000000..2a154f23c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/prev/ExprPreviousNode.cs @@ -0,0 +1,298 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.prev +{ + /// + /// Represents the 'prev' previous event function in an expression node tree. + /// + [Serializable] + public class ExprPreviousNode + : ExprNodeBase + , ExprEvaluator + , ExprEvaluatorEnumeration + { + private readonly ExprPreviousNodePreviousType? _previousType; + + private Type _resultType; + [NonSerialized] private EventType _enumerationMethodType; + + [NonSerialized] private ExprPreviousEvalStrategy _evaluator; + + public ExprPreviousNode(ExprPreviousNodePreviousType? previousType) + { + _previousType = previousType; + } + + public override ExprEvaluator ExprEvaluator + { + get { return this; } + } + + public ExprPreviousEvalStrategy Evaluator + { + get { return _evaluator; } + set { _evaluator = value; } + } + + public int StreamNumber { get; private set; } + + public int? ConstantIndexNumber { get; private set; } + + public bool IsConstantIndex { get; private set; } + + public Type ResultType + { + get { return _resultType; } + } + + public override ExprNode Validate(ExprValidationContext validationContext) + { + if ((ChildNodes.Count > 2) || (ChildNodes.Count == 0)) + { + throw new ExprValidationException("Previous node must have 1 or 2 parameters"); + } + + // add constant of 1 for previous index + if (ChildNodes.Count == 1) + { + if (_previousType == ExprPreviousNodePreviousType.PREV) + { + AddChildNodeToFront(new ExprConstantNodeImpl(1)); + } + else + { + AddChildNodeToFront(new ExprConstantNodeImpl(0)); + } + } + + // the row recognition patterns allows "Prev(prop, index)", we switch index the first position + if (ExprNodeUtility.IsConstantValueExpr(ChildNodes[1])) + { + var first = ChildNodes[0]; + var second = ChildNodes[1]; + SetChildNodes(second, first); + } + + // Determine if the index is a constant value or an expression to evaluate + if (ChildNodes[0].IsConstantResult) + { + var constantNode = ChildNodes[0]; + var value = constantNode.ExprEvaluator.Evaluate(new EvaluateParams(null, false, validationContext.ExprEvaluatorContext)); + if (!(value.IsNumber())) + { + throw new ExprValidationException( + "Previous function requires an integer index parameter or expression"); + } + + if (TypeHelper.IsFloatingPointNumber(value)) + { + throw new ExprValidationException( + "Previous function requires an integer index parameter or expression"); + } + + ConstantIndexNumber = value.AsInt(); + IsConstantIndex = true; + } + + // Determine stream number + if (ChildNodes[1] is ExprIdentNode) + { + var identNode = (ExprIdentNode) ChildNodes[1]; + StreamNumber = identNode.StreamId; + _resultType = ChildNodes[1].ExprEvaluator.ReturnType.GetBoxedType(); + } + else if (ChildNodes[1] is ExprStreamUnderlyingNode) + { + var streamNode = (ExprStreamUnderlyingNode) ChildNodes[1]; + StreamNumber = streamNode.StreamId; + _resultType = ChildNodes[1].ExprEvaluator.ReturnType.GetBoxedType(); + _enumerationMethodType = validationContext.StreamTypeService.EventTypes[streamNode.StreamId]; + } + else + { + throw new ExprValidationException("Previous function requires an event property as parameter"); + } + + if (_previousType == ExprPreviousNodePreviousType.PREVCOUNT) + { + _resultType = typeof (long); + } + if (_previousType == ExprPreviousNodePreviousType.PREVWINDOW) + { + _resultType = TypeHelper.GetArrayType(_resultType); + } + + if (validationContext.ViewResourceDelegate == null) + { + throw new ExprValidationException("Previous function cannot be used in this context"); + } + validationContext.ViewResourceDelegate.AddPreviousRequest(this); + return null; + } + + public ExprPreviousNodePreviousType? PreviousType + { + get { return _previousType; } + } + + public Type ReturnType + { + get { return _resultType; } + } + + public override bool IsConstantResult + { + get { return false; } + } + + public ICollection EvaluateGetROCollectionEvents(EvaluateParams evaluateParams) + { + if (!evaluateParams.IsNewData) + { + return null; + } + return Evaluator.EvaluateGetCollEvents(evaluateParams.EventsPerStream, evaluateParams.ExprEvaluatorContext); + } + + public EventBean EvaluateGetEventBean(EvaluateParams evaluateParams) + { + if (!evaluateParams.IsNewData) + { + return null; + } + return Evaluator.EvaluateGetEventBean(evaluateParams.EventsPerStream, evaluateParams.ExprEvaluatorContext); + } + + public ICollection EvaluateGetROCollectionScalar(EvaluateParams evaluateParams) + { + if (!evaluateParams.IsNewData) + { + return null; + } + return Evaluator.EvaluateGetCollScalar(evaluateParams.EventsPerStream, evaluateParams.ExprEvaluatorContext); + } + + public EventType GetEventTypeCollection(EventAdapterService eventAdapterService, int statementId) + { + if (_previousType == ExprPreviousNodePreviousType.PREV || + _previousType == ExprPreviousNodePreviousType.PREVTAIL) + { + return null; + } + return _enumerationMethodType; + } + + public EventType GetEventTypeSingle(EventAdapterService eventAdapterService, int statementId) + { + if (_previousType == ExprPreviousNodePreviousType.PREV || + _previousType == ExprPreviousNodePreviousType.PREVTAIL) + { + return _enumerationMethodType; + } + return null; + } + + public Type ComponentTypeCollection + { + get + { + if (_resultType.IsArray) + { + return ResultType.GetElementType(); + } + return _resultType; + } + } + + public object Evaluate(EvaluateParams evaluateParams) + { + return Evaluate( + evaluateParams.EventsPerStream, + evaluateParams.IsNewData, + evaluateParams.ExprEvaluatorContext + ); + } + + public Object Evaluate(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext exprEvaluatorContext) + { + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().QExprPrev(this, isNewData); + Object result = null; + if (isNewData) + { + result = _evaluator.Evaluate(eventsPerStream, exprEvaluatorContext); + } + InstrumentationHelper.Get().AExprPrev(result); + return result; + } + + if (!isNewData) + { + return null; + } + return Evaluator.Evaluate(eventsPerStream, exprEvaluatorContext); + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + writer.Write(_previousType.ToString().ToLowerInvariant()); + writer.Write("("); + if (_previousType == ExprPreviousNodePreviousType.PREVCOUNT || + _previousType == ExprPreviousNodePreviousType.PREVWINDOW) + { + ChildNodes[1].ToEPL(writer, ExprPrecedenceEnum.MINIMUM); + } + else + { + ChildNodes[0].ToEPL(writer, ExprPrecedenceEnum.MINIMUM); + writer.Write(","); + ChildNodes[1].ToEPL(writer, ExprPrecedenceEnum.MINIMUM); + } + writer.Write(')'); + } + + public override ExprPrecedenceEnum Precedence + { + get { return ExprPrecedenceEnum.UNARY; } + } + + public override int GetHashCode() + { + return _previousType != null ? _previousType.GetHashCode() : 0; + } + + public override bool EqualsNode(ExprNode node) + { + var that = node as ExprPreviousNode; + if (that == null) + { + return false; + } + + if (_previousType != that._previousType) + { + return false; + } + + return true; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/prev/ExprPreviousNodePreviousType.cs b/NEsper.Core/NEsper.Core/epl/expression/prev/ExprPreviousNodePreviousType.cs new file mode 100755 index 000000000..c1ac188ac --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/prev/ExprPreviousNodePreviousType.cs @@ -0,0 +1,18 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.expression.prev +{ + public enum ExprPreviousNodePreviousType + { + PREV, + PREVTAIL, + PREVWINDOW, + PREVCOUNT + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/prior/ExprPriorEvalStrategy.cs b/NEsper.Core/NEsper.Core/epl/expression/prior/ExprPriorEvalStrategy.cs new file mode 100755 index 000000000..062a459f8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/prior/ExprPriorEvalStrategy.cs @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.prior +{ + /// Represents the 'prior' prior event resolution strategy for use in an expression node tree. + public interface ExprPriorEvalStrategy + { + Object Evaluate(EventBean[] eventsPerStream, + bool isNewData, + ExprEvaluatorContext exprEvaluatorContext, + int streamNumber, + ExprEvaluator evaluator, + int constantIndexNumber); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/prior/ExprPriorEvalStrategyBase.cs b/NEsper.Core/NEsper.Core/epl/expression/prior/ExprPriorEvalStrategyBase.cs new file mode 100755 index 000000000..d06cb5ba5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/prior/ExprPriorEvalStrategyBase.cs @@ -0,0 +1,36 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.prior +{ + /// + /// Represents the 'prior' prior event function in an expression node tree. + /// + public abstract class ExprPriorEvalStrategyBase : ExprPriorEvalStrategy + { + public abstract EventBean GetSubstituteEvent(EventBean originalEvent, bool isNewData, int constantIndexNumber); + + public Object Evaluate(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext exprEvaluatorContext, int streamNumber, ExprEvaluator evaluator, int constantIndexNumber) + { + var originalEvent = eventsPerStream[streamNumber]; + var substituteEvent = GetSubstituteEvent(originalEvent, isNewData, constantIndexNumber); + + // Substitute original event with prior event, evaluate inner expression + eventsPerStream[streamNumber] = substituteEvent; + Object evalResult = evaluator.Evaluate(new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext)); + eventsPerStream[streamNumber] = originalEvent; + + return evalResult; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/prior/ExprPriorEvalStrategyRandomAccess.cs b/NEsper.Core/NEsper.Core/epl/expression/prior/ExprPriorEvalStrategyRandomAccess.cs new file mode 100755 index 000000000..86557985d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/prior/ExprPriorEvalStrategyRandomAccess.cs @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.view.window; + +namespace com.espertech.esper.epl.expression.prior +{ + /// + /// Represents the 'prior' prior event function in an expression node tree. + /// + [Serializable] + public class ExprPriorEvalStrategyRandomAccess : ExprPriorEvalStrategyBase + { + [NonSerialized] private readonly RandomAccessByIndex _randomAccess; + + public ExprPriorEvalStrategyRandomAccess(RandomAccessByIndex randomAccess) { + this._randomAccess = randomAccess; + } + + public override EventBean GetSubstituteEvent(EventBean originalEvent, bool isNewData, int constantIndexNumber) { + if (isNewData) + { + return _randomAccess.GetNewData(constantIndexNumber); + } + else + { + return _randomAccess.GetOldData(constantIndexNumber); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/prior/ExprPriorEvalStrategyRelativeAccess.cs b/NEsper.Core/NEsper.Core/epl/expression/prior/ExprPriorEvalStrategyRelativeAccess.cs new file mode 100755 index 000000000..5814a1c31 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/prior/ExprPriorEvalStrategyRelativeAccess.cs @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.view.window; + +namespace com.espertech.esper.epl.expression.prior +{ + /// + /// Represents the 'prior' prior event function in an expression node tree. + /// + public class ExprPriorEvalStrategyRelativeAccess : ExprPriorEvalStrategyBase + { + [NonSerialized] + private readonly RelativeAccessByEventNIndex _relativeAccess; + + public ExprPriorEvalStrategyRelativeAccess(RelativeAccessByEventNIndex relativeAccess) + { + _relativeAccess = relativeAccess; + } + + public override EventBean GetSubstituteEvent(EventBean originalEvent, bool isNewData, int constantIndexNumber) + { + return _relativeAccess.GetRelativeToEvent(originalEvent, constantIndexNumber); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/prior/ExprPriorNode.cs b/NEsper.Core/NEsper.Core/epl/expression/prior/ExprPriorNode.cs new file mode 100755 index 000000000..2a9076bea --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/prior/ExprPriorNode.cs @@ -0,0 +1,155 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.prior +{ + /// + /// Represents the 'prior' prior event function in an expression node tree. + /// + [Serializable] + public class ExprPriorNode : ExprNodeBase, ExprEvaluator + { + private Type _resultType; + private int _streamNumber; + private int _constantIndexNumber; + [NonSerialized] private ExprPriorEvalStrategy _priorStrategy; + [NonSerialized] private ExprEvaluator _innerEvaluator; + + public override ExprEvaluator ExprEvaluator + { + get { return this; } + } + + public int StreamNumber + { + get { return _streamNumber; } + } + + public int ConstantIndexNumber + { + get { return _constantIndexNumber; } + } + + public ExprPriorEvalStrategy PriorStrategy + { + set { _priorStrategy = value; } + } + + public ExprEvaluator InnerEvaluator + { + get { return _innerEvaluator; } + } + + public override ExprNode Validate(ExprValidationContext validationContext) + { + if (ChildNodes.Count != 2) + { + throw new ExprValidationException("Prior node must have 2 parameters"); + } + if (!(ChildNodes[0].IsConstantResult)) + { + throw new ExprValidationException("Prior function requires a constant-value integer-typed index expression as the first parameter"); + } + + var constantNode = ChildNodes[0]; + if (constantNode.ExprEvaluator.ReturnType.GetBoxedType() != typeof(int?)) + { + throw new ExprValidationException("Prior function requires an integer index parameter"); + } + + var value = constantNode.ExprEvaluator.Evaluate( + new EvaluateParams(null, false, validationContext.ExprEvaluatorContext)); + _constantIndexNumber = value.AsInt(); + _innerEvaluator = ChildNodes[1].ExprEvaluator; + + // Determine stream number + // Determine stream number + if (ChildNodes[1] is ExprIdentNode) { + var identNode = (ExprIdentNode) ChildNodes[1]; + _streamNumber = identNode.StreamId; + _resultType = _innerEvaluator.ReturnType; + } + else if (ChildNodes[1] is ExprStreamUnderlyingNode) { + var streamNode = (ExprStreamUnderlyingNode) ChildNodes[1]; + _streamNumber = streamNode.StreamId; + _resultType = _innerEvaluator.ReturnType; + } + else + { + throw new ExprValidationException("Previous function requires an event property as parameter"); + } + + // add request + if (validationContext.ViewResourceDelegate == null) { + throw new ExprValidationException("Prior function cannot be used in this context"); + } + validationContext.ViewResourceDelegate.AddPriorNodeRequest(this); + + return null; + } + + public Type ReturnType + { + get { return _resultType; } + } + + public override bool IsConstantResult + { + get { return false; } + } + + public object Evaluate(EvaluateParams evaluateParams) + { + return Evaluate( + evaluateParams.EventsPerStream, + evaluateParams.IsNewData, + evaluateParams.ExprEvaluatorContext); + } + + public Object Evaluate(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext exprEvaluatorContext) + { + var result = new Mutable(); + + using(Instrument.With( + i => i.QExprPrior(this), + i => i.AExprPrior(result))) + { + result.Value = _priorStrategy.Evaluate(eventsPerStream, isNewData, exprEvaluatorContext, _streamNumber, _innerEvaluator, _constantIndexNumber); + return result.Value; + } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + writer.Write("prior("); + ChildNodes[0].ToEPL(writer, ExprPrecedenceEnum.MINIMUM); + writer.Write(','); + ChildNodes[1].ToEPL(writer, ExprPrecedenceEnum.MINIMUM); + writer.Write(')'); + } + + public override ExprPrecedenceEnum Precedence + { + get { return ExprPrecedenceEnum.UNARY; } + } + + public override bool EqualsNode(ExprNode node) + { + return node is ExprPriorNode; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/subquery/ExprSubselectAllSomeAnyNode.cs b/NEsper.Core/NEsper.Core/epl/expression/subquery/ExprSubselectAllSomeAnyNode.cs new file mode 100755 index 000000000..aafb94434 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/subquery/ExprSubselectAllSomeAnyNode.cs @@ -0,0 +1,176 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; +using com.espertech.esper.events; +using com.espertech.esper.type; + +namespace com.espertech.esper.epl.expression.subquery +{ + /// Represents a subselect in an expression tree. + [Serializable] + public class ExprSubselectAllSomeAnyNode : ExprSubselectNode + { + private static readonly ILog Log = + LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly bool _isNot; + private readonly bool _isAll; + private readonly RelationalOpEnum? _relationalOp; + [NonSerialized] private SubselectEvalStrategyNR _evalStrategy; + + /// + /// Ctor. + /// + /// is the lookup statement spec from the parser, unvalidated + /// when NOT + /// when ALL, false for ANY + /// operator + public ExprSubselectAllSomeAnyNode( + StatementSpecRaw statementSpec, + bool not, + bool all, + RelationalOpEnum? relationalOpEnum) + : base(statementSpec) + { + _isNot = not; + _isAll = all; + _relationalOp = relationalOpEnum; + } + + /// + /// Returns true for not. + /// + /// not indicator + public bool IsNot + { + get { return _isNot; } + } + + /// + /// Returns true for all. + /// + /// all indicator + public bool IsAll + { + get { return _isAll; } + } + + /// + /// Returns relational op. + /// + /// op + public RelationalOpEnum? RelationalOp + { + get { return _relationalOp; } + } + + public override Type ReturnType + { + get { return typeof (bool?); } + } + + public override void ValidateSubquery(ExprValidationContext validationContext) + { + _evalStrategy = SubselectEvalStrategyNRFactory.CreateStrategyAnyAllIn( + this, _isNot, _isAll, !_isAll, _relationalOp); + } + + public override Object Evaluate( + EventBean[] eventsPerStream, + bool isNewData, + ICollection matchingEvents, + ExprEvaluatorContext exprEvaluatorContext) + { + return _evalStrategy.Evaluate( + eventsPerStream, isNewData, matchingEvents, exprEvaluatorContext, SubselectAggregationService); + } + + public override LinkedHashMap TypableGetRowProperties + { + get { return null; } + } + + public override Object[] EvaluateTypableSingle( + EventBean[] eventsPerStream, + bool isNewData, + ICollection matchingEvents, + ExprEvaluatorContext exprEvaluatorContext) + { + return null; + } + + public override Object[][] EvaluateTypableMulti( + EventBean[] eventsPerStream, + bool isNewData, + ICollection matchingEvents, + ExprEvaluatorContext exprEvaluatorContext) + { + return null; + } + + public override ICollection EvaluateGetCollEvents( + EventBean[] eventsPerStream, + bool isNewData, + ICollection matchingEvents, + ExprEvaluatorContext context) + { + return null; + } + + public override ICollection EvaluateGetCollScalar( + EventBean[] eventsPerStream, + bool isNewData, + ICollection matchingEvents, + ExprEvaluatorContext exprEvaluatorContext) + { + return null; + } + + public override EventType GetEventTypeCollection(EventAdapterService eventAdapterService, int statementId) + { + return null; + } + + public override Type ComponentTypeCollection + { + get { return null; } + } + + public override EventBean EvaluateGetEventBean( + EventBean[] eventsPerStream, + bool isNewData, + ICollection matchingEvents, + ExprEvaluatorContext exprEvaluatorContext) + { + return null; + } + + public override EventType GetEventTypeSingle(EventAdapterService eventAdapterService, int statementId) + { + return null; + } + + public override EventBean EvaluateGetEventBean(EvaluateParams evaluateParams) + { + return null; + } + + public override bool IsAllowMultiColumnSelect + { + get { return false; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/subquery/ExprSubselectExistsNode.cs b/NEsper.Core/NEsper.Core/epl/expression/subquery/ExprSubselectExistsNode.cs new file mode 100755 index 000000000..b5e20d473 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/subquery/ExprSubselectExistsNode.cs @@ -0,0 +1,134 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.expression.subquery +{ + /// Represents an exists-subselect in an expression tree. + [Serializable] + public class ExprSubselectExistsNode : ExprSubselectNode + { + private static readonly ILog Log = + LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + [NonSerialized] private SubselectEvalStrategyNR _subselectEvalStrategyNr; + + /// + /// Ctor. + /// + /// is the lookup statement spec from the parser, unvalidated + public ExprSubselectExistsNode(StatementSpecRaw statementSpec) + : base(statementSpec) + { + } + + public override Type ReturnType + { + get { return typeof (bool?); } + } + + public override void ValidateSubquery(ExprValidationContext validationContext) + { + _subselectEvalStrategyNr = SubselectEvalStrategyNRFactory.CreateStrategyExists(this); + } + + public override Object Evaluate( + EventBean[] eventsPerStream, + bool isNewData, + ICollection matchingEvents, + ExprEvaluatorContext exprEvaluatorContext) + { + return _subselectEvalStrategyNr.Evaluate( + eventsPerStream, isNewData, matchingEvents, exprEvaluatorContext, SubselectAggregationService); + } + + public override LinkedHashMap TypableGetRowProperties + { + get { return null; } + } + + public override Object[] EvaluateTypableSingle( + EventBean[] eventsPerStream, + bool isNewData, + ICollection matchingEvents, + ExprEvaluatorContext exprEvaluatorContext) + { + return null; + } + + public override Object[][] EvaluateTypableMulti( + EventBean[] eventsPerStream, + bool isNewData, + ICollection matchingEvents, + ExprEvaluatorContext exprEvaluatorContext) + { + return null; + } + + public override ICollection EvaluateGetCollEvents( + EventBean[] eventsPerStream, + bool isNewData, + ICollection matchingEvents, + ExprEvaluatorContext context) + { + return null; + } + + public override EventBean EvaluateGetEventBean( + EventBean[] eventsPerStream, + bool isNewData, + ICollection matchingEvents, + ExprEvaluatorContext exprEvaluatorContext) + { + return null; + } + + public override EventType GetEventTypeCollection(EventAdapterService eventAdapterService, int statementId) + { + return null; + } + + public override Type ComponentTypeCollection + { + get { return null; } + } + + public override ICollection EvaluateGetCollScalar( + EventBean[] eventsPerStream, + bool isNewData, + ICollection matchingEvents, + ExprEvaluatorContext exprEvaluatorContext) + { + return null; + } + + public override bool IsAllowMultiColumnSelect + { + get { return false; } + } + + public override EventType GetEventTypeSingle(EventAdapterService eventAdapterService, int statementId) + { + return null; + } + + public override EventBean EvaluateGetEventBean(EvaluateParams evaluateParams) + { + return null; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/subquery/ExprSubselectInNode.cs b/NEsper.Core/NEsper.Core/epl/expression/subquery/ExprSubselectInNode.cs new file mode 100755 index 000000000..4f01ad212 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/subquery/ExprSubselectInNode.cs @@ -0,0 +1,102 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.expression.subquery +{ + /// Represents a subselect in an expression tree. + [Serializable] + public class ExprSubselectInNode : ExprSubselectNode + { + private readonly bool _isNotIn; + [NonSerialized] private SubselectEvalStrategyNR _subselectEvalStrategyNr; + + public ExprSubselectInNode(StatementSpecRaw statementSpec, bool isNotIn) + : base(statementSpec) + { + _isNotIn = isNotIn; + } + + public override Type ReturnType + { + get { return typeof (bool?); } + } + + /// + /// Returns true for not-in, or false for in. + /// + /// true for not-in + public bool IsNotIn + { + get { return _isNotIn; } + } + + public override void ValidateSubquery(ExprValidationContext validationContext) { + _subselectEvalStrategyNr = SubselectEvalStrategyNRFactory.CreateStrategyAnyAllIn(this, _isNotIn, false, false, null); + } + + public override Object Evaluate(EventBean[] eventsPerStream, bool isNewData, ICollection matchingEvents, ExprEvaluatorContext exprEvaluatorContext) { + return _subselectEvalStrategyNr.Evaluate(eventsPerStream, isNewData, matchingEvents, exprEvaluatorContext, SubselectAggregationService); + } + + public override LinkedHashMap TypableGetRowProperties + { + get { return null; } + } + + public override Object[] EvaluateTypableSingle(EventBean[] eventsPerStream, bool isNewData, ICollection matchingEvents, ExprEvaluatorContext exprEvaluatorContext) { + return null; + } + + public override Object[][] EvaluateTypableMulti(EventBean[] eventsPerStream, bool isNewData, ICollection matchingEvents, ExprEvaluatorContext exprEvaluatorContext) { + return null; + } + + public override ICollection EvaluateGetCollEvents(EventBean[] eventsPerStream, bool isNewData, ICollection matchingEvents, ExprEvaluatorContext context) { + return null; + } + + public override EventType GetEventTypeCollection(EventAdapterService eventAdapterService, int statementId) { + return null; + } + + public override Type ComponentTypeCollection + { + get { return null; } + } + + public override EventBean EvaluateGetEventBean(EventBean[] eventsPerStream, bool isNewData, ICollection matchingEvents, ExprEvaluatorContext exprEvaluatorContext) { + return null; + } + + public override ICollection EvaluateGetCollScalar(EventBean[] eventsPerStream, bool isNewData, ICollection matchingEvents, ExprEvaluatorContext exprEvaluatorContext) { + return null; + } + + public override bool IsAllowMultiColumnSelect + { + get { return false; } + } + + public override EventType GetEventTypeSingle(EventAdapterService eventAdapterService, int statementId) { + return null; + } + + public override EventBean EvaluateGetEventBean(EvaluateParams evaluateParams) { + return null; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/subquery/ExprSubselectNode.cs b/NEsper.Core/NEsper.Core/epl/expression/subquery/ExprSubselectNode.cs new file mode 100755 index 000000000..11881bc3d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/subquery/ExprSubselectNode.cs @@ -0,0 +1,357 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; +using com.espertech.esper.events; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.expression.subquery +{ + /// Represents a subselect in an expression tree. + [Serializable] + public abstract class ExprSubselectNode + : ExprNodeBase + , ExprEvaluator + , ExprEvaluatorEnumeration + , ExprEvaluatorTypableReturn + { + public static readonly ExprSubselectNode[] EMPTY_SUBSELECT_ARRAY = new ExprSubselectNode[0]; + + /// The validated select clause. + private ExprNode[] _selectClause; + [NonSerialized] private ExprEvaluator[] _selectClauseEvaluator; + + private string[] _selectAsNames; + + /// The validate filter expression. + [NonSerialized] private ExprEvaluator _filterExpr; + + /// The validated having expression. + [NonSerialized] private ExprEvaluator _havingExpr; + + /// The event type generated for wildcard selects. + [NonSerialized] private EventType _rawEventType; + + private string _statementName; + private int _subselectNumber; + [NonSerialized] private AggregationService _subselectAggregationService; + [NonSerialized] private StreamTypeService _filterSubqueryStreamTypes; + private readonly StatementSpecRaw _statementSpecRaw; + [NonSerialized] private StatementSpecCompiled _statementSpecCompiled; + [NonSerialized] private ExprSubselectStrategy _strategy; + [NonSerialized] private SubqueryAggregationType _subselectAggregationType; + private bool _filterStreamSubselect; + + /// + /// Ctor. + /// + /// is the lookup statement spec from the parser, unvalidated + protected ExprSubselectNode(StatementSpecRaw statementSpec) + { + _statementSpecRaw = statementSpec; + } + + public static ExprSubselectNode[] ToArray(IList subselectNodes) + { + if (subselectNodes.IsEmpty()) + { + return EMPTY_SUBSELECT_ARRAY; + } + return subselectNodes.ToArray(); + } + + public virtual object Evaluate(EvaluateParams evaluateParams) + { + return Evaluate( + evaluateParams.EventsPerStream, + evaluateParams.IsNewData, + evaluateParams.ExprEvaluatorContext + ); + } + + /// + /// Evaluate the lookup expression returning an evaluation result object. + /// + /// is the events for each stream in a join + /// is true for new data, or false for old data + /// is filtered results from the table of stored lookup events + /// context for expression evalauation + /// evaluation result + public abstract Object Evaluate(EventBean[] eventsPerStream, bool isNewData, ICollection matchingEvents, ExprEvaluatorContext exprEvaluatorContext); + + public abstract ICollection EvaluateGetCollEvents(EventBean[] eventsPerStream, bool isNewData, ICollection matchingEvents, ExprEvaluatorContext exprEvaluatorContext); + + public abstract ICollection EvaluateGetCollScalar(EventBean[] eventsPerStream, bool isNewData, ICollection matchingEvents, ExprEvaluatorContext exprEvaluatorContext); + + public abstract EventBean EvaluateGetEventBean(EventBean[] eventsPerStream, bool isNewData, ICollection matchingEvents, ExprEvaluatorContext exprEvaluatorContext); + + public abstract bool IsAllowMultiColumnSelect { get; } + + public abstract void ValidateSubquery(ExprValidationContext validationContext) ; + + public abstract LinkedHashMap TypableGetRowProperties { get; } + + public abstract Object[] EvaluateTypableSingle( + EventBean[] eventsPerStream, + bool isNewData, + ICollection matchingEvents, + ExprEvaluatorContext exprEvaluatorContext); + + public abstract Object[][] EvaluateTypableMulti( + EventBean[] eventsPerStream, + bool isNewData, + ICollection matchingEvents, + ExprEvaluatorContext exprEvaluatorContext); + + public override ExprEvaluator ExprEvaluator + { + get { return this; } + } + + public override bool IsConstantResult + { + get { return false; } + } + + public ExprEvaluator[] SelectClauseEvaluator + { + get { return _selectClauseEvaluator; } + } + + public string StatementName + { + get { return _statementName; } + } + + /// + /// Sets the strategy for boiling down the table of lookup events into a subset against which to run the filter. + /// + /// is the looking strategy (full table scan or indexed) + public ExprSubselectStrategy Strategy + { + get { return _strategy; } + set { _strategy = value; } + } + + public bool FilterStreamSubselect + { + get { return _filterStreamSubselect; } + } + + public override ExprNode Validate(ExprValidationContext validationContext) { + _statementName = validationContext.StatementName; + ValidateSubquery(validationContext); + return null; + } + + /// + /// Supplies a compiled statement spec. + /// + /// compiled validated filters + /// subselect assigned number + public void SetStatementSpecCompiled(StatementSpecCompiled statementSpecCompiled, int subselectNumber) { + _statementSpecCompiled = statementSpecCompiled; + _subselectNumber = subselectNumber; + } + + /// + /// Returns the compiled statement spec. + /// + /// compiled statement + public StatementSpecCompiled StatementSpecCompiled + { + get { return _statementSpecCompiled; } + } + + public Object Evaluate(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext exprEvaluatorContext) { + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().QExprSubselect(this); + ICollection matchingEventsX = EvaluateMatching(eventsPerStream, exprEvaluatorContext); + Object result = Evaluate(eventsPerStream, isNewData, matchingEventsX, exprEvaluatorContext); + InstrumentationHelper.Get().AExprSubselect(result); + return result; + } + ICollection matchingEvents = EvaluateMatching(eventsPerStream, exprEvaluatorContext); + return Evaluate(eventsPerStream, isNewData, matchingEvents, exprEvaluatorContext); + } + + public ICollection EvaluateGetROCollectionEvents(EvaluateParams evaluateParams) { + ICollection matchingEvents = EvaluateMatching(evaluateParams.EventsPerStream, evaluateParams.ExprEvaluatorContext); + return EvaluateGetCollEvents(evaluateParams.EventsPerStream, evaluateParams.IsNewData, matchingEvents, evaluateParams.ExprEvaluatorContext); + } + + public ICollection EvaluateGetROCollectionScalar(EvaluateParams evaluateParams) { + ICollection matchingEvents = EvaluateMatching(evaluateParams.EventsPerStream, evaluateParams.ExprEvaluatorContext); + return EvaluateGetCollScalar(evaluateParams.EventsPerStream, evaluateParams.IsNewData, matchingEvents, evaluateParams.ExprEvaluatorContext); + } + + public virtual EventBean EvaluateGetEventBean(EvaluateParams evaluateParams) { + ICollection matchingEvents = EvaluateMatching(evaluateParams.EventsPerStream, evaluateParams.ExprEvaluatorContext); + return EvaluateGetEventBean(evaluateParams.EventsPerStream, evaluateParams.IsNewData, matchingEvents, evaluateParams.ExprEvaluatorContext); + } + + private ICollection EvaluateMatching(EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext) { + return _strategy.EvaluateMatching(eventsPerStream, exprEvaluatorContext); + } + + public IDictionary RowProperties + { + get { return TypableGetRowProperties; } + } + + public bool? IsMultirow + { + get { return true; } // subselect can always return multiple rows + } + + public Object[] EvaluateTypableSingle(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + ICollection matching = _strategy.EvaluateMatching(eventsPerStream, context); + return EvaluateTypableSingle(eventsPerStream, isNewData, matching, context); + } + + public Object[][] EvaluateTypableMulti(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + ICollection matching = _strategy.EvaluateMatching(eventsPerStream, context); + return EvaluateTypableMulti(eventsPerStream, isNewData, matching, context); + } + + /// + /// Returns the uncompiled statement spec. + /// + /// statement spec uncompiled + public StatementSpecRaw StatementSpecRaw + { + get { return _statementSpecRaw; } + } + + /// + /// Supplies the name of the select expression as-tag + /// + /// is the as-Name(s) + public string[] SelectAsNames + { + get { return _selectAsNames; } + set { _selectAsNames = value; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + if ((_selectAsNames != null) && (_selectAsNames[0] != null)) { + writer.Write(_selectAsNames[0]); + return; + } + writer.Write("subselect_"); + writer.Write(_subselectNumber); + } + + public override ExprPrecedenceEnum Precedence + { + get { return ExprPrecedenceEnum.UNARY; } + } + + public override bool EqualsNode(ExprNode node) { + return false; // 2 subselects are never equivalent + } + + /// + /// Returns the select clause or null if none. + /// + /// clause + public ExprNode[] SelectClause + { + get { return _selectClause; } + set + { + _selectClause = value; + _selectClauseEvaluator = ExprNodeUtility.GetEvaluators(value); + } + } + + /// + /// Returns filter expr or null if none. + /// + /// filter + public ExprEvaluator FilterExpr + { + get { return _filterExpr; } + set { _filterExpr = value; } + } + + public ExprEvaluator HavingExpr + { + get { return _havingExpr; } + set { _havingExpr = value; } + } + + /// + /// Returns the event type. + /// + /// type + public EventType RawEventType + { + get { return _rawEventType; } + set { _rawEventType = value; } + } + + /// + /// Return stream types. + /// + /// types + public StreamTypeService FilterSubqueryStreamTypes + { + get { return _filterSubqueryStreamTypes; } + set { _filterSubqueryStreamTypes = value; } + } + + public SubqueryAggregationType SubselectAggregationType + { + get { return _subselectAggregationType; } + set { _subselectAggregationType = value; } + } + + public int SubselectNumber + { + get { return _subselectNumber; } + } + + public bool IsFilterStreamSubselect + { + get { return _filterStreamSubselect; } + set { _filterStreamSubselect = value; } + } + + public AggregationService SubselectAggregationService + { + get { return _subselectAggregationService; } + set { _subselectAggregationService = value; } + } + + public abstract Type ReturnType { get; } + public abstract EventType GetEventTypeCollection(EventAdapterService eventAdapterService, int statementId); + public abstract Type ComponentTypeCollection { get; } + public abstract EventType GetEventTypeSingle(EventAdapterService eventAdapterService, int statementId); + + public enum SubqueryAggregationType + { + NONE, + FULLY_AGGREGATED_NOPROPS, + FULLY_AGGREGATED_WPROPS + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/subquery/ExprSubselectRowNode.cs b/NEsper.Core/NEsper.Core/epl/expression/subquery/ExprSubselectRowNode.cs new file mode 100755 index 000000000..a66310f7a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/subquery/ExprSubselectRowNode.cs @@ -0,0 +1,397 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; +using com.espertech.esper.events; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.subquery +{ + /// Represents a subselect in an expression tree. + [Serializable] + public class ExprSubselectRowNode : ExprSubselectNode + { + public static readonly SubselectEvalStrategyRow UNFILTERED_SELECTED = + new SubselectEvalStrategyRowUnfilteredSelected(); + + public static readonly SubselectEvalStrategyRow FILTERED_UNSELECTED = + new SubselectEvalStrategyRowFilteredUnselected(); + + public static readonly SubselectEvalStrategyRow FILTERED_SELECTED = + new SubselectEvalStrategyRowFilteredSelected(); + + public static readonly SubselectEvalStrategyRow HAVING_SELECTED = new SubselectEvalStrategyRowHavingSelected(); + + public static readonly SubselectEvalStrategyRow UNFILTERED_SELECTED_GROUPED = + new SubselectEvalStrategyRowUnfilteredSelectedGroupedNoHaving(); + + [NonSerialized] private SubselectMultirowType _subselectMultirowType; + [NonSerialized] private SubselectEvalStrategyRow _evalStrategy; + + /// + /// Ctor. + /// + /// is the lookup statement spec from the parser, unvalidated + public ExprSubselectRowNode(StatementSpecRaw statementSpec) + : base(statementSpec) + { + } + + public SubselectEvalStrategyRow EvalStrategy + { + get { return _evalStrategy; } + } + + internal SubselectMultirowType SubselectMultirowType + { + get { return _subselectMultirowType; } + } + + public override Type ReturnType + { + get + { + if (base.SelectClause == null) + { + // wildcards allowed + return RawEventType.UnderlyingType; + } + if (base.SelectClause.Length == 1) + { + return base.SelectClause[0].ExprEvaluator.ReturnType.GetBoxedType(); + } + return null; + } + } + + public override void ValidateSubquery(ExprValidationContext validationContext) + { + // Strategy for subselect depends on presence of filter + presence of select clause expressions + // the filter expression is handled elsewhere if there is any aggregation + if (FilterExpr == null) + { + if (SelectClause == null) + { + var tableMetadata = validationContext.TableService.GetTableMetadataFromEventType(RawEventType); + if (tableMetadata != null) + { + _evalStrategy = new SubselectEvalStrategyRowUnfilteredUnselectedTable(tableMetadata); + } + else + { + _evalStrategy = SubselectEvalStrategyRowUnfilteredUnselected.INSTANCE; + } + } + else + { + if (StatementSpecCompiled.GroupByExpressions != null && + StatementSpecCompiled.GroupByExpressions.GroupByNodes.Length > 0) + { + if (HavingExpr != null) + { + _evalStrategy = new SubselectEvalStrategyRowUnfilteredSelectedGroupedWHaving(HavingExpr); + } + else + { + _evalStrategy = UNFILTERED_SELECTED_GROUPED; + } + } + else + { + if (HavingExpr != null) + { + _evalStrategy = HAVING_SELECTED; + } + else + { + _evalStrategy = UNFILTERED_SELECTED; + } + } + } + } + else + { + if (SelectClause == null) + { + var tableMetadata = validationContext.TableService.GetTableMetadataFromEventType(RawEventType); + if (tableMetadata != null) + { + _evalStrategy = new SubselectEvalStrategyRowFilteredUnselectedTable(tableMetadata); + } + else + { + _evalStrategy = FILTERED_UNSELECTED; + } + } + else + { + _evalStrategy = FILTERED_SELECTED; + } + } + } + + public override Object Evaluate( + EventBean[] eventsPerStream, + bool isNewData, + ICollection matchingEvents, + ExprEvaluatorContext exprEvaluatorContext) + { + if (matchingEvents == null || matchingEvents.Count == 0) + { + return null; + } + return _evalStrategy.Evaluate(eventsPerStream, isNewData, matchingEvents, exprEvaluatorContext, this); + } + + public override ICollection EvaluateGetCollEvents( + EventBean[] eventsPerStream, + bool isNewData, + ICollection matchingEvents, + ExprEvaluatorContext context) + { + if (matchingEvents == null) + { + return null; + } + if (matchingEvents.Count == 0) + { + return Collections.GetEmptyList(); + } + return _evalStrategy.EvaluateGetCollEvents(eventsPerStream, isNewData, matchingEvents, context, this); + } + + public override ICollection EvaluateGetCollScalar( + EventBean[] eventsPerStream, + bool isNewData, + ICollection matchingEvents, + ExprEvaluatorContext context) + { + if (matchingEvents == null) + { + return null; + } + if (matchingEvents.Count == 0) + { + return Collections.GetEmptyList(); + } + return _evalStrategy.EvaluateGetCollScalar(eventsPerStream, isNewData, matchingEvents, context, this); + } + + public override EventBean EvaluateGetEventBean( + EventBean[] eventsPerStream, + bool isNewData, + ICollection matchingEvents, + ExprEvaluatorContext exprEvaluatorContext) + { + if (matchingEvents == null || matchingEvents.Count == 0) + { + return null; + } + return _evalStrategy.EvaluateGetEventBean( + eventsPerStream, isNewData, matchingEvents, exprEvaluatorContext, this); + } + + public override Object[] EvaluateTypableSingle( + EventBean[] eventsPerStream, + bool isNewData, + ICollection matchingEvents, + ExprEvaluatorContext exprEvaluatorContext) + { + + if (matchingEvents == null || matchingEvents.Count == 0) + { + return null; + } + return _evalStrategy.TypableEvaluate(eventsPerStream, isNewData, matchingEvents, exprEvaluatorContext, this); + } + + public override Object[][] EvaluateTypableMulti( + EventBean[] eventsPerStream, + bool isNewData, + ICollection matchingEvents, + ExprEvaluatorContext exprEvaluatorContext) + { + if (matchingEvents == null) + { + return null; + } + if (matchingEvents.Count == 0) + { + return new Object[0][]; + } + return _evalStrategy.TypableEvaluateMultirow( + eventsPerStream, isNewData, matchingEvents, exprEvaluatorContext, this); + } + + public override LinkedHashMap TypableGetRowProperties + { + get + { + if ((SelectClause == null) || (SelectClause.Length < 2)) + { + return null; + } + return RowType; + } + } + + public override EventType GetEventTypeSingle(EventAdapterService eventAdapterService, int statementId) + { + if (SelectClause == null) + { + return null; + } + if (this.SubselectAggregationType != SubqueryAggregationType.FULLY_AGGREGATED_NOPROPS) + { + return null; + } + return GetAssignAnonymousType(eventAdapterService, statementId); + } + + public override EventType GetEventTypeCollection(EventAdapterService eventAdapterService, int statementId) + { + var selectClause = SelectClause; + if (selectClause == null) + { + // wildcards allowed + return RawEventType; + } + + // special case: selecting a single property that is itself an event + if (selectClause.Length == 1 && selectClause[0] is ExprIdentNode) + { + var identNode = (ExprIdentNode) selectClause[0]; + var fragment = RawEventType.GetFragmentType(identNode.ResolvedPropertyName); + if (fragment != null && !fragment.IsIndexed) + { + return fragment.FragmentType; + } + } + + // select of a single value otherwise results in a collection of scalar values + if (selectClause.Length == 1) + { + return null; + } + + // fully-aggregated always returns zero or one row + if (this.SubselectAggregationType == SubqueryAggregationType.FULLY_AGGREGATED_NOPROPS) + { + return null; + } + + return GetAssignAnonymousType(eventAdapterService, statementId); + } + + private EventType GetAssignAnonymousType(EventAdapterService eventAdapterService, int statementId) + { + IDictionary rowType = RowType; + var resultEventType = + eventAdapterService.CreateAnonymousMapType( + statementId + "_subquery_" + this.SubselectNumber, rowType, true); + _subselectMultirowType = new SubselectMultirowType(resultEventType, eventAdapterService); + return resultEventType; + } + + public override Type ComponentTypeCollection + { + get + { + if (SelectClause == null) + { + // wildcards allowed + return null; + } + if (SelectClauseEvaluator.Length > 1) + { + return null; + } + return SelectClauseEvaluator[0].ReturnType; + } + } + + public override bool IsAllowMultiColumnSelect + { + get { return true; } + } + + private LinkedHashMap RowType + { + get + { + var uniqueNames = new HashSet(); + var type = new LinkedHashMap(); + + var selectAsNames = SelectAsNames; + var selectClause = SelectClause; + for (var i = 0; i < selectClause.Length; i++) + { + string assignedName = selectAsNames[i]; + if (assignedName == null) + { + assignedName = ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(selectClause[i]); + } + if (uniqueNames.Add(assignedName)) + { + type.Put(assignedName, selectClause[i].ExprEvaluator.ReturnType); + } + else + { + throw new ExprValidationException( + "Column " + i + " in subquery does not have a unique column name assigned"); + } + } + return type; + } + } + + public string GetMultirowMessage() + { + return "Subselect of statement '" + StatementName + "' returned more then one row in subselect " + + SubselectNumber + " '" + ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(this) + + "', returning null result"; + } + + internal IDictionary EvaluateRow( + EventBean[] eventsPerStream, + bool isNewData, + ExprEvaluatorContext context) + { + var selectAsNames = SelectAsNames; + var selectClauseEvaluator = SelectClauseEvaluator; + var evaluateParams = new EvaluateParams(eventsPerStream, isNewData, context); + var map = new Dictionary(); + for (var i = 0; i < selectClauseEvaluator.Length; i++) + { + var resultEntry = selectClauseEvaluator[i].Evaluate(evaluateParams); + map.Put(selectAsNames[i], resultEntry); + } + return map; + } + } + + internal class SubselectMultirowType + { + internal SubselectMultirowType(EventType eventType, EventAdapterService eventAdapterService) + { + EventType = eventType; + EventAdapterService = eventAdapterService; + } + + public EventType EventType { get; private set; } + + public EventAdapterService EventAdapterService { get; private set; } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/subquery/ExprSubselectRowNodeUtility.cs b/NEsper.Core/NEsper.Core/epl/expression/subquery/ExprSubselectRowNodeUtility.cs new file mode 100755 index 000000000..5490e5b79 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/subquery/ExprSubselectRowNodeUtility.cs @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.subquery +{ + public class ExprSubselectRowNodeUtility + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + public static EventBean EvaluateFilterExpectSingleMatch( + EventBean[] eventsZeroSubselect, + bool newData, + ICollection matchingEvents, + ExprEvaluatorContext exprEvaluatorContext, + ExprSubselectRowNode parent) + { + var evaluateParams = new EvaluateParams(eventsZeroSubselect, newData, exprEvaluatorContext); + + EventBean subSelectResult = null; + foreach (EventBean subselectEvent in matchingEvents) + { + // Prepare filter expression event list + eventsZeroSubselect[0] = subselectEvent; + + var pass = parent.FilterExpr.Evaluate(evaluateParams); + if ((pass != null) && true.Equals(pass)) + { + if (subSelectResult != null) + { + Log.Warn(parent.GetMultirowMessage()); + return null; + } + subSelectResult = subselectEvent; + } + } + + return subSelectResult; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/subquery/ExprSubselectStrategy.cs b/NEsper.Core/NEsper.Core/epl/expression/subquery/ExprSubselectStrategy.cs new file mode 100755 index 000000000..572409f58 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/subquery/ExprSubselectStrategy.cs @@ -0,0 +1,56 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.subquery +{ + /// + /// + /// + public interface ExprSubselectStrategy + { + ICollection EvaluateMatching(EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext); + } + + public class ProxyExprSubselectStrategy : ExprSubselectStrategy + { + public Func> ProcEvaluateMatching; + + /// + /// Initializes a new instance of the class. + /// + public ProxyExprSubselectStrategy() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The evaluate matching. + public ProxyExprSubselectStrategy(Func> procEvaluateMatching) + { + ProcEvaluateMatching = procEvaluateMatching; + } + + /// + /// Evaluates the matching. + /// + /// The events per stream. + /// The expr evaluator context. + /// + public ICollection EvaluateMatching(EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext) + { + return ProcEvaluateMatching.Invoke(eventsPerStream, exprEvaluatorContext); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNR.cs b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNR.cs new file mode 100755 index 000000000..4dcffe644 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNR.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.subquery +{ + /// Strategy for evaluation of a subselect. + public interface SubselectEvalStrategyNR + { + /// + /// Evaluate. + /// + /// events per stream + /// true for new data + /// prefiltered events + /// expression evaluation context + /// aggregation service or null if none + /// eval result + Object Evaluate( + EventBean[] eventsPerStream, + bool isNewData, + ICollection matchingEvents, + ExprEvaluatorContext exprEvaluatorContext, + AggregationService aggregationService); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNRBase.cs b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNRBase.cs new file mode 100755 index 000000000..33287d8ee --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNRBase.cs @@ -0,0 +1,61 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.expression.subquery +{ + public abstract class SubselectEvalStrategyNRBase : SubselectEvalStrategyNR + { + protected readonly ExprEvaluator ValueEval; + protected readonly ExprEvaluator SelectEval; + private readonly bool _resultWhenNoMatchingEvents; + + protected SubselectEvalStrategyNRBase( + ExprEvaluator valueEval, + ExprEvaluator selectEval, + bool resultWhenNoMatchingEvents) + { + ValueEval = valueEval; + SelectEval = selectEval; + _resultWhenNoMatchingEvents = resultWhenNoMatchingEvents; + } + + protected abstract Object EvaluateInternal( + Object leftResult, + EventBean[] events, + bool isNewData, + ICollection matchingEvents, + ExprEvaluatorContext exprEvaluatorContext, + AggregationService aggregationService); + + public Object Evaluate( + EventBean[] eventsPerStream, + bool isNewData, + ICollection matchingEvents, + ExprEvaluatorContext exprEvaluatorContext, + AggregationService aggregationService) + { + if (matchingEvents == null || matchingEvents.Count == 0) + { + return _resultWhenNoMatchingEvents; + } + + var leftResult = ValueEval.Evaluate(new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext)); + var events = EventBeanUtility.AllocatePerStreamShift(eventsPerStream); + return EvaluateInternal( + leftResult, events, isNewData, matchingEvents, exprEvaluatorContext, aggregationService); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNREqualsAllAnyAggregated.cs b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNREqualsAllAnyAggregated.cs new file mode 100755 index 000000000..d1355040f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNREqualsAllAnyAggregated.cs @@ -0,0 +1,83 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.subquery +{ + /// Strategy for subselects with "=/!=/>< ALL". + public class SubselectEvalStrategyNREqualsAllAnyAggregated : SubselectEvalStrategyNREqualsBase + { + private readonly ExprEvaluator _havingEval; + + public SubselectEvalStrategyNREqualsAllAnyAggregated( + ExprEvaluator valueEval, + ExprEvaluator selectEval, + bool resultWhenNoMatchingEvents, + bool notIn, + Coercer coercer, + ExprEvaluator havingEval) + : base(valueEval, selectEval, resultWhenNoMatchingEvents, notIn, coercer) + { + _havingEval = havingEval; + } + + protected override Object EvaluateInternal( + Object leftResult, + EventBean[] events, + bool isNewData, + ICollection matchingEvents, + ExprEvaluatorContext exprEvaluatorContext, + AggregationService aggregationService) + + { + var evaluateParams = new EvaluateParams(events, true, exprEvaluatorContext); + + if (_havingEval != null) + { + var pass = _havingEval.Evaluate(evaluateParams); + if ((pass == null) || (false.Equals(pass))) + { + return null; + } + } + + var rightResult = SelectEval.Evaluate(evaluateParams); + if (rightResult == null) + { + return null; + } + + if (Coercer == null) + { + var eq = leftResult.Equals(rightResult); + if ((IsNot && eq) || (!IsNot && !eq)) + { + return false; + } + } + else + { + var left = Coercer.Invoke(leftResult); + var right = Coercer.Invoke(rightResult); + bool eq = left.Equals(right); + if ((IsNot && eq) || (!IsNot && !eq)) + { + return false; + } + } + return true; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNREqualsAllDefault.cs b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNREqualsAllDefault.cs new file mode 100755 index 000000000..851923d18 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNREqualsAllDefault.cs @@ -0,0 +1,110 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.subquery +{ + /// Strategy for subselects with "=/!=/>< ALL". + public class SubselectEvalStrategyNREqualsAllDefault : SubselectEvalStrategyNREqualsBase + { + private readonly ExprEvaluator _filterEval; + + public SubselectEvalStrategyNREqualsAllDefault( + ExprEvaluator valueEval, + ExprEvaluator selectEval, + bool resultWhenNoMatchingEvents, + bool notIn, + Coercer coercer, + ExprEvaluator filterEval) + : base(valueEval, selectEval, resultWhenNoMatchingEvents, notIn, coercer) + { + + this._filterEval = filterEval; + } + + protected override Object EvaluateInternal( + Object leftResult, + EventBean[] events, + bool isNewData, + ICollection matchingEvents, + ExprEvaluatorContext exprEvaluatorContext, + AggregationService aggregationService) + { + var hasNullRow = false; + var evaluateParams = new EvaluateParams(events, true, exprEvaluatorContext); + + foreach (EventBean theEvent in matchingEvents) + { + events[0] = theEvent; + + // Eval filter expression + if (_filterEval != null) + { + var pass = _filterEval.Evaluate(evaluateParams); + if ((pass == null) || (false.Equals(pass))) + { + continue; + } + } + if (leftResult == null) + { + return null; + } + + Object rightResult; + if (SelectEval != null) + { + rightResult = SelectEval.Evaluate(evaluateParams); + } + else + { + rightResult = events[0].Underlying; + } + + if (rightResult != null) + { + if (Coercer == null) + { + bool eq = leftResult.Equals(rightResult); + if ((IsNot && eq) || (!IsNot && !eq)) + { + return false; + } + } + else + { + var left = Coercer.Invoke(leftResult); + var right = Coercer.Invoke(rightResult); + var eq = left.Equals(right); + if ((IsNot && eq) || (!IsNot && !eq)) + { + return false; + } + } + } + else + { + hasNullRow = true; + } + } + + if (hasNullRow) + { + return null; + } + return true; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNREqualsAllWGroupBy.cs b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNREqualsAllWGroupBy.cs new file mode 100755 index 000000000..f00e6b969 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNREqualsAllWGroupBy.cs @@ -0,0 +1,114 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.subquery +{ + /// Strategy for subselects with "=/!=/>< ALL". + public class SubselectEvalStrategyNREqualsAllWGroupBy : SubselectEvalStrategyNREqualsBase + { + private readonly ExprEvaluator _havingEval; + + public SubselectEvalStrategyNREqualsAllWGroupBy( + ExprEvaluator valueEval, + ExprEvaluator selectEval, + bool resultWhenNoMatchingEvents, + bool notIn, + Coercer coercer, + ExprEvaluator havingEval) + : base(valueEval, selectEval, resultWhenNoMatchingEvents, notIn, coercer) + { + + _havingEval = havingEval; + } + + protected override Object EvaluateInternal( + Object leftResult, + EventBean[] events, + bool isNewData, + ICollection matchingEvents, + ExprEvaluatorContext exprEvaluatorContext, + AggregationService aggregationServiceAnyPartition) + { + AggregationService aggregationService = + aggregationServiceAnyPartition.GetContextPartitionAggregationService( + exprEvaluatorContext.AgentInstanceId); + ICollection groupKeys = aggregationService.GetGroupKeys(exprEvaluatorContext); + bool hasNullRow = false; + + var evaluateParams = new EvaluateParams(events, true, exprEvaluatorContext); + + foreach (Object groupKey in groupKeys) + { + if (leftResult == null) + { + return null; + } + aggregationService.SetCurrentAccess(groupKey, exprEvaluatorContext.AgentInstanceId, null); + + if (_havingEval != null) + { + var pass = _havingEval.Evaluate(evaluateParams); + if ((pass == null) || (false.Equals(pass))) + { + continue; + } + } + + Object rightResult; + if (SelectEval != null) + { + rightResult = SelectEval.Evaluate(evaluateParams); + } + else + { + rightResult = events[0].Underlying; + } + + if (rightResult != null) + { + if (Coercer == null) + { + bool eq = leftResult.Equals(rightResult); + if ((IsNot && eq) || (!IsNot && !eq)) + { + return false; + } + } + else + { + var left = Coercer.Invoke(leftResult); + var right = Coercer.Invoke(rightResult); + bool eq = left.Equals(right); + if ((IsNot && eq) || (!IsNot && !eq)) + { + return false; + } + } + } + else + { + hasNullRow = true; + } + } + + if (hasNullRow) + { + return null; + } + return true; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNREqualsAnyDefault.cs b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNREqualsAnyDefault.cs new file mode 100755 index 000000000..37e47c718 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNREqualsAnyDefault.cs @@ -0,0 +1,91 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.subquery +{ + /// Strategy for subselects with "=/!=/>< ANY". + public class SubselectEvalStrategyNREqualsAnyDefault : SubselectEvalStrategyNREqualsBase + { + private readonly ExprEvaluator _filterEval; + + public SubselectEvalStrategyNREqualsAnyDefault( + ExprEvaluator valueEval, + ExprEvaluator selectEval, + bool resultWhenNoMatchingEvents, + bool notIn, + Coercer coercer, + ExprEvaluator filterEval) + : base(valueEval, selectEval, resultWhenNoMatchingEvents, notIn, coercer) + { + _filterEval = filterEval; + } + + protected override Object EvaluateInternal(Object leftResult, EventBean[] events, bool isNewData, ICollection matchingEvents, ExprEvaluatorContext exprEvaluatorContext, AggregationService aggregationService) + { + bool hasNullRow = false; + + var evaluateParams = new EvaluateParams(events, true, exprEvaluatorContext); + foreach (EventBean theEvent in matchingEvents) + { + events[0] = theEvent; + + Object rightResult; + if (SelectEval != null) + { + rightResult = SelectEval.Evaluate(evaluateParams); + } + else { + rightResult = events[0].Underlying; + } + + // Eval filter expression + if (_filterEval != null) { + var pass = _filterEval.Evaluate(evaluateParams); + if ((pass == null) || (false.Equals(pass))) { + continue; + } + } + if (leftResult == null) { + return null; + } + + if (rightResult != null) { + if (Coercer == null) { + bool eq = leftResult.Equals(rightResult); + if ((IsNot && !eq) || (!IsNot && eq)) { + return true; + } + } else { + var left = Coercer.Invoke(leftResult); + var right = Coercer.Invoke(rightResult); + bool eq = left.Equals(right); + if ((IsNot && !eq) || (!IsNot && eq)) { + return true; + } + } + } else { + hasNullRow = true; + } + } + + if (hasNullRow) { + return null; + } + + return false; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNREqualsAnyWGroupBy.cs b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNREqualsAnyWGroupBy.cs new file mode 100755 index 000000000..2ab575d9b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNREqualsAnyWGroupBy.cs @@ -0,0 +1,112 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.subquery +{ + /// Strategy for subselects with "=/!=/>< ANY". + public class SubselectEvalStrategyNREqualsAnyWGroupBy : SubselectEvalStrategyNREqualsBase + { + private readonly ExprEvaluator _havingEval; + + public SubselectEvalStrategyNREqualsAnyWGroupBy( + ExprEvaluator valueEval, + ExprEvaluator selectEval, + bool resultWhenNoMatchingEvents, + bool notIn, + Coercer coercer, + ExprEvaluator havingEval) + : base(valueEval, selectEval, resultWhenNoMatchingEvents, notIn, coercer) + { + _havingEval = havingEval; + } + + protected override Object EvaluateInternal( + Object leftResult, + EventBean[] events, + bool isNewData, + ICollection matchingEvents, + ExprEvaluatorContext exprEvaluatorContext, + AggregationService aggregationServiceAnyPartition) + { + var aggregationService = + aggregationServiceAnyPartition.GetContextPartitionAggregationService( + exprEvaluatorContext.AgentInstanceId); + var groupKeys = aggregationService.GetGroupKeys(exprEvaluatorContext); + var hasNullRow = false; + + var evaluateParams = new EvaluateParams(events, true, exprEvaluatorContext); + foreach (var groupKey in groupKeys) + { + if (leftResult == null) + { + return null; + } + aggregationService.SetCurrentAccess(groupKey, exprEvaluatorContext.AgentInstanceId, null); + + if (_havingEval != null) + { + var pass = _havingEval.Evaluate(evaluateParams); + if ((pass == null) || (false.Equals(pass))) + { + continue; + } + } + + Object rightResult; + if (SelectEval != null) + { + rightResult = SelectEval.Evaluate(evaluateParams); + } + else + { + rightResult = events[0].Underlying; + } + + if (rightResult != null) + { + if (Coercer == null) + { + var eq = leftResult.Equals(rightResult); + if ((IsNot && !eq) || (!IsNot && eq)) + { + return true; + } + } + else + { + var left = Coercer.Invoke(leftResult); + var right = Coercer.Invoke(rightResult); + var eq = left.Equals(right); + if ((IsNot && !eq) || (!IsNot && eq)) + { + return true; + } + } + } + else + { + hasNullRow = true; + } + } + + if (hasNullRow) + { + return null; + } + return false; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNREqualsBase.cs b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNREqualsBase.cs new file mode 100755 index 000000000..b619ba8fe --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNREqualsBase.cs @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.subquery +{ + /// Strategy for subselects with "=/!=/>< ALL". + public abstract class SubselectEvalStrategyNREqualsBase : SubselectEvalStrategyNRBase + { + protected readonly bool IsNot; + protected readonly Coercer Coercer; + + protected SubselectEvalStrategyNREqualsBase(ExprEvaluator valueEval, ExprEvaluator selectEval, bool resultWhenNoMatchingEvents, bool notIn, Coercer coercer) + : base(valueEval, selectEval, resultWhenNoMatchingEvents) + { + IsNot = notIn; + Coercer = coercer; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNREqualsInAggregated.cs b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNREqualsInAggregated.cs new file mode 100755 index 000000000..2e23a6eb5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNREqualsInAggregated.cs @@ -0,0 +1,64 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.subquery +{ + /// Represents a in-subselect evaluation strategy. + public class SubselectEvalStrategyNREqualsInAggregated : SubselectEvalStrategyNREqualsInBase + { + private readonly ExprEvaluator _havingEval; + + public SubselectEvalStrategyNREqualsInAggregated(ExprEvaluator valueEval, ExprEvaluator selectEval, bool notIn, Coercer coercer, ExprEvaluator havingEval) + : base(valueEval, selectEval, notIn, coercer) + { + this._havingEval = havingEval; + } + + protected override Object EvaluateInternal(Object leftResult, EventBean[] events, bool isNewData, ICollection matchingEvents, ExprEvaluatorContext exprEvaluatorContext, AggregationService aggregationService) + { + if (leftResult == null) { + return null; + } + + var evaluateParams = new EvaluateParams(events, true, exprEvaluatorContext); + + if (_havingEval != null) { + var pass = (bool?)_havingEval.Evaluate(evaluateParams); + if ((pass == null) || (false.Equals(pass))) { + return null; + } + } + + Object rightResult = SelectEval.Evaluate(evaluateParams); + if (rightResult == null) { + return null; + } + + if (Coercer == null) { + if (leftResult.Equals(rightResult)) { + return !IsNotIn; + } + } else { + var left = Coercer.Invoke(leftResult); + var right = Coercer.Invoke(rightResult); + if (left.Equals(right)) { + return !IsNotIn; + } + } + return IsNotIn; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNREqualsInBase.cs b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNREqualsInBase.cs new file mode 100755 index 000000000..0ff6584e8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNREqualsInBase.cs @@ -0,0 +1,31 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.subquery +{ + /// Represents a in-subselect evaluation strategy. + public abstract class SubselectEvalStrategyNREqualsInBase : SubselectEvalStrategyNRBase + { + protected readonly Coercer Coercer; + protected readonly bool IsNotIn; + + protected SubselectEvalStrategyNREqualsInBase( + ExprEvaluator valueEval, + ExprEvaluator selectEval, + bool notIn, + Coercer coercer) + : base(valueEval, selectEval, notIn) + { + IsNotIn = notIn; + Coercer = coercer; + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNREqualsInFiltered.cs b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNREqualsInFiltered.cs new file mode 100755 index 000000000..2ade0c94b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNREqualsInFiltered.cs @@ -0,0 +1,100 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.subquery +{ + /// Represents a in-subselect evaluation strategy. + public class SubselectEvalStrategyNREqualsInFiltered : SubselectEvalStrategyNREqualsInBase + { + private readonly ExprEvaluator _filterEval; + + public SubselectEvalStrategyNREqualsInFiltered(ExprEvaluator valueEval, ExprEvaluator selectEval, bool notIn, Coercer coercer, ExprEvaluator filterEval) + : base(valueEval, selectEval, notIn, coercer) + { + _filterEval = filterEval; + } + + protected override Object EvaluateInternal( + Object leftResult, + EventBean[] eventsZeroOffset, + bool isNewData, + ICollection matchingEvents, + ExprEvaluatorContext exprEvaluatorContext, + AggregationService aggregationService) + { + bool hasNullRow = false; + var evaluateParams = new EvaluateParams(eventsZeroOffset, true, exprEvaluatorContext); + + foreach (EventBean subselectEvent in matchingEvents) + { + // Prepare filter expression event list + eventsZeroOffset[0] = subselectEvent; + + // Eval filter expression + var pass = _filterEval.Evaluate(evaluateParams); + if ((pass == null) || (false.Equals(pass))) + { + continue; + } + if (leftResult == null) + { + return null; + } + + Object rightResult; + if (SelectEval != null) + { + rightResult = SelectEval.Evaluate(evaluateParams); + } + else + { + rightResult = eventsZeroOffset[0].Underlying; + } + + if (rightResult == null) + { + hasNullRow = true; + } + else + { + if (Coercer == null) + { + if (leftResult.Equals(rightResult)) + { + return !IsNotIn; + } + } + else + { + var left = Coercer.Invoke(leftResult); + var right = Coercer.Invoke(rightResult); + if (left.Equals(right)) + { + return !IsNotIn; + } + } + } + } + + if (hasNullRow) + { + return null; + } + + return IsNotIn; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNREqualsInUnfiltered.cs b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNREqualsInUnfiltered.cs new file mode 100755 index 000000000..22bfc9ffd --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNREqualsInUnfiltered.cs @@ -0,0 +1,94 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.subquery +{ + /// Represents a in-subselect evaluation strategy. + public class SubselectEvalStrategyNREqualsInUnfiltered : SubselectEvalStrategyNREqualsInBase + { + public SubselectEvalStrategyNREqualsInUnfiltered( + ExprEvaluator valueEval, + ExprEvaluator selectEval, + bool notIn, + Coercer coercer) + : base(valueEval, selectEval, notIn, coercer) + { + } + + protected override Object EvaluateInternal( + Object leftResult, + EventBean[] events, + bool isNewData, + ICollection matchingEvents, + ExprEvaluatorContext exprEvaluatorContext, + AggregationService aggregationService) + { + if (leftResult == null) + { + return null; + } + + var evaluateParams = new EvaluateParams(events, true, exprEvaluatorContext); + + // Evaluate each select until we have a match + bool hasNullRow = false; + foreach (EventBean theEvent in matchingEvents) + { + events[0] = theEvent; + + Object rightResult; + if (SelectEval != null) + { + rightResult = SelectEval.Evaluate(evaluateParams); + } + else + { + rightResult = events[0].Underlying; + } + + if (rightResult != null) + { + if (Coercer == null) + { + if (leftResult.Equals(rightResult)) + { + return !IsNotIn; + } + } + else + { + var left = Coercer.Invoke(leftResult); + var right = Coercer.Invoke(rightResult); + if (left.Equals(right)) + { + return !IsNotIn; + } + } + } + else + { + hasNullRow = true; + } + } + + if (hasNullRow) + { + return null; + } + return IsNotIn; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNREqualsInWGroupBy.cs b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNREqualsInWGroupBy.cs new file mode 100755 index 000000000..ae8cb61f8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNREqualsInWGroupBy.cs @@ -0,0 +1,110 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.subquery +{ + public class SubselectEvalStrategyNREqualsInWGroupBy : SubselectEvalStrategyNREqualsInBase + { + private readonly ExprEvaluator _havingEval; + + public SubselectEvalStrategyNREqualsInWGroupBy( + ExprEvaluator valueEval, + ExprEvaluator selectEval, + bool notIn, + Coercer coercer, + ExprEvaluator havingEval) + : base(valueEval, selectEval, notIn, coercer) + { + _havingEval = havingEval; + } + + protected override Object EvaluateInternal( + Object leftResult, + EventBean[] events, + bool isNewData, + ICollection matchingEvents, + ExprEvaluatorContext exprEvaluatorContext, + AggregationService aggregationServiceAnyPartition) + { + if (leftResult == null) + { + return null; + } + + AggregationService aggregationService = + aggregationServiceAnyPartition.GetContextPartitionAggregationService( + exprEvaluatorContext.AgentInstanceId); + ICollection groupKeys = aggregationService.GetGroupKeys(exprEvaluatorContext); + var evaluateParams = new EvaluateParams(events, true, exprEvaluatorContext); + + // Evaluate each select until we have a match + bool hasNullRow = false; + foreach (Object groupKey in groupKeys) + { + aggregationService.SetCurrentAccess(groupKey, exprEvaluatorContext.AgentInstanceId, null); + + if (_havingEval != null) + { + var pass = (bool?) _havingEval.Evaluate(evaluateParams); + if ((pass == null) || (false.Equals(pass))) + { + continue; + } + } + + Object rightResult; + if (SelectEval != null) + { + rightResult = SelectEval.Evaluate(evaluateParams); + } + else + { + rightResult = events[0].Underlying; + } + + if (rightResult != null) + { + if (Coercer == null) + { + if (leftResult.Equals(rightResult)) + { + return !IsNotIn; + } + } + else + { + var left = Coercer.Invoke(leftResult); + var right = Coercer.Invoke(rightResult); + if (left.Equals(right)) + { + return !IsNotIn; + } + } + } + else + { + hasNullRow = true; + } + } + + if (hasNullRow) + { + return null; + } + return IsNotIn; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNRExistsAggregated.cs b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNRExistsAggregated.cs new file mode 100755 index 000000000..07a57612c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNRExistsAggregated.cs @@ -0,0 +1,35 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.expression.subquery +{ + public class SubselectEvalStrategyNRExistsAggregated : SubselectEvalStrategyNR + { + private readonly ExprEvaluator _havingEval; + + public SubselectEvalStrategyNRExistsAggregated(ExprEvaluator havingEval) + { + _havingEval = havingEval; + } + + public Object Evaluate(EventBean[] eventsPerStream, bool isNewData, ICollection matchingEvents, ExprEvaluatorContext exprEvaluatorContext, AggregationService aggregationService) + { + var events = EventBeanUtility.AllocatePerStreamShift(eventsPerStream); + var pass = _havingEval.Evaluate(new EvaluateParams(events, true, exprEvaluatorContext)); + return pass != null && true.Equals(pass); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNRExistsAlwaysTrue.cs b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNRExistsAlwaysTrue.cs new file mode 100755 index 000000000..2a208bc2e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNRExistsAlwaysTrue.cs @@ -0,0 +1,36 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.subquery +{ + public class SubselectEvalStrategyNRExistsAlwaysTrue : SubselectEvalStrategyNR + { + public static readonly SubselectEvalStrategyNRExistsAlwaysTrue INSTANCE = new SubselectEvalStrategyNRExistsAlwaysTrue(); + + private SubselectEvalStrategyNRExistsAlwaysTrue() + { + } + + public Object Evaluate( + EventBean[] eventsPerStream, + bool isNewData, + ICollection matchingEvents, + ExprEvaluatorContext exprEvaluatorContext, + AggregationService aggregationService) + { + return true; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNRExistsDefault.cs b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNRExistsDefault.cs new file mode 100755 index 000000000..416420a6a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNRExistsDefault.cs @@ -0,0 +1,76 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.expression.subquery +{ + public class SubselectEvalStrategyNRExistsDefault : SubselectEvalStrategyNR + { + private readonly ExprEvaluator _filterEval; + private readonly ExprEvaluator _havingEval; + + public SubselectEvalStrategyNRExistsDefault(ExprEvaluator filterEval, ExprEvaluator havingEval) + { + _filterEval = filterEval; + _havingEval = havingEval; + } + + public Object Evaluate( + EventBean[] eventsPerStream, + bool isNewData, + ICollection matchingEvents, + ExprEvaluatorContext exprEvaluatorContext, + AggregationService aggregationService) + { + if (matchingEvents == null || matchingEvents.Count == 0) + { + return false; + } + if (_filterEval == null && _havingEval == null) + { + return true; + } + + EventBean[] events = EventBeanUtility.AllocatePerStreamShift(eventsPerStream); + var evaluateParams = new EvaluateParams(events, true, exprEvaluatorContext); + + if (_havingEval != null) + { + var pass = _havingEval.Evaluate(evaluateParams); + return (pass != null) && true.Equals(pass); + } + else if (_filterEval != null) + { + foreach (EventBean subselectEvent in matchingEvents) + { + // Prepare filter expression event list + events[0] = subselectEvent; + + var pass = _filterEval.Evaluate(evaluateParams); + if ((pass != null) && true.Equals(pass)) + { + return true; + } + } + return false; + } + else + { + throw new IllegalStateException("Both filter and having clause encountered"); + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNRExistsWGroupBy.cs b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNRExistsWGroupBy.cs new file mode 100755 index 000000000..17e215128 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNRExistsWGroupBy.cs @@ -0,0 +1,42 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.subquery +{ + public class SubselectEvalStrategyNRExistsWGroupBy : SubselectEvalStrategyNR + { + public static readonly SubselectEvalStrategyNRExistsWGroupBy INSTANCE = + new SubselectEvalStrategyNRExistsWGroupBy(); + + private SubselectEvalStrategyNRExistsWGroupBy() + { + } + + public Object Evaluate( + EventBean[] eventsPerStream, + bool isNewData, + ICollection matchingEvents, + ExprEvaluatorContext exprEvaluatorContext, + AggregationService aggregationService) + { + if (matchingEvents == null || matchingEvents.Count == 0) + { + return false; + } + return !aggregationService.GetGroupKeys(exprEvaluatorContext).IsEmpty(); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNRExistsWGroupByWHaving.cs b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNRExistsWGroupByWHaving.cs new file mode 100755 index 000000000..dbf4ad157 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNRExistsWGroupByWHaving.cs @@ -0,0 +1,60 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.expression.subquery +{ + public class SubselectEvalStrategyNRExistsWGroupByWHaving : SubselectEvalStrategyNR + { + private readonly ExprEvaluator _havingEval; + + public SubselectEvalStrategyNRExistsWGroupByWHaving(ExprEvaluator havingEval) + { + _havingEval = havingEval; + } + + public Object Evaluate( + EventBean[] eventsPerStream, + bool isNewData, + ICollection matchingEvents, + ExprEvaluatorContext exprEvaluatorContext, + AggregationService aggregationServiceAnyPartition) + { + if (matchingEvents == null || matchingEvents.Count == 0) + { + return false; + } + var aggregationService = + aggregationServiceAnyPartition.GetContextPartitionAggregationService( + exprEvaluatorContext.AgentInstanceId); + var groupKeys = aggregationService.GetGroupKeys(exprEvaluatorContext); + var events = EventBeanUtility.AllocatePerStreamShift(eventsPerStream); + var evaluateParams = new EvaluateParams(events, true, exprEvaluatorContext); + + foreach (var groupKey in groupKeys) + { + aggregationService.SetCurrentAccess(groupKey, exprEvaluatorContext.AgentInstanceId, null); + + var pass = _havingEval.Evaluate(evaluateParams); + if ((pass == null) || (false.Equals(pass))) + { + continue; + } + return true; + } + return false; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNRFactory.cs b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNRFactory.cs new file mode 100755 index 000000000..1d16860e2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNRFactory.cs @@ -0,0 +1,171 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.compat; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; +using com.espertech.esper.type; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.subquery +{ + using RelationalComputer = Func; + + /// Factory for subselect evaluation strategies. + public class SubselectEvalStrategyNRFactory + { + public static SubselectEvalStrategyNR CreateStrategyExists(ExprSubselectExistsNode subselectExpression) { + bool aggregated = Aggregated(subselectExpression.SubselectAggregationType); + bool grouped = Grouped(subselectExpression.StatementSpecCompiled.GroupByExpressions); + if (grouped) { + if (subselectExpression.HavingExpr != null) { + return new SubselectEvalStrategyNRExistsWGroupByWHaving(subselectExpression.HavingExpr); + } + return SubselectEvalStrategyNRExistsWGroupBy.INSTANCE; + } + if (aggregated) { + if (subselectExpression.HavingExpr != null) { + return new SubselectEvalStrategyNRExistsAggregated(subselectExpression.HavingExpr); + } + return SubselectEvalStrategyNRExistsAlwaysTrue.INSTANCE; + } + return new SubselectEvalStrategyNRExistsDefault(subselectExpression.FilterExpr, subselectExpression.HavingExpr); + } + + public static SubselectEvalStrategyNR CreateStrategyAnyAllIn(ExprSubselectNode subselectExpression, + bool isNot, + bool isAll, + bool isAny, + RelationalOpEnum? relationalOp) + { + if (subselectExpression.ChildNodes.Count != 1) { + throw new ExprValidationException("The Subselect-IN requires 1 child expression"); + } + ExprNode valueExpr = subselectExpression.ChildNodes[0]; + + // Must be the same boxed type returned by expressions under this + Type typeOne = subselectExpression.ChildNodes[0].ExprEvaluator.ReturnType.GetBoxedType(); + + // collections, array or map not supported + if ((typeOne.IsArray) || + (typeOne.IsImplementsInterface(typeof(ICollection))) || + (typeOne.IsImplementsInterface(typeof(IDictionary)))) { + throw new ExprValidationException("Collection or array comparison is not allowed for the IN, ANY, SOME or ALL keywords"); + } + + Type typeTwo; + if (subselectExpression.SelectClause != null) { + typeTwo = subselectExpression.SelectClause[0].ExprEvaluator.ReturnType; + } else { + typeTwo = subselectExpression.RawEventType.UnderlyingType; + } + + bool aggregated = Aggregated(subselectExpression.SubselectAggregationType); + bool grouped = Grouped(subselectExpression.StatementSpecCompiled.GroupByExpressions); + ExprEvaluator selectEval = subselectExpression.SelectClause == null ? null : subselectExpression.SelectClause[0].ExprEvaluator; + ExprEvaluator valueEval = valueExpr.ExprEvaluator; + ExprEvaluator filterEval = subselectExpression.FilterExpr; + ExprEvaluator havingEval = subselectExpression.HavingExpr; + + if (relationalOp != null) { + if ((typeOne != typeof(string)) || (typeTwo != typeof(string))) { + if (!typeOne.IsNumeric()) { + throw new ExprValidationException("Implicit conversion from datatype '" + Name.Of(typeOne) + "' to numeric is not allowed"); + } + if (!typeTwo.IsNumeric()) { + throw new ExprValidationException("Implicit conversion from datatype '" + Name.Of(typeTwo) + "' to numeric is not allowed"); + } + } + + Type compareType = typeOne.GetCompareToCoercionType(typeTwo); + RelationalComputer computer = relationalOp.Value.GetComputer(compareType, typeOne, typeTwo); + + if (isAny) { + if (grouped) { + return new SubselectEvalStrategyNRRelOpAnyWGroupBy(valueEval, selectEval, false, computer, havingEval); + } + if (aggregated) { + return new SubselectEvalStrategyNRRelOpAllAnyAggregated(valueEval, selectEval, false, computer, havingEval); + } + return new SubselectEvalStrategyNRRelOpAnyDefault(valueEval, selectEval, false, computer, filterEval); + } + + // handle ALL + if (grouped) { + return new SubselectEvalStrategyNRRelOpAllWGroupBy(valueEval, selectEval, true, computer, havingEval); + } + if (aggregated) { + return new SubselectEvalStrategyNRRelOpAllAnyAggregated(valueEval, selectEval, true, computer, havingEval); + } + return new SubselectEvalStrategyNRRelOpAllDefault(valueEval, selectEval, true, computer, filterEval); + } + + Coercer coercer = GetCoercer(typeOne, typeTwo); + if (isAll) { + if (grouped) { + return new SubselectEvalStrategyNREqualsAllWGroupBy(valueEval, selectEval, true, isNot, coercer, havingEval); + } + if (aggregated) { + return new SubselectEvalStrategyNREqualsAllAnyAggregated(valueEval, selectEval, true, isNot, coercer, havingEval); + } + return new SubselectEvalStrategyNREqualsAllDefault(valueEval, selectEval, true, isNot, coercer, filterEval); + } else if (isAny) { + if (grouped) { + return new SubselectEvalStrategyNREqualsAnyWGroupBy(valueEval, selectEval, false, isNot, coercer, havingEval); + } + if (aggregated) { + return new SubselectEvalStrategyNREqualsAllAnyAggregated(valueEval, selectEval, true, isNot, coercer, havingEval); + } + return new SubselectEvalStrategyNREqualsAnyDefault(valueEval, selectEval, false, isNot, coercer, filterEval); + } else { + if (grouped) { + return new SubselectEvalStrategyNREqualsInWGroupBy(valueEval, selectEval, isNot, coercer, havingEval); + } + if (aggregated) { + return new SubselectEvalStrategyNREqualsInAggregated(valueEval, selectEval, isNot, coercer, havingEval); + } + if (filterEval == null) { + return new SubselectEvalStrategyNREqualsInUnfiltered(valueEval, selectEval, isNot, coercer); + } + return new SubselectEvalStrategyNREqualsInFiltered(valueEval, selectEval, isNot, coercer, filterEval); + } + } + + private static Coercer GetCoercer(Type typeOne, Type typeTwo) { + // Get the common type such as Bool, string or double? and Long + Type coercionType; + bool mustCoerce; + try { + coercionType = typeOne.GetCompareToCoercionType(typeTwo); + } catch (CoercionException ) { + throw new ExprValidationException(string.Format("Implicit conversion from datatype '{0}' to '{1}' is not allowed", Name.Of(typeTwo), Name.Of(typeOne))); + } + + // Check if we need to coerce + mustCoerce = false; + if ((coercionType != typeOne.GetBoxedType()) || + (coercionType != typeTwo.GetBoxedType())) { + if (coercionType.IsNumeric()) { + mustCoerce = true; + } + } + return !mustCoerce ? null : CoercerFactory.GetCoercer(null, coercionType); + } + + private static bool Grouped(GroupByClauseExpressions groupByExpressions) { + return groupByExpressions != null && groupByExpressions.GroupByNodes != null && groupByExpressions.GroupByNodes.Length != 0; + } + + private static bool Aggregated(ExprSubselectNode.SubqueryAggregationType subqueryAggregationType) { + return subqueryAggregationType != null && subqueryAggregationType != ExprSubselectNode.SubqueryAggregationType.NONE; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNRRelOpAllAnyAggregated.cs b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNRRelOpAllAnyAggregated.cs new file mode 100755 index 000000000..c7b2c3b90 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNRRelOpAllAnyAggregated.cs @@ -0,0 +1,63 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.type; + +namespace com.espertech.esper.epl.expression.subquery +{ + using RelationalComputer = Func; + + public class SubselectEvalStrategyNRRelOpAllAnyAggregated : SubselectEvalStrategyNRRelOpBase + { + private readonly ExprEvaluator _havingEval; + + public SubselectEvalStrategyNRRelOpAllAnyAggregated( + ExprEvaluator valueEval, + ExprEvaluator selectEval, + bool resultWhenNoMatchingEvents, + RelationalComputer computer, + ExprEvaluator havingEval) + : base(valueEval, selectEval, resultWhenNoMatchingEvents, computer) + { + + _havingEval = havingEval; + } + + protected override Object EvaluateInternal( + Object leftResult, + EventBean[] events, + bool isNewData, + ICollection matchingEvents, + ExprEvaluatorContext exprEvaluatorContext, + AggregationService aggregationService) + { + var evaluateParams = new EvaluateParams(events, true, exprEvaluatorContext); + + if (_havingEval != null) + { + var pass = _havingEval.Evaluate(evaluateParams); + if ((pass == null) || (false.Equals(pass))) + { + return null; + } + } + var valueRight = SelectEval.Evaluate(evaluateParams); + if (valueRight == null) + { + return null; + } + return Computer.Invoke(leftResult, valueRight); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNRRelOpAllDefault.cs b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNRRelOpAllDefault.cs new file mode 100755 index 000000000..2ca9d5c08 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNRRelOpAllDefault.cs @@ -0,0 +1,102 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.subquery +{ + using RelationalComputer = Func; + + public class SubselectEvalStrategyNRRelOpAllDefault : SubselectEvalStrategyNRRelOpBase + { + private readonly ExprEvaluator _filterOrHavingEval; + + public SubselectEvalStrategyNRRelOpAllDefault( + ExprEvaluator valueEval, + ExprEvaluator selectEval, + bool resultWhenNoMatchingEvents, + RelationalComputer computer, + ExprEvaluator filterOrHavingEval) + : base(valueEval, selectEval, resultWhenNoMatchingEvents, computer) + { + _filterOrHavingEval = filterOrHavingEval; + } + + protected override Object EvaluateInternal( + Object leftResult, + EventBean[] events, + bool isNewData, + ICollection matchingEvents, + ExprEvaluatorContext exprEvaluatorContext, + AggregationService aggregationService) + { + bool hasRows = false; + bool hasNullRow = false; + + var evaluateParams = new EvaluateParams(events, true, exprEvaluatorContext); + foreach (EventBean subselectEvent in matchingEvents) + { + events[0] = subselectEvent; + + if (_filterOrHavingEval != null) + { + var pass = _filterOrHavingEval.Evaluate(evaluateParams); + if ((pass == null) || (false.Equals(pass))) + { + continue; + } + } + hasRows = true; + + Object valueRight; + if (SelectEval != null) + { + valueRight = SelectEval.Evaluate(evaluateParams); + } + else + { + valueRight = events[0].Underlying; + } + + if (valueRight == null) + { + hasNullRow = true; + } + else + { + if (leftResult != null) + { + if (!Computer.Invoke(leftResult, valueRight)) + { + return false; + } + } + } + } + + if (!hasRows) + { + return true; + } + if (leftResult == null) + { + return null; + } + if (hasNullRow) + { + return null; + } + return true; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNRRelOpAllWGroupBy.cs b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNRRelOpAllWGroupBy.cs new file mode 100755 index 000000000..67dcb88cc --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNRRelOpAllWGroupBy.cs @@ -0,0 +1,106 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.subquery +{ + using RelationalComputer = Func; + + public class SubselectEvalStrategyNRRelOpAllWGroupBy : SubselectEvalStrategyNRRelOpBase + { + private readonly ExprEvaluator _havingEval; + + public SubselectEvalStrategyNRRelOpAllWGroupBy( + ExprEvaluator valueEval, + ExprEvaluator selectEval, + bool resultWhenNoMatchingEvents, + RelationalComputer computer, + ExprEvaluator havingEval) + : base(valueEval, selectEval, resultWhenNoMatchingEvents, computer) + { + _havingEval = havingEval; + } + + protected override Object EvaluateInternal( + Object leftResult, + EventBean[] events, + bool isNewData, + ICollection matchingEvents, + ExprEvaluatorContext exprEvaluatorContext, + AggregationService aggregationServiceAnyPartition) + { + AggregationService aggregationService = + aggregationServiceAnyPartition.GetContextPartitionAggregationService( + exprEvaluatorContext.AgentInstanceId); + ICollection groupKeys = aggregationService.GetGroupKeys(exprEvaluatorContext); + bool hasRows = false; + bool hasNullRow = false; + + var evaluateParams = new EvaluateParams(events, true, exprEvaluatorContext); + + foreach (Object groupKey in groupKeys) + { + aggregationService.SetCurrentAccess(groupKey, exprEvaluatorContext.AgentInstanceId, null); + if (_havingEval != null) + { + var pass = _havingEval.Evaluate(evaluateParams); + if ((pass == null) || (false.Equals(pass))) + { + continue; + } + } + hasRows = true; + + Object valueRight; + if (SelectEval != null) + { + valueRight = SelectEval.Evaluate(evaluateParams); + } + else + { + valueRight = events[0].Underlying; + } + + if (valueRight == null) + { + hasNullRow = true; + } + else + { + if (leftResult != null) + { + if (!Computer.Invoke(leftResult, valueRight)) + { + return false; + } + } + } + } + + if (!hasRows) + { + return true; + } + if (leftResult == null) + { + return null; + } + if (hasNullRow) + { + return null; + } + return true; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNRRelOpAnyDefault.cs b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNRRelOpAnyDefault.cs new file mode 100755 index 000000000..4039b182a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNRRelOpAnyDefault.cs @@ -0,0 +1,99 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.type; + +namespace com.espertech.esper.epl.expression.subquery +{ + using RelationalComputer = Func; + + public class SubselectEvalStrategyNRRelOpAnyDefault : SubselectEvalStrategyNRRelOpBase + { + private readonly ExprEvaluator _filterEval; + + public SubselectEvalStrategyNRRelOpAnyDefault( + ExprEvaluator valueEval, + ExprEvaluator selectEval, + bool resultWhenNoMatchingEvents, + RelationalComputer computer, + ExprEvaluator filterEval) + : base(valueEval, selectEval, resultWhenNoMatchingEvents, computer) + { + + _filterEval = filterEval; + } + + protected override Object EvaluateInternal( + Object leftResult, + EventBean[] events, + bool isNewData, + ICollection matchingEvents, + ExprEvaluatorContext exprEvaluatorContext, + AggregationService aggregationService) + { + bool hasNonNullRow = false; + bool hasRows = false; + var evaluateParams = new EvaluateParams(events, true, exprEvaluatorContext); + + foreach (EventBean subselectEvent in matchingEvents) + { + events[0] = subselectEvent; + + // Eval filter expression + if (_filterEval != null) + { + var pass = _filterEval.Evaluate(evaluateParams); + if ((pass == null) || (false.Equals(pass))) + { + continue; + } + } + hasRows = true; + + Object valueRight; + if (SelectEval != null) + { + valueRight = SelectEval.Evaluate(evaluateParams); + } + else + { + valueRight = events[0].Underlying; + } + + if (valueRight != null) + { + hasNonNullRow = true; + } + + if ((leftResult != null) && (valueRight != null)) + { + if (Computer.Invoke(leftResult, valueRight)) + { + return true; + } + } + } + + if (!hasRows) + { + return false; + } + if ((!hasNonNullRow) || (leftResult == null)) + { + return null; + } + return false; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNRRelOpAnyWGroupBy.cs b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNRRelOpAnyWGroupBy.cs new file mode 100755 index 000000000..b0809264b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNRRelOpAnyWGroupBy.cs @@ -0,0 +1,103 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.type; + +namespace com.espertech.esper.epl.expression.subquery +{ + using RelationalComputer = Func; + + public class SubselectEvalStrategyNRRelOpAnyWGroupBy : SubselectEvalStrategyNRRelOpBase + { + private readonly ExprEvaluator _havingEval; + + public SubselectEvalStrategyNRRelOpAnyWGroupBy( + ExprEvaluator valueEval, + ExprEvaluator selectEval, + bool resultWhenNoMatchingEvents, + RelationalComputer computer, + ExprEvaluator havingEval) + : base(valueEval, selectEval, resultWhenNoMatchingEvents, computer) + { + _havingEval = havingEval; + } + + protected override Object EvaluateInternal( + Object leftResult, + EventBean[] events, + bool isNewData, + ICollection matchingEvents, + ExprEvaluatorContext exprEvaluatorContext, + AggregationService aggregationServiceAnyPartition) + { + var aggregationService = + aggregationServiceAnyPartition.GetContextPartitionAggregationService( + exprEvaluatorContext.AgentInstanceId); + var groupKeys = aggregationService.GetGroupKeys(exprEvaluatorContext); + var hasNonNullRow = false; + var hasRows = false; + + var evaluateParams = new EvaluateParams(events, true, exprEvaluatorContext); + + foreach (var groupKey in groupKeys) + { + aggregationService.SetCurrentAccess(groupKey, exprEvaluatorContext.AgentInstanceId, null); + + if (_havingEval != null) + { + var pass = _havingEval.Evaluate(evaluateParams); + if ((pass == null) || (false.Equals(pass))) + { + continue; + } + } + + hasRows = true; + + Object valueRight; + if (SelectEval != null) + { + valueRight = SelectEval.Evaluate(evaluateParams); + } + else + { + valueRight = events[0].Underlying; + } + + if (valueRight != null) + { + hasNonNullRow = true; + } + + if ((leftResult != null) && (valueRight != null)) + { + if (Computer.Invoke(leftResult, valueRight)) + { + return true; + } + } + } + + if (!hasRows) + { + return false; + } + if ((!hasNonNullRow) || (leftResult == null)) + { + return null; + } + return false; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNRRelOpBase.cs b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNRRelOpBase.cs new file mode 100755 index 000000000..6fd5b3d78 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyNRRelOpBase.cs @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.subquery +{ + using RelationalComputer = Func; + + public abstract class SubselectEvalStrategyNRRelOpBase : SubselectEvalStrategyNRBase + { + protected readonly RelationalComputer Computer; + + protected SubselectEvalStrategyNRRelOpBase(ExprEvaluator valueEval, ExprEvaluator selectEval, bool resultWhenNoMatchingEvents, RelationalComputer computer) + : base(valueEval, selectEval, resultWhenNoMatchingEvents) + { + Computer = computer; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyRow.cs b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyRow.cs new file mode 100755 index 000000000..efab715b0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyRow.cs @@ -0,0 +1,61 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.subquery +{ + public interface SubselectEvalStrategyRow + { + Object Evaluate( + EventBean[] eventsPerStream, + bool newData, + ICollection matchingEvents, + ExprEvaluatorContext exprEvaluatorContext, + ExprSubselectRowNode exprSubselectRowNode); + + ICollection EvaluateGetCollEvents( + EventBean[] eventsPerStream, + bool newData, + ICollection matchingEvents, + ExprEvaluatorContext context, + ExprSubselectRowNode parent); + + ICollection EvaluateGetCollScalar( + EventBean[] eventsPerStream, + bool newData, + ICollection matchingEvents, + ExprEvaluatorContext context, + ExprSubselectRowNode parent); + + Object[] TypableEvaluate( + EventBean[] eventsPerStream, + bool newData, + ICollection matchingEvents, + ExprEvaluatorContext exprEvaluatorContext, + ExprSubselectRowNode parent); + + Object[][] TypableEvaluateMultirow( + EventBean[] eventsPerStream, + bool newData, + ICollection matchingEvents, + ExprEvaluatorContext exprEvaluatorContext, + ExprSubselectRowNode parent); + + EventBean EvaluateGetEventBean( + EventBean[] eventsPerStream, + bool newData, + ICollection matchingEvents, + ExprEvaluatorContext exprEvaluatorContext, + ExprSubselectRowNode exprSubselectRowNode); + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyRowFilteredSelected.cs b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyRowFilteredSelected.cs new file mode 100755 index 000000000..fd9068169 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyRowFilteredSelected.cs @@ -0,0 +1,178 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.expression.subquery +{ + public class SubselectEvalStrategyRowFilteredSelected : SubselectEvalStrategyRow + { + // Filter and Select + public Object Evaluate( + EventBean[] eventsPerStream, + bool newData, + ICollection matchingEvents, + ExprEvaluatorContext exprEvaluatorContext, + ExprSubselectRowNode parent) + { + var eventsZeroBased = EventBeanUtility.AllocatePerStreamShift(eventsPerStream); + var subSelectResult = ExprSubselectRowNodeUtility.EvaluateFilterExpectSingleMatch( + eventsZeroBased, newData, matchingEvents, exprEvaluatorContext, parent); + if (subSelectResult == null) + { + return null; + } + + eventsZeroBased[0] = subSelectResult; + Object result; + if (parent.SelectClauseEvaluator.Length == 1) + { + result = parent.SelectClauseEvaluator[0].Evaluate(new EvaluateParams(eventsZeroBased, true, exprEvaluatorContext)); + } + else + { + // we are returning a Map here, not object-array, preferring the self-describing structure + result = parent.EvaluateRow(eventsZeroBased, true, exprEvaluatorContext); + } + + return result; + } + + // Filter and Select + public ICollection EvaluateGetCollEvents( + EventBean[] eventsPerStream, + bool newData, + ICollection matchingEvents, + ExprEvaluatorContext context, + ExprSubselectRowNode parent) + { + return null; + } + + // Filter and Select + public ICollection EvaluateGetCollScalar( + EventBean[] eventsPerStream, + bool isNewData, + ICollection matchingEvents, + ExprEvaluatorContext context, + ExprSubselectRowNode parent) + { + var result = new List(); + var selectClauseEvaluator = parent.SelectClauseEvaluator; + var filterExpr = parent.FilterExpr; + var events = EventBeanUtility.AllocatePerStreamShift(eventsPerStream); + var evaluateParamsA = new EvaluateParams(events, true, context); + var evaluateParamsB = new EvaluateParams(events, isNewData, context); + foreach (var subselectEvent in matchingEvents) + { + events[0] = subselectEvent; + var pass = filterExpr.Evaluate(evaluateParamsA); + if ((pass != null) && true.Equals(pass)) + { + result.Add(selectClauseEvaluator[0].Evaluate(evaluateParamsB)); + } + } + return result; + } + + // Filter and Select + public Object[] TypableEvaluate( + EventBean[] eventsPerStream, + bool isNewData, + ICollection matchingEvents, + ExprEvaluatorContext exprEvaluatorContext, + ExprSubselectRowNode parent) + { + var events = EventBeanUtility.AllocatePerStreamShift(eventsPerStream); + var subSelectResult = ExprSubselectRowNodeUtility.EvaluateFilterExpectSingleMatch( + events, isNewData, matchingEvents, exprEvaluatorContext, parent); + if (subSelectResult == null) + { + return null; + } + + events[0] = subSelectResult; + var selectClauseEvaluator = parent.SelectClauseEvaluator; + var results = new Object[selectClauseEvaluator.Length]; + var evaluateParams = new EvaluateParams(events, isNewData, exprEvaluatorContext); + for (var i = 0; i < results.Length; i++) + { + results[i] = selectClauseEvaluator[i].Evaluate(evaluateParams); + } + return results; + } + + public Object[][] TypableEvaluateMultirow( + EventBean[] eventsPerStream, + bool newData, + ICollection matchingEvents, + ExprEvaluatorContext exprEvaluatorContext, + ExprSubselectRowNode parent) + { + var rows = new Object[matchingEvents.Count][]; + var index = -1; + var events = EventBeanUtility.AllocatePerStreamShift(eventsPerStream); + var evaluateParams = new EvaluateParams(events, newData, exprEvaluatorContext); + + foreach (var matchingEvent in matchingEvents) + { + events[0] = matchingEvent; + + var pass = parent.FilterExpr.Evaluate(evaluateParams); + if ((pass != null) && true.Equals(pass)) + { + index++; + var selectClauseEvaluator = parent.SelectClauseEvaluator; + var results = new Object[selectClauseEvaluator.Length]; + for (var i = 0; i < results.Length; i++) + { + results[i] = selectClauseEvaluator[i].Evaluate(evaluateParams); + } + rows[index] = results; + } + } + if (index == rows.Length - 1) + { + return rows; + } + if (index == -1) + { + return new Object[0][]; + } + var access = index + 1; + var rowArray = new Object[access][]; + Array.Copy(rows, 0, rowArray, 0, rowArray.Length); + return rowArray; + } + + // Filter and Select + public EventBean EvaluateGetEventBean( + EventBean[] eventsPerStream, + bool isNewData, + ICollection matchingEvents, + ExprEvaluatorContext context, + ExprSubselectRowNode parent) + { + var events = EventBeanUtility.AllocatePerStreamShift(eventsPerStream); + var subSelectResult = ExprSubselectRowNodeUtility.EvaluateFilterExpectSingleMatch( + events, isNewData, matchingEvents, context, parent); + if (subSelectResult == null) + { + return null; + } + var row = parent.EvaluateRow(events, true, context); + return parent.SubselectMultirowType.EventAdapterService.AdapterForTypedMap( + row, parent.SubselectMultirowType.EventType); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyRowFilteredUnselected.cs b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyRowFilteredUnselected.cs new file mode 100755 index 000000000..d45e6056f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyRowFilteredUnselected.cs @@ -0,0 +1,112 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.expression.subquery +{ + public class SubselectEvalStrategyRowFilteredUnselected : SubselectEvalStrategyRow + { + + // Filter and no-select + public virtual Object Evaluate( + EventBean[] eventsPerStream, + bool newData, + ICollection matchingEvents, + ExprEvaluatorContext exprEvaluatorContext, + ExprSubselectRowNode parent) + { + var eventsZeroBased = EventBeanUtility.AllocatePerStreamShift(eventsPerStream); + var subSelectResult = ExprSubselectRowNodeUtility.EvaluateFilterExpectSingleMatch( + eventsZeroBased, newData, matchingEvents, exprEvaluatorContext, parent); + if (subSelectResult == null) + { + return null; + } + return subSelectResult.Underlying; + } + + // Filter and no-select + public ICollection EvaluateGetCollEvents( + EventBean[] eventsPerStream, + bool newData, + ICollection matchingEvents, + ExprEvaluatorContext context, + ExprSubselectRowNode parent) + { + var events = EventBeanUtility.AllocatePerStreamShift(eventsPerStream); + var evaluateParams = new EvaluateParams(events, true, context); + + ArrayDeque filtered = null; + foreach (var subselectEvent in matchingEvents) + { + events[0] = subselectEvent; + var pass = parent.FilterExpr.Evaluate(evaluateParams); + if ((pass != null) && true.Equals(pass)) + { + if (filtered == null) + { + filtered = new ArrayDeque(); + } + filtered.Add(subselectEvent); + } + } + return filtered; + } + + // Filter and no-select + public ICollection EvaluateGetCollScalar( + EventBean[] eventsPerStream, + bool isNewData, + ICollection matchingEvents, + ExprEvaluatorContext context, + ExprSubselectRowNode parent) + { + return null; + } + + // Filter and no-select + public Object[] TypableEvaluate( + EventBean[] eventsPerStream, + bool isNewData, + ICollection matchingEvents, + ExprEvaluatorContext exprEvaluatorContext, + ExprSubselectRowNode parent) + { + return null; + } + + // Filer and no-select + public Object[][] TypableEvaluateMultirow( + EventBean[] eventsPerStream, + bool newData, + ICollection matchingEvents, + ExprEvaluatorContext exprEvaluatorContext, + ExprSubselectRowNode parent) + { + return null; + } + + // Filter and no-select + public EventBean EvaluateGetEventBean( + EventBean[] eventsPerStream, + bool newData, + ICollection matchingEvents, + ExprEvaluatorContext context, + ExprSubselectRowNode parent) + { + return null; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyRowFilteredUnselectedTable.cs b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyRowFilteredUnselectedTable.cs new file mode 100755 index 000000000..b40c6a232 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyRowFilteredUnselectedTable.cs @@ -0,0 +1,46 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.expression.subquery +{ + public class SubselectEvalStrategyRowFilteredUnselectedTable : SubselectEvalStrategyRowFilteredUnselected + { + private readonly TableMetadata _tableMetadata; + + public SubselectEvalStrategyRowFilteredUnselectedTable(TableMetadata tableMetadata) + { + _tableMetadata = tableMetadata; + } + + public override Object Evaluate( + EventBean[] eventsPerStream, + bool newData, + ICollection matchingEvents, + ExprEvaluatorContext exprEvaluatorContext, + ExprSubselectRowNode parent) + { + EventBean[] eventsZeroBased = EventBeanUtility.AllocatePerStreamShift(eventsPerStream); + EventBean subSelectResult = ExprSubselectRowNodeUtility.EvaluateFilterExpectSingleMatch( + eventsZeroBased, newData, matchingEvents, exprEvaluatorContext, parent); + if (subSelectResult == null) + { + return null; + } + return _tableMetadata.EventToPublic.ConvertToUnd( + subSelectResult, new EvaluateParams(eventsPerStream, newData, exprEvaluatorContext)); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyRowHavingSelected.cs b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyRowHavingSelected.cs new file mode 100755 index 000000000..f86e8fc8a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyRowHavingSelected.cs @@ -0,0 +1,73 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.expression.subquery +{ + public class SubselectEvalStrategyRowHavingSelected : SubselectEvalStrategyRow + { + public Object Evaluate( + EventBean[] eventsPerStream, + bool newData, + ICollection matchingEvents, + ExprEvaluatorContext exprEvaluatorContext, + ExprSubselectRowNode parent) + { + EventBean[] eventsZeroBased = EventBeanUtility.AllocatePerStreamShift(eventsPerStream); + var pass = parent.HavingExpr.Evaluate(new EvaluateParams(eventsZeroBased, newData, exprEvaluatorContext)); + if ((pass == null) || (false.Equals(pass))) { + return null; + } + + Object result; + if (parent.SelectClauseEvaluator.Length == 1) { + result = parent.SelectClauseEvaluator[0].Evaluate(new EvaluateParams(eventsZeroBased, true, exprEvaluatorContext)); + } else { + // we are returning a Map here, not object-array, preferring the self-describing structure + result = parent.EvaluateRow(eventsZeroBased, true, exprEvaluatorContext); + } + + return result; + } + + // Filter and Select + public ICollection EvaluateGetCollEvents(EventBean[] eventsPerStream, bool newData, ICollection matchingEvents, ExprEvaluatorContext context, ExprSubselectRowNode parent) + { + return null; + } + + // Filter and Select + public ICollection EvaluateGetCollScalar(EventBean[] eventsPerStream, bool isNewData, ICollection matchingEvents, ExprEvaluatorContext context, ExprSubselectRowNode parent) + { + return null; + } + + // Filter and Select + public Object[] TypableEvaluate(EventBean[] eventsPerStream, bool isNewData, ICollection matchingEvents, ExprEvaluatorContext exprEvaluatorContext, ExprSubselectRowNode parent) + { + return null; + } + + public Object[][] TypableEvaluateMultirow(EventBean[] eventsPerStream, bool newData, ICollection matchingEvents, ExprEvaluatorContext exprEvaluatorContext, ExprSubselectRowNode parent) + { + return null; + } + + // Filter and Select + public EventBean EvaluateGetEventBean(EventBean[] eventsPerStream, bool isNewData, ICollection matchingEvents, ExprEvaluatorContext context, ExprSubselectRowNode parent) + { + return null; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyRowUnfilteredSelected.cs b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyRowUnfilteredSelected.cs new file mode 100755 index 000000000..6ea9ff7cf --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyRowUnfilteredSelected.cs @@ -0,0 +1,141 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.expression.subquery +{ + public class SubselectEvalStrategyRowUnfilteredSelected : SubselectEvalStrategyRow + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + // No filter and with select clause + public virtual Object Evaluate( + EventBean[] eventsPerStream, + bool newData, + ICollection matchingEvents, + ExprEvaluatorContext exprEvaluatorContext, + ExprSubselectRowNode parent) + { + if (matchingEvents.Count > 1) { + Log.Warn(parent.GetMultirowMessage()); + return null; + } + + var events = EventBeanUtility.AllocatePerStreamShift(eventsPerStream); + events[0] = EventBeanUtility.GetNonemptyFirstEvent(matchingEvents); + + Object result; + if (parent.SelectClauseEvaluator.Length == 1) { + result = parent.SelectClauseEvaluator[0].Evaluate(new EvaluateParams(events, true, exprEvaluatorContext)); + } else { + // we are returning a Map here, not object-array, preferring the self-describing structure + result = parent.EvaluateRow(events, true, exprEvaluatorContext); + } + return result; + } + + // No filter and with select clause + public virtual ICollection EvaluateGetCollEvents(EventBean[] eventsPerStream, bool newData, ICollection matchingEvents, ExprEvaluatorContext context, ExprSubselectRowNode parent) + { + if (matchingEvents.Count == 0) { + return Collections.GetEmptyList(); + } + + // when selecting a single property in the select clause that provides a fragment + if (parent.SubselectMultirowType == null) { + var eventsX = new ArrayDeque(matchingEvents.Count); + var eval = (ExprIdentNodeEvaluator) parent.SelectClauseEvaluator[0]; + var getter = eval.Getter; + foreach (var subselectEvent in matchingEvents) { + var fragment = getter.GetFragment(subselectEvent); + if (fragment == null) { + continue; + } + eventsX.Add((EventBean) fragment); + } + return eventsX; + } + + // when selecting a combined output row that contains multiple fields + var events = new ArrayDeque(matchingEvents.Count); + var eventsPerStreamEval = EventBeanUtility.AllocatePerStreamShift(eventsPerStream); + foreach (var subselectEvent in matchingEvents) { + eventsPerStreamEval[0] = subselectEvent; + var row = parent.EvaluateRow(eventsPerStreamEval, true, context); + var @event = parent.SubselectMultirowType.EventAdapterService.AdapterForTypedMap(row, parent.SubselectMultirowType.EventType); + events.Add(@event); + } + return events; + } + + // No filter and with select clause + public virtual ICollection EvaluateGetCollScalar(EventBean[] eventsPerStream, bool isNewData, ICollection matchingEvents, ExprEvaluatorContext context, ExprSubselectRowNode parent) + { + var result = new List(); + var events = EventBeanUtility.AllocatePerStreamShift(eventsPerStream); + var evaluateParams = new EvaluateParams(events, isNewData, context); + foreach (var subselectEvent in matchingEvents) + { + events[0] = subselectEvent; + result.Add(parent.SelectClauseEvaluator[0].Evaluate(evaluateParams)); + } + return result; + } + + // No filter and with select clause + public Object[] TypableEvaluate(EventBean[] eventsPerStream, bool isNewData, ICollection matchingEvents, ExprEvaluatorContext exprEvaluatorContext, ExprSubselectRowNode parent) + { + // take the first match only + var events = EventBeanUtility.AllocatePerStreamShift(eventsPerStream); + var evaluateParams = new EvaluateParams(events, isNewData, exprEvaluatorContext); + events[0] = EventBeanUtility.GetNonemptyFirstEvent(matchingEvents); + var results = new Object[parent.SelectClauseEvaluator.Length]; + for (var i = 0; i < results.Length; i++) { + results[i] = parent.SelectClauseEvaluator[i].Evaluate(evaluateParams); + } + return results; + } + + // No filter and with select clause + public Object[][] TypableEvaluateMultirow(EventBean[] eventsPerStream, bool isNewData, ICollection matchingEvents, ExprEvaluatorContext exprEvaluatorContext, ExprSubselectRowNode parent) + { + var rows = new Object[matchingEvents.Count][]; + var index = -1; + var events = EventBeanUtility.AllocatePerStreamShift(eventsPerStream); + var evaluateParams = new EvaluateParams(events, isNewData, exprEvaluatorContext); + foreach (var matchingEvent in matchingEvents) + { + index++; + events[0] = matchingEvent; + var results = new Object[parent.SelectClauseEvaluator.Length]; + for (var i = 0; i < results.Length; i++) { + results[i] = parent.SelectClauseEvaluator[i].Evaluate(evaluateParams); + } + rows[index] = results; + } + return rows; + } + + // No filter and with select clause + public virtual EventBean EvaluateGetEventBean(EventBean[] eventsPerStream, bool newData, ICollection matchingEvents, ExprEvaluatorContext context, ExprSubselectRowNode parent) + { + var events = EventBeanUtility.AllocatePerStreamShift(eventsPerStream); + events[0] = EventBeanUtility.GetNonemptyFirstEvent(matchingEvents); + var row = parent.EvaluateRow(events, true, context); + return parent.SubselectMultirowType.EventAdapterService.AdapterForTypedMap(row, parent.SubselectMultirowType.EventType); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyRowUnfilteredSelectedGroupedNoHaving.cs b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyRowUnfilteredSelectedGroupedNoHaving.cs new file mode 100755 index 000000000..0563ab1fa --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyRowUnfilteredSelectedGroupedNoHaving.cs @@ -0,0 +1,87 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.subquery +{ + /// Represents a subselect in an expression tree. + public class SubselectEvalStrategyRowUnfilteredSelectedGroupedNoHaving + : SubselectEvalStrategyRowUnfilteredSelected + , SubselectEvalStrategyRow + { + public override Object Evaluate( + EventBean[] eventsPerStream, + bool newData, + ICollection matchingEvents, + ExprEvaluatorContext exprEvaluatorContext, + ExprSubselectRowNode parent) + { + ICollection groupKeys = parent.SubselectAggregationService.GetGroupKeys(exprEvaluatorContext); + if (groupKeys.IsEmpty() || groupKeys.Count > 1) + { + return null; + } + parent.SubselectAggregationService.SetCurrentAccess( + groupKeys.First(), exprEvaluatorContext.AgentInstanceId, null); + + return base.Evaluate(eventsPerStream, newData, matchingEvents, exprEvaluatorContext, parent); + } + + public override ICollection EvaluateGetCollScalar( + EventBean[] eventsPerStream, + bool isNewData, + ICollection matchingEvents, + ExprEvaluatorContext context, + ExprSubselectRowNode parent) + { + return null; + } + + public override EventBean EvaluateGetEventBean( + EventBean[] eventsPerStream, + bool newData, + ICollection matchingEvents, + ExprEvaluatorContext context, + ExprSubselectRowNode parent) + { + return null; + } + + public override ICollection EvaluateGetCollEvents( + EventBean[] eventsPerStream, + bool newData, + ICollection matchingEvents, + ExprEvaluatorContext context, + ExprSubselectRowNode parent) + { + var aggregationService = parent.SubselectAggregationService.GetContextPartitionAggregationService(context.AgentInstanceId); + var groupKeys = aggregationService.GetGroupKeys(context); + if (groupKeys.IsEmpty()) + { + return null; + } + var events = new ArrayDeque(groupKeys.Count); + foreach (Object groupKey in groupKeys) + { + aggregationService.SetCurrentAccess(groupKey, context.AgentInstanceId, null); + IDictionary row = parent.EvaluateRow(null, true, context); + EventBean @event = parent.SubselectMultirowType.EventAdapterService.AdapterForTypedMap( + row, parent.SubselectMultirowType.EventType); + events.Add(@event); + } + return events; + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyRowUnfilteredSelectedGroupedWHaving.cs b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyRowUnfilteredSelectedGroupedWHaving.cs new file mode 100755 index 000000000..c88b307a9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyRowUnfilteredSelectedGroupedWHaving.cs @@ -0,0 +1,126 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.expression.subquery +{ + /// Represents a subselect in an expression tree. + public class SubselectEvalStrategyRowUnfilteredSelectedGroupedWHaving + : SubselectEvalStrategyRowUnfilteredSelected + , SubselectEvalStrategyRow + { + private readonly ExprEvaluator _havingClause; + + public SubselectEvalStrategyRowUnfilteredSelectedGroupedWHaving(ExprEvaluator havingClause) + { + _havingClause = havingClause; + } + + public override Object Evaluate( + EventBean[] eventsPerStream, + bool newData, + ICollection matchingEvents, + ExprEvaluatorContext exprEvaluatorContext, + ExprSubselectRowNode parent) + { + var aggregationService = parent.SubselectAggregationService.GetContextPartitionAggregationService( + exprEvaluatorContext.AgentInstanceId); + var groupKeys = aggregationService.GetGroupKeys(exprEvaluatorContext); + if (groupKeys.IsEmpty()) + { + return null; + } + + var events = EventBeanUtility.AllocatePerStreamShift(eventsPerStream); + bool haveResult = false; + Object result = null; + + var evaluateParams = new EvaluateParams(events, newData, exprEvaluatorContext); + foreach (object groupKey in groupKeys) + { + aggregationService.SetCurrentAccess(groupKey, exprEvaluatorContext.AgentInstanceId,null); + + var pass = _havingClause.Evaluate(evaluateParams); + if (true.Equals(pass)) + { + if (haveResult) + { + return null; + } + + result = base.Evaluate(eventsPerStream, newData, matchingEvents, exprEvaluatorContext, parent); + haveResult = true; + } + } + + return haveResult ? result : null; + } + + + public override ICollection EvaluateGetCollScalar( + EventBean[] eventsPerStream, + bool isNewData, + ICollection matchingEvents, + ExprEvaluatorContext context, + ExprSubselectRowNode parent) + { + return null; + } + + public override EventBean EvaluateGetEventBean( + EventBean[] eventsPerStream, + bool newData, + ICollection matchingEvents, + ExprEvaluatorContext context, + ExprSubselectRowNode parent) + { + return null; + } + + public override ICollection EvaluateGetCollEvents( + EventBean[] eventsPerStream, + bool newData, + ICollection matchingEvents, + ExprEvaluatorContext context, + ExprSubselectRowNode parent) + { + AggregationService aggregationService = + parent.SubselectAggregationService.GetContextPartitionAggregationService(context.AgentInstanceId); + ICollection groupKeys = aggregationService.GetGroupKeys(context); + if (groupKeys.IsEmpty()) + { + return null; + } + var events = EventBeanUtility.AllocatePerStreamShift(eventsPerStream); + var evaluateParams = new EvaluateParams(events, newData, context); + var result = new ArrayDeque(groupKeys.Count); + foreach (object groupKey in groupKeys) + { + aggregationService.SetCurrentAccess(groupKey, context.AgentInstanceId, null); + + var pass = _havingClause.Evaluate(evaluateParams); + if (true.Equals(pass)) + { + IDictionary row = parent.EvaluateRow(events, true, context); + EventBean @event = parent.SubselectMultirowType.EventAdapterService.AdapterForTypedMap( + row, parent.SubselectMultirowType.EventType); + result.Add(@event); + } + } + return result; + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyRowUnfilteredUnselected.cs b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyRowUnfilteredUnselected.cs new file mode 100755 index 000000000..d90692294 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyRowUnfilteredUnselected.cs @@ -0,0 +1,68 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.expression.subquery +{ + /// Represents a subselect in an expression tree. + public class SubselectEvalStrategyRowUnfilteredUnselected : SubselectEvalStrategyRow + { + public static readonly SubselectEvalStrategyRowUnfilteredUnselected INSTANCE = new SubselectEvalStrategyRowUnfilteredUnselected(); + + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + // No filter and no select-clause: return underlying event + public virtual Object Evaluate( + EventBean[] eventsPerStream, + bool newData, + ICollection matchingEvents, + ExprEvaluatorContext exprEvaluatorContext, + ExprSubselectRowNode parent) + { + if (matchingEvents.Count > 1) { + Log.Warn(parent.GetMultirowMessage()); + return null; + } + return EventBeanUtility.GetNonemptyFirstEventUnderlying(matchingEvents); + } + + // No filter and no select-clause: return matching events + public ICollection EvaluateGetCollEvents(EventBean[] eventsPerStream, bool newData, ICollection matchingEvents, ExprEvaluatorContext context, ExprSubselectRowNode parent) + { + return matchingEvents; + } + + // No filter and no select-clause: no value can be determined + public ICollection EvaluateGetCollScalar(EventBean[] eventsPerStream, bool isNewData, ICollection matchingEvents, ExprEvaluatorContext context, ExprSubselectRowNode parent) + { + return null; + } + + // No filter and no select-clause: no value can be determined + public Object[] TypableEvaluate(EventBean[] eventsPerStream, bool newData, ICollection matchingEvents, ExprEvaluatorContext exprEvaluatorContext, ExprSubselectRowNode parent) + { + return null; + } + + public Object[][] TypableEvaluateMultirow(EventBean[] eventsPerStream, bool newData, ICollection matchingEvents, ExprEvaluatorContext exprEvaluatorContext, ExprSubselectRowNode parent) + { + return null; + } + + public EventBean EvaluateGetEventBean(EventBean[] eventsPerStream, bool newData, ICollection matchingEvents, ExprEvaluatorContext exprEvaluatorContext, ExprSubselectRowNode parent) { + return null; // this actually only applies to when there is a select-clause + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyRowUnfilteredUnselectedTable.cs b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyRowUnfilteredUnselectedTable.cs new file mode 100755 index 000000000..a3aed7d65 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/subquery/SubselectEvalStrategyRowUnfilteredUnselectedTable.cs @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.expression.subquery +{ + public class SubselectEvalStrategyRowUnfilteredUnselectedTable : SubselectEvalStrategyRowUnfilteredUnselected + { + private static readonly ILog Log = + LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly TableMetadata _tableMetadata; + + public SubselectEvalStrategyRowUnfilteredUnselectedTable(TableMetadata tableMetadata) + { + _tableMetadata = tableMetadata; + } + + public override Object Evaluate( + EventBean[] eventsPerStream, + bool newData, + ICollection matchingEvents, + ExprEvaluatorContext exprEvaluatorContext, + ExprSubselectRowNode parent) + { + if (matchingEvents.Count > 1) + { + Log.Warn(parent.GetMultirowMessage()); + return null; + } + var @event = EventBeanUtility.GetNonemptyFirstEvent(matchingEvents); + return _tableMetadata.EventToPublic.ConvertToUnd(@event, new EvaluateParams(eventsPerStream, newData, exprEvaluatorContext)); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/table/ExprTableAccessEvalStrategy.cs b/NEsper.Core/NEsper.Core/epl/expression/table/ExprTableAccessEvalStrategy.cs new file mode 100755 index 000000000..ae6a55338 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/table/ExprTableAccessEvalStrategy.cs @@ -0,0 +1,31 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.table +{ + public interface ExprTableAccessEvalStrategy + { + object Evaluate(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext exprEvaluatorContext); + + ICollection EvaluateGetROCollectionEvents(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context); + + EventBean EvaluateGetEventBean(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context); + + ICollection EvaluateGetROCollectionScalar(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context); + + object[] EvaluateTypableSingle(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/table/ExprTableAccessNode.cs b/NEsper.Core/NEsper.Core/epl/expression/table/ExprTableAccessNode.cs new file mode 100755 index 000000000..f911a7e68 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/table/ExprTableAccessNode.cs @@ -0,0 +1,142 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.table.mgmt; + +namespace com.espertech.esper.epl.expression.table +{ + [Serializable] + public abstract class ExprTableAccessNode : ExprNodeBase + { + private readonly string _tableName; + + [NonSerialized] private ExprTableAccessEvalStrategy _strategy; + [NonSerialized] private ExprEvaluator[] _groupKeyEvaluators; + + protected abstract void ValidateBindingInternal(ExprValidationContext validationContext, TableMetadata tableMetadata); + protected abstract bool EqualsNodeInternal(ExprTableAccessNode other); + + protected ExprTableAccessNode(string tableName) + { + _tableName = tableName; + } + + public string TableName + { + get { return _tableName; } + } + + public ExprEvaluator[] GroupKeyEvaluators + { + get { return _groupKeyEvaluators; } + } + + public ExprTableAccessEvalStrategy Strategy + { + internal get { return _strategy; } + set { _strategy = value; } + } + + public override bool IsConstantResult + { + get { return false; } + } + + public override ExprNode Validate(ExprValidationContext validationContext) + { + if (validationContext.TableService == null || !validationContext.IsAllowBindingConsumption) + { + throw new ExprValidationException("Invalid use of table access expression, expression '" + _tableName + "' is not allowed here"); + } + TableMetadata metadata = validationContext.TableService.GetTableMetadata(_tableName); + if (metadata == null) { + throw new ExprValidationException("A table '" + _tableName + "' could not be found"); + } + + if (metadata.ContextName != null && + validationContext.ContextDescriptor != null && + !metadata.ContextName.Equals(validationContext.ContextDescriptor.ContextName)) { + throw new ExprValidationException("Table by name '" + _tableName + "' has been declared for context '" + metadata.ContextName + "' and can only be used within the same context"); + } + + // additional validations depend on detail + ValidateBindingInternal(validationContext, metadata); + return null; + } + + protected void ValidateGroupKeys(TableMetadata metadata) + { + if (ChildNodes.Count > 0) { + _groupKeyEvaluators = ExprNodeUtility.GetEvaluators(ChildNodes); + } + else { + _groupKeyEvaluators = new ExprEvaluator[0]; + } + var typesReturned = ExprNodeUtility.GetExprResultTypes(_groupKeyEvaluators); + ExprTableNodeUtil.ValidateExpressions(_tableName, typesReturned, "key", ChildNodes, metadata.KeyTypes, "key"); + } + + public override ExprPrecedenceEnum Precedence + { + get { return ExprPrecedenceEnum.UNARY; } + } + + protected void ToPrecedenceFreeEPLInternal(TextWriter writer, string subprop) + { + ToPrecedenceFreeEPLInternal(writer); + writer.Write("."); + writer.Write(subprop); + } + + protected void ToPrecedenceFreeEPLInternal(TextWriter writer) + { + writer.Write(_tableName); + if (ChildNodes.Count > 0) { + writer.Write("["); + var delimiter = ""; + foreach (var expr in ChildNodes) { + writer.Write(delimiter); + expr.ToEPL(writer, ExprPrecedenceEnum.MINIMUM); + delimiter = ","; + } + writer.Write("]"); + } + } + + protected TableMetadataColumn ValidateSubpropertyGetCol(TableMetadata tableMetadata, string subpropName) + { + var column = tableMetadata.TableColumns.Get(subpropName); + if (column == null) { + throw new ExprValidationException("A column '" + subpropName + "' could not be found for table '" + _tableName + "'"); + } + return column; + } + + public override bool EqualsNode(ExprNode o) + { + if (this == o) return true; + if (o == null || GetType() != o.GetType()) return false; + + var that = (ExprTableAccessNode) o; + if (!_tableName.Equals(that._tableName)) return false; + + return EqualsNodeInternal(that); + } + + public override int GetHashCode() + { + return _tableName != null ? _tableName.GetHashCode() : 0; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/table/ExprTableAccessNodeKeys.cs b/NEsper.Core/NEsper.Core/epl/expression/table/ExprTableAccessNodeKeys.cs new file mode 100755 index 000000000..5d2903a4b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/table/ExprTableAccessNodeKeys.cs @@ -0,0 +1,60 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.table.mgmt; + +namespace com.espertech.esper.epl.expression.table +{ + [Serializable] + public class ExprTableAccessNodeKeys + : ExprTableAccessNode + , ExprEvaluator + { + public ExprTableAccessNodeKeys(string tableName) + : base(tableName) + { + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + ToPrecedenceFreeEPLInternal(writer); + writer.Write(".keys()"); + } + + public override ExprEvaluator ExprEvaluator + { + get { return this; } + } + + protected override void ValidateBindingInternal(ExprValidationContext validationContext, TableMetadata tableMetadata) + { + } + + public object Evaluate(EvaluateParams evaluateParams) + { + return Strategy.Evaluate( + evaluateParams.EventsPerStream, + evaluateParams.IsNewData, + evaluateParams.ExprEvaluatorContext); + } + + public virtual Type ReturnType + { + get { return typeof (object[]); } + } + + protected override bool EqualsNodeInternal(ExprTableAccessNode other) + { + return true; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/table/ExprTableAccessNodeSubprop.cs b/NEsper.Core/NEsper.Core/epl/expression/table/ExprTableAccessNodeSubprop.cs new file mode 100755 index 000000000..104fe5478 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/table/ExprTableAccessNodeSubprop.cs @@ -0,0 +1,133 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.dot; +using com.espertech.esper.epl.rettype; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.events; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.expression.table +{ + [Serializable] + public class ExprTableAccessNodeSubprop + : ExprTableAccessNode + , ExprEvaluator + , ExprEvaluatorEnumeration + { + private readonly string _subpropName; + + private Type _bindingReturnType; + [NonSerialized] private EPType _optionalEnumerationType; + [NonSerialized] private ExprEvaluatorEnumerationGivenEvent _optionalPropertyEnumEvaluator; + + public ExprTableAccessNodeSubprop(string tableName, string subpropName) + : base(tableName) + { + _subpropName = subpropName; + } + + public override ExprEvaluator ExprEvaluator + { + get { return this; } + } + + protected override void ValidateBindingInternal(ExprValidationContext validationContext, TableMetadata tableMetadata) + { + ValidateGroupKeys(tableMetadata); + var column = ValidateSubpropertyGetCol(tableMetadata, _subpropName); + if (column is TableMetadataColumnPlain) { + _bindingReturnType = tableMetadata.InternalEventType.GetPropertyType(_subpropName); + var enumerationSource = ExprDotNodeUtility.GetPropertyEnumerationSource(_subpropName, 0, tableMetadata.InternalEventType, true, true); + _optionalEnumerationType = enumerationSource.ReturnType; + _optionalPropertyEnumEvaluator = enumerationSource.EnumerationGivenEvent; + } else { + var aggcol = (TableMetadataColumnAggregation) column; + _optionalEnumerationType = aggcol.OptionalEnumerationType; + _bindingReturnType = aggcol.Factory.ResultType; + } + } + + public Type ReturnType + { + get { return _bindingReturnType; } + } + + public Object Evaluate(EvaluateParams evaluateParams) + { + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().QExprTableSubproperty(this, base.TableName, _subpropName); + var result = base.Strategy.Evaluate(evaluateParams.EventsPerStream, evaluateParams.IsNewData, evaluateParams.ExprEvaluatorContext); + InstrumentationHelper.Get().AExprTableSubproperty(result); + return result; + } + + return Strategy.Evaluate( + evaluateParams.EventsPerStream, + evaluateParams.IsNewData, + evaluateParams.ExprEvaluatorContext + ); + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) { + ToPrecedenceFreeEPLInternal(writer, _subpropName); + } + + public string SubpropName + { + get { return _subpropName; } + } + + public EventType GetEventTypeCollection(EventAdapterService eventAdapterService, int statementId) + { + return EPTypeHelper.OptionalIsEventTypeColl(_optionalEnumerationType); + } + + public ICollection EvaluateGetROCollectionEvents(EvaluateParams evaluateParams) + { + return Strategy.EvaluateGetROCollectionEvents(evaluateParams.EventsPerStream, evaluateParams.IsNewData, evaluateParams.ExprEvaluatorContext); + } + + public Type ComponentTypeCollection + { + get { return EPTypeHelper.OptionalIsComponentTypeColl(_optionalEnumerationType); } + } + + public ICollection EvaluateGetROCollectionScalar(EvaluateParams evaluateParams) + { + return Strategy.EvaluateGetROCollectionScalar(evaluateParams.EventsPerStream, evaluateParams.IsNewData, evaluateParams.ExprEvaluatorContext); + } + + public EventType GetEventTypeSingle(EventAdapterService eventAdapterService, int statementId) + { + return EPTypeHelper.OptionalIsEventTypeSingle(_optionalEnumerationType); + } + + public EventBean EvaluateGetEventBean(EvaluateParams evaluateParams) + { + return Strategy.EvaluateGetEventBean(evaluateParams.EventsPerStream, evaluateParams.IsNewData, evaluateParams.ExprEvaluatorContext); + } + + public ExprEvaluatorEnumerationGivenEvent OptionalPropertyEnumEvaluator + { + get { return _optionalPropertyEnumEvaluator; } + } + + protected override bool EqualsNodeInternal(ExprTableAccessNode other) + { + var that = (ExprTableAccessNodeSubprop) other; + return _subpropName.Equals(that._subpropName); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/table/ExprTableAccessNodeSubpropAccessor.cs b/NEsper.Core/NEsper.Core/epl/expression/table/ExprTableAccessNodeSubpropAccessor.cs new file mode 100755 index 000000000..b28b53c9b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/table/ExprTableAccessNodeSubpropAccessor.cs @@ -0,0 +1,163 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.client; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.accessagg; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.events; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.expression.table +{ + [Serializable] + public class ExprTableAccessNodeSubpropAccessor + : ExprTableAccessNode + , ExprEvaluator + , ExprEvaluatorEnumeration + { + private readonly string _subpropName; + private readonly ExprNode _aggregateAccessMultiValueNode; + [NonSerialized] private AggregationMethodFactory _accessorFactory; + + public ExprTableAccessNodeSubpropAccessor( + string tableName, + string subpropName, + ExprNode aggregateAccessMultiValueNode) + : base(tableName) + { + _subpropName = subpropName; + _aggregateAccessMultiValueNode = aggregateAccessMultiValueNode; + } + + public ExprAggregateNodeBase AggregateAccessMultiValueNode + { + get { return (ExprAggregateNodeBase) _aggregateAccessMultiValueNode; } + } + + public override ExprEvaluator ExprEvaluator + { + get { return this; } + } + + public Type ReturnType + { + get { return _accessorFactory.ResultType; } + } + + public AggregationAccessor Accessor + { + get { return _accessorFactory.Accessor; } + } + + protected override void ValidateBindingInternal( + ExprValidationContext validationContext, + TableMetadata tableMetadata) + { + // validate group keys + ValidateGroupKeys(tableMetadata); + var column = + (TableMetadataColumnAggregation) ValidateSubpropertyGetCol(tableMetadata, _subpropName); + + // validate accessor factory i.e. the parameters types and the match to the required state + if (column.AccessAccessorSlotPair == null) + { + throw new ExprValidationException("Invalid combination of aggregation state and aggregation accessor"); + } + var mfNode = + (ExprAggregateAccessMultiValueNode) _aggregateAccessMultiValueNode; + mfNode.ValidatePositionals(); + _accessorFactory = mfNode.ValidateAggregationParamsWBinding(validationContext, column); + } + + public object Evaluate(EvaluateParams evaluateParams) + { + return Evaluate( + evaluateParams.EventsPerStream, + evaluateParams.IsNewData, + evaluateParams.ExprEvaluatorContext + ); + } + + public Object Evaluate(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext exprEvaluatorContext) + { + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get() + .QExprTableSubpropAccessor(this, TableName, _subpropName, _accessorFactory.AggregationExpression); + var result = Strategy.Evaluate(eventsPerStream, isNewData, exprEvaluatorContext); + InstrumentationHelper.Get().AExprTableSubpropAccessor(result); + return result; + } + return Strategy.Evaluate(eventsPerStream, isNewData, exprEvaluatorContext); + } + + public string SubpropName + { + get { return _subpropName; } + } + + public EventType GetEventTypeCollection(EventAdapterService eventAdapterService, int statementId) + { + return + ((ExprAggregateAccessMultiValueNode) _aggregateAccessMultiValueNode).GetEventTypeCollection( + eventAdapterService, statementId); + } + + public ICollection EvaluateGetROCollectionEvents(EvaluateParams evaluateParams) + { + return Strategy.EvaluateGetROCollectionEvents(evaluateParams.EventsPerStream, evaluateParams.IsNewData, evaluateParams.ExprEvaluatorContext); + } + + public Type ComponentTypeCollection + { + get { return ((ExprAggregateAccessMultiValueNode) _aggregateAccessMultiValueNode).ComponentTypeCollection; } + } + + public ICollection EvaluateGetROCollectionScalar(EvaluateParams evaluateParams) + { + return Strategy.EvaluateGetROCollectionScalar(evaluateParams.EventsPerStream, evaluateParams.IsNewData, evaluateParams.ExprEvaluatorContext); + } + + public EventType GetEventTypeSingle(EventAdapterService eventAdapterService, int statementId) + { + return + ((ExprAggregateAccessMultiValueNode) _aggregateAccessMultiValueNode).GetEventTypeSingle( + eventAdapterService, statementId); + } + + public EventBean EvaluateGetEventBean(EvaluateParams evaluateParams) + { + return Strategy.EvaluateGetEventBean(evaluateParams.EventsPerStream, evaluateParams.IsNewData, evaluateParams.ExprEvaluatorContext); + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + ToPrecedenceFreeEPLInternal(writer, _subpropName); + writer.Write("."); + _aggregateAccessMultiValueNode.ToEPL(writer, ExprPrecedenceEnum.MINIMUM); + } + + protected override bool EqualsNodeInternal(ExprTableAccessNode other) + { + var that = (ExprTableAccessNodeSubpropAccessor) other; + if (!_subpropName.Equals(that._subpropName)) + { + return false; + } + return ExprNodeUtility.DeepEquals(_aggregateAccessMultiValueNode, that._aggregateAccessMultiValueNode); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/table/ExprTableAccessNodeTopLevel.cs b/NEsper.Core/NEsper.Core/epl/expression/table/ExprTableAccessNodeTopLevel.cs new file mode 100755 index 000000000..116fe5637 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/table/ExprTableAccessNodeTopLevel.cs @@ -0,0 +1,113 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.table +{ + [Serializable] + public class ExprTableAccessNodeTopLevel + : ExprTableAccessNode + , ExprEvaluatorTypableReturn + { + [NonSerialized] + private LinkedHashMap _eventType; + + public ExprTableAccessNodeTopLevel(string tableName) : base(tableName) + { + } + + public override ExprEvaluator ExprEvaluator + { + get { return this; } + } + + protected override void ValidateBindingInternal(ExprValidationContext validationContext, TableMetadata tableMetadata) + { + ValidateGroupKeys(tableMetadata); + _eventType = new LinkedHashMap(); + foreach (var entry in tableMetadata.TableColumns) + { + Type classResult; + if (entry.Value is TableMetadataColumnPlain) { + classResult = tableMetadata.InternalEventType.GetPropertyType(entry.Key); + } + else + { + TableMetadataColumnAggregation aggcol = (TableMetadataColumnAggregation) entry.Value; + classResult = TypeHelper.GetBoxedType(aggcol.Factory.ResultType); + } + _eventType.Put(entry.Key, classResult); + } + } + + public Type ReturnType + { + get { return typeof(IDictionary); } + } + + public object Evaluate(EvaluateParams evaluateParams) + { + return Evaluate( + evaluateParams.EventsPerStream, + evaluateParams.IsNewData, + evaluateParams.ExprEvaluatorContext); + } + + public object Evaluate(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext exprEvaluatorContext) + { + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().QExprTableTop(this, TableName); + object result = Strategy.Evaluate(eventsPerStream, isNewData, exprEvaluatorContext); + InstrumentationHelper.Get().AExprTableTop(result); + return result; + } + return Strategy.Evaluate(eventsPerStream, isNewData, exprEvaluatorContext); + } + + public IDictionary RowProperties + { + get { return _eventType; } + } + + public bool? IsMultirow + { + get { return false; } + } + + public object[] EvaluateTypableSingle(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + return Strategy.EvaluateTypableSingle(eventsPerStream, isNewData, context); + } + + public object[][] EvaluateTypableMulti(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + throw new UnsupportedOperationException(); + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + ToPrecedenceFreeEPLInternal(writer); + } + + protected override bool EqualsNodeInternal(ExprTableAccessNode other) + { + return true; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/table/ExprTableIdentNode.cs b/NEsper.Core/NEsper.Core/epl/expression/table/ExprTableIdentNode.cs new file mode 100755 index 000000000..ae9a4facb --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/table/ExprTableIdentNode.cs @@ -0,0 +1,65 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.table +{ + [Serializable] + public class ExprTableIdentNode : ExprNodeBase + { + private readonly string _streamOrPropertyName; + private readonly string _unresolvedPropertyName; + + [NonSerialized] + private ExprEvaluator _eval; + + public ExprTableIdentNode(string streamOrPropertyName, string unresolvedPropertyName) + { + _streamOrPropertyName = streamOrPropertyName; + _unresolvedPropertyName = unresolvedPropertyName; + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) { + ExprIdentNodeImpl.ToPrecedenceFreeEPL(writer, _streamOrPropertyName, _unresolvedPropertyName); + } + + public override ExprEvaluator ExprEvaluator + { + get { return _eval; } + } + + public ExprEvaluator Eval + { + set { _eval = value; } + } + + public override ExprPrecedenceEnum Precedence + { + get { return ExprPrecedenceEnum.UNARY; } + } + + public override bool IsConstantResult + { + get { return false; } + } + + public override bool EqualsNode(ExprNode node) { + return false; + } + + public override ExprNode Validate(ExprValidationContext validationContext) { + return null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/table/ExprTableIdentNodeSubpropAccessor.cs b/NEsper.Core/NEsper.Core/epl/expression/table/ExprTableIdentNodeSubpropAccessor.cs new file mode 100755 index 000000000..fc509b060 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/table/ExprTableIdentNodeSubpropAccessor.cs @@ -0,0 +1,171 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.accessagg; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.epl.table.strategy; +using com.espertech.esper.events; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.epl.expression.table +{ + [Serializable] + public class ExprTableIdentNodeSubpropAccessor + : ExprNodeBase + , ExprEvaluator + , ExprEvaluatorEnumeration + { + private readonly int _streamNum; + private readonly string _optionalStreamName; + private readonly TableMetadataColumnAggregation _tableAccessColumn; + private readonly ExprNode _aggregateAccessMultiValueNode; + + [NonSerialized] + private AggregationMethodFactory _accessorFactory; + [NonSerialized] + private AggregationAccessor _accessor; + + public ExprTableIdentNodeSubpropAccessor(int streamNum, string optionalStreamName, TableMetadataColumnAggregation tableAccessColumn, ExprNode aggregateAccessMultiValueNode) + { + _streamNum = streamNum; + _optionalStreamName = optionalStreamName; + _tableAccessColumn = tableAccessColumn; + _aggregateAccessMultiValueNode = aggregateAccessMultiValueNode; + } + + public override ExprNode Validate(ExprValidationContext validationContext) + { + if (_tableAccessColumn.AccessAccessorSlotPair == null) { + throw new ExprValidationException("Invalid combination of aggregation state and aggregation accessor"); + } + + var mfNode = (ExprAggregateAccessMultiValueNode) _aggregateAccessMultiValueNode; + mfNode.ValidatePositionals(); + _accessorFactory = mfNode.ValidateAggregationParamsWBinding(validationContext, _tableAccessColumn); + _accessor = _accessorFactory.Accessor; + + return null; + } + + public override ExprEvaluator ExprEvaluator + { + get { return this; } + } + + public virtual Type ReturnType + { + get { return _accessorFactory.ResultType; } + } + + public object Evaluate(EvaluateParams evaluateParams) + { + return Evaluate( + evaluateParams.EventsPerStream, + evaluateParams.IsNewData, + evaluateParams.ExprEvaluatorContext); + } + + public object Evaluate(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext exprEvaluatorContext) + { + AggregationState state = GetState(eventsPerStream); + if (state == null) { + return null; + } + return _accessor.GetValue(state, new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext)); + } + + public EventType GetEventTypeCollection(EventAdapterService eventAdapterService, int statementId) + { + return ((ExprAggregateAccessMultiValueNode) _aggregateAccessMultiValueNode).GetEventTypeCollection(eventAdapterService, statementId); + } + + public ICollection EvaluateGetROCollectionEvents(EvaluateParams evaluateParams) + { + AggregationState state = GetState(evaluateParams.EventsPerStream); + if (state == null) { + return null; + } + return _accessor.GetEnumerableEvents(state, evaluateParams); + } + + public Type ComponentTypeCollection + { + get { return ((ExprAggregateAccessMultiValueNode) _aggregateAccessMultiValueNode).ComponentTypeCollection; } + } + + public ICollection EvaluateGetROCollectionScalar(EvaluateParams evaluateParams) + { + AggregationState state = GetState(evaluateParams.EventsPerStream); + if (state == null) { + return null; + } + return _accessor.GetEnumerableScalar(state, evaluateParams); + } + + public EventType GetEventTypeSingle(EventAdapterService eventAdapterService, int statementId) + { + return ((ExprAggregateAccessMultiValueNode) _aggregateAccessMultiValueNode).GetEventTypeSingle(eventAdapterService, statementId); + } + + public EventBean EvaluateGetEventBean(EvaluateParams evaluateParams) + { + AggregationState state = GetState(evaluateParams.EventsPerStream); + if (state == null) { + return null; + } + return _accessor.GetEnumerableEvent(state, evaluateParams); + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + if (_optionalStreamName != null) { + writer.Write(_optionalStreamName); + writer.Write("."); + } + writer.Write(_tableAccessColumn.ColumnName); + writer.Write("."); + _aggregateAccessMultiValueNode.ToEPL(writer, ExprPrecedenceEnum.MINIMUM); + } + + public override ExprPrecedenceEnum Precedence + { + get { return ExprPrecedenceEnum.UNARY; } + } + + public override bool IsConstantResult + { + get { return false; } + } + + public override bool EqualsNode(ExprNode node) + { + return false; + } + + private AggregationState GetState(EventBean[] eventsPerStream) + { + EventBean @event = eventsPerStream[_streamNum]; + if (@event == null) + { + return null; + } + AggregationRowPair row = ExprTableEvalStrategyUtil.GetRow((ObjectArrayBackedEventBean) @event); + return row.States[_tableAccessColumn.AccessAccessorSlotPair.Slot]; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/table/ExprTableNodeUtil.cs b/NEsper.Core/NEsper.Core/epl/expression/table/ExprTableNodeUtil.cs new file mode 100755 index 000000000..4015ece78 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/table/ExprTableNodeUtil.cs @@ -0,0 +1,55 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.table +{ + public class ExprTableNodeUtil + { + public static void ValidateExpressions( + string tableName, + Type[] providedTypes, + string providedName, + IList providedExpr, + Type[] expectedTypes, + string expectedName) + { + if (expectedTypes.Length != providedTypes.Length) + { + string actual = (providedTypes.Length == 0 ? "no" : "" + providedTypes.Length) + " " + providedName + " expressions"; + string expected = (expectedTypes.Length == 0 ? "no" : "" + expectedTypes.Length) + " " + expectedName + " expressions"; + throw new ExprValidationException( + string.Format( + "Incompatible number of {0} expressions for use with table '{1}', the table expects {2} and provided are {3}", + providedName, tableName, expected, actual)); + } + + for (int i = 0; i < expectedTypes.Length; i++) + { + var actual = providedTypes[i].GetBoxedType(); + var expected = expectedTypes[i].GetBoxedType(); + if (!TypeHelper.IsSubclassOrImplementsInterface(actual, expected)) + { + throw new ExprValidationException( + string.Format( + "Incompatible type returned by a {0} expression for use with table '{1}', the {0} expression '{2}' returns '{3}' but the table expects '{4}'", + providedName, + tableName, + ExprNodeUtility.ToExpressionStringMinPrecedence(providedExpr), + actual.GetCleanName(), + expected.GetCleanName())); + } + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/time/ExprTimePeriod.cs b/NEsper.Core/NEsper.Core/epl/expression/time/ExprTimePeriod.cs new file mode 100755 index 000000000..e211a7468 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/time/ExprTimePeriod.cs @@ -0,0 +1,66 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.client.util; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.time +{ + /// + /// Expression representing a time period. + /// + /// Child nodes to this expression carry the actual parts and must return a numeric value. + /// + public interface ExprTimePeriod : ExprNode + { + TimePeriod EvaluateGetTimePeriod(EvaluateParams evaluateParams); + + bool HasVariable { get; } + + ExprTimePeriodEvalDeltaConst ConstEvaluator(ExprEvaluatorContext context); + ExprTimePeriodEvalDeltaNonConst NonconstEvaluator(); + double EvaluateAsSeconds(EventBean[] eventsPerStream, bool newData, ExprEvaluatorContext context); + + /// Indicator whether the time period has a day part child expression. + /// true for part present, false for not present + bool HasDay { get; } + + /// Indicator whether the time period has a hour part child expression. + /// true for part present, false for not present + bool HasHour { get; } + + /// Indicator whether the time period has a minute part child expression. + /// true for part present, false for not present + bool HasMinute { get; } + + /// Indicator whether the time period has a second part child expression. + /// true for part present, false for not present + bool HasSecond { get; } + + /// Indicator whether the time period has a millisecond part child expression. + /// true for part present, false for not present + bool HasMillisecond { get; } + + /// Indicator whether the time period has a microsecond part child expression. + /// true for part present, false for not present + bool HasMicrosecond { get; } + + /// Indicator whether the time period has a year part child expression. + /// true for part present, false for not present + bool HasYear { get; } + + /// Indicator whether the time period has a month part child expression. + /// true for part present, false for not present + bool HasMonth { get; } + + /// Indicator whether the time period has a week part child expression. + /// true for part present, false for not present + bool HasWeek { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/time/ExprTimePeriodEvalDeltaConst.cs b/NEsper.Core/NEsper.Core/epl/expression/time/ExprTimePeriodEvalDeltaConst.cs new file mode 100755 index 000000000..906773620 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/time/ExprTimePeriodEvalDeltaConst.cs @@ -0,0 +1,21 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.expression.time +{ + public interface ExprTimePeriodEvalDeltaConst : ExprTimePeriodEvalDeltaConstFactory + { + long DeltaAdd(long fromTime); + + long DeltaSubtract(long fromTime); + + ExprTimePeriodEvalDeltaResult DeltaAddWReference(long fromTime, long reference); + + bool EqualsTimePeriod(ExprTimePeriodEvalDeltaConst timeDeltaComputation); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/time/ExprTimePeriodEvalDeltaConstFactory.cs b/NEsper.Core/NEsper.Core/epl/expression/time/ExprTimePeriodEvalDeltaConstFactory.cs new file mode 100755 index 000000000..3291cd668 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/time/ExprTimePeriodEvalDeltaConstFactory.cs @@ -0,0 +1,21 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.util; + +namespace com.espertech.esper.epl.expression.time +{ + public interface ExprTimePeriodEvalDeltaConstFactory { + ExprTimePeriodEvalDeltaConst Make(string validateMsgName, string validateMsgValue, AgentInstanceContext agentInstanceContext); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/time/ExprTimePeriodEvalDeltaConstFactoryMsec.cs b/NEsper.Core/NEsper.Core/epl/expression/time/ExprTimePeriodEvalDeltaConstFactoryMsec.cs new file mode 100755 index 000000000..a0ae2a9fc --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/time/ExprTimePeriodEvalDeltaConstFactoryMsec.cs @@ -0,0 +1,39 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.time +{ + public class ExprTimePeriodEvalDeltaConstFactoryMsec : ExprTimePeriodEvalDeltaConstFactory + { + private readonly ExprEvaluator _secondsEvaluator; + private readonly TimeAbacus _timeAbacus; + + public ExprTimePeriodEvalDeltaConstFactoryMsec(ExprEvaluator secondsEvaluator, TimeAbacus timeAbacus) + { + _secondsEvaluator = secondsEvaluator; + _timeAbacus = timeAbacus; + } + + public ExprTimePeriodEvalDeltaConst Make( + string validateMsgName, + string validateMsgValue, + AgentInstanceContext agentInstanceContext) + { + object time = _secondsEvaluator.Evaluate(new EvaluateParams(null, true, agentInstanceContext)); + if (!ExprTimePeriodUtil.ValidateTime(time, agentInstanceContext.StatementContext.TimeAbacus)) + { + throw new EPException(ExprTimePeriodUtil.GetTimeInvalidMsg(validateMsgName, validateMsgValue, time)); + } + return new ExprTimePeriodEvalDeltaConstGivenDelta(_timeAbacus.DeltaForSecondsNumber(time)); + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/expression/time/ExprTimePeriodEvalDeltaConstGivenDelta.cs b/NEsper.Core/NEsper.Core/epl/expression/time/ExprTimePeriodEvalDeltaConstGivenDelta.cs new file mode 100755 index 000000000..2a75e3bfe --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/time/ExprTimePeriodEvalDeltaConstGivenDelta.cs @@ -0,0 +1,77 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.context.util; + +namespace com.espertech.esper.epl.expression.time +{ + public class ExprTimePeriodEvalDeltaConstGivenDelta + : ExprTimePeriodEvalDeltaConst + , ExprTimePeriodEvalDeltaConstFactory + { + private readonly long _timeDelta; + + public ExprTimePeriodEvalDeltaConstGivenDelta(long timeDelta) + { + _timeDelta = timeDelta; + } + + internal static long DeltaAddWReference(long current, long reference, long msec) + { + // Example: current c=2300, reference r=1000, interval i=500, solution s=200 + // + // int n = ((2300 - 1000) / 500) = 2 + // r + (n + 1) * i - c = 200 + // + // Negative example: current c=2300, reference r=4200, interval i=500, solution s=400 + // int n = ((2300 - 4200) / 500) = -3 + // r + (n + 1) * i - c = 4200 - 3*500 - 2300 = 400 + // + var n = (current - reference) / msec; + if (reference > current) { // References in the future need to deduct one window + n--; + } + var solution = reference + (n + 1) * msec - current; + if (solution == 0) { + return msec; + } + return solution; + } + + public ExprTimePeriodEvalDeltaConst Make(string validateMsgName, string validateMsgValue, AgentInstanceContext agentInstanceContext) + { + return this; + } + + public bool EqualsTimePeriod(ExprTimePeriodEvalDeltaConst otherComputation) + { + if (otherComputation is ExprTimePeriodEvalDeltaConstGivenDelta) + { + var other = (ExprTimePeriodEvalDeltaConstGivenDelta) otherComputation; + return other._timeDelta == _timeDelta; + } + return false; + } + + public long DeltaAdd(long fromTime) + { + return _timeDelta; + } + + public long DeltaSubtract(long fromTime) + { + return _timeDelta; + } + + public ExprTimePeriodEvalDeltaResult DeltaAddWReference(long fromTime, long reference) + { + return new ExprTimePeriodEvalDeltaResult(DeltaAddWReference(fromTime, reference, _timeDelta), reference); + } + + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/time/ExprTimePeriodEvalDeltaConstGivenDtxAdd.cs b/NEsper.Core/NEsper.Core/epl/expression/time/ExprTimePeriodEvalDeltaConstGivenDtxAdd.cs new file mode 100755 index 000000000..6abac95be --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/time/ExprTimePeriodEvalDeltaConstGivenDtxAdd.cs @@ -0,0 +1,126 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.threading; +using com.espertech.esper.core.context.util; + +namespace com.espertech.esper.epl.expression.time +{ + public class ExprTimePeriodEvalDeltaConstGivenDtxAdd + : ExprTimePeriodEvalDeltaConst + , ExprTimePeriodEvalDeltaConstFactory + { + private readonly DateTimeEx _dateTime; + private readonly ExprTimePeriodImpl.TimePeriodAdder[] _adders; + private readonly int[] _added; + private readonly TimeAbacus _timeAbacus; + private readonly int _indexMicroseconds; + private readonly ILockable _iLock; + + public ExprTimePeriodEvalDeltaConstGivenDtxAdd( + ExprTimePeriodImpl.TimePeriodAdder[] adders, + int[] added, + TimeZoneInfo timeZone, + TimeAbacus timeAbacus) + { + _iLock = LockManager.CreateLock(MethodBase.GetCurrentMethod().DeclaringType); + _adders = adders; + _added = added; + _dateTime = new DateTimeEx(DateTimeOffset.Now, timeZone); + _timeAbacus = timeAbacus; + _indexMicroseconds = ExprTimePeriodUtil.FindIndexMicroseconds(adders); + } + + public ExprTimePeriodEvalDeltaConst Make( + string validateMsgName, + string validateMsgValue, + AgentInstanceContext agentInstanceContext) + { + return this; + } + + public bool EqualsTimePeriod(ExprTimePeriodEvalDeltaConst otherComputation) + { + if (otherComputation is ExprTimePeriodEvalDeltaConstGivenDtxAdd) + { + var other = (ExprTimePeriodEvalDeltaConstGivenDtxAdd) otherComputation; + if (other._adders.Length != _adders.Length) + { + return false; + } + for (int i = 0; i < _adders.Length; i++) + { + if (_added[i] != other._added[i] || _adders[i].GetType() != other._adders[i].GetType()) + { + return false; + } + } + return true; + } + return false; + } + + public long DeltaAdd(long fromTime) + { + using (_iLock.Acquire()) + { + long target = AddSubtract(fromTime, 1); + return target - fromTime; + } + } + + public long DeltaSubtract(long fromTime) + { + using (_iLock.Acquire()) + { + long target = AddSubtract(fromTime, -1); + return fromTime - target; + } + } + + public ExprTimePeriodEvalDeltaResult DeltaAddWReference(long fromTime, long reference) + { + // find the next-nearest reference higher then the current time, compute delta, return reference one lower + if (reference > fromTime) + { + while (reference > fromTime) + { + reference = reference - DeltaSubtract(reference); + } + } + + long next = reference; + long last; + do + { + last = next; + next = next + DeltaAdd(last); + } while (next <= fromTime); + return new ExprTimePeriodEvalDeltaResult(next - fromTime, last); + } + + private long AddSubtract(long fromTime, int factor) + { + long remainder = _timeAbacus.CalendarSet(fromTime, _dateTime); + for (int i = 0; i < _adders.Length; i++) + { + _adders[i].Add(_dateTime, factor*_added[i]); + } + long result = _timeAbacus.CalendarGet(_dateTime, remainder); + if (_indexMicroseconds != -1) + { + result += factor*_added[_indexMicroseconds]; + } + return result; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/time/ExprTimePeriodEvalDeltaNonConst.cs b/NEsper.Core/NEsper.Core/epl/expression/time/ExprTimePeriodEvalDeltaNonConst.cs new file mode 100755 index 000000000..eb5149d64 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/time/ExprTimePeriodEvalDeltaNonConst.cs @@ -0,0 +1,30 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.time +{ + public interface ExprTimePeriodEvalDeltaNonConst + { + long DeltaAdd(long currentTime, EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context); + + long DeltaSubtract(long currentTime, EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context); + + long DeltaUseEngineTime(EventBean[] eventsPerStream, AgentInstanceContext agentInstanceContext); + + ExprTimePeriodEvalDeltaResult DeltaAddWReference( + long current, + long reference, + EventBean[] eventsPerStream, + bool isNewData, + ExprEvaluatorContext context); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/time/ExprTimePeriodEvalDeltaNonConstDtxAdd.cs b/NEsper.Core/NEsper.Core/epl/expression/time/ExprTimePeriodEvalDeltaNonConstDtxAdd.cs new file mode 100755 index 000000000..11a976a8f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/time/ExprTimePeriodEvalDeltaNonConstDtxAdd.cs @@ -0,0 +1,108 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.time +{ + public class ExprTimePeriodEvalDeltaNonConstDtxAdd : ExprTimePeriodEvalDeltaNonConst + { + private readonly DateTimeEx _dateTime; + private readonly ExprTimePeriodImpl _parent; + private readonly int _indexMicroseconds; + + public ExprTimePeriodEvalDeltaNonConstDtxAdd(TimeZoneInfo timeZone, ExprTimePeriodImpl parent) + { + _parent = parent; + _dateTime = new DateTimeEx(DateTimeOffsetHelper.Now(timeZone), timeZone); + _indexMicroseconds = ExprTimePeriodUtil.FindIndexMicroseconds(parent.Adders); + } + + public long DeltaAdd(long currentTime, EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + lock (this) + { + return AddSubtract(currentTime, 1, eventsPerStream, isNewData, context); + } + } + + public long DeltaSubtract(long currentTime, EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + lock (this) + { + return AddSubtract(currentTime, -1, eventsPerStream, isNewData, context); + } + } + + public long DeltaUseEngineTime(EventBean[] eventsPerStream, AgentInstanceContext agentInstanceContext) + { + lock (this) + { + long currentTime = agentInstanceContext.StatementContext.SchedulingService.Time; + return AddSubtract(currentTime, 1, eventsPerStream, true, agentInstanceContext); + } + } + + public ExprTimePeriodEvalDeltaResult DeltaAddWReference(long current, long reference, EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + lock (this) + { + // find the next-nearest reference higher then the current time, compute delta, return reference one lower + if (reference > current) + { + while (reference > current) + { + reference = reference - DeltaSubtract(reference, eventsPerStream, isNewData, context); + } + } + + long next = reference; + long last; + do + { + last = next; + next = next + DeltaAdd(last, eventsPerStream, isNewData, context); + } while (next <= current); + return new ExprTimePeriodEvalDeltaResult(next - current, last); + } + } + + private long AddSubtract(long currentTime, int factor, EventBean[] eventsPerStream, bool newData, ExprEvaluatorContext context) + { + var remainder = _parent.TimeAbacus.CalendarSet(currentTime, _dateTime); + var adders = _parent.Adders; + var evaluators = _parent.Evaluators; + var evaluateParams = new EvaluateParams(eventsPerStream, newData, context); + var usec = 0; + for (int i = 0; i < adders.Length; i++) + { + var value = evaluators[i].Evaluate(evaluateParams).AsInt(); + if (i == _indexMicroseconds) + { + usec = value; + } + else + { + adders[i].Add(_dateTime, factor * value); + } + } + + long result = _parent.TimeAbacus.CalendarGet(_dateTime, remainder); + if (_indexMicroseconds != -1) + { + result += factor * usec; + } + return result - currentTime; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/time/ExprTimePeriodEvalDeltaNonConstMsec.cs b/NEsper.Core/NEsper.Core/epl/expression/time/ExprTimePeriodEvalDeltaNonConstMsec.cs new file mode 100755 index 000000000..705028529 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/time/ExprTimePeriodEvalDeltaNonConstMsec.cs @@ -0,0 +1,60 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.time +{ + public class ExprTimePeriodEvalDeltaNonConstMsec : ExprTimePeriodEvalDeltaNonConst + { + private readonly ExprTimePeriodImpl _exprTimePeriod; + + public ExprTimePeriodEvalDeltaNonConstMsec(ExprTimePeriodImpl exprTimePeriod) + { + this._exprTimePeriod = exprTimePeriod; + } + + public long DeltaAdd( + long currentTime, + EventBean[] eventsPerStream, + bool isNewData, + ExprEvaluatorContext context) + { + double d = _exprTimePeriod.EvaluateAsSeconds(eventsPerStream, isNewData, context); + return _exprTimePeriod.TimeAbacus.DeltaForSecondsDouble(d); + } + + public long DeltaSubtract( + long currentTime, + EventBean[] eventsPerStream, + bool isNewData, + ExprEvaluatorContext context) + { + return DeltaAdd(currentTime, eventsPerStream, isNewData, context); + } + + public long DeltaUseEngineTime(EventBean[] eventsPerStream, AgentInstanceContext agentInstanceContext) + { + return DeltaAdd(0, eventsPerStream, true, agentInstanceContext); + } + + public ExprTimePeriodEvalDeltaResult DeltaAddWReference( + long current, + long reference, + EventBean[] eventsPerStream, + bool isNewData, + ExprEvaluatorContext context) + { + long msec = DeltaAdd(current, eventsPerStream, isNewData, context); + return new ExprTimePeriodEvalDeltaResult( + ExprTimePeriodEvalDeltaConstGivenDelta.DeltaAddWReference(current, reference, msec), reference); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/time/ExprTimePeriodEvalDeltaResult.cs b/NEsper.Core/NEsper.Core/epl/expression/time/ExprTimePeriodEvalDeltaResult.cs new file mode 100755 index 000000000..5ef55b37b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/time/ExprTimePeriodEvalDeltaResult.cs @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.expression.time +{ + public class ExprTimePeriodEvalDeltaResult + { + public ExprTimePeriodEvalDeltaResult(long delta, long lastReference) + { + Delta = delta; + LastReference = lastReference; + } + + public long Delta { get; private set; } + + public long LastReference { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/time/ExprTimePeriodEvalMillis.cs b/NEsper.Core/NEsper.Core/epl/expression/time/ExprTimePeriodEvalMillis.cs new file mode 100755 index 000000000..423747ae2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/time/ExprTimePeriodEvalMillis.cs @@ -0,0 +1,18 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.time +{ + public interface ExprTimePeriodEvalMillis + { + long Evaluate(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/time/ExprTimePeriodImpl.cs b/NEsper.Core/NEsper.Core/epl/expression/time/ExprTimePeriodImpl.cs new file mode 100755 index 000000000..34af9aee4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/time/ExprTimePeriodImpl.cs @@ -0,0 +1,698 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; +using System.Linq; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.client.util; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.time +{ + /// + /// Expression representing a time period. + /// + /// Child nodes to this expression carry the actual parts and must return a numeric value. + /// + [Serializable] + public class ExprTimePeriodImpl + : ExprNodeBase + , ExprTimePeriod + , ExprEvaluator + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly TimeZoneInfo _timeZone; + private readonly bool _hasYear; + private readonly bool _hasMonth; + private readonly bool _hasWeek; + private readonly bool _hasDay; + private readonly bool _hasHour; + private readonly bool _hasMinute; + private readonly bool _hasSecond; + private readonly bool _hasMillisecond; + private readonly bool _hasMicrosecond; + private readonly TimeAbacus _timeAbacus; + private bool _hasVariable; + [NonSerialized] + private ExprEvaluator[] _evaluators; + [NonSerialized] + private TimePeriodAdder[] _adders; + + /// + /// Ctor. + /// + /// The time zone. + /// if set to true [has year]. + /// if set to true [has month]. + /// if set to true [has week]. + /// true if the expression has that part, false if not + /// true if the expression has that part, false if not + /// true if the expression has that part, false if not + /// true if the expression has that part, false if not + /// true if the expression has that part, false if not + /// if set to true [has microsecond]. + /// The time abacus. + public ExprTimePeriodImpl( + TimeZoneInfo timeZone, + bool hasYear, + bool hasMonth, + bool hasWeek, + bool hasDay, + bool hasHour, + bool hasMinute, + bool hasSecond, + bool hasMillisecond, + bool hasMicrosecond, + TimeAbacus timeAbacus) + { + _timeZone = timeZone; + _hasYear = hasYear; + _hasMonth = hasMonth; + _hasWeek = hasWeek; + _hasDay = hasDay; + _hasHour = hasHour; + _hasMinute = hasMinute; + _hasSecond = hasSecond; + _hasMillisecond = hasMillisecond; + _hasMicrosecond = hasMicrosecond; + _timeAbacus = timeAbacus; + } + + public ExprTimePeriodEvalDeltaConst ConstEvaluator(ExprEvaluatorContext context) + { + if (!_hasMonth && !_hasYear) + { + double seconds = EvaluateAsSeconds(null, true, context); + long msec = _timeAbacus.DeltaForSecondsDouble(seconds); + return new ExprTimePeriodEvalDeltaConstGivenDelta(msec); + } + else + { + var evaluateParams = new EvaluateParams(null, true, context); + var values = new int[_adders.Length]; + for (int i = 0; i < values.Length; i++) + { + values[i] = _evaluators[i].Evaluate(evaluateParams).AsInt(); + } + return new ExprTimePeriodEvalDeltaConstGivenDtxAdd(_adders, values, _timeZone, _timeAbacus); + } + } + + public ExprTimePeriodEvalDeltaNonConst NonconstEvaluator() + { + if (!_hasMonth && !_hasYear) + { + return new ExprTimePeriodEvalDeltaNonConstMsec(this); + } + else + { + return new ExprTimePeriodEvalDeltaNonConstDtxAdd(_timeZone, this); + } + } + + public TimeAbacus TimeAbacus + { + get { return _timeAbacus; } + } + + public override ExprEvaluator ExprEvaluator + { + get { return this; } + } + + public object Evaluate(EvaluateParams evaluateParams) + { + throw new IllegalStateException("Time-Period expression must be evaluated via any of " + typeof(ExprTimePeriod).Name + " interface methods"); + } + + public TimePeriodAdder[] Adders + { + get { return _adders; } + } + + public ExprEvaluator[] Evaluators + { + get { return _evaluators; } + } + + /// Indicator whether the time period has a day part child expression. + /// true for part present, false for not present + public bool HasDay + { + get { return _hasDay; } + } + + /// Indicator whether the time period has a hour part child expression. + /// true for part present, false for not present + public bool HasHour + { + get { return _hasHour; } + } + + /// Indicator whether the time period has a minute part child expression. + /// true for part present, false for not present + public bool HasMinute + { + get { return _hasMinute; } + } + + /// Indicator whether the time period has a second part child expression. + /// true for part present, false for not present + public bool HasSecond + { + get { return _hasSecond; } + } + + /// Indicator whether the time period has a millisecond part child expression. + /// true for part present, false for not present + public bool HasMillisecond + { + get { return _hasMillisecond; } + } + + /// Indicator whether the time period has a microsecond part child expression. + /// true for part present, false for not present + public bool HasMicrosecond + { + get { return _hasMicrosecond; } + } + + /// Indicator whether the time period has a year part child expression. + /// true for part present, false for not present + public bool HasYear + { + get { return _hasYear; } + } + + /// Indicator whether the time period has a month part child expression. + /// true for part present, false for not present + public bool HasMonth + { + get { return _hasMonth; } + } + + /// Indicator whether the time period has a week part child expression. + /// true for part present, false for not present + public bool HasWeek + { + get { return _hasWeek; } + } + + /// Indicator whether the time period has a variable in any of the child expressions. + /// true for variable present, false for not present + public bool HasVariable + { + get { return _hasVariable; } + } + + public override ExprNode Validate(ExprValidationContext validationContext) + { + _evaluators = ExprNodeUtility.GetEvaluators(ChildNodes); + foreach (ExprNode childNode in ChildNodes) + { + Validate(childNode); + } + + var list = new ArrayDeque(); + if (_hasYear) + { + list.Add(new TimePeriodAdderYear()); + } + if (_hasMonth) + { + list.Add(new TimePeriodAdderMonth()); + } + if (_hasWeek) + { + list.Add(new TimePeriodAdderWeek()); + } + if (_hasDay) + { + list.Add(new TimePeriodAdderDay()); + } + if (_hasHour) + { + list.Add(new TimePeriodAdderHour()); + } + if (_hasMinute) + { + list.Add(new TimePeriodAdderMinute()); + } + if (_hasSecond) + { + list.Add(new TimePeriodAdderSecond()); + } + if (_hasMillisecond) + { + list.Add(new TimePeriodAdderMSec()); + } + if (_hasMicrosecond) + { + list.Add(new TimePeriodAdderUSec()); + } + _adders = list.ToArray(); + + return null; + } + + private void Validate(ExprNode expression) + { + if (expression == null) + { + return; + } + var returnType = expression.ExprEvaluator.ReturnType; + if (!returnType.IsNumeric()) + { + throw new ExprValidationException("Time period expression requires a numeric parameter type"); + } + if ((_hasMonth || _hasYear) && (returnType.GetBoxedType() != typeof(int?))) + { + throw new ExprValidationException("Time period expressions with month or year component require integer values, received a " + returnType.FullName + " value"); + } + if (expression is ExprVariableNode) + { + _hasVariable = true; + } + } + + public double EvaluateAsSeconds(EventBean[] eventsPerStream, bool newData, ExprEvaluatorContext context) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QExprTimePeriod(this); } + double seconds = 0; + for (int i = 0; i < _adders.Length; i++) + { + var result = Eval(_evaluators[i], eventsPerStream, newData, context); + if (result == null) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprTimePeriod(null); } + throw new EPException("Failed to evaluate time period, received a null value for '" + ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(this) + "'"); + } + seconds += _adders[i].Compute(result.Value); + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprTimePeriod(seconds); } + return seconds; + } + + private double? Eval(ExprEvaluator expr, EventBean[] events, bool isNewData, ExprEvaluatorContext exprEvaluatorContext) + { + var value = expr.Evaluate(new EvaluateParams(events, isNewData, exprEvaluatorContext)); + if (value == null) + { + return null; + } + return (value).AsDouble(); + } + + public TimePeriod EvaluateGetTimePeriod(EvaluateParams evaluateParams) + { + int exprCtr = 0; + + int? year = null; + if (_hasYear) + { + year = GetInt(_evaluators[exprCtr++].Evaluate(evaluateParams)); + } + + int? month = null; + if (_hasMonth) + { + month = GetInt(_evaluators[exprCtr++].Evaluate(evaluateParams)); + } + + int? week = null; + if (_hasWeek) + { + week = GetInt(_evaluators[exprCtr++].Evaluate(evaluateParams)); + } + + int? day = null; + if (_hasDay) + { + day = GetInt(_evaluators[exprCtr++].Evaluate(evaluateParams)); + } + + int? hours = null; + if (_hasHour) + { + hours = GetInt(_evaluators[exprCtr++].Evaluate(evaluateParams)); + } + + int? minutes = null; + if (_hasMinute) + { + minutes = GetInt(_evaluators[exprCtr++].Evaluate(evaluateParams)); + } + + int? seconds = null; + if (_hasSecond) + { + seconds = GetInt(_evaluators[exprCtr++].Evaluate(evaluateParams)); + } + + int? milliseconds = null; + if (_hasMillisecond) + { + milliseconds = GetInt(_evaluators[exprCtr++].Evaluate(evaluateParams)); + } + + int? microseconds = null; + if (_hasMicrosecond) + { + microseconds = GetInt(_evaluators[exprCtr].Evaluate(evaluateParams)); + } + + return new TimePeriod(year, month, week, day, hours, minutes, seconds, milliseconds, microseconds); + } + + private int? GetInt(Object evaluated) + { + if (evaluated == null) + { + return null; + } + return (evaluated).AsInt(); + } + + public interface TimePeriodAdder + { + double Compute(double value); + void Add(DateTimeEx dateTime, int value); + bool IsMicroseconds { get; } + } + + public class TimePeriodAdderYear : TimePeriodAdder + { + private const double MULTIPLIER = 365 * 24 * 60 * 60; + + public double Compute(double value) + { + return value * MULTIPLIER; + } + + public void Add(DateTimeEx dateTime, int value) + { + dateTime.AddYears(value, DateTimeMathStyle.Java); + } + + public bool IsMicroseconds + { + get { return false; } + } + } + + public class TimePeriodAdderMonth : TimePeriodAdder + { + private const double MULTIPLIER = 30 * 24 * 60 * 60; + + public double Compute(double value) + { + return value * MULTIPLIER; + } + + public void Add(DateTimeEx dateTime, int value) + { + dateTime.AddMonths(value, DateTimeMathStyle.Java); + } + + public bool IsMicroseconds + { + get { return false; } + } + } + + public class TimePeriodAdderWeek : TimePeriodAdder + { + private const double MULTIPLIER = 7 * 24 * 60 * 60; + + public double Compute(double value) + { + return value * MULTIPLIER; + } + + public void Add(DateTimeEx dateTime, int value) + { + dateTime.AddDays(7 * value, DateTimeMathStyle.Java); + } + + public bool IsMicroseconds + { + get { return false; } + } + } + + public class TimePeriodAdderDay : TimePeriodAdder + { + private const double MULTIPLIER = 24 * 60 * 60; + + public double Compute(double value) + { + return value * MULTIPLIER; + } + + public void Add(DateTimeEx dateTime, int value) + { + dateTime.AddDays(value, DateTimeMathStyle.Java); + } + + public bool IsMicroseconds + { + get { return false; } + } + } + + public class TimePeriodAdderHour : TimePeriodAdder + { + private const double MULTIPLIER = 60 * 60; + + public double Compute(double value) + { + return value * MULTIPLIER; + } + + public void Add(DateTimeEx dateTime, int value) + { + dateTime.AddHours(value); + } + + public bool IsMicroseconds + { + get { return false; } + } + } + + public class TimePeriodAdderMinute : TimePeriodAdder + { + private const double MULTIPLIER = 60; + + public double Compute(double value) + { + return value * MULTIPLIER; + } + + public void Add(DateTimeEx dateTime, int value) + { + dateTime = dateTime.AddMinutes(value); + } + + public bool IsMicroseconds + { + get { return false; } + } + } + + public class TimePeriodAdderSecond : TimePeriodAdder + { + public double Compute(double value) + { + return value; + } + + public void Add(DateTimeEx dateTime, int value) + { + dateTime.AddSeconds(value); + } + + public bool IsMicroseconds + { + get { return false; } + } + } + + public class TimePeriodAdderMSec : TimePeriodAdder + { + public double Compute(double value) + { + return value / 1000d; + } + + public void Add(DateTimeEx dateTime, int value) + { + dateTime.AddMilliseconds(value); + } + + public bool IsMicroseconds + { + get { return false; } + } + } + + public class TimePeriodAdderUSec : TimePeriodAdder + { + public double Compute(double value) + { + return value / 1000000d; + } + + public void Add(DateTimeEx dateTime, int value) + { + // no action : DateTimeEx does not add microseconds + } + + public bool IsMicroseconds + { + get { return true; } + } + } + + public Type ReturnType + { + get { return typeof(double?); } + } + + public override bool IsConstantResult + { + get { return ChildNodes.All(child => child.IsConstantResult); } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + var exprCtr = 0; + var delimiter = ""; + var childNodes = ChildNodes; + if (_hasYear) + { + childNodes[exprCtr++].ToEPL(writer, Precedence); + writer.Write(" years"); + delimiter = " "; + } + if (_hasMonth) + { + writer.Write(delimiter); + childNodes[exprCtr++].ToEPL(writer, Precedence); + writer.Write(" months"); + delimiter = " "; + } + if (_hasWeek) + { + writer.Write(delimiter); + childNodes[exprCtr++].ToEPL(writer, Precedence); + writer.Write(" weeks"); + delimiter = " "; + } + if (_hasDay) + { + writer.Write(delimiter); + childNodes[exprCtr++].ToEPL(writer, Precedence); + writer.Write(" days"); + delimiter = " "; + } + if (_hasHour) + { + writer.Write(delimiter); + childNodes[exprCtr++].ToEPL(writer, Precedence); + writer.Write(" hours"); + delimiter = " "; + } + if (_hasMinute) + { + writer.Write(delimiter); + childNodes[exprCtr++].ToEPL(writer, Precedence); + writer.Write(" minutes"); + delimiter = " "; + } + if (_hasSecond) + { + writer.Write(delimiter); + childNodes[exprCtr++].ToEPL(writer, Precedence); + writer.Write(" seconds"); + delimiter = " "; + } + if (_hasMillisecond) + { + writer.Write(delimiter); + childNodes[exprCtr++].ToEPL(writer, Precedence); + writer.Write(" milliseconds"); + } + if (_hasMicrosecond) + { + writer.Write(delimiter); + childNodes[exprCtr].ToEPL(writer, Precedence); + writer.Write(" microseconds"); + } + } + + public override ExprPrecedenceEnum Precedence + { + get { return ExprPrecedenceEnum.UNARY; } + } + + public override bool EqualsNode(ExprNode node) + { + if (!(node is ExprTimePeriodImpl)) + { + return false; + } + + var other = (ExprTimePeriodImpl)node; + + if (_hasYear != other._hasYear) + { + return false; + } + if (_hasMonth != other._hasMonth) + { + return false; + } + if (_hasWeek != other._hasWeek) + { + return false; + } + if (_hasDay != other._hasDay) + { + return false; + } + if (_hasHour != other._hasHour) + { + return false; + } + if (_hasMinute != other._hasMinute) + { + return false; + } + if (_hasSecond != other._hasSecond) + { + return false; + } + if (_hasMillisecond != other._hasMillisecond) + { + return false; + } + return (_hasMicrosecond == other._hasMicrosecond); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/time/ExprTimePeriodUtil.cs b/NEsper.Core/NEsper.Core/epl/expression/time/ExprTimePeriodUtil.cs new file mode 100755 index 000000000..df61ad27e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/time/ExprTimePeriodUtil.cs @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.expression.time +{ + public class ExprTimePeriodUtil + { + public static bool ValidateTime(object timeInSeconds, TimeAbacus timeAbacus) + { + return timeInSeconds != null && timeAbacus.DeltaForSecondsNumber(timeInSeconds) >= 1; + } + + public static string GetTimeInvalidMsg(string validateMsgName, string validateMsgValue, object timeInSeconds) + { + return validateMsgName + " " + validateMsgValue + " requires a size of at least 1 msec but received " + + timeInSeconds; + } + + public static int FindIndexMicroseconds(ExprTimePeriodImpl.TimePeriodAdder[] adders) + { + int indexMicros = -1; + for (int i = 0; i < adders.Length; i++) + { + if (adders[i].IsMicroseconds) + { + indexMicros = i; + break; + } + } + return indexMicros; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/time/ExprTimestampNode.cs b/NEsper.Core/NEsper.Core/epl/expression/time/ExprTimestampNode.cs new file mode 100755 index 000000000..81ae945e4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/time/ExprTimestampNode.cs @@ -0,0 +1,78 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.expression.time +{ + /// + /// Represents the CURRENT_TIMESTAMP() function or reserved keyword in an expression tree. + /// + [Serializable] + public class ExprTimestampNode : ExprNodeBase, ExprEvaluator + { + /// Ctor. + public ExprTimestampNode() + { + } + + public override ExprEvaluator ExprEvaluator + { + get { return this; } + } + + public override ExprNode Validate(ExprValidationContext validationContext) + { + if (ChildNodes.Count != 0) + { + throw new ExprValidationException("current_timestamp function node cannot have a child node"); + } + + return null; + } + + public override bool IsConstantResult + { + get { return false; } + } + + public Type ReturnType + { + get { return typeof (long?); } + } + + public object Evaluate(EvaluateParams evaluateParams) + { + if (InstrumentationHelper.ENABLED) { + var value = evaluateParams.ExprEvaluatorContext.TimeProvider.Time; + InstrumentationHelper.Get().QaExprTimestamp(this, value); + return value; + } + return evaluateParams.ExprEvaluatorContext.TimeProvider.Time; + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + writer.Write("current_timestamp()"); + } + + public override ExprPrecedenceEnum Precedence + { + get { return ExprPrecedenceEnum.UNARY; } + } + + public override bool EqualsNode(ExprNode node) + { + return node is ExprTimestampNode; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/time/TimeAbacus.cs b/NEsper.Core/NEsper.Core/epl/expression/time/TimeAbacus.cs new file mode 100755 index 000000000..70f591bb4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/time/TimeAbacus.cs @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.compat; + +namespace com.espertech.esper.epl.expression.time +{ + public interface TimeAbacus + { + long DeltaForSecondsNumber(object timeInSeconds); + + long DeltaForSecondsDouble(double seconds); + + long CalendarSet(long fromTime, DateTimeEx dt); + + long CalendarGet(DateTimeEx dt, long remainder); + + long GetOneSecond(); + + DateTimeEx ToDate(long ts); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/time/TimeAbacusMicroseconds.cs b/NEsper.Core/NEsper.Core/epl/expression/time/TimeAbacusMicroseconds.cs new file mode 100755 index 000000000..254acd414 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/time/TimeAbacusMicroseconds.cs @@ -0,0 +1,60 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.time +{ + public class TimeAbacusMicroseconds : TimeAbacus + { + public static readonly TimeAbacusMicroseconds INSTANCE = new TimeAbacusMicroseconds(); + + private TimeAbacusMicroseconds() + { + } + + public long DeltaForSecondsDouble(double seconds) + { + return (long) Math.Round(1000000d*seconds); + } + + public long DeltaForSecondsNumber(object timeInSeconds) + { + if (timeInSeconds.IsFloatingPointNumber()) + { + return DeltaForSecondsDouble(timeInSeconds.AsDouble()); + } + return 1000000*timeInSeconds.AsLong(); + } + + public long CalendarSet(long fromTime, DateTimeEx dt) + { + long millis = fromTime/1000; + dt.SetUtcMillis(millis); + return fromTime - millis*1000; + } + + public long CalendarGet(DateTimeEx dt, long remainder) + { + return dt.TimeInMillis*1000 + remainder; + } + + public long GetOneSecond() + { + return 1000000; + } + + public DateTimeEx ToDate(long ts) + { + return DateTimeEx.GetInstance(TimeZoneInfo.Utc, ts / 1000); + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/expression/time/TimeAbacusMilliseconds.cs b/NEsper.Core/NEsper.Core/epl/expression/time/TimeAbacusMilliseconds.cs new file mode 100755 index 000000000..ab5394bda --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/time/TimeAbacusMilliseconds.cs @@ -0,0 +1,59 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.expression.time +{ + public class TimeAbacusMilliseconds : TimeAbacus + { + public static readonly TimeAbacusMilliseconds INSTANCE = new TimeAbacusMilliseconds(); + + private TimeAbacusMilliseconds() + { + } + + public long DeltaForSecondsDouble(double seconds) + { + return (long) Math.Round(1000d*seconds); + } + + public long DeltaForSecondsNumber(object timeInSeconds) + { + if (timeInSeconds.IsFloatingPointNumber()) + { + return DeltaForSecondsDouble(timeInSeconds.AsDouble()); + } + return 1000*timeInSeconds.AsLong(); + } + + public long CalendarSet(long fromTime, DateTimeEx dt) + { + dt.SetUtcMillis(fromTime); + return 0; + } + + public long CalendarGet(DateTimeEx dt, long remainder) + { + return dt.TimeInMillis; + } + + public long GetOneSecond() + { + return 1000; + } + + public DateTimeEx ToDate(long ts) + { + return DateTimeEx.GetInstance(TimeZoneInfo.Utc, ts); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeContextPropertiesVisitor.cs b/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeContextPropertiesVisitor.cs new file mode 100755 index 000000000..2340a2e57 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeContextPropertiesVisitor.cs @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.visitor +{ + /// + /// Visitor that early-exists when it finds a context partition property. + /// + public class ExprNodeContextPropertiesVisitor : ExprNodeVisitor + { + public bool IsFound { get; private set; } + + public bool IsVisit(ExprNode exprNode) + { + return !IsFound; + } + + public void Visit(ExprNode exprNode) + { + if (!(exprNode is ExprContextPropertyNode)) + { + return; + } + IsFound = true; + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeDeclaredVisitor.cs b/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeDeclaredVisitor.cs new file mode 100755 index 000000000..89f8f2d1f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeDeclaredVisitor.cs @@ -0,0 +1,59 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.epl.declexpr; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.visitor +{ + /// + /// Visitor that collects instances. + /// + public class ExprNodeDeclaredVisitor : ExprNodeVisitor + { + private readonly IList _declaredExpressions; + + /// + /// Ctor. + /// + public ExprNodeDeclaredVisitor() + { + _declaredExpressions = new List(1); + } + + public IList DeclaredExpressions + { + get { return _declaredExpressions; } + } + + public void Reset() + { + _declaredExpressions.Clear(); + } + + public bool IsVisit(ExprNode exprNode) + { + return true; + } + + public void Visit(ExprNode exprNode) + { + if (exprNode is ExprDeclaredNode) + { + _declaredExpressions.Add((ExprDeclaredNode) exprNode); + } + } + + public void Clear() + { + _declaredExpressions.Clear(); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeGroupingVisitorWParent.cs b/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeGroupingVisitorWParent.cs new file mode 100755 index 000000000..41b05c963 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeGroupingVisitorWParent.cs @@ -0,0 +1,49 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.collection; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.visitor +{ + [Serializable] + public class ExprNodeGroupingVisitorWParent : ExprNodeVisitorWithParent + { + /// Ctor. + public ExprNodeGroupingVisitorWParent() + { + GroupingIdNodes = new List>(2); + GroupingNodes = new List>(2); + } + + public bool IsVisit(ExprNode exprNode) + { + return true; + } + + public IList> GroupingIdNodes { get; private set; } + + public IList> GroupingNodes { get; private set; } + + public void Visit(ExprNode exprNode, ExprNode parentExprNode) + { + if (exprNode is ExprGroupingIdNode) + { + GroupingIdNodes.Add( + new Pair(parentExprNode, (ExprGroupingIdNode) exprNode)); + } + if (exprNode is ExprGroupingNode) + { + GroupingNodes.Add(new Pair(parentExprNode, (ExprGroupingNode) exprNode)); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeIdentVisitorWParent.cs b/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeIdentVisitorWParent.cs new file mode 100755 index 000000000..9f5874c95 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeIdentVisitorWParent.cs @@ -0,0 +1,43 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.collection; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.visitor +{ + /// + /// Visitor for getting a list of identifier nodes with their parent node, which can be null if there is no parent node. + /// + [Serializable] + public class ExprNodeIdentVisitorWParent : ExprNodeVisitorWithParent + { + private readonly IList> _identNodes = new List>(); + + public bool IsVisit(ExprNode exprNode) + { + return true; + } + + public void Visit(ExprNode exprNode, ExprNode parentExprNode) + { + if (exprNode is ExprIdentNode) + { + _identNodes.Add(new Pair(parentExprNode, (ExprIdentNode) exprNode)); + } + } + + public IList> IdentNodes + { + get { return _identNodes; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeIdentifierAndStreamRefVisitor.cs b/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeIdentifierAndStreamRefVisitor.cs new file mode 100755 index 000000000..27de827ad --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeIdentifierAndStreamRefVisitor.cs @@ -0,0 +1,73 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.enummethod.dot; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.visitor +{ + public class ExprNodeIdentifierAndStreamRefVisitor : ExprNodeVisitor + { + private readonly bool _isVisitAggregateNodes; + private List _refs; + + public ExprNodeIdentifierAndStreamRefVisitor(bool isVisitAggregateNodes) { + _isVisitAggregateNodes = isVisitAggregateNodes; + } + + public bool IsVisit(ExprNode exprNode) { + if (exprNode is ExprLambdaGoesNode) { + return false; + } + if (_isVisitAggregateNodes) { + return true; + } + return !(exprNode is ExprAggregateNode); + } + + public IList GetRefs() { + if (_refs == null) { + return Collections.GetEmptyList(); + } + return _refs; + } + + public void Visit(ExprNode exprNode) { + if (exprNode is ExprIdentNode) { + var identNode = (ExprIdentNode) exprNode; + var streamId = identNode.StreamId; + var propertyName = identNode.ResolvedPropertyName; + CheckAllocatedRefs(); + _refs.Add(new ExprNodePropOrStreamPropDesc(streamId, propertyName)); + } else if (exprNode is ExprStreamRefNode) { + var streamRefNode = (ExprStreamRefNode) exprNode; + var stream = streamRefNode.StreamReferencedIfAny; + CheckAllocatedRefs(); + if (stream != null) { + _refs.Add(new ExprNodePropOrStreamExprDesc(stream.Value, streamRefNode)); + } + } + } + + public void Reset() { + if (_refs != null) { + _refs.Clear(); + } + } + + private void CheckAllocatedRefs() { + if (_refs == null) { + _refs = new List(4); + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeIdentifierCollectVisitor.cs b/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeIdentifierCollectVisitor.cs new file mode 100755 index 000000000..78857e2c1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeIdentifierCollectVisitor.cs @@ -0,0 +1,61 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.visitor +{ + /// + /// Visitor that collects event property identifier information under expression nodes. + /// + public class ExprNodeIdentifierCollectVisitor : ExprNodeVisitor + { + /// + /// Ctor. + /// + public ExprNodeIdentifierCollectVisitor() + { + ExprProperties = new List(); + } + + public bool IsVisit(ExprNode exprNode) + { + return true; + } + + /// Returns list of event property stream numbers and names that uniquely identify which property is from whcih stream, and the name of each. + /// list of event property statement-unique INFO + public IList ExprProperties { get; private set; } + + public ICollection StreamsRequired + { + get + { + ICollection streams = new HashSet(); + foreach (ExprIdentNode node in ExprProperties) + { + streams.Add(node.StreamId); + } + return streams; + } + } + + public void Visit(ExprNode exprNode) + { + var identNode = exprNode as ExprIdentNode; + if (identNode == null) + { + return; + } + + ExprProperties.Add(identNode); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeIdentifierCollectVisitorWContainer.cs b/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeIdentifierCollectVisitorWContainer.cs new file mode 100755 index 000000000..54bb993e8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeIdentifierCollectVisitorWContainer.cs @@ -0,0 +1,54 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.collection; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.visitor +{ + /// + /// Visitor that collects event property identifier information under expression nodes. + /// + [Serializable] + public class ExprNodeIdentifierCollectVisitorWContainer : ExprNodeVisitorWithParent + { + private readonly IList> _exprProperties; + + /// Ctor. + public ExprNodeIdentifierCollectVisitorWContainer() + { + _exprProperties = new List>(2); + } + + public bool IsVisit(ExprNode exprNode) + { + return true; + } + + /// + /// Returns list of event property stream numbers and names that uniquely identify which property is from whcih stream, and the name of each. + /// + /// list of event property statement-unique INFO + public IList> ExprProperties + { + get { return _exprProperties; } + } + + public void Visit(ExprNode exprNode, ExprNode containerExprNode) + { + var identNode = exprNode as ExprIdentNode; + if (identNode != null) + { + _exprProperties.Add(new Pair(containerExprNode, identNode)); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeIdentifierVisitor.cs b/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeIdentifierVisitor.cs new file mode 100755 index 000000000..cde0f9a53 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeIdentifierVisitor.cs @@ -0,0 +1,81 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.collection; +using com.espertech.esper.epl.enummethod.dot; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.visitor +{ + /// + /// Visitor that collects event property identifier information under expression nodes. + /// The visitor can be configued to not visit aggregation nodes thus ignoring + /// properties under aggregation nodes such as sum, avg, min/max etc. + /// + public class ExprNodeIdentifierVisitor : ExprNodeVisitor + { + private readonly IList> _exprProperties; + private readonly bool _isVisitAggregateNodes; + + /// + /// Ctor. + /// + /// + /// true to indicate that the visitor should visit aggregate nodes, or false + /// if the visitor ignores aggregate nodes + /// + public ExprNodeIdentifierVisitor(bool visitAggregateNodes) + { + _isVisitAggregateNodes = visitAggregateNodes; + _exprProperties = new List>(); + } + + public bool IsVisit(ExprNode exprNode) + { + if (exprNode is ExprLambdaGoesNode) + { + return false; + } + + if (_isVisitAggregateNodes) + { + return true; + } + + return !(exprNode is ExprAggregateNode); + } + + /// + /// Returns list of event property stream numbers and names that uniquely identify which + /// property is from whcih stream, and the name of each. + /// + /// list of event property statement-unique INFO + public IList> ExprProperties + { + get { return _exprProperties; } + } + + public void Visit(ExprNode exprNode) + { + if (!(exprNode is ExprIdentNode)) + { + return; + } + + var identNode = (ExprIdentNode) exprNode; + + var streamId = identNode.StreamId; + var propertyName = identNode.ResolvedPropertyName; + + _exprProperties.Add(new Pair(streamId, propertyName)); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodePreviousVisitorWParent.cs b/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodePreviousVisitorWParent.cs new file mode 100755 index 000000000..07bd75ea1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodePreviousVisitorWParent.cs @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.collection; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.prev; + +namespace com.espertech.esper.epl.expression.visitor +{ + /// + /// Visitor for getting a list of "prev" functions. + /// + public class ExprNodePreviousVisitorWParent : ExprNodeVisitorWithParent + { + private IList> _previous; + + public bool IsVisit(ExprNode exprNode) + { + return true; + } + + public void Visit(ExprNode exprNode, ExprNode parentExprNode) + { + if (exprNode is ExprPreviousNode) + { + if (_previous == null) + { + _previous = new List>(); + } + _previous.Add(new Pair(parentExprNode, (ExprPreviousNode) exprNode)); + } + } + + /// Returns the pair of previous nodes and their parent expression. + /// nodes + public IList> Previous + { + get { return _previous; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeStreamRequiredVisitor.cs b/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeStreamRequiredVisitor.cs new file mode 100755 index 000000000..dddbb7412 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeStreamRequiredVisitor.cs @@ -0,0 +1,47 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.visitor +{ + public class ExprNodeStreamRequiredVisitor : ExprNodeVisitor + { + private readonly ISet _streams; + + public ExprNodeStreamRequiredVisitor() + { + _streams = new HashSet(); + } + + public bool IsVisit(ExprNode exprNode) + { + return true; + } + + public void Visit(ExprNode exprNode) + { + if (exprNode is ExprStreamRefNode) + { + var streamRefNode = (ExprStreamRefNode) exprNode; + int? streamRef = streamRefNode.StreamReferencedIfAny; + if (streamRef != null) + { + _streams.Add(streamRef.Value); + } + } + } + + public ISet StreamsRequired + { + get { return _streams; } + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeStreamSelectVisitor.cs b/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeStreamSelectVisitor.cs new file mode 100755 index 000000000..d4964137f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeStreamSelectVisitor.cs @@ -0,0 +1,69 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.enummethod.dot; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.dot; + +namespace com.espertech.esper.epl.expression.visitor +{ + /// + /// Visitor that collects event property identifier information under expression nodes. + /// The visitor can be configued to not visit aggregation nodes thus ignoring + /// properties under aggregation nodes such as sum, avg, min/max etc. + /// + public class ExprNodeStreamSelectVisitor : ExprNodeVisitor { + private readonly bool isVisitAggregateNodes; + private bool hasStreamSelect; + + /// + /// Ctor. + /// + /// + /// true to indicate that the visitor should visit aggregate nodes, or false + /// if the visitor ignores aggregate nodes + /// + public ExprNodeStreamSelectVisitor(bool visitAggregateNodes) { + this.isVisitAggregateNodes = visitAggregateNodes; + } + + public bool IsVisit(ExprNode exprNode) { + if (exprNode is ExprLambdaGoesNode) { + return false; + } + + if (isVisitAggregateNodes) { + return true; + } + + return !(exprNode is ExprAggregateNode); + } + + public bool HasStreamSelect() { + return hasStreamSelect; + } + + public void Visit(ExprNode exprNode) { + if (exprNode is ExprStreamUnderlyingNode) { + hasStreamSelect = true; + } + if (exprNode is ExprDotNode) { + ExprDotNode streamRef = (ExprDotNode) exprNode; + if (streamRef.StreamReferencedIfAny != null) { + hasStreamSelect = true; + } + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeStreamUseCollectVisitor.cs b/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeStreamUseCollectVisitor.cs new file mode 100755 index 000000000..b13dbf4f8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeStreamUseCollectVisitor.cs @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.visitor +{ + public class ExprNodeStreamUseCollectVisitor : ExprNodeVisitor + { + private readonly IList referenced = new List(); + + public bool IsVisit(ExprNode exprNode) + { + return true; + } + + public IList Referenced + { + get { return referenced; } + } + + public void Visit(ExprNode exprNode) + { + if (!(exprNode is ExprStreamRefNode)) + { + return; + } + referenced.Add((ExprStreamRefNode) exprNode); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeSubselectDeclaredDotVisitor.cs b/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeSubselectDeclaredDotVisitor.cs new file mode 100755 index 000000000..4d09bb6e9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeSubselectDeclaredDotVisitor.cs @@ -0,0 +1,73 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.epl.declexpr; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.dot; +using com.espertech.esper.epl.expression.subquery; + +namespace com.espertech.esper.epl.expression.visitor +{ + /// + /// Visitor that collects instances. + /// + public class ExprNodeSubselectDeclaredDotVisitor : ExprNodeVisitor + { + /// Ctor. + public ExprNodeSubselectDeclaredDotVisitor() + { + Subselects = new List(); + ChainedExpressionsDot = new List(); + DeclaredExpressions = new List(); + } + + public void Reset() + { + Subselects.Clear(); + ChainedExpressionsDot.Clear(); + DeclaredExpressions.Clear(); + } + + /// Returns a list of lookup expression nodes. + /// lookup nodes + public IList Subselects { get; private set; } + + public IList ChainedExpressionsDot { get; private set; } + + public IList DeclaredExpressions { get; private set; } + + public bool IsVisit(ExprNode exprNode) + { + return true; + } + + public void Visit(ExprNode exprNode) + { + + if (exprNode is ExprDotNode) + { + ChainedExpressionsDot.Add((ExprDotNode)exprNode); + } + + if (exprNode is ExprDeclaredNode) + { + DeclaredExpressions.Add((ExprDeclaredNode)exprNode); + } + + if (!(exprNode is ExprSubselectNode)) + { + return; + } + + var subselectNode = (ExprSubselectNode)exprNode; + Subselects.Add(subselectNode); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeSubselectDeclaredNoTraverseVisitor.cs b/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeSubselectDeclaredNoTraverseVisitor.cs new file mode 100755 index 000000000..40ebb28aa --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeSubselectDeclaredNoTraverseVisitor.cs @@ -0,0 +1,62 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.epl.declexpr; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.subquery; + +namespace com.espertech.esper.epl.expression.visitor +{ + /// + /// Visitor that collects instances only + /// directly under alias expressions, and declared expressions, stopping at nested declared expressions. + /// + public class ExprNodeSubselectDeclaredNoTraverseVisitor : ExprNodeVisitorWithParent + { + private readonly ExprDeclaredNode _declaration; + private readonly IList _subselects; + + /// Ctor. + /// + public ExprNodeSubselectDeclaredNoTraverseVisitor(ExprDeclaredNode declaration) + { + _declaration = declaration; + _subselects = new List(1); + } + + public void Reset() + { + _subselects.Clear(); + } + + /// + /// Returns a list of lookup expression nodes. + /// + /// lookup nodes + public IList Subselects + { + get { return _subselects; } + } + + public bool IsVisit(ExprNode exprNode) + { + return exprNode != _declaration && !(exprNode is ExprDeclaredNode); + } + + public void Visit(ExprNode exprNode, ExprNode parentExprNode) + { + var subselectNode = exprNode as ExprSubselectNode; + if (subselectNode != null) + { + _subselects.Add(subselectNode); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeSummaryVisitor.cs b/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeSummaryVisitor.cs new file mode 100755 index 000000000..5ccb4de54 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeSummaryVisitor.cs @@ -0,0 +1,100 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.prev; +using com.espertech.esper.epl.expression.prior; +using com.espertech.esper.epl.expression.subquery; + +namespace com.espertech.esper.epl.expression.visitor +{ + /// + /// Visitor for compiling usage informaton of special expressions within an expression tree. + /// + public class ExprNodeSummaryVisitor : ExprNodeVisitor + { + /// + /// Returns true if the expression is a plain-value expression, without any of the following: + /// properties, aggregation, subselect, stream select, previous or prior + /// + /// true for plain + public bool IsPlain + { + get { return !(HasProperties | HasAggregation | HasSubselect | HasStreamSelect | HasPreviousPrior); } + } + + public bool HasProperties { get; private set; } + + public bool HasAggregation { get; private set; } + + public bool HasSubselect { get; private set; } + + public bool HasStreamSelect { get; private set; } + + public bool HasPreviousPrior { get; private set; } + + public bool IsVisit(ExprNode exprNode) + { + return true; + } + + public void Visit(ExprNode exprNode) + { + if (exprNode is ExprIdentNode) + { + HasProperties = true; + } + else if (exprNode is ExprSubselectNode) + { + HasSubselect = true; + } + else if (exprNode is ExprAggregateNode) + { + HasAggregation = true; + } + else if (exprNode is ExprStreamUnderlyingNode) + { + HasStreamSelect = true; + } + else if ((exprNode is ExprPriorNode) || (exprNode is ExprPreviousNode)) + { + HasPreviousPrior = true; + } + } + + /// + /// Returns a message if the expression contains special-instruction expressions. + /// + /// message + public string GetMessage() + { + if (HasProperties) + { + return "event properties"; + } + else if (HasAggregation) + { + return "aggregation functions"; + } + else if (HasSubselect) + { + return "sub-selects"; + } + else if (HasStreamSelect) + { + return "stream selects or event instance methods"; + } + else if (HasPreviousPrior) + { + return "previous or prior functions"; + } + return null; + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeTableAccessFinderVisitor.cs b/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeTableAccessFinderVisitor.cs new file mode 100755 index 000000000..2c04f63bb --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeTableAccessFinderVisitor.cs @@ -0,0 +1,36 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.table; + +namespace com.espertech.esper.epl.expression.visitor +{ + public class ExprNodeTableAccessFinderVisitor : ExprNodeVisitor + { + private bool _hasTableAccess; + + public bool HasTableAccess + { + get { return _hasTableAccess; } + } + + public bool IsVisit(ExprNode exprNode) + { + return !_hasTableAccess; + } + + public void Visit(ExprNode exprNode) + { + if (exprNode is ExprTableAccessNode) + { + _hasTableAccess = true; + } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeTableAccessVisitor.cs b/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeTableAccessVisitor.cs new file mode 100755 index 000000000..368aa9dc0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeTableAccessVisitor.cs @@ -0,0 +1,39 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.table; + +namespace com.espertech.esper.epl.expression.visitor +{ + public class ExprNodeTableAccessVisitor : ExprNodeVisitor + { + private readonly ISet _nodesToAddTo; + + public ExprNodeTableAccessVisitor(ISet nodesToAddTo) + { + _nodesToAddTo = nodesToAddTo; + } + + public bool IsVisit(ExprNode exprNode) + { + return true; + } + + public void Visit(ExprNode exprNode) + { + var asTableAccessNode = exprNode as ExprTableAccessNode; + if (asTableAccessNode != null) + { + _nodesToAddTo.Add(asTableAccessNode); + } + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeVariableVisitor.cs b/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeVariableVisitor.cs new file mode 100755 index 000000000..d2890a93b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeVariableVisitor.cs @@ -0,0 +1,81 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.dot; +using com.espertech.esper.epl.variable; + +namespace com.espertech.esper.epl.expression.visitor +{ + /// + /// Visitor for expression node trees that determines if the expressions within contain a variable. + /// + public class ExprNodeVariableVisitor : ExprNodeVisitor + { + private readonly VariableService _variableService; + private ISet _variableNames; + + public ExprNodeVariableVisitor(VariableService variableService) + { + _variableService = variableService; + } + + public bool IsVisit(ExprNode exprNode) + { + return true; + } + + /// + /// Returns true if the visitor finds a variable value. + /// + /// true for variable present in expression + public bool HasVariables + { + get { return _variableNames != null && !_variableNames.IsEmpty(); } + } + + public void Visit(ExprNode exprNode) + { + if (exprNode is ExprDotNode) + { + var exprDotNode = (ExprDotNode) exprNode; + var variableName = exprDotNode.IsVariableOpGetName(_variableService); + if (variableName != null) + { + AddVariableName(variableName); + } + } + if (exprNode is ExprVariableNode) + { + var variableNode = (ExprVariableNode) exprNode; + AddVariableName(variableNode.VariableName); + } + } + + /// + /// Returns the set of variable names encoountered. + /// + /// variable names + public ISet VariableNames + { + get { return _variableNames; } + } + + private void AddVariableName(string name) + { + if (_variableNames == null) + { + _variableNames = new HashSet(); + } + _variableNames.Add(name); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeViewResourceVisitor.cs b/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeViewResourceVisitor.cs new file mode 100755 index 000000000..00f4a234f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeViewResourceVisitor.cs @@ -0,0 +1,49 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.prev; +using com.espertech.esper.epl.expression.prior; + +namespace com.espertech.esper.epl.expression.visitor +{ + /// + /// Visitor that collects expression nodes that require view resources. + /// + public class ExprNodeViewResourceVisitor : ExprNodeVisitor + { + /// + /// Ctor. + /// + public ExprNodeViewResourceVisitor() + { + ExprNodes = new List(); + } + + public bool IsVisit(ExprNode exprNode) + { + return true; + } + + /// + /// Returns the list of expression nodes requiring view resources. + /// + /// expr nodes such as 'prior' or 'prev' + public IList ExprNodes { get; private set; } + + public void Visit(ExprNode exprNode) + { + if (exprNode is ExprPreviousNode || exprNode is ExprPriorNode) + { + ExprNodes.Add(exprNode); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeVisitor.cs b/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeVisitor.cs new file mode 100755 index 000000000..2ea00fec4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeVisitor.cs @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.visitor +{ + /// + /// Visitor interface for use with expression node trees. + /// + + public interface ExprNodeVisitor + { + /// Allows visitor to indicate whether to visit a given node. + /// Implicitly if a visitor doesn't visit a node it would also not visit any descendent child nodes of that node. + /// + /// is the node in questions + /// + /// true if the visitor wants to visit the child node (next call is visit), or false to skip child + /// + bool IsVisit(ExprNode exprNode); + + /// Visit the given expression node. + /// is the expression node to visit + /// + void Visit(ExprNode exprNode); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeVisitorWithParent.cs b/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeVisitorWithParent.cs new file mode 100755 index 000000000..ce3f7cc40 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/expression/visitor/ExprNodeVisitorWithParent.cs @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.expression.visitor +{ + /// + /// Visitor interface for use with expression node trees, receives both the child + /// node and the parent node (or null to indicate no parent node). + /// + public interface ExprNodeVisitorWithParent + { + /// + /// Allows visitor to indicate whether to visit a given node. Implicitly if a + /// visitor doesn't visit a node it would also not visit any descendent child nodes of that + /// node. + /// + /// is the node in questions + /// + /// true if the visitor wants to visit the child node (next call is visit), or false + /// to skip child + /// + bool IsVisit(ExprNode exprNode); + + /// + /// Visit the given expression node. + /// + /// is the expression node to visit + /// parent to visit + void Visit(ExprNode exprNode, ExprNode parentExprNode); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/fafquery/FireAndForgetQueryExec.cs b/NEsper.Core/NEsper.Core/epl/fafquery/FireAndForgetQueryExec.cs new file mode 100755 index 000000000..088fd9818 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/fafquery/FireAndForgetQueryExec.cs @@ -0,0 +1,329 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.@join.exec.@base; +using com.espertech.esper.epl.join.exec.composite; +using com.espertech.esper.epl.join.hint; +using com.espertech.esper.epl.join.plan; +using com.espertech.esper.epl.join.table; +using com.espertech.esper.epl.join.util; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.epl.virtualdw; +using com.espertech.esper.filter; + +namespace com.espertech.esper.epl.fafquery +{ + public class FireAndForgetQueryExec + { + public static ICollection Snapshot( + FilterSpecCompiled optionalFilter, + Attribute[] annotations, + VirtualDWView virtualDataWindow, + EventTableIndexRepository indexRepository, + bool queryPlanLogging, + ILog queryPlanLogDestination, + string objectName, + AgentInstanceContext agentInstanceContext) + { + if (optionalFilter == null || optionalFilter.Parameters.Length == 0) + { + if (virtualDataWindow != null) + { + var pair = virtualDataWindow.GetFireAndForgetDesc(Collections.GetEmptySet(), Collections.GetEmptySet()); + return virtualDataWindow.GetFireAndForgetData(pair.Second, new object[0], new RangeIndexLookupValue[0], annotations); + } + return null; + } + + // Determine what straight-equals keys and which ranges are available. + // Widening/Coercion is part of filter spec compile. + ISet keysAvailable = new HashSet(); + ISet rangesAvailable = new HashSet(); + if (optionalFilter.Parameters.Length == 1) + { + foreach (FilterSpecParam param in optionalFilter.Parameters[0]) + { + if (!(param is FilterSpecParamConstant || + param is FilterSpecParamRange || + param is FilterSpecParamIn)) + { + continue; + } + + if (param.FilterOperator == FilterOperator.EQUAL || + param.FilterOperator == FilterOperator.IS || + param.FilterOperator == FilterOperator.IN_LIST_OF_VALUES) + { + keysAvailable.Add(param.Lookupable.Expression); + } + else if (param.FilterOperator.IsRangeOperator() || + param.FilterOperator.IsInvertedRangeOperator() || + param.FilterOperator.IsComparisonOperator()) + { + rangesAvailable.Add(param.Lookupable.Expression); + } + else if (param.FilterOperator.IsRangeOperator()) + { + rangesAvailable.Add(param.Lookupable.Expression); + } + } + } + + // Find an index that matches the needs + Pair tablePair; + if (virtualDataWindow != null) + { + var tablePairNoName = virtualDataWindow.GetFireAndForgetDesc(keysAvailable, rangesAvailable); + tablePair = new Pair(tablePairNoName.First, new EventTableAndNamePair(tablePairNoName.Second, null)); + } + else + { + var indexHint = IndexHint.GetIndexHint(annotations); + IList optionalIndexHintInstructions = null; + if (indexHint != null) + { + optionalIndexHintInstructions = indexHint.InstructionsFireAndForget; + } + tablePair = indexRepository.FindTable(keysAvailable, rangesAvailable, optionalIndexHintInstructions); + } + + var hook = QueryPlanIndexHookUtil.GetHook(annotations, agentInstanceContext.StatementContext.EngineImportService); + if (queryPlanLogging && (queryPlanLogDestination.IsInfoEnabled || hook != null)) + { + var prefix = "Fire-and-forget from " + objectName + " "; + var indexName = tablePair != null && tablePair.Second != null ? tablePair.Second.IndexName : null; + var indexText = indexName != null ? "index " + indexName + " " : "full table scan "; + indexText += "(snapshot only, for join see separate query plan)"; + if (tablePair == null) + { + queryPlanLogDestination.Info(prefix + indexText); + } + else + { + queryPlanLogDestination.Info(prefix + indexText + tablePair.Second.EventTable.ToQueryPlan()); + } + + if (hook != null) + { + hook.FireAndForget(new QueryPlanIndexDescFAF( + new IndexNameAndDescPair[] { + new IndexNameAndDescPair(indexName, tablePair != null ? + tablePair.Second.EventTable.ProviderClass.Name : null) + })); + } + } + + if (tablePair == null) + { + return null; // indicates table scan + } + + // Compile key sets which contain key index lookup values + var keyIndexProps = IndexedPropDesc.GetIndexProperties(tablePair.First.HashIndexedProps); + var hasKeyWithInClause = false; + var keyValues = new object[keyIndexProps.Length]; + for (var keyIndex = 0; keyIndex < keyIndexProps.Length; keyIndex++) + { + foreach (var param in optionalFilter.Parameters[0]) + { + if (param.Lookupable.Expression.Equals(keyIndexProps[keyIndex])) + { + if (param.FilterOperator == FilterOperator.IN_LIST_OF_VALUES) + { + var keyValuesList = ((MultiKeyUntyped)param.GetFilterValue(null, agentInstanceContext)).Keys; + if (keyValuesList.Length == 0) + { + continue; + } + else if (keyValuesList.Length == 1) + { + keyValues[keyIndex] = keyValuesList[0]; + } + else + { + keyValues[keyIndex] = keyValuesList; + hasKeyWithInClause = true; + } + } + else + { + keyValues[keyIndex] = param.GetFilterValue(null, agentInstanceContext); + } + break; + } + } + } + + // Analyze ranges - these may include key lookup value (EQUALS semantics) + var rangeIndexProps = IndexedPropDesc.GetIndexProperties(tablePair.First.RangeIndexedProps); + RangeIndexLookupValue[] rangeValues; + if (rangeIndexProps.Length > 0) + { + rangeValues = CompileRangeLookupValues(rangeIndexProps, optionalFilter.Parameters[0], agentInstanceContext); + } + else + { + rangeValues = new RangeIndexLookupValue[0]; + } + + var eventTable = tablePair.Second.EventTable; + var indexMultiKey = tablePair.First; + + // table lookup without in-clause + if (!hasKeyWithInClause) + { + return FafTableLookup(virtualDataWindow, indexMultiKey, eventTable, keyValues, rangeValues, annotations); + } + + // table lookup with in-clause: determine combinations + var combinations = new object[keyIndexProps.Length][]; + for (var i = 0; i < keyValues.Length; i++) + { + if (keyValues[i] is object[]) + { + combinations[i] = (object[])keyValues[i]; + } + else + { + combinations[i] = new object[] { keyValues[i] }; + } + } + + // enumerate combinations + var enumeration = new CombinationEnumeration(combinations); + var events = new HashSet(); + for (; enumeration.MoveNext(); ) + { + object[] keys = enumeration.Current; + var result = FafTableLookup(virtualDataWindow, indexMultiKey, eventTable, keys, rangeValues, annotations); + events.AddAll(result); + } + return events; + } + + private static ICollection FafTableLookup(VirtualDWView virtualDataWindow, IndexMultiKey indexMultiKey, EventTable eventTable, object[] keyValues, RangeIndexLookupValue[] rangeValues, Attribute[] annotations) + { + if (virtualDataWindow != null) + { + return virtualDataWindow.GetFireAndForgetData(eventTable, keyValues, rangeValues, annotations); + } + + ISet result; + if (indexMultiKey.HashIndexedProps.Length > 0 && indexMultiKey.RangeIndexedProps.Length == 0) + { + if (indexMultiKey.HashIndexedProps.Length == 1) + { + var table = (PropertyIndexedEventTableSingle)eventTable; + result = table.Lookup(keyValues[0]); + } + else + { + var table = (PropertyIndexedEventTable)eventTable; + result = table.Lookup(keyValues); + } + } + else if (indexMultiKey.HashIndexedProps.Length == 0 && indexMultiKey.RangeIndexedProps.Length == 1) + { + var table = (PropertySortedEventTable)eventTable; + result = table.LookupConstants(rangeValues[0]); + } + else + { + var table = (PropertyCompositeEventTable)eventTable; + var rangeCoercion = table.OptRangeCoercedTypes; + var lookup = CompositeIndexLookupFactory.Make(keyValues, rangeValues, rangeCoercion); + result = new HashSet(); + lookup.Lookup(table.IndexTable, result, table.PostProcessor); + } + if (result != null) + { + return result; + } + return Collections.GetEmptyList(); + } + + private static RangeIndexLookupValue[] CompileRangeLookupValues(string[] rangeIndexProps, FilterSpecParam[] parameters, AgentInstanceContext agentInstanceContext) + { + var result = new RangeIndexLookupValue[rangeIndexProps.Length]; + + for (var rangeIndex = 0; rangeIndex < rangeIndexProps.Length; rangeIndex++) + { + foreach (var param in parameters) + { + if (!(param.Lookupable.Expression.Equals(rangeIndexProps[rangeIndex]))) + { + continue; + } + + if (param.FilterOperator == FilterOperator.EQUAL || param.FilterOperator == FilterOperator.IS) + { + result[rangeIndex] = new RangeIndexLookupValueEquals(param.GetFilterValue(null, agentInstanceContext)); + } + else if (param.FilterOperator.IsRangeOperator() || param.FilterOperator.IsInvertedRangeOperator()) + { + QueryGraphRangeEnum opAdd = param.FilterOperator.MapFrom(); + result[rangeIndex] = new RangeIndexLookupValueRange(param.GetFilterValue(null, agentInstanceContext), opAdd, true); + } + else if (param.FilterOperator.IsComparisonOperator()) + { + + var existing = result[rangeIndex]; + QueryGraphRangeEnum opAdd = param.FilterOperator.MapFrom(); + if (existing == null) + { + result[rangeIndex] = new RangeIndexLookupValueRange(param.GetFilterValue(null, agentInstanceContext), opAdd, true); + } + else + { + if (!(existing is RangeIndexLookupValueRange)) + { + continue; + } + var existingRange = (RangeIndexLookupValueRange)existing; + var opExist = existingRange.Operator; + var desc = QueryGraphRangeUtil.GetCanConsolidate(opExist, opAdd); + if (desc != null) + { + var doubleRange = GetDoubleRange(desc.IsReverse, existing.Value, param.GetFilterValue(null, agentInstanceContext)); + result[rangeIndex] = new RangeIndexLookupValueRange(doubleRange, desc.RangeType, false); + } + } + } + } + } + return result; + } + + private static DoubleRange GetDoubleRange(bool reverse, object start, object end) + { + if (start == null || end == null) + { + return null; + } + double startDbl = start.AsDouble(); + double endDbl = end.AsDouble(); + if (reverse) + { + return new DoubleRange(startDbl, endDbl); + } + else + { + return new DoubleRange(endDbl, startDbl); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/generated/EsperEPL2GrammarBaseListener.cs b/NEsper.Core/NEsper.Core/epl/generated/EsperEPL2GrammarBaseListener.cs new file mode 100755 index 000000000..2ef5642a6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/generated/EsperEPL2GrammarBaseListener.cs @@ -0,0 +1,3350 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// ANTLR Version: 4.6 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +// Generated from C:\Src\Espertech\NEsper-master\NEsper\grammar\EsperEPL2Grammar.g4 by ANTLR 4.6 + +// Unreachable code detected +#pragma warning disable 0162 +// The variable '...' is assigned but its value is never used +#pragma warning disable 0219 +// Missing XML comment for publicly visible type or member '...' +#pragma warning disable 1591 +// Ambiguous reference in cref attribute +#pragma warning disable 419 + +namespace com.espertech.esper.epl.generated { + + using System; + using System.Collections.Generic; + + +using Antlr4.Runtime.Misc; +using IErrorNode = Antlr4.Runtime.Tree.IErrorNode; +using ITerminalNode = Antlr4.Runtime.Tree.ITerminalNode; +using IToken = Antlr4.Runtime.IToken; +using ParserRuleContext = Antlr4.Runtime.ParserRuleContext; + +/// +/// This class provides an empty implementation of , +/// which can be extended to create a listener which only needs to handle a subset +/// of the available methods. +/// +[System.CodeDom.Compiler.GeneratedCode("ANTLR", "4.6")] +[System.CLSCompliant(false)] +public partial class EsperEPL2GrammarBaseListener : IEsperEPL2GrammarListener { + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterStartPatternExpressionRule([NotNull] EsperEPL2GrammarParser.StartPatternExpressionRuleContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitStartPatternExpressionRule([NotNull] EsperEPL2GrammarParser.StartPatternExpressionRuleContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterStartEPLExpressionRule([NotNull] EsperEPL2GrammarParser.StartEPLExpressionRuleContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitStartEPLExpressionRule([NotNull] EsperEPL2GrammarParser.StartEPLExpressionRuleContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterStartEventPropertyRule([NotNull] EsperEPL2GrammarParser.StartEventPropertyRuleContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitStartEventPropertyRule([NotNull] EsperEPL2GrammarParser.StartEventPropertyRuleContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterStartJsonValueRule([NotNull] EsperEPL2GrammarParser.StartJsonValueRuleContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitStartJsonValueRule([NotNull] EsperEPL2GrammarParser.StartJsonValueRuleContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterExpressionDecl([NotNull] EsperEPL2GrammarParser.ExpressionDeclContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitExpressionDecl([NotNull] EsperEPL2GrammarParser.ExpressionDeclContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterExpressionDialect([NotNull] EsperEPL2GrammarParser.ExpressionDialectContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitExpressionDialect([NotNull] EsperEPL2GrammarParser.ExpressionDialectContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterExpressionDef([NotNull] EsperEPL2GrammarParser.ExpressionDefContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitExpressionDef([NotNull] EsperEPL2GrammarParser.ExpressionDefContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterExpressionLambdaDecl([NotNull] EsperEPL2GrammarParser.ExpressionLambdaDeclContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitExpressionLambdaDecl([NotNull] EsperEPL2GrammarParser.ExpressionLambdaDeclContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterExpressionTypeAnno([NotNull] EsperEPL2GrammarParser.ExpressionTypeAnnoContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitExpressionTypeAnno([NotNull] EsperEPL2GrammarParser.ExpressionTypeAnnoContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterAnnotationEnum([NotNull] EsperEPL2GrammarParser.AnnotationEnumContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitAnnotationEnum([NotNull] EsperEPL2GrammarParser.AnnotationEnumContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterElementValuePairsEnum([NotNull] EsperEPL2GrammarParser.ElementValuePairsEnumContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitElementValuePairsEnum([NotNull] EsperEPL2GrammarParser.ElementValuePairsEnumContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterElementValuePairEnum([NotNull] EsperEPL2GrammarParser.ElementValuePairEnumContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitElementValuePairEnum([NotNull] EsperEPL2GrammarParser.ElementValuePairEnumContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterElementValueEnum([NotNull] EsperEPL2GrammarParser.ElementValueEnumContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitElementValueEnum([NotNull] EsperEPL2GrammarParser.ElementValueEnumContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterElementValueArrayEnum([NotNull] EsperEPL2GrammarParser.ElementValueArrayEnumContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitElementValueArrayEnum([NotNull] EsperEPL2GrammarParser.ElementValueArrayEnumContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterEplExpression([NotNull] EsperEPL2GrammarParser.EplExpressionContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitEplExpression([NotNull] EsperEPL2GrammarParser.EplExpressionContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterContextExpr([NotNull] EsperEPL2GrammarParser.ContextExprContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitContextExpr([NotNull] EsperEPL2GrammarParser.ContextExprContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterSelectExpr([NotNull] EsperEPL2GrammarParser.SelectExprContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitSelectExpr([NotNull] EsperEPL2GrammarParser.SelectExprContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterOnExpr([NotNull] EsperEPL2GrammarParser.OnExprContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitOnExpr([NotNull] EsperEPL2GrammarParser.OnExprContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterOnStreamExpr([NotNull] EsperEPL2GrammarParser.OnStreamExprContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitOnStreamExpr([NotNull] EsperEPL2GrammarParser.OnStreamExprContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterUpdateExpr([NotNull] EsperEPL2GrammarParser.UpdateExprContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitUpdateExpr([NotNull] EsperEPL2GrammarParser.UpdateExprContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterUpdateDetails([NotNull] EsperEPL2GrammarParser.UpdateDetailsContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitUpdateDetails([NotNull] EsperEPL2GrammarParser.UpdateDetailsContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterOnMergeExpr([NotNull] EsperEPL2GrammarParser.OnMergeExprContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitOnMergeExpr([NotNull] EsperEPL2GrammarParser.OnMergeExprContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterMergeItem([NotNull] EsperEPL2GrammarParser.MergeItemContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitMergeItem([NotNull] EsperEPL2GrammarParser.MergeItemContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterMergeMatched([NotNull] EsperEPL2GrammarParser.MergeMatchedContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitMergeMatched([NotNull] EsperEPL2GrammarParser.MergeMatchedContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterMergeMatchedItem([NotNull] EsperEPL2GrammarParser.MergeMatchedItemContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitMergeMatchedItem([NotNull] EsperEPL2GrammarParser.MergeMatchedItemContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterMergeUnmatched([NotNull] EsperEPL2GrammarParser.MergeUnmatchedContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitMergeUnmatched([NotNull] EsperEPL2GrammarParser.MergeUnmatchedContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterMergeUnmatchedItem([NotNull] EsperEPL2GrammarParser.MergeUnmatchedItemContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitMergeUnmatchedItem([NotNull] EsperEPL2GrammarParser.MergeUnmatchedItemContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterMergeInsert([NotNull] EsperEPL2GrammarParser.MergeInsertContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitMergeInsert([NotNull] EsperEPL2GrammarParser.MergeInsertContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterOnSelectExpr([NotNull] EsperEPL2GrammarParser.OnSelectExprContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitOnSelectExpr([NotNull] EsperEPL2GrammarParser.OnSelectExprContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterOnUpdateExpr([NotNull] EsperEPL2GrammarParser.OnUpdateExprContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitOnUpdateExpr([NotNull] EsperEPL2GrammarParser.OnUpdateExprContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterOnSelectInsertExpr([NotNull] EsperEPL2GrammarParser.OnSelectInsertExprContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitOnSelectInsertExpr([NotNull] EsperEPL2GrammarParser.OnSelectInsertExprContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterOnSelectInsertFromClause([NotNull] EsperEPL2GrammarParser.OnSelectInsertFromClauseContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitOnSelectInsertFromClause([NotNull] EsperEPL2GrammarParser.OnSelectInsertFromClauseContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterOutputClauseInsert([NotNull] EsperEPL2GrammarParser.OutputClauseInsertContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitOutputClauseInsert([NotNull] EsperEPL2GrammarParser.OutputClauseInsertContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterOnDeleteExpr([NotNull] EsperEPL2GrammarParser.OnDeleteExprContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitOnDeleteExpr([NotNull] EsperEPL2GrammarParser.OnDeleteExprContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterOnSetExpr([NotNull] EsperEPL2GrammarParser.OnSetExprContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitOnSetExpr([NotNull] EsperEPL2GrammarParser.OnSetExprContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterOnSetAssignmentList([NotNull] EsperEPL2GrammarParser.OnSetAssignmentListContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitOnSetAssignmentList([NotNull] EsperEPL2GrammarParser.OnSetAssignmentListContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterOnSetAssignment([NotNull] EsperEPL2GrammarParser.OnSetAssignmentContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitOnSetAssignment([NotNull] EsperEPL2GrammarParser.OnSetAssignmentContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterOnExprFrom([NotNull] EsperEPL2GrammarParser.OnExprFromContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitOnExprFrom([NotNull] EsperEPL2GrammarParser.OnExprFromContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterCreateWindowExpr([NotNull] EsperEPL2GrammarParser.CreateWindowExprContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitCreateWindowExpr([NotNull] EsperEPL2GrammarParser.CreateWindowExprContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterCreateWindowExprModelAfter([NotNull] EsperEPL2GrammarParser.CreateWindowExprModelAfterContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitCreateWindowExprModelAfter([NotNull] EsperEPL2GrammarParser.CreateWindowExprModelAfterContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterCreateIndexExpr([NotNull] EsperEPL2GrammarParser.CreateIndexExprContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitCreateIndexExpr([NotNull] EsperEPL2GrammarParser.CreateIndexExprContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterCreateIndexColumnList([NotNull] EsperEPL2GrammarParser.CreateIndexColumnListContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitCreateIndexColumnList([NotNull] EsperEPL2GrammarParser.CreateIndexColumnListContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterCreateIndexColumn([NotNull] EsperEPL2GrammarParser.CreateIndexColumnContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitCreateIndexColumn([NotNull] EsperEPL2GrammarParser.CreateIndexColumnContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterCreateVariableExpr([NotNull] EsperEPL2GrammarParser.CreateVariableExprContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitCreateVariableExpr([NotNull] EsperEPL2GrammarParser.CreateVariableExprContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterCreateTableExpr([NotNull] EsperEPL2GrammarParser.CreateTableExprContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitCreateTableExpr([NotNull] EsperEPL2GrammarParser.CreateTableExprContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterCreateTableColumnList([NotNull] EsperEPL2GrammarParser.CreateTableColumnListContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitCreateTableColumnList([NotNull] EsperEPL2GrammarParser.CreateTableColumnListContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterCreateTableColumn([NotNull] EsperEPL2GrammarParser.CreateTableColumnContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitCreateTableColumn([NotNull] EsperEPL2GrammarParser.CreateTableColumnContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterCreateTableColumnPlain([NotNull] EsperEPL2GrammarParser.CreateTableColumnPlainContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitCreateTableColumnPlain([NotNull] EsperEPL2GrammarParser.CreateTableColumnPlainContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterCreateColumnList([NotNull] EsperEPL2GrammarParser.CreateColumnListContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitCreateColumnList([NotNull] EsperEPL2GrammarParser.CreateColumnListContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterCreateColumnListElement([NotNull] EsperEPL2GrammarParser.CreateColumnListElementContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitCreateColumnListElement([NotNull] EsperEPL2GrammarParser.CreateColumnListElementContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterCreateSelectionList([NotNull] EsperEPL2GrammarParser.CreateSelectionListContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitCreateSelectionList([NotNull] EsperEPL2GrammarParser.CreateSelectionListContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterCreateSelectionListElement([NotNull] EsperEPL2GrammarParser.CreateSelectionListElementContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitCreateSelectionListElement([NotNull] EsperEPL2GrammarParser.CreateSelectionListElementContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterCreateSchemaExpr([NotNull] EsperEPL2GrammarParser.CreateSchemaExprContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitCreateSchemaExpr([NotNull] EsperEPL2GrammarParser.CreateSchemaExprContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterCreateSchemaDef([NotNull] EsperEPL2GrammarParser.CreateSchemaDefContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitCreateSchemaDef([NotNull] EsperEPL2GrammarParser.CreateSchemaDefContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterFafDelete([NotNull] EsperEPL2GrammarParser.FafDeleteContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitFafDelete([NotNull] EsperEPL2GrammarParser.FafDeleteContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterFafUpdate([NotNull] EsperEPL2GrammarParser.FafUpdateContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitFafUpdate([NotNull] EsperEPL2GrammarParser.FafUpdateContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterFafInsert([NotNull] EsperEPL2GrammarParser.FafInsertContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitFafInsert([NotNull] EsperEPL2GrammarParser.FafInsertContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterCreateDataflow([NotNull] EsperEPL2GrammarParser.CreateDataflowContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitCreateDataflow([NotNull] EsperEPL2GrammarParser.CreateDataflowContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterGopList([NotNull] EsperEPL2GrammarParser.GopListContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitGopList([NotNull] EsperEPL2GrammarParser.GopListContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterGop([NotNull] EsperEPL2GrammarParser.GopContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitGop([NotNull] EsperEPL2GrammarParser.GopContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterGopParams([NotNull] EsperEPL2GrammarParser.GopParamsContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitGopParams([NotNull] EsperEPL2GrammarParser.GopParamsContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterGopParamsItemList([NotNull] EsperEPL2GrammarParser.GopParamsItemListContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitGopParamsItemList([NotNull] EsperEPL2GrammarParser.GopParamsItemListContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterGopParamsItem([NotNull] EsperEPL2GrammarParser.GopParamsItemContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitGopParamsItem([NotNull] EsperEPL2GrammarParser.GopParamsItemContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterGopParamsItemMany([NotNull] EsperEPL2GrammarParser.GopParamsItemManyContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitGopParamsItemMany([NotNull] EsperEPL2GrammarParser.GopParamsItemManyContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterGopParamsItemAs([NotNull] EsperEPL2GrammarParser.GopParamsItemAsContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitGopParamsItemAs([NotNull] EsperEPL2GrammarParser.GopParamsItemAsContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterGopOut([NotNull] EsperEPL2GrammarParser.GopOutContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitGopOut([NotNull] EsperEPL2GrammarParser.GopOutContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterGopOutItem([NotNull] EsperEPL2GrammarParser.GopOutItemContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitGopOutItem([NotNull] EsperEPL2GrammarParser.GopOutItemContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterGopOutTypeList([NotNull] EsperEPL2GrammarParser.GopOutTypeListContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitGopOutTypeList([NotNull] EsperEPL2GrammarParser.GopOutTypeListContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterGopOutTypeParam([NotNull] EsperEPL2GrammarParser.GopOutTypeParamContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitGopOutTypeParam([NotNull] EsperEPL2GrammarParser.GopOutTypeParamContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterGopOutTypeItem([NotNull] EsperEPL2GrammarParser.GopOutTypeItemContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitGopOutTypeItem([NotNull] EsperEPL2GrammarParser.GopOutTypeItemContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterGopDetail([NotNull] EsperEPL2GrammarParser.GopDetailContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitGopDetail([NotNull] EsperEPL2GrammarParser.GopDetailContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterGopConfig([NotNull] EsperEPL2GrammarParser.GopConfigContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitGopConfig([NotNull] EsperEPL2GrammarParser.GopConfigContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterCreateContextExpr([NotNull] EsperEPL2GrammarParser.CreateContextExprContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitCreateContextExpr([NotNull] EsperEPL2GrammarParser.CreateContextExprContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterCreateExpressionExpr([NotNull] EsperEPL2GrammarParser.CreateExpressionExprContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitCreateExpressionExpr([NotNull] EsperEPL2GrammarParser.CreateExpressionExprContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterCreateContextDetail([NotNull] EsperEPL2GrammarParser.CreateContextDetailContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitCreateContextDetail([NotNull] EsperEPL2GrammarParser.CreateContextDetailContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterContextContextNested([NotNull] EsperEPL2GrammarParser.ContextContextNestedContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitContextContextNested([NotNull] EsperEPL2GrammarParser.ContextContextNestedContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterCreateContextChoice([NotNull] EsperEPL2GrammarParser.CreateContextChoiceContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitCreateContextChoice([NotNull] EsperEPL2GrammarParser.CreateContextChoiceContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterCreateContextDistinct([NotNull] EsperEPL2GrammarParser.CreateContextDistinctContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitCreateContextDistinct([NotNull] EsperEPL2GrammarParser.CreateContextDistinctContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterCreateContextRangePoint([NotNull] EsperEPL2GrammarParser.CreateContextRangePointContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitCreateContextRangePoint([NotNull] EsperEPL2GrammarParser.CreateContextRangePointContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterCreateContextFilter([NotNull] EsperEPL2GrammarParser.CreateContextFilterContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitCreateContextFilter([NotNull] EsperEPL2GrammarParser.CreateContextFilterContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterCreateContextPartitionItem([NotNull] EsperEPL2GrammarParser.CreateContextPartitionItemContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitCreateContextPartitionItem([NotNull] EsperEPL2GrammarParser.CreateContextPartitionItemContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterCreateContextCoalesceItem([NotNull] EsperEPL2GrammarParser.CreateContextCoalesceItemContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitCreateContextCoalesceItem([NotNull] EsperEPL2GrammarParser.CreateContextCoalesceItemContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterCreateContextGroupItem([NotNull] EsperEPL2GrammarParser.CreateContextGroupItemContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitCreateContextGroupItem([NotNull] EsperEPL2GrammarParser.CreateContextGroupItemContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterCreateSchemaQual([NotNull] EsperEPL2GrammarParser.CreateSchemaQualContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitCreateSchemaQual([NotNull] EsperEPL2GrammarParser.CreateSchemaQualContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterVariantList([NotNull] EsperEPL2GrammarParser.VariantListContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitVariantList([NotNull] EsperEPL2GrammarParser.VariantListContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterVariantListElement([NotNull] EsperEPL2GrammarParser.VariantListElementContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitVariantListElement([NotNull] EsperEPL2GrammarParser.VariantListElementContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterIntoTableExpr([NotNull] EsperEPL2GrammarParser.IntoTableExprContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitIntoTableExpr([NotNull] EsperEPL2GrammarParser.IntoTableExprContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterInsertIntoExpr([NotNull] EsperEPL2GrammarParser.InsertIntoExprContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitInsertIntoExpr([NotNull] EsperEPL2GrammarParser.InsertIntoExprContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterColumnList([NotNull] EsperEPL2GrammarParser.ColumnListContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitColumnList([NotNull] EsperEPL2GrammarParser.ColumnListContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterFromClause([NotNull] EsperEPL2GrammarParser.FromClauseContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitFromClause([NotNull] EsperEPL2GrammarParser.FromClauseContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterRegularJoin([NotNull] EsperEPL2GrammarParser.RegularJoinContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitRegularJoin([NotNull] EsperEPL2GrammarParser.RegularJoinContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterOuterJoinList([NotNull] EsperEPL2GrammarParser.OuterJoinListContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitOuterJoinList([NotNull] EsperEPL2GrammarParser.OuterJoinListContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterOuterJoin([NotNull] EsperEPL2GrammarParser.OuterJoinContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitOuterJoin([NotNull] EsperEPL2GrammarParser.OuterJoinContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterOuterJoinIdent([NotNull] EsperEPL2GrammarParser.OuterJoinIdentContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitOuterJoinIdent([NotNull] EsperEPL2GrammarParser.OuterJoinIdentContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterOuterJoinIdentPair([NotNull] EsperEPL2GrammarParser.OuterJoinIdentPairContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitOuterJoinIdentPair([NotNull] EsperEPL2GrammarParser.OuterJoinIdentPairContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterWhereClause([NotNull] EsperEPL2GrammarParser.WhereClauseContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitWhereClause([NotNull] EsperEPL2GrammarParser.WhereClauseContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterSelectClause([NotNull] EsperEPL2GrammarParser.SelectClauseContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitSelectClause([NotNull] EsperEPL2GrammarParser.SelectClauseContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterSelectionList([NotNull] EsperEPL2GrammarParser.SelectionListContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitSelectionList([NotNull] EsperEPL2GrammarParser.SelectionListContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterSelectionListElement([NotNull] EsperEPL2GrammarParser.SelectionListElementContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitSelectionListElement([NotNull] EsperEPL2GrammarParser.SelectionListElementContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterSelectionListElementExpr([NotNull] EsperEPL2GrammarParser.SelectionListElementExprContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitSelectionListElementExpr([NotNull] EsperEPL2GrammarParser.SelectionListElementExprContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterSelectionListElementAnno([NotNull] EsperEPL2GrammarParser.SelectionListElementAnnoContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitSelectionListElementAnno([NotNull] EsperEPL2GrammarParser.SelectionListElementAnnoContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterStreamSelector([NotNull] EsperEPL2GrammarParser.StreamSelectorContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitStreamSelector([NotNull] EsperEPL2GrammarParser.StreamSelectorContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterStreamExpression([NotNull] EsperEPL2GrammarParser.StreamExpressionContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitStreamExpression([NotNull] EsperEPL2GrammarParser.StreamExpressionContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterForExpr([NotNull] EsperEPL2GrammarParser.ForExprContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitForExpr([NotNull] EsperEPL2GrammarParser.ForExprContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterPatternInclusionExpression([NotNull] EsperEPL2GrammarParser.PatternInclusionExpressionContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitPatternInclusionExpression([NotNull] EsperEPL2GrammarParser.PatternInclusionExpressionContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterDatabaseJoinExpression([NotNull] EsperEPL2GrammarParser.DatabaseJoinExpressionContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitDatabaseJoinExpression([NotNull] EsperEPL2GrammarParser.DatabaseJoinExpressionContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterMethodJoinExpression([NotNull] EsperEPL2GrammarParser.MethodJoinExpressionContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitMethodJoinExpression([NotNull] EsperEPL2GrammarParser.MethodJoinExpressionContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterViewExpressions([NotNull] EsperEPL2GrammarParser.ViewExpressionsContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitViewExpressions([NotNull] EsperEPL2GrammarParser.ViewExpressionsContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterViewExpressionWNamespace([NotNull] EsperEPL2GrammarParser.ViewExpressionWNamespaceContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitViewExpressionWNamespace([NotNull] EsperEPL2GrammarParser.ViewExpressionWNamespaceContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterViewExpressionOptNamespace([NotNull] EsperEPL2GrammarParser.ViewExpressionOptNamespaceContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitViewExpressionOptNamespace([NotNull] EsperEPL2GrammarParser.ViewExpressionOptNamespaceContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterViewWParameters([NotNull] EsperEPL2GrammarParser.ViewWParametersContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitViewWParameters([NotNull] EsperEPL2GrammarParser.ViewWParametersContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterGroupByListExpr([NotNull] EsperEPL2GrammarParser.GroupByListExprContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitGroupByListExpr([NotNull] EsperEPL2GrammarParser.GroupByListExprContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterGroupByListChoice([NotNull] EsperEPL2GrammarParser.GroupByListChoiceContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitGroupByListChoice([NotNull] EsperEPL2GrammarParser.GroupByListChoiceContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterGroupByCubeOrRollup([NotNull] EsperEPL2GrammarParser.GroupByCubeOrRollupContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitGroupByCubeOrRollup([NotNull] EsperEPL2GrammarParser.GroupByCubeOrRollupContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterGroupByGroupingSets([NotNull] EsperEPL2GrammarParser.GroupByGroupingSetsContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitGroupByGroupingSets([NotNull] EsperEPL2GrammarParser.GroupByGroupingSetsContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterGroupBySetsChoice([NotNull] EsperEPL2GrammarParser.GroupBySetsChoiceContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitGroupBySetsChoice([NotNull] EsperEPL2GrammarParser.GroupBySetsChoiceContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterGroupByCombinableExpr([NotNull] EsperEPL2GrammarParser.GroupByCombinableExprContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitGroupByCombinableExpr([NotNull] EsperEPL2GrammarParser.GroupByCombinableExprContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterOrderByListExpr([NotNull] EsperEPL2GrammarParser.OrderByListExprContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitOrderByListExpr([NotNull] EsperEPL2GrammarParser.OrderByListExprContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterOrderByListElement([NotNull] EsperEPL2GrammarParser.OrderByListElementContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitOrderByListElement([NotNull] EsperEPL2GrammarParser.OrderByListElementContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterHavingClause([NotNull] EsperEPL2GrammarParser.HavingClauseContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitHavingClause([NotNull] EsperEPL2GrammarParser.HavingClauseContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterOutputLimit([NotNull] EsperEPL2GrammarParser.OutputLimitContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitOutputLimit([NotNull] EsperEPL2GrammarParser.OutputLimitContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterOutputLimitAndTerm([NotNull] EsperEPL2GrammarParser.OutputLimitAndTermContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitOutputLimitAndTerm([NotNull] EsperEPL2GrammarParser.OutputLimitAndTermContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterOutputLimitAfter([NotNull] EsperEPL2GrammarParser.OutputLimitAfterContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitOutputLimitAfter([NotNull] EsperEPL2GrammarParser.OutputLimitAfterContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterRowLimit([NotNull] EsperEPL2GrammarParser.RowLimitContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitRowLimit([NotNull] EsperEPL2GrammarParser.RowLimitContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterCrontabLimitParameterSet([NotNull] EsperEPL2GrammarParser.CrontabLimitParameterSetContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitCrontabLimitParameterSet([NotNull] EsperEPL2GrammarParser.CrontabLimitParameterSetContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterWhenClause([NotNull] EsperEPL2GrammarParser.WhenClauseContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitWhenClause([NotNull] EsperEPL2GrammarParser.WhenClauseContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterElseClause([NotNull] EsperEPL2GrammarParser.ElseClauseContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitElseClause([NotNull] EsperEPL2GrammarParser.ElseClauseContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterMatchRecog([NotNull] EsperEPL2GrammarParser.MatchRecogContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitMatchRecog([NotNull] EsperEPL2GrammarParser.MatchRecogContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterMatchRecogPartitionBy([NotNull] EsperEPL2GrammarParser.MatchRecogPartitionByContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitMatchRecogPartitionBy([NotNull] EsperEPL2GrammarParser.MatchRecogPartitionByContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterMatchRecogMeasures([NotNull] EsperEPL2GrammarParser.MatchRecogMeasuresContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitMatchRecogMeasures([NotNull] EsperEPL2GrammarParser.MatchRecogMeasuresContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterMatchRecogMeasureItem([NotNull] EsperEPL2GrammarParser.MatchRecogMeasureItemContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitMatchRecogMeasureItem([NotNull] EsperEPL2GrammarParser.MatchRecogMeasureItemContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterMatchRecogMatchesSelection([NotNull] EsperEPL2GrammarParser.MatchRecogMatchesSelectionContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitMatchRecogMatchesSelection([NotNull] EsperEPL2GrammarParser.MatchRecogMatchesSelectionContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterMatchRecogPattern([NotNull] EsperEPL2GrammarParser.MatchRecogPatternContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitMatchRecogPattern([NotNull] EsperEPL2GrammarParser.MatchRecogPatternContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterMatchRecogMatchesAfterSkip([NotNull] EsperEPL2GrammarParser.MatchRecogMatchesAfterSkipContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitMatchRecogMatchesAfterSkip([NotNull] EsperEPL2GrammarParser.MatchRecogMatchesAfterSkipContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterMatchRecogMatchesInterval([NotNull] EsperEPL2GrammarParser.MatchRecogMatchesIntervalContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitMatchRecogMatchesInterval([NotNull] EsperEPL2GrammarParser.MatchRecogMatchesIntervalContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterMatchRecogPatternAlteration([NotNull] EsperEPL2GrammarParser.MatchRecogPatternAlterationContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitMatchRecogPatternAlteration([NotNull] EsperEPL2GrammarParser.MatchRecogPatternAlterationContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterMatchRecogPatternConcat([NotNull] EsperEPL2GrammarParser.MatchRecogPatternConcatContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitMatchRecogPatternConcat([NotNull] EsperEPL2GrammarParser.MatchRecogPatternConcatContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterMatchRecogPatternUnary([NotNull] EsperEPL2GrammarParser.MatchRecogPatternUnaryContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitMatchRecogPatternUnary([NotNull] EsperEPL2GrammarParser.MatchRecogPatternUnaryContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterMatchRecogPatternNested([NotNull] EsperEPL2GrammarParser.MatchRecogPatternNestedContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitMatchRecogPatternNested([NotNull] EsperEPL2GrammarParser.MatchRecogPatternNestedContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterMatchRecogPatternPermute([NotNull] EsperEPL2GrammarParser.MatchRecogPatternPermuteContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitMatchRecogPatternPermute([NotNull] EsperEPL2GrammarParser.MatchRecogPatternPermuteContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterMatchRecogPatternAtom([NotNull] EsperEPL2GrammarParser.MatchRecogPatternAtomContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitMatchRecogPatternAtom([NotNull] EsperEPL2GrammarParser.MatchRecogPatternAtomContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterMatchRecogPatternRepeat([NotNull] EsperEPL2GrammarParser.MatchRecogPatternRepeatContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitMatchRecogPatternRepeat([NotNull] EsperEPL2GrammarParser.MatchRecogPatternRepeatContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterMatchRecogDefine([NotNull] EsperEPL2GrammarParser.MatchRecogDefineContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitMatchRecogDefine([NotNull] EsperEPL2GrammarParser.MatchRecogDefineContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterMatchRecogDefineItem([NotNull] EsperEPL2GrammarParser.MatchRecogDefineItemContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitMatchRecogDefineItem([NotNull] EsperEPL2GrammarParser.MatchRecogDefineItemContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterExpression([NotNull] EsperEPL2GrammarParser.ExpressionContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitExpression([NotNull] EsperEPL2GrammarParser.ExpressionContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterCaseExpression([NotNull] EsperEPL2GrammarParser.CaseExpressionContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitCaseExpression([NotNull] EsperEPL2GrammarParser.CaseExpressionContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterEvalOrExpression([NotNull] EsperEPL2GrammarParser.EvalOrExpressionContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitEvalOrExpression([NotNull] EsperEPL2GrammarParser.EvalOrExpressionContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterEvalAndExpression([NotNull] EsperEPL2GrammarParser.EvalAndExpressionContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitEvalAndExpression([NotNull] EsperEPL2GrammarParser.EvalAndExpressionContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterBitWiseExpression([NotNull] EsperEPL2GrammarParser.BitWiseExpressionContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitBitWiseExpression([NotNull] EsperEPL2GrammarParser.BitWiseExpressionContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterNegatedExpression([NotNull] EsperEPL2GrammarParser.NegatedExpressionContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitNegatedExpression([NotNull] EsperEPL2GrammarParser.NegatedExpressionContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterEvalEqualsExpression([NotNull] EsperEPL2GrammarParser.EvalEqualsExpressionContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitEvalEqualsExpression([NotNull] EsperEPL2GrammarParser.EvalEqualsExpressionContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterEvalRelationalExpression([NotNull] EsperEPL2GrammarParser.EvalRelationalExpressionContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitEvalRelationalExpression([NotNull] EsperEPL2GrammarParser.EvalRelationalExpressionContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterInSubSelectQuery([NotNull] EsperEPL2GrammarParser.InSubSelectQueryContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitInSubSelectQuery([NotNull] EsperEPL2GrammarParser.InSubSelectQueryContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterConcatenationExpr([NotNull] EsperEPL2GrammarParser.ConcatenationExprContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitConcatenationExpr([NotNull] EsperEPL2GrammarParser.ConcatenationExprContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterAdditiveExpression([NotNull] EsperEPL2GrammarParser.AdditiveExpressionContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitAdditiveExpression([NotNull] EsperEPL2GrammarParser.AdditiveExpressionContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterMultiplyExpression([NotNull] EsperEPL2GrammarParser.MultiplyExpressionContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitMultiplyExpression([NotNull] EsperEPL2GrammarParser.MultiplyExpressionContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterUnaryExpression([NotNull] EsperEPL2GrammarParser.UnaryExpressionContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitUnaryExpression([NotNull] EsperEPL2GrammarParser.UnaryExpressionContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterSubstitutionCanChain([NotNull] EsperEPL2GrammarParser.SubstitutionCanChainContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitSubstitutionCanChain([NotNull] EsperEPL2GrammarParser.SubstitutionCanChainContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterChainedFunction([NotNull] EsperEPL2GrammarParser.ChainedFunctionContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitChainedFunction([NotNull] EsperEPL2GrammarParser.ChainedFunctionContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterNewAssign([NotNull] EsperEPL2GrammarParser.NewAssignContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitNewAssign([NotNull] EsperEPL2GrammarParser.NewAssignContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterRowSubSelectExpression([NotNull] EsperEPL2GrammarParser.RowSubSelectExpressionContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitRowSubSelectExpression([NotNull] EsperEPL2GrammarParser.RowSubSelectExpressionContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterSubSelectGroupExpression([NotNull] EsperEPL2GrammarParser.SubSelectGroupExpressionContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitSubSelectGroupExpression([NotNull] EsperEPL2GrammarParser.SubSelectGroupExpressionContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterExistsSubSelectExpression([NotNull] EsperEPL2GrammarParser.ExistsSubSelectExpressionContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitExistsSubSelectExpression([NotNull] EsperEPL2GrammarParser.ExistsSubSelectExpressionContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterSubQueryExpr([NotNull] EsperEPL2GrammarParser.SubQueryExprContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitSubQueryExpr([NotNull] EsperEPL2GrammarParser.SubQueryExprContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterSubSelectFilterExpr([NotNull] EsperEPL2GrammarParser.SubSelectFilterExprContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitSubSelectFilterExpr([NotNull] EsperEPL2GrammarParser.SubSelectFilterExprContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterArrayExpression([NotNull] EsperEPL2GrammarParser.ArrayExpressionContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitArrayExpression([NotNull] EsperEPL2GrammarParser.ArrayExpressionContext context) { } + /// + /// Enter a parse tree produced by the builtin_sum + /// labeled alternative in . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterBuiltin_sum([NotNull] EsperEPL2GrammarParser.Builtin_sumContext context) { } + /// + /// Exit a parse tree produced by the builtin_sum + /// labeled alternative in . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitBuiltin_sum([NotNull] EsperEPL2GrammarParser.Builtin_sumContext context) { } + /// + /// Enter a parse tree produced by the builtin_avg + /// labeled alternative in . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterBuiltin_avg([NotNull] EsperEPL2GrammarParser.Builtin_avgContext context) { } + /// + /// Exit a parse tree produced by the builtin_avg + /// labeled alternative in . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitBuiltin_avg([NotNull] EsperEPL2GrammarParser.Builtin_avgContext context) { } + /// + /// Enter a parse tree produced by the builtin_cnt + /// labeled alternative in . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterBuiltin_cnt([NotNull] EsperEPL2GrammarParser.Builtin_cntContext context) { } + /// + /// Exit a parse tree produced by the builtin_cnt + /// labeled alternative in . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitBuiltin_cnt([NotNull] EsperEPL2GrammarParser.Builtin_cntContext context) { } + /// + /// Enter a parse tree produced by the builtin_median + /// labeled alternative in . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterBuiltin_median([NotNull] EsperEPL2GrammarParser.Builtin_medianContext context) { } + /// + /// Exit a parse tree produced by the builtin_median + /// labeled alternative in . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitBuiltin_median([NotNull] EsperEPL2GrammarParser.Builtin_medianContext context) { } + /// + /// Enter a parse tree produced by the builtin_stddev + /// labeled alternative in . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterBuiltin_stddev([NotNull] EsperEPL2GrammarParser.Builtin_stddevContext context) { } + /// + /// Exit a parse tree produced by the builtin_stddev + /// labeled alternative in . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitBuiltin_stddev([NotNull] EsperEPL2GrammarParser.Builtin_stddevContext context) { } + /// + /// Enter a parse tree produced by the builtin_avedev + /// labeled alternative in . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterBuiltin_avedev([NotNull] EsperEPL2GrammarParser.Builtin_avedevContext context) { } + /// + /// Exit a parse tree produced by the builtin_avedev + /// labeled alternative in . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitBuiltin_avedev([NotNull] EsperEPL2GrammarParser.Builtin_avedevContext context) { } + /// + /// Enter a parse tree produced by the builtin_firstlastwindow + /// labeled alternative in . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterBuiltin_firstlastwindow([NotNull] EsperEPL2GrammarParser.Builtin_firstlastwindowContext context) { } + /// + /// Exit a parse tree produced by the builtin_firstlastwindow + /// labeled alternative in . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitBuiltin_firstlastwindow([NotNull] EsperEPL2GrammarParser.Builtin_firstlastwindowContext context) { } + /// + /// Enter a parse tree produced by the builtin_coalesce + /// labeled alternative in . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterBuiltin_coalesce([NotNull] EsperEPL2GrammarParser.Builtin_coalesceContext context) { } + /// + /// Exit a parse tree produced by the builtin_coalesce + /// labeled alternative in . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitBuiltin_coalesce([NotNull] EsperEPL2GrammarParser.Builtin_coalesceContext context) { } + /// + /// Enter a parse tree produced by the builtin_prev + /// labeled alternative in . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterBuiltin_prev([NotNull] EsperEPL2GrammarParser.Builtin_prevContext context) { } + /// + /// Exit a parse tree produced by the builtin_prev + /// labeled alternative in . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitBuiltin_prev([NotNull] EsperEPL2GrammarParser.Builtin_prevContext context) { } + /// + /// Enter a parse tree produced by the builtin_prevtail + /// labeled alternative in . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterBuiltin_prevtail([NotNull] EsperEPL2GrammarParser.Builtin_prevtailContext context) { } + /// + /// Exit a parse tree produced by the builtin_prevtail + /// labeled alternative in . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitBuiltin_prevtail([NotNull] EsperEPL2GrammarParser.Builtin_prevtailContext context) { } + /// + /// Enter a parse tree produced by the builtin_prevcount + /// labeled alternative in . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterBuiltin_prevcount([NotNull] EsperEPL2GrammarParser.Builtin_prevcountContext context) { } + /// + /// Exit a parse tree produced by the builtin_prevcount + /// labeled alternative in . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitBuiltin_prevcount([NotNull] EsperEPL2GrammarParser.Builtin_prevcountContext context) { } + /// + /// Enter a parse tree produced by the builtin_prevwindow + /// labeled alternative in . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterBuiltin_prevwindow([NotNull] EsperEPL2GrammarParser.Builtin_prevwindowContext context) { } + /// + /// Exit a parse tree produced by the builtin_prevwindow + /// labeled alternative in . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitBuiltin_prevwindow([NotNull] EsperEPL2GrammarParser.Builtin_prevwindowContext context) { } + /// + /// Enter a parse tree produced by the builtin_prior + /// labeled alternative in . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterBuiltin_prior([NotNull] EsperEPL2GrammarParser.Builtin_priorContext context) { } + /// + /// Exit a parse tree produced by the builtin_prior + /// labeled alternative in . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitBuiltin_prior([NotNull] EsperEPL2GrammarParser.Builtin_priorContext context) { } + /// + /// Enter a parse tree produced by the builtin_grouping + /// labeled alternative in . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterBuiltin_grouping([NotNull] EsperEPL2GrammarParser.Builtin_groupingContext context) { } + /// + /// Exit a parse tree produced by the builtin_grouping + /// labeled alternative in . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitBuiltin_grouping([NotNull] EsperEPL2GrammarParser.Builtin_groupingContext context) { } + /// + /// Enter a parse tree produced by the builtin_groupingid + /// labeled alternative in . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterBuiltin_groupingid([NotNull] EsperEPL2GrammarParser.Builtin_groupingidContext context) { } + /// + /// Exit a parse tree produced by the builtin_groupingid + /// labeled alternative in . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitBuiltin_groupingid([NotNull] EsperEPL2GrammarParser.Builtin_groupingidContext context) { } + /// + /// Enter a parse tree produced by the builtin_instanceof + /// labeled alternative in . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterBuiltin_instanceof([NotNull] EsperEPL2GrammarParser.Builtin_instanceofContext context) { } + /// + /// Exit a parse tree produced by the builtin_instanceof + /// labeled alternative in . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitBuiltin_instanceof([NotNull] EsperEPL2GrammarParser.Builtin_instanceofContext context) { } + /// + /// Enter a parse tree produced by the builtin_typeof + /// labeled alternative in . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterBuiltin_typeof([NotNull] EsperEPL2GrammarParser.Builtin_typeofContext context) { } + /// + /// Exit a parse tree produced by the builtin_typeof + /// labeled alternative in . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitBuiltin_typeof([NotNull] EsperEPL2GrammarParser.Builtin_typeofContext context) { } + /// + /// Enter a parse tree produced by the builtin_cast + /// labeled alternative in . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterBuiltin_cast([NotNull] EsperEPL2GrammarParser.Builtin_castContext context) { } + /// + /// Exit a parse tree produced by the builtin_cast + /// labeled alternative in . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitBuiltin_cast([NotNull] EsperEPL2GrammarParser.Builtin_castContext context) { } + /// + /// Enter a parse tree produced by the builtin_exists + /// labeled alternative in . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterBuiltin_exists([NotNull] EsperEPL2GrammarParser.Builtin_existsContext context) { } + /// + /// Exit a parse tree produced by the builtin_exists + /// labeled alternative in . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitBuiltin_exists([NotNull] EsperEPL2GrammarParser.Builtin_existsContext context) { } + /// + /// Enter a parse tree produced by the builtin_currts + /// labeled alternative in . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterBuiltin_currts([NotNull] EsperEPL2GrammarParser.Builtin_currtsContext context) { } + /// + /// Exit a parse tree produced by the builtin_currts + /// labeled alternative in . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitBuiltin_currts([NotNull] EsperEPL2GrammarParser.Builtin_currtsContext context) { } + /// + /// Enter a parse tree produced by the builtin_istream + /// labeled alternative in . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterBuiltin_istream([NotNull] EsperEPL2GrammarParser.Builtin_istreamContext context) { } + /// + /// Exit a parse tree produced by the builtin_istream + /// labeled alternative in . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitBuiltin_istream([NotNull] EsperEPL2GrammarParser.Builtin_istreamContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterFirstLastWindowAggregation([NotNull] EsperEPL2GrammarParser.FirstLastWindowAggregationContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitFirstLastWindowAggregation([NotNull] EsperEPL2GrammarParser.FirstLastWindowAggregationContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterEventPropertyOrLibFunction([NotNull] EsperEPL2GrammarParser.EventPropertyOrLibFunctionContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitEventPropertyOrLibFunction([NotNull] EsperEPL2GrammarParser.EventPropertyOrLibFunctionContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterLibFunction([NotNull] EsperEPL2GrammarParser.LibFunctionContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitLibFunction([NotNull] EsperEPL2GrammarParser.LibFunctionContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterLibFunctionWithClass([NotNull] EsperEPL2GrammarParser.LibFunctionWithClassContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitLibFunctionWithClass([NotNull] EsperEPL2GrammarParser.LibFunctionWithClassContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterLibFunctionNoClass([NotNull] EsperEPL2GrammarParser.LibFunctionNoClassContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitLibFunctionNoClass([NotNull] EsperEPL2GrammarParser.LibFunctionNoClassContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterFuncIdentTop([NotNull] EsperEPL2GrammarParser.FuncIdentTopContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitFuncIdentTop([NotNull] EsperEPL2GrammarParser.FuncIdentTopContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterFuncIdentInner([NotNull] EsperEPL2GrammarParser.FuncIdentInnerContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitFuncIdentInner([NotNull] EsperEPL2GrammarParser.FuncIdentInnerContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterFuncIdentChained([NotNull] EsperEPL2GrammarParser.FuncIdentChainedContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitFuncIdentChained([NotNull] EsperEPL2GrammarParser.FuncIdentChainedContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterLibFunctionArgs([NotNull] EsperEPL2GrammarParser.LibFunctionArgsContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitLibFunctionArgs([NotNull] EsperEPL2GrammarParser.LibFunctionArgsContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterLibFunctionArgItem([NotNull] EsperEPL2GrammarParser.LibFunctionArgItemContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitLibFunctionArgItem([NotNull] EsperEPL2GrammarParser.LibFunctionArgItemContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterBetweenList([NotNull] EsperEPL2GrammarParser.BetweenListContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitBetweenList([NotNull] EsperEPL2GrammarParser.BetweenListContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterPatternExpression([NotNull] EsperEPL2GrammarParser.PatternExpressionContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitPatternExpression([NotNull] EsperEPL2GrammarParser.PatternExpressionContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterFollowedByExpression([NotNull] EsperEPL2GrammarParser.FollowedByExpressionContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitFollowedByExpression([NotNull] EsperEPL2GrammarParser.FollowedByExpressionContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterFollowedByRepeat([NotNull] EsperEPL2GrammarParser.FollowedByRepeatContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitFollowedByRepeat([NotNull] EsperEPL2GrammarParser.FollowedByRepeatContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterOrExpression([NotNull] EsperEPL2GrammarParser.OrExpressionContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitOrExpression([NotNull] EsperEPL2GrammarParser.OrExpressionContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterAndExpression([NotNull] EsperEPL2GrammarParser.AndExpressionContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitAndExpression([NotNull] EsperEPL2GrammarParser.AndExpressionContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterMatchUntilExpression([NotNull] EsperEPL2GrammarParser.MatchUntilExpressionContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitMatchUntilExpression([NotNull] EsperEPL2GrammarParser.MatchUntilExpressionContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterQualifyExpression([NotNull] EsperEPL2GrammarParser.QualifyExpressionContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitQualifyExpression([NotNull] EsperEPL2GrammarParser.QualifyExpressionContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterGuardPostFix([NotNull] EsperEPL2GrammarParser.GuardPostFixContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitGuardPostFix([NotNull] EsperEPL2GrammarParser.GuardPostFixContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterDistinctExpressionList([NotNull] EsperEPL2GrammarParser.DistinctExpressionListContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitDistinctExpressionList([NotNull] EsperEPL2GrammarParser.DistinctExpressionListContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterDistinctExpressionAtom([NotNull] EsperEPL2GrammarParser.DistinctExpressionAtomContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitDistinctExpressionAtom([NotNull] EsperEPL2GrammarParser.DistinctExpressionAtomContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterAtomicExpression([NotNull] EsperEPL2GrammarParser.AtomicExpressionContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitAtomicExpression([NotNull] EsperEPL2GrammarParser.AtomicExpressionContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterObserverExpression([NotNull] EsperEPL2GrammarParser.ObserverExpressionContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitObserverExpression([NotNull] EsperEPL2GrammarParser.ObserverExpressionContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterGuardWhereExpression([NotNull] EsperEPL2GrammarParser.GuardWhereExpressionContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitGuardWhereExpression([NotNull] EsperEPL2GrammarParser.GuardWhereExpressionContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterGuardWhileExpression([NotNull] EsperEPL2GrammarParser.GuardWhileExpressionContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitGuardWhileExpression([NotNull] EsperEPL2GrammarParser.GuardWhileExpressionContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterMatchUntilRange([NotNull] EsperEPL2GrammarParser.MatchUntilRangeContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitMatchUntilRange([NotNull] EsperEPL2GrammarParser.MatchUntilRangeContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterEventFilterExpression([NotNull] EsperEPL2GrammarParser.EventFilterExpressionContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitEventFilterExpression([NotNull] EsperEPL2GrammarParser.EventFilterExpressionContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterPropertyExpression([NotNull] EsperEPL2GrammarParser.PropertyExpressionContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitPropertyExpression([NotNull] EsperEPL2GrammarParser.PropertyExpressionContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterPropertyExpressionAtomic([NotNull] EsperEPL2GrammarParser.PropertyExpressionAtomicContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitPropertyExpressionAtomic([NotNull] EsperEPL2GrammarParser.PropertyExpressionAtomicContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterPropertyExpressionSelect([NotNull] EsperEPL2GrammarParser.PropertyExpressionSelectContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitPropertyExpressionSelect([NotNull] EsperEPL2GrammarParser.PropertyExpressionSelectContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterPropertySelectionList([NotNull] EsperEPL2GrammarParser.PropertySelectionListContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitPropertySelectionList([NotNull] EsperEPL2GrammarParser.PropertySelectionListContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterPropertySelectionListElement([NotNull] EsperEPL2GrammarParser.PropertySelectionListElementContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitPropertySelectionListElement([NotNull] EsperEPL2GrammarParser.PropertySelectionListElementContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterPropertyStreamSelector([NotNull] EsperEPL2GrammarParser.PropertyStreamSelectorContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitPropertyStreamSelector([NotNull] EsperEPL2GrammarParser.PropertyStreamSelectorContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterTypeExpressionAnnotation([NotNull] EsperEPL2GrammarParser.TypeExpressionAnnotationContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitTypeExpressionAnnotation([NotNull] EsperEPL2GrammarParser.TypeExpressionAnnotationContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterPatternFilterExpression([NotNull] EsperEPL2GrammarParser.PatternFilterExpressionContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitPatternFilterExpression([NotNull] EsperEPL2GrammarParser.PatternFilterExpressionContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterPatternFilterAnnotation([NotNull] EsperEPL2GrammarParser.PatternFilterAnnotationContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitPatternFilterAnnotation([NotNull] EsperEPL2GrammarParser.PatternFilterAnnotationContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterClassIdentifier([NotNull] EsperEPL2GrammarParser.ClassIdentifierContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitClassIdentifier([NotNull] EsperEPL2GrammarParser.ClassIdentifierContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterSlashIdentifier([NotNull] EsperEPL2GrammarParser.SlashIdentifierContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitSlashIdentifier([NotNull] EsperEPL2GrammarParser.SlashIdentifierContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterExpressionListWithNamed([NotNull] EsperEPL2GrammarParser.ExpressionListWithNamedContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitExpressionListWithNamed([NotNull] EsperEPL2GrammarParser.ExpressionListWithNamedContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterExpressionListWithNamedWithTime([NotNull] EsperEPL2GrammarParser.ExpressionListWithNamedWithTimeContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitExpressionListWithNamedWithTime([NotNull] EsperEPL2GrammarParser.ExpressionListWithNamedWithTimeContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterExpressionWithNamed([NotNull] EsperEPL2GrammarParser.ExpressionWithNamedContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitExpressionWithNamed([NotNull] EsperEPL2GrammarParser.ExpressionWithNamedContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterExpressionWithNamedWithTime([NotNull] EsperEPL2GrammarParser.ExpressionWithNamedWithTimeContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitExpressionWithNamedWithTime([NotNull] EsperEPL2GrammarParser.ExpressionWithNamedWithTimeContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterExpressionNamedParameter([NotNull] EsperEPL2GrammarParser.ExpressionNamedParameterContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitExpressionNamedParameter([NotNull] EsperEPL2GrammarParser.ExpressionNamedParameterContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterExpressionNamedParameterWithTime([NotNull] EsperEPL2GrammarParser.ExpressionNamedParameterWithTimeContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitExpressionNamedParameterWithTime([NotNull] EsperEPL2GrammarParser.ExpressionNamedParameterWithTimeContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterExpressionList([NotNull] EsperEPL2GrammarParser.ExpressionListContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitExpressionList([NotNull] EsperEPL2GrammarParser.ExpressionListContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterExpressionWithTimeList([NotNull] EsperEPL2GrammarParser.ExpressionWithTimeListContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitExpressionWithTimeList([NotNull] EsperEPL2GrammarParser.ExpressionWithTimeListContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterExpressionWithTime([NotNull] EsperEPL2GrammarParser.ExpressionWithTimeContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitExpressionWithTime([NotNull] EsperEPL2GrammarParser.ExpressionWithTimeContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterExpressionWithTimeInclLast([NotNull] EsperEPL2GrammarParser.ExpressionWithTimeInclLastContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitExpressionWithTimeInclLast([NotNull] EsperEPL2GrammarParser.ExpressionWithTimeInclLastContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterExpressionQualifyable([NotNull] EsperEPL2GrammarParser.ExpressionQualifyableContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitExpressionQualifyable([NotNull] EsperEPL2GrammarParser.ExpressionQualifyableContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterLastWeekdayOperand([NotNull] EsperEPL2GrammarParser.LastWeekdayOperandContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitLastWeekdayOperand([NotNull] EsperEPL2GrammarParser.LastWeekdayOperandContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterLastOperand([NotNull] EsperEPL2GrammarParser.LastOperandContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitLastOperand([NotNull] EsperEPL2GrammarParser.LastOperandContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterFrequencyOperand([NotNull] EsperEPL2GrammarParser.FrequencyOperandContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitFrequencyOperand([NotNull] EsperEPL2GrammarParser.FrequencyOperandContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterRangeOperand([NotNull] EsperEPL2GrammarParser.RangeOperandContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitRangeOperand([NotNull] EsperEPL2GrammarParser.RangeOperandContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterLastOperator([NotNull] EsperEPL2GrammarParser.LastOperatorContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitLastOperator([NotNull] EsperEPL2GrammarParser.LastOperatorContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterWeekDayOperator([NotNull] EsperEPL2GrammarParser.WeekDayOperatorContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitWeekDayOperator([NotNull] EsperEPL2GrammarParser.WeekDayOperatorContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterNumericParameterList([NotNull] EsperEPL2GrammarParser.NumericParameterListContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitNumericParameterList([NotNull] EsperEPL2GrammarParser.NumericParameterListContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterNumericListParameter([NotNull] EsperEPL2GrammarParser.NumericListParameterContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitNumericListParameter([NotNull] EsperEPL2GrammarParser.NumericListParameterContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterEventProperty([NotNull] EsperEPL2GrammarParser.EventPropertyContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitEventProperty([NotNull] EsperEPL2GrammarParser.EventPropertyContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterEventPropertyAtomic([NotNull] EsperEPL2GrammarParser.EventPropertyAtomicContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitEventPropertyAtomic([NotNull] EsperEPL2GrammarParser.EventPropertyAtomicContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterEventPropertyIdent([NotNull] EsperEPL2GrammarParser.EventPropertyIdentContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitEventPropertyIdent([NotNull] EsperEPL2GrammarParser.EventPropertyIdentContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterKeywordAllowedIdent([NotNull] EsperEPL2GrammarParser.KeywordAllowedIdentContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitKeywordAllowedIdent([NotNull] EsperEPL2GrammarParser.KeywordAllowedIdentContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterEscapableStr([NotNull] EsperEPL2GrammarParser.EscapableStrContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitEscapableStr([NotNull] EsperEPL2GrammarParser.EscapableStrContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterEscapableIdent([NotNull] EsperEPL2GrammarParser.EscapableIdentContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitEscapableIdent([NotNull] EsperEPL2GrammarParser.EscapableIdentContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterTimePeriod([NotNull] EsperEPL2GrammarParser.TimePeriodContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitTimePeriod([NotNull] EsperEPL2GrammarParser.TimePeriodContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterYearPart([NotNull] EsperEPL2GrammarParser.YearPartContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitYearPart([NotNull] EsperEPL2GrammarParser.YearPartContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterMonthPart([NotNull] EsperEPL2GrammarParser.MonthPartContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitMonthPart([NotNull] EsperEPL2GrammarParser.MonthPartContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterWeekPart([NotNull] EsperEPL2GrammarParser.WeekPartContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitWeekPart([NotNull] EsperEPL2GrammarParser.WeekPartContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterDayPart([NotNull] EsperEPL2GrammarParser.DayPartContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitDayPart([NotNull] EsperEPL2GrammarParser.DayPartContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterHourPart([NotNull] EsperEPL2GrammarParser.HourPartContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitHourPart([NotNull] EsperEPL2GrammarParser.HourPartContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterMinutePart([NotNull] EsperEPL2GrammarParser.MinutePartContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitMinutePart([NotNull] EsperEPL2GrammarParser.MinutePartContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterSecondPart([NotNull] EsperEPL2GrammarParser.SecondPartContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitSecondPart([NotNull] EsperEPL2GrammarParser.SecondPartContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterMillisecondPart([NotNull] EsperEPL2GrammarParser.MillisecondPartContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitMillisecondPart([NotNull] EsperEPL2GrammarParser.MillisecondPartContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterMicrosecondPart([NotNull] EsperEPL2GrammarParser.MicrosecondPartContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitMicrosecondPart([NotNull] EsperEPL2GrammarParser.MicrosecondPartContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterNumber([NotNull] EsperEPL2GrammarParser.NumberContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitNumber([NotNull] EsperEPL2GrammarParser.NumberContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterSubstitution([NotNull] EsperEPL2GrammarParser.SubstitutionContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitSubstitution([NotNull] EsperEPL2GrammarParser.SubstitutionContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterConstant([NotNull] EsperEPL2GrammarParser.ConstantContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitConstant([NotNull] EsperEPL2GrammarParser.ConstantContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterNumberconstant([NotNull] EsperEPL2GrammarParser.NumberconstantContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitNumberconstant([NotNull] EsperEPL2GrammarParser.NumberconstantContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterStringconstant([NotNull] EsperEPL2GrammarParser.StringconstantContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitStringconstant([NotNull] EsperEPL2GrammarParser.StringconstantContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterJsonvalue([NotNull] EsperEPL2GrammarParser.JsonvalueContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitJsonvalue([NotNull] EsperEPL2GrammarParser.JsonvalueContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterJsonobject([NotNull] EsperEPL2GrammarParser.JsonobjectContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitJsonobject([NotNull] EsperEPL2GrammarParser.JsonobjectContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterJsonarray([NotNull] EsperEPL2GrammarParser.JsonarrayContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitJsonarray([NotNull] EsperEPL2GrammarParser.JsonarrayContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterJsonelements([NotNull] EsperEPL2GrammarParser.JsonelementsContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitJsonelements([NotNull] EsperEPL2GrammarParser.JsonelementsContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterJsonmembers([NotNull] EsperEPL2GrammarParser.JsonmembersContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitJsonmembers([NotNull] EsperEPL2GrammarParser.JsonmembersContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterJsonpair([NotNull] EsperEPL2GrammarParser.JsonpairContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitJsonpair([NotNull] EsperEPL2GrammarParser.JsonpairContext context) { } + + /// + /// The default implementation does nothing. + public virtual void EnterEveryRule([NotNull] ParserRuleContext context) { } + /// + /// The default implementation does nothing. + public virtual void ExitEveryRule([NotNull] ParserRuleContext context) { } + /// + /// The default implementation does nothing. + public virtual void VisitTerminal([NotNull] ITerminalNode node) { } + /// + /// The default implementation does nothing. + public virtual void VisitErrorNode([NotNull] IErrorNode node) { } +} +} // namespace com.espertech.esper.epl.generated diff --git a/NEsper.Core/NEsper.Core/epl/generated/EsperEPL2GrammarBaseVisitor.cs b/NEsper.Core/NEsper.Core/epl/generated/EsperEPL2GrammarBaseVisitor.cs new file mode 100755 index 000000000..068223e4b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/generated/EsperEPL2GrammarBaseVisitor.cs @@ -0,0 +1,2773 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// ANTLR Version: 4.6 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +// Generated from C:\Src\Espertech\NEsper-master\NEsper\grammar\EsperEPL2Grammar.g4 by ANTLR 4.6 + +// Unreachable code detected +#pragma warning disable 0162 +// The variable '...' is assigned but its value is never used +#pragma warning disable 0219 +// Missing XML comment for publicly visible type or member '...' +#pragma warning disable 1591 +// Ambiguous reference in cref attribute +#pragma warning disable 419 + +namespace com.espertech.esper.epl.generated { + + using System; + using System.Collections.Generic; + +using Antlr4.Runtime.Misc; +using Antlr4.Runtime.Tree; +using IToken = Antlr4.Runtime.IToken; +using ParserRuleContext = Antlr4.Runtime.ParserRuleContext; + +/// +/// This class provides an empty implementation of , +/// which can be extended to create a visitor which only needs to handle a subset +/// of the available methods. +/// +/// The return type of the visit operation. +[System.CodeDom.Compiler.GeneratedCode("ANTLR", "4.6")] +[System.CLSCompliant(false)] +public partial class EsperEPL2GrammarBaseVisitor : AbstractParseTreeVisitor, IEsperEPL2GrammarVisitor { + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitStartPatternExpressionRule([NotNull] EsperEPL2GrammarParser.StartPatternExpressionRuleContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitStartEPLExpressionRule([NotNull] EsperEPL2GrammarParser.StartEPLExpressionRuleContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitStartEventPropertyRule([NotNull] EsperEPL2GrammarParser.StartEventPropertyRuleContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitStartJsonValueRule([NotNull] EsperEPL2GrammarParser.StartJsonValueRuleContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitExpressionDecl([NotNull] EsperEPL2GrammarParser.ExpressionDeclContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitExpressionDialect([NotNull] EsperEPL2GrammarParser.ExpressionDialectContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitExpressionDef([NotNull] EsperEPL2GrammarParser.ExpressionDefContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitExpressionLambdaDecl([NotNull] EsperEPL2GrammarParser.ExpressionLambdaDeclContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitExpressionTypeAnno([NotNull] EsperEPL2GrammarParser.ExpressionTypeAnnoContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitAnnotationEnum([NotNull] EsperEPL2GrammarParser.AnnotationEnumContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitElementValuePairsEnum([NotNull] EsperEPL2GrammarParser.ElementValuePairsEnumContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitElementValuePairEnum([NotNull] EsperEPL2GrammarParser.ElementValuePairEnumContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitElementValueEnum([NotNull] EsperEPL2GrammarParser.ElementValueEnumContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitElementValueArrayEnum([NotNull] EsperEPL2GrammarParser.ElementValueArrayEnumContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitEplExpression([NotNull] EsperEPL2GrammarParser.EplExpressionContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitContextExpr([NotNull] EsperEPL2GrammarParser.ContextExprContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitSelectExpr([NotNull] EsperEPL2GrammarParser.SelectExprContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitOnExpr([NotNull] EsperEPL2GrammarParser.OnExprContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitOnStreamExpr([NotNull] EsperEPL2GrammarParser.OnStreamExprContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitUpdateExpr([NotNull] EsperEPL2GrammarParser.UpdateExprContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitUpdateDetails([NotNull] EsperEPL2GrammarParser.UpdateDetailsContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitOnMergeExpr([NotNull] EsperEPL2GrammarParser.OnMergeExprContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitMergeItem([NotNull] EsperEPL2GrammarParser.MergeItemContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitMergeMatched([NotNull] EsperEPL2GrammarParser.MergeMatchedContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitMergeMatchedItem([NotNull] EsperEPL2GrammarParser.MergeMatchedItemContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitMergeUnmatched([NotNull] EsperEPL2GrammarParser.MergeUnmatchedContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitMergeUnmatchedItem([NotNull] EsperEPL2GrammarParser.MergeUnmatchedItemContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitMergeInsert([NotNull] EsperEPL2GrammarParser.MergeInsertContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitOnSelectExpr([NotNull] EsperEPL2GrammarParser.OnSelectExprContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitOnUpdateExpr([NotNull] EsperEPL2GrammarParser.OnUpdateExprContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitOnSelectInsertExpr([NotNull] EsperEPL2GrammarParser.OnSelectInsertExprContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitOnSelectInsertFromClause([NotNull] EsperEPL2GrammarParser.OnSelectInsertFromClauseContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitOutputClauseInsert([NotNull] EsperEPL2GrammarParser.OutputClauseInsertContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitOnDeleteExpr([NotNull] EsperEPL2GrammarParser.OnDeleteExprContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitOnSetExpr([NotNull] EsperEPL2GrammarParser.OnSetExprContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitOnSetAssignmentList([NotNull] EsperEPL2GrammarParser.OnSetAssignmentListContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitOnSetAssignment([NotNull] EsperEPL2GrammarParser.OnSetAssignmentContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitOnExprFrom([NotNull] EsperEPL2GrammarParser.OnExprFromContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitCreateWindowExpr([NotNull] EsperEPL2GrammarParser.CreateWindowExprContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitCreateWindowExprModelAfter([NotNull] EsperEPL2GrammarParser.CreateWindowExprModelAfterContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitCreateIndexExpr([NotNull] EsperEPL2GrammarParser.CreateIndexExprContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitCreateIndexColumnList([NotNull] EsperEPL2GrammarParser.CreateIndexColumnListContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitCreateIndexColumn([NotNull] EsperEPL2GrammarParser.CreateIndexColumnContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitCreateVariableExpr([NotNull] EsperEPL2GrammarParser.CreateVariableExprContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitCreateTableExpr([NotNull] EsperEPL2GrammarParser.CreateTableExprContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitCreateTableColumnList([NotNull] EsperEPL2GrammarParser.CreateTableColumnListContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitCreateTableColumn([NotNull] EsperEPL2GrammarParser.CreateTableColumnContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitCreateTableColumnPlain([NotNull] EsperEPL2GrammarParser.CreateTableColumnPlainContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitCreateColumnList([NotNull] EsperEPL2GrammarParser.CreateColumnListContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitCreateColumnListElement([NotNull] EsperEPL2GrammarParser.CreateColumnListElementContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitCreateSelectionList([NotNull] EsperEPL2GrammarParser.CreateSelectionListContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitCreateSelectionListElement([NotNull] EsperEPL2GrammarParser.CreateSelectionListElementContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitCreateSchemaExpr([NotNull] EsperEPL2GrammarParser.CreateSchemaExprContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitCreateSchemaDef([NotNull] EsperEPL2GrammarParser.CreateSchemaDefContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitFafDelete([NotNull] EsperEPL2GrammarParser.FafDeleteContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitFafUpdate([NotNull] EsperEPL2GrammarParser.FafUpdateContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitFafInsert([NotNull] EsperEPL2GrammarParser.FafInsertContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitCreateDataflow([NotNull] EsperEPL2GrammarParser.CreateDataflowContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitGopList([NotNull] EsperEPL2GrammarParser.GopListContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitGop([NotNull] EsperEPL2GrammarParser.GopContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitGopParams([NotNull] EsperEPL2GrammarParser.GopParamsContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitGopParamsItemList([NotNull] EsperEPL2GrammarParser.GopParamsItemListContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitGopParamsItem([NotNull] EsperEPL2GrammarParser.GopParamsItemContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitGopParamsItemMany([NotNull] EsperEPL2GrammarParser.GopParamsItemManyContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitGopParamsItemAs([NotNull] EsperEPL2GrammarParser.GopParamsItemAsContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitGopOut([NotNull] EsperEPL2GrammarParser.GopOutContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitGopOutItem([NotNull] EsperEPL2GrammarParser.GopOutItemContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitGopOutTypeList([NotNull] EsperEPL2GrammarParser.GopOutTypeListContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitGopOutTypeParam([NotNull] EsperEPL2GrammarParser.GopOutTypeParamContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitGopOutTypeItem([NotNull] EsperEPL2GrammarParser.GopOutTypeItemContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitGopDetail([NotNull] EsperEPL2GrammarParser.GopDetailContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitGopConfig([NotNull] EsperEPL2GrammarParser.GopConfigContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitCreateContextExpr([NotNull] EsperEPL2GrammarParser.CreateContextExprContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitCreateExpressionExpr([NotNull] EsperEPL2GrammarParser.CreateExpressionExprContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitCreateContextDetail([NotNull] EsperEPL2GrammarParser.CreateContextDetailContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitContextContextNested([NotNull] EsperEPL2GrammarParser.ContextContextNestedContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitCreateContextChoice([NotNull] EsperEPL2GrammarParser.CreateContextChoiceContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitCreateContextDistinct([NotNull] EsperEPL2GrammarParser.CreateContextDistinctContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitCreateContextRangePoint([NotNull] EsperEPL2GrammarParser.CreateContextRangePointContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitCreateContextFilter([NotNull] EsperEPL2GrammarParser.CreateContextFilterContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitCreateContextPartitionItem([NotNull] EsperEPL2GrammarParser.CreateContextPartitionItemContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitCreateContextCoalesceItem([NotNull] EsperEPL2GrammarParser.CreateContextCoalesceItemContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitCreateContextGroupItem([NotNull] EsperEPL2GrammarParser.CreateContextGroupItemContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitCreateSchemaQual([NotNull] EsperEPL2GrammarParser.CreateSchemaQualContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitVariantList([NotNull] EsperEPL2GrammarParser.VariantListContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitVariantListElement([NotNull] EsperEPL2GrammarParser.VariantListElementContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitIntoTableExpr([NotNull] EsperEPL2GrammarParser.IntoTableExprContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitInsertIntoExpr([NotNull] EsperEPL2GrammarParser.InsertIntoExprContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitColumnList([NotNull] EsperEPL2GrammarParser.ColumnListContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitFromClause([NotNull] EsperEPL2GrammarParser.FromClauseContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitRegularJoin([NotNull] EsperEPL2GrammarParser.RegularJoinContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitOuterJoinList([NotNull] EsperEPL2GrammarParser.OuterJoinListContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitOuterJoin([NotNull] EsperEPL2GrammarParser.OuterJoinContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitOuterJoinIdent([NotNull] EsperEPL2GrammarParser.OuterJoinIdentContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitOuterJoinIdentPair([NotNull] EsperEPL2GrammarParser.OuterJoinIdentPairContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitWhereClause([NotNull] EsperEPL2GrammarParser.WhereClauseContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitSelectClause([NotNull] EsperEPL2GrammarParser.SelectClauseContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitSelectionList([NotNull] EsperEPL2GrammarParser.SelectionListContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitSelectionListElement([NotNull] EsperEPL2GrammarParser.SelectionListElementContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitSelectionListElementExpr([NotNull] EsperEPL2GrammarParser.SelectionListElementExprContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitSelectionListElementAnno([NotNull] EsperEPL2GrammarParser.SelectionListElementAnnoContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitStreamSelector([NotNull] EsperEPL2GrammarParser.StreamSelectorContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitStreamExpression([NotNull] EsperEPL2GrammarParser.StreamExpressionContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitForExpr([NotNull] EsperEPL2GrammarParser.ForExprContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitPatternInclusionExpression([NotNull] EsperEPL2GrammarParser.PatternInclusionExpressionContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitDatabaseJoinExpression([NotNull] EsperEPL2GrammarParser.DatabaseJoinExpressionContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitMethodJoinExpression([NotNull] EsperEPL2GrammarParser.MethodJoinExpressionContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitViewExpressions([NotNull] EsperEPL2GrammarParser.ViewExpressionsContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitViewExpressionWNamespace([NotNull] EsperEPL2GrammarParser.ViewExpressionWNamespaceContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitViewExpressionOptNamespace([NotNull] EsperEPL2GrammarParser.ViewExpressionOptNamespaceContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitViewWParameters([NotNull] EsperEPL2GrammarParser.ViewWParametersContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitGroupByListExpr([NotNull] EsperEPL2GrammarParser.GroupByListExprContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitGroupByListChoice([NotNull] EsperEPL2GrammarParser.GroupByListChoiceContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitGroupByCubeOrRollup([NotNull] EsperEPL2GrammarParser.GroupByCubeOrRollupContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitGroupByGroupingSets([NotNull] EsperEPL2GrammarParser.GroupByGroupingSetsContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitGroupBySetsChoice([NotNull] EsperEPL2GrammarParser.GroupBySetsChoiceContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitGroupByCombinableExpr([NotNull] EsperEPL2GrammarParser.GroupByCombinableExprContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitOrderByListExpr([NotNull] EsperEPL2GrammarParser.OrderByListExprContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitOrderByListElement([NotNull] EsperEPL2GrammarParser.OrderByListElementContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitHavingClause([NotNull] EsperEPL2GrammarParser.HavingClauseContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitOutputLimit([NotNull] EsperEPL2GrammarParser.OutputLimitContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitOutputLimitAndTerm([NotNull] EsperEPL2GrammarParser.OutputLimitAndTermContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitOutputLimitAfter([NotNull] EsperEPL2GrammarParser.OutputLimitAfterContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitRowLimit([NotNull] EsperEPL2GrammarParser.RowLimitContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitCrontabLimitParameterSet([NotNull] EsperEPL2GrammarParser.CrontabLimitParameterSetContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitWhenClause([NotNull] EsperEPL2GrammarParser.WhenClauseContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitElseClause([NotNull] EsperEPL2GrammarParser.ElseClauseContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitMatchRecog([NotNull] EsperEPL2GrammarParser.MatchRecogContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitMatchRecogPartitionBy([NotNull] EsperEPL2GrammarParser.MatchRecogPartitionByContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitMatchRecogMeasures([NotNull] EsperEPL2GrammarParser.MatchRecogMeasuresContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitMatchRecogMeasureItem([NotNull] EsperEPL2GrammarParser.MatchRecogMeasureItemContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitMatchRecogMatchesSelection([NotNull] EsperEPL2GrammarParser.MatchRecogMatchesSelectionContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitMatchRecogPattern([NotNull] EsperEPL2GrammarParser.MatchRecogPatternContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitMatchRecogMatchesAfterSkip([NotNull] EsperEPL2GrammarParser.MatchRecogMatchesAfterSkipContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitMatchRecogMatchesInterval([NotNull] EsperEPL2GrammarParser.MatchRecogMatchesIntervalContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitMatchRecogPatternAlteration([NotNull] EsperEPL2GrammarParser.MatchRecogPatternAlterationContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitMatchRecogPatternConcat([NotNull] EsperEPL2GrammarParser.MatchRecogPatternConcatContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitMatchRecogPatternUnary([NotNull] EsperEPL2GrammarParser.MatchRecogPatternUnaryContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitMatchRecogPatternNested([NotNull] EsperEPL2GrammarParser.MatchRecogPatternNestedContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitMatchRecogPatternPermute([NotNull] EsperEPL2GrammarParser.MatchRecogPatternPermuteContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitMatchRecogPatternAtom([NotNull] EsperEPL2GrammarParser.MatchRecogPatternAtomContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitMatchRecogPatternRepeat([NotNull] EsperEPL2GrammarParser.MatchRecogPatternRepeatContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitMatchRecogDefine([NotNull] EsperEPL2GrammarParser.MatchRecogDefineContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitMatchRecogDefineItem([NotNull] EsperEPL2GrammarParser.MatchRecogDefineItemContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitExpression([NotNull] EsperEPL2GrammarParser.ExpressionContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitCaseExpression([NotNull] EsperEPL2GrammarParser.CaseExpressionContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitEvalOrExpression([NotNull] EsperEPL2GrammarParser.EvalOrExpressionContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitEvalAndExpression([NotNull] EsperEPL2GrammarParser.EvalAndExpressionContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitBitWiseExpression([NotNull] EsperEPL2GrammarParser.BitWiseExpressionContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitNegatedExpression([NotNull] EsperEPL2GrammarParser.NegatedExpressionContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitEvalEqualsExpression([NotNull] EsperEPL2GrammarParser.EvalEqualsExpressionContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitEvalRelationalExpression([NotNull] EsperEPL2GrammarParser.EvalRelationalExpressionContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitInSubSelectQuery([NotNull] EsperEPL2GrammarParser.InSubSelectQueryContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitConcatenationExpr([NotNull] EsperEPL2GrammarParser.ConcatenationExprContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitAdditiveExpression([NotNull] EsperEPL2GrammarParser.AdditiveExpressionContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitMultiplyExpression([NotNull] EsperEPL2GrammarParser.MultiplyExpressionContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitUnaryExpression([NotNull] EsperEPL2GrammarParser.UnaryExpressionContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitSubstitutionCanChain([NotNull] EsperEPL2GrammarParser.SubstitutionCanChainContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitChainedFunction([NotNull] EsperEPL2GrammarParser.ChainedFunctionContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitNewAssign([NotNull] EsperEPL2GrammarParser.NewAssignContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitRowSubSelectExpression([NotNull] EsperEPL2GrammarParser.RowSubSelectExpressionContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitSubSelectGroupExpression([NotNull] EsperEPL2GrammarParser.SubSelectGroupExpressionContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitExistsSubSelectExpression([NotNull] EsperEPL2GrammarParser.ExistsSubSelectExpressionContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitSubQueryExpr([NotNull] EsperEPL2GrammarParser.SubQueryExprContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitSubSelectFilterExpr([NotNull] EsperEPL2GrammarParser.SubSelectFilterExprContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitArrayExpression([NotNull] EsperEPL2GrammarParser.ArrayExpressionContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by the builtin_sum + /// labeled alternative in . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitBuiltin_sum([NotNull] EsperEPL2GrammarParser.Builtin_sumContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by the builtin_avg + /// labeled alternative in . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitBuiltin_avg([NotNull] EsperEPL2GrammarParser.Builtin_avgContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by the builtin_cnt + /// labeled alternative in . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitBuiltin_cnt([NotNull] EsperEPL2GrammarParser.Builtin_cntContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by the builtin_median + /// labeled alternative in . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitBuiltin_median([NotNull] EsperEPL2GrammarParser.Builtin_medianContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by the builtin_stddev + /// labeled alternative in . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitBuiltin_stddev([NotNull] EsperEPL2GrammarParser.Builtin_stddevContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by the builtin_avedev + /// labeled alternative in . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitBuiltin_avedev([NotNull] EsperEPL2GrammarParser.Builtin_avedevContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by the builtin_firstlastwindow + /// labeled alternative in . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitBuiltin_firstlastwindow([NotNull] EsperEPL2GrammarParser.Builtin_firstlastwindowContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by the builtin_coalesce + /// labeled alternative in . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitBuiltin_coalesce([NotNull] EsperEPL2GrammarParser.Builtin_coalesceContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by the builtin_prev + /// labeled alternative in . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitBuiltin_prev([NotNull] EsperEPL2GrammarParser.Builtin_prevContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by the builtin_prevtail + /// labeled alternative in . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitBuiltin_prevtail([NotNull] EsperEPL2GrammarParser.Builtin_prevtailContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by the builtin_prevcount + /// labeled alternative in . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitBuiltin_prevcount([NotNull] EsperEPL2GrammarParser.Builtin_prevcountContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by the builtin_prevwindow + /// labeled alternative in . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitBuiltin_prevwindow([NotNull] EsperEPL2GrammarParser.Builtin_prevwindowContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by the builtin_prior + /// labeled alternative in . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitBuiltin_prior([NotNull] EsperEPL2GrammarParser.Builtin_priorContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by the builtin_grouping + /// labeled alternative in . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitBuiltin_grouping([NotNull] EsperEPL2GrammarParser.Builtin_groupingContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by the builtin_groupingid + /// labeled alternative in . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitBuiltin_groupingid([NotNull] EsperEPL2GrammarParser.Builtin_groupingidContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by the builtin_instanceof + /// labeled alternative in . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitBuiltin_instanceof([NotNull] EsperEPL2GrammarParser.Builtin_instanceofContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by the builtin_typeof + /// labeled alternative in . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitBuiltin_typeof([NotNull] EsperEPL2GrammarParser.Builtin_typeofContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by the builtin_cast + /// labeled alternative in . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitBuiltin_cast([NotNull] EsperEPL2GrammarParser.Builtin_castContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by the builtin_exists + /// labeled alternative in . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitBuiltin_exists([NotNull] EsperEPL2GrammarParser.Builtin_existsContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by the builtin_currts + /// labeled alternative in . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitBuiltin_currts([NotNull] EsperEPL2GrammarParser.Builtin_currtsContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by the builtin_istream + /// labeled alternative in . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitBuiltin_istream([NotNull] EsperEPL2GrammarParser.Builtin_istreamContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitFirstLastWindowAggregation([NotNull] EsperEPL2GrammarParser.FirstLastWindowAggregationContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitEventPropertyOrLibFunction([NotNull] EsperEPL2GrammarParser.EventPropertyOrLibFunctionContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitLibFunction([NotNull] EsperEPL2GrammarParser.LibFunctionContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitLibFunctionWithClass([NotNull] EsperEPL2GrammarParser.LibFunctionWithClassContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitLibFunctionNoClass([NotNull] EsperEPL2GrammarParser.LibFunctionNoClassContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitFuncIdentTop([NotNull] EsperEPL2GrammarParser.FuncIdentTopContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitFuncIdentInner([NotNull] EsperEPL2GrammarParser.FuncIdentInnerContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitFuncIdentChained([NotNull] EsperEPL2GrammarParser.FuncIdentChainedContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitLibFunctionArgs([NotNull] EsperEPL2GrammarParser.LibFunctionArgsContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitLibFunctionArgItem([NotNull] EsperEPL2GrammarParser.LibFunctionArgItemContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitBetweenList([NotNull] EsperEPL2GrammarParser.BetweenListContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitPatternExpression([NotNull] EsperEPL2GrammarParser.PatternExpressionContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitFollowedByExpression([NotNull] EsperEPL2GrammarParser.FollowedByExpressionContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitFollowedByRepeat([NotNull] EsperEPL2GrammarParser.FollowedByRepeatContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitOrExpression([NotNull] EsperEPL2GrammarParser.OrExpressionContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitAndExpression([NotNull] EsperEPL2GrammarParser.AndExpressionContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitMatchUntilExpression([NotNull] EsperEPL2GrammarParser.MatchUntilExpressionContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitQualifyExpression([NotNull] EsperEPL2GrammarParser.QualifyExpressionContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitGuardPostFix([NotNull] EsperEPL2GrammarParser.GuardPostFixContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitDistinctExpressionList([NotNull] EsperEPL2GrammarParser.DistinctExpressionListContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitDistinctExpressionAtom([NotNull] EsperEPL2GrammarParser.DistinctExpressionAtomContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitAtomicExpression([NotNull] EsperEPL2GrammarParser.AtomicExpressionContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitObserverExpression([NotNull] EsperEPL2GrammarParser.ObserverExpressionContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitGuardWhereExpression([NotNull] EsperEPL2GrammarParser.GuardWhereExpressionContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitGuardWhileExpression([NotNull] EsperEPL2GrammarParser.GuardWhileExpressionContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitMatchUntilRange([NotNull] EsperEPL2GrammarParser.MatchUntilRangeContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitEventFilterExpression([NotNull] EsperEPL2GrammarParser.EventFilterExpressionContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitPropertyExpression([NotNull] EsperEPL2GrammarParser.PropertyExpressionContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitPropertyExpressionAtomic([NotNull] EsperEPL2GrammarParser.PropertyExpressionAtomicContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitPropertyExpressionSelect([NotNull] EsperEPL2GrammarParser.PropertyExpressionSelectContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitPropertySelectionList([NotNull] EsperEPL2GrammarParser.PropertySelectionListContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitPropertySelectionListElement([NotNull] EsperEPL2GrammarParser.PropertySelectionListElementContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitPropertyStreamSelector([NotNull] EsperEPL2GrammarParser.PropertyStreamSelectorContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitTypeExpressionAnnotation([NotNull] EsperEPL2GrammarParser.TypeExpressionAnnotationContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitPatternFilterExpression([NotNull] EsperEPL2GrammarParser.PatternFilterExpressionContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitPatternFilterAnnotation([NotNull] EsperEPL2GrammarParser.PatternFilterAnnotationContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitClassIdentifier([NotNull] EsperEPL2GrammarParser.ClassIdentifierContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitSlashIdentifier([NotNull] EsperEPL2GrammarParser.SlashIdentifierContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitExpressionListWithNamed([NotNull] EsperEPL2GrammarParser.ExpressionListWithNamedContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitExpressionListWithNamedWithTime([NotNull] EsperEPL2GrammarParser.ExpressionListWithNamedWithTimeContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitExpressionWithNamed([NotNull] EsperEPL2GrammarParser.ExpressionWithNamedContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitExpressionWithNamedWithTime([NotNull] EsperEPL2GrammarParser.ExpressionWithNamedWithTimeContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitExpressionNamedParameter([NotNull] EsperEPL2GrammarParser.ExpressionNamedParameterContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitExpressionNamedParameterWithTime([NotNull] EsperEPL2GrammarParser.ExpressionNamedParameterWithTimeContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitExpressionList([NotNull] EsperEPL2GrammarParser.ExpressionListContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitExpressionWithTimeList([NotNull] EsperEPL2GrammarParser.ExpressionWithTimeListContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitExpressionWithTime([NotNull] EsperEPL2GrammarParser.ExpressionWithTimeContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitExpressionWithTimeInclLast([NotNull] EsperEPL2GrammarParser.ExpressionWithTimeInclLastContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitExpressionQualifyable([NotNull] EsperEPL2GrammarParser.ExpressionQualifyableContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitLastWeekdayOperand([NotNull] EsperEPL2GrammarParser.LastWeekdayOperandContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitLastOperand([NotNull] EsperEPL2GrammarParser.LastOperandContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitFrequencyOperand([NotNull] EsperEPL2GrammarParser.FrequencyOperandContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitRangeOperand([NotNull] EsperEPL2GrammarParser.RangeOperandContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitLastOperator([NotNull] EsperEPL2GrammarParser.LastOperatorContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitWeekDayOperator([NotNull] EsperEPL2GrammarParser.WeekDayOperatorContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitNumericParameterList([NotNull] EsperEPL2GrammarParser.NumericParameterListContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitNumericListParameter([NotNull] EsperEPL2GrammarParser.NumericListParameterContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitEventProperty([NotNull] EsperEPL2GrammarParser.EventPropertyContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitEventPropertyAtomic([NotNull] EsperEPL2GrammarParser.EventPropertyAtomicContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitEventPropertyIdent([NotNull] EsperEPL2GrammarParser.EventPropertyIdentContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitKeywordAllowedIdent([NotNull] EsperEPL2GrammarParser.KeywordAllowedIdentContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitEscapableStr([NotNull] EsperEPL2GrammarParser.EscapableStrContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitEscapableIdent([NotNull] EsperEPL2GrammarParser.EscapableIdentContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitTimePeriod([NotNull] EsperEPL2GrammarParser.TimePeriodContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitYearPart([NotNull] EsperEPL2GrammarParser.YearPartContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitMonthPart([NotNull] EsperEPL2GrammarParser.MonthPartContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitWeekPart([NotNull] EsperEPL2GrammarParser.WeekPartContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitDayPart([NotNull] EsperEPL2GrammarParser.DayPartContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitHourPart([NotNull] EsperEPL2GrammarParser.HourPartContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitMinutePart([NotNull] EsperEPL2GrammarParser.MinutePartContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitSecondPart([NotNull] EsperEPL2GrammarParser.SecondPartContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitMillisecondPart([NotNull] EsperEPL2GrammarParser.MillisecondPartContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitMicrosecondPart([NotNull] EsperEPL2GrammarParser.MicrosecondPartContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitNumber([NotNull] EsperEPL2GrammarParser.NumberContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitSubstitution([NotNull] EsperEPL2GrammarParser.SubstitutionContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitConstant([NotNull] EsperEPL2GrammarParser.ConstantContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitNumberconstant([NotNull] EsperEPL2GrammarParser.NumberconstantContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitStringconstant([NotNull] EsperEPL2GrammarParser.StringconstantContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitJsonvalue([NotNull] EsperEPL2GrammarParser.JsonvalueContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitJsonobject([NotNull] EsperEPL2GrammarParser.JsonobjectContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitJsonarray([NotNull] EsperEPL2GrammarParser.JsonarrayContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitJsonelements([NotNull] EsperEPL2GrammarParser.JsonelementsContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitJsonmembers([NotNull] EsperEPL2GrammarParser.JsonmembersContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitJsonpair([NotNull] EsperEPL2GrammarParser.JsonpairContext context) { return VisitChildren(context); } +} +} // namespace com.espertech.esper.epl.generated diff --git a/NEsper.Core/NEsper.Core/epl/generated/EsperEPL2GrammarLexer.cs b/NEsper.Core/NEsper.Core/epl/generated/EsperEPL2GrammarLexer.cs new file mode 100755 index 000000000..2f975755e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/generated/EsperEPL2GrammarLexer.cs @@ -0,0 +1,1328 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// ANTLR Version: 4.6 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +// Generated from C:\Src\Espertech\NEsper-master\NEsper\grammar\EsperEPL2Grammar.g4 by ANTLR 4.6 + +// Unreachable code detected +#pragma warning disable 0162 +// The variable '...' is assigned but its value is never used +#pragma warning disable 0219 +// Missing XML comment for publicly visible type or member '...' +#pragma warning disable 1591 +// Ambiguous reference in cref attribute +#pragma warning disable 419 + +namespace com.espertech.esper.epl.generated { + + using System; + using System.Collections.Generic; + +using System; +using System.Text; +using Antlr4.Runtime; +using Antlr4.Runtime.Atn; +using Antlr4.Runtime.Misc; +using DFA = Antlr4.Runtime.Dfa.DFA; + +[System.CodeDom.Compiler.GeneratedCode("ANTLR", "4.6")] +[System.CLSCompliant(false)] +public partial class EsperEPL2GrammarLexer : Lexer { + protected static DFA[] decisionToDFA; + protected static PredictionContextCache sharedContextCache = new PredictionContextCache(); + public const int + CREATE=1, WINDOW=2, IN_SET=3, BETWEEN=4, LIKE=5, REGEXP=6, ESCAPE=7, OR_EXPR=8, + AND_EXPR=9, NOT_EXPR=10, EVERY_EXPR=11, EVERY_DISTINCT_EXPR=12, WHERE=13, + AS=14, SUM=15, AVG=16, MAX=17, MIN=18, COALESCE=19, MEDIAN=20, STDDEV=21, + AVEDEV=22, COUNT=23, SELECT=24, CASE=25, ELSE=26, WHEN=27, THEN=28, END=29, + FROM=30, OUTER=31, INNER=32, JOIN=33, LEFT=34, RIGHT=35, FULL=36, ON=37, + IS=38, BY=39, GROUP=40, HAVING=41, DISTINCT=42, ALL=43, ANY=44, SOME=45, + OUTPUT=46, EVENTS=47, FIRST=48, LAST=49, INSERT=50, INTO=51, VALUES=52, + ORDER=53, ASC=54, DESC=55, RSTREAM=56, ISTREAM=57, IRSTREAM=58, SCHEMA=59, + UNIDIRECTIONAL=60, RETAINUNION=61, RETAININTERSECTION=62, PATTERN=63, + SQL=64, METADATASQL=65, PREVIOUS=66, PREVIOUSTAIL=67, PREVIOUSCOUNT=68, + PREVIOUSWINDOW=69, PRIOR=70, EXISTS=71, WEEKDAY=72, LW=73, INSTANCEOF=74, + TYPEOF=75, CAST=76, CURRENT_TIMESTAMP=77, DELETE=78, SNAPSHOT=79, SET=80, + VARIABLE=81, TABLE=82, UNTIL=83, AT=84, INDEX=85, TIMEPERIOD_YEAR=86, + TIMEPERIOD_YEARS=87, TIMEPERIOD_MONTH=88, TIMEPERIOD_MONTHS=89, TIMEPERIOD_WEEK=90, + TIMEPERIOD_WEEKS=91, TIMEPERIOD_DAY=92, TIMEPERIOD_DAYS=93, TIMEPERIOD_HOUR=94, + TIMEPERIOD_HOURS=95, TIMEPERIOD_MINUTE=96, TIMEPERIOD_MINUTES=97, TIMEPERIOD_SEC=98, + TIMEPERIOD_SECOND=99, TIMEPERIOD_SECONDS=100, TIMEPERIOD_MILLISEC=101, + TIMEPERIOD_MILLISECOND=102, TIMEPERIOD_MILLISECONDS=103, TIMEPERIOD_MICROSEC=104, + TIMEPERIOD_MICROSECOND=105, TIMEPERIOD_MICROSECONDS=106, BOOLEAN_TRUE=107, + BOOLEAN_FALSE=108, VALUE_NULL=109, ROW_LIMIT_EXPR=110, OFFSET=111, UPDATE=112, + MATCH_RECOGNIZE=113, MATCH_RECOGNIZE_PERMUTE=114, MEASURES=115, DEFINE=116, + PARTITION=117, MATCHES=118, AFTER=119, FOR=120, WHILE=121, USING=122, + MERGE=123, MATCHED=124, EXPRESSIONDECL=125, NEWKW=126, START=127, CONTEXT=128, + INITIATED=129, TERMINATED=130, DATAFLOW=131, CUBE=132, ROLLUP=133, GROUPING=134, + GROUPING_ID=135, SETS=136, FOLLOWMAX_BEGIN=137, FOLLOWMAX_END=138, FOLLOWED_BY=139, + GOES=140, EQUALS=141, SQL_NE=142, QUESTION=143, LPAREN=144, RPAREN=145, + LBRACK=146, RBRACK=147, LCURLY=148, RCURLY=149, COLON=150, COMMA=151, + EQUAL=152, LNOT=153, BNOT=154, NOT_EQUAL=155, DIV=156, DIV_ASSIGN=157, + PLUS=158, PLUS_ASSIGN=159, INC=160, MINUS=161, MINUS_ASSIGN=162, DEC=163, + STAR=164, STAR_ASSIGN=165, MOD=166, MOD_ASSIGN=167, GE=168, GT=169, LE=170, + LT=171, BXOR=172, BXOR_ASSIGN=173, BOR=174, BOR_ASSIGN=175, LOR=176, BAND=177, + BAND_ASSIGN=178, LAND=179, SEMI=180, DOT=181, NUM_LONG=182, NUM_DOUBLE=183, + NUM_FLOAT=184, ESCAPECHAR=185, ESCAPEBACKTICK=186, ATCHAR=187, HASHCHAR=188, + WS=189, SL_COMMENT=190, ML_COMMENT=191, TICKED_STRING_LITERAL=192, QUOTED_STRING_LITERAL=193, + STRING_LITERAL=194, IDENT=195, IntegerLiteral=196, FloatingPointLiteral=197; + public static string[] modeNames = { + "DEFAULT_MODE" + }; + + public static readonly string[] ruleNames = { + "CREATE", "WINDOW", "IN_SET", "BETWEEN", "LIKE", "REGEXP", "ESCAPE", "OR_EXPR", + "AND_EXPR", "NOT_EXPR", "EVERY_EXPR", "EVERY_DISTINCT_EXPR", "WHERE", + "AS", "SUM", "AVG", "MAX", "MIN", "COALESCE", "MEDIAN", "STDDEV", "AVEDEV", + "COUNT", "SELECT", "CASE", "ELSE", "WHEN", "THEN", "END", "FROM", "OUTER", + "INNER", "JOIN", "LEFT", "RIGHT", "FULL", "ON", "IS", "BY", "GROUP", "HAVING", + "DISTINCT", "ALL", "ANY", "SOME", "OUTPUT", "EVENTS", "FIRST", "LAST", + "INSERT", "INTO", "VALUES", "ORDER", "ASC", "DESC", "RSTREAM", "ISTREAM", + "IRSTREAM", "SCHEMA", "UNIDIRECTIONAL", "RETAINUNION", "RETAININTERSECTION", + "PATTERN", "SQL", "METADATASQL", "PREVIOUS", "PREVIOUSTAIL", "PREVIOUSCOUNT", + "PREVIOUSWINDOW", "PRIOR", "EXISTS", "WEEKDAY", "LW", "INSTANCEOF", "TYPEOF", + "CAST", "CURRENT_TIMESTAMP", "DELETE", "SNAPSHOT", "SET", "VARIABLE", + "TABLE", "UNTIL", "AT", "INDEX", "TIMEPERIOD_YEAR", "TIMEPERIOD_YEARS", + "TIMEPERIOD_MONTH", "TIMEPERIOD_MONTHS", "TIMEPERIOD_WEEK", "TIMEPERIOD_WEEKS", + "TIMEPERIOD_DAY", "TIMEPERIOD_DAYS", "TIMEPERIOD_HOUR", "TIMEPERIOD_HOURS", + "TIMEPERIOD_MINUTE", "TIMEPERIOD_MINUTES", "TIMEPERIOD_SEC", "TIMEPERIOD_SECOND", + "TIMEPERIOD_SECONDS", "TIMEPERIOD_MILLISEC", "TIMEPERIOD_MILLISECOND", + "TIMEPERIOD_MILLISECONDS", "TIMEPERIOD_MICROSEC", "TIMEPERIOD_MICROSECOND", + "TIMEPERIOD_MICROSECONDS", "BOOLEAN_TRUE", "BOOLEAN_FALSE", "VALUE_NULL", + "ROW_LIMIT_EXPR", "OFFSET", "UPDATE", "MATCH_RECOGNIZE", "MATCH_RECOGNIZE_PERMUTE", + "MEASURES", "DEFINE", "PARTITION", "MATCHES", "AFTER", "FOR", "WHILE", + "USING", "MERGE", "MATCHED", "EXPRESSIONDECL", "NEWKW", "START", "CONTEXT", + "INITIATED", "TERMINATED", "DATAFLOW", "CUBE", "ROLLUP", "GROUPING", "GROUPING_ID", + "SETS", "FOLLOWMAX_BEGIN", "FOLLOWMAX_END", "FOLLOWED_BY", "GOES", "EQUALS", + "SQL_NE", "QUESTION", "LPAREN", "RPAREN", "LBRACK", "RBRACK", "LCURLY", + "RCURLY", "COLON", "COMMA", "EQUAL", "LNOT", "BNOT", "NOT_EQUAL", "DIV", + "DIV_ASSIGN", "PLUS", "PLUS_ASSIGN", "INC", "MINUS", "MINUS_ASSIGN", "DEC", + "STAR", "STAR_ASSIGN", "MOD", "MOD_ASSIGN", "GE", "GT", "LE", "LT", "BXOR", + "BXOR_ASSIGN", "BOR", "BOR_ASSIGN", "LOR", "BAND", "BAND_ASSIGN", "LAND", + "SEMI", "DOT", "NUM_LONG", "NUM_DOUBLE", "NUM_FLOAT", "ESCAPECHAR", "ESCAPEBACKTICK", + "ATCHAR", "HASHCHAR", "WS", "SL_COMMENT", "ML_COMMENT", "TICKED_STRING_LITERAL", + "QUOTED_STRING_LITERAL", "STRING_LITERAL", "EscapeSequence", "IDENT", + "IntegerLiteral", "FloatingPointLiteral", "OctalEscape", "UnicodeEscape", + "DecimalIntegerLiteral", "HexIntegerLiteral", "OctalIntegerLiteral", "BinaryIntegerLiteral", + "IntegerTypeSuffix", "DecimalNumeral", "Digits", "Digit", "NonZeroDigit", + "DigitOrUnderscore", "Underscores", "HexNumeral", "HexDigits", "HexDigit", + "HexDigitOrUnderscore", "OctalNumeral", "OctalDigits", "OctalDigit", "OctalDigitOrUnderscore", + "BinaryNumeral", "BinaryDigits", "BinaryDigit", "BinaryDigitOrUnderscore", + "DecimalFloatingPointLiteral", "ExponentPart", "ExponentIndicator", "SignedInteger", + "Sign", "FloatTypeSuffix", "HexadecimalFloatingPointLiteral", "HexSignificand", + "BinaryExponent", "BinaryExponentIndicator" + }; + + + // provide nice error messages + private System.Collections.Generic.Stack paraphrases = + new System.Collections.Generic.Stack(); + + // static information initialized once + private static System.Collections.Generic.IDictionary lexerTokenParaphrases = + new System.Collections.Generic.Dictionary(); + private static System.Collections.Generic.IDictionary parserTokenParaphrases = + new System.Collections.Generic.Dictionary(); + private static System.Collections.Generic.ISet parserKeywordSet = + new System.Collections.Generic.HashSet(); + private static System.Collections.Generic.ISet afterScriptTokens = + new System.Collections.Generic.HashSet(); + + private static readonly Object _iLock = new Object(); + + public System.Collections.Generic.Stack GetParaphrases() + { + return paraphrases; + } + + public System.Collections.Generic.ISet GetKeywords() + { + GetParserTokenParaphrases(); + return parserKeywordSet; + } + + public static System.Collections.Generic.IDictionary GetLexerTokenParaphrases() + { + lock(_iLock) + { + if (lexerTokenParaphrases.Count == 0) + { + lexerTokenParaphrases[IDENT] = "an identifier"; + lexerTokenParaphrases[FOLLOWED_BY] = "an followed-by '->'"; + lexerTokenParaphrases[EQUALS] = "an equals '='"; + lexerTokenParaphrases[SQL_NE] = "a sql-style not equals '<>'"; + lexerTokenParaphrases[QUESTION] = "a questionmark '?'"; + lexerTokenParaphrases[LPAREN] = "an opening parenthesis '('"; + lexerTokenParaphrases[RPAREN] = "a closing parenthesis ')'"; + lexerTokenParaphrases[LBRACK] = "a left angle bracket '['"; + lexerTokenParaphrases[RBRACK] = "a right angle bracket ']'"; + lexerTokenParaphrases[LCURLY] = "a left curly bracket '{'"; + lexerTokenParaphrases[RCURLY] = "a right curly bracket '}'"; + lexerTokenParaphrases[COLON] = "a colon ':'"; + lexerTokenParaphrases[COMMA] = "a comma ','"; + lexerTokenParaphrases[EQUAL] = "an equals compare '=='"; + lexerTokenParaphrases[LNOT] = "a not '!'"; + lexerTokenParaphrases[BNOT] = "a binary not '~'"; + lexerTokenParaphrases[NOT_EQUAL] = "a not equals '!='"; + lexerTokenParaphrases[DIV] = "a division operator '\'"; + lexerTokenParaphrases[DIV_ASSIGN] = "a division assign '/='"; + lexerTokenParaphrases[PLUS] = "a plus operator '+'"; + lexerTokenParaphrases[PLUS_ASSIGN] = "a plus assign '+='"; + lexerTokenParaphrases[INC] = "an increment operator '++'"; + lexerTokenParaphrases[MINUS] = "a minus '-'"; + lexerTokenParaphrases[MINUS_ASSIGN] = "a minus assign '-='"; + lexerTokenParaphrases[DEC] = "a decrement operator '--'"; + lexerTokenParaphrases[STAR] = "a star '*'"; + lexerTokenParaphrases[STAR_ASSIGN] = "a star assign '*='"; + lexerTokenParaphrases[MOD] = "a modulo"; + lexerTokenParaphrases[MOD_ASSIGN] = "a modulo assign"; + lexerTokenParaphrases[GE] = "a greater equals '>='"; + lexerTokenParaphrases[GT] = "a greater then '>'"; + lexerTokenParaphrases[LE] = "a less equals '<='"; + lexerTokenParaphrases[LT] = "a lesser then '<'"; + lexerTokenParaphrases[BXOR] = "a binary xor '^'"; + lexerTokenParaphrases[BXOR_ASSIGN] = "a binary xor assign '^='"; + lexerTokenParaphrases[BOR] = "a binary or '|'"; + lexerTokenParaphrases[BOR_ASSIGN] = "a binary or assign '|='"; + lexerTokenParaphrases[LOR] = "a logical or '||'"; + lexerTokenParaphrases[BAND] = "a binary and '&'"; + lexerTokenParaphrases[BAND_ASSIGN] = "a binary and assign '&='"; + lexerTokenParaphrases[LAND] = "a logical and '&&'"; + lexerTokenParaphrases[SEMI] = "a semicolon ';'"; + lexerTokenParaphrases[DOT] = "a dot '.'"; + } + } + + return lexerTokenParaphrases; + } + + public static System.Collections.Generic.IDictionary GetParserTokenParaphrases() + { + lock(_iLock) + { + if (parserTokenParaphrases.Count == 0) + { + parserTokenParaphrases[CREATE] = "'create'"; + parserTokenParaphrases[WINDOW] = "'window'"; + parserTokenParaphrases[IN_SET] = "'in'"; + parserTokenParaphrases[BETWEEN] = "'between'"; + parserTokenParaphrases[LIKE] = "'like'"; + parserTokenParaphrases[REGEXP] = "'regexp'"; + parserTokenParaphrases[ESCAPE] = "'escape'"; + parserTokenParaphrases[OR_EXPR] = "'or'"; + parserTokenParaphrases[AND_EXPR] = "'and'"; + parserTokenParaphrases[NOT_EXPR] = "'not'"; + parserTokenParaphrases[EVERY_EXPR] = "'every'"; + parserTokenParaphrases[EVERY_DISTINCT_EXPR] = "'every-distinct'"; + parserTokenParaphrases[WHERE] = "'where'"; + parserTokenParaphrases[AS] = "'as'"; + parserTokenParaphrases[SUM] = "'sum'"; + parserTokenParaphrases[AVG] = "'avg'"; + parserTokenParaphrases[MAX] = "'max'"; + parserTokenParaphrases[MIN] = "'min'"; + parserTokenParaphrases[COALESCE] = "'coalesce'"; + parserTokenParaphrases[MEDIAN] = "'median'"; + parserTokenParaphrases[STDDEV] = "'stddev'"; + parserTokenParaphrases[AVEDEV] = "'avedev'"; + parserTokenParaphrases[COUNT] = "'count'"; + parserTokenParaphrases[SELECT] = "'select'"; + parserTokenParaphrases[CASE] = "'case'"; + parserTokenParaphrases[ELSE] = "'else'"; + parserTokenParaphrases[WHEN] = "'when'"; + parserTokenParaphrases[THEN] = "'then'"; + parserTokenParaphrases[END] = "'end'"; + parserTokenParaphrases[FROM] = "'from'"; + parserTokenParaphrases[OUTER] = "'outer'"; + parserTokenParaphrases[INNER] = "'inner'"; + parserTokenParaphrases[JOIN] = "'join'"; + parserTokenParaphrases[LEFT] = "'left'"; + parserTokenParaphrases[RIGHT] = "'right'"; + parserTokenParaphrases[FULL] = "'full'"; + parserTokenParaphrases[ON] = "'on'"; + parserTokenParaphrases[IS] = "'is'"; + parserTokenParaphrases[BY] = "'by'"; + parserTokenParaphrases[GROUP] = "'group'"; + parserTokenParaphrases[HAVING] = "'having'"; + parserTokenParaphrases[ALL] = "'all'"; + parserTokenParaphrases[ANY] = "'any'"; + parserTokenParaphrases[SOME] = "'some'"; + parserTokenParaphrases[OUTPUT] = "'output'"; + parserTokenParaphrases[EVENTS] = "'events'"; + parserTokenParaphrases[FIRST] = "'first'"; + parserTokenParaphrases[LAST] = "'last'"; + parserTokenParaphrases[INSERT] = "'insert'"; + parserTokenParaphrases[INTO] = "'into'"; + parserTokenParaphrases[ORDER] = "'order'"; + parserTokenParaphrases[ASC] = "'asc'"; + parserTokenParaphrases[DESC] = "'desc'"; + parserTokenParaphrases[RSTREAM] = "'rstream'"; + parserTokenParaphrases[ISTREAM] = "'istream'"; + parserTokenParaphrases[IRSTREAM] = "'irstream'"; + parserTokenParaphrases[SCHEMA] = "'schema'"; + parserTokenParaphrases[UNIDIRECTIONAL] = "'unidirectional'"; + parserTokenParaphrases[RETAINUNION] = "'retain-union'"; + parserTokenParaphrases[RETAININTERSECTION] = "'retain-intersection'"; + parserTokenParaphrases[PATTERN] = "'pattern'"; + parserTokenParaphrases[SQL] = "'sql'"; + parserTokenParaphrases[METADATASQL] = "'metadatasql'"; + parserTokenParaphrases[PREVIOUS] = "'prev'"; + parserTokenParaphrases[PREVIOUSTAIL] = "'prevtail'"; + parserTokenParaphrases[PREVIOUSCOUNT] = "'prevcount'"; + parserTokenParaphrases[PREVIOUSWINDOW] = "'prevwindow'"; + parserTokenParaphrases[PRIOR] = "'prior'"; + parserTokenParaphrases[EXISTS] = "'exists'"; + parserTokenParaphrases[WEEKDAY] = "'weekday'"; + parserTokenParaphrases[LW] = "'lastweekday'"; + parserTokenParaphrases[INSTANCEOF] = "'instanceof'"; + parserTokenParaphrases[TYPEOF] = "'typeof'"; + parserTokenParaphrases[CAST] = "'cast'"; + parserTokenParaphrases[CURRENT_TIMESTAMP] = "'current_timestamp'"; + parserTokenParaphrases[DELETE] = "'delete'"; + parserTokenParaphrases[DISTINCT] = "'distinct'"; + parserTokenParaphrases[SNAPSHOT] = "'snapshot'"; + parserTokenParaphrases[SET] = "'set'"; + parserTokenParaphrases[VARIABLE] = "'variable'"; + parserTokenParaphrases[TABLE] = "'table'"; + parserTokenParaphrases[INDEX] = "'index'"; + parserTokenParaphrases[UNTIL] = "'until'"; + parserTokenParaphrases[AT] = "'at'"; + parserTokenParaphrases[TIMEPERIOD_YEAR] = "'year'"; + parserTokenParaphrases[TIMEPERIOD_YEARS] = "'years'"; + parserTokenParaphrases[TIMEPERIOD_MONTH] = "'month'"; + parserTokenParaphrases[TIMEPERIOD_MONTHS] = "'months'"; + parserTokenParaphrases[TIMEPERIOD_WEEK] = "'week'"; + parserTokenParaphrases[TIMEPERIOD_WEEKS] = "'weeks'"; + parserTokenParaphrases[TIMEPERIOD_DAY] = "'day'"; + parserTokenParaphrases[TIMEPERIOD_DAYS] = "'days'"; + parserTokenParaphrases[TIMEPERIOD_HOUR] = "'hour'"; + parserTokenParaphrases[TIMEPERIOD_HOURS] = "'hours'"; + parserTokenParaphrases[TIMEPERIOD_MINUTE] = "'minute'"; + parserTokenParaphrases[TIMEPERIOD_MINUTES] = "'minutes'"; + parserTokenParaphrases[TIMEPERIOD_SEC] = "'sec'"; + parserTokenParaphrases[TIMEPERIOD_SECOND] = "'second'"; + parserTokenParaphrases[TIMEPERIOD_SECONDS] = "'seconds'"; + parserTokenParaphrases[TIMEPERIOD_MILLISEC] = "'msec'"; + parserTokenParaphrases[TIMEPERIOD_MILLISECOND] = "'millisecond'"; + parserTokenParaphrases[TIMEPERIOD_MILLISECONDS] = "'milliseconds'"; + parserTokenParaphrases[TIMEPERIOD_MICROSEC] = "'usec'"; + parserTokenParaphrases[TIMEPERIOD_MICROSECOND] = "'microsecond'"; + parserTokenParaphrases[TIMEPERIOD_MICROSECONDS] = "'microseconds'"; + parserTokenParaphrases[BOOLEAN_TRUE] = "'true'"; + parserTokenParaphrases[BOOLEAN_FALSE] = "'false'"; + parserTokenParaphrases[VALUE_NULL] = "'null'"; + parserTokenParaphrases[ROW_LIMIT_EXPR] = "'limit'"; + parserTokenParaphrases[OFFSET] = "'offset'"; + parserTokenParaphrases[UPDATE] = "'update'"; + parserTokenParaphrases[MATCH_RECOGNIZE] = "'match_recognize'"; + parserTokenParaphrases[MEASURES] = "'measures'"; + parserTokenParaphrases[DEFINE] = "'define'"; + parserTokenParaphrases[PARTITION] = "'partition'"; + parserTokenParaphrases[MATCHES] = "'matches'"; + parserTokenParaphrases[AFTER] = "'after'"; + parserTokenParaphrases[FOR] = "'for'"; + parserTokenParaphrases[WHILE] = "'while'"; + parserTokenParaphrases[MERGE] = "'merge'"; + parserTokenParaphrases[MATCHED] = "'matched'"; + parserTokenParaphrases[CONTEXT] = "'context'"; + parserTokenParaphrases[START] = "'start'"; + parserTokenParaphrases[END] = "'end'"; + parserTokenParaphrases[INITIATED] = "'initiated'"; + parserTokenParaphrases[TERMINATED] = "'terminated'"; + parserTokenParaphrases[USING] = "'using'"; + parserTokenParaphrases[EXPRESSIONDECL] = "'expression'"; + parserTokenParaphrases[NEWKW] = "'new'"; + parserTokenParaphrases[DATAFLOW] = "'dataflow'"; + parserTokenParaphrases[VALUES] = "'values'"; + parserTokenParaphrases[CUBE] = "'cube'"; + parserTokenParaphrases[ROLLUP] = "'rollup'"; + parserTokenParaphrases[GROUPING] = "'grouping'"; + parserTokenParaphrases[GROUPING_ID] = "'grouping_id'"; + parserTokenParaphrases[SETS] = "'sets'"; + + parserKeywordSet = new HashSet( + parserTokenParaphrases.Values); + } + } + + return parserTokenParaphrases; + } + + public static System.Collections.Generic.ISet GetAfterScriptTokens() + { + if (afterScriptTokens.Count == 0) + { + afterScriptTokens.Add(CREATE); + afterScriptTokens.Add(EXPRESSIONDECL); + afterScriptTokens.Add(SELECT); + afterScriptTokens.Add(INSERT); + afterScriptTokens.Add(ON); + afterScriptTokens.Add(DELETE); + afterScriptTokens.Add(UPDATE); + afterScriptTokens.Add(ATCHAR); + } + + return afterScriptTokens; + } + + + public EsperEPL2GrammarLexer(ICharStream input) + : base(input) + { + Interpreter = new LexerATNSimulator(this, _ATN, decisionToDFA, sharedContextCache); + } + + private static readonly string[] _LiteralNames = { + null, "'create'", "'window'", "'in'", "'between'", "'like'", "'regexp'", + "'escape'", "'or'", "'and'", "'not'", "'every'", "'every-distinct'", "'where'", + "'as'", "'sum'", "'avg'", "'max'", "'min'", "'coalesce'", "'median'", + "'stddev'", "'avedev'", "'count'", "'select'", "'case'", "'else'", "'when'", + "'then'", "'end'", "'from'", "'outer'", "'inner'", "'join'", "'left'", + "'right'", "'full'", "'on'", "'is'", "'by'", "'group'", "'having'", "'distinct'", + "'all'", "'any'", "'some'", "'output'", "'events'", "'first'", "'last'", + "'insert'", "'into'", "'values'", "'order'", "'asc'", "'desc'", "'rstream'", + "'istream'", "'irstream'", "'schema'", "'unidirectional'", "'retain-union'", + "'retain-intersection'", "'pattern'", "'sql'", "'metadatasql'", "'prev'", + "'prevtail'", "'prevcount'", "'prevwindow'", "'prior'", "'exists'", "'weekday'", + "'lastweekday'", "'instanceof'", "'typeof'", "'cast'", "'current_timestamp'", + "'delete'", "'snapshot'", "'set'", "'variable'", "'table'", "'until'", + "'at'", "'index'", "'year'", "'years'", "'month'", "'months'", "'week'", + "'weeks'", "'day'", "'days'", "'hour'", "'hours'", "'minute'", "'minutes'", + "'sec'", "'second'", "'seconds'", "'msec'", "'millisecond'", "'milliseconds'", + "'usec'", "'microsecond'", "'microseconds'", "'true'", "'false'", "'null'", + "'limit'", "'offset'", "'update'", "'match_recognize'", "'match_recognize_permute'", + "'measures'", "'define'", "'partition'", "'matches'", "'after'", "'for'", + "'while'", "'using'", "'merge'", "'matched'", "'expression'", "'new'", + "'start'", "'context'", "'initiated'", "'terminated'", "'dataflow'", "'cube'", + "'rollup'", "'grouping'", "'grouping_id'", "'sets'", "'-['", "']>'", "'->'", + "'=>'", "'='", "'<>'", "'?'", "'('", "')'", "'['", "']'", "'{'", "'}'", + "':'", "','", "'=='", "'!'", "'~'", "'!='", "'/'", "'/='", "'+'", "'+='", + "'++'", "'-'", "'-='", "'--'", "'*'", "'*='", "'%'", "'%='", "'>='", "'>'", + "'<='", "'<'", "'^'", "'^='", "'|'", "'|='", "'||'", "'&'", "'&='", "'&&'", + "';'", "'.'", "'\\u18FF'", "'\\u18FE'", "'\\u18FD'", "'\\'", "'`'", "'@'", + "'#'" + }; + private static readonly string[] _SymbolicNames = { + null, "CREATE", "WINDOW", "IN_SET", "BETWEEN", "LIKE", "REGEXP", "ESCAPE", + "OR_EXPR", "AND_EXPR", "NOT_EXPR", "EVERY_EXPR", "EVERY_DISTINCT_EXPR", + "WHERE", "AS", "SUM", "AVG", "MAX", "MIN", "COALESCE", "MEDIAN", "STDDEV", + "AVEDEV", "COUNT", "SELECT", "CASE", "ELSE", "WHEN", "THEN", "END", "FROM", + "OUTER", "INNER", "JOIN", "LEFT", "RIGHT", "FULL", "ON", "IS", "BY", "GROUP", + "HAVING", "DISTINCT", "ALL", "ANY", "SOME", "OUTPUT", "EVENTS", "FIRST", + "LAST", "INSERT", "INTO", "VALUES", "ORDER", "ASC", "DESC", "RSTREAM", + "ISTREAM", "IRSTREAM", "SCHEMA", "UNIDIRECTIONAL", "RETAINUNION", "RETAININTERSECTION", + "PATTERN", "SQL", "METADATASQL", "PREVIOUS", "PREVIOUSTAIL", "PREVIOUSCOUNT", + "PREVIOUSWINDOW", "PRIOR", "EXISTS", "WEEKDAY", "LW", "INSTANCEOF", "TYPEOF", + "CAST", "CURRENT_TIMESTAMP", "DELETE", "SNAPSHOT", "SET", "VARIABLE", + "TABLE", "UNTIL", "AT", "INDEX", "TIMEPERIOD_YEAR", "TIMEPERIOD_YEARS", + "TIMEPERIOD_MONTH", "TIMEPERIOD_MONTHS", "TIMEPERIOD_WEEK", "TIMEPERIOD_WEEKS", + "TIMEPERIOD_DAY", "TIMEPERIOD_DAYS", "TIMEPERIOD_HOUR", "TIMEPERIOD_HOURS", + "TIMEPERIOD_MINUTE", "TIMEPERIOD_MINUTES", "TIMEPERIOD_SEC", "TIMEPERIOD_SECOND", + "TIMEPERIOD_SECONDS", "TIMEPERIOD_MILLISEC", "TIMEPERIOD_MILLISECOND", + "TIMEPERIOD_MILLISECONDS", "TIMEPERIOD_MICROSEC", "TIMEPERIOD_MICROSECOND", + "TIMEPERIOD_MICROSECONDS", "BOOLEAN_TRUE", "BOOLEAN_FALSE", "VALUE_NULL", + "ROW_LIMIT_EXPR", "OFFSET", "UPDATE", "MATCH_RECOGNIZE", "MATCH_RECOGNIZE_PERMUTE", + "MEASURES", "DEFINE", "PARTITION", "MATCHES", "AFTER", "FOR", "WHILE", + "USING", "MERGE", "MATCHED", "EXPRESSIONDECL", "NEWKW", "START", "CONTEXT", + "INITIATED", "TERMINATED", "DATAFLOW", "CUBE", "ROLLUP", "GROUPING", "GROUPING_ID", + "SETS", "FOLLOWMAX_BEGIN", "FOLLOWMAX_END", "FOLLOWED_BY", "GOES", "EQUALS", + "SQL_NE", "QUESTION", "LPAREN", "RPAREN", "LBRACK", "RBRACK", "LCURLY", + "RCURLY", "COLON", "COMMA", "EQUAL", "LNOT", "BNOT", "NOT_EQUAL", "DIV", + "DIV_ASSIGN", "PLUS", "PLUS_ASSIGN", "INC", "MINUS", "MINUS_ASSIGN", "DEC", + "STAR", "STAR_ASSIGN", "MOD", "MOD_ASSIGN", "GE", "GT", "LE", "LT", "BXOR", + "BXOR_ASSIGN", "BOR", "BOR_ASSIGN", "LOR", "BAND", "BAND_ASSIGN", "LAND", + "SEMI", "DOT", "NUM_LONG", "NUM_DOUBLE", "NUM_FLOAT", "ESCAPECHAR", "ESCAPEBACKTICK", + "ATCHAR", "HASHCHAR", "WS", "SL_COMMENT", "ML_COMMENT", "TICKED_STRING_LITERAL", + "QUOTED_STRING_LITERAL", "STRING_LITERAL", "IDENT", "IntegerLiteral", + "FloatingPointLiteral" + }; + public static readonly IVocabulary DefaultVocabulary = new Vocabulary(_LiteralNames, _SymbolicNames); + + [NotNull] + public override IVocabulary Vocabulary + { + get + { + return DefaultVocabulary; + } + } + + public override string GrammarFileName { get { return "EsperEPL2Grammar.g4"; } } + + public override string[] RuleNames { get { return ruleNames; } } + + public override string[] ModeNames { get { return modeNames; } } + + public override string SerializedAtn { get { return _serializedATN; } } + + static EsperEPL2GrammarLexer() { + decisionToDFA = new DFA[_ATN.NumberOfDecisions]; + for (int i = 0; i < _ATN.NumberOfDecisions; i++) { + decisionToDFA[i] = new DFA(_ATN.GetDecisionState(i), i); + } + } + private static string _serializedATN = _serializeATN(); + private static string _serializeATN() + { + StringBuilder sb = new StringBuilder(); + sb.Append("\x3\x430\xD6D1\x8206\xAD2D\x4417\xAEF1\x8D80\xAADD\x2\xC7"); + sb.Append("\x742\b\x1\x4\x2\t\x2\x4\x3\t\x3\x4\x4\t\x4\x4\x5\t\x5\x4\x6"); + sb.Append("\t\x6\x4\a\t\a\x4\b\t\b\x4\t\t\t\x4\n\t\n\x4\v\t\v\x4\f\t\f"); + sb.Append("\x4\r\t\r\x4\xE\t\xE\x4\xF\t\xF\x4\x10\t\x10\x4\x11\t\x11\x4"); + sb.Append("\x12\t\x12\x4\x13\t\x13\x4\x14\t\x14\x4\x15\t\x15\x4\x16\t\x16"); + sb.Append("\x4\x17\t\x17\x4\x18\t\x18\x4\x19\t\x19\x4\x1A\t\x1A\x4\x1B"); + sb.Append("\t\x1B\x4\x1C\t\x1C\x4\x1D\t\x1D\x4\x1E\t\x1E\x4\x1F\t\x1F\x4"); + sb.Append(" \t \x4!\t!\x4\"\t\"\x4#\t#\x4$\t$\x4%\t%\x4&\t&\x4\'\t\'\x4"); + sb.Append("(\t(\x4)\t)\x4*\t*\x4+\t+\x4,\t,\x4-\t-\x4.\t.\x4/\t/\x4\x30"); + sb.Append("\t\x30\x4\x31\t\x31\x4\x32\t\x32\x4\x33\t\x33\x4\x34\t\x34\x4"); + sb.Append("\x35\t\x35\x4\x36\t\x36\x4\x37\t\x37\x4\x38\t\x38\x4\x39\t\x39"); + sb.Append("\x4:\t:\x4;\t;\x4<\t<\x4=\t=\x4>\t>\x4?\t?\x4@\t@\x4\x41\t\x41"); + sb.Append("\x4\x42\t\x42\x4\x43\t\x43\x4\x44\t\x44\x4\x45\t\x45\x4\x46"); + sb.Append("\t\x46\x4G\tG\x4H\tH\x4I\tI\x4J\tJ\x4K\tK\x4L\tL\x4M\tM\x4N"); + sb.Append("\tN\x4O\tO\x4P\tP\x4Q\tQ\x4R\tR\x4S\tS\x4T\tT\x4U\tU\x4V\tV"); + sb.Append("\x4W\tW\x4X\tX\x4Y\tY\x4Z\tZ\x4[\t[\x4\\\t\\\x4]\t]\x4^\t^\x4"); + sb.Append("_\t_\x4`\t`\x4\x61\t\x61\x4\x62\t\x62\x4\x63\t\x63\x4\x64\t"); + sb.Append("\x64\x4\x65\t\x65\x4\x66\t\x66\x4g\tg\x4h\th\x4i\ti\x4j\tj\x4"); + sb.Append("k\tk\x4l\tl\x4m\tm\x4n\tn\x4o\to\x4p\tp\x4q\tq\x4r\tr\x4s\t"); + sb.Append("s\x4t\tt\x4u\tu\x4v\tv\x4w\tw\x4x\tx\x4y\ty\x4z\tz\x4{\t{\x4"); + sb.Append("|\t|\x4}\t}\x4~\t~\x4\x7F\t\x7F\x4\x80\t\x80\x4\x81\t\x81\x4"); + sb.Append("\x82\t\x82\x4\x83\t\x83\x4\x84\t\x84\x4\x85\t\x85\x4\x86\t\x86"); + sb.Append("\x4\x87\t\x87\x4\x88\t\x88\x4\x89\t\x89\x4\x8A\t\x8A\x4\x8B"); + sb.Append("\t\x8B\x4\x8C\t\x8C\x4\x8D\t\x8D\x4\x8E\t\x8E\x4\x8F\t\x8F\x4"); + sb.Append("\x90\t\x90\x4\x91\t\x91\x4\x92\t\x92\x4\x93\t\x93\x4\x94\t\x94"); + sb.Append("\x4\x95\t\x95\x4\x96\t\x96\x4\x97\t\x97\x4\x98\t\x98\x4\x99"); + sb.Append("\t\x99\x4\x9A\t\x9A\x4\x9B\t\x9B\x4\x9C\t\x9C\x4\x9D\t\x9D\x4"); + sb.Append("\x9E\t\x9E\x4\x9F\t\x9F\x4\xA0\t\xA0\x4\xA1\t\xA1\x4\xA2\t\xA2"); + sb.Append("\x4\xA3\t\xA3\x4\xA4\t\xA4\x4\xA5\t\xA5\x4\xA6\t\xA6\x4\xA7"); + sb.Append("\t\xA7\x4\xA8\t\xA8\x4\xA9\t\xA9\x4\xAA\t\xAA\x4\xAB\t\xAB\x4"); + sb.Append("\xAC\t\xAC\x4\xAD\t\xAD\x4\xAE\t\xAE\x4\xAF\t\xAF\x4\xB0\t\xB0"); + sb.Append("\x4\xB1\t\xB1\x4\xB2\t\xB2\x4\xB3\t\xB3\x4\xB4\t\xB4\x4\xB5"); + sb.Append("\t\xB5\x4\xB6\t\xB6\x4\xB7\t\xB7\x4\xB8\t\xB8\x4\xB9\t\xB9\x4"); + sb.Append("\xBA\t\xBA\x4\xBB\t\xBB\x4\xBC\t\xBC\x4\xBD\t\xBD\x4\xBE\t\xBE"); + sb.Append("\x4\xBF\t\xBF\x4\xC0\t\xC0\x4\xC1\t\xC1\x4\xC2\t\xC2\x4\xC3"); + sb.Append("\t\xC3\x4\xC4\t\xC4\x4\xC5\t\xC5\x4\xC6\t\xC6\x4\xC7\t\xC7\x4"); + sb.Append("\xC8\t\xC8\x4\xC9\t\xC9\x4\xCA\t\xCA\x4\xCB\t\xCB\x4\xCC\t\xCC"); + sb.Append("\x4\xCD\t\xCD\x4\xCE\t\xCE\x4\xCF\t\xCF\x4\xD0\t\xD0\x4\xD1"); + sb.Append("\t\xD1\x4\xD2\t\xD2\x4\xD3\t\xD3\x4\xD4\t\xD4\x4\xD5\t\xD5\x4"); + sb.Append("\xD6\t\xD6\x4\xD7\t\xD7\x4\xD8\t\xD8\x4\xD9\t\xD9\x4\xDA\t\xDA"); + sb.Append("\x4\xDB\t\xDB\x4\xDC\t\xDC\x4\xDD\t\xDD\x4\xDE\t\xDE\x4\xDF"); + sb.Append("\t\xDF\x4\xE0\t\xE0\x4\xE1\t\xE1\x4\xE2\t\xE2\x4\xE3\t\xE3\x4"); + sb.Append("\xE4\t\xE4\x4\xE5\t\xE5\x4\xE6\t\xE6\x4\xE7\t\xE7\x4\xE8\t\xE8"); + sb.Append("\x4\xE9\t\xE9\x4\xEA\t\xEA\x3\x2\x3\x2\x3\x2\x3\x2\x3\x2\x3"); + sb.Append("\x2\x3\x2\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x4\x3"); + sb.Append("\x4\x3\x4\x3\x5\x3\x5\x3\x5\x3\x5\x3\x5\x3\x5\x3\x5\x3\x5\x3"); + sb.Append("\x6\x3\x6\x3\x6\x3\x6\x3\x6\x3\a\x3\a\x3\a\x3\a\x3\a\x3\a\x3"); + sb.Append("\a\x3\b\x3\b\x3\b\x3\b\x3\b\x3\b\x3\b\x3\t\x3\t\x3\t\x3\n\x3"); + sb.Append("\n\x3\n\x3\n\x3\v\x3\v\x3\v\x3\v\x3\f\x3\f\x3\f\x3\f\x3\f\x3"); + sb.Append("\f\x3\r\x3\r\x3\r\x3\r\x3\r\x3\r\x3\r\x3\r\x3\r\x3\r\x3\r\x3"); + sb.Append("\r\x3\r\x3\r\x3\r\x3\xE\x3\xE\x3\xE\x3\xE\x3\xE\x3\xE\x3\xF"); + sb.Append("\x3\xF\x3\xF\x3\x10\x3\x10\x3\x10\x3\x10\x3\x11\x3\x11\x3\x11"); + sb.Append("\x3\x11\x3\x12\x3\x12\x3\x12\x3\x12\x3\x13\x3\x13\x3\x13\x3"); + sb.Append("\x13\x3\x14\x3\x14\x3\x14\x3\x14\x3\x14\x3\x14\x3\x14\x3\x14"); + sb.Append("\x3\x14\x3\x15\x3\x15\x3\x15\x3\x15\x3\x15\x3\x15\x3\x15\x3"); + sb.Append("\x16\x3\x16\x3\x16\x3\x16\x3\x16\x3\x16\x3\x16\x3\x17\x3\x17"); + sb.Append("\x3\x17\x3\x17\x3\x17\x3\x17\x3\x17\x3\x18\x3\x18\x3\x18\x3"); + sb.Append("\x18\x3\x18\x3\x18\x3\x19\x3\x19\x3\x19\x3\x19\x3\x19\x3\x19"); + sb.Append("\x3\x19\x3\x1A\x3\x1A\x3\x1A\x3\x1A\x3\x1A\x3\x1B\x3\x1B\x3"); + sb.Append("\x1B\x3\x1B\x3\x1B\x3\x1C\x3\x1C\x3\x1C\x3\x1C\x3\x1C\x3\x1D"); + sb.Append("\x3\x1D\x3\x1D\x3\x1D\x3\x1D\x3\x1E\x3\x1E\x3\x1E\x3\x1E\x3"); + sb.Append("\x1F\x3\x1F\x3\x1F\x3\x1F\x3\x1F\x3 \x3 \x3 \x3 \x3 \x3 \x3"); + sb.Append("!\x3!\x3!\x3!\x3!\x3!\x3\"\x3\"\x3\"\x3\"\x3\"\x3#\x3#\x3#\x3"); + sb.Append("#\x3#\x3$\x3$\x3$\x3$\x3$\x3$\x3%\x3%\x3%\x3%\x3%\x3&\x3&\x3"); + sb.Append("&\x3\'\x3\'\x3\'\x3(\x3(\x3(\x3)\x3)\x3)\x3)\x3)\x3)\x3*\x3"); + sb.Append("*\x3*\x3*\x3*\x3*\x3*\x3+\x3+\x3+\x3+\x3+\x3+\x3+\x3+\x3+\x3"); + sb.Append(",\x3,\x3,\x3,\x3-\x3-\x3-\x3-\x3.\x3.\x3.\x3.\x3.\x3/\x3/\x3"); + sb.Append("/\x3/\x3/\x3/\x3/\x3\x30\x3\x30\x3\x30\x3\x30\x3\x30\x3\x30"); + sb.Append("\x3\x30\x3\x31\x3\x31\x3\x31\x3\x31\x3\x31\x3\x31\x3\x32\x3"); + sb.Append("\x32\x3\x32\x3\x32\x3\x32\x3\x33\x3\x33\x3\x33\x3\x33\x3\x33"); + sb.Append("\x3\x33\x3\x33\x3\x34\x3\x34\x3\x34\x3\x34\x3\x34\x3\x35\x3"); + sb.Append("\x35\x3\x35\x3\x35\x3\x35\x3\x35\x3\x35\x3\x36\x3\x36\x3\x36"); + sb.Append("\x3\x36\x3\x36\x3\x36\x3\x37\x3\x37\x3\x37\x3\x37\x3\x38\x3"); + sb.Append("\x38\x3\x38\x3\x38\x3\x38\x3\x39\x3\x39\x3\x39\x3\x39\x3\x39"); + sb.Append("\x3\x39\x3\x39\x3\x39\x3:\x3:\x3:\x3:\x3:\x3:\x3:\x3:\x3;\x3"); + sb.Append(";\x3;\x3;\x3;\x3;\x3;\x3;\x3;\x3<\x3<\x3<\x3<\x3<\x3<\x3<\x3"); + sb.Append("=\x3=\x3=\x3=\x3=\x3=\x3=\x3=\x3=\x3=\x3=\x3=\x3=\x3=\x3=\x3"); + sb.Append(">\x3>\x3>\x3>\x3>\x3>\x3>\x3>\x3>\x3>\x3>\x3>\x3>\x3?\x3?\x3"); + sb.Append("?\x3?\x3?\x3?\x3?\x3?\x3?\x3?\x3?\x3?\x3?\x3?\x3?\x3?\x3?\x3"); + sb.Append("?\x3?\x3?\x3@\x3@\x3@\x3@\x3@\x3@\x3@\x3@\x3\x41\x3\x41\x3\x41"); + sb.Append("\x3\x41\x3\x42\x3\x42\x3\x42\x3\x42\x3\x42\x3\x42\x3\x42\x3"); + sb.Append("\x42\x3\x42\x3\x42\x3\x42\x3\x42\x3\x43\x3\x43\x3\x43\x3\x43"); + sb.Append("\x3\x43\x3\x44\x3\x44\x3\x44\x3\x44\x3\x44\x3\x44\x3\x44\x3"); + sb.Append("\x44\x3\x44\x3\x45\x3\x45\x3\x45\x3\x45\x3\x45\x3\x45\x3\x45"); + sb.Append("\x3\x45\x3\x45\x3\x45\x3\x46\x3\x46\x3\x46\x3\x46\x3\x46\x3"); + sb.Append("\x46\x3\x46\x3\x46\x3\x46\x3\x46\x3\x46\x3G\x3G\x3G\x3G\x3G"); + sb.Append("\x3G\x3H\x3H\x3H\x3H\x3H\x3H\x3H\x3I\x3I\x3I\x3I\x3I\x3I\x3"); + sb.Append("I\x3I\x3J\x3J\x3J\x3J\x3J\x3J\x3J\x3J\x3J\x3J\x3J\x3J\x3K\x3"); + sb.Append("K\x3K\x3K\x3K\x3K\x3K\x3K\x3K\x3K\x3K\x3L\x3L\x3L\x3L\x3L\x3"); + sb.Append("L\x3L\x3M\x3M\x3M\x3M\x3M\x3N\x3N\x3N\x3N\x3N\x3N\x3N\x3N\x3"); + sb.Append("N\x3N\x3N\x3N\x3N\x3N\x3N\x3N\x3N\x3N\x3O\x3O\x3O\x3O\x3O\x3"); + sb.Append("O\x3O\x3P\x3P\x3P\x3P\x3P\x3P\x3P\x3P\x3P\x3Q\x3Q\x3Q\x3Q\x3"); + sb.Append("R\x3R\x3R\x3R\x3R\x3R\x3R\x3R\x3R\x3S\x3S\x3S\x3S\x3S\x3S\x3"); + sb.Append("T\x3T\x3T\x3T\x3T\x3T\x3U\x3U\x3U\x3V\x3V\x3V\x3V\x3V\x3V\x3"); + sb.Append("W\x3W\x3W\x3W\x3W\x3X\x3X\x3X\x3X\x3X\x3X\x3Y\x3Y\x3Y\x3Y\x3"); + sb.Append("Y\x3Y\x3Z\x3Z\x3Z\x3Z\x3Z\x3Z\x3Z\x3[\x3[\x3[\x3[\x3[\x3\\\x3"); + sb.Append("\\\x3\\\x3\\\x3\\\x3\\\x3]\x3]\x3]\x3]\x3^\x3^\x3^\x3^\x3^\x3"); + sb.Append("_\x3_\x3_\x3_\x3_\x3`\x3`\x3`\x3`\x3`\x3`\x3\x61\x3\x61\x3\x61"); + sb.Append("\x3\x61\x3\x61\x3\x61\x3\x61\x3\x62\x3\x62\x3\x62\x3\x62\x3"); + sb.Append("\x62\x3\x62\x3\x62\x3\x62\x3\x63\x3\x63\x3\x63\x3\x63\x3\x64"); + sb.Append("\x3\x64\x3\x64\x3\x64\x3\x64\x3\x64\x3\x64\x3\x65\x3\x65\x3"); + sb.Append("\x65\x3\x65\x3\x65\x3\x65\x3\x65\x3\x65\x3\x66\x3\x66\x3\x66"); + sb.Append("\x3\x66\x3\x66\x3g\x3g\x3g\x3g\x3g\x3g\x3g\x3g\x3g\x3g\x3g\x3"); + sb.Append("g\x3h\x3h\x3h\x3h\x3h\x3h\x3h\x3h\x3h\x3h\x3h\x3h\x3h\x3i\x3"); + sb.Append("i\x3i\x3i\x3i\x3j\x3j\x3j\x3j\x3j\x3j\x3j\x3j\x3j\x3j\x3j\x3"); + sb.Append("j\x3k\x3k\x3k\x3k\x3k\x3k\x3k\x3k\x3k\x3k\x3k\x3k\x3k\x3l\x3"); + sb.Append("l\x3l\x3l\x3l\x3m\x3m\x3m\x3m\x3m\x3m\x3n\x3n\x3n\x3n\x3n\x3"); + sb.Append("o\x3o\x3o\x3o\x3o\x3o\x3p\x3p\x3p\x3p\x3p\x3p\x3p\x3q\x3q\x3"); + sb.Append("q\x3q\x3q\x3q\x3q\x3r\x3r\x3r\x3r\x3r\x3r\x3r\x3r\x3r\x3r\x3"); + sb.Append("r\x3r\x3r\x3r\x3r\x3r\x3s\x3s\x3s\x3s\x3s\x3s\x3s\x3s\x3s\x3"); + sb.Append("s\x3s\x3s\x3s\x3s\x3s\x3s\x3s\x3s\x3s\x3s\x3s\x3s\x3s\x3s\x3"); + sb.Append("t\x3t\x3t\x3t\x3t\x3t\x3t\x3t\x3t\x3u\x3u\x3u\x3u\x3u\x3u\x3"); + sb.Append("u\x3v\x3v\x3v\x3v\x3v\x3v\x3v\x3v\x3v\x3v\x3w\x3w\x3w\x3w\x3"); + sb.Append("w\x3w\x3w\x3w\x3x\x3x\x3x\x3x\x3x\x3x\x3y\x3y\x3y\x3y\x3z\x3"); + sb.Append("z\x3z\x3z\x3z\x3z\x3{\x3{\x3{\x3{\x3{\x3{\x3|\x3|\x3|\x3|\x3"); + sb.Append("|\x3|\x3}\x3}\x3}\x3}\x3}\x3}\x3}\x3}\x3~\x3~\x3~\x3~\x3~\x3"); + sb.Append("~\x3~\x3~\x3~\x3~\x3~\x3\x7F\x3\x7F\x3\x7F\x3\x7F\x3\x80\x3"); + sb.Append("\x80\x3\x80\x3\x80\x3\x80\x3\x80\x3\x81\x3\x81\x3\x81\x3\x81"); + sb.Append("\x3\x81\x3\x81\x3\x81\x3\x81\x3\x82\x3\x82\x3\x82\x3\x82\x3"); + sb.Append("\x82\x3\x82\x3\x82\x3\x82\x3\x82\x3\x82\x3\x83\x3\x83\x3\x83"); + sb.Append("\x3\x83\x3\x83\x3\x83\x3\x83\x3\x83\x3\x83\x3\x83\x3\x83\x3"); + sb.Append("\x84\x3\x84\x3\x84\x3\x84\x3\x84\x3\x84\x3\x84\x3\x84\x3\x84"); + sb.Append("\x3\x85\x3\x85\x3\x85\x3\x85\x3\x85\x3\x86\x3\x86\x3\x86\x3"); + sb.Append("\x86\x3\x86\x3\x86\x3\x86\x3\x87\x3\x87\x3\x87\x3\x87\x3\x87"); + sb.Append("\x3\x87\x3\x87\x3\x87\x3\x87\x3\x88\x3\x88\x3\x88\x3\x88\x3"); + sb.Append("\x88\x3\x88\x3\x88\x3\x88\x3\x88\x3\x88\x3\x88\x3\x88\x3\x89"); + sb.Append("\x3\x89\x3\x89\x3\x89\x3\x89\x3\x8A\x3\x8A\x3\x8A\x3\x8B\x3"); + sb.Append("\x8B\x3\x8B\x3\x8C\x3\x8C\x3\x8C\x3\x8D\x3\x8D\x3\x8D\x3\x8E"); + sb.Append("\x3\x8E\x3\x8F\x3\x8F\x3\x8F\x3\x90\x3\x90\x3\x91\x3\x91\x3"); + sb.Append("\x92\x3\x92\x3\x93\x3\x93\x3\x94\x3\x94\x3\x95\x3\x95\x3\x96"); + sb.Append("\x3\x96\x3\x97\x3\x97\x3\x98\x3\x98\x3\x99\x3\x99\x3\x99\x3"); + sb.Append("\x9A\x3\x9A\x3\x9B\x3\x9B\x3\x9C\x3\x9C\x3\x9C\x3\x9D\x3\x9D"); + sb.Append("\x3\x9E\x3\x9E\x3\x9E\x3\x9F\x3\x9F\x3\xA0\x3\xA0\x3\xA0\x3"); + sb.Append("\xA1\x3\xA1\x3\xA1\x3\xA2\x3\xA2\x3\xA3\x3\xA3\x3\xA3\x3\xA4"); + sb.Append("\x3\xA4\x3\xA4\x3\xA5\x3\xA5\x3\xA6\x3\xA6\x3\xA6\x3\xA7\x3"); + sb.Append("\xA7\x3\xA8\x3\xA8\x3\xA8\x3\xA9\x3\xA9\x3\xA9\x3\xAA\x3\xAA"); + sb.Append("\x3\xAB\x3\xAB\x3\xAB\x3\xAC\x3\xAC\x3\xAD\x3\xAD\x3\xAE\x3"); + sb.Append("\xAE\x3\xAE\x3\xAF\x3\xAF\x3\xB0\x3\xB0\x3\xB0\x3\xB1\x3\xB1"); + sb.Append("\x3\xB1\x3\xB2\x3\xB2\x3\xB3\x3\xB3\x3\xB3\x3\xB4\x3\xB4\x3"); + sb.Append("\xB4\x3\xB5\x3\xB5\x3\xB6\x3\xB6\x3\xB7\x3\xB7\x3\xB8\x3\xB8"); + sb.Append("\x3\xB9\x3\xB9\x3\xBA\x3\xBA\x3\xBB\x3\xBB\x3\xBC\x3\xBC\x3"); + sb.Append("\xBD\x3\xBD\x3\xBE\x6\xBE\x618\n\xBE\r\xBE\xE\xBE\x619\x3\xBE"); + sb.Append("\x3\xBE\x3\xBF\x3\xBF\x3\xBF\x3\xBF\a\xBF\x622\n\xBF\f\xBF\xE"); + sb.Append("\xBF\x625\v\xBF\x3\xBF\x3\xBF\x3\xBF\x5\xBF\x62A\n\xBF\x5\xBF"); + sb.Append("\x62C\n\xBF\x3\xBF\x3\xBF\x3\xC0\x3\xC0\x3\xC0\x3\xC0\a\xC0"); + sb.Append("\x634\n\xC0\f\xC0\xE\xC0\x637\v\xC0\x3\xC0\x3\xC0\x3\xC0\x3"); + sb.Append("\xC0\x3\xC0\x3\xC1\x3\xC1\x3\xC1\a\xC1\x641\n\xC1\f\xC1\xE\xC1"); + sb.Append("\x644\v\xC1\x3\xC1\x3\xC1\x3\xC2\x3\xC2\x3\xC2\a\xC2\x64B\n"); + sb.Append("\xC2\f\xC2\xE\xC2\x64E\v\xC2\x3\xC2\x3\xC2\x3\xC3\x3\xC3\x3"); + sb.Append("\xC3\a\xC3\x655\n\xC3\f\xC3\xE\xC3\x658\v\xC3\x3\xC3\x3\xC3"); + sb.Append("\x3\xC4\x3\xC4\x3\xC4\x3\xC4\x3\xC4\x5\xC4\x661\n\xC4\x3\xC5"); + sb.Append("\x3\xC5\a\xC5\x665\n\xC5\f\xC5\xE\xC5\x668\v\xC5\x3\xC6\x3\xC6"); + sb.Append("\x3\xC6\x3\xC6\x5\xC6\x66E\n\xC6\x3\xC7\x3\xC7\x5\xC7\x672\n"); + sb.Append("\xC7\x3\xC8\x3\xC8\x3\xC8\x3\xC8\x3\xC8\x3\xC8\x3\xC8\x3\xC8"); + sb.Append("\x3\xC8\x5\xC8\x67D\n\xC8\x3\xC9\x3\xC9\x3\xC9\x3\xC9\x3\xC9"); + sb.Append("\x3\xC9\x3\xC9\x3\xCA\x3\xCA\x5\xCA\x688\n\xCA\x3\xCB\x3\xCB"); + sb.Append("\x5\xCB\x68C\n\xCB\x3\xCC\x3\xCC\x5\xCC\x690\n\xCC\x3\xCD\x3"); + sb.Append("\xCD\x5\xCD\x694\n\xCD\x3\xCE\x3\xCE\x3\xCF\x3\xCF\a\xCF\x69A"); + sb.Append("\n\xCF\f\xCF\xE\xCF\x69D\v\xCF\x3\xCF\x3\xCF\x5\xCF\x6A1\n\xCF"); + sb.Append("\x3\xCF\x3\xCF\x3\xCF\x5\xCF\x6A6\n\xCF\x5\xCF\x6A8\n\xCF\x3"); + sb.Append("\xD0\x3\xD0\a\xD0\x6AC\n\xD0\f\xD0\xE\xD0\x6AF\v\xD0\x3\xD0"); + sb.Append("\x5\xD0\x6B2\n\xD0\x3\xD1\x3\xD1\x5\xD1\x6B6\n\xD1\x3\xD2\x3"); + sb.Append("\xD2\x3\xD3\x3\xD3\x5\xD3\x6BC\n\xD3\x3\xD4\x6\xD4\x6BF\n\xD4"); + sb.Append("\r\xD4\xE\xD4\x6C0\x3\xD5\x3\xD5\x3\xD5\x3\xD5\x3\xD6\x3\xD6"); + sb.Append("\a\xD6\x6C9\n\xD6\f\xD6\xE\xD6\x6CC\v\xD6\x3\xD6\x5\xD6\x6CF"); + sb.Append("\n\xD6\x3\xD7\x3\xD7\x3\xD8\x3\xD8\x5\xD8\x6D5\n\xD8\x3\xD9"); + sb.Append("\x3\xD9\x5\xD9\x6D9\n\xD9\x3\xD9\x3\xD9\x3\xDA\x3\xDA\a\xDA"); + sb.Append("\x6DF\n\xDA\f\xDA\xE\xDA\x6E2\v\xDA\x3\xDA\x5\xDA\x6E5\n\xDA"); + sb.Append("\x3\xDB\x3\xDB\x3\xDC\x3\xDC\x5\xDC\x6EB\n\xDC\x3\xDD\x3\xDD"); + sb.Append("\x3\xDD\x3\xDD\x3\xDE\x3\xDE\a\xDE\x6F3\n\xDE\f\xDE\xE\xDE\x6F6"); + sb.Append("\v\xDE\x3\xDE\x5\xDE\x6F9\n\xDE\x3\xDF\x3\xDF\x3\xE0\x3\xE0"); + sb.Append("\x5\xE0\x6FF\n\xE0\x3\xE1\x3\xE1\x3\xE1\x5\xE1\x704\n\xE1\x3"); + sb.Append("\xE1\x5\xE1\x707\n\xE1\x3\xE1\x5\xE1\x70A\n\xE1\x3\xE1\x3\xE1"); + sb.Append("\x3\xE1\x5\xE1\x70F\n\xE1\x3\xE1\x5\xE1\x712\n\xE1\x3\xE1\x3"); + sb.Append("\xE1\x3\xE1\x5\xE1\x717\n\xE1\x3\xE1\x3\xE1\x3\xE1\x5\xE1\x71C"); + sb.Append("\n\xE1\x3\xE2\x3\xE2\x3\xE2\x3\xE3\x3\xE3\x3\xE4\x5\xE4\x724"); + sb.Append("\n\xE4\x3\xE4\x3\xE4\x3\xE5\x3\xE5\x3\xE6\x3\xE6\x3\xE7\x3\xE7"); + sb.Append("\x3\xE7\x5\xE7\x72F\n\xE7\x3\xE8\x3\xE8\x5\xE8\x733\n\xE8\x3"); + sb.Append("\xE8\x3\xE8\x3\xE8\x5\xE8\x738\n\xE8\x3\xE8\x3\xE8\x5\xE8\x73C"); + sb.Append("\n\xE8\x3\xE9\x3\xE9\x3\xE9\x3\xEA\x3\xEA\x3\x635\x2\xEB\x3"); + sb.Append("\x3\x5\x4\a\x5\t\x6\v\a\r\b\xF\t\x11\n\x13\v\x15\f\x17\r\x19"); + sb.Append("\xE\x1B\xF\x1D\x10\x1F\x11!\x12#\x13%\x14\'\x15)\x16+\x17-\x18"); + sb.Append("/\x19\x31\x1A\x33\x1B\x35\x1C\x37\x1D\x39\x1E;\x1F= ?!\x41\""); + sb.Append("\x43#\x45$G%I&K\'M(O)Q*S+U,W-Y.[/]\x30_\x31\x61\x32\x63\x33"); + sb.Append("\x65\x34g\x35i\x36k\x37m\x38o\x39q:s;u{?}@\x7F\x41\x81"); + sb.Append("\x42\x83\x43\x85\x44\x87\x45\x89\x46\x8BG\x8DH\x8FI\x91J\x93"); + sb.Append("K\x95L\x97M\x99N\x9BO\x9DP\x9FQ\xA1R\xA3S\xA5T\xA7U\xA9V\xAB"); + sb.Append("W\xADX\xAFY\xB1Z\xB3[\xB5\\\xB7]\xB9^\xBB_\xBD`\xBF\x61\xC1"); + sb.Append("\x62\xC3\x63\xC5\x64\xC7\x65\xC9\x66\xCBg\xCDh\xCFi\xD1j\xD3"); + sb.Append("k\xD5l\xD7m\xD9n\xDBo\xDDp\xDFq\xE1r\xE3s\xE5t\xE7u\xE9v\xEB"); + sb.Append("w\xEDx\xEFy\xF1z\xF3{\xF5|\xF7}\xF9~\xFB\x7F\xFD\x80\xFF\x81"); + sb.Append("\x101\x82\x103\x83\x105\x84\x107\x85\x109\x86\x10B\x87\x10D"); + sb.Append("\x88\x10F\x89\x111\x8A\x113\x8B\x115\x8C\x117\x8D\x119\x8E\x11B"); + sb.Append("\x8F\x11D\x90\x11F\x91\x121\x92\x123\x93\x125\x94\x127\x95\x129"); + sb.Append("\x96\x12B\x97\x12D\x98\x12F\x99\x131\x9A\x133\x9B\x135\x9C\x137"); + sb.Append("\x9D\x139\x9E\x13B\x9F\x13D\xA0\x13F\xA1\x141\xA2\x143\xA3\x145"); + sb.Append("\xA4\x147\xA5\x149\xA6\x14B\xA7\x14D\xA8\x14F\xA9\x151\xAA\x153"); + sb.Append("\xAB\x155\xAC\x157\xAD\x159\xAE\x15B\xAF\x15D\xB0\x15F\xB1\x161"); + sb.Append("\xB2\x163\xB3\x165\xB4\x167\xB5\x169\xB6\x16B\xB7\x16D\xB8\x16F"); + sb.Append("\xB9\x171\xBA\x173\xBB\x175\xBC\x177\xBD\x179\xBE\x17B\xBF\x17D"); + sb.Append("\xC0\x17F\xC1\x181\xC2\x183\xC3\x185\xC4\x187\x2\x189\xC5\x18B"); + sb.Append("\xC6\x18D\xC7\x18F\x2\x191\x2\x193\x2\x195\x2\x197\x2\x199\x2"); + sb.Append("\x19B\x2\x19D\x2\x19F\x2\x1A1\x2\x1A3\x2\x1A5\x2\x1A7\x2\x1A9"); + sb.Append("\x2\x1AB\x2\x1AD\x2\x1AF\x2\x1B1\x2\x1B3\x2\x1B5\x2\x1B7\x2"); + sb.Append("\x1B9\x2\x1BB\x2\x1BD\x2\x1BF\x2\x1C1\x2\x1C3\x2\x1C5\x2\x1C7"); + sb.Append("\x2\x1C9\x2\x1CB\x2\x1CD\x2\x1CF\x2\x1D1\x2\x1D3\x2\x3\x2\x15"); + sb.Append("\x5\x2\v\f\xE\xF\"\"\x4\x2\f\f\xF\xF\x4\x2^^\x62\x62\x4\x2)"); + sb.Append(")^^\x4\x2$$^^\n\x2$$))^^\x64\x64hhppttvv\x4\x2\x61\x61\x63|"); + sb.Append("\x6\x2&&\x32;\x61\x61\x63|\x4\x2NNnn\x3\x2\x33;\x4\x2ZZzz\x5"); + sb.Append("\x2\x32;\x43H\x63h\x3\x2\x32\x39\x4\x2\x44\x44\x64\x64\x3\x2"); + sb.Append("\x32\x33\x4\x2GGgg\x4\x2--//\x6\x2\x46\x46HH\x66\x66hh\x4\x2"); + sb.Append("RRrr\x758\x2\x3\x3\x2\x2\x2\x2\x5\x3\x2\x2\x2\x2\a\x3\x2\x2"); + sb.Append("\x2\x2\t\x3\x2\x2\x2\x2\v\x3\x2\x2\x2\x2\r\x3\x2\x2\x2\x2\xF"); + sb.Append("\x3\x2\x2\x2\x2\x11\x3\x2\x2\x2\x2\x13\x3\x2\x2\x2\x2\x15\x3"); + sb.Append("\x2\x2\x2\x2\x17\x3\x2\x2\x2\x2\x19\x3\x2\x2\x2\x2\x1B\x3\x2"); + sb.Append("\x2\x2\x2\x1D\x3\x2\x2\x2\x2\x1F\x3\x2\x2\x2\x2!\x3\x2\x2\x2"); + sb.Append("\x2#\x3\x2\x2\x2\x2%\x3\x2\x2\x2\x2\'\x3\x2\x2\x2\x2)\x3\x2"); + sb.Append("\x2\x2\x2+\x3\x2\x2\x2\x2-\x3\x2\x2\x2\x2/\x3\x2\x2\x2\x2\x31"); + sb.Append("\x3\x2\x2\x2\x2\x33\x3\x2\x2\x2\x2\x35\x3\x2\x2\x2\x2\x37\x3"); + sb.Append("\x2\x2\x2\x2\x39\x3\x2\x2\x2\x2;\x3\x2\x2\x2\x2=\x3\x2\x2\x2"); + sb.Append("\x2?\x3\x2\x2\x2\x2\x41\x3\x2\x2\x2\x2\x43\x3\x2\x2\x2\x2\x45"); + sb.Append("\x3\x2\x2\x2\x2G\x3\x2\x2\x2\x2I\x3\x2\x2\x2\x2K\x3\x2\x2\x2"); + sb.Append("\x2M\x3\x2\x2\x2\x2O\x3\x2\x2\x2\x2Q\x3\x2\x2\x2\x2S\x3\x2\x2"); + sb.Append("\x2\x2U\x3\x2\x2\x2\x2W\x3\x2\x2\x2\x2Y\x3\x2\x2\x2\x2[\x3\x2"); + sb.Append("\x2\x2\x2]\x3\x2\x2\x2\x2_\x3\x2\x2\x2\x2\x61\x3\x2\x2\x2\x2"); + sb.Append("\x63\x3\x2\x2\x2\x2\x65\x3\x2\x2\x2\x2g\x3\x2\x2\x2\x2i\x3\x2"); + sb.Append("\x2\x2\x2k\x3\x2\x2\x2\x2m\x3\x2\x2\x2\x2o\x3\x2\x2\x2\x2q\x3"); + sb.Append("\x2\x2\x2\x2s\x3\x2\x2\x2\x2u\x3\x2\x2\x2\x2w\x3\x2\x2\x2\x2"); + sb.Append("y\x3\x2\x2\x2\x2{\x3\x2\x2\x2\x2}\x3\x2\x2\x2\x2\x7F\x3\x2\x2"); + sb.Append("\x2\x2\x81\x3\x2\x2\x2\x2\x83\x3\x2\x2\x2\x2\x85\x3\x2\x2\x2"); + sb.Append("\x2\x87\x3\x2\x2\x2\x2\x89\x3\x2\x2\x2\x2\x8B\x3\x2\x2\x2\x2"); + sb.Append("\x8D\x3\x2\x2\x2\x2\x8F\x3\x2\x2\x2\x2\x91\x3\x2\x2\x2\x2\x93"); + sb.Append("\x3\x2\x2\x2\x2\x95\x3\x2\x2\x2\x2\x97\x3\x2\x2\x2\x2\x99\x3"); + sb.Append("\x2\x2\x2\x2\x9B\x3\x2\x2\x2\x2\x9D\x3\x2\x2\x2\x2\x9F\x3\x2"); + sb.Append("\x2\x2\x2\xA1\x3\x2\x2\x2\x2\xA3\x3\x2\x2\x2\x2\xA5\x3\x2\x2"); + sb.Append("\x2\x2\xA7\x3\x2\x2\x2\x2\xA9\x3\x2\x2\x2\x2\xAB\x3\x2\x2\x2"); + sb.Append("\x2\xAD\x3\x2\x2\x2\x2\xAF\x3\x2\x2\x2\x2\xB1\x3\x2\x2\x2\x2"); + sb.Append("\xB3\x3\x2\x2\x2\x2\xB5\x3\x2\x2\x2\x2\xB7\x3\x2\x2\x2\x2\xB9"); + sb.Append("\x3\x2\x2\x2\x2\xBB\x3\x2\x2\x2\x2\xBD\x3\x2\x2\x2\x2\xBF\x3"); + sb.Append("\x2\x2\x2\x2\xC1\x3\x2\x2\x2\x2\xC3\x3\x2\x2\x2\x2\xC5\x3\x2"); + sb.Append("\x2\x2\x2\xC7\x3\x2\x2\x2\x2\xC9\x3\x2\x2\x2\x2\xCB\x3\x2\x2"); + sb.Append("\x2\x2\xCD\x3\x2\x2\x2\x2\xCF\x3\x2\x2\x2\x2\xD1\x3\x2\x2\x2"); + sb.Append("\x2\xD3\x3\x2\x2\x2\x2\xD5\x3\x2\x2\x2\x2\xD7\x3\x2\x2\x2\x2"); + sb.Append("\xD9\x3\x2\x2\x2\x2\xDB\x3\x2\x2\x2\x2\xDD\x3\x2\x2\x2\x2\xDF"); + sb.Append("\x3\x2\x2\x2\x2\xE1\x3\x2\x2\x2\x2\xE3\x3\x2\x2\x2\x2\xE5\x3"); + sb.Append("\x2\x2\x2\x2\xE7\x3\x2\x2\x2\x2\xE9\x3\x2\x2\x2\x2\xEB\x3\x2"); + sb.Append("\x2\x2\x2\xED\x3\x2\x2\x2\x2\xEF\x3\x2\x2\x2\x2\xF1\x3\x2\x2"); + sb.Append("\x2\x2\xF3\x3\x2\x2\x2\x2\xF5\x3\x2\x2\x2\x2\xF7\x3\x2\x2\x2"); + sb.Append("\x2\xF9\x3\x2\x2\x2\x2\xFB\x3\x2\x2\x2\x2\xFD\x3\x2\x2\x2\x2"); + sb.Append("\xFF\x3\x2\x2\x2\x2\x101\x3\x2\x2\x2\x2\x103\x3\x2\x2\x2\x2"); + sb.Append("\x105\x3\x2\x2\x2\x2\x107\x3\x2\x2\x2\x2\x109\x3\x2\x2\x2\x2"); + sb.Append("\x10B\x3\x2\x2\x2\x2\x10D\x3\x2\x2\x2\x2\x10F\x3\x2\x2\x2\x2"); + sb.Append("\x111\x3\x2\x2\x2\x2\x113\x3\x2\x2\x2\x2\x115\x3\x2\x2\x2\x2"); + sb.Append("\x117\x3\x2\x2\x2\x2\x119\x3\x2\x2\x2\x2\x11B\x3\x2\x2\x2\x2"); + sb.Append("\x11D\x3\x2\x2\x2\x2\x11F\x3\x2\x2\x2\x2\x121\x3\x2\x2\x2\x2"); + sb.Append("\x123\x3\x2\x2\x2\x2\x125\x3\x2\x2\x2\x2\x127\x3\x2\x2\x2\x2"); + sb.Append("\x129\x3\x2\x2\x2\x2\x12B\x3\x2\x2\x2\x2\x12D\x3\x2\x2\x2\x2"); + sb.Append("\x12F\x3\x2\x2\x2\x2\x131\x3\x2\x2\x2\x2\x133\x3\x2\x2\x2\x2"); + sb.Append("\x135\x3\x2\x2\x2\x2\x137\x3\x2\x2\x2\x2\x139\x3\x2\x2\x2\x2"); + sb.Append("\x13B\x3\x2\x2\x2\x2\x13D\x3\x2\x2\x2\x2\x13F\x3\x2\x2\x2\x2"); + sb.Append("\x141\x3\x2\x2\x2\x2\x143\x3\x2\x2\x2\x2\x145\x3\x2\x2\x2\x2"); + sb.Append("\x147\x3\x2\x2\x2\x2\x149\x3\x2\x2\x2\x2\x14B\x3\x2\x2\x2\x2"); + sb.Append("\x14D\x3\x2\x2\x2\x2\x14F\x3\x2\x2\x2\x2\x151\x3\x2\x2\x2\x2"); + sb.Append("\x153\x3\x2\x2\x2\x2\x155\x3\x2\x2\x2\x2\x157\x3\x2\x2\x2\x2"); + sb.Append("\x159\x3\x2\x2\x2\x2\x15B\x3\x2\x2\x2\x2\x15D\x3\x2\x2\x2\x2"); + sb.Append("\x15F\x3\x2\x2\x2\x2\x161\x3\x2\x2\x2\x2\x163\x3\x2\x2\x2\x2"); + sb.Append("\x165\x3\x2\x2\x2\x2\x167\x3\x2\x2\x2\x2\x169\x3\x2\x2\x2\x2"); + sb.Append("\x16B\x3\x2\x2\x2\x2\x16D\x3\x2\x2\x2\x2\x16F\x3\x2\x2\x2\x2"); + sb.Append("\x171\x3\x2\x2\x2\x2\x173\x3\x2\x2\x2\x2\x175\x3\x2\x2\x2\x2"); + sb.Append("\x177\x3\x2\x2\x2\x2\x179\x3\x2\x2\x2\x2\x17B\x3\x2\x2\x2\x2"); + sb.Append("\x17D\x3\x2\x2\x2\x2\x17F\x3\x2\x2\x2\x2\x181\x3\x2\x2\x2\x2"); + sb.Append("\x183\x3\x2\x2\x2\x2\x185\x3\x2\x2\x2\x2\x189\x3\x2\x2\x2\x2"); + sb.Append("\x18B\x3\x2\x2\x2\x2\x18D\x3\x2\x2\x2\x3\x1D5\x3\x2\x2\x2\x5"); + sb.Append("\x1DC\x3\x2\x2\x2\a\x1E3\x3\x2\x2\x2\t\x1E6\x3\x2\x2\x2\v\x1EE"); + sb.Append("\x3\x2\x2\x2\r\x1F3\x3\x2\x2\x2\xF\x1FA\x3\x2\x2\x2\x11\x201"); + sb.Append("\x3\x2\x2\x2\x13\x204\x3\x2\x2\x2\x15\x208\x3\x2\x2\x2\x17\x20C"); + sb.Append("\x3\x2\x2\x2\x19\x212\x3\x2\x2\x2\x1B\x221\x3\x2\x2\x2\x1D\x227"); + sb.Append("\x3\x2\x2\x2\x1F\x22A\x3\x2\x2\x2!\x22E\x3\x2\x2\x2#\x232\x3"); + sb.Append("\x2\x2\x2%\x236\x3\x2\x2\x2\'\x23A\x3\x2\x2\x2)\x243\x3\x2\x2"); + sb.Append("\x2+\x24A\x3\x2\x2\x2-\x251\x3\x2\x2\x2/\x258\x3\x2\x2\x2\x31"); + sb.Append("\x25E\x3\x2\x2\x2\x33\x265\x3\x2\x2\x2\x35\x26A\x3\x2\x2\x2"); + sb.Append("\x37\x26F\x3\x2\x2\x2\x39\x274\x3\x2\x2\x2;\x279\x3\x2\x2\x2"); + sb.Append("=\x27D\x3\x2\x2\x2?\x282\x3\x2\x2\x2\x41\x288\x3\x2\x2\x2\x43"); + sb.Append("\x28E\x3\x2\x2\x2\x45\x293\x3\x2\x2\x2G\x298\x3\x2\x2\x2I\x29E"); + sb.Append("\x3\x2\x2\x2K\x2A3\x3\x2\x2\x2M\x2A6\x3\x2\x2\x2O\x2A9\x3\x2"); + sb.Append("\x2\x2Q\x2AC\x3\x2\x2\x2S\x2B2\x3\x2\x2\x2U\x2B9\x3\x2\x2\x2"); + sb.Append("W\x2C2\x3\x2\x2\x2Y\x2C6\x3\x2\x2\x2[\x2CA\x3\x2\x2\x2]\x2CF"); + sb.Append("\x3\x2\x2\x2_\x2D6\x3\x2\x2\x2\x61\x2DD\x3\x2\x2\x2\x63\x2E3"); + sb.Append("\x3\x2\x2\x2\x65\x2E8\x3\x2\x2\x2g\x2EF\x3\x2\x2\x2i\x2F4\x3"); + sb.Append("\x2\x2\x2k\x2FB\x3\x2\x2\x2m\x301\x3\x2\x2\x2o\x305\x3\x2\x2"); + sb.Append("\x2q\x30A\x3\x2\x2\x2s\x312\x3\x2\x2\x2u\x31A\x3\x2\x2\x2w\x323"); + sb.Append("\x3\x2\x2\x2y\x32A\x3\x2\x2\x2{\x339\x3\x2\x2\x2}\x346\x3\x2"); + sb.Append("\x2\x2\x7F\x35A\x3\x2\x2\x2\x81\x362\x3\x2\x2\x2\x83\x366\x3"); + sb.Append("\x2\x2\x2\x85\x372\x3\x2\x2\x2\x87\x377\x3\x2\x2\x2\x89\x380"); + sb.Append("\x3\x2\x2\x2\x8B\x38A\x3\x2\x2\x2\x8D\x395\x3\x2\x2\x2\x8F\x39B"); + sb.Append("\x3\x2\x2\x2\x91\x3A2\x3\x2\x2\x2\x93\x3AA\x3\x2\x2\x2\x95\x3B6"); + sb.Append("\x3\x2\x2\x2\x97\x3C1\x3\x2\x2\x2\x99\x3C8\x3\x2\x2\x2\x9B\x3CD"); + sb.Append("\x3\x2\x2\x2\x9D\x3DF\x3\x2\x2\x2\x9F\x3E6\x3\x2\x2\x2\xA1\x3EF"); + sb.Append("\x3\x2\x2\x2\xA3\x3F3\x3\x2\x2\x2\xA5\x3FC\x3\x2\x2\x2\xA7\x402"); + sb.Append("\x3\x2\x2\x2\xA9\x408\x3\x2\x2\x2\xAB\x40B\x3\x2\x2\x2\xAD\x411"); + sb.Append("\x3\x2\x2\x2\xAF\x416\x3\x2\x2\x2\xB1\x41C\x3\x2\x2\x2\xB3\x422"); + sb.Append("\x3\x2\x2\x2\xB5\x429\x3\x2\x2\x2\xB7\x42E\x3\x2\x2\x2\xB9\x434"); + sb.Append("\x3\x2\x2\x2\xBB\x438\x3\x2\x2\x2\xBD\x43D\x3\x2\x2\x2\xBF\x442"); + sb.Append("\x3\x2\x2\x2\xC1\x448\x3\x2\x2\x2\xC3\x44F\x3\x2\x2\x2\xC5\x457"); + sb.Append("\x3\x2\x2\x2\xC7\x45B\x3\x2\x2\x2\xC9\x462\x3\x2\x2\x2\xCB\x46A"); + sb.Append("\x3\x2\x2\x2\xCD\x46F\x3\x2\x2\x2\xCF\x47B\x3\x2\x2\x2\xD1\x488"); + sb.Append("\x3\x2\x2\x2\xD3\x48D\x3\x2\x2\x2\xD5\x499\x3\x2\x2\x2\xD7\x4A6"); + sb.Append("\x3\x2\x2\x2\xD9\x4AB\x3\x2\x2\x2\xDB\x4B1\x3\x2\x2\x2\xDD\x4B6"); + sb.Append("\x3\x2\x2\x2\xDF\x4BC\x3\x2\x2\x2\xE1\x4C3\x3\x2\x2\x2\xE3\x4CA"); + sb.Append("\x3\x2\x2\x2\xE5\x4DA\x3\x2\x2\x2\xE7\x4F2\x3\x2\x2\x2\xE9\x4FB"); + sb.Append("\x3\x2\x2\x2\xEB\x502\x3\x2\x2\x2\xED\x50C\x3\x2\x2\x2\xEF\x514"); + sb.Append("\x3\x2\x2\x2\xF1\x51A\x3\x2\x2\x2\xF3\x51E\x3\x2\x2\x2\xF5\x524"); + sb.Append("\x3\x2\x2\x2\xF7\x52A\x3\x2\x2\x2\xF9\x530\x3\x2\x2\x2\xFB\x538"); + sb.Append("\x3\x2\x2\x2\xFD\x543\x3\x2\x2\x2\xFF\x547\x3\x2\x2\x2\x101"); + sb.Append("\x54D\x3\x2\x2\x2\x103\x555\x3\x2\x2\x2\x105\x55F\x3\x2\x2\x2"); + sb.Append("\x107\x56A\x3\x2\x2\x2\x109\x573\x3\x2\x2\x2\x10B\x578\x3\x2"); + sb.Append("\x2\x2\x10D\x57F\x3\x2\x2\x2\x10F\x588\x3\x2\x2\x2\x111\x594"); + sb.Append("\x3\x2\x2\x2\x113\x599\x3\x2\x2\x2\x115\x59C\x3\x2\x2\x2\x117"); + sb.Append("\x59F\x3\x2\x2\x2\x119\x5A2\x3\x2\x2\x2\x11B\x5A5\x3\x2\x2\x2"); + sb.Append("\x11D\x5A7\x3\x2\x2\x2\x11F\x5AA\x3\x2\x2\x2\x121\x5AC\x3\x2"); + sb.Append("\x2\x2\x123\x5AE\x3\x2\x2\x2\x125\x5B0\x3\x2\x2\x2\x127\x5B2"); + sb.Append("\x3\x2\x2\x2\x129\x5B4\x3\x2\x2\x2\x12B\x5B6\x3\x2\x2\x2\x12D"); + sb.Append("\x5B8\x3\x2\x2\x2\x12F\x5BA\x3\x2\x2\x2\x131\x5BC\x3\x2\x2\x2"); + sb.Append("\x133\x5BF\x3\x2\x2\x2\x135\x5C1\x3\x2\x2\x2\x137\x5C3\x3\x2"); + sb.Append("\x2\x2\x139\x5C6\x3\x2\x2\x2\x13B\x5C8\x3\x2\x2\x2\x13D\x5CB"); + sb.Append("\x3\x2\x2\x2\x13F\x5CD\x3\x2\x2\x2\x141\x5D0\x3\x2\x2\x2\x143"); + sb.Append("\x5D3\x3\x2\x2\x2\x145\x5D5\x3\x2\x2\x2\x147\x5D8\x3\x2\x2\x2"); + sb.Append("\x149\x5DB\x3\x2\x2\x2\x14B\x5DD\x3\x2\x2\x2\x14D\x5E0\x3\x2"); + sb.Append("\x2\x2\x14F\x5E2\x3\x2\x2\x2\x151\x5E5\x3\x2\x2\x2\x153\x5E8"); + sb.Append("\x3\x2\x2\x2\x155\x5EA\x3\x2\x2\x2\x157\x5ED\x3\x2\x2\x2\x159"); + sb.Append("\x5EF\x3\x2\x2\x2\x15B\x5F1\x3\x2\x2\x2\x15D\x5F4\x3\x2\x2\x2"); + sb.Append("\x15F\x5F6\x3\x2\x2\x2\x161\x5F9\x3\x2\x2\x2\x163\x5FC\x3\x2"); + sb.Append("\x2\x2\x165\x5FE\x3\x2\x2\x2\x167\x601\x3\x2\x2\x2\x169\x604"); + sb.Append("\x3\x2\x2\x2\x16B\x606\x3\x2\x2\x2\x16D\x608\x3\x2\x2\x2\x16F"); + sb.Append("\x60A\x3\x2\x2\x2\x171\x60C\x3\x2\x2\x2\x173\x60E\x3\x2\x2\x2"); + sb.Append("\x175\x610\x3\x2\x2\x2\x177\x612\x3\x2\x2\x2\x179\x614\x3\x2"); + sb.Append("\x2\x2\x17B\x617\x3\x2\x2\x2\x17D\x61D\x3\x2\x2\x2\x17F\x62F"); + sb.Append("\x3\x2\x2\x2\x181\x63D\x3\x2\x2\x2\x183\x647\x3\x2\x2\x2\x185"); + sb.Append("\x651\x3\x2\x2\x2\x187\x65B\x3\x2\x2\x2\x189\x662\x3\x2\x2\x2"); + sb.Append("\x18B\x66D\x3\x2\x2\x2\x18D\x671\x3\x2\x2\x2\x18F\x67C\x3\x2"); + sb.Append("\x2\x2\x191\x67E\x3\x2\x2\x2\x193\x685\x3\x2\x2\x2\x195\x689"); + sb.Append("\x3\x2\x2\x2\x197\x68D\x3\x2\x2\x2\x199\x691\x3\x2\x2\x2\x19B"); + sb.Append("\x695\x3\x2\x2\x2\x19D\x6A7\x3\x2\x2\x2\x19F\x6A9\x3\x2\x2\x2"); + sb.Append("\x1A1\x6B5\x3\x2\x2\x2\x1A3\x6B7\x3\x2\x2\x2\x1A5\x6BB\x3\x2"); + sb.Append("\x2\x2\x1A7\x6BE\x3\x2\x2\x2\x1A9\x6C2\x3\x2\x2\x2\x1AB\x6C6"); + sb.Append("\x3\x2\x2\x2\x1AD\x6D0\x3\x2\x2\x2\x1AF\x6D4\x3\x2\x2\x2\x1B1"); + sb.Append("\x6D6\x3\x2\x2\x2\x1B3\x6DC\x3\x2\x2\x2\x1B5\x6E6\x3\x2\x2\x2"); + sb.Append("\x1B7\x6EA\x3\x2\x2\x2\x1B9\x6EC\x3\x2\x2\x2\x1BB\x6F0\x3\x2"); + sb.Append("\x2\x2\x1BD\x6FA\x3\x2\x2\x2\x1BF\x6FE\x3\x2\x2\x2\x1C1\x71B"); + sb.Append("\x3\x2\x2\x2\x1C3\x71D\x3\x2\x2\x2\x1C5\x720\x3\x2\x2\x2\x1C7"); + sb.Append("\x723\x3\x2\x2\x2\x1C9\x727\x3\x2\x2\x2\x1CB\x729\x3\x2\x2\x2"); + sb.Append("\x1CD\x72B\x3\x2\x2\x2\x1CF\x73B\x3\x2\x2\x2\x1D1\x73D\x3\x2"); + sb.Append("\x2\x2\x1D3\x740\x3\x2\x2\x2\x1D5\x1D6\a\x65\x2\x2\x1D6\x1D7"); + sb.Append("\at\x2\x2\x1D7\x1D8\ag\x2\x2\x1D8\x1D9\a\x63\x2\x2\x1D9\x1DA"); + sb.Append("\av\x2\x2\x1DA\x1DB\ag\x2\x2\x1DB\x4\x3\x2\x2\x2\x1DC\x1DD\a"); + sb.Append("y\x2\x2\x1DD\x1DE\ak\x2\x2\x1DE\x1DF\ap\x2\x2\x1DF\x1E0\a\x66"); + sb.Append("\x2\x2\x1E0\x1E1\aq\x2\x2\x1E1\x1E2\ay\x2\x2\x1E2\x6\x3\x2\x2"); + sb.Append("\x2\x1E3\x1E4\ak\x2\x2\x1E4\x1E5\ap\x2\x2\x1E5\b\x3\x2\x2\x2"); + sb.Append("\x1E6\x1E7\a\x64\x2\x2\x1E7\x1E8\ag\x2\x2\x1E8\x1E9\av\x2\x2"); + sb.Append("\x1E9\x1EA\ay\x2\x2\x1EA\x1EB\ag\x2\x2\x1EB\x1EC\ag\x2\x2\x1EC"); + sb.Append("\x1ED\ap\x2\x2\x1ED\n\x3\x2\x2\x2\x1EE\x1EF\an\x2\x2\x1EF\x1F0"); + sb.Append("\ak\x2\x2\x1F0\x1F1\am\x2\x2\x1F1\x1F2\ag\x2\x2\x1F2\f\x3\x2"); + sb.Append("\x2\x2\x1F3\x1F4\at\x2\x2\x1F4\x1F5\ag\x2\x2\x1F5\x1F6\ai\x2"); + sb.Append("\x2\x1F6\x1F7\ag\x2\x2\x1F7\x1F8\az\x2\x2\x1F8\x1F9\ar\x2\x2"); + sb.Append("\x1F9\xE\x3\x2\x2\x2\x1FA\x1FB\ag\x2\x2\x1FB\x1FC\au\x2\x2\x1FC"); + sb.Append("\x1FD\a\x65\x2\x2\x1FD\x1FE\a\x63\x2\x2\x1FE\x1FF\ar\x2\x2\x1FF"); + sb.Append("\x200\ag\x2\x2\x200\x10\x3\x2\x2\x2\x201\x202\aq\x2\x2\x202"); + sb.Append("\x203\at\x2\x2\x203\x12\x3\x2\x2\x2\x204\x205\a\x63\x2\x2\x205"); + sb.Append("\x206\ap\x2\x2\x206\x207\a\x66\x2\x2\x207\x14\x3\x2\x2\x2\x208"); + sb.Append("\x209\ap\x2\x2\x209\x20A\aq\x2\x2\x20A\x20B\av\x2\x2\x20B\x16"); + sb.Append("\x3\x2\x2\x2\x20C\x20D\ag\x2\x2\x20D\x20E\ax\x2\x2\x20E\x20F"); + sb.Append("\ag\x2\x2\x20F\x210\at\x2\x2\x210\x211\a{\x2\x2\x211\x18\x3"); + sb.Append("\x2\x2\x2\x212\x213\ag\x2\x2\x213\x214\ax\x2\x2\x214\x215\a"); + sb.Append("g\x2\x2\x215\x216\at\x2\x2\x216\x217\a{\x2\x2\x217\x218\a/\x2"); + sb.Append("\x2\x218\x219\a\x66\x2\x2\x219\x21A\ak\x2\x2\x21A\x21B\au\x2"); + sb.Append("\x2\x21B\x21C\av\x2\x2\x21C\x21D\ak\x2\x2\x21D\x21E\ap\x2\x2"); + sb.Append("\x21E\x21F\a\x65\x2\x2\x21F\x220\av\x2\x2\x220\x1A\x3\x2\x2"); + sb.Append("\x2\x221\x222\ay\x2\x2\x222\x223\aj\x2\x2\x223\x224\ag\x2\x2"); + sb.Append("\x224\x225\at\x2\x2\x225\x226\ag\x2\x2\x226\x1C\x3\x2\x2\x2"); + sb.Append("\x227\x228\a\x63\x2\x2\x228\x229\au\x2\x2\x229\x1E\x3\x2\x2"); + sb.Append("\x2\x22A\x22B\au\x2\x2\x22B\x22C\aw\x2\x2\x22C\x22D\ao\x2\x2"); + sb.Append("\x22D \x3\x2\x2\x2\x22E\x22F\a\x63\x2\x2\x22F\x230\ax\x2\x2"); + sb.Append("\x230\x231\ai\x2\x2\x231\"\x3\x2\x2\x2\x232\x233\ao\x2\x2\x233"); + sb.Append("\x234\a\x63\x2\x2\x234\x235\az\x2\x2\x235$\x3\x2\x2\x2\x236"); + sb.Append("\x237\ao\x2\x2\x237\x238\ak\x2\x2\x238\x239\ap\x2\x2\x239&\x3"); + sb.Append("\x2\x2\x2\x23A\x23B\a\x65\x2\x2\x23B\x23C\aq\x2\x2\x23C\x23D"); + sb.Append("\a\x63\x2\x2\x23D\x23E\an\x2\x2\x23E\x23F\ag\x2\x2\x23F\x240"); + sb.Append("\au\x2\x2\x240\x241\a\x65\x2\x2\x241\x242\ag\x2\x2\x242(\x3"); + sb.Append("\x2\x2\x2\x243\x244\ao\x2\x2\x244\x245\ag\x2\x2\x245\x246\a"); + sb.Append("\x66\x2\x2\x246\x247\ak\x2\x2\x247\x248\a\x63\x2\x2\x248\x249"); + sb.Append("\ap\x2\x2\x249*\x3\x2\x2\x2\x24A\x24B\au\x2\x2\x24B\x24C\av"); + sb.Append("\x2\x2\x24C\x24D\a\x66\x2\x2\x24D\x24E\a\x66\x2\x2\x24E\x24F"); + sb.Append("\ag\x2\x2\x24F\x250\ax\x2\x2\x250,\x3\x2\x2\x2\x251\x252\a\x63"); + sb.Append("\x2\x2\x252\x253\ax\x2\x2\x253\x254\ag\x2\x2\x254\x255\a\x66"); + sb.Append("\x2\x2\x255\x256\ag\x2\x2\x256\x257\ax\x2\x2\x257.\x3\x2\x2"); + sb.Append("\x2\x258\x259\a\x65\x2\x2\x259\x25A\aq\x2\x2\x25A\x25B\aw\x2"); + sb.Append("\x2\x25B\x25C\ap\x2\x2\x25C\x25D\av\x2\x2\x25D\x30\x3\x2\x2"); + sb.Append("\x2\x25E\x25F\au\x2\x2\x25F\x260\ag\x2\x2\x260\x261\an\x2\x2"); + sb.Append("\x261\x262\ag\x2\x2\x262\x263\a\x65\x2\x2\x263\x264\av\x2\x2"); + sb.Append("\x264\x32\x3\x2\x2\x2\x265\x266\a\x65\x2\x2\x266\x267\a\x63"); + sb.Append("\x2\x2\x267\x268\au\x2\x2\x268\x269\ag\x2\x2\x269\x34\x3\x2"); + sb.Append("\x2\x2\x26A\x26B\ag\x2\x2\x26B\x26C\an\x2\x2\x26C\x26D\au\x2"); + sb.Append("\x2\x26D\x26E\ag\x2\x2\x26E\x36\x3\x2\x2\x2\x26F\x270\ay\x2"); + sb.Append("\x2\x270\x271\aj\x2\x2\x271\x272\ag\x2\x2\x272\x273\ap\x2\x2"); + sb.Append("\x273\x38\x3\x2\x2\x2\x274\x275\av\x2\x2\x275\x276\aj\x2\x2"); + sb.Append("\x276\x277\ag\x2\x2\x277\x278\ap\x2\x2\x278:\x3\x2\x2\x2\x279"); + sb.Append("\x27A\ag\x2\x2\x27A\x27B\ap\x2\x2\x27B\x27C\a\x66\x2\x2\x27C"); + sb.Append("<\x3\x2\x2\x2\x27D\x27E\ah\x2\x2\x27E\x27F\at\x2\x2\x27F\x280"); + sb.Append("\aq\x2\x2\x280\x281\ao\x2\x2\x281>\x3\x2\x2\x2\x282\x283\aq"); + sb.Append("\x2\x2\x283\x284\aw\x2\x2\x284\x285\av\x2\x2\x285\x286\ag\x2"); + sb.Append("\x2\x286\x287\at\x2\x2\x287@\x3\x2\x2\x2\x288\x289\ak\x2\x2"); + sb.Append("\x289\x28A\ap\x2\x2\x28A\x28B\ap\x2\x2\x28B\x28C\ag\x2\x2\x28C"); + sb.Append("\x28D\at\x2\x2\x28D\x42\x3\x2\x2\x2\x28E\x28F\al\x2\x2\x28F"); + sb.Append("\x290\aq\x2\x2\x290\x291\ak\x2\x2\x291\x292\ap\x2\x2\x292\x44"); + sb.Append("\x3\x2\x2\x2\x293\x294\an\x2\x2\x294\x295\ag\x2\x2\x295\x296"); + sb.Append("\ah\x2\x2\x296\x297\av\x2\x2\x297\x46\x3\x2\x2\x2\x298\x299"); + sb.Append("\at\x2\x2\x299\x29A\ak\x2\x2\x29A\x29B\ai\x2\x2\x29B\x29C\a"); + sb.Append("j\x2\x2\x29C\x29D\av\x2\x2\x29DH\x3\x2\x2\x2\x29E\x29F\ah\x2"); + sb.Append("\x2\x29F\x2A0\aw\x2\x2\x2A0\x2A1\an\x2\x2\x2A1\x2A2\an\x2\x2"); + sb.Append("\x2A2J\x3\x2\x2\x2\x2A3\x2A4\aq\x2\x2\x2A4\x2A5\ap\x2\x2\x2A5"); + sb.Append("L\x3\x2\x2\x2\x2A6\x2A7\ak\x2\x2\x2A7\x2A8\au\x2\x2\x2A8N\x3"); + sb.Append("\x2\x2\x2\x2A9\x2AA\a\x64\x2\x2\x2AA\x2AB\a{\x2\x2\x2ABP\x3"); + sb.Append("\x2\x2\x2\x2AC\x2AD\ai\x2\x2\x2AD\x2AE\at\x2\x2\x2AE\x2AF\a"); + sb.Append("q\x2\x2\x2AF\x2B0\aw\x2\x2\x2B0\x2B1\ar\x2\x2\x2B1R\x3\x2\x2"); + sb.Append("\x2\x2B2\x2B3\aj\x2\x2\x2B3\x2B4\a\x63\x2\x2\x2B4\x2B5\ax\x2"); + sb.Append("\x2\x2B5\x2B6\ak\x2\x2\x2B6\x2B7\ap\x2\x2\x2B7\x2B8\ai\x2\x2"); + sb.Append("\x2B8T\x3\x2\x2\x2\x2B9\x2BA\a\x66\x2\x2\x2BA\x2BB\ak\x2\x2"); + sb.Append("\x2BB\x2BC\au\x2\x2\x2BC\x2BD\av\x2\x2\x2BD\x2BE\ak\x2\x2\x2BE"); + sb.Append("\x2BF\ap\x2\x2\x2BF\x2C0\a\x65\x2\x2\x2C0\x2C1\av\x2\x2\x2C1"); + sb.Append("V\x3\x2\x2\x2\x2C2\x2C3\a\x63\x2\x2\x2C3\x2C4\an\x2\x2\x2C4"); + sb.Append("\x2C5\an\x2\x2\x2C5X\x3\x2\x2\x2\x2C6\x2C7\a\x63\x2\x2\x2C7"); + sb.Append("\x2C8\ap\x2\x2\x2C8\x2C9\a{\x2\x2\x2C9Z\x3\x2\x2\x2\x2CA\x2CB"); + sb.Append("\au\x2\x2\x2CB\x2CC\aq\x2\x2\x2CC\x2CD\ao\x2\x2\x2CD\x2CE\a"); + sb.Append("g\x2\x2\x2CE\\\x3\x2\x2\x2\x2CF\x2D0\aq\x2\x2\x2D0\x2D1\aw\x2"); + sb.Append("\x2\x2D1\x2D2\av\x2\x2\x2D2\x2D3\ar\x2\x2\x2D3\x2D4\aw\x2\x2"); + sb.Append("\x2D4\x2D5\av\x2\x2\x2D5^\x3\x2\x2\x2\x2D6\x2D7\ag\x2\x2\x2D7"); + sb.Append("\x2D8\ax\x2\x2\x2D8\x2D9\ag\x2\x2\x2D9\x2DA\ap\x2\x2\x2DA\x2DB"); + sb.Append("\av\x2\x2\x2DB\x2DC\au\x2\x2\x2DC`\x3\x2\x2\x2\x2DD\x2DE\ah"); + sb.Append("\x2\x2\x2DE\x2DF\ak\x2\x2\x2DF\x2E0\at\x2\x2\x2E0\x2E1\au\x2"); + sb.Append("\x2\x2E1\x2E2\av\x2\x2\x2E2\x62\x3\x2\x2\x2\x2E3\x2E4\an\x2"); + sb.Append("\x2\x2E4\x2E5\a\x63\x2\x2\x2E5\x2E6\au\x2\x2\x2E6\x2E7\av\x2"); + sb.Append("\x2\x2E7\x64\x3\x2\x2\x2\x2E8\x2E9\ak\x2\x2\x2E9\x2EA\ap\x2"); + sb.Append("\x2\x2EA\x2EB\au\x2\x2\x2EB\x2EC\ag\x2\x2\x2EC\x2ED\at\x2\x2"); + sb.Append("\x2ED\x2EE\av\x2\x2\x2EE\x66\x3\x2\x2\x2\x2EF\x2F0\ak\x2\x2"); + sb.Append("\x2F0\x2F1\ap\x2\x2\x2F1\x2F2\av\x2\x2\x2F2\x2F3\aq\x2\x2\x2F3"); + sb.Append("h\x3\x2\x2\x2\x2F4\x2F5\ax\x2\x2\x2F5\x2F6\a\x63\x2\x2\x2F6"); + sb.Append("\x2F7\an\x2\x2\x2F7\x2F8\aw\x2\x2\x2F8\x2F9\ag\x2\x2\x2F9\x2FA"); + sb.Append("\au\x2\x2\x2FAj\x3\x2\x2\x2\x2FB\x2FC\aq\x2\x2\x2FC\x2FD\at"); + sb.Append("\x2\x2\x2FD\x2FE\a\x66\x2\x2\x2FE\x2FF\ag\x2\x2\x2FF\x300\a"); + sb.Append("t\x2\x2\x300l\x3\x2\x2\x2\x301\x302\a\x63\x2\x2\x302\x303\a"); + sb.Append("u\x2\x2\x303\x304\a\x65\x2\x2\x304n\x3\x2\x2\x2\x305\x306\a"); + sb.Append("\x66\x2\x2\x306\x307\ag\x2\x2\x307\x308\au\x2\x2\x308\x309\a"); + sb.Append("\x65\x2\x2\x309p\x3\x2\x2\x2\x30A\x30B\at\x2\x2\x30B\x30C\a"); + sb.Append("u\x2\x2\x30C\x30D\av\x2\x2\x30D\x30E\at\x2\x2\x30E\x30F\ag\x2"); + sb.Append("\x2\x30F\x310\a\x63\x2\x2\x310\x311\ao\x2\x2\x311r\x3\x2\x2"); + sb.Append("\x2\x312\x313\ak\x2\x2\x313\x314\au\x2\x2\x314\x315\av\x2\x2"); + sb.Append("\x315\x316\at\x2\x2\x316\x317\ag\x2\x2\x317\x318\a\x63\x2\x2"); + sb.Append("\x318\x319\ao\x2\x2\x319t\x3\x2\x2\x2\x31A\x31B\ak\x2\x2\x31B"); + sb.Append("\x31C\at\x2\x2\x31C\x31D\au\x2\x2\x31D\x31E\av\x2\x2\x31E\x31F"); + sb.Append("\at\x2\x2\x31F\x320\ag\x2\x2\x320\x321\a\x63\x2\x2\x321\x322"); + sb.Append("\ao\x2\x2\x322v\x3\x2\x2\x2\x323\x324\au\x2\x2\x324\x325\a\x65"); + sb.Append("\x2\x2\x325\x326\aj\x2\x2\x326\x327\ag\x2\x2\x327\x328\ao\x2"); + sb.Append("\x2\x328\x329\a\x63\x2\x2\x329x\x3\x2\x2\x2\x32A\x32B\aw\x2"); + sb.Append("\x2\x32B\x32C\ap\x2\x2\x32C\x32D\ak\x2\x2\x32D\x32E\a\x66\x2"); + sb.Append("\x2\x32E\x32F\ak\x2\x2\x32F\x330\at\x2\x2\x330\x331\ag\x2\x2"); + sb.Append("\x331\x332\a\x65\x2\x2\x332\x333\av\x2\x2\x333\x334\ak\x2\x2"); + sb.Append("\x334\x335\aq\x2\x2\x335\x336\ap\x2\x2\x336\x337\a\x63\x2\x2"); + sb.Append("\x337\x338\an\x2\x2\x338z\x3\x2\x2\x2\x339\x33A\at\x2\x2\x33A"); + sb.Append("\x33B\ag\x2\x2\x33B\x33C\av\x2\x2\x33C\x33D\a\x63\x2\x2\x33D"); + sb.Append("\x33E\ak\x2\x2\x33E\x33F\ap\x2\x2\x33F\x340\a/\x2\x2\x340\x341"); + sb.Append("\aw\x2\x2\x341\x342\ap\x2\x2\x342\x343\ak\x2\x2\x343\x344\a"); + sb.Append("q\x2\x2\x344\x345\ap\x2\x2\x345|\x3\x2\x2\x2\x346\x347\at\x2"); + sb.Append("\x2\x347\x348\ag\x2\x2\x348\x349\av\x2\x2\x349\x34A\a\x63\x2"); + sb.Append("\x2\x34A\x34B\ak\x2\x2\x34B\x34C\ap\x2\x2\x34C\x34D\a/\x2\x2"); + sb.Append("\x34D\x34E\ak\x2\x2\x34E\x34F\ap\x2\x2\x34F\x350\av\x2\x2\x350"); + sb.Append("\x351\ag\x2\x2\x351\x352\at\x2\x2\x352\x353\au\x2\x2\x353\x354"); + sb.Append("\ag\x2\x2\x354\x355\a\x65\x2\x2\x355\x356\av\x2\x2\x356\x357"); + sb.Append("\ak\x2\x2\x357\x358\aq\x2\x2\x358\x359\ap\x2\x2\x359~\x3\x2"); + sb.Append("\x2\x2\x35A\x35B\ar\x2\x2\x35B\x35C\a\x63\x2\x2\x35C\x35D\a"); + sb.Append("v\x2\x2\x35D\x35E\av\x2\x2\x35E\x35F\ag\x2\x2\x35F\x360\at\x2"); + sb.Append("\x2\x360\x361\ap\x2\x2\x361\x80\x3\x2\x2\x2\x362\x363\au\x2"); + sb.Append("\x2\x363\x364\as\x2\x2\x364\x365\an\x2\x2\x365\x82\x3\x2\x2"); + sb.Append("\x2\x366\x367\ao\x2\x2\x367\x368\ag\x2\x2\x368\x369\av\x2\x2"); + sb.Append("\x369\x36A\a\x63\x2\x2\x36A\x36B\a\x66\x2\x2\x36B\x36C\a\x63"); + sb.Append("\x2\x2\x36C\x36D\av\x2\x2\x36D\x36E\a\x63\x2\x2\x36E\x36F\a"); + sb.Append("u\x2\x2\x36F\x370\as\x2\x2\x370\x371\an\x2\x2\x371\x84\x3\x2"); + sb.Append("\x2\x2\x372\x373\ar\x2\x2\x373\x374\at\x2\x2\x374\x375\ag\x2"); + sb.Append("\x2\x375\x376\ax\x2\x2\x376\x86\x3\x2\x2\x2\x377\x378\ar\x2"); + sb.Append("\x2\x378\x379\at\x2\x2\x379\x37A\ag\x2\x2\x37A\x37B\ax\x2\x2"); + sb.Append("\x37B\x37C\av\x2\x2\x37C\x37D\a\x63\x2\x2\x37D\x37E\ak\x2\x2"); + sb.Append("\x37E\x37F\an\x2\x2\x37F\x88\x3\x2\x2\x2\x380\x381\ar\x2\x2"); + sb.Append("\x381\x382\at\x2\x2\x382\x383\ag\x2\x2\x383\x384\ax\x2\x2\x384"); + sb.Append("\x385\a\x65\x2\x2\x385\x386\aq\x2\x2\x386\x387\aw\x2\x2\x387"); + sb.Append("\x388\ap\x2\x2\x388\x389\av\x2\x2\x389\x8A\x3\x2\x2\x2\x38A"); + sb.Append("\x38B\ar\x2\x2\x38B\x38C\at\x2\x2\x38C\x38D\ag\x2\x2\x38D\x38E"); + sb.Append("\ax\x2\x2\x38E\x38F\ay\x2\x2\x38F\x390\ak\x2\x2\x390\x391\a"); + sb.Append("p\x2\x2\x391\x392\a\x66\x2\x2\x392\x393\aq\x2\x2\x393\x394\a"); + sb.Append("y\x2\x2\x394\x8C\x3\x2\x2\x2\x395\x396\ar\x2\x2\x396\x397\a"); + sb.Append("t\x2\x2\x397\x398\ak\x2\x2\x398\x399\aq\x2\x2\x399\x39A\at\x2"); + sb.Append("\x2\x39A\x8E\x3\x2\x2\x2\x39B\x39C\ag\x2\x2\x39C\x39D\az\x2"); + sb.Append("\x2\x39D\x39E\ak\x2\x2\x39E\x39F\au\x2\x2\x39F\x3A0\av\x2\x2"); + sb.Append("\x3A0\x3A1\au\x2\x2\x3A1\x90\x3\x2\x2\x2\x3A2\x3A3\ay\x2\x2"); + sb.Append("\x3A3\x3A4\ag\x2\x2\x3A4\x3A5\ag\x2\x2\x3A5\x3A6\am\x2\x2\x3A6"); + sb.Append("\x3A7\a\x66\x2\x2\x3A7\x3A8\a\x63\x2\x2\x3A8\x3A9\a{\x2\x2\x3A9"); + sb.Append("\x92\x3\x2\x2\x2\x3AA\x3AB\an\x2\x2\x3AB\x3AC\a\x63\x2\x2\x3AC"); + sb.Append("\x3AD\au\x2\x2\x3AD\x3AE\av\x2\x2\x3AE\x3AF\ay\x2\x2\x3AF\x3B0"); + sb.Append("\ag\x2\x2\x3B0\x3B1\ag\x2\x2\x3B1\x3B2\am\x2\x2\x3B2\x3B3\a"); + sb.Append("\x66\x2\x2\x3B3\x3B4\a\x63\x2\x2\x3B4\x3B5\a{\x2\x2\x3B5\x94"); + sb.Append("\x3\x2\x2\x2\x3B6\x3B7\ak\x2\x2\x3B7\x3B8\ap\x2\x2\x3B8\x3B9"); + sb.Append("\au\x2\x2\x3B9\x3BA\av\x2\x2\x3BA\x3BB\a\x63\x2\x2\x3BB\x3BC"); + sb.Append("\ap\x2\x2\x3BC\x3BD\a\x65\x2\x2\x3BD\x3BE\ag\x2\x2\x3BE\x3BF"); + sb.Append("\aq\x2\x2\x3BF\x3C0\ah\x2\x2\x3C0\x96\x3\x2\x2\x2\x3C1\x3C2"); + sb.Append("\av\x2\x2\x3C2\x3C3\a{\x2\x2\x3C3\x3C4\ar\x2\x2\x3C4\x3C5\a"); + sb.Append("g\x2\x2\x3C5\x3C6\aq\x2\x2\x3C6\x3C7\ah\x2\x2\x3C7\x98\x3\x2"); + sb.Append("\x2\x2\x3C8\x3C9\a\x65\x2\x2\x3C9\x3CA\a\x63\x2\x2\x3CA\x3CB"); + sb.Append("\au\x2\x2\x3CB\x3CC\av\x2\x2\x3CC\x9A\x3\x2\x2\x2\x3CD\x3CE"); + sb.Append("\a\x65\x2\x2\x3CE\x3CF\aw\x2\x2\x3CF\x3D0\at\x2\x2\x3D0\x3D1"); + sb.Append("\at\x2\x2\x3D1\x3D2\ag\x2\x2\x3D2\x3D3\ap\x2\x2\x3D3\x3D4\a"); + sb.Append("v\x2\x2\x3D4\x3D5\a\x61\x2\x2\x3D5\x3D6\av\x2\x2\x3D6\x3D7\a"); + sb.Append("k\x2\x2\x3D7\x3D8\ao\x2\x2\x3D8\x3D9\ag\x2\x2\x3D9\x3DA\au\x2"); + sb.Append("\x2\x3DA\x3DB\av\x2\x2\x3DB\x3DC\a\x63\x2\x2\x3DC\x3DD\ao\x2"); + sb.Append("\x2\x3DD\x3DE\ar\x2\x2\x3DE\x9C\x3\x2\x2\x2\x3DF\x3E0\a\x66"); + sb.Append("\x2\x2\x3E0\x3E1\ag\x2\x2\x3E1\x3E2\an\x2\x2\x3E2\x3E3\ag\x2"); + sb.Append("\x2\x3E3\x3E4\av\x2\x2\x3E4\x3E5\ag\x2\x2\x3E5\x9E\x3\x2\x2"); + sb.Append("\x2\x3E6\x3E7\au\x2\x2\x3E7\x3E8\ap\x2\x2\x3E8\x3E9\a\x63\x2"); + sb.Append("\x2\x3E9\x3EA\ar\x2\x2\x3EA\x3EB\au\x2\x2\x3EB\x3EC\aj\x2\x2"); + sb.Append("\x3EC\x3ED\aq\x2\x2\x3ED\x3EE\av\x2\x2\x3EE\xA0\x3\x2\x2\x2"); + sb.Append("\x3EF\x3F0\au\x2\x2\x3F0\x3F1\ag\x2\x2\x3F1\x3F2\av\x2\x2\x3F2"); + sb.Append("\xA2\x3\x2\x2\x2\x3F3\x3F4\ax\x2\x2\x3F4\x3F5\a\x63\x2\x2\x3F5"); + sb.Append("\x3F6\at\x2\x2\x3F6\x3F7\ak\x2\x2\x3F7\x3F8\a\x63\x2\x2\x3F8"); + sb.Append("\x3F9\a\x64\x2\x2\x3F9\x3FA\an\x2\x2\x3FA\x3FB\ag\x2\x2\x3FB"); + sb.Append("\xA4\x3\x2\x2\x2\x3FC\x3FD\av\x2\x2\x3FD\x3FE\a\x63\x2\x2\x3FE"); + sb.Append("\x3FF\a\x64\x2\x2\x3FF\x400\an\x2\x2\x400\x401\ag\x2\x2\x401"); + sb.Append("\xA6\x3\x2\x2\x2\x402\x403\aw\x2\x2\x403\x404\ap\x2\x2\x404"); + sb.Append("\x405\av\x2\x2\x405\x406\ak\x2\x2\x406\x407\an\x2\x2\x407\xA8"); + sb.Append("\x3\x2\x2\x2\x408\x409\a\x63\x2\x2\x409\x40A\av\x2\x2\x40A\xAA"); + sb.Append("\x3\x2\x2\x2\x40B\x40C\ak\x2\x2\x40C\x40D\ap\x2\x2\x40D\x40E"); + sb.Append("\a\x66\x2\x2\x40E\x40F\ag\x2\x2\x40F\x410\az\x2\x2\x410\xAC"); + sb.Append("\x3\x2\x2\x2\x411\x412\a{\x2\x2\x412\x413\ag\x2\x2\x413\x414"); + sb.Append("\a\x63\x2\x2\x414\x415\at\x2\x2\x415\xAE\x3\x2\x2\x2\x416\x417"); + sb.Append("\a{\x2\x2\x417\x418\ag\x2\x2\x418\x419\a\x63\x2\x2\x419\x41A"); + sb.Append("\at\x2\x2\x41A\x41B\au\x2\x2\x41B\xB0\x3\x2\x2\x2\x41C\x41D"); + sb.Append("\ao\x2\x2\x41D\x41E\aq\x2\x2\x41E\x41F\ap\x2\x2\x41F\x420\a"); + sb.Append("v\x2\x2\x420\x421\aj\x2\x2\x421\xB2\x3\x2\x2\x2\x422\x423\a"); + sb.Append("o\x2\x2\x423\x424\aq\x2\x2\x424\x425\ap\x2\x2\x425\x426\av\x2"); + sb.Append("\x2\x426\x427\aj\x2\x2\x427\x428\au\x2\x2\x428\xB4\x3\x2\x2"); + sb.Append("\x2\x429\x42A\ay\x2\x2\x42A\x42B\ag\x2\x2\x42B\x42C\ag\x2\x2"); + sb.Append("\x42C\x42D\am\x2\x2\x42D\xB6\x3\x2\x2\x2\x42E\x42F\ay\x2\x2"); + sb.Append("\x42F\x430\ag\x2\x2\x430\x431\ag\x2\x2\x431\x432\am\x2\x2\x432"); + sb.Append("\x433\au\x2\x2\x433\xB8\x3\x2\x2\x2\x434\x435\a\x66\x2\x2\x435"); + sb.Append("\x436\a\x63\x2\x2\x436\x437\a{\x2\x2\x437\xBA\x3\x2\x2\x2\x438"); + sb.Append("\x439\a\x66\x2\x2\x439\x43A\a\x63\x2\x2\x43A\x43B\a{\x2\x2\x43B"); + sb.Append("\x43C\au\x2\x2\x43C\xBC\x3\x2\x2\x2\x43D\x43E\aj\x2\x2\x43E"); + sb.Append("\x43F\aq\x2\x2\x43F\x440\aw\x2\x2\x440\x441\at\x2\x2\x441\xBE"); + sb.Append("\x3\x2\x2\x2\x442\x443\aj\x2\x2\x443\x444\aq\x2\x2\x444\x445"); + sb.Append("\aw\x2\x2\x445\x446\at\x2\x2\x446\x447\au\x2\x2\x447\xC0\x3"); + sb.Append("\x2\x2\x2\x448\x449\ao\x2\x2\x449\x44A\ak\x2\x2\x44A\x44B\a"); + sb.Append("p\x2\x2\x44B\x44C\aw\x2\x2\x44C\x44D\av\x2\x2\x44D\x44E\ag\x2"); + sb.Append("\x2\x44E\xC2\x3\x2\x2\x2\x44F\x450\ao\x2\x2\x450\x451\ak\x2"); + sb.Append("\x2\x451\x452\ap\x2\x2\x452\x453\aw\x2\x2\x453\x454\av\x2\x2"); + sb.Append("\x454\x455\ag\x2\x2\x455\x456\au\x2\x2\x456\xC4\x3\x2\x2\x2"); + sb.Append("\x457\x458\au\x2\x2\x458\x459\ag\x2\x2\x459\x45A\a\x65\x2\x2"); + sb.Append("\x45A\xC6\x3\x2\x2\x2\x45B\x45C\au\x2\x2\x45C\x45D\ag\x2\x2"); + sb.Append("\x45D\x45E\a\x65\x2\x2\x45E\x45F\aq\x2\x2\x45F\x460\ap\x2\x2"); + sb.Append("\x460\x461\a\x66\x2\x2\x461\xC8\x3\x2\x2\x2\x462\x463\au\x2"); + sb.Append("\x2\x463\x464\ag\x2\x2\x464\x465\a\x65\x2\x2\x465\x466\aq\x2"); + sb.Append("\x2\x466\x467\ap\x2\x2\x467\x468\a\x66\x2\x2\x468\x469\au\x2"); + sb.Append("\x2\x469\xCA\x3\x2\x2\x2\x46A\x46B\ao\x2\x2\x46B\x46C\au\x2"); + sb.Append("\x2\x46C\x46D\ag\x2\x2\x46D\x46E\a\x65\x2\x2\x46E\xCC\x3\x2"); + sb.Append("\x2\x2\x46F\x470\ao\x2\x2\x470\x471\ak\x2\x2\x471\x472\an\x2"); + sb.Append("\x2\x472\x473\an\x2\x2\x473\x474\ak\x2\x2\x474\x475\au\x2\x2"); + sb.Append("\x475\x476\ag\x2\x2\x476\x477\a\x65\x2\x2\x477\x478\aq\x2\x2"); + sb.Append("\x478\x479\ap\x2\x2\x479\x47A\a\x66\x2\x2\x47A\xCE\x3\x2\x2"); + sb.Append("\x2\x47B\x47C\ao\x2\x2\x47C\x47D\ak\x2\x2\x47D\x47E\an\x2\x2"); + sb.Append("\x47E\x47F\an\x2\x2\x47F\x480\ak\x2\x2\x480\x481\au\x2\x2\x481"); + sb.Append("\x482\ag\x2\x2\x482\x483\a\x65\x2\x2\x483\x484\aq\x2\x2\x484"); + sb.Append("\x485\ap\x2\x2\x485\x486\a\x66\x2\x2\x486\x487\au\x2\x2\x487"); + sb.Append("\xD0\x3\x2\x2\x2\x488\x489\aw\x2\x2\x489\x48A\au\x2\x2\x48A"); + sb.Append("\x48B\ag\x2\x2\x48B\x48C\a\x65\x2\x2\x48C\xD2\x3\x2\x2\x2\x48D"); + sb.Append("\x48E\ao\x2\x2\x48E\x48F\ak\x2\x2\x48F\x490\a\x65\x2\x2\x490"); + sb.Append("\x491\at\x2\x2\x491\x492\aq\x2\x2\x492\x493\au\x2\x2\x493\x494"); + sb.Append("\ag\x2\x2\x494\x495\a\x65\x2\x2\x495\x496\aq\x2\x2\x496\x497"); + sb.Append("\ap\x2\x2\x497\x498\a\x66\x2\x2\x498\xD4\x3\x2\x2\x2\x499\x49A"); + sb.Append("\ao\x2\x2\x49A\x49B\ak\x2\x2\x49B\x49C\a\x65\x2\x2\x49C\x49D"); + sb.Append("\at\x2\x2\x49D\x49E\aq\x2\x2\x49E\x49F\au\x2\x2\x49F\x4A0\a"); + sb.Append("g\x2\x2\x4A0\x4A1\a\x65\x2\x2\x4A1\x4A2\aq\x2\x2\x4A2\x4A3\a"); + sb.Append("p\x2\x2\x4A3\x4A4\a\x66\x2\x2\x4A4\x4A5\au\x2\x2\x4A5\xD6\x3"); + sb.Append("\x2\x2\x2\x4A6\x4A7\av\x2\x2\x4A7\x4A8\at\x2\x2\x4A8\x4A9\a"); + sb.Append("w\x2\x2\x4A9\x4AA\ag\x2\x2\x4AA\xD8\x3\x2\x2\x2\x4AB\x4AC\a"); + sb.Append("h\x2\x2\x4AC\x4AD\a\x63\x2\x2\x4AD\x4AE\an\x2\x2\x4AE\x4AF\a"); + sb.Append("u\x2\x2\x4AF\x4B0\ag\x2\x2\x4B0\xDA\x3\x2\x2\x2\x4B1\x4B2\a"); + sb.Append("p\x2\x2\x4B2\x4B3\aw\x2\x2\x4B3\x4B4\an\x2\x2\x4B4\x4B5\an\x2"); + sb.Append("\x2\x4B5\xDC\x3\x2\x2\x2\x4B6\x4B7\an\x2\x2\x4B7\x4B8\ak\x2"); + sb.Append("\x2\x4B8\x4B9\ao\x2\x2\x4B9\x4BA\ak\x2\x2\x4BA\x4BB\av\x2\x2"); + sb.Append("\x4BB\xDE\x3\x2\x2\x2\x4BC\x4BD\aq\x2\x2\x4BD\x4BE\ah\x2\x2"); + sb.Append("\x4BE\x4BF\ah\x2\x2\x4BF\x4C0\au\x2\x2\x4C0\x4C1\ag\x2\x2\x4C1"); + sb.Append("\x4C2\av\x2\x2\x4C2\xE0\x3\x2\x2\x2\x4C3\x4C4\aw\x2\x2\x4C4"); + sb.Append("\x4C5\ar\x2\x2\x4C5\x4C6\a\x66\x2\x2\x4C6\x4C7\a\x63\x2\x2\x4C7"); + sb.Append("\x4C8\av\x2\x2\x4C8\x4C9\ag\x2\x2\x4C9\xE2\x3\x2\x2\x2\x4CA"); + sb.Append("\x4CB\ao\x2\x2\x4CB\x4CC\a\x63\x2\x2\x4CC\x4CD\av\x2\x2\x4CD"); + sb.Append("\x4CE\a\x65\x2\x2\x4CE\x4CF\aj\x2\x2\x4CF\x4D0\a\x61\x2\x2\x4D0"); + sb.Append("\x4D1\at\x2\x2\x4D1\x4D2\ag\x2\x2\x4D2\x4D3\a\x65\x2\x2\x4D3"); + sb.Append("\x4D4\aq\x2\x2\x4D4\x4D5\ai\x2\x2\x4D5\x4D6\ap\x2\x2\x4D6\x4D7"); + sb.Append("\ak\x2\x2\x4D7\x4D8\a|\x2\x2\x4D8\x4D9\ag\x2\x2\x4D9\xE4\x3"); + sb.Append("\x2\x2\x2\x4DA\x4DB\ao\x2\x2\x4DB\x4DC\a\x63\x2\x2\x4DC\x4DD"); + sb.Append("\av\x2\x2\x4DD\x4DE\a\x65\x2\x2\x4DE\x4DF\aj\x2\x2\x4DF\x4E0"); + sb.Append("\a\x61\x2\x2\x4E0\x4E1\at\x2\x2\x4E1\x4E2\ag\x2\x2\x4E2\x4E3"); + sb.Append("\a\x65\x2\x2\x4E3\x4E4\aq\x2\x2\x4E4\x4E5\ai\x2\x2\x4E5\x4E6"); + sb.Append("\ap\x2\x2\x4E6\x4E7\ak\x2\x2\x4E7\x4E8\a|\x2\x2\x4E8\x4E9\a"); + sb.Append("g\x2\x2\x4E9\x4EA\a\x61\x2\x2\x4EA\x4EB\ar\x2\x2\x4EB\x4EC\a"); + sb.Append("g\x2\x2\x4EC\x4ED\at\x2\x2\x4ED\x4EE\ao\x2\x2\x4EE\x4EF\aw\x2"); + sb.Append("\x2\x4EF\x4F0\av\x2\x2\x4F0\x4F1\ag\x2\x2\x4F1\xE6\x3\x2\x2"); + sb.Append("\x2\x4F2\x4F3\ao\x2\x2\x4F3\x4F4\ag\x2\x2\x4F4\x4F5\a\x63\x2"); + sb.Append("\x2\x4F5\x4F6\au\x2\x2\x4F6\x4F7\aw\x2\x2\x4F7\x4F8\at\x2\x2"); + sb.Append("\x4F8\x4F9\ag\x2\x2\x4F9\x4FA\au\x2\x2\x4FA\xE8\x3\x2\x2\x2"); + sb.Append("\x4FB\x4FC\a\x66\x2\x2\x4FC\x4FD\ag\x2\x2\x4FD\x4FE\ah\x2\x2"); + sb.Append("\x4FE\x4FF\ak\x2\x2\x4FF\x500\ap\x2\x2\x500\x501\ag\x2\x2\x501"); + sb.Append("\xEA\x3\x2\x2\x2\x502\x503\ar\x2\x2\x503\x504\a\x63\x2\x2\x504"); + sb.Append("\x505\at\x2\x2\x505\x506\av\x2\x2\x506\x507\ak\x2\x2\x507\x508"); + sb.Append("\av\x2\x2\x508\x509\ak\x2\x2\x509\x50A\aq\x2\x2\x50A\x50B\a"); + sb.Append("p\x2\x2\x50B\xEC\x3\x2\x2\x2\x50C\x50D\ao\x2\x2\x50D\x50E\a"); + sb.Append("\x63\x2\x2\x50E\x50F\av\x2\x2\x50F\x510\a\x65\x2\x2\x510\x511"); + sb.Append("\aj\x2\x2\x511\x512\ag\x2\x2\x512\x513\au\x2\x2\x513\xEE\x3"); + sb.Append("\x2\x2\x2\x514\x515\a\x63\x2\x2\x515\x516\ah\x2\x2\x516\x517"); + sb.Append("\av\x2\x2\x517\x518\ag\x2\x2\x518\x519\at\x2\x2\x519\xF0\x3"); + sb.Append("\x2\x2\x2\x51A\x51B\ah\x2\x2\x51B\x51C\aq\x2\x2\x51C\x51D\a"); + sb.Append("t\x2\x2\x51D\xF2\x3\x2\x2\x2\x51E\x51F\ay\x2\x2\x51F\x520\a"); + sb.Append("j\x2\x2\x520\x521\ak\x2\x2\x521\x522\an\x2\x2\x522\x523\ag\x2"); + sb.Append("\x2\x523\xF4\x3\x2\x2\x2\x524\x525\aw\x2\x2\x525\x526\au\x2"); + sb.Append("\x2\x526\x527\ak\x2\x2\x527\x528\ap\x2\x2\x528\x529\ai\x2\x2"); + sb.Append("\x529\xF6\x3\x2\x2\x2\x52A\x52B\ao\x2\x2\x52B\x52C\ag\x2\x2"); + sb.Append("\x52C\x52D\at\x2\x2\x52D\x52E\ai\x2\x2\x52E\x52F\ag\x2\x2\x52F"); + sb.Append("\xF8\x3\x2\x2\x2\x530\x531\ao\x2\x2\x531\x532\a\x63\x2\x2\x532"); + sb.Append("\x533\av\x2\x2\x533\x534\a\x65\x2\x2\x534\x535\aj\x2\x2\x535"); + sb.Append("\x536\ag\x2\x2\x536\x537\a\x66\x2\x2\x537\xFA\x3\x2\x2\x2\x538"); + sb.Append("\x539\ag\x2\x2\x539\x53A\az\x2\x2\x53A\x53B\ar\x2\x2\x53B\x53C"); + sb.Append("\at\x2\x2\x53C\x53D\ag\x2\x2\x53D\x53E\au\x2\x2\x53E\x53F\a"); + sb.Append("u\x2\x2\x53F\x540\ak\x2\x2\x540\x541\aq\x2\x2\x541\x542\ap\x2"); + sb.Append("\x2\x542\xFC\x3\x2\x2\x2\x543\x544\ap\x2\x2\x544\x545\ag\x2"); + sb.Append("\x2\x545\x546\ay\x2\x2\x546\xFE\x3\x2\x2\x2\x547\x548\au\x2"); + sb.Append("\x2\x548\x549\av\x2\x2\x549\x54A\a\x63\x2\x2\x54A\x54B\at\x2"); + sb.Append("\x2\x54B\x54C\av\x2\x2\x54C\x100\x3\x2\x2\x2\x54D\x54E\a\x65"); + sb.Append("\x2\x2\x54E\x54F\aq\x2\x2\x54F\x550\ap\x2\x2\x550\x551\av\x2"); + sb.Append("\x2\x551\x552\ag\x2\x2\x552\x553\az\x2\x2\x553\x554\av\x2\x2"); + sb.Append("\x554\x102\x3\x2\x2\x2\x555\x556\ak\x2\x2\x556\x557\ap\x2\x2"); + sb.Append("\x557\x558\ak\x2\x2\x558\x559\av\x2\x2\x559\x55A\ak\x2\x2\x55A"); + sb.Append("\x55B\a\x63\x2\x2\x55B\x55C\av\x2\x2\x55C\x55D\ag\x2\x2\x55D"); + sb.Append("\x55E\a\x66\x2\x2\x55E\x104\x3\x2\x2\x2\x55F\x560\av\x2\x2\x560"); + sb.Append("\x561\ag\x2\x2\x561\x562\at\x2\x2\x562\x563\ao\x2\x2\x563\x564"); + sb.Append("\ak\x2\x2\x564\x565\ap\x2\x2\x565\x566\a\x63\x2\x2\x566\x567"); + sb.Append("\av\x2\x2\x567\x568\ag\x2\x2\x568\x569\a\x66\x2\x2\x569\x106"); + sb.Append("\x3\x2\x2\x2\x56A\x56B\a\x66\x2\x2\x56B\x56C\a\x63\x2\x2\x56C"); + sb.Append("\x56D\av\x2\x2\x56D\x56E\a\x63\x2\x2\x56E\x56F\ah\x2\x2\x56F"); + sb.Append("\x570\an\x2\x2\x570\x571\aq\x2\x2\x571\x572\ay\x2\x2\x572\x108"); + sb.Append("\x3\x2\x2\x2\x573\x574\a\x65\x2\x2\x574\x575\aw\x2\x2\x575\x576"); + sb.Append("\a\x64\x2\x2\x576\x577\ag\x2\x2\x577\x10A\x3\x2\x2\x2\x578\x579"); + sb.Append("\at\x2\x2\x579\x57A\aq\x2\x2\x57A\x57B\an\x2\x2\x57B\x57C\a"); + sb.Append("n\x2\x2\x57C\x57D\aw\x2\x2\x57D\x57E\ar\x2\x2\x57E\x10C\x3\x2"); + sb.Append("\x2\x2\x57F\x580\ai\x2\x2\x580\x581\at\x2\x2\x581\x582\aq\x2"); + sb.Append("\x2\x582\x583\aw\x2\x2\x583\x584\ar\x2\x2\x584\x585\ak\x2\x2"); + sb.Append("\x585\x586\ap\x2\x2\x586\x587\ai\x2\x2\x587\x10E\x3\x2\x2\x2"); + sb.Append("\x588\x589\ai\x2\x2\x589\x58A\at\x2\x2\x58A\x58B\aq\x2\x2\x58B"); + sb.Append("\x58C\aw\x2\x2\x58C\x58D\ar\x2\x2\x58D\x58E\ak\x2\x2\x58E\x58F"); + sb.Append("\ap\x2\x2\x58F\x590\ai\x2\x2\x590\x591\a\x61\x2\x2\x591\x592"); + sb.Append("\ak\x2\x2\x592\x593\a\x66\x2\x2\x593\x110\x3\x2\x2\x2\x594\x595"); + sb.Append("\au\x2\x2\x595\x596\ag\x2\x2\x596\x597\av\x2\x2\x597\x598\a"); + sb.Append("u\x2\x2\x598\x112\x3\x2\x2\x2\x599\x59A\a/\x2\x2\x59A\x59B\a"); + sb.Append("]\x2\x2\x59B\x114\x3\x2\x2\x2\x59C\x59D\a_\x2\x2\x59D\x59E\a"); + sb.Append("@\x2\x2\x59E\x116\x3\x2\x2\x2\x59F\x5A0\a/\x2\x2\x5A0\x5A1\a"); + sb.Append("@\x2\x2\x5A1\x118\x3\x2\x2\x2\x5A2\x5A3\a?\x2\x2\x5A3\x5A4\a"); + sb.Append("@\x2\x2\x5A4\x11A\x3\x2\x2\x2\x5A5\x5A6\a?\x2\x2\x5A6\x11C\x3"); + sb.Append("\x2\x2\x2\x5A7\x5A8\a>\x2\x2\x5A8\x5A9\a@\x2\x2\x5A9\x11E\x3"); + sb.Append("\x2\x2\x2\x5AA\x5AB\a\x41\x2\x2\x5AB\x120\x3\x2\x2\x2\x5AC\x5AD"); + sb.Append("\a*\x2\x2\x5AD\x122\x3\x2\x2\x2\x5AE\x5AF\a+\x2\x2\x5AF\x124"); + sb.Append("\x3\x2\x2\x2\x5B0\x5B1\a]\x2\x2\x5B1\x126\x3\x2\x2\x2\x5B2\x5B3"); + sb.Append("\a_\x2\x2\x5B3\x128\x3\x2\x2\x2\x5B4\x5B5\a}\x2\x2\x5B5\x12A"); + sb.Append("\x3\x2\x2\x2\x5B6\x5B7\a\x7F\x2\x2\x5B7\x12C\x3\x2\x2\x2\x5B8"); + sb.Append("\x5B9\a<\x2\x2\x5B9\x12E\x3\x2\x2\x2\x5BA\x5BB\a.\x2\x2\x5BB"); + sb.Append("\x130\x3\x2\x2\x2\x5BC\x5BD\a?\x2\x2\x5BD\x5BE\a?\x2\x2\x5BE"); + sb.Append("\x132\x3\x2\x2\x2\x5BF\x5C0\a#\x2\x2\x5C0\x134\x3\x2\x2\x2\x5C1"); + sb.Append("\x5C2\a\x80\x2\x2\x5C2\x136\x3\x2\x2\x2\x5C3\x5C4\a#\x2\x2\x5C4"); + sb.Append("\x5C5\a?\x2\x2\x5C5\x138\x3\x2\x2\x2\x5C6\x5C7\a\x31\x2\x2\x5C7"); + sb.Append("\x13A\x3\x2\x2\x2\x5C8\x5C9\a\x31\x2\x2\x5C9\x5CA\a?\x2\x2\x5CA"); + sb.Append("\x13C\x3\x2\x2\x2\x5CB\x5CC\a-\x2\x2\x5CC\x13E\x3\x2\x2\x2\x5CD"); + sb.Append("\x5CE\a-\x2\x2\x5CE\x5CF\a?\x2\x2\x5CF\x140\x3\x2\x2\x2\x5D0"); + sb.Append("\x5D1\a-\x2\x2\x5D1\x5D2\a-\x2\x2\x5D2\x142\x3\x2\x2\x2\x5D3"); + sb.Append("\x5D4\a/\x2\x2\x5D4\x144\x3\x2\x2\x2\x5D5\x5D6\a/\x2\x2\x5D6"); + sb.Append("\x5D7\a?\x2\x2\x5D7\x146\x3\x2\x2\x2\x5D8\x5D9\a/\x2\x2\x5D9"); + sb.Append("\x5DA\a/\x2\x2\x5DA\x148\x3\x2\x2\x2\x5DB\x5DC\a,\x2\x2\x5DC"); + sb.Append("\x14A\x3\x2\x2\x2\x5DD\x5DE\a,\x2\x2\x5DE\x5DF\a?\x2\x2\x5DF"); + sb.Append("\x14C\x3\x2\x2\x2\x5E0\x5E1\a\'\x2\x2\x5E1\x14E\x3\x2\x2\x2"); + sb.Append("\x5E2\x5E3\a\'\x2\x2\x5E3\x5E4\a?\x2\x2\x5E4\x150\x3\x2\x2\x2"); + sb.Append("\x5E5\x5E6\a@\x2\x2\x5E6\x5E7\a?\x2\x2\x5E7\x152\x3\x2\x2\x2"); + sb.Append("\x5E8\x5E9\a@\x2\x2\x5E9\x154\x3\x2\x2\x2\x5EA\x5EB\a>\x2\x2"); + sb.Append("\x5EB\x5EC\a?\x2\x2\x5EC\x156\x3\x2\x2\x2\x5ED\x5EE\a>\x2\x2"); + sb.Append("\x5EE\x158\x3\x2\x2\x2\x5EF\x5F0\a`\x2\x2\x5F0\x15A\x3\x2\x2"); + sb.Append("\x2\x5F1\x5F2\a`\x2\x2\x5F2\x5F3\a?\x2\x2\x5F3\x15C\x3\x2\x2"); + sb.Append("\x2\x5F4\x5F5\a~\x2\x2\x5F5\x15E\x3\x2\x2\x2\x5F6\x5F7\a~\x2"); + sb.Append("\x2\x5F7\x5F8\a?\x2\x2\x5F8\x160\x3\x2\x2\x2\x5F9\x5FA\a~\x2"); + sb.Append("\x2\x5FA\x5FB\a~\x2\x2\x5FB\x162\x3\x2\x2\x2\x5FC\x5FD\a(\x2"); + sb.Append("\x2\x5FD\x164\x3\x2\x2\x2\x5FE\x5FF\a(\x2\x2\x5FF\x600\a?\x2"); + sb.Append("\x2\x600\x166\x3\x2\x2\x2\x601\x602\a(\x2\x2\x602\x603\a(\x2"); + sb.Append("\x2\x603\x168\x3\x2\x2\x2\x604\x605\a=\x2\x2\x605\x16A\x3\x2"); + sb.Append("\x2\x2\x606\x607\a\x30\x2\x2\x607\x16C\x3\x2\x2\x2\x608\x609"); + sb.Append("\a\x1901\x2\x2\x609\x16E\x3\x2\x2\x2\x60A\x60B\a\x1900\x2\x2"); + sb.Append("\x60B\x170\x3\x2\x2\x2\x60C\x60D\a\x18FF\x2\x2\x60D\x172\x3"); + sb.Append("\x2\x2\x2\x60E\x60F\a^\x2\x2\x60F\x174\x3\x2\x2\x2\x610\x611"); + sb.Append("\a\x62\x2\x2\x611\x176\x3\x2\x2\x2\x612\x613\a\x42\x2\x2\x613"); + sb.Append("\x178\x3\x2\x2\x2\x614\x615\a%\x2\x2\x615\x17A\x3\x2\x2\x2\x616"); + sb.Append("\x618\t\x2\x2\x2\x617\x616\x3\x2\x2\x2\x618\x619\x3\x2\x2\x2"); + sb.Append("\x619\x617\x3\x2\x2\x2\x619\x61A\x3\x2\x2\x2\x61A\x61B\x3\x2"); + sb.Append("\x2\x2\x61B\x61C\b\xBE\x2\x2\x61C\x17C\x3\x2\x2\x2\x61D\x61E"); + sb.Append("\a\x31\x2\x2\x61E\x61F\a\x31\x2\x2\x61F\x623\x3\x2\x2\x2\x620"); + sb.Append("\x622\n\x3\x2\x2\x621\x620\x3\x2\x2\x2\x622\x625\x3\x2\x2\x2"); + sb.Append("\x623\x621\x3\x2\x2\x2\x623\x624\x3\x2\x2\x2\x624\x62B\x3\x2"); + sb.Append("\x2\x2\x625\x623\x3\x2\x2\x2\x626\x62C\a\f\x2\x2\x627\x629\a"); + sb.Append("\xF\x2\x2\x628\x62A\a\f\x2\x2\x629\x628\x3\x2\x2\x2\x629\x62A"); + sb.Append("\x3\x2\x2\x2\x62A\x62C\x3\x2\x2\x2\x62B\x626\x3\x2\x2\x2\x62B"); + sb.Append("\x627\x3\x2\x2\x2\x62B\x62C\x3\x2\x2\x2\x62C\x62D\x3\x2\x2\x2"); + sb.Append("\x62D\x62E\b\xBF\x2\x2\x62E\x17E\x3\x2\x2\x2\x62F\x630\a\x31"); + sb.Append("\x2\x2\x630\x631\a,\x2\x2\x631\x635\x3\x2\x2\x2\x632\x634\v"); + sb.Append("\x2\x2\x2\x633\x632\x3\x2\x2\x2\x634\x637\x3\x2\x2\x2\x635\x636"); + sb.Append("\x3\x2\x2\x2\x635\x633\x3\x2\x2\x2\x636\x638\x3\x2\x2\x2\x637"); + sb.Append("\x635\x3\x2\x2\x2\x638\x639\a,\x2\x2\x639\x63A\a\x31\x2\x2\x63A"); + sb.Append("\x63B\x3\x2\x2\x2\x63B\x63C\b\xC0\x2\x2\x63C\x180\x3\x2\x2\x2"); + sb.Append("\x63D\x642\a\x62\x2\x2\x63E\x641\x5\x187\xC4\x2\x63F\x641\n"); + sb.Append("\x4\x2\x2\x640\x63E\x3\x2\x2\x2\x640\x63F\x3\x2\x2\x2\x641\x644"); + sb.Append("\x3\x2\x2\x2\x642\x640\x3\x2\x2\x2\x642\x643\x3\x2\x2\x2\x643"); + sb.Append("\x645\x3\x2\x2\x2\x644\x642\x3\x2\x2\x2\x645\x646\a\x62\x2\x2"); + sb.Append("\x646\x182\x3\x2\x2\x2\x647\x64C\a)\x2\x2\x648\x64B\x5\x187"); + sb.Append("\xC4\x2\x649\x64B\n\x5\x2\x2\x64A\x648\x3\x2\x2\x2\x64A\x649"); + sb.Append("\x3\x2\x2\x2\x64B\x64E\x3\x2\x2\x2\x64C\x64A\x3\x2\x2\x2\x64C"); + sb.Append("\x64D\x3\x2\x2\x2\x64D\x64F\x3\x2\x2\x2\x64E\x64C\x3\x2\x2\x2"); + sb.Append("\x64F\x650\a)\x2\x2\x650\x184\x3\x2\x2\x2\x651\x656\a$\x2\x2"); + sb.Append("\x652\x655\x5\x187\xC4\x2\x653\x655\n\x6\x2\x2\x654\x652\x3"); + sb.Append("\x2\x2\x2\x654\x653\x3\x2\x2\x2\x655\x658\x3\x2\x2\x2\x656\x654"); + sb.Append("\x3\x2\x2\x2\x656\x657\x3\x2\x2\x2\x657\x659\x3\x2\x2\x2\x658"); + sb.Append("\x656\x3\x2\x2\x2\x659\x65A\a$\x2\x2\x65A\x186\x3\x2\x2\x2\x65B"); + sb.Append("\x660\a^\x2\x2\x65C\x661\t\a\x2\x2\x65D\x661\x5\x191\xC9\x2"); + sb.Append("\x65E\x661\x5\x18F\xC8\x2\x65F\x661\v\x2\x2\x2\x660\x65C\x3"); + sb.Append("\x2\x2\x2\x660\x65D\x3\x2\x2\x2\x660\x65E\x3\x2\x2\x2\x660\x65F"); + sb.Append("\x3\x2\x2\x2\x661\x188\x3\x2\x2\x2\x662\x666\t\b\x2\x2\x663"); + sb.Append("\x665\t\t\x2\x2\x664\x663\x3\x2\x2\x2\x665\x668\x3\x2\x2\x2"); + sb.Append("\x666\x664\x3\x2\x2\x2\x666\x667\x3\x2\x2\x2\x667\x18A\x3\x2"); + sb.Append("\x2\x2\x668\x666\x3\x2\x2\x2\x669\x66E\x5\x193\xCA\x2\x66A\x66E"); + sb.Append("\x5\x195\xCB\x2\x66B\x66E\x5\x197\xCC\x2\x66C\x66E\x5\x199\xCD"); + sb.Append("\x2\x66D\x669\x3\x2\x2\x2\x66D\x66A\x3\x2\x2\x2\x66D\x66B\x3"); + sb.Append("\x2\x2\x2\x66D\x66C\x3\x2\x2\x2\x66E\x18C\x3\x2\x2\x2\x66F\x672"); + sb.Append("\x5\x1C1\xE1\x2\x670\x672\x5\x1CD\xE7\x2\x671\x66F\x3\x2\x2"); + sb.Append("\x2\x671\x670\x3\x2\x2\x2\x672\x18E\x3\x2\x2\x2\x673\x674\a"); + sb.Append("^\x2\x2\x674\x675\x4\x32\x35\x2\x675\x676\x4\x32\x39\x2\x676"); + sb.Append("\x67D\x4\x32\x39\x2\x677\x678\a^\x2\x2\x678\x679\x4\x32\x39"); + sb.Append("\x2\x679\x67D\x4\x32\x39\x2\x67A\x67B\a^\x2\x2\x67B\x67D\x4"); + sb.Append("\x32\x39\x2\x67C\x673\x3\x2\x2\x2\x67C\x677\x3\x2\x2\x2\x67C"); + sb.Append("\x67A\x3\x2\x2\x2\x67D\x190\x3\x2\x2\x2\x67E\x67F\a^\x2\x2\x67F"); + sb.Append("\x680\aw\x2\x2\x680\x681\x5\x1AD\xD7\x2\x681\x682\x5\x1AD\xD7"); + sb.Append("\x2\x682\x683\x5\x1AD\xD7\x2\x683\x684\x5\x1AD\xD7\x2\x684\x192"); + sb.Append("\x3\x2\x2\x2\x685\x687\x5\x19D\xCF\x2\x686\x688\x5\x19B\xCE"); + sb.Append("\x2\x687\x686\x3\x2\x2\x2\x687\x688\x3\x2\x2\x2\x688\x194\x3"); + sb.Append("\x2\x2\x2\x689\x68B\x5\x1A9\xD5\x2\x68A\x68C\x5\x19B\xCE\x2"); + sb.Append("\x68B\x68A\x3\x2\x2\x2\x68B\x68C\x3\x2\x2\x2\x68C\x196\x3\x2"); + sb.Append("\x2\x2\x68D\x68F\x5\x1B1\xD9\x2\x68E\x690\x5\x19B\xCE\x2\x68F"); + sb.Append("\x68E\x3\x2\x2\x2\x68F\x690\x3\x2\x2\x2\x690\x198\x3\x2\x2\x2"); + sb.Append("\x691\x693\x5\x1B9\xDD\x2\x692\x694\x5\x19B\xCE\x2\x693\x692"); + sb.Append("\x3\x2\x2\x2\x693\x694\x3\x2\x2\x2\x694\x19A\x3\x2\x2\x2\x695"); + sb.Append("\x696\t\n\x2\x2\x696\x19C\x3\x2\x2\x2\x697\x6A8\a\x32\x2\x2"); + sb.Append("\x698\x69A\a\x32\x2\x2\x699\x698\x3\x2\x2\x2\x69A\x69D\x3\x2"); + sb.Append("\x2\x2\x69B\x699\x3\x2\x2\x2\x69B\x69C\x3\x2\x2\x2\x69C\x69E"); + sb.Append("\x3\x2\x2\x2\x69D\x69B\x3\x2\x2\x2\x69E\x6A5\x5\x1A3\xD2\x2"); + sb.Append("\x69F\x6A1\x5\x19F\xD0\x2\x6A0\x69F\x3\x2\x2\x2\x6A0\x6A1\x3"); + sb.Append("\x2\x2\x2\x6A1\x6A6\x3\x2\x2\x2\x6A2\x6A3\x5\x1A7\xD4\x2\x6A3"); + sb.Append("\x6A4\x5\x19F\xD0\x2\x6A4\x6A6\x3\x2\x2\x2\x6A5\x6A0\x3\x2\x2"); + sb.Append("\x2\x6A5\x6A2\x3\x2\x2\x2\x6A6\x6A8\x3\x2\x2\x2\x6A7\x697\x3"); + sb.Append("\x2\x2\x2\x6A7\x69B\x3\x2\x2\x2\x6A8\x19E\x3\x2\x2\x2\x6A9\x6B1"); + sb.Append("\x5\x1A1\xD1\x2\x6AA\x6AC\x5\x1A5\xD3\x2\x6AB\x6AA\x3\x2\x2"); + sb.Append("\x2\x6AC\x6AF\x3\x2\x2\x2\x6AD\x6AB\x3\x2\x2\x2\x6AD\x6AE\x3"); + sb.Append("\x2\x2\x2\x6AE\x6B0\x3\x2\x2\x2\x6AF\x6AD\x3\x2\x2\x2\x6B0\x6B2"); + sb.Append("\x5\x1A1\xD1\x2\x6B1\x6AD\x3\x2\x2\x2\x6B1\x6B2\x3\x2\x2\x2"); + sb.Append("\x6B2\x1A0\x3\x2\x2\x2\x6B3\x6B6\a\x32\x2\x2\x6B4\x6B6\x5\x1A3"); + sb.Append("\xD2\x2\x6B5\x6B3\x3\x2\x2\x2\x6B5\x6B4\x3\x2\x2\x2\x6B6\x1A2"); + sb.Append("\x3\x2\x2\x2\x6B7\x6B8\t\v\x2\x2\x6B8\x1A4\x3\x2\x2\x2\x6B9"); + sb.Append("\x6BC\x5\x1A1\xD1\x2\x6BA\x6BC\a\x61\x2\x2\x6BB\x6B9\x3\x2\x2"); + sb.Append("\x2\x6BB\x6BA\x3\x2\x2\x2\x6BC\x1A6\x3\x2\x2\x2\x6BD\x6BF\a"); + sb.Append("\x61\x2\x2\x6BE\x6BD\x3\x2\x2\x2\x6BF\x6C0\x3\x2\x2\x2\x6C0"); + sb.Append("\x6BE\x3\x2\x2\x2\x6C0\x6C1\x3\x2\x2\x2\x6C1\x1A8\x3\x2\x2\x2"); + sb.Append("\x6C2\x6C3\a\x32\x2\x2\x6C3\x6C4\t\f\x2\x2\x6C4\x6C5\x5\x1AB"); + sb.Append("\xD6\x2\x6C5\x1AA\x3\x2\x2\x2\x6C6\x6CE\x5\x1AD\xD7\x2\x6C7"); + sb.Append("\x6C9\x5\x1AF\xD8\x2\x6C8\x6C7\x3\x2\x2\x2\x6C9\x6CC\x3\x2\x2"); + sb.Append("\x2\x6CA\x6C8\x3\x2\x2\x2\x6CA\x6CB\x3\x2\x2\x2\x6CB\x6CD\x3"); + sb.Append("\x2\x2\x2\x6CC\x6CA\x3\x2\x2\x2\x6CD\x6CF\x5\x1AD\xD7\x2\x6CE"); + sb.Append("\x6CA\x3\x2\x2\x2\x6CE\x6CF\x3\x2\x2\x2\x6CF\x1AC\x3\x2\x2\x2"); + sb.Append("\x6D0\x6D1\t\r\x2\x2\x6D1\x1AE\x3\x2\x2\x2\x6D2\x6D5\x5\x1AD"); + sb.Append("\xD7\x2\x6D3\x6D5\a\x61\x2\x2\x6D4\x6D2\x3\x2\x2\x2\x6D4\x6D3"); + sb.Append("\x3\x2\x2\x2\x6D5\x1B0\x3\x2\x2\x2\x6D6\x6D8\a\x32\x2\x2\x6D7"); + sb.Append("\x6D9\x5\x1A7\xD4\x2\x6D8\x6D7\x3\x2\x2\x2\x6D8\x6D9\x3\x2\x2"); + sb.Append("\x2\x6D9\x6DA\x3\x2\x2\x2\x6DA\x6DB\x5\x1B3\xDA\x2\x6DB\x1B2"); + sb.Append("\x3\x2\x2\x2\x6DC\x6E4\x5\x1B5\xDB\x2\x6DD\x6DF\x5\x1B7\xDC"); + sb.Append("\x2\x6DE\x6DD\x3\x2\x2\x2\x6DF\x6E2\x3\x2\x2\x2\x6E0\x6DE\x3"); + sb.Append("\x2\x2\x2\x6E0\x6E1\x3\x2\x2\x2\x6E1\x6E3\x3\x2\x2\x2\x6E2\x6E0"); + sb.Append("\x3\x2\x2\x2\x6E3\x6E5\x5\x1B5\xDB\x2\x6E4\x6E0\x3\x2\x2\x2"); + sb.Append("\x6E4\x6E5\x3\x2\x2\x2\x6E5\x1B4\x3\x2\x2\x2\x6E6\x6E7\t\xE"); + sb.Append("\x2\x2\x6E7\x1B6\x3\x2\x2\x2\x6E8\x6EB\x5\x1B5\xDB\x2\x6E9\x6EB"); + sb.Append("\a\x61\x2\x2\x6EA\x6E8\x3\x2\x2\x2\x6EA\x6E9\x3\x2\x2\x2\x6EB"); + sb.Append("\x1B8\x3\x2\x2\x2\x6EC\x6ED\a\x32\x2\x2\x6ED\x6EE\t\xF\x2\x2"); + sb.Append("\x6EE\x6EF\x5\x1BB\xDE\x2\x6EF\x1BA\x3\x2\x2\x2\x6F0\x6F8\x5"); + sb.Append("\x1BD\xDF\x2\x6F1\x6F3\x5\x1BF\xE0\x2\x6F2\x6F1\x3\x2\x2\x2"); + sb.Append("\x6F3\x6F6\x3\x2\x2\x2\x6F4\x6F2\x3\x2\x2\x2\x6F4\x6F5\x3\x2"); + sb.Append("\x2\x2\x6F5\x6F7\x3\x2\x2\x2\x6F6\x6F4\x3\x2\x2\x2\x6F7\x6F9"); + sb.Append("\x5\x1BD\xDF\x2\x6F8\x6F4\x3\x2\x2\x2\x6F8\x6F9\x3\x2\x2\x2"); + sb.Append("\x6F9\x1BC\x3\x2\x2\x2\x6FA\x6FB\t\x10\x2\x2\x6FB\x1BE\x3\x2"); + sb.Append("\x2\x2\x6FC\x6FF\x5\x1BD\xDF\x2\x6FD\x6FF\a\x61\x2\x2\x6FE\x6FC"); + sb.Append("\x3\x2\x2\x2\x6FE\x6FD\x3\x2\x2\x2\x6FF\x1C0\x3\x2\x2\x2\x700"); + sb.Append("\x701\x5\x19F\xD0\x2\x701\x703\a\x30\x2\x2\x702\x704\x5\x19F"); + sb.Append("\xD0\x2\x703\x702\x3\x2\x2\x2\x703\x704\x3\x2\x2\x2\x704\x706"); + sb.Append("\x3\x2\x2\x2\x705\x707\x5\x1C3\xE2\x2\x706\x705\x3\x2\x2\x2"); + sb.Append("\x706\x707\x3\x2\x2\x2\x707\x709\x3\x2\x2\x2\x708\x70A\x5\x1CB"); + sb.Append("\xE6\x2\x709\x708\x3\x2\x2\x2\x709\x70A\x3\x2\x2\x2\x70A\x71C"); + sb.Append("\x3\x2\x2\x2\x70B\x70C\a\x30\x2\x2\x70C\x70E\x5\x19F\xD0\x2"); + sb.Append("\x70D\x70F\x5\x1C3\xE2\x2\x70E\x70D\x3\x2\x2\x2\x70E\x70F\x3"); + sb.Append("\x2\x2\x2\x70F\x711\x3\x2\x2\x2\x710\x712\x5\x1CB\xE6\x2\x711"); + sb.Append("\x710\x3\x2\x2\x2\x711\x712\x3\x2\x2\x2\x712\x71C\x3\x2\x2\x2"); + sb.Append("\x713\x714\x5\x19F\xD0\x2\x714\x716\x5\x1C3\xE2\x2\x715\x717"); + sb.Append("\x5\x1CB\xE6\x2\x716\x715\x3\x2\x2\x2\x716\x717\x3\x2\x2\x2"); + sb.Append("\x717\x71C\x3\x2\x2\x2\x718\x719\x5\x19F\xD0\x2\x719\x71A\x5"); + sb.Append("\x1CB\xE6\x2\x71A\x71C\x3\x2\x2\x2\x71B\x700\x3\x2\x2\x2\x71B"); + sb.Append("\x70B\x3\x2\x2\x2\x71B\x713\x3\x2\x2\x2\x71B\x718\x3\x2\x2\x2"); + sb.Append("\x71C\x1C2\x3\x2\x2\x2\x71D\x71E\x5\x1C5\xE3\x2\x71E\x71F\x5"); + sb.Append("\x1C7\xE4\x2\x71F\x1C4\x3\x2\x2\x2\x720\x721\t\x11\x2\x2\x721"); + sb.Append("\x1C6\x3\x2\x2\x2\x722\x724\x5\x1C9\xE5\x2\x723\x722\x3\x2\x2"); + sb.Append("\x2\x723\x724\x3\x2\x2\x2\x724\x725\x3\x2\x2\x2\x725\x726\x5"); + sb.Append("\x19F\xD0\x2\x726\x1C8\x3\x2\x2\x2\x727\x728\t\x12\x2\x2\x728"); + sb.Append("\x1CA\x3\x2\x2\x2\x729\x72A\t\x13\x2\x2\x72A\x1CC\x3\x2\x2\x2"); + sb.Append("\x72B\x72C\x5\x1CF\xE8\x2\x72C\x72E\x5\x1D1\xE9\x2\x72D\x72F"); + sb.Append("\x5\x1CB\xE6\x2\x72E\x72D\x3\x2\x2\x2\x72E\x72F\x3\x2\x2\x2"); + sb.Append("\x72F\x1CE\x3\x2\x2\x2\x730\x732\x5\x1A9\xD5\x2\x731\x733\a"); + sb.Append("\x30\x2\x2\x732\x731\x3\x2\x2\x2\x732\x733\x3\x2\x2\x2\x733"); + sb.Append("\x73C\x3\x2\x2\x2\x734\x735\a\x32\x2\x2\x735\x737\t\f\x2\x2"); + sb.Append("\x736\x738\x5\x1AB\xD6\x2\x737\x736\x3\x2\x2\x2\x737\x738\x3"); + sb.Append("\x2\x2\x2\x738\x739\x3\x2\x2\x2\x739\x73A\a\x30\x2\x2\x73A\x73C"); + sb.Append("\x5\x1AB\xD6\x2\x73B\x730\x3\x2\x2\x2\x73B\x734\x3\x2\x2\x2"); + sb.Append("\x73C\x1D0\x3\x2\x2\x2\x73D\x73E\x5\x1D3\xEA\x2\x73E\x73F\x5"); + sb.Append("\x1C7\xE4\x2\x73F\x1D2\x3\x2\x2\x2\x740\x741\t\x14\x2\x2\x741"); + sb.Append("\x1D4\x3\x2\x2\x2\x37\x2\x617\x619\x623\x629\x62B\x635\x640"); + sb.Append("\x642\x64A\x64C\x654\x656\x660\x666\x66D\x671\x67C\x687\x68B"); + sb.Append("\x68F\x693\x69B\x6A0\x6A5\x6A7\x6AD\x6B1\x6B5\x6BB\x6C0\x6CA"); + sb.Append("\x6CE\x6D4\x6D8\x6E0\x6E4\x6EA\x6F4\x6F8\x6FE\x703\x706\x709"); + sb.Append("\x70E\x711\x716\x71B\x723\x72E\x732\x737\x73B\x3\x2\x3\x2"); + return sb.ToString(); + } + + public static readonly ATN _ATN = + new ATNDeserializer().Deserialize(_serializedATN.ToCharArray()); + + +} +} // namespace com.espertech.esper.epl.generated diff --git a/NEsper.Core/NEsper.Core/epl/generated/EsperEPL2GrammarListener.cs b/NEsper.Core/NEsper.Core/epl/generated/EsperEPL2GrammarListener.cs new file mode 100755 index 000000000..8e529c9dc --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/generated/EsperEPL2GrammarListener.cs @@ -0,0 +1,2791 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// ANTLR Version: 4.6 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +// Generated from C:\Src\Espertech\NEsper-master\NEsper\grammar\EsperEPL2Grammar.g4 by ANTLR 4.6 + +// Unreachable code detected +#pragma warning disable 0162 +// The variable '...' is assigned but its value is never used +#pragma warning disable 0219 +// Missing XML comment for publicly visible type or member '...' +#pragma warning disable 1591 +// Ambiguous reference in cref attribute +#pragma warning disable 419 + +namespace com.espertech.esper.epl.generated { + + using System; + using System.Collections.Generic; + +using Antlr4.Runtime.Misc; +using IParseTreeListener = Antlr4.Runtime.Tree.IParseTreeListener; +using IToken = Antlr4.Runtime.IToken; + +/// +/// This interface defines a complete listener for a parse tree produced by +/// . +/// +[System.CodeDom.Compiler.GeneratedCode("ANTLR", "4.6")] +[System.CLSCompliant(false)] +public interface IEsperEPL2GrammarListener : IParseTreeListener { + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterStartPatternExpressionRule([NotNull] EsperEPL2GrammarParser.StartPatternExpressionRuleContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitStartPatternExpressionRule([NotNull] EsperEPL2GrammarParser.StartPatternExpressionRuleContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterStartEPLExpressionRule([NotNull] EsperEPL2GrammarParser.StartEPLExpressionRuleContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitStartEPLExpressionRule([NotNull] EsperEPL2GrammarParser.StartEPLExpressionRuleContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterStartEventPropertyRule([NotNull] EsperEPL2GrammarParser.StartEventPropertyRuleContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitStartEventPropertyRule([NotNull] EsperEPL2GrammarParser.StartEventPropertyRuleContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterStartJsonValueRule([NotNull] EsperEPL2GrammarParser.StartJsonValueRuleContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitStartJsonValueRule([NotNull] EsperEPL2GrammarParser.StartJsonValueRuleContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterExpressionDecl([NotNull] EsperEPL2GrammarParser.ExpressionDeclContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitExpressionDecl([NotNull] EsperEPL2GrammarParser.ExpressionDeclContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterExpressionDialect([NotNull] EsperEPL2GrammarParser.ExpressionDialectContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitExpressionDialect([NotNull] EsperEPL2GrammarParser.ExpressionDialectContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterExpressionDef([NotNull] EsperEPL2GrammarParser.ExpressionDefContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitExpressionDef([NotNull] EsperEPL2GrammarParser.ExpressionDefContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterExpressionLambdaDecl([NotNull] EsperEPL2GrammarParser.ExpressionLambdaDeclContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitExpressionLambdaDecl([NotNull] EsperEPL2GrammarParser.ExpressionLambdaDeclContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterExpressionTypeAnno([NotNull] EsperEPL2GrammarParser.ExpressionTypeAnnoContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitExpressionTypeAnno([NotNull] EsperEPL2GrammarParser.ExpressionTypeAnnoContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterAnnotationEnum([NotNull] EsperEPL2GrammarParser.AnnotationEnumContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitAnnotationEnum([NotNull] EsperEPL2GrammarParser.AnnotationEnumContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterElementValuePairsEnum([NotNull] EsperEPL2GrammarParser.ElementValuePairsEnumContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitElementValuePairsEnum([NotNull] EsperEPL2GrammarParser.ElementValuePairsEnumContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterElementValuePairEnum([NotNull] EsperEPL2GrammarParser.ElementValuePairEnumContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitElementValuePairEnum([NotNull] EsperEPL2GrammarParser.ElementValuePairEnumContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterElementValueEnum([NotNull] EsperEPL2GrammarParser.ElementValueEnumContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitElementValueEnum([NotNull] EsperEPL2GrammarParser.ElementValueEnumContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterElementValueArrayEnum([NotNull] EsperEPL2GrammarParser.ElementValueArrayEnumContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitElementValueArrayEnum([NotNull] EsperEPL2GrammarParser.ElementValueArrayEnumContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterEplExpression([NotNull] EsperEPL2GrammarParser.EplExpressionContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitEplExpression([NotNull] EsperEPL2GrammarParser.EplExpressionContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterContextExpr([NotNull] EsperEPL2GrammarParser.ContextExprContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitContextExpr([NotNull] EsperEPL2GrammarParser.ContextExprContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterSelectExpr([NotNull] EsperEPL2GrammarParser.SelectExprContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitSelectExpr([NotNull] EsperEPL2GrammarParser.SelectExprContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterOnExpr([NotNull] EsperEPL2GrammarParser.OnExprContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitOnExpr([NotNull] EsperEPL2GrammarParser.OnExprContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterOnStreamExpr([NotNull] EsperEPL2GrammarParser.OnStreamExprContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitOnStreamExpr([NotNull] EsperEPL2GrammarParser.OnStreamExprContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterUpdateExpr([NotNull] EsperEPL2GrammarParser.UpdateExprContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitUpdateExpr([NotNull] EsperEPL2GrammarParser.UpdateExprContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterUpdateDetails([NotNull] EsperEPL2GrammarParser.UpdateDetailsContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitUpdateDetails([NotNull] EsperEPL2GrammarParser.UpdateDetailsContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterOnMergeExpr([NotNull] EsperEPL2GrammarParser.OnMergeExprContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitOnMergeExpr([NotNull] EsperEPL2GrammarParser.OnMergeExprContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterMergeItem([NotNull] EsperEPL2GrammarParser.MergeItemContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitMergeItem([NotNull] EsperEPL2GrammarParser.MergeItemContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterMergeMatched([NotNull] EsperEPL2GrammarParser.MergeMatchedContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitMergeMatched([NotNull] EsperEPL2GrammarParser.MergeMatchedContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterMergeMatchedItem([NotNull] EsperEPL2GrammarParser.MergeMatchedItemContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitMergeMatchedItem([NotNull] EsperEPL2GrammarParser.MergeMatchedItemContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterMergeUnmatched([NotNull] EsperEPL2GrammarParser.MergeUnmatchedContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitMergeUnmatched([NotNull] EsperEPL2GrammarParser.MergeUnmatchedContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterMergeUnmatchedItem([NotNull] EsperEPL2GrammarParser.MergeUnmatchedItemContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitMergeUnmatchedItem([NotNull] EsperEPL2GrammarParser.MergeUnmatchedItemContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterMergeInsert([NotNull] EsperEPL2GrammarParser.MergeInsertContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitMergeInsert([NotNull] EsperEPL2GrammarParser.MergeInsertContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterOnSelectExpr([NotNull] EsperEPL2GrammarParser.OnSelectExprContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitOnSelectExpr([NotNull] EsperEPL2GrammarParser.OnSelectExprContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterOnUpdateExpr([NotNull] EsperEPL2GrammarParser.OnUpdateExprContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitOnUpdateExpr([NotNull] EsperEPL2GrammarParser.OnUpdateExprContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterOnSelectInsertExpr([NotNull] EsperEPL2GrammarParser.OnSelectInsertExprContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitOnSelectInsertExpr([NotNull] EsperEPL2GrammarParser.OnSelectInsertExprContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterOnSelectInsertFromClause([NotNull] EsperEPL2GrammarParser.OnSelectInsertFromClauseContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitOnSelectInsertFromClause([NotNull] EsperEPL2GrammarParser.OnSelectInsertFromClauseContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterOutputClauseInsert([NotNull] EsperEPL2GrammarParser.OutputClauseInsertContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitOutputClauseInsert([NotNull] EsperEPL2GrammarParser.OutputClauseInsertContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterOnDeleteExpr([NotNull] EsperEPL2GrammarParser.OnDeleteExprContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitOnDeleteExpr([NotNull] EsperEPL2GrammarParser.OnDeleteExprContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterOnSetExpr([NotNull] EsperEPL2GrammarParser.OnSetExprContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitOnSetExpr([NotNull] EsperEPL2GrammarParser.OnSetExprContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterOnSetAssignmentList([NotNull] EsperEPL2GrammarParser.OnSetAssignmentListContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitOnSetAssignmentList([NotNull] EsperEPL2GrammarParser.OnSetAssignmentListContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterOnSetAssignment([NotNull] EsperEPL2GrammarParser.OnSetAssignmentContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitOnSetAssignment([NotNull] EsperEPL2GrammarParser.OnSetAssignmentContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterOnExprFrom([NotNull] EsperEPL2GrammarParser.OnExprFromContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitOnExprFrom([NotNull] EsperEPL2GrammarParser.OnExprFromContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterCreateWindowExpr([NotNull] EsperEPL2GrammarParser.CreateWindowExprContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitCreateWindowExpr([NotNull] EsperEPL2GrammarParser.CreateWindowExprContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterCreateWindowExprModelAfter([NotNull] EsperEPL2GrammarParser.CreateWindowExprModelAfterContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitCreateWindowExprModelAfter([NotNull] EsperEPL2GrammarParser.CreateWindowExprModelAfterContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterCreateIndexExpr([NotNull] EsperEPL2GrammarParser.CreateIndexExprContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitCreateIndexExpr([NotNull] EsperEPL2GrammarParser.CreateIndexExprContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterCreateIndexColumnList([NotNull] EsperEPL2GrammarParser.CreateIndexColumnListContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitCreateIndexColumnList([NotNull] EsperEPL2GrammarParser.CreateIndexColumnListContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterCreateIndexColumn([NotNull] EsperEPL2GrammarParser.CreateIndexColumnContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitCreateIndexColumn([NotNull] EsperEPL2GrammarParser.CreateIndexColumnContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterCreateVariableExpr([NotNull] EsperEPL2GrammarParser.CreateVariableExprContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitCreateVariableExpr([NotNull] EsperEPL2GrammarParser.CreateVariableExprContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterCreateTableExpr([NotNull] EsperEPL2GrammarParser.CreateTableExprContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitCreateTableExpr([NotNull] EsperEPL2GrammarParser.CreateTableExprContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterCreateTableColumnList([NotNull] EsperEPL2GrammarParser.CreateTableColumnListContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitCreateTableColumnList([NotNull] EsperEPL2GrammarParser.CreateTableColumnListContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterCreateTableColumn([NotNull] EsperEPL2GrammarParser.CreateTableColumnContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitCreateTableColumn([NotNull] EsperEPL2GrammarParser.CreateTableColumnContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterCreateTableColumnPlain([NotNull] EsperEPL2GrammarParser.CreateTableColumnPlainContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitCreateTableColumnPlain([NotNull] EsperEPL2GrammarParser.CreateTableColumnPlainContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterCreateColumnList([NotNull] EsperEPL2GrammarParser.CreateColumnListContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitCreateColumnList([NotNull] EsperEPL2GrammarParser.CreateColumnListContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterCreateColumnListElement([NotNull] EsperEPL2GrammarParser.CreateColumnListElementContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitCreateColumnListElement([NotNull] EsperEPL2GrammarParser.CreateColumnListElementContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterCreateSelectionList([NotNull] EsperEPL2GrammarParser.CreateSelectionListContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitCreateSelectionList([NotNull] EsperEPL2GrammarParser.CreateSelectionListContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterCreateSelectionListElement([NotNull] EsperEPL2GrammarParser.CreateSelectionListElementContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitCreateSelectionListElement([NotNull] EsperEPL2GrammarParser.CreateSelectionListElementContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterCreateSchemaExpr([NotNull] EsperEPL2GrammarParser.CreateSchemaExprContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitCreateSchemaExpr([NotNull] EsperEPL2GrammarParser.CreateSchemaExprContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterCreateSchemaDef([NotNull] EsperEPL2GrammarParser.CreateSchemaDefContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitCreateSchemaDef([NotNull] EsperEPL2GrammarParser.CreateSchemaDefContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterFafDelete([NotNull] EsperEPL2GrammarParser.FafDeleteContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitFafDelete([NotNull] EsperEPL2GrammarParser.FafDeleteContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterFafUpdate([NotNull] EsperEPL2GrammarParser.FafUpdateContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitFafUpdate([NotNull] EsperEPL2GrammarParser.FafUpdateContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterFafInsert([NotNull] EsperEPL2GrammarParser.FafInsertContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitFafInsert([NotNull] EsperEPL2GrammarParser.FafInsertContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterCreateDataflow([NotNull] EsperEPL2GrammarParser.CreateDataflowContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitCreateDataflow([NotNull] EsperEPL2GrammarParser.CreateDataflowContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterGopList([NotNull] EsperEPL2GrammarParser.GopListContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitGopList([NotNull] EsperEPL2GrammarParser.GopListContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterGop([NotNull] EsperEPL2GrammarParser.GopContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitGop([NotNull] EsperEPL2GrammarParser.GopContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterGopParams([NotNull] EsperEPL2GrammarParser.GopParamsContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitGopParams([NotNull] EsperEPL2GrammarParser.GopParamsContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterGopParamsItemList([NotNull] EsperEPL2GrammarParser.GopParamsItemListContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitGopParamsItemList([NotNull] EsperEPL2GrammarParser.GopParamsItemListContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterGopParamsItem([NotNull] EsperEPL2GrammarParser.GopParamsItemContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitGopParamsItem([NotNull] EsperEPL2GrammarParser.GopParamsItemContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterGopParamsItemMany([NotNull] EsperEPL2GrammarParser.GopParamsItemManyContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitGopParamsItemMany([NotNull] EsperEPL2GrammarParser.GopParamsItemManyContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterGopParamsItemAs([NotNull] EsperEPL2GrammarParser.GopParamsItemAsContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitGopParamsItemAs([NotNull] EsperEPL2GrammarParser.GopParamsItemAsContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterGopOut([NotNull] EsperEPL2GrammarParser.GopOutContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitGopOut([NotNull] EsperEPL2GrammarParser.GopOutContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterGopOutItem([NotNull] EsperEPL2GrammarParser.GopOutItemContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitGopOutItem([NotNull] EsperEPL2GrammarParser.GopOutItemContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterGopOutTypeList([NotNull] EsperEPL2GrammarParser.GopOutTypeListContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitGopOutTypeList([NotNull] EsperEPL2GrammarParser.GopOutTypeListContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterGopOutTypeParam([NotNull] EsperEPL2GrammarParser.GopOutTypeParamContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitGopOutTypeParam([NotNull] EsperEPL2GrammarParser.GopOutTypeParamContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterGopOutTypeItem([NotNull] EsperEPL2GrammarParser.GopOutTypeItemContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitGopOutTypeItem([NotNull] EsperEPL2GrammarParser.GopOutTypeItemContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterGopDetail([NotNull] EsperEPL2GrammarParser.GopDetailContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitGopDetail([NotNull] EsperEPL2GrammarParser.GopDetailContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterGopConfig([NotNull] EsperEPL2GrammarParser.GopConfigContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitGopConfig([NotNull] EsperEPL2GrammarParser.GopConfigContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterCreateContextExpr([NotNull] EsperEPL2GrammarParser.CreateContextExprContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitCreateContextExpr([NotNull] EsperEPL2GrammarParser.CreateContextExprContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterCreateExpressionExpr([NotNull] EsperEPL2GrammarParser.CreateExpressionExprContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitCreateExpressionExpr([NotNull] EsperEPL2GrammarParser.CreateExpressionExprContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterCreateContextDetail([NotNull] EsperEPL2GrammarParser.CreateContextDetailContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitCreateContextDetail([NotNull] EsperEPL2GrammarParser.CreateContextDetailContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterContextContextNested([NotNull] EsperEPL2GrammarParser.ContextContextNestedContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitContextContextNested([NotNull] EsperEPL2GrammarParser.ContextContextNestedContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterCreateContextChoice([NotNull] EsperEPL2GrammarParser.CreateContextChoiceContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitCreateContextChoice([NotNull] EsperEPL2GrammarParser.CreateContextChoiceContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterCreateContextDistinct([NotNull] EsperEPL2GrammarParser.CreateContextDistinctContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitCreateContextDistinct([NotNull] EsperEPL2GrammarParser.CreateContextDistinctContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterCreateContextRangePoint([NotNull] EsperEPL2GrammarParser.CreateContextRangePointContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitCreateContextRangePoint([NotNull] EsperEPL2GrammarParser.CreateContextRangePointContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterCreateContextFilter([NotNull] EsperEPL2GrammarParser.CreateContextFilterContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitCreateContextFilter([NotNull] EsperEPL2GrammarParser.CreateContextFilterContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterCreateContextPartitionItem([NotNull] EsperEPL2GrammarParser.CreateContextPartitionItemContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitCreateContextPartitionItem([NotNull] EsperEPL2GrammarParser.CreateContextPartitionItemContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterCreateContextCoalesceItem([NotNull] EsperEPL2GrammarParser.CreateContextCoalesceItemContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitCreateContextCoalesceItem([NotNull] EsperEPL2GrammarParser.CreateContextCoalesceItemContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterCreateContextGroupItem([NotNull] EsperEPL2GrammarParser.CreateContextGroupItemContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitCreateContextGroupItem([NotNull] EsperEPL2GrammarParser.CreateContextGroupItemContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterCreateSchemaQual([NotNull] EsperEPL2GrammarParser.CreateSchemaQualContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitCreateSchemaQual([NotNull] EsperEPL2GrammarParser.CreateSchemaQualContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterVariantList([NotNull] EsperEPL2GrammarParser.VariantListContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitVariantList([NotNull] EsperEPL2GrammarParser.VariantListContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterVariantListElement([NotNull] EsperEPL2GrammarParser.VariantListElementContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitVariantListElement([NotNull] EsperEPL2GrammarParser.VariantListElementContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterIntoTableExpr([NotNull] EsperEPL2GrammarParser.IntoTableExprContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitIntoTableExpr([NotNull] EsperEPL2GrammarParser.IntoTableExprContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterInsertIntoExpr([NotNull] EsperEPL2GrammarParser.InsertIntoExprContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitInsertIntoExpr([NotNull] EsperEPL2GrammarParser.InsertIntoExprContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterColumnList([NotNull] EsperEPL2GrammarParser.ColumnListContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitColumnList([NotNull] EsperEPL2GrammarParser.ColumnListContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterFromClause([NotNull] EsperEPL2GrammarParser.FromClauseContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitFromClause([NotNull] EsperEPL2GrammarParser.FromClauseContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterRegularJoin([NotNull] EsperEPL2GrammarParser.RegularJoinContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitRegularJoin([NotNull] EsperEPL2GrammarParser.RegularJoinContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterOuterJoinList([NotNull] EsperEPL2GrammarParser.OuterJoinListContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitOuterJoinList([NotNull] EsperEPL2GrammarParser.OuterJoinListContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterOuterJoin([NotNull] EsperEPL2GrammarParser.OuterJoinContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitOuterJoin([NotNull] EsperEPL2GrammarParser.OuterJoinContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterOuterJoinIdent([NotNull] EsperEPL2GrammarParser.OuterJoinIdentContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitOuterJoinIdent([NotNull] EsperEPL2GrammarParser.OuterJoinIdentContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterOuterJoinIdentPair([NotNull] EsperEPL2GrammarParser.OuterJoinIdentPairContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitOuterJoinIdentPair([NotNull] EsperEPL2GrammarParser.OuterJoinIdentPairContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterWhereClause([NotNull] EsperEPL2GrammarParser.WhereClauseContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitWhereClause([NotNull] EsperEPL2GrammarParser.WhereClauseContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterSelectClause([NotNull] EsperEPL2GrammarParser.SelectClauseContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitSelectClause([NotNull] EsperEPL2GrammarParser.SelectClauseContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterSelectionList([NotNull] EsperEPL2GrammarParser.SelectionListContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitSelectionList([NotNull] EsperEPL2GrammarParser.SelectionListContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterSelectionListElement([NotNull] EsperEPL2GrammarParser.SelectionListElementContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitSelectionListElement([NotNull] EsperEPL2GrammarParser.SelectionListElementContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterSelectionListElementExpr([NotNull] EsperEPL2GrammarParser.SelectionListElementExprContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitSelectionListElementExpr([NotNull] EsperEPL2GrammarParser.SelectionListElementExprContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterSelectionListElementAnno([NotNull] EsperEPL2GrammarParser.SelectionListElementAnnoContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitSelectionListElementAnno([NotNull] EsperEPL2GrammarParser.SelectionListElementAnnoContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterStreamSelector([NotNull] EsperEPL2GrammarParser.StreamSelectorContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitStreamSelector([NotNull] EsperEPL2GrammarParser.StreamSelectorContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterStreamExpression([NotNull] EsperEPL2GrammarParser.StreamExpressionContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitStreamExpression([NotNull] EsperEPL2GrammarParser.StreamExpressionContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterForExpr([NotNull] EsperEPL2GrammarParser.ForExprContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitForExpr([NotNull] EsperEPL2GrammarParser.ForExprContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterPatternInclusionExpression([NotNull] EsperEPL2GrammarParser.PatternInclusionExpressionContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitPatternInclusionExpression([NotNull] EsperEPL2GrammarParser.PatternInclusionExpressionContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterDatabaseJoinExpression([NotNull] EsperEPL2GrammarParser.DatabaseJoinExpressionContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitDatabaseJoinExpression([NotNull] EsperEPL2GrammarParser.DatabaseJoinExpressionContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterMethodJoinExpression([NotNull] EsperEPL2GrammarParser.MethodJoinExpressionContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitMethodJoinExpression([NotNull] EsperEPL2GrammarParser.MethodJoinExpressionContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterViewExpressions([NotNull] EsperEPL2GrammarParser.ViewExpressionsContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitViewExpressions([NotNull] EsperEPL2GrammarParser.ViewExpressionsContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterViewExpressionWNamespace([NotNull] EsperEPL2GrammarParser.ViewExpressionWNamespaceContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitViewExpressionWNamespace([NotNull] EsperEPL2GrammarParser.ViewExpressionWNamespaceContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterViewExpressionOptNamespace([NotNull] EsperEPL2GrammarParser.ViewExpressionOptNamespaceContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitViewExpressionOptNamespace([NotNull] EsperEPL2GrammarParser.ViewExpressionOptNamespaceContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterViewWParameters([NotNull] EsperEPL2GrammarParser.ViewWParametersContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitViewWParameters([NotNull] EsperEPL2GrammarParser.ViewWParametersContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterGroupByListExpr([NotNull] EsperEPL2GrammarParser.GroupByListExprContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitGroupByListExpr([NotNull] EsperEPL2GrammarParser.GroupByListExprContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterGroupByListChoice([NotNull] EsperEPL2GrammarParser.GroupByListChoiceContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitGroupByListChoice([NotNull] EsperEPL2GrammarParser.GroupByListChoiceContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterGroupByCubeOrRollup([NotNull] EsperEPL2GrammarParser.GroupByCubeOrRollupContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitGroupByCubeOrRollup([NotNull] EsperEPL2GrammarParser.GroupByCubeOrRollupContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterGroupByGroupingSets([NotNull] EsperEPL2GrammarParser.GroupByGroupingSetsContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitGroupByGroupingSets([NotNull] EsperEPL2GrammarParser.GroupByGroupingSetsContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterGroupBySetsChoice([NotNull] EsperEPL2GrammarParser.GroupBySetsChoiceContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitGroupBySetsChoice([NotNull] EsperEPL2GrammarParser.GroupBySetsChoiceContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterGroupByCombinableExpr([NotNull] EsperEPL2GrammarParser.GroupByCombinableExprContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitGroupByCombinableExpr([NotNull] EsperEPL2GrammarParser.GroupByCombinableExprContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterOrderByListExpr([NotNull] EsperEPL2GrammarParser.OrderByListExprContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitOrderByListExpr([NotNull] EsperEPL2GrammarParser.OrderByListExprContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterOrderByListElement([NotNull] EsperEPL2GrammarParser.OrderByListElementContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitOrderByListElement([NotNull] EsperEPL2GrammarParser.OrderByListElementContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterHavingClause([NotNull] EsperEPL2GrammarParser.HavingClauseContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitHavingClause([NotNull] EsperEPL2GrammarParser.HavingClauseContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterOutputLimit([NotNull] EsperEPL2GrammarParser.OutputLimitContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitOutputLimit([NotNull] EsperEPL2GrammarParser.OutputLimitContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterOutputLimitAndTerm([NotNull] EsperEPL2GrammarParser.OutputLimitAndTermContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitOutputLimitAndTerm([NotNull] EsperEPL2GrammarParser.OutputLimitAndTermContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterOutputLimitAfter([NotNull] EsperEPL2GrammarParser.OutputLimitAfterContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitOutputLimitAfter([NotNull] EsperEPL2GrammarParser.OutputLimitAfterContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterRowLimit([NotNull] EsperEPL2GrammarParser.RowLimitContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitRowLimit([NotNull] EsperEPL2GrammarParser.RowLimitContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterCrontabLimitParameterSet([NotNull] EsperEPL2GrammarParser.CrontabLimitParameterSetContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitCrontabLimitParameterSet([NotNull] EsperEPL2GrammarParser.CrontabLimitParameterSetContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterWhenClause([NotNull] EsperEPL2GrammarParser.WhenClauseContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitWhenClause([NotNull] EsperEPL2GrammarParser.WhenClauseContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterElseClause([NotNull] EsperEPL2GrammarParser.ElseClauseContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitElseClause([NotNull] EsperEPL2GrammarParser.ElseClauseContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterMatchRecog([NotNull] EsperEPL2GrammarParser.MatchRecogContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitMatchRecog([NotNull] EsperEPL2GrammarParser.MatchRecogContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterMatchRecogPartitionBy([NotNull] EsperEPL2GrammarParser.MatchRecogPartitionByContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitMatchRecogPartitionBy([NotNull] EsperEPL2GrammarParser.MatchRecogPartitionByContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterMatchRecogMeasures([NotNull] EsperEPL2GrammarParser.MatchRecogMeasuresContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitMatchRecogMeasures([NotNull] EsperEPL2GrammarParser.MatchRecogMeasuresContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterMatchRecogMeasureItem([NotNull] EsperEPL2GrammarParser.MatchRecogMeasureItemContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitMatchRecogMeasureItem([NotNull] EsperEPL2GrammarParser.MatchRecogMeasureItemContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterMatchRecogMatchesSelection([NotNull] EsperEPL2GrammarParser.MatchRecogMatchesSelectionContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitMatchRecogMatchesSelection([NotNull] EsperEPL2GrammarParser.MatchRecogMatchesSelectionContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterMatchRecogPattern([NotNull] EsperEPL2GrammarParser.MatchRecogPatternContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitMatchRecogPattern([NotNull] EsperEPL2GrammarParser.MatchRecogPatternContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterMatchRecogMatchesAfterSkip([NotNull] EsperEPL2GrammarParser.MatchRecogMatchesAfterSkipContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitMatchRecogMatchesAfterSkip([NotNull] EsperEPL2GrammarParser.MatchRecogMatchesAfterSkipContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterMatchRecogMatchesInterval([NotNull] EsperEPL2GrammarParser.MatchRecogMatchesIntervalContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitMatchRecogMatchesInterval([NotNull] EsperEPL2GrammarParser.MatchRecogMatchesIntervalContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterMatchRecogPatternAlteration([NotNull] EsperEPL2GrammarParser.MatchRecogPatternAlterationContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitMatchRecogPatternAlteration([NotNull] EsperEPL2GrammarParser.MatchRecogPatternAlterationContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterMatchRecogPatternConcat([NotNull] EsperEPL2GrammarParser.MatchRecogPatternConcatContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitMatchRecogPatternConcat([NotNull] EsperEPL2GrammarParser.MatchRecogPatternConcatContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterMatchRecogPatternUnary([NotNull] EsperEPL2GrammarParser.MatchRecogPatternUnaryContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitMatchRecogPatternUnary([NotNull] EsperEPL2GrammarParser.MatchRecogPatternUnaryContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterMatchRecogPatternNested([NotNull] EsperEPL2GrammarParser.MatchRecogPatternNestedContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitMatchRecogPatternNested([NotNull] EsperEPL2GrammarParser.MatchRecogPatternNestedContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterMatchRecogPatternPermute([NotNull] EsperEPL2GrammarParser.MatchRecogPatternPermuteContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitMatchRecogPatternPermute([NotNull] EsperEPL2GrammarParser.MatchRecogPatternPermuteContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterMatchRecogPatternAtom([NotNull] EsperEPL2GrammarParser.MatchRecogPatternAtomContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitMatchRecogPatternAtom([NotNull] EsperEPL2GrammarParser.MatchRecogPatternAtomContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterMatchRecogPatternRepeat([NotNull] EsperEPL2GrammarParser.MatchRecogPatternRepeatContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitMatchRecogPatternRepeat([NotNull] EsperEPL2GrammarParser.MatchRecogPatternRepeatContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterMatchRecogDefine([NotNull] EsperEPL2GrammarParser.MatchRecogDefineContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitMatchRecogDefine([NotNull] EsperEPL2GrammarParser.MatchRecogDefineContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterMatchRecogDefineItem([NotNull] EsperEPL2GrammarParser.MatchRecogDefineItemContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitMatchRecogDefineItem([NotNull] EsperEPL2GrammarParser.MatchRecogDefineItemContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterExpression([NotNull] EsperEPL2GrammarParser.ExpressionContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitExpression([NotNull] EsperEPL2GrammarParser.ExpressionContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterCaseExpression([NotNull] EsperEPL2GrammarParser.CaseExpressionContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitCaseExpression([NotNull] EsperEPL2GrammarParser.CaseExpressionContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterEvalOrExpression([NotNull] EsperEPL2GrammarParser.EvalOrExpressionContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitEvalOrExpression([NotNull] EsperEPL2GrammarParser.EvalOrExpressionContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterEvalAndExpression([NotNull] EsperEPL2GrammarParser.EvalAndExpressionContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitEvalAndExpression([NotNull] EsperEPL2GrammarParser.EvalAndExpressionContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterBitWiseExpression([NotNull] EsperEPL2GrammarParser.BitWiseExpressionContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitBitWiseExpression([NotNull] EsperEPL2GrammarParser.BitWiseExpressionContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterNegatedExpression([NotNull] EsperEPL2GrammarParser.NegatedExpressionContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitNegatedExpression([NotNull] EsperEPL2GrammarParser.NegatedExpressionContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterEvalEqualsExpression([NotNull] EsperEPL2GrammarParser.EvalEqualsExpressionContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitEvalEqualsExpression([NotNull] EsperEPL2GrammarParser.EvalEqualsExpressionContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterEvalRelationalExpression([NotNull] EsperEPL2GrammarParser.EvalRelationalExpressionContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitEvalRelationalExpression([NotNull] EsperEPL2GrammarParser.EvalRelationalExpressionContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterInSubSelectQuery([NotNull] EsperEPL2GrammarParser.InSubSelectQueryContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitInSubSelectQuery([NotNull] EsperEPL2GrammarParser.InSubSelectQueryContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterConcatenationExpr([NotNull] EsperEPL2GrammarParser.ConcatenationExprContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitConcatenationExpr([NotNull] EsperEPL2GrammarParser.ConcatenationExprContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterAdditiveExpression([NotNull] EsperEPL2GrammarParser.AdditiveExpressionContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitAdditiveExpression([NotNull] EsperEPL2GrammarParser.AdditiveExpressionContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterMultiplyExpression([NotNull] EsperEPL2GrammarParser.MultiplyExpressionContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitMultiplyExpression([NotNull] EsperEPL2GrammarParser.MultiplyExpressionContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterUnaryExpression([NotNull] EsperEPL2GrammarParser.UnaryExpressionContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitUnaryExpression([NotNull] EsperEPL2GrammarParser.UnaryExpressionContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterSubstitutionCanChain([NotNull] EsperEPL2GrammarParser.SubstitutionCanChainContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitSubstitutionCanChain([NotNull] EsperEPL2GrammarParser.SubstitutionCanChainContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterChainedFunction([NotNull] EsperEPL2GrammarParser.ChainedFunctionContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitChainedFunction([NotNull] EsperEPL2GrammarParser.ChainedFunctionContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterNewAssign([NotNull] EsperEPL2GrammarParser.NewAssignContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitNewAssign([NotNull] EsperEPL2GrammarParser.NewAssignContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterRowSubSelectExpression([NotNull] EsperEPL2GrammarParser.RowSubSelectExpressionContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitRowSubSelectExpression([NotNull] EsperEPL2GrammarParser.RowSubSelectExpressionContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterSubSelectGroupExpression([NotNull] EsperEPL2GrammarParser.SubSelectGroupExpressionContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitSubSelectGroupExpression([NotNull] EsperEPL2GrammarParser.SubSelectGroupExpressionContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterExistsSubSelectExpression([NotNull] EsperEPL2GrammarParser.ExistsSubSelectExpressionContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitExistsSubSelectExpression([NotNull] EsperEPL2GrammarParser.ExistsSubSelectExpressionContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterSubQueryExpr([NotNull] EsperEPL2GrammarParser.SubQueryExprContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitSubQueryExpr([NotNull] EsperEPL2GrammarParser.SubQueryExprContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterSubSelectFilterExpr([NotNull] EsperEPL2GrammarParser.SubSelectFilterExprContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitSubSelectFilterExpr([NotNull] EsperEPL2GrammarParser.SubSelectFilterExprContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterArrayExpression([NotNull] EsperEPL2GrammarParser.ArrayExpressionContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitArrayExpression([NotNull] EsperEPL2GrammarParser.ArrayExpressionContext context); + /// + /// Enter a parse tree produced by the builtin_sum + /// labeled alternative in . + /// + /// The parse tree. + void EnterBuiltin_sum([NotNull] EsperEPL2GrammarParser.Builtin_sumContext context); + /// + /// Exit a parse tree produced by the builtin_sum + /// labeled alternative in . + /// + /// The parse tree. + void ExitBuiltin_sum([NotNull] EsperEPL2GrammarParser.Builtin_sumContext context); + /// + /// Enter a parse tree produced by the builtin_avg + /// labeled alternative in . + /// + /// The parse tree. + void EnterBuiltin_avg([NotNull] EsperEPL2GrammarParser.Builtin_avgContext context); + /// + /// Exit a parse tree produced by the builtin_avg + /// labeled alternative in . + /// + /// The parse tree. + void ExitBuiltin_avg([NotNull] EsperEPL2GrammarParser.Builtin_avgContext context); + /// + /// Enter a parse tree produced by the builtin_cnt + /// labeled alternative in . + /// + /// The parse tree. + void EnterBuiltin_cnt([NotNull] EsperEPL2GrammarParser.Builtin_cntContext context); + /// + /// Exit a parse tree produced by the builtin_cnt + /// labeled alternative in . + /// + /// The parse tree. + void ExitBuiltin_cnt([NotNull] EsperEPL2GrammarParser.Builtin_cntContext context); + /// + /// Enter a parse tree produced by the builtin_median + /// labeled alternative in . + /// + /// The parse tree. + void EnterBuiltin_median([NotNull] EsperEPL2GrammarParser.Builtin_medianContext context); + /// + /// Exit a parse tree produced by the builtin_median + /// labeled alternative in . + /// + /// The parse tree. + void ExitBuiltin_median([NotNull] EsperEPL2GrammarParser.Builtin_medianContext context); + /// + /// Enter a parse tree produced by the builtin_stddev + /// labeled alternative in . + /// + /// The parse tree. + void EnterBuiltin_stddev([NotNull] EsperEPL2GrammarParser.Builtin_stddevContext context); + /// + /// Exit a parse tree produced by the builtin_stddev + /// labeled alternative in . + /// + /// The parse tree. + void ExitBuiltin_stddev([NotNull] EsperEPL2GrammarParser.Builtin_stddevContext context); + /// + /// Enter a parse tree produced by the builtin_avedev + /// labeled alternative in . + /// + /// The parse tree. + void EnterBuiltin_avedev([NotNull] EsperEPL2GrammarParser.Builtin_avedevContext context); + /// + /// Exit a parse tree produced by the builtin_avedev + /// labeled alternative in . + /// + /// The parse tree. + void ExitBuiltin_avedev([NotNull] EsperEPL2GrammarParser.Builtin_avedevContext context); + /// + /// Enter a parse tree produced by the builtin_firstlastwindow + /// labeled alternative in . + /// + /// The parse tree. + void EnterBuiltin_firstlastwindow([NotNull] EsperEPL2GrammarParser.Builtin_firstlastwindowContext context); + /// + /// Exit a parse tree produced by the builtin_firstlastwindow + /// labeled alternative in . + /// + /// The parse tree. + void ExitBuiltin_firstlastwindow([NotNull] EsperEPL2GrammarParser.Builtin_firstlastwindowContext context); + /// + /// Enter a parse tree produced by the builtin_coalesce + /// labeled alternative in . + /// + /// The parse tree. + void EnterBuiltin_coalesce([NotNull] EsperEPL2GrammarParser.Builtin_coalesceContext context); + /// + /// Exit a parse tree produced by the builtin_coalesce + /// labeled alternative in . + /// + /// The parse tree. + void ExitBuiltin_coalesce([NotNull] EsperEPL2GrammarParser.Builtin_coalesceContext context); + /// + /// Enter a parse tree produced by the builtin_prev + /// labeled alternative in . + /// + /// The parse tree. + void EnterBuiltin_prev([NotNull] EsperEPL2GrammarParser.Builtin_prevContext context); + /// + /// Exit a parse tree produced by the builtin_prev + /// labeled alternative in . + /// + /// The parse tree. + void ExitBuiltin_prev([NotNull] EsperEPL2GrammarParser.Builtin_prevContext context); + /// + /// Enter a parse tree produced by the builtin_prevtail + /// labeled alternative in . + /// + /// The parse tree. + void EnterBuiltin_prevtail([NotNull] EsperEPL2GrammarParser.Builtin_prevtailContext context); + /// + /// Exit a parse tree produced by the builtin_prevtail + /// labeled alternative in . + /// + /// The parse tree. + void ExitBuiltin_prevtail([NotNull] EsperEPL2GrammarParser.Builtin_prevtailContext context); + /// + /// Enter a parse tree produced by the builtin_prevcount + /// labeled alternative in . + /// + /// The parse tree. + void EnterBuiltin_prevcount([NotNull] EsperEPL2GrammarParser.Builtin_prevcountContext context); + /// + /// Exit a parse tree produced by the builtin_prevcount + /// labeled alternative in . + /// + /// The parse tree. + void ExitBuiltin_prevcount([NotNull] EsperEPL2GrammarParser.Builtin_prevcountContext context); + /// + /// Enter a parse tree produced by the builtin_prevwindow + /// labeled alternative in . + /// + /// The parse tree. + void EnterBuiltin_prevwindow([NotNull] EsperEPL2GrammarParser.Builtin_prevwindowContext context); + /// + /// Exit a parse tree produced by the builtin_prevwindow + /// labeled alternative in . + /// + /// The parse tree. + void ExitBuiltin_prevwindow([NotNull] EsperEPL2GrammarParser.Builtin_prevwindowContext context); + /// + /// Enter a parse tree produced by the builtin_prior + /// labeled alternative in . + /// + /// The parse tree. + void EnterBuiltin_prior([NotNull] EsperEPL2GrammarParser.Builtin_priorContext context); + /// + /// Exit a parse tree produced by the builtin_prior + /// labeled alternative in . + /// + /// The parse tree. + void ExitBuiltin_prior([NotNull] EsperEPL2GrammarParser.Builtin_priorContext context); + /// + /// Enter a parse tree produced by the builtin_grouping + /// labeled alternative in . + /// + /// The parse tree. + void EnterBuiltin_grouping([NotNull] EsperEPL2GrammarParser.Builtin_groupingContext context); + /// + /// Exit a parse tree produced by the builtin_grouping + /// labeled alternative in . + /// + /// The parse tree. + void ExitBuiltin_grouping([NotNull] EsperEPL2GrammarParser.Builtin_groupingContext context); + /// + /// Enter a parse tree produced by the builtin_groupingid + /// labeled alternative in . + /// + /// The parse tree. + void EnterBuiltin_groupingid([NotNull] EsperEPL2GrammarParser.Builtin_groupingidContext context); + /// + /// Exit a parse tree produced by the builtin_groupingid + /// labeled alternative in . + /// + /// The parse tree. + void ExitBuiltin_groupingid([NotNull] EsperEPL2GrammarParser.Builtin_groupingidContext context); + /// + /// Enter a parse tree produced by the builtin_instanceof + /// labeled alternative in . + /// + /// The parse tree. + void EnterBuiltin_instanceof([NotNull] EsperEPL2GrammarParser.Builtin_instanceofContext context); + /// + /// Exit a parse tree produced by the builtin_instanceof + /// labeled alternative in . + /// + /// The parse tree. + void ExitBuiltin_instanceof([NotNull] EsperEPL2GrammarParser.Builtin_instanceofContext context); + /// + /// Enter a parse tree produced by the builtin_typeof + /// labeled alternative in . + /// + /// The parse tree. + void EnterBuiltin_typeof([NotNull] EsperEPL2GrammarParser.Builtin_typeofContext context); + /// + /// Exit a parse tree produced by the builtin_typeof + /// labeled alternative in . + /// + /// The parse tree. + void ExitBuiltin_typeof([NotNull] EsperEPL2GrammarParser.Builtin_typeofContext context); + /// + /// Enter a parse tree produced by the builtin_cast + /// labeled alternative in . + /// + /// The parse tree. + void EnterBuiltin_cast([NotNull] EsperEPL2GrammarParser.Builtin_castContext context); + /// + /// Exit a parse tree produced by the builtin_cast + /// labeled alternative in . + /// + /// The parse tree. + void ExitBuiltin_cast([NotNull] EsperEPL2GrammarParser.Builtin_castContext context); + /// + /// Enter a parse tree produced by the builtin_exists + /// labeled alternative in . + /// + /// The parse tree. + void EnterBuiltin_exists([NotNull] EsperEPL2GrammarParser.Builtin_existsContext context); + /// + /// Exit a parse tree produced by the builtin_exists + /// labeled alternative in . + /// + /// The parse tree. + void ExitBuiltin_exists([NotNull] EsperEPL2GrammarParser.Builtin_existsContext context); + /// + /// Enter a parse tree produced by the builtin_currts + /// labeled alternative in . + /// + /// The parse tree. + void EnterBuiltin_currts([NotNull] EsperEPL2GrammarParser.Builtin_currtsContext context); + /// + /// Exit a parse tree produced by the builtin_currts + /// labeled alternative in . + /// + /// The parse tree. + void ExitBuiltin_currts([NotNull] EsperEPL2GrammarParser.Builtin_currtsContext context); + /// + /// Enter a parse tree produced by the builtin_istream + /// labeled alternative in . + /// + /// The parse tree. + void EnterBuiltin_istream([NotNull] EsperEPL2GrammarParser.Builtin_istreamContext context); + /// + /// Exit a parse tree produced by the builtin_istream + /// labeled alternative in . + /// + /// The parse tree. + void ExitBuiltin_istream([NotNull] EsperEPL2GrammarParser.Builtin_istreamContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterFirstLastWindowAggregation([NotNull] EsperEPL2GrammarParser.FirstLastWindowAggregationContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitFirstLastWindowAggregation([NotNull] EsperEPL2GrammarParser.FirstLastWindowAggregationContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterEventPropertyOrLibFunction([NotNull] EsperEPL2GrammarParser.EventPropertyOrLibFunctionContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitEventPropertyOrLibFunction([NotNull] EsperEPL2GrammarParser.EventPropertyOrLibFunctionContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterLibFunction([NotNull] EsperEPL2GrammarParser.LibFunctionContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitLibFunction([NotNull] EsperEPL2GrammarParser.LibFunctionContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterLibFunctionWithClass([NotNull] EsperEPL2GrammarParser.LibFunctionWithClassContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitLibFunctionWithClass([NotNull] EsperEPL2GrammarParser.LibFunctionWithClassContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterLibFunctionNoClass([NotNull] EsperEPL2GrammarParser.LibFunctionNoClassContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitLibFunctionNoClass([NotNull] EsperEPL2GrammarParser.LibFunctionNoClassContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterFuncIdentTop([NotNull] EsperEPL2GrammarParser.FuncIdentTopContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitFuncIdentTop([NotNull] EsperEPL2GrammarParser.FuncIdentTopContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterFuncIdentInner([NotNull] EsperEPL2GrammarParser.FuncIdentInnerContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitFuncIdentInner([NotNull] EsperEPL2GrammarParser.FuncIdentInnerContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterFuncIdentChained([NotNull] EsperEPL2GrammarParser.FuncIdentChainedContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitFuncIdentChained([NotNull] EsperEPL2GrammarParser.FuncIdentChainedContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterLibFunctionArgs([NotNull] EsperEPL2GrammarParser.LibFunctionArgsContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitLibFunctionArgs([NotNull] EsperEPL2GrammarParser.LibFunctionArgsContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterLibFunctionArgItem([NotNull] EsperEPL2GrammarParser.LibFunctionArgItemContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitLibFunctionArgItem([NotNull] EsperEPL2GrammarParser.LibFunctionArgItemContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterBetweenList([NotNull] EsperEPL2GrammarParser.BetweenListContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitBetweenList([NotNull] EsperEPL2GrammarParser.BetweenListContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterPatternExpression([NotNull] EsperEPL2GrammarParser.PatternExpressionContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitPatternExpression([NotNull] EsperEPL2GrammarParser.PatternExpressionContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterFollowedByExpression([NotNull] EsperEPL2GrammarParser.FollowedByExpressionContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitFollowedByExpression([NotNull] EsperEPL2GrammarParser.FollowedByExpressionContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterFollowedByRepeat([NotNull] EsperEPL2GrammarParser.FollowedByRepeatContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitFollowedByRepeat([NotNull] EsperEPL2GrammarParser.FollowedByRepeatContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterOrExpression([NotNull] EsperEPL2GrammarParser.OrExpressionContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitOrExpression([NotNull] EsperEPL2GrammarParser.OrExpressionContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterAndExpression([NotNull] EsperEPL2GrammarParser.AndExpressionContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitAndExpression([NotNull] EsperEPL2GrammarParser.AndExpressionContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterMatchUntilExpression([NotNull] EsperEPL2GrammarParser.MatchUntilExpressionContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitMatchUntilExpression([NotNull] EsperEPL2GrammarParser.MatchUntilExpressionContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterQualifyExpression([NotNull] EsperEPL2GrammarParser.QualifyExpressionContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitQualifyExpression([NotNull] EsperEPL2GrammarParser.QualifyExpressionContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterGuardPostFix([NotNull] EsperEPL2GrammarParser.GuardPostFixContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitGuardPostFix([NotNull] EsperEPL2GrammarParser.GuardPostFixContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterDistinctExpressionList([NotNull] EsperEPL2GrammarParser.DistinctExpressionListContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitDistinctExpressionList([NotNull] EsperEPL2GrammarParser.DistinctExpressionListContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterDistinctExpressionAtom([NotNull] EsperEPL2GrammarParser.DistinctExpressionAtomContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitDistinctExpressionAtom([NotNull] EsperEPL2GrammarParser.DistinctExpressionAtomContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterAtomicExpression([NotNull] EsperEPL2GrammarParser.AtomicExpressionContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitAtomicExpression([NotNull] EsperEPL2GrammarParser.AtomicExpressionContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterObserverExpression([NotNull] EsperEPL2GrammarParser.ObserverExpressionContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitObserverExpression([NotNull] EsperEPL2GrammarParser.ObserverExpressionContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterGuardWhereExpression([NotNull] EsperEPL2GrammarParser.GuardWhereExpressionContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitGuardWhereExpression([NotNull] EsperEPL2GrammarParser.GuardWhereExpressionContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterGuardWhileExpression([NotNull] EsperEPL2GrammarParser.GuardWhileExpressionContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitGuardWhileExpression([NotNull] EsperEPL2GrammarParser.GuardWhileExpressionContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterMatchUntilRange([NotNull] EsperEPL2GrammarParser.MatchUntilRangeContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitMatchUntilRange([NotNull] EsperEPL2GrammarParser.MatchUntilRangeContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterEventFilterExpression([NotNull] EsperEPL2GrammarParser.EventFilterExpressionContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitEventFilterExpression([NotNull] EsperEPL2GrammarParser.EventFilterExpressionContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterPropertyExpression([NotNull] EsperEPL2GrammarParser.PropertyExpressionContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitPropertyExpression([NotNull] EsperEPL2GrammarParser.PropertyExpressionContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterPropertyExpressionAtomic([NotNull] EsperEPL2GrammarParser.PropertyExpressionAtomicContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitPropertyExpressionAtomic([NotNull] EsperEPL2GrammarParser.PropertyExpressionAtomicContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterPropertyExpressionSelect([NotNull] EsperEPL2GrammarParser.PropertyExpressionSelectContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitPropertyExpressionSelect([NotNull] EsperEPL2GrammarParser.PropertyExpressionSelectContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterPropertySelectionList([NotNull] EsperEPL2GrammarParser.PropertySelectionListContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitPropertySelectionList([NotNull] EsperEPL2GrammarParser.PropertySelectionListContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterPropertySelectionListElement([NotNull] EsperEPL2GrammarParser.PropertySelectionListElementContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitPropertySelectionListElement([NotNull] EsperEPL2GrammarParser.PropertySelectionListElementContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterPropertyStreamSelector([NotNull] EsperEPL2GrammarParser.PropertyStreamSelectorContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitPropertyStreamSelector([NotNull] EsperEPL2GrammarParser.PropertyStreamSelectorContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterTypeExpressionAnnotation([NotNull] EsperEPL2GrammarParser.TypeExpressionAnnotationContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitTypeExpressionAnnotation([NotNull] EsperEPL2GrammarParser.TypeExpressionAnnotationContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterPatternFilterExpression([NotNull] EsperEPL2GrammarParser.PatternFilterExpressionContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitPatternFilterExpression([NotNull] EsperEPL2GrammarParser.PatternFilterExpressionContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterPatternFilterAnnotation([NotNull] EsperEPL2GrammarParser.PatternFilterAnnotationContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitPatternFilterAnnotation([NotNull] EsperEPL2GrammarParser.PatternFilterAnnotationContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterClassIdentifier([NotNull] EsperEPL2GrammarParser.ClassIdentifierContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitClassIdentifier([NotNull] EsperEPL2GrammarParser.ClassIdentifierContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterSlashIdentifier([NotNull] EsperEPL2GrammarParser.SlashIdentifierContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitSlashIdentifier([NotNull] EsperEPL2GrammarParser.SlashIdentifierContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterExpressionListWithNamed([NotNull] EsperEPL2GrammarParser.ExpressionListWithNamedContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitExpressionListWithNamed([NotNull] EsperEPL2GrammarParser.ExpressionListWithNamedContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterExpressionListWithNamedWithTime([NotNull] EsperEPL2GrammarParser.ExpressionListWithNamedWithTimeContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitExpressionListWithNamedWithTime([NotNull] EsperEPL2GrammarParser.ExpressionListWithNamedWithTimeContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterExpressionWithNamed([NotNull] EsperEPL2GrammarParser.ExpressionWithNamedContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitExpressionWithNamed([NotNull] EsperEPL2GrammarParser.ExpressionWithNamedContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterExpressionWithNamedWithTime([NotNull] EsperEPL2GrammarParser.ExpressionWithNamedWithTimeContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitExpressionWithNamedWithTime([NotNull] EsperEPL2GrammarParser.ExpressionWithNamedWithTimeContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterExpressionNamedParameter([NotNull] EsperEPL2GrammarParser.ExpressionNamedParameterContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitExpressionNamedParameter([NotNull] EsperEPL2GrammarParser.ExpressionNamedParameterContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterExpressionNamedParameterWithTime([NotNull] EsperEPL2GrammarParser.ExpressionNamedParameterWithTimeContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitExpressionNamedParameterWithTime([NotNull] EsperEPL2GrammarParser.ExpressionNamedParameterWithTimeContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterExpressionList([NotNull] EsperEPL2GrammarParser.ExpressionListContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitExpressionList([NotNull] EsperEPL2GrammarParser.ExpressionListContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterExpressionWithTimeList([NotNull] EsperEPL2GrammarParser.ExpressionWithTimeListContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitExpressionWithTimeList([NotNull] EsperEPL2GrammarParser.ExpressionWithTimeListContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterExpressionWithTime([NotNull] EsperEPL2GrammarParser.ExpressionWithTimeContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitExpressionWithTime([NotNull] EsperEPL2GrammarParser.ExpressionWithTimeContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterExpressionWithTimeInclLast([NotNull] EsperEPL2GrammarParser.ExpressionWithTimeInclLastContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitExpressionWithTimeInclLast([NotNull] EsperEPL2GrammarParser.ExpressionWithTimeInclLastContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterExpressionQualifyable([NotNull] EsperEPL2GrammarParser.ExpressionQualifyableContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitExpressionQualifyable([NotNull] EsperEPL2GrammarParser.ExpressionQualifyableContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterLastWeekdayOperand([NotNull] EsperEPL2GrammarParser.LastWeekdayOperandContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitLastWeekdayOperand([NotNull] EsperEPL2GrammarParser.LastWeekdayOperandContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterLastOperand([NotNull] EsperEPL2GrammarParser.LastOperandContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitLastOperand([NotNull] EsperEPL2GrammarParser.LastOperandContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterFrequencyOperand([NotNull] EsperEPL2GrammarParser.FrequencyOperandContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitFrequencyOperand([NotNull] EsperEPL2GrammarParser.FrequencyOperandContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterRangeOperand([NotNull] EsperEPL2GrammarParser.RangeOperandContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitRangeOperand([NotNull] EsperEPL2GrammarParser.RangeOperandContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterLastOperator([NotNull] EsperEPL2GrammarParser.LastOperatorContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitLastOperator([NotNull] EsperEPL2GrammarParser.LastOperatorContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterWeekDayOperator([NotNull] EsperEPL2GrammarParser.WeekDayOperatorContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitWeekDayOperator([NotNull] EsperEPL2GrammarParser.WeekDayOperatorContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterNumericParameterList([NotNull] EsperEPL2GrammarParser.NumericParameterListContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitNumericParameterList([NotNull] EsperEPL2GrammarParser.NumericParameterListContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterNumericListParameter([NotNull] EsperEPL2GrammarParser.NumericListParameterContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitNumericListParameter([NotNull] EsperEPL2GrammarParser.NumericListParameterContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterEventProperty([NotNull] EsperEPL2GrammarParser.EventPropertyContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitEventProperty([NotNull] EsperEPL2GrammarParser.EventPropertyContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterEventPropertyAtomic([NotNull] EsperEPL2GrammarParser.EventPropertyAtomicContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitEventPropertyAtomic([NotNull] EsperEPL2GrammarParser.EventPropertyAtomicContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterEventPropertyIdent([NotNull] EsperEPL2GrammarParser.EventPropertyIdentContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitEventPropertyIdent([NotNull] EsperEPL2GrammarParser.EventPropertyIdentContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterKeywordAllowedIdent([NotNull] EsperEPL2GrammarParser.KeywordAllowedIdentContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitKeywordAllowedIdent([NotNull] EsperEPL2GrammarParser.KeywordAllowedIdentContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterEscapableStr([NotNull] EsperEPL2GrammarParser.EscapableStrContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitEscapableStr([NotNull] EsperEPL2GrammarParser.EscapableStrContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterEscapableIdent([NotNull] EsperEPL2GrammarParser.EscapableIdentContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitEscapableIdent([NotNull] EsperEPL2GrammarParser.EscapableIdentContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterTimePeriod([NotNull] EsperEPL2GrammarParser.TimePeriodContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitTimePeriod([NotNull] EsperEPL2GrammarParser.TimePeriodContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterYearPart([NotNull] EsperEPL2GrammarParser.YearPartContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitYearPart([NotNull] EsperEPL2GrammarParser.YearPartContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterMonthPart([NotNull] EsperEPL2GrammarParser.MonthPartContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitMonthPart([NotNull] EsperEPL2GrammarParser.MonthPartContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterWeekPart([NotNull] EsperEPL2GrammarParser.WeekPartContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitWeekPart([NotNull] EsperEPL2GrammarParser.WeekPartContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterDayPart([NotNull] EsperEPL2GrammarParser.DayPartContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitDayPart([NotNull] EsperEPL2GrammarParser.DayPartContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterHourPart([NotNull] EsperEPL2GrammarParser.HourPartContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitHourPart([NotNull] EsperEPL2GrammarParser.HourPartContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterMinutePart([NotNull] EsperEPL2GrammarParser.MinutePartContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitMinutePart([NotNull] EsperEPL2GrammarParser.MinutePartContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterSecondPart([NotNull] EsperEPL2GrammarParser.SecondPartContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitSecondPart([NotNull] EsperEPL2GrammarParser.SecondPartContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterMillisecondPart([NotNull] EsperEPL2GrammarParser.MillisecondPartContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitMillisecondPart([NotNull] EsperEPL2GrammarParser.MillisecondPartContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterMicrosecondPart([NotNull] EsperEPL2GrammarParser.MicrosecondPartContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitMicrosecondPart([NotNull] EsperEPL2GrammarParser.MicrosecondPartContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterNumber([NotNull] EsperEPL2GrammarParser.NumberContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitNumber([NotNull] EsperEPL2GrammarParser.NumberContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterSubstitution([NotNull] EsperEPL2GrammarParser.SubstitutionContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitSubstitution([NotNull] EsperEPL2GrammarParser.SubstitutionContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterConstant([NotNull] EsperEPL2GrammarParser.ConstantContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitConstant([NotNull] EsperEPL2GrammarParser.ConstantContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterNumberconstant([NotNull] EsperEPL2GrammarParser.NumberconstantContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitNumberconstant([NotNull] EsperEPL2GrammarParser.NumberconstantContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterStringconstant([NotNull] EsperEPL2GrammarParser.StringconstantContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitStringconstant([NotNull] EsperEPL2GrammarParser.StringconstantContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterJsonvalue([NotNull] EsperEPL2GrammarParser.JsonvalueContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitJsonvalue([NotNull] EsperEPL2GrammarParser.JsonvalueContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterJsonobject([NotNull] EsperEPL2GrammarParser.JsonobjectContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitJsonobject([NotNull] EsperEPL2GrammarParser.JsonobjectContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterJsonarray([NotNull] EsperEPL2GrammarParser.JsonarrayContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitJsonarray([NotNull] EsperEPL2GrammarParser.JsonarrayContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterJsonelements([NotNull] EsperEPL2GrammarParser.JsonelementsContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitJsonelements([NotNull] EsperEPL2GrammarParser.JsonelementsContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterJsonmembers([NotNull] EsperEPL2GrammarParser.JsonmembersContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitJsonmembers([NotNull] EsperEPL2GrammarParser.JsonmembersContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterJsonpair([NotNull] EsperEPL2GrammarParser.JsonpairContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitJsonpair([NotNull] EsperEPL2GrammarParser.JsonpairContext context); +} +} // namespace com.espertech.esper.epl.generated diff --git a/NEsper.Core/NEsper.Core/epl/generated/EsperEPL2GrammarParser.cs b/NEsper.Core/NEsper.Core/epl/generated/EsperEPL2GrammarParser.cs new file mode 100755 index 000000000..1b04abcb6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/generated/EsperEPL2GrammarParser.cs @@ -0,0 +1,24590 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// ANTLR Version: 4.6 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +// Generated from C:\Src\Espertech\NEsper-master\NEsper\grammar\EsperEPL2Grammar.g4 by ANTLR 4.6 + +// Unreachable code detected +#pragma warning disable 0162 +// The variable '...' is assigned but its value is never used +#pragma warning disable 0219 +// Missing XML comment for publicly visible type or member '...' +#pragma warning disable 1591 +// Ambiguous reference in cref attribute +#pragma warning disable 419 + +namespace com.espertech.esper.epl.generated { + + using System; + using System.Collections.Generic; + +using System; +using System.Text; +using System.Diagnostics; +using System.Collections.Generic; +using Antlr4.Runtime; +using Antlr4.Runtime.Atn; +using Antlr4.Runtime.Misc; +using Antlr4.Runtime.Tree; +using DFA = Antlr4.Runtime.Dfa.DFA; + +[System.CodeDom.Compiler.GeneratedCode("ANTLR", "4.6")] +[System.CLSCompliant(false)] +public partial class EsperEPL2GrammarParser : Parser { + protected static DFA[] decisionToDFA; + protected static PredictionContextCache sharedContextCache = new PredictionContextCache(); + public const int + CREATE=1, WINDOW=2, IN_SET=3, BETWEEN=4, LIKE=5, REGEXP=6, ESCAPE=7, OR_EXPR=8, + AND_EXPR=9, NOT_EXPR=10, EVERY_EXPR=11, EVERY_DISTINCT_EXPR=12, WHERE=13, + AS=14, SUM=15, AVG=16, MAX=17, MIN=18, COALESCE=19, MEDIAN=20, STDDEV=21, + AVEDEV=22, COUNT=23, SELECT=24, CASE=25, ELSE=26, WHEN=27, THEN=28, END=29, + FROM=30, OUTER=31, INNER=32, JOIN=33, LEFT=34, RIGHT=35, FULL=36, ON=37, + IS=38, BY=39, GROUP=40, HAVING=41, DISTINCT=42, ALL=43, ANY=44, SOME=45, + OUTPUT=46, EVENTS=47, FIRST=48, LAST=49, INSERT=50, INTO=51, VALUES=52, + ORDER=53, ASC=54, DESC=55, RSTREAM=56, ISTREAM=57, IRSTREAM=58, SCHEMA=59, + UNIDIRECTIONAL=60, RETAINUNION=61, RETAININTERSECTION=62, PATTERN=63, + SQL=64, METADATASQL=65, PREVIOUS=66, PREVIOUSTAIL=67, PREVIOUSCOUNT=68, + PREVIOUSWINDOW=69, PRIOR=70, EXISTS=71, WEEKDAY=72, LW=73, INSTANCEOF=74, + TYPEOF=75, CAST=76, CURRENT_TIMESTAMP=77, DELETE=78, SNAPSHOT=79, SET=80, + VARIABLE=81, TABLE=82, UNTIL=83, AT=84, INDEX=85, TIMEPERIOD_YEAR=86, + TIMEPERIOD_YEARS=87, TIMEPERIOD_MONTH=88, TIMEPERIOD_MONTHS=89, TIMEPERIOD_WEEK=90, + TIMEPERIOD_WEEKS=91, TIMEPERIOD_DAY=92, TIMEPERIOD_DAYS=93, TIMEPERIOD_HOUR=94, + TIMEPERIOD_HOURS=95, TIMEPERIOD_MINUTE=96, TIMEPERIOD_MINUTES=97, TIMEPERIOD_SEC=98, + TIMEPERIOD_SECOND=99, TIMEPERIOD_SECONDS=100, TIMEPERIOD_MILLISEC=101, + TIMEPERIOD_MILLISECOND=102, TIMEPERIOD_MILLISECONDS=103, TIMEPERIOD_MICROSEC=104, + TIMEPERIOD_MICROSECOND=105, TIMEPERIOD_MICROSECONDS=106, BOOLEAN_TRUE=107, + BOOLEAN_FALSE=108, VALUE_NULL=109, ROW_LIMIT_EXPR=110, OFFSET=111, UPDATE=112, + MATCH_RECOGNIZE=113, MATCH_RECOGNIZE_PERMUTE=114, MEASURES=115, DEFINE=116, + PARTITION=117, MATCHES=118, AFTER=119, FOR=120, WHILE=121, USING=122, + MERGE=123, MATCHED=124, EXPRESSIONDECL=125, NEWKW=126, START=127, CONTEXT=128, + INITIATED=129, TERMINATED=130, DATAFLOW=131, CUBE=132, ROLLUP=133, GROUPING=134, + GROUPING_ID=135, SETS=136, FOLLOWMAX_BEGIN=137, FOLLOWMAX_END=138, FOLLOWED_BY=139, + GOES=140, EQUALS=141, SQL_NE=142, QUESTION=143, LPAREN=144, RPAREN=145, + LBRACK=146, RBRACK=147, LCURLY=148, RCURLY=149, COLON=150, COMMA=151, + EQUAL=152, LNOT=153, BNOT=154, NOT_EQUAL=155, DIV=156, DIV_ASSIGN=157, + PLUS=158, PLUS_ASSIGN=159, INC=160, MINUS=161, MINUS_ASSIGN=162, DEC=163, + STAR=164, STAR_ASSIGN=165, MOD=166, MOD_ASSIGN=167, GE=168, GT=169, LE=170, + LT=171, BXOR=172, BXOR_ASSIGN=173, BOR=174, BOR_ASSIGN=175, LOR=176, BAND=177, + BAND_ASSIGN=178, LAND=179, SEMI=180, DOT=181, NUM_LONG=182, NUM_DOUBLE=183, + NUM_FLOAT=184, ESCAPECHAR=185, ESCAPEBACKTICK=186, ATCHAR=187, HASHCHAR=188, + WS=189, SL_COMMENT=190, ML_COMMENT=191, TICKED_STRING_LITERAL=192, QUOTED_STRING_LITERAL=193, + STRING_LITERAL=194, IDENT=195, IntegerLiteral=196, FloatingPointLiteral=197; + public const int + RULE_startPatternExpressionRule = 0, RULE_startEPLExpressionRule = 1, + RULE_startEventPropertyRule = 2, RULE_startJsonValueRule = 3, RULE_expressionDecl = 4, + RULE_expressionDialect = 5, RULE_expressionDef = 6, RULE_expressionLambdaDecl = 7, + RULE_expressionTypeAnno = 8, RULE_annotationEnum = 9, RULE_elementValuePairsEnum = 10, + RULE_elementValuePairEnum = 11, RULE_elementValueEnum = 12, RULE_elementValueArrayEnum = 13, + RULE_eplExpression = 14, RULE_contextExpr = 15, RULE_selectExpr = 16, + RULE_onExpr = 17, RULE_onStreamExpr = 18, RULE_updateExpr = 19, RULE_updateDetails = 20, + RULE_onMergeExpr = 21, RULE_mergeItem = 22, RULE_mergeMatched = 23, RULE_mergeMatchedItem = 24, + RULE_mergeUnmatched = 25, RULE_mergeUnmatchedItem = 26, RULE_mergeInsert = 27, + RULE_onSelectExpr = 28, RULE_onUpdateExpr = 29, RULE_onSelectInsertExpr = 30, + RULE_onSelectInsertFromClause = 31, RULE_outputClauseInsert = 32, RULE_onDeleteExpr = 33, + RULE_onSetExpr = 34, RULE_onSetAssignmentList = 35, RULE_onSetAssignment = 36, + RULE_onExprFrom = 37, RULE_createWindowExpr = 38, RULE_createWindowExprModelAfter = 39, + RULE_createIndexExpr = 40, RULE_createIndexColumnList = 41, RULE_createIndexColumn = 42, + RULE_createVariableExpr = 43, RULE_createTableExpr = 44, RULE_createTableColumnList = 45, + RULE_createTableColumn = 46, RULE_createTableColumnPlain = 47, RULE_createColumnList = 48, + RULE_createColumnListElement = 49, RULE_createSelectionList = 50, RULE_createSelectionListElement = 51, + RULE_createSchemaExpr = 52, RULE_createSchemaDef = 53, RULE_fafDelete = 54, + RULE_fafUpdate = 55, RULE_fafInsert = 56, RULE_createDataflow = 57, RULE_gopList = 58, + RULE_gop = 59, RULE_gopParams = 60, RULE_gopParamsItemList = 61, RULE_gopParamsItem = 62, + RULE_gopParamsItemMany = 63, RULE_gopParamsItemAs = 64, RULE_gopOut = 65, + RULE_gopOutItem = 66, RULE_gopOutTypeList = 67, RULE_gopOutTypeParam = 68, + RULE_gopOutTypeItem = 69, RULE_gopDetail = 70, RULE_gopConfig = 71, RULE_createContextExpr = 72, + RULE_createExpressionExpr = 73, RULE_createContextDetail = 74, RULE_contextContextNested = 75, + RULE_createContextChoice = 76, RULE_createContextDistinct = 77, RULE_createContextRangePoint = 78, + RULE_createContextFilter = 79, RULE_createContextPartitionItem = 80, RULE_createContextCoalesceItem = 81, + RULE_createContextGroupItem = 82, RULE_createSchemaQual = 83, RULE_variantList = 84, + RULE_variantListElement = 85, RULE_intoTableExpr = 86, RULE_insertIntoExpr = 87, + RULE_columnList = 88, RULE_fromClause = 89, RULE_regularJoin = 90, RULE_outerJoinList = 91, + RULE_outerJoin = 92, RULE_outerJoinIdent = 93, RULE_outerJoinIdentPair = 94, + RULE_whereClause = 95, RULE_selectClause = 96, RULE_selectionList = 97, + RULE_selectionListElement = 98, RULE_selectionListElementExpr = 99, RULE_selectionListElementAnno = 100, + RULE_streamSelector = 101, RULE_streamExpression = 102, RULE_forExpr = 103, + RULE_patternInclusionExpression = 104, RULE_databaseJoinExpression = 105, + RULE_methodJoinExpression = 106, RULE_viewExpressions = 107, RULE_viewExpressionWNamespace = 108, + RULE_viewExpressionOptNamespace = 109, RULE_viewWParameters = 110, RULE_groupByListExpr = 111, + RULE_groupByListChoice = 112, RULE_groupByCubeOrRollup = 113, RULE_groupByGroupingSets = 114, + RULE_groupBySetsChoice = 115, RULE_groupByCombinableExpr = 116, RULE_orderByListExpr = 117, + RULE_orderByListElement = 118, RULE_havingClause = 119, RULE_outputLimit = 120, + RULE_outputLimitAndTerm = 121, RULE_outputLimitAfter = 122, RULE_rowLimit = 123, + RULE_crontabLimitParameterSet = 124, RULE_whenClause = 125, RULE_elseClause = 126, + RULE_matchRecog = 127, RULE_matchRecogPartitionBy = 128, RULE_matchRecogMeasures = 129, + RULE_matchRecogMeasureItem = 130, RULE_matchRecogMatchesSelection = 131, + RULE_matchRecogPattern = 132, RULE_matchRecogMatchesAfterSkip = 133, RULE_matchRecogMatchesInterval = 134, + RULE_matchRecogPatternAlteration = 135, RULE_matchRecogPatternConcat = 136, + RULE_matchRecogPatternUnary = 137, RULE_matchRecogPatternNested = 138, + RULE_matchRecogPatternPermute = 139, RULE_matchRecogPatternAtom = 140, + RULE_matchRecogPatternRepeat = 141, RULE_matchRecogDefine = 142, RULE_matchRecogDefineItem = 143, + RULE_expression = 144, RULE_caseExpression = 145, RULE_evalOrExpression = 146, + RULE_evalAndExpression = 147, RULE_bitWiseExpression = 148, RULE_negatedExpression = 149, + RULE_evalEqualsExpression = 150, RULE_evalRelationalExpression = 151, + RULE_inSubSelectQuery = 152, RULE_concatenationExpr = 153, RULE_additiveExpression = 154, + RULE_multiplyExpression = 155, RULE_unaryExpression = 156, RULE_substitutionCanChain = 157, + RULE_chainedFunction = 158, RULE_newAssign = 159, RULE_rowSubSelectExpression = 160, + RULE_subSelectGroupExpression = 161, RULE_existsSubSelectExpression = 162, + RULE_subQueryExpr = 163, RULE_subSelectFilterExpr = 164, RULE_arrayExpression = 165, + RULE_builtinFunc = 166, RULE_firstLastWindowAggregation = 167, RULE_eventPropertyOrLibFunction = 168, + RULE_libFunction = 169, RULE_libFunctionWithClass = 170, RULE_libFunctionNoClass = 171, + RULE_funcIdentTop = 172, RULE_funcIdentInner = 173, RULE_funcIdentChained = 174, + RULE_libFunctionArgs = 175, RULE_libFunctionArgItem = 176, RULE_betweenList = 177, + RULE_patternExpression = 178, RULE_followedByExpression = 179, RULE_followedByRepeat = 180, + RULE_orExpression = 181, RULE_andExpression = 182, RULE_matchUntilExpression = 183, + RULE_qualifyExpression = 184, RULE_guardPostFix = 185, RULE_distinctExpressionList = 186, + RULE_distinctExpressionAtom = 187, RULE_atomicExpression = 188, RULE_observerExpression = 189, + RULE_guardWhereExpression = 190, RULE_guardWhileExpression = 191, RULE_matchUntilRange = 192, + RULE_eventFilterExpression = 193, RULE_propertyExpression = 194, RULE_propertyExpressionAtomic = 195, + RULE_propertyExpressionSelect = 196, RULE_propertySelectionList = 197, + RULE_propertySelectionListElement = 198, RULE_propertyStreamSelector = 199, + RULE_typeExpressionAnnotation = 200, RULE_patternFilterExpression = 201, + RULE_patternFilterAnnotation = 202, RULE_classIdentifier = 203, RULE_slashIdentifier = 204, + RULE_expressionListWithNamed = 205, RULE_expressionListWithNamedWithTime = 206, + RULE_expressionWithNamed = 207, RULE_expressionWithNamedWithTime = 208, + RULE_expressionNamedParameter = 209, RULE_expressionNamedParameterWithTime = 210, + RULE_expressionList = 211, RULE_expressionWithTimeList = 212, RULE_expressionWithTime = 213, + RULE_expressionWithTimeInclLast = 214, RULE_expressionQualifyable = 215, + RULE_lastWeekdayOperand = 216, RULE_lastOperand = 217, RULE_frequencyOperand = 218, + RULE_rangeOperand = 219, RULE_lastOperator = 220, RULE_weekDayOperator = 221, + RULE_numericParameterList = 222, RULE_numericListParameter = 223, RULE_eventProperty = 224, + RULE_eventPropertyAtomic = 225, RULE_eventPropertyIdent = 226, RULE_keywordAllowedIdent = 227, + RULE_escapableStr = 228, RULE_escapableIdent = 229, RULE_timePeriod = 230, + RULE_yearPart = 231, RULE_monthPart = 232, RULE_weekPart = 233, RULE_dayPart = 234, + RULE_hourPart = 235, RULE_minutePart = 236, RULE_secondPart = 237, RULE_millisecondPart = 238, + RULE_microsecondPart = 239, RULE_number = 240, RULE_substitution = 241, + RULE_constant = 242, RULE_numberconstant = 243, RULE_stringconstant = 244, + RULE_jsonvalue = 245, RULE_jsonobject = 246, RULE_jsonarray = 247, RULE_jsonelements = 248, + RULE_jsonmembers = 249, RULE_jsonpair = 250; + public static readonly string[] ruleNames = { + "startPatternExpressionRule", "startEPLExpressionRule", "startEventPropertyRule", + "startJsonValueRule", "expressionDecl", "expressionDialect", "expressionDef", + "expressionLambdaDecl", "expressionTypeAnno", "annotationEnum", "elementValuePairsEnum", + "elementValuePairEnum", "elementValueEnum", "elementValueArrayEnum", "eplExpression", + "contextExpr", "selectExpr", "onExpr", "onStreamExpr", "updateExpr", "updateDetails", + "onMergeExpr", "mergeItem", "mergeMatched", "mergeMatchedItem", "mergeUnmatched", + "mergeUnmatchedItem", "mergeInsert", "onSelectExpr", "onUpdateExpr", "onSelectInsertExpr", + "onSelectInsertFromClause", "outputClauseInsert", "onDeleteExpr", "onSetExpr", + "onSetAssignmentList", "onSetAssignment", "onExprFrom", "createWindowExpr", + "createWindowExprModelAfter", "createIndexExpr", "createIndexColumnList", + "createIndexColumn", "createVariableExpr", "createTableExpr", "createTableColumnList", + "createTableColumn", "createTableColumnPlain", "createColumnList", "createColumnListElement", + "createSelectionList", "createSelectionListElement", "createSchemaExpr", + "createSchemaDef", "fafDelete", "fafUpdate", "fafInsert", "createDataflow", + "gopList", "gop", "gopParams", "gopParamsItemList", "gopParamsItem", "gopParamsItemMany", + "gopParamsItemAs", "gopOut", "gopOutItem", "gopOutTypeList", "gopOutTypeParam", + "gopOutTypeItem", "gopDetail", "gopConfig", "createContextExpr", "createExpressionExpr", + "createContextDetail", "contextContextNested", "createContextChoice", + "createContextDistinct", "createContextRangePoint", "createContextFilter", + "createContextPartitionItem", "createContextCoalesceItem", "createContextGroupItem", + "createSchemaQual", "variantList", "variantListElement", "intoTableExpr", + "insertIntoExpr", "columnList", "fromClause", "regularJoin", "outerJoinList", + "outerJoin", "outerJoinIdent", "outerJoinIdentPair", "whereClause", "selectClause", + "selectionList", "selectionListElement", "selectionListElementExpr", "selectionListElementAnno", + "streamSelector", "streamExpression", "forExpr", "patternInclusionExpression", + "databaseJoinExpression", "methodJoinExpression", "viewExpressions", "viewExpressionWNamespace", + "viewExpressionOptNamespace", "viewWParameters", "groupByListExpr", "groupByListChoice", + "groupByCubeOrRollup", "groupByGroupingSets", "groupBySetsChoice", "groupByCombinableExpr", + "orderByListExpr", "orderByListElement", "havingClause", "outputLimit", + "outputLimitAndTerm", "outputLimitAfter", "rowLimit", "crontabLimitParameterSet", + "whenClause", "elseClause", "matchRecog", "matchRecogPartitionBy", "matchRecogMeasures", + "matchRecogMeasureItem", "matchRecogMatchesSelection", "matchRecogPattern", + "matchRecogMatchesAfterSkip", "matchRecogMatchesInterval", "matchRecogPatternAlteration", + "matchRecogPatternConcat", "matchRecogPatternUnary", "matchRecogPatternNested", + "matchRecogPatternPermute", "matchRecogPatternAtom", "matchRecogPatternRepeat", + "matchRecogDefine", "matchRecogDefineItem", "expression", "caseExpression", + "evalOrExpression", "evalAndExpression", "bitWiseExpression", "negatedExpression", + "evalEqualsExpression", "evalRelationalExpression", "inSubSelectQuery", + "concatenationExpr", "additiveExpression", "multiplyExpression", "unaryExpression", + "substitutionCanChain", "chainedFunction", "newAssign", "rowSubSelectExpression", + "subSelectGroupExpression", "existsSubSelectExpression", "subQueryExpr", + "subSelectFilterExpr", "arrayExpression", "builtinFunc", "firstLastWindowAggregation", + "eventPropertyOrLibFunction", "libFunction", "libFunctionWithClass", "libFunctionNoClass", + "funcIdentTop", "funcIdentInner", "funcIdentChained", "libFunctionArgs", + "libFunctionArgItem", "betweenList", "patternExpression", "followedByExpression", + "followedByRepeat", "orExpression", "andExpression", "matchUntilExpression", + "qualifyExpression", "guardPostFix", "distinctExpressionList", "distinctExpressionAtom", + "atomicExpression", "observerExpression", "guardWhereExpression", "guardWhileExpression", + "matchUntilRange", "eventFilterExpression", "propertyExpression", "propertyExpressionAtomic", + "propertyExpressionSelect", "propertySelectionList", "propertySelectionListElement", + "propertyStreamSelector", "typeExpressionAnnotation", "patternFilterExpression", + "patternFilterAnnotation", "classIdentifier", "slashIdentifier", "expressionListWithNamed", + "expressionListWithNamedWithTime", "expressionWithNamed", "expressionWithNamedWithTime", + "expressionNamedParameter", "expressionNamedParameterWithTime", "expressionList", + "expressionWithTimeList", "expressionWithTime", "expressionWithTimeInclLast", + "expressionQualifyable", "lastWeekdayOperand", "lastOperand", "frequencyOperand", + "rangeOperand", "lastOperator", "weekDayOperator", "numericParameterList", + "numericListParameter", "eventProperty", "eventPropertyAtomic", "eventPropertyIdent", + "keywordAllowedIdent", "escapableStr", "escapableIdent", "timePeriod", + "yearPart", "monthPart", "weekPart", "dayPart", "hourPart", "minutePart", + "secondPart", "millisecondPart", "microsecondPart", "number", "substitution", + "constant", "numberconstant", "stringconstant", "jsonvalue", "jsonobject", + "jsonarray", "jsonelements", "jsonmembers", "jsonpair" + }; + + private static readonly string[] _LiteralNames = { + null, "'create'", "'window'", "'in'", "'between'", "'like'", "'regexp'", + "'escape'", "'or'", "'and'", "'not'", "'every'", "'every-distinct'", "'where'", + "'as'", "'sum'", "'avg'", "'max'", "'min'", "'coalesce'", "'median'", + "'stddev'", "'avedev'", "'count'", "'select'", "'case'", "'else'", "'when'", + "'then'", "'end'", "'from'", "'outer'", "'inner'", "'join'", "'left'", + "'right'", "'full'", "'on'", "'is'", "'by'", "'group'", "'having'", "'distinct'", + "'all'", "'any'", "'some'", "'output'", "'events'", "'first'", "'last'", + "'insert'", "'into'", "'values'", "'order'", "'asc'", "'desc'", "'rstream'", + "'istream'", "'irstream'", "'schema'", "'unidirectional'", "'retain-union'", + "'retain-intersection'", "'pattern'", "'sql'", "'metadatasql'", "'prev'", + "'prevtail'", "'prevcount'", "'prevwindow'", "'prior'", "'exists'", "'weekday'", + "'lastweekday'", "'instanceof'", "'typeof'", "'cast'", "'current_timestamp'", + "'delete'", "'snapshot'", "'set'", "'variable'", "'table'", "'until'", + "'at'", "'index'", "'year'", "'years'", "'month'", "'months'", "'week'", + "'weeks'", "'day'", "'days'", "'hour'", "'hours'", "'minute'", "'minutes'", + "'sec'", "'second'", "'seconds'", "'msec'", "'millisecond'", "'milliseconds'", + "'usec'", "'microsecond'", "'microseconds'", "'true'", "'false'", "'null'", + "'limit'", "'offset'", "'update'", "'match_recognize'", "'match_recognize_permute'", + "'measures'", "'define'", "'partition'", "'matches'", "'after'", "'for'", + "'while'", "'using'", "'merge'", "'matched'", "'expression'", "'new'", + "'start'", "'context'", "'initiated'", "'terminated'", "'dataflow'", "'cube'", + "'rollup'", "'grouping'", "'grouping_id'", "'sets'", "'-['", "']>'", "'->'", + "'=>'", "'='", "'<>'", "'?'", "'('", "')'", "'['", "']'", "'{'", "'}'", + "':'", "','", "'=='", "'!'", "'~'", "'!='", "'/'", "'/='", "'+'", "'+='", + "'++'", "'-'", "'-='", "'--'", "'*'", "'*='", "'%'", "'%='", "'>='", "'>'", + "'<='", "'<'", "'^'", "'^='", "'|'", "'|='", "'||'", "'&'", "'&='", "'&&'", + "';'", "'.'", "'\\u18FF'", "'\\u18FE'", "'\\u18FD'", "'\\'", "'`'", "'@'", + "'#'" + }; + private static readonly string[] _SymbolicNames = { + null, "CREATE", "WINDOW", "IN_SET", "BETWEEN", "LIKE", "REGEXP", "ESCAPE", + "OR_EXPR", "AND_EXPR", "NOT_EXPR", "EVERY_EXPR", "EVERY_DISTINCT_EXPR", + "WHERE", "AS", "SUM", "AVG", "MAX", "MIN", "COALESCE", "MEDIAN", "STDDEV", + "AVEDEV", "COUNT", "SELECT", "CASE", "ELSE", "WHEN", "THEN", "END", "FROM", + "OUTER", "INNER", "JOIN", "LEFT", "RIGHT", "FULL", "ON", "IS", "BY", "GROUP", + "HAVING", "DISTINCT", "ALL", "ANY", "SOME", "OUTPUT", "EVENTS", "FIRST", + "LAST", "INSERT", "INTO", "VALUES", "ORDER", "ASC", "DESC", "RSTREAM", + "ISTREAM", "IRSTREAM", "SCHEMA", "UNIDIRECTIONAL", "RETAINUNION", "RETAININTERSECTION", + "PATTERN", "SQL", "METADATASQL", "PREVIOUS", "PREVIOUSTAIL", "PREVIOUSCOUNT", + "PREVIOUSWINDOW", "PRIOR", "EXISTS", "WEEKDAY", "LW", "INSTANCEOF", "TYPEOF", + "CAST", "CURRENT_TIMESTAMP", "DELETE", "SNAPSHOT", "SET", "VARIABLE", + "TABLE", "UNTIL", "AT", "INDEX", "TIMEPERIOD_YEAR", "TIMEPERIOD_YEARS", + "TIMEPERIOD_MONTH", "TIMEPERIOD_MONTHS", "TIMEPERIOD_WEEK", "TIMEPERIOD_WEEKS", + "TIMEPERIOD_DAY", "TIMEPERIOD_DAYS", "TIMEPERIOD_HOUR", "TIMEPERIOD_HOURS", + "TIMEPERIOD_MINUTE", "TIMEPERIOD_MINUTES", "TIMEPERIOD_SEC", "TIMEPERIOD_SECOND", + "TIMEPERIOD_SECONDS", "TIMEPERIOD_MILLISEC", "TIMEPERIOD_MILLISECOND", + "TIMEPERIOD_MILLISECONDS", "TIMEPERIOD_MICROSEC", "TIMEPERIOD_MICROSECOND", + "TIMEPERIOD_MICROSECONDS", "BOOLEAN_TRUE", "BOOLEAN_FALSE", "VALUE_NULL", + "ROW_LIMIT_EXPR", "OFFSET", "UPDATE", "MATCH_RECOGNIZE", "MATCH_RECOGNIZE_PERMUTE", + "MEASURES", "DEFINE", "PARTITION", "MATCHES", "AFTER", "FOR", "WHILE", + "USING", "MERGE", "MATCHED", "EXPRESSIONDECL", "NEWKW", "START", "CONTEXT", + "INITIATED", "TERMINATED", "DATAFLOW", "CUBE", "ROLLUP", "GROUPING", "GROUPING_ID", + "SETS", "FOLLOWMAX_BEGIN", "FOLLOWMAX_END", "FOLLOWED_BY", "GOES", "EQUALS", + "SQL_NE", "QUESTION", "LPAREN", "RPAREN", "LBRACK", "RBRACK", "LCURLY", + "RCURLY", "COLON", "COMMA", "EQUAL", "LNOT", "BNOT", "NOT_EQUAL", "DIV", + "DIV_ASSIGN", "PLUS", "PLUS_ASSIGN", "INC", "MINUS", "MINUS_ASSIGN", "DEC", + "STAR", "STAR_ASSIGN", "MOD", "MOD_ASSIGN", "GE", "GT", "LE", "LT", "BXOR", + "BXOR_ASSIGN", "BOR", "BOR_ASSIGN", "LOR", "BAND", "BAND_ASSIGN", "LAND", + "SEMI", "DOT", "NUM_LONG", "NUM_DOUBLE", "NUM_FLOAT", "ESCAPECHAR", "ESCAPEBACKTICK", + "ATCHAR", "HASHCHAR", "WS", "SL_COMMENT", "ML_COMMENT", "TICKED_STRING_LITERAL", + "QUOTED_STRING_LITERAL", "STRING_LITERAL", "IDENT", "IntegerLiteral", + "FloatingPointLiteral" + }; + public static readonly IVocabulary DefaultVocabulary = new Vocabulary(_LiteralNames, _SymbolicNames); + + [NotNull] + public override IVocabulary Vocabulary + { + get + { + return DefaultVocabulary; + } + } + + public override string GrammarFileName { get { return "EsperEPL2Grammar.g4"; } } + + public override string[] RuleNames { get { return ruleNames; } } + + public override string SerializedAtn { get { return _serializedATN; } } + + static EsperEPL2GrammarParser() { + decisionToDFA = new DFA[_ATN.NumberOfDecisions]; + for (int i = 0; i < _ATN.NumberOfDecisions; i++) { + decisionToDFA[i] = new DFA(_ATN.GetDecisionState(i), i); + } + } + + + // provide nice error messages + private System.Collections.Generic.Stack paraphrases = + new System.Collections.Generic.Stack(); + + // static information initialized once + private static System.Collections.Generic.IDictionary lexerTokenParaphrases = + new System.Collections.Generic.Dictionary(); + private static System.Collections.Generic.IDictionary parserTokenParaphrases = + new System.Collections.Generic.Dictionary(); + private static System.Collections.Generic.ISet parserKeywordSet = + new System.Collections.Generic.HashSet(); + private static System.Collections.Generic.ISet afterScriptTokens = + new System.Collections.Generic.HashSet(); + + private static readonly Object _iLock = new Object(); + + public System.Collections.Generic.Stack GetParaphrases() + { + return paraphrases; + } + + public System.Collections.Generic.ISet GetKeywords() + { + GetParserTokenParaphrases(); + return parserKeywordSet; + } + + public static System.Collections.Generic.IDictionary GetLexerTokenParaphrases() + { + lock(_iLock) + { + if (lexerTokenParaphrases.Count == 0) + { + lexerTokenParaphrases[IDENT] = "an identifier"; + lexerTokenParaphrases[FOLLOWED_BY] = "an followed-by '->'"; + lexerTokenParaphrases[EQUALS] = "an equals '='"; + lexerTokenParaphrases[SQL_NE] = "a sql-style not equals '<>'"; + lexerTokenParaphrases[QUESTION] = "a questionmark '?'"; + lexerTokenParaphrases[LPAREN] = "an opening parenthesis '('"; + lexerTokenParaphrases[RPAREN] = "a closing parenthesis ')'"; + lexerTokenParaphrases[LBRACK] = "a left angle bracket '['"; + lexerTokenParaphrases[RBRACK] = "a right angle bracket ']'"; + lexerTokenParaphrases[LCURLY] = "a left curly bracket '{'"; + lexerTokenParaphrases[RCURLY] = "a right curly bracket '}'"; + lexerTokenParaphrases[COLON] = "a colon ':'"; + lexerTokenParaphrases[COMMA] = "a comma ','"; + lexerTokenParaphrases[EQUAL] = "an equals compare '=='"; + lexerTokenParaphrases[LNOT] = "a not '!'"; + lexerTokenParaphrases[BNOT] = "a binary not '~'"; + lexerTokenParaphrases[NOT_EQUAL] = "a not equals '!='"; + lexerTokenParaphrases[DIV] = "a division operator '\'"; + lexerTokenParaphrases[DIV_ASSIGN] = "a division assign '/='"; + lexerTokenParaphrases[PLUS] = "a plus operator '+'"; + lexerTokenParaphrases[PLUS_ASSIGN] = "a plus assign '+='"; + lexerTokenParaphrases[INC] = "an increment operator '++'"; + lexerTokenParaphrases[MINUS] = "a minus '-'"; + lexerTokenParaphrases[MINUS_ASSIGN] = "a minus assign '-='"; + lexerTokenParaphrases[DEC] = "a decrement operator '--'"; + lexerTokenParaphrases[STAR] = "a star '*'"; + lexerTokenParaphrases[STAR_ASSIGN] = "a star assign '*='"; + lexerTokenParaphrases[MOD] = "a modulo"; + lexerTokenParaphrases[MOD_ASSIGN] = "a modulo assign"; + lexerTokenParaphrases[GE] = "a greater equals '>='"; + lexerTokenParaphrases[GT] = "a greater then '>'"; + lexerTokenParaphrases[LE] = "a less equals '<='"; + lexerTokenParaphrases[LT] = "a lesser then '<'"; + lexerTokenParaphrases[BXOR] = "a binary xor '^'"; + lexerTokenParaphrases[BXOR_ASSIGN] = "a binary xor assign '^='"; + lexerTokenParaphrases[BOR] = "a binary or '|'"; + lexerTokenParaphrases[BOR_ASSIGN] = "a binary or assign '|='"; + lexerTokenParaphrases[LOR] = "a logical or '||'"; + lexerTokenParaphrases[BAND] = "a binary and '&'"; + lexerTokenParaphrases[BAND_ASSIGN] = "a binary and assign '&='"; + lexerTokenParaphrases[LAND] = "a logical and '&&'"; + lexerTokenParaphrases[SEMI] = "a semicolon ';'"; + lexerTokenParaphrases[DOT] = "a dot '.'"; + } + } + + return lexerTokenParaphrases; + } + + public static System.Collections.Generic.IDictionary GetParserTokenParaphrases() + { + lock(_iLock) + { + if (parserTokenParaphrases.Count == 0) + { + parserTokenParaphrases[CREATE] = "'create'"; + parserTokenParaphrases[WINDOW] = "'window'"; + parserTokenParaphrases[IN_SET] = "'in'"; + parserTokenParaphrases[BETWEEN] = "'between'"; + parserTokenParaphrases[LIKE] = "'like'"; + parserTokenParaphrases[REGEXP] = "'regexp'"; + parserTokenParaphrases[ESCAPE] = "'escape'"; + parserTokenParaphrases[OR_EXPR] = "'or'"; + parserTokenParaphrases[AND_EXPR] = "'and'"; + parserTokenParaphrases[NOT_EXPR] = "'not'"; + parserTokenParaphrases[EVERY_EXPR] = "'every'"; + parserTokenParaphrases[EVERY_DISTINCT_EXPR] = "'every-distinct'"; + parserTokenParaphrases[WHERE] = "'where'"; + parserTokenParaphrases[AS] = "'as'"; + parserTokenParaphrases[SUM] = "'sum'"; + parserTokenParaphrases[AVG] = "'avg'"; + parserTokenParaphrases[MAX] = "'max'"; + parserTokenParaphrases[MIN] = "'min'"; + parserTokenParaphrases[COALESCE] = "'coalesce'"; + parserTokenParaphrases[MEDIAN] = "'median'"; + parserTokenParaphrases[STDDEV] = "'stddev'"; + parserTokenParaphrases[AVEDEV] = "'avedev'"; + parserTokenParaphrases[COUNT] = "'count'"; + parserTokenParaphrases[SELECT] = "'select'"; + parserTokenParaphrases[CASE] = "'case'"; + parserTokenParaphrases[ELSE] = "'else'"; + parserTokenParaphrases[WHEN] = "'when'"; + parserTokenParaphrases[THEN] = "'then'"; + parserTokenParaphrases[END] = "'end'"; + parserTokenParaphrases[FROM] = "'from'"; + parserTokenParaphrases[OUTER] = "'outer'"; + parserTokenParaphrases[INNER] = "'inner'"; + parserTokenParaphrases[JOIN] = "'join'"; + parserTokenParaphrases[LEFT] = "'left'"; + parserTokenParaphrases[RIGHT] = "'right'"; + parserTokenParaphrases[FULL] = "'full'"; + parserTokenParaphrases[ON] = "'on'"; + parserTokenParaphrases[IS] = "'is'"; + parserTokenParaphrases[BY] = "'by'"; + parserTokenParaphrases[GROUP] = "'group'"; + parserTokenParaphrases[HAVING] = "'having'"; + parserTokenParaphrases[ALL] = "'all'"; + parserTokenParaphrases[ANY] = "'any'"; + parserTokenParaphrases[SOME] = "'some'"; + parserTokenParaphrases[OUTPUT] = "'output'"; + parserTokenParaphrases[EVENTS] = "'events'"; + parserTokenParaphrases[FIRST] = "'first'"; + parserTokenParaphrases[LAST] = "'last'"; + parserTokenParaphrases[INSERT] = "'insert'"; + parserTokenParaphrases[INTO] = "'into'"; + parserTokenParaphrases[ORDER] = "'order'"; + parserTokenParaphrases[ASC] = "'asc'"; + parserTokenParaphrases[DESC] = "'desc'"; + parserTokenParaphrases[RSTREAM] = "'rstream'"; + parserTokenParaphrases[ISTREAM] = "'istream'"; + parserTokenParaphrases[IRSTREAM] = "'irstream'"; + parserTokenParaphrases[SCHEMA] = "'schema'"; + parserTokenParaphrases[UNIDIRECTIONAL] = "'unidirectional'"; + parserTokenParaphrases[RETAINUNION] = "'retain-union'"; + parserTokenParaphrases[RETAININTERSECTION] = "'retain-intersection'"; + parserTokenParaphrases[PATTERN] = "'pattern'"; + parserTokenParaphrases[SQL] = "'sql'"; + parserTokenParaphrases[METADATASQL] = "'metadatasql'"; + parserTokenParaphrases[PREVIOUS] = "'prev'"; + parserTokenParaphrases[PREVIOUSTAIL] = "'prevtail'"; + parserTokenParaphrases[PREVIOUSCOUNT] = "'prevcount'"; + parserTokenParaphrases[PREVIOUSWINDOW] = "'prevwindow'"; + parserTokenParaphrases[PRIOR] = "'prior'"; + parserTokenParaphrases[EXISTS] = "'exists'"; + parserTokenParaphrases[WEEKDAY] = "'weekday'"; + parserTokenParaphrases[LW] = "'lastweekday'"; + parserTokenParaphrases[INSTANCEOF] = "'instanceof'"; + parserTokenParaphrases[TYPEOF] = "'typeof'"; + parserTokenParaphrases[CAST] = "'cast'"; + parserTokenParaphrases[CURRENT_TIMESTAMP] = "'current_timestamp'"; + parserTokenParaphrases[DELETE] = "'delete'"; + parserTokenParaphrases[DISTINCT] = "'distinct'"; + parserTokenParaphrases[SNAPSHOT] = "'snapshot'"; + parserTokenParaphrases[SET] = "'set'"; + parserTokenParaphrases[VARIABLE] = "'variable'"; + parserTokenParaphrases[TABLE] = "'table'"; + parserTokenParaphrases[INDEX] = "'index'"; + parserTokenParaphrases[UNTIL] = "'until'"; + parserTokenParaphrases[AT] = "'at'"; + parserTokenParaphrases[TIMEPERIOD_YEAR] = "'year'"; + parserTokenParaphrases[TIMEPERIOD_YEARS] = "'years'"; + parserTokenParaphrases[TIMEPERIOD_MONTH] = "'month'"; + parserTokenParaphrases[TIMEPERIOD_MONTHS] = "'months'"; + parserTokenParaphrases[TIMEPERIOD_WEEK] = "'week'"; + parserTokenParaphrases[TIMEPERIOD_WEEKS] = "'weeks'"; + parserTokenParaphrases[TIMEPERIOD_DAY] = "'day'"; + parserTokenParaphrases[TIMEPERIOD_DAYS] = "'days'"; + parserTokenParaphrases[TIMEPERIOD_HOUR] = "'hour'"; + parserTokenParaphrases[TIMEPERIOD_HOURS] = "'hours'"; + parserTokenParaphrases[TIMEPERIOD_MINUTE] = "'minute'"; + parserTokenParaphrases[TIMEPERIOD_MINUTES] = "'minutes'"; + parserTokenParaphrases[TIMEPERIOD_SEC] = "'sec'"; + parserTokenParaphrases[TIMEPERIOD_SECOND] = "'second'"; + parserTokenParaphrases[TIMEPERIOD_SECONDS] = "'seconds'"; + parserTokenParaphrases[TIMEPERIOD_MILLISEC] = "'msec'"; + parserTokenParaphrases[TIMEPERIOD_MILLISECOND] = "'millisecond'"; + parserTokenParaphrases[TIMEPERIOD_MILLISECONDS] = "'milliseconds'"; + parserTokenParaphrases[TIMEPERIOD_MICROSEC] = "'usec'"; + parserTokenParaphrases[TIMEPERIOD_MICROSECOND] = "'microsecond'"; + parserTokenParaphrases[TIMEPERIOD_MICROSECONDS] = "'microseconds'"; + parserTokenParaphrases[BOOLEAN_TRUE] = "'true'"; + parserTokenParaphrases[BOOLEAN_FALSE] = "'false'"; + parserTokenParaphrases[VALUE_NULL] = "'null'"; + parserTokenParaphrases[ROW_LIMIT_EXPR] = "'limit'"; + parserTokenParaphrases[OFFSET] = "'offset'"; + parserTokenParaphrases[UPDATE] = "'update'"; + parserTokenParaphrases[MATCH_RECOGNIZE] = "'match_recognize'"; + parserTokenParaphrases[MEASURES] = "'measures'"; + parserTokenParaphrases[DEFINE] = "'define'"; + parserTokenParaphrases[PARTITION] = "'partition'"; + parserTokenParaphrases[MATCHES] = "'matches'"; + parserTokenParaphrases[AFTER] = "'after'"; + parserTokenParaphrases[FOR] = "'for'"; + parserTokenParaphrases[WHILE] = "'while'"; + parserTokenParaphrases[MERGE] = "'merge'"; + parserTokenParaphrases[MATCHED] = "'matched'"; + parserTokenParaphrases[CONTEXT] = "'context'"; + parserTokenParaphrases[START] = "'start'"; + parserTokenParaphrases[END] = "'end'"; + parserTokenParaphrases[INITIATED] = "'initiated'"; + parserTokenParaphrases[TERMINATED] = "'terminated'"; + parserTokenParaphrases[USING] = "'using'"; + parserTokenParaphrases[EXPRESSIONDECL] = "'expression'"; + parserTokenParaphrases[NEWKW] = "'new'"; + parserTokenParaphrases[DATAFLOW] = "'dataflow'"; + parserTokenParaphrases[VALUES] = "'values'"; + parserTokenParaphrases[CUBE] = "'cube'"; + parserTokenParaphrases[ROLLUP] = "'rollup'"; + parserTokenParaphrases[GROUPING] = "'grouping'"; + parserTokenParaphrases[GROUPING_ID] = "'grouping_id'"; + parserTokenParaphrases[SETS] = "'sets'"; + + parserKeywordSet = new HashSet( + parserTokenParaphrases.Values); + } + } + + return parserTokenParaphrases; + } + + public static System.Collections.Generic.ISet GetAfterScriptTokens() + { + if (afterScriptTokens.Count == 0) + { + afterScriptTokens.Add(CREATE); + afterScriptTokens.Add(EXPRESSIONDECL); + afterScriptTokens.Add(SELECT); + afterScriptTokens.Add(INSERT); + afterScriptTokens.Add(ON); + afterScriptTokens.Add(DELETE); + afterScriptTokens.Add(UPDATE); + afterScriptTokens.Add(ATCHAR); + } + + return afterScriptTokens; + } + + public EsperEPL2GrammarParser(ITokenStream input) + : base(input) + { + Interpreter = new ParserATNSimulator(this, _ATN, decisionToDFA, sharedContextCache); + } + public partial class StartPatternExpressionRuleContext : ParserRuleContext { + public PatternExpressionContext patternExpression() { + return GetRuleContext(0); + } + public ITerminalNode Eof() { return GetToken(EsperEPL2GrammarParser.Eof, 0); } + public AnnotationEnumContext[] annotationEnum() { + return GetRuleContexts(); + } + public AnnotationEnumContext annotationEnum(int i) { + return GetRuleContext(i); + } + public ExpressionDeclContext[] expressionDecl() { + return GetRuleContexts(); + } + public ExpressionDeclContext expressionDecl(int i) { + return GetRuleContext(i); + } + public StartPatternExpressionRuleContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_startPatternExpressionRule; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterStartPatternExpressionRule(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitStartPatternExpressionRule(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitStartPatternExpressionRule(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public StartPatternExpressionRuleContext startPatternExpressionRule() { + StartPatternExpressionRuleContext _localctx = new StartPatternExpressionRuleContext(Context, State); + EnterRule(_localctx, 0, RULE_startPatternExpressionRule); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 506; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==EXPRESSIONDECL || _la==ATCHAR) { + { + State = 504; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case ATCHAR: + { + State = 502; annotationEnum(); + } + break; + case EXPRESSIONDECL: + { + State = 503; expressionDecl(); + } + break; + default: + throw new NoViableAltException(this); + } + } + State = 508; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + State = 509; patternExpression(); + State = 510; Match(Eof); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class StartEPLExpressionRuleContext : ParserRuleContext { + public EplExpressionContext eplExpression() { + return GetRuleContext(0); + } + public ITerminalNode Eof() { return GetToken(EsperEPL2GrammarParser.Eof, 0); } + public AnnotationEnumContext[] annotationEnum() { + return GetRuleContexts(); + } + public AnnotationEnumContext annotationEnum(int i) { + return GetRuleContext(i); + } + public ExpressionDeclContext[] expressionDecl() { + return GetRuleContexts(); + } + public ExpressionDeclContext expressionDecl(int i) { + return GetRuleContext(i); + } + public StartEPLExpressionRuleContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_startEPLExpressionRule; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterStartEPLExpressionRule(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitStartEPLExpressionRule(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitStartEPLExpressionRule(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public StartEPLExpressionRuleContext startEPLExpressionRule() { + StartEPLExpressionRuleContext _localctx = new StartEPLExpressionRuleContext(Context, State); + EnterRule(_localctx, 2, RULE_startEPLExpressionRule); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 516; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==EXPRESSIONDECL || _la==ATCHAR) { + { + State = 514; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case ATCHAR: + { + State = 512; annotationEnum(); + } + break; + case EXPRESSIONDECL: + { + State = 513; expressionDecl(); + } + break; + default: + throw new NoViableAltException(this); + } + } + State = 518; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + State = 519; eplExpression(); + State = 520; Match(Eof); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class StartEventPropertyRuleContext : ParserRuleContext { + public EventPropertyContext eventProperty() { + return GetRuleContext(0); + } + public ITerminalNode Eof() { return GetToken(EsperEPL2GrammarParser.Eof, 0); } + public StartEventPropertyRuleContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_startEventPropertyRule; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterStartEventPropertyRule(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitStartEventPropertyRule(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitStartEventPropertyRule(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public StartEventPropertyRuleContext startEventPropertyRule() { + StartEventPropertyRuleContext _localctx = new StartEventPropertyRuleContext(Context, State); + EnterRule(_localctx, 4, RULE_startEventPropertyRule); + try { + EnterOuterAlt(_localctx, 1); + { + State = 522; eventProperty(); + State = 523; Match(Eof); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class StartJsonValueRuleContext : ParserRuleContext { + public JsonvalueContext jsonvalue() { + return GetRuleContext(0); + } + public ITerminalNode Eof() { return GetToken(EsperEPL2GrammarParser.Eof, 0); } + public StartJsonValueRuleContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_startJsonValueRule; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterStartJsonValueRule(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitStartJsonValueRule(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitStartJsonValueRule(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public StartJsonValueRuleContext startJsonValueRule() { + StartJsonValueRuleContext _localctx = new StartJsonValueRuleContext(Context, State); + EnterRule(_localctx, 6, RULE_startJsonValueRule); + try { + EnterOuterAlt(_localctx, 1); + { + State = 525; jsonvalue(); + State = 526; Match(Eof); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class ExpressionDeclContext : ParserRuleContext { + public IToken array; + public IToken name; + public IToken alias; + public ITerminalNode EXPRESSIONDECL() { return GetToken(EsperEPL2GrammarParser.EXPRESSIONDECL, 0); } + public ExpressionDefContext expressionDef() { + return GetRuleContext(0); + } + public ITerminalNode[] IDENT() { return GetTokens(EsperEPL2GrammarParser.IDENT); } + public ITerminalNode IDENT(int i) { + return GetToken(EsperEPL2GrammarParser.IDENT, i); + } + public ClassIdentifierContext classIdentifier() { + return GetRuleContext(0); + } + public ITerminalNode RBRACK() { return GetToken(EsperEPL2GrammarParser.RBRACK, 0); } + public TypeExpressionAnnotationContext typeExpressionAnnotation() { + return GetRuleContext(0); + } + public ExpressionDialectContext expressionDialect() { + return GetRuleContext(0); + } + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public ITerminalNode FOR() { return GetToken(EsperEPL2GrammarParser.FOR, 0); } + public ITerminalNode LBRACK() { return GetToken(EsperEPL2GrammarParser.LBRACK, 0); } + public ColumnListContext columnList() { + return GetRuleContext(0); + } + public ExpressionDeclContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_expressionDecl; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterExpressionDecl(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitExpressionDecl(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitExpressionDecl(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public ExpressionDeclContext expressionDecl() { + ExpressionDeclContext _localctx = new ExpressionDeclContext(Context, State); + EnterRule(_localctx, 8, RULE_expressionDecl); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 528; Match(EXPRESSIONDECL); + State = 530; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,4,Context) ) { + case 1: + { + State = 529; classIdentifier(); + } + break; + } + State = 534; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==LBRACK) { + { + State = 532; _localctx.array = Match(LBRACK); + State = 533; Match(RBRACK); + } + } + + State = 537; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==ATCHAR) { + { + State = 536; typeExpressionAnnotation(); + } + } + + State = 540; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,7,Context) ) { + case 1: + { + State = 539; expressionDialect(); + } + break; + } + State = 542; _localctx.name = Match(IDENT); + State = 548; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==LPAREN) { + { + State = 543; Match(LPAREN); + State = 545; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==IDENT) { + { + State = 544; columnList(); + } + } + + State = 547; Match(RPAREN); + } + } + + State = 552; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==IDENT) { + { + State = 550; _localctx.alias = Match(IDENT); + State = 551; Match(FOR); + } + } + + State = 554; expressionDef(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class ExpressionDialectContext : ParserRuleContext { + public IToken d; + public ITerminalNode COLON() { return GetToken(EsperEPL2GrammarParser.COLON, 0); } + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public ExpressionDialectContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_expressionDialect; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterExpressionDialect(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitExpressionDialect(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitExpressionDialect(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public ExpressionDialectContext expressionDialect() { + ExpressionDialectContext _localctx = new ExpressionDialectContext(Context, State); + EnterRule(_localctx, 10, RULE_expressionDialect); + try { + EnterOuterAlt(_localctx, 1); + { + State = 556; _localctx.d = Match(IDENT); + State = 557; Match(COLON); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class ExpressionDefContext : ParserRuleContext { + public ITerminalNode LCURLY() { return GetToken(EsperEPL2GrammarParser.LCURLY, 0); } + public ExpressionContext expression() { + return GetRuleContext(0); + } + public ITerminalNode RCURLY() { return GetToken(EsperEPL2GrammarParser.RCURLY, 0); } + public ExpressionLambdaDeclContext expressionLambdaDecl() { + return GetRuleContext(0); + } + public ITerminalNode LBRACK() { return GetToken(EsperEPL2GrammarParser.LBRACK, 0); } + public StringconstantContext stringconstant() { + return GetRuleContext(0); + } + public ITerminalNode RBRACK() { return GetToken(EsperEPL2GrammarParser.RBRACK, 0); } + public ExpressionDefContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_expressionDef; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterExpressionDef(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitExpressionDef(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitExpressionDef(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public ExpressionDefContext expressionDef() { + ExpressionDefContext _localctx = new ExpressionDefContext(Context, State); + EnterRule(_localctx, 12, RULE_expressionDef); + try { + State = 570; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case LCURLY: + EnterOuterAlt(_localctx, 1); + { + State = 559; Match(LCURLY); + State = 561; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,11,Context) ) { + case 1: + { + State = 560; expressionLambdaDecl(); + } + break; + } + State = 563; expression(); + State = 564; Match(RCURLY); + } + break; + case LBRACK: + EnterOuterAlt(_localctx, 2); + { + State = 566; Match(LBRACK); + State = 567; stringconstant(); + State = 568; Match(RBRACK); + } + break; + default: + throw new NoViableAltException(this); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class ExpressionLambdaDeclContext : ParserRuleContext { + public IToken i; + public ITerminalNode GOES() { return GetToken(EsperEPL2GrammarParser.GOES, 0); } + public ITerminalNode FOLLOWED_BY() { return GetToken(EsperEPL2GrammarParser.FOLLOWED_BY, 0); } + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public ColumnListContext columnList() { + return GetRuleContext(0); + } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public ExpressionLambdaDeclContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_expressionLambdaDecl; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterExpressionLambdaDecl(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitExpressionLambdaDecl(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitExpressionLambdaDecl(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public ExpressionLambdaDeclContext expressionLambdaDecl() { + ExpressionLambdaDeclContext _localctx = new ExpressionLambdaDeclContext(Context, State); + EnterRule(_localctx, 14, RULE_expressionLambdaDecl); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 577; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case IDENT: + { + State = 572; _localctx.i = Match(IDENT); + } + break; + case LPAREN: + { + { + State = 573; Match(LPAREN); + State = 574; columnList(); + State = 575; Match(RPAREN); + } + } + break; + default: + throw new NoViableAltException(this); + } + State = 579; + _la = TokenStream.LA(1); + if ( !(_la==FOLLOWED_BY || _la==GOES) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class ExpressionTypeAnnoContext : ParserRuleContext { + public IToken n; + public IToken v; + public ITerminalNode ATCHAR() { return GetToken(EsperEPL2GrammarParser.ATCHAR, 0); } + public ITerminalNode[] IDENT() { return GetTokens(EsperEPL2GrammarParser.IDENT); } + public ITerminalNode IDENT(int i) { + return GetToken(EsperEPL2GrammarParser.IDENT, i); + } + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public ExpressionTypeAnnoContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_expressionTypeAnno; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterExpressionTypeAnno(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitExpressionTypeAnno(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitExpressionTypeAnno(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public ExpressionTypeAnnoContext expressionTypeAnno() { + ExpressionTypeAnnoContext _localctx = new ExpressionTypeAnnoContext(Context, State); + EnterRule(_localctx, 16, RULE_expressionTypeAnno); + try { + EnterOuterAlt(_localctx, 1); + { + State = 581; Match(ATCHAR); + State = 582; _localctx.n = Match(IDENT); + { + State = 583; Match(LPAREN); + State = 584; _localctx.v = Match(IDENT); + State = 585; Match(RPAREN); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class AnnotationEnumContext : ParserRuleContext { + public ITerminalNode ATCHAR() { return GetToken(EsperEPL2GrammarParser.ATCHAR, 0); } + public ClassIdentifierContext classIdentifier() { + return GetRuleContext(0); + } + public ElementValuePairsEnumContext elementValuePairsEnum() { + return GetRuleContext(0); + } + public ElementValueEnumContext elementValueEnum() { + return GetRuleContext(0); + } + public AnnotationEnumContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_annotationEnum; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterAnnotationEnum(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitAnnotationEnum(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitAnnotationEnum(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public AnnotationEnumContext annotationEnum() { + AnnotationEnumContext _localctx = new AnnotationEnumContext(Context, State); + EnterRule(_localctx, 18, RULE_annotationEnum); + try { + EnterOuterAlt(_localctx, 1); + { + State = 587; Match(ATCHAR); + State = 588; classIdentifier(); + State = 595; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,15,Context) ) { + case 1: + { + State = 589; Match(LPAREN); + State = 592; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,14,Context) ) { + case 1: + { + State = 590; elementValuePairsEnum(); + } + break; + case 2: + { + State = 591; elementValueEnum(); + } + break; + } + State = 594; Match(RPAREN); + } + break; + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class ElementValuePairsEnumContext : ParserRuleContext { + public ElementValuePairEnumContext[] elementValuePairEnum() { + return GetRuleContexts(); + } + public ElementValuePairEnumContext elementValuePairEnum(int i) { + return GetRuleContext(i); + } + public ITerminalNode[] COMMA() { return GetTokens(EsperEPL2GrammarParser.COMMA); } + public ITerminalNode COMMA(int i) { + return GetToken(EsperEPL2GrammarParser.COMMA, i); + } + public ElementValuePairsEnumContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_elementValuePairsEnum; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterElementValuePairsEnum(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitElementValuePairsEnum(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitElementValuePairsEnum(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public ElementValuePairsEnumContext elementValuePairsEnum() { + ElementValuePairsEnumContext _localctx = new ElementValuePairsEnumContext(Context, State); + EnterRule(_localctx, 20, RULE_elementValuePairsEnum); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 597; elementValuePairEnum(); + State = 602; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 598; Match(COMMA); + State = 599; elementValuePairEnum(); + } + } + State = 604; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class ElementValuePairEnumContext : ParserRuleContext { + public KeywordAllowedIdentContext keywordAllowedIdent() { + return GetRuleContext(0); + } + public ElementValueEnumContext elementValueEnum() { + return GetRuleContext(0); + } + public ElementValuePairEnumContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_elementValuePairEnum; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterElementValuePairEnum(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitElementValuePairEnum(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitElementValuePairEnum(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public ElementValuePairEnumContext elementValuePairEnum() { + ElementValuePairEnumContext _localctx = new ElementValuePairEnumContext(Context, State); + EnterRule(_localctx, 22, RULE_elementValuePairEnum); + try { + EnterOuterAlt(_localctx, 1); + { + State = 605; keywordAllowedIdent(); + State = 606; Match(EQUALS); + State = 607; elementValueEnum(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class ElementValueEnumContext : ParserRuleContext { + public IToken v; + public AnnotationEnumContext annotationEnum() { + return GetRuleContext(0); + } + public ElementValueArrayEnumContext elementValueArrayEnum() { + return GetRuleContext(0); + } + public ConstantContext constant() { + return GetRuleContext(0); + } + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public ClassIdentifierContext classIdentifier() { + return GetRuleContext(0); + } + public ElementValueEnumContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_elementValueEnum; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterElementValueEnum(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitElementValueEnum(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitElementValueEnum(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public ElementValueEnumContext elementValueEnum() { + ElementValueEnumContext _localctx = new ElementValueEnumContext(Context, State); + EnterRule(_localctx, 24, RULE_elementValueEnum); + try { + State = 614; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,17,Context) ) { + case 1: + EnterOuterAlt(_localctx, 1); + { + State = 609; annotationEnum(); + } + break; + case 2: + EnterOuterAlt(_localctx, 2); + { + State = 610; elementValueArrayEnum(); + } + break; + case 3: + EnterOuterAlt(_localctx, 3); + { + State = 611; constant(); + } + break; + case 4: + EnterOuterAlt(_localctx, 4); + { + State = 612; _localctx.v = Match(IDENT); + } + break; + case 5: + EnterOuterAlt(_localctx, 5); + { + State = 613; classIdentifier(); + } + break; + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class ElementValueArrayEnumContext : ParserRuleContext { + public ElementValueEnumContext[] elementValueEnum() { + return GetRuleContexts(); + } + public ElementValueEnumContext elementValueEnum(int i) { + return GetRuleContext(i); + } + public ElementValueArrayEnumContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_elementValueArrayEnum; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterElementValueArrayEnum(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitElementValueArrayEnum(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitElementValueArrayEnum(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public ElementValueArrayEnumContext elementValueArrayEnum() { + ElementValueArrayEnumContext _localctx = new ElementValueArrayEnumContext(Context, State); + EnterRule(_localctx, 26, RULE_elementValueArrayEnum); + int _la; + try { + int _alt; + EnterOuterAlt(_localctx, 1); + { + State = 616; Match(LCURLY); + State = 625; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (((((_la - 47)) & ~0x3f) == 0 && ((1L << (_la - 47)) & ((1L << (EVENTS - 47)) | (1L << (BOOLEAN_TRUE - 47)) | (1L << (BOOLEAN_FALSE - 47)) | (1L << (VALUE_NULL - 47)))) != 0) || ((((_la - 148)) & ~0x3f) == 0 && ((1L << (_la - 148)) & ((1L << (LCURLY - 148)) | (1L << (PLUS - 148)) | (1L << (MINUS - 148)) | (1L << (ATCHAR - 148)) | (1L << (TICKED_STRING_LITERAL - 148)) | (1L << (QUOTED_STRING_LITERAL - 148)) | (1L << (STRING_LITERAL - 148)) | (1L << (IDENT - 148)) | (1L << (IntegerLiteral - 148)) | (1L << (FloatingPointLiteral - 148)))) != 0)) { + { + State = 617; elementValueEnum(); + State = 622; + ErrorHandler.Sync(this); + _alt = Interpreter.AdaptivePredict(TokenStream,18,Context); + while ( _alt!=2 && _alt!=global::Antlr4.Runtime.Atn.ATN.INVALID_ALT_NUMBER ) { + if ( _alt==1 ) { + { + { + State = 618; Match(COMMA); + State = 619; elementValueEnum(); + } + } + } + State = 624; + ErrorHandler.Sync(this); + _alt = Interpreter.AdaptivePredict(TokenStream,18,Context); + } + } + } + + State = 628; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==COMMA) { + { + State = 627; Match(COMMA); + } + } + + State = 630; Match(RCURLY); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class EplExpressionContext : ParserRuleContext { + public SelectExprContext selectExpr() { + return GetRuleContext(0); + } + public CreateWindowExprContext createWindowExpr() { + return GetRuleContext(0); + } + public CreateIndexExprContext createIndexExpr() { + return GetRuleContext(0); + } + public CreateVariableExprContext createVariableExpr() { + return GetRuleContext(0); + } + public CreateTableExprContext createTableExpr() { + return GetRuleContext(0); + } + public CreateSchemaExprContext createSchemaExpr() { + return GetRuleContext(0); + } + public CreateContextExprContext createContextExpr() { + return GetRuleContext(0); + } + public CreateExpressionExprContext createExpressionExpr() { + return GetRuleContext(0); + } + public OnExprContext onExpr() { + return GetRuleContext(0); + } + public UpdateExprContext updateExpr() { + return GetRuleContext(0); + } + public CreateDataflowContext createDataflow() { + return GetRuleContext(0); + } + public FafDeleteContext fafDelete() { + return GetRuleContext(0); + } + public FafUpdateContext fafUpdate() { + return GetRuleContext(0); + } + public FafInsertContext fafInsert() { + return GetRuleContext(0); + } + public ContextExprContext contextExpr() { + return GetRuleContext(0); + } + public ForExprContext forExpr() { + return GetRuleContext(0); + } + public EplExpressionContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_eplExpression; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterEplExpression(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitEplExpression(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitEplExpression(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public EplExpressionContext eplExpression() { + EplExpressionContext _localctx = new EplExpressionContext(Context, State); + EnterRule(_localctx, 28, RULE_eplExpression); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 633; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==CONTEXT) { + { + State = 632; contextExpr(); + } + } + + State = 649; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,22,Context) ) { + case 1: + { + State = 635; selectExpr(); + } + break; + case 2: + { + State = 636; createWindowExpr(); + } + break; + case 3: + { + State = 637; createIndexExpr(); + } + break; + case 4: + { + State = 638; createVariableExpr(); + } + break; + case 5: + { + State = 639; createTableExpr(); + } + break; + case 6: + { + State = 640; createSchemaExpr(); + } + break; + case 7: + { + State = 641; createContextExpr(); + } + break; + case 8: + { + State = 642; createExpressionExpr(); + } + break; + case 9: + { + State = 643; onExpr(); + } + break; + case 10: + { + State = 644; updateExpr(); + } + break; + case 11: + { + State = 645; createDataflow(); + } + break; + case 12: + { + State = 646; fafDelete(); + } + break; + case 13: + { + State = 647; fafUpdate(); + } + break; + case 14: + { + State = 648; fafInsert(); + } + break; + } + State = 652; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==FOR) { + { + State = 651; forExpr(); + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class ContextExprContext : ParserRuleContext { + public IToken i; + public ITerminalNode CONTEXT() { return GetToken(EsperEPL2GrammarParser.CONTEXT, 0); } + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public ContextExprContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_contextExpr; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterContextExpr(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitContextExpr(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitContextExpr(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public ContextExprContext contextExpr() { + ContextExprContext _localctx = new ContextExprContext(Context, State); + EnterRule(_localctx, 30, RULE_contextExpr); + try { + EnterOuterAlt(_localctx, 1); + { + State = 654; Match(CONTEXT); + State = 655; _localctx.i = Match(IDENT); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class SelectExprContext : ParserRuleContext { + public ITerminalNode SELECT() { return GetToken(EsperEPL2GrammarParser.SELECT, 0); } + public SelectClauseContext selectClause() { + return GetRuleContext(0); + } + public ITerminalNode INTO() { return GetToken(EsperEPL2GrammarParser.INTO, 0); } + public IntoTableExprContext intoTableExpr() { + return GetRuleContext(0); + } + public ITerminalNode INSERT() { return GetToken(EsperEPL2GrammarParser.INSERT, 0); } + public InsertIntoExprContext insertIntoExpr() { + return GetRuleContext(0); + } + public ITerminalNode FROM() { return GetToken(EsperEPL2GrammarParser.FROM, 0); } + public FromClauseContext fromClause() { + return GetRuleContext(0); + } + public MatchRecogContext matchRecog() { + return GetRuleContext(0); + } + public ITerminalNode WHERE() { return GetToken(EsperEPL2GrammarParser.WHERE, 0); } + public WhereClauseContext whereClause() { + return GetRuleContext(0); + } + public ITerminalNode GROUP() { return GetToken(EsperEPL2GrammarParser.GROUP, 0); } + public ITerminalNode[] BY() { return GetTokens(EsperEPL2GrammarParser.BY); } + public ITerminalNode BY(int i) { + return GetToken(EsperEPL2GrammarParser.BY, i); + } + public GroupByListExprContext groupByListExpr() { + return GetRuleContext(0); + } + public ITerminalNode HAVING() { return GetToken(EsperEPL2GrammarParser.HAVING, 0); } + public HavingClauseContext havingClause() { + return GetRuleContext(0); + } + public ITerminalNode OUTPUT() { return GetToken(EsperEPL2GrammarParser.OUTPUT, 0); } + public OutputLimitContext outputLimit() { + return GetRuleContext(0); + } + public ITerminalNode ORDER() { return GetToken(EsperEPL2GrammarParser.ORDER, 0); } + public OrderByListExprContext orderByListExpr() { + return GetRuleContext(0); + } + public ITerminalNode ROW_LIMIT_EXPR() { return GetToken(EsperEPL2GrammarParser.ROW_LIMIT_EXPR, 0); } + public RowLimitContext rowLimit() { + return GetRuleContext(0); + } + public SelectExprContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_selectExpr; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterSelectExpr(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitSelectExpr(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitSelectExpr(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public SelectExprContext selectExpr() { + SelectExprContext _localctx = new SelectExprContext(Context, State); + EnterRule(_localctx, 32, RULE_selectExpr); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 659; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==INTO) { + { + State = 657; Match(INTO); + State = 658; intoTableExpr(); + } + } + + State = 663; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==INSERT) { + { + State = 661; Match(INSERT); + State = 662; insertIntoExpr(); + } + } + + State = 665; Match(SELECT); + State = 666; selectClause(); + State = 669; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==FROM) { + { + State = 667; Match(FROM); + State = 668; fromClause(); + } + } + + State = 672; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==MATCH_RECOGNIZE) { + { + State = 671; matchRecog(); + } + } + + State = 676; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==WHERE) { + { + State = 674; Match(WHERE); + State = 675; whereClause(); + } + } + + State = 681; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==GROUP) { + { + State = 678; Match(GROUP); + State = 679; Match(BY); + State = 680; groupByListExpr(); + } + } + + State = 685; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==HAVING) { + { + State = 683; Match(HAVING); + State = 684; havingClause(); + } + } + + State = 689; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==OUTPUT) { + { + State = 687; Match(OUTPUT); + State = 688; outputLimit(); + } + } + + State = 694; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==ORDER) { + { + State = 691; Match(ORDER); + State = 692; Match(BY); + State = 693; orderByListExpr(); + } + } + + State = 698; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==ROW_LIMIT_EXPR) { + { + State = 696; Match(ROW_LIMIT_EXPR); + State = 697; rowLimit(); + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class OnExprContext : ParserRuleContext { + public ITerminalNode ON() { return GetToken(EsperEPL2GrammarParser.ON, 0); } + public OnStreamExprContext onStreamExpr() { + return GetRuleContext(0); + } + public OnDeleteExprContext onDeleteExpr() { + return GetRuleContext(0); + } + public OnSelectExprContext onSelectExpr() { + return GetRuleContext(0); + } + public OnSetExprContext onSetExpr() { + return GetRuleContext(0); + } + public OnUpdateExprContext onUpdateExpr() { + return GetRuleContext(0); + } + public OnMergeExprContext onMergeExpr() { + return GetRuleContext(0); + } + public OnSelectInsertExprContext[] onSelectInsertExpr() { + return GetRuleContexts(); + } + public OnSelectInsertExprContext onSelectInsertExpr(int i) { + return GetRuleContext(i); + } + public OutputClauseInsertContext outputClauseInsert() { + return GetRuleContext(0); + } + public OnExprContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_onExpr; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterOnExpr(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitOnExpr(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitOnExpr(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public OnExprContext onExpr() { + OnExprContext _localctx = new OnExprContext(Context, State); + EnterRule(_localctx, 34, RULE_onExpr); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 700; Match(ON); + State = 701; onStreamExpr(); + State = 717; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case DELETE: + { + State = 702; onDeleteExpr(); + } + break; + case SELECT: + case INSERT: + { + State = 703; onSelectExpr(); + State = 712; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==INSERT) { + { + State = 705; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + do { + { + { + State = 704; onSelectInsertExpr(); + } + } + State = 707; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } while ( _la==INSERT ); + State = 710; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==OUTPUT) { + { + State = 709; outputClauseInsert(); + } + } + + } + } + + } + break; + case SET: + { + State = 714; onSetExpr(); + } + break; + case UPDATE: + { + State = 715; onUpdateExpr(); + } + break; + case MERGE: + { + State = 716; onMergeExpr(); + } + break; + default: + throw new NoViableAltException(this); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class OnStreamExprContext : ParserRuleContext { + public IToken i; + public EventFilterExpressionContext eventFilterExpression() { + return GetRuleContext(0); + } + public PatternInclusionExpressionContext patternInclusionExpression() { + return GetRuleContext(0); + } + public ITerminalNode AS() { return GetToken(EsperEPL2GrammarParser.AS, 0); } + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public OnStreamExprContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_onStreamExpr; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterOnStreamExpr(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitOnStreamExpr(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitOnStreamExpr(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public OnStreamExprContext onStreamExpr() { + OnStreamExprContext _localctx = new OnStreamExprContext(Context, State); + EnterRule(_localctx, 36, RULE_onStreamExpr); + try { + EnterOuterAlt(_localctx, 1); + { + State = 721; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case EVENTS: + case TICKED_STRING_LITERAL: + case IDENT: + { + State = 719; eventFilterExpression(); + } + break; + case PATTERN: + { + State = 720; patternInclusionExpression(); + } + break; + default: + throw new NoViableAltException(this); + } + State = 726; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case AS: + { + State = 723; Match(AS); + State = 724; _localctx.i = Match(IDENT); + } + break; + case IDENT: + { + State = 725; _localctx.i = Match(IDENT); + } + break; + case SELECT: + case INSERT: + case DELETE: + case SET: + case UPDATE: + case MERGE: + break; + default: + throw new NoViableAltException(this); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class UpdateExprContext : ParserRuleContext { + public ITerminalNode UPDATE() { return GetToken(EsperEPL2GrammarParser.UPDATE, 0); } + public ITerminalNode ISTREAM() { return GetToken(EsperEPL2GrammarParser.ISTREAM, 0); } + public UpdateDetailsContext updateDetails() { + return GetRuleContext(0); + } + public UpdateExprContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_updateExpr; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterUpdateExpr(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitUpdateExpr(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitUpdateExpr(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public UpdateExprContext updateExpr() { + UpdateExprContext _localctx = new UpdateExprContext(Context, State); + EnterRule(_localctx, 38, RULE_updateExpr); + try { + EnterOuterAlt(_localctx, 1); + { + State = 728; Match(UPDATE); + State = 729; Match(ISTREAM); + State = 730; updateDetails(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class UpdateDetailsContext : ParserRuleContext { + public IToken i; + public ClassIdentifierContext classIdentifier() { + return GetRuleContext(0); + } + public ITerminalNode SET() { return GetToken(EsperEPL2GrammarParser.SET, 0); } + public OnSetAssignmentListContext onSetAssignmentList() { + return GetRuleContext(0); + } + public ITerminalNode AS() { return GetToken(EsperEPL2GrammarParser.AS, 0); } + public ITerminalNode WHERE() { return GetToken(EsperEPL2GrammarParser.WHERE, 0); } + public WhereClauseContext whereClause() { + return GetRuleContext(0); + } + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public UpdateDetailsContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_updateDetails; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterUpdateDetails(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitUpdateDetails(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitUpdateDetails(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public UpdateDetailsContext updateDetails() { + UpdateDetailsContext _localctx = new UpdateDetailsContext(Context, State); + EnterRule(_localctx, 40, RULE_updateDetails); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 732; classIdentifier(); + State = 736; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case AS: + { + State = 733; Match(AS); + State = 734; _localctx.i = Match(IDENT); + } + break; + case IDENT: + { + State = 735; _localctx.i = Match(IDENT); + } + break; + case SET: + break; + default: + throw new NoViableAltException(this); + } + State = 738; Match(SET); + State = 739; onSetAssignmentList(); + State = 742; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==WHERE) { + { + State = 740; Match(WHERE); + State = 741; whereClause(); + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class OnMergeExprContext : ParserRuleContext { + public IToken n; + public IToken i; + public ITerminalNode MERGE() { return GetToken(EsperEPL2GrammarParser.MERGE, 0); } + public ITerminalNode[] IDENT() { return GetTokens(EsperEPL2GrammarParser.IDENT); } + public ITerminalNode IDENT(int i) { + return GetToken(EsperEPL2GrammarParser.IDENT, i); + } + public ITerminalNode INTO() { return GetToken(EsperEPL2GrammarParser.INTO, 0); } + public ITerminalNode AS() { return GetToken(EsperEPL2GrammarParser.AS, 0); } + public ITerminalNode WHERE() { return GetToken(EsperEPL2GrammarParser.WHERE, 0); } + public WhereClauseContext whereClause() { + return GetRuleContext(0); + } + public MergeItemContext[] mergeItem() { + return GetRuleContexts(); + } + public MergeItemContext mergeItem(int i) { + return GetRuleContext(i); + } + public OnMergeExprContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_onMergeExpr; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterOnMergeExpr(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitOnMergeExpr(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitOnMergeExpr(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public OnMergeExprContext onMergeExpr() { + OnMergeExprContext _localctx = new OnMergeExprContext(Context, State); + EnterRule(_localctx, 42, RULE_onMergeExpr); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 744; Match(MERGE); + State = 746; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==INTO) { + { + State = 745; Match(INTO); + } + } + + State = 748; _localctx.n = Match(IDENT); + State = 752; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case AS: + { + State = 749; Match(AS); + State = 750; _localctx.i = Match(IDENT); + } + break; + case IDENT: + { + State = 751; _localctx.i = Match(IDENT); + } + break; + case WHERE: + case WHEN: + break; + default: + throw new NoViableAltException(this); + } + State = 756; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==WHERE) { + { + State = 754; Match(WHERE); + State = 755; whereClause(); + } + } + + State = 759; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + do { + { + { + State = 758; mergeItem(); + } + } + State = 761; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } while ( _la==WHEN ); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class MergeItemContext : ParserRuleContext { + public MergeMatchedContext mergeMatched() { + return GetRuleContext(0); + } + public MergeUnmatchedContext mergeUnmatched() { + return GetRuleContext(0); + } + public MergeItemContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_mergeItem; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterMergeItem(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitMergeItem(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitMergeItem(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public MergeItemContext mergeItem() { + MergeItemContext _localctx = new MergeItemContext(Context, State); + EnterRule(_localctx, 44, RULE_mergeItem); + try { + EnterOuterAlt(_localctx, 1); + { + State = 765; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,46,Context) ) { + case 1: + { + State = 763; mergeMatched(); + } + break; + case 2: + { + State = 764; mergeUnmatched(); + } + break; + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class MergeMatchedContext : ParserRuleContext { + public ITerminalNode WHEN() { return GetToken(EsperEPL2GrammarParser.WHEN, 0); } + public ITerminalNode MATCHED() { return GetToken(EsperEPL2GrammarParser.MATCHED, 0); } + public ITerminalNode AND_EXPR() { return GetToken(EsperEPL2GrammarParser.AND_EXPR, 0); } + public ExpressionContext expression() { + return GetRuleContext(0); + } + public MergeMatchedItemContext[] mergeMatchedItem() { + return GetRuleContexts(); + } + public MergeMatchedItemContext mergeMatchedItem(int i) { + return GetRuleContext(i); + } + public MergeMatchedContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_mergeMatched; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterMergeMatched(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitMergeMatched(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitMergeMatched(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public MergeMatchedContext mergeMatched() { + MergeMatchedContext _localctx = new MergeMatchedContext(Context, State); + EnterRule(_localctx, 46, RULE_mergeMatched); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 767; Match(WHEN); + State = 768; Match(MATCHED); + State = 771; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==AND_EXPR) { + { + State = 769; Match(AND_EXPR); + State = 770; expression(); + } + } + + State = 774; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + do { + { + { + State = 773; mergeMatchedItem(); + } + } + State = 776; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } while ( _la==THEN ); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class MergeMatchedItemContext : ParserRuleContext { + public IToken u; + public IToken d; + public ITerminalNode THEN() { return GetToken(EsperEPL2GrammarParser.THEN, 0); } + public MergeInsertContext mergeInsert() { + return GetRuleContext(0); + } + public ITerminalNode DELETE() { return GetToken(EsperEPL2GrammarParser.DELETE, 0); } + public ITerminalNode SET() { return GetToken(EsperEPL2GrammarParser.SET, 0); } + public OnSetAssignmentListContext onSetAssignmentList() { + return GetRuleContext(0); + } + public ITerminalNode UPDATE() { return GetToken(EsperEPL2GrammarParser.UPDATE, 0); } + public ITerminalNode WHERE() { return GetToken(EsperEPL2GrammarParser.WHERE, 0); } + public WhereClauseContext whereClause() { + return GetRuleContext(0); + } + public MergeMatchedItemContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_mergeMatchedItem; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterMergeMatchedItem(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitMergeMatchedItem(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitMergeMatchedItem(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public MergeMatchedItemContext mergeMatchedItem() { + MergeMatchedItemContext _localctx = new MergeMatchedItemContext(Context, State); + EnterRule(_localctx, 48, RULE_mergeMatchedItem); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 778; Match(THEN); + State = 793; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case UPDATE: + { + { + State = 779; _localctx.u = Match(UPDATE); + State = 780; Match(SET); + State = 781; onSetAssignmentList(); + } + State = 785; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==WHERE) { + { + State = 783; Match(WHERE); + State = 784; whereClause(); + } + } + + } + break; + case DELETE: + { + State = 787; _localctx.d = Match(DELETE); + State = 790; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==WHERE) { + { + State = 788; Match(WHERE); + State = 789; whereClause(); + } + } + + } + break; + case INSERT: + { + State = 792; mergeInsert(); + } + break; + default: + throw new NoViableAltException(this); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class MergeUnmatchedContext : ParserRuleContext { + public ITerminalNode WHEN() { return GetToken(EsperEPL2GrammarParser.WHEN, 0); } + public ITerminalNode NOT_EXPR() { return GetToken(EsperEPL2GrammarParser.NOT_EXPR, 0); } + public ITerminalNode MATCHED() { return GetToken(EsperEPL2GrammarParser.MATCHED, 0); } + public ITerminalNode AND_EXPR() { return GetToken(EsperEPL2GrammarParser.AND_EXPR, 0); } + public ExpressionContext expression() { + return GetRuleContext(0); + } + public MergeUnmatchedItemContext[] mergeUnmatchedItem() { + return GetRuleContexts(); + } + public MergeUnmatchedItemContext mergeUnmatchedItem(int i) { + return GetRuleContext(i); + } + public MergeUnmatchedContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_mergeUnmatched; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterMergeUnmatched(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitMergeUnmatched(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitMergeUnmatched(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public MergeUnmatchedContext mergeUnmatched() { + MergeUnmatchedContext _localctx = new MergeUnmatchedContext(Context, State); + EnterRule(_localctx, 50, RULE_mergeUnmatched); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 795; Match(WHEN); + State = 796; Match(NOT_EXPR); + State = 797; Match(MATCHED); + State = 800; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==AND_EXPR) { + { + State = 798; Match(AND_EXPR); + State = 799; expression(); + } + } + + State = 803; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + do { + { + { + State = 802; mergeUnmatchedItem(); + } + } + State = 805; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } while ( _la==THEN ); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class MergeUnmatchedItemContext : ParserRuleContext { + public ITerminalNode THEN() { return GetToken(EsperEPL2GrammarParser.THEN, 0); } + public MergeInsertContext mergeInsert() { + return GetRuleContext(0); + } + public MergeUnmatchedItemContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_mergeUnmatchedItem; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterMergeUnmatchedItem(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitMergeUnmatchedItem(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitMergeUnmatchedItem(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public MergeUnmatchedItemContext mergeUnmatchedItem() { + MergeUnmatchedItemContext _localctx = new MergeUnmatchedItemContext(Context, State); + EnterRule(_localctx, 52, RULE_mergeUnmatchedItem); + try { + EnterOuterAlt(_localctx, 1); + { + State = 807; Match(THEN); + State = 808; mergeInsert(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class MergeInsertContext : ParserRuleContext { + public ITerminalNode INSERT() { return GetToken(EsperEPL2GrammarParser.INSERT, 0); } + public ITerminalNode SELECT() { return GetToken(EsperEPL2GrammarParser.SELECT, 0); } + public SelectionListContext selectionList() { + return GetRuleContext(0); + } + public ITerminalNode INTO() { return GetToken(EsperEPL2GrammarParser.INTO, 0); } + public ClassIdentifierContext classIdentifier() { + return GetRuleContext(0); + } + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public ColumnListContext columnList() { + return GetRuleContext(0); + } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public ITerminalNode WHERE() { return GetToken(EsperEPL2GrammarParser.WHERE, 0); } + public WhereClauseContext whereClause() { + return GetRuleContext(0); + } + public MergeInsertContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_mergeInsert; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterMergeInsert(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitMergeInsert(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitMergeInsert(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public MergeInsertContext mergeInsert() { + MergeInsertContext _localctx = new MergeInsertContext(Context, State); + EnterRule(_localctx, 54, RULE_mergeInsert); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 810; Match(INSERT); + State = 813; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==INTO) { + { + State = 811; Match(INTO); + State = 812; classIdentifier(); + } + } + + State = 819; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==LPAREN) { + { + State = 815; Match(LPAREN); + State = 816; columnList(); + State = 817; Match(RPAREN); + } + } + + State = 821; Match(SELECT); + State = 822; selectionList(); + State = 825; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==WHERE) { + { + State = 823; Match(WHERE); + State = 824; whereClause(); + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class OnSelectExprContext : ParserRuleContext { + public IToken d; + public ITerminalNode SELECT() { return GetToken(EsperEPL2GrammarParser.SELECT, 0); } + public SelectionListContext selectionList() { + return GetRuleContext(0); + } + public ITerminalNode INSERT() { return GetToken(EsperEPL2GrammarParser.INSERT, 0); } + public InsertIntoExprContext insertIntoExpr() { + return GetRuleContext(0); + } + public ITerminalNode DISTINCT() { return GetToken(EsperEPL2GrammarParser.DISTINCT, 0); } + public OnExprFromContext onExprFrom() { + return GetRuleContext(0); + } + public ITerminalNode WHERE() { return GetToken(EsperEPL2GrammarParser.WHERE, 0); } + public WhereClauseContext whereClause() { + return GetRuleContext(0); + } + public ITerminalNode GROUP() { return GetToken(EsperEPL2GrammarParser.GROUP, 0); } + public ITerminalNode[] BY() { return GetTokens(EsperEPL2GrammarParser.BY); } + public ITerminalNode BY(int i) { + return GetToken(EsperEPL2GrammarParser.BY, i); + } + public GroupByListExprContext groupByListExpr() { + return GetRuleContext(0); + } + public ITerminalNode HAVING() { return GetToken(EsperEPL2GrammarParser.HAVING, 0); } + public HavingClauseContext havingClause() { + return GetRuleContext(0); + } + public ITerminalNode ORDER() { return GetToken(EsperEPL2GrammarParser.ORDER, 0); } + public OrderByListExprContext orderByListExpr() { + return GetRuleContext(0); + } + public ITerminalNode ROW_LIMIT_EXPR() { return GetToken(EsperEPL2GrammarParser.ROW_LIMIT_EXPR, 0); } + public RowLimitContext rowLimit() { + return GetRuleContext(0); + } + public ITerminalNode DELETE() { return GetToken(EsperEPL2GrammarParser.DELETE, 0); } + public ITerminalNode AND_EXPR() { return GetToken(EsperEPL2GrammarParser.AND_EXPR, 0); } + public OnSelectExprContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_onSelectExpr; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterOnSelectExpr(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitOnSelectExpr(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitOnSelectExpr(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public OnSelectExprContext onSelectExpr() { + OnSelectExprContext _localctx = new OnSelectExprContext(Context, State); + EnterRule(_localctx, 56, RULE_onSelectExpr); + paraphrases.Push("on-select clause"); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 829; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==INSERT) { + { + State = 827; Match(INSERT); + State = 828; insertIntoExpr(); + } + } + + State = 831; Match(SELECT); + State = 836; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==AND_EXPR || _la==DELETE) { + { + State = 833; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==AND_EXPR) { + { + State = 832; Match(AND_EXPR); + } + } + + State = 835; _localctx.d = Match(DELETE); + } + } + + State = 839; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==DISTINCT) { + { + State = 838; Match(DISTINCT); + } + } + + State = 841; selectionList(); + State = 843; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==FROM) { + { + State = 842; onExprFrom(); + } + } + + State = 847; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==WHERE) { + { + State = 845; Match(WHERE); + State = 846; whereClause(); + } + } + + State = 852; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==GROUP) { + { + State = 849; Match(GROUP); + State = 850; Match(BY); + State = 851; groupByListExpr(); + } + } + + State = 856; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==HAVING) { + { + State = 854; Match(HAVING); + State = 855; havingClause(); + } + } + + State = 861; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==ORDER) { + { + State = 858; Match(ORDER); + State = 859; Match(BY); + State = 860; orderByListExpr(); + } + } + + State = 865; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==ROW_LIMIT_EXPR) { + { + State = 863; Match(ROW_LIMIT_EXPR); + State = 864; rowLimit(); + } + } + + } + Context.Stop = TokenStream.LT(-1); + paraphrases.Pop(); + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class OnUpdateExprContext : ParserRuleContext { + public IToken n; + public IToken i; + public ITerminalNode UPDATE() { return GetToken(EsperEPL2GrammarParser.UPDATE, 0); } + public ITerminalNode SET() { return GetToken(EsperEPL2GrammarParser.SET, 0); } + public OnSetAssignmentListContext onSetAssignmentList() { + return GetRuleContext(0); + } + public ITerminalNode[] IDENT() { return GetTokens(EsperEPL2GrammarParser.IDENT); } + public ITerminalNode IDENT(int i) { + return GetToken(EsperEPL2GrammarParser.IDENT, i); + } + public ITerminalNode AS() { return GetToken(EsperEPL2GrammarParser.AS, 0); } + public ITerminalNode WHERE() { return GetToken(EsperEPL2GrammarParser.WHERE, 0); } + public WhereClauseContext whereClause() { + return GetRuleContext(0); + } + public OnUpdateExprContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_onUpdateExpr; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterOnUpdateExpr(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitOnUpdateExpr(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitOnUpdateExpr(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public OnUpdateExprContext onUpdateExpr() { + OnUpdateExprContext _localctx = new OnUpdateExprContext(Context, State); + EnterRule(_localctx, 58, RULE_onUpdateExpr); + paraphrases.Push("on-update clause"); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 867; Match(UPDATE); + State = 868; _localctx.n = Match(IDENT); + State = 872; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case AS: + { + State = 869; Match(AS); + State = 870; _localctx.i = Match(IDENT); + } + break; + case IDENT: + { + State = 871; _localctx.i = Match(IDENT); + } + break; + case SET: + break; + default: + throw new NoViableAltException(this); + } + State = 874; Match(SET); + State = 875; onSetAssignmentList(); + State = 878; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==WHERE) { + { + State = 876; Match(WHERE); + State = 877; whereClause(); + } + } + + } + Context.Stop = TokenStream.LT(-1); + paraphrases.Pop(); + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class OnSelectInsertExprContext : ParserRuleContext { + public ITerminalNode INSERT() { return GetToken(EsperEPL2GrammarParser.INSERT, 0); } + public InsertIntoExprContext insertIntoExpr() { + return GetRuleContext(0); + } + public ITerminalNode SELECT() { return GetToken(EsperEPL2GrammarParser.SELECT, 0); } + public SelectionListContext selectionList() { + return GetRuleContext(0); + } + public OnSelectInsertFromClauseContext onSelectInsertFromClause() { + return GetRuleContext(0); + } + public ITerminalNode WHERE() { return GetToken(EsperEPL2GrammarParser.WHERE, 0); } + public WhereClauseContext whereClause() { + return GetRuleContext(0); + } + public OnSelectInsertExprContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_onSelectInsertExpr; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterOnSelectInsertExpr(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitOnSelectInsertExpr(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitOnSelectInsertExpr(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public OnSelectInsertExprContext onSelectInsertExpr() { + OnSelectInsertExprContext _localctx = new OnSelectInsertExprContext(Context, State); + EnterRule(_localctx, 60, RULE_onSelectInsertExpr); + paraphrases.Push("on-select-insert clause"); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 880; Match(INSERT); + State = 881; insertIntoExpr(); + State = 882; Match(SELECT); + State = 883; selectionList(); + State = 885; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==FROM) { + { + State = 884; onSelectInsertFromClause(); + } + } + + State = 889; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==WHERE) { + { + State = 887; Match(WHERE); + State = 888; whereClause(); + } + } + + } + Context.Stop = TokenStream.LT(-1); + paraphrases.Pop(); + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class OnSelectInsertFromClauseContext : ParserRuleContext { + public IToken i; + public ITerminalNode FROM() { return GetToken(EsperEPL2GrammarParser.FROM, 0); } + public PropertyExpressionContext propertyExpression() { + return GetRuleContext(0); + } + public ITerminalNode AS() { return GetToken(EsperEPL2GrammarParser.AS, 0); } + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public OnSelectInsertFromClauseContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_onSelectInsertFromClause; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterOnSelectInsertFromClause(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitOnSelectInsertFromClause(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitOnSelectInsertFromClause(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public OnSelectInsertFromClauseContext onSelectInsertFromClause() { + OnSelectInsertFromClauseContext _localctx = new OnSelectInsertFromClauseContext(Context, State); + EnterRule(_localctx, 62, RULE_onSelectInsertFromClause); + try { + EnterOuterAlt(_localctx, 1); + { + State = 891; Match(FROM); + State = 892; propertyExpression(); + State = 896; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case AS: + { + State = 893; Match(AS); + State = 894; _localctx.i = Match(IDENT); + } + break; + case IDENT: + { + State = 895; _localctx.i = Match(IDENT); + } + break; + case Eof: + case WHERE: + case OUTPUT: + case INSERT: + case FOR: + break; + default: + throw new NoViableAltException(this); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class OutputClauseInsertContext : ParserRuleContext { + public IToken f; + public IToken a; + public ITerminalNode OUTPUT() { return GetToken(EsperEPL2GrammarParser.OUTPUT, 0); } + public ITerminalNode FIRST() { return GetToken(EsperEPL2GrammarParser.FIRST, 0); } + public ITerminalNode ALL() { return GetToken(EsperEPL2GrammarParser.ALL, 0); } + public OutputClauseInsertContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_outputClauseInsert; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterOutputClauseInsert(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitOutputClauseInsert(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitOutputClauseInsert(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public OutputClauseInsertContext outputClauseInsert() { + OutputClauseInsertContext _localctx = new OutputClauseInsertContext(Context, State); + EnterRule(_localctx, 64, RULE_outputClauseInsert); + try { + EnterOuterAlt(_localctx, 1); + { + State = 898; Match(OUTPUT); + State = 901; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case FIRST: + { + State = 899; _localctx.f = Match(FIRST); + } + break; + case ALL: + { + State = 900; _localctx.a = Match(ALL); + } + break; + default: + throw new NoViableAltException(this); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class OnDeleteExprContext : ParserRuleContext { + public ITerminalNode DELETE() { return GetToken(EsperEPL2GrammarParser.DELETE, 0); } + public OnExprFromContext onExprFrom() { + return GetRuleContext(0); + } + public ITerminalNode WHERE() { return GetToken(EsperEPL2GrammarParser.WHERE, 0); } + public WhereClauseContext whereClause() { + return GetRuleContext(0); + } + public OnDeleteExprContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_onDeleteExpr; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterOnDeleteExpr(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitOnDeleteExpr(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitOnDeleteExpr(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public OnDeleteExprContext onDeleteExpr() { + OnDeleteExprContext _localctx = new OnDeleteExprContext(Context, State); + EnterRule(_localctx, 66, RULE_onDeleteExpr); + paraphrases.Push("on-delete clause"); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 903; Match(DELETE); + State = 904; onExprFrom(); + State = 907; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==WHERE) { + { + State = 905; Match(WHERE); + State = 906; whereClause(); + } + } + + } + Context.Stop = TokenStream.LT(-1); + paraphrases.Pop(); + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class OnSetExprContext : ParserRuleContext { + public ITerminalNode SET() { return GetToken(EsperEPL2GrammarParser.SET, 0); } + public OnSetAssignmentListContext onSetAssignmentList() { + return GetRuleContext(0); + } + public OnSetExprContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_onSetExpr; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterOnSetExpr(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitOnSetExpr(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitOnSetExpr(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public OnSetExprContext onSetExpr() { + OnSetExprContext _localctx = new OnSetExprContext(Context, State); + EnterRule(_localctx, 68, RULE_onSetExpr); + paraphrases.Push("on-set clause"); + try { + EnterOuterAlt(_localctx, 1); + { + State = 909; Match(SET); + State = 910; onSetAssignmentList(); + } + Context.Stop = TokenStream.LT(-1); + paraphrases.Pop(); + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class OnSetAssignmentListContext : ParserRuleContext { + public OnSetAssignmentContext[] onSetAssignment() { + return GetRuleContexts(); + } + public OnSetAssignmentContext onSetAssignment(int i) { + return GetRuleContext(i); + } + public ITerminalNode[] COMMA() { return GetTokens(EsperEPL2GrammarParser.COMMA); } + public ITerminalNode COMMA(int i) { + return GetToken(EsperEPL2GrammarParser.COMMA, i); + } + public OnSetAssignmentListContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_onSetAssignmentList; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterOnSetAssignmentList(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitOnSetAssignmentList(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitOnSetAssignmentList(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public OnSetAssignmentListContext onSetAssignmentList() { + OnSetAssignmentListContext _localctx = new OnSetAssignmentListContext(Context, State); + EnterRule(_localctx, 70, RULE_onSetAssignmentList); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 912; onSetAssignment(); + State = 917; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 913; Match(COMMA); + State = 914; onSetAssignment(); + } + } + State = 919; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class OnSetAssignmentContext : ParserRuleContext { + public EventPropertyContext eventProperty() { + return GetRuleContext(0); + } + public ITerminalNode EQUALS() { return GetToken(EsperEPL2GrammarParser.EQUALS, 0); } + public ExpressionContext expression() { + return GetRuleContext(0); + } + public OnSetAssignmentContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_onSetAssignment; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterOnSetAssignment(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitOnSetAssignment(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitOnSetAssignment(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public OnSetAssignmentContext onSetAssignment() { + OnSetAssignmentContext _localctx = new OnSetAssignmentContext(Context, State); + EnterRule(_localctx, 72, RULE_onSetAssignment); + try { + State = 925; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,75,Context) ) { + case 1: + EnterOuterAlt(_localctx, 1); + { + State = 920; eventProperty(); + State = 921; Match(EQUALS); + State = 922; expression(); + } + break; + case 2: + EnterOuterAlt(_localctx, 2); + { + State = 924; expression(); + } + break; + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class OnExprFromContext : ParserRuleContext { + public IToken n; + public IToken i; + public ITerminalNode FROM() { return GetToken(EsperEPL2GrammarParser.FROM, 0); } + public ITerminalNode[] IDENT() { return GetTokens(EsperEPL2GrammarParser.IDENT); } + public ITerminalNode IDENT(int i) { + return GetToken(EsperEPL2GrammarParser.IDENT, i); + } + public ITerminalNode AS() { return GetToken(EsperEPL2GrammarParser.AS, 0); } + public OnExprFromContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_onExprFrom; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterOnExprFrom(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitOnExprFrom(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitOnExprFrom(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public OnExprFromContext onExprFrom() { + OnExprFromContext _localctx = new OnExprFromContext(Context, State); + EnterRule(_localctx, 74, RULE_onExprFrom); + try { + EnterOuterAlt(_localctx, 1); + { + State = 927; Match(FROM); + State = 928; _localctx.n = Match(IDENT); + State = 932; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case AS: + { + State = 929; Match(AS); + State = 930; _localctx.i = Match(IDENT); + } + break; + case IDENT: + { + State = 931; _localctx.i = Match(IDENT); + } + break; + case Eof: + case WHERE: + case GROUP: + case HAVING: + case INSERT: + case ORDER: + case ROW_LIMIT_EXPR: + case FOR: + break; + default: + throw new NoViableAltException(this); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class CreateWindowExprContext : ParserRuleContext { + public IToken i; + public IToken ru; + public IToken ri; + public IToken i1; + public ITerminalNode CREATE() { return GetToken(EsperEPL2GrammarParser.CREATE, 0); } + public ITerminalNode WINDOW() { return GetToken(EsperEPL2GrammarParser.WINDOW, 0); } + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public CreateWindowExprModelAfterContext createWindowExprModelAfter() { + return GetRuleContext(0); + } + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public CreateColumnListContext createColumnList() { + return GetRuleContext(0); + } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public ViewExpressionsContext viewExpressions() { + return GetRuleContext(0); + } + public ITerminalNode AS() { return GetToken(EsperEPL2GrammarParser.AS, 0); } + public ITerminalNode RETAINUNION() { return GetToken(EsperEPL2GrammarParser.RETAINUNION, 0); } + public ITerminalNode RETAININTERSECTION() { return GetToken(EsperEPL2GrammarParser.RETAININTERSECTION, 0); } + public ITerminalNode INSERT() { return GetToken(EsperEPL2GrammarParser.INSERT, 0); } + public ITerminalNode WHERE() { return GetToken(EsperEPL2GrammarParser.WHERE, 0); } + public ExpressionContext expression() { + return GetRuleContext(0); + } + public CreateWindowExprContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_createWindowExpr; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterCreateWindowExpr(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitCreateWindowExpr(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitCreateWindowExpr(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public CreateWindowExprContext createWindowExpr() { + CreateWindowExprContext _localctx = new CreateWindowExprContext(Context, State); + EnterRule(_localctx, 76, RULE_createWindowExpr); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 934; Match(CREATE); + State = 935; Match(WINDOW); + State = 936; _localctx.i = Match(IDENT); + State = 938; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==DOT || _la==HASHCHAR) { + { + State = 937; viewExpressions(); + } + } + + State = 942; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case RETAINUNION: + { + State = 940; _localctx.ru = Match(RETAINUNION); + } + break; + case RETAININTERSECTION: + { + State = 941; _localctx.ri = Match(RETAININTERSECTION); + } + break; + case AS: + case SELECT: + case EVENTS: + case LPAREN: + case TICKED_STRING_LITERAL: + case IDENT: + break; + default: + throw new NoViableAltException(this); + } + State = 945; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==AS) { + { + State = 944; Match(AS); + } + } + + State = 952; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case SELECT: + case EVENTS: + case TICKED_STRING_LITERAL: + case IDENT: + { + State = 947; createWindowExprModelAfter(); + } + break; + case LPAREN: + { + State = 948; Match(LPAREN); + State = 949; createColumnList(); + State = 950; Match(RPAREN); + } + break; + default: + throw new NoViableAltException(this); + } + State = 959; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==INSERT) { + { + State = 954; _localctx.i1 = Match(INSERT); + State = 957; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==WHERE) { + { + State = 955; Match(WHERE); + State = 956; expression(); + } + } + + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class CreateWindowExprModelAfterContext : ParserRuleContext { + public ClassIdentifierContext classIdentifier() { + return GetRuleContext(0); + } + public ITerminalNode SELECT() { return GetToken(EsperEPL2GrammarParser.SELECT, 0); } + public CreateSelectionListContext createSelectionList() { + return GetRuleContext(0); + } + public ITerminalNode FROM() { return GetToken(EsperEPL2GrammarParser.FROM, 0); } + public CreateWindowExprModelAfterContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_createWindowExprModelAfter; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterCreateWindowExprModelAfter(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitCreateWindowExprModelAfter(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitCreateWindowExprModelAfter(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public CreateWindowExprModelAfterContext createWindowExprModelAfter() { + CreateWindowExprModelAfterContext _localctx = new CreateWindowExprModelAfterContext(Context, State); + EnterRule(_localctx, 78, RULE_createWindowExprModelAfter); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 965; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==SELECT) { + { + State = 961; Match(SELECT); + State = 962; createSelectionList(); + State = 963; Match(FROM); + } + } + + State = 967; classIdentifier(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class CreateIndexExprContext : ParserRuleContext { + public IToken u; + public IToken n; + public IToken w; + public ITerminalNode CREATE() { return GetToken(EsperEPL2GrammarParser.CREATE, 0); } + public ITerminalNode INDEX() { return GetToken(EsperEPL2GrammarParser.INDEX, 0); } + public ITerminalNode ON() { return GetToken(EsperEPL2GrammarParser.ON, 0); } + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public CreateIndexColumnListContext createIndexColumnList() { + return GetRuleContext(0); + } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public ITerminalNode[] IDENT() { return GetTokens(EsperEPL2GrammarParser.IDENT); } + public ITerminalNode IDENT(int i) { + return GetToken(EsperEPL2GrammarParser.IDENT, i); + } + public CreateIndexExprContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_createIndexExpr; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterCreateIndexExpr(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitCreateIndexExpr(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitCreateIndexExpr(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public CreateIndexExprContext createIndexExpr() { + CreateIndexExprContext _localctx = new CreateIndexExprContext(Context, State); + EnterRule(_localctx, 80, RULE_createIndexExpr); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 969; Match(CREATE); + State = 971; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==IDENT) { + { + State = 970; _localctx.u = Match(IDENT); + } + } + + State = 973; Match(INDEX); + State = 974; _localctx.n = Match(IDENT); + State = 975; Match(ON); + State = 976; _localctx.w = Match(IDENT); + State = 977; Match(LPAREN); + State = 978; createIndexColumnList(); + State = 979; Match(RPAREN); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class CreateIndexColumnListContext : ParserRuleContext { + public CreateIndexColumnContext[] createIndexColumn() { + return GetRuleContexts(); + } + public CreateIndexColumnContext createIndexColumn(int i) { + return GetRuleContext(i); + } + public ITerminalNode[] COMMA() { return GetTokens(EsperEPL2GrammarParser.COMMA); } + public ITerminalNode COMMA(int i) { + return GetToken(EsperEPL2GrammarParser.COMMA, i); + } + public CreateIndexColumnListContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_createIndexColumnList; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterCreateIndexColumnList(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitCreateIndexColumnList(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitCreateIndexColumnList(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public CreateIndexColumnListContext createIndexColumnList() { + CreateIndexColumnListContext _localctx = new CreateIndexColumnListContext(Context, State); + EnterRule(_localctx, 82, RULE_createIndexColumnList); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 981; createIndexColumn(); + State = 986; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 982; Match(COMMA); + State = 983; createIndexColumn(); + } + } + State = 988; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class CreateIndexColumnContext : ParserRuleContext { + public IToken c; + public IToken t; + public ITerminalNode[] IDENT() { return GetTokens(EsperEPL2GrammarParser.IDENT); } + public ITerminalNode IDENT(int i) { + return GetToken(EsperEPL2GrammarParser.IDENT, i); + } + public CreateIndexColumnContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_createIndexColumn; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterCreateIndexColumn(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitCreateIndexColumn(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitCreateIndexColumn(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public CreateIndexColumnContext createIndexColumn() { + CreateIndexColumnContext _localctx = new CreateIndexColumnContext(Context, State); + EnterRule(_localctx, 84, RULE_createIndexColumn); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 989; _localctx.c = Match(IDENT); + State = 991; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==IDENT) { + { + State = 990; _localctx.t = Match(IDENT); + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class CreateVariableExprContext : ParserRuleContext { + public IToken c; + public IToken arr; + public IToken p; + public IToken n; + public ITerminalNode CREATE() { return GetToken(EsperEPL2GrammarParser.CREATE, 0); } + public ITerminalNode VARIABLE() { return GetToken(EsperEPL2GrammarParser.VARIABLE, 0); } + public ClassIdentifierContext classIdentifier() { + return GetRuleContext(0); + } + public ITerminalNode[] IDENT() { return GetTokens(EsperEPL2GrammarParser.IDENT); } + public ITerminalNode IDENT(int i) { + return GetToken(EsperEPL2GrammarParser.IDENT, i); + } + public ITerminalNode RBRACK() { return GetToken(EsperEPL2GrammarParser.RBRACK, 0); } + public ITerminalNode EQUALS() { return GetToken(EsperEPL2GrammarParser.EQUALS, 0); } + public ExpressionContext expression() { + return GetRuleContext(0); + } + public ITerminalNode LBRACK() { return GetToken(EsperEPL2GrammarParser.LBRACK, 0); } + public CreateVariableExprContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_createVariableExpr; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterCreateVariableExpr(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitCreateVariableExpr(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitCreateVariableExpr(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public CreateVariableExprContext createVariableExpr() { + CreateVariableExprContext _localctx = new CreateVariableExprContext(Context, State); + EnterRule(_localctx, 86, RULE_createVariableExpr); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 993; Match(CREATE); + State = 995; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==IDENT) { + { + State = 994; _localctx.c = Match(IDENT); + } + } + + State = 997; Match(VARIABLE); + State = 998; classIdentifier(); + State = 1004; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==LBRACK) { + { + State = 999; _localctx.arr = Match(LBRACK); + State = 1001; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==IDENT) { + { + State = 1000; _localctx.p = Match(IDENT); + } + } + + State = 1003; Match(RBRACK); + } + } + + State = 1006; _localctx.n = Match(IDENT); + State = 1009; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==EQUALS) { + { + State = 1007; Match(EQUALS); + State = 1008; expression(); + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class CreateTableExprContext : ParserRuleContext { + public IToken n; + public ITerminalNode CREATE() { return GetToken(EsperEPL2GrammarParser.CREATE, 0); } + public ITerminalNode TABLE() { return GetToken(EsperEPL2GrammarParser.TABLE, 0); } + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public CreateTableColumnListContext createTableColumnList() { + return GetRuleContext(0); + } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public ITerminalNode AS() { return GetToken(EsperEPL2GrammarParser.AS, 0); } + public CreateTableExprContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_createTableExpr; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterCreateTableExpr(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitCreateTableExpr(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitCreateTableExpr(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public CreateTableExprContext createTableExpr() { + CreateTableExprContext _localctx = new CreateTableExprContext(Context, State); + EnterRule(_localctx, 88, RULE_createTableExpr); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1011; Match(CREATE); + State = 1012; Match(TABLE); + State = 1013; _localctx.n = Match(IDENT); + State = 1015; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==AS) { + { + State = 1014; Match(AS); + } + } + + State = 1017; Match(LPAREN); + State = 1018; createTableColumnList(); + State = 1019; Match(RPAREN); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class CreateTableColumnListContext : ParserRuleContext { + public CreateTableColumnContext[] createTableColumn() { + return GetRuleContexts(); + } + public CreateTableColumnContext createTableColumn(int i) { + return GetRuleContext(i); + } + public ITerminalNode[] COMMA() { return GetTokens(EsperEPL2GrammarParser.COMMA); } + public ITerminalNode COMMA(int i) { + return GetToken(EsperEPL2GrammarParser.COMMA, i); + } + public CreateTableColumnListContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_createTableColumnList; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterCreateTableColumnList(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitCreateTableColumnList(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitCreateTableColumnList(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public CreateTableColumnListContext createTableColumnList() { + CreateTableColumnListContext _localctx = new CreateTableColumnListContext(Context, State); + EnterRule(_localctx, 90, RULE_createTableColumnList); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1021; createTableColumn(); + State = 1026; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 1022; Match(COMMA); + State = 1023; createTableColumn(); + } + } + State = 1028; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class CreateTableColumnContext : ParserRuleContext { + public IToken n; + public IToken p; + public IToken k; + public ITerminalNode[] IDENT() { return GetTokens(EsperEPL2GrammarParser.IDENT); } + public ITerminalNode IDENT(int i) { + return GetToken(EsperEPL2GrammarParser.IDENT, i); + } + public CreateTableColumnPlainContext createTableColumnPlain() { + return GetRuleContext(0); + } + public BuiltinFuncContext builtinFunc() { + return GetRuleContext(0); + } + public LibFunctionContext libFunction() { + return GetRuleContext(0); + } + public TypeExpressionAnnotationContext[] typeExpressionAnnotation() { + return GetRuleContexts(); + } + public TypeExpressionAnnotationContext typeExpressionAnnotation(int i) { + return GetRuleContext(i); + } + public AnnotationEnumContext[] annotationEnum() { + return GetRuleContexts(); + } + public AnnotationEnumContext annotationEnum(int i) { + return GetRuleContext(i); + } + public CreateTableColumnContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_createTableColumn; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterCreateTableColumn(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitCreateTableColumn(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitCreateTableColumn(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public CreateTableColumnContext createTableColumn() { + CreateTableColumnContext _localctx = new CreateTableColumnContext(Context, State); + EnterRule(_localctx, 92, RULE_createTableColumn); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1029; _localctx.n = Match(IDENT); + State = 1033; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,93,Context) ) { + case 1: + { + State = 1030; createTableColumnPlain(); + } + break; + case 2: + { + State = 1031; builtinFunc(); + } + break; + case 3: + { + State = 1032; libFunction(); + } + break; + } + State = 1036; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,94,Context) ) { + case 1: + { + State = 1035; _localctx.p = Match(IDENT); + } + break; + } + State = 1039; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==IDENT) { + { + State = 1038; _localctx.k = Match(IDENT); + } + } + + State = 1045; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==ATCHAR) { + { + State = 1043; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,96,Context) ) { + case 1: + { + State = 1041; typeExpressionAnnotation(); + } + break; + case 2: + { + State = 1042; annotationEnum(); + } + break; + } + } + State = 1047; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class CreateTableColumnPlainContext : ParserRuleContext { + public IToken b; + public IToken p; + public ClassIdentifierContext classIdentifier() { + return GetRuleContext(0); + } + public ITerminalNode RBRACK() { return GetToken(EsperEPL2GrammarParser.RBRACK, 0); } + public ITerminalNode LBRACK() { return GetToken(EsperEPL2GrammarParser.LBRACK, 0); } + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public CreateTableColumnPlainContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_createTableColumnPlain; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterCreateTableColumnPlain(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitCreateTableColumnPlain(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitCreateTableColumnPlain(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public CreateTableColumnPlainContext createTableColumnPlain() { + CreateTableColumnPlainContext _localctx = new CreateTableColumnPlainContext(Context, State); + EnterRule(_localctx, 94, RULE_createTableColumnPlain); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1048; classIdentifier(); + State = 1054; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==LBRACK) { + { + State = 1049; _localctx.b = Match(LBRACK); + State = 1051; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==IDENT) { + { + State = 1050; _localctx.p = Match(IDENT); + } + } + + State = 1053; Match(RBRACK); + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class CreateColumnListContext : ParserRuleContext { + public CreateColumnListElementContext[] createColumnListElement() { + return GetRuleContexts(); + } + public CreateColumnListElementContext createColumnListElement(int i) { + return GetRuleContext(i); + } + public ITerminalNode[] COMMA() { return GetTokens(EsperEPL2GrammarParser.COMMA); } + public ITerminalNode COMMA(int i) { + return GetToken(EsperEPL2GrammarParser.COMMA, i); + } + public CreateColumnListContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_createColumnList; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterCreateColumnList(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitCreateColumnList(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitCreateColumnList(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public CreateColumnListContext createColumnList() { + CreateColumnListContext _localctx = new CreateColumnListContext(Context, State); + EnterRule(_localctx, 96, RULE_createColumnList); + paraphrases.Push("column list"); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1056; createColumnListElement(); + State = 1061; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 1057; Match(COMMA); + State = 1058; createColumnListElement(); + } + } + State = 1063; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + Context.Stop = TokenStream.LT(-1); + paraphrases.Pop(); + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class CreateColumnListElementContext : ParserRuleContext { + public IToken b; + public IToken p; + public ClassIdentifierContext[] classIdentifier() { + return GetRuleContexts(); + } + public ClassIdentifierContext classIdentifier(int i) { + return GetRuleContext(i); + } + public ITerminalNode VALUE_NULL() { return GetToken(EsperEPL2GrammarParser.VALUE_NULL, 0); } + public ITerminalNode RBRACK() { return GetToken(EsperEPL2GrammarParser.RBRACK, 0); } + public ITerminalNode LBRACK() { return GetToken(EsperEPL2GrammarParser.LBRACK, 0); } + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public CreateColumnListElementContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_createColumnListElement; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterCreateColumnListElement(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitCreateColumnListElement(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitCreateColumnListElement(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public CreateColumnListElementContext createColumnListElement() { + CreateColumnListElementContext _localctx = new CreateColumnListElementContext(Context, State); + EnterRule(_localctx, 98, RULE_createColumnListElement); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1064; classIdentifier(); + State = 1074; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case VALUE_NULL: + { + State = 1065; Match(VALUE_NULL); + } + break; + case EVENTS: + case TICKED_STRING_LITERAL: + case IDENT: + { + { + State = 1066; classIdentifier(); + State = 1072; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==LBRACK) { + { + State = 1067; _localctx.b = Match(LBRACK); + State = 1069; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==IDENT) { + { + State = 1068; _localctx.p = Match(IDENT); + } + } + + State = 1071; Match(RBRACK); + } + } + + } + } + break; + default: + throw new NoViableAltException(this); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class CreateSelectionListContext : ParserRuleContext { + public CreateSelectionListElementContext[] createSelectionListElement() { + return GetRuleContexts(); + } + public CreateSelectionListElementContext createSelectionListElement(int i) { + return GetRuleContext(i); + } + public ITerminalNode[] COMMA() { return GetTokens(EsperEPL2GrammarParser.COMMA); } + public ITerminalNode COMMA(int i) { + return GetToken(EsperEPL2GrammarParser.COMMA, i); + } + public CreateSelectionListContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_createSelectionList; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterCreateSelectionList(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitCreateSelectionList(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitCreateSelectionList(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public CreateSelectionListContext createSelectionList() { + CreateSelectionListContext _localctx = new CreateSelectionListContext(Context, State); + EnterRule(_localctx, 100, RULE_createSelectionList); + paraphrases.Push("select clause"); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1076; createSelectionListElement(); + State = 1081; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 1077; Match(COMMA); + State = 1078; createSelectionListElement(); + } + } + State = 1083; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + Context.Stop = TokenStream.LT(-1); + paraphrases.Pop(); + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class CreateSelectionListElementContext : ParserRuleContext { + public IToken s; + public IToken i; + public ITerminalNode STAR() { return GetToken(EsperEPL2GrammarParser.STAR, 0); } + public EventPropertyContext eventProperty() { + return GetRuleContext(0); + } + public ITerminalNode AS() { return GetToken(EsperEPL2GrammarParser.AS, 0); } + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public ConstantContext constant() { + return GetRuleContext(0); + } + public CreateSelectionListElementContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_createSelectionListElement; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterCreateSelectionListElement(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitCreateSelectionListElement(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitCreateSelectionListElement(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public CreateSelectionListElementContext createSelectionListElement() { + CreateSelectionListElementContext _localctx = new CreateSelectionListElementContext(Context, State); + EnterRule(_localctx, 102, RULE_createSelectionListElement); + int _la; + try { + State = 1094; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case STAR: + EnterOuterAlt(_localctx, 1); + { + State = 1084; _localctx.s = Match(STAR); + } + break; + case WINDOW: + case ESCAPE: + case EVERY_EXPR: + case SUM: + case AVG: + case MAX: + case MIN: + case COALESCE: + case MEDIAN: + case STDDEV: + case AVEDEV: + case COUNT: + case OUTER: + case JOIN: + case LEFT: + case RIGHT: + case FULL: + case EVENTS: + case FIRST: + case LAST: + case SCHEMA: + case UNIDIRECTIONAL: + case RETAINUNION: + case RETAININTERSECTION: + case PATTERN: + case SQL: + case METADATASQL: + case PREVIOUS: + case PREVIOUSTAIL: + case PRIOR: + case WEEKDAY: + case LW: + case INSTANCEOF: + case TYPEOF: + case CAST: + case SNAPSHOT: + case VARIABLE: + case TABLE: + case UNTIL: + case AT: + case INDEX: + case DEFINE: + case PARTITION: + case MATCHES: + case FOR: + case WHILE: + case USING: + case MERGE: + case MATCHED: + case CONTEXT: + case TICKED_STRING_LITERAL: + case IDENT: + EnterOuterAlt(_localctx, 2); + { + State = 1085; eventProperty(); + State = 1088; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==AS) { + { + State = 1086; Match(AS); + State = 1087; _localctx.i = Match(IDENT); + } + } + + } + break; + case BOOLEAN_TRUE: + case BOOLEAN_FALSE: + case VALUE_NULL: + case PLUS: + case MINUS: + case QUOTED_STRING_LITERAL: + case STRING_LITERAL: + case IntegerLiteral: + case FloatingPointLiteral: + EnterOuterAlt(_localctx, 3); + { + State = 1090; constant(); + State = 1091; Match(AS); + State = 1092; _localctx.i = Match(IDENT); + } + break; + default: + throw new NoViableAltException(this); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class CreateSchemaExprContext : ParserRuleContext { + public IToken keyword; + public ITerminalNode CREATE() { return GetToken(EsperEPL2GrammarParser.CREATE, 0); } + public CreateSchemaDefContext createSchemaDef() { + return GetRuleContext(0); + } + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public CreateSchemaExprContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_createSchemaExpr; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterCreateSchemaExpr(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitCreateSchemaExpr(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitCreateSchemaExpr(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public CreateSchemaExprContext createSchemaExpr() { + CreateSchemaExprContext _localctx = new CreateSchemaExprContext(Context, State); + EnterRule(_localctx, 104, RULE_createSchemaExpr); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1096; Match(CREATE); + State = 1098; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==IDENT) { + { + State = 1097; _localctx.keyword = Match(IDENT); + } + } + + State = 1100; createSchemaDef(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class CreateSchemaDefContext : ParserRuleContext { + public IToken name; + public ITerminalNode SCHEMA() { return GetToken(EsperEPL2GrammarParser.SCHEMA, 0); } + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public VariantListContext variantList() { + return GetRuleContext(0); + } + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public ITerminalNode AS() { return GetToken(EsperEPL2GrammarParser.AS, 0); } + public CreateSchemaQualContext[] createSchemaQual() { + return GetRuleContexts(); + } + public CreateSchemaQualContext createSchemaQual(int i) { + return GetRuleContext(i); + } + public CreateColumnListContext createColumnList() { + return GetRuleContext(0); + } + public CreateSchemaDefContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_createSchemaDef; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterCreateSchemaDef(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitCreateSchemaDef(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitCreateSchemaDef(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public CreateSchemaDefContext createSchemaDef() { + CreateSchemaDefContext _localctx = new CreateSchemaDefContext(Context, State); + EnterRule(_localctx, 106, RULE_createSchemaDef); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1102; Match(SCHEMA); + State = 1103; _localctx.name = Match(IDENT); + State = 1105; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==AS) { + { + State = 1104; Match(AS); + } + } + + State = 1113; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case EVENTS: + case STAR: + case TICKED_STRING_LITERAL: + case IDENT: + { + State = 1107; variantList(); + } + break; + case LPAREN: + { + State = 1108; Match(LPAREN); + State = 1110; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==EVENTS || _la==TICKED_STRING_LITERAL || _la==IDENT) { + { + State = 1109; createColumnList(); + } + } + + State = 1112; Match(RPAREN); + } + break; + default: + throw new NoViableAltException(this); + } + State = 1118; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==IDENT) { + { + { + State = 1115; createSchemaQual(); + } + } + State = 1120; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class FafDeleteContext : ParserRuleContext { + public IToken i; + public ITerminalNode DELETE() { return GetToken(EsperEPL2GrammarParser.DELETE, 0); } + public ITerminalNode FROM() { return GetToken(EsperEPL2GrammarParser.FROM, 0); } + public ClassIdentifierContext classIdentifier() { + return GetRuleContext(0); + } + public ITerminalNode AS() { return GetToken(EsperEPL2GrammarParser.AS, 0); } + public ITerminalNode WHERE() { return GetToken(EsperEPL2GrammarParser.WHERE, 0); } + public WhereClauseContext whereClause() { + return GetRuleContext(0); + } + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public FafDeleteContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_fafDelete; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterFafDelete(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitFafDelete(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitFafDelete(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public FafDeleteContext fafDelete() { + FafDeleteContext _localctx = new FafDeleteContext(Context, State); + EnterRule(_localctx, 108, RULE_fafDelete); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1121; Match(DELETE); + State = 1122; Match(FROM); + State = 1123; classIdentifier(); + State = 1127; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case AS: + { + State = 1124; Match(AS); + State = 1125; _localctx.i = Match(IDENT); + } + break; + case IDENT: + { + State = 1126; _localctx.i = Match(IDENT); + } + break; + case Eof: + case WHERE: + case FOR: + break; + default: + throw new NoViableAltException(this); + } + State = 1131; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==WHERE) { + { + State = 1129; Match(WHERE); + State = 1130; whereClause(); + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class FafUpdateContext : ParserRuleContext { + public ITerminalNode UPDATE() { return GetToken(EsperEPL2GrammarParser.UPDATE, 0); } + public UpdateDetailsContext updateDetails() { + return GetRuleContext(0); + } + public FafUpdateContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_fafUpdate; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterFafUpdate(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitFafUpdate(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitFafUpdate(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public FafUpdateContext fafUpdate() { + FafUpdateContext _localctx = new FafUpdateContext(Context, State); + EnterRule(_localctx, 110, RULE_fafUpdate); + try { + EnterOuterAlt(_localctx, 1); + { + State = 1133; Match(UPDATE); + State = 1134; updateDetails(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class FafInsertContext : ParserRuleContext { + public ITerminalNode INSERT() { return GetToken(EsperEPL2GrammarParser.INSERT, 0); } + public InsertIntoExprContext insertIntoExpr() { + return GetRuleContext(0); + } + public ITerminalNode VALUES() { return GetToken(EsperEPL2GrammarParser.VALUES, 0); } + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public ExpressionListContext expressionList() { + return GetRuleContext(0); + } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public FafInsertContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_fafInsert; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterFafInsert(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitFafInsert(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitFafInsert(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public FafInsertContext fafInsert() { + FafInsertContext _localctx = new FafInsertContext(Context, State); + EnterRule(_localctx, 112, RULE_fafInsert); + try { + EnterOuterAlt(_localctx, 1); + { + State = 1136; Match(INSERT); + State = 1137; insertIntoExpr(); + State = 1138; Match(VALUES); + State = 1139; Match(LPAREN); + State = 1140; expressionList(); + State = 1141; Match(RPAREN); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class CreateDataflowContext : ParserRuleContext { + public IToken name; + public ITerminalNode CREATE() { return GetToken(EsperEPL2GrammarParser.CREATE, 0); } + public ITerminalNode DATAFLOW() { return GetToken(EsperEPL2GrammarParser.DATAFLOW, 0); } + public GopListContext gopList() { + return GetRuleContext(0); + } + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public ITerminalNode AS() { return GetToken(EsperEPL2GrammarParser.AS, 0); } + public CreateDataflowContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_createDataflow; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterCreateDataflow(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitCreateDataflow(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitCreateDataflow(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public CreateDataflowContext createDataflow() { + CreateDataflowContext _localctx = new CreateDataflowContext(Context, State); + EnterRule(_localctx, 114, RULE_createDataflow); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1143; Match(CREATE); + State = 1144; Match(DATAFLOW); + State = 1145; _localctx.name = Match(IDENT); + State = 1147; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==AS) { + { + State = 1146; Match(AS); + } + } + + State = 1149; gopList(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class GopListContext : ParserRuleContext { + public GopContext[] gop() { + return GetRuleContexts(); + } + public GopContext gop(int i) { + return GetRuleContext(i); + } + public GopListContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_gopList; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterGopList(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitGopList(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitGopList(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public GopListContext gopList() { + GopListContext _localctx = new GopListContext(Context, State); + EnterRule(_localctx, 116, RULE_gopList); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1151; gop(); + State = 1155; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==CREATE || _la==SELECT || _la==ATCHAR || _la==IDENT) { + { + { + State = 1152; gop(); + } + } + State = 1157; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class GopContext : ParserRuleContext { + public IToken opName; + public IToken s; + public ITerminalNode LCURLY() { return GetToken(EsperEPL2GrammarParser.LCURLY, 0); } + public ITerminalNode RCURLY() { return GetToken(EsperEPL2GrammarParser.RCURLY, 0); } + public AnnotationEnumContext[] annotationEnum() { + return GetRuleContexts(); + } + public AnnotationEnumContext annotationEnum(int i) { + return GetRuleContext(i); + } + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public ITerminalNode SELECT() { return GetToken(EsperEPL2GrammarParser.SELECT, 0); } + public GopParamsContext gopParams() { + return GetRuleContext(0); + } + public GopOutContext gopOut() { + return GetRuleContext(0); + } + public GopDetailContext gopDetail() { + return GetRuleContext(0); + } + public ITerminalNode COMMA() { return GetToken(EsperEPL2GrammarParser.COMMA, 0); } + public CreateSchemaExprContext createSchemaExpr() { + return GetRuleContext(0); + } + public GopContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_gop; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterGop(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitGop(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitGop(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public GopContext gop() { + GopContext _localctx = new GopContext(Context, State); + EnterRule(_localctx, 118, RULE_gop); + int _la; + try { + State = 1185; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case SELECT: + case ATCHAR: + case IDENT: + EnterOuterAlt(_localctx, 1); + { + State = 1161; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==ATCHAR) { + { + { + State = 1158; annotationEnum(); + } + } + State = 1163; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + State = 1166; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case IDENT: + { + State = 1164; _localctx.opName = Match(IDENT); + } + break; + case SELECT: + { + State = 1165; _localctx.s = Match(SELECT); + } + break; + default: + throw new NoViableAltException(this); + } + State = 1169; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==LPAREN) { + { + State = 1168; gopParams(); + } + } + + State = 1172; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==FOLLOWED_BY) { + { + State = 1171; gopOut(); + } + } + + State = 1174; Match(LCURLY); + State = 1176; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==SELECT || _la==IDENT) { + { + State = 1175; gopDetail(); + } + } + + State = 1179; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==COMMA) { + { + State = 1178; Match(COMMA); + } + } + + State = 1181; Match(RCURLY); + } + break; + case CREATE: + EnterOuterAlt(_localctx, 2); + { + State = 1182; createSchemaExpr(); + State = 1183; Match(COMMA); + } + break; + default: + throw new NoViableAltException(this); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class GopParamsContext : ParserRuleContext { + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public GopParamsItemListContext gopParamsItemList() { + return GetRuleContext(0); + } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public GopParamsContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_gopParams; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterGopParams(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitGopParams(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitGopParams(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public GopParamsContext gopParams() { + GopParamsContext _localctx = new GopParamsContext(Context, State); + EnterRule(_localctx, 120, RULE_gopParams); + try { + EnterOuterAlt(_localctx, 1); + { + State = 1187; Match(LPAREN); + State = 1188; gopParamsItemList(); + State = 1189; Match(RPAREN); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class GopParamsItemListContext : ParserRuleContext { + public GopParamsItemContext[] gopParamsItem() { + return GetRuleContexts(); + } + public GopParamsItemContext gopParamsItem(int i) { + return GetRuleContext(i); + } + public ITerminalNode[] COMMA() { return GetTokens(EsperEPL2GrammarParser.COMMA); } + public ITerminalNode COMMA(int i) { + return GetToken(EsperEPL2GrammarParser.COMMA, i); + } + public GopParamsItemListContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_gopParamsItemList; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterGopParamsItemList(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitGopParamsItemList(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitGopParamsItemList(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public GopParamsItemListContext gopParamsItemList() { + GopParamsItemListContext _localctx = new GopParamsItemListContext(Context, State); + EnterRule(_localctx, 122, RULE_gopParamsItemList); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1191; gopParamsItem(); + State = 1196; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 1192; Match(COMMA); + State = 1193; gopParamsItem(); + } + } + State = 1198; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class GopParamsItemContext : ParserRuleContext { + public ClassIdentifierContext n; + public GopParamsItemManyContext gopParamsItemMany() { + return GetRuleContext(0); + } + public ClassIdentifierContext classIdentifier() { + return GetRuleContext(0); + } + public GopParamsItemAsContext gopParamsItemAs() { + return GetRuleContext(0); + } + public GopParamsItemContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_gopParamsItem; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterGopParamsItem(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitGopParamsItem(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitGopParamsItem(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public GopParamsItemContext gopParamsItem() { + GopParamsItemContext _localctx = new GopParamsItemContext(Context, State); + EnterRule(_localctx, 124, RULE_gopParamsItem); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1201; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case EVENTS: + case TICKED_STRING_LITERAL: + case IDENT: + { + State = 1199; _localctx.n = classIdentifier(); + } + break; + case LPAREN: + { + State = 1200; gopParamsItemMany(); + } + break; + default: + throw new NoViableAltException(this); + } + State = 1204; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==AS) { + { + State = 1203; gopParamsItemAs(); + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class GopParamsItemManyContext : ParserRuleContext { + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public ClassIdentifierContext[] classIdentifier() { + return GetRuleContexts(); + } + public ClassIdentifierContext classIdentifier(int i) { + return GetRuleContext(i); + } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public ITerminalNode COMMA() { return GetToken(EsperEPL2GrammarParser.COMMA, 0); } + public GopParamsItemManyContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_gopParamsItemMany; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterGopParamsItemMany(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitGopParamsItemMany(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitGopParamsItemMany(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public GopParamsItemManyContext gopParamsItemMany() { + GopParamsItemManyContext _localctx = new GopParamsItemManyContext(Context, State); + EnterRule(_localctx, 126, RULE_gopParamsItemMany); + try { + EnterOuterAlt(_localctx, 1); + { + State = 1206; Match(LPAREN); + State = 1207; classIdentifier(); + { + State = 1208; Match(COMMA); + State = 1209; classIdentifier(); + } + State = 1211; Match(RPAREN); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class GopParamsItemAsContext : ParserRuleContext { + public IToken a; + public ITerminalNode AS() { return GetToken(EsperEPL2GrammarParser.AS, 0); } + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public GopParamsItemAsContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_gopParamsItemAs; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterGopParamsItemAs(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitGopParamsItemAs(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitGopParamsItemAs(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public GopParamsItemAsContext gopParamsItemAs() { + GopParamsItemAsContext _localctx = new GopParamsItemAsContext(Context, State); + EnterRule(_localctx, 128, RULE_gopParamsItemAs); + try { + EnterOuterAlt(_localctx, 1); + { + State = 1213; Match(AS); + State = 1214; _localctx.a = Match(IDENT); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class GopOutContext : ParserRuleContext { + public ITerminalNode FOLLOWED_BY() { return GetToken(EsperEPL2GrammarParser.FOLLOWED_BY, 0); } + public GopOutItemContext[] gopOutItem() { + return GetRuleContexts(); + } + public GopOutItemContext gopOutItem(int i) { + return GetRuleContext(i); + } + public ITerminalNode[] COMMA() { return GetTokens(EsperEPL2GrammarParser.COMMA); } + public ITerminalNode COMMA(int i) { + return GetToken(EsperEPL2GrammarParser.COMMA, i); + } + public GopOutContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_gopOut; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterGopOut(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitGopOut(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitGopOut(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public GopOutContext gopOut() { + GopOutContext _localctx = new GopOutContext(Context, State); + EnterRule(_localctx, 130, RULE_gopOut); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1216; Match(FOLLOWED_BY); + State = 1217; gopOutItem(); + State = 1222; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 1218; Match(COMMA); + State = 1219; gopOutItem(); + } + } + State = 1224; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class GopOutItemContext : ParserRuleContext { + public ClassIdentifierContext n; + public ClassIdentifierContext classIdentifier() { + return GetRuleContext(0); + } + public GopOutTypeListContext gopOutTypeList() { + return GetRuleContext(0); + } + public GopOutItemContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_gopOutItem; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterGopOutItem(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitGopOutItem(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitGopOutItem(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public GopOutItemContext gopOutItem() { + GopOutItemContext _localctx = new GopOutItemContext(Context, State); + EnterRule(_localctx, 132, RULE_gopOutItem); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1225; _localctx.n = classIdentifier(); + State = 1227; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==LT) { + { + State = 1226; gopOutTypeList(); + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class GopOutTypeListContext : ParserRuleContext { + public ITerminalNode LT() { return GetToken(EsperEPL2GrammarParser.LT, 0); } + public GopOutTypeParamContext[] gopOutTypeParam() { + return GetRuleContexts(); + } + public GopOutTypeParamContext gopOutTypeParam(int i) { + return GetRuleContext(i); + } + public ITerminalNode GT() { return GetToken(EsperEPL2GrammarParser.GT, 0); } + public ITerminalNode[] COMMA() { return GetTokens(EsperEPL2GrammarParser.COMMA); } + public ITerminalNode COMMA(int i) { + return GetToken(EsperEPL2GrammarParser.COMMA, i); + } + public GopOutTypeListContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_gopOutTypeList; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterGopOutTypeList(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitGopOutTypeList(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitGopOutTypeList(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public GopOutTypeListContext gopOutTypeList() { + GopOutTypeListContext _localctx = new GopOutTypeListContext(Context, State); + EnterRule(_localctx, 134, RULE_gopOutTypeList); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1229; Match(LT); + State = 1230; gopOutTypeParam(); + State = 1235; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 1231; Match(COMMA); + State = 1232; gopOutTypeParam(); + } + } + State = 1237; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + State = 1238; Match(GT); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class GopOutTypeParamContext : ParserRuleContext { + public IToken q; + public GopOutTypeItemContext gopOutTypeItem() { + return GetRuleContext(0); + } + public ITerminalNode QUESTION() { return GetToken(EsperEPL2GrammarParser.QUESTION, 0); } + public GopOutTypeParamContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_gopOutTypeParam; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterGopOutTypeParam(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitGopOutTypeParam(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitGopOutTypeParam(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public GopOutTypeParamContext gopOutTypeParam() { + GopOutTypeParamContext _localctx = new GopOutTypeParamContext(Context, State); + EnterRule(_localctx, 136, RULE_gopOutTypeParam); + try { + EnterOuterAlt(_localctx, 1); + { + State = 1242; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case EVENTS: + case TICKED_STRING_LITERAL: + case IDENT: + { + State = 1240; gopOutTypeItem(); + } + break; + case QUESTION: + { + State = 1241; _localctx.q = Match(QUESTION); + } + break; + default: + throw new NoViableAltException(this); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class GopOutTypeItemContext : ParserRuleContext { + public ClassIdentifierContext classIdentifier() { + return GetRuleContext(0); + } + public GopOutTypeListContext gopOutTypeList() { + return GetRuleContext(0); + } + public GopOutTypeItemContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_gopOutTypeItem; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterGopOutTypeItem(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitGopOutTypeItem(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitGopOutTypeItem(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public GopOutTypeItemContext gopOutTypeItem() { + GopOutTypeItemContext _localctx = new GopOutTypeItemContext(Context, State); + EnterRule(_localctx, 138, RULE_gopOutTypeItem); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1244; classIdentifier(); + State = 1246; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==LT) { + { + State = 1245; gopOutTypeList(); + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class GopDetailContext : ParserRuleContext { + public GopConfigContext[] gopConfig() { + return GetRuleContexts(); + } + public GopConfigContext gopConfig(int i) { + return GetRuleContext(i); + } + public ITerminalNode[] COMMA() { return GetTokens(EsperEPL2GrammarParser.COMMA); } + public ITerminalNode COMMA(int i) { + return GetToken(EsperEPL2GrammarParser.COMMA, i); + } + public GopDetailContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_gopDetail; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterGopDetail(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitGopDetail(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitGopDetail(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public GopDetailContext gopDetail() { + GopDetailContext _localctx = new GopDetailContext(Context, State); + EnterRule(_localctx, 140, RULE_gopDetail); + try { + int _alt; + EnterOuterAlt(_localctx, 1); + { + State = 1248; gopConfig(); + State = 1253; + ErrorHandler.Sync(this); + _alt = Interpreter.AdaptivePredict(TokenStream,131,Context); + while ( _alt!=2 && _alt!=global::Antlr4.Runtime.Atn.ATN.INVALID_ALT_NUMBER ) { + if ( _alt==1 ) { + { + { + State = 1249; Match(COMMA); + State = 1250; gopConfig(); + } + } + } + State = 1255; + ErrorHandler.Sync(this); + _alt = Interpreter.AdaptivePredict(TokenStream,131,Context); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class GopConfigContext : ParserRuleContext { + public IToken n; + public ITerminalNode SELECT() { return GetToken(EsperEPL2GrammarParser.SELECT, 0); } + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public SelectExprContext selectExpr() { + return GetRuleContext(0); + } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public ITerminalNode COLON() { return GetToken(EsperEPL2GrammarParser.COLON, 0); } + public ITerminalNode EQUALS() { return GetToken(EsperEPL2GrammarParser.EQUALS, 0); } + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public ExpressionContext expression() { + return GetRuleContext(0); + } + public JsonobjectContext jsonobject() { + return GetRuleContext(0); + } + public JsonarrayContext jsonarray() { + return GetRuleContext(0); + } + public GopConfigContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_gopConfig; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterGopConfig(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitGopConfig(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitGopConfig(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public GopConfigContext gopConfig() { + GopConfigContext _localctx = new GopConfigContext(Context, State); + EnterRule(_localctx, 142, RULE_gopConfig); + int _la; + try { + State = 1269; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case SELECT: + EnterOuterAlt(_localctx, 1); + { + State = 1256; Match(SELECT); + State = 1257; + _la = TokenStream.LA(1); + if ( !(_la==EQUALS || _la==COLON) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + State = 1258; Match(LPAREN); + State = 1259; selectExpr(); + State = 1260; Match(RPAREN); + } + break; + case IDENT: + EnterOuterAlt(_localctx, 2); + { + State = 1262; _localctx.n = Match(IDENT); + State = 1263; + _la = TokenStream.LA(1); + if ( !(_la==EQUALS || _la==COLON) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + State = 1267; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,132,Context) ) { + case 1: + { + State = 1264; expression(); + } + break; + case 2: + { + State = 1265; jsonobject(); + } + break; + case 3: + { + State = 1266; jsonarray(); + } + break; + } + } + break; + default: + throw new NoViableAltException(this); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class CreateContextExprContext : ParserRuleContext { + public IToken name; + public ITerminalNode CREATE() { return GetToken(EsperEPL2GrammarParser.CREATE, 0); } + public ITerminalNode CONTEXT() { return GetToken(EsperEPL2GrammarParser.CONTEXT, 0); } + public CreateContextDetailContext createContextDetail() { + return GetRuleContext(0); + } + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public ITerminalNode AS() { return GetToken(EsperEPL2GrammarParser.AS, 0); } + public CreateContextExprContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_createContextExpr; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterCreateContextExpr(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitCreateContextExpr(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitCreateContextExpr(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public CreateContextExprContext createContextExpr() { + CreateContextExprContext _localctx = new CreateContextExprContext(Context, State); + EnterRule(_localctx, 144, RULE_createContextExpr); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1271; Match(CREATE); + State = 1272; Match(CONTEXT); + State = 1273; _localctx.name = Match(IDENT); + State = 1275; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==AS) { + { + State = 1274; Match(AS); + } + } + + State = 1277; createContextDetail(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class CreateExpressionExprContext : ParserRuleContext { + public ITerminalNode CREATE() { return GetToken(EsperEPL2GrammarParser.CREATE, 0); } + public ExpressionDeclContext expressionDecl() { + return GetRuleContext(0); + } + public CreateExpressionExprContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_createExpressionExpr; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterCreateExpressionExpr(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitCreateExpressionExpr(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitCreateExpressionExpr(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public CreateExpressionExprContext createExpressionExpr() { + CreateExpressionExprContext _localctx = new CreateExpressionExprContext(Context, State); + EnterRule(_localctx, 146, RULE_createExpressionExpr); + try { + EnterOuterAlt(_localctx, 1); + { + State = 1279; Match(CREATE); + State = 1280; expressionDecl(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class CreateContextDetailContext : ParserRuleContext { + public CreateContextChoiceContext createContextChoice() { + return GetRuleContext(0); + } + public ContextContextNestedContext[] contextContextNested() { + return GetRuleContexts(); + } + public ContextContextNestedContext contextContextNested(int i) { + return GetRuleContext(i); + } + public ITerminalNode[] COMMA() { return GetTokens(EsperEPL2GrammarParser.COMMA); } + public ITerminalNode COMMA(int i) { + return GetToken(EsperEPL2GrammarParser.COMMA, i); + } + public CreateContextDetailContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_createContextDetail; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterCreateContextDetail(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitCreateContextDetail(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitCreateContextDetail(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public CreateContextDetailContext createContextDetail() { + CreateContextDetailContext _localctx = new CreateContextDetailContext(Context, State); + EnterRule(_localctx, 148, RULE_createContextDetail); + int _la; + try { + State = 1293; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case COALESCE: + case GROUP: + case PARTITION: + case START: + case INITIATED: + EnterOuterAlt(_localctx, 1); + { + State = 1282; createContextChoice(); + } + break; + case CONTEXT: + EnterOuterAlt(_localctx, 2); + { + State = 1283; contextContextNested(); + State = 1284; Match(COMMA); + State = 1285; contextContextNested(); + State = 1290; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 1286; Match(COMMA); + State = 1287; contextContextNested(); + } + } + State = 1292; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + break; + default: + throw new NoViableAltException(this); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class ContextContextNestedContext : ParserRuleContext { + public IToken name; + public ITerminalNode CONTEXT() { return GetToken(EsperEPL2GrammarParser.CONTEXT, 0); } + public CreateContextChoiceContext createContextChoice() { + return GetRuleContext(0); + } + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public ITerminalNode AS() { return GetToken(EsperEPL2GrammarParser.AS, 0); } + public ContextContextNestedContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_contextContextNested; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterContextContextNested(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitContextContextNested(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitContextContextNested(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public ContextContextNestedContext contextContextNested() { + ContextContextNestedContext _localctx = new ContextContextNestedContext(Context, State); + EnterRule(_localctx, 150, RULE_contextContextNested); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1295; Match(CONTEXT); + State = 1296; _localctx.name = Match(IDENT); + State = 1298; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==AS) { + { + State = 1297; Match(AS); + } + } + + State = 1300; createContextChoice(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class CreateContextChoiceContext : ParserRuleContext { + public IToken i; + public CreateContextRangePointContext r1; + public CreateContextRangePointContext r2; + public IToken g; + public IToken p; + public ITerminalNode START() { return GetToken(EsperEPL2GrammarParser.START, 0); } + public ITerminalNode ATCHAR() { return GetToken(EsperEPL2GrammarParser.ATCHAR, 0); } + public ITerminalNode[] IDENT() { return GetTokens(EsperEPL2GrammarParser.IDENT); } + public ITerminalNode IDENT(int i) { + return GetToken(EsperEPL2GrammarParser.IDENT, i); + } + public CreateContextRangePointContext[] createContextRangePoint() { + return GetRuleContexts(); + } + public CreateContextRangePointContext createContextRangePoint(int i) { + return GetRuleContext(i); + } + public ITerminalNode END() { return GetToken(EsperEPL2GrammarParser.END, 0); } + public ITerminalNode INITIATED() { return GetToken(EsperEPL2GrammarParser.INITIATED, 0); } + public ITerminalNode[] BY() { return GetTokens(EsperEPL2GrammarParser.BY); } + public ITerminalNode BY(int i) { + return GetToken(EsperEPL2GrammarParser.BY, i); + } + public CreateContextDistinctContext createContextDistinct() { + return GetRuleContext(0); + } + public ITerminalNode AND_EXPR() { return GetToken(EsperEPL2GrammarParser.AND_EXPR, 0); } + public ITerminalNode TERMINATED() { return GetToken(EsperEPL2GrammarParser.TERMINATED, 0); } + public ITerminalNode PARTITION() { return GetToken(EsperEPL2GrammarParser.PARTITION, 0); } + public CreateContextPartitionItemContext[] createContextPartitionItem() { + return GetRuleContexts(); + } + public CreateContextPartitionItemContext createContextPartitionItem(int i) { + return GetRuleContext(i); + } + public ITerminalNode[] COMMA() { return GetTokens(EsperEPL2GrammarParser.COMMA); } + public ITerminalNode COMMA(int i) { + return GetToken(EsperEPL2GrammarParser.COMMA, i); + } + public CreateContextGroupItemContext[] createContextGroupItem() { + return GetRuleContexts(); + } + public CreateContextGroupItemContext createContextGroupItem(int i) { + return GetRuleContext(i); + } + public ITerminalNode FROM() { return GetToken(EsperEPL2GrammarParser.FROM, 0); } + public EventFilterExpressionContext eventFilterExpression() { + return GetRuleContext(0); + } + public ITerminalNode COALESCE() { return GetToken(EsperEPL2GrammarParser.COALESCE, 0); } + public CreateContextCoalesceItemContext[] createContextCoalesceItem() { + return GetRuleContexts(); + } + public CreateContextCoalesceItemContext createContextCoalesceItem(int i) { + return GetRuleContext(i); + } + public NumberContext number() { + return GetRuleContext(0); + } + public CreateContextChoiceContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_createContextChoice; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterCreateContextChoice(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitCreateContextChoice(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitCreateContextChoice(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public CreateContextChoiceContext createContextChoice() { + CreateContextChoiceContext _localctx = new CreateContextChoiceContext(Context, State); + EnterRule(_localctx, 152, RULE_createContextChoice); + int _la; + try { + int _alt; + State = 1372; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case START: + EnterOuterAlt(_localctx, 1); + { + State = 1302; Match(START); + State = 1306; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case ATCHAR: + { + State = 1303; Match(ATCHAR); + State = 1304; _localctx.i = Match(IDENT); + } + break; + case EVENTS: + case PATTERN: + case AFTER: + case LPAREN: + case TICKED_STRING_LITERAL: + case IDENT: + { + State = 1305; _localctx.r1 = createContextRangePoint(); + } + break; + default: + throw new NoViableAltException(this); + } + State = 1310; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==END) { + { + State = 1308; Match(END); + State = 1309; _localctx.r2 = createContextRangePoint(); + } + } + + } + break; + case INITIATED: + EnterOuterAlt(_localctx, 2); + { + State = 1312; Match(INITIATED); + State = 1314; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==BY) { + { + State = 1313; Match(BY); + } + } + + State = 1317; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==DISTINCT) { + { + State = 1316; createContextDistinct(); + } + } + + State = 1322; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==ATCHAR) { + { + State = 1319; Match(ATCHAR); + State = 1320; _localctx.i = Match(IDENT); + State = 1321; Match(AND_EXPR); + } + } + + State = 1324; _localctx.r1 = createContextRangePoint(); + State = 1330; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==TERMINATED) { + { + State = 1325; Match(TERMINATED); + State = 1327; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==BY) { + { + State = 1326; Match(BY); + } + } + + State = 1329; _localctx.r2 = createContextRangePoint(); + } + } + + } + break; + case PARTITION: + EnterOuterAlt(_localctx, 3); + { + State = 1332; Match(PARTITION); + State = 1334; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==BY) { + { + State = 1333; Match(BY); + } + } + + State = 1336; createContextPartitionItem(); + State = 1341; + ErrorHandler.Sync(this); + _alt = Interpreter.AdaptivePredict(TokenStream,146,Context); + while ( _alt!=2 && _alt!=global::Antlr4.Runtime.Atn.ATN.INVALID_ALT_NUMBER ) { + if ( _alt==1 ) { + { + { + State = 1337; Match(COMMA); + State = 1338; createContextPartitionItem(); + } + } + } + State = 1343; + ErrorHandler.Sync(this); + _alt = Interpreter.AdaptivePredict(TokenStream,146,Context); + } + } + break; + case GROUP: + EnterOuterAlt(_localctx, 4); + { + State = 1344; createContextGroupItem(); + State = 1349; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 1345; Match(COMMA); + State = 1346; createContextGroupItem(); + } + } + State = 1351; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + State = 1352; Match(FROM); + State = 1353; eventFilterExpression(); + } + break; + case COALESCE: + EnterOuterAlt(_localctx, 5); + { + State = 1355; Match(COALESCE); + State = 1357; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==BY) { + { + State = 1356; Match(BY); + } + } + + State = 1359; createContextCoalesceItem(); + State = 1364; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 1360; Match(COMMA); + State = 1361; createContextCoalesceItem(); + } + } + State = 1366; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + State = 1367; _localctx.g = Match(IDENT); + State = 1368; number(); + State = 1370; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==IDENT) { + { + State = 1369; _localctx.p = Match(IDENT); + } + } + + } + break; + default: + throw new NoViableAltException(this); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class CreateContextDistinctContext : ParserRuleContext { + public ITerminalNode DISTINCT() { return GetToken(EsperEPL2GrammarParser.DISTINCT, 0); } + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public ExpressionListContext expressionList() { + return GetRuleContext(0); + } + public CreateContextDistinctContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_createContextDistinct; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterCreateContextDistinct(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitCreateContextDistinct(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitCreateContextDistinct(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public CreateContextDistinctContext createContextDistinct() { + CreateContextDistinctContext _localctx = new CreateContextDistinctContext(Context, State); + EnterRule(_localctx, 154, RULE_createContextDistinct); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1374; Match(DISTINCT); + State = 1375; Match(LPAREN); + State = 1377; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (((((_la - 2)) & ~0x3f) == 0 && ((1L << (_la - 2)) & ((1L << (WINDOW - 2)) | (1L << (ESCAPE - 2)) | (1L << (NOT_EXPR - 2)) | (1L << (EVERY_EXPR - 2)) | (1L << (SUM - 2)) | (1L << (AVG - 2)) | (1L << (MAX - 2)) | (1L << (MIN - 2)) | (1L << (COALESCE - 2)) | (1L << (MEDIAN - 2)) | (1L << (STDDEV - 2)) | (1L << (AVEDEV - 2)) | (1L << (COUNT - 2)) | (1L << (CASE - 2)) | (1L << (OUTER - 2)) | (1L << (JOIN - 2)) | (1L << (LEFT - 2)) | (1L << (RIGHT - 2)) | (1L << (FULL - 2)) | (1L << (EVENTS - 2)) | (1L << (FIRST - 2)) | (1L << (LAST - 2)) | (1L << (ISTREAM - 2)) | (1L << (SCHEMA - 2)) | (1L << (UNIDIRECTIONAL - 2)) | (1L << (RETAINUNION - 2)) | (1L << (RETAININTERSECTION - 2)) | (1L << (PATTERN - 2)) | (1L << (SQL - 2)) | (1L << (METADATASQL - 2)))) != 0) || ((((_la - 66)) & ~0x3f) == 0 && ((1L << (_la - 66)) & ((1L << (PREVIOUS - 66)) | (1L << (PREVIOUSTAIL - 66)) | (1L << (PREVIOUSCOUNT - 66)) | (1L << (PREVIOUSWINDOW - 66)) | (1L << (PRIOR - 66)) | (1L << (EXISTS - 66)) | (1L << (WEEKDAY - 66)) | (1L << (LW - 66)) | (1L << (INSTANCEOF - 66)) | (1L << (TYPEOF - 66)) | (1L << (CAST - 66)) | (1L << (CURRENT_TIMESTAMP - 66)) | (1L << (SNAPSHOT - 66)) | (1L << (VARIABLE - 66)) | (1L << (TABLE - 66)) | (1L << (UNTIL - 66)) | (1L << (AT - 66)) | (1L << (INDEX - 66)) | (1L << (BOOLEAN_TRUE - 66)) | (1L << (BOOLEAN_FALSE - 66)) | (1L << (VALUE_NULL - 66)) | (1L << (DEFINE - 66)) | (1L << (PARTITION - 66)) | (1L << (MATCHES - 66)) | (1L << (FOR - 66)) | (1L << (WHILE - 66)) | (1L << (USING - 66)) | (1L << (MERGE - 66)) | (1L << (MATCHED - 66)) | (1L << (NEWKW - 66)) | (1L << (CONTEXT - 66)))) != 0) || ((((_la - 134)) & ~0x3f) == 0 && ((1L << (_la - 134)) & ((1L << (GROUPING - 134)) | (1L << (GROUPING_ID - 134)) | (1L << (QUESTION - 134)) | (1L << (LPAREN - 134)) | (1L << (LCURLY - 134)) | (1L << (PLUS - 134)) | (1L << (MINUS - 134)) | (1L << (TICKED_STRING_LITERAL - 134)) | (1L << (QUOTED_STRING_LITERAL - 134)) | (1L << (STRING_LITERAL - 134)) | (1L << (IDENT - 134)) | (1L << (IntegerLiteral - 134)) | (1L << (FloatingPointLiteral - 134)))) != 0)) { + { + State = 1376; expressionList(); + } + } + + State = 1379; Match(RPAREN); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class CreateContextRangePointContext : ParserRuleContext { + public IToken i; + public CreateContextFilterContext createContextFilter() { + return GetRuleContext(0); + } + public PatternInclusionExpressionContext patternInclusionExpression() { + return GetRuleContext(0); + } + public ITerminalNode ATCHAR() { return GetToken(EsperEPL2GrammarParser.ATCHAR, 0); } + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public CrontabLimitParameterSetContext crontabLimitParameterSet() { + return GetRuleContext(0); + } + public ITerminalNode AFTER() { return GetToken(EsperEPL2GrammarParser.AFTER, 0); } + public TimePeriodContext timePeriod() { + return GetRuleContext(0); + } + public CreateContextRangePointContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_createContextRangePoint; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterCreateContextRangePoint(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitCreateContextRangePoint(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitCreateContextRangePoint(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public CreateContextRangePointContext createContextRangePoint() { + CreateContextRangePointContext _localctx = new CreateContextRangePointContext(Context, State); + EnterRule(_localctx, 156, RULE_createContextRangePoint); + int _la; + try { + State = 1390; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case EVENTS: + case TICKED_STRING_LITERAL: + case IDENT: + EnterOuterAlt(_localctx, 1); + { + State = 1381; createContextFilter(); + } + break; + case PATTERN: + EnterOuterAlt(_localctx, 2); + { + State = 1382; patternInclusionExpression(); + State = 1385; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==ATCHAR) { + { + State = 1383; Match(ATCHAR); + State = 1384; _localctx.i = Match(IDENT); + } + } + + } + break; + case LPAREN: + EnterOuterAlt(_localctx, 3); + { + State = 1387; crontabLimitParameterSet(); + } + break; + case AFTER: + EnterOuterAlt(_localctx, 4); + { + State = 1388; Match(AFTER); + State = 1389; timePeriod(); + } + break; + default: + throw new NoViableAltException(this); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class CreateContextFilterContext : ParserRuleContext { + public IToken i; + public EventFilterExpressionContext eventFilterExpression() { + return GetRuleContext(0); + } + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public ITerminalNode AS() { return GetToken(EsperEPL2GrammarParser.AS, 0); } + public CreateContextFilterContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_createContextFilter; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterCreateContextFilter(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitCreateContextFilter(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitCreateContextFilter(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public CreateContextFilterContext createContextFilter() { + CreateContextFilterContext _localctx = new CreateContextFilterContext(Context, State); + EnterRule(_localctx, 158, RULE_createContextFilter); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1392; eventFilterExpression(); + State = 1397; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==AS || _la==IDENT) { + { + State = 1394; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==AS) { + { + State = 1393; Match(AS); + } + } + + State = 1396; _localctx.i = Match(IDENT); + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class CreateContextPartitionItemContext : ParserRuleContext { + public EventPropertyContext[] eventProperty() { + return GetRuleContexts(); + } + public EventPropertyContext eventProperty(int i) { + return GetRuleContext(i); + } + public ITerminalNode FROM() { return GetToken(EsperEPL2GrammarParser.FROM, 0); } + public EventFilterExpressionContext eventFilterExpression() { + return GetRuleContext(0); + } + public ITerminalNode[] AND_EXPR() { return GetTokens(EsperEPL2GrammarParser.AND_EXPR); } + public ITerminalNode AND_EXPR(int i) { + return GetToken(EsperEPL2GrammarParser.AND_EXPR, i); + } + public ITerminalNode[] COMMA() { return GetTokens(EsperEPL2GrammarParser.COMMA); } + public ITerminalNode COMMA(int i) { + return GetToken(EsperEPL2GrammarParser.COMMA, i); + } + public CreateContextPartitionItemContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_createContextPartitionItem; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterCreateContextPartitionItem(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitCreateContextPartitionItem(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitCreateContextPartitionItem(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public CreateContextPartitionItemContext createContextPartitionItem() { + CreateContextPartitionItemContext _localctx = new CreateContextPartitionItemContext(Context, State); + EnterRule(_localctx, 160, RULE_createContextPartitionItem); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1399; eventProperty(); + State = 1404; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==AND_EXPR || _la==COMMA) { + { + { + State = 1400; + _la = TokenStream.LA(1); + if ( !(_la==AND_EXPR || _la==COMMA) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + State = 1401; eventProperty(); + } + } + State = 1406; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + State = 1407; Match(FROM); + State = 1408; eventFilterExpression(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class CreateContextCoalesceItemContext : ParserRuleContext { + public LibFunctionNoClassContext libFunctionNoClass() { + return GetRuleContext(0); + } + public ITerminalNode FROM() { return GetToken(EsperEPL2GrammarParser.FROM, 0); } + public EventFilterExpressionContext eventFilterExpression() { + return GetRuleContext(0); + } + public CreateContextCoalesceItemContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_createContextCoalesceItem; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterCreateContextCoalesceItem(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitCreateContextCoalesceItem(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitCreateContextCoalesceItem(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public CreateContextCoalesceItemContext createContextCoalesceItem() { + CreateContextCoalesceItemContext _localctx = new CreateContextCoalesceItemContext(Context, State); + EnterRule(_localctx, 162, RULE_createContextCoalesceItem); + try { + EnterOuterAlt(_localctx, 1); + { + State = 1410; libFunctionNoClass(); + State = 1411; Match(FROM); + State = 1412; eventFilterExpression(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class CreateContextGroupItemContext : ParserRuleContext { + public IToken i; + public ITerminalNode GROUP() { return GetToken(EsperEPL2GrammarParser.GROUP, 0); } + public ExpressionContext expression() { + return GetRuleContext(0); + } + public ITerminalNode AS() { return GetToken(EsperEPL2GrammarParser.AS, 0); } + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public ITerminalNode BY() { return GetToken(EsperEPL2GrammarParser.BY, 0); } + public CreateContextGroupItemContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_createContextGroupItem; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterCreateContextGroupItem(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitCreateContextGroupItem(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitCreateContextGroupItem(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public CreateContextGroupItemContext createContextGroupItem() { + CreateContextGroupItemContext _localctx = new CreateContextGroupItemContext(Context, State); + EnterRule(_localctx, 164, RULE_createContextGroupItem); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1414; Match(GROUP); + State = 1416; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==BY) { + { + State = 1415; Match(BY); + } + } + + State = 1418; expression(); + State = 1419; Match(AS); + State = 1420; _localctx.i = Match(IDENT); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class CreateSchemaQualContext : ParserRuleContext { + public IToken i; + public ColumnListContext columnList() { + return GetRuleContext(0); + } + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public CreateSchemaQualContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_createSchemaQual; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterCreateSchemaQual(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitCreateSchemaQual(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitCreateSchemaQual(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public CreateSchemaQualContext createSchemaQual() { + CreateSchemaQualContext _localctx = new CreateSchemaQualContext(Context, State); + EnterRule(_localctx, 166, RULE_createSchemaQual); + try { + EnterOuterAlt(_localctx, 1); + { + State = 1422; _localctx.i = Match(IDENT); + State = 1423; columnList(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class VariantListContext : ParserRuleContext { + public VariantListElementContext[] variantListElement() { + return GetRuleContexts(); + } + public VariantListElementContext variantListElement(int i) { + return GetRuleContext(i); + } + public ITerminalNode[] COMMA() { return GetTokens(EsperEPL2GrammarParser.COMMA); } + public ITerminalNode COMMA(int i) { + return GetToken(EsperEPL2GrammarParser.COMMA, i); + } + public VariantListContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_variantList; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterVariantList(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitVariantList(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitVariantList(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public VariantListContext variantList() { + VariantListContext _localctx = new VariantListContext(Context, State); + EnterRule(_localctx, 168, RULE_variantList); + try { + int _alt; + EnterOuterAlt(_localctx, 1); + { + State = 1425; variantListElement(); + State = 1430; + ErrorHandler.Sync(this); + _alt = Interpreter.AdaptivePredict(TokenStream,159,Context); + while ( _alt!=2 && _alt!=global::Antlr4.Runtime.Atn.ATN.INVALID_ALT_NUMBER ) { + if ( _alt==1 ) { + { + { + State = 1426; Match(COMMA); + State = 1427; variantListElement(); + } + } + } + State = 1432; + ErrorHandler.Sync(this); + _alt = Interpreter.AdaptivePredict(TokenStream,159,Context); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class VariantListElementContext : ParserRuleContext { + public ITerminalNode STAR() { return GetToken(EsperEPL2GrammarParser.STAR, 0); } + public ClassIdentifierContext classIdentifier() { + return GetRuleContext(0); + } + public VariantListElementContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_variantListElement; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterVariantListElement(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitVariantListElement(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitVariantListElement(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public VariantListElementContext variantListElement() { + VariantListElementContext _localctx = new VariantListElementContext(Context, State); + EnterRule(_localctx, 170, RULE_variantListElement); + try { + State = 1435; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case STAR: + EnterOuterAlt(_localctx, 1); + { + State = 1433; Match(STAR); + } + break; + case EVENTS: + case TICKED_STRING_LITERAL: + case IDENT: + EnterOuterAlt(_localctx, 2); + { + State = 1434; classIdentifier(); + } + break; + default: + throw new NoViableAltException(this); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class IntoTableExprContext : ParserRuleContext { + public IToken i; + public ITerminalNode TABLE() { return GetToken(EsperEPL2GrammarParser.TABLE, 0); } + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public IntoTableExprContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_intoTableExpr; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterIntoTableExpr(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitIntoTableExpr(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitIntoTableExpr(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public IntoTableExprContext intoTableExpr() { + IntoTableExprContext _localctx = new IntoTableExprContext(Context, State); + EnterRule(_localctx, 172, RULE_intoTableExpr); + paraphrases.Push("into-table clause"); + try { + EnterOuterAlt(_localctx, 1); + { + State = 1437; Match(TABLE); + State = 1438; _localctx.i = Match(IDENT); + } + Context.Stop = TokenStream.LT(-1); + paraphrases.Pop(); + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class InsertIntoExprContext : ParserRuleContext { + public IToken i; + public IToken r; + public IToken ir; + public ITerminalNode INTO() { return GetToken(EsperEPL2GrammarParser.INTO, 0); } + public ClassIdentifierContext classIdentifier() { + return GetRuleContext(0); + } + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public ITerminalNode ISTREAM() { return GetToken(EsperEPL2GrammarParser.ISTREAM, 0); } + public ITerminalNode RSTREAM() { return GetToken(EsperEPL2GrammarParser.RSTREAM, 0); } + public ITerminalNode IRSTREAM() { return GetToken(EsperEPL2GrammarParser.IRSTREAM, 0); } + public ColumnListContext columnList() { + return GetRuleContext(0); + } + public InsertIntoExprContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_insertIntoExpr; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterInsertIntoExpr(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitInsertIntoExpr(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitInsertIntoExpr(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public InsertIntoExprContext insertIntoExpr() { + InsertIntoExprContext _localctx = new InsertIntoExprContext(Context, State); + EnterRule(_localctx, 174, RULE_insertIntoExpr); + paraphrases.Push("insert-into clause"); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1443; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case ISTREAM: + { + State = 1440; _localctx.i = Match(ISTREAM); + } + break; + case RSTREAM: + { + State = 1441; _localctx.r = Match(RSTREAM); + } + break; + case IRSTREAM: + { + State = 1442; _localctx.ir = Match(IRSTREAM); + } + break; + case INTO: + break; + default: + throw new NoViableAltException(this); + } + State = 1445; Match(INTO); + State = 1446; classIdentifier(); + State = 1452; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==LPAREN) { + { + State = 1447; Match(LPAREN); + State = 1449; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==IDENT) { + { + State = 1448; columnList(); + } + } + + State = 1451; Match(RPAREN); + } + } + + } + Context.Stop = TokenStream.LT(-1); + paraphrases.Pop(); + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class ColumnListContext : ParserRuleContext { + public ITerminalNode[] IDENT() { return GetTokens(EsperEPL2GrammarParser.IDENT); } + public ITerminalNode IDENT(int i) { + return GetToken(EsperEPL2GrammarParser.IDENT, i); + } + public ITerminalNode[] COMMA() { return GetTokens(EsperEPL2GrammarParser.COMMA); } + public ITerminalNode COMMA(int i) { + return GetToken(EsperEPL2GrammarParser.COMMA, i); + } + public ColumnListContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_columnList; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterColumnList(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitColumnList(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitColumnList(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public ColumnListContext columnList() { + ColumnListContext _localctx = new ColumnListContext(Context, State); + EnterRule(_localctx, 176, RULE_columnList); + try { + int _alt; + EnterOuterAlt(_localctx, 1); + { + State = 1454; Match(IDENT); + State = 1459; + ErrorHandler.Sync(this); + _alt = Interpreter.AdaptivePredict(TokenStream,164,Context); + while ( _alt!=2 && _alt!=global::Antlr4.Runtime.Atn.ATN.INVALID_ALT_NUMBER ) { + if ( _alt==1 ) { + { + { + State = 1455; Match(COMMA); + State = 1456; Match(IDENT); + } + } + } + State = 1461; + ErrorHandler.Sync(this); + _alt = Interpreter.AdaptivePredict(TokenStream,164,Context); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class FromClauseContext : ParserRuleContext { + public StreamExpressionContext streamExpression() { + return GetRuleContext(0); + } + public RegularJoinContext regularJoin() { + return GetRuleContext(0); + } + public OuterJoinListContext outerJoinList() { + return GetRuleContext(0); + } + public FromClauseContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_fromClause; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterFromClause(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitFromClause(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitFromClause(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public FromClauseContext fromClause() { + FromClauseContext _localctx = new FromClauseContext(Context, State); + EnterRule(_localctx, 178, RULE_fromClause); + paraphrases.Push("from clause"); + try { + EnterOuterAlt(_localctx, 1); + { + State = 1462; streamExpression(); + State = 1465; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case Eof: + case WHERE: + case GROUP: + case HAVING: + case OUTPUT: + case ORDER: + case ROW_LIMIT_EXPR: + case MATCH_RECOGNIZE: + case FOR: + case RPAREN: + case COMMA: + { + State = 1463; regularJoin(); + } + break; + case INNER: + case JOIN: + case LEFT: + case RIGHT: + case FULL: + { + State = 1464; outerJoinList(); + } + break; + default: + throw new NoViableAltException(this); + } + } + Context.Stop = TokenStream.LT(-1); + paraphrases.Pop(); + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class RegularJoinContext : ParserRuleContext { + public ITerminalNode[] COMMA() { return GetTokens(EsperEPL2GrammarParser.COMMA); } + public ITerminalNode COMMA(int i) { + return GetToken(EsperEPL2GrammarParser.COMMA, i); + } + public StreamExpressionContext[] streamExpression() { + return GetRuleContexts(); + } + public StreamExpressionContext streamExpression(int i) { + return GetRuleContext(i); + } + public RegularJoinContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_regularJoin; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterRegularJoin(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitRegularJoin(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitRegularJoin(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public RegularJoinContext regularJoin() { + RegularJoinContext _localctx = new RegularJoinContext(Context, State); + EnterRule(_localctx, 180, RULE_regularJoin); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1471; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 1467; Match(COMMA); + State = 1468; streamExpression(); + } + } + State = 1473; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class OuterJoinListContext : ParserRuleContext { + public OuterJoinContext[] outerJoin() { + return GetRuleContexts(); + } + public OuterJoinContext outerJoin(int i) { + return GetRuleContext(i); + } + public OuterJoinListContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_outerJoinList; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterOuterJoinList(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitOuterJoinList(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitOuterJoinList(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public OuterJoinListContext outerJoinList() { + OuterJoinListContext _localctx = new OuterJoinListContext(Context, State); + EnterRule(_localctx, 182, RULE_outerJoinList); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1474; outerJoin(); + State = 1478; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while ((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << INNER) | (1L << JOIN) | (1L << LEFT) | (1L << RIGHT) | (1L << FULL))) != 0)) { + { + { + State = 1475; outerJoin(); + } + } + State = 1480; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class OuterJoinContext : ParserRuleContext { + public IToken tl; + public IToken tr; + public IToken tf; + public IToken i; + public ITerminalNode JOIN() { return GetToken(EsperEPL2GrammarParser.JOIN, 0); } + public StreamExpressionContext streamExpression() { + return GetRuleContext(0); + } + public OuterJoinIdentContext outerJoinIdent() { + return GetRuleContext(0); + } + public ITerminalNode OUTER() { return GetToken(EsperEPL2GrammarParser.OUTER, 0); } + public ITerminalNode INNER() { return GetToken(EsperEPL2GrammarParser.INNER, 0); } + public ITerminalNode LEFT() { return GetToken(EsperEPL2GrammarParser.LEFT, 0); } + public ITerminalNode RIGHT() { return GetToken(EsperEPL2GrammarParser.RIGHT, 0); } + public ITerminalNode FULL() { return GetToken(EsperEPL2GrammarParser.FULL, 0); } + public OuterJoinContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_outerJoin; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterOuterJoin(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitOuterJoin(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitOuterJoin(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public OuterJoinContext outerJoin() { + OuterJoinContext _localctx = new OuterJoinContext(Context, State); + EnterRule(_localctx, 184, RULE_outerJoin); + paraphrases.Push("outer join"); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1490; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case JOIN: + case LEFT: + case RIGHT: + case FULL: + { + State = 1487; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if ((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << LEFT) | (1L << RIGHT) | (1L << FULL))) != 0)) { + { + State = 1484; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case LEFT: + { + State = 1481; _localctx.tl = Match(LEFT); + } + break; + case RIGHT: + { + State = 1482; _localctx.tr = Match(RIGHT); + } + break; + case FULL: + { + State = 1483; _localctx.tf = Match(FULL); + } + break; + default: + throw new NoViableAltException(this); + } + State = 1486; Match(OUTER); + } + } + + } + break; + case INNER: + { + { + State = 1489; _localctx.i = Match(INNER); + } + } + break; + default: + throw new NoViableAltException(this); + } + State = 1492; Match(JOIN); + State = 1493; streamExpression(); + State = 1495; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==ON) { + { + State = 1494; outerJoinIdent(); + } + } + + } + Context.Stop = TokenStream.LT(-1); + paraphrases.Pop(); + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class OuterJoinIdentContext : ParserRuleContext { + public ITerminalNode ON() { return GetToken(EsperEPL2GrammarParser.ON, 0); } + public OuterJoinIdentPairContext[] outerJoinIdentPair() { + return GetRuleContexts(); + } + public OuterJoinIdentPairContext outerJoinIdentPair(int i) { + return GetRuleContext(i); + } + public ITerminalNode[] AND_EXPR() { return GetTokens(EsperEPL2GrammarParser.AND_EXPR); } + public ITerminalNode AND_EXPR(int i) { + return GetToken(EsperEPL2GrammarParser.AND_EXPR, i); + } + public OuterJoinIdentContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_outerJoinIdent; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterOuterJoinIdent(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitOuterJoinIdent(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitOuterJoinIdent(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public OuterJoinIdentContext outerJoinIdent() { + OuterJoinIdentContext _localctx = new OuterJoinIdentContext(Context, State); + EnterRule(_localctx, 186, RULE_outerJoinIdent); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1497; Match(ON); + State = 1498; outerJoinIdentPair(); + State = 1503; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==AND_EXPR) { + { + { + State = 1499; Match(AND_EXPR); + State = 1500; outerJoinIdentPair(); + } + } + State = 1505; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class OuterJoinIdentPairContext : ParserRuleContext { + public EventPropertyContext[] eventProperty() { + return GetRuleContexts(); + } + public EventPropertyContext eventProperty(int i) { + return GetRuleContext(i); + } + public ITerminalNode EQUALS() { return GetToken(EsperEPL2GrammarParser.EQUALS, 0); } + public OuterJoinIdentPairContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_outerJoinIdentPair; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterOuterJoinIdentPair(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitOuterJoinIdentPair(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitOuterJoinIdentPair(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public OuterJoinIdentPairContext outerJoinIdentPair() { + OuterJoinIdentPairContext _localctx = new OuterJoinIdentPairContext(Context, State); + EnterRule(_localctx, 188, RULE_outerJoinIdentPair); + try { + EnterOuterAlt(_localctx, 1); + { + State = 1506; eventProperty(); + State = 1507; Match(EQUALS); + State = 1508; eventProperty(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class WhereClauseContext : ParserRuleContext { + public EvalOrExpressionContext evalOrExpression() { + return GetRuleContext(0); + } + public WhereClauseContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_whereClause; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterWhereClause(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitWhereClause(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitWhereClause(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public WhereClauseContext whereClause() { + WhereClauseContext _localctx = new WhereClauseContext(Context, State); + EnterRule(_localctx, 190, RULE_whereClause); + paraphrases.Push("where clause"); + try { + EnterOuterAlt(_localctx, 1); + { + State = 1510; evalOrExpression(); + } + Context.Stop = TokenStream.LT(-1); + paraphrases.Pop(); + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class SelectClauseContext : ParserRuleContext { + public IToken s; + public IToken d; + public SelectionListContext selectionList() { + return GetRuleContext(0); + } + public ITerminalNode RSTREAM() { return GetToken(EsperEPL2GrammarParser.RSTREAM, 0); } + public ITerminalNode ISTREAM() { return GetToken(EsperEPL2GrammarParser.ISTREAM, 0); } + public ITerminalNode IRSTREAM() { return GetToken(EsperEPL2GrammarParser.IRSTREAM, 0); } + public ITerminalNode DISTINCT() { return GetToken(EsperEPL2GrammarParser.DISTINCT, 0); } + public SelectClauseContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_selectClause; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterSelectClause(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitSelectClause(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitSelectClause(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public SelectClauseContext selectClause() { + SelectClauseContext _localctx = new SelectClauseContext(Context, State); + EnterRule(_localctx, 192, RULE_selectClause); + paraphrases.Push("select clause"); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1515; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,173,Context) ) { + case 1: + { + State = 1512; _localctx.s = Match(RSTREAM); + } + break; + case 2: + { + State = 1513; _localctx.s = Match(ISTREAM); + } + break; + case 3: + { + State = 1514; _localctx.s = Match(IRSTREAM); + } + break; + } + State = 1518; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==DISTINCT) { + { + State = 1517; _localctx.d = Match(DISTINCT); + } + } + + State = 1520; selectionList(); + } + Context.Stop = TokenStream.LT(-1); + paraphrases.Pop(); + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class SelectionListContext : ParserRuleContext { + public SelectionListElementContext[] selectionListElement() { + return GetRuleContexts(); + } + public SelectionListElementContext selectionListElement(int i) { + return GetRuleContext(i); + } + public ITerminalNode[] COMMA() { return GetTokens(EsperEPL2GrammarParser.COMMA); } + public ITerminalNode COMMA(int i) { + return GetToken(EsperEPL2GrammarParser.COMMA, i); + } + public SelectionListContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_selectionList; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterSelectionList(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitSelectionList(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitSelectionList(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public SelectionListContext selectionList() { + SelectionListContext _localctx = new SelectionListContext(Context, State); + EnterRule(_localctx, 194, RULE_selectionList); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1522; selectionListElement(); + State = 1527; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 1523; Match(COMMA); + State = 1524; selectionListElement(); + } + } + State = 1529; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class SelectionListElementContext : ParserRuleContext { + public IToken s; + public ITerminalNode STAR() { return GetToken(EsperEPL2GrammarParser.STAR, 0); } + public StreamSelectorContext streamSelector() { + return GetRuleContext(0); + } + public SelectionListElementExprContext selectionListElementExpr() { + return GetRuleContext(0); + } + public SelectionListElementContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_selectionListElement; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterSelectionListElement(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitSelectionListElement(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitSelectionListElement(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public SelectionListElementContext selectionListElement() { + SelectionListElementContext _localctx = new SelectionListElementContext(Context, State); + EnterRule(_localctx, 196, RULE_selectionListElement); + try { + State = 1533; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,176,Context) ) { + case 1: + EnterOuterAlt(_localctx, 1); + { + State = 1530; _localctx.s = Match(STAR); + } + break; + case 2: + EnterOuterAlt(_localctx, 2); + { + State = 1531; streamSelector(); + } + break; + case 3: + EnterOuterAlt(_localctx, 3); + { + State = 1532; selectionListElementExpr(); + } + break; + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class SelectionListElementExprContext : ParserRuleContext { + public ExpressionContext expression() { + return GetRuleContext(0); + } + public SelectionListElementAnnoContext selectionListElementAnno() { + return GetRuleContext(0); + } + public KeywordAllowedIdentContext keywordAllowedIdent() { + return GetRuleContext(0); + } + public ITerminalNode AS() { return GetToken(EsperEPL2GrammarParser.AS, 0); } + public SelectionListElementExprContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_selectionListElementExpr; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterSelectionListElementExpr(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitSelectionListElementExpr(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitSelectionListElementExpr(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public SelectionListElementExprContext selectionListElementExpr() { + SelectionListElementExprContext _localctx = new SelectionListElementExprContext(Context, State); + EnterRule(_localctx, 198, RULE_selectionListElementExpr); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1535; expression(); + State = 1537; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==ATCHAR) { + { + State = 1536; selectionListElementAnno(); + } + } + + State = 1543; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,179,Context) ) { + case 1: + { + State = 1540; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==AS) { + { + State = 1539; Match(AS); + } + } + + State = 1542; keywordAllowedIdent(); + } + break; + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class SelectionListElementAnnoContext : ParserRuleContext { + public IToken i; + public ITerminalNode ATCHAR() { return GetToken(EsperEPL2GrammarParser.ATCHAR, 0); } + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public SelectionListElementAnnoContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_selectionListElementAnno; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterSelectionListElementAnno(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitSelectionListElementAnno(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitSelectionListElementAnno(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public SelectionListElementAnnoContext selectionListElementAnno() { + SelectionListElementAnnoContext _localctx = new SelectionListElementAnnoContext(Context, State); + EnterRule(_localctx, 200, RULE_selectionListElementAnno); + try { + EnterOuterAlt(_localctx, 1); + { + State = 1545; Match(ATCHAR); + State = 1546; _localctx.i = Match(IDENT); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class StreamSelectorContext : ParserRuleContext { + public IToken s; + public IToken i; + public ITerminalNode DOT() { return GetToken(EsperEPL2GrammarParser.DOT, 0); } + public ITerminalNode STAR() { return GetToken(EsperEPL2GrammarParser.STAR, 0); } + public ITerminalNode[] IDENT() { return GetTokens(EsperEPL2GrammarParser.IDENT); } + public ITerminalNode IDENT(int i) { + return GetToken(EsperEPL2GrammarParser.IDENT, i); + } + public ITerminalNode AS() { return GetToken(EsperEPL2GrammarParser.AS, 0); } + public StreamSelectorContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_streamSelector; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterStreamSelector(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitStreamSelector(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitStreamSelector(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public StreamSelectorContext streamSelector() { + StreamSelectorContext _localctx = new StreamSelectorContext(Context, State); + EnterRule(_localctx, 202, RULE_streamSelector); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1548; _localctx.s = Match(IDENT); + State = 1549; Match(DOT); + State = 1550; Match(STAR); + State = 1553; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==AS) { + { + State = 1551; Match(AS); + State = 1552; _localctx.i = Match(IDENT); + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class StreamExpressionContext : ParserRuleContext { + public IToken i; + public IToken u; + public IToken ru; + public IToken ri; + public EventFilterExpressionContext eventFilterExpression() { + return GetRuleContext(0); + } + public PatternInclusionExpressionContext patternInclusionExpression() { + return GetRuleContext(0); + } + public DatabaseJoinExpressionContext databaseJoinExpression() { + return GetRuleContext(0); + } + public MethodJoinExpressionContext methodJoinExpression() { + return GetRuleContext(0); + } + public ViewExpressionsContext viewExpressions() { + return GetRuleContext(0); + } + public ITerminalNode AS() { return GetToken(EsperEPL2GrammarParser.AS, 0); } + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public ITerminalNode UNIDIRECTIONAL() { return GetToken(EsperEPL2GrammarParser.UNIDIRECTIONAL, 0); } + public ITerminalNode RETAINUNION() { return GetToken(EsperEPL2GrammarParser.RETAINUNION, 0); } + public ITerminalNode RETAININTERSECTION() { return GetToken(EsperEPL2GrammarParser.RETAININTERSECTION, 0); } + public StreamExpressionContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_streamExpression; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterStreamExpression(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitStreamExpression(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitStreamExpression(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public StreamExpressionContext streamExpression() { + StreamExpressionContext _localctx = new StreamExpressionContext(Context, State); + EnterRule(_localctx, 204, RULE_streamExpression); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1559; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,181,Context) ) { + case 1: + { + State = 1555; eventFilterExpression(); + } + break; + case 2: + { + State = 1556; patternInclusionExpression(); + } + break; + case 3: + { + State = 1557; databaseJoinExpression(); + } + break; + case 4: + { + State = 1558; methodJoinExpression(); + } + break; + } + State = 1562; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==DOT || _la==HASHCHAR) { + { + State = 1561; viewExpressions(); + } + } + + State = 1567; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case AS: + { + State = 1564; Match(AS); + State = 1565; _localctx.i = Match(IDENT); + } + break; + case IDENT: + { + State = 1566; _localctx.i = Match(IDENT); + } + break; + case Eof: + case WHERE: + case INNER: + case JOIN: + case LEFT: + case RIGHT: + case FULL: + case ON: + case GROUP: + case HAVING: + case OUTPUT: + case ORDER: + case UNIDIRECTIONAL: + case RETAINUNION: + case RETAININTERSECTION: + case ROW_LIMIT_EXPR: + case MATCH_RECOGNIZE: + case FOR: + case RPAREN: + case COMMA: + break; + default: + throw new NoViableAltException(this); + } + State = 1570; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==UNIDIRECTIONAL) { + { + State = 1569; _localctx.u = Match(UNIDIRECTIONAL); + } + } + + State = 1574; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case RETAINUNION: + { + State = 1572; _localctx.ru = Match(RETAINUNION); + } + break; + case RETAININTERSECTION: + { + State = 1573; _localctx.ri = Match(RETAININTERSECTION); + } + break; + case Eof: + case WHERE: + case INNER: + case JOIN: + case LEFT: + case RIGHT: + case FULL: + case ON: + case GROUP: + case HAVING: + case OUTPUT: + case ORDER: + case ROW_LIMIT_EXPR: + case MATCH_RECOGNIZE: + case FOR: + case RPAREN: + case COMMA: + break; + default: + throw new NoViableAltException(this); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class ForExprContext : ParserRuleContext { + public IToken i; + public ITerminalNode FOR() { return GetToken(EsperEPL2GrammarParser.FOR, 0); } + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public ExpressionListContext expressionList() { + return GetRuleContext(0); + } + public ForExprContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_forExpr; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterForExpr(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitForExpr(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitForExpr(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public ForExprContext forExpr() { + ForExprContext _localctx = new ForExprContext(Context, State); + EnterRule(_localctx, 206, RULE_forExpr); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1576; Match(FOR); + State = 1577; _localctx.i = Match(IDENT); + State = 1583; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==LPAREN) { + { + State = 1578; Match(LPAREN); + State = 1580; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (((((_la - 2)) & ~0x3f) == 0 && ((1L << (_la - 2)) & ((1L << (WINDOW - 2)) | (1L << (ESCAPE - 2)) | (1L << (NOT_EXPR - 2)) | (1L << (EVERY_EXPR - 2)) | (1L << (SUM - 2)) | (1L << (AVG - 2)) | (1L << (MAX - 2)) | (1L << (MIN - 2)) | (1L << (COALESCE - 2)) | (1L << (MEDIAN - 2)) | (1L << (STDDEV - 2)) | (1L << (AVEDEV - 2)) | (1L << (COUNT - 2)) | (1L << (CASE - 2)) | (1L << (OUTER - 2)) | (1L << (JOIN - 2)) | (1L << (LEFT - 2)) | (1L << (RIGHT - 2)) | (1L << (FULL - 2)) | (1L << (EVENTS - 2)) | (1L << (FIRST - 2)) | (1L << (LAST - 2)) | (1L << (ISTREAM - 2)) | (1L << (SCHEMA - 2)) | (1L << (UNIDIRECTIONAL - 2)) | (1L << (RETAINUNION - 2)) | (1L << (RETAININTERSECTION - 2)) | (1L << (PATTERN - 2)) | (1L << (SQL - 2)) | (1L << (METADATASQL - 2)))) != 0) || ((((_la - 66)) & ~0x3f) == 0 && ((1L << (_la - 66)) & ((1L << (PREVIOUS - 66)) | (1L << (PREVIOUSTAIL - 66)) | (1L << (PREVIOUSCOUNT - 66)) | (1L << (PREVIOUSWINDOW - 66)) | (1L << (PRIOR - 66)) | (1L << (EXISTS - 66)) | (1L << (WEEKDAY - 66)) | (1L << (LW - 66)) | (1L << (INSTANCEOF - 66)) | (1L << (TYPEOF - 66)) | (1L << (CAST - 66)) | (1L << (CURRENT_TIMESTAMP - 66)) | (1L << (SNAPSHOT - 66)) | (1L << (VARIABLE - 66)) | (1L << (TABLE - 66)) | (1L << (UNTIL - 66)) | (1L << (AT - 66)) | (1L << (INDEX - 66)) | (1L << (BOOLEAN_TRUE - 66)) | (1L << (BOOLEAN_FALSE - 66)) | (1L << (VALUE_NULL - 66)) | (1L << (DEFINE - 66)) | (1L << (PARTITION - 66)) | (1L << (MATCHES - 66)) | (1L << (FOR - 66)) | (1L << (WHILE - 66)) | (1L << (USING - 66)) | (1L << (MERGE - 66)) | (1L << (MATCHED - 66)) | (1L << (NEWKW - 66)) | (1L << (CONTEXT - 66)))) != 0) || ((((_la - 134)) & ~0x3f) == 0 && ((1L << (_la - 134)) & ((1L << (GROUPING - 134)) | (1L << (GROUPING_ID - 134)) | (1L << (QUESTION - 134)) | (1L << (LPAREN - 134)) | (1L << (LCURLY - 134)) | (1L << (PLUS - 134)) | (1L << (MINUS - 134)) | (1L << (TICKED_STRING_LITERAL - 134)) | (1L << (QUOTED_STRING_LITERAL - 134)) | (1L << (STRING_LITERAL - 134)) | (1L << (IDENT - 134)) | (1L << (IntegerLiteral - 134)) | (1L << (FloatingPointLiteral - 134)))) != 0)) { + { + State = 1579; expressionList(); + } + } + + State = 1582; Match(RPAREN); + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class PatternInclusionExpressionContext : ParserRuleContext { + public ITerminalNode PATTERN() { return GetToken(EsperEPL2GrammarParser.PATTERN, 0); } + public ITerminalNode LBRACK() { return GetToken(EsperEPL2GrammarParser.LBRACK, 0); } + public PatternExpressionContext patternExpression() { + return GetRuleContext(0); + } + public ITerminalNode RBRACK() { return GetToken(EsperEPL2GrammarParser.RBRACK, 0); } + public AnnotationEnumContext[] annotationEnum() { + return GetRuleContexts(); + } + public AnnotationEnumContext annotationEnum(int i) { + return GetRuleContext(i); + } + public PatternInclusionExpressionContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_patternInclusionExpression; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterPatternInclusionExpression(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitPatternInclusionExpression(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitPatternInclusionExpression(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public PatternInclusionExpressionContext patternInclusionExpression() { + PatternInclusionExpressionContext _localctx = new PatternInclusionExpressionContext(Context, State); + EnterRule(_localctx, 208, RULE_patternInclusionExpression); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1585; Match(PATTERN); + State = 1589; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==ATCHAR) { + { + { + State = 1586; annotationEnum(); + } + } + State = 1591; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + State = 1592; Match(LBRACK); + State = 1593; patternExpression(); + State = 1594; Match(RBRACK); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class DatabaseJoinExpressionContext : ParserRuleContext { + public IToken i; + public IToken s; + public IToken s2; + public ITerminalNode SQL() { return GetToken(EsperEPL2GrammarParser.SQL, 0); } + public ITerminalNode COLON() { return GetToken(EsperEPL2GrammarParser.COLON, 0); } + public ITerminalNode LBRACK() { return GetToken(EsperEPL2GrammarParser.LBRACK, 0); } + public ITerminalNode RBRACK() { return GetToken(EsperEPL2GrammarParser.RBRACK, 0); } + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public ITerminalNode[] STRING_LITERAL() { return GetTokens(EsperEPL2GrammarParser.STRING_LITERAL); } + public ITerminalNode STRING_LITERAL(int i) { + return GetToken(EsperEPL2GrammarParser.STRING_LITERAL, i); + } + public ITerminalNode[] QUOTED_STRING_LITERAL() { return GetTokens(EsperEPL2GrammarParser.QUOTED_STRING_LITERAL); } + public ITerminalNode QUOTED_STRING_LITERAL(int i) { + return GetToken(EsperEPL2GrammarParser.QUOTED_STRING_LITERAL, i); + } + public ITerminalNode METADATASQL() { return GetToken(EsperEPL2GrammarParser.METADATASQL, 0); } + public DatabaseJoinExpressionContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_databaseJoinExpression; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterDatabaseJoinExpression(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitDatabaseJoinExpression(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitDatabaseJoinExpression(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public DatabaseJoinExpressionContext databaseJoinExpression() { + DatabaseJoinExpressionContext _localctx = new DatabaseJoinExpressionContext(Context, State); + EnterRule(_localctx, 210, RULE_databaseJoinExpression); + paraphrases.Push("relational data join"); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1596; Match(SQL); + State = 1597; Match(COLON); + State = 1598; _localctx.i = Match(IDENT); + State = 1599; Match(LBRACK); + State = 1602; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case STRING_LITERAL: + { + State = 1600; _localctx.s = Match(STRING_LITERAL); + } + break; + case QUOTED_STRING_LITERAL: + { + State = 1601; _localctx.s = Match(QUOTED_STRING_LITERAL); + } + break; + default: + throw new NoViableAltException(this); + } + State = 1609; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==METADATASQL) { + { + State = 1604; Match(METADATASQL); + State = 1607; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case STRING_LITERAL: + { + State = 1605; _localctx.s2 = Match(STRING_LITERAL); + } + break; + case QUOTED_STRING_LITERAL: + { + State = 1606; _localctx.s2 = Match(QUOTED_STRING_LITERAL); + } + break; + default: + throw new NoViableAltException(this); + } + } + } + + State = 1611; Match(RBRACK); + } + Context.Stop = TokenStream.LT(-1); + paraphrases.Pop(); + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class MethodJoinExpressionContext : ParserRuleContext { + public IToken i; + public ITerminalNode COLON() { return GetToken(EsperEPL2GrammarParser.COLON, 0); } + public ClassIdentifierContext classIdentifier() { + return GetRuleContext(0); + } + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public TypeExpressionAnnotationContext typeExpressionAnnotation() { + return GetRuleContext(0); + } + public ExpressionListContext expressionList() { + return GetRuleContext(0); + } + public MethodJoinExpressionContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_methodJoinExpression; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterMethodJoinExpression(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitMethodJoinExpression(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitMethodJoinExpression(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public MethodJoinExpressionContext methodJoinExpression() { + MethodJoinExpressionContext _localctx = new MethodJoinExpressionContext(Context, State); + EnterRule(_localctx, 212, RULE_methodJoinExpression); + paraphrases.Push("method invocation join"); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1613; _localctx.i = Match(IDENT); + State = 1614; Match(COLON); + State = 1615; classIdentifier(); + State = 1621; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==LPAREN) { + { + State = 1616; Match(LPAREN); + State = 1618; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (((((_la - 2)) & ~0x3f) == 0 && ((1L << (_la - 2)) & ((1L << (WINDOW - 2)) | (1L << (ESCAPE - 2)) | (1L << (NOT_EXPR - 2)) | (1L << (EVERY_EXPR - 2)) | (1L << (SUM - 2)) | (1L << (AVG - 2)) | (1L << (MAX - 2)) | (1L << (MIN - 2)) | (1L << (COALESCE - 2)) | (1L << (MEDIAN - 2)) | (1L << (STDDEV - 2)) | (1L << (AVEDEV - 2)) | (1L << (COUNT - 2)) | (1L << (CASE - 2)) | (1L << (OUTER - 2)) | (1L << (JOIN - 2)) | (1L << (LEFT - 2)) | (1L << (RIGHT - 2)) | (1L << (FULL - 2)) | (1L << (EVENTS - 2)) | (1L << (FIRST - 2)) | (1L << (LAST - 2)) | (1L << (ISTREAM - 2)) | (1L << (SCHEMA - 2)) | (1L << (UNIDIRECTIONAL - 2)) | (1L << (RETAINUNION - 2)) | (1L << (RETAININTERSECTION - 2)) | (1L << (PATTERN - 2)) | (1L << (SQL - 2)) | (1L << (METADATASQL - 2)))) != 0) || ((((_la - 66)) & ~0x3f) == 0 && ((1L << (_la - 66)) & ((1L << (PREVIOUS - 66)) | (1L << (PREVIOUSTAIL - 66)) | (1L << (PREVIOUSCOUNT - 66)) | (1L << (PREVIOUSWINDOW - 66)) | (1L << (PRIOR - 66)) | (1L << (EXISTS - 66)) | (1L << (WEEKDAY - 66)) | (1L << (LW - 66)) | (1L << (INSTANCEOF - 66)) | (1L << (TYPEOF - 66)) | (1L << (CAST - 66)) | (1L << (CURRENT_TIMESTAMP - 66)) | (1L << (SNAPSHOT - 66)) | (1L << (VARIABLE - 66)) | (1L << (TABLE - 66)) | (1L << (UNTIL - 66)) | (1L << (AT - 66)) | (1L << (INDEX - 66)) | (1L << (BOOLEAN_TRUE - 66)) | (1L << (BOOLEAN_FALSE - 66)) | (1L << (VALUE_NULL - 66)) | (1L << (DEFINE - 66)) | (1L << (PARTITION - 66)) | (1L << (MATCHES - 66)) | (1L << (FOR - 66)) | (1L << (WHILE - 66)) | (1L << (USING - 66)) | (1L << (MERGE - 66)) | (1L << (MATCHED - 66)) | (1L << (NEWKW - 66)) | (1L << (CONTEXT - 66)))) != 0) || ((((_la - 134)) & ~0x3f) == 0 && ((1L << (_la - 134)) & ((1L << (GROUPING - 134)) | (1L << (GROUPING_ID - 134)) | (1L << (QUESTION - 134)) | (1L << (LPAREN - 134)) | (1L << (LCURLY - 134)) | (1L << (PLUS - 134)) | (1L << (MINUS - 134)) | (1L << (TICKED_STRING_LITERAL - 134)) | (1L << (QUOTED_STRING_LITERAL - 134)) | (1L << (STRING_LITERAL - 134)) | (1L << (IDENT - 134)) | (1L << (IntegerLiteral - 134)) | (1L << (FloatingPointLiteral - 134)))) != 0)) { + { + State = 1617; expressionList(); + } + } + + State = 1620; Match(RPAREN); + } + } + + State = 1624; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==ATCHAR) { + { + State = 1623; typeExpressionAnnotation(); + } + } + + } + Context.Stop = TokenStream.LT(-1); + paraphrases.Pop(); + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class ViewExpressionsContext : ParserRuleContext { + public ITerminalNode[] DOT() { return GetTokens(EsperEPL2GrammarParser.DOT); } + public ITerminalNode DOT(int i) { + return GetToken(EsperEPL2GrammarParser.DOT, i); + } + public ViewExpressionWNamespaceContext[] viewExpressionWNamespace() { + return GetRuleContexts(); + } + public ViewExpressionWNamespaceContext viewExpressionWNamespace(int i) { + return GetRuleContext(i); + } + public ITerminalNode[] HASHCHAR() { return GetTokens(EsperEPL2GrammarParser.HASHCHAR); } + public ITerminalNode HASHCHAR(int i) { + return GetToken(EsperEPL2GrammarParser.HASHCHAR, i); + } + public ViewExpressionOptNamespaceContext[] viewExpressionOptNamespace() { + return GetRuleContexts(); + } + public ViewExpressionOptNamespaceContext viewExpressionOptNamespace(int i) { + return GetRuleContext(i); + } + public ViewExpressionsContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_viewExpressions; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterViewExpressions(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitViewExpressions(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitViewExpressions(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public ViewExpressionsContext viewExpressions() { + ViewExpressionsContext _localctx = new ViewExpressionsContext(Context, State); + EnterRule(_localctx, 214, RULE_viewExpressions); + paraphrases.Push("view specifications"); + int _la; + try { + State = 1644; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case DOT: + EnterOuterAlt(_localctx, 1); + { + { + State = 1626; Match(DOT); + State = 1627; viewExpressionWNamespace(); + State = 1632; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==DOT) { + { + { + State = 1628; Match(DOT); + State = 1629; viewExpressionWNamespace(); + } + } + State = 1634; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + break; + case HASHCHAR: + EnterOuterAlt(_localctx, 2); + { + { + State = 1635; Match(HASHCHAR); + State = 1636; viewExpressionOptNamespace(); + State = 1641; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==HASHCHAR) { + { + { + State = 1637; Match(HASHCHAR); + State = 1638; viewExpressionOptNamespace(); + } + } + State = 1643; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + break; + default: + throw new NoViableAltException(this); + } + Context.Stop = TokenStream.LT(-1); + paraphrases.Pop(); + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class ViewExpressionWNamespaceContext : ParserRuleContext { + public IToken ns; + public ITerminalNode COLON() { return GetToken(EsperEPL2GrammarParser.COLON, 0); } + public ViewWParametersContext viewWParameters() { + return GetRuleContext(0); + } + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public ViewExpressionWNamespaceContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_viewExpressionWNamespace; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterViewExpressionWNamespace(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitViewExpressionWNamespace(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitViewExpressionWNamespace(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public ViewExpressionWNamespaceContext viewExpressionWNamespace() { + ViewExpressionWNamespaceContext _localctx = new ViewExpressionWNamespaceContext(Context, State); + EnterRule(_localctx, 216, RULE_viewExpressionWNamespace); + try { + EnterOuterAlt(_localctx, 1); + { + State = 1646; _localctx.ns = Match(IDENT); + State = 1647; Match(COLON); + State = 1648; viewWParameters(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class ViewExpressionOptNamespaceContext : ParserRuleContext { + public IToken ns; + public ViewWParametersContext viewWParameters() { + return GetRuleContext(0); + } + public ITerminalNode COLON() { return GetToken(EsperEPL2GrammarParser.COLON, 0); } + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public ViewExpressionOptNamespaceContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_viewExpressionOptNamespace; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterViewExpressionOptNamespace(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitViewExpressionOptNamespace(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitViewExpressionOptNamespace(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public ViewExpressionOptNamespaceContext viewExpressionOptNamespace() { + ViewExpressionOptNamespaceContext _localctx = new ViewExpressionOptNamespaceContext(Context, State); + EnterRule(_localctx, 218, RULE_viewExpressionOptNamespace); + try { + EnterOuterAlt(_localctx, 1); + { + State = 1652; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,198,Context) ) { + case 1: + { + State = 1650; _localctx.ns = Match(IDENT); + State = 1651; Match(COLON); + } + break; + } + State = 1654; viewWParameters(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class ViewWParametersContext : ParserRuleContext { + public IToken i; + public IToken m; + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public ITerminalNode MERGE() { return GetToken(EsperEPL2GrammarParser.MERGE, 0); } + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public ExpressionWithTimeListContext expressionWithTimeList() { + return GetRuleContext(0); + } + public ViewWParametersContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_viewWParameters; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterViewWParameters(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitViewWParameters(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitViewWParameters(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public ViewWParametersContext viewWParameters() { + ViewWParametersContext _localctx = new ViewWParametersContext(Context, State); + EnterRule(_localctx, 220, RULE_viewWParameters); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1658; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case IDENT: + { + State = 1656; _localctx.i = Match(IDENT); + } + break; + case MERGE: + { + State = 1657; _localctx.m = Match(MERGE); + } + break; + default: + throw new NoViableAltException(this); + } + State = 1665; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,201,Context) ) { + case 1: + { + State = 1660; Match(LPAREN); + State = 1662; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (((((_la - 2)) & ~0x3f) == 0 && ((1L << (_la - 2)) & ((1L << (WINDOW - 2)) | (1L << (ESCAPE - 2)) | (1L << (NOT_EXPR - 2)) | (1L << (EVERY_EXPR - 2)) | (1L << (SUM - 2)) | (1L << (AVG - 2)) | (1L << (MAX - 2)) | (1L << (MIN - 2)) | (1L << (COALESCE - 2)) | (1L << (MEDIAN - 2)) | (1L << (STDDEV - 2)) | (1L << (AVEDEV - 2)) | (1L << (COUNT - 2)) | (1L << (CASE - 2)) | (1L << (OUTER - 2)) | (1L << (JOIN - 2)) | (1L << (LEFT - 2)) | (1L << (RIGHT - 2)) | (1L << (FULL - 2)) | (1L << (EVENTS - 2)) | (1L << (FIRST - 2)) | (1L << (LAST - 2)) | (1L << (ISTREAM - 2)) | (1L << (SCHEMA - 2)) | (1L << (UNIDIRECTIONAL - 2)) | (1L << (RETAINUNION - 2)) | (1L << (RETAININTERSECTION - 2)) | (1L << (PATTERN - 2)) | (1L << (SQL - 2)) | (1L << (METADATASQL - 2)))) != 0) || ((((_la - 66)) & ~0x3f) == 0 && ((1L << (_la - 66)) & ((1L << (PREVIOUS - 66)) | (1L << (PREVIOUSTAIL - 66)) | (1L << (PREVIOUSCOUNT - 66)) | (1L << (PREVIOUSWINDOW - 66)) | (1L << (PRIOR - 66)) | (1L << (EXISTS - 66)) | (1L << (WEEKDAY - 66)) | (1L << (LW - 66)) | (1L << (INSTANCEOF - 66)) | (1L << (TYPEOF - 66)) | (1L << (CAST - 66)) | (1L << (CURRENT_TIMESTAMP - 66)) | (1L << (SNAPSHOT - 66)) | (1L << (VARIABLE - 66)) | (1L << (TABLE - 66)) | (1L << (UNTIL - 66)) | (1L << (AT - 66)) | (1L << (INDEX - 66)) | (1L << (BOOLEAN_TRUE - 66)) | (1L << (BOOLEAN_FALSE - 66)) | (1L << (VALUE_NULL - 66)) | (1L << (DEFINE - 66)) | (1L << (PARTITION - 66)) | (1L << (MATCHES - 66)) | (1L << (FOR - 66)) | (1L << (WHILE - 66)) | (1L << (USING - 66)) | (1L << (MERGE - 66)) | (1L << (MATCHED - 66)) | (1L << (NEWKW - 66)) | (1L << (CONTEXT - 66)))) != 0) || ((((_la - 134)) & ~0x3f) == 0 && ((1L << (_la - 134)) & ((1L << (GROUPING - 134)) | (1L << (GROUPING_ID - 134)) | (1L << (QUESTION - 134)) | (1L << (LPAREN - 134)) | (1L << (LBRACK - 134)) | (1L << (LCURLY - 134)) | (1L << (PLUS - 134)) | (1L << (MINUS - 134)) | (1L << (STAR - 134)) | (1L << (TICKED_STRING_LITERAL - 134)) | (1L << (QUOTED_STRING_LITERAL - 134)) | (1L << (STRING_LITERAL - 134)) | (1L << (IDENT - 134)) | (1L << (IntegerLiteral - 134)) | (1L << (FloatingPointLiteral - 134)))) != 0)) { + { + State = 1661; expressionWithTimeList(); + } + } + + State = 1664; Match(RPAREN); + } + break; + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class GroupByListExprContext : ParserRuleContext { + public GroupByListChoiceContext[] groupByListChoice() { + return GetRuleContexts(); + } + public GroupByListChoiceContext groupByListChoice(int i) { + return GetRuleContext(i); + } + public ITerminalNode[] COMMA() { return GetTokens(EsperEPL2GrammarParser.COMMA); } + public ITerminalNode COMMA(int i) { + return GetToken(EsperEPL2GrammarParser.COMMA, i); + } + public GroupByListExprContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_groupByListExpr; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterGroupByListExpr(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitGroupByListExpr(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitGroupByListExpr(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public GroupByListExprContext groupByListExpr() { + GroupByListExprContext _localctx = new GroupByListExprContext(Context, State); + EnterRule(_localctx, 222, RULE_groupByListExpr); + paraphrases.Push("group-by clause"); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1667; groupByListChoice(); + State = 1672; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 1668; Match(COMMA); + State = 1669; groupByListChoice(); + } + } + State = 1674; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + Context.Stop = TokenStream.LT(-1); + paraphrases.Pop(); + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class GroupByListChoiceContext : ParserRuleContext { + public ExpressionContext e1; + public ExpressionContext expression() { + return GetRuleContext(0); + } + public GroupByCubeOrRollupContext groupByCubeOrRollup() { + return GetRuleContext(0); + } + public GroupByGroupingSetsContext groupByGroupingSets() { + return GetRuleContext(0); + } + public GroupByListChoiceContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_groupByListChoice; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterGroupByListChoice(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitGroupByListChoice(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitGroupByListChoice(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public GroupByListChoiceContext groupByListChoice() { + GroupByListChoiceContext _localctx = new GroupByListChoiceContext(Context, State); + EnterRule(_localctx, 224, RULE_groupByListChoice); + try { + State = 1678; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,203,Context) ) { + case 1: + EnterOuterAlt(_localctx, 1); + { + State = 1675; _localctx.e1 = expression(); + } + break; + case 2: + EnterOuterAlt(_localctx, 2); + { + State = 1676; groupByCubeOrRollup(); + } + break; + case 3: + EnterOuterAlt(_localctx, 3); + { + State = 1677; groupByGroupingSets(); + } + break; + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class GroupByCubeOrRollupContext : ParserRuleContext { + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public GroupByCombinableExprContext[] groupByCombinableExpr() { + return GetRuleContexts(); + } + public GroupByCombinableExprContext groupByCombinableExpr(int i) { + return GetRuleContext(i); + } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public ITerminalNode CUBE() { return GetToken(EsperEPL2GrammarParser.CUBE, 0); } + public ITerminalNode ROLLUP() { return GetToken(EsperEPL2GrammarParser.ROLLUP, 0); } + public ITerminalNode[] COMMA() { return GetTokens(EsperEPL2GrammarParser.COMMA); } + public ITerminalNode COMMA(int i) { + return GetToken(EsperEPL2GrammarParser.COMMA, i); + } + public GroupByCubeOrRollupContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_groupByCubeOrRollup; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterGroupByCubeOrRollup(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitGroupByCubeOrRollup(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitGroupByCubeOrRollup(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public GroupByCubeOrRollupContext groupByCubeOrRollup() { + GroupByCubeOrRollupContext _localctx = new GroupByCubeOrRollupContext(Context, State); + EnterRule(_localctx, 226, RULE_groupByCubeOrRollup); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1680; + _la = TokenStream.LA(1); + if ( !(_la==CUBE || _la==ROLLUP) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + State = 1681; Match(LPAREN); + State = 1682; groupByCombinableExpr(); + State = 1687; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 1683; Match(COMMA); + State = 1684; groupByCombinableExpr(); + } + } + State = 1689; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + State = 1690; Match(RPAREN); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class GroupByGroupingSetsContext : ParserRuleContext { + public ITerminalNode GROUPING() { return GetToken(EsperEPL2GrammarParser.GROUPING, 0); } + public ITerminalNode SETS() { return GetToken(EsperEPL2GrammarParser.SETS, 0); } + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public GroupBySetsChoiceContext[] groupBySetsChoice() { + return GetRuleContexts(); + } + public GroupBySetsChoiceContext groupBySetsChoice(int i) { + return GetRuleContext(i); + } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public ITerminalNode[] COMMA() { return GetTokens(EsperEPL2GrammarParser.COMMA); } + public ITerminalNode COMMA(int i) { + return GetToken(EsperEPL2GrammarParser.COMMA, i); + } + public GroupByGroupingSetsContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_groupByGroupingSets; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterGroupByGroupingSets(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitGroupByGroupingSets(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitGroupByGroupingSets(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public GroupByGroupingSetsContext groupByGroupingSets() { + GroupByGroupingSetsContext _localctx = new GroupByGroupingSetsContext(Context, State); + EnterRule(_localctx, 228, RULE_groupByGroupingSets); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1692; Match(GROUPING); + State = 1693; Match(SETS); + State = 1694; Match(LPAREN); + State = 1695; groupBySetsChoice(); + State = 1700; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 1696; Match(COMMA); + State = 1697; groupBySetsChoice(); + } + } + State = 1702; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + State = 1703; Match(RPAREN); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class GroupBySetsChoiceContext : ParserRuleContext { + public GroupByCubeOrRollupContext groupByCubeOrRollup() { + return GetRuleContext(0); + } + public GroupByCombinableExprContext groupByCombinableExpr() { + return GetRuleContext(0); + } + public GroupBySetsChoiceContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_groupBySetsChoice; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterGroupBySetsChoice(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitGroupBySetsChoice(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitGroupBySetsChoice(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public GroupBySetsChoiceContext groupBySetsChoice() { + GroupBySetsChoiceContext _localctx = new GroupBySetsChoiceContext(Context, State); + EnterRule(_localctx, 230, RULE_groupBySetsChoice); + try { + State = 1707; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case CUBE: + case ROLLUP: + EnterOuterAlt(_localctx, 1); + { + State = 1705; groupByCubeOrRollup(); + } + break; + case WINDOW: + case ESCAPE: + case NOT_EXPR: + case EVERY_EXPR: + case SUM: + case AVG: + case MAX: + case MIN: + case COALESCE: + case MEDIAN: + case STDDEV: + case AVEDEV: + case COUNT: + case CASE: + case OUTER: + case JOIN: + case LEFT: + case RIGHT: + case FULL: + case EVENTS: + case FIRST: + case LAST: + case ISTREAM: + case SCHEMA: + case UNIDIRECTIONAL: + case RETAINUNION: + case RETAININTERSECTION: + case PATTERN: + case SQL: + case METADATASQL: + case PREVIOUS: + case PREVIOUSTAIL: + case PREVIOUSCOUNT: + case PREVIOUSWINDOW: + case PRIOR: + case EXISTS: + case WEEKDAY: + case LW: + case INSTANCEOF: + case TYPEOF: + case CAST: + case CURRENT_TIMESTAMP: + case SNAPSHOT: + case VARIABLE: + case TABLE: + case UNTIL: + case AT: + case INDEX: + case BOOLEAN_TRUE: + case BOOLEAN_FALSE: + case VALUE_NULL: + case DEFINE: + case PARTITION: + case MATCHES: + case FOR: + case WHILE: + case USING: + case MERGE: + case MATCHED: + case NEWKW: + case CONTEXT: + case GROUPING: + case GROUPING_ID: + case QUESTION: + case LPAREN: + case LCURLY: + case PLUS: + case MINUS: + case TICKED_STRING_LITERAL: + case QUOTED_STRING_LITERAL: + case STRING_LITERAL: + case IDENT: + case IntegerLiteral: + case FloatingPointLiteral: + EnterOuterAlt(_localctx, 2); + { + State = 1706; groupByCombinableExpr(); + } + break; + default: + throw new NoViableAltException(this); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class GroupByCombinableExprContext : ParserRuleContext { + public ExpressionContext e1; + public ExpressionContext[] expression() { + return GetRuleContexts(); + } + public ExpressionContext expression(int i) { + return GetRuleContext(i); + } + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public ITerminalNode[] COMMA() { return GetTokens(EsperEPL2GrammarParser.COMMA); } + public ITerminalNode COMMA(int i) { + return GetToken(EsperEPL2GrammarParser.COMMA, i); + } + public GroupByCombinableExprContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_groupByCombinableExpr; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterGroupByCombinableExpr(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitGroupByCombinableExpr(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitGroupByCombinableExpr(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public GroupByCombinableExprContext groupByCombinableExpr() { + GroupByCombinableExprContext _localctx = new GroupByCombinableExprContext(Context, State); + EnterRule(_localctx, 232, RULE_groupByCombinableExpr); + int _la; + try { + State = 1722; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,209,Context) ) { + case 1: + EnterOuterAlt(_localctx, 1); + { + State = 1709; _localctx.e1 = expression(); + } + break; + case 2: + EnterOuterAlt(_localctx, 2); + { + State = 1710; Match(LPAREN); + State = 1719; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (((((_la - 2)) & ~0x3f) == 0 && ((1L << (_la - 2)) & ((1L << (WINDOW - 2)) | (1L << (ESCAPE - 2)) | (1L << (NOT_EXPR - 2)) | (1L << (EVERY_EXPR - 2)) | (1L << (SUM - 2)) | (1L << (AVG - 2)) | (1L << (MAX - 2)) | (1L << (MIN - 2)) | (1L << (COALESCE - 2)) | (1L << (MEDIAN - 2)) | (1L << (STDDEV - 2)) | (1L << (AVEDEV - 2)) | (1L << (COUNT - 2)) | (1L << (CASE - 2)) | (1L << (OUTER - 2)) | (1L << (JOIN - 2)) | (1L << (LEFT - 2)) | (1L << (RIGHT - 2)) | (1L << (FULL - 2)) | (1L << (EVENTS - 2)) | (1L << (FIRST - 2)) | (1L << (LAST - 2)) | (1L << (ISTREAM - 2)) | (1L << (SCHEMA - 2)) | (1L << (UNIDIRECTIONAL - 2)) | (1L << (RETAINUNION - 2)) | (1L << (RETAININTERSECTION - 2)) | (1L << (PATTERN - 2)) | (1L << (SQL - 2)) | (1L << (METADATASQL - 2)))) != 0) || ((((_la - 66)) & ~0x3f) == 0 && ((1L << (_la - 66)) & ((1L << (PREVIOUS - 66)) | (1L << (PREVIOUSTAIL - 66)) | (1L << (PREVIOUSCOUNT - 66)) | (1L << (PREVIOUSWINDOW - 66)) | (1L << (PRIOR - 66)) | (1L << (EXISTS - 66)) | (1L << (WEEKDAY - 66)) | (1L << (LW - 66)) | (1L << (INSTANCEOF - 66)) | (1L << (TYPEOF - 66)) | (1L << (CAST - 66)) | (1L << (CURRENT_TIMESTAMP - 66)) | (1L << (SNAPSHOT - 66)) | (1L << (VARIABLE - 66)) | (1L << (TABLE - 66)) | (1L << (UNTIL - 66)) | (1L << (AT - 66)) | (1L << (INDEX - 66)) | (1L << (BOOLEAN_TRUE - 66)) | (1L << (BOOLEAN_FALSE - 66)) | (1L << (VALUE_NULL - 66)) | (1L << (DEFINE - 66)) | (1L << (PARTITION - 66)) | (1L << (MATCHES - 66)) | (1L << (FOR - 66)) | (1L << (WHILE - 66)) | (1L << (USING - 66)) | (1L << (MERGE - 66)) | (1L << (MATCHED - 66)) | (1L << (NEWKW - 66)) | (1L << (CONTEXT - 66)))) != 0) || ((((_la - 134)) & ~0x3f) == 0 && ((1L << (_la - 134)) & ((1L << (GROUPING - 134)) | (1L << (GROUPING_ID - 134)) | (1L << (QUESTION - 134)) | (1L << (LPAREN - 134)) | (1L << (LCURLY - 134)) | (1L << (PLUS - 134)) | (1L << (MINUS - 134)) | (1L << (TICKED_STRING_LITERAL - 134)) | (1L << (QUOTED_STRING_LITERAL - 134)) | (1L << (STRING_LITERAL - 134)) | (1L << (IDENT - 134)) | (1L << (IntegerLiteral - 134)) | (1L << (FloatingPointLiteral - 134)))) != 0)) { + { + State = 1711; expression(); + State = 1716; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 1712; Match(COMMA); + State = 1713; expression(); + } + } + State = 1718; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + + State = 1721; Match(RPAREN); + } + break; + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class OrderByListExprContext : ParserRuleContext { + public OrderByListElementContext[] orderByListElement() { + return GetRuleContexts(); + } + public OrderByListElementContext orderByListElement(int i) { + return GetRuleContext(i); + } + public ITerminalNode[] COMMA() { return GetTokens(EsperEPL2GrammarParser.COMMA); } + public ITerminalNode COMMA(int i) { + return GetToken(EsperEPL2GrammarParser.COMMA, i); + } + public OrderByListExprContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_orderByListExpr; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterOrderByListExpr(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitOrderByListExpr(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitOrderByListExpr(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public OrderByListExprContext orderByListExpr() { + OrderByListExprContext _localctx = new OrderByListExprContext(Context, State); + EnterRule(_localctx, 234, RULE_orderByListExpr); + paraphrases.Push("order by clause"); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1724; orderByListElement(); + State = 1729; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 1725; Match(COMMA); + State = 1726; orderByListElement(); + } + } + State = 1731; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + Context.Stop = TokenStream.LT(-1); + paraphrases.Pop(); + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class OrderByListElementContext : ParserRuleContext { + public IToken a; + public IToken d; + public ExpressionContext expression() { + return GetRuleContext(0); + } + public ITerminalNode ASC() { return GetToken(EsperEPL2GrammarParser.ASC, 0); } + public ITerminalNode DESC() { return GetToken(EsperEPL2GrammarParser.DESC, 0); } + public OrderByListElementContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_orderByListElement; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterOrderByListElement(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitOrderByListElement(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitOrderByListElement(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public OrderByListElementContext orderByListElement() { + OrderByListElementContext _localctx = new OrderByListElementContext(Context, State); + EnterRule(_localctx, 236, RULE_orderByListElement); + try { + EnterOuterAlt(_localctx, 1); + { + State = 1732; expression(); + State = 1735; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case ASC: + { + State = 1733; _localctx.a = Match(ASC); + } + break; + case DESC: + { + State = 1734; _localctx.d = Match(DESC); + } + break; + case Eof: + case INSERT: + case ROW_LIMIT_EXPR: + case FOR: + case RPAREN: + case COMMA: + break; + default: + throw new NoViableAltException(this); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class HavingClauseContext : ParserRuleContext { + public EvalOrExpressionContext evalOrExpression() { + return GetRuleContext(0); + } + public HavingClauseContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_havingClause; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterHavingClause(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitHavingClause(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitHavingClause(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public HavingClauseContext havingClause() { + HavingClauseContext _localctx = new HavingClauseContext(Context, State); + EnterRule(_localctx, 238, RULE_havingClause); + paraphrases.Push("having clause"); + try { + EnterOuterAlt(_localctx, 1); + { + State = 1737; evalOrExpression(); + } + Context.Stop = TokenStream.LT(-1); + paraphrases.Pop(); + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class OutputLimitContext : ParserRuleContext { + public IToken k; + public IToken ev; + public IToken i; + public IToken e; + public IToken at; + public IToken wh; + public IToken t; + public OutputLimitAfterContext outputLimitAfter() { + return GetRuleContext(0); + } + public OutputLimitAndTermContext outputLimitAndTerm() { + return GetRuleContext(0); + } + public ITerminalNode ALL() { return GetToken(EsperEPL2GrammarParser.ALL, 0); } + public ITerminalNode FIRST() { return GetToken(EsperEPL2GrammarParser.FIRST, 0); } + public ITerminalNode LAST() { return GetToken(EsperEPL2GrammarParser.LAST, 0); } + public ITerminalNode SNAPSHOT() { return GetToken(EsperEPL2GrammarParser.SNAPSHOT, 0); } + public CrontabLimitParameterSetContext crontabLimitParameterSet() { + return GetRuleContext(0); + } + public ExpressionContext expression() { + return GetRuleContext(0); + } + public ITerminalNode TERMINATED() { return GetToken(EsperEPL2GrammarParser.TERMINATED, 0); } + public ITerminalNode EVERY_EXPR() { return GetToken(EsperEPL2GrammarParser.EVERY_EXPR, 0); } + public ITerminalNode AT() { return GetToken(EsperEPL2GrammarParser.AT, 0); } + public ITerminalNode WHEN() { return GetToken(EsperEPL2GrammarParser.WHEN, 0); } + public TimePeriodContext timePeriod() { + return GetRuleContext(0); + } + public ITerminalNode THEN() { return GetToken(EsperEPL2GrammarParser.THEN, 0); } + public OnSetExprContext onSetExpr() { + return GetRuleContext(0); + } + public ITerminalNode AND_EXPR() { return GetToken(EsperEPL2GrammarParser.AND_EXPR, 0); } + public NumberContext number() { + return GetRuleContext(0); + } + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public ITerminalNode EVENTS() { return GetToken(EsperEPL2GrammarParser.EVENTS, 0); } + public OutputLimitContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_outputLimit; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterOutputLimit(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitOutputLimit(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitOutputLimit(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public OutputLimitContext outputLimit() { + OutputLimitContext _localctx = new OutputLimitContext(Context, State); + EnterRule(_localctx, 240, RULE_outputLimit); + paraphrases.Push("output rate clause"); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1740; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==AFTER) { + { + State = 1739; outputLimitAfter(); + } + } + + State = 1746; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case ALL: + { + State = 1742; _localctx.k = Match(ALL); + } + break; + case FIRST: + { + State = 1743; _localctx.k = Match(FIRST); + } + break; + case LAST: + { + State = 1744; _localctx.k = Match(LAST); + } + break; + case SNAPSHOT: + { + State = 1745; _localctx.k = Match(SNAPSHOT); + } + break; + case Eof: + case AND_EXPR: + case EVERY_EXPR: + case WHEN: + case ORDER: + case AT: + case ROW_LIMIT_EXPR: + case FOR: + case RPAREN: + break; + default: + throw new NoViableAltException(this); + } + State = 1776; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,219,Context) ) { + case 1: + { + { + State = 1748; _localctx.ev = Match(EVERY_EXPR); + State = 1755; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,215,Context) ) { + case 1: + { + State = 1749; timePeriod(); + } + break; + case 2: + { + State = 1752; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case IntegerLiteral: + case FloatingPointLiteral: + { + State = 1750; number(); + } + break; + case IDENT: + { + State = 1751; _localctx.i = Match(IDENT); + } + break; + default: + throw new NoViableAltException(this); + } + { + State = 1754; _localctx.e = Match(EVENTS); + } + } + break; + } + } + } + break; + case 2: + { + { + State = 1757; _localctx.at = Match(AT); + State = 1758; crontabLimitParameterSet(); + } + } + break; + case 3: + { + { + State = 1759; _localctx.wh = Match(WHEN); + State = 1760; expression(); + State = 1763; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==THEN) { + { + State = 1761; Match(THEN); + State = 1762; onSetExpr(); + } + } + + } + } + break; + case 4: + { + { + State = 1765; _localctx.t = Match(WHEN); + State = 1766; Match(TERMINATED); + State = 1769; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,217,Context) ) { + case 1: + { + State = 1767; Match(AND_EXPR); + State = 1768; expression(); + } + break; + } + State = 1773; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==THEN) { + { + State = 1771; Match(THEN); + State = 1772; onSetExpr(); + } + } + + } + } + break; + case 5: + { + } + break; + } + State = 1779; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==AND_EXPR) { + { + State = 1778; outputLimitAndTerm(); + } + } + + } + Context.Stop = TokenStream.LT(-1); + paraphrases.Pop(); + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class OutputLimitAndTermContext : ParserRuleContext { + public ITerminalNode[] AND_EXPR() { return GetTokens(EsperEPL2GrammarParser.AND_EXPR); } + public ITerminalNode AND_EXPR(int i) { + return GetToken(EsperEPL2GrammarParser.AND_EXPR, i); + } + public ITerminalNode WHEN() { return GetToken(EsperEPL2GrammarParser.WHEN, 0); } + public ITerminalNode TERMINATED() { return GetToken(EsperEPL2GrammarParser.TERMINATED, 0); } + public ExpressionContext expression() { + return GetRuleContext(0); + } + public ITerminalNode THEN() { return GetToken(EsperEPL2GrammarParser.THEN, 0); } + public OnSetExprContext onSetExpr() { + return GetRuleContext(0); + } + public OutputLimitAndTermContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_outputLimitAndTerm; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterOutputLimitAndTerm(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitOutputLimitAndTerm(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitOutputLimitAndTerm(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public OutputLimitAndTermContext outputLimitAndTerm() { + OutputLimitAndTermContext _localctx = new OutputLimitAndTermContext(Context, State); + EnterRule(_localctx, 242, RULE_outputLimitAndTerm); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1781; Match(AND_EXPR); + State = 1782; Match(WHEN); + State = 1783; Match(TERMINATED); + State = 1786; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==AND_EXPR) { + { + State = 1784; Match(AND_EXPR); + State = 1785; expression(); + } + } + + State = 1790; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==THEN) { + { + State = 1788; Match(THEN); + State = 1789; onSetExpr(); + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class OutputLimitAfterContext : ParserRuleContext { + public IToken a; + public ITerminalNode AFTER() { return GetToken(EsperEPL2GrammarParser.AFTER, 0); } + public TimePeriodContext timePeriod() { + return GetRuleContext(0); + } + public NumberContext number() { + return GetRuleContext(0); + } + public ITerminalNode EVENTS() { return GetToken(EsperEPL2GrammarParser.EVENTS, 0); } + public OutputLimitAfterContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_outputLimitAfter; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterOutputLimitAfter(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitOutputLimitAfter(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitOutputLimitAfter(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public OutputLimitAfterContext outputLimitAfter() { + OutputLimitAfterContext _localctx = new OutputLimitAfterContext(Context, State); + EnterRule(_localctx, 244, RULE_outputLimitAfter); + try { + EnterOuterAlt(_localctx, 1); + { + State = 1792; _localctx.a = Match(AFTER); + State = 1797; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,223,Context) ) { + case 1: + { + State = 1793; timePeriod(); + } + break; + case 2: + { + State = 1794; number(); + State = 1795; Match(EVENTS); + } + break; + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class RowLimitContext : ParserRuleContext { + public NumberconstantContext n1; + public IToken i1; + public IToken c; + public IToken o; + public NumberconstantContext n2; + public IToken i2; + public NumberconstantContext[] numberconstant() { + return GetRuleContexts(); + } + public NumberconstantContext numberconstant(int i) { + return GetRuleContext(i); + } + public ITerminalNode[] IDENT() { return GetTokens(EsperEPL2GrammarParser.IDENT); } + public ITerminalNode IDENT(int i) { + return GetToken(EsperEPL2GrammarParser.IDENT, i); + } + public ITerminalNode COMMA() { return GetToken(EsperEPL2GrammarParser.COMMA, 0); } + public ITerminalNode OFFSET() { return GetToken(EsperEPL2GrammarParser.OFFSET, 0); } + public RowLimitContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_rowLimit; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterRowLimit(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitRowLimit(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitRowLimit(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public RowLimitContext rowLimit() { + RowLimitContext _localctx = new RowLimitContext(Context, State); + EnterRule(_localctx, 246, RULE_rowLimit); + paraphrases.Push("row limit clause"); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1801; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case PLUS: + case MINUS: + case IntegerLiteral: + case FloatingPointLiteral: + { + State = 1799; _localctx.n1 = numberconstant(); + } + break; + case IDENT: + { + State = 1800; _localctx.i1 = Match(IDENT); + } + break; + default: + throw new NoViableAltException(this); + } + State = 1811; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==OFFSET || _la==COMMA) { + { + State = 1805; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case COMMA: + { + State = 1803; _localctx.c = Match(COMMA); + } + break; + case OFFSET: + { + State = 1804; _localctx.o = Match(OFFSET); + } + break; + default: + throw new NoViableAltException(this); + } + State = 1809; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case PLUS: + case MINUS: + case IntegerLiteral: + case FloatingPointLiteral: + { + State = 1807; _localctx.n2 = numberconstant(); + } + break; + case IDENT: + { + State = 1808; _localctx.i2 = Match(IDENT); + } + break; + default: + throw new NoViableAltException(this); + } + } + } + + } + Context.Stop = TokenStream.LT(-1); + paraphrases.Pop(); + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class CrontabLimitParameterSetContext : ParserRuleContext { + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public ExpressionWithTimeListContext expressionWithTimeList() { + return GetRuleContext(0); + } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public CrontabLimitParameterSetContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_crontabLimitParameterSet; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterCrontabLimitParameterSet(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitCrontabLimitParameterSet(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitCrontabLimitParameterSet(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public CrontabLimitParameterSetContext crontabLimitParameterSet() { + CrontabLimitParameterSetContext _localctx = new CrontabLimitParameterSetContext(Context, State); + EnterRule(_localctx, 248, RULE_crontabLimitParameterSet); + try { + EnterOuterAlt(_localctx, 1); + { + State = 1813; Match(LPAREN); + State = 1814; expressionWithTimeList(); + State = 1815; Match(RPAREN); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class WhenClauseContext : ParserRuleContext { + public ITerminalNode WHEN() { return GetToken(EsperEPL2GrammarParser.WHEN, 0); } + public ExpressionContext[] expression() { + return GetRuleContexts(); + } + public ExpressionContext expression(int i) { + return GetRuleContext(i); + } + public ITerminalNode THEN() { return GetToken(EsperEPL2GrammarParser.THEN, 0); } + public WhenClauseContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_whenClause; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterWhenClause(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitWhenClause(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitWhenClause(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public WhenClauseContext whenClause() { + WhenClauseContext _localctx = new WhenClauseContext(Context, State); + EnterRule(_localctx, 250, RULE_whenClause); + try { + EnterOuterAlt(_localctx, 1); + { + { + State = 1817; Match(WHEN); + State = 1818; expression(); + State = 1819; Match(THEN); + State = 1820; expression(); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class ElseClauseContext : ParserRuleContext { + public ITerminalNode ELSE() { return GetToken(EsperEPL2GrammarParser.ELSE, 0); } + public ExpressionContext expression() { + return GetRuleContext(0); + } + public ElseClauseContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_elseClause; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterElseClause(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitElseClause(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitElseClause(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public ElseClauseContext elseClause() { + ElseClauseContext _localctx = new ElseClauseContext(Context, State); + EnterRule(_localctx, 252, RULE_elseClause); + try { + EnterOuterAlt(_localctx, 1); + { + { + State = 1822; Match(ELSE); + State = 1823; expression(); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class MatchRecogContext : ParserRuleContext { + public ITerminalNode MATCH_RECOGNIZE() { return GetToken(EsperEPL2GrammarParser.MATCH_RECOGNIZE, 0); } + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public MatchRecogMeasuresContext matchRecogMeasures() { + return GetRuleContext(0); + } + public MatchRecogPatternContext matchRecogPattern() { + return GetRuleContext(0); + } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public MatchRecogPartitionByContext matchRecogPartitionBy() { + return GetRuleContext(0); + } + public MatchRecogMatchesSelectionContext matchRecogMatchesSelection() { + return GetRuleContext(0); + } + public MatchRecogMatchesAfterSkipContext matchRecogMatchesAfterSkip() { + return GetRuleContext(0); + } + public MatchRecogMatchesIntervalContext matchRecogMatchesInterval() { + return GetRuleContext(0); + } + public MatchRecogDefineContext matchRecogDefine() { + return GetRuleContext(0); + } + public MatchRecogContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_matchRecog; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterMatchRecog(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitMatchRecog(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitMatchRecog(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public MatchRecogContext matchRecog() { + MatchRecogContext _localctx = new MatchRecogContext(Context, State); + EnterRule(_localctx, 254, RULE_matchRecog); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1825; Match(MATCH_RECOGNIZE); + State = 1826; Match(LPAREN); + State = 1828; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==PARTITION) { + { + State = 1827; matchRecogPartitionBy(); + } + } + + State = 1830; matchRecogMeasures(); + State = 1832; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==ALL) { + { + State = 1831; matchRecogMatchesSelection(); + } + } + + State = 1835; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==AFTER) { + { + State = 1834; matchRecogMatchesAfterSkip(); + } + } + + State = 1837; matchRecogPattern(); + State = 1839; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==IDENT) { + { + State = 1838; matchRecogMatchesInterval(); + } + } + + State = 1842; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==DEFINE) { + { + State = 1841; matchRecogDefine(); + } + } + + State = 1844; Match(RPAREN); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class MatchRecogPartitionByContext : ParserRuleContext { + public ITerminalNode PARTITION() { return GetToken(EsperEPL2GrammarParser.PARTITION, 0); } + public ITerminalNode BY() { return GetToken(EsperEPL2GrammarParser.BY, 0); } + public ExpressionContext[] expression() { + return GetRuleContexts(); + } + public ExpressionContext expression(int i) { + return GetRuleContext(i); + } + public ITerminalNode[] COMMA() { return GetTokens(EsperEPL2GrammarParser.COMMA); } + public ITerminalNode COMMA(int i) { + return GetToken(EsperEPL2GrammarParser.COMMA, i); + } + public MatchRecogPartitionByContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_matchRecogPartitionBy; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterMatchRecogPartitionBy(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitMatchRecogPartitionBy(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitMatchRecogPartitionBy(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public MatchRecogPartitionByContext matchRecogPartitionBy() { + MatchRecogPartitionByContext _localctx = new MatchRecogPartitionByContext(Context, State); + EnterRule(_localctx, 256, RULE_matchRecogPartitionBy); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1846; Match(PARTITION); + State = 1847; Match(BY); + State = 1848; expression(); + State = 1853; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 1849; Match(COMMA); + State = 1850; expression(); + } + } + State = 1855; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class MatchRecogMeasuresContext : ParserRuleContext { + public ITerminalNode MEASURES() { return GetToken(EsperEPL2GrammarParser.MEASURES, 0); } + public MatchRecogMeasureItemContext[] matchRecogMeasureItem() { + return GetRuleContexts(); + } + public MatchRecogMeasureItemContext matchRecogMeasureItem(int i) { + return GetRuleContext(i); + } + public ITerminalNode[] COMMA() { return GetTokens(EsperEPL2GrammarParser.COMMA); } + public ITerminalNode COMMA(int i) { + return GetToken(EsperEPL2GrammarParser.COMMA, i); + } + public MatchRecogMeasuresContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_matchRecogMeasures; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterMatchRecogMeasures(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitMatchRecogMeasures(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitMatchRecogMeasures(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public MatchRecogMeasuresContext matchRecogMeasures() { + MatchRecogMeasuresContext _localctx = new MatchRecogMeasuresContext(Context, State); + EnterRule(_localctx, 258, RULE_matchRecogMeasures); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1856; Match(MEASURES); + State = 1857; matchRecogMeasureItem(); + State = 1862; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 1858; Match(COMMA); + State = 1859; matchRecogMeasureItem(); + } + } + State = 1864; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class MatchRecogMeasureItemContext : ParserRuleContext { + public IToken i; + public ExpressionContext expression() { + return GetRuleContext(0); + } + public ITerminalNode AS() { return GetToken(EsperEPL2GrammarParser.AS, 0); } + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public MatchRecogMeasureItemContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_matchRecogMeasureItem; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterMatchRecogMeasureItem(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitMatchRecogMeasureItem(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitMatchRecogMeasureItem(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public MatchRecogMeasureItemContext matchRecogMeasureItem() { + MatchRecogMeasureItemContext _localctx = new MatchRecogMeasureItemContext(Context, State); + EnterRule(_localctx, 260, RULE_matchRecogMeasureItem); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1865; expression(); + State = 1870; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==AS) { + { + State = 1866; Match(AS); + State = 1868; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==IDENT) { + { + State = 1867; _localctx.i = Match(IDENT); + } + } + + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class MatchRecogMatchesSelectionContext : ParserRuleContext { + public ITerminalNode ALL() { return GetToken(EsperEPL2GrammarParser.ALL, 0); } + public ITerminalNode MATCHES() { return GetToken(EsperEPL2GrammarParser.MATCHES, 0); } + public MatchRecogMatchesSelectionContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_matchRecogMatchesSelection; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterMatchRecogMatchesSelection(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitMatchRecogMatchesSelection(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitMatchRecogMatchesSelection(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public MatchRecogMatchesSelectionContext matchRecogMatchesSelection() { + MatchRecogMatchesSelectionContext _localctx = new MatchRecogMatchesSelectionContext(Context, State); + EnterRule(_localctx, 262, RULE_matchRecogMatchesSelection); + try { + EnterOuterAlt(_localctx, 1); + { + State = 1872; Match(ALL); + State = 1873; Match(MATCHES); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class MatchRecogPatternContext : ParserRuleContext { + public ITerminalNode PATTERN() { return GetToken(EsperEPL2GrammarParser.PATTERN, 0); } + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public MatchRecogPatternAlterationContext matchRecogPatternAlteration() { + return GetRuleContext(0); + } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public MatchRecogPatternContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_matchRecogPattern; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterMatchRecogPattern(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitMatchRecogPattern(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitMatchRecogPattern(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public MatchRecogPatternContext matchRecogPattern() { + MatchRecogPatternContext _localctx = new MatchRecogPatternContext(Context, State); + EnterRule(_localctx, 264, RULE_matchRecogPattern); + try { + EnterOuterAlt(_localctx, 1); + { + State = 1875; Match(PATTERN); + State = 1876; Match(LPAREN); + State = 1877; matchRecogPatternAlteration(); + State = 1878; Match(RPAREN); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class MatchRecogMatchesAfterSkipContext : ParserRuleContext { + public KeywordAllowedIdentContext i1; + public KeywordAllowedIdentContext i2; + public KeywordAllowedIdentContext i3; + public KeywordAllowedIdentContext i4; + public KeywordAllowedIdentContext i5; + public ITerminalNode AFTER() { return GetToken(EsperEPL2GrammarParser.AFTER, 0); } + public KeywordAllowedIdentContext[] keywordAllowedIdent() { + return GetRuleContexts(); + } + public KeywordAllowedIdentContext keywordAllowedIdent(int i) { + return GetRuleContext(i); + } + public MatchRecogMatchesAfterSkipContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_matchRecogMatchesAfterSkip; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterMatchRecogMatchesAfterSkip(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitMatchRecogMatchesAfterSkip(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitMatchRecogMatchesAfterSkip(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public MatchRecogMatchesAfterSkipContext matchRecogMatchesAfterSkip() { + MatchRecogMatchesAfterSkipContext _localctx = new MatchRecogMatchesAfterSkipContext(Context, State); + EnterRule(_localctx, 266, RULE_matchRecogMatchesAfterSkip); + try { + EnterOuterAlt(_localctx, 1); + { + State = 1880; Match(AFTER); + State = 1881; _localctx.i1 = keywordAllowedIdent(); + State = 1882; _localctx.i2 = keywordAllowedIdent(); + State = 1883; _localctx.i3 = keywordAllowedIdent(); + State = 1884; _localctx.i4 = keywordAllowedIdent(); + State = 1885; _localctx.i5 = keywordAllowedIdent(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class MatchRecogMatchesIntervalContext : ParserRuleContext { + public IToken i; + public IToken t; + public TimePeriodContext timePeriod() { + return GetRuleContext(0); + } + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public ITerminalNode OR_EXPR() { return GetToken(EsperEPL2GrammarParser.OR_EXPR, 0); } + public ITerminalNode TERMINATED() { return GetToken(EsperEPL2GrammarParser.TERMINATED, 0); } + public MatchRecogMatchesIntervalContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_matchRecogMatchesInterval; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterMatchRecogMatchesInterval(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitMatchRecogMatchesInterval(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitMatchRecogMatchesInterval(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public MatchRecogMatchesIntervalContext matchRecogMatchesInterval() { + MatchRecogMatchesIntervalContext _localctx = new MatchRecogMatchesIntervalContext(Context, State); + EnterRule(_localctx, 268, RULE_matchRecogMatchesInterval); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1887; _localctx.i = Match(IDENT); + State = 1888; timePeriod(); + State = 1891; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==OR_EXPR) { + { + State = 1889; Match(OR_EXPR); + State = 1890; _localctx.t = Match(TERMINATED); + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class MatchRecogPatternAlterationContext : ParserRuleContext { + public IToken o; + public MatchRecogPatternConcatContext[] matchRecogPatternConcat() { + return GetRuleContexts(); + } + public MatchRecogPatternConcatContext matchRecogPatternConcat(int i) { + return GetRuleContext(i); + } + public ITerminalNode[] BOR() { return GetTokens(EsperEPL2GrammarParser.BOR); } + public ITerminalNode BOR(int i) { + return GetToken(EsperEPL2GrammarParser.BOR, i); + } + public MatchRecogPatternAlterationContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_matchRecogPatternAlteration; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterMatchRecogPatternAlteration(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitMatchRecogPatternAlteration(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitMatchRecogPatternAlteration(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public MatchRecogPatternAlterationContext matchRecogPatternAlteration() { + MatchRecogPatternAlterationContext _localctx = new MatchRecogPatternAlterationContext(Context, State); + EnterRule(_localctx, 270, RULE_matchRecogPatternAlteration); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1893; matchRecogPatternConcat(); + State = 1898; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==BOR) { + { + { + State = 1894; _localctx.o = Match(BOR); + State = 1895; matchRecogPatternConcat(); + } + } + State = 1900; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class MatchRecogPatternConcatContext : ParserRuleContext { + public MatchRecogPatternUnaryContext[] matchRecogPatternUnary() { + return GetRuleContexts(); + } + public MatchRecogPatternUnaryContext matchRecogPatternUnary(int i) { + return GetRuleContext(i); + } + public MatchRecogPatternConcatContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_matchRecogPatternConcat; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterMatchRecogPatternConcat(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitMatchRecogPatternConcat(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitMatchRecogPatternConcat(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public MatchRecogPatternConcatContext matchRecogPatternConcat() { + MatchRecogPatternConcatContext _localctx = new MatchRecogPatternConcatContext(Context, State); + EnterRule(_localctx, 272, RULE_matchRecogPatternConcat); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1902; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + do { + { + { + State = 1901; matchRecogPatternUnary(); + } + } + State = 1904; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } while ( _la==MATCH_RECOGNIZE_PERMUTE || _la==LPAREN || _la==IDENT ); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class MatchRecogPatternUnaryContext : ParserRuleContext { + public MatchRecogPatternPermuteContext matchRecogPatternPermute() { + return GetRuleContext(0); + } + public MatchRecogPatternNestedContext matchRecogPatternNested() { + return GetRuleContext(0); + } + public MatchRecogPatternAtomContext matchRecogPatternAtom() { + return GetRuleContext(0); + } + public MatchRecogPatternUnaryContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_matchRecogPatternUnary; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterMatchRecogPatternUnary(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitMatchRecogPatternUnary(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitMatchRecogPatternUnary(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public MatchRecogPatternUnaryContext matchRecogPatternUnary() { + MatchRecogPatternUnaryContext _localctx = new MatchRecogPatternUnaryContext(Context, State); + EnterRule(_localctx, 274, RULE_matchRecogPatternUnary); + try { + State = 1909; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case MATCH_RECOGNIZE_PERMUTE: + EnterOuterAlt(_localctx, 1); + { + State = 1906; matchRecogPatternPermute(); + } + break; + case LPAREN: + EnterOuterAlt(_localctx, 2); + { + State = 1907; matchRecogPatternNested(); + } + break; + case IDENT: + EnterOuterAlt(_localctx, 3); + { + State = 1908; matchRecogPatternAtom(); + } + break; + default: + throw new NoViableAltException(this); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class MatchRecogPatternNestedContext : ParserRuleContext { + public IToken s; + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public MatchRecogPatternAlterationContext matchRecogPatternAlteration() { + return GetRuleContext(0); + } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public MatchRecogPatternRepeatContext matchRecogPatternRepeat() { + return GetRuleContext(0); + } + public ITerminalNode STAR() { return GetToken(EsperEPL2GrammarParser.STAR, 0); } + public ITerminalNode PLUS() { return GetToken(EsperEPL2GrammarParser.PLUS, 0); } + public ITerminalNode QUESTION() { return GetToken(EsperEPL2GrammarParser.QUESTION, 0); } + public MatchRecogPatternNestedContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_matchRecogPatternNested; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterMatchRecogPatternNested(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitMatchRecogPatternNested(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitMatchRecogPatternNested(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public MatchRecogPatternNestedContext matchRecogPatternNested() { + MatchRecogPatternNestedContext _localctx = new MatchRecogPatternNestedContext(Context, State); + EnterRule(_localctx, 276, RULE_matchRecogPatternNested); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1911; Match(LPAREN); + State = 1912; matchRecogPatternAlteration(); + State = 1913; Match(RPAREN); + State = 1917; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case STAR: + { + State = 1914; _localctx.s = Match(STAR); + } + break; + case PLUS: + { + State = 1915; _localctx.s = Match(PLUS); + } + break; + case QUESTION: + { + State = 1916; _localctx.s = Match(QUESTION); + } + break; + case MATCH_RECOGNIZE_PERMUTE: + case LPAREN: + case RPAREN: + case LCURLY: + case COMMA: + case BOR: + case IDENT: + break; + default: + throw new NoViableAltException(this); + } + State = 1920; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==LCURLY) { + { + State = 1919; matchRecogPatternRepeat(); + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class MatchRecogPatternPermuteContext : ParserRuleContext { + public ITerminalNode MATCH_RECOGNIZE_PERMUTE() { return GetToken(EsperEPL2GrammarParser.MATCH_RECOGNIZE_PERMUTE, 0); } + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public MatchRecogPatternAlterationContext[] matchRecogPatternAlteration() { + return GetRuleContexts(); + } + public MatchRecogPatternAlterationContext matchRecogPatternAlteration(int i) { + return GetRuleContext(i); + } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public ITerminalNode[] COMMA() { return GetTokens(EsperEPL2GrammarParser.COMMA); } + public ITerminalNode COMMA(int i) { + return GetToken(EsperEPL2GrammarParser.COMMA, i); + } + public MatchRecogPatternPermuteContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_matchRecogPatternPermute; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterMatchRecogPatternPermute(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitMatchRecogPatternPermute(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitMatchRecogPatternPermute(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public MatchRecogPatternPermuteContext matchRecogPatternPermute() { + MatchRecogPatternPermuteContext _localctx = new MatchRecogPatternPermuteContext(Context, State); + EnterRule(_localctx, 278, RULE_matchRecogPatternPermute); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1922; Match(MATCH_RECOGNIZE_PERMUTE); + State = 1923; Match(LPAREN); + State = 1924; matchRecogPatternAlteration(); + State = 1929; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 1925; Match(COMMA); + State = 1926; matchRecogPatternAlteration(); + } + } + State = 1931; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + State = 1932; Match(RPAREN); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class MatchRecogPatternAtomContext : ParserRuleContext { + public IToken i; + public IToken s; + public IToken reluctant; + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public MatchRecogPatternRepeatContext matchRecogPatternRepeat() { + return GetRuleContext(0); + } + public ITerminalNode STAR() { return GetToken(EsperEPL2GrammarParser.STAR, 0); } + public ITerminalNode PLUS() { return GetToken(EsperEPL2GrammarParser.PLUS, 0); } + public ITerminalNode[] QUESTION() { return GetTokens(EsperEPL2GrammarParser.QUESTION); } + public ITerminalNode QUESTION(int i) { + return GetToken(EsperEPL2GrammarParser.QUESTION, i); + } + public MatchRecogPatternAtomContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_matchRecogPatternAtom; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterMatchRecogPatternAtom(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitMatchRecogPatternAtom(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitMatchRecogPatternAtom(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public MatchRecogPatternAtomContext matchRecogPatternAtom() { + MatchRecogPatternAtomContext _localctx = new MatchRecogPatternAtomContext(Context, State); + EnterRule(_localctx, 280, RULE_matchRecogPatternAtom); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1934; _localctx.i = Match(IDENT); + State = 1943; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (((((_la - 143)) & ~0x3f) == 0 && ((1L << (_la - 143)) & ((1L << (QUESTION - 143)) | (1L << (PLUS - 143)) | (1L << (STAR - 143)))) != 0)) { + { + State = 1938; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case STAR: + { + State = 1935; _localctx.s = Match(STAR); + } + break; + case PLUS: + { + State = 1936; _localctx.s = Match(PLUS); + } + break; + case QUESTION: + { + State = 1937; _localctx.s = Match(QUESTION); + } + break; + default: + throw new NoViableAltException(this); + } + State = 1941; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==QUESTION) { + { + State = 1940; _localctx.reluctant = Match(QUESTION); + } + } + + } + } + + State = 1946; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==LCURLY) { + { + State = 1945; matchRecogPatternRepeat(); + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class MatchRecogPatternRepeatContext : ParserRuleContext { + public ExpressionContext e1; + public IToken comma; + public ExpressionContext e2; + public ITerminalNode LCURLY() { return GetToken(EsperEPL2GrammarParser.LCURLY, 0); } + public ITerminalNode RCURLY() { return GetToken(EsperEPL2GrammarParser.RCURLY, 0); } + public ExpressionContext[] expression() { + return GetRuleContexts(); + } + public ExpressionContext expression(int i) { + return GetRuleContext(i); + } + public ITerminalNode COMMA() { return GetToken(EsperEPL2GrammarParser.COMMA, 0); } + public MatchRecogPatternRepeatContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_matchRecogPatternRepeat; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterMatchRecogPatternRepeat(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitMatchRecogPatternRepeat(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitMatchRecogPatternRepeat(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public MatchRecogPatternRepeatContext matchRecogPatternRepeat() { + MatchRecogPatternRepeatContext _localctx = new MatchRecogPatternRepeatContext(Context, State); + EnterRule(_localctx, 282, RULE_matchRecogPatternRepeat); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1948; Match(LCURLY); + State = 1950; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,248,Context) ) { + case 1: + { + State = 1949; _localctx.e1 = expression(); + } + break; + } + State = 1953; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==COMMA) { + { + State = 1952; _localctx.comma = Match(COMMA); + } + } + + State = 1956; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (((((_la - 2)) & ~0x3f) == 0 && ((1L << (_la - 2)) & ((1L << (WINDOW - 2)) | (1L << (ESCAPE - 2)) | (1L << (NOT_EXPR - 2)) | (1L << (EVERY_EXPR - 2)) | (1L << (SUM - 2)) | (1L << (AVG - 2)) | (1L << (MAX - 2)) | (1L << (MIN - 2)) | (1L << (COALESCE - 2)) | (1L << (MEDIAN - 2)) | (1L << (STDDEV - 2)) | (1L << (AVEDEV - 2)) | (1L << (COUNT - 2)) | (1L << (CASE - 2)) | (1L << (OUTER - 2)) | (1L << (JOIN - 2)) | (1L << (LEFT - 2)) | (1L << (RIGHT - 2)) | (1L << (FULL - 2)) | (1L << (EVENTS - 2)) | (1L << (FIRST - 2)) | (1L << (LAST - 2)) | (1L << (ISTREAM - 2)) | (1L << (SCHEMA - 2)) | (1L << (UNIDIRECTIONAL - 2)) | (1L << (RETAINUNION - 2)) | (1L << (RETAININTERSECTION - 2)) | (1L << (PATTERN - 2)) | (1L << (SQL - 2)) | (1L << (METADATASQL - 2)))) != 0) || ((((_la - 66)) & ~0x3f) == 0 && ((1L << (_la - 66)) & ((1L << (PREVIOUS - 66)) | (1L << (PREVIOUSTAIL - 66)) | (1L << (PREVIOUSCOUNT - 66)) | (1L << (PREVIOUSWINDOW - 66)) | (1L << (PRIOR - 66)) | (1L << (EXISTS - 66)) | (1L << (WEEKDAY - 66)) | (1L << (LW - 66)) | (1L << (INSTANCEOF - 66)) | (1L << (TYPEOF - 66)) | (1L << (CAST - 66)) | (1L << (CURRENT_TIMESTAMP - 66)) | (1L << (SNAPSHOT - 66)) | (1L << (VARIABLE - 66)) | (1L << (TABLE - 66)) | (1L << (UNTIL - 66)) | (1L << (AT - 66)) | (1L << (INDEX - 66)) | (1L << (BOOLEAN_TRUE - 66)) | (1L << (BOOLEAN_FALSE - 66)) | (1L << (VALUE_NULL - 66)) | (1L << (DEFINE - 66)) | (1L << (PARTITION - 66)) | (1L << (MATCHES - 66)) | (1L << (FOR - 66)) | (1L << (WHILE - 66)) | (1L << (USING - 66)) | (1L << (MERGE - 66)) | (1L << (MATCHED - 66)) | (1L << (NEWKW - 66)) | (1L << (CONTEXT - 66)))) != 0) || ((((_la - 134)) & ~0x3f) == 0 && ((1L << (_la - 134)) & ((1L << (GROUPING - 134)) | (1L << (GROUPING_ID - 134)) | (1L << (QUESTION - 134)) | (1L << (LPAREN - 134)) | (1L << (LCURLY - 134)) | (1L << (PLUS - 134)) | (1L << (MINUS - 134)) | (1L << (TICKED_STRING_LITERAL - 134)) | (1L << (QUOTED_STRING_LITERAL - 134)) | (1L << (STRING_LITERAL - 134)) | (1L << (IDENT - 134)) | (1L << (IntegerLiteral - 134)) | (1L << (FloatingPointLiteral - 134)))) != 0)) { + { + State = 1955; _localctx.e2 = expression(); + } + } + + State = 1958; Match(RCURLY); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class MatchRecogDefineContext : ParserRuleContext { + public ITerminalNode DEFINE() { return GetToken(EsperEPL2GrammarParser.DEFINE, 0); } + public MatchRecogDefineItemContext[] matchRecogDefineItem() { + return GetRuleContexts(); + } + public MatchRecogDefineItemContext matchRecogDefineItem(int i) { + return GetRuleContext(i); + } + public ITerminalNode[] COMMA() { return GetTokens(EsperEPL2GrammarParser.COMMA); } + public ITerminalNode COMMA(int i) { + return GetToken(EsperEPL2GrammarParser.COMMA, i); + } + public MatchRecogDefineContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_matchRecogDefine; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterMatchRecogDefine(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitMatchRecogDefine(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitMatchRecogDefine(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public MatchRecogDefineContext matchRecogDefine() { + MatchRecogDefineContext _localctx = new MatchRecogDefineContext(Context, State); + EnterRule(_localctx, 284, RULE_matchRecogDefine); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1960; Match(DEFINE); + State = 1961; matchRecogDefineItem(); + State = 1966; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 1962; Match(COMMA); + State = 1963; matchRecogDefineItem(); + } + } + State = 1968; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class MatchRecogDefineItemContext : ParserRuleContext { + public IToken i; + public ITerminalNode AS() { return GetToken(EsperEPL2GrammarParser.AS, 0); } + public ExpressionContext expression() { + return GetRuleContext(0); + } + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public MatchRecogDefineItemContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_matchRecogDefineItem; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterMatchRecogDefineItem(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitMatchRecogDefineItem(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitMatchRecogDefineItem(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public MatchRecogDefineItemContext matchRecogDefineItem() { + MatchRecogDefineItemContext _localctx = new MatchRecogDefineItemContext(Context, State); + EnterRule(_localctx, 286, RULE_matchRecogDefineItem); + try { + EnterOuterAlt(_localctx, 1); + { + State = 1969; _localctx.i = Match(IDENT); + State = 1970; Match(AS); + State = 1971; expression(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class ExpressionContext : ParserRuleContext { + public CaseExpressionContext caseExpression() { + return GetRuleContext(0); + } + public ExpressionContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_expression; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterExpression(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitExpression(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitExpression(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public ExpressionContext expression() { + ExpressionContext _localctx = new ExpressionContext(Context, State); + EnterRule(_localctx, 288, RULE_expression); + try { + EnterOuterAlt(_localctx, 1); + { + State = 1973; caseExpression(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class CaseExpressionContext : ParserRuleContext { + public ITerminalNode CASE() { return GetToken(EsperEPL2GrammarParser.CASE, 0); } + public ITerminalNode END() { return GetToken(EsperEPL2GrammarParser.END, 0); } + public WhenClauseContext[] whenClause() { + return GetRuleContexts(); + } + public WhenClauseContext whenClause(int i) { + return GetRuleContext(i); + } + public ElseClauseContext elseClause() { + return GetRuleContext(0); + } + public ExpressionContext expression() { + return GetRuleContext(0); + } + public EvalOrExpressionContext evalOrExpression() { + return GetRuleContext(0); + } + public CaseExpressionContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_caseExpression; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterCaseExpression(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitCaseExpression(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitCaseExpression(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public CaseExpressionContext caseExpression() { + CaseExpressionContext _localctx = new CaseExpressionContext(Context, State); + EnterRule(_localctx, 290, RULE_caseExpression); + int _la; + try { + State = 2003; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,256,Context) ) { + case 1: + EnterOuterAlt(_localctx, 1); + { + paraphrases.Push("case expression"); + State = 1976; Match(CASE); + State = 1978; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + do { + { + { + State = 1977; whenClause(); + } + } + State = 1980; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } while ( _la==WHEN ); + State = 1983; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==ELSE) { + { + State = 1982; elseClause(); + } + } + + State = 1985; Match(END); + paraphrases.Pop(); + } + break; + case 2: + EnterOuterAlt(_localctx, 2); + { + paraphrases.Push("case expression"); + State = 1989; Match(CASE); + State = 1990; expression(); + State = 1992; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + do { + { + { + State = 1991; whenClause(); + } + } + State = 1994; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } while ( _la==WHEN ); + State = 1997; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==ELSE) { + { + State = 1996; elseClause(); + } + } + + State = 1999; Match(END); + paraphrases.Pop(); + } + break; + case 3: + EnterOuterAlt(_localctx, 3); + { + State = 2002; evalOrExpression(); + } + break; + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class EvalOrExpressionContext : ParserRuleContext { + public IToken op; + public EvalAndExpressionContext[] evalAndExpression() { + return GetRuleContexts(); + } + public EvalAndExpressionContext evalAndExpression(int i) { + return GetRuleContext(i); + } + public ITerminalNode[] OR_EXPR() { return GetTokens(EsperEPL2GrammarParser.OR_EXPR); } + public ITerminalNode OR_EXPR(int i) { + return GetToken(EsperEPL2GrammarParser.OR_EXPR, i); + } + public EvalOrExpressionContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_evalOrExpression; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterEvalOrExpression(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitEvalOrExpression(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitEvalOrExpression(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public EvalOrExpressionContext evalOrExpression() { + EvalOrExpressionContext _localctx = new EvalOrExpressionContext(Context, State); + EnterRule(_localctx, 292, RULE_evalOrExpression); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 2005; evalAndExpression(); + State = 2010; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==OR_EXPR) { + { + { + State = 2006; _localctx.op = Match(OR_EXPR); + State = 2007; evalAndExpression(); + } + } + State = 2012; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class EvalAndExpressionContext : ParserRuleContext { + public IToken op; + public BitWiseExpressionContext[] bitWiseExpression() { + return GetRuleContexts(); + } + public BitWiseExpressionContext bitWiseExpression(int i) { + return GetRuleContext(i); + } + public ITerminalNode[] AND_EXPR() { return GetTokens(EsperEPL2GrammarParser.AND_EXPR); } + public ITerminalNode AND_EXPR(int i) { + return GetToken(EsperEPL2GrammarParser.AND_EXPR, i); + } + public EvalAndExpressionContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_evalAndExpression; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterEvalAndExpression(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitEvalAndExpression(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitEvalAndExpression(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public EvalAndExpressionContext evalAndExpression() { + EvalAndExpressionContext _localctx = new EvalAndExpressionContext(Context, State); + EnterRule(_localctx, 294, RULE_evalAndExpression); + try { + int _alt; + EnterOuterAlt(_localctx, 1); + { + State = 2013; bitWiseExpression(); + State = 2018; + ErrorHandler.Sync(this); + _alt = Interpreter.AdaptivePredict(TokenStream,258,Context); + while ( _alt!=2 && _alt!=global::Antlr4.Runtime.Atn.ATN.INVALID_ALT_NUMBER ) { + if ( _alt==1 ) { + { + { + State = 2014; _localctx.op = Match(AND_EXPR); + State = 2015; bitWiseExpression(); + } + } + } + State = 2020; + ErrorHandler.Sync(this); + _alt = Interpreter.AdaptivePredict(TokenStream,258,Context); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class BitWiseExpressionContext : ParserRuleContext { + public NegatedExpressionContext[] negatedExpression() { + return GetRuleContexts(); + } + public NegatedExpressionContext negatedExpression(int i) { + return GetRuleContext(i); + } + public ITerminalNode[] BAND() { return GetTokens(EsperEPL2GrammarParser.BAND); } + public ITerminalNode BAND(int i) { + return GetToken(EsperEPL2GrammarParser.BAND, i); + } + public ITerminalNode[] BOR() { return GetTokens(EsperEPL2GrammarParser.BOR); } + public ITerminalNode BOR(int i) { + return GetToken(EsperEPL2GrammarParser.BOR, i); + } + public ITerminalNode[] BXOR() { return GetTokens(EsperEPL2GrammarParser.BXOR); } + public ITerminalNode BXOR(int i) { + return GetToken(EsperEPL2GrammarParser.BXOR, i); + } + public BitWiseExpressionContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_bitWiseExpression; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterBitWiseExpression(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitBitWiseExpression(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitBitWiseExpression(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public BitWiseExpressionContext bitWiseExpression() { + BitWiseExpressionContext _localctx = new BitWiseExpressionContext(Context, State); + EnterRule(_localctx, 296, RULE_bitWiseExpression); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 2021; negatedExpression(); + State = 2026; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (((((_la - 172)) & ~0x3f) == 0 && ((1L << (_la - 172)) & ((1L << (BXOR - 172)) | (1L << (BOR - 172)) | (1L << (BAND - 172)))) != 0)) { + { + { + State = 2022; + _la = TokenStream.LA(1); + if ( !(((((_la - 172)) & ~0x3f) == 0 && ((1L << (_la - 172)) & ((1L << (BXOR - 172)) | (1L << (BOR - 172)) | (1L << (BAND - 172)))) != 0)) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + State = 2023; negatedExpression(); + } + } + State = 2028; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class NegatedExpressionContext : ParserRuleContext { + public EvalEqualsExpressionContext evalEqualsExpression() { + return GetRuleContext(0); + } + public ITerminalNode NOT_EXPR() { return GetToken(EsperEPL2GrammarParser.NOT_EXPR, 0); } + public NegatedExpressionContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_negatedExpression; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterNegatedExpression(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitNegatedExpression(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitNegatedExpression(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public NegatedExpressionContext negatedExpression() { + NegatedExpressionContext _localctx = new NegatedExpressionContext(Context, State); + EnterRule(_localctx, 298, RULE_negatedExpression); + try { + State = 2032; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case WINDOW: + case ESCAPE: + case EVERY_EXPR: + case SUM: + case AVG: + case MAX: + case MIN: + case COALESCE: + case MEDIAN: + case STDDEV: + case AVEDEV: + case COUNT: + case OUTER: + case JOIN: + case LEFT: + case RIGHT: + case FULL: + case EVENTS: + case FIRST: + case LAST: + case ISTREAM: + case SCHEMA: + case UNIDIRECTIONAL: + case RETAINUNION: + case RETAININTERSECTION: + case PATTERN: + case SQL: + case METADATASQL: + case PREVIOUS: + case PREVIOUSTAIL: + case PREVIOUSCOUNT: + case PREVIOUSWINDOW: + case PRIOR: + case EXISTS: + case WEEKDAY: + case LW: + case INSTANCEOF: + case TYPEOF: + case CAST: + case CURRENT_TIMESTAMP: + case SNAPSHOT: + case VARIABLE: + case TABLE: + case UNTIL: + case AT: + case INDEX: + case BOOLEAN_TRUE: + case BOOLEAN_FALSE: + case VALUE_NULL: + case DEFINE: + case PARTITION: + case MATCHES: + case FOR: + case WHILE: + case USING: + case MERGE: + case MATCHED: + case NEWKW: + case CONTEXT: + case GROUPING: + case GROUPING_ID: + case QUESTION: + case LPAREN: + case LCURLY: + case PLUS: + case MINUS: + case TICKED_STRING_LITERAL: + case QUOTED_STRING_LITERAL: + case STRING_LITERAL: + case IDENT: + case IntegerLiteral: + case FloatingPointLiteral: + EnterOuterAlt(_localctx, 1); + { + State = 2029; evalEqualsExpression(); + } + break; + case NOT_EXPR: + EnterOuterAlt(_localctx, 2); + { + State = 2030; Match(NOT_EXPR); + State = 2031; evalEqualsExpression(); + } + break; + default: + throw new NoViableAltException(this); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class EvalEqualsExpressionContext : ParserRuleContext { + public IToken eq; + public IToken @is; + public IToken isnot; + public IToken sqlne; + public IToken ne; + public IToken a; + public EvalRelationalExpressionContext[] evalRelationalExpression() { + return GetRuleContexts(); + } + public EvalRelationalExpressionContext evalRelationalExpression(int i) { + return GetRuleContext(i); + } + public ITerminalNode[] NOT_EXPR() { return GetTokens(EsperEPL2GrammarParser.NOT_EXPR); } + public ITerminalNode NOT_EXPR(int i) { + return GetToken(EsperEPL2GrammarParser.NOT_EXPR, i); + } + public ITerminalNode[] EQUALS() { return GetTokens(EsperEPL2GrammarParser.EQUALS); } + public ITerminalNode EQUALS(int i) { + return GetToken(EsperEPL2GrammarParser.EQUALS, i); + } + public ITerminalNode[] IS() { return GetTokens(EsperEPL2GrammarParser.IS); } + public ITerminalNode IS(int i) { + return GetToken(EsperEPL2GrammarParser.IS, i); + } + public ITerminalNode[] SQL_NE() { return GetTokens(EsperEPL2GrammarParser.SQL_NE); } + public ITerminalNode SQL_NE(int i) { + return GetToken(EsperEPL2GrammarParser.SQL_NE, i); + } + public ITerminalNode[] NOT_EQUAL() { return GetTokens(EsperEPL2GrammarParser.NOT_EQUAL); } + public ITerminalNode NOT_EQUAL(int i) { + return GetToken(EsperEPL2GrammarParser.NOT_EQUAL, i); + } + public SubSelectGroupExpressionContext[] subSelectGroupExpression() { + return GetRuleContexts(); + } + public SubSelectGroupExpressionContext subSelectGroupExpression(int i) { + return GetRuleContext(i); + } + public ITerminalNode[] ANY() { return GetTokens(EsperEPL2GrammarParser.ANY); } + public ITerminalNode ANY(int i) { + return GetToken(EsperEPL2GrammarParser.ANY, i); + } + public ITerminalNode[] SOME() { return GetTokens(EsperEPL2GrammarParser.SOME); } + public ITerminalNode SOME(int i) { + return GetToken(EsperEPL2GrammarParser.SOME, i); + } + public ITerminalNode[] ALL() { return GetTokens(EsperEPL2GrammarParser.ALL); } + public ITerminalNode ALL(int i) { + return GetToken(EsperEPL2GrammarParser.ALL, i); + } + public ITerminalNode[] LPAREN() { return GetTokens(EsperEPL2GrammarParser.LPAREN); } + public ITerminalNode LPAREN(int i) { + return GetToken(EsperEPL2GrammarParser.LPAREN, i); + } + public ITerminalNode[] RPAREN() { return GetTokens(EsperEPL2GrammarParser.RPAREN); } + public ITerminalNode RPAREN(int i) { + return GetToken(EsperEPL2GrammarParser.RPAREN, i); + } + public ExpressionListContext[] expressionList() { + return GetRuleContexts(); + } + public ExpressionListContext expressionList(int i) { + return GetRuleContext(i); + } + public EvalEqualsExpressionContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_evalEqualsExpression; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterEvalEqualsExpression(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitEvalEqualsExpression(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitEvalEqualsExpression(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public EvalEqualsExpressionContext evalEqualsExpression() { + EvalEqualsExpressionContext _localctx = new EvalEqualsExpressionContext(Context, State); + EnterRule(_localctx, 300, RULE_evalEqualsExpression); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 2034; evalRelationalExpression(); + State = 2061; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==IS || ((((_la - 141)) & ~0x3f) == 0 && ((1L << (_la - 141)) & ((1L << (EQUALS - 141)) | (1L << (SQL_NE - 141)) | (1L << (NOT_EQUAL - 141)))) != 0)) { + { + { + State = 2041; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,261,Context) ) { + case 1: + { + State = 2035; _localctx.eq = Match(EQUALS); + } + break; + case 2: + { + State = 2036; _localctx.@is = Match(IS); + } + break; + case 3: + { + State = 2037; _localctx.isnot = Match(IS); + State = 2038; Match(NOT_EXPR); + } + break; + case 4: + { + State = 2039; _localctx.sqlne = Match(SQL_NE); + } + break; + case 5: + { + State = 2040; _localctx.ne = Match(NOT_EQUAL); + } + break; + } + State = 2057; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case WINDOW: + case ESCAPE: + case EVERY_EXPR: + case SUM: + case AVG: + case MAX: + case MIN: + case COALESCE: + case MEDIAN: + case STDDEV: + case AVEDEV: + case COUNT: + case OUTER: + case JOIN: + case LEFT: + case RIGHT: + case FULL: + case EVENTS: + case FIRST: + case LAST: + case ISTREAM: + case SCHEMA: + case UNIDIRECTIONAL: + case RETAINUNION: + case RETAININTERSECTION: + case PATTERN: + case SQL: + case METADATASQL: + case PREVIOUS: + case PREVIOUSTAIL: + case PREVIOUSCOUNT: + case PREVIOUSWINDOW: + case PRIOR: + case EXISTS: + case WEEKDAY: + case LW: + case INSTANCEOF: + case TYPEOF: + case CAST: + case CURRENT_TIMESTAMP: + case SNAPSHOT: + case VARIABLE: + case TABLE: + case UNTIL: + case AT: + case INDEX: + case BOOLEAN_TRUE: + case BOOLEAN_FALSE: + case VALUE_NULL: + case DEFINE: + case PARTITION: + case MATCHES: + case FOR: + case WHILE: + case USING: + case MERGE: + case MATCHED: + case NEWKW: + case CONTEXT: + case GROUPING: + case GROUPING_ID: + case QUESTION: + case LPAREN: + case LCURLY: + case PLUS: + case MINUS: + case TICKED_STRING_LITERAL: + case QUOTED_STRING_LITERAL: + case STRING_LITERAL: + case IDENT: + case IntegerLiteral: + case FloatingPointLiteral: + { + State = 2043; evalRelationalExpression(); + } + break; + case ALL: + case ANY: + case SOME: + { + State = 2047; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case ANY: + { + State = 2044; _localctx.a = Match(ANY); + } + break; + case SOME: + { + State = 2045; _localctx.a = Match(SOME); + } + break; + case ALL: + { + State = 2046; _localctx.a = Match(ALL); + } + break; + default: + throw new NoViableAltException(this); + } + State = 2055; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,264,Context) ) { + case 1: + { + { + State = 2049; Match(LPAREN); + State = 2051; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (((((_la - 2)) & ~0x3f) == 0 && ((1L << (_la - 2)) & ((1L << (WINDOW - 2)) | (1L << (ESCAPE - 2)) | (1L << (NOT_EXPR - 2)) | (1L << (EVERY_EXPR - 2)) | (1L << (SUM - 2)) | (1L << (AVG - 2)) | (1L << (MAX - 2)) | (1L << (MIN - 2)) | (1L << (COALESCE - 2)) | (1L << (MEDIAN - 2)) | (1L << (STDDEV - 2)) | (1L << (AVEDEV - 2)) | (1L << (COUNT - 2)) | (1L << (CASE - 2)) | (1L << (OUTER - 2)) | (1L << (JOIN - 2)) | (1L << (LEFT - 2)) | (1L << (RIGHT - 2)) | (1L << (FULL - 2)) | (1L << (EVENTS - 2)) | (1L << (FIRST - 2)) | (1L << (LAST - 2)) | (1L << (ISTREAM - 2)) | (1L << (SCHEMA - 2)) | (1L << (UNIDIRECTIONAL - 2)) | (1L << (RETAINUNION - 2)) | (1L << (RETAININTERSECTION - 2)) | (1L << (PATTERN - 2)) | (1L << (SQL - 2)) | (1L << (METADATASQL - 2)))) != 0) || ((((_la - 66)) & ~0x3f) == 0 && ((1L << (_la - 66)) & ((1L << (PREVIOUS - 66)) | (1L << (PREVIOUSTAIL - 66)) | (1L << (PREVIOUSCOUNT - 66)) | (1L << (PREVIOUSWINDOW - 66)) | (1L << (PRIOR - 66)) | (1L << (EXISTS - 66)) | (1L << (WEEKDAY - 66)) | (1L << (LW - 66)) | (1L << (INSTANCEOF - 66)) | (1L << (TYPEOF - 66)) | (1L << (CAST - 66)) | (1L << (CURRENT_TIMESTAMP - 66)) | (1L << (SNAPSHOT - 66)) | (1L << (VARIABLE - 66)) | (1L << (TABLE - 66)) | (1L << (UNTIL - 66)) | (1L << (AT - 66)) | (1L << (INDEX - 66)) | (1L << (BOOLEAN_TRUE - 66)) | (1L << (BOOLEAN_FALSE - 66)) | (1L << (VALUE_NULL - 66)) | (1L << (DEFINE - 66)) | (1L << (PARTITION - 66)) | (1L << (MATCHES - 66)) | (1L << (FOR - 66)) | (1L << (WHILE - 66)) | (1L << (USING - 66)) | (1L << (MERGE - 66)) | (1L << (MATCHED - 66)) | (1L << (NEWKW - 66)) | (1L << (CONTEXT - 66)))) != 0) || ((((_la - 134)) & ~0x3f) == 0 && ((1L << (_la - 134)) & ((1L << (GROUPING - 134)) | (1L << (GROUPING_ID - 134)) | (1L << (QUESTION - 134)) | (1L << (LPAREN - 134)) | (1L << (LCURLY - 134)) | (1L << (PLUS - 134)) | (1L << (MINUS - 134)) | (1L << (TICKED_STRING_LITERAL - 134)) | (1L << (QUOTED_STRING_LITERAL - 134)) | (1L << (STRING_LITERAL - 134)) | (1L << (IDENT - 134)) | (1L << (IntegerLiteral - 134)) | (1L << (FloatingPointLiteral - 134)))) != 0)) { + { + State = 2050; expressionList(); + } + } + + State = 2053; Match(RPAREN); + } + } + break; + case 2: + { + State = 2054; subSelectGroupExpression(); + } + break; + } + } + break; + default: + throw new NoViableAltException(this); + } + } + } + State = 2063; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class EvalRelationalExpressionContext : ParserRuleContext { + public IToken r; + public IToken g; + public IToken n; + public IToken @in; + public IToken l; + public IToken col; + public IToken inset; + public IToken between; + public IToken like; + public IToken regex; + public ConcatenationExprContext[] concatenationExpr() { + return GetRuleContexts(); + } + public ConcatenationExprContext concatenationExpr(int i) { + return GetRuleContext(i); + } + public InSubSelectQueryContext inSubSelectQuery() { + return GetRuleContext(0); + } + public BetweenListContext betweenList() { + return GetRuleContext(0); + } + public ITerminalNode IN_SET() { return GetToken(EsperEPL2GrammarParser.IN_SET, 0); } + public ITerminalNode BETWEEN() { return GetToken(EsperEPL2GrammarParser.BETWEEN, 0); } + public ITerminalNode LIKE() { return GetToken(EsperEPL2GrammarParser.LIKE, 0); } + public ITerminalNode REGEXP() { return GetToken(EsperEPL2GrammarParser.REGEXP, 0); } + public ITerminalNode NOT_EXPR() { return GetToken(EsperEPL2GrammarParser.NOT_EXPR, 0); } + public ExpressionContext[] expression() { + return GetRuleContexts(); + } + public ExpressionContext expression(int i) { + return GetRuleContext(i); + } + public ITerminalNode ESCAPE() { return GetToken(EsperEPL2GrammarParser.ESCAPE, 0); } + public StringconstantContext stringconstant() { + return GetRuleContext(0); + } + public ITerminalNode[] LPAREN() { return GetTokens(EsperEPL2GrammarParser.LPAREN); } + public ITerminalNode LPAREN(int i) { + return GetToken(EsperEPL2GrammarParser.LPAREN, i); + } + public ITerminalNode LBRACK() { return GetToken(EsperEPL2GrammarParser.LBRACK, 0); } + public ITerminalNode[] RPAREN() { return GetTokens(EsperEPL2GrammarParser.RPAREN); } + public ITerminalNode RPAREN(int i) { + return GetToken(EsperEPL2GrammarParser.RPAREN, i); + } + public ITerminalNode RBRACK() { return GetToken(EsperEPL2GrammarParser.RBRACK, 0); } + public ITerminalNode[] LT() { return GetTokens(EsperEPL2GrammarParser.LT); } + public ITerminalNode LT(int i) { + return GetToken(EsperEPL2GrammarParser.LT, i); + } + public ITerminalNode[] GT() { return GetTokens(EsperEPL2GrammarParser.GT); } + public ITerminalNode GT(int i) { + return GetToken(EsperEPL2GrammarParser.GT, i); + } + public ITerminalNode[] LE() { return GetTokens(EsperEPL2GrammarParser.LE); } + public ITerminalNode LE(int i) { + return GetToken(EsperEPL2GrammarParser.LE, i); + } + public ITerminalNode[] GE() { return GetTokens(EsperEPL2GrammarParser.GE); } + public ITerminalNode GE(int i) { + return GetToken(EsperEPL2GrammarParser.GE, i); + } + public SubSelectGroupExpressionContext[] subSelectGroupExpression() { + return GetRuleContexts(); + } + public SubSelectGroupExpressionContext subSelectGroupExpression(int i) { + return GetRuleContext(i); + } + public ITerminalNode COLON() { return GetToken(EsperEPL2GrammarParser.COLON, 0); } + public ITerminalNode[] ANY() { return GetTokens(EsperEPL2GrammarParser.ANY); } + public ITerminalNode ANY(int i) { + return GetToken(EsperEPL2GrammarParser.ANY, i); + } + public ITerminalNode[] SOME() { return GetTokens(EsperEPL2GrammarParser.SOME); } + public ITerminalNode SOME(int i) { + return GetToken(EsperEPL2GrammarParser.SOME, i); + } + public ITerminalNode[] ALL() { return GetTokens(EsperEPL2GrammarParser.ALL); } + public ITerminalNode ALL(int i) { + return GetToken(EsperEPL2GrammarParser.ALL, i); + } + public ITerminalNode[] COMMA() { return GetTokens(EsperEPL2GrammarParser.COMMA); } + public ITerminalNode COMMA(int i) { + return GetToken(EsperEPL2GrammarParser.COMMA, i); + } + public ExpressionListContext[] expressionList() { + return GetRuleContexts(); + } + public ExpressionListContext expressionList(int i) { + return GetRuleContext(i); + } + public EvalRelationalExpressionContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_evalRelationalExpression; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterEvalRelationalExpression(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitEvalRelationalExpression(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitEvalRelationalExpression(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public EvalRelationalExpressionContext evalRelationalExpression() { + EvalRelationalExpressionContext _localctx = new EvalRelationalExpressionContext(Context, State); + EnterRule(_localctx, 302, RULE_evalRelationalExpression); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 2064; concatenationExpr(); + State = 2130; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,280,Context) ) { + case 1: + { + { + State = 2089; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (((((_la - 168)) & ~0x3f) == 0 && ((1L << (_la - 168)) & ((1L << (GE - 168)) | (1L << (GT - 168)) | (1L << (LE - 168)) | (1L << (LT - 168)))) != 0)) { + { + { + State = 2069; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case LT: + { + State = 2065; _localctx.r = Match(LT); + } + break; + case GT: + { + State = 2066; _localctx.r = Match(GT); + } + break; + case LE: + { + State = 2067; _localctx.r = Match(LE); + } + break; + case GE: + { + State = 2068; _localctx.r = Match(GE); + } + break; + default: + throw new NoViableAltException(this); + } + State = 2085; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case WINDOW: + case ESCAPE: + case EVERY_EXPR: + case SUM: + case AVG: + case MAX: + case MIN: + case COALESCE: + case MEDIAN: + case STDDEV: + case AVEDEV: + case COUNT: + case OUTER: + case JOIN: + case LEFT: + case RIGHT: + case FULL: + case EVENTS: + case FIRST: + case LAST: + case ISTREAM: + case SCHEMA: + case UNIDIRECTIONAL: + case RETAINUNION: + case RETAININTERSECTION: + case PATTERN: + case SQL: + case METADATASQL: + case PREVIOUS: + case PREVIOUSTAIL: + case PREVIOUSCOUNT: + case PREVIOUSWINDOW: + case PRIOR: + case EXISTS: + case WEEKDAY: + case LW: + case INSTANCEOF: + case TYPEOF: + case CAST: + case CURRENT_TIMESTAMP: + case SNAPSHOT: + case VARIABLE: + case TABLE: + case UNTIL: + case AT: + case INDEX: + case BOOLEAN_TRUE: + case BOOLEAN_FALSE: + case VALUE_NULL: + case DEFINE: + case PARTITION: + case MATCHES: + case FOR: + case WHILE: + case USING: + case MERGE: + case MATCHED: + case NEWKW: + case CONTEXT: + case GROUPING: + case GROUPING_ID: + case QUESTION: + case LPAREN: + case LCURLY: + case PLUS: + case MINUS: + case TICKED_STRING_LITERAL: + case QUOTED_STRING_LITERAL: + case STRING_LITERAL: + case IDENT: + case IntegerLiteral: + case FloatingPointLiteral: + { + State = 2071; concatenationExpr(); + } + break; + case ALL: + case ANY: + case SOME: + { + State = 2075; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case ANY: + { + State = 2072; _localctx.g = Match(ANY); + } + break; + case SOME: + { + State = 2073; _localctx.g = Match(SOME); + } + break; + case ALL: + { + State = 2074; _localctx.g = Match(ALL); + } + break; + default: + throw new NoViableAltException(this); + } + State = 2083; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,270,Context) ) { + case 1: + { + { + State = 2077; Match(LPAREN); + State = 2079; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (((((_la - 2)) & ~0x3f) == 0 && ((1L << (_la - 2)) & ((1L << (WINDOW - 2)) | (1L << (ESCAPE - 2)) | (1L << (NOT_EXPR - 2)) | (1L << (EVERY_EXPR - 2)) | (1L << (SUM - 2)) | (1L << (AVG - 2)) | (1L << (MAX - 2)) | (1L << (MIN - 2)) | (1L << (COALESCE - 2)) | (1L << (MEDIAN - 2)) | (1L << (STDDEV - 2)) | (1L << (AVEDEV - 2)) | (1L << (COUNT - 2)) | (1L << (CASE - 2)) | (1L << (OUTER - 2)) | (1L << (JOIN - 2)) | (1L << (LEFT - 2)) | (1L << (RIGHT - 2)) | (1L << (FULL - 2)) | (1L << (EVENTS - 2)) | (1L << (FIRST - 2)) | (1L << (LAST - 2)) | (1L << (ISTREAM - 2)) | (1L << (SCHEMA - 2)) | (1L << (UNIDIRECTIONAL - 2)) | (1L << (RETAINUNION - 2)) | (1L << (RETAININTERSECTION - 2)) | (1L << (PATTERN - 2)) | (1L << (SQL - 2)) | (1L << (METADATASQL - 2)))) != 0) || ((((_la - 66)) & ~0x3f) == 0 && ((1L << (_la - 66)) & ((1L << (PREVIOUS - 66)) | (1L << (PREVIOUSTAIL - 66)) | (1L << (PREVIOUSCOUNT - 66)) | (1L << (PREVIOUSWINDOW - 66)) | (1L << (PRIOR - 66)) | (1L << (EXISTS - 66)) | (1L << (WEEKDAY - 66)) | (1L << (LW - 66)) | (1L << (INSTANCEOF - 66)) | (1L << (TYPEOF - 66)) | (1L << (CAST - 66)) | (1L << (CURRENT_TIMESTAMP - 66)) | (1L << (SNAPSHOT - 66)) | (1L << (VARIABLE - 66)) | (1L << (TABLE - 66)) | (1L << (UNTIL - 66)) | (1L << (AT - 66)) | (1L << (INDEX - 66)) | (1L << (BOOLEAN_TRUE - 66)) | (1L << (BOOLEAN_FALSE - 66)) | (1L << (VALUE_NULL - 66)) | (1L << (DEFINE - 66)) | (1L << (PARTITION - 66)) | (1L << (MATCHES - 66)) | (1L << (FOR - 66)) | (1L << (WHILE - 66)) | (1L << (USING - 66)) | (1L << (MERGE - 66)) | (1L << (MATCHED - 66)) | (1L << (NEWKW - 66)) | (1L << (CONTEXT - 66)))) != 0) || ((((_la - 134)) & ~0x3f) == 0 && ((1L << (_la - 134)) & ((1L << (GROUPING - 134)) | (1L << (GROUPING_ID - 134)) | (1L << (QUESTION - 134)) | (1L << (LPAREN - 134)) | (1L << (LCURLY - 134)) | (1L << (PLUS - 134)) | (1L << (MINUS - 134)) | (1L << (TICKED_STRING_LITERAL - 134)) | (1L << (QUOTED_STRING_LITERAL - 134)) | (1L << (STRING_LITERAL - 134)) | (1L << (IDENT - 134)) | (1L << (IntegerLiteral - 134)) | (1L << (FloatingPointLiteral - 134)))) != 0)) { + { + State = 2078; expressionList(); + } + } + + State = 2081; Match(RPAREN); + } + } + break; + case 2: + { + State = 2082; subSelectGroupExpression(); + } + break; + } + } + break; + default: + throw new NoViableAltException(this); + } + } + } + State = 2091; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + break; + case 2: + { + State = 2093; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==NOT_EXPR) { + { + State = 2092; _localctx.n = Match(NOT_EXPR); + } + } + + State = 2128; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,279,Context) ) { + case 1: + { + { + State = 2095; _localctx.@in = Match(IN_SET); + State = 2098; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case LPAREN: + { + State = 2096; _localctx.l = Match(LPAREN); + } + break; + case LBRACK: + { + State = 2097; _localctx.l = Match(LBRACK); + } + break; + default: + throw new NoViableAltException(this); + } + State = 2100; expression(); + State = 2110; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case COLON: + { + { + State = 2101; _localctx.col = Match(COLON); + { + State = 2102; expression(); + } + } + } + break; + case RPAREN: + case RBRACK: + case COMMA: + { + { + State = 2107; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 2103; Match(COMMA); + State = 2104; expression(); + } + } + State = 2109; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + break; + default: + throw new NoViableAltException(this); + } + State = 2114; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case RPAREN: + { + State = 2112; _localctx.r = Match(RPAREN); + } + break; + case RBRACK: + { + State = 2113; _localctx.r = Match(RBRACK); + } + break; + default: + throw new NoViableAltException(this); + } + } + } + break; + case 2: + { + State = 2116; _localctx.inset = Match(IN_SET); + State = 2117; inSubSelectQuery(); + } + break; + case 3: + { + State = 2118; _localctx.between = Match(BETWEEN); + State = 2119; betweenList(); + } + break; + case 4: + { + State = 2120; _localctx.like = Match(LIKE); + State = 2121; concatenationExpr(); + State = 2124; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,278,Context) ) { + case 1: + { + State = 2122; Match(ESCAPE); + State = 2123; stringconstant(); + } + break; + } + } + break; + case 5: + { + State = 2126; _localctx.regex = Match(REGEXP); + State = 2127; concatenationExpr(); + } + break; + } + } + break; + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class InSubSelectQueryContext : ParserRuleContext { + public SubQueryExprContext subQueryExpr() { + return GetRuleContext(0); + } + public InSubSelectQueryContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_inSubSelectQuery; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterInSubSelectQuery(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitInSubSelectQuery(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitInSubSelectQuery(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public InSubSelectQueryContext inSubSelectQuery() { + InSubSelectQueryContext _localctx = new InSubSelectQueryContext(Context, State); + EnterRule(_localctx, 304, RULE_inSubSelectQuery); + try { + EnterOuterAlt(_localctx, 1); + { + State = 2132; subQueryExpr(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class ConcatenationExprContext : ParserRuleContext { + public IToken c; + public AdditiveExpressionContext[] additiveExpression() { + return GetRuleContexts(); + } + public AdditiveExpressionContext additiveExpression(int i) { + return GetRuleContext(i); + } + public ITerminalNode[] LOR() { return GetTokens(EsperEPL2GrammarParser.LOR); } + public ITerminalNode LOR(int i) { + return GetToken(EsperEPL2GrammarParser.LOR, i); + } + public ConcatenationExprContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_concatenationExpr; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterConcatenationExpr(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitConcatenationExpr(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitConcatenationExpr(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public ConcatenationExprContext concatenationExpr() { + ConcatenationExprContext _localctx = new ConcatenationExprContext(Context, State); + EnterRule(_localctx, 306, RULE_concatenationExpr); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 2134; additiveExpression(); + State = 2144; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==LOR) { + { + State = 2135; _localctx.c = Match(LOR); + State = 2136; additiveExpression(); + State = 2141; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==LOR) { + { + { + State = 2137; Match(LOR); + State = 2138; additiveExpression(); + } + } + State = 2143; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class AdditiveExpressionContext : ParserRuleContext { + public MultiplyExpressionContext[] multiplyExpression() { + return GetRuleContexts(); + } + public MultiplyExpressionContext multiplyExpression(int i) { + return GetRuleContext(i); + } + public ITerminalNode[] PLUS() { return GetTokens(EsperEPL2GrammarParser.PLUS); } + public ITerminalNode PLUS(int i) { + return GetToken(EsperEPL2GrammarParser.PLUS, i); + } + public ITerminalNode[] MINUS() { return GetTokens(EsperEPL2GrammarParser.MINUS); } + public ITerminalNode MINUS(int i) { + return GetToken(EsperEPL2GrammarParser.MINUS, i); + } + public AdditiveExpressionContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_additiveExpression; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterAdditiveExpression(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitAdditiveExpression(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitAdditiveExpression(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public AdditiveExpressionContext additiveExpression() { + AdditiveExpressionContext _localctx = new AdditiveExpressionContext(Context, State); + EnterRule(_localctx, 308, RULE_additiveExpression); + int _la; + try { + int _alt; + EnterOuterAlt(_localctx, 1); + { + State = 2146; multiplyExpression(); + State = 2151; + ErrorHandler.Sync(this); + _alt = Interpreter.AdaptivePredict(TokenStream,283,Context); + while ( _alt!=2 && _alt!=global::Antlr4.Runtime.Atn.ATN.INVALID_ALT_NUMBER ) { + if ( _alt==1 ) { + { + { + State = 2147; + _la = TokenStream.LA(1); + if ( !(_la==PLUS || _la==MINUS) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + State = 2148; multiplyExpression(); + } + } + } + State = 2153; + ErrorHandler.Sync(this); + _alt = Interpreter.AdaptivePredict(TokenStream,283,Context); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class MultiplyExpressionContext : ParserRuleContext { + public UnaryExpressionContext[] unaryExpression() { + return GetRuleContexts(); + } + public UnaryExpressionContext unaryExpression(int i) { + return GetRuleContext(i); + } + public ITerminalNode[] STAR() { return GetTokens(EsperEPL2GrammarParser.STAR); } + public ITerminalNode STAR(int i) { + return GetToken(EsperEPL2GrammarParser.STAR, i); + } + public ITerminalNode[] DIV() { return GetTokens(EsperEPL2GrammarParser.DIV); } + public ITerminalNode DIV(int i) { + return GetToken(EsperEPL2GrammarParser.DIV, i); + } + public ITerminalNode[] MOD() { return GetTokens(EsperEPL2GrammarParser.MOD); } + public ITerminalNode MOD(int i) { + return GetToken(EsperEPL2GrammarParser.MOD, i); + } + public MultiplyExpressionContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_multiplyExpression; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterMultiplyExpression(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitMultiplyExpression(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitMultiplyExpression(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public MultiplyExpressionContext multiplyExpression() { + MultiplyExpressionContext _localctx = new MultiplyExpressionContext(Context, State); + EnterRule(_localctx, 310, RULE_multiplyExpression); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 2154; unaryExpression(); + State = 2159; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (((((_la - 156)) & ~0x3f) == 0 && ((1L << (_la - 156)) & ((1L << (DIV - 156)) | (1L << (STAR - 156)) | (1L << (MOD - 156)))) != 0)) { + { + { + State = 2155; + _la = TokenStream.LA(1); + if ( !(((((_la - 156)) & ~0x3f) == 0 && ((1L << (_la - 156)) & ((1L << (DIV - 156)) | (1L << (STAR - 156)) | (1L << (MOD - 156)))) != 0)) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + State = 2156; unaryExpression(); + } + } + State = 2161; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class UnaryExpressionContext : ParserRuleContext { + public IToken inner; + public IToken b; + public ITerminalNode MINUS() { return GetToken(EsperEPL2GrammarParser.MINUS, 0); } + public EventPropertyContext eventProperty() { + return GetRuleContext(0); + } + public ConstantContext constant() { + return GetRuleContext(0); + } + public SubstitutionCanChainContext substitutionCanChain() { + return GetRuleContext(0); + } + public ExpressionContext[] expression() { + return GetRuleContexts(); + } + public ExpressionContext expression(int i) { + return GetRuleContext(i); + } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public ChainedFunctionContext chainedFunction() { + return GetRuleContext(0); + } + public BuiltinFuncContext builtinFunc() { + return GetRuleContext(0); + } + public EventPropertyOrLibFunctionContext eventPropertyOrLibFunction() { + return GetRuleContext(0); + } + public ArrayExpressionContext arrayExpression() { + return GetRuleContext(0); + } + public RowSubSelectExpressionContext rowSubSelectExpression() { + return GetRuleContext(0); + } + public ExistsSubSelectExpressionContext existsSubSelectExpression() { + return GetRuleContext(0); + } + public ITerminalNode NEWKW() { return GetToken(EsperEPL2GrammarParser.NEWKW, 0); } + public ITerminalNode LCURLY() { return GetToken(EsperEPL2GrammarParser.LCURLY, 0); } + public NewAssignContext[] newAssign() { + return GetRuleContexts(); + } + public NewAssignContext newAssign(int i) { + return GetRuleContext(i); + } + public ITerminalNode RCURLY() { return GetToken(EsperEPL2GrammarParser.RCURLY, 0); } + public ITerminalNode[] COMMA() { return GetTokens(EsperEPL2GrammarParser.COMMA); } + public ITerminalNode COMMA(int i) { + return GetToken(EsperEPL2GrammarParser.COMMA, i); + } + public ClassIdentifierContext classIdentifier() { + return GetRuleContext(0); + } + public ITerminalNode LBRACK() { return GetToken(EsperEPL2GrammarParser.LBRACK, 0); } + public ITerminalNode RBRACK() { return GetToken(EsperEPL2GrammarParser.RBRACK, 0); } + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public JsonobjectContext jsonobject() { + return GetRuleContext(0); + } + public UnaryExpressionContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_unaryExpression; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterUnaryExpression(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitUnaryExpression(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitUnaryExpression(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public UnaryExpressionContext unaryExpression() { + UnaryExpressionContext _localctx = new UnaryExpressionContext(Context, State); + EnterRule(_localctx, 312, RULE_unaryExpression); + int _la; + try { + State = 2221; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,292,Context) ) { + case 1: + EnterOuterAlt(_localctx, 1); + { + State = 2162; Match(MINUS); + State = 2163; eventProperty(); + } + break; + case 2: + EnterOuterAlt(_localctx, 2); + { + State = 2164; constant(); + } + break; + case 3: + EnterOuterAlt(_localctx, 3); + { + State = 2165; substitutionCanChain(); + } + break; + case 4: + EnterOuterAlt(_localctx, 4); + { + State = 2166; _localctx.inner = Match(LPAREN); + State = 2167; expression(); + State = 2168; Match(RPAREN); + State = 2170; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==DOT) { + { + State = 2169; chainedFunction(); + } + } + + } + break; + case 5: + EnterOuterAlt(_localctx, 5); + { + State = 2172; builtinFunc(); + } + break; + case 6: + EnterOuterAlt(_localctx, 6); + { + State = 2173; eventPropertyOrLibFunction(); + } + break; + case 7: + EnterOuterAlt(_localctx, 7); + { + State = 2174; arrayExpression(); + } + break; + case 8: + EnterOuterAlt(_localctx, 8); + { + State = 2175; rowSubSelectExpression(); + } + break; + case 9: + EnterOuterAlt(_localctx, 9); + { + State = 2176; existsSubSelectExpression(); + } + break; + case 10: + EnterOuterAlt(_localctx, 10); + { + State = 2177; Match(NEWKW); + State = 2178; Match(LCURLY); + State = 2179; newAssign(); + State = 2184; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 2180; Match(COMMA); + State = 2181; newAssign(); + } + } + State = 2186; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + State = 2187; Match(RCURLY); + } + break; + case 11: + EnterOuterAlt(_localctx, 11); + { + State = 2189; Match(NEWKW); + State = 2190; classIdentifier(); + State = 2191; Match(LPAREN); + State = 2200; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (((((_la - 2)) & ~0x3f) == 0 && ((1L << (_la - 2)) & ((1L << (WINDOW - 2)) | (1L << (ESCAPE - 2)) | (1L << (NOT_EXPR - 2)) | (1L << (EVERY_EXPR - 2)) | (1L << (SUM - 2)) | (1L << (AVG - 2)) | (1L << (MAX - 2)) | (1L << (MIN - 2)) | (1L << (COALESCE - 2)) | (1L << (MEDIAN - 2)) | (1L << (STDDEV - 2)) | (1L << (AVEDEV - 2)) | (1L << (COUNT - 2)) | (1L << (CASE - 2)) | (1L << (OUTER - 2)) | (1L << (JOIN - 2)) | (1L << (LEFT - 2)) | (1L << (RIGHT - 2)) | (1L << (FULL - 2)) | (1L << (EVENTS - 2)) | (1L << (FIRST - 2)) | (1L << (LAST - 2)) | (1L << (ISTREAM - 2)) | (1L << (SCHEMA - 2)) | (1L << (UNIDIRECTIONAL - 2)) | (1L << (RETAINUNION - 2)) | (1L << (RETAININTERSECTION - 2)) | (1L << (PATTERN - 2)) | (1L << (SQL - 2)) | (1L << (METADATASQL - 2)))) != 0) || ((((_la - 66)) & ~0x3f) == 0 && ((1L << (_la - 66)) & ((1L << (PREVIOUS - 66)) | (1L << (PREVIOUSTAIL - 66)) | (1L << (PREVIOUSCOUNT - 66)) | (1L << (PREVIOUSWINDOW - 66)) | (1L << (PRIOR - 66)) | (1L << (EXISTS - 66)) | (1L << (WEEKDAY - 66)) | (1L << (LW - 66)) | (1L << (INSTANCEOF - 66)) | (1L << (TYPEOF - 66)) | (1L << (CAST - 66)) | (1L << (CURRENT_TIMESTAMP - 66)) | (1L << (SNAPSHOT - 66)) | (1L << (VARIABLE - 66)) | (1L << (TABLE - 66)) | (1L << (UNTIL - 66)) | (1L << (AT - 66)) | (1L << (INDEX - 66)) | (1L << (BOOLEAN_TRUE - 66)) | (1L << (BOOLEAN_FALSE - 66)) | (1L << (VALUE_NULL - 66)) | (1L << (DEFINE - 66)) | (1L << (PARTITION - 66)) | (1L << (MATCHES - 66)) | (1L << (FOR - 66)) | (1L << (WHILE - 66)) | (1L << (USING - 66)) | (1L << (MERGE - 66)) | (1L << (MATCHED - 66)) | (1L << (NEWKW - 66)) | (1L << (CONTEXT - 66)))) != 0) || ((((_la - 134)) & ~0x3f) == 0 && ((1L << (_la - 134)) & ((1L << (GROUPING - 134)) | (1L << (GROUPING_ID - 134)) | (1L << (QUESTION - 134)) | (1L << (LPAREN - 134)) | (1L << (LCURLY - 134)) | (1L << (PLUS - 134)) | (1L << (MINUS - 134)) | (1L << (TICKED_STRING_LITERAL - 134)) | (1L << (QUOTED_STRING_LITERAL - 134)) | (1L << (STRING_LITERAL - 134)) | (1L << (IDENT - 134)) | (1L << (IntegerLiteral - 134)) | (1L << (FloatingPointLiteral - 134)))) != 0)) { + { + State = 2192; expression(); + State = 2197; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 2193; Match(COMMA); + State = 2194; expression(); + } + } + State = 2199; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + + State = 2202; Match(RPAREN); + State = 2204; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==DOT) { + { + State = 2203; chainedFunction(); + } + } + + } + break; + case 12: + EnterOuterAlt(_localctx, 12); + { + State = 2206; _localctx.b = Match(IDENT); + State = 2207; Match(LBRACK); + State = 2208; expression(); + State = 2213; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 2209; Match(COMMA); + State = 2210; expression(); + } + } + State = 2215; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + State = 2216; Match(RBRACK); + State = 2218; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==DOT) { + { + State = 2217; chainedFunction(); + } + } + + } + break; + case 13: + EnterOuterAlt(_localctx, 13); + { + State = 2220; jsonobject(); + } + break; + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class SubstitutionCanChainContext : ParserRuleContext { + public SubstitutionContext substitution() { + return GetRuleContext(0); + } + public ChainedFunctionContext chainedFunction() { + return GetRuleContext(0); + } + public SubstitutionCanChainContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_substitutionCanChain; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterSubstitutionCanChain(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitSubstitutionCanChain(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitSubstitutionCanChain(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public SubstitutionCanChainContext substitutionCanChain() { + SubstitutionCanChainContext _localctx = new SubstitutionCanChainContext(Context, State); + EnterRule(_localctx, 314, RULE_substitutionCanChain); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 2223; substitution(); + State = 2225; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==DOT) { + { + State = 2224; chainedFunction(); + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class ChainedFunctionContext : ParserRuleContext { + public IToken d; + public LibFunctionNoClassContext[] libFunctionNoClass() { + return GetRuleContexts(); + } + public LibFunctionNoClassContext libFunctionNoClass(int i) { + return GetRuleContext(i); + } + public ITerminalNode[] DOT() { return GetTokens(EsperEPL2GrammarParser.DOT); } + public ITerminalNode DOT(int i) { + return GetToken(EsperEPL2GrammarParser.DOT, i); + } + public ChainedFunctionContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_chainedFunction; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterChainedFunction(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitChainedFunction(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitChainedFunction(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public ChainedFunctionContext chainedFunction() { + ChainedFunctionContext _localctx = new ChainedFunctionContext(Context, State); + EnterRule(_localctx, 316, RULE_chainedFunction); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 2227; _localctx.d = Match(DOT); + State = 2228; libFunctionNoClass(); + State = 2233; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==DOT) { + { + { + State = 2229; _localctx.d = Match(DOT); + State = 2230; libFunctionNoClass(); + } + } + State = 2235; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class NewAssignContext : ParserRuleContext { + public EventPropertyContext eventProperty() { + return GetRuleContext(0); + } + public ITerminalNode EQUALS() { return GetToken(EsperEPL2GrammarParser.EQUALS, 0); } + public ExpressionContext expression() { + return GetRuleContext(0); + } + public NewAssignContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_newAssign; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterNewAssign(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitNewAssign(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitNewAssign(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public NewAssignContext newAssign() { + NewAssignContext _localctx = new NewAssignContext(Context, State); + EnterRule(_localctx, 318, RULE_newAssign); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 2236; eventProperty(); + State = 2239; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==EQUALS) { + { + State = 2237; Match(EQUALS); + State = 2238; expression(); + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class RowSubSelectExpressionContext : ParserRuleContext { + public SubQueryExprContext subQueryExpr() { + return GetRuleContext(0); + } + public ChainedFunctionContext chainedFunction() { + return GetRuleContext(0); + } + public RowSubSelectExpressionContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_rowSubSelectExpression; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterRowSubSelectExpression(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitRowSubSelectExpression(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitRowSubSelectExpression(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public RowSubSelectExpressionContext rowSubSelectExpression() { + RowSubSelectExpressionContext _localctx = new RowSubSelectExpressionContext(Context, State); + EnterRule(_localctx, 320, RULE_rowSubSelectExpression); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 2241; subQueryExpr(); + State = 2243; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==DOT) { + { + State = 2242; chainedFunction(); + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class SubSelectGroupExpressionContext : ParserRuleContext { + public SubQueryExprContext subQueryExpr() { + return GetRuleContext(0); + } + public SubSelectGroupExpressionContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_subSelectGroupExpression; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterSubSelectGroupExpression(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitSubSelectGroupExpression(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitSubSelectGroupExpression(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public SubSelectGroupExpressionContext subSelectGroupExpression() { + SubSelectGroupExpressionContext _localctx = new SubSelectGroupExpressionContext(Context, State); + EnterRule(_localctx, 322, RULE_subSelectGroupExpression); + try { + EnterOuterAlt(_localctx, 1); + { + State = 2245; subQueryExpr(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class ExistsSubSelectExpressionContext : ParserRuleContext { + public ITerminalNode EXISTS() { return GetToken(EsperEPL2GrammarParser.EXISTS, 0); } + public SubQueryExprContext subQueryExpr() { + return GetRuleContext(0); + } + public ExistsSubSelectExpressionContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_existsSubSelectExpression; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterExistsSubSelectExpression(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitExistsSubSelectExpression(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitExistsSubSelectExpression(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public ExistsSubSelectExpressionContext existsSubSelectExpression() { + ExistsSubSelectExpressionContext _localctx = new ExistsSubSelectExpressionContext(Context, State); + EnterRule(_localctx, 324, RULE_existsSubSelectExpression); + try { + EnterOuterAlt(_localctx, 1); + { + State = 2247; Match(EXISTS); + State = 2248; subQueryExpr(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class SubQueryExprContext : ParserRuleContext { + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public ITerminalNode SELECT() { return GetToken(EsperEPL2GrammarParser.SELECT, 0); } + public SelectionListContext selectionList() { + return GetRuleContext(0); + } + public ITerminalNode FROM() { return GetToken(EsperEPL2GrammarParser.FROM, 0); } + public SubSelectFilterExprContext subSelectFilterExpr() { + return GetRuleContext(0); + } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public ITerminalNode DISTINCT() { return GetToken(EsperEPL2GrammarParser.DISTINCT, 0); } + public ITerminalNode WHERE() { return GetToken(EsperEPL2GrammarParser.WHERE, 0); } + public WhereClauseContext whereClause() { + return GetRuleContext(0); + } + public ITerminalNode GROUP() { return GetToken(EsperEPL2GrammarParser.GROUP, 0); } + public ITerminalNode BY() { return GetToken(EsperEPL2GrammarParser.BY, 0); } + public GroupByListExprContext groupByListExpr() { + return GetRuleContext(0); + } + public ITerminalNode HAVING() { return GetToken(EsperEPL2GrammarParser.HAVING, 0); } + public HavingClauseContext havingClause() { + return GetRuleContext(0); + } + public SubQueryExprContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_subQueryExpr; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterSubQueryExpr(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitSubQueryExpr(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitSubQueryExpr(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public SubQueryExprContext subQueryExpr() { + SubQueryExprContext _localctx = new SubQueryExprContext(Context, State); + EnterRule(_localctx, 326, RULE_subQueryExpr); + paraphrases.Push("subquery"); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 2250; Match(LPAREN); + State = 2251; Match(SELECT); + State = 2253; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==DISTINCT) { + { + State = 2252; Match(DISTINCT); + } + } + + State = 2255; selectionList(); + State = 2256; Match(FROM); + State = 2257; subSelectFilterExpr(); + State = 2260; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==WHERE) { + { + State = 2258; Match(WHERE); + State = 2259; whereClause(); + } + } + + State = 2265; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==GROUP) { + { + State = 2262; Match(GROUP); + State = 2263; Match(BY); + State = 2264; groupByListExpr(); + } + } + + State = 2269; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==HAVING) { + { + State = 2267; Match(HAVING); + State = 2268; havingClause(); + } + } + + State = 2271; Match(RPAREN); + } + Context.Stop = TokenStream.LT(-1); + paraphrases.Pop(); + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class SubSelectFilterExprContext : ParserRuleContext { + public IToken i; + public IToken ru; + public IToken ri; + public EventFilterExpressionContext eventFilterExpression() { + return GetRuleContext(0); + } + public ViewExpressionsContext viewExpressions() { + return GetRuleContext(0); + } + public ITerminalNode AS() { return GetToken(EsperEPL2GrammarParser.AS, 0); } + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public ITerminalNode RETAINUNION() { return GetToken(EsperEPL2GrammarParser.RETAINUNION, 0); } + public ITerminalNode RETAININTERSECTION() { return GetToken(EsperEPL2GrammarParser.RETAININTERSECTION, 0); } + public SubSelectFilterExprContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_subSelectFilterExpr; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterSubSelectFilterExpr(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitSubSelectFilterExpr(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitSubSelectFilterExpr(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public SubSelectFilterExprContext subSelectFilterExpr() { + SubSelectFilterExprContext _localctx = new SubSelectFilterExprContext(Context, State); + EnterRule(_localctx, 328, RULE_subSelectFilterExpr); + paraphrases.Push("subquery filter specification"); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 2273; eventFilterExpression(); + State = 2275; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==DOT || _la==HASHCHAR) { + { + State = 2274; viewExpressions(); + } + } + + State = 2280; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case AS: + { + State = 2277; Match(AS); + State = 2278; _localctx.i = Match(IDENT); + } + break; + case IDENT: + { + State = 2279; _localctx.i = Match(IDENT); + } + break; + case WHERE: + case GROUP: + case HAVING: + case RETAINUNION: + case RETAININTERSECTION: + case RPAREN: + break; + default: + throw new NoViableAltException(this); + } + State = 2284; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case RETAINUNION: + { + State = 2282; _localctx.ru = Match(RETAINUNION); + } + break; + case RETAININTERSECTION: + { + State = 2283; _localctx.ri = Match(RETAININTERSECTION); + } + break; + case WHERE: + case GROUP: + case HAVING: + case RPAREN: + break; + default: + throw new NoViableAltException(this); + } + } + Context.Stop = TokenStream.LT(-1); + paraphrases.Pop(); + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class ArrayExpressionContext : ParserRuleContext { + public ITerminalNode LCURLY() { return GetToken(EsperEPL2GrammarParser.LCURLY, 0); } + public ITerminalNode RCURLY() { return GetToken(EsperEPL2GrammarParser.RCURLY, 0); } + public ExpressionContext[] expression() { + return GetRuleContexts(); + } + public ExpressionContext expression(int i) { + return GetRuleContext(i); + } + public ChainedFunctionContext chainedFunction() { + return GetRuleContext(0); + } + public ITerminalNode[] COMMA() { return GetTokens(EsperEPL2GrammarParser.COMMA); } + public ITerminalNode COMMA(int i) { + return GetToken(EsperEPL2GrammarParser.COMMA, i); + } + public ArrayExpressionContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_arrayExpression; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterArrayExpression(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitArrayExpression(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitArrayExpression(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public ArrayExpressionContext arrayExpression() { + ArrayExpressionContext _localctx = new ArrayExpressionContext(Context, State); + EnterRule(_localctx, 330, RULE_arrayExpression); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 2286; Match(LCURLY); + State = 2295; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (((((_la - 2)) & ~0x3f) == 0 && ((1L << (_la - 2)) & ((1L << (WINDOW - 2)) | (1L << (ESCAPE - 2)) | (1L << (NOT_EXPR - 2)) | (1L << (EVERY_EXPR - 2)) | (1L << (SUM - 2)) | (1L << (AVG - 2)) | (1L << (MAX - 2)) | (1L << (MIN - 2)) | (1L << (COALESCE - 2)) | (1L << (MEDIAN - 2)) | (1L << (STDDEV - 2)) | (1L << (AVEDEV - 2)) | (1L << (COUNT - 2)) | (1L << (CASE - 2)) | (1L << (OUTER - 2)) | (1L << (JOIN - 2)) | (1L << (LEFT - 2)) | (1L << (RIGHT - 2)) | (1L << (FULL - 2)) | (1L << (EVENTS - 2)) | (1L << (FIRST - 2)) | (1L << (LAST - 2)) | (1L << (ISTREAM - 2)) | (1L << (SCHEMA - 2)) | (1L << (UNIDIRECTIONAL - 2)) | (1L << (RETAINUNION - 2)) | (1L << (RETAININTERSECTION - 2)) | (1L << (PATTERN - 2)) | (1L << (SQL - 2)) | (1L << (METADATASQL - 2)))) != 0) || ((((_la - 66)) & ~0x3f) == 0 && ((1L << (_la - 66)) & ((1L << (PREVIOUS - 66)) | (1L << (PREVIOUSTAIL - 66)) | (1L << (PREVIOUSCOUNT - 66)) | (1L << (PREVIOUSWINDOW - 66)) | (1L << (PRIOR - 66)) | (1L << (EXISTS - 66)) | (1L << (WEEKDAY - 66)) | (1L << (LW - 66)) | (1L << (INSTANCEOF - 66)) | (1L << (TYPEOF - 66)) | (1L << (CAST - 66)) | (1L << (CURRENT_TIMESTAMP - 66)) | (1L << (SNAPSHOT - 66)) | (1L << (VARIABLE - 66)) | (1L << (TABLE - 66)) | (1L << (UNTIL - 66)) | (1L << (AT - 66)) | (1L << (INDEX - 66)) | (1L << (BOOLEAN_TRUE - 66)) | (1L << (BOOLEAN_FALSE - 66)) | (1L << (VALUE_NULL - 66)) | (1L << (DEFINE - 66)) | (1L << (PARTITION - 66)) | (1L << (MATCHES - 66)) | (1L << (FOR - 66)) | (1L << (WHILE - 66)) | (1L << (USING - 66)) | (1L << (MERGE - 66)) | (1L << (MATCHED - 66)) | (1L << (NEWKW - 66)) | (1L << (CONTEXT - 66)))) != 0) || ((((_la - 134)) & ~0x3f) == 0 && ((1L << (_la - 134)) & ((1L << (GROUPING - 134)) | (1L << (GROUPING_ID - 134)) | (1L << (QUESTION - 134)) | (1L << (LPAREN - 134)) | (1L << (LCURLY - 134)) | (1L << (PLUS - 134)) | (1L << (MINUS - 134)) | (1L << (TICKED_STRING_LITERAL - 134)) | (1L << (QUOTED_STRING_LITERAL - 134)) | (1L << (STRING_LITERAL - 134)) | (1L << (IDENT - 134)) | (1L << (IntegerLiteral - 134)) | (1L << (FloatingPointLiteral - 134)))) != 0)) { + { + State = 2287; expression(); + State = 2292; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 2288; Match(COMMA); + State = 2289; expression(); + } + } + State = 2294; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + + State = 2297; Match(RCURLY); + State = 2299; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==DOT) { + { + State = 2298; chainedFunction(); + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class BuiltinFuncContext : ParserRuleContext { + public BuiltinFuncContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_builtinFunc; } } + + public BuiltinFuncContext() { } + public virtual void CopyFrom(BuiltinFuncContext context) { + base.CopyFrom(context); + } + } + public partial class Builtin_castContext : BuiltinFuncContext { + public ITerminalNode CAST() { return GetToken(EsperEPL2GrammarParser.CAST, 0); } + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public ExpressionContext expression() { + return GetRuleContext(0); + } + public ClassIdentifierContext classIdentifier() { + return GetRuleContext(0); + } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public ITerminalNode[] COMMA() { return GetTokens(EsperEPL2GrammarParser.COMMA); } + public ITerminalNode COMMA(int i) { + return GetToken(EsperEPL2GrammarParser.COMMA, i); + } + public ITerminalNode AS() { return GetToken(EsperEPL2GrammarParser.AS, 0); } + public ExpressionNamedParameterContext expressionNamedParameter() { + return GetRuleContext(0); + } + public ChainedFunctionContext chainedFunction() { + return GetRuleContext(0); + } + public Builtin_castContext(BuiltinFuncContext context) { CopyFrom(context); } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterBuiltin_cast(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitBuiltin_cast(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitBuiltin_cast(this); + else return visitor.VisitChildren(this); + } + } + public partial class Builtin_cntContext : BuiltinFuncContext { + public IToken a; + public IToken d; + public ITerminalNode COUNT() { return GetToken(EsperEPL2GrammarParser.COUNT, 0); } + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public ExpressionListWithNamedContext expressionListWithNamed() { + return GetRuleContext(0); + } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public ITerminalNode ALL() { return GetToken(EsperEPL2GrammarParser.ALL, 0); } + public ITerminalNode DISTINCT() { return GetToken(EsperEPL2GrammarParser.DISTINCT, 0); } + public Builtin_cntContext(BuiltinFuncContext context) { CopyFrom(context); } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterBuiltin_cnt(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitBuiltin_cnt(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitBuiltin_cnt(this); + else return visitor.VisitChildren(this); + } + } + public partial class Builtin_sumContext : BuiltinFuncContext { + public ITerminalNode SUM() { return GetToken(EsperEPL2GrammarParser.SUM, 0); } + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public ExpressionListWithNamedContext expressionListWithNamed() { + return GetRuleContext(0); + } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public ITerminalNode ALL() { return GetToken(EsperEPL2GrammarParser.ALL, 0); } + public ITerminalNode DISTINCT() { return GetToken(EsperEPL2GrammarParser.DISTINCT, 0); } + public Builtin_sumContext(BuiltinFuncContext context) { CopyFrom(context); } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterBuiltin_sum(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitBuiltin_sum(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitBuiltin_sum(this); + else return visitor.VisitChildren(this); + } + } + public partial class Builtin_priorContext : BuiltinFuncContext { + public ITerminalNode PRIOR() { return GetToken(EsperEPL2GrammarParser.PRIOR, 0); } + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public ExpressionContext expression() { + return GetRuleContext(0); + } + public ITerminalNode COMMA() { return GetToken(EsperEPL2GrammarParser.COMMA, 0); } + public EventPropertyContext eventProperty() { + return GetRuleContext(0); + } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public Builtin_priorContext(BuiltinFuncContext context) { CopyFrom(context); } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterBuiltin_prior(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitBuiltin_prior(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitBuiltin_prior(this); + else return visitor.VisitChildren(this); + } + } + public partial class Builtin_existsContext : BuiltinFuncContext { + public ITerminalNode EXISTS() { return GetToken(EsperEPL2GrammarParser.EXISTS, 0); } + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public EventPropertyContext eventProperty() { + return GetRuleContext(0); + } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public Builtin_existsContext(BuiltinFuncContext context) { CopyFrom(context); } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterBuiltin_exists(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitBuiltin_exists(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitBuiltin_exists(this); + else return visitor.VisitChildren(this); + } + } + public partial class Builtin_prevtailContext : BuiltinFuncContext { + public ITerminalNode PREVIOUSTAIL() { return GetToken(EsperEPL2GrammarParser.PREVIOUSTAIL, 0); } + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public ExpressionContext[] expression() { + return GetRuleContexts(); + } + public ExpressionContext expression(int i) { + return GetRuleContext(i); + } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public ITerminalNode COMMA() { return GetToken(EsperEPL2GrammarParser.COMMA, 0); } + public ChainedFunctionContext chainedFunction() { + return GetRuleContext(0); + } + public Builtin_prevtailContext(BuiltinFuncContext context) { CopyFrom(context); } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterBuiltin_prevtail(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitBuiltin_prevtail(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitBuiltin_prevtail(this); + else return visitor.VisitChildren(this); + } + } + public partial class Builtin_istreamContext : BuiltinFuncContext { + public ITerminalNode ISTREAM() { return GetToken(EsperEPL2GrammarParser.ISTREAM, 0); } + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public Builtin_istreamContext(BuiltinFuncContext context) { CopyFrom(context); } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterBuiltin_istream(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitBuiltin_istream(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitBuiltin_istream(this); + else return visitor.VisitChildren(this); + } + } + public partial class Builtin_medianContext : BuiltinFuncContext { + public ITerminalNode MEDIAN() { return GetToken(EsperEPL2GrammarParser.MEDIAN, 0); } + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public ExpressionListWithNamedContext expressionListWithNamed() { + return GetRuleContext(0); + } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public ITerminalNode ALL() { return GetToken(EsperEPL2GrammarParser.ALL, 0); } + public ITerminalNode DISTINCT() { return GetToken(EsperEPL2GrammarParser.DISTINCT, 0); } + public Builtin_medianContext(BuiltinFuncContext context) { CopyFrom(context); } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterBuiltin_median(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitBuiltin_median(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitBuiltin_median(this); + else return visitor.VisitChildren(this); + } + } + public partial class Builtin_currtsContext : BuiltinFuncContext { + public ITerminalNode CURRENT_TIMESTAMP() { return GetToken(EsperEPL2GrammarParser.CURRENT_TIMESTAMP, 0); } + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public ChainedFunctionContext chainedFunction() { + return GetRuleContext(0); + } + public Builtin_currtsContext(BuiltinFuncContext context) { CopyFrom(context); } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterBuiltin_currts(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitBuiltin_currts(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitBuiltin_currts(this); + else return visitor.VisitChildren(this); + } + } + public partial class Builtin_coalesceContext : BuiltinFuncContext { + public ITerminalNode COALESCE() { return GetToken(EsperEPL2GrammarParser.COALESCE, 0); } + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public ExpressionContext[] expression() { + return GetRuleContexts(); + } + public ExpressionContext expression(int i) { + return GetRuleContext(i); + } + public ITerminalNode[] COMMA() { return GetTokens(EsperEPL2GrammarParser.COMMA); } + public ITerminalNode COMMA(int i) { + return GetToken(EsperEPL2GrammarParser.COMMA, i); + } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public Builtin_coalesceContext(BuiltinFuncContext context) { CopyFrom(context); } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterBuiltin_coalesce(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitBuiltin_coalesce(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitBuiltin_coalesce(this); + else return visitor.VisitChildren(this); + } + } + public partial class Builtin_prevContext : BuiltinFuncContext { + public ITerminalNode PREVIOUS() { return GetToken(EsperEPL2GrammarParser.PREVIOUS, 0); } + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public ExpressionContext[] expression() { + return GetRuleContexts(); + } + public ExpressionContext expression(int i) { + return GetRuleContext(i); + } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public ITerminalNode COMMA() { return GetToken(EsperEPL2GrammarParser.COMMA, 0); } + public ChainedFunctionContext chainedFunction() { + return GetRuleContext(0); + } + public Builtin_prevContext(BuiltinFuncContext context) { CopyFrom(context); } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterBuiltin_prev(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitBuiltin_prev(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitBuiltin_prev(this); + else return visitor.VisitChildren(this); + } + } + public partial class Builtin_prevcountContext : BuiltinFuncContext { + public ITerminalNode PREVIOUSCOUNT() { return GetToken(EsperEPL2GrammarParser.PREVIOUSCOUNT, 0); } + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public ExpressionContext expression() { + return GetRuleContext(0); + } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public Builtin_prevcountContext(BuiltinFuncContext context) { CopyFrom(context); } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterBuiltin_prevcount(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitBuiltin_prevcount(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitBuiltin_prevcount(this); + else return visitor.VisitChildren(this); + } + } + public partial class Builtin_groupingidContext : BuiltinFuncContext { + public ITerminalNode GROUPING_ID() { return GetToken(EsperEPL2GrammarParser.GROUPING_ID, 0); } + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public ExpressionListContext expressionList() { + return GetRuleContext(0); + } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public Builtin_groupingidContext(BuiltinFuncContext context) { CopyFrom(context); } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterBuiltin_groupingid(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitBuiltin_groupingid(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitBuiltin_groupingid(this); + else return visitor.VisitChildren(this); + } + } + public partial class Builtin_prevwindowContext : BuiltinFuncContext { + public ITerminalNode PREVIOUSWINDOW() { return GetToken(EsperEPL2GrammarParser.PREVIOUSWINDOW, 0); } + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public ExpressionContext expression() { + return GetRuleContext(0); + } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public ChainedFunctionContext chainedFunction() { + return GetRuleContext(0); + } + public Builtin_prevwindowContext(BuiltinFuncContext context) { CopyFrom(context); } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterBuiltin_prevwindow(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitBuiltin_prevwindow(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitBuiltin_prevwindow(this); + else return visitor.VisitChildren(this); + } + } + public partial class Builtin_stddevContext : BuiltinFuncContext { + public ITerminalNode STDDEV() { return GetToken(EsperEPL2GrammarParser.STDDEV, 0); } + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public ExpressionListWithNamedContext expressionListWithNamed() { + return GetRuleContext(0); + } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public ITerminalNode ALL() { return GetToken(EsperEPL2GrammarParser.ALL, 0); } + public ITerminalNode DISTINCT() { return GetToken(EsperEPL2GrammarParser.DISTINCT, 0); } + public Builtin_stddevContext(BuiltinFuncContext context) { CopyFrom(context); } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterBuiltin_stddev(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitBuiltin_stddev(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitBuiltin_stddev(this); + else return visitor.VisitChildren(this); + } + } + public partial class Builtin_groupingContext : BuiltinFuncContext { + public ITerminalNode GROUPING() { return GetToken(EsperEPL2GrammarParser.GROUPING, 0); } + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public ExpressionContext expression() { + return GetRuleContext(0); + } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public Builtin_groupingContext(BuiltinFuncContext context) { CopyFrom(context); } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterBuiltin_grouping(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitBuiltin_grouping(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitBuiltin_grouping(this); + else return visitor.VisitChildren(this); + } + } + public partial class Builtin_typeofContext : BuiltinFuncContext { + public ITerminalNode TYPEOF() { return GetToken(EsperEPL2GrammarParser.TYPEOF, 0); } + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public ExpressionContext expression() { + return GetRuleContext(0); + } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public Builtin_typeofContext(BuiltinFuncContext context) { CopyFrom(context); } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterBuiltin_typeof(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitBuiltin_typeof(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitBuiltin_typeof(this); + else return visitor.VisitChildren(this); + } + } + public partial class Builtin_firstlastwindowContext : BuiltinFuncContext { + public FirstLastWindowAggregationContext firstLastWindowAggregation() { + return GetRuleContext(0); + } + public Builtin_firstlastwindowContext(BuiltinFuncContext context) { CopyFrom(context); } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterBuiltin_firstlastwindow(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitBuiltin_firstlastwindow(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitBuiltin_firstlastwindow(this); + else return visitor.VisitChildren(this); + } + } + public partial class Builtin_instanceofContext : BuiltinFuncContext { + public ITerminalNode INSTANCEOF() { return GetToken(EsperEPL2GrammarParser.INSTANCEOF, 0); } + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public ExpressionContext expression() { + return GetRuleContext(0); + } + public ITerminalNode[] COMMA() { return GetTokens(EsperEPL2GrammarParser.COMMA); } + public ITerminalNode COMMA(int i) { + return GetToken(EsperEPL2GrammarParser.COMMA, i); + } + public ClassIdentifierContext[] classIdentifier() { + return GetRuleContexts(); + } + public ClassIdentifierContext classIdentifier(int i) { + return GetRuleContext(i); + } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public Builtin_instanceofContext(BuiltinFuncContext context) { CopyFrom(context); } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterBuiltin_instanceof(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitBuiltin_instanceof(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitBuiltin_instanceof(this); + else return visitor.VisitChildren(this); + } + } + public partial class Builtin_avgContext : BuiltinFuncContext { + public ITerminalNode AVG() { return GetToken(EsperEPL2GrammarParser.AVG, 0); } + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public ExpressionListWithNamedContext expressionListWithNamed() { + return GetRuleContext(0); + } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public ITerminalNode ALL() { return GetToken(EsperEPL2GrammarParser.ALL, 0); } + public ITerminalNode DISTINCT() { return GetToken(EsperEPL2GrammarParser.DISTINCT, 0); } + public Builtin_avgContext(BuiltinFuncContext context) { CopyFrom(context); } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterBuiltin_avg(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitBuiltin_avg(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitBuiltin_avg(this); + else return visitor.VisitChildren(this); + } + } + public partial class Builtin_avedevContext : BuiltinFuncContext { + public ITerminalNode AVEDEV() { return GetToken(EsperEPL2GrammarParser.AVEDEV, 0); } + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public ExpressionListWithNamedContext expressionListWithNamed() { + return GetRuleContext(0); + } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public ITerminalNode ALL() { return GetToken(EsperEPL2GrammarParser.ALL, 0); } + public ITerminalNode DISTINCT() { return GetToken(EsperEPL2GrammarParser.DISTINCT, 0); } + public Builtin_avedevContext(BuiltinFuncContext context) { CopyFrom(context); } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterBuiltin_avedev(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitBuiltin_avedev(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitBuiltin_avedev(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public BuiltinFuncContext builtinFunc() { + BuiltinFuncContext _localctx = new BuiltinFuncContext(Context, State); + EnterRule(_localctx, 332, RULE_builtinFunc); + int _la; + try { + State = 2464; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case SUM: + _localctx = new Builtin_sumContext(_localctx); + EnterOuterAlt(_localctx, 1); + { + State = 2301; Match(SUM); + State = 2302; Match(LPAREN); + State = 2304; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==DISTINCT || _la==ALL) { + { + State = 2303; + _la = TokenStream.LA(1); + if ( !(_la==DISTINCT || _la==ALL) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + } + } + + State = 2306; expressionListWithNamed(); + State = 2307; Match(RPAREN); + } + break; + case AVG: + _localctx = new Builtin_avgContext(_localctx); + EnterOuterAlt(_localctx, 2); + { + State = 2309; Match(AVG); + State = 2310; Match(LPAREN); + State = 2312; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==DISTINCT || _la==ALL) { + { + State = 2311; + _la = TokenStream.LA(1); + if ( !(_la==DISTINCT || _la==ALL) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + } + } + + State = 2314; expressionListWithNamed(); + State = 2315; Match(RPAREN); + } + break; + case COUNT: + _localctx = new Builtin_cntContext(_localctx); + EnterOuterAlt(_localctx, 3); + { + State = 2317; Match(COUNT); + State = 2318; Match(LPAREN); + State = 2321; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case ALL: + { + State = 2319; ((Builtin_cntContext)_localctx).a = Match(ALL); + } + break; + case DISTINCT: + { + State = 2320; ((Builtin_cntContext)_localctx).d = Match(DISTINCT); + } + break; + case WINDOW: + case ESCAPE: + case NOT_EXPR: + case EVERY_EXPR: + case SUM: + case AVG: + case MAX: + case MIN: + case COALESCE: + case MEDIAN: + case STDDEV: + case AVEDEV: + case COUNT: + case CASE: + case OUTER: + case JOIN: + case LEFT: + case RIGHT: + case FULL: + case EVENTS: + case FIRST: + case LAST: + case ISTREAM: + case SCHEMA: + case UNIDIRECTIONAL: + case RETAINUNION: + case RETAININTERSECTION: + case PATTERN: + case SQL: + case METADATASQL: + case PREVIOUS: + case PREVIOUSTAIL: + case PREVIOUSCOUNT: + case PREVIOUSWINDOW: + case PRIOR: + case EXISTS: + case WEEKDAY: + case LW: + case INSTANCEOF: + case TYPEOF: + case CAST: + case CURRENT_TIMESTAMP: + case SNAPSHOT: + case VARIABLE: + case TABLE: + case UNTIL: + case AT: + case INDEX: + case BOOLEAN_TRUE: + case BOOLEAN_FALSE: + case VALUE_NULL: + case DEFINE: + case PARTITION: + case MATCHES: + case FOR: + case WHILE: + case USING: + case MERGE: + case MATCHED: + case NEWKW: + case CONTEXT: + case GROUPING: + case GROUPING_ID: + case QUESTION: + case LPAREN: + case LBRACK: + case LCURLY: + case PLUS: + case MINUS: + case STAR: + case TICKED_STRING_LITERAL: + case QUOTED_STRING_LITERAL: + case STRING_LITERAL: + case IDENT: + case IntegerLiteral: + case FloatingPointLiteral: + break; + default: + throw new NoViableAltException(this); + } + State = 2323; expressionListWithNamed(); + State = 2324; Match(RPAREN); + } + break; + case MEDIAN: + _localctx = new Builtin_medianContext(_localctx); + EnterOuterAlt(_localctx, 4); + { + State = 2326; Match(MEDIAN); + State = 2327; Match(LPAREN); + State = 2329; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==DISTINCT || _la==ALL) { + { + State = 2328; + _la = TokenStream.LA(1); + if ( !(_la==DISTINCT || _la==ALL) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + } + } + + State = 2331; expressionListWithNamed(); + State = 2332; Match(RPAREN); + } + break; + case STDDEV: + _localctx = new Builtin_stddevContext(_localctx); + EnterOuterAlt(_localctx, 5); + { + State = 2334; Match(STDDEV); + State = 2335; Match(LPAREN); + State = 2337; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==DISTINCT || _la==ALL) { + { + State = 2336; + _la = TokenStream.LA(1); + if ( !(_la==DISTINCT || _la==ALL) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + } + } + + State = 2339; expressionListWithNamed(); + State = 2340; Match(RPAREN); + } + break; + case AVEDEV: + _localctx = new Builtin_avedevContext(_localctx); + EnterOuterAlt(_localctx, 6); + { + State = 2342; Match(AVEDEV); + State = 2343; Match(LPAREN); + State = 2345; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==DISTINCT || _la==ALL) { + { + State = 2344; + _la = TokenStream.LA(1); + if ( !(_la==DISTINCT || _la==ALL) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + } + } + + State = 2347; expressionListWithNamed(); + State = 2348; Match(RPAREN); + } + break; + case WINDOW: + case FIRST: + case LAST: + _localctx = new Builtin_firstlastwindowContext(_localctx); + EnterOuterAlt(_localctx, 7); + { + State = 2350; firstLastWindowAggregation(); + } + break; + case COALESCE: + _localctx = new Builtin_coalesceContext(_localctx); + EnterOuterAlt(_localctx, 8); + { + State = 2351; Match(COALESCE); + State = 2352; Match(LPAREN); + State = 2353; expression(); + State = 2354; Match(COMMA); + State = 2355; expression(); + State = 2360; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 2356; Match(COMMA); + State = 2357; expression(); + } + } + State = 2362; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + State = 2363; Match(RPAREN); + } + break; + case PREVIOUS: + _localctx = new Builtin_prevContext(_localctx); + EnterOuterAlt(_localctx, 9); + { + State = 2365; Match(PREVIOUS); + State = 2366; Match(LPAREN); + State = 2367; expression(); + State = 2370; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==COMMA) { + { + State = 2368; Match(COMMA); + State = 2369; expression(); + } + } + + State = 2372; Match(RPAREN); + State = 2374; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==DOT) { + { + State = 2373; chainedFunction(); + } + } + + } + break; + case PREVIOUSTAIL: + _localctx = new Builtin_prevtailContext(_localctx); + EnterOuterAlt(_localctx, 10); + { + State = 2376; Match(PREVIOUSTAIL); + State = 2377; Match(LPAREN); + State = 2378; expression(); + State = 2381; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==COMMA) { + { + State = 2379; Match(COMMA); + State = 2380; expression(); + } + } + + State = 2383; Match(RPAREN); + State = 2385; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==DOT) { + { + State = 2384; chainedFunction(); + } + } + + } + break; + case PREVIOUSCOUNT: + _localctx = new Builtin_prevcountContext(_localctx); + EnterOuterAlt(_localctx, 11); + { + State = 2387; Match(PREVIOUSCOUNT); + State = 2388; Match(LPAREN); + State = 2389; expression(); + State = 2390; Match(RPAREN); + } + break; + case PREVIOUSWINDOW: + _localctx = new Builtin_prevwindowContext(_localctx); + EnterOuterAlt(_localctx, 12); + { + State = 2392; Match(PREVIOUSWINDOW); + State = 2393; Match(LPAREN); + State = 2394; expression(); + State = 2395; Match(RPAREN); + State = 2397; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==DOT) { + { + State = 2396; chainedFunction(); + } + } + + } + break; + case PRIOR: + _localctx = new Builtin_priorContext(_localctx); + EnterOuterAlt(_localctx, 13); + { + State = 2399; Match(PRIOR); + State = 2400; Match(LPAREN); + State = 2401; expression(); + State = 2402; Match(COMMA); + State = 2403; eventProperty(); + State = 2404; Match(RPAREN); + } + break; + case GROUPING: + _localctx = new Builtin_groupingContext(_localctx); + EnterOuterAlt(_localctx, 14); + { + State = 2406; Match(GROUPING); + State = 2407; Match(LPAREN); + State = 2408; expression(); + State = 2409; Match(RPAREN); + } + break; + case GROUPING_ID: + _localctx = new Builtin_groupingidContext(_localctx); + EnterOuterAlt(_localctx, 15); + { + State = 2411; Match(GROUPING_ID); + State = 2412; Match(LPAREN); + State = 2413; expressionList(); + State = 2414; Match(RPAREN); + } + break; + case INSTANCEOF: + _localctx = new Builtin_instanceofContext(_localctx); + EnterOuterAlt(_localctx, 16); + { + State = 2416; Match(INSTANCEOF); + State = 2417; Match(LPAREN); + State = 2418; expression(); + State = 2419; Match(COMMA); + State = 2420; classIdentifier(); + State = 2425; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 2421; Match(COMMA); + State = 2422; classIdentifier(); + } + } + State = 2427; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + State = 2428; Match(RPAREN); + } + break; + case TYPEOF: + _localctx = new Builtin_typeofContext(_localctx); + EnterOuterAlt(_localctx, 17); + { + State = 2430; Match(TYPEOF); + State = 2431; Match(LPAREN); + State = 2432; expression(); + State = 2433; Match(RPAREN); + } + break; + case CAST: + _localctx = new Builtin_castContext(_localctx); + EnterOuterAlt(_localctx, 18); + { + State = 2435; Match(CAST); + State = 2436; Match(LPAREN); + State = 2437; expression(); + State = 2438; + _la = TokenStream.LA(1); + if ( !(_la==AS || _la==COMMA) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + State = 2439; classIdentifier(); + State = 2442; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==COMMA) { + { + State = 2440; Match(COMMA); + State = 2441; expressionNamedParameter(); + } + } + + State = 2444; Match(RPAREN); + State = 2446; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==DOT) { + { + State = 2445; chainedFunction(); + } + } + + } + break; + case EXISTS: + _localctx = new Builtin_existsContext(_localctx); + EnterOuterAlt(_localctx, 19); + { + State = 2448; Match(EXISTS); + State = 2449; Match(LPAREN); + State = 2450; eventProperty(); + State = 2451; Match(RPAREN); + } + break; + case CURRENT_TIMESTAMP: + _localctx = new Builtin_currtsContext(_localctx); + EnterOuterAlt(_localctx, 20); + { + State = 2453; Match(CURRENT_TIMESTAMP); + State = 2456; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,322,Context) ) { + case 1: + { + State = 2454; Match(LPAREN); + State = 2455; Match(RPAREN); + } + break; + } + State = 2459; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==DOT) { + { + State = 2458; chainedFunction(); + } + } + + } + break; + case ISTREAM: + _localctx = new Builtin_istreamContext(_localctx); + EnterOuterAlt(_localctx, 21); + { + State = 2461; Match(ISTREAM); + State = 2462; Match(LPAREN); + State = 2463; Match(RPAREN); + } + break; + default: + throw new NoViableAltException(this); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class FirstLastWindowAggregationContext : ParserRuleContext { + public IToken q; + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public ITerminalNode FIRST() { return GetToken(EsperEPL2GrammarParser.FIRST, 0); } + public ITerminalNode LAST() { return GetToken(EsperEPL2GrammarParser.LAST, 0); } + public ITerminalNode WINDOW() { return GetToken(EsperEPL2GrammarParser.WINDOW, 0); } + public ExpressionListWithNamedContext expressionListWithNamed() { + return GetRuleContext(0); + } + public ChainedFunctionContext chainedFunction() { + return GetRuleContext(0); + } + public FirstLastWindowAggregationContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_firstLastWindowAggregation; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterFirstLastWindowAggregation(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitFirstLastWindowAggregation(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitFirstLastWindowAggregation(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public FirstLastWindowAggregationContext firstLastWindowAggregation() { + FirstLastWindowAggregationContext _localctx = new FirstLastWindowAggregationContext(Context, State); + EnterRule(_localctx, 334, RULE_firstLastWindowAggregation); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 2469; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case FIRST: + { + State = 2466; _localctx.q = Match(FIRST); + } + break; + case LAST: + { + State = 2467; _localctx.q = Match(LAST); + } + break; + case WINDOW: + { + State = 2468; _localctx.q = Match(WINDOW); + } + break; + default: + throw new NoViableAltException(this); + } + State = 2471; Match(LPAREN); + State = 2473; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (((((_la - 2)) & ~0x3f) == 0 && ((1L << (_la - 2)) & ((1L << (WINDOW - 2)) | (1L << (ESCAPE - 2)) | (1L << (NOT_EXPR - 2)) | (1L << (EVERY_EXPR - 2)) | (1L << (SUM - 2)) | (1L << (AVG - 2)) | (1L << (MAX - 2)) | (1L << (MIN - 2)) | (1L << (COALESCE - 2)) | (1L << (MEDIAN - 2)) | (1L << (STDDEV - 2)) | (1L << (AVEDEV - 2)) | (1L << (COUNT - 2)) | (1L << (CASE - 2)) | (1L << (OUTER - 2)) | (1L << (JOIN - 2)) | (1L << (LEFT - 2)) | (1L << (RIGHT - 2)) | (1L << (FULL - 2)) | (1L << (EVENTS - 2)) | (1L << (FIRST - 2)) | (1L << (LAST - 2)) | (1L << (ISTREAM - 2)) | (1L << (SCHEMA - 2)) | (1L << (UNIDIRECTIONAL - 2)) | (1L << (RETAINUNION - 2)) | (1L << (RETAININTERSECTION - 2)) | (1L << (PATTERN - 2)) | (1L << (SQL - 2)) | (1L << (METADATASQL - 2)))) != 0) || ((((_la - 66)) & ~0x3f) == 0 && ((1L << (_la - 66)) & ((1L << (PREVIOUS - 66)) | (1L << (PREVIOUSTAIL - 66)) | (1L << (PREVIOUSCOUNT - 66)) | (1L << (PREVIOUSWINDOW - 66)) | (1L << (PRIOR - 66)) | (1L << (EXISTS - 66)) | (1L << (WEEKDAY - 66)) | (1L << (LW - 66)) | (1L << (INSTANCEOF - 66)) | (1L << (TYPEOF - 66)) | (1L << (CAST - 66)) | (1L << (CURRENT_TIMESTAMP - 66)) | (1L << (SNAPSHOT - 66)) | (1L << (VARIABLE - 66)) | (1L << (TABLE - 66)) | (1L << (UNTIL - 66)) | (1L << (AT - 66)) | (1L << (INDEX - 66)) | (1L << (BOOLEAN_TRUE - 66)) | (1L << (BOOLEAN_FALSE - 66)) | (1L << (VALUE_NULL - 66)) | (1L << (DEFINE - 66)) | (1L << (PARTITION - 66)) | (1L << (MATCHES - 66)) | (1L << (FOR - 66)) | (1L << (WHILE - 66)) | (1L << (USING - 66)) | (1L << (MERGE - 66)) | (1L << (MATCHED - 66)) | (1L << (NEWKW - 66)) | (1L << (CONTEXT - 66)))) != 0) || ((((_la - 134)) & ~0x3f) == 0 && ((1L << (_la - 134)) & ((1L << (GROUPING - 134)) | (1L << (GROUPING_ID - 134)) | (1L << (QUESTION - 134)) | (1L << (LPAREN - 134)) | (1L << (LBRACK - 134)) | (1L << (LCURLY - 134)) | (1L << (PLUS - 134)) | (1L << (MINUS - 134)) | (1L << (STAR - 134)) | (1L << (TICKED_STRING_LITERAL - 134)) | (1L << (QUOTED_STRING_LITERAL - 134)) | (1L << (STRING_LITERAL - 134)) | (1L << (IDENT - 134)) | (1L << (IntegerLiteral - 134)) | (1L << (FloatingPointLiteral - 134)))) != 0)) { + { + State = 2472; expressionListWithNamed(); + } + } + + State = 2475; Match(RPAREN); + State = 2477; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==DOT) { + { + State = 2476; chainedFunction(); + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class EventPropertyOrLibFunctionContext : ParserRuleContext { + public EventPropertyContext eventProperty() { + return GetRuleContext(0); + } + public LibFunctionContext libFunction() { + return GetRuleContext(0); + } + public EventPropertyOrLibFunctionContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_eventPropertyOrLibFunction; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterEventPropertyOrLibFunction(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitEventPropertyOrLibFunction(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitEventPropertyOrLibFunction(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public EventPropertyOrLibFunctionContext eventPropertyOrLibFunction() { + EventPropertyOrLibFunctionContext _localctx = new EventPropertyOrLibFunctionContext(Context, State); + EnterRule(_localctx, 336, RULE_eventPropertyOrLibFunction); + try { + State = 2481; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,328,Context) ) { + case 1: + EnterOuterAlt(_localctx, 1); + { + State = 2479; eventProperty(); + } + break; + case 2: + EnterOuterAlt(_localctx, 2); + { + State = 2480; libFunction(); + } + break; + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class LibFunctionContext : ParserRuleContext { + public LibFunctionWithClassContext libFunctionWithClass() { + return GetRuleContext(0); + } + public ITerminalNode[] DOT() { return GetTokens(EsperEPL2GrammarParser.DOT); } + public ITerminalNode DOT(int i) { + return GetToken(EsperEPL2GrammarParser.DOT, i); + } + public LibFunctionNoClassContext[] libFunctionNoClass() { + return GetRuleContexts(); + } + public LibFunctionNoClassContext libFunctionNoClass(int i) { + return GetRuleContext(i); + } + public LibFunctionContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_libFunction; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterLibFunction(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitLibFunction(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitLibFunction(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public LibFunctionContext libFunction() { + LibFunctionContext _localctx = new LibFunctionContext(Context, State); + EnterRule(_localctx, 338, RULE_libFunction); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 2483; libFunctionWithClass(); + State = 2488; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==DOT) { + { + { + State = 2484; Match(DOT); + State = 2485; libFunctionNoClass(); + } + } + State = 2490; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class LibFunctionWithClassContext : ParserRuleContext { + public IToken l; + public FuncIdentTopContext funcIdentTop() { + return GetRuleContext(0); + } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public ClassIdentifierContext classIdentifier() { + return GetRuleContext(0); + } + public ITerminalNode DOT() { return GetToken(EsperEPL2GrammarParser.DOT, 0); } + public FuncIdentInnerContext funcIdentInner() { + return GetRuleContext(0); + } + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public LibFunctionArgsContext libFunctionArgs() { + return GetRuleContext(0); + } + public LibFunctionWithClassContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_libFunctionWithClass; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterLibFunctionWithClass(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitLibFunctionWithClass(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitLibFunctionWithClass(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public LibFunctionWithClassContext libFunctionWithClass() { + LibFunctionWithClassContext _localctx = new LibFunctionWithClassContext(Context, State); + EnterRule(_localctx, 340, RULE_libFunctionWithClass); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 2496; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,330,Context) ) { + case 1: + { + { + State = 2491; classIdentifier(); + State = 2492; Match(DOT); + State = 2493; funcIdentInner(); + } + } + break; + case 2: + { + State = 2495; funcIdentTop(); + } + break; + } + State = 2503; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,332,Context) ) { + case 1: + { + State = 2498; _localctx.l = Match(LPAREN); + State = 2500; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (((((_la - 2)) & ~0x3f) == 0 && ((1L << (_la - 2)) & ((1L << (WINDOW - 2)) | (1L << (ESCAPE - 2)) | (1L << (NOT_EXPR - 2)) | (1L << (EVERY_EXPR - 2)) | (1L << (SUM - 2)) | (1L << (AVG - 2)) | (1L << (MAX - 2)) | (1L << (MIN - 2)) | (1L << (COALESCE - 2)) | (1L << (MEDIAN - 2)) | (1L << (STDDEV - 2)) | (1L << (AVEDEV - 2)) | (1L << (COUNT - 2)) | (1L << (CASE - 2)) | (1L << (OUTER - 2)) | (1L << (JOIN - 2)) | (1L << (LEFT - 2)) | (1L << (RIGHT - 2)) | (1L << (FULL - 2)) | (1L << (DISTINCT - 2)) | (1L << (ALL - 2)) | (1L << (EVENTS - 2)) | (1L << (FIRST - 2)) | (1L << (LAST - 2)) | (1L << (ISTREAM - 2)) | (1L << (SCHEMA - 2)) | (1L << (UNIDIRECTIONAL - 2)) | (1L << (RETAINUNION - 2)) | (1L << (RETAININTERSECTION - 2)) | (1L << (PATTERN - 2)) | (1L << (SQL - 2)) | (1L << (METADATASQL - 2)))) != 0) || ((((_la - 66)) & ~0x3f) == 0 && ((1L << (_la - 66)) & ((1L << (PREVIOUS - 66)) | (1L << (PREVIOUSTAIL - 66)) | (1L << (PREVIOUSCOUNT - 66)) | (1L << (PREVIOUSWINDOW - 66)) | (1L << (PRIOR - 66)) | (1L << (EXISTS - 66)) | (1L << (WEEKDAY - 66)) | (1L << (LW - 66)) | (1L << (INSTANCEOF - 66)) | (1L << (TYPEOF - 66)) | (1L << (CAST - 66)) | (1L << (CURRENT_TIMESTAMP - 66)) | (1L << (SNAPSHOT - 66)) | (1L << (VARIABLE - 66)) | (1L << (TABLE - 66)) | (1L << (UNTIL - 66)) | (1L << (AT - 66)) | (1L << (INDEX - 66)) | (1L << (BOOLEAN_TRUE - 66)) | (1L << (BOOLEAN_FALSE - 66)) | (1L << (VALUE_NULL - 66)) | (1L << (DEFINE - 66)) | (1L << (PARTITION - 66)) | (1L << (MATCHES - 66)) | (1L << (FOR - 66)) | (1L << (WHILE - 66)) | (1L << (USING - 66)) | (1L << (MERGE - 66)) | (1L << (MATCHED - 66)) | (1L << (NEWKW - 66)) | (1L << (CONTEXT - 66)))) != 0) || ((((_la - 134)) & ~0x3f) == 0 && ((1L << (_la - 134)) & ((1L << (GROUPING - 134)) | (1L << (GROUPING_ID - 134)) | (1L << (QUESTION - 134)) | (1L << (LPAREN - 134)) | (1L << (LBRACK - 134)) | (1L << (LCURLY - 134)) | (1L << (PLUS - 134)) | (1L << (MINUS - 134)) | (1L << (STAR - 134)) | (1L << (TICKED_STRING_LITERAL - 134)) | (1L << (QUOTED_STRING_LITERAL - 134)) | (1L << (STRING_LITERAL - 134)) | (1L << (IDENT - 134)) | (1L << (IntegerLiteral - 134)) | (1L << (FloatingPointLiteral - 134)))) != 0)) { + { + State = 2499; libFunctionArgs(); + } + } + + State = 2502; Match(RPAREN); + } + break; + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class LibFunctionNoClassContext : ParserRuleContext { + public IToken l; + public FuncIdentChainedContext funcIdentChained() { + return GetRuleContext(0); + } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public LibFunctionArgsContext libFunctionArgs() { + return GetRuleContext(0); + } + public LibFunctionNoClassContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_libFunctionNoClass; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterLibFunctionNoClass(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitLibFunctionNoClass(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitLibFunctionNoClass(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public LibFunctionNoClassContext libFunctionNoClass() { + LibFunctionNoClassContext _localctx = new LibFunctionNoClassContext(Context, State); + EnterRule(_localctx, 342, RULE_libFunctionNoClass); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 2505; funcIdentChained(); + State = 2511; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,334,Context) ) { + case 1: + { + State = 2506; _localctx.l = Match(LPAREN); + State = 2508; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (((((_la - 2)) & ~0x3f) == 0 && ((1L << (_la - 2)) & ((1L << (WINDOW - 2)) | (1L << (ESCAPE - 2)) | (1L << (NOT_EXPR - 2)) | (1L << (EVERY_EXPR - 2)) | (1L << (SUM - 2)) | (1L << (AVG - 2)) | (1L << (MAX - 2)) | (1L << (MIN - 2)) | (1L << (COALESCE - 2)) | (1L << (MEDIAN - 2)) | (1L << (STDDEV - 2)) | (1L << (AVEDEV - 2)) | (1L << (COUNT - 2)) | (1L << (CASE - 2)) | (1L << (OUTER - 2)) | (1L << (JOIN - 2)) | (1L << (LEFT - 2)) | (1L << (RIGHT - 2)) | (1L << (FULL - 2)) | (1L << (DISTINCT - 2)) | (1L << (ALL - 2)) | (1L << (EVENTS - 2)) | (1L << (FIRST - 2)) | (1L << (LAST - 2)) | (1L << (ISTREAM - 2)) | (1L << (SCHEMA - 2)) | (1L << (UNIDIRECTIONAL - 2)) | (1L << (RETAINUNION - 2)) | (1L << (RETAININTERSECTION - 2)) | (1L << (PATTERN - 2)) | (1L << (SQL - 2)) | (1L << (METADATASQL - 2)))) != 0) || ((((_la - 66)) & ~0x3f) == 0 && ((1L << (_la - 66)) & ((1L << (PREVIOUS - 66)) | (1L << (PREVIOUSTAIL - 66)) | (1L << (PREVIOUSCOUNT - 66)) | (1L << (PREVIOUSWINDOW - 66)) | (1L << (PRIOR - 66)) | (1L << (EXISTS - 66)) | (1L << (WEEKDAY - 66)) | (1L << (LW - 66)) | (1L << (INSTANCEOF - 66)) | (1L << (TYPEOF - 66)) | (1L << (CAST - 66)) | (1L << (CURRENT_TIMESTAMP - 66)) | (1L << (SNAPSHOT - 66)) | (1L << (VARIABLE - 66)) | (1L << (TABLE - 66)) | (1L << (UNTIL - 66)) | (1L << (AT - 66)) | (1L << (INDEX - 66)) | (1L << (BOOLEAN_TRUE - 66)) | (1L << (BOOLEAN_FALSE - 66)) | (1L << (VALUE_NULL - 66)) | (1L << (DEFINE - 66)) | (1L << (PARTITION - 66)) | (1L << (MATCHES - 66)) | (1L << (FOR - 66)) | (1L << (WHILE - 66)) | (1L << (USING - 66)) | (1L << (MERGE - 66)) | (1L << (MATCHED - 66)) | (1L << (NEWKW - 66)) | (1L << (CONTEXT - 66)))) != 0) || ((((_la - 134)) & ~0x3f) == 0 && ((1L << (_la - 134)) & ((1L << (GROUPING - 134)) | (1L << (GROUPING_ID - 134)) | (1L << (QUESTION - 134)) | (1L << (LPAREN - 134)) | (1L << (LBRACK - 134)) | (1L << (LCURLY - 134)) | (1L << (PLUS - 134)) | (1L << (MINUS - 134)) | (1L << (STAR - 134)) | (1L << (TICKED_STRING_LITERAL - 134)) | (1L << (QUOTED_STRING_LITERAL - 134)) | (1L << (STRING_LITERAL - 134)) | (1L << (IDENT - 134)) | (1L << (IntegerLiteral - 134)) | (1L << (FloatingPointLiteral - 134)))) != 0)) { + { + State = 2507; libFunctionArgs(); + } + } + + State = 2510; Match(RPAREN); + } + break; + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class FuncIdentTopContext : ParserRuleContext { + public EscapableIdentContext escapableIdent() { + return GetRuleContext(0); + } + public ITerminalNode MAX() { return GetToken(EsperEPL2GrammarParser.MAX, 0); } + public ITerminalNode MIN() { return GetToken(EsperEPL2GrammarParser.MIN, 0); } + public FuncIdentTopContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_funcIdentTop; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterFuncIdentTop(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitFuncIdentTop(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitFuncIdentTop(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public FuncIdentTopContext funcIdentTop() { + FuncIdentTopContext _localctx = new FuncIdentTopContext(Context, State); + EnterRule(_localctx, 344, RULE_funcIdentTop); + try { + State = 2516; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case TICKED_STRING_LITERAL: + case IDENT: + EnterOuterAlt(_localctx, 1); + { + State = 2513; escapableIdent(); + } + break; + case MAX: + EnterOuterAlt(_localctx, 2); + { + State = 2514; Match(MAX); + } + break; + case MIN: + EnterOuterAlt(_localctx, 3); + { + State = 2515; Match(MIN); + } + break; + default: + throw new NoViableAltException(this); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class FuncIdentInnerContext : ParserRuleContext { + public EscapableIdentContext escapableIdent() { + return GetRuleContext(0); + } + public ITerminalNode LAST() { return GetToken(EsperEPL2GrammarParser.LAST, 0); } + public ITerminalNode FIRST() { return GetToken(EsperEPL2GrammarParser.FIRST, 0); } + public ITerminalNode WINDOW() { return GetToken(EsperEPL2GrammarParser.WINDOW, 0); } + public FuncIdentInnerContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_funcIdentInner; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterFuncIdentInner(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitFuncIdentInner(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitFuncIdentInner(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public FuncIdentInnerContext funcIdentInner() { + FuncIdentInnerContext _localctx = new FuncIdentInnerContext(Context, State); + EnterRule(_localctx, 346, RULE_funcIdentInner); + try { + State = 2522; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case TICKED_STRING_LITERAL: + case IDENT: + EnterOuterAlt(_localctx, 1); + { + State = 2518; escapableIdent(); + } + break; + case LAST: + EnterOuterAlt(_localctx, 2); + { + State = 2519; Match(LAST); + } + break; + case FIRST: + EnterOuterAlt(_localctx, 3); + { + State = 2520; Match(FIRST); + } + break; + case WINDOW: + EnterOuterAlt(_localctx, 4); + { + State = 2521; Match(WINDOW); + } + break; + default: + throw new NoViableAltException(this); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class FuncIdentChainedContext : ParserRuleContext { + public EscapableIdentContext escapableIdent() { + return GetRuleContext(0); + } + public ITerminalNode LAST() { return GetToken(EsperEPL2GrammarParser.LAST, 0); } + public ITerminalNode FIRST() { return GetToken(EsperEPL2GrammarParser.FIRST, 0); } + public ITerminalNode WINDOW() { return GetToken(EsperEPL2GrammarParser.WINDOW, 0); } + public ITerminalNode MAX() { return GetToken(EsperEPL2GrammarParser.MAX, 0); } + public ITerminalNode MIN() { return GetToken(EsperEPL2GrammarParser.MIN, 0); } + public ITerminalNode WHERE() { return GetToken(EsperEPL2GrammarParser.WHERE, 0); } + public ITerminalNode SET() { return GetToken(EsperEPL2GrammarParser.SET, 0); } + public ITerminalNode AFTER() { return GetToken(EsperEPL2GrammarParser.AFTER, 0); } + public ITerminalNode BETWEEN() { return GetToken(EsperEPL2GrammarParser.BETWEEN, 0); } + public FuncIdentChainedContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_funcIdentChained; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterFuncIdentChained(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitFuncIdentChained(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitFuncIdentChained(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public FuncIdentChainedContext funcIdentChained() { + FuncIdentChainedContext _localctx = new FuncIdentChainedContext(Context, State); + EnterRule(_localctx, 348, RULE_funcIdentChained); + try { + State = 2534; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case TICKED_STRING_LITERAL: + case IDENT: + EnterOuterAlt(_localctx, 1); + { + State = 2524; escapableIdent(); + } + break; + case LAST: + EnterOuterAlt(_localctx, 2); + { + State = 2525; Match(LAST); + } + break; + case FIRST: + EnterOuterAlt(_localctx, 3); + { + State = 2526; Match(FIRST); + } + break; + case WINDOW: + EnterOuterAlt(_localctx, 4); + { + State = 2527; Match(WINDOW); + } + break; + case MAX: + EnterOuterAlt(_localctx, 5); + { + State = 2528; Match(MAX); + } + break; + case MIN: + EnterOuterAlt(_localctx, 6); + { + State = 2529; Match(MIN); + } + break; + case WHERE: + EnterOuterAlt(_localctx, 7); + { + State = 2530; Match(WHERE); + } + break; + case SET: + EnterOuterAlt(_localctx, 8); + { + State = 2531; Match(SET); + } + break; + case AFTER: + EnterOuterAlt(_localctx, 9); + { + State = 2532; Match(AFTER); + } + break; + case BETWEEN: + EnterOuterAlt(_localctx, 10); + { + State = 2533; Match(BETWEEN); + } + break; + default: + throw new NoViableAltException(this); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class LibFunctionArgsContext : ParserRuleContext { + public LibFunctionArgItemContext[] libFunctionArgItem() { + return GetRuleContexts(); + } + public LibFunctionArgItemContext libFunctionArgItem(int i) { + return GetRuleContext(i); + } + public ITerminalNode[] COMMA() { return GetTokens(EsperEPL2GrammarParser.COMMA); } + public ITerminalNode COMMA(int i) { + return GetToken(EsperEPL2GrammarParser.COMMA, i); + } + public ITerminalNode ALL() { return GetToken(EsperEPL2GrammarParser.ALL, 0); } + public ITerminalNode DISTINCT() { return GetToken(EsperEPL2GrammarParser.DISTINCT, 0); } + public LibFunctionArgsContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_libFunctionArgs; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterLibFunctionArgs(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitLibFunctionArgs(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitLibFunctionArgs(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public LibFunctionArgsContext libFunctionArgs() { + LibFunctionArgsContext _localctx = new LibFunctionArgsContext(Context, State); + EnterRule(_localctx, 350, RULE_libFunctionArgs); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 2537; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==DISTINCT || _la==ALL) { + { + State = 2536; + _la = TokenStream.LA(1); + if ( !(_la==DISTINCT || _la==ALL) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + } + } + + State = 2539; libFunctionArgItem(); + State = 2544; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 2540; Match(COMMA); + State = 2541; libFunctionArgItem(); + } + } + State = 2546; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class LibFunctionArgItemContext : ParserRuleContext { + public ExpressionWithNamedContext expressionWithNamed() { + return GetRuleContext(0); + } + public ExpressionLambdaDeclContext expressionLambdaDecl() { + return GetRuleContext(0); + } + public LibFunctionArgItemContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_libFunctionArgItem; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterLibFunctionArgItem(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitLibFunctionArgItem(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitLibFunctionArgItem(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public LibFunctionArgItemContext libFunctionArgItem() { + LibFunctionArgItemContext _localctx = new LibFunctionArgItemContext(Context, State); + EnterRule(_localctx, 352, RULE_libFunctionArgItem); + try { + EnterOuterAlt(_localctx, 1); + { + State = 2548; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,340,Context) ) { + case 1: + { + State = 2547; expressionLambdaDecl(); + } + break; + } + State = 2550; expressionWithNamed(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class BetweenListContext : ParserRuleContext { + public ConcatenationExprContext[] concatenationExpr() { + return GetRuleContexts(); + } + public ConcatenationExprContext concatenationExpr(int i) { + return GetRuleContext(i); + } + public ITerminalNode AND_EXPR() { return GetToken(EsperEPL2GrammarParser.AND_EXPR, 0); } + public BetweenListContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_betweenList; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterBetweenList(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitBetweenList(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitBetweenList(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public BetweenListContext betweenList() { + BetweenListContext _localctx = new BetweenListContext(Context, State); + EnterRule(_localctx, 354, RULE_betweenList); + try { + EnterOuterAlt(_localctx, 1); + { + State = 2552; concatenationExpr(); + State = 2553; Match(AND_EXPR); + State = 2554; concatenationExpr(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class PatternExpressionContext : ParserRuleContext { + public FollowedByExpressionContext followedByExpression() { + return GetRuleContext(0); + } + public PatternExpressionContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_patternExpression; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterPatternExpression(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitPatternExpression(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitPatternExpression(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public PatternExpressionContext patternExpression() { + PatternExpressionContext _localctx = new PatternExpressionContext(Context, State); + EnterRule(_localctx, 356, RULE_patternExpression); + paraphrases.Push("pattern expression"); + try { + EnterOuterAlt(_localctx, 1); + { + State = 2556; followedByExpression(); + } + Context.Stop = TokenStream.LT(-1); + paraphrases.Pop(); + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class FollowedByExpressionContext : ParserRuleContext { + public OrExpressionContext orExpression() { + return GetRuleContext(0); + } + public FollowedByRepeatContext[] followedByRepeat() { + return GetRuleContexts(); + } + public FollowedByRepeatContext followedByRepeat(int i) { + return GetRuleContext(i); + } + public FollowedByExpressionContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_followedByExpression; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterFollowedByExpression(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitFollowedByExpression(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitFollowedByExpression(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public FollowedByExpressionContext followedByExpression() { + FollowedByExpressionContext _localctx = new FollowedByExpressionContext(Context, State); + EnterRule(_localctx, 358, RULE_followedByExpression); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 2558; orExpression(); + State = 2562; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==FOLLOWMAX_BEGIN || _la==FOLLOWED_BY) { + { + { + State = 2559; followedByRepeat(); + } + } + State = 2564; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class FollowedByRepeatContext : ParserRuleContext { + public IToken f; + public IToken g; + public OrExpressionContext orExpression() { + return GetRuleContext(0); + } + public ITerminalNode FOLLOWED_BY() { return GetToken(EsperEPL2GrammarParser.FOLLOWED_BY, 0); } + public ExpressionContext expression() { + return GetRuleContext(0); + } + public ITerminalNode FOLLOWMAX_END() { return GetToken(EsperEPL2GrammarParser.FOLLOWMAX_END, 0); } + public ITerminalNode FOLLOWMAX_BEGIN() { return GetToken(EsperEPL2GrammarParser.FOLLOWMAX_BEGIN, 0); } + public FollowedByRepeatContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_followedByRepeat; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterFollowedByRepeat(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitFollowedByRepeat(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitFollowedByRepeat(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public FollowedByRepeatContext followedByRepeat() { + FollowedByRepeatContext _localctx = new FollowedByRepeatContext(Context, State); + EnterRule(_localctx, 360, RULE_followedByRepeat); + try { + EnterOuterAlt(_localctx, 1); + { + State = 2570; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case FOLLOWED_BY: + { + State = 2565; _localctx.f = Match(FOLLOWED_BY); + } + break; + case FOLLOWMAX_BEGIN: + { + { + State = 2566; _localctx.g = Match(FOLLOWMAX_BEGIN); + State = 2567; expression(); + State = 2568; Match(FOLLOWMAX_END); + } + } + break; + default: + throw new NoViableAltException(this); + } + State = 2572; orExpression(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class OrExpressionContext : ParserRuleContext { + public IToken o; + public AndExpressionContext[] andExpression() { + return GetRuleContexts(); + } + public AndExpressionContext andExpression(int i) { + return GetRuleContext(i); + } + public ITerminalNode[] OR_EXPR() { return GetTokens(EsperEPL2GrammarParser.OR_EXPR); } + public ITerminalNode OR_EXPR(int i) { + return GetToken(EsperEPL2GrammarParser.OR_EXPR, i); + } + public OrExpressionContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_orExpression; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterOrExpression(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitOrExpression(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitOrExpression(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public OrExpressionContext orExpression() { + OrExpressionContext _localctx = new OrExpressionContext(Context, State); + EnterRule(_localctx, 362, RULE_orExpression); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 2574; andExpression(); + State = 2579; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==OR_EXPR) { + { + { + State = 2575; _localctx.o = Match(OR_EXPR); + State = 2576; andExpression(); + } + } + State = 2581; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class AndExpressionContext : ParserRuleContext { + public IToken a; + public MatchUntilExpressionContext[] matchUntilExpression() { + return GetRuleContexts(); + } + public MatchUntilExpressionContext matchUntilExpression(int i) { + return GetRuleContext(i); + } + public ITerminalNode[] AND_EXPR() { return GetTokens(EsperEPL2GrammarParser.AND_EXPR); } + public ITerminalNode AND_EXPR(int i) { + return GetToken(EsperEPL2GrammarParser.AND_EXPR, i); + } + public AndExpressionContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_andExpression; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterAndExpression(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitAndExpression(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitAndExpression(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public AndExpressionContext andExpression() { + AndExpressionContext _localctx = new AndExpressionContext(Context, State); + EnterRule(_localctx, 364, RULE_andExpression); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 2582; matchUntilExpression(); + State = 2587; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==AND_EXPR) { + { + { + State = 2583; _localctx.a = Match(AND_EXPR); + State = 2584; matchUntilExpression(); + } + } + State = 2589; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class MatchUntilExpressionContext : ParserRuleContext { + public MatchUntilRangeContext r; + public QualifyExpressionContext until; + public QualifyExpressionContext[] qualifyExpression() { + return GetRuleContexts(); + } + public QualifyExpressionContext qualifyExpression(int i) { + return GetRuleContext(i); + } + public ITerminalNode UNTIL() { return GetToken(EsperEPL2GrammarParser.UNTIL, 0); } + public MatchUntilRangeContext matchUntilRange() { + return GetRuleContext(0); + } + public MatchUntilExpressionContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_matchUntilExpression; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterMatchUntilExpression(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitMatchUntilExpression(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitMatchUntilExpression(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public MatchUntilExpressionContext matchUntilExpression() { + MatchUntilExpressionContext _localctx = new MatchUntilExpressionContext(Context, State); + EnterRule(_localctx, 366, RULE_matchUntilExpression); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 2591; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==LBRACK) { + { + State = 2590; _localctx.r = matchUntilRange(); + } + } + + State = 2593; qualifyExpression(); + State = 2596; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==UNTIL) { + { + State = 2594; Match(UNTIL); + State = 2595; _localctx.until = qualifyExpression(); + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class QualifyExpressionContext : ParserRuleContext { + public IToken e; + public IToken n; + public IToken d; + public GuardPostFixContext guardPostFix() { + return GetRuleContext(0); + } + public DistinctExpressionListContext distinctExpressionList() { + return GetRuleContext(0); + } + public ITerminalNode EVERY_EXPR() { return GetToken(EsperEPL2GrammarParser.EVERY_EXPR, 0); } + public ITerminalNode NOT_EXPR() { return GetToken(EsperEPL2GrammarParser.NOT_EXPR, 0); } + public ITerminalNode EVERY_DISTINCT_EXPR() { return GetToken(EsperEPL2GrammarParser.EVERY_DISTINCT_EXPR, 0); } + public MatchUntilRangeContext matchUntilRange() { + return GetRuleContext(0); + } + public QualifyExpressionContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_qualifyExpression; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterQualifyExpression(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitQualifyExpression(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitQualifyExpression(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public QualifyExpressionContext qualifyExpression() { + QualifyExpressionContext _localctx = new QualifyExpressionContext(Context, State); + EnterRule(_localctx, 368, RULE_qualifyExpression); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 2607; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if ((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << NOT_EXPR) | (1L << EVERY_EXPR) | (1L << EVERY_DISTINCT_EXPR))) != 0)) { + { + State = 2602; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case EVERY_EXPR: + { + State = 2598; _localctx.e = Match(EVERY_EXPR); + } + break; + case NOT_EXPR: + { + State = 2599; _localctx.n = Match(NOT_EXPR); + } + break; + case EVERY_DISTINCT_EXPR: + { + State = 2600; _localctx.d = Match(EVERY_DISTINCT_EXPR); + State = 2601; distinctExpressionList(); + } + break; + default: + throw new NoViableAltException(this); + } + State = 2605; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==LBRACK) { + { + State = 2604; matchUntilRange(); + } + } + + } + } + + State = 2609; guardPostFix(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class GuardPostFixContext : ParserRuleContext { + public IToken l; + public IToken wh; + public IToken wi; + public AtomicExpressionContext atomicExpression() { + return GetRuleContext(0); + } + public PatternExpressionContext patternExpression() { + return GetRuleContext(0); + } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public GuardWhereExpressionContext guardWhereExpression() { + return GetRuleContext(0); + } + public GuardWhileExpressionContext guardWhileExpression() { + return GetRuleContext(0); + } + public ITerminalNode WHERE() { return GetToken(EsperEPL2GrammarParser.WHERE, 0); } + public ITerminalNode WHILE() { return GetToken(EsperEPL2GrammarParser.WHILE, 0); } + public GuardPostFixContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_guardPostFix; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterGuardPostFix(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitGuardPostFix(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitGuardPostFix(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public GuardPostFixContext guardPostFix() { + GuardPostFixContext _localctx = new GuardPostFixContext(Context, State); + EnterRule(_localctx, 370, RULE_guardPostFix); + try { + EnterOuterAlt(_localctx, 1); + { + State = 2616; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case EVENTS: + case TICKED_STRING_LITERAL: + case IDENT: + { + State = 2611; atomicExpression(); + } + break; + case LPAREN: + { + State = 2612; _localctx.l = Match(LPAREN); + State = 2613; patternExpression(); + State = 2614; Match(RPAREN); + } + break; + default: + throw new NoViableAltException(this); + } + State = 2622; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case WHERE: + { + { + State = 2618; _localctx.wh = Match(WHERE); + State = 2619; guardWhereExpression(); + } + } + break; + case WHILE: + { + { + State = 2620; _localctx.wi = Match(WHILE); + State = 2621; guardWhileExpression(); + } + } + break; + case Eof: + case OR_EXPR: + case AND_EXPR: + case UNTIL: + case FOLLOWMAX_BEGIN: + case FOLLOWED_BY: + case RPAREN: + case RBRACK: + break; + default: + throw new NoViableAltException(this); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class DistinctExpressionListContext : ParserRuleContext { + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public DistinctExpressionAtomContext[] distinctExpressionAtom() { + return GetRuleContexts(); + } + public DistinctExpressionAtomContext distinctExpressionAtom(int i) { + return GetRuleContext(i); + } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public ITerminalNode[] COMMA() { return GetTokens(EsperEPL2GrammarParser.COMMA); } + public ITerminalNode COMMA(int i) { + return GetToken(EsperEPL2GrammarParser.COMMA, i); + } + public DistinctExpressionListContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_distinctExpressionList; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterDistinctExpressionList(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitDistinctExpressionList(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitDistinctExpressionList(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public DistinctExpressionListContext distinctExpressionList() { + DistinctExpressionListContext _localctx = new DistinctExpressionListContext(Context, State); + EnterRule(_localctx, 372, RULE_distinctExpressionList); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 2624; Match(LPAREN); + State = 2625; distinctExpressionAtom(); + State = 2630; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 2626; Match(COMMA); + State = 2627; distinctExpressionAtom(); + } + } + State = 2632; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + State = 2633; Match(RPAREN); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class DistinctExpressionAtomContext : ParserRuleContext { + public ExpressionWithTimeContext expressionWithTime() { + return GetRuleContext(0); + } + public DistinctExpressionAtomContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_distinctExpressionAtom; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterDistinctExpressionAtom(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitDistinctExpressionAtom(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitDistinctExpressionAtom(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public DistinctExpressionAtomContext distinctExpressionAtom() { + DistinctExpressionAtomContext _localctx = new DistinctExpressionAtomContext(Context, State); + EnterRule(_localctx, 374, RULE_distinctExpressionAtom); + try { + EnterOuterAlt(_localctx, 1); + { + State = 2635; expressionWithTime(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class AtomicExpressionContext : ParserRuleContext { + public ObserverExpressionContext observerExpression() { + return GetRuleContext(0); + } + public PatternFilterExpressionContext patternFilterExpression() { + return GetRuleContext(0); + } + public AtomicExpressionContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_atomicExpression; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterAtomicExpression(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitAtomicExpression(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitAtomicExpression(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public AtomicExpressionContext atomicExpression() { + AtomicExpressionContext _localctx = new AtomicExpressionContext(Context, State); + EnterRule(_localctx, 376, RULE_atomicExpression); + try { + State = 2639; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,353,Context) ) { + case 1: + EnterOuterAlt(_localctx, 1); + { + State = 2637; observerExpression(); + } + break; + case 2: + EnterOuterAlt(_localctx, 2); + { + State = 2638; patternFilterExpression(); + } + break; + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class ObserverExpressionContext : ParserRuleContext { + public IToken ns; + public IToken nm; + public IToken a; + public ITerminalNode COLON() { return GetToken(EsperEPL2GrammarParser.COLON, 0); } + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public ITerminalNode[] IDENT() { return GetTokens(EsperEPL2GrammarParser.IDENT); } + public ITerminalNode IDENT(int i) { + return GetToken(EsperEPL2GrammarParser.IDENT, i); + } + public ITerminalNode AT() { return GetToken(EsperEPL2GrammarParser.AT, 0); } + public ExpressionListWithNamedWithTimeContext expressionListWithNamedWithTime() { + return GetRuleContext(0); + } + public ObserverExpressionContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_observerExpression; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterObserverExpression(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitObserverExpression(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitObserverExpression(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public ObserverExpressionContext observerExpression() { + ObserverExpressionContext _localctx = new ObserverExpressionContext(Context, State); + EnterRule(_localctx, 378, RULE_observerExpression); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 2641; _localctx.ns = Match(IDENT); + State = 2642; Match(COLON); + State = 2645; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case IDENT: + { + State = 2643; _localctx.nm = Match(IDENT); + } + break; + case AT: + { + State = 2644; _localctx.a = Match(AT); + } + break; + default: + throw new NoViableAltException(this); + } + State = 2647; Match(LPAREN); + State = 2649; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (((((_la - 2)) & ~0x3f) == 0 && ((1L << (_la - 2)) & ((1L << (WINDOW - 2)) | (1L << (ESCAPE - 2)) | (1L << (NOT_EXPR - 2)) | (1L << (EVERY_EXPR - 2)) | (1L << (SUM - 2)) | (1L << (AVG - 2)) | (1L << (MAX - 2)) | (1L << (MIN - 2)) | (1L << (COALESCE - 2)) | (1L << (MEDIAN - 2)) | (1L << (STDDEV - 2)) | (1L << (AVEDEV - 2)) | (1L << (COUNT - 2)) | (1L << (CASE - 2)) | (1L << (OUTER - 2)) | (1L << (JOIN - 2)) | (1L << (LEFT - 2)) | (1L << (RIGHT - 2)) | (1L << (FULL - 2)) | (1L << (EVENTS - 2)) | (1L << (FIRST - 2)) | (1L << (LAST - 2)) | (1L << (ISTREAM - 2)) | (1L << (SCHEMA - 2)) | (1L << (UNIDIRECTIONAL - 2)) | (1L << (RETAINUNION - 2)) | (1L << (RETAININTERSECTION - 2)) | (1L << (PATTERN - 2)) | (1L << (SQL - 2)) | (1L << (METADATASQL - 2)))) != 0) || ((((_la - 66)) & ~0x3f) == 0 && ((1L << (_la - 66)) & ((1L << (PREVIOUS - 66)) | (1L << (PREVIOUSTAIL - 66)) | (1L << (PREVIOUSCOUNT - 66)) | (1L << (PREVIOUSWINDOW - 66)) | (1L << (PRIOR - 66)) | (1L << (EXISTS - 66)) | (1L << (WEEKDAY - 66)) | (1L << (LW - 66)) | (1L << (INSTANCEOF - 66)) | (1L << (TYPEOF - 66)) | (1L << (CAST - 66)) | (1L << (CURRENT_TIMESTAMP - 66)) | (1L << (SNAPSHOT - 66)) | (1L << (VARIABLE - 66)) | (1L << (TABLE - 66)) | (1L << (UNTIL - 66)) | (1L << (AT - 66)) | (1L << (INDEX - 66)) | (1L << (BOOLEAN_TRUE - 66)) | (1L << (BOOLEAN_FALSE - 66)) | (1L << (VALUE_NULL - 66)) | (1L << (DEFINE - 66)) | (1L << (PARTITION - 66)) | (1L << (MATCHES - 66)) | (1L << (FOR - 66)) | (1L << (WHILE - 66)) | (1L << (USING - 66)) | (1L << (MERGE - 66)) | (1L << (MATCHED - 66)) | (1L << (NEWKW - 66)) | (1L << (CONTEXT - 66)))) != 0) || ((((_la - 134)) & ~0x3f) == 0 && ((1L << (_la - 134)) & ((1L << (GROUPING - 134)) | (1L << (GROUPING_ID - 134)) | (1L << (QUESTION - 134)) | (1L << (LPAREN - 134)) | (1L << (LBRACK - 134)) | (1L << (LCURLY - 134)) | (1L << (PLUS - 134)) | (1L << (MINUS - 134)) | (1L << (STAR - 134)) | (1L << (TICKED_STRING_LITERAL - 134)) | (1L << (QUOTED_STRING_LITERAL - 134)) | (1L << (STRING_LITERAL - 134)) | (1L << (IDENT - 134)) | (1L << (IntegerLiteral - 134)) | (1L << (FloatingPointLiteral - 134)))) != 0)) { + { + State = 2648; expressionListWithNamedWithTime(); + } + } + + State = 2651; Match(RPAREN); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class GuardWhereExpressionContext : ParserRuleContext { + public ITerminalNode[] IDENT() { return GetTokens(EsperEPL2GrammarParser.IDENT); } + public ITerminalNode IDENT(int i) { + return GetToken(EsperEPL2GrammarParser.IDENT, i); + } + public ITerminalNode COLON() { return GetToken(EsperEPL2GrammarParser.COLON, 0); } + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public ExpressionWithTimeListContext expressionWithTimeList() { + return GetRuleContext(0); + } + public GuardWhereExpressionContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_guardWhereExpression; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterGuardWhereExpression(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitGuardWhereExpression(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitGuardWhereExpression(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public GuardWhereExpressionContext guardWhereExpression() { + GuardWhereExpressionContext _localctx = new GuardWhereExpressionContext(Context, State); + EnterRule(_localctx, 380, RULE_guardWhereExpression); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 2653; Match(IDENT); + State = 2654; Match(COLON); + State = 2655; Match(IDENT); + State = 2656; Match(LPAREN); + State = 2658; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (((((_la - 2)) & ~0x3f) == 0 && ((1L << (_la - 2)) & ((1L << (WINDOW - 2)) | (1L << (ESCAPE - 2)) | (1L << (NOT_EXPR - 2)) | (1L << (EVERY_EXPR - 2)) | (1L << (SUM - 2)) | (1L << (AVG - 2)) | (1L << (MAX - 2)) | (1L << (MIN - 2)) | (1L << (COALESCE - 2)) | (1L << (MEDIAN - 2)) | (1L << (STDDEV - 2)) | (1L << (AVEDEV - 2)) | (1L << (COUNT - 2)) | (1L << (CASE - 2)) | (1L << (OUTER - 2)) | (1L << (JOIN - 2)) | (1L << (LEFT - 2)) | (1L << (RIGHT - 2)) | (1L << (FULL - 2)) | (1L << (EVENTS - 2)) | (1L << (FIRST - 2)) | (1L << (LAST - 2)) | (1L << (ISTREAM - 2)) | (1L << (SCHEMA - 2)) | (1L << (UNIDIRECTIONAL - 2)) | (1L << (RETAINUNION - 2)) | (1L << (RETAININTERSECTION - 2)) | (1L << (PATTERN - 2)) | (1L << (SQL - 2)) | (1L << (METADATASQL - 2)))) != 0) || ((((_la - 66)) & ~0x3f) == 0 && ((1L << (_la - 66)) & ((1L << (PREVIOUS - 66)) | (1L << (PREVIOUSTAIL - 66)) | (1L << (PREVIOUSCOUNT - 66)) | (1L << (PREVIOUSWINDOW - 66)) | (1L << (PRIOR - 66)) | (1L << (EXISTS - 66)) | (1L << (WEEKDAY - 66)) | (1L << (LW - 66)) | (1L << (INSTANCEOF - 66)) | (1L << (TYPEOF - 66)) | (1L << (CAST - 66)) | (1L << (CURRENT_TIMESTAMP - 66)) | (1L << (SNAPSHOT - 66)) | (1L << (VARIABLE - 66)) | (1L << (TABLE - 66)) | (1L << (UNTIL - 66)) | (1L << (AT - 66)) | (1L << (INDEX - 66)) | (1L << (BOOLEAN_TRUE - 66)) | (1L << (BOOLEAN_FALSE - 66)) | (1L << (VALUE_NULL - 66)) | (1L << (DEFINE - 66)) | (1L << (PARTITION - 66)) | (1L << (MATCHES - 66)) | (1L << (FOR - 66)) | (1L << (WHILE - 66)) | (1L << (USING - 66)) | (1L << (MERGE - 66)) | (1L << (MATCHED - 66)) | (1L << (NEWKW - 66)) | (1L << (CONTEXT - 66)))) != 0) || ((((_la - 134)) & ~0x3f) == 0 && ((1L << (_la - 134)) & ((1L << (GROUPING - 134)) | (1L << (GROUPING_ID - 134)) | (1L << (QUESTION - 134)) | (1L << (LPAREN - 134)) | (1L << (LBRACK - 134)) | (1L << (LCURLY - 134)) | (1L << (PLUS - 134)) | (1L << (MINUS - 134)) | (1L << (STAR - 134)) | (1L << (TICKED_STRING_LITERAL - 134)) | (1L << (QUOTED_STRING_LITERAL - 134)) | (1L << (STRING_LITERAL - 134)) | (1L << (IDENT - 134)) | (1L << (IntegerLiteral - 134)) | (1L << (FloatingPointLiteral - 134)))) != 0)) { + { + State = 2657; expressionWithTimeList(); + } + } + + State = 2660; Match(RPAREN); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class GuardWhileExpressionContext : ParserRuleContext { + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public ExpressionContext expression() { + return GetRuleContext(0); + } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public GuardWhileExpressionContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_guardWhileExpression; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterGuardWhileExpression(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitGuardWhileExpression(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitGuardWhileExpression(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public GuardWhileExpressionContext guardWhileExpression() { + GuardWhileExpressionContext _localctx = new GuardWhileExpressionContext(Context, State); + EnterRule(_localctx, 382, RULE_guardWhileExpression); + try { + EnterOuterAlt(_localctx, 1); + { + State = 2662; Match(LPAREN); + State = 2663; expression(); + State = 2664; Match(RPAREN); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class MatchUntilRangeContext : ParserRuleContext { + public ExpressionContext low; + public IToken c1; + public ExpressionContext high; + public IToken c2; + public ExpressionContext upper; + public ITerminalNode LBRACK() { return GetToken(EsperEPL2GrammarParser.LBRACK, 0); } + public ITerminalNode RBRACK() { return GetToken(EsperEPL2GrammarParser.RBRACK, 0); } + public ExpressionContext[] expression() { + return GetRuleContexts(); + } + public ExpressionContext expression(int i) { + return GetRuleContext(i); + } + public ITerminalNode COLON() { return GetToken(EsperEPL2GrammarParser.COLON, 0); } + public MatchUntilRangeContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_matchUntilRange; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterMatchUntilRange(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitMatchUntilRange(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitMatchUntilRange(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public MatchUntilRangeContext matchUntilRange() { + MatchUntilRangeContext _localctx = new MatchUntilRangeContext(Context, State); + EnterRule(_localctx, 384, RULE_matchUntilRange); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 2666; Match(LBRACK); + State = 2676; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case WINDOW: + case ESCAPE: + case NOT_EXPR: + case EVERY_EXPR: + case SUM: + case AVG: + case MAX: + case MIN: + case COALESCE: + case MEDIAN: + case STDDEV: + case AVEDEV: + case COUNT: + case CASE: + case OUTER: + case JOIN: + case LEFT: + case RIGHT: + case FULL: + case EVENTS: + case FIRST: + case LAST: + case ISTREAM: + case SCHEMA: + case UNIDIRECTIONAL: + case RETAINUNION: + case RETAININTERSECTION: + case PATTERN: + case SQL: + case METADATASQL: + case PREVIOUS: + case PREVIOUSTAIL: + case PREVIOUSCOUNT: + case PREVIOUSWINDOW: + case PRIOR: + case EXISTS: + case WEEKDAY: + case LW: + case INSTANCEOF: + case TYPEOF: + case CAST: + case CURRENT_TIMESTAMP: + case SNAPSHOT: + case VARIABLE: + case TABLE: + case UNTIL: + case AT: + case INDEX: + case BOOLEAN_TRUE: + case BOOLEAN_FALSE: + case VALUE_NULL: + case DEFINE: + case PARTITION: + case MATCHES: + case FOR: + case WHILE: + case USING: + case MERGE: + case MATCHED: + case NEWKW: + case CONTEXT: + case GROUPING: + case GROUPING_ID: + case QUESTION: + case LPAREN: + case LCURLY: + case PLUS: + case MINUS: + case TICKED_STRING_LITERAL: + case QUOTED_STRING_LITERAL: + case STRING_LITERAL: + case IDENT: + case IntegerLiteral: + case FloatingPointLiteral: + { + State = 2667; _localctx.low = expression(); + State = 2672; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==COLON) { + { + State = 2668; _localctx.c1 = Match(COLON); + State = 2670; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (((((_la - 2)) & ~0x3f) == 0 && ((1L << (_la - 2)) & ((1L << (WINDOW - 2)) | (1L << (ESCAPE - 2)) | (1L << (NOT_EXPR - 2)) | (1L << (EVERY_EXPR - 2)) | (1L << (SUM - 2)) | (1L << (AVG - 2)) | (1L << (MAX - 2)) | (1L << (MIN - 2)) | (1L << (COALESCE - 2)) | (1L << (MEDIAN - 2)) | (1L << (STDDEV - 2)) | (1L << (AVEDEV - 2)) | (1L << (COUNT - 2)) | (1L << (CASE - 2)) | (1L << (OUTER - 2)) | (1L << (JOIN - 2)) | (1L << (LEFT - 2)) | (1L << (RIGHT - 2)) | (1L << (FULL - 2)) | (1L << (EVENTS - 2)) | (1L << (FIRST - 2)) | (1L << (LAST - 2)) | (1L << (ISTREAM - 2)) | (1L << (SCHEMA - 2)) | (1L << (UNIDIRECTIONAL - 2)) | (1L << (RETAINUNION - 2)) | (1L << (RETAININTERSECTION - 2)) | (1L << (PATTERN - 2)) | (1L << (SQL - 2)) | (1L << (METADATASQL - 2)))) != 0) || ((((_la - 66)) & ~0x3f) == 0 && ((1L << (_la - 66)) & ((1L << (PREVIOUS - 66)) | (1L << (PREVIOUSTAIL - 66)) | (1L << (PREVIOUSCOUNT - 66)) | (1L << (PREVIOUSWINDOW - 66)) | (1L << (PRIOR - 66)) | (1L << (EXISTS - 66)) | (1L << (WEEKDAY - 66)) | (1L << (LW - 66)) | (1L << (INSTANCEOF - 66)) | (1L << (TYPEOF - 66)) | (1L << (CAST - 66)) | (1L << (CURRENT_TIMESTAMP - 66)) | (1L << (SNAPSHOT - 66)) | (1L << (VARIABLE - 66)) | (1L << (TABLE - 66)) | (1L << (UNTIL - 66)) | (1L << (AT - 66)) | (1L << (INDEX - 66)) | (1L << (BOOLEAN_TRUE - 66)) | (1L << (BOOLEAN_FALSE - 66)) | (1L << (VALUE_NULL - 66)) | (1L << (DEFINE - 66)) | (1L << (PARTITION - 66)) | (1L << (MATCHES - 66)) | (1L << (FOR - 66)) | (1L << (WHILE - 66)) | (1L << (USING - 66)) | (1L << (MERGE - 66)) | (1L << (MATCHED - 66)) | (1L << (NEWKW - 66)) | (1L << (CONTEXT - 66)))) != 0) || ((((_la - 134)) & ~0x3f) == 0 && ((1L << (_la - 134)) & ((1L << (GROUPING - 134)) | (1L << (GROUPING_ID - 134)) | (1L << (QUESTION - 134)) | (1L << (LPAREN - 134)) | (1L << (LCURLY - 134)) | (1L << (PLUS - 134)) | (1L << (MINUS - 134)) | (1L << (TICKED_STRING_LITERAL - 134)) | (1L << (QUOTED_STRING_LITERAL - 134)) | (1L << (STRING_LITERAL - 134)) | (1L << (IDENT - 134)) | (1L << (IntegerLiteral - 134)) | (1L << (FloatingPointLiteral - 134)))) != 0)) { + { + State = 2669; _localctx.high = expression(); + } + } + + } + } + + } + break; + case COLON: + { + State = 2674; _localctx.c2 = Match(COLON); + State = 2675; _localctx.upper = expression(); + } + break; + default: + throw new NoViableAltException(this); + } + State = 2678; Match(RBRACK); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class EventFilterExpressionContext : ParserRuleContext { + public IToken i; + public ClassIdentifierContext classIdentifier() { + return GetRuleContext(0); + } + public ITerminalNode EQUALS() { return GetToken(EsperEPL2GrammarParser.EQUALS, 0); } + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public PropertyExpressionContext propertyExpression() { + return GetRuleContext(0); + } + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public ExpressionListContext expressionList() { + return GetRuleContext(0); + } + public EventFilterExpressionContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_eventFilterExpression; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterEventFilterExpression(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitEventFilterExpression(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitEventFilterExpression(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public EventFilterExpressionContext eventFilterExpression() { + EventFilterExpressionContext _localctx = new EventFilterExpressionContext(Context, State); + EnterRule(_localctx, 386, RULE_eventFilterExpression); + paraphrases.Push("filter specification"); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 2682; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,360,Context) ) { + case 1: + { + State = 2680; _localctx.i = Match(IDENT); + State = 2681; Match(EQUALS); + } + break; + } + State = 2684; classIdentifier(); + State = 2690; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==LPAREN) { + { + State = 2685; Match(LPAREN); + State = 2687; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (((((_la - 2)) & ~0x3f) == 0 && ((1L << (_la - 2)) & ((1L << (WINDOW - 2)) | (1L << (ESCAPE - 2)) | (1L << (NOT_EXPR - 2)) | (1L << (EVERY_EXPR - 2)) | (1L << (SUM - 2)) | (1L << (AVG - 2)) | (1L << (MAX - 2)) | (1L << (MIN - 2)) | (1L << (COALESCE - 2)) | (1L << (MEDIAN - 2)) | (1L << (STDDEV - 2)) | (1L << (AVEDEV - 2)) | (1L << (COUNT - 2)) | (1L << (CASE - 2)) | (1L << (OUTER - 2)) | (1L << (JOIN - 2)) | (1L << (LEFT - 2)) | (1L << (RIGHT - 2)) | (1L << (FULL - 2)) | (1L << (EVENTS - 2)) | (1L << (FIRST - 2)) | (1L << (LAST - 2)) | (1L << (ISTREAM - 2)) | (1L << (SCHEMA - 2)) | (1L << (UNIDIRECTIONAL - 2)) | (1L << (RETAINUNION - 2)) | (1L << (RETAININTERSECTION - 2)) | (1L << (PATTERN - 2)) | (1L << (SQL - 2)) | (1L << (METADATASQL - 2)))) != 0) || ((((_la - 66)) & ~0x3f) == 0 && ((1L << (_la - 66)) & ((1L << (PREVIOUS - 66)) | (1L << (PREVIOUSTAIL - 66)) | (1L << (PREVIOUSCOUNT - 66)) | (1L << (PREVIOUSWINDOW - 66)) | (1L << (PRIOR - 66)) | (1L << (EXISTS - 66)) | (1L << (WEEKDAY - 66)) | (1L << (LW - 66)) | (1L << (INSTANCEOF - 66)) | (1L << (TYPEOF - 66)) | (1L << (CAST - 66)) | (1L << (CURRENT_TIMESTAMP - 66)) | (1L << (SNAPSHOT - 66)) | (1L << (VARIABLE - 66)) | (1L << (TABLE - 66)) | (1L << (UNTIL - 66)) | (1L << (AT - 66)) | (1L << (INDEX - 66)) | (1L << (BOOLEAN_TRUE - 66)) | (1L << (BOOLEAN_FALSE - 66)) | (1L << (VALUE_NULL - 66)) | (1L << (DEFINE - 66)) | (1L << (PARTITION - 66)) | (1L << (MATCHES - 66)) | (1L << (FOR - 66)) | (1L << (WHILE - 66)) | (1L << (USING - 66)) | (1L << (MERGE - 66)) | (1L << (MATCHED - 66)) | (1L << (NEWKW - 66)) | (1L << (CONTEXT - 66)))) != 0) || ((((_la - 134)) & ~0x3f) == 0 && ((1L << (_la - 134)) & ((1L << (GROUPING - 134)) | (1L << (GROUPING_ID - 134)) | (1L << (QUESTION - 134)) | (1L << (LPAREN - 134)) | (1L << (LCURLY - 134)) | (1L << (PLUS - 134)) | (1L << (MINUS - 134)) | (1L << (TICKED_STRING_LITERAL - 134)) | (1L << (QUOTED_STRING_LITERAL - 134)) | (1L << (STRING_LITERAL - 134)) | (1L << (IDENT - 134)) | (1L << (IntegerLiteral - 134)) | (1L << (FloatingPointLiteral - 134)))) != 0)) { + { + State = 2686; expressionList(); + } + } + + State = 2689; Match(RPAREN); + } + } + + State = 2693; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==LBRACK) { + { + State = 2692; propertyExpression(); + } + } + + } + Context.Stop = TokenStream.LT(-1); + paraphrases.Pop(); + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class PropertyExpressionContext : ParserRuleContext { + public PropertyExpressionAtomicContext[] propertyExpressionAtomic() { + return GetRuleContexts(); + } + public PropertyExpressionAtomicContext propertyExpressionAtomic(int i) { + return GetRuleContext(i); + } + public PropertyExpressionContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_propertyExpression; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterPropertyExpression(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitPropertyExpression(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitPropertyExpression(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public PropertyExpressionContext propertyExpression() { + PropertyExpressionContext _localctx = new PropertyExpressionContext(Context, State); + EnterRule(_localctx, 388, RULE_propertyExpression); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 2695; propertyExpressionAtomic(); + State = 2699; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==LBRACK) { + { + { + State = 2696; propertyExpressionAtomic(); + } + } + State = 2701; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class PropertyExpressionAtomicContext : ParserRuleContext { + public IToken n; + public ExpressionContext where; + public ITerminalNode LBRACK() { return GetToken(EsperEPL2GrammarParser.LBRACK, 0); } + public ExpressionContext[] expression() { + return GetRuleContexts(); + } + public ExpressionContext expression(int i) { + return GetRuleContext(i); + } + public ITerminalNode RBRACK() { return GetToken(EsperEPL2GrammarParser.RBRACK, 0); } + public PropertyExpressionSelectContext propertyExpressionSelect() { + return GetRuleContext(0); + } + public TypeExpressionAnnotationContext typeExpressionAnnotation() { + return GetRuleContext(0); + } + public ITerminalNode AS() { return GetToken(EsperEPL2GrammarParser.AS, 0); } + public ITerminalNode WHERE() { return GetToken(EsperEPL2GrammarParser.WHERE, 0); } + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public PropertyExpressionAtomicContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_propertyExpressionAtomic; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterPropertyExpressionAtomic(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitPropertyExpressionAtomic(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitPropertyExpressionAtomic(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public PropertyExpressionAtomicContext propertyExpressionAtomic() { + PropertyExpressionAtomicContext _localctx = new PropertyExpressionAtomicContext(Context, State); + EnterRule(_localctx, 390, RULE_propertyExpressionAtomic); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 2702; Match(LBRACK); + State = 2704; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==SELECT) { + { + State = 2703; propertyExpressionSelect(); + } + } + + State = 2706; expression(); + State = 2708; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==ATCHAR) { + { + State = 2707; typeExpressionAnnotation(); + } + } + + State = 2712; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==AS) { + { + State = 2710; Match(AS); + State = 2711; _localctx.n = Match(IDENT); + } + } + + State = 2716; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==WHERE) { + { + State = 2714; Match(WHERE); + State = 2715; _localctx.where = expression(); + } + } + + State = 2718; Match(RBRACK); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class PropertyExpressionSelectContext : ParserRuleContext { + public ITerminalNode SELECT() { return GetToken(EsperEPL2GrammarParser.SELECT, 0); } + public PropertySelectionListContext propertySelectionList() { + return GetRuleContext(0); + } + public ITerminalNode FROM() { return GetToken(EsperEPL2GrammarParser.FROM, 0); } + public PropertyExpressionSelectContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_propertyExpressionSelect; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterPropertyExpressionSelect(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitPropertyExpressionSelect(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitPropertyExpressionSelect(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public PropertyExpressionSelectContext propertyExpressionSelect() { + PropertyExpressionSelectContext _localctx = new PropertyExpressionSelectContext(Context, State); + EnterRule(_localctx, 392, RULE_propertyExpressionSelect); + try { + EnterOuterAlt(_localctx, 1); + { + State = 2720; Match(SELECT); + State = 2721; propertySelectionList(); + State = 2722; Match(FROM); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class PropertySelectionListContext : ParserRuleContext { + public PropertySelectionListElementContext[] propertySelectionListElement() { + return GetRuleContexts(); + } + public PropertySelectionListElementContext propertySelectionListElement(int i) { + return GetRuleContext(i); + } + public ITerminalNode[] COMMA() { return GetTokens(EsperEPL2GrammarParser.COMMA); } + public ITerminalNode COMMA(int i) { + return GetToken(EsperEPL2GrammarParser.COMMA, i); + } + public PropertySelectionListContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_propertySelectionList; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterPropertySelectionList(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitPropertySelectionList(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitPropertySelectionList(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public PropertySelectionListContext propertySelectionList() { + PropertySelectionListContext _localctx = new PropertySelectionListContext(Context, State); + EnterRule(_localctx, 394, RULE_propertySelectionList); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 2724; propertySelectionListElement(); + State = 2729; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 2725; Match(COMMA); + State = 2726; propertySelectionListElement(); + } + } + State = 2731; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class PropertySelectionListElementContext : ParserRuleContext { + public IToken s; + public ITerminalNode STAR() { return GetToken(EsperEPL2GrammarParser.STAR, 0); } + public PropertyStreamSelectorContext propertyStreamSelector() { + return GetRuleContext(0); + } + public ExpressionContext expression() { + return GetRuleContext(0); + } + public ITerminalNode AS() { return GetToken(EsperEPL2GrammarParser.AS, 0); } + public KeywordAllowedIdentContext keywordAllowedIdent() { + return GetRuleContext(0); + } + public PropertySelectionListElementContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_propertySelectionListElement; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterPropertySelectionListElement(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitPropertySelectionListElement(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitPropertySelectionListElement(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public PropertySelectionListElementContext propertySelectionListElement() { + PropertySelectionListElementContext _localctx = new PropertySelectionListElementContext(Context, State); + EnterRule(_localctx, 396, RULE_propertySelectionListElement); + int _la; + try { + State = 2739; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,371,Context) ) { + case 1: + EnterOuterAlt(_localctx, 1); + { + State = 2732; _localctx.s = Match(STAR); + } + break; + case 2: + EnterOuterAlt(_localctx, 2); + { + State = 2733; propertyStreamSelector(); + } + break; + case 3: + EnterOuterAlt(_localctx, 3); + { + State = 2734; expression(); + State = 2737; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==AS) { + { + State = 2735; Match(AS); + State = 2736; keywordAllowedIdent(); + } + } + + } + break; + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class PropertyStreamSelectorContext : ParserRuleContext { + public IToken s; + public IToken i; + public ITerminalNode DOT() { return GetToken(EsperEPL2GrammarParser.DOT, 0); } + public ITerminalNode STAR() { return GetToken(EsperEPL2GrammarParser.STAR, 0); } + public ITerminalNode[] IDENT() { return GetTokens(EsperEPL2GrammarParser.IDENT); } + public ITerminalNode IDENT(int i) { + return GetToken(EsperEPL2GrammarParser.IDENT, i); + } + public ITerminalNode AS() { return GetToken(EsperEPL2GrammarParser.AS, 0); } + public PropertyStreamSelectorContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_propertyStreamSelector; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterPropertyStreamSelector(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitPropertyStreamSelector(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitPropertyStreamSelector(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public PropertyStreamSelectorContext propertyStreamSelector() { + PropertyStreamSelectorContext _localctx = new PropertyStreamSelectorContext(Context, State); + EnterRule(_localctx, 398, RULE_propertyStreamSelector); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 2741; _localctx.s = Match(IDENT); + State = 2742; Match(DOT); + State = 2743; Match(STAR); + State = 2746; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==AS) { + { + State = 2744; Match(AS); + State = 2745; _localctx.i = Match(IDENT); + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class TypeExpressionAnnotationContext : ParserRuleContext { + public IToken n; + public IToken v; + public ITerminalNode ATCHAR() { return GetToken(EsperEPL2GrammarParser.ATCHAR, 0); } + public ITerminalNode[] IDENT() { return GetTokens(EsperEPL2GrammarParser.IDENT); } + public ITerminalNode IDENT(int i) { + return GetToken(EsperEPL2GrammarParser.IDENT, i); + } + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public TypeExpressionAnnotationContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_typeExpressionAnnotation; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterTypeExpressionAnnotation(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitTypeExpressionAnnotation(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitTypeExpressionAnnotation(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public TypeExpressionAnnotationContext typeExpressionAnnotation() { + TypeExpressionAnnotationContext _localctx = new TypeExpressionAnnotationContext(Context, State); + EnterRule(_localctx, 400, RULE_typeExpressionAnnotation); + try { + EnterOuterAlt(_localctx, 1); + { + State = 2748; Match(ATCHAR); + State = 2749; _localctx.n = Match(IDENT); + { + State = 2750; Match(LPAREN); + State = 2751; _localctx.v = Match(IDENT); + State = 2752; Match(RPAREN); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class PatternFilterExpressionContext : ParserRuleContext { + public IToken i; + public ClassIdentifierContext classIdentifier() { + return GetRuleContext(0); + } + public ITerminalNode EQUALS() { return GetToken(EsperEPL2GrammarParser.EQUALS, 0); } + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public PropertyExpressionContext propertyExpression() { + return GetRuleContext(0); + } + public PatternFilterAnnotationContext patternFilterAnnotation() { + return GetRuleContext(0); + } + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public ExpressionListContext expressionList() { + return GetRuleContext(0); + } + public PatternFilterExpressionContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_patternFilterExpression; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterPatternFilterExpression(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitPatternFilterExpression(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitPatternFilterExpression(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public PatternFilterExpressionContext patternFilterExpression() { + PatternFilterExpressionContext _localctx = new PatternFilterExpressionContext(Context, State); + EnterRule(_localctx, 402, RULE_patternFilterExpression); + paraphrases.Push("filter specification"); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 2756; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,373,Context) ) { + case 1: + { + State = 2754; _localctx.i = Match(IDENT); + State = 2755; Match(EQUALS); + } + break; + } + State = 2758; classIdentifier(); + State = 2764; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==LPAREN) { + { + State = 2759; Match(LPAREN); + State = 2761; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (((((_la - 2)) & ~0x3f) == 0 && ((1L << (_la - 2)) & ((1L << (WINDOW - 2)) | (1L << (ESCAPE - 2)) | (1L << (NOT_EXPR - 2)) | (1L << (EVERY_EXPR - 2)) | (1L << (SUM - 2)) | (1L << (AVG - 2)) | (1L << (MAX - 2)) | (1L << (MIN - 2)) | (1L << (COALESCE - 2)) | (1L << (MEDIAN - 2)) | (1L << (STDDEV - 2)) | (1L << (AVEDEV - 2)) | (1L << (COUNT - 2)) | (1L << (CASE - 2)) | (1L << (OUTER - 2)) | (1L << (JOIN - 2)) | (1L << (LEFT - 2)) | (1L << (RIGHT - 2)) | (1L << (FULL - 2)) | (1L << (EVENTS - 2)) | (1L << (FIRST - 2)) | (1L << (LAST - 2)) | (1L << (ISTREAM - 2)) | (1L << (SCHEMA - 2)) | (1L << (UNIDIRECTIONAL - 2)) | (1L << (RETAINUNION - 2)) | (1L << (RETAININTERSECTION - 2)) | (1L << (PATTERN - 2)) | (1L << (SQL - 2)) | (1L << (METADATASQL - 2)))) != 0) || ((((_la - 66)) & ~0x3f) == 0 && ((1L << (_la - 66)) & ((1L << (PREVIOUS - 66)) | (1L << (PREVIOUSTAIL - 66)) | (1L << (PREVIOUSCOUNT - 66)) | (1L << (PREVIOUSWINDOW - 66)) | (1L << (PRIOR - 66)) | (1L << (EXISTS - 66)) | (1L << (WEEKDAY - 66)) | (1L << (LW - 66)) | (1L << (INSTANCEOF - 66)) | (1L << (TYPEOF - 66)) | (1L << (CAST - 66)) | (1L << (CURRENT_TIMESTAMP - 66)) | (1L << (SNAPSHOT - 66)) | (1L << (VARIABLE - 66)) | (1L << (TABLE - 66)) | (1L << (UNTIL - 66)) | (1L << (AT - 66)) | (1L << (INDEX - 66)) | (1L << (BOOLEAN_TRUE - 66)) | (1L << (BOOLEAN_FALSE - 66)) | (1L << (VALUE_NULL - 66)) | (1L << (DEFINE - 66)) | (1L << (PARTITION - 66)) | (1L << (MATCHES - 66)) | (1L << (FOR - 66)) | (1L << (WHILE - 66)) | (1L << (USING - 66)) | (1L << (MERGE - 66)) | (1L << (MATCHED - 66)) | (1L << (NEWKW - 66)) | (1L << (CONTEXT - 66)))) != 0) || ((((_la - 134)) & ~0x3f) == 0 && ((1L << (_la - 134)) & ((1L << (GROUPING - 134)) | (1L << (GROUPING_ID - 134)) | (1L << (QUESTION - 134)) | (1L << (LPAREN - 134)) | (1L << (LCURLY - 134)) | (1L << (PLUS - 134)) | (1L << (MINUS - 134)) | (1L << (TICKED_STRING_LITERAL - 134)) | (1L << (QUOTED_STRING_LITERAL - 134)) | (1L << (STRING_LITERAL - 134)) | (1L << (IDENT - 134)) | (1L << (IntegerLiteral - 134)) | (1L << (FloatingPointLiteral - 134)))) != 0)) { + { + State = 2760; expressionList(); + } + } + + State = 2763; Match(RPAREN); + } + } + + State = 2767; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==LBRACK) { + { + State = 2766; propertyExpression(); + } + } + + State = 2770; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==ATCHAR) { + { + State = 2769; patternFilterAnnotation(); + } + } + + } + Context.Stop = TokenStream.LT(-1); + paraphrases.Pop(); + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class PatternFilterAnnotationContext : ParserRuleContext { + public IToken i; + public ITerminalNode ATCHAR() { return GetToken(EsperEPL2GrammarParser.ATCHAR, 0); } + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public NumberContext number() { + return GetRuleContext(0); + } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public PatternFilterAnnotationContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_patternFilterAnnotation; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterPatternFilterAnnotation(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitPatternFilterAnnotation(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitPatternFilterAnnotation(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public PatternFilterAnnotationContext patternFilterAnnotation() { + PatternFilterAnnotationContext _localctx = new PatternFilterAnnotationContext(Context, State); + EnterRule(_localctx, 404, RULE_patternFilterAnnotation); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 2772; Match(ATCHAR); + State = 2773; _localctx.i = Match(IDENT); + State = 2778; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==LPAREN) { + { + State = 2774; Match(LPAREN); + State = 2775; number(); + State = 2776; Match(RPAREN); + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class ClassIdentifierContext : ParserRuleContext { + public EscapableStrContext i1; + public EscapableStrContext i2; + public EscapableStrContext[] escapableStr() { + return GetRuleContexts(); + } + public EscapableStrContext escapableStr(int i) { + return GetRuleContext(i); + } + public ITerminalNode[] DOT() { return GetTokens(EsperEPL2GrammarParser.DOT); } + public ITerminalNode DOT(int i) { + return GetToken(EsperEPL2GrammarParser.DOT, i); + } + public ClassIdentifierContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_classIdentifier; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterClassIdentifier(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitClassIdentifier(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitClassIdentifier(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public ClassIdentifierContext classIdentifier() { + ClassIdentifierContext _localctx = new ClassIdentifierContext(Context, State); + EnterRule(_localctx, 406, RULE_classIdentifier); + try { + int _alt; + EnterOuterAlt(_localctx, 1); + { + State = 2780; _localctx.i1 = escapableStr(); + State = 2785; + ErrorHandler.Sync(this); + _alt = Interpreter.AdaptivePredict(TokenStream,379,Context); + while ( _alt!=2 && _alt!=global::Antlr4.Runtime.Atn.ATN.INVALID_ALT_NUMBER ) { + if ( _alt==1 ) { + { + { + State = 2781; Match(DOT); + State = 2782; _localctx.i2 = escapableStr(); + } + } + } + State = 2787; + ErrorHandler.Sync(this); + _alt = Interpreter.AdaptivePredict(TokenStream,379,Context); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class SlashIdentifierContext : ParserRuleContext { + public IToken d; + public EscapableStrContext i1; + public EscapableStrContext i2; + public EscapableStrContext[] escapableStr() { + return GetRuleContexts(); + } + public EscapableStrContext escapableStr(int i) { + return GetRuleContext(i); + } + public ITerminalNode[] DIV() { return GetTokens(EsperEPL2GrammarParser.DIV); } + public ITerminalNode DIV(int i) { + return GetToken(EsperEPL2GrammarParser.DIV, i); + } + public SlashIdentifierContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_slashIdentifier; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterSlashIdentifier(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitSlashIdentifier(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitSlashIdentifier(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public SlashIdentifierContext slashIdentifier() { + SlashIdentifierContext _localctx = new SlashIdentifierContext(Context, State); + EnterRule(_localctx, 408, RULE_slashIdentifier); + int _la; + try { + int _alt; + EnterOuterAlt(_localctx, 1); + { + State = 2789; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==DIV) { + { + State = 2788; _localctx.d = Match(DIV); + } + } + + State = 2791; _localctx.i1 = escapableStr(); + State = 2796; + ErrorHandler.Sync(this); + _alt = Interpreter.AdaptivePredict(TokenStream,381,Context); + while ( _alt!=2 && _alt!=global::Antlr4.Runtime.Atn.ATN.INVALID_ALT_NUMBER ) { + if ( _alt==1 ) { + { + { + State = 2792; Match(DIV); + State = 2793; _localctx.i2 = escapableStr(); + } + } + } + State = 2798; + ErrorHandler.Sync(this); + _alt = Interpreter.AdaptivePredict(TokenStream,381,Context); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class ExpressionListWithNamedContext : ParserRuleContext { + public ExpressionWithNamedContext[] expressionWithNamed() { + return GetRuleContexts(); + } + public ExpressionWithNamedContext expressionWithNamed(int i) { + return GetRuleContext(i); + } + public ITerminalNode[] COMMA() { return GetTokens(EsperEPL2GrammarParser.COMMA); } + public ITerminalNode COMMA(int i) { + return GetToken(EsperEPL2GrammarParser.COMMA, i); + } + public ExpressionListWithNamedContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_expressionListWithNamed; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterExpressionListWithNamed(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitExpressionListWithNamed(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitExpressionListWithNamed(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public ExpressionListWithNamedContext expressionListWithNamed() { + ExpressionListWithNamedContext _localctx = new ExpressionListWithNamedContext(Context, State); + EnterRule(_localctx, 410, RULE_expressionListWithNamed); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 2799; expressionWithNamed(); + State = 2804; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 2800; Match(COMMA); + State = 2801; expressionWithNamed(); + } + } + State = 2806; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class ExpressionListWithNamedWithTimeContext : ParserRuleContext { + public ExpressionWithNamedWithTimeContext[] expressionWithNamedWithTime() { + return GetRuleContexts(); + } + public ExpressionWithNamedWithTimeContext expressionWithNamedWithTime(int i) { + return GetRuleContext(i); + } + public ITerminalNode[] COMMA() { return GetTokens(EsperEPL2GrammarParser.COMMA); } + public ITerminalNode COMMA(int i) { + return GetToken(EsperEPL2GrammarParser.COMMA, i); + } + public ExpressionListWithNamedWithTimeContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_expressionListWithNamedWithTime; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterExpressionListWithNamedWithTime(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitExpressionListWithNamedWithTime(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitExpressionListWithNamedWithTime(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public ExpressionListWithNamedWithTimeContext expressionListWithNamedWithTime() { + ExpressionListWithNamedWithTimeContext _localctx = new ExpressionListWithNamedWithTimeContext(Context, State); + EnterRule(_localctx, 412, RULE_expressionListWithNamedWithTime); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 2807; expressionWithNamedWithTime(); + State = 2812; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 2808; Match(COMMA); + State = 2809; expressionWithNamedWithTime(); + } + } + State = 2814; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class ExpressionWithNamedContext : ParserRuleContext { + public ExpressionNamedParameterContext expressionNamedParameter() { + return GetRuleContext(0); + } + public ExpressionWithTimeContext expressionWithTime() { + return GetRuleContext(0); + } + public ExpressionWithNamedContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_expressionWithNamed; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterExpressionWithNamed(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitExpressionWithNamed(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitExpressionWithNamed(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public ExpressionWithNamedContext expressionWithNamed() { + ExpressionWithNamedContext _localctx = new ExpressionWithNamedContext(Context, State); + EnterRule(_localctx, 414, RULE_expressionWithNamed); + try { + State = 2817; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,384,Context) ) { + case 1: + EnterOuterAlt(_localctx, 1); + { + State = 2815; expressionNamedParameter(); + } + break; + case 2: + EnterOuterAlt(_localctx, 2); + { + State = 2816; expressionWithTime(); + } + break; + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class ExpressionWithNamedWithTimeContext : ParserRuleContext { + public ExpressionNamedParameterWithTimeContext expressionNamedParameterWithTime() { + return GetRuleContext(0); + } + public ExpressionWithTimeInclLastContext expressionWithTimeInclLast() { + return GetRuleContext(0); + } + public ExpressionWithNamedWithTimeContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_expressionWithNamedWithTime; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterExpressionWithNamedWithTime(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitExpressionWithNamedWithTime(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitExpressionWithNamedWithTime(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public ExpressionWithNamedWithTimeContext expressionWithNamedWithTime() { + ExpressionWithNamedWithTimeContext _localctx = new ExpressionWithNamedWithTimeContext(Context, State); + EnterRule(_localctx, 416, RULE_expressionWithNamedWithTime); + try { + State = 2821; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,385,Context) ) { + case 1: + EnterOuterAlt(_localctx, 1); + { + State = 2819; expressionNamedParameterWithTime(); + } + break; + case 2: + EnterOuterAlt(_localctx, 2); + { + State = 2820; expressionWithTimeInclLast(); + } + break; + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class ExpressionNamedParameterContext : ParserRuleContext { + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public ITerminalNode COLON() { return GetToken(EsperEPL2GrammarParser.COLON, 0); } + public ExpressionContext expression() { + return GetRuleContext(0); + } + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public ExpressionListContext expressionList() { + return GetRuleContext(0); + } + public ExpressionNamedParameterContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_expressionNamedParameter; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterExpressionNamedParameter(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitExpressionNamedParameter(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitExpressionNamedParameter(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public ExpressionNamedParameterContext expressionNamedParameter() { + ExpressionNamedParameterContext _localctx = new ExpressionNamedParameterContext(Context, State); + EnterRule(_localctx, 418, RULE_expressionNamedParameter); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 2823; Match(IDENT); + State = 2824; Match(COLON); + State = 2831; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,387,Context) ) { + case 1: + { + State = 2825; expression(); + } + break; + case 2: + { + State = 2826; Match(LPAREN); + State = 2828; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (((((_la - 2)) & ~0x3f) == 0 && ((1L << (_la - 2)) & ((1L << (WINDOW - 2)) | (1L << (ESCAPE - 2)) | (1L << (NOT_EXPR - 2)) | (1L << (EVERY_EXPR - 2)) | (1L << (SUM - 2)) | (1L << (AVG - 2)) | (1L << (MAX - 2)) | (1L << (MIN - 2)) | (1L << (COALESCE - 2)) | (1L << (MEDIAN - 2)) | (1L << (STDDEV - 2)) | (1L << (AVEDEV - 2)) | (1L << (COUNT - 2)) | (1L << (CASE - 2)) | (1L << (OUTER - 2)) | (1L << (JOIN - 2)) | (1L << (LEFT - 2)) | (1L << (RIGHT - 2)) | (1L << (FULL - 2)) | (1L << (EVENTS - 2)) | (1L << (FIRST - 2)) | (1L << (LAST - 2)) | (1L << (ISTREAM - 2)) | (1L << (SCHEMA - 2)) | (1L << (UNIDIRECTIONAL - 2)) | (1L << (RETAINUNION - 2)) | (1L << (RETAININTERSECTION - 2)) | (1L << (PATTERN - 2)) | (1L << (SQL - 2)) | (1L << (METADATASQL - 2)))) != 0) || ((((_la - 66)) & ~0x3f) == 0 && ((1L << (_la - 66)) & ((1L << (PREVIOUS - 66)) | (1L << (PREVIOUSTAIL - 66)) | (1L << (PREVIOUSCOUNT - 66)) | (1L << (PREVIOUSWINDOW - 66)) | (1L << (PRIOR - 66)) | (1L << (EXISTS - 66)) | (1L << (WEEKDAY - 66)) | (1L << (LW - 66)) | (1L << (INSTANCEOF - 66)) | (1L << (TYPEOF - 66)) | (1L << (CAST - 66)) | (1L << (CURRENT_TIMESTAMP - 66)) | (1L << (SNAPSHOT - 66)) | (1L << (VARIABLE - 66)) | (1L << (TABLE - 66)) | (1L << (UNTIL - 66)) | (1L << (AT - 66)) | (1L << (INDEX - 66)) | (1L << (BOOLEAN_TRUE - 66)) | (1L << (BOOLEAN_FALSE - 66)) | (1L << (VALUE_NULL - 66)) | (1L << (DEFINE - 66)) | (1L << (PARTITION - 66)) | (1L << (MATCHES - 66)) | (1L << (FOR - 66)) | (1L << (WHILE - 66)) | (1L << (USING - 66)) | (1L << (MERGE - 66)) | (1L << (MATCHED - 66)) | (1L << (NEWKW - 66)) | (1L << (CONTEXT - 66)))) != 0) || ((((_la - 134)) & ~0x3f) == 0 && ((1L << (_la - 134)) & ((1L << (GROUPING - 134)) | (1L << (GROUPING_ID - 134)) | (1L << (QUESTION - 134)) | (1L << (LPAREN - 134)) | (1L << (LCURLY - 134)) | (1L << (PLUS - 134)) | (1L << (MINUS - 134)) | (1L << (TICKED_STRING_LITERAL - 134)) | (1L << (QUOTED_STRING_LITERAL - 134)) | (1L << (STRING_LITERAL - 134)) | (1L << (IDENT - 134)) | (1L << (IntegerLiteral - 134)) | (1L << (FloatingPointLiteral - 134)))) != 0)) { + { + State = 2827; expressionList(); + } + } + + State = 2830; Match(RPAREN); + } + break; + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class ExpressionNamedParameterWithTimeContext : ParserRuleContext { + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public ITerminalNode COLON() { return GetToken(EsperEPL2GrammarParser.COLON, 0); } + public ExpressionWithTimeContext expressionWithTime() { + return GetRuleContext(0); + } + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public ExpressionWithTimeListContext expressionWithTimeList() { + return GetRuleContext(0); + } + public ExpressionNamedParameterWithTimeContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_expressionNamedParameterWithTime; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterExpressionNamedParameterWithTime(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitExpressionNamedParameterWithTime(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitExpressionNamedParameterWithTime(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public ExpressionNamedParameterWithTimeContext expressionNamedParameterWithTime() { + ExpressionNamedParameterWithTimeContext _localctx = new ExpressionNamedParameterWithTimeContext(Context, State); + EnterRule(_localctx, 420, RULE_expressionNamedParameterWithTime); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 2833; Match(IDENT); + State = 2834; Match(COLON); + State = 2841; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,389,Context) ) { + case 1: + { + State = 2835; expressionWithTime(); + } + break; + case 2: + { + State = 2836; Match(LPAREN); + State = 2838; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (((((_la - 2)) & ~0x3f) == 0 && ((1L << (_la - 2)) & ((1L << (WINDOW - 2)) | (1L << (ESCAPE - 2)) | (1L << (NOT_EXPR - 2)) | (1L << (EVERY_EXPR - 2)) | (1L << (SUM - 2)) | (1L << (AVG - 2)) | (1L << (MAX - 2)) | (1L << (MIN - 2)) | (1L << (COALESCE - 2)) | (1L << (MEDIAN - 2)) | (1L << (STDDEV - 2)) | (1L << (AVEDEV - 2)) | (1L << (COUNT - 2)) | (1L << (CASE - 2)) | (1L << (OUTER - 2)) | (1L << (JOIN - 2)) | (1L << (LEFT - 2)) | (1L << (RIGHT - 2)) | (1L << (FULL - 2)) | (1L << (EVENTS - 2)) | (1L << (FIRST - 2)) | (1L << (LAST - 2)) | (1L << (ISTREAM - 2)) | (1L << (SCHEMA - 2)) | (1L << (UNIDIRECTIONAL - 2)) | (1L << (RETAINUNION - 2)) | (1L << (RETAININTERSECTION - 2)) | (1L << (PATTERN - 2)) | (1L << (SQL - 2)) | (1L << (METADATASQL - 2)))) != 0) || ((((_la - 66)) & ~0x3f) == 0 && ((1L << (_la - 66)) & ((1L << (PREVIOUS - 66)) | (1L << (PREVIOUSTAIL - 66)) | (1L << (PREVIOUSCOUNT - 66)) | (1L << (PREVIOUSWINDOW - 66)) | (1L << (PRIOR - 66)) | (1L << (EXISTS - 66)) | (1L << (WEEKDAY - 66)) | (1L << (LW - 66)) | (1L << (INSTANCEOF - 66)) | (1L << (TYPEOF - 66)) | (1L << (CAST - 66)) | (1L << (CURRENT_TIMESTAMP - 66)) | (1L << (SNAPSHOT - 66)) | (1L << (VARIABLE - 66)) | (1L << (TABLE - 66)) | (1L << (UNTIL - 66)) | (1L << (AT - 66)) | (1L << (INDEX - 66)) | (1L << (BOOLEAN_TRUE - 66)) | (1L << (BOOLEAN_FALSE - 66)) | (1L << (VALUE_NULL - 66)) | (1L << (DEFINE - 66)) | (1L << (PARTITION - 66)) | (1L << (MATCHES - 66)) | (1L << (FOR - 66)) | (1L << (WHILE - 66)) | (1L << (USING - 66)) | (1L << (MERGE - 66)) | (1L << (MATCHED - 66)) | (1L << (NEWKW - 66)) | (1L << (CONTEXT - 66)))) != 0) || ((((_la - 134)) & ~0x3f) == 0 && ((1L << (_la - 134)) & ((1L << (GROUPING - 134)) | (1L << (GROUPING_ID - 134)) | (1L << (QUESTION - 134)) | (1L << (LPAREN - 134)) | (1L << (LBRACK - 134)) | (1L << (LCURLY - 134)) | (1L << (PLUS - 134)) | (1L << (MINUS - 134)) | (1L << (STAR - 134)) | (1L << (TICKED_STRING_LITERAL - 134)) | (1L << (QUOTED_STRING_LITERAL - 134)) | (1L << (STRING_LITERAL - 134)) | (1L << (IDENT - 134)) | (1L << (IntegerLiteral - 134)) | (1L << (FloatingPointLiteral - 134)))) != 0)) { + { + State = 2837; expressionWithTimeList(); + } + } + + State = 2840; Match(RPAREN); + } + break; + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class ExpressionListContext : ParserRuleContext { + public ExpressionContext[] expression() { + return GetRuleContexts(); + } + public ExpressionContext expression(int i) { + return GetRuleContext(i); + } + public ITerminalNode[] COMMA() { return GetTokens(EsperEPL2GrammarParser.COMMA); } + public ITerminalNode COMMA(int i) { + return GetToken(EsperEPL2GrammarParser.COMMA, i); + } + public ExpressionListContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_expressionList; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterExpressionList(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitExpressionList(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitExpressionList(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public ExpressionListContext expressionList() { + ExpressionListContext _localctx = new ExpressionListContext(Context, State); + EnterRule(_localctx, 422, RULE_expressionList); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 2843; expression(); + State = 2848; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 2844; Match(COMMA); + State = 2845; expression(); + } + } + State = 2850; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class ExpressionWithTimeListContext : ParserRuleContext { + public ExpressionWithTimeInclLastContext[] expressionWithTimeInclLast() { + return GetRuleContexts(); + } + public ExpressionWithTimeInclLastContext expressionWithTimeInclLast(int i) { + return GetRuleContext(i); + } + public ITerminalNode[] COMMA() { return GetTokens(EsperEPL2GrammarParser.COMMA); } + public ITerminalNode COMMA(int i) { + return GetToken(EsperEPL2GrammarParser.COMMA, i); + } + public ExpressionWithTimeListContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_expressionWithTimeList; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterExpressionWithTimeList(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitExpressionWithTimeList(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitExpressionWithTimeList(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public ExpressionWithTimeListContext expressionWithTimeList() { + ExpressionWithTimeListContext _localctx = new ExpressionWithTimeListContext(Context, State); + EnterRule(_localctx, 424, RULE_expressionWithTimeList); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 2851; expressionWithTimeInclLast(); + State = 2856; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 2852; Match(COMMA); + State = 2853; expressionWithTimeInclLast(); + } + } + State = 2858; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class ExpressionWithTimeContext : ParserRuleContext { + public LastWeekdayOperandContext lastWeekdayOperand() { + return GetRuleContext(0); + } + public TimePeriodContext timePeriod() { + return GetRuleContext(0); + } + public ExpressionQualifyableContext expressionQualifyable() { + return GetRuleContext(0); + } + public RangeOperandContext rangeOperand() { + return GetRuleContext(0); + } + public FrequencyOperandContext frequencyOperand() { + return GetRuleContext(0); + } + public LastOperatorContext lastOperator() { + return GetRuleContext(0); + } + public WeekDayOperatorContext weekDayOperator() { + return GetRuleContext(0); + } + public NumericParameterListContext numericParameterList() { + return GetRuleContext(0); + } + public ITerminalNode STAR() { return GetToken(EsperEPL2GrammarParser.STAR, 0); } + public PropertyStreamSelectorContext propertyStreamSelector() { + return GetRuleContext(0); + } + public ExpressionWithTimeContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_expressionWithTime; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterExpressionWithTime(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitExpressionWithTime(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitExpressionWithTime(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public ExpressionWithTimeContext expressionWithTime() { + ExpressionWithTimeContext _localctx = new ExpressionWithTimeContext(Context, State); + EnterRule(_localctx, 426, RULE_expressionWithTime); + try { + State = 2869; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,392,Context) ) { + case 1: + EnterOuterAlt(_localctx, 1); + { + State = 2859; lastWeekdayOperand(); + } + break; + case 2: + EnterOuterAlt(_localctx, 2); + { + State = 2860; timePeriod(); + } + break; + case 3: + EnterOuterAlt(_localctx, 3); + { + State = 2861; expressionQualifyable(); + } + break; + case 4: + EnterOuterAlt(_localctx, 4); + { + State = 2862; rangeOperand(); + } + break; + case 5: + EnterOuterAlt(_localctx, 5); + { + State = 2863; frequencyOperand(); + } + break; + case 6: + EnterOuterAlt(_localctx, 6); + { + State = 2864; lastOperator(); + } + break; + case 7: + EnterOuterAlt(_localctx, 7); + { + State = 2865; weekDayOperator(); + } + break; + case 8: + EnterOuterAlt(_localctx, 8); + { + State = 2866; numericParameterList(); + } + break; + case 9: + EnterOuterAlt(_localctx, 9); + { + State = 2867; Match(STAR); + } + break; + case 10: + EnterOuterAlt(_localctx, 10); + { + State = 2868; propertyStreamSelector(); + } + break; + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class ExpressionWithTimeInclLastContext : ParserRuleContext { + public LastOperandContext lastOperand() { + return GetRuleContext(0); + } + public ExpressionWithTimeContext expressionWithTime() { + return GetRuleContext(0); + } + public ExpressionWithTimeInclLastContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_expressionWithTimeInclLast; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterExpressionWithTimeInclLast(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitExpressionWithTimeInclLast(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitExpressionWithTimeInclLast(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public ExpressionWithTimeInclLastContext expressionWithTimeInclLast() { + ExpressionWithTimeInclLastContext _localctx = new ExpressionWithTimeInclLastContext(Context, State); + EnterRule(_localctx, 428, RULE_expressionWithTimeInclLast); + try { + State = 2873; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,393,Context) ) { + case 1: + EnterOuterAlt(_localctx, 1); + { + State = 2871; lastOperand(); + } + break; + case 2: + EnterOuterAlt(_localctx, 2); + { + State = 2872; expressionWithTime(); + } + break; + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class ExpressionQualifyableContext : ParserRuleContext { + public IToken a; + public IToken d; + public IToken s; + public ExpressionContext expression() { + return GetRuleContext(0); + } + public ITerminalNode ASC() { return GetToken(EsperEPL2GrammarParser.ASC, 0); } + public ITerminalNode DESC() { return GetToken(EsperEPL2GrammarParser.DESC, 0); } + public ITerminalNode TIMEPERIOD_SECONDS() { return GetToken(EsperEPL2GrammarParser.TIMEPERIOD_SECONDS, 0); } + public ITerminalNode TIMEPERIOD_SECOND() { return GetToken(EsperEPL2GrammarParser.TIMEPERIOD_SECOND, 0); } + public ITerminalNode TIMEPERIOD_SEC() { return GetToken(EsperEPL2GrammarParser.TIMEPERIOD_SEC, 0); } + public ExpressionQualifyableContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_expressionQualifyable; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterExpressionQualifyable(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitExpressionQualifyable(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitExpressionQualifyable(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public ExpressionQualifyableContext expressionQualifyable() { + ExpressionQualifyableContext _localctx = new ExpressionQualifyableContext(Context, State); + EnterRule(_localctx, 430, RULE_expressionQualifyable); + try { + EnterOuterAlt(_localctx, 1); + { + State = 2875; expression(); + State = 2881; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case ASC: + { + State = 2876; _localctx.a = Match(ASC); + } + break; + case DESC: + { + State = 2877; _localctx.d = Match(DESC); + } + break; + case TIMEPERIOD_SECONDS: + { + State = 2878; _localctx.s = Match(TIMEPERIOD_SECONDS); + } + break; + case TIMEPERIOD_SECOND: + { + State = 2879; _localctx.s = Match(TIMEPERIOD_SECOND); + } + break; + case TIMEPERIOD_SEC: + { + State = 2880; _localctx.s = Match(TIMEPERIOD_SEC); + } + break; + case RPAREN: + case COMMA: + break; + default: + throw new NoViableAltException(this); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class LastWeekdayOperandContext : ParserRuleContext { + public ITerminalNode LW() { return GetToken(EsperEPL2GrammarParser.LW, 0); } + public LastWeekdayOperandContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_lastWeekdayOperand; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterLastWeekdayOperand(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitLastWeekdayOperand(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitLastWeekdayOperand(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public LastWeekdayOperandContext lastWeekdayOperand() { + LastWeekdayOperandContext _localctx = new LastWeekdayOperandContext(Context, State); + EnterRule(_localctx, 432, RULE_lastWeekdayOperand); + try { + EnterOuterAlt(_localctx, 1); + { + State = 2883; Match(LW); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class LastOperandContext : ParserRuleContext { + public ITerminalNode LAST() { return GetToken(EsperEPL2GrammarParser.LAST, 0); } + public LastOperandContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_lastOperand; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterLastOperand(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitLastOperand(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitLastOperand(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public LastOperandContext lastOperand() { + LastOperandContext _localctx = new LastOperandContext(Context, State); + EnterRule(_localctx, 434, RULE_lastOperand); + try { + EnterOuterAlt(_localctx, 1); + { + State = 2885; Match(LAST); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class FrequencyOperandContext : ParserRuleContext { + public IToken i; + public ITerminalNode STAR() { return GetToken(EsperEPL2GrammarParser.STAR, 0); } + public ITerminalNode DIV() { return GetToken(EsperEPL2GrammarParser.DIV, 0); } + public NumberContext number() { + return GetRuleContext(0); + } + public SubstitutionContext substitution() { + return GetRuleContext(0); + } + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public FrequencyOperandContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_frequencyOperand; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterFrequencyOperand(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitFrequencyOperand(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitFrequencyOperand(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public FrequencyOperandContext frequencyOperand() { + FrequencyOperandContext _localctx = new FrequencyOperandContext(Context, State); + EnterRule(_localctx, 436, RULE_frequencyOperand); + try { + EnterOuterAlt(_localctx, 1); + { + State = 2887; Match(STAR); + State = 2888; Match(DIV); + State = 2892; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case IntegerLiteral: + case FloatingPointLiteral: + { + State = 2889; number(); + } + break; + case IDENT: + { + State = 2890; _localctx.i = Match(IDENT); + } + break; + case QUESTION: + { + State = 2891; substitution(); + } + break; + default: + throw new NoViableAltException(this); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class RangeOperandContext : ParserRuleContext { + public NumberContext n1; + public IToken i1; + public SubstitutionContext s1; + public NumberContext n2; + public IToken i2; + public SubstitutionContext s2; + public ITerminalNode COLON() { return GetToken(EsperEPL2GrammarParser.COLON, 0); } + public NumberContext[] number() { + return GetRuleContexts(); + } + public NumberContext number(int i) { + return GetRuleContext(i); + } + public ITerminalNode[] IDENT() { return GetTokens(EsperEPL2GrammarParser.IDENT); } + public ITerminalNode IDENT(int i) { + return GetToken(EsperEPL2GrammarParser.IDENT, i); + } + public SubstitutionContext[] substitution() { + return GetRuleContexts(); + } + public SubstitutionContext substitution(int i) { + return GetRuleContext(i); + } + public RangeOperandContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_rangeOperand; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterRangeOperand(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitRangeOperand(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitRangeOperand(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public RangeOperandContext rangeOperand() { + RangeOperandContext _localctx = new RangeOperandContext(Context, State); + EnterRule(_localctx, 438, RULE_rangeOperand); + try { + EnterOuterAlt(_localctx, 1); + { + State = 2897; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case IntegerLiteral: + case FloatingPointLiteral: + { + State = 2894; _localctx.n1 = number(); + } + break; + case IDENT: + { + State = 2895; _localctx.i1 = Match(IDENT); + } + break; + case QUESTION: + { + State = 2896; _localctx.s1 = substitution(); + } + break; + default: + throw new NoViableAltException(this); + } + State = 2899; Match(COLON); + State = 2903; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case IntegerLiteral: + case FloatingPointLiteral: + { + State = 2900; _localctx.n2 = number(); + } + break; + case IDENT: + { + State = 2901; _localctx.i2 = Match(IDENT); + } + break; + case QUESTION: + { + State = 2902; _localctx.s2 = substitution(); + } + break; + default: + throw new NoViableAltException(this); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class LastOperatorContext : ParserRuleContext { + public IToken i; + public ITerminalNode LAST() { return GetToken(EsperEPL2GrammarParser.LAST, 0); } + public NumberContext number() { + return GetRuleContext(0); + } + public SubstitutionContext substitution() { + return GetRuleContext(0); + } + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public LastOperatorContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_lastOperator; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterLastOperator(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitLastOperator(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitLastOperator(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public LastOperatorContext lastOperator() { + LastOperatorContext _localctx = new LastOperatorContext(Context, State); + EnterRule(_localctx, 440, RULE_lastOperator); + try { + EnterOuterAlt(_localctx, 1); + { + State = 2908; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case IntegerLiteral: + case FloatingPointLiteral: + { + State = 2905; number(); + } + break; + case IDENT: + { + State = 2906; _localctx.i = Match(IDENT); + } + break; + case QUESTION: + { + State = 2907; substitution(); + } + break; + default: + throw new NoViableAltException(this); + } + State = 2910; Match(LAST); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class WeekDayOperatorContext : ParserRuleContext { + public IToken i; + public ITerminalNode WEEKDAY() { return GetToken(EsperEPL2GrammarParser.WEEKDAY, 0); } + public NumberContext number() { + return GetRuleContext(0); + } + public SubstitutionContext substitution() { + return GetRuleContext(0); + } + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public WeekDayOperatorContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_weekDayOperator; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterWeekDayOperator(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitWeekDayOperator(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitWeekDayOperator(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public WeekDayOperatorContext weekDayOperator() { + WeekDayOperatorContext _localctx = new WeekDayOperatorContext(Context, State); + EnterRule(_localctx, 442, RULE_weekDayOperator); + try { + EnterOuterAlt(_localctx, 1); + { + State = 2915; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case IntegerLiteral: + case FloatingPointLiteral: + { + State = 2912; number(); + } + break; + case IDENT: + { + State = 2913; _localctx.i = Match(IDENT); + } + break; + case QUESTION: + { + State = 2914; substitution(); + } + break; + default: + throw new NoViableAltException(this); + } + State = 2917; Match(WEEKDAY); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class NumericParameterListContext : ParserRuleContext { + public ITerminalNode LBRACK() { return GetToken(EsperEPL2GrammarParser.LBRACK, 0); } + public NumericListParameterContext[] numericListParameter() { + return GetRuleContexts(); + } + public NumericListParameterContext numericListParameter(int i) { + return GetRuleContext(i); + } + public ITerminalNode RBRACK() { return GetToken(EsperEPL2GrammarParser.RBRACK, 0); } + public ITerminalNode[] COMMA() { return GetTokens(EsperEPL2GrammarParser.COMMA); } + public ITerminalNode COMMA(int i) { + return GetToken(EsperEPL2GrammarParser.COMMA, i); + } + public NumericParameterListContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_numericParameterList; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterNumericParameterList(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitNumericParameterList(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitNumericParameterList(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public NumericParameterListContext numericParameterList() { + NumericParameterListContext _localctx = new NumericParameterListContext(Context, State); + EnterRule(_localctx, 444, RULE_numericParameterList); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 2919; Match(LBRACK); + State = 2920; numericListParameter(); + State = 2925; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 2921; Match(COMMA); + State = 2922; numericListParameter(); + } + } + State = 2927; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + State = 2928; Match(RBRACK); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class NumericListParameterContext : ParserRuleContext { + public RangeOperandContext rangeOperand() { + return GetRuleContext(0); + } + public FrequencyOperandContext frequencyOperand() { + return GetRuleContext(0); + } + public NumberconstantContext numberconstant() { + return GetRuleContext(0); + } + public NumericListParameterContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_numericListParameter; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterNumericListParameter(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitNumericListParameter(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitNumericListParameter(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public NumericListParameterContext numericListParameter() { + NumericListParameterContext _localctx = new NumericListParameterContext(Context, State); + EnterRule(_localctx, 446, RULE_numericListParameter); + try { + State = 2933; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,401,Context) ) { + case 1: + EnterOuterAlt(_localctx, 1); + { + State = 2930; rangeOperand(); + } + break; + case 2: + EnterOuterAlt(_localctx, 2); + { + State = 2931; frequencyOperand(); + } + break; + case 3: + EnterOuterAlt(_localctx, 3); + { + State = 2932; numberconstant(); + } + break; + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class EventPropertyContext : ParserRuleContext { + public EventPropertyAtomicContext[] eventPropertyAtomic() { + return GetRuleContexts(); + } + public EventPropertyAtomicContext eventPropertyAtomic(int i) { + return GetRuleContext(i); + } + public ITerminalNode[] DOT() { return GetTokens(EsperEPL2GrammarParser.DOT); } + public ITerminalNode DOT(int i) { + return GetToken(EsperEPL2GrammarParser.DOT, i); + } + public EventPropertyContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_eventProperty; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterEventProperty(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitEventProperty(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitEventProperty(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public EventPropertyContext eventProperty() { + EventPropertyContext _localctx = new EventPropertyContext(Context, State); + EnterRule(_localctx, 448, RULE_eventProperty); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 2935; eventPropertyAtomic(); + State = 2940; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==DOT) { + { + { + State = 2936; Match(DOT); + State = 2937; eventPropertyAtomic(); + } + } + State = 2942; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class EventPropertyAtomicContext : ParserRuleContext { + public IToken lb; + public NumberContext ni; + public IToken q; + public IToken lp; + public IToken s; + public IToken q1; + public EventPropertyIdentContext eventPropertyIdent() { + return GetRuleContext(0); + } + public ITerminalNode RBRACK() { return GetToken(EsperEPL2GrammarParser.RBRACK, 0); } + public ITerminalNode RPAREN() { return GetToken(EsperEPL2GrammarParser.RPAREN, 0); } + public ITerminalNode LBRACK() { return GetToken(EsperEPL2GrammarParser.LBRACK, 0); } + public NumberContext number() { + return GetRuleContext(0); + } + public ITerminalNode LPAREN() { return GetToken(EsperEPL2GrammarParser.LPAREN, 0); } + public ITerminalNode QUESTION() { return GetToken(EsperEPL2GrammarParser.QUESTION, 0); } + public ITerminalNode STRING_LITERAL() { return GetToken(EsperEPL2GrammarParser.STRING_LITERAL, 0); } + public ITerminalNode QUOTED_STRING_LITERAL() { return GetToken(EsperEPL2GrammarParser.QUOTED_STRING_LITERAL, 0); } + public EventPropertyAtomicContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_eventPropertyAtomic; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterEventPropertyAtomic(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitEventPropertyAtomic(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitEventPropertyAtomic(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public EventPropertyAtomicContext eventPropertyAtomic() { + EventPropertyAtomicContext _localctx = new EventPropertyAtomicContext(Context, State); + EnterRule(_localctx, 450, RULE_eventPropertyAtomic); + try { + EnterOuterAlt(_localctx, 1); + { + State = 2943; eventPropertyIdent(); + State = 2960; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,406,Context) ) { + case 1: + { + State = 2944; _localctx.lb = Match(LBRACK); + State = 2945; _localctx.ni = number(); + State = 2946; Match(RBRACK); + State = 2948; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,403,Context) ) { + case 1: + { + State = 2947; _localctx.q = Match(QUESTION); + } + break; + } + } + break; + case 2: + { + State = 2950; _localctx.lp = Match(LPAREN); + State = 2953; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case STRING_LITERAL: + { + State = 2951; _localctx.s = Match(STRING_LITERAL); + } + break; + case QUOTED_STRING_LITERAL: + { + State = 2952; _localctx.s = Match(QUOTED_STRING_LITERAL); + } + break; + default: + throw new NoViableAltException(this); + } + State = 2955; Match(RPAREN); + State = 2957; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,405,Context) ) { + case 1: + { + State = 2956; _localctx.q = Match(QUESTION); + } + break; + } + } + break; + case 3: + { + State = 2959; _localctx.q1 = Match(QUESTION); + } + break; + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class EventPropertyIdentContext : ParserRuleContext { + public KeywordAllowedIdentContext ipi; + public KeywordAllowedIdentContext ipi2; + public KeywordAllowedIdentContext[] keywordAllowedIdent() { + return GetRuleContexts(); + } + public KeywordAllowedIdentContext keywordAllowedIdent(int i) { + return GetRuleContext(i); + } + public ITerminalNode[] ESCAPECHAR() { return GetTokens(EsperEPL2GrammarParser.ESCAPECHAR); } + public ITerminalNode ESCAPECHAR(int i) { + return GetToken(EsperEPL2GrammarParser.ESCAPECHAR, i); + } + public ITerminalNode[] DOT() { return GetTokens(EsperEPL2GrammarParser.DOT); } + public ITerminalNode DOT(int i) { + return GetToken(EsperEPL2GrammarParser.DOT, i); + } + public EventPropertyIdentContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_eventPropertyIdent; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterEventPropertyIdent(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitEventPropertyIdent(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitEventPropertyIdent(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public EventPropertyIdentContext eventPropertyIdent() { + EventPropertyIdentContext _localctx = new EventPropertyIdentContext(Context, State); + EnterRule(_localctx, 452, RULE_eventPropertyIdent); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 2962; _localctx.ipi = keywordAllowedIdent(); + State = 2970; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==ESCAPECHAR) { + { + { + State = 2963; Match(ESCAPECHAR); + State = 2964; Match(DOT); + State = 2966; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,407,Context) ) { + case 1: + { + State = 2965; _localctx.ipi2 = keywordAllowedIdent(); + } + break; + } + } + } + State = 2972; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class KeywordAllowedIdentContext : ParserRuleContext { + public IToken i1; + public IToken i2; + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public ITerminalNode TICKED_STRING_LITERAL() { return GetToken(EsperEPL2GrammarParser.TICKED_STRING_LITERAL, 0); } + public ITerminalNode AT() { return GetToken(EsperEPL2GrammarParser.AT, 0); } + public ITerminalNode COUNT() { return GetToken(EsperEPL2GrammarParser.COUNT, 0); } + public ITerminalNode ESCAPE() { return GetToken(EsperEPL2GrammarParser.ESCAPE, 0); } + public ITerminalNode EVERY_EXPR() { return GetToken(EsperEPL2GrammarParser.EVERY_EXPR, 0); } + public ITerminalNode SCHEMA() { return GetToken(EsperEPL2GrammarParser.SCHEMA, 0); } + public ITerminalNode SUM() { return GetToken(EsperEPL2GrammarParser.SUM, 0); } + public ITerminalNode AVG() { return GetToken(EsperEPL2GrammarParser.AVG, 0); } + public ITerminalNode MAX() { return GetToken(EsperEPL2GrammarParser.MAX, 0); } + public ITerminalNode MIN() { return GetToken(EsperEPL2GrammarParser.MIN, 0); } + public ITerminalNode COALESCE() { return GetToken(EsperEPL2GrammarParser.COALESCE, 0); } + public ITerminalNode MEDIAN() { return GetToken(EsperEPL2GrammarParser.MEDIAN, 0); } + public ITerminalNode STDDEV() { return GetToken(EsperEPL2GrammarParser.STDDEV, 0); } + public ITerminalNode AVEDEV() { return GetToken(EsperEPL2GrammarParser.AVEDEV, 0); } + public ITerminalNode EVENTS() { return GetToken(EsperEPL2GrammarParser.EVENTS, 0); } + public ITerminalNode FIRST() { return GetToken(EsperEPL2GrammarParser.FIRST, 0); } + public ITerminalNode LAST() { return GetToken(EsperEPL2GrammarParser.LAST, 0); } + public ITerminalNode WHILE() { return GetToken(EsperEPL2GrammarParser.WHILE, 0); } + public ITerminalNode MERGE() { return GetToken(EsperEPL2GrammarParser.MERGE, 0); } + public ITerminalNode MATCHED() { return GetToken(EsperEPL2GrammarParser.MATCHED, 0); } + public ITerminalNode UNIDIRECTIONAL() { return GetToken(EsperEPL2GrammarParser.UNIDIRECTIONAL, 0); } + public ITerminalNode RETAINUNION() { return GetToken(EsperEPL2GrammarParser.RETAINUNION, 0); } + public ITerminalNode RETAININTERSECTION() { return GetToken(EsperEPL2GrammarParser.RETAININTERSECTION, 0); } + public ITerminalNode UNTIL() { return GetToken(EsperEPL2GrammarParser.UNTIL, 0); } + public ITerminalNode PATTERN() { return GetToken(EsperEPL2GrammarParser.PATTERN, 0); } + public ITerminalNode SQL() { return GetToken(EsperEPL2GrammarParser.SQL, 0); } + public ITerminalNode METADATASQL() { return GetToken(EsperEPL2GrammarParser.METADATASQL, 0); } + public ITerminalNode PREVIOUS() { return GetToken(EsperEPL2GrammarParser.PREVIOUS, 0); } + public ITerminalNode PREVIOUSTAIL() { return GetToken(EsperEPL2GrammarParser.PREVIOUSTAIL, 0); } + public ITerminalNode PRIOR() { return GetToken(EsperEPL2GrammarParser.PRIOR, 0); } + public ITerminalNode WEEKDAY() { return GetToken(EsperEPL2GrammarParser.WEEKDAY, 0); } + public ITerminalNode LW() { return GetToken(EsperEPL2GrammarParser.LW, 0); } + public ITerminalNode INSTANCEOF() { return GetToken(EsperEPL2GrammarParser.INSTANCEOF, 0); } + public ITerminalNode TYPEOF() { return GetToken(EsperEPL2GrammarParser.TYPEOF, 0); } + public ITerminalNode CAST() { return GetToken(EsperEPL2GrammarParser.CAST, 0); } + public ITerminalNode SNAPSHOT() { return GetToken(EsperEPL2GrammarParser.SNAPSHOT, 0); } + public ITerminalNode VARIABLE() { return GetToken(EsperEPL2GrammarParser.VARIABLE, 0); } + public ITerminalNode TABLE() { return GetToken(EsperEPL2GrammarParser.TABLE, 0); } + public ITerminalNode INDEX() { return GetToken(EsperEPL2GrammarParser.INDEX, 0); } + public ITerminalNode WINDOW() { return GetToken(EsperEPL2GrammarParser.WINDOW, 0); } + public ITerminalNode LEFT() { return GetToken(EsperEPL2GrammarParser.LEFT, 0); } + public ITerminalNode RIGHT() { return GetToken(EsperEPL2GrammarParser.RIGHT, 0); } + public ITerminalNode OUTER() { return GetToken(EsperEPL2GrammarParser.OUTER, 0); } + public ITerminalNode FULL() { return GetToken(EsperEPL2GrammarParser.FULL, 0); } + public ITerminalNode JOIN() { return GetToken(EsperEPL2GrammarParser.JOIN, 0); } + public ITerminalNode DEFINE() { return GetToken(EsperEPL2GrammarParser.DEFINE, 0); } + public ITerminalNode PARTITION() { return GetToken(EsperEPL2GrammarParser.PARTITION, 0); } + public ITerminalNode MATCHES() { return GetToken(EsperEPL2GrammarParser.MATCHES, 0); } + public ITerminalNode CONTEXT() { return GetToken(EsperEPL2GrammarParser.CONTEXT, 0); } + public ITerminalNode FOR() { return GetToken(EsperEPL2GrammarParser.FOR, 0); } + public ITerminalNode USING() { return GetToken(EsperEPL2GrammarParser.USING, 0); } + public KeywordAllowedIdentContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_keywordAllowedIdent; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterKeywordAllowedIdent(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitKeywordAllowedIdent(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitKeywordAllowedIdent(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public KeywordAllowedIdentContext keywordAllowedIdent() { + KeywordAllowedIdentContext _localctx = new KeywordAllowedIdentContext(Context, State); + EnterRule(_localctx, 454, RULE_keywordAllowedIdent); + try { + State = 3025; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case IDENT: + EnterOuterAlt(_localctx, 1); + { + State = 2973; _localctx.i1 = Match(IDENT); + } + break; + case TICKED_STRING_LITERAL: + EnterOuterAlt(_localctx, 2); + { + State = 2974; _localctx.i2 = Match(TICKED_STRING_LITERAL); + } + break; + case AT: + EnterOuterAlt(_localctx, 3); + { + State = 2975; Match(AT); + } + break; + case COUNT: + EnterOuterAlt(_localctx, 4); + { + State = 2976; Match(COUNT); + } + break; + case ESCAPE: + EnterOuterAlt(_localctx, 5); + { + State = 2977; Match(ESCAPE); + } + break; + case EVERY_EXPR: + EnterOuterAlt(_localctx, 6); + { + State = 2978; Match(EVERY_EXPR); + } + break; + case SCHEMA: + EnterOuterAlt(_localctx, 7); + { + State = 2979; Match(SCHEMA); + } + break; + case SUM: + EnterOuterAlt(_localctx, 8); + { + State = 2980; Match(SUM); + } + break; + case AVG: + EnterOuterAlt(_localctx, 9); + { + State = 2981; Match(AVG); + } + break; + case MAX: + EnterOuterAlt(_localctx, 10); + { + State = 2982; Match(MAX); + } + break; + case MIN: + EnterOuterAlt(_localctx, 11); + { + State = 2983; Match(MIN); + } + break; + case COALESCE: + EnterOuterAlt(_localctx, 12); + { + State = 2984; Match(COALESCE); + } + break; + case MEDIAN: + EnterOuterAlt(_localctx, 13); + { + State = 2985; Match(MEDIAN); + } + break; + case STDDEV: + EnterOuterAlt(_localctx, 14); + { + State = 2986; Match(STDDEV); + } + break; + case AVEDEV: + EnterOuterAlt(_localctx, 15); + { + State = 2987; Match(AVEDEV); + } + break; + case EVENTS: + EnterOuterAlt(_localctx, 16); + { + State = 2988; Match(EVENTS); + } + break; + case FIRST: + EnterOuterAlt(_localctx, 17); + { + State = 2989; Match(FIRST); + } + break; + case LAST: + EnterOuterAlt(_localctx, 18); + { + State = 2990; Match(LAST); + } + break; + case WHILE: + EnterOuterAlt(_localctx, 19); + { + State = 2991; Match(WHILE); + } + break; + case MERGE: + EnterOuterAlt(_localctx, 20); + { + State = 2992; Match(MERGE); + } + break; + case MATCHED: + EnterOuterAlt(_localctx, 21); + { + State = 2993; Match(MATCHED); + } + break; + case UNIDIRECTIONAL: + EnterOuterAlt(_localctx, 22); + { + State = 2994; Match(UNIDIRECTIONAL); + } + break; + case RETAINUNION: + EnterOuterAlt(_localctx, 23); + { + State = 2995; Match(RETAINUNION); + } + break; + case RETAININTERSECTION: + EnterOuterAlt(_localctx, 24); + { + State = 2996; Match(RETAININTERSECTION); + } + break; + case UNTIL: + EnterOuterAlt(_localctx, 25); + { + State = 2997; Match(UNTIL); + } + break; + case PATTERN: + EnterOuterAlt(_localctx, 26); + { + State = 2998; Match(PATTERN); + } + break; + case SQL: + EnterOuterAlt(_localctx, 27); + { + State = 2999; Match(SQL); + } + break; + case METADATASQL: + EnterOuterAlt(_localctx, 28); + { + State = 3000; Match(METADATASQL); + } + break; + case PREVIOUS: + EnterOuterAlt(_localctx, 29); + { + State = 3001; Match(PREVIOUS); + } + break; + case PREVIOUSTAIL: + EnterOuterAlt(_localctx, 30); + { + State = 3002; Match(PREVIOUSTAIL); + } + break; + case PRIOR: + EnterOuterAlt(_localctx, 31); + { + State = 3003; Match(PRIOR); + } + break; + case WEEKDAY: + EnterOuterAlt(_localctx, 32); + { + State = 3004; Match(WEEKDAY); + } + break; + case LW: + EnterOuterAlt(_localctx, 33); + { + State = 3005; Match(LW); + } + break; + case INSTANCEOF: + EnterOuterAlt(_localctx, 34); + { + State = 3006; Match(INSTANCEOF); + } + break; + case TYPEOF: + EnterOuterAlt(_localctx, 35); + { + State = 3007; Match(TYPEOF); + } + break; + case CAST: + EnterOuterAlt(_localctx, 36); + { + State = 3008; Match(CAST); + } + break; + case SNAPSHOT: + EnterOuterAlt(_localctx, 37); + { + State = 3009; Match(SNAPSHOT); + } + break; + case VARIABLE: + EnterOuterAlt(_localctx, 38); + { + State = 3010; Match(VARIABLE); + } + break; + case TABLE: + EnterOuterAlt(_localctx, 39); + { + State = 3011; Match(TABLE); + } + break; + case INDEX: + EnterOuterAlt(_localctx, 40); + { + State = 3012; Match(INDEX); + } + break; + case WINDOW: + EnterOuterAlt(_localctx, 41); + { + State = 3013; Match(WINDOW); + } + break; + case LEFT: + EnterOuterAlt(_localctx, 42); + { + State = 3014; Match(LEFT); + } + break; + case RIGHT: + EnterOuterAlt(_localctx, 43); + { + State = 3015; Match(RIGHT); + } + break; + case OUTER: + EnterOuterAlt(_localctx, 44); + { + State = 3016; Match(OUTER); + } + break; + case FULL: + EnterOuterAlt(_localctx, 45); + { + State = 3017; Match(FULL); + } + break; + case JOIN: + EnterOuterAlt(_localctx, 46); + { + State = 3018; Match(JOIN); + } + break; + case DEFINE: + EnterOuterAlt(_localctx, 47); + { + State = 3019; Match(DEFINE); + } + break; + case PARTITION: + EnterOuterAlt(_localctx, 48); + { + State = 3020; Match(PARTITION); + } + break; + case MATCHES: + EnterOuterAlt(_localctx, 49); + { + State = 3021; Match(MATCHES); + } + break; + case CONTEXT: + EnterOuterAlt(_localctx, 50); + { + State = 3022; Match(CONTEXT); + } + break; + case FOR: + EnterOuterAlt(_localctx, 51); + { + State = 3023; Match(FOR); + } + break; + case USING: + EnterOuterAlt(_localctx, 52); + { + State = 3024; Match(USING); + } + break; + default: + throw new NoViableAltException(this); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class EscapableStrContext : ParserRuleContext { + public IToken i1; + public IToken i2; + public IToken i3; + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public ITerminalNode EVENTS() { return GetToken(EsperEPL2GrammarParser.EVENTS, 0); } + public ITerminalNode TICKED_STRING_LITERAL() { return GetToken(EsperEPL2GrammarParser.TICKED_STRING_LITERAL, 0); } + public EscapableStrContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_escapableStr; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterEscapableStr(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitEscapableStr(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitEscapableStr(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public EscapableStrContext escapableStr() { + EscapableStrContext _localctx = new EscapableStrContext(Context, State); + EnterRule(_localctx, 456, RULE_escapableStr); + try { + State = 3030; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case IDENT: + EnterOuterAlt(_localctx, 1); + { + State = 3027; _localctx.i1 = Match(IDENT); + } + break; + case EVENTS: + EnterOuterAlt(_localctx, 2); + { + State = 3028; _localctx.i2 = Match(EVENTS); + } + break; + case TICKED_STRING_LITERAL: + EnterOuterAlt(_localctx, 3); + { + State = 3029; _localctx.i3 = Match(TICKED_STRING_LITERAL); + } + break; + default: + throw new NoViableAltException(this); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class EscapableIdentContext : ParserRuleContext { + public IToken t; + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public ITerminalNode TICKED_STRING_LITERAL() { return GetToken(EsperEPL2GrammarParser.TICKED_STRING_LITERAL, 0); } + public EscapableIdentContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_escapableIdent; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterEscapableIdent(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitEscapableIdent(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitEscapableIdent(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public EscapableIdentContext escapableIdent() { + EscapableIdentContext _localctx = new EscapableIdentContext(Context, State); + EnterRule(_localctx, 458, RULE_escapableIdent); + try { + State = 3034; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case IDENT: + EnterOuterAlt(_localctx, 1); + { + State = 3032; Match(IDENT); + } + break; + case TICKED_STRING_LITERAL: + EnterOuterAlt(_localctx, 2); + { + State = 3033; _localctx.t = Match(TICKED_STRING_LITERAL); + } + break; + default: + throw new NoViableAltException(this); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class TimePeriodContext : ParserRuleContext { + public YearPartContext yearPart() { + return GetRuleContext(0); + } + public MonthPartContext monthPart() { + return GetRuleContext(0); + } + public WeekPartContext weekPart() { + return GetRuleContext(0); + } + public DayPartContext dayPart() { + return GetRuleContext(0); + } + public HourPartContext hourPart() { + return GetRuleContext(0); + } + public MinutePartContext minutePart() { + return GetRuleContext(0); + } + public SecondPartContext secondPart() { + return GetRuleContext(0); + } + public MillisecondPartContext millisecondPart() { + return GetRuleContext(0); + } + public MicrosecondPartContext microsecondPart() { + return GetRuleContext(0); + } + public TimePeriodContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_timePeriod; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterTimePeriod(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitTimePeriod(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitTimePeriod(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public TimePeriodContext timePeriod() { + TimePeriodContext _localctx = new TimePeriodContext(Context, State); + EnterRule(_localctx, 460, RULE_timePeriod); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 3153; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,448,Context) ) { + case 1: + { + State = 3036; yearPart(); + State = 3038; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,412,Context) ) { + case 1: + { + State = 3037; monthPart(); + } + break; + } + State = 3041; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,413,Context) ) { + case 1: + { + State = 3040; weekPart(); + } + break; + } + State = 3044; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,414,Context) ) { + case 1: + { + State = 3043; dayPart(); + } + break; + } + State = 3047; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,415,Context) ) { + case 1: + { + State = 3046; hourPart(); + } + break; + } + State = 3050; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,416,Context) ) { + case 1: + { + State = 3049; minutePart(); + } + break; + } + State = 3053; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,417,Context) ) { + case 1: + { + State = 3052; secondPart(); + } + break; + } + State = 3056; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,418,Context) ) { + case 1: + { + State = 3055; millisecondPart(); + } + break; + } + State = 3059; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (((((_la - 143)) & ~0x3f) == 0 && ((1L << (_la - 143)) & ((1L << (QUESTION - 143)) | (1L << (PLUS - 143)) | (1L << (MINUS - 143)) | (1L << (IDENT - 143)) | (1L << (IntegerLiteral - 143)) | (1L << (FloatingPointLiteral - 143)))) != 0)) { + { + State = 3058; microsecondPart(); + } + } + + } + break; + case 2: + { + State = 3061; monthPart(); + State = 3063; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,420,Context) ) { + case 1: + { + State = 3062; weekPart(); + } + break; + } + State = 3066; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,421,Context) ) { + case 1: + { + State = 3065; dayPart(); + } + break; + } + State = 3069; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,422,Context) ) { + case 1: + { + State = 3068; hourPart(); + } + break; + } + State = 3072; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,423,Context) ) { + case 1: + { + State = 3071; minutePart(); + } + break; + } + State = 3075; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,424,Context) ) { + case 1: + { + State = 3074; secondPart(); + } + break; + } + State = 3078; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,425,Context) ) { + case 1: + { + State = 3077; millisecondPart(); + } + break; + } + State = 3081; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (((((_la - 143)) & ~0x3f) == 0 && ((1L << (_la - 143)) & ((1L << (QUESTION - 143)) | (1L << (PLUS - 143)) | (1L << (MINUS - 143)) | (1L << (IDENT - 143)) | (1L << (IntegerLiteral - 143)) | (1L << (FloatingPointLiteral - 143)))) != 0)) { + { + State = 3080; microsecondPart(); + } + } + + } + break; + case 3: + { + State = 3083; weekPart(); + State = 3085; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,427,Context) ) { + case 1: + { + State = 3084; dayPart(); + } + break; + } + State = 3088; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,428,Context) ) { + case 1: + { + State = 3087; hourPart(); + } + break; + } + State = 3091; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,429,Context) ) { + case 1: + { + State = 3090; minutePart(); + } + break; + } + State = 3094; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,430,Context) ) { + case 1: + { + State = 3093; secondPart(); + } + break; + } + State = 3097; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,431,Context) ) { + case 1: + { + State = 3096; millisecondPart(); + } + break; + } + State = 3100; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (((((_la - 143)) & ~0x3f) == 0 && ((1L << (_la - 143)) & ((1L << (QUESTION - 143)) | (1L << (PLUS - 143)) | (1L << (MINUS - 143)) | (1L << (IDENT - 143)) | (1L << (IntegerLiteral - 143)) | (1L << (FloatingPointLiteral - 143)))) != 0)) { + { + State = 3099; microsecondPart(); + } + } + + } + break; + case 4: + { + State = 3102; dayPart(); + State = 3104; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,433,Context) ) { + case 1: + { + State = 3103; hourPart(); + } + break; + } + State = 3107; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,434,Context) ) { + case 1: + { + State = 3106; minutePart(); + } + break; + } + State = 3110; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,435,Context) ) { + case 1: + { + State = 3109; secondPart(); + } + break; + } + State = 3113; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,436,Context) ) { + case 1: + { + State = 3112; millisecondPart(); + } + break; + } + State = 3116; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (((((_la - 143)) & ~0x3f) == 0 && ((1L << (_la - 143)) & ((1L << (QUESTION - 143)) | (1L << (PLUS - 143)) | (1L << (MINUS - 143)) | (1L << (IDENT - 143)) | (1L << (IntegerLiteral - 143)) | (1L << (FloatingPointLiteral - 143)))) != 0)) { + { + State = 3115; microsecondPart(); + } + } + + } + break; + case 5: + { + State = 3118; hourPart(); + State = 3120; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,438,Context) ) { + case 1: + { + State = 3119; minutePart(); + } + break; + } + State = 3123; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,439,Context) ) { + case 1: + { + State = 3122; secondPart(); + } + break; + } + State = 3126; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,440,Context) ) { + case 1: + { + State = 3125; millisecondPart(); + } + break; + } + State = 3129; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (((((_la - 143)) & ~0x3f) == 0 && ((1L << (_la - 143)) & ((1L << (QUESTION - 143)) | (1L << (PLUS - 143)) | (1L << (MINUS - 143)) | (1L << (IDENT - 143)) | (1L << (IntegerLiteral - 143)) | (1L << (FloatingPointLiteral - 143)))) != 0)) { + { + State = 3128; microsecondPart(); + } + } + + } + break; + case 6: + { + State = 3131; minutePart(); + State = 3133; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,442,Context) ) { + case 1: + { + State = 3132; secondPart(); + } + break; + } + State = 3136; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,443,Context) ) { + case 1: + { + State = 3135; millisecondPart(); + } + break; + } + State = 3139; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (((((_la - 143)) & ~0x3f) == 0 && ((1L << (_la - 143)) & ((1L << (QUESTION - 143)) | (1L << (PLUS - 143)) | (1L << (MINUS - 143)) | (1L << (IDENT - 143)) | (1L << (IntegerLiteral - 143)) | (1L << (FloatingPointLiteral - 143)))) != 0)) { + { + State = 3138; microsecondPart(); + } + } + + } + break; + case 7: + { + State = 3141; secondPart(); + State = 3143; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,445,Context) ) { + case 1: + { + State = 3142; millisecondPart(); + } + break; + } + State = 3146; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (((((_la - 143)) & ~0x3f) == 0 && ((1L << (_la - 143)) & ((1L << (QUESTION - 143)) | (1L << (PLUS - 143)) | (1L << (MINUS - 143)) | (1L << (IDENT - 143)) | (1L << (IntegerLiteral - 143)) | (1L << (FloatingPointLiteral - 143)))) != 0)) { + { + State = 3145; microsecondPart(); + } + } + + } + break; + case 8: + { + State = 3148; millisecondPart(); + State = 3150; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (((((_la - 143)) & ~0x3f) == 0 && ((1L << (_la - 143)) & ((1L << (QUESTION - 143)) | (1L << (PLUS - 143)) | (1L << (MINUS - 143)) | (1L << (IDENT - 143)) | (1L << (IntegerLiteral - 143)) | (1L << (FloatingPointLiteral - 143)))) != 0)) { + { + State = 3149; microsecondPart(); + } + } + + } + break; + case 9: + { + State = 3152; microsecondPart(); + } + break; + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class YearPartContext : ParserRuleContext { + public IToken i; + public ITerminalNode TIMEPERIOD_YEARS() { return GetToken(EsperEPL2GrammarParser.TIMEPERIOD_YEARS, 0); } + public ITerminalNode TIMEPERIOD_YEAR() { return GetToken(EsperEPL2GrammarParser.TIMEPERIOD_YEAR, 0); } + public NumberconstantContext numberconstant() { + return GetRuleContext(0); + } + public SubstitutionContext substitution() { + return GetRuleContext(0); + } + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public YearPartContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_yearPart; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterYearPart(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitYearPart(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitYearPart(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public YearPartContext yearPart() { + YearPartContext _localctx = new YearPartContext(Context, State); + EnterRule(_localctx, 462, RULE_yearPart); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 3158; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case PLUS: + case MINUS: + case IntegerLiteral: + case FloatingPointLiteral: + { + State = 3155; numberconstant(); + } + break; + case IDENT: + { + State = 3156; _localctx.i = Match(IDENT); + } + break; + case QUESTION: + { + State = 3157; substitution(); + } + break; + default: + throw new NoViableAltException(this); + } + State = 3160; + _la = TokenStream.LA(1); + if ( !(_la==TIMEPERIOD_YEAR || _la==TIMEPERIOD_YEARS) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class MonthPartContext : ParserRuleContext { + public IToken i; + public ITerminalNode TIMEPERIOD_MONTHS() { return GetToken(EsperEPL2GrammarParser.TIMEPERIOD_MONTHS, 0); } + public ITerminalNode TIMEPERIOD_MONTH() { return GetToken(EsperEPL2GrammarParser.TIMEPERIOD_MONTH, 0); } + public NumberconstantContext numberconstant() { + return GetRuleContext(0); + } + public SubstitutionContext substitution() { + return GetRuleContext(0); + } + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public MonthPartContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_monthPart; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterMonthPart(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitMonthPart(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitMonthPart(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public MonthPartContext monthPart() { + MonthPartContext _localctx = new MonthPartContext(Context, State); + EnterRule(_localctx, 464, RULE_monthPart); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 3165; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case PLUS: + case MINUS: + case IntegerLiteral: + case FloatingPointLiteral: + { + State = 3162; numberconstant(); + } + break; + case IDENT: + { + State = 3163; _localctx.i = Match(IDENT); + } + break; + case QUESTION: + { + State = 3164; substitution(); + } + break; + default: + throw new NoViableAltException(this); + } + State = 3167; + _la = TokenStream.LA(1); + if ( !(_la==TIMEPERIOD_MONTH || _la==TIMEPERIOD_MONTHS) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class WeekPartContext : ParserRuleContext { + public IToken i; + public ITerminalNode TIMEPERIOD_WEEKS() { return GetToken(EsperEPL2GrammarParser.TIMEPERIOD_WEEKS, 0); } + public ITerminalNode TIMEPERIOD_WEEK() { return GetToken(EsperEPL2GrammarParser.TIMEPERIOD_WEEK, 0); } + public NumberconstantContext numberconstant() { + return GetRuleContext(0); + } + public SubstitutionContext substitution() { + return GetRuleContext(0); + } + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public WeekPartContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_weekPart; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterWeekPart(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitWeekPart(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitWeekPart(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public WeekPartContext weekPart() { + WeekPartContext _localctx = new WeekPartContext(Context, State); + EnterRule(_localctx, 466, RULE_weekPart); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 3172; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case PLUS: + case MINUS: + case IntegerLiteral: + case FloatingPointLiteral: + { + State = 3169; numberconstant(); + } + break; + case IDENT: + { + State = 3170; _localctx.i = Match(IDENT); + } + break; + case QUESTION: + { + State = 3171; substitution(); + } + break; + default: + throw new NoViableAltException(this); + } + State = 3174; + _la = TokenStream.LA(1); + if ( !(_la==TIMEPERIOD_WEEK || _la==TIMEPERIOD_WEEKS) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class DayPartContext : ParserRuleContext { + public IToken i; + public ITerminalNode TIMEPERIOD_DAYS() { return GetToken(EsperEPL2GrammarParser.TIMEPERIOD_DAYS, 0); } + public ITerminalNode TIMEPERIOD_DAY() { return GetToken(EsperEPL2GrammarParser.TIMEPERIOD_DAY, 0); } + public NumberconstantContext numberconstant() { + return GetRuleContext(0); + } + public SubstitutionContext substitution() { + return GetRuleContext(0); + } + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public DayPartContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_dayPart; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterDayPart(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitDayPart(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitDayPart(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public DayPartContext dayPart() { + DayPartContext _localctx = new DayPartContext(Context, State); + EnterRule(_localctx, 468, RULE_dayPart); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 3179; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case PLUS: + case MINUS: + case IntegerLiteral: + case FloatingPointLiteral: + { + State = 3176; numberconstant(); + } + break; + case IDENT: + { + State = 3177; _localctx.i = Match(IDENT); + } + break; + case QUESTION: + { + State = 3178; substitution(); + } + break; + default: + throw new NoViableAltException(this); + } + State = 3181; + _la = TokenStream.LA(1); + if ( !(_la==TIMEPERIOD_DAY || _la==TIMEPERIOD_DAYS) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class HourPartContext : ParserRuleContext { + public IToken i; + public ITerminalNode TIMEPERIOD_HOURS() { return GetToken(EsperEPL2GrammarParser.TIMEPERIOD_HOURS, 0); } + public ITerminalNode TIMEPERIOD_HOUR() { return GetToken(EsperEPL2GrammarParser.TIMEPERIOD_HOUR, 0); } + public NumberconstantContext numberconstant() { + return GetRuleContext(0); + } + public SubstitutionContext substitution() { + return GetRuleContext(0); + } + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public HourPartContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_hourPart; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterHourPart(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitHourPart(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitHourPart(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public HourPartContext hourPart() { + HourPartContext _localctx = new HourPartContext(Context, State); + EnterRule(_localctx, 470, RULE_hourPart); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 3186; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case PLUS: + case MINUS: + case IntegerLiteral: + case FloatingPointLiteral: + { + State = 3183; numberconstant(); + } + break; + case IDENT: + { + State = 3184; _localctx.i = Match(IDENT); + } + break; + case QUESTION: + { + State = 3185; substitution(); + } + break; + default: + throw new NoViableAltException(this); + } + State = 3188; + _la = TokenStream.LA(1); + if ( !(_la==TIMEPERIOD_HOUR || _la==TIMEPERIOD_HOURS) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class MinutePartContext : ParserRuleContext { + public IToken i; + public ITerminalNode TIMEPERIOD_MINUTES() { return GetToken(EsperEPL2GrammarParser.TIMEPERIOD_MINUTES, 0); } + public ITerminalNode TIMEPERIOD_MINUTE() { return GetToken(EsperEPL2GrammarParser.TIMEPERIOD_MINUTE, 0); } + public ITerminalNode MIN() { return GetToken(EsperEPL2GrammarParser.MIN, 0); } + public NumberconstantContext numberconstant() { + return GetRuleContext(0); + } + public SubstitutionContext substitution() { + return GetRuleContext(0); + } + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public MinutePartContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_minutePart; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterMinutePart(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitMinutePart(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitMinutePart(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public MinutePartContext minutePart() { + MinutePartContext _localctx = new MinutePartContext(Context, State); + EnterRule(_localctx, 472, RULE_minutePart); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 3193; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case PLUS: + case MINUS: + case IntegerLiteral: + case FloatingPointLiteral: + { + State = 3190; numberconstant(); + } + break; + case IDENT: + { + State = 3191; _localctx.i = Match(IDENT); + } + break; + case QUESTION: + { + State = 3192; substitution(); + } + break; + default: + throw new NoViableAltException(this); + } + State = 3195; + _la = TokenStream.LA(1); + if ( !(_la==MIN || _la==TIMEPERIOD_MINUTE || _la==TIMEPERIOD_MINUTES) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class SecondPartContext : ParserRuleContext { + public IToken i; + public ITerminalNode TIMEPERIOD_SECONDS() { return GetToken(EsperEPL2GrammarParser.TIMEPERIOD_SECONDS, 0); } + public ITerminalNode TIMEPERIOD_SECOND() { return GetToken(EsperEPL2GrammarParser.TIMEPERIOD_SECOND, 0); } + public ITerminalNode TIMEPERIOD_SEC() { return GetToken(EsperEPL2GrammarParser.TIMEPERIOD_SEC, 0); } + public NumberconstantContext numberconstant() { + return GetRuleContext(0); + } + public SubstitutionContext substitution() { + return GetRuleContext(0); + } + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public SecondPartContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_secondPart; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterSecondPart(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitSecondPart(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitSecondPart(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public SecondPartContext secondPart() { + SecondPartContext _localctx = new SecondPartContext(Context, State); + EnterRule(_localctx, 474, RULE_secondPart); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 3200; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case PLUS: + case MINUS: + case IntegerLiteral: + case FloatingPointLiteral: + { + State = 3197; numberconstant(); + } + break; + case IDENT: + { + State = 3198; _localctx.i = Match(IDENT); + } + break; + case QUESTION: + { + State = 3199; substitution(); + } + break; + default: + throw new NoViableAltException(this); + } + State = 3202; + _la = TokenStream.LA(1); + if ( !(((((_la - 98)) & ~0x3f) == 0 && ((1L << (_la - 98)) & ((1L << (TIMEPERIOD_SEC - 98)) | (1L << (TIMEPERIOD_SECOND - 98)) | (1L << (TIMEPERIOD_SECONDS - 98)))) != 0)) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class MillisecondPartContext : ParserRuleContext { + public IToken i; + public ITerminalNode TIMEPERIOD_MILLISECONDS() { return GetToken(EsperEPL2GrammarParser.TIMEPERIOD_MILLISECONDS, 0); } + public ITerminalNode TIMEPERIOD_MILLISECOND() { return GetToken(EsperEPL2GrammarParser.TIMEPERIOD_MILLISECOND, 0); } + public ITerminalNode TIMEPERIOD_MILLISEC() { return GetToken(EsperEPL2GrammarParser.TIMEPERIOD_MILLISEC, 0); } + public NumberconstantContext numberconstant() { + return GetRuleContext(0); + } + public SubstitutionContext substitution() { + return GetRuleContext(0); + } + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public MillisecondPartContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_millisecondPart; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterMillisecondPart(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitMillisecondPart(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitMillisecondPart(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public MillisecondPartContext millisecondPart() { + MillisecondPartContext _localctx = new MillisecondPartContext(Context, State); + EnterRule(_localctx, 476, RULE_millisecondPart); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 3207; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case PLUS: + case MINUS: + case IntegerLiteral: + case FloatingPointLiteral: + { + State = 3204; numberconstant(); + } + break; + case IDENT: + { + State = 3205; _localctx.i = Match(IDENT); + } + break; + case QUESTION: + { + State = 3206; substitution(); + } + break; + default: + throw new NoViableAltException(this); + } + State = 3209; + _la = TokenStream.LA(1); + if ( !(((((_la - 101)) & ~0x3f) == 0 && ((1L << (_la - 101)) & ((1L << (TIMEPERIOD_MILLISEC - 101)) | (1L << (TIMEPERIOD_MILLISECOND - 101)) | (1L << (TIMEPERIOD_MILLISECONDS - 101)))) != 0)) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class MicrosecondPartContext : ParserRuleContext { + public IToken i; + public ITerminalNode TIMEPERIOD_MICROSECONDS() { return GetToken(EsperEPL2GrammarParser.TIMEPERIOD_MICROSECONDS, 0); } + public ITerminalNode TIMEPERIOD_MICROSECOND() { return GetToken(EsperEPL2GrammarParser.TIMEPERIOD_MICROSECOND, 0); } + public ITerminalNode TIMEPERIOD_MICROSEC() { return GetToken(EsperEPL2GrammarParser.TIMEPERIOD_MICROSEC, 0); } + public NumberconstantContext numberconstant() { + return GetRuleContext(0); + } + public SubstitutionContext substitution() { + return GetRuleContext(0); + } + public ITerminalNode IDENT() { return GetToken(EsperEPL2GrammarParser.IDENT, 0); } + public MicrosecondPartContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_microsecondPart; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterMicrosecondPart(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitMicrosecondPart(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitMicrosecondPart(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public MicrosecondPartContext microsecondPart() { + MicrosecondPartContext _localctx = new MicrosecondPartContext(Context, State); + EnterRule(_localctx, 478, RULE_microsecondPart); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 3214; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case PLUS: + case MINUS: + case IntegerLiteral: + case FloatingPointLiteral: + { + State = 3211; numberconstant(); + } + break; + case IDENT: + { + State = 3212; _localctx.i = Match(IDENT); + } + break; + case QUESTION: + { + State = 3213; substitution(); + } + break; + default: + throw new NoViableAltException(this); + } + State = 3216; + _la = TokenStream.LA(1); + if ( !(((((_la - 104)) & ~0x3f) == 0 && ((1L << (_la - 104)) & ((1L << (TIMEPERIOD_MICROSEC - 104)) | (1L << (TIMEPERIOD_MICROSECOND - 104)) | (1L << (TIMEPERIOD_MICROSECONDS - 104)))) != 0)) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class NumberContext : ParserRuleContext { + public ITerminalNode IntegerLiteral() { return GetToken(EsperEPL2GrammarParser.IntegerLiteral, 0); } + public ITerminalNode FloatingPointLiteral() { return GetToken(EsperEPL2GrammarParser.FloatingPointLiteral, 0); } + public NumberContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_number; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterNumber(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitNumber(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitNumber(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public NumberContext number() { + NumberContext _localctx = new NumberContext(Context, State); + EnterRule(_localctx, 480, RULE_number); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 3218; + _la = TokenStream.LA(1); + if ( !(_la==IntegerLiteral || _la==FloatingPointLiteral) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class SubstitutionContext : ParserRuleContext { + public IToken q; + public ITerminalNode QUESTION() { return GetToken(EsperEPL2GrammarParser.QUESTION, 0); } + public ITerminalNode COLON() { return GetToken(EsperEPL2GrammarParser.COLON, 0); } + public SlashIdentifierContext slashIdentifier() { + return GetRuleContext(0); + } + public SubstitutionContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_substitution; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterSubstitution(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitSubstitution(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitSubstitution(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public SubstitutionContext substitution() { + SubstitutionContext _localctx = new SubstitutionContext(Context, State); + EnterRule(_localctx, 482, RULE_substitution); + try { + EnterOuterAlt(_localctx, 1); + { + State = 3220; _localctx.q = Match(QUESTION); + State = 3223; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,458,Context) ) { + case 1: + { + State = 3221; Match(COLON); + State = 3222; slashIdentifier(); + } + break; + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class ConstantContext : ParserRuleContext { + public IToken t; + public IToken f; + public IToken nu; + public NumberconstantContext numberconstant() { + return GetRuleContext(0); + } + public StringconstantContext stringconstant() { + return GetRuleContext(0); + } + public ITerminalNode BOOLEAN_TRUE() { return GetToken(EsperEPL2GrammarParser.BOOLEAN_TRUE, 0); } + public ITerminalNode BOOLEAN_FALSE() { return GetToken(EsperEPL2GrammarParser.BOOLEAN_FALSE, 0); } + public ITerminalNode VALUE_NULL() { return GetToken(EsperEPL2GrammarParser.VALUE_NULL, 0); } + public ConstantContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_constant; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterConstant(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitConstant(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitConstant(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public ConstantContext constant() { + ConstantContext _localctx = new ConstantContext(Context, State); + EnterRule(_localctx, 484, RULE_constant); + try { + State = 3230; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case PLUS: + case MINUS: + case IntegerLiteral: + case FloatingPointLiteral: + EnterOuterAlt(_localctx, 1); + { + State = 3225; numberconstant(); + } + break; + case QUOTED_STRING_LITERAL: + case STRING_LITERAL: + EnterOuterAlt(_localctx, 2); + { + State = 3226; stringconstant(); + } + break; + case BOOLEAN_TRUE: + EnterOuterAlt(_localctx, 3); + { + State = 3227; _localctx.t = Match(BOOLEAN_TRUE); + } + break; + case BOOLEAN_FALSE: + EnterOuterAlt(_localctx, 4); + { + State = 3228; _localctx.f = Match(BOOLEAN_FALSE); + } + break; + case VALUE_NULL: + EnterOuterAlt(_localctx, 5); + { + State = 3229; _localctx.nu = Match(VALUE_NULL); + } + break; + default: + throw new NoViableAltException(this); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class NumberconstantContext : ParserRuleContext { + public IToken m; + public IToken p; + public NumberContext number() { + return GetRuleContext(0); + } + public ITerminalNode MINUS() { return GetToken(EsperEPL2GrammarParser.MINUS, 0); } + public ITerminalNode PLUS() { return GetToken(EsperEPL2GrammarParser.PLUS, 0); } + public NumberconstantContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_numberconstant; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterNumberconstant(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitNumberconstant(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitNumberconstant(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public NumberconstantContext numberconstant() { + NumberconstantContext _localctx = new NumberconstantContext(Context, State); + EnterRule(_localctx, 486, RULE_numberconstant); + try { + EnterOuterAlt(_localctx, 1); + { + State = 3234; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case MINUS: + { + State = 3232; _localctx.m = Match(MINUS); + } + break; + case PLUS: + { + State = 3233; _localctx.p = Match(PLUS); + } + break; + case IntegerLiteral: + case FloatingPointLiteral: + break; + default: + throw new NoViableAltException(this); + } + State = 3236; number(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class StringconstantContext : ParserRuleContext { + public IToken sl; + public IToken qsl; + public ITerminalNode STRING_LITERAL() { return GetToken(EsperEPL2GrammarParser.STRING_LITERAL, 0); } + public ITerminalNode QUOTED_STRING_LITERAL() { return GetToken(EsperEPL2GrammarParser.QUOTED_STRING_LITERAL, 0); } + public StringconstantContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_stringconstant; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterStringconstant(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitStringconstant(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitStringconstant(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public StringconstantContext stringconstant() { + StringconstantContext _localctx = new StringconstantContext(Context, State); + EnterRule(_localctx, 488, RULE_stringconstant); + try { + State = 3240; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case STRING_LITERAL: + EnterOuterAlt(_localctx, 1); + { + State = 3238; _localctx.sl = Match(STRING_LITERAL); + } + break; + case QUOTED_STRING_LITERAL: + EnterOuterAlt(_localctx, 2); + { + State = 3239; _localctx.qsl = Match(QUOTED_STRING_LITERAL); + } + break; + default: + throw new NoViableAltException(this); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class JsonvalueContext : ParserRuleContext { + public ConstantContext constant() { + return GetRuleContext(0); + } + public JsonobjectContext jsonobject() { + return GetRuleContext(0); + } + public JsonarrayContext jsonarray() { + return GetRuleContext(0); + } + public JsonvalueContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_jsonvalue; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterJsonvalue(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitJsonvalue(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitJsonvalue(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public JsonvalueContext jsonvalue() { + JsonvalueContext _localctx = new JsonvalueContext(Context, State); + EnterRule(_localctx, 490, RULE_jsonvalue); + try { + State = 3245; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case BOOLEAN_TRUE: + case BOOLEAN_FALSE: + case VALUE_NULL: + case PLUS: + case MINUS: + case QUOTED_STRING_LITERAL: + case STRING_LITERAL: + case IntegerLiteral: + case FloatingPointLiteral: + EnterOuterAlt(_localctx, 1); + { + State = 3242; constant(); + } + break; + case LCURLY: + EnterOuterAlt(_localctx, 2); + { + State = 3243; jsonobject(); + } + break; + case LBRACK: + EnterOuterAlt(_localctx, 3); + { + State = 3244; jsonarray(); + } + break; + default: + throw new NoViableAltException(this); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class JsonobjectContext : ParserRuleContext { + public ITerminalNode LCURLY() { return GetToken(EsperEPL2GrammarParser.LCURLY, 0); } + public JsonmembersContext jsonmembers() { + return GetRuleContext(0); + } + public ITerminalNode RCURLY() { return GetToken(EsperEPL2GrammarParser.RCURLY, 0); } + public JsonobjectContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_jsonobject; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterJsonobject(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitJsonobject(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitJsonobject(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public JsonobjectContext jsonobject() { + JsonobjectContext _localctx = new JsonobjectContext(Context, State); + EnterRule(_localctx, 492, RULE_jsonobject); + try { + EnterOuterAlt(_localctx, 1); + { + State = 3247; Match(LCURLY); + State = 3248; jsonmembers(); + State = 3249; Match(RCURLY); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class JsonarrayContext : ParserRuleContext { + public ITerminalNode LBRACK() { return GetToken(EsperEPL2GrammarParser.LBRACK, 0); } + public ITerminalNode RBRACK() { return GetToken(EsperEPL2GrammarParser.RBRACK, 0); } + public JsonelementsContext jsonelements() { + return GetRuleContext(0); + } + public JsonarrayContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_jsonarray; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterJsonarray(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitJsonarray(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitJsonarray(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public JsonarrayContext jsonarray() { + JsonarrayContext _localctx = new JsonarrayContext(Context, State); + EnterRule(_localctx, 494, RULE_jsonarray); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 3251; Match(LBRACK); + State = 3253; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (((((_la - 107)) & ~0x3f) == 0 && ((1L << (_la - 107)) & ((1L << (BOOLEAN_TRUE - 107)) | (1L << (BOOLEAN_FALSE - 107)) | (1L << (VALUE_NULL - 107)) | (1L << (LBRACK - 107)) | (1L << (LCURLY - 107)) | (1L << (PLUS - 107)) | (1L << (MINUS - 107)))) != 0) || ((((_la - 193)) & ~0x3f) == 0 && ((1L << (_la - 193)) & ((1L << (QUOTED_STRING_LITERAL - 193)) | (1L << (STRING_LITERAL - 193)) | (1L << (IntegerLiteral - 193)) | (1L << (FloatingPointLiteral - 193)))) != 0)) { + { + State = 3252; jsonelements(); + } + } + + State = 3255; Match(RBRACK); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class JsonelementsContext : ParserRuleContext { + public JsonvalueContext[] jsonvalue() { + return GetRuleContexts(); + } + public JsonvalueContext jsonvalue(int i) { + return GetRuleContext(i); + } + public ITerminalNode[] COMMA() { return GetTokens(EsperEPL2GrammarParser.COMMA); } + public ITerminalNode COMMA(int i) { + return GetToken(EsperEPL2GrammarParser.COMMA, i); + } + public JsonelementsContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_jsonelements; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterJsonelements(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitJsonelements(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitJsonelements(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public JsonelementsContext jsonelements() { + JsonelementsContext _localctx = new JsonelementsContext(Context, State); + EnterRule(_localctx, 496, RULE_jsonelements); + int _la; + try { + int _alt; + EnterOuterAlt(_localctx, 1); + { + State = 3257; jsonvalue(); + State = 3262; + ErrorHandler.Sync(this); + _alt = Interpreter.AdaptivePredict(TokenStream,464,Context); + while ( _alt!=2 && _alt!=global::Antlr4.Runtime.Atn.ATN.INVALID_ALT_NUMBER ) { + if ( _alt==1 ) { + { + { + State = 3258; Match(COMMA); + State = 3259; jsonvalue(); + } + } + } + State = 3264; + ErrorHandler.Sync(this); + _alt = Interpreter.AdaptivePredict(TokenStream,464,Context); + } + State = 3266; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==COMMA) { + { + State = 3265; Match(COMMA); + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class JsonmembersContext : ParserRuleContext { + public JsonpairContext[] jsonpair() { + return GetRuleContexts(); + } + public JsonpairContext jsonpair(int i) { + return GetRuleContext(i); + } + public ITerminalNode[] COMMA() { return GetTokens(EsperEPL2GrammarParser.COMMA); } + public ITerminalNode COMMA(int i) { + return GetToken(EsperEPL2GrammarParser.COMMA, i); + } + public JsonmembersContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_jsonmembers; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterJsonmembers(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitJsonmembers(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitJsonmembers(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public JsonmembersContext jsonmembers() { + JsonmembersContext _localctx = new JsonmembersContext(Context, State); + EnterRule(_localctx, 498, RULE_jsonmembers); + int _la; + try { + int _alt; + EnterOuterAlt(_localctx, 1); + { + State = 3268; jsonpair(); + State = 3273; + ErrorHandler.Sync(this); + _alt = Interpreter.AdaptivePredict(TokenStream,466,Context); + while ( _alt!=2 && _alt!=global::Antlr4.Runtime.Atn.ATN.INVALID_ALT_NUMBER ) { + if ( _alt==1 ) { + { + { + State = 3269; Match(COMMA); + State = 3270; jsonpair(); + } + } + } + State = 3275; + ErrorHandler.Sync(this); + _alt = Interpreter.AdaptivePredict(TokenStream,466,Context); + } + State = 3277; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==COMMA) { + { + State = 3276; Match(COMMA); + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class JsonpairContext : ParserRuleContext { + public ITerminalNode COLON() { return GetToken(EsperEPL2GrammarParser.COLON, 0); } + public JsonvalueContext jsonvalue() { + return GetRuleContext(0); + } + public StringconstantContext stringconstant() { + return GetRuleContext(0); + } + public KeywordAllowedIdentContext keywordAllowedIdent() { + return GetRuleContext(0); + } + public JsonpairContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_jsonpair; } } + public override void EnterRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.EnterJsonpair(this); + } + public override void ExitRule(IParseTreeListener listener) { + IEsperEPL2GrammarListener typedListener = listener as IEsperEPL2GrammarListener; + if (typedListener != null) typedListener.ExitJsonpair(this); + } + public override TResult Accept(IParseTreeVisitor visitor) { + IEsperEPL2GrammarVisitor typedVisitor = visitor as IEsperEPL2GrammarVisitor; + if (typedVisitor != null) return typedVisitor.VisitJsonpair(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public JsonpairContext jsonpair() { + JsonpairContext _localctx = new JsonpairContext(Context, State); + EnterRule(_localctx, 500, RULE_jsonpair); + try { + EnterOuterAlt(_localctx, 1); + { + State = 3281; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case QUOTED_STRING_LITERAL: + case STRING_LITERAL: + { + State = 3279; stringconstant(); + } + break; + case WINDOW: + case ESCAPE: + case EVERY_EXPR: + case SUM: + case AVG: + case MAX: + case MIN: + case COALESCE: + case MEDIAN: + case STDDEV: + case AVEDEV: + case COUNT: + case OUTER: + case JOIN: + case LEFT: + case RIGHT: + case FULL: + case EVENTS: + case FIRST: + case LAST: + case SCHEMA: + case UNIDIRECTIONAL: + case RETAINUNION: + case RETAININTERSECTION: + case PATTERN: + case SQL: + case METADATASQL: + case PREVIOUS: + case PREVIOUSTAIL: + case PRIOR: + case WEEKDAY: + case LW: + case INSTANCEOF: + case TYPEOF: + case CAST: + case SNAPSHOT: + case VARIABLE: + case TABLE: + case UNTIL: + case AT: + case INDEX: + case DEFINE: + case PARTITION: + case MATCHES: + case FOR: + case WHILE: + case USING: + case MERGE: + case MATCHED: + case CONTEXT: + case TICKED_STRING_LITERAL: + case IDENT: + { + State = 3280; keywordAllowedIdent(); + } + break; + default: + throw new NoViableAltException(this); + } + State = 3283; Match(COLON); + State = 3284; jsonvalue(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + private static string _serializedATN = _serializeATN(); + private static string _serializeATN() + { + StringBuilder sb = new StringBuilder(); + sb.Append("\x3\x430\xD6D1\x8206\xAD2D\x4417\xAEF1\x8D80\xAADD\x3\xC7"); + sb.Append("\xCD9\x4\x2\t\x2\x4\x3\t\x3\x4\x4\t\x4\x4\x5\t\x5\x4\x6\t\x6"); + sb.Append("\x4\a\t\a\x4\b\t\b\x4\t\t\t\x4\n\t\n\x4\v\t\v\x4\f\t\f\x4\r"); + sb.Append("\t\r\x4\xE\t\xE\x4\xF\t\xF\x4\x10\t\x10\x4\x11\t\x11\x4\x12"); + sb.Append("\t\x12\x4\x13\t\x13\x4\x14\t\x14\x4\x15\t\x15\x4\x16\t\x16\x4"); + sb.Append("\x17\t\x17\x4\x18\t\x18\x4\x19\t\x19\x4\x1A\t\x1A\x4\x1B\t\x1B"); + sb.Append("\x4\x1C\t\x1C\x4\x1D\t\x1D\x4\x1E\t\x1E\x4\x1F\t\x1F\x4 \t "); + sb.Append("\x4!\t!\x4\"\t\"\x4#\t#\x4$\t$\x4%\t%\x4&\t&\x4\'\t\'\x4(\t"); + sb.Append("(\x4)\t)\x4*\t*\x4+\t+\x4,\t,\x4-\t-\x4.\t.\x4/\t/\x4\x30\t"); + sb.Append("\x30\x4\x31\t\x31\x4\x32\t\x32\x4\x33\t\x33\x4\x34\t\x34\x4"); + sb.Append("\x35\t\x35\x4\x36\t\x36\x4\x37\t\x37\x4\x38\t\x38\x4\x39\t\x39"); + sb.Append("\x4:\t:\x4;\t;\x4<\t<\x4=\t=\x4>\t>\x4?\t?\x4@\t@\x4\x41\t\x41"); + sb.Append("\x4\x42\t\x42\x4\x43\t\x43\x4\x44\t\x44\x4\x45\t\x45\x4\x46"); + sb.Append("\t\x46\x4G\tG\x4H\tH\x4I\tI\x4J\tJ\x4K\tK\x4L\tL\x4M\tM\x4N"); + sb.Append("\tN\x4O\tO\x4P\tP\x4Q\tQ\x4R\tR\x4S\tS\x4T\tT\x4U\tU\x4V\tV"); + sb.Append("\x4W\tW\x4X\tX\x4Y\tY\x4Z\tZ\x4[\t[\x4\\\t\\\x4]\t]\x4^\t^\x4"); + sb.Append("_\t_\x4`\t`\x4\x61\t\x61\x4\x62\t\x62\x4\x63\t\x63\x4\x64\t"); + sb.Append("\x64\x4\x65\t\x65\x4\x66\t\x66\x4g\tg\x4h\th\x4i\ti\x4j\tj\x4"); + sb.Append("k\tk\x4l\tl\x4m\tm\x4n\tn\x4o\to\x4p\tp\x4q\tq\x4r\tr\x4s\t"); + sb.Append("s\x4t\tt\x4u\tu\x4v\tv\x4w\tw\x4x\tx\x4y\ty\x4z\tz\x4{\t{\x4"); + sb.Append("|\t|\x4}\t}\x4~\t~\x4\x7F\t\x7F\x4\x80\t\x80\x4\x81\t\x81\x4"); + sb.Append("\x82\t\x82\x4\x83\t\x83\x4\x84\t\x84\x4\x85\t\x85\x4\x86\t\x86"); + sb.Append("\x4\x87\t\x87\x4\x88\t\x88\x4\x89\t\x89\x4\x8A\t\x8A\x4\x8B"); + sb.Append("\t\x8B\x4\x8C\t\x8C\x4\x8D\t\x8D\x4\x8E\t\x8E\x4\x8F\t\x8F\x4"); + sb.Append("\x90\t\x90\x4\x91\t\x91\x4\x92\t\x92\x4\x93\t\x93\x4\x94\t\x94"); + sb.Append("\x4\x95\t\x95\x4\x96\t\x96\x4\x97\t\x97\x4\x98\t\x98\x4\x99"); + sb.Append("\t\x99\x4\x9A\t\x9A\x4\x9B\t\x9B\x4\x9C\t\x9C\x4\x9D\t\x9D\x4"); + sb.Append("\x9E\t\x9E\x4\x9F\t\x9F\x4\xA0\t\xA0\x4\xA1\t\xA1\x4\xA2\t\xA2"); + sb.Append("\x4\xA3\t\xA3\x4\xA4\t\xA4\x4\xA5\t\xA5\x4\xA6\t\xA6\x4\xA7"); + sb.Append("\t\xA7\x4\xA8\t\xA8\x4\xA9\t\xA9\x4\xAA\t\xAA\x4\xAB\t\xAB\x4"); + sb.Append("\xAC\t\xAC\x4\xAD\t\xAD\x4\xAE\t\xAE\x4\xAF\t\xAF\x4\xB0\t\xB0"); + sb.Append("\x4\xB1\t\xB1\x4\xB2\t\xB2\x4\xB3\t\xB3\x4\xB4\t\xB4\x4\xB5"); + sb.Append("\t\xB5\x4\xB6\t\xB6\x4\xB7\t\xB7\x4\xB8\t\xB8\x4\xB9\t\xB9\x4"); + sb.Append("\xBA\t\xBA\x4\xBB\t\xBB\x4\xBC\t\xBC\x4\xBD\t\xBD\x4\xBE\t\xBE"); + sb.Append("\x4\xBF\t\xBF\x4\xC0\t\xC0\x4\xC1\t\xC1\x4\xC2\t\xC2\x4\xC3"); + sb.Append("\t\xC3\x4\xC4\t\xC4\x4\xC5\t\xC5\x4\xC6\t\xC6\x4\xC7\t\xC7\x4"); + sb.Append("\xC8\t\xC8\x4\xC9\t\xC9\x4\xCA\t\xCA\x4\xCB\t\xCB\x4\xCC\t\xCC"); + sb.Append("\x4\xCD\t\xCD\x4\xCE\t\xCE\x4\xCF\t\xCF\x4\xD0\t\xD0\x4\xD1"); + sb.Append("\t\xD1\x4\xD2\t\xD2\x4\xD3\t\xD3\x4\xD4\t\xD4\x4\xD5\t\xD5\x4"); + sb.Append("\xD6\t\xD6\x4\xD7\t\xD7\x4\xD8\t\xD8\x4\xD9\t\xD9\x4\xDA\t\xDA"); + sb.Append("\x4\xDB\t\xDB\x4\xDC\t\xDC\x4\xDD\t\xDD\x4\xDE\t\xDE\x4\xDF"); + sb.Append("\t\xDF\x4\xE0\t\xE0\x4\xE1\t\xE1\x4\xE2\t\xE2\x4\xE3\t\xE3\x4"); + sb.Append("\xE4\t\xE4\x4\xE5\t\xE5\x4\xE6\t\xE6\x4\xE7\t\xE7\x4\xE8\t\xE8"); + sb.Append("\x4\xE9\t\xE9\x4\xEA\t\xEA\x4\xEB\t\xEB\x4\xEC\t\xEC\x4\xED"); + sb.Append("\t\xED\x4\xEE\t\xEE\x4\xEF\t\xEF\x4\xF0\t\xF0\x4\xF1\t\xF1\x4"); + sb.Append("\xF2\t\xF2\x4\xF3\t\xF3\x4\xF4\t\xF4\x4\xF5\t\xF5\x4\xF6\t\xF6"); + sb.Append("\x4\xF7\t\xF7\x4\xF8\t\xF8\x4\xF9\t\xF9\x4\xFA\t\xFA\x4\xFB"); + sb.Append("\t\xFB\x4\xFC\t\xFC\x3\x2\x3\x2\a\x2\x1FB\n\x2\f\x2\xE\x2\x1FE"); + sb.Append("\v\x2\x3\x2\x3\x2\x3\x2\x3\x3\x3\x3\a\x3\x205\n\x3\f\x3\xE\x3"); + sb.Append("\x208\v\x3\x3\x3\x3\x3\x3\x3\x3\x4\x3\x4\x3\x4\x3\x5\x3\x5\x3"); + sb.Append("\x5\x3\x6\x3\x6\x5\x6\x215\n\x6\x3\x6\x3\x6\x5\x6\x219\n\x6"); + sb.Append("\x3\x6\x5\x6\x21C\n\x6\x3\x6\x5\x6\x21F\n\x6\x3\x6\x3\x6\x3"); + sb.Append("\x6\x5\x6\x224\n\x6\x3\x6\x5\x6\x227\n\x6\x3\x6\x3\x6\x5\x6"); + sb.Append("\x22B\n\x6\x3\x6\x3\x6\x3\a\x3\a\x3\a\x3\b\x3\b\x5\b\x234\n"); + sb.Append("\b\x3\b\x3\b\x3\b\x3\b\x3\b\x3\b\x3\b\x5\b\x23D\n\b\x3\t\x3"); + sb.Append("\t\x3\t\x3\t\x3\t\x5\t\x244\n\t\x3\t\x3\t\x3\n\x3\n\x3\n\x3"); + sb.Append("\n\x3\n\x3\n\x3\v\x3\v\x3\v\x3\v\x3\v\x5\v\x253\n\v\x3\v\x5"); + sb.Append("\v\x256\n\v\x3\f\x3\f\x3\f\a\f\x25B\n\f\f\f\xE\f\x25E\v\f\x3"); + sb.Append("\r\x3\r\x3\r\x3\r\x3\xE\x3\xE\x3\xE\x3\xE\x3\xE\x5\xE\x269\n"); + sb.Append("\xE\x3\xF\x3\xF\x3\xF\x3\xF\a\xF\x26F\n\xF\f\xF\xE\xF\x272\v"); + sb.Append("\xF\x5\xF\x274\n\xF\x3\xF\x5\xF\x277\n\xF\x3\xF\x3\xF\x3\x10"); + sb.Append("\x5\x10\x27C\n\x10\x3\x10\x3\x10\x3\x10\x3\x10\x3\x10\x3\x10"); + sb.Append("\x3\x10\x3\x10\x3\x10\x3\x10\x3\x10\x3\x10\x3\x10\x3\x10\x5"); + sb.Append("\x10\x28C\n\x10\x3\x10\x5\x10\x28F\n\x10\x3\x11\x3\x11\x3\x11"); + sb.Append("\x3\x12\x3\x12\x5\x12\x296\n\x12\x3\x12\x3\x12\x5\x12\x29A\n"); + sb.Append("\x12\x3\x12\x3\x12\x3\x12\x3\x12\x5\x12\x2A0\n\x12\x3\x12\x5"); + sb.Append("\x12\x2A3\n\x12\x3\x12\x3\x12\x5\x12\x2A7\n\x12\x3\x12\x3\x12"); + sb.Append("\x3\x12\x5\x12\x2AC\n\x12\x3\x12\x3\x12\x5\x12\x2B0\n\x12\x3"); + sb.Append("\x12\x3\x12\x5\x12\x2B4\n\x12\x3\x12\x3\x12\x3\x12\x5\x12\x2B9"); + sb.Append("\n\x12\x3\x12\x3\x12\x5\x12\x2BD\n\x12\x3\x13\x3\x13\x3\x13"); + sb.Append("\x3\x13\x3\x13\x6\x13\x2C4\n\x13\r\x13\xE\x13\x2C5\x3\x13\x5"); + sb.Append("\x13\x2C9\n\x13\x5\x13\x2CB\n\x13\x3\x13\x3\x13\x3\x13\x5\x13"); + sb.Append("\x2D0\n\x13\x3\x14\x3\x14\x5\x14\x2D4\n\x14\x3\x14\x3\x14\x3"); + sb.Append("\x14\x5\x14\x2D9\n\x14\x3\x15\x3\x15\x3\x15\x3\x15\x3\x16\x3"); + sb.Append("\x16\x3\x16\x3\x16\x5\x16\x2E3\n\x16\x3\x16\x3\x16\x3\x16\x3"); + sb.Append("\x16\x5\x16\x2E9\n\x16\x3\x17\x3\x17\x5\x17\x2ED\n\x17\x3\x17"); + sb.Append("\x3\x17\x3\x17\x3\x17\x5\x17\x2F3\n\x17\x3\x17\x3\x17\x5\x17"); + sb.Append("\x2F7\n\x17\x3\x17\x6\x17\x2FA\n\x17\r\x17\xE\x17\x2FB\x3\x18"); + sb.Append("\x3\x18\x5\x18\x300\n\x18\x3\x19\x3\x19\x3\x19\x3\x19\x5\x19"); + sb.Append("\x306\n\x19\x3\x19\x6\x19\x309\n\x19\r\x19\xE\x19\x30A\x3\x1A"); + sb.Append("\x3\x1A\x3\x1A\x3\x1A\x3\x1A\x3\x1A\x3\x1A\x5\x1A\x314\n\x1A"); + sb.Append("\x3\x1A\x3\x1A\x3\x1A\x5\x1A\x319\n\x1A\x3\x1A\x5\x1A\x31C\n"); + sb.Append("\x1A\x3\x1B\x3\x1B\x3\x1B\x3\x1B\x3\x1B\x5\x1B\x323\n\x1B\x3"); + sb.Append("\x1B\x6\x1B\x326\n\x1B\r\x1B\xE\x1B\x327\x3\x1C\x3\x1C\x3\x1C"); + sb.Append("\x3\x1D\x3\x1D\x3\x1D\x5\x1D\x330\n\x1D\x3\x1D\x3\x1D\x3\x1D"); + sb.Append("\x3\x1D\x5\x1D\x336\n\x1D\x3\x1D\x3\x1D\x3\x1D\x3\x1D\x5\x1D"); + sb.Append("\x33C\n\x1D\x3\x1E\x3\x1E\x5\x1E\x340\n\x1E\x3\x1E\x3\x1E\x5"); + sb.Append("\x1E\x344\n\x1E\x3\x1E\x5\x1E\x347\n\x1E\x3\x1E\x5\x1E\x34A"); + sb.Append("\n\x1E\x3\x1E\x3\x1E\x5\x1E\x34E\n\x1E\x3\x1E\x3\x1E\x5\x1E"); + sb.Append("\x352\n\x1E\x3\x1E\x3\x1E\x3\x1E\x5\x1E\x357\n\x1E\x3\x1E\x3"); + sb.Append("\x1E\x5\x1E\x35B\n\x1E\x3\x1E\x3\x1E\x3\x1E\x5\x1E\x360\n\x1E"); + sb.Append("\x3\x1E\x3\x1E\x5\x1E\x364\n\x1E\x3\x1F\x3\x1F\x3\x1F\x3\x1F"); + sb.Append("\x3\x1F\x5\x1F\x36B\n\x1F\x3\x1F\x3\x1F\x3\x1F\x3\x1F\x5\x1F"); + sb.Append("\x371\n\x1F\x3 \x3 \x3 \x3 \x3 \x5 \x378\n \x3 \x3 \x5 \x37C"); + sb.Append("\n \x3!\x3!\x3!\x3!\x3!\x5!\x383\n!\x3\"\x3\"\x3\"\x5\"\x388"); + sb.Append("\n\"\x3#\x3#\x3#\x3#\x5#\x38E\n#\x3$\x3$\x3$\x3%\x3%\x3%\a%"); + sb.Append("\x396\n%\f%\xE%\x399\v%\x3&\x3&\x3&\x3&\x3&\x5&\x3A0\n&\x3\'"); + sb.Append("\x3\'\x3\'\x3\'\x3\'\x5\'\x3A7\n\'\x3(\x3(\x3(\x3(\x5(\x3AD"); + sb.Append("\n(\x3(\x3(\x5(\x3B1\n(\x3(\x5(\x3B4\n(\x3(\x3(\x3(\x3(\x3("); + sb.Append("\x5(\x3BB\n(\x3(\x3(\x3(\x5(\x3C0\n(\x5(\x3C2\n(\x3)\x3)\x3"); + sb.Append(")\x3)\x5)\x3C8\n)\x3)\x3)\x3*\x3*\x5*\x3CE\n*\x3*\x3*\x3*\x3"); + sb.Append("*\x3*\x3*\x3*\x3*\x3+\x3+\x3+\a+\x3DB\n+\f+\xE+\x3DE\v+\x3,"); + sb.Append("\x3,\x5,\x3E2\n,\x3-\x3-\x5-\x3E6\n-\x3-\x3-\x3-\x3-\x5-\x3EC"); + sb.Append("\n-\x3-\x5-\x3EF\n-\x3-\x3-\x3-\x5-\x3F4\n-\x3.\x3.\x3.\x3."); + sb.Append("\x5.\x3FA\n.\x3.\x3.\x3.\x3.\x3/\x3/\x3/\a/\x403\n/\f/\xE/\x406"); + sb.Append("\v/\x3\x30\x3\x30\x3\x30\x3\x30\x5\x30\x40C\n\x30\x3\x30\x5"); + sb.Append("\x30\x40F\n\x30\x3\x30\x5\x30\x412\n\x30\x3\x30\x3\x30\a\x30"); + sb.Append("\x416\n\x30\f\x30\xE\x30\x419\v\x30\x3\x31\x3\x31\x3\x31\x5"); + sb.Append("\x31\x41E\n\x31\x3\x31\x5\x31\x421\n\x31\x3\x32\x3\x32\x3\x32"); + sb.Append("\a\x32\x426\n\x32\f\x32\xE\x32\x429\v\x32\x3\x33\x3\x33\x3\x33"); + sb.Append("\x3\x33\x3\x33\x5\x33\x430\n\x33\x3\x33\x5\x33\x433\n\x33\x5"); + sb.Append("\x33\x435\n\x33\x3\x34\x3\x34\x3\x34\a\x34\x43A\n\x34\f\x34"); + sb.Append("\xE\x34\x43D\v\x34\x3\x35\x3\x35\x3\x35\x3\x35\x5\x35\x443\n"); + sb.Append("\x35\x3\x35\x3\x35\x3\x35\x3\x35\x5\x35\x449\n\x35\x3\x36\x3"); + sb.Append("\x36\x5\x36\x44D\n\x36\x3\x36\x3\x36\x3\x37\x3\x37\x3\x37\x5"); + sb.Append("\x37\x454\n\x37\x3\x37\x3\x37\x3\x37\x5\x37\x459\n\x37\x3\x37"); + sb.Append("\x5\x37\x45C\n\x37\x3\x37\a\x37\x45F\n\x37\f\x37\xE\x37\x462"); + sb.Append("\v\x37\x3\x38\x3\x38\x3\x38\x3\x38\x3\x38\x3\x38\x5\x38\x46A"); + sb.Append("\n\x38\x3\x38\x3\x38\x5\x38\x46E\n\x38\x3\x39\x3\x39\x3\x39"); + sb.Append("\x3:\x3:\x3:\x3:\x3:\x3:\x3:\x3;\x3;\x3;\x3;\x5;\x47E\n;\x3"); + sb.Append(";\x3;\x3<\x3<\a<\x484\n<\f<\xE<\x487\v<\x3=\a=\x48A\n=\f=\xE"); + sb.Append("=\x48D\v=\x3=\x3=\x5=\x491\n=\x3=\x5=\x494\n=\x3=\x5=\x497\n"); + sb.Append("=\x3=\x3=\x5=\x49B\n=\x3=\x5=\x49E\n=\x3=\x3=\x3=\x3=\x5=\x4A4"); + sb.Append("\n=\x3>\x3>\x3>\x3>\x3?\x3?\x3?\a?\x4AD\n?\f?\xE?\x4B0\v?\x3"); + sb.Append("@\x3@\x5@\x4B4\n@\x3@\x5@\x4B7\n@\x3\x41\x3\x41\x3\x41\x3\x41"); + sb.Append("\x3\x41\x3\x41\x3\x41\x3\x42\x3\x42\x3\x42\x3\x43\x3\x43\x3"); + sb.Append("\x43\x3\x43\a\x43\x4C7\n\x43\f\x43\xE\x43\x4CA\v\x43\x3\x44"); + sb.Append("\x3\x44\x5\x44\x4CE\n\x44\x3\x45\x3\x45\x3\x45\x3\x45\a\x45"); + sb.Append("\x4D4\n\x45\f\x45\xE\x45\x4D7\v\x45\x3\x45\x3\x45\x3\x46\x3"); + sb.Append("\x46\x5\x46\x4DD\n\x46\x3G\x3G\x5G\x4E1\nG\x3H\x3H\x3H\aH\x4E6"); + sb.Append("\nH\fH\xEH\x4E9\vH\x3I\x3I\x3I\x3I\x3I\x3I\x3I\x3I\x3I\x3I\x3"); + sb.Append("I\x5I\x4F6\nI\x5I\x4F8\nI\x3J\x3J\x3J\x3J\x5J\x4FE\nJ\x3J\x3"); + sb.Append("J\x3K\x3K\x3K\x3L\x3L\x3L\x3L\x3L\x3L\aL\x50B\nL\fL\xEL\x50E"); + sb.Append("\vL\x5L\x510\nL\x3M\x3M\x3M\x5M\x515\nM\x3M\x3M\x3N\x3N\x3N"); + sb.Append("\x3N\x5N\x51D\nN\x3N\x3N\x5N\x521\nN\x3N\x3N\x5N\x525\nN\x3"); + sb.Append("N\x5N\x528\nN\x3N\x3N\x3N\x5N\x52D\nN\x3N\x3N\x3N\x5N\x532\n"); + sb.Append("N\x3N\x5N\x535\nN\x3N\x3N\x5N\x539\nN\x3N\x3N\x3N\aN\x53E\n"); + sb.Append("N\fN\xEN\x541\vN\x3N\x3N\x3N\aN\x546\nN\fN\xEN\x549\vN\x3N\x3"); + sb.Append("N\x3N\x3N\x3N\x5N\x550\nN\x3N\x3N\x3N\aN\x555\nN\fN\xEN\x558"); + sb.Append("\vN\x3N\x3N\x3N\x5N\x55D\nN\x5N\x55F\nN\x3O\x3O\x3O\x5O\x564"); + sb.Append("\nO\x3O\x3O\x3P\x3P\x3P\x3P\x5P\x56C\nP\x3P\x3P\x3P\x5P\x571"); + sb.Append("\nP\x3Q\x3Q\x5Q\x575\nQ\x3Q\x5Q\x578\nQ\x3R\x3R\x3R\aR\x57D"); + sb.Append("\nR\fR\xER\x580\vR\x3R\x3R\x3R\x3S\x3S\x3S\x3S\x3T\x3T\x5T\x58B"); + sb.Append("\nT\x3T\x3T\x3T\x3T\x3U\x3U\x3U\x3V\x3V\x3V\aV\x597\nV\fV\xE"); + sb.Append("V\x59A\vV\x3W\x3W\x5W\x59E\nW\x3X\x3X\x3X\x3Y\x3Y\x3Y\x5Y\x5A6"); + sb.Append("\nY\x3Y\x3Y\x3Y\x3Y\x5Y\x5AC\nY\x3Y\x5Y\x5AF\nY\x3Z\x3Z\x3Z"); + sb.Append("\aZ\x5B4\nZ\fZ\xEZ\x5B7\vZ\x3[\x3[\x3[\x5[\x5BC\n[\x3\\\x3\\"); + sb.Append("\a\\\x5C0\n\\\f\\\xE\\\x5C3\v\\\x3]\x3]\a]\x5C7\n]\f]\xE]\x5CA"); + sb.Append("\v]\x3^\x3^\x3^\x5^\x5CF\n^\x3^\x5^\x5D2\n^\x3^\x5^\x5D5\n^"); + sb.Append("\x3^\x3^\x3^\x5^\x5DA\n^\x3_\x3_\x3_\x3_\a_\x5E0\n_\f_\xE_\x5E3"); + sb.Append("\v_\x3`\x3`\x3`\x3`\x3\x61\x3\x61\x3\x62\x3\x62\x3\x62\x5\x62"); + sb.Append("\x5EE\n\x62\x3\x62\x5\x62\x5F1\n\x62\x3\x62\x3\x62\x3\x63\x3"); + sb.Append("\x63\x3\x63\a\x63\x5F8\n\x63\f\x63\xE\x63\x5FB\v\x63\x3\x64"); + sb.Append("\x3\x64\x3\x64\x5\x64\x600\n\x64\x3\x65\x3\x65\x5\x65\x604\n"); + sb.Append("\x65\x3\x65\x5\x65\x607\n\x65\x3\x65\x5\x65\x60A\n\x65\x3\x66"); + sb.Append("\x3\x66\x3\x66\x3g\x3g\x3g\x3g\x3g\x5g\x614\ng\x3h\x3h\x3h\x3"); + sb.Append("h\x5h\x61A\nh\x3h\x5h\x61D\nh\x3h\x3h\x3h\x5h\x622\nh\x3h\x5"); + sb.Append("h\x625\nh\x3h\x3h\x5h\x629\nh\x3i\x3i\x3i\x3i\x5i\x62F\ni\x3"); + sb.Append("i\x5i\x632\ni\x3j\x3j\aj\x636\nj\fj\xEj\x639\vj\x3j\x3j\x3j"); + sb.Append("\x3j\x3k\x3k\x3k\x3k\x3k\x3k\x5k\x645\nk\x3k\x3k\x3k\x5k\x64A"); + sb.Append("\nk\x5k\x64C\nk\x3k\x3k\x3l\x3l\x3l\x3l\x3l\x5l\x655\nl\x3l"); + sb.Append("\x5l\x658\nl\x3l\x5l\x65B\nl\x3m\x3m\x3m\x3m\am\x661\nm\fm\xE"); + sb.Append("m\x664\vm\x3m\x3m\x3m\x3m\am\x66A\nm\fm\xEm\x66D\vm\x5m\x66F"); + sb.Append("\nm\x3n\x3n\x3n\x3n\x3o\x3o\x5o\x677\no\x3o\x3o\x3p\x3p\x5p"); + sb.Append("\x67D\np\x3p\x3p\x5p\x681\np\x3p\x5p\x684\np\x3q\x3q\x3q\aq"); + sb.Append("\x689\nq\fq\xEq\x68C\vq\x3r\x3r\x3r\x5r\x691\nr\x3s\x3s\x3s"); + sb.Append("\x3s\x3s\as\x698\ns\fs\xEs\x69B\vs\x3s\x3s\x3t\x3t\x3t\x3t\x3"); + sb.Append("t\x3t\at\x6A5\nt\ft\xEt\x6A8\vt\x3t\x3t\x3u\x3u\x5u\x6AE\nu"); + sb.Append("\x3v\x3v\x3v\x3v\x3v\av\x6B5\nv\fv\xEv\x6B8\vv\x5v\x6BA\nv\x3"); + sb.Append("v\x5v\x6BD\nv\x3w\x3w\x3w\aw\x6C2\nw\fw\xEw\x6C5\vw\x3x\x3x"); + sb.Append("\x3x\x5x\x6CA\nx\x3y\x3y\x3z\x5z\x6CF\nz\x3z\x3z\x3z\x3z\x5"); + sb.Append("z\x6D5\nz\x3z\x3z\x3z\x3z\x5z\x6DB\nz\x3z\x5z\x6DE\nz\x3z\x3"); + sb.Append("z\x3z\x3z\x3z\x3z\x5z\x6E6\nz\x3z\x3z\x3z\x3z\x5z\x6EC\nz\x3"); + sb.Append("z\x3z\x5z\x6F0\nz\x3z\x5z\x6F3\nz\x3z\x5z\x6F6\nz\x3{\x3{\x3"); + sb.Append("{\x3{\x3{\x5{\x6FD\n{\x3{\x3{\x5{\x701\n{\x3|\x3|\x3|\x3|\x3"); + sb.Append("|\x5|\x708\n|\x3}\x3}\x5}\x70C\n}\x3}\x3}\x5}\x710\n}\x3}\x3"); + sb.Append("}\x5}\x714\n}\x5}\x716\n}\x3~\x3~\x3~\x3~\x3\x7F\x3\x7F\x3\x7F"); + sb.Append("\x3\x7F\x3\x7F\x3\x80\x3\x80\x3\x80\x3\x81\x3\x81\x3\x81\x5"); + sb.Append("\x81\x727\n\x81\x3\x81\x3\x81\x5\x81\x72B\n\x81\x3\x81\x5\x81"); + sb.Append("\x72E\n\x81\x3\x81\x3\x81\x5\x81\x732\n\x81\x3\x81\x5\x81\x735"); + sb.Append("\n\x81\x3\x81\x3\x81\x3\x82\x3\x82\x3\x82\x3\x82\x3\x82\a\x82"); + sb.Append("\x73E\n\x82\f\x82\xE\x82\x741\v\x82\x3\x83\x3\x83\x3\x83\x3"); + sb.Append("\x83\a\x83\x747\n\x83\f\x83\xE\x83\x74A\v\x83\x3\x84\x3\x84"); + sb.Append("\x3\x84\x5\x84\x74F\n\x84\x5\x84\x751\n\x84\x3\x85\x3\x85\x3"); + sb.Append("\x85\x3\x86\x3\x86\x3\x86\x3\x86\x3\x86\x3\x87\x3\x87\x3\x87"); + sb.Append("\x3\x87\x3\x87\x3\x87\x3\x87\x3\x88\x3\x88\x3\x88\x3\x88\x5"); + sb.Append("\x88\x766\n\x88\x3\x89\x3\x89\x3\x89\a\x89\x76B\n\x89\f\x89"); + sb.Append("\xE\x89\x76E\v\x89\x3\x8A\x6\x8A\x771\n\x8A\r\x8A\xE\x8A\x772"); + sb.Append("\x3\x8B\x3\x8B\x3\x8B\x5\x8B\x778\n\x8B\x3\x8C\x3\x8C\x3\x8C"); + sb.Append("\x3\x8C\x3\x8C\x3\x8C\x5\x8C\x780\n\x8C\x3\x8C\x5\x8C\x783\n"); + sb.Append("\x8C\x3\x8D\x3\x8D\x3\x8D\x3\x8D\x3\x8D\a\x8D\x78A\n\x8D\f\x8D"); + sb.Append("\xE\x8D\x78D\v\x8D\x3\x8D\x3\x8D\x3\x8E\x3\x8E\x3\x8E\x3\x8E"); + sb.Append("\x5\x8E\x795\n\x8E\x3\x8E\x5\x8E\x798\n\x8E\x5\x8E\x79A\n\x8E"); + sb.Append("\x3\x8E\x5\x8E\x79D\n\x8E\x3\x8F\x3\x8F\x5\x8F\x7A1\n\x8F\x3"); + sb.Append("\x8F\x5\x8F\x7A4\n\x8F\x3\x8F\x5\x8F\x7A7\n\x8F\x3\x8F\x3\x8F"); + sb.Append("\x3\x90\x3\x90\x3\x90\x3\x90\a\x90\x7AF\n\x90\f\x90\xE\x90\x7B2"); + sb.Append("\v\x90\x3\x91\x3\x91\x3\x91\x3\x91\x3\x92\x3\x92\x3\x93\x3\x93"); + sb.Append("\x3\x93\x6\x93\x7BD\n\x93\r\x93\xE\x93\x7BE\x3\x93\x5\x93\x7C2"); + sb.Append("\n\x93\x3\x93\x3\x93\x3\x93\x3\x93\x3\x93\x3\x93\x3\x93\x6\x93"); + sb.Append("\x7CB\n\x93\r\x93\xE\x93\x7CC\x3\x93\x5\x93\x7D0\n\x93\x3\x93"); + sb.Append("\x3\x93\x3\x93\x3\x93\x5\x93\x7D6\n\x93\x3\x94\x3\x94\x3\x94"); + sb.Append("\a\x94\x7DB\n\x94\f\x94\xE\x94\x7DE\v\x94\x3\x95\x3\x95\x3\x95"); + sb.Append("\a\x95\x7E3\n\x95\f\x95\xE\x95\x7E6\v\x95\x3\x96\x3\x96\x3\x96"); + sb.Append("\a\x96\x7EB\n\x96\f\x96\xE\x96\x7EE\v\x96\x3\x97\x3\x97\x3\x97"); + sb.Append("\x5\x97\x7F3\n\x97\x3\x98\x3\x98\x3\x98\x3\x98\x3\x98\x3\x98"); + sb.Append("\x3\x98\x5\x98\x7FC\n\x98\x3\x98\x3\x98\x3\x98\x3\x98\x5\x98"); + sb.Append("\x802\n\x98\x3\x98\x3\x98\x5\x98\x806\n\x98\x3\x98\x3\x98\x5"); + sb.Append("\x98\x80A\n\x98\x5\x98\x80C\n\x98\a\x98\x80E\n\x98\f\x98\xE"); + sb.Append("\x98\x811\v\x98\x3\x99\x3\x99\x3\x99\x3\x99\x3\x99\x5\x99\x818"); + sb.Append("\n\x99\x3\x99\x3\x99\x3\x99\x3\x99\x5\x99\x81E\n\x99\x3\x99"); + sb.Append("\x3\x99\x5\x99\x822\n\x99\x3\x99\x3\x99\x5\x99\x826\n\x99\x5"); + sb.Append("\x99\x828\n\x99\a\x99\x82A\n\x99\f\x99\xE\x99\x82D\v\x99\x3"); + sb.Append("\x99\x5\x99\x830\n\x99\x3\x99\x3\x99\x3\x99\x5\x99\x835\n\x99"); + sb.Append("\x3\x99\x3\x99\x3\x99\x3\x99\x3\x99\a\x99\x83C\n\x99\f\x99\xE"); + sb.Append("\x99\x83F\v\x99\x5\x99\x841\n\x99\x3\x99\x3\x99\x5\x99\x845"); + sb.Append("\n\x99\x3\x99\x3\x99\x3\x99\x3\x99\x3\x99\x3\x99\x3\x99\x3\x99"); + sb.Append("\x5\x99\x84F\n\x99\x3\x99\x3\x99\x5\x99\x853\n\x99\x5\x99\x855"); + sb.Append("\n\x99\x3\x9A\x3\x9A\x3\x9B\x3\x9B\x3\x9B\x3\x9B\x3\x9B\a\x9B"); + sb.Append("\x85E\n\x9B\f\x9B\xE\x9B\x861\v\x9B\x5\x9B\x863\n\x9B\x3\x9C"); + sb.Append("\x3\x9C\x3\x9C\a\x9C\x868\n\x9C\f\x9C\xE\x9C\x86B\v\x9C\x3\x9D"); + sb.Append("\x3\x9D\x3\x9D\a\x9D\x870\n\x9D\f\x9D\xE\x9D\x873\v\x9D\x3\x9E"); + sb.Append("\x3\x9E\x3\x9E\x3\x9E\x3\x9E\x3\x9E\x3\x9E\x3\x9E\x5\x9E\x87D"); + sb.Append("\n\x9E\x3\x9E\x3\x9E\x3\x9E\x3\x9E\x3\x9E\x3\x9E\x3\x9E\x3\x9E"); + sb.Append("\x3\x9E\x3\x9E\a\x9E\x889\n\x9E\f\x9E\xE\x9E\x88C\v\x9E\x3\x9E"); + sb.Append("\x3\x9E\x3\x9E\x3\x9E\x3\x9E\x3\x9E\x3\x9E\x3\x9E\a\x9E\x896"); + sb.Append("\n\x9E\f\x9E\xE\x9E\x899\v\x9E\x5\x9E\x89B\n\x9E\x3\x9E\x3\x9E"); + sb.Append("\x5\x9E\x89F\n\x9E\x3\x9E\x3\x9E\x3\x9E\x3\x9E\x3\x9E\a\x9E"); + sb.Append("\x8A6\n\x9E\f\x9E\xE\x9E\x8A9\v\x9E\x3\x9E\x3\x9E\x5\x9E\x8AD"); + sb.Append("\n\x9E\x3\x9E\x5\x9E\x8B0\n\x9E\x3\x9F\x3\x9F\x5\x9F\x8B4\n"); + sb.Append("\x9F\x3\xA0\x3\xA0\x3\xA0\x3\xA0\a\xA0\x8BA\n\xA0\f\xA0\xE\xA0"); + sb.Append("\x8BD\v\xA0\x3\xA1\x3\xA1\x3\xA1\x5\xA1\x8C2\n\xA1\x3\xA2\x3"); + sb.Append("\xA2\x5\xA2\x8C6\n\xA2\x3\xA3\x3\xA3\x3\xA4\x3\xA4\x3\xA4\x3"); + sb.Append("\xA5\x3\xA5\x3\xA5\x5\xA5\x8D0\n\xA5\x3\xA5\x3\xA5\x3\xA5\x3"); + sb.Append("\xA5\x3\xA5\x5\xA5\x8D7\n\xA5\x3\xA5\x3\xA5\x3\xA5\x5\xA5\x8DC"); + sb.Append("\n\xA5\x3\xA5\x3\xA5\x5\xA5\x8E0\n\xA5\x3\xA5\x3\xA5\x3\xA6"); + sb.Append("\x3\xA6\x5\xA6\x8E6\n\xA6\x3\xA6\x3\xA6\x3\xA6\x5\xA6\x8EB\n"); + sb.Append("\xA6\x3\xA6\x3\xA6\x5\xA6\x8EF\n\xA6\x3\xA7\x3\xA7\x3\xA7\x3"); + sb.Append("\xA7\a\xA7\x8F5\n\xA7\f\xA7\xE\xA7\x8F8\v\xA7\x5\xA7\x8FA\n"); + sb.Append("\xA7\x3\xA7\x3\xA7\x5\xA7\x8FE\n\xA7\x3\xA8\x3\xA8\x3\xA8\x5"); + sb.Append("\xA8\x903\n\xA8\x3\xA8\x3\xA8\x3\xA8\x3\xA8\x3\xA8\x3\xA8\x5"); + sb.Append("\xA8\x90B\n\xA8\x3\xA8\x3\xA8\x3\xA8\x3\xA8\x3\xA8\x3\xA8\x3"); + sb.Append("\xA8\x5\xA8\x914\n\xA8\x3\xA8\x3\xA8\x3\xA8\x3\xA8\x3\xA8\x3"); + sb.Append("\xA8\x5\xA8\x91C\n\xA8\x3\xA8\x3\xA8\x3\xA8\x3\xA8\x3\xA8\x3"); + sb.Append("\xA8\x5\xA8\x924\n\xA8\x3\xA8\x3\xA8\x3\xA8\x3\xA8\x3\xA8\x3"); + sb.Append("\xA8\x5\xA8\x92C\n\xA8\x3\xA8\x3\xA8\x3\xA8\x3\xA8\x3\xA8\x3"); + sb.Append("\xA8\x3\xA8\x3\xA8\x3\xA8\x3\xA8\x3\xA8\a\xA8\x939\n\xA8\f\xA8"); + sb.Append("\xE\xA8\x93C\v\xA8\x3\xA8\x3\xA8\x3\xA8\x3\xA8\x3\xA8\x3\xA8"); + sb.Append("\x3\xA8\x5\xA8\x945\n\xA8\x3\xA8\x3\xA8\x5\xA8\x949\n\xA8\x3"); + sb.Append("\xA8\x3\xA8\x3\xA8\x3\xA8\x3\xA8\x5\xA8\x950\n\xA8\x3\xA8\x3"); + sb.Append("\xA8\x5\xA8\x954\n\xA8\x3\xA8\x3\xA8\x3\xA8\x3\xA8\x3\xA8\x3"); + sb.Append("\xA8\x3\xA8\x3\xA8\x3\xA8\x3\xA8\x5\xA8\x960\n\xA8\x3\xA8\x3"); + sb.Append("\xA8\x3\xA8\x3\xA8\x3\xA8\x3\xA8\x3\xA8\x3\xA8\x3\xA8\x3\xA8"); + sb.Append("\x3\xA8\x3\xA8\x3\xA8\x3\xA8\x3\xA8\x3\xA8\x3\xA8\x3\xA8\x3"); + sb.Append("\xA8\x3\xA8\x3\xA8\x3\xA8\x3\xA8\x3\xA8\a\xA8\x97A\n\xA8\f\xA8"); + sb.Append("\xE\xA8\x97D\v\xA8\x3\xA8\x3\xA8\x3\xA8\x3\xA8\x3\xA8\x3\xA8"); + sb.Append("\x3\xA8\x3\xA8\x3\xA8\x3\xA8\x3\xA8\x3\xA8\x3\xA8\x3\xA8\x5"); + sb.Append("\xA8\x98D\n\xA8\x3\xA8\x3\xA8\x5\xA8\x991\n\xA8\x3\xA8\x3\xA8"); + sb.Append("\x3\xA8\x3\xA8\x3\xA8\x3\xA8\x3\xA8\x3\xA8\x5\xA8\x99B\n\xA8"); + sb.Append("\x3\xA8\x5\xA8\x99E\n\xA8\x3\xA8\x3\xA8\x3\xA8\x5\xA8\x9A3\n"); + sb.Append("\xA8\x3\xA9\x3\xA9\x3\xA9\x5\xA9\x9A8\n\xA9\x3\xA9\x3\xA9\x5"); + sb.Append("\xA9\x9AC\n\xA9\x3\xA9\x3\xA9\x5\xA9\x9B0\n\xA9\x3\xAA\x3\xAA"); + sb.Append("\x5\xAA\x9B4\n\xAA\x3\xAB\x3\xAB\x3\xAB\a\xAB\x9B9\n\xAB\f\xAB"); + sb.Append("\xE\xAB\x9BC\v\xAB\x3\xAC\x3\xAC\x3\xAC\x3\xAC\x3\xAC\x5\xAC"); + sb.Append("\x9C3\n\xAC\x3\xAC\x3\xAC\x5\xAC\x9C7\n\xAC\x3\xAC\x5\xAC\x9CA"); + sb.Append("\n\xAC\x3\xAD\x3\xAD\x3\xAD\x5\xAD\x9CF\n\xAD\x3\xAD\x5\xAD"); + sb.Append("\x9D2\n\xAD\x3\xAE\x3\xAE\x3\xAE\x5\xAE\x9D7\n\xAE\x3\xAF\x3"); + sb.Append("\xAF\x3\xAF\x3\xAF\x5\xAF\x9DD\n\xAF\x3\xB0\x3\xB0\x3\xB0\x3"); + sb.Append("\xB0\x3\xB0\x3\xB0\x3\xB0\x3\xB0\x3\xB0\x3\xB0\x5\xB0\x9E9\n"); + sb.Append("\xB0\x3\xB1\x5\xB1\x9EC\n\xB1\x3\xB1\x3\xB1\x3\xB1\a\xB1\x9F1"); + sb.Append("\n\xB1\f\xB1\xE\xB1\x9F4\v\xB1\x3\xB2\x5\xB2\x9F7\n\xB2\x3\xB2"); + sb.Append("\x3\xB2\x3\xB3\x3\xB3\x3\xB3\x3\xB3\x3\xB4\x3\xB4\x3\xB5\x3"); + sb.Append("\xB5\a\xB5\xA03\n\xB5\f\xB5\xE\xB5\xA06\v\xB5\x3\xB6\x3\xB6"); + sb.Append("\x3\xB6\x3\xB6\x3\xB6\x5\xB6\xA0D\n\xB6\x3\xB6\x3\xB6\x3\xB7"); + sb.Append("\x3\xB7\x3\xB7\a\xB7\xA14\n\xB7\f\xB7\xE\xB7\xA17\v\xB7\x3\xB8"); + sb.Append("\x3\xB8\x3\xB8\a\xB8\xA1C\n\xB8\f\xB8\xE\xB8\xA1F\v\xB8\x3\xB9"); + sb.Append("\x5\xB9\xA22\n\xB9\x3\xB9\x3\xB9\x3\xB9\x5\xB9\xA27\n\xB9\x3"); + sb.Append("\xBA\x3\xBA\x3\xBA\x3\xBA\x5\xBA\xA2D\n\xBA\x3\xBA\x5\xBA\xA30"); + sb.Append("\n\xBA\x5\xBA\xA32\n\xBA\x3\xBA\x3\xBA\x3\xBB\x3\xBB\x3\xBB"); + sb.Append("\x3\xBB\x3\xBB\x5\xBB\xA3B\n\xBB\x3\xBB\x3\xBB\x3\xBB\x3\xBB"); + sb.Append("\x5\xBB\xA41\n\xBB\x3\xBC\x3\xBC\x3\xBC\x3\xBC\a\xBC\xA47\n"); + sb.Append("\xBC\f\xBC\xE\xBC\xA4A\v\xBC\x3\xBC\x3\xBC\x3\xBD\x3\xBD\x3"); + sb.Append("\xBE\x3\xBE\x5\xBE\xA52\n\xBE\x3\xBF\x3\xBF\x3\xBF\x3\xBF\x5"); + sb.Append("\xBF\xA58\n\xBF\x3\xBF\x3\xBF\x5\xBF\xA5C\n\xBF\x3\xBF\x3\xBF"); + sb.Append("\x3\xC0\x3\xC0\x3\xC0\x3\xC0\x3\xC0\x5\xC0\xA65\n\xC0\x3\xC0"); + sb.Append("\x3\xC0\x3\xC1\x3\xC1\x3\xC1\x3\xC1\x3\xC2\x3\xC2\x3\xC2\x3"); + sb.Append("\xC2\x5\xC2\xA71\n\xC2\x5\xC2\xA73\n\xC2\x3\xC2\x3\xC2\x5\xC2"); + sb.Append("\xA77\n\xC2\x3\xC2\x3\xC2\x3\xC3\x3\xC3\x5\xC3\xA7D\n\xC3\x3"); + sb.Append("\xC3\x3\xC3\x3\xC3\x5\xC3\xA82\n\xC3\x3\xC3\x5\xC3\xA85\n\xC3"); + sb.Append("\x3\xC3\x5\xC3\xA88\n\xC3\x3\xC4\x3\xC4\a\xC4\xA8C\n\xC4\f\xC4"); + sb.Append("\xE\xC4\xA8F\v\xC4\x3\xC5\x3\xC5\x5\xC5\xA93\n\xC5\x3\xC5\x3"); + sb.Append("\xC5\x5\xC5\xA97\n\xC5\x3\xC5\x3\xC5\x5\xC5\xA9B\n\xC5\x3\xC5"); + sb.Append("\x3\xC5\x5\xC5\xA9F\n\xC5\x3\xC5\x3\xC5\x3\xC6\x3\xC6\x3\xC6"); + sb.Append("\x3\xC6\x3\xC7\x3\xC7\x3\xC7\a\xC7\xAAA\n\xC7\f\xC7\xE\xC7\xAAD"); + sb.Append("\v\xC7\x3\xC8\x3\xC8\x3\xC8\x3\xC8\x3\xC8\x5\xC8\xAB4\n\xC8"); + sb.Append("\x5\xC8\xAB6\n\xC8\x3\xC9\x3\xC9\x3\xC9\x3\xC9\x3\xC9\x5\xC9"); + sb.Append("\xABD\n\xC9\x3\xCA\x3\xCA\x3\xCA\x3\xCA\x3\xCA\x3\xCA\x3\xCB"); + sb.Append("\x3\xCB\x5\xCB\xAC7\n\xCB\x3\xCB\x3\xCB\x3\xCB\x5\xCB\xACC\n"); + sb.Append("\xCB\x3\xCB\x5\xCB\xACF\n\xCB\x3\xCB\x5\xCB\xAD2\n\xCB\x3\xCB"); + sb.Append("\x5\xCB\xAD5\n\xCB\x3\xCC\x3\xCC\x3\xCC\x3\xCC\x3\xCC\x3\xCC"); + sb.Append("\x5\xCC\xADD\n\xCC\x3\xCD\x3\xCD\x3\xCD\a\xCD\xAE2\n\xCD\f\xCD"); + sb.Append("\xE\xCD\xAE5\v\xCD\x3\xCE\x5\xCE\xAE8\n\xCE\x3\xCE\x3\xCE\x3"); + sb.Append("\xCE\a\xCE\xAED\n\xCE\f\xCE\xE\xCE\xAF0\v\xCE\x3\xCF\x3\xCF"); + sb.Append("\x3\xCF\a\xCF\xAF5\n\xCF\f\xCF\xE\xCF\xAF8\v\xCF\x3\xD0\x3\xD0"); + sb.Append("\x3\xD0\a\xD0\xAFD\n\xD0\f\xD0\xE\xD0\xB00\v\xD0\x3\xD1\x3\xD1"); + sb.Append("\x5\xD1\xB04\n\xD1\x3\xD2\x3\xD2\x5\xD2\xB08\n\xD2\x3\xD3\x3"); + sb.Append("\xD3\x3\xD3\x3\xD3\x3\xD3\x5\xD3\xB0F\n\xD3\x3\xD3\x5\xD3\xB12"); + sb.Append("\n\xD3\x3\xD4\x3\xD4\x3\xD4\x3\xD4\x3\xD4\x5\xD4\xB19\n\xD4"); + sb.Append("\x3\xD4\x5\xD4\xB1C\n\xD4\x3\xD5\x3\xD5\x3\xD5\a\xD5\xB21\n"); + sb.Append("\xD5\f\xD5\xE\xD5\xB24\v\xD5\x3\xD6\x3\xD6\x3\xD6\a\xD6\xB29"); + sb.Append("\n\xD6\f\xD6\xE\xD6\xB2C\v\xD6\x3\xD7\x3\xD7\x3\xD7\x3\xD7\x3"); + sb.Append("\xD7\x3\xD7\x3\xD7\x3\xD7\x3\xD7\x3\xD7\x5\xD7\xB38\n\xD7\x3"); + sb.Append("\xD8\x3\xD8\x5\xD8\xB3C\n\xD8\x3\xD9\x3\xD9\x3\xD9\x3\xD9\x3"); + sb.Append("\xD9\x3\xD9\x5\xD9\xB44\n\xD9\x3\xDA\x3\xDA\x3\xDB\x3\xDB\x3"); + sb.Append("\xDC\x3\xDC\x3\xDC\x3\xDC\x3\xDC\x5\xDC\xB4F\n\xDC\x3\xDD\x3"); + sb.Append("\xDD\x3\xDD\x5\xDD\xB54\n\xDD\x3\xDD\x3\xDD\x3\xDD\x3\xDD\x5"); + sb.Append("\xDD\xB5A\n\xDD\x3\xDE\x3\xDE\x3\xDE\x5\xDE\xB5F\n\xDE\x3\xDE"); + sb.Append("\x3\xDE\x3\xDF\x3\xDF\x3\xDF\x5\xDF\xB66\n\xDF\x3\xDF\x3\xDF"); + sb.Append("\x3\xE0\x3\xE0\x3\xE0\x3\xE0\a\xE0\xB6E\n\xE0\f\xE0\xE\xE0\xB71"); + sb.Append("\v\xE0\x3\xE0\x3\xE0\x3\xE1\x3\xE1\x3\xE1\x5\xE1\xB78\n\xE1"); + sb.Append("\x3\xE2\x3\xE2\x3\xE2\a\xE2\xB7D\n\xE2\f\xE2\xE\xE2\xB80\v\xE2"); + sb.Append("\x3\xE3\x3\xE3\x3\xE3\x3\xE3\x3\xE3\x5\xE3\xB87\n\xE3\x3\xE3"); + sb.Append("\x3\xE3\x3\xE3\x5\xE3\xB8C\n\xE3\x3\xE3\x3\xE3\x5\xE3\xB90\n"); + sb.Append("\xE3\x3\xE3\x5\xE3\xB93\n\xE3\x3\xE4\x3\xE4\x3\xE4\x3\xE4\x5"); + sb.Append("\xE4\xB99\n\xE4\a\xE4\xB9B\n\xE4\f\xE4\xE\xE4\xB9E\v\xE4\x3"); + sb.Append("\xE5\x3\xE5\x3\xE5\x3\xE5\x3\xE5\x3\xE5\x3\xE5\x3\xE5\x3\xE5"); + sb.Append("\x3\xE5\x3\xE5\x3\xE5\x3\xE5\x3\xE5\x3\xE5\x3\xE5\x3\xE5\x3"); + sb.Append("\xE5\x3\xE5\x3\xE5\x3\xE5\x3\xE5\x3\xE5\x3\xE5\x3\xE5\x3\xE5"); + sb.Append("\x3\xE5\x3\xE5\x3\xE5\x3\xE5\x3\xE5\x3\xE5\x3\xE5\x3\xE5\x3"); + sb.Append("\xE5\x3\xE5\x3\xE5\x3\xE5\x3\xE5\x3\xE5\x3\xE5\x3\xE5\x3\xE5"); + sb.Append("\x3\xE5\x3\xE5\x3\xE5\x3\xE5\x3\xE5\x3\xE5\x3\xE5\x3\xE5\x3"); + sb.Append("\xE5\x5\xE5\xBD4\n\xE5\x3\xE6\x3\xE6\x3\xE6\x5\xE6\xBD9\n\xE6"); + sb.Append("\x3\xE7\x3\xE7\x5\xE7\xBDD\n\xE7\x3\xE8\x3\xE8\x5\xE8\xBE1\n"); + sb.Append("\xE8\x3\xE8\x5\xE8\xBE4\n\xE8\x3\xE8\x5\xE8\xBE7\n\xE8\x3\xE8"); + sb.Append("\x5\xE8\xBEA\n\xE8\x3\xE8\x5\xE8\xBED\n\xE8\x3\xE8\x5\xE8\xBF0"); + sb.Append("\n\xE8\x3\xE8\x5\xE8\xBF3\n\xE8\x3\xE8\x5\xE8\xBF6\n\xE8\x3"); + sb.Append("\xE8\x3\xE8\x5\xE8\xBFA\n\xE8\x3\xE8\x5\xE8\xBFD\n\xE8\x3\xE8"); + sb.Append("\x5\xE8\xC00\n\xE8\x3\xE8\x5\xE8\xC03\n\xE8\x3\xE8\x5\xE8\xC06"); + sb.Append("\n\xE8\x3\xE8\x5\xE8\xC09\n\xE8\x3\xE8\x5\xE8\xC0C\n\xE8\x3"); + sb.Append("\xE8\x3\xE8\x5\xE8\xC10\n\xE8\x3\xE8\x5\xE8\xC13\n\xE8\x3\xE8"); + sb.Append("\x5\xE8\xC16\n\xE8\x3\xE8\x5\xE8\xC19\n\xE8\x3\xE8\x5\xE8\xC1C"); + sb.Append("\n\xE8\x3\xE8\x5\xE8\xC1F\n\xE8\x3\xE8\x3\xE8\x5\xE8\xC23\n"); + sb.Append("\xE8\x3\xE8\x5\xE8\xC26\n\xE8\x3\xE8\x5\xE8\xC29\n\xE8\x3\xE8"); + sb.Append("\x5\xE8\xC2C\n\xE8\x3\xE8\x5\xE8\xC2F\n\xE8\x3\xE8\x3\xE8\x5"); + sb.Append("\xE8\xC33\n\xE8\x3\xE8\x5\xE8\xC36\n\xE8\x3\xE8\x5\xE8\xC39"); + sb.Append("\n\xE8\x3\xE8\x5\xE8\xC3C\n\xE8\x3\xE8\x3\xE8\x5\xE8\xC40\n"); + sb.Append("\xE8\x3\xE8\x5\xE8\xC43\n\xE8\x3\xE8\x5\xE8\xC46\n\xE8\x3\xE8"); + sb.Append("\x3\xE8\x5\xE8\xC4A\n\xE8\x3\xE8\x5\xE8\xC4D\n\xE8\x3\xE8\x3"); + sb.Append("\xE8\x5\xE8\xC51\n\xE8\x3\xE8\x5\xE8\xC54\n\xE8\x3\xE9\x3\xE9"); + sb.Append("\x3\xE9\x5\xE9\xC59\n\xE9\x3\xE9\x3\xE9\x3\xEA\x3\xEA\x3\xEA"); + sb.Append("\x5\xEA\xC60\n\xEA\x3\xEA\x3\xEA\x3\xEB\x3\xEB\x3\xEB\x5\xEB"); + sb.Append("\xC67\n\xEB\x3\xEB\x3\xEB\x3\xEC\x3\xEC\x3\xEC\x5\xEC\xC6E\n"); + sb.Append("\xEC\x3\xEC\x3\xEC\x3\xED\x3\xED\x3\xED\x5\xED\xC75\n\xED\x3"); + sb.Append("\xED\x3\xED\x3\xEE\x3\xEE\x3\xEE\x5\xEE\xC7C\n\xEE\x3\xEE\x3"); + sb.Append("\xEE\x3\xEF\x3\xEF\x3\xEF\x5\xEF\xC83\n\xEF\x3\xEF\x3\xEF\x3"); + sb.Append("\xF0\x3\xF0\x3\xF0\x5\xF0\xC8A\n\xF0\x3\xF0\x3\xF0\x3\xF1\x3"); + sb.Append("\xF1\x3\xF1\x5\xF1\xC91\n\xF1\x3\xF1\x3\xF1\x3\xF2\x3\xF2\x3"); + sb.Append("\xF3\x3\xF3\x3\xF3\x5\xF3\xC9A\n\xF3\x3\xF4\x3\xF4\x3\xF4\x3"); + sb.Append("\xF4\x3\xF4\x5\xF4\xCA1\n\xF4\x3\xF5\x3\xF5\x5\xF5\xCA5\n\xF5"); + sb.Append("\x3\xF5\x3\xF5\x3\xF6\x3\xF6\x5\xF6\xCAB\n\xF6\x3\xF7\x3\xF7"); + sb.Append("\x3\xF7\x5\xF7\xCB0\n\xF7\x3\xF8\x3\xF8\x3\xF8\x3\xF8\x3\xF9"); + sb.Append("\x3\xF9\x5\xF9\xCB8\n\xF9\x3\xF9\x3\xF9\x3\xFA\x3\xFA\x3\xFA"); + sb.Append("\a\xFA\xCBF\n\xFA\f\xFA\xE\xFA\xCC2\v\xFA\x3\xFA\x5\xFA\xCC5"); + sb.Append("\n\xFA\x3\xFB\x3\xFB\x3\xFB\a\xFB\xCCA\n\xFB\f\xFB\xE\xFB\xCCD"); + sb.Append("\v\xFB\x3\xFB\x5\xFB\xCD0\n\xFB\x3\xFC\x3\xFC\x5\xFC\xCD4\n"); + sb.Append("\xFC\x3\xFC\x3\xFC\x3\xFC\x3\xFC\x2\x2\xFD\x2\x4\x6\b\n\f\xE"); + sb.Append("\x10\x12\x14\x16\x18\x1A\x1C\x1E \"$&(*,.\x30\x32\x34\x36\x38"); + sb.Append(":<>@\x42\x44\x46HJLNPRTVXZ\\^`\x62\x64\x66hjlnprtvxz|~\x80\x82"); + sb.Append("\x84\x86\x88\x8A\x8C\x8E\x90\x92\x94\x96\x98\x9A\x9C\x9E\xA0"); + sb.Append("\xA2\xA4\xA6\xA8\xAA\xAC\xAE\xB0\xB2\xB4\xB6\xB8\xBA\xBC\xBE"); + sb.Append("\xC0\xC2\xC4\xC6\xC8\xCA\xCC\xCE\xD0\xD2\xD4\xD6\xD8\xDA\xDC"); + sb.Append("\xDE\xE0\xE2\xE4\xE6\xE8\xEA\xEC\xEE\xF0\xF2\xF4\xF6\xF8\xFA"); + sb.Append("\xFC\xFE\x100\x102\x104\x106\x108\x10A\x10C\x10E\x110\x112\x114"); + sb.Append("\x116\x118\x11A\x11C\x11E\x120\x122\x124\x126\x128\x12A\x12C"); + sb.Append("\x12E\x130\x132\x134\x136\x138\x13A\x13C\x13E\x140\x142\x144"); + sb.Append("\x146\x148\x14A\x14C\x14E\x150\x152\x154\x156\x158\x15A\x15C"); + sb.Append("\x15E\x160\x162\x164\x166\x168\x16A\x16C\x16E\x170\x172\x174"); + sb.Append("\x176\x178\x17A\x17C\x17E\x180\x182\x184\x186\x188\x18A\x18C"); + sb.Append("\x18E\x190\x192\x194\x196\x198\x19A\x19C\x19E\x1A0\x1A2\x1A4"); + sb.Append("\x1A6\x1A8\x1AA\x1AC\x1AE\x1B0\x1B2\x1B4\x1B6\x1B8\x1BA\x1BC"); + sb.Append("\x1BE\x1C0\x1C2\x1C4\x1C6\x1C8\x1CA\x1CC\x1CE\x1D0\x1D2\x1D4"); + sb.Append("\x1D6\x1D8\x1DA\x1DC\x1DE\x1E0\x1E2\x1E4\x1E6\x1E8\x1EA\x1EC"); + sb.Append("\x1EE\x1F0\x1F2\x1F4\x1F6\x2\x15\x3\x2\x8D\x8E\x4\x2\x8F\x8F"); + sb.Append("\x98\x98\x4\x2\v\v\x99\x99\x3\x2\x86\x87\x5\x2\xAE\xAE\xB0\xB0"); + sb.Append("\xB3\xB3\x4\x2\xA0\xA0\xA3\xA3\x5\x2\x9E\x9E\xA6\xA6\xA8\xA8"); + sb.Append("\x3\x2,-\x4\x2\x10\x10\x99\x99\x3\x2XY\x3\x2Z[\x3\x2\\]\x3\x2"); + sb.Append("^_\x3\x2`\x61\x4\x2\x14\x14\x62\x63\x3\x2\x64\x66\x3\x2gi\x3"); + sb.Append("\x2jl\x3\x2\xC6\xC7\xE83\x2\x1FC\x3\x2\x2\x2\x4\x206\x3\x2\x2"); + sb.Append("\x2\x6\x20C\x3\x2\x2\x2\b\x20F\x3\x2\x2\x2\n\x212\x3\x2\x2\x2"); + sb.Append("\f\x22E\x3\x2\x2\x2\xE\x23C\x3\x2\x2\x2\x10\x243\x3\x2\x2\x2"); + sb.Append("\x12\x247\x3\x2\x2\x2\x14\x24D\x3\x2\x2\x2\x16\x257\x3\x2\x2"); + sb.Append("\x2\x18\x25F\x3\x2\x2\x2\x1A\x268\x3\x2\x2\x2\x1C\x26A\x3\x2"); + sb.Append("\x2\x2\x1E\x27B\x3\x2\x2\x2 \x290\x3\x2\x2\x2\"\x295\x3\x2\x2"); + sb.Append("\x2$\x2BE\x3\x2\x2\x2&\x2D3\x3\x2\x2\x2(\x2DA\x3\x2\x2\x2*\x2DE"); + sb.Append("\x3\x2\x2\x2,\x2EA\x3\x2\x2\x2.\x2FF\x3\x2\x2\x2\x30\x301\x3"); + sb.Append("\x2\x2\x2\x32\x30C\x3\x2\x2\x2\x34\x31D\x3\x2\x2\x2\x36\x329"); + sb.Append("\x3\x2\x2\x2\x38\x32C\x3\x2\x2\x2:\x33F\x3\x2\x2\x2<\x365\x3"); + sb.Append("\x2\x2\x2>\x372\x3\x2\x2\x2@\x37D\x3\x2\x2\x2\x42\x384\x3\x2"); + sb.Append("\x2\x2\x44\x389\x3\x2\x2\x2\x46\x38F\x3\x2\x2\x2H\x392\x3\x2"); + sb.Append("\x2\x2J\x39F\x3\x2\x2\x2L\x3A1\x3\x2\x2\x2N\x3A8\x3\x2\x2\x2"); + sb.Append("P\x3C7\x3\x2\x2\x2R\x3CB\x3\x2\x2\x2T\x3D7\x3\x2\x2\x2V\x3DF"); + sb.Append("\x3\x2\x2\x2X\x3E3\x3\x2\x2\x2Z\x3F5\x3\x2\x2\x2\\\x3FF\x3\x2"); + sb.Append("\x2\x2^\x407\x3\x2\x2\x2`\x41A\x3\x2\x2\x2\x62\x422\x3\x2\x2"); + sb.Append("\x2\x64\x42A\x3\x2\x2\x2\x66\x436\x3\x2\x2\x2h\x448\x3\x2\x2"); + sb.Append("\x2j\x44A\x3\x2\x2\x2l\x450\x3\x2\x2\x2n\x463\x3\x2\x2\x2p\x46F"); + sb.Append("\x3\x2\x2\x2r\x472\x3\x2\x2\x2t\x479\x3\x2\x2\x2v\x481\x3\x2"); + sb.Append("\x2\x2x\x4A3\x3\x2\x2\x2z\x4A5\x3\x2\x2\x2|\x4A9\x3\x2\x2\x2"); + sb.Append("~\x4B3\x3\x2\x2\x2\x80\x4B8\x3\x2\x2\x2\x82\x4BF\x3\x2\x2\x2"); + sb.Append("\x84\x4C2\x3\x2\x2\x2\x86\x4CB\x3\x2\x2\x2\x88\x4CF\x3\x2\x2"); + sb.Append("\x2\x8A\x4DC\x3\x2\x2\x2\x8C\x4DE\x3\x2\x2\x2\x8E\x4E2\x3\x2"); + sb.Append("\x2\x2\x90\x4F7\x3\x2\x2\x2\x92\x4F9\x3\x2\x2\x2\x94\x501\x3"); + sb.Append("\x2\x2\x2\x96\x50F\x3\x2\x2\x2\x98\x511\x3\x2\x2\x2\x9A\x55E"); + sb.Append("\x3\x2\x2\x2\x9C\x560\x3\x2\x2\x2\x9E\x570\x3\x2\x2\x2\xA0\x572"); + sb.Append("\x3\x2\x2\x2\xA2\x579\x3\x2\x2\x2\xA4\x584\x3\x2\x2\x2\xA6\x588"); + sb.Append("\x3\x2\x2\x2\xA8\x590\x3\x2\x2\x2\xAA\x593\x3\x2\x2\x2\xAC\x59D"); + sb.Append("\x3\x2\x2\x2\xAE\x59F\x3\x2\x2\x2\xB0\x5A5\x3\x2\x2\x2\xB2\x5B0"); + sb.Append("\x3\x2\x2\x2\xB4\x5B8\x3\x2\x2\x2\xB6\x5C1\x3\x2\x2\x2\xB8\x5C4"); + sb.Append("\x3\x2\x2\x2\xBA\x5D4\x3\x2\x2\x2\xBC\x5DB\x3\x2\x2\x2\xBE\x5E4"); + sb.Append("\x3\x2\x2\x2\xC0\x5E8\x3\x2\x2\x2\xC2\x5ED\x3\x2\x2\x2\xC4\x5F4"); + sb.Append("\x3\x2\x2\x2\xC6\x5FF\x3\x2\x2\x2\xC8\x601\x3\x2\x2\x2\xCA\x60B"); + sb.Append("\x3\x2\x2\x2\xCC\x60E\x3\x2\x2\x2\xCE\x619\x3\x2\x2\x2\xD0\x62A"); + sb.Append("\x3\x2\x2\x2\xD2\x633\x3\x2\x2\x2\xD4\x63E\x3\x2\x2\x2\xD6\x64F"); + sb.Append("\x3\x2\x2\x2\xD8\x66E\x3\x2\x2\x2\xDA\x670\x3\x2\x2\x2\xDC\x676"); + sb.Append("\x3\x2\x2\x2\xDE\x67C\x3\x2\x2\x2\xE0\x685\x3\x2\x2\x2\xE2\x690"); + sb.Append("\x3\x2\x2\x2\xE4\x692\x3\x2\x2\x2\xE6\x69E\x3\x2\x2\x2\xE8\x6AD"); + sb.Append("\x3\x2\x2\x2\xEA\x6BC\x3\x2\x2\x2\xEC\x6BE\x3\x2\x2\x2\xEE\x6C6"); + sb.Append("\x3\x2\x2\x2\xF0\x6CB\x3\x2\x2\x2\xF2\x6CE\x3\x2\x2\x2\xF4\x6F7"); + sb.Append("\x3\x2\x2\x2\xF6\x702\x3\x2\x2\x2\xF8\x70B\x3\x2\x2\x2\xFA\x717"); + sb.Append("\x3\x2\x2\x2\xFC\x71B\x3\x2\x2\x2\xFE\x720\x3\x2\x2\x2\x100"); + sb.Append("\x723\x3\x2\x2\x2\x102\x738\x3\x2\x2\x2\x104\x742\x3\x2\x2\x2"); + sb.Append("\x106\x74B\x3\x2\x2\x2\x108\x752\x3\x2\x2\x2\x10A\x755\x3\x2"); + sb.Append("\x2\x2\x10C\x75A\x3\x2\x2\x2\x10E\x761\x3\x2\x2\x2\x110\x767"); + sb.Append("\x3\x2\x2\x2\x112\x770\x3\x2\x2\x2\x114\x777\x3\x2\x2\x2\x116"); + sb.Append("\x779\x3\x2\x2\x2\x118\x784\x3\x2\x2\x2\x11A\x790\x3\x2\x2\x2"); + sb.Append("\x11C\x79E\x3\x2\x2\x2\x11E\x7AA\x3\x2\x2\x2\x120\x7B3\x3\x2"); + sb.Append("\x2\x2\x122\x7B7\x3\x2\x2\x2\x124\x7D5\x3\x2\x2\x2\x126\x7D7"); + sb.Append("\x3\x2\x2\x2\x128\x7DF\x3\x2\x2\x2\x12A\x7E7\x3\x2\x2\x2\x12C"); + sb.Append("\x7F2\x3\x2\x2\x2\x12E\x7F4\x3\x2\x2\x2\x130\x812\x3\x2\x2\x2"); + sb.Append("\x132\x856\x3\x2\x2\x2\x134\x858\x3\x2\x2\x2\x136\x864\x3\x2"); + sb.Append("\x2\x2\x138\x86C\x3\x2\x2\x2\x13A\x8AF\x3\x2\x2\x2\x13C\x8B1"); + sb.Append("\x3\x2\x2\x2\x13E\x8B5\x3\x2\x2\x2\x140\x8BE\x3\x2\x2\x2\x142"); + sb.Append("\x8C3\x3\x2\x2\x2\x144\x8C7\x3\x2\x2\x2\x146\x8C9\x3\x2\x2\x2"); + sb.Append("\x148\x8CC\x3\x2\x2\x2\x14A\x8E3\x3\x2\x2\x2\x14C\x8F0\x3\x2"); + sb.Append("\x2\x2\x14E\x9A2\x3\x2\x2\x2\x150\x9A7\x3\x2\x2\x2\x152\x9B3"); + sb.Append("\x3\x2\x2\x2\x154\x9B5\x3\x2\x2\x2\x156\x9C2\x3\x2\x2\x2\x158"); + sb.Append("\x9CB\x3\x2\x2\x2\x15A\x9D6\x3\x2\x2\x2\x15C\x9DC\x3\x2\x2\x2"); + sb.Append("\x15E\x9E8\x3\x2\x2\x2\x160\x9EB\x3\x2\x2\x2\x162\x9F6\x3\x2"); + sb.Append("\x2\x2\x164\x9FA\x3\x2\x2\x2\x166\x9FE\x3\x2\x2\x2\x168\xA00"); + sb.Append("\x3\x2\x2\x2\x16A\xA0C\x3\x2\x2\x2\x16C\xA10\x3\x2\x2\x2\x16E"); + sb.Append("\xA18\x3\x2\x2\x2\x170\xA21\x3\x2\x2\x2\x172\xA31\x3\x2\x2\x2"); + sb.Append("\x174\xA3A\x3\x2\x2\x2\x176\xA42\x3\x2\x2\x2\x178\xA4D\x3\x2"); + sb.Append("\x2\x2\x17A\xA51\x3\x2\x2\x2\x17C\xA53\x3\x2\x2\x2\x17E\xA5F"); + sb.Append("\x3\x2\x2\x2\x180\xA68\x3\x2\x2\x2\x182\xA6C\x3\x2\x2\x2\x184"); + sb.Append("\xA7C\x3\x2\x2\x2\x186\xA89\x3\x2\x2\x2\x188\xA90\x3\x2\x2\x2"); + sb.Append("\x18A\xAA2\x3\x2\x2\x2\x18C\xAA6\x3\x2\x2\x2\x18E\xAB5\x3\x2"); + sb.Append("\x2\x2\x190\xAB7\x3\x2\x2\x2\x192\xABE\x3\x2\x2\x2\x194\xAC6"); + sb.Append("\x3\x2\x2\x2\x196\xAD6\x3\x2\x2\x2\x198\xADE\x3\x2\x2\x2\x19A"); + sb.Append("\xAE7\x3\x2\x2\x2\x19C\xAF1\x3\x2\x2\x2\x19E\xAF9\x3\x2\x2\x2"); + sb.Append("\x1A0\xB03\x3\x2\x2\x2\x1A2\xB07\x3\x2\x2\x2\x1A4\xB09\x3\x2"); + sb.Append("\x2\x2\x1A6\xB13\x3\x2\x2\x2\x1A8\xB1D\x3\x2\x2\x2\x1AA\xB25"); + sb.Append("\x3\x2\x2\x2\x1AC\xB37\x3\x2\x2\x2\x1AE\xB3B\x3\x2\x2\x2\x1B0"); + sb.Append("\xB3D\x3\x2\x2\x2\x1B2\xB45\x3\x2\x2\x2\x1B4\xB47\x3\x2\x2\x2"); + sb.Append("\x1B6\xB49\x3\x2\x2\x2\x1B8\xB53\x3\x2\x2\x2\x1BA\xB5E\x3\x2"); + sb.Append("\x2\x2\x1BC\xB65\x3\x2\x2\x2\x1BE\xB69\x3\x2\x2\x2\x1C0\xB77"); + sb.Append("\x3\x2\x2\x2\x1C2\xB79\x3\x2\x2\x2\x1C4\xB81\x3\x2\x2\x2\x1C6"); + sb.Append("\xB94\x3\x2\x2\x2\x1C8\xBD3\x3\x2\x2\x2\x1CA\xBD8\x3\x2\x2\x2"); + sb.Append("\x1CC\xBDC\x3\x2\x2\x2\x1CE\xC53\x3\x2\x2\x2\x1D0\xC58\x3\x2"); + sb.Append("\x2\x2\x1D2\xC5F\x3\x2\x2\x2\x1D4\xC66\x3\x2\x2\x2\x1D6\xC6D"); + sb.Append("\x3\x2\x2\x2\x1D8\xC74\x3\x2\x2\x2\x1DA\xC7B\x3\x2\x2\x2\x1DC"); + sb.Append("\xC82\x3\x2\x2\x2\x1DE\xC89\x3\x2\x2\x2\x1E0\xC90\x3\x2\x2\x2"); + sb.Append("\x1E2\xC94\x3\x2\x2\x2\x1E4\xC96\x3\x2\x2\x2\x1E6\xCA0\x3\x2"); + sb.Append("\x2\x2\x1E8\xCA4\x3\x2\x2\x2\x1EA\xCAA\x3\x2\x2\x2\x1EC\xCAF"); + sb.Append("\x3\x2\x2\x2\x1EE\xCB1\x3\x2\x2\x2\x1F0\xCB5\x3\x2\x2\x2\x1F2"); + sb.Append("\xCBB\x3\x2\x2\x2\x1F4\xCC6\x3\x2\x2\x2\x1F6\xCD3\x3\x2\x2\x2"); + sb.Append("\x1F8\x1FB\x5\x14\v\x2\x1F9\x1FB\x5\n\x6\x2\x1FA\x1F8\x3\x2"); + sb.Append("\x2\x2\x1FA\x1F9\x3\x2\x2\x2\x1FB\x1FE\x3\x2\x2\x2\x1FC\x1FA"); + sb.Append("\x3\x2\x2\x2\x1FC\x1FD\x3\x2\x2\x2\x1FD\x1FF\x3\x2\x2\x2\x1FE"); + sb.Append("\x1FC\x3\x2\x2\x2\x1FF\x200\x5\x166\xB4\x2\x200\x201\a\x2\x2"); + sb.Append("\x3\x201\x3\x3\x2\x2\x2\x202\x205\x5\x14\v\x2\x203\x205\x5\n"); + sb.Append("\x6\x2\x204\x202\x3\x2\x2\x2\x204\x203\x3\x2\x2\x2\x205\x208"); + sb.Append("\x3\x2\x2\x2\x206\x204\x3\x2\x2\x2\x206\x207\x3\x2\x2\x2\x207"); + sb.Append("\x209\x3\x2\x2\x2\x208\x206\x3\x2\x2\x2\x209\x20A\x5\x1E\x10"); + sb.Append("\x2\x20A\x20B\a\x2\x2\x3\x20B\x5\x3\x2\x2\x2\x20C\x20D\x5\x1C2"); + sb.Append("\xE2\x2\x20D\x20E\a\x2\x2\x3\x20E\a\x3\x2\x2\x2\x20F\x210\x5"); + sb.Append("\x1EC\xF7\x2\x210\x211\a\x2\x2\x3\x211\t\x3\x2\x2\x2\x212\x214"); + sb.Append("\a\x7F\x2\x2\x213\x215\x5\x198\xCD\x2\x214\x213\x3\x2\x2\x2"); + sb.Append("\x214\x215\x3\x2\x2\x2\x215\x218\x3\x2\x2\x2\x216\x217\a\x94"); + sb.Append("\x2\x2\x217\x219\a\x95\x2\x2\x218\x216\x3\x2\x2\x2\x218\x219"); + sb.Append("\x3\x2\x2\x2\x219\x21B\x3\x2\x2\x2\x21A\x21C\x5\x192\xCA\x2"); + sb.Append("\x21B\x21A\x3\x2\x2\x2\x21B\x21C\x3\x2\x2\x2\x21C\x21E\x3\x2"); + sb.Append("\x2\x2\x21D\x21F\x5\f\a\x2\x21E\x21D\x3\x2\x2\x2\x21E\x21F\x3"); + sb.Append("\x2\x2\x2\x21F\x220\x3\x2\x2\x2\x220\x226\a\xC5\x2\x2\x221\x223"); + sb.Append("\a\x92\x2\x2\x222\x224\x5\xB2Z\x2\x223\x222\x3\x2\x2\x2\x223"); + sb.Append("\x224\x3\x2\x2\x2\x224\x225\x3\x2\x2\x2\x225\x227\a\x93\x2\x2"); + sb.Append("\x226\x221\x3\x2\x2\x2\x226\x227\x3\x2\x2\x2\x227\x22A\x3\x2"); + sb.Append("\x2\x2\x228\x229\a\xC5\x2\x2\x229\x22B\az\x2\x2\x22A\x228\x3"); + sb.Append("\x2\x2\x2\x22A\x22B\x3\x2\x2\x2\x22B\x22C\x3\x2\x2\x2\x22C\x22D"); + sb.Append("\x5\xE\b\x2\x22D\v\x3\x2\x2\x2\x22E\x22F\a\xC5\x2\x2\x22F\x230"); + sb.Append("\a\x98\x2\x2\x230\r\x3\x2\x2\x2\x231\x233\a\x96\x2\x2\x232\x234"); + sb.Append("\x5\x10\t\x2\x233\x232\x3\x2\x2\x2\x233\x234\x3\x2\x2\x2\x234"); + sb.Append("\x235\x3\x2\x2\x2\x235\x236\x5\x122\x92\x2\x236\x237\a\x97\x2"); + sb.Append("\x2\x237\x23D\x3\x2\x2\x2\x238\x239\a\x94\x2\x2\x239\x23A\x5"); + sb.Append("\x1EA\xF6\x2\x23A\x23B\a\x95\x2\x2\x23B\x23D\x3\x2\x2\x2\x23C"); + sb.Append("\x231\x3\x2\x2\x2\x23C\x238\x3\x2\x2\x2\x23D\xF\x3\x2\x2\x2"); + sb.Append("\x23E\x244\a\xC5\x2\x2\x23F\x240\a\x92\x2\x2\x240\x241\x5\xB2"); + sb.Append("Z\x2\x241\x242\a\x93\x2\x2\x242\x244\x3\x2\x2\x2\x243\x23E\x3"); + sb.Append("\x2\x2\x2\x243\x23F\x3\x2\x2\x2\x244\x245\x3\x2\x2\x2\x245\x246"); + sb.Append("\t\x2\x2\x2\x246\x11\x3\x2\x2\x2\x247\x248\a\xBD\x2\x2\x248"); + sb.Append("\x249\a\xC5\x2\x2\x249\x24A\a\x92\x2\x2\x24A\x24B\a\xC5\x2\x2"); + sb.Append("\x24B\x24C\a\x93\x2\x2\x24C\x13\x3\x2\x2\x2\x24D\x24E\a\xBD"); + sb.Append("\x2\x2\x24E\x255\x5\x198\xCD\x2\x24F\x252\a\x92\x2\x2\x250\x253"); + sb.Append("\x5\x16\f\x2\x251\x253\x5\x1A\xE\x2\x252\x250\x3\x2\x2\x2\x252"); + sb.Append("\x251\x3\x2\x2\x2\x252\x253\x3\x2\x2\x2\x253\x254\x3\x2\x2\x2"); + sb.Append("\x254\x256\a\x93\x2\x2\x255\x24F\x3\x2\x2\x2\x255\x256\x3\x2"); + sb.Append("\x2\x2\x256\x15\x3\x2\x2\x2\x257\x25C\x5\x18\r\x2\x258\x259"); + sb.Append("\a\x99\x2\x2\x259\x25B\x5\x18\r\x2\x25A\x258\x3\x2\x2\x2\x25B"); + sb.Append("\x25E\x3\x2\x2\x2\x25C\x25A\x3\x2\x2\x2\x25C\x25D\x3\x2\x2\x2"); + sb.Append("\x25D\x17\x3\x2\x2\x2\x25E\x25C\x3\x2\x2\x2\x25F\x260\x5\x1C8"); + sb.Append("\xE5\x2\x260\x261\a\x8F\x2\x2\x261\x262\x5\x1A\xE\x2\x262\x19"); + sb.Append("\x3\x2\x2\x2\x263\x269\x5\x14\v\x2\x264\x269\x5\x1C\xF\x2\x265"); + sb.Append("\x269\x5\x1E6\xF4\x2\x266\x269\a\xC5\x2\x2\x267\x269\x5\x198"); + sb.Append("\xCD\x2\x268\x263\x3\x2\x2\x2\x268\x264\x3\x2\x2\x2\x268\x265"); + sb.Append("\x3\x2\x2\x2\x268\x266\x3\x2\x2\x2\x268\x267\x3\x2\x2\x2\x269"); + sb.Append("\x1B\x3\x2\x2\x2\x26A\x273\a\x96\x2\x2\x26B\x270\x5\x1A\xE\x2"); + sb.Append("\x26C\x26D\a\x99\x2\x2\x26D\x26F\x5\x1A\xE\x2\x26E\x26C\x3\x2"); + sb.Append("\x2\x2\x26F\x272\x3\x2\x2\x2\x270\x26E\x3\x2\x2\x2\x270\x271"); + sb.Append("\x3\x2\x2\x2\x271\x274\x3\x2\x2\x2\x272\x270\x3\x2\x2\x2\x273"); + sb.Append("\x26B\x3\x2\x2\x2\x273\x274\x3\x2\x2\x2\x274\x276\x3\x2\x2\x2"); + sb.Append("\x275\x277\a\x99\x2\x2\x276\x275\x3\x2\x2\x2\x276\x277\x3\x2"); + sb.Append("\x2\x2\x277\x278\x3\x2\x2\x2\x278\x279\a\x97\x2\x2\x279\x1D"); + sb.Append("\x3\x2\x2\x2\x27A\x27C\x5 \x11\x2\x27B\x27A\x3\x2\x2\x2\x27B"); + sb.Append("\x27C\x3\x2\x2\x2\x27C\x28B\x3\x2\x2\x2\x27D\x28C\x5\"\x12\x2"); + sb.Append("\x27E\x28C\x5N(\x2\x27F\x28C\x5R*\x2\x280\x28C\x5X-\x2\x281"); + sb.Append("\x28C\x5Z.\x2\x282\x28C\x5j\x36\x2\x283\x28C\x5\x92J\x2\x284"); + sb.Append("\x28C\x5\x94K\x2\x285\x28C\x5$\x13\x2\x286\x28C\x5(\x15\x2\x287"); + sb.Append("\x28C\x5t;\x2\x288\x28C\x5n\x38\x2\x289\x28C\x5p\x39\x2\x28A"); + sb.Append("\x28C\x5r:\x2\x28B\x27D\x3\x2\x2\x2\x28B\x27E\x3\x2\x2\x2\x28B"); + sb.Append("\x27F\x3\x2\x2\x2\x28B\x280\x3\x2\x2\x2\x28B\x281\x3\x2\x2\x2"); + sb.Append("\x28B\x282\x3\x2\x2\x2\x28B\x283\x3\x2\x2\x2\x28B\x284\x3\x2"); + sb.Append("\x2\x2\x28B\x285\x3\x2\x2\x2\x28B\x286\x3\x2\x2\x2\x28B\x287"); + sb.Append("\x3\x2\x2\x2\x28B\x288\x3\x2\x2\x2\x28B\x289\x3\x2\x2\x2\x28B"); + sb.Append("\x28A\x3\x2\x2\x2\x28C\x28E\x3\x2\x2\x2\x28D\x28F\x5\xD0i\x2"); + sb.Append("\x28E\x28D\x3\x2\x2\x2\x28E\x28F\x3\x2\x2\x2\x28F\x1F\x3\x2"); + sb.Append("\x2\x2\x290\x291\a\x82\x2\x2\x291\x292\a\xC5\x2\x2\x292!\x3"); + sb.Append("\x2\x2\x2\x293\x294\a\x35\x2\x2\x294\x296\x5\xAEX\x2\x295\x293"); + sb.Append("\x3\x2\x2\x2\x295\x296\x3\x2\x2\x2\x296\x299\x3\x2\x2\x2\x297"); + sb.Append("\x298\a\x34\x2\x2\x298\x29A\x5\xB0Y\x2\x299\x297\x3\x2\x2\x2"); + sb.Append("\x299\x29A\x3\x2\x2\x2\x29A\x29B\x3\x2\x2\x2\x29B\x29C\a\x1A"); + sb.Append("\x2\x2\x29C\x29F\x5\xC2\x62\x2\x29D\x29E\a \x2\x2\x29E\x2A0"); + sb.Append("\x5\xB4[\x2\x29F\x29D\x3\x2\x2\x2\x29F\x2A0\x3\x2\x2\x2\x2A0"); + sb.Append("\x2A2\x3\x2\x2\x2\x2A1\x2A3\x5\x100\x81\x2\x2A2\x2A1\x3\x2\x2"); + sb.Append("\x2\x2A2\x2A3\x3\x2\x2\x2\x2A3\x2A6\x3\x2\x2\x2\x2A4\x2A5\a"); + sb.Append("\xF\x2\x2\x2A5\x2A7\x5\xC0\x61\x2\x2A6\x2A4\x3\x2\x2\x2\x2A6"); + sb.Append("\x2A7\x3\x2\x2\x2\x2A7\x2AB\x3\x2\x2\x2\x2A8\x2A9\a*\x2\x2\x2A9"); + sb.Append("\x2AA\a)\x2\x2\x2AA\x2AC\x5\xE0q\x2\x2AB\x2A8\x3\x2\x2\x2\x2AB"); + sb.Append("\x2AC\x3\x2\x2\x2\x2AC\x2AF\x3\x2\x2\x2\x2AD\x2AE\a+\x2\x2\x2AE"); + sb.Append("\x2B0\x5\xF0y\x2\x2AF\x2AD\x3\x2\x2\x2\x2AF\x2B0\x3\x2\x2\x2"); + sb.Append("\x2B0\x2B3\x3\x2\x2\x2\x2B1\x2B2\a\x30\x2\x2\x2B2\x2B4\x5\xF2"); + sb.Append("z\x2\x2B3\x2B1\x3\x2\x2\x2\x2B3\x2B4\x3\x2\x2\x2\x2B4\x2B8\x3"); + sb.Append("\x2\x2\x2\x2B5\x2B6\a\x37\x2\x2\x2B6\x2B7\a)\x2\x2\x2B7\x2B9"); + sb.Append("\x5\xECw\x2\x2B8\x2B5\x3\x2\x2\x2\x2B8\x2B9\x3\x2\x2\x2\x2B9"); + sb.Append("\x2BC\x3\x2\x2\x2\x2BA\x2BB\ap\x2\x2\x2BB\x2BD\x5\xF8}\x2\x2BC"); + sb.Append("\x2BA\x3\x2\x2\x2\x2BC\x2BD\x3\x2\x2\x2\x2BD#\x3\x2\x2\x2\x2BE"); + sb.Append("\x2BF\a\'\x2\x2\x2BF\x2CF\x5&\x14\x2\x2C0\x2D0\x5\x44#\x2\x2C1"); + sb.Append("\x2CA\x5:\x1E\x2\x2C2\x2C4\x5> \x2\x2C3\x2C2\x3\x2\x2\x2\x2C4"); + sb.Append("\x2C5\x3\x2\x2\x2\x2C5\x2C3\x3\x2\x2\x2\x2C5\x2C6\x3\x2\x2\x2"); + sb.Append("\x2C6\x2C8\x3\x2\x2\x2\x2C7\x2C9\x5\x42\"\x2\x2C8\x2C7\x3\x2"); + sb.Append("\x2\x2\x2C8\x2C9\x3\x2\x2\x2\x2C9\x2CB\x3\x2\x2\x2\x2CA\x2C3"); + sb.Append("\x3\x2\x2\x2\x2CA\x2CB\x3\x2\x2\x2\x2CB\x2D0\x3\x2\x2\x2\x2CC"); + sb.Append("\x2D0\x5\x46$\x2\x2CD\x2D0\x5<\x1F\x2\x2CE\x2D0\x5,\x17\x2\x2CF"); + sb.Append("\x2C0\x3\x2\x2\x2\x2CF\x2C1\x3\x2\x2\x2\x2CF\x2CC\x3\x2\x2\x2"); + sb.Append("\x2CF\x2CD\x3\x2\x2\x2\x2CF\x2CE\x3\x2\x2\x2\x2D0%\x3\x2\x2"); + sb.Append("\x2\x2D1\x2D4\x5\x184\xC3\x2\x2D2\x2D4\x5\xD2j\x2\x2D3\x2D1"); + sb.Append("\x3\x2\x2\x2\x2D3\x2D2\x3\x2\x2\x2\x2D4\x2D8\x3\x2\x2\x2\x2D5"); + sb.Append("\x2D6\a\x10\x2\x2\x2D6\x2D9\a\xC5\x2\x2\x2D7\x2D9\a\xC5\x2\x2"); + sb.Append("\x2D8\x2D5\x3\x2\x2\x2\x2D8\x2D7\x3\x2\x2\x2\x2D8\x2D9\x3\x2"); + sb.Append("\x2\x2\x2D9\'\x3\x2\x2\x2\x2DA\x2DB\ar\x2\x2\x2DB\x2DC\a;\x2"); + sb.Append("\x2\x2DC\x2DD\x5*\x16\x2\x2DD)\x3\x2\x2\x2\x2DE\x2E2\x5\x198"); + sb.Append("\xCD\x2\x2DF\x2E0\a\x10\x2\x2\x2E0\x2E3\a\xC5\x2\x2\x2E1\x2E3"); + sb.Append("\a\xC5\x2\x2\x2E2\x2DF\x3\x2\x2\x2\x2E2\x2E1\x3\x2\x2\x2\x2E2"); + sb.Append("\x2E3\x3\x2\x2\x2\x2E3\x2E4\x3\x2\x2\x2\x2E4\x2E5\aR\x2\x2\x2E5"); + sb.Append("\x2E8\x5H%\x2\x2E6\x2E7\a\xF\x2\x2\x2E7\x2E9\x5\xC0\x61\x2\x2E8"); + sb.Append("\x2E6\x3\x2\x2\x2\x2E8\x2E9\x3\x2\x2\x2\x2E9+\x3\x2\x2\x2\x2EA"); + sb.Append("\x2EC\a}\x2\x2\x2EB\x2ED\a\x35\x2\x2\x2EC\x2EB\x3\x2\x2\x2\x2EC"); + sb.Append("\x2ED\x3\x2\x2\x2\x2ED\x2EE\x3\x2\x2\x2\x2EE\x2F2\a\xC5\x2\x2"); + sb.Append("\x2EF\x2F0\a\x10\x2\x2\x2F0\x2F3\a\xC5\x2\x2\x2F1\x2F3\a\xC5"); + sb.Append("\x2\x2\x2F2\x2EF\x3\x2\x2\x2\x2F2\x2F1\x3\x2\x2\x2\x2F2\x2F3"); + sb.Append("\x3\x2\x2\x2\x2F3\x2F6\x3\x2\x2\x2\x2F4\x2F5\a\xF\x2\x2\x2F5"); + sb.Append("\x2F7\x5\xC0\x61\x2\x2F6\x2F4\x3\x2\x2\x2\x2F6\x2F7\x3\x2\x2"); + sb.Append("\x2\x2F7\x2F9\x3\x2\x2\x2\x2F8\x2FA\x5.\x18\x2\x2F9\x2F8\x3"); + sb.Append("\x2\x2\x2\x2FA\x2FB\x3\x2\x2\x2\x2FB\x2F9\x3\x2\x2\x2\x2FB\x2FC"); + sb.Append("\x3\x2\x2\x2\x2FC-\x3\x2\x2\x2\x2FD\x300\x5\x30\x19\x2\x2FE"); + sb.Append("\x300\x5\x34\x1B\x2\x2FF\x2FD\x3\x2\x2\x2\x2FF\x2FE\x3\x2\x2"); + sb.Append("\x2\x300/\x3\x2\x2\x2\x301\x302\a\x1D\x2\x2\x302\x305\a~\x2"); + sb.Append("\x2\x303\x304\a\v\x2\x2\x304\x306\x5\x122\x92\x2\x305\x303\x3"); + sb.Append("\x2\x2\x2\x305\x306\x3\x2\x2\x2\x306\x308\x3\x2\x2\x2\x307\x309"); + sb.Append("\x5\x32\x1A\x2\x308\x307\x3\x2\x2\x2\x309\x30A\x3\x2\x2\x2\x30A"); + sb.Append("\x308\x3\x2\x2\x2\x30A\x30B\x3\x2\x2\x2\x30B\x31\x3\x2\x2\x2"); + sb.Append("\x30C\x31B\a\x1E\x2\x2\x30D\x30E\ar\x2\x2\x30E\x30F\aR\x2\x2"); + sb.Append("\x30F\x310\x5H%\x2\x310\x313\x3\x2\x2\x2\x311\x312\a\xF\x2\x2"); + sb.Append("\x312\x314\x5\xC0\x61\x2\x313\x311\x3\x2\x2\x2\x313\x314\x3"); + sb.Append("\x2\x2\x2\x314\x31C\x3\x2\x2\x2\x315\x318\aP\x2\x2\x316\x317"); + sb.Append("\a\xF\x2\x2\x317\x319\x5\xC0\x61\x2\x318\x316\x3\x2\x2\x2\x318"); + sb.Append("\x319\x3\x2\x2\x2\x319\x31C\x3\x2\x2\x2\x31A\x31C\x5\x38\x1D"); + sb.Append("\x2\x31B\x30D\x3\x2\x2\x2\x31B\x315\x3\x2\x2\x2\x31B\x31A\x3"); + sb.Append("\x2\x2\x2\x31C\x33\x3\x2\x2\x2\x31D\x31E\a\x1D\x2\x2\x31E\x31F"); + sb.Append("\a\f\x2\x2\x31F\x322\a~\x2\x2\x320\x321\a\v\x2\x2\x321\x323"); + sb.Append("\x5\x122\x92\x2\x322\x320\x3\x2\x2\x2\x322\x323\x3\x2\x2\x2"); + sb.Append("\x323\x325\x3\x2\x2\x2\x324\x326\x5\x36\x1C\x2\x325\x324\x3"); + sb.Append("\x2\x2\x2\x326\x327\x3\x2\x2\x2\x327\x325\x3\x2\x2\x2\x327\x328"); + sb.Append("\x3\x2\x2\x2\x328\x35\x3\x2\x2\x2\x329\x32A\a\x1E\x2\x2\x32A"); + sb.Append("\x32B\x5\x38\x1D\x2\x32B\x37\x3\x2\x2\x2\x32C\x32F\a\x34\x2"); + sb.Append("\x2\x32D\x32E\a\x35\x2\x2\x32E\x330\x5\x198\xCD\x2\x32F\x32D"); + sb.Append("\x3\x2\x2\x2\x32F\x330\x3\x2\x2\x2\x330\x335\x3\x2\x2\x2\x331"); + sb.Append("\x332\a\x92\x2\x2\x332\x333\x5\xB2Z\x2\x333\x334\a\x93\x2\x2"); + sb.Append("\x334\x336\x3\x2\x2\x2\x335\x331\x3\x2\x2\x2\x335\x336\x3\x2"); + sb.Append("\x2\x2\x336\x337\x3\x2\x2\x2\x337\x338\a\x1A\x2\x2\x338\x33B"); + sb.Append("\x5\xC4\x63\x2\x339\x33A\a\xF\x2\x2\x33A\x33C\x5\xC0\x61\x2"); + sb.Append("\x33B\x339\x3\x2\x2\x2\x33B\x33C\x3\x2\x2\x2\x33C\x39\x3\x2"); + sb.Append("\x2\x2\x33D\x33E\a\x34\x2\x2\x33E\x340\x5\xB0Y\x2\x33F\x33D"); + sb.Append("\x3\x2\x2\x2\x33F\x340\x3\x2\x2\x2\x340\x341\x3\x2\x2\x2\x341"); + sb.Append("\x346\a\x1A\x2\x2\x342\x344\a\v\x2\x2\x343\x342\x3\x2\x2\x2"); + sb.Append("\x343\x344\x3\x2\x2\x2\x344\x345\x3\x2\x2\x2\x345\x347\aP\x2"); + sb.Append("\x2\x346\x343\x3\x2\x2\x2\x346\x347\x3\x2\x2\x2\x347\x349\x3"); + sb.Append("\x2\x2\x2\x348\x34A\a,\x2\x2\x349\x348\x3\x2\x2\x2\x349\x34A"); + sb.Append("\x3\x2\x2\x2\x34A\x34B\x3\x2\x2\x2\x34B\x34D\x5\xC4\x63\x2\x34C"); + sb.Append("\x34E\x5L\'\x2\x34D\x34C\x3\x2\x2\x2\x34D\x34E\x3\x2\x2\x2\x34E"); + sb.Append("\x351\x3\x2\x2\x2\x34F\x350\a\xF\x2\x2\x350\x352\x5\xC0\x61"); + sb.Append("\x2\x351\x34F\x3\x2\x2\x2\x351\x352\x3\x2\x2\x2\x352\x356\x3"); + sb.Append("\x2\x2\x2\x353\x354\a*\x2\x2\x354\x355\a)\x2\x2\x355\x357\x5"); + sb.Append("\xE0q\x2\x356\x353\x3\x2\x2\x2\x356\x357\x3\x2\x2\x2\x357\x35A"); + sb.Append("\x3\x2\x2\x2\x358\x359\a+\x2\x2\x359\x35B\x5\xF0y\x2\x35A\x358"); + sb.Append("\x3\x2\x2\x2\x35A\x35B\x3\x2\x2\x2\x35B\x35F\x3\x2\x2\x2\x35C"); + sb.Append("\x35D\a\x37\x2\x2\x35D\x35E\a)\x2\x2\x35E\x360\x5\xECw\x2\x35F"); + sb.Append("\x35C\x3\x2\x2\x2\x35F\x360\x3\x2\x2\x2\x360\x363\x3\x2\x2\x2"); + sb.Append("\x361\x362\ap\x2\x2\x362\x364\x5\xF8}\x2\x363\x361\x3\x2\x2"); + sb.Append("\x2\x363\x364\x3\x2\x2\x2\x364;\x3\x2\x2\x2\x365\x366\ar\x2"); + sb.Append("\x2\x366\x36A\a\xC5\x2\x2\x367\x368\a\x10\x2\x2\x368\x36B\a"); + sb.Append("\xC5\x2\x2\x369\x36B\a\xC5\x2\x2\x36A\x367\x3\x2\x2\x2\x36A"); + sb.Append("\x369\x3\x2\x2\x2\x36A\x36B\x3\x2\x2\x2\x36B\x36C\x3\x2\x2\x2"); + sb.Append("\x36C\x36D\aR\x2\x2\x36D\x370\x5H%\x2\x36E\x36F\a\xF\x2\x2\x36F"); + sb.Append("\x371\x5\xC0\x61\x2\x370\x36E\x3\x2\x2\x2\x370\x371\x3\x2\x2"); + sb.Append("\x2\x371=\x3\x2\x2\x2\x372\x373\a\x34\x2\x2\x373\x374\x5\xB0"); + sb.Append("Y\x2\x374\x375\a\x1A\x2\x2\x375\x377\x5\xC4\x63\x2\x376\x378"); + sb.Append("\x5@!\x2\x377\x376\x3\x2\x2\x2\x377\x378\x3\x2\x2\x2\x378\x37B"); + sb.Append("\x3\x2\x2\x2\x379\x37A\a\xF\x2\x2\x37A\x37C\x5\xC0\x61\x2\x37B"); + sb.Append("\x379\x3\x2\x2\x2\x37B\x37C\x3\x2\x2\x2\x37C?\x3\x2\x2\x2\x37D"); + sb.Append("\x37E\a \x2\x2\x37E\x382\x5\x186\xC4\x2\x37F\x380\a\x10\x2\x2"); + sb.Append("\x380\x383\a\xC5\x2\x2\x381\x383\a\xC5\x2\x2\x382\x37F\x3\x2"); + sb.Append("\x2\x2\x382\x381\x3\x2\x2\x2\x382\x383\x3\x2\x2\x2\x383\x41"); + sb.Append("\x3\x2\x2\x2\x384\x387\a\x30\x2\x2\x385\x388\a\x32\x2\x2\x386"); + sb.Append("\x388\a-\x2\x2\x387\x385\x3\x2\x2\x2\x387\x386\x3\x2\x2\x2\x388"); + sb.Append("\x43\x3\x2\x2\x2\x389\x38A\aP\x2\x2\x38A\x38D\x5L\'\x2\x38B"); + sb.Append("\x38C\a\xF\x2\x2\x38C\x38E\x5\xC0\x61\x2\x38D\x38B\x3\x2\x2"); + sb.Append("\x2\x38D\x38E\x3\x2\x2\x2\x38E\x45\x3\x2\x2\x2\x38F\x390\aR"); + sb.Append("\x2\x2\x390\x391\x5H%\x2\x391G\x3\x2\x2\x2\x392\x397\x5J&\x2"); + sb.Append("\x393\x394\a\x99\x2\x2\x394\x396\x5J&\x2\x395\x393\x3\x2\x2"); + sb.Append("\x2\x396\x399\x3\x2\x2\x2\x397\x395\x3\x2\x2\x2\x397\x398\x3"); + sb.Append("\x2\x2\x2\x398I\x3\x2\x2\x2\x399\x397\x3\x2\x2\x2\x39A\x39B"); + sb.Append("\x5\x1C2\xE2\x2\x39B\x39C\a\x8F\x2\x2\x39C\x39D\x5\x122\x92"); + sb.Append("\x2\x39D\x3A0\x3\x2\x2\x2\x39E\x3A0\x5\x122\x92\x2\x39F\x39A"); + sb.Append("\x3\x2\x2\x2\x39F\x39E\x3\x2\x2\x2\x3A0K\x3\x2\x2\x2\x3A1\x3A2"); + sb.Append("\a \x2\x2\x3A2\x3A6\a\xC5\x2\x2\x3A3\x3A4\a\x10\x2\x2\x3A4\x3A7"); + sb.Append("\a\xC5\x2\x2\x3A5\x3A7\a\xC5\x2\x2\x3A6\x3A3\x3\x2\x2\x2\x3A6"); + sb.Append("\x3A5\x3\x2\x2\x2\x3A6\x3A7\x3\x2\x2\x2\x3A7M\x3\x2\x2\x2\x3A8"); + sb.Append("\x3A9\a\x3\x2\x2\x3A9\x3AA\a\x4\x2\x2\x3AA\x3AC\a\xC5\x2\x2"); + sb.Append("\x3AB\x3AD\x5\xD8m\x2\x3AC\x3AB\x3\x2\x2\x2\x3AC\x3AD\x3\x2"); + sb.Append("\x2\x2\x3AD\x3B0\x3\x2\x2\x2\x3AE\x3B1\a?\x2\x2\x3AF\x3B1\a"); + sb.Append("@\x2\x2\x3B0\x3AE\x3\x2\x2\x2\x3B0\x3AF\x3\x2\x2\x2\x3B0\x3B1"); + sb.Append("\x3\x2\x2\x2\x3B1\x3B3\x3\x2\x2\x2\x3B2\x3B4\a\x10\x2\x2\x3B3"); + sb.Append("\x3B2\x3\x2\x2\x2\x3B3\x3B4\x3\x2\x2\x2\x3B4\x3BA\x3\x2\x2\x2"); + sb.Append("\x3B5\x3BB\x5P)\x2\x3B6\x3B7\a\x92\x2\x2\x3B7\x3B8\x5\x62\x32"); + sb.Append("\x2\x3B8\x3B9\a\x93\x2\x2\x3B9\x3BB\x3\x2\x2\x2\x3BA\x3B5\x3"); + sb.Append("\x2\x2\x2\x3BA\x3B6\x3\x2\x2\x2\x3BB\x3C1\x3\x2\x2\x2\x3BC\x3BF"); + sb.Append("\a\x34\x2\x2\x3BD\x3BE\a\xF\x2\x2\x3BE\x3C0\x5\x122\x92\x2\x3BF"); + sb.Append("\x3BD\x3\x2\x2\x2\x3BF\x3C0\x3\x2\x2\x2\x3C0\x3C2\x3\x2\x2\x2"); + sb.Append("\x3C1\x3BC\x3\x2\x2\x2\x3C1\x3C2\x3\x2\x2\x2\x3C2O\x3\x2\x2"); + sb.Append("\x2\x3C3\x3C4\a\x1A\x2\x2\x3C4\x3C5\x5\x66\x34\x2\x3C5\x3C6"); + sb.Append("\a \x2\x2\x3C6\x3C8\x3\x2\x2\x2\x3C7\x3C3\x3\x2\x2\x2\x3C7\x3C8"); + sb.Append("\x3\x2\x2\x2\x3C8\x3C9\x3\x2\x2\x2\x3C9\x3CA\x5\x198\xCD\x2"); + sb.Append("\x3CAQ\x3\x2\x2\x2\x3CB\x3CD\a\x3\x2\x2\x3CC\x3CE\a\xC5\x2\x2"); + sb.Append("\x3CD\x3CC\x3\x2\x2\x2\x3CD\x3CE\x3\x2\x2\x2\x3CE\x3CF\x3\x2"); + sb.Append("\x2\x2\x3CF\x3D0\aW\x2\x2\x3D0\x3D1\a\xC5\x2\x2\x3D1\x3D2\a"); + sb.Append("\'\x2\x2\x3D2\x3D3\a\xC5\x2\x2\x3D3\x3D4\a\x92\x2\x2\x3D4\x3D5"); + sb.Append("\x5T+\x2\x3D5\x3D6\a\x93\x2\x2\x3D6S\x3\x2\x2\x2\x3D7\x3DC\x5"); + sb.Append("V,\x2\x3D8\x3D9\a\x99\x2\x2\x3D9\x3DB\x5V,\x2\x3DA\x3D8\x3\x2"); + sb.Append("\x2\x2\x3DB\x3DE\x3\x2\x2\x2\x3DC\x3DA\x3\x2\x2\x2\x3DC\x3DD"); + sb.Append("\x3\x2\x2\x2\x3DDU\x3\x2\x2\x2\x3DE\x3DC\x3\x2\x2\x2\x3DF\x3E1"); + sb.Append("\a\xC5\x2\x2\x3E0\x3E2\a\xC5\x2\x2\x3E1\x3E0\x3\x2\x2\x2\x3E1"); + sb.Append("\x3E2\x3\x2\x2\x2\x3E2W\x3\x2\x2\x2\x3E3\x3E5\a\x3\x2\x2\x3E4"); + sb.Append("\x3E6\a\xC5\x2\x2\x3E5\x3E4\x3\x2\x2\x2\x3E5\x3E6\x3\x2\x2\x2"); + sb.Append("\x3E6\x3E7\x3\x2\x2\x2\x3E7\x3E8\aS\x2\x2\x3E8\x3EE\x5\x198"); + sb.Append("\xCD\x2\x3E9\x3EB\a\x94\x2\x2\x3EA\x3EC\a\xC5\x2\x2\x3EB\x3EA"); + sb.Append("\x3\x2\x2\x2\x3EB\x3EC\x3\x2\x2\x2\x3EC\x3ED\x3\x2\x2\x2\x3ED"); + sb.Append("\x3EF\a\x95\x2\x2\x3EE\x3E9\x3\x2\x2\x2\x3EE\x3EF\x3\x2\x2\x2"); + sb.Append("\x3EF\x3F0\x3\x2\x2\x2\x3F0\x3F3\a\xC5\x2\x2\x3F1\x3F2\a\x8F"); + sb.Append("\x2\x2\x3F2\x3F4\x5\x122\x92\x2\x3F3\x3F1\x3\x2\x2\x2\x3F3\x3F4"); + sb.Append("\x3\x2\x2\x2\x3F4Y\x3\x2\x2\x2\x3F5\x3F6\a\x3\x2\x2\x3F6\x3F7"); + sb.Append("\aT\x2\x2\x3F7\x3F9\a\xC5\x2\x2\x3F8\x3FA\a\x10\x2\x2\x3F9\x3F8"); + sb.Append("\x3\x2\x2\x2\x3F9\x3FA\x3\x2\x2\x2\x3FA\x3FB\x3\x2\x2\x2\x3FB"); + sb.Append("\x3FC\a\x92\x2\x2\x3FC\x3FD\x5\\/\x2\x3FD\x3FE\a\x93\x2\x2\x3FE"); + sb.Append("[\x3\x2\x2\x2\x3FF\x404\x5^\x30\x2\x400\x401\a\x99\x2\x2\x401"); + sb.Append("\x403\x5^\x30\x2\x402\x400\x3\x2\x2\x2\x403\x406\x3\x2\x2\x2"); + sb.Append("\x404\x402\x3\x2\x2\x2\x404\x405\x3\x2\x2\x2\x405]\x3\x2\x2"); + sb.Append("\x2\x406\x404\x3\x2\x2\x2\x407\x40B\a\xC5\x2\x2\x408\x40C\x5"); + sb.Append("`\x31\x2\x409\x40C\x5\x14E\xA8\x2\x40A\x40C\x5\x154\xAB\x2\x40B"); + sb.Append("\x408\x3\x2\x2\x2\x40B\x409\x3\x2\x2\x2\x40B\x40A\x3\x2\x2\x2"); + sb.Append("\x40C\x40E\x3\x2\x2\x2\x40D\x40F\a\xC5\x2\x2\x40E\x40D\x3\x2"); + sb.Append("\x2\x2\x40E\x40F\x3\x2\x2\x2\x40F\x411\x3\x2\x2\x2\x410\x412"); + sb.Append("\a\xC5\x2\x2\x411\x410\x3\x2\x2\x2\x411\x412\x3\x2\x2\x2\x412"); + sb.Append("\x417\x3\x2\x2\x2\x413\x416\x5\x192\xCA\x2\x414\x416\x5\x14"); + sb.Append("\v\x2\x415\x413\x3\x2\x2\x2\x415\x414\x3\x2\x2\x2\x416\x419"); + sb.Append("\x3\x2\x2\x2\x417\x415\x3\x2\x2\x2\x417\x418\x3\x2\x2\x2\x418"); + sb.Append("_\x3\x2\x2\x2\x419\x417\x3\x2\x2\x2\x41A\x420\x5\x198\xCD\x2"); + sb.Append("\x41B\x41D\a\x94\x2\x2\x41C\x41E\a\xC5\x2\x2\x41D\x41C\x3\x2"); + sb.Append("\x2\x2\x41D\x41E\x3\x2\x2\x2\x41E\x41F\x3\x2\x2\x2\x41F\x421"); + sb.Append("\a\x95\x2\x2\x420\x41B\x3\x2\x2\x2\x420\x421\x3\x2\x2\x2\x421"); + sb.Append("\x61\x3\x2\x2\x2\x422\x427\x5\x64\x33\x2\x423\x424\a\x99\x2"); + sb.Append("\x2\x424\x426\x5\x64\x33\x2\x425\x423\x3\x2\x2\x2\x426\x429"); + sb.Append("\x3\x2\x2\x2\x427\x425\x3\x2\x2\x2\x427\x428\x3\x2\x2\x2\x428"); + sb.Append("\x63\x3\x2\x2\x2\x429\x427\x3\x2\x2\x2\x42A\x434\x5\x198\xCD"); + sb.Append("\x2\x42B\x435\ao\x2\x2\x42C\x432\x5\x198\xCD\x2\x42D\x42F\a"); + sb.Append("\x94\x2\x2\x42E\x430\a\xC5\x2\x2\x42F\x42E\x3\x2\x2\x2\x42F"); + sb.Append("\x430\x3\x2\x2\x2\x430\x431\x3\x2\x2\x2\x431\x433\a\x95\x2\x2"); + sb.Append("\x432\x42D\x3\x2\x2\x2\x432\x433\x3\x2\x2\x2\x433\x435\x3\x2"); + sb.Append("\x2\x2\x434\x42B\x3\x2\x2\x2\x434\x42C\x3\x2\x2\x2\x435\x65"); + sb.Append("\x3\x2\x2\x2\x436\x43B\x5h\x35\x2\x437\x438\a\x99\x2\x2\x438"); + sb.Append("\x43A\x5h\x35\x2\x439\x437\x3\x2\x2\x2\x43A\x43D\x3\x2\x2\x2"); + sb.Append("\x43B\x439\x3\x2\x2\x2\x43B\x43C\x3\x2\x2\x2\x43Cg\x3\x2\x2"); + sb.Append("\x2\x43D\x43B\x3\x2\x2\x2\x43E\x449\a\xA6\x2\x2\x43F\x442\x5"); + sb.Append("\x1C2\xE2\x2\x440\x441\a\x10\x2\x2\x441\x443\a\xC5\x2\x2\x442"); + sb.Append("\x440\x3\x2\x2\x2\x442\x443\x3\x2\x2\x2\x443\x449\x3\x2\x2\x2"); + sb.Append("\x444\x445\x5\x1E6\xF4\x2\x445\x446\a\x10\x2\x2\x446\x447\a"); + sb.Append("\xC5\x2\x2\x447\x449\x3\x2\x2\x2\x448\x43E\x3\x2\x2\x2\x448"); + sb.Append("\x43F\x3\x2\x2\x2\x448\x444\x3\x2\x2\x2\x449i\x3\x2\x2\x2\x44A"); + sb.Append("\x44C\a\x3\x2\x2\x44B\x44D\a\xC5\x2\x2\x44C\x44B\x3\x2\x2\x2"); + sb.Append("\x44C\x44D\x3\x2\x2\x2\x44D\x44E\x3\x2\x2\x2\x44E\x44F\x5l\x37"); + sb.Append("\x2\x44Fk\x3\x2\x2\x2\x450\x451\a=\x2\x2\x451\x453\a\xC5\x2"); + sb.Append("\x2\x452\x454\a\x10\x2\x2\x453\x452\x3\x2\x2\x2\x453\x454\x3"); + sb.Append("\x2\x2\x2\x454\x45B\x3\x2\x2\x2\x455\x45C\x5\xAAV\x2\x456\x458"); + sb.Append("\a\x92\x2\x2\x457\x459\x5\x62\x32\x2\x458\x457\x3\x2\x2\x2\x458"); + sb.Append("\x459\x3\x2\x2\x2\x459\x45A\x3\x2\x2\x2\x45A\x45C\a\x93\x2\x2"); + sb.Append("\x45B\x455\x3\x2\x2\x2\x45B\x456\x3\x2\x2\x2\x45C\x460\x3\x2"); + sb.Append("\x2\x2\x45D\x45F\x5\xA8U\x2\x45E\x45D\x3\x2\x2\x2\x45F\x462"); + sb.Append("\x3\x2\x2\x2\x460\x45E\x3\x2\x2\x2\x460\x461\x3\x2\x2\x2\x461"); + sb.Append("m\x3\x2\x2\x2\x462\x460\x3\x2\x2\x2\x463\x464\aP\x2\x2\x464"); + sb.Append("\x465\a \x2\x2\x465\x469\x5\x198\xCD\x2\x466\x467\a\x10\x2\x2"); + sb.Append("\x467\x46A\a\xC5\x2\x2\x468\x46A\a\xC5\x2\x2\x469\x466\x3\x2"); + sb.Append("\x2\x2\x469\x468\x3\x2\x2\x2\x469\x46A\x3\x2\x2\x2\x46A\x46D"); + sb.Append("\x3\x2\x2\x2\x46B\x46C\a\xF\x2\x2\x46C\x46E\x5\xC0\x61\x2\x46D"); + sb.Append("\x46B\x3\x2\x2\x2\x46D\x46E\x3\x2\x2\x2\x46Eo\x3\x2\x2\x2\x46F"); + sb.Append("\x470\ar\x2\x2\x470\x471\x5*\x16\x2\x471q\x3\x2\x2\x2\x472\x473"); + sb.Append("\a\x34\x2\x2\x473\x474\x5\xB0Y\x2\x474\x475\a\x36\x2\x2\x475"); + sb.Append("\x476\a\x92\x2\x2\x476\x477\x5\x1A8\xD5\x2\x477\x478\a\x93\x2"); + sb.Append("\x2\x478s\x3\x2\x2\x2\x479\x47A\a\x3\x2\x2\x47A\x47B\a\x85\x2"); + sb.Append("\x2\x47B\x47D\a\xC5\x2\x2\x47C\x47E\a\x10\x2\x2\x47D\x47C\x3"); + sb.Append("\x2\x2\x2\x47D\x47E\x3\x2\x2\x2\x47E\x47F\x3\x2\x2\x2\x47F\x480"); + sb.Append("\x5v<\x2\x480u\x3\x2\x2\x2\x481\x485\x5x=\x2\x482\x484\x5x="); + sb.Append("\x2\x483\x482\x3\x2\x2\x2\x484\x487\x3\x2\x2\x2\x485\x483\x3"); + sb.Append("\x2\x2\x2\x485\x486\x3\x2\x2\x2\x486w\x3\x2\x2\x2\x487\x485"); + sb.Append("\x3\x2\x2\x2\x488\x48A\x5\x14\v\x2\x489\x488\x3\x2\x2\x2\x48A"); + sb.Append("\x48D\x3\x2\x2\x2\x48B\x489\x3\x2\x2\x2\x48B\x48C\x3\x2\x2\x2"); + sb.Append("\x48C\x490\x3\x2\x2\x2\x48D\x48B\x3\x2\x2\x2\x48E\x491\a\xC5"); + sb.Append("\x2\x2\x48F\x491\a\x1A\x2\x2\x490\x48E\x3\x2\x2\x2\x490\x48F"); + sb.Append("\x3\x2\x2\x2\x491\x493\x3\x2\x2\x2\x492\x494\x5z>\x2\x493\x492"); + sb.Append("\x3\x2\x2\x2\x493\x494\x3\x2\x2\x2\x494\x496\x3\x2\x2\x2\x495"); + sb.Append("\x497\x5\x84\x43\x2\x496\x495\x3\x2\x2\x2\x496\x497\x3\x2\x2"); + sb.Append("\x2\x497\x498\x3\x2\x2\x2\x498\x49A\a\x96\x2\x2\x499\x49B\x5"); + sb.Append("\x8EH\x2\x49A\x499\x3\x2\x2\x2\x49A\x49B\x3\x2\x2\x2\x49B\x49D"); + sb.Append("\x3\x2\x2\x2\x49C\x49E\a\x99\x2\x2\x49D\x49C\x3\x2\x2\x2\x49D"); + sb.Append("\x49E\x3\x2\x2\x2\x49E\x49F\x3\x2\x2\x2\x49F\x4A4\a\x97\x2\x2"); + sb.Append("\x4A0\x4A1\x5j\x36\x2\x4A1\x4A2\a\x99\x2\x2\x4A2\x4A4\x3\x2"); + sb.Append("\x2\x2\x4A3\x48B\x3\x2\x2\x2\x4A3\x4A0\x3\x2\x2\x2\x4A4y\x3"); + sb.Append("\x2\x2\x2\x4A5\x4A6\a\x92\x2\x2\x4A6\x4A7\x5|?\x2\x4A7\x4A8"); + sb.Append("\a\x93\x2\x2\x4A8{\x3\x2\x2\x2\x4A9\x4AE\x5~@\x2\x4AA\x4AB\a"); + sb.Append("\x99\x2\x2\x4AB\x4AD\x5~@\x2\x4AC\x4AA\x3\x2\x2\x2\x4AD\x4B0"); + sb.Append("\x3\x2\x2\x2\x4AE\x4AC\x3\x2\x2\x2\x4AE\x4AF\x3\x2\x2\x2\x4AF"); + sb.Append("}\x3\x2\x2\x2\x4B0\x4AE\x3\x2\x2\x2\x4B1\x4B4\x5\x198\xCD\x2"); + sb.Append("\x4B2\x4B4\x5\x80\x41\x2\x4B3\x4B1\x3\x2\x2\x2\x4B3\x4B2\x3"); + sb.Append("\x2\x2\x2\x4B4\x4B6\x3\x2\x2\x2\x4B5\x4B7\x5\x82\x42\x2\x4B6"); + sb.Append("\x4B5\x3\x2\x2\x2\x4B6\x4B7\x3\x2\x2\x2\x4B7\x7F\x3\x2\x2\x2"); + sb.Append("\x4B8\x4B9\a\x92\x2\x2\x4B9\x4BA\x5\x198\xCD\x2\x4BA\x4BB\a"); + sb.Append("\x99\x2\x2\x4BB\x4BC\x5\x198\xCD\x2\x4BC\x4BD\x3\x2\x2\x2\x4BD"); + sb.Append("\x4BE\a\x93\x2\x2\x4BE\x81\x3\x2\x2\x2\x4BF\x4C0\a\x10\x2\x2"); + sb.Append("\x4C0\x4C1\a\xC5\x2\x2\x4C1\x83\x3\x2\x2\x2\x4C2\x4C3\a\x8D"); + sb.Append("\x2\x2\x4C3\x4C8\x5\x86\x44\x2\x4C4\x4C5\a\x99\x2\x2\x4C5\x4C7"); + sb.Append("\x5\x86\x44\x2\x4C6\x4C4\x3\x2\x2\x2\x4C7\x4CA\x3\x2\x2\x2\x4C8"); + sb.Append("\x4C6\x3\x2\x2\x2\x4C8\x4C9\x3\x2\x2\x2\x4C9\x85\x3\x2\x2\x2"); + sb.Append("\x4CA\x4C8\x3\x2\x2\x2\x4CB\x4CD\x5\x198\xCD\x2\x4CC\x4CE\x5"); + sb.Append("\x88\x45\x2\x4CD\x4CC\x3\x2\x2\x2\x4CD\x4CE\x3\x2\x2\x2\x4CE"); + sb.Append("\x87\x3\x2\x2\x2\x4CF\x4D0\a\xAD\x2\x2\x4D0\x4D5\x5\x8A\x46"); + sb.Append("\x2\x4D1\x4D2\a\x99\x2\x2\x4D2\x4D4\x5\x8A\x46\x2\x4D3\x4D1"); + sb.Append("\x3\x2\x2\x2\x4D4\x4D7\x3\x2\x2\x2\x4D5\x4D3\x3\x2\x2\x2\x4D5"); + sb.Append("\x4D6\x3\x2\x2\x2\x4D6\x4D8\x3\x2\x2\x2\x4D7\x4D5\x3\x2\x2\x2"); + sb.Append("\x4D8\x4D9\a\xAB\x2\x2\x4D9\x89\x3\x2\x2\x2\x4DA\x4DD\x5\x8C"); + sb.Append("G\x2\x4DB\x4DD\a\x91\x2\x2\x4DC\x4DA\x3\x2\x2\x2\x4DC\x4DB\x3"); + sb.Append("\x2\x2\x2\x4DD\x8B\x3\x2\x2\x2\x4DE\x4E0\x5\x198\xCD\x2\x4DF"); + sb.Append("\x4E1\x5\x88\x45\x2\x4E0\x4DF\x3\x2\x2\x2\x4E0\x4E1\x3\x2\x2"); + sb.Append("\x2\x4E1\x8D\x3\x2\x2\x2\x4E2\x4E7\x5\x90I\x2\x4E3\x4E4\a\x99"); + sb.Append("\x2\x2\x4E4\x4E6\x5\x90I\x2\x4E5\x4E3\x3\x2\x2\x2\x4E6\x4E9"); + sb.Append("\x3\x2\x2\x2\x4E7\x4E5\x3\x2\x2\x2\x4E7\x4E8\x3\x2\x2\x2\x4E8"); + sb.Append("\x8F\x3\x2\x2\x2\x4E9\x4E7\x3\x2\x2\x2\x4EA\x4EB\a\x1A\x2\x2"); + sb.Append("\x4EB\x4EC\t\x3\x2\x2\x4EC\x4ED\a\x92\x2\x2\x4ED\x4EE\x5\"\x12"); + sb.Append("\x2\x4EE\x4EF\a\x93\x2\x2\x4EF\x4F8\x3\x2\x2\x2\x4F0\x4F1\a"); + sb.Append("\xC5\x2\x2\x4F1\x4F5\t\x3\x2\x2\x4F2\x4F6\x5\x122\x92\x2\x4F3"); + sb.Append("\x4F6\x5\x1EE\xF8\x2\x4F4\x4F6\x5\x1F0\xF9\x2\x4F5\x4F2\x3\x2"); + sb.Append("\x2\x2\x4F5\x4F3\x3\x2\x2\x2\x4F5\x4F4\x3\x2\x2\x2\x4F6\x4F8"); + sb.Append("\x3\x2\x2\x2\x4F7\x4EA\x3\x2\x2\x2\x4F7\x4F0\x3\x2\x2\x2\x4F8"); + sb.Append("\x91\x3\x2\x2\x2\x4F9\x4FA\a\x3\x2\x2\x4FA\x4FB\a\x82\x2\x2"); + sb.Append("\x4FB\x4FD\a\xC5\x2\x2\x4FC\x4FE\a\x10\x2\x2\x4FD\x4FC\x3\x2"); + sb.Append("\x2\x2\x4FD\x4FE\x3\x2\x2\x2\x4FE\x4FF\x3\x2\x2\x2\x4FF\x500"); + sb.Append("\x5\x96L\x2\x500\x93\x3\x2\x2\x2\x501\x502\a\x3\x2\x2\x502\x503"); + sb.Append("\x5\n\x6\x2\x503\x95\x3\x2\x2\x2\x504\x510\x5\x9AN\x2\x505\x506"); + sb.Append("\x5\x98M\x2\x506\x507\a\x99\x2\x2\x507\x50C\x5\x98M\x2\x508"); + sb.Append("\x509\a\x99\x2\x2\x509\x50B\x5\x98M\x2\x50A\x508\x3\x2\x2\x2"); + sb.Append("\x50B\x50E\x3\x2\x2\x2\x50C\x50A\x3\x2\x2\x2\x50C\x50D\x3\x2"); + sb.Append("\x2\x2\x50D\x510\x3\x2\x2\x2\x50E\x50C\x3\x2\x2\x2\x50F\x504"); + sb.Append("\x3\x2\x2\x2\x50F\x505\x3\x2\x2\x2\x510\x97\x3\x2\x2\x2\x511"); + sb.Append("\x512\a\x82\x2\x2\x512\x514\a\xC5\x2\x2\x513\x515\a\x10\x2\x2"); + sb.Append("\x514\x513\x3\x2\x2\x2\x514\x515\x3\x2\x2\x2\x515\x516\x3\x2"); + sb.Append("\x2\x2\x516\x517\x5\x9AN\x2\x517\x99\x3\x2\x2\x2\x518\x51C\a"); + sb.Append("\x81\x2\x2\x519\x51A\a\xBD\x2\x2\x51A\x51D\a\xC5\x2\x2\x51B"); + sb.Append("\x51D\x5\x9EP\x2\x51C\x519\x3\x2\x2\x2\x51C\x51B\x3\x2\x2\x2"); + sb.Append("\x51D\x520\x3\x2\x2\x2\x51E\x51F\a\x1F\x2\x2\x51F\x521\x5\x9E"); + sb.Append("P\x2\x520\x51E\x3\x2\x2\x2\x520\x521\x3\x2\x2\x2\x521\x55F\x3"); + sb.Append("\x2\x2\x2\x522\x524\a\x83\x2\x2\x523\x525\a)\x2\x2\x524\x523"); + sb.Append("\x3\x2\x2\x2\x524\x525\x3\x2\x2\x2\x525\x527\x3\x2\x2\x2\x526"); + sb.Append("\x528\x5\x9CO\x2\x527\x526\x3\x2\x2\x2\x527\x528\x3\x2\x2\x2"); + sb.Append("\x528\x52C\x3\x2\x2\x2\x529\x52A\a\xBD\x2\x2\x52A\x52B\a\xC5"); + sb.Append("\x2\x2\x52B\x52D\a\v\x2\x2\x52C\x529\x3\x2\x2\x2\x52C\x52D\x3"); + sb.Append("\x2\x2\x2\x52D\x52E\x3\x2\x2\x2\x52E\x534\x5\x9EP\x2\x52F\x531"); + sb.Append("\a\x84\x2\x2\x530\x532\a)\x2\x2\x531\x530\x3\x2\x2\x2\x531\x532"); + sb.Append("\x3\x2\x2\x2\x532\x533\x3\x2\x2\x2\x533\x535\x5\x9EP\x2\x534"); + sb.Append("\x52F\x3\x2\x2\x2\x534\x535\x3\x2\x2\x2\x535\x55F\x3\x2\x2\x2"); + sb.Append("\x536\x538\aw\x2\x2\x537\x539\a)\x2\x2\x538\x537\x3\x2\x2\x2"); + sb.Append("\x538\x539\x3\x2\x2\x2\x539\x53A\x3\x2\x2\x2\x53A\x53F\x5\xA2"); + sb.Append("R\x2\x53B\x53C\a\x99\x2\x2\x53C\x53E\x5\xA2R\x2\x53D\x53B\x3"); + sb.Append("\x2\x2\x2\x53E\x541\x3\x2\x2\x2\x53F\x53D\x3\x2\x2\x2\x53F\x540"); + sb.Append("\x3\x2\x2\x2\x540\x55F\x3\x2\x2\x2\x541\x53F\x3\x2\x2\x2\x542"); + sb.Append("\x547\x5\xA6T\x2\x543\x544\a\x99\x2\x2\x544\x546\x5\xA6T\x2"); + sb.Append("\x545\x543\x3\x2\x2\x2\x546\x549\x3\x2\x2\x2\x547\x545\x3\x2"); + sb.Append("\x2\x2\x547\x548\x3\x2\x2\x2\x548\x54A\x3\x2\x2\x2\x549\x547"); + sb.Append("\x3\x2\x2\x2\x54A\x54B\a \x2\x2\x54B\x54C\x5\x184\xC3\x2\x54C"); + sb.Append("\x55F\x3\x2\x2\x2\x54D\x54F\a\x15\x2\x2\x54E\x550\a)\x2\x2\x54F"); + sb.Append("\x54E\x3\x2\x2\x2\x54F\x550\x3\x2\x2\x2\x550\x551\x3\x2\x2\x2"); + sb.Append("\x551\x556\x5\xA4S\x2\x552\x553\a\x99\x2\x2\x553\x555\x5\xA4"); + sb.Append("S\x2\x554\x552\x3\x2\x2\x2\x555\x558\x3\x2\x2\x2\x556\x554\x3"); + sb.Append("\x2\x2\x2\x556\x557\x3\x2\x2\x2\x557\x559\x3\x2\x2\x2\x558\x556"); + sb.Append("\x3\x2\x2\x2\x559\x55A\a\xC5\x2\x2\x55A\x55C\x5\x1E2\xF2\x2"); + sb.Append("\x55B\x55D\a\xC5\x2\x2\x55C\x55B\x3\x2\x2\x2\x55C\x55D\x3\x2"); + sb.Append("\x2\x2\x55D\x55F\x3\x2\x2\x2\x55E\x518\x3\x2\x2\x2\x55E\x522"); + sb.Append("\x3\x2\x2\x2\x55E\x536\x3\x2\x2\x2\x55E\x542\x3\x2\x2\x2\x55E"); + sb.Append("\x54D\x3\x2\x2\x2\x55F\x9B\x3\x2\x2\x2\x560\x561\a,\x2\x2\x561"); + sb.Append("\x563\a\x92\x2\x2\x562\x564\x5\x1A8\xD5\x2\x563\x562\x3\x2\x2"); + sb.Append("\x2\x563\x564\x3\x2\x2\x2\x564\x565\x3\x2\x2\x2\x565\x566\a"); + sb.Append("\x93\x2\x2\x566\x9D\x3\x2\x2\x2\x567\x571\x5\xA0Q\x2\x568\x56B"); + sb.Append("\x5\xD2j\x2\x569\x56A\a\xBD\x2\x2\x56A\x56C\a\xC5\x2\x2\x56B"); + sb.Append("\x569\x3\x2\x2\x2\x56B\x56C\x3\x2\x2\x2\x56C\x571\x3\x2\x2\x2"); + sb.Append("\x56D\x571\x5\xFA~\x2\x56E\x56F\ay\x2\x2\x56F\x571\x5\x1CE\xE8"); + sb.Append("\x2\x570\x567\x3\x2\x2\x2\x570\x568\x3\x2\x2\x2\x570\x56D\x3"); + sb.Append("\x2\x2\x2\x570\x56E\x3\x2\x2\x2\x571\x9F\x3\x2\x2\x2\x572\x577"); + sb.Append("\x5\x184\xC3\x2\x573\x575\a\x10\x2\x2\x574\x573\x3\x2\x2\x2"); + sb.Append("\x574\x575\x3\x2\x2\x2\x575\x576\x3\x2\x2\x2\x576\x578\a\xC5"); + sb.Append("\x2\x2\x577\x574\x3\x2\x2\x2\x577\x578\x3\x2\x2\x2\x578\xA1"); + sb.Append("\x3\x2\x2\x2\x579\x57E\x5\x1C2\xE2\x2\x57A\x57B\t\x4\x2\x2\x57B"); + sb.Append("\x57D\x5\x1C2\xE2\x2\x57C\x57A\x3\x2\x2\x2\x57D\x580\x3\x2\x2"); + sb.Append("\x2\x57E\x57C\x3\x2\x2\x2\x57E\x57F\x3\x2\x2\x2\x57F\x581\x3"); + sb.Append("\x2\x2\x2\x580\x57E\x3\x2\x2\x2\x581\x582\a \x2\x2\x582\x583"); + sb.Append("\x5\x184\xC3\x2\x583\xA3\x3\x2\x2\x2\x584\x585\x5\x158\xAD\x2"); + sb.Append("\x585\x586\a \x2\x2\x586\x587\x5\x184\xC3\x2\x587\xA5\x3\x2"); + sb.Append("\x2\x2\x588\x58A\a*\x2\x2\x589\x58B\a)\x2\x2\x58A\x589\x3\x2"); + sb.Append("\x2\x2\x58A\x58B\x3\x2\x2\x2\x58B\x58C\x3\x2\x2\x2\x58C\x58D"); + sb.Append("\x5\x122\x92\x2\x58D\x58E\a\x10\x2\x2\x58E\x58F\a\xC5\x2\x2"); + sb.Append("\x58F\xA7\x3\x2\x2\x2\x590\x591\a\xC5\x2\x2\x591\x592\x5\xB2"); + sb.Append("Z\x2\x592\xA9\x3\x2\x2\x2\x593\x598\x5\xACW\x2\x594\x595\a\x99"); + sb.Append("\x2\x2\x595\x597\x5\xACW\x2\x596\x594\x3\x2\x2\x2\x597\x59A"); + sb.Append("\x3\x2\x2\x2\x598\x596\x3\x2\x2\x2\x598\x599\x3\x2\x2\x2\x599"); + sb.Append("\xAB\x3\x2\x2\x2\x59A\x598\x3\x2\x2\x2\x59B\x59E\a\xA6\x2\x2"); + sb.Append("\x59C\x59E\x5\x198\xCD\x2\x59D\x59B\x3\x2\x2\x2\x59D\x59C\x3"); + sb.Append("\x2\x2\x2\x59E\xAD\x3\x2\x2\x2\x59F\x5A0\aT\x2\x2\x5A0\x5A1"); + sb.Append("\a\xC5\x2\x2\x5A1\xAF\x3\x2\x2\x2\x5A2\x5A6\a;\x2\x2\x5A3\x5A6"); + sb.Append("\a:\x2\x2\x5A4\x5A6\a<\x2\x2\x5A5\x5A2\x3\x2\x2\x2\x5A5\x5A3"); + sb.Append("\x3\x2\x2\x2\x5A5\x5A4\x3\x2\x2\x2\x5A5\x5A6\x3\x2\x2\x2\x5A6"); + sb.Append("\x5A7\x3\x2\x2\x2\x5A7\x5A8\a\x35\x2\x2\x5A8\x5AE\x5\x198\xCD"); + sb.Append("\x2\x5A9\x5AB\a\x92\x2\x2\x5AA\x5AC\x5\xB2Z\x2\x5AB\x5AA\x3"); + sb.Append("\x2\x2\x2\x5AB\x5AC\x3\x2\x2\x2\x5AC\x5AD\x3\x2\x2\x2\x5AD\x5AF"); + sb.Append("\a\x93\x2\x2\x5AE\x5A9\x3\x2\x2\x2\x5AE\x5AF\x3\x2\x2\x2\x5AF"); + sb.Append("\xB1\x3\x2\x2\x2\x5B0\x5B5\a\xC5\x2\x2\x5B1\x5B2\a\x99\x2\x2"); + sb.Append("\x5B2\x5B4\a\xC5\x2\x2\x5B3\x5B1\x3\x2\x2\x2\x5B4\x5B7\x3\x2"); + sb.Append("\x2\x2\x5B5\x5B3\x3\x2\x2\x2\x5B5\x5B6\x3\x2\x2\x2\x5B6\xB3"); + sb.Append("\x3\x2\x2\x2\x5B7\x5B5\x3\x2\x2\x2\x5B8\x5BB\x5\xCEh\x2\x5B9"); + sb.Append("\x5BC\x5\xB6\\\x2\x5BA\x5BC\x5\xB8]\x2\x5BB\x5B9\x3\x2\x2\x2"); + sb.Append("\x5BB\x5BA\x3\x2\x2\x2\x5BC\xB5\x3\x2\x2\x2\x5BD\x5BE\a\x99"); + sb.Append("\x2\x2\x5BE\x5C0\x5\xCEh\x2\x5BF\x5BD\x3\x2\x2\x2\x5C0\x5C3"); + sb.Append("\x3\x2\x2\x2\x5C1\x5BF\x3\x2\x2\x2\x5C1\x5C2\x3\x2\x2\x2\x5C2"); + sb.Append("\xB7\x3\x2\x2\x2\x5C3\x5C1\x3\x2\x2\x2\x5C4\x5C8\x5\xBA^\x2"); + sb.Append("\x5C5\x5C7\x5\xBA^\x2\x5C6\x5C5\x3\x2\x2\x2\x5C7\x5CA\x3\x2"); + sb.Append("\x2\x2\x5C8\x5C6\x3\x2\x2\x2\x5C8\x5C9\x3\x2\x2\x2\x5C9\xB9"); + sb.Append("\x3\x2\x2\x2\x5CA\x5C8\x3\x2\x2\x2\x5CB\x5CF\a$\x2\x2\x5CC\x5CF"); + sb.Append("\a%\x2\x2\x5CD\x5CF\a&\x2\x2\x5CE\x5CB\x3\x2\x2\x2\x5CE\x5CC"); + sb.Append("\x3\x2\x2\x2\x5CE\x5CD\x3\x2\x2\x2\x5CF\x5D0\x3\x2\x2\x2\x5D0"); + sb.Append("\x5D2\a!\x2\x2\x5D1\x5CE\x3\x2\x2\x2\x5D1\x5D2\x3\x2\x2\x2\x5D2"); + sb.Append("\x5D5\x3\x2\x2\x2\x5D3\x5D5\a\"\x2\x2\x5D4\x5D1\x3\x2\x2\x2"); + sb.Append("\x5D4\x5D3\x3\x2\x2\x2\x5D5\x5D6\x3\x2\x2\x2\x5D6\x5D7\a#\x2"); + sb.Append("\x2\x5D7\x5D9\x5\xCEh\x2\x5D8\x5DA\x5\xBC_\x2\x5D9\x5D8\x3\x2"); + sb.Append("\x2\x2\x5D9\x5DA\x3\x2\x2\x2\x5DA\xBB\x3\x2\x2\x2\x5DB\x5DC"); + sb.Append("\a\'\x2\x2\x5DC\x5E1\x5\xBE`\x2\x5DD\x5DE\a\v\x2\x2\x5DE\x5E0"); + sb.Append("\x5\xBE`\x2\x5DF\x5DD\x3\x2\x2\x2\x5E0\x5E3\x3\x2\x2\x2\x5E1"); + sb.Append("\x5DF\x3\x2\x2\x2\x5E1\x5E2\x3\x2\x2\x2\x5E2\xBD\x3\x2\x2\x2"); + sb.Append("\x5E3\x5E1\x3\x2\x2\x2\x5E4\x5E5\x5\x1C2\xE2\x2\x5E5\x5E6\a"); + sb.Append("\x8F\x2\x2\x5E6\x5E7\x5\x1C2\xE2\x2\x5E7\xBF\x3\x2\x2\x2\x5E8"); + sb.Append("\x5E9\x5\x126\x94\x2\x5E9\xC1\x3\x2\x2\x2\x5EA\x5EE\a:\x2\x2"); + sb.Append("\x5EB\x5EE\a;\x2\x2\x5EC\x5EE\a<\x2\x2\x5ED\x5EA\x3\x2\x2\x2"); + sb.Append("\x5ED\x5EB\x3\x2\x2\x2\x5ED\x5EC\x3\x2\x2\x2\x5ED\x5EE\x3\x2"); + sb.Append("\x2\x2\x5EE\x5F0\x3\x2\x2\x2\x5EF\x5F1\a,\x2\x2\x5F0\x5EF\x3"); + sb.Append("\x2\x2\x2\x5F0\x5F1\x3\x2\x2\x2\x5F1\x5F2\x3\x2\x2\x2\x5F2\x5F3"); + sb.Append("\x5\xC4\x63\x2\x5F3\xC3\x3\x2\x2\x2\x5F4\x5F9\x5\xC6\x64\x2"); + sb.Append("\x5F5\x5F6\a\x99\x2\x2\x5F6\x5F8\x5\xC6\x64\x2\x5F7\x5F5\x3"); + sb.Append("\x2\x2\x2\x5F8\x5FB\x3\x2\x2\x2\x5F9\x5F7\x3\x2\x2\x2\x5F9\x5FA"); + sb.Append("\x3\x2\x2\x2\x5FA\xC5\x3\x2\x2\x2\x5FB\x5F9\x3\x2\x2\x2\x5FC"); + sb.Append("\x600\a\xA6\x2\x2\x5FD\x600\x5\xCCg\x2\x5FE\x600\x5\xC8\x65"); + sb.Append("\x2\x5FF\x5FC\x3\x2\x2\x2\x5FF\x5FD\x3\x2\x2\x2\x5FF\x5FE\x3"); + sb.Append("\x2\x2\x2\x600\xC7\x3\x2\x2\x2\x601\x603\x5\x122\x92\x2\x602"); + sb.Append("\x604\x5\xCA\x66\x2\x603\x602\x3\x2\x2\x2\x603\x604\x3\x2\x2"); + sb.Append("\x2\x604\x609\x3\x2\x2\x2\x605\x607\a\x10\x2\x2\x606\x605\x3"); + sb.Append("\x2\x2\x2\x606\x607\x3\x2\x2\x2\x607\x608\x3\x2\x2\x2\x608\x60A"); + sb.Append("\x5\x1C8\xE5\x2\x609\x606\x3\x2\x2\x2\x609\x60A\x3\x2\x2\x2"); + sb.Append("\x60A\xC9\x3\x2\x2\x2\x60B\x60C\a\xBD\x2\x2\x60C\x60D\a\xC5"); + sb.Append("\x2\x2\x60D\xCB\x3\x2\x2\x2\x60E\x60F\a\xC5\x2\x2\x60F\x610"); + sb.Append("\a\xB7\x2\x2\x610\x613\a\xA6\x2\x2\x611\x612\a\x10\x2\x2\x612"); + sb.Append("\x614\a\xC5\x2\x2\x613\x611\x3\x2\x2\x2\x613\x614\x3\x2\x2\x2"); + sb.Append("\x614\xCD\x3\x2\x2\x2\x615\x61A\x5\x184\xC3\x2\x616\x61A\x5"); + sb.Append("\xD2j\x2\x617\x61A\x5\xD4k\x2\x618\x61A\x5\xD6l\x2\x619\x615"); + sb.Append("\x3\x2\x2\x2\x619\x616\x3\x2\x2\x2\x619\x617\x3\x2\x2\x2\x619"); + sb.Append("\x618\x3\x2\x2\x2\x61A\x61C\x3\x2\x2\x2\x61B\x61D\x5\xD8m\x2"); + sb.Append("\x61C\x61B\x3\x2\x2\x2\x61C\x61D\x3\x2\x2\x2\x61D\x621\x3\x2"); + sb.Append("\x2\x2\x61E\x61F\a\x10\x2\x2\x61F\x622\a\xC5\x2\x2\x620\x622"); + sb.Append("\a\xC5\x2\x2\x621\x61E\x3\x2\x2\x2\x621\x620\x3\x2\x2\x2\x621"); + sb.Append("\x622\x3\x2\x2\x2\x622\x624\x3\x2\x2\x2\x623\x625\a>\x2\x2\x624"); + sb.Append("\x623\x3\x2\x2\x2\x624\x625\x3\x2\x2\x2\x625\x628\x3\x2\x2\x2"); + sb.Append("\x626\x629\a?\x2\x2\x627\x629\a@\x2\x2\x628\x626\x3\x2\x2\x2"); + sb.Append("\x628\x627\x3\x2\x2\x2\x628\x629\x3\x2\x2\x2\x629\xCF\x3\x2"); + sb.Append("\x2\x2\x62A\x62B\az\x2\x2\x62B\x631\a\xC5\x2\x2\x62C\x62E\a"); + sb.Append("\x92\x2\x2\x62D\x62F\x5\x1A8\xD5\x2\x62E\x62D\x3\x2\x2\x2\x62E"); + sb.Append("\x62F\x3\x2\x2\x2\x62F\x630\x3\x2\x2\x2\x630\x632\a\x93\x2\x2"); + sb.Append("\x631\x62C\x3\x2\x2\x2\x631\x632\x3\x2\x2\x2\x632\xD1\x3\x2"); + sb.Append("\x2\x2\x633\x637\a\x41\x2\x2\x634\x636\x5\x14\v\x2\x635\x634"); + sb.Append("\x3\x2\x2\x2\x636\x639\x3\x2\x2\x2\x637\x635\x3\x2\x2\x2\x637"); + sb.Append("\x638\x3\x2\x2\x2\x638\x63A\x3\x2\x2\x2\x639\x637\x3\x2\x2\x2"); + sb.Append("\x63A\x63B\a\x94\x2\x2\x63B\x63C\x5\x166\xB4\x2\x63C\x63D\a"); + sb.Append("\x95\x2\x2\x63D\xD3\x3\x2\x2\x2\x63E\x63F\a\x42\x2\x2\x63F\x640"); + sb.Append("\a\x98\x2\x2\x640\x641\a\xC5\x2\x2\x641\x644\a\x94\x2\x2\x642"); + sb.Append("\x645\a\xC4\x2\x2\x643\x645\a\xC3\x2\x2\x644\x642\x3\x2\x2\x2"); + sb.Append("\x644\x643\x3\x2\x2\x2\x645\x64B\x3\x2\x2\x2\x646\x649\a\x43"); + sb.Append("\x2\x2\x647\x64A\a\xC4\x2\x2\x648\x64A\a\xC3\x2\x2\x649\x647"); + sb.Append("\x3\x2\x2\x2\x649\x648\x3\x2\x2\x2\x64A\x64C\x3\x2\x2\x2\x64B"); + sb.Append("\x646\x3\x2\x2\x2\x64B\x64C\x3\x2\x2\x2\x64C\x64D\x3\x2\x2\x2"); + sb.Append("\x64D\x64E\a\x95\x2\x2\x64E\xD5\x3\x2\x2\x2\x64F\x650\a\xC5"); + sb.Append("\x2\x2\x650\x651\a\x98\x2\x2\x651\x657\x5\x198\xCD\x2\x652\x654"); + sb.Append("\a\x92\x2\x2\x653\x655\x5\x1A8\xD5\x2\x654\x653\x3\x2\x2\x2"); + sb.Append("\x654\x655\x3\x2\x2\x2\x655\x656\x3\x2\x2\x2\x656\x658\a\x93"); + sb.Append("\x2\x2\x657\x652\x3\x2\x2\x2\x657\x658\x3\x2\x2\x2\x658\x65A"); + sb.Append("\x3\x2\x2\x2\x659\x65B\x5\x192\xCA\x2\x65A\x659\x3\x2\x2\x2"); + sb.Append("\x65A\x65B\x3\x2\x2\x2\x65B\xD7\x3\x2\x2\x2\x65C\x65D\a\xB7"); + sb.Append("\x2\x2\x65D\x662\x5\xDAn\x2\x65E\x65F\a\xB7\x2\x2\x65F\x661"); + sb.Append("\x5\xDAn\x2\x660\x65E\x3\x2\x2\x2\x661\x664\x3\x2\x2\x2\x662"); + sb.Append("\x660\x3\x2\x2\x2\x662\x663\x3\x2\x2\x2\x663\x66F\x3\x2\x2\x2"); + sb.Append("\x664\x662\x3\x2\x2\x2\x665\x666\a\xBE\x2\x2\x666\x66B\x5\xDC"); + sb.Append("o\x2\x667\x668\a\xBE\x2\x2\x668\x66A\x5\xDCo\x2\x669\x667\x3"); + sb.Append("\x2\x2\x2\x66A\x66D\x3\x2\x2\x2\x66B\x669\x3\x2\x2\x2\x66B\x66C"); + sb.Append("\x3\x2\x2\x2\x66C\x66F\x3\x2\x2\x2\x66D\x66B\x3\x2\x2\x2\x66E"); + sb.Append("\x65C\x3\x2\x2\x2\x66E\x665\x3\x2\x2\x2\x66F\xD9\x3\x2\x2\x2"); + sb.Append("\x670\x671\a\xC5\x2\x2\x671\x672\a\x98\x2\x2\x672\x673\x5\xDE"); + sb.Append("p\x2\x673\xDB\x3\x2\x2\x2\x674\x675\a\xC5\x2\x2\x675\x677\a"); + sb.Append("\x98\x2\x2\x676\x674\x3\x2\x2\x2\x676\x677\x3\x2\x2\x2\x677"); + sb.Append("\x678\x3\x2\x2\x2\x678\x679\x5\xDEp\x2\x679\xDD\x3\x2\x2\x2"); + sb.Append("\x67A\x67D\a\xC5\x2\x2\x67B\x67D\a}\x2\x2\x67C\x67A\x3\x2\x2"); + sb.Append("\x2\x67C\x67B\x3\x2\x2\x2\x67D\x683\x3\x2\x2\x2\x67E\x680\a"); + sb.Append("\x92\x2\x2\x67F\x681\x5\x1AA\xD6\x2\x680\x67F\x3\x2\x2\x2\x680"); + sb.Append("\x681\x3\x2\x2\x2\x681\x682\x3\x2\x2\x2\x682\x684\a\x93\x2\x2"); + sb.Append("\x683\x67E\x3\x2\x2\x2\x683\x684\x3\x2\x2\x2\x684\xDF\x3\x2"); + sb.Append("\x2\x2\x685\x68A\x5\xE2r\x2\x686\x687\a\x99\x2\x2\x687\x689"); + sb.Append("\x5\xE2r\x2\x688\x686\x3\x2\x2\x2\x689\x68C\x3\x2\x2\x2\x68A"); + sb.Append("\x688\x3\x2\x2\x2\x68A\x68B\x3\x2\x2\x2\x68B\xE1\x3\x2\x2\x2"); + sb.Append("\x68C\x68A\x3\x2\x2\x2\x68D\x691\x5\x122\x92\x2\x68E\x691\x5"); + sb.Append("\xE4s\x2\x68F\x691\x5\xE6t\x2\x690\x68D\x3\x2\x2\x2\x690\x68E"); + sb.Append("\x3\x2\x2\x2\x690\x68F\x3\x2\x2\x2\x691\xE3\x3\x2\x2\x2\x692"); + sb.Append("\x693\t\x5\x2\x2\x693\x694\a\x92\x2\x2\x694\x699\x5\xEAv\x2"); + sb.Append("\x695\x696\a\x99\x2\x2\x696\x698\x5\xEAv\x2\x697\x695\x3\x2"); + sb.Append("\x2\x2\x698\x69B\x3\x2\x2\x2\x699\x697\x3\x2\x2\x2\x699\x69A"); + sb.Append("\x3\x2\x2\x2\x69A\x69C\x3\x2\x2\x2\x69B\x699\x3\x2\x2\x2\x69C"); + sb.Append("\x69D\a\x93\x2\x2\x69D\xE5\x3\x2\x2\x2\x69E\x69F\a\x88\x2\x2"); + sb.Append("\x69F\x6A0\a\x8A\x2\x2\x6A0\x6A1\a\x92\x2\x2\x6A1\x6A6\x5\xE8"); + sb.Append("u\x2\x6A2\x6A3\a\x99\x2\x2\x6A3\x6A5\x5\xE8u\x2\x6A4\x6A2\x3"); + sb.Append("\x2\x2\x2\x6A5\x6A8\x3\x2\x2\x2\x6A6\x6A4\x3\x2\x2\x2\x6A6\x6A7"); + sb.Append("\x3\x2\x2\x2\x6A7\x6A9\x3\x2\x2\x2\x6A8\x6A6\x3\x2\x2\x2\x6A9"); + sb.Append("\x6AA\a\x93\x2\x2\x6AA\xE7\x3\x2\x2\x2\x6AB\x6AE\x5\xE4s\x2"); + sb.Append("\x6AC\x6AE\x5\xEAv\x2\x6AD\x6AB\x3\x2\x2\x2\x6AD\x6AC\x3\x2"); + sb.Append("\x2\x2\x6AE\xE9\x3\x2\x2\x2\x6AF\x6BD\x5\x122\x92\x2\x6B0\x6B9"); + sb.Append("\a\x92\x2\x2\x6B1\x6B6\x5\x122\x92\x2\x6B2\x6B3\a\x99\x2\x2"); + sb.Append("\x6B3\x6B5\x5\x122\x92\x2\x6B4\x6B2\x3\x2\x2\x2\x6B5\x6B8\x3"); + sb.Append("\x2\x2\x2\x6B6\x6B4\x3\x2\x2\x2\x6B6\x6B7\x3\x2\x2\x2\x6B7\x6BA"); + sb.Append("\x3\x2\x2\x2\x6B8\x6B6\x3\x2\x2\x2\x6B9\x6B1\x3\x2\x2\x2\x6B9"); + sb.Append("\x6BA\x3\x2\x2\x2\x6BA\x6BB\x3\x2\x2\x2\x6BB\x6BD\a\x93\x2\x2"); + sb.Append("\x6BC\x6AF\x3\x2\x2\x2\x6BC\x6B0\x3\x2\x2\x2\x6BD\xEB\x3\x2"); + sb.Append("\x2\x2\x6BE\x6C3\x5\xEEx\x2\x6BF\x6C0\a\x99\x2\x2\x6C0\x6C2"); + sb.Append("\x5\xEEx\x2\x6C1\x6BF\x3\x2\x2\x2\x6C2\x6C5\x3\x2\x2\x2\x6C3"); + sb.Append("\x6C1\x3\x2\x2\x2\x6C3\x6C4\x3\x2\x2\x2\x6C4\xED\x3\x2\x2\x2"); + sb.Append("\x6C5\x6C3\x3\x2\x2\x2\x6C6\x6C9\x5\x122\x92\x2\x6C7\x6CA\a"); + sb.Append("\x38\x2\x2\x6C8\x6CA\a\x39\x2\x2\x6C9\x6C7\x3\x2\x2\x2\x6C9"); + sb.Append("\x6C8\x3\x2\x2\x2\x6C9\x6CA\x3\x2\x2\x2\x6CA\xEF\x3\x2\x2\x2"); + sb.Append("\x6CB\x6CC\x5\x126\x94\x2\x6CC\xF1\x3\x2\x2\x2\x6CD\x6CF\x5"); + sb.Append("\xF6|\x2\x6CE\x6CD\x3\x2\x2\x2\x6CE\x6CF\x3\x2\x2\x2\x6CF\x6D4"); + sb.Append("\x3\x2\x2\x2\x6D0\x6D5\a-\x2\x2\x6D1\x6D5\a\x32\x2\x2\x6D2\x6D5"); + sb.Append("\a\x33\x2\x2\x6D3\x6D5\aQ\x2\x2\x6D4\x6D0\x3\x2\x2\x2\x6D4\x6D1"); + sb.Append("\x3\x2\x2\x2\x6D4\x6D2\x3\x2\x2\x2\x6D4\x6D3\x3\x2\x2\x2\x6D4"); + sb.Append("\x6D5\x3\x2\x2\x2\x6D5\x6F2\x3\x2\x2\x2\x6D6\x6DD\a\r\x2\x2"); + sb.Append("\x6D7\x6DE\x5\x1CE\xE8\x2\x6D8\x6DB\x5\x1E2\xF2\x2\x6D9\x6DB"); + sb.Append("\a\xC5\x2\x2\x6DA\x6D8\x3\x2\x2\x2\x6DA\x6D9\x3\x2\x2\x2\x6DB"); + sb.Append("\x6DC\x3\x2\x2\x2\x6DC\x6DE\a\x31\x2\x2\x6DD\x6D7\x3\x2\x2\x2"); + sb.Append("\x6DD\x6DA\x3\x2\x2\x2\x6DE\x6F3\x3\x2\x2\x2\x6DF\x6E0\aV\x2"); + sb.Append("\x2\x6E0\x6F3\x5\xFA~\x2\x6E1\x6E2\a\x1D\x2\x2\x6E2\x6E5\x5"); + sb.Append("\x122\x92\x2\x6E3\x6E4\a\x1E\x2\x2\x6E4\x6E6\x5\x46$\x2\x6E5"); + sb.Append("\x6E3\x3\x2\x2\x2\x6E5\x6E6\x3\x2\x2\x2\x6E6\x6F3\x3\x2\x2\x2"); + sb.Append("\x6E7\x6E8\a\x1D\x2\x2\x6E8\x6EB\a\x84\x2\x2\x6E9\x6EA\a\v\x2"); + sb.Append("\x2\x6EA\x6EC\x5\x122\x92\x2\x6EB\x6E9\x3\x2\x2\x2\x6EB\x6EC"); + sb.Append("\x3\x2\x2\x2\x6EC\x6EF\x3\x2\x2\x2\x6ED\x6EE\a\x1E\x2\x2\x6EE"); + sb.Append("\x6F0\x5\x46$\x2\x6EF\x6ED\x3\x2\x2\x2\x6EF\x6F0\x3\x2\x2\x2"); + sb.Append("\x6F0\x6F3\x3\x2\x2\x2\x6F1\x6F3\x3\x2\x2\x2\x6F2\x6D6\x3\x2"); + sb.Append("\x2\x2\x6F2\x6DF\x3\x2\x2\x2\x6F2\x6E1\x3\x2\x2\x2\x6F2\x6E7"); + sb.Append("\x3\x2\x2\x2\x6F2\x6F1\x3\x2\x2\x2\x6F3\x6F5\x3\x2\x2\x2\x6F4"); + sb.Append("\x6F6\x5\xF4{\x2\x6F5\x6F4\x3\x2\x2\x2\x6F5\x6F6\x3\x2\x2\x2"); + sb.Append("\x6F6\xF3\x3\x2\x2\x2\x6F7\x6F8\a\v\x2\x2\x6F8\x6F9\a\x1D\x2"); + sb.Append("\x2\x6F9\x6FC\a\x84\x2\x2\x6FA\x6FB\a\v\x2\x2\x6FB\x6FD\x5\x122"); + sb.Append("\x92\x2\x6FC\x6FA\x3\x2\x2\x2\x6FC\x6FD\x3\x2\x2\x2\x6FD\x700"); + sb.Append("\x3\x2\x2\x2\x6FE\x6FF\a\x1E\x2\x2\x6FF\x701\x5\x46$\x2\x700"); + sb.Append("\x6FE\x3\x2\x2\x2\x700\x701\x3\x2\x2\x2\x701\xF5\x3\x2\x2\x2"); + sb.Append("\x702\x707\ay\x2\x2\x703\x708\x5\x1CE\xE8\x2\x704\x705\x5\x1E2"); + sb.Append("\xF2\x2\x705\x706\a\x31\x2\x2\x706\x708\x3\x2\x2\x2\x707\x703"); + sb.Append("\x3\x2\x2\x2\x707\x704\x3\x2\x2\x2\x708\xF7\x3\x2\x2\x2\x709"); + sb.Append("\x70C\x5\x1E8\xF5\x2\x70A\x70C\a\xC5\x2\x2\x70B\x709\x3\x2\x2"); + sb.Append("\x2\x70B\x70A\x3\x2\x2\x2\x70C\x715\x3\x2\x2\x2\x70D\x710\a"); + sb.Append("\x99\x2\x2\x70E\x710\aq\x2\x2\x70F\x70D\x3\x2\x2\x2\x70F\x70E"); + sb.Append("\x3\x2\x2\x2\x710\x713\x3\x2\x2\x2\x711\x714\x5\x1E8\xF5\x2"); + sb.Append("\x712\x714\a\xC5\x2\x2\x713\x711\x3\x2\x2\x2\x713\x712\x3\x2"); + sb.Append("\x2\x2\x714\x716\x3\x2\x2\x2\x715\x70F\x3\x2\x2\x2\x715\x716"); + sb.Append("\x3\x2\x2\x2\x716\xF9\x3\x2\x2\x2\x717\x718\a\x92\x2\x2\x718"); + sb.Append("\x719\x5\x1AA\xD6\x2\x719\x71A\a\x93\x2\x2\x71A\xFB\x3\x2\x2"); + sb.Append("\x2\x71B\x71C\a\x1D\x2\x2\x71C\x71D\x5\x122\x92\x2\x71D\x71E"); + sb.Append("\a\x1E\x2\x2\x71E\x71F\x5\x122\x92\x2\x71F\xFD\x3\x2\x2\x2\x720"); + sb.Append("\x721\a\x1C\x2\x2\x721\x722\x5\x122\x92\x2\x722\xFF\x3\x2\x2"); + sb.Append("\x2\x723\x724\as\x2\x2\x724\x726\a\x92\x2\x2\x725\x727\x5\x102"); + sb.Append("\x82\x2\x726\x725\x3\x2\x2\x2\x726\x727\x3\x2\x2\x2\x727\x728"); + sb.Append("\x3\x2\x2\x2\x728\x72A\x5\x104\x83\x2\x729\x72B\x5\x108\x85"); + sb.Append("\x2\x72A\x729\x3\x2\x2\x2\x72A\x72B\x3\x2\x2\x2\x72B\x72D\x3"); + sb.Append("\x2\x2\x2\x72C\x72E\x5\x10C\x87\x2\x72D\x72C\x3\x2\x2\x2\x72D"); + sb.Append("\x72E\x3\x2\x2\x2\x72E\x72F\x3\x2\x2\x2\x72F\x731\x5\x10A\x86"); + sb.Append("\x2\x730\x732\x5\x10E\x88\x2\x731\x730\x3\x2\x2\x2\x731\x732"); + sb.Append("\x3\x2\x2\x2\x732\x734\x3\x2\x2\x2\x733\x735\x5\x11E\x90\x2"); + sb.Append("\x734\x733\x3\x2\x2\x2\x734\x735\x3\x2\x2\x2\x735\x736\x3\x2"); + sb.Append("\x2\x2\x736\x737\a\x93\x2\x2\x737\x101\x3\x2\x2\x2\x738\x739"); + sb.Append("\aw\x2\x2\x739\x73A\a)\x2\x2\x73A\x73F\x5\x122\x92\x2\x73B\x73C"); + sb.Append("\a\x99\x2\x2\x73C\x73E\x5\x122\x92\x2\x73D\x73B\x3\x2\x2\x2"); + sb.Append("\x73E\x741\x3\x2\x2\x2\x73F\x73D\x3\x2\x2\x2\x73F\x740\x3\x2"); + sb.Append("\x2\x2\x740\x103\x3\x2\x2\x2\x741\x73F\x3\x2\x2\x2\x742\x743"); + sb.Append("\au\x2\x2\x743\x748\x5\x106\x84\x2\x744\x745\a\x99\x2\x2\x745"); + sb.Append("\x747\x5\x106\x84\x2\x746\x744\x3\x2\x2\x2\x747\x74A\x3\x2\x2"); + sb.Append("\x2\x748\x746\x3\x2\x2\x2\x748\x749\x3\x2\x2\x2\x749\x105\x3"); + sb.Append("\x2\x2\x2\x74A\x748\x3\x2\x2\x2\x74B\x750\x5\x122\x92\x2\x74C"); + sb.Append("\x74E\a\x10\x2\x2\x74D\x74F\a\xC5\x2\x2\x74E\x74D\x3\x2\x2\x2"); + sb.Append("\x74E\x74F\x3\x2\x2\x2\x74F\x751\x3\x2\x2\x2\x750\x74C\x3\x2"); + sb.Append("\x2\x2\x750\x751\x3\x2\x2\x2\x751\x107\x3\x2\x2\x2\x752\x753"); + sb.Append("\a-\x2\x2\x753\x754\ax\x2\x2\x754\x109\x3\x2\x2\x2\x755\x756"); + sb.Append("\a\x41\x2\x2\x756\x757\a\x92\x2\x2\x757\x758\x5\x110\x89\x2"); + sb.Append("\x758\x759\a\x93\x2\x2\x759\x10B\x3\x2\x2\x2\x75A\x75B\ay\x2"); + sb.Append("\x2\x75B\x75C\x5\x1C8\xE5\x2\x75C\x75D\x5\x1C8\xE5\x2\x75D\x75E"); + sb.Append("\x5\x1C8\xE5\x2\x75E\x75F\x5\x1C8\xE5\x2\x75F\x760\x5\x1C8\xE5"); + sb.Append("\x2\x760\x10D\x3\x2\x2\x2\x761\x762\a\xC5\x2\x2\x762\x765\x5"); + sb.Append("\x1CE\xE8\x2\x763\x764\a\n\x2\x2\x764\x766\a\x84\x2\x2\x765"); + sb.Append("\x763\x3\x2\x2\x2\x765\x766\x3\x2\x2\x2\x766\x10F\x3\x2\x2\x2"); + sb.Append("\x767\x76C\x5\x112\x8A\x2\x768\x769\a\xB0\x2\x2\x769\x76B\x5"); + sb.Append("\x112\x8A\x2\x76A\x768\x3\x2\x2\x2\x76B\x76E\x3\x2\x2\x2\x76C"); + sb.Append("\x76A\x3\x2\x2\x2\x76C\x76D\x3\x2\x2\x2\x76D\x111\x3\x2\x2\x2"); + sb.Append("\x76E\x76C\x3\x2\x2\x2\x76F\x771\x5\x114\x8B\x2\x770\x76F\x3"); + sb.Append("\x2\x2\x2\x771\x772\x3\x2\x2\x2\x772\x770\x3\x2\x2\x2\x772\x773"); + sb.Append("\x3\x2\x2\x2\x773\x113\x3\x2\x2\x2\x774\x778\x5\x118\x8D\x2"); + sb.Append("\x775\x778\x5\x116\x8C\x2\x776\x778\x5\x11A\x8E\x2\x777\x774"); + sb.Append("\x3\x2\x2\x2\x777\x775\x3\x2\x2\x2\x777\x776\x3\x2\x2\x2\x778"); + sb.Append("\x115\x3\x2\x2\x2\x779\x77A\a\x92\x2\x2\x77A\x77B\x5\x110\x89"); + sb.Append("\x2\x77B\x77F\a\x93\x2\x2\x77C\x780\a\xA6\x2\x2\x77D\x780\a"); + sb.Append("\xA0\x2\x2\x77E\x780\a\x91\x2\x2\x77F\x77C\x3\x2\x2\x2\x77F"); + sb.Append("\x77D\x3\x2\x2\x2\x77F\x77E\x3\x2\x2\x2\x77F\x780\x3\x2\x2\x2"); + sb.Append("\x780\x782\x3\x2\x2\x2\x781\x783\x5\x11C\x8F\x2\x782\x781\x3"); + sb.Append("\x2\x2\x2\x782\x783\x3\x2\x2\x2\x783\x117\x3\x2\x2\x2\x784\x785"); + sb.Append("\at\x2\x2\x785\x786\a\x92\x2\x2\x786\x78B\x5\x110\x89\x2\x787"); + sb.Append("\x788\a\x99\x2\x2\x788\x78A\x5\x110\x89\x2\x789\x787\x3\x2\x2"); + sb.Append("\x2\x78A\x78D\x3\x2\x2\x2\x78B\x789\x3\x2\x2\x2\x78B\x78C\x3"); + sb.Append("\x2\x2\x2\x78C\x78E\x3\x2\x2\x2\x78D\x78B\x3\x2\x2\x2\x78E\x78F"); + sb.Append("\a\x93\x2\x2\x78F\x119\x3\x2\x2\x2\x790\x799\a\xC5\x2\x2\x791"); + sb.Append("\x795\a\xA6\x2\x2\x792\x795\a\xA0\x2\x2\x793\x795\a\x91\x2\x2"); + sb.Append("\x794\x791\x3\x2\x2\x2\x794\x792\x3\x2\x2\x2\x794\x793\x3\x2"); + sb.Append("\x2\x2\x795\x797\x3\x2\x2\x2\x796\x798\a\x91\x2\x2\x797\x796"); + sb.Append("\x3\x2\x2\x2\x797\x798\x3\x2\x2\x2\x798\x79A\x3\x2\x2\x2\x799"); + sb.Append("\x794\x3\x2\x2\x2\x799\x79A\x3\x2\x2\x2\x79A\x79C\x3\x2\x2\x2"); + sb.Append("\x79B\x79D\x5\x11C\x8F\x2\x79C\x79B\x3\x2\x2\x2\x79C\x79D\x3"); + sb.Append("\x2\x2\x2\x79D\x11B\x3\x2\x2\x2\x79E\x7A0\a\x96\x2\x2\x79F\x7A1"); + sb.Append("\x5\x122\x92\x2\x7A0\x79F\x3\x2\x2\x2\x7A0\x7A1\x3\x2\x2\x2"); + sb.Append("\x7A1\x7A3\x3\x2\x2\x2\x7A2\x7A4\a\x99\x2\x2\x7A3\x7A2\x3\x2"); + sb.Append("\x2\x2\x7A3\x7A4\x3\x2\x2\x2\x7A4\x7A6\x3\x2\x2\x2\x7A5\x7A7"); + sb.Append("\x5\x122\x92\x2\x7A6\x7A5\x3\x2\x2\x2\x7A6\x7A7\x3\x2\x2\x2"); + sb.Append("\x7A7\x7A8\x3\x2\x2\x2\x7A8\x7A9\a\x97\x2\x2\x7A9\x11D\x3\x2"); + sb.Append("\x2\x2\x7AA\x7AB\av\x2\x2\x7AB\x7B0\x5\x120\x91\x2\x7AC\x7AD"); + sb.Append("\a\x99\x2\x2\x7AD\x7AF\x5\x120\x91\x2\x7AE\x7AC\x3\x2\x2\x2"); + sb.Append("\x7AF\x7B2\x3\x2\x2\x2\x7B0\x7AE\x3\x2\x2\x2\x7B0\x7B1\x3\x2"); + sb.Append("\x2\x2\x7B1\x11F\x3\x2\x2\x2\x7B2\x7B0\x3\x2\x2\x2\x7B3\x7B4"); + sb.Append("\a\xC5\x2\x2\x7B4\x7B5\a\x10\x2\x2\x7B5\x7B6\x5\x122\x92\x2"); + sb.Append("\x7B6\x121\x3\x2\x2\x2\x7B7\x7B8\x5\x124\x93\x2\x7B8\x123\x3"); + sb.Append("\x2\x2\x2\x7B9\x7BA\b\x93\x1\x2\x7BA\x7BC\a\x1B\x2\x2\x7BB\x7BD"); + sb.Append("\x5\xFC\x7F\x2\x7BC\x7BB\x3\x2\x2\x2\x7BD\x7BE\x3\x2\x2\x2\x7BE"); + sb.Append("\x7BC\x3\x2\x2\x2\x7BE\x7BF\x3\x2\x2\x2\x7BF\x7C1\x3\x2\x2\x2"); + sb.Append("\x7C0\x7C2\x5\xFE\x80\x2\x7C1\x7C0\x3\x2\x2\x2\x7C1\x7C2\x3"); + sb.Append("\x2\x2\x2\x7C2\x7C3\x3\x2\x2\x2\x7C3\x7C4\a\x1F\x2\x2\x7C4\x7C5"); + sb.Append("\b\x93\x1\x2\x7C5\x7D6\x3\x2\x2\x2\x7C6\x7C7\b\x93\x1\x2\x7C7"); + sb.Append("\x7C8\a\x1B\x2\x2\x7C8\x7CA\x5\x122\x92\x2\x7C9\x7CB\x5\xFC"); + sb.Append("\x7F\x2\x7CA\x7C9\x3\x2\x2\x2\x7CB\x7CC\x3\x2\x2\x2\x7CC\x7CA"); + sb.Append("\x3\x2\x2\x2\x7CC\x7CD\x3\x2\x2\x2\x7CD\x7CF\x3\x2\x2\x2\x7CE"); + sb.Append("\x7D0\x5\xFE\x80\x2\x7CF\x7CE\x3\x2\x2\x2\x7CF\x7D0\x3\x2\x2"); + sb.Append("\x2\x7D0\x7D1\x3\x2\x2\x2\x7D1\x7D2\a\x1F\x2\x2\x7D2\x7D3\b"); + sb.Append("\x93\x1\x2\x7D3\x7D6\x3\x2\x2\x2\x7D4\x7D6\x5\x126\x94\x2\x7D5"); + sb.Append("\x7B9\x3\x2\x2\x2\x7D5\x7C6\x3\x2\x2\x2\x7D5\x7D4\x3\x2\x2\x2"); + sb.Append("\x7D6\x125\x3\x2\x2\x2\x7D7\x7DC\x5\x128\x95\x2\x7D8\x7D9\a"); + sb.Append("\n\x2\x2\x7D9\x7DB\x5\x128\x95\x2\x7DA\x7D8\x3\x2\x2\x2\x7DB"); + sb.Append("\x7DE\x3\x2\x2\x2\x7DC\x7DA\x3\x2\x2\x2\x7DC\x7DD\x3\x2\x2\x2"); + sb.Append("\x7DD\x127\x3\x2\x2\x2\x7DE\x7DC\x3\x2\x2\x2\x7DF\x7E4\x5\x12A"); + sb.Append("\x96\x2\x7E0\x7E1\a\v\x2\x2\x7E1\x7E3\x5\x12A\x96\x2\x7E2\x7E0"); + sb.Append("\x3\x2\x2\x2\x7E3\x7E6\x3\x2\x2\x2\x7E4\x7E2\x3\x2\x2\x2\x7E4"); + sb.Append("\x7E5\x3\x2\x2\x2\x7E5\x129\x3\x2\x2\x2\x7E6\x7E4\x3\x2\x2\x2"); + sb.Append("\x7E7\x7EC\x5\x12C\x97\x2\x7E8\x7E9\t\x6\x2\x2\x7E9\x7EB\x5"); + sb.Append("\x12C\x97\x2\x7EA\x7E8\x3\x2\x2\x2\x7EB\x7EE\x3\x2\x2\x2\x7EC"); + sb.Append("\x7EA\x3\x2\x2\x2\x7EC\x7ED\x3\x2\x2\x2\x7ED\x12B\x3\x2\x2\x2"); + sb.Append("\x7EE\x7EC\x3\x2\x2\x2\x7EF\x7F3\x5\x12E\x98\x2\x7F0\x7F1\a"); + sb.Append("\f\x2\x2\x7F1\x7F3\x5\x12E\x98\x2\x7F2\x7EF\x3\x2\x2\x2\x7F2"); + sb.Append("\x7F0\x3\x2\x2\x2\x7F3\x12D\x3\x2\x2\x2\x7F4\x80F\x5\x130\x99"); + sb.Append("\x2\x7F5\x7FC\a\x8F\x2\x2\x7F6\x7FC\a(\x2\x2\x7F7\x7F8\a(\x2"); + sb.Append("\x2\x7F8\x7FC\a\f\x2\x2\x7F9\x7FC\a\x90\x2\x2\x7FA\x7FC\a\x9D"); + sb.Append("\x2\x2\x7FB\x7F5\x3\x2\x2\x2\x7FB\x7F6\x3\x2\x2\x2\x7FB\x7F7"); + sb.Append("\x3\x2\x2\x2\x7FB\x7F9\x3\x2\x2\x2\x7FB\x7FA\x3\x2\x2\x2\x7FC"); + sb.Append("\x80B\x3\x2\x2\x2\x7FD\x80C\x5\x130\x99\x2\x7FE\x802\a.\x2\x2"); + sb.Append("\x7FF\x802\a/\x2\x2\x800\x802\a-\x2\x2\x801\x7FE\x3\x2\x2\x2"); + sb.Append("\x801\x7FF\x3\x2\x2\x2\x801\x800\x3\x2\x2\x2\x802\x809\x3\x2"); + sb.Append("\x2\x2\x803\x805\a\x92\x2\x2\x804\x806\x5\x1A8\xD5\x2\x805\x804"); + sb.Append("\x3\x2\x2\x2\x805\x806\x3\x2\x2\x2\x806\x807\x3\x2\x2\x2\x807"); + sb.Append("\x80A\a\x93\x2\x2\x808\x80A\x5\x144\xA3\x2\x809\x803\x3\x2\x2"); + sb.Append("\x2\x809\x808\x3\x2\x2\x2\x80A\x80C\x3\x2\x2\x2\x80B\x7FD\x3"); + sb.Append("\x2\x2\x2\x80B\x801\x3\x2\x2\x2\x80C\x80E\x3\x2\x2\x2\x80D\x7FB"); + sb.Append("\x3\x2\x2\x2\x80E\x811\x3\x2\x2\x2\x80F\x80D\x3\x2\x2\x2\x80F"); + sb.Append("\x810\x3\x2\x2\x2\x810\x12F\x3\x2\x2\x2\x811\x80F\x3\x2\x2\x2"); + sb.Append("\x812\x854\x5\x134\x9B\x2\x813\x818\a\xAD\x2\x2\x814\x818\a"); + sb.Append("\xAB\x2\x2\x815\x818\a\xAC\x2\x2\x816\x818\a\xAA\x2\x2\x817"); + sb.Append("\x813\x3\x2\x2\x2\x817\x814\x3\x2\x2\x2\x817\x815\x3\x2\x2\x2"); + sb.Append("\x817\x816\x3\x2\x2\x2\x818\x827\x3\x2\x2\x2\x819\x828\x5\x134"); + sb.Append("\x9B\x2\x81A\x81E\a.\x2\x2\x81B\x81E\a/\x2\x2\x81C\x81E\a-\x2"); + sb.Append("\x2\x81D\x81A\x3\x2\x2\x2\x81D\x81B\x3\x2\x2\x2\x81D\x81C\x3"); + sb.Append("\x2\x2\x2\x81E\x825\x3\x2\x2\x2\x81F\x821\a\x92\x2\x2\x820\x822"); + sb.Append("\x5\x1A8\xD5\x2\x821\x820\x3\x2\x2\x2\x821\x822\x3\x2\x2\x2"); + sb.Append("\x822\x823\x3\x2\x2\x2\x823\x826\a\x93\x2\x2\x824\x826\x5\x144"); + sb.Append("\xA3\x2\x825\x81F\x3\x2\x2\x2\x825\x824\x3\x2\x2\x2\x826\x828"); + sb.Append("\x3\x2\x2\x2\x827\x819\x3\x2\x2\x2\x827\x81D\x3\x2\x2\x2\x828"); + sb.Append("\x82A\x3\x2\x2\x2\x829\x817\x3\x2\x2\x2\x82A\x82D\x3\x2\x2\x2"); + sb.Append("\x82B\x829\x3\x2\x2\x2\x82B\x82C\x3\x2\x2\x2\x82C\x855\x3\x2"); + sb.Append("\x2\x2\x82D\x82B\x3\x2\x2\x2\x82E\x830\a\f\x2\x2\x82F\x82E\x3"); + sb.Append("\x2\x2\x2\x82F\x830\x3\x2\x2\x2\x830\x852\x3\x2\x2\x2\x831\x834"); + sb.Append("\a\x5\x2\x2\x832\x835\a\x92\x2\x2\x833\x835\a\x94\x2\x2\x834"); + sb.Append("\x832\x3\x2\x2\x2\x834\x833\x3\x2\x2\x2\x835\x836\x3\x2\x2\x2"); + sb.Append("\x836\x840\x5\x122\x92\x2\x837\x838\a\x98\x2\x2\x838\x841\x5"); + sb.Append("\x122\x92\x2\x839\x83A\a\x99\x2\x2\x83A\x83C\x5\x122\x92\x2"); + sb.Append("\x83B\x839\x3\x2\x2\x2\x83C\x83F\x3\x2\x2\x2\x83D\x83B\x3\x2"); + sb.Append("\x2\x2\x83D\x83E\x3\x2\x2\x2\x83E\x841\x3\x2\x2\x2\x83F\x83D"); + sb.Append("\x3\x2\x2\x2\x840\x837\x3\x2\x2\x2\x840\x83D\x3\x2\x2\x2\x841"); + sb.Append("\x844\x3\x2\x2\x2\x842\x845\a\x93\x2\x2\x843\x845\a\x95\x2\x2"); + sb.Append("\x844\x842\x3\x2\x2\x2\x844\x843\x3\x2\x2\x2\x845\x853\x3\x2"); + sb.Append("\x2\x2\x846\x847\a\x5\x2\x2\x847\x853\x5\x132\x9A\x2\x848\x849"); + sb.Append("\a\x6\x2\x2\x849\x853\x5\x164\xB3\x2\x84A\x84B\a\a\x2\x2\x84B"); + sb.Append("\x84E\x5\x134\x9B\x2\x84C\x84D\a\t\x2\x2\x84D\x84F\x5\x1EA\xF6"); + sb.Append("\x2\x84E\x84C\x3\x2\x2\x2\x84E\x84F\x3\x2\x2\x2\x84F\x853\x3"); + sb.Append("\x2\x2\x2\x850\x851\a\b\x2\x2\x851\x853\x5\x134\x9B\x2\x852"); + sb.Append("\x831\x3\x2\x2\x2\x852\x846\x3\x2\x2\x2\x852\x848\x3\x2\x2\x2"); + sb.Append("\x852\x84A\x3\x2\x2\x2\x852\x850\x3\x2\x2\x2\x853\x855\x3\x2"); + sb.Append("\x2\x2\x854\x82B\x3\x2\x2\x2\x854\x82F\x3\x2\x2\x2\x855\x131"); + sb.Append("\x3\x2\x2\x2\x856\x857\x5\x148\xA5\x2\x857\x133\x3\x2\x2\x2"); + sb.Append("\x858\x862\x5\x136\x9C\x2\x859\x85A\a\xB2\x2\x2\x85A\x85F\x5"); + sb.Append("\x136\x9C\x2\x85B\x85C\a\xB2\x2\x2\x85C\x85E\x5\x136\x9C\x2"); + sb.Append("\x85D\x85B\x3\x2\x2\x2\x85E\x861\x3\x2\x2\x2\x85F\x85D\x3\x2"); + sb.Append("\x2\x2\x85F\x860\x3\x2\x2\x2\x860\x863\x3\x2\x2\x2\x861\x85F"); + sb.Append("\x3\x2\x2\x2\x862\x859\x3\x2\x2\x2\x862\x863\x3\x2\x2\x2\x863"); + sb.Append("\x135\x3\x2\x2\x2\x864\x869\x5\x138\x9D\x2\x865\x866\t\a\x2"); + sb.Append("\x2\x866\x868\x5\x138\x9D\x2\x867\x865\x3\x2\x2\x2\x868\x86B"); + sb.Append("\x3\x2\x2\x2\x869\x867\x3\x2\x2\x2\x869\x86A\x3\x2\x2\x2\x86A"); + sb.Append("\x137\x3\x2\x2\x2\x86B\x869\x3\x2\x2\x2\x86C\x871\x5\x13A\x9E"); + sb.Append("\x2\x86D\x86E\t\b\x2\x2\x86E\x870\x5\x13A\x9E\x2\x86F\x86D\x3"); + sb.Append("\x2\x2\x2\x870\x873\x3\x2\x2\x2\x871\x86F\x3\x2\x2\x2\x871\x872"); + sb.Append("\x3\x2\x2\x2\x872\x139\x3\x2\x2\x2\x873\x871\x3\x2\x2\x2\x874"); + sb.Append("\x875\a\xA3\x2\x2\x875\x8B0\x5\x1C2\xE2\x2\x876\x8B0\x5\x1E6"); + sb.Append("\xF4\x2\x877\x8B0\x5\x13C\x9F\x2\x878\x879\a\x92\x2\x2\x879"); + sb.Append("\x87A\x5\x122\x92\x2\x87A\x87C\a\x93\x2\x2\x87B\x87D\x5\x13E"); + sb.Append("\xA0\x2\x87C\x87B\x3\x2\x2\x2\x87C\x87D\x3\x2\x2\x2\x87D\x8B0"); + sb.Append("\x3\x2\x2\x2\x87E\x8B0\x5\x14E\xA8\x2\x87F\x8B0\x5\x152\xAA"); + sb.Append("\x2\x880\x8B0\x5\x14C\xA7\x2\x881\x8B0\x5\x142\xA2\x2\x882\x8B0"); + sb.Append("\x5\x146\xA4\x2\x883\x884\a\x80\x2\x2\x884\x885\a\x96\x2\x2"); + sb.Append("\x885\x88A\x5\x140\xA1\x2\x886\x887\a\x99\x2\x2\x887\x889\x5"); + sb.Append("\x140\xA1\x2\x888\x886\x3\x2\x2\x2\x889\x88C\x3\x2\x2\x2\x88A"); + sb.Append("\x888\x3\x2\x2\x2\x88A\x88B\x3\x2\x2\x2\x88B\x88D\x3\x2\x2\x2"); + sb.Append("\x88C\x88A\x3\x2\x2\x2\x88D\x88E\a\x97\x2\x2\x88E\x8B0\x3\x2"); + sb.Append("\x2\x2\x88F\x890\a\x80\x2\x2\x890\x891\x5\x198\xCD\x2\x891\x89A"); + sb.Append("\a\x92\x2\x2\x892\x897\x5\x122\x92\x2\x893\x894\a\x99\x2\x2"); + sb.Append("\x894\x896\x5\x122\x92\x2\x895\x893\x3\x2\x2\x2\x896\x899\x3"); + sb.Append("\x2\x2\x2\x897\x895\x3\x2\x2\x2\x897\x898\x3\x2\x2\x2\x898\x89B"); + sb.Append("\x3\x2\x2\x2\x899\x897\x3\x2\x2\x2\x89A\x892\x3\x2\x2\x2\x89A"); + sb.Append("\x89B\x3\x2\x2\x2\x89B\x89C\x3\x2\x2\x2\x89C\x89E\a\x93\x2\x2"); + sb.Append("\x89D\x89F\x5\x13E\xA0\x2\x89E\x89D\x3\x2\x2\x2\x89E\x89F\x3"); + sb.Append("\x2\x2\x2\x89F\x8B0\x3\x2\x2\x2\x8A0\x8A1\a\xC5\x2\x2\x8A1\x8A2"); + sb.Append("\a\x94\x2\x2\x8A2\x8A7\x5\x122\x92\x2\x8A3\x8A4\a\x99\x2\x2"); + sb.Append("\x8A4\x8A6\x5\x122\x92\x2\x8A5\x8A3\x3\x2\x2\x2\x8A6\x8A9\x3"); + sb.Append("\x2\x2\x2\x8A7\x8A5\x3\x2\x2\x2\x8A7\x8A8\x3\x2\x2\x2\x8A8\x8AA"); + sb.Append("\x3\x2\x2\x2\x8A9\x8A7\x3\x2\x2\x2\x8AA\x8AC\a\x95\x2\x2\x8AB"); + sb.Append("\x8AD\x5\x13E\xA0\x2\x8AC\x8AB\x3\x2\x2\x2\x8AC\x8AD\x3\x2\x2"); + sb.Append("\x2\x8AD\x8B0\x3\x2\x2\x2\x8AE\x8B0\x5\x1EE\xF8\x2\x8AF\x874"); + sb.Append("\x3\x2\x2\x2\x8AF\x876\x3\x2\x2\x2\x8AF\x877\x3\x2\x2\x2\x8AF"); + sb.Append("\x878\x3\x2\x2\x2\x8AF\x87E\x3\x2\x2\x2\x8AF\x87F\x3\x2\x2\x2"); + sb.Append("\x8AF\x880\x3\x2\x2\x2\x8AF\x881\x3\x2\x2\x2\x8AF\x882\x3\x2"); + sb.Append("\x2\x2\x8AF\x883\x3\x2\x2\x2\x8AF\x88F\x3\x2\x2\x2\x8AF\x8A0"); + sb.Append("\x3\x2\x2\x2\x8AF\x8AE\x3\x2\x2\x2\x8B0\x13B\x3\x2\x2\x2\x8B1"); + sb.Append("\x8B3\x5\x1E4\xF3\x2\x8B2\x8B4\x5\x13E\xA0\x2\x8B3\x8B2\x3\x2"); + sb.Append("\x2\x2\x8B3\x8B4\x3\x2\x2\x2\x8B4\x13D\x3\x2\x2\x2\x8B5\x8B6"); + sb.Append("\a\xB7\x2\x2\x8B6\x8BB\x5\x158\xAD\x2\x8B7\x8B8\a\xB7\x2\x2"); + sb.Append("\x8B8\x8BA\x5\x158\xAD\x2\x8B9\x8B7\x3\x2\x2\x2\x8BA\x8BD\x3"); + sb.Append("\x2\x2\x2\x8BB\x8B9\x3\x2\x2\x2\x8BB\x8BC\x3\x2\x2\x2\x8BC\x13F"); + sb.Append("\x3\x2\x2\x2\x8BD\x8BB\x3\x2\x2\x2\x8BE\x8C1\x5\x1C2\xE2\x2"); + sb.Append("\x8BF\x8C0\a\x8F\x2\x2\x8C0\x8C2\x5\x122\x92\x2\x8C1\x8BF\x3"); + sb.Append("\x2\x2\x2\x8C1\x8C2\x3\x2\x2\x2\x8C2\x141\x3\x2\x2\x2\x8C3\x8C5"); + sb.Append("\x5\x148\xA5\x2\x8C4\x8C6\x5\x13E\xA0\x2\x8C5\x8C4\x3\x2\x2"); + sb.Append("\x2\x8C5\x8C6\x3\x2\x2\x2\x8C6\x143\x3\x2\x2\x2\x8C7\x8C8\x5"); + sb.Append("\x148\xA5\x2\x8C8\x145\x3\x2\x2\x2\x8C9\x8CA\aI\x2\x2\x8CA\x8CB"); + sb.Append("\x5\x148\xA5\x2\x8CB\x147\x3\x2\x2\x2\x8CC\x8CD\a\x92\x2\x2"); + sb.Append("\x8CD\x8CF\a\x1A\x2\x2\x8CE\x8D0\a,\x2\x2\x8CF\x8CE\x3\x2\x2"); + sb.Append("\x2\x8CF\x8D0\x3\x2\x2\x2\x8D0\x8D1\x3\x2\x2\x2\x8D1\x8D2\x5"); + sb.Append("\xC4\x63\x2\x8D2\x8D3\a \x2\x2\x8D3\x8D6\x5\x14A\xA6\x2\x8D4"); + sb.Append("\x8D5\a\xF\x2\x2\x8D5\x8D7\x5\xC0\x61\x2\x8D6\x8D4\x3\x2\x2"); + sb.Append("\x2\x8D6\x8D7\x3\x2\x2\x2\x8D7\x8DB\x3\x2\x2\x2\x8D8\x8D9\a"); + sb.Append("*\x2\x2\x8D9\x8DA\a)\x2\x2\x8DA\x8DC\x5\xE0q\x2\x8DB\x8D8\x3"); + sb.Append("\x2\x2\x2\x8DB\x8DC\x3\x2\x2\x2\x8DC\x8DF\x3\x2\x2\x2\x8DD\x8DE"); + sb.Append("\a+\x2\x2\x8DE\x8E0\x5\xF0y\x2\x8DF\x8DD\x3\x2\x2\x2\x8DF\x8E0"); + sb.Append("\x3\x2\x2\x2\x8E0\x8E1\x3\x2\x2\x2\x8E1\x8E2\a\x93\x2\x2\x8E2"); + sb.Append("\x149\x3\x2\x2\x2\x8E3\x8E5\x5\x184\xC3\x2\x8E4\x8E6\x5\xD8"); + sb.Append("m\x2\x8E5\x8E4\x3\x2\x2\x2\x8E5\x8E6\x3\x2\x2\x2\x8E6\x8EA\x3"); + sb.Append("\x2\x2\x2\x8E7\x8E8\a\x10\x2\x2\x8E8\x8EB\a\xC5\x2\x2\x8E9\x8EB"); + sb.Append("\a\xC5\x2\x2\x8EA\x8E7\x3\x2\x2\x2\x8EA\x8E9\x3\x2\x2\x2\x8EA"); + sb.Append("\x8EB\x3\x2\x2\x2\x8EB\x8EE\x3\x2\x2\x2\x8EC\x8EF\a?\x2\x2\x8ED"); + sb.Append("\x8EF\a@\x2\x2\x8EE\x8EC\x3\x2\x2\x2\x8EE\x8ED\x3\x2\x2\x2\x8EE"); + sb.Append("\x8EF\x3\x2\x2\x2\x8EF\x14B\x3\x2\x2\x2\x8F0\x8F9\a\x96\x2\x2"); + sb.Append("\x8F1\x8F6\x5\x122\x92\x2\x8F2\x8F3\a\x99\x2\x2\x8F3\x8F5\x5"); + sb.Append("\x122\x92\x2\x8F4\x8F2\x3\x2\x2\x2\x8F5\x8F8\x3\x2\x2\x2\x8F6"); + sb.Append("\x8F4\x3\x2\x2\x2\x8F6\x8F7\x3\x2\x2\x2\x8F7\x8FA\x3\x2\x2\x2"); + sb.Append("\x8F8\x8F6\x3\x2\x2\x2\x8F9\x8F1\x3\x2\x2\x2\x8F9\x8FA\x3\x2"); + sb.Append("\x2\x2\x8FA\x8FB\x3\x2\x2\x2\x8FB\x8FD\a\x97\x2\x2\x8FC\x8FE"); + sb.Append("\x5\x13E\xA0\x2\x8FD\x8FC\x3\x2\x2\x2\x8FD\x8FE\x3\x2\x2\x2"); + sb.Append("\x8FE\x14D\x3\x2\x2\x2\x8FF\x900\a\x11\x2\x2\x900\x902\a\x92"); + sb.Append("\x2\x2\x901\x903\t\t\x2\x2\x902\x901\x3\x2\x2\x2\x902\x903\x3"); + sb.Append("\x2\x2\x2\x903\x904\x3\x2\x2\x2\x904\x905\x5\x19C\xCF\x2\x905"); + sb.Append("\x906\a\x93\x2\x2\x906\x9A3\x3\x2\x2\x2\x907\x908\a\x12\x2\x2"); + sb.Append("\x908\x90A\a\x92\x2\x2\x909\x90B\t\t\x2\x2\x90A\x909\x3\x2\x2"); + sb.Append("\x2\x90A\x90B\x3\x2\x2\x2\x90B\x90C\x3\x2\x2\x2\x90C\x90D\x5"); + sb.Append("\x19C\xCF\x2\x90D\x90E\a\x93\x2\x2\x90E\x9A3\x3\x2\x2\x2\x90F"); + sb.Append("\x910\a\x19\x2\x2\x910\x913\a\x92\x2\x2\x911\x914\a-\x2\x2\x912"); + sb.Append("\x914\a,\x2\x2\x913\x911\x3\x2\x2\x2\x913\x912\x3\x2\x2\x2\x913"); + sb.Append("\x914\x3\x2\x2\x2\x914\x915\x3\x2\x2\x2\x915\x916\x5\x19C\xCF"); + sb.Append("\x2\x916\x917\a\x93\x2\x2\x917\x9A3\x3\x2\x2\x2\x918\x919\a"); + sb.Append("\x16\x2\x2\x919\x91B\a\x92\x2\x2\x91A\x91C\t\t\x2\x2\x91B\x91A"); + sb.Append("\x3\x2\x2\x2\x91B\x91C\x3\x2\x2\x2\x91C\x91D\x3\x2\x2\x2\x91D"); + sb.Append("\x91E\x5\x19C\xCF\x2\x91E\x91F\a\x93\x2\x2\x91F\x9A3\x3\x2\x2"); + sb.Append("\x2\x920\x921\a\x17\x2\x2\x921\x923\a\x92\x2\x2\x922\x924\t"); + sb.Append("\t\x2\x2\x923\x922\x3\x2\x2\x2\x923\x924\x3\x2\x2\x2\x924\x925"); + sb.Append("\x3\x2\x2\x2\x925\x926\x5\x19C\xCF\x2\x926\x927\a\x93\x2\x2"); + sb.Append("\x927\x9A3\x3\x2\x2\x2\x928\x929\a\x18\x2\x2\x929\x92B\a\x92"); + sb.Append("\x2\x2\x92A\x92C\t\t\x2\x2\x92B\x92A\x3\x2\x2\x2\x92B\x92C\x3"); + sb.Append("\x2\x2\x2\x92C\x92D\x3\x2\x2\x2\x92D\x92E\x5\x19C\xCF\x2\x92E"); + sb.Append("\x92F\a\x93\x2\x2\x92F\x9A3\x3\x2\x2\x2\x930\x9A3\x5\x150\xA9"); + sb.Append("\x2\x931\x932\a\x15\x2\x2\x932\x933\a\x92\x2\x2\x933\x934\x5"); + sb.Append("\x122\x92\x2\x934\x935\a\x99\x2\x2\x935\x93A\x5\x122\x92\x2"); + sb.Append("\x936\x937\a\x99\x2\x2\x937\x939\x5\x122\x92\x2\x938\x936\x3"); + sb.Append("\x2\x2\x2\x939\x93C\x3\x2\x2\x2\x93A\x938\x3\x2\x2\x2\x93A\x93B"); + sb.Append("\x3\x2\x2\x2\x93B\x93D\x3\x2\x2\x2\x93C\x93A\x3\x2\x2\x2\x93D"); + sb.Append("\x93E\a\x93\x2\x2\x93E\x9A3\x3\x2\x2\x2\x93F\x940\a\x44\x2\x2"); + sb.Append("\x940\x941\a\x92\x2\x2\x941\x944\x5\x122\x92\x2\x942\x943\a"); + sb.Append("\x99\x2\x2\x943\x945\x5\x122\x92\x2\x944\x942\x3\x2\x2\x2\x944"); + sb.Append("\x945\x3\x2\x2\x2\x945\x946\x3\x2\x2\x2\x946\x948\a\x93\x2\x2"); + sb.Append("\x947\x949\x5\x13E\xA0\x2\x948\x947\x3\x2\x2\x2\x948\x949\x3"); + sb.Append("\x2\x2\x2\x949\x9A3\x3\x2\x2\x2\x94A\x94B\a\x45\x2\x2\x94B\x94C"); + sb.Append("\a\x92\x2\x2\x94C\x94F\x5\x122\x92\x2\x94D\x94E\a\x99\x2\x2"); + sb.Append("\x94E\x950\x5\x122\x92\x2\x94F\x94D\x3\x2\x2\x2\x94F\x950\x3"); + sb.Append("\x2\x2\x2\x950\x951\x3\x2\x2\x2\x951\x953\a\x93\x2\x2\x952\x954"); + sb.Append("\x5\x13E\xA0\x2\x953\x952\x3\x2\x2\x2\x953\x954\x3\x2\x2\x2"); + sb.Append("\x954\x9A3\x3\x2\x2\x2\x955\x956\a\x46\x2\x2\x956\x957\a\x92"); + sb.Append("\x2\x2\x957\x958\x5\x122\x92\x2\x958\x959\a\x93\x2\x2\x959\x9A3"); + sb.Append("\x3\x2\x2\x2\x95A\x95B\aG\x2\x2\x95B\x95C\a\x92\x2\x2\x95C\x95D"); + sb.Append("\x5\x122\x92\x2\x95D\x95F\a\x93\x2\x2\x95E\x960\x5\x13E\xA0"); + sb.Append("\x2\x95F\x95E\x3\x2\x2\x2\x95F\x960\x3\x2\x2\x2\x960\x9A3\x3"); + sb.Append("\x2\x2\x2\x961\x962\aH\x2\x2\x962\x963\a\x92\x2\x2\x963\x964"); + sb.Append("\x5\x122\x92\x2\x964\x965\a\x99\x2\x2\x965\x966\x5\x1C2\xE2"); + sb.Append("\x2\x966\x967\a\x93\x2\x2\x967\x9A3\x3\x2\x2\x2\x968\x969\a"); + sb.Append("\x88\x2\x2\x969\x96A\a\x92\x2\x2\x96A\x96B\x5\x122\x92\x2\x96B"); + sb.Append("\x96C\a\x93\x2\x2\x96C\x9A3\x3\x2\x2\x2\x96D\x96E\a\x89\x2\x2"); + sb.Append("\x96E\x96F\a\x92\x2\x2\x96F\x970\x5\x1A8\xD5\x2\x970\x971\a"); + sb.Append("\x93\x2\x2\x971\x9A3\x3\x2\x2\x2\x972\x973\aL\x2\x2\x973\x974"); + sb.Append("\a\x92\x2\x2\x974\x975\x5\x122\x92\x2\x975\x976\a\x99\x2\x2"); + sb.Append("\x976\x97B\x5\x198\xCD\x2\x977\x978\a\x99\x2\x2\x978\x97A\x5"); + sb.Append("\x198\xCD\x2\x979\x977\x3\x2\x2\x2\x97A\x97D\x3\x2\x2\x2\x97B"); + sb.Append("\x979\x3\x2\x2\x2\x97B\x97C\x3\x2\x2\x2\x97C\x97E\x3\x2\x2\x2"); + sb.Append("\x97D\x97B\x3\x2\x2\x2\x97E\x97F\a\x93\x2\x2\x97F\x9A3\x3\x2"); + sb.Append("\x2\x2\x980\x981\aM\x2\x2\x981\x982\a\x92\x2\x2\x982\x983\x5"); + sb.Append("\x122\x92\x2\x983\x984\a\x93\x2\x2\x984\x9A3\x3\x2\x2\x2\x985"); + sb.Append("\x986\aN\x2\x2\x986\x987\a\x92\x2\x2\x987\x988\x5\x122\x92\x2"); + sb.Append("\x988\x989\t\n\x2\x2\x989\x98C\x5\x198\xCD\x2\x98A\x98B\a\x99"); + sb.Append("\x2\x2\x98B\x98D\x5\x1A4\xD3\x2\x98C\x98A\x3\x2\x2\x2\x98C\x98D"); + sb.Append("\x3\x2\x2\x2\x98D\x98E\x3\x2\x2\x2\x98E\x990\a\x93\x2\x2\x98F"); + sb.Append("\x991\x5\x13E\xA0\x2\x990\x98F\x3\x2\x2\x2\x990\x991\x3\x2\x2"); + sb.Append("\x2\x991\x9A3\x3\x2\x2\x2\x992\x993\aI\x2\x2\x993\x994\a\x92"); + sb.Append("\x2\x2\x994\x995\x5\x1C2\xE2\x2\x995\x996\a\x93\x2\x2\x996\x9A3"); + sb.Append("\x3\x2\x2\x2\x997\x99A\aO\x2\x2\x998\x999\a\x92\x2\x2\x999\x99B"); + sb.Append("\a\x93\x2\x2\x99A\x998\x3\x2\x2\x2\x99A\x99B\x3\x2\x2\x2\x99B"); + sb.Append("\x99D\x3\x2\x2\x2\x99C\x99E\x5\x13E\xA0\x2\x99D\x99C\x3\x2\x2"); + sb.Append("\x2\x99D\x99E\x3\x2\x2\x2\x99E\x9A3\x3\x2\x2\x2\x99F\x9A0\a"); + sb.Append(";\x2\x2\x9A0\x9A1\a\x92\x2\x2\x9A1\x9A3\a\x93\x2\x2\x9A2\x8FF"); + sb.Append("\x3\x2\x2\x2\x9A2\x907\x3\x2\x2\x2\x9A2\x90F\x3\x2\x2\x2\x9A2"); + sb.Append("\x918\x3\x2\x2\x2\x9A2\x920\x3\x2\x2\x2\x9A2\x928\x3\x2\x2\x2"); + sb.Append("\x9A2\x930\x3\x2\x2\x2\x9A2\x931\x3\x2\x2\x2\x9A2\x93F\x3\x2"); + sb.Append("\x2\x2\x9A2\x94A\x3\x2\x2\x2\x9A2\x955\x3\x2\x2\x2\x9A2\x95A"); + sb.Append("\x3\x2\x2\x2\x9A2\x961\x3\x2\x2\x2\x9A2\x968\x3\x2\x2\x2\x9A2"); + sb.Append("\x96D\x3\x2\x2\x2\x9A2\x972\x3\x2\x2\x2\x9A2\x980\x3\x2\x2\x2"); + sb.Append("\x9A2\x985\x3\x2\x2\x2\x9A2\x992\x3\x2\x2\x2\x9A2\x997\x3\x2"); + sb.Append("\x2\x2\x9A2\x99F\x3\x2\x2\x2\x9A3\x14F\x3\x2\x2\x2\x9A4\x9A8"); + sb.Append("\a\x32\x2\x2\x9A5\x9A8\a\x33\x2\x2\x9A6\x9A8\a\x4\x2\x2\x9A7"); + sb.Append("\x9A4\x3\x2\x2\x2\x9A7\x9A5\x3\x2\x2\x2\x9A7\x9A6\x3\x2\x2\x2"); + sb.Append("\x9A8\x9A9\x3\x2\x2\x2\x9A9\x9AB\a\x92\x2\x2\x9AA\x9AC\x5\x19C"); + sb.Append("\xCF\x2\x9AB\x9AA\x3\x2\x2\x2\x9AB\x9AC\x3\x2\x2\x2\x9AC\x9AD"); + sb.Append("\x3\x2\x2\x2\x9AD\x9AF\a\x93\x2\x2\x9AE\x9B0\x5\x13E\xA0\x2"); + sb.Append("\x9AF\x9AE\x3\x2\x2\x2\x9AF\x9B0\x3\x2\x2\x2\x9B0\x151\x3\x2"); + sb.Append("\x2\x2\x9B1\x9B4\x5\x1C2\xE2\x2\x9B2\x9B4\x5\x154\xAB\x2\x9B3"); + sb.Append("\x9B1\x3\x2\x2\x2\x9B3\x9B2\x3\x2\x2\x2\x9B4\x153\x3\x2\x2\x2"); + sb.Append("\x9B5\x9BA\x5\x156\xAC\x2\x9B6\x9B7\a\xB7\x2\x2\x9B7\x9B9\x5"); + sb.Append("\x158\xAD\x2\x9B8\x9B6\x3\x2\x2\x2\x9B9\x9BC\x3\x2\x2\x2\x9BA"); + sb.Append("\x9B8\x3\x2\x2\x2\x9BA\x9BB\x3\x2\x2\x2\x9BB\x155\x3\x2\x2\x2"); + sb.Append("\x9BC\x9BA\x3\x2\x2\x2\x9BD\x9BE\x5\x198\xCD\x2\x9BE\x9BF\a"); + sb.Append("\xB7\x2\x2\x9BF\x9C0\x5\x15C\xAF\x2\x9C0\x9C3\x3\x2\x2\x2\x9C1"); + sb.Append("\x9C3\x5\x15A\xAE\x2\x9C2\x9BD\x3\x2\x2\x2\x9C2\x9C1\x3\x2\x2"); + sb.Append("\x2\x9C3\x9C9\x3\x2\x2\x2\x9C4\x9C6\a\x92\x2\x2\x9C5\x9C7\x5"); + sb.Append("\x160\xB1\x2\x9C6\x9C5\x3\x2\x2\x2\x9C6\x9C7\x3\x2\x2\x2\x9C7"); + sb.Append("\x9C8\x3\x2\x2\x2\x9C8\x9CA\a\x93\x2\x2\x9C9\x9C4\x3\x2\x2\x2"); + sb.Append("\x9C9\x9CA\x3\x2\x2\x2\x9CA\x157\x3\x2\x2\x2\x9CB\x9D1\x5\x15E"); + sb.Append("\xB0\x2\x9CC\x9CE\a\x92\x2\x2\x9CD\x9CF\x5\x160\xB1\x2\x9CE"); + sb.Append("\x9CD\x3\x2\x2\x2\x9CE\x9CF\x3\x2\x2\x2\x9CF\x9D0\x3\x2\x2\x2"); + sb.Append("\x9D0\x9D2\a\x93\x2\x2\x9D1\x9CC\x3\x2\x2\x2\x9D1\x9D2\x3\x2"); + sb.Append("\x2\x2\x9D2\x159\x3\x2\x2\x2\x9D3\x9D7\x5\x1CC\xE7\x2\x9D4\x9D7"); + sb.Append("\a\x13\x2\x2\x9D5\x9D7\a\x14\x2\x2\x9D6\x9D3\x3\x2\x2\x2\x9D6"); + sb.Append("\x9D4\x3\x2\x2\x2\x9D6\x9D5\x3\x2\x2\x2\x9D7\x15B\x3\x2\x2\x2"); + sb.Append("\x9D8\x9DD\x5\x1CC\xE7\x2\x9D9\x9DD\a\x33\x2\x2\x9DA\x9DD\a"); + sb.Append("\x32\x2\x2\x9DB\x9DD\a\x4\x2\x2\x9DC\x9D8\x3\x2\x2\x2\x9DC\x9D9"); + sb.Append("\x3\x2\x2\x2\x9DC\x9DA\x3\x2\x2\x2\x9DC\x9DB\x3\x2\x2\x2\x9DD"); + sb.Append("\x15D\x3\x2\x2\x2\x9DE\x9E9\x5\x1CC\xE7\x2\x9DF\x9E9\a\x33\x2"); + sb.Append("\x2\x9E0\x9E9\a\x32\x2\x2\x9E1\x9E9\a\x4\x2\x2\x9E2\x9E9\a\x13"); + sb.Append("\x2\x2\x9E3\x9E9\a\x14\x2\x2\x9E4\x9E9\a\xF\x2\x2\x9E5\x9E9"); + sb.Append("\aR\x2\x2\x9E6\x9E9\ay\x2\x2\x9E7\x9E9\a\x6\x2\x2\x9E8\x9DE"); + sb.Append("\x3\x2\x2\x2\x9E8\x9DF\x3\x2\x2\x2\x9E8\x9E0\x3\x2\x2\x2\x9E8"); + sb.Append("\x9E1\x3\x2\x2\x2\x9E8\x9E2\x3\x2\x2\x2\x9E8\x9E3\x3\x2\x2\x2"); + sb.Append("\x9E8\x9E4\x3\x2\x2\x2\x9E8\x9E5\x3\x2\x2\x2\x9E8\x9E6\x3\x2"); + sb.Append("\x2\x2\x9E8\x9E7\x3\x2\x2\x2\x9E9\x15F\x3\x2\x2\x2\x9EA\x9EC"); + sb.Append("\t\t\x2\x2\x9EB\x9EA\x3\x2\x2\x2\x9EB\x9EC\x3\x2\x2\x2\x9EC"); + sb.Append("\x9ED\x3\x2\x2\x2\x9ED\x9F2\x5\x162\xB2\x2\x9EE\x9EF\a\x99\x2"); + sb.Append("\x2\x9EF\x9F1\x5\x162\xB2\x2\x9F0\x9EE\x3\x2\x2\x2\x9F1\x9F4"); + sb.Append("\x3\x2\x2\x2\x9F2\x9F0\x3\x2\x2\x2\x9F2\x9F3\x3\x2\x2\x2\x9F3"); + sb.Append("\x161\x3\x2\x2\x2\x9F4\x9F2\x3\x2\x2\x2\x9F5\x9F7\x5\x10\t\x2"); + sb.Append("\x9F6\x9F5\x3\x2\x2\x2\x9F6\x9F7\x3\x2\x2\x2\x9F7\x9F8\x3\x2"); + sb.Append("\x2\x2\x9F8\x9F9\x5\x1A0\xD1\x2\x9F9\x163\x3\x2\x2\x2\x9FA\x9FB"); + sb.Append("\x5\x134\x9B\x2\x9FB\x9FC\a\v\x2\x2\x9FC\x9FD\x5\x134\x9B\x2"); + sb.Append("\x9FD\x165\x3\x2\x2\x2\x9FE\x9FF\x5\x168\xB5\x2\x9FF\x167\x3"); + sb.Append("\x2\x2\x2\xA00\xA04\x5\x16C\xB7\x2\xA01\xA03\x5\x16A\xB6\x2"); + sb.Append("\xA02\xA01\x3\x2\x2\x2\xA03\xA06\x3\x2\x2\x2\xA04\xA02\x3\x2"); + sb.Append("\x2\x2\xA04\xA05\x3\x2\x2\x2\xA05\x169\x3\x2\x2\x2\xA06\xA04"); + sb.Append("\x3\x2\x2\x2\xA07\xA0D\a\x8D\x2\x2\xA08\xA09\a\x8B\x2\x2\xA09"); + sb.Append("\xA0A\x5\x122\x92\x2\xA0A\xA0B\a\x8C\x2\x2\xA0B\xA0D\x3\x2\x2"); + sb.Append("\x2\xA0C\xA07\x3\x2\x2\x2\xA0C\xA08\x3\x2\x2\x2\xA0D\xA0E\x3"); + sb.Append("\x2\x2\x2\xA0E\xA0F\x5\x16C\xB7\x2\xA0F\x16B\x3\x2\x2\x2\xA10"); + sb.Append("\xA15\x5\x16E\xB8\x2\xA11\xA12\a\n\x2\x2\xA12\xA14\x5\x16E\xB8"); + sb.Append("\x2\xA13\xA11\x3\x2\x2\x2\xA14\xA17\x3\x2\x2\x2\xA15\xA13\x3"); + sb.Append("\x2\x2\x2\xA15\xA16\x3\x2\x2\x2\xA16\x16D\x3\x2\x2\x2\xA17\xA15"); + sb.Append("\x3\x2\x2\x2\xA18\xA1D\x5\x170\xB9\x2\xA19\xA1A\a\v\x2\x2\xA1A"); + sb.Append("\xA1C\x5\x170\xB9\x2\xA1B\xA19\x3\x2\x2\x2\xA1C\xA1F\x3\x2\x2"); + sb.Append("\x2\xA1D\xA1B\x3\x2\x2\x2\xA1D\xA1E\x3\x2\x2\x2\xA1E\x16F\x3"); + sb.Append("\x2\x2\x2\xA1F\xA1D\x3\x2\x2\x2\xA20\xA22\x5\x182\xC2\x2\xA21"); + sb.Append("\xA20\x3\x2\x2\x2\xA21\xA22\x3\x2\x2\x2\xA22\xA23\x3\x2\x2\x2"); + sb.Append("\xA23\xA26\x5\x172\xBA\x2\xA24\xA25\aU\x2\x2\xA25\xA27\x5\x172"); + sb.Append("\xBA\x2\xA26\xA24\x3\x2\x2\x2\xA26\xA27\x3\x2\x2\x2\xA27\x171"); + sb.Append("\x3\x2\x2\x2\xA28\xA2D\a\r\x2\x2\xA29\xA2D\a\f\x2\x2\xA2A\xA2B"); + sb.Append("\a\xE\x2\x2\xA2B\xA2D\x5\x176\xBC\x2\xA2C\xA28\x3\x2\x2\x2\xA2C"); + sb.Append("\xA29\x3\x2\x2\x2\xA2C\xA2A\x3\x2\x2\x2\xA2D\xA2F\x3\x2\x2\x2"); + sb.Append("\xA2E\xA30\x5\x182\xC2\x2\xA2F\xA2E\x3\x2\x2\x2\xA2F\xA30\x3"); + sb.Append("\x2\x2\x2\xA30\xA32\x3\x2\x2\x2\xA31\xA2C\x3\x2\x2\x2\xA31\xA32"); + sb.Append("\x3\x2\x2\x2\xA32\xA33\x3\x2\x2\x2\xA33\xA34\x5\x174\xBB\x2"); + sb.Append("\xA34\x173\x3\x2\x2\x2\xA35\xA3B\x5\x17A\xBE\x2\xA36\xA37\a"); + sb.Append("\x92\x2\x2\xA37\xA38\x5\x166\xB4\x2\xA38\xA39\a\x93\x2\x2\xA39"); + sb.Append("\xA3B\x3\x2\x2\x2\xA3A\xA35\x3\x2\x2\x2\xA3A\xA36\x3\x2\x2\x2"); + sb.Append("\xA3B\xA40\x3\x2\x2\x2\xA3C\xA3D\a\xF\x2\x2\xA3D\xA41\x5\x17E"); + sb.Append("\xC0\x2\xA3E\xA3F\a{\x2\x2\xA3F\xA41\x5\x180\xC1\x2\xA40\xA3C"); + sb.Append("\x3\x2\x2\x2\xA40\xA3E\x3\x2\x2\x2\xA40\xA41\x3\x2\x2\x2\xA41"); + sb.Append("\x175\x3\x2\x2\x2\xA42\xA43\a\x92\x2\x2\xA43\xA48\x5\x178\xBD"); + sb.Append("\x2\xA44\xA45\a\x99\x2\x2\xA45\xA47\x5\x178\xBD\x2\xA46\xA44"); + sb.Append("\x3\x2\x2\x2\xA47\xA4A\x3\x2\x2\x2\xA48\xA46\x3\x2\x2\x2\xA48"); + sb.Append("\xA49\x3\x2\x2\x2\xA49\xA4B\x3\x2\x2\x2\xA4A\xA48\x3\x2\x2\x2"); + sb.Append("\xA4B\xA4C\a\x93\x2\x2\xA4C\x177\x3\x2\x2\x2\xA4D\xA4E\x5\x1AC"); + sb.Append("\xD7\x2\xA4E\x179\x3\x2\x2\x2\xA4F\xA52\x5\x17C\xBF\x2\xA50"); + sb.Append("\xA52\x5\x194\xCB\x2\xA51\xA4F\x3\x2\x2\x2\xA51\xA50\x3\x2\x2"); + sb.Append("\x2\xA52\x17B\x3\x2\x2\x2\xA53\xA54\a\xC5\x2\x2\xA54\xA57\a"); + sb.Append("\x98\x2\x2\xA55\xA58\a\xC5\x2\x2\xA56\xA58\aV\x2\x2\xA57\xA55"); + sb.Append("\x3\x2\x2\x2\xA57\xA56\x3\x2\x2\x2\xA58\xA59\x3\x2\x2\x2\xA59"); + sb.Append("\xA5B\a\x92\x2\x2\xA5A\xA5C\x5\x19E\xD0\x2\xA5B\xA5A\x3\x2\x2"); + sb.Append("\x2\xA5B\xA5C\x3\x2\x2\x2\xA5C\xA5D\x3\x2\x2\x2\xA5D\xA5E\a"); + sb.Append("\x93\x2\x2\xA5E\x17D\x3\x2\x2\x2\xA5F\xA60\a\xC5\x2\x2\xA60"); + sb.Append("\xA61\a\x98\x2\x2\xA61\xA62\a\xC5\x2\x2\xA62\xA64\a\x92\x2\x2"); + sb.Append("\xA63\xA65\x5\x1AA\xD6\x2\xA64\xA63\x3\x2\x2\x2\xA64\xA65\x3"); + sb.Append("\x2\x2\x2\xA65\xA66\x3\x2\x2\x2\xA66\xA67\a\x93\x2\x2\xA67\x17F"); + sb.Append("\x3\x2\x2\x2\xA68\xA69\a\x92\x2\x2\xA69\xA6A\x5\x122\x92\x2"); + sb.Append("\xA6A\xA6B\a\x93\x2\x2\xA6B\x181\x3\x2\x2\x2\xA6C\xA76\a\x94"); + sb.Append("\x2\x2\xA6D\xA72\x5\x122\x92\x2\xA6E\xA70\a\x98\x2\x2\xA6F\xA71"); + sb.Append("\x5\x122\x92\x2\xA70\xA6F\x3\x2\x2\x2\xA70\xA71\x3\x2\x2\x2"); + sb.Append("\xA71\xA73\x3\x2\x2\x2\xA72\xA6E\x3\x2\x2\x2\xA72\xA73\x3\x2"); + sb.Append("\x2\x2\xA73\xA77\x3\x2\x2\x2\xA74\xA75\a\x98\x2\x2\xA75\xA77"); + sb.Append("\x5\x122\x92\x2\xA76\xA6D\x3\x2\x2\x2\xA76\xA74\x3\x2\x2\x2"); + sb.Append("\xA77\xA78\x3\x2\x2\x2\xA78\xA79\a\x95\x2\x2\xA79\x183\x3\x2"); + sb.Append("\x2\x2\xA7A\xA7B\a\xC5\x2\x2\xA7B\xA7D\a\x8F\x2\x2\xA7C\xA7A"); + sb.Append("\x3\x2\x2\x2\xA7C\xA7D\x3\x2\x2\x2\xA7D\xA7E\x3\x2\x2\x2\xA7E"); + sb.Append("\xA84\x5\x198\xCD\x2\xA7F\xA81\a\x92\x2\x2\xA80\xA82\x5\x1A8"); + sb.Append("\xD5\x2\xA81\xA80\x3\x2\x2\x2\xA81\xA82\x3\x2\x2\x2\xA82\xA83"); + sb.Append("\x3\x2\x2\x2\xA83\xA85\a\x93\x2\x2\xA84\xA7F\x3\x2\x2\x2\xA84"); + sb.Append("\xA85\x3\x2\x2\x2\xA85\xA87\x3\x2\x2\x2\xA86\xA88\x5\x186\xC4"); + sb.Append("\x2\xA87\xA86\x3\x2\x2\x2\xA87\xA88\x3\x2\x2\x2\xA88\x185\x3"); + sb.Append("\x2\x2\x2\xA89\xA8D\x5\x188\xC5\x2\xA8A\xA8C\x5\x188\xC5\x2"); + sb.Append("\xA8B\xA8A\x3\x2\x2\x2\xA8C\xA8F\x3\x2\x2\x2\xA8D\xA8B\x3\x2"); + sb.Append("\x2\x2\xA8D\xA8E\x3\x2\x2\x2\xA8E\x187\x3\x2\x2\x2\xA8F\xA8D"); + sb.Append("\x3\x2\x2\x2\xA90\xA92\a\x94\x2\x2\xA91\xA93\x5\x18A\xC6\x2"); + sb.Append("\xA92\xA91\x3\x2\x2\x2\xA92\xA93\x3\x2\x2\x2\xA93\xA94\x3\x2"); + sb.Append("\x2\x2\xA94\xA96\x5\x122\x92\x2\xA95\xA97\x5\x192\xCA\x2\xA96"); + sb.Append("\xA95\x3\x2\x2\x2\xA96\xA97\x3\x2\x2\x2\xA97\xA9A\x3\x2\x2\x2"); + sb.Append("\xA98\xA99\a\x10\x2\x2\xA99\xA9B\a\xC5\x2\x2\xA9A\xA98\x3\x2"); + sb.Append("\x2\x2\xA9A\xA9B\x3\x2\x2\x2\xA9B\xA9E\x3\x2\x2\x2\xA9C\xA9D"); + sb.Append("\a\xF\x2\x2\xA9D\xA9F\x5\x122\x92\x2\xA9E\xA9C\x3\x2\x2\x2\xA9E"); + sb.Append("\xA9F\x3\x2\x2\x2\xA9F\xAA0\x3\x2\x2\x2\xAA0\xAA1\a\x95\x2\x2"); + sb.Append("\xAA1\x189\x3\x2\x2\x2\xAA2\xAA3\a\x1A\x2\x2\xAA3\xAA4\x5\x18C"); + sb.Append("\xC7\x2\xAA4\xAA5\a \x2\x2\xAA5\x18B\x3\x2\x2\x2\xAA6\xAAB\x5"); + sb.Append("\x18E\xC8\x2\xAA7\xAA8\a\x99\x2\x2\xAA8\xAAA\x5\x18E\xC8\x2"); + sb.Append("\xAA9\xAA7\x3\x2\x2\x2\xAAA\xAAD\x3\x2\x2\x2\xAAB\xAA9\x3\x2"); + sb.Append("\x2\x2\xAAB\xAAC\x3\x2\x2\x2\xAAC\x18D\x3\x2\x2\x2\xAAD\xAAB"); + sb.Append("\x3\x2\x2\x2\xAAE\xAB6\a\xA6\x2\x2\xAAF\xAB6\x5\x190\xC9\x2"); + sb.Append("\xAB0\xAB3\x5\x122\x92\x2\xAB1\xAB2\a\x10\x2\x2\xAB2\xAB4\x5"); + sb.Append("\x1C8\xE5\x2\xAB3\xAB1\x3\x2\x2\x2\xAB3\xAB4\x3\x2\x2\x2\xAB4"); + sb.Append("\xAB6\x3\x2\x2\x2\xAB5\xAAE\x3\x2\x2\x2\xAB5\xAAF\x3\x2\x2\x2"); + sb.Append("\xAB5\xAB0\x3\x2\x2\x2\xAB6\x18F\x3\x2\x2\x2\xAB7\xAB8\a\xC5"); + sb.Append("\x2\x2\xAB8\xAB9\a\xB7\x2\x2\xAB9\xABC\a\xA6\x2\x2\xABA\xABB"); + sb.Append("\a\x10\x2\x2\xABB\xABD\a\xC5\x2\x2\xABC\xABA\x3\x2\x2\x2\xABC"); + sb.Append("\xABD\x3\x2\x2\x2\xABD\x191\x3\x2\x2\x2\xABE\xABF\a\xBD\x2\x2"); + sb.Append("\xABF\xAC0\a\xC5\x2\x2\xAC0\xAC1\a\x92\x2\x2\xAC1\xAC2\a\xC5"); + sb.Append("\x2\x2\xAC2\xAC3\a\x93\x2\x2\xAC3\x193\x3\x2\x2\x2\xAC4\xAC5"); + sb.Append("\a\xC5\x2\x2\xAC5\xAC7\a\x8F\x2\x2\xAC6\xAC4\x3\x2\x2\x2\xAC6"); + sb.Append("\xAC7\x3\x2\x2\x2\xAC7\xAC8\x3\x2\x2\x2\xAC8\xACE\x5\x198\xCD"); + sb.Append("\x2\xAC9\xACB\a\x92\x2\x2\xACA\xACC\x5\x1A8\xD5\x2\xACB\xACA"); + sb.Append("\x3\x2\x2\x2\xACB\xACC\x3\x2\x2\x2\xACC\xACD\x3\x2\x2\x2\xACD"); + sb.Append("\xACF\a\x93\x2\x2\xACE\xAC9\x3\x2\x2\x2\xACE\xACF\x3\x2\x2\x2"); + sb.Append("\xACF\xAD1\x3\x2\x2\x2\xAD0\xAD2\x5\x186\xC4\x2\xAD1\xAD0\x3"); + sb.Append("\x2\x2\x2\xAD1\xAD2\x3\x2\x2\x2\xAD2\xAD4\x3\x2\x2\x2\xAD3\xAD5"); + sb.Append("\x5\x196\xCC\x2\xAD4\xAD3\x3\x2\x2\x2\xAD4\xAD5\x3\x2\x2\x2"); + sb.Append("\xAD5\x195\x3\x2\x2\x2\xAD6\xAD7\a\xBD\x2\x2\xAD7\xADC\a\xC5"); + sb.Append("\x2\x2\xAD8\xAD9\a\x92\x2\x2\xAD9\xADA\x5\x1E2\xF2\x2\xADA\xADB"); + sb.Append("\a\x93\x2\x2\xADB\xADD\x3\x2\x2\x2\xADC\xAD8\x3\x2\x2\x2\xADC"); + sb.Append("\xADD\x3\x2\x2\x2\xADD\x197\x3\x2\x2\x2\xADE\xAE3\x5\x1CA\xE6"); + sb.Append("\x2\xADF\xAE0\a\xB7\x2\x2\xAE0\xAE2\x5\x1CA\xE6\x2\xAE1\xADF"); + sb.Append("\x3\x2\x2\x2\xAE2\xAE5\x3\x2\x2\x2\xAE3\xAE1\x3\x2\x2\x2\xAE3"); + sb.Append("\xAE4\x3\x2\x2\x2\xAE4\x199\x3\x2\x2\x2\xAE5\xAE3\x3\x2\x2\x2"); + sb.Append("\xAE6\xAE8\a\x9E\x2\x2\xAE7\xAE6\x3\x2\x2\x2\xAE7\xAE8\x3\x2"); + sb.Append("\x2\x2\xAE8\xAE9\x3\x2\x2\x2\xAE9\xAEE\x5\x1CA\xE6\x2\xAEA\xAEB"); + sb.Append("\a\x9E\x2\x2\xAEB\xAED\x5\x1CA\xE6\x2\xAEC\xAEA\x3\x2\x2\x2"); + sb.Append("\xAED\xAF0\x3\x2\x2\x2\xAEE\xAEC\x3\x2\x2\x2\xAEE\xAEF\x3\x2"); + sb.Append("\x2\x2\xAEF\x19B\x3\x2\x2\x2\xAF0\xAEE\x3\x2\x2\x2\xAF1\xAF6"); + sb.Append("\x5\x1A0\xD1\x2\xAF2\xAF3\a\x99\x2\x2\xAF3\xAF5\x5\x1A0\xD1"); + sb.Append("\x2\xAF4\xAF2\x3\x2\x2\x2\xAF5\xAF8\x3\x2\x2\x2\xAF6\xAF4\x3"); + sb.Append("\x2\x2\x2\xAF6\xAF7\x3\x2\x2\x2\xAF7\x19D\x3\x2\x2\x2\xAF8\xAF6"); + sb.Append("\x3\x2\x2\x2\xAF9\xAFE\x5\x1A2\xD2\x2\xAFA\xAFB\a\x99\x2\x2"); + sb.Append("\xAFB\xAFD\x5\x1A2\xD2\x2\xAFC\xAFA\x3\x2\x2\x2\xAFD\xB00\x3"); + sb.Append("\x2\x2\x2\xAFE\xAFC\x3\x2\x2\x2\xAFE\xAFF\x3\x2\x2\x2\xAFF\x19F"); + sb.Append("\x3\x2\x2\x2\xB00\xAFE\x3\x2\x2\x2\xB01\xB04\x5\x1A4\xD3\x2"); + sb.Append("\xB02\xB04\x5\x1AC\xD7\x2\xB03\xB01\x3\x2\x2\x2\xB03\xB02\x3"); + sb.Append("\x2\x2\x2\xB04\x1A1\x3\x2\x2\x2\xB05\xB08\x5\x1A6\xD4\x2\xB06"); + sb.Append("\xB08\x5\x1AE\xD8\x2\xB07\xB05\x3\x2\x2\x2\xB07\xB06\x3\x2\x2"); + sb.Append("\x2\xB08\x1A3\x3\x2\x2\x2\xB09\xB0A\a\xC5\x2\x2\xB0A\xB11\a"); + sb.Append("\x98\x2\x2\xB0B\xB12\x5\x122\x92\x2\xB0C\xB0E\a\x92\x2\x2\xB0D"); + sb.Append("\xB0F\x5\x1A8\xD5\x2\xB0E\xB0D\x3\x2\x2\x2\xB0E\xB0F\x3\x2\x2"); + sb.Append("\x2\xB0F\xB10\x3\x2\x2\x2\xB10\xB12\a\x93\x2\x2\xB11\xB0B\x3"); + sb.Append("\x2\x2\x2\xB11\xB0C\x3\x2\x2\x2\xB12\x1A5\x3\x2\x2\x2\xB13\xB14"); + sb.Append("\a\xC5\x2\x2\xB14\xB1B\a\x98\x2\x2\xB15\xB1C\x5\x1AC\xD7\x2"); + sb.Append("\xB16\xB18\a\x92\x2\x2\xB17\xB19\x5\x1AA\xD6\x2\xB18\xB17\x3"); + sb.Append("\x2\x2\x2\xB18\xB19\x3\x2\x2\x2\xB19\xB1A\x3\x2\x2\x2\xB1A\xB1C"); + sb.Append("\a\x93\x2\x2\xB1B\xB15\x3\x2\x2\x2\xB1B\xB16\x3\x2\x2\x2\xB1C"); + sb.Append("\x1A7\x3\x2\x2\x2\xB1D\xB22\x5\x122\x92\x2\xB1E\xB1F\a\x99\x2"); + sb.Append("\x2\xB1F\xB21\x5\x122\x92\x2\xB20\xB1E\x3\x2\x2\x2\xB21\xB24"); + sb.Append("\x3\x2\x2\x2\xB22\xB20\x3\x2\x2\x2\xB22\xB23\x3\x2\x2\x2\xB23"); + sb.Append("\x1A9\x3\x2\x2\x2\xB24\xB22\x3\x2\x2\x2\xB25\xB2A\x5\x1AE\xD8"); + sb.Append("\x2\xB26\xB27\a\x99\x2\x2\xB27\xB29\x5\x1AE\xD8\x2\xB28\xB26"); + sb.Append("\x3\x2\x2\x2\xB29\xB2C\x3\x2\x2\x2\xB2A\xB28\x3\x2\x2\x2\xB2A"); + sb.Append("\xB2B\x3\x2\x2\x2\xB2B\x1AB\x3\x2\x2\x2\xB2C\xB2A\x3\x2\x2\x2"); + sb.Append("\xB2D\xB38\x5\x1B2\xDA\x2\xB2E\xB38\x5\x1CE\xE8\x2\xB2F\xB38"); + sb.Append("\x5\x1B0\xD9\x2\xB30\xB38\x5\x1B8\xDD\x2\xB31\xB38\x5\x1B6\xDC"); + sb.Append("\x2\xB32\xB38\x5\x1BA\xDE\x2\xB33\xB38\x5\x1BC\xDF\x2\xB34\xB38"); + sb.Append("\x5\x1BE\xE0\x2\xB35\xB38\a\xA6\x2\x2\xB36\xB38\x5\x190\xC9"); + sb.Append("\x2\xB37\xB2D\x3\x2\x2\x2\xB37\xB2E\x3\x2\x2\x2\xB37\xB2F\x3"); + sb.Append("\x2\x2\x2\xB37\xB30\x3\x2\x2\x2\xB37\xB31\x3\x2\x2\x2\xB37\xB32"); + sb.Append("\x3\x2\x2\x2\xB37\xB33\x3\x2\x2\x2\xB37\xB34\x3\x2\x2\x2\xB37"); + sb.Append("\xB35\x3\x2\x2\x2\xB37\xB36\x3\x2\x2\x2\xB38\x1AD\x3\x2\x2\x2"); + sb.Append("\xB39\xB3C\x5\x1B4\xDB\x2\xB3A\xB3C\x5\x1AC\xD7\x2\xB3B\xB39"); + sb.Append("\x3\x2\x2\x2\xB3B\xB3A\x3\x2\x2\x2\xB3C\x1AF\x3\x2\x2\x2\xB3D"); + sb.Append("\xB43\x5\x122\x92\x2\xB3E\xB44\a\x38\x2\x2\xB3F\xB44\a\x39\x2"); + sb.Append("\x2\xB40\xB44\a\x66\x2\x2\xB41\xB44\a\x65\x2\x2\xB42\xB44\a"); + sb.Append("\x64\x2\x2\xB43\xB3E\x3\x2\x2\x2\xB43\xB3F\x3\x2\x2\x2\xB43"); + sb.Append("\xB40\x3\x2\x2\x2\xB43\xB41\x3\x2\x2\x2\xB43\xB42\x3\x2\x2\x2"); + sb.Append("\xB43\xB44\x3\x2\x2\x2\xB44\x1B1\x3\x2\x2\x2\xB45\xB46\aK\x2"); + sb.Append("\x2\xB46\x1B3\x3\x2\x2\x2\xB47\xB48\a\x33\x2\x2\xB48\x1B5\x3"); + sb.Append("\x2\x2\x2\xB49\xB4A\a\xA6\x2\x2\xB4A\xB4E\a\x9E\x2\x2\xB4B\xB4F"); + sb.Append("\x5\x1E2\xF2\x2\xB4C\xB4F\a\xC5\x2\x2\xB4D\xB4F\x5\x1E4\xF3"); + sb.Append("\x2\xB4E\xB4B\x3\x2\x2\x2\xB4E\xB4C\x3\x2\x2\x2\xB4E\xB4D\x3"); + sb.Append("\x2\x2\x2\xB4F\x1B7\x3\x2\x2\x2\xB50\xB54\x5\x1E2\xF2\x2\xB51"); + sb.Append("\xB54\a\xC5\x2\x2\xB52\xB54\x5\x1E4\xF3\x2\xB53\xB50\x3\x2\x2"); + sb.Append("\x2\xB53\xB51\x3\x2\x2\x2\xB53\xB52\x3\x2\x2\x2\xB54\xB55\x3"); + sb.Append("\x2\x2\x2\xB55\xB59\a\x98\x2\x2\xB56\xB5A\x5\x1E2\xF2\x2\xB57"); + sb.Append("\xB5A\a\xC5\x2\x2\xB58\xB5A\x5\x1E4\xF3\x2\xB59\xB56\x3\x2\x2"); + sb.Append("\x2\xB59\xB57\x3\x2\x2\x2\xB59\xB58\x3\x2\x2\x2\xB5A\x1B9\x3"); + sb.Append("\x2\x2\x2\xB5B\xB5F\x5\x1E2\xF2\x2\xB5C\xB5F\a\xC5\x2\x2\xB5D"); + sb.Append("\xB5F\x5\x1E4\xF3\x2\xB5E\xB5B\x3\x2\x2\x2\xB5E\xB5C\x3\x2\x2"); + sb.Append("\x2\xB5E\xB5D\x3\x2\x2\x2\xB5F\xB60\x3\x2\x2\x2\xB60\xB61\a"); + sb.Append("\x33\x2\x2\xB61\x1BB\x3\x2\x2\x2\xB62\xB66\x5\x1E2\xF2\x2\xB63"); + sb.Append("\xB66\a\xC5\x2\x2\xB64\xB66\x5\x1E4\xF3\x2\xB65\xB62\x3\x2\x2"); + sb.Append("\x2\xB65\xB63\x3\x2\x2\x2\xB65\xB64\x3\x2\x2\x2\xB66\xB67\x3"); + sb.Append("\x2\x2\x2\xB67\xB68\aJ\x2\x2\xB68\x1BD\x3\x2\x2\x2\xB69\xB6A"); + sb.Append("\a\x94\x2\x2\xB6A\xB6F\x5\x1C0\xE1\x2\xB6B\xB6C\a\x99\x2\x2"); + sb.Append("\xB6C\xB6E\x5\x1C0\xE1\x2\xB6D\xB6B\x3\x2\x2\x2\xB6E\xB71\x3"); + sb.Append("\x2\x2\x2\xB6F\xB6D\x3\x2\x2\x2\xB6F\xB70\x3\x2\x2\x2\xB70\xB72"); + sb.Append("\x3\x2\x2\x2\xB71\xB6F\x3\x2\x2\x2\xB72\xB73\a\x95\x2\x2\xB73"); + sb.Append("\x1BF\x3\x2\x2\x2\xB74\xB78\x5\x1B8\xDD\x2\xB75\xB78\x5\x1B6"); + sb.Append("\xDC\x2\xB76\xB78\x5\x1E8\xF5\x2\xB77\xB74\x3\x2\x2\x2\xB77"); + sb.Append("\xB75\x3\x2\x2\x2\xB77\xB76\x3\x2\x2\x2\xB78\x1C1\x3\x2\x2\x2"); + sb.Append("\xB79\xB7E\x5\x1C4\xE3\x2\xB7A\xB7B\a\xB7\x2\x2\xB7B\xB7D\x5"); + sb.Append("\x1C4\xE3\x2\xB7C\xB7A\x3\x2\x2\x2\xB7D\xB80\x3\x2\x2\x2\xB7E"); + sb.Append("\xB7C\x3\x2\x2\x2\xB7E\xB7F\x3\x2\x2\x2\xB7F\x1C3\x3\x2\x2\x2"); + sb.Append("\xB80\xB7E\x3\x2\x2\x2\xB81\xB92\x5\x1C6\xE4\x2\xB82\xB83\a"); + sb.Append("\x94\x2\x2\xB83\xB84\x5\x1E2\xF2\x2\xB84\xB86\a\x95\x2\x2\xB85"); + sb.Append("\xB87\a\x91\x2\x2\xB86\xB85\x3\x2\x2\x2\xB86\xB87\x3\x2\x2\x2"); + sb.Append("\xB87\xB93\x3\x2\x2\x2\xB88\xB8B\a\x92\x2\x2\xB89\xB8C\a\xC4"); + sb.Append("\x2\x2\xB8A\xB8C\a\xC3\x2\x2\xB8B\xB89\x3\x2\x2\x2\xB8B\xB8A"); + sb.Append("\x3\x2\x2\x2\xB8C\xB8D\x3\x2\x2\x2\xB8D\xB8F\a\x93\x2\x2\xB8E"); + sb.Append("\xB90\a\x91\x2\x2\xB8F\xB8E\x3\x2\x2\x2\xB8F\xB90\x3\x2\x2\x2"); + sb.Append("\xB90\xB93\x3\x2\x2\x2\xB91\xB93\a\x91\x2\x2\xB92\xB82\x3\x2"); + sb.Append("\x2\x2\xB92\xB88\x3\x2\x2\x2\xB92\xB91\x3\x2\x2\x2\xB92\xB93"); + sb.Append("\x3\x2\x2\x2\xB93\x1C5\x3\x2\x2\x2\xB94\xB9C\x5\x1C8\xE5\x2"); + sb.Append("\xB95\xB96\a\xBB\x2\x2\xB96\xB98\a\xB7\x2\x2\xB97\xB99\x5\x1C8"); + sb.Append("\xE5\x2\xB98\xB97\x3\x2\x2\x2\xB98\xB99\x3\x2\x2\x2\xB99\xB9B"); + sb.Append("\x3\x2\x2\x2\xB9A\xB95\x3\x2\x2\x2\xB9B\xB9E\x3\x2\x2\x2\xB9C"); + sb.Append("\xB9A\x3\x2\x2\x2\xB9C\xB9D\x3\x2\x2\x2\xB9D\x1C7\x3\x2\x2\x2"); + sb.Append("\xB9E\xB9C\x3\x2\x2\x2\xB9F\xBD4\a\xC5\x2\x2\xBA0\xBD4\a\xC2"); + sb.Append("\x2\x2\xBA1\xBD4\aV\x2\x2\xBA2\xBD4\a\x19\x2\x2\xBA3\xBD4\a"); + sb.Append("\t\x2\x2\xBA4\xBD4\a\r\x2\x2\xBA5\xBD4\a=\x2\x2\xBA6\xBD4\a"); + sb.Append("\x11\x2\x2\xBA7\xBD4\a\x12\x2\x2\xBA8\xBD4\a\x13\x2\x2\xBA9"); + sb.Append("\xBD4\a\x14\x2\x2\xBAA\xBD4\a\x15\x2\x2\xBAB\xBD4\a\x16\x2\x2"); + sb.Append("\xBAC\xBD4\a\x17\x2\x2\xBAD\xBD4\a\x18\x2\x2\xBAE\xBD4\a\x31"); + sb.Append("\x2\x2\xBAF\xBD4\a\x32\x2\x2\xBB0\xBD4\a\x33\x2\x2\xBB1\xBD4"); + sb.Append("\a{\x2\x2\xBB2\xBD4\a}\x2\x2\xBB3\xBD4\a~\x2\x2\xBB4\xBD4\a"); + sb.Append(">\x2\x2\xBB5\xBD4\a?\x2\x2\xBB6\xBD4\a@\x2\x2\xBB7\xBD4\aU\x2"); + sb.Append("\x2\xBB8\xBD4\a\x41\x2\x2\xBB9\xBD4\a\x42\x2\x2\xBBA\xBD4\a"); + sb.Append("\x43\x2\x2\xBBB\xBD4\a\x44\x2\x2\xBBC\xBD4\a\x45\x2\x2\xBBD"); + sb.Append("\xBD4\aH\x2\x2\xBBE\xBD4\aJ\x2\x2\xBBF\xBD4\aK\x2\x2\xBC0\xBD4"); + sb.Append("\aL\x2\x2\xBC1\xBD4\aM\x2\x2\xBC2\xBD4\aN\x2\x2\xBC3\xBD4\a"); + sb.Append("Q\x2\x2\xBC4\xBD4\aS\x2\x2\xBC5\xBD4\aT\x2\x2\xBC6\xBD4\aW\x2"); + sb.Append("\x2\xBC7\xBD4\a\x4\x2\x2\xBC8\xBD4\a$\x2\x2\xBC9\xBD4\a%\x2"); + sb.Append("\x2\xBCA\xBD4\a!\x2\x2\xBCB\xBD4\a&\x2\x2\xBCC\xBD4\a#\x2\x2"); + sb.Append("\xBCD\xBD4\av\x2\x2\xBCE\xBD4\aw\x2\x2\xBCF\xBD4\ax\x2\x2\xBD0"); + sb.Append("\xBD4\a\x82\x2\x2\xBD1\xBD4\az\x2\x2\xBD2\xBD4\a|\x2\x2\xBD3"); + sb.Append("\xB9F\x3\x2\x2\x2\xBD3\xBA0\x3\x2\x2\x2\xBD3\xBA1\x3\x2\x2\x2"); + sb.Append("\xBD3\xBA2\x3\x2\x2\x2\xBD3\xBA3\x3\x2\x2\x2\xBD3\xBA4\x3\x2"); + sb.Append("\x2\x2\xBD3\xBA5\x3\x2\x2\x2\xBD3\xBA6\x3\x2\x2\x2\xBD3\xBA7"); + sb.Append("\x3\x2\x2\x2\xBD3\xBA8\x3\x2\x2\x2\xBD3\xBA9\x3\x2\x2\x2\xBD3"); + sb.Append("\xBAA\x3\x2\x2\x2\xBD3\xBAB\x3\x2\x2\x2\xBD3\xBAC\x3\x2\x2\x2"); + sb.Append("\xBD3\xBAD\x3\x2\x2\x2\xBD3\xBAE\x3\x2\x2\x2\xBD3\xBAF\x3\x2"); + sb.Append("\x2\x2\xBD3\xBB0\x3\x2\x2\x2\xBD3\xBB1\x3\x2\x2\x2\xBD3\xBB2"); + sb.Append("\x3\x2\x2\x2\xBD3\xBB3\x3\x2\x2\x2\xBD3\xBB4\x3\x2\x2\x2\xBD3"); + sb.Append("\xBB5\x3\x2\x2\x2\xBD3\xBB6\x3\x2\x2\x2\xBD3\xBB7\x3\x2\x2\x2"); + sb.Append("\xBD3\xBB8\x3\x2\x2\x2\xBD3\xBB9\x3\x2\x2\x2\xBD3\xBBA\x3\x2"); + sb.Append("\x2\x2\xBD3\xBBB\x3\x2\x2\x2\xBD3\xBBC\x3\x2\x2\x2\xBD3\xBBD"); + sb.Append("\x3\x2\x2\x2\xBD3\xBBE\x3\x2\x2\x2\xBD3\xBBF\x3\x2\x2\x2\xBD3"); + sb.Append("\xBC0\x3\x2\x2\x2\xBD3\xBC1\x3\x2\x2\x2\xBD3\xBC2\x3\x2\x2\x2"); + sb.Append("\xBD3\xBC3\x3\x2\x2\x2\xBD3\xBC4\x3\x2\x2\x2\xBD3\xBC5\x3\x2"); + sb.Append("\x2\x2\xBD3\xBC6\x3\x2\x2\x2\xBD3\xBC7\x3\x2\x2\x2\xBD3\xBC8"); + sb.Append("\x3\x2\x2\x2\xBD3\xBC9\x3\x2\x2\x2\xBD3\xBCA\x3\x2\x2\x2\xBD3"); + sb.Append("\xBCB\x3\x2\x2\x2\xBD3\xBCC\x3\x2\x2\x2\xBD3\xBCD\x3\x2\x2\x2"); + sb.Append("\xBD3\xBCE\x3\x2\x2\x2\xBD3\xBCF\x3\x2\x2\x2\xBD3\xBD0\x3\x2"); + sb.Append("\x2\x2\xBD3\xBD1\x3\x2\x2\x2\xBD3\xBD2\x3\x2\x2\x2\xBD4\x1C9"); + sb.Append("\x3\x2\x2\x2\xBD5\xBD9\a\xC5\x2\x2\xBD6\xBD9\a\x31\x2\x2\xBD7"); + sb.Append("\xBD9\a\xC2\x2\x2\xBD8\xBD5\x3\x2\x2\x2\xBD8\xBD6\x3\x2\x2\x2"); + sb.Append("\xBD8\xBD7\x3\x2\x2\x2\xBD9\x1CB\x3\x2\x2\x2\xBDA\xBDD\a\xC5"); + sb.Append("\x2\x2\xBDB\xBDD\a\xC2\x2\x2\xBDC\xBDA\x3\x2\x2\x2\xBDC\xBDB"); + sb.Append("\x3\x2\x2\x2\xBDD\x1CD\x3\x2\x2\x2\xBDE\xBE0\x5\x1D0\xE9\x2"); + sb.Append("\xBDF\xBE1\x5\x1D2\xEA\x2\xBE0\xBDF\x3\x2\x2\x2\xBE0\xBE1\x3"); + sb.Append("\x2\x2\x2\xBE1\xBE3\x3\x2\x2\x2\xBE2\xBE4\x5\x1D4\xEB\x2\xBE3"); + sb.Append("\xBE2\x3\x2\x2\x2\xBE3\xBE4\x3\x2\x2\x2\xBE4\xBE6\x3\x2\x2\x2"); + sb.Append("\xBE5\xBE7\x5\x1D6\xEC\x2\xBE6\xBE5\x3\x2\x2\x2\xBE6\xBE7\x3"); + sb.Append("\x2\x2\x2\xBE7\xBE9\x3\x2\x2\x2\xBE8\xBEA\x5\x1D8\xED\x2\xBE9"); + sb.Append("\xBE8\x3\x2\x2\x2\xBE9\xBEA\x3\x2\x2\x2\xBEA\xBEC\x3\x2\x2\x2"); + sb.Append("\xBEB\xBED\x5\x1DA\xEE\x2\xBEC\xBEB\x3\x2\x2\x2\xBEC\xBED\x3"); + sb.Append("\x2\x2\x2\xBED\xBEF\x3\x2\x2\x2\xBEE\xBF0\x5\x1DC\xEF\x2\xBEF"); + sb.Append("\xBEE\x3\x2\x2\x2\xBEF\xBF0\x3\x2\x2\x2\xBF0\xBF2\x3\x2\x2\x2"); + sb.Append("\xBF1\xBF3\x5\x1DE\xF0\x2\xBF2\xBF1\x3\x2\x2\x2\xBF2\xBF3\x3"); + sb.Append("\x2\x2\x2\xBF3\xBF5\x3\x2\x2\x2\xBF4\xBF6\x5\x1E0\xF1\x2\xBF5"); + sb.Append("\xBF4\x3\x2\x2\x2\xBF5\xBF6\x3\x2\x2\x2\xBF6\xC54\x3\x2\x2\x2"); + sb.Append("\xBF7\xBF9\x5\x1D2\xEA\x2\xBF8\xBFA\x5\x1D4\xEB\x2\xBF9\xBF8"); + sb.Append("\x3\x2\x2\x2\xBF9\xBFA\x3\x2\x2\x2\xBFA\xBFC\x3\x2\x2\x2\xBFB"); + sb.Append("\xBFD\x5\x1D6\xEC\x2\xBFC\xBFB\x3\x2\x2\x2\xBFC\xBFD\x3\x2\x2"); + sb.Append("\x2\xBFD\xBFF\x3\x2\x2\x2\xBFE\xC00\x5\x1D8\xED\x2\xBFF\xBFE"); + sb.Append("\x3\x2\x2\x2\xBFF\xC00\x3\x2\x2\x2\xC00\xC02\x3\x2\x2\x2\xC01"); + sb.Append("\xC03\x5\x1DA\xEE\x2\xC02\xC01\x3\x2\x2\x2\xC02\xC03\x3\x2\x2"); + sb.Append("\x2\xC03\xC05\x3\x2\x2\x2\xC04\xC06\x5\x1DC\xEF\x2\xC05\xC04"); + sb.Append("\x3\x2\x2\x2\xC05\xC06\x3\x2\x2\x2\xC06\xC08\x3\x2\x2\x2\xC07"); + sb.Append("\xC09\x5\x1DE\xF0\x2\xC08\xC07\x3\x2\x2\x2\xC08\xC09\x3\x2\x2"); + sb.Append("\x2\xC09\xC0B\x3\x2\x2\x2\xC0A\xC0C\x5\x1E0\xF1\x2\xC0B\xC0A"); + sb.Append("\x3\x2\x2\x2\xC0B\xC0C\x3\x2\x2\x2\xC0C\xC54\x3\x2\x2\x2\xC0D"); + sb.Append("\xC0F\x5\x1D4\xEB\x2\xC0E\xC10\x5\x1D6\xEC\x2\xC0F\xC0E\x3\x2"); + sb.Append("\x2\x2\xC0F\xC10\x3\x2\x2\x2\xC10\xC12\x3\x2\x2\x2\xC11\xC13"); + sb.Append("\x5\x1D8\xED\x2\xC12\xC11\x3\x2\x2\x2\xC12\xC13\x3\x2\x2\x2"); + sb.Append("\xC13\xC15\x3\x2\x2\x2\xC14\xC16\x5\x1DA\xEE\x2\xC15\xC14\x3"); + sb.Append("\x2\x2\x2\xC15\xC16\x3\x2\x2\x2\xC16\xC18\x3\x2\x2\x2\xC17\xC19"); + sb.Append("\x5\x1DC\xEF\x2\xC18\xC17\x3\x2\x2\x2\xC18\xC19\x3\x2\x2\x2"); + sb.Append("\xC19\xC1B\x3\x2\x2\x2\xC1A\xC1C\x5\x1DE\xF0\x2\xC1B\xC1A\x3"); + sb.Append("\x2\x2\x2\xC1B\xC1C\x3\x2\x2\x2\xC1C\xC1E\x3\x2\x2\x2\xC1D\xC1F"); + sb.Append("\x5\x1E0\xF1\x2\xC1E\xC1D\x3\x2\x2\x2\xC1E\xC1F\x3\x2\x2\x2"); + sb.Append("\xC1F\xC54\x3\x2\x2\x2\xC20\xC22\x5\x1D6\xEC\x2\xC21\xC23\x5"); + sb.Append("\x1D8\xED\x2\xC22\xC21\x3\x2\x2\x2\xC22\xC23\x3\x2\x2\x2\xC23"); + sb.Append("\xC25\x3\x2\x2\x2\xC24\xC26\x5\x1DA\xEE\x2\xC25\xC24\x3\x2\x2"); + sb.Append("\x2\xC25\xC26\x3\x2\x2\x2\xC26\xC28\x3\x2\x2\x2\xC27\xC29\x5"); + sb.Append("\x1DC\xEF\x2\xC28\xC27\x3\x2\x2\x2\xC28\xC29\x3\x2\x2\x2\xC29"); + sb.Append("\xC2B\x3\x2\x2\x2\xC2A\xC2C\x5\x1DE\xF0\x2\xC2B\xC2A\x3\x2\x2"); + sb.Append("\x2\xC2B\xC2C\x3\x2\x2\x2\xC2C\xC2E\x3\x2\x2\x2\xC2D\xC2F\x5"); + sb.Append("\x1E0\xF1\x2\xC2E\xC2D\x3\x2\x2\x2\xC2E\xC2F\x3\x2\x2\x2\xC2F"); + sb.Append("\xC54\x3\x2\x2\x2\xC30\xC32\x5\x1D8\xED\x2\xC31\xC33\x5\x1DA"); + sb.Append("\xEE\x2\xC32\xC31\x3\x2\x2\x2\xC32\xC33\x3\x2\x2\x2\xC33\xC35"); + sb.Append("\x3\x2\x2\x2\xC34\xC36\x5\x1DC\xEF\x2\xC35\xC34\x3\x2\x2\x2"); + sb.Append("\xC35\xC36\x3\x2\x2\x2\xC36\xC38\x3\x2\x2\x2\xC37\xC39\x5\x1DE"); + sb.Append("\xF0\x2\xC38\xC37\x3\x2\x2\x2\xC38\xC39\x3\x2\x2\x2\xC39\xC3B"); + sb.Append("\x3\x2\x2\x2\xC3A\xC3C\x5\x1E0\xF1\x2\xC3B\xC3A\x3\x2\x2\x2"); + sb.Append("\xC3B\xC3C\x3\x2\x2\x2\xC3C\xC54\x3\x2\x2\x2\xC3D\xC3F\x5\x1DA"); + sb.Append("\xEE\x2\xC3E\xC40\x5\x1DC\xEF\x2\xC3F\xC3E\x3\x2\x2\x2\xC3F"); + sb.Append("\xC40\x3\x2\x2\x2\xC40\xC42\x3\x2\x2\x2\xC41\xC43\x5\x1DE\xF0"); + sb.Append("\x2\xC42\xC41\x3\x2\x2\x2\xC42\xC43\x3\x2\x2\x2\xC43\xC45\x3"); + sb.Append("\x2\x2\x2\xC44\xC46\x5\x1E0\xF1\x2\xC45\xC44\x3\x2\x2\x2\xC45"); + sb.Append("\xC46\x3\x2\x2\x2\xC46\xC54\x3\x2\x2\x2\xC47\xC49\x5\x1DC\xEF"); + sb.Append("\x2\xC48\xC4A\x5\x1DE\xF0\x2\xC49\xC48\x3\x2\x2\x2\xC49\xC4A"); + sb.Append("\x3\x2\x2\x2\xC4A\xC4C\x3\x2\x2\x2\xC4B\xC4D\x5\x1E0\xF1\x2"); + sb.Append("\xC4C\xC4B\x3\x2\x2\x2\xC4C\xC4D\x3\x2\x2\x2\xC4D\xC54\x3\x2"); + sb.Append("\x2\x2\xC4E\xC50\x5\x1DE\xF0\x2\xC4F\xC51\x5\x1E0\xF1\x2\xC50"); + sb.Append("\xC4F\x3\x2\x2\x2\xC50\xC51\x3\x2\x2\x2\xC51\xC54\x3\x2\x2\x2"); + sb.Append("\xC52\xC54\x5\x1E0\xF1\x2\xC53\xBDE\x3\x2\x2\x2\xC53\xBF7\x3"); + sb.Append("\x2\x2\x2\xC53\xC0D\x3\x2\x2\x2\xC53\xC20\x3\x2\x2\x2\xC53\xC30"); + sb.Append("\x3\x2\x2\x2\xC53\xC3D\x3\x2\x2\x2\xC53\xC47\x3\x2\x2\x2\xC53"); + sb.Append("\xC4E\x3\x2\x2\x2\xC53\xC52\x3\x2\x2\x2\xC54\x1CF\x3\x2\x2\x2"); + sb.Append("\xC55\xC59\x5\x1E8\xF5\x2\xC56\xC59\a\xC5\x2\x2\xC57\xC59\x5"); + sb.Append("\x1E4\xF3\x2\xC58\xC55\x3\x2\x2\x2\xC58\xC56\x3\x2\x2\x2\xC58"); + sb.Append("\xC57\x3\x2\x2\x2\xC59\xC5A\x3\x2\x2\x2\xC5A\xC5B\t\v\x2\x2"); + sb.Append("\xC5B\x1D1\x3\x2\x2\x2\xC5C\xC60\x5\x1E8\xF5\x2\xC5D\xC60\a"); + sb.Append("\xC5\x2\x2\xC5E\xC60\x5\x1E4\xF3\x2\xC5F\xC5C\x3\x2\x2\x2\xC5F"); + sb.Append("\xC5D\x3\x2\x2\x2\xC5F\xC5E\x3\x2\x2\x2\xC60\xC61\x3\x2\x2\x2"); + sb.Append("\xC61\xC62\t\f\x2\x2\xC62\x1D3\x3\x2\x2\x2\xC63\xC67\x5\x1E8"); + sb.Append("\xF5\x2\xC64\xC67\a\xC5\x2\x2\xC65\xC67\x5\x1E4\xF3\x2\xC66"); + sb.Append("\xC63\x3\x2\x2\x2\xC66\xC64\x3\x2\x2\x2\xC66\xC65\x3\x2\x2\x2"); + sb.Append("\xC67\xC68\x3\x2\x2\x2\xC68\xC69\t\r\x2\x2\xC69\x1D5\x3\x2\x2"); + sb.Append("\x2\xC6A\xC6E\x5\x1E8\xF5\x2\xC6B\xC6E\a\xC5\x2\x2\xC6C\xC6E"); + sb.Append("\x5\x1E4\xF3\x2\xC6D\xC6A\x3\x2\x2\x2\xC6D\xC6B\x3\x2\x2\x2"); + sb.Append("\xC6D\xC6C\x3\x2\x2\x2\xC6E\xC6F\x3\x2\x2\x2\xC6F\xC70\t\xE"); + sb.Append("\x2\x2\xC70\x1D7\x3\x2\x2\x2\xC71\xC75\x5\x1E8\xF5\x2\xC72\xC75"); + sb.Append("\a\xC5\x2\x2\xC73\xC75\x5\x1E4\xF3\x2\xC74\xC71\x3\x2\x2\x2"); + sb.Append("\xC74\xC72\x3\x2\x2\x2\xC74\xC73\x3\x2\x2\x2\xC75\xC76\x3\x2"); + sb.Append("\x2\x2\xC76\xC77\t\xF\x2\x2\xC77\x1D9\x3\x2\x2\x2\xC78\xC7C"); + sb.Append("\x5\x1E8\xF5\x2\xC79\xC7C\a\xC5\x2\x2\xC7A\xC7C\x5\x1E4\xF3"); + sb.Append("\x2\xC7B\xC78\x3\x2\x2\x2\xC7B\xC79\x3\x2\x2\x2\xC7B\xC7A\x3"); + sb.Append("\x2\x2\x2\xC7C\xC7D\x3\x2\x2\x2\xC7D\xC7E\t\x10\x2\x2\xC7E\x1DB"); + sb.Append("\x3\x2\x2\x2\xC7F\xC83\x5\x1E8\xF5\x2\xC80\xC83\a\xC5\x2\x2"); + sb.Append("\xC81\xC83\x5\x1E4\xF3\x2\xC82\xC7F\x3\x2\x2\x2\xC82\xC80\x3"); + sb.Append("\x2\x2\x2\xC82\xC81\x3\x2\x2\x2\xC83\xC84\x3\x2\x2\x2\xC84\xC85"); + sb.Append("\t\x11\x2\x2\xC85\x1DD\x3\x2\x2\x2\xC86\xC8A\x5\x1E8\xF5\x2"); + sb.Append("\xC87\xC8A\a\xC5\x2\x2\xC88\xC8A\x5\x1E4\xF3\x2\xC89\xC86\x3"); + sb.Append("\x2\x2\x2\xC89\xC87\x3\x2\x2\x2\xC89\xC88\x3\x2\x2\x2\xC8A\xC8B"); + sb.Append("\x3\x2\x2\x2\xC8B\xC8C\t\x12\x2\x2\xC8C\x1DF\x3\x2\x2\x2\xC8D"); + sb.Append("\xC91\x5\x1E8\xF5\x2\xC8E\xC91\a\xC5\x2\x2\xC8F\xC91\x5\x1E4"); + sb.Append("\xF3\x2\xC90\xC8D\x3\x2\x2\x2\xC90\xC8E\x3\x2\x2\x2\xC90\xC8F"); + sb.Append("\x3\x2\x2\x2\xC91\xC92\x3\x2\x2\x2\xC92\xC93\t\x13\x2\x2\xC93"); + sb.Append("\x1E1\x3\x2\x2\x2\xC94\xC95\t\x14\x2\x2\xC95\x1E3\x3\x2\x2\x2"); + sb.Append("\xC96\xC99\a\x91\x2\x2\xC97\xC98\a\x98\x2\x2\xC98\xC9A\x5\x19A"); + sb.Append("\xCE\x2\xC99\xC97\x3\x2\x2\x2\xC99\xC9A\x3\x2\x2\x2\xC9A\x1E5"); + sb.Append("\x3\x2\x2\x2\xC9B\xCA1\x5\x1E8\xF5\x2\xC9C\xCA1\x5\x1EA\xF6"); + sb.Append("\x2\xC9D\xCA1\am\x2\x2\xC9E\xCA1\an\x2\x2\xC9F\xCA1\ao\x2\x2"); + sb.Append("\xCA0\xC9B\x3\x2\x2\x2\xCA0\xC9C\x3\x2\x2\x2\xCA0\xC9D\x3\x2"); + sb.Append("\x2\x2\xCA0\xC9E\x3\x2\x2\x2\xCA0\xC9F\x3\x2\x2\x2\xCA1\x1E7"); + sb.Append("\x3\x2\x2\x2\xCA2\xCA5\a\xA3\x2\x2\xCA3\xCA5\a\xA0\x2\x2\xCA4"); + sb.Append("\xCA2\x3\x2\x2\x2\xCA4\xCA3\x3\x2\x2\x2\xCA4\xCA5\x3\x2\x2\x2"); + sb.Append("\xCA5\xCA6\x3\x2\x2\x2\xCA6\xCA7\x5\x1E2\xF2\x2\xCA7\x1E9\x3"); + sb.Append("\x2\x2\x2\xCA8\xCAB\a\xC4\x2\x2\xCA9\xCAB\a\xC3\x2\x2\xCAA\xCA8"); + sb.Append("\x3\x2\x2\x2\xCAA\xCA9\x3\x2\x2\x2\xCAB\x1EB\x3\x2\x2\x2\xCAC"); + sb.Append("\xCB0\x5\x1E6\xF4\x2\xCAD\xCB0\x5\x1EE\xF8\x2\xCAE\xCB0\x5\x1F0"); + sb.Append("\xF9\x2\xCAF\xCAC\x3\x2\x2\x2\xCAF\xCAD\x3\x2\x2\x2\xCAF\xCAE"); + sb.Append("\x3\x2\x2\x2\xCB0\x1ED\x3\x2\x2\x2\xCB1\xCB2\a\x96\x2\x2\xCB2"); + sb.Append("\xCB3\x5\x1F4\xFB\x2\xCB3\xCB4\a\x97\x2\x2\xCB4\x1EF\x3\x2\x2"); + sb.Append("\x2\xCB5\xCB7\a\x94\x2\x2\xCB6\xCB8\x5\x1F2\xFA\x2\xCB7\xCB6"); + sb.Append("\x3\x2\x2\x2\xCB7\xCB8\x3\x2\x2\x2\xCB8\xCB9\x3\x2\x2\x2\xCB9"); + sb.Append("\xCBA\a\x95\x2\x2\xCBA\x1F1\x3\x2\x2\x2\xCBB\xCC0\x5\x1EC\xF7"); + sb.Append("\x2\xCBC\xCBD\a\x99\x2\x2\xCBD\xCBF\x5\x1EC\xF7\x2\xCBE\xCBC"); + sb.Append("\x3\x2\x2\x2\xCBF\xCC2\x3\x2\x2\x2\xCC0\xCBE\x3\x2\x2\x2\xCC0"); + sb.Append("\xCC1\x3\x2\x2\x2\xCC1\xCC4\x3\x2\x2\x2\xCC2\xCC0\x3\x2\x2\x2"); + sb.Append("\xCC3\xCC5\a\x99\x2\x2\xCC4\xCC3\x3\x2\x2\x2\xCC4\xCC5\x3\x2"); + sb.Append("\x2\x2\xCC5\x1F3\x3\x2\x2\x2\xCC6\xCCB\x5\x1F6\xFC\x2\xCC7\xCC8"); + sb.Append("\a\x99\x2\x2\xCC8\xCCA\x5\x1F6\xFC\x2\xCC9\xCC7\x3\x2\x2\x2"); + sb.Append("\xCCA\xCCD\x3\x2\x2\x2\xCCB\xCC9\x3\x2\x2\x2\xCCB\xCCC\x3\x2"); + sb.Append("\x2\x2\xCCC\xCCF\x3\x2\x2\x2\xCCD\xCCB\x3\x2\x2\x2\xCCE\xCD0"); + sb.Append("\a\x99\x2\x2\xCCF\xCCE\x3\x2\x2\x2\xCCF\xCD0\x3\x2\x2\x2\xCD0"); + sb.Append("\x1F5\x3\x2\x2\x2\xCD1\xCD4\x5\x1EA\xF6\x2\xCD2\xCD4\x5\x1C8"); + sb.Append("\xE5\x2\xCD3\xCD1\x3\x2\x2\x2\xCD3\xCD2\x3\x2\x2\x2\xCD4\xCD5"); + sb.Append("\x3\x2\x2\x2\xCD5\xCD6\a\x98\x2\x2\xCD6\xCD7\x5\x1EC\xF7\x2"); + sb.Append("\xCD7\x1F7\x3\x2\x2\x2\x1D7\x1FA\x1FC\x204\x206\x214\x218\x21B"); + sb.Append("\x21E\x223\x226\x22A\x233\x23C\x243\x252\x255\x25C\x268\x270"); + sb.Append("\x273\x276\x27B\x28B\x28E\x295\x299\x29F\x2A2\x2A6\x2AB\x2AF"); + sb.Append("\x2B3\x2B8\x2BC\x2C5\x2C8\x2CA\x2CF\x2D3\x2D8\x2E2\x2E8\x2EC"); + sb.Append("\x2F2\x2F6\x2FB\x2FF\x305\x30A\x313\x318\x31B\x322\x327\x32F"); + sb.Append("\x335\x33B\x33F\x343\x346\x349\x34D\x351\x356\x35A\x35F\x363"); + sb.Append("\x36A\x370\x377\x37B\x382\x387\x38D\x397\x39F\x3A6\x3AC\x3B0"); + sb.Append("\x3B3\x3BA\x3BF\x3C1\x3C7\x3CD\x3DC\x3E1\x3E5\x3EB\x3EE\x3F3"); + sb.Append("\x3F9\x404\x40B\x40E\x411\x415\x417\x41D\x420\x427\x42F\x432"); + sb.Append("\x434\x43B\x442\x448\x44C\x453\x458\x45B\x460\x469\x46D\x47D"); + sb.Append("\x485\x48B\x490\x493\x496\x49A\x49D\x4A3\x4AE\x4B3\x4B6\x4C8"); + sb.Append("\x4CD\x4D5\x4DC\x4E0\x4E7\x4F5\x4F7\x4FD\x50C\x50F\x514\x51C"); + sb.Append("\x520\x524\x527\x52C\x531\x534\x538\x53F\x547\x54F\x556\x55C"); + sb.Append("\x55E\x563\x56B\x570\x574\x577\x57E\x58A\x598\x59D\x5A5\x5AB"); + sb.Append("\x5AE\x5B5\x5BB\x5C1\x5C8\x5CE\x5D1\x5D4\x5D9\x5E1\x5ED\x5F0"); + sb.Append("\x5F9\x5FF\x603\x606\x609\x613\x619\x61C\x621\x624\x628\x62E"); + sb.Append("\x631\x637\x644\x649\x64B\x654\x657\x65A\x662\x66B\x66E\x676"); + sb.Append("\x67C\x680\x683\x68A\x690\x699\x6A6\x6AD\x6B6\x6B9\x6BC\x6C3"); + sb.Append("\x6C9\x6CE\x6D4\x6DA\x6DD\x6E5\x6EB\x6EF\x6F2\x6F5\x6FC\x700"); + sb.Append("\x707\x70B\x70F\x713\x715\x726\x72A\x72D\x731\x734\x73F\x748"); + sb.Append("\x74E\x750\x765\x76C\x772\x777\x77F\x782\x78B\x794\x797\x799"); + sb.Append("\x79C\x7A0\x7A3\x7A6\x7B0\x7BE\x7C1\x7CC\x7CF\x7D5\x7DC\x7E4"); + sb.Append("\x7EC\x7F2\x7FB\x801\x805\x809\x80B\x80F\x817\x81D\x821\x825"); + sb.Append("\x827\x82B\x82F\x834\x83D\x840\x844\x84E\x852\x854\x85F\x862"); + sb.Append("\x869\x871\x87C\x88A\x897\x89A\x89E\x8A7\x8AC\x8AF\x8B3\x8BB"); + sb.Append("\x8C1\x8C5\x8CF\x8D6\x8DB\x8DF\x8E5\x8EA\x8EE\x8F6\x8F9\x8FD"); + sb.Append("\x902\x90A\x913\x91B\x923\x92B\x93A\x944\x948\x94F\x953\x95F"); + sb.Append("\x97B\x98C\x990\x99A\x99D\x9A2\x9A7\x9AB\x9AF\x9B3\x9BA\x9C2"); + sb.Append("\x9C6\x9C9\x9CE\x9D1\x9D6\x9DC\x9E8\x9EB\x9F2\x9F6\xA04\xA0C"); + sb.Append("\xA15\xA1D\xA21\xA26\xA2C\xA2F\xA31\xA3A\xA40\xA48\xA51\xA57"); + sb.Append("\xA5B\xA64\xA70\xA72\xA76\xA7C\xA81\xA84\xA87\xA8D\xA92\xA96"); + sb.Append("\xA9A\xA9E\xAAB\xAB3\xAB5\xABC\xAC6\xACB\xACE\xAD1\xAD4\xADC"); + sb.Append("\xAE3\xAE7\xAEE\xAF6\xAFE\xB03\xB07\xB0E\xB11\xB18\xB1B\xB22"); + sb.Append("\xB2A\xB37\xB3B\xB43\xB4E\xB53\xB59\xB5E\xB65\xB6F\xB77\xB7E"); + sb.Append("\xB86\xB8B\xB8F\xB92\xB98\xB9C\xBD3\xBD8\xBDC\xBE0\xBE3\xBE6"); + sb.Append("\xBE9\xBEC\xBEF\xBF2\xBF5\xBF9\xBFC\xBFF\xC02\xC05\xC08\xC0B"); + sb.Append("\xC0F\xC12\xC15\xC18\xC1B\xC1E\xC22\xC25\xC28\xC2B\xC2E\xC32"); + sb.Append("\xC35\xC38\xC3B\xC3F\xC42\xC45\xC49\xC4C\xC50\xC53\xC58\xC5F"); + sb.Append("\xC66\xC6D\xC74\xC7B\xC82\xC89\xC90\xC99\xCA0\xCA4\xCAA\xCAF"); + sb.Append("\xCB7\xCC0\xCC4\xCCB\xCCF\xCD3"); + return sb.ToString(); + } + + public static readonly ATN _ATN = + new ATNDeserializer().Deserialize(_serializedATN.ToCharArray()); + + +} +} // namespace com.espertech.esper.epl.generated diff --git a/NEsper.Core/NEsper.Core/epl/generated/EsperEPL2GrammarVisitor.cs b/NEsper.Core/NEsper.Core/epl/generated/EsperEPL2GrammarVisitor.cs new file mode 100755 index 000000000..55869fca0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/generated/EsperEPL2GrammarVisitor.cs @@ -0,0 +1,1687 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// ANTLR Version: 4.6 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +// Generated from C:\Src\Espertech\NEsper-master\NEsper\grammar\EsperEPL2Grammar.g4 by ANTLR 4.6 + +// Unreachable code detected +#pragma warning disable 0162 +// The variable '...' is assigned but its value is never used +#pragma warning disable 0219 +// Missing XML comment for publicly visible type or member '...' +#pragma warning disable 1591 +// Ambiguous reference in cref attribute +#pragma warning disable 419 + +namespace com.espertech.esper.epl.generated { + + using System; + using System.Collections.Generic; + +using Antlr4.Runtime.Misc; +using Antlr4.Runtime.Tree; +using IToken = Antlr4.Runtime.IToken; + +/// +/// This interface defines a complete generic visitor for a parse tree produced +/// by . +/// +/// The return type of the visit operation. +[System.CodeDom.Compiler.GeneratedCode("ANTLR", "4.6")] +[System.CLSCompliant(false)] +public interface IEsperEPL2GrammarVisitor : IParseTreeVisitor { + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitStartPatternExpressionRule([NotNull] EsperEPL2GrammarParser.StartPatternExpressionRuleContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitStartEPLExpressionRule([NotNull] EsperEPL2GrammarParser.StartEPLExpressionRuleContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitStartEventPropertyRule([NotNull] EsperEPL2GrammarParser.StartEventPropertyRuleContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitStartJsonValueRule([NotNull] EsperEPL2GrammarParser.StartJsonValueRuleContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitExpressionDecl([NotNull] EsperEPL2GrammarParser.ExpressionDeclContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitExpressionDialect([NotNull] EsperEPL2GrammarParser.ExpressionDialectContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitExpressionDef([NotNull] EsperEPL2GrammarParser.ExpressionDefContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitExpressionLambdaDecl([NotNull] EsperEPL2GrammarParser.ExpressionLambdaDeclContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitExpressionTypeAnno([NotNull] EsperEPL2GrammarParser.ExpressionTypeAnnoContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitAnnotationEnum([NotNull] EsperEPL2GrammarParser.AnnotationEnumContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitElementValuePairsEnum([NotNull] EsperEPL2GrammarParser.ElementValuePairsEnumContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitElementValuePairEnum([NotNull] EsperEPL2GrammarParser.ElementValuePairEnumContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitElementValueEnum([NotNull] EsperEPL2GrammarParser.ElementValueEnumContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitElementValueArrayEnum([NotNull] EsperEPL2GrammarParser.ElementValueArrayEnumContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitEplExpression([NotNull] EsperEPL2GrammarParser.EplExpressionContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitContextExpr([NotNull] EsperEPL2GrammarParser.ContextExprContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitSelectExpr([NotNull] EsperEPL2GrammarParser.SelectExprContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitOnExpr([NotNull] EsperEPL2GrammarParser.OnExprContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitOnStreamExpr([NotNull] EsperEPL2GrammarParser.OnStreamExprContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitUpdateExpr([NotNull] EsperEPL2GrammarParser.UpdateExprContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitUpdateDetails([NotNull] EsperEPL2GrammarParser.UpdateDetailsContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitOnMergeExpr([NotNull] EsperEPL2GrammarParser.OnMergeExprContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitMergeItem([NotNull] EsperEPL2GrammarParser.MergeItemContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitMergeMatched([NotNull] EsperEPL2GrammarParser.MergeMatchedContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitMergeMatchedItem([NotNull] EsperEPL2GrammarParser.MergeMatchedItemContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitMergeUnmatched([NotNull] EsperEPL2GrammarParser.MergeUnmatchedContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitMergeUnmatchedItem([NotNull] EsperEPL2GrammarParser.MergeUnmatchedItemContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitMergeInsert([NotNull] EsperEPL2GrammarParser.MergeInsertContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitOnSelectExpr([NotNull] EsperEPL2GrammarParser.OnSelectExprContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitOnUpdateExpr([NotNull] EsperEPL2GrammarParser.OnUpdateExprContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitOnSelectInsertExpr([NotNull] EsperEPL2GrammarParser.OnSelectInsertExprContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitOnSelectInsertFromClause([NotNull] EsperEPL2GrammarParser.OnSelectInsertFromClauseContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitOutputClauseInsert([NotNull] EsperEPL2GrammarParser.OutputClauseInsertContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitOnDeleteExpr([NotNull] EsperEPL2GrammarParser.OnDeleteExprContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitOnSetExpr([NotNull] EsperEPL2GrammarParser.OnSetExprContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitOnSetAssignmentList([NotNull] EsperEPL2GrammarParser.OnSetAssignmentListContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitOnSetAssignment([NotNull] EsperEPL2GrammarParser.OnSetAssignmentContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitOnExprFrom([NotNull] EsperEPL2GrammarParser.OnExprFromContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitCreateWindowExpr([NotNull] EsperEPL2GrammarParser.CreateWindowExprContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitCreateWindowExprModelAfter([NotNull] EsperEPL2GrammarParser.CreateWindowExprModelAfterContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitCreateIndexExpr([NotNull] EsperEPL2GrammarParser.CreateIndexExprContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitCreateIndexColumnList([NotNull] EsperEPL2GrammarParser.CreateIndexColumnListContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitCreateIndexColumn([NotNull] EsperEPL2GrammarParser.CreateIndexColumnContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitCreateVariableExpr([NotNull] EsperEPL2GrammarParser.CreateVariableExprContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitCreateTableExpr([NotNull] EsperEPL2GrammarParser.CreateTableExprContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitCreateTableColumnList([NotNull] EsperEPL2GrammarParser.CreateTableColumnListContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitCreateTableColumn([NotNull] EsperEPL2GrammarParser.CreateTableColumnContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitCreateTableColumnPlain([NotNull] EsperEPL2GrammarParser.CreateTableColumnPlainContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitCreateColumnList([NotNull] EsperEPL2GrammarParser.CreateColumnListContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitCreateColumnListElement([NotNull] EsperEPL2GrammarParser.CreateColumnListElementContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitCreateSelectionList([NotNull] EsperEPL2GrammarParser.CreateSelectionListContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitCreateSelectionListElement([NotNull] EsperEPL2GrammarParser.CreateSelectionListElementContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitCreateSchemaExpr([NotNull] EsperEPL2GrammarParser.CreateSchemaExprContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitCreateSchemaDef([NotNull] EsperEPL2GrammarParser.CreateSchemaDefContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitFafDelete([NotNull] EsperEPL2GrammarParser.FafDeleteContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitFafUpdate([NotNull] EsperEPL2GrammarParser.FafUpdateContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitFafInsert([NotNull] EsperEPL2GrammarParser.FafInsertContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitCreateDataflow([NotNull] EsperEPL2GrammarParser.CreateDataflowContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitGopList([NotNull] EsperEPL2GrammarParser.GopListContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitGop([NotNull] EsperEPL2GrammarParser.GopContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitGopParams([NotNull] EsperEPL2GrammarParser.GopParamsContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitGopParamsItemList([NotNull] EsperEPL2GrammarParser.GopParamsItemListContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitGopParamsItem([NotNull] EsperEPL2GrammarParser.GopParamsItemContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitGopParamsItemMany([NotNull] EsperEPL2GrammarParser.GopParamsItemManyContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitGopParamsItemAs([NotNull] EsperEPL2GrammarParser.GopParamsItemAsContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitGopOut([NotNull] EsperEPL2GrammarParser.GopOutContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitGopOutItem([NotNull] EsperEPL2GrammarParser.GopOutItemContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitGopOutTypeList([NotNull] EsperEPL2GrammarParser.GopOutTypeListContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitGopOutTypeParam([NotNull] EsperEPL2GrammarParser.GopOutTypeParamContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitGopOutTypeItem([NotNull] EsperEPL2GrammarParser.GopOutTypeItemContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitGopDetail([NotNull] EsperEPL2GrammarParser.GopDetailContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitGopConfig([NotNull] EsperEPL2GrammarParser.GopConfigContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitCreateContextExpr([NotNull] EsperEPL2GrammarParser.CreateContextExprContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitCreateExpressionExpr([NotNull] EsperEPL2GrammarParser.CreateExpressionExprContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitCreateContextDetail([NotNull] EsperEPL2GrammarParser.CreateContextDetailContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitContextContextNested([NotNull] EsperEPL2GrammarParser.ContextContextNestedContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitCreateContextChoice([NotNull] EsperEPL2GrammarParser.CreateContextChoiceContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitCreateContextDistinct([NotNull] EsperEPL2GrammarParser.CreateContextDistinctContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitCreateContextRangePoint([NotNull] EsperEPL2GrammarParser.CreateContextRangePointContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitCreateContextFilter([NotNull] EsperEPL2GrammarParser.CreateContextFilterContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitCreateContextPartitionItem([NotNull] EsperEPL2GrammarParser.CreateContextPartitionItemContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitCreateContextCoalesceItem([NotNull] EsperEPL2GrammarParser.CreateContextCoalesceItemContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitCreateContextGroupItem([NotNull] EsperEPL2GrammarParser.CreateContextGroupItemContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitCreateSchemaQual([NotNull] EsperEPL2GrammarParser.CreateSchemaQualContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitVariantList([NotNull] EsperEPL2GrammarParser.VariantListContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitVariantListElement([NotNull] EsperEPL2GrammarParser.VariantListElementContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitIntoTableExpr([NotNull] EsperEPL2GrammarParser.IntoTableExprContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitInsertIntoExpr([NotNull] EsperEPL2GrammarParser.InsertIntoExprContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitColumnList([NotNull] EsperEPL2GrammarParser.ColumnListContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitFromClause([NotNull] EsperEPL2GrammarParser.FromClauseContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitRegularJoin([NotNull] EsperEPL2GrammarParser.RegularJoinContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitOuterJoinList([NotNull] EsperEPL2GrammarParser.OuterJoinListContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitOuterJoin([NotNull] EsperEPL2GrammarParser.OuterJoinContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitOuterJoinIdent([NotNull] EsperEPL2GrammarParser.OuterJoinIdentContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitOuterJoinIdentPair([NotNull] EsperEPL2GrammarParser.OuterJoinIdentPairContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitWhereClause([NotNull] EsperEPL2GrammarParser.WhereClauseContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitSelectClause([NotNull] EsperEPL2GrammarParser.SelectClauseContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitSelectionList([NotNull] EsperEPL2GrammarParser.SelectionListContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitSelectionListElement([NotNull] EsperEPL2GrammarParser.SelectionListElementContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitSelectionListElementExpr([NotNull] EsperEPL2GrammarParser.SelectionListElementExprContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitSelectionListElementAnno([NotNull] EsperEPL2GrammarParser.SelectionListElementAnnoContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitStreamSelector([NotNull] EsperEPL2GrammarParser.StreamSelectorContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitStreamExpression([NotNull] EsperEPL2GrammarParser.StreamExpressionContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitForExpr([NotNull] EsperEPL2GrammarParser.ForExprContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitPatternInclusionExpression([NotNull] EsperEPL2GrammarParser.PatternInclusionExpressionContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitDatabaseJoinExpression([NotNull] EsperEPL2GrammarParser.DatabaseJoinExpressionContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitMethodJoinExpression([NotNull] EsperEPL2GrammarParser.MethodJoinExpressionContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitViewExpressions([NotNull] EsperEPL2GrammarParser.ViewExpressionsContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitViewExpressionWNamespace([NotNull] EsperEPL2GrammarParser.ViewExpressionWNamespaceContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitViewExpressionOptNamespace([NotNull] EsperEPL2GrammarParser.ViewExpressionOptNamespaceContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitViewWParameters([NotNull] EsperEPL2GrammarParser.ViewWParametersContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitGroupByListExpr([NotNull] EsperEPL2GrammarParser.GroupByListExprContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitGroupByListChoice([NotNull] EsperEPL2GrammarParser.GroupByListChoiceContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitGroupByCubeOrRollup([NotNull] EsperEPL2GrammarParser.GroupByCubeOrRollupContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitGroupByGroupingSets([NotNull] EsperEPL2GrammarParser.GroupByGroupingSetsContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitGroupBySetsChoice([NotNull] EsperEPL2GrammarParser.GroupBySetsChoiceContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitGroupByCombinableExpr([NotNull] EsperEPL2GrammarParser.GroupByCombinableExprContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitOrderByListExpr([NotNull] EsperEPL2GrammarParser.OrderByListExprContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitOrderByListElement([NotNull] EsperEPL2GrammarParser.OrderByListElementContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitHavingClause([NotNull] EsperEPL2GrammarParser.HavingClauseContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitOutputLimit([NotNull] EsperEPL2GrammarParser.OutputLimitContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitOutputLimitAndTerm([NotNull] EsperEPL2GrammarParser.OutputLimitAndTermContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitOutputLimitAfter([NotNull] EsperEPL2GrammarParser.OutputLimitAfterContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitRowLimit([NotNull] EsperEPL2GrammarParser.RowLimitContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitCrontabLimitParameterSet([NotNull] EsperEPL2GrammarParser.CrontabLimitParameterSetContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitWhenClause([NotNull] EsperEPL2GrammarParser.WhenClauseContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitElseClause([NotNull] EsperEPL2GrammarParser.ElseClauseContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitMatchRecog([NotNull] EsperEPL2GrammarParser.MatchRecogContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitMatchRecogPartitionBy([NotNull] EsperEPL2GrammarParser.MatchRecogPartitionByContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitMatchRecogMeasures([NotNull] EsperEPL2GrammarParser.MatchRecogMeasuresContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitMatchRecogMeasureItem([NotNull] EsperEPL2GrammarParser.MatchRecogMeasureItemContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitMatchRecogMatchesSelection([NotNull] EsperEPL2GrammarParser.MatchRecogMatchesSelectionContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitMatchRecogPattern([NotNull] EsperEPL2GrammarParser.MatchRecogPatternContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitMatchRecogMatchesAfterSkip([NotNull] EsperEPL2GrammarParser.MatchRecogMatchesAfterSkipContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitMatchRecogMatchesInterval([NotNull] EsperEPL2GrammarParser.MatchRecogMatchesIntervalContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitMatchRecogPatternAlteration([NotNull] EsperEPL2GrammarParser.MatchRecogPatternAlterationContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitMatchRecogPatternConcat([NotNull] EsperEPL2GrammarParser.MatchRecogPatternConcatContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitMatchRecogPatternUnary([NotNull] EsperEPL2GrammarParser.MatchRecogPatternUnaryContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitMatchRecogPatternNested([NotNull] EsperEPL2GrammarParser.MatchRecogPatternNestedContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitMatchRecogPatternPermute([NotNull] EsperEPL2GrammarParser.MatchRecogPatternPermuteContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitMatchRecogPatternAtom([NotNull] EsperEPL2GrammarParser.MatchRecogPatternAtomContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitMatchRecogPatternRepeat([NotNull] EsperEPL2GrammarParser.MatchRecogPatternRepeatContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitMatchRecogDefine([NotNull] EsperEPL2GrammarParser.MatchRecogDefineContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitMatchRecogDefineItem([NotNull] EsperEPL2GrammarParser.MatchRecogDefineItemContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitExpression([NotNull] EsperEPL2GrammarParser.ExpressionContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitCaseExpression([NotNull] EsperEPL2GrammarParser.CaseExpressionContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitEvalOrExpression([NotNull] EsperEPL2GrammarParser.EvalOrExpressionContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitEvalAndExpression([NotNull] EsperEPL2GrammarParser.EvalAndExpressionContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitBitWiseExpression([NotNull] EsperEPL2GrammarParser.BitWiseExpressionContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitNegatedExpression([NotNull] EsperEPL2GrammarParser.NegatedExpressionContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitEvalEqualsExpression([NotNull] EsperEPL2GrammarParser.EvalEqualsExpressionContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitEvalRelationalExpression([NotNull] EsperEPL2GrammarParser.EvalRelationalExpressionContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitInSubSelectQuery([NotNull] EsperEPL2GrammarParser.InSubSelectQueryContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitConcatenationExpr([NotNull] EsperEPL2GrammarParser.ConcatenationExprContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitAdditiveExpression([NotNull] EsperEPL2GrammarParser.AdditiveExpressionContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitMultiplyExpression([NotNull] EsperEPL2GrammarParser.MultiplyExpressionContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitUnaryExpression([NotNull] EsperEPL2GrammarParser.UnaryExpressionContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitSubstitutionCanChain([NotNull] EsperEPL2GrammarParser.SubstitutionCanChainContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitChainedFunction([NotNull] EsperEPL2GrammarParser.ChainedFunctionContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitNewAssign([NotNull] EsperEPL2GrammarParser.NewAssignContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitRowSubSelectExpression([NotNull] EsperEPL2GrammarParser.RowSubSelectExpressionContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitSubSelectGroupExpression([NotNull] EsperEPL2GrammarParser.SubSelectGroupExpressionContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitExistsSubSelectExpression([NotNull] EsperEPL2GrammarParser.ExistsSubSelectExpressionContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitSubQueryExpr([NotNull] EsperEPL2GrammarParser.SubQueryExprContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitSubSelectFilterExpr([NotNull] EsperEPL2GrammarParser.SubSelectFilterExprContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitArrayExpression([NotNull] EsperEPL2GrammarParser.ArrayExpressionContext context); + /// + /// Visit a parse tree produced by the builtin_sum + /// labeled alternative in . + /// + /// The parse tree. + /// The visitor result. + Result VisitBuiltin_sum([NotNull] EsperEPL2GrammarParser.Builtin_sumContext context); + /// + /// Visit a parse tree produced by the builtin_avg + /// labeled alternative in . + /// + /// The parse tree. + /// The visitor result. + Result VisitBuiltin_avg([NotNull] EsperEPL2GrammarParser.Builtin_avgContext context); + /// + /// Visit a parse tree produced by the builtin_cnt + /// labeled alternative in . + /// + /// The parse tree. + /// The visitor result. + Result VisitBuiltin_cnt([NotNull] EsperEPL2GrammarParser.Builtin_cntContext context); + /// + /// Visit a parse tree produced by the builtin_median + /// labeled alternative in . + /// + /// The parse tree. + /// The visitor result. + Result VisitBuiltin_median([NotNull] EsperEPL2GrammarParser.Builtin_medianContext context); + /// + /// Visit a parse tree produced by the builtin_stddev + /// labeled alternative in . + /// + /// The parse tree. + /// The visitor result. + Result VisitBuiltin_stddev([NotNull] EsperEPL2GrammarParser.Builtin_stddevContext context); + /// + /// Visit a parse tree produced by the builtin_avedev + /// labeled alternative in . + /// + /// The parse tree. + /// The visitor result. + Result VisitBuiltin_avedev([NotNull] EsperEPL2GrammarParser.Builtin_avedevContext context); + /// + /// Visit a parse tree produced by the builtin_firstlastwindow + /// labeled alternative in . + /// + /// The parse tree. + /// The visitor result. + Result VisitBuiltin_firstlastwindow([NotNull] EsperEPL2GrammarParser.Builtin_firstlastwindowContext context); + /// + /// Visit a parse tree produced by the builtin_coalesce + /// labeled alternative in . + /// + /// The parse tree. + /// The visitor result. + Result VisitBuiltin_coalesce([NotNull] EsperEPL2GrammarParser.Builtin_coalesceContext context); + /// + /// Visit a parse tree produced by the builtin_prev + /// labeled alternative in . + /// + /// The parse tree. + /// The visitor result. + Result VisitBuiltin_prev([NotNull] EsperEPL2GrammarParser.Builtin_prevContext context); + /// + /// Visit a parse tree produced by the builtin_prevtail + /// labeled alternative in . + /// + /// The parse tree. + /// The visitor result. + Result VisitBuiltin_prevtail([NotNull] EsperEPL2GrammarParser.Builtin_prevtailContext context); + /// + /// Visit a parse tree produced by the builtin_prevcount + /// labeled alternative in . + /// + /// The parse tree. + /// The visitor result. + Result VisitBuiltin_prevcount([NotNull] EsperEPL2GrammarParser.Builtin_prevcountContext context); + /// + /// Visit a parse tree produced by the builtin_prevwindow + /// labeled alternative in . + /// + /// The parse tree. + /// The visitor result. + Result VisitBuiltin_prevwindow([NotNull] EsperEPL2GrammarParser.Builtin_prevwindowContext context); + /// + /// Visit a parse tree produced by the builtin_prior + /// labeled alternative in . + /// + /// The parse tree. + /// The visitor result. + Result VisitBuiltin_prior([NotNull] EsperEPL2GrammarParser.Builtin_priorContext context); + /// + /// Visit a parse tree produced by the builtin_grouping + /// labeled alternative in . + /// + /// The parse tree. + /// The visitor result. + Result VisitBuiltin_grouping([NotNull] EsperEPL2GrammarParser.Builtin_groupingContext context); + /// + /// Visit a parse tree produced by the builtin_groupingid + /// labeled alternative in . + /// + /// The parse tree. + /// The visitor result. + Result VisitBuiltin_groupingid([NotNull] EsperEPL2GrammarParser.Builtin_groupingidContext context); + /// + /// Visit a parse tree produced by the builtin_instanceof + /// labeled alternative in . + /// + /// The parse tree. + /// The visitor result. + Result VisitBuiltin_instanceof([NotNull] EsperEPL2GrammarParser.Builtin_instanceofContext context); + /// + /// Visit a parse tree produced by the builtin_typeof + /// labeled alternative in . + /// + /// The parse tree. + /// The visitor result. + Result VisitBuiltin_typeof([NotNull] EsperEPL2GrammarParser.Builtin_typeofContext context); + /// + /// Visit a parse tree produced by the builtin_cast + /// labeled alternative in . + /// + /// The parse tree. + /// The visitor result. + Result VisitBuiltin_cast([NotNull] EsperEPL2GrammarParser.Builtin_castContext context); + /// + /// Visit a parse tree produced by the builtin_exists + /// labeled alternative in . + /// + /// The parse tree. + /// The visitor result. + Result VisitBuiltin_exists([NotNull] EsperEPL2GrammarParser.Builtin_existsContext context); + /// + /// Visit a parse tree produced by the builtin_currts + /// labeled alternative in . + /// + /// The parse tree. + /// The visitor result. + Result VisitBuiltin_currts([NotNull] EsperEPL2GrammarParser.Builtin_currtsContext context); + /// + /// Visit a parse tree produced by the builtin_istream + /// labeled alternative in . + /// + /// The parse tree. + /// The visitor result. + Result VisitBuiltin_istream([NotNull] EsperEPL2GrammarParser.Builtin_istreamContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitFirstLastWindowAggregation([NotNull] EsperEPL2GrammarParser.FirstLastWindowAggregationContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitEventPropertyOrLibFunction([NotNull] EsperEPL2GrammarParser.EventPropertyOrLibFunctionContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitLibFunction([NotNull] EsperEPL2GrammarParser.LibFunctionContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitLibFunctionWithClass([NotNull] EsperEPL2GrammarParser.LibFunctionWithClassContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitLibFunctionNoClass([NotNull] EsperEPL2GrammarParser.LibFunctionNoClassContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitFuncIdentTop([NotNull] EsperEPL2GrammarParser.FuncIdentTopContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitFuncIdentInner([NotNull] EsperEPL2GrammarParser.FuncIdentInnerContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitFuncIdentChained([NotNull] EsperEPL2GrammarParser.FuncIdentChainedContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitLibFunctionArgs([NotNull] EsperEPL2GrammarParser.LibFunctionArgsContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitLibFunctionArgItem([NotNull] EsperEPL2GrammarParser.LibFunctionArgItemContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitBetweenList([NotNull] EsperEPL2GrammarParser.BetweenListContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitPatternExpression([NotNull] EsperEPL2GrammarParser.PatternExpressionContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitFollowedByExpression([NotNull] EsperEPL2GrammarParser.FollowedByExpressionContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitFollowedByRepeat([NotNull] EsperEPL2GrammarParser.FollowedByRepeatContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitOrExpression([NotNull] EsperEPL2GrammarParser.OrExpressionContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitAndExpression([NotNull] EsperEPL2GrammarParser.AndExpressionContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitMatchUntilExpression([NotNull] EsperEPL2GrammarParser.MatchUntilExpressionContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitQualifyExpression([NotNull] EsperEPL2GrammarParser.QualifyExpressionContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitGuardPostFix([NotNull] EsperEPL2GrammarParser.GuardPostFixContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitDistinctExpressionList([NotNull] EsperEPL2GrammarParser.DistinctExpressionListContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitDistinctExpressionAtom([NotNull] EsperEPL2GrammarParser.DistinctExpressionAtomContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitAtomicExpression([NotNull] EsperEPL2GrammarParser.AtomicExpressionContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitObserverExpression([NotNull] EsperEPL2GrammarParser.ObserverExpressionContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitGuardWhereExpression([NotNull] EsperEPL2GrammarParser.GuardWhereExpressionContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitGuardWhileExpression([NotNull] EsperEPL2GrammarParser.GuardWhileExpressionContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitMatchUntilRange([NotNull] EsperEPL2GrammarParser.MatchUntilRangeContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitEventFilterExpression([NotNull] EsperEPL2GrammarParser.EventFilterExpressionContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitPropertyExpression([NotNull] EsperEPL2GrammarParser.PropertyExpressionContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitPropertyExpressionAtomic([NotNull] EsperEPL2GrammarParser.PropertyExpressionAtomicContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitPropertyExpressionSelect([NotNull] EsperEPL2GrammarParser.PropertyExpressionSelectContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitPropertySelectionList([NotNull] EsperEPL2GrammarParser.PropertySelectionListContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitPropertySelectionListElement([NotNull] EsperEPL2GrammarParser.PropertySelectionListElementContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitPropertyStreamSelector([NotNull] EsperEPL2GrammarParser.PropertyStreamSelectorContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitTypeExpressionAnnotation([NotNull] EsperEPL2GrammarParser.TypeExpressionAnnotationContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitPatternFilterExpression([NotNull] EsperEPL2GrammarParser.PatternFilterExpressionContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitPatternFilterAnnotation([NotNull] EsperEPL2GrammarParser.PatternFilterAnnotationContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitClassIdentifier([NotNull] EsperEPL2GrammarParser.ClassIdentifierContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitSlashIdentifier([NotNull] EsperEPL2GrammarParser.SlashIdentifierContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitExpressionListWithNamed([NotNull] EsperEPL2GrammarParser.ExpressionListWithNamedContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitExpressionListWithNamedWithTime([NotNull] EsperEPL2GrammarParser.ExpressionListWithNamedWithTimeContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitExpressionWithNamed([NotNull] EsperEPL2GrammarParser.ExpressionWithNamedContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitExpressionWithNamedWithTime([NotNull] EsperEPL2GrammarParser.ExpressionWithNamedWithTimeContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitExpressionNamedParameter([NotNull] EsperEPL2GrammarParser.ExpressionNamedParameterContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitExpressionNamedParameterWithTime([NotNull] EsperEPL2GrammarParser.ExpressionNamedParameterWithTimeContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitExpressionList([NotNull] EsperEPL2GrammarParser.ExpressionListContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitExpressionWithTimeList([NotNull] EsperEPL2GrammarParser.ExpressionWithTimeListContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitExpressionWithTime([NotNull] EsperEPL2GrammarParser.ExpressionWithTimeContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitExpressionWithTimeInclLast([NotNull] EsperEPL2GrammarParser.ExpressionWithTimeInclLastContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitExpressionQualifyable([NotNull] EsperEPL2GrammarParser.ExpressionQualifyableContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitLastWeekdayOperand([NotNull] EsperEPL2GrammarParser.LastWeekdayOperandContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitLastOperand([NotNull] EsperEPL2GrammarParser.LastOperandContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitFrequencyOperand([NotNull] EsperEPL2GrammarParser.FrequencyOperandContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitRangeOperand([NotNull] EsperEPL2GrammarParser.RangeOperandContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitLastOperator([NotNull] EsperEPL2GrammarParser.LastOperatorContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitWeekDayOperator([NotNull] EsperEPL2GrammarParser.WeekDayOperatorContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitNumericParameterList([NotNull] EsperEPL2GrammarParser.NumericParameterListContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitNumericListParameter([NotNull] EsperEPL2GrammarParser.NumericListParameterContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitEventProperty([NotNull] EsperEPL2GrammarParser.EventPropertyContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitEventPropertyAtomic([NotNull] EsperEPL2GrammarParser.EventPropertyAtomicContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitEventPropertyIdent([NotNull] EsperEPL2GrammarParser.EventPropertyIdentContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitKeywordAllowedIdent([NotNull] EsperEPL2GrammarParser.KeywordAllowedIdentContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitEscapableStr([NotNull] EsperEPL2GrammarParser.EscapableStrContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitEscapableIdent([NotNull] EsperEPL2GrammarParser.EscapableIdentContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitTimePeriod([NotNull] EsperEPL2GrammarParser.TimePeriodContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitYearPart([NotNull] EsperEPL2GrammarParser.YearPartContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitMonthPart([NotNull] EsperEPL2GrammarParser.MonthPartContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitWeekPart([NotNull] EsperEPL2GrammarParser.WeekPartContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitDayPart([NotNull] EsperEPL2GrammarParser.DayPartContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitHourPart([NotNull] EsperEPL2GrammarParser.HourPartContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitMinutePart([NotNull] EsperEPL2GrammarParser.MinutePartContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitSecondPart([NotNull] EsperEPL2GrammarParser.SecondPartContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitMillisecondPart([NotNull] EsperEPL2GrammarParser.MillisecondPartContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitMicrosecondPart([NotNull] EsperEPL2GrammarParser.MicrosecondPartContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitNumber([NotNull] EsperEPL2GrammarParser.NumberContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitSubstitution([NotNull] EsperEPL2GrammarParser.SubstitutionContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitConstant([NotNull] EsperEPL2GrammarParser.ConstantContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitNumberconstant([NotNull] EsperEPL2GrammarParser.NumberconstantContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitStringconstant([NotNull] EsperEPL2GrammarParser.StringconstantContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitJsonvalue([NotNull] EsperEPL2GrammarParser.JsonvalueContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitJsonobject([NotNull] EsperEPL2GrammarParser.JsonobjectContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitJsonarray([NotNull] EsperEPL2GrammarParser.JsonarrayContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitJsonelements([NotNull] EsperEPL2GrammarParser.JsonelementsContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitJsonmembers([NotNull] EsperEPL2GrammarParser.JsonmembersContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitJsonpair([NotNull] EsperEPL2GrammarParser.JsonpairContext context); +} +} // namespace com.espertech.esper.epl.generated diff --git a/NEsper.Core/NEsper.Core/epl/join/assemble/AssemblyStrategyTreeBuilder.cs b/NEsper.Core/NEsper.Core/epl/join/assemble/AssemblyStrategyTreeBuilder.cs new file mode 100755 index 000000000..c751b3a8e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/assemble/AssemblyStrategyTreeBuilder.cs @@ -0,0 +1,165 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.IO; +using System.Collections.Generic; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.join.plan; +using com.espertech.esper.util; + + +namespace com.espertech.esper.epl.join.assemble +{ + /// + /// Builds a tree of assembly nodes given a strategy for how to join streams. + /// + + public class AssemblyStrategyTreeBuilder + { + /// Builds a tree of from join strategy information. + /// the root stream supplying the event to evaluate + /// + /// a map in which the key is the stream number to supply an event, + /// and the value is an array of streams to find events in for the given event + /// + /// indicates which streams are required join streams versus optional streams + /// + /// root assembly node + /// + + public static BaseAssemblyNodeFactory Build(int rootStream, IDictionary streamsJoinedPerStream, bool[] isRequiredPerStream) + { + if (streamsJoinedPerStream.Count < 3) + { + throw new ArgumentException("Not a 3-way join"); + } + if ((rootStream < 0) || (rootStream >= streamsJoinedPerStream.Count)) + { + throw new ArgumentException("Invalid root stream"); + } + if (isRequiredPerStream.Length != streamsJoinedPerStream.Count) + { + throw new ArgumentException("Arrays not matching up"); + } + + NStreamOuterQueryPlanBuilder.VerifyJoinedPerStream(rootStream, streamsJoinedPerStream); + + if (Log.IsDebugEnabled) + { + Log.Debug( + ".build Building node for root stream " + rootStream + + " streamsJoinedPerStream=" + NStreamOuterQueryPlanBuilder.Print(streamsJoinedPerStream) + + " isRequiredPerStream=" + isRequiredPerStream.Render()); + } + + BaseAssemblyNodeFactory topNode = CreateNode( + true, + rootStream, + streamsJoinedPerStream.Count, + streamsJoinedPerStream[rootStream], + isRequiredPerStream); + + RecursiveBuild(rootStream, topNode, streamsJoinedPerStream, isRequiredPerStream); + + if (Log.IsDebugEnabled) + { + StringWriter buf = new StringWriter(); + IndentWriter indentWriter = new IndentWriter(buf, 0, 2); + topNode.PrintDescendends(indentWriter); + + Log.Debug(".build Dumping root node for stream " + rootStream + ": \n" + buf.ToString()); + } + + return topNode; + } + + private static void RecursiveBuild(int parentStreamNum, BaseAssemblyNodeFactory parentNode, IDictionary streamsJoinedPerStream, bool[] isRequiredPerStream) + { + int numStreams = streamsJoinedPerStream.Count; + + for (int i = 0; i < streamsJoinedPerStream[parentStreamNum].Length; i++) + { + int streamJoined = streamsJoinedPerStream[parentStreamNum][i]; + BaseAssemblyNodeFactory childNode = CreateNode( + false, + streamJoined, + numStreams, + streamsJoinedPerStream[streamJoined], + isRequiredPerStream); + parentNode.AddChild(childNode); + + if (streamsJoinedPerStream[streamJoined].Length > 0) + { + RecursiveBuild(streamJoined, childNode, streamsJoinedPerStream, isRequiredPerStream); + } + } + } + + private static BaseAssemblyNodeFactory CreateNode(bool isRoot, int streamNum, int numStreams, int[] joinedStreams, bool[] isRequiredPerStream) + { + if (joinedStreams.Length == 0) + { + return new LeafAssemblyNodeFactory(streamNum, numStreams); + } + if (joinedStreams.Length == 1) + { + int joinedStream = joinedStreams[0]; + bool isRequired = isRequiredPerStream[joinedStream]; + if (isRequired) + { + if (isRoot) + { + return new RootRequiredAssemblyNodeFactory(streamNum, numStreams); + } + else + { + return new BranchRequiredAssemblyNodeFactory(streamNum, numStreams); + } + } + else + { + if (isRoot) + { + return new RootOptionalAssemblyNodeFactory(streamNum, numStreams); + } + else + { + return new BranchOptionalAssemblyNodeFactory(streamNum, numStreams); + } + } + } + + // Determine if all substream are outer (optional) joins + bool allSubStreamsOptional = true; + for (int i = 0; i < joinedStreams.Length; i++) + { + int stream = joinedStreams[i]; + if (isRequiredPerStream[stream]) + { + allSubStreamsOptional = false; + } + } + + // Make node for building a cartesian product + if (isRoot) + { + return new RootCartProdAssemblyNodeFactory(streamNum, numStreams, allSubStreamsOptional); + } + else + { + return new CartesianProdAssemblyNodeFactory(streamNum, numStreams, allSubStreamsOptional); + } + } + + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/assemble/BaseAssemblyNode.cs b/NEsper.Core/NEsper.Core/epl/join/assemble/BaseAssemblyNode.cs new file mode 100755 index 000000000..58a2dc859 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/assemble/BaseAssemblyNode.cs @@ -0,0 +1,158 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.join.rep; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.join.assemble +{ + /// + /// Represents a node in a tree responsible for assembling outer join query results. + /// + /// The tree is double-linked, child nodes know each parent and parent know all child nodes. + /// + /// Each specific subclass of this abstract assembly node is dedicated to assembling results for + /// a certain event stream. + /// + + public abstract class BaseAssemblyNode : ResultAssembler + { + /// Returns the stream number. + /// stream number + /// + + public virtual int StreamNum + { + get { return _streamNum; } + } + + /// Returns an array of stream numbers that lists all child node's stream numbers. + /// child node stream numbers + /// + public virtual int[] Substreams + { + get + { + IList substreams = new List(); + RecusiveAddSubstreams(substreams); + + // copy to array + int[] substreamArr = new int[substreams.Count]; + int count = 0; + + foreach (Int32 stream in substreams) + { + substreamArr[count++] = stream; + } + + return substreamArr; + } + + } + /// Parent node. + protected ResultAssembler ParentNode; + + /// Child nodes. + private readonly IList _childNodes; + + /// Stream number. + private readonly int _streamNum; + + /// Number of streams in statement. + private readonly int _numStreams; + + /// Ctor. + /// stream number of the event stream that this node assembles results for. + /// + /// number of streams + /// + + protected BaseAssemblyNode(int streamNum, int numStreams) + { + _streamNum = streamNum; + _numStreams = numStreams; + _childNodes = new List(); + } + + /// Provides results to assembly nodes for initialization. + /// is a list of result nodes per stream + /// + public abstract void Init(IList[] result); + + /// + /// Process results. + /// + /// is a list of result nodes per stream + /// The result final rows. + /// The result root event. + public abstract void Process(IList[] result, ICollection resultFinalRows, EventBean resultRootEvent); + + /// Output this node using writer, not outputting child nodes. + /// to use for output + /// + public abstract void Print(IndentWriter indentWriter); + + /// Add a child node. + /// to add + /// + public virtual void AddChild(BaseAssemblyNode childNode) + { + childNode.ParentNode = this; + _childNodes.Add(childNode); + } + + internal int NumStreams + { + get { return _numStreams; } + } + + /// Returns child nodes. + /// child nodes + /// + public IList ChildNodes + { + get + { + return _childNodes; + } + } + + /// Gets or sets the parent node. + /// parent node + /// + public virtual ResultAssembler ParentAssembler + { + // get { return ParentNode; } + set { ParentNode = value; } + } + + private void RecusiveAddSubstreams(IList substreams) + { + substreams.Add(_streamNum); + + foreach (BaseAssemblyNode child in _childNodes) + { + child.RecusiveAddSubstreams(substreams); + } + } + + public abstract void Result(EventBean[] row, + int fromStreamNum, + EventBean myEvent, + Node myNode, + ICollection resultFinalRows, + EventBean resultRootEvent); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/assemble/BaseAssemblyNodeFactory.cs b/NEsper.Core/NEsper.Core/epl/join/assemble/BaseAssemblyNodeFactory.cs new file mode 100755 index 000000000..0ed2b160a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/assemble/BaseAssemblyNodeFactory.cs @@ -0,0 +1,181 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.join.assemble +{ + /// + /// Represents the factory of a node in a tree responsible for assembling outer join query results. + /// The tree of factory nodes is double-linked, child nodes know each parent and parent know all child nodes. + /// + public abstract class BaseAssemblyNodeFactory + { + /// + /// Parent node. + /// + private BaseAssemblyNodeFactory _parentNode; + + /// + /// Child nodes. + /// + private readonly IList _childNodes; + + /// + /// Stream number. + /// + private readonly int _streamNum; + + /// + /// Number of streams in statement. + /// + private readonly int _numStreams; + + /// + /// Ctor. + /// + /// stream number of the event stream that this node assembles results for. + /// number of streams + protected BaseAssemblyNodeFactory(int streamNum, int numStreams) + { + _streamNum = streamNum; + _numStreams = numStreams; + _childNodes = new List(4); + } + + public abstract BaseAssemblyNode MakeAssemblerUnassociated(); + + /// + /// Output this node using writer, not outputting child nodes. + /// + /// to use for output + public abstract void Print(IndentWriter indentWriter); + + /// + /// Set parent node. + /// + public void SetParent(BaseAssemblyNodeFactory parent) + { + _parentNode = parent; + } + + public BaseAssemblyNodeFactory ParentNode + { + get { return _parentNode; } + } + + public int NumStreams + { + get { return _numStreams; } + } + + /// + /// Add a child node. + /// + /// to add + public virtual void AddChild(BaseAssemblyNodeFactory childNode) + { + childNode._parentNode = this; + _childNodes.Add(childNode); + } + + /// + /// Returns the stream number. + /// + /// stream number + protected internal int StreamNum + { + get { return _streamNum; } + } + + /// + /// Returns child nodes. + /// + /// child nodes + public IList ChildNodes + { + get { return _childNodes; } + } + + /// + /// Output this node and all descendent nodes using writer, outputting child nodes. + /// + /// to output to + public void PrintDescendends(IndentWriter indentWriter) + { + Print(indentWriter); + foreach (BaseAssemblyNodeFactory child in _childNodes) + { + indentWriter.IncrIndent(); + child.Print(indentWriter); + indentWriter.DecrIndent(); + } + } + + /// + /// Returns all descendent nodes to the top node in a list in which the utmost descendants are + /// listed first and the top node itself is listed last. + /// + /// is the root node of a tree structure + /// list of nodes with utmost descendants first ordered by level of depth in tree with top node last + public static IList GetDescendentNodesBottomUp(BaseAssemblyNodeFactory topNode) + { + var result = new List(); + + // Map to hold per level of the node (1 to Count depth) of node a list of nodes, if any + // exist at that level + var nodesPerLevel = new OrderedDictionary>(); + + // Recursively enter all aggregate functions and their level into map + RecursiveAggregateEnter(topNode, nodesPerLevel, 1); + + // Done if none found + if (nodesPerLevel.IsEmpty()) + { + throw new IllegalStateException("EmptyFalse collection for nodes per level"); + } + + // From the deepest (highest) level to the lowest, add aggregates to list + int deepLevel = nodesPerLevel.Keys.Last(); + for (int i = deepLevel; i >= 1; i--) + { + var list = nodesPerLevel.Get(i); + if (list == null) + { + continue; + } + result.AddAll(list); + } + + return result; + } + + private static void RecursiveAggregateEnter(BaseAssemblyNodeFactory currentNode, IDictionary> nodesPerLevel, int currentLevel) + { + // ask all child nodes to enter themselves + foreach (BaseAssemblyNodeFactory node in currentNode._childNodes) + { + RecursiveAggregateEnter(node, nodesPerLevel, currentLevel + 1); + } + + // Add myself to list + var aggregates = nodesPerLevel.Get(currentLevel); + if (aggregates == null) + { + aggregates = new LinkedList(); + nodesPerLevel.Put(currentLevel, aggregates); + } + aggregates.Add(currentNode); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/join/assemble/BranchOptionalAssemblyNode.cs b/NEsper.Core/NEsper.Core/epl/join/assemble/BranchOptionalAssemblyNode.cs new file mode 100755 index 000000000..50f4ff97e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/assemble/BranchOptionalAssemblyNode.cs @@ -0,0 +1,139 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.epl.join.rep; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.join.assemble +{ + /// + /// Assembly node for an event stream that is a branch with a single optional child node below it. + /// + public class BranchOptionalAssemblyNode : BaseAssemblyNode + { + private IList _resultsForStream; + + // For tracking when we only have a single event for this stream as a result + private Node _singleResultNode; + private EventBean _singleResultEvent; + private bool _haveChildResults; + + // For tracking when we have multiple events for this stream + private ICollection _completedEvents; + + /// Ctor. + /// is the stream number + /// is the number of streams + public BranchOptionalAssemblyNode(int streamNum, int numStreams) + : base(streamNum, numStreams) + { + } + + public override void Init(IList[] result) + { + _resultsForStream = result[StreamNum]; + _singleResultNode = null; + _singleResultEvent = null; + _haveChildResults = false; + + if (_resultsForStream != null) + { + int numNodes = _resultsForStream.Count; + if (numNodes == 1) + { + Node node = _resultsForStream[0]; + ICollection nodeEvents = node.Events; + + // If there is a single result event (typical case) + if (nodeEvents.Count == 1) + { + _singleResultNode = node; + _singleResultEvent = nodeEvents.First(); + } + } + + if (_singleResultNode == null) + { + _completedEvents = new HashSet(); + } + } + } + + public override void Process(IList[] result, ICollection resultFinalRows, EventBean resultRootEvent) + { + // there cannot be child nodes to compute a cartesian product if this node had no results + if (_resultsForStream == null) + { + return; + } + + // If this node's result set consisted of a single event + if (_singleResultNode != null) + { + // If there are no child results, post a row + if (!_haveChildResults) + { + var row = new EventBean[NumStreams]; + row[StreamNum] = _singleResultEvent; + ParentNode.Result(row, StreamNum, _singleResultNode.ParentEvent, _singleResultNode, resultFinalRows, resultRootEvent); + } + + // if there were child results we are done since they have already been posted to the parent + return; + } + + // We have multiple events for this node, generate an event row for each event not yet received from + // event rows generated by the child node. + foreach (Node node in _resultsForStream) + { + ICollection events = node.Events; + foreach (EventBean theEvent in events) + { + if (_completedEvents.Contains(theEvent)) + { + continue; + } + ProcessEvent(theEvent, node, resultFinalRows, resultRootEvent); + } + } + } + + public override void Result(EventBean[] row, int fromStreamNum, EventBean myEvent, Node myNode, ICollection resultFinalRows, EventBean resultRootEvent) + { + row[StreamNum] = myEvent; + Node parentResultNode = myNode.Parent; + ParentNode.Result(row, StreamNum, myNode.ParentEvent, parentResultNode, resultFinalRows, resultRootEvent); + + // record the fact that an event that was generated by a child + _haveChildResults = true; + + // If we had more then on result event for this stream, we need to track all the different events + // generated by the child node + if (_singleResultNode == null) + { + _completedEvents.Add(myEvent); + } + } + + public override void Print(IndentWriter indentWriter) + { + indentWriter.WriteLine("BranchOptionalAssemblyNode StreamNum=" + StreamNum); + } + + private void ProcessEvent(EventBean theEvent, Node currentNode, ICollection resultFinalRows, EventBean resultRootEvent) + { + var row = new EventBean[NumStreams]; + row[StreamNum] = theEvent; + ParentNode.Result(row, StreamNum, currentNode.ParentEvent, currentNode.Parent, resultFinalRows, resultRootEvent); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/assemble/BranchOptionalAssemblyNodeFactory.cs b/NEsper.Core/NEsper.Core/epl/join/assemble/BranchOptionalAssemblyNodeFactory.cs new file mode 100755 index 000000000..23867b741 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/assemble/BranchOptionalAssemblyNodeFactory.cs @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.join.assemble +{ + /// + /// Assembly factory node for an event stream that is a branch with a single optional child node below it. + /// + public class BranchOptionalAssemblyNodeFactory : BaseAssemblyNodeFactory + { + /// + /// Ctor. + /// + /// is the stream number + /// is the number of streams + public BranchOptionalAssemblyNodeFactory(int streamNum, int numStreams) + : base(streamNum, numStreams) + { + } + + public override void Print(IndentWriter indentWriter) + { + indentWriter.WriteLine("BranchOptionalAssemblyNode streamNum=" + StreamNum); + } + + public override BaseAssemblyNode MakeAssemblerUnassociated() + { + return new BranchOptionalAssemblyNode(StreamNum, NumStreams); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/join/assemble/BranchRequiredAssemblyNode.cs b/NEsper.Core/NEsper.Core/epl/join/assemble/BranchRequiredAssemblyNode.cs new file mode 100755 index 000000000..610d9cfa6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/assemble/BranchRequiredAssemblyNode.cs @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.join.rep; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.join.assemble +{ + /// + /// Assembly node for an event stream that is a branch with a single required child node below it. + /// + public class BranchRequiredAssemblyNode : BaseAssemblyNode + { + /// Ctor. + /// is the stream number + /// is the number of streams + public BranchRequiredAssemblyNode(int streamNum, int numStreams) + : base(streamNum, numStreams) + { + } + + public override void Init(IList[] result) + { + // need not be concerned with results, all is passed from the child node + } + + public override void Process(IList[] result, ICollection resultFinalRows, EventBean resultRootEvent) + { + // no action here, since we have a required child row + // The single required child generates all events that may exist + } + + public override void Result(EventBean[] row, int fromStreamNum, EventBean myEvent, Node myNode, ICollection resultFinalRows, EventBean resultRootEvent) + { + row[StreamNum] = myEvent; + Node parentResultNode = myNode.Parent; + ParentNode.Result(row, StreamNum, myNode.ParentEvent, parentResultNode, resultFinalRows, resultRootEvent); + } + + public override void Print(IndentWriter indentWriter) + { + indentWriter.WriteLine("BranchRequiredAssemblyNode StreamNum=" + StreamNum); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/assemble/BranchRequiredAssemblyNodeFactory.cs b/NEsper.Core/NEsper.Core/epl/join/assemble/BranchRequiredAssemblyNodeFactory.cs new file mode 100755 index 000000000..5d3ab05de --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/assemble/BranchRequiredAssemblyNodeFactory.cs @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.join.assemble +{ + /// + /// Assembly node factory for an event stream that is a branch with a single required child node below it. + /// + public class BranchRequiredAssemblyNodeFactory : BaseAssemblyNodeFactory + { + /// + /// Ctor. + /// + /// is the stream number + /// is the number of streams + public BranchRequiredAssemblyNodeFactory(int streamNum, int numStreams) + : base(streamNum, numStreams) + { + } + + public override void Print(IndentWriter indentWriter) + { + indentWriter.WriteLine("BranchRequiredAssemblyNode streamNum=" + StreamNum); + } + + public override BaseAssemblyNode MakeAssemblerUnassociated() + { + return new BranchRequiredAssemblyNode(StreamNum, NumStreams); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/join/assemble/CartesianProdAssemblyNode.cs b/NEsper.Core/NEsper.Core/epl/join/assemble/CartesianProdAssemblyNode.cs new file mode 100755 index 000000000..d202946ba --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/assemble/CartesianProdAssemblyNode.cs @@ -0,0 +1,265 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.join.rep; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.join.assemble +{ + /// + /// Assembly node for an event stream that is a branch with a two or more child + /// nodes (required and optional) below it. + /// + public class CartesianProdAssemblyNode : BaseAssemblyNode + { + private readonly int[] _childStreamIndex; // maintain mapping of stream number to index in array + private readonly bool _allSubStreamsOptional; + + // keep a reference to results for processing optional child nodes not generating results + private IList _resultsForStream; + + // maintain for each child the list of stream number descending that child + private int[][] _subStreamsNumsPerChild; + private int[][] _combinedSubStreams; // for any cartesian product past 2 streams + + // For tracking when we only have a single event for this stream as a result + private Node _singleResultNode; + private EventBean _singleResultParentEvent; + private IList[] _singleResultRowsPerStream; + private bool _haveChildResults; + + // For tracking when we have multiple events for this stream + private IDictionary _completedEvents; + + /// + /// Ctor. + /// + /// is the stream number + /// is the number of streams + /// true if all child nodes to this node are optional, or false ifone or more child nodes are required for a result. + public CartesianProdAssemblyNode(int streamNum, int numStreams, bool allSubStreamsOptional, int[] childStreamIndex) + : base(streamNum, numStreams) + { + _childStreamIndex = childStreamIndex; + _allSubStreamsOptional = allSubStreamsOptional; + } + + public override void Init(IList[] result) + { + _resultsForStream = result[StreamNum]; + _singleResultNode = null; + _singleResultParentEvent = null; + _singleResultRowsPerStream = null; + _haveChildResults = false; + + if (_subStreamsNumsPerChild == null) + { + if (ChildNodes.Count < 2) + { + throw new IllegalStateException("Expecting at least 2 child nodes"); + } + _subStreamsNumsPerChild = new int[ChildNodes.Count][]; + for (int i = 0; i < ChildNodes.Count; i++) + { + _subStreamsNumsPerChild[i] = ChildNodes[i].Substreams; + } + + _combinedSubStreams = RootCartProdAssemblyNode.ComputeCombined(_subStreamsNumsPerChild); + } + + if (_resultsForStream != null) + { + int numNodes = _resultsForStream.Count; + if (numNodes == 1) + { + Node node = _resultsForStream[0]; + ICollection nodeEvents = node.Events; + + // If there is a single result event (typical case) + if (nodeEvents.Count == 1) + { + _singleResultNode = node; + _singleResultParentEvent = nodeEvents.First(); + _singleResultRowsPerStream = new IList[ChildNodes.Count]; + } + } + + if (_singleResultNode == null) + { + _completedEvents = new Dictionary(); + } + } + else + { + _completedEvents = new Dictionary(); + } + } + + public override void Process(IList[] result, ICollection resultFinalRows, EventBean resultRootEvent) + { + // there cannot be child nodes to compute a cartesian product if this node had no results + if (_resultsForStream == null) + { + return; + } + + // If this node's result set consisted of a single event + if (_singleResultNode != null) + { + // If no child has posted any rows + if (!_haveChildResults) + { + // And all substreams are optional, generate a row + if (_allSubStreamsOptional) + { + var row = new EventBean[NumStreams]; + row[StreamNum] = _singleResultParentEvent; + ParentNode.Result(row, StreamNum, _singleResultNode.ParentEvent, _singleResultNode, resultFinalRows, resultRootEvent); + } + return; + } + + // Compute the cartesian product + PostCartesian(_singleResultRowsPerStream, _singleResultNode, resultFinalRows, resultRootEvent); + return; + } + + // We have multiple events for this node, generate an event row for each event not yet received from + // event rows generated by the child node. + foreach (Node node in _resultsForStream) + { + ICollection events = node.Events; + foreach (EventBean theEvent in events) + { + ChildStreamResults results = _completedEvents.Get(theEvent); + + // If there were no results for the event posted by any child nodes + if (results == null) + { + if (_allSubStreamsOptional) + { + var row = new EventBean[NumStreams]; + row[StreamNum] = theEvent; + ParentNode.Result(row, StreamNum, node.ParentEvent, node.Parent, resultFinalRows, resultRootEvent); + } + continue; + } + + // Compute the cartesian product + PostCartesian(results.RowsPerStream, node, resultFinalRows, resultRootEvent); + } + } + } + + private void PostCartesian(IList[] rowsPerStream, Node node, ICollection resultFinalRows, EventBean resultRootEvent) + { + IList result = new List(); + CartesianUtil.ComputeCartesian( + rowsPerStream[0], _subStreamsNumsPerChild[0], + rowsPerStream[1], _subStreamsNumsPerChild[1], + result); + + if (rowsPerStream.Length > 2) + { + for (int i = 0; i < _subStreamsNumsPerChild.Length - 2; i++) + { + var product = new List(); + CartesianUtil.ComputeCartesian( + result, _combinedSubStreams[i], + rowsPerStream[i + 2], _subStreamsNumsPerChild[i + 2], + product); + result = product; + } + } + + foreach (EventBean[] row in result) + { + ParentNode.Result(row, StreamNum, node.ParentEvent, node.Parent, resultFinalRows, resultRootEvent); + } + } + + public override void Result(EventBean[] row, int fromStreamNum, EventBean myEvent, Node myNode, ICollection resultFinalRows, EventBean resultRootEvent) + { + // fill event in + row[StreamNum] = myEvent; + int childStreamArrIndex = _childStreamIndex[fromStreamNum]; + + // treat single-event result for this stream + if (_singleResultNode != null) + { + // record the fact that an event that was generated by a child + _haveChildResults = true; + + if (_singleResultRowsPerStream == null) + { + _singleResultRowsPerStream = new IList[ChildNodes.Count]; + } + + IList streamRows = _singleResultRowsPerStream[childStreamArrIndex]; + if (streamRows == null) + { + streamRows = new List(); + _singleResultRowsPerStream[childStreamArrIndex] = streamRows; + } + + streamRows.Add(row); + return; + } + + ChildStreamResults childStreamResults = _completedEvents.Get(myEvent); + if (childStreamResults == null) + { + childStreamResults = new ChildStreamResults(ChildNodes.Count); + _completedEvents.Put(myEvent, childStreamResults); + } + + childStreamResults.Add(childStreamArrIndex, row); + } + + public override void Print(IndentWriter indentWriter) + { + indentWriter.WriteLine("CartesianProdAssemblyNode StreamNum=" + StreamNum); + } + + /// Structure to represent a list of event result rows per stream. + public class ChildStreamResults + { + /// Ctor. + /// number of streams + public ChildStreamResults(int size) + { + RowsPerStream = new IList[size]; + } + + /// Add result from stream. + /// from stream + /// row to add + public void Add(int fromStreamIndex, EventBean[] row) + { + IList rows = RowsPerStream[fromStreamIndex]; + if (rows == null) + { + rows = new List(); + RowsPerStream[fromStreamIndex] = rows; + } + + rows.Add(row); + } + + /// Returns rows per stream. + /// rows per stream + public IList[] RowsPerStream { get; private set; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/assemble/CartesianProdAssemblyNodeFactory.cs b/NEsper.Core/NEsper.Core/epl/join/assemble/CartesianProdAssemblyNodeFactory.cs new file mode 100755 index 000000000..21e2e407f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/assemble/CartesianProdAssemblyNodeFactory.cs @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.join.assemble +{ + /// + /// Assembly node for an event stream that is a branch with a two or more child nodes (required and optional) below it. + /// + public class CartesianProdAssemblyNodeFactory : BaseAssemblyNodeFactory + { + private readonly int[] _childStreamIndex; // maintain mapping of stream number to index in array + private readonly bool _allSubStreamsOptional; + + /// + /// Ctor. + /// + /// is the stream number + /// is the number of streams + /// true if all child nodes to this node are optional, or false ifone or more child nodes are required for a result. + /// + public CartesianProdAssemblyNodeFactory(int streamNum, int numStreams, bool allSubStreamsOptional) + : base(streamNum, numStreams) + { + _childStreamIndex = new int[numStreams]; + _allSubStreamsOptional = allSubStreamsOptional; + } + + public override void AddChild(BaseAssemblyNodeFactory childNode) + { + _childStreamIndex[childNode.StreamNum] = ChildNodes.Count; + base.AddChild(childNode); + } + + public override void Print(IndentWriter indentWriter) + { + indentWriter.WriteLine("CartesianProdAssemblyNode streamNum=" + StreamNum); + } + + public override BaseAssemblyNode MakeAssemblerUnassociated() + { + return new CartesianProdAssemblyNode(StreamNum, NumStreams, _allSubStreamsOptional, _childStreamIndex); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/join/assemble/CartesianUtil.cs b/NEsper.Core/NEsper.Core/epl/join/assemble/CartesianUtil.cs new file mode 100755 index 000000000..e91b61a5b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/assemble/CartesianUtil.cs @@ -0,0 +1,146 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.epl.join.assemble +{ + /// + /// Helper class to compute the cartesian product of the events from two streams. + /// + + public class CartesianUtil + { + /// + /// Form the 2-ary cartesian product between zero or more events from 2 streams. + /// + /// the events from stream one + /// the list of substream numbers to stream one to include in the product + /// the events from stream two + /// the list of substream numbers to stream two to include in the product + /// where the result of the cartesian product is added to + + public static void ComputeCartesian(IList streamOne, int[] subStreamNumsOne, + IList streamTwo, int[] subStreamNumsTwo, + IList resultList) + { + + if ((streamTwo == null) || (streamTwo.Count == 0)) + { + if ((streamOne == null) || (streamOne.Count == 0)) + { + return; + } + + resultList.AddAll(streamOne); + return; + } + + if ((streamOne == null) || (streamOne.Count == 0)) + { + resultList.AddAll(streamTwo); + return; + } + + int streamOneSize = streamOne.Count; + int streamTwoSize = streamTwo.Count; + + if (streamOneSize == 1) + { + // Yes we are re-using the results of stream two, same row reference + CopyToEach(subStreamNumsOne, streamOne[0], streamTwo); + resultList.AddAll(streamTwo); + return; + } + + if (streamTwoSize == 1) + { + // Yes we are re-using the results of stream one, same row reference + CopyToEach(subStreamNumsTwo, streamTwo[0], streamOne); + resultList.AddAll(streamOne); + return; + } + + // we have more then 1 rows each child stream + + // Exchange streams if one is smaller then two + // Since if one has 100 rows the other has 2 then we can re-use the 100 event rows. + if (streamTwoSize > streamOneSize) + { + IList holdRows = streamOne; + int holdSize = streamOneSize; + + streamOne = streamTwo; + streamOneSize = streamTwoSize; + + streamTwo = holdRows; + streamTwoSize = holdSize; + subStreamNumsTwo = subStreamNumsOne; + } + + // allocate resultList of join + int cartesianTotalRows = streamOneSize * streamTwoSize; + int numColumns = streamOne[0].Length; + EventBean[][] results = new EventBean[cartesianTotalRows][]; + + // Allocate and pre-populate copies of stream 1 + int streamOneCount = 0; + foreach (EventBean[] row in streamOne) + { + // first use all events in stream 1 + results[streamOneCount] = row; + + // then allocate copies for each in stream 2 + for (int i = 1; i < streamTwoSize; i++) + { + EventBean[] dupRow = new EventBean[numColumns]; + Array.Copy(row, 0, dupRow, 0, numColumns); + + int index = streamOneSize * i + streamOneCount; + results[index] = dupRow; + } + + streamOneCount++; + } + + // Copy stream 2 rows into rows of stream 1 + int streamTwoCount = 0; + foreach (EventBean[] row in streamTwo) + { + for (int i = 0; i < streamOneSize; i++) + { + int index = streamTwoCount * streamOneSize + i; + Copy(subStreamNumsTwo, row, results[index]); + } + streamTwoCount++; + } + + // Add results + resultList.AddAll(results); + } + + private static void CopyToEach(int[] subStreamNums, EventBean[] sourceRow, IList destRows) + { + foreach (EventBean[] destRow in destRows) + { + Copy(subStreamNums, sourceRow, destRow); + } + } + + private static void Copy(int[] subStreamsFrom, EventBean[] from, EventBean[] to) + { + foreach (int index in subStreamsFrom) { + to[index] = from[index]; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/assemble/LeafAssemblyNode.cs b/NEsper.Core/NEsper.Core/epl/join/assemble/LeafAssemblyNode.cs new file mode 100755 index 000000000..10870beed --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/assemble/LeafAssemblyNode.cs @@ -0,0 +1,70 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.join.rep; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.join.assemble +{ + /// + /// Assembly node for an event stream that is a leaf with a no child nodes below it. + /// + public class LeafAssemblyNode : BaseAssemblyNode + { + /// Ctor. + /// is the stream number + /// is the number of streams + public LeafAssemblyNode(int streamNum, int numStreams) + : base(streamNum, numStreams) + { + } + + public override void Init(IList[] result) + { + } + + public override void Process(IList[] result, ICollection resultFinalRows, EventBean resultRootEvent) + { + IList nodes = result[StreamNum]; + if (nodes == null) + { + return; + } + + foreach (Node node in nodes) + { + ICollection events = node.Events; + foreach (EventBean theEvent in events) + { + ProcessEvent(theEvent, node, resultFinalRows, resultRootEvent); + } + } + } + + private void ProcessEvent(EventBean theEvent, Node currentNode, ICollection resultFinalRows, EventBean resultRootEvent) + { + EventBean[] row = new EventBean[NumStreams]; + row[StreamNum] = theEvent; + ParentNode.Result(row, StreamNum, currentNode.ParentEvent, currentNode.Parent, resultFinalRows, resultRootEvent); + } + + public override void Result(EventBean[] row, int streamNum, EventBean myEvent, Node myNode, ICollection resultFinalRows, EventBean resultRootEvent) + { + throw new UnsupportedOperationException("Leaf node cannot process child results"); + } + + public override void Print(IndentWriter indentWriter) + { + indentWriter.WriteLine("LeafAssemblyNode StreamNum=" + StreamNum); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/join/assemble/LeafAssemblyNodeFactory.cs b/NEsper.Core/NEsper.Core/epl/join/assemble/LeafAssemblyNodeFactory.cs new file mode 100755 index 000000000..11c593c33 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/assemble/LeafAssemblyNodeFactory.cs @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.join.assemble +{ + /// + /// Assembly node factory for an event stream that is a leaf with a no child nodes below it. + /// + public class LeafAssemblyNodeFactory : BaseAssemblyNodeFactory + { + /// + /// Ctor. + /// + /// is the stream number + /// is the number of streams + public LeafAssemblyNodeFactory(int streamNum, int numStreams) + : base(streamNum, numStreams) + { + } + + public override void Print(IndentWriter indentWriter) + { + indentWriter.WriteLine("LeafAssemblyNode streamNum=" + StreamNum); + } + + public override BaseAssemblyNode MakeAssemblerUnassociated() + { + return new LeafAssemblyNode(StreamNum, NumStreams); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/join/assemble/ResultAssembler.cs b/NEsper.Core/NEsper.Core/epl/join/assemble/ResultAssembler.cs new file mode 100755 index 000000000..52ae3a35a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/assemble/ResultAssembler.cs @@ -0,0 +1,33 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.join.rep; + +namespace com.espertech.esper.epl.join.assemble +{ + /// + /// Interface for indicating a result in the form of a single row of multiple events, which could + /// represent either a full result over all streams or a partial result over a subset of streams. + /// + public interface ResultAssembler + { + /// + /// Publish a result row. + /// + /// is the result to publish + /// is the originitor that publishes the row + /// is optional and is the event that led to the row result + /// is optional and is the result node of the event that led to the row result + /// The result final rows. + /// The result root event. + void Result(EventBean[] row, int fromStreamNum, EventBean myEvent, Node myNode, ICollection resultFinalRows, EventBean resultRootEvent); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/assemble/RootCartProdAssemblyNode.cs b/NEsper.Core/NEsper.Core/epl/join/assemble/RootCartProdAssemblyNode.cs new file mode 100755 index 000000000..6f4829cd7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/assemble/RootCartProdAssemblyNode.cs @@ -0,0 +1,188 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.join.rep; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.join.assemble +{ + /// + /// Assembly node for an event stream that is a root with a two or more child nodes below it. + /// + public class RootCartProdAssemblyNode : BaseAssemblyNode + { + private readonly int[] _childStreamIndex; // maintain mapping of stream number to index in array + private readonly IList[] _rowsPerStream; + private readonly bool _allSubStreamsOptional; + + // maintain for each child the list of stream number descending that child + private int[][] _subStreamsNumsPerChild; + private int[][] _combinedSubStreams; // for any cartesian product past 2 streams + private bool _haveChildResults; + + /// + /// Ctor. + /// + /// is the stream number + /// is the number of streams + /// true if all substreams are optional and none are required + /// Index of the child stream. + public RootCartProdAssemblyNode(int streamNum, int numStreams, bool allSubStreamsOptional, int[] childStreamIndex) + : base(streamNum, numStreams) + { + _allSubStreamsOptional = allSubStreamsOptional; + _childStreamIndex = childStreamIndex; + _rowsPerStream = new List[numStreams]; + } + + public override void Init(IList[] result) + { + if (_subStreamsNumsPerChild == null) + { + if (ChildNodes.Count < 2) + { + throw new IllegalStateException("Expecting at least 2 child nodes"); + } + + var childNodes = ChildNodes; + _subStreamsNumsPerChild = new int[childNodes.Count][]; + for (int i = 0; i < childNodes.Count; i++) + { + _subStreamsNumsPerChild[i] = childNodes[i].Substreams; + } + + _combinedSubStreams = ComputeCombined(_subStreamsNumsPerChild); + } + + _haveChildResults = false; + for (int i = 0; i < _rowsPerStream.Length; i++) + { + _rowsPerStream[i] = null; + } + } + + public override void Process(IList[] result, ICollection resultFinalRows, EventBean resultRootEvent) + { + // If no child has posted any rows, generate row and done + if ((!_haveChildResults) && (_allSubStreamsOptional)) + { + // post an empty row + var row = new EventBean[NumStreams]; + ParentNode.Result(row, StreamNum, null, null, resultFinalRows, resultRootEvent); + return; + } + + // Compute the cartesian product + PostCartesian(_rowsPerStream, resultFinalRows, resultRootEvent); + } + + public override void Result(EventBean[] row, int fromStreamNum, EventBean myEvent, Node myNode, ICollection resultFinalRows, EventBean resultRootEvent) + { + _haveChildResults = true; + + // fill event in + row[StreamNum] = myEvent; + int childStreamArrIndex = _childStreamIndex[fromStreamNum]; + + // keep a reference to the row to build a cartesian product on the call to process + IList rows = _rowsPerStream[childStreamArrIndex]; + if (rows == null) + { + rows = new List(); + _rowsPerStream[childStreamArrIndex] = rows; + } + rows.Add(row); + } + + public override void Print(IndentWriter indentWriter) + { + indentWriter.WriteLine("RootCartProdAssemblyNode StreamNum=" + StreamNum); + } + + private void PostCartesian(IList[] rowsPerStream, ICollection resultFinalRows, EventBean resultRootEvent) + { + IList result = new List(); + CartesianUtil.ComputeCartesian( + rowsPerStream[0], _subStreamsNumsPerChild[0], + rowsPerStream[1], _subStreamsNumsPerChild[1], + result); + + if (rowsPerStream.Length > 2) + { + for (int i = 0; i < _subStreamsNumsPerChild.Length - 2; i++) + { + var product = new List(); + CartesianUtil.ComputeCartesian( + result, _combinedSubStreams[i], + rowsPerStream[i + 2], _subStreamsNumsPerChild[i + 2], + product); + result = product; + } + } + + foreach (EventBean[] row in result) + { + ParentNode.Result(row, StreamNum, null, null, resultFinalRows, resultRootEvent); + } + } + + /// Compute an array of supersets of sub stream numbers per stream, for at least 3 or more streams. + /// is for each stream number a list of direct child sub streams + /// an array in with length (subStreamsPerChild.lenght - 2) in whicharray[0] contains the streams for subStreamsPerChild[0] and subStreamsPerChild[1] combined, and array[1] contains the streams for subStreamsPerChild[0], subStreamsPerChild[1] and subStreamsPerChild[2] combined + internal static int[][] ComputeCombined(int[][] subStreamsPerChild) + { + unchecked + { + if (subStreamsPerChild.Length < 3) + { + return null; + } + + // Add all substreams of (1 + 2) up into = Sum3 + // Then add all substreams of (Sum3 + 3) => Sum4 + // Results in an array of size (subStreamsPerChild.lenght - 2) containing Sum3, Sum4 etc + + var result = new int[subStreamsPerChild.Length - 2][]; + + result[0] = AddSubstreams(subStreamsPerChild[0], subStreamsPerChild[1]); + for (int i = 0; i < subStreamsPerChild.Length - 3; i++) + { + result[i + 1] = AddSubstreams(result[i], subStreamsPerChild[i + 2]); + } + + return result; + } + } + + private static int[] AddSubstreams(int[] arrayOne, int[] arrayTwo) + { + unchecked + { + var result = new int[arrayOne.Length + arrayTwo.Length]; + + int count = 0; + for (int i = 0; i < arrayOne.Length; i++) + { + result[count] = arrayOne[i]; + count++; + } + + for (int i = 0; i < arrayTwo.Length; i++) + { + result[count] = arrayTwo[i]; + count++; + } + return result; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/assemble/RootCartProdAssemblyNodeFactory.cs b/NEsper.Core/NEsper.Core/epl/join/assemble/RootCartProdAssemblyNodeFactory.cs new file mode 100755 index 000000000..c4a92062e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/assemble/RootCartProdAssemblyNodeFactory.cs @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.join.assemble +{ + /// + /// Assembly factory node for an event stream that is a root with a two or more child nodes below it. + /// + public class RootCartProdAssemblyNodeFactory : BaseAssemblyNodeFactory + { + private readonly int[] _childStreamIndex; // maintain mapping of stream number to index in array + private readonly bool _allSubStreamsOptional; + + /// + /// Ctor. + /// + /// is the stream number + /// is the number of streams + /// true if all substreams are optional and none are required + public RootCartProdAssemblyNodeFactory(int streamNum, int numStreams, bool allSubStreamsOptional) + : base(streamNum, numStreams) + { + _allSubStreamsOptional = allSubStreamsOptional; + _childStreamIndex = new int[numStreams]; + } + + public override void AddChild(BaseAssemblyNodeFactory childNode) + { + _childStreamIndex[childNode.StreamNum] = ChildNodes.Count; + base.AddChild(childNode); + } + + public override void Print(IndentWriter indentWriter) + { + indentWriter.WriteLine("RootCartProdAssemblyNode streamNum=" + StreamNum); + } + + public override BaseAssemblyNode MakeAssemblerUnassociated() + { + return new RootCartProdAssemblyNode(StreamNum, NumStreams, _allSubStreamsOptional, _childStreamIndex); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/join/assemble/RootOptionalAssemblyNode.cs b/NEsper.Core/NEsper.Core/epl/join/assemble/RootOptionalAssemblyNode.cs new file mode 100755 index 000000000..e40839dfe --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/assemble/RootOptionalAssemblyNode.cs @@ -0,0 +1,60 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.join.rep; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.join.assemble +{ + /// + /// Assembly node for an event stream that is a root with a one optional child node below it. + /// + public class RootOptionalAssemblyNode : BaseAssemblyNode + { + private bool _haveChildResults; + + /// Ctor. + /// is the stream number + /// is the number of streams + public RootOptionalAssemblyNode(int streamNum, int numStreams) + : base(streamNum, numStreams) + { + } + + public override void Init(IList[] result) + { + _haveChildResults = false; + } + + public override void Process(IList[] result, ICollection resultFinalRows, EventBean resultRootEvent) + { + // If we don't have child results, post an empty row + if (!_haveChildResults) + { + var row = new EventBean[NumStreams]; + ParentNode.Result(row, StreamNum, null, null, resultFinalRows, resultRootEvent); + } + } + + public override void Result(EventBean[] row, int fromStreamNum, EventBean myEvent, Node myNode, ICollection resultFinalRows, EventBean resultRootEvent) + { + ParentNode.Result(row, StreamNum, null, null, resultFinalRows, resultRootEvent); + + // record the fact that a row that was generated by a child + _haveChildResults = true; + } + + public override void Print(IndentWriter indentWriter) + { + indentWriter.WriteLine("RootOptionalAssemblyNode StreamNum=" + StreamNum); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/assemble/RootOptionalAssemblyNodeFactory.cs b/NEsper.Core/NEsper.Core/epl/join/assemble/RootOptionalAssemblyNodeFactory.cs new file mode 100755 index 000000000..97f8de3d7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/assemble/RootOptionalAssemblyNodeFactory.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.join.assemble +{ + /// + /// Assembly factory node for an event stream that is a root with a one optional child node below it. + /// + public class RootOptionalAssemblyNodeFactory : BaseAssemblyNodeFactory + { + public RootOptionalAssemblyNodeFactory(int streamNum, int numStreams) + : base(streamNum, numStreams) + { + } + + public override void Print(IndentWriter indentWriter) + { + indentWriter.WriteLine("RootOptionalAssemblyNode streamNum=" + StreamNum); + } + + public override BaseAssemblyNode MakeAssemblerUnassociated() + { + return new RootOptionalAssemblyNode(StreamNum, NumStreams); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/join/assemble/RootRequiredAssemblyNode.cs b/NEsper.Core/NEsper.Core/epl/join/assemble/RootRequiredAssemblyNode.cs new file mode 100755 index 000000000..daa5df38c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/assemble/RootRequiredAssemblyNode.cs @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.join.rep; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.join.assemble +{ + /// Assembly node for an event stream that is a root with a one required child node below it. + public class RootRequiredAssemblyNode : BaseAssemblyNode + { + /// Ctor. + /// is the stream number + /// is the number of streams + public RootRequiredAssemblyNode(int streamNum, int numStreams) + + : base(streamNum, numStreams) + { + } + + public override void Init(IList[] result) + { + // need not be concerned with results, all is passed from the child node + } + + public override void Process(IList[] result, ICollection resultFinalRows, EventBean resultRootEvent) + { + // no action here, since we have a required child row + // The single required child generates all events that may exist + } + + public override void Result(EventBean[] row, int fromStreamNum, EventBean myEvent, Node myNode, ICollection resultFinalRows, EventBean resultRootEvent) + { + ParentNode.Result(row, StreamNum, null, null, resultFinalRows, resultRootEvent); + } + + public override void Print(IndentWriter indentWriter) + { + indentWriter.WriteLine(string.Format("RootRequiredAssemblyNode StreamNum={0}", StreamNum)); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/assemble/RootRequiredAssemblyNodeFactory.cs b/NEsper.Core/NEsper.Core/epl/join/assemble/RootRequiredAssemblyNodeFactory.cs new file mode 100755 index 000000000..1172bf635 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/assemble/RootRequiredAssemblyNodeFactory.cs @@ -0,0 +1,42 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.join.assemble +{ + /// + /// Assembly node factory for an event stream that is a root with a one required child node below it. + /// + public class RootRequiredAssemblyNodeFactory : BaseAssemblyNodeFactory + { + /// + /// Ctor. + /// + /// is the stream number + /// is the number of streams + public RootRequiredAssemblyNodeFactory(int streamNum, int numStreams) + : base(streamNum, numStreams) + { + } + + public override void Print(IndentWriter indentWriter) + { + indentWriter.WriteLine("RootRequiredAssemblyNode streamNum=" + StreamNum); + } + + public override BaseAssemblyNode MakeAssemblerUnassociated() + { + return new RootRequiredAssemblyNode(StreamNum, NumStreams); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/join/base/ExecNodeQueryStrategy.cs b/NEsper.Core/NEsper.Core/epl/join/base/ExecNodeQueryStrategy.cs new file mode 100755 index 000000000..3db7d193a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/base/ExecNodeQueryStrategy.cs @@ -0,0 +1,77 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.attributes; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.join.exec.@base; + +namespace com.espertech.esper.epl.join.@base +{ + /// + /// Query strategy for building a join tuple set by using an execution node tree. + /// + [EsperVersion("6.0.1.*")] + public sealed class ExecNodeQueryStrategy : QueryStrategy + { + /// CTor. + /// stream the strategy is for + /// number of streams in total + /// execution node for building join tuple set + public ExecNodeQueryStrategy(int forStream, int numStreams, ExecNode execNode) + { + ForStream = forStream; + NumStreams = numStreams; + ExecNode = execNode; + } + + public void Lookup(EventBean[] lookupEvents, ICollection> joinSet, ExprEvaluatorContext exprEvaluatorContext) + { + if ((lookupEvents != null) && (lookupEvents.Length != 0)) + { + unchecked + { + var results = new List(); + int count = lookupEvents.Length; + for (int ii = 0; ii < count; ii++) + { + var theEvent = lookupEvents[ii]; + var prototype = new EventBean[NumStreams]; + + // Set up _prototype row + prototype[ForStream] = theEvent; + + // Perform execution + ExecNode.Process(theEvent, prototype, results, exprEvaluatorContext); + + // Convert results into unique set + results.ForEach(row => joinSet.Add(new MultiKey(row))); + results.Clear(); + } + } + } + } + + /// Return stream number this strategy is for. + /// stream num + internal int ForStream { get; private set; } + + /// Returns the total number of streams. + /// number of streams + internal int NumStreams { get; private set; } + + /// Returns execution node. + /// execution node + internal ExecNode ExecNode { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/base/HistoricalDataQueryStrategy.cs b/NEsper.Core/NEsper.Core/epl/join/base/HistoricalDataQueryStrategy.cs new file mode 100755 index 000000000..968532f04 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/base/HistoricalDataQueryStrategy.cs @@ -0,0 +1,157 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.join.pollindex; +using com.espertech.esper.epl.join.table; +using com.espertech.esper.view; + +namespace com.espertech.esper.epl.join.@base +{ + /// + /// Query strategy for use with to perform lookup for a given stream using the poll method on a viewable. + /// + public class HistoricalDataQueryStrategy : QueryStrategy + { + private readonly HistoricalEventViewable _historicalEventViewable; + private readonly int _historicalStreamNumber; + private readonly HistoricalIndexLookupStrategy _indexLookupStrategy; + private readonly bool _isOuterJoin; + private readonly EventBean[][] _lookupRows1Event; + private readonly int _myStreamNumber; + private readonly ExprEvaluator _outerJoinCompareNode; + private readonly PollResultIndexingStrategy _pollResultIndexingStrategy; + + /// Ctor. + /// is the strategy's stream number + /// is the stream number of the view to be polled + /// is the view to be polled from + /// is this is an outer join + /// is the node to perform the on-comparison for outer joins + /// the strategy to use for limiting the cache result setto only those rows that match filter criteria + /// the strategy for indexing poll-results such that astrategy can use the index instead of a full table scan to resolve rows + public HistoricalDataQueryStrategy(int myStreamNumber, + int historicalStreamNumber, + HistoricalEventViewable historicalEventViewable, + bool isOuterJoin, + ExprEvaluator outerJoinCompareNode, + HistoricalIndexLookupStrategy indexLookupStrategy, + PollResultIndexingStrategy pollResultIndexingStrategy) + { + _myStreamNumber = myStreamNumber; + _historicalStreamNumber = historicalStreamNumber; + _historicalEventViewable = historicalEventViewable; + _isOuterJoin = isOuterJoin; + _outerJoinCompareNode = outerJoinCompareNode; + + _lookupRows1Event = new EventBean[1][]; + _lookupRows1Event[0] = new EventBean[2]; + + _indexLookupStrategy = indexLookupStrategy; + _pollResultIndexingStrategy = pollResultIndexingStrategy; + } + + #region QueryStrategy Members + + public void Lookup(EventBean[] lookupEvents, + ICollection> joinSet, + ExprEvaluatorContext exprEvaluatorContext) + { + EventBean[][] lookupRows; + + // If looking up a single event, reuse the buffered array + if (lookupEvents.Length == 1) + { + lookupRows = _lookupRows1Event; + lookupRows[0][_myStreamNumber] = lookupEvents[0]; + } + else + { + // Prepare rows with each row Count events where Count is the number of streams + lookupRows = new EventBean[lookupEvents.Length][]; + for (int i = 0; i < lookupEvents.Length; i++) + { + lookupRows[i] = new EventBean[2]; + lookupRows[i][_myStreamNumber] = lookupEvents[i]; + } + } + + EventTable[][] indexPerLookupRow = _historicalEventViewable.Poll( + lookupRows, _pollResultIndexingStrategy, exprEvaluatorContext); + + int count = 0; + foreach (EventTable[] index in indexPerLookupRow) + { + // Using the index, determine a subset of the whole indexed table to process, unless + // the strategy is a full table scan + IEnumerator subsetIter = + _indexLookupStrategy.Lookup(lookupEvents[count], index, exprEvaluatorContext); + + // Ensure that the subset enumerator is advanced; assuming that there + // was an iterator at all. + bool subsetIterAdvanced = + (subsetIter != null) && + (subsetIter.MoveNext()); + + // In an outer join + if (_isOuterJoin && !subsetIterAdvanced) + { + var resultRow = new EventBean[2]; + resultRow[_myStreamNumber] = lookupEvents[count]; + joinSet.Add(new MultiKey(resultRow)); + } + else + { + bool foundMatch = false; + if (subsetIterAdvanced) + { + // Add each row to the join result or, for outer joins, run through the outer join filter + + do + { + var resultRow = new EventBean[2]; + resultRow[_myStreamNumber] = lookupEvents[count]; + resultRow[_historicalStreamNumber] = subsetIter.Current; + + // In an outer join compare the on-fields + if (_outerJoinCompareNode != null) + { + var compareResult = + (bool?)_outerJoinCompareNode.Evaluate(new EvaluateParams(resultRow, true, exprEvaluatorContext)); + if ((compareResult != null) && (compareResult.Value)) + { + joinSet.Add(new MultiKey(resultRow)); + foundMatch = true; + } + } + else + { + joinSet.Add(new MultiKey(resultRow)); + } + } while (subsetIter.MoveNext()); + } + + if ((_isOuterJoin) && (!foundMatch)) + { + var resultRow = new EventBean[2]; + resultRow[_myStreamNumber] = lookupEvents[count]; + joinSet.Add(new MultiKey(resultRow)); + } + } + count++; + } + } + + #endregion + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/join/base/HistoricalIndexLookupStrategy.cs b/NEsper.Core/NEsper.Core/epl/join/base/HistoricalIndexLookupStrategy.cs new file mode 100755 index 000000000..c80166537 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/base/HistoricalIndexLookupStrategy.cs @@ -0,0 +1,76 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.join.table; + +namespace com.espertech.esper.epl.join.@base +{ + /// + /// Strategy for use in poll-based joins to reduce a cached result set (represented by ), + /// in which the cache result set may have been indexed, to fewer rows following the join-criteria in a where clause. + /// + public interface HistoricalIndexLookupStrategy + { + /// + /// Look up into the index, potentially using some of the properties in the procLookup event, returning a partial or full result in respect to the index. + /// + /// provides properties to use as key values for indexes + /// is the table providing the cache result set, potentially indexed by index fields + /// The context. + /// full set or partial index iterator + IEnumerator Lookup(EventBean lookupEvent, EventTable[] index, ExprEvaluatorContext context); + + String ToQueryPlan(); + } + + public class ProxyHistoricalIndexLookupStrategy : HistoricalIndexLookupStrategy + { + public Func> ProcLookup { get; set; } + public Func ProcToQueryPlan { get; set; } + + public ProxyHistoricalIndexLookupStrategy() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The procLookup. + /// To plan. + public ProxyHistoricalIndexLookupStrategy(Func> procLookup, + Func procToQueryPlan) + { + ProcLookup = procLookup; + ProcToQueryPlan = procToQueryPlan; + } + + /// + /// Look up into the index, potentially using some of the properties in the procLookup event, returning a partial or full result in respect to the index. + /// + /// provides properties to use as key values for indexes + /// is the table providing the cache result set, potentially indexed by index fields + /// The context. + /// full set or partial index iterator + public IEnumerator Lookup(EventBean lookupEvent, EventTable[] index, ExprEvaluatorContext context) + { + return ProcLookup.Invoke(lookupEvent, index, context); + } + + public string ToQueryPlan() + { + return ProcToQueryPlan.Invoke(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/base/HistoricalIndexLookupStrategyComposite.cs b/NEsper.Core/NEsper.Core/epl/join/base/HistoricalIndexLookupStrategyComposite.cs new file mode 100755 index 000000000..dbbbaa8f7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/base/HistoricalIndexLookupStrategyComposite.cs @@ -0,0 +1,58 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.join.exec.composite; +using com.espertech.esper.epl.join.plan; +using com.espertech.esper.epl.join.table; + +namespace com.espertech.esper.epl.join.@base +{ + /// + /// MapIndex lookup strategy into a poll-based cache result. + /// + public class HistoricalIndexLookupStrategyComposite : HistoricalIndexLookupStrategy + { + private readonly CompositeIndexQuery _chain; + + public HistoricalIndexLookupStrategyComposite(int lookupStream, IList hashKeys, Type[] keyCoercionTypes, IList rangeKeyPairs, Type[] rangeCoercionTypes) + { + _chain = CompositeIndexQueryFactory.MakeJoinSingleLookupStream(false, lookupStream, hashKeys, keyCoercionTypes, rangeKeyPairs, rangeCoercionTypes); + } + + public IEnumerator Lookup(EventBean lookupEvent, EventTable[] indexTable, ExprEvaluatorContext context) + { + // The table may not be indexed as the cache may not actively cache, in which case indexing doesn't makes sense + if (indexTable[0] is PropertyCompositeEventTable) + { + var table = (PropertyCompositeEventTable) indexTable[0]; + var index = table.IndexTable; + + var events = _chain.Get(lookupEvent, index, context, table.PostProcessor); + if (events != null) + { + return events.GetEnumerator(); + } + return null; + } + + return indexTable[0].GetEnumerator(); + } + + public String ToQueryPlan() + { + return string.Format("{0} chain {1}", GetType().Name, _chain.GetType().Name); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/base/HistoricalIndexLookupStrategyInKeywordMulti.cs b/NEsper.Core/NEsper.Core/epl/join/base/HistoricalIndexLookupStrategyInKeywordMulti.cs new file mode 100755 index 000000000..74cab59e7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/base/HistoricalIndexLookupStrategyInKeywordMulti.cs @@ -0,0 +1,52 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.@join.plan; +using com.espertech.esper.epl.@join.table; + +namespace com.espertech.esper.epl.join.@base +{ + /// Index lookup strategy into a poll-based cache result. + public class HistoricalIndexLookupStrategyInKeywordMulti : HistoricalIndexLookupStrategy + { + private readonly ExprEvaluator _evaluator; + private readonly EventBean[] _eventsPerStream; + private readonly int _lookupStream; + + public HistoricalIndexLookupStrategyInKeywordMulti(int lookupStream, ExprNode expression) + { + _eventsPerStream = new EventBean[lookupStream + 1]; + _evaluator = expression.ExprEvaluator; + _lookupStream = lookupStream; + } + + public IEnumerator Lookup( + EventBean lookupEvent, + EventTable[] indexTable, + ExprEvaluatorContext exprEvaluatorContext) + { + _eventsPerStream[_lookupStream] = lookupEvent; + var result = InKeywordTableLookupUtil.MultiIndexLookup( + _evaluator, _eventsPerStream, exprEvaluatorContext, indexTable); + if (result == null) + { + return null; + } + return result.GetEnumerator(); + } + + public string ToQueryPlan() + { + return this.GetType().Name; + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/join/base/HistoricalIndexLookupStrategyInKeywordSingle.cs b/NEsper.Core/NEsper.Core/epl/join/base/HistoricalIndexLookupStrategyInKeywordSingle.cs new file mode 100755 index 000000000..8c44697cf --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/base/HistoricalIndexLookupStrategyInKeywordSingle.cs @@ -0,0 +1,54 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.@join.plan; +using com.espertech.esper.epl.@join.table; + +namespace com.espertech.esper.epl.join.@base +{ + /// Index lookup strategy into a poll-based cache result. + public class HistoricalIndexLookupStrategyInKeywordSingle : HistoricalIndexLookupStrategy + { + private readonly ExprEvaluator[] _evaluators; + private readonly EventBean[] _eventsPerStream; + private readonly int _lookupStream; + + public HistoricalIndexLookupStrategyInKeywordSingle(int lookupStream, ExprNode[] expressions) + { + _eventsPerStream = new EventBean[lookupStream + 1]; + _evaluators = ExprNodeUtility.GetEvaluators(expressions); + _lookupStream = lookupStream; + } + + public IEnumerator Lookup( + EventBean lookupEvent, + EventTable[] indexTable, + ExprEvaluatorContext exprEvaluatorContext) + { + var table = (PropertyIndexedEventTableSingle) indexTable[0]; + _eventsPerStream[_lookupStream] = lookupEvent; + + var result = InKeywordTableLookupUtil.SingleIndexLookup( + _evaluators, _eventsPerStream, exprEvaluatorContext, table); + if (result == null) + { + return null; + } + return result.GetEnumerator(); + } + + public string ToQueryPlan() + { + return this.GetType().Name; + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/join/base/HistoricalIndexLookupStrategyIndex.cs b/NEsper.Core/NEsper.Core/epl/join/base/HistoricalIndexLookupStrategyIndex.cs new file mode 100755 index 000000000..4d91cac81 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/base/HistoricalIndexLookupStrategyIndex.cs @@ -0,0 +1,79 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.@join.plan; +using com.espertech.esper.epl.@join.table; + +namespace com.espertech.esper.epl.join.@base +{ + /// Index lookup strategy into a poll-based cache result. + public class HistoricalIndexLookupStrategyIndex : HistoricalIndexLookupStrategy + { + private readonly ExprEvaluator[] _evaluators; + private readonly EventBean[] _eventsPerStream; + private readonly int _lookupStream; + + public HistoricalIndexLookupStrategyIndex( + EventType eventType, + int lookupStream, + IList hashKeys) + { + _evaluators = new ExprEvaluator[hashKeys.Count]; + for (int i = 0; i < hashKeys.Count; i++) + { + _evaluators[i] = hashKeys[i].KeyExpr.ExprEvaluator; + } + _eventsPerStream = new EventBean[lookupStream + 1]; + _lookupStream = lookupStream; + } + + public IEnumerator Lookup( + EventBean lookupEvent, + EventTable[] indexTable, + ExprEvaluatorContext exprEvaluatorContext) + { + // The table may not be indexed as the cache may not actively cache, in which case indexing doesn't makes sense + if (indexTable[0] is PropertyIndexedEventTable) + { + var index = (PropertyIndexedEventTable) indexTable[0]; + Object[] keys = GetKeys(lookupEvent, exprEvaluatorContext); + + ISet events = index.Lookup(keys); + if (events != null) + { + return events.GetEnumerator(); + } + return null; + } + + return indexTable[0].GetEnumerator(); + } + + public string ToQueryPlan() + { + return GetType().Name + " evaluators " + ExprNodeUtility.PrintEvaluators(_evaluators); + } + + private Object[] GetKeys(EventBean theEvent, ExprEvaluatorContext exprEvaluatorContext) + { + _eventsPerStream[_lookupStream] = theEvent; + var evaluateParams = new EvaluateParams(_eventsPerStream, true, exprEvaluatorContext); + var keys = new Object[_evaluators.Length]; + for (int i = 0; i < _evaluators.Length; i++) + { + keys[i] = _evaluators[i].Evaluate(evaluateParams); + } + return keys; + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/join/base/HistoricalIndexLookupStrategyIndexSingle.cs b/NEsper.Core/NEsper.Core/epl/join/base/HistoricalIndexLookupStrategyIndexSingle.cs new file mode 100755 index 000000000..1b0bd43a3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/base/HistoricalIndexLookupStrategyIndexSingle.cs @@ -0,0 +1,55 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.@join.plan; +using com.espertech.esper.epl.@join.table; + +namespace com.espertech.esper.epl.join.@base +{ + /// Index lookup strategy into a poll-based cache result. + public class HistoricalIndexLookupStrategyIndexSingle : HistoricalIndexLookupStrategy + { + private readonly EventBean[] _eventsPerStream; + private readonly ExprEvaluator _evaluator; + private readonly int _lookupStream; + + public HistoricalIndexLookupStrategyIndexSingle(int lookupStream, QueryGraphValueEntryHashKeyed hashKey) + { + _eventsPerStream = new EventBean[lookupStream + 1]; + _evaluator = hashKey.KeyExpr.ExprEvaluator; + _lookupStream = lookupStream; + } + + public IEnumerator Lookup(EventBean lookupEvent, EventTable[] indexTable, ExprEvaluatorContext exprEvaluatorContext) + { + // The table may not be indexed as the cache may not actively cache, in which case indexing doesn't makes sense + if (indexTable[0] is PropertyIndexedEventTableSingle) + { + var index = (PropertyIndexedEventTableSingle) indexTable[0]; + _eventsPerStream[_lookupStream] = lookupEvent; + var key = _evaluator.Evaluate(new EvaluateParams(_eventsPerStream, true, exprEvaluatorContext)); + + var events = index.Lookup(key); + if (events != null) { + return events.GetEnumerator(); + } + return null; + } + + return indexTable[0].GetEnumerator(); + } + + public string ToQueryPlan() { + return GetType().Name + " evaluator " + _evaluator.GetType().Name; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/join/base/HistoricalIndexLookupStrategyNoIndex.cs b/NEsper.Core/NEsper.Core/epl/join/base/HistoricalIndexLookupStrategyNoIndex.cs new file mode 100755 index 000000000..a5c34bc78 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/base/HistoricalIndexLookupStrategyNoIndex.cs @@ -0,0 +1,39 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.join.table; + +namespace com.espertech.esper.epl.join.@base +{ + /// + /// Full table scan strategy for a poll-based cache result. + /// + public class HistoricalIndexLookupStrategyNoIndex : HistoricalIndexLookupStrategy + { + #region HistoricalIndexLookupStrategy Members + + public IEnumerator Lookup(EventBean lookupEvent, + EventTable[] index, + ExprEvaluatorContext context) + { + return index[0].GetEnumerator(); + } + + public string ToQueryPlan() + { + return GetType().FullName; + } + + #endregion + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/join/base/HistoricalIndexLookupStrategySorted.cs b/NEsper.Core/NEsper.Core/epl/join/base/HistoricalIndexLookupStrategySorted.cs new file mode 100755 index 000000000..5c173c054 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/base/HistoricalIndexLookupStrategySorted.cs @@ -0,0 +1,54 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.@join.exec.sorted; +using com.espertech.esper.epl.@join.plan; +using com.espertech.esper.epl.@join.table; + +namespace com.espertech.esper.epl.join.@base +{ + /// Index lookup strategy into a poll-based cache result. + public class HistoricalIndexLookupStrategySorted : HistoricalIndexLookupStrategy + { + private readonly SortedAccessStrategy _strategy; + + public HistoricalIndexLookupStrategySorted(int lookupStream, QueryGraphValueEntryRange property) + { + _strategy = SortedAccessStrategyFactory.Make(false, lookupStream, -1, property, null); + } + + public IEnumerator Lookup( + EventBean lookupEvent, + EventTable[] indexTable, + ExprEvaluatorContext context) + { + // The table may not be indexed as the cache may not actively cache, in which case indexing doesn't makes sense + if (indexTable[0] is PropertySortedEventTable) + { + var index = (PropertySortedEventTable) indexTable[0]; + ICollection events = _strategy.Lookup(lookupEvent, index, context); + if (events != null) + { + return events.GetEnumerator(); + } + return null; + } + + return indexTable[0].GetEnumerator(); + } + + public string ToQueryPlan() + { + return this.GetType().Name + " strategy: " + _strategy.ToQueryPlan(); + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/join/base/HistoricalViewableDesc.cs b/NEsper.Core/NEsper.Core/epl/join/base/HistoricalViewableDesc.cs new file mode 100755 index 000000000..b5f2729d3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/base/HistoricalViewableDesc.cs @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.epl.join.@base +{ + public class HistoricalViewableDesc + { + public HistoricalViewableDesc(int numStreams) + { + DependenciesPerHistorical = new SortedSet[numStreams]; + Historical = new bool[numStreams]; + } + + public bool HasHistorical { get; private set; } + + public ICollection[] DependenciesPerHistorical { get; private set; } + + public bool[] Historical { get; private set; } + + public void SetHistorical(int streamNum, ICollection dependencies) + { + HasHistorical = true; + Historical[streamNum] = true; + if (DependenciesPerHistorical[streamNum] != null) + { + throw new EPException("Dependencies for stream " + streamNum + "already initialized"); + } + DependenciesPerHistorical[streamNum] = new SortedSet(); + if (dependencies != null) + { + DependenciesPerHistorical[streamNum].AddAll(dependencies); + } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/join/base/JoinExecStrategyDispatchable.cs b/NEsper.Core/NEsper.Core/epl/join/base/JoinExecStrategyDispatchable.cs new file mode 100755 index 000000000..aed9119d3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/base/JoinExecStrategyDispatchable.cs @@ -0,0 +1,84 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.service; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.view.internals; + +namespace com.espertech.esper.epl.join.@base +{ + /// + /// This class reacts to any new data buffered by registring with the dispatch service. + /// When dispatched via execute, it takes the buffered events and hands these to the join + /// execution strategy. + /// + public class JoinExecStrategyDispatchable : EPStatementDispatch, BufferObserver + { + private readonly JoinExecutionStrategy _joinExecutionStrategy; + private readonly IDictionary _oldStreamBuffer; + private readonly IDictionary _newStreamBuffer; + private readonly int _numStreams; + + private bool _hasNewData; + + /// CTor. + /// strategy for executing the join + /// number of stream + public JoinExecStrategyDispatchable(JoinExecutionStrategy joinExecutionStrategy, int numStreams) + { + _joinExecutionStrategy = joinExecutionStrategy; + _numStreams = numStreams; + + _oldStreamBuffer = new Dictionary(); + _newStreamBuffer = new Dictionary(); + } + + public void Execute() + { + if (!_hasNewData) + { + return; + } + _hasNewData = false; + + var oldDataPerStream = new EventBean[_numStreams][]; + var newDataPerStream = new EventBean[_numStreams][]; + + for (var i = 0; i < _numStreams; i++) + { + oldDataPerStream[i] = GetBufferData(_oldStreamBuffer.Get(i)); + newDataPerStream[i] = GetBufferData(_newStreamBuffer.Get(i)); + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QJoinDispatch(newDataPerStream, oldDataPerStream);} + _joinExecutionStrategy.Join(newDataPerStream, oldDataPerStream); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AJoinDispatch();} + } + + private static EventBean[] GetBufferData(FlushedEventBuffer buffer) + { + if (buffer == null) + { + return null; + } + return buffer.GetAndFlush(); + } + + public void NewData(int streamId, FlushedEventBuffer newEventBuffer, FlushedEventBuffer oldEventBuffer) + { + _hasNewData = true; + _newStreamBuffer.Put(streamId, newEventBuffer); + _oldStreamBuffer.Put(streamId, oldEventBuffer); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/base/JoinExecutionStrategy.cs b/NEsper.Core/NEsper.Core/epl/join/base/JoinExecutionStrategy.cs new file mode 100755 index 000000000..087957275 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/base/JoinExecutionStrategy.cs @@ -0,0 +1,36 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; + +namespace com.espertech.esper.epl.join.@base +{ + /// + /// Strategy for executing a join. + /// + public interface JoinExecutionStrategy + { + /// + /// Execute join. The first dimension in the 2-dim arrays is the stream that generated the events, and the second dimension is the actual events generated. + /// + /// new events for each stream + /// old events for each stream + void Join(EventBean[][] newDataPerStream, EventBean[][] oldDataPerStream); + + /// + /// A static join is for use with iterating over join statements. + /// + /// + /// set of rows, each row with two or more events, one for each stream + /// + ISet> StaticJoin(); + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/join/base/JoinExecutionStrategyImpl.cs b/NEsper.Core/NEsper.Core/epl/join/base/JoinExecutionStrategyImpl.cs new file mode 100755 index 000000000..9b133db51 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/base/JoinExecutionStrategyImpl.cs @@ -0,0 +1,68 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.join.@base +{ + /// + /// Join execution strategy based on a 3-step getSelectListEvents of composing a join + /// set, filtering the join set and indicating. + /// + public class JoinExecutionStrategyImpl : JoinExecutionStrategy + { + private readonly JoinSetComposer _composer; + private readonly JoinSetProcessor _filter; + private readonly JoinSetProcessor _indicator; + private readonly ExprEvaluatorContext _staticExprEvaluatorContext; + + /// + /// Ctor. + /// + /// determines join tuple set + /// for filtering among tuples + /// for presenting the INFO to a view + /// expression evaluation context for static evaluation (not for runtime eval) + public JoinExecutionStrategyImpl(JoinSetComposer composer, JoinSetProcessor filter, JoinSetProcessor indicator, ExprEvaluatorContext staticExprEvaluatorContext) + { + _composer = composer; + _filter = filter; + _indicator = indicator; + _staticExprEvaluatorContext = staticExprEvaluatorContext; + } + + public void Join(EventBean[][] newDataPerStream, EventBean[][] oldDataPerStream) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QJoinExexStrategy();} + var joinSet = _composer.Join(newDataPerStream, oldDataPerStream, _staticExprEvaluatorContext); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AJoinExecStrategy(joinSet);} + + _filter.Process(joinSet.First, joinSet.Second, _staticExprEvaluatorContext); + + if ( (!joinSet.First.IsEmpty()) || (!joinSet.Second.IsEmpty()) ) { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QJoinExecProcess(joinSet);} + _indicator.Process(joinSet.First, joinSet.Second, _staticExprEvaluatorContext); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AJoinExecProcess();} + } + } + + public ISet> StaticJoin() + { + var joinSet = _composer.StaticJoin(); + _filter.Process(joinSet, null, _staticExprEvaluatorContext); + return joinSet; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/base/JoinPreloadMethod.cs b/NEsper.Core/NEsper.Core/epl/join/base/JoinPreloadMethod.cs new file mode 100755 index 000000000..84f784588 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/base/JoinPreloadMethod.cs @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using com.espertech.esper.epl.core; +using com.espertech.esper.view.internals; + +namespace com.espertech.esper.epl.join.@base +{ + /// + /// Method for preloading events for a given stream onto the stream's indexes, from a + /// buffer already associated with a stream. + /// + public interface JoinPreloadMethod + { + /// + /// Initialize a stream from the stream buffers data. + /// + /// to initialize and load indexes + void PreloadFromBuffer(int stream); + + /// + /// Initialize the result set process for the purpose of grouping and aggregation from the join result set. + /// + /// is the grouping and aggregation result processing + void PreloadAggregation(ResultSetProcessor resultSetProcessor); + + /// Sets the buffee to use. + /// buffer to use + /// stream + void SetBuffer(BufferView buffer, int i); + + bool IsPreloading { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/base/JoinPreloadMethodImpl.cs b/NEsper.Core/NEsper.Core/epl/join/base/JoinPreloadMethodImpl.cs new file mode 100755 index 000000000..a274d2ad8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/base/JoinPreloadMethodImpl.cs @@ -0,0 +1,65 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.epl.core; +using com.espertech.esper.view.internals; + +namespace com.espertech.esper.epl.join.@base +{ + /// + /// : a method for pre-loading (initializing) join indexes from a filled buffer. + /// + public class JoinPreloadMethodImpl : JoinPreloadMethod + { + private readonly int _numStreams; + private readonly BufferView[] _bufferViews; + private readonly JoinSetComposer _joinSetComposer; + + /// Ctor. + /// number of streams + /// the composer holding stream indexes + public JoinPreloadMethodImpl(int numStreams, JoinSetComposer joinSetComposer) + { + _numStreams = numStreams; + _bufferViews = new BufferView[numStreams]; + _joinSetComposer = joinSetComposer; + } + + /// Sets the buffer for a stream to preload events from. + /// buffer + /// the stream number for the buffer + public void SetBuffer(BufferView view, int stream) + { + _bufferViews[stream] = view; + } + + public void PreloadFromBuffer(int stream) + { + var preloadEvents = _bufferViews[stream].NewDataBuffer.GetAndFlush(); + var eventsPerStream = new EventBean[_numStreams][]; + eventsPerStream[stream] = preloadEvents; + _joinSetComposer.Init(eventsPerStream); + } + + public void PreloadAggregation(ResultSetProcessor resultSetProcessor) + { + var newEvents = _joinSetComposer.StaticJoin(); + var oldEvents = new HashSet>(); + resultSetProcessor.ProcessJoinResult(newEvents, oldEvents, false); + } + + public bool IsPreloading + { + get { return true; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/base/JoinPreloadMethodNull.cs b/NEsper.Core/NEsper.Core/epl/join/base/JoinPreloadMethodNull.cs new file mode 100755 index 000000000..d1990c33a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/base/JoinPreloadMethodNull.cs @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.core; +using com.espertech.esper.view.internals; + +namespace com.espertech.esper.epl.join.@base +{ + /// + /// : a method for pre-loading (initializing) join that does not return any events. + /// + public class JoinPreloadMethodNull : JoinPreloadMethod + { + /// Ctor. + public JoinPreloadMethodNull() + { + } + + public void PreloadFromBuffer(int stream) + { + } + + public void PreloadAggregation(ResultSetProcessor resultSetProcessor) + { + } + + public void SetBuffer(BufferView buffer, int i) + { + } + + public bool IsPreloading + { + get { return false; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/base/JoinSetComposer.cs b/NEsper.Core/NEsper.Core/epl/join/base/JoinSetComposer.cs new file mode 100755 index 000000000..4994f5436 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/base/JoinSetComposer.cs @@ -0,0 +1,49 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.core.context.factory; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.join.@base +{ + /// + /// Interface for populating a join tuple result set from new data and old data for each stream. + /// + public interface JoinSetComposer + { + /// + /// Returns true whether initialization events per stream to populate join indexes can be processed (init method). + /// + /// + bool AllowsInit { get; } + + /// Provides initialization events per stream to composer to populate join indexes, if required + /// is an array of events for each stream, with null elements to indicate no events for a stream + void Init(EventBean[][] eventsPerStream); + + /// Return join tuple result set from new data and old data for each stream. + /// for each stream the event array (can be null). + /// for each stream the event array (can be null). + /// expression evaluation context + /// join tuples + UniformPair>> Join(EventBean[][] newDataPerStream, EventBean[][] oldDataPerStream, ExprEvaluatorContext exprEvaluatorContext); + + /// For use in iteration over join statements, this must build a join tuple result set from all events in indexes, executing query strategies for each. + /// static join result + ISet> StaticJoin(); + + /// Dispose stateful index tables, if any. + void Destroy(); + + void VisitIndexes(StatementAgentInstancePostLoadIndexVisitor visitor); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/base/JoinSetComposerAllUnidirectionalOuter.cs b/NEsper.Core/NEsper.Core/epl/join/base/JoinSetComposerAllUnidirectionalOuter.cs new file mode 100755 index 000000000..1a066608a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/base/JoinSetComposerAllUnidirectionalOuter.cs @@ -0,0 +1,90 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.factory; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.join.@base +{ + /// + /// Implements the function to determine a join result for a all-unidirectional full-outer-join (all streams), + /// in which a single stream's events are ever only evaluated and repositories don't exist. + /// + public class JoinSetComposerAllUnidirectionalOuter : JoinSetComposer + { + private readonly QueryStrategy[] _queryStrategies; + + private readonly ISet> _emptyResults = new LinkedHashSet>(); + private readonly ISet> _newResults = new LinkedHashSet>(); + + public JoinSetComposerAllUnidirectionalOuter(QueryStrategy[] queryStrategies) + { + _queryStrategies = queryStrategies; + } + + public bool AllowsInit + { + get { return false; } + } + + public void Init(EventBean[][] eventsPerStream) + { + } + + public void Destroy() + { + } + + public UniformPair>> Join( + EventBean[][] newDataPerStream, + EventBean[][] oldDataPerStream, + ExprEvaluatorContext exprEvaluatorContext) + { + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().QJoinCompositionStreamToWin(); + } + _newResults.Clear(); + + for (int i = 0; i < _queryStrategies.Length; i++) + { + if (newDataPerStream[i] != null) + { + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().QJoinCompositionQueryStrategy(true, i, newDataPerStream[i]); + } + _queryStrategies[i].Lookup(newDataPerStream[i], _newResults, exprEvaluatorContext); + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().AJoinCompositionQueryStrategy(); + } + } + } + + return new UniformPair>>(_newResults, _emptyResults); + } + + public ISet> StaticJoin() + { + throw new UnsupportedOperationException("Iteration over a unidirectional join is not supported"); + } + + public void VisitIndexes(StatementAgentInstancePostLoadIndexVisitor visitor) + { + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/join/base/JoinSetComposerDesc.cs b/NEsper.Core/NEsper.Core/epl/join/base/JoinSetComposerDesc.cs new file mode 100755 index 000000000..69bc60687 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/base/JoinSetComposerDesc.cs @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.join.@base +{ + public class JoinSetComposerDesc + { + public JoinSetComposerDesc(JoinSetComposer joinSetComposer, ExprEvaluator postJoinFilterEvaluator) + { + JoinSetComposer = joinSetComposer; + PostJoinFilterEvaluator = postJoinFilterEvaluator; + } + + public JoinSetComposer JoinSetComposer { get; private set; } + + public ExprEvaluator PostJoinFilterEvaluator { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/base/JoinSetComposerFAFImpl.cs b/NEsper.Core/NEsper.Core/epl/join/base/JoinSetComposerFAFImpl.cs new file mode 100755 index 000000000..a6e773118 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/base/JoinSetComposerFAFImpl.cs @@ -0,0 +1,110 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.@join.plan; +using com.espertech.esper.epl.join.table; + +namespace com.espertech.esper.epl.join.@base +{ + /// + /// Implements the function to determine a join result set using tables/indexes and query strategy instances for each stream. + /// + public class JoinSetComposerFAFImpl : JoinSetComposerImpl + { + private readonly bool _isOuterJoins; + + public JoinSetComposerFAFImpl(IDictionary[] repositories, QueryStrategy[] queryStrategies, bool isPureSelfJoin, ExprEvaluatorContext exprEvaluatorContext, bool joinRemoveStream, bool outerJoins) + : base(false, repositories, queryStrategies, isPureSelfJoin, exprEvaluatorContext, joinRemoveStream) + { + _isOuterJoins = outerJoins; + } + + public override void Init(EventBean[][] eventsPerStream) + { + // no action + } + + public override void Destroy() + { + // no action + } + + public override UniformPair>> Join( + EventBean[][] newDataPerStream, + EventBean[][] oldDataPerStream, + ExprEvaluatorContext exprEvaluatorContext) + { + NewResults.Clear(); + + // We add and remove data in one call to each index. + // Most indexes will add first then remove as newdata and olddata may contain the same event. + // Unique indexes may remove then add. + for (int stream = 0; stream < newDataPerStream.Length; stream++) + { + var repositories = Repositories; + for (int j = 0; j < repositories[stream].Length; j++) + { + repositories[stream][j].AddRemove(newDataPerStream[stream], oldDataPerStream[stream]); + } + } + + // for outer joins, execute each query strategy + if (_isOuterJoins) + { + for (int i = 0; i < newDataPerStream.Length; i++) + { + if (newDataPerStream[i] != null) + { + QueryStrategies[i].Lookup(newDataPerStream[i], NewResults, exprEvaluatorContext); + } + } + } + // handle all-inner joins by executing the smallest number of event's query strategy + else + { + int minStream = -1; + int minStreamCount = -1; + for (int i = 0; i < newDataPerStream.Length; i++) + { + if (newDataPerStream[i] != null) + { + if (newDataPerStream[i].Length == 0) + { + minStream = -1; + break; + } + if (newDataPerStream[i].Length > minStreamCount) + { + minStream = i; + minStreamCount = newDataPerStream[i].Length; + } + } + } + if (minStream != -1) + { + QueryStrategies[minStream].Lookup(newDataPerStream[minStream], NewResults, exprEvaluatorContext); + } + } + + return new UniformPair>>(NewResults, OldResults); + } + + public override ISet> StaticJoin() + { + // no action + return null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/base/JoinSetComposerHistoricalImpl.cs b/NEsper.Core/NEsper.Core/epl/join/base/JoinSetComposerHistoricalImpl.cs new file mode 100755 index 000000000..b8ebd9317 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/base/JoinSetComposerHistoricalImpl.cs @@ -0,0 +1,263 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.factory; +using com.espertech.esper.epl.db; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.@join.plan; +using com.espertech.esper.epl.@join.table; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.view; + +namespace com.espertech.esper.epl.join.@base +{ + /// + /// Implements the function to determine a join result set using tables/indexes and + /// query strategy instances for each stream. + /// + public class JoinSetComposerHistoricalImpl : JoinSetComposer + { + private readonly bool _allowInitIndex; + private readonly EventTable[][] _repositories; + private readonly QueryStrategy[] _queryStrategies; + + // Set semantic eliminates duplicates in result set, use Linked set to preserve order + private readonly ISet> _oldResults = new LinkedHashSet>(); + private readonly ISet> _newResults = new LinkedHashSet>(); + private readonly EventTable[][] _tables = new EventTable[0][]; + private readonly Viewable[] _streamViews; + private readonly ExprEvaluatorContext _staticEvalExprEvaluatorContext; + + /// + /// Ctor. + /// + /// if set to true [allow initialize index]. + /// indexes for non-historical streams + /// for each stream a strategy to execute the join + /// the viewable representing each stream + /// expression evaluation context for static (not runtime) evaluation + public JoinSetComposerHistoricalImpl( + bool allowInitIndex, + IDictionary[] repositories, + QueryStrategy[] queryStrategies, + Viewable[] streamViews, + ExprEvaluatorContext staticEvalExprEvaluatorContext) + { + _allowInitIndex = allowInitIndex; + _repositories = JoinSetComposerUtil.ToArray(repositories, streamViews.Length); + _queryStrategies = queryStrategies; + _streamViews = streamViews; + _staticEvalExprEvaluatorContext = staticEvalExprEvaluatorContext; + } + + public bool AllowsInit + { + get { return _allowInitIndex; } + } + + public void Init(EventBean[][] eventsPerStream) + { + if (!_allowInitIndex) + { + throw new IllegalStateException("Initialization by events not supported"); + } + + if (_repositories == null) + { + return; + } + + for (var i = 0; i < eventsPerStream.Length; i++) + { + if ((eventsPerStream[i] != null) && (_repositories[i] != null)) + { + for (var j = 0; j < _repositories[i].Length; j++) + { + _repositories[i][j].Add((eventsPerStream[i])); + } + } + } + } + + public void Destroy() + { + if (_repositories == null) + { + return; + } + + for (var i = 0; i < _repositories.Length; i++) + { + if (_repositories[i] != null) + { + foreach (var table in _repositories[i]) + { + table.Destroy(); + } + } + } + } + + public UniformPair>> Join(EventBean[][] newDataPerStream, EventBean[][] oldDataPerStream, ExprEvaluatorContext exprEvaluatorContext) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QJoinCompositionHistorical(); } + + _oldResults.Clear(); + _newResults.Clear(); + + // join old data + for (var i = 0; i < oldDataPerStream.Length; i++) + { + if (oldDataPerStream[i] != null) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QJoinCompositionQueryStrategy(false, i, oldDataPerStream[i]); } + _queryStrategies[i].Lookup(oldDataPerStream[i], _oldResults, exprEvaluatorContext); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AJoinCompositionQueryStrategy(); } + } + } + + if (_repositories != null) + { + // We add and remove data in one call to each index. + // Most indexes will add first then remove as newdata and olddata may contain the same event. + // Unique indexes may remove then add. + for (var stream = 0; stream < newDataPerStream.Length; stream++) + { + for (var j = 0; j < _repositories[stream].Length; j++) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QJoinCompositionStepUpdIndex(stream, newDataPerStream[stream], oldDataPerStream[stream]); } + _repositories[stream][j].AddRemove(newDataPerStream[stream], oldDataPerStream[stream]); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AJoinCompositionStepUpdIndex(); } + } + } + } + + // join new data + for (var i = 0; i < newDataPerStream.Length; i++) + { + if (newDataPerStream[i] != null) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QJoinCompositionQueryStrategy(true, i, newDataPerStream[i]); } + _queryStrategies[i].Lookup(newDataPerStream[i], _newResults, exprEvaluatorContext); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AJoinCompositionQueryStrategy(); } + } + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AJoinCompositionHistorical(_newResults, _oldResults); } + return new UniformPair>>(_newResults, _oldResults); + } + + /// Returns tables. + /// tables for stream. + protected EventTable[][] Tables + { + get { return _tables; } + } + + /// Returns query strategies. + /// query strategies + protected QueryStrategy[] QueryStrategies + { + get { return _queryStrategies; } + } + + public ISet> StaticJoin() + { + var result = new LinkedHashSet>(); + var lookupEvents = new EventBean[1]; + + // Assign a local cache for the thread's evaluation of the join + // This ensures that if a SQL/method generates a row for a result set based on an input parameter, the event instance is the same + // in the join, and thus the same row does not appear twice. + var caches = new DataCacheClearableMap[_queryStrategies.Length]; + AssignThreadLocalCache(_streamViews, caches); + + // perform join + try + { + // for each stream, perform query strategy + for (var stream = 0; stream < _queryStrategies.Length; stream++) + { + if (_streamViews[stream] is HistoricalEventViewable) + { + var historicalViewable = (HistoricalEventViewable)_streamViews[stream]; + if (historicalViewable.HasRequiredStreams) + { + continue; + } + + // there may not be a query strategy since only a full outer join may need to consider all rows + if (_queryStrategies[stream] != null) + { + var streamEvents = historicalViewable.GetEnumerator(); + for (; streamEvents.MoveNext(); ) + { + lookupEvents[0] = streamEvents.Current; + _queryStrategies[stream].Lookup(lookupEvents, result, _staticEvalExprEvaluatorContext); + } + } + } + else + { + var streamEvents = _streamViews[stream].GetEnumerator(); + for (; streamEvents.MoveNext(); ) + { + lookupEvents[0] = streamEvents.Current; + _queryStrategies[stream].Lookup(lookupEvents, result, _staticEvalExprEvaluatorContext); + } + } + } + } + finally + { + DeassignThreadLocalCache(_streamViews, caches); + } + + return result; + } + + public void VisitIndexes(StatementAgentInstancePostLoadIndexVisitor visitor) + { + visitor.Visit(_repositories); + } + + private void AssignThreadLocalCache(Viewable[] streamViews, DataCacheClearableMap[] caches) + { + for (var stream = 0; stream < streamViews.Length; stream++) + { + if (streamViews[stream] is HistoricalEventViewable) + { + var historicalViewable = (HistoricalEventViewable)streamViews[stream]; + caches[stream] = new DataCacheClearableMap(); + historicalViewable.DataCacheThreadLocal.Value = caches[stream]; + } + } + } + + private void DeassignThreadLocalCache(Viewable[] streamViews, DataCacheClearableMap[] caches) + { + for (var stream = 0; stream < streamViews.Length; stream++) + { + if (streamViews[stream] is HistoricalEventViewable) + { + var historicalViewable = (HistoricalEventViewable)streamViews[stream]; + historicalViewable.DataCacheThreadLocal.Value = null; + caches[stream].Clear(); + } + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/base/JoinSetComposerImpl.cs b/NEsper.Core/NEsper.Core/epl/join/base/JoinSetComposerImpl.cs new file mode 100755 index 000000000..e7d6fdbf6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/base/JoinSetComposerImpl.cs @@ -0,0 +1,224 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.factory; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.@join.plan; +using com.espertech.esper.epl.@join.table; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.join.@base +{ + /// + /// Implements the function to determine a join result set using tables/indexes and + /// query strategy instances for each stream. + /// + public class JoinSetComposerImpl : JoinSetComposer + { + private readonly bool _allowInitIndex; + private readonly EventTable[][] _repositories; + private readonly QueryStrategy[] _queryStrategies; + private readonly bool _isPureSelfJoin; + private readonly ExprEvaluatorContext _exprEvaluatorContext; + private readonly bool _joinRemoveStream; + + // Set semantic eliminates duplicates in result set, use Linked set to preserve order + protected ISet> OldResults = new LinkedHashSet>(); + protected ISet> NewResults = new LinkedHashSet>(); + + /// + /// Ctor. + /// + /// if set to true [allow initialize index]. + /// for each stream an array of (indexed/unindexed) tables for lookup. + /// for each stream a strategy to execute the join + /// for self-join only + /// expression evaluation context + /// if set to true [join remove stream]. + public JoinSetComposerImpl( + bool allowInitIndex, + IDictionary[] repositories, + QueryStrategy[] queryStrategies, + bool isPureSelfJoin, + ExprEvaluatorContext exprEvaluatorContext, + bool joinRemoveStream) + { + _allowInitIndex = allowInitIndex; + _repositories = JoinSetComposerUtil.ToArray(repositories); + _queryStrategies = queryStrategies; + _isPureSelfJoin = isPureSelfJoin; + _exprEvaluatorContext = exprEvaluatorContext; + _joinRemoveStream = joinRemoveStream; + } + + public bool AllowsInit + { + get { return _allowInitIndex; } + } + + public virtual void Init(EventBean[][] eventsPerStream) + { + if (!_allowInitIndex) + { + throw new IllegalStateException("Initialization by events not supported"); + } + + for (var i = 0; i < eventsPerStream.Length; i++) + { + if (eventsPerStream[i] != null) + { + for (var j = 0; j < _repositories[i].Length; j++) + { + _repositories[i][j].Add((eventsPerStream[i])); + } + } + } + } + + public virtual void Destroy() + { + unchecked + { + for (var i = 0; i < _repositories.Length; i++) + { + if (_repositories[i] != null) + { + foreach (var table in _repositories[i]) + { + table.Destroy(); + } + } + } + } + } + + public virtual UniformPair>> Join( + EventBean[][] newDataPerStream, + EventBean[][] oldDataPerStream, + ExprEvaluatorContext exprEvaluatorContext) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QJoinCompositionWinToWin(); } + + OldResults.Clear(); + NewResults.Clear(); + + // join old data + if (_joinRemoveStream) + { + for (var i = 0; i < oldDataPerStream.Length; i++) + { + if (oldDataPerStream[i] != null) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QJoinCompositionQueryStrategy(false, i, oldDataPerStream[i]); } + _queryStrategies[i].Lookup(oldDataPerStream[i], OldResults, exprEvaluatorContext); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AJoinCompositionQueryStrategy(); } + } + } + } + + // We add and remove data in one call to each index. + // Most indexes will add first then remove as newdata and olddata may contain the same event. + // Unique indexes may remove then add. + for (var stream = 0; stream < newDataPerStream.Length; stream++) + { + for (var j = 0; j < _repositories[stream].Length; j++) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QJoinCompositionStepUpdIndex(stream, newDataPerStream[stream], oldDataPerStream[stream]); } + _repositories[stream][j].AddRemove(newDataPerStream[stream], oldDataPerStream[stream]); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AJoinCompositionStepUpdIndex(); } + } + } + + // join new data + for (var i = 0; i < newDataPerStream.Length; i++) + { + if (newDataPerStream[i] != null) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QJoinCompositionQueryStrategy(true, i, newDataPerStream[i]); } + _queryStrategies[i].Lookup(newDataPerStream[i], NewResults, exprEvaluatorContext); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AJoinCompositionQueryStrategy(); } + } + } + + // on self-joins there can be repositories which are temporary for join execution + if (_isPureSelfJoin) + { + foreach (var repository in _repositories) + { + foreach (var aRepository in repository) + { + aRepository.Clear(); + } + } + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AJoinCompositionWinToWin(NewResults, OldResults); } + return new UniformPair>>(NewResults, OldResults); + } + + /// + /// Gets the repositories. + /// + /// The repositories. + public EventTable[][] Repositories + { + get { return _repositories; } + } + + /// Returns tables. + /// tables for stream. + internal EventTable[][] Tables + { + get { return _repositories; } + } + + /// Returns query strategies. + /// query strategies + internal QueryStrategy[] QueryStrategies + { + get { return _queryStrategies; } + } + + public virtual ISet> StaticJoin() + { + var result = new LinkedHashSet>(); + var lookupEvents = new EventBean[1]; + + // for each stream, perform query strategy + for (var stream = 0; stream < _queryStrategies.Length; stream++) + { + if (_repositories[stream] == null) + { + continue; + } + + IEnumerator streamEvents = _repositories[stream][0].GetEnumerator(); + while (streamEvents.MoveNext()) + { + lookupEvents[0] = streamEvents.Current; + _queryStrategies[stream].Lookup(lookupEvents, result, _exprEvaluatorContext); + } + } + + return result; + } + + public void VisitIndexes(StatementAgentInstancePostLoadIndexVisitor visitor) + { + visitor.Visit(_repositories); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/join/base/JoinSetComposerPrototype.cs b/NEsper.Core/NEsper.Core/epl/join/base/JoinSetComposerPrototype.cs new file mode 100755 index 000000000..23a285cb4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/base/JoinSetComposerPrototype.cs @@ -0,0 +1,22 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.context.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.epl.join.@base +{ + /// + /// Interface for a _prototype populating a join tuple result set from new data and + /// old data for each stream. + /// + public interface JoinSetComposerPrototype + { + JoinSetComposerDesc Create(Viewable[] streamViews, bool isFireAndForget, AgentInstanceContext agentInstanceContext, bool isRecoveringResilient); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/base/JoinSetComposerPrototypeFactory.cs b/NEsper.Core/NEsper.Core/epl/join/base/JoinSetComposerPrototypeFactory.cs new file mode 100755 index 000000000..5998ded71 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/base/JoinSetComposerPrototypeFactory.cs @@ -0,0 +1,487 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.ops; +using com.espertech.esper.epl.join.hint; +using com.espertech.esper.epl.join.plan; +using com.espertech.esper.epl.join.pollindex; +using com.espertech.esper.epl.join.table; +using com.espertech.esper.epl.join.util; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.type; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.join.@base +{ + /// + /// Factory for building a from analyzing filter nodes, for + /// fast join tuple result set composition. + /// + public class JoinSetComposerPrototypeFactory + { + private static readonly ILog QUERY_PLAN_LOG = LogManager.GetLogger(AuditPath.QUERYPLAN_LOG); + + private static readonly ILog Log = + LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + public static JoinSetComposerPrototype MakeComposerPrototype( + string statementName, + int statementId, + OuterJoinDesc[] outerJoinDescList, + ExprNode optionalFilterNode, + EventType[] streamTypes, + string[] streamNames, + StreamJoinAnalysisResult streamJoinAnalysisResult, + bool queryPlanLogging, + StatementContext statementContext, + HistoricalViewableDesc historicalViewableDesc, + ExprEvaluatorContext exprEvaluatorContext, + bool selectsRemoveStream, + bool hasAggregations, + TableService tableService, + bool isOnDemandQuery, + bool allowIndexInit) + { + // Determine if there is a historical stream, and what dependencies exist + var historicalDependencyGraph = new DependencyGraph(streamTypes.Length, false); + for (var i = 0; i < streamTypes.Length; i++) + { + if (historicalViewableDesc.Historical[i]) + { + var streamsThisStreamDependsOn = historicalViewableDesc.DependenciesPerHistorical[i]; + historicalDependencyGraph.AddDependency(i, streamsThisStreamDependsOn); + } + } + + if (Log.IsDebugEnabled) + { + Log.Debug("Dependency graph: " + historicalDependencyGraph); + } + + // Handle a join with a database or other historical data source for 2 streams + if ((historicalViewableDesc.HasHistorical) && (streamTypes.Length == 2)) + { + return MakeComposerHistorical2Stream( + outerJoinDescList, optionalFilterNode, streamTypes, historicalViewableDesc, queryPlanLogging, + exprEvaluatorContext, statementContext, streamNames, allowIndexInit); + } + + var isOuterJoins = !OuterJoinDesc.ConsistsOfAllInnerJoins(outerJoinDescList); + + // Query graph for graph relationships between streams/historicals + // For outer joins the query graph will just contain outer join relationships + var hint = ExcludePlanHint.GetHint(streamNames, statementContext); + var queryGraph = new QueryGraph(streamTypes.Length, hint, false); + if (outerJoinDescList.Length > 0) + { + OuterJoinAnalyzer.Analyze(outerJoinDescList, queryGraph); + if (Log.IsDebugEnabled) + { + Log.Debug(".MakeComposer After outer join queryGraph=\n" + queryGraph); + } + } + + // Let the query graph reflect the where-clause + if (optionalFilterNode != null) + { + // Analyze relationships between streams using the optional filter expression. + // Relationships are properties in AND and EQUALS nodes of joins. + FilterExprAnalyzer.Analyze(optionalFilterNode, queryGraph, isOuterJoins); + if (Log.IsDebugEnabled) + { + Log.Debug(".MakeComposer After filter expression queryGraph=\n" + queryGraph); + } + + // Add navigation entries based on key and index property equivalency (a=b, b=c follows a=c) + QueryGraph.FillEquivalentNav(streamTypes, queryGraph); + if (Log.IsDebugEnabled) + { + Log.Debug(".MakeComposer After fill equiv. nav. queryGraph=\n" + queryGraph); + } + } + + // Historical index lists + var historicalStreamIndexLists = new HistoricalStreamIndexList[streamTypes.Length]; + + var queryPlan = QueryPlanBuilder.GetPlan( + streamTypes, outerJoinDescList, queryGraph, streamNames, + historicalViewableDesc, historicalDependencyGraph, historicalStreamIndexLists, + streamJoinAnalysisResult, queryPlanLogging, statementContext.Annotations, exprEvaluatorContext); + + // remove unused indexes - consider all streams or all unidirectional + var usedIndexes = new HashSet(); + var indexSpecs = queryPlan.IndexSpecs; + for (var streamNum = 0; streamNum < queryPlan.ExecNodeSpecs.Length; streamNum++) + { + var planNode = queryPlan.ExecNodeSpecs[streamNum]; + if (planNode != null) + { + planNode.AddIndexes(usedIndexes); + } + } + foreach (var indexSpec in indexSpecs) + { + if (indexSpec == null) + { + continue; + } + var items = indexSpec.Items; + var indexNames = items.Keys.ToArray(); + foreach (var indexName in indexNames) + { + if (!usedIndexes.Contains(indexName)) + { + items.Remove(indexName); + } + } + } + + var hook = QueryPlanIndexHookUtil.GetHook( + statementContext.Annotations, statementContext.EngineImportService); + if (queryPlanLogging && (QUERY_PLAN_LOG.IsInfoEnabled || hook != null)) + { + QUERY_PLAN_LOG.Info("Query plan: " + queryPlan.ToQueryPlan()); + if (hook != null) + { + hook.Join(queryPlan); + } + } + + // register index-use references for tables + if (!isOnDemandQuery) + { + foreach (var usedIndex in usedIndexes) + { + if (usedIndex.TableName != null) + { + tableService.GetTableMetadata(usedIndex.TableName) + .AddIndexReference(usedIndex.Name, statementName); + } + } + } + + var joinRemoveStream = selectsRemoveStream || hasAggregations; + return new JoinSetComposerPrototypeImpl( + statementName, + statementId, + outerJoinDescList, + optionalFilterNode, + streamTypes, + streamNames, + streamJoinAnalysisResult, + statementContext.Annotations, + historicalViewableDesc, + exprEvaluatorContext, + indexSpecs, + queryPlan, + historicalStreamIndexLists, + joinRemoveStream, + isOuterJoins, + tableService, statementContext.EventTableIndexService); + } + + private static JoinSetComposerPrototype MakeComposerHistorical2Stream( + OuterJoinDesc[] outerJoinDescList, + ExprNode optionalFilterNode, + EventType[] streamTypes, + HistoricalViewableDesc historicalViewableDesc, + bool queryPlanLogging, + ExprEvaluatorContext exprEvaluatorContext, + StatementContext statementContext, + string[] streamNames, + bool allowIndexInit) + { + var polledViewNum = 0; + var streamViewNum = 1; + if (historicalViewableDesc.Historical[1]) + { + streamViewNum = 0; + polledViewNum = 1; + } + + // if all-historical join, check dependency + var isAllHistoricalNoSubordinate = false; + if ((historicalViewableDesc.Historical[0]) && historicalViewableDesc.Historical[1]) + { + var graph = new DependencyGraph(2, false); + graph.AddDependency(0, historicalViewableDesc.DependenciesPerHistorical[0]); + graph.AddDependency(1, historicalViewableDesc.DependenciesPerHistorical[1]); + if (graph.FirstCircularDependency != null) + { + throw new ExprValidationException("Circular dependency detected between historical streams"); + } + + // if both streams are independent + if (graph.RootNodes.Count == 2) + { + isAllHistoricalNoSubordinate = true; // No parameters used by either historical + } + else + { + if (graph.GetDependenciesForStream(0).Count == 0) + { + streamViewNum = 0; + polledViewNum = 1; + } + else + { + streamViewNum = 1; + polledViewNum = 0; + } + } + } + + // Build an outer join expression node + var isOuterJoin = false; + ExprNode outerJoinEqualsNode = null; + var isInnerJoinOnly = false; + if (outerJoinDescList.Length > 0) + { + var outerJoinDesc = outerJoinDescList[0]; + isInnerJoinOnly = outerJoinDesc.OuterJoinType.Equals(OuterJoinType.INNER); + + if (outerJoinDesc.OuterJoinType.Equals(OuterJoinType.FULL)) + { + isOuterJoin = true; + } + else if ((outerJoinDesc.OuterJoinType.Equals(OuterJoinType.LEFT)) && + (streamViewNum == 0)) + { + isOuterJoin = true; + } + else if ((outerJoinDesc.OuterJoinType.Equals(OuterJoinType.RIGHT)) && + (streamViewNum == 1)) + { + isOuterJoin = true; + } + + outerJoinEqualsNode = outerJoinDesc.MakeExprNode(exprEvaluatorContext); + } + + // Determine filter for indexing purposes + ExprNode filterForIndexing = null; + if ((outerJoinEqualsNode != null) && (optionalFilterNode != null) && isInnerJoinOnly) + { + // both filter and outer join, add + filterForIndexing = new ExprAndNodeImpl(); + filterForIndexing.AddChildNode(optionalFilterNode); + filterForIndexing.AddChildNode(outerJoinEqualsNode); + } + else if ((outerJoinEqualsNode == null) && (optionalFilterNode != null)) + { + filterForIndexing = optionalFilterNode; + } + else if (outerJoinEqualsNode != null) + { + filterForIndexing = outerJoinEqualsNode; + } + + var indexStrategies = + DetermineIndexing( + filterForIndexing, streamTypes[polledViewNum], streamTypes[streamViewNum], polledViewNum, + streamViewNum, statementContext, streamNames); + + var hook = QueryPlanIndexHookUtil.GetHook( + statementContext.Annotations, statementContext.EngineImportService); + if (queryPlanLogging && (QUERY_PLAN_LOG.IsInfoEnabled || hook != null)) + { + QUERY_PLAN_LOG.Info("historical lookup strategy: " + indexStrategies.First.ToQueryPlan()); + QUERY_PLAN_LOG.Info("historical index strategy: " + indexStrategies.Second.ToQueryPlan()); + if (hook != null) + { + hook.Historical( + new QueryPlanIndexDescHistorical( + indexStrategies.First.GetType().Name, indexStrategies.Second.GetType().Name)); + } + } + + return new JoinSetComposerPrototypeHistorical2StreamImpl( + optionalFilterNode, + streamTypes, + exprEvaluatorContext, + polledViewNum, + streamViewNum, + isOuterJoin, + outerJoinEqualsNode, + indexStrategies, + isAllHistoricalNoSubordinate, + outerJoinDescList, allowIndexInit); + } + + private static Pair DetermineIndexing( + ExprNode filterForIndexing, + EventType polledViewType, + EventType streamViewType, + int polledViewStreamNum, + int streamViewStreamNum, + StatementContext statementContext, + string[] streamNames) + { + // No filter means unindexed event tables + if (filterForIndexing == null) + { + return new Pair( + new HistoricalIndexLookupStrategyNoIndex(), new PollResultIndexingStrategyNoIndex()); + } + + // analyze query graph; Whereas stream0=named window, stream1=delete-expr filter + var hint = ExcludePlanHint.GetHint(streamNames, statementContext); + var queryGraph = new QueryGraph(2, hint, false); + FilterExprAnalyzer.Analyze(filterForIndexing, queryGraph, false); + + return DetermineIndexing( + queryGraph, polledViewType, streamViewType, polledViewStreamNum, streamViewStreamNum); + } + + /// + /// Constructs indexing and lookup strategy for a given relationship that a historical stream may have with another + /// stream (historical or not) that looks up into results of a poll of a historical stream. + /// + /// The term "polled" refers to the assumed-historical stream. + /// + /// + /// relationship representation of where-clause filter and outer join on-expressions + /// the event type of the historical that is indexed + /// the event type of the stream looking up in indexes + /// the stream number of the historical that is indexed + /// the stream number of the historical that is looking up + /// indexing and lookup strategy pair + public static Pair DetermineIndexing( + QueryGraph queryGraph, + EventType polledViewType, + EventType streamViewType, + int polledViewStreamNum, + int streamViewStreamNum) + { + var queryGraphValue = queryGraph.GetGraphValue(streamViewStreamNum, polledViewStreamNum); + var hashKeysAndIndes = queryGraphValue.HashKeyProps; + var rangeKeysAndIndex = queryGraphValue.RangeProps; + + // index and key property names + var hashKeys = hashKeysAndIndes.Keys; + var hashIndexes = hashKeysAndIndes.Indexed; + var rangeKeys = rangeKeysAndIndex.Keys; + var rangeIndexes = rangeKeysAndIndex.Indexed; + + // If the analysis revealed no join columns, must use the brute-force full table scan + if (hashKeys.IsEmpty() && rangeKeys.IsEmpty()) + { + var inKeywordSingles = queryGraphValue.InKeywordSingles; + if (inKeywordSingles != null && inKeywordSingles.Indexed.Length != 0) + { + var indexed = inKeywordSingles.Indexed[0]; + var lookup = inKeywordSingles.Key[0]; + var strategy = new HistoricalIndexLookupStrategyInKeywordSingle( + streamViewStreamNum, lookup.KeyExprs); + var indexing = new PollResultIndexingStrategyIndexSingle( + polledViewStreamNum, polledViewType, indexed); + return new Pair(strategy, indexing); + } + + var multis = queryGraphValue.InKeywordMulti; + if (!multis.IsEmpty()) + { + var multi = multis[0]; + var strategy = new HistoricalIndexLookupStrategyInKeywordMulti( + streamViewStreamNum, multi.Key.KeyExpr); + var indexing = new PollResultIndexingStrategyIndexSingleArray( + polledViewStreamNum, polledViewType, + ExprNodeUtility.GetIdentResolvedPropertyNames(multi.Indexed)); + return new Pair(strategy, indexing); + } + + return new Pair( + new HistoricalIndexLookupStrategyNoIndex(), new PollResultIndexingStrategyNoIndex()); + } + + var keyCoercionTypes = CoercionUtil.GetCoercionTypesHash( + new EventType[] + { + streamViewType, + polledViewType + }, 0, 1, hashKeys, hashIndexes); + + if (rangeKeys.IsEmpty()) + { + // No coercion + if (!keyCoercionTypes.IsCoerce) + { + if (hashIndexes.Count == 1) + { + var indexing = new PollResultIndexingStrategyIndexSingle( + polledViewStreamNum, polledViewType, hashIndexes[0]); + var strategy = new HistoricalIndexLookupStrategyIndexSingle(streamViewStreamNum, hashKeys[0]); + return new Pair(strategy, indexing); + } + else + { + var indexing = new PollResultIndexingStrategyIndex( + polledViewStreamNum, polledViewType, hashIndexes); + var strategy = new HistoricalIndexLookupStrategyIndex( + streamViewType, streamViewStreamNum, hashKeys); + return new Pair(strategy, indexing); + } + } + + // With coercion, same lookup strategy as the index coerces + if (hashIndexes.Count == 1) + { + var indexing = new PollResultIndexingStrategyIndexCoerceSingle( + polledViewStreamNum, polledViewType, hashIndexes[0], keyCoercionTypes.CoercionTypes[0]); + var strategy = new HistoricalIndexLookupStrategyIndexSingle(streamViewStreamNum, hashKeys[0]); + return new Pair(strategy, indexing); + } + else + { + var indexing = new PollResultIndexingStrategyIndexCoerce( + polledViewStreamNum, polledViewType, hashIndexes, keyCoercionTypes.CoercionTypes); + var strategy = new HistoricalIndexLookupStrategyIndex(streamViewType, streamViewStreamNum, hashKeys); + return new Pair(strategy, indexing); + } + } + else + { + var rangeCoercionTypes = CoercionUtil.GetCoercionTypesRange( + new EventType[] + { + streamViewType, + polledViewType + }, 1, rangeIndexes, rangeKeys); + if (rangeKeys.Count == 1 && hashKeys.Count == 0) + { + var rangeCoercionType = rangeCoercionTypes.IsCoerce ? rangeCoercionTypes.CoercionTypes[0] : null; + var indexing = new PollResultIndexingStrategySorted( + polledViewStreamNum, polledViewType, rangeIndexes[0], rangeCoercionType); + var strategy = new HistoricalIndexLookupStrategySorted(streamViewStreamNum, rangeKeys[0]); + return new Pair(strategy, indexing); + } + else + { + var indexing = new PollResultIndexingStrategyComposite( + polledViewStreamNum, polledViewType, hashIndexes, keyCoercionTypes.CoercionTypes, rangeIndexes, + rangeCoercionTypes.CoercionTypes); + var strategy = new HistoricalIndexLookupStrategyComposite( + streamViewStreamNum, hashKeys, keyCoercionTypes.CoercionTypes, rangeKeys, + rangeCoercionTypes.CoercionTypes); + return new Pair(strategy, indexing); + } + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/join/base/JoinSetComposerPrototypeHistorical2StreamImpl.cs b/NEsper.Core/NEsper.Core/epl/join/base/JoinSetComposerPrototypeHistorical2StreamImpl.cs new file mode 100755 index 000000000..af3d9b591 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/base/JoinSetComposerPrototypeHistorical2StreamImpl.cs @@ -0,0 +1,97 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.@join.pollindex; +using com.espertech.esper.epl.spec; +using com.espertech.esper.type; +using com.espertech.esper.view; + +namespace com.espertech.esper.epl.join.@base +{ + public class JoinSetComposerPrototypeHistorical2StreamImpl : JoinSetComposerPrototype + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly ExprNode _optionalFilterNode; + private readonly EventType[] _streamTypes; + private readonly ExprEvaluatorContext _exprEvaluatorContext; + private readonly int _polledViewNum; + private readonly int _streamViewNum; + private readonly bool _isOuterJoin; + private readonly ExprNode _outerJoinEqualsNode; + private readonly Pair _indexStrategies; + private readonly bool _isAllHistoricalNoSubordinate; + private readonly OuterJoinDesc[] _outerJoinDescList; + private readonly bool _allowIndexInit; + + public JoinSetComposerPrototypeHistorical2StreamImpl( + ExprNode optionalFilterNode, + EventType[] streamTypes, + ExprEvaluatorContext exprEvaluatorContext, + int polledViewNum, + int streamViewNum, + bool outerJoin, + ExprNode outerJoinEqualsNode, + Pair indexStrategies, + bool allHistoricalNoSubordinate, + OuterJoinDesc[] outerJoinDescList, + bool allowIndexInit) + { + _optionalFilterNode = optionalFilterNode; + _streamTypes = streamTypes; + _exprEvaluatorContext = exprEvaluatorContext; + _polledViewNum = polledViewNum; + _streamViewNum = streamViewNum; + _isOuterJoin = outerJoin; + _outerJoinEqualsNode = outerJoinEqualsNode; + _indexStrategies = indexStrategies; + _isAllHistoricalNoSubordinate = allHistoricalNoSubordinate; + _outerJoinDescList = outerJoinDescList; + _allowIndexInit = allowIndexInit; + } + + public JoinSetComposerDesc Create(Viewable[] streamViews, bool isFireAndForget, AgentInstanceContext agentInstanceContext, bool isRecoveringResilient) { + var queryStrategies = new QueryStrategy[_streamTypes.Length]; + var viewable = (HistoricalEventViewable) streamViews[_polledViewNum]; + var outerJoinEqualsNodeEval = _outerJoinEqualsNode == null ? null : _outerJoinEqualsNode.ExprEvaluator; + queryStrategies[_streamViewNum] = new HistoricalDataQueryStrategy(_streamViewNum, _polledViewNum, viewable, _isOuterJoin, outerJoinEqualsNodeEval, + _indexStrategies.First, _indexStrategies.Second); + + // for strictly historical joins, create a query strategy for the non-subordinate historical view + if (_isAllHistoricalNoSubordinate) { + var isOuterJoinX = false; + if (_outerJoinDescList.Length > 0) { + var outerJoinDesc = _outerJoinDescList[0]; + if (outerJoinDesc.OuterJoinType.Equals(OuterJoinType.FULL)) { + isOuterJoinX = true; + } else if ((outerJoinDesc.OuterJoinType.Equals(OuterJoinType.LEFT)) && + (_polledViewNum == 0)) { + isOuterJoinX = true; + } else if ((outerJoinDesc.OuterJoinType.Equals(OuterJoinType.RIGHT)) && + (_polledViewNum == 1)) { + isOuterJoinX = true; + } + } + viewable = (HistoricalEventViewable) streamViews[_streamViewNum]; + queryStrategies[_polledViewNum] = new HistoricalDataQueryStrategy(_polledViewNum, _streamViewNum, viewable, isOuterJoinX, outerJoinEqualsNodeEval, + new HistoricalIndexLookupStrategyNoIndex(), new PollResultIndexingStrategyNoIndex()); + } + + var composer = new JoinSetComposerHistoricalImpl(_allowIndexInit, null, queryStrategies, streamViews, _exprEvaluatorContext); + var postJoinEval = _optionalFilterNode == null ? null : _optionalFilterNode.ExprEvaluator; + return new JoinSetComposerDesc(composer, postJoinEval); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/join/base/JoinSetComposerPrototypeImpl.cs b/NEsper.Core/NEsper.Core/epl/join/base/JoinSetComposerPrototypeImpl.cs new file mode 100755 index 000000000..ad933828c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/base/JoinSetComposerPrototypeImpl.cs @@ -0,0 +1,345 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.compat.threading; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.ops; +using com.espertech.esper.epl.@join.exec.@base; +using com.espertech.esper.epl.@join.plan; +using com.espertech.esper.epl.@join.table; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.epl.virtualdw; +using com.espertech.esper.view; + +namespace com.espertech.esper.epl.join.@base +{ + public class JoinSetComposerPrototypeImpl : JoinSetComposerPrototype + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly string _statementName; + private readonly int _statementId; + private readonly OuterJoinDesc[] _outerJoinDescList; + private readonly ExprNode _optionalFilterNode; + private readonly EventType[] _streamTypes; + private readonly string[] _streamNames; + private readonly StreamJoinAnalysisResult _streamJoinAnalysisResult; + private readonly Attribute[] _annotations; + private readonly HistoricalViewableDesc _historicalViewableDesc; + private readonly ExprEvaluatorContext _exprEvaluatorContext; + private readonly QueryPlanIndex[] _indexSpecs; + private readonly QueryPlan _queryPlan; + private readonly HistoricalStreamIndexList[] _historicalStreamIndexLists; + private readonly bool _joinRemoveStream; + private readonly bool _isOuterJoins; + private readonly TableService _tableService; + private readonly EventTableIndexService _eventTableIndexService; + + public JoinSetComposerPrototypeImpl( + string statementName, + int statementId, + OuterJoinDesc[] outerJoinDescList, + ExprNode optionalFilterNode, + EventType[] streamTypes, + string[] streamNames, + StreamJoinAnalysisResult streamJoinAnalysisResult, + Attribute[] annotations, + HistoricalViewableDesc historicalViewableDesc, + ExprEvaluatorContext exprEvaluatorContext, + QueryPlanIndex[] indexSpecs, + QueryPlan queryPlan, + HistoricalStreamIndexList[] historicalStreamIndexLists, + bool joinRemoveStream, + bool isOuterJoins, + TableService tableService, + EventTableIndexService eventTableIndexService) + { + _statementName = statementName; + _statementId = statementId; + _outerJoinDescList = outerJoinDescList; + _optionalFilterNode = optionalFilterNode; + _streamTypes = streamTypes; + _streamNames = streamNames; + _streamJoinAnalysisResult = streamJoinAnalysisResult; + _annotations = annotations; + _historicalViewableDesc = historicalViewableDesc; + _exprEvaluatorContext = exprEvaluatorContext; + _indexSpecs = indexSpecs; + _queryPlan = queryPlan; + _historicalStreamIndexLists = historicalStreamIndexLists; + _joinRemoveStream = joinRemoveStream; + _isOuterJoins = isOuterJoins; + _tableService = tableService; + _eventTableIndexService = eventTableIndexService; + } + + public JoinSetComposerDesc Create(Viewable[] streamViews, bool isFireAndForget, AgentInstanceContext agentInstanceContext, bool isRecoveringResilient) + { + // Build indexes + var indexesPerStream = new IDictionary[_indexSpecs.Length]; + var tableSecondaryIndexLocks = new ILockable[_indexSpecs.Length]; + var hasTable = false; + for (var streamNo = 0; streamNo < _indexSpecs.Length; streamNo++) + { + if (_indexSpecs[streamNo] == null) + { + continue; + } + + var items = _indexSpecs[streamNo].Items; + indexesPerStream[streamNo] = new LinkedHashMap(); + + if (_streamJoinAnalysisResult.TablesPerStream[streamNo] != null) + { + // build for tables + var metadata = _streamJoinAnalysisResult.TablesPerStream[streamNo]; + var state = _tableService.GetState(metadata.TableName, agentInstanceContext.AgentInstanceId); + foreach (var indexName in state.SecondaryIndexes) + { // add secondary indexes + indexesPerStream[streamNo].Put(new TableLookupIndexReqKey(indexName, metadata.TableName), state.GetIndex(indexName)); + } + var index = state.GetIndex(metadata.TableName); // add primary index + indexesPerStream[streamNo].Put(new TableLookupIndexReqKey(metadata.TableName, metadata.TableName), index); + hasTable = true; + tableSecondaryIndexLocks[streamNo] = agentInstanceContext.StatementContext.IsWritesToTables ? + state.TableLevelRWLock.WriteLock : state.TableLevelRWLock.ReadLock; + } + else + { + // build tables for implicit indexes + foreach (var entry in items) + { + EventTable index; + if (_streamJoinAnalysisResult.ViewExternal[streamNo] != null) + { + VirtualDWView view = _streamJoinAnalysisResult.ViewExternal[streamNo].Invoke(agentInstanceContext); + index = view.GetJoinIndexTable(items.Get(entry.Key)); + } + else + { + index = EventTableUtil.BuildIndex( + agentInstanceContext, streamNo, items.Get(entry.Key), _streamTypes[streamNo], false, + entry.Value.IsUnique, null, null, isFireAndForget); + } + indexesPerStream[streamNo].Put(entry.Key, index); + } + } + } + + // obtain any external views + var externalViewProviders = _streamJoinAnalysisResult.ViewExternal; + var externalViews = new VirtualDWView[externalViewProviders.Length]; + for (var i = 0; i < externalViews.Length; i++) + { + if (externalViewProviders[i] != null) + { + externalViews[i] = _streamJoinAnalysisResult.ViewExternal[i].Invoke(agentInstanceContext); + } + } + + // Build strategies + var queryExecSpecs = _queryPlan.ExecNodeSpecs; + var queryStrategies = new QueryStrategy[queryExecSpecs.Length]; + for (var i = 0; i < queryExecSpecs.Length; i++) + { + var planNode = queryExecSpecs[i]; + if (planNode == null) + { + Log.Debug(".MakeComposer No execution node for stream " + i + " '" + _streamNames[i] + "'"); + continue; + } + + var executionNode = planNode.MakeExec( + _statementName, _statementId, _annotations, indexesPerStream, _streamTypes, streamViews, + _historicalStreamIndexLists, externalViews, tableSecondaryIndexLocks); + + if (Log.IsDebugEnabled) + { + Log.Debug(".MakeComposer Execution nodes for stream " + i + " '" + _streamNames[i] + + "' : \n" + ExecNode.Print(executionNode)); + } + + queryStrategies[i] = new ExecNodeQueryStrategy(i, _streamTypes.Length, executionNode); + } + + // Remove indexes that are from tables as these are only available to query strategies + if (hasTable) + { + indexesPerStream = RemoveTableIndexes(indexesPerStream, _streamJoinAnalysisResult.TablesPerStream); + } + + // If this is not unidirectional and not a self-join (excluding self-outer-join) + JoinSetComposerDesc joinSetComposerDesc; + if ((!_streamJoinAnalysisResult.IsUnidirectional) && + (!_streamJoinAnalysisResult.IsPureSelfJoin || _outerJoinDescList.Length > 0)) + { + JoinSetComposer composer; + if (_historicalViewableDesc.HasHistorical) + { + composer = new JoinSetComposerHistoricalImpl(_eventTableIndexService.AllowInitIndex(isRecoveringResilient), indexesPerStream, queryStrategies, streamViews, _exprEvaluatorContext); + } + else + { + if (isFireAndForget) + { + composer = new JoinSetComposerFAFImpl(indexesPerStream, queryStrategies, _streamJoinAnalysisResult.IsPureSelfJoin, _exprEvaluatorContext, _joinRemoveStream, _isOuterJoins); + } + else + { + composer = new JoinSetComposerImpl(_eventTableIndexService.AllowInitIndex(isRecoveringResilient), indexesPerStream, queryStrategies, _streamJoinAnalysisResult.IsPureSelfJoin, _exprEvaluatorContext, _joinRemoveStream); + } + } + + // rewrite the filter expression for all-inner joins in case "on"-clause outer join syntax was used to include those expressions + var filterExpression = GetFilterExpressionInclOnClause(_optionalFilterNode, _outerJoinDescList); + + var postJoinEval = filterExpression == null ? null : filterExpression.ExprEvaluator; + joinSetComposerDesc = new JoinSetComposerDesc(composer, postJoinEval); + } + else + { + ExprEvaluator postJoinEval = _optionalFilterNode == null ? null : _optionalFilterNode.ExprEvaluator; + + if (_streamJoinAnalysisResult.IsUnidirectionalAll) + { + JoinSetComposer composer = new JoinSetComposerAllUnidirectionalOuter(queryStrategies); + joinSetComposerDesc = new JoinSetComposerDesc(composer, postJoinEval); + } + else + { + QueryStrategy driver; + int unidirectionalStream; + if (_streamJoinAnalysisResult.IsUnidirectional) + { + unidirectionalStream = _streamJoinAnalysisResult.UnidirectionalStreamNumberFirst; + driver = queryStrategies[unidirectionalStream]; + } + else + { + unidirectionalStream = 0; + driver = queryStrategies[0]; + } + + JoinSetComposer composer = new JoinSetComposerStreamToWinImpl( + _eventTableIndexService.AllowInitIndex(isRecoveringResilient), indexesPerStream, + _streamJoinAnalysisResult.IsPureSelfJoin, + unidirectionalStream, driver, + _streamJoinAnalysisResult.UnidirectionalNonDriving); + joinSetComposerDesc = new JoinSetComposerDesc(composer, postJoinEval); + } + } + + // init if the join-set-composer allows it + if (joinSetComposerDesc.JoinSetComposer.AllowsInit) + { + + // compile prior events per stream to preload any indexes + var eventsPerStream = new EventBean[_streamNames.Length][]; + var events = new List(); + for (var i = 0; i < eventsPerStream.Length; i++) + { + // For named windows and tables, we don't need to preload indexes from the iterators as this is always done already + if (_streamJoinAnalysisResult.NamedWindow[i] || _streamJoinAnalysisResult.TablesPerStream[i] != null) + { + continue; + } + + IEnumerator it = null; + if (!(streamViews[i] is HistoricalEventViewable) && !(streamViews[i] is DerivedValueView)) + { + try + { + it = streamViews[i].GetEnumerator(); + } + catch (UnsupportedOperationException ex) + { + // Joins do not support the iterator + } + } + + if (it != null) + { + while (it.MoveNext()) + { + events.Add(it.Current); + } + eventsPerStream[i] = events.ToArray(); + events.Clear(); + } + else + { + eventsPerStream[i] = new EventBean[0]; + } + } + + // init + joinSetComposerDesc.JoinSetComposer.Init(eventsPerStream); + } + + return joinSetComposerDesc; + } + + private IDictionary[] RemoveTableIndexes(IDictionary[] indexesPerStream, TableMetadata[] tablesPerStream) + { + var result = new IDictionary[indexesPerStream.Length]; + for (var i = 0; i < indexesPerStream.Length; i++) + { + if (tablesPerStream[i] == null) + { + result[i] = indexesPerStream[i]; + continue; + } + result[i] = Collections.GetEmptyMap(); + } + return result; + } + + private ExprNode GetFilterExpressionInclOnClause(ExprNode optionalFilterNode, OuterJoinDesc[] outerJoinDescList) + { + if (optionalFilterNode == null) + { // no need to add as query planning is fully based on on-clause + return null; + } + if (outerJoinDescList.Length == 0) + { // not an outer-join syntax + return optionalFilterNode; + } + if (!OuterJoinDesc.ConsistsOfAllInnerJoins(outerJoinDescList)) + { // all-inner joins + return optionalFilterNode; + } + ExprAndNode andNode = new ExprAndNodeImpl(); + andNode.AddChildNode(optionalFilterNode); + foreach (var outerJoinDesc in outerJoinDescList) + { + andNode.AddChildNode(outerJoinDesc.MakeExprNode(null)); + } + try + { + andNode.Validate(null); + } + catch (ExprValidationException ex) + { + throw new EPRuntimeException("Unexpected exception validating expression: " + ex.Message, ex); + } + return andNode; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/join/base/JoinSetComposerStreamToWinImpl.cs b/NEsper.Core/NEsper.Core/epl/join/base/JoinSetComposerStreamToWinImpl.cs new file mode 100755 index 000000000..e45689269 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/base/JoinSetComposerStreamToWinImpl.cs @@ -0,0 +1,177 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.factory; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.@join.plan; +using com.espertech.esper.epl.@join.table; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.join.@base +{ + /// + /// Implements the function to determine a join result for a unidirectional stream-to-window + /// joins, in which a single stream's events are ever only evaluated using a query strategy. + /// + public class JoinSetComposerStreamToWinImpl : JoinSetComposer + { + private readonly bool _allowInitIndex; + private readonly EventTable[][] _repositories; + private readonly int _streamNumber; + private readonly QueryStrategy _queryStrategy; + + private readonly bool _isResetSelfJoinRepositories; + private readonly bool[] _selfJoinRepositoryResets; + + private readonly ISet> _emptyResults = new LinkedHashSet>(); + private readonly ISet> _newResults = new LinkedHashSet>(); + + /// + /// Ctor. + /// + /// if set to true [allow initialize index]. + /// for each stream an array of (indexed/unindexed) tables for lookup. + /// for self-joins + /// is the undirectional stream + /// is the lookup query strategy for the stream + /// indicators for any stream's table that reset after strategy executon + public JoinSetComposerStreamToWinImpl( + bool allowInitIndex, + IDictionary[] repositories, + bool isPureSelfJoin, + int streamNumber, + QueryStrategy queryStrategy, + bool[] selfJoinRepositoryResets) + { + _allowInitIndex = allowInitIndex; + _repositories = JoinSetComposerUtil.ToArray(repositories); + _streamNumber = streamNumber; + _queryStrategy = queryStrategy; + + _selfJoinRepositoryResets = selfJoinRepositoryResets; + if (isPureSelfJoin) + { + _isResetSelfJoinRepositories = true; + selfJoinRepositoryResets.Fill(true); + } + else + { + bool flag = false; + foreach (bool selfJoinRepositoryReset in selfJoinRepositoryResets) + { + flag |= selfJoinRepositoryReset; + } + _isResetSelfJoinRepositories = flag; + } + } + + public bool AllowsInit + { + get { return _allowInitIndex; } + } + + public virtual void Init(EventBean[][] eventsPerStream) + { + if (!_allowInitIndex) + { + throw new IllegalStateException("Initialization by events not supported"); + } + for (int i = 0; i < eventsPerStream.Length; i++) + { + if ((eventsPerStream[i] != null) && (i != _streamNumber)) + { + for (int j = 0; j < _repositories[i].Length; j++) + { + _repositories[i][j].Add((eventsPerStream[i])); + } + } + } + } + + public void Destroy() + { + foreach (EventTable[] repository in _repositories) + { + if (repository != null) + { + foreach (EventTable table in repository) + { + table.Destroy(); + } + } + } + } + + public UniformPair>> Join(EventBean[][] newDataPerStream, EventBean[][] oldDataPerStream, ExprEvaluatorContext exprEvaluatorContext) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QJoinCompositionStreamToWin(); } + _newResults.Clear(); + + // We add and remove data in one call to each index. + // Most indexes will add first then remove as newdata and olddata may contain the same event. + // Unique indexes may remove then add. + for (int stream = 0; stream < newDataPerStream.Length; stream++) + { + if (stream != _streamNumber) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QJoinCompositionStepUpdIndex(stream, newDataPerStream[stream], oldDataPerStream[stream]); } + for (int j = 0; j < _repositories[stream].Length; j++) + { + _repositories[stream][j].AddRemove(newDataPerStream[stream], oldDataPerStream[stream]); + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AJoinCompositionStepUpdIndex(); } + } + } + + // join new data + if (newDataPerStream[_streamNumber] != null) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QJoinCompositionQueryStrategy(true, _streamNumber, newDataPerStream[_streamNumber]); } + _queryStrategy.Lookup(newDataPerStream[_streamNumber], _newResults, exprEvaluatorContext); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AJoinCompositionQueryStrategy(); } + } + + // on self-joins there can be repositories which are temporary for join execution + if (_isResetSelfJoinRepositories) + { + for (int i = 0; i < _selfJoinRepositoryResets.Length; i++) + { + if (!_selfJoinRepositoryResets[i]) + { + continue; + } + for (int j = 0; j < _repositories[i].Length; j++) + { + _repositories[i][j].Clear(); + } + } + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AJoinCompositionStreamToWin(_newResults); } + return new UniformPair>>(_newResults, _emptyResults); + } + + public ISet> StaticJoin() + { + throw new UnsupportedOperationException("Iteration over a unidirectional join is not supported"); + } + + public void VisitIndexes(StatementAgentInstancePostLoadIndexVisitor visitor) + { + visitor.Visit(_repositories); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/base/JoinSetComposerUtil.cs b/NEsper.Core/NEsper.Core/epl/join/base/JoinSetComposerUtil.cs new file mode 100755 index 000000000..166be2e3d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/base/JoinSetComposerUtil.cs @@ -0,0 +1,59 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.join.plan; +using com.espertech.esper.epl.join.table; + +namespace com.espertech.esper.epl.join.@base +{ + public class JoinSetComposerUtil { + private static readonly EventTable[] EMPTY = new EventTable[0]; + + public static EventTable[][] ToArray(IDictionary[] repositories) { + return ToArray(repositories, repositories.Length); + } + + public static EventTable[][] ToArray(IDictionary[] repositories, int length) { + if (repositories == null) { + return GetDefaultTablesArray(length); + } + var tables = new EventTable[repositories.Length][]; + for (int i = 0; i < repositories.Length; i++) { + tables[i] = ToArray(repositories[i]); + } + return tables; + } + + private static EventTable[] ToArray(IDictionary repository) { + if (repository == null) { + return EMPTY; + } + var tables = new EventTable[repository.Count]; + int count = 0; + foreach (var entries in repository) { + tables[count] = entries.Value; + count++; + } + return tables; + } + + private static EventTable[][] GetDefaultTablesArray(int length) { + var result = new EventTable[length][]; + for (int i = 0; i < result.Length; i++) { + result[i] = EMPTY; + } + return result; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/join/base/JoinSetFilter.cs b/NEsper.Core/NEsper.Core/epl/join/base/JoinSetFilter.cs new file mode 100755 index 000000000..f205ad020 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/base/JoinSetFilter.cs @@ -0,0 +1,83 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.join.@base +{ + /// + /// Processes join tuple set by filtering out tuples. + /// + public class JoinSetFilter : JoinSetProcessor + { + private readonly ExprEvaluator _filterExprNode; + + /// Ctor. + /// filter tree + public JoinSetFilter(ExprEvaluator filterExprNode) + { + _filterExprNode = filterExprNode; + } + + public void Process(ISet> newEvents, ISet> oldEvents, ExprEvaluatorContext exprEvaluatorContext) + { + // Filter + if (_filterExprNode != null) + { + if (InstrumentationHelper.ENABLED) { + if ((newEvents != null && newEvents.Count > 0) || (oldEvents != null && oldEvents.Count > 0)) { + InstrumentationHelper.Get().QJoinExecFilter(); + Filter(_filterExprNode, newEvents, true, exprEvaluatorContext); + if (oldEvents != null) { + Filter(_filterExprNode, oldEvents, false, exprEvaluatorContext); + } + InstrumentationHelper.Get().AJoinExecFilter(newEvents, oldEvents); + } + return; + } + + Filter(_filterExprNode, newEvents, true, exprEvaluatorContext); + if (oldEvents != null) { + Filter(_filterExprNode, oldEvents, false, exprEvaluatorContext); + } + } + } + + /// + /// Filter event by applying the filter nodes evaluation method. + /// + /// top node of the filter expression tree. + /// set of tuples of events + /// true to indicate filter new data (istream) and not old data (rstream) + /// expression evaluation context + public static void Filter(ExprEvaluator filterExprNode, ICollection> events, bool isNewData, ExprEvaluatorContext exprEvaluatorContext) + { + var keyList = new List>(); + + foreach (MultiKey key in events) + { + var eventArr = key.Array; + var evaluateParams = new EvaluateParams(eventArr, isNewData, exprEvaluatorContext); + var matched = filterExprNode.Evaluate(evaluateParams); + if ((matched == null) || (false.Equals(matched))) + { + keyList.Add(key); + } + } + + keyList.ForEach(ev => events.Remove(ev)); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/base/JoinSetIndicator.cs b/NEsper.Core/NEsper.Core/epl/join/base/JoinSetIndicator.cs new file mode 100755 index 000000000..bc6abdb3e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/base/JoinSetIndicator.cs @@ -0,0 +1,17 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.join.@base +{ + /// + /// Marker interface for indicators to join set processors. + /// + public interface JoinSetIndicator : JoinSetProcessor + { + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/base/JoinSetProcessor.cs b/NEsper.Core/NEsper.Core/epl/join/base/JoinSetProcessor.cs new file mode 100755 index 000000000..50c248316 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/base/JoinSetProcessor.cs @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.join.@base +{ + /// + /// Processes a join result set constisting of sets of tuples of events. + /// + public interface JoinSetProcessor + { + /// Process join result set. + /// set of event tuples representing new data + /// set of event tuples representing old data + /// expression evaluation context + void Process(ISet> newEvents, ISet> oldEvents, ExprEvaluatorContext exprEvaluatorContext); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/base/QueryStrategy.cs b/NEsper.Core/NEsper.Core/epl/join/base/QueryStrategy.cs new file mode 100755 index 000000000..2ce527cc1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/base/QueryStrategy.cs @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.join.@base +{ + /// Encapsulates the strategy use to resolve the events for a stream into a tuples of events in a join. + public interface QueryStrategy + { + /// Look up events returning tuples of joined events. + /// events to use to perform the join + /// result join tuples of events + /// expression evaluation context + void Lookup(EventBean[] lookupEvents, + ICollection> joinSet, + ExprEvaluatorContext exprEvaluatorContext); + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/join/exec/base/CompositeTableLookupStrategy.cs b/NEsper.Core/NEsper.Core/epl/join/exec/base/CompositeTableLookupStrategy.cs new file mode 100755 index 000000000..9cb1cf690 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/exec/base/CompositeTableLookupStrategy.cs @@ -0,0 +1,100 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.@join.exec.composite; +using com.espertech.esper.epl.@join.plan; +using com.espertech.esper.epl.@join.rep; +using com.espertech.esper.epl.@join.table; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.join.exec.@base +{ + /// + /// Lookup on an nested map structure that represents an index for use with at least one range and possibly multiple ranges + /// and optionally keyed by one or more unique keys. + /// + /// Use the sorted strategy instead if supporting a single range only and no other unique keys are part of the index. + /// + /// + public class CompositeTableLookupStrategy : JoinExecTableLookupStrategy + { + private readonly EventType _eventType; + private readonly PropertyCompositeEventTable _index; + private readonly CompositeIndexQuery _chain; + private readonly IList _rangeKeyPairs; + private readonly LookupStrategyDesc _lookupStrategyDesc; + + public CompositeTableLookupStrategy(EventType eventType, int lookupStream, IList hashKeys, IList rangeKeyPairs, PropertyCompositeEventTable index) { + _eventType = eventType; + _index = index; + _rangeKeyPairs = rangeKeyPairs; + _chain = CompositeIndexQueryFactory.MakeJoinSingleLookupStream(false, lookupStream, hashKeys, index.OptKeyCoercedTypes, rangeKeyPairs, index.OptRangeCoercedTypes); + + var expressionTexts = new ArrayDeque(); + foreach (QueryGraphValueEntryRange pair in rangeKeyPairs) { + ExprNode[] expressions = pair.Expressions; + foreach (ExprNode node in expressions) { + expressionTexts.Add(ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(node)); + } + } + _lookupStrategyDesc = new LookupStrategyDesc(LookupStrategyType.COMPOSITE, expressionTexts.ToArray()); + } + + /// + /// Returns event type of the lookup event. + /// + /// event type of the lookup event + public EventType EventType + { + get { return _eventType; } + } + + /// + /// Returns index to look up in. + /// + /// index to use + public PropertyCompositeEventTable Index + { + get { return _index; } + } + + public ICollection Lookup(EventBean theEvent, Cursor cursor, ExprEvaluatorContext context) + { + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().QIndexJoinLookup(this, _index); + var keys = new List(2); + ICollection resultX = _chain.GetCollectKeys(theEvent, _index.MapIndex, context, keys, _index.PostProcessor); + InstrumentationHelper.Get().AIndexJoinLookup(resultX, keys.Count > 1 ? keys.ToArray() : keys[0]); + return resultX; + } + + ICollection result = _chain.Get(theEvent, _index.MapIndex, context, _index.PostProcessor); + if (result != null && result.IsEmpty()) { + return null; + } + return result; + } + + public LookupStrategyDesc StrategyDesc + { + get { return _lookupStrategyDesc; } + } + + public override String ToString() { + return string.Format("CompositeTableLookupStrategy indexProps={0} index=({1})", CompatExtensions.Render(_rangeKeyPairs), _index); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/join/exec/base/ExecNode.cs b/NEsper.Core/NEsper.Core/epl/join/exec/base/ExecNode.cs new file mode 100755 index 000000000..f125d5bc7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/exec/base/ExecNode.cs @@ -0,0 +1,49 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.join.exec.@base +{ + /// + /// Interface for an execution node that looks up events and builds a result set contributing to an overall join result set. + /// + public abstract class ExecNode + { + /// Process single event using the prefill events to compile lookup results. + /// event to look up for or query for + /// set of events currently in the example tuple to serveas a _prototype for result rows. + /// is the list of tuples to add a result row to + /// context for expression evaluation + public abstract void Process(EventBean lookupEvent, EventBean[] prefillPath, ICollection result, ExprEvaluatorContext exprEvaluatorContext); + + /// Output the execution strategy. + /// to output to + public abstract void Print(IndentWriter writer); + + /// Print in readable format the execution strategy. + /// execution node to print + /// readable text with execution nodes constructed for actual streams + public static String Print(ExecNode execNode) + { + var stringWriter = new StringWriter(); + var indentWriter = new IndentWriter(stringWriter, 4, 2); + execNode.Print(indentWriter); + + return stringWriter.ToString(); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/join/exec/base/ExecNodeAllUnidirectionalOuter.cs b/NEsper.Core/NEsper.Core/epl/join/exec/base/ExecNodeAllUnidirectionalOuter.cs new file mode 100755 index 000000000..bd8ddf15f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/exec/base/ExecNodeAllUnidirectionalOuter.cs @@ -0,0 +1,44 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.join.exec.@base +{ + public class ExecNodeAllUnidirectionalOuter : ExecNode + { + private readonly int _streamNum; + private readonly int _numStreams; + + public ExecNodeAllUnidirectionalOuter(int streamNum, int numStreams) + { + _streamNum = streamNum; + _numStreams = numStreams; + } + + public override void Process( + EventBean lookupEvent, + EventBean[] prefillPath, + ICollection result, + ExprEvaluatorContext exprEvaluatorContext) + { + var events = new EventBean[_numStreams]; + events[_streamNum] = lookupEvent; + result.Add(events); + } + + public override void Print(IndentWriter writer) + { + writer.WriteLine("ExecNodeNoOp"); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/join/exec/base/ExecNodeNoOp.cs b/NEsper.Core/NEsper.Core/epl/join/exec/base/ExecNodeNoOp.cs new file mode 100755 index 000000000..6a2c64028 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/exec/base/ExecNodeNoOp.cs @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.join.exec.@base +{ + public class ExecNodeNoOp : ExecNode + { + public override void Process(EventBean lookupEvent, EventBean[] prefillPath, ICollection result, ExprEvaluatorContext exprEvaluatorContext) + { + } + + public override void Print(IndentWriter writer) + { + writer.WriteLine("ExecNodeNoOp"); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/exec/base/FullTableScanLookupStrategy.cs b/NEsper.Core/NEsper.Core/epl/join/exec/base/FullTableScanLookupStrategy.cs new file mode 100755 index 000000000..2a586402f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/exec/base/FullTableScanLookupStrategy.cs @@ -0,0 +1,65 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.@join.rep; +using com.espertech.esper.epl.@join.table; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.join.exec.@base +{ + /// + /// Lookup on an unindexed table returning the full table as matching events. + /// + public class FullTableScanLookupStrategy : JoinExecTableLookupStrategy + { + private readonly UnindexedEventTable _eventIndex; + + /// Ctor. + /// table to use + public FullTableScanLookupStrategy(UnindexedEventTable eventIndex) + { + _eventIndex = eventIndex; + } + + public ICollection Lookup(EventBean theEvent, Cursor cursor, ExprEvaluatorContext exprEvaluatorContext) + { + var result = new Mutable>(); + + using (Instrument.With( + i => i.QIndexJoinLookup(this, _eventIndex), + i => i.AIndexJoinLookup(result.Value, null))) + { + result.Value = _eventIndex.EventSet; + if (result.Value.IsEmpty()) + result.Value = null; + + return result.Value; + } + } + + /// Returns the associated table. + /// table for lookup. + public UnindexedEventTable EventIndex + { + get { return _eventIndex; } + } + + public LookupStrategyDesc StrategyDesc + { + get { return new LookupStrategyDesc(LookupStrategyType.FULLTABLESCAN, null); } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/exec/base/FullTableScanUniqueValueLookupStrategy.cs b/NEsper.Core/NEsper.Core/epl/join/exec/base/FullTableScanUniqueValueLookupStrategy.cs new file mode 100755 index 000000000..8d7ab628a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/exec/base/FullTableScanUniqueValueLookupStrategy.cs @@ -0,0 +1,58 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.@join.exec.@base; +using com.espertech.esper.epl.join.rep; +using com.espertech.esper.epl.join.table; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.join.exec.@base +{ + /// + /// Lookup on an unindexed table returning the full table as matching events. + /// + public class FullTableScanUniqueValueLookupStrategy : JoinExecTableLookupStrategy + { + private readonly EventTableAsSet _eventIndex; + + /// + /// Ctor. + /// + /// table to use + public FullTableScanUniqueValueLookupStrategy(EventTableAsSet eventIndex) + { + _eventIndex = eventIndex; + } + + public ICollection Lookup(EventBean theEvent, Cursor cursor, ExprEvaluatorContext exprEvaluatorContext) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QIndexJoinLookup(this, _eventIndex); } + ISet result = _eventIndex.AllValues; + if (result.IsEmpty()) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AIndexJoinLookup(null, null); } + return null; + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AIndexJoinLookup(result, null); } + return result; + } + + public LookupStrategyDesc StrategyDesc + { + get { return new LookupStrategyDesc(LookupStrategyType.FULLTABLESCAN, null); } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/exec/base/HistoricalDataExecNode.cs b/NEsper.Core/NEsper.Core/epl/join/exec/base/HistoricalDataExecNode.cs new file mode 100755 index 000000000..6e75f8225 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/exec/base/HistoricalDataExecNode.cs @@ -0,0 +1,103 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.@join.@base; +using com.espertech.esper.epl.@join.pollindex; +using com.espertech.esper.epl.@join.table; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.epl.join.exec.@base +{ + /// + /// Execution node for executing a join or outer join against a historical data source, + /// using an lookup strategy for looking up into cached indexes, and an indexing strategy for indexing poll results + /// for future lookups. + /// + public class HistoricalDataExecNode : ExecNode + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly HistoricalEventViewable _historicalEventViewable; + private readonly int _historicalStreamNumber; + private readonly HistoricalIndexLookupStrategy _indexLookupStrategy; + private readonly PollResultIndexingStrategy _indexingStrategy; + private readonly EventBean[][] _lookupRows1Event; + private readonly int _numStreams; + + /// + /// Ctor. + /// + /// the view of the historical + /// the strategy to index poll result for future use + /// the strategy to use past indexed results + /// the number of streams in the join + /// the stream number of the historical + public HistoricalDataExecNode( + HistoricalEventViewable historicalEventViewable, + PollResultIndexingStrategy indexingStrategy, + HistoricalIndexLookupStrategy indexLookupStrategy, + int numStreams, + int historicalStreamNumber) + { + _historicalEventViewable = historicalEventViewable; + _indexingStrategy = indexingStrategy; + _indexLookupStrategy = indexLookupStrategy; + _numStreams = numStreams; + _historicalStreamNumber = historicalStreamNumber; + + _lookupRows1Event = new EventBean[1][]; + _lookupRows1Event[0] = new EventBean[numStreams]; + } + + public override void Process( + EventBean lookupEvent, + EventBean[] prefillPath, + ICollection result, + ExprEvaluatorContext exprEvaluatorContext) + { + _lookupRows1Event[0] = prefillPath; + EventTable[][] indexPerLookupRow = _historicalEventViewable.Poll( + _lookupRows1Event, _indexingStrategy, exprEvaluatorContext); + + for(int ii = 0; ii < indexPerLookupRow.Length; ii++) + { + var index = indexPerLookupRow[ii]; + + // Using the index, determine a subset of the whole indexed table to process, unless + // the strategy is a full table scan + IEnumerator subsetIter = _indexLookupStrategy.Lookup( + lookupEvent, index, exprEvaluatorContext); + + if (subsetIter != null) + { + // Add each row to the join result or, for outer joins, run through the outer join filter + for (; subsetIter.MoveNext();) + { + var resultRow = new EventBean[_numStreams]; + Array.Copy(prefillPath, 0, resultRow, 0, _numStreams); + resultRow[_historicalStreamNumber] = subsetIter.Current; + result.Add(resultRow); + } + } + } + } + + public override void Print(IndentWriter writer) + { + writer.WriteLine("HistoricalDataExecNode"); + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/join/exec/base/HistoricalTableLookupStrategy.cs b/NEsper.Core/NEsper.Core/epl/join/exec/base/HistoricalTableLookupStrategy.cs new file mode 100755 index 000000000..fc7891c5f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/exec/base/HistoricalTableLookupStrategy.cs @@ -0,0 +1,148 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.@join.@base; +using com.espertech.esper.epl.@join.pollindex; +using com.espertech.esper.epl.@join.rep; +using com.espertech.esper.epl.@join.table; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.view; + +namespace com.espertech.esper.epl.join.exec.@base +{ + /// + /// A lookup strategy for use in outer joins onto historical streams. + /// + public class HistoricalTableLookupStrategy : JoinExecTableLookupStrategy + { + private readonly HistoricalEventViewable _viewable; + private readonly PollResultIndexingStrategy _indexingStrategy; + private readonly HistoricalIndexLookupStrategy _lookupStrategy; + private readonly int _streamNum; + private readonly int _rootStreamNum; + private readonly ExprEvaluator _outerJoinExprNode; + private readonly EventBean[][] _lookupEventsPerStream; + + /// + /// Ctor. + /// + /// providing the polling access + /// strategy for indexing results + /// strategy for using indexed results + /// number of streams + /// stream number of the historical stream + /// the query plan root stream number + /// an optional outer join expression + public HistoricalTableLookupStrategy( + HistoricalEventViewable viewable, + PollResultIndexingStrategy indexingStrategy, + HistoricalIndexLookupStrategy lookupStrategy, + int numStreams, + int streamNum, + int rootStreamNum, + ExprEvaluator outerJoinExprNode) + { + _viewable = viewable; + _indexingStrategy = indexingStrategy; + _lookupStrategy = lookupStrategy; + _streamNum = streamNum; + _rootStreamNum = rootStreamNum; + _outerJoinExprNode = outerJoinExprNode; + _lookupEventsPerStream = new EventBean[][] + { + new EventBean[numStreams] + }; + } + + public ICollection Lookup(EventBean theEvent, Cursor cursor, ExprEvaluatorContext exprEvaluatorContext) + { + int currStream = cursor.Stream; + + // fill the current stream and the deep cursor events + _lookupEventsPerStream[0][currStream] = theEvent; + RecursiveFill(_lookupEventsPerStream[0], cursor.Node); + + // poll + EventTable[][] indexPerLookupRow = _viewable.Poll( + _lookupEventsPerStream, _indexingStrategy, exprEvaluatorContext); + + ISet result = null; + foreach (EventTable[] index in indexPerLookupRow) + { + // Using the index, determine a subset of the whole indexed table to process, unless + // the strategy is a full table scan + IEnumerator subsetIter = _lookupStrategy.Lookup(theEvent, index, exprEvaluatorContext); + + if (subsetIter != null) + { + if (_outerJoinExprNode != null) + { + // Add each row to the join result or, for outer joins, run through the outer join filter + for (; subsetIter.MoveNext();) + { + EventBean candidate = subsetIter.Current; + + _lookupEventsPerStream[0][_streamNum] = candidate; + var pass = _outerJoinExprNode.Evaluate(new EvaluateParams(_lookupEventsPerStream[0], true, exprEvaluatorContext)); + if ((pass != null) && true.Equals(pass)) + { + if (result == null) + { + result = new HashSet(); + } + result.Add(candidate); + } + } + } + else + { + // Add each row to the join result or, for outer joins, run through the outer join filter + for (; subsetIter.MoveNext();) + { + var candidate = subsetIter.Current; + if (result == null) + { + result = new HashSet(); + } + result.Add(candidate); + } + } + } + } + + return result; + } + + private void RecursiveFill(EventBean[] lookupEventsPerStream, Node node) + { + if (node == null) + { + return; + } + + Node parent = node.Parent; + if (parent == null) + { + lookupEventsPerStream[_rootStreamNum] = node.ParentEvent; + return; + } + + lookupEventsPerStream[parent.Stream] = node.ParentEvent; + RecursiveFill(lookupEventsPerStream, parent); + } + + public LookupStrategyDesc StrategyDesc + { + get { return null; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/join/exec/base/InKeywordMultiTableLookupStrategyExpr.cs b/NEsper.Core/NEsper.Core/epl/join/exec/base/InKeywordMultiTableLookupStrategyExpr.cs new file mode 100755 index 000000000..9a5abffcd --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/exec/base/InKeywordMultiTableLookupStrategyExpr.cs @@ -0,0 +1,74 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.join.plan; +using com.espertech.esper.epl.join.rep; +using com.espertech.esper.epl.join.table; +using com.espertech.esper.epl.lookup; + +namespace com.espertech.esper.epl.join.exec.@base +{ + /// Lookup on an index using a set of expression results as key values. + public class InKeywordMultiTableLookupStrategyExpr : JoinExecTableLookupStrategy + { + private readonly PropertyIndexedEventTableSingle[] _indexes; + private readonly int _streamNum; + private readonly EventBean[] _eventsPerStream; + private readonly ExprEvaluator _evaluator; + private readonly LookupStrategyDesc _lookupStrategyDesc; + + /// + /// Ctor. + /// + /// The evaluator. + /// The stream num. + /// The indexes. + /// The lookup strategy desc. + public InKeywordMultiTableLookupStrategyExpr(ExprEvaluator evaluator, int streamNum, PropertyIndexedEventTableSingle[] indexes, LookupStrategyDesc lookupStrategyDesc) + { + if (indexes == null) + { + throw new ArgumentException("Unexpected null index received"); + } + this._indexes = indexes; + this._streamNum = streamNum; + this._eventsPerStream = new EventBean[streamNum + 1]; + this._evaluator = evaluator; + this._lookupStrategyDesc = lookupStrategyDesc; + } + + /// Returns index to look up in. + /// index to use + public PropertyIndexedEventTableSingle[] GetIndex() + { + return _indexes; + } + + public ICollection Lookup(EventBean theEvent, Cursor cursor, ExprEvaluatorContext exprEvaluatorContext) + { + _eventsPerStream[_streamNum] = theEvent; + return InKeywordTableLookupUtil.MultiIndexLookup(_evaluator, _eventsPerStream, exprEvaluatorContext, _indexes); + } + + public override String ToString() + { + return this.GetType().Name + " " + _lookupStrategyDesc; + } + + public LookupStrategyDesc StrategyDesc + { + get { return _lookupStrategyDesc; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/exec/base/InKeywordSingleTableLookupStrategyExpr.cs b/NEsper.Core/NEsper.Core/epl/join/exec/base/InKeywordSingleTableLookupStrategyExpr.cs new file mode 100755 index 000000000..d1b8d755c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/exec/base/InKeywordSingleTableLookupStrategyExpr.cs @@ -0,0 +1,84 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.@join.plan; +using com.espertech.esper.epl.@join.rep; +using com.espertech.esper.epl.@join.table; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.join.exec.@base +{ + /// + /// Lookup on an index using a set of expression results as key values. + /// + public class InKeywordSingleTableLookupStrategyExpr : JoinExecTableLookupStrategy + { + private readonly PropertyIndexedEventTableSingle _index; + private readonly int _streamNum; + private readonly EventBean[] _eventsPerStream; + private readonly ExprEvaluator[] _evaluators; + private readonly LookupStrategyDesc _lookupStrategyDesc; + + /// + /// Ctor. + /// + /// The evaluators. + /// The stream num. + /// index to look up in + /// The lookup strategy desc. + public InKeywordSingleTableLookupStrategyExpr(ExprEvaluator[] evaluators, int streamNum, PropertyIndexedEventTableSingle index, LookupStrategyDesc lookupStrategyDesc) + { + if (index == null) + { + throw new ArgumentException("Unexpected null index received"); + } + _index = index; + _streamNum = streamNum; + _eventsPerStream = new EventBean[streamNum + 1]; + _evaluators = evaluators; + _lookupStrategyDesc = lookupStrategyDesc; + } + + /// Returns index to look up in. + /// index to use + public PropertyIndexedEventTableSingle Index + { + get { return _index; } + } + + public ICollection Lookup(EventBean theEvent, Cursor cursor, ExprEvaluatorContext exprEvaluatorContext) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QIndexJoinLookup(this, _index); } + + _eventsPerStream[_streamNum] = theEvent; + var result = InKeywordTableLookupUtil.SingleIndexLookup( + _evaluators, _eventsPerStream, exprEvaluatorContext, _index); + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AIndexJoinLookup(result, null); } + return result; + } + + public override String ToString() + { + return "IndexedTableLookupStrategyExpr expressions" + + " index=(" + _index + ')'; + } + + public LookupStrategyDesc StrategyDesc + { + get { return _lookupStrategyDesc; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/exec/base/IndexedTableLookupStrategy.cs b/NEsper.Core/NEsper.Core/epl/join/exec/base/IndexedTableLookupStrategy.cs new file mode 100755 index 000000000..d48742888 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/exec/base/IndexedTableLookupStrategy.cs @@ -0,0 +1,107 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.@join.rep; +using com.espertech.esper.epl.@join.table; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.events; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.join.exec.@base +{ + /// + /// Lookup on an index using a set of properties as key values. + /// + public class IndexedTableLookupStrategy : JoinExecTableLookupStrategy + { + private readonly EventType _eventType; + private readonly String[] _properties; + private readonly PropertyIndexedEventTable _index; + private readonly EventPropertyGetter[] _propertyGetters; + + /// Ctor. + /// event type to expect for lookup + /// key properties + /// index to look up in + public IndexedTableLookupStrategy(EventType eventType, String[] properties, PropertyIndexedEventTable index) + { + _eventType = eventType; + _properties = properties; + if (index == null) { + throw new ArgumentException("Unexpected null index received"); + } + _index = index; + + _propertyGetters = new EventPropertyGetter[properties.Length]; + for (int i = 0; i < properties.Length; i++) + { + _propertyGetters[i] = eventType.GetGetter(properties[i]); + + if (_propertyGetters[i] == null) + { + throw new ArgumentException("Property named '" + properties[i] + "' is invalid for type " + eventType); + } + } + } + + /// Returns event type of the lookup event. + /// event type of the lookup event + public EventType EventType + { + get { return _eventType; } + } + + /// Returns properties to use from lookup event to look up in index. + /// properties to use from lookup event + public string[] Properties + { + get { return _properties; } + } + + /// Returns index to look up in. + /// index to use + public PropertyIndexedEventTable Index + { + get { return _index; } + } + + public ICollection Lookup(EventBean theEvent, Cursor cursor, ExprEvaluatorContext exprEvaluatorContext) + { + if (InstrumentationHelper.ENABLED) {InstrumentationHelper.Get().QIndexJoinLookup(this, _index);} + + var keys = GetKeys(theEvent); + var result = _index.Lookup(keys); + + if (InstrumentationHelper.ENABLED) {InstrumentationHelper.Get().AIndexJoinLookup(result, keys);} + return result; + } + + private Object[] GetKeys(EventBean theEvent) + { + return EventBeanUtility.GetPropertyArray(theEvent, _propertyGetters); + } + + public override String ToString() + { + return "IndexedTableLookupStrategy indexProps=" + _properties.Render() + + " index=(" + _index + ')'; + } + + public LookupStrategyDesc StrategyDesc + { + get { return new LookupStrategyDesc(LookupStrategyType.MULTIPROP, _properties); } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/exec/base/IndexedTableLookupStrategyExpr.cs b/NEsper.Core/NEsper.Core/epl/join/exec/base/IndexedTableLookupStrategyExpr.cs new file mode 100755 index 000000000..3b2b1e5e8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/exec/base/IndexedTableLookupStrategyExpr.cs @@ -0,0 +1,88 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.@join.rep; +using com.espertech.esper.epl.@join.table; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.join.exec.@base +{ + /// + /// Lookup on an index using a set of expression results as key values. + /// + public class IndexedTableLookupStrategyExpr : JoinExecTableLookupStrategy + { + private readonly PropertyIndexedEventTable _index; + private readonly int _streamNum; + private readonly EventBean[] _eventsPerStream; + private readonly ExprEvaluator[] _evaluators; + private readonly LookupStrategyDesc _lookupStrategyDesc; + + /// + /// Ctor. + /// + /// The evaluators. + /// The stream num. + /// index to look up in + /// The lookup strategy desc. + public IndexedTableLookupStrategyExpr(ExprEvaluator[] evaluators, int streamNum, PropertyIndexedEventTable index, LookupStrategyDesc lookupStrategyDesc) + { + if (index == null) + { + throw new ArgumentException("Unexpected null index received"); + } + _index = index; + _streamNum = streamNum; + _eventsPerStream = new EventBean[streamNum + 1]; + _evaluators = evaluators; + _lookupStrategyDesc = lookupStrategyDesc; + } + + /// Returns index to look up in. + /// index to use + public PropertyIndexedEventTable Index + { + get { return _index; } + } + + public ICollection Lookup(EventBean theEvent, Cursor cursor, ExprEvaluatorContext exprEvaluatorContext) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QIndexJoinLookup(this, _index); } + + var keys = new Object[_evaluators.Length]; + _eventsPerStream[_streamNum] = theEvent; + for (int i = 0; i < _evaluators.Length; i++) + { + keys[i] = _evaluators[i].Evaluate(new EvaluateParams(_eventsPerStream, true, exprEvaluatorContext)); + } + + var result = _index.Lookup(keys); + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AIndexJoinLookup(result, keys); } + return result; + } + + public override String ToString() + { + return "IndexedTableLookupStrategyExpr expressions" + + " index=(" + _index + ')'; + } + + public LookupStrategyDesc StrategyDesc + { + get { return _lookupStrategyDesc; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/exec/base/IndexedTableLookupStrategySingle.cs b/NEsper.Core/NEsper.Core/epl/join/exec/base/IndexedTableLookupStrategySingle.cs new file mode 100755 index 000000000..c40814948 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/exec/base/IndexedTableLookupStrategySingle.cs @@ -0,0 +1,95 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.@join.rep; +using com.espertech.esper.epl.@join.table; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.events; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.join.exec.@base +{ + public class IndexedTableLookupStrategySingle : JoinExecTableLookupStrategy + { + private readonly EventType _eventType; + private readonly String _property; + private readonly PropertyIndexedEventTableSingle _index; + private readonly EventPropertyGetter _propertyGetter; + + /// + /// Ctor. + /// + /// event type to expect for lookup + /// The property. + /// index to look up in + public IndexedTableLookupStrategySingle(EventType eventType, String property, PropertyIndexedEventTableSingle index) + { + _eventType = eventType; + _property = property; + if (index == null) { + throw new ArgumentException("Unexpected null index received"); + } + _index = index; + _propertyGetter = EventBeanUtility.GetAssertPropertyGetter(eventType, property); + } + + /// Returns event type of the lookup event. + /// event type of the lookup event + public EventType EventType + { + get { return _eventType; } + } + + /// Returns index to look up in. + /// index to use + public PropertyIndexedEventTableSingle Index + { + get { return _index; } + } + + public ICollection Lookup(EventBean theEvent, Cursor cursor, ExprEvaluatorContext exprEvaluatorContext) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QIndexJoinLookup(this, _index);} + + var key = GetKey(theEvent); + var result = _index.Lookup(key); + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AIndexJoinLookup(result, key); } + return result; + } + + public LookupStrategyDesc StrategyDesc + { + get + { + return new LookupStrategyDesc( + LookupStrategyType.SINGLEPROP, new String[] + { + _property + }); + } + } + + private Object GetKey(EventBean theEvent) + { + return _propertyGetter.Get(theEvent); + } + + public override String ToString() + { + return "IndexedTableLookupStrategy indexProp=" + _property + + " index=(" + _index + ')'; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/exec/base/IndexedTableLookupStrategySingleExpr.cs b/NEsper.Core/NEsper.Core/epl/join/exec/base/IndexedTableLookupStrategySingleExpr.cs new file mode 100755 index 000000000..2051e4b5d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/exec/base/IndexedTableLookupStrategySingleExpr.cs @@ -0,0 +1,81 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.@join.rep; +using com.espertech.esper.epl.@join.table; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.join.exec.@base +{ + public class IndexedTableLookupStrategySingleExpr : JoinExecTableLookupStrategy + { + private readonly PropertyIndexedEventTableSingle _index; + private readonly ExprEvaluator _exprEvaluator; + private readonly int _streamNum; + private readonly EventBean[] _eventsPerStream; + private readonly LookupStrategyDesc _strategyDesc; + + /// + /// Ctor. + /// + /// The expr node. + /// The stream num. + /// index to look up in + /// The strategy desc. + public IndexedTableLookupStrategySingleExpr(ExprNode exprNode, int streamNum, PropertyIndexedEventTableSingle index, LookupStrategyDesc strategyDesc) + { + if (index == null) + { + throw new ArgumentException("Unexpected null index received"); + } + _index = index; + _streamNum = streamNum; + _strategyDesc = strategyDesc; + _eventsPerStream = new EventBean[streamNum + 1]; + _exprEvaluator = exprNode.ExprEvaluator; + } + + /// Returns index to look up in. + /// index to use + public PropertyIndexedEventTableSingle GetIndex() + { + return _index; + } + + public ICollection Lookup(EventBean theEvent, Cursor cursor, ExprEvaluatorContext exprEvaluatorContext) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QIndexJoinLookup(this, _index); } + + _eventsPerStream[_streamNum] = theEvent; + var key = _exprEvaluator.Evaluate(new EvaluateParams(_eventsPerStream, true, exprEvaluatorContext)); + var result = _index.Lookup(key); + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AIndexJoinLookup(result, key); } + + return result; + } + + public override String ToString() + { + return "IndexedTableLookupStrategySingleExpr evaluation" + + " index=(" + _index + ')'; + } + + public LookupStrategyDesc StrategyDesc + { + get { return _strategyDesc; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/exec/base/JoinExecTableLookupStrategy.cs b/NEsper.Core/NEsper.Core/epl/join/exec/base/JoinExecTableLookupStrategy.cs new file mode 100755 index 000000000..11f63e4c1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/exec/base/JoinExecTableLookupStrategy.cs @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.join.rep; +using com.espertech.esper.epl.lookup; + +namespace com.espertech.esper.epl.join.exec.@base +{ + /// + /// Strategy for looking up, in some sort of table or index, an event, potentially based + /// on the events properties, and returning a set of matched events. + /// + public interface JoinExecTableLookupStrategy + { + /// Returns matched events for a event to look up for. Never returns an empty result set, always returns null to indicate no results. + /// to look up + /// the path in the query that the lookup took + /// expression evaluation context + /// set of matching events, or null if none matching + ICollection Lookup(EventBean theEvent, Cursor cursor, ExprEvaluatorContext exprEvaluatorContext); + + LookupStrategyDesc StrategyDesc { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/exec/base/LookupInstructionExec.cs b/NEsper.Core/NEsper.Core/epl/join/exec/base/LookupInstructionExec.cs new file mode 100755 index 000000000..11595d96d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/exec/base/LookupInstructionExec.cs @@ -0,0 +1,180 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.join.rep; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.join.exec.@base +{ + /// + /// Execution for a lookup instruction to look up in one or more event streams with a supplied + /// event and using a given set of lookup strategies, and adding any lookup results to a lighweight + /// repository object for later result assembly. + /// + public class LookupInstructionExec + { + private readonly int _fromStream; + private readonly String _fromStreamName; + private readonly JoinExecTableLookupStrategy[] _lookupStrategies; + + private readonly int _numSubStreams; + private readonly ICollection[] _resultPerStream; + private readonly int[] _requiredSubStreams; + private readonly int[] _optionalSubStreams; + private readonly bool _hasRequiredSubStreams; + + /// Ctor. + /// the stream supplying the lookup event + /// the stream name supplying the lookup event + /// the set of streams to look up in + /// the strategy to use for each stream to look up in + /// indicates which of the lookup streams are required to build a result and which are not + public LookupInstructionExec(int fromStream, String fromStreamName, int[] toStreams, JoinExecTableLookupStrategy[] lookupStrategies, bool[] requiredPerStream) + { + if (toStreams.Length != lookupStrategies.Length) + { + throw new ArgumentException("Invalid number of strategies for each stream"); + } + if (requiredPerStream.Length < lookupStrategies.Length) + { + throw new ArgumentException("Invalid required per stream array"); + } + if ((fromStream < 0) || (fromStream >= requiredPerStream.Length)) + { + throw new ArgumentException("Invalid from stream"); + } + + _fromStream = fromStream; + _fromStreamName = fromStreamName; + _numSubStreams = toStreams.Length; + _lookupStrategies = lookupStrategies; + + _resultPerStream = new ICollection[_numSubStreams]; + + // Build a separate array for the required and for the optional streams + var required = new List(); + var optional = new List(); + foreach (int stream in toStreams) + { + if (requiredPerStream[stream]) + { + required.Add(stream); + } + else + { + optional.Add(stream); + } + } + _requiredSubStreams = required.ToArray(); + _optionalSubStreams = optional.ToArray(); + _hasRequiredSubStreams = _requiredSubStreams.Length > 0; + } + + /// Returns the stream number of the stream supplying the event to use for lookup. + /// stream number + public int FromStream + { + get { return _fromStream; } + } + + /// Returns true if there is one or more required substreams or false if no substreams are required joins. + /// true if any substreams are required (inner) joins, or false if not + public bool HasRequiredStream + { + get { return _hasRequiredSubStreams; } + } + + /// Execute the instruction adding results to the repository and obtaining events for lookup from the repository. + /// supplies events for lookup, and place to add results to + /// expression evaluation context + /// true if one or more results, false if no results + public bool Process(Repository repository, ExprEvaluatorContext exprEvaluatorContext) + { + bool hasOneResultRow = false; + IEnumerator it = repository.GetCursors(_fromStream); + + // Loop over all events for that stream + for (;it.MoveNext();) + { + Cursor cursor = it.Current; + EventBean lookupEvent = cursor.Event; + int streamCount = 0; + + // For that event, lookup in all required streams + while (streamCount < _requiredSubStreams.Length) + { + ICollection lookupResult = _lookupStrategies[streamCount].Lookup(lookupEvent, cursor, exprEvaluatorContext); + + // There is no result, break if this is a required stream + if (lookupResult == null || lookupResult.IsEmpty()) + { + break; + } + _resultPerStream[streamCount] = lookupResult; + streamCount++; + } + + // No results for a required stream, we are done with this event + if (streamCount < _requiredSubStreams.Length) + { + continue; + } + else + { + // Add results to repository + for (int i = 0; i < _requiredSubStreams.Length; i++) + { + hasOneResultRow = true; + repository.AddResult(cursor, _resultPerStream[i], _requiredSubStreams[i]); + } + } + + // For that event, lookup in all optional streams + for (int i = 0; i < _optionalSubStreams.Length; i++) + { + ICollection lookupResult = _lookupStrategies[streamCount].Lookup(lookupEvent, cursor, exprEvaluatorContext); + + if (lookupResult != null) + { + hasOneResultRow = true; + repository.AddResult(cursor, lookupResult, _optionalSubStreams[i]); + } + streamCount++; + } + } + + return hasOneResultRow; + } + + /// Output the instruction. + /// is the write to output to + public void Print(IndentWriter writer) + { + writer.WriteLine("LookupInstructionExec" + + " fromStream=" + _fromStream + + " fromStreamName=" + _fromStreamName + + " numSubStreams=" + _numSubStreams + + " requiredSubStreams=" + _requiredSubStreams.Render() + + " optionalSubStreams=" + _optionalSubStreams.Render()); + + writer.IncrIndent(); + for (int i = 0; i < _lookupStrategies.Length; i++) + { + writer.WriteLine("lookupStrategies[" + i + "] : " + _lookupStrategies[i]); + } + writer.DecrIndent(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/exec/base/LookupInstructionExecNode.cs b/NEsper.Core/NEsper.Core/epl/join/exec/base/LookupInstructionExecNode.cs new file mode 100755 index 000000000..1bd740909 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/exec/base/LookupInstructionExecNode.cs @@ -0,0 +1,210 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.join.assemble; +using com.espertech.esper.epl.join.rep; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.join.exec.@base +{ + /// + /// Execution for a set of lookup instructions and for a set of result assemble + /// instructions to perform joins and construct a complex result. + /// + public class LookupInstructionExecNode : ExecNode + { + private readonly int _rootStream; + private readonly String _rootStreamName; + private readonly int _numStreams; + private readonly bool[] _requiredPerStream; + private readonly LookupInstructionExec[] _lookupInstructions; + private readonly BaseAssemblyNode[] _assemblyInstructions; + private readonly MyResultAssembler _myResultAssembler; + private readonly int _requireResultsInstruction; + + /// + /// Ctor. + /// + /// is the stream supplying the lookup event + /// is the name of the stream supplying the lookup event + /// is the number of streams + /// is a list of lookups to perform + /// indicates which streams are required and which are optional in the lookup + /// The assembly instruction factories. + public LookupInstructionExecNode( + int rootStream, + String rootStreamName, + int numStreams, + LookupInstructionExec[] lookupInstructions, + bool[] requiredPerStream, + IList assemblyInstructionFactories) + { + _rootStream = rootStream; + _rootStreamName = rootStreamName; + _numStreams = numStreams; + _lookupInstructions = lookupInstructions; + _requiredPerStream = requiredPerStream; + + // We have a list of factories that are pointing to each other in a tree, i.e.: + // F1 (->F3), F2 (->F3), F3 + var nodes = new IdentityDictionary(); + foreach (BaseAssemblyNodeFactory factory in assemblyInstructionFactories) { + nodes[factory] = factory.MakeAssemblerUnassociated(); + } + + // re-associate each node after allocation + foreach (var nodeWithFactory in nodes) { + var parentFactory = nodeWithFactory.Key.ParentNode; + if (parentFactory != null) { + nodeWithFactory.Value.ParentAssembler = nodes.Get(parentFactory); + } + foreach (var childNodeFactory in nodeWithFactory.Key.ChildNodes) { + nodeWithFactory.Value.AddChild(nodes.Get(childNodeFactory)); + } + } + + _assemblyInstructions = assemblyInstructionFactories.Select(nodes.Get).ToArray(); + + _myResultAssembler = new MyResultAssembler(rootStream); + _assemblyInstructions[_assemblyInstructions.Length - 1].ParentAssembler = _myResultAssembler; + + // Determine up to which instruction we are dealing with optional results. + // When dealing with optional results we don't do fast exists if we find no lookup results + _requireResultsInstruction = 1; // we always require results from the very first lookup + for (int i = 1; i < lookupInstructions.Length; i++) + { + int fromStream = lookupInstructions[i].FromStream; + if (requiredPerStream[fromStream]) + { + _requireResultsInstruction = i + 1; // require results as long as the from-stream is a required stream + } + else + { + break; + } + } + } + + public override void Process(EventBean lookupEvent, EventBean[] prefillPath, ICollection resultFinalRows, ExprEvaluatorContext exprEvaluatorContext) + { + var repository = new RepositoryImpl(_rootStream, lookupEvent, _numStreams); + var processOptional = true; + + for (int i = 0; i < _requireResultsInstruction; i++) + { + var currentInstruction = _lookupInstructions[i]; + var hasResults = currentInstruction.Process(repository,exprEvaluatorContext); + + // no results, check what to do + if (!hasResults) + { + // If there was a required stream, we are done. + if (currentInstruction.HasRequiredStream) + { + return; + } + + // If this is the first stream and there are no results, we are done with lookups + if (i == 0) + { + processOptional = false; // go to result processing + } + } + } + + if (processOptional) + { + for (int i = _requireResultsInstruction; i < _lookupInstructions.Length; i++) + { + var currentInstruction = _lookupInstructions[i]; + currentInstruction.Process(repository, exprEvaluatorContext); + } + } + + // go over the assembly instruction set + var results = repository.NodesPerStream; + + // no results - need to execute the very last instruction/top node + if (results == null) + { + var lastAssemblyNode = _assemblyInstructions[_assemblyInstructions.Length - 1]; + lastAssemblyNode.Init(null); + lastAssemblyNode.Process(null, resultFinalRows, lookupEvent); + return; + } + + // we have results - execute all instructions + BaseAssemblyNode assemblyNode; + for (int i = 0; i < _assemblyInstructions.Length; i++) + { + assemblyNode = _assemblyInstructions[i]; + assemblyNode.Init(results); + } + for (int i = 0; i < _assemblyInstructions.Length; i++) + { + assemblyNode = _assemblyInstructions[i]; + assemblyNode.Process(results, resultFinalRows, lookupEvent); + } + } + + public override void Print(IndentWriter writer) + { + writer.WriteLine("LookupInstructionExecNode" + + " rootStream=" + _rootStream + + " name=" + _rootStreamName + + " requiredPerStream=" + _requiredPerStream.Render()); + + writer.IncrIndent(); + for (int i = 0; i < _lookupInstructions.Length; i++) + { + writer.WriteLine("lookup inst node " + i); + writer.IncrIndent(); + _lookupInstructions[i].Print(writer); + writer.DecrIndent(); + } + writer.DecrIndent(); + + writer.IncrIndent(); + for (int i = 0; i < _assemblyInstructions.Length; i++) + { + writer.WriteLine("assembly inst node " + i); + writer.IncrIndent(); + _assemblyInstructions[i].Print(writer); + writer.DecrIndent(); + } + writer.DecrIndent(); + } + + /// Receives result rows posted by result set assembly nodes. + public class MyResultAssembler : ResultAssembler + { + private readonly int _rootStream; + + /// Ctor. + /// is the root stream for which we get results + public MyResultAssembler(int rootStream) + { + _rootStream = rootStream; + } + + public void Result(EventBean[] row, int fromStreamNum, EventBean myEvent, Node myNode, ICollection resultFinalRows, EventBean resultRootEvent) + { + row[_rootStream] = resultRootEvent; + resultFinalRows.Add(row); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/exec/base/NestedIterationExecNode.cs b/NEsper.Core/NEsper.Core/epl/join/exec/base/NestedIterationExecNode.cs new file mode 100755 index 000000000..4f9dde0ec --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/exec/base/NestedIterationExecNode.cs @@ -0,0 +1,102 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.join.exec.@base +{ + /// + /// Execution node that performs a nested iteration over all child nodes. + /// + /// Each child node under this node typically represents a table lookup. The implementation + /// 'hops' from the first child to the next recursively for each row returned by a child. + /// + /// + /// It passes a 'prototype' row (prefillPath) to each new child which contains the current partial event set. + /// + /// + public class NestedIterationExecNode : ExecNode + { + private readonly List _childNodes; + private readonly int[] _nestedStreams; + private int _nestingOrderLength; + + /// + /// Ctor. + /// + /// - array of integers defining order of streams in nested join. + public NestedIterationExecNode(int[] nestedStreams) + { + _nestedStreams = nestedStreams; + _childNodes = new List(); + } + + /// + /// Add a child node. + /// + /// to add + public void AddChildNode(ExecNode childNode) + { + _childNodes.Add(childNode); + } + + public override void Process(EventBean lookupEvent, EventBean[] prefillPath, ICollection result, ExprEvaluatorContext exprEvaluatorContext) + { + _nestingOrderLength = _childNodes.Count; + RecursiveNestedJoin(lookupEvent, 0, prefillPath, result, exprEvaluatorContext); + } + + /// + /// Recursive method to run through all child nodes and, for each result set tuple returned + /// by a child node, execute the inner child of the child node until there are no inner child nodes. + /// + /// - current event to use for lookup by child node + /// - index within the child nodes indicating what nesting level we are at + /// - prototype result row to use by child nodes for generating result rows + /// - result tuple rows to be populated + /// context for expression evalauation + protected void RecursiveNestedJoin(EventBean lookupEvent, int nestingOrderIndex, EventBean[] currentPath, ICollection result, ExprEvaluatorContext exprEvaluatorContext) + { + var nestedResult = new List(); + var nestedExecNode = _childNodes[nestingOrderIndex]; + nestedExecNode.Process(lookupEvent, currentPath, nestedResult, exprEvaluatorContext); + var isLastStream = nestingOrderIndex == _nestingOrderLength - 1; + + // This is not the last nesting level so no result rows are added. Invoke next nesting level for + // each event found. + if (!isLastStream) { + for (int ii = 0; ii < nestedResult.Count; ii++) + { + var row = nestedResult[ii]; + EventBean lookup = row[_nestedStreams[nestingOrderIndex]]; + RecursiveNestedJoin(lookup, nestingOrderIndex + 1, row, result, exprEvaluatorContext); + } + return; + } + + // Loop to add result rows + for(int ii = 0; ii < nestedResult.Count; ii++) { + result.Add(nestedResult[ii]); + } + } + + public override void Print(IndentWriter writer) { + writer.WriteLine("NestedIterationExecNode"); + writer.IncrIndent(); + + foreach (ExecNode child in _childNodes) { + child.Print(writer); + } + writer.DecrIndent(); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/join/exec/base/RangeIndexLookupValue.cs b/NEsper.Core/NEsper.Core/epl/join/exec/base/RangeIndexLookupValue.cs new file mode 100755 index 000000000..af9e49aba --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/exec/base/RangeIndexLookupValue.cs @@ -0,0 +1,22 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.join.exec.@base +{ + public abstract class RangeIndexLookupValue + { + public object Value { get; private set; } + + protected RangeIndexLookupValue(Object value) + { + Value = value; + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/join/exec/base/RangeIndexLookupValueEquals.cs b/NEsper.Core/NEsper.Core/epl/join/exec/base/RangeIndexLookupValueEquals.cs new file mode 100755 index 000000000..c64f952f5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/exec/base/RangeIndexLookupValueEquals.cs @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.join.exec.@base +{ + public class RangeIndexLookupValueEquals : RangeIndexLookupValue + { + public RangeIndexLookupValueEquals(Object value) + : base(value) + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/exec/base/RangeIndexLookupValueRange.cs b/NEsper.Core/NEsper.Core/epl/join/exec/base/RangeIndexLookupValueRange.cs new file mode 100755 index 000000000..2504ca040 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/exec/base/RangeIndexLookupValueRange.cs @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.join.plan; + +namespace com.espertech.esper.epl.join.exec.@base +{ + public class RangeIndexLookupValueRange : RangeIndexLookupValue + { + public QueryGraphRangeEnum Operator { get; private set; } + + public bool IsAllowRangeReverse { get; private set; } + + public RangeIndexLookupValueRange(Object value, QueryGraphRangeEnum @operator, bool allowRangeReverse) + : base(value) + { + Operator = @operator; + IsAllowRangeReverse = allowRangeReverse; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/exec/base/SortedTableLookupStrategy.cs b/NEsper.Core/NEsper.Core/epl/join/exec/base/SortedTableLookupStrategy.cs new file mode 100755 index 000000000..3690a34df --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/exec/base/SortedTableLookupStrategy.cs @@ -0,0 +1,86 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.@join.exec.sorted; +using com.espertech.esper.epl.@join.plan; +using com.espertech.esper.epl.@join.rep; +using com.espertech.esper.epl.@join.table; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.join.exec.@base +{ + /// + /// Lookup on an index that is a sorted index on a single property queried as a range. + /// + /// Use the composite strategy if supporting multiple ranges or if range is in combination with unique key. + /// + public class SortedTableLookupStrategy : JoinExecTableLookupStrategy + { + private readonly QueryGraphValueEntryRange _rangeKeyPair; + private readonly PropertySortedEventTable _index; + private readonly SortedAccessStrategy _strategy; + + /// + /// Ctor. + /// + /// The lookup stream. + /// The num streams. + /// The range key pair. + /// Type of the coercion. + /// index to look up in + public SortedTableLookupStrategy(int lookupStream, int numStreams, QueryGraphValueEntryRange rangeKeyPair, Type coercionType, PropertySortedEventTable index) + { + _rangeKeyPair = rangeKeyPair; + _index = index; + _strategy = SortedAccessStrategyFactory.Make(false, lookupStream, numStreams, rangeKeyPair, coercionType); + } + + /// Returns index to look up in. + /// index to use + public PropertySortedEventTable Index + { + get { return _index; } + } + + public ICollection Lookup(EventBean theEvent, Cursor cursor, ExprEvaluatorContext exprEvaluatorContext) + { + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().QIndexJoinLookup(this, _index); + var keys = new List(2); + var result = _strategy.LookupCollectKeys(theEvent, _index, exprEvaluatorContext, keys); + InstrumentationHelper.Get().AIndexJoinLookup(result, keys.Count > 1 ? keys.ToArray() : keys[0]); + return result; + } + + return _strategy.Lookup(theEvent, _index, exprEvaluatorContext); + } + + public override String ToString() + { + return "SortedTableLookupStrategy indexProps=" + _rangeKeyPair + + " index=(" + _index + ')'; + } + + public LookupStrategyDesc StrategyDesc + { + get + { + return new LookupStrategyDesc( + LookupStrategyType.RANGE, ExprNodeUtility.ToExpressionStringsMinPrecedence(_rangeKeyPair.Expressions)); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/exec/base/TableLookupExecNode.cs b/NEsper.Core/NEsper.Core/epl/join/exec/base/TableLookupExecNode.cs new file mode 100755 index 000000000..2e288bd37 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/exec/base/TableLookupExecNode.cs @@ -0,0 +1,75 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.join.exec.@base +{ + /// + /// Execution node for lookup in a table. + /// + public class TableLookupExecNode : ExecNode + { + /// + /// Ctor. + /// + /// stream indexed for lookup + /// strategy to use for lookup (full table/indexed) + public TableLookupExecNode(int indexedStream, JoinExecTableLookupStrategy lookupStrategy) + { + IndexedStream = indexedStream; + LookupStrategy = lookupStrategy; + } + + /// + /// Returns strategy for lookup. + /// + /// lookup strategy + public JoinExecTableLookupStrategy LookupStrategy { get; private set; } + + public override void Process(EventBean lookupEvent, EventBean[] prefillPath, ICollection result, ExprEvaluatorContext exprEvaluatorContext) + { + // Lookup events + var joinedEvents = LookupStrategy.Lookup(lookupEvent, null, exprEvaluatorContext); + if (joinedEvents == null) { + return; + } + ProcessResults(prefillPath, result, joinedEvents); + } + + protected void ProcessResults(EventBean[] prefillPath, ICollection result, IEnumerable joinedEvents) + { + // Create result row for each found event + foreach (EventBean joinedEvent in joinedEvents) + { + EventBean[] events = new EventBean[prefillPath.Length]; + Array.Copy(prefillPath, 0, events, 0, events.Length); + events[IndexedStream] = joinedEvent; + result.Add(events); + } + } + + /// + /// Returns target stream for lookup. + /// + /// indexed stream + public int IndexedStream { get; private set; } + + public override void Print(IndentWriter writer) + { + writer.WriteLine("TableLookupExecNode indexedStream=" + IndexedStream + " lookup=" + LookupStrategy); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/join/exec/base/TableLookupExecNodeTableLocking.cs b/NEsper.Core/NEsper.Core/epl/join/exec/base/TableLookupExecNodeTableLocking.cs new file mode 100755 index 000000000..48defc15a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/exec/base/TableLookupExecNodeTableLocking.cs @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.threading; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.join.exec.@base +{ + /// + /// Execution node for lookup in a table. + /// + public class TableLookupExecNodeTableLocking : TableLookupExecNode + { + private readonly ILockable _lock; + + public TableLookupExecNodeTableLocking(int indexedStream, JoinExecTableLookupStrategy lookupStrategy, ILockable @lock) + : base(indexedStream, lookupStrategy) + { + _lock = @lock; + } + + public override void Process(EventBean lookupEvent, EventBean[] prefillPath, ICollection result, ExprEvaluatorContext exprEvaluatorContext) + { + // acquire table index lock + exprEvaluatorContext.TableExprEvaluatorContext.AddAcquiredLock(_lock); + + // lookup events + var joinedEvents = LookupStrategy.Lookup(lookupEvent, null, exprEvaluatorContext); + if (joinedEvents == null) { + return; + } + + // process results + base.ProcessResults(prefillPath, result, joinedEvents); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/exec/base/TableOuterLookupExecNode.cs b/NEsper.Core/NEsper.Core/epl/join/exec/base/TableOuterLookupExecNode.cs new file mode 100755 index 000000000..82c88f7dc --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/exec/base/TableOuterLookupExecNode.cs @@ -0,0 +1,82 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.join.exec.@base +{ + /// + /// Execution node for lookup in a table for outer joins. This execution node thus generates + /// rows even if no joined events could be found, the joined table events are set to null if + /// no joined events are found. + /// + public class TableOuterLookupExecNode : ExecNode + { + /// Ctor. + /// stream indexed for lookup + /// strategy to use for lookup (full table/indexed) + public TableOuterLookupExecNode(int indexedStream, JoinExecTableLookupStrategy lookupStrategy) + { + IndexedStream = indexedStream; + LookupStrategy = lookupStrategy; + } + + /// Returns strategy for lookup. + /// lookup strategy + public JoinExecTableLookupStrategy LookupStrategy { get; private set; } + + /// Returns target stream for lookup. + /// indexed stream + public int IndexedStream { get; private set; } + + public override void Process(EventBean lookupEvent, EventBean[] prefillPath, ICollection result, ExprEvaluatorContext exprEvaluatorContext) + { + // Lookup events + ICollection joinedEvents = LookupStrategy.Lookup(lookupEvent, null, exprEvaluatorContext); + + // process + ProcessResults(prefillPath, result, joinedEvents); + } + + protected void ProcessResults(EventBean[] prefillPath, ICollection result, ICollection joinedEvents) + { + // If no events are found, since this is an outer join, create a result row leaving the + // joined event as null. + if ((joinedEvents == null) || (joinedEvents.IsEmpty())) + { + var events = new EventBean[prefillPath.Length]; + Array.Copy(prefillPath, 0, events, 0, events.Length); + result.Add(events); + + return; + } + + // Create result row for each found event + foreach (EventBean joinedEvent in joinedEvents) + { + var events = new EventBean[prefillPath.Length]; + Array.Copy(prefillPath, 0, events, 0, events.Length); + events[IndexedStream] = joinedEvent; + result.Add(events); + } + } + + + public override void Print(IndentWriter writer) + { + writer.WriteLine("TableOuterLookupExecNode indexedStream=" + IndexedStream + " lookup=" + LookupStrategy); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/exec/base/TableOuterLookupExecNodeTableLocking.cs b/NEsper.Core/NEsper.Core/epl/join/exec/base/TableOuterLookupExecNodeTableLocking.cs new file mode 100755 index 000000000..978cf6494 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/exec/base/TableOuterLookupExecNodeTableLocking.cs @@ -0,0 +1,42 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.threading; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.join.exec.@base +{ + public class TableOuterLookupExecNodeTableLocking : TableOuterLookupExecNode + { + private readonly ILockable _lock; + + public TableOuterLookupExecNodeTableLocking(int indexedStream, JoinExecTableLookupStrategy lookupStrategy, ILockable @lock) + : base(indexedStream, lookupStrategy) + { + _lock = @lock; + } + + public override void Process(EventBean lookupEvent, EventBean[] prefillPath, ICollection result, ExprEvaluatorContext exprEvaluatorContext) + { + // acquire table index lock + exprEvaluatorContext.TableExprEvaluatorContext.AddAcquiredLock(_lock); + + // lookup events + var joinedEvents = LookupStrategy.Lookup(lookupEvent, null, exprEvaluatorContext); + + // process results + base.ProcessResults(prefillPath, result, joinedEvents); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeAccessStrategy.cs b/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeAccessStrategy.cs new file mode 100755 index 000000000..15ad440f1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeAccessStrategy.cs @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.join.exec.composite +{ + using Map = IDictionary; + + public interface CompositeAccessStrategy + { + ICollection Lookup( + EventBean theEvent, + Map parent, + ICollection result, + CompositeIndexQuery next, + ExprEvaluatorContext context, + IList optionalKeyCollector, + CompositeIndexQueryResultPostProcessor postProcessor); + + ICollection Lookup( + EventBean[] eventPerStream, + Map parent, + ICollection result, + CompositeIndexQuery next, + ExprEvaluatorContext context, + IList optionalKeyCollector, + CompositeIndexQueryResultPostProcessor postProcessor); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeAccessStrategyGE.cs b/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeAccessStrategyGE.cs new file mode 100755 index 000000000..6b8aa2b13 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeAccessStrategyGE.cs @@ -0,0 +1,75 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.join.exec.composite +{ + using DataMap = IDictionary; + + public class CompositeAccessStrategyGE + : CompositeAccessStrategyRelOpBase + , CompositeAccessStrategy + { + public CompositeAccessStrategyGE( + bool isNWOnTrigger, + int lookupStream, + int numStreams, + ExprEvaluator key, + Type coercionType) + : base(isNWOnTrigger, lookupStream, numStreams, key, coercionType) + { + } + + public ICollection Lookup( + EventBean theEvent, + DataMap parent, + ICollection result, + CompositeIndexQuery next, + ExprEvaluatorContext context, + IList optionalKeyCollector, + CompositeIndexQueryResultPostProcessor postProcessor) + { + var index = (OrderedDictionary) parent; + var comparable = EvaluateLookup(theEvent, context); + if (optionalKeyCollector != null) + optionalKeyCollector.Add(comparable); + if (comparable == null) + return null; + comparable = EventBeanUtility.Coerce(comparable, CoercionType); + return CompositeIndexQueryRange.Handle(theEvent, index.Tail(comparable), null, result, next, postProcessor); + } + + public ICollection Lookup( + EventBean[] eventPerStream, + DataMap parent, + ICollection result, + CompositeIndexQuery next, + ExprEvaluatorContext context, + IList optionalKeyCollector, + CompositeIndexQueryResultPostProcessor postProcessor) + { + var index = (OrderedDictionary) parent; + var comparable = EvaluatePerStream(eventPerStream, context); + if (optionalKeyCollector != null) + optionalKeyCollector.Add(comparable); + if (comparable == null) + return null; + comparable = EventBeanUtility.Coerce(comparable, CoercionType); + return CompositeIndexQueryRange.Handle( + eventPerStream, index.Tail(comparable), null, result, next, postProcessor); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeAccessStrategyGT.cs b/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeAccessStrategyGT.cs new file mode 100755 index 000000000..eecdd2940 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeAccessStrategyGT.cs @@ -0,0 +1,75 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.join.exec.composite +{ + using DataMap = IDictionary; + + public class CompositeAccessStrategyGT + : CompositeAccessStrategyRelOpBase + , CompositeAccessStrategy + { + public CompositeAccessStrategyGT( + bool isNWOnTrigger, + int lookupStream, + int numStreams, + ExprEvaluator key, + Type coercionType) + : base(isNWOnTrigger, lookupStream, numStreams, key, coercionType) + { + } + + public ICollection Lookup( + EventBean theEvent, + DataMap parent, + ICollection result, + CompositeIndexQuery next, + ExprEvaluatorContext context, + IList optionalKeyCollector, + CompositeIndexQueryResultPostProcessor postProcessor) + { + var index = (OrderedDictionary) parent; + var comparable = EvaluateLookup(theEvent, context); + if (optionalKeyCollector != null) + optionalKeyCollector.Add(comparable); + if (comparable == null) + return null; + comparable = EventBeanUtility.Coerce(comparable, CoercionType); + return CompositeIndexQueryRange.Handle(theEvent, index.Tail(comparable, false), null, result, next, postProcessor); + } + + public ICollection Lookup( + EventBean[] eventPerStream, + DataMap parent, + ICollection result, + CompositeIndexQuery next, + ExprEvaluatorContext context, + IList optionalKeyCollector, + CompositeIndexQueryResultPostProcessor postProcessor) + { + var index = (OrderedDictionary) parent; + var comparable = EvaluatePerStream(eventPerStream, context); + if (optionalKeyCollector != null) + optionalKeyCollector.Add(comparable); + if (comparable == null) + return null; + comparable = EventBeanUtility.Coerce(comparable, CoercionType); + return CompositeIndexQueryRange.Handle( + eventPerStream, index.Tail(comparable, false), null, result, next, postProcessor); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeAccessStrategyLE.cs b/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeAccessStrategyLE.cs new file mode 100755 index 000000000..98d5ed655 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeAccessStrategyLE.cs @@ -0,0 +1,75 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.join.exec.composite +{ + using DataMap = IDictionary; + + public class CompositeAccessStrategyLE + : CompositeAccessStrategyRelOpBase + , CompositeAccessStrategy + { + public CompositeAccessStrategyLE( + bool isNWOnTrigger, + int lookupStream, + int numStreams, + ExprEvaluator key, + Type coercionType) + : base(isNWOnTrigger, lookupStream, numStreams, key, coercionType) + { + } + + public ICollection Lookup( + EventBean theEvent, + DataMap parent, + ICollection result, + CompositeIndexQuery next, + ExprEvaluatorContext context, + IList optionalKeyCollector, + CompositeIndexQueryResultPostProcessor postProcessor) + { + var index = (OrderedDictionary) parent; + var comparable = base.EvaluateLookup(theEvent, context); + if (optionalKeyCollector != null) + optionalKeyCollector.Add(comparable); + if (comparable == null) + return null; + comparable = EventBeanUtility.Coerce(comparable, CoercionType); + return CompositeIndexQueryRange.Handle(theEvent, index.Head(comparable, true), null, result, next, postProcessor); + } + + public ICollection Lookup( + EventBean[] eventPerStream, + DataMap parent, + ICollection result, + CompositeIndexQuery next, + ExprEvaluatorContext context, + IList optionalKeyCollector, + CompositeIndexQueryResultPostProcessor postProcessor) + { + var index = (OrderedDictionary) parent; + var comparable = base.EvaluatePerStream(eventPerStream, context); + if (optionalKeyCollector != null) + optionalKeyCollector.Add(comparable); + if (comparable == null) + return null; + comparable = EventBeanUtility.Coerce(comparable, CoercionType); + return CompositeIndexQueryRange.Handle( + eventPerStream, index.Head(comparable, true), null, result, next, postProcessor); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeAccessStrategyLT.cs b/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeAccessStrategyLT.cs new file mode 100755 index 000000000..414974b6d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeAccessStrategyLT.cs @@ -0,0 +1,75 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.join.exec.composite +{ + using DataMap = IDictionary; + + public class CompositeAccessStrategyLT + : CompositeAccessStrategyRelOpBase + , CompositeAccessStrategy + { + public CompositeAccessStrategyLT( + bool isNWOnTrigger, + int lookupStream, + int numStreams, + ExprEvaluator key, + Type coercionType) + : base(isNWOnTrigger, lookupStream, numStreams, key, coercionType) + { + } + + public ICollection Lookup( + EventBean theEvent, + DataMap parent, + ICollection result, + CompositeIndexQuery next, + ExprEvaluatorContext context, + IList optionalKeyCollector, + CompositeIndexQueryResultPostProcessor postProcessor) + { + var index = (OrderedDictionary) parent; + var comparable = base.EvaluateLookup(theEvent, context); + if (optionalKeyCollector != null) + optionalKeyCollector.Add(comparable); + if (comparable == null) + return null; + comparable = EventBeanUtility.Coerce(comparable, CoercionType); + return CompositeIndexQueryRange.Handle(theEvent, index.Head(comparable), null, result, next, postProcessor); + } + + public ICollection Lookup( + EventBean[] eventsPerStream, + DataMap parent, + ICollection result, + CompositeIndexQuery next, + ExprEvaluatorContext context, + IList optionalKeyCollector, + CompositeIndexQueryResultPostProcessor postProcessor) + { + var index = (OrderedDictionary) parent; + var comparable = base.EvaluatePerStream(eventsPerStream, context); + if (optionalKeyCollector != null) + optionalKeyCollector.Add(comparable); + if (comparable == null) + return null; + comparable = EventBeanUtility.Coerce(comparable, CoercionType); + return CompositeIndexQueryRange.Handle( + eventsPerStream, index.Head(comparable), null, result, next, postProcessor); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeAccessStrategyRangeBase.cs b/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeAccessStrategyRangeBase.cs new file mode 100755 index 000000000..23aef161a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeAccessStrategyRangeBase.cs @@ -0,0 +1,79 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.join.exec.composite +{ + public abstract class CompositeAccessStrategyRangeBase + { + protected readonly ExprEvaluator Start; + protected readonly bool IncludeStart; + + protected readonly ExprEvaluator End; + protected readonly bool IncludeEnd; + + private readonly EventBean[] _events; + private readonly int _lookupStream; + + protected readonly Type CoercionType; + private readonly bool _isNwOnTrigger; + + protected CompositeAccessStrategyRangeBase(bool isNWOnTrigger, int lookupStream, int numStreams, ExprEvaluator start, bool includeStart, ExprEvaluator end, bool includeEnd, Type coercionType) + { + Start = start; + IncludeStart = includeStart; + End = end; + IncludeEnd = includeEnd; + CoercionType = coercionType; + _isNwOnTrigger = isNWOnTrigger; + + if (lookupStream != -1) { + _events = new EventBean[lookupStream + 1]; + } + else { + _events = new EventBean[numStreams + 1]; + } + _lookupStream = lookupStream; + } + + public Object EvaluateLookupStart(EventBean theEvent, ExprEvaluatorContext context) { + _events[_lookupStream] = theEvent; + return Start.Evaluate(new EvaluateParams(_events, true, context)); + } + + public Object EvaluateLookupEnd(EventBean theEvent, ExprEvaluatorContext context) { + _events[_lookupStream] = theEvent; + return End.Evaluate(new EvaluateParams(_events, true, context)); + } + + public Object EvaluatePerStreamStart(EventBean[] eventPerStream, ExprEvaluatorContext context) { + if (_isNwOnTrigger) { + return Start.Evaluate(new EvaluateParams(eventPerStream, true, context)); + } + else { + Array.Copy(eventPerStream, 0, _events, 1, eventPerStream.Length); + return Start.Evaluate(new EvaluateParams(_events, true, context)); + } + } + + public Object EvaluatePerStreamEnd(EventBean[] eventPerStream, ExprEvaluatorContext context) { + if (_isNwOnTrigger) { + return End.Evaluate(new EvaluateParams(eventPerStream, true, context)); + } + else { + Array.Copy(eventPerStream, 0, _events, 1, eventPerStream.Length); + return End.Evaluate(new EvaluateParams(_events, true, context)); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeAccessStrategyRangeInverted.cs b/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeAccessStrategyRangeInverted.cs new file mode 100755 index 000000000..e04f405a9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeAccessStrategyRangeInverted.cs @@ -0,0 +1,82 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.join.exec.composite +{ + using Map = IDictionary; + using TreeMap = OrderedDictionary; + + public class CompositeAccessStrategyRangeInverted + : CompositeAccessStrategyRangeBase + , CompositeAccessStrategy + { + public CompositeAccessStrategyRangeInverted(bool isNWOnTrigger, int lookupStream, int numStreams, ExprEvaluator start, bool includeStart, ExprEvaluator end, bool includeEnd, Type coercionType) + : base(isNWOnTrigger, lookupStream, numStreams, start, includeStart, end, includeEnd, coercionType) + { + } + + public ICollection Lookup(EventBean theEvent, Map parent, ICollection result, CompositeIndexQuery next, ExprEvaluatorContext context, IList optionalKeyCollector, CompositeIndexQueryResultPostProcessor postProcessor) + { + var comparableStart = base.EvaluateLookupStart(theEvent, context); + if (optionalKeyCollector != null) { + optionalKeyCollector.Add(comparableStart); + } + if (comparableStart == null) { + return null; + } + var comparableEnd = base.EvaluateLookupEnd(theEvent, context); + if (optionalKeyCollector != null) { + optionalKeyCollector.Add(comparableEnd); + } + if (comparableEnd == null) { + return null; + } + comparableStart = EventBeanUtility.Coerce(comparableStart, CoercionType); + comparableEnd = EventBeanUtility.Coerce(comparableEnd, CoercionType); + + var index = (TreeMap) parent; + var submapOne = index.Head(comparableStart, !IncludeStart); + var submapTwo = index.Tail(comparableEnd, !IncludeEnd); + return CompositeIndexQueryRange.Handle(theEvent, submapOne, submapTwo, result, next, postProcessor); + } + + public ICollection Lookup(EventBean[] eventPerStream, Map parent, ICollection result, CompositeIndexQuery next, ExprEvaluatorContext context, IList optionalKeyCollector, CompositeIndexQueryResultPostProcessor postProcessor) + { + var comparableStart = base.EvaluatePerStreamStart(eventPerStream, context); + if (optionalKeyCollector != null) { + optionalKeyCollector.Add(comparableStart); + } + if (comparableStart == null) { + return null; + } + var comparableEnd = base.EvaluatePerStreamEnd(eventPerStream, context); + if (optionalKeyCollector != null) { + optionalKeyCollector.Add(comparableEnd); + } + if (comparableEnd == null) { + return null; + } + comparableStart = EventBeanUtility.Coerce(comparableStart, CoercionType); + comparableEnd = EventBeanUtility.Coerce(comparableEnd, CoercionType); + + var index = (TreeMap) parent; + var submapOne = index.Head(comparableStart, !IncludeStart); + var submapTwo = index.Tail(comparableEnd, !IncludeEnd); + return CompositeIndexQueryRange.Handle(eventPerStream, submapOne, submapTwo, result, next, postProcessor); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeAccessStrategyRangeNormal.cs b/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeAccessStrategyRangeNormal.cs new file mode 100755 index 000000000..044890d6d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeAccessStrategyRangeNormal.cs @@ -0,0 +1,132 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.join.exec.composite +{ + using TreeMap = OrderedDictionary; + using Map = IDictionary; + + public class CompositeAccessStrategyRangeNormal + : CompositeAccessStrategyRangeBase + , CompositeAccessStrategy + { + private readonly bool _allowReverseRange; + + public CompositeAccessStrategyRangeNormal( + bool isNWOnTrigger, + int lookupStream, + int numStreams, + ExprEvaluator start, + bool includeStart, + ExprEvaluator end, + bool includeEnd, + Type coercionType, + bool allowReverseRange) + : base(isNWOnTrigger, lookupStream, numStreams, start, includeStart, end, includeEnd, coercionType) + { + _allowReverseRange = allowReverseRange; + } + + public ICollection Lookup(EventBean theEvent, Map parent, ICollection result, CompositeIndexQuery next, ExprEvaluatorContext context, IList optionalKeyCollector, CompositeIndexQueryResultPostProcessor postProcessor) + { + var comparableStart = base.EvaluateLookupStart(theEvent, context); + if (optionalKeyCollector != null) + { + optionalKeyCollector.Add(comparableStart); + } + if (comparableStart == null) + { + return null; + } + var comparableEnd = base.EvaluateLookupEnd(theEvent, context); + if (optionalKeyCollector != null) + { + optionalKeyCollector.Add(comparableEnd); + } + if (comparableEnd == null) + { + return null; + } + var index = (OrderedDictionary) parent; + comparableStart = EventBeanUtility.Coerce(comparableStart, CoercionType); + comparableEnd = EventBeanUtility.Coerce(comparableEnd, CoercionType); + + IDictionary submap; + try + { + submap = index.Between(comparableStart, IncludeStart, comparableEnd, IncludeEnd); + } + catch (ArgumentException) + { + if (_allowReverseRange) + { + submap = index.Between(comparableEnd, IncludeStart, comparableStart, IncludeEnd); + } + else + { + return null; + } + } + + return CompositeIndexQueryRange.Handle(theEvent, submap, null, result, next, postProcessor); + } + + public ICollection Lookup(EventBean[] eventPerStream, Map parent, ICollection result, CompositeIndexQuery next, ExprEvaluatorContext context, IList optionalKeyCollector, CompositeIndexQueryResultPostProcessor postProcessor) + { + var comparableStart = base.EvaluatePerStreamStart(eventPerStream, context); + if (optionalKeyCollector != null) + { + optionalKeyCollector.Add(comparableStart); + } + if (comparableStart == null) + { + return null; + } + var comparableEnd = base.EvaluatePerStreamEnd(eventPerStream, context); + if (optionalKeyCollector != null) + { + optionalKeyCollector.Add(comparableEnd); + } + if (comparableEnd == null) + { + return null; + } + var index = (OrderedDictionary) parent; + comparableStart = EventBeanUtility.Coerce(comparableStart, CoercionType); + comparableEnd = EventBeanUtility.Coerce(comparableEnd, CoercionType); + + IDictionary submap; + try + { + submap = index.Between(comparableStart, IncludeStart, comparableEnd, IncludeEnd); + } + catch (ArgumentException) + { + if (_allowReverseRange) + { + submap = index.Between(comparableEnd, IncludeStart, comparableStart, IncludeEnd); + } + else + { + return null; + } + } + + return CompositeIndexQueryRange.Handle(eventPerStream, submap, null, result, next, postProcessor); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeAccessStrategyRelOpBase.cs b/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeAccessStrategyRelOpBase.cs new file mode 100755 index 000000000..00a87ab9e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeAccessStrategyRelOpBase.cs @@ -0,0 +1,67 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.join.exec.composite +{ + using DataMap = IDictionary; + + public abstract class CompositeAccessStrategyRelOpBase + { + protected ExprEvaluator Key; + protected Type CoercionType; + + private readonly EventBean[] _events; + private readonly int _lookupStream; + private readonly bool _isNwOnTrigger; + + protected CompositeAccessStrategyRelOpBase(bool isNWOnTrigger, int lookupStream, int numStreams, ExprEvaluator key, Type coercionType) + { + Key = key; + CoercionType = coercionType; + + if (lookupStream != -1) + { + _events = new EventBean[lookupStream + 1]; + } + else + { + _events = new EventBean[numStreams + 1]; + } + + _lookupStream = lookupStream; + _isNwOnTrigger = isNWOnTrigger; + } + + public Object EvaluateLookup(EventBean theEvent, ExprEvaluatorContext context) + { + _events[_lookupStream] = theEvent; + return Key.Evaluate(new EvaluateParams(_events, true, context)); + } + + public Object EvaluatePerStream(EventBean[] eventPerStream, ExprEvaluatorContext context) + { + if (_isNwOnTrigger) + { + return Key.Evaluate(new EvaluateParams(eventPerStream, true, context)); + } + else + { + Array.Copy(eventPerStream, 0, _events, 1, eventPerStream.Length); + return Key.Evaluate(new EvaluateParams(_events, true, context)); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeIndexEnterRemove.cs b/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeIndexEnterRemove.cs new file mode 100755 index 000000000..0f7135230 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeIndexEnterRemove.cs @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; + +namespace com.espertech.esper.epl.join.exec.composite +{ + using DataMap = IDictionary; + using AnyMap = IDictionary; + + public interface CompositeIndexEnterRemove + { + void Enter(EventBean theEvent, AnyMap parent); + void SetNext(CompositeIndexEnterRemove next); + void Remove(EventBean theEvent, AnyMap parent); + void GetAll(ICollection result, AnyMap parent); + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeIndexEnterRemoveKeyed.cs b/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeIndexEnterRemoveKeyed.cs new file mode 100755 index 000000000..7378832df --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeIndexEnterRemoveKeyed.cs @@ -0,0 +1,74 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.join.exec.composite +{ + using DataMap = IDictionary; + using AnyMap = IDictionary; + + public class CompositeIndexEnterRemoveKeyed + : CompositeIndexEnterRemove + { + private readonly IList _propertyGetters; + private readonly IList _keyCoercionTypes; + private CompositeIndexEnterRemove _next; + + public CompositeIndexEnterRemoveKeyed(EventType eventType, IList keysProps, IList keyCoercionTypes) { + _keyCoercionTypes = keyCoercionTypes; + _propertyGetters = new EventPropertyGetter[keysProps.Count]; + for (int i = 0; i < keysProps.Count; i++) + { + _propertyGetters[i] = EventBeanUtility.GetAssertPropertyGetter(eventType, keysProps[i]); + } + } + + public void SetNext(CompositeIndexEnterRemove next) + { + _next = next; + } + + public void Enter(EventBean theEvent, AnyMap parent) { + var mk = EventBeanUtility.GetMultiKey(theEvent, _propertyGetters, _keyCoercionTypes); + var innerIndex = (AnyMap)parent.Get(mk); + if (innerIndex == null) { + innerIndex = new OrderedDictionary(); + parent.Put(mk, innerIndex); + } + _next.Enter(theEvent, innerIndex); + } + + public void Remove(EventBean theEvent, AnyMap parent) + { + var mk = EventBeanUtility.GetMultiKey(theEvent, _propertyGetters, _keyCoercionTypes); + var innerIndex = (AnyMap)parent.Get(mk); + if (innerIndex == null) { + return; + } + _next.Remove(theEvent, innerIndex); + if (innerIndex.IsEmpty()) { + parent.Remove(mk); + } + } + + public void GetAll(ICollection result, AnyMap parent) + { + var map = parent; + foreach (var entry in map) + { + _next.GetAll(result, entry.Value as AnyMap); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeIndexEnterRemoveRange.cs b/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeIndexEnterRemoveRange.cs new file mode 100755 index 000000000..6f114c729 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeIndexEnterRemoveRange.cs @@ -0,0 +1,142 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.join.exec.composite +{ + using DataMap = IDictionary; + using AnyMap = IDictionary; + + public class CompositeIndexEnterRemoveRange + : CompositeIndexEnterRemove + { + private readonly EventPropertyGetter _propertyGetter; + private readonly Type _coercionType; + private HashSet _nullKeys; + private CompositeIndexEnterRemove _next; + + public CompositeIndexEnterRemoveRange(EventType eventType, String rangeProp, Type coercionType) { + _propertyGetter = EventBeanUtility.GetAssertPropertyGetter(eventType, rangeProp); + _coercionType = coercionType; + } + + public void SetNext(CompositeIndexEnterRemove next) { + _next = next; + } + + public void GetAll(ICollection result, AnyMap parent) + { + if (_next == null) { + var eventMap = parent; + foreach (var entry in eventMap) + { + result.AddAll(entry.Value as ICollection); + } + } + else { + var eventMap = parent; + foreach (var entry in eventMap) { + _next.GetAll(result, entry.Value as AnyMap); + } + } + if (_nullKeys != null) { + result.AddAll(_nullKeys); + } + } + + public void Enter(EventBean theEvent, AnyMap parent) { + Object sortable = _propertyGetter.Get(theEvent); + + if (sortable == null) { + if (_nullKeys == null) { + _nullKeys = new HashSet(); + } + _nullKeys.Add(theEvent); + return; + } + + sortable = EventBeanUtility.Coerce(sortable, _coercionType); + + // if this is a leaf, enter event + if (_next == null) { + var eventMap = parent; + var events = eventMap.Get(sortable) as ICollection; + if (events == null) + { + events = new HashSet(); + eventMap.Put(sortable, events); + } + + events.Add(theEvent); + } + else { + AnyMap innerIndex = (AnyMap) parent.Get(sortable); + if (innerIndex == null) { + innerIndex = new OrderedDictionary(); + parent.Put(sortable, innerIndex); + } + _next.Enter(theEvent, innerIndex); + } + } + + public void Remove(EventBean theEvent, AnyMap parent) + { + Object sortable = _propertyGetter.Get(theEvent); + + if (sortable == null) { + if (_nullKeys != null) { + _nullKeys.Remove(theEvent); + } + return; + } + + sortable = EventBeanUtility.Coerce(sortable, _coercionType); + + // if this is a leaf, remove event + if (_next == null) { + var eventMap = parent; + if (eventMap == null) { + return; + } + + var events = eventMap.Get(sortable) as ICollection; + if (events == null) + { + return; + } + + if (!events.Remove(theEvent)) + { + return; + } + + if (events.IsEmpty()) + { + parent.Remove(sortable); + } + } + else { + var innerIndex = (AnyMap) parent.Get(sortable); + if (innerIndex == null) { + return; + } + _next.Remove(theEvent, innerIndex); + if (innerIndex.IsEmpty()) { + parent.Remove(sortable); + } + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeIndexLookup.cs b/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeIndexLookup.cs new file mode 100755 index 000000000..c0fd57a97 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeIndexLookup.cs @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; + +namespace com.espertech.esper.epl.join.exec.composite +{ + public interface CompositeIndexLookup + { + void Lookup(IDictionary parent, ICollection result, CompositeIndexQueryResultPostProcessor postProcessor); + void SetNext(CompositeIndexLookup value); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeIndexLookupFactory.cs b/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeIndexLookupFactory.cs new file mode 100755 index 000000000..8b68dd006 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeIndexLookupFactory.cs @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.epl.join.exec.@base; + +namespace com.espertech.esper.epl.join.exec.composite +{ + public class CompositeIndexLookupFactory + { + public static CompositeIndexLookup Make(Object[] keyValues, IList rangeValues, IList rangeCoercion) + { + // construct chain + var queries = new List(); + if (keyValues != null && keyValues.Length > 0) { + queries.Add(new CompositeIndexLookupKeyed(keyValues)); + } + for (int i = 0; i < rangeValues.Count; i++) { + queries.Add(new CompositeIndexLookupRange(rangeValues[i], rangeCoercion[i])); + } + + // Hook up as chain for remove + CompositeIndexLookup last = null; + foreach (CompositeIndexLookup action in queries) { + if (last != null) { + last.SetNext(action); + } + last = action; + } + return queries[0]; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeIndexLookupKeyed.cs b/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeIndexLookupKeyed.cs new file mode 100755 index 000000000..92586dec4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeIndexLookupKeyed.cs @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.epl.join.exec.composite +{ + using DataMap = IDictionary; + + public class CompositeIndexLookupKeyed + : CompositeIndexLookup + { + private readonly Object[] _keys; + private CompositeIndexLookup _next; + + public CompositeIndexLookupKeyed(Object[] keys) { + _keys = keys; + } + + public void SetNext(CompositeIndexLookup value) + { + _next = value; + } + + public void Lookup(IDictionary parent, ICollection result, CompositeIndexQueryResultPostProcessor postProcessor) + { + var mk = new MultiKeyUntyped(_keys); + var innerIndex = (IDictionary)parent.Get(mk); + if (innerIndex == null) { + return; + } + _next.Lookup(innerIndex, result, postProcessor); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeIndexLookupRange.cs b/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeIndexLookupRange.cs new file mode 100755 index 000000000..1b087f753 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeIndexLookupRange.cs @@ -0,0 +1,300 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.@join.exec.@base; +using com.espertech.esper.epl.@join.plan; +using com.espertech.esper.events; +using com.espertech.esper.filter; + +namespace com.espertech.esper.epl.join.exec.composite +{ + public class CompositeIndexLookupRange : CompositeIndexLookup + { + private readonly RangeIndexLookupValue _lookupValue; + private readonly Type _coercionType; + private CompositeIndexLookup _next; + + public CompositeIndexLookupRange(RangeIndexLookupValue lookupValue, Type coercionType) + { + _lookupValue = lookupValue; + _coercionType = coercionType; + } + + public void Lookup( + IDictionary parent, + ICollection result, + CompositeIndexQueryResultPostProcessor postProcessor) + { + if (_lookupValue is RangeIndexLookupValueEquals) + { + var equals = (RangeIndexLookupValueEquals) _lookupValue; + var inner = parent.Get(equals.Value); + if (_next == null) + { + result.AddAll((ICollection) inner); + } + else + { + var innerMap = (IDictionary) inner; + _next.Lookup(innerMap, result, postProcessor); + } + return; + } + + var lookup = (RangeIndexLookupValueRange) _lookupValue; + var treeMap = (OrderedDictionary) parent; + var rangeValue = lookup.Value; + switch (lookup.Operator) + { + case QueryGraphRangeEnum.RANGE_CLOSED: + { + var range = (Range) rangeValue; + LookupRange(result, treeMap, range.LowEndpoint, true, range.HighEndpoint, true, true, postProcessor); + } + break; + case QueryGraphRangeEnum.RANGE_HALF_OPEN: + { + var range = (Range) rangeValue; + LookupRange( + result, treeMap, range.LowEndpoint, true, range.HighEndpoint, false, true, postProcessor); + } + break; + case QueryGraphRangeEnum.RANGE_HALF_CLOSED: + { + var range = (Range) rangeValue; + LookupRange( + result, treeMap, range.LowEndpoint, false, range.HighEndpoint, true, true, postProcessor); + } + break; + case QueryGraphRangeEnum.RANGE_OPEN: + { + var range = (Range) rangeValue; + LookupRange( + result, treeMap, range.LowEndpoint, false, range.HighEndpoint, false, true, postProcessor); + } + break; + case QueryGraphRangeEnum.NOT_RANGE_CLOSED: + { + var range = (Range) rangeValue; + LookupRangeInverted( + result, treeMap, range.LowEndpoint, true, range.HighEndpoint, true, postProcessor); + } + break; + case QueryGraphRangeEnum.NOT_RANGE_HALF_OPEN: + { + var range = (Range) rangeValue; + LookupRangeInverted( + result, treeMap, range.LowEndpoint, true, range.HighEndpoint, false, postProcessor); + } + break; + case QueryGraphRangeEnum.NOT_RANGE_HALF_CLOSED: + { + var range = (Range) rangeValue; + LookupRangeInverted( + result, treeMap, range.LowEndpoint, false, range.HighEndpoint, true, postProcessor); + } + break; + case QueryGraphRangeEnum.NOT_RANGE_OPEN: + { + var range = (Range) rangeValue; + LookupRangeInverted( + result, treeMap, range.LowEndpoint, false, range.HighEndpoint, false, postProcessor); + } + break; + case QueryGraphRangeEnum.GREATER: + LookupGreater(result, treeMap, rangeValue, postProcessor); + break; + case QueryGraphRangeEnum.GREATER_OR_EQUAL: + LookupGreaterEqual(result, treeMap, rangeValue, postProcessor); + break; + case QueryGraphRangeEnum.LESS: + LookupLess(result, treeMap, rangeValue, postProcessor); + break; + case QueryGraphRangeEnum.LESS_OR_EQUAL: + LookupLessEqual(result, treeMap, rangeValue, postProcessor); + break; + default: + throw new ArgumentException("Unrecognized operator '" + lookup.Operator + "'"); + } + } + + public void LookupRange( + ICollection result, + OrderedDictionary propertyIndex, + object keyStart, + bool includeStart, + object keyEnd, + bool includeEnd, + bool allowRangeReversal, + CompositeIndexQueryResultPostProcessor postProcessor) + { + if (keyStart == null || keyEnd == null) + { + return; + } + keyStart = Coerce(keyStart); + keyEnd = Coerce(keyEnd); + IEnumerable> submap; + try + { + submap = propertyIndex.Between(keyStart, includeStart, keyEnd, includeEnd); + } + catch (ArgumentException) + { + if (allowRangeReversal) + { + submap = propertyIndex.Between(keyEnd, includeStart, keyStart, includeEnd); + } + else + { + return; + } + } + Normalize(result, submap, postProcessor); + } + + public void LookupRangeInverted( + ICollection result, + OrderedDictionary propertyIndex, + object keyStart, + bool includeStart, + object keyEnd, + bool includeEnd, + CompositeIndexQueryResultPostProcessor postProcessor) + { + if (keyStart == null || keyEnd == null) + { + return; + } + keyStart = Coerce(keyStart); + keyEnd = Coerce(keyEnd); + var submapOne = propertyIndex.Head(keyStart, !includeStart); + var submapTwo = propertyIndex.Tail(keyEnd, !includeEnd); + Normalize(result, submapOne, submapTwo, postProcessor); + } + + public void LookupLess( + ICollection result, + OrderedDictionary propertyIndex, + object keyStart, + CompositeIndexQueryResultPostProcessor postProcessor) + { + if (keyStart == null) + { + return; + } + keyStart = Coerce(keyStart); + Normalize(result, propertyIndex.Head(keyStart), postProcessor); + } + + public void LookupLessEqual( + ICollection result, + OrderedDictionary propertyIndex, + object keyStart, + CompositeIndexQueryResultPostProcessor postProcessor) + { + if (keyStart == null) + { + return; + } + keyStart = Coerce(keyStart); + Normalize(result, propertyIndex.Head(keyStart, true), postProcessor); + } + + public void LookupGreaterEqual( + ICollection result, + OrderedDictionary propertyIndex, + object keyStart, + CompositeIndexQueryResultPostProcessor postProcessor) + { + if (keyStart == null) + { + return; + } + keyStart = Coerce(keyStart); + Normalize(result, propertyIndex.Tail(keyStart), postProcessor); + } + + public void LookupGreater( + ICollection result, + OrderedDictionary propertyIndex, + object keyStart, + CompositeIndexQueryResultPostProcessor postProcessor) + { + if (keyStart == null) + { + return; + } + keyStart = Coerce(keyStart); + Normalize(result, propertyIndex.Tail(keyStart, false), postProcessor); + } + + private object Coerce(object key) + { + return EventBeanUtility.Coerce(key, _coercionType); + } + + private void Normalize( + ICollection result, + IEnumerable> submap, + CompositeIndexQueryResultPostProcessor postProcessor) + { + if (!submap.Any()) + { + return; + } + if (_next == null) + { + if (postProcessor != null) + { + foreach (var entry in submap) + { + postProcessor.Add(entry.Value, result); + } + } + else + { + foreach (var entry in submap) + { + var set = (ISet) entry.Value; + result.AddAll(set); + } + } + } + else + { + foreach (var entry in submap) + { + var index = (OrderedDictionary) entry.Value; + _next.Lookup(index, result, postProcessor); + } + } + } + + private void Normalize( + ICollection result, + IEnumerable> submapOne, + IEnumerable> submapTwo, + CompositeIndexQueryResultPostProcessor postProcessor) + { + Normalize(result, submapTwo, postProcessor); + Normalize(result, submapOne, postProcessor); + } + + public void SetNext(CompositeIndexLookup next) + { + _next = next; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeIndexQuery.cs b/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeIndexQuery.cs new file mode 100755 index 000000000..854cf0a51 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeIndexQuery.cs @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.join.exec.composite +{ + using Map = IDictionary; + + public interface CompositeIndexQuery + { + void Add(EventBean theEvent, Map value, ICollection result, CompositeIndexQueryResultPostProcessor postProcessor); + void Add(EventBean[] eventsPerStream, Map value, ICollection result, CompositeIndexQueryResultPostProcessor postProcessor); + ICollection Get(EventBean theEvent, Map parent, ExprEvaluatorContext context, CompositeIndexQueryResultPostProcessor postProcessor); + ICollection Get(EventBean[] eventsPerStream, Map parent, ExprEvaluatorContext context, CompositeIndexQueryResultPostProcessor postProcessor); + ICollection GetCollectKeys(EventBean theEvent, Map parent, ExprEvaluatorContext context, IList keys, CompositeIndexQueryResultPostProcessor postProcessor); + ICollection GetCollectKeys(EventBean[] eventsPerStream, Map parent, ExprEvaluatorContext context, IList keys, CompositeIndexQueryResultPostProcessor postProcessor); + void SetNext(CompositeIndexQuery value); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeIndexQueryFactory.cs b/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeIndexQueryFactory.cs new file mode 100755 index 000000000..96ad255e8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeIndexQueryFactory.cs @@ -0,0 +1,96 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.join.plan; +using com.espertech.esper.epl.lookup; + +namespace com.espertech.esper.epl.join.exec.composite +{ + public class CompositeIndexQueryFactory + { + public static CompositeIndexQuery MakeSubordinate( + bool isNWOnTrigger, + int numOuterStreams, + ICollection keyExpr, + Type[] coercionKeyTypes, + ICollection rangeProps, + Type[] rangeCoercionTypes, + IList expressionTexts) + { + // construct chain + IList queries = new List(); + if (keyExpr.Count > 0) { + IList hashKeys = new List(); + foreach (SubordPropHashKey keyExp in keyExpr) { + expressionTexts.Add(ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(keyExp.HashKey.KeyExpr)); + hashKeys.Add(keyExp.HashKey); + } + queries.Add(new CompositeIndexQueryKeyed(isNWOnTrigger, -1, numOuterStreams, hashKeys, coercionKeyTypes)); + } + int count = 0; + foreach (SubordPropRangeKey rangeProp in rangeProps) { + Type coercionType = rangeCoercionTypes == null ? null : rangeCoercionTypes[count]; + queries.Add(new CompositeIndexQueryRange(isNWOnTrigger, -1, numOuterStreams, rangeProp, coercionType, expressionTexts)); + count++; + } + + // Hook up as chain for remove + CompositeIndexQuery last = null; + foreach (CompositeIndexQuery action in queries) { + if (last != null) + { + last.SetNext(action); + } + last = action; + } + return queries[0]; + } + + public static CompositeIndexQuery MakeJoinSingleLookupStream( + bool isNWOnTrigger, + int lookupStream, + IList hashKeys, + IList keyCoercionTypes, + IList rangeProps, + IList rangeCoercionTypes) + { + // construct chain + IList queries = new List(); + if (hashKeys.Count > 0) + { + queries.Add(new CompositeIndexQueryKeyed(false, lookupStream, -1, hashKeys, keyCoercionTypes)); + } + int count = 0; + foreach (QueryGraphValueEntryRange rangeProp in rangeProps) + { + Type coercionType = rangeCoercionTypes == null ? null : rangeCoercionTypes[count]; + SubordPropRangeKey rkey = new SubordPropRangeKey(rangeProp, coercionType); + queries.Add( + new CompositeIndexQueryRange(isNWOnTrigger, lookupStream, -1, rkey, coercionType, new List())); + count++; + } + + // Hook up as chain for remove + CompositeIndexQuery last = null; + foreach (CompositeIndexQuery action in queries) + { + if (last != null) + { + last.SetNext(action); + } + last = action; + } + return queries[0]; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeIndexQueryKeyed.cs b/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeIndexQueryKeyed.cs new file mode 100755 index 000000000..7214ea488 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeIndexQueryKeyed.cs @@ -0,0 +1,167 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.@join.plan; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.join.exec.composite +{ + using DataMap = IDictionary; + + public class CompositeIndexQueryKeyed : CompositeIndexQuery + { + private readonly ExprEvaluator[] _evaluators; + private readonly IList _keyCoercionTypes; + private readonly int _lookupStream; + private readonly EventBean[] _events; + private readonly bool _isNWOnTrigger; + + private CompositeIndexQuery _next; + + public CompositeIndexQueryKeyed( + bool isNWOnTrigger, + int lookupStream, + int numStreams, + IList hashKeys, + IList keyCoercionTypes) + { + _keyCoercionTypes = keyCoercionTypes; + _evaluators = new ExprEvaluator[hashKeys.Count]; + _isNWOnTrigger = isNWOnTrigger; + _lookupStream = lookupStream; + + for (var i = 0; i < _evaluators.Length; i++) + { + _evaluators[i] = hashKeys[i].KeyExpr.ExprEvaluator; + } + if (lookupStream != -1) + { + _events = new EventBean[lookupStream + 1]; + } + else + { + _events = new EventBean[numStreams + 1]; + } + } + + public void SetNext(CompositeIndexQuery next) + { + _next = next; + } + + public ICollection Get( + EventBean theEvent, + DataMap parent, + ExprEvaluatorContext context, + CompositeIndexQueryResultPostProcessor postProcessor) + { + _events[_lookupStream] = theEvent; + var mk = EventBeanUtility.GetMultiKey(_events, _evaluators, context, _keyCoercionTypes); + var innerIndex = (DataMap) parent.Get(mk); + if (innerIndex == null) + { + return null; + } + return _next.Get(theEvent, innerIndex, context, postProcessor); + } + + public ICollection GetCollectKeys( + EventBean theEvent, + DataMap parent, + ExprEvaluatorContext context, + IList keys, + CompositeIndexQueryResultPostProcessor postProcessor) + { + _events[_lookupStream] = theEvent; + var mk = EventBeanUtility.GetMultiKey(_events, _evaluators, context, _keyCoercionTypes); + keys.AddAll(mk.Keys); + var innerIndex = (DataMap) parent.Get(mk); + if (innerIndex == null) + { + return null; + } + return _next.GetCollectKeys(theEvent, innerIndex, context, keys, postProcessor); + } + + public ICollection Get( + EventBean[] eventsPerStream, + DataMap parent, + ExprEvaluatorContext context, + CompositeIndexQueryResultPostProcessor postProcessor) + { + EventBean[] eventsToUse; + if (_isNWOnTrigger) + { + eventsToUse = eventsPerStream; + } + else + { + Array.Copy(eventsPerStream, 0, _events, 1, eventsPerStream.Length); + eventsToUse = _events; + } + + var mk = EventBeanUtility.GetMultiKey(eventsToUse, _evaluators, context, _keyCoercionTypes); + var innerIndex = (DataMap) parent.Get(mk); + if (innerIndex == null) + { + return null; + } + return _next.Get(eventsPerStream, innerIndex, context, postProcessor); + } + + public ICollection GetCollectKeys( + EventBean[] eventsPerStream, + DataMap parent, + ExprEvaluatorContext context, + IList keys, + CompositeIndexQueryResultPostProcessor postProcessor) + { + EventBean[] eventsToUse; + if (_isNWOnTrigger) + { + eventsToUse = eventsPerStream; + } + else + { + Array.Copy(eventsPerStream, 0, _events, 1, eventsPerStream.Length); + eventsToUse = _events; + } + + var mk = EventBeanUtility.GetMultiKey(eventsToUse, _evaluators, context, _keyCoercionTypes); + keys.AddAll(mk.Keys); + var innerIndex = (DataMap) parent.Get(mk); + if (innerIndex == null) + { + return null; + } + return _next.GetCollectKeys(eventsPerStream, innerIndex, context, keys, postProcessor); + } + + public void Add(EventBean theEvent, DataMap value, ICollection result, CompositeIndexQueryResultPostProcessor postProcessor) + { + throw new UnsupportedOperationException(); + } + + public void Add( + EventBean[] eventsPerStream, + DataMap value, + ICollection result, + CompositeIndexQueryResultPostProcessor postProcessor) + { + throw new UnsupportedOperationException(); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeIndexQueryRange.cs b/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeIndexQueryRange.cs new file mode 100755 index 000000000..bc9f21384 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeIndexQueryRange.cs @@ -0,0 +1,276 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.join.plan; +using com.espertech.esper.epl.lookup; + +namespace com.espertech.esper.epl.join.exec.composite +{ + public class CompositeIndexQueryRange : CompositeIndexQuery + { + private readonly CompositeAccessStrategy _strategy; + private CompositeIndexQuery _next; + + public CompositeIndexQueryRange( + bool isNWOnTrigger, + int lookupStream, + int numStreams, + SubordPropRangeKey subqRangeKey, + Type coercionType, + IList expressionTexts) + { + var rangeProp = subqRangeKey.RangeInfo; + + if (rangeProp.RangeType.IsRange()) + { + var rangeIn = (QueryGraphValueEntryRangeIn) rangeProp; + var start = rangeIn.ExprStart.ExprEvaluator; + expressionTexts.Add(rangeIn.ExprStart.ToExpressionStringMinPrecedenceSafe()); + var includeStart = rangeProp.RangeType.IsIncludeStart(); + + var end = rangeIn.ExprEnd.ExprEvaluator; + expressionTexts.Add(rangeIn.ExprEnd.ToExpressionStringMinPrecedenceSafe()); + var includeEnd = rangeProp.RangeType.IsIncludeEnd(); + + if (!rangeProp.RangeType.IsRangeInverted()) + { + _strategy = new CompositeAccessStrategyRangeNormal( + isNWOnTrigger, lookupStream, numStreams, start, includeStart, end, includeEnd, coercionType, + ((QueryGraphValueEntryRangeIn) rangeProp).IsAllowRangeReversal); + } + else + { + _strategy = new CompositeAccessStrategyRangeInverted( + isNWOnTrigger, lookupStream, numStreams, start, includeStart, end, includeEnd, coercionType); + } + } + else + { + var relOp = (QueryGraphValueEntryRangeRelOp) rangeProp; + var key = relOp.Expression.ExprEvaluator; + expressionTexts.Add(relOp.Expression.ToExpressionStringMinPrecedenceSafe()); + switch (rangeProp.RangeType) + { + case QueryGraphRangeEnum.GREATER_OR_EQUAL: + _strategy = new CompositeAccessStrategyGE( + isNWOnTrigger, lookupStream, numStreams, key, coercionType); + break; + case QueryGraphRangeEnum.GREATER: + _strategy = new CompositeAccessStrategyGT( + isNWOnTrigger, lookupStream, numStreams, key, coercionType); + break; + case QueryGraphRangeEnum.LESS_OR_EQUAL: + _strategy = new CompositeAccessStrategyLE( + isNWOnTrigger, lookupStream, numStreams, key, coercionType); + break; + case QueryGraphRangeEnum.LESS: + _strategy = new CompositeAccessStrategyLT( + isNWOnTrigger, lookupStream, numStreams, key, coercionType); + break; + default: + throw new ArgumentException("Comparison operator " + rangeProp.RangeType + " not supported"); + } + } + } + + public void Add( + EventBean theEvent, + IDictionary parent, + ICollection result, + CompositeIndexQueryResultPostProcessor postProcessor) + { + _strategy.Lookup(theEvent, parent, result, _next, null, null, postProcessor); + } + + public void Add( + EventBean[] eventsPerStream, + IDictionary parent, + ICollection result, + CompositeIndexQueryResultPostProcessor postProcessor) + { + _strategy.Lookup(eventsPerStream, parent, result, _next, null, null, postProcessor); + } + + public ICollection Get( + EventBean theEvent, + IDictionary parent, + ExprEvaluatorContext context, + CompositeIndexQueryResultPostProcessor postProcessor) + { + return _strategy.Lookup(theEvent, parent, null, _next, context, null, postProcessor); + } + + public ICollection Get( + EventBean[] eventsPerStream, + IDictionary parent, + ExprEvaluatorContext context, + CompositeIndexQueryResultPostProcessor postProcessor) + { + return _strategy.Lookup(eventsPerStream, parent, null, _next, context, null, postProcessor); + } + + public ICollection GetCollectKeys( + EventBean theEvent, + IDictionary parent, + ExprEvaluatorContext context, + IList keys, + CompositeIndexQueryResultPostProcessor postProcessor) + { + return _strategy.Lookup(theEvent, parent, null, _next, context, keys, postProcessor); + } + + public ICollection GetCollectKeys( + EventBean[] eventsPerStream, + IDictionary parent, + ExprEvaluatorContext context, + IList keys, + CompositeIndexQueryResultPostProcessor postProcessor) + { + return _strategy.Lookup(eventsPerStream, parent, null, _next, context, keys, postProcessor); + } + + protected internal static ICollection Handle( + EventBean theEvent, + IDictionary sortedMapOne, + IDictionary sortedMapTwo, + ICollection result, + CompositeIndexQuery next, + CompositeIndexQueryResultPostProcessor postProcessor) + { + if (next == null) + { + if (result == null) + { + result = new HashSet(); + } + AddResults( + sortedMapOne != null ? + sortedMapOne.Select(entry => new KeyValuePair>(entry.Key, entry.Value as ICollection)) : + null, + sortedMapTwo != null ? + sortedMapTwo.Select(entry => new KeyValuePair>(entry.Key, entry.Value as ICollection)) : + null, + result, + postProcessor); + return result; + } + else + { + if (result == null) + { + result = new HashSet(); + } + foreach (var entry in sortedMapOne) + { + next.Add(theEvent, entry.Value as IDictionary, result, postProcessor); + } + if (sortedMapTwo != null) + { + foreach (var entry in sortedMapTwo) + { + next.Add(theEvent, entry.Value as IDictionary, result, postProcessor); + } + } + return result; + } + } + + protected internal static ICollection Handle( + EventBean[] eventsPerStream, + IDictionary sortedMapOne, + IDictionary sortedMapTwo, + ICollection result, + CompositeIndexQuery next, + CompositeIndexQueryResultPostProcessor postProcessor) + { + if (next == null) + { + if (result == null) + { + result = new HashSet(); + } + AddResults( + sortedMapOne != null ? + sortedMapOne.Select(entry => new KeyValuePair>(entry.Key, entry.Value as ICollection)) : + null, + sortedMapTwo != null ? + sortedMapTwo.Select(entry => new KeyValuePair>(entry.Key, entry.Value as ICollection)) : + null, + result, + postProcessor); + + return result; + } + else + { + if (result == null) + { + result = new HashSet(); + } + foreach (var entry in sortedMapOne) + { + next.Add(eventsPerStream, entry.Value as IDictionary, result, postProcessor); + } + if (sortedMapTwo != null) + { + foreach (var entry in sortedMapTwo) + { + next.Add(eventsPerStream, entry.Value as IDictionary, result, postProcessor); + } + } + return result; + } + } + + private static void AddResults( + IEnumerable>> sortedMapOne, + IEnumerable>> sortedMapTwo, + ICollection result, + CompositeIndexQueryResultPostProcessor postProcessor) + { + AddResults(sortedMapOne, result, postProcessor); + if (sortedMapTwo != null) + { + AddResults(sortedMapTwo, result, postProcessor); + } + } + + private static void AddResults( + IEnumerable>> sortedMapOne, + ICollection result, + CompositeIndexQueryResultPostProcessor postProcessor) + { + if (postProcessor != null) + { + foreach (var entry in sortedMapOne) + { + postProcessor.Add(entry.Value, result); + } + } + else + { + foreach (var entry in sortedMapOne) + { + result.AddAll(entry.Value); + } + } + } + + public void SetNext(CompositeIndexQuery next) + { + _next = next; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeIndexQueryResultPostProcessor.cs b/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeIndexQueryResultPostProcessor.cs new file mode 100755 index 000000000..a596b2485 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/exec/composite/CompositeIndexQueryResultPostProcessor.cs @@ -0,0 +1,19 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; + +namespace com.espertech.esper.epl.join.exec.composite +{ + public interface CompositeIndexQueryResultPostProcessor + { + void Add(object value, ICollection result); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/join/exec/sorted/SortedAccessStrategy.cs b/NEsper.Core/NEsper.Core/epl/join/exec/sorted/SortedAccessStrategy.cs new file mode 100755 index 000000000..0fc256b91 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/exec/sorted/SortedAccessStrategy.cs @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.join.table; + +namespace com.espertech.esper.epl.join.exec.sorted +{ + public interface SortedAccessStrategy + { + ICollection Lookup(EventBean theEvent, PropertySortedEventTable index, ExprEvaluatorContext context); + ICollection LookupCollectKeys(EventBean theEvent, PropertySortedEventTable index, ExprEvaluatorContext context, IList keys); + ICollection Lookup(EventBean[] eventsPerStream, PropertySortedEventTable index, ExprEvaluatorContext context); + ICollection LookupCollectKeys(EventBean[] eventsPerStream, PropertySortedEventTable index, ExprEvaluatorContext context, IList keys); + String ToQueryPlan(); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/exec/sorted/SortedAccessStrategyFactory.cs b/NEsper.Core/NEsper.Core/epl/join/exec/sorted/SortedAccessStrategyFactory.cs new file mode 100755 index 000000000..64742b338 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/exec/sorted/SortedAccessStrategyFactory.cs @@ -0,0 +1,74 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.join.plan; +using com.espertech.esper.epl.lookup; + +namespace com.espertech.esper.epl.join.exec.sorted +{ + public class SortedAccessStrategyFactory + { + public static SortedAccessStrategy Make(bool isNWOnTrigger, int lookupStream, int numStreams, QueryGraphValueEntryRange rangeKeyPair, Type coercionType) + { + return Make(isNWOnTrigger, lookupStream, numStreams, new SubordPropRangeKey(rangeKeyPair, coercionType)); + } + + public static SortedAccessStrategy Make(bool isNWOnTrigger, int lookupStream, int numStreams, SubordPropRangeKey streamRangeKey) + { + var rangeKeyPair = streamRangeKey.RangeInfo; + + if (rangeKeyPair.RangeType.IsRange()) { + var rangeIn = (QueryGraphValueEntryRangeIn)rangeKeyPair; + var startExpr = rangeIn.ExprStart.ExprEvaluator; + var endExpr = rangeIn.ExprEnd.ExprEvaluator; + var includeStart = rangeKeyPair.RangeType.IsIncludeStart(); + + var includeEnd = rangeKeyPair.RangeType.IsIncludeEnd(); + if (!rangeKeyPair.RangeType.IsRangeInverted()) { + return new SortedAccessStrategyRange( + isNWOnTrigger, + lookupStream, + numStreams, + startExpr, + includeStart, + endExpr, + includeEnd, + rangeIn.IsAllowRangeReversal); + } + return new SortedAccessStrategyRangeInverted( + isNWOnTrigger, + lookupStream, + numStreams, + startExpr, + includeStart, + endExpr, + includeEnd); + } + var relOp = (QueryGraphValueEntryRangeRelOp) rangeKeyPair; + var keyExpr = relOp.Expression.ExprEvaluator; + if (rangeKeyPair.RangeType == QueryGraphRangeEnum.GREATER_OR_EQUAL) { + return new SortedAccessStrategyGE(isNWOnTrigger, lookupStream, numStreams, keyExpr); + } + if (rangeKeyPair.RangeType == QueryGraphRangeEnum.GREATER) + { + return new SortedAccessStrategyGT(isNWOnTrigger, lookupStream, numStreams, keyExpr); + } + if (rangeKeyPair.RangeType == QueryGraphRangeEnum.LESS_OR_EQUAL) + { + return new SortedAccessStrategyLE(isNWOnTrigger, lookupStream, numStreams, keyExpr); + } + if (rangeKeyPair.RangeType == QueryGraphRangeEnum.LESS) + { + return new SortedAccessStrategyLT(isNWOnTrigger, lookupStream, numStreams, keyExpr); + } + throw new ArgumentException("Comparison operator " + rangeKeyPair.RangeType + " not supported"); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/exec/sorted/SortedAccessStrategyGE.cs b/NEsper.Core/NEsper.Core/epl/join/exec/sorted/SortedAccessStrategyGE.cs new file mode 100755 index 000000000..854ccee61 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/exec/sorted/SortedAccessStrategyGE.cs @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.join.table; + +namespace com.espertech.esper.epl.join.exec.sorted +{ + public class SortedAccessStrategyGE : SortedAccessStrategyRelOpBase, SortedAccessStrategy + { + public SortedAccessStrategyGE(bool isNWOnTrigger, int lookupStream, int numStreams, ExprEvaluator keyEval) + : base(isNWOnTrigger, lookupStream, numStreams, keyEval) + { + } + + public ICollection Lookup(EventBean theEvent, PropertySortedEventTable index, ExprEvaluatorContext context) + { + return index.LookupGreaterEqual(base.EvaluateLookup(theEvent, context)); + } + + public ICollection LookupCollectKeys(EventBean theEvent, PropertySortedEventTable index, ExprEvaluatorContext context, IList keys) + { + Object point = base.EvaluateLookup(theEvent, context); + keys.Add(point); + return index.LookupGreaterEqual(point); + } + + public ICollection Lookup(EventBean[] eventsPerStream, PropertySortedEventTable index, ExprEvaluatorContext context) + { + return index.LookupGreaterEqualColl(base.EvaluatePerStream(eventsPerStream, context)); + } + + public ICollection LookupCollectKeys(EventBean[] eventsPerStream, PropertySortedEventTable index, ExprEvaluatorContext context, IList keys) + { + Object point = base.EvaluatePerStream(eventsPerStream, context); + keys.Add(point); + return index.LookupGreaterEqualColl(point); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/exec/sorted/SortedAccessStrategyGT.cs b/NEsper.Core/NEsper.Core/epl/join/exec/sorted/SortedAccessStrategyGT.cs new file mode 100755 index 000000000..ba6abfc91 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/exec/sorted/SortedAccessStrategyGT.cs @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.join.table; + +namespace com.espertech.esper.epl.join.exec.sorted +{ + public class SortedAccessStrategyGT : SortedAccessStrategyRelOpBase, SortedAccessStrategy + { + public SortedAccessStrategyGT(bool isNWOnTrigger, int lookupStream, int numStreams, ExprEvaluator keyEval) + : base(isNWOnTrigger, lookupStream, numStreams, keyEval) + { + } + + public ICollection Lookup(EventBean theEvent, PropertySortedEventTable index, ExprEvaluatorContext context) + { + return index.LookupGreater(base.EvaluateLookup(theEvent, context)); + } + + public ICollection LookupCollectKeys(EventBean theEvent, PropertySortedEventTable index, ExprEvaluatorContext context, IList keys) + { + Object point = base.EvaluateLookup(theEvent, context); + keys.Add(point); + return index.LookupGreater(point); + } + + public ICollection Lookup(EventBean[] eventsPerStream, PropertySortedEventTable index, ExprEvaluatorContext context) + { + return index.LookupGreaterColl(base.EvaluatePerStream(eventsPerStream, context)); + } + + public ICollection LookupCollectKeys(EventBean[] eventsPerStream, PropertySortedEventTable index, ExprEvaluatorContext context, IList keys) + { + Object point = base.EvaluatePerStream(eventsPerStream, context); + keys.Add(point); + return index.LookupGreaterColl(point); + } + } + +} diff --git a/NEsper.Core/NEsper.Core/epl/join/exec/sorted/SortedAccessStrategyLE.cs b/NEsper.Core/NEsper.Core/epl/join/exec/sorted/SortedAccessStrategyLE.cs new file mode 100755 index 000000000..732f2e83d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/exec/sorted/SortedAccessStrategyLE.cs @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.@join.table; + +namespace com.espertech.esper.epl.join.exec.sorted +{ + public class SortedAccessStrategyLE : SortedAccessStrategyRelOpBase, SortedAccessStrategy + { + public SortedAccessStrategyLE(bool isNWOnTrigger, int lookupStream, int numStreams, ExprEvaluator keyEval) + : base(isNWOnTrigger, lookupStream, numStreams, keyEval) + { + } + + public ICollection Lookup(EventBean theEvent, PropertySortedEventTable index, ExprEvaluatorContext context) + { + return index.LookupLessEqual(base.EvaluateLookup(theEvent, context)); + } + + public ICollection LookupCollectKeys(EventBean theEvent, PropertySortedEventTable index, ExprEvaluatorContext context, IList keys) + { + Object point = base.EvaluateLookup(theEvent, context); + keys.Add(point); + return index.LookupLessEqual(point); + } + + public ICollection Lookup(EventBean[] eventsPerStream, PropertySortedEventTable index, ExprEvaluatorContext context) + { + return index.LookupLessEqualColl(base.EvaluatePerStream(eventsPerStream, context)); + } + + public ICollection LookupCollectKeys(EventBean[] eventsPerStream, PropertySortedEventTable index, ExprEvaluatorContext context, IList keys) + { + Object point = base.EvaluatePerStream(eventsPerStream, context); + keys.Add(point); + return index.LookupLessEqualColl(point); + } + } + +} diff --git a/NEsper.Core/NEsper.Core/epl/join/exec/sorted/SortedAccessStrategyLT.cs b/NEsper.Core/NEsper.Core/epl/join/exec/sorted/SortedAccessStrategyLT.cs new file mode 100755 index 000000000..f4f17069d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/exec/sorted/SortedAccessStrategyLT.cs @@ -0,0 +1,49 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.join.table; + +namespace com.espertech.esper.epl.join.exec.sorted +{ + public class SortedAccessStrategyLT : SortedAccessStrategyRelOpBase, SortedAccessStrategy + { + public SortedAccessStrategyLT(bool isNWOnTrigger, int lookupStream, int numStreams, ExprEvaluator keyEval) + : base(isNWOnTrigger, lookupStream, numStreams, keyEval) + { + } + + public ICollection Lookup(EventBean theEvent, PropertySortedEventTable index, ExprEvaluatorContext context) { + return index.LookupLess(base.EvaluateLookup(theEvent, context)); + } + + public ICollection LookupCollectKeys(EventBean theEvent, PropertySortedEventTable index, ExprEvaluatorContext context, IList keys) + { + Object point = base.EvaluateLookup(theEvent, context); + keys.Add(point); + return index.LookupLess(point); + } + + public ICollection Lookup(EventBean[] eventsPerStream, PropertySortedEventTable index, ExprEvaluatorContext context) + { + return index.LookupLessThenColl(base.EvaluatePerStream(eventsPerStream, context)); + } + + public ICollection LookupCollectKeys(EventBean[] eventsPerStream, PropertySortedEventTable index, ExprEvaluatorContext context, IList keys) + { + Object point = base.EvaluatePerStream(eventsPerStream, context); + keys.Add(point); + return index.LookupLessThenColl(point); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/exec/sorted/SortedAccessStrategyRange.cs b/NEsper.Core/NEsper.Core/epl/join/exec/sorted/SortedAccessStrategyRange.cs new file mode 100755 index 000000000..b617d3a18 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/exec/sorted/SortedAccessStrategyRange.cs @@ -0,0 +1,58 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.join.table; + +namespace com.espertech.esper.epl.join.exec.sorted +{ + public class SortedAccessStrategyRange : SortedAccessStrategyRangeBase, SortedAccessStrategy + { + // indicate whether "a between 60 and 50" should return no results (false, equivalent to a>= X and a <=Y) or should return results (true, equivalent to 'between' and 'in') + private readonly bool allowRangeReversal; + + public SortedAccessStrategyRange(bool isNWOnTrigger, int lookupStream, int numStreams, ExprEvaluator start, bool includeStart, ExprEvaluator end, bool includeEnd, bool allowRangeReversal) + : base(isNWOnTrigger, lookupStream, numStreams, start, includeStart, end, includeEnd) + { + this.allowRangeReversal = allowRangeReversal; + } + + public ICollection Lookup(EventBean theEvent, PropertySortedEventTable index, ExprEvaluatorContext context) + { + return index.LookupRange(base.EvaluateLookupStart(theEvent, context), IncludeStart, base.EvaluateLookupEnd(theEvent, context), IncludeEnd, allowRangeReversal); + } + + public ICollection LookupCollectKeys(EventBean theEvent, PropertySortedEventTable index, ExprEvaluatorContext context, IList keys) + { + Object start = base.EvaluateLookupStart(theEvent, context); + keys.Add(start); + Object end = base.EvaluateLookupEnd(theEvent, context); + keys.Add(end); + return index.LookupRange(start, IncludeStart, end, IncludeEnd, allowRangeReversal); + } + + public ICollection Lookup(EventBean[] eventsPerStream, PropertySortedEventTable index, ExprEvaluatorContext context) + { + return index.LookupRangeColl(base.EvaluatePerStreamStart(eventsPerStream, context), IncludeStart, base.EvaluatePerStreamEnd(eventsPerStream, context), IncludeEnd, allowRangeReversal); + } + + public ICollection LookupCollectKeys(EventBean[] eventsPerStream, PropertySortedEventTable index, ExprEvaluatorContext context, IList keys) + { + Object start = base.EvaluatePerStreamStart(eventsPerStream, context); + keys.Add(start); + Object end = base.EvaluatePerStreamEnd(eventsPerStream, context); + keys.Add(end); + return index.LookupRangeColl(start, IncludeStart, end, IncludeEnd, allowRangeReversal); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/exec/sorted/SortedAccessStrategyRangeBase.cs b/NEsper.Core/NEsper.Core/epl/join/exec/sorted/SortedAccessStrategyRangeBase.cs new file mode 100755 index 000000000..53803598f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/exec/sorted/SortedAccessStrategyRangeBase.cs @@ -0,0 +1,82 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.join.exec.sorted +{ + public abstract class SortedAccessStrategyRangeBase + { + protected ExprEvaluator Start; + protected bool IncludeStart; + protected ExprEvaluator End; + protected bool IncludeEnd; + + private readonly bool _isNWOnTrigger; + private readonly EventBean[] _events; + private readonly int _lookupStream; + + protected SortedAccessStrategyRangeBase(bool isNWOnTrigger, int lookupStream, int numStreams, ExprEvaluator start, bool includeStart, ExprEvaluator end, bool includeEnd) + { + Start = start; + IncludeStart = includeStart; + End = end; + IncludeEnd = includeEnd; + _isNWOnTrigger = isNWOnTrigger; + + _lookupStream = lookupStream; + if (lookupStream != -1) { + _events = new EventBean[lookupStream + 1]; + } + else { + _events = new EventBean[numStreams + 1]; + } + } + + public Object EvaluateLookupStart(EventBean theEvent, ExprEvaluatorContext context) { + _events[_lookupStream] = theEvent; + return Start.Evaluate(new EvaluateParams(_events, true, context)); + } + + public Object EvaluateLookupEnd(EventBean theEvent, ExprEvaluatorContext context) { + _events[_lookupStream] = theEvent; + return End.Evaluate(new EvaluateParams(_events, true, context)); + } + + public Object EvaluatePerStreamStart(EventBean[] eventsPerStream, ExprEvaluatorContext context) { + if (_isNWOnTrigger) { + return Start.Evaluate(new EvaluateParams(eventsPerStream, true, context)); + } + else { + Array.Copy(eventsPerStream, 0, _events, 1, eventsPerStream.Length); + return Start.Evaluate(new EvaluateParams(_events, true, context)); + } + } + + public Object EvaluatePerStreamEnd(EventBean[] eventsPerStream, ExprEvaluatorContext context) { + if (_isNWOnTrigger) { + return End.Evaluate(new EvaluateParams(eventsPerStream, true, context)); + } + else { + Array.Copy(eventsPerStream, 0, _events, 1, eventsPerStream.Length); + return End.Evaluate(new EvaluateParams(_events, true, context)); + } + } + + public String ToQueryPlan() { + return GetType().FullName + " start=" + Start.GetType().Name + + ", includeStart=" + IncludeStart + + ", end=" + End.GetType().Name + + ", includeEnd=" + IncludeEnd; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/exec/sorted/SortedAccessStrategyRangeInverted.cs b/NEsper.Core/NEsper.Core/epl/join/exec/sorted/SortedAccessStrategyRangeInverted.cs new file mode 100755 index 000000000..18d391524 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/exec/sorted/SortedAccessStrategyRangeInverted.cs @@ -0,0 +1,55 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.join.table; + +namespace com.espertech.esper.epl.join.exec.sorted +{ + public class SortedAccessStrategyRangeInverted : SortedAccessStrategyRangeBase, SortedAccessStrategy + { + public SortedAccessStrategyRangeInverted(bool isNWOnTrigger, int lookupStream, int numStreams, ExprEvaluator start, bool includeStart, ExprEvaluator end, bool includeEnd) + : base(isNWOnTrigger, lookupStream, numStreams, start, includeStart, end, includeEnd) + { + } + + public ICollection Lookup(EventBean theEvent, PropertySortedEventTable index, ExprEvaluatorContext context) + { + return index.LookupRangeInverted(base.EvaluateLookupStart(theEvent, context), IncludeStart, base.EvaluateLookupEnd(theEvent, context), IncludeEnd); + } + + public ICollection LookupCollectKeys(EventBean theEvent, PropertySortedEventTable index, ExprEvaluatorContext context, IList keys) + { + Object start = base.EvaluateLookupStart(theEvent, context); + keys.Add(start); + Object end = base.EvaluateLookupEnd(theEvent, context); + keys.Add(end); + return index.LookupRangeInverted(start, IncludeStart, end, IncludeEnd); + } + + public ICollection Lookup(EventBean[] eventsPerStream, PropertySortedEventTable index, ExprEvaluatorContext context) + { + return index.LookupRangeInvertedColl(base.EvaluatePerStreamStart(eventsPerStream, context), IncludeStart, base.EvaluatePerStreamEnd(eventsPerStream, context), IncludeEnd); + } + + public ICollection LookupCollectKeys(EventBean[] eventsPerStream, PropertySortedEventTable index, ExprEvaluatorContext context, IList keys) + { + Object start = base.EvaluatePerStreamStart(eventsPerStream, context); + keys.Add(start); + Object end = base.EvaluatePerStreamEnd(eventsPerStream, context); + keys.Add(end); + return index.LookupRangeInvertedColl(start, IncludeStart, end, IncludeEnd); + } + } + +} diff --git a/NEsper.Core/NEsper.Core/epl/join/exec/sorted/SortedAccessStrategyRelOpBase.cs b/NEsper.Core/NEsper.Core/epl/join/exec/sorted/SortedAccessStrategyRelOpBase.cs new file mode 100755 index 000000000..54eea8a00 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/exec/sorted/SortedAccessStrategyRelOpBase.cs @@ -0,0 +1,56 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.join.exec.sorted +{ + public abstract class SortedAccessStrategyRelOpBase + { + private readonly ExprEvaluator keyEval; + private readonly EventBean[] events; + private readonly int lookupStream; + private readonly bool isNWOnTrigger; + + protected SortedAccessStrategyRelOpBase(bool isNWOnTrigger, int lookupStream, int numStreams, ExprEvaluator keyEval) + { + this.lookupStream = lookupStream; + this.keyEval = keyEval; + this.isNWOnTrigger = isNWOnTrigger; + if (lookupStream != -1) { + events = new EventBean[lookupStream + 1]; + } + else { + events = new EventBean[numStreams + 1]; + } + } + + public Object EvaluateLookup(EventBean theEvent, ExprEvaluatorContext context) { + events[lookupStream] = theEvent; + return keyEval.Evaluate(new EvaluateParams(events, true, context)); + } + + public Object EvaluatePerStream(EventBean[] eventsPerStream, ExprEvaluatorContext context) { + if (isNWOnTrigger) { + return keyEval.Evaluate(new EvaluateParams(eventsPerStream, true, context)); + } + else { + Array.Copy(eventsPerStream, 0, events, 1, eventsPerStream.Length); + return keyEval.Evaluate(new EvaluateParams(events, true, context)); + } + } + + public String ToQueryPlan() { + return this.GetType().FullName + " key " + keyEval.GetType().Name; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/hint/ExcludePlanFilterOperatorType.cs b/NEsper.Core/NEsper.Core/epl/join/hint/ExcludePlanFilterOperatorType.cs new file mode 100755 index 000000000..54d72a5b3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/hint/ExcludePlanFilterOperatorType.cs @@ -0,0 +1,17 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.join.hint +{ + public enum ExcludePlanFilterOperatorType + { + RELOP, + INKW, + EQUALS + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/hint/ExcludePlanHint.cs b/NEsper.Core/NEsper.Core/epl/join/hint/ExcludePlanHint.cs new file mode 100755 index 000000000..8a94f0837 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/hint/ExcludePlanHint.cs @@ -0,0 +1,95 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.client.annotation; +using com.espertech.esper.compat; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.join.hint +{ + public class ExcludePlanHint + { + private static readonly ILog QUERY_PLAN_LOG = LogManager.GetLogger(AuditPath.QUERYPLAN_LOG); + + private readonly String[] _streamNames; + private readonly IList _evaluators; + private readonly ExprEvaluatorContext _exprEvaluatorContext; + private readonly bool _queryPlanLogging; + + public ExcludePlanHint(String[] streamNames, IList evaluators, StatementContext statementContext) + { + _streamNames = streamNames; + _evaluators = evaluators; + _exprEvaluatorContext = new ExprEvaluatorContextStatement(statementContext, false); + _queryPlanLogging = statementContext.ConfigSnapshot.EngineDefaults.Logging.IsEnableQueryPlan; + } + + public static ExcludePlanHint GetHint(String[] streamNames, StatementContext statementContext) + { + IList hints = HintEnum.EXCLUDE_PLAN.GetHintAssignedValues(statementContext.Annotations); + if (hints == null) + { + return null; + } + IList filters = new List(); + foreach (String hint in hints) + { + if (string.IsNullOrWhiteSpace(hint)) + { + continue; + } + ExprEvaluator evaluator = ExcludePlanHintExprUtil.ToExpression(hint, statementContext); + if (TypeHelper.GetBoxedType(evaluator.ReturnType) != typeof(bool?)) + { + throw new ExprValidationException("Expression provided for hint " + HintEnum.EXCLUDE_PLAN.GetValue() + " must return a boolean value"); + } + filters.Add(evaluator); + } + return new ExcludePlanHint(streamNames, filters, statementContext); + } + + public bool Filter(int streamLookup, int streamIndexed, ExcludePlanFilterOperatorType opType, params ExprNode[] exprNodes) + { + EventBean @event = ExcludePlanHintExprUtil.ToEvent(streamLookup, + streamIndexed, _streamNames[streamLookup], _streamNames[streamIndexed], + opType.GetName().ToLower(), exprNodes); + if (_queryPlanLogging && QUERY_PLAN_LOG.IsInfoEnabled) + { + QUERY_PLAN_LOG.Info("Exclude-plan-hint combination " + EventBeanUtility.PrintEvent(@event)); + } + EventBean[] eventsPerStream = new EventBean[] { @event }; + + var evaluateParams = new EvaluateParams(eventsPerStream, true, _exprEvaluatorContext); + foreach (ExprEvaluator evaluator in _evaluators) + { + var pass = evaluator.Evaluate(evaluateParams); + if (pass != null && true.Equals(pass)) + { + if (_queryPlanLogging && QUERY_PLAN_LOG.IsInfoEnabled) + { + QUERY_PLAN_LOG.Info("Exclude-plan-hint combination : true"); + } + return true; + } + } + if (_queryPlanLogging && QUERY_PLAN_LOG.IsInfoEnabled) + { + QUERY_PLAN_LOG.Info("Exclude-plan-hint combination : false"); + } + return false; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/hint/ExcludePlanHintExprUtil.cs b/NEsper.Core/NEsper.Core/epl/join/hint/ExcludePlanHintExprUtil.cs new file mode 100755 index 000000000..1b41e6f4d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/hint/ExcludePlanHintExprUtil.cs @@ -0,0 +1,74 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.mgr; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.declexpr; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.events; +using com.espertech.esper.events.arr; +using com.espertech.esper.pattern; + +namespace com.espertech.esper.epl.join.hint +{ + public class ExcludePlanHintExprUtil { + + internal static readonly ObjectArrayEventType OAEXPRESSIONTYPE; + + static ExcludePlanHintExprUtil() + { + var properties = new Dictionary(); + properties.Put("from_streamnum", typeof(int)); + properties.Put("to_streamnum", typeof(int)); + properties.Put("from_streamname", typeof(string)); + properties.Put("to_streamname", typeof(string)); + properties.Put("opname", typeof(string)); + properties.Put("exprs", typeof(string[])); + OAEXPRESSIONTYPE = + new ObjectArrayEventType( + EventTypeMetadata.CreateAnonymous(typeof (ExcludePlanHintExprUtil).Name, ApplicationType.OBJECTARR), + typeof (ExcludePlanHintExprUtil).Name, 0, null, properties, null, null, null); + } + + public static EventBean ToEvent(int fromStreamnum, + int toStreamnum, + string fromStreamname, + string toStreamname, + string opname, + ExprNode[] expressions) { + var texts = new string[expressions.Length]; + for (var i = 0; i < expressions.Length; i++) { + texts[i] = ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(expressions[i]); + } + var @event = new Object[]{fromStreamnum, toStreamnum, fromStreamname, toStreamname, opname, texts}; + return new ObjectArrayEventBean(@event, OAEXPRESSIONTYPE); + } + + public static ExprEvaluator ToExpression(string hint, StatementContext statementContext) { + var toCompile = "select * from com.esper.espertech.compat.DateTimeOffsetHelper#TimeInMillis(" + hint + ")"; + var raw = EPAdministratorHelper.CompileEPL(toCompile, hint, false, null, + SelectClauseStreamSelectorEnum.ISTREAM_ONLY, statementContext.EngineImportService, + statementContext.VariableService, statementContext.SchedulingService, + statementContext.EngineURI, statementContext.ConfigSnapshot, + new PatternNodeFactoryImpl(), new ContextManagementServiceImpl(), + new ExprDeclaredServiceImpl(), new TableServiceImpl()); + var expr = raw.StreamSpecs[0].ViewSpecs[0].ObjectParameters[0]; + var validated = ExprNodeUtility.ValidateSimpleGetSubtree(ExprNodeOrigin.HINT, expr, statementContext, OAEXPRESSIONTYPE, false); + return validated.ExprEvaluator; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/join/hint/IndexHint.cs b/NEsper.Core/NEsper.Core/epl/join/hint/IndexHint.cs new file mode 100755 index 000000000..626efe5c1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/hint/IndexHint.cs @@ -0,0 +1,175 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.client.annotation; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.epl.join.hint +{ + public class IndexHint + { + private readonly List _pairs; + + public IndexHint(List pairs) + { + _pairs = pairs; + } + + public static IndexHint GetIndexHint(Attribute[] annotations) + { + IList hints = HintEnum.INDEX.GetHintAssignedValues(annotations); + if (hints == null) + { + return null; + } + + var pairs = new List(); + foreach (String hint in hints) + { + String[] hintAtoms = HintEnumExtensions.SplitCommaUnlessInParen(hint); + var selectors = new List(); + var instructions = new List(); + for (int i = 0; i < hintAtoms.Length; i++) + { + String hintAtom = hintAtoms[i]; + if (hintAtom.ToLower().Trim() == "bust") + { + instructions.Add(new IndexHintInstructionBust()); + } + else if (hintAtom.ToLower().Trim() == "explicit") + { + instructions.Add(new IndexHintInstructionExplicit()); + } + else if (CheckValueInParen("subquery", hintAtom.ToLower())) + { + int subqueryNum = ExtractValueParen(hintAtom); + selectors.Add(new IndexHintSelectorSubquery(subqueryNum)); + } + else + { + instructions.Add(new IndexHintInstructionIndexName(hintAtom.Trim())); + } + } + pairs.Add(new SelectorInstructionPair(selectors, instructions)); + } + return new IndexHint(pairs); + } + + public IList GetInstructionsSubquery(int subqueryNumber) + { + foreach (SelectorInstructionPair pair in _pairs) + { + if (pair.Selector.IsEmpty()) + { + // empty selector mean hint applies to all + return pair.Instructions; + } + foreach (IndexHintSelector selector in pair.Selector) + { + if (selector.MatchesSubquery(subqueryNumber)) + { + return pair.Instructions; + } + } + } + return Collections.GetEmptyList(); + } + + public IList InstructionsFireAndForget + { + get + { + foreach (SelectorInstructionPair pair in _pairs) + { + if (pair.Selector.IsEmpty()) + { + // empty selector mean hint applies to all + return pair.Instructions; + } + } + return Collections.GetEmptyList(); + } + } + + private static bool CheckValueInParen(String type, String value) + { + int indexOpen = value.IndexOf('('); + if (indexOpen != -1) + { + String noparen = value.Substring(0, indexOpen).Trim().ToLower(); + if (type.Equals(noparen)) + { + return true; + } + } + return false; + } + + internal static bool CheckValueAssignment(String type, String value) + { + int indexEquals = value.IndexOf('='); + if (indexEquals != -1) + { + var noequals = value.Substring(0, indexEquals).Trim().ToLower(); + if (type.Equals(noequals)) + { + return true; + } + } + return false; + } + + private static int ExtractValueParen(String text) + { + int indexOpen = text.IndexOf('('); + int indexClosed = text.LastIndexOf(')'); + if (indexOpen != -1) + { + string value = text.Substring(indexOpen + 1, indexClosed - indexOpen - 1).Trim(); + try + { + return int.Parse(value); + } + catch (Exception) + { + throw new EPException("Failed to parse '" + value + "' as an index hint integer value"); + } + } + + throw new IllegalStateException("Not a parentheses value"); + } + + internal static Object ExtractValueEqualsStringOrInt(String text) + { + String value = ExtractValueEquals(text); + try + { + return int.Parse(value); + } + catch (Exception) + { + return value; + } + } + + internal static String ExtractValueEquals(String text) + { + int indexEquals = text.IndexOf('='); + if (indexEquals != -1) + { + return text.Substring(indexEquals + 1).Trim(); + } + throw new IllegalStateException("Not a parentheses value"); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/join/hint/IndexHintInstruction.cs b/NEsper.Core/NEsper.Core/epl/join/hint/IndexHintInstruction.cs new file mode 100755 index 000000000..401a4fb47 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/hint/IndexHintInstruction.cs @@ -0,0 +1,14 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.join.hint +{ + public interface IndexHintInstruction + { + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/hint/IndexHintInstructionBust.cs b/NEsper.Core/NEsper.Core/epl/join/hint/IndexHintInstructionBust.cs new file mode 100755 index 000000000..e5b7d82ff --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/hint/IndexHintInstructionBust.cs @@ -0,0 +1,14 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.join.hint +{ + public class IndexHintInstructionBust : IndexHintInstruction + { + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/join/hint/IndexHintInstructionExplicit.cs b/NEsper.Core/NEsper.Core/epl/join/hint/IndexHintInstructionExplicit.cs new file mode 100755 index 000000000..ac3dbee08 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/hint/IndexHintInstructionExplicit.cs @@ -0,0 +1,14 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.join.hint +{ + public class IndexHintInstructionExplicit : IndexHintInstruction + { + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/join/hint/IndexHintInstructionIndexName.cs b/NEsper.Core/NEsper.Core/epl/join/hint/IndexHintInstructionIndexName.cs new file mode 100755 index 000000000..3050fa4d5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/hint/IndexHintInstructionIndexName.cs @@ -0,0 +1,22 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.join.hint +{ + public class IndexHintInstructionIndexName : IndexHintInstruction + { + public IndexHintInstructionIndexName(String indexName) + { + IndexName = indexName; + } + + public string IndexName { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/join/hint/IndexHintSelector.cs b/NEsper.Core/NEsper.Core/epl/join/hint/IndexHintSelector.cs new file mode 100755 index 000000000..0f7cc6452 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/hint/IndexHintSelector.cs @@ -0,0 +1,15 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.join.hint +{ + public interface IndexHintSelector + { + bool MatchesSubquery(int subqueryNumber); + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/join/hint/IndexHintSelectorSubquery.cs b/NEsper.Core/NEsper.Core/epl/join/hint/IndexHintSelectorSubquery.cs new file mode 100755 index 000000000..51672823b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/hint/IndexHintSelectorSubquery.cs @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.join.hint +{ + public class IndexHintSelectorSubquery : IndexHintSelector + { + public IndexHintSelectorSubquery(int subqueryNum) + { + SubqueryNum = subqueryNum; + } + + public int SubqueryNum { get; private set; } + + #region IndexHintSelector Members + + public bool MatchesSubquery(int subqueryNumber) + { + return SubqueryNum == subqueryNumber; + } + + #endregion + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/join/hint/SelectorInstructionPair.cs b/NEsper.Core/NEsper.Core/epl/join/hint/SelectorInstructionPair.cs new file mode 100755 index 000000000..53af82fdf --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/hint/SelectorInstructionPair.cs @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +namespace com.espertech.esper.epl.join.hint +{ + public class SelectorInstructionPair + { + public SelectorInstructionPair(IList selector, IList instructions) + { + Selector = selector; + Instructions = instructions; + } + + public IList Selector { get; private set; } + + public IList Instructions { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/join/plan/CoercionDesc.cs b/NEsper.Core/NEsper.Core/epl/join/plan/CoercionDesc.cs new file mode 100755 index 000000000..8e487b972 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/plan/CoercionDesc.cs @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.join.plan +{ + public class CoercionDesc + { + public CoercionDesc(bool coerce, Type[] coercionTypes) + { + IsCoerce = coerce; + CoercionTypes = coercionTypes; + } + + public bool IsCoerce { get; private set; } + + public Type[] CoercionTypes { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/plan/CoercionUtil.cs b/NEsper.Core/NEsper.Core/epl/join/plan/CoercionUtil.cs new file mode 100755 index 000000000..aed93035e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/plan/CoercionUtil.cs @@ -0,0 +1,212 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.join.plan +{ + public class CoercionUtil { + + private static readonly Type[] NULL_ARRAY = new Type[0]; + + public static CoercionDesc GetCoercionTypesRange(EventType[] typesPerStream, int indexedStream, IList indexedProp, IList rangeEntries) + { + if (rangeEntries.IsEmpty()) { + return new CoercionDesc(false, NULL_ARRAY); + } + + var coercionTypes = new Type[rangeEntries.Count]; + bool mustCoerce = false; + for (int i = 0; i < rangeEntries.Count; i++) + { + QueryGraphValueEntryRange entry = rangeEntries[i]; + + String indexed = indexedProp[i]; + Type valuePropType = typesPerStream[indexedStream].GetPropertyType(indexed).GetBoxedType(); + Type coercionType; + + if (entry.RangeType.IsRange()) { + var rangeIn = (QueryGraphValueEntryRangeIn) entry; + coercionType = GetCoercionTypeRangeIn(valuePropType, rangeIn.ExprStart, rangeIn.ExprEnd); + } + else { + var relOp = (QueryGraphValueEntryRangeRelOp) entry; + coercionType = GetCoercionType(valuePropType, relOp.Expression.ExprEvaluator.ReturnType); + } + + if (coercionType == null) { + coercionTypes[i] = valuePropType; + } + else { + mustCoerce = true; + coercionTypes[i] = coercionType; + } + } + + return new CoercionDesc(mustCoerce, coercionTypes); + } + + /// Returns null if no coercion is required, or an array of classes for use in coercing the lookup keys and index keys into a common type. + /// is the event types for each stream + /// is the stream looked up from + /// is the indexed stream + /// is the properties to use to look up + /// is the properties to index on + /// coercion types, or null if none required + public static CoercionDesc GetCoercionTypesHash(EventType[] typesPerStream, + int lookupStream, + int indexedStream, + IList keyProps, + IList indexProps) + { + if (indexProps.Count == 0 && keyProps.Count == 0) { + return new CoercionDesc(false, NULL_ARRAY); + } + if (indexProps.Count != keyProps.Count) + { + throw new IllegalStateException("Mismatch in the number of key and index properties"); + } + + var coercionTypes = new Type[indexProps.Count]; + bool mustCoerce = false; + for (int i = 0; i < keyProps.Count; i++) + { + Type keyPropType; + if (keyProps[i] is QueryGraphValueEntryHashKeyedExpr) + { + var hashExpr = (QueryGraphValueEntryHashKeyedExpr) keyProps[i]; + keyPropType = hashExpr.KeyExpr.ExprEvaluator.ReturnType; + } + else + { + var hashKeyProp = (QueryGraphValueEntryHashKeyedProp) keyProps[i]; + keyPropType = typesPerStream[lookupStream].GetPropertyType(hashKeyProp.KeyProperty).GetBoxedType(); + } + + Type indexedPropType = typesPerStream[indexedStream].GetPropertyType(indexProps[i]).GetBoxedType(); + Type coercionType = indexedPropType; + if (keyPropType != indexedPropType) + { + coercionType = keyPropType.GetCompareToCoercionType(indexedPropType); + mustCoerce = true; + } + coercionTypes[i] = coercionType; + } + return new CoercionDesc(mustCoerce, coercionTypes); + } + + public static Type GetCoercionTypeRange(EventType indexedType, String indexedProp, SubordPropRangeKey rangeKey) { + QueryGraphValueEntryRange desc = rangeKey.RangeInfo; + if (desc.RangeType.IsRange()) { + var rangeIn = (QueryGraphValueEntryRangeIn) desc; + return GetCoercionTypeRangeIn(indexedType.GetPropertyType(indexedProp), rangeIn.ExprStart, rangeIn.ExprEnd); + } + + var relOp = (QueryGraphValueEntryRangeRelOp) desc; + return GetCoercionType(indexedType.GetPropertyType(indexedProp), relOp.Expression.ExprEvaluator.ReturnType); + } + + public static CoercionDesc GetCoercionTypesRange(EventType viewableEventType, IDictionary rangeProps, EventType[] typesPerStream) { + if (rangeProps.IsEmpty()) { + return new CoercionDesc(false, NULL_ARRAY); + } + + var coercionTypes = new Type[rangeProps.Count]; + bool mustCoerce = false; + int count = 0; + foreach (KeyValuePair entry in rangeProps) + { + SubordPropRangeKey subQRange = entry.Value; + QueryGraphValueEntryRange rangeDesc = entry.Value.RangeInfo; + + Type valuePropType = viewableEventType.GetPropertyType(entry.Key).GetBoxedType(); + Type coercionType; + + if (rangeDesc.RangeType.IsRange()) { + var rangeIn = (QueryGraphValueEntryRangeIn) rangeDesc; + coercionType = GetCoercionTypeRangeIn(valuePropType, rangeIn.ExprStart, rangeIn.ExprEnd); + } + else { + var relOp = (QueryGraphValueEntryRangeRelOp) rangeDesc; + coercionType = GetCoercionType(valuePropType, relOp.Expression.ExprEvaluator.ReturnType); + } + + if (coercionType == null) { + coercionTypes[count++] = valuePropType; + } + else { + mustCoerce = true; + coercionTypes[count++] = coercionType; + } + } + return new CoercionDesc(mustCoerce, coercionTypes); + } + + private static Type GetCoercionType(Type valuePropType, Type keyPropTypeExpr) + { + Type coercionType = null; + Type keyPropType = keyPropTypeExpr.GetBoxedType(); + if (valuePropType != keyPropType) + { + coercionType = valuePropType.GetCompareToCoercionType(keyPropType); + } + return coercionType; + } + + public static CoercionDesc GetCoercionTypesHash(EventType viewableEventType, String[] indexProps, IList hashKeys) { + if (indexProps.Length == 0 && hashKeys.Count == 0) { + return new CoercionDesc(false, NULL_ARRAY); + } + if (indexProps.Length != hashKeys.Count) { + throw new IllegalStateException("Mismatch in the number of key and index properties"); + } + + var coercionTypes = new Type[indexProps.Length]; + bool mustCoerce = false; + for (int i = 0; i < hashKeys.Count; i++) + { + Type keyPropType = hashKeys[i].HashKey.KeyExpr.ExprEvaluator.ReturnType.GetBoxedType(); + Type indexedPropType = viewableEventType.GetPropertyType(indexProps[i]).GetBoxedType(); + Type coercionType = indexedPropType; + if (keyPropType != indexedPropType) + { + coercionType = keyPropType.GetCompareToCoercionType(indexedPropType); + mustCoerce = true; + } + coercionTypes[i] = coercionType; + } + return new CoercionDesc(mustCoerce, coercionTypes); + } + + public static Type GetCoercionTypeRangeIn(Type valuePropType, ExprNode exprStart, ExprNode exprEnd) + { + Type coercionType = null; + Type startPropType = exprStart.ExprEvaluator.ReturnType.GetBoxedType(); + Type endPropType = exprEnd.ExprEvaluator.ReturnType.GetBoxedType(); + + if (valuePropType != startPropType) + { + coercionType = valuePropType.GetCompareToCoercionType(startPropType); + } + if (valuePropType != endPropType) + { + coercionType = coercionType.GetCompareToCoercionType(endPropType); + } + return coercionType; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/plan/CompositeTableLookupPlan.cs b/NEsper.Core/NEsper.Core/epl/join/plan/CompositeTableLookupPlan.cs new file mode 100755 index 000000000..dd07a71b3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/plan/CompositeTableLookupPlan.cs @@ -0,0 +1,61 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.join.exec.@base; +using com.espertech.esper.epl.join.table; + +namespace com.espertech.esper.epl.join.plan +{ + /// + /// Plan to perform an indexed table lookup. + /// + public class CompositeTableLookupPlan : TableLookupPlan + { + private readonly IList _hashKeys; + private readonly IList _rangeKeyPairs; + + /// + /// Ctor. + /// + /// stream that generates event to look up for + /// stream to index table lookup + /// index number for the table containing the full unindexed contents + /// The hash keys. + /// The range key pairs. + public CompositeTableLookupPlan(int lookupStream, int indexedStream, TableLookupIndexReqKey indexNum, IList hashKeys, IList rangeKeyPairs) + : base(lookupStream, indexedStream, new TableLookupIndexReqKey[] { indexNum }) + { + _hashKeys = hashKeys; + _rangeKeyPairs = rangeKeyPairs; + } + + public override TableLookupKeyDesc KeyDescriptor + { + get { return new TableLookupKeyDesc(_hashKeys, _rangeKeyPairs); } + } + + public override JoinExecTableLookupStrategy MakeStrategyInternal(EventTable[] eventTable, EventType[] eventTypes) + { + var index = (PropertyCompositeEventTable)eventTable[0]; + return new CompositeTableLookupStrategy(eventTypes[LookupStream], LookupStream, _hashKeys, _rangeKeyPairs, index); + } + + public override String ToString() + { + return string.Format("CompositeTableLookupPlan {0} directKeys={1} rangeKeys={2}", + base.ToString(), + _hashKeys.Render(), + _rangeKeyPairs.Render()); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/plan/FilterExprAnalyzer.cs b/NEsper.Core/NEsper.Core/epl/join/plan/FilterExprAnalyzer.cs new file mode 100755 index 000000000..56c097713 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/plan/FilterExprAnalyzer.cs @@ -0,0 +1,396 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.dot; +using com.espertech.esper.epl.expression.ops; +using com.espertech.esper.epl.join.util; +using com.espertech.esper.filter; +using com.espertech.esper.type; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.join.plan +{ + /// + /// Analyzes a filter expression and builds a query graph model. The 'equals', 'and', + /// 'between' and relational operators expressions in the filter expression are extracted + /// and placed in the query graph model as navigable relationships (by key and index + /// properties as well as ranges) between streams. + /// + public class FilterExprAnalyzer + { + /// + /// Analyzes filter expression to build query graph model. + /// + /// filter top node + /// model containing relationships between streams, to be written to + /// if set to true [is outer join]. + public static void Analyze(ExprNode topNode, QueryGraph queryGraph, bool isOuterJoin) + { + // Analyze relationships between streams. Relationships are properties in AND and EQUALS nodes of joins. + if (topNode is ExprEqualsNode) + { + var equalsNode = (ExprEqualsNode)topNode; + if (!equalsNode.IsNotEquals) + { + AnalyzeEqualsNode(equalsNode, queryGraph, isOuterJoin); + } + } + else if (topNode is ExprAndNode) + { + var andNode = (ExprAndNode)topNode; + AnalyzeAndNode(andNode, queryGraph, isOuterJoin); + } + else if (topNode is ExprBetweenNode) + { + var betweenNode = (ExprBetweenNode)topNode; + AnalyzeBetweenNode(betweenNode, queryGraph); + } + else if (topNode is ExprRelationalOpNode) + { + var relNode = (ExprRelationalOpNode)topNode; + AnalyzeRelationalOpNode(relNode, queryGraph); + } + else if (topNode is ExprDotNode && !isOuterJoin) + { + var dotNode = (ExprDotNode)topNode; + AnalyzeDotNode(dotNode, queryGraph); + } + else if (topNode is ExprInNode) + { + ExprInNode inNode = (ExprInNode)topNode; + AnalyzeInNode(inNode, queryGraph); + } + else if (topNode is ExprOrNode) + { + ExprNode rewritten = FilterSpecCompilerMakeParamUtil.RewriteOrToInIfApplicable(topNode); + if (rewritten is ExprInNode) + { + var inNode = (ExprInNode)rewritten; + AnalyzeInNode(inNode, queryGraph); + } + } + } + + private static void AnalyzeInNode(ExprInNode inNode, QueryGraph queryGraph) + { + if (inNode.IsNotIn) + { + return; + } + + // direction of lookup is value-set (keys) to single-expression (single index) + AnalyzeInNodeSingleIndex(inNode, queryGraph); + + // direction of lookup is single-expression (key) to value-set (multi index) + AnalyzeInNodeMultiIndex(inNode, queryGraph); + } + + private static void AnalyzeInNodeMultiIndex(ExprInNode inNode, QueryGraph queryGraph) + { + ExprNode[] setExpressions = GetInNodeSetExpressions(inNode); + if (setExpressions.Length == 0) + { + return; + } + + var perStreamExprs = new Dictionary>(); + foreach (ExprNode exprNodeSet in setExpressions) + { + if (!(exprNodeSet is ExprIdentNode)) + { + continue; + } + var setIdent = (ExprIdentNode)exprNodeSet; + AddToList(setIdent.StreamId, setIdent, perStreamExprs); + } + if (perStreamExprs.IsEmpty()) + { + return; + } + + var testExpr = inNode.ChildNodes[0]; + var testExprType = testExpr.ExprEvaluator.ReturnType.GetBoxedType(); + if (perStreamExprs.Count > 1) + { + return; + } + var entry = perStreamExprs.First(); + ExprNode[] exprNodes = ExprNodeUtility.ToArray(entry.Value); + foreach (ExprNode node in exprNodes) + { + var exprType = node.ExprEvaluator.ReturnType; + if (exprType.GetBoxedType() != testExprType) + { + return; + } + } + + int? testStreamNum; + int setStream = entry.Key.Value; + if (!(testExpr is ExprIdentNode)) + { + EligibilityDesc eligibility = EligibilityUtil.VerifyInputStream(testExpr, setStream); + if (!eligibility.Eligibility.IsEligible()) + { + return; + } + if (eligibility.Eligibility == Eligibility.REQUIRE_ONE && setStream == eligibility.StreamNum) + { + return; + } + testStreamNum = eligibility.StreamNum; + } + else + { + testStreamNum = ((ExprIdentNode)testExpr).StreamId; + } + + if (testStreamNum == null) + { + queryGraph.AddInSetMultiIndexUnkeyed(testExpr, setStream, exprNodes); + } + else + { + if (testStreamNum.Equals(entry.Key)) + { + return; + } + queryGraph.AddInSetMultiIndex(testStreamNum.Value, testExpr, setStream, exprNodes); + } + } + + private static void AnalyzeInNodeSingleIndex(ExprInNode inNode, QueryGraph queryGraph) + { + if (!(inNode.ChildNodes[0] is ExprIdentNode)) + { + return; + } + var testIdent = (ExprIdentNode)inNode.ChildNodes[0]; + var testIdentClass = TypeHelper.GetBoxedType(testIdent.ExprEvaluator.ReturnType); + int indexedStream = testIdent.StreamId; + + ExprNode[] setExpressions = GetInNodeSetExpressions(inNode); + if (setExpressions.Length == 0) + { + return; + } + + var perStreamExprs = new LinkedHashMap>(); + + foreach (ExprNode exprNodeSet in setExpressions) + { + if (exprNodeSet.ExprEvaluator.ReturnType.GetBoxedType() != testIdentClass) + { + continue; + } + if (exprNodeSet is ExprIdentNode) + { + var setIdent = (ExprIdentNode)exprNodeSet; + AddToList(setIdent.StreamId, setIdent, perStreamExprs); + } + else + { + EligibilityDesc eligibility = EligibilityUtil.VerifyInputStream(exprNodeSet, indexedStream); + if (!eligibility.Eligibility.IsEligible()) + { + continue; + } + AddToList(eligibility.StreamNum, exprNodeSet, perStreamExprs); + } + } + + foreach (var entry in perStreamExprs) + { + ExprNode[] exprNodes = ExprNodeUtility.ToArray(entry.Value); + if (entry.Key == null) + { + queryGraph.AddInSetSingleIndexUnkeyed(testIdent.StreamId, testIdent, exprNodes); + continue; + } + if (entry.Key.Value != indexedStream) + { + queryGraph.AddInSetSingleIndex(testIdent.StreamId, testIdent, entry.Key.Value, exprNodes); + } + } + } + + private static void AddToList(int? streamIdAllowNull, ExprNode expr, IDictionary> perStreamExpression) + { + var perStream = perStreamExpression.Get(streamIdAllowNull); + if (perStream == null) + { + perStream = new List(); + perStreamExpression.Put(streamIdAllowNull, perStream); + } + perStream.Add(expr); + } + + private static ExprNode[] GetInNodeSetExpressions(ExprInNode inNode) + { + var setExpressions = new ExprNode[inNode.ChildNodes.Count - 1]; + var count = 0; + for (int i = 1; i < inNode.ChildNodes.Count; i++) + { + setExpressions[count++] = inNode.ChildNodes[i]; + } + return setExpressions; + } + + private static void AnalyzeDotNode(ExprDotNode dotNode, QueryGraph queryGraph) + { + var interval = dotNode.ExprDotNodeFilterAnalyzerDesc; + if (interval == null) + { + return; + } + interval.Apply(queryGraph); + } + + private static void AnalyzeRelationalOpNode(ExprRelationalOpNode relNode, QueryGraph queryGraph) + { + if (((relNode.ChildNodes[0] is ExprIdentNode)) && + ((relNode.ChildNodes[1] is ExprIdentNode))) + { + var identNodeLeft = (ExprIdentNode)relNode.ChildNodes[0]; + var identNodeRight = (ExprIdentNode)relNode.ChildNodes[1]; + + if (identNodeLeft.StreamId != identNodeRight.StreamId) + { + queryGraph.AddRelationalOpStrict( + identNodeLeft.StreamId, identNodeLeft, + identNodeRight.StreamId, identNodeRight, + relNode.RelationalOpEnum); + } + return; + } + + var indexedStream = -1; + ExprIdentNode indexedPropExpr = null; + ExprNode exprNodeNoIdent = null; + RelationalOpEnum relop = relNode.RelationalOpEnum; + + if (relNode.ChildNodes[0] is ExprIdentNode) + { + indexedPropExpr = (ExprIdentNode)relNode.ChildNodes[0]; + indexedStream = indexedPropExpr.StreamId; + exprNodeNoIdent = relNode.ChildNodes[1]; + } + else if (relNode.ChildNodes[1] is ExprIdentNode) + { + indexedPropExpr = (ExprIdentNode)relNode.ChildNodes[1]; + indexedStream = indexedPropExpr.StreamId; + exprNodeNoIdent = relNode.ChildNodes[0]; + relop = relop.Reversed(); + } + if (indexedStream == -1) + { + return; // require property of right/left side of equals + } + + var eligibility = EligibilityUtil.VerifyInputStream(exprNodeNoIdent, indexedStream); + if (!eligibility.Eligibility.IsEligible()) + { + return; + } + + queryGraph.AddRelationalOp(indexedStream, indexedPropExpr, eligibility.StreamNum, exprNodeNoIdent, relop); + } + + private static void AnalyzeBetweenNode(ExprBetweenNode betweenNode, QueryGraph queryGraph) + { + RangeFilterAnalyzer.Apply(betweenNode.ChildNodes[0], betweenNode.ChildNodes[1], betweenNode.ChildNodes[2], + betweenNode.IsLowEndpointIncluded, betweenNode.IsHighEndpointIncluded, betweenNode.IsNotBetween, + queryGraph); + } + + /// + /// Analye EQUALS (=) node. + /// + /// node to analyze + /// store relationships between stream properties + /// if set to true [is outer join]. + internal static void AnalyzeEqualsNode(ExprEqualsNode equalsNode, QueryGraph queryGraph, bool isOuterJoin) + { + if ((equalsNode.ChildNodes[0] is ExprIdentNode) && + (equalsNode.ChildNodes[1] is ExprIdentNode)) + { + var identNodeLeft = (ExprIdentNode)equalsNode.ChildNodes[0]; + var identNodeRight = (ExprIdentNode)equalsNode.ChildNodes[1]; + + if (identNodeLeft.StreamId != identNodeRight.StreamId) + { + queryGraph.AddStrictEquals(identNodeLeft.StreamId, identNodeLeft.ResolvedPropertyName, identNodeLeft, + identNodeRight.StreamId, identNodeRight.ResolvedPropertyName, identNodeRight); + } + + return; + } + if (isOuterJoin) + { // outerjoins don't use constants or one-way expression-derived information to evaluate join + return; + } + + // handle constant-compare or transformation case + var indexedStream = -1; + ExprIdentNode indexedPropExpr = null; + ExprNode exprNodeNoIdent = null; + + if (equalsNode.ChildNodes[0] is ExprIdentNode) + { + indexedPropExpr = (ExprIdentNode)equalsNode.ChildNodes[0]; + indexedStream = indexedPropExpr.StreamId; + exprNodeNoIdent = equalsNode.ChildNodes[1]; + } + else if (equalsNode.ChildNodes[1] is ExprIdentNode) + { + indexedPropExpr = (ExprIdentNode)equalsNode.ChildNodes[1]; + indexedStream = indexedPropExpr.StreamId; + exprNodeNoIdent = equalsNode.ChildNodes[0]; + } + if (indexedStream == -1) + { + return; // require property of right/left side of equals + } + + var eligibility = EligibilityUtil.VerifyInputStream(exprNodeNoIdent, indexedStream); + if (!eligibility.Eligibility.IsEligible()) + { + return; + } + + if (eligibility.Eligibility == Eligibility.REQUIRE_NONE) + { + queryGraph.AddUnkeyedExpression(indexedStream, indexedPropExpr, exprNodeNoIdent); + } + else + { + queryGraph.AddKeyedExpression(indexedStream, indexedPropExpr, eligibility.StreamNum.Value, exprNodeNoIdent); + } + } + + /// + /// Analyze the AND-node. + /// + /// node to analyze + /// to store relationships between stream properties + /// if set to true [is outer join]. + internal static void AnalyzeAndNode(ExprAndNode andNode, QueryGraph queryGraph, bool isOuterJoin) + { + foreach (var childNode in andNode.ChildNodes) + { + Analyze(childNode, queryGraph, isOuterJoin); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/plan/FullTableScanLookupPlan.cs b/NEsper.Core/NEsper.Core/epl/join/plan/FullTableScanLookupPlan.cs new file mode 100755 index 000000000..3e1921190 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/plan/FullTableScanLookupPlan.cs @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.join.exec.@base; +using com.espertech.esper.epl.join.table; + +namespace com.espertech.esper.epl.join.plan +{ + /// Plan for a full table scan. + public class FullTableScanLookupPlan : TableLookupPlan + { + /// Ctor. + /// stream that generates event to look up for + /// stream to full table scan + /// index number for the table containing the full unindexed contents + public FullTableScanLookupPlan(int lookupStream, int indexedStream, TableLookupIndexReqKey indexNum) + : base(lookupStream, indexedStream, new TableLookupIndexReqKey[] { indexNum }) + { + } + + public override TableLookupKeyDesc KeyDescriptor + { + get + { + return new TableLookupKeyDesc( + new List(), + new List()); + } + } + + public override JoinExecTableLookupStrategy MakeStrategyInternal(EventTable[] eventTable, EventType[] eventTypes) + { + UnindexedEventTable index = (UnindexedEventTable) eventTable[0]; + return new FullTableScanLookupStrategy(index); + } + + public override String ToString() + { + return "FullTableScanLookupPlan " + + base.ToString(); + } + + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/plan/FullTableScanUniquePerKeyLookupPlan.cs b/NEsper.Core/NEsper.Core/epl/join/plan/FullTableScanUniquePerKeyLookupPlan.cs new file mode 100755 index 000000000..1071ea1d9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/plan/FullTableScanUniquePerKeyLookupPlan.cs @@ -0,0 +1,57 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.@join.exec.@base; +using com.espertech.esper.epl.join.table; + +namespace com.espertech.esper.epl.join.plan +{ + /// + /// Plan for a full table scan. + /// + public class FullTableScanUniquePerKeyLookupPlan : TableLookupPlan + { + /// + /// Ctor. + /// + /// stream that generates event to look up for + /// stream to full table scan + /// index number for the table containing the full unindexed contents + public FullTableScanUniquePerKeyLookupPlan(int lookupStream, int indexedStream, TableLookupIndexReqKey indexNum) + : base(lookupStream, indexedStream, new TableLookupIndexReqKey[] {indexNum}) + { + } + + public override TableLookupKeyDesc KeyDescriptor + { + get + { + return new TableLookupKeyDesc( + Collections.GetEmptyList(), + Collections.GetEmptyList()); + } + } + + public override JoinExecTableLookupStrategy MakeStrategyInternal(EventTable[] eventTable, EventType[] eventTypes) + { + return new FullTableScanUniqueValueLookupStrategy((EventTableAsSet) eventTable[0]); + } + + public override string ToString() + { + return "FullTableScanLookupPlan " + base.ToString(); + } + + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/plan/HistoricalDataPlanNode.cs b/NEsper.Core/NEsper.Core/epl/join/plan/HistoricalDataPlanNode.cs new file mode 100755 index 000000000..3a859803d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/plan/HistoricalDataPlanNode.cs @@ -0,0 +1,83 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.threading; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.@join.exec.@base; +using com.espertech.esper.epl.@join.table; +using com.espertech.esper.epl.virtualdw; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.epl.join.plan +{ + /// + /// Query plan for performing a historical data lookup. + /// Translates into a particular execution for use in regular and outer joins. + /// + public class HistoricalDataPlanNode : QueryPlanNode + { + private readonly int _streamNum; + private readonly int _rootStreamNum; + private readonly int _lookupStreamNum; + private readonly int _numStreams; + private readonly ExprNode _outerJoinExprNode; + + /// + /// Ctor. + /// + /// the historical stream num + /// the stream number of the query plan providing incoming events + /// the stream that provides polling/lookup events + /// number of streams in join + /// outer join expression node or null if none defined + public HistoricalDataPlanNode(int streamNum, int rootStreamNum, int lookupStreamNum, int numStreams, ExprNode exprNode) + { + _streamNum = streamNum; + _rootStreamNum = rootStreamNum; + _lookupStreamNum = lookupStreamNum; + _numStreams = numStreams; + _outerJoinExprNode = exprNode; + } + + public override ExecNode MakeExec(string statementName, int statementId, Attribute[] annotations, IDictionary[] indexesPerStream, EventType[] streamTypes, Viewable[] streamViews, HistoricalStreamIndexList[] historicalStreamIndexLists, VirtualDWView[] viewExternal, ILockable[] tableSecondaryIndexLocks) + { + var pair = historicalStreamIndexLists[_streamNum].GetStrategy(_lookupStreamNum); + var viewable = (HistoricalEventViewable) streamViews[_streamNum]; + return new HistoricalDataExecNode(viewable, pair.Second, pair.First, _numStreams, _streamNum); + } + + public override void AddIndexes(ISet usedIndexes) { + // none to add + } + + /// + /// Returns the table lookup strategy for use in outer joins. + /// + /// all views in join + /// the stream number of the stream looking up into the historical + /// the index management for the historical stream + /// strategy + public HistoricalTableLookupStrategy MakeOuterJoinStategy(Viewable[] streamViews, int pollingStreamNum, HistoricalStreamIndexList[] historicalStreamIndexLists) + { + var pair = historicalStreamIndexLists[_streamNum].GetStrategy(pollingStreamNum); + var viewable = (HistoricalEventViewable) streamViews[_streamNum]; + return new HistoricalTableLookupStrategy(viewable, pair.Second, pair.First, _numStreams, _streamNum, _rootStreamNum, _outerJoinExprNode == null ? null : _outerJoinExprNode.ExprEvaluator); + } + + public override void Print(IndentWriter writer) + { + writer.IncrIndent(); + writer.WriteLine("HistoricalDataPlanNode streamNum=" + _streamNum); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/join/plan/InKeywordTableLookupPlanMultiIdx.cs b/NEsper.Core/NEsper.Core/epl/join/plan/InKeywordTableLookupPlanMultiIdx.cs new file mode 100755 index 000000000..ef2a3596e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/plan/InKeywordTableLookupPlanMultiIdx.cs @@ -0,0 +1,74 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.join.exec.@base; +using com.espertech.esper.epl.join.table; +using com.espertech.esper.epl.lookup; + +namespace com.espertech.esper.epl.join.plan +{ + /// + /// Plan to perform an indexed table lookup. + /// + public class InKeywordTableLookupPlanMultiIdx : TableLookupPlan + { + private readonly ExprNode _keyExpr; + + /// + /// Ctor. + /// + /// stream that generates event to look up for + /// stream to index table lookup + /// index number for the table containing the full unindexed contents + /// The key expr. + public InKeywordTableLookupPlanMultiIdx(int lookupStream, int indexedStream, TableLookupIndexReqKey[] indexNum, ExprNode keyExpr) + : base(lookupStream, indexedStream, indexNum) + { + _keyExpr = keyExpr; + } + + public ExprNode KeyExpr + { + get { return _keyExpr; } + } + + public override TableLookupKeyDesc KeyDescriptor + { + get + { + return new TableLookupKeyDesc( + Collections.GetEmptyList(), + Collections.GetEmptyList()); + } + } + + public override JoinExecTableLookupStrategy MakeStrategyInternal(EventTable[] eventTable, EventType[] eventTypes) + { + var evaluator = _keyExpr.ExprEvaluator; + var singles = new PropertyIndexedEventTableSingle[eventTable.Length]; + for (int i = 0; i < eventTable.Length; i++) + { + singles[i] = (PropertyIndexedEventTableSingle)eventTable[i]; + } + return new InKeywordMultiTableLookupStrategyExpr(evaluator, LookupStream, singles, new LookupStrategyDesc(LookupStrategyType.INKEYWORDMULTIIDX, new String[] { ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(_keyExpr) })); + } + + public override String ToString() + { + return this.GetType().Name + " " + + base.ToString() + + " keyProperties=" + ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(_keyExpr); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/plan/InKeywordTableLookupPlanSingleIdx.cs b/NEsper.Core/NEsper.Core/epl/join/plan/InKeywordTableLookupPlanSingleIdx.cs new file mode 100755 index 000000000..d60247fe4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/plan/InKeywordTableLookupPlanSingleIdx.cs @@ -0,0 +1,72 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.join.exec.@base; +using com.espertech.esper.epl.join.table; +using com.espertech.esper.epl.lookup; + +namespace com.espertech.esper.epl.join.plan +{ + /// + /// Plan to perform an indexed table lookup. + /// + public class InKeywordTableLookupPlanSingleIdx : TableLookupPlan + { + private readonly ExprNode[] _expressions; + + /// Ctor. + /// stream that generates event to look up for + /// stream to index table lookup + /// index number for the table containing the full unindexed contents + /// + public InKeywordTableLookupPlanSingleIdx(int lookupStream, int indexedStream, TableLookupIndexReqKey indexNum, ExprNode[] expressions) + : base(lookupStream, indexedStream, new TableLookupIndexReqKey[] { indexNum }) + { + _expressions = expressions; + } + + public ExprNode[] Expressions + { + get { return _expressions; } + } + + public override TableLookupKeyDesc KeyDescriptor + { + get + { + return new TableLookupKeyDesc( + Collections.GetEmptyList(), + Collections.GetEmptyList()); + } + } + + public override JoinExecTableLookupStrategy MakeStrategyInternal(EventTable[] eventTable, EventType[] eventTypes) + { + var single = (PropertyIndexedEventTableSingle) eventTable[0]; + var evaluators = new ExprEvaluator[_expressions.Length]; + for (var i = 0; i < _expressions.Length; i++) { + evaluators[i] = _expressions[i].ExprEvaluator; + } + return new InKeywordSingleTableLookupStrategyExpr(evaluators, + LookupStream, single, new LookupStrategyDesc(LookupStrategyType.INKEYWORDSINGLEIDX, ExprNodeUtility.ToExpressionStringsMinPrecedence(_expressions))); + } + + public override String ToString() + { + return GetType().Name + " " + + base.ToString() + + " keyProperties=" + ExprNodeUtility.ToExpressionStringMinPrecedence(_expressions); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/plan/InKeywordTableLookupUtil.cs b/NEsper.Core/NEsper.Core/epl/join/plan/InKeywordTableLookupUtil.cs new file mode 100755 index 000000000..51f93c965 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/plan/InKeywordTableLookupUtil.cs @@ -0,0 +1,80 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.join.table; + +namespace com.espertech.esper.epl.join.plan +{ + public class InKeywordTableLookupUtil + { + public static ICollection MultiIndexLookup(ExprEvaluator evaluator, EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext, EventTable[] indexes) + { + var evaluateParams = new EvaluateParams(eventsPerStream, true, exprEvaluatorContext); + var key = evaluator.Evaluate(evaluateParams); + var first = true; + ICollection result = null; + + foreach (var table in indexes) { + + ICollection found = ((PropertyIndexedEventTableSingle) table).Lookup(key); + if (found != null && !found.IsEmpty()) { + if (result == null) { + result = found; + } + else if (first) { + var copy = new LinkedHashSet(); + copy.AddAll(result); + copy.AddAll(found); + result = copy; + first = false; + } + else { + result.AddAll(found); + } + } + } + + return result; + } + + public static ICollection SingleIndexLookup(ExprEvaluator[] evaluators, EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext, PropertyIndexedEventTableSingle index) + { + var first = true; + ICollection result = null; + var evaluateParams = new EvaluateParams(eventsPerStream, true, exprEvaluatorContext); + + foreach (var evaluator in evaluators) { + var key = evaluator.Evaluate(evaluateParams); + ICollection found = index.Lookup(key); + if (found != null && !found.IsEmpty()) { + if (result == null) { + result = found; + } + else if (first) { + var copy = new LinkedHashSet(); + copy.AddAll(result); + copy.AddAll(found); + result = copy; + first = false; + } + else { + result.AddAll(found); + } + } + } + + return result; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/plan/IndexedTableLookupPlanMulti.cs b/NEsper.Core/NEsper.Core/epl/join/plan/IndexedTableLookupPlanMulti.cs new file mode 100755 index 000000000..3ae00be4c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/plan/IndexedTableLookupPlanMulti.cs @@ -0,0 +1,78 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.join.exec.@base; +using com.espertech.esper.epl.join.table; +using com.espertech.esper.epl.lookup; + +namespace com.espertech.esper.epl.join.plan +{ + /// + /// Plan to perform an indexed table lookup. + /// + public class IndexedTableLookupPlanMulti : TableLookupPlan + { + private readonly IList _keyProperties; + + /// Ctor. + /// stream that generates event to look up for + /// stream to index table lookup + /// index number for the table containing the full unindexed contents + /// properties to use in lookup event to access index + public IndexedTableLookupPlanMulti(int lookupStream, int indexedStream, TableLookupIndexReqKey indexNum, IList keyProperties) + : base(lookupStream, indexedStream, new TableLookupIndexReqKey[] { indexNum }) + { + _keyProperties = keyProperties; + } + + public override TableLookupKeyDesc KeyDescriptor + { + get { return new TableLookupKeyDesc(_keyProperties, Collections.GetEmptyList()); } + } + + public override JoinExecTableLookupStrategy MakeStrategyInternal(EventTable[] eventTable, EventType[] eventTypes) + { + var index = (PropertyIndexedEventTable) eventTable[0]; + var keyProps = new String[_keyProperties.Count]; + var evaluators = new ExprEvaluator[_keyProperties.Count]; + var expressions = new String[_keyProperties.Count]; + var isStrictlyProps = true; + for (var i = 0; i < keyProps.Length; i++) { + isStrictlyProps = isStrictlyProps && _keyProperties[i] is QueryGraphValueEntryHashKeyedProp; + evaluators[i] = _keyProperties[i].KeyExpr.ExprEvaluator; + expressions[i] = ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(_keyProperties[i].KeyExpr); + + if (_keyProperties[i] is QueryGraphValueEntryHashKeyedProp) + { + keyProps[i] = ((QueryGraphValueEntryHashKeyedProp)_keyProperties[i]).KeyProperty; + } + else { + isStrictlyProps = false; + } + } + if (isStrictlyProps) { + return new IndexedTableLookupStrategy(eventTypes[this.LookupStream], keyProps, index); + } + else { + return new IndexedTableLookupStrategyExpr(evaluators, LookupStream, index, new LookupStrategyDesc(LookupStrategyType.MULTIEXPR, expressions)); + } + } + + public override String ToString() + { + return GetType().FullName + " " + base.ToString() + " keyProperties=" + QueryGraphValueEntryHashKeyed.ToQueryPlan(_keyProperties); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/plan/IndexedTableLookupPlanSingle.cs b/NEsper.Core/NEsper.Core/epl/join/plan/IndexedTableLookupPlanSingle.cs new file mode 100755 index 000000000..e70b06bf6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/plan/IndexedTableLookupPlanSingle.cs @@ -0,0 +1,78 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.@join.exec.@base; +using com.espertech.esper.epl.@join.table; +using com.espertech.esper.epl.lookup; + +namespace com.espertech.esper.epl.join.plan +{ + /// + /// Plan to perform an indexed table lookup. + /// + public class IndexedTableLookupPlanSingle : TableLookupPlan + { + private readonly QueryGraphValueEntryHashKeyed _hashKey; + + /// Ctor. + /// stream that generates event to look up for + /// stream to index table lookup + /// index number for the table containing the full unindexed contents + /// properties to use in lookup event to access index + public IndexedTableLookupPlanSingle(int lookupStream, int indexedStream, TableLookupIndexReqKey indexNum, QueryGraphValueEntryHashKeyed hashKey) + : base(lookupStream, indexedStream, new TableLookupIndexReqKey[] { indexNum }) + { + _hashKey = hashKey; + } + + public override TableLookupKeyDesc KeyDescriptor + { + get + { + return new TableLookupKeyDesc( + Collections.SingletonList(_hashKey), + Collections.GetEmptyList()); + } + } + + public override JoinExecTableLookupStrategy MakeStrategyInternal(EventTable[] eventTable, EventType[] eventTypes) + { + var index = (PropertyIndexedEventTableSingle) eventTable[0]; + if (_hashKey is QueryGraphValueEntryHashKeyedExpr) { + var expr = (QueryGraphValueEntryHashKeyedExpr) _hashKey; + return new IndexedTableLookupStrategySingleExpr(expr.KeyExpr, base.LookupStream, index, + new LookupStrategyDesc(LookupStrategyType.SINGLEEXPR, new String[] {ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(expr.KeyExpr)})); + } + else if (_hashKey is QueryGraphValueEntryHashKeyedProp) { + var prop = (QueryGraphValueEntryHashKeyedProp) _hashKey; + return new IndexedTableLookupStrategySingle(eventTypes[LookupStream], prop.KeyProperty, index); + } + else { + throw new ArgumentException("Invalid hashkey instance " + _hashKey); + } + } + + public QueryGraphValueEntryHashKeyed HashKey + { + get { return _hashKey; } + } + + public override String ToString() + { + return "IndexedTableLookupPlan " + + base.ToString() + + " keyProperty=" + KeyDescriptor; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/plan/InnerJoinGraph.cs b/NEsper.Core/NEsper.Core/epl/join/plan/InnerJoinGraph.cs new file mode 100755 index 000000000..ec69491ac --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/plan/InnerJoinGraph.cs @@ -0,0 +1,144 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.spec; +using com.espertech.esper.type; + +namespace com.espertech.esper.epl.join.plan +{ + public class InnerJoinGraph + { + private readonly int _numStreams; + private readonly bool _isAllInnerJoin; + private readonly ICollection> _innerJoins; + + public InnerJoinGraph(int numStreams, ICollection> innerJoins) + { + _numStreams = numStreams; + _isAllInnerJoin = false; + _innerJoins = innerJoins; + } + + public InnerJoinGraph(int numStreams, bool isAllInnerJoin) + { + _numStreams = numStreams; + _isAllInnerJoin = isAllInnerJoin; + _innerJoins = null; + } + + public bool IsAllInnerJoin + { + get { return _isAllInnerJoin; } + } + + public bool IsEmpty() + { + if (_isAllInnerJoin) { + return false; + } + return _innerJoins.IsEmpty(); + } + + public bool HasInnerJoin(int toStream) + { + if (_isAllInnerJoin) { + return true; + } + bool hasInnerJoin = false; + foreach (InterchangeablePair pair in _innerJoins) + { + if (pair.First == toStream) + { + hasInnerJoin = true; + } + if (pair.Second == toStream) + { + hasInnerJoin = true; + } + } + return hasInnerJoin; + } + + public static InnerJoinGraph GraphInnerJoins(int numStreams, OuterJoinDesc[] outerJoinDescList) + { + if ((outerJoinDescList.Length + 1) != numStreams) + { + throw new ArgumentException("Number of outer join descriptors and number of streams not matching up"); + } + + ICollection> graph = new HashSet>(); + + bool allInnerJoin = true; + for (int i = 0; i < outerJoinDescList.Length; i++) + { + OuterJoinDesc desc = outerJoinDescList[i]; + int streamMax = i + 1; // the outer join must references streams less then streamMax + + // Check outer join on-expression, if provided + if (desc.OptLeftNode != null) { + int streamOne = desc.OptLeftNode.StreamId; + int streamTwo = desc.OptRightNode.StreamId; + + if ((streamOne > streamMax) || (streamTwo > streamMax) || (streamOne == streamTwo)) + { + throw new ArgumentException("Outer join descriptors reference future streams, or same streams"); + } + + if (desc.OuterJoinType == OuterJoinType.INNER) + { + graph.Add(new InterchangeablePair(streamOne, streamTwo)); + } + } + + if (desc.OuterJoinType != OuterJoinType.INNER) + { + allInnerJoin = false; + } + } + + if (allInnerJoin) { + return new InnerJoinGraph(numStreams, true); + } + return new InnerJoinGraph(numStreams, graph); + } + + public void AddRequiredStreams(int streamNum, ICollection requiredStreams, ICollection completedStreams) { + if (_isAllInnerJoin) { + for (int i = 0; i < _numStreams; i++) { + if (!completedStreams.Contains(i)) { + requiredStreams.Add(i); + } + } + return; + } + + foreach (InterchangeablePair pair in _innerJoins) + { + if (pair.First == streamNum) + { + if (!completedStreams.Contains(pair.Second)) + { + requiredStreams.Add(pair.Second); + } + } + if (pair.Second == streamNum) + { + if (!completedStreams.Contains(pair.First)) + { + requiredStreams.Add(pair.First); + } + } + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/plan/LookupInstructionPlan.cs b/NEsper.Core/NEsper.Core/epl/join/plan/LookupInstructionPlan.cs new file mode 100755 index 000000000..45a6b74d0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/plan/LookupInstructionPlan.cs @@ -0,0 +1,156 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.join.exec.@base; +using com.espertech.esper.epl.join.table; +using com.espertech.esper.epl.virtualdw; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.epl.join.plan +{ + /// + /// Plan for lookup using a from-stream event looking up one or more to-streams using a specified lookup plan for each + /// to-stream. + /// + public class LookupInstructionPlan + { + /// + /// Ctor. + /// + /// - the stream supplying the lookup event + /// - the stream name supplying the lookup event + /// - the set of streams to look up in + /// - the plan to use for each stream to look up in + /// - indicates which of the lookup streams are required to build a result and which are not + /// - plans for use with historical streams + public LookupInstructionPlan( + int fromStream, + string fromStreamName, + int[] toStreams, + TableLookupPlan[] lookupPlans, + HistoricalDataPlanNode[] historicalPlans, + bool[] requiredPerStream) + { + if (toStreams.Length != lookupPlans.Length) + { + throw new ArgumentException("Invalid number of lookup plans for each stream"); + } + if (requiredPerStream.Length < lookupPlans.Length) + { + throw new ArgumentException("Invalid required per stream array"); + } + if ((fromStream < 0) || (fromStream >= requiredPerStream.Length)) + { + throw new ArgumentException("Invalid from stream"); + } + + FromStream = fromStream; + FromStreamName = fromStreamName; + ToStreams = toStreams; + LookupPlans = lookupPlans; + HistoricalPlans = historicalPlans; + RequiredPerStream = requiredPerStream; + } + + /// + /// Constructs the executable from the plan. + /// + /// statement name + /// statement id + /// annotations + /// is the index objects for use in lookups + /// is the types of each stream + /// the viewable representing each stream + /// index management for historical streams @return executable instruction + /// virtual data window + /// instruction exec + public LookupInstructionExec MakeExec( + string statementName, + int statementId, + Attribute[] annotations, + IDictionary[] indexesPerStream, + EventType[] streamTypes, + Viewable[] streamViews, + HistoricalStreamIndexList[] historicalStreamIndexLists, + VirtualDWView[] viewExternal) + { + var strategies = new JoinExecTableLookupStrategy[LookupPlans.Length]; + for (int i = 0; i < LookupPlans.Length; i++) + { + if (LookupPlans[i] != null) + { + strategies[i] = LookupPlans[i].MakeStrategy( + statementName, statementId, annotations, indexesPerStream, streamTypes, viewExternal); + } + else + { + strategies[i] = HistoricalPlans[i].MakeOuterJoinStategy( + streamViews, FromStream, historicalStreamIndexLists); + } + } + return new LookupInstructionExec(FromStream, FromStreamName, ToStreams, strategies, RequiredPerStream); + } + + /// + /// Output the planned instruction. + /// + /// to output to + public void Print(IndentWriter writer) + { + writer.WriteLine( + "LookupInstructionPlan" + + " fromStream=" + FromStream + + " fromStreamName=" + FromStreamName + + " toStreams=" + ToStreams.Render() + ); + + writer.IncrIndent(); + for (int i = 0; i < LookupPlans.Length; i++) + { + if (LookupPlans[i] != null) + { + writer.WriteLine("plan " + i + " :" + LookupPlans[i]); + } + else + { + writer.WriteLine("plan " + i + " : no lookup plan"); + } + } + writer.DecrIndent(); + } + + public void AddIndexes(ISet usedIndexes) + { + for (int i = 0; i < LookupPlans.Length; i++) + { + if (LookupPlans[i] != null) + { + usedIndexes.AddAll(LookupPlans[i].IndexNum); + } + } + } + + public int FromStream { get; private set; } + + public string FromStreamName { get; private set; } + + public int[] ToStreams { get; private set; } + + public TableLookupPlan[] LookupPlans { get; private set; } + + public bool[] RequiredPerStream { get; private set; } + + public HistoricalDataPlanNode[] HistoricalPlans { get; private set; } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/join/plan/LookupInstructionQueryPlanNode.cs b/NEsper.Core/NEsper.Core/epl/join/plan/LookupInstructionQueryPlanNode.cs new file mode 100755 index 000000000..0c0160679 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/plan/LookupInstructionQueryPlanNode.cs @@ -0,0 +1,146 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.threading; +using com.espertech.esper.epl.join.assemble; +using com.espertech.esper.epl.join.exec.@base; +using com.espertech.esper.epl.join.table; +using com.espertech.esper.epl.virtualdw; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.epl.join.plan +{ + /// + /// Query plan for executing a set of lookup instructions and assembling an end result + /// via a set of assembly instructions. + /// + public class LookupInstructionQueryPlanNode : QueryPlanNode + { + private readonly IList _assemblyInstructionFactories; + private readonly IList _lookupInstructions; + private readonly int _numStreams; + private readonly bool[] _requiredPerStream; + private readonly int _rootStream; + private readonly String _rootStreamName; + + /// Ctor. + /// is the stream supplying the lookup event + /// is the name of the stream supplying the lookup event + /// is the number of streams + /// is a list of lookups to perform + /// indicates which streams are required and which are optional in the lookup + /// is the bottom-up assembly factory nodes to assemble a lookup result nodes + public LookupInstructionQueryPlanNode( + int rootStream, + String rootStreamName, + int numStreams, + bool[] requiredPerStream, + IList lookupInstructions, + IList assemblyInstructionFactories) + { + _rootStream = rootStream; + _rootStreamName = rootStreamName; + _lookupInstructions = lookupInstructions; + _numStreams = numStreams; + _requiredPerStream = requiredPerStream; + _assemblyInstructionFactories = assemblyInstructionFactories; + } + + public override ExecNode MakeExec(string statementName, int statementId, Attribute[] annotations, IDictionary[] indexesPerStream, EventType[] streamTypes, Viewable[] streamViews, HistoricalStreamIndexList[] historicalStreamIndexLists, VirtualDWView[] viewExternal, ILockable[] tableSecondaryIndexLocks) + { + var execs = new LookupInstructionExec[_lookupInstructions.Count]; + + int count = 0; + foreach (LookupInstructionPlan instruction in _lookupInstructions) + { + LookupInstructionExec exec = instruction.MakeExec(statementName, statementId, annotations, + indexesPerStream, streamTypes, streamViews, + historicalStreamIndexLists, viewExternal); + execs[count] = exec; + count++; + } + + return new LookupInstructionExecNode( + _rootStream, _rootStreamName, + _numStreams, execs, _requiredPerStream, + _assemblyInstructionFactories); + } + + public override void AddIndexes(ISet usedIndexes) + { + foreach (LookupInstructionPlan plan in _lookupInstructions) + { + plan.AddIndexes(usedIndexes); + } + } + + public override void Print(IndentWriter writer) + { + writer.WriteLine("LookupInstructionQueryPlanNode" + + " rootStream=" + _rootStream + + " requiredPerStream=" + _requiredPerStream.Render()); + + writer.IncrIndent(); + for (int i = 0; i < _lookupInstructions.Count; i++) + { + writer.WriteLine("lookup step " + i); + writer.IncrIndent(); + _lookupInstructions[i].Print(writer); + writer.DecrIndent(); + } + writer.DecrIndent(); + + writer.IncrIndent(); + for (int i = 0; i < _assemblyInstructionFactories.Count; i++) + { + writer.WriteLine("assembly step " + i); + writer.IncrIndent(); + _assemblyInstructionFactories[i].Print(writer); + writer.DecrIndent(); + } + writer.DecrIndent(); + } + + public IList AssemblyInstructionFactories + { + get { return _assemblyInstructionFactories; } + } + + public IList LookupInstructions + { + get { return _lookupInstructions; } + } + + public int NumStreams + { + get { return _numStreams; } + } + + public bool[] RequiredPerStream + { + get { return _requiredPerStream; } + } + + public int RootStream + { + get { return _rootStream; } + } + + public string RootStreamName + { + get { return _rootStreamName; } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/join/plan/NStreamOuterQueryPlanBuilder.cs b/NEsper.Core/NEsper.Core/epl/join/plan/NStreamOuterQueryPlanBuilder.cs new file mode 100755 index 000000000..3cc519cb0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/plan/NStreamOuterQueryPlanBuilder.cs @@ -0,0 +1,751 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.join.assemble; +using com.espertech.esper.epl.join.@base; +using com.espertech.esper.epl.join.table; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.type; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.join.plan +{ + /// + /// Builds a query plan for 3 or more streams in a outer join. + /// + public class NStreamOuterQueryPlanBuilder + { + /// + /// Build a query plan based on the stream property relationships indicated in queryGraph. + /// + /// navigation info between streams + /// descriptors for all outer joins + /// stream names + /// event types for each stream + /// The historical viewable desc. + /// dependencies between historical streams + /// index management, populated for the query plan + /// context for expression evalauation + /// The indexed streams unique props. + /// The tables per stream. + /// + /// query plan + /// + /// ExprValidationException if the query planning failed + internal static QueryPlan Build( + QueryGraph queryGraph, + OuterJoinDesc[] outerJoinDescList, + string[] streamNames, + EventType[] typesPerStream, + HistoricalViewableDesc historicalViewableDesc, + DependencyGraph dependencyGraph, + HistoricalStreamIndexList[] historicalStreamIndexLists, + ExprEvaluatorContext exprEvaluatorContext, + string[][][] indexedStreamsUniqueProps, + TableMetadata[] tablesPerStream) + { + if (Log.IsDebugEnabled) + { + Log.Debug(".build queryGraph=" + queryGraph); + } + + var numStreams = queryGraph.NumStreams; + var planNodeSpecs = new QueryPlanNode[numStreams]; + + // Build index specifications + var indexSpecs = QueryPlanIndexBuilder.BuildIndexSpec(queryGraph, typesPerStream, indexedStreamsUniqueProps); + if (Log.IsDebugEnabled) + { + Log.Debug(".build Index build completed, indexes=" + QueryPlanIndex.Print(indexSpecs)); + } + + // any historical streams don't get indexes, the lookup strategy accounts for cached indexes + if (historicalViewableDesc.HasHistorical) + { + for (var i = 0; i < historicalViewableDesc.Historical.Length; i++) + { + if (historicalViewableDesc.Historical[i]) + { + indexSpecs[i] = null; + } + } + } + + // Build graph of the outer join to inner table relationships. + // Build a map of inner joins. + OuterInnerDirectionalGraph outerInnerGraph; + InnerJoinGraph innerJoinGraph; + if (outerJoinDescList.Length > 0) + { + outerInnerGraph = GraphOuterJoins(numStreams, outerJoinDescList); + innerJoinGraph = InnerJoinGraph.GraphInnerJoins(numStreams, outerJoinDescList); + } + else + { + // all inner joins - thereby no (or empty) directional graph + outerInnerGraph = new OuterInnerDirectionalGraph(numStreams); + innerJoinGraph = new InnerJoinGraph(numStreams, true); + } + if (Log.IsDebugEnabled) + { + Log.Debug(".build directional graph=" + outerInnerGraph.Print()); + } + + // For each stream determine the query plan + for (var streamNo = 0; streamNo < numStreams; streamNo++) + { + // no plan for historical streams that are dependent upon other streams + if ((historicalViewableDesc.Historical[streamNo]) && (dependencyGraph.HasDependency(streamNo))) + { + planNodeSpecs[streamNo] = new QueryPlanNodeNoOp(); + continue; + } + + var queryPlanNode = BuildPlanNode(numStreams, streamNo, streamNames, queryGraph, outerInnerGraph, outerJoinDescList, innerJoinGraph, indexSpecs, typesPerStream, historicalViewableDesc.Historical, dependencyGraph, historicalStreamIndexLists, exprEvaluatorContext, tablesPerStream); + + if (Log.IsDebugEnabled) + { + Log.Debug(".build spec for stream '" + streamNames[streamNo] + + "' number " + streamNo + " is " + queryPlanNode); + } + + planNodeSpecs[streamNo] = queryPlanNode; + } + + var queryPlan = new QueryPlan(indexSpecs, planNodeSpecs); + if (Log.IsDebugEnabled) + { + Log.Debug(".build query plan=" + queryPlan); + } + + return queryPlan; + } + + private static QueryPlanNode BuildPlanNode( + int numStreams, + int streamNo, + string[] streamNames, + QueryGraph queryGraph, + OuterInnerDirectionalGraph outerInnerGraph, + OuterJoinDesc[] outerJoinDescList, + InnerJoinGraph innerJoinGraph, + QueryPlanIndex[] indexSpecs, + EventType[] typesPerStream, + bool[] ishistorical, + DependencyGraph dependencyGraph, + HistoricalStreamIndexList[] historicalStreamIndexLists, + ExprEvaluatorContext exprEvaluatorContext, + TableMetadata[] tablesPerStream) + { + // For each stream build an array of substreams, considering required streams (inner joins) first + // The order is relevant therefore preserving order via a LinkedHashMap. + var substreamsPerStream = new LinkedHashMap(); + var requiredPerStream = new bool[numStreams]; + + // Recursive populating the required (outer) and optional (inner) relationships + // of this stream and the substream + ISet completedStreams = new HashSet(); + // keep track of tree path as only those stream events are always available to historical streams + var streamCallStack = new Stack(); + streamCallStack.Push(streamNo); + + // For all inner-joins, the algorithm is slightly different + if (innerJoinGraph.IsAllInnerJoin) + { + requiredPerStream.Fill(true); + RecursiveBuildInnerJoin(streamNo, streamCallStack, queryGraph, completedStreams, substreamsPerStream, dependencyGraph); + + // compute a best chain to see if all streams are handled and add the remaining + var bestChain = NStreamQueryPlanBuilder.ComputeBestPath(streamNo, queryGraph, dependencyGraph); + AddNotYetNavigated(streamNo, numStreams, substreamsPerStream, bestChain); + } + else + { + RecursiveBuild(streamNo, streamCallStack, queryGraph, outerInnerGraph, innerJoinGraph, completedStreams, substreamsPerStream, requiredPerStream, dependencyGraph); + } + + // verify the substreamsPerStream, all streams must exists and be linked + VerifyJoinedPerStream(streamNo, substreamsPerStream); + + // build list of instructions for lookup + var lookupInstructions = BuildLookupInstructions(streamNo, substreamsPerStream, requiredPerStream, + streamNames, queryGraph, indexSpecs, typesPerStream, outerJoinDescList, ishistorical, historicalStreamIndexLists, exprEvaluatorContext, tablesPerStream); + + // build strategy tree for putting the result back together + var assemblyTopNodeFactory = AssemblyStrategyTreeBuilder.Build(streamNo, substreamsPerStream, requiredPerStream); + var assemblyInstructionFactories = BaseAssemblyNodeFactory.GetDescendentNodesBottomUp(assemblyTopNodeFactory); + + return new LookupInstructionQueryPlanNode(streamNo, streamNames[streamNo], numStreams, requiredPerStream, + lookupInstructions, assemblyInstructionFactories); + } + + private static void AddNotYetNavigated( + int streamNo, + int numStreams, + LinkedHashMap substreamsPerStream, + NStreamQueryPlanBuilder.BestChainResult bestChain) + { + // sum up all substreams (the query plan for each stream: nested iteration or cardinal) + ISet streams = new HashSet(); + streams.Add(streamNo); + RecursiveAdd(streamNo, streamNo, substreamsPerStream, streams, false); + + // we are done, all have navigated + if (streams.Count == numStreams) + { + return; + } + + var previous = streamNo; + foreach (var stream in bestChain.Chain) + { + if (streams.Contains(stream)) + { + previous = stream; + continue; + } + + // add node as a nested join to the previous stream + var substreams = substreamsPerStream.Get(previous); + if (substreams == null) + { + substreams = new int[0]; + } + var added = CollectionUtil.AddValue(substreams, stream); + substreamsPerStream.Put(previous, added); + + if (!substreamsPerStream.ContainsKey(stream)) + { + substreamsPerStream.Put(stream, new int[0]); + } + + previous = stream; + } + } + + private static IList BuildLookupInstructions( + int rootStreamNum, + LinkedHashMap substreamsPerStream, + bool[] requiredPerStream, + string[] streamNames, + QueryGraph queryGraph, + QueryPlanIndex[] indexSpecs, + EventType[] typesPerStream, + OuterJoinDesc[] outerJoinDescList, + bool[] isHistorical, + HistoricalStreamIndexList[] historicalStreamIndexLists, + ExprEvaluatorContext exprEvaluatorContext, + TableMetadata[] tablesPerStream) + { + IList result = new List(); + + foreach (int fromStream in substreamsPerStream.Keys) + { + var substreams = substreamsPerStream.Get(fromStream); + + // for streams with no substreams we don't need to look up + if (substreams.Length == 0) + { + continue; + } + + var plans = new TableLookupPlan[substreams.Length]; + var historicalPlans = new HistoricalDataPlanNode[substreams.Length]; + + for (var i = 0; i < substreams.Length; i++) + { + var toStream = substreams[i]; + + if (isHistorical[toStream]) + { + // There may not be an outer-join descriptor, use if provided to build the associated expression + ExprNode outerJoinExpr = null; + if (outerJoinDescList.Length > 0) + { + OuterJoinDesc outerJoinDesc; + if (toStream == 0) + { + outerJoinDesc = outerJoinDescList[0]; + } + else + { + outerJoinDesc = outerJoinDescList[toStream - 1]; + } + outerJoinExpr = outerJoinDesc.MakeExprNode(exprEvaluatorContext); + } + + if (historicalStreamIndexLists[toStream] == null) + { + historicalStreamIndexLists[toStream] = new HistoricalStreamIndexList(toStream, typesPerStream, queryGraph); + } + historicalStreamIndexLists[toStream].AddIndex(fromStream); + historicalPlans[i] = new HistoricalDataPlanNode(toStream, rootStreamNum, fromStream, typesPerStream.Length, outerJoinExpr); + } + else + { + plans[i] = NStreamQueryPlanBuilder.CreateLookupPlan(queryGraph, fromStream, toStream, indexSpecs[toStream], typesPerStream, tablesPerStream[toStream]); + } + } + + var fromStreamName = streamNames[fromStream]; + var instruction = new LookupInstructionPlan(fromStream, fromStreamName, substreams, plans, historicalPlans, requiredPerStream); + result.Add(instruction); + } + + return result; + } + + /// + /// Recusivly builds a substream-per-stream ordered tree graph using the + /// join information supplied for outer joins and from the query graph (where clause). + /// Required streams are considered first and their lookup is placed first in the list + /// to gain performance. + /// + /// is the root stream number that supplies the incoming event to build the tree for + /// the query plan call stack of streams available via cursor + /// contains where-clause stream relationship info + /// contains the outer join stream relationship info + /// The inner join graph. + /// is a temporary holder for streams already considered + /// is the ordered, tree-like structure to be filled + /// indicates which streams are required and which are optional + /// dependencies between historical streams + /// Historical stream + streamNum + parameter dependency originating in stream + dependentStream + cannot or may not be satisfied by the join + /// ExprValidationException if the query planning failed + internal static void RecursiveBuild( + int streamNum, + Stack streamCallStack, + QueryGraph queryGraph, + OuterInnerDirectionalGraph outerInnerGraph, + InnerJoinGraph innerJoinGraph, + ISet completedStreams, + IDictionary substreamsPerStream, + bool[] requiredPerStream, + DependencyGraph dependencyGraph) + { + // add this stream to the set of completed streams + completedStreams.Add(streamNum); + + // check if the dependencies have been satisfied + if (dependencyGraph.HasDependency(streamNum)) + { + var dependencies = dependencyGraph.GetDependenciesForStream(streamNum); + foreach (var dependentStream in dependencies) + { + if (!streamCallStack.Contains(dependentStream)) + { + throw new ExprValidationException("Historical stream " + streamNum + " parameter dependency originating in stream " + dependentStream + " cannot or may not be satisfied by the join"); + } + } + } + + // Determine the streams we can navigate to from this stream + var navigableStreams = queryGraph.GetNavigableStreams(streamNum); + + // add unqualified navigable streams (since on-expressions in outer joins are optional) + var unqualifiedNavigable = outerInnerGraph.UnqualifiedNavigableStreams.Get(streamNum); + if (unqualifiedNavigable != null) + { + navigableStreams.AddAll(unqualifiedNavigable); + } + + // remove those already done + navigableStreams.RemoveAll(completedStreams); + + // Which streams are inner streams to this stream (optional), which ones are outer to the stream (required) + var requiredStreams = GetOuterStreams(streamNum, navigableStreams, outerInnerGraph); + + // Add inner joins, if any, unless already completed for this stream + innerJoinGraph.AddRequiredStreams(streamNum, requiredStreams, completedStreams); + + var optionalStreams = GetInnerStreams(streamNum, navigableStreams, outerInnerGraph, innerJoinGraph, completedStreams); + + // Remove from the required streams the optional streams which places 'full' joined streams + // into the optional stream category + requiredStreams.RemoveAll(optionalStreams); + + // if we are a leaf node, we are done + if (navigableStreams.IsEmpty()) + { + substreamsPerStream.Put(streamNum, new int[0]); + return; + } + + // First the outer (required) streams to this stream, then the inner (optional) streams + var substreams = new int[requiredStreams.Count + optionalStreams.Count]; + substreamsPerStream.Put(streamNum, substreams); + var count = 0; + foreach (int stream in requiredStreams) + { + substreams[count++] = stream; + requiredPerStream[stream] = true; + } + foreach (int stream in optionalStreams) + { + substreams[count++] = stream; + } + + // next we look at all the required streams and add their dependent streams + foreach (int stream in requiredStreams) + { + completedStreams.Add(stream); + } + + foreach (int stream in requiredStreams) + { + streamCallStack.Push(stream); + RecursiveBuild(stream, streamCallStack, queryGraph, outerInnerGraph, innerJoinGraph, + completedStreams, substreamsPerStream, requiredPerStream, dependencyGraph); + streamCallStack.Pop(); + } + // look at all the optional streams and add their dependent streams + foreach (int stream in optionalStreams) + { + streamCallStack.Push(stream); + RecursiveBuild(stream, streamCallStack, queryGraph, outerInnerGraph, innerJoinGraph, + completedStreams, substreamsPerStream, requiredPerStream, dependencyGraph); + streamCallStack.Pop(); + } + } + + /// + /// Recusivly builds a substream-per-stream ordered tree graph using the + /// join information supplied for outer joins and from the query graph (where clause). + /// Required streams are considered first and their lookup is placed first in the list + /// to gain performance. + /// + /// is the root stream number that supplies the incoming event to build the tree for + /// contains where-clause stream relationship info + /// is a temporary holder for streams already considered + /// is the ordered, tree-like structure to be filled + /// the query plan call stack of streams available via cursor + /// dependencies between historical streams + /// ExprValidationException if the query planning failed + internal static void RecursiveBuildInnerJoin( + int streamNum, + Stack streamCallStack, + QueryGraph queryGraph, + ISet completedStreams, + IDictionary substreamsPerStream, + DependencyGraph dependencyGraph) + { + // add this stream to the set of completed streams + completedStreams.Add(streamNum); + + // check if the dependencies have been satisfied + if (dependencyGraph.HasDependency(streamNum)) + { + var dependencies = dependencyGraph.GetDependenciesForStream(streamNum); + foreach (var dependentStream in dependencies) + { + if (!streamCallStack.Contains(dependentStream)) + { + throw new ExprValidationException("Historical stream " + streamNum + " parameter dependency originating in stream " + dependentStream + " cannot or may not be satisfied by the join"); + } + } + } + + // Determine the streams we can navigate to from this stream + var navigableStreams = queryGraph.GetNavigableStreams(streamNum); + + // remove streams with a dependency on other streams not yet processed + var navigableStreamArr = navigableStreams.ToArray(); + foreach (int navigableStream in navigableStreamArr) + { + if (dependencyGraph.HasUnsatisfiedDependency(navigableStream, completedStreams)) + { + navigableStreams.Remove(navigableStream); + } + } + + // remove those already done + navigableStreams.RemoveAll(completedStreams); + + // if we are a leaf node, we are done + if (navigableStreams.IsEmpty()) + { + substreamsPerStream.Put(streamNum, new int[0]); + return; + } + + // First the outer (required) streams to this stream, then the inner (optional) streams + var substreams = new int[navigableStreams.Count]; + substreamsPerStream.Put(streamNum, substreams); + var count = 0; + foreach (int stream in navigableStreams) + { + substreams[count++] = stream; + completedStreams.Add(stream); + } + + foreach (int stream in navigableStreams) + { + streamCallStack.Push(stream); + RecursiveBuildInnerJoin(stream, streamCallStack, queryGraph, completedStreams, substreamsPerStream, dependencyGraph); + streamCallStack.Pop(); + } + } + + private static ISet GetInnerStreams( + int fromStream, + IEnumerable toStreams, + OuterInnerDirectionalGraph outerInnerGraph, + InnerJoinGraph innerJoinGraph, + ISet completedStreams) + { + ISet innerStreams = new HashSet(); + foreach (int toStream in toStreams) + { + if (outerInnerGraph.IsInner(fromStream, toStream)) + { + // if the to-stream, recursively, has an inner join itself, it becomes a required stream and not optional + var hasInnerJoin = false; + if (!innerJoinGraph.IsEmpty()) + { + var doNotUseStreams = new HashSet(completedStreams); + completedStreams.Add(fromStream); + hasInnerJoin = RecursiveHasInnerJoin(toStream, outerInnerGraph, innerJoinGraph, doNotUseStreams); + } + + if (!hasInnerJoin) + { + innerStreams.Add(toStream); + } + } + } + return innerStreams; + } + + private static bool RecursiveHasInnerJoin( + int toStream, + OuterInnerDirectionalGraph outerInnerGraph, + InnerJoinGraph innerJoinGraph, + ICollection completedStreams) + { + // Check if the to-stream is in any of the inner joins + var hasInnerJoin = innerJoinGraph.HasInnerJoin(toStream); + + if (hasInnerJoin) + { + return true; + } + + var innerToToStream = outerInnerGraph.GetInner(toStream); + if (innerToToStream != null) + { + foreach (int nextStream in innerToToStream) + { + if (completedStreams.Contains(nextStream)) + { + continue; + } + + var notConsider = new HashSet(completedStreams); + notConsider.Add(toStream); + var result = RecursiveHasInnerJoin(nextStream, outerInnerGraph, innerJoinGraph, notConsider); + + if (result) + { + return true; + } + } + } + + var outerToToStream = outerInnerGraph.GetOuter(toStream); + if (outerToToStream != null) + { + foreach (int nextStream in outerToToStream) + { + if (completedStreams.Contains(nextStream)) + { + continue; + } + + var notConsider = new HashSet(completedStreams); + notConsider.Add(toStream); + var result = RecursiveHasInnerJoin(nextStream, outerInnerGraph, innerJoinGraph, notConsider); + + if (result) + { + return true; + } + } + } + + return false; + } + + // which streams are to this table an outer stream + private static ISet GetOuterStreams(int fromStream, IEnumerable toStreams, OuterInnerDirectionalGraph outerInnerGraph) + { + ISet outerStreams = new HashSet(); + foreach (int toStream in toStreams) + { + if (outerInnerGraph.IsOuter(toStream, fromStream)) + { + outerStreams.Add(toStream); + } + } + return outerStreams; + } + + /// + /// Builds a graph of outer joins given the outer join information from the statement. + /// Eliminates right and left joins and full joins by placing the information in a graph object. + /// + /// is the number of streams + /// list of outer join stream numbers and property names + /// graph object + internal static OuterInnerDirectionalGraph GraphOuterJoins(int numStreams, OuterJoinDesc[] outerJoinDescList) + { + if ((outerJoinDescList.Length + 1) != numStreams) + { + throw new ArgumentException("Number of outer join descriptors and number of streams not matching up"); + } + + var graph = new OuterInnerDirectionalGraph(numStreams); + + for (var i = 0; i < outerJoinDescList.Length; i++) + { + var desc = outerJoinDescList[i]; + var streamMax = i + 1; // the outer join must references streams less then streamMax + + // Check outer join on-expression, if provided + int streamOne; + int streamTwo; + int lowerStream; + int higherStream; + if (desc.OptLeftNode != null) + { + streamOne = desc.OptLeftNode.StreamId; + streamTwo = desc.OptRightNode.StreamId; + + if ((streamOne > streamMax) || (streamTwo > streamMax) || + (streamOne == streamTwo)) + { + throw new ArgumentException("Outer join descriptors reference future streams, or same streams"); + } + + // Determine who is the first stream in the streams listed + lowerStream = streamOne; + higherStream = streamTwo; + if (streamOne > streamTwo) + { + lowerStream = streamTwo; + higherStream = streamOne; + } + } + else + { + streamOne = i; + streamTwo = i + 1; + lowerStream = i; + higherStream = i + 1; + + graph.AddUnqualifiedNavigable(streamOne, streamTwo); + } + + // Add to graph + if (desc.OuterJoinType == OuterJoinType.FULL) + { + graph.Add(streamOne, streamTwo); + graph.Add(streamTwo, streamOne); + } + else if (desc.OuterJoinType == OuterJoinType.LEFT) + { + graph.Add(lowerStream, higherStream); + } + else if (desc.OuterJoinType == OuterJoinType.RIGHT) + { + graph.Add(higherStream, lowerStream); + } + else if (desc.OuterJoinType == OuterJoinType.INNER) + { + // no navigability for inner joins + } + else + { + throw new ArgumentException("Outer join descriptors join type not handled, type=" + desc.OuterJoinType); + } + } + + return graph; + } + + /// + /// Verifies that the tree-like structure representing which streams join (lookup) into which sub-streams + /// is correct, ie. all streams are included and none are listed twice. + /// + /// is the stream supplying the incoming event + /// is keyed by the from-stream number and contains as values allstream numbers of lookup into to-streams. + /// + public static void VerifyJoinedPerStream(int rootStream, IDictionary streamsJoinedPerStream) + { + ISet streams = new HashSet(); + streams.Add(rootStream); + + RecursiveAdd(rootStream, rootStream, streamsJoinedPerStream, streams, true); + + if (streams.Count != streamsJoinedPerStream.Count) + { + throw new ArgumentException("Not all streams found, streamsJoinedPerStream=" + + Print(streamsJoinedPerStream)); + } + } + + private static void RecursiveAdd(int validatedStream, int currentStream, IDictionary streamsJoinedPerStream, ISet streams, bool verify) + { + if (currentStream >= streamsJoinedPerStream.Count && verify) + { + throw new ArgumentException("Error in stream " + currentStream + " streamsJoinedPerStream=" + + Print(streamsJoinedPerStream)); + } + var joinedStreams = streamsJoinedPerStream.Get(currentStream); + for (var i = 0; i < joinedStreams.Length; i++) + { + var addStream = joinedStreams[i]; + if (streams.Contains(addStream)) + { + throw new ArgumentException("Stream " + addStream + " found twice when validating " + validatedStream); + } + streams.Add(addStream); + RecursiveAdd(validatedStream, addStream, streamsJoinedPerStream, streams, verify); + } + } + + /// + /// Returns textual presentation of stream-substream relationships. + /// + /// is the tree-like structure of stream-substream + /// textual presentation + public static string Print(IDictionary streamsJoinedPerStream) + { + var writer = new StringWriter(); + + foreach (int stream in streamsJoinedPerStream.Keys) + { + var substreams = streamsJoinedPerStream.Get(stream); + writer.WriteLine("stream " + stream + " : " + substreams.Render()); + } + + return writer.ToString(); + } + + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/join/plan/NStreamQueryPlanBuilder.cs b/NEsper.Core/NEsper.Core/epl/join/plan/NStreamQueryPlanBuilder.cs new file mode 100755 index 000000000..1ba7b3c88 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/plan/NStreamQueryPlanBuilder.cs @@ -0,0 +1,698 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.join.@base; +using com.espertech.esper.epl.join.table; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.join.plan +{ + /* + * + 2 Stream query strategy/execution tree + (stream 0) Lookup in stream 1 + (stream 1) Lookup in stream 0 + + * ------ Example 1 a 3 table join + * + * " where streamA.id = streamB.id " + + " and streamB.id = streamC.id"; + + => Index propery names for each stream + for stream 0 to 4 = "id" + + => join order, ie. + for stream 0 = {1, 2} + for stream 1 = {factor [0,2]} + for stream 2 = {1, 0} + + => IndexKeyGen optionalIndexKeyGen, created by nested query plan nodes + + + 3 Stream query strategy + (stream 0) Nested iteration + Lookup in stream 1 Lookup in stream 2 + + (stream 1) Factor + Lookup in stream 0 Lookup in stream 2 + + (stream 2) Nested iteration + Lookup in stream 1 Lookup in stream 0 + + + * ------ Example 2 a 4 table join + * + * " where streamA.id = streamB.id " + + " and streamB.id = streamC.id"; + " and streamC.id = streamD.id"; + + => join order, ie. + for stream 0 = {1, 2, 3} + for stream 1 = {factor [0,2], use 2 for 3} + for stream 2 = {factor [1,3], use 1 for 0} + for stream 3 = {2, 1, 0} + + + concepts... nested iteration, inner loop + + select * from s1, s2, s3, s4 where s1.id=s2.id and s2.id=s3.id and s3.id=s4.id + + + (stream 0) Nested iteration + Lookup in stream 1 Lookup in stream 2 Lookup in stream 3 + + (stream 1) Factor + lookup in stream 0 Nested iteration + Lookup in stream 2 Lookup in stream 3 + + (stream 2) Factor + lookup in stream 3 Nested iteration + Lookup in stream 1 Lookup in stream 0 + + (stream 3) Nested iteration + Lookup in stream 2 Lookup in stream 1 Lookup in stream 0 + + * ------ Example 4 a 4 table join, orphan table + * + * " where streamA.id = streamB.id " + + " and streamB.id = streamC.id"; (no table D join criteria) + + * ------ Example 5 a 3 table join with 2 indexes for stream B + * + * " where streamA.A1 = streamB.B1 " + + " and streamB.B2 = streamC.C1"; (no table D join criteria) + */ + + /// + /// Builds a query plan for 3 or more streams in a join. + /// + public class NStreamQueryPlanBuilder + { + /// + /// Build a query plan based on the stream property relationships indicated in queryGraph. + /// + /// navigation info between streams + /// event types for each stream + /// The historical viewable desc. + /// dependencies between historical streams + /// index management, populated for the query plan + /// if set to true [has force nested iter]. + /// The indexed streams unique props. + /// The tables per stream. + /// + /// query plan + /// + internal static QueryPlan Build( + QueryGraph queryGraph, + EventType[] typesPerStream, + HistoricalViewableDesc historicalViewableDesc, + DependencyGraph dependencyGraph, + HistoricalStreamIndexList[] historicalStreamIndexLists, + bool hasForceNestedIter, + string[][][] indexedStreamsUniqueProps, + TableMetadata[] tablesPerStream) + { + if (Log.IsDebugEnabled) + { + Log.Debug(".build queryGraph=" + queryGraph); + } + + var numStreams = queryGraph.NumStreams; + var indexSpecs = QueryPlanIndexBuilder.BuildIndexSpec(queryGraph, typesPerStream, indexedStreamsUniqueProps); + if (Log.IsDebugEnabled) + { + Log.Debug(".build Index build completed, indexes=" + QueryPlanIndex.Print(indexSpecs)); + } + + // any historical streams don't get indexes, the lookup strategy accounts for cached indexes + if (historicalViewableDesc.HasHistorical) + { + for (var i = 0; i < historicalViewableDesc.Historical.Length; i++) + { + if (historicalViewableDesc.Historical[i]) + { + indexSpecs[i] = null; + } + } + } + + var planNodeSpecs = new QueryPlanNode[numStreams]; + int worstDepth = int.MaxValue; + for (var streamNo = 0; streamNo < numStreams; streamNo++) + { + // no plan for historical streams that are dependent upon other streams + if ((historicalViewableDesc.Historical[streamNo]) && (dependencyGraph.HasDependency(streamNo))) + { + planNodeSpecs[streamNo] = new QueryPlanNodeNoOp(); + continue; + } + + var bestChainResult = ComputeBestPath(streamNo, queryGraph, dependencyGraph); + var bestChain = bestChainResult.Chain; + if (Log.IsDebugEnabled) + { + Log.Debug(".build For stream " + streamNo + " bestChain=" + bestChain.Render()); + } + + if (bestChainResult.Depth < worstDepth) + { + worstDepth = bestChainResult.Depth; + } + + planNodeSpecs[streamNo] = CreateStreamPlan(streamNo, bestChain, queryGraph, indexSpecs, typesPerStream, historicalViewableDesc.Historical, historicalStreamIndexLists, tablesPerStream); + if (Log.IsDebugEnabled) + { + Log.Debug(".build spec=" + planNodeSpecs[streamNo]); + } + } + + // We use the merge/nested (outer) join algorithm instead. + if ((worstDepth < numStreams - 1) && (!hasForceNestedIter)) + { + return null; + } + return new QueryPlan(indexSpecs, planNodeSpecs); + } + + /// + /// Walks the chain of lookups and constructs lookup strategy and plan specification based + /// on the index specifications. + /// + /// the stream to construct the query plan for + /// the chain that the lookup follows to make best use of indexes + /// the repository for key properties to indexes + /// specifications of indexes + /// event types for each stream + /// indicator for each stream if it is a historical streams or not + /// index management, populated for the query plan + /// NestedIterationNode with lookups attached underneath + internal static QueryPlanNode CreateStreamPlan( + int lookupStream, + int[] bestChain, + QueryGraph queryGraph, + QueryPlanIndex[] indexSpecsPerStream, + EventType[] typesPerStream, + bool[] isHistorical, + HistoricalStreamIndexList[] historicalStreamIndexLists, + TableMetadata[] tablesPerStream) + { + var nestedIterNode = new NestedIterationNode(bestChain); + var currentLookupStream = lookupStream; + + // Walk through each successive lookup + for (var i = 0; i < bestChain.Length; i++) + { + var indexedStream = bestChain[i]; + + QueryPlanNode node; + if (isHistorical[indexedStream]) + { + if (historicalStreamIndexLists[indexedStream] == null) + { + historicalStreamIndexLists[indexedStream] = new HistoricalStreamIndexList(indexedStream, typesPerStream, queryGraph); + } + historicalStreamIndexLists[indexedStream].AddIndex(currentLookupStream); + node = new HistoricalDataPlanNode(indexedStream, lookupStream, currentLookupStream, typesPerStream.Length, null); + } + else + { + var tableLookupPlan = CreateLookupPlan(queryGraph, currentLookupStream, indexedStream, indexSpecsPerStream[indexedStream], typesPerStream, tablesPerStream[indexedStream]); + node = new TableLookupNode(tableLookupPlan); + } + nestedIterNode.AddChildNode(node); + + currentLookupStream = bestChain[i]; + } + + return nestedIterNode; + } + + /// + /// Create the table lookup plan for a from-stream to look up in an indexed stream + /// using the columns supplied in the query graph and looking at the actual indexes available + /// and their index number. + /// + /// contains properties joining the 2 streams + /// stream to use key values from + /// stream to look up in + /// index specification defining indexes to be created for stream + /// event types for each stream + /// plan for performing a lookup in a given table using one of the indexes supplied + public static TableLookupPlan CreateLookupPlan(QueryGraph queryGraph, int currentLookupStream, int indexedStream, + QueryPlanIndex indexSpecs, EventType[] typesPerStream, + TableMetadata indexedStreamTableMeta) + { + var queryGraphValue = queryGraph.GetGraphValue(currentLookupStream, indexedStream); + var hashKeyProps = queryGraphValue.HashKeyProps; + var hashPropsKeys = hashKeyProps.Keys; + var hashIndexProps = hashKeyProps.Indexed.ToArray(); + + var rangeProps = queryGraphValue.RangeProps; + var rangePropsKeys = rangeProps.Keys; + var rangeIndexProps = rangeProps.Indexed.ToArray(); + + var pairIndexHashRewrite = indexSpecs.GetIndexNum(hashIndexProps, rangeIndexProps); + var indexNum = pairIndexHashRewrite == null ? null : pairIndexHashRewrite.First; + + // handle index redirection towards unique index + if (pairIndexHashRewrite != null && pairIndexHashRewrite.Second != null) + { + var indexes = pairIndexHashRewrite.Second; + var newHashIndexProps = new string[indexes.Length]; + IList newHashKeys = new List(); + for (var i = 0; i < indexes.Length; i++) + { + newHashIndexProps[i] = hashIndexProps[indexes[i]]; + newHashKeys.Add(hashPropsKeys[indexes[i]]); + } + hashIndexProps = newHashIndexProps; + hashPropsKeys = newHashKeys; + rangeIndexProps = new string[0]; + rangePropsKeys = Collections.GetEmptyList(); + } + + // no direct hash or range lookups + if (hashIndexProps.Length == 0 && rangeIndexProps.Length == 0) + { + + // handle single-direction 'in' keyword + var singles = queryGraphValue.InKeywordSingles; + if (!singles.Key.IsEmpty()) + { + + QueryGraphValueEntryInKeywordSingleIdx single = null; + indexNum = null; + if (indexedStreamTableMeta != null) + { + var indexes = singles.Indexed; + var count = 0; + foreach (var index in indexes) + { + Pair indexPairFound = + EventTableIndexUtil.FindIndexBestAvailable( + indexedStreamTableMeta.EventTableIndexMetadataRepo.Indexes, + Collections.SingletonSet(index), + Collections.GetEmptySet(), null); + if (indexPairFound != null) + { + indexNum = new TableLookupIndexReqKey(indexPairFound.Second.OptionalIndexName, indexedStreamTableMeta.TableName); + single = singles.Key[count]; + } + count++; + } + } + else + { + single = singles.Key[0]; + var pairIndex = indexSpecs.GetIndexNum(new string[] { singles.Indexed[0] }, null); + indexNum = pairIndex.First; + } + + if (indexNum != null) + { + return new InKeywordTableLookupPlanSingleIdx(currentLookupStream, indexedStream, indexNum, single.KeyExprs); + } + } + + // handle multi-direction 'in' keyword + var multis = queryGraphValue.InKeywordMulti; + if (!multis.IsEmpty()) + { + if (indexedStreamTableMeta != null) + { + return GetFullTableScanTable(currentLookupStream, indexedStream, indexedStreamTableMeta); + } + QueryGraphValuePairInKWMultiIdx multi = multis[0]; + var indexNameArray = new TableLookupIndexReqKey[multi.Indexed.Length]; + var foundAll = true; + for (var i = 0; i < multi.Indexed.Length; i++) + { + var identNode = (ExprIdentNode)multi.Indexed[i]; + var pairIndex = indexSpecs.GetIndexNum(new string[] { identNode.ResolvedPropertyName }, null); + if (pairIndex == null) + { + foundAll = false; + } + else + { + indexNameArray[i] = pairIndex.First; + } + } + if (foundAll) + { + return new InKeywordTableLookupPlanMultiIdx(currentLookupStream, indexedStream, indexNameArray, multi.Key.KeyExpr); + } + } + + // We don't use a keyed index but use the full stream set as the stream does not have any indexes + + // If no such full set index exists yet, add to specs + if (indexedStreamTableMeta != null) + { + return GetFullTableScanTable(currentLookupStream, indexedStream, indexedStreamTableMeta); + } + if (indexNum == null) + { + indexNum = new TableLookupIndexReqKey(indexSpecs.AddIndex(null, null)); + } + return new FullTableScanLookupPlan(currentLookupStream, indexedStream, indexNum); + } + + if (indexNum == null) + { + throw new IllegalStateException("Failed to query plan as index for " + hashIndexProps.Render() + " and " + rangeIndexProps.Render() + " in the index specification"); + } + + if (indexedStreamTableMeta != null) + { + var indexPairFound = EventTableIndexUtil.FindIndexBestAvailable(indexedStreamTableMeta.EventTableIndexMetadataRepo.Indexes, ToSet(hashIndexProps), ToSet(rangeIndexProps), null); + if (indexPairFound != null) + { + var indexKeyInfo = SubordinateQueryPlannerUtil.CompileIndexKeyInfo(indexPairFound.First, hashIndexProps, GetHashKeyFuncsAsSubProp(hashPropsKeys), rangeIndexProps, GetRangeFuncsAsSubProp(rangePropsKeys)); + if (indexKeyInfo.OrderedKeyCoercionTypes.IsCoerce || indexKeyInfo.OrderedRangeCoercionTypes.IsCoerce) + { + return GetFullTableScanTable(currentLookupStream, indexedStream, indexedStreamTableMeta); + } + hashPropsKeys = ToHashKeyFuncs(indexKeyInfo.OrderedHashDesc); + hashIndexProps = IndexedPropDesc.GetIndexProperties(indexPairFound.First.HashIndexedProps); + rangePropsKeys = ToRangeKeyFuncs(indexKeyInfo.OrderedRangeDesc); + rangeIndexProps = IndexedPropDesc.GetIndexProperties(indexPairFound.First.RangeIndexedProps); + indexNum = new TableLookupIndexReqKey(indexPairFound.Second.OptionalIndexName, indexedStreamTableMeta.TableName); + // the plan will be created below + if (hashIndexProps.Length == 0 && rangeIndexProps.Length == 0) + { + return GetFullTableScanTable(currentLookupStream, indexedStream, indexedStreamTableMeta); + } + } + else + { + return GetFullTableScanTable(currentLookupStream, indexedStream, indexedStreamTableMeta); + } + } + + // straight keyed-index lookup + if (hashIndexProps.Length > 0 && rangeIndexProps.Length == 0) + { + TableLookupPlan tableLookupPlan; + if (hashPropsKeys.Count == 1) + { + tableLookupPlan = new IndexedTableLookupPlanSingle(currentLookupStream, indexedStream, indexNum, hashPropsKeys[0]); + } + else + { + tableLookupPlan = new IndexedTableLookupPlanMulti(currentLookupStream, indexedStream, indexNum, hashPropsKeys); + } + + // Determine coercion required + var coercionTypes = CoercionUtil.GetCoercionTypesHash(typesPerStream, currentLookupStream, indexedStream, hashPropsKeys, hashIndexProps); + if (coercionTypes.IsCoerce) + { + // check if there already are coercion types for this index + var existCoercionTypes = indexSpecs.GetCoercionTypes(hashIndexProps); + if (existCoercionTypes != null) + { + for (var i = 0; i < existCoercionTypes.Length; i++) + { + coercionTypes.CoercionTypes[i] = TypeHelper.GetCompareToCoercionType(existCoercionTypes[i], coercionTypes.CoercionTypes[i]); + } + } + indexSpecs.SetCoercionTypes(hashIndexProps, coercionTypes.CoercionTypes); + } + + return tableLookupPlan; + } + + // sorted index lookup + if (hashIndexProps.Length == 0 && rangeIndexProps.Length == 1) + { + QueryGraphValueEntryRange range = rangePropsKeys[0]; + return new SortedTableLookupPlan(currentLookupStream, indexedStream, indexNum, range); + } + // composite range and index lookup + else + { + return new CompositeTableLookupPlan(currentLookupStream, indexedStream, indexNum, hashPropsKeys, rangePropsKeys); + } + } + + /// + /// Compute a best chain or path for lookups to take for the lookup stream passed in and the query + /// property relationships. + /// The method runs through all possible permutations of lookup path until a path is found in which all streams can be accessed via an index. + /// If not such path is found, the method returns the path with the greatest depth, ie. where + /// the first one or more streams are index accesses. + /// If no depth other then zero is found, returns the default nesting order. + /// + /// stream to start look up + /// navigability between streams + /// dependencies between historical streams + /// chain and chain depth + internal static BestChainResult ComputeBestPath(int lookupStream, QueryGraph queryGraph, DependencyGraph dependencyGraph) + { + var defNestingorder = BuildDefaultNestingOrder(queryGraph.NumStreams, lookupStream); + IEnumerator streamEnum; + if (defNestingorder.Length < 6) + { + streamEnum = NumberSetPermutationEnumeration.New(defNestingorder).GetEnumerator(); + } + else + { + streamEnum = NumberSetShiftGroupEnumeration.New(defNestingorder).GetEnumerator(); + } + int[] bestPermutation = null; + var bestDepth = -1; + + while (streamEnum.MoveNext()) + { + int[] permutation = streamEnum.Current; + + // Only if the permutation satisfies all dependencies is the permutation considered + if (dependencyGraph != null) + { + var pass = IsDependencySatisfied(lookupStream, permutation, dependencyGraph); + if (!pass) + { + continue; + } + } + + var permutationDepth = ComputeNavigableDepth(lookupStream, permutation, queryGraph); + + if (permutationDepth > bestDepth) + { + bestPermutation = permutation; + bestDepth = permutationDepth; + } + + // Stop when the permutation yielding the full depth (lenght of stream chain) was hit + if (permutationDepth == queryGraph.NumStreams - 1) + { + break; + } + } + + return new BestChainResult(bestDepth, bestPermutation); + } + + /// + /// Determine if the proposed permutation of lookups passes dependencies + /// + /// stream to initiate + /// permutation of lookups + /// dependencies + /// pass or fail indication + internal static bool IsDependencySatisfied(int lookupStream, int[] permutation, DependencyGraph dependencyGraph) + { + foreach (var entry in dependencyGraph.Dependencies) + { + var target = entry.Key; + var positionTarget = PositionOf(target, lookupStream, permutation); + if (positionTarget == -1) + { + throw new ArgumentException("Target dependency not found in permutation for target " + target + " and permutation " + permutation.Render() + " and lookup stream " + lookupStream); + } + + // check the position of each dependency, it must be higher + foreach (int dependency in entry.Value) + { + var positonDep = PositionOf(dependency, lookupStream, permutation); + if (positonDep == -1) + { + throw new ArgumentException("Dependency not found in permutation for dependency " + dependency + " and permutation " + permutation.Render() + " and lookup stream " + lookupStream); + } + + if (positonDep > positionTarget) + { + return false; + } + } + } + return true; + } + + private static int PositionOf(int stream, int lookupStream, int[] permutation) + { + if (stream == lookupStream) + { + return 0; + } + for (var i = 0; i < permutation.Length; i++) + { + if (permutation[i] == stream) + { + return i + 1; + } + } + return -1; + } + + /// + /// Given a chain of streams to look up and indexing information, compute the index within the + /// chain of the first non-index lookup. + /// + /// stream to start lookup for + /// list of stream numbers next in lookup + /// indexing information + /// value between 0 and (nextStreams.lenght - 1) + internal static int ComputeNavigableDepth(int lookupStream, int[] nextStreams, QueryGraph queryGraph) + { + var currentStream = lookupStream; + var currentDepth = 0; + + for (var i = 0; i < nextStreams.Length; i++) + { + var nextStream = nextStreams[i]; + var navigable = queryGraph.IsNavigableAtAll(currentStream, nextStream); + if (!navigable) + { + break; + } + currentStream = nextStream; + currentDepth++; + } + + return currentDepth; + } + + /// + /// Returns default nesting order for a given number of streams for a certain stream. + /// Example: numStreams = 5, forStream = 2, result = {0, 1, 3, 4} + /// The resulting array has all streams except the forStream, in ascdending order. + /// + /// number of streams + /// stream to generate a nesting order for + /// int array with all stream numbers starting at 0 to (numStreams - 1) leaving theforStream out + /// + internal static int[] BuildDefaultNestingOrder(int numStreams, int forStream) + { + var nestingOrder = new int[numStreams - 1]; + + var count = 0; + for (var i = 0; i < numStreams; i++) + { + if (i == forStream) + { + continue; + } + nestingOrder[count++] = i; + } + + return nestingOrder; + } + + private static IList ToRangeKeyFuncs(IEnumerable orderedRangeDesc) + { + return orderedRangeDesc.Select(key => key.RangeInfo).ToList(); + } + + private static IList ToHashKeyFuncs(IEnumerable orderedHashProperties) + { + return orderedHashProperties.Select(key => key.HashKey).ToList(); + } + + private static TableLookupPlan GetFullTableScanTable(int lookupStream, int indexedStream, TableMetadata indexedStreamTableMeta) + { + var indexName = new TableLookupIndexReqKey(indexedStreamTableMeta.TableName, indexedStreamTableMeta.TableName); + return new FullTableScanUniquePerKeyLookupPlan(lookupStream, indexedStream, indexName); + } + + private static ISet ToSet(IEnumerable strings) + { + return new LinkedHashSet(strings); + } + + private static SubordPropRangeKey[] GetRangeFuncsAsSubProp(IList funcs) + { + var keys = new SubordPropRangeKey[funcs.Count]; + for (var i = 0; i < funcs.Count; i++) + { + var func = funcs[i]; + keys[i] = new SubordPropRangeKey(func, func.Expressions[0].ExprEvaluator.ReturnType); + } + return keys; + } + + private static SubordPropHashKey[] GetHashKeyFuncsAsSubProp(IList funcs) + { + var keys = new SubordPropHashKey[funcs.Count]; + for (var i = 0; i < funcs.Count; i++) + { + keys[i] = new SubordPropHashKey(funcs[i], null, null); + } + return keys; + } + + /// + /// Encapsulates the chain information. + /// + public class BestChainResult + { + /// + /// Ctor. + /// + /// depth this chain resolves into a indexed lookup + /// chain for nested lookup + public BestChainResult(int depth, int[] chain) + { + Depth = depth; + Chain = chain; + } + + /// + /// Returns depth of lookups via index in chain. + /// + /// depth + public int Depth { get; private set; } + + /// + /// Returns chain of stream numbers. + /// + /// array of stream numbers + public int[] Chain { get; private set; } + + public override string ToString() + { + return "depth=" + Depth + " chain=" + Chain.Render(); + } + } + + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/join/plan/NestedIterationNode.cs b/NEsper.Core/NEsper.Core/epl/join/plan/NestedIterationNode.cs new file mode 100755 index 000000000..5b6c1edd8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/plan/NestedIterationNode.cs @@ -0,0 +1,94 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.threading; +using com.espertech.esper.epl.@join.exec.@base; +using com.espertech.esper.epl.@join.table; +using com.espertech.esper.epl.virtualdw; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.epl.join.plan +{ + /// Plan to perform a nested iteration over child nodes. + public class NestedIterationNode : QueryPlanNode + { + private readonly List _childNodes; + private readonly int[] _nestingOrder; + + /// Ctor. + /// order of streams in nested iteration + public NestedIterationNode(int[] nestingOrder) + { + _nestingOrder = nestingOrder; + _childNodes = new List(); + + if (nestingOrder.Length == 0) + { + throw new ArgumentException("Invalid empty nesting order"); + } + } + + /// Returns list of child nodes. + /// list of child nodes + protected internal List ChildNodes + { + get { return _childNodes; } + } + + /// Adds a child node. + /// is the child evaluation tree node to add + public void AddChildNode(QueryPlanNode childNode) + { + _childNodes.Add(childNode); + } + + public override ExecNode MakeExec(string statementName, int statementId, Attribute[] annotations, IDictionary[] indexPerStream, EventType[] streamTypes, Viewable[] streamViews, HistoricalStreamIndexList[] historicalStreamIndexList, VirtualDWView[] viewExternal, ILockable[] tableSecondaryIndexLocks) + { + if (_childNodes.IsEmpty()) + { + throw new IllegalStateException("Zero child nodes for nested iteration"); + } + + var execNode = new NestedIterationExecNode(_nestingOrder); + foreach (QueryPlanNode child in _childNodes) + { + ExecNode childExec = child.MakeExec( + statementName, statementId, annotations, indexPerStream, streamTypes, streamViews, + historicalStreamIndexList, viewExternal, tableSecondaryIndexLocks); + execNode.AddChildNode(childExec); + } + return execNode; + } + + public override void AddIndexes(ISet usedIndexes) + { + foreach (QueryPlanNode child in _childNodes) + { + child.AddIndexes(usedIndexes); + } + } + + public override void Print(IndentWriter indentWriter) + { + indentWriter.WriteLine("NestedIterationNode with nesting order " + _nestingOrder.Render()); + indentWriter.IncrIndent(); + foreach (QueryPlanNode child in _childNodes) + { + child.Print(indentWriter); + } + indentWriter.DecrIndent(); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/join/plan/OuterInnerDirectionalGraph.cs b/NEsper.Core/NEsper.Core/epl/join/plan/OuterInnerDirectionalGraph.cs new file mode 100755 index 000000000..c1a6d6e3c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/plan/OuterInnerDirectionalGraph.cs @@ -0,0 +1,213 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; +using System.Text; + +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.epl.join.plan +{ + /// This class represents outer-join relationships between outer and inner tables. + /// To add a left outer join between streams 0 and 1 use "Add(0, 1)". + /// To add a full outer join between streams 0 and 1 use "Add(0, 1)" and "Add(1, 0)". + /// To add a right outer join between streams 0 and 1 use "Add(1, 0)". + /// + + public class OuterInnerDirectionalGraph + { + private readonly IDictionary> _streamToInnerMap; + private readonly IDictionary> _unqualifiedNavigableStreams; + private readonly int _numStreams; + + /// Ctor. + /// number of streams + /// + + public OuterInnerDirectionalGraph(int numStreams) + { + _numStreams = numStreams; + _streamToInnerMap = new Dictionary>(); + _unqualifiedNavigableStreams = new Dictionary>(); + } + + /// Add an outer-to-inner join stream relationship. + /// is the stream number of the outer stream + /// + /// is the stream number of the inner stream + /// + /// graph object + /// + public virtual OuterInnerDirectionalGraph Add(int outerStream, int innerStream) + { + CheckArgs(outerStream, innerStream); + + // add set + ICollection innerSet = _streamToInnerMap.Get( outerStream, null ) ; + if ( innerSet == null ) + { + innerSet = new HashSet(); + _streamToInnerMap[outerStream] = innerSet; + } + + // populate + if (innerSet.Contains(innerStream)) + { + throw new ArgumentException("Inner stream already in collection"); + } + + innerSet.Add(innerStream); + + return this; + } + + /// Returns the set of inner streams for the given outer stream number. + /// is the stream number of the outer stream + /// + /// set of inner streams, or null if empty + /// + public ICollection GetInner(int outerStream) + { + CheckArgs(outerStream); + return _streamToInnerMap.Get(outerStream, null); + } + + /// Returns the set of outer streams for the given inner stream number. + /// is the stream number of the inner stream + /// + /// set of outer streams, or null if empty + /// + public ICollection GetOuter(int innerStream) + { + CheckArgs(innerStream); + + ICollection result = new HashSet(); + foreach( KeyValuePair> keyValuePair in _streamToInnerMap ) + { + int key = keyValuePair.Key; + ICollection set = keyValuePair.Value; + + if (set.Contains(innerStream)) + { + result.Add(key); + } + } + + if (result.Count == 0) + { + return null; + } + + return result; + } + + /// Returns true if the outer stream has an optional relationship to the inner stream. + /// is the stream number of the outer stream + /// + /// is the stream number of the inner stream + /// + /// true if outer-inner relationship between streams, false if not + /// + public virtual bool IsInner(int outerStream, int innerStream) + { + CheckArgs(outerStream, innerStream); + + ICollection innerSet = _streamToInnerMap.Get(outerStream, null); + if (innerSet == null) + { + return false; + } + return innerSet.Contains(innerStream); + } + + /// Returns true if the inner stream has a relationship to the outer stream. + /// is the stream number of the outer stream + /// + /// is the stream number of the inner stream + /// + /// true if outer-inner relationship between streams, false if not + /// + public virtual bool IsOuter(int outerStream, int innerStream) + { + CheckArgs(outerStream, innerStream); + ICollection outerStreams = GetOuter(innerStream); + if (outerStreams == null) + { + return false; + } + return outerStreams.Contains(outerStream); + } + + /// Prints out collection. + /// textual output of keys and values + /// + public virtual String Print() + { + StringBuilder buffer = new StringBuilder(); + String delimiter = ""; + + foreach( KeyValuePair> kvPair in _streamToInnerMap ) + { + ICollection set = kvPair.Value; + + buffer.Append(delimiter); + buffer.Append(kvPair.Key); + buffer.Append('='); + buffer.Append(set.ToString()); + + delimiter = ", "; + } + + return buffer.ToString(); + } + + public IDictionary> UnqualifiedNavigableStreams + { + get { return _unqualifiedNavigableStreams; } + } + + public void AddUnqualifiedNavigable(int streamOne, int streamTwo) + { + AddUnqualifiedInternal(streamOne, streamTwo); + AddUnqualifiedInternal(streamTwo, streamOne); + } + + private void AddUnqualifiedInternal(int streamOne, int streamTwo) + { + var set = _unqualifiedNavigableStreams.Get(streamOne); + if (set == null) + { + set = new HashSet(); + _unqualifiedNavigableStreams.Put(streamOne, set); + } + set.Add(streamTwo); + } + + private void CheckArgs(int stream) + { + if ((stream >= _numStreams) || (stream < 0)) + { + throw new ArgumentException("Out of bounds parameter for stream num"); + } + } + + private void CheckArgs(int outerStream, int innerStream) + { + if ((outerStream >= _numStreams) || (innerStream >= _numStreams) || (outerStream < 0) || (innerStream < 0)) + { + throw new ArgumentException("Out of bounds parameter for inner or outer stream num"); + } + if (outerStream == innerStream) + { + throw new ArgumentException("Unexpected equal stream num for inner and outer stream"); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/plan/OuterJoinAnalyzer.cs b/NEsper.Core/NEsper.Core/epl/join/plan/OuterJoinAnalyzer.cs new file mode 100755 index 000000000..c3cea9ae8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/plan/OuterJoinAnalyzer.cs @@ -0,0 +1,55 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.epl.join.plan +{ + /// Analyzes an outer join descriptor list and builds a query graph model from it. The 'on' expression identifiers are extracted and placed in the query graph model as navigable relationships (by key and index properties) between streams. + public class OuterJoinAnalyzer + { + /// Analyzes the outer join descriptor list to build a query graph model. + /// list of outer join descriptors + /// model containing relationships between streams that is written into + /// queryGraph object + public static QueryGraph Analyze(OuterJoinDesc[] outerJoinDescList, QueryGraph queryGraph) + { + foreach (OuterJoinDesc outerJoinDesc in outerJoinDescList) + { + // add optional on-expressions + if (outerJoinDesc.OptLeftNode != null) { + ExprIdentNode identNodeLeft = outerJoinDesc.OptLeftNode; + ExprIdentNode identNodeRight = outerJoinDesc.OptRightNode; + + Add(queryGraph, identNodeLeft, identNodeRight); + + if (outerJoinDesc.AdditionalLeftNodes != null) + { + for (int i = 0; i < outerJoinDesc.AdditionalLeftNodes.Length; i++) + { + Add(queryGraph, outerJoinDesc.AdditionalLeftNodes[i], outerJoinDesc.AdditionalRightNodes[i]); + } + } + } + else { + + } + } + + return queryGraph; + } + + private static void Add(QueryGraph queryGraph, ExprIdentNode identNodeLeft, ExprIdentNode identNodeRight) + { + queryGraph.AddStrictEquals(identNodeLeft.StreamId, identNodeLeft.ResolvedPropertyName, identNodeLeft, + identNodeRight.StreamId, identNodeRight.ResolvedPropertyName, identNodeRight); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraph.cs b/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraph.cs new file mode 100755 index 000000000..4f7341b49 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraph.cs @@ -0,0 +1,547 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.@join.hint; +using com.espertech.esper.type; + +namespace com.espertech.esper.epl.join.plan +{ + /// + /// Model of relationships between streams based on properties in both streams that are specified + /// as equal in a filter expression. + /// + public class QueryGraph + { + private readonly int _numStreams; + private readonly ExcludePlanHint _optionalHint; + private readonly bool _nToZeroAnalysis; // for subqueries and on-action + private readonly IDictionary _streamJoinMap; + + /// Ctor. + /// number of streams + public QueryGraph(int numStreams, ExcludePlanHint optionalHint, bool nToZeroAnalysis) + { + _numStreams = numStreams; + _optionalHint = optionalHint; + _nToZeroAnalysis = nToZeroAnalysis; + _streamJoinMap = new NullableDictionary(); + } + + /// Returns the number of streams. + /// number of streams + public int NumStreams + { + get { return _numStreams; } + } + + /// + /// Add properties for 2 streams that are equal. + /// + /// left hand stream + /// left hand stream property + /// The node left. + /// right hand stream + /// right hand stream property + /// The node right. + /// + /// true if added and did not exist, false if already known + /// + public bool AddStrictEquals(int streamLeft, String propertyLeft, ExprIdentNode nodeLeft, int streamRight, String propertyRight, ExprIdentNode nodeRight) + { + Check(streamLeft, streamRight); + if (propertyLeft == null || propertyRight == null) + { + throw new ArgumentException("Null property names supplied"); + } + + if (streamLeft == streamRight) + { + throw new ArgumentException("Streams supplied are the same"); + } + + var addedLeft = InternalAddEquals(streamLeft, propertyLeft, nodeLeft, streamRight, nodeRight); + var addedRight = InternalAddEquals(streamRight, propertyRight, nodeRight, streamLeft, nodeLeft); + return addedLeft || addedRight; + } + + public bool IsNavigableAtAll(int streamFrom, int streamTo) + { + var key = new QueryGraphKey(streamFrom, streamTo); + var value = _streamJoinMap.Get(key); + return value != null && !value.IsEmptyNotNavigable; + } + + /// Returns set of streams that the given stream is navigable to. + /// from stream number + /// set of streams related to this stream, or empty set if none + public ICollection GetNavigableStreams(int streamFrom) + { + ICollection result = new HashSet(); + for (var i = 0; i < _numStreams; i++) + { + if (IsNavigableAtAll(streamFrom, i)) + { + result.Add(i); + } + } + return result; + } + + public QueryGraphValue GetGraphValue(int streamLookup, int streamIndexed) + { + var key = new QueryGraphKey(streamLookup, streamIndexed); + var value = _streamJoinMap.Get(key); + if (value != null) + { + return value; + } + return new QueryGraphValue(); + } + + /// + /// Fill in equivalent key properties (navigation entries) on all streams. For example, if + /// a=b and b=c then addRelOpInternal a=c. The method adds new equalivalent key properties + /// until no additional entries to be added are found, ie. several passes can be made. + /// + /// The types per stream. + /// navigablity INFO between streamss + public static void FillEquivalentNav(EventType[] typesPerStream, QueryGraph queryGraph) + { + bool addedEquivalency; + + // Repeat until no more entries were added + do + { + addedEquivalency = false; + + // For each stream-to-stream combination + for (var lookupStream = 0; lookupStream < queryGraph._numStreams; lookupStream++) + { + for (var indexedStream = 0; indexedStream < queryGraph._numStreams; indexedStream++) + { + if (lookupStream == indexedStream) + { + continue; + } + + var added = FillEquivalentNav(typesPerStream, queryGraph, lookupStream, indexedStream); + if (added) + { + addedEquivalency = true; + } + } + } + } + while (addedEquivalency); + } + + /* + * Looks at the key and index (aka. left and right) properties of the 2 streams and checks + * for each property if any equivalent index properties exist for other streams. + */ + private static bool FillEquivalentNav(EventType[] typesPerStream, QueryGraph queryGraph, int lookupStream, int indexedStream) + { + var addedEquivalency = false; + + var value = queryGraph.GetGraphValue(lookupStream, indexedStream); + if (value.IsEmptyNotNavigable) + { + return false; + } + + var hashKeys = value.HashKeyProps; + var strictKeyProps = hashKeys.StrictKeys; + var indexProps = hashKeys.Indexed; + + if (strictKeyProps.Count == 0) + { + return false; + } + if (strictKeyProps.Count != indexProps.Count) + { + throw new IllegalStateException("Unexpected key and index property number mismatch"); + } + + for (var i = 0; i < strictKeyProps.Count; i++) + { + if (strictKeyProps[i] == null) + { + continue; // not a strict key + } + + var added = FillEquivalentNav(typesPerStream, queryGraph, lookupStream, strictKeyProps[i], indexedStream, indexProps[i]); + if (added) + { + addedEquivalency = true; + } + } + + return addedEquivalency; + } + + /// + /// Looks at the key and index (aka. left and right) properties of the 2 streams and checks + /// for each property if any equivalent index properties exist for other streams. + /// + /// Example: s0.p0 = s1.p1 and s1.p1 = s2.p2 ==> therefore s0.p0 = s2.p2 + /// ==> look stream s0, property p0; indexed stream s1, property p1 + /// Is there any other lookup stream that has stream 1 and property p1 as index property? ==> this is stream s2, p2 + /// Add navigation entry between stream s0 and property p0 to stream s2, property p2 + /// + private static bool FillEquivalentNav(EventType[] typesPerStream, QueryGraph queryGraph, int lookupStream, String keyProp, int indexedStream, String indexProp) + { + var addedEquivalency = false; + + for (var otherStream = 0; otherStream < queryGraph._numStreams; otherStream++) + { + if ((otherStream == lookupStream) || (otherStream == indexedStream)) + { + continue; + } + + var value = queryGraph.GetGraphValue(otherStream, indexedStream); + var hashKeys = value.HashKeyProps; + + var otherStrictKeyProps = hashKeys.StrictKeys; + var otherIndexProps = hashKeys.Indexed; + var otherPropertyNum = -1; + + if (otherIndexProps == null) + { + continue; + } + + for (var i = 0; i < otherIndexProps.Count; i++) + { + if (otherIndexProps[i].Equals(indexProp)) + { + otherPropertyNum = i; + break; + } + } + + if (otherPropertyNum != -1) + { + if (otherStrictKeyProps[otherPropertyNum] != null) + { + ExprIdentNode identNodeLookup = new ExprIdentNodeImpl(typesPerStream[lookupStream], keyProp, lookupStream); + ExprIdentNode identNodeOther = new ExprIdentNodeImpl(typesPerStream[otherStream], otherStrictKeyProps[otherPropertyNum], otherStream); + var added = queryGraph.AddStrictEquals(lookupStream, keyProp, identNodeLookup, otherStream, otherStrictKeyProps[otherPropertyNum], identNodeOther); + if (added) + { + addedEquivalency = true; + } + } + } + } + + return addedEquivalency; + } + + public override String ToString() + { + var writer = new StringWriter(); + + var count = 0; + foreach (var entry in _streamJoinMap) + { + count++; + writer.WriteLine("Record " + count + ": key=" + entry.Key); + writer.WriteLine(" value=" + entry.Value); + } + + return writer.ToString(); + } + + public void AddRangeStrict(int streamNumStart, ExprIdentNode propertyStartExpr, + int streamNumEnd, ExprIdentNode propertyEndExpr, + int streamNumValue, ExprIdentNode propertyValueExpr, + bool includeStart, bool includeEnd, bool isInverted) + { + Check(streamNumStart, streamNumValue); + Check(streamNumEnd, streamNumValue); + + // add as a range if the endpoints are from the same stream + if (streamNumStart == streamNumEnd && streamNumStart != streamNumValue) + { + var rangeOp = QueryGraphRangeEnumExtensions.GetRangeOp(includeStart, includeEnd, isInverted); + InternalAddRange(streamNumStart, streamNumValue, rangeOp, propertyStartExpr, propertyEndExpr, propertyValueExpr); + InternalAddRelOp(streamNumValue, streamNumStart, propertyValueExpr, QueryGraphRangeEnum.GREATER_OR_EQUAL, propertyEndExpr, false); + InternalAddRelOp(streamNumValue, streamNumStart, propertyValueExpr, QueryGraphRangeEnum.LESS_OR_EQUAL, propertyStartExpr, false); + } + else + { + // endpoints from a different stream, add individually + if (streamNumValue != streamNumStart) + { + // read propertyValue >= propertyStart + InternalAddRelOp(streamNumStart, streamNumValue, propertyStartExpr, QueryGraphRangeEnum.GREATER_OR_EQUAL, propertyValueExpr, true); + // read propertyStart <= propertyValue + InternalAddRelOp(streamNumValue, streamNumStart, propertyValueExpr, QueryGraphRangeEnum.LESS_OR_EQUAL, propertyStartExpr, true); + } + + if (streamNumValue != streamNumEnd) + { + // read propertyValue <= propertyEnd + InternalAddRelOp(streamNumEnd, streamNumValue, propertyEndExpr, QueryGraphRangeEnum.LESS_OR_EQUAL, propertyValueExpr, true); + // read propertyEnd >= propertyValue + InternalAddRelOp(streamNumValue, streamNumEnd, propertyValueExpr, QueryGraphRangeEnum.GREATER_OR_EQUAL, propertyEndExpr, true); + } + } + } + + public void AddRelationalOpStrict(int streamIdLeft, ExprIdentNode propertyLeftExpr, + int streamIdRight, ExprIdentNode propertyRightExpr, + RelationalOpEnum relationalOpEnum) + { + Check(streamIdLeft, streamIdRight); + InternalAddRelOp(streamIdLeft, streamIdRight, propertyLeftExpr, QueryGraphRangeEnumExtensions.MapFrom(relationalOpEnum.Reversed()), propertyRightExpr, false); + InternalAddRelOp(streamIdRight, streamIdLeft, propertyRightExpr, QueryGraphRangeEnumExtensions.MapFrom(relationalOpEnum), propertyLeftExpr, false); + } + + public void AddUnkeyedExpression(int indexedStream, ExprIdentNode indexedProp, ExprNode exprNodeNoIdent) + { + if (indexedStream < 0 || indexedStream >= _numStreams) + { + throw new ArgumentException("Invalid indexed stream " + indexedStream); + } + + for (var i = 0; i < _numStreams; i++) + { + if (i != indexedStream) + { + InternalAddEqualsUnkeyed(i, indexedStream, indexedProp, exprNodeNoIdent); + } + } + } + + public void AddKeyedExpression(int indexedStream, ExprIdentNode indexedProp, int keyExprStream, ExprNode exprNodeNoIdent) + { + Check(indexedStream, keyExprStream); + InternalAddEqualsNoProp(keyExprStream, indexedStream, indexedProp, exprNodeNoIdent); + } + + private void Check(int indexedStream, int keyStream) + { + if (indexedStream < 0 || indexedStream >= _numStreams) + { + throw new ArgumentException("Invalid indexed stream " + indexedStream); + } + if (keyStream < 0 || keyStream >= _numStreams) + { + throw new ArgumentException("Invalid key stream " + keyStream); + } + if (keyStream == indexedStream) + { + throw new ArgumentException("Invalid key stream equals indexed stream " + keyStream); + } + } + + public void AddRangeExpr(int indexedStream, ExprIdentNode indexedProp, ExprNode startNode, int? optionalStartStreamNum, ExprNode endNode, int? optionalEndStreamNum) + { + if (optionalStartStreamNum == null && optionalEndStreamNum == null) + { + for (var i = 0; i < _numStreams; i++) + { + if (i == indexedStream) + { + continue; + } + InternalAddRange(i, indexedStream, QueryGraphRangeEnum.RANGE_CLOSED, startNode, endNode, indexedProp); + } + return; + } + + optionalStartStreamNum = optionalStartStreamNum ?? -1; + optionalEndStreamNum = optionalEndStreamNum ?? -1; + + // add for a specific stream only + if (optionalStartStreamNum.Equals(optionalEndStreamNum) || optionalEndStreamNum.Equals(-1)) + { + InternalAddRange(optionalStartStreamNum.Value, indexedStream, QueryGraphRangeEnum.RANGE_CLOSED, startNode, endNode, indexedProp); + } + if (optionalStartStreamNum.Equals(-1)) + { + InternalAddRange(optionalEndStreamNum.Value, indexedStream, QueryGraphRangeEnum.RANGE_CLOSED, startNode, endNode, indexedProp); + } + } + + public void AddRelationalOp(int indexedStream, ExprIdentNode indexedProp, int? keyStreamNum, ExprNode exprNodeNoIdent, RelationalOpEnum relationalOpEnum) + { + if (keyStreamNum == null) + { + for (var i = 0; i < _numStreams; i++) + { + if (i == indexedStream) + { + continue; + } + InternalAddRelOp(i, indexedStream, exprNodeNoIdent, QueryGraphRangeEnumExtensions.MapFrom(relationalOpEnum), indexedProp, false); + } + return; + } + + // add for a specific stream only + InternalAddRelOp(keyStreamNum.Value, indexedStream, exprNodeNoIdent, QueryGraphRangeEnumExtensions.MapFrom(relationalOpEnum), indexedProp, false); + } + + public void AddInSetSingleIndex(int testStreamNum, ExprNode testPropExpr, int setStreamNum, ExprNode[] setPropExpr) + { + Check(testStreamNum, setStreamNum); + InternalAddInKeywordSingleIndex(setStreamNum, testStreamNum, testPropExpr, setPropExpr); + } + + public void AddInSetSingleIndexUnkeyed(int testStreamNum, ExprNode testPropExpr, ExprNode[] setPropExpr) + { + for (var i = 0; i < _numStreams; i++) + { + if (i != testStreamNum) + { + InternalAddInKeywordSingleIndex(i, testStreamNum, testPropExpr, setPropExpr); + } + } + } + + public void AddInSetMultiIndex(int testStreamNum, ExprNode testPropExpr, int setStreamNum, ExprNode[] setPropExpr) + { + Check(testStreamNum, setStreamNum); + InternalAddInKeywordMultiIndex(testStreamNum, setStreamNum, testPropExpr, setPropExpr); + } + + public void AddInSetMultiIndexUnkeyed(ExprNode testPropExpr, int setStreamNum, ExprNode[] setPropExpr) + { + for (var i = 0; i < _numStreams; i++) + { + if (i != setStreamNum) + { + InternalAddInKeywordMultiIndex(i, setStreamNum, testPropExpr, setPropExpr); + } + } + } + + private void InternalAddRange(int streamKey, int streamValue, QueryGraphRangeEnum rangeOp, ExprNode propertyStartExpr, ExprNode propertyEndExpr, ExprIdentNode propertyValueExpr) + { + if (_nToZeroAnalysis && streamValue != 0) + { + return; + } + if (_optionalHint != null && _optionalHint.Filter(streamKey, streamValue, ExcludePlanFilterOperatorType.RELOP)) + { + return; + } + var valueLeft = GetCreateValue(streamKey, streamValue); + valueLeft.AddRange(rangeOp, propertyStartExpr, propertyEndExpr, propertyValueExpr); + } + + private void InternalAddRelOp(int streamKey, int streamValue, ExprNode keyExpr, QueryGraphRangeEnum rangeEnum, ExprIdentNode valueExpr, bool isBetweenOrIn) + { + if (_nToZeroAnalysis && streamValue != 0) + { + return; + } + if (_optionalHint != null && _optionalHint.Filter(streamKey, streamValue, ExcludePlanFilterOperatorType.RELOP)) + { + return; + } + var value = GetCreateValue(streamKey, streamValue); + value.AddRelOp(keyExpr, rangeEnum, valueExpr, isBetweenOrIn); + } + + private bool InternalAddEquals(int streamLookup, String propertyLookup, ExprIdentNode propertyLookupNode, int streamIndexed, ExprIdentNode propertyIndexedNode) + { + if (_nToZeroAnalysis && streamIndexed != 0) + { + return false; + } + if (_optionalHint != null && _optionalHint.Filter(streamLookup, propertyIndexedNode.StreamId, ExcludePlanFilterOperatorType.EQUALS, propertyLookupNode, propertyIndexedNode)) + { + return false; + } + var value = GetCreateValue(streamLookup, streamIndexed); + return value.AddStrictCompare(propertyLookup, propertyLookupNode, propertyIndexedNode); + } + + private void InternalAddEqualsNoProp(int keyExprStream, int indexedStream, ExprIdentNode indexedProp, ExprNode exprNodeNoIdent) + { + if (_nToZeroAnalysis && indexedStream != 0) + { + return; + } + if (_optionalHint != null && _optionalHint.Filter(keyExprStream, indexedStream, ExcludePlanFilterOperatorType.EQUALS)) + { + return; + } + var value = GetCreateValue(keyExprStream, indexedStream); + value.AddKeyedExpr(indexedProp, exprNodeNoIdent); + } + + private void InternalAddEqualsUnkeyed(int streamKey, int streamValue, ExprIdentNode indexedProp, ExprNode exprNodeNoIdent) + { + if (_nToZeroAnalysis && streamValue != 0) + { + return; + } + if (_optionalHint != null && _optionalHint.Filter(streamKey, streamValue, ExcludePlanFilterOperatorType.EQUALS)) + { + return; + } + var value = GetCreateValue(streamKey, streamValue); + value.AddUnkeyedExpr(indexedProp, exprNodeNoIdent); + } + + private void InternalAddInKeywordSingleIndex(int streamKey, int streamValue, ExprNode testPropExpr, ExprNode[] setPropExpr) + { + if (_nToZeroAnalysis && streamValue != 0) + { + return; + } + if (_optionalHint != null && _optionalHint.Filter(streamKey, streamValue, ExcludePlanFilterOperatorType.INKW)) + { + return; + } + var valueSingleIdx = GetCreateValue(streamKey, streamValue); + valueSingleIdx.AddInKeywordSingleIdx(testPropExpr, setPropExpr); + } + + private void InternalAddInKeywordMultiIndex(int streamKey, int streamValue, ExprNode testPropExpr, ExprNode[] setPropExpr) + { + if (_nToZeroAnalysis && streamValue != 0) + { + return; + } + if (_optionalHint != null && _optionalHint.Filter(streamKey, streamValue, ExcludePlanFilterOperatorType.INKW)) + { + return; + } + var value = GetCreateValue(streamKey, streamValue); + value.AddInKeywordMultiIdx(testPropExpr, setPropExpr); + } + + private QueryGraphValue GetCreateValue(int streamKey, int streamValue) + { + Check(streamValue, streamKey); + var key = new QueryGraphKey(streamKey, streamValue); + var value = _streamJoinMap.Get(key); + if (value == null) + { + value = new QueryGraphValue(); + _streamJoinMap.Put(key, value); + } + return value; + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraphKey.cs b/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraphKey.cs new file mode 100755 index 000000000..6947aca3d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraphKey.cs @@ -0,0 +1,58 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.collection; + + +namespace com.espertech.esper.epl.join.plan +{ + /// + /// Value consisting of 2 integer stream numbers, for use by . + /// + public class QueryGraphKey + { + private readonly UniformPair _streams; + + /// Ctor. + /// from stream + /// to stream + public QueryGraphKey(int streamOne, int streamTwo) + { + _streams = new UniformPair(streamOne, streamTwo); + } + + public override bool Equals(Object obj) + { + if (this == obj) + { + return true; + } + + if (!(obj is QueryGraphKey)) + { + return false; + } + + var other = (QueryGraphKey) obj; + return other._streams.Equals(_streams); + } + + public override int GetHashCode() + { + return _streams.GetHashCode(); + } + + public override String ToString() + { + return "QueryGraphKey " + _streams.First + " and " + _streams.Second; + } + } + +} diff --git a/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraphRangeConsolidateDesc.cs b/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraphRangeConsolidateDesc.cs new file mode 100755 index 000000000..ba9471841 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraphRangeConsolidateDesc.cs @@ -0,0 +1,21 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.join.plan +{ + public class QueryGraphRangeConsolidateDesc { + public QueryGraphRangeConsolidateDesc(QueryGraphRangeEnum type, bool reverse) { + RangeType = type; + IsReverse = reverse; + } + + public QueryGraphRangeEnum RangeType { get; private set; } + + public bool IsReverse { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraphRangeEnum.cs b/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraphRangeEnum.cs new file mode 100755 index 000000000..038620167 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraphRangeEnum.cs @@ -0,0 +1,239 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using com.espertech.esper.compat; +using com.espertech.esper.filter; +using com.espertech.esper.type; + +namespace com.espertech.esper.epl.join.plan +{ + public enum QueryGraphRangeEnum + { + /// Less (<). + LESS, + + /// Less or equal (<=). + LESS_OR_EQUAL, + + /// Greater or equal (>=). + GREATER_OR_EQUAL, + + /// Greater (>). + GREATER, + + /// Range contains neither endpoint, i.e. (a,b) + RANGE_OPEN, + + /// Range contains low and high endpoint, i.e. [a,b] + RANGE_CLOSED, + + /// Range includes low endpoint but not high endpoint, i.e. [a,b) + RANGE_HALF_OPEN, + + /// Range includes high endpoint but not low endpoint, i.e. (a,b] + RANGE_HALF_CLOSED, + + /// Inverted-Range contains neither endpoint, i.e. (a,b) + NOT_RANGE_OPEN, + + /// Inverted-Range contains low and high endpoint, i.e. [a,b] + NOT_RANGE_CLOSED, + + /// Inverted-Range includes low endpoint but not high endpoint, i.e. [a,b) + NOT_RANGE_HALF_OPEN, + + /// Inverted-Range includes high endpoint but not low endpoint, i.e. (a,b] + NOT_RANGE_HALF_CLOSED + } + + public static class QueryGraphRangeEnumExtensions + { + public static QueryGraphRangeEnum MapFrom(this FilterOperator op) + { + switch (op) + { + case FilterOperator.GREATER: + return QueryGraphRangeEnum.GREATER; + case FilterOperator.GREATER_OR_EQUAL: + return QueryGraphRangeEnum.GREATER_OR_EQUAL; + case FilterOperator.LESS: + return QueryGraphRangeEnum.LESS; + case FilterOperator.LESS_OR_EQUAL: + return QueryGraphRangeEnum.LESS_OR_EQUAL; + case FilterOperator.RANGE_OPEN: + return QueryGraphRangeEnum.RANGE_OPEN; + case FilterOperator.RANGE_HALF_CLOSED: + return QueryGraphRangeEnum.RANGE_HALF_CLOSED; + case FilterOperator.RANGE_HALF_OPEN: + return QueryGraphRangeEnum.RANGE_HALF_OPEN; + case FilterOperator.RANGE_CLOSED: + return QueryGraphRangeEnum.RANGE_CLOSED; + case FilterOperator.NOT_RANGE_OPEN: + return QueryGraphRangeEnum.NOT_RANGE_OPEN; + case FilterOperator.NOT_RANGE_HALF_CLOSED: + return QueryGraphRangeEnum.NOT_RANGE_HALF_CLOSED; + case FilterOperator.NOT_RANGE_HALF_OPEN: + return QueryGraphRangeEnum.NOT_RANGE_HALF_OPEN; + case FilterOperator.NOT_RANGE_CLOSED: + return QueryGraphRangeEnum.NOT_RANGE_CLOSED; + default: + throw new ArgumentException("invalid filter", "op"); + } + } + + public static QueryGraphRangeEnum MapFrom(RelationalOpEnum relationalOpEnum) + { + if (relationalOpEnum == RelationalOpEnum.GE) + { + return QueryGraphRangeEnum.GREATER_OR_EQUAL; + } + if (relationalOpEnum == RelationalOpEnum.GT) + { + return QueryGraphRangeEnum.GREATER; + } + if (relationalOpEnum == RelationalOpEnum.LT) + { + return QueryGraphRangeEnum.LESS; + } + if (relationalOpEnum == RelationalOpEnum.LE) + { + return QueryGraphRangeEnum.LESS_OR_EQUAL; + } + + throw new ArgumentException("Failed to map code " + relationalOpEnum); + } + + public static bool IsRange(this QueryGraphRangeEnum @enum) + { + switch (@enum) + { + case QueryGraphRangeEnum.LESS: + return false; + case QueryGraphRangeEnum.LESS_OR_EQUAL: + return false; + case QueryGraphRangeEnum.GREATER_OR_EQUAL: + return false; + case QueryGraphRangeEnum.GREATER: + return false; + case QueryGraphRangeEnum.RANGE_OPEN: + return true; + case QueryGraphRangeEnum.RANGE_CLOSED: + return true; + case QueryGraphRangeEnum.RANGE_HALF_OPEN: + return true; + case QueryGraphRangeEnum.RANGE_HALF_CLOSED: + return true; + case QueryGraphRangeEnum.NOT_RANGE_OPEN: + return true; + case QueryGraphRangeEnum.NOT_RANGE_CLOSED: + return true; + case QueryGraphRangeEnum.NOT_RANGE_HALF_OPEN: + return true; + case QueryGraphRangeEnum.NOT_RANGE_HALF_CLOSED: + return true; + default: + throw new ArgumentException("invalid value", "enum"); + } + } + + public static bool IsIncludeStart(this QueryGraphRangeEnum @enum) + { + if (!@enum.IsRange()) + { + throw new UnsupportedOperationException("Cannot determine endpoint-start included for op " + @enum); + } + return @enum == QueryGraphRangeEnum.RANGE_HALF_OPEN || + @enum == QueryGraphRangeEnum.RANGE_CLOSED || + @enum == QueryGraphRangeEnum.NOT_RANGE_HALF_OPEN || + @enum == QueryGraphRangeEnum.NOT_RANGE_CLOSED; + } + + public static bool IsIncludeEnd(this QueryGraphRangeEnum @enum) + { + if (!@enum.IsRange()) + { + throw new UnsupportedOperationException("Cannot determine endpoint-end included for op " + @enum); + } + return @enum == QueryGraphRangeEnum.RANGE_HALF_CLOSED || + @enum == QueryGraphRangeEnum.RANGE_CLOSED || + @enum == QueryGraphRangeEnum.NOT_RANGE_HALF_CLOSED || + @enum == QueryGraphRangeEnum.NOT_RANGE_CLOSED; + } + + public static QueryGraphRangeEnum GetRangeOp(bool includeStart, bool includeEnd, bool isInverted) + { + if (!isInverted) + { + if (includeStart) + { + return includeEnd + ? QueryGraphRangeEnum.RANGE_CLOSED + : QueryGraphRangeEnum.RANGE_HALF_OPEN; + } + + return includeEnd + ? QueryGraphRangeEnum.RANGE_HALF_CLOSED + : QueryGraphRangeEnum.RANGE_OPEN; + } + + if (includeStart) + { + return includeEnd + ? QueryGraphRangeEnum.NOT_RANGE_CLOSED + : QueryGraphRangeEnum.NOT_RANGE_HALF_OPEN; + } + + return includeEnd ? + QueryGraphRangeEnum.NOT_RANGE_HALF_CLOSED : + QueryGraphRangeEnum.NOT_RANGE_OPEN; + } + + public static bool IsRangeInverted(this QueryGraphRangeEnum @enum) + { + return IsRange(@enum) && + (@enum == QueryGraphRangeEnum.NOT_RANGE_HALF_CLOSED || + @enum == QueryGraphRangeEnum.NOT_RANGE_HALF_OPEN || + @enum == QueryGraphRangeEnum.NOT_RANGE_OPEN || + @enum == QueryGraphRangeEnum.NOT_RANGE_CLOSED); + } + + public static string GetStringOp(this QueryGraphRangeEnum @enum) + { + switch (@enum) + { + case QueryGraphRangeEnum.LESS: + return "<"; + case QueryGraphRangeEnum.LESS_OR_EQUAL: + return "<="; + case QueryGraphRangeEnum.GREATER_OR_EQUAL: + return ">="; + case QueryGraphRangeEnum.GREATER: + return ">"; + case QueryGraphRangeEnum.RANGE_OPEN: + return "(,)"; + case QueryGraphRangeEnum.RANGE_CLOSED: + return "[,]"; + case QueryGraphRangeEnum.RANGE_HALF_OPEN: + return "[,)"; + case QueryGraphRangeEnum.RANGE_HALF_CLOSED: + return "(,]"; + case QueryGraphRangeEnum.NOT_RANGE_OPEN: + return "-(,)"; + case QueryGraphRangeEnum.NOT_RANGE_CLOSED: + return "-[,]"; + case QueryGraphRangeEnum.NOT_RANGE_HALF_OPEN: + return "-[,)"; + case QueryGraphRangeEnum.NOT_RANGE_HALF_CLOSED: + return "-(,])"; + default: + throw new ArgumentException("invalid value", "enum"); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraphRangeUtil.cs b/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraphRangeUtil.cs new file mode 100755 index 000000000..a81ae5d5e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraphRangeUtil.cs @@ -0,0 +1,57 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.epl.join.plan +{ + /// + /// Property lists stored as a value for each stream-to-stream relationship, for use by + /// . + /// + public class QueryGraphRangeUtil + { + private static readonly IDictionary OPS_TABLE = + new Dictionary(); + + static QueryGraphRangeUtil() + { + Add(QueryGraphRangeEnum.LESS_OR_EQUAL, QueryGraphRangeEnum.GREATER_OR_EQUAL, QueryGraphRangeEnum.RANGE_CLOSED); + Add(QueryGraphRangeEnum.LESS, QueryGraphRangeEnum.GREATER, QueryGraphRangeEnum.RANGE_OPEN); + Add(QueryGraphRangeEnum.LESS_OR_EQUAL, QueryGraphRangeEnum.GREATER, QueryGraphRangeEnum.RANGE_HALF_CLOSED); + Add(QueryGraphRangeEnum.LESS, QueryGraphRangeEnum.GREATER_OR_EQUAL, QueryGraphRangeEnum.RANGE_HALF_OPEN); + } + + private static void Add(QueryGraphRangeEnum opOne, QueryGraphRangeEnum opTwo, QueryGraphRangeEnum range) + { + MultiKeyUntyped keyOne = GetKey(opOne, opTwo); + OPS_TABLE.Put(keyOne, new QueryGraphRangeConsolidateDesc(range, false)); + MultiKeyUntyped keyRev = GetKey(opTwo, opOne); + OPS_TABLE.Put(keyRev, new QueryGraphRangeConsolidateDesc(range, true)); + } + + private static MultiKeyUntyped GetKey(QueryGraphRangeEnum op1, QueryGraphRangeEnum op2) + { + return new MultiKeyUntyped( + new Object[] + { + op1, + op2 + }); + } + + public static QueryGraphRangeConsolidateDesc GetCanConsolidate(QueryGraphRangeEnum op1, QueryGraphRangeEnum op2) + { + return OPS_TABLE.Get(GetKey(op1, op2)); + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraphValue.cs b/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraphValue.cs new file mode 100755 index 000000000..471dc61a6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraphValue.cs @@ -0,0 +1,273 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.join.plan +{ + /// + /// Property lists stored as a value for each stream-to-stream relationship, for use by . + /// + public class QueryGraphValue + { + private readonly IList _items; + + /// + /// Ctor. + /// + public QueryGraphValue() + { + _items = new List(); + } + + public bool IsEmptyNotNavigable + { + get { return _items.IsEmpty(); } + } + + public IList Items + { + get { return _items; } + } + + /// + /// Add key and index property. + /// + /// key property + /// index property + /// true if added and either property did not exist, false if either already existed + public bool AddStrictCompare(string keyProperty, ExprIdentNode keyPropNode, ExprIdentNode indexPropertyIdent) + { + var value = FindIdentEntry(indexPropertyIdent); + if (value != null && value.Entry is QueryGraphValueEntryHashKeyedExpr) { + // if this index property exists and is compared to a constant, ignore the index prop + var expr = (QueryGraphValueEntryHashKeyedExpr) value.Entry; + if (expr.IsConstant) { + return false; + } + } + if (value != null && value.Entry is QueryGraphValueEntryHashKeyedProp) { + return false; // second comparison, ignore + } + + _items.Add(new QueryGraphValueDesc(new ExprNode[]{indexPropertyIdent}, new QueryGraphValueEntryHashKeyedProp(keyPropNode, keyProperty))); + return true; + } + + public void AddRange(QueryGraphRangeEnum rangeType, ExprNode propertyStart, ExprNode propertyEnd, ExprIdentNode propertyValueIdent) + { + if (!rangeType.IsRange()) + { + throw new ArgumentException("Expected range type, received " + rangeType); + } + + // duplicate can be removed right away + if (FindIdentEntry(propertyValueIdent) != null) { + return; + } + + _items.Add(new QueryGraphValueDesc(new ExprNode[] {propertyValueIdent}, new QueryGraphValueEntryRangeIn(rangeType, propertyStart, propertyEnd, true))); + } + + public void AddRelOp(ExprNode propertyKey, QueryGraphRangeEnum op, ExprIdentNode propertyValueIdent, bool isBetweenOrIn) + { + // Note: Read as follows: + // Console.WriteLine("If I have an index on '" + propertyValue + "' I'm evaluating " + propertyKey + " and finding all values of " + propertyValue + " " + op + " then " + propertyKey); + + // Check if there is an opportunity to convert this to a range or remove an earlier specification + var existing = FindIdentEntry(propertyValueIdent); + if (existing == null) { + _items.Add(new QueryGraphValueDesc(new ExprNode[]{propertyValueIdent}, new QueryGraphValueEntryRangeRelOp(op, propertyKey, isBetweenOrIn))); + return; + } + + if (!(existing.Entry is QueryGraphValueEntryRangeRelOp)) { + return; // another comparison exists already, don't add range + } + + var relOp = (QueryGraphValueEntryRangeRelOp) existing.Entry; + var opsDesc = QueryGraphRangeUtil.GetCanConsolidate(op, relOp.RangeType); + if (opsDesc != null) { + var start = !opsDesc.IsReverse ? relOp.Expression : propertyKey; + var end = !opsDesc.IsReverse ? propertyKey : relOp.Expression; + _items.Remove(existing); + AddRange(opsDesc.RangeType, start, end, propertyValueIdent); + } + } + + public void AddUnkeyedExpr(ExprIdentNode indexedPropIdent, ExprNode exprNodeNoIdent) { + _items.Add(new QueryGraphValueDesc(new ExprNode[] {indexedPropIdent}, new QueryGraphValueEntryHashKeyedExpr(exprNodeNoIdent, false))); + } + + public void AddKeyedExpr(ExprIdentNode indexedPropIdent, ExprNode exprNodeNoIdent) { + _items.Add(new QueryGraphValueDesc(new ExprNode[]{indexedPropIdent}, new QueryGraphValueEntryHashKeyedExpr(exprNodeNoIdent, true))); + } + + public QueryGraphValuePairHashKeyIndex HashKeyProps + { + get + { + IList keys = new List(); + Deque indexed = new ArrayDeque(); + foreach (var desc in _items) + { + if (desc.Entry is QueryGraphValueEntryHashKeyed) + { + var keyprop = (QueryGraphValueEntryHashKeyed) desc.Entry; + keys.Add(keyprop); + indexed.Add(GetSingleIdentNodeProp(desc.IndexExprs)); + } + } + + var strictKeys = new string[indexed.Count]; + var count = 0; + foreach (var desc in _items) + { + if (desc.Entry is QueryGraphValueEntryHashKeyed) + { + if (desc.Entry is QueryGraphValueEntryHashKeyedProp) + { + var keyprop = (QueryGraphValueEntryHashKeyedProp) desc.Entry; + strictKeys[count] = keyprop.KeyProperty; + } + count++; + } + } + + return new QueryGraphValuePairHashKeyIndex(indexed.ToArray(), keys, strictKeys); + } + } + + public QueryGraphValuePairRangeIndex RangeProps + { + get + { + Deque indexed = new ArrayDeque(); + IList keys = new List(); + foreach (var desc in _items) + { + if (desc.Entry is QueryGraphValueEntryRange) + { + var keyprop = (QueryGraphValueEntryRange) desc.Entry; + keys.Add(keyprop); + indexed.Add(GetSingleIdentNodeProp(desc.IndexExprs)); + } + } + return new QueryGraphValuePairRangeIndex(indexed.ToArray(), keys); + } + } + + public override string ToString() + { + var writer = new StringWriter(); + writer.Write("QueryGraphValue "); + var delimiter = ""; + foreach (var desc in _items) { + writer.Write(delimiter); + writer.Write(ExprNodeUtility.ToExpressionStringMinPrecedence(desc.IndexExprs)); + writer.Write(": "); + writer.Write(desc.Entry.ToString()); + delimiter = ", "; + } + return writer.ToString(); + } + + public void AddInKeywordMultiIdx(ExprNode testPropExpr, ExprNode[] setProps) { + _items.Add(new QueryGraphValueDesc(setProps, new QueryGraphValueEntryInKeywordMultiIdx(testPropExpr))); + } + + public void AddInKeywordSingleIdx(ExprNode testPropIdent, ExprNode[] setPropExpr) { + var indexExpressions = new ExprNode[]{testPropIdent}; + var found = FindEntry(indexExpressions); + + var setExpressions = setPropExpr; + if (found != null && found.Entry is QueryGraphValueEntryInKeywordSingleIdx) { + var existing = (QueryGraphValueEntryInKeywordSingleIdx) found.Entry; + setExpressions = (ExprNode[]) CollectionUtil.AddArrays(existing.KeyExprs, setPropExpr); + _items.Remove(found); + } + _items.Add(new QueryGraphValueDesc(new ExprNode[]{testPropIdent}, new QueryGraphValueEntryInKeywordSingleIdx(setExpressions))); + } + + public QueryGraphValuePairInKWSingleIdx InKeywordSingles + { + get + { + IList indexedProps = new List(); + IList single = new List(); + foreach (var desc in _items) + { + if (desc.Entry is QueryGraphValueEntryInKeywordSingleIdx) + { + var keyprop = (QueryGraphValueEntryInKeywordSingleIdx) desc.Entry; + single.Add(keyprop); + indexedProps.Add(GetSingleIdentNodeProp(desc.IndexExprs)); + } + } + return new QueryGraphValuePairInKWSingleIdx(indexedProps.ToArray(), single); + } + } + + public IList InKeywordMulti + { + get + { + IList multi = new List(); + foreach (var desc in _items) + { + if (desc.Entry is QueryGraphValueEntryInKeywordMultiIdx) + { + var keyprop = (QueryGraphValueEntryInKeywordMultiIdx) desc.Entry; + multi.Add(new QueryGraphValuePairInKWMultiIdx(desc.IndexExprs, keyprop)); + } + } + return multi; + } + } + + private QueryGraphValueDesc FindIdentEntry(ExprIdentNode search) { + foreach (var desc in _items) { + if (desc.IndexExprs.Length > 1 || !(desc.IndexExprs[0] is ExprIdentNode)) { + continue; + } + var other = (ExprIdentNode) desc.IndexExprs[0]; + if (search.ResolvedPropertyName.Equals(other.ResolvedPropertyName)) { + return desc; + } + } + return null; + } + + private QueryGraphValueDesc FindEntry(ExprNode[] search) { + foreach (var desc in _items) { + if (ExprNodeUtility.DeepEquals(search, desc.IndexExprs)) { + return desc; + } + } + return null; + } + + private string GetSingleIdentNodeProp(ExprNode[] indexExprs) { + if (indexExprs.Length != 1) { + throw new IllegalStateException("Incorrect number of index expressions"); + } + var identNode = (ExprIdentNode) indexExprs[0]; + return identNode.ResolvedPropertyName; + } + } + +} diff --git a/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraphValueDesc.cs b/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraphValueDesc.cs new file mode 100755 index 000000000..842739417 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraphValueDesc.cs @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.join.plan +{ + public class QueryGraphValueDesc + { + public QueryGraphValueDesc(ExprNode[] indexExprs, QueryGraphValueEntry entry) + { + IndexExprs = indexExprs; + Entry = entry; + } + + public ExprNode[] IndexExprs { get; private set; } + + public QueryGraphValueEntry Entry { get; private set; } + } + +} diff --git a/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraphValueEntry.cs b/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraphValueEntry.cs new file mode 100755 index 000000000..47bae51e6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraphValueEntry.cs @@ -0,0 +1,15 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.join.plan +{ + public interface QueryGraphValueEntry + { + } + +} diff --git a/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraphValueEntryHashKeyed.cs b/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraphValueEntryHashKeyed.cs new file mode 100755 index 000000000..f03e7a111 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraphValueEntryHashKeyed.cs @@ -0,0 +1,42 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.join.plan +{ + public abstract class QueryGraphValueEntryHashKeyed : QueryGraphValueEntry + { + protected QueryGraphValueEntryHashKeyed(ExprNode keyExpr) + { + KeyExpr = keyExpr; + } + + public ExprNode KeyExpr { get; private set; } + + public abstract string ToQueryPlan(); + + public static string ToQueryPlan(IList keyProperties) + { + StringWriter writer = new StringWriter(); + String delimiter = ""; + foreach (QueryGraphValueEntryHashKeyed item in keyProperties) + { + writer.Write(delimiter); + writer.Write(item.ToQueryPlan()); + delimiter = ", "; + } + return writer.ToString(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraphValueEntryHashKeyedExpr.cs b/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraphValueEntryHashKeyedExpr.cs new file mode 100755 index 000000000..358dcdef0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraphValueEntryHashKeyedExpr.cs @@ -0,0 +1,36 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.join.plan +{ + public class QueryGraphValueEntryHashKeyedExpr : QueryGraphValueEntryHashKeyed + { + public QueryGraphValueEntryHashKeyedExpr(ExprNode keyExpr, bool requiresKey) + : base(keyExpr) + { + IsRequiresKey = requiresKey; + } + + public bool IsRequiresKey { get; private set; } + + public bool IsConstant + { + get { return ExprNodeUtility.IsConstantValueExpr(KeyExpr); } + } + + public override String ToQueryPlan() + { + return KeyExpr.ToExpressionStringMinPrecedenceSafe(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraphValueEntryHashKeyedProp.cs b/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraphValueEntryHashKeyedProp.cs new file mode 100755 index 000000000..2907caf01 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraphValueEntryHashKeyedProp.cs @@ -0,0 +1,43 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.join.plan +{ + public class QueryGraphValueEntryHashKeyedProp : QueryGraphValueEntryHashKeyed + { + public QueryGraphValueEntryHashKeyedProp(ExprNode keyExpr, String keyProperty) + : base(keyExpr) + { + KeyProperty = keyProperty; + } + + public string KeyProperty { get; private set; } + + public override String ToQueryPlan() + { + return KeyProperty; + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + /// 2 + public override string ToString() + { + return string.Format("KeyProperty: {0}", KeyProperty); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraphValueEntryInKeywordMultiIdx.cs b/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraphValueEntryInKeywordMultiIdx.cs new file mode 100755 index 000000000..5727b4f0a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraphValueEntryInKeywordMultiIdx.cs @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.join.plan +{ + [Serializable] + public class QueryGraphValueEntryInKeywordMultiIdx : QueryGraphValueEntry + { + public QueryGraphValueEntryInKeywordMultiIdx(ExprNode keyExpr) + { + KeyExpr = keyExpr; + } + + public ExprNode KeyExpr { get; private set; } + + public String ToQueryPlan() + { + return "in-keyword multi-indexed single keyed lookup " + ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(KeyExpr); + } + } + +} diff --git a/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraphValueEntryInKeywordSingleIdx.cs b/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraphValueEntryInKeywordSingleIdx.cs new file mode 100755 index 000000000..e32a0e9dc --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraphValueEntryInKeywordSingleIdx.cs @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.join.plan +{ + [Serializable] + public class QueryGraphValueEntryInKeywordSingleIdx : QueryGraphValueEntry + { + public QueryGraphValueEntryInKeywordSingleIdx(ExprNode[] keyExprs) + { + KeyExprs = keyExprs; + } + + public ExprNode[] KeyExprs { get; private set; } + + public String ToQueryPlan() + { + return "in-keyword single-indexed multiple key lookup " + ExprNodeUtility.ToExpressionStringMinPrecedence(KeyExprs); + } + } + +} diff --git a/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraphValueEntryRange.cs b/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraphValueEntryRange.cs new file mode 100755 index 000000000..0f288d209 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraphValueEntryRange.cs @@ -0,0 +1,49 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.join.plan +{ + [Serializable] + public abstract class QueryGraphValueEntryRange : QueryGraphValueEntry + { + private readonly QueryGraphRangeEnum _type; + + protected QueryGraphValueEntryRange(QueryGraphRangeEnum type) + { + _type = type; + } + + public QueryGraphRangeEnum RangeType + { + get { return _type; } + } + + public abstract String ToQueryPlan(); + + public abstract ExprNode[] Expressions { get; } + + public static String ToQueryPlan(IEnumerable rangeKeyPairs) + { + var writer = new StringWriter(); + var delimiter = ""; + foreach (QueryGraphValueEntryRange item in rangeKeyPairs) { + writer.Write(delimiter); + writer.Write(item.ToQueryPlan()); + delimiter = ", "; + } + return writer.ToString(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraphValueEntryRangeIn.cs b/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraphValueEntryRangeIn.cs new file mode 100755 index 000000000..b4ccfd52e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraphValueEntryRangeIn.cs @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.join.plan +{ + public class QueryGraphValueEntryRangeIn : QueryGraphValueEntryRange + { + public QueryGraphValueEntryRangeIn(QueryGraphRangeEnum rangeType, ExprNode exprStart, ExprNode exprEnd, bool allowRangeReversal) + : base(rangeType) + { + if (!rangeType.IsRange()) { + throw new ArgumentException("Range type expected but received " + rangeType.GetName()); + } + ExprStart = exprStart; + ExprEnd = exprEnd; + IsAllowRangeReversal = allowRangeReversal; + } + + public bool IsAllowRangeReversal { get; private set; } + + public ExprNode ExprStart { get; private set; } + + public ExprNode ExprEnd { get; private set; } + + public override String ToQueryPlan() + { + return RangeType.GetName(); + } + + public override ExprNode[] Expressions + { + get + { + return new ExprNode[] + { + ExprStart, + ExprEnd + }; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraphValueEntryRangeRelOp.cs b/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraphValueEntryRangeRelOp.cs new file mode 100755 index 000000000..6b798dce1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraphValueEntryRangeRelOp.cs @@ -0,0 +1,44 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.join.plan +{ + public class QueryGraphValueEntryRangeRelOp : QueryGraphValueEntryRange + { + public QueryGraphValueEntryRangeRelOp(QueryGraphRangeEnum type, ExprNode expression, bool isBetweenPart) + : base(type) + { + if (type.IsRange()) { + throw new ArgumentException("Invalid ctor for use with ranges"); + } + Expression = expression; + IsBetweenPart = isBetweenPart; + } + + public ExprNode Expression { get; private set; } + + public bool IsBetweenPart { get; private set; } + + public override String ToQueryPlan() { + return RangeType.GetStringOp() + " on " + Expression.ToExpressionStringMinPrecedenceSafe(); + } + + public override ExprNode[] Expressions + { + get + { + return new ExprNode[] { Expression }; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraphValuePairHashKeyIndex.cs b/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraphValuePairHashKeyIndex.cs new file mode 100755 index 000000000..17a920fa1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraphValuePairHashKeyIndex.cs @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +namespace com.espertech.esper.epl.join.plan +{ + public class QueryGraphValuePairHashKeyIndex + { + public QueryGraphValuePairHashKeyIndex(IList indexed, IList key, IList strictKeys) + { + Indexed = indexed; + Keys = key; + StrictKeys = strictKeys; + } + + public IList Indexed { get; private set; } + + public IList Keys { get; private set; } + + public IList StrictKeys { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraphValuePairInKWMultiIdx.cs b/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraphValuePairInKWMultiIdx.cs new file mode 100755 index 000000000..22b8c81a3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraphValuePairInKWMultiIdx.cs @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.join.plan +{ + public class QueryGraphValuePairInKWMultiIdx + { + public QueryGraphValuePairInKWMultiIdx(ExprNode[] indexed, QueryGraphValueEntryInKeywordMultiIdx key) + { + Indexed = indexed; + Key = key; + } + + public ExprNode[] Indexed { get; private set; } + + public QueryGraphValueEntryInKeywordMultiIdx Key { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraphValuePairInKWSingleIdx.cs b/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraphValuePairInKWSingleIdx.cs new file mode 100755 index 000000000..628ae301e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraphValuePairInKWSingleIdx.cs @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.epl.join.plan +{ + public class QueryGraphValuePairInKWSingleIdx + { + public QueryGraphValuePairInKWSingleIdx(String[] indexed, IList key) + { + Indexed = indexed; + Key = key; + } + + public string[] Indexed { get; private set; } + + public IList Key { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraphValuePairRangeIndex.cs b/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraphValuePairRangeIndex.cs new file mode 100755 index 000000000..8ec118fa7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/plan/QueryGraphValuePairRangeIndex.cs @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +namespace com.espertech.esper.epl.join.plan +{ + public class QueryGraphValuePairRangeIndex + { + public QueryGraphValuePairRangeIndex(IList indexed, IList key) + { + Indexed = indexed; + Keys = key; + } + + public IList Indexed { get; private set; } + + public IList Keys { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/join/plan/QueryPlan.cs b/NEsper.Core/NEsper.Core/epl/join/plan/QueryPlan.cs new file mode 100755 index 000000000..363e43ea0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/plan/QueryPlan.cs @@ -0,0 +1,49 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Text; + +namespace com.espertech.esper.epl.join.plan +{ + /// Contains the query plan for all streams. + public class QueryPlan + { + /// Ctor. + /// specs for indexes to create + /// specs for execution nodes to create + public QueryPlan(QueryPlanIndex[] indexSpecs, + QueryPlanNode[] execNodeSpecs) + { + IndexSpecs = indexSpecs; + ExecNodeSpecs = execNodeSpecs; + } + + /// Return index specs. + /// index specs + public QueryPlanIndex[] IndexSpecs { get; private set; } + + /// Return execution node specs. + /// execution node specs + public QueryPlanNode[] ExecNodeSpecs { get; private set; } + + public override String ToString() + { + return ToQueryPlan(); + } + + public String ToQueryPlan() + { + var buffer = new StringBuilder(); + buffer.Append("QueryPlanNode\n"); + buffer.Append(QueryPlanIndex.Print(IndexSpecs)); + buffer.Append(QueryPlanNode.Print(ExecNodeSpecs)); + return buffer.ToString(); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/join/plan/QueryPlanBuilder.cs b/NEsper.Core/NEsper.Core/epl/join/plan/QueryPlanBuilder.cs new file mode 100755 index 000000000..79c77b350 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/plan/QueryPlanBuilder.cs @@ -0,0 +1,157 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.client.annotation; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.join.@base; +using com.espertech.esper.epl.join.table; +using com.espertech.esper.epl.spec; +using com.espertech.esper.type; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.join.plan +{ + /// Build a query plan based on filtering information. + public class QueryPlanBuilder + { + private static readonly ILog QUERY_PLAN_LOG = LogManager.GetLogger(AuditPath.QUERYPLAN_LOG); + + private static readonly ILog Log = + LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// Build query plan using the filter. + /// + /// - event types for each stream + /// - list of outer join criteria, or null if there are no outer joins + /// - relationships between streams based on filter expressions and outer-join on-criteria + /// - names of streams + /// - dependencies between historical streams + /// - index management, populated for the query plan + /// stream join analysis metadata + /// historicals + /// for logging + /// context + /// annotations + /// if the query plan fails + /// query plan + public static QueryPlan GetPlan( + EventType[] typesPerStream, + OuterJoinDesc[] outerJoinDescList, + QueryGraph queryGraph, + string[] streamNames, + HistoricalViewableDesc historicalViewableDesc, + DependencyGraph dependencyGraph, + HistoricalStreamIndexList[] historicalStreamIndexLists, + StreamJoinAnalysisResult streamJoinAnalysisResult, + bool isQueryPlanLogging, + Attribute[] annotations, + ExprEvaluatorContext exprEvaluatorContext) + { + const string methodName = ".GetPlan "; + + int numStreams = typesPerStream.Length; + if (numStreams < 2) + { + throw new ArgumentException("Number of join stream types is less then 2"); + } + if (outerJoinDescList.Length >= numStreams) + { + throw new ArgumentException("Too many outer join descriptors found"); + } + + if (numStreams == 2) + { + OuterJoinType? outerJoinType = null; + if (outerJoinDescList.Length > 0) + { + outerJoinType = outerJoinDescList[0].OuterJoinType; + } + + QueryPlan queryPlan = TwoStreamQueryPlanBuilder.Build( + typesPerStream, queryGraph, outerJoinType, streamJoinAnalysisResult.UniqueKeys, + streamJoinAnalysisResult.TablesPerStream); + RemoveUnidirectionalAndTable(queryPlan, streamJoinAnalysisResult); + + if (Log.IsDebugEnabled) + { + Log.Debug(methodName + "2-Stream queryPlan=" + queryPlan); + } + return queryPlan; + } + + bool hasPreferMergeJoin = HintEnum.PREFER_MERGE_JOIN.GetHint(annotations) != null; + bool hasForceNestedIter = HintEnum.FORCE_NESTED_ITER.GetHint(annotations) != null; + bool isAllInnerJoins = outerJoinDescList.Length == 0 || + OuterJoinDesc.ConsistsOfAllInnerJoins(outerJoinDescList); + + if (isAllInnerJoins && !hasPreferMergeJoin) + { + QueryPlan queryPlan = NStreamQueryPlanBuilder.Build( + queryGraph, typesPerStream, + historicalViewableDesc, dependencyGraph, historicalStreamIndexLists, + hasForceNestedIter, streamJoinAnalysisResult.UniqueKeys, + streamJoinAnalysisResult.TablesPerStream); + + if (queryPlan != null) + { + RemoveUnidirectionalAndTable(queryPlan, streamJoinAnalysisResult); + + if (Log.IsDebugEnabled) + { + Log.Debug(methodName + "N-Stream inner-join queryPlan=" + queryPlan); + } + return queryPlan; + } + + if (isQueryPlanLogging && QUERY_PLAN_LOG.IsInfoEnabled) + { + Log.Info("Switching to Outer-NStream algorithm for query plan"); + } + } + + QueryPlan queryPlanX = NStreamOuterQueryPlanBuilder.Build( + queryGraph, outerJoinDescList, streamNames, typesPerStream, + historicalViewableDesc, dependencyGraph, historicalStreamIndexLists, exprEvaluatorContext, + streamJoinAnalysisResult.UniqueKeys, + streamJoinAnalysisResult.TablesPerStream); + RemoveUnidirectionalAndTable(queryPlanX, streamJoinAnalysisResult); + return queryPlanX; + } + + // Remove plans for non-unidirectional streams + private static void RemoveUnidirectionalAndTable( + QueryPlan queryPlan, + StreamJoinAnalysisResult streamJoinAnalysisResult) + { + bool allUnidirectional = streamJoinAnalysisResult.IsUnidirectionalAll; + for (int streamNum = 0; streamNum < queryPlan.ExecNodeSpecs.Length; streamNum++) + { + if (allUnidirectional) + { + queryPlan.ExecNodeSpecs[streamNum] = new QueryPlanNodeAllUnidirectionalOuter(streamNum); + } + else + { + bool unidirectional = streamJoinAnalysisResult.IsUnidirectional && + !streamJoinAnalysisResult.UnidirectionalInd[streamNum]; + bool table = streamJoinAnalysisResult.TablesPerStream[streamNum] != null; + if (unidirectional || table) + { + queryPlan.ExecNodeSpecs[streamNum] = new QueryPlanNodeNoOp(); + } + } + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/join/plan/QueryPlanIndex.cs b/NEsper.Core/NEsper.Core/epl/join/plan/QueryPlanIndex.cs new file mode 100755 index 000000000..f54c7f4b8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/plan/QueryPlanIndex.cs @@ -0,0 +1,204 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.join.plan +{ + /// + /// Specifies an index to build as part of an overall query plan. + /// + public class QueryPlanIndex + { + private readonly IDictionary _items; + + public QueryPlanIndex(IDictionary items) + { + if (items == null) + { + throw new ArgumentException("Null value not allowed for items"); + } + _items = items; + } + + public IDictionary Items + { + get { return _items; } + } + + public static QueryPlanIndex MakeIndex(params QueryPlanIndexItem[] items) + { + IDictionary result = new LinkedHashMap(); + foreach (QueryPlanIndexItem item in items) { + result.Put(new TableLookupIndexReqKey(UuidGenerator.Generate()), item); + } + return new QueryPlanIndex(result); + } + + public static QueryPlanIndex MakeIndex(IList indexesSet) + { + IDictionary items = new LinkedHashMap(); + foreach (QueryPlanIndexItem item in indexesSet) { + items.Put(new TableLookupIndexReqKey(UuidGenerator.Generate()), item); + } + return new QueryPlanIndex(items); + } + + /// + /// Find a matching index for the property names supplied. + /// + /// property names to search for + /// -1 if not found, or offset within indexes if found + internal Pair GetIndexNum(string[] indexProps, string[] rangeProps) + { + // find an exact match first + QueryPlanIndexItem proposed = new QueryPlanIndexItem(indexProps, null, rangeProps, null, false); + foreach (KeyValuePair entry in _items) { + if (entry.Value.EqualsCompareSortedProps(proposed)) { + return new Pair(entry.Key, null); + } + } + + // find partial match second, i.e. for unique indexes where the where-clause is overspecific + foreach (KeyValuePair entry in _items) { + if (entry.Value.RangeProps == null || entry.Value.RangeProps.Count == 0) { + int[] indexes = QueryPlanIndexUniqueHelper.CheckSufficientGetAssignment(entry.Value.IndexProps, indexProps); + if (indexes != null && indexes.Length != 0) { + return new Pair(entry.Key, indexes); + } + } + } + + return null; + } + + protected TableLookupIndexReqKey FirstIndexNum + { + get { return _items.Keys.First(); } + } + + /// + /// Add an index specification element. + /// + /// list of property names to index + /// list of coercion types if required, or null if no coercion required + /// number indicating position of index that was added + public string AddIndex(string[] indexProperties, Type[] coercionTypes) + { + string uuid = UuidGenerator.Generate(); + _items.Put(new TableLookupIndexReqKey(uuid), new QueryPlanIndexItem(indexProperties, coercionTypes, null, null, false)); + return uuid; + } + + /// + /// For testing - Returns property names of all indexes. + /// + /// property names array + public string[][] IndexProps + { + get + { + return _items.Values + .Select(v => v.IndexProps != null ? v.IndexProps.ToArray() : null) + .ToArray(); + } + } + + /// + /// Returns a list of coercion types for a given index. + /// + /// is the index field names + /// coercion types, or null if no coercion is required + public Type[] GetCoercionTypes(string[] indexProperties) + { + foreach (var value in _items.Values) + { + if (CompatExtensions.DeepEquals(value.IndexProps, indexProperties)) + { + return value.OptIndexCoercionTypes.ToArray(); + } + } + throw new ArgumentException("Index properties not found"); + } + + /// + /// Sets the coercion types for a given index. + /// + /// is the index property names + /// is the coercion types + public void SetCoercionTypes(string[] indexProperties, Type[] coercionTypes) + { + bool found = false; + foreach (var entry in _items.Values) + { + if (CompatExtensions.DeepEquals(entry.IndexProps, indexProperties)) + { + entry.OptIndexCoercionTypes = coercionTypes; + found = true; + } + } + if (!found) + { + throw new ArgumentException("Index properties not found"); + } + } + + public override string ToString() + { + if (_items.IsEmpty()) { + return " (none)"; + } + var buf = new StringBuilder(); + var delimiter = ""; + foreach (var entry in _items) + { + buf.Append(delimiter); + string info = entry.Value == null ? "" : " : " + entry.Value; + buf.Append(" index " + entry.Key + info); + delimiter = "\n"; + } + return buf.ToString(); + } + + /// + /// Print index specifications in readable format. + /// + /// define indexes + /// readable format of index INFO + + public static string Print(QueryPlanIndex[] indexSpecs) + { + StringBuilder buffer = new StringBuilder(); + buffer.Append("QueryPlanIndex[]\n"); + + string delimiter = ""; + for (int i = 0; i < indexSpecs.Length; i++) + { + buffer.Append(delimiter); + buffer.AppendFormat(" index spec stream {0} : \n{1}", i, (indexSpecs[i] == null ? " null" : indexSpecs[i].ToString())); + delimiter = "\n"; + } + + return buffer + "\n"; + } + + public static QueryPlanIndex MakeIndexTableAccess(TableLookupIndexReqKey indexName) + { + IDictionary indexMap = new Dictionary(); + indexMap.Put(indexName, null); + return new QueryPlanIndex(indexMap); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/join/plan/QueryPlanIndexBuilder.cs b/NEsper.Core/NEsper.Core/epl/join/plan/QueryPlanIndexBuilder.cs new file mode 100755 index 000000000..f1ffb8015 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/plan/QueryPlanIndexBuilder.cs @@ -0,0 +1,308 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.@join.hint; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.join.plan +{ + /// + /// Build query index plans. + /// + public class QueryPlanIndexBuilder + { + /// + /// Build index specification from navigability INFO. + /// + /// Looks at each stream and determines which properties in the stream must be indexed + /// in order for other streams to look up into the stream. Determines the unique set of + /// properties to avoid building duplicate indexes on the same set of properties. + /// + /// navigability INFO + /// The type per stream. + /// The indexed streams unique props. + /// query index specs for each stream + public static QueryPlanIndex[] BuildIndexSpec(QueryGraph queryGraph, EventType[] typePerStream, String[][][] indexedStreamsUniqueProps) + { + var numStreams = queryGraph.NumStreams; + var indexSpecs = new QueryPlanIndex[numStreams]; + + // For each stream compile a list of index property sets. + for (int streamIndexed = 0; streamIndexed < numStreams; streamIndexed++) + { + var indexesSet = new List(); + + // Look at the index from the viewpoint of the stream looking up in the index + for (int streamLookup = 0; streamLookup < numStreams; streamLookup++) + { + if (streamIndexed == streamLookup) + { + continue; + } + + var value = queryGraph.GetGraphValue(streamLookup, streamIndexed); + var hashKeyAndIndexProps = value.HashKeyProps; + + // Sort index properties, but use the sorted properties only to eliminate duplicates + var hashIndexProps = hashKeyAndIndexProps.Indexed; + var hashKeyProps = hashKeyAndIndexProps.Keys; + var indexCoercionTypes = CoercionUtil.GetCoercionTypesHash(typePerStream, streamLookup, streamIndexed, hashKeyProps, hashIndexProps); + var hashCoercionTypeArr = indexCoercionTypes.CoercionTypes; + + var rangeAndIndexProps = value.RangeProps; + var rangeIndexProps = rangeAndIndexProps.Indexed; + var rangeKeyProps = rangeAndIndexProps.Keys; + var rangeCoercionTypes = CoercionUtil.GetCoercionTypesRange(typePerStream, streamIndexed, rangeIndexProps, rangeKeyProps); + var rangeCoercionTypeArr = rangeCoercionTypes.CoercionTypes; + + if (hashIndexProps.Count == 0 && rangeIndexProps.Count == 0) + { + QueryGraphValuePairInKWSingleIdx singles = value.InKeywordSingles; + if (!singles.Key.IsEmpty()) { + String indexedProp = singles.Indexed[0]; + QueryPlanIndexItem indexItem = new QueryPlanIndexItem(new String[] {indexedProp}, null, null, null, false); + CheckDuplicateOrAdd(indexItem, indexesSet); + } + + IList multis = value.InKeywordMulti; + if (!multis.IsEmpty()) { + QueryGraphValuePairInKWMultiIdx multi = multis[0]; + foreach (ExprNode propIndexed in multi.Indexed) { + ExprIdentNode identNode = (ExprIdentNode) propIndexed; + QueryPlanIndexItem indexItem = new QueryPlanIndexItem(new String[] {identNode.ResolvedPropertyName}, null, null, null, false); + CheckDuplicateOrAdd(indexItem, indexesSet); + } + } + + continue; + } + + // reduce to any unique index if applicable + var unique = false; + var reduced = QueryPlanIndexUniqueHelper.ReduceToUniqueIfPossible(hashIndexProps, hashCoercionTypeArr, hashKeyProps, indexedStreamsUniqueProps[streamIndexed]); + if (reduced != null) + { + hashIndexProps = reduced.PropertyNames; + hashCoercionTypeArr = reduced.CoercionTypes; + unique = true; + rangeIndexProps = new String[0]; + rangeCoercionTypeArr = new Type[0]; + } + + var proposed = new QueryPlanIndexItem(hashIndexProps, hashCoercionTypeArr, rangeIndexProps, rangeCoercionTypeArr, unique); + CheckDuplicateOrAdd(proposed, indexesSet); + } + + // create full-table-scan + if (indexesSet.IsEmpty()) { + indexesSet.Add(new QueryPlanIndexItem(null, null, null, null, false)); + } + + indexSpecs[streamIndexed] = QueryPlanIndex.MakeIndex(indexesSet); + } + + return indexSpecs; + } + + public static SubordPropPlan GetJoinProps(ExprNode filterExpr, int outsideStreamCount, EventType[] allStreamTypesZeroIndexed, ExcludePlanHint excludePlanHint) + { + // No filter expression means full table scan + if (filterExpr == null) + { + return new SubordPropPlan(); + } + + // analyze query graph + var queryGraph = new QueryGraph(outsideStreamCount + 1, excludePlanHint, true); + FilterExprAnalyzer.Analyze(filterExpr, queryGraph, false); + + // Build a list of streams and indexes + var joinProps = new LinkedHashMap(); + var rangeProps = new LinkedHashMap(); + for (int stream = 0; stream < outsideStreamCount; stream++) + { + int lookupStream = stream + 1; + + QueryGraphValue queryGraphValue = queryGraph.GetGraphValue(lookupStream, 0); + QueryGraphValuePairHashKeyIndex hashKeysAndIndexes = queryGraphValue.HashKeyProps; + + // handle key-lookups + var keyPropertiesJoin = hashKeysAndIndexes.Keys; + var indexPropertiesJoin = hashKeysAndIndexes.Indexed; + if (keyPropertiesJoin.IsNotEmpty()) + { + if (keyPropertiesJoin.Count != indexPropertiesJoin.Count) + { + throw new IllegalStateException("Invalid query key and index property collection for stream " + stream); + } + + for (int i = 0; i < keyPropertiesJoin.Count; i++) + { + QueryGraphValueEntryHashKeyed keyDesc = keyPropertiesJoin[i]; + ExprNode compareNode = keyDesc.KeyExpr; + + var keyPropType = compareNode.ExprEvaluator.ReturnType.GetBoxedType(); + var indexedPropType = allStreamTypesZeroIndexed[0].GetPropertyType(indexPropertiesJoin[i]).GetBoxedType(); + var coercionType = indexedPropType; + if (keyPropType != indexedPropType) + { + coercionType = keyPropType.GetCompareToCoercionType(indexedPropType); + } + + SubordPropHashKey desc; + if (keyPropertiesJoin[i] is QueryGraphValueEntryHashKeyedExpr) { + var keyExpr = (QueryGraphValueEntryHashKeyedExpr) keyPropertiesJoin[i]; + var keyStreamNum = keyExpr.IsRequiresKey ? stream : (int?) null; + desc = new SubordPropHashKey(keyDesc, keyStreamNum, coercionType); + } + else { + var prop = (QueryGraphValueEntryHashKeyedProp) keyDesc; + desc = new SubordPropHashKey(prop, stream, coercionType); + } + joinProps.Put(indexPropertiesJoin[i], desc); + } + } + + // handle range lookups + QueryGraphValuePairRangeIndex rangeKeysAndIndexes = queryGraphValue.RangeProps; + var rangeIndexes = rangeKeysAndIndexes.Indexed; + var rangeDescs = rangeKeysAndIndexes.Keys; + if (rangeDescs.IsEmpty()) { + continue; + } + + // get all ranges lookups + int count = -1; + foreach (QueryGraphValueEntryRange rangeDesc in rangeDescs) { + count++; + String rangeIndexProp = rangeIndexes[count]; + + SubordPropRangeKey subqRangeDesc = rangeProps.Get(rangeIndexProp); + + // other streams may specify the start or end endpoint of a range, therefore this operation can be additive + if (subqRangeDesc != null) { + if (subqRangeDesc.RangeInfo.RangeType.IsRange()) { + continue; + } + + // see if we can make this additive by using a range + var relOpOther = (QueryGraphValueEntryRangeRelOp) subqRangeDesc.RangeInfo; + var relOpThis = (QueryGraphValueEntryRangeRelOp) rangeDesc; + + QueryGraphRangeConsolidateDesc opsDesc = QueryGraphRangeUtil.GetCanConsolidate( + relOpThis.RangeType, + relOpOther.RangeType); + if (opsDesc != null) { + ExprNode start; + ExprNode end; + if (!opsDesc.IsReverse) { + start = relOpOther.Expression; + end = relOpThis.Expression; + } + else { + start = relOpThis.Expression; + end = relOpOther.Expression; + } + var allowRangeReversal = relOpOther.IsBetweenPart && relOpThis.IsBetweenPart; + var rangeIn = new QueryGraphValueEntryRangeIn(opsDesc.RangeType, start, end, allowRangeReversal); + + var indexedPropType = allStreamTypesZeroIndexed[0].GetPropertyType(rangeIndexProp).GetBoxedType(); + var coercionType = indexedPropType; + var proposedType = CoercionUtil.GetCoercionTypeRangeIn(indexedPropType, rangeIn.ExprStart, rangeIn.ExprEnd); + if (proposedType != null && proposedType != indexedPropType) + { + coercionType = proposedType; + } + + subqRangeDesc = new SubordPropRangeKey(rangeIn, coercionType); + rangeProps.Put(rangeIndexProp, subqRangeDesc); + } + // ignore + continue; + } + + // an existing entry has not been found + if (rangeDesc.RangeType.IsRange()) { + var rangeIn = (QueryGraphValueEntryRangeIn) rangeDesc; + var indexedPropType = allStreamTypesZeroIndexed[0].GetPropertyType(rangeIndexProp).GetBoxedType(); + var coercionType = indexedPropType; + var proposedType = CoercionUtil.GetCoercionTypeRangeIn(indexedPropType, rangeIn.ExprStart, rangeIn.ExprEnd); + if (proposedType != null && proposedType != indexedPropType) + { + coercionType = proposedType; + } + subqRangeDesc = new SubordPropRangeKey(rangeDesc, coercionType); + } + else { + var relOp = (QueryGraphValueEntryRangeRelOp) rangeDesc; + var keyPropType = relOp.Expression.ExprEvaluator.ReturnType; + var indexedPropType = allStreamTypesZeroIndexed[0].GetPropertyType(rangeIndexProp).GetBoxedType(); + var coercionType = indexedPropType; + if (keyPropType != indexedPropType) + { + coercionType = keyPropType.GetCompareToCoercionType(indexedPropType); + } + subqRangeDesc = new SubordPropRangeKey(rangeDesc, coercionType); + } + rangeProps.Put(rangeIndexProp, subqRangeDesc); + } + } + + SubordPropInKeywordSingleIndex inKeywordSingleIdxProp = null; + SubordPropInKeywordMultiIndex inKeywordMultiIdxProp = null; + if (joinProps.IsEmpty() && rangeProps.IsEmpty()) { + for (int stream = 0; stream < outsideStreamCount; stream++) { + int lookupStream = stream + 1; + QueryGraphValue queryGraphValue = queryGraph.GetGraphValue(lookupStream, 0); + + QueryGraphValuePairInKWSingleIdx inkwSingles = queryGraphValue.InKeywordSingles; + if (inkwSingles.Indexed.Length != 0) { + ExprNode[] keys = inkwSingles.Key[0].KeyExprs; + String key = inkwSingles.Indexed[0]; + if (inKeywordSingleIdxProp != null) { + continue; + } + var coercionType = keys[0].ExprEvaluator.ReturnType; // for in-comparison the same type is required + inKeywordSingleIdxProp = new SubordPropInKeywordSingleIndex(key, coercionType, keys); + } + + IList inkwMultis = queryGraphValue.InKeywordMulti; + if (!inkwMultis.IsEmpty()) { + QueryGraphValuePairInKWMultiIdx multi = inkwMultis[0]; + inKeywordMultiIdxProp = new SubordPropInKeywordMultiIndex(ExprNodeUtility.GetIdentResolvedPropertyNames(multi.Indexed), multi.Indexed[0].ExprEvaluator.ReturnType, multi.Key.KeyExpr); + } + + if (inKeywordSingleIdxProp != null && inKeywordMultiIdxProp != null) { + inKeywordMultiIdxProp = null; + } + } + } + + return new SubordPropPlan(joinProps, rangeProps, inKeywordSingleIdxProp, inKeywordMultiIdxProp); + } + + private static void CheckDuplicateOrAdd(QueryPlanIndexItem proposed, IList indexesSet) + { + var found = indexesSet.Any(proposed.EqualsCompareSortedProps); + if (!found) + { + indexesSet.Add(proposed); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/plan/QueryPlanIndexItem.cs b/NEsper.Core/NEsper.Core/epl/join/plan/QueryPlanIndexItem.cs new file mode 100755 index 000000000..0f9ab28f2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/plan/QueryPlanIndexItem.cs @@ -0,0 +1,84 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.join.plan +{ + /// + /// Specifies an index to build as part of an overall query plan. + /// + public class QueryPlanIndexItem + { + /// + /// Ctor. + /// + /// array of property names with the first dimension suplying the number ofdistinct indexes. The second dimension can be empty and indicates a full table scan. + /// array of coercion types for each index, or null entry for no coercion required + /// The range props. + /// The opt range coercion types. + /// if set to true [unique]. + public QueryPlanIndexItem(IList indexProps, IList optIndexCoercionTypes, IList rangeProps, IList optRangeCoercionTypes, bool unique) + { + IndexProps = indexProps; + OptIndexCoercionTypes = optIndexCoercionTypes; + RangeProps = (rangeProps == null || rangeProps.Count == 0) ? null : rangeProps; + OptRangeCoercionTypes = optRangeCoercionTypes; + IsUnique = unique; + if (IsUnique && indexProps.Count == 0) + { + throw new IllegalStateException("Invalid unique index planned without hash index props"); + } + if (IsUnique && rangeProps.Count > 0) + { + throw new IllegalStateException("Invalid unique index planned that includes range props"); + } + } + + public IList IndexProps { get; private set; } + + public IList OptIndexCoercionTypes { get; set; } + + public IList RangeProps { get; private set; } + + public IList OptRangeCoercionTypes { get; private set; } + + public Boolean IsUnique { get; private set; } + + public override String ToString() { + return "QueryPlanIndexItem{" + + "unique=" + IsUnique + + ", indexProps=" + IndexProps.Render() + + ", rangeProps=" + RangeProps.Render() + + ", optIndexCoercionTypes=" + OptIndexCoercionTypes.Render() + + ", optRangeCoercionTypes=" + OptRangeCoercionTypes.Render() + + '}'; + } + + public bool EqualsCompareSortedProps(QueryPlanIndexItem other) + { + if (IsUnique != other.IsUnique) + { + return false; + } + + String[] otherIndexProps = CollectionUtil.CopySortArray(other.IndexProps); + String[] thisIndexProps = CollectionUtil.CopySortArray(IndexProps); + String[] otherRangeProps = CollectionUtil.CopySortArray(other.RangeProps); + String[] thisRangeProps = CollectionUtil.CopySortArray(RangeProps); + return + CollectionUtil.Compare(otherIndexProps, thisIndexProps) && + CollectionUtil.Compare(otherRangeProps, thisRangeProps); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/plan/QueryPlanIndexUniqueHelper.cs b/NEsper.Core/NEsper.Core/epl/join/plan/QueryPlanIndexUniqueHelper.cs new file mode 100755 index 000000000..dafc7703c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/plan/QueryPlanIndexUniqueHelper.cs @@ -0,0 +1,104 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.epl.join.plan +{ + public class QueryPlanIndexUniqueHelper + { + public static ReducedHashKeys ReduceToUniqueIfPossible(IList hashPropsProvided, + IList hashCoercionTypes, + IList hashFunctions, + String[][] hashPropsRequiredPerIndex) + { + if (hashPropsRequiredPerIndex == null || hashPropsRequiredPerIndex.Length == 0) + { + return null; + } + foreach (String[] hashPropsRequired in hashPropsRequiredPerIndex) + { + int[] indexes = CheckSufficientGetAssignment(hashPropsRequired, hashPropsProvided); + if (indexes != null) + { + var props = new String[indexes.Length]; + var types = new Type[indexes.Length]; + var functions = new List(); + for (int i = 0; i < indexes.Length; i++) + { + props[i] = hashPropsProvided[indexes[i]]; + types[i] = hashCoercionTypes == null ? null : hashCoercionTypes[indexes[i]]; + functions.Add(hashFunctions[indexes[i]]); + } + return new ReducedHashKeys(props, types, functions); + } + } + return null; + } + + public static int[] CheckSufficientGetAssignment(IList hashPropsRequired, IList hashPropsProvided) + { + if (hashPropsProvided == null || hashPropsRequired == null || hashPropsProvided.Count < hashPropsRequired.Count) + { + return null; + } + // first pass: determine if possible + foreach (String required in hashPropsRequired) + { + bool found = false; + foreach (String provided in hashPropsProvided) + { + if (provided.Equals(required)) + { + found = true; + break; + } + } + if (!found) + { + return null; + } + } + + // second pass: determine assignments + int[] indexes = new int[hashPropsRequired.Count]; + for (int i = 0; i < indexes.Length; i++) + { + int foundIndex = -1; + String required = hashPropsRequired[i]; + for (int j = 0; j < hashPropsProvided.Count; j++) + { + if (hashPropsProvided[j].Equals(required)) + { + foundIndex = j; + break; + } + } + indexes[i] = foundIndex; + } + return indexes; + } + + public class ReducedHashKeys + { + internal ReducedHashKeys(String[] propertyNames, Type[] coercionTypes, List hashKeyFunctions) + { + PropertyNames = propertyNames; + CoercionTypes = coercionTypes; + HashKeyFunctions = hashKeyFunctions; + } + + public string[] PropertyNames { get; private set; } + + public Type[] CoercionTypes { get; private set; } + + public List HashKeyFunctions { get; private set; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/plan/QueryPlanNode.cs b/NEsper.Core/NEsper.Core/epl/join/plan/QueryPlanNode.cs new file mode 100755 index 000000000..56f2177f5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/plan/QueryPlanNode.cs @@ -0,0 +1,79 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using com.espertech.esper.client; +using com.espertech.esper.compat.threading; +using com.espertech.esper.epl.join.exec.@base; +using com.espertech.esper.epl.join.table; +using com.espertech.esper.epl.virtualdw; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.epl.join.plan +{ + /// + /// Specification node for a query execution plan to be extended by specific execution specification nodes. + /// + public abstract class QueryPlanNode + { + /// + /// Make execution node from this specification. + /// + /// the statement name + /// the statement id + /// annotations + /// tables build for each stream + /// event type of each stream + /// viewable per stream for access to historical data + /// index management for historical streams + /// @return execution node matching spec + /// The table secondary index locks. + /// + public abstract ExecNode MakeExec(string statementName, int statementId, Attribute[] annotations, IDictionary[] indexesPerStream, EventType[] streamTypes, Viewable[] streamViews, HistoricalStreamIndexList[] historicalStreamIndexLists, VirtualDWView[] viewExternal, ILockable[] tableSecondaryIndexLocks); + + public abstract void AddIndexes(ISet usedIndexes); + + /// Print a long readable format of the query node to the supplied PrintWriter. + /// is the indentation writer to print to + public abstract void Print(IndentWriter writer); + + /// Print in readable format the execution plan spec. + /// plans to print + /// readable text with plans + public static String Print(QueryPlanNode[] planNodeSpecs) + { + var buffer = new StringBuilder(); + buffer.Append("QueryPlanNode[]\n"); + + for (int i = 0; i < planNodeSpecs.Length; i++) + { + buffer.Append(" node spec " + i + " :\n"); + + var writer = new StringWriter(); + var indentWriter = new IndentWriter(writer, 4, 2); + + if (planNodeSpecs[i] != null) + { + planNodeSpecs[i].Print(indentWriter); + } + else + { + indentWriter.WriteLine("no plan (historical)"); + } + + buffer.Append(writer.ToString()); + } + + return buffer.ToString(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/plan/QueryPlanNodeAllUnidirectionalOuter.cs b/NEsper.Core/NEsper.Core/epl/join/plan/QueryPlanNodeAllUnidirectionalOuter.cs new file mode 100755 index 000000000..8b2b03a39 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/plan/QueryPlanNodeAllUnidirectionalOuter.cs @@ -0,0 +1,57 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.compat.threading; +using com.espertech.esper.epl.@join.exec.@base; +using com.espertech.esper.epl.join.table; +using com.espertech.esper.epl.virtualdw; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.epl.join.plan +{ + public class QueryPlanNodeAllUnidirectionalOuter : QueryPlanNode + { + private readonly int _streamNum; + + public QueryPlanNodeAllUnidirectionalOuter(int streamNum) + { + _streamNum = streamNum; + } + + public override ExecNode MakeExec( + string statementName, + int statementId, + Attribute[] annotations, + IDictionary[] indexesPerStream, + EventType[] streamTypes, + Viewable[] streamViews, + HistoricalStreamIndexList[] historicalStreamIndexLists, + VirtualDWView[] viewExternal, + ILockable[] tableSecondaryIndexLocks) + { + return new ExecNodeAllUnidirectionalOuter(_streamNum, streamTypes.Length); + } + + public override void AddIndexes(ISet usedIndexes) + { + } + + public override void Print(IndentWriter writer) + { + writer.WriteLine("Unidirectional Full-Outer-Join-All Execution"); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/join/plan/QueryPlanNodeNoOp.cs b/NEsper.Core/NEsper.Core/epl/join/plan/QueryPlanNodeNoOp.cs new file mode 100755 index 000000000..ae586ecab --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/plan/QueryPlanNodeNoOp.cs @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.threading; +using com.espertech.esper.epl.join.exec.@base; +using com.espertech.esper.epl.join.table; +using com.espertech.esper.epl.virtualdw; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.epl.join.plan +{ + public class QueryPlanNodeNoOp : QueryPlanNode + { + private static readonly ExecNodeNoOp NOOP = new ExecNodeNoOp(); + + public override ExecNode MakeExec(string statementName, int statementId, Attribute[] annotations, IDictionary[] indexesPerStream, EventType[] streamTypes, Viewable[] streamViews, HistoricalStreamIndexList[] historicalStreamIndexLists, VirtualDWView[] viewExternal, ILockable[] tableSecondaryIndexLocks) + { + return NOOP; + } + + public override void AddIndexes(ISet usedIndexes) + { + } + + public override void Print(IndentWriter writer) + { + writer.WriteLine("No-Op Execution"); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/plan/SortedTableLookupPlan.cs b/NEsper.Core/NEsper.Core/epl/join/plan/SortedTableLookupPlan.cs new file mode 100755 index 000000000..ccf3d0055 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/plan/SortedTableLookupPlan.cs @@ -0,0 +1,61 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.join.exec.@base; +using com.espertech.esper.epl.join.table; + +namespace com.espertech.esper.epl.join.plan +{ + /// + /// Plan to perform an indexed table lookup. + /// + public class SortedTableLookupPlan : TableLookupPlan + { + private readonly QueryGraphValueEntryRange _rangeKeyPair; + private readonly int _lookupStream; + + /// + /// Ctor. + /// + /// stream that generates event to look up for + /// stream to index table lookup + /// index number for the table containing the full unindexed contents + /// The range key pair. + public SortedTableLookupPlan(int lookupStream, int indexedStream, TableLookupIndexReqKey indexNum, QueryGraphValueEntryRange rangeKeyPair) + : base(lookupStream, indexedStream, new TableLookupIndexReqKey[] { indexNum }) + { + _rangeKeyPair = rangeKeyPair; + _lookupStream = lookupStream; + } + + public QueryGraphValueEntryRange RangeKeyPair + { + get { return _rangeKeyPair; } + } + + public override TableLookupKeyDesc KeyDescriptor + { + get { return new TableLookupKeyDesc(new QueryGraphValueEntryHashKeyed[0], new[] {_rangeKeyPair}); } + } + + public override JoinExecTableLookupStrategy MakeStrategyInternal(EventTable[] eventTable, EventType[] eventTypes) + { + PropertySortedEventTable index = (PropertySortedEventTable) eventTable[0]; + return new SortedTableLookupStrategy(_lookupStream, -1, _rangeKeyPair, null, index); + } + + public override String ToString() + { + return string.Format("SortedTableLookupPlan {0} keyProperties={1}", base.ToString(), _rangeKeyPair.RenderAny()); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/plan/TableLookupIndexReqKey.cs b/NEsper.Core/NEsper.Core/epl/join/plan/TableLookupIndexReqKey.cs new file mode 100755 index 000000000..3a98dfcc1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/plan/TableLookupIndexReqKey.cs @@ -0,0 +1,68 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.join.plan +{ + public class TableLookupIndexReqKey + { + private readonly string _name; + private readonly string _tableName; + + public TableLookupIndexReqKey(string name) + : this(name, null) + { + } + + public TableLookupIndexReqKey(string name, string tableName) + { + _name = name; + _tableName = tableName; + } + + public string Name + { + get { return _name; } + } + + public string TableName + { + get { return _tableName; } + } + + public override bool Equals(object o) + { + if (this == o) return true; + if (o == null || GetType() != o.GetType()) return false; + + var that = (TableLookupIndexReqKey) o; + + if (_tableName != null ? !_tableName.Equals(that._tableName) : that._tableName != null) + return false; + if (!_name.Equals(that._name)) return false; + + return true; + } + + public override int GetHashCode() + { + int result = _name.GetHashCode(); + result = 31 * result + (_tableName != null ? _tableName.GetHashCode() : 0); + return result; + } + + public override string ToString() + { + if (_tableName == null) { + return _name; + } + else { + return "table '" + _tableName + "' index '" + _name + "'"; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/plan/TableLookupKeyDesc.cs b/NEsper.Core/NEsper.Core/epl/join/plan/TableLookupKeyDesc.cs new file mode 100755 index 000000000..0ef4fb53a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/plan/TableLookupKeyDesc.cs @@ -0,0 +1,33 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +namespace com.espertech.esper.epl.join.plan +{ + public class TableLookupKeyDesc + { + public TableLookupKeyDesc(IList hashes, IList ranges) + { + Hashes = hashes; + Ranges = ranges; + } + + public IList Hashes { get; private set; } + + public IList Ranges { get; private set; } + + public override string ToString() + { + return "TableLookupKeyDesc{" + + "hashes=" + QueryGraphValueEntryHashKeyed.ToQueryPlan(Hashes) + + ", btree=" + QueryGraphValueEntryRange.ToQueryPlan(Ranges) + + '}'; + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/join/plan/TableLookupNode.cs b/NEsper.Core/NEsper.Core/epl/join/plan/TableLookupNode.cs new file mode 100755 index 000000000..e6747d614 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/plan/TableLookupNode.cs @@ -0,0 +1,69 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.threading; +using com.espertech.esper.epl.join.exec.@base; +using com.espertech.esper.epl.join.table; +using com.espertech.esper.epl.virtualdw; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.epl.join.plan +{ + /// + /// Specifies exection of a table lookup using the supplied plan for performing the lookup. + /// + public class TableLookupNode : QueryPlanNode + { + private readonly TableLookupPlan _tableLookupPlan; + + /// Ctor. + /// plan for performing lookup + public TableLookupNode(TableLookupPlan tableLookupPlan) + { + _tableLookupPlan = tableLookupPlan; + } + + /// Returns lookup plan. + /// lookup plan + public TableLookupPlan LookupStrategySpec + { + get { return _tableLookupPlan; } + } + + public TableLookupPlan TableLookupPlan + { + get { return _tableLookupPlan; } + } + + public override void Print(IndentWriter writer) + { + writer.WriteLine(string.Format("TableLookupNode tableLookupPlan={0}", _tableLookupPlan)); + } + + public override ExecNode MakeExec(string statementName, int statementId, Attribute[] annotations, IDictionary[] indexesPerStream, EventType[] streamTypes, Viewable[] streamViews, HistoricalStreamIndexList[] historicalStreamIndexLists, VirtualDWView[] viewExternal, ILockable[] tableSecondaryIndexLocks) + { + JoinExecTableLookupStrategy lookupStrategy = _tableLookupPlan.MakeStrategy(statementName, statementId, annotations, indexesPerStream, streamTypes, viewExternal); + int indexedStream = _tableLookupPlan.IndexedStream; + if (tableSecondaryIndexLocks[indexedStream] != null) + { + return new TableLookupExecNodeTableLocking(indexedStream, lookupStrategy, tableSecondaryIndexLocks[indexedStream]); + } + return new TableLookupExecNode(indexedStream, lookupStrategy); + } + + public override void AddIndexes(ISet usedIndexes) { + usedIndexes.AddAll(_tableLookupPlan.IndexNum); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/plan/TableLookupPlan.cs b/NEsper.Core/NEsper.Core/epl/join/plan/TableLookupPlan.cs new file mode 100755 index 000000000..c1c9469b8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/plan/TableLookupPlan.cs @@ -0,0 +1,93 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.@join.exec.@base; +using com.espertech.esper.epl.@join.table; +using com.espertech.esper.epl.virtualdw; + +namespace com.espertech.esper.epl.join.plan +{ + /// + /// Abstract specification on how to perform a table lookup. + /// + public abstract class TableLookupPlan + { + public abstract JoinExecTableLookupStrategy MakeStrategyInternal(EventTable[] eventTable, EventType[] eventTypes); + public abstract TableLookupKeyDesc KeyDescriptor { get; } + + /// + /// Instantiates the lookup plan into a execution strategy for the lookup. + /// + /// Name of the statement. + /// The statement identifier. + /// The accessed by statement annotations. + /// tables for each stream + /// types of events in stream + /// The view externals. + /// + /// lookup strategy instance + /// + public JoinExecTableLookupStrategy MakeStrategy(string statementName, int statementId, Attribute[] accessedByStmtAnnotations, IDictionary[] indexesPerStream, EventType[] eventTypes, VirtualDWView[] viewExternals) + { + var eventTables = new EventTable[IndexNum.Length]; + for (var i = 0; i < IndexNum.Length; i++) + { + eventTables[i] = indexesPerStream[IndexedStream].Get(IndexNum[i]); + } + if (viewExternals[IndexedStream] != null) + { + return viewExternals[IndexedStream].GetJoinLookupStrategy(statementName, statementId, accessedByStmtAnnotations, eventTables, KeyDescriptor, LookupStream); + } + return MakeStrategyInternal(eventTables, eventTypes); + } + + /// + /// Ctor. + /// + /// stream number of stream that supplies event to be used to look up + /// stream number of stream that is being access via index/table + /// index to use for lookup + protected TableLookupPlan(int lookupStream, int indexedStream, TableLookupIndexReqKey[] indexNum) + { + LookupStream = lookupStream; + IndexedStream = indexedStream; + IndexNum = indexNum; + } + + /// + /// Returns the lookup stream. + /// + /// lookup stream + public int LookupStream { get; private set; } + + /// + /// Returns indexed stream. + /// + /// indexed stream + public int IndexedStream { get; private set; } + + /// + /// Returns index number to use for looking up in. + /// + /// index number + public TableLookupIndexReqKey[] IndexNum { get; private set; } + + public override string ToString() + { + return "lookupStream=" + LookupStream + + " indexedStream=" + IndexedStream + + " indexNum=" + IndexNum.Render(); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/join/plan/TableOuterLookupNode.cs b/NEsper.Core/NEsper.Core/epl/join/plan/TableOuterLookupNode.cs new file mode 100755 index 000000000..51031b1c0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/plan/TableOuterLookupNode.cs @@ -0,0 +1,67 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.threading; +using com.espertech.esper.epl.join.exec.@base; +using com.espertech.esper.epl.join.table; +using com.espertech.esper.epl.virtualdw; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.epl.join.plan +{ + /// + /// Specifies exection of a table lookup with outer join using the a specified lookup plan. + /// + public class TableOuterLookupNode : QueryPlanNode + { + private readonly TableLookupPlan _tableLookupPlan; + + /// Ctor. + /// plan for performing lookup + public TableOuterLookupNode(TableLookupPlan tableLookupPlan) + { + _tableLookupPlan = tableLookupPlan; + } + + /// Returns lookup plan. + /// lookup plan + public TableLookupPlan LookupStrategySpec + { + get { return _tableLookupPlan; } + } + + public override void Print(IndentWriter writer) + { + writer.WriteLine("TableOuterLookupNode " + + " tableLookupPlan=" + _tableLookupPlan); + } + + public override ExecNode MakeExec(string statementName, int statementId, Attribute[] annotations, IDictionary[] indexesPerStream, EventType[] streamTypes, Viewable[] streamViews, HistoricalStreamIndexList[] historicalStreamIndexLists, VirtualDWView[] viewExternal, ILockable[] tableSecondaryIndexLocks) + { + JoinExecTableLookupStrategy lookupStrategy = _tableLookupPlan.MakeStrategy(statementName, statementId, annotations, indexesPerStream, streamTypes, viewExternal); + int indexedStream = _tableLookupPlan.IndexedStream; + if (tableSecondaryIndexLocks[indexedStream] != null) + { + return new TableOuterLookupExecNodeTableLocking(indexedStream, lookupStrategy, tableSecondaryIndexLocks[indexedStream]); + } + + return new TableOuterLookupExecNode(_tableLookupPlan.IndexedStream, lookupStrategy); + } + + public override void AddIndexes(ISet usedIndexes) + { + usedIndexes.AddAll(_tableLookupPlan.IndexNum); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/plan/TwoStreamQueryPlanBuilder.cs b/NEsper.Core/NEsper.Core/epl/join/plan/TwoStreamQueryPlanBuilder.cs new file mode 100755 index 000000000..2d936d32b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/plan/TwoStreamQueryPlanBuilder.cs @@ -0,0 +1,67 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.type; + +namespace com.espertech.esper.epl.join.plan +{ + /// Builds a query plan for the simple 2-stream scenario. + public class TwoStreamQueryPlanBuilder + { + /// + /// Build query plan. + /// + /// - navigability INFO + /// - outer join type, null if not an outer join + /// - event types for each stream + /// - table INFO + /// props of unique indexes + /// query plan + public static QueryPlan Build( + EventType[] typesPerStream, + QueryGraph queryGraph, + OuterJoinType? optionalOuterJoinType, + string[][][] uniqueIndexProps, + TableMetadata[] tablesPerStream) + { + var indexSpecs = QueryPlanIndexBuilder.BuildIndexSpec( + queryGraph, typesPerStream, uniqueIndexProps); + + var execNodeSpecs = new QueryPlanNode[2]; + var lookupPlans = new TableLookupPlan[2]; + + // plan lookup from 1 to zero + lookupPlans[1] = NStreamQueryPlanBuilder.CreateLookupPlan( + queryGraph, 1, 0, indexSpecs[0], typesPerStream, tablesPerStream[0]); + + // plan lookup from zero to 1 + lookupPlans[0] = NStreamQueryPlanBuilder.CreateLookupPlan( + queryGraph, 0, 1, indexSpecs[1], typesPerStream, tablesPerStream[1]); + execNodeSpecs[0] = new TableLookupNode(lookupPlans[0]); + execNodeSpecs[1] = new TableLookupNode(lookupPlans[1]); + + if (optionalOuterJoinType != null) + { + if ((optionalOuterJoinType.Equals(OuterJoinType.LEFT)) || + (optionalOuterJoinType.Equals(OuterJoinType.FULL))) + { + execNodeSpecs[0] = new TableOuterLookupNode(lookupPlans[0]); + } + if ((optionalOuterJoinType.Equals(OuterJoinType.RIGHT)) || + (optionalOuterJoinType.Equals(OuterJoinType.FULL))) + { + execNodeSpecs[1] = new TableOuterLookupNode(lookupPlans[1]); + } + } + + return new QueryPlan(indexSpecs, execNodeSpecs); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/join/pollindex/PollResultIndexingStrategy.cs b/NEsper.Core/NEsper.Core/epl/join/pollindex/PollResultIndexingStrategy.cs new file mode 100755 index 000000000..9c596376f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/pollindex/PollResultIndexingStrategy.cs @@ -0,0 +1,54 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.join.table; + +namespace com.espertech.esper.epl.join.pollindex +{ + /// + /// A strategy for converting a poll-result into a potentially indexed table. + /// + /// Some implementations may decide to not index the poll result and simply hold + /// a reference to the result. Other implementations may use predetermined index + /// properties to index the poll result for faster lookup. + /// + public interface PollResultIndexingStrategy + { + /// + /// Build and index of a poll result. + /// + /// result of a poll operation + /// true to indicate that caching is active and therefore index building makes sense asthe index structure is not a throw-away. + /// + /// indexed collection of poll results + EventTable[] Index(IList pollResult, bool isActiveCache, StatementContext statementContext); + + String ToQueryPlan(); + } + + public class ProxyPollResultIndexingStrategy : PollResultIndexingStrategy + { + public Func, bool, StatementContext, EventTable[]> ProcIndex { get; set; } + public Func ProcToQueryPlan { get; set; } + + public EventTable[] Index(IList pollResult, bool isActiveCache, StatementContext statementContext) + { + return ProcIndex(pollResult, isActiveCache, statementContext); + } + + public string ToQueryPlan() + { + return ProcToQueryPlan(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/pollindex/PollResultIndexingStrategyComposite.cs b/NEsper.Core/NEsper.Core/epl/join/pollindex/PollResultIndexingStrategyComposite.cs new file mode 100755 index 000000000..9caf61903 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/pollindex/PollResultIndexingStrategyComposite.cs @@ -0,0 +1,71 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.@join.table; + +namespace com.espertech.esper.epl.join.pollindex +{ + /// + /// Strategy for building an index out of poll-results knowing the properties to base the index on. + /// + public class PollResultIndexingStrategyComposite : PollResultIndexingStrategy + { + private readonly int _streamNum; + private readonly EventType _eventType; + private readonly IList _indexPropertiesJoin; + private readonly IList _keyCoercionTypes; + private readonly IList _rangePropertiesJoin; + private readonly IList _rangeCoercionTypes; + + public PollResultIndexingStrategyComposite( + int streamNum, + EventType eventType, + IList indexPropertiesJoin, + IList keyCoercionTypes, + IList rangePropertiesJoin, + IList rangeCoercionTypes) + { + _streamNum = streamNum; + _eventType = eventType; + _keyCoercionTypes = keyCoercionTypes; + _indexPropertiesJoin = indexPropertiesJoin; + _rangePropertiesJoin = rangePropertiesJoin; + _rangeCoercionTypes = rangeCoercionTypes; + } + + public EventTable[] Index(IList pollResult, bool isActiveCache, StatementContext statementContext) + { + if (!isActiveCache) { + return new EventTable[]{new UnindexedEventTableList(pollResult, _streamNum)}; + } + var factory = new PropertyCompositeEventTableFactory(_streamNum, _eventType, _indexPropertiesJoin, _keyCoercionTypes, _rangePropertiesJoin, _rangeCoercionTypes); + EventTable[] tables = factory.MakeEventTables(new EventTableFactoryTableIdentStmt(statementContext)); + foreach (EventTable table in tables) { + table.Add(pollResult.ToArray()); + } + return tables; + } + + public string ToQueryPlan() + { + return string.Format( + "{0} hash {1} btree {2} key coercion {3} range coercion {4}", GetType().Name, + CompatExtensions.Render(_indexPropertiesJoin), + CompatExtensions.Render(_rangePropertiesJoin), + CompatExtensions.Render(_keyCoercionTypes), + CompatExtensions.Render(_rangeCoercionTypes)); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/join/pollindex/PollResultIndexingStrategyIndex.cs b/NEsper.Core/NEsper.Core/epl/join/pollindex/PollResultIndexingStrategyIndex.cs new file mode 100755 index 000000000..30de47174 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/pollindex/PollResultIndexingStrategyIndex.cs @@ -0,0 +1,61 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.@join.table; + +namespace com.espertech.esper.epl.join.pollindex +{ + /// + /// Strategy for building an index out of poll-results knowing the properties to base the index on. + /// + public class PollResultIndexingStrategyIndex : PollResultIndexingStrategy + { + private readonly int _streamNum; + private readonly EventType _eventType; + private readonly IList _propertyNames; + + /// Ctor. + /// is the stream number of the indexed stream + /// is the event type of the indexed stream + /// is the property names to be indexed + public PollResultIndexingStrategyIndex(int streamNum, EventType eventType, IList propertyNames) + { + _streamNum = streamNum; + _eventType = eventType; + _propertyNames = propertyNames; + } + + public EventTable[] Index(IList pollResult, bool isActiveCache, StatementContext statementContext) + { + if (!isActiveCache) + { + return new EventTable[] + { + new UnindexedEventTableList(pollResult, _streamNum) + }; + } + var factory = new PropertyIndexedEventTableFactory(_streamNum, _eventType, _propertyNames, false, null); + var tables = factory.MakeEventTables(new EventTableFactoryTableIdentStmt(statementContext)); + foreach(var table in tables) + table.Add(pollResult.ToArray()); + return tables; + } + + public String ToQueryPlan() + { + return GetType().Name + " properties " + _propertyNames.Render(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/pollindex/PollResultIndexingStrategyIndexCoerce.cs b/NEsper.Core/NEsper.Core/epl/join/pollindex/PollResultIndexingStrategyIndexCoerce.cs new file mode 100755 index 000000000..c13c32376 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/pollindex/PollResultIndexingStrategyIndexCoerce.cs @@ -0,0 +1,67 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.@join.table; + +namespace com.espertech.esper.epl.join.pollindex +{ + /// + /// Strategy for building an index out of poll-results knowing the properties to base the index on, and their coercion types. + /// + public class PollResultIndexingStrategyIndexCoerce : PollResultIndexingStrategy + { + private readonly int _streamNum; + private readonly EventType _eventType; + private readonly IList _propertyNames; + private readonly IList _coercionTypes; + + /// Ctor. + /// is the stream number of the indexed stream + /// is the event type of the indexed stream + /// is the property names to be indexed + /// is the types to coerce to for keys and values + public PollResultIndexingStrategyIndexCoerce(int streamNum, EventType eventType, IList propertyNames, IList coercionTypes) + { + _streamNum = streamNum; + _eventType = eventType; + _propertyNames = propertyNames; + _coercionTypes = coercionTypes; + } + + public EventTable[] Index(IList pollResult, bool isActiveCache, StatementContext statementContext) + { + if (!isActiveCache) + { + return new EventTable[] + { + new UnindexedEventTableList(pollResult, _streamNum) + }; + } + + var factory = new PropertyIndexedEventTableCoerceAllFactory(_streamNum, _eventType, _propertyNames, _coercionTypes); + var tables = factory.MakeEventTables(new EventTableFactoryTableIdentStmt(statementContext)); + foreach (var table in tables) + { + table.Add(pollResult.ToArray()); + } + return tables; + } + + public String ToQueryPlan() + { + return GetType().FullName + " properties " + _propertyNames.Render() + " coercion " + _coercionTypes.Render(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/pollindex/PollResultIndexingStrategyIndexCoerceSingle.cs b/NEsper.Core/NEsper.Core/epl/join/pollindex/PollResultIndexingStrategyIndexCoerceSingle.cs new file mode 100755 index 000000000..48efeecb8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/pollindex/PollResultIndexingStrategyIndexCoerceSingle.cs @@ -0,0 +1,66 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.@join.table; + +namespace com.espertech.esper.epl.join.pollindex +{ + /// + /// Strategy for building an index out of poll-results knowing the properties to base the index on, and their + /// coercion types. + /// + public class PollResultIndexingStrategyIndexCoerceSingle : PollResultIndexingStrategy + { + private readonly int _streamNum; + private readonly EventType _eventType; + private readonly string _propertyName; + private readonly Type _coercionType; + + public PollResultIndexingStrategyIndexCoerceSingle( + int streamNum, + EventType eventType, + string propertyName, + Type coercionType) + { + _streamNum = streamNum; + _eventType = eventType; + _propertyName = propertyName; + _coercionType = coercionType; + } + + public EventTable[] Index(IList pollResult, bool isActiveCache, StatementContext statementContext) + { + if (!isActiveCache) + { + return new EventTable[] + { + new UnindexedEventTableList(pollResult, _streamNum) + }; + } + var factory = new PropertyIndexedEventTableSingleCoerceAllFactory( + _streamNum, _eventType, _propertyName, _coercionType); + EventTable[] tables = factory.MakeEventTables(new EventTableFactoryTableIdentStmt(statementContext)); + foreach (EventTable table in tables) + { + table.Add(pollResult.ToArray()); + } + return tables; + } + + public string ToQueryPlan() + { + return GetType().Name + " property " + _propertyName + " coercion " + _coercionType; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/join/pollindex/PollResultIndexingStrategyIndexSingle.cs b/NEsper.Core/NEsper.Core/epl/join/pollindex/PollResultIndexingStrategyIndexSingle.cs new file mode 100755 index 000000000..10ffca0dc --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/pollindex/PollResultIndexingStrategyIndexSingle.cs @@ -0,0 +1,63 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.@join.table; + +namespace com.espertech.esper.epl.join.pollindex +{ + /// + /// Strategy for building an index out of poll-results knowing the properties to base the index on. + /// + public class PollResultIndexingStrategyIndexSingle : PollResultIndexingStrategy + { + private readonly int _streamNum; + private readonly EventType _eventType; + private readonly String _propertyName; + + /// Ctor. + /// is the stream number of the indexed stream + /// is the event type of the indexed stream + /// is the property names to be indexed + public PollResultIndexingStrategyIndexSingle(int streamNum, EventType eventType, String propertyName) + { + _streamNum = streamNum; + _eventType = eventType; + _propertyName = propertyName; + } + + public EventTable[] Index(IList pollResult, bool isActiveCache, StatementContext statementContext) + { + if (!isActiveCache) + { + return new EventTable[] + { + new UnindexedEventTableList(pollResult, _streamNum) + }; + } + var factory = new PropertyIndexedEventTableSingleFactory(_streamNum, _eventType, _propertyName, false, null); + var tables = factory.MakeEventTables(new EventTableFactoryTableIdentStmt(statementContext)); + foreach (var table in tables) + { + table.Add(pollResult.ToArray()); + } + + return tables; + } + + public String ToQueryPlan() + { + return GetType().FullName + " properties " + _propertyName; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/pollindex/PollResultIndexingStrategyIndexSingleArray.cs b/NEsper.Core/NEsper.Core/epl/join/pollindex/PollResultIndexingStrategyIndexSingleArray.cs new file mode 100755 index 000000000..e93d7d0f5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/pollindex/PollResultIndexingStrategyIndexSingleArray.cs @@ -0,0 +1,60 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.@join.table; + +namespace com.espertech.esper.epl.join.pollindex +{ + /// + /// Strategy for building an index out of poll-results knowing the properties to base the index on. + /// + public class PollResultIndexingStrategyIndexSingleArray : PollResultIndexingStrategy + { + private readonly int _streamNum; + private readonly EventType _eventType; + private readonly String[] _propertyNames; + + /// Ctor. + /// is the stream number of the indexed stream + /// is the event type of the indexed stream + /// is the property names to be indexed + public PollResultIndexingStrategyIndexSingleArray(int streamNum, EventType eventType, String[] propertyNames) + { + _streamNum = streamNum; + _eventType = eventType; + _propertyNames = propertyNames; + } + + public EventTable[] Index(IList pollResult, bool isActiveCache, StatementContext statementContext) + { + if (!isActiveCache) + { + return new EventTable[] {new UnindexedEventTableList(pollResult, _streamNum)}; + } + var tables = new EventTable[_propertyNames.Length]; + for (var i = 0; i < _propertyNames.Length; i++) { + var factory = new PropertyIndexedEventTableSingleFactory(_streamNum, _eventType, _propertyNames[i], false, null); + tables[i] = factory.MakeEventTables(new EventTableFactoryTableIdentStmt(statementContext))[0]; + tables[i].Add(pollResult.ToArray()); + } + return tables; + } + + public String ToQueryPlan() + { + return GetType().Name + " properties " + _propertyNames.Render(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/pollindex/PollResultIndexingStrategyNoIndex.cs b/NEsper.Core/NEsper.Core/epl/join/pollindex/PollResultIndexingStrategyNoIndex.cs new file mode 100755 index 000000000..d216cde4a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/pollindex/PollResultIndexingStrategyNoIndex.cs @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.join.table; + +namespace com.espertech.esper.epl.join.pollindex +{ + /// Strategy of indexing that simply builds an unindexed table of poll results. For use when caching is disabled or when no proper index could be build because no where-clause or on-clause exists or these clauses don't yield indexable columns on analysis. + public class PollResultIndexingStrategyNoIndex : PollResultIndexingStrategy + { + public EventTable[] Index(IList pollResult, bool isActiveCache, StatementContext statementContext) + { + return new EventTable[] + { + new UnindexedEventTableList(pollResult, -1) + }; + } + + public String ToQueryPlan() + { + return this.GetType().Name; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/pollindex/PollResultIndexingStrategySorted.cs b/NEsper.Core/NEsper.Core/epl/join/pollindex/PollResultIndexingStrategySorted.cs new file mode 100755 index 000000000..19894b7ea --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/pollindex/PollResultIndexingStrategySorted.cs @@ -0,0 +1,73 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.@join.table; + +namespace com.espertech.esper.epl.join.pollindex +{ + /// + /// Strategy for building an index out of poll-results knowing the properties to base the index on. + /// + public class PollResultIndexingStrategySorted : PollResultIndexingStrategy + { + private readonly Type _coercionType; + private readonly EventType _eventType; + private readonly string _propertyName; + private readonly int _streamNum; + + public PollResultIndexingStrategySorted( + int streamNum, + EventType eventType, + string propertyName, + Type coercionType) + { + _streamNum = streamNum; + _eventType = eventType; + _propertyName = propertyName; + _coercionType = coercionType; + } + + public EventTable[] Index(IList pollResult, bool isActiveCache, StatementContext statementContext) + { + if (!isActiveCache) + { + return new EventTable[] + { + new UnindexedEventTableList(pollResult, _streamNum) + }; + } + PropertySortedEventTableFactory tableFactory; + if (_coercionType == null) + { + tableFactory = new PropertySortedEventTableFactory(_streamNum, _eventType, _propertyName); + } + else + { + tableFactory = new PropertySortedEventTableCoercedFactory( + _streamNum, _eventType, _propertyName, _coercionType); + } + EventTable[] tables = tableFactory.MakeEventTables(new EventTableFactoryTableIdentStmt(statementContext)); + foreach (EventTable table in tables) + { + table.Add(pollResult.ToArray()); + } + return tables; + } + + public string ToQueryPlan() + { + return GetType().Name + " property " + _propertyName + " coercion " + _coercionType; + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/join/rep/Cursor.cs b/NEsper.Core/NEsper.Core/epl/join/rep/Cursor.cs new file mode 100755 index 000000000..8559242f6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/rep/Cursor.cs @@ -0,0 +1,42 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; + +namespace com.espertech.esper.epl.join.rep +{ + /// + /// This class supplies position information for + /// to use for iterating over events for lookup. + /// + public class Cursor + { + /// Ctor. + /// is the current event + /// is the current stream + /// is the node containing the set of events to which the event belongs to + public Cursor(EventBean theEvent, int stream, Node node) + { + Event = theEvent; + Stream = stream; + Node = node; + } + + /// Supplies current event. + /// event + public EventBean Event { get; private set; } + + /// Returns current stream the event belongs to. + /// stream number for event + public int Stream { get; private set; } + + /// Returns current result node the event belong to. + /// result node of event + public Node Node { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/rep/Node.cs b/NEsper.Core/NEsper.Core/epl/join/rep/Node.cs new file mode 100755 index 000000000..bff397486 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/rep/Node.cs @@ -0,0 +1,58 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; + +namespace com.espertech.esper.epl.join.rep +{ + /// + /// Node is the structure to hold results of event lookups in joined streams. A node holds + /// a set of event which are the result of a lookup in a stream's table. A Node can be + /// linked to its parent node and the event within the parent node, which was the event + /// that was used to perform a lookup. + /// + public class Node + { + /// Returns the stream number of the stream that supplied the event results. + /// stream number for results + /// + public int Stream { get; private set; } + + /// + /// Gets or sets the parent node, or null if this is a root node. + /// + /// The parent. + /// parent node or null for root node + /// + public Node Parent { get; set; } + + /// + /// Gets or sets lookup event. + /// + /// The parent event. + /// parent node's event that was used to lookup + /// + public EventBean ParentEvent { get; set; } + + /// + /// Gets or sets the events. + /// + /// The events. + public ICollection Events { get; set; } + + /// Ctor. + /// this node stores results for + /// + public Node(int stream) + { + Stream = stream; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/rep/Repository.cs b/NEsper.Core/NEsper.Core/epl/join/rep/Repository.cs new file mode 100755 index 000000000..d4c856dcf --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/rep/Repository.cs @@ -0,0 +1,42 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System.Collections.Generic; +using com.espertech.esper.client; + +namespace com.espertech.esper.epl.join.rep +{ + /// + /// An interface for a repository of events in a lookup/join scheme that + /// supplies events for event stream table lookups and receives results + /// of lookups. + /// + + public interface Repository + { + /// + /// Supply events for performing look ups for a given stream. + /// + /// is the stream to perform lookup for + /// + /// an iterator over events with additional positioning information + /// + + IEnumerator GetCursors(int lookupStream); + + /// + /// Add a lookup result. + /// + /// provides result position and parent event and node information + /// is the events found + /// is the stream number of the stream providing the results + + void AddResult(Cursor cursor, ICollection lookupResults, int resultStream); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/rep/RepositoryImpl.cs b/NEsper.Core/NEsper.Core/epl/join/rep/RepositoryImpl.cs new file mode 100755 index 000000000..606c20fb2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/rep/RepositoryImpl.cs @@ -0,0 +1,129 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.epl.join.rep +{ + /// + /// : a repository for join events and lookup results. + /// + public class RepositoryImpl : Repository + { + private readonly int _numStreams; + private readonly EventBean _rootEvent; + private readonly int _rootStream; + + private IList[] _nodesPerStream; + + /// + /// Ctor. + /// + /// is the stream supplying the root event + /// is the root event + /// is the number of streams + public RepositoryImpl(int rootStream, + EventBean rootEvent, + int numStreams) + { + _rootStream = rootStream; + _rootEvent = rootEvent; + _numStreams = numStreams; + } + + /// + /// Returns a list of nodes that are the lookup results per stream. + /// + /// + /// result nodes per stream + /// + public IList[] NodesPerStream + { + get { return _nodesPerStream; } + } + + #region Repository Members + + public IEnumerator GetCursors(int lookupFromStream) + { + if (lookupFromStream == _rootStream) + { + yield return new Cursor(_rootEvent, _rootStream, null); + } + else + { + IList nodeList = _nodesPerStream[lookupFromStream]; + if (nodeList != null) + { + int nodeListCount = nodeList.Count; + for (int ii = 0; ii < nodeListCount; ii++) + { + Node node = nodeList[ii]; + ICollection eventCollection = node.Events; + foreach (EventBean currEvent in eventCollection) + { + yield return new Cursor(currEvent, lookupFromStream, node); + } + } + } + } + } + + public void AddResult(Cursor cursor, + ICollection lookupResults, + int resultStream) + { + if (lookupResults.IsEmpty()) + { + throw new ArgumentException("Attempting to add zero results"); + } + + Node parentNode = cursor.Node; + if (parentNode == null) + { + var leafNodeInner = new Node(resultStream); + leafNodeInner.Events = lookupResults; + + if (_nodesPerStream == null) + { + _nodesPerStream = new List[_numStreams]; + } + + IList nodesInner = _nodesPerStream[resultStream]; + if (nodesInner == null) + { + nodesInner = new List(); + _nodesPerStream[resultStream] = nodesInner; + } + leafNodeInner.ParentEvent = _rootEvent; + + nodesInner.Add(leafNodeInner); + return; + } + + var leafNode = new Node(resultStream); + leafNode.Events = lookupResults; + leafNode.Parent = cursor.Node; + leafNode.ParentEvent = cursor.Event; + + IList nodes = _nodesPerStream[resultStream]; + if (nodes == null) + { + nodes = new List(); + _nodesPerStream[resultStream] = nodes; + } + + nodes.Add(leafNode); + } + + #endregion + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/join/table/EventTable.cs b/NEsper.Core/NEsper.Core/epl/join/table/EventTable.cs new file mode 100755 index 000000000..7b0b4fc03 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/table/EventTable.cs @@ -0,0 +1,104 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; + +namespace com.espertech.esper.epl.join.table +{ + /// + /// Table of events allowing add and remove. Lookup in table is coordinated + /// through the underlying implementation. + /// + public interface EventTable : IEnumerable + { + /// + /// Add and remove events from table. + /// It is up to the index to decide whether to add first and then remove, + /// or whether to remove and then add. + /// It is important to note that a given event can be in both the + /// removed and the added events. This means that unique indexes probably need to remove first + /// and then add. Most other non-unique indexes will add first and then remove + /// since the an event can be both in the add and the remove stream. + /// + /// to add + /// to remove + void AddRemove(EventBean[] newData, EventBean[] oldData); + + /// + /// Add events to table. + /// + /// to add + void Add(EventBean[] events); + + /// + /// Add event to table. + /// + /// to add + void Add(EventBean @event); + + /// + /// Remove events from table. + /// + /// to remove + void Remove(EventBean[] events); + + /// + /// Remove event from table. + /// + /// to remove + void Remove(EventBean @event); + + /// + /// Returns true if the index is empty, or false if not + /// + /// true for empty index + bool IsEmpty(); + + /// + /// Clear out index. + /// + void Clear(); + + /// + /// Dispose index. + /// + void Destroy(); + + string ToQueryPlan(); + + Type ProviderClass { get; } + + /// + /// If the number of events is readily available, an implementation will return that number + /// or it may return null to indicate that the count is not readily available. + /// + /// number of events + int? NumberOfEvents { get; } + + /// + /// If the index retains events using some key-based _organization this returns the number of keys, + /// and may return null to indicate that either the number of keys is not available or + /// costly to obtain. + /// The number returned can be an estimate and may not be accurate. + /// + /// number of events + int NumKeys { get; } + + /// + /// Return the index object itself, or an object-array for multiple index structures. + /// May return null if the information is not readily available, i.e. externally maintained index + /// + /// index object + object Index { get; } + + EventTableOrganization Organization { get; } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/join/table/EventTableAndNamePair.cs b/NEsper.Core/NEsper.Core/epl/join/table/EventTableAndNamePair.cs new file mode 100755 index 000000000..89873a633 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/table/EventTableAndNamePair.cs @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.join.table +{ + public class EventTableAndNamePair + { + public EventTableAndNamePair(EventTable eventTable, String indexName) + { + EventTable = eventTable; + IndexName = indexName; + } + + public EventTable EventTable { get; private set; } + + public string IndexName { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/join/table/EventTableAsSet.cs b/NEsper.Core/NEsper.Core/epl/join/table/EventTableAsSet.cs new file mode 100755 index 000000000..b67e7adce --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/table/EventTableAsSet.cs @@ -0,0 +1,22 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.epl.join.table +{ + public interface EventTableAsSet : EventTable + { + ISet AllValues { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/table/EventTableFactory.cs b/NEsper.Core/NEsper.Core/epl/join/table/EventTableFactory.cs new file mode 100755 index 000000000..68aee06ef --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/table/EventTableFactory.cs @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.join.table +{ + /// + /// Table of events allowing add and remove. Lookup in table is coordinated through + /// the underlying implementation. + /// + public interface EventTableFactory + { + Type EventTableType { get; } + + EventTable[] MakeEventTables(EventTableFactoryTableIdent tableIdent); + + String ToQueryPlan(); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/table/EventTableFactoryTableIdent.cs b/NEsper.Core/NEsper.Core/epl/join/table/EventTableFactoryTableIdent.cs new file mode 100755 index 000000000..484e9c279 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/table/EventTableFactoryTableIdent.cs @@ -0,0 +1,21 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.service; + +namespace com.espertech.esper.epl.join.table +{ + public interface EventTableFactoryTableIdent + { + StatementContext StatementContext { get; } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/join/table/EventTableFactoryTableIdentAgentInstance.cs b/NEsper.Core/NEsper.Core/epl/join/table/EventTableFactoryTableIdentAgentInstance.cs new file mode 100755 index 000000000..6b0d86f70 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/table/EventTableFactoryTableIdentAgentInstance.cs @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; + +namespace com.espertech.esper.epl.join.table +{ + public class EventTableFactoryTableIdentAgentInstance : EventTableFactoryTableIdent + { + public EventTableFactoryTableIdentAgentInstance(AgentInstanceContext agentInstanceContext) + { + AgentInstanceContext = agentInstanceContext; + } + + public AgentInstanceContext AgentInstanceContext { get; private set; } + + public StatementContext StatementContext + { + get { return AgentInstanceContext.StatementContext; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/join/table/EventTableFactoryTableIdentAgentInstanceSubq.cs b/NEsper.Core/NEsper.Core/epl/join/table/EventTableFactoryTableIdentAgentInstanceSubq.cs new file mode 100755 index 000000000..453870766 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/table/EventTableFactoryTableIdentAgentInstanceSubq.cs @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.context.util; + +namespace com.espertech.esper.epl.join.table +{ + public class EventTableFactoryTableIdentAgentInstanceSubq : EventTableFactoryTableIdentAgentInstance + { + public EventTableFactoryTableIdentAgentInstanceSubq(AgentInstanceContext agentInstanceContext, int subqueryNumber) + : base(agentInstanceContext) + { + SubqueryNumber = subqueryNumber; + } + + public int SubqueryNumber { get; private set; } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/join/table/EventTableFactoryTableIdentStmt.cs b/NEsper.Core/NEsper.Core/epl/join/table/EventTableFactoryTableIdentStmt.cs new file mode 100755 index 000000000..b8ee74ecf --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/table/EventTableFactoryTableIdentStmt.cs @@ -0,0 +1,22 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.service; + +namespace com.espertech.esper.epl.join.table +{ + public class EventTableFactoryTableIdentStmt : EventTableFactoryTableIdent + { + public EventTableFactoryTableIdentStmt(StatementContext statementContext) + { + StatementContext = statementContext; + } + + public StatementContext StatementContext { get; private set; } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/join/table/EventTableOrganization.cs b/NEsper.Core/NEsper.Core/epl/join/table/EventTableOrganization.cs new file mode 100755 index 000000000..d39ae6519 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/table/EventTableOrganization.cs @@ -0,0 +1,44 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.epl.join.table +{ + public class EventTableOrganization + { + public EventTableOrganization( + String indexName, + bool unique, + bool coercing, + int streamNum, + IList expressions, + EventTableOrganizationType type) + { + IndexName = indexName; + IsUnique = unique; + IsCoercing = coercing; + StreamNum = streamNum; + Expressions = expressions; + OrganizationType = type; + } + + public string IndexName { get; private set; } + + public bool IsUnique { get; private set; } + + public int StreamNum { get; private set; } + + public IList Expressions { get; private set; } + + public EventTableOrganizationType OrganizationType { get; private set; } + + public bool IsCoercing { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/join/table/EventTableOrganizationType.cs b/NEsper.Core/NEsper.Core/epl/join/table/EventTableOrganizationType.cs new file mode 100755 index 000000000..b66b24413 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/table/EventTableOrganizationType.cs @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.join.table +{ + public enum EventTableOrganizationType + { + UNORGANIZED, + HASH, + BTREE, + COMPOSITE, + MULTIINDEX, + VDW + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/join/table/EventTableUtil.cs b/NEsper.Core/NEsper.Core/epl/join/table/EventTableUtil.cs new file mode 100755 index 000000000..a0e94627a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/table/EventTableUtil.cs @@ -0,0 +1,111 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.join.plan; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.join.table +{ + public class EventTableUtil { + /// + /// Build an index/table instance using the event properties for the event type. + /// + /// - number of stream indexed + /// - type of event to expect + /// index name + /// context + /// plan item + /// serde if any + /// indicates fire-and-forget + /// indicates unique + /// indicator whether to coerce on value-add + /// table build + public static EventTable BuildIndex(AgentInstanceContext agentInstanceContext, int indexedStreamNum, QueryPlanIndexItem item, EventType eventType, bool coerceOnAddOnly, bool unique, string optionalIndexName, Object optionalSerde, bool isFireAndForget) { + var indexProps = item.IndexProps; + var indexCoercionTypes = Normalize(item.OptIndexCoercionTypes); + var rangeProps = item.RangeProps; + var rangeCoercionTypes = Normalize(item.OptRangeCoercionTypes); + var ident = new EventTableFactoryTableIdentAgentInstance(agentInstanceContext); + EventTableIndexService eventTableIndexService = agentInstanceContext.StatementContext.EventTableIndexService; + + EventTable table; + if (rangeProps == null || rangeProps.Count == 0) { + if (indexProps == null || indexProps.Count == 0) + { + EventTableFactory factory = eventTableIndexService.CreateUnindexed(indexedStreamNum, optionalSerde, isFireAndForget); + table = factory.MakeEventTables(ident)[0]; + } else { + // single index key + if (indexProps.Count == 1) + { + if (indexCoercionTypes == null || indexCoercionTypes.Count == 0) { + EventTableFactory factory = eventTableIndexService.CreateSingle(indexedStreamNum, eventType, indexProps[0], unique, optionalIndexName, optionalSerde, isFireAndForget); + table = factory.MakeEventTables(ident)[0]; + } else { + if (coerceOnAddOnly) { + EventTableFactory factory = eventTableIndexService.CreateSingleCoerceAdd(indexedStreamNum, eventType, indexProps[0], indexCoercionTypes[0], optionalSerde, isFireAndForget); + table = factory.MakeEventTables(ident)[0]; + } else { + EventTableFactory factory = eventTableIndexService.CreateSingleCoerceAll(indexedStreamNum, eventType, indexProps[0], indexCoercionTypes[0], optionalSerde, isFireAndForget); + table = factory.MakeEventTables(ident)[0]; + } + } + } else { + // Multiple index keys + if (indexCoercionTypes == null || indexCoercionTypes.Count == 0) { + EventTableFactory factory = eventTableIndexService.CreateMultiKey(indexedStreamNum, eventType, indexProps, unique, optionalIndexName, optionalSerde, isFireAndForget); + table = factory.MakeEventTables(ident)[0]; + } else { + if (coerceOnAddOnly) { + EventTableFactory factory = eventTableIndexService.CreateMultiKeyCoerceAdd(indexedStreamNum, eventType, indexProps, indexCoercionTypes, isFireAndForget); + table = factory.MakeEventTables(ident)[0]; + } else { + EventTableFactory factory = eventTableIndexService.CreateMultiKeyCoerceAll(indexedStreamNum, eventType, indexProps, indexCoercionTypes, isFireAndForget); + table = factory.MakeEventTables(ident)[0]; + } + } + } + } + } else { + if ((rangeProps.Count == 1) && (indexProps == null || indexProps.Count == 0)) + { + if (rangeCoercionTypes == null) { + EventTableFactory factory = eventTableIndexService.CreateSorted(indexedStreamNum, eventType, rangeProps[0], isFireAndForget); + return factory.MakeEventTables(ident)[0]; + } else { + EventTableFactory factory = eventTableIndexService.CreateSortedCoerce(indexedStreamNum, eventType, rangeProps[0], rangeCoercionTypes[0], isFireAndForget); + return factory.MakeEventTables(ident)[0]; + } + } else { + EventTableFactory factory = eventTableIndexService.CreateComposite(indexedStreamNum, eventType, indexProps, indexCoercionTypes, rangeProps, rangeCoercionTypes, isFireAndForget); + return factory.MakeEventTables(ident)[0]; + } + } + return table; + } + + private static IList Normalize(IList types) { + if (types == null) { + return null; + } + if (CollectionUtil.IsAllNullArray(types)) { + return null; + } + return types; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/join/table/HistoricalStreamIndexDesc.cs b/NEsper.Core/NEsper.Core/epl/join/table/HistoricalStreamIndexDesc.cs new file mode 100755 index 000000000..b69dcc10a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/table/HistoricalStreamIndexDesc.cs @@ -0,0 +1,83 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.epl.join.table +{ + /// + /// Descriptor for an index requirement on a historical stream. + /// + /// Saves and compares the properties indexed and their types, as well as the types + /// of key properties to account for coercion. + /// + public class HistoricalStreamIndexDesc + { + /// Ctor. + /// index properties + /// index property types + /// key property types + public HistoricalStreamIndexDesc(IList indexProperties, Type[] indexPropTypes, Type[] keyPropTypes) + { + IndexProperties = indexProperties; + IndexPropTypes = indexPropTypes; + KeyPropTypes = keyPropTypes; + } + + /// Returns index properties. + /// index properties + internal IList IndexProperties; // { get; private set; } + + /// Returns index property types. + /// index property types + internal Type[] IndexPropTypes; // { get; private set; } + + /// Returns key property types. + /// key property types + internal Type[] KeyPropTypes; // { get; private set; } + + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// + /// true if the specified is equal to the current ; otherwise, false. + /// + /// The parameter is null. + public override bool Equals(object obj) + { + if (this == obj) return true; + if (obj == null || GetType() != obj.GetType()) return false; + + HistoricalStreamIndexDesc that = (HistoricalStreamIndexDesc)obj; + + if (!Collections.AreEqual(IndexPropTypes, that.IndexPropTypes)) return false; + if (!Collections.AreEqual(IndexProperties, that.IndexProperties)) return false; + if (!Collections.AreEqual(KeyPropTypes, that.KeyPropTypes)) return false; + + return true; + } + + /// + /// Serves as a hash function for a particular type. + /// + /// + /// A hash code for the current . + /// + public override int GetHashCode() + { + int result = Collections.GetHashCode(IndexProperties); + result = 31 * result + Collections.GetHashCode(IndexPropTypes); + result = 31 * result + Collections.GetHashCode(KeyPropTypes); + return result; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/table/HistoricalStreamIndexList.cs b/NEsper.Core/NEsper.Core/epl/join/table/HistoricalStreamIndexList.cs new file mode 100755 index 000000000..ae853afca --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/table/HistoricalStreamIndexList.cs @@ -0,0 +1,208 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.@join.@base; +using com.espertech.esper.epl.@join.plan; +using com.espertech.esper.epl.@join.pollindex; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.join.table +{ + /// + /// Manages index-building and sharing for historical streams by collecting required + /// indexes during the query planning phase, and by providing the right lookup strategy + /// and indexing strategy during query execution node creation. + /// + public class HistoricalStreamIndexList + { + private readonly int _historicalStreamNum; + private readonly EventType[] _typesPerStream; + private readonly QueryGraph _queryGraph; + private readonly SortedSet _pollingStreams; + + private IDictionary> _indexesUsedByStreams; + private PollResultIndexingStrategy _masterIndexingStrategy; + + /// Ctor. + /// number of the historical stream + /// event types for each stream + /// relationship between key and index properties + public HistoricalStreamIndexList(int historicalStreamNum, EventType[] typesPerStream, QueryGraph queryGraph) + { + _historicalStreamNum = historicalStreamNum; + _typesPerStream = typesPerStream; + _queryGraph = queryGraph; + _pollingStreams = new SortedSet(); + } + + /// Used during query plan phase to indicate that an index must be provided for use in lookup of historical events by using a stream's events. + /// the stream providing lookup events + public void AddIndex(int streamViewStreamNum) + { + _pollingStreams.Add(streamViewStreamNum); + } + + /// Get the strategies to use for polling from a given stream. + /// the stream providing the polling events + /// looking and indexing strategy + public Pair GetStrategy(int streamViewStreamNum) + { + // If there is only a single polling stream, then build a single index + if (_pollingStreams.Count == 1) + { + return JoinSetComposerPrototypeFactory.DetermineIndexing(_queryGraph, _typesPerStream[_historicalStreamNum], _typesPerStream[streamViewStreamNum], _historicalStreamNum, streamViewStreamNum); + } + + // If there are multiple polling streams, determine if a single index is appropriate. + // An index can be reused if: + // (a) indexed property names are the same + // (b) indexed property types are the same + // (c) key property types are the same (because of coercion) + // A index lookup strategy is always specific to the providing stream. + if (_indexesUsedByStreams == null) + { + _indexesUsedByStreams = new LinkedHashMap>(); + foreach (var pollingStream in _pollingStreams) + { + var queryGraphValue = _queryGraph.GetGraphValue(pollingStream, _historicalStreamNum); + var hashKeyProps = queryGraphValue.HashKeyProps; + var indexProperties = hashKeyProps.Indexed; + + var keyTypes = GetPropertyTypes(hashKeyProps.Keys); + var indexTypes = GetPropertyTypes(_typesPerStream[_historicalStreamNum], indexProperties); + + var desc = new HistoricalStreamIndexDesc(indexProperties, indexTypes, keyTypes); + var usedByStreams = _indexesUsedByStreams.Get(desc); + if (usedByStreams == null) + { + usedByStreams = new List(); + _indexesUsedByStreams.Put(desc, usedByStreams); + } + usedByStreams.Add(pollingStream); + } + + // There are multiple indexes required: + // Build a master indexing strategy that forms multiple indexes and numbers each. + if (_indexesUsedByStreams.Count > 1) + { + var numIndexes = _indexesUsedByStreams.Count; + var indexingStrategies = new PollResultIndexingStrategy[numIndexes]; + + // create an indexing strategy for each index + var count = 0; + foreach (var desc in _indexesUsedByStreams) + { + var sampleStreamViewStreamNum = desc.Value[0]; + indexingStrategies[count] = JoinSetComposerPrototypeFactory.DetermineIndexing(_queryGraph, _typesPerStream[_historicalStreamNum], _typesPerStream[sampleStreamViewStreamNum], _historicalStreamNum, sampleStreamViewStreamNum).Second; + count++; + } + + // create a master indexing strategy that utilizes each indexing strategy to create a set of indexes + var streamNum = streamViewStreamNum; + _masterIndexingStrategy = new ProxyPollResultIndexingStrategy + { + ProcIndex = (pollResult, isActiveCache, statementContext) => + { + var tables = new EventTable[numIndexes]; + for (var i = 0; i < numIndexes; i++) + { + tables[i] = indexingStrategies[i].Index(pollResult, isActiveCache, statementContext)[0]; + } + + var organization = new EventTableOrganization(null, false, false, streamNum, null, EventTableOrganizationType.MULTIINDEX); + return new EventTable[] + { + new MultiIndexEventTable(tables, organization) + }; + }, + ProcToQueryPlan = () => + { + var writer = new StringWriter(); + var delimiter = ""; + foreach (var strategy in indexingStrategies) { + writer.Write(delimiter); + writer.Write(strategy.ToQueryPlan()); + delimiter = ", "; + } + return GetType().FullName + " " + writer; + } + }; + } + } + + // there is one type of index + if (_indexesUsedByStreams.Count == 1) + { + return JoinSetComposerPrototypeFactory.DetermineIndexing( + _queryGraph, _typesPerStream[_historicalStreamNum], _typesPerStream[streamViewStreamNum], _historicalStreamNum, streamViewStreamNum); + } + + // determine which index number the polling stream must use + var indexUsed = 0; + var found = false; + foreach (var desc in _indexesUsedByStreams.Values) + { + if (desc.Contains(streamViewStreamNum)) + { + found = true; + break; + } + indexUsed++; + } + if (!found) { + throw new IllegalStateException("MapIndex not found for use by stream " + streamViewStreamNum); + } + + // Use one of the indexes built by the master index and a lookup strategy + var indexNumber = indexUsed; + HistoricalIndexLookupStrategy innerLookupStrategy = JoinSetComposerPrototypeFactory.DetermineIndexing(_queryGraph, _typesPerStream[_historicalStreamNum], _typesPerStream[streamViewStreamNum], _historicalStreamNum, streamViewStreamNum).First; + + var lookupStrategy = new ProxyHistoricalIndexLookupStrategy + { + ProcLookup = (lookupEvent, index, context) => + { + var multiIndex = (MultiIndexEventTable) index[0]; + var indexToUse = multiIndex.Tables[indexNumber]; + return innerLookupStrategy.Lookup(lookupEvent, new EventTable[] { indexToUse }, context); + }, + ProcToQueryPlan = () => GetType().FullName + " inner: " + innerLookupStrategy.ToQueryPlan() + }; + + return new Pair (lookupStrategy, _masterIndexingStrategy); + } + + private Type[] GetPropertyTypes(EventType eventType, IList properties) + { + var types = new Type[properties.Count]; + for (var i = 0; i < properties.Count; i++) + { + types[i] = eventType.GetPropertyType(properties[i]).GetBoxedType(); + } + return types; + } + + private Type[] GetPropertyTypes(IList hashKeys) + { + var types = new Type[hashKeys.Count]; + for (var i = 0; i < hashKeys.Count; i++) + { + types[i] = hashKeys[i].KeyExpr.ExprEvaluator.ReturnType.GetBoxedType(); + } + return types; + } + + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/table/MultiIndexEventTable.cs b/NEsper.Core/NEsper.Core/epl/join/table/MultiIndexEventTable.cs new file mode 100755 index 000000000..c6e3f219c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/table/MultiIndexEventTable.cs @@ -0,0 +1,159 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.client; +using com.espertech.esper.compat; + +namespace com.espertech.esper.epl.join.table +{ + /// + /// An event table for holding multiple tables for use when multiple indexes of the + /// same dataset must be entered into a cache for use in historical data lookup. + /// + /// Does not allow iteration, adding and removing events. Does allow clearing all tables + /// and asking for filled or empty tables. All tables are expected to be filled and empty + /// at the same time, reflecting multiple indexes on a single set of data. + /// + public class MultiIndexEventTable : EventTable + { + private readonly EventTable[] _tables; + private readonly EventTableOrganization _organization; + + /// + /// Ctor. + /// + /// tables to hold + /// The _organization. + public MultiIndexEventTable(EventTable[] tables, EventTableOrganization organization) + { + _tables = tables; + _organization = organization; + } + + /// Returns all tables. + /// tables + public EventTable[] Tables + { + get { return _tables; } + } + + public void AddRemove(EventBean[] newData, EventBean[] oldData) + { + throw new UnsupportedOperationException(); + } + + public void Add(EventBean[] events) + { + throw new UnsupportedOperationException(); + } + + public void Remove(EventBean[] events) + { + throw new UnsupportedOperationException(); + } + + public void Add(EventBean @event) + { + throw new UnsupportedOperationException(); + } + + public void Remove(EventBean @event) + { + throw new UnsupportedOperationException(); + } + + public IEnumerator GetEnumerator() + { + throw new UnsupportedOperationException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public bool IsEmpty() + { + return _tables[0].IsEmpty(); + } + + public void Clear() + { + for (var i = 0; i < _tables.Length; i++) + { + _tables[i].Clear(); + } + } + + public void Destroy() + { + Clear(); + } + + public String ToQueryPlan() + { + var buf = new StringWriter(); + var delimiter = ""; + foreach (var table in _tables) { + buf.Write(delimiter); + buf.Write(table.ToQueryPlan()); + delimiter = ", "; + } + return GetType().FullName + " " + buf.ToString(); + } + + public int? NumberOfEvents + { + get + { + foreach (var table in _tables) + { + var num = table.NumberOfEvents; + if (num != null) + { + return num; + } + } + return null; + } + } + + public int NumKeys + { + get { return _tables[0].NumKeys; } + } + + public object Index + { + get + { + var indexes = new Object[_tables.Length]; + for (var i = 0; i < indexes.Length; i++) + { + indexes[i] = _tables[i].Index; + } + return indexes; + } + } + + public EventTableOrganization Organization + { + get { return _organization; } + } + + public Type ProviderClass + { + get { return typeof (MultiIndexEventTable); } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/table/PropertyCompositeEventTable.cs b/NEsper.Core/NEsper.Core/epl/join/table/PropertyCompositeEventTable.cs new file mode 100755 index 000000000..802707201 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/table/PropertyCompositeEventTable.cs @@ -0,0 +1,154 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.@join.exec.composite; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.join.table +{ + public abstract class PropertyCompositeEventTable : EventTable + { + private readonly IList _optKeyCoercedTypes; + private readonly IList _optRangeCoercedTypes; + private readonly EventTableOrganization _organization; + + public abstract IDictionary IndexTable { get; } + public abstract CompositeIndexQueryResultPostProcessor PostProcessor { get; } + + public abstract void Add(EventBean @event); + public abstract void Remove(EventBean @event); + public abstract bool IsEmpty(); + public abstract void Clear(); + public abstract void Destroy(); + public abstract IEnumerator GetEnumerator(); + + protected PropertyCompositeEventTable(IList optKeyCoercedTypes, IList optRangeCoercedTypes, EventTableOrganization organization) + { + _optKeyCoercedTypes = optKeyCoercedTypes; + _optRangeCoercedTypes = optRangeCoercedTypes; + _organization = organization; + } + + public void AddRemove(EventBean[] newData, EventBean[] oldData) { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QIndexAddRemove(this, newData, oldData);} + if (newData != null) { + foreach (EventBean theEvent in newData) { + Add(theEvent); + } + } + if (oldData != null) { + foreach (EventBean theEvent in oldData) { + Remove(theEvent); + } + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AIndexAddRemove();} + } + + /// + /// Add an array of events. Same event instance is not added twice. Event properties should be immutable. + /// Allow null passed instead of an empty array. + /// + /// to add + /// ArgumentException if the event was already existed in the index + public void Add(EventBean[] events) + { + if (events != null) { + + if (InstrumentationHelper.ENABLED && events.Length > 0) { + InstrumentationHelper.Get().QIndexAdd(this, events); + foreach (EventBean theEvent in events) { + Add(theEvent); + } + InstrumentationHelper.Get().AIndexAdd(); + return; + } + + foreach (EventBean theEvent in events) { + Add(theEvent); + } + } + } + + /// + /// Remove events. + /// + /// to be removed, can be null instead of an empty array. + /// ArgumentException when the event could not be removed as its not in the index + public void Remove(EventBean[] events) + { + if (events != null) { + + if (InstrumentationHelper.ENABLED && events.Length > 0) { + InstrumentationHelper.Get().QIndexRemove(this, events); + foreach (EventBean theEvent in events) { + Remove(theEvent); + } + InstrumentationHelper.Get().AIndexRemove(); + return; + } + + foreach (EventBean theEvent in events) { + Remove(theEvent); + } + } + } + + public override string ToString() + { + return ToQueryPlan(); + } + + public string ToQueryPlan() + { + return GetType().FullName; + } + + public IList OptRangeCoercedTypes + { + get { return _optRangeCoercedTypes; } + } + + public IList OptKeyCoercedTypes + { + get { return _optKeyCoercedTypes; } + } + + public int? NumberOfEvents + { + get { return null; } + } + + public virtual object Index + { + get { return IndexTable; } + } + + public virtual IDictionary MapIndex + { + get { return IndexTable; } + } + + public EventTableOrganization Organization + { + get { return _organization; } + } + + public abstract Type ProviderClass { get; } + public abstract int NumKeys { get; } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/join/table/PropertyCompositeEventTableFactory.cs b/NEsper.Core/NEsper.Core/epl/join/table/PropertyCompositeEventTableFactory.cs new file mode 100755 index 000000000..440797f4d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/table/PropertyCompositeEventTableFactory.cs @@ -0,0 +1,126 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.@join.exec.composite; + +namespace com.espertech.esper.epl.join.table +{ + /// + /// For use when the index comprises of either two or more ranges or a unique key in + /// combination with a range. Expected at least either (A) one key and one range or + /// (B) zero keys and 2 ranges. + /// + /// - not applicable for range-only lookups (since there the key can be the value itself + /// - not applicable for multiple nested range as ordering not nested + /// - each add/remove and lookup would also need to construct a key object. + /// + public class PropertyCompositeEventTableFactory : EventTableFactory + { + protected readonly int StreamNum; + protected readonly IList OptionalKeyedProps; + protected readonly IList RangeProps; + protected readonly CompositeIndexEnterRemove Chain; + protected readonly IList OptKeyCoercedTypes; + protected readonly IList OptRangeCoercedTypes; + + /// + /// Ctor. + /// + /// the stream number that is indexed + /// types of events indexed + /// The optional keyed props. + /// The opt key coerced types. + /// The range props. + /// property types + public PropertyCompositeEventTableFactory(int streamNum, EventType eventType, IList optionalKeyedProps, IList optKeyCoercedTypes, IList rangeProps, IList optRangeCoercedTypes) + { + StreamNum = streamNum; + RangeProps = rangeProps; + OptionalKeyedProps = optionalKeyedProps; + OptKeyCoercedTypes = optKeyCoercedTypes; + OptRangeCoercedTypes = optRangeCoercedTypes; + + // construct chain + var enterRemoves = new List(); + if (optionalKeyedProps != null && optionalKeyedProps.Count > 0) + { + enterRemoves.Add(new CompositeIndexEnterRemoveKeyed(eventType, optionalKeyedProps, optKeyCoercedTypes)); + } + int count = 0; + foreach (String rangeProp in rangeProps) + { + var coercionType = optRangeCoercedTypes == null ? null : optRangeCoercedTypes[count]; + enterRemoves.Add(new CompositeIndexEnterRemoveRange(eventType, rangeProp, coercionType)); + count++; + } + + // Hook up as chain for remove + CompositeIndexEnterRemove last = null; + foreach (CompositeIndexEnterRemove action in enterRemoves) + { + if (last != null) + { + last.SetNext(action); + } + last = action; + } + Chain = enterRemoves[0]; + } + + public EventTable[] MakeEventTables(EventTableFactoryTableIdent tableIdent) + { + EventTableOrganization organization = Organization; + return new EventTable[] { new PropertyCompositeEventTableImpl( + OptKeyCoercedTypes, OptRangeCoercedTypes, organization, (OptionalKeyedProps != null && OptionalKeyedProps.Count > 0), Chain) }; + } + + protected EventTableOrganization Organization + { + get + { + return new EventTableOrganization( + null, false, OptKeyCoercedTypes != null || OptRangeCoercedTypes != null, StreamNum, + CombinedPropertyLists(OptionalKeyedProps, RangeProps), EventTableOrganizationType.COMPOSITE); + } + } + + public Type EventTableType + { + get { return typeof(PropertyCompositeEventTable); } + } + + public String ToQueryPlan() + { + return GetType().FullName + + " streamNum=" + StreamNum + + " keys=" + OptionalKeyedProps.Render() + + " ranges=" + RangeProps.Render(); + } + + private IList CombinedPropertyLists(IList optionalKeyedProps, IList rangeProps) + { + if (optionalKeyedProps == null) + { + return rangeProps; + } + if (rangeProps == null) + { + return optionalKeyedProps; + } + + return Enumerable.Concat(optionalKeyedProps, rangeProps).ToList(); + //return (IList) CollectionUtil.AddArrays(optionalKeyedProps, rangeProps); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/table/PropertyCompositeEventTableImpl.cs b/NEsper.Core/NEsper.Core/epl/join/table/PropertyCompositeEventTableImpl.cs new file mode 100755 index 000000000..0ff4af402 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/table/PropertyCompositeEventTableImpl.cs @@ -0,0 +1,99 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.join.exec.composite; + +namespace com.espertech.esper.epl.join.table +{ + /// + /// For use when the index comprises of either two or more ranges or a unique key in combination with a range. + /// Expected at least either (A) one key and one range or (B) zero keys and 2 ranges. + /// - not applicable for range-only lookups (since there the key can be the value itself + /// - not applicable for multiple nested range as ordering not nested + /// - each add/remove and lookup would also need to construct a key object. + /// + public class PropertyCompositeEventTableImpl : PropertyCompositeEventTable + { + private readonly CompositeIndexEnterRemove _chain; + + /// + /// Index table (sorted and/or keyed, always nested). + /// + private readonly IDictionary _index; + + public PropertyCompositeEventTableImpl(IList optKeyCoercedTypes, IList optRangeCoercedTypes, EventTableOrganization organization, bool isHashKeyed, CompositeIndexEnterRemove chain) + : base(optKeyCoercedTypes, optRangeCoercedTypes, organization) + { + _chain = chain; + _index = isHashKeyed + ? (IDictionary) new Dictionary() + : (IDictionary) new OrderedDictionary(); + } + + public override object Index + { + get { return _index; } + } + + public override IDictionary IndexTable + { + get { return _index; } + } + + public override void Add(EventBean theEvent) + { + _chain.Enter(theEvent, _index); + } + + public override void Remove(EventBean theEvent) + { + _chain.Remove(theEvent, _index); + } + + public override bool IsEmpty() + { + return _index.IsEmpty(); + } + + public override IEnumerator GetEnumerator() + { + var result = new LinkedHashSet(); + _chain.GetAll(result, _index); + return result.GetEnumerator(); + } + + public override void Clear() + { + _index.Clear(); + } + + public override void Destroy() { + Clear(); + } + + public override int NumKeys + { + get { return _index.Count; } + } + + public override Type ProviderClass + { + get { return typeof (PropertyCompositeEventTable); } + } + + public override CompositeIndexQueryResultPostProcessor PostProcessor + { + get { return null; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/join/table/PropertyIndexedEventTable.cs b/NEsper.Core/NEsper.Core/epl/join/table/PropertyIndexedEventTable.cs new file mode 100755 index 000000000..c0e099122 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/table/PropertyIndexedEventTable.cs @@ -0,0 +1,144 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.events; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.join.table +{ + /// + /// Index that organizes events by the event property values into hash buckets. Based on a HashMap + /// with keys that store the property values. + /// Takes a list of property names as parameter. Doesn't care which event type the events have as long as the properties + /// exist. If the same event is added twice, the class throws an exception on add. + /// + public abstract class PropertyIndexedEventTable : EventTable + { + protected readonly EventPropertyGetter[] propertyGetters; + protected readonly EventTableOrganization organization; + + public abstract ISet Lookup(object[] keys); + + public abstract void Add(EventBean @event); + public abstract void Remove(EventBean @event); + public abstract bool IsEmpty(); + public abstract void Clear(); + public abstract void Destroy(); + public abstract IEnumerator GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + protected PropertyIndexedEventTable(EventPropertyGetter[] propertyGetters, EventTableOrganization organization) + { + this.propertyGetters = propertyGetters; + this.organization = organization; + } + + /// + /// Determine multikey for index access. + /// + /// to get properties from for key + /// multi key + protected virtual MultiKeyUntyped GetMultiKey(EventBean theEvent) + { + return EventBeanUtility.GetMultiKey(theEvent, propertyGetters); + } + + public virtual void AddRemove(EventBean[] newData, EventBean[] oldData) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QIndexAddRemove(this, newData, oldData);} + if (newData != null) { + foreach (EventBean theEvent in newData) { + Add(theEvent); + } + } + if (oldData != null) { + foreach (EventBean theEvent in oldData) { + Remove(theEvent); + } + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AIndexAddRemove();} + } + + /// + /// Add an array of events. Same event instance is not added twice. Event properties should be immutable. + /// Allow null passed instead of an empty array. + /// + /// to add + /// ArgumentException if the event was already existed in the index + public virtual void Add(EventBean[] events) + { + if (events != null) { + + if (InstrumentationHelper.ENABLED && events.Length > 0) { + InstrumentationHelper.Get().QIndexAdd(this, events); + foreach (EventBean theEvent in events) { + Add(theEvent); + } + InstrumentationHelper.Get().AIndexAdd(); + return; + } + + foreach (EventBean theEvent in events) { + Add(theEvent); + } + } + } + + /// + /// Remove events. + /// + /// to be removed, can be null instead of an empty array. + /// ArgumentException when the event could not be removed as its not in the index + public virtual void Remove(EventBean[] events) + { + if (events != null) { + + if (InstrumentationHelper.ENABLED && events.Length > 0) { + InstrumentationHelper.Get().QIndexRemove(this, events); + foreach (EventBean theEvent in events) { + Remove(theEvent); + } + InstrumentationHelper.Get().AIndexRemove(); + return; + } + + foreach (EventBean theEvent in events) { + Remove(theEvent); + } + } + } + + public string ToQueryPlan() + { + return this.GetType().Name + + " streamNum=" + organization.StreamNum + + " propertyGetters=" + CompatExtensions.Render(propertyGetters); + } + + public EventTableOrganization Organization + { + get { return organization; } + } + + public abstract Type ProviderClass { get; } + public abstract int? NumberOfEvents { get; } + public abstract int NumKeys { get; } + public abstract object Index { get; } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/join/table/PropertyIndexedEventTableCoerceAdd.cs b/NEsper.Core/NEsper.Core/epl/join/table/PropertyIndexedEventTableCoerceAdd.cs new file mode 100755 index 000000000..d71e4c75c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/table/PropertyIndexedEventTableCoerceAdd.cs @@ -0,0 +1,57 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.join.table +{ + /// + /// MapIndex that organizes events by the event property values into hash buckets. Based + /// on a HashMap with keys + /// that store the property values. Performs coercion of the index keys before + /// storing the keys. + /// + /// Takes a list of property names as parameter. Doesn't care which event type the events + /// have as long as the properties exist. If the same event is added twice, the class + /// throws an exception on add. + /// + public class PropertyIndexedEventTableCoerceAdd : PropertyIndexedEventTableUnadorned + { + protected readonly Coercer[] Coercers; + protected readonly Type[] CoercionTypes; + + public PropertyIndexedEventTableCoerceAdd(EventPropertyGetter[] propertyGetters, EventTableOrganization organization, Coercer[] coercers, Type[] coercionTypes) + : base(propertyGetters, organization) + { + Coercers = coercers; + CoercionTypes = coercionTypes; + } + + protected override MultiKeyUntyped GetMultiKey(EventBean theEvent) + { + var keyValues = new Object[propertyGetters.Length]; + for (int i = 0; i < propertyGetters.Length; i++) + { + var value = propertyGetters[i].Get(theEvent); + if ((value != null) && (value.GetType() != CoercionTypes[i])) + { + if (value.IsNumber()) + { + value = Coercers[i].Invoke(value); + } + } + keyValues[i] = value; + } + return new MultiKeyUntyped(keyValues); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/table/PropertyIndexedEventTableCoerceAddFactory.cs b/NEsper.Core/NEsper.Core/epl/join/table/PropertyIndexedEventTableCoerceAddFactory.cs new file mode 100755 index 000000000..7ba903f82 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/table/PropertyIndexedEventTableCoerceAddFactory.cs @@ -0,0 +1,67 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.join.table +{ + /// + /// MapIndex that organizes events by the event property values into hash buckets. Based + /// on a HashMap with keys + /// that store the property values. Performs coercion of the index keys before storing + /// the keys. + /// + /// Takes a list of property names as parameter. Doesn't care which event type the events have + /// as long as the properties exist. If the same event is added twice, the class throws an + /// exception on add. + /// + public class PropertyIndexedEventTableCoerceAddFactory : PropertyIndexedEventTableFactory + { + protected readonly Coercer[] Coercers; + protected readonly Type[] CoercionType; + + /// Ctor. + /// is the stream number of the indexed stream + /// is the event type of the indexed stream + /// are the property names to get property values + /// are the classes to coerce indexed values to + public PropertyIndexedEventTableCoerceAddFactory(int streamNum, EventType eventType, IList propertyNames, IList coercionType) + : base(streamNum, eventType, propertyNames, false, null) + { + CoercionType = coercionType.ToArray(); + Coercers = new Coercer[coercionType.Count]; + for (int i = 0; i < coercionType.Count; i++) + { + if (coercionType[i].IsNumeric()) + { + Coercers[i] = CoercerFactory.GetCoercer(null, coercionType[i]); + } + } + } + + public override EventTable[] MakeEventTables(EventTableFactoryTableIdent tableIdent) + { + EventTableOrganization organization = Organization; + return new EventTable[] { new PropertyIndexedEventTableCoerceAdd(propertyGetters, organization, Coercers, CoercionType) }; + } + + protected override EventTableOrganization Organization + { + get + { + return new EventTableOrganization( + optionalIndexName, unique, true, streamNum, propertyNames, EventTableOrganizationType.HASH); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/table/PropertyIndexedEventTableCoerceAll.cs b/NEsper.Core/NEsper.Core/epl/join/table/PropertyIndexedEventTableCoerceAll.cs new file mode 100755 index 000000000..88cd19297 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/table/PropertyIndexedEventTableCoerceAll.cs @@ -0,0 +1,59 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.join.table +{ + /// + /// MapIndex that organizes events by the event property values into hash buckets. Based + /// on a HashMap with keys + /// that store the property values. Performs coercion of the index keys before storing + /// the keys, and coercion of the lookup keys before lookup. + /// + /// Takes a list of property names as parameter. Doesn't care which event type the events have + /// as long as the properties exist. If the same event is added twice, the class throws an + /// exception on add. + /// + public class PropertyIndexedEventTableCoerceAll : PropertyIndexedEventTableCoerceAdd + { + public PropertyIndexedEventTableCoerceAll(EventPropertyGetter[] propertyGetters, EventTableOrganization organization, Coercer[] coercers, Type[] coercionType) + : base(propertyGetters, organization, coercers, coercionType) + { + } + + /// Returns the set of events that have the same property value as the given event. + /// to compare against + /// set of events with property value, or null if none found (never returns zero-sized set) + public override ISet Lookup(Object[] keys) + { + for (int i = 0; i < keys.Length; i++) + { + var coercionType = CoercionTypes[i]; + var key = keys[i]; + + if ((key != null) && (key.GetType() != coercionType)) + { + if (key.IsNumber()) + { + key = CoercerFactory.CoerceBoxed(key, CoercionTypes[i]); + keys[i] = key; + } + } + } + + return propertyIndex.Get(new MultiKeyUntyped(keys)); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/table/PropertyIndexedEventTableCoerceAllFactory.cs b/NEsper.Core/NEsper.Core/epl/join/table/PropertyIndexedEventTableCoerceAllFactory.cs new file mode 100755 index 000000000..b83916943 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/table/PropertyIndexedEventTableCoerceAllFactory.cs @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; + +namespace com.espertech.esper.epl.join.table +{ + /// + /// Index that organizes events by the event property values into hash buckets. Based + /// on a HashMap with + /// keys that store the property values. Performs coercion of the index keys + /// before storing the keys, and coercion of the lookup keys before lookup. + /// + /// Takes a list of property names as parameter. Doesn't care which event type the events + /// have as long as the properties exist. If the same event is added twice, the class + /// throws an exception on add. + /// + public class PropertyIndexedEventTableCoerceAllFactory : PropertyIndexedEventTableCoerceAddFactory + { + /// Ctor. + /// is the stream number of the indexed stream + /// is the event type of the indexed stream + /// are the property names to get property values + /// are the classes to coerce indexed values to + public PropertyIndexedEventTableCoerceAllFactory(int streamNum, EventType eventType, IList propertyNames, IList coercionType) + : base(streamNum, eventType, propertyNames, coercionType) + { + } + + public override EventTable[] MakeEventTables(EventTableFactoryTableIdent tableIdent) + { + EventTableOrganization organization = Organization; + return new EventTable[] { new PropertyIndexedEventTableCoerceAll(propertyGetters, organization, Coercers, CoercionType) }; + } + + protected override EventTableOrganization Organization + { + get + { + return new EventTableOrganization( + optionalIndexName, unique, true, streamNum, propertyNames, EventTableOrganizationType.HASH); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/table/PropertyIndexedEventTableFactory.cs b/NEsper.Core/NEsper.Core/epl/join/table/PropertyIndexedEventTableFactory.cs new file mode 100755 index 000000000..444358885 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/table/PropertyIndexedEventTableFactory.cs @@ -0,0 +1,123 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; + +namespace com.espertech.esper.epl.join.table +{ + /// + /// Index factory that organizes events by the event property values into hash buckets. Based on a HashMap + /// with keys that store the property values. + /// Takes a list of property names as parameter. Doesn't care which event type the events have as long as the properties + /// exist. If the same event is added twice, the class throws an exception on add. + /// + public class PropertyIndexedEventTableFactory : EventTableFactory + { + protected readonly int streamNum; + protected readonly IList propertyNames; + protected readonly bool unique; + protected readonly string optionalIndexName; + + /// + /// Getters for properties. + /// + protected readonly EventPropertyGetter[] propertyGetters; + + /// + /// Ctor. + /// + /// the stream number that is indexed + /// types of events indexed + /// property names to use for indexing + /// + /// + public PropertyIndexedEventTableFactory(int streamNum, EventType eventType, IList propertyNames, bool unique, string optionalIndexName) + { + this.streamNum = streamNum; + this.propertyNames = propertyNames; + this.unique = unique; + this.optionalIndexName = optionalIndexName; + + // Init getters + propertyGetters = new EventPropertyGetter[propertyNames.Count]; + for (int i = 0; i < propertyNames.Count; i++) + { + propertyGetters[i] = eventType.GetGetter(propertyNames[i]); + } + } + + public virtual EventTable[] MakeEventTables(EventTableFactoryTableIdent tableIdent) + { + EventTableOrganization organization = Organization; + if (unique) + { + return new EventTable[] { new PropertyIndexedEventTableUnique(propertyGetters, organization) }; + } + else + { + return new EventTable[] { new PropertyIndexedEventTableUnadorned(propertyGetters, organization) }; + } + } + + public Type EventTableType + { + get + { + return unique ? typeof(PropertyIndexedEventTableUnique) : typeof(PropertyIndexedEventTableUnadorned); + } + } + + public string ToQueryPlan() + { + return string.Format("{0}{1} streamNum={2} propertyNames={3}", + GetType().Name, (unique ? " unique" : " non-unique"), streamNum, propertyNames.Render()); + } + + public int StreamNum + { + get { return streamNum; } + } + + public IList PropertyNames + { + get { return propertyNames; } + } + + public bool IsUnique + { + get { return unique; } + } + + public string OptionalIndexName + { + get { return optionalIndexName; } + } + + public EventPropertyGetter[] PropertyGetters + { + get { return propertyGetters; } + } + + protected virtual EventTableOrganization Organization + { + get + { + return new EventTableOrganization( + optionalIndexName, unique, false, + streamNum, propertyNames, EventTableOrganizationType.HASH); + } + } + + private static readonly ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/join/table/PropertyIndexedEventTableSingle.cs b/NEsper.Core/NEsper.Core/epl/join/table/PropertyIndexedEventTableSingle.cs new file mode 100755 index 000000000..ff6970817 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/table/PropertyIndexedEventTableSingle.cs @@ -0,0 +1,145 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.join.table +{ + /// + /// Index that organizes events by the event property values into hash buckets. Based on a HashMap + /// with keys that store the property values. + /// + public abstract class PropertyIndexedEventTableSingle : EventTable + { + protected readonly EventPropertyGetter propertyGetter; + protected readonly EventTableOrganization organization; + + public abstract ISet Lookup(object key); + + public abstract IEnumerator GetEnumerator(); + public abstract void Add(EventBean @event); + public abstract void Remove(EventBean @event); + public abstract bool IsEmpty(); + public abstract void Clear(); + public abstract void Destroy(); + public abstract Type ProviderClass { get; } + public abstract int? NumberOfEvents { get; } + public abstract int NumKeys { get; } + public abstract object Index { get; } + + protected PropertyIndexedEventTableSingle(EventPropertyGetter propertyGetter, EventTableOrganization organization) + { + this.propertyGetter = propertyGetter; + this.organization = organization; + } + + /// + /// Determine multikey for index access. + /// + /// to get properties from for key + /// multi key + protected virtual object GetKey(EventBean theEvent) + { + return propertyGetter.Get(theEvent); + } + + public virtual void AddRemove(EventBean[] newData, EventBean[] oldData) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QIndexAddRemove(this, newData, oldData);} + if (newData != null) { + foreach (EventBean theEvent in newData) { + Add(theEvent); + } + } + if (oldData != null) { + foreach (EventBean theEvent in oldData) { + Remove(theEvent); + } + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AIndexAddRemove();} + } + + /// + /// Add an array of events. Same event instance is not added twice. Event properties should be immutable. + /// Allow null passed instead of an empty array. + /// + /// to add + /// ArgumentException if the event was already existed in the index + public virtual void Add(EventBean[] events) + { + if (events != null) { + + if (InstrumentationHelper.ENABLED && events.Length > 0) { + InstrumentationHelper.Get().QIndexAdd(this, events); + foreach (EventBean theEvent in events) { + Add(theEvent); + } + InstrumentationHelper.Get().AIndexAdd(); + return; + } + + foreach (EventBean theEvent in events) { + Add(theEvent); + } + } + } + + /// + /// Remove events. + /// + /// to be removed, can be null instead of an empty array. + /// ArgumentException when the event could not be removed as its not in the index + public virtual void Remove(EventBean[] events) + { + if (events != null) { + + if (InstrumentationHelper.ENABLED && events.Length > 0) { + InstrumentationHelper.Get().QIndexRemove(this, events); + foreach (EventBean theEvent in events) { + Remove(theEvent); + } + InstrumentationHelper.Get().AIndexRemove(); + return; + } + + foreach (EventBean theEvent in events) { + Remove(theEvent); + } + } + } + + public override string ToString() + { + return ToQueryPlan(); + } + + public virtual string ToQueryPlan() + { + return GetType().Name + + " streamNum=" + organization.StreamNum + + " _propertyGetter=" + propertyGetter; + } + + public EventTableOrganization Organization + { + get { return organization; } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/join/table/PropertyIndexedEventTableSingleArrayFactory.cs b/NEsper.Core/NEsper.Core/epl/join/table/PropertyIndexedEventTableSingleArrayFactory.cs new file mode 100755 index 000000000..caa3c24ff --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/table/PropertyIndexedEventTableSingleArrayFactory.cs @@ -0,0 +1,99 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.join.table +{ + /// + /// Index factory that organizes events by the event property values into hash buckets. + /// Based on a Dictionary with + /// keys that store the property values. + /// + public class PropertyIndexedEventTableSingleArrayFactory : EventTableFactory + { + protected readonly int StreamNum; + protected readonly String[] PropertyNames; + protected readonly bool Unique; + protected readonly String OptionalIndexName; + + protected readonly EventPropertyGetter[] PropertyGetters; + + /// + /// Ctor. + /// + /// the stream number that is indexed + /// types of events indexed + /// The property names. + /// if set to true [unique]. + /// Name of the optional index. + public PropertyIndexedEventTableSingleArrayFactory(int streamNum, EventType eventType, String[] propertyNames, bool unique, String optionalIndexName) + { + StreamNum = streamNum; + PropertyNames = propertyNames; + Unique = unique; + OptionalIndexName = optionalIndexName; + + // Init getters + PropertyGetters = new EventPropertyGetter[propertyNames.Length]; + for (var i = 0; i < propertyNames.Length; i++) + { + PropertyGetters[i] = EventBeanUtility.GetAssertPropertyGetter(eventType, propertyNames[i]); + } + } + + public EventTable[] MakeEventTables(EventTableFactoryTableIdent tableIdent) + { + var tables = new EventTable[PropertyGetters.Length]; + if (Unique) + { + for (var i = 0; i < tables.Length; i++) + { + var organization = new EventTableOrganization(OptionalIndexName, Unique, false, StreamNum, new String[] { PropertyNames[i] }, EventTableOrganizationType.HASH); + tables[i] = new PropertyIndexedEventTableSingleUnique(PropertyGetters[i], organization); + } + } + else + { + for (var i = 0; i < tables.Length; i++) + { + var organization = new EventTableOrganization(OptionalIndexName, Unique, false, StreamNum, new String[] { PropertyNames[i] }, EventTableOrganizationType.HASH); + tables[i] = new PropertyIndexedEventTableSingleUnadorned(PropertyGetters[i], organization); + } + } + return tables; + } + + public Type EventTableType + { + get + { + if (Unique) + { + return typeof(PropertyIndexedEventTableSingleUnique); + } + else + { + return typeof(PropertyIndexedEventTableSingle); + } + } + } + + public String ToQueryPlan() + { + return GetType().Name + + (Unique ? " unique" : " non-unique") + + " streamNum=" + StreamNum + + " propertyNames=" + PropertyNames.Render(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/table/PropertyIndexedEventTableSingleCoerceAdd.cs b/NEsper.Core/NEsper.Core/epl/join/table/PropertyIndexedEventTableSingleCoerceAdd.cs new file mode 100755 index 000000000..806934d9e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/table/PropertyIndexedEventTableSingleCoerceAdd.cs @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.events; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.join.table +{ + public class PropertyIndexedEventTableSingleCoerceAdd : PropertyIndexedEventTableSingleUnadorned + { + private readonly Coercer _coercer; + private readonly Type _coercionType; + + public PropertyIndexedEventTableSingleCoerceAdd(EventPropertyGetter propertyGetter, EventTableOrganization organization, Coercer coercer, Type coercionType) + : base(propertyGetter, organization) + { + _coercer = coercer; + _coercionType = coercionType; + } + + protected override Object GetKey(EventBean theEvent) + { + var keyValue = base.GetKey(theEvent); + if ((keyValue != null) && (keyValue.GetType() != _coercionType)) + { + keyValue = keyValue.IsNumber() + ? _coercer.Invoke(keyValue) + : EventBeanUtility.Coerce(keyValue, _coercionType); + } + return keyValue; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/table/PropertyIndexedEventTableSingleCoerceAddFactory.cs b/NEsper.Core/NEsper.Core/epl/join/table/PropertyIndexedEventTableSingleCoerceAddFactory.cs new file mode 100755 index 000000000..6a1ef1b26 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/table/PropertyIndexedEventTableSingleCoerceAddFactory.cs @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.join.table +{ + public class PropertyIndexedEventTableSingleCoerceAddFactory : PropertyIndexedEventTableSingleFactory + { + internal readonly Coercer Coercer; + internal readonly Type CoercionType; + + /// Ctor. + /// is the stream number of the indexed stream + /// is the event type of the indexed stream + /// are the property names to get property values + /// are the classes to coerce indexed values to + public PropertyIndexedEventTableSingleCoerceAddFactory( + int streamNum, + EventType eventType, + String propertyName, + Type coercionType) + : base(streamNum, eventType, propertyName, false, null) + { + CoercionType = coercionType; + Coercer = coercionType.IsNumeric() ? CoercerFactory.GetCoercer(null, coercionType) : null; + } + + public override EventTable[] MakeEventTables(EventTableFactoryTableIdent tableIdent) + { + EventTableOrganization organization = GetOrganization(); + return new EventTable[] { new PropertyIndexedEventTableSingleCoerceAdd(PropertyGetter, organization, Coercer, CoercionType) }; + } + + protected virtual EventTableOrganization GetOrganization() + { + return new EventTableOrganization(OptionalIndexName, Unique, true, StreamNum, new String[] { PropertyName }, EventTableOrganizationType.HASH); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/join/table/PropertyIndexedEventTableSingleCoerceAll.cs b/NEsper.Core/NEsper.Core/epl/join/table/PropertyIndexedEventTableSingleCoerceAll.cs new file mode 100755 index 000000000..494b5f9b2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/table/PropertyIndexedEventTableSingleCoerceAll.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.events; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.join.table +{ + public class PropertyIndexedEventTableSingleCoerceAll : PropertyIndexedEventTableSingleCoerceAdd + { + private readonly Type _coercionType; + + public PropertyIndexedEventTableSingleCoerceAll(EventPropertyGetter propertyGetter, EventTableOrganization organization, Coercer coercer, Type coercionType) + : base(propertyGetter, organization, coercer, coercionType) + { + _coercionType = coercionType; + } + + /// Returns the set of events that have the same property value as the given event. + /// set of events with property value, or null if none found (never returns zero-sized set) + public override ISet Lookup(Object key) + { + key = EventBeanUtility.Coerce(key, _coercionType); + return propertyIndex.Get(key); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/table/PropertyIndexedEventTableSingleCoerceAllFactory.cs b/NEsper.Core/NEsper.Core/epl/join/table/PropertyIndexedEventTableSingleCoerceAllFactory.cs new file mode 100755 index 000000000..8de9b5687 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/table/PropertyIndexedEventTableSingleCoerceAllFactory.cs @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.epl.join.table +{ + public class PropertyIndexedEventTableSingleCoerceAllFactory : PropertyIndexedEventTableSingleCoerceAddFactory + { + /// + /// Ctor. + /// + /// is the stream number of the indexed stream + /// is the event type of the indexed stream + /// are the property names to get property values + /// are the classes to coerce indexed values to + public PropertyIndexedEventTableSingleCoerceAllFactory(int streamNum, EventType eventType, String propertyName, Type coercionType) + : base(streamNum, eventType, propertyName, coercionType) + { + } + + public override EventTable[] MakeEventTables(EventTableFactoryTableIdent tableIdent) + { + EventTableOrganization organization = GetOrganization(); + return new EventTable[] { new PropertyIndexedEventTableSingleCoerceAll(PropertyGetter, organization, Coercer, CoercionType) }; + } + + protected override EventTableOrganization GetOrganization() + { + return new EventTableOrganization(OptionalIndexName, Unique, true, StreamNum, new String[] { PropertyName }, EventTableOrganizationType.HASH); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/table/PropertyIndexedEventTableSingleFactory.cs b/NEsper.Core/NEsper.Core/epl/join/table/PropertyIndexedEventTableSingleFactory.cs new file mode 100755 index 000000000..bad3e28c9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/table/PropertyIndexedEventTableSingleFactory.cs @@ -0,0 +1,85 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.join.table +{ + /// + /// Index factory that organizes events by the event property values into hash buckets. + /// Based on a HashMap with + /// keys that store the property values. + /// + public class PropertyIndexedEventTableSingleFactory : EventTableFactory + { + protected readonly int StreamNum; + protected readonly String PropertyName; + protected readonly bool Unique; + protected readonly String OptionalIndexName; + + protected readonly EventPropertyGetter PropertyGetter; + + /// + /// Ctor. + /// + /// the stream number that is indexed + /// types of events indexed + /// Name of the property. + /// if set to true [unique]. + /// Name of the optional index. + public PropertyIndexedEventTableSingleFactory( + int streamNum, + EventType eventType, + String propertyName, + bool unique, + String optionalIndexName) + { + StreamNum = streamNum; + PropertyName = propertyName; + Unique = unique; + OptionalIndexName = optionalIndexName; + + // Init getters + PropertyGetter = EventBeanUtility.GetAssertPropertyGetter(eventType, propertyName); + } + + public virtual EventTable[] MakeEventTables(EventTableFactoryTableIdent tableIdent) + { + var organization = new EventTableOrganization(OptionalIndexName, Unique, false, StreamNum, new String[] { PropertyName }, EventTableOrganizationType.HASH); + if (Unique) + { + return new EventTable[] { new PropertyIndexedEventTableSingleUnique(PropertyGetter, organization) }; + } + else + { + return new EventTable[] { new PropertyIndexedEventTableSingleUnadorned(PropertyGetter, organization) }; + } + } + + public virtual Type EventTableType + { + get + { + return Unique + ? typeof(PropertyIndexedEventTableSingleUnique) + : typeof(PropertyIndexedEventTableSingle); + } + } + + public virtual String ToQueryPlan() + { + return GetType().Name + + (Unique ? " unique" : " non-unique") + + " streamNum=" + StreamNum + + " propertyName=" + PropertyName; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/table/PropertyIndexedEventTableSingleUnadorned.cs b/NEsper.Core/NEsper.Core/epl/join/table/PropertyIndexedEventTableSingleUnadorned.cs new file mode 100755 index 000000000..d3c4555b3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/table/PropertyIndexedEventTableSingleUnadorned.cs @@ -0,0 +1,121 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.epl.join.table +{ + /// + /// Index that organizes events by the event property values into hash buckets. Based on a HashMap + /// with keys that store the property values. + /// + public class PropertyIndexedEventTableSingleUnadorned : PropertyIndexedEventTableSingle + { + protected readonly IDictionary> propertyIndex; + + public PropertyIndexedEventTableSingleUnadorned(EventPropertyGetter propertyGetter, EventTableOrganization organization) + : base(propertyGetter, organization) + { + propertyIndex = new Dictionary>().WithNullSupport(); + } + + /// + /// Returns the set of events that have the same property value as the given event. + /// + /// to compare against + /// set of events with property value, or null if none found (never returns zero-sized set) + public override ISet Lookup(object key) + { + return propertyIndex.Get(key); + } + + public override void Add(EventBean theEvent) + { + var key = GetKey(theEvent); + + var events = propertyIndex.Get(key); + if (events == null) + { + events = new LinkedHashSet(); + propertyIndex.Put(key, events); + } + + events.Add(theEvent); + } + + public override void Remove(EventBean theEvent) + { + var key = GetKey(theEvent); + + var events = propertyIndex.Get(key); + if (events == null) + { + return; + } + + if (!events.Remove(theEvent)) + { + // Not an error, its possible that an old-data event is artificial (such as for statistics) and + // thus did not correspond to a new-data event raised earlier. + return; + } + + if (events.IsEmpty()) + { + propertyIndex.Remove(key); + } + } + + public override bool IsEmpty() + { + return propertyIndex.IsEmpty(); + } + + public override IEnumerator GetEnumerator() + { + return propertyIndex.Values + .SelectMany(eventBeans => eventBeans) + .GetEnumerator(); + } + + public override void Clear() + { + propertyIndex.Clear(); + } + + public override void Destroy() { + Clear(); + } + + public override int? NumberOfEvents + { + get { return null; } + } + + public override int NumKeys + { + get { return propertyIndex.Count; } + } + + public override object Index + { + get { return propertyIndex; } + } + + public override Type ProviderClass + { + get { return typeof (PropertyIndexedEventTableSingle); } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/join/table/PropertyIndexedEventTableSingleUnique.cs b/NEsper.Core/NEsper.Core/epl/join/table/PropertyIndexedEventTableSingleUnique.cs new file mode 100755 index 000000000..6e4cda886 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/table/PropertyIndexedEventTableSingleUnique.cs @@ -0,0 +1,146 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.join.table +{ + /// + /// Unique index. + /// + public class PropertyIndexedEventTableSingleUnique + : PropertyIndexedEventTableSingle + , EventTableAsSet + { + private readonly IDictionary _propertyIndex; + private readonly bool _canClear; + + public PropertyIndexedEventTableSingleUnique(EventPropertyGetter propertyGetter, EventTableOrganization organization) + : base(propertyGetter, organization) + { + _propertyIndex = new NullableDictionary(); + _canClear = true; + } + + public PropertyIndexedEventTableSingleUnique(EventPropertyGetter propertyGetter, EventTableOrganization organization, IDictionary propertyIndex) + : base(propertyGetter, organization) + { + _propertyIndex = propertyIndex; + _canClear = false; + } + + public override ISet Lookup(object key) + { + EventBean @event = _propertyIndex.Get(key); + if (@event != null) { + return Collections.SingletonSet(@event); + } + return null; + } + + public override int NumKeys + { + get { return _propertyIndex.Count; } + } + + public override object Index + { + get { return _propertyIndex; } + } + + /// + /// Remove then add events. + /// + /// to add + /// to remove + public override void AddRemove(EventBean[] newData, EventBean[] oldData) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QIndexAddRemove(this, newData, oldData);} + if (oldData != null) { + foreach (EventBean theEvent in oldData) { + Remove(theEvent); + } + } + if (newData != null) { + foreach (EventBean theEvent in newData) { + Add(theEvent); + } + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AIndexAddRemove();} + } + + public override void Add(EventBean theEvent) + { + var key = GetKey(theEvent); + + var existing = _propertyIndex.Push(key, theEvent); + if (existing != null && !existing.Equals(theEvent)) { + throw PropertyIndexedEventTableUnique.HandleUniqueIndexViolation(organization.IndexName, key); + } + } + + public override void Remove(EventBean theEvent) + { + object key = GetKey(theEvent); + _propertyIndex.Remove(key); + } + + public override bool IsEmpty() + { + return _propertyIndex.IsEmpty(); + } + + public override IEnumerator GetEnumerator() + { + return _propertyIndex.Values.GetEnumerator(); + } + + public override void Clear() + { + if (_canClear) { + _propertyIndex.Clear(); + } + } + + public override void Destroy() { + Clear(); + } + + public override string ToString() + { + return ToQueryPlan(); + } + + public override int? NumberOfEvents + { + get { return _propertyIndex.Count; } + } + + public ISet AllValues + { + get + { + if (_propertyIndex.IsEmpty()) + { + return Collections.GetEmptySet(); + } + return new HashSet(_propertyIndex.Values); + } + } + + public override Type ProviderClass + { + get { return typeof (PropertyIndexedEventTableSingleUnique); } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/join/table/PropertyIndexedEventTableUnadorned.cs b/NEsper.Core/NEsper.Core/epl/join/table/PropertyIndexedEventTableUnadorned.cs new file mode 100755 index 000000000..dea4070d7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/table/PropertyIndexedEventTableUnadorned.cs @@ -0,0 +1,118 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.epl.join.table +{ + public class PropertyIndexedEventTableUnadorned : PropertyIndexedEventTable + { + protected readonly IDictionary> propertyIndex; + + public PropertyIndexedEventTableUnadorned(EventPropertyGetter[] propertyGetters, EventTableOrganization organization) + : base(propertyGetters, organization) + { + propertyIndex = new Dictionary>(); + } + + /// + /// Returns the set of events that have the same property value as the given event. + /// + /// to compare against + /// set of events with property value, or null if none found (never returns zero-sized set) + public override ISet Lookup(object[] keys) + { + var key = new MultiKeyUntyped(keys); + return propertyIndex.Get(key); + } + + public override void Add(EventBean theEvent) + { + var key = GetMultiKey(theEvent); + + var events = propertyIndex.Get(key); + if (events == null) + { + events = new LinkedHashSet(); + propertyIndex.Put(key, events); + } + + events.Add(theEvent); + } + + public override void Remove(EventBean theEvent) + { + var key = GetMultiKey(theEvent); + + var events = propertyIndex.Get(key); + if (events == null) + { + return; + } + + if (!events.Remove(theEvent)) + { + // Not an error, its possible that an old-data event is artificial (such as for statistics) and + // thus did not correspond to a new-data event raised earlier. + return; + } + + if (events.IsEmpty()) + { + propertyIndex.Remove(key); + } + } + + public override bool IsEmpty() + { + return propertyIndex.IsEmpty(); + } + + public override IEnumerator GetEnumerator() + { + return propertyIndex.Values + .SelectMany(eventBeans => eventBeans) + .GetEnumerator(); + } + + public override void Clear() + { + propertyIndex.Clear(); + } + + public override void Destroy() { + Clear(); + } + + public override int? NumberOfEvents + { + get { return null; } + } + + public override int NumKeys + { + get { return propertyIndex.Count; } + } + + public override object Index + { + get { return propertyIndex; } + } + + public override Type ProviderClass + { + get { return typeof (PropertyIndexedEventTable); } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/join/table/PropertyIndexedEventTableUnique.cs b/NEsper.Core/NEsper.Core/epl/join/table/PropertyIndexedEventTableUnique.cs new file mode 100755 index 000000000..d80235f6c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/table/PropertyIndexedEventTableUnique.cs @@ -0,0 +1,144 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.join.table +{ + public class PropertyIndexedEventTableUnique : PropertyIndexedEventTable , EventTableAsSet + { + internal readonly IDictionary _propertyIndex; + private readonly bool _canClear; + + public PropertyIndexedEventTableUnique(EventPropertyGetter[] propertyGetters, EventTableOrganization organization) + : base(propertyGetters, organization) + { + _propertyIndex = new Dictionary(); + _canClear = true; + } + + public PropertyIndexedEventTableUnique(EventPropertyGetter[] propertyGetters, EventTableOrganization organization, IDictionary propertyIndex) + : base(propertyGetters, organization) + { + _propertyIndex = propertyIndex; + _canClear = false; + } + + /// + /// Remove then add events. + /// + /// to add + /// to remove + public override void AddRemove(EventBean[] newData, EventBean[] oldData) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QIndexAddRemove(this, newData, oldData);} + if (oldData != null) { + foreach (var theEvent in oldData) { + Remove(theEvent); + } + } + if (newData != null) { + foreach (var theEvent in newData) { + Add(theEvent); + } + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AIndexAddRemove();} + } + + public override ISet Lookup(object[] keys) + { + var key = new MultiKeyUntyped(keys); + var @event = _propertyIndex.Get(key); + if (@event != null) { + return Collections.SingletonSet(@event); + } + return null; + } + + public override void Add(EventBean theEvent) + { + var key = GetMultiKey(theEvent); + var existing = _propertyIndex.Push(key, theEvent); + if (existing != null && !existing.Equals(theEvent)) { + throw HandleUniqueIndexViolation(organization.IndexName, key); + } + } + + public static EPException HandleUniqueIndexViolation(string indexName, object key) + { + var indexNameDisplay = indexName == null ? "" : " '" + indexName + "'"; + throw new EPException("Unique index violation, index" + indexNameDisplay + " is a unique index and key '" + key + "' already exists"); + } + + public override void Remove(EventBean theEvent) + { + var key = GetMultiKey(theEvent); + _propertyIndex.Remove(key); + } + + public override bool IsEmpty() + { + return _propertyIndex.IsEmpty(); + } + + public override IEnumerator GetEnumerator() + { + return _propertyIndex.Values.GetEnumerator(); + } + + public override void Clear() + { + if (_canClear) { + _propertyIndex.Clear(); + } + } + + public override void Destroy() + { + Clear(); + } + + public override int? NumberOfEvents + { + get { return _propertyIndex.Count; } + } + + public override int NumKeys + { + get { return _propertyIndex.Count; } + } + + public override object Index + { + get { return _propertyIndex; } + } + + public ISet AllValues + { + get + { + if (_propertyIndex.IsEmpty()) + { + return Collections.GetEmptySet(); + } + return new HashSet(_propertyIndex.Values); + } + } + + public override Type ProviderClass + { + get { return typeof (PropertyIndexedEventTableUnique); } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/join/table/PropertySortedEventTable.cs b/NEsper.Core/NEsper.Core/epl/join/table/PropertySortedEventTable.cs new file mode 100755 index 000000000..69e039651 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/table/PropertySortedEventTable.cs @@ -0,0 +1,255 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.@join.exec.@base; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.join.table +{ + /// + /// Index that organizes events by the event property values into a single TreeMap sortable non-nested index + /// with Object keys that store the property values. + /// + public abstract class PropertySortedEventTable : EventTable + { + protected readonly EventPropertyGetter _propertyGetter; + protected readonly EventTableOrganization _organization; + + public abstract ISet LookupRange(object keyStart, bool includeStart, object keyEnd, bool includeEnd, bool allowRangeReversal); + public abstract ICollection LookupRangeColl(object keyStart, bool includeStart, object keyEnd, bool includeEnd, bool allowRangeReversal); + public abstract ISet LookupRangeInverted(object keyStart, bool includeStart, object keyEnd, bool includeEnd); + public abstract ICollection LookupRangeInvertedColl(object keyStart, bool includeStart, object keyEnd, bool includeEnd); + public abstract ISet LookupLess(object keyStart); + public abstract ICollection LookupLessThenColl(object keyStart); + public abstract ISet LookupLessEqual(object keyStart); + public abstract ICollection LookupLessEqualColl(object keyStart); + public abstract ISet LookupGreaterEqual(object keyStart); + public abstract ICollection LookupGreaterEqualColl(object keyStart); + public abstract ISet LookupGreater(object keyStart); + public abstract ICollection LookupGreaterColl(object keyStart); + public abstract ISet LookupConstants(RangeIndexLookupValue lookupValueBase); + + public abstract void Add(EventBean @event); + public abstract void Remove(EventBean @event); + public abstract bool IsEmpty(); + public abstract void Clear(); + public abstract void Destroy(); + + public abstract IEnumerator GetEnumerator(); + + /// + /// Ctor. + /// + public PropertySortedEventTable(EventPropertyGetter propertyGetter, EventTableOrganization organization) + { + this._propertyGetter = propertyGetter; + this._organization = organization; + } + + /// + /// Determine multikey for index access. + /// + /// to get properties from for key + /// multi key + protected internal object GetIndexedValue(EventBean theEvent) + { + return _propertyGetter.Get(theEvent); + } + + public void AddRemove(EventBean[] newData, EventBean[] oldData) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QIndexAddRemove(this, newData, oldData); } + if (newData != null) + { + foreach (var theEvent in newData) + { + Add(theEvent); + } + } + if (oldData != null) + { + foreach (var theEvent in oldData) + { + Remove(theEvent); + } + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AIndexAddRemove(); } + } + + /// + /// Add an array of events. Same event instance is not added twice. Event properties should be immutable. + /// Allow null passed instead of an empty array. + /// + /// to add + /// IllegalArgumentException if the event was already existed in the index + public void Add(EventBean[] events) + { + if (events != null) + { + + if (InstrumentationHelper.ENABLED && events.Length > 0) + { + InstrumentationHelper.Get().QIndexAdd(this, events); + foreach (var theEvent in events) + { + Add(theEvent); + } + InstrumentationHelper.Get().AIndexAdd(); + return; + } + + foreach (var theEvent in events) + { + Add(theEvent); + } + } + } + + /// + /// Remove events. + /// + /// to be removed, can be null instead of an empty array. + /// IllegalArgumentException when the event could not be removed as its not in the index + public void Remove(EventBean[] events) + { + if (events != null) + { + + if (InstrumentationHelper.ENABLED && events.Length > 0) + { + InstrumentationHelper.Get().QIndexRemove(this, events); + foreach (var theEvent in events) + { + Remove(theEvent); + } + InstrumentationHelper.Get().AIndexRemove(); + return; + } + + foreach (var theEvent in events) + { + Remove(theEvent); + } + } + } + + public virtual int? NumberOfEvents + { + get { return null; } + } + + internal static ISet Normalize(IDictionary> submap) + { + if (submap.Count == 0) + { + return null; + } + if (submap.Count == 1) + { + return submap.Get(submap.Keys.First()); + } + ISet result = new LinkedHashSet(); + foreach (var entry in submap) + { + result.AddAll(entry.Value); + } + return result; + } + + internal static ICollection NormalizeCollection(IDictionary> submap) + { + if (submap.Count == 0) + { + return null; + } + if (submap.Count == 1) + { + return submap.Get(submap.Keys.First()); + } + var result = new ArrayDeque(); + foreach (var entry in submap) + { + result.AddAll(entry.Value); + } + return result; + } + + internal static ICollection NormalizeCollection(IDictionary> submapOne, IDictionary> submapTwo) + { + if (submapOne.Count == 0) + { + return NormalizeCollection(submapTwo); + } + if (submapTwo.Count == 0) + { + return NormalizeCollection(submapOne); + } + var result = new ArrayDeque(); + foreach (var entry in submapOne) + { + result.AddAll(entry.Value); + } + foreach (var entry in submapTwo) + { + result.AddAll(entry.Value); + } + return result; + } + + internal static ISet Normalize(IDictionary> submapOne, IDictionary> submapTwo) + { + if (submapOne.Count == 0) + { + return Normalize(submapTwo); + } + if (submapTwo.Count == 0) + { + return Normalize(submapOne); + } + ISet result = new LinkedHashSet(); + foreach (var entry in submapOne) + { + result.AddAll(entry.Value); + } + foreach (var entry in submapTwo) + { + result.AddAll(entry.Value); + } + return result; + } + + public string ToQueryPlan() + { + return this.GetType().Name + + " streamNum=" + _organization.StreamNum + + " _propertyGetter=" + _propertyGetter; + } + + public EventTableOrganization Organization + { + get { return _organization; } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public abstract Type ProviderClass { get; } + public abstract int NumKeys { get; } + public abstract object Index { get; } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/join/table/PropertySortedEventTableCoerced.cs b/NEsper.Core/NEsper.Core/epl/join/table/PropertySortedEventTableCoerced.cs new file mode 100755 index 000000000..9a980d1ec --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/table/PropertySortedEventTableCoerced.cs @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.join.table +{ + public class PropertySortedEventTableCoerced : PropertySortedEventTableImpl + { + private readonly Type _coercionType; + + public PropertySortedEventTableCoerced(EventPropertyGetter propertyGetter, EventTableOrganization organization, Type coercionType) + : base(propertyGetter, organization) + { + _coercionType = coercionType; + } + + protected override Object Coerce(Object value) + { + if (value != null && value.GetType() != _coercionType) + { + if (value.IsNumber()) + { + return CoercerFactory.CoerceBoxed(value, _coercionType); + } + } + return value; + } + + public override String ToString() + { + return "PropertySortedEventTableCoerced" + + " streamNum=" + Organization.StreamNum + + " propertyGetter=" + _propertyGetter + + " coercionType=" + _coercionType; + } + + public override Type ProviderClass + { + get { return typeof (PropertySortedEventTableCoerced); } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/table/PropertySortedEventTableCoercedFactory.cs b/NEsper.Core/NEsper.Core/epl/join/table/PropertySortedEventTableCoercedFactory.cs new file mode 100755 index 000000000..ed0c07c99 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/table/PropertySortedEventTableCoercedFactory.cs @@ -0,0 +1,60 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.epl.join.table +{ + public class PropertySortedEventTableCoercedFactory : PropertySortedEventTableFactory + { + protected readonly Type coercionType; + + /// Ctor. + /// the stream number that is indexed + /// types of events indexed + /// property names to use for indexing + /// property types + public PropertySortedEventTableCoercedFactory(int streamNum, EventType eventType, String propertyName, Type coercionType) + : base(streamNum, eventType, propertyName) + { + this.coercionType = coercionType; + } + + public override EventTable[] MakeEventTables(EventTableFactoryTableIdent tableIdent) + { + var organization = Organization; + return new EventTable[] + { + new PropertySortedEventTableCoerced(PropertyGetter, organization, coercionType) + }; + } + + public override String ToString() + { + return "PropertySortedEventTableCoerced" + + " streamNum=" + StreamNum + + " propertyName=" + PropertyName + + " coercionType=" + coercionType; + } + + protected override EventTableOrganization Organization + { + get + { + return new EventTableOrganization(null, false, true, StreamNum, new String[] { PropertyName }, EventTableOrganizationType.BTREE); + } + } + + public Type ProviderClass + { + get { return typeof (PropertySortedEventTableCoerced); } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/table/PropertySortedEventTableEnumerator.cs b/NEsper.Core/NEsper.Core/epl/join/table/PropertySortedEventTableEnumerator.cs new file mode 100755 index 000000000..23c25c110 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/table/PropertySortedEventTableEnumerator.cs @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; + +namespace com.espertech.esper.epl.join.table +{ + /// + /// Enumerator for use by . + /// + public sealed class PropertySortedEventTableEnumerator + { + public static IEnumerator Create(IDictionary> window) + { + return window.SelectMany(entry => entry.Value).GetEnumerator(); + } + + public static IEnumerable CreateEnumerable(IDictionary> window) + { + return window.SelectMany(entry => entry.Value); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/table/PropertySortedEventTableFactory.cs b/NEsper.Core/NEsper.Core/epl/join/table/PropertySortedEventTableFactory.cs new file mode 100755 index 000000000..bac996798 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/table/PropertySortedEventTableFactory.cs @@ -0,0 +1,67 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.join.table +{ + /// + /// MapIndex that organizes events by the event property values into a single TreeMap sortable + /// non-nested index with Object keys that store the property values. + /// + public class PropertySortedEventTableFactory : EventTableFactory + { + /// + /// Ctor. + /// + /// the stream number that is indexed + /// types of events indexed + /// Name of the property. + public PropertySortedEventTableFactory(int streamNum, EventType eventType, String propertyName) + { + StreamNum = streamNum; + PropertyName = propertyName; + PropertyGetter = EventBeanUtility.GetAssertPropertyGetter(eventType, propertyName); + } + + public virtual EventTable[] MakeEventTables(EventTableFactoryTableIdent tableIdent) + { + EventTableOrganization organization = Organization; + return new EventTable[] { new PropertySortedEventTableImpl(PropertyGetter, organization) }; + } + + public virtual Type EventTableType + { + get { return typeof (PropertySortedEventTable); } + } + + public virtual String ToQueryPlan() + { + return GetType().FullName + + " streamNum=" + StreamNum + + " propertyName=" + PropertyName; + } + + public int StreamNum { get; private set; } + + public string PropertyName { get; private set; } + + public EventPropertyGetter PropertyGetter { get; private set; } + + protected virtual EventTableOrganization Organization + { + get + { + return new EventTableOrganization(null, false, false, StreamNum, new string[] { PropertyName }, EventTableOrganizationType.BTREE); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/table/PropertySortedEventTableImpl.cs b/NEsper.Core/NEsper.Core/epl/join/table/PropertySortedEventTableImpl.cs new file mode 100755 index 000000000..49493d7a2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/table/PropertySortedEventTableImpl.cs @@ -0,0 +1,390 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.@join.exec.@base; +using com.espertech.esper.epl.join.plan; +using com.espertech.esper.filter; + +namespace com.espertech.esper.epl.join.table +{ + /// + /// Index that organizes events by the event property values into a single TreeMap sortable non-nested index + /// with Object keys that store the property values. + /// + public class PropertySortedEventTableImpl : PropertySortedEventTable + { + private static readonly ISet EmptySet = Collections.GetEmptySet(); + private static readonly IList EmptyList = Collections.GetEmptyList(); + + /// + /// Index table. + /// + private readonly OrderedDictionary> _propertyIndex; + private readonly ISet _nullKeyedValues; + + // override in a subclass + protected virtual object Coerce(object value) + { + return value; + } + + /// + /// Ctor. + /// + public PropertySortedEventTableImpl(EventPropertyGetter propertyGetter, EventTableOrganization organization) + : base(propertyGetter, organization) + { + _propertyIndex = new OrderedDictionary>(); + _nullKeyedValues = new LinkedHashSet(); + } + + /// + /// Returns the set of events that have the same property value as the given event. + /// + /// to compare against + /// to compare against + /// indicate whether "a between 60 and 50" should return no results (equivalent to a>= X and a <=Y) or should return results (equivalent to 'between' and 'in' + /// set of events with property value, or null if none found (never returns zero-sized set) + public override ISet LookupRange(object keyStart, bool includeStart, object keyEnd, bool includeEnd, bool allowRangeReversal) + { + if (keyStart == null || keyEnd == null) + { + return EmptySet; + } + keyStart = Coerce(keyStart); + keyEnd = Coerce(keyEnd); + IDictionary> submap; + try + { + submap = _propertyIndex.Between(keyStart, includeStart, keyEnd, includeEnd); + } + catch (ArgumentException ex) + { + if (allowRangeReversal) + { + submap = _propertyIndex.Between(keyEnd, includeStart, keyStart, includeEnd); + } + else + { + return EmptySet; + } + } + return Normalize(submap); + } + + public override ICollection LookupRangeColl(object keyStart, bool includeStart, object keyEnd, bool includeEnd, bool allowRangeReversal) + { + if (keyStart == null || keyEnd == null) + { + return EmptyList; + } + keyStart = Coerce(keyStart); + keyEnd = Coerce(keyEnd); + IDictionary> submap; + try + { + submap = _propertyIndex.Between(keyStart, includeStart, keyEnd, includeEnd); + } + catch (ArgumentException ex) + { + if (allowRangeReversal) + { + submap = _propertyIndex.Between(keyEnd, includeStart, keyStart, includeEnd); + } + else + { + return EmptyList; + } + } + return NormalizeCollection(submap); + } + + public override ISet LookupRangeInverted(object keyStart, bool includeStart, object keyEnd, bool includeEnd) + { + if (keyStart == null || keyEnd == null) + { + return EmptySet; + } + keyStart = Coerce(keyStart); + keyEnd = Coerce(keyEnd); + var submapOne = _propertyIndex.Head(keyStart, !includeStart); + var submapTwo = _propertyIndex.Tail(keyEnd, !includeEnd); + return Normalize(submapOne, submapTwo); + } + + public override ICollection LookupRangeInvertedColl(object keyStart, bool includeStart, object keyEnd, bool includeEnd) + { + if (keyStart == null || keyEnd == null) + { + return EmptySet; + } + keyStart = Coerce(keyStart); + keyEnd = Coerce(keyEnd); + var submapOne = _propertyIndex.Head(keyStart, !includeStart); + var submapTwo = _propertyIndex.Tail(keyEnd, !includeEnd); + return NormalizeCollection(submapOne, submapTwo); + } + + public override ISet LookupLess(object keyStart) + { + if (keyStart == null) + { + return EmptySet; + } + keyStart = Coerce(keyStart); + return Normalize(_propertyIndex.Head(keyStart)); + } + + public override ICollection LookupLessThenColl(object keyStart) + { + if (keyStart == null) + { + return EmptyList; + } + keyStart = Coerce(keyStart); + return NormalizeCollection(_propertyIndex.Head(keyStart)); + } + + public override ISet LookupLessEqual(object keyStart) + { + if (keyStart == null) + { + return EmptySet; + } + keyStart = Coerce(keyStart); + return Normalize(_propertyIndex.Head(keyStart, true)); + } + + public override ICollection LookupLessEqualColl(object keyStart) + { + if (keyStart == null) + { + return EmptyList; + } + keyStart = Coerce(keyStart); + return NormalizeCollection(_propertyIndex.Head(keyStart, true)); + } + + public override ISet LookupGreaterEqual(object keyStart) + { + if (keyStart == null) + { + return EmptySet; + } + keyStart = Coerce(keyStart); + return Normalize(_propertyIndex.Tail(keyStart)); + } + + public override ICollection LookupGreaterEqualColl(object keyStart) + { + if (keyStart == null) + { + return EmptyList; + } + keyStart = Coerce(keyStart); + return NormalizeCollection(_propertyIndex.Tail(keyStart)); + } + + public override ISet LookupGreater(object keyStart) + { + if (keyStart == null) + { + return EmptySet; + } + keyStart = Coerce(keyStart); + return Normalize(_propertyIndex.Tail(keyStart, false)); + } + + public override ICollection LookupGreaterColl(object keyStart) + { + if (keyStart == null) + { + return EmptyList; + } + keyStart = Coerce(keyStart); + return NormalizeCollection(_propertyIndex.Tail(keyStart, false)); + } + + public override int? NumberOfEvents + { + get { return null; } + } + + public override int NumKeys + { + get { return _propertyIndex.Count; } + } + + public override object Index + { + get { return _propertyIndex; } + } + + public override void Add(EventBean theEvent) + { + var key = GetIndexedValue(theEvent); + + key = Coerce(key); + + if (key == null) + { + _nullKeyedValues.Add(theEvent); + return; + } + +#if true + var events = _propertyIndex.TryInsert( + key, () => new LinkedHashSet()); +#else + var events = _propertyIndex.Get(key); + if (events == null) + { + events = new LinkedHashSet(); + _propertyIndex.Put(key, events); + } +#endif + + events.Add(theEvent); + } + + public override void Remove(EventBean theEvent) + { + var key = GetIndexedValue(theEvent); + + if (key == null) + { + _nullKeyedValues.Remove(theEvent); + return; + } + + key = Coerce(key); + + var events = _propertyIndex.Get(key); + if (events == null) + { + return; + } + + if (!events.Remove(theEvent)) + { + // Not an error, its possible that an old-data event is artificial (such as for statistics) and + // thus did not correspond to a new-data event raised earlier. + return; + } + + if (events.IsEmpty()) + { + _propertyIndex.Remove(key); + } + } + + public override bool IsEmpty() + { + return _propertyIndex.IsEmpty(); + } + + public override IEnumerator GetEnumerator() + { + if (_nullKeyedValues.IsEmpty()) + { + return _propertyIndex.Values + .SelectMany(eventBeans => eventBeans) + .GetEnumerator(); + } + + return Enumerable + .Union(_propertyIndex.Values.SelectMany(eventBeans => eventBeans), _nullKeyedValues) + .GetEnumerator(); + } + + public override void Clear() + { + _propertyIndex.Clear(); + } + + public override void Destroy() + { + Clear(); + } + + public override ISet LookupConstants(RangeIndexLookupValue lookupValueBase) + { + if (lookupValueBase is RangeIndexLookupValueEquals) + { + var equals = (RangeIndexLookupValueEquals)lookupValueBase; + return _propertyIndex.Get(equals.Value); + } + + var lookupValue = (RangeIndexLookupValueRange)lookupValueBase; + switch (lookupValue.Operator) + { + case QueryGraphRangeEnum.RANGE_CLOSED: + { + var range = (Range)lookupValue.Value; + return LookupRange(range.LowEndpoint, true, range.HighEndpoint, true, lookupValue.IsAllowRangeReverse); + } + case QueryGraphRangeEnum.RANGE_HALF_OPEN: + { + var range = (Range)lookupValue.Value; + return LookupRange(range.LowEndpoint, true, range.HighEndpoint, false, lookupValue.IsAllowRangeReverse); + } + case QueryGraphRangeEnum.RANGE_HALF_CLOSED: + { + var range = (Range)lookupValue.Value; + return LookupRange(range.LowEndpoint, false, range.HighEndpoint, true, lookupValue.IsAllowRangeReverse); + } + case QueryGraphRangeEnum.RANGE_OPEN: + { + var range = (Range)lookupValue.Value; + return LookupRange(range.LowEndpoint, false, range.HighEndpoint, false, lookupValue.IsAllowRangeReverse); + } + case QueryGraphRangeEnum.NOT_RANGE_CLOSED: + { + var range = (Range)lookupValue.Value; + return LookupRangeInverted(range.LowEndpoint, true, range.HighEndpoint, true); + } + case QueryGraphRangeEnum.NOT_RANGE_HALF_OPEN: + { + var range = (Range)lookupValue.Value; + return LookupRangeInverted(range.LowEndpoint, true, range.HighEndpoint, false); + } + case QueryGraphRangeEnum.NOT_RANGE_HALF_CLOSED: + { + var range = (Range)lookupValue.Value; + return LookupRangeInverted(range.LowEndpoint, false, range.HighEndpoint, true); + } + case QueryGraphRangeEnum.NOT_RANGE_OPEN: + { + var range = (Range)lookupValue.Value; + return LookupRangeInverted(range.LowEndpoint, false, range.HighEndpoint, false); + } + case QueryGraphRangeEnum.GREATER: + return LookupGreater(lookupValue.Value); + case QueryGraphRangeEnum.GREATER_OR_EQUAL: + return LookupGreaterEqual(lookupValue.Value); + case QueryGraphRangeEnum.LESS: + return LookupLess(lookupValue.Value); + case QueryGraphRangeEnum.LESS_OR_EQUAL: + return LookupLessEqual(lookupValue.Value); + default: + throw new ArgumentException("Unrecognized operator '" + lookupValue.Operator + "'"); + } + } + + public override Type ProviderClass + { + get { return typeof(PropertySortedEventTable); } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/join/table/SingleReferenceEventTable.cs b/NEsper.Core/NEsper.Core/epl/join/table/SingleReferenceEventTable.cs new file mode 100755 index 000000000..bfcda0a7f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/table/SingleReferenceEventTable.cs @@ -0,0 +1,121 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.events; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.epl.join.table +{ + public class SingleReferenceEventTable : EventTable, EventTableAsSet + { + private readonly EventTableOrganization _organization; + private readonly Atomic _eventReference; + + public SingleReferenceEventTable(EventTableOrganization organization, Atomic eventReference) { + this._organization = organization; + this._eventReference = eventReference; + } + + public void AddRemove(EventBean[] newData, EventBean[] oldData) { + throw new UnsupportedOperationException(); + } + + public void Add(EventBean[] events) { + throw new UnsupportedOperationException(); + } + + public void Add(EventBean @event) { + throw new UnsupportedOperationException(); + } + + public void Remove(EventBean[] events) { + throw new UnsupportedOperationException(); + } + + public void Remove(EventBean @event) { + throw new UnsupportedOperationException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public IEnumerator GetEnumerator() + { + var eventBean = _eventReference.Get(); + if (eventBean != null) + yield return eventBean; + } + + public virtual bool IsEmpty() + { + return _eventReference.Get() == null; + } + + public virtual void Clear() + { + throw new UnsupportedOperationException(); + } + + public virtual void Destroy() + { + } + + public virtual string ToQueryPlan() + { + return "single-reference"; + } + + public virtual int? NumberOfEvents + { + get { return _eventReference.Get() == null ? 0 : 1; } + } + + public virtual int NumKeys + { + get { return 0; } + } + + public virtual object Index + { + get { return null; } + } + + public virtual EventTableOrganization Organization + { + get { return _organization; } + } + + public virtual ISet AllValues + { + get + { + EventBean @event = _eventReference.Get(); + if (@event != null) + { + return Collections.SingletonSet(@event); + } + return Collections.GetEmptySet(); + } + } + + public virtual Type ProviderClass + { + get { return typeof (SingleReferenceEventTable); } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/table/UnindexedEventTable.cs b/NEsper.Core/NEsper.Core/epl/join/table/UnindexedEventTable.cs new file mode 100755 index 000000000..6f886d67b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/table/UnindexedEventTable.cs @@ -0,0 +1,76 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections; +using System.Collections.Generic; + +using com.espertech.esper.client; + +namespace com.espertech.esper.epl.join.table +{ + /// + /// Simple table of events without an index. + /// + public abstract class UnindexedEventTable : EventTable + { + private readonly int _streamNum; + + public abstract ISet EventSet { get; } + + public abstract IEnumerator GetEnumerator(); + public abstract void AddRemove(EventBean[] newData, EventBean[] oldData); + public abstract void Add(EventBean[] events); + public abstract void Add(EventBean @event); + public abstract void Remove(EventBean[] events); + public abstract void Remove(EventBean @event); + public abstract bool IsEmpty(); + public abstract void Clear(); + public abstract void Destroy(); + public abstract Type ProviderClass { get; } + public abstract int? NumberOfEvents { get; } + public abstract object Index { get; } + + /// + /// Ctor. + /// + /// is the indexed stream's number + protected UnindexedEventTable(int streamNum) + { + _streamNum = streamNum; + } + + public override string ToString() + { + return ToQueryPlan(); + } + + public string ToQueryPlan() + { + return GetType().Name + " streamNum=" + _streamNum; + } + + public virtual int NumKeys + { + get { return 0; } + } + + public virtual EventTableOrganization Organization + { + get + { + return new EventTableOrganization(null, false, false, _streamNum, null, EventTableOrganizationType.UNORGANIZED); + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/join/table/UnindexedEventTableFactory.cs b/NEsper.Core/NEsper.Core/epl/join/table/UnindexedEventTableFactory.cs new file mode 100755 index 000000000..710bed036 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/table/UnindexedEventTableFactory.cs @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.join.table +{ + /// + /// Factory for simple table of events without an index. + /// + public class UnindexedEventTableFactory : EventTableFactory + { + private readonly int _streamNum; + + public UnindexedEventTableFactory(int streamNum) + { + _streamNum = streamNum; + } + + public EventTable[] MakeEventTables(EventTableFactoryTableIdent tableIdent) + { + return new EventTable[] { new UnindexedEventTableImpl(_streamNum) }; + } + + public Type EventTableType + { + get { return typeof(UnindexedEventTable); } + } + + public String ToQueryPlan() + { + return GetType().Name + " StreamNum=" + _streamNum; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/table/UnindexedEventTableImpl.cs b/NEsper.Core/NEsper.Core/epl/join/table/UnindexedEventTableImpl.cs new file mode 100755 index 000000000..40ca5eabc --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/table/UnindexedEventTableImpl.cs @@ -0,0 +1,134 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.join.table +{ + /// + /// Simple table of events without an index. + /// + public class UnindexedEventTableImpl : UnindexedEventTable + { + private readonly ISet _eventSet = new LinkedHashSet(); + + /// + /// Ctor. + /// + /// is the indexed stream's number + public UnindexedEventTableImpl(int streamNum) + : base(streamNum) + { + } + + public override void Clear() + { + _eventSet.Clear(); + } + + public override void Destroy() + { + Clear(); + } + + public override void AddRemove(EventBean[] newData, EventBean[] oldData) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QIndexAddRemove(this, newData, oldData);} + if (newData != null) { + _eventSet.AddAll(newData); + } + if (oldData != null) { + foreach (EventBean removeEvent in oldData) { + _eventSet.Remove(removeEvent); + } + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AIndexAddRemove();} + } + + public override void Add(EventBean[] events) + { + if (events != null) { + + if (InstrumentationHelper.ENABLED && events.Length > 0) { + InstrumentationHelper.Get().QIndexAdd(this, events); + _eventSet.AddAll(events); + InstrumentationHelper.Get().AIndexAdd(); + return; + } + + _eventSet.AddAll(events); + } + } + + public override void Remove(EventBean[] events) + { + if (events != null) { + + if (InstrumentationHelper.ENABLED && events.Length > 0) { + InstrumentationHelper.Get().QIndexRemove(this, events); + foreach (EventBean removeEvent in events) { + _eventSet.Remove(removeEvent); + } + InstrumentationHelper.Get().AIndexRemove(); + return; + } + + foreach (EventBean removeEvent in events) { + _eventSet.Remove(removeEvent); + } + } + } + + public override void Add(EventBean @event) { + _eventSet.Add(@event); + } + + public override void Remove(EventBean @event) { + _eventSet.Remove(@event); + } + + public override bool IsEmpty() + { + return _eventSet.IsEmpty(); + } + + /// + /// Returns events in table. + /// + /// all events + public override ISet EventSet + { + get { return _eventSet; } + } + + public override IEnumerator GetEnumerator() + { + return _eventSet.GetEnumerator(); + } + + public override int? NumberOfEvents + { + get { return _eventSet.Count; } + } + + public override object Index + { + get { return _eventSet; } + } + + public override Type ProviderClass + { + get { return typeof (UnindexedEventTable); } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/join/table/UnindexedEventTableList.cs b/NEsper.Core/NEsper.Core/epl/join/table/UnindexedEventTableList.cs new file mode 100755 index 000000000..713838c4e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/table/UnindexedEventTableList.cs @@ -0,0 +1,167 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.join.table +{ + /// + /// Simple table of events without an index, based on a List implementation rather then + /// a set since we know there cannot be duplicates (such as a poll returning individual rows). + /// + public class UnindexedEventTableList : EventTable + { + private readonly IList _eventSet; + private readonly int _streamNum; + + /// + /// Ctor. + /// + /// is a list initializing the table + /// The stream num. + public UnindexedEventTableList(IList eventSet, int streamNum) + { + _eventSet = eventSet; + _streamNum = streamNum; + } + + public void AddRemove(EventBean[] newData, EventBean[] oldData) + { + Instrument.With( + i => i.QIndexAddRemove(this, newData, oldData), + i => i.AIndexAddRemove(), + () => + { + if (newData != null) + { + _eventSet.AddAll(newData); + } + if (oldData != null) + { + foreach (EventBean removeEvent in oldData) + { + _eventSet.Remove(removeEvent); + } + } + }); + } + + public void Add(EventBean[] events) + { + if (events != null && events.Length > 0) + { + Instrument.With( + i => i.QIndexAdd(this, events), + i => i.AIndexAdd(), + () => _eventSet.AddAll(events)); + } + + } + + public void Remove(EventBean[] events) + { + if (events != null && events.Length > 0) + { + Instrument.With( + i => i.QIndexRemove(this, events), + i => i.AIndexRemove(), + () => + { + foreach (EventBean removeEvent in events) + { + _eventSet.Remove(removeEvent); + } + }); + } + } + + public void Add(EventBean @event) + { + _eventSet.Add(@event); + } + + public void Remove(EventBean @event) + { + _eventSet.Remove(@event); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public IEnumerator GetEnumerator() + { + if (_eventSet == null) + { + return CollectionUtil.NULL_EVENT_ITERATOR; + } + return _eventSet.GetEnumerator(); + } + + public bool IsEmpty() + { + return _eventSet.IsEmpty(); + } + + public override String ToString() + { + return ToQueryPlan(); + } + + public String ToQueryPlan() { + return GetType().FullName; + } + + public void Clear() + { + _eventSet.Clear(); + } + + public void Destroy() + { + Clear(); + } + + public int? NumberOfEvents + { + get { return _eventSet.Count; } + } + + public int NumKeys + { + get { return 0; } + } + + public object Index + { + get { return _eventSet; } + } + + public EventTableOrganization Organization + { + get + { + return new EventTableOrganization( + null, false, false, _streamNum, null, EventTableOrganizationType.UNORGANIZED); + } + } + + public Type ProviderClass + { + get { return typeof (UnindexedEventTableList); } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/util/Eligibility.cs b/NEsper.Core/NEsper.Core/epl/join/util/Eligibility.cs new file mode 100755 index 000000000..2643e3e3e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/util/Eligibility.cs @@ -0,0 +1,22 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.join.util +{ + public enum Eligibility { + REQUIRE_NONE, + REQUIRE_ONE, + INELIGIBLE + } + + public static class EligibilityExtensions { + public static bool IsEligible(this Eligibility e) { + return e != Eligibility.INELIGIBLE; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/util/EligibilityDesc.cs b/NEsper.Core/NEsper.Core/epl/join/util/EligibilityDesc.cs new file mode 100755 index 000000000..1e15238d0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/util/EligibilityDesc.cs @@ -0,0 +1,24 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.join.util +{ + public class EligibilityDesc + { + public EligibilityDesc(Eligibility eligibility, + int? streamNum) + { + Eligibility = eligibility; + StreamNum = streamNum; + } + + public Eligibility Eligibility { get; private set; } + + public int? StreamNum { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/join/util/EligibilityUtil.cs b/NEsper.Core/NEsper.Core/epl/join/util/EligibilityUtil.cs new file mode 100755 index 000000000..67ac0962a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/util/EligibilityUtil.cs @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Linq; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.visitor; + +namespace com.espertech.esper.epl.join.util +{ + public class EligibilityUtil + { + public static EligibilityDesc VerifyInputStream(ExprNode expression, int indexedStream) + { + var visitor = new ExprNodeIdentifierCollectVisitor(); + expression.Accept(visitor); + + var inputStreamsRequired = visitor.StreamsRequired; + if (inputStreamsRequired.Count > 1) { // multi-stream dependency no optimization (i.e. a+b=c) + return new EligibilityDesc(Eligibility.INELIGIBLE, null); + } + if (inputStreamsRequired.Count == 1 && inputStreamsRequired.First() == indexedStream) { // self-compared no optimization + return new EligibilityDesc(Eligibility.INELIGIBLE, null); + } + if (inputStreamsRequired.IsEmpty()) { + return new EligibilityDesc(Eligibility.REQUIRE_NONE, null); + } + return new EligibilityDesc(Eligibility.REQUIRE_ONE, inputStreamsRequired.First()); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/util/IndexNameAndDescPair.cs b/NEsper.Core/NEsper.Core/epl/join/util/IndexNameAndDescPair.cs new file mode 100755 index 000000000..1fb2efd9e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/util/IndexNameAndDescPair.cs @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.epl.join.util +{ + [Serializable] + public class IndexNameAndDescPair + { + public IndexNameAndDescPair(string tableName, string indexDesc) + { + IndexName = tableName; + IndexDesc = indexDesc; + } + + public string IndexName { get; private set; } + + public string IndexDesc { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/join/util/QueryPlanIndexDescBase.cs b/NEsper.Core/NEsper.Core/epl/join/util/QueryPlanIndexDescBase.cs new file mode 100755 index 000000000..6d4d1574e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/util/QueryPlanIndexDescBase.cs @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.join.util +{ + public abstract class QueryPlanIndexDescBase + { + protected QueryPlanIndexDescBase(IndexNameAndDescPair[] tables) + { + Tables = tables; + } + + public IndexNameAndDescPair[] Tables { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/join/util/QueryPlanIndexDescFAF.cs b/NEsper.Core/NEsper.Core/epl/join/util/QueryPlanIndexDescFAF.cs new file mode 100755 index 000000000..fd333097b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/util/QueryPlanIndexDescFAF.cs @@ -0,0 +1,18 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.join.util +{ + public class QueryPlanIndexDescFAF : QueryPlanIndexDescBase + { + public QueryPlanIndexDescFAF(IndexNameAndDescPair[] tables) + : base(tables) + { + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/join/util/QueryPlanIndexDescHistorical.cs b/NEsper.Core/NEsper.Core/epl/join/util/QueryPlanIndexDescHistorical.cs new file mode 100755 index 000000000..2caf842e1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/util/QueryPlanIndexDescHistorical.cs @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.join.util +{ + public class QueryPlanIndexDescHistorical + { + public QueryPlanIndexDescHistorical(String strategyName, String indexName) + { + StrategyName = strategyName; + IndexName = indexName; + } + + public string StrategyName { get; private set; } + + public string IndexName { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/join/util/QueryPlanIndexDescOnExpr.cs b/NEsper.Core/NEsper.Core/epl/join/util/QueryPlanIndexDescOnExpr.cs new file mode 100755 index 000000000..d7ae8b121 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/util/QueryPlanIndexDescOnExpr.cs @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.join.util +{ + public class QueryPlanIndexDescOnExpr : QueryPlanIndexDescBase + { + public QueryPlanIndexDescOnExpr(IndexNameAndDescPair[] tables, String strategyName, String tableLookupStrategy) + : base(tables) + { + StrategyName = strategyName; + TableLookupStrategy = tableLookupStrategy; + } + + public string StrategyName { get; private set; } + + public string TableLookupStrategy { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/join/util/QueryPlanIndexDescSubquery.cs b/NEsper.Core/NEsper.Core/epl/join/util/QueryPlanIndexDescSubquery.cs new file mode 100755 index 000000000..bda3a40c5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/util/QueryPlanIndexDescSubquery.cs @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.join.util +{ + public class QueryPlanIndexDescSubquery : QueryPlanIndexDescBase + { + public QueryPlanIndexDescSubquery(IndexNameAndDescPair[] tables, int subqueryNum, String tableLookupStrategy) + : base(tables) + { + SubqueryNum = subqueryNum; + TableLookupStrategy = tableLookupStrategy; + } + + public int SubqueryNum { get; private set; } + public string TableLookupStrategy { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/join/util/QueryPlanIndexHook.cs b/NEsper.Core/NEsper.Core/epl/join/util/QueryPlanIndexHook.cs new file mode 100755 index 000000000..43859e34e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/util/QueryPlanIndexHook.cs @@ -0,0 +1,21 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.join.plan; + +namespace com.espertech.esper.epl.join.util +{ + public interface QueryPlanIndexHook + { + void Subquery(QueryPlanIndexDescSubquery subquery); + void InfraOnExpr(QueryPlanIndexDescOnExpr onexpr); + void FireAndForget(QueryPlanIndexDescFAF queryPlanIndexDescFAF); + void Join(QueryPlan join); + void Historical(QueryPlanIndexDescHistorical historical); + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/join/util/QueryPlanIndexHookUtil.cs b/NEsper.Core/NEsper.Core/epl/join/util/QueryPlanIndexHookUtil.cs new file mode 100755 index 000000000..4b230b5a9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/util/QueryPlanIndexHookUtil.cs @@ -0,0 +1,36 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.client.annotation; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.join.util +{ + public class QueryPlanIndexHookUtil + { + public static QueryPlanIndexHook GetHook(Attribute[] annotations, EngineImportService engineImportService) + { + try + { + return + (QueryPlanIndexHook) + TypeHelper.GetAnnotationHook( + annotations, HookType.INTERNAL_QUERY_PLAN, typeof (QueryPlanIndexHook), engineImportService); + } + catch (ExprValidationException e) + { + throw new EPException("Failed to obtain hook for " + HookType.INTERNAL_QUERY_PLAN); + } + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/join/util/RangeFilterAnalyzer.cs b/NEsper.Core/NEsper.Core/epl/join/util/RangeFilterAnalyzer.cs new file mode 100755 index 000000000..0bb5bd554 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/join/util/RangeFilterAnalyzer.cs @@ -0,0 +1,67 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.join.plan; + +namespace com.espertech.esper.epl.join.util +{ + public class RangeFilterAnalyzer + { + public static void Apply(ExprNode target, + ExprNode start, + ExprNode end, + bool includeStart, + bool includeEnd, + bool isNot, + QueryGraph queryGraph) + { + if (((target is ExprIdentNode)) && + ((start is ExprIdentNode)) && + ((end is ExprIdentNode))) + { + var identNodeValue = (ExprIdentNode) target; + var identNodeStart = (ExprIdentNode) start; + var identNodeEnd = (ExprIdentNode) end; + + int keyStreamStart = identNodeStart.StreamId; + int keyStreamEnd = identNodeEnd.StreamId; + int valueStream = identNodeValue.StreamId; + queryGraph.AddRangeStrict( + keyStreamStart, identNodeStart, keyStreamEnd, + identNodeEnd, valueStream, + identNodeValue, + includeStart, includeEnd, isNot); + + return; + } + + // handle constant-compare or transformation case + if (target is ExprIdentNode) + { + var identNode = (ExprIdentNode) target; + var indexedStream = identNode.StreamId; + + var eligibilityStart = EligibilityUtil.VerifyInputStream(start, indexedStream); + if (!eligibilityStart.Eligibility.IsEligible()) + { + return; + } + + var eligibilityEnd = EligibilityUtil.VerifyInputStream(end, indexedStream); + if (!eligibilityEnd.Eligibility.IsEligible()) + { + return; + } + + queryGraph.AddRangeExpr(indexedStream, identNode, start, eligibilityStart.StreamNum, end, eligibilityEnd.StreamNum); + } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/lookup/EventTableCreateIndexDesc.cs b/NEsper.Core/NEsper.Core/epl/lookup/EventTableCreateIndexDesc.cs new file mode 100755 index 000000000..d41e5cd85 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/EventTableCreateIndexDesc.cs @@ -0,0 +1,36 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +namespace com.espertech.esper.epl.lookup +{ + public class EventTableCreateIndexDesc + { + public EventTableCreateIndexDesc(IList hashProps, IList btreeProps, bool unique) + { + HashProps = hashProps; + BtreeProps = btreeProps; + IsUnique = unique; + } + + public IList HashProps { get; private set; } + + public IList BtreeProps { get; private set; } + + public bool IsUnique { get; private set; } + + public static EventTableCreateIndexDesc FromMultiKey(IndexMultiKey multiKey) + { + return new EventTableCreateIndexDesc( + multiKey.HashIndexedProps, + multiKey.RangeIndexedProps, + multiKey.IsUnique); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/lookup/EventTableIndexEntryBase.cs b/NEsper.Core/NEsper.Core/epl/lookup/EventTableIndexEntryBase.cs new file mode 100755 index 000000000..9912bf7c3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/EventTableIndexEntryBase.cs @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.lookup +{ + public abstract class EventTableIndexEntryBase + { + protected EventTableIndexEntryBase(string optionalIndexName) + { + OptionalIndexName = optionalIndexName; + } + + public string OptionalIndexName { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/lookup/EventTableIndexMetadata.cs b/NEsper.Core/NEsper.Core/epl/lookup/EventTableIndexMetadata.cs new file mode 100755 index 000000000..99f6a5ea2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/EventTableIndexMetadata.cs @@ -0,0 +1,142 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.@join.plan; + +namespace com.espertech.esper.epl.lookup +{ + public class EventTableIndexMetadata + { + private readonly IDictionary indexes = new Dictionary(); + + public EventTableIndexMetadata() + { + } + + public void AddIndex(bool isPrimary, IndexMultiKey indexMultiKey, string explicitIndexName, string statementName, bool failIfExists, QueryPlanIndexItem optionalQueryPlanIndexItem) + { + if (GetIndexByName(explicitIndexName) != null) { + throw new ExprValidationException("An index by name '" + explicitIndexName + "' already exists"); + } + if (indexes.ContainsKey(indexMultiKey)) { + if (failIfExists) { + throw new ExprValidationException("An index for the same columns already exists"); + } + return; + } + EventTableIndexMetadataEntry entry = new EventTableIndexMetadataEntry(explicitIndexName, isPrimary, optionalQueryPlanIndexItem); + entry.AddReferringStatement(statementName); + indexes.Put(indexMultiKey, entry); + } + + public IDictionary Indexes + { + get { return indexes; } + } + + public void RemoveIndex(IndexMultiKey imk) + { + indexes.Remove(imk); + } + + public bool RemoveIndexReference(IndexMultiKey index, string referringStatementName) + { + var entry = indexes.Get(index); + if (entry == null) { + return false; + } + return entry.RemoveReferringStatement(referringStatementName); + } + + public void AddIndexReference(string indexName, string statementName) + { + var entry = FindIndex(indexName); + if (entry == null) { + return; + } + entry.Value.Value.AddReferringStatement(statementName); + } + + public void AddIndexReference(IndexMultiKey indexMultiKey, string statementName) + { + EventTableIndexMetadataEntry entry = indexes.Get(indexMultiKey); + if (entry == null) { + return; + } + entry.AddReferringStatement(statementName); + } + + public IndexMultiKey GetIndexByName(string indexName) + { + var entry = FindIndex(indexName); + if (entry == null) { + return null; + } + return entry.Value.Key; + } + + public ICollection GetRemoveRefIndexesDereferenced(string statementName) + { + ICollection indexNamesDerrefd = null; + foreach (var entry in indexes) { + bool last = entry.Value.RemoveReferringStatement(statementName); + if (last) { + if (indexNamesDerrefd == null) { + indexNamesDerrefd = new ArrayDeque(2); + } + indexNamesDerrefd.Add(entry.Value.OptionalIndexName); + } + } + if (indexNamesDerrefd == null) { + return Collections.GetEmptyList(); + } + foreach (string name in indexNamesDerrefd) { + RemoveIndex(GetIndexByName(name)); + } + return indexNamesDerrefd; + } + + private KeyValuePair? FindIndex(string indexName) + { + foreach (var entry in indexes) + { + if (entry.Value.OptionalIndexName != null && entry.Value.OptionalIndexName == indexName) + { + return entry; + } + } + return null; + } + + public string[][] UniqueIndexProps + { + get + { + var uniques = new ArrayDeque(2); + foreach (var entry in indexes) + { + if (entry.Key.IsUnique) + { + var props = new string[entry.Key.HashIndexedProps.Length]; + for (int i = 0; i < entry.Key.HashIndexedProps.Length; i++) + { + props[i] = entry.Key.HashIndexedProps[i].IndexPropName; + } + uniques.Add(props); + } + } + return uniques.ToArray(); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/lookup/EventTableIndexMetadataEntry.cs b/NEsper.Core/NEsper.Core/epl/lookup/EventTableIndexMetadataEntry.cs new file mode 100755 index 000000000..9967e12aa --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/EventTableIndexMetadataEntry.cs @@ -0,0 +1,64 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.@join.plan; + +namespace com.espertech.esper.epl.lookup +{ + public class EventTableIndexMetadataEntry : EventTableIndexEntryBase + { + private readonly bool _primary; + private readonly ISet _referencedByStmt; + private readonly QueryPlanIndexItem _queryPlanIndexItem; + + public EventTableIndexMetadataEntry(string optionalIndexName, bool primary, QueryPlanIndexItem queryPlanIndexItem) + : base(optionalIndexName) + { + _primary = primary; + _queryPlanIndexItem = queryPlanIndexItem; + _referencedByStmt = primary ? null : new HashSet(); + } + + public void AddReferringStatement(string statementName) + { + if (!_primary) { + _referencedByStmt.Add(statementName); + } + } + + public bool RemoveReferringStatement(string referringStatementName) + { + if (!_primary) { + _referencedByStmt.Remove(referringStatementName); + if (_referencedByStmt.IsEmpty()) { + return true; + } + } + return false; + } + + public bool IsPrimary + { + get { return _primary; } + } + + public string[] ReferringStatements + { + get { return _referencedByStmt.ToArray(); } + } + + public QueryPlanIndexItem QueryPlanIndexItem + { + get { return _queryPlanIndexItem; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/lookup/EventTableIndexRepository.cs b/NEsper.Core/NEsper.Core/epl/lookup/EventTableIndexRepository.cs new file mode 100755 index 000000000..b1a79e7cb --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/EventTableIndexRepository.cs @@ -0,0 +1,218 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.join.hint; +using com.espertech.esper.epl.join.plan; +using com.espertech.esper.epl.join.table; +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.epl.lookup +{ + /// + /// A repository of index tables for use with anything that + /// may use the indexes to correlate triggering events with indexed events. + /// Maintains index tables and keeps a reference count for user. Allows reuse of indexes for multiple + /// deleting statements. + /// + public class EventTableIndexRepository + { + private readonly IList _tables; + private readonly IDictionary _tableIndexesRefCount; + private readonly IDictionary _explicitIndexes; + + /// + /// Ctor. + /// + public EventTableIndexRepository() + { + _tables = new List(); + _tableIndexesRefCount = new Dictionary(); + _explicitIndexes = new Dictionary(); + } + + public Pair AddExplicitIndexOrReuse( + bool unique, + IList hashProps, + IList btreeProps, + IEnumerable prefilledEvents, + EventType indexedType, + string indexName, + AgentInstanceContext agentInstanceContext, + object optionalSerde) + { + if (hashProps.IsEmpty() && btreeProps.IsEmpty()) { + throw new ArgumentException("Invalid zero element list for hash and btree columns"); + } + + // Get an existing table, if any, matching the exact requirement + var indexPropKeyMatch = EventTableIndexUtil.FindExactMatchNameAndType(_tableIndexesRefCount.Keys, unique, hashProps, btreeProps); + if (indexPropKeyMatch != null) { + var refTablePair = _tableIndexesRefCount.Get(indexPropKeyMatch); + return new Pair(indexPropKeyMatch, new EventTableAndNamePair(refTablePair.Table, refTablePair.OptionalIndexName)); + } + + return AddIndex(unique, hashProps, btreeProps, prefilledEvents, indexedType, indexName, false, agentInstanceContext, optionalSerde); + } + + public void AddIndex(IndexMultiKey indexMultiKey, EventTableIndexRepositoryEntry entry) { + _tableIndexesRefCount.Put(indexMultiKey, entry); + _tables.Add(entry.Table); + } + + /// + /// Returns a list of current index tables in the repository. + /// + /// index tables + public IList GetTables() + { + return _tables; + } + + /// + /// Dispose indexes. + /// + public void Destroy() + { + foreach (var table in _tables) { + table.Destroy(); + } + _tables.Clear(); + _tableIndexesRefCount.Clear(); + } + + public Pair FindTable(ISet keyPropertyNames, ISet rangePropertyNames, IList optionalIndexHintInstructions) { + var pair = EventTableIndexUtil.FindIndexBestAvailable(_tableIndexesRefCount, keyPropertyNames, rangePropertyNames, optionalIndexHintInstructions); + if (pair == null) { + return null; + } + var tableFound = ((EventTableIndexRepositoryEntry) pair.Second).Table; + return new Pair(pair.First, new EventTableAndNamePair(tableFound, pair.Second.OptionalIndexName)); + } + + public IndexMultiKey[] GetIndexDescriptors() + { + return _tableIndexesRefCount.Keys.ToArray(); + } + + public void ValidateAddExplicitIndex(bool unique, string indexName, IList columns, EventType eventType, IEnumerable dataWindowContents, AgentInstanceContext agentInstanceContext, bool allowIndexExists, object optionalSerde) + { + if (_explicitIndexes.ContainsKey(indexName)) { + if (allowIndexExists) { + return; + } + throw new ExprValidationException("Index by name '" + indexName + "' already exists"); + } + + var desc = EventTableIndexUtil.ValidateCompileExplicitIndex(unique, columns, eventType); + AddExplicitIndex(indexName, desc, eventType, dataWindowContents, agentInstanceContext, optionalSerde); + } + + public void AddExplicitIndex(string indexName, EventTableCreateIndexDesc desc, EventType eventType, IEnumerable dataWindowContents, AgentInstanceContext agentInstanceContext, object optionalSerde) + { + var pair = AddExplicitIndexOrReuse(desc.IsUnique, desc.HashProps, desc.BtreeProps, dataWindowContents, eventType, indexName, agentInstanceContext, optionalSerde); + _explicitIndexes.Put(indexName, pair.Second.EventTable); + } + + public EventTable GetExplicitIndexByName(string indexName) + { + return _explicitIndexes.Get(indexName); + } + + public EventTable GetIndexByDesc(IndexMultiKey indexKey) + { + var entry = _tableIndexesRefCount.Get(indexKey); + if (entry == null) { + return null; + } + return entry.Table; + } + + private Pair AddIndex(bool unique, IList hashProps, IList btreeProps, IEnumerable prefilledEvents, EventType indexedType, string indexName, bool mustCoerce, AgentInstanceContext agentInstanceContext, object optionalSerde) + { + // not resolved as full match and not resolved as unique index match, allocate + var indexPropKey = new IndexMultiKey(unique, hashProps, btreeProps); + + var indexedPropDescs = hashProps.ToArray(); + var indexProps = IndexedPropDesc.GetIndexProperties(indexedPropDescs); + var indexCoercionTypes = IndexedPropDesc.GetCoercionTypes(indexedPropDescs); + if (!mustCoerce) { + indexCoercionTypes = null; + } + + var rangePropDescs = btreeProps.ToArray(); + var rangeProps = IndexedPropDesc.GetIndexProperties(rangePropDescs); + var rangeCoercionTypes = IndexedPropDesc.GetCoercionTypes(rangePropDescs); + + var indexItem = new QueryPlanIndexItem(indexProps, indexCoercionTypes, rangeProps, rangeCoercionTypes, false); + var table = EventTableUtil.BuildIndex(agentInstanceContext, 0, indexItem, indexedType, true, unique, indexName, optionalSerde, false); + + // fill table since its new + var events = new EventBean[1]; + foreach (var prefilledEvent in prefilledEvents) + { + events[0] = prefilledEvent; + table.Add(events); + } + + // add table + _tables.Add(table); + + // add index, reference counted + _tableIndexesRefCount.Put(indexPropKey, new EventTableIndexRepositoryEntry(indexName, table)); + + return new Pair(indexPropKey, new EventTableAndNamePair(table, indexName)); + } + + public string[] GetExplicitIndexNames() + { + return _explicitIndexes.Keys.ToArray(); + } + + public void RemoveIndex(IndexMultiKey index) + { + var entry = _tableIndexesRefCount.Delete(index); + if (entry != null) { + _tables.Remove(entry.Table); + if (entry.OptionalIndexName != null) { + _explicitIndexes.Remove(entry.OptionalIndexName); + } + entry.Table.Destroy(); + } + } + + public IndexMultiKey GetIndexByName(string indexName) + { + foreach (var entry in _tableIndexesRefCount) + { + if (entry.Value.OptionalIndexName == indexName) + { + return entry.Key; + } + } + return null; + } + + public void RemoveExplicitIndex(string indexName) + { + var eventTable = _explicitIndexes.Delete(indexName); + if (eventTable != null) { + eventTable.Destroy(); + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/lookup/EventTableIndexRepositoryEntry.cs b/NEsper.Core/NEsper.Core/epl/lookup/EventTableIndexRepositoryEntry.cs new file mode 100755 index 000000000..608a3ef6f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/EventTableIndexRepositoryEntry.cs @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.join.table; + +namespace com.espertech.esper.epl.lookup +{ + public class EventTableIndexRepositoryEntry : EventTableIndexEntryBase + { + public EventTableIndexRepositoryEntry(string optionalIndexName, EventTable table) + : base(optionalIndexName) + { + Table = table; + } + + public EventTable Table { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/lookup/EventTableIndexService.cs b/NEsper.Core/NEsper.Core/epl/lookup/EventTableIndexService.cs new file mode 100755 index 000000000..e01aa1c4d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/EventTableIndexService.cs @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.join.table; + +namespace com.espertech.esper.epl.lookup +{ + public interface EventTableIndexService + { + bool AllowInitIndex(bool isRecoveringResilient); + EventTableFactory CreateUnindexed(int indexedStreamNum, object optionalSerde, bool isFireAndForget); + EventTableFactory CreateSingle(int indexedStreamNum, EventType eventType, string indexProp, bool unique, string optionalIndexName, object optionalSerde, bool isFireAndForget); + EventTableFactory CreateSingleCoerceAdd(int indexedStreamNum, EventType eventType, string indexProp, Type indexCoercionType, object optionalSerde, bool isFireAndForget); + EventTableFactory CreateSingleCoerceAll(int indexedStreamNum, EventType eventType, string indexProp, Type indexCoercionType, object optionalSerde, bool isFireAndForget); + EventTableFactory CreateMultiKey(int indexedStreamNum, EventType eventType, IList indexProps, bool unique, string optionalIndexName, object optionalSerde, bool isFireAndForget); + EventTableFactory CreateMultiKeyCoerceAdd(int indexedStreamNum, EventType eventType, IList indexProps, IList indexCoercionTypes, bool isFireAndForget); + EventTableFactory CreateMultiKeyCoerceAll(int indexedStreamNum, EventType eventType, IList indexProps, IList indexCoercionTypes, bool isFireAndForget); + EventTableFactory CreateComposite(int indexedStreamNum, EventType eventType, IList indexedKeyProps, IList coercionKeyTypes, IList indexedRangeProps, IList coercionRangeTypes, bool isFireAndForget); + EventTableFactory CreateSorted(int indexedStreamNum, EventType eventType, string indexedProp, bool isFireAndForget); + EventTableFactory CreateSortedCoerce(int indexedStreamNum, EventType eventType, string indexedProp, Type indexCoercionType, bool isFireAndForget); + EventTableFactory CreateInArray(int indexedStreamNum, EventType eventType, string[] indexedProp, bool unique); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/lookup/EventTableIndexServiceImpl.cs b/NEsper.Core/NEsper.Core/epl/lookup/EventTableIndexServiceImpl.cs new file mode 100755 index 000000000..4360d6381 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/EventTableIndexServiceImpl.cs @@ -0,0 +1,66 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.join.table; + +namespace com.espertech.esper.epl.lookup +{ + public class EventTableIndexServiceImpl : EventTableIndexService { + public bool AllowInitIndex(bool isRecoveringResilient) { + return true; + } + + public EventTableFactory CreateSingleCoerceAll(int indexedStreamNum, EventType eventType, string indexProp, Type indexCoercionType, object optionalSerde, bool isFireAndForget) { + return new PropertyIndexedEventTableSingleCoerceAllFactory(indexedStreamNum, eventType, indexProp, indexCoercionType); + } + + public EventTableFactory CreateSingleCoerceAdd(int indexedStreamNum, EventType eventType, string indexProp, Type indexCoercionType, object optionalSerde, bool isFireAndForget) { + return new PropertyIndexedEventTableSingleCoerceAddFactory(indexedStreamNum, eventType, indexProp, indexCoercionType); + } + + public EventTableFactory CreateSingle(int indexedStreamNum, EventType eventType, string propertyName, bool unique, string optionalIndexName, object optionalSerde, bool isFireAndForget) { + return new PropertyIndexedEventTableSingleFactory(indexedStreamNum, eventType, propertyName, unique, optionalIndexName); + } + + public EventTableFactory CreateUnindexed(int indexedStreamNum, object optionalSerde, bool isFireAndForget) { + return new UnindexedEventTableFactory(indexedStreamNum); + } + + public EventTableFactory CreateMultiKey(int indexedStreamNum, EventType eventType, IList indexProps, bool unique, string optionalIndexName, object optionalSerde, bool isFireAndForget) { + return new PropertyIndexedEventTableFactory(indexedStreamNum, eventType, indexProps, unique, optionalIndexName); + } + + public EventTableFactory CreateMultiKeyCoerceAdd(int indexedStreamNum, EventType eventType, IList indexProps, IList indexCoercionTypes, bool isFireAndForget) { + return new PropertyIndexedEventTableCoerceAddFactory(indexedStreamNum, eventType, indexProps, indexCoercionTypes); + } + + public EventTableFactory CreateMultiKeyCoerceAll(int indexedStreamNum, EventType eventType, IList indexProps, IList indexCoercionTypes, bool isFireAndForget) { + return new PropertyIndexedEventTableCoerceAllFactory(indexedStreamNum, eventType, indexProps, indexCoercionTypes); + } + + public EventTableFactory CreateComposite(int indexedStreamNum, EventType eventType, IList indexedKeyProps, IList coercionKeyTypes, IList indexedRangeProps, IList coercionRangeTypes, bool isFireAndForget) { + return new PropertyCompositeEventTableFactory(indexedStreamNum, eventType, indexedKeyProps, coercionKeyTypes, indexedRangeProps, coercionRangeTypes); + } + + public EventTableFactory CreateSorted(int indexedStreamNum, EventType eventType, string indexedProp, bool isFireAndForget) { + return new PropertySortedEventTableFactory(indexedStreamNum, eventType, indexedProp); + } + + public EventTableFactory CreateSortedCoerce(int indexedStreamNum, EventType eventType, string indexedProp, Type indexCoercionType, bool isFireAndForget) { + return new PropertySortedEventTableCoercedFactory(indexedStreamNum, eventType, indexedProp, indexCoercionType); + } + + public EventTableFactory CreateInArray(int indexedStreamNum, EventType eventType, string[] indexedProp, bool unique) { + return new PropertyIndexedEventTableSingleArrayFactory(0, eventType, indexedProp, unique, null); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/lookup/EventTableIndexUtil.cs b/NEsper.Core/NEsper.Core/epl/lookup/EventTableIndexUtil.cs new file mode 100755 index 000000000..532beeb4a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/EventTableIndexUtil.cs @@ -0,0 +1,353 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.join.hint; +using com.espertech.esper.epl.spec; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.lookup +{ + public class EventTableIndexUtil + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + private static readonly IndexComparatorShortestPath INDEX_COMPARATOR_INSTANCE = new IndexComparatorShortestPath(); + + public static EventTableCreateIndexDesc ValidateCompileExplicitIndex(bool unique, IList columns, EventType eventType) + { + IList hashProps = new List(); + IList btreeProps = new List(); + + ISet indexed = new HashSet(); + foreach (var columnDesc in columns) + { + var columnName = columnDesc.Name; + + var type = eventType.GetPropertyType(columnName).GetBoxedType(); + if (type == null) + { + throw new ExprValidationException("Property named '" + columnName + "' not found"); + } + if (!indexed.Add(columnName)) + { + throw new ExprValidationException("Property named '" + columnName + "' has been declared more then once"); + } + + var desc = new IndexedPropDesc(columnName, type); + if (columnDesc.Type == CreateIndexType.HASH) + { + hashProps.Add(desc); + } + else + { + btreeProps.Add(desc); + } + } + + if (unique && !btreeProps.IsEmpty()) + { + throw new ExprValidationException("Combination of unique index with btree (range) is not supported"); + } + return new EventTableCreateIndexDesc(hashProps, btreeProps, unique); + } + + public static IndexMultiKey FindIndexConsiderTyping(IDictionary tableIndexesRefCount, + IList hashProps, + IList btreeProps, + IList optionalIndexHintInstructions) + { + if (hashProps.IsEmpty() && btreeProps.IsEmpty()) + { + throw new ArgumentException("Invalid zero element list for hash and btree columns"); + } + + var indexCandidates = //(IDictionary) + EventTableIndexUtil.FindCandidates(tableIndexesRefCount, hashProps, btreeProps); + + // if there are hints, follow these + if (optionalIndexHintInstructions != null) + { + var found = EventTableIndexUtil.FindByIndexHint(indexCandidates, optionalIndexHintInstructions); + if (found != null) + { + return found; + } + } + + // Get an existing table, if any, matching the exact requirement, prefer unique + var indexPropKeyMatch = EventTableIndexUtil.FindExactMatchNameAndType(tableIndexesRefCount.Keys, true, hashProps, btreeProps); + if (indexPropKeyMatch == null) + { + indexPropKeyMatch = EventTableIndexUtil.FindExactMatchNameAndType(tableIndexesRefCount.Keys, false, hashProps, btreeProps); + } + if (indexPropKeyMatch != null) + { + return indexPropKeyMatch; + } + + if (indexCandidates.IsEmpty()) + { + return null; + } + + var transIndexCandidates = indexCandidates.Transform( + k => k, v => v, + k => k, v => (EventTableIndexMetadataEntry)v); + + return GetBestCandidate(transIndexCandidates).First; + } + + public static Pair FindIndexBestAvailable( + IDictionary tablesAvailable, + ISet keyPropertyNames, + ISet rangePropertyNames, + IList optionalIndexHintInstructions) where T : EventTableIndexEntryBase + { + if (keyPropertyNames.IsEmpty() && rangePropertyNames.IsEmpty()) + { + return null; + } + + // determine candidates + IList hashProps = new List(); + foreach (var keyPropertyName in keyPropertyNames) + { + hashProps.Add(new IndexedPropDesc(keyPropertyName, null)); + } + IList rangeProps = new List(); + foreach (var rangePropertyName in rangePropertyNames) + { + rangeProps.Add(new IndexedPropDesc(rangePropertyName, null)); + } + + var indexCandidates = EventTableIndexUtil + .FindCandidates(tablesAvailable, hashProps, rangeProps) + .Transform( + k => k, v => v, + k => k, v => v as T); + + // handle hint + if (optionalIndexHintInstructions != null) + { + var found = EventTableIndexUtil.FindByIndexHint(indexCandidates, optionalIndexHintInstructions); + if (found != null) + { + return GetPair(tablesAvailable, found); + } + } + + // no candidates + if (indexCandidates == null || indexCandidates.IsEmpty()) + { + if (Log.IsDebugEnabled) + { + Log.Debug("No index found."); + } + return null; + } + + return GetBestCandidate(indexCandidates); + } + + private static Pair GetBestCandidate(IDictionary indexCandidates) + { + // take the table that has a unique index + IList indexes = new List(); + foreach (var entry in indexCandidates) + { + if (entry.Key.IsUnique) + { + indexes.Add(entry.Key); + } + } + if (!indexes.IsEmpty()) + { + indexes.SortInPlace(INDEX_COMPARATOR_INSTANCE); + return GetPair(indexCandidates, indexes[0]); + } + + // take the best available table + indexes.Clear(); + indexes.AddAll(indexCandidates.Keys); + if (indexes.Count > 1) + { + indexes.SortInPlace(INDEX_COMPARATOR_INSTANCE); + } + return GetPair(indexCandidates, indexes[0]); + } + + public static IndexMultiKey FindByIndexHint(IDictionary indexCandidates, IList instructions) + where T : EventTableIndexEntryBase + { + foreach (var instruction in instructions) + { + if (instruction is IndexHintInstructionIndexName) + { + var indexName = ((IndexHintInstructionIndexName)instruction).IndexName; + var found = FindExplicitIndexByName(indexCandidates, indexName); + if (found != null) + { + return found; + } + } + if (instruction is IndexHintInstructionExplicit) + { + var found = FindExplicitIndexAnyName(indexCandidates); + if (found != null) + { + return found; + } + } + if (instruction is IndexHintInstructionBust) + { + throw new EPException("Failed to plan index access, index hint busted out"); + } + } + return null; + } + + public static IndexMultiKey FindExactMatchNameAndType(IEnumerable indexMultiKeys, bool unique, IList hashProps, IList btreeProps) + { + foreach (var existing in indexMultiKeys) + { + if (IsExactMatch(existing, unique, hashProps, btreeProps)) + { + return existing; + } + } + return null; + } + + private static IDictionary FindCandidates(IDictionary indexes, IList hashProps, IList btreeProps) + where T : EventTableIndexEntryBase + { + IDictionary indexCandidates = new Dictionary(); + foreach (var entry in indexes) + { + var matches = IndexMatchesProvided(entry.Key, hashProps, btreeProps); + if (matches) + { + indexCandidates.Put(entry.Key, entry.Value); + } + } + return indexCandidates; + } + + private static IndexMultiKey FindExplicitIndexByName(IDictionary indexCandidates, string name) + where T : EventTableIndexEntryBase + { + foreach (var entry in indexCandidates) + { + if (entry.Value.OptionalIndexName != null && entry.Value.OptionalIndexName.Equals(name)) + { + return entry.Key; + } + } + return null; + } + + private static IndexMultiKey FindExplicitIndexAnyName(IDictionary indexCandidates) + where T : EventTableIndexEntryBase + { + foreach (var entry in indexCandidates) + { + if (entry.Value.OptionalIndexName != null) + { + return entry.Key; + } + } + return null; + } + + private static bool IndexHashIsProvided(IndexedPropDesc hashPropIndexed, IList hashPropsProvided) + { + foreach (var hashPropProvided in hashPropsProvided) + { + var nameMatch = hashPropProvided.IndexPropName.Equals(hashPropIndexed.IndexPropName); + var typeMatch = true; + if (hashPropProvided.CoercionType != null && !TypeHelper.IsSubclassOrImplementsInterface(hashPropProvided.CoercionType.GetBoxedType(), hashPropIndexed.CoercionType.GetBoxedType())) + { + typeMatch = false; + } + if (nameMatch && typeMatch) + { + return true; + } + } + return false; + } + + private static bool IsExactMatch(IndexMultiKey existing, bool unique, IList hashProps, IList btreeProps) + { + if (existing.IsUnique != unique) + { + return false; + } + var keyPropCompare = IndexedPropDesc.Compare(existing.HashIndexedProps, hashProps); + return keyPropCompare && IndexedPropDesc.Compare(existing.RangeIndexedProps, btreeProps); + } + + private static bool IndexMatchesProvided(IndexMultiKey indexDesc, IList hashPropsProvided, IList rangePropsProvided) + { + var hashPropIndexedList = indexDesc.HashIndexedProps; + foreach (var hashPropIndexed in hashPropIndexedList) + { + var foundHashProp = IndexHashIsProvided(hashPropIndexed, hashPropsProvided); + if (!foundHashProp) + { + return false; + } + } + + var rangePropIndexedList = indexDesc.RangeIndexedProps; + foreach (var rangePropIndexed in rangePropIndexedList) + { + var foundRangeProp = IndexHashIsProvided(rangePropIndexed, rangePropsProvided); + if (!foundRangeProp) + { + return false; + } + } + + return true; + } + + private static Pair GetPair(IDictionary tableIndexesRefCount, IndexMultiKey indexMultiKey) + where T : EventTableIndexEntryBase + { + EventTableIndexEntryBase indexFound = tableIndexesRefCount.Get(indexMultiKey); + return new Pair(indexMultiKey, indexFound); + } + + [Serializable] + internal class IndexComparatorShortestPath : IComparer + { + public int Compare(IndexMultiKey o1, IndexMultiKey o2) + { + var indexedProps1 = IndexedPropDesc.GetIndexProperties(o1.HashIndexedProps); + var indexedProps2 = IndexedPropDesc.GetIndexProperties(o2.HashIndexedProps); + if (indexedProps1.Length > indexedProps2.Length) + { + return 1; // sort desc by count columns + } + if (indexedProps1.Length == indexedProps2.Length) + { + return 0; + } + return -1; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/lookup/IndexKeyInfo.cs b/NEsper.Core/NEsper.Core/epl/lookup/IndexKeyInfo.cs new file mode 100755 index 000000000..b12ac9fe0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/IndexKeyInfo.cs @@ -0,0 +1,33 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.epl.join.plan; + +namespace com.espertech.esper.epl.lookup +{ + public class IndexKeyInfo + { + public IndexKeyInfo(IList orderedKeyDesc, CoercionDesc orderedKeyCoercionTypes, IList orderedRangeDesc, CoercionDesc orderedRangeCoercionTypes) + { + OrderedHashDesc = orderedKeyDesc; + OrderedKeyCoercionTypes = orderedKeyCoercionTypes; + OrderedRangeDesc = orderedRangeDesc; + OrderedRangeCoercionTypes = orderedRangeCoercionTypes; + } + + public IList OrderedHashDesc { get; private set; } + + public CoercionDesc OrderedKeyCoercionTypes { get; private set; } + + public IList OrderedRangeDesc { get; private set; } + + public CoercionDesc OrderedRangeCoercionTypes { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/lookup/IndexMultiKey.cs b/NEsper.Core/NEsper.Core/epl/lookup/IndexMultiKey.cs new file mode 100755 index 000000000..ebf9daf17 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/IndexMultiKey.cs @@ -0,0 +1,104 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.epl.lookup +{ + public class IndexMultiKey + { + public IndexMultiKey(Boolean unique, + IList hashIndexedProps, + IList rangeIndexedProps) + { + IsUnique = unique; + HashIndexedProps = hashIndexedProps.ToArray(); + RangeIndexedProps = rangeIndexedProps.ToArray(); + } + + public Boolean IsUnique { get; private set; } + public IndexedPropDesc[] HashIndexedProps { get; private set; } + public IndexedPropDesc[] RangeIndexedProps { get; private set; } + + public String ToQueryPlan() + { + StringWriter writer = new StringWriter(); + writer.Write(IsUnique ? "unique " : "non-unique "); + writer.Write("hash={"); + IndexedPropDesc.ToQueryPlan(writer, HashIndexedProps); + writer.Write("} btree={"); + IndexedPropDesc.ToQueryPlan(writer, RangeIndexedProps); + writer.Write("}"); + return writer.ToString(); + } + + public bool Equals(IndexMultiKey other) + { + if (ReferenceEquals(null, other)) + return false; + if (ReferenceEquals(this, other)) + return true; + return other.IsUnique.Equals(IsUnique) + && Collections.AreEqual(other.HashIndexedProps, HashIndexedProps) + && Collections.AreEqual(other.RangeIndexedProps, RangeIndexedProps); + } + + /// + /// Determines whether the specified is equal to the current . + /// + /// + /// true if the specified is equal to the current ; otherwise, false. + /// + /// The to compare with the current . 2 + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) + return false; + if (ReferenceEquals(this, obj)) + return true; + if (obj.GetType() != typeof (IndexMultiKey)) + return false; + return Equals((IndexMultiKey) obj); + } + + /// + /// Serves as a hash function for a particular type. + /// + /// + /// A hash code for the current . + /// + /// 2 + public override int GetHashCode() + { + unchecked + { + int result = IsUnique.GetHashCode(); + result = (result*397) ^ (HashIndexedProps != null ? Collections.GetHashCode(HashIndexedProps) : 0); + result = (result*397) ^ (RangeIndexedProps != null ? Collections.GetHashCode(RangeIndexedProps) : 0); + return result; + } + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + /// 2 + public override string ToString() + { + return string.Format("IsUnique: {0}, HashIndexedProps: {1}, RangeIndexedProps: {2}", IsUnique, HashIndexedProps, RangeIndexedProps); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/lookup/IndexedPropDesc.cs b/NEsper.Core/NEsper.Core/epl/lookup/IndexedPropDesc.cs new file mode 100755 index 000000000..72b40708f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/IndexedPropDesc.cs @@ -0,0 +1,157 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.lookup +{ + /// + /// Holds property information for joined properties in a lookup. + /// + public class IndexedPropDesc : IComparable + { + /// Ctor. + /// is the property name of the indexed field + /// is the type to coerce to + public IndexedPropDesc(String indexPropName, Type coercionType) + { + IndexPropName = indexPropName; + CoercionType = coercionType; + } + + /// Returns the property name of the indexed field. + /// property name of indexed field + public string IndexPropName { get; private set; } + + /// Returns the coercion type of key to index field. + /// type to coerce to + public Type CoercionType { get; private set; } + + /// Returns the index property names given an array of descriptors. + /// descriptors of joined properties + /// array of index property names + public static String[] GetIndexProperties(IndexedPropDesc[] descList) + { + var result = new String[descList.Length]; + int count = 0; + foreach (IndexedPropDesc desc in descList) + { + result[count++] = desc.IndexPropName; + } + return result; + } + + public static String[] GetIndexProperties(List descList) { + var result = new String[descList.Count]; + int count = 0; + foreach (IndexedPropDesc desc in descList) + { + result[count++] = desc.IndexPropName; + } + return result; + } + + public static int GetPropertyIndex(String propertyName, IndexedPropDesc[] descList) { + for (int i = 0; i < descList.Length; i++) { + if (descList[i].IndexPropName.Equals(propertyName)) { + return i; + } + } + return -1; + } + + /// Returns the key coercion types. + /// a list of descriptors + /// key coercion types + public static Type[] GetCoercionTypes(IndexedPropDesc[] descList) + { + var result = new Type[descList.Length]; + int count = 0; + foreach (IndexedPropDesc desc in descList) + { + result[count++] = desc.CoercionType; + } + return result; + } + + public int CompareTo(Object o) + { + var other = (IndexedPropDesc) o; + return IndexPropName.CompareTo(other.IndexPropName); + } + + public override bool Equals(Object o) + { + if (this == o) + { + return true; + } + if (o == null || GetType() != o.GetType()) + { + return false; + } + + var that = (IndexedPropDesc) o; + + if (!CoercionType.Equals(that.CoercionType)) + { + return false; + } + if (!IndexPropName.Equals(that.IndexPropName)) + { + return false; + } + return true; + } + + public static bool Compare(IList first, IList second) { + if (first.Count != second.Count) { + return false; + } + var copyFirst = first.OrderBy(o => o.IndexPropName).ToList(); + var copySecond = second.OrderBy(o => o.IndexPropName).ToList(); + return !copyFirst.Where((t,i) => !Equals(t, copySecond[i])).Any(); + } + + /// + /// Serves as a hash function for a particular type. + /// + /// + /// A hash code for the current . + /// + /// 2 + public override int GetHashCode() + { + unchecked + { + return + ((IndexPropName != null ? IndexPropName.GetHashCode() : 0) * 397) ^ + (CoercionType != null ? CoercionType.GetHashCode() : 0); + } + } + + public static void ToQueryPlan(TextWriter writer, IndexedPropDesc[] indexedProps) + { + String delimiter = ""; + foreach (IndexedPropDesc prop in indexedProps) + { + writer.Write(delimiter); + writer.Write(prop.IndexPropName); + writer.Write("("); + writer.Write(TypeHelper.GetSimpleNameForType(prop.CoercionType)); + writer.Write(")"); + delimiter = ","; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/lookup/LookupStrategyDesc.cs b/NEsper.Core/NEsper.Core/epl/lookup/LookupStrategyDesc.cs new file mode 100755 index 000000000..1173644ad --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/LookupStrategyDesc.cs @@ -0,0 +1,35 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.epl.lookup +{ + public class LookupStrategyDesc + { + public LookupStrategyDesc(LookupStrategyType lookupStrategy, String[] expressionsTexts) + { + LookupStrategy = lookupStrategy; + ExpressionsTexts = expressionsTexts; + } + + public LookupStrategyType LookupStrategy { get; private set; } + + public string[] ExpressionsTexts { get; private set; } + + public override String ToString() + { + return "LookupStrategyDesc{" + + "lookupStrategy=" + LookupStrategy + + ", expressionsTexts=" + (ExpressionsTexts == null ? null : ExpressionsTexts.Render()) + + '}'; + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/lookup/LookupStrategyType.cs b/NEsper.Core/NEsper.Core/epl/lookup/LookupStrategyType.cs new file mode 100755 index 000000000..d2c4c07a4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/LookupStrategyType.cs @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.lookup +{ + public enum LookupStrategyType + { + NULLROWS, + FULLTABLESCAN, + VDW, + SINGLEPROP, + SINGLEPROPUNIQUE, + SINGLEPROPNONUNIQUE, + MULTIPROP, + RANGE, + COMPOSITE, + SINGLEEXPR, + MULTIEXPR, + INKEYWORDMULTIIDX, + INKEYWORDSINGLEIDX + } +} diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordCompositeTableLookupStrategy.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordCompositeTableLookupStrategy.cs new file mode 100755 index 000000000..08a0273b0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordCompositeTableLookupStrategy.cs @@ -0,0 +1,59 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.@join.exec.composite; +using com.espertech.esper.epl.@join.table; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.lookup +{ + /// + /// MapIndex lookup strategy for subqueries. + /// + public class SubordCompositeTableLookupStrategy : SubordTableLookupStrategy + { + private readonly CompositeIndexQuery _innerIndexQuery; + private readonly PropertyCompositeEventTable _index; + private readonly LookupStrategyDesc _strategyDesc; + + public SubordCompositeTableLookupStrategy(CompositeIndexQuery innerIndexQuery, PropertyCompositeEventTable index, LookupStrategyDesc strategyDesc) + { + _innerIndexQuery = innerIndexQuery; + _index = index; + _strategyDesc = strategyDesc; + } + + public ICollection Lookup(EventBean[] eventsPerStream, ExprEvaluatorContext context) + { + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().QIndexSubordLookup(this, _index, null); + var keys = new List(); // can collect nulls + var result = _innerIndexQuery.GetCollectKeys(eventsPerStream, _index.IndexTable, context, keys, _index.PostProcessor); + InstrumentationHelper.Get().AIndexSubordLookup(result, keys.Count > 1 ? keys.ToArray() : keys[0]); + return result; + } + + return _innerIndexQuery.Get(eventsPerStream, _index.IndexTable, context, _index.PostProcessor); + } + + public String ToQueryPlan() { + return GetType().FullName; + } + + public LookupStrategyDesc StrategyDesc + { + get { return _strategyDesc; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordCompositeTableLookupStrategyFactory.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordCompositeTableLookupStrategyFactory.cs new file mode 100755 index 000000000..938aeb77a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordCompositeTableLookupStrategyFactory.cs @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.epl.@join.exec.composite; +using com.espertech.esper.epl.@join.table; +using com.espertech.esper.epl.virtualdw; + +namespace com.espertech.esper.epl.lookup +{ + /// + /// MapIndex lookup strategy for subqueries. + /// + public class SubordCompositeTableLookupStrategyFactory : SubordTableLookupStrategyFactory + { + private readonly CompositeIndexQuery _innerIndexQuery; + private readonly ICollection _rangeDescs; + private readonly LookupStrategyDesc _strategyDesc; + + public SubordCompositeTableLookupStrategyFactory( + bool isNWOnTrigger, + int numStreams, + ICollection keyExpr, + Type[] coercionKeyTypes, + ICollection rangeProps, + Type[] coercionRangeTypes) + { + _rangeDescs = rangeProps; + var expressionTexts = new List(); + _innerIndexQuery = CompositeIndexQueryFactory.MakeSubordinate( + isNWOnTrigger, numStreams, keyExpr, coercionKeyTypes, rangeProps, coercionRangeTypes, expressionTexts); + _strategyDesc = new LookupStrategyDesc(LookupStrategyType.COMPOSITE, expressionTexts.ToArray()); + } + + public SubordTableLookupStrategy MakeStrategy(EventTable[] eventTable, VirtualDWView vdw) + { + return new SubordCompositeTableLookupStrategy( + _innerIndexQuery, (PropertyCompositeEventTable) eventTable[0], _strategyDesc); + } + + public String ToQueryPlan() + { + return GetType().FullName + " ranges=" + SubordPropRangeKey.ToQueryPlan(_rangeDescs); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordFullTableScanLookupStrategy.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordFullTableScanLookupStrategy.cs new file mode 100755 index 000000000..7a40bcee6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordFullTableScanLookupStrategy.cs @@ -0,0 +1,61 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.join.table; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.lookup +{ + /// + /// Lookup on an unindexed table returning the full table as matching events. + /// + public class SubordFullTableScanLookupStrategy : SubordTableLookupStrategy + { + private readonly UnindexedEventTable _eventIndex; + + /// Ctor. + /// table to use + public SubordFullTableScanLookupStrategy(UnindexedEventTable eventIndex) + { + _eventIndex = eventIndex; + } + + public ICollection Lookup(EventBean[] eventPerStream, ExprEvaluatorContext context) + { + return LookupInternal(); + } + + private ICollection LookupInternal() + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QIndexSubordLookup(this, _eventIndex, null);} + ICollection result = _eventIndex.EventSet; + if (result.IsEmpty()) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AIndexSubordLookup(null, null);} + return null; + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AIndexSubordLookup(result, null);} + return result; + } + + public String ToQueryPlan() { + return this.GetType().Name; + } + + public LookupStrategyDesc StrategyDesc + { + get { return new LookupStrategyDesc(LookupStrategyType.FULLTABLESCAN, null); } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordFullTableScanLookupStrategyFactory.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordFullTableScanLookupStrategyFactory.cs new file mode 100755 index 000000000..31aa1dc64 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordFullTableScanLookupStrategyFactory.cs @@ -0,0 +1,31 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.join.table; +using com.espertech.esper.epl.virtualdw; + +namespace com.espertech.esper.epl.lookup +{ + /// + /// Factory for lookup on an unindexed table returning the full table as matching events. + /// + public class SubordFullTableScanLookupStrategyFactory : SubordTableLookupStrategyFactory + { + public SubordTableLookupStrategy MakeStrategy(EventTable[] eventTable, VirtualDWView vdw) + { + return new SubordFullTableScanLookupStrategy((UnindexedEventTable)eventTable[0]); + } + + public String ToQueryPlan() + { + return GetType().Name; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordFullTableScanLookupStrategyLocking.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordFullTableScanLookupStrategyLocking.cs new file mode 100755 index 000000000..31b17ff8f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordFullTableScanLookupStrategyLocking.cs @@ -0,0 +1,69 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.threading; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.lookup +{ + /// + /// MapIndex lookup strategy for subqueries. + /// + public class SubordFullTableScanLookupStrategyLocking : SubordTableLookupStrategy + { + private readonly IEnumerable _contents; + private readonly IReaderWriterLock _statementLock; + + public SubordFullTableScanLookupStrategyLocking(IEnumerable contents, IReaderWriterLock statementLock) + { + _contents = contents; + _statementLock = statementLock; + } + + public ICollection Lookup(EventBean[] events, ExprEvaluatorContext context) + { + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().QIndexSubordLookup(this, null, null); + ICollection result = LookupInternal(); + InstrumentationHelper.Get().AIndexSubordLookup(result, null); + return result; + } + return LookupInternal(); + } + + private ICollection LookupInternal() + { + using(_statementLock.AcquireReadLock()) + { + var result = new ArrayDeque(); + foreach (EventBean eventBean in _contents) + { + result.Add(eventBean); + } + return result; + } + } + + public LookupStrategyDesc StrategyDesc + { + get { return new LookupStrategyDesc(LookupStrategyType.FULLTABLESCAN, null); } + } + + public String ToQueryPlan() + { + return GetType().Name; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordFullTableScanTableLookupStrategy.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordFullTableScanTableLookupStrategy.cs new file mode 100755 index 000000000..f799c4c2f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordFullTableScanTableLookupStrategy.cs @@ -0,0 +1,74 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.threading; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.table.strategy; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.lookup +{ + /// + /// Index lookup strategy for tables. + /// + public class SubordFullTableScanTableLookupStrategy : SubordTableLookupStrategy + { + private readonly ILockable _tableLevelLock; + private readonly IEnumerable _contents; + + public SubordFullTableScanTableLookupStrategy(ILockable tableLevelLock, IEnumerable contents) + { + _tableLevelLock = tableLevelLock; + _contents = contents; + } + + public ICollection Lookup(EventBean[] events, ExprEvaluatorContext context) + { + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().QIndexSubordLookup(this, null, null); + ICollection result = LookupInternal(context); + InstrumentationHelper.Get().AIndexSubordLookup(result, null); + return result; + } + return LookupInternal(context); + } + + private ICollection LookupInternal(ExprEvaluatorContext context) + { + ExprTableEvalLockUtil.ObtainLockUnless(_tableLevelLock, context); + + var it = _contents.GetEnumerator(); + if (!it.MoveNext()) { + return null; + } + + var result = new ArrayDeque(2); + do + { + result.Add(it.Current); + } while (it.MoveNext()); + + return result; + } + + public LookupStrategyDesc StrategyDesc + { + get { return new LookupStrategyDesc(LookupStrategyType.FULLTABLESCAN, null); } + } + + public string ToQueryPlan() + { + return GetType().Name; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordInKeywordMultiTableLookupStrategy.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordInKeywordMultiTableLookupStrategy.cs new file mode 100755 index 000000000..38791e93c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordInKeywordMultiTableLookupStrategy.cs @@ -0,0 +1,62 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.@join.plan; +using com.espertech.esper.epl.@join.table; + +namespace com.espertech.esper.epl.lookup +{ + /// Index lookup strategy for subqueries. + public class SubordInKeywordMultiTableLookupStrategy : SubordTableLookupStrategy + { + private readonly ExprEvaluator _evaluator; + private readonly EventBean[] _events; + + /// Index to look up in. + private readonly PropertyIndexedEventTableSingle[] _indexes; + + private readonly LookupStrategyDesc strategyDesc; + + public SubordInKeywordMultiTableLookupStrategy( + int numStreamsOuter, + ExprEvaluator evaluator, + EventTable[] tables, + LookupStrategyDesc strategyDesc) + { + _evaluator = evaluator; + this.strategyDesc = strategyDesc; + _events = new EventBean[numStreamsOuter + 1]; + _indexes = new PropertyIndexedEventTableSingle[tables.Length]; + for (int i = 0; i < tables.Length; i++) + { + _indexes[i] = (PropertyIndexedEventTableSingle) tables[i]; + } + } + + public ICollection Lookup(EventBean[] eventsPerStream, ExprEvaluatorContext context) + { + Array.Copy(eventsPerStream, 0, _events, 1, eventsPerStream.Length); + return InKeywordTableLookupUtil.MultiIndexLookup(_evaluator, _events, context, _indexes); + } + + public LookupStrategyDesc StrategyDesc + { + get { return strategyDesc; } + } + + public string ToQueryPlan() + { + return GetType().Name; + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordInKeywordMultiTableLookupStrategyFactory.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordInKeywordMultiTableLookupStrategyFactory.cs new file mode 100755 index 000000000..da6bd96d1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordInKeywordMultiTableLookupStrategyFactory.cs @@ -0,0 +1,52 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.@join.table; +using com.espertech.esper.epl.virtualdw; + +namespace com.espertech.esper.epl.lookup +{ + /// + /// Index lookup strategy for subqueries. + /// + public class SubordInKeywordMultiTableLookupStrategyFactory : SubordTableLookupStrategyFactory + { + protected readonly ExprEvaluator Evaluator; + protected bool IsNWOnTrigger; + protected int StreamCountOuter; + protected readonly LookupStrategyDesc StrategyDesc; + + public SubordInKeywordMultiTableLookupStrategyFactory(bool isNWOnTrigger, int streamCountOuter, ExprNode exprNode) + { + StreamCountOuter = streamCountOuter; + Evaluator = exprNode.ExprEvaluator; + IsNWOnTrigger = isNWOnTrigger; + StrategyDesc = new LookupStrategyDesc(LookupStrategyType.INKEYWORDMULTIIDX, new String[] {ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(exprNode)}); + } + + public SubordTableLookupStrategy MakeStrategy(EventTable[] eventTable, VirtualDWView vdw) + { + if (IsNWOnTrigger) + { + return new SubordInKeywordMultiTableLookupStrategyNW(Evaluator, eventTable, StrategyDesc); + } + else + { + return new SubordInKeywordMultiTableLookupStrategy(StreamCountOuter, Evaluator, eventTable, StrategyDesc); + } + } + + public String ToQueryPlan() + { + return this.GetType().Name; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordInKeywordMultiTableLookupStrategyNW.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordInKeywordMultiTableLookupStrategyNW.cs new file mode 100755 index 000000000..0a9affd83 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordInKeywordMultiTableLookupStrategyNW.cs @@ -0,0 +1,59 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.@join.plan; +using com.espertech.esper.epl.@join.table; + +namespace com.espertech.esper.epl.lookup +{ + /// + /// Index lookup strategy for subqueries for in-keyword single-index sided. + /// + public class SubordInKeywordMultiTableLookupStrategyNW : SubordTableLookupStrategy + { + private readonly ExprEvaluator _evaluator; + + /// Index to look up in. + private readonly PropertyIndexedEventTableSingle[] _indexes; + + private readonly LookupStrategyDesc _strategyDesc; + + public SubordInKeywordMultiTableLookupStrategyNW( + ExprEvaluator evaluator, + EventTable[] tables, + LookupStrategyDesc strategyDesc) + { + _evaluator = evaluator; + _indexes = new PropertyIndexedEventTableSingle[tables.Length]; + for (int i = 0; i < tables.Length; i++) + { + _indexes[i] = (PropertyIndexedEventTableSingle) tables[i]; + } + _strategyDesc = strategyDesc; + } + + public ICollection Lookup(EventBean[] eventsPerStream, ExprEvaluatorContext context) + { + return InKeywordTableLookupUtil.MultiIndexLookup(_evaluator, eventsPerStream, context, _indexes); + } + + public LookupStrategyDesc StrategyDesc + { + get { return _strategyDesc; } + } + + public string ToQueryPlan() + { + return GetType().Name; + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordInKeywordSingleTableLookupStrategy.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordInKeywordSingleTableLookupStrategy.cs new file mode 100755 index 000000000..f61ab6fb7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordInKeywordSingleTableLookupStrategy.cs @@ -0,0 +1,72 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.@join.plan; +using com.espertech.esper.epl.@join.table; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.lookup +{ + /// + /// Index lookup strategy for subqueries. + /// + public class SubordInKeywordSingleTableLookupStrategy : SubordTableLookupStrategy + { + /// Stream numbers to get key values from. + protected readonly ExprEvaluator[] Evaluators; + + private readonly EventBean[] _events; + + private readonly LookupStrategyDesc _strategyDesc; + + /// Index to look up in. + private PropertyIndexedEventTableSingle _index; + + public SubordInKeywordSingleTableLookupStrategy(int streamCountOuter, ExprEvaluator[] evaluators, PropertyIndexedEventTableSingle index, LookupStrategyDesc strategyDesc) + { + Evaluators = evaluators; + _index = index; + _events = new EventBean[streamCountOuter+1]; + _strategyDesc = strategyDesc; + } + + /// Returns index to look up in. + /// index to use + public PropertyIndexedEventTableSingle Index + { + get { return _index; } + internal set { _index = value; } + } + + public ICollection Lookup(EventBean[] eventsPerStream, ExprEvaluatorContext context) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QIndexSubordLookup(this, _index, null);} + + Array.Copy(eventsPerStream, 0, _events, 1, eventsPerStream.Length); + ICollection result = InKeywordTableLookupUtil.SingleIndexLookup(Evaluators, _events, context, _index); + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AIndexSubordLookup(result, null);} + return result; + } + + public LookupStrategyDesc StrategyDesc + { + get { return _strategyDesc; } + } + + public String ToQueryPlan() + { + return this.GetType().Name; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordInKeywordSingleTableLookupStrategyFactory.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordInKeywordSingleTableLookupStrategyFactory.cs new file mode 100755 index 000000000..549e35163 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordInKeywordSingleTableLookupStrategyFactory.cs @@ -0,0 +1,52 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.@join.table; +using com.espertech.esper.epl.virtualdw; + +namespace com.espertech.esper.epl.lookup +{ + /// + /// Index lookup strategy for subqueries. + /// + public class SubordInKeywordSingleTableLookupStrategyFactory : SubordTableLookupStrategyFactory + { + protected readonly ExprEvaluator[] Evaluators; + protected bool IsNWOnTrigger; + protected int StreamCountOuter; + protected readonly LookupStrategyDesc StrategyDesc; + + public SubordInKeywordSingleTableLookupStrategyFactory(bool isNWOnTrigger, int streamCountOuter, ExprNode[] exprNodes) + { + StreamCountOuter = streamCountOuter; + Evaluators = ExprNodeUtility.GetEvaluators(exprNodes); + IsNWOnTrigger = isNWOnTrigger; + StrategyDesc = new LookupStrategyDesc(LookupStrategyType.INKEYWORDSINGLEIDX, ExprNodeUtility.ToExpressionStringsMinPrecedence(exprNodes)); + } + + public SubordTableLookupStrategy MakeStrategy(EventTable[] eventTable, VirtualDWView vdw) + { + if (IsNWOnTrigger) + { + return new SubordInKeywordSingleTableLookupStrategyNW(Evaluators, (PropertyIndexedEventTableSingle) eventTable[0], StrategyDesc); + } + else + { + return new SubordInKeywordSingleTableLookupStrategy(StreamCountOuter, Evaluators, (PropertyIndexedEventTableSingle) eventTable[0], StrategyDesc); + } + } + + public String ToQueryPlan() + { + return GetType().Name; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordInKeywordSingleTableLookupStrategyNW.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordInKeywordSingleTableLookupStrategyNW.cs new file mode 100755 index 000000000..0ad02bca4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordInKeywordSingleTableLookupStrategyNW.cs @@ -0,0 +1,70 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.@join.plan; +using com.espertech.esper.epl.@join.table; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.lookup +{ + /// + /// Index lookup strategy for subqueries for in-keyword single-index sided. + /// + public class SubordInKeywordSingleTableLookupStrategyNW : SubordTableLookupStrategy + { + /// Index to look up in. + private readonly PropertyIndexedEventTableSingle _index; + + private readonly ExprEvaluator[] _evaluators; + + private readonly LookupStrategyDesc _strategyDesc; + + public SubordInKeywordSingleTableLookupStrategyNW(ExprEvaluator[] evaluators, PropertyIndexedEventTableSingle index, LookupStrategyDesc strategyDesc) + { + _evaluators = evaluators; + _index = index; + _strategyDesc = strategyDesc; + } + + /// + /// Returns index to look up in. + /// + /// index to use + public PropertyIndexedEventTableSingle Index + { + get { return _index; } + } + + public ICollection Lookup(EventBean[] eventsPerStream, ExprEvaluatorContext context) + { + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().QIndexSubordLookup(this, _index, null); + } + + var result = InKeywordTableLookupUtil.SingleIndexLookup(_evaluators, eventsPerStream, context, _index); + + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().AIndexSubordLookup(result, null); + } + return result; + } + + public LookupStrategyDesc StrategyDesc + { + get { return _strategyDesc; } + } + + public string ToQueryPlan() { + return GetType().Name + " evaluators " + ExprNodeUtility.PrintEvaluators(_evaluators); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordIndexedTableLookupStrategyCoercing.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordIndexedTableLookupStrategyCoercing.cs new file mode 100755 index 000000000..6351c5edb --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordIndexedTableLookupStrategyCoercing.cs @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.join.table; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.lookup +{ + /// + /// MapIndex lookup strategy that coerces the key values before performing a lookup. + /// + public class SubordIndexedTableLookupStrategyCoercing : SubordIndexedTableLookupStrategyExpr + { + private readonly Type[] _coercionTypes; + + public SubordIndexedTableLookupStrategyCoercing(int numStreamsOuter, ExprEvaluator[] evaluators, PropertyIndexedEventTable index, Type[] coercionTypes, LookupStrategyDesc strategyDesc) + : base(numStreamsOuter, evaluators, index, strategyDesc) + { + _coercionTypes = coercionTypes; + } + + protected override Object[] GetKeys(EventBean[] eventsPerStream, ExprEvaluatorContext context) + { + var keys = base.GetKeys(eventsPerStream, context); + for (int i = 0; i < keys.Length; i++) + { + var value = keys[i]; + + var coercionType = _coercionTypes[i]; + if ((value != null) && (!(value.GetType() == coercionType))) + { + if (value.IsNumber()) + { + value = CoercerFactory.CoerceBoxed(value, _coercionTypes[i]); + } + keys[i] = value; + } + } + return keys; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordIndexedTableLookupStrategyCoercingFactory.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordIndexedTableLookupStrategyCoercingFactory.cs new file mode 100755 index 000000000..75eddae54 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordIndexedTableLookupStrategyCoercingFactory.cs @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.epl.join.table; +using com.espertech.esper.epl.virtualdw; + +namespace com.espertech.esper.epl.lookup +{ + /// + /// MapIndex lookup strategy that coerces the key values before performing a lookup. + /// + public class SubordIndexedTableLookupStrategyCoercingFactory : SubordIndexedTableLookupStrategyExprFactory + { + private readonly Type[] _coercionTypes; + + public SubordIndexedTableLookupStrategyCoercingFactory(bool isNWOnTrigger, int numStreamsOuter, IList hashKeys, Type[] coercionTypes) + : base(isNWOnTrigger, numStreamsOuter, hashKeys) + { + _coercionTypes = coercionTypes; + } + + public override SubordTableLookupStrategy MakeStrategy(EventTable[] eventTable, VirtualDWView vdw) + { + if (IsNWOnTrigger) { + return new SubordIndexedTableLookupStrategyCoercingNW(Evaluators, (PropertyIndexedEventTable) eventTable[0], _coercionTypes, StrategyDesc); + } + else { + return new SubordIndexedTableLookupStrategyCoercing(NumStreamsOuter, Evaluators, (PropertyIndexedEventTable)eventTable[0], _coercionTypes, StrategyDesc); + } + } + + } +} diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordIndexedTableLookupStrategyCoercingNW.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordIndexedTableLookupStrategyCoercingNW.cs new file mode 100755 index 000000000..7a1bd32c6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordIndexedTableLookupStrategyCoercingNW.cs @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.join.table; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.lookup +{ + /// + /// MapIndex lookup strategy that coerces the key values before performing a lookup. + /// + public class SubordIndexedTableLookupStrategyCoercingNW : SubordIndexedTableLookupStrategyExprNW + { + private readonly Type[] _coercionTypes; + + public SubordIndexedTableLookupStrategyCoercingNW(ExprEvaluator[] evaluators, PropertyIndexedEventTable index, Type[] coercionTypes, LookupStrategyDesc strategyDesc) + : base(evaluators, index, strategyDesc) + { + _coercionTypes = coercionTypes; + } + + protected override Object[] GetKeys(EventBean[] eventsPerStream, ExprEvaluatorContext context) + { + var keys = base.GetKeys(eventsPerStream, context); + for (var i = 0; i < keys.Length; i++) + { + var value = keys[i]; + + var coercionType = _coercionTypes[i]; + if ((value != null) && (value.GetType() != coercionType)) + { + if (value.IsNumber()) + { + value = CoercerFactory.CoerceBoxed(value, _coercionTypes[i]); + } + keys[i] = value; + } + } + return keys; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordIndexedTableLookupStrategyExpr.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordIndexedTableLookupStrategyExpr.cs new file mode 100755 index 000000000..a094fff2e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordIndexedTableLookupStrategyExpr.cs @@ -0,0 +1,87 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.join.table; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.lookup +{ + /// Index lookup strategy for subqueries. + public class SubordIndexedTableLookupStrategyExpr : SubordTableLookupStrategy + { + /// Index to look up in. + private readonly PropertyIndexedEventTable _index; + + private readonly ExprEvaluator[] _evaluators; + private readonly LookupStrategyDesc _strategyDesc; + private readonly EventBean[] _events; + + public SubordIndexedTableLookupStrategyExpr(int numStreamsOuter, ExprEvaluator[] evaluators, PropertyIndexedEventTable index, LookupStrategyDesc strategyDesc) { + _evaluators = evaluators; + _strategyDesc = strategyDesc; + _events = new EventBean[numStreamsOuter + 1]; + _index = index; + } + + /// + /// Returns index to look up in. + /// + /// index to use + public PropertyIndexedEventTable Index + { + get { return _index; } + } + + public ICollection Lookup(EventBean[] eventsPerStream, ExprEvaluatorContext context) { + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().QIndexSubordLookup(this, _index, null); + } + + Object[] keys = GetKeys(eventsPerStream, context); + + if (InstrumentationHelper.ENABLED) { + ISet result = _index.Lookup(keys); + InstrumentationHelper.Get().AIndexSubordLookup(result, keys); + return result; + } + return Index.Lookup(keys); + } + + /// + /// Get the index lookup keys. + /// + /// is the events for each stream + /// context + /// key object + protected virtual Object[] GetKeys(EventBean[] eventsPerStream, ExprEvaluatorContext context) { + Array.Copy(eventsPerStream, 0, _events, 1, eventsPerStream.Length); + var keyValues = new Object[_evaluators.Length]; + var evaluateParams = new EvaluateParams(_events, true, context); + for (int i = 0; i < _evaluators.Length; i++) + { + keyValues[i] = _evaluators[i].Evaluate(evaluateParams); + } + return keyValues; + } + + public LookupStrategyDesc StrategyDesc + { + get { return _strategyDesc; } + } + + public string ToQueryPlan() + { + return this.GetType().Name + " evaluators " + ExprNodeUtility.PrintEvaluators(_evaluators); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordIndexedTableLookupStrategyExprFactory.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordIndexedTableLookupStrategyExprFactory.cs new file mode 100755 index 000000000..d33506beb --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordIndexedTableLookupStrategyExprFactory.cs @@ -0,0 +1,79 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.@join.table; +using com.espertech.esper.epl.virtualdw; + +namespace com.espertech.esper.epl.lookup +{ + /// + /// MapIndex lookup strategy for subqueries. + /// + public class SubordIndexedTableLookupStrategyExprFactory : SubordTableLookupStrategyFactory + { + private readonly ExprEvaluator[] _evaluators; + private readonly bool _isNWOnTrigger; + private readonly int _numStreamsOuter; + private readonly LookupStrategyDesc _strategyDesc; + + public SubordIndexedTableLookupStrategyExprFactory(bool isNWOnTrigger, int numStreamsOuter, IList hashKeys) + { + _evaluators = new ExprEvaluator[hashKeys.Count]; + var expressions = new String[_evaluators.Length]; + for (int i = 0; i < hashKeys.Count; i++) + { + _evaluators[i] = hashKeys[i].HashKey.KeyExpr.ExprEvaluator; + expressions[i] = hashKeys[i].HashKey.KeyExpr.ToExpressionStringMinPrecedenceSafe(); + } + _isNWOnTrigger = isNWOnTrigger; + _numStreamsOuter = numStreamsOuter; + _strategyDesc = new LookupStrategyDesc(LookupStrategyType.MULTIEXPR, expressions); + } + + public virtual SubordTableLookupStrategy MakeStrategy(EventTable[] eventTable, VirtualDWView vdw) + { + if (_isNWOnTrigger) + { + return new SubordIndexedTableLookupStrategyExprNW(_evaluators, (PropertyIndexedEventTable) eventTable[0], _strategyDesc); + } + else + { + return new SubordIndexedTableLookupStrategyExpr(_numStreamsOuter, _evaluators, (PropertyIndexedEventTable)eventTable[0], _strategyDesc); + } + } + + public String ToQueryPlan() + { + return GetType().FullName + " evaluators " + ExprNodeUtility.PrintEvaluators(_evaluators); + } + + protected bool IsNWOnTrigger + { + get { return _isNWOnTrigger; } + } + + protected int NumStreamsOuter + { + get { return _numStreamsOuter; } + } + + protected LookupStrategyDesc StrategyDesc + { + get { return _strategyDesc; } + } + + protected ExprEvaluator[] Evaluators + { + get { return _evaluators; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordIndexedTableLookupStrategyExprNW.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordIndexedTableLookupStrategyExprNW.cs new file mode 100755 index 000000000..0f75f834c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordIndexedTableLookupStrategyExprNW.cs @@ -0,0 +1,86 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.@join.table; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.lookup +{ + /// Index lookup strategy for subqueries. + public class SubordIndexedTableLookupStrategyExprNW : SubordTableLookupStrategy + { + /// Index to look up in. + private readonly PropertyIndexedEventTable _index; + + private readonly ExprEvaluator[] _evaluators; + + private readonly LookupStrategyDesc _strategyDesc; + + public SubordIndexedTableLookupStrategyExprNW(ExprEvaluator[] evaluators, PropertyIndexedEventTable index, LookupStrategyDesc strategyDesc) { + _evaluators = evaluators; + _index = index; + _strategyDesc = strategyDesc; + } + + /// + /// Returns index to look up in. + /// + /// index to use + public PropertyIndexedEventTable Index + { + get { return _index; } + } + + public ICollection Lookup(EventBean[] eventsPerStream, ExprEvaluatorContext context) { + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().QIndexSubordLookup(this, _index, null); + } + + Object[] keys = GetKeys(eventsPerStream, context); + + if (InstrumentationHelper.ENABLED) { + ISet result = _index.Lookup(keys); + InstrumentationHelper.Get().AIndexSubordLookup(result, keys); + return result; + } + return _index.Lookup(keys); + } + + /// + /// Get the index lookup keys. + /// + /// is the events for each stream + /// context + /// key object + protected virtual Object[] GetKeys(EventBean[] eventsPerStream, ExprEvaluatorContext context) + { + var keyValues = new Object[_evaluators.Length]; + var evaluateParams = new EvaluateParams(eventsPerStream, true, context); + for (int i = 0; i < _evaluators.Length; i++) + { + keyValues[i] = _evaluators[i].Evaluate(evaluateParams); + } + return keyValues; + } + + public LookupStrategyDesc StrategyDesc + { + get { return _strategyDesc; } + } + + public string ToQueryPlan() + { + return GetType().Name + " evaluators " + ExprNodeUtility.PrintEvaluators(_evaluators); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordIndexedTableLookupStrategyLocking.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordIndexedTableLookupStrategyLocking.cs new file mode 100755 index 000000000..b967fd39b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordIndexedTableLookupStrategyLocking.cs @@ -0,0 +1,57 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.threading; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.lookup +{ + /// + /// MapIndex lookup strategy for subqueries. + /// + public class SubordIndexedTableLookupStrategyLocking : SubordTableLookupStrategy + { + private readonly SubordTableLookupStrategy _inner; + private readonly IReaderWriterLock _statementLock; + + public SubordIndexedTableLookupStrategyLocking(SubordTableLookupStrategy inner, IReaderWriterLock statementLock) + { + _inner = inner; + _statementLock = statementLock; + } + + public ICollection Lookup(EventBean[] events, ExprEvaluatorContext context) + { + using(_statementLock.AcquireReadLock()) + { + ICollection result = _inner.Lookup(events, context); + if (result != null) { + return new ArrayDeque(result); + } + else { + return Collections.GetEmptySet(); + } + } + } + + public LookupStrategyDesc StrategyDesc + { + get { return _inner.StrategyDesc; } + } + + public String ToQueryPlan() + { + return GetType().Name + " inner " + _inner.ToQueryPlan(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordIndexedTableLookupStrategyProp.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordIndexedTableLookupStrategyProp.cs new file mode 100755 index 000000000..c32e9a040 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordIndexedTableLookupStrategyProp.cs @@ -0,0 +1,96 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.@join.table; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.lookup +{ + /// + /// Index lookup strategy for subqueries. + /// + public class SubordIndexedTableLookupStrategyProp : SubordTableLookupStrategy + { + /// Stream numbers to get key values from. + private readonly int[] _keyStreamNums; + + /// Getters to use to get key values. + private readonly EventPropertyGetter[] _propertyGetters; + + /// MapIndex to look up in. + private readonly PropertyIndexedEventTable _index; + + private readonly LookupStrategyDesc _strategyDesc; + + public SubordIndexedTableLookupStrategyProp(int[] keyStreamNums, EventPropertyGetter[] propertyGetters, PropertyIndexedEventTable index, LookupStrategyDesc strategyDesc) + { + _keyStreamNums = keyStreamNums; + _propertyGetters = propertyGetters; + _index = index; + _strategyDesc = strategyDesc; + } + + /// + /// Returns index to look up in. + /// + /// index to use + public PropertyIndexedEventTable Index + { + get { return _index; } + } + + public ICollection Lookup(EventBean[] eventsPerStream, ExprEvaluatorContext context) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QIndexSubordLookup(this, _index, _keyStreamNums);} + + Object[] keys = GetKeys(eventsPerStream); + + if (InstrumentationHelper.ENABLED) { + ISet result = _index.Lookup(keys); + InstrumentationHelper.Get().AIndexSubordLookup(result, keys); + return result; + } + return _index.Lookup(keys); + } + + /// Get the index lookup keys. + /// is the events for each stream + /// key object + protected Object[] GetKeys(EventBean[] eventsPerStream) + { + Object[] keyValues = new Object[_propertyGetters.Length]; + for (int i = 0; i < _propertyGetters.Length; i++) + { + int streamNum = _keyStreamNums[i]; + EventBean theEvent = eventsPerStream[streamNum]; + keyValues[i] = _propertyGetters[i].Get(theEvent); + } + return keyValues; + } + + public override String ToString() + { + return ToQueryPlan(); + } + + public LookupStrategyDesc StrategyDesc + { + get { return _strategyDesc; } + } + + public String ToQueryPlan() { + return GetType().FullName + " keyStreamNums=" + _keyStreamNums.Render(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordIndexedTableLookupStrategyPropFactory.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordIndexedTableLookupStrategyPropFactory.cs new file mode 100755 index 000000000..5867f2c18 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordIndexedTableLookupStrategyPropFactory.cs @@ -0,0 +1,80 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.@join.table; +using com.espertech.esper.epl.virtualdw; + +namespace com.espertech.esper.epl.lookup +{ + /// Index lookup strategy for subqueries. + public class SubordIndexedTableLookupStrategyPropFactory : SubordTableLookupStrategyFactory + { + private readonly string[] _properties; + + /// Stream numbers to get key values from. + private readonly int[] _keyStreamNums; + + /// Getters to use to get key values. + private readonly EventPropertyGetter[] _propertyGetters; + + private readonly LookupStrategyDesc _strategyDesc; + + /// + /// Ctor. + /// + /// is the event types per stream + /// is the stream number per property + /// is the key properties + /// indicator whether named window trigger + public SubordIndexedTableLookupStrategyPropFactory(bool isNWOnTrigger, EventType[] eventTypes, int[] keyStreamNumbers, string[] properties) + { + _keyStreamNums = keyStreamNumbers; + _properties = properties; + _strategyDesc = new LookupStrategyDesc(LookupStrategyType.MULTIPROP, properties); + + _propertyGetters = new EventPropertyGetter[properties.Length]; + for (int i = 0; i < keyStreamNumbers.Length; i++) { + int streamNumber = keyStreamNumbers[i]; + string property = properties[i]; + EventType eventType = eventTypes[streamNumber]; + _propertyGetters[i] = eventType.GetGetter(property); + + if (_propertyGetters[i] == null) { + throw new ArgumentException("Property named '" + properties[i] + "' is invalid for type " + eventType); + } + } + + for (int i = 0; i < _keyStreamNums.Length; i++) { + _keyStreamNums[i] += isNWOnTrigger ? 1 : 0; // for on-trigger the key will be provided in a {1,2,...} stream and not {0,...} + } + } + + public SubordTableLookupStrategy MakeStrategy(EventTable[] eventTable, VirtualDWView vdw) { + return new SubordIndexedTableLookupStrategyProp(_keyStreamNums, _propertyGetters, (PropertyIndexedEventTable) eventTable[0], _strategyDesc); + } + + /// + /// Returns properties to use from lookup event to look up in index. + /// + /// properties to use from lookup event + public string[] Properties + { + get { return _properties; } + } + + public string ToQueryPlan() { + return GetType().Name + + " indexProps=" + CompatExtensions.Render(_properties) + + " keyStreamNums=" + CompatExtensions.Render(_keyStreamNums); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordIndexedTableLookupStrategySingleCoercing.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordIndexedTableLookupStrategySingleCoercing.cs new file mode 100755 index 000000000..0d075ec91 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordIndexedTableLookupStrategySingleCoercing.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.@join.table; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.lookup +{ + /// + /// MapIndex lookup strategy that coerces the key values before performing a lookup. + /// + public class SubordIndexedTableLookupStrategySingleCoercing : SubordIndexedTableLookupStrategySingleExpr + { + private readonly Type _coercionType; + + public SubordIndexedTableLookupStrategySingleCoercing(int streamCountOuter, ExprEvaluator evaluator, PropertyIndexedEventTableSingle index, Type coercionType, LookupStrategyDesc strategyDesc) + : base(streamCountOuter, evaluator, index, strategyDesc) + { + _coercionType = coercionType; + } + + protected override Object GetKey(EventBean[] eventsPerStream, ExprEvaluatorContext context) + { + Object key = base.GetKey(eventsPerStream, context); + return EventBeanUtility.Coerce(key, _coercionType); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordIndexedTableLookupStrategySingleCoercingFactory.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordIndexedTableLookupStrategySingleCoercingFactory.cs new file mode 100755 index 000000000..7f3a01071 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordIndexedTableLookupStrategySingleCoercingFactory.cs @@ -0,0 +1,42 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.@join.table; +using com.espertech.esper.epl.virtualdw; + +namespace com.espertech.esper.epl.lookup +{ + /// + /// MapIndex lookup strategy that coerces the key values before performing a lookup. + /// + public class SubordIndexedTableLookupStrategySingleCoercingFactory : SubordIndexedTableLookupStrategySingleExprFactory + { + private readonly Type _coercionType; + + /// Ctor. + public SubordIndexedTableLookupStrategySingleCoercingFactory(bool isNWOnTrigger, int streamCountOuter, SubordPropHashKey hashKey, Type coercionType) + : base(isNWOnTrigger, streamCountOuter, hashKey) + { + _coercionType = coercionType; + } + + public override SubordTableLookupStrategy MakeStrategy(EventTable[] eventTable, VirtualDWView vdw) + { + if (IsNWOnTrigger) + { + return new SubordIndexedTableLookupStrategySingleCoercingNW(Evaluator, (PropertyIndexedEventTableSingle)eventTable[0], _coercionType, StrategyDesc); + } + else + { + return new SubordIndexedTableLookupStrategySingleCoercing(StreamCountOuter, Evaluator, (PropertyIndexedEventTableSingle)eventTable[0], _coercionType, StrategyDesc); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordIndexedTableLookupStrategySingleCoercingNW.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordIndexedTableLookupStrategySingleCoercingNW.cs new file mode 100755 index 000000000..381e63f95 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordIndexedTableLookupStrategySingleCoercingNW.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.@join.table; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.lookup +{ + /// + /// Index lookup strategy that coerces the key values before performing a lookup. + /// + public class SubordIndexedTableLookupStrategySingleCoercingNW : SubordIndexedTableLookupStrategySingleExprNW + { + private readonly Type _coercionType; + + public SubordIndexedTableLookupStrategySingleCoercingNW(ExprEvaluator evaluator, PropertyIndexedEventTableSingle index, Type coercionType, LookupStrategyDesc strategyDesc) + : base(evaluator, index, strategyDesc) + { + _coercionType = coercionType; + } + + protected override Object GetKey(EventBean[] eventsPerStream, ExprEvaluatorContext context) + { + Object key = base.GetKey(eventsPerStream, context); + return EventBeanUtility.Coerce(key, _coercionType); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordIndexedTableLookupStrategySingleExpr.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordIndexedTableLookupStrategySingleExpr.cs new file mode 100755 index 000000000..42d838738 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordIndexedTableLookupStrategySingleExpr.cs @@ -0,0 +1,85 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.@join.table; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.lookup +{ + /// + /// MapIndex lookup strategy for subqueries. + /// + public class SubordIndexedTableLookupStrategySingleExpr : SubordTableLookupStrategy + { + /// Stream numbers to get key values from. + protected readonly ExprEvaluator Evaluator; + + private readonly EventBean[] _events; + + private readonly LookupStrategyDesc _strategyDesc; + + /// MapIndex to look up in. + protected readonly PropertyIndexedEventTableSingle Index; + + public SubordIndexedTableLookupStrategySingleExpr(int streamCountOuter, ExprEvaluator evaluator, PropertyIndexedEventTableSingle index, LookupStrategyDesc strategyDesc) + { + Evaluator = evaluator; + Index = index; + _events = new EventBean[streamCountOuter+1]; + _strategyDesc = strategyDesc; + } + + /// Returns index to look up in. + /// index to use + public PropertyIndexedEventTableSingle GetIndex() + { + return Index; + } + + public ICollection Lookup(EventBean[] eventsPerStream, ExprEvaluatorContext context) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QIndexSubordLookup(this, Index, null); } + + Object key = GetKey(eventsPerStream, context); + + if (InstrumentationHelper.ENABLED) { + ISet result = Index.Lookup(key); + InstrumentationHelper.Get().AIndexSubordLookup(result, key); + return result; + } + return Index.Lookup(key); + } + + /// + /// Get the index lookup keys. + /// + /// is the events for each stream + /// The context. + /// key object + protected virtual Object GetKey(EventBean[] eventsPerStream, ExprEvaluatorContext context) + { + Array.Copy(eventsPerStream, 0, _events, 1, eventsPerStream.Length); + return Evaluator.Evaluate(new EvaluateParams(_events, true, context)); + } + + public LookupStrategyDesc StrategyDesc + { + get { return _strategyDesc; } + } + + public String ToQueryPlan() + { + return GetType().FullName + " evaluator " + Evaluator.GetType().Name; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordIndexedTableLookupStrategySingleExprFactory.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordIndexedTableLookupStrategySingleExprFactory.cs new file mode 100755 index 000000000..2fe59f0fb --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordIndexedTableLookupStrategySingleExprFactory.cs @@ -0,0 +1,54 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.@join.table; +using com.espertech.esper.epl.virtualdw; + +namespace com.espertech.esper.epl.lookup +{ + /// + /// MapIndex lookup strategy for subqueries. + /// + public class SubordIndexedTableLookupStrategySingleExprFactory : SubordTableLookupStrategyFactory + { + protected readonly ExprEvaluator Evaluator; + protected bool IsNWOnTrigger; + protected int StreamCountOuter; + protected readonly LookupStrategyDesc StrategyDesc; + + public SubordIndexedTableLookupStrategySingleExprFactory(bool isNWOnTrigger, int streamCountOuter, SubordPropHashKey hashKey) + { + StreamCountOuter = streamCountOuter; + Evaluator = hashKey.HashKey.KeyExpr.ExprEvaluator; + IsNWOnTrigger = isNWOnTrigger; + StrategyDesc = new LookupStrategyDesc(LookupStrategyType.SINGLEEXPR, new String[] { hashKey.HashKey.KeyExpr.ToExpressionStringMinPrecedenceSafe() }); + } + + public virtual SubordTableLookupStrategy MakeStrategy(EventTable[] eventTable, VirtualDWView vdw) + { + if (IsNWOnTrigger) + { + return new SubordIndexedTableLookupStrategySingleExprNW( + Evaluator, (PropertyIndexedEventTableSingle) eventTable[0], StrategyDesc); + } + else + { + return new SubordIndexedTableLookupStrategySingleExpr( + StreamCountOuter, Evaluator, (PropertyIndexedEventTableSingle) eventTable[0], StrategyDesc); + } + } + + public String ToQueryPlan() + { + return GetType().FullName + " evaluator " + Evaluator.GetType().Name; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordIndexedTableLookupStrategySingleExprNW.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordIndexedTableLookupStrategySingleExprNW.cs new file mode 100755 index 000000000..29ad495fc --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordIndexedTableLookupStrategySingleExprNW.cs @@ -0,0 +1,82 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.@join.table; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.lookup +{ + /// + /// MapIndex lookup strategy for subqueries. + /// + public class SubordIndexedTableLookupStrategySingleExprNW : SubordTableLookupStrategy + { + /// Stream numbers to get key values from. + protected readonly ExprEvaluator Evaluator; + + /// MapIndex to look up in. + private readonly PropertyIndexedEventTableSingle _index; + + private readonly LookupStrategyDesc _strategyDesc; + + public SubordIndexedTableLookupStrategySingleExprNW(ExprEvaluator evaluator, PropertyIndexedEventTableSingle index, LookupStrategyDesc strategyDesc) + { + Evaluator = evaluator; + _index = index; + _strategyDesc = strategyDesc; + } + + /// Returns index to look up in. + /// index to use + public virtual PropertyIndexedEventTableSingle Index + { + get { return _index; } + } + + public virtual ICollection Lookup(EventBean[] eventsPerStream, ExprEvaluatorContext context) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QIndexSubordLookup(this, _index, null); } + + Object key = GetKey(eventsPerStream, context); + + if (InstrumentationHelper.ENABLED) { + ISet result = _index.Lookup(key); + InstrumentationHelper.Get().AIndexSubordLookup(result, key); + return result; + } + + return _index.Lookup(key); + } + + /// + /// Get the index lookup keys. + /// + /// is the events for each stream + /// The context. + /// key object + protected virtual Object GetKey(EventBean[] eventsPerStream, ExprEvaluatorContext context) + { + return Evaluator.Evaluate(new EvaluateParams(eventsPerStream, true, context)); + } + + public virtual LookupStrategyDesc StrategyDesc + { + get { return _strategyDesc; } + } + + public virtual String ToQueryPlan() + { + return GetType().FullName + " evaluator " + Evaluator.GetType().Name; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordIndexedTableLookupStrategySingleProp.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordIndexedTableLookupStrategySingleProp.cs new file mode 100755 index 000000000..414a81a01 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordIndexedTableLookupStrategySingleProp.cs @@ -0,0 +1,87 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.join.table; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.lookup +{ + /// + /// MapIndex lookup strategy for subqueries. + /// + public class SubordIndexedTableLookupStrategySingleProp : SubordTableLookupStrategy + { + /// Stream numbers to get key values from. + private readonly int _keyStreamNum; + + /// Getters to use to get key values. + private readonly EventPropertyGetter _propertyGetter; + + /// MapIndex to look up in. + private readonly PropertyIndexedEventTableSingle _index; + + private readonly LookupStrategyDesc _strategyDesc; + + public SubordIndexedTableLookupStrategySingleProp(int keyStreamNum, EventPropertyGetter propertyGetter, PropertyIndexedEventTableSingle index, LookupStrategyDesc strategyDesc) + { + this._keyStreamNum = keyStreamNum; + this._propertyGetter = propertyGetter; + this._index = index; + this._strategyDesc = strategyDesc; + } + + /// Returns index to look up in. + /// index to use + public PropertyIndexedEventTableSingle Index + { + get { return _index; } + } + + public ICollection Lookup(EventBean[] eventsPerStream, ExprEvaluatorContext context) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QIndexSubordLookup(this, _index, new int[]{_keyStreamNum}); } + + Object key = GetKey(eventsPerStream); + + if (InstrumentationHelper.ENABLED) { + ISet result = _index.Lookup(key); + InstrumentationHelper.Get().AIndexSubordLookup(result, key); + return result; + } + return _index.Lookup(key); + } + + /// Get the index lookup keys. + /// is the events for each stream + /// key object + protected Object GetKey(EventBean[] eventsPerStream) + { + EventBean theEvent = eventsPerStream[_keyStreamNum]; + return _propertyGetter.Get(theEvent); + } + + public override String ToString() + { + return ToQueryPlan(); + } + + public LookupStrategyDesc StrategyDesc + { + get { return _strategyDesc; } + } + + public String ToQueryPlan() { + return GetType().FullName + " stream=" + _keyStreamNum; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordIndexedTableLookupStrategySinglePropFactory.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordIndexedTableLookupStrategySinglePropFactory.cs new file mode 100755 index 000000000..0ad4e460e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordIndexedTableLookupStrategySinglePropFactory.cs @@ -0,0 +1,64 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.@join.table; +using com.espertech.esper.epl.virtualdw; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.lookup +{ + /// + /// Index lookup strategy for subqueries. + /// + public class SubordIndexedTableLookupStrategySinglePropFactory : SubordTableLookupStrategyFactory + { + private readonly String _property; + + /// Stream numbers to get key values from. + private readonly int _keyStreamNum; + + /// Getters to use to get key values. + private readonly EventPropertyGetter _propertyGetter; + + /// + /// Ctor. + /// + /// if set to true [is NW on trigger]. + /// is the event types per stream + /// is the stream number per property + /// is the key properties + public SubordIndexedTableLookupStrategySinglePropFactory(bool isNWOnTrigger, EventType[] eventTypes, int keyStreamNum, String property) + { + _keyStreamNum = keyStreamNum + (isNWOnTrigger ? 1 : 0); // for on-trigger the key will be provided in a {1,2,...} stream and not {0,...} + _property = property; + _propertyGetter = EventBeanUtility.GetAssertPropertyGetter(eventTypes, keyStreamNum, property); + } + + public SubordTableLookupStrategy MakeStrategy(EventTable[] eventTable, VirtualDWView vdw) + { + if (eventTable[0] is PropertyIndexedEventTableSingleUnique) { + return new SubordIndexedTableLookupStrategySinglePropUnique(_keyStreamNum, _propertyGetter, (PropertyIndexedEventTableSingleUnique) eventTable[0], + new LookupStrategyDesc(LookupStrategyType.SINGLEPROPUNIQUE, new String[] {_property})); + } + LookupStrategyDesc desc = new LookupStrategyDesc(LookupStrategyType.SINGLEPROPNONUNIQUE, new String[] {_property}); + return new SubordIndexedTableLookupStrategySingleProp(_keyStreamNum, _propertyGetter, (PropertyIndexedEventTableSingle) eventTable[0], desc); + } + + public override String ToString() + { + return ToQueryPlan(); + } + + public String ToQueryPlan() { + return GetType().Name + " property=" + _property + " stream=" + _keyStreamNum; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordIndexedTableLookupStrategySinglePropUnique.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordIndexedTableLookupStrategySinglePropUnique.cs new file mode 100755 index 000000000..7f504ebf8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordIndexedTableLookupStrategySinglePropUnique.cs @@ -0,0 +1,87 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.@join.table; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.lookup +{ + /// + /// Index lookup strategy for subqueries. + /// + public class SubordIndexedTableLookupStrategySinglePropUnique : SubordTableLookupStrategy + { + /// Stream numbers to get key values from. + private readonly int _keyStreamNum; + + /// Getters to use to get key values. + private readonly EventPropertyGetter _propertyGetter; + + /// MapIndex to look up in. + private readonly PropertyIndexedEventTableSingleUnique _index; + + private readonly LookupStrategyDesc _strategyDesc; + + public SubordIndexedTableLookupStrategySinglePropUnique(int keyStreamNum, EventPropertyGetter propertyGetter, PropertyIndexedEventTableSingleUnique index, LookupStrategyDesc strategyDesc) + { + _keyStreamNum = keyStreamNum; + _propertyGetter = propertyGetter; + _index = index; + _strategyDesc = strategyDesc; + } + + /// Returns index to look up in. + /// index to use + public PropertyIndexedEventTableSingleUnique Index + { + get { return _index; } + } + + public ICollection Lookup(EventBean[] eventsPerStream, ExprEvaluatorContext context) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QIndexSubordLookup(this, _index, new int[]{_keyStreamNum});} + + Object key = GetKey(eventsPerStream); + + if (InstrumentationHelper.ENABLED) { + ISet result = _index.Lookup(key); + InstrumentationHelper.Get().AIndexSubordLookup(result, key); + return result; + } + return _index.Lookup(key); + } + + public LookupStrategyDesc StrategyDesc + { + get { return _strategyDesc; } + } + + /// Get the index lookup keys. + /// is the events for each stream + /// key object + protected Object GetKey(EventBean[] eventsPerStream) + { + EventBean theEvent = eventsPerStream[_keyStreamNum]; + return _propertyGetter.Get(theEvent); + } + + public override String ToString() + { + return ToQueryPlan(); + } + + public String ToQueryPlan() { + return GetType().FullName + " stream=" + _keyStreamNum; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordIndexedTableLookupTableStrategy.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordIndexedTableLookupTableStrategy.cs new file mode 100755 index 000000000..df1a2e7dc --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordIndexedTableLookupTableStrategy.cs @@ -0,0 +1,55 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.threading; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.table.strategy; + +namespace com.espertech.esper.epl.lookup +{ + /// + /// Index lookup strategy for subqueries against tables, full table scan. + /// + public class SubordIndexedTableLookupTableStrategy : SubordTableLookupStrategy + { + private readonly SubordTableLookupStrategy _inner; + private readonly ILockable _lock; + + public SubordIndexedTableLookupTableStrategy(SubordTableLookupStrategy inner, ILockable @lock) + { + _inner = inner; + _lock = @lock; + } + + public ICollection Lookup(EventBean[] events, ExprEvaluatorContext context) + { + ExprTableEvalLockUtil.ObtainLockUnless(_lock, context); + + var result = _inner.Lookup(events, context); + if (result == null) + { + return Collections.GetEmptyList(); + } + return result; + } + + public LookupStrategyDesc StrategyDesc + { + get { return _inner.StrategyDesc; } + } + + public string ToQueryPlan() + { + return GetType().Name + " inner " + _inner.ToQueryPlan(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordPropHashKey.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordPropHashKey.cs new file mode 100755 index 000000000..0ac14ccfe --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordPropHashKey.cs @@ -0,0 +1,31 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.join.plan; + +namespace com.espertech.esper.epl.lookup +{ + /// Holds property information for joined properties in a lookup. + public class SubordPropHashKey + { + public SubordPropHashKey(QueryGraphValueEntryHashKeyed hashKey, int? optionalKeyStreamNum, Type coercionType) + { + HashKey = hashKey; + OptionalKeyStreamNum = optionalKeyStreamNum; + CoercionType = coercionType; + } + + public int? OptionalKeyStreamNum { get; private set; } + + public QueryGraphValueEntryHashKeyed HashKey { get; private set; } + + public Type CoercionType { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordPropInKeywordMultiIndex.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordPropInKeywordMultiIndex.cs new file mode 100755 index 000000000..a28f43ca6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordPropInKeywordMultiIndex.cs @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.lookup +{ + /// + /// Holds property information for joined properties in a lookup. + /// + [Serializable] + public class SubordPropInKeywordMultiIndex + { + public SubordPropInKeywordMultiIndex(String[] indexedProp, Type coercionType, ExprNode expression) + { + IndexedProp = indexedProp; + CoercionType = coercionType; + Expression = expression; + } + + public string[] IndexedProp { get; private set; } + + public Type CoercionType { get; private set; } + + public ExprNode Expression { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordPropInKeywordSingleIndex.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordPropInKeywordSingleIndex.cs new file mode 100755 index 000000000..ac8366cfe --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordPropInKeywordSingleIndex.cs @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.lookup +{ + /// + /// Holds property information for joined properties in a lookup. + /// + [Serializable] + public class SubordPropInKeywordSingleIndex + { + public SubordPropInKeywordSingleIndex(String indexedProp, Type coercionType, ExprNode[] expressions) + { + IndexedProp = indexedProp; + CoercionType = coercionType; + Expressions = expressions; + } + + public string IndexedProp { get; private set; } + + public Type CoercionType { get; private set; } + + public ExprNode[] Expressions { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordPropPlan.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordPropPlan.cs new file mode 100755 index 000000000..62b25e223 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordPropPlan.cs @@ -0,0 +1,46 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.epl.lookup +{ + public class SubordPropPlan + { + public SubordPropPlan() + { + HashProps = new LinkedHashMap(); + RangeProps = new LinkedHashMap(); + InKeywordSingleIndex = null; + InKeywordMultiIndex = null; + } + + public SubordPropPlan( + IDictionary hashProps, + IDictionary rangeProps, + SubordPropInKeywordSingleIndex inKeywordSingleIndex, + SubordPropInKeywordMultiIndex inKeywordMultiIndex) + { + HashProps = hashProps; + RangeProps = rangeProps; + InKeywordSingleIndex = inKeywordSingleIndex; + InKeywordMultiIndex = inKeywordMultiIndex; + } + + public IDictionary RangeProps { get; private set; } + + public IDictionary HashProps { get; private set; } + + public SubordPropInKeywordSingleIndex InKeywordSingleIndex { get; private set; } + + public SubordPropInKeywordMultiIndex InKeywordMultiIndex { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordPropRangeKey.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordPropRangeKey.cs new file mode 100755 index 000000000..3a5bf76ee --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordPropRangeKey.cs @@ -0,0 +1,47 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.epl.join.plan; + +namespace com.espertech.esper.epl.lookup +{ + public class SubordPropRangeKey + { + public SubordPropRangeKey(QueryGraphValueEntryRange rangeInfo, Type coercionType) + { + RangeInfo = rangeInfo; + CoercionType = coercionType; + } + + public Type CoercionType { get; private set; } + + public QueryGraphValueEntryRange RangeInfo { get; private set; } + + public String ToQueryPlan() + { + return string.Format(" INFO {0} coercion {1}", RangeInfo.ToQueryPlan(), CoercionType); + } + + public static String ToQueryPlan(ICollection rangeDescs) + { + var writer = new StringWriter(); + var delimiter = ""; + foreach (var key in rangeDescs) + { + writer.Write(delimiter); + writer.Write(key.ToQueryPlan()); + delimiter = ", "; + } + return writer.ToString(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordPropUtil.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordPropUtil.cs new file mode 100755 index 000000000..537ccc77f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordPropUtil.cs @@ -0,0 +1,101 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; +using com.espertech.esper.compat; +using com.espertech.esper.epl.join.plan; + + +namespace com.espertech.esper.epl.lookup +{ + public class SubordPropUtil { + + public static bool IsStrictKeyJoin(ICollection hashKeys) + { + return hashKeys.All( + hashKey => (hashKey.HashKey is QueryGraphValueEntryHashKeyedProp)); + } + + /// Returns the key stream numbers. + /// a list of descriptors + /// key stream numbers + public static int[] GetKeyStreamNums(ICollection descList) + { + var streamIds = new int[descList.Count]; + var count = 0; + foreach (SubordPropHashKey desc in descList) + { + if (!(desc.HashKey is QueryGraphValueEntryHashKeyedProp)) { + throw new UnsupportedOperationException("Not a strict key compare"); + } + streamIds[count++] = desc.OptionalKeyStreamNum.Value; + } + return streamIds; + } + + /// Returns the key property names. + /// a list of descriptors + /// key property names + public static String[] GetKeyProperties(ICollection descList) + { + var result = new String[descList.Count]; + var count = 0; + foreach (var desc in descList) + { + if (!(desc.HashKey is QueryGraphValueEntryHashKeyedProp)) { + throw new UnsupportedOperationException("Not a strict key compare"); + } + var keyed = (QueryGraphValueEntryHashKeyedProp) desc.HashKey; + result[count++] = keyed.KeyProperty; + } + return result; + } + + /// Returns the key property names. + /// a list of descriptors + /// key property names + public static String[] GetKeyProperties(SubordPropHashKey[] descList) + { + var result = new String[descList.Length]; + var count = 0; + foreach (var desc in descList) + { + if (!(desc.HashKey is QueryGraphValueEntryHashKeyedProp)) { + throw new UnsupportedOperationException("Not a strict key compare"); + } + var keyed = (QueryGraphValueEntryHashKeyedProp) desc.HashKey; + result[count++] = keyed.KeyProperty; + } + return result; + } + + /// Returns the key coercion types. + /// a list of descriptors + /// key coercion types + public static Type[] GetCoercionTypes(ICollection descList) + { + var result = new Type[descList.Count]; + var count = 0; + foreach (var desc in descList) + { + result[count++] = desc.CoercionType; + } + return result; + } + + /// Returns the key coercion types. + /// a list of descriptors + /// key coercion types + public static Type[] GetCoercionTypes(SubordPropHashKey[] descList) + { + return descList.Select(desc => desc.CoercionType).ToArray(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordSortedTableLookupStrategy.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordSortedTableLookupStrategy.cs new file mode 100755 index 000000000..6a581a50b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordSortedTableLookupStrategy.cs @@ -0,0 +1,63 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.@join.exec.sorted; +using com.espertech.esper.epl.@join.table; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.lookup +{ + /// + /// MapIndex lookup strategy for subqueries. + /// + public class SubordSortedTableLookupStrategy : SubordTableLookupStrategy + { + private readonly SortedAccessStrategy _strategy; + + /// MapIndex to look up in. + private readonly PropertySortedEventTable _index; + + private readonly LookupStrategyDesc _strategyDesc; + + public SubordSortedTableLookupStrategy(SortedAccessStrategy strategy, PropertySortedEventTable index, LookupStrategyDesc strategyDesc) + { + _strategy = strategy; + _index = index; + _strategyDesc = strategyDesc; + } + + public ICollection Lookup(EventBean[] eventsPerStream, ExprEvaluatorContext context) + { + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().QIndexSubordLookup(this, _index, null); + List keys = new List(2); + ICollection result = _strategy.LookupCollectKeys(eventsPerStream, _index, context, keys); + InstrumentationHelper.Get().AIndexSubordLookup(result, keys.Count > 1 ? keys.ToArray() : keys[0]); + return result; + } + + return _strategy.Lookup(eventsPerStream, _index, context); + } + + public String ToQueryPlan() + { + return GetType().Name; + } + + public LookupStrategyDesc StrategyDesc + { + get { return _strategyDesc; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordSortedTableLookupStrategyFactory.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordSortedTableLookupStrategyFactory.cs new file mode 100755 index 000000000..f09ffe0f1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordSortedTableLookupStrategyFactory.cs @@ -0,0 +1,44 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.@join.exec.sorted; +using com.espertech.esper.epl.@join.table; +using com.espertech.esper.epl.virtualdw; + +namespace com.espertech.esper.epl.lookup +{ + /// + /// MapIndex lookup strategy for subqueries. + /// + public class SubordSortedTableLookupStrategyFactory : SubordTableLookupStrategyFactory + { + private readonly SubordPropRangeKey _rangeKey; + + private readonly SortedAccessStrategy _strategy; + + private readonly LookupStrategyDesc _strategyDesc; + + public SubordSortedTableLookupStrategyFactory(bool isNWOnTrigger, int numStreams, SubordPropRangeKey rangeKey) + { + _rangeKey = rangeKey; + _strategy = SortedAccessStrategyFactory.Make(isNWOnTrigger, -1, numStreams, rangeKey); + _strategyDesc = new LookupStrategyDesc(LookupStrategyType.RANGE, ExprNodeUtility.ToExpressionStringsMinPrecedence(rangeKey.RangeInfo.Expressions)); + } + + public SubordTableLookupStrategy MakeStrategy(EventTable[] eventTable, VirtualDWView vdw) { + return new SubordSortedTableLookupStrategy(_strategy, (PropertySortedEventTable) eventTable[0], _strategyDesc); + } + + public String ToQueryPlan() { + return GetType().Name + " range " + _rangeKey.ToQueryPlan(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordTableLookupStrategy.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordTableLookupStrategy.cs new file mode 100755 index 000000000..e130b4432 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordTableLookupStrategy.cs @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.lookup +{ + /// + /// Strategy for looking up, in some sort of table or index, or a set of events, potentially + /// based on the events properties, and returning a set of matched events. + /// + public interface SubordTableLookupStrategy + { + /// + /// Returns matched events for a set of events to look up for. Never returns an empty + /// result set, always returns null to indicate no results. + /// + /// to look up + /// The context. + /// + /// set of matching events, or null if none matching + /// + ICollection Lookup(EventBean[] events, ExprEvaluatorContext context); + + String ToQueryPlan(); + + LookupStrategyDesc StrategyDesc { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordTableLookupStrategyFactory.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordTableLookupStrategyFactory.cs new file mode 100755 index 000000000..9e93ff427 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordTableLookupStrategyFactory.cs @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.join.table; +using com.espertech.esper.epl.virtualdw; + +namespace com.espertech.esper.epl.lookup +{ + /// + /// Strategy for looking up, in some sort of table or index, or a set of events, potentially based on the + /// events properties, and returning a set of matched events. + /// + public interface SubordTableLookupStrategyFactory + { + SubordTableLookupStrategy MakeStrategy(EventTable[] eventTable, VirtualDWView vdw); + String ToQueryPlan(); + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordTableLookupStrategyFactoryVDW.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordTableLookupStrategyFactoryVDW.cs new file mode 100755 index 000000000..69d3dd426 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordTableLookupStrategyFactoryVDW.cs @@ -0,0 +1,71 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.epl.@join.plan; +using com.espertech.esper.epl.@join.table; +using com.espertech.esper.epl.virtualdw; + +namespace com.espertech.esper.epl.lookup +{ + /// + /// Strategy for looking up, in some sort of table or index, or a set of events, potentially based on the + /// events properties, and returning a set of matched events. + /// + public class SubordTableLookupStrategyFactoryVDW : SubordTableLookupStrategyFactory + { + private readonly string _statementName; + private readonly int _statementId; + private readonly Attribute[] _annotations; + private readonly EventType[] _outerStreams; + private readonly IList _hashKeys; + private readonly CoercionDesc _hashKeyCoercionTypes; + private readonly IList _rangeKeys; + private readonly CoercionDesc _rangeKeyCoercionTypes; + private readonly bool _nwOnTrigger; + private readonly SubordPropPlan _joinDesc; + private readonly bool _forceTableScan; + private readonly SubordinateQueryPlannerIndexPropListPair _hashAndRanges; + + public SubordTableLookupStrategyFactoryVDW(string statementName, int statementId, Attribute[] annotations, EventType[] outerStreams, IList hashKeys, CoercionDesc hashKeyCoercionTypes, IList rangeKeys, CoercionDesc rangeKeyCoercionTypes, bool nwOnTrigger, SubordPropPlan joinDesc, bool forceTableScan, SubordinateQueryPlannerIndexPropListPair hashAndRanges) + { + _statementName = statementName; + _statementId = statementId; + _annotations = annotations; + _outerStreams = outerStreams; + _hashKeys = hashKeys; + _hashKeyCoercionTypes = hashKeyCoercionTypes; + _rangeKeys = rangeKeys; + _rangeKeyCoercionTypes = rangeKeyCoercionTypes; + _nwOnTrigger = nwOnTrigger; + _joinDesc = joinDesc; + _forceTableScan = forceTableScan; + _hashAndRanges = hashAndRanges; + } + + public SubordTableLookupStrategy MakeStrategy(EventTable[] eventTable, VirtualDWView vdw) + { + Pair tableVW = vdw.GetSubordinateQueryDesc(false, _hashAndRanges.HashedProps, _hashAndRanges.BtreeProps); + return vdw.GetSubordinateLookupStrategy( + _statementName, + _statementId, + _annotations, + _outerStreams, _hashKeys, _hashKeyCoercionTypes, _rangeKeys, _rangeKeyCoercionTypes, _nwOnTrigger, + tableVW.Second, _joinDesc, _forceTableScan); + } + + public string ToQueryPlan() + { + return GetType().Name; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordTableLookupStrategyNullRow.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordTableLookupStrategyNullRow.cs new file mode 100755 index 000000000..cf0e11fd3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordTableLookupStrategyNullRow.cs @@ -0,0 +1,52 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.lookup +{ + /// + /// Implementation for a table lookup strategy that returns exactly one row + /// but leaves that row as an undefined value. + /// + public class SubordTableLookupStrategyNullRow : SubordTableLookupStrategy + { + private static readonly ISet SINGLE_NULL_ROW_EVENT_SET = new HashSet(); + + static SubordTableLookupStrategyNullRow() + { + SINGLE_NULL_ROW_EVENT_SET.Add(null); + } + + public SubordTableLookupStrategyNullRow() + { + } + + public ICollection Lookup(EventBean[] events, ExprEvaluatorContext context) + { + return SINGLE_NULL_ROW_EVENT_SET; + } + + public string ToQueryPlan() + { + return GetType().Name; + } + + public LookupStrategyDesc StrategyDesc + { + get { return new LookupStrategyDesc(LookupStrategyType.NULLROWS, null); } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordWMatchExprLookupStrategy.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordWMatchExprLookupStrategy.cs new file mode 100755 index 000000000..8362e1ac9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordWMatchExprLookupStrategy.cs @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.lookup +{ + /// + /// A lookup strategy that receives an additional match expression. + /// + public interface SubordWMatchExprLookupStrategy + { + /// + /// Determines the events. + /// + /// is the correlation events + /// the events + /// expression evaluation context + EventBean[] Lookup(EventBean[] newData, ExprEvaluatorContext exprEvaluatorContext); + + string ToQueryPlan(); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordWMatchExprLookupStrategyAllFiltered.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordWMatchExprLookupStrategyAllFiltered.cs new file mode 100755 index 000000000..491f22885 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordWMatchExprLookupStrategyAllFiltered.cs @@ -0,0 +1,89 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.lookup +{ + public class SubordWMatchExprLookupStrategyAllFiltered : SubordWMatchExprLookupStrategy + { + private readonly ExprEvaluator _joinExpr; + private readonly EventBean[] _eventsPerStream; + private readonly IEnumerable _iterableEvents; + + /// + /// Ctor. + /// + /// is the where clause + public SubordWMatchExprLookupStrategyAllFiltered(ExprEvaluator joinExpr, IEnumerable iterable) + { + _joinExpr = joinExpr; + _eventsPerStream = new EventBean[2]; + _iterableEvents = iterable; + } + + public EventBean[] Lookup(EventBean[] newData, ExprEvaluatorContext exprEvaluatorContext) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QInfraTriggeredLookup(SubordWMatchExprLookupStrategyType.FULLTABLESCAN_FILTERED); } + + var evaluateParams = new EvaluateParams(_eventsPerStream, true, exprEvaluatorContext); + + ISet removeEvents = null; + IEnumerator eventsIt = _iterableEvents.GetEnumerator(); + for (;eventsIt.MoveNext();) + { + _eventsPerStream[0] = eventsIt.Current; + + foreach (EventBean aNewData in newData) + { + _eventsPerStream[1] = aNewData; // Stream 1 events are the originating events (on-delete events) + + var resultX = _joinExpr.Evaluate(evaluateParams); + if (resultX != null) + { + if (true.Equals(resultX)) + { + if (removeEvents == null) + { + removeEvents = new LinkedHashSet(); + } + removeEvents.Add(_eventsPerStream[0]); + } + } + } + } + + if (removeEvents == null) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AInfraTriggeredLookup(null); } + return null; + } + + EventBean[] result = removeEvents.ToArray(); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AInfraTriggeredLookup(result); } + return result; + } + + public override string ToString() + { + return ToQueryPlan(); + } + + public string ToQueryPlan() + { + return GetType().Name; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordWMatchExprLookupStrategyAllUnfiltered.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordWMatchExprLookupStrategyAllUnfiltered.cs new file mode 100755 index 000000000..8bc492c64 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordWMatchExprLookupStrategyAllUnfiltered.cs @@ -0,0 +1,47 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.lookup +{ + public class SubordWMatchExprLookupStrategyAllUnfiltered : SubordWMatchExprLookupStrategy + { + private readonly IEnumerable _source; + + public SubordWMatchExprLookupStrategyAllUnfiltered(IEnumerable source) + { + this._source = source; + } + + public EventBean[] Lookup(EventBean[] newData, ExprEvaluatorContext exprEvaluatorContext) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QInfraTriggeredLookup(SubordWMatchExprLookupStrategyType.FULLTABLESCAN_UNFILTERED); } + + var result = _source.ToArray(); + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AInfraTriggeredLookup(result); } + return result; + } + + public override string ToString() + { + return ToQueryPlan(); + } + + public string ToQueryPlan() + { + return this.GetType().Name; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordWMatchExprLookupStrategyFactory.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordWMatchExprLookupStrategyFactory.cs new file mode 100755 index 000000000..c4b9eb066 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordWMatchExprLookupStrategyFactory.cs @@ -0,0 +1,24 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.join.table; +using com.espertech.esper.epl.virtualdw; + +namespace com.espertech.esper.epl.lookup +{ + public interface SubordWMatchExprLookupStrategyFactory + { + SubordWMatchExprLookupStrategy Realize(EventTable[] indexes, AgentInstanceContext agentInstanceContext, IEnumerable scanIterable, VirtualDWView virtualDataWindow); + string ToQueryPlan(); + SubordTableLookupStrategyFactory OptionalInnerStrategy { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordWMatchExprLookupStrategyFactoryAllFiltered.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordWMatchExprLookupStrategyFactoryAllFiltered.cs new file mode 100755 index 000000000..19da31569 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordWMatchExprLookupStrategyFactoryAllFiltered.cs @@ -0,0 +1,43 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.join.table; +using com.espertech.esper.epl.virtualdw; + +namespace com.espertech.esper.epl.lookup +{ + public class SubordWMatchExprLookupStrategyFactoryAllFiltered : SubordWMatchExprLookupStrategyFactory + { + private readonly ExprEvaluator _exprEvaluator; + + public SubordWMatchExprLookupStrategyFactoryAllFiltered(ExprEvaluator exprEvaluator) + { + this._exprEvaluator = exprEvaluator; + } + + public SubordWMatchExprLookupStrategy Realize(EventTable[] indexes, AgentInstanceContext agentInstanceContext, IEnumerable scanIterable, VirtualDWView virtualDataWindow) + { + return new SubordWMatchExprLookupStrategyAllFiltered(_exprEvaluator, scanIterable); + } + + public string ToQueryPlan() + { + return this.GetType().Name; + } + + public SubordTableLookupStrategyFactory OptionalInnerStrategy + { + get { return null; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordWMatchExprLookupStrategyFactoryAllUnfiltered.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordWMatchExprLookupStrategyFactoryAllUnfiltered.cs new file mode 100755 index 000000000..3174ef4e6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordWMatchExprLookupStrategyFactoryAllUnfiltered.cs @@ -0,0 +1,35 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.join.table; +using com.espertech.esper.epl.virtualdw; + +namespace com.espertech.esper.epl.lookup +{ + public class SubordWMatchExprLookupStrategyFactoryAllUnfiltered : SubordWMatchExprLookupStrategyFactory + { + public SubordWMatchExprLookupStrategy Realize(EventTable[] indexes, AgentInstanceContext agentInstanceContext, IEnumerable scanIterable, VirtualDWView virtualDataWindow) + { + return new SubordWMatchExprLookupStrategyAllUnfiltered(scanIterable); + } + + public string ToQueryPlan() + { + return this.GetType().Name; + } + + public SubordTableLookupStrategyFactory OptionalInnerStrategy + { + get { return null; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordWMatchExprLookupStrategyFactoryIndexedFiltered.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordWMatchExprLookupStrategyFactoryIndexedFiltered.cs new file mode 100755 index 000000000..418455375 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordWMatchExprLookupStrategyFactoryIndexedFiltered.cs @@ -0,0 +1,46 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.join.table; +using com.espertech.esper.epl.virtualdw; + +namespace com.espertech.esper.epl.lookup +{ + public class SubordWMatchExprLookupStrategyFactoryIndexedFiltered : SubordWMatchExprLookupStrategyFactory + { + private readonly ExprEvaluator _exprEvaluator; + private readonly SubordTableLookupStrategyFactory _lookupStrategyFactory; + + public SubordWMatchExprLookupStrategyFactoryIndexedFiltered(ExprEvaluator exprEvaluator, SubordTableLookupStrategyFactory lookupStrategyFactory) + { + this._exprEvaluator = exprEvaluator; + this._lookupStrategyFactory = lookupStrategyFactory; + } + + public SubordWMatchExprLookupStrategy Realize(EventTable[] indexes, AgentInstanceContext agentInstanceContext, IEnumerable scanIterable, VirtualDWView virtualDataWindow) + { + SubordTableLookupStrategy strategy = _lookupStrategyFactory.MakeStrategy(indexes, virtualDataWindow); + return new SubordWMatchExprLookupStrategyIndexedFiltered(_exprEvaluator, strategy); + } + + public string ToQueryPlan() + { + return this.GetType().Name + " " + " strategy " + _lookupStrategyFactory.ToQueryPlan(); + } + + public SubordTableLookupStrategyFactory OptionalInnerStrategy + { + get { return _lookupStrategyFactory; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordWMatchExprLookupStrategyFactoryIndexedUnfiltered.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordWMatchExprLookupStrategyFactoryIndexedUnfiltered.cs new file mode 100755 index 000000000..3ee0ee8d3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordWMatchExprLookupStrategyFactoryIndexedUnfiltered.cs @@ -0,0 +1,43 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.join.table; +using com.espertech.esper.epl.virtualdw; + +namespace com.espertech.esper.epl.lookup +{ + public class SubordWMatchExprLookupStrategyFactoryIndexedUnfiltered : SubordWMatchExprLookupStrategyFactory + { + private readonly SubordTableLookupStrategyFactory _lookupStrategyFactory; + + public SubordWMatchExprLookupStrategyFactoryIndexedUnfiltered(SubordTableLookupStrategyFactory lookupStrategyFactory) + { + this._lookupStrategyFactory = lookupStrategyFactory; + } + + public SubordWMatchExprLookupStrategy Realize(EventTable[] indexes, AgentInstanceContext agentInstanceContext, IEnumerable scanIterable, VirtualDWView virtualDataWindow) + { + SubordTableLookupStrategy strategy = _lookupStrategyFactory.MakeStrategy(indexes, virtualDataWindow); + return new SubordWMatchExprLookupStrategyIndexedUnfiltered(strategy); + } + + public string ToQueryPlan() + { + return this.GetType().Name + " " + " strategy " + _lookupStrategyFactory.ToQueryPlan(); + } + + public SubordTableLookupStrategyFactory OptionalInnerStrategy + { + get { return _lookupStrategyFactory; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordWMatchExprLookupStrategyIndexedFiltered.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordWMatchExprLookupStrategyIndexedFiltered.cs new file mode 100755 index 000000000..5fe5b3df3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordWMatchExprLookupStrategyIndexedFiltered.cs @@ -0,0 +1,107 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.lookup +{ + public class SubordWMatchExprLookupStrategyIndexedFiltered : SubordWMatchExprLookupStrategy + { + private readonly ExprEvaluator _joinExpr; + private readonly EventBean[] _eventsPerStream; + private readonly SubordTableLookupStrategy _tableLookupStrategy; + + /// + /// Ctor. + /// + /// the validated where clause of the on-delete + /// the strategy for looking up in an index the matching events using correlation + public SubordWMatchExprLookupStrategyIndexedFiltered(ExprEvaluator joinExpr, SubordTableLookupStrategy tableLookupStrategy) + { + _joinExpr = joinExpr; + _eventsPerStream = new EventBean[2]; + _tableLookupStrategy = tableLookupStrategy; + } + + public SubordTableLookupStrategy TableLookupStrategy + { + get { return _tableLookupStrategy; } + } + + public EventBean[] Lookup(EventBean[] newData, ExprEvaluatorContext exprEvaluatorContext) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QInfraTriggeredLookup(SubordWMatchExprLookupStrategyType.INDEXED_FILTERED); } + ISet foundEvents = null; + + var evaluateParams = new EvaluateParams(_eventsPerStream, true, exprEvaluatorContext); + + // For every new event (usually 1) + foreach (EventBean newEvent in newData) + { + _eventsPerStream[1] = newEvent; + + // use index to find match + var matches = _tableLookupStrategy.Lookup(_eventsPerStream, exprEvaluatorContext); + if ((matches == null) || (matches.IsEmpty())) + { + continue; + } + + // evaluate expression + var eventsIt = matches.GetEnumerator(); + while (eventsIt.MoveNext()) + { + _eventsPerStream[0] = eventsIt.Current; + + foreach (EventBean aNewData in newData) + { + _eventsPerStream[1] = aNewData; // Stream 1 events are the originating events (on-delete events) + + var result = (bool?) _joinExpr.Evaluate(evaluateParams); + if (result != null) + { + if (result.Value) + { + if (foundEvents == null) + { + foundEvents = new LinkedHashSet(); + } + foundEvents.Add(_eventsPerStream[0]); + } + } + } + } + } + + if (foundEvents == null) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AInfraTriggeredLookup(null); } + return null; + } + + EventBean[] events = foundEvents.ToArray(); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AInfraTriggeredLookup(events); } + + return events; + } + + public override string ToString() { + return ToQueryPlan(); + } + + public string ToQueryPlan() { + return GetType().Name + " " + " strategy " + _tableLookupStrategy.ToQueryPlan(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordWMatchExprLookupStrategyIndexedUnfiltered.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordWMatchExprLookupStrategyIndexedUnfiltered.cs new file mode 100755 index 000000000..b0a6d0c77 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordWMatchExprLookupStrategyIndexedUnfiltered.cs @@ -0,0 +1,80 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.lookup +{ + public class SubordWMatchExprLookupStrategyIndexedUnfiltered : SubordWMatchExprLookupStrategy + { + private readonly EventBean[] _eventsPerStream; + private readonly SubordTableLookupStrategy _tableLookupStrategy; + + /// + /// Ctor. + /// + /// the strategy for looking up in an index the matching events using correlation + public SubordWMatchExprLookupStrategyIndexedUnfiltered(SubordTableLookupStrategy tableLookupStrategy) + { + _eventsPerStream = new EventBean[2]; + _tableLookupStrategy = tableLookupStrategy; + } + + public EventBean[] Lookup(EventBean[] newData, ExprEvaluatorContext exprEvaluatorContext) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QInfraTriggeredLookup(SubordWMatchExprLookupStrategyType.INDEXED_UNFILTERED); } + + ISet removeEvents = null; + + // For every new event (usually 1) + foreach (EventBean newEvent in newData) + { + _eventsPerStream[1] = newEvent; + + // use index to find match + ICollection matches = _tableLookupStrategy.Lookup(_eventsPerStream, exprEvaluatorContext); + if ((matches == null) || (matches.IsEmpty())) + { + continue; + } + + if (removeEvents == null) + { + removeEvents = new LinkedHashSet(); + } + removeEvents.AddAll(matches); + } + + if (removeEvents == null) + { + return null; + } + + EventBean[] result = removeEvents.ToArray(); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AInfraTriggeredLookup(result); } + + return result; + } + + public override string ToString() + { + return ToQueryPlan(); + } + + public string ToQueryPlan() + { + return this.GetType().Name + " " + " strategy " + _tableLookupStrategy.ToQueryPlan(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordWMatchExprLookupStrategyType.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordWMatchExprLookupStrategyType.cs new file mode 100755 index 000000000..6eea74109 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordWMatchExprLookupStrategyType.cs @@ -0,0 +1,18 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.lookup +{ + public enum SubordWMatchExprLookupStrategyType + { + FULLTABLESCAN_UNFILTERED, + FULLTABLESCAN_FILTERED, + INDEXED_FILTERED, + INDEXED_UNFILTERED + } +} diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordinateQueryIndexDesc.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordinateQueryIndexDesc.cs new file mode 100755 index 000000000..564e61827 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordinateQueryIndexDesc.cs @@ -0,0 +1,35 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.@join.plan; + +namespace com.espertech.esper.epl.lookup +{ + public class SubordinateQueryIndexDesc + { + public SubordinateQueryIndexDesc( + IndexKeyInfo indexKeyInfo, + string indexName, + IndexMultiKey indexMultiKey, + QueryPlanIndexItem queryPlanIndexItem) + { + IndexKeyInfo = indexKeyInfo; + IndexName = indexName; + IndexMultiKey = indexMultiKey; + QueryPlanIndexItem = queryPlanIndexItem; + } + + public IndexKeyInfo IndexKeyInfo { get; private set; } + + public string IndexName { get; private set; } + + public IndexMultiKey IndexMultiKey { get; private set; } + + public QueryPlanIndexItem QueryPlanIndexItem { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordinateQueryPlanDesc.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordinateQueryPlanDesc.cs new file mode 100755 index 000000000..054364523 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordinateQueryPlanDesc.cs @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.lookup +{ + public class SubordinateQueryPlanDesc + { + public SubordinateQueryPlanDesc(SubordTableLookupStrategyFactory lookupStrategyFactory, SubordinateQueryIndexDesc[] indexDescs) + { + LookupStrategyFactory = lookupStrategyFactory; + IndexDescs = indexDescs; + } + + public SubordTableLookupStrategyFactory LookupStrategyFactory { get; private set; } + + public SubordinateQueryIndexDesc[] IndexDescs { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordinateQueryPlanner.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordinateQueryPlanner.cs new file mode 100755 index 000000000..b7194cd93 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordinateQueryPlanner.cs @@ -0,0 +1,342 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.join.hint; +using com.espertech.esper.epl.join.plan; + +namespace com.espertech.esper.epl.lookup +{ + public class SubordinateQueryPlanner + { + public static SubordinateWMatchExprQueryPlanResult PlanOnExpression( + ExprNode joinExpr, + EventType filterEventType, + IndexHint optionalIndexHint, + bool isIndexShare, + int subqueryNumber, + ExcludePlanHint excludePlanHint, + bool isVirtualDataWindow, + EventTableIndexMetadata indexMetadata, + EventType eventTypeIndexed, + ICollection optionalUniqueKeyProps, + bool onlyUseExistingIndexes, + string statementName, + int statementId, + Attribute[] annotations) + { + var allStreamsZeroIndexed = new EventType[] { eventTypeIndexed, filterEventType }; + var outerStreams = new EventType[] { filterEventType }; + var joinedPropPlan = QueryPlanIndexBuilder.GetJoinProps(joinExpr, 1, allStreamsZeroIndexed, excludePlanHint); + + // No join expression means all + if (joinExpr == null && !isVirtualDataWindow) + { + return new SubordinateWMatchExprQueryPlanResult(new SubordWMatchExprLookupStrategyFactoryAllUnfiltered(), null); + } + + var queryPlanDesc = PlanSubquery(outerStreams, joinedPropPlan, true, false, optionalIndexHint, isIndexShare, subqueryNumber, + isVirtualDataWindow, indexMetadata, optionalUniqueKeyProps, onlyUseExistingIndexes, statementName, statementId, annotations); + + if (queryPlanDesc == null) + { + return new SubordinateWMatchExprQueryPlanResult(new SubordWMatchExprLookupStrategyFactoryAllFiltered(joinExpr.ExprEvaluator), null); + } + + if (joinExpr == null) + { // it can be null when using virtual data window + return new SubordinateWMatchExprQueryPlanResult( + new SubordWMatchExprLookupStrategyFactoryIndexedUnfiltered(queryPlanDesc.LookupStrategyFactory), queryPlanDesc.IndexDescs); + } + else + { + return new SubordinateWMatchExprQueryPlanResult( + new SubordWMatchExprLookupStrategyFactoryIndexedFiltered(joinExpr.ExprEvaluator, queryPlanDesc.LookupStrategyFactory), queryPlanDesc.IndexDescs); + } + } + + public static SubordinateQueryPlanDesc PlanSubquery( + EventType[] outerStreams, + SubordPropPlan joinDesc, + bool isNWOnTrigger, + bool forceTableScan, + IndexHint optionalIndexHint, + bool indexShare, + int subqueryNumber, + bool isVirtualDataWindow, + EventTableIndexMetadata indexMetadata, + ICollection optionalUniqueKeyProps, + bool onlyUseExistingIndexes, + string statementName, + int statementId, + Attribute[] annotations) + { + if (isVirtualDataWindow) + { + var indexProps = GetIndexPropDesc(joinDesc.HashProps, joinDesc.RangeProps); + var lookupStrategyFactoryVdw = new SubordTableLookupStrategyFactoryVDW(statementName, statementId, annotations, + outerStreams, + indexProps.HashJoinedProps, + new CoercionDesc(false, indexProps.HashIndexCoercionType), + indexProps.RangeJoinedProps, + new CoercionDesc(false, indexProps.RangeIndexCoercionType), + isNWOnTrigger, + joinDesc, forceTableScan, indexProps.ListPair); + return new SubordinateQueryPlanDesc(lookupStrategyFactoryVdw, null); + } + + var hashKeys = Collections.GetEmptyList(); + CoercionDesc hashKeyCoercionTypes = null; + var rangeKeys = Collections.GetEmptyList(); + CoercionDesc rangeKeyCoercionTypes = null; + ExprNode[] inKeywordSingleIdxKeys = null; + ExprNode inKeywordMultiIdxKey = null; + + SubordinateQueryIndexDesc[] indexDescs; + if (joinDesc.InKeywordSingleIndex != null) + { + var single = joinDesc.InKeywordSingleIndex; + var keyInfo = new SubordPropHashKey(new QueryGraphValueEntryHashKeyedExpr(single.Expressions[0], false), null, single.CoercionType); + var indexDesc = FindOrSuggestIndex( + Collections.SingletonMap(single.IndexedProp, keyInfo), + Collections.GetEmptyMap(), optionalIndexHint, indexShare, subqueryNumber, + indexMetadata, optionalUniqueKeyProps, onlyUseExistingIndexes); + if (indexDesc == null) + { + return null; + } + var desc = new SubordinateQueryIndexDesc(indexDesc.IndexKeyInfo, indexDesc.IndexName, indexDesc.IndexMultiKey, indexDesc.QueryPlanIndexItem); + indexDescs = new SubordinateQueryIndexDesc[] { desc }; + inKeywordSingleIdxKeys = single.Expressions; + } + else if (joinDesc.InKeywordMultiIndex != null) + { + var multi = joinDesc.InKeywordMultiIndex; + + indexDescs = new SubordinateQueryIndexDesc[multi.IndexedProp.Length]; + for (var i = 0; i < multi.IndexedProp.Length; i++) + { + var keyInfo = new SubordPropHashKey(new QueryGraphValueEntryHashKeyedExpr(multi.Expression, false), null, multi.CoercionType); + var indexDesc = FindOrSuggestIndex( + Collections.SingletonMap(multi.IndexedProp[i], keyInfo), + Collections.GetEmptyMap(), optionalIndexHint, indexShare, subqueryNumber, + indexMetadata, optionalUniqueKeyProps, onlyUseExistingIndexes); + if (indexDesc == null) + { + return null; + } + indexDescs[i] = indexDesc; + } + inKeywordMultiIdxKey = multi.Expression; + } + else + { + var indexDesc = FindOrSuggestIndex(joinDesc.HashProps, + joinDesc.RangeProps, optionalIndexHint, false, subqueryNumber, + indexMetadata, optionalUniqueKeyProps, onlyUseExistingIndexes); + if (indexDesc == null) + { + return null; + } + var indexKeyInfo = indexDesc.IndexKeyInfo; + hashKeys = indexKeyInfo.OrderedHashDesc; + hashKeyCoercionTypes = indexKeyInfo.OrderedKeyCoercionTypes; + rangeKeys = indexKeyInfo.OrderedRangeDesc; + rangeKeyCoercionTypes = indexKeyInfo.OrderedRangeCoercionTypes; + var desc = new SubordinateQueryIndexDesc(indexDesc.IndexKeyInfo, indexDesc.IndexName, indexDesc.IndexMultiKey, indexDesc.QueryPlanIndexItem); + indexDescs = new SubordinateQueryIndexDesc[] { desc }; + } + + if (forceTableScan) + { + return null; + } + + var lookupStrategyFactory = SubordinateTableLookupStrategyUtil.GetLookupStrategy(outerStreams, + hashKeys, hashKeyCoercionTypes, rangeKeys, rangeKeyCoercionTypes, inKeywordSingleIdxKeys, inKeywordMultiIdxKey, isNWOnTrigger); + return new SubordinateQueryPlanDesc(lookupStrategyFactory, indexDescs); + } + + private static SubordinateQueryIndexDesc FindOrSuggestIndex( + IDictionary hashProps, + IDictionary rangeProps, + IndexHint optionalIndexHint, + bool isIndexShare, + int subqueryNumber, + EventTableIndexMetadata indexMetadata, + ICollection optionalUniqueKeyProps, + bool onlyUseExistingIndexes) + { + var indexProps = GetIndexPropDesc(hashProps, rangeProps); + var hashedAndBtreeProps = indexProps.ListPair; + + // Get or create the table for this index (exact match or property names, type of index and coercion type is expected) + IndexKeyInfo indexKeyInfo; // how needs all of IndexKeyInfo+QueryPlanIndexItem+IndexMultiKey + IndexMultiKey indexMultiKey; + string indexName = null; + QueryPlanIndexItem planIndexItem = null; + + if (hashedAndBtreeProps.HashedProps.IsEmpty() && hashedAndBtreeProps.BtreeProps.IsEmpty()) + { + return null; + } + + Pair existing = null; + Pair planned = null; + + // consider index hints + IList optionalIndexHintInstructions = null; + if (optionalIndexHint != null) + { + optionalIndexHintInstructions = optionalIndexHint.GetInstructionsSubquery(subqueryNumber); + } + + var indexFoundPair = EventTableIndexUtil.FindIndexConsiderTyping(indexMetadata.Indexes, hashedAndBtreeProps.HashedProps, hashedAndBtreeProps.BtreeProps, optionalIndexHintInstructions); + if (indexFoundPair != null) + { + var hintIndex = indexMetadata.Indexes.Get(indexFoundPair); + existing = new Pair(indexFoundPair, hintIndex.OptionalIndexName); + } + + // nothing found: plan one + if (existing == null && !onlyUseExistingIndexes) + { + // not found, see if the item is declared unique + var proposedHashedProps = hashedAndBtreeProps.HashedProps; + var proposedBtreeProps = hashedAndBtreeProps.BtreeProps; + + // match against unique-key properties when suggesting an index + var unique = false; + var coerce = !isIndexShare; + if (optionalUniqueKeyProps != null && !optionalUniqueKeyProps.IsEmpty()) + { + IList newHashProps = new List(); + foreach (var uniqueKey in optionalUniqueKeyProps) + { + var found = false; + foreach (var hashProp in hashedAndBtreeProps.HashedProps) + { + if (hashProp.IndexPropName.Equals(uniqueKey)) + { + newHashProps.Add(hashProp); + found = true; + break; + } + } + if (!found) + { + newHashProps = null; + break; + } + } + if (newHashProps != null) + { + proposedHashedProps = newHashProps; + proposedBtreeProps = Collections.GetEmptyList(); + unique = true; + coerce = false; + } + } + + planned = PlanIndex(unique, proposedHashedProps, proposedBtreeProps, coerce); + } + + // compile index information + if (existing == null && planned == null) + { + return null; + } + // handle existing + if (existing != null) + { + indexKeyInfo = SubordinateQueryPlannerUtil.CompileIndexKeyInfo(existing.First, + indexProps.HashIndexPropsProvided, indexProps.HashJoinedProps, + indexProps.RangeIndexPropsProvided, indexProps.RangeJoinedProps); + indexName = existing.Second; + indexMultiKey = existing.First; + } + // handle planned + else + { + indexKeyInfo = SubordinateQueryPlannerUtil.CompileIndexKeyInfo(planned.Second, + indexProps.HashIndexPropsProvided, indexProps.HashJoinedProps, + indexProps.RangeIndexPropsProvided, indexProps.RangeJoinedProps); + indexMultiKey = planned.Second; + planIndexItem = planned.First; + } + + return new SubordinateQueryIndexDesc(indexKeyInfo, indexName, indexMultiKey, planIndexItem); + } + + private static SubordinateQueryPlannerIndexPropDesc GetIndexPropDesc(IDictionary hashProps, IDictionary rangeProps) + { + // hash property names and types + var hashIndexPropsProvided = new string[hashProps.Count]; + var hashIndexCoercionType = new Type[hashProps.Count]; + var hashJoinedProps = new SubordPropHashKey[hashProps.Count]; + var count = 0; + foreach (var entry in hashProps) + { + hashIndexPropsProvided[count] = entry.Key; + hashIndexCoercionType[count] = entry.Value.CoercionType; + hashJoinedProps[count++] = entry.Value; + } + + // range property names and types + var rangeIndexPropsProvided = new string[rangeProps.Count]; + var rangeIndexCoercionType = new Type[rangeProps.Count]; + var rangeJoinedProps = new SubordPropRangeKey[rangeProps.Count]; + count = 0; + foreach (var entry in rangeProps) + { + rangeIndexPropsProvided[count] = entry.Key; + rangeIndexCoercionType[count] = entry.Value.CoercionType; + rangeJoinedProps[count++] = entry.Value; + } + + // Add all joined fields to an array for sorting + var listPair = SubordinateQueryPlannerUtil.ToListOfHashedAndBtreeProps(hashIndexPropsProvided, + hashIndexCoercionType, rangeIndexPropsProvided, rangeIndexCoercionType); + return new SubordinateQueryPlannerIndexPropDesc(hashIndexPropsProvided, hashIndexCoercionType, + rangeIndexPropsProvided, rangeIndexCoercionType, listPair, + hashJoinedProps, rangeJoinedProps); + } + + private static Pair PlanIndex( + bool unique, + IList hashProps, + IList btreeProps, + bool mustCoerce) + { + // not resolved as full match and not resolved as unique index match, allocate + var indexPropKey = new IndexMultiKey(unique, hashProps, btreeProps); + + var indexedPropDescs = hashProps.ToArray(); + var indexProps = IndexedPropDesc.GetIndexProperties(indexedPropDescs); + var indexCoercionTypes = IndexedPropDesc.GetCoercionTypes(indexedPropDescs); + if (!mustCoerce) + { + indexCoercionTypes = null; + } + + var rangePropDescs = btreeProps.ToArray(); + var rangeProps = IndexedPropDesc.GetIndexProperties(rangePropDescs); + var rangeCoercionTypes = IndexedPropDesc.GetCoercionTypes(rangePropDescs); + + var indexItem = new QueryPlanIndexItem(indexProps, indexCoercionTypes, rangeProps, rangeCoercionTypes, unique); + return new Pair(indexItem, indexPropKey); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordinateQueryPlannerIndexPropDesc.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordinateQueryPlannerIndexPropDesc.cs new file mode 100755 index 000000000..f70c738b0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordinateQueryPlannerIndexPropDesc.cs @@ -0,0 +1,47 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.lookup +{ + public class SubordinateQueryPlannerIndexPropDesc + { + public SubordinateQueryPlannerIndexPropDesc( + string[] hashIndexPropsProvided, + Type[] hashIndexCoercionType, + string[] rangeIndexPropsProvided, + Type[] rangeIndexCoercionType, + SubordinateQueryPlannerIndexPropListPair listPair, + SubordPropHashKey[] hashJoinedProps, + SubordPropRangeKey[] rangeJoinedProps) + { + HashIndexPropsProvided = hashIndexPropsProvided; + HashIndexCoercionType = hashIndexCoercionType; + RangeIndexPropsProvided = rangeIndexPropsProvided; + RangeIndexCoercionType = rangeIndexCoercionType; + ListPair = listPair; + HashJoinedProps = hashJoinedProps; + RangeJoinedProps = rangeJoinedProps; + } + + public string[] HashIndexPropsProvided { get; private set; } + + public Type[] HashIndexCoercionType { get; private set; } + + public string[] RangeIndexPropsProvided { get; private set; } + + public Type[] RangeIndexCoercionType { get; private set; } + + public SubordinateQueryPlannerIndexPropListPair ListPair { get; private set; } + + public SubordPropHashKey[] HashJoinedProps { get; private set; } + + public SubordPropRangeKey[] RangeJoinedProps { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordinateQueryPlannerIndexPropListPair.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordinateQueryPlannerIndexPropListPair.cs new file mode 100755 index 000000000..e5fa562b1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordinateQueryPlannerIndexPropListPair.cs @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +namespace com.espertech.esper.epl.lookup +{ + public class SubordinateQueryPlannerIndexPropListPair + { + public SubordinateQueryPlannerIndexPropListPair( + IList hashedProps, + IList btreeProps) + { + HashedProps = hashedProps; + BtreeProps = btreeProps; + } + + public IList HashedProps { get; private set; } + + public IList BtreeProps { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordinateQueryPlannerUtil.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordinateQueryPlannerUtil.cs new file mode 100755 index 000000000..0506ff096 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordinateQueryPlannerUtil.cs @@ -0,0 +1,264 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.join.plan; +using com.espertech.esper.epl.join.table; +using com.espertech.esper.epl.join.util; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.lookup +{ + public class SubordinateQueryPlannerUtil + { + public static void QueryPlanLogOnExpr( + bool queryPlanLogging, + ILog queryPlanLog, + SubordinateWMatchExprQueryPlanResult strategy, + Attribute[] annotations, + EngineImportService engineImportService) + { + var hook = QueryPlanIndexHookUtil.GetHook(annotations, engineImportService); + if (queryPlanLogging && (queryPlanLog.IsInfoEnabled || hook != null)) + { + var prefix = "On-Expr "; + queryPlanLog.Info(prefix + "strategy " + strategy.Factory.ToQueryPlan()); + if (strategy.IndexDescs == null) + { + queryPlanLog.Info(prefix + "full table scan"); + } + else + { + for (var i = 0; i < strategy.IndexDescs.Length; i++) + { + var indexName = strategy.IndexDescs[i].IndexName; + var indexText = indexName != null ? "index " + indexName + " " : "(implicit) (" + i + ")"; + queryPlanLog.Info(prefix + indexText); + } + } + if (hook != null) + { + var pairs = GetPairs(strategy.IndexDescs); + var inner = strategy.Factory.OptionalInnerStrategy; + hook.InfraOnExpr(new QueryPlanIndexDescOnExpr(pairs, + strategy.Factory.GetType().Name, + inner == null ? null : inner.GetType().Name)); + } + } + } + + public static void QueryPlanLogOnSubq( + bool queryPlanLogging, + ILog queryPlanLog, + SubordinateQueryPlanDesc plan, + int subqueryNum, + Attribute[] annotations, + EngineImportService engineImportService) + { + var hook = QueryPlanIndexHookUtil.GetHook(annotations, engineImportService); + if (queryPlanLogging && (queryPlanLog.IsInfoEnabled || hook != null)) + { + var prefix = "Subquery " + subqueryNum + " "; + var strategy = (plan == null || plan.LookupStrategyFactory == null) ? "table scan" : plan.LookupStrategyFactory.ToQueryPlan(); + queryPlanLog.Info(prefix + "strategy " + strategy); + if (plan != null) + { + if (plan.IndexDescs != null) + { + for (var i = 0; i < plan.IndexDescs.Length; i++) + { + var indexName = plan.IndexDescs[i].IndexName; + var indexText = indexName != null ? "index " + indexName + " " : "(implicit) "; + queryPlanLog.Info(prefix + "shared index"); + queryPlanLog.Info(prefix + indexText); + } + } + } + if (hook != null) + { + var pairs = plan == null ? new IndexNameAndDescPair[0] : GetPairs(plan.IndexDescs); + string factory = plan == null ? null : plan.LookupStrategyFactory.GetType().Name; + hook.Subquery(new QueryPlanIndexDescSubquery(pairs, subqueryNum, factory)); + } + } + } + + private static IndexNameAndDescPair[] GetPairs(SubordinateQueryIndexDesc[] indexDescs) + { + if (indexDescs == null) + { + return null; + } + var pairs = new IndexNameAndDescPair[indexDescs.Length]; + for (var i = 0; i < indexDescs.Length; i++) + { + var index = indexDescs[i]; + pairs[i] = new IndexNameAndDescPair(index.IndexName, index.IndexMultiKey.ToQueryPlan()); + } + return pairs; + } + + public static SubordinateQueryPlannerIndexPropListPair ToListOfHashedAndBtreeProps( + string[] hashIndexPropsProvided, + Type[] hashIndexCoercionType, + string[] rangeIndexPropsProvided, + Type[] rangeIndexCoercionType) + { + IList hashedProps = new List(); + IList btreeProps = new List(); + for (var i = 0; i < hashIndexPropsProvided.Length; i++) + { + hashedProps.Add(new IndexedPropDesc(hashIndexPropsProvided[i], hashIndexCoercionType[i])); + } + for (var i = 0; i < rangeIndexPropsProvided.Length; i++) + { + btreeProps.Add(new IndexedPropDesc(rangeIndexPropsProvided[i], rangeIndexCoercionType[i])); + } + return new SubordinateQueryPlannerIndexPropListPair(hashedProps, btreeProps); + } + + /// + /// Given an index with a defined set of hash(equals) and range(btree) props and uniqueness flag, + /// and given a list of indexable properties and accessors for both hash and range, + /// return the ordered keys and coercion information. + /// + /// index definition + /// hash indexable properties + /// keys for hash indexable properties + /// btree indexable properties + /// keys for btree indexable properties + /// ordered set of key information + public static IndexKeyInfo CompileIndexKeyInfo( + IndexMultiKey indexMultiKey, + string[] hashIndexPropsProvided, + SubordPropHashKey[] hashJoinedProps, + string[] rangeIndexPropsProvided, + SubordPropRangeKey[] rangeJoinedProps) + { + // map the order of indexed columns (key) to the key information available + var indexedKeyProps = indexMultiKey.HashIndexedProps; + var isCoerceHash = false; + var hashesDesc = new SubordPropHashKey[indexedKeyProps.Length]; + var hashPropCoercionTypes = new Type[indexedKeyProps.Length]; + + for (var i = 0; i < indexedKeyProps.Length; i++) + { + var indexField = indexedKeyProps[i].IndexPropName; + var index = CollectionUtil.FindItem(hashIndexPropsProvided, indexField); + if (index == -1) + { + throw new IllegalStateException("Could not find index property for lookup '" + indexedKeyProps[i]); + } + hashesDesc[i] = hashJoinedProps[index]; + hashPropCoercionTypes[i] = indexedKeyProps[i].CoercionType; + var evaluatorHashkey = hashesDesc[i].HashKey.KeyExpr.ExprEvaluator; + if (evaluatorHashkey != null && TypeHelper.GetBoxedType(indexedKeyProps[i].CoercionType) != TypeHelper.GetBoxedType(evaluatorHashkey.ReturnType)) + { // we allow null evaluator + isCoerceHash = true; + } + } + + // map the order of range columns (range) to the range information available + indexedKeyProps = indexMultiKey.RangeIndexedProps; + var rangesDesc = new SubordPropRangeKey[indexedKeyProps.Length]; + var rangePropCoercionTypes = new Type[indexedKeyProps.Length]; + var isCoerceRange = false; + for (var i = 0; i < indexedKeyProps.Length; i++) + { + var indexField = indexedKeyProps[i].IndexPropName; + var index = CollectionUtil.FindItem(rangeIndexPropsProvided, indexField); + if (index == -1) + { + throw new IllegalStateException("Could not find range property for lookup '" + indexedKeyProps[i]); + } + rangesDesc[i] = rangeJoinedProps[index]; + rangePropCoercionTypes[i] = rangeJoinedProps[index].CoercionType; + if (TypeHelper.GetBoxedType(indexedKeyProps[i].CoercionType) != TypeHelper.GetBoxedType(rangePropCoercionTypes[i])) + { + isCoerceRange = true; + } + } + + return new IndexKeyInfo(hashesDesc, + new CoercionDesc(isCoerceHash, hashPropCoercionTypes), rangesDesc, new CoercionDesc(isCoerceRange, rangePropCoercionTypes)); + } + + private static IndexNameAndDescPair[] GetTableClassNamePairs(EventTableAndNamePair[] pairs) + { + if (pairs == null) + { + return new IndexNameAndDescPair[0]; + } + var names = new IndexNameAndDescPair[pairs.Length]; + for (var i = 0; i < pairs.Length; i++) + { + names[i] = new IndexNameAndDescPair(pairs[i].IndexName, pairs[i].EventTable.ProviderClass.Name); + } + return names; + } + + public static EventTable[] RealizeTables( + SubordinateQueryIndexDesc[] indexDescriptors, + EventType eventType, + EventTableIndexRepository indexRepository, + IEnumerable contents, + AgentInstanceContext agentInstanceContext, + bool isRecoveringResilient) + { + var tables = new EventTable[indexDescriptors.Length]; + for (var i = 0; i < tables.Length; i++) + { + var desc = indexDescriptors[i]; + var table = indexRepository.GetIndexByDesc(desc.IndexMultiKey); + if (table == null) + { + table = EventTableUtil.BuildIndex(agentInstanceContext, 0, desc.QueryPlanIndexItem, eventType, true, desc.IndexMultiKey.IsUnique, null, null, false); + + // fill table since its new + if (!isRecoveringResilient) + { + var events = new EventBean[1]; + foreach (var prefilledEvent in contents) + { + events[0] = prefilledEvent; + table.Add(events); + } + } + + indexRepository.AddIndex(desc.IndexMultiKey, new EventTableIndexRepositoryEntry(null, table)); + } + tables[i] = table; + } + return tables; + } + + public static void AddIndexMetaAndRef(SubordinateQueryIndexDesc[] indexDescs, EventTableIndexMetadata repo, string statementName) + { + foreach (var desc in indexDescs) + { + if (desc.IndexName != null) + { + repo.AddIndexReference(desc.IndexName, statementName); + } + else + { + repo.AddIndex(false, desc.IndexMultiKey, null, statementName, false, desc.QueryPlanIndexItem); + repo.AddIndexReference(desc.IndexMultiKey, statementName); + } + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordinateTableLookupStrategyUtil.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordinateTableLookupStrategyUtil.cs new file mode 100755 index 000000000..0251a47d4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordinateTableLookupStrategyUtil.cs @@ -0,0 +1,111 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.join.plan; + +namespace com.espertech.esper.epl.lookup +{ + public class SubordinateTableLookupStrategyUtil + { + public static SubordTableLookupStrategyFactory GetLookupStrategy( + EventType[] outerStreamTypesZeroIndexed, + IList hashKeys, + CoercionDesc hashKeyCoercionTypes, + IList rangeKeys, + CoercionDesc rangeKeyCoercionTypes, + ExprNode[] inKeywordSingleIdxKeys, + ExprNode inKeywordMultiIdxKey, + bool isNWOnTrigger) + { + var isStrictKeys = SubordPropUtil.IsStrictKeyJoin(hashKeys); + string[] hashStrictKeys = null; + int[] hashStrictKeyStreams = null; + if (isStrictKeys) + { + hashStrictKeyStreams = SubordPropUtil.GetKeyStreamNums(hashKeys); + hashStrictKeys = SubordPropUtil.GetKeyProperties(hashKeys); + } + + int numStreamsTotal = outerStreamTypesZeroIndexed.Length + 1; + SubordTableLookupStrategyFactory lookupStrategy; + if (inKeywordSingleIdxKeys != null) + { + lookupStrategy = new SubordInKeywordSingleTableLookupStrategyFactory(isNWOnTrigger, numStreamsTotal, inKeywordSingleIdxKeys); + } + else if (inKeywordMultiIdxKey != null) + { + lookupStrategy = new SubordInKeywordMultiTableLookupStrategyFactory(isNWOnTrigger, numStreamsTotal, inKeywordMultiIdxKey); + } + else if (hashKeys.IsEmpty() && rangeKeys.IsEmpty()) + { + lookupStrategy = new SubordFullTableScanLookupStrategyFactory(); + } + else if (hashKeys.Count > 0 && rangeKeys.IsEmpty()) + { + if (hashKeys.Count == 1) + { + if (!hashKeyCoercionTypes.IsCoerce) + { + if (isStrictKeys) + { + lookupStrategy = new SubordIndexedTableLookupStrategySinglePropFactory( + isNWOnTrigger, outerStreamTypesZeroIndexed, hashStrictKeyStreams[0], hashStrictKeys[0]); + } + else + { + lookupStrategy = new SubordIndexedTableLookupStrategySingleExprFactory( + isNWOnTrigger, numStreamsTotal, hashKeys[0]); + } + } + else + { + lookupStrategy = new SubordIndexedTableLookupStrategySingleCoercingFactory( + isNWOnTrigger, numStreamsTotal, hashKeys[0], hashKeyCoercionTypes.CoercionTypes[0]); + } + } + else + { + if (!hashKeyCoercionTypes.IsCoerce) + { + if (isStrictKeys) + { + lookupStrategy = new SubordIndexedTableLookupStrategyPropFactory( + isNWOnTrigger, outerStreamTypesZeroIndexed, hashStrictKeyStreams, hashStrictKeys); + } + else + { + lookupStrategy = new SubordIndexedTableLookupStrategyExprFactory( + isNWOnTrigger, numStreamsTotal, hashKeys); + } + } + else + { + lookupStrategy = new SubordIndexedTableLookupStrategyCoercingFactory( + isNWOnTrigger, numStreamsTotal, hashKeys, hashKeyCoercionTypes.CoercionTypes); + } + } + } + else if (hashKeys.Count == 0 && rangeKeys.Count == 1) + { + lookupStrategy = new SubordSortedTableLookupStrategyFactory(isNWOnTrigger, numStreamsTotal, rangeKeys[0]); + } + else + { + lookupStrategy = new SubordCompositeTableLookupStrategyFactory( + isNWOnTrigger, numStreamsTotal, hashKeys, hashKeyCoercionTypes.CoercionTypes, + rangeKeys, rangeKeyCoercionTypes.CoercionTypes); + } + return lookupStrategy; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/lookup/SubordinateWMatchExprQueryPlanResult.cs b/NEsper.Core/NEsper.Core/epl/lookup/SubordinateWMatchExprQueryPlanResult.cs new file mode 100755 index 000000000..fa70a289f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/lookup/SubordinateWMatchExprQueryPlanResult.cs @@ -0,0 +1,31 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.lookup +{ + public class SubordinateWMatchExprQueryPlanResult + { + private readonly SubordWMatchExprLookupStrategyFactory factory; + private readonly SubordinateQueryIndexDesc[] indexDescs; + + public SubordinateWMatchExprQueryPlanResult(SubordWMatchExprLookupStrategyFactory factory, SubordinateQueryIndexDesc[] indexDescs) { + this.factory = factory; + this.indexDescs = indexDescs; + } + + public SubordWMatchExprLookupStrategyFactory Factory + { + get { return factory; } + } + + public SubordinateQueryIndexDesc[] IndexDescs + { + get { return indexDescs; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/methodbase/DotMethodFP.cs b/NEsper.Core/NEsper.Core/epl/methodbase/DotMethodFP.cs new file mode 100755 index 000000000..241fe3520 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/methodbase/DotMethodFP.cs @@ -0,0 +1,108 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; +using System.Text; + +namespace com.espertech.esper.epl.methodbase +{ + public class DotMethodFP + { + public DotMethodFP(DotMethodFPInputEnum input, params DotMethodFPParam[] parameters) + { + Input = input; + Parameters = parameters; + } + + public DotMethodFPInputEnum Input { get; private set; } + + public DotMethodFPParam[] Parameters { get; private set; } + + public String ToStringFootprint(bool isLambdaApplies) + { + if (Parameters.Length == 0) + { + return "no parameters"; + } + var buf = new StringBuilder(); + var delimiter = ""; + foreach (var param in Parameters) + { + buf.Append(delimiter); + + if (isLambdaApplies) + { + if (param.LambdaParamNum == 0) + { + buf.Append("an (non-lambda)"); + } + else if (param.LambdaParamNum == 1) + { + buf.Append("a lambda"); + } + else + { + buf.Append("a " + param.LambdaParamNum + "-parameter lambda"); + } + } + else + { + buf.Append("an"); + } + buf.Append(" expression"); + buf.Append(" providing "); + buf.Append(param.Description); + delimiter = " and "; + } + + return buf.ToString(); + } + + public static String ToStringProvided(DotMethodFPProvided provided, bool isLambdaApplies) + { + if (provided.Parameters.Length == 0) + { + return "no parameters"; + } + + var buf = new StringWriter(); + var delimiter = ""; + + if (!isLambdaApplies) + { + buf.Write(provided.Parameters.Length); + buf.Write(" expressions"); + } + else + { + foreach (DotMethodFPProvidedParam param in provided.Parameters) + { + buf.Write(delimiter); + + if (param.LambdaParamNum == 0) + { + buf.Write("an (non-lambda)"); + } + else if (param.LambdaParamNum == 1) + { + buf.Write("a lambda"); + } + else + { + buf.Write("a " + param.LambdaParamNum + "-parameter lambda"); + } + buf.Write(" expression"); + delimiter = " and "; + } + } + + return buf.ToString(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/methodbase/DotMethodFPInputEnum.cs b/NEsper.Core/NEsper.Core/epl/methodbase/DotMethodFPInputEnum.cs new file mode 100755 index 000000000..350b9e613 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/methodbase/DotMethodFPInputEnum.cs @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.methodbase +{ + public enum DotMethodFPInputEnum + { + SCALAR_NUMERIC, + SCALAR_ANY, + EVENTCOLL, + ANY + } ; + + public static class DotMethodFPInputEnumExtensions + { + public static bool IsScalar(this DotMethodFPInputEnum value) + { + return + value == DotMethodFPInputEnum.SCALAR_ANY || + value == DotMethodFPInputEnum.SCALAR_NUMERIC; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/methodbase/DotMethodFPParam.cs b/NEsper.Core/NEsper.Core/epl/methodbase/DotMethodFPParam.cs new file mode 100755 index 000000000..eb13c7a21 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/methodbase/DotMethodFPParam.cs @@ -0,0 +1,43 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.methodbase +{ + public class DotMethodFPParam + { + public DotMethodFPParam(int lambdaParamNum, String description, DotMethodFPParamTypeEnum type) + { + LambdaParamNum = lambdaParamNum; + Description = description; + ParamType = type; + SpecificType = null; + if (type == DotMethodFPParamTypeEnum.SPECIFIC) + { + throw new ArgumentException("Invalid ctor for specific-type parameter"); + } + } + + public DotMethodFPParam(String description, DotMethodFPParamTypeEnum type, Type specificType) + { + Description = description; + ParamType = type; + SpecificType = specificType; + LambdaParamNum = 0; + } + + public int LambdaParamNum { get; private set; } + + public string Description { get; private set; } + + public DotMethodFPParamTypeEnum ParamType { get; private set; } + + public Type SpecificType { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/methodbase/DotMethodFPParamTypeEnum.cs b/NEsper.Core/NEsper.Core/epl/methodbase/DotMethodFPParamTypeEnum.cs new file mode 100755 index 000000000..18f638fb7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/methodbase/DotMethodFPParamTypeEnum.cs @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.methodbase +{ + public enum DotMethodFPParamTypeEnum + { + BOOLEAN, + NUMERIC, + ANY, + SPECIFIC, + TIME_PERIOD_OR_SEC, + DATETIME + } +} diff --git a/NEsper.Core/NEsper.Core/epl/methodbase/DotMethodFPProvided.cs b/NEsper.Core/NEsper.Core/epl/methodbase/DotMethodFPProvided.cs new file mode 100755 index 000000000..a8e731980 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/methodbase/DotMethodFPProvided.cs @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.methodbase +{ + public class DotMethodFPProvided + { + public DotMethodFPProvided(DotMethodFPProvidedParam[] parameters) + { + Parameters = parameters; + } + + public DotMethodFPProvidedParam[] Parameters { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/methodbase/DotMethodFPProvidedParam.cs b/NEsper.Core/NEsper.Core/epl/methodbase/DotMethodFPProvidedParam.cs new file mode 100755 index 000000000..c0442569d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/methodbase/DotMethodFPProvidedParam.cs @@ -0,0 +1,30 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.methodbase +{ + public class DotMethodFPProvidedParam + { + public DotMethodFPProvidedParam(int lambdaParamNum, Type returnType, ExprNode expression) + { + LambdaParamNum = lambdaParamNum; + ReturnType = returnType; + Expression = expression; + } + + public int LambdaParamNum { get; private set; } + + public Type ReturnType { get; private set; } + + public ExprNode Expression { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/methodbase/DotMethodInputTypeMatcher.cs b/NEsper.Core/NEsper.Core/epl/methodbase/DotMethodInputTypeMatcher.cs new file mode 100755 index 000000000..93383ea61 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/methodbase/DotMethodInputTypeMatcher.cs @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.methodbase +{ + public interface DotMethodInputTypeMatcher + { + bool Matches(DotMethodFP footprint); + } + + public sealed class DotMethodInputTypeMatcherImpl : DotMethodInputTypeMatcher + { + public static readonly DotMethodInputTypeMatcher DEFAULT_ALL = new DotMethodInputTypeMatcherImpl(); + + #region DotMethodInputTypeMatcher Members + + public bool Matches(DotMethodFP footprint) + { + return true; + } + + #endregion + } + + public sealed class ProxyDotMethodInputTypeMatcher : DotMethodInputTypeMatcher + { + public Func ProcMatches { get; set; } + + public bool Matches(DotMethodFP footprint) + { + return ProcMatches.Invoke(footprint); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/methodbase/DotMethodTypeEnum.cs b/NEsper.Core/NEsper.Core/epl/methodbase/DotMethodTypeEnum.cs new file mode 100755 index 000000000..ab38d8e4f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/methodbase/DotMethodTypeEnum.cs @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.methodbase +{ + public enum DotMethodTypeEnum + { + ENUM, + DATETIME + } ; + + public static class DotMethodTypeEnumExtensions + { + public static String GetTypeName(this DotMethodTypeEnum value) { + switch(value) { + case DotMethodTypeEnum.ENUM: + return "enumeration"; + case DotMethodTypeEnum.DATETIME: + return "date-time"; + default: + throw new ArgumentException("invalid value", "value"); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/methodbase/DotMethodUtil.cs b/NEsper.Core/NEsper.Core/epl/methodbase/DotMethodUtil.cs new file mode 100755 index 000000000..a658f48f2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/methodbase/DotMethodUtil.cs @@ -0,0 +1,229 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.enummethod.dot; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.time; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.methodbase +{ + public class DotMethodUtil + { + public static DotMethodFPProvided GetProvidedFootprint(IList parameters) + { + var @params = new List(); + foreach (ExprNode node in parameters) + { + if (!(node is ExprLambdaGoesNode)) + { + @params.Add(new DotMethodFPProvidedParam(0, node.ExprEvaluator.ReturnType, node)); + continue; + } + var goesNode = (ExprLambdaGoesNode)node; + @params.Add(new DotMethodFPProvidedParam(goesNode.GoesToNames.Count, null, goesNode)); + } + return new DotMethodFPProvided(@params.ToArray()); + } + + public static DotMethodFP ValidateParametersDetermineFootprint(DotMethodFP[] footprints, DotMethodTypeEnum methodType, String methodUsedName, DotMethodFPProvided providedFootprint, DotMethodInputTypeMatcher inputTypeMatcher) + { + Boolean isLambdaApplies = DotMethodTypeEnum.ENUM == methodType; + + // determine footprint candidates strictly based on parameters + List candidates = null; + DotMethodFP bestMatch = null; + + foreach (var footprint in footprints) + { + var requiredParams = footprint.Parameters; + if (requiredParams.Length != providedFootprint.Parameters.Length) + { + continue; + } + + if (bestMatch == null) + { // take first if number of parameters matches + bestMatch = footprint; + } + + var paramMatch = true; + var count = 0; + foreach (var requiredParam in requiredParams) + { + var providedParam = providedFootprint.Parameters[count++]; + if (requiredParam.LambdaParamNum != providedParam.LambdaParamNum) + { + paramMatch = false; + } + } + + if (paramMatch) + { + if (candidates == null) + { + candidates = new List(); + } + candidates.Add(footprint); + } + } + + // if there are multiple candidates, eliminate by input (event bean collection or component collection) + if (candidates != null && candidates.Count > 1) + { + candidates + .Where(fp => !inputTypeMatcher.Matches(fp)) + .ToList() + .ForEach(fp => candidates.Remove(fp)); + } + + // handle single remaining candidate + if (candidates != null && candidates.Count == 1) + { + DotMethodFP found = candidates[0]; + ValidateSpecificTypes(methodUsedName, methodType, found.Parameters, providedFootprint.Parameters); + return found; + } + + // check all candidates in detail to see which one matches, take first one + if (candidates != null && !candidates.IsEmpty()) + { + bestMatch = candidates[0]; + var candidateIt = candidates.GetEnumerator(); + ExprValidationException firstException = null; + while (candidateIt.MoveNext() ) + { + DotMethodFP fp = candidateIt.Current; + try + { + ValidateSpecificTypes(methodUsedName, methodType, fp.Parameters, providedFootprint.Parameters); + return fp; + } + catch (ExprValidationException ex) + { + if (firstException == null) + { + firstException = ex; + } + } + } + if (firstException != null) + { + throw firstException; + } + } + var message = string.Format("Parameters mismatch for {0} method '{1}', the method ", methodType.GetTypeName(), methodUsedName); + if (bestMatch != null) + { + var buf = new StringWriter(); + buf.Write(bestMatch.ToStringFootprint(isLambdaApplies)); + buf.Write(", but receives "); + buf.Write(DotMethodFP.ToStringProvided(providedFootprint, isLambdaApplies)); + throw new ExprValidationException( + string.Format("{0}requires {1}", message, buf)); + } + + if (footprints.Length == 1) + { + throw new ExprValidationException( + string.Format("{0}requires {1}", message, footprints[0].ToStringFootprint(isLambdaApplies))); + } + else + { + var buf = new StringWriter(); + var delimiter = ""; + foreach (DotMethodFP footprint in footprints) + { + buf.Write(delimiter); + buf.Write(footprint.ToStringFootprint(isLambdaApplies)); + delimiter = ", or "; + } + + throw new ExprValidationException(message + "has multiple footprints accepting " + buf + + ", but receives " + DotMethodFP.ToStringProvided(providedFootprint, isLambdaApplies)); + } + } + + private static void ValidateSpecificTypes(String methodUsedName, DotMethodTypeEnum type, DotMethodFPParam[] foundParams, DotMethodFPProvidedParam[] @params) + { + for (int i = 0; i < foundParams.Length; i++) + { + DotMethodFPParam found = foundParams[i]; + DotMethodFPProvidedParam provided = @params[i]; + + // Lambda-type expressions not validated here + if (found.LambdaParamNum > 0) + { + continue; + } + ValidateSpecificType(methodUsedName, type, found.ParamType, found.SpecificType, provided.ReturnType, i, provided.Expression); + } + } + + public static void ValidateSpecificType(String methodUsedName, DotMethodTypeEnum type, DotMethodFPParamTypeEnum expectedTypeEnum, Type expectedTypeClass, Type providedType, int parameterNum, ExprNode parameterExpression) + { + string message = string.Format("Error validating {0} method '{1}', ", type.GetTypeName(), methodUsedName); + if (expectedTypeEnum == DotMethodFPParamTypeEnum.BOOLEAN && (!providedType.IsBoolean())) + { + throw new ExprValidationException( + string.Format("{0}expected a boolean-type result for expression parameter {1} but received {2}", + message, parameterNum, providedType.FullName)); + } + if (expectedTypeEnum == DotMethodFPParamTypeEnum.NUMERIC && (!providedType.IsNumeric())) + { + throw new ExprValidationException( + string.Format("{0}expected a number-type result for expression parameter {1} but received {2}", + message, parameterNum, providedType.FullName)); + } + if (expectedTypeEnum == DotMethodFPParamTypeEnum.SPECIFIC) + { + var boxedExpectedType = expectedTypeClass.GetBoxedType(); + var boxedProvidedType = providedType.GetBoxedType(); + if (!TypeHelper.IsSubclassOrImplementsInterface(boxedProvidedType, boxedExpectedType)) + { + throw new ExprValidationException( + string.Format("{0}expected a {1}-type result for expression parameter {2} but received {3}", + message, boxedExpectedType.Name, parameterNum, providedType.FullName)); + } + } + else if (expectedTypeEnum == DotMethodFPParamTypeEnum.TIME_PERIOD_OR_SEC) + { + if (parameterExpression is ExprTimePeriod || parameterExpression is ExprStreamUnderlyingNode) + { + return; + } + if (!providedType.IsNumeric()) + { + throw new ExprValidationException(message + + "expected a time-period expression or a numeric-type result for expression parameter " + + parameterNum + " but received " + + TypeHelper.GetTypeNameFullyQualPretty(providedType)); + } + } + else if (expectedTypeEnum == DotMethodFPParamTypeEnum.DATETIME) + { + if (!providedType.IsDateTime()) + { + throw new ExprValidationException(message + + "expected a long-typed or DateTime-typed result for expression parameter " + + parameterNum + " but received " + + TypeHelper.GetTypeNameFullyQualPretty(providedType)); + } + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/metric/MetricEventRouter.cs b/NEsper.Core/NEsper.Core/epl/metric/MetricEventRouter.cs new file mode 100755 index 000000000..957c5e1dc --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/metric/MetricEventRouter.cs @@ -0,0 +1,22 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client.metric; + +namespace com.espertech.esper.epl.metric +{ + /// + /// Interface for routing metric events for processing. + /// + public interface MetricEventRouter + { + /// Process metric event. + /// metric event to process + void Route(MetricEvent metricEvent); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/metric/MetricExec.cs b/NEsper.Core/NEsper.Core/epl/metric/MetricExec.cs new file mode 100755 index 000000000..3ca546111 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/metric/MetricExec.cs @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.metric +{ + /// + /// Interface for producing a metric events. + /// + public interface MetricExec + { + /// Execute the production of metric events. + /// provides services and scheduling + void Execute(MetricExecutionContext context); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/metric/MetricExecEngine.cs b/NEsper.Core/NEsper.Core/epl/metric/MetricExecEngine.cs new file mode 100755 index 000000000..ab0face66 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/metric/MetricExecEngine.cs @@ -0,0 +1,58 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using com.espertech.esper.client.metric; + +namespace com.espertech.esper.epl.metric +{ + /// Metrics execution producing engine metric events. + public class MetricExecEngine : MetricExec + { + private readonly String _engineURI; + private readonly MetricEventRouter _metricEventRouter; + private readonly MetricScheduleService _metricScheduleService; + private EngineMetric _lastMetric; + + /// Ctor. + /// for routing metric events + /// engine uri + /// for scheduling a new execution + /// for rescheduling the execution + public MetricExecEngine(MetricEventRouter metricEventRouter, + String engineURI, + MetricScheduleService metricScheduleService, + long interval) + { + _metricEventRouter = metricEventRouter; + _engineURI = engineURI; + _metricScheduleService = metricScheduleService; + Interval = interval; + } + + /// Returns reporting interval. + /// reporting interval + public long Interval { get; private set; } + + #region MetricExec Members + + public void Execute(MetricExecutionContext context) + { + long inputCount = context.Services.FilterService.NumEventsEvaluated; + long schedDepth = context.Services.SchedulingService.ScheduleHandleCount; + long deltaInputCount = _lastMetric == null ? inputCount : inputCount - _lastMetric.InputCount; + var metric = new EngineMetric(_engineURI, _metricScheduleService.CurrentTime, inputCount, deltaInputCount, + schedDepth); + _lastMetric = metric; + _metricEventRouter.Route(metric); + _metricScheduleService.Add(Interval, this); + } + + #endregion + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/metric/MetricExecStatement.cs b/NEsper.Core/NEsper.Core/epl/metric/MetricExecStatement.cs new file mode 100755 index 000000000..856b557f7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/metric/MetricExecStatement.cs @@ -0,0 +1,76 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using com.espertech.esper.client.metric; + +namespace com.espertech.esper.epl.metric +{ + /// Metrics execution producing statement metric events. + public class MetricExecStatement : MetricExec + { + private readonly MetricEventRouter metricEventRouter; + private readonly MetricScheduleService metricScheduleService; + private readonly int statementGroup; + + private long interval; + + /// Ctor. + /// for routing metric events + /// for scheduling a new execution + /// for rescheduling the execution + /// group number of statement group + public MetricExecStatement(MetricEventRouter metricEventRouter, MetricScheduleService metricScheduleService, long interval, int statementGroup) + { + this.metricEventRouter = metricEventRouter; + this.metricScheduleService = metricScheduleService; + this.interval = interval; + this.statementGroup = statementGroup; + } + + public void Execute(MetricExecutionContext context) + { + long timestamp = metricScheduleService.CurrentTime; + StatementMetric[] metrics = context.StatementMetricRepository.ReportGroup(statementGroup); + if (metrics != null) + { + for (int i = 0; i < metrics.Length; i++) + { + StatementMetric metric = metrics[i]; + if (metric != null) + { + metric.Timestamp = timestamp; + metricEventRouter.Route(metrics[i]); + } + } + } + + if (interval != -1) + { + metricScheduleService.Add(interval, this); + } + } + + /// + /// Set a new interval, cancels the existing schedule, re-establishes the new schedule if the interval is a positive number. + /// + public long Interval + { + get { return interval; } + set + { + interval = value; + metricScheduleService.Remove(this); + if (interval > 0) + { + metricScheduleService.Add(interval, this); + } + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/metric/MetricExecutionContext.cs b/NEsper.Core/NEsper.Core/epl/metric/MetricExecutionContext.cs new file mode 100755 index 000000000..2c17edc2d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/metric/MetricExecutionContext.cs @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.core.service; + + +namespace com.espertech.esper.epl.metric +{ + /// Execution context for metrics reporting executions. + public class MetricExecutionContext + { + /// Ctor. + /// services context + /// for routing events + /// for getting statement data + public MetricExecutionContext(EPServicesContext epServicesContext, EPRuntime runtime, StatementMetricRepository statementMetricRepository) + { + Services = epServicesContext; + Runtime = runtime; + StatementMetricRepository = statementMetricRepository; + } + + /// Returns services. + /// services + public EPServicesContext Services { get; private set; } + + /// Returns runtime + /// runtime + public EPRuntime Runtime { get; private set; } + + /// Returns statement metric holder + /// holder for metrics + public StatementMetricRepository StatementMetricRepository { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/metric/MetricReportingPath.cs b/NEsper.Core/NEsper.Core/epl/metric/MetricReportingPath.cs new file mode 100755 index 000000000..75752f624 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/metric/MetricReportingPath.cs @@ -0,0 +1,44 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.compat.logging; + +namespace com.espertech.esper.epl.metric +{ + /// Global bool for enabling and disable metrics reporting. + public class MetricReportingPath + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + /// Public access. + private static bool _isMetricsEnabled = false; + + /// + /// Sets execution path debug logging. + /// + /// true if metric reporting should be enabled + public static bool IsMetricsEnabled + { + get { return _isMetricsEnabled; } + set + { + if (value) + { + Log.Info( + "Metrics reporting has been enabled, this setting takes affect for all engine instances at engine initialization time."); + } + else + { + Log.Debug( + "Metrics reporting has been disabled, this setting takes affect for all engine instances at engine initialization time."); + } + _isMetricsEnabled = value; + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/metric/MetricReportingService.cs b/NEsper.Core/NEsper.Core/epl/metric/MetricReportingService.cs new file mode 100755 index 000000000..56dca6402 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/metric/MetricReportingService.cs @@ -0,0 +1,76 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.core.service; + +namespace com.espertech.esper.epl.metric +{ + /// + /// Metrics reporting service for instrumentation data publishing, if enabled. + /// + public interface MetricReportingService : IDisposable + { + /// + /// Gets the performance collector. + /// + /// The performance collector. + PerformanceCollector PerformanceCollector { get; } + + /// Sets runtime and services. + /// runtime + /// services + void SetContext(EPRuntime runtime, EPServicesContext servicesContext); + + /// Indicates current engine time. + /// engine time + void ProcessTimeEvent(long currentTime); + + /// + /// Account for statement execution time. + /// + /// statement handle + /// The cpu time. + /// The wall time. + /// The num input. + void AccountTime(StatementMetricHandle metricsHandle, long cpuTime, long wallTime, int numInput); + + /// Account for statement output row counting. + /// statement handle + /// number of insert stream rows + /// number of remove stream rows + void AccountOutput(StatementMetricHandle handle, int numIStream, int numRStream); + + /// Returns for a new statement a handle for later accounting. + /// statement id + /// statement name + /// handle + StatementMetricHandle GetStatementHandle(int statementId, string statementName); + + /// Change the reporting interval for the given statement group name. + /// group name + /// new interval, or zero or negative value to disable reporting + void SetMetricsReportingInterval(String stmtGroupName, long newInterval); + + /// Disable metrics reporting for statement. + /// statement name + void SetMetricsReportingStmtDisabled(String statementName); + + /// Enable metrics reporting for statement. + /// statement name + void SetMetricsReportingStmtEnabled(String statementName); + + /// Enables metrics reporting globally. + void SetMetricsReportingEnabled(); + + /// Disables metrics reporting globally. + void SetMetricsReportingDisabled(); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/metric/MetricReportingServiceImpl.cs b/NEsper.Core/NEsper.Core/epl/metric/MetricReportingServiceImpl.cs new file mode 100755 index 000000000..aa21c3572 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/metric/MetricReportingServiceImpl.cs @@ -0,0 +1,301 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.client.metric; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.service; + +namespace com.espertech.esper.epl.metric +{ + /// + /// Metrics reporting. + /// + /// Reports for all statements even if not in a statement group, i.e. statement in default group. + /// + public class MetricReportingServiceImpl + : MetricReportingServiceSPI + , MetricEventRouter + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly MetricsReportingConfig _specification; + private readonly String _engineUri; + + private volatile MetricExecutionContext _executionContext; + + private bool _isScheduled; + private readonly MetricScheduleService _schedule; + private readonly StatementMetricRepository _stmtMetricRepository; + + private MetricExecEngine _metricExecEngine; + private MetricExecStatement _metricExecStmtGroupDefault; + private readonly IDictionary _statementGroupExecutions; + + private readonly IDictionary _statementMetricHandles; + private readonly MetricsExecutor _metricsExecutor; + + /// Ctor. + /// configuration + /// engine URI + public MetricReportingServiceImpl(MetricsReportingConfig specification, String engineUri) + { + _specification = specification; + _engineUri = engineUri; + _schedule = new MetricScheduleService(); + + _stmtMetricRepository = new StatementMetricRepository(engineUri, specification); + _statementGroupExecutions = new LinkedHashMap(); + _statementMetricHandles = new Dictionary(); + StatementOutputHooks = new CopyOnWriteArraySet(); + + if (specification.IsThreading) + { + _metricsExecutor = new MetricsExecutorThreaded(engineUri); + } + else + { + _metricsExecutor = new MetricsExecutorUnthreaded(); + } + } + + /// + /// Gets the performance collector. + /// + /// The performance collector. + public PerformanceCollector PerformanceCollector + { + get { return AccountTime; } + } + + public void AddStatementResultListener(StatementResultListener listener) + { + StatementOutputHooks.Add(listener); + } + + public void RemoveStatementResultListener(StatementResultListener listener) + { + StatementOutputHooks.Remove(listener); + } + + public ICollection StatementOutputHooks { get; private set; } + + public void SetContext(EPRuntime runtime, EPServicesContext servicesContext) + { + var metricsExecutionContext = new MetricExecutionContext(servicesContext, runtime, _stmtMetricRepository); + + // create all engine and statement executions + _metricExecEngine = new MetricExecEngine(this, _engineUri, _schedule, _specification.EngineInterval); + _metricExecStmtGroupDefault = new MetricExecStatement(this, _schedule, _specification.StatementInterval, 0); + + int countGroups = 1; + foreach (var entry in _specification.StatementGroups) + { + var config = entry.Value; + var metricsExecution = new MetricExecStatement(this, _schedule, config.Interval, countGroups); + _statementGroupExecutions.Put(entry.Key, metricsExecution); + countGroups++; + } + + // last assign this volatile variable so the time event processing may schedule callbacks + _executionContext = metricsExecutionContext; + } + + public void ProcessTimeEvent(long timeEventTime) + { + if (!MetricReportingPath.IsMetricsEnabled) + { + return; + } + + _schedule.CurrentTime = timeEventTime; + if (!_isScheduled) + { + if (_executionContext != null) + { + ScheduleExecutions(); + _isScheduled = true; + } + else + { + return; // not initialized yet, race condition and must wait till initialized + } + } + + // fast evaluation against nearest scheduled time + var nearestTime = _schedule.NearestTime; + if ((nearestTime == null) || (nearestTime > timeEventTime)) + { + return; + } + + // get executions + var executions = new List(2); + _schedule.Evaluate(executions); + if (executions.IsEmpty()) + { + return; + } + + // execute + if (_executionContext == null) + { + Log.Debug(".processTimeEvent No execution context"); + return; + } + + foreach (MetricExec execution in executions) + { + _metricsExecutor.Execute(execution, _executionContext); + } + } + + public void Dispose() + { + _schedule.Clear(); + _metricsExecutor.Dispose(); + } + + public void Route(MetricEvent metricEvent) + { + _executionContext.Runtime.SendEvent(metricEvent); + } + + public void AccountTime(StatementMetricHandle metricsHandle, long deltaCPU, long deltaWall, int numInputEvents) + { + _stmtMetricRepository.AccountTimes(metricsHandle, deltaCPU, deltaWall, numInputEvents); + } + + public void AccountOutput(StatementMetricHandle handle, int numIStream, int numRStream) + { + _stmtMetricRepository.AccountOutput(handle, numIStream, numRStream); + } + + public StatementMetricHandle GetStatementHandle(int statementId, string statementName) + { + if (!MetricReportingPath.IsMetricsEnabled) + { + return null; + } + + StatementMetricHandle handle = _stmtMetricRepository.AddStatement(statementName); + _statementMetricHandles.Put(statementName, handle); + return handle; + } + + public void Observe(StatementLifecycleEvent theEvent) + { + if (!MetricReportingPath.IsMetricsEnabled) + { + return; + } + + if (theEvent.EventType == StatementLifecycleEvent.LifecycleEventType.STATECHANGE) + { + if (theEvent.Statement.IsDisposed) + { + _stmtMetricRepository.RemoveStatement(theEvent.Statement.Name); + _statementMetricHandles.Remove(theEvent.Statement.Name); + } + } + } + + public void SetMetricsReportingInterval(String stmtGroupName, long newInterval) + { + if (stmtGroupName == null) + { + _metricExecStmtGroupDefault.Interval = newInterval; + return; + } + + MetricExecStatement exec = _statementGroupExecutions.Get(stmtGroupName); + if (exec == null) + { + throw new ArgumentException("Statement group by name '" + stmtGroupName + "' could not be found"); + } + exec.Interval = newInterval; + } + + private bool IsConsiderSchedule(long value) + { + if ((value > 0) && (value < long.MaxValue)) + { + return true; + } + return false; + } + + public void SetMetricsReportingStmtDisabled(String statementName) + { + StatementMetricHandle handle = _statementMetricHandles.Get(statementName); + if (handle == null) + { + throw new ConfigurationException("Statement by name '" + statementName + "' not found in metrics collection"); + } + handle.IsEnabled = false; + } + + public void SetMetricsReportingStmtEnabled(String statementName) + { + StatementMetricHandle handle = _statementMetricHandles.Get(statementName); + if (handle == null) + { + throw new ConfigurationException("Statement by name '" + statementName + "' not found in metrics collection"); + } + handle.IsEnabled = true; + } + + public void SetMetricsReportingEnabled() + { + if (!_specification.IsEnableMetricsReporting) + { + throw new ConfigurationException("Metrics reporting must be enabled through initialization-time configuration"); + } + ScheduleExecutions(); + MetricReportingPath.IsMetricsEnabled = true; + } + + public void SetMetricsReportingDisabled() + { + _schedule.Clear(); + MetricReportingPath.IsMetricsEnabled = false; + } + + private void ScheduleExecutions() + { + if (!_specification.IsEnableMetricsReporting) + { + return; + } + + if (IsConsiderSchedule(_metricExecEngine.Interval)) + { + _schedule.Add(_metricExecEngine.Interval, _metricExecEngine); + } + + // schedule each statement group, count the "default" group as the first group + if (IsConsiderSchedule(_metricExecStmtGroupDefault.Interval)) + { + _schedule.Add(_metricExecStmtGroupDefault.Interval, _metricExecStmtGroupDefault); + } + + foreach (MetricExecStatement metricsExecution in _statementGroupExecutions.Values) + { + if (IsConsiderSchedule(metricsExecution.Interval)) + { + _schedule.Add(metricsExecution.Interval, metricsExecution); + } + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/metric/MetricReportingServiceSPI.cs b/NEsper.Core/NEsper.Core/epl/metric/MetricReportingServiceSPI.cs new file mode 100755 index 000000000..5356ffe5c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/metric/MetricReportingServiceSPI.cs @@ -0,0 +1,30 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.core.service; + +namespace com.espertech.esper.epl.metric +{ + /// SPI for metrics activity. + public interface MetricReportingServiceSPI : MetricReportingService + { + /// Add stmt result listener. + /// to add + void AddStatementResultListener(StatementResultListener listener); + + /// Remove stmt result listener. + /// to remove + void RemoveStatementResultListener(StatementResultListener listener); + + /// Returns output hooks. + /// hooks. + ICollection StatementOutputHooks { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/metric/MetricScheduleService.cs b/NEsper.Core/NEsper.Core/epl/metric/MetricScheduleService.cs new file mode 100755 index 000000000..fc200a4d6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/metric/MetricScheduleService.cs @@ -0,0 +1,142 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; + +namespace com.espertech.esper.epl.metric +{ + /// + /// Scheduling for metrics execution is handled by this service. + /// + public sealed class MetricScheduleService : MetricTimeSource + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly SortedDictionary> _timeHandleMap; + + // Current time - used for evaluation as well as for adding new handles + private long? _currentTime; + + private long? _nearestTime; + + /// Constructor. + public MetricScheduleService() + { + _timeHandleMap = new SortedDictionary>(); + } + + /// + /// Gets or sets the current time. + /// + /// The current time. + public long CurrentTime + { + get { return _currentTime.GetValueOrDefault(); } + set + { + lock (this) + { + _currentTime = value; + } + } + } + + /// Clears the schedule. + public void Clear() + { + Log.Debug("Clearing scheduling service"); + _timeHandleMap.Clear(); + _nearestTime = null; + } + + /// Adds an execution to the schedule. + /// offset to add at + /// execution to add + public void Add(long afterMSec, MetricExec execution) + { + lock (this) + { + if (execution == null) + { + throw new ArgumentException("Unexpected parameters : null execution"); + } + long triggerOnTime = _currentTime.GetValueOrDefault() + afterMSec; + IList handleSet = _timeHandleMap.Get(triggerOnTime); + if (handleSet == null) + { + handleSet = new List(); + _timeHandleMap.Put(triggerOnTime, handleSet); + } + handleSet.Add(execution); + + _nearestTime = _timeHandleMap.Keys.First(); + } + } + + /// Evaluate the schedule and populates executions, if any. + /// to populate + public void Evaluate(ICollection handles) + { + lock (this) + { + var current = _currentTime.GetValueOrDefault() + 1; + var headMap = _timeHandleMap.Where(keyValuePair => keyValuePair.Key < current); + + // First determine all triggers to shoot + var removeKeys = new List(); + foreach (KeyValuePair> entry in headMap) + { + long key = entry.Key; + IList value = entry.Value; + removeKeys.Add(key); + foreach (MetricExec handle in value) + { + handles.Add(handle); + } + } + + // Remove all triggered msec values + foreach (long key in removeKeys) + { + _timeHandleMap.Remove(key); + } + + if (_timeHandleMap.IsNotEmpty()) + { + _nearestTime = _timeHandleMap.Keys.First(); + } + else + { + _nearestTime = null; + } + } + } + + /// Returns nearest scheduled time. + /// nearest scheduled time, or null if none/empty schedule. + public long? NearestTime + { + get { return _nearestTime; } + } + + /// Remove from schedule an execution. + /// to remove + public void Remove(MetricExec metricExec) + { + foreach (var entry in _timeHandleMap) + { + entry.Value.Remove(metricExec); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/metric/MetricTimeSource.cs b/NEsper.Core/NEsper.Core/epl/metric/MetricTimeSource.cs new file mode 100755 index 000000000..2fa6a6783 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/metric/MetricTimeSource.cs @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.metric +{ + /// + /// Interface for the time of the metrics generation. + /// + public interface MetricTimeSource + { + /// Returns current time for metrics reporting. + /// metrics current time + long CurrentTime { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/metric/MetricsExecutor.cs b/NEsper.Core/NEsper.Core/epl/metric/MetricsExecutor.cs new file mode 100755 index 000000000..b0774050c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/metric/MetricsExecutor.cs @@ -0,0 +1,21 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.metric +{ + /// Executor for metrics executions. + public interface MetricsExecutor : IDisposable + { + /// Execute a metrics execution. + /// to execute + /// context in which to execute + void Execute(MetricExec execution, MetricExecutionContext executionContext); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/metric/MetricsExecutorThreaded.cs b/NEsper.Core/NEsper.Core/epl/metric/MetricsExecutorThreaded.cs new file mode 100755 index 000000000..92e309c0d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/metric/MetricsExecutorThreaded.cs @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat.threading; + +namespace com.espertech.esper.epl.metric +{ + /// + /// Metrics executor relying on a cached threadpool. + /// + public class MetricsExecutorThreaded : MetricsExecutor + { + private readonly IExecutorService _threadPool; + + /// Ctor. + /// engine URI + public MetricsExecutorThreaded(String engineURI) + { + _threadPool = new DedicatedExecutorService("Metrics", 1); + } + + public void Execute(MetricExec execution, MetricExecutionContext executionContext) + { + _threadPool.Submit(() => execution.Execute(executionContext)); + } + + public void Dispose() + { + _threadPool.Shutdown(); + _threadPool.AwaitTermination(new TimeSpan(0, 0, 10)); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/metric/MetricsExecutorUnthreaded.cs b/NEsper.Core/NEsper.Core/epl/metric/MetricsExecutorUnthreaded.cs new file mode 100755 index 000000000..d0f43931f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/metric/MetricsExecutorUnthreaded.cs @@ -0,0 +1,24 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.metric +{ + /// Metrics executor executing in-thread. + public class MetricsExecutorUnthreaded : MetricsExecutor + { + public void Execute(MetricExec execution, MetricExecutionContext executionContext) + { + execution.Execute(executionContext); + } + + public void Dispose() + { + // no action required, nothing to stop + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/metric/StatementMetricArray.cs b/NEsper.Core/NEsper.Core/epl/metric/StatementMetricArray.cs new file mode 100755 index 000000000..0362e761d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/metric/StatementMetricArray.cs @@ -0,0 +1,223 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using com.espertech.esper.client.metric; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.threading; + +namespace com.espertech.esper.epl.metric +{ + /// + /// Holder for statement group's statement metrics. + /// + /// Changes to StatementMetric instances must be done in a read-lock: + ///
    +    /// getRwLock.readLock.Lock()
    +    /// metric = GetAddMetric(index) metric.AccountFor(cpu, wall, etc)
    +    /// getRwLock.readLock.unLock()
    +    /// 
    + /// namespace com.espertech.esper.epl.metric { + /// + /// All other changes are done under write lock for this class. + /// + /// This is a collection backed by an array that grows by 50% each time expanded, maintains + /// a free/busy list of statement names, maintains an element number of last used element. + /// + /// The flush operaton copies the complete array, thereby keeping array size. Statement names + /// are only removed on the next flush. + ///
    + public class StatementMetricArray + { + private readonly String engineURI; + + // Lock + // Read lock applies to each current transaction on a StatementMetric instance + // Write lock applies to flush and to add a new statement + private readonly bool isReportInactive; + private readonly ICollection removedStatementNames; + private readonly IReaderWriterLock rwLock; + + // Active statements + + // Count of active statements + private int currentLastElement; + + // Flushed metric per statement + private volatile StatementMetric[] metrics; + private String[] statementNames; + + // Statements ids to remove with the next flush + + /// Ctor. + /// engine URI + /// name of statement group + /// initial size of array + /// true to indicate to report on inactive statements + public StatementMetricArray(String engineURI, String name, int initialSize, bool isReportInactive) + { + this.engineURI = engineURI; + this.isReportInactive = isReportInactive; + + metrics = new StatementMetric[initialSize]; + statementNames = new String[initialSize]; + currentLastElement = -1; + rwLock = ReaderWriterLockManager.CreateLock(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + removedStatementNames = new HashSet(); + } + + /// Returns the read-write lock, for read-lock when modifications are made. + /// lock + public IReaderWriterLock RWLock + { + get { return rwLock; } + } + + /// Returns maximum collection size (last used element), which may not truely reflect the number of actual statements held as some slots may empty up when statements are removed. + /// known maximum size + public int SizeLastElement + { + get { return currentLastElement + 1; } + } + + /// + /// Remove a statement. + /// + /// Next flush actually frees the slot that this statement occupied. + /// + /// to remove + public void RemoveStatement(String statementName) + { + using (rwLock.AcquireWriteLock()) { + removedStatementNames.Add(statementName); + if (removedStatementNames.Count > 1000) + { + for (int i = 0; i <= currentLastElement; i++) + { + if (removedStatementNames.Contains(statementNames[i])) + { + statementNames[i] = null; + } + } + + removedStatementNames.Clear(); + } + } + } + + /// + /// Adds a statement and returns the index added at. + /// + /// May reuse an empty slot, grow the underlying array, or append to the end. + /// + /// to add + /// index added to + public int AddStatementGetIndex(String statementName) + { + using (rwLock.AcquireWriteLock()) { + // see if there is room + if ((currentLastElement + 1) < metrics.Length) { + currentLastElement++; + statementNames[currentLastElement] = statementName; + return currentLastElement; + } + + // no room, try to use an existing slot of a removed statement + for (int i = 0; i < statementNames.Length; i++) { + if (statementNames[i] == null) { + statementNames[i] = statementName; + if ((i + 1) > currentLastElement) { + currentLastElement = i; + } + return i; + } + } + + // still no room, expand storage by 50% + var newSize = (int) (metrics.Length*1.5); + var newStatementNames = new String[newSize]; + var newMetrics = new StatementMetric[newSize]; + Array.Copy(statementNames, 0, newStatementNames, 0, statementNames.Length); + Array.Copy(metrics, 0, newMetrics, 0, metrics.Length); + + statementNames = newStatementNames; + metrics = newMetrics; + + currentLastElement++; + statementNames[currentLastElement] = statementName; + + return currentLastElement; + } + } + + /// + /// Flushes the existing metrics via array copy and swap. + /// + /// May report all statements (empty and non-empty slots) and thereby null values. + /// + /// Returns null to indicate no reports to do. + /// + /// metrics + public StatementMetric[] FlushMetrics() + { + using (rwLock.AcquireWriteLock()) { + bool isEmpty = false; + if (currentLastElement == -1) { + isEmpty = true; + } + + // first fill in the blanks if there are no reports and we report inactive statements + if (isReportInactive) { + for (int i = 0; i <= currentLastElement; i++) { + if (statementNames[i] != null) { + metrics[i] = new StatementMetric(engineURI, statementNames[i]); + } + } + } + + // remove statement ids that disappeared during the interval + if ((currentLastElement > -1) && (removedStatementNames.IsNotEmpty())) { + for (int i = 0; i <= currentLastElement; i++) { + if (removedStatementNames.Contains(statementNames[i])) { + statementNames[i] = null; + } + } + } + + // adjust last used element + while ((currentLastElement != -1) && (statementNames[currentLastElement] == null)) { + currentLastElement--; + } + + if (isEmpty) { + return null; // no copies made if empty collection + } + + // perform flush + var newMetrics = new StatementMetric[metrics.Length]; + StatementMetric[] oldMetrics = metrics; + metrics = newMetrics; + return oldMetrics; + } + } + + /// Returns an existing or creates a new statement metric for the index. + /// of statement + /// metric to modify under read lock + public StatementMetric GetAddMetric(int index) + { + StatementMetric metric = metrics[index]; + if (metric == null) { + metric = new StatementMetric(engineURI, statementNames[index]); + metrics[index] = metric; + } + return metric; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/metric/StatementMetricHandle.cs b/NEsper.Core/NEsper.Core/epl/metric/StatementMetricHandle.cs new file mode 100755 index 000000000..f546406c7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/metric/StatementMetricHandle.cs @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.metric +{ + /// Handle for statements metric reporting by runtime. + public class StatementMetricHandle + { + private readonly int groupNum; + private readonly int index; + + /// Ctor. + /// group number, zero for default group + /// index slot + public StatementMetricHandle(int groupNum, int index) + { + this.groupNum = groupNum; + this.index = index; + this.IsEnabled = true; + } + + /// Returns group number for statement. + /// group number + public int GroupNum + { + get { return groupNum; } + } + + /// Returns slot number of metric. + /// metric index + public int Index + { + get { return index; } + } + + /// Gets or sets an indicator that if true is enabled for statement. + /// enabled flag + public bool IsEnabled { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/metric/StatementMetricHandleExtension.cs b/NEsper.Core/NEsper.Core/epl/metric/StatementMetricHandleExtension.cs new file mode 100755 index 000000000..5dae5e5f8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/metric/StatementMetricHandleExtension.cs @@ -0,0 +1,122 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Runtime.InteropServices; + +using com.espertech.esper.compat; + +namespace com.espertech.esper.epl.metric +{ + public static class StatementMetricHandleExtension + { +#if MONO + [StructLayout(LayoutKind.Auto)] + public struct Rusage + { + /* user time used */ + public long ru_utime_sec; + public long ru_utime_usec; + /* system time used */ + public long ru_stime_sec; + public long ru_stime_usec; + public long ru_maxrss; /* maximum resident set size */ + public long ru_ixrss; /* integral shared memory size */ + public long ru_idrss; /* integral unshared data size */ + public long ru_isrss; /* integral unshared stack size */ + public long ru_minflt; /* page reclaims */ + public long ru_majflt; /* page faults */ + public long ru_nswap; /* swaps */ + public long ru_inblock; /* block input operations */ + public long ru_oublock; /* block output operations */ + public long ru_msgsnd; /* messages sent */ + public long ru_msgrcv; /* messages received */ + public long ru_nsignals; /* signals received */ + public long ru_nvcsw; /* voluntary context switches */ + public long ru_nivcsw; /* involuntary " */ + } + + public const int RUSAGE_SELF = 0; + public const int RUSAGE_THREAD = 1; + + [DllImport("Kernel32.dll")] + private static extern bool getrusage(int who, ref Rusage usage); +#else + [DllImport("Kernel32.dll", EntryPoint = "GetCurrentThreadId", ExactSpelling = true)] + public static extern Int32 GetCurrentWin32ThreadId(); + + [DllImport("kernel32.dll", SetLastError = true)] + static extern bool GetThreadTimes(IntPtr hThread, out long lpCreationTime, + out long lpExitTime, out long lpKernelTime, out long lpUserTime); + + [DllImport("kernel32.dll")] + static extern IntPtr GetCurrentThread(); +#endif + + /// + /// Calls the specified observable call. + /// + /// The statement metric handle. + /// The perf collector. + /// The observable call. + /// The num input. + public static void Call(this StatementMetricHandle statementMetricHandle, PerformanceCollector perfCollector, Action observableCall, int numInput = 1) + { + if ((MetricReportingPath.IsMetricsEnabled) && statementMetricHandle.IsEnabled) + { +#if MONO + long lCreationTime, lExitTime; + long lUserTimeA, lKernelTimeA; + long lUserTimeB, lKernelTimeB; + //IntPtr thread = GetCurrentThread(); + long lWallTimeA = PerformanceObserverMono.NanoTime; + + //GetThreadTimes(out lUserTimeA, out lKernelTimeA); + observableCall.Invoke(); + //GetThreadTimes(out lUserTimeB, out lKernelTimeB); + + long lWallTimeB = PerformanceObserverMono.NanoTime; + long lTimeA = 0; // lKernelTimeA + lUserTimeA + long lTimeB = 0; // lKernelTimeB + lUserTimeB + + perfCollector.Invoke( + metricValue, + 100 * (lTimeB - lTimeA), + lWallTimeB - lWallTimeA); +#else + + long lCreationTime, lExitTime; + long lUserTimeA, lKernelTimeA; + long lUserTimeB, lKernelTimeB; + IntPtr thread = GetCurrentThread(); + long lWallTimeA = PerformanceObserverMono.NanoTime; + + GetThreadTimes(thread, out lCreationTime, out lExitTime, out lUserTimeA, out lKernelTimeA); + observableCall.Invoke(); + GetThreadTimes(thread, out lCreationTime, out lExitTime, out lUserTimeB, out lKernelTimeB); + + long lWallTimeB = PerformanceObserverMono.NanoTime; + long lTimeA = (lKernelTimeA + lUserTimeA); + long lTimeB = (lKernelTimeB + lUserTimeB); + + perfCollector.Invoke( + statementMetricHandle, + 100 * (lTimeB - lTimeA), + lWallTimeB - lWallTimeA, + numInput); +#endif + } + else + { + observableCall.Invoke(); + } + } + } + + public delegate void PerformanceCollector(StatementMetricHandle statementMetricHandle, long cpuTime, long wallTime, int numInput); +} diff --git a/NEsper.Core/NEsper.Core/epl/metric/StatementMetricRepository.cs b/NEsper.Core/NEsper.Core/epl/metric/StatementMetricRepository.cs new file mode 100755 index 000000000..3fb13311a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/metric/StatementMetricRepository.cs @@ -0,0 +1,152 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using com.espertech.esper.client; +using com.espertech.esper.client.metric; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.type; + +namespace com.espertech.esper.epl.metric +{ + /// + /// A repository for all statement metrics that organizes statements into statement groups. + /// + /// At a minimum there is one group (the default) of index zero. + public class StatementMetricRepository + { + private readonly StatementMetricArray[] _groupMetrics; + private readonly MetricsReportingConfig _specification; + private readonly IDictionary _statementGroups; + + /// Ctor. + /// engine URI + /// specifies statement groups + public StatementMetricRepository(String engineURI, + MetricsReportingConfig specification) + { + _specification = specification; + int numGroups = specification.StatementGroups.Count + 1; // +1 for default group (remaining stmts) + _groupMetrics = new StatementMetricArray[numGroups]; + + // default group + _groupMetrics[0] = new StatementMetricArray(engineURI, "group-default", 100, false); + + // initialize all other groups + int countGroups = 1; + foreach (var entry in specification.StatementGroups) + { + MetricsReportingConfig.StmtGroupMetrics config = entry.Value; + + int initialNumStmts = config.NumStatements; + if (initialNumStmts < 10) + { + initialNumStmts = 10; + } + _groupMetrics[countGroups] = new StatementMetricArray(engineURI, "group-" + countGroups, initialNumStmts, + config.IsReportInactive); + countGroups++; + } + + _statementGroups = new Dictionary(); + } + + /// Add a statement, inspecting the statement name and adding it to a statement group or the default group, if none. + /// name to inspect + /// handle for statement + public StatementMetricHandle AddStatement(String stmtName) + { + // determine group + int countGroups = 1; + int groupNumber = -1; + foreach (var entry in _specification.StatementGroups) + { + IList> patterns = entry.Value.Patterns; + bool result = StringPatternSetUtil.Evaluate(entry.Value.IsDefaultInclude, patterns, stmtName); + + if (result) + { + groupNumber = countGroups; + break; + } + countGroups++; + } + + // assign to default group if none other apply + if (groupNumber == -1) + { + groupNumber = 0; + } + + int index = _groupMetrics[groupNumber].AddStatementGetIndex(stmtName); + + _statementGroups.Put(stmtName, groupNumber); + + return new StatementMetricHandle(groupNumber, index); + } + + /// Remove statement. + /// to remove + public void RemoveStatement(String stmtName) + { + int? group = _statementGroups.Delete(stmtName); + if (group != null) + { + _groupMetrics[group.Value].RemoveStatement(stmtName); + } + } + + /// + /// Account statement times. + /// + /// statement handle + /// time + /// time + /// The num input. + public void AccountTimes(StatementMetricHandle handle, + long cpu, + long wall, + int numInput) + { + StatementMetricArray array = _groupMetrics[handle.GroupNum]; + using (array.RWLock.AcquireReadLock()) + { + StatementMetric metric = array.GetAddMetric(handle.Index); + metric.IncrementTime(cpu, wall); + metric.AddNumInput(numInput); + } + } + + /// Account row output. + /// statement handle + /// num rows insert stream + /// num rows remove stream + public void AccountOutput(StatementMetricHandle handle, + int numIStream, + int numRStream) + { + StatementMetricArray array = _groupMetrics[handle.GroupNum]; + using (array.RWLock.AcquireReadLock()) + { + StatementMetric metric = array.GetAddMetric(handle.Index); + metric.AddNumOutputIStream(numIStream); + metric.AddNumOutputRStream(numRStream); + } + } + + /// Report for a given statement group. + /// to report + /// metrics or null if none + public StatementMetric[] ReportGroup(int group) + { + return _groupMetrics[group].FlushMetrics(); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/named/NamedWindowConsumerCallback.cs b/NEsper.Core/NEsper.Core/epl/named/NamedWindowConsumerCallback.cs new file mode 100755 index 000000000..d87431002 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/named/NamedWindowConsumerCallback.cs @@ -0,0 +1,75 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections; +using System.Collections.Generic; + +using com.espertech.esper.client; + +namespace com.espertech.esper.epl.named +{ + public interface NamedWindowConsumerCallback : IEnumerable + { + void Stopped(NamedWindowConsumerView namedWindowConsumerView); + } + + public class ProxyNamedWindowConsumerCallback : NamedWindowConsumerCallback + { + public Func> ProcGetEnumerator { get; set; } + public Action ProcStopped { get; set; } + + /// + /// Initializes a new instance of the class. + /// + public ProxyNamedWindowConsumerCallback() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The get enumerator. + /// The stopped. + public ProxyNamedWindowConsumerCallback(Func> procGetEnumerator, + Action procStopped) + { + ProcGetEnumerator = procGetEnumerator; + ProcStopped = procStopped; + } + + /// + /// Returns an enumerator that iterates through a collection. + /// + /// + /// An object that can be used to iterate through the collection. + /// + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + /// + /// Gets the enumerator. + /// + /// + public IEnumerator GetEnumerator() + { + return ProcGetEnumerator.Invoke(); + } + + /// + /// Stoppeds the specified named window consumer view. + /// + /// The named window consumer view. + public void Stopped(NamedWindowConsumerView namedWindowConsumerView) + { + ProcStopped.Invoke(namedWindowConsumerView); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/named/NamedWindowConsumerDesc.cs b/NEsper.Core/NEsper.Core/epl/named/NamedWindowConsumerDesc.cs new file mode 100755 index 000000000..284ccc044 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/named/NamedWindowConsumerDesc.cs @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.property; + +namespace com.espertech.esper.epl.named +{ + public class NamedWindowConsumerDesc + { + public NamedWindowConsumerDesc(IList filterList, PropertyEvaluator optPropertyEvaluator, AgentInstanceContext agentInstanceContext) { + FilterList = filterList; + OptPropertyEvaluator = optPropertyEvaluator; + AgentInstanceContext = agentInstanceContext; + } + + public IList FilterList { get; private set; } + + public PropertyEvaluator OptPropertyEvaluator { get; private set; } + + public AgentInstanceContext AgentInstanceContext { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/named/NamedWindowConsumerLatch.cs b/NEsper.Core/NEsper.Core/epl/named/NamedWindowConsumerLatch.cs new file mode 100755 index 000000000..b138cf5e1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/named/NamedWindowConsumerLatch.cs @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.Threading; + +using com.espertech.esper.core.context.util; + +namespace com.espertech.esper.epl.named +{ + public abstract class NamedWindowConsumerLatch + { + protected NamedWindowConsumerLatch(NamedWindowDeltaData deltaData, IDictionary> dispatchTo) + { + DeltaData = deltaData; + DispatchTo = dispatchTo; + } + + public abstract Thread CurrentThread { get; } + public abstract void Await(); + public abstract void Done(); + + public NamedWindowDeltaData DeltaData { get; private set; } + + public IDictionary> DispatchTo { get; private set; } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/named/NamedWindowConsumerLatchFactory.cs b/NEsper.Core/NEsper.Core/epl/named/NamedWindowConsumerLatchFactory.cs new file mode 100755 index 000000000..45764b8d5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/named/NamedWindowConsumerLatchFactory.cs @@ -0,0 +1,95 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.timer; + +namespace com.espertech.esper.epl.named +{ + /// + /// Class to hold a current latch per named window. + /// + public class NamedWindowConsumerLatchFactory + { + private NamedWindowConsumerLatchSpin _currentLatchSpin; + private NamedWindowConsumerLatchWait _currentLatchWait; + + /// + /// Ctor. + /// + /// the factory name + /// + /// the number of milliseconds latches will await maximually + /// the blocking strategy to employ + /// time source provider + /// + public NamedWindowConsumerLatchFactory(string name, bool enabled, long msecWait, ConfigurationEngineDefaults.ThreadingConfig.Locking locking, TimeSourceService timeSourceService, bool initializenow) + { + Name = name; + Enabled = enabled; + MsecWait = msecWait; + TimeSourceService = timeSourceService; + + UseSpin = enabled && (locking == ConfigurationEngineDefaults.ThreadingConfig.Locking.SPIN); + + // construct a completed latch as an initial root latch + if (initializenow && UseSpin) + { + _currentLatchSpin = new NamedWindowConsumerLatchSpin(this); + } + else if (initializenow && enabled) + { + _currentLatchWait = new NamedWindowConsumerLatchWait(this); + } + } + + /// + /// Returns a new latch. + /// Need not be synchronized as there is one per statement and execution is during statement lock. + /// + /// latch + public NamedWindowConsumerLatch NewLatch( + NamedWindowDeltaData delta, + IDictionary> consumers) + { + if (UseSpin) + { + var nextLatch = new NamedWindowConsumerLatchSpin(delta, consumers, this, _currentLatchSpin); + _currentLatchSpin = nextLatch; + return nextLatch; + } + else + { + if (Enabled) + { + var nextLatch = new NamedWindowConsumerLatchWait(delta, consumers, this, _currentLatchWait); + _currentLatchWait.Later = nextLatch; + _currentLatchWait = nextLatch; + return nextLatch; + } + return new NamedWindowConsumerLatchNone(delta, consumers); + } + } + + public TimeSourceService TimeSourceService { get; private set; } + + public string Name { get; protected internal set; } + + public long MsecWait { get; protected internal set; } + + public bool UseSpin { get; protected internal set; } + + public bool Enabled { get; protected internal set; } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/named/NamedWindowConsumerLatchNone.cs b/NEsper.Core/NEsper.Core/epl/named/NamedWindowConsumerLatchNone.cs new file mode 100755 index 000000000..5049a59ad --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/named/NamedWindowConsumerLatchNone.cs @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.Threading; + +using com.espertech.esper.core.context.util; + +namespace com.espertech.esper.epl.named +{ + /// + /// A no-latch implementation of a latch for use in guaranteeing delivery between + /// a named window delta result and consumable by another statement. + /// + public class NamedWindowConsumerLatchNone : NamedWindowConsumerLatch + { + public NamedWindowConsumerLatchNone(NamedWindowDeltaData deltaData, IDictionary> dispatchTo) + : base(deltaData, dispatchTo) + { + } + + public override Thread CurrentThread + { + get { return Thread.CurrentThread; } + } + + public override void Await() + { + } + + public override void Done() + { + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/named/NamedWindowConsumerLatchSpin.cs b/NEsper.Core/NEsper.Core/epl/named/NamedWindowConsumerLatchSpin.cs new file mode 100755 index 000000000..f4ba16694 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/named/NamedWindowConsumerLatchSpin.cs @@ -0,0 +1,118 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Threading; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.util; + +namespace com.espertech.esper.epl.named +{ + /// + /// A spin-locking implementation of a latch for use in guaranteeing delivery between + /// a delta stream produced by a named window and consumable by another statement. + /// + public class NamedWindowConsumerLatchSpin : NamedWindowConsumerLatch + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + // The earlier latch is the latch generated before this latch + private readonly NamedWindowConsumerLatchFactory _factory; + private NamedWindowConsumerLatchSpin _earlier; + private Thread _currentThread; + + private volatile bool _isCompleted; + + /// + /// Ctor. + /// + /// the latch before this latch that this latch should be waiting for + public NamedWindowConsumerLatchSpin(NamedWindowDeltaData deltaData, IDictionary> dispatchTo, NamedWindowConsumerLatchFactory factory, NamedWindowConsumerLatchSpin earlier) + : base(deltaData, dispatchTo) + { + _factory = factory; + _earlier = earlier; + } + + /// + /// Ctor - use for the first and unused latch to indicate completion. + /// + public NamedWindowConsumerLatchSpin(NamedWindowConsumerLatchFactory factory) + : base(null, null) + { + _factory = factory; + _isCompleted = true; + _earlier = null; + } + + /// + /// Returns true if the dispatch completed for this future. + /// + /// true for completed, false if not + public bool IsCompleted + { + get { return _isCompleted; } + } + + /// + /// Blocking call that returns only when the earlier latch completed. + /// + /// unit of the latch + public override void Await() + { + var thread = Thread.CurrentThread; + try + { + if (_earlier._isCompleted) + { + return; + } + + if (((NamedWindowConsumerLatch)_earlier).CurrentThread == thread) + { + return; + } + + long spinStartTime = _factory.TimeSourceService.GetTimeMillis(); + while (!_earlier._isCompleted) + { + Thread.Yield(); + long spinDelta = _factory.TimeSourceService.GetTimeMillis() - spinStartTime; + if (spinDelta > _factory.MsecWait) + { + Log.Info("Spin wait timeout exceeded in named window '{0}' consumer dispatch at {1}ms for {0}, consider disabling named window consumer dispatch latching for better performance", _factory.Name, _factory.MsecWait); + break; + } + } + } + finally + { + _currentThread = thread; + } + } + + public override Thread CurrentThread + { + get { return _currentThread; } + } + + /// + /// Called to indicate that the latch completed and a later latch can start. + /// + public override void Done() + { + _isCompleted = true; + _earlier = null; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/named/NamedWindowConsumerLatchWait.cs b/NEsper.Core/NEsper.Core/epl/named/NamedWindowConsumerLatchWait.cs new file mode 100755 index 000000000..644ab10c2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/named/NamedWindowConsumerLatchWait.cs @@ -0,0 +1,147 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.Reflection; +using System.Threading; + +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.util; + +namespace com.espertech.esper.epl.named +{ + /// + /// A suspend-and-notify implementation of a latch for use in guaranteeing delivery between + /// a named window delta result and consumable by another statement. + /// + public class NamedWindowConsumerLatchWait : NamedWindowConsumerLatch + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + // The earlier latch is the latch generated before this latch + private readonly NamedWindowConsumerLatchFactory _factory; + private NamedWindowConsumerLatchWait _earlier; + + // The later latch is the latch generated after this latch + private NamedWindowConsumerLatchWait _later; + private volatile bool _isCompleted; + private Thread _currentThread; + + /// + /// Ctor. + /// + /// the latch before this latch that this latch should be waiting for + public NamedWindowConsumerLatchWait(NamedWindowDeltaData deltaData, IDictionary> dispatchTo, NamedWindowConsumerLatchFactory factory, NamedWindowConsumerLatchWait earlier) + : base(deltaData, dispatchTo) + { + _factory = factory; + _earlier = earlier; + } + + /// + /// Ctor - use for the first and unused latch to indicate completion. + /// + public NamedWindowConsumerLatchWait(NamedWindowConsumerLatchFactory factory) + : base(null, null) + { + _factory = factory; + _isCompleted = true; + _earlier = null; + } + + /// + /// Returns true if the dispatch completed for this future. + /// + /// true for completed, false if not + public bool IsCompleted + { + get { return _isCompleted; } + } + + /// + /// Hand a later latch to use for indicating completion via notify. + /// + /// is the later latch + public NamedWindowConsumerLatchWait Later + { + set { _later = value; } + } + + /// + /// Blcking call that returns only when the earlier latch completed. + /// + /// payload of the latch + public override void Await() + { + var thread = Thread.CurrentThread; + + try + { + if (_earlier._isCompleted) + { + return; + } + + if (((NamedWindowConsumerLatch)_earlier).CurrentThread == thread) + { + return; + } + + lock (this) + { + if (!_earlier._isCompleted) + { + try + { + Monitor.Wait(this, (int)_factory.MsecWait); + } + catch (ThreadAbortException e) + { + Log.Error("thread aborted", e); + } + catch (ThreadInterruptedException e) + { + Log.Error("thread interrupted", e); + } + } + } + + if (!_earlier._isCompleted) + { + Log.Info("Wait timeout exceeded for named window '" + "' consumer dispatch with notify"); + } + } + finally + { + _currentThread = thread; + } + } + + public override Thread CurrentThread + { + get { return _currentThread; } + } + + /// + /// Called to indicate that the latch completed and a later latch can start. + /// + public override void Done() + { + _isCompleted = true; + if (_later != null) + { + lock (_later) + { + Monitor.Pulse(_later); + } + } + _earlier = null; + _later = null; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/named/NamedWindowConsumerMgmtService.cs b/NEsper.Core/NEsper.Core/epl/named/NamedWindowConsumerMgmtService.cs new file mode 100755 index 000000000..4397855da --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/named/NamedWindowConsumerMgmtService.cs @@ -0,0 +1,22 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.service; +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.epl.named +{ + public interface NamedWindowConsumerMgmtService + { + void AddConsumer(StatementContext statementContext, NamedWindowConsumerStreamSpec namedSpec); + void Start(string statementName); + void Stop(string statementName); + void Destroy(string statementName); + void RemoveReferences(string statementName); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/named/NamedWindowConsumerMgmtServiceImpl.cs b/NEsper.Core/NEsper.Core/epl/named/NamedWindowConsumerMgmtServiceImpl.cs new file mode 100755 index 000000000..636e8ec83 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/named/NamedWindowConsumerMgmtServiceImpl.cs @@ -0,0 +1,57 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.epl.named +{ + public class NamedWindowConsumerMgmtServiceImpl : NamedWindowConsumerMgmtService { + + public static readonly NamedWindowConsumerMgmtServiceImpl INSTANCE = new NamedWindowConsumerMgmtServiceImpl(); + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private NamedWindowConsumerMgmtServiceImpl() { + } + + public void AddConsumer(StatementContext statementContext, NamedWindowConsumerStreamSpec namedSpec) { + if (Log.IsDebugEnabled) { + Log.Debug("Statement '" + statementContext.StatementName + " registers consumer for '" + namedSpec.WindowName + "'"); + } + } + + public void Start(string statementName) { + if (Log.IsDebugEnabled) { + Log.Debug("Statement '" + statementName + " starts consuming"); + } + } + + public void Stop(string statementName) { + if (Log.IsDebugEnabled) { + Log.Debug("Statement '" + statementName + " stop consuming"); + } + } + + public void Destroy(string statementName) { + if (Log.IsDebugEnabled) { + Log.Debug("Statement '" + statementName + " destroyed"); + } + } + + public void RemoveReferences(string statementName) { + if (Log.IsDebugEnabled) { + Log.Debug("Statement '" + statementName + " removing references"); + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/named/NamedWindowConsumerView.cs b/NEsper.Core/NEsper.Core/epl/named/NamedWindowConsumerView.cs new file mode 100755 index 000000000..941061f52 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/named/NamedWindowConsumerView.cs @@ -0,0 +1,177 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.client.annotation; +using com.espertech.esper.collection; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.property; +using com.espertech.esper.events; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.epl.named +{ + /// + /// Represents a consumer of a named window that selects from a named window via a from-clause. + /// + /// The view simply dispatches directly to child views, and keeps the last new event for iteration. + /// + public class NamedWindowConsumerView + : ViewSupport + , StopCallback + { + private readonly ExprEvaluator[] _filterList; + private readonly EventType _eventType; + private readonly NamedWindowConsumerCallback _consumerCallback; + private readonly ExprEvaluatorContext _exprEvaluatorContext; + private readonly PropertyEvaluator _optPropertyEvaluator; + private readonly FlushedEventBuffer _optPropertyContainedBuffer; + private readonly bool _audit; + + /// + /// Ctor. + /// + /// is a list of filter expressions + /// The opt property evaluator. + /// the event type of the named window + /// The consumer callback. + /// context for expression evalauation + /// if set to true [audit]. + public NamedWindowConsumerView(ExprEvaluator[] filterList, + PropertyEvaluator optPropertyEvaluator, + EventType eventType, + NamedWindowConsumerCallback consumerCallback, + ExprEvaluatorContext exprEvaluatorContext, + bool audit) + { + _filterList = filterList; + _optPropertyEvaluator = optPropertyEvaluator; + _optPropertyContainedBuffer = optPropertyEvaluator != null ? new FlushedEventBuffer() : null; + _eventType = eventType; + _consumerCallback = consumerCallback; + _exprEvaluatorContext = exprEvaluatorContext; + _audit = audit; + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + if (_audit) + { + if (AuditPath.IsAuditEnabled) + { + AuditPath.AuditLog(_exprEvaluatorContext.EngineURI, _exprEvaluatorContext.StatementName, AuditEnum.STREAM, _eventType.Name + " insert {" + EventBeanUtility.Summarize(newData) + "} remove {" + EventBeanUtility.Summarize(oldData) + "}"); + } + } + + // if we have a filter for the named window, + if (_filterList.Length != 0) + { + var eventPerStream = new EventBean[1]; + newData = PassFilter(newData, true, _exprEvaluatorContext, eventPerStream); + oldData = PassFilter(oldData, false, _exprEvaluatorContext, eventPerStream); + } + + if (_optPropertyEvaluator != null) + { + newData = GetUnpacked(newData); + oldData = GetUnpacked(oldData); + } + + if ((newData != null) || (oldData != null)) + { + UpdateChildren(newData, oldData); + } + } + + private EventBean[] GetUnpacked(EventBean[] data) + { + if (data == null) + { + return null; + } + if (data.Length == 0) + { + return data; + } + + for (int i = 0; i < data.Length; i++) + { + EventBean[] unpacked = _optPropertyEvaluator.GetProperty(data[i], _exprEvaluatorContext); + _optPropertyContainedBuffer.Add(unpacked); + } + return _optPropertyContainedBuffer.GetAndFlush(); + } + + private EventBean[] PassFilter(EventBean[] eventData, bool isNewData, ExprEvaluatorContext exprEvaluatorContext, EventBean[] eventPerStream) + { + if ((eventData == null) || (eventData.Length == 0)) + { + return null; + } + + OneEventCollection filtered = null; + foreach (EventBean theEvent in eventData) + { + eventPerStream[0] = theEvent; + bool pass = true; + foreach (ExprEvaluator filter in _filterList) + { + var result = (bool?)filter.Evaluate(new EvaluateParams(eventPerStream, isNewData, exprEvaluatorContext)); + if (result == null || !result.Value) + { + pass = false; + break; + } + } + + if (pass) + { + if (filtered == null) + { + filtered = new OneEventCollection(); + } + filtered.Add(theEvent); + } + } + + if (filtered == null) + { + return null; + } + return filtered.ToArray(); + } + + public override EventType EventType + { + get + { + if (_optPropertyEvaluator != null) + { + return _optPropertyEvaluator.FragmentEventType; + } + return _eventType; + } + } + + public override IEnumerator GetEnumerator() + { + return FilteredEventEnumerator.Enumerate( + _filterList, + _consumerCallback, + _exprEvaluatorContext); + } + + public void Stop() + { + _consumerCallback.Stopped(this); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/named/NamedWindowDeltaData.cs b/NEsper.Core/NEsper.Core/epl/named/NamedWindowDeltaData.cs new file mode 100755 index 000000000..837083dc0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/named/NamedWindowDeltaData.cs @@ -0,0 +1,74 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using com.espertech.esper.client; + +namespace com.espertech.esper.epl.named +{ +/// +/// A holder for events posted by a named window as an insert and remove stream. +/// + public class NamedWindowDeltaData + { + private readonly EventBean[] newData; + private readonly EventBean[] oldData; + + /// Ctor. + /// is the insert stream events, or null if none + /// is the remove stream events, or null if none + public NamedWindowDeltaData(EventBean[] newData, EventBean[] oldData) + { + this.newData = newData; + this.oldData = oldData; + } + + /// Ctor aggregates two deltas into a single delta. + /// + /// is the insert and remove stream events of a first result + /// + /// + /// is the insert and remove stream events of a second result + /// + public NamedWindowDeltaData(NamedWindowDeltaData deltaOne, NamedWindowDeltaData deltaTwo) + { + this.newData = Aggregate(deltaOne.NewData, deltaTwo.NewData); + this.oldData = Aggregate(deltaOne.OldData, deltaTwo.OldData); + } + + /// Returns the insert stream events. + /// insert stream + public EventBean[] NewData + { + get { return newData; } + } + + /// Returns the remove stream events. + /// remove stream + public EventBean[] OldData + { + get { return oldData; } + } + + private static EventBean[] Aggregate(EventBean[] arrOne, EventBean[] arrTwo) + { + if (arrOne == null) + { + return arrTwo; + } + if (arrTwo == null) + { + return arrOne; + } + EventBean[] arr = new EventBean[arrOne.Length + arrTwo.Length]; + Array.Copy(arrOne, 0, arr, 0, arrOne.Length); + Array.Copy(arrTwo, 0, arr, arrOne.Length, arrTwo.Length); + return arr; + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/epl/named/NamedWindowDispatchService.cs b/NEsper.Core/NEsper.Core/epl/named/NamedWindowDispatchService.cs new file mode 100755 index 000000000..7c68ba6ad --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/named/NamedWindowDispatchService.cs @@ -0,0 +1,47 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.metric; +using com.espertech.esper.events.vaevent; +using com.espertech.esper.timer; + +namespace com.espertech.esper.epl.named +{ + /// + /// Service to manage named window dispatches, locks and processors on an engine level. + /// + public interface NamedWindowDispatchService + { + NamedWindowProcessor CreateProcessor(string name, NamedWindowMgmtServiceImpl namedWindowMgmtService, NamedWindowDispatchService namedWindowDispatchService, string contextName, EventType eventType, StatementResultService statementResultService, ValueAddEventProcessor revisionProcessor, string eplExpression, string statementName, bool isPrioritized, bool isEnableSubqueryIndexShare, bool enableQueryPlanLog, MetricReportingService metricReportingService, bool isBatchingDataWindow, bool isVirtualDataWindow, ICollection optionalUniqueKeyProps, string eventTypeAsName, StatementContext statementContextCreateWindow); + NamedWindowTailView CreateTailView(EventType eventType, NamedWindowMgmtService namedWindowMgmtService, NamedWindowDispatchService namedWindowDispatchService, StatementResultService statementResultService, ValueAddEventProcessor revisionProcessor, bool prioritized, bool parentBatchWindow, string contextName, TimeSourceService timeSourceService, ConfigurationEngineDefaults.ThreadingConfig threadingConfig); + + /// + /// Dispatch events of the insert and remove stream of named windows to consumers, as part of the + /// main event processing or dispatch loop. + /// + /// send events to consuming statements + bool Dispatch(); + + /// + /// For use to add a result of a named window that must be dispatched to consuming views. + /// + /// is the result to dispatch + /// is the destination of the dispatch, a map of statements to one or more consuming views + void AddDispatch(NamedWindowConsumerLatchFactory latchFactory, NamedWindowDeltaData delta, IDictionary> consumers); + + /// + /// Dispose service. + /// + void Dispose(); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/named/NamedWindowDispatchServiceImpl.cs b/NEsper.Core/NEsper.Core/epl/named/NamedWindowDispatchServiceImpl.cs new file mode 100755 index 000000000..6c55164cb --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/named/NamedWindowDispatchServiceImpl.cs @@ -0,0 +1,401 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.client.hook; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.threading; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.metric; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.epl.variable; +using com.espertech.esper.events.vaevent; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.schedule; +using com.espertech.esper.timer; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.named +{ + /// + /// This service hold for each named window a dedicated processor and a lock to the named window. + /// This lock is shrared between the named window and on-delete statements. + /// + public class NamedWindowDispatchServiceImpl : NamedWindowDispatchService + { + private readonly SchedulingService _schedulingService; + private readonly VariableService _variableService; + private readonly TableService _tableService; + private readonly ExceptionHandlingService _exceptionHandlingService; + private readonly bool _isPrioritized; + private readonly IReaderWriterLock _eventProcessingRwLock; + private readonly MetricReportingService _metricReportingService; + + private readonly IThreadLocal> _threadLocal = ThreadLocalManager.Create>( + () => new List()); + + private readonly IThreadLocal> _dispatchesPerStmtTl = ThreadLocalManager.Create>( + () => new Dictionary()); + + /// + /// Ctor. + /// + /// The scheduling service. + /// is for variable access + /// The table service. + /// if the engine is running with prioritized execution + /// The event processing rw lock. + /// The exception handling service. + /// The metric reporting service. + public NamedWindowDispatchServiceImpl( + SchedulingService schedulingService, + VariableService variableService, + TableService tableService, + bool isPrioritized, + IReaderWriterLock eventProcessingRWLock, + ExceptionHandlingService exceptionHandlingService, + MetricReportingService metricReportingService) + { + _schedulingService = schedulingService; + _variableService = variableService; + _tableService = tableService; + _isPrioritized = isPrioritized; + _eventProcessingRwLock = eventProcessingRWLock; + _exceptionHandlingService = exceptionHandlingService; + _metricReportingService = metricReportingService; + } + + public NamedWindowProcessor CreateProcessor( + string name, + NamedWindowMgmtServiceImpl namedWindowMgmtService, + NamedWindowDispatchService namedWindowDispatchService, + string contextName, + EventType eventType, + StatementResultService statementResultService, + ValueAddEventProcessor revisionProcessor, + string eplExpression, + string statementName, + bool isPrioritized, + bool isEnableSubqueryIndexShare, + bool enableQueryPlanLog, + MetricReportingService metricReportingService, + bool isBatchingDataWindow, + bool isVirtualDataWindow, + ICollection optionalUniqueKeyProps, + string eventTypeAsName, + StatementContext statementContextCreateWindow) + { + return new NamedWindowProcessor( + name, namedWindowMgmtService, namedWindowDispatchService, contextName, eventType, statementResultService, + revisionProcessor, eplExpression, statementName, isPrioritized, isEnableSubqueryIndexShare, + enableQueryPlanLog, metricReportingService, isBatchingDataWindow, isVirtualDataWindow, + optionalUniqueKeyProps, eventTypeAsName, statementContextCreateWindow); + } + + public NamedWindowTailView CreateTailView( + EventType eventType, + NamedWindowMgmtService namedWindowMgmtService, + NamedWindowDispatchService namedWindowDispatchService, + StatementResultService statementResultService, + ValueAddEventProcessor revisionProcessor, + bool prioritized, + bool parentBatchWindow, + string contextName, + TimeSourceService timeSourceService, + ConfigurationEngineDefaults.ThreadingConfig threadingConfig) + { + return new NamedWindowTailView(eventType, namedWindowMgmtService, namedWindowDispatchService, statementResultService, revisionProcessor, _isPrioritized, parentBatchWindow, timeSourceService, threadingConfig); + } + + public void Dispose() + { + _threadLocal.Dispose(); + _dispatchesPerStmtTl.Dispose(); + } + + public void AddDispatch( + NamedWindowConsumerLatchFactory latchFactory, + NamedWindowDeltaData delta, + IDictionary> consumers) + { + _threadLocal.GetOrCreate().Add( + latchFactory.NewLatch(delta, consumers)); + } + + public bool Dispatch() + { + var dispatches = _threadLocal.GetOrCreate(); + if (dispatches.IsEmpty()) + { + return false; + } + + while (!dispatches.IsEmpty()) + { + // Acquire main processing lock which locks out statement management + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QNamedWindowDispatch(_exceptionHandlingService.EngineURI); } + + try + { + using (_eventProcessingRwLock.AcquireReadLock()) + { + try + { + var units = dispatches.ToArray(); + dispatches.Clear(); + ProcessDispatches(units); + } + catch (Exception ex) + { + throw new EPException(ex); + } + } + } + finally + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().ANamedWindowDispatch(); } + } + } + + return true; + } + + private void ProcessDispatches(NamedWindowConsumerLatch[] dispatches) + { + if (dispatches.Length == 1) + { + var latch = dispatches[0]; + 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. + var dispatchesPerStmt = _dispatchesPerStmtTl.GetOrCreate(); + for (int ii = 0; ii < dispatches.Length; ii++) + { + var latch = dispatches[ii]; + latch.Await(); + foreach (var entry in latch.DispatchTo) + { + var handle = entry.Key; + var perStmtObj = dispatchesPerStmt.Get(handle); + if (perStmtObj == null) + { + dispatchesPerStmt.Put(handle, latch); + } + else if (perStmtObj is IList) + { + var list = (IList)perStmtObj; + list.Add(latch); + } + else // convert from object to list + { + var unitObj = (NamedWindowConsumerLatch)perStmtObj; + IList list = new List(); + list.Add(unitObj); + list.Add(latch); + dispatchesPerStmt.Put(handle, list); + } + } + } + + try + { + // Dispatch - with or without metrics reporting + foreach (var entry in dispatchesPerStmt) + { + var handle = entry.Key; + var perStmtObj = entry.Value; + + // dispatch of a single result to the statement + if (perStmtObj is NamedWindowConsumerLatch) + { + var unit = (NamedWindowConsumerLatch)perStmtObj; + var newData = unit.DeltaData.NewData; + var oldData = unit.DeltaData.OldData; + + handle.StatementHandle.MetricsHandle.Call( + _metricReportingService.PerformanceCollector, + () => ProcessHandle(handle, unit.DispatchTo.Get(handle), newData, oldData)); + + if ((_isPrioritized) && (handle.IsPreemptive)) + { + break; + } + + continue; + } + + // dispatch of multiple results to a the same statement, need to aggregate per consumer view + var deltaPerConsumer = GetDeltaPerConsumer(perStmtObj, handle); + handle.StatementHandle.MetricsHandle.Call( + _metricReportingService.PerformanceCollector, + () => ProcessHandleMultiple(handle, deltaPerConsumer)); + + if ((_isPrioritized) && (handle.IsPreemptive)) + { + break; + } + } + } + finally + { + for (int ii = 0; ii < dispatches.Length; ii++) + { + dispatches[ii].Done(); + } + } + + dispatchesPerStmt.Clear(); + } + + private void ProcessHandleMultiple(EPStatementAgentInstanceHandle handle, IDictionary deltaPerConsumer) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QNamedWindowCPMulti(_exceptionHandlingService.EngineURI, deltaPerConsumer, handle, _schedulingService.Time); } + + try + { + using (handle.StatementAgentInstanceLock.AcquireWriteLock()) + { + try + { + if (handle.HasVariables) + { + _variableService.SetLocalVersion(); + } + foreach (var entryDelta in deltaPerConsumer) + { + var newData = entryDelta.Value.NewData; + var oldData = entryDelta.Value.OldData; + entryDelta.Key.Update(newData, oldData); + } + + // internal join processing, if applicable + handle.InternalDispatch(); + } + catch (Exception ex) + { + _exceptionHandlingService.HandleException(ex, handle, ExceptionHandlerExceptionType.PROCESS, null); + } + finally + { + if (handle.HasTableAccess) + { + _tableService.TableExprEvaluatorContext.ReleaseAcquiredLocks(); + } + } + } + } + finally + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().ANamedWindowCPMulti(); } + } + } + + private void ProcessHandle(EPStatementAgentInstanceHandle handle, IList value, EventBean[] newData, EventBean[] oldData) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QNamedWindowCPSingle(_exceptionHandlingService.EngineURI, value, newData, oldData, handle, _schedulingService.Time); } + + try + { + using (handle.StatementAgentInstanceLock.AcquireWriteLock()) + { + try + { + if (handle.HasVariables) + { + _variableService.SetLocalVersion(); + } + + foreach (var consumerView in value) + { + consumerView.Update(newData, oldData); + } + + // internal join processing, if applicable + handle.InternalDispatch(); + } + catch (Exception ex) + { + _exceptionHandlingService.HandleException(ex, handle, ExceptionHandlerExceptionType.PROCESS, null); + } + finally + { + if (handle.HasTableAccess) + { + _tableService.TableExprEvaluatorContext.ReleaseAcquiredLocks(); + } + } + } + } + finally + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().ANamedWindowCPSingle(); } + } + } + + public IDictionary GetDeltaPerConsumer(object perStmtObj, EPStatementAgentInstanceHandle handle) + { + var list = (IList)perStmtObj; + var deltaPerConsumer = new LinkedHashMap(); + foreach (var unit in list) // for each unit + { + foreach (var consumerView in unit.DispatchTo.Get(handle)) // each consumer + { + var deltaForConsumer = deltaPerConsumer.Get(consumerView); + if (deltaForConsumer == null) + { + deltaPerConsumer.Put(consumerView, unit.DeltaData); + } + else + { + var aggregated = new NamedWindowDeltaData(deltaForConsumer, unit.DeltaData); + deltaPerConsumer.Put(consumerView, aggregated); + } + } + } + return deltaPerConsumer; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/named/NamedWindowLifecycleEvent.cs b/NEsper.Core/NEsper.Core/epl/named/NamedWindowLifecycleEvent.cs new file mode 100755 index 000000000..03b19dc0e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/named/NamedWindowLifecycleEvent.cs @@ -0,0 +1,73 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.named +{ + /// Event indicating named window lifecycle management. + public class NamedWindowLifecycleEvent + { + private readonly String _name; + private readonly NamedWindowProcessor _processor; + private readonly LifecycleEventType _eventType; + private readonly Object[] _paramList; + + /// Event types. + public enum LifecycleEventType { + /// Named window created. + CREATE, + + /// Named window removed. + DESTROY + } + + /// + /// Ctor. + /// + /// is the name of the named window + /// instance for processing the named window contents + /// the type of event + /// event parameters + protected internal NamedWindowLifecycleEvent(String name, NamedWindowProcessor processor, LifecycleEventType eventType, params Object[] paramList) + { + _name = name; + _processor = processor; + _eventType = eventType; + _paramList = paramList; + } + + /// Returns the named window name. + /// name + public string Name + { + get { return _name; } + } + + /// Return the processor originating the event. + /// processor + public NamedWindowProcessor Processor + { + get { return _processor; } + } + + /// Returns the event type. + /// type of event + public LifecycleEventType EventType + { + get { return _eventType; } + } + + /// Returns event parameters. + /// paramList + public object[] Params + { + get { return _paramList; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/named/NamedWindowLifecycleObserver.cs b/NEsper.Core/NEsper.Core/epl/named/NamedWindowLifecycleObserver.cs new file mode 100755 index 000000000..b8b23b9d7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/named/NamedWindowLifecycleObserver.cs @@ -0,0 +1,18 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.named +{ + /// Observer named window events. + public interface NamedWindowLifecycleObserver + { + /// Observer named window changes. + /// indicates named window action + void Observe(NamedWindowLifecycleEvent theEvent); + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/named/NamedWindowMgmtService.cs b/NEsper.Core/NEsper.Core/epl/named/NamedWindowMgmtService.cs new file mode 100755 index 000000000..b9f71423b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/named/NamedWindowMgmtService.cs @@ -0,0 +1,139 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.threading; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.events.vaevent; + +namespace com.espertech.esper.epl.named +{ + /// + /// Service to manage named windows on an engine level. + /// + public interface NamedWindowMgmtService + { + /// + /// Returns true to indicate that the name is a named window. + /// + /// is the window name + /// true if a named window, false if not a named window + bool IsNamedWindow(string name); + + /// + /// Returns the names of all named windows known. + /// + /// named window names + string[] NamedWindows { get; } + + /// + /// Create a new named window. + /// + /// window name + /// Name of the context. + /// the event type of the window + /// for coordinating on whether insert and remove stream events should be posted + /// handles update events + /// is the expression + /// the name of the statement + /// if the engine is running with prioritized execution + /// if set to true [is enable subquery index share]. + /// if set to true [is batching data window]. + /// if set to true [is virtual data window]. + /// The optional unique key props. + /// Name of the event type as. + /// The statement context create window. + /// The named window dispatch service. + /// + /// processor for the named window + /// + /// ViewProcessingException if the named window already exists + NamedWindowProcessor AddProcessor(string name, string contextName, EventType eventType, StatementResultService statementResultService, ValueAddEventProcessor revisionProcessor, string eplExpression, string statementName, bool isPrioritized, bool isEnableSubqueryIndexShare, bool isBatchingDataWindow, bool isVirtualDataWindow, ICollection optionalUniqueKeyProps, string eventTypeAsName, StatementContext statementContextCreateWindow, NamedWindowDispatchService namedWindowDispatchService); + + /// + /// Returns the processing instance for a given named window. + /// + /// window name + /// processor for the named window + NamedWindowProcessor GetProcessor(string name); + + /// + /// Upon destroy of the named window creation statement, the named window processor must be removed. + /// + /// is the named window name + void RemoveProcessor(string name); + + /// + /// Returns the statement lock for the named window, to be shared with on-delete statements for the same named window. + /// + /// is the window name + /// the lock for the named window, or null if the window dos not yet exists + IReaderWriterLock GetNamedWindowLock(string windowName); + + /// + /// Sets the lock to use for a named window. + /// + /// is the named window name + /// is the statement lock for the create window statement + /// the name of the statement that is the "create window" + void AddNamedWindowLock(string windowName, IReaderWriterLock statementResourceLock, string statementName); + + /// + /// Remove the lock associated to the named window. + /// + /// the name of the statement that is the "create window" + void RemoveNamedWindowLock(string statementName); + + /// + /// Clear out the service. + /// + void Dispose(); + + /// + /// Add an observer to be called back when named window state changes occur. + /// Observers have set-semantics: the same Observer cannot be added twice + /// + /// to add + void AddObserver(NamedWindowLifecycleObserver observer); + + /// + /// Remove an observer to be called back when named window state changes occur. + /// + /// to remove + void RemoveObserver(NamedWindowLifecycleObserver observer); + + /// + /// Returns an index descriptor array describing all available indexes for the named window. + /// + /// window name + /// indexes + IndexMultiKey[] GetNamedWindowIndexes(string windowName); + + /// + /// Remove the named window instance(s), when found + /// + /// to remove + void RemoveNamedWindowIfFound(string namedWindowName); + } + + public class NamedWindowMgmtServiceConstants + { + /// + /// Error message for data windows required. + /// + public const string ERROR_MSG_DATAWINDOWS = "Named windows require one or more child views that are data window views"; + + /// + /// Error message for no data window allowed. + /// + public const string ERROR_MSG_NO_DATAWINDOW_ALLOWED = "Consuming statements to a named window cannot declare a data window view onto the named window"; + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/named/NamedWindowMgmtServiceImpl.cs b/NEsper.Core/NEsper.Core/epl/named/NamedWindowMgmtServiceImpl.cs new file mode 100755 index 000000000..067a23c5e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/named/NamedWindowMgmtServiceImpl.cs @@ -0,0 +1,184 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.threading; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.epl.metric; +using com.espertech.esper.events.vaevent; +using com.espertech.esper.view; + +namespace com.espertech.esper.epl.named +{ + /// + /// This service hold for each named window a dedicated processor and a lock to the named window. + /// This lock is shrared between the named window and on-delete statements. + /// + public class NamedWindowMgmtServiceImpl : NamedWindowMgmtService + { + private readonly IDictionary _processors; + private readonly IDictionary _windowStatementLocks; + private readonly ISet _observers; + private readonly bool _enableQueryPlanLog; + private readonly MetricReportingService _metricReportingService; + + /// + /// Ctor. + /// + public NamedWindowMgmtServiceImpl(bool enableQueryPlanLog, MetricReportingService metricReportingService) + { + _processors = new Dictionary().WithNullSupport(); + _windowStatementLocks = new Dictionary().WithNullSupport(); + _observers = new HashSet(); + _enableQueryPlanLog = enableQueryPlanLog; + _metricReportingService = metricReportingService; + } + + public void Dispose() + { + _processors.Clear(); + } + + public string[] NamedWindows + { + get + { + return _processors.Keys.ToArrayOrNull(); + } + } + + public IReaderWriterLock GetNamedWindowLock(string windowName) + { + var pair = _windowStatementLocks.Get(windowName); + if (pair == null) + { + return null; + } + return pair.Lock; + } + + public void AddNamedWindowLock(string windowName, IReaderWriterLock statementResourceLock, string statementName) + { + _windowStatementLocks.Put(windowName, new NamedWindowLockPair(statementName, statementResourceLock)); + } + + public void RemoveNamedWindowLock(string statementName) + { + foreach (var entry in _windowStatementLocks) + { + if (entry.Value.StatementName == statementName) + { + _windowStatementLocks.Remove(entry.Key); + return; + } + } + } + + public bool IsNamedWindow(string name) + { + return _processors.ContainsKey(name); + } + + public NamedWindowProcessor GetProcessor(string name) + { + return _processors.Get(name); + } + + public IndexMultiKey[] GetNamedWindowIndexes(string windowName) + { + var processor = _processors.Get(windowName); + if (processor == null) + { + return null; + } + return processor.GetProcessorInstance(null).IndexDescriptors; + } + + public void RemoveNamedWindowIfFound(string namedWindowName) + { + var processor = _processors.Get(namedWindowName); + if (processor == null) + { + return; + } + processor.ClearProcessorInstances(); + RemoveProcessor(namedWindowName); + } + + public NamedWindowProcessor AddProcessor(string name, string contextName, EventType eventType, StatementResultService statementResultService, ValueAddEventProcessor revisionProcessor, string eplExpression, string statementName, bool isPrioritized, bool isEnableSubqueryIndexShare, bool isBatchingDataWindow, bool isVirtualDataWindow, ICollection optionalUniqueKeyProps, string eventTypeAsName, StatementContext statementContextCreateWindow, NamedWindowDispatchService namedWindowDispatchService) + { + if (_processors.ContainsKey(name)) + { + throw new ViewProcessingException("A named window by name '" + name + "' has already been created"); + } + + var processor = namedWindowDispatchService.CreateProcessor( + name, this, namedWindowDispatchService, contextName, eventType, statementResultService, revisionProcessor, + eplExpression, statementName, isPrioritized, isEnableSubqueryIndexShare, _enableQueryPlanLog, + _metricReportingService, isBatchingDataWindow, isVirtualDataWindow, optionalUniqueKeyProps, eventTypeAsName, + statementContextCreateWindow); + _processors.Put(name, processor); + + if (!_observers.IsEmpty()) + { + var theEvent = new NamedWindowLifecycleEvent(name, processor, NamedWindowLifecycleEvent.LifecycleEventType.CREATE); + foreach (var observer in _observers) + { + observer.Observe(theEvent); + } + } + + return processor; + } + + public void RemoveProcessor(string name) + { + var processor = _processors.Get(name); + if (processor != null) + { + processor.Dispose(); + _processors.Remove(name); + + if (!_observers.IsEmpty()) + { + var theEvent = new NamedWindowLifecycleEvent(name, processor, NamedWindowLifecycleEvent.LifecycleEventType.DESTROY); + foreach (var observer in _observers) + { + observer.Observe(theEvent); + } + } + } + } + + public void AddObserver(NamedWindowLifecycleObserver observer) + { + _observers.Add(observer); + } + + public void RemoveObserver(NamedWindowLifecycleObserver observer) + { + _observers.Remove(observer); + } + + internal class NamedWindowLockPair + { + internal string StatementName { get; private set; } + internal IReaderWriterLock Lock { get; private set; } + + internal NamedWindowLockPair(string statementName, IReaderWriterLock mlock) + { + StatementName = statementName; + Lock = mlock; + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/named/NamedWindowOnDeleteView.cs b/NEsper.Core/NEsper.Core/epl/named/NamedWindowOnDeleteView.cs new file mode 100755 index 000000000..f5371fe00 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/named/NamedWindowOnDeleteView.cs @@ -0,0 +1,68 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.epl.spec; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.named +{ + /// + /// View for the on-delete statement that handles removing events from a named window. + /// + public class NamedWindowOnDeleteView : NamedWindowOnExprBaseView + { + private readonly NamedWindowOnDeleteViewFactory _parent; + private EventBean[] _lastResult; + + public NamedWindowOnDeleteView(SubordWMatchExprLookupStrategy lookupStrategy, NamedWindowRootViewInstance rootView, ExprEvaluatorContext exprEvaluatorContext, NamedWindowOnDeleteViewFactory parent) + : base(lookupStrategy, rootView, exprEvaluatorContext) + { + _parent = parent; + } + + public override void HandleMatching(EventBean[] triggerEvents, EventBean[] matchingEvents) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QInfraOnAction(OnTriggerType.ON_DELETE, triggerEvents, matchingEvents); } + + if ((matchingEvents != null) && (matchingEvents.Length > 0)) + { + // Events to delete are indicated via old data + RootView.Update(null, matchingEvents); + + // The on-delete listeners receive the events deleted, but only if there is interest + if (_parent.StatementResultService.IsMakeNatural || _parent.StatementResultService.IsMakeSynthetic) { + UpdateChildren(matchingEvents, null); + } + } + + // Keep the last delete records + _lastResult = matchingEvents; + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AInfraOnAction(); } + } + + public override EventType EventType + { + get { return RootView.EventType; } + } + + public override IEnumerator GetEnumerator() + { + if (_lastResult == null) + return EnumerationHelper.Empty(); + return ((IEnumerable)_lastResult).GetEnumerator(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/named/NamedWindowOnDeleteViewFactory.cs b/NEsper.Core/NEsper.Core/epl/named/NamedWindowOnDeleteViewFactory.cs new file mode 100755 index 000000000..9edd96a58 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/named/NamedWindowOnDeleteViewFactory.cs @@ -0,0 +1,33 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.lookup; + +namespace com.espertech.esper.epl.named +{ + /// View for the on-delete statement that handles removing events from a named window. + public class NamedWindowOnDeleteViewFactory : NamedWindowOnExprBaseViewFactory + { + public NamedWindowOnDeleteViewFactory(EventType namedWindowEventType, StatementResultService statementResultService) + : base(namedWindowEventType) + { + StatementResultService = statementResultService; + } + + public StatementResultService StatementResultService { get; private set; } + + public override NamedWindowOnExprBaseView Make(SubordWMatchExprLookupStrategy lookupStrategy, NamedWindowRootViewInstance namedWindowRootViewInstance, AgentInstanceContext agentInstanceContext, ResultSetProcessor resultSetProcessor) + { + return new NamedWindowOnDeleteView(lookupStrategy, namedWindowRootViewInstance, agentInstanceContext, this); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/named/NamedWindowOnExprBaseView.cs b/NEsper.Core/NEsper.Core/epl/named/NamedWindowOnExprBaseView.cs new file mode 100755 index 000000000..5f7cce30c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/named/NamedWindowOnExprBaseView.cs @@ -0,0 +1,78 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.view; + +namespace com.espertech.esper.epl.named +{ + /// + /// View for the on-delete statement that handles removing events from a named window. + /// + public abstract class NamedWindowOnExprBaseView : ViewSupport + { + /// + /// The event type of the events hosted in the named window. + /// + private readonly SubordWMatchExprLookupStrategy _lookupStrategy; + private readonly ExprEvaluatorContext _exprEvaluatorContext; + + /// + /// The root view accepting removals (old data). + /// + protected readonly NamedWindowRootViewInstance RootView; + + /// + /// Ctor. + /// + /// for handling trigger events to determine deleted events + /// to indicate which events to delete + /// context for expression evalauation + protected NamedWindowOnExprBaseView( + SubordWMatchExprLookupStrategy lookupStrategy, + NamedWindowRootViewInstance rootView, + ExprEvaluatorContext exprEvaluatorContext) + { + _lookupStrategy = lookupStrategy; + RootView = rootView; + _exprEvaluatorContext = exprEvaluatorContext; + } + + /// + /// Implemented by on-trigger views to action on the combination of trigger and matching events in the named window. + /// + /// is the trigger events (usually 1) + /// is the matching events retrieved via lookup strategy + public abstract void HandleMatching(EventBean[] triggerEvents, EventBean[] matchingEvents); + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + if (newData == null) + { + return; + } + + // Determine via the lookup strategy a subset of events to process + EventBean[] eventsFound = _lookupStrategy.Lookup(newData, _exprEvaluatorContext); + + // Let the implementation handle the delete or other action + HandleMatching(newData, eventsFound); + } + + /// + /// returns expr context. + /// + /// context + public ExprEvaluatorContext ExprEvaluatorContext + { + get { return _exprEvaluatorContext; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/named/NamedWindowOnExprBaseViewFactory.cs b/NEsper.Core/NEsper.Core/epl/named/NamedWindowOnExprBaseViewFactory.cs new file mode 100755 index 000000000..d071daf70 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/named/NamedWindowOnExprBaseViewFactory.cs @@ -0,0 +1,30 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.lookup; + +namespace com.espertech.esper.epl.named +{ + /// + /// View for the on-delete statement that handles removing events from a named window. + /// + public abstract class NamedWindowOnExprBaseViewFactory : NamedWindowOnExprFactory + { + protected readonly EventType NamedWindowEventType; + + protected NamedWindowOnExprBaseViewFactory(EventType namedWindowEventType) + { + NamedWindowEventType = namedWindowEventType; + } + + public abstract NamedWindowOnExprBaseView Make(SubordWMatchExprLookupStrategy lookupStrategy, NamedWindowRootViewInstance namedWindowRootViewInstance, AgentInstanceContext agentInstanceContext, ResultSetProcessor resultSetProcessor); + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/named/NamedWindowOnExprFactory.cs b/NEsper.Core/NEsper.Core/epl/named/NamedWindowOnExprFactory.cs new file mode 100755 index 000000000..657b481d7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/named/NamedWindowOnExprFactory.cs @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.lookup; + +namespace com.espertech.esper.epl.named +{ + /// + /// View for the on-delete statement that handles removing events from a named window. + /// + public interface NamedWindowOnExprFactory + { + NamedWindowOnExprBaseView Make( + SubordWMatchExprLookupStrategy lookupStrategy, + NamedWindowRootViewInstance namedWindowRootViewInstance, + AgentInstanceContext agentInstanceContext, + ResultSetProcessor resultSetProcessor); + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/named/NamedWindowOnExprFactoryFactory.cs b/NEsper.Core/NEsper.Core/epl/named/NamedWindowOnExprFactoryFactory.cs new file mode 100755 index 000000000..3742b7db7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/named/NamedWindowOnExprFactoryFactory.cs @@ -0,0 +1,92 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.client.soda; +using com.espertech.esper.compat; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.metric; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.updatehelper; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.named +{ + /// + /// View for the on-delete statement that handles removing events from a named window. + /// + public class NamedWindowOnExprFactoryFactory + { + public static NamedWindowOnExprFactory Make( + EventType namedWindowEventType, + string namedWindowName, + string namedWindowAlias, + OnTriggerDesc onTriggerDesc, + EventType filterEventType, + string filterStreamName, + bool addToFront, + InternalEventRouter internalEventRouter, + EventType outputEventType, + StatementContext statementContext, + StatementMetricHandle createNamedWindowMetricsHandle, + bool isDistinct, + StreamSelector? optionalStreamSelector, + string optionalInsertIntoTableName) + { + if (onTriggerDesc.OnTriggerType == OnTriggerType.ON_DELETE) + { + return new NamedWindowOnDeleteViewFactory(namedWindowEventType, statementContext.StatementResultService); + } + else if (onTriggerDesc.OnTriggerType == OnTriggerType.ON_SELECT) + { + EventBeanReader eventBeanReader = null; + if (isDistinct) + { + if (outputEventType is EventTypeSPI) + { + eventBeanReader = ((EventTypeSPI) outputEventType).Reader; + } + if (eventBeanReader == null) + { + eventBeanReader = new EventBeanReaderDefaultImpl(outputEventType); + } + } + var windowDesc = (OnTriggerWindowDesc) onTriggerDesc; + return new NamedWindowOnSelectViewFactory( + namedWindowEventType, internalEventRouter, addToFront, + statementContext.EpStatementHandle, eventBeanReader, isDistinct, + statementContext.StatementResultService, statementContext.InternalEventEngineRouteDest, + windowDesc.IsDeleteAndSelect, optionalStreamSelector, optionalInsertIntoTableName); + } + else if (onTriggerDesc.OnTriggerType == OnTriggerType.ON_UPDATE) + { + var updateDesc = (OnTriggerWindowUpdateDesc) onTriggerDesc; + var updateHelper = EventBeanUpdateHelperFactory.Make( + namedWindowName, (EventTypeSPI) namedWindowEventType, updateDesc.Assignments, namedWindowAlias, + filterEventType, true, statementContext.StatementName, statementContext.EngineURI, + statementContext.EventAdapterService); + return new NamedWindowOnUpdateViewFactory( + namedWindowEventType, statementContext.StatementResultService, updateHelper); + } + else if (onTriggerDesc.OnTriggerType == OnTriggerType.ON_MERGE) + { + var onMergeTriggerDesc = (OnTriggerMergeDesc) onTriggerDesc; + var onMergeHelper = new NamedWindowOnMergeHelper( + statementContext, onMergeTriggerDesc, filterEventType, filterStreamName, internalEventRouter, + namedWindowName, (EventTypeSPI) namedWindowEventType); + return new NamedWindowOnMergeViewFactory( + namedWindowEventType, onMergeHelper, statementContext.StatementResultService, + createNamedWindowMetricsHandle, statementContext.MetricReportingService); + } + else + { + throw new IllegalStateException("Unknown trigger type " + onTriggerDesc.OnTriggerType); + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/named/NamedWindowOnMergeAction.cs b/NEsper.Core/NEsper.Core/epl/named/NamedWindowOnMergeAction.cs new file mode 100755 index 000000000..d44087573 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/named/NamedWindowOnMergeAction.cs @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.named +{ + public abstract class NamedWindowOnMergeAction + { + private readonly ExprEvaluator _optionalFilter; + + protected NamedWindowOnMergeAction(ExprEvaluator optionalFilter) + { + _optionalFilter = optionalFilter; + } + + public bool IsApplies(EventBean[] eventsPerStream, ExprEvaluatorContext context) + { + if (_optionalFilter == null) + { + return true; + } + var evaluateParams = new EvaluateParams(eventsPerStream, true, context); + var result = _optionalFilter.Evaluate(evaluateParams); + return result != null && (bool) result; + } + + public abstract void Apply(EventBean matchingEvent, EventBean[] eventsPerStream, OneEventCollection newData, OneEventCollection oldData, ExprEvaluatorContext exprEvaluatorContext); + public abstract String GetName(); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/named/NamedWindowOnMergeActionDel.cs b/NEsper.Core/NEsper.Core/epl/named/NamedWindowOnMergeActionDel.cs new file mode 100755 index 000000000..8d039bf78 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/named/NamedWindowOnMergeActionDel.cs @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.named +{ + public class NamedWindowOnMergeActionDel : NamedWindowOnMergeAction + { + public NamedWindowOnMergeActionDel(ExprEvaluator optionalFilter) + : base(optionalFilter) + { + } + + public override void Apply(EventBean matchingEvent, EventBean[] eventsPerStream, OneEventCollection newData, OneEventCollection oldData, ExprEvaluatorContext exprEvaluatorContext) { + oldData.Add(matchingEvent); + } + + public override String GetName() + { + return "delete"; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/named/NamedWindowOnMergeActionIns.cs b/NEsper.Core/NEsper.Core/epl/named/NamedWindowOnMergeActionIns.cs new file mode 100755 index 000000000..738ef7d16 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/named/NamedWindowOnMergeActionIns.cs @@ -0,0 +1,83 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.named +{ + public class NamedWindowOnMergeActionIns : NamedWindowOnMergeAction + { + private readonly SelectExprProcessor _insertHelper; + private readonly InternalEventRouter _internalEventRouter; + private readonly String _insertIntoTableName; + private readonly TableService _tableService; + private readonly EPStatementHandle _statementHandle; + private readonly InternalEventRouteDest _internalEventRouteDest; + private readonly bool _audit; + + public NamedWindowOnMergeActionIns( + ExprEvaluator optionalFilter, + SelectExprProcessor insertHelper, + InternalEventRouter internalEventRouter, + String insertIntoTableName, + TableService tableService, + EPStatementHandle statementHandle, + InternalEventRouteDest internalEventRouteDest, + bool audit) + : base(optionalFilter) + { + _insertHelper = insertHelper; + _internalEventRouter = internalEventRouter; + _insertIntoTableName = insertIntoTableName; + _tableService = tableService; + _statementHandle = statementHandle; + _internalEventRouteDest = internalEventRouteDest; + _audit = audit; + } + + public override void Apply(EventBean matchingEvent, EventBean[] eventsPerStream, OneEventCollection newData, OneEventCollection oldData, ExprEvaluatorContext exprEvaluatorContext) + { + EventBean theEvent = _insertHelper.Process(eventsPerStream, true, true, exprEvaluatorContext); + + if (_insertIntoTableName != null) + { + TableStateInstance tableStateInstance = _tableService.GetState(_insertIntoTableName, exprEvaluatorContext.AgentInstanceId); + if (_audit) + { + AuditPath.AuditInsertInto(tableStateInstance.AgentInstanceContext.EngineURI, _statementHandle.StatementName, theEvent); + } + tableStateInstance.AddEventUnadorned(theEvent); + return; + } + + if (_internalEventRouter == null) { + newData.Add(theEvent); + return; + } + + if (_audit) { + AuditPath.AuditInsertInto(_internalEventRouteDest.EngineURI, _statementHandle.StatementName, theEvent); + } + _internalEventRouter.Route(theEvent, _statementHandle, _internalEventRouteDest, exprEvaluatorContext, false); + } + + public override String GetName() + { + return _internalEventRouter != null ? "insert-into" : "select"; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/named/NamedWindowOnMergeActionUpd.cs b/NEsper.Core/NEsper.Core/epl/named/NamedWindowOnMergeActionUpd.cs new file mode 100755 index 000000000..2c1573452 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/named/NamedWindowOnMergeActionUpd.cs @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.updatehelper; + +namespace com.espertech.esper.epl.named +{ + public class NamedWindowOnMergeActionUpd : NamedWindowOnMergeAction + { + private readonly EventBeanUpdateHelper _updateHelper; + + public NamedWindowOnMergeActionUpd(ExprEvaluator optionalFilter, EventBeanUpdateHelper updateHelper) + : base(optionalFilter) + { + _updateHelper = updateHelper; + } + + public override void Apply(EventBean matchingEvent, EventBean[] eventsPerStream, OneEventCollection newData, OneEventCollection oldData, ExprEvaluatorContext exprEvaluatorContext) + { + EventBean copy = _updateHelper.UpdateWCopy(matchingEvent, eventsPerStream, exprEvaluatorContext); + newData.Add(copy); + oldData.Add(matchingEvent); + } + + public override String GetName() + { + return "Update"; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/named/NamedWindowOnMergeHelper.cs b/NEsper.Core/NEsper.Core/epl/named/NamedWindowOnMergeHelper.cs new file mode 100755 index 000000000..ace8a8760 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/named/NamedWindowOnMergeHelper.cs @@ -0,0 +1,187 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.client.annotation; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.updatehelper; +using com.espertech.esper.events; +using com.espertech.esper.events.map; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.named +{ + /// Factory for handles for updates/inserts/deletes/select + public class NamedWindowOnMergeHelper + { + private readonly List _matched; + private readonly List _unmatched; + + public NamedWindowOnMergeHelper( + StatementContext statementContext, + OnTriggerMergeDesc onTriggerDesc, + EventType triggeringEventType, + string triggeringStreamName, + InternalEventRouter internalEventRouter, + string namedWindowName, + EventTypeSPI namedWindowType) + { + _matched = new List(); + _unmatched = new List(); + + var count = 1; + foreach (var matchedItem in onTriggerDesc.Items) + { + var actions = new List(); + foreach (var item in matchedItem.Actions) + { + try + { + if (item is OnTriggerMergeActionInsert) + { + var insertDesc = (OnTriggerMergeActionInsert) item; + actions.Add( + SetupInsert( + namedWindowName, internalEventRouter, namedWindowType, count, insertDesc, + triggeringEventType, triggeringStreamName, statementContext)); + } + else if (item is OnTriggerMergeActionUpdate) + { + var updateDesc = (OnTriggerMergeActionUpdate) item; + var updateHelper = EventBeanUpdateHelperFactory.Make( + namedWindowName, namedWindowType, updateDesc.Assignments, onTriggerDesc.OptionalAsName, + triggeringEventType, true, statementContext.StatementName, statementContext.EngineURI, + statementContext.EventAdapterService); + var filterEval = updateDesc.OptionalWhereClause == null + ? null + : updateDesc.OptionalWhereClause.ExprEvaluator; + actions.Add(new NamedWindowOnMergeActionUpd(filterEval, updateHelper)); + } + else if (item is OnTriggerMergeActionDelete) + { + var deleteDesc = (OnTriggerMergeActionDelete) item; + var filterEval = deleteDesc.OptionalWhereClause == null + ? null + : deleteDesc.OptionalWhereClause.ExprEvaluator; + actions.Add(new NamedWindowOnMergeActionDel(filterEval)); + } + else + { + throw new ArgumentException("Invalid type of merge item '" + item.GetType() + "'"); + } + count++; + } + catch (ExprValidationException ex) + { + var isNot = item is OnTriggerMergeActionInsert; + var message = "Validation failed in when-" + (isNot ? "not-" : "") + "matched (clause " + count + + "): " + ex.Message; + throw new ExprValidationException(message, ex); + } + } + + if (matchedItem.IsMatchedUnmatched) + { + _matched.Add(new NamedWindowOnMergeMatch(matchedItem.OptionalMatchCond, actions)); + } + else + { + _unmatched.Add(new NamedWindowOnMergeMatch(matchedItem.OptionalMatchCond, actions)); + } + } + } + + public static IList CompileSelectNoWildcard(string triggeringStreamName, IList selectClause) + { + var selectNoWildcard = new List(); + foreach (var element in selectClause) { + if (!(element is SelectClauseElementWildcard)) { + selectNoWildcard.Add(element); + continue; + } + var streamSelect = new SelectClauseStreamCompiledSpec(triggeringStreamName, null); + streamSelect.StreamNumber = 1; + selectNoWildcard.Add(streamSelect); + } + return selectNoWildcard; + } + + private NamedWindowOnMergeActionIns SetupInsert(string namedWindowName, InternalEventRouter internalEventRouter, EventTypeSPI eventTypeNamedWindow, int selectClauseNumber, OnTriggerMergeActionInsert desc, EventType triggeringEventType, string triggeringStreamName, StatementContext statementContext) + { + + // Compile insert-into INFO + string streamName = desc.OptionalStreamName ?? eventTypeNamedWindow.Name; + var insertIntoDesc = InsertIntoDesc.FromColumns(streamName, desc.Columns); + + // rewrite any wildcards to use "stream.wildcard" + if (triggeringStreamName == null) { + triggeringStreamName = UuidGenerator.Generate(); + } + var selectNoWildcard = CompileSelectNoWildcard(triggeringStreamName, desc.SelectClauseCompiled); + + // Set up event types for select-clause evaluation: The first type does not contain anything as its the named window row which is not present for insert + var dummyTypeNoProperties = new MapEventType(EventTypeMetadata.CreateAnonymous("merge_named_window_insert", ApplicationType.MAP), "merge_named_window_insert", 0, null, Collections.EmptyDataMap, null, null, null); + var eventTypes = new EventType[]{dummyTypeNoProperties, triggeringEventType}; + var streamNames = new string[]{UuidGenerator.Generate(), triggeringStreamName}; + var streamTypeService = new StreamTypeServiceImpl(eventTypes, streamNames, new bool[1], statementContext.EngineURI, false); + + // Get select expr processor + var selectExprEventTypeRegistry = new SelectExprEventTypeRegistry(statementContext.StatementName, statementContext.StatementEventTypeRef); + var exprEvaluatorContext = new ExprEvaluatorContextStatement(statementContext, false); + var insertHelper = SelectExprProcessorFactory.GetProcessor(Collections.SingletonList(selectClauseNumber), + selectNoWildcard.ToArray(), false, insertIntoDesc, null, null, streamTypeService, + statementContext.EventAdapterService, + statementContext.StatementResultService, + statementContext.ValueAddEventService, + selectExprEventTypeRegistry, + statementContext.EngineImportService, + exprEvaluatorContext, + statementContext.VariableService, + statementContext.ScriptingService, + statementContext.TableService, + statementContext.TimeProvider, + statementContext.EngineURI, + statementContext.StatementId, + statementContext.StatementName, + statementContext.Annotations, + statementContext.ContextDescriptor, + statementContext.ConfigSnapshot, null, + statementContext.NamedWindowMgmtService, null, null, + statementContext.StatementExtensionServicesContext); + var filterEval = desc.OptionalWhereClause == null ? null : desc.OptionalWhereClause.ExprEvaluator; + + var routerToUser = streamName.Equals(namedWindowName) ? null : internalEventRouter; + var audit = AuditEnum.INSERT.GetAudit(statementContext.Annotations) != null; + + string insertIntoTableName = null; + if (statementContext.TableService.GetTableMetadata(insertIntoDesc.EventTypeName) != null) { + insertIntoTableName = insertIntoDesc.EventTypeName; + } + + return new NamedWindowOnMergeActionIns(filterEval, insertHelper, routerToUser, insertIntoTableName, statementContext.TableService, statementContext.EpStatementHandle, statementContext.InternalEventEngineRouteDest, audit); + } + + public IList Matched + { + get { return _matched; } + } + + public IList Unmatched + { + get { return _unmatched; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/named/NamedWindowOnMergeMatch.cs b/NEsper.Core/NEsper.Core/epl/named/NamedWindowOnMergeMatch.cs new file mode 100755 index 000000000..0fe0b5d88 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/named/NamedWindowOnMergeMatch.cs @@ -0,0 +1,66 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.named +{ + public class NamedWindowOnMergeMatch + { + private readonly ExprEvaluator _optionalCond; + private readonly IList _actions; + + public NamedWindowOnMergeMatch(ExprNode optionalCond, IList actions) + { + _optionalCond = optionalCond != null ? optionalCond.ExprEvaluator : null; + _actions = actions; + } + + public bool IsApplies(EventBean[] eventsPerStream, ExprEvaluatorContext context) + { + if (_optionalCond == null) { + return true; + } + + var evaluateParams = new EvaluateParams(eventsPerStream, true, context); + var result = _optionalCond.Evaluate(evaluateParams); + return result != null && (bool) result; + } + + public void Apply(EventBean matchingEvent, EventBean[] eventsPerStream, OneEventCollection newData, OneEventCollection oldData, ExprEvaluatorContext context) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QInfraMergeWhenThenActions(_actions.Count); } + + int count = -1; + foreach (NamedWindowOnMergeAction action in _actions) { + count++; + + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().QInfraMergeWhenThenActionItem(count, action.GetName()); + bool applies = action.IsApplies(eventsPerStream, context); + if (applies) { + action.Apply(matchingEvent, eventsPerStream, newData, oldData, context); + } + InstrumentationHelper.Get().AInfraMergeWhenThenActionItem(applies); + continue; + } + + if (action.IsApplies(eventsPerStream, context)) { + action.Apply(matchingEvent, eventsPerStream, newData, oldData, context); + } + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AInfraMergeWhenThenActions(); } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/named/NamedWindowOnMergeView.cs b/NEsper.Core/NEsper.Core/epl/named/NamedWindowOnMergeView.cs new file mode 100755 index 000000000..0ec1e4692 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/named/NamedWindowOnMergeView.cs @@ -0,0 +1,147 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.epl.metric; +using com.espertech.esper.epl.spec; +using com.espertech.esper.events; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.named +{ + /// + /// View for the on-delete statement that handles removing events from a named window. + /// + public class NamedWindowOnMergeView : NamedWindowOnExprBaseView + { + private readonly NamedWindowOnMergeViewFactory _parent; + private EventBean[] _lastResult; + + public NamedWindowOnMergeView(SubordWMatchExprLookupStrategy lookupStrategy, NamedWindowRootViewInstance rootView, ExprEvaluatorContext exprEvaluatorContext, NamedWindowOnMergeViewFactory parent) + : base(lookupStrategy, rootView, exprEvaluatorContext) + { + _parent = parent; + } + + public override void HandleMatching(EventBean[] triggerEvents, EventBean[] matchingEvents) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QInfraOnAction(OnTriggerType.ON_MERGE, triggerEvents, matchingEvents);} + + var newData = new OneEventCollection(); + OneEventCollection oldData = null; + var eventsPerStream = new EventBean[3]; // first:named window, second: trigger, third:before-update (optional) + + if ((matchingEvents == null) || (matchingEvents.Length == 0)){ + + var unmatched = _parent.NamedWindowOnMergeHelper.Unmatched; + + foreach (var triggerEvent in triggerEvents) { + eventsPerStream[1] = triggerEvent; + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QInfraMergeWhenThens(false, triggerEvent, unmatched.Count);} + + var count = -1; + foreach (var action in unmatched) { + count++; + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QInfraMergeWhenThenItem(false, count);} + if (!action.IsApplies(eventsPerStream, base.ExprEvaluatorContext)) { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AInfraMergeWhenThenItem(false, false);} + continue; + } + action.Apply(null, eventsPerStream, newData, oldData, base.ExprEvaluatorContext); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AInfraMergeWhenThenItem(false, true);} + break; // apply no other actions + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AInfraMergeWhenThens(false);} + } + } + else { + + // handle update/ + oldData = new OneEventCollection(); + + var matched = _parent.NamedWindowOnMergeHelper.Matched; + + foreach (var triggerEvent in triggerEvents) { + eventsPerStream[1] = triggerEvent; + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QInfraMergeWhenThens(true, triggerEvent, matched.Count);} + + foreach (var matchingEvent in matchingEvents) { + eventsPerStream[0] = matchingEvent; + + var count = -1; + foreach (var action in matched) { + count++; + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QInfraMergeWhenThenItem(true, count);} + if (!action.IsApplies(eventsPerStream, base.ExprEvaluatorContext)) { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AInfraMergeWhenThenItem(true, false);} + continue; + } + action.Apply(matchingEvent, eventsPerStream, newData, oldData, base.ExprEvaluatorContext); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AInfraMergeWhenThenItem(true, true);} + break; // apply no other actions + } + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AInfraMergeWhenThens(true);} + } + + } + + if (!newData.IsEmpty() || (oldData != null && !oldData.IsEmpty())) + { + if ((MetricReportingPath.IsMetricsEnabled) && (_parent.CreateNamedWindowMetricHandle.IsEnabled) && !newData.IsEmpty()) + { + _parent.MetricReportingService.AccountTime(_parent.CreateNamedWindowMetricHandle, 0, 0, newData.ToArray().Length); + } + + // Events to delete are indicated via old data + // The on-merge listeners receive the events deleted, but only if there is interest + if (_parent.StatementResultService.IsMakeNatural) { + var eventsPerStreamNaturalNew = newData.IsEmpty() ? null : newData.ToArray(); + var eventsPerStreamNaturalOld = (oldData == null || oldData.IsEmpty()) ? null : oldData.ToArray(); + RootView.Update(EventBeanUtility.Denaturalize(eventsPerStreamNaturalNew), EventBeanUtility.Denaturalize(eventsPerStreamNaturalOld)); + UpdateChildren(eventsPerStreamNaturalNew, eventsPerStreamNaturalOld); + } + else { + var eventsPerStreamNew = newData.IsEmpty() ? null : newData.ToArray(); + var eventsPerStreamOld = (oldData == null || oldData.IsEmpty()) ? null : oldData.ToArray(); + RootView.Update(eventsPerStreamNew, eventsPerStreamOld); + if (_parent.StatementResultService.IsMakeSynthetic) { + UpdateChildren(eventsPerStreamNew, eventsPerStreamOld); + } + } + } + + // Keep the last delete records + _lastResult = matchingEvents; + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AInfraOnAction();} + } + + public override EventType EventType + { + get { return RootView.EventType; } + } + + public override IEnumerator GetEnumerator() + { + if (_lastResult == null) + return EnumerationHelper.Empty(); + return ((IEnumerable)_lastResult).GetEnumerator(); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/named/NamedWindowOnMergeViewFactory.cs b/NEsper.Core/NEsper.Core/epl/named/NamedWindowOnMergeViewFactory.cs new file mode 100755 index 000000000..958653e1d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/named/NamedWindowOnMergeViewFactory.cs @@ -0,0 +1,66 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.epl.metric; + +namespace com.espertech.esper.epl.named +{ + /// + /// View for the on-delete statement that handles removing events from a named window. + /// + public class NamedWindowOnMergeViewFactory : NamedWindowOnExprBaseViewFactory + { + private readonly StatementMetricHandle _createNamedWindowMetricHandle; + private readonly MetricReportingService _metricReportingService; + private readonly NamedWindowOnMergeHelper _namedWindowOnMergeHelper; + private readonly StatementResultService _statementResultService; + + public NamedWindowOnMergeViewFactory(EventType namedWindowEventType, + NamedWindowOnMergeHelper namedWindowOnMergeHelper, + StatementResultService statementResultService, + StatementMetricHandle createNamedWindowMetricHandle, + MetricReportingService metricReportingService) + : base(namedWindowEventType) + { + _namedWindowOnMergeHelper = namedWindowOnMergeHelper; + _statementResultService = statementResultService; + _createNamedWindowMetricHandle = createNamedWindowMetricHandle; + _metricReportingService = metricReportingService; + } + + public NamedWindowOnMergeHelper NamedWindowOnMergeHelper + { + get { return _namedWindowOnMergeHelper; } + } + + public StatementResultService StatementResultService + { + get { return _statementResultService; } + } + + public StatementMetricHandle CreateNamedWindowMetricHandle + { + get { return _createNamedWindowMetricHandle; } + } + + public MetricReportingService MetricReportingService + { + get { return _metricReportingService; } + } + + public override NamedWindowOnExprBaseView Make(SubordWMatchExprLookupStrategy lookupStrategy, NamedWindowRootViewInstance namedWindowRootViewInstance, AgentInstanceContext agentInstanceContext, ResultSetProcessor resultSetProcessor) + { + return new NamedWindowOnMergeView(lookupStrategy, namedWindowRootViewInstance, agentInstanceContext, this); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/named/NamedWindowOnSelectView.cs b/NEsper.Core/NEsper.Core/epl/named/NamedWindowOnSelectView.cs new file mode 100755 index 000000000..a6bbf74c0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/named/NamedWindowOnSelectView.cs @@ -0,0 +1,166 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.events; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.named +{ + /// + /// View for the on-select statement that handles selecting events from a named window. + /// + public class NamedWindowOnSelectView : NamedWindowOnExprBaseView + { + private readonly NamedWindowOnSelectViewFactory _parent; + private readonly ResultSetProcessor _resultSetProcessor; + private EventBean[] _lastResult; + private readonly ISet> _oldEvents = new HashSet>(); + private readonly bool _audit; + private readonly bool _isDelete; + private readonly TableStateInstance _tableStateInstanceInsertInto; + + public NamedWindowOnSelectView(SubordWMatchExprLookupStrategy lookupStrategy, NamedWindowRootViewInstance rootView, ExprEvaluatorContext exprEvaluatorContext, NamedWindowOnSelectViewFactory parent, ResultSetProcessor resultSetProcessor, bool audit, bool isDelete, TableStateInstance tableStateInstanceInsertInto) + : base(lookupStrategy, rootView, exprEvaluatorContext) + { + _parent = parent; + _resultSetProcessor = resultSetProcessor; + _audit = audit; + _isDelete = isDelete; + _tableStateInstanceInsertInto = tableStateInstanceInsertInto; + } + + public override void HandleMatching(EventBean[] triggerEvents, EventBean[] matchingEvents) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QInfraOnAction(OnTriggerType.ON_SELECT, triggerEvents, matchingEvents); } + + EventBean[] newData; + + // clear state from prior results + _resultSetProcessor.Clear(); + + // build join result + // use linked hash set to retain order of join results for last/first/window to work most intuitively + var newEvents = BuildJoinResult(triggerEvents, matchingEvents); + + // process matches + var pair = _resultSetProcessor.ProcessJoinResult(newEvents, _oldEvents, false); + newData = (pair != null ? pair.First : null); + + if (_parent.IsDistinct) + { + newData = EventBeanUtility.GetDistinctByProp(newData, _parent.EventBeanReader); + } + + if (_tableStateInstanceInsertInto != null) + { + if (newData != null) + { + foreach (var aNewData in newData) + { + if (_audit) + { + AuditPath.AuditInsertInto(ExprEvaluatorContext.EngineURI, ExprEvaluatorContext.StatementName, aNewData); + } + _tableStateInstanceInsertInto.AddEventUnadorned(aNewData); + } + } + } + else if (_parent.InternalEventRouter != null) + { + if (newData != null) + { + foreach (var aNewData in newData) + { + if (_audit) + { + AuditPath.AuditInsertInto(ExprEvaluatorContext.EngineURI, ExprEvaluatorContext.StatementName, aNewData); + } + _parent.InternalEventRouter.Route(aNewData, _parent.StatementHandle, _parent.InternalEventRouteDest, ExprEvaluatorContext, _parent.IsAddToFront); + } + } + } + + // The on-select listeners receive the events selected + if ((newData != null) && (newData.Length > 0)) + { + // And post only if we have listeners/subscribers that need the data + if (_parent.StatementResultService.IsMakeNatural || _parent.StatementResultService.IsMakeSynthetic) + { + UpdateChildren(newData, null); + } + } + _lastResult = newData; + + // clear state from prior results + _resultSetProcessor.Clear(); + + // Events to delete are indicated via old data + if (_isDelete) + { + RootView.Update(null, matchingEvents); + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AInfraOnAction(); } + } + + public static ISet> BuildJoinResult(EventBean[] triggerEvents, EventBean[] matchingEvents) + { + var events = new LinkedHashSet>(); + for (var i = 0; i < triggerEvents.Length; i++) + { + var triggerEvent = triggerEvents[0]; + if (matchingEvents != null) + { + for (var j = 0; j < matchingEvents.Length; j++) + { + var eventsPerStream = new EventBean[2]; + eventsPerStream[0] = matchingEvents[j]; + eventsPerStream[1] = triggerEvent; + events.Add(new MultiKey(eventsPerStream)); + } + } + } + return events; + } + + public override EventType EventType + { + get + { + if (_resultSetProcessor != null) + { + return _resultSetProcessor.ResultEventType; + } + else + { + return RootView.EventType; + } + } + } + + public override IEnumerator GetEnumerator() + { + if (_lastResult == null) + return EnumerationHelper.Empty(); + return ((IEnumerable)_lastResult).GetEnumerator(); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/named/NamedWindowOnSelectViewFactory.cs b/NEsper.Core/NEsper.Core/epl/named/NamedWindowOnSelectViewFactory.cs new file mode 100755 index 000000000..ec771eb81 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/named/NamedWindowOnSelectViewFactory.cs @@ -0,0 +1,70 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.client.annotation; +using com.espertech.esper.client.soda; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.named +{ + /// + /// View for the on-select statement that handles selecting events from a named window. + /// + public class NamedWindowOnSelectViewFactory : NamedWindowOnExprBaseViewFactory + { + private readonly bool _deleteAndSelect; + private readonly string _optionalInsertIntoTableName; + + public NamedWindowOnSelectViewFactory(EventType namedWindowEventType, InternalEventRouter internalEventRouter, bool addToFront, EPStatementHandle statementHandle, EventBeanReader eventBeanReader, bool distinct, StatementResultService statementResultService, InternalEventRouteDest internalEventRouteDest, bool deleteAndSelect, StreamSelector? optionalStreamSelector, string optionalInsertIntoTableName) + : base(namedWindowEventType) + { + InternalEventRouter = internalEventRouter; + IsAddToFront = addToFront; + StatementHandle = statementHandle; + EventBeanReader = eventBeanReader; + IsDistinct = distinct; + StatementResultService = statementResultService; + InternalEventRouteDest = internalEventRouteDest; + _deleteAndSelect = deleteAndSelect; + OptionalStreamSelector = optionalStreamSelector; + _optionalInsertIntoTableName = optionalInsertIntoTableName; + } + + public override NamedWindowOnExprBaseView Make(SubordWMatchExprLookupStrategy lookupStrategy, NamedWindowRootViewInstance namedWindowRootViewInstance, AgentInstanceContext agentInstanceContext, ResultSetProcessor resultSetProcessor) + { + bool audit = AuditEnum.INSERT.GetAudit(agentInstanceContext.StatementContext.Annotations) != null; + TableStateInstance tableStateInstance = null; + if (_optionalInsertIntoTableName != null) { + tableStateInstance = agentInstanceContext.StatementContext.TableService.GetState(_optionalInsertIntoTableName, agentInstanceContext.AgentInstanceId); + } + return new NamedWindowOnSelectView(lookupStrategy, namedWindowRootViewInstance, agentInstanceContext, this, resultSetProcessor, audit, _deleteAndSelect, tableStateInstance); + } + + public InternalEventRouter InternalEventRouter { get; private set; } + + public bool IsAddToFront { get; private set; } + + public EPStatementHandle StatementHandle { get; private set; } + + public EventBeanReader EventBeanReader { get; private set; } + + public bool IsDistinct { get; private set; } + + public StatementResultService StatementResultService { get; private set; } + + public InternalEventRouteDest InternalEventRouteDest { get; private set; } + + public StreamSelector? OptionalStreamSelector { get; private set; } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/named/NamedWindowOnUpdateView.cs b/NEsper.Core/NEsper.Core/epl/named/NamedWindowOnUpdateView.cs new file mode 100755 index 000000000..d3162ed83 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/named/NamedWindowOnUpdateView.cs @@ -0,0 +1,86 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.epl.spec; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.named +{ + /// + /// View for the on-delete statement that handles removing events from a named window. + /// + public class NamedWindowOnUpdateView : NamedWindowOnExprBaseView + { + private readonly NamedWindowOnUpdateViewFactory _parent; + private EventBean[] _lastResult; + + public NamedWindowOnUpdateView(SubordWMatchExprLookupStrategy lookupStrategy, NamedWindowRootViewInstance rootView, ExprEvaluatorContext exprEvaluatorContext, NamedWindowOnUpdateViewFactory parent) + : base(lookupStrategy, rootView, exprEvaluatorContext) + { + _parent = parent; + } + + public override void HandleMatching(EventBean[] triggerEvents, EventBean[] matchingEvents) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QInfraOnAction(OnTriggerType.ON_UPDATE, triggerEvents, matchingEvents);} + + if ((matchingEvents == null) || (matchingEvents.Length == 0)){ + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AInfraOnAction();} + return; + } + + var eventsPerStream = new EventBean[3]; + + var newData = new OneEventCollection(); + var oldData = new OneEventCollection(); + + foreach (var triggerEvent in triggerEvents) { + eventsPerStream[1] = triggerEvent; + foreach (var matchingEvent in matchingEvents) { + var copy = _parent.UpdateHelper.UpdateWCopy(matchingEvent, eventsPerStream, base.ExprEvaluatorContext); + newData.Add(copy); + oldData.Add(matchingEvent); + } + } + + if (!newData.IsEmpty()) + { + // Events to delete are indicated via old data + RootView.Update(newData.ToArray(), oldData.ToArray()); + + // The on-delete listeners receive the events deleted, but only if there is interest + if (_parent.StatementResultService.IsMakeNatural || _parent.StatementResultService.IsMakeSynthetic) { + UpdateChildren(newData.ToArray(), oldData.ToArray()); + } + } + + // Keep the last delete records + _lastResult = matchingEvents; + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AInfraOnAction();} + } + + public override EventType EventType + { + get { return RootView.EventType; } + } + + public override IEnumerator GetEnumerator() + { + if (_lastResult == null) + return EnumerationHelper.Empty(); + return ((IEnumerable)_lastResult).GetEnumerator(); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/named/NamedWindowOnUpdateViewFactory.cs b/NEsper.Core/NEsper.Core/epl/named/NamedWindowOnUpdateViewFactory.cs new file mode 100755 index 000000000..f3640f182 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/named/NamedWindowOnUpdateViewFactory.cs @@ -0,0 +1,52 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.epl.updatehelper; + +namespace com.espertech.esper.epl.named +{ + /// + /// View for the on-delete statement that handles removing events from a named window. + /// + public class NamedWindowOnUpdateViewFactory : NamedWindowOnExprBaseViewFactory + { + private readonly StatementResultService _statementResultService; + private readonly EventBeanUpdateHelper _updateHelper; + + public NamedWindowOnUpdateViewFactory(EventType namedWindowEventType, StatementResultService statementResultService, EventBeanUpdateHelper updateHelper) + : base(namedWindowEventType) + { + _statementResultService = statementResultService; + _updateHelper = updateHelper; + } + + public override NamedWindowOnExprBaseView Make( + SubordWMatchExprLookupStrategy lookupStrategy, + NamedWindowRootViewInstance namedWindowRootViewInstance, + AgentInstanceContext agentInstanceContext, + ResultSetProcessor resultSetProcessor) + { + return new NamedWindowOnUpdateView(lookupStrategy, namedWindowRootViewInstance, agentInstanceContext, this); + } + + public StatementResultService StatementResultService + { + get { return _statementResultService; } + } + + public EventBeanUpdateHelper UpdateHelper + { + get { return _updateHelper; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/named/NamedWindowProcessor.cs b/NEsper.Core/NEsper.Core/epl/named/NamedWindowProcessor.cs new file mode 100755 index 000000000..508e02bd4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/named/NamedWindowProcessor.cs @@ -0,0 +1,401 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.threading; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.core.service.resource; +using com.espertech.esper.core.start; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.epl.metric; +using com.espertech.esper.events.vaevent; + +namespace com.espertech.esper.epl.named +{ + /// + /// An instance of this class is associated with a specific named window. The processor + /// provides the views to create-window, on-delete statements and statements selecting from a named window. + /// + public class NamedWindowProcessor + { + private readonly string _namedWindowName; + private readonly NamedWindowTailView _tailView; + private readonly NamedWindowRootView _rootView; + private readonly string _contextName; + private readonly EventType _eventType; + private readonly string _eplExpression; + private readonly string _statementName; + private readonly bool _isEnableSubqueryIndexShare; + private readonly bool _isVirtualDataWindow; + private readonly ICollection _optionalUniqueKeyProps; + private readonly string _eventTypeAsName; + private readonly EventTableIndexMetadata _eventTableIndexMetadataRepo = new EventTableIndexMetadata(); + private readonly StatementContext _statementContextCreateWindow; + + private readonly ILockable _lock = LockManager.CreateLock(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// Ctor. + /// + /// Name of the named window. + /// service for dispatching results + /// The named window dispatch service. + /// Name of the context. + /// the type of event held by the named window + /// for coordinating on whether insert and remove stream events should be posted + /// for revision processing + /// epl expression + /// statement name + /// if the engine is running with prioritized execution + /// if set to true [is enable subquery index share]. + /// if set to true [enable query plan log]. + /// The metric reporting service. + /// if set to true [is batching data window]. + /// if set to true [is virtual data window]. + /// The optional unique key props. + /// Name of the event type as. + /// The statement context create window. + public NamedWindowProcessor( + string namedWindowName, + NamedWindowMgmtService namedWindowMgmtService, + NamedWindowDispatchService namedWindowDispatchService, + string contextName, + EventType eventType, + StatementResultService statementResultService, + ValueAddEventProcessor revisionProcessor, + string eplExpression, + string statementName, + bool isPrioritized, + bool isEnableSubqueryIndexShare, + bool enableQueryPlanLog, + MetricReportingService metricReportingService, + bool isBatchingDataWindow, + bool isVirtualDataWindow, + ICollection optionalUniqueKeyProps, + string eventTypeAsName, + StatementContext statementContextCreateWindow) + { + _namedWindowName = namedWindowName; + _contextName = contextName; + _eventType = eventType; + _eplExpression = eplExpression; + _statementName = statementName; + _isEnableSubqueryIndexShare = isEnableSubqueryIndexShare; + _isVirtualDataWindow = isVirtualDataWindow; + _optionalUniqueKeyProps = optionalUniqueKeyProps; + _eventTypeAsName = eventTypeAsName; + _statementContextCreateWindow = statementContextCreateWindow; + + _rootView = new NamedWindowRootView(revisionProcessor, enableQueryPlanLog, metricReportingService, eventType, isBatchingDataWindow, isEnableSubqueryIndexShare, optionalUniqueKeyProps); + _tailView = namedWindowDispatchService.CreateTailView( + eventType, namedWindowMgmtService, namedWindowDispatchService, statementResultService, revisionProcessor, + isPrioritized, isBatchingDataWindow, contextName, statementContextCreateWindow.TimeSourceService, + statementContextCreateWindow.ConfigSnapshot.EngineDefaults.Threading); + } + + public string EventTypeAsName + { + get { return _eventTypeAsName; } + } + + public NamedWindowProcessorInstance AddInstance(AgentInstanceContext agentInstanceContext) + { + using (_lock.Acquire()) + { + if (_contextName == null) + { + return new NamedWindowProcessorInstance(null, this, agentInstanceContext); + } + + int instanceId = agentInstanceContext.AgentInstanceId; + return new NamedWindowProcessorInstance(instanceId, this, agentInstanceContext); + } + } + + public NamedWindowProcessorInstance ProcessorInstanceNoContext + { + get + { + StatementResourceHolder holder = + _statementContextCreateWindow.StatementExtensionServicesContext.StmtResources.Unpartitioned; + return holder == null ? null : holder.NamedWindowProcessorInstance; + } + } + + public NamedWindowProcessorInstance GetProcessorInstance(int agentInstanceId) + { + StatementResourceHolder holder = _statementContextCreateWindow.StatementExtensionServicesContext.StmtResources.GetPartitioned(agentInstanceId); + return holder == null ? null : holder.NamedWindowProcessorInstance; + } + + public NamedWindowProcessorInstance GetProcessorInstanceAllowUnpartitioned(int agentInstanceId) + { + if (agentInstanceId == EPStatementStartMethodConst.DEFAULT_AGENT_INSTANCE_ID) + { + return ProcessorInstanceNoContext; + } + StatementResourceHolder holder = _statementContextCreateWindow.StatementExtensionServicesContext.StmtResources.GetPartitioned(agentInstanceId); + return holder == null ? null : holder.NamedWindowProcessorInstance; + } + + public ICollection ProcessorInstancesAll + { + get + { + using (_lock.Acquire()) + { + var keyset = + _statementContextCreateWindow.StatementExtensionServicesContext.StmtResources.ResourcesPartitioned.Keys; + return new ArrayDeque(keyset); + } + } + } + + public NamedWindowProcessorInstance GetProcessorInstance(AgentInstanceContext agentInstanceContext) + { + if (_contextName == null) + { + return ProcessorInstanceNoContext; + } + + if (agentInstanceContext.StatementContext.ContextDescriptor == null) + { + return null; + } + + if (_contextName.Equals(agentInstanceContext.StatementContext.ContextDescriptor.ContextName)) + { + return GetProcessorInstance(agentInstanceContext.AgentInstanceId); + } + + return null; + } + + public string ContextName + { + get { return _contextName; } + } + + public NamedWindowConsumerView AddConsumer(NamedWindowConsumerDesc consumerDesc, bool isSubselect) + { + StatementResourceService statementResourceService = _statementContextCreateWindow.StatementExtensionServicesContext.StmtResources; + + // handle same-context consumer + if (_contextName != null) + { + ContextDescriptor contextDescriptor = consumerDesc.AgentInstanceContext.StatementContext.ContextDescriptor; + if (contextDescriptor != null && _contextName.Equals(contextDescriptor.ContextName)) + { + StatementResourceHolder holder = statementResourceService.GetPartitioned(consumerDesc.AgentInstanceContext.AgentInstanceId); + return holder.NamedWindowProcessorInstance.TailViewInstance.AddConsumer(consumerDesc, isSubselect); + } + else + { + // consumer is out-of-context + return _tailView.AddConsumer(consumerDesc); // non-context consumers + } + } + + // handle no context associated + return statementResourceService.ResourcesUnpartitioned.NamedWindowProcessorInstance.TailViewInstance.AddConsumer(consumerDesc, isSubselect); + } + + public bool IsVirtualDataWindow + { + get { return _isVirtualDataWindow; } + } + + /// + /// Returns the tail view of the named window, hooked into the view chain after the named window's data window views, + /// as the last view. + /// + /// tail view + public NamedWindowTailView TailView + { + get { return _tailView; } // hooked as the tail sview before any data windows + } + + /// + /// Returns the root view of the named window, hooked into the view chain before the named window's data window views, + /// right after the filter stream that filters for insert-into events. + /// + /// tail view + public NamedWindowRootView RootView + { + get { return _rootView; } // hooked as the top view before any data windows + } + + /// + /// Returns the event type of the named window. + /// + /// event type + public EventType NamedWindowType + { + get { return _eventType; } + } + + /// + /// Returns the EPL expression. + /// + /// epl + public string EplExpression + { + get { return _eplExpression; } + } + + /// + /// Returns the statement name. + /// + /// name + public string StatementName + { + get { return _statementName; } + } + + /// + /// Deletes a named window and removes any associated resources. + /// + public void Dispose() + { + } + + public bool IsEnableSubqueryIndexShare + { + get { return _isEnableSubqueryIndexShare; } + } + + public StatementMetricHandle CreateNamedWindowMetricsHandle + { + get { return _statementContextCreateWindow.EpStatementHandle.MetricsHandle; } + } + + public string NamedWindowName + { + get { return _namedWindowName; } + } + + public string[][] UniqueIndexes + { + get + { + IList unique = null; + + var indexDescriptors = EventTableIndexMetadataRepo.Indexes.Keys; + foreach (var index in indexDescriptors) + { + if (!index.IsUnique) + { + continue; + } + string[] uniqueKeys = IndexedPropDesc.GetIndexProperties(index.HashIndexedProps); + if (unique == null) + { + unique = new List(); + } + unique.Add(uniqueKeys); + } + if (_optionalUniqueKeyProps != null) + { + if (unique == null) + { + unique = new List(); + } + unique.Add(_optionalUniqueKeyProps.ToArray()); + } + if (unique == null) + { + return null; + } + return unique.ToArray(); + } + } + + public ICollection OptionalUniqueKeyProps + { + get { return _optionalUniqueKeyProps; } + } + + public EventTableIndexMetadata EventTableIndexMetadataRepo + { + get { return _eventTableIndexMetadataRepo; } + } + + public StatementContext StatementContextCreateWindow + { + get { return _statementContextCreateWindow; } + } + + public void RemoveAllInstanceIndexes(IndexMultiKey index) + { + StatementResourceService statementResourceService = _statementContextCreateWindow.StatementExtensionServicesContext.StmtResources; + + if (_contextName == null) + { + StatementResourceHolder holder = statementResourceService.Unpartitioned; + if (holder != null && holder.NamedWindowProcessorInstance != null) + { + holder.NamedWindowProcessorInstance.RemoveIndex(index); + } + } + else + { + foreach (var entry in statementResourceService.ResourcesPartitioned) + { + if (entry.Value.NamedWindowProcessorInstance != null) + { + entry.Value.NamedWindowProcessorInstance.RemoveIndex(index); + } + } + } + } + + public void ValidateAddIndex(string statementName, string indexName, IndexMultiKey imk) + { + _eventTableIndexMetadataRepo.AddIndex(false, imk, indexName, statementName, true, null); + } + + public void RemoveIndexReferencesStmtMayRemoveIndex(IndexMultiKey imk, string finalStatementName) + { + bool last = _eventTableIndexMetadataRepo.RemoveIndexReference(imk, finalStatementName); + if (last) + { + _eventTableIndexMetadataRepo.RemoveIndex(imk); + RemoveAllInstanceIndexes(imk); + } + } + + public void ClearProcessorInstances() + { + if (_contextName == null) + { + NamedWindowProcessorInstance instance = ProcessorInstanceNoContext; + if (instance != null) + { + instance.Dispose(); + } + return; + } + var cpids = ProcessorInstancesAll; + foreach (int cpid in cpids) + { + NamedWindowProcessorInstance instance = GetProcessorInstance(cpid); + if (instance != null) + { + instance.Dispose(); + } + return; + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/named/NamedWindowProcessorInstance.cs b/NEsper.Core/NEsper.Core/epl/named/NamedWindowProcessorInstance.cs new file mode 100755 index 000000000..5a92324ac --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/named/NamedWindowProcessorInstance.cs @@ -0,0 +1,102 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.factory; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.lookup; + +namespace com.espertech.esper.epl.named +{ + /// + /// An instance of this class is associated with a specific named window. The processor + /// provides the views to create-window, on-delete statements and statements selecting from a named window. + /// + public class NamedWindowProcessorInstance + { + private readonly int? _agentInstanceId; + private readonly NamedWindowTailViewInstance _tailViewInstance; + private readonly NamedWindowRootViewInstance _rootViewInstance; + + public NamedWindowProcessorInstance(int? agentInstanceId, NamedWindowProcessor processor, AgentInstanceContext agentInstanceContext) { + _agentInstanceId = agentInstanceId; + _rootViewInstance = new NamedWindowRootViewInstance(processor.RootView, agentInstanceContext, processor.EventTableIndexMetadataRepo); + _tailViewInstance = new NamedWindowTailViewInstance(_rootViewInstance, processor.TailView, processor, agentInstanceContext); + _rootViewInstance.DataWindowContents = _tailViewInstance; // for iteration used for delete without index + } + + public NamedWindowTailViewInstance TailViewInstance + { + get { return _tailViewInstance; } + } + + public NamedWindowRootViewInstance RootViewInstance + { + get { return _rootViewInstance; } + } + + /// + /// Returns the number of events held. + /// + /// number of events + public long CountDataWindow + { + get { return _tailViewInstance.NumberOfEvents; } + } + + /// + /// Deletes a named window and removes any associated resources. + /// + public void Dispose() + { + _tailViewInstance.Dispose(); + _rootViewInstance.Dispose(); + } + + public void Stop() + { + _tailViewInstance.Stop(); + _rootViewInstance.Stop(); + } + + public IndexMultiKey[] IndexDescriptors + { + get { return _rootViewInstance.Indexes; } + } + + public int? AgentInstanceId + { + get { return _agentInstanceId; } + } + + public StatementAgentInstancePostLoad PostLoad + { + get + { + return new ProxyStatementAgentInstancePostLoad() + { + ProcExecutePostLoad = () => _rootViewInstance.PostLoad(), + ProcAcceptIndexVisitor = (visitor) => _rootViewInstance.VisitIndexes(visitor), + }; + } + } + + public void RemoveIndex(IndexMultiKey index) + { + _rootViewInstance.IndexRepository.RemoveIndex(index); + } + + public void RemoveExplicitIndex(string indexName) + { + _rootViewInstance.IndexRepository.RemoveExplicitIndex(indexName); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/named/NamedWindowRootView.cs b/NEsper.Core/NEsper.Core/epl/named/NamedWindowRootView.cs new file mode 100755 index 000000000..edfad37c6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/named/NamedWindowRootView.cs @@ -0,0 +1,65 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.metric; +using com.espertech.esper.events.vaevent; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.named +{ + /// + /// The root window in a named window plays multiple roles: It holds the indexes for + /// deleting rows, if any on-delete statement requires such indexes. Such indexes are + /// updated when events arrive, or remove from when a data window or on-delete statement + /// expires events. The view keeps track of on-delete statements their indexes used. + /// + public class NamedWindowRootView + { + public static readonly ILog QueryPlanLogInstance = LogManager.GetLogger(AuditPath.QUERYPLAN_LOG); + + public static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public NamedWindowRootView(ValueAddEventProcessor revisionProcessor, + bool queryPlanLogging, + MetricReportingService metricReportingService, + EventType eventType, + bool childBatching, + bool isEnableIndexShare, + ICollection optionalUniqueKeyProps) + { + RevisionProcessor = revisionProcessor; + IsQueryPlanLogging = queryPlanLogging; + EventType = eventType; + IsChildBatching = childBatching; + IsEnableIndexShare = isEnableIndexShare; + OptionalUniqueKeyProps = optionalUniqueKeyProps; + } + + public static ILog QueryPlanLog + { + get { return QueryPlanLogInstance; } + } + + public ValueAddEventProcessor RevisionProcessor { get; private set; } + + public bool IsChildBatching { get; private set; } + + public bool IsQueryPlanLogging { get; private set; } + + public EventType EventType { get; private set; } + + public bool IsEnableIndexShare { get; private set; } + + public ICollection OptionalUniqueKeyProps { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/named/NamedWindowRootViewInstance.cs b/NEsper.Core/NEsper.Core/epl/named/NamedWindowRootViewInstance.cs new file mode 100755 index 000000000..e4a91fccd --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/named/NamedWindowRootViewInstance.cs @@ -0,0 +1,243 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.core.context.factory; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.fafquery; +using com.espertech.esper.epl.@join.table; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.virtualdw; +using com.espertech.esper.filter; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.epl.named +{ + /// + /// The root window in a named window plays multiple roles: It holds the indexes for deleting rows, if any on-delete statement + /// requires such indexes. Such indexes are updated when events arrive, or remove from when a data window + /// or on-delete statement expires events. The view keeps track of on-delete statements their indexes used. + /// + public class NamedWindowRootViewInstance : ViewSupport + { + private readonly NamedWindowRootView _rootView; + private readonly AgentInstanceContext _agentInstanceContext; + + private readonly EventTableIndexRepository _indexRepository; + private readonly IDictionary _tablePerMultiLookup; + + private IEnumerable _dataWindowContents; + + public NamedWindowRootViewInstance(NamedWindowRootView rootView, AgentInstanceContext agentInstanceContext, EventTableIndexMetadata eventTableIndexMetadata) + { + _rootView = rootView; + _agentInstanceContext = agentInstanceContext; + + _indexRepository = new EventTableIndexRepository(); + foreach (KeyValuePair entry in eventTableIndexMetadata.Indexes) + { + if (entry.Value.QueryPlanIndexItem != null) + { + EventTable index = EventTableUtil.BuildIndex(agentInstanceContext, 0, entry.Value.QueryPlanIndexItem, rootView.EventType, true, entry.Key.IsUnique, entry.Value.OptionalIndexName, null, false); + _indexRepository.AddIndex(entry.Key, new EventTableIndexRepositoryEntry(entry.Value.OptionalIndexName, index)); + } + } + + _tablePerMultiLookup = new Dictionary(); + } + + public AgentInstanceContext AgentInstanceContext + { + get { return _agentInstanceContext; } + } + + public EventTableIndexRepository IndexRepository + { + get { return _indexRepository; } + } + + public IndexMultiKey[] Indexes + { + get { return _indexRepository.GetIndexDescriptors(); } + } + + /// + /// Gets or sets the enumeratable to use to obtain current named window data window contents. + /// + /// iterator over events help by named window + public IEnumerable DataWindowContents + { + get { return _dataWindowContents; } + set { _dataWindowContents = value; } + } + + /// + /// Called by tail view to indicate that the data window view exired events that must be removed from index tables. + /// + /// removed stream of the data window + public void RemoveOldData(EventBean[] oldData) + { + if (_rootView.RevisionProcessor != null) + { + _rootView.RevisionProcessor.RemoveOldData(oldData, _indexRepository); + } + else + { + foreach (EventTable table in _indexRepository.GetTables()) + { + table.Remove(oldData); + } + } + } + + /// + /// Called by tail view to indicate that the data window view has new events that must be added to index tables. + /// + /// new event + public void AddNewData(EventBean[] newData) + { + if (_rootView.RevisionProcessor == null) { + // Update indexes for fast deletion, if there are any + foreach (EventTable table in _indexRepository.GetTables()) + { + table.Add(newData); + } + } + } + + // Called by deletion strategy and also the insert-into for new events only + public override void Update(EventBean[] newData, EventBean[] oldData) + { + if (_rootView.RevisionProcessor != null) + { + _rootView.RevisionProcessor.OnUpdate(newData, oldData, this, _indexRepository); + } + else + { + // Update indexes for fast deletion, if there are any + foreach (EventTable table in _indexRepository.GetTables()) + { + if (_rootView.IsChildBatching) { + table.Add(newData); + } + } + + // Update child views + UpdateChildren(newData, oldData); + } + } + + public override EventType EventType + { + get { return _rootView.EventType; } + } + + public override IEnumerator GetEnumerator() + { + return null; + } + + /// + /// Dispose and clear resources. + /// + public void Dispose() + { + _indexRepository.Destroy(); + _tablePerMultiLookup.Clear(); + if (IsVirtualDataWindow) { + VirtualDataWindow.HandleStopWindow(); + } + } + + /// + /// Return a snapshot using index lookup filters. + /// + /// to index lookup + /// events + public ICollection Snapshot(FilterSpecCompiled optionalFilter, Attribute[] annotations) + { + VirtualDWView virtualDataWindow = null; + if (IsVirtualDataWindow) { + virtualDataWindow = VirtualDataWindow; + } + return FireAndForgetQueryExec.Snapshot(optionalFilter, annotations, virtualDataWindow, + _indexRepository, _rootView.IsQueryPlanLogging, NamedWindowRootView.QueryPlanLog, + _rootView.EventType.Name, _agentInstanceContext); + } + + /// + /// Add an explicit index. + /// + /// indicator whether unique + /// indexname + /// properties indexed + /// if set to true [is recovering resilient]. + /// com.espertech.esper.epl.expression.core.ExprValidationException if the index fails to be valid + public void AddExplicitIndex(bool unique, string indexName, IList columns, bool isRecoveringResilient) + { + lock (this) + { + var initIndex = _agentInstanceContext.StatementContext.EventTableIndexService.AllowInitIndex(isRecoveringResilient); + var initializeFrom = initIndex ? _dataWindowContents : CollectionUtil.NULL_EVENT_ITERABLE; + _indexRepository.ValidateAddExplicitIndex( + unique, indexName, columns, _rootView.EventType, initializeFrom, _agentInstanceContext, + isRecoveringResilient, null); + } + } + + public bool IsVirtualDataWindow + { + get { return Views[0] is VirtualDWView; } + } + + public VirtualDWView VirtualDataWindow + { + get + { + if (!IsVirtualDataWindow) + { + return null; + } + return (VirtualDWView) Views[0]; + } + } + + public void PostLoad() + { + EventBean[] events = new EventBean[1]; + foreach (EventBean @event in _dataWindowContents) + { + events[0] = @event; + foreach (EventTable table in _indexRepository.GetTables()) { + table.Add(events); + } + } + } + + public void VisitIndexes(StatementAgentInstancePostLoadIndexVisitor visitor) + { + visitor.Visit(_indexRepository.GetTables()); + } + + public bool IsQueryPlanLogging + { + get { return _rootView.IsQueryPlanLogging; } + } + + public void Stop() { + if (IsVirtualDataWindow) { + VirtualDataWindow.HandleStopWindow(); + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/named/NamedWindowTailView.cs b/NEsper.Core/NEsper.Core/epl/named/NamedWindowTailView.cs new file mode 100755 index 000000000..5b9b96af9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/named/NamedWindowTailView.cs @@ -0,0 +1,172 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.client.annotation; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events.vaevent; +using com.espertech.esper.timer; + +namespace com.espertech.esper.epl.named +{ + /// + /// This view is hooked into a named window's view chain as the last view and handles + /// dispatching of named window insert and remove stream results via + /// to consuming statements. + /// + public class NamedWindowTailView + { + private volatile IDictionary> _consumersNonContext; // handles as copy-on-write + + public NamedWindowTailView( + EventType eventType, + NamedWindowMgmtService namedWindowMgmtService, + NamedWindowDispatchService namedWindowDispatchService, + StatementResultService statementResultService, + ValueAddEventProcessor revisionProcessor, + bool prioritized, + bool parentBatchWindow, + TimeSourceService timeSourceService, + ConfigurationEngineDefaults.ThreadingConfig threadingConfig) + { + EventType = eventType; + NamedWindowMgmtService = namedWindowMgmtService; + NamedWindowDispatchService = namedWindowDispatchService; + StatementResultService = statementResultService; + RevisionProcessor = revisionProcessor; + IsPrioritized = prioritized; + IsParentBatchWindow = parentBatchWindow; + _consumersNonContext = NamedWindowUtil.CreateConsumerMap(IsPrioritized); + ThreadingConfig = threadingConfig; + TimeSourceService = timeSourceService; + } + + /// Returns true to indicate that the data window view is a batch view. + /// true if batch view + public bool IsParentBatchWindow { get; protected internal set; } + + public EventType EventType { get; protected internal set; } + + public StatementResultService StatementResultService { get; protected internal set; } + + public NamedWindowMgmtService NamedWindowMgmtService { get; protected internal set; } + + public NamedWindowDispatchService NamedWindowDispatchService { get; protected internal set; } + + public bool IsPrioritized { get; protected internal set; } + + public ValueAddEventProcessor RevisionProcessor { get; protected internal set; } + + public IDictionary> ConsumersNonContext + { + get { return _consumersNonContext; } + protected internal set { _consumersNonContext = value; } + } + + public TimeSourceService TimeSourceService { get; protected internal set; } + + public ConfigurationEngineDefaults.ThreadingConfig ThreadingConfig { get; protected internal set; } + + public NamedWindowConsumerView AddConsumer(NamedWindowConsumerDesc consumerDesc) + { + NamedWindowConsumerCallback consumerCallback = new ProxyNamedWindowConsumerCallback( + () => + { + throw new UnsupportedOperationException( + "GetEnumerator not supported on named windows that have a context attached and when that context is not the same as the consuming statement's context"); + }, + RemoveConsumer); + + // Construct consumer view, allow a callback to this view to remove the consumer + var audit = AuditEnum.STREAM.GetAudit(consumerDesc.AgentInstanceContext.StatementContext.Annotations) != null; + var consumerView = new NamedWindowConsumerView( + ExprNodeUtility.GetEvaluators(consumerDesc.FilterList), + consumerDesc.OptPropertyEvaluator, + EventType, + consumerCallback, + consumerDesc.AgentInstanceContext, + audit); + + // Keep a list of consumer views per statement to accomodate joins and subqueries + IList viewsPerStatements = _consumersNonContext.Get(consumerDesc.AgentInstanceContext.EpStatementAgentInstanceHandle); + if (viewsPerStatements == null) + { + viewsPerStatements = new CopyOnWriteList(); + + // avoid concurrent modification as a thread may currently iterate over consumers as its dispatching + // without the engine lock + var newConsumers = NamedWindowUtil.CreateConsumerMap(IsPrioritized); + newConsumers.PutAll(_consumersNonContext); + newConsumers.Put(consumerDesc.AgentInstanceContext.EpStatementAgentInstanceHandle, viewsPerStatements); + _consumersNonContext = newConsumers; + } + viewsPerStatements.Add(consumerView); + + return consumerView; + } + + /// Called by the consumer view to indicate it was stopped or destroyed, such that the consumer can be deregistered and further dispatches disregard this consumer. + /// is the consumer representative view + public void RemoveConsumer(NamedWindowConsumerView namedWindowConsumerView) + { + EPStatementAgentInstanceHandle handleRemoved = null; + // Find the consumer view + foreach (var entry in _consumersNonContext) + { + var foundAndRemoved = entry.Value.Remove(namedWindowConsumerView); + // Remove the consumer view + if ((foundAndRemoved) && (entry.Value.Count == 0)) + { + // Remove the handle if this list is now empty + handleRemoved = entry.Key; + break; + } + } + if (handleRemoved != null) + { + var newConsumers = NamedWindowUtil.CreateConsumerMap(IsPrioritized); + newConsumers.PutAll(_consumersNonContext); + newConsumers.Remove(handleRemoved); + _consumersNonContext = newConsumers; + } + } + + public void AddDispatches( + NamedWindowConsumerLatchFactory latchFactory, + IDictionary> consumersInContext, + NamedWindowDeltaData delta, + AgentInstanceContext agentInstanceContext) + { + if (!consumersInContext.IsEmpty()) + { + NamedWindowDispatchService.AddDispatch(latchFactory, delta, consumersInContext); + } + if (!_consumersNonContext.IsEmpty()) + { + NamedWindowDispatchService.AddDispatch(latchFactory, delta, _consumersNonContext); + } + } + + public NamedWindowConsumerLatchFactory MakeLatchFactory() + { + return new NamedWindowConsumerLatchFactory( + EventType.Name, + ThreadingConfig.IsNamedWindowConsumerDispatchPreserveOrder, + ThreadingConfig.NamedWindowConsumerDispatchTimeout, + ThreadingConfig.NamedWindowConsumerDispatchLocking, + TimeSourceService, true + ); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/named/NamedWindowTailViewInstance.cs b/NEsper.Core/NEsper.Core/epl/named/NamedWindowTailViewInstance.cs new file mode 100755 index 000000000..e25b9ac09 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/named/NamedWindowTailViewInstance.cs @@ -0,0 +1,413 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.client.annotation; +using com.espertech.esper.client.hook; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.updatehelper; +using com.espertech.esper.filter; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.epl.named +{ + /// + /// This view is hooked into a named window's view chain as the last view and handles dispatching of named window + /// insert and remove stream results via to consuming statements. + /// + public class NamedWindowTailViewInstance + : ViewSupport + , IEnumerable + { + private readonly NamedWindowRootViewInstance _rootViewInstance; + private readonly NamedWindowTailView _tailView; + private readonly NamedWindowProcessor _namedWindowProcessor; + private readonly AgentInstanceContext _agentInstanceContext; + private readonly NamedWindowConsumerLatchFactory _latchFactory; + + private volatile IDictionary> _consumersInContext; // handles as copy-on-write + private long _numberOfEvents; + + public NamedWindowTailViewInstance(NamedWindowRootViewInstance rootViewInstance, NamedWindowTailView tailView, NamedWindowProcessor namedWindowProcessor, AgentInstanceContext agentInstanceContext) + { + _rootViewInstance = rootViewInstance; + _tailView = tailView; + _namedWindowProcessor = namedWindowProcessor; + _agentInstanceContext = agentInstanceContext; + _consumersInContext = NamedWindowUtil.CreateConsumerMap(tailView.IsPrioritized); + _latchFactory = tailView.MakeLatchFactory(); + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + // Only old data (remove stream) needs to be removed from indexes (kept by root view), if any + if (oldData != null) + { + _rootViewInstance.RemoveOldData(oldData); + _numberOfEvents -= oldData.Length; + } + + if ((newData != null) && (!_tailView.IsParentBatchWindow)) + { + _rootViewInstance.AddNewData(newData); + } + + if (newData != null) + { + _numberOfEvents += newData.Length; + } + + // Post to child views, only if there are listeners or subscribers + if (_tailView.StatementResultService.IsMakeNatural || _tailView.StatementResultService.IsMakeSynthetic) + { + UpdateChildren(newData, oldData); + } + + var delta = new NamedWindowDeltaData(newData, oldData); + _tailView.AddDispatches(_latchFactory, _consumersInContext, delta, _agentInstanceContext); + } + + /// + /// Adds a consuming (selecting) statement to the named window. + /// + /// The consumer desc. + /// if set to true [is subselect]. + /// + /// consumer view + /// + public NamedWindowConsumerView AddConsumer(NamedWindowConsumerDesc consumerDesc, bool isSubselect) + { + NamedWindowConsumerCallback consumerCallback = new ProxyNamedWindowConsumerCallback() + { + ProcGetEnumerator = () => + { + var instance = _namedWindowProcessor.GetProcessorInstance(_agentInstanceContext); + if (instance == null) + { + // this can happen on context-partition "output when terminated" + return GetEnumerator(); + } + return instance.TailViewInstance.GetEnumerator(); + }, + ProcStopped = RemoveConsumer, + }; + + // Construct consumer view, allow a callback to this view to remove the consumer + var audit = AuditEnum.STREAM.GetAudit(consumerDesc.AgentInstanceContext.StatementContext.Annotations) != null; + var consumerView = new NamedWindowConsumerView( + ExprNodeUtility.GetEvaluators(consumerDesc.FilterList), + consumerDesc.OptPropertyEvaluator, + _tailView.EventType, + consumerCallback, + consumerDesc.AgentInstanceContext, + audit); + + // indicate to virtual data window that a consumer was added + var virtualDWView = _rootViewInstance.VirtualDataWindow; + if (virtualDWView != null) + { + virtualDWView.VirtualDataWindow.HandleEvent( + new VirtualDataWindowEventConsumerAdd( + _tailView.EventType.Name, + consumerView, + consumerDesc.AgentInstanceContext.StatementName, + consumerDesc.AgentInstanceContext.AgentInstanceId, + ExprNodeUtility.ToArray(consumerDesc.FilterList), + _agentInstanceContext)); + } + + // Keep a list of consumer views per statement to accommodate joins and subqueries + var viewsPerStatements = _consumersInContext.Get(consumerDesc.AgentInstanceContext.EpStatementAgentInstanceHandle); + if (viewsPerStatements == null) + { + viewsPerStatements = new CopyOnWriteList(); + + // avoid concurrent modification as a thread may currently iterate over consumers as its dispatching + // without the engine lock + var newConsumers = NamedWindowUtil.CreateConsumerMap(_tailView.IsPrioritized); + newConsumers.PutAll(_consumersInContext); + newConsumers.Put(consumerDesc.AgentInstanceContext.EpStatementAgentInstanceHandle, viewsPerStatements); + _consumersInContext = newConsumers; + } + if (isSubselect) + { + viewsPerStatements.Insert(0, consumerView); + } + else + { + viewsPerStatements.Add(consumerView); + } + + return consumerView; + } + + /// + /// Called by the consumer view to indicate it was stopped or destroyed, such that the + /// consumer can be deregistered and further dispatches disregard this consumer. + /// + /// is the consumer representative view + public void RemoveConsumer(NamedWindowConsumerView namedWindowConsumerView) + { + EPStatementAgentInstanceHandle handleRemoved = null; + // Find the consumer view + foreach (var entry in _consumersInContext) + { + var foundAndRemoved = entry.Value.Remove(namedWindowConsumerView); + // Remove the consumer view + if ((foundAndRemoved) && (entry.Value.Count == 0)) + { + // Remove the handle if this list is now empty + handleRemoved = entry.Key; + break; + } + } + if (handleRemoved != null) + { + var newConsumers = NamedWindowUtil.CreateConsumerMap(_tailView.IsPrioritized); + newConsumers.PutAll(_consumersInContext); + newConsumers.Remove(handleRemoved); + _consumersInContext = newConsumers; + } + + // indicate to virtual data window that a consumer was added + var virtualDWView = _rootViewInstance.VirtualDataWindow; + if (virtualDWView != null && handleRemoved != null) + { + virtualDWView.VirtualDataWindow.HandleEvent(new VirtualDataWindowEventConsumerRemove(_tailView.EventType.Name, namedWindowConsumerView, handleRemoved.StatementHandle.StatementName, handleRemoved.AgentInstanceId)); + } + } + + public override EventType EventType + { + get { return _tailView.EventType; } + } + + public override IEnumerator GetEnumerator() + { + if (_tailView.RevisionProcessor != null) + { + var coll = _tailView.RevisionProcessor.GetSnapshot(_agentInstanceContext.EpStatementAgentInstanceHandle, Parent); + return coll.GetEnumerator(); + } + + using (_agentInstanceContext.EpStatementAgentInstanceHandle.StatementAgentInstanceLock.AcquireReadLock()) + { + IEnumerator en = Parent.GetEnumerator(); + if (!en.MoveNext()) + { + return CollectionUtil.NULL_EVENT_ITERATOR; + } + var list = new List(); + do + { + list.Add(en.Current); + } while (en.MoveNext()); + + return list.GetEnumerator(); + } + } + + /// + /// Returns a snapshot of window contents, thread-safely + /// + /// filters if any + /// The annotations. + /// + /// window contents + /// + public ICollection Snapshot(FilterSpecCompiled filter, Attribute[] annotations) + { + if (_tailView.RevisionProcessor != null) + { + return _tailView.RevisionProcessor.GetSnapshot(_agentInstanceContext.EpStatementAgentInstanceHandle, Parent); + } + + using (_agentInstanceContext.EpStatementAgentInstanceHandle.StatementAgentInstanceLock.AcquireReadLock()) + { + try + { + return SnapshotNoLock(filter, annotations); + } + finally + { + ReleaseTableLocks(_agentInstanceContext); + } + } + } + + public EventBean[] SnapshotUpdate(FilterSpecCompiled filter, ExprNode optionalWhereClause, EventBeanUpdateHelper updateHelper, Attribute[] annotations) + { + using (_agentInstanceContext.EpStatementAgentInstanceHandle.StatementAgentInstanceLock.AcquireReadLock()) + { + try + { + var events = SnapshotNoLockWithFilter(filter, annotations, optionalWhereClause, _agentInstanceContext); + if (events.IsEmpty()) + { + return CollectionUtil.EVENTBEANARRAY_EMPTY; + } + + var eventsPerStream = new EventBean[3]; + var updated = new EventBean[events.Count]; + var count = 0; + foreach (var @event in events) + { + updated[count++] = updateHelper.UpdateWCopy(@event, eventsPerStream, _agentInstanceContext); + } + + var deleted = events.ToArray(); + _rootViewInstance.Update(updated, deleted); + return updated; + } + finally + { + ReleaseTableLocks(_agentInstanceContext); + } + } + } + + public EventBean[] SnapshotDelete(FilterSpecCompiled filter, ExprNode filterExpr, Attribute[] annotations) + { + using (_agentInstanceContext.EpStatementAgentInstanceHandle.StatementAgentInstanceLock.AcquireReadLock()) + { + try + { + var events = SnapshotNoLockWithFilter(filter, annotations, filterExpr, _agentInstanceContext); + if (events.IsEmpty()) + { + return CollectionUtil.EVENTBEANARRAY_EMPTY; + } + var eventsDeleted = events.ToArray(); + _rootViewInstance.Update(null, eventsDeleted); + return eventsDeleted; + } + finally + { + ReleaseTableLocks(_agentInstanceContext); + } + } + } + + public ICollection SnapshotNoLock(FilterSpecCompiled filter, Attribute[] annotations) + { + if (_tailView.RevisionProcessor != null) + { + return _tailView.RevisionProcessor.GetSnapshot(_agentInstanceContext.EpStatementAgentInstanceHandle, Parent); + } + + var indexedResult = _rootViewInstance.Snapshot(filter, annotations); + if (indexedResult != null) + { + return indexedResult; + } + + var en = Parent.GetEnumerator(); + if (!en.MoveNext()) + { + return Collections.GetEmptyList(); + } + + var list = new ArrayDeque(1024); + do + { + list.Add(en.Current); + } while (en.MoveNext()); + + return list; + } + + public ICollection SnapshotNoLockWithFilter(FilterSpecCompiled filter, Attribute[] annotations, ExprNode filterExpr, ExprEvaluatorContext exprEvaluatorContext) + { + if (_tailView.RevisionProcessor != null) + { + return _tailView.RevisionProcessor.GetSnapshot(_agentInstanceContext.EpStatementAgentInstanceHandle, Parent); + } + + var indexedResult = _rootViewInstance.Snapshot(filter, annotations); + if (indexedResult != null) + { + if (indexedResult.IsEmpty()) + { + return indexedResult; + } + if (filterExpr == null) + { + return indexedResult; + } + var deque = new ArrayDeque(Math.Min(indexedResult.Count, 16)); + ExprNodeUtility.ApplyFilterExpressionIterable(indexedResult.GetEnumerator(), filterExpr.ExprEvaluator, exprEvaluatorContext, deque); + return deque; + } + + // fall back to window operator if snapshot doesn't resolve successfully + var en = Parent.GetEnumerator(); + if (!en.MoveNext()) + { + return Collections.GetEmptyList(); + } + var list = new ArrayDeque(); + if (filterExpr != null) + { + en = Parent.GetEnumerator(); + ExprNodeUtility.ApplyFilterExpressionIterable(en, filterExpr.ExprEvaluator, _agentInstanceContext, list); + } + else + { + do + { + list.Add(en.Current); + } while (en.MoveNext()); + } + return list; + } + + public AgentInstanceContext AgentInstanceContext + { + get { return _agentInstanceContext; } + } + + /// + /// Dispose the view. + /// + public void Dispose() + { + _consumersInContext = NamedWindowUtil.CreateConsumerMap(_tailView.IsPrioritized); + } + + /// + /// Returns the number of events held. + /// + /// number of events + public long NumberOfEvents + { + get { return _numberOfEvents; } + } + + public NamedWindowTailView TailView + { + get { return _tailView; } + } + + private void ReleaseTableLocks(AgentInstanceContext agentInstanceContext) + { + agentInstanceContext.StatementContext.TableExprEvaluatorContext.ReleaseAcquiredLocks(); + } + + public void Stop() + { + // no action + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/named/NamedWindowUtil.cs b/NEsper.Core/NEsper.Core/epl/named/NamedWindowUtil.cs new file mode 100755 index 000000000..23562de24 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/named/NamedWindowUtil.cs @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; + +namespace com.espertech.esper.epl.named +{ + public class NamedWindowUtil + { + public static IDictionary> CreateConsumerMap(bool isPrioritized) + { + if (!isPrioritized) + { + return new LinkedHashMap>(); + } + + return new OrderedDictionary>( + EPStatementAgentInstanceHandleComparator.Instance); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/parse/ASTAggregationHelper.cs b/NEsper.Core/NEsper.Core/epl/parse/ASTAggregationHelper.cs new file mode 100755 index 000000000..2055b061d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/parse/ASTAggregationHelper.cs @@ -0,0 +1,67 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.client.hook; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.accessagg; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.methodagg; +using com.espertech.esper.plugin; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.parse +{ + public class ASTAggregationHelper + { + public static ExprNode TryResolveAsAggregation( + EngineImportService engineImportService, + bool distinct, + string functionName, + LazyAllocatedMap plugInAggregations, + string engineURI) + { + try + { + AggregationFunctionFactory aggregationFactory = + engineImportService.ResolveAggregationFactory(functionName); + return new ExprPlugInAggNode(distinct, aggregationFactory, functionName); + } + catch (EngineImportUndefinedException) + { + // Not an aggregation function + } + catch (EngineImportException e) + { + throw new IllegalStateException("Error resolving aggregation: " + e.Message, e); + } + + // try plug-in aggregation multi-function + ConfigurationPlugInAggregationMultiFunction config = + engineImportService.ResolveAggregationMultiFunction(functionName); + if (config != null) + { + PlugInAggregationMultiFunctionFactory factory = plugInAggregations.Map.Get(config); + if (factory == null) + { + factory = TypeHelper.Instantiate(config.MultiFunctionFactoryClassName, engineImportService.GetClassForNameProvider()); + plugInAggregations.Map.Put(config, factory); + } + factory.AddAggregationFunction( + new PlugInAggregationMultiFunctionDeclarationContext( + functionName.ToLowerInvariant(), distinct, engineURI, config)); + return new ExprPlugInAggMultiFunctionNode(distinct, config, factory, functionName); + } + + // try built-in expanded set of aggregation functions + return engineImportService.ResolveAggExtendedBuiltin(functionName, distinct); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/parse/ASTAnnotationHelper.cs b/NEsper.Core/NEsper.Core/epl/parse/ASTAnnotationHelper.cs new file mode 100755 index 000000000..1dd993a88 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/parse/ASTAnnotationHelper.cs @@ -0,0 +1,138 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.collection; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.generated; +using com.espertech.esper.epl.spec; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.parse +{ + /// Walker to annotation stuctures. + public class ASTAnnotationHelper + { + /// + /// Walk an annotation root name or child node (nested annotations). + /// + /// annotation walk node + /// for engine imports + /// if the walk failed + /// annotation descriptor + public static AnnotationDesc Walk( + EsperEPL2GrammarParser.AnnotationEnumContext ctx, + EngineImportService engineImportService) + { + string name = ASTUtil.UnescapeClassIdent(ctx.classIdentifier()); + var values = new List>(); + if (ctx.elementValueEnum() != null) + { + Object value = WalkValue(ctx.elementValueEnum(), engineImportService); + values.Add(new Pair("Value", value)); + } + else if (ctx.elementValuePairsEnum() != null) + { + WalkValuePairs(ctx.elementValuePairsEnum(), values, engineImportService); + } + + return new AnnotationDesc(name, values); + } + + private static void WalkValuePairs( + EsperEPL2GrammarParser.ElementValuePairsEnumContext elementValuePairsEnumContext, + IList> values, + EngineImportService engineImportService) + { + + foreach ( + EsperEPL2GrammarParser.ElementValuePairEnumContext ctx in + elementValuePairsEnumContext.elementValuePairEnum()) + { + Pair pair = WalkValuePair(ctx, engineImportService); + values.Add(pair); + } + } + + private static Object WalkValue( + EsperEPL2GrammarParser.ElementValueEnumContext ctx, + EngineImportService engineImportService) + { + if (ctx.elementValueArrayEnum() != null) + { + return WalkArray(ctx.elementValueArrayEnum(), engineImportService); + } + if (ctx.annotationEnum() != null) + { + return Walk(ctx.annotationEnum(), engineImportService); + } + else if (ctx.v != null) + { + return ctx.v.Text; + } + else if (ctx.classIdentifier() != null) + { + return WalkClassIdent(ctx.classIdentifier(), engineImportService); + } + else + { + return ASTConstantHelper.Parse(ctx.constant()); + } + } + + private static Pair WalkValuePair( + EsperEPL2GrammarParser.ElementValuePairEnumContext ctx, + EngineImportService engineImportService) + { + string name = ctx.keywordAllowedIdent().GetText(); + Object value = WalkValue(ctx.elementValueEnum(), engineImportService); + return new Pair(name, value); + } + + private static Object WalkClassIdent( + EsperEPL2GrammarParser.ClassIdentifierContext ctx, + EngineImportService engineImportService) + { + string enumValueText = ctx.GetText(); + Object enumValue; + try + { + enumValue = TypeHelper.ResolveIdentAsEnumConst(enumValueText, engineImportService, true); + } + catch (ExprValidationException) + { + throw ASTWalkException.From( + "Annotation value '" + enumValueText + + "' is not recognized as an enumeration value, please check imports or use a primitive or string type"); + } + if (enumValue != null) + { + return enumValue; + } + throw ASTWalkException.From( + "Annotation enumeration value '" + enumValueText + + "' not recognized as an enumeration class, please check imports or type used"); + } + + private static Object[] WalkArray( + EsperEPL2GrammarParser.ElementValueArrayEnumContext ctx, + EngineImportService engineImportService) + { + var elements = ctx.elementValueEnum(); + var values = new Object[elements.Length]; + for (int i = 0; i < elements.Length; i++) + { + values[i] = WalkValue(elements[i], engineImportService); + } + return values; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/parse/ASTConstantHelper.cs b/NEsper.Core/NEsper.Core/epl/parse/ASTConstantHelper.cs new file mode 100755 index 000000000..8a34dc8a3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/parse/ASTConstantHelper.cs @@ -0,0 +1,167 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using Antlr4.Runtime.Tree; + +using com.espertech.esper.epl.generated; +using com.espertech.esper.type; + +namespace com.espertech.esper.epl.parse +{ + /// + /// Parses constant strings and returns the constant Object. + /// + public class ASTConstantHelper + { + /// + /// Remove tick '`' character from a string start and end. + /// + /// delimited string + /// delimited string with ticks removed, if starting and ending with tick + public static String RemoveTicks(String tickedString) + { + var indexFirst = tickedString.IndexOf('`'); + var indexLast = tickedString.LastIndexOf('`'); + if ((indexFirst != indexLast) && (indexFirst != -1) && (indexLast != -1)) + { + return tickedString.Substring(indexFirst+1, indexLast - indexFirst - 1); + } + return tickedString; + } + + /// + /// Parse the AST constant node and return Object value. + /// + /// parse node for which to parse the string value + /// value matching AST node type + public static Object Parse(IParseTree node) + { + if (node is ITerminalNode) { + var terminal = (ITerminalNode) node; + switch(terminal.Symbol.Type) + { + case EsperEPL2GrammarParser.BOOLEAN_TRUE: return BoolValue.ParseString(terminal.GetText()); + case EsperEPL2GrammarParser.BOOLEAN_FALSE: return BoolValue.ParseString(terminal.GetText()); + case EsperEPL2GrammarParser.VALUE_NULL: return null; + default: + throw ASTWalkException.From("Encountered unexpected constant type " + terminal.Symbol.Type, terminal.Symbol); + } + } + else { + var ruleNode = (IRuleNode)node; + var ruleIndex = ruleNode.RuleContext.RuleIndex; + if (ruleIndex == EsperEPL2GrammarParser.RULE_number) { + return ParseNumber(ruleNode, 1); + } + else if (ruleIndex == EsperEPL2GrammarParser.RULE_numberconstant) { + var number = FindChildRuleByType(ruleNode, EsperEPL2GrammarParser.RULE_number); + if (ruleNode.ChildCount > 1) { + if (ASTUtil.IsTerminatedOfType(ruleNode.GetChild(0), EsperEPL2GrammarLexer.MINUS)) { + return ParseNumber(number, -1); + } + return ParseNumber(number, 1); + } + else { + return ParseNumber(number, 1); + } + } + else if (ruleIndex == EsperEPL2GrammarParser.RULE_stringconstant) { + return StringValue.ParseString(node.GetText()); + } + else if (ruleIndex == EsperEPL2GrammarParser.RULE_constant) { + return Parse(ruleNode.GetChild(0)); + } + throw ASTWalkException.From("Encountered unrecognized constant", node.GetText()); + } + } + + private static Object ParseNumber(IRuleNode number, int factor) { + var tokenType = GetSingleChildTokenType(number); + if (tokenType == EsperEPL2GrammarLexer.IntegerLiteral) { + return ParseIntLongByte(number.GetText(), factor); + } + + else if (tokenType == EsperEPL2GrammarLexer.FloatingPointLiteral) { + var numberText = number.GetText(); + if (numberText.EndsWith("f") || numberText.EndsWith("F")) { + return FloatValue.ParseString(number.GetText()) * factor; + } + else { + return DoubleValue.ParseString(number.GetText()) * factor; + } + } + throw ASTWalkException.From("Encountered unrecognized constant", number.GetText()); + } + + private static Object ParseIntLongByte(String arg, int factor) + { + // try to parse as an int first, else try to parse as a long + try + { + return IntValue.ParseString(arg)*factor; + } + catch (OverflowException e1) + { + try + { + return (Int64) (LongValue.ParseString(arg)*factor); + } + catch + { + } + + throw; + } + catch (FormatException e1) + { + try + { + return (Int64) (LongValue.ParseString(arg)*factor); + } + catch (Exception) + { + try + { + return (Byte) (ByteValue.ParseString(arg)*factor); + } + catch (Exception) + { + throw e1; + } + } + } + } + + private static IRuleNode FindChildRuleByType(ITree node, int ruleNum) + { + for (var i = 0; i < node.ChildCount; i++) { + var child = node.GetChild(i); + if (IsRuleOfType(child, ruleNum)) { + return (IRuleNode) child; + } + } + return null; + } + + private static bool IsRuleOfType(ITree child, int ruleNum) { + if (!(child is IRuleNode)) + { + return false; + } + var ruleNode = (IRuleNode)child; + return ruleNode.RuleContext.RuleIndex == ruleNum; + } + + private static int GetSingleChildTokenType(IRuleNode node) + { + return ((ITerminalNode) node.GetChild(0)).Symbol.Type; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/parse/ASTContextHelper.cs b/NEsper.Core/NEsper.Core/epl/parse/ASTContextHelper.cs new file mode 100755 index 000000000..e1e8587d3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/parse/ASTContextHelper.cs @@ -0,0 +1,285 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using Antlr4.Runtime; +using Antlr4.Runtime.Tree; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.time; +using com.espertech.esper.epl.generated; +using com.espertech.esper.epl.spec; +using com.espertech.esper.pattern; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.parse +{ + public class ASTContextHelper + { + private static readonly ILog Log = + LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + public static CreateContextDesc WalkCreateContext( + EsperEPL2GrammarParser.CreateContextExprContext ctx, + IDictionary astExprNodeMap, + IDictionary astPatternNodeMap, + PropertyEvalSpec propertyEvalSpec, + FilterSpecRaw filterSpec) + { + var contextName = ctx.name.Text; + ContextDetail contextDetail; + + var choice = ctx.createContextDetail().createContextChoice(); + if (choice != null) + { + contextDetail = WalkChoice(choice, astExprNodeMap, astPatternNodeMap, propertyEvalSpec, filterSpec); + } + else + { + contextDetail = WalkNested( + ctx.createContextDetail().contextContextNested(), astExprNodeMap, astPatternNodeMap, + propertyEvalSpec, filterSpec); + } + return new CreateContextDesc(contextName, contextDetail); + } + + private static ContextDetail WalkNested( + IList nestedContexts, + IDictionary astExprNodeMap, + IDictionary astPatternNodeMap, + PropertyEvalSpec propertyEvalSpec, + FilterSpecRaw filterSpec) + { + var contexts = new List(nestedContexts.Count); + foreach (var nestedctx in nestedContexts) + { + var contextDetail = WalkChoice( + nestedctx.createContextChoice(), astExprNodeMap, astPatternNodeMap, propertyEvalSpec, filterSpec); + var desc = new CreateContextDesc(nestedctx.name.Text, contextDetail); + contexts.Add(desc); + } + return new ContextDetailNested(contexts); + } + + private static ContextDetail WalkChoice( + EsperEPL2GrammarParser.CreateContextChoiceContext ctx, + IDictionary astExprNodeMap, + IDictionary astPatternNodeMap, + PropertyEvalSpec propertyEvalSpec, + FilterSpecRaw filterSpec) + { + + // temporal fixed (start+end) and overlapping (initiated/terminated) + if (ctx.START() != null || ctx.INITIATED() != null) + { + + ExprNode[] distinctExpressions = null; + if (ctx.createContextDistinct() != null) + { + if (ctx.createContextDistinct().expressionList() == null) + { + distinctExpressions = new ExprNode[0]; + } + else + { + distinctExpressions = + ASTExprHelper.ExprCollectSubNodesPerNode( + ctx.createContextDistinct().expressionList().expression(), astExprNodeMap); + } + } + + ContextDetailCondition startEndpoint; + if (ctx.START() != null) + { + var immediate = CheckNow(ctx.i); + if (immediate) + { + startEndpoint = ContextDetailConditionImmediate.INSTANCE; + } + else + { + startEndpoint = GetContextCondition( + ctx.r1, astExprNodeMap, astPatternNodeMap, propertyEvalSpec, false); + } + } + else + { + var immediate = CheckNow(ctx.i); + startEndpoint = GetContextCondition( + ctx.r1, astExprNodeMap, astPatternNodeMap, propertyEvalSpec, immediate); + } + + var overlapping = ctx.INITIATED() != null; + var endEndpoint = GetContextCondition( + ctx.r2, astExprNodeMap, astPatternNodeMap, propertyEvalSpec, false); + return new ContextDetailInitiatedTerminated( + startEndpoint, endEndpoint, overlapping, distinctExpressions); + } + + // partitioned + if (ctx.PARTITION() != null) + { + IList partitions = + ctx.createContextPartitionItem(); + var rawSpecs = new List(); + foreach (var partition in partitions) + { + + filterSpec = ASTFilterSpecHelper.WalkFilterSpec( + partition.eventFilterExpression(), propertyEvalSpec, astExprNodeMap); + propertyEvalSpec = null; + + var propertyNames = new List(); + IList properties = partition.eventProperty(); + foreach (var property in properties) + { + var propertyName = ASTUtil.GetPropertyName(property, 0); + propertyNames.Add(propertyName); + } + ASTExprHelper.ExprCollectSubNodes(partition, 0, astExprNodeMap); // remove expressions + + rawSpecs.Add(new ContextDetailPartitionItem(filterSpec, propertyNames)); + } + return new ContextDetailPartitioned(rawSpecs); + } + else if (ctx.COALESCE() != null) + { + // hash + IList coalesces = + ctx.createContextCoalesceItem(); + var rawSpecs = new List(coalesces.Count); + foreach (var coalesce in coalesces) + { + var func = ASTLibFunctionHelper.GetLibFunctionChainSpec( + coalesce.libFunctionNoClass(), astExprNodeMap); + filterSpec = ASTFilterSpecHelper.WalkFilterSpec( + coalesce.eventFilterExpression(), propertyEvalSpec, astExprNodeMap); + propertyEvalSpec = null; + rawSpecs.Add(new ContextDetailHashItem(func, filterSpec)); + } + + var granularity = ctx.g.Text; + if (!granularity.ToLowerInvariant().Equals("granularity")) + { + throw ASTWalkException.From( + "Expected 'granularity' keyword after list of coalesce items, found '" + granularity + + "' instead"); + } + var num = ASTConstantHelper.Parse(ctx.number()); + var preallocateStr = ctx.p != null ? ctx.p.Text : null; + if (preallocateStr != null && !preallocateStr.ToLowerInvariant().Equals("preallocate")) + { + throw ASTWalkException.From( + "Expected 'preallocate' keyword after list of coalesce items, found '" + preallocateStr + + "' instead"); + } + if (!TypeHelper.IsNumericNonFP(num.GetType()) || TypeHelper.GetBoxedType(num.GetType()) == typeof (long?)) + { + throw ASTWalkException.From( + "Granularity provided must be an int-type number, received " + num.GetType() + " instead"); + } + + return new ContextDetailHash(rawSpecs, num.AsInt(), preallocateStr != null); + } + + // categorized + if (ctx.createContextGroupItem() != null) + { + IList grps = ctx.createContextGroupItem(); + var items = new List(); + foreach (var grp in grps) + { + var exprNode = ASTExprHelper.ExprCollectSubNodes(grp, 0, astExprNodeMap)[0]; + var name = grp.i.Text; + items.Add(new ContextDetailCategoryItem(exprNode, name)); + } + filterSpec = ASTFilterSpecHelper.WalkFilterSpec( + ctx.eventFilterExpression(), propertyEvalSpec, astExprNodeMap); + return new ContextDetailCategory(items, filterSpec); + } + + throw new IllegalStateException("Unrecognized context detail type"); + } + + private static ContextDetailCondition GetContextCondition( + EsperEPL2GrammarParser.CreateContextRangePointContext ctx, + IDictionary astExprNodeMap, + IDictionary astPatternNodeMap, + PropertyEvalSpec propertyEvalSpec, + bool immediate) + { + if (ctx == null) + { + return ContextDetailConditionNever.INSTANCE; + } + if (ctx.crontabLimitParameterSet() != null) + { + var crontab = ASTExprHelper.ExprCollectSubNodes( + ctx.crontabLimitParameterSet(), 0, astExprNodeMap); + return new ContextDetailConditionCrontab(crontab, immediate); + } + else if (ctx.patternInclusionExpression() != null) + { + var evalNode = ASTExprHelper.PatternGetRemoveTopNode( + ctx.patternInclusionExpression(), astPatternNodeMap); + var inclusive = false; + if (ctx.i != null) + { + var ident = ctx.i.Text; + if (ident != null && !ident.ToLowerInvariant().Equals("inclusive")) + { + throw ASTWalkException.From( + "Expected 'inclusive' keyword after '@', found '" + ident + "' instead"); + } + inclusive = true; + } + return new ContextDetailConditionPattern(evalNode, inclusive, immediate); + } + else if (ctx.createContextFilter() != null) + { + var filterSpecRaw = + ASTFilterSpecHelper.WalkFilterSpec( + ctx.createContextFilter().eventFilterExpression(), propertyEvalSpec, astExprNodeMap); + var asName = ctx.createContextFilter().i != null ? ctx.createContextFilter().i.Text : null; + if (immediate) + { + throw ASTWalkException.From( + "Invalid use of 'now' with initiated-by stream, this combination is not supported"); + } + return new ContextDetailConditionFilter(filterSpecRaw, asName); + } + else if (ctx.AFTER() != null) + { + var timePeriod = + (ExprTimePeriod) ASTExprHelper.ExprCollectSubNodes(ctx.timePeriod(), 0, astExprNodeMap)[0]; + return new ContextDetailConditionTimePeriod(timePeriod, immediate); + } + else + { + throw new IllegalStateException("Unrecognized child type"); + } + } + + private static bool CheckNow(IToken i) + { + if (i == null) + { + return false; + } + var ident = i.Text; + if (!ident.ToLowerInvariant().Equals("now")) + { + throw ASTWalkException.From("Expected 'now' keyword after '@', found '" + ident + "' instead"); + } + return true; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/parse/ASTCreateSchemaHelper.cs b/NEsper.Core/NEsper.Core/epl/parse/ASTCreateSchemaHelper.cs new file mode 100755 index 000000000..ce5a525a7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/parse/ASTCreateSchemaHelper.cs @@ -0,0 +1,117 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using Antlr4.Runtime; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.generated; +using com.espertech.esper.epl.spec; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.parse +{ + public class ASTCreateSchemaHelper + { + public static CreateSchemaDesc WalkCreateSchema(EsperEPL2GrammarParser.CreateSchemaExprContext ctx) + { + var assignedType = AssignedType.NONE; + if (ctx.keyword != null) { + assignedType = AssignedTypeExtensions.ParseKeyword(ctx.keyword.Text); + } + return GetSchemaDesc(ctx.createSchemaDef(), assignedType); + } + + private static CreateSchemaDesc GetSchemaDesc(EsperEPL2GrammarParser.CreateSchemaDefContext ctx, AssignedType assignedType) + { + var schemaName = ctx.name.Text; + var columnTypes = GetColTypeList(ctx.createColumnList()); + + // get model-after types (could be multiple for variants) + ISet typeNames = new LinkedHashSet(); + if (ctx.variantList() != null) { + IList variantCtxs = ctx.variantList().variantListElement(); + foreach (var variantCtx in variantCtxs) { + typeNames.Add(variantCtx.GetText().UnmaskTypeName()); + } + } + + // get inherited and start timestamp and end timestamps + string startTimestamp = null; + string endTimestamp = null; + ISet inherited = new LinkedHashSet(); + ISet copyFrom = new LinkedHashSet(); + if (ctx.createSchemaQual() != null) { + IList qualCtxs = ctx.createSchemaQual(); + foreach (var qualCtx in qualCtxs) { + var qualName = qualCtx.i.Text.ToLower(); + var cols = ASTUtil.GetIdentList(qualCtx.columnList()); + var qualNameLower = qualName.ToLower(); + switch (qualNameLower) + { + case "inherits": + inherited.AddAll(cols); + continue; + case "starttimestamp": + startTimestamp = cols[0]; + continue; + case "endtimestamp": + endTimestamp = cols[0]; + continue; + case "copyfrom": + copyFrom.AddAll(cols); + continue; + } + throw new EPException("Expected 'inherits', 'starttimestamp', 'endtimestamp' or 'copyfrom' keyword after create-schema clause but encountered '" + qualName + "'"); + } + } + + return new CreateSchemaDesc(schemaName, typeNames, columnTypes, inherited, assignedType, startTimestamp, endTimestamp, copyFrom); + } + + public static IList GetColTypeList(EsperEPL2GrammarParser.CreateColumnListContext ctx) + { + if (ctx == null) { + return Collections.GetEmptyList(); + } + IList result = new List(ctx.createColumnListElement().Length); + foreach (var colctx in ctx.createColumnListElement()) { + IList idents = colctx.classIdentifier(); + var name = ASTUtil.UnescapeClassIdent(idents[0]); + + string type; + if (colctx.VALUE_NULL() != null) + { + type = null; + } + else + { + type = ASTUtil.UnescapeClassIdent(idents[1]); + } + + var array = colctx.b != null; + var primitiveArray = ValidateIsPrimitiveArray(colctx.p); + result.Add(new ColumnDesc(name, type, array, primitiveArray)); + } + return result; + } + + public static bool ValidateIsPrimitiveArray(IToken p) + { + if (p != null) { + if (!p.Text.ToLower().Equals("primitive")) { + throw ASTWalkException.From("Column type keyword '" + p.Text + "' not recognized, expecting '[primitive]'"); + } + return true; + } + return false; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/parse/ASTExprHelper.cs b/NEsper.Core/NEsper.Core/epl/parse/ASTExprHelper.cs new file mode 100755 index 000000000..aec3208a3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/parse/ASTExprHelper.cs @@ -0,0 +1,460 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using Antlr4.Runtime; +using Antlr4.Runtime.Tree; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.ops; +using com.espertech.esper.epl.expression.time; +using com.espertech.esper.epl.generated; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.variable; +using com.espertech.esper.pattern; +using com.espertech.esper.rowregex; +using com.espertech.esper.type; + +namespace com.espertech.esper.epl.parse +{ + public class ASTExprHelper + { + public static ExprNode ResolvePropertyOrVariableIdentifier(String identifier, VariableService variableService, StatementSpecRaw spec) + { + var metaData = variableService.GetVariableMetaData(identifier); + if (metaData != null) + { + var exprNode = new ExprVariableNodeImpl(metaData, null); + spec.HasVariables = true; + AddVariableReference(spec, metaData.VariableName); + var message = VariableServiceUtil.CheckVariableContextName(spec.OptionalContextName, metaData); + if (message != null) + { + throw ASTWalkException.From(message); + } + return exprNode; + } + else + { + return new ExprIdentNodeImpl(identifier); + } + } + + public static void AddVariableReference(StatementSpecRaw statementSpec, String variableName) + { + if (statementSpec.ReferencedVariables == null) + { + statementSpec.ReferencedVariables = new HashSet(); + } + statementSpec.ReferencedVariables.Add(variableName); + } + + public static ExprTimePeriod TimePeriodGetExprAllParams( + EsperEPL2GrammarParser.TimePeriodContext ctx, + IDictionary astExprNodeMap, + VariableService variableService, + StatementSpecRaw spec, + ConfigurationInformation config, + TimeAbacus timeAbacus) + { + var nodes = new ExprNode[9]; + for (var i = 0; i < ctx.ChildCount; i++) + { + var unitRoot = ctx.GetChild(i); + + ExprNode valueExpr; + if (ASTUtil.IsTerminatedOfType(unitRoot.GetChild(0), EsperEPL2GrammarLexer.IDENT)) + { + var ident = unitRoot.GetChild(0).GetText(); + valueExpr = ASTExprHelper.ResolvePropertyOrVariableIdentifier(ident, variableService, spec); + } + else + { + var @ref = new Atomic(); + ExprAction action = (exprNode, astExprNodeMapX, nodeX) => + { + astExprNodeMapX.Remove(nodeX); + @ref.Set(exprNode); + }; + ASTExprHelper.RecursiveFindRemoveChildExprNode(unitRoot.GetChild(0), astExprNodeMap, action); + valueExpr = @ref.Get(); + } + + if (ASTUtil.GetRuleIndexIfProvided(unitRoot) == EsperEPL2GrammarParser.RULE_microsecondPart) + { + nodes[8] = valueExpr; + } + if (ASTUtil.GetRuleIndexIfProvided(unitRoot) == EsperEPL2GrammarParser.RULE_millisecondPart) + { + nodes[7] = valueExpr; + } + if (ASTUtil.GetRuleIndexIfProvided(unitRoot) == EsperEPL2GrammarParser.RULE_secondPart) + { + nodes[6] = valueExpr; + } + if (ASTUtil.GetRuleIndexIfProvided(unitRoot) == EsperEPL2GrammarParser.RULE_minutePart) + { + nodes[5] = valueExpr; + } + if (ASTUtil.GetRuleIndexIfProvided(unitRoot) == EsperEPL2GrammarParser.RULE_hourPart) + { + nodes[4] = valueExpr; + } + if (ASTUtil.GetRuleIndexIfProvided(unitRoot) == EsperEPL2GrammarParser.RULE_dayPart) + { + nodes[3] = valueExpr; + } + if (ASTUtil.GetRuleIndexIfProvided(unitRoot) == EsperEPL2GrammarParser.RULE_weekPart) + { + nodes[2] = valueExpr; + } + if (ASTUtil.GetRuleIndexIfProvided(unitRoot) == EsperEPL2GrammarParser.RULE_monthPart) + { + nodes[1] = valueExpr; + } + if (ASTUtil.GetRuleIndexIfProvided(unitRoot) == EsperEPL2GrammarParser.RULE_yearPart) + { + nodes[0] = valueExpr; + } + } + + ExprTimePeriod timeNode = new ExprTimePeriodImpl(config.EngineDefaults.Expression.TimeZone, + nodes[0] != null, nodes[1] != null, nodes[2] != null, nodes[3] != null, nodes[4] != null, nodes[5] != null, nodes[6] != null, nodes[7] != null, nodes[8] != null, timeAbacus); + + foreach (ExprNode node in nodes) { + if (node != null) timeNode.AddChildNode(node); + } + + return timeNode; + } + + public static ExprTimePeriod TimePeriodGetExprJustSeconds( + EsperEPL2GrammarParser.ExpressionContext expression, + IDictionary astExprNodeMap, + ConfigurationInformation config, + TimeAbacus timeAbacus) + { + var node = ExprCollectSubNodes(expression, 0, astExprNodeMap)[0]; + var timeNode = new ExprTimePeriodImpl( + config.EngineDefaults.Expression.TimeZone, + false, false, false, false, false, false, true, false, false, timeAbacus); + timeNode.AddChildNode(node); + return timeNode; + } + + /// + /// Returns the list of set-variable assignments under the given node. + /// + /// map of AST to expression + /// list of assignments + internal static IList GetOnTriggerSetAssignments( + EsperEPL2GrammarParser.OnSetAssignmentListContext ctx, + IDictionary astExprNodeMap) + { + if (ctx == null || ctx.onSetAssignment().IsEmpty()) + { + return Collections.GetEmptyList(); + } + IList ctxs = ctx.onSetAssignment(); + IList assignments = new List(ctx.onSetAssignment().Length); + foreach (var assign in ctxs) + { + ExprNode childEvalNode; + if (assign.eventProperty() != null) + { + var prop = ASTExprHelper.ExprCollectSubNodes(assign.eventProperty(), 0, astExprNodeMap)[0]; + var value = ASTExprHelper.ExprCollectSubNodes(assign.expression(), 0, astExprNodeMap)[0]; + ExprEqualsNode equals = new ExprEqualsNodeImpl(false, false); + equals.AddChildNode(prop); + equals.AddChildNode(value); + childEvalNode = equals; + } + else + { + childEvalNode = ASTExprHelper.ExprCollectSubNodes(assign, 0, astExprNodeMap)[0]; + } + assignments.Add(new OnTriggerSetAssignment(childEvalNode)); + } + return assignments; + } + + public static void PatternCollectAddSubnodesAddParentNode(EvalFactoryNode evalNode, ITree node, IDictionary astPatternNodeMap) + { + if (evalNode == null) + { + throw ASTWalkException.From("Invalid null expression node for '" + ASTUtil.PrintNode(node) + "'"); + } + for (var i = 0; i < node.ChildCount; i++) + { + var childNode = node.GetChild(i); + var childEvalNode = PatternGetRemoveTopNode(childNode, astPatternNodeMap); + if (childEvalNode != null) + { + evalNode.AddChildNode(childEvalNode); + } + } + astPatternNodeMap.Put(node, evalNode); + } + + public static EvalFactoryNode PatternGetRemoveTopNode(ITree node, IDictionary astPatternNodeMap) + { + var pattern = astPatternNodeMap.Get(node); + if (pattern != null) + { + astPatternNodeMap.Remove(node); + return pattern; + } + for (var i = 0; i < node.ChildCount; i++) + { + pattern = PatternGetRemoveTopNode(node.GetChild(i), astPatternNodeMap); + if (pattern != null) + { + return pattern; + } + } + return null; + } + + public static void RegExCollectAddSubNodesAddParentNode(RowRegexExprNode exprNode, ITree node, IDictionary astRegExNodeMap) + { + RegExCollectAddSubNodes(exprNode, node, astRegExNodeMap); + astRegExNodeMap.Put(node, exprNode); + } + + public static void RegExCollectAddSubNodes(RowRegexExprNode regexNode, ITree node, IDictionary astRegExNodeMap) + { + if (regexNode == null) + { + throw ASTWalkException.From("Invalid null expression node for '" + ASTUtil.PrintNode(node) + "'"); + } + RegExAction action = (exprNode, astRegExNodeMapX, nodeX) => + { + astRegExNodeMapX.Remove(nodeX); + regexNode.AddChildNode(exprNode); + }; + for (var i = 0; i < node.ChildCount; i++) + { + var childNode = node.GetChild(i); + RegExApplyActionRecursive(childNode, astRegExNodeMap, action); + } + } + + public static void RegExApplyActionRecursive(ITree node, IDictionary astRegExNodeMap, RegExAction action) + { + var expr = astRegExNodeMap.Get(node); + if (expr != null) + { + action.Invoke(expr, astRegExNodeMap, node); + return; + } + for (var i = 0; i < node.ChildCount; i++) + { + RegExApplyActionRecursive(node.GetChild(i), astRegExNodeMap, action); + } + } + + public static void ExprCollectAddSubNodesAddParentNode(ExprNode exprNode, ITree node, IDictionary astExprNodeMap) + { + ExprCollectAddSubNodes(exprNode, node, astExprNodeMap); + astExprNodeMap.Put(node, exprNode); + } + + public static void ExprCollectAddSubNodes(ExprNode parentNode, ITree node, IDictionary astExprNodeMap) + { + if (parentNode == null) + { + throw ASTWalkException.From("Invalid null expression node for '" + ASTUtil.PrintNode(node) + "'"); + } + if (node == null) + { + return; + } + ExprAction action = (exprNode, astExprNodeMapX, nodeX) => + { + astExprNodeMapX.Remove(nodeX); + parentNode.AddChildNode(exprNode); + }; + for (var i = 0; i < node.ChildCount; i++) + { + var childNode = node.GetChild(i); + RecursiveFindRemoveChildExprNode(childNode, astExprNodeMap, action); + } + } + + public static void ExprCollectAddSingle(ExprNode parentNode, ITree node, IDictionary astExprNodeMap) + { + if (parentNode == null) + { + throw ASTWalkException.From("Invalid null expression node for '" + ASTUtil.PrintNode(node) + "'"); + } + if (node == null) + { + return; + } + ExprAction action = (exprNodeX, astExprNodeMapX, nodeX) => + { + astExprNodeMapX.Remove(nodeX); + parentNode.AddChildNode(exprNodeX); + }; + RecursiveFindRemoveChildExprNode(node, astExprNodeMap, action); + } + + public static void ExprCollectAddSubNodesExpressionCtx(ExprNode parentNode, IList expressionContexts, IDictionary astExprNodeMap) + { + ExprAction action = (exprNode, astExprNodeMapX, node) => + { + astExprNodeMapX.Remove(node); + parentNode.AddChildNode(exprNode); + }; + foreach (var ctx in expressionContexts) + { + RecursiveFindRemoveChildExprNode(ctx, astExprNodeMap, action); + } + } + + public static IList ExprCollectSubNodes(ITree parentNode, int startIndex, IDictionary astExprNodeMap) + { + var selfNode = astExprNodeMap.Delete(parentNode); + if (selfNode != null) + { + return Collections.SingletonList(selfNode); + } + IList exprNodes = new List(); + ExprAction action = (exprNode, astExprNodeMapX, node) => + { + astExprNodeMapX.Remove(node); + exprNodes.Add(exprNode); + }; + for (var i = startIndex; i < parentNode.ChildCount; i++) + { + var currentNode = parentNode.GetChild(i); + RecursiveFindRemoveChildExprNode(currentNode, astExprNodeMap, action); + } + return exprNodes; + } + + private static void RecursiveFindRemoveChildExprNode(ITree node, IDictionary astExprNodeMap, ExprAction action) + { + var expr = astExprNodeMap.Get(node); + if (expr != null) + { + action.Invoke(expr, astExprNodeMap, node); + return; + } + for (var i = 0; i < node.ChildCount; i++) + { + RecursiveFindRemoveChildExprNode(node.GetChild(i), astExprNodeMap, action); + } + } + + public static RowRegexExprNode RegExGetRemoveTopNode(ITree node, IDictionary astRowRegexNodeMap) + { + var regex = astRowRegexNodeMap.Get(node); + if (regex != null) + { + astRowRegexNodeMap.Remove(node); + return regex; + } + for (var i = 0; i < node.ChildCount; i++) + { + regex = RegExGetRemoveTopNode(node.GetChild(i), astRowRegexNodeMap); + if (regex != null) + { + return regex; + } + } + return null; + } + + public static ExprNode MathGetExpr(IParseTree ctx, IDictionary astExprNodeMap, ConfigurationInformation configurationInformation) + { + + var count = 1; + var @base = ASTExprHelper.ExprCollectSubNodes(ctx.GetChild(0), 0, astExprNodeMap)[0]; + + while (true) + { + int token = ASTUtil.GetAssertTerminatedTokenType(ctx.GetChild(count)); + var mathArithTypeEnum = TokenToMathEnum(token); + + var right = ASTExprHelper.ExprCollectSubNodes(ctx.GetChild(count + 1), 0, astExprNodeMap)[0]; + + var math = new ExprMathNode(mathArithTypeEnum, + configurationInformation.EngineDefaults.Expression.IsIntegerDivision, + configurationInformation.EngineDefaults.Expression.IsDivisionByZeroReturnsNull); + math.AddChildNode(@base); + math.AddChildNode(right); + @base = math; + + count += 2; + if (count >= ctx.ChildCount) + { + break; + } + } + return @base; + } + + private static MathArithTypeEnum TokenToMathEnum(int token) + { + switch (token) + { + case EsperEPL2GrammarLexer.DIV: + return MathArithTypeEnum.DIVIDE; + case EsperEPL2GrammarLexer.STAR: + return MathArithTypeEnum.MULTIPLY; + case EsperEPL2GrammarLexer.PLUS: + return MathArithTypeEnum.ADD; + case EsperEPL2GrammarLexer.MINUS: + return MathArithTypeEnum.SUBTRACT; + case EsperEPL2GrammarLexer.MOD: + return MathArithTypeEnum.MODULO; + default: + throw ASTWalkException.From("Encountered unrecognized math token type " + token); + } + } + + public static void AddOptionalNumber(ExprNode exprNode, EsperEPL2GrammarParser.NumberContext number) + { + if (number == null) + { + return; + } + ExprConstantNode constantNode = new ExprConstantNodeImpl(ASTConstantHelper.Parse(number)); + exprNode.AddChildNode(constantNode); + } + + public static void AddOptionalSimpleProperty(ExprNode exprNode, IToken token, VariableService variableService, StatementSpecRaw spec) + { + if (token == null) + { + return; + } + var node = ASTExprHelper.ResolvePropertyOrVariableIdentifier(token.Text, variableService, spec); + exprNode.AddChildNode(node); + } + + public static ExprNode[] ExprCollectSubNodesPerNode(IList expression, IDictionary astExprNodeMap) + { + var nodes = new ExprNode[expression.Count]; + for (var i = 0; i < expression.Count; i++) + { + nodes[i] = ExprCollectSubNodes(expression[i], 0, astExprNodeMap)[0]; + } + return nodes; + } + + public delegate void ExprAction(ExprNode exprNode, IDictionary astExprNodeMap, ITree node); + public delegate void RegExAction(RowRegexExprNode exprNode, IDictionary astRegExNodeMap, ITree node); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/parse/ASTExpressionDeclHelper.cs b/NEsper.Core/NEsper.Core/epl/parse/ASTExpressionDeclHelper.cs new file mode 100755 index 000000000..d6196f02d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/parse/ASTExpressionDeclHelper.cs @@ -0,0 +1,92 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using Antlr4.Runtime; +using Antlr4.Runtime.Tree; + +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.generated; +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.epl.parse +{ + public class ASTExpressionDeclHelper + { + public static Pair WalkExpressionDecl( + EsperEPL2GrammarParser.ExpressionDeclContext ctx, + IList scriptBodies, + IDictionary astExprNodeMap, + CommonTokenStream tokenStream) + { + var name = ctx.name.Text; + + if (ctx.alias != null) + { + if (ctx.alias.Text.ToLower().Trim() != "alias") + { + throw ASTWalkException.From("For expression alias '" + name + "' expecting 'alias' keyword but received '" + ctx.alias.Text + "'"); + } + if (ctx.columnList() != null) + { + throw ASTWalkException.From("For expression alias '" + name + "' expecting no parameters but received '" + tokenStream.GetText(ctx.columnList()) + "'"); + } + if (ctx.expressionDef() != null && ctx.expressionDef().expressionLambdaDecl() != null) + { + throw ASTWalkException.From("For expression alias '" + name + "' expecting an expression without parameters but received '" + tokenStream.GetText(ctx.expressionDef().expressionLambdaDecl()) + "'"); + } + if (ctx.expressionDef().stringconstant() != null) + { + throw ASTWalkException.From("For expression alias '" + name + "' expecting an expression but received a script"); + } + var exprNode = ASTExprHelper.ExprCollectSubNodes(ctx, 0, astExprNodeMap)[0]; + var alias = ctx.name.Text; + var decl = new ExpressionDeclItem(alias, Collections.GetEmptyList(), exprNode, true); + return new Pair(decl, null); + } + + if (ctx.expressionDef().stringconstant() != null) + { + var expressionText = scriptBodies[0]; + scriptBodies.RemoveAt(0); + var parameters = ASTUtil.GetIdentList(ctx.columnList()); + var optionalReturnType = ctx.classIdentifier() == null + ? null + : ASTUtil.UnescapeClassIdent(ctx.classIdentifier()); + var optionalReturnTypeArray = ctx.array != null; + var optionalDialect = ctx.expressionDialect() == null ? null : ctx.expressionDialect().d.Text; + var optionalEventTypeName = ASTTypeExpressionAnnoHelper.ExpectMayTypeAnno(ctx.typeExpressionAnnotation(), tokenStream); + var script = new ExpressionScriptProvided( + name, expressionText, parameters, + optionalReturnType, + optionalReturnTypeArray, + optionalEventTypeName, + optionalDialect); + return new Pair(null, script); + } + + var ctxexpr = ctx.expressionDef(); + var inner = ASTExprHelper.ExprCollectSubNodes(ctxexpr.expression(), 0, astExprNodeMap)[0]; + + var parametersNames = Collections.GetEmptyList(); + var lambdactx = ctxexpr.expressionLambdaDecl(); + if (ctxexpr.expressionLambdaDecl() != null) + { + parametersNames = ASTLibFunctionHelper.GetLambdaGoesParams(lambdactx); + } + + var expr = new ExpressionDeclItem(name, parametersNames, inner, false); + return new Pair(expr, null); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/parse/ASTFilterSpecHelper.cs b/NEsper.Core/NEsper.Core/epl/parse/ASTFilterSpecHelper.cs new file mode 100755 index 000000000..96da4e993 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/parse/ASTFilterSpecHelper.cs @@ -0,0 +1,36 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using Antlr4.Runtime.Tree; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.generated; +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.epl.parse +{ + /// + /// Builds a filter specification from filter AST nodes. + /// + public class ASTFilterSpecHelper + { + public static FilterSpecRaw WalkFilterSpec( + EsperEPL2GrammarParser.EventFilterExpressionContext ctx, + PropertyEvalSpec propertyEvalSpec, + IDictionary astExprNodeMap) + { + string eventName = ASTUtil.UnescapeClassIdent(ctx.classIdentifier()); + IList exprNodes = ctx.expressionList() != null + ? ASTExprHelper.ExprCollectSubNodes(ctx.expressionList(), 0, astExprNodeMap) + : new List(1); + return new FilterSpecRaw(eventName, exprNodes, propertyEvalSpec); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/parse/ASTGraphHelper.cs b/NEsper.Core/NEsper.Core/epl/parse/ASTGraphHelper.cs new file mode 100755 index 000000000..24733bcb7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/parse/ASTGraphHelper.cs @@ -0,0 +1,151 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using Antlr4.Runtime.Tree; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.generated; +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.epl.parse +{ + public class ASTGraphHelper + { + public static CreateDataFlowDesc WalkCreateDataFlow(EsperEPL2GrammarParser.CreateDataflowContext ctx, IDictionary astGraphNodeMap, EngineImportService engineImportService) + { + var graphName = ctx.name.Text; + + IList ops = new List(); + IList schemas = new List(); + + IList gopctxs = ctx.gopList().gop(); + foreach (var gopctx in gopctxs) { + if (gopctx.createSchemaExpr() != null) { + schemas.Add(ASTCreateSchemaHelper.WalkCreateSchema(gopctx.createSchemaExpr())); + } + else { + ops.Add(ParseOp(gopctx, astGraphNodeMap, engineImportService)); + } + } + return new CreateDataFlowDesc(graphName, ops, schemas); + } + + private static GraphOperatorSpec ParseOp(EsperEPL2GrammarParser.GopContext ctx, IDictionary astGraphNodeMap, EngineImportService engineImportService) + { + var operatorName = ctx.opName != null ? ctx.opName.Text : ctx.s.Text; + + var input = new GraphOperatorInput(); + if (ctx.gopParams() != null) { + ParseParams(ctx.gopParams(), input); + } + + var output = new GraphOperatorOutput(); + if (ctx.gopOut() != null) { + ParseOutput(ctx.gopOut(), output); + } + + GraphOperatorDetail detail = null; + if (ctx.gopDetail() != null) { + var configs = new LinkedHashMap(); + var cfgctxs = ctx.gopDetail().gopConfig(); + foreach (var cfgctx in cfgctxs) { + String name; + Object value = astGraphNodeMap.Delete(cfgctx); + if (cfgctx.n != null) { + name = cfgctx.n.Text; + } + else { + name = "select"; + } + configs.Put(name, value); + } + detail = new GraphOperatorDetail(configs); + } + + IList annotations; + if (ctx.annotationEnum() != null) { + var annoctxs = ctx.annotationEnum(); + annotations = annoctxs + .Select(annoctx => ASTAnnotationHelper.Walk(annoctx, engineImportService)) + .ToList(); + } + else { + annotations = Collections.GetEmptyList(); + } + + return new GraphOperatorSpec(operatorName, input, output, detail, annotations); + } + + private static void ParseParams(EsperEPL2GrammarParser.GopParamsContext ctx, GraphOperatorInput input) { + if (ctx.gopParamsItemList() == null) { + return; + } + IList items = ctx.gopParamsItemList().gopParamsItem(); + foreach (var item in items) { + var streamNames = ParseParamsStreamNames(item); + var aliasName = item.gopParamsItemAs() != null ? item.gopParamsItemAs().a.Text : null; + input.StreamNamesAndAliases.Add(new GraphOperatorInputNamesAlias(streamNames, aliasName)); + } + } + + private static String[] ParseParamsStreamNames(EsperEPL2GrammarParser.GopParamsItemContext item) { + IList paramNames = new List(1); + if (item.gopParamsItemMany() != null) { + foreach (var ctx in item.gopParamsItemMany().classIdentifier()) { + paramNames.Add(ctx.GetText()); + } + } + else { + paramNames.Add(ASTUtil.UnescapeClassIdent(item.classIdentifier())); + } + return paramNames.ToArray(); + } + + private static void ParseOutput(EsperEPL2GrammarParser.GopOutContext ctx, GraphOperatorOutput output) { + if (ctx == null) { + return; + } + IList items = ctx.gopOutItem(); + foreach (var item in items) { + var streamName = item.n.GetText(); + + IList types = new List(); + if (item.gopOutTypeList() != null) { + foreach (var pctx in item.gopOutTypeList().gopOutTypeParam()) { + var type = ParseType(pctx); + types.Add(type); + } + } + output.Items.Add(new GraphOperatorOutputItem(streamName, types)); + } + } + + private static GraphOperatorOutputItemType ParseType(EsperEPL2GrammarParser.GopOutTypeParamContext ctx) { + + if (ctx.q != null) { + return new GraphOperatorOutputItemType(true, null, null); + } + + var className = ASTUtil.UnescapeClassIdent(ctx.gopOutTypeItem().classIdentifier()); + IList typeParams = new List(); + if (ctx.gopOutTypeItem().gopOutTypeList() != null) { + IList pctxs = ctx.gopOutTypeItem().gopOutTypeList().gopOutTypeParam(); + foreach (var pctx in pctxs) { + var type = ParseType(pctx); + typeParams.Add(type); + } + } + return new GraphOperatorOutputItemType(false, className, typeParams); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/parse/ASTGroupByHelper.cs b/NEsper.Core/NEsper.Core/epl/parse/ASTGroupByHelper.cs new file mode 100755 index 000000000..25107a329 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/parse/ASTGroupByHelper.cs @@ -0,0 +1,105 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using Antlr4.Runtime.Tree; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.generated; +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.epl.parse +{ + public class ASTGroupByHelper + { + public static void WalkGroupBy( + EsperEPL2GrammarParser.GroupByListExprContext ctx, + IDictionary astExprNodeMap, + IList groupByExpressions) + { + IList choices = ctx.groupByListChoice(); + foreach (EsperEPL2GrammarParser.GroupByListChoiceContext choice in choices) + { + GroupByClauseElement element = WalkChoice(choice, astExprNodeMap); + groupByExpressions.Add(element); + } + } + + private static GroupByClauseElement WalkChoice( + EsperEPL2GrammarParser.GroupByListChoiceContext choice, + IDictionary astExprNodeMap) + { + if (choice.e1 != null) + { + ExprNode expr = ASTExprHelper.ExprCollectSubNodes(choice.e1, 0, astExprNodeMap)[0]; + return new GroupByClauseElementExpr(expr); + } + if (choice.groupByCubeOrRollup() != null) + { + return WalkCubeOrRollup(choice.groupByCubeOrRollup(), astExprNodeMap); + } + return WalkGroupingSets(choice.groupByGroupingSets().groupBySetsChoice(), astExprNodeMap); + + } + + private static GroupByClauseElement WalkCubeOrRollup( + EsperEPL2GrammarParser.GroupByCubeOrRollupContext ctx, + IDictionary astExprNodeMap) + { + bool cube = ctx.CUBE() != null; + IList combinables = WalkCombinables(ctx.groupByCombinableExpr(), astExprNodeMap); + return new GroupByClauseElementRollupOrCube(cube, combinables); + } + + private static IList WalkCombinables( + IList ctxs, + IDictionary astExprNodeMap) + { + IList elements = new List(); + foreach (EsperEPL2GrammarParser.GroupByCombinableExprContext ctx in ctxs) + { + elements.Add(WalkCombinable(ctx, astExprNodeMap)); + } + return elements; + } + + private static GroupByClauseElement WalkCombinable( + EsperEPL2GrammarParser.GroupByCombinableExprContext ctx, + IDictionary astExprNodeMap) + { + if (ctx.e1 != null && ctx.LPAREN() == null) + { + ExprNode expr = ASTExprHelper.ExprCollectSubNodes(ctx.e1, 0, astExprNodeMap)[0]; + return new GroupByClauseElementExpr(expr); + } + IList combined = ASTExprHelper.ExprCollectSubNodes(ctx, 0, astExprNodeMap); + return new GroupByClauseElementCombinedExpr(combined); + } + + private static GroupByClauseElementGroupingSet WalkGroupingSets( + IList ctxs, + IDictionary astExprNodeMap) + { + IList elements = new List(); + foreach (EsperEPL2GrammarParser.GroupBySetsChoiceContext ctx in ctxs) + { + if (ctx.groupByCubeOrRollup() != null) + { + elements.Add(WalkCubeOrRollup(ctx.groupByCubeOrRollup(), astExprNodeMap)); + } + else + { + elements.Add(WalkCombinable(ctx.groupByCombinableExpr(), astExprNodeMap)); + } + } + return new GroupByClauseElementGroupingSet(elements); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/parse/ASTJsonHelper.cs b/NEsper.Core/NEsper.Core/epl/parse/ASTJsonHelper.cs new file mode 100755 index 000000000..1099b2661 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/parse/ASTJsonHelper.cs @@ -0,0 +1,194 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; + +using Antlr4.Runtime; + +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.generated; + +namespace com.espertech.esper.epl.parse +{ + /// + /// Walker to annotation stuctures. + /// + public class ASTJsonHelper + { + /// + /// Walk an annotation root name or child node (nested annotations). + /// + /// annotation walk node + /// annotation descriptor + /// com.espertech.esper.epl.parse.ASTWalkException if the walk failed + public static Object Walk(CommonTokenStream tokenStream, EsperEPL2GrammarParser.JsonvalueContext node) + { + if (node.constant() != null) + { + EsperEPL2GrammarParser.ConstantContext constCtx = node.constant(); + if (constCtx.stringconstant() != null) + { + return ExtractString(constCtx.stringconstant().GetText()); + } + else + { + return ASTConstantHelper.Parse(constCtx.GetChild(0)); + } + } + else if (node.jsonobject() != null) + { + return WalkObject(tokenStream, node.jsonobject()); + } + else if (node.jsonarray() != null) + { + return WalkArray(tokenStream, node.jsonarray()); + } + throw ASTWalkException.From("Encountered unexpected node type in json tree", tokenStream, node); + } + + public static IDictionary WalkObject(CommonTokenStream tokenStream, EsperEPL2GrammarParser.JsonobjectContext ctx) + { + IDictionary map = new LinkedHashMap(); + IList pairs = ctx.jsonmembers().jsonpair(); + foreach (EsperEPL2GrammarParser.JsonpairContext pair in pairs) + { + Pair value = WalkJSONField(tokenStream, pair); + map.Put(value.First, value.Second); + } + return map; + } + + public static IList WalkArray(CommonTokenStream tokenStream, EsperEPL2GrammarParser.JsonarrayContext ctx) + { + IList list = new List(); + if (ctx.jsonelements() == null) + { + return list; + } + IList values = ctx.jsonelements().jsonvalue(); + foreach (EsperEPL2GrammarParser.JsonvalueContext value in values) + { + Object val = Walk(tokenStream, value); + list.Add(val); + } + return list; + } + + private static Pair WalkJSONField(CommonTokenStream tokenStream, EsperEPL2GrammarParser.JsonpairContext ctx) + { + String label; + if (ctx.stringconstant() != null) + { + label = ExtractString(ctx.stringconstant().GetText()); + } + else + { + label = ctx.keywordAllowedIdent().GetText(); + } + Object value = Walk(tokenStream, ctx.jsonvalue()); + return new Pair(label, value); + } + + private static String ExtractString(String text) + { + var sb = new StringBuilder(text); + int startPoint = 1; + for (; ; ) + { + int slashIndex = sb.IndexOf("\\", startPoint); + if (slashIndex == -1) + { + break; + } + char escapeType = sb[slashIndex + 1]; + switch (escapeType) + { + case 'u': + String unicode = ExtractUnicode(sb, slashIndex); + sb.Remove(slashIndex, 6); + sb.Insert(slashIndex, unicode); + //sb.Replace(slashIndex, slashIndex + 6, unicode); // backspace + break; // back to the loop + + // note: C#'s character escapes match JSON's, which is why it looks like we're replacing + // "\b" with "\b". We're actually replacing 2 characters (slash-b) with one (backspace). + case 'b': + sb.Remove(slashIndex, 2); + sb.Insert(slashIndex, "\b"); + //sb.Replace(slashIndex, slashIndex + 2, "\b"); + break; + case 't': + sb.Remove(slashIndex, 2); + sb.Insert(slashIndex, "\t"); + //sb.Replace(slashIndex, slashIndex + 2, "\t"); + break; + case 'n': + sb.Remove(slashIndex, 2); + sb.Insert(slashIndex, "\n"); + //sb.Replace(slashIndex, slashIndex + 2, "\n"); + break; + case 'f': + sb.Remove(slashIndex, 2); + sb.Insert(slashIndex, "\f"); + //sb.Replace(slashIndex, slashIndex + 2, "\f"); + break; + case 'r': + sb.Remove(slashIndex, 2); + sb.Insert(slashIndex, "\r"); + //sb.Replace(slashIndex, slashIndex + 2, "\r"); + break; + case '\'': + sb.Remove(slashIndex, 2); + sb.Insert(slashIndex, "\'"); + //sb.Replace(slashIndex, slashIndex + 2, "\'"); + break; + case '\"': + sb.Remove(slashIndex, 2); + sb.Insert(slashIndex, "\""); + //sb.Replace(slashIndex, slashIndex + 2, "\""); + break; + case '\\': + sb.Remove(slashIndex, 2); + sb.Insert(slashIndex, "\\"); + //sb.Replace(slashIndex, slashIndex + 2, "\\"); + break; + case '/': + sb.Remove(slashIndex, 2); + sb.Insert(slashIndex, "/"); + //sb.Replace(slashIndex, slashIndex + 2, "/"); + break; + default: + break; + } + startPoint = slashIndex + 1; + } + sb.Remove(0, 1); + sb.Remove(sb.Length - 1, 1); + return sb.ToString(); + } + + private static String ExtractUnicode(StringBuilder sb, int slashIndex) + { + String result; + String code = sb.ToString().Substring(slashIndex + 2, 4); + int ordinal = int.Parse(code, NumberStyles.HexNumber); // hex to integer + if (ordinal < 0x10000) + return ((char)ordinal).ToString(); + + return string.Format( + "{0}{1}", + (char)((ordinal - 0x10000) >> 10 + 0xD800), + (char)((ordinal - 0x10000) & 0x3FF + 0xDC00)); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/parse/ASTLibFunctionHelper.cs b/NEsper.Core/NEsper.Core/epl/parse/ASTLibFunctionHelper.cs new file mode 100755 index 000000000..6ca036fab --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/parse/ASTLibFunctionHelper.cs @@ -0,0 +1,535 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.IO; + +using Antlr4.Runtime; +using Antlr4.Runtime.Tree; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.declexpr; +using com.espertech.esper.epl.enummethod.dot; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.dot; +using com.espertech.esper.epl.expression.funcs; +using com.espertech.esper.epl.expression.methodagg; +using com.espertech.esper.epl.generated; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.epl.variable; +using com.espertech.esper.plugin; +using com.espertech.esper.type; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.parse +{ + public class ASTLibFunctionHelper + { + public static IList GetLibFuncChain( + ICollection ctxs, + IDictionary astExprNodeMap) + { + var chained = new List(ctxs.Count); + foreach (var ctx in ctxs) + { + var chainSpec = GetLibFunctionChainSpec(ctx, astExprNodeMap); + chained.Add(chainSpec); + } + return chained; + } + + public static ExprChainedSpec GetLibFunctionChainSpec( + EsperEPL2GrammarParser.LibFunctionNoClassContext ctx, + IDictionary astExprNodeMap) + { + var methodName = ASTConstantHelper.RemoveTicks(ctx.funcIdentChained().GetText()); + + var parameters = GetExprNodesLibFunc(ctx.libFunctionArgs(), astExprNodeMap); + var property = ctx.l == null; + return new ExprChainedSpec(methodName, parameters, property); + } + + public static IList GetExprNodesLibFunc( + EsperEPL2GrammarParser.LibFunctionArgsContext ctx, + IDictionary astExprNodeMap) + { + if (ctx == null) + { + return Collections.GetEmptyList(); + } + IList args = ctx.libFunctionArgItem(); + if (args == null || args.IsEmpty()) + { + return Collections.GetEmptyList(); + } + var parameters = new List(args.Count); + foreach (var arg in args) + { + if (arg.expressionLambdaDecl() != null) + { + var lambdaparams = GetLambdaGoesParams(arg.expressionLambdaDecl()); + var goes = new ExprLambdaGoesNode(lambdaparams); + var lambdaExpr = + ASTExprHelper.ExprCollectSubNodes(arg.expressionWithNamed(), 0, astExprNodeMap)[0]; + goes.AddChildNode(lambdaExpr); + parameters.Add(goes); + } + else + { + var parameter = + ASTExprHelper.ExprCollectSubNodes(arg.expressionWithNamed(), 0, astExprNodeMap)[0]; + parameters.Add(parameter); + } + } + return parameters; + } + + internal static IList GetLambdaGoesParams(EsperEPL2GrammarParser.ExpressionLambdaDeclContext ctx) + { + IList parameters; + if (ctx.i != null) + { + parameters = new List(1); + parameters.Add(ctx.i.Text); + } + else + { + parameters = ASTUtil.GetIdentList(ctx.columnList()); + } + return parameters; + } + + public static void HandleLibFunc( + CommonTokenStream tokenStream, + EsperEPL2GrammarParser.LibFunctionContext ctx, + ConfigurationInformation configurationInformation, + EngineImportService engineImportService, + IDictionary astExprNodeMap, + LazyAllocatedMap plugInAggregations, + string engineURI, + ExpressionDeclDesc expressionDeclarations, + ExprDeclaredService exprDeclaredService, + IList scriptExpressions, + ContextDescriptor contextDescriptor, + TableService tableService, + StatementSpecRaw statementSpec, + VariableService variableService) + { + + var model = GetModel(ctx, tokenStream); + var duckType = configurationInformation.EngineDefaults.Expression.IsDuckTyping; + var udfCache = configurationInformation.EngineDefaults.Expression.IsUdfCache; + + // handle "some.Xyz(...)" or "some.other.Xyz(...)" + if (model.ChainElements.Count == 1 && + model.OptionalClassIdent != null && + ASTTableExprHelper.CheckTableNameGetExprForProperty(tableService, model.OptionalClassIdent) == null) + { + + var chainSpec = GetLibFunctionChainSpec(model.ChainElements[0], astExprNodeMap); + + var declaredNodeX = ExprDeclaredHelper.GetExistsDeclaredExpr( + model.OptionalClassIdent, Collections.GetEmptyList(), expressionDeclarations.Expressions, + exprDeclaredService, contextDescriptor); + if (declaredNodeX != null) + { + var exprNode = new ExprDotNodeImpl(Collections.SingletonList(chainSpec), duckType, udfCache); + exprNode.AddChildNode(declaredNodeX); + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(exprNode, ctx, astExprNodeMap); + return; + } + + var chainX = new List(2); + chainX.Add(new ExprChainedSpec(model.OptionalClassIdent, Collections.GetEmptyList(), true)); + chainX.Add(chainSpec); + var dotNodeX = new ExprDotNodeImpl( + chainX, configurationInformation.EngineDefaults.Expression.IsDuckTyping, + configurationInformation.EngineDefaults.Expression.IsUdfCache); + if (dotNodeX.IsVariableOpGetName(variableService) != null) + { + statementSpec.HasVariables = true; + } + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(dotNodeX, ctx, astExprNodeMap); + return; + } + + // try additional built-in single-row function + var singleRowExtNode = + engineImportService.ResolveSingleRowExtendedBuiltin(model.ChainElements[0].FuncName); + if (singleRowExtNode != null) + { + if (model.ChainElements.Count == 1) + { + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(singleRowExtNode, ctx, astExprNodeMap); + return; + } + var spec = new List(); + var firstArgs = model.ChainElements[0].Args; + var childExpressions = GetExprNodesLibFunc(firstArgs, astExprNodeMap); + singleRowExtNode.AddChildNodes(childExpressions); + AddChainRemainderFromOffset(model.ChainElements, 1, spec, astExprNodeMap); + var dotNodeX = new ExprDotNodeImpl( + spec, configurationInformation.EngineDefaults.Expression.IsDuckTyping, + configurationInformation.EngineDefaults.Expression.IsUdfCache); + dotNodeX.AddChildNode(singleRowExtNode); + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(dotNodeX, ctx, astExprNodeMap); + return; + } + + // try plug-in single-row function + try + { + var firstFunctionX = model.ChainElements[0].FuncName; + var firstFunctionIsProperty = !model.ChainElements[0].HasLeftParen; + var classMethodPair = + engineImportService.ResolveSingleRow(firstFunctionX); + var spec = new List(); + var firstArgs = model.ChainElements[0].Args; + var childExpressions = GetExprNodesLibFunc(firstArgs, astExprNodeMap); + spec.Add( + new ExprChainedSpec(classMethodPair.Second.MethodName, childExpressions, firstFunctionIsProperty)); + AddChainRemainderFromOffset(model.ChainElements, 1, spec, astExprNodeMap); + var plugin = new ExprPlugInSingleRowNode( + firstFunctionX, classMethodPair.First, spec, classMethodPair.Second); + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(plugin, ctx, astExprNodeMap); + return; + } + catch (EngineImportUndefinedException) + { + // Not an single-row function + } + catch (EngineImportException e) + { + throw new IllegalStateException("Error resolving single-row function: " + e.Message, e); + } + + // special case for min,max + var firstFunction = model.ChainElements[0].FuncName; + if ((firstFunction.ToLowerInvariant().Equals("max")) || (firstFunction.ToLowerInvariant().Equals("min")) || + (firstFunction.ToLowerInvariant().Equals("fmax")) || (firstFunction.ToLowerInvariant().Equals("fmin"))) + { + var firstArgs = model.ChainElements[0].Args; + HandleMinMax(firstFunction, firstArgs, astExprNodeMap); + return; + } + + // obtain chain with actual expressions + IList chain = new List(); + AddChainRemainderFromOffset(model.ChainElements, 0, chain, astExprNodeMap); + + // add chain element for class INFO, if any + var distinct = model.ChainElements[0].Args != null && model.ChainElements[0].Args.DISTINCT() != null; + if (model.OptionalClassIdent != null) + { + chain.Insert( + 0, new ExprChainedSpec(model.OptionalClassIdent, Collections.GetEmptyList(), true)); + distinct = false; + } + firstFunction = chain[0].Name; + + // try plug-in aggregation function + var aggregationNode = ASTAggregationHelper.TryResolveAsAggregation( + engineImportService, distinct, firstFunction, plugInAggregations, engineURI); + if (aggregationNode != null) + { + var firstSpec = chain.DeleteAt(0); + aggregationNode.AddChildNodes(firstSpec.Parameters); + ExprNode exprNode; + if (chain.IsEmpty()) + { + exprNode = aggregationNode; + } + else + { + exprNode = new ExprDotNodeImpl(chain, duckType, udfCache); + exprNode.AddChildNode(aggregationNode); + } + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(exprNode, ctx, astExprNodeMap); + return; + } + + // try declared or alias expression + var declaredNode = ExprDeclaredHelper.GetExistsDeclaredExpr( + firstFunction, chain[0].Parameters, expressionDeclarations.Expressions, exprDeclaredService, + contextDescriptor); + if (declaredNode != null) + { + chain.RemoveAt(0); + ExprNode exprNode; + if (chain.IsEmpty()) + { + exprNode = declaredNode; + } + else + { + exprNode = new ExprDotNodeImpl(chain, duckType, udfCache); + exprNode.AddChildNode(declaredNode); + } + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(exprNode, ctx, astExprNodeMap); + return; + } + + // try script + var scriptNode = + ExprDeclaredHelper.GetExistsScript( + configurationInformation.EngineDefaults.Scripts.DefaultDialect, chain[0].Name, chain[0].Parameters, + scriptExpressions, exprDeclaredService); + if (scriptNode != null) + { + chain.RemoveAt(0); + ExprNode exprNode; + if (chain.IsEmpty()) + { + exprNode = scriptNode; + } + else + { + exprNode = new ExprDotNodeImpl(chain, duckType, udfCache); + exprNode.AddChildNode(scriptNode); + } + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(exprNode, ctx, astExprNodeMap); + return; + } + + // try table + var tableInfo = ASTTableExprHelper.CheckTableNameGetLibFunc( + tableService, engineImportService, plugInAggregations, engineURI, firstFunction, chain); + if (tableInfo != null) + { + ASTTableExprHelper.AddTableExpressionReference(statementSpec, tableInfo.First); + chain = tableInfo.Second; + ExprNode exprNode; + if (chain.IsEmpty()) + { + exprNode = tableInfo.First; + } + else + { + exprNode = new ExprDotNodeImpl(chain, duckType, udfCache); + exprNode.AddChildNode(tableInfo.First); + } + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(exprNode, ctx, astExprNodeMap); + return; + } + + // Could be a mapped property with an expression-parameter "Mapped(expr)" or array property with an expression-parameter "Array(expr)". + ExprDotNode dotNode; + if (chain.Count == 1) + { + dotNode = new ExprDotNodeImpl(chain, false, false); + } + else + { + dotNode = new ExprDotNodeImpl(chain, duckType, udfCache); + } + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(dotNode, ctx, astExprNodeMap); + } + + private static void AddChainRemainderFromOffset( + IList chainElements, + int offset, + IList specList, + IDictionary astExprNodeMap) + { + for (var i = offset; i < chainElements.Count; i++) + { + var spec = GetLibFunctionChainSpec(chainElements[i], astExprNodeMap); + specList.Add(spec); + } + } + + private static ExprChainedSpec GetLibFunctionChainSpec( + ASTLibModelChainElement element, + IDictionary astExprNodeMap) + { + var methodName = ASTConstantHelper.RemoveTicks(element.FuncName); + var parameters = GetExprNodesLibFunc(element.Args, astExprNodeMap); + return new ExprChainedSpec(methodName, parameters, !element.HasLeftParen); + } + + private static ASTLibModel GetModel( + EsperEPL2GrammarParser.LibFunctionContext ctx, + CommonTokenStream tokenStream) + { + var root = ctx.libFunctionWithClass(); + IList ctxElements = ctx.libFunctionNoClass(); + + // there are no additional methods + if (ctxElements == null || ctxElements.IsEmpty()) + { + var classIdent = root.classIdentifier() == null + ? null + : ASTUtil.UnescapeClassIdent(root.classIdentifier()); + var ele = FromRoot(root); + return new ASTLibModel(classIdent, Collections.SingletonList(ele)); + } + + // add root and chain to just a list of elements + var chainElements = new List(ctxElements.Count + 1); + var rootElement = FromRoot(root); + chainElements.Add(rootElement); + foreach (var chainedCtx in ctxElements) + { + var chainedElement = new ASTLibModelChainElement( + chainedCtx.funcIdentChained().GetText(), chainedCtx.libFunctionArgs(), chainedCtx.l != null); + chainElements.Add(chainedElement); + } + + // determine/remove the list of chain elements, from the start and uninterrupted, that don't have parameters (no parenthesis 'l') + var chainElementsNoArgs = new List(chainElements.Count); + for (var ii = 0; ii < chainElements.Count; ii++) + { + var element = chainElements[ii]; + if (!element.HasLeftParen) + { + // has no parenthesis, therefore part of class identifier + chainElementsNoArgs.Add(element); + chainElements.RemoveAt(ii); + ii--; + } + else + { + // else stop here + break; + } + } + + // write the class identifier including the no-arg chain elements + var classIdentBuf = new StringWriter(); + var delimiter = ""; + if (root.classIdentifier() != null) + { + classIdentBuf.Write(ASTUtil.UnescapeClassIdent(root.classIdentifier())); + delimiter = "."; + } + foreach (var noarg in chainElementsNoArgs) + { + classIdentBuf.Write(delimiter); + classIdentBuf.Write(noarg.FuncName); + delimiter = "."; + } + + if (chainElements.IsEmpty()) + { + // would this be an event property, but then that is handled greedily by parser + throw ASTWalkException.From("Encountered unrecognized lib function call", tokenStream, ctx); + } + + // class ident can be null if empty + var classIdentifierString = classIdentBuf.ToString(); + var classIdentifier = classIdentifierString.Length > 0 ? classIdentifierString : null; + + return new ASTLibModel(classIdentifier, chainElements); + } + + public static ASTLibModelChainElement FromRoot(EsperEPL2GrammarParser.LibFunctionWithClassContext root) + { + if (root.funcIdentTop() != null) + { + return new ASTLibModelChainElement( + root.funcIdentTop().GetText(), root.libFunctionArgs(), root.l != null); + } + else + { + return new ASTLibModelChainElement( + root.funcIdentInner().GetText(), root.libFunctionArgs(), root.l != null); + } + } + + // Min/Max nodes can be either an aggregate or a per-row function depending on the number or arguments + private static void HandleMinMax( + string ident, + EsperEPL2GrammarParser.LibFunctionArgsContext ctxArgs, + IDictionary astExprNodeMap) + { + // Determine min or max + var childNodeText = ident; + MinMaxTypeEnum minMaxTypeEnum; + var filtered = childNodeText.StartsWith("f"); + if (childNodeText.ToLowerInvariant().Equals("min") || childNodeText.ToLowerInvariant().Equals("fmin")) + { + minMaxTypeEnum = MinMaxTypeEnum.MIN; + } + else if (childNodeText.ToLowerInvariant().Equals("max") || childNodeText.ToLowerInvariant().Equals("fmax")) + { + minMaxTypeEnum = MinMaxTypeEnum.MAX; + } + else + { + throw ASTWalkException.From("Uncountered unrecognized min or max node '" + ident + "'"); + } + + var args = Collections.GetEmptyList(); + if (ctxArgs != null && ctxArgs.libFunctionArgItem() != null) + { + args = ASTExprHelper.ExprCollectSubNodes(ctxArgs, 0, astExprNodeMap); + } + var numArgsPositional = ExprAggregateNodeUtil.CountPositionalArgs(args); + + var isDistinct = ctxArgs != null && ctxArgs.DISTINCT() != null; + if (numArgsPositional > 1 && isDistinct && !filtered) + { + throw ASTWalkException.From( + "The distinct keyword is not valid in per-row min and max " + + "functions with multiple sub-expressions"); + } + + ExprNode minMaxNode; + if (!isDistinct && numArgsPositional > 1 && !filtered) + { + // use the row function + minMaxNode = new ExprMinMaxRowNode(minMaxTypeEnum); + } + else + { + // use the aggregation function + minMaxNode = new ExprMinMaxAggrNode(isDistinct, minMaxTypeEnum, filtered, false); + } + minMaxNode.AddChildNodes(args); + astExprNodeMap.Put(ctxArgs, minMaxNode); + } + + public class ASTLibModel + { + public ASTLibModel(string optionalClassIdent, IList chainElements) + { + OptionalClassIdent = optionalClassIdent; + ChainElements = chainElements; + } + + public string OptionalClassIdent { get; private set; } + + public IList ChainElements { get; private set; } + } + + public class ASTLibModelChainElement + { + public ASTLibModelChainElement( + string funcName, + EsperEPL2GrammarParser.LibFunctionArgsContext args, + bool hasLeftParen) + { + FuncName = funcName; + Args = args; + HasLeftParen = hasLeftParen; + } + + public string FuncName { get; private set; } + + public EsperEPL2GrammarParser.LibFunctionArgsContext Args { get; private set; } + + public bool HasLeftParen { get; private set; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/parse/ASTMatchRecognizeHelper.cs b/NEsper.Core/NEsper.Core/epl/parse/ASTMatchRecognizeHelper.cs new file mode 100755 index 000000000..f51ff5c26 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/parse/ASTMatchRecognizeHelper.cs @@ -0,0 +1,84 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using Antlr4.Runtime; +using Antlr4.Runtime.Tree; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.generated; +using com.espertech.esper.epl.spec; +using com.espertech.esper.rowregex; + +namespace com.espertech.esper.epl.parse +{ + /// + /// Helper class for walking the match-recognize AST. + /// + public class ASTMatchRecognizeHelper + { + private const String Message = "Match-recognize AFTER clause must be either AFTER MATCH SKIP TO LAST ROW or AFTER MATCH SKIP TO NEXT ROW or AFTER MATCH SKIP TO CURRENT ROW"; + + public static MatchRecognizeSkipEnum ParseSkip(CommonTokenStream tokenStream, EsperEPL2GrammarParser.MatchRecogMatchesAfterSkipContext ctx) + { + if ((!ctx.i1.GetText().ToUpper().Equals("MATCH")) || + (!ctx.i2.GetText().ToUpper().Equals("SKIP")) || + (!ctx.i5.GetText().ToUpper().Equals("ROW")) + ) + { + throw ASTWalkException.From(Message, tokenStream, ctx); + } + + if ((!ctx.i3.GetText().ToUpper().Equals("TO")) && + (!ctx.i3.GetText().ToUpper().Equals("PAST")) + ) + { + throw ASTWalkException.From(Message, tokenStream, ctx); + } + + if (ctx.i4.GetText().ToUpper().Equals("LAST")) + { + return MatchRecognizeSkipEnum.PAST_LAST_ROW; + } + else if (ctx.i4.GetText().ToUpper().Equals("NEXT")) + { + return MatchRecognizeSkipEnum.TO_NEXT_ROW; + } + else if (ctx.i4.GetText().ToUpper().Equals("CURRENT")) + { + return MatchRecognizeSkipEnum.TO_CURRENT_ROW; + } + throw ASTWalkException.From(Message); + } + + public static RowRegexExprRepeatDesc walkOptionalRepeat(EsperEPL2GrammarParser.MatchRecogPatternRepeatContext ctx, IDictionary astExprNodeMap) + { + if (ctx == null) + { + return null; + } + + ExprNode e1 = ctx.e1 == null ? null : ASTExprHelper.ExprCollectSubNodes(ctx.e1, 0, astExprNodeMap)[0]; + ExprNode e2 = ctx.e2 == null ? null : ASTExprHelper.ExprCollectSubNodes(ctx.e2, 0, astExprNodeMap)[0]; + + if (ctx.comma == null && ctx.e1 != null) + { + return new RowRegexExprRepeatDesc(null, null, e1); + } + + if (e1 == null && e2 == null) + { + throw ASTWalkException.From("Invalid match-recognize quantifier '" + ctx.GetText() + "', expecting an expression"); + } + + return new RowRegexExprRepeatDesc(e1, e2, null); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/parse/ASTMatchUntilHelper.cs b/NEsper.Core/NEsper.Core/epl/parse/ASTMatchUntilHelper.cs new file mode 100755 index 000000000..f7eb048e0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/parse/ASTMatchUntilHelper.cs @@ -0,0 +1,99 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.parse +{ + /// + /// Helper for walking a pattern match-until clause. + /// + public class ASTMatchUntilHelper + { + const string NumericMessage = "Match-until bounds expect a numeric or expression value"; + + /// + /// Validate. + /// + /// is the lower bounds, or null if none supplied + /// is the upper bounds, or null if none supplied + /// true to allow zero value for lower range + /// + /// true if closed range of constants and the constants are the same value + /// + /// ASTWalkException if the AST is incorrect + public static bool Validate(ExprNode lowerBounds, ExprNode upperBounds, bool isAllowLowerZero) + { + var isConstants = true; + + Object constantLower = null; + if (ExprNodeUtility.IsConstantValueExpr(lowerBounds)) { + constantLower = lowerBounds.ExprEvaluator.Evaluate(new EvaluateParams(null, true, null)); + if (constantLower == null || !(constantLower.IsNumber())) + { + throw ASTWalkException.From(NumericMessage); + } + } + else { + isConstants = lowerBounds == null; + } + + Object constantUpper = null; + if (ExprNodeUtility.IsConstantValueExpr(upperBounds)) { + constantUpper = upperBounds.ExprEvaluator.Evaluate(new EvaluateParams(null, true, null)); + if (constantUpper == null || !(constantUpper.IsNumber())) + { + throw ASTWalkException.From(NumericMessage); + } + } + else { + isConstants = isConstants && upperBounds == null; + } + + if (!isConstants) { + return true; + } + + if (constantLower != null && constantUpper != null) { + int lower = constantLower.AsInt(); + int upper = constantUpper.AsInt(); + if (lower > upper) { + throw ASTWalkException.From( + "Incorrect range specification, lower bounds value '" + lower + + "' is higher then higher bounds '" + upper + "'"); + } + } + + VerifyConstant(constantLower, isAllowLowerZero); + VerifyConstant(constantUpper, false); + + return constantLower != null && constantUpper != null && constantLower.Equals(constantUpper); + } + + private static void VerifyConstant(Object value, bool isAllowZero) { + if (value != null) { + int bound = value.AsInt(); + if (isAllowZero) { + if (bound < 0) { + throw ASTWalkException.From("Incorrect range specification, a bounds value of negative value is not allowed"); + } + } + else { + if (bound <= 0) { + throw ASTWalkException.From("Incorrect range specification, a bounds value of zero or negative value is not allowed"); + } + } + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/parse/ASTOutputLimitHelper.cs b/NEsper.Core/NEsper.Core/epl/parse/ASTOutputLimitHelper.cs new file mode 100755 index 000000000..9f272ef1b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/parse/ASTOutputLimitHelper.cs @@ -0,0 +1,223 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using Antlr4.Runtime; +using Antlr4.Runtime.Tree; + +using com.espertech.esper.compat; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.time; +using com.espertech.esper.epl.generated; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.variable; +using com.espertech.esper.schedule; + +namespace com.espertech.esper.epl.parse +{ + /// + /// Builds an output limit spec from an output limit AST node. + /// + public class ASTOutputLimitHelper + { + /// + /// Build an output limit spec from the AST node supplied. + /// + /// The token stream. + /// The context. + /// is the map of current AST tree nodes to their respective expression root node + /// provides variable resolution + /// the engine uri + /// provides time + /// context for expression evaluatiom + /// + /// output limit spec + /// + public static OutputLimitSpec BuildOutputLimitSpec( + CommonTokenStream tokenStream, + EsperEPL2GrammarParser.OutputLimitContext ctx, + IDictionary astExprNodeMap, + VariableService variableService, + String engineURI, + TimeProvider timeProvider, + ExprEvaluatorContext exprEvaluatorContext) + { + var displayLimit = OutputLimitLimitType.DEFAULT; + if (ctx.k != null) + { + switch (ctx.k.Type) + { + case EsperEPL2GrammarParser.FIRST: displayLimit = OutputLimitLimitType.FIRST; break; + case EsperEPL2GrammarParser.LAST: displayLimit = OutputLimitLimitType.LAST; break; + case EsperEPL2GrammarParser.SNAPSHOT: displayLimit = OutputLimitLimitType.SNAPSHOT; break; + case EsperEPL2GrammarParser.ALL: displayLimit = OutputLimitLimitType.ALL; break; + default: throw ASTWalkException.From("Encountered unrecognized token " + ctx.k.Text, tokenStream, ctx); + } + } + + // next is a variable, or time period, or number + String variableName = null; + double? rate = null; + ExprNode whenExpression = null; + IList crontabScheduleSpec = null; + IList thenExpressions = null; + ExprTimePeriod timePeriodExpr = null; + OutputLimitRateType rateType; + ExprNode andAfterTerminateExpr = null; + IList andAfterTerminateSetExpressions = null; + + if (ctx.t != null) + { + rateType = OutputLimitRateType.TERM; + if (ctx.expression() != null) + { + andAfterTerminateExpr = ASTExprHelper.ExprCollectSubNodes(ctx.expression(), 0, astExprNodeMap)[0]; + } + if (ctx.onSetExpr() != null) + { + andAfterTerminateSetExpressions = ASTExprHelper.GetOnTriggerSetAssignments(ctx.onSetExpr().onSetAssignmentList(), astExprNodeMap); + } + } + else if (ctx.wh != null) + { + rateType = OutputLimitRateType.WHEN_EXPRESSION; + whenExpression = ASTExprHelper.ExprCollectSubNodes(ctx.expression(), 0, astExprNodeMap)[0]; + if (ctx.onSetExpr() != null) + { + thenExpressions = ASTExprHelper.GetOnTriggerSetAssignments(ctx.onSetExpr().onSetAssignmentList(), astExprNodeMap); + } + } + else if (ctx.at != null) + { + rateType = OutputLimitRateType.CRONTAB; + crontabScheduleSpec = ASTExprHelper.ExprCollectSubNodes(ctx.crontabLimitParameterSet(), 0, astExprNodeMap); + } + else + { + if (ctx.ev != null) + { + rateType = ctx.e != null ? OutputLimitRateType.EVENTS : OutputLimitRateType.TIME_PERIOD; + if (ctx.i != null) + { + variableName = ctx.i.Text; + } + else if (ctx.timePeriod() != null) + { + timePeriodExpr = (ExprTimePeriod)ASTExprHelper.ExprCollectSubNodes(ctx.timePeriod(), 0, astExprNodeMap)[0]; + } + else + { + ASTExprHelper.ExprCollectSubNodes(ctx.number(), 0, astExprNodeMap); // remove + rate = Double.Parse(ctx.number().GetText()); + } + } + else + { + rateType = OutputLimitRateType.AFTER; + } + } + + // get the AFTER time period + ExprTimePeriod afterTimePeriodExpr = null; + int? afterNumberOfEvents = null; + if (ctx.outputLimitAfter() != null) + { + if (ctx.outputLimitAfter().timePeriod() != null) + { + ExprNode expression = ASTExprHelper.ExprCollectSubNodes(ctx.outputLimitAfter(), 0, astExprNodeMap)[0]; + afterTimePeriodExpr = (ExprTimePeriod)expression; + } + else + { + Object constant = ASTConstantHelper.Parse(ctx.outputLimitAfter().number()); + afterNumberOfEvents = constant.AsInt(); + } + } + + bool andAfterTerminate = false; + if (ctx.outputLimitAndTerm() != null) + { + andAfterTerminate = true; + if (ctx.outputLimitAndTerm().expression() != null) + { + andAfterTerminateExpr = ASTExprHelper.ExprCollectSubNodes(ctx.outputLimitAndTerm().expression(), 0, astExprNodeMap)[0]; + } + if (ctx.outputLimitAndTerm().onSetExpr() != null) + { + andAfterTerminateSetExpressions = ASTExprHelper.GetOnTriggerSetAssignments(ctx.outputLimitAndTerm().onSetExpr().onSetAssignmentList(), astExprNodeMap); + } + } + + return new OutputLimitSpec(rate, variableName, rateType, displayLimit, whenExpression, thenExpressions, crontabScheduleSpec, timePeriodExpr, afterTimePeriodExpr, afterNumberOfEvents, andAfterTerminate, andAfterTerminateExpr, andAfterTerminateSetExpressions); + } + + /// + /// Builds a row limit specification. + /// + /// row limit spec + public static RowLimitSpec BuildRowLimitSpec(EsperEPL2GrammarParser.RowLimitContext ctx) + { + Object numRows; + Object offset; + if (ctx.o != null) + { // format "rows offset offsetcount" + numRows = ParseNumOrVariableIdent(ctx.n1, ctx.i1); + offset = ParseNumOrVariableIdent(ctx.n2, ctx.i2); + } + else if (ctx.c != null) + { // format "offsetcount, rows" + offset = ParseNumOrVariableIdent(ctx.n1, ctx.i1); + numRows = ParseNumOrVariableIdent(ctx.n2, ctx.i2); + } + else + { + numRows = ParseNumOrVariableIdent(ctx.n1, ctx.i1); + offset = null; + } + + int? numRowsInt = null; + String numRowsVariable = null; + if (numRows is String) + { + numRowsVariable = (String)numRows; + } + else + { + numRowsInt = (int?)numRows; + } + + int? offsetInt = null; + String offsetVariable = null; + if (offset is String) + { + offsetVariable = (String)offset; + } + else + { + offsetInt = (int?)offset; + } + + return new RowLimitSpec(numRowsInt, offsetInt, numRowsVariable, offsetVariable); + } + + private static Object ParseNumOrVariableIdent(EsperEPL2GrammarParser.NumberconstantContext num, IToken ident) + { + if (ident != null) + { + return ident.Text; + } + else + { + return ASTConstantHelper.Parse(num); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/parse/ASTSubstitutionHelper.cs b/NEsper.Core/NEsper.Core/epl/parse/ASTSubstitutionHelper.cs new file mode 100755 index 000000000..e16d3da96 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/parse/ASTSubstitutionHelper.cs @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.parse +{ + public class ASTSubstitutionHelper + { + public static void ValidateNewSubstitution(IList substitutionParamNodes, ExprSubstitutionNode substitutionNode) + { + if (substitutionParamNodes.IsEmpty()) { + return; + } + ExprSubstitutionNode first = substitutionParamNodes[0]; + if (substitutionNode.Index != null && first.Index == null) { + throw GetException(); + } + if (substitutionNode.Name != null && first.Name == null) { + throw GetException(); + } + } + + private static ASTWalkException GetException() + { + return ASTWalkException.From("Inconsistent use of substitution parameters, expecting all substitutions to either all provide a name or provide no name"); + } + } + +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/parse/ASTTableExprHelper.cs b/NEsper.Core/NEsper.Core/epl/parse/ASTTableExprHelper.cs new file mode 100755 index 000000000..1bf76730c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/parse/ASTTableExprHelper.cs @@ -0,0 +1,205 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.dot; +using com.espertech.esper.epl.expression.table; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.plugin; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.parse +{ + public class ASTTableExprHelper + { + public static void AddTableExpressionReference(StatementSpecRaw statementSpec, ExprTableAccessNode tableNode) + { + if (statementSpec.TableExpressions == null) + { + statementSpec.TableExpressions = new HashSet(); + } + statementSpec.TableExpressions.Add(tableNode); + } + + public static void AddTableExpressionReference( + StatementSpecRaw statementSpec, + ISet tableNodes) + { + if (tableNodes == null || tableNodes.IsEmpty()) + { + return; + } + if (statementSpec.TableExpressions == null) + { + statementSpec.TableExpressions = new HashSet(); + } + statementSpec.TableExpressions.AddAll(tableNodes); + } + + public static Pair CheckTableNameGetExprForSubproperty( + TableService tableService, + string tableName, + string subproperty) + { + var metadata = tableService.GetTableMetadata(tableName); + if (metadata == null) + { + return null; + } + + var index = ASTUtil.UnescapedIndexOfDot(subproperty); + if (index == -1) + { + if (metadata.KeyTypes.Length > 0) + { + return null; + } + var tableNodeX = new ExprTableAccessNodeSubprop(tableName, subproperty); + return new Pair(tableNodeX, null); + } + + // we have a nested subproperty such as "tablename.subproperty.abc" + var chainedSpecs = new List(1); + chainedSpecs.Add(new ExprChainedSpec(subproperty.Substring(index + 1), Collections.GetEmptyList(), true)); + var tableNode = new ExprTableAccessNodeSubprop(tableName, subproperty.Substring(0, index)); + var dotNode = new ExprDotNodeImpl(chainedSpecs, false, false); + dotNode.AddChildNode(tableNode); + return new Pair(tableNode, dotNode); + } + + /// + /// Resolve "table" and "table.property" when nested-property, not chainable + /// + /// tables + /// property name + /// table access node + public static ExprTableAccessNode CheckTableNameGetExprForProperty( + TableService tableService, + string propertyName) + { + // handle "var_name" alone, without chained, like an simple event property + var index = ASTUtil.UnescapedIndexOfDot(propertyName); + if (index == -1) + { + if (tableService.GetTableMetadata(propertyName) != null) + { + return new ExprTableAccessNodeTopLevel(propertyName); + } + return null; + } + + // handle "var_name.column", without chained, like a nested event property + var tableName = ASTUtil.UnescapeDot(propertyName.Substring(0, index)); + if (tableService.GetTableMetadata(tableName) == null) + { + return null; + } + + // it is a tables's subproperty + var sub = propertyName.Substring(index + 1); + return new ExprTableAccessNodeSubprop(tableName, sub); + } + + public static Pair> CheckTableNameGetLibFunc( + TableService tableService, + EngineImportService engineImportService, + LazyAllocatedMap plugInAggregations, + string engineURI, + string classIdent, + IList chain) + { + var index = ASTUtil.UnescapedIndexOfDot(classIdent); + + // handle special case "table.Keys()" function + if (index == -1) + { + if (tableService.GetTableMetadata(classIdent) == null) + { + return null; // not a table + } + var funcName = chain[1].Name; + if (funcName.ToLowerInvariant().Equals("keys")) + { + var subchain = chain.SubList(2, chain.Count); + var node = new ExprTableAccessNodeKeys(classIdent); + return new Pair>(node, subchain); + } + else + { + throw ASTWalkException.From( + "Invalid use of variable '" + classIdent + "', unrecognized use of function '" + funcName + + "', expected 'keys()'"); + } + } + + // Handle "table.property" (without the variable[...] syntax since this is ungrouped use) + var tableName = ASTUtil.UnescapeDot(classIdent.Substring(0, index)); + if (tableService.GetTableMetadata(tableName) == null) + { + return null; + } + + // this is a table access expression + var sub = classIdent.Substring(index + 1); + return HandleTable(engineImportService, plugInAggregations, engineURI, tableName, sub, chain); + } + + public static Pair> GetTableExprChainable( + EngineImportService engineImportService, + LazyAllocatedMap plugInAggregations, + string engineURI, + string tableName, + IList chain) + { + // handle just "variable[...].sub" + var subpropName = chain[0].Name; + if (chain.Count == 1) + { + chain.RemoveAt(0); + var tableNode = new ExprTableAccessNodeSubprop(tableName, subpropName); + return new Pair>(tableNode, chain); + } + + // we have a chain "variable[...].sub.xyz" + return HandleTable(engineImportService, plugInAggregations, engineURI, tableName, subpropName, chain); + } + + private static Pair> HandleTable( + EngineImportService engineImportService, + LazyAllocatedMap plugInAggregations, + string engineURI, + string tableName, + string sub, + IList chain) + { + ExprTableAccessNode node = new ExprTableAccessNodeSubprop(tableName, sub); + var subchain = chain.SubList(1, chain.Count); + + var candidateAccessor = subchain[0].Name; + var exprNode = (ExprAggregateNodeBase) ASTAggregationHelper.TryResolveAsAggregation( + engineImportService, false, candidateAccessor, plugInAggregations, engineURI); + if (exprNode != null) + { + node = new ExprTableAccessNodeSubpropAccessor(tableName, sub, exprNode); + exprNode.AddChildNodes(subchain[0].Parameters); + subchain.RemoveAt(0); + } + + return new Pair>(node, subchain); + + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/parse/ASTTableHelper.cs b/NEsper.Core/NEsper.Core/epl/parse/ASTTableHelper.cs new file mode 100755 index 000000000..2847cc6e6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/parse/ASTTableHelper.cs @@ -0,0 +1,92 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using Antlr4.Runtime.Tree; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.generated; +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.epl.parse +{ + public class ASTTableHelper + { + public static IList GetColumns(IList ctxs, IDictionary astExprNodeMap, EngineImportService engineImportService) + { + IList cols = new List(ctxs.Count); + foreach (EsperEPL2GrammarParser.CreateTableColumnContext colctx in ctxs) + { + cols.Add(GetColumn(colctx, astExprNodeMap, engineImportService)); + } + return cols; + } + + private static CreateTableColumn GetColumn(EsperEPL2GrammarParser.CreateTableColumnContext ctx, IDictionary astExprNodeMap, EngineImportService engineImportService) + { + string columnName = ctx.n.Text; + + ExprNode optExpression = null; + if (ctx.builtinFunc() != null || ctx.libFunction() != null) + { + optExpression = ASTExprHelper.ExprCollectSubNodes(ctx, 0, astExprNodeMap)[0]; + } + + string optTypeName = null; + bool? optTypeIsArray = null; + bool? optTypeIsPrimitiveArray = null; + if (ctx.createTableColumnPlain() != null) + { + EsperEPL2GrammarParser.CreateTableColumnPlainContext sub = ctx.createTableColumnPlain(); + optTypeName = ASTUtil.UnescapeClassIdent(sub.classIdentifier()); + optTypeIsArray = sub.b != null; + optTypeIsPrimitiveArray = ASTCreateSchemaHelper.ValidateIsPrimitiveArray(sub.p); + } + + bool primaryKey = false; + if (ctx.p != null) + { + if (ctx.p.Text.ToLowerInvariant() != "primary") + { + throw ASTWalkException.From("Invalid keyword '" + ctx.p.Text + "' encountered, expected 'primary key'"); + } + if (ctx.k.Text.ToLowerInvariant() != "key") + { + throw ASTWalkException.From("Invalid keyword '" + ctx.k.Text + "' encountered, expected 'primary key'"); + } + primaryKey = true; + } + + IList annots = Collections.GetEmptyList(); + if (ctx.annotationEnum() != null) + { + annots = new List(ctx.annotationEnum().Length); + foreach (EsperEPL2GrammarParser.AnnotationEnumContext anctx in ctx.annotationEnum()) + { + annots.Add(ASTAnnotationHelper.Walk(anctx, engineImportService)); + } + } + if (ctx.typeExpressionAnnotation() != null) + { + if (annots.IsEmpty()) + { + annots = new List(); + } + foreach (EsperEPL2GrammarParser.TypeExpressionAnnotationContext anno in ctx.typeExpressionAnnotation()) + { + annots.Add(new AnnotationDesc(anno.n.Text, anno.v.Text)); + } + } + + return new CreateTableColumn(columnName, optExpression, optTypeName, optTypeIsArray, optTypeIsPrimitiveArray, annots, primaryKey); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/parse/ASTTypeExpressionAnnoHelper.cs b/NEsper.Core/NEsper.Core/epl/parse/ASTTypeExpressionAnnoHelper.cs new file mode 100755 index 000000000..6aad307da --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/parse/ASTTypeExpressionAnnoHelper.cs @@ -0,0 +1,35 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using Antlr4.Runtime; + +using com.espertech.esper.epl.generated; + +namespace com.espertech.esper.epl.parse +{ + public class ASTTypeExpressionAnnoHelper + { + public static string ExpectMayTypeAnno( + EsperEPL2GrammarParser.TypeExpressionAnnotationContext ctx, + CommonTokenStream tokenStream) + { + if (ctx == null) + { + return null; + } + string annoName = ctx.n.Text; + if (!annoName.ToLowerInvariant().Equals("type")) + { + throw ASTWalkException.From( + "Invalid annotation for property selection, expected 'type' but found '" + annoName + "'", + tokenStream, ctx); + } + return ctx.v.Text; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/parse/ASTUtil.cs b/NEsper.Core/NEsper.Core/epl/parse/ASTUtil.cs new file mode 100755 index 000000000..14c29ad33 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/parse/ASTUtil.cs @@ -0,0 +1,400 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +using Antlr4.Runtime; +using Antlr4.Runtime.Tree; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.generated; + +namespace com.espertech.esper.epl.parse +{ + /// + /// Utility class for AST node handling. + /// + public class ASTUtil + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private const String PROPERTY_ENABLED_AST_DUMP = "ENABLE_AST_DUMP"; + + public static IList GetIdentList(EsperEPL2GrammarParser.ColumnListContext ctx) + { + if (ctx == null || (ctx.ChildCount == 0)) + { + return Collections.GetEmptyList(); + } + IList idents = ctx.IDENT(); + IList parameters = new List(idents.Count); + foreach (var ident in idents) + { + parameters.Add(ident.GetText()); + } + return parameters; + } + + public static bool IsTerminatedOfType(ITree child, int tokenType) + { + if (!(child is ITerminalNode)) + { + return false; + } + var termNode = (ITerminalNode)child; + return termNode.Symbol.Type == tokenType; + } + + public static int GetRuleIndexIfProvided(IParseTree tree) + { + if (!(tree is IRuleNode)) + { + return -1; + } + var ruleNode = (IRuleNode)tree; + return ruleNode.RuleContext.RuleIndex; + } + + public static int GetAssertTerminatedTokenType(IParseTree child) + { + if (!(child is ITerminalNode)) + { + throw ASTWalkException.From("Unexpected exception walking AST, expected terminal node", child.GetText()); + } + var term = (ITerminalNode)child; + return term.Symbol.Type; + } + + public static String PrintNode(ITree node) + { + var writer = new StringWriter(); + ASTUtil.DumpAST(writer, node, 0); + return writer.ToString(); + } + + public static bool IsRecursiveParentRule(RuleContext ctx, ICollection rulesIds) + { + var parent = ctx.Parent; + if (parent == null) + { + return false; + } + return rulesIds.Contains(parent.RuleIndex) || IsRecursiveParentRule(parent, rulesIds); + } + + /// Dump the AST node to system.out. + /// to dump + public static void DumpAST(ITree ast) + { + if (Environment.GetEnvironmentVariable(PROPERTY_ENABLED_AST_DUMP) != null) + { + var writer = new StringWriter(); + RenderNode(new char[0], ast, writer); + DumpAST(writer, ast, 2); + + Log.Info(".dumpAST ANTLR Tree dump follows...\n" + writer); + } + } + + public static void DumpAST(TextWriter printer, ITree ast, int ident) + { + var identChars = new char[ident]; + identChars.Fill(' '); + + if (ast == null) + { + RenderNode(identChars, null, printer); + return; + } + for (var i = 0; i < ast.ChildCount; i++) + { + var node = ast.GetChild(i); + if (node == null) + { + throw new NullReferenceException("Null AST node"); + } + RenderNode(identChars, node, printer); + DumpAST(printer, node, ident + 2); + } + } + + /// + /// Print the token stream to the logger. + /// + /// to print + public static void PrintTokens(CommonTokenStream tokens) + { + if (Log.IsDebugEnabled) + { + var tokenList = tokens.GetTokens(); + + var writer = new StringWriter(); + for (var i = 0; i < tokens.Size; i++) + { + var t = tokenList[i]; + var text = t.Text; + if (text.Trim().Length == 0) + { + writer.Write("'" + text + "'"); + } + else + { + writer.Write(text); + } + writer.Write('['); + writer.Write(t.Type); + writer.Write(']'); + writer.Write(" "); + } + writer.WriteLine(); + Log.Debug("Tokens: " + writer); + } + } + + private static void RenderNode(char[] ident, ITree node, TextWriter printer) + { + printer.Write(ident); + if (node == null) + { + printer.Write("NULL NODE"); + } + else + { + if (node is ParserRuleContext) + { + var ctx = (ParserRuleContext)node; + var ruleIndex = ctx.RuleIndex; + var ruleName = EsperEPL2GrammarParser.ruleNames[ruleIndex]; + printer.Write(ruleName); + } + else + { + var terminal = (ITerminalNode)node; + printer.Write(terminal.Symbol.Text); + printer.Write(" ["); + printer.Write(terminal.Symbol.Type); + printer.Write("]"); + } + + if (node is IParseTree) + { + var parseTree = (IParseTree)node; + var parseTreeText = parseTree.GetText(); + if (parseTreeText == null) + { + printer.Write(" (null value in text)"); + } + else if (parseTreeText.Contains("\\")) + { + var count = 0; + for (var i = 0; i < parseTreeText.Length; i++) + { + if (parseTreeText[i] == '\\') + { + count++; + } + } + printer.Write(" (" + count + " backlashes)"); + } + } + } + printer.WriteLine(); + } + + /// Escape all unescape dot characters in the text (identifier only) passed in. + /// text to escape + /// text where dots are escaped + public static String EscapeDot(String identifierToEscape) + { + var indexof = identifierToEscape.IndexOf("."); + if (indexof == -1) + { + return identifierToEscape; + } + + var builder = new StringBuilder(); + for (var i = 0; i < identifierToEscape.Length; i++) + { + var c = identifierToEscape[i]; + if (c != '.') + { + builder.Append(c); + continue; + } + + if (i > 0) + { + if (identifierToEscape[i - 1] == '\\') + { + builder.Append('.'); + continue; + } + } + + builder.Append('\\'); + builder.Append('.'); + } + + return builder.ToString(); + } + + /// Find the index of an unescaped dot (.) character, or return -1 if none found. + /// text to find an un-escaped dot character + /// index of first unescaped dot + public static int UnescapedIndexOfDot(String identifier) + { + var indexof = identifier.IndexOf("."); + if (indexof == -1) + { + return -1; + } + + for (var i = 0; i < identifier.Length; i++) + { + var c = identifier[i]; + if (c != '.') + { + continue; + } + + if (i > 0) + { + if (identifier[i - 1] == '\\') + { + continue; + } + } + + return i; + } + + return -1; + } + + /// Un-Escape all escaped dot characters in the text (identifier only) passed in. + /// text to un-escape + /// string + public static String UnescapeDot(String identifierToUnescape) + { + var indexof = identifierToUnescape.IndexOf("."); + if (indexof == -1) + { + return identifierToUnescape; + } + indexof = identifierToUnescape.IndexOf("\\"); + if (indexof == -1) + { + return identifierToUnescape; + } + + var builder = new StringBuilder(); + var index = -1; + var max = identifierToUnescape.Length - 1; + do + { + index++; + var c = identifierToUnescape[index]; + if (c != '\\') + { + builder.Append(c); + continue; + } + if (index < identifierToUnescape.Length - 1) + { + if (identifierToUnescape[index + 1] == '.') + { + builder.Append('.'); + index++; + } + } + } + while (index < max); + + return builder.ToString(); + } + + public static String GetPropertyName(EsperEPL2GrammarParser.EventPropertyContext ctx, int startNode) + { + var buf = new StringBuilder(); + for (var i = startNode; i < ctx.ChildCount; i++) + { + var tree = ctx.GetChild(i); + buf.Append(tree.GetText()); + } + return buf.ToString(); + } + + public static String UnescapeBacktick(String text) + { + var indexof = text.IndexOf("`"); + if (indexof == -1) + { + return text; + } + + var builder = new StringBuilder(); + var index = -1; + var max = text.Length - 1; + var skip = false; + do + { + index++; + var c = text[index]; + if (c == '`') + { + skip = !skip; + } + else + { + builder.Append(c); + } + } + while (index < max); + + return builder.ToString(); + } + + public static String UnescapeClassIdent(EsperEPL2GrammarParser.ClassIdentifierContext classIdentCtx) + { + return UnescapeEscapableStr(classIdentCtx.escapableStr(), "."); + } + + public static String UnescapeSlashIdentifier(EsperEPL2GrammarParser.SlashIdentifierContext ctx) + { + String name = UnescapeEscapableStr(ctx.escapableStr(), "/"); + if (ctx.d != null) + { + name = "/" + name; + } + return name; + } + + private static String UnescapeEscapableStr(IList ctxs, String delimiterConst) + { + if (ctxs.Count == 1) + { + return UnescapeBacktick(UnescapeDot(ctxs[0].GetText())); + } + + var writer = new StringWriter(); + var delimiter = ""; + foreach (var ctx in ctxs) + { + writer.Write(delimiter); + writer.Write(UnescapeBacktick(UnescapeDot(ctx.GetText()))); + delimiter = delimiterConst; + } + + return writer.ToString(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/parse/ASTWalkException.cs b/NEsper.Core/NEsper.Core/epl/parse/ASTWalkException.cs new file mode 100755 index 000000000..418cacb36 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/parse/ASTWalkException.cs @@ -0,0 +1,56 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using Antlr4.Runtime; + +namespace com.espertech.esper.epl.parse +{ + /// + /// This exception is thrown to indicate a problem in statement creation. + /// + [Serializable] + public class ASTWalkException : Exception + { + public static ASTWalkException From(String message, Exception ex) { + return new ASTWalkException(message, ex); + } + + public static ASTWalkException From(String message) { + return new ASTWalkException(message); + } + + public static ASTWalkException From(String message, String parseTreeTextMayHaveNoWhitespace) { + return new ASTWalkException(message + " in text '" + parseTreeTextMayHaveNoWhitespace + "'"); + } + + public static ASTWalkException From(String message, CommonTokenStream tokenStream, RuleContext parseTree) { + return new ASTWalkException(message + " in text '" + tokenStream.GetText(parseTree) + "'"); + } + + public static ASTWalkException From(String message, IToken token) { + return new ASTWalkException(message + " in text '" + token.Text + "'"); + } + + /// + /// Ctor. + /// + /// is the error message + private ASTWalkException(String message) + : base(message) + { + } + + public ASTWalkException(String message, Exception cause) + : base(message, cause) + { + } + } + +} diff --git a/NEsper.Core/NEsper.Core/epl/parse/Antlr4ErrorListener.cs b/NEsper.Core/NEsper.Core/epl/parse/Antlr4ErrorListener.cs new file mode 100755 index 000000000..a0f18df23 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/parse/Antlr4ErrorListener.cs @@ -0,0 +1,88 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.IO; +using System.Reflection; + +using Antlr4.Runtime; +using Antlr4.Runtime.Atn; +using Antlr4.Runtime.Dfa; +using Antlr4.Runtime.Sharpen; + +using com.espertech.esper.compat.logging; + +namespace com.espertech.esper.epl.parse +{ + public class Antlr4ErrorListener : IAntlrErrorListener + { + public static readonly Antlr4ErrorListener INSTANCE = new Antlr4ErrorListener(); + + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private Antlr4ErrorListener() + { + } + + public void SyntaxError( + IRecognizer recognizer, + T offendingSymbol, + int line, + int charPositionInLine, + string msg, + RecognitionException e) + { + throw e; + } + + public void SyntaxError( + TextWriter output, + IRecognizer recognizer, + T offendingSymbol, + int line, + int charPositionInLine, + string msg, + RecognitionException e) + { + throw e; + } + + public void ReportAmbiguity( + Parser recognizer, + DFA dfa, + int startIndex, + int stopIndex, + bool exact, + BitSet ambigAlts, + ATNConfigSet configs) + { + Log.Debug("reportAmbiguity"); + } + + public void ReportAttemptingFullContext( + Parser recognizer, + DFA dfa, + int startIndex, + int stopIndex, + BitSet conflictingAlts, + ATNConfigSet configs) + { + Log.Debug("reportAttemptingFullContext"); + } + + public void ReportContextSensitivity( + Parser recognizer, + DFA dfa, + int startIndex, + int stopIndex, + int prediction, + ATNConfigSet configs) + { + Log.Debug("reportContextSensitivity"); + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/parse/Antlr4ErrorStrategy.cs b/NEsper.Core/NEsper.Core/epl/parse/Antlr4ErrorStrategy.cs new file mode 100755 index 000000000..717315409 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/parse/Antlr4ErrorStrategy.cs @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using Antlr4.Runtime; + +namespace com.espertech.esper.epl.parse +{ + public class Antlr4ErrorStrategy : BailErrorStrategy + { + public override void ReportError(Parser recognizer, RecognitionException e) + { + // Antlr has an issue handling LexerNoViableAltException as then offending token can be null + // Try: "select a.b('aa\") from A" + if (e is LexerNoViableAltException && e.OffendingToken == null) + { + return; + } + base.ReportError(recognizer, e); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/parse/EPLTreeWalkerListener.cs b/NEsper.Core/NEsper.Core/epl/parse/EPLTreeWalkerListener.cs new file mode 100755 index 000000000..168561ed9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/parse/EPLTreeWalkerListener.cs @@ -0,0 +1,3280 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Linq; +using System.Reflection; +using System.Collections.Generic; + +using Antlr4.Runtime; +using Antlr4.Runtime.Tree; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.mgr; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.db; +using com.espertech.esper.epl.declexpr; +using com.espertech.esper.epl.expression.accessagg; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.dot; +using com.espertech.esper.epl.expression.funcs; +using com.espertech.esper.epl.expression.methodagg; +using com.espertech.esper.epl.expression.ops; +using com.espertech.esper.epl.expression.prev; +using com.espertech.esper.epl.expression.prior; +using com.espertech.esper.epl.expression.subquery; +using com.espertech.esper.epl.expression.table; +using com.espertech.esper.epl.expression.time; +using com.espertech.esper.epl.generated; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.epl.variable; +using com.espertech.esper.events.property; +using com.espertech.esper.pattern; +using com.espertech.esper.pattern.guard; +using com.espertech.esper.plugin; +using com.espertech.esper.rowregex; +using com.espertech.esper.schedule; +using com.espertech.esper.type; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.parse +{ + /// + /// Called during the walks of a EPL expression AST tree as specified in the grammar file. + /// Constructs filter and view specifications etc. + /// + public class EPLTreeWalkerListener : IEsperEPL2GrammarListener + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private static readonly ISet EVENT_FILTER_WALK_EXCEPTIONS_RECURSIVE = new HashSet(); + private static readonly ISet WHERE_CLAUSE_WALK_EXCEPTIONS_RECURSIVE = new HashSet(); + private static readonly ISet EVENT_PROPERTY_WALK_EXCEPTIONS_PARENT = new HashSet(); + private static readonly ISet SELECT_EXPRELE_WALK_EXCEPTIONS_RECURSIVE = new HashSet(); + + static EPLTreeWalkerListener() + { + EVENT_FILTER_WALK_EXCEPTIONS_RECURSIVE.Add(EsperEPL2GrammarParser.RULE_createContextDetail); + EVENT_FILTER_WALK_EXCEPTIONS_RECURSIVE.Add(EsperEPL2GrammarParser.RULE_createContextFilter); + EVENT_FILTER_WALK_EXCEPTIONS_RECURSIVE.Add(EsperEPL2GrammarParser.RULE_createContextPartitionItem); + EVENT_FILTER_WALK_EXCEPTIONS_RECURSIVE.Add(EsperEPL2GrammarParser.RULE_createContextCoalesceItem); + + WHERE_CLAUSE_WALK_EXCEPTIONS_RECURSIVE.Add(EsperEPL2GrammarParser.RULE_patternExpression); + WHERE_CLAUSE_WALK_EXCEPTIONS_RECURSIVE.Add(EsperEPL2GrammarParser.RULE_mergeMatchedItem); + WHERE_CLAUSE_WALK_EXCEPTIONS_RECURSIVE.Add(EsperEPL2GrammarParser.RULE_mergeInsert); + WHERE_CLAUSE_WALK_EXCEPTIONS_RECURSIVE.Add(EsperEPL2GrammarParser.RULE_updateDetails); + WHERE_CLAUSE_WALK_EXCEPTIONS_RECURSIVE.Add(EsperEPL2GrammarParser.RULE_onSetExpr); + WHERE_CLAUSE_WALK_EXCEPTIONS_RECURSIVE.Add(EsperEPL2GrammarParser.RULE_onUpdateExpr); + + EVENT_PROPERTY_WALK_EXCEPTIONS_PARENT.Add(EsperEPL2GrammarParser.RULE_newAssign); + EVENT_PROPERTY_WALK_EXCEPTIONS_PARENT.Add(EsperEPL2GrammarParser.RULE_createContextPartitionItem); + EVENT_PROPERTY_WALK_EXCEPTIONS_PARENT.Add(EsperEPL2GrammarParser.RULE_createContextDetail); + EVENT_PROPERTY_WALK_EXCEPTIONS_PARENT.Add(EsperEPL2GrammarParser.RULE_createContextFilter); + EVENT_PROPERTY_WALK_EXCEPTIONS_PARENT.Add(EsperEPL2GrammarParser.RULE_createContextCoalesceItem); + + SELECT_EXPRELE_WALK_EXCEPTIONS_RECURSIVE.Add(EsperEPL2GrammarParser.RULE_mergeInsert); + } + + private readonly Stack> _astExprNodeMapStack; + private readonly IDictionary _astPatternNodeMap = new LinkedHashMap(); + private readonly IDictionary _astRowRegexNodeMap = new HashMap(); + private readonly IDictionary _astGopNodeMap = new HashMap(); + private readonly IDictionary _astStatementSpecMap = new HashMap(); + private readonly IList _viewSpecs = new List(); + private readonly Stack _statementSpecStack; + private readonly CommonTokenStream _tokenStream; + private readonly EngineImportService _engineImportService; + private readonly VariableService _variableService; + private readonly TimeProvider _timeProvider; + private readonly ExprEvaluatorContext _exprEvaluatorContext; + private readonly SelectClauseStreamSelectorEnum _defaultStreamSelector; + private readonly string _engineURI; + private readonly ConfigurationInformation _configurationInformation; + private readonly SchedulingService _schedulingService; + private readonly PatternNodeFactory _patternNodeFactory; + private readonly ContextManagementService _contextManagementService; + private readonly IList _scriptBodies; + private readonly ExprDeclaredService _exprDeclaredService; + private readonly IList _scriptExpressions; + private readonly ExpressionDeclDesc _expressionDeclarations; + private readonly TableService _tableService; + // private holding areas for accumulated INFO + private IDictionary _astExprNodeMap = new LinkedHashMap(); + private IDictionary _onTriggerSplitPropertyEvals; + private readonly LazyAllocatedMap _plugInAggregations = new LazyAllocatedMap(); + private FilterSpecRaw _filterSpec; + // AST Walk result + private readonly IList _substitutionParamNodes = new List(); + private StatementSpecRaw _statementSpec; + private IList _propertySelectRaw; + private PropertyEvalSpec _propertyEvalSpec; + private IList _mergeMatcheds; + private IList _mergeActions; + private ContextDescriptor _contextDescriptor; + + public EPLTreeWalkerListener( + CommonTokenStream tokenStream, + EngineImportService engineImportService, + VariableService variableService, + SchedulingService schedulingService, + SelectClauseStreamSelectorEnum? defaultStreamSelector, + string engineURI, + ConfigurationInformation configurationInformation, + PatternNodeFactory patternNodeFactory, + ContextManagementService contextManagementService, + IList scriptBodies, + ExprDeclaredService exprDeclaredService, + TableService tableService) + { + _tokenStream = tokenStream; + _engineImportService = engineImportService; + _variableService = variableService; + _timeProvider = schedulingService; + _patternNodeFactory = patternNodeFactory; + _exprEvaluatorContext = new ExprEvaluatorContextTimeOnly(_timeProvider); + _engineURI = engineURI; + _configurationInformation = configurationInformation; + _schedulingService = schedulingService; + _contextManagementService = contextManagementService; + _scriptBodies = scriptBodies; + _exprDeclaredService = exprDeclaredService; + _tableService = tableService; + + if (defaultStreamSelector == null) { + throw ASTWalkException.From("Default stream selector is null"); + } + + _defaultStreamSelector = defaultStreamSelector.Value; + _statementSpec = new StatementSpecRaw(defaultStreamSelector.Value); + _statementSpecStack = new Stack(); + _astExprNodeMapStack = new Stack>(); + + // statement-global items + _expressionDeclarations = new ExpressionDeclDesc(); + _statementSpec.ExpressionDeclDesc = _expressionDeclarations; + _scriptExpressions = new List(); + _statementSpec.ScriptExpressions = _scriptExpressions; + } + + /// + /// Pushes a statement into the stack, creating a new empty statement to fill in. + /// The leave node method for lookup statements pops from the stack. + /// The leave node method for lookup statements pops from the stack. + /// + private void PushStatementContext() { + _statementSpecStack.Push(_statementSpec); + _astExprNodeMapStack.Push(_astExprNodeMap); + + _statementSpec = new StatementSpecRaw(_defaultStreamSelector); + _astExprNodeMap = new HashMap(); + } + + private void PopStatementContext(IParseTree ctx) { + var currentSpec = _statementSpec; + _statementSpec = _statementSpecStack.Pop(); + if (currentSpec.HasVariables) { + _statementSpec.HasVariables = true; + } + ASTTableExprHelper.AddTableExpressionReference(_statementSpec, currentSpec.TableExpressions); + if (currentSpec.ReferencedVariables != null) { + foreach (var var in currentSpec.ReferencedVariables) { + ASTExprHelper.AddVariableReference(_statementSpec, var); + } + } + _astExprNodeMap = _astExprNodeMapStack.Pop(); + _astStatementSpecMap.Put(ctx, currentSpec); + } + + public StatementSpecRaw StatementSpec + { + get { return _statementSpec; } + } + + public void ExitContextExpr(EsperEPL2GrammarParser.ContextExprContext ctx) { + var contextName = ctx.i.Text; + _statementSpec.OptionalContextName = contextName; + _contextDescriptor = _contextManagementService.GetContextDescriptor(contextName); + } + + public void ExitEvalRelationalExpression(EsperEPL2GrammarParser.EvalRelationalExpressionContext ctx) { + if (ctx.ChildCount < 2) { + return; + } + var isNot = ctx.n != null; + ExprNode exprNode; + if (ctx.like != null) { + exprNode = new ExprLikeNode(isNot); + } else if (ctx.@in != null && ctx.col != null) { // range + var isLowInclude = ctx.LBRACK() != null; + var isHighInclude = ctx.RBRACK() != null; + exprNode = new ExprBetweenNodeImpl(isLowInclude, isHighInclude, isNot); + } else if (ctx.@in != null) { + exprNode = new ExprInNodeImpl(isNot); + } else if (ctx.inSubSelectQuery() != null) { + var currentSpec = _astStatementSpecMap.Delete(ctx.inSubSelectQuery().subQueryExpr()); + exprNode = new ExprSubselectInNode(currentSpec, isNot); + } else if (ctx.between != null) { + exprNode = new ExprBetweenNodeImpl(true, true, isNot); + } else if (ctx.regex != null) { + exprNode = new ExprRegexpNode(isNot); + } else if (ctx.r != null) { + RelationalOpEnum relationalOpEnum; + switch (ctx.r.Type) { + case EsperEPL2GrammarLexer.LT: + relationalOpEnum = RelationalOpEnum.LT; + break; + case EsperEPL2GrammarLexer.GT: + relationalOpEnum = RelationalOpEnum.GT; + break; + case EsperEPL2GrammarLexer.LE: + relationalOpEnum = RelationalOpEnum.LE; + break; + case EsperEPL2GrammarLexer.GE: + relationalOpEnum = RelationalOpEnum.GE; + break; + default: + throw ASTWalkException.From("Encountered unrecognized node type " + ctx.r.Type, _tokenStream, ctx); + } + + var isAll = ctx.g != null && ctx.g.Type == EsperEPL2GrammarLexer.ALL; + var isAny = ctx.g != null && (ctx.g.Type == EsperEPL2GrammarLexer.ANY || ctx.g.Type == EsperEPL2GrammarLexer.SOME); + + if (isAll || isAny) { + if (ctx.subSelectGroupExpression() != null && !ctx.subSelectGroupExpression().IsEmpty()) { + StatementSpecRaw currentSpec = _astStatementSpecMap.Delete(ctx.subSelectGroupExpression()[0].subQueryExpr()); + exprNode = new ExprSubselectAllSomeAnyNode(currentSpec, false, isAll, relationalOpEnum); + } else { + exprNode = new ExprRelationalOpAllAnyNode(relationalOpEnum, isAll); + } + } else { + exprNode = new ExprRelationalOpNodeImpl(relationalOpEnum); + } + } else { + throw ASTWalkException.From("Encountered unrecognized relational op", _tokenStream, ctx); + } + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(exprNode, ctx, _astExprNodeMap); + if (ctx.like != null && ctx.stringconstant() != null) { + exprNode.AddChildNode(new ExprConstantNodeImpl(ASTConstantHelper.Parse(ctx.stringconstant()))); + } + } + + public void ExitLibFunction(EsperEPL2GrammarParser.LibFunctionContext ctx) { + ASTLibFunctionHelper.HandleLibFunc(_tokenStream, ctx, _configurationInformation, _engineImportService, _astExprNodeMap, _plugInAggregations, _engineURI, _expressionDeclarations, _exprDeclaredService, _scriptExpressions, _contextDescriptor, _tableService, _statementSpec, _variableService); + } + + public void ExitMatchRecog(EsperEPL2GrammarParser.MatchRecogContext ctx) { + var allMatches = ctx.matchRecogMatchesSelection() != null && ctx.matchRecogMatchesSelection().ALL() != null; + if (ctx.matchRecogMatchesAfterSkip() != null) { + var skip = ASTMatchRecognizeHelper.ParseSkip(_tokenStream, ctx.matchRecogMatchesAfterSkip()); + _statementSpec.MatchRecognizeSpec.Skip.Skip = skip; + } + + if (ctx.matchRecogMatchesInterval() != null) { + if (!ctx.matchRecogMatchesInterval().i.Text.ToLowerInvariant().Equals("interval")) { + throw ASTWalkException.From("Invalid interval-clause within match-recognize, expecting keyword INTERVAL", _tokenStream, ctx.matchRecogMatchesInterval()); + } + var expression = ASTExprHelper.ExprCollectSubNodes(ctx.matchRecogMatchesInterval().timePeriod(), 0, _astExprNodeMap)[0]; + var timePeriodExpr = (ExprTimePeriod) expression; + var orTerminated = ctx.matchRecogMatchesInterval().TERMINATED() != null; + _statementSpec.MatchRecognizeSpec.Interval = new MatchRecognizeInterval(timePeriodExpr, orTerminated); + } + + _statementSpec.MatchRecognizeSpec.IsAllMatches = allMatches; + } + + public void ExitMatchRecogPartitionBy(EsperEPL2GrammarParser.MatchRecogPartitionByContext ctx) { + if (_statementSpec.MatchRecognizeSpec == null) { + _statementSpec.MatchRecognizeSpec = new MatchRecognizeSpec(); + } + var nodes = ASTExprHelper.ExprCollectSubNodes(ctx, 0, _astExprNodeMap); + _statementSpec.MatchRecognizeSpec.PartitionByExpressions.AddAll(nodes); + } + + public void ExitMergeMatchedItem(EsperEPL2GrammarParser.MergeMatchedItemContext ctx) { + if (_mergeActions == null) { + _mergeActions = new List(); + } + ExprNode whereCond = null; + if (ctx.whereClause() != null) { + whereCond = ASTExprHelper.ExprCollectSubNodes(ctx.whereClause(), 0, _astExprNodeMap)[0]; + } + if (ctx.d != null) { + _mergeActions.Add(new OnTriggerMergeActionDelete(whereCond)); + } + if (ctx.u != null) { + var sets = ASTExprHelper.GetOnTriggerSetAssignments(ctx.onSetAssignmentList(), _astExprNodeMap); + _mergeActions.Add(new OnTriggerMergeActionUpdate(whereCond, sets)); + } + if (ctx.mergeInsert() != null) { + HandleMergeInsert(ctx.mergeInsert()); + } + } + + public void EnterSubQueryExpr(EsperEPL2GrammarParser.SubQueryExprContext ctx) { + PushStatementContext(); + } + + public void ExitSubQueryExpr(EsperEPL2GrammarParser.SubQueryExprContext ctx) { + PopStatementContext(ctx); + } + + public void ExitMatchRecogDefineItem(EsperEPL2GrammarParser.MatchRecogDefineItemContext ctx) { + var first = ctx.i.Text; + var exprNode = ASTExprHelper.ExprCollectSubNodes(ctx, 0, _astExprNodeMap)[0]; + _statementSpec.MatchRecognizeSpec.Defines.Add(new MatchRecognizeDefineItem(first, exprNode)); + } + + public void ExitMergeUnmatchedItem(EsperEPL2GrammarParser.MergeUnmatchedItemContext ctx) { + if (_mergeActions == null) { + _mergeActions = new List(); + } + HandleMergeInsert(ctx.mergeInsert()); + } + + public void ExitHavingClause(EsperEPL2GrammarParser.HavingClauseContext ctx) { + if (_astExprNodeMap.Count != 1) { + throw new IllegalStateException("Having clause generated zero or more then one expression nodes"); + } + _statementSpec.HavingExprRootNode = ASTExprHelper.ExprCollectSubNodes(ctx, 0, _astExprNodeMap)[0]; + _astExprNodeMap.Clear(); + } + + public void ExitMatchRecogMeasureItem(EsperEPL2GrammarParser.MatchRecogMeasureItemContext ctx) { + if (_statementSpec.MatchRecognizeSpec == null) { + _statementSpec.MatchRecognizeSpec = new MatchRecognizeSpec(); + } + var exprNode = ASTExprHelper.ExprCollectSubNodes(ctx, 0, _astExprNodeMap)[0]; + var name = ctx.i != null ? ctx.i.Text : null; + _statementSpec.MatchRecognizeSpec.AddMeasureItem(new MatchRecognizeMeasureItem(exprNode, name)); + } + + public void ExitObserverExpression(EsperEPL2GrammarParser.ObserverExpressionContext ctx) { + var objectNamespace = ctx.ns.Text; + var objectName = ctx.a != null ? ctx.a.Text : ctx.nm.Text; + var obsParameters = ASTExprHelper.ExprCollectSubNodes(ctx, 2, _astExprNodeMap); + + var observerSpec = new PatternObserverSpec(objectNamespace, objectName, obsParameters); + var observerNode = _patternNodeFactory.MakeObserverNode(observerSpec); + ASTExprHelper.PatternCollectAddSubnodesAddParentNode(observerNode, ctx, _astPatternNodeMap); + } + + public void ExitMatchRecogPatternNested(EsperEPL2GrammarParser.MatchRecogPatternNestedContext ctx) { + if (ctx.ChildCount < 2) { + return; + } + var type = RegexNFATypeEnum.SINGLE; + if (ctx.s != null) { + type = RegexNFATypeEnumExtensions.FromString(ctx.s.Text, null); + } + var repeat = ASTMatchRecognizeHelper.walkOptionalRepeat(ctx.matchRecogPatternRepeat(), _astExprNodeMap); + var nestedNode = new RowRegexExprNodeNested(type, repeat); + ASTExprHelper.RegExCollectAddSubNodesAddParentNode(nestedNode, ctx, _astRowRegexNodeMap); + } + + public void ExitMatchRecogPatternPermute(EsperEPL2GrammarParser.MatchRecogPatternPermuteContext ctx) { + var permuteNode = new RowRegexExprNodePermute(); + ASTExprHelper.RegExCollectAddSubNodesAddParentNode(permuteNode, ctx, _astRowRegexNodeMap); + } + + public void ExitEvalOrExpression(EsperEPL2GrammarParser.EvalOrExpressionContext ctx) { + if (ctx.ChildCount < 2) { + return; + } + var or = new ExprOrNode(); + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(or, ctx, _astExprNodeMap); + } + + public void ExitTimePeriod(EsperEPL2GrammarParser.TimePeriodContext ctx) { + var timeNode = ASTExprHelper.TimePeriodGetExprAllParams(ctx, _astExprNodeMap, _variableService, _statementSpec, _configurationInformation, _engineImportService.TimeAbacus); + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(timeNode, ctx, _astExprNodeMap); + } + + public void ExitSelectionListElementExpr(EsperEPL2GrammarParser.SelectionListElementExprContext ctx) { + ExprNode exprNode; + if (ASTUtil.IsRecursiveParentRule(ctx, SELECT_EXPRELE_WALK_EXCEPTIONS_RECURSIVE)) { + exprNode = ASTExprHelper.ExprCollectSubNodes(ctx, 0, _astExprNodeMap)[0]; + } else { + if ((_astExprNodeMap.Count > 1) || ((_astExprNodeMap.IsEmpty()))) { + throw ASTWalkException.From("Unexpected AST tree contains zero or more then 1 child element for root", _tokenStream, ctx); + } + exprNode = _astExprNodeMap.Values.First(); + _astExprNodeMap.Clear(); + } + + // Get list element name + string optionalName = null; + if (ctx.keywordAllowedIdent() != null) { + optionalName = ctx.keywordAllowedIdent().GetText(); + } + + var eventsAnnotation = false; + if (ctx.selectionListElementAnno() != null) { + var annotation = ctx.selectionListElementAnno().i.Text.ToLowerInvariant(); + if (annotation.Equals("eventbean") || annotation.Equals("eventbean")) { + eventsAnnotation = true; + } else { + throw ASTWalkException.From("Failed to recognize select-expression annotation '" + annotation + "', expected 'eventbean'", _tokenStream, ctx); + } + } + + // Add as selection element + _statementSpec.SelectClauseSpec.Add(new SelectClauseExprRawSpec(exprNode, optionalName, eventsAnnotation)); + } + + public void ExitEventFilterExpression(EsperEPL2GrammarParser.EventFilterExpressionContext ctx) { + if (ASTUtil.IsRecursiveParentRule(ctx, EVENT_FILTER_WALK_EXCEPTIONS_RECURSIVE)) { + return; + } + + // for event streams we keep the filter spec around for use when the stream definition is completed + _filterSpec = ASTFilterSpecHelper.WalkFilterSpec(ctx, _propertyEvalSpec, _astExprNodeMap); + + // set property eval to null + _propertyEvalSpec = null; + + _astExprNodeMap.Clear(); + } + + public void ExitMatchRecogPatternConcat(EsperEPL2GrammarParser.MatchRecogPatternConcatContext ctx) { + if (ctx.ChildCount < 2) { + return; + } + var concatNode = new RowRegexExprNodeConcatenation(); + ASTExprHelper.RegExCollectAddSubNodesAddParentNode(concatNode, ctx, _astRowRegexNodeMap); + } + + public void ExitNumberconstant(EsperEPL2GrammarParser.NumberconstantContext ctx) { + // if the parent is constant, don't need an expression + if (ctx.Parent.RuleIndex == EsperEPL2GrammarParser.RULE_constant) { + return; + } + var constantNode = new ExprConstantNodeImpl(ASTConstantHelper.Parse(ctx)); + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(constantNode, ctx, _astExprNodeMap); + } + + public void ExitMatchRecogPattern(EsperEPL2GrammarParser.MatchRecogPatternContext ctx) { + var exprNode = ASTExprHelper.RegExGetRemoveTopNode(ctx, _astRowRegexNodeMap); + if (exprNode == null) { + throw new IllegalStateException("Expression node for AST node not found"); + } + _statementSpec.MatchRecognizeSpec.Pattern = exprNode; + } + + public void ExitWhereClause(EsperEPL2GrammarParser.WhereClauseContext ctx) { + if (ctx.Parent.RuleIndex != EsperEPL2GrammarParser.RULE_subQueryExpr && + ASTUtil.IsRecursiveParentRule(ctx, WHERE_CLAUSE_WALK_EXCEPTIONS_RECURSIVE)) { // ignore pattern + return; + } + if (_astExprNodeMap.Count != 1) { + throw new IllegalStateException("Where clause generated zero or more then one expression nodes"); + } + + // Just assign the single root ExprNode not consumed yet + _statementSpec.FilterExprRootNode = _astExprNodeMap.Values.First(); + _astExprNodeMap.Clear(); + } + + public void ExitMatchRecogPatternAtom(EsperEPL2GrammarParser.MatchRecogPatternAtomContext ctx) { + var first = ctx.i.Text; + var type = RegexNFATypeEnum.SINGLE; + if (ctx.reluctant != null && ctx.s != null) { + type = RegexNFATypeEnumExtensions.FromString(ctx.s.Text, ctx.reluctant.Text); + } else if (ctx.s != null) { + type = RegexNFATypeEnumExtensions.FromString(ctx.s.Text, null); + } + + var repeat = ASTMatchRecognizeHelper.walkOptionalRepeat(ctx.matchRecogPatternRepeat(), _astExprNodeMap); + var item = new RowRegexExprNodeAtom(first, type, repeat); + ASTExprHelper.RegExCollectAddSubNodesAddParentNode(item, ctx, _astRowRegexNodeMap); + } + + public void ExitUpdateExpr(EsperEPL2GrammarParser.UpdateExprContext ctx) { + var updctx = ctx.updateDetails(); + var eventTypeName = ASTUtil.UnescapeClassIdent(updctx.classIdentifier()); + var streamSpec = new FilterStreamSpecRaw(new FilterSpecRaw(eventTypeName, Collections.GetEmptyList(), null), ViewSpec.EMPTY_VIEWSPEC_ARRAY, eventTypeName, StreamSpecOptions.DEFAULT); + _statementSpec.StreamSpecs.Add(streamSpec); + var optionalStreamName = updctx.i != null ? updctx.i.Text : null; + var assignments = ASTExprHelper.GetOnTriggerSetAssignments(updctx.onSetAssignmentList(), _astExprNodeMap); + var whereClause = updctx.WHERE() != null ? ASTExprHelper.ExprCollectSubNodes(updctx.whereClause(), 0, _astExprNodeMap)[0] : null; + _statementSpec.UpdateDesc = new UpdateDesc(optionalStreamName, assignments, whereClause); + } + + public void ExitFrequencyOperand(EsperEPL2GrammarParser.FrequencyOperandContext ctx) { + var exprNode = new ExprNumberSetFrequency(); + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(exprNode, ctx, _astExprNodeMap); + ASTExprHelper.AddOptionalNumber(exprNode, ctx.number()); + ASTExprHelper.AddOptionalSimpleProperty(exprNode, ctx.i, _variableService, _statementSpec); + } + + public void ExitCreateDataflow(EsperEPL2GrammarParser.CreateDataflowContext ctx) { + var graphDesc = ASTGraphHelper.WalkCreateDataFlow(ctx, _astGopNodeMap, _engineImportService); + _statementSpec.CreateDataFlowDesc = graphDesc; + } + + public void ExitInsertIntoExpr(EsperEPL2GrammarParser.InsertIntoExprContext ctx) { + var selector = SelectClauseStreamSelectorEnum.ISTREAM_ONLY; + if (ctx.r != null) { + selector = SelectClauseStreamSelectorEnum.RSTREAM_ONLY; + } else if (ctx.ir != null) { + selector = SelectClauseStreamSelectorEnum.RSTREAM_ISTREAM_BOTH; + } + + // type name + var eventTypeName = ASTUtil.UnescapeClassIdent(ctx.classIdentifier()); + var insertIntoDesc = new InsertIntoDesc(selector, eventTypeName); + + // optional columns + if (ctx.columnList() != null) { + for (var i = 0; i < ctx.columnList().ChildCount; i++) { + var node = ctx.columnList().GetChild(i); + if (ASTUtil.IsTerminatedOfType(node, EsperEPL2GrammarLexer.IDENT)) { + insertIntoDesc.Add(node.GetText()); + } + } + } + + _statementSpec.InsertIntoDesc = insertIntoDesc; + } + + public void ExitCreateVariableExpr(EsperEPL2GrammarParser.CreateVariableExprContext ctx) { + + var constant = false; + if (ctx.c != null) { + var text = ctx.c.Text; + if (text.Equals("constant") || text.Equals("const")) { + constant = true; + } else { + throw new EPException("Expected 'constant' or 'const' keyword after create for create-variable syntax but encountered '" + text + "'"); + } + } + + var variableType = ASTUtil.UnescapeClassIdent(ctx.classIdentifier()); + var variableName = ctx.n.Text; + + var array = ctx.arr != null; + var arrayOfPrimitive = ASTCreateSchemaHelper.ValidateIsPrimitiveArray(ctx.p); + + ExprNode assignment = null; + if (ctx.EQUALS() != null) { + assignment = ASTExprHelper.ExprCollectSubNodes(ctx.expression(), 0, _astExprNodeMap)[0]; + } + + var desc = new CreateVariableDesc(variableType, variableName, assignment, constant, array, arrayOfPrimitive); + _statementSpec.CreateVariableDesc = desc; + } + + public void ExitOnStreamExpr(EsperEPL2GrammarParser.OnStreamExprContext ctx) { + var streamAsName = ctx.i != null ? ctx.i.Text : null; + + // get stream to use (pattern or filter) + StreamSpecRaw streamSpec; + if (ctx.eventFilterExpression() != null) { + streamSpec = new FilterStreamSpecRaw(_filterSpec, ViewSpec.EMPTY_VIEWSPEC_ARRAY, streamAsName, StreamSpecOptions.DEFAULT); + } else if (ctx.patternInclusionExpression() != null) { + if ((_astPatternNodeMap.Count > 1) || ((_astPatternNodeMap.IsEmpty()))) { + throw ASTWalkException.From("Unexpected AST tree contains zero or more then 1 child elements for root"); + } + // Get expression node sub-tree from the AST nodes placed so far + var evalNode = _astPatternNodeMap.Values.First(); + var flags = GetPatternFlags(ctx.patternInclusionExpression().annotationEnum()); + streamSpec = new PatternStreamSpecRaw(evalNode, _viewSpecs.ToArray(), streamAsName, StreamSpecOptions.DEFAULT, flags.IsSuppressSameEventMatches, flags.IsDiscardPartialsOnMatch); + _astPatternNodeMap.Clear(); + } else { + throw new IllegalStateException("Invalid AST type node, cannot map to stream specification"); + } + _statementSpec.StreamSpecs.Add(streamSpec); + } + + public void ExitOnSelectInsertFromClause(EsperEPL2GrammarParser.OnSelectInsertFromClauseContext ctx) { + if (_onTriggerSplitPropertyEvals == null) { + _onTriggerSplitPropertyEvals = new Dictionary(); + } + _onTriggerSplitPropertyEvals.Put(_statementSpec, new OnTriggerSplitStreamFromClause(_propertyEvalSpec, ctx.i == null ? null : ctx.i.Text)); + _propertyEvalSpec = null; + } + + public void ExitPropertyExpressionAtomic(EsperEPL2GrammarParser.PropertyExpressionAtomicContext ctx) { + // initialize if not set + if (_propertyEvalSpec == null) { + _propertyEvalSpec = new PropertyEvalSpec(); + } + + // get select clause + var optionalSelectClause = new SelectClauseSpecRaw(); + if (_propertySelectRaw != null) { + optionalSelectClause.SelectExprList.AddAll(_propertySelectRaw); + _propertySelectRaw = null; + } + + // get the splitter expression + var splitterExpression = ASTExprHelper.ExprCollectSubNodes(ctx.expression(0), 0, _astExprNodeMap)[0]; + + // get where-clause, if any + var optionalWhereClause = ctx.where == null ? null : ASTExprHelper.ExprCollectSubNodes(ctx.where, 0, _astExprNodeMap)[0]; + + var optionalAsName = ctx.n == null ? null : ctx.n.Text; + + string splitterEventTypeName = ASTTypeExpressionAnnoHelper.ExpectMayTypeAnno(ctx.typeExpressionAnnotation(), _tokenStream); + var atom = new PropertyEvalAtom(splitterExpression, splitterEventTypeName, optionalAsName, optionalSelectClause, optionalWhereClause); + _propertyEvalSpec.Add(atom); + } + + public void ExitFafUpdate(EsperEPL2GrammarParser.FafUpdateContext ctx) { + HandleFAFNamedWindowStream(ctx.updateDetails().classIdentifier(), ctx.updateDetails().i); + var assignments = ASTExprHelper.GetOnTriggerSetAssignments(ctx.updateDetails().onSetAssignmentList(), _astExprNodeMap); + var whereClause = ctx.updateDetails().whereClause() == null ? null : ASTExprHelper.ExprCollectSubNodes(ctx.updateDetails().whereClause(), 0, _astExprNodeMap)[0]; + _statementSpec.FilterExprRootNode = whereClause; + _statementSpec.FireAndForgetSpec = new FireAndForgetSpecUpdate(assignments); + } + + public void ExitBitWiseExpression(EsperEPL2GrammarParser.BitWiseExpressionContext ctx) { + if (ctx.ChildCount < 2) { + return; + } + BitWiseOpEnum bitWiseOpEnum; + var token = ASTUtil.GetAssertTerminatedTokenType(ctx.GetChild(1)); + switch (token) { + case EsperEPL2GrammarLexer.BAND: + bitWiseOpEnum = BitWiseOpEnum.BAND; + break; + case EsperEPL2GrammarLexer.BOR: + bitWiseOpEnum = BitWiseOpEnum.BOR; + break; + case EsperEPL2GrammarLexer.BXOR: + bitWiseOpEnum = BitWiseOpEnum.BXOR; + break; + default: + throw ASTWalkException.From("Node type " + token + " not a recognized bit wise node type", _tokenStream, ctx); + } + + var bwNode = new ExprBitWiseNode(bitWiseOpEnum); + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(bwNode, ctx, _astExprNodeMap); + } + + public void ExitEvalEqualsExpression(EsperEPL2GrammarParser.EvalEqualsExpressionContext ctx) { + if (ctx.ChildCount < 2) { + return; + } + ExprNode exprNode; + var isNot = ctx.ne != null || ctx.isnot != null || ctx.sqlne != null; + if (ctx.a == null) { + var isIs = ctx.@is != null || ctx.isnot != null; + exprNode = new ExprEqualsNodeImpl(isNot, isIs); + } else { + var isAll = ctx.a.Type == EsperEPL2GrammarLexer.ALL; + IList subselect = ctx.subSelectGroupExpression(); + if (subselect != null && !subselect.IsEmpty()) { + StatementSpecRaw currentSpec = _astStatementSpecMap.Delete(ctx.subSelectGroupExpression()[0].subQueryExpr()); + exprNode = new ExprSubselectAllSomeAnyNode(currentSpec, isNot, isAll, null); + } else { + exprNode = new ExprEqualsAllAnyNode(isNot, isAll); + } + } + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(exprNode, ctx, _astExprNodeMap); + } + + public void ExitGopConfig(EsperEPL2GrammarParser.GopConfigContext ctx) { + + Object value; + if (ctx.SELECT() == null) { + if (ctx.expression() != null) { + value = ASTExprHelper.ExprCollectSubNodes(ctx, 0, _astExprNodeMap)[0]; + } else { + if (ctx.jsonarray() != null) { + value = new ExprConstantNodeImpl(ASTJsonHelper.WalkArray(_tokenStream, ctx.jsonarray())); + } else { + value = new ExprConstantNodeImpl(ASTJsonHelper.WalkObject(_tokenStream, ctx.jsonobject())); + } + ASTExprHelper.ExprCollectSubNodes(ctx, 0, _astExprNodeMap); + } + } else { + var newSpec = new StatementSpecRaw(_defaultStreamSelector); + newSpec.Annotations.AddAll(_statementSpec.Annotations); + + var existingSpec = _statementSpec; + existingSpec.CreateSchemaDesc = null; + value = existingSpec; + existingSpec.Annotations = Collections.GetEmptyList(); // clearing property-level annotations + + _statementSpec = newSpec; + } + _astGopNodeMap.Put(ctx, value); + } + + public void ExitCreateSelectionListElement(EsperEPL2GrammarParser.CreateSelectionListElementContext ctx) { + if (ctx.STAR() != null) { + _statementSpec.SelectClauseSpec.Add(new SelectClauseElementWildcard()); + } else { + var expr = ASTExprHelper.ExprCollectSubNodes(ctx, 0, _astExprNodeMap)[0]; + var asName = ctx.i != null ? ctx.i.Text : null; + _statementSpec.SelectClauseSpec.Add(new SelectClauseExprRawSpec(expr, asName, false)); + } + } + + public void ExitFafDelete(EsperEPL2GrammarParser.FafDeleteContext ctx) { + HandleFAFNamedWindowStream(ctx.classIdentifier(), ctx.i); + _statementSpec.FireAndForgetSpec = new FireAndForgetSpecDelete(); + } + + public void ExitConstant(EsperEPL2GrammarParser.ConstantContext ctx) { + var constantNode = new ExprConstantNodeImpl(ASTConstantHelper.Parse(ctx.GetChild(0))); + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(constantNode, ctx, _astExprNodeMap); + } + + public void ExitMergeMatched(EsperEPL2GrammarParser.MergeMatchedContext ctx) { + HandleMergeMatchedUnmatched(ctx.expression(), true); + } + + public void ExitEvalAndExpression(EsperEPL2GrammarParser.EvalAndExpressionContext ctx) { + if (ctx.ChildCount < 2) { + return; + } + var and = new ExprAndNodeImpl(); + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(and, ctx, _astExprNodeMap); + } + + public void ExitForExpr(EsperEPL2GrammarParser.ForExprContext ctx) { + if (_statementSpec.ForClauseSpec == null) { + _statementSpec.ForClauseSpec = new ForClauseSpec(); + } + var ident = ctx.i.Text; + var expressions = ASTExprHelper.ExprCollectSubNodes(ctx, 0, _astExprNodeMap); + _statementSpec.ForClauseSpec.Clauses.Add(new ForClauseItemSpec(ident, expressions)); + } + + public void ExitExpressionQualifyable(EsperEPL2GrammarParser.ExpressionQualifyableContext ctx) { + if (ctx.ChildCount < 2) { + return; + } + if (ctx.s != null) { + ExprNode node = ASTExprHelper.TimePeriodGetExprJustSeconds( + ctx.expression(), _astExprNodeMap, _configurationInformation, _engineImportService.TimeAbacus); + _astExprNodeMap.Put(ctx, node); + } else if (ctx.a != null || ctx.d != null) { + var isDescending = ctx.d != null; + var node = ASTExprHelper.ExprCollectSubNodes(ctx.expression(), 0, _astExprNodeMap)[0]; + var exprNode = new ExprOrderedExpr(isDescending); + exprNode.AddChildNode(node); + _astExprNodeMap.Put(ctx, exprNode); + } + } + + public void ExitPropertySelectionListElement(EsperEPL2GrammarParser.PropertySelectionListElementContext ctx) { + SelectClauseElementRaw raw; + if (ctx.s != null) { + raw = new SelectClauseElementWildcard(); + } else if (ctx.propertyStreamSelector() != null) { + raw = new SelectClauseStreamRawSpec(ctx.propertyStreamSelector().s.Text, + ctx.propertyStreamSelector().i != null ? ctx.propertyStreamSelector().i.Text : null); + } else { + var exprNode = ASTExprHelper.ExprCollectSubNodes(ctx.expression(), 0, _astExprNodeMap)[0]; + var optionalName = ctx.keywordAllowedIdent() != null ? ctx.keywordAllowedIdent().GetText() : null; + raw = new SelectClauseExprRawSpec(exprNode, optionalName, false); + } + + // Add as selection element + if (_propertySelectRaw == null) { + _propertySelectRaw = new List(); + } + _propertySelectRaw.Add(raw); + } + + public void ExitExpressionDecl(EsperEPL2GrammarParser.ExpressionDeclContext ctx) { + if (ctx.Parent.RuleIndex == EsperEPL2GrammarParser.RULE_createExpressionExpr) { + return; + } + + var pair = ASTExpressionDeclHelper.WalkExpressionDecl(ctx, _scriptBodies, _astExprNodeMap, _tokenStream); + if (pair.First != null) { + _expressionDeclarations.Add(pair.First); + } else { + _scriptExpressions.Add(pair.Second); + } + } + + public void ExitSubstitutionCanChain(EsperEPL2GrammarParser.SubstitutionCanChainContext ctx) { + if (ctx.chainedFunction() == null) { + return; + } + var substitutionNode = (ExprSubstitutionNode) _astExprNodeMap.Delete(ctx.substitution()); + IList chainSpec = ASTLibFunctionHelper.GetLibFuncChain(ctx.chainedFunction().libFunctionNoClass(), _astExprNodeMap); + var exprNode = new ExprDotNodeImpl(chainSpec, _engineImportService.IsDuckType, _engineImportService.IsUdfCache); + exprNode.AddChildNode(substitutionNode); + _astExprNodeMap.Put(ctx, exprNode); + } + + public void ExitSubstitution(EsperEPL2GrammarParser.SubstitutionContext ctx) { + var currentSize = _substitutionParamNodes.Count; + ExprSubstitutionNode substitutionNode; + if (ctx.slashIdentifier() != null) { + var name = ASTUtil.UnescapeSlashIdentifier(ctx.slashIdentifier()); + substitutionNode = new ExprSubstitutionNode(name); + } else { + substitutionNode = new ExprSubstitutionNode(currentSize + 1); + } + ASTSubstitutionHelper.ValidateNewSubstitution(_substitutionParamNodes, substitutionNode); + _substitutionParamNodes.Add(substitutionNode); + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(substitutionNode, ctx, _astExprNodeMap); + } + + public void ExitWeekDayOperator(EsperEPL2GrammarParser.WeekDayOperatorContext ctx) { + var exprNode = new ExprNumberSetCronParam(CronOperatorEnum.WEEKDAY); + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(exprNode, ctx, _astExprNodeMap); + ASTExprHelper.AddOptionalNumber(exprNode, ctx.number()); + ASTExprHelper.AddOptionalSimpleProperty(exprNode, ctx.i, _variableService, _statementSpec); + } + + public void ExitLastWeekdayOperand(EsperEPL2GrammarParser.LastWeekdayOperandContext ctx) { + var exprNode = new ExprNumberSetCronParam(CronOperatorEnum.LASTWEEKDAY); + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(exprNode, ctx, _astExprNodeMap); + } + + public void ExitGroupByListExpr(EsperEPL2GrammarParser.GroupByListExprContext ctx) { + ASTGroupByHelper.WalkGroupBy(ctx, _astExprNodeMap, _statementSpec.GroupByExpressions); + _astExprNodeMap.Clear(); + } + + public void ExitStreamSelector(EsperEPL2GrammarParser.StreamSelectorContext ctx) { + var streamName = ctx.s.Text; + var optionalName = ctx.i != null ? ctx.i.Text : null; + _statementSpec.SelectClauseSpec.Add(new SelectClauseStreamRawSpec(streamName, optionalName)); + } + + public void ExitStreamExpression(EsperEPL2GrammarParser.StreamExpressionContext ctx) { + // Determine the optional stream name + var streamName = ctx.i != null ? ctx.i.Text : null; + + var isUnidirectional = ctx.UNIDIRECTIONAL() != null; + var isRetainUnion = ctx.RETAINUNION() != null; + var isRetainIntersection = ctx.RETAININTERSECTION() != null; + + // Convert to a stream specification instance + StreamSpecRaw streamSpec; + var options = new StreamSpecOptions(isUnidirectional, isRetainUnion, isRetainIntersection); + + // If the first subnode is a filter node, we have a filter stream specification + if (ASTUtil.GetRuleIndexIfProvided(ctx.GetChild(0)) == EsperEPL2GrammarParser.RULE_eventFilterExpression) { + streamSpec = new FilterStreamSpecRaw(_filterSpec, _viewSpecs.ToArray(), streamName, options); + } else if (ASTUtil.GetRuleIndexIfProvided(ctx.GetChild(0)) == EsperEPL2GrammarParser.RULE_patternInclusionExpression) { + if ((_astPatternNodeMap.Count > 1) || ((_astPatternNodeMap.IsEmpty()))) { + throw ASTWalkException.From("Unexpected AST tree contains zero or more then 1 child elements for root"); + } + var pctx = (EsperEPL2GrammarParser.PatternInclusionExpressionContext) ctx.GetChild(0); + + // Get expression node sub-tree from the AST nodes placed so far + EvalFactoryNode evalNode = _astPatternNodeMap.Values.First(); + var flags = GetPatternFlags(pctx.annotationEnum()); + streamSpec = new PatternStreamSpecRaw(evalNode, _viewSpecs.ToArray(), streamName, options, flags.IsSuppressSameEventMatches, flags.IsDiscardPartialsOnMatch); + _astPatternNodeMap.Clear(); + } else if (ctx.databaseJoinExpression() != null) { + var dbctx = ctx.databaseJoinExpression(); + var dbName = dbctx.i.Text; + var sqlWithParams = StringValue.ParseString(dbctx.s.Text); + + // determine if there is variables used + IList sqlFragments; + try { + sqlFragments = PlaceholderParser.ParsePlaceholder(sqlWithParams); + foreach (var fragment in sqlFragments) { + if (!(fragment is PlaceholderParser.ParameterFragment)) { + continue; + } + + // Parse expression, store for substitution parameters + var expression = fragment.Value; + if (expression.ToUpperInvariant().Equals(DatabasePollingViewableFactory.SAMPLE_WHERECLAUSE_PLACEHOLDER)) { + continue; + } + + if (expression.Trim().Length == 0) { + throw ASTWalkException.From("Missing expression within ${...} in SQL statement"); + } + var toCompile = "select * from System.Object where " + expression; + var raw = EPAdministratorHelper.CompileEPL(toCompile, expression, false, null, SelectClauseStreamSelectorEnum.ISTREAM_ONLY, + _engineImportService, _variableService, _schedulingService, _engineURI, _configurationInformation, _patternNodeFactory, _contextManagementService, _exprDeclaredService, _tableService); + + if ((raw.SubstitutionParameters != null) && (raw.SubstitutionParameters.Count > 0)) { + throw ASTWalkException.From("EPL substitution parameters are not allowed in SQL ${...} expressions, consider using a variable instead"); + } + + if (raw.HasVariables) { + _statementSpec.HasVariables = true; + } + + // add expression + if (_statementSpec.SqlParameters == null) { + _statementSpec.SqlParameters = new Dictionary>(); + } + var listExp = _statementSpec.SqlParameters.Get(_statementSpec.StreamSpecs.Count); + if (listExp == null) { + listExp = new List(); + _statementSpec.SqlParameters.Put(_statementSpec.StreamSpecs.Count, listExp); + } + listExp.Add(raw.FilterRootNode); + } + } catch (PlaceholderParseException ex) { + Log.Warn("Failed to parse SQL text '" + sqlWithParams + "' :" + ex.Message); + // Let the view construction handle the validation + } + + string sampleSQL = null; + if (dbctx.s2 != null) { + sampleSQL = dbctx.s2.Text; + sampleSQL = StringValue.ParseString(sampleSQL.Trim()); + } + + streamSpec = new DBStatementStreamSpec(streamName, _viewSpecs.ToArray(), dbName, sqlWithParams, sampleSQL); + } else if (ctx.methodJoinExpression() != null) { + var mthctx = ctx.methodJoinExpression(); + var prefixIdent = mthctx.i.Text; + var fullName = ASTUtil.UnescapeClassIdent(mthctx.classIdentifier()); + + var indexDot = fullName.LastIndexOf('.'); + string classNamePart; + string methodNamePart; + if (indexDot == -1) { + classNamePart = null; + methodNamePart = fullName; + } else { + classNamePart = fullName.Substring(0, indexDot); + methodNamePart = fullName.Substring(indexDot + 1); + } + var exprNodes = ASTExprHelper.ExprCollectSubNodes(mthctx, 0, _astExprNodeMap); + + if (_variableService.GetVariableMetaData(classNamePart) != null) { + _statementSpec.HasVariables = true; + } + + string eventTypeName = ASTTypeExpressionAnnoHelper.ExpectMayTypeAnno(ctx.methodJoinExpression().typeExpressionAnnotation(), _tokenStream); + + streamSpec = new MethodStreamSpec(streamName, _viewSpecs.ToArray(), prefixIdent, classNamePart, methodNamePart, exprNodes, eventTypeName); + } else { + throw ASTWalkException.From("Unexpected AST child node to stream expression", _tokenStream, ctx); + } + _viewSpecs.Clear(); + _statementSpec.StreamSpecs.Add(streamSpec); + } + + public void ExitViewExpressionWNamespace(EsperEPL2GrammarParser.ViewExpressionWNamespaceContext ctx) { + var objectNamespace = ctx.GetChild(0).GetText(); + var objectName = ctx.viewWParameters().GetChild(0).GetText(); + var viewParameters = ASTExprHelper.ExprCollectSubNodes(ctx.viewWParameters(), 1, _astExprNodeMap); + _viewSpecs.Add(new ViewSpec(objectNamespace, objectName, viewParameters)); + } + + public void ExitViewExpressionOptNamespace(EsperEPL2GrammarParser.ViewExpressionOptNamespaceContext ctx) { + string objectNamespace = null; + var objectName = ctx.viewWParameters().GetChild(0).GetText(); + if (ctx.ns != null) { + objectNamespace = ctx.ns.Text; + } + var viewParameters = ASTExprHelper.ExprCollectSubNodes(ctx.viewWParameters(), 1, _astExprNodeMap); + _viewSpecs.Add(new ViewSpec(objectNamespace, objectName, viewParameters)); + } + + public void ExitPatternFilterExpression(EsperEPL2GrammarParser.PatternFilterExpressionContext ctx) { + string optionalPatternTagName = null; + if (ctx.i != null) { + optionalPatternTagName = ctx.i.Text; + } + + var eventName = ASTUtil.UnescapeClassIdent(ctx.classIdentifier()); + + var anno = ctx.patternFilterAnnotation(); + int? consumption = null; + if (anno != null) { + var name = ctx.patternFilterAnnotation().i.Text; + if (!name.ToUpper().Equals("CONSUME")) { + throw new EPException("Unexpected pattern filter @ annotation, expecting 'consume' but received '" + name + "'"); + } + if (anno.number() != null) { + consumption = ASTConstantHelper.Parse(anno.number()).AsInt(); + } else { + consumption = 1; + } + } + + var exprNodes = ASTExprHelper.ExprCollectSubNodes(ctx, 0, _astExprNodeMap); + + var rawFilterSpec = new FilterSpecRaw(eventName, exprNodes, _propertyEvalSpec); + _propertyEvalSpec = null; + var filterNode = _patternNodeFactory.MakeFilterNode(rawFilterSpec, optionalPatternTagName, consumption); + ASTExprHelper.PatternCollectAddSubnodesAddParentNode(filterNode, ctx, _astPatternNodeMap); + } + + public void ExitOnSelectExpr(EsperEPL2GrammarParser.OnSelectExprContext ctx) { + _statementSpec.SelectClauseSpec.IsDistinct = ctx.DISTINCT() != null; + } + + public void ExitStartPatternExpressionRule(EsperEPL2GrammarParser.StartPatternExpressionRuleContext ctx) { + if ((_astPatternNodeMap.Count > 1) || ((_astPatternNodeMap.IsEmpty()))) { + throw ASTWalkException.From("Unexpected AST tree contains zero or more then 1 child elements for root"); + } + + // Get expression node sub-tree from the AST nodes placed so far + var evalNode = _astPatternNodeMap.Values.First(); + + var streamSpec = new PatternStreamSpecRaw(evalNode, ViewSpec.EMPTY_VIEWSPEC_ARRAY, null, StreamSpecOptions.DEFAULT, false, false); + _statementSpec.StreamSpecs.Add(streamSpec); + _statementSpec.SubstitutionParameters = _substitutionParamNodes; + + _astPatternNodeMap.Clear(); + } + + public void ExitOutputLimit(EsperEPL2GrammarParser.OutputLimitContext ctx) { + var spec = ASTOutputLimitHelper.BuildOutputLimitSpec(_tokenStream, ctx, _astExprNodeMap, _variableService, _engineURI, _timeProvider, _exprEvaluatorContext); + _statementSpec.OutputLimitSpec = spec; + if (spec.VariableName != null) { + _statementSpec.HasVariables = true; + ASTExprHelper.AddVariableReference(_statementSpec, spec.VariableName); + } + } + + public void ExitNumericParameterList(EsperEPL2GrammarParser.NumericParameterListContext ctx) { + var exprNode = new ExprNumberSetList(); + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(exprNode, ctx, _astExprNodeMap); + } + + public void ExitCreateSchemaExpr(EsperEPL2GrammarParser.CreateSchemaExprContext ctx) { + var createSchema = ASTCreateSchemaHelper.WalkCreateSchema(ctx); + if (ctx.Parent.RuleIndex == EsperEPL2GrammarParser.RULE_eplExpression) { + _statementSpec.StreamSpecs.Add(new FilterStreamSpecRaw(new FilterSpecRaw(typeof(Object).FullName, Collections.GetEmptyList(), null), ViewSpec.EMPTY_VIEWSPEC_ARRAY, null, StreamSpecOptions.DEFAULT)); + } + _statementSpec.CreateSchemaDesc = createSchema; + } + + public void ExitLastOperator(EsperEPL2GrammarParser.LastOperatorContext ctx) { + var exprNode = new ExprNumberSetCronParam(CronOperatorEnum.LASTDAY); + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(exprNode, ctx, _astExprNodeMap); + ASTExprHelper.AddOptionalNumber(exprNode, ctx.number()); + ASTExprHelper.AddOptionalSimpleProperty(exprNode, ctx.i, _variableService, _statementSpec); + } + + public void ExitCreateIndexExpr(EsperEPL2GrammarParser.CreateIndexExprContext ctx) { + var indexName = ctx.n.Text; + var windowName = ctx.w.Text; + + var columns = new List(); + var unique = false; + IList cols = ctx.createIndexColumnList().createIndexColumn(); + foreach (var col in cols) { + var type = CreateIndexType.HASH; + var columnName = col.c.Text; + if (col.t != null) { + var typeName = col.t.Text; + try { + type = EnumHelper.Parse(typeName, true); + } catch (Exception ex) + { + throw ASTWalkException.From(string.Format("Invalid column index type '{0}' encountered, please use any of the following index type names {1}", + typeName, EnumHelper.GetNames().Render())); + } + } + columns.Add(new CreateIndexItem(columnName, type)); + } + + if (ctx.u != null) { + var ident = ctx.u.Text; + if (ident.ToLowerInvariant().Trim().Equals("unique")) { + unique = true; + } else { + throw ASTWalkException.From("Invalid keyword '" + ident + "' in create-index encountered, expected 'unique'"); + } + } + + _statementSpec.CreateIndexDesc = new CreateIndexDesc(unique, indexName, windowName, columns); + } + + public void ExitAnnotationEnum(EsperEPL2GrammarParser.AnnotationEnumContext ctx) { + if (ctx.Parent.RuleIndex != EsperEPL2GrammarParser.RULE_startEPLExpressionRule && + ctx.Parent.RuleIndex != EsperEPL2GrammarParser.RULE_startPatternExpressionRule) { + return; + } + + _statementSpec.Annotations.Add(ASTAnnotationHelper.Walk(ctx, _engineImportService)); + _astExprNodeMap.Clear(); + } + + public void ExitCreateContextExpr(EsperEPL2GrammarParser.CreateContextExprContext ctx) { + var contextDesc = ASTContextHelper.WalkCreateContext(ctx, _astExprNodeMap, _astPatternNodeMap, _propertyEvalSpec, _filterSpec); + _filterSpec = null; + _propertyEvalSpec = null; + _statementSpec.CreateContextDesc = contextDesc; + } + + public void ExitLastOperand(EsperEPL2GrammarParser.LastOperandContext ctx) { + var exprNode = new ExprNumberSetCronParam(CronOperatorEnum.LASTDAY); + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(exprNode, ctx, _astExprNodeMap); + } + + public void ExitCreateWindowExpr(EsperEPL2GrammarParser.CreateWindowExprContext ctx) { + var windowName = ctx.i.Text; + + var eventName = "System.Object"; + if (ctx.createWindowExprModelAfter() != null) { + eventName = ASTUtil.UnescapeClassIdent(ctx.createWindowExprModelAfter().classIdentifier()); + } + + var isRetainUnion = ctx.ru != null; + var isRetainIntersection = ctx.ri != null; + var streamSpecOptions = new StreamSpecOptions(false, isRetainUnion, isRetainIntersection); + + // handle table-create clause, i.e. (col1 type, col2 type) + IList colums = ASTCreateSchemaHelper.GetColTypeList(ctx.createColumnList()); + + var isInsert = ctx.INSERT() != null; + ExprNode insertWhereExpr = null; + if (isInsert && ctx.expression() != null) { + insertWhereExpr = ASTExprHelper.ExprCollectSubNodes(ctx.expression(), 0, _astExprNodeMap)[0]; + } + + var desc = new CreateWindowDesc(windowName, _viewSpecs, streamSpecOptions, isInsert, insertWhereExpr, colums, eventName); + _statementSpec.CreateWindowDesc = desc; + + // this is good for indicating what is being selected from + var rawFilterSpec = new FilterSpecRaw(eventName, new List(), null); + var streamSpec = new FilterStreamSpecRaw(rawFilterSpec, ViewSpec.EMPTY_VIEWSPEC_ARRAY, null, streamSpecOptions); + _statementSpec.StreamSpecs.Add(streamSpec); + } + + public void ExitCreateExpressionExpr(EsperEPL2GrammarParser.CreateExpressionExprContext ctx) { + var pair = ASTExpressionDeclHelper.WalkExpressionDecl(ctx.expressionDecl(), _scriptBodies, _astExprNodeMap, _tokenStream); + _statementSpec.CreateExpressionDesc = new CreateExpressionDesc(pair); + } + + public void ExitRangeOperand(EsperEPL2GrammarParser.RangeOperandContext ctx) { + var exprNode = new ExprNumberSetRange(); + _astExprNodeMap.Put(ctx, exprNode); + if (ctx.s1 != null) { + ASTExprHelper.ExprCollectAddSubNodes(exprNode, ctx.s1, _astExprNodeMap); + } + ASTExprHelper.AddOptionalNumber(exprNode, ctx.n1); + ASTExprHelper.AddOptionalSimpleProperty(exprNode, ctx.i1, _variableService, _statementSpec); + if (ctx.s2 != null) { + ASTExprHelper.ExprCollectAddSubNodes(exprNode, ctx.s2, _astExprNodeMap); + } + ASTExprHelper.AddOptionalNumber(exprNode, ctx.n2); + ASTExprHelper.AddOptionalSimpleProperty(exprNode, ctx.i2, _variableService, _statementSpec); + } + + public void ExitRowSubSelectExpression(EsperEPL2GrammarParser.RowSubSelectExpressionContext ctx) { + StatementSpecRaw statementSpec = _astStatementSpecMap.Delete(ctx.subQueryExpr()); + var subselectNode = new ExprSubselectRowNode(statementSpec); + if (ctx.chainedFunction() != null) { + HandleChainedFunction(ctx, ctx.chainedFunction(), subselectNode); + } else { + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(subselectNode, ctx, _astExprNodeMap); + } + } + + public void ExitUnaryExpression(EsperEPL2GrammarParser.UnaryExpressionContext ctx) { + if (ctx.inner != null && ctx.chainedFunction() != null) { + HandleChainedFunction(ctx, ctx.chainedFunction(), null); + } + if (ctx.NEWKW() != null && ctx.newAssign() != null) { + var columnNames = new List(); + var expressions = new List(); + IList assigns = ctx.newAssign(); + foreach (var assign in assigns) { + var property = ASTUtil.GetPropertyName(assign.eventProperty(), 0); + columnNames.Add(property); + ExprNode expr; + if (assign.expression() != null) { + expr = ASTExprHelper.ExprCollectSubNodes(assign.expression(), 0, _astExprNodeMap)[0]; + } else { + expr = new ExprIdentNodeImpl(property); + } + expressions.Add(expr); + } + string[] columns = columnNames.ToArray(); + var newNode = new ExprNewStructNode(columns); + newNode.AddChildNodes(expressions); + _astExprNodeMap.Put(ctx, newNode); + } + if (ctx.NEWKW() != null && ctx.classIdentifier() != null) { + var classIdent = ASTUtil.UnescapeClassIdent(ctx.classIdentifier()); + ExprNode exprNode; + var newNode = new ExprNewInstanceNode(classIdent); + if (ctx.chainedFunction() != null) { + IList chainSpec = ASTLibFunctionHelper.GetLibFuncChain(ctx.chainedFunction().libFunctionNoClass(), _astExprNodeMap); + var dotNode = new ExprDotNodeImpl(chainSpec, _engineImportService.IsDuckType, _engineImportService.IsUdfCache); + dotNode.AddChildNode(newNode); + exprNode = dotNode; + } else { + exprNode = newNode; + } + ASTExprHelper.ExprCollectAddSubNodes(newNode, ctx, _astExprNodeMap); + _astExprNodeMap.Put(ctx, exprNode); + } + if (ctx.b != null) { + // handle "variable[xxx]" + var tableName = ctx.b.Text; + ExprNode exprNode; + ExprTableAccessNode tableNode; + if (ctx.chainedFunction() == null) { + tableNode = new ExprTableAccessNodeTopLevel(tableName); + exprNode = tableNode; + } else { + IList chainSpec = ASTLibFunctionHelper.GetLibFuncChain(ctx.chainedFunction().libFunctionNoClass(), _astExprNodeMap); + var pair = ASTTableExprHelper.GetTableExprChainable(_engineImportService, _plugInAggregations, _engineURI, tableName, chainSpec); + tableNode = pair.First; + if (pair.Second.IsEmpty()) { + exprNode = tableNode; + } else { + exprNode = new ExprDotNodeImpl(pair.Second, _engineImportService.IsDuckType, _engineImportService.IsUdfCache); + exprNode.AddChildNode(tableNode); + } + } + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(tableNode, ctx, _astExprNodeMap); + _astExprNodeMap.Put(ctx, exprNode); + ASTTableExprHelper.AddTableExpressionReference(_statementSpec, tableNode); + } + } + + public void EnterOnSelectInsertExpr(EsperEPL2GrammarParser.OnSelectInsertExprContext ctx) { + PushStatementContext(); + } + + public void ExitSelectClause(EsperEPL2GrammarParser.SelectClauseContext ctx) { + SelectClauseStreamSelectorEnum selector; + if (ctx.s != null) { + if (ctx.s.Type == EsperEPL2GrammarLexer.RSTREAM) { + selector = SelectClauseStreamSelectorEnum.RSTREAM_ONLY; + } else if (ctx.s.Type == EsperEPL2GrammarLexer.ISTREAM) { + selector = SelectClauseStreamSelectorEnum.ISTREAM_ONLY; + } else if (ctx.s.Type == EsperEPL2GrammarLexer.IRSTREAM) { + selector = SelectClauseStreamSelectorEnum.RSTREAM_ISTREAM_BOTH; + } else { + throw ASTWalkException.From("Encountered unrecognized token type " + ctx.s.Type, _tokenStream, ctx); + } + _statementSpec.SelectStreamDirEnum = selector; + } + _statementSpec.SelectClauseSpec.IsDistinct = ctx.d != null; + } + + public void ExitConcatenationExpr(EsperEPL2GrammarParser.ConcatenationExprContext ctx) { + if (ctx.ChildCount < 2) { + return; + } + var concatNode = new ExprConcatNode(); + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(concatNode, ctx, _astExprNodeMap); + } + + public void ExitSubSelectFilterExpr(EsperEPL2GrammarParser.SubSelectFilterExprContext ctx) { + var streamName = ctx.i != null ? ctx.i.Text : null; + var isRetainUnion = ctx.ru != null; + var isRetainIntersection = ctx.ri != null; + var options = new StreamSpecOptions(false, isRetainUnion, isRetainIntersection); + var streamSpec = new FilterStreamSpecRaw(_filterSpec, _viewSpecs.ToArray(), streamName, options); + _viewSpecs.Clear(); + _statementSpec.StreamSpecs.Add(streamSpec); + } + + public void ExitNegatedExpression(EsperEPL2GrammarParser.NegatedExpressionContext ctx) { + if (ctx.ChildCount < 2) { + return; + } + var notNode = new ExprNotNode(); + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(notNode, ctx, _astExprNodeMap); + } + + public void ExitAdditiveExpression(EsperEPL2GrammarParser.AdditiveExpressionContext ctx) { + if (ctx.ChildCount < 2) { + return; + } + var expr = ASTExprHelper.MathGetExpr(ctx, _astExprNodeMap, _configurationInformation); + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(expr, ctx, _astExprNodeMap); + } + + public void ExitMultiplyExpression(EsperEPL2GrammarParser.MultiplyExpressionContext ctx) { + if (ctx.ChildCount < 2) { + return; + } + var expr = ASTExprHelper.MathGetExpr(ctx, _astExprNodeMap, _configurationInformation); + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(expr, ctx, _astExprNodeMap); + } + + public void ExitEventProperty(EsperEPL2GrammarParser.EventPropertyContext ctx) { + if (EVENT_PROPERTY_WALK_EXCEPTIONS_PARENT.Contains(ctx.Parent.RuleIndex)) { + return; + } + + if (ctx.ChildCount == 0) { + throw new IllegalStateException("EmptyFalse event property expression encountered"); + } + + ExprNode exprNode; + string propertyName; + + // The stream name may precede the event property name, but cannot be told apart from the property name: + // s0.p1 could be a nested property, or could be stream 's0' and property 'p1' + + // A single entry means this must be the property name. + // And a non-simple property means that it cannot be a stream name. + if (ctx.eventPropertyAtomic().Length == 1 || PropertyParser.IsNestedPropertyWithNonSimpleLead(ctx)) { + propertyName = ctx.GetText(); + exprNode = new ExprIdentNodeImpl(propertyName); + + var first = ctx.eventPropertyAtomic()[0]; + + // test table access expression + if (first.lb != null) { + string nameText = first.eventPropertyIdent().GetText(); + if (_tableService.GetTableMetadata(nameText) != null) { + ExprTableAccessNode tableNode; + if (ctx.eventPropertyAtomic().Length == 1) + { + tableNode = new ExprTableAccessNodeTopLevel(nameText); + } + else if (ctx.eventPropertyAtomic().Length == 2) + { + var column = ctx.eventPropertyAtomic()[1].GetText(); + tableNode = new ExprTableAccessNodeSubprop(nameText, column); + } else { + throw ASTWalkException.From("Invalid table expression '" + _tokenStream.GetText(ctx)); + } + exprNode = tableNode; + ASTTableExprHelper.AddTableExpressionReference(_statementSpec, tableNode); + ASTExprHelper.AddOptionalNumber(tableNode, first.ni); + } + } + + // test script + if (first.lp != null) { + var ident = ASTUtil.EscapeDot(first.eventPropertyIdent().GetText()); + var key = StringValue.ParseString(first.s.Text); + var @params = Collections.SingletonList(new ExprConstantNodeImpl(key)); + var scriptNode = ExprDeclaredHelper.GetExistsScript(GetDefaultDialect(), ident, @params, _scriptExpressions, _exprDeclaredService); + if (scriptNode != null) { + exprNode = scriptNode; + } + } + + var found = ExprDeclaredHelper.GetExistsDeclaredExpr(propertyName, Collections.GetEmptyList(), _expressionDeclarations.Expressions, _exprDeclaredService, _contextDescriptor); + if (found != null) { + exprNode = found; + } + } else { + // --> this is more then one child node, and the first child node is a simple property + // we may have a stream name in the first simple property, or a nested property + // i.e. 's0.p0' could mean that the event has a nested property to 's0' of name 'p0', or 's0' is the stream name + var leadingIdentifier = ctx.GetChild(0).GetChild(0).GetText(); + var streamOrNestedPropertyName = ASTUtil.EscapeDot(leadingIdentifier); + propertyName = ASTUtil.GetPropertyName(ctx, 2); + + var tableNode = ASTTableExprHelper.CheckTableNameGetExprForSubproperty(_tableService, streamOrNestedPropertyName, propertyName); + var variableMetaData = _variableService.GetVariableMetaData(leadingIdentifier); + if (tableNode != null) { + if (tableNode.Second != null) { + exprNode = tableNode.Second; + } else { + exprNode = tableNode.First; + } + ASTTableExprHelper.AddTableExpressionReference(_statementSpec, tableNode.First); + } else if (variableMetaData != null) { + exprNode = new ExprVariableNodeImpl(variableMetaData, propertyName); + _statementSpec.HasVariables = true; + var message = VariableServiceUtil.CheckVariableContextName(_statementSpec.OptionalContextName, variableMetaData); + if (message != null) { + throw ASTWalkException.From(message); + } + ASTExprHelper.AddVariableReference(_statementSpec, variableMetaData.VariableName); + } else if (_contextDescriptor != null && _contextDescriptor.ContextPropertyRegistry.IsContextPropertyPrefix(streamOrNestedPropertyName)) { + exprNode = new ExprContextPropertyNode(propertyName); + } else { + exprNode = new ExprIdentNodeImpl(propertyName, streamOrNestedPropertyName); + } + } + + // handle variable + var variableMetaDataX = _variableService.GetVariableMetaData(propertyName); + if (variableMetaDataX != null) { + exprNode = new ExprVariableNodeImpl(variableMetaDataX, null); + _statementSpec.HasVariables = true; + var message = VariableServiceUtil.CheckVariableContextName(_statementSpec.OptionalContextName, variableMetaDataX); + if (message != null) { + throw ASTWalkException.From(message); + } + ASTExprHelper.AddVariableReference(_statementSpec, variableMetaDataX.VariableName); + } + + // handle table + var table = ASTTableExprHelper.CheckTableNameGetExprForProperty(_tableService, propertyName); + if (table != null) { + exprNode = table; + ASTTableExprHelper.AddTableExpressionReference(_statementSpec, table); + } + + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(exprNode, ctx, _astExprNodeMap); + } + + public void ExitOuterJoin(EsperEPL2GrammarParser.OuterJoinContext ctx) { + OuterJoinType joinType; + if (ctx.i != null) { + joinType = OuterJoinType.INNER; + } else if (ctx.tr != null) { + joinType = OuterJoinType.RIGHT; + } else if (ctx.tl != null) { + joinType = OuterJoinType.LEFT; + } else if (ctx.tf != null) { + joinType = OuterJoinType.FULL; + } else { + joinType = OuterJoinType.INNER; + } + + // always starts with ON-token, so as to not produce an empty node + ExprIdentNode left = null; + ExprIdentNode right = null; + ExprIdentNode[] addLeftArr = null; + ExprIdentNode[] addRightArr = null; + + // get subnodes representing the on-expression, if provided + if (ctx.outerJoinIdent() != null) { + IList pairs = ctx.outerJoinIdent().outerJoinIdentPair(); + IList props = pairs[0].eventProperty(); + left = ValidateOuterJoinGetIdentNode(ASTExprHelper.ExprCollectSubNodes(props[0], 0, _astExprNodeMap)[0]); + right = ValidateOuterJoinGetIdentNode(ASTExprHelper.ExprCollectSubNodes(props[1], 0, _astExprNodeMap)[0]); + + if (pairs.Count > 1) { + var addLeft = new List(pairs.Count - 1); + var addRight = new List(pairs.Count - 1); + for (var i = 1; i < pairs.Count; i++) { + props = pairs[i].eventProperty(); + var moreLeft = ValidateOuterJoinGetIdentNode(ASTExprHelper.ExprCollectSubNodes(props[0], 0, _astExprNodeMap)[0]); + var moreRight = ValidateOuterJoinGetIdentNode(ASTExprHelper.ExprCollectSubNodes(props[1], 0, _astExprNodeMap)[0]); + addLeft.Add(moreLeft); + addRight.Add(moreRight); + } + addLeftArr = addLeft.ToArray(); + addRightArr = addRight.ToArray(); + } + } + + var outerJoinDesc = new OuterJoinDesc(joinType, left, right, addLeftArr, addRightArr); + _statementSpec.OuterJoinDescList.Add(outerJoinDesc); + } + + public void ExitOnExpr(EsperEPL2GrammarParser.OnExprContext ctx) { + if (ctx.onMergeExpr() != null) { + var windowName = ctx.onMergeExpr().n.Text; + var asName = ctx.onMergeExpr().i != null ? ctx.onMergeExpr().i.Text : null; + var desc = new OnTriggerMergeDesc(windowName, asName, _mergeMatcheds ?? Collections.GetEmptyList()); + _statementSpec.OnTriggerDesc = desc; + } else if (ctx.onSetExpr() == null) { + var windowName = GetOnExprWindowName(ctx); + var deleteAndSelect = ctx.onSelectExpr() != null && ctx.onSelectExpr().d != null; + if (windowName == null) { + // on the statement spec, the deepest spec is the outermost + var splitStreams = new List(); + var statementSpecList = _statementSpecStack.Reverse().ToArray(); + + for (var ii = 1; ii < statementSpecList.Length; ii++) { + StatementSpecRaw raw = statementSpecList[ii]; + OnTriggerSplitStreamFromClause fromClause = _onTriggerSplitPropertyEvals == null ? null : _onTriggerSplitPropertyEvals.Get(raw); + splitStreams.Add(new OnTriggerSplitStream(raw.InsertIntoDesc, raw.SelectClauseSpec, fromClause, raw.FilterExprRootNode)); + } + OnTriggerSplitStreamFromClause fromClauseX = _onTriggerSplitPropertyEvals == null ? null : _onTriggerSplitPropertyEvals.Get(_statementSpec); + splitStreams.Add(new OnTriggerSplitStream(_statementSpec.InsertIntoDesc, _statementSpec.SelectClauseSpec, fromClauseX, _statementSpec.FilterExprRootNode)); + if (!statementSpecList.IsEmpty()) { + _statementSpec = statementSpecList[0]; + } + var isFirst = ctx.outputClauseInsert() == null || ctx.outputClauseInsert().ALL() == null; + _statementSpec.OnTriggerDesc = new OnTriggerSplitStreamDesc(OnTriggerType.ON_SPLITSTREAM, isFirst, splitStreams); + _statementSpecStack.Clear(); + } else if (ctx.onUpdateExpr() != null) { + var assignments = ASTExprHelper.GetOnTriggerSetAssignments(ctx.onUpdateExpr().onSetAssignmentList(), _astExprNodeMap); + _statementSpec.OnTriggerDesc = new OnTriggerWindowUpdateDesc(windowName.First, windowName.Second, assignments); + if (ctx.onUpdateExpr().whereClause() != null) { + _statementSpec.FilterExprRootNode = ASTExprHelper.ExprCollectSubNodes(ctx.onUpdateExpr().whereClause(), 0, _astExprNodeMap)[0]; + } + } else { + _statementSpec.OnTriggerDesc = new OnTriggerWindowDesc(windowName.First, windowName.Second, ctx.onDeleteExpr() != null ? OnTriggerType.ON_DELETE : OnTriggerType.ON_SELECT, deleteAndSelect); + } + } else { + var assignments = ASTExprHelper.GetOnTriggerSetAssignments(ctx.onSetExpr().onSetAssignmentList(), _astExprNodeMap); + _statementSpec.OnTriggerDesc = new OnTriggerSetDesc(assignments); + } + } + + public void ExitMatchRecogPatternAlteration(EsperEPL2GrammarParser.MatchRecogPatternAlterationContext ctx) { + if (ctx.ChildCount < 2) { + return; + } + var alterNode = new RowRegexExprNodeAlteration(); + ASTExprHelper.RegExCollectAddSubNodesAddParentNode(alterNode, ctx, _astRowRegexNodeMap); + } + + public void ExitCaseExpression(EsperEPL2GrammarParser.CaseExpressionContext ctx) { + if (ctx.ChildCount < 2) { + return; + } + if (_astExprNodeMap.IsEmpty()) { + throw ASTWalkException.From("Unexpected AST tree contains zero child element for case node", _tokenStream, ctx); + } + if (_astExprNodeMap.Count == 1) { + throw ASTWalkException.From("AST tree does not contain at least when node for case node", _tokenStream, ctx); + } + + var caseNode = new ExprCaseNode(ctx.expression() != null); + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(caseNode, ctx, _astExprNodeMap); + } + + public void ExitRowLimit(EsperEPL2GrammarParser.RowLimitContext ctx) { + var spec = ASTOutputLimitHelper.BuildRowLimitSpec(ctx); + _statementSpec.RowLimitSpec = spec; + + if ((spec.NumRowsVariable != null) || (spec.OptionalOffsetVariable != null)) { + _statementSpec.HasVariables = true; + ASTExprHelper.AddVariableReference(_statementSpec, spec.OptionalOffsetVariable); + } + _astExprNodeMap.Clear(); + } + + public void ExitOrderByListElement(EsperEPL2GrammarParser.OrderByListElementContext ctx) { + var exprNode = ASTExprHelper.ExprCollectSubNodes(ctx, 0, _astExprNodeMap)[0]; + _astExprNodeMap.Clear(); + var descending = ctx.d != null; + _statementSpec.OrderByList.Add(new OrderByItem(exprNode, descending)); + } + + public void ExitMergeUnmatched(EsperEPL2GrammarParser.MergeUnmatchedContext ctx) { + HandleMergeMatchedUnmatched(ctx.expression(), false); + } + + public void ExitExistsSubSelectExpression(EsperEPL2GrammarParser.ExistsSubSelectExpressionContext ctx) { + StatementSpecRaw currentSpec = _astStatementSpecMap.Delete(ctx.subQueryExpr()); + var subselectNode = new ExprSubselectExistsNode(currentSpec); + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(subselectNode, ctx, _astExprNodeMap); + } + + public void ExitArrayExpression(EsperEPL2GrammarParser.ArrayExpressionContext ctx) { + var arrayNode = new ExprArrayNode(); + if (ctx.chainedFunction() != null) { + ASTExprHelper.ExprCollectAddSubNodesExpressionCtx(arrayNode, ctx.expression(), _astExprNodeMap); + HandleChainedFunction(ctx, ctx.chainedFunction(), arrayNode); + } else { + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(arrayNode, ctx, _astExprNodeMap); + } + } + + public void VisitTerminal(ITerminalNode terminalNode) { + if (terminalNode.Symbol.Type == EsperEPL2GrammarLexer.STAR) { + var ruleIndex = ASTUtil.GetRuleIndexIfProvided(terminalNode.Parent); + if (ruleIndex == EsperEPL2GrammarParser.RULE_selectionListElement) { + _statementSpec.SelectClauseSpec.Add(new SelectClauseElementWildcard()); + } + if (ruleIndex == EsperEPL2GrammarParser.STAR || ruleIndex == EsperEPL2GrammarParser.RULE_expressionWithTime) { + var exprNode = new ExprWildcardImpl(); + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(exprNode, terminalNode, _astExprNodeMap); + } + } + } + + public void ExitAndExpression(EsperEPL2GrammarParser.AndExpressionContext ctx) { + if (ctx.ChildCount < 2) { + return; + } + var andNode = _patternNodeFactory.MakeAndNode(); + ASTExprHelper.PatternCollectAddSubnodesAddParentNode(andNode, ctx, _astPatternNodeMap); + } + + public void ExitFollowedByExpression(EsperEPL2GrammarParser.FollowedByExpressionContext ctx) { + if (ctx.ChildCount < 2) { + return; + } + IList repeats = ctx.followedByRepeat(); + var maxExpressions = new ExprNode[ctx.ChildCount - 1]; + for (var i = 0; i < repeats.Count; i++) { + EsperEPL2GrammarParser.FollowedByRepeatContext repeat = repeats[i]; + if (repeat.expression() != null) { + maxExpressions[i] = ASTExprHelper.ExprCollectSubNodes(repeat.expression(), 0, _astExprNodeMap)[0]; + } + } + + IList expressions = Collections.GetEmptyList(); + if (!CollectionUtil.IsAllNullArray(maxExpressions)) { + expressions = maxExpressions; // can contain null elements as max/no-max can be mixed + } + + var fbNode = _patternNodeFactory.MakeFollowedByNode(expressions, _configurationInformation.EngineDefaults.Patterns.MaxSubexpressions != null); + ASTExprHelper.PatternCollectAddSubnodesAddParentNode(fbNode, ctx, _astPatternNodeMap); + } + + public void ExitOrExpression(EsperEPL2GrammarParser.OrExpressionContext ctx) { + if (ctx.ChildCount < 2) { + return; + } + var orNode = _patternNodeFactory.MakeOrNode(); + ASTExprHelper.PatternCollectAddSubnodesAddParentNode(orNode, ctx, _astPatternNodeMap); + } + + public void ExitQualifyExpression(EsperEPL2GrammarParser.QualifyExpressionContext ctx) { + if (ctx.ChildCount < 2) { + return; + } + if (ctx.matchUntilRange() != null) { + var matchUntil = MakeMatchUntil(ctx.matchUntilRange(), false); + ASTExprHelper.PatternCollectAddSubnodesAddParentNode(matchUntil, ctx.guardPostFix(), _astPatternNodeMap); + } + + EvalFactoryNode theNode; + if (ctx.e != null) { + theNode = _patternNodeFactory.MakeEveryNode(); + } else if (ctx.n != null) { + theNode = _patternNodeFactory.MakeNotNode(); + } else if (ctx.d != null) { + var exprNodes = ASTExprHelper.ExprCollectSubNodes(ctx.distinctExpressionList(), 0, _astExprNodeMap); + theNode = _patternNodeFactory.MakeEveryDistinctNode(exprNodes); + } else { + throw ASTWalkException.From("Failed to recognize node"); + } + ASTExprHelper.PatternCollectAddSubnodesAddParentNode(theNode, ctx, _astPatternNodeMap); + } + + public void ExitMatchUntilExpression(EsperEPL2GrammarParser.MatchUntilExpressionContext ctx) { + if (ctx.ChildCount < 2) { + return; + } + EvalFactoryNode node; + if (ctx.matchUntilRange() != null) { + node = MakeMatchUntil(ctx.matchUntilRange(), ctx.until != null); + } else { + node = _patternNodeFactory.MakeMatchUntilNode(null, null, null); + } + ASTExprHelper.PatternCollectAddSubnodesAddParentNode(node, ctx, _astPatternNodeMap); + } + + private EvalFactoryNode MakeMatchUntil(EsperEPL2GrammarParser.MatchUntilRangeContext range, bool hasUntil) { + var hasRange = true; + ExprNode low = null; + ExprNode high = null; + ExprNode single = null; + var allowZeroLowerBounds = false; + + if (range.low != null && range.c1 != null && range.high == null) { // [expr:] + low = ASTExprHelper.ExprCollectSubNodes(range.low, 0, _astExprNodeMap)[0]; + } else if (range.c2 != null && range.upper != null) { // [:expr] + high = ASTExprHelper.ExprCollectSubNodes(range.upper, 0, _astExprNodeMap)[0]; + } else if (range.low != null && range.c1 == null) { // [expr] + single = ASTExprHelper.ExprCollectSubNodes(range.low, 0, _astExprNodeMap)[0]; + hasRange = false; + } else if (range.low != null) { // [expr:expr] + low = ASTExprHelper.ExprCollectSubNodes(range.low, 0, _astExprNodeMap)[0]; + high = ASTExprHelper.ExprCollectSubNodes(range.high, 0, _astExprNodeMap)[0]; + allowZeroLowerBounds = true; + } + + bool tightlyBound; + if (single != null) { + ASTMatchUntilHelper.Validate(single, single, allowZeroLowerBounds); + tightlyBound = true; + } else { + tightlyBound = ASTMatchUntilHelper.Validate(low, high, allowZeroLowerBounds); + } + if (hasRange && !tightlyBound && !hasUntil) { + throw ASTWalkException.From("Variable bounds repeat operator requires an until-expression"); + } + return _patternNodeFactory.MakeMatchUntilNode(low, high, single); + } + + public void ExitGuardPostFix(EsperEPL2GrammarParser.GuardPostFixContext ctx) { + if (ctx.ChildCount < 2) { + return; + } + if (ctx.guardWhereExpression() == null && ctx.guardWhileExpression() == null) { // nested + return; + } + string objectNamespace; + string objectName; + IList obsParameters; + if (ctx.guardWhereExpression() != null) { + objectNamespace = ctx.guardWhereExpression().GetChild(0).GetText(); + objectName = ctx.guardWhereExpression().GetChild(2).GetText(); + obsParameters = ASTExprHelper.ExprCollectSubNodes(ctx.guardWhereExpression(), 3, _astExprNodeMap); + } else { + objectNamespace = GuardEnum.WHILE_GUARD.GetNamespace(); + objectName = GuardEnum.WHILE_GUARD.GetName(); + obsParameters = ASTExprHelper.ExprCollectSubNodes(ctx.guardWhileExpression(), 1, _astExprNodeMap); + } + + var guardSpec = new PatternGuardSpec(objectNamespace, objectName, obsParameters); + var guardNode = _patternNodeFactory.MakeGuardNode(guardSpec); + ASTExprHelper.PatternCollectAddSubnodesAddParentNode(guardNode, ctx, _astPatternNodeMap); + } + + public void ExitBuiltin_coalesce(EsperEPL2GrammarParser.Builtin_coalesceContext ctx) { + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(new ExprCoalesceNode(), ctx, _astExprNodeMap); + } + + public void ExitBuiltin_typeof(EsperEPL2GrammarParser.Builtin_typeofContext ctx) { + var typeofNode = new ExprTypeofNode(); + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(typeofNode, ctx, _astExprNodeMap); + } + + public void ExitBuiltin_avedev(EsperEPL2GrammarParser.Builtin_avedevContext ctx) { + var aggregateNode = new ExprAvedevNode(ctx.DISTINCT() != null); + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(aggregateNode, ctx, _astExprNodeMap); + } + + public void ExitBuiltin_prevcount(EsperEPL2GrammarParser.Builtin_prevcountContext ctx) { + var previousNode = new ExprPreviousNode(ExprPreviousNodePreviousType.PREVCOUNT); + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(previousNode, ctx, _astExprNodeMap); + } + + public void ExitBuiltin_stddev(EsperEPL2GrammarParser.Builtin_stddevContext ctx) { + var aggregateNode = new ExprStddevNode(ctx.DISTINCT() != null); + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(aggregateNode, ctx, _astExprNodeMap); + } + + public void ExitBuiltin_sum(EsperEPL2GrammarParser.Builtin_sumContext ctx) { + var aggregateNode = new ExprSumNode(ctx.DISTINCT() != null); + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(aggregateNode, ctx, _astExprNodeMap); + } + + public void ExitBuiltin_exists(EsperEPL2GrammarParser.Builtin_existsContext ctx) { + var existsNode = new ExprPropertyExistsNode(); + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(existsNode, ctx, _astExprNodeMap); + } + + public void ExitBuiltin_prior(EsperEPL2GrammarParser.Builtin_priorContext ctx) { + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(new ExprPriorNode(), ctx, _astExprNodeMap); + } + + public void ExitBuiltin_instanceof(EsperEPL2GrammarParser.Builtin_instanceofContext ctx) { + // get class identifiers + var classes = new List(); + IList classCtxs = ctx.classIdentifier(); + foreach (var classCtx in classCtxs) { + classes.Add(ASTUtil.UnescapeClassIdent(classCtx)); + } + + string[] idents = classes.ToArray(); + var instanceofNode = new ExprInstanceofNode(idents); + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(instanceofNode, ctx, _astExprNodeMap); + } + + public void ExitBuiltin_currts(EsperEPL2GrammarParser.Builtin_currtsContext ctx) { + var timeNode = new ExprTimestampNode(); + if (ctx.chainedFunction() != null) { + HandleChainedFunction(ctx, ctx.chainedFunction(), timeNode); + } else { + _astExprNodeMap.Put(ctx, timeNode); + } + } + + public void ExitBuiltin_median(EsperEPL2GrammarParser.Builtin_medianContext ctx) { + var aggregateNode = new ExprMedianNode(ctx.DISTINCT() != null); + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(aggregateNode, ctx, _astExprNodeMap); + } + + public void ExitBuiltin_firstlastwindow(EsperEPL2GrammarParser.Builtin_firstlastwindowContext ctx) { + AggregationStateType? stateType = AggregationStateTypeExtensions.FromString(ctx.firstLastWindowAggregation().q.Text, true); + var expr = new ExprAggMultiFunctionLinearAccessNode(stateType.Value); + ASTExprHelper.ExprCollectAddSubNodes(expr, ctx.firstLastWindowAggregation().expressionListWithNamed(), _astExprNodeMap); + if (ctx.firstLastWindowAggregation().chainedFunction() != null) { + HandleChainedFunction(ctx, ctx.firstLastWindowAggregation().chainedFunction(), expr); + } else { + _astExprNodeMap.Put(ctx, expr); + } + } + + public void ExitBuiltin_avg(EsperEPL2GrammarParser.Builtin_avgContext ctx) { + var aggregateNode = new ExprAvgNode(ctx.DISTINCT() != null); + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(aggregateNode, ctx, _astExprNodeMap); + } + + public void ExitBuiltin_cast(EsperEPL2GrammarParser.Builtin_castContext ctx) { + var classIdent = ASTUtil.UnescapeClassIdent(ctx.classIdentifier()); + var castNode = new ExprCastNode(classIdent); + if (ctx.chainedFunction() != null) { + ASTExprHelper.ExprCollectAddSubNodes(castNode, ctx.expression(), _astExprNodeMap); + ASTExprHelper.ExprCollectAddSingle(castNode, ctx.expressionNamedParameter(), _astExprNodeMap); + HandleChainedFunction(ctx, ctx.chainedFunction(), castNode); + } else { + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(castNode, ctx, _astExprNodeMap); + } + } + + public void ExitBuiltin_cnt(EsperEPL2GrammarParser.Builtin_cntContext ctx) { + var aggregateNode = new ExprCountNode(ctx.DISTINCT() != null); + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(aggregateNode, ctx, _astExprNodeMap); + } + + public void ExitBuiltin_prev(EsperEPL2GrammarParser.Builtin_prevContext ctx) { + var previousNode = new ExprPreviousNode(ExprPreviousNodePreviousType.PREV); + if (ctx.chainedFunction() != null) { + ASTExprHelper.ExprCollectAddSubNodesExpressionCtx(previousNode, ctx.expression(), _astExprNodeMap); + HandleChainedFunction(ctx, ctx.chainedFunction(), previousNode); + } else { + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(previousNode, ctx, _astExprNodeMap); + } + } + + public void ExitBuiltin_istream(EsperEPL2GrammarParser.Builtin_istreamContext ctx) { + var istreamNode = new ExprIStreamNode(); + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(istreamNode, ctx, _astExprNodeMap); + } + + public void ExitBuiltin_prevwindow(EsperEPL2GrammarParser.Builtin_prevwindowContext ctx) { + var previousNode = new ExprPreviousNode(ExprPreviousNodePreviousType.PREVWINDOW); + if (ctx.chainedFunction() != null) { + ASTExprHelper.ExprCollectAddSubNodes(previousNode, ctx.expression(), _astExprNodeMap); + HandleChainedFunction(ctx, ctx.chainedFunction(), previousNode); + } else { + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(previousNode, ctx, _astExprNodeMap); + } + } + + public void ExitBuiltin_prevtail(EsperEPL2GrammarParser.Builtin_prevtailContext ctx) { + var previousNode = new ExprPreviousNode(ExprPreviousNodePreviousType.PREVTAIL); + if (ctx.chainedFunction() != null) { + ASTExprHelper.ExprCollectAddSubNodesExpressionCtx(previousNode, ctx.expression(), _astExprNodeMap); + HandleChainedFunction(ctx, ctx.chainedFunction(), previousNode); + } else { + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(previousNode, ctx, _astExprNodeMap); + } + } + + private PatternLevelAnnotationFlags GetPatternFlags(IEnumerable ctxList) { + var flags = new PatternLevelAnnotationFlags(); + if (ctxList != null) { + foreach (var ctx in ctxList) { + var desc = ASTAnnotationHelper.Walk(ctx, _engineImportService); + PatternLevelAnnotationUtil.ValidateSetFlags(flags, desc.Name); + } + } + return flags; + } + + private UniformPair GetOnExprWindowName(EsperEPL2GrammarParser.OnExprContext ctx) { + if (ctx.onDeleteExpr() != null) { + return GetOnExprWindowName(ctx.onDeleteExpr().onExprFrom()); + } + if (ctx.onSelectExpr() != null && ctx.onSelectExpr().onExprFrom() != null) { + return GetOnExprWindowName(ctx.onSelectExpr().onExprFrom()); + } + if (ctx.onUpdateExpr() != null) { + var alias = ctx.onUpdateExpr().i; + return new UniformPair(ctx.onUpdateExpr().n.Text, alias != null ? alias.Text : null); + } + return null; + } + + private UniformPair GetOnExprWindowName(EsperEPL2GrammarParser.OnExprFromContext ctx) { + var windowName = ctx.n.Text; + var windowStreamName = ctx.i != null ? ctx.i.Text : null; + return new UniformPair(windowName, windowStreamName); + } + + private string GetDefaultDialect() { + return _configurationInformation.EngineDefaults.Scripts.DefaultDialect; + } + + private void HandleMergeMatchedUnmatched(EsperEPL2GrammarParser.ExpressionContext expression, bool b) { + if (_mergeMatcheds == null) { + _mergeMatcheds = new List(); + } + ExprNode filterSpec = null; + if (expression != null) { + filterSpec = ASTExprHelper.ExprCollectSubNodes(expression, 0, _astExprNodeMap)[0]; + } + _mergeMatcheds.Add(new OnTriggerMergeMatched(b, filterSpec, _mergeActions)); + _mergeActions = null; + } + + private void HandleMergeInsert(EsperEPL2GrammarParser.MergeInsertContext mergeInsertContext) { + ExprNode whereCond = null; + if (mergeInsertContext.whereClause() != null) { + whereCond = ASTExprHelper.ExprCollectSubNodes(mergeInsertContext.whereClause(), 0, _astExprNodeMap)[0]; + } + var expressions = new List(_statementSpec.SelectClauseSpec.SelectExprList); + _statementSpec.SelectClauseSpec.SelectExprList.Clear(); + + var optionalInsertName = mergeInsertContext.classIdentifier() != null ? ASTUtil.UnescapeClassIdent(mergeInsertContext.classIdentifier()) : null; + IList columsList = ASTUtil.GetIdentList(mergeInsertContext.columnList()); + _mergeActions.Add(new OnTriggerMergeActionInsert(whereCond, optionalInsertName, columsList, expressions)); + } + + private void HandleChainedFunction(ParserRuleContext parentCtx, EsperEPL2GrammarParser.ChainedFunctionContext chainedCtx, ExprNode childExpression) { + IList chainSpec = ASTLibFunctionHelper.GetLibFuncChain(chainedCtx.libFunctionNoClass(), _astExprNodeMap); + var dotNode = new ExprDotNodeImpl(chainSpec, _configurationInformation.EngineDefaults.Expression.IsDuckTyping, + _configurationInformation.EngineDefaults.Expression.IsUdfCache); + if (childExpression != null) { + dotNode.AddChildNode(childExpression); + } + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(dotNode, parentCtx, _astExprNodeMap); + } + + private void HandleFAFNamedWindowStream(EsperEPL2GrammarParser.ClassIdentifierContext node, IToken i) { + var windowName = ASTUtil.UnescapeClassIdent(node); + var alias = i != null ? i.Text : null; + _statementSpec.StreamSpecs.Add(new FilterStreamSpecRaw(new FilterSpecRaw(windowName, Collections.GetEmptyList(), null), _viewSpecs.ToArray(), alias, StreamSpecOptions.DEFAULT)); + } + + public void ExitFafInsert(EsperEPL2GrammarParser.FafInsertContext ctx) { + IList valueExprs = ctx.expressionList().expression(); + foreach (var valueExpr in valueExprs) { + var expr = ASTExprHelper.ExprCollectSubNodes(valueExpr, 0, _astExprNodeMap)[0]; + _statementSpec.SelectClauseSpec.Add(new SelectClauseExprRawSpec(expr, null, false)); + } + _statementSpec.FireAndForgetSpec = new FireAndForgetSpecInsert(true); + } + + internal void End() { + if (_astExprNodeMap.Count > 1) { + throw ASTWalkException.From("Unexpected AST tree contains left over child elements," + + " not all expression nodes have been removed from AST-to-expression nodes map"); + } + if (_astPatternNodeMap.Count > 1) { + throw ASTWalkException.From("Unexpected AST tree contains left over child elements," + + " not all pattern nodes have been removed from AST-to-pattern nodes map"); + } + + // detect insert-into fire-and-forget query + if (_statementSpec.InsertIntoDesc != null && _statementSpec.StreamSpecs.IsEmpty() && _statementSpec.FireAndForgetSpec == null) { + _statementSpec.FireAndForgetSpec = new FireAndForgetSpecInsert(false); + } + + _statementSpec.SubstitutionParameters = _substitutionParamNodes; + } + + public void ExitBuiltin_grouping(EsperEPL2GrammarParser.Builtin_groupingContext ctx) { + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(new ExprGroupingNode(), ctx, _astExprNodeMap); + } + + + public void ExitBuiltin_groupingid(EsperEPL2GrammarParser.Builtin_groupingidContext ctx) { + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(new ExprGroupingIdNode(), ctx, _astExprNodeMap); + } + + public void ExitIntoTableExpr(EsperEPL2GrammarParser.IntoTableExprContext ctx) { + var name = ctx.i.Text; + _statementSpec.IntoTableSpec = new IntoTableSpec(name); + } + + public void ExitCreateTableExpr(EsperEPL2GrammarParser.CreateTableExprContext ctx) { + var tableName = ctx.n.Text; + + // obtain item declarations + IList cols = ASTTableHelper.GetColumns(ctx.createTableColumnList().createTableColumn(), _astExprNodeMap, _engineImportService); + _statementSpec.CreateTableDesc = new CreateTableDesc(tableName, cols); + } + + public void ExitJsonobject(EsperEPL2GrammarParser.JsonobjectContext ctx) { + var node = new ExprConstantNodeImpl(ASTJsonHelper.WalkObject(_tokenStream, ctx)); + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(node, ctx, _astExprNodeMap); + } + + public void ExitPropertyStreamSelector(EsperEPL2GrammarParser.PropertyStreamSelectorContext ctx) { + var streamWildcard = ctx.s.Text; + var node = new ExprStreamUnderlyingNodeImpl(streamWildcard, true); + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(node, ctx, _astExprNodeMap); + } + + public void ExitExpressionNamedParameter(EsperEPL2GrammarParser.ExpressionNamedParameterContext ctx) { + var named = new ExprNamedParameterNodeImpl(ctx.IDENT().GetText()); + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(named, ctx, _astExprNodeMap); + } + + public void ExitExpressionNamedParameterWithTime(EsperEPL2GrammarParser.ExpressionNamedParameterWithTimeContext ctx) { + var named = new ExprNamedParameterNodeImpl(ctx.IDENT().GetText()); + ASTExprHelper.ExprCollectAddSubNodesAddParentNode(named, ctx, _astExprNodeMap); + } + + private ExprIdentNode ValidateOuterJoinGetIdentNode(ExprNode exprNode) { + if (exprNode is ExprIdentNode) { + return (ExprIdentNode) exprNode; + } + if (exprNode is ExprTableAccessNodeSubprop) { + var subprop = (ExprTableAccessNodeSubprop) exprNode; + return new ExprIdentNodeImpl(subprop.SubpropName, subprop.TableName); + } + throw ASTWalkException.From("Failed to validated 'on'-keyword expressions in outer join, expected identifiers only"); + } + + public void EnterContextExpr(EsperEPL2GrammarParser.ContextExprContext ctx) { + } + + public void EnterExpressionList(EsperEPL2GrammarParser.ExpressionListContext ctx) { + } + + public void ExitExpressionList(EsperEPL2GrammarParser.ExpressionListContext ctx) { + } + + public void EnterSelectionList(EsperEPL2GrammarParser.SelectionListContext ctx) { + } + + public void ExitSelectionList(EsperEPL2GrammarParser.SelectionListContext ctx) { + } + + public void EnterEvalRelationalExpression(EsperEPL2GrammarParser.EvalRelationalExpressionContext ctx) { + } + + public void EnterPatternInclusionExpression(EsperEPL2GrammarParser.PatternInclusionExpressionContext ctx) { + } + + public void ExitPatternInclusionExpression(EsperEPL2GrammarParser.PatternInclusionExpressionContext ctx) { + } + + public void EnterLibFunction(EsperEPL2GrammarParser.LibFunctionContext ctx) { + } + + public void EnterSelectionListElement(EsperEPL2GrammarParser.SelectionListElementContext ctx) { + } + + public void ExitSelectionListElement(EsperEPL2GrammarParser.SelectionListElementContext ctx) { + } + + public void EnterGopOutTypeList(EsperEPL2GrammarParser.GopOutTypeListContext ctx) { + } + + public void ExitGopOutTypeList(EsperEPL2GrammarParser.GopOutTypeListContext ctx) { + } + + public void EnterGopOutTypeItem(EsperEPL2GrammarParser.GopOutTypeItemContext ctx) { + } + + public void ExitGopOutTypeItem(EsperEPL2GrammarParser.GopOutTypeItemContext ctx) { + } + + public void EnterMatchRecog(EsperEPL2GrammarParser.MatchRecogContext ctx) { + } + + public void EnterJsonmembers(EsperEPL2GrammarParser.JsonmembersContext ctx) { + } + + public void ExitJsonmembers(EsperEPL2GrammarParser.JsonmembersContext ctx) { + } + + public void EnterNumber(EsperEPL2GrammarParser.NumberContext ctx) { + } + + public void ExitNumber(EsperEPL2GrammarParser.NumberContext ctx) { + } + + public void EnterVariantList(EsperEPL2GrammarParser.VariantListContext ctx) { + } + + public void ExitVariantList(EsperEPL2GrammarParser.VariantListContext ctx) { + } + + public void EnterMatchRecogPartitionBy(EsperEPL2GrammarParser.MatchRecogPartitionByContext ctx) { + } + + public void EnterOutputLimitAfter(EsperEPL2GrammarParser.OutputLimitAfterContext ctx) { + } + + public void ExitOutputLimitAfter(EsperEPL2GrammarParser.OutputLimitAfterContext ctx) { + } + + public void EnterCreateColumnList(EsperEPL2GrammarParser.CreateColumnListContext ctx) { + } + + public void ExitCreateColumnList(EsperEPL2GrammarParser.CreateColumnListContext ctx) { + } + + public void EnterMergeMatchedItem(EsperEPL2GrammarParser.MergeMatchedItemContext ctx) { + } + + public void EnterMatchRecogMatchesSelection(EsperEPL2GrammarParser.MatchRecogMatchesSelectionContext ctx) { + } + + public void ExitMatchRecogMatchesSelection(EsperEPL2GrammarParser.MatchRecogMatchesSelectionContext ctx) { + } + + public void EnterClassIdentifier(EsperEPL2GrammarParser.ClassIdentifierContext ctx) { + } + + public void ExitClassIdentifier(EsperEPL2GrammarParser.ClassIdentifierContext ctx) { + } + + public void EnterDatabaseJoinExpression(EsperEPL2GrammarParser.DatabaseJoinExpressionContext ctx) { + } + + public void ExitDatabaseJoinExpression(EsperEPL2GrammarParser.DatabaseJoinExpressionContext ctx) { + } + + public void EnterMatchRecogDefineItem(EsperEPL2GrammarParser.MatchRecogDefineItemContext ctx) { + } + + public void EnterLibFunctionArgs(EsperEPL2GrammarParser.LibFunctionArgsContext ctx) { + } + + public void ExitLibFunctionArgs(EsperEPL2GrammarParser.LibFunctionArgsContext ctx) { + } + + public void EnterMergeUnmatchedItem(EsperEPL2GrammarParser.MergeUnmatchedItemContext ctx) { + } + + public void EnterHavingClause(EsperEPL2GrammarParser.HavingClauseContext ctx) { + } + + public void EnterMatchRecogMeasureItem(EsperEPL2GrammarParser.MatchRecogMeasureItemContext ctx) { + } + + public void EnterMatchRecogMatchesInterval(EsperEPL2GrammarParser.MatchRecogMatchesIntervalContext ctx) { + } + + public void ExitMatchRecogMatchesInterval(EsperEPL2GrammarParser.MatchRecogMatchesIntervalContext ctx) { + } + + public void EnterObserverExpression(EsperEPL2GrammarParser.ObserverExpressionContext ctx) { + } + + public void EnterMatchRecogPatternNested(EsperEPL2GrammarParser.MatchRecogPatternNestedContext ctx) { + } + + public void EnterCreateContextFilter(EsperEPL2GrammarParser.CreateContextFilterContext ctx) { + } + + public void ExitCreateContextFilter(EsperEPL2GrammarParser.CreateContextFilterContext ctx) { + } + + public void EnterEvalOrExpression(EsperEPL2GrammarParser.EvalOrExpressionContext ctx) { + } + + public void EnterExpressionDef(EsperEPL2GrammarParser.ExpressionDefContext ctx) { + } + + public void ExitExpressionDef(EsperEPL2GrammarParser.ExpressionDefContext ctx) { + } + + public void EnterOutputLimitAndTerm(EsperEPL2GrammarParser.OutputLimitAndTermContext ctx) { + } + + public void ExitOutputLimitAndTerm(EsperEPL2GrammarParser.OutputLimitAndTermContext ctx) { + } + + public void EnterNumericListParameter(EsperEPL2GrammarParser.NumericListParameterContext ctx) { + } + + public void ExitNumericListParameter(EsperEPL2GrammarParser.NumericListParameterContext ctx) { + } + + public void EnterTimePeriod(EsperEPL2GrammarParser.TimePeriodContext ctx) { + } + + public void EnterEventPropertyAtomic(EsperEPL2GrammarParser.EventPropertyAtomicContext ctx) { + } + + public void ExitEventPropertyAtomic(EsperEPL2GrammarParser.EventPropertyAtomicContext ctx) { + } + + public void EnterSubSelectGroupExpression(EsperEPL2GrammarParser.SubSelectGroupExpressionContext ctx) { + } + + public void ExitSubSelectGroupExpression(EsperEPL2GrammarParser.SubSelectGroupExpressionContext ctx) { + } + + public void EnterOuterJoinList(EsperEPL2GrammarParser.OuterJoinListContext ctx) { + } + + public void ExitOuterJoinList(EsperEPL2GrammarParser.OuterJoinListContext ctx) { + } + + public void EnterSelectionListElementExpr(EsperEPL2GrammarParser.SelectionListElementExprContext ctx) { + } + + public void EnterEventFilterExpression(EsperEPL2GrammarParser.EventFilterExpressionContext ctx) { + } + + public void EnterGopParamsItemList(EsperEPL2GrammarParser.GopParamsItemListContext ctx) { + } + + public void ExitGopParamsItemList(EsperEPL2GrammarParser.GopParamsItemListContext ctx) { + } + + public void EnterMatchRecogPatternConcat(EsperEPL2GrammarParser.MatchRecogPatternConcatContext ctx) { + } + + public void EnterNumberconstant(EsperEPL2GrammarParser.NumberconstantContext ctx) { + } + + public void EnterOnSetAssignment(EsperEPL2GrammarParser.OnSetAssignmentContext ctx) { + } + + public void ExitOnSetAssignment(EsperEPL2GrammarParser.OnSetAssignmentContext ctx) { + } + + public void EnterContextContextNested(EsperEPL2GrammarParser.ContextContextNestedContext ctx) { + } + + public void ExitContextContextNested(EsperEPL2GrammarParser.ContextContextNestedContext ctx) { + } + + public void EnterExpressionWithTime(EsperEPL2GrammarParser.ExpressionWithTimeContext ctx) { + } + + public void ExitExpressionWithTime(EsperEPL2GrammarParser.ExpressionWithTimeContext ctx) { + } + + public void EnterMatchRecogPattern(EsperEPL2GrammarParser.MatchRecogPatternContext ctx) { + } + + public void EnterMergeInsert(EsperEPL2GrammarParser.MergeInsertContext ctx) { + } + + public void ExitMergeInsert(EsperEPL2GrammarParser.MergeInsertContext ctx) { + } + + public void EnterOrderByListExpr(EsperEPL2GrammarParser.OrderByListExprContext ctx) { + } + + public void ExitOrderByListExpr(EsperEPL2GrammarParser.OrderByListExprContext ctx) { + } + + public void EnterElementValuePairsEnum(EsperEPL2GrammarParser.ElementValuePairsEnumContext ctx) { + } + + public void ExitElementValuePairsEnum(EsperEPL2GrammarParser.ElementValuePairsEnumContext ctx) { + } + + public void EnterDistinctExpressionAtom(EsperEPL2GrammarParser.DistinctExpressionAtomContext ctx) { + } + + public void ExitDistinctExpressionAtom(EsperEPL2GrammarParser.DistinctExpressionAtomContext ctx) { + } + + public void EnterExpression(EsperEPL2GrammarParser.ExpressionContext ctx) { + } + + public void ExitExpression(EsperEPL2GrammarParser.ExpressionContext ctx) { + } + + public void EnterWhereClause(EsperEPL2GrammarParser.WhereClauseContext ctx) { + } + + public void EnterCreateColumnListElement(EsperEPL2GrammarParser.CreateColumnListElementContext ctx) { + } + + public void ExitCreateColumnListElement(EsperEPL2GrammarParser.CreateColumnListElementContext ctx) { + } + + public void EnterGopList(EsperEPL2GrammarParser.GopListContext ctx) { + } + + public void ExitGopList(EsperEPL2GrammarParser.GopListContext ctx) { + } + + public void EnterPatternFilterAnnotation(EsperEPL2GrammarParser.PatternFilterAnnotationContext ctx) { + } + + public void ExitPatternFilterAnnotation(EsperEPL2GrammarParser.PatternFilterAnnotationContext ctx) { + } + + public void EnterElementValueArrayEnum(EsperEPL2GrammarParser.ElementValueArrayEnumContext ctx) { + } + + public void ExitElementValueArrayEnum(EsperEPL2GrammarParser.ElementValueArrayEnumContext ctx) { + } + + public void EnterHourPart(EsperEPL2GrammarParser.HourPartContext ctx) { + } + + public void ExitHourPart(EsperEPL2GrammarParser.HourPartContext ctx) { + } + + public void EnterOnDeleteExpr(EsperEPL2GrammarParser.OnDeleteExprContext ctx) { + } + + public void ExitOnDeleteExpr(EsperEPL2GrammarParser.OnDeleteExprContext ctx) { + } + + public void EnterMatchRecogPatternAtom(EsperEPL2GrammarParser.MatchRecogPatternAtomContext ctx) { + } + + public void EnterGopOutTypeParam(EsperEPL2GrammarParser.GopOutTypeParamContext ctx) { + } + + public void ExitGopOutTypeParam(EsperEPL2GrammarParser.GopOutTypeParamContext ctx) { + } + + public void EnterMergeItem(EsperEPL2GrammarParser.MergeItemContext ctx) { + } + + public void ExitMergeItem(EsperEPL2GrammarParser.MergeItemContext ctx) { + } + + public void EnterYearPart(EsperEPL2GrammarParser.YearPartContext ctx) { + } + + public void ExitYearPart(EsperEPL2GrammarParser.YearPartContext ctx) { + } + + public void EnterEventPropertyOrLibFunction(EsperEPL2GrammarParser.EventPropertyOrLibFunctionContext ctx) { + } + + public void ExitEventPropertyOrLibFunction(EsperEPL2GrammarParser.EventPropertyOrLibFunctionContext ctx) { + } + + public void EnterCreateDataflow(EsperEPL2GrammarParser.CreateDataflowContext ctx) { + } + + public void EnterUpdateExpr(EsperEPL2GrammarParser.UpdateExprContext ctx) { + } + + public void EnterFrequencyOperand(EsperEPL2GrammarParser.FrequencyOperandContext ctx) { + } + + public void EnterOnSetAssignmentList(EsperEPL2GrammarParser.OnSetAssignmentListContext ctx) { + } + + public void ExitOnSetAssignmentList(EsperEPL2GrammarParser.OnSetAssignmentListContext ctx) { + } + + public void EnterPropertyStreamSelector(EsperEPL2GrammarParser.PropertyStreamSelectorContext ctx) { + } + + public void EnterInsertIntoExpr(EsperEPL2GrammarParser.InsertIntoExprContext ctx) { + } + + public void EnterCreateVariableExpr(EsperEPL2GrammarParser.CreateVariableExprContext ctx) { + } + + public void EnterGopParamsItem(EsperEPL2GrammarParser.GopParamsItemContext ctx) { + } + + public void ExitGopParamsItem(EsperEPL2GrammarParser.GopParamsItemContext ctx) { + } + + public void EnterOnStreamExpr(EsperEPL2GrammarParser.OnStreamExprContext ctx) { + } + + public void EnterPropertyExpressionAtomic(EsperEPL2GrammarParser.PropertyExpressionAtomicContext ctx) { + } + + public void EnterGopDetail(EsperEPL2GrammarParser.GopDetailContext ctx) { + } + + public void ExitGopDetail(EsperEPL2GrammarParser.GopDetailContext ctx) { + } + + public void EnterGop(EsperEPL2GrammarParser.GopContext ctx) { + } + + public void ExitGop(EsperEPL2GrammarParser.GopContext ctx) { + } + + public void EnterOutputClauseInsert(EsperEPL2GrammarParser.OutputClauseInsertContext ctx) { + } + + public void ExitOutputClauseInsert(EsperEPL2GrammarParser.OutputClauseInsertContext ctx) { + } + + public void EnterEplExpression(EsperEPL2GrammarParser.EplExpressionContext ctx) { + } + + public void ExitEplExpression(EsperEPL2GrammarParser.EplExpressionContext ctx) { + } + + public void EnterOnMergeExpr(EsperEPL2GrammarParser.OnMergeExprContext ctx) { + } + + public void ExitOnMergeExpr(EsperEPL2GrammarParser.OnMergeExprContext ctx) { + } + + public void EnterFafUpdate(EsperEPL2GrammarParser.FafUpdateContext ctx) { + } + + public void EnterCreateSelectionList(EsperEPL2GrammarParser.CreateSelectionListContext ctx) { + } + + public void ExitCreateSelectionList(EsperEPL2GrammarParser.CreateSelectionListContext ctx) { + } + + public void EnterOnSetExpr(EsperEPL2GrammarParser.OnSetExprContext ctx) { + } + + public void ExitOnSetExpr(EsperEPL2GrammarParser.OnSetExprContext ctx) { + } + + public void EnterBitWiseExpression(EsperEPL2GrammarParser.BitWiseExpressionContext ctx) { + } + + public void EnterChainedFunction(EsperEPL2GrammarParser.ChainedFunctionContext ctx) { + } + + public void ExitChainedFunction(EsperEPL2GrammarParser.ChainedFunctionContext ctx) { + } + + public void EnterMatchRecogPatternUnary(EsperEPL2GrammarParser.MatchRecogPatternUnaryContext ctx) { + } + + public void ExitMatchRecogPatternUnary(EsperEPL2GrammarParser.MatchRecogPatternUnaryContext ctx) { + } + + public void EnterBetweenList(EsperEPL2GrammarParser.BetweenListContext ctx) { + } + + public void ExitBetweenList(EsperEPL2GrammarParser.BetweenListContext ctx) { + } + + public void EnterSecondPart(EsperEPL2GrammarParser.SecondPartContext ctx) { + } + + public void ExitSecondPart(EsperEPL2GrammarParser.SecondPartContext ctx) { + } + + public void EnterEvalEqualsExpression(EsperEPL2GrammarParser.EvalEqualsExpressionContext ctx) { + } + + public void EnterGopConfig(EsperEPL2GrammarParser.GopConfigContext ctx) { + } + + public void EnterMergeMatched(EsperEPL2GrammarParser.MergeMatchedContext ctx) { + } + + public void EnterCreateSelectionListElement(EsperEPL2GrammarParser.CreateSelectionListElementContext ctx) { + } + + public void EnterFafDelete(EsperEPL2GrammarParser.FafDeleteContext ctx) { + } + + public void EnterDayPart(EsperEPL2GrammarParser.DayPartContext ctx) { + } + + public void ExitDayPart(EsperEPL2GrammarParser.DayPartContext ctx) { + } + + public void EnterConstant(EsperEPL2GrammarParser.ConstantContext ctx) { + } + + public void EnterGopOut(EsperEPL2GrammarParser.GopOutContext ctx) { + } + + public void ExitGopOut(EsperEPL2GrammarParser.GopOutContext ctx) { + } + + public void EnterGuardWhereExpression(EsperEPL2GrammarParser.GuardWhereExpressionContext ctx) { + } + + public void ExitGuardWhereExpression(EsperEPL2GrammarParser.GuardWhereExpressionContext ctx) { + } + + public void EnterKeywordAllowedIdent(EsperEPL2GrammarParser.KeywordAllowedIdentContext ctx) { + } + + public void ExitKeywordAllowedIdent(EsperEPL2GrammarParser.KeywordAllowedIdentContext ctx) { + } + + public void EnterCreateContextGroupItem(EsperEPL2GrammarParser.CreateContextGroupItemContext ctx) { + } + + public void ExitCreateContextGroupItem(EsperEPL2GrammarParser.CreateContextGroupItemContext ctx) { + } + + public void EnterEvalAndExpression(EsperEPL2GrammarParser.EvalAndExpressionContext ctx) { + } + + public void EnterMultiplyExpression(EsperEPL2GrammarParser.MultiplyExpressionContext ctx) { + } + + public void EnterExpressionLambdaDecl(EsperEPL2GrammarParser.ExpressionLambdaDeclContext ctx) { + } + + public void ExitExpressionLambdaDecl(EsperEPL2GrammarParser.ExpressionLambdaDeclContext ctx) { + } + + public void EnterPropertyExpression(EsperEPL2GrammarParser.PropertyExpressionContext ctx) { + } + + public void ExitPropertyExpression(EsperEPL2GrammarParser.PropertyExpressionContext ctx) { + } + + public void EnterOuterJoinIdentPair(EsperEPL2GrammarParser.OuterJoinIdentPairContext ctx) { + } + + public void ExitOuterJoinIdentPair(EsperEPL2GrammarParser.OuterJoinIdentPairContext ctx) { + } + + public void EnterGopOutItem(EsperEPL2GrammarParser.GopOutItemContext ctx) { + } + + public void ExitGopOutItem(EsperEPL2GrammarParser.GopOutItemContext ctx) { + } + + public void EnterForExpr(EsperEPL2GrammarParser.ForExprContext ctx) { + } + + public void EnterPropertyExpressionSelect(EsperEPL2GrammarParser.PropertyExpressionSelectContext ctx) { + } + + public void ExitPropertyExpressionSelect(EsperEPL2GrammarParser.PropertyExpressionSelectContext ctx) { + } + + public void EnterExpressionQualifyable(EsperEPL2GrammarParser.ExpressionQualifyableContext ctx) { + } + + public void EnterExpressionDialect(EsperEPL2GrammarParser.ExpressionDialectContext ctx) { + } + + public void ExitExpressionDialect(EsperEPL2GrammarParser.ExpressionDialectContext ctx) { + } + + public void EnterStartEventPropertyRule(EsperEPL2GrammarParser.StartEventPropertyRuleContext ctx) { + } + + public void ExitStartEventPropertyRule(EsperEPL2GrammarParser.StartEventPropertyRuleContext ctx) { + } + + public void EnterPropertySelectionListElement(EsperEPL2GrammarParser.PropertySelectionListElementContext ctx) { + } + + public void EnterExpressionDecl(EsperEPL2GrammarParser.ExpressionDeclContext ctx) { + } + + public void EnterSubstitution(EsperEPL2GrammarParser.SubstitutionContext ctx) { + } + + public void EnterCrontabLimitParameterSet(EsperEPL2GrammarParser.CrontabLimitParameterSetContext ctx) { + } + + public void ExitCrontabLimitParameterSet(EsperEPL2GrammarParser.CrontabLimitParameterSetContext ctx) { + } + + public void EnterWeekDayOperator(EsperEPL2GrammarParser.WeekDayOperatorContext ctx) { + } + + public void EnterWhenClause(EsperEPL2GrammarParser.WhenClauseContext ctx) { + } + + public void ExitWhenClause(EsperEPL2GrammarParser.WhenClauseContext ctx) { + } + + public void EnterNewAssign(EsperEPL2GrammarParser.NewAssignContext ctx) { + } + + public void ExitNewAssign(EsperEPL2GrammarParser.NewAssignContext ctx) { + } + + public void EnterLastWeekdayOperand(EsperEPL2GrammarParser.LastWeekdayOperandContext ctx) { + } + + public void EnterGroupByListExpr(EsperEPL2GrammarParser.GroupByListExprContext ctx) { + } + + public void EnterStreamSelector(EsperEPL2GrammarParser.StreamSelectorContext ctx) { + } + + public void EnterStartJsonValueRule(EsperEPL2GrammarParser.StartJsonValueRuleContext ctx) { + } + + public void ExitStartJsonValueRule(EsperEPL2GrammarParser.StartJsonValueRuleContext ctx) { + } + + public void EnterStreamExpression(EsperEPL2GrammarParser.StreamExpressionContext ctx) { + } + + public void EnterOuterJoinIdent(EsperEPL2GrammarParser.OuterJoinIdentContext ctx) { + } + + public void ExitOuterJoinIdent(EsperEPL2GrammarParser.OuterJoinIdentContext ctx) { + } + + public void EnterCreateIndexColumnList(EsperEPL2GrammarParser.CreateIndexColumnListContext ctx) { + } + + public void ExitCreateIndexColumnList(EsperEPL2GrammarParser.CreateIndexColumnListContext ctx) { + } + + public void EnterColumnList(EsperEPL2GrammarParser.ColumnListContext ctx) { + } + + public void ExitColumnList(EsperEPL2GrammarParser.ColumnListContext ctx) { + } + + public void EnterPatternFilterExpression(EsperEPL2GrammarParser.PatternFilterExpressionContext ctx) { + } + + public void EnterJsonpair(EsperEPL2GrammarParser.JsonpairContext ctx) { + } + + public void ExitJsonpair(EsperEPL2GrammarParser.JsonpairContext ctx) { + } + + public void EnterOnSelectExpr(EsperEPL2GrammarParser.OnSelectExprContext ctx) { + } + + public void EnterElementValuePairEnum(EsperEPL2GrammarParser.ElementValuePairEnumContext ctx) { + } + + public void ExitElementValuePairEnum(EsperEPL2GrammarParser.ElementValuePairEnumContext ctx) { + } + + public void EnterStartPatternExpressionRule(EsperEPL2GrammarParser.StartPatternExpressionRuleContext ctx) { + } + + public void EnterSelectionListElementAnno(EsperEPL2GrammarParser.SelectionListElementAnnoContext ctx) { + } + + public void ExitSelectionListElementAnno(EsperEPL2GrammarParser.SelectionListElementAnnoContext ctx) { + } + + public void EnterOutputLimit(EsperEPL2GrammarParser.OutputLimitContext ctx) { + } + + public void EnterCreateContextDistinct(EsperEPL2GrammarParser.CreateContextDistinctContext ctx) { + } + + public void ExitCreateContextDistinct(EsperEPL2GrammarParser.CreateContextDistinctContext ctx) { + } + + public void EnterJsonelements(EsperEPL2GrammarParser.JsonelementsContext ctx) { + } + + public void ExitJsonelements(EsperEPL2GrammarParser.JsonelementsContext ctx) { + } + + public void EnterNumericParameterList(EsperEPL2GrammarParser.NumericParameterListContext ctx) { + } + + public void EnterLibFunctionWithClass(EsperEPL2GrammarParser.LibFunctionWithClassContext ctx) { + } + + public void ExitLibFunctionWithClass(EsperEPL2GrammarParser.LibFunctionWithClassContext ctx) { + } + + public void EnterStringconstant(EsperEPL2GrammarParser.StringconstantContext ctx) { + } + + public void ExitStringconstant(EsperEPL2GrammarParser.StringconstantContext ctx) { + } + + public void EnterCreateSchemaExpr(EsperEPL2GrammarParser.CreateSchemaExprContext ctx) { + } + + public void EnterElseClause(EsperEPL2GrammarParser.ElseClauseContext ctx) { + } + + public void ExitElseClause(EsperEPL2GrammarParser.ElseClauseContext ctx) { + } + + public void EnterGuardWhileExpression(EsperEPL2GrammarParser.GuardWhileExpressionContext ctx) { + } + + public void ExitGuardWhileExpression(EsperEPL2GrammarParser.GuardWhileExpressionContext ctx) { + } + + public void EnterCreateWindowExprModelAfter(EsperEPL2GrammarParser.CreateWindowExprModelAfterContext ctx) { + } + + public void ExitCreateWindowExprModelAfter(EsperEPL2GrammarParser.CreateWindowExprModelAfterContext ctx) { + } + + public void EnterMatchRecogMatchesAfterSkip(EsperEPL2GrammarParser.MatchRecogMatchesAfterSkipContext ctx) { + } + + public void ExitMatchRecogMatchesAfterSkip(EsperEPL2GrammarParser.MatchRecogMatchesAfterSkipContext ctx) { + } + + public void EnterCreateContextDetail(EsperEPL2GrammarParser.CreateContextDetailContext ctx) { + } + + public void ExitCreateContextDetail(EsperEPL2GrammarParser.CreateContextDetailContext ctx) { + } + + public void EnterMonthPart(EsperEPL2GrammarParser.MonthPartContext ctx) { + } + + public void ExitMonthPart(EsperEPL2GrammarParser.MonthPartContext ctx) { + } + + public void EnterPatternExpression(EsperEPL2GrammarParser.PatternExpressionContext ctx) { + } + + public void ExitPatternExpression(EsperEPL2GrammarParser.PatternExpressionContext ctx) { + } + + public void EnterLastOperator(EsperEPL2GrammarParser.LastOperatorContext ctx) { + } + + public void EnterCreateSchemaDef(EsperEPL2GrammarParser.CreateSchemaDefContext ctx) { + } + + public void ExitCreateSchemaDef(EsperEPL2GrammarParser.CreateSchemaDefContext ctx) { + } + + public void EnterEventPropertyIdent(EsperEPL2GrammarParser.EventPropertyIdentContext ctx) { + } + + public void ExitEventPropertyIdent(EsperEPL2GrammarParser.EventPropertyIdentContext ctx) { + } + + public void EnterCreateIndexExpr(EsperEPL2GrammarParser.CreateIndexExprContext ctx) { + } + + public void EnterAtomicExpression(EsperEPL2GrammarParser.AtomicExpressionContext ctx) { + } + + public void ExitAtomicExpression(EsperEPL2GrammarParser.AtomicExpressionContext ctx) { + } + + public void EnterJsonvalue(EsperEPL2GrammarParser.JsonvalueContext ctx) { + } + + public void ExitJsonvalue(EsperEPL2GrammarParser.JsonvalueContext ctx) { + } + + public void EnterLibFunctionNoClass(EsperEPL2GrammarParser.LibFunctionNoClassContext ctx) { + } + + public void ExitLibFunctionNoClass(EsperEPL2GrammarParser.LibFunctionNoClassContext ctx) { + } + + public void EnterElementValueEnum(EsperEPL2GrammarParser.ElementValueEnumContext ctx) { + } + + public void ExitElementValueEnum(EsperEPL2GrammarParser.ElementValueEnumContext ctx) { + } + + public void EnterOnUpdateExpr(EsperEPL2GrammarParser.OnUpdateExprContext ctx) { + } + + public void ExitOnUpdateExpr(EsperEPL2GrammarParser.OnUpdateExprContext ctx) { + } + + public void EnterAnnotationEnum(EsperEPL2GrammarParser.AnnotationEnumContext ctx) { + } + + public void EnterCreateContextExpr(EsperEPL2GrammarParser.CreateContextExprContext ctx) { + } + + public void EnterLastOperand(EsperEPL2GrammarParser.LastOperandContext ctx) { + } + + public void EnterExpressionWithTimeInclLast(EsperEPL2GrammarParser.ExpressionWithTimeInclLastContext ctx) { + } + + public void ExitExpressionWithTimeInclLast(EsperEPL2GrammarParser.ExpressionWithTimeInclLastContext ctx) { + } + + public void EnterCreateContextPartitionItem(EsperEPL2GrammarParser.CreateContextPartitionItemContext ctx) { + } + + public void ExitCreateContextPartitionItem(EsperEPL2GrammarParser.CreateContextPartitionItemContext ctx) { + } + + public void EnterCreateWindowExpr(EsperEPL2GrammarParser.CreateWindowExprContext ctx) { + } + + public void EnterVariantListElement(EsperEPL2GrammarParser.VariantListElementContext ctx) { + } + + public void ExitVariantListElement(EsperEPL2GrammarParser.VariantListElementContext ctx) { + } + + public void EnterCreateExpressionExpr(EsperEPL2GrammarParser.CreateExpressionExprContext ctx) { + } + + public void EnterRangeOperand(EsperEPL2GrammarParser.RangeOperandContext ctx) { + } + + public void EnterInSubSelectQuery(EsperEPL2GrammarParser.InSubSelectQueryContext ctx) { + } + + public void ExitInSubSelectQuery(EsperEPL2GrammarParser.InSubSelectQueryContext ctx) { + } + + public void EnterEscapableStr(EsperEPL2GrammarParser.EscapableStrContext ctx) { + } + + public void ExitEscapableStr(EsperEPL2GrammarParser.EscapableStrContext ctx) { + } + + public void EnterRowSubSelectExpression(EsperEPL2GrammarParser.RowSubSelectExpressionContext ctx) { + } + + public void EnterUnaryExpression(EsperEPL2GrammarParser.UnaryExpressionContext ctx) { + } + + public void EnterDistinctExpressionList(EsperEPL2GrammarParser.DistinctExpressionListContext ctx) { + } + + public void ExitDistinctExpressionList(EsperEPL2GrammarParser.DistinctExpressionListContext ctx) { + } + + public void ExitOnSelectInsertExpr(EsperEPL2GrammarParser.OnSelectInsertExprContext ctx) { + } + + public void EnterSelectClause(EsperEPL2GrammarParser.SelectClauseContext ctx) { + } + + public void EnterConcatenationExpr(EsperEPL2GrammarParser.ConcatenationExprContext ctx) { + } + + public void EnterStartEPLExpressionRule(EsperEPL2GrammarParser.StartEPLExpressionRuleContext ctx) { + } + + public void ExitStartEPLExpressionRule(EsperEPL2GrammarParser.StartEPLExpressionRuleContext ctx) { + } + + public void EnterSubSelectFilterExpr(EsperEPL2GrammarParser.SubSelectFilterExprContext ctx) { + } + + public void EnterCreateContextCoalesceItem(EsperEPL2GrammarParser.CreateContextCoalesceItemContext ctx) { + } + + public void ExitCreateContextCoalesceItem(EsperEPL2GrammarParser.CreateContextCoalesceItemContext ctx) { + } + + public void EnterMillisecondPart(EsperEPL2GrammarParser.MillisecondPartContext ctx) { + } + + public void ExitMillisecondPart(EsperEPL2GrammarParser.MillisecondPartContext ctx) { + } + + public void EnterMicrosecondPart(EsperEPL2GrammarParser.MicrosecondPartContext ctx) { + } + + public void ExitMicrosecondPart(EsperEPL2GrammarParser.MicrosecondPartContext ctx) { + } + + public void EnterOnExprFrom(EsperEPL2GrammarParser.OnExprFromContext ctx) { + } + + public void ExitOnExprFrom(EsperEPL2GrammarParser.OnExprFromContext ctx) { + } + + public void EnterNegatedExpression(EsperEPL2GrammarParser.NegatedExpressionContext ctx) { + } + + public void EnterSelectExpr(EsperEPL2GrammarParser.SelectExprContext ctx) { + } + + public void EnterMatchRecogMeasures(EsperEPL2GrammarParser.MatchRecogMeasuresContext ctx) { + } + + public void ExitMatchRecogMeasures(EsperEPL2GrammarParser.MatchRecogMeasuresContext ctx) { + } + + public void EnterAdditiveExpression(EsperEPL2GrammarParser.AdditiveExpressionContext ctx) { + } + + public void EnterEventProperty(EsperEPL2GrammarParser.EventPropertyContext ctx) { + } + + public void EnterJsonarray(EsperEPL2GrammarParser.JsonarrayContext ctx) { + } + + public void ExitJsonarray(EsperEPL2GrammarParser.JsonarrayContext ctx) { + } + + public void EnterJsonobject(EsperEPL2GrammarParser.JsonobjectContext ctx) { + } + + public void EnterOuterJoin(EsperEPL2GrammarParser.OuterJoinContext ctx) { + } + + public void EnterEscapableIdent(EsperEPL2GrammarParser.EscapableIdentContext ctx) { + } + + public void ExitEscapableIdent(EsperEPL2GrammarParser.EscapableIdentContext ctx) { + } + + public void EnterFromClause(EsperEPL2GrammarParser.FromClauseContext ctx) { + } + + public void ExitFromClause(EsperEPL2GrammarParser.FromClauseContext ctx) { + } + + public void EnterOnExpr(EsperEPL2GrammarParser.OnExprContext ctx) { + } + + public void EnterGopParamsItemMany(EsperEPL2GrammarParser.GopParamsItemManyContext ctx) { + } + + public void ExitGopParamsItemMany(EsperEPL2GrammarParser.GopParamsItemManyContext ctx) { + } + + public void EnterPropertySelectionList(EsperEPL2GrammarParser.PropertySelectionListContext ctx) { + } + + public void ExitPropertySelectionList(EsperEPL2GrammarParser.PropertySelectionListContext ctx) { + } + + public void EnterWeekPart(EsperEPL2GrammarParser.WeekPartContext ctx) { + } + + public void ExitWeekPart(EsperEPL2GrammarParser.WeekPartContext ctx) { + } + + public void EnterMatchRecogPatternAlteration(EsperEPL2GrammarParser.MatchRecogPatternAlterationContext ctx) { + } + + public void EnterGopParams(EsperEPL2GrammarParser.GopParamsContext ctx) { + } + + public void ExitGopParams(EsperEPL2GrammarParser.GopParamsContext ctx) { + } + + public void EnterCreateContextChoice(EsperEPL2GrammarParser.CreateContextChoiceContext ctx) { + } + + public void ExitCreateContextChoice(EsperEPL2GrammarParser.CreateContextChoiceContext ctx) { + } + + public void EnterCaseExpression(EsperEPL2GrammarParser.CaseExpressionContext ctx) { + } + + public void EnterCreateIndexColumn(EsperEPL2GrammarParser.CreateIndexColumnContext ctx) { + } + + public void ExitCreateIndexColumn(EsperEPL2GrammarParser.CreateIndexColumnContext ctx) { + } + + public void EnterExpressionWithTimeList(EsperEPL2GrammarParser.ExpressionWithTimeListContext ctx) { + } + + public void ExitExpressionWithTimeList(EsperEPL2GrammarParser.ExpressionWithTimeListContext ctx) { + } + + public void EnterGopParamsItemAs(EsperEPL2GrammarParser.GopParamsItemAsContext ctx) { + } + + public void ExitGopParamsItemAs(EsperEPL2GrammarParser.GopParamsItemAsContext ctx) { + } + + public void EnterRowLimit(EsperEPL2GrammarParser.RowLimitContext ctx) { + } + + public void EnterCreateSchemaQual(EsperEPL2GrammarParser.CreateSchemaQualContext ctx) { + } + + public void ExitCreateSchemaQual(EsperEPL2GrammarParser.CreateSchemaQualContext ctx) { + } + + public void EnterMatchUntilRange(EsperEPL2GrammarParser.MatchUntilRangeContext ctx) { + } + + public void ExitMatchUntilRange(EsperEPL2GrammarParser.MatchUntilRangeContext ctx) { + } + + public void EnterMatchRecogDefine(EsperEPL2GrammarParser.MatchRecogDefineContext ctx) { + } + + public void ExitMatchRecogDefine(EsperEPL2GrammarParser.MatchRecogDefineContext ctx) { + } + + public void EnterOrderByListElement(EsperEPL2GrammarParser.OrderByListElementContext ctx) { + } + + public void EnterMinutePart(EsperEPL2GrammarParser.MinutePartContext ctx) { + } + + public void ExitMinutePart(EsperEPL2GrammarParser.MinutePartContext ctx) { + } + + public void EnterMergeUnmatched(EsperEPL2GrammarParser.MergeUnmatchedContext ctx) { + } + + public void EnterMethodJoinExpression(EsperEPL2GrammarParser.MethodJoinExpressionContext ctx) { + } + + public void ExitMethodJoinExpression(EsperEPL2GrammarParser.MethodJoinExpressionContext ctx) { + } + + public void EnterExistsSubSelectExpression(EsperEPL2GrammarParser.ExistsSubSelectExpressionContext ctx) { + } + + public void EnterCreateContextRangePoint(EsperEPL2GrammarParser.CreateContextRangePointContext ctx) { + } + + public void ExitCreateContextRangePoint(EsperEPL2GrammarParser.CreateContextRangePointContext ctx) { + } + + public void EnterLibFunctionArgItem(EsperEPL2GrammarParser.LibFunctionArgItemContext ctx) { + } + + public void ExitLibFunctionArgItem(EsperEPL2GrammarParser.LibFunctionArgItemContext ctx) { + } + + public void EnterRegularJoin(EsperEPL2GrammarParser.RegularJoinContext ctx) { + } + + public void ExitRegularJoin(EsperEPL2GrammarParser.RegularJoinContext ctx) { + } + + public void EnterUpdateDetails(EsperEPL2GrammarParser.UpdateDetailsContext ctx) { + } + + public void ExitUpdateDetails(EsperEPL2GrammarParser.UpdateDetailsContext ctx) { + } + + public void EnterArrayExpression(EsperEPL2GrammarParser.ArrayExpressionContext ctx) { + } + + public void VisitErrorNode(IErrorNode errorNode) { + } + + public void EnterEveryRule(ParserRuleContext parserRuleContext) { + } + + public void ExitEveryRule(ParserRuleContext parserRuleContext) { + } + + public void EnterAndExpression(EsperEPL2GrammarParser.AndExpressionContext ctx) { + } + + public void EnterFollowedByRepeat(EsperEPL2GrammarParser.FollowedByRepeatContext ctx) { + } + + public void ExitFollowedByRepeat(EsperEPL2GrammarParser.FollowedByRepeatContext ctx) { + } + + public void EnterFollowedByExpression(EsperEPL2GrammarParser.FollowedByExpressionContext ctx) { + } + + public void EnterOrExpression(EsperEPL2GrammarParser.OrExpressionContext ctx) { + } + + public void EnterQualifyExpression(EsperEPL2GrammarParser.QualifyExpressionContext ctx) { + } + + public void EnterMatchUntilExpression(EsperEPL2GrammarParser.MatchUntilExpressionContext ctx) { + } + + public void EnterGuardPostFix(EsperEPL2GrammarParser.GuardPostFixContext ctx) { + } + + public void EnterBuiltin_coalesce(EsperEPL2GrammarParser.Builtin_coalesceContext ctx) { + } + + public void EnterBuiltin_typeof(EsperEPL2GrammarParser.Builtin_typeofContext ctx) { + } + + public void EnterBuiltin_avedev(EsperEPL2GrammarParser.Builtin_avedevContext ctx) { + } + + public void EnterBuiltin_prevcount(EsperEPL2GrammarParser.Builtin_prevcountContext ctx) { + } + + public void EnterBuiltin_stddev(EsperEPL2GrammarParser.Builtin_stddevContext ctx) { + } + + public void EnterBuiltin_sum(EsperEPL2GrammarParser.Builtin_sumContext ctx) { + } + + public void EnterBuiltin_exists(EsperEPL2GrammarParser.Builtin_existsContext ctx) { + } + + public void EnterBuiltin_prior(EsperEPL2GrammarParser.Builtin_priorContext ctx) { + } + + public void EnterBuiltin_instanceof(EsperEPL2GrammarParser.Builtin_instanceofContext ctx) { + } + + public void EnterBuiltin_currts(EsperEPL2GrammarParser.Builtin_currtsContext ctx) { + } + + public void EnterBuiltin_median(EsperEPL2GrammarParser.Builtin_medianContext ctx) { + } + + public void EnterFuncIdentChained(EsperEPL2GrammarParser.FuncIdentChainedContext ctx) { + } + + public void ExitFuncIdentChained(EsperEPL2GrammarParser.FuncIdentChainedContext ctx) { + } + + public void EnterFuncIdentTop(EsperEPL2GrammarParser.FuncIdentTopContext ctx) { + } + + public void ExitFuncIdentTop(EsperEPL2GrammarParser.FuncIdentTopContext ctx) { + } + + public void EnterBuiltin_avg(EsperEPL2GrammarParser.Builtin_avgContext ctx) { + } + + public void EnterBuiltin_cast(EsperEPL2GrammarParser.Builtin_castContext ctx) { + } + + public void EnterBuiltin_cnt(EsperEPL2GrammarParser.Builtin_cntContext ctx) { + } + + public void EnterBuiltin_prev(EsperEPL2GrammarParser.Builtin_prevContext ctx) { + } + + public void EnterBuiltin_istream(EsperEPL2GrammarParser.Builtin_istreamContext ctx) { + } + + public void EnterBuiltin_prevwindow(EsperEPL2GrammarParser.Builtin_prevwindowContext ctx) { + } + + public void EnterBuiltin_prevtail(EsperEPL2GrammarParser.Builtin_prevtailContext ctx) { + } + + public void EnterFafInsert(EsperEPL2GrammarParser.FafInsertContext ctx) { + } + + public void EnterGroupByListChoice(EsperEPL2GrammarParser.GroupByListChoiceContext ctx) { + } + + public void ExitGroupByListChoice(EsperEPL2GrammarParser.GroupByListChoiceContext ctx) { + } + + public void EnterGroupBySetsChoice(EsperEPL2GrammarParser.GroupBySetsChoiceContext ctx) { + } + + public void ExitGroupBySetsChoice(EsperEPL2GrammarParser.GroupBySetsChoiceContext ctx) { + } + + public void ExitSelectExpr(EsperEPL2GrammarParser.SelectExprContext ctx) { + } + + public void EnterGroupByCubeOrRollup(EsperEPL2GrammarParser.GroupByCubeOrRollupContext ctx) { + } + + public void ExitGroupByCubeOrRollup(EsperEPL2GrammarParser.GroupByCubeOrRollupContext ctx) { + } + + public void EnterGroupByGroupingSets(EsperEPL2GrammarParser.GroupByGroupingSetsContext ctx) { + } + + public void ExitGroupByGroupingSets(EsperEPL2GrammarParser.GroupByGroupingSetsContext ctx) { + } + + public void EnterGroupByCombinableExpr(EsperEPL2GrammarParser.GroupByCombinableExprContext ctx) { + } + + public void ExitGroupByCombinableExpr(EsperEPL2GrammarParser.GroupByCombinableExprContext ctx) { + } + + public void EnterBuiltin_grouping(EsperEPL2GrammarParser.Builtin_groupingContext ctx) { + } + + public void EnterBuiltin_groupingid(EsperEPL2GrammarParser.Builtin_groupingidContext ctx) { + } + + public void EnterFuncIdentInner(EsperEPL2GrammarParser.FuncIdentInnerContext ctx) { + } + + public void ExitFuncIdentInner(EsperEPL2GrammarParser.FuncIdentInnerContext ctx) { + } + + public void EnterCreateTableColumnPlain(EsperEPL2GrammarParser.CreateTableColumnPlainContext ctx) { + } + + public void ExitCreateTableColumnPlain(EsperEPL2GrammarParser.CreateTableColumnPlainContext ctx) { + } + + public void EnterCreateTableExpr(EsperEPL2GrammarParser.CreateTableExprContext ctx) { + } + + public void EnterCreateTableColumn(EsperEPL2GrammarParser.CreateTableColumnContext ctx) { + } + + public void ExitCreateTableColumn(EsperEPL2GrammarParser.CreateTableColumnContext ctx) { + } + + public void EnterCreateTableColumnList(EsperEPL2GrammarParser.CreateTableColumnListContext ctx) { + } + + public void ExitCreateTableColumnList(EsperEPL2GrammarParser.CreateTableColumnListContext ctx) { + } + + public void EnterIntoTableExpr(EsperEPL2GrammarParser.IntoTableExprContext ctx) { + } + + public void EnterSubstitutionCanChain(EsperEPL2GrammarParser.SubstitutionCanChainContext ctx) { + } + + public void EnterSlashIdentifier(EsperEPL2GrammarParser.SlashIdentifierContext ctx) { + } + + public void ExitSlashIdentifier(EsperEPL2GrammarParser.SlashIdentifierContext ctx) { + } + + public void EnterMatchRecogPatternRepeat(EsperEPL2GrammarParser.MatchRecogPatternRepeatContext ctx) { + } + + public void ExitMatchRecogPatternRepeat(EsperEPL2GrammarParser.MatchRecogPatternRepeatContext ctx) { + } + + public void EnterMatchRecogPatternPermute(EsperEPL2GrammarParser.MatchRecogPatternPermuteContext ctx) { + } + + public void EnterExpressionListWithNamed(EsperEPL2GrammarParser.ExpressionListWithNamedContext ctx) { + } + + public void ExitExpressionListWithNamed(EsperEPL2GrammarParser.ExpressionListWithNamedContext ctx) { + } + + public void EnterExpressionNamedParameter(EsperEPL2GrammarParser.ExpressionNamedParameterContext ctx) { + } + + public void EnterExpressionWithNamed(EsperEPL2GrammarParser.ExpressionWithNamedContext ctx) { + } + + public void ExitExpressionWithNamed(EsperEPL2GrammarParser.ExpressionWithNamedContext ctx) { + } + + public void EnterBuiltin_firstlastwindow(EsperEPL2GrammarParser.Builtin_firstlastwindowContext ctx) { + } + + public void EnterFirstLastWindowAggregation(EsperEPL2GrammarParser.FirstLastWindowAggregationContext ctx) { + } + + public void ExitFirstLastWindowAggregation(EsperEPL2GrammarParser.FirstLastWindowAggregationContext ctx) { + } + + public void EnterExpressionWithNamedWithTime(EsperEPL2GrammarParser.ExpressionWithNamedWithTimeContext ctx) { + } + + public void ExitExpressionWithNamedWithTime(EsperEPL2GrammarParser.ExpressionWithNamedWithTimeContext ctx) { + } + + public void EnterExpressionNamedParameterWithTime(EsperEPL2GrammarParser.ExpressionNamedParameterWithTimeContext ctx) { + } + + public void EnterExpressionListWithNamedWithTime(EsperEPL2GrammarParser.ExpressionListWithNamedWithTimeContext ctx) { + } + + public void ExitExpressionListWithNamedWithTime(EsperEPL2GrammarParser.ExpressionListWithNamedWithTimeContext ctx) { + } + + public void EnterViewExpressions(EsperEPL2GrammarParser.ViewExpressionsContext ctx) { + } + + public void ExitViewExpressions(EsperEPL2GrammarParser.ViewExpressionsContext ctx) { + } + + public void EnterViewWParameters(EsperEPL2GrammarParser.ViewWParametersContext ctx) { + } + + public void EnterViewExpressionWNamespace(EsperEPL2GrammarParser.ViewExpressionWNamespaceContext ctx) { + } + + public void ExitViewWParameters(EsperEPL2GrammarParser.ViewWParametersContext ctx) { + } + + public void EnterViewExpressionOptNamespace(EsperEPL2GrammarParser.ViewExpressionOptNamespaceContext ctx) { + } + + public void EnterOnSelectInsertFromClause(EsperEPL2GrammarParser.OnSelectInsertFromClauseContext ctx) { + } + + public void EnterExpressionTypeAnno(EsperEPL2GrammarParser.ExpressionTypeAnnoContext ctx) { + } + + public void ExitExpressionTypeAnno(EsperEPL2GrammarParser.ExpressionTypeAnnoContext ctx) { + } + + public void EnterTypeExpressionAnnotation(EsperEPL2GrammarParser.TypeExpressionAnnotationContext ctx) { + } + + public void ExitTypeExpressionAnnotation(EsperEPL2GrammarParser.TypeExpressionAnnotationContext ctx) { + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/parse/ExceptionConvertor.cs b/NEsper.Core/NEsper.Core/epl/parse/ExceptionConvertor.cs new file mode 100755 index 000000000..0df197a1c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/parse/ExceptionConvertor.cs @@ -0,0 +1,338 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.IO; +using System.Text; + +using Antlr4.Runtime; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.generated; + +namespace com.espertech.esper.epl.parse +{ + /// + /// Converts recognition exceptions. + /// + public class ExceptionConvertor + { + protected const string END_OF_INPUT_TEXT = "end-of-input"; + + /// + /// Converts from a syntax error to a nice statement exception. + /// + /// is the syntax error + /// is the expression text + /// the parser that parsed the expression + /// indicates to add "please check" paraphrases + /// syntax exception + public static EPStatementSyntaxException ConvertStatement(RecognitionException e, string expression, bool addPleaseCheck, EsperEPL2GrammarParser parser) + { + var pair = Convert(e, expression, addPleaseCheck, parser); + return new EPStatementSyntaxException(pair.First, pair.Second); + } + + /// + /// Converts from a syntax error to a nice property exception. + /// + /// is the syntax error + /// is the expression text + /// the parser that parsed the expression + /// indicates to add "please check" paraphrases + /// syntax exception + public static PropertyAccessException ConvertProperty(RecognitionException e, string expression, bool addPleaseCheck, EsperEPL2GrammarParser parser) + { + var pair = Convert(e, expression, addPleaseCheck, parser); + return new PropertyAccessException(pair.First, pair.Second); + } + + /// + /// Converts from a syntax error to a nice exception. + /// + /// is the syntax error + /// is the expression text + /// the parser that parsed the expression + /// indicates to add "please check" paraphrases + /// syntax exception + public static UniformPair Convert(RecognitionException e, string expression, bool addPleaseCheck, EsperEPL2GrammarParser parser) + { + string message; + + if (expression.Trim().Length == 0) + { + message = "Unexpected " + END_OF_INPUT_TEXT; + return new UniformPair(message, expression); + } + + IToken t; + IToken tBeforeBefore = null; + IToken tBefore = null; + IToken tAfter = null; + + ITokenStream tokenStream = parser.InputStream as ITokenStream; + + var tIndex = e.OffendingToken != null ? e.OffendingToken.TokenIndex : int.MaxValue; + if (tIndex < tokenStream.Size) + { + t = tokenStream.Get(tIndex); + if ((tIndex + 1) < tokenStream.Size) + { + tAfter = tokenStream.Get(tIndex + 1); + } + if (tIndex - 1 >= 0) + { + tBefore = tokenStream.Get(tIndex - 1); + } + if (tIndex - 2 >= 0) + { + tBeforeBefore = tokenStream.Get(tIndex - 2); + } + } + else + { + if (tokenStream.Size >= 1) + { + tBeforeBefore = tokenStream.Get(tokenStream.Size - 1); + } + if (tokenStream.Size >= 2) + { + tBefore = tokenStream.Get(tokenStream.Size - 2); + } + t = tokenStream.Get(tokenStream.Size - 1); + } + + IToken tEnd = null; + if (tokenStream.Size > 0) + { + tEnd = tokenStream.Get(tokenStream.Size - 1); + } + + var positionInfo = GetPositionInfo(t); + var token = t.Type == EsperEPL2GrammarParser.Eof ? "end-of-input" : "'" + t.Text + "'"; + + var stack = parser.GetParaphrases(); + var check = ""; + var isSelect = stack.Count == 1 && stack.Peek().Equals("select clause"); + if ((stack.Count > 0) && addPleaseCheck) + { + var delimiter = ""; + var checkList = new StringBuilder(); + checkList.Append(", please check the "); + while (stack.Count != 0) + { + checkList.Append(delimiter); + checkList.Append(stack.Pop()); + delimiter = " within the "; + } + check = checkList.ToString(); + } + + // check if token is a reserved keyword + var keywords = parser.GetKeywords(); + var reservedKeyword = false; + if (keywords.Contains(token.ToLower())) + { + token += " (a reserved keyword)"; + reservedKeyword = true; + } + else if (tAfter != null && keywords.Contains("'" + tAfter.Text.ToLower() + "'")) + { + token += " ('" + tAfter.Text + "' is a reserved keyword)"; + reservedKeyword = true; + } + else + { + if ((tBefore != null) && + (tAfter != null) && + (keywords.Contains("'" + tBefore.Text.ToLower() + "'")) && + (keywords.Contains("'" + tAfter.Text.ToLower() + "'"))) + { + token += " ('" + tBefore.Text + "' and '" + tAfter.Text + "' are a reserved keyword)"; + reservedKeyword = true; + } + else if ((tBefore != null) && + (keywords.Contains("'" + tBefore.Text.ToLower() + "'"))) + { + token += " ('" + tBefore.Text + "' is a reserved keyword)"; + reservedKeyword = true; + } + else if (tEnd != null && keywords.Contains("'" + tEnd.Text.ToLower() + "'")) + { + token += " ('" + tEnd.Text + "' is a reserved keyword)"; + reservedKeyword = true; + } + } + + // special handling for the select-clause "as" keyword, which is required + if (isSelect && !reservedKeyword) + { + check += GetSelectClauseAsText(tBeforeBefore, t); + } + + message = "Incorrect syntax near " + token + positionInfo + check; + if (e is NoViableAltException || e is LexerNoViableAltException || CheckForInputMismatchWithNoExpected(e)) + { + var nvaeToken = e.OffendingToken; + var nvaeTokenType = nvaeToken != null ? nvaeToken.Type : EsperEPL2GrammarLexer.Eof; + + if (nvaeTokenType == EsperEPL2GrammarLexer.Eof) + { + if (token.Equals(END_OF_INPUT_TEXT)) + { + message = "Unexpected " + END_OF_INPUT_TEXT + positionInfo + check; + } + else + { + if (ParseHelper.HasControlCharacters(expression)) + { + message = "Unrecognized control characters found in text" + positionInfo; + } + else + { + message = "Unexpected " + END_OF_INPUT_TEXT + " near " + token + positionInfo + check; + } + } + } + else + { + var parserTokenParaphrases = EsperEPL2GrammarParser.GetParserTokenParaphrases(); + if (parserTokenParaphrases.Get(nvaeTokenType) != null) + { + message = "Incorrect syntax near " + token + positionInfo + check; + } + else + { + // find next keyword in the next 3 tokens + var currentIndex = tIndex + 1; + while ((currentIndex > 0) && + (currentIndex < tokenStream.Size - 1) && + (currentIndex < tIndex + 3)) + { + IToken next = tokenStream.Get(currentIndex); + currentIndex++; + + var quotedToken = "'" + next.Text + "'"; + if (parser.GetKeywords().Contains(quotedToken)) + { + check += " near reserved keyword '" + next.Text + "'"; + break; + } + } + message = "Incorrect syntax near " + token + positionInfo + check; + } + } + } + else if (e is InputMismatchException) + { + var mismatched = (InputMismatchException)e; + + string expected; + var expectedTokens = mismatched.GetExpectedTokens().ToList(); + if (expectedTokens.Count > 1) + { + var writer = new StringWriter(); + writer.Write("any of the following tokens {"); + var delimiter = ""; + for (var i = 0; i < expectedTokens.Count; i++) + { + writer.Write(delimiter); + if (i > 5) + { + writer.Write("..."); + writer.Write(expectedTokens.Count - 5); + writer.Write(" more"); + break; + } + delimiter = ", "; + writer.Write(GetTokenText(parser, expectedTokens[i])); + } + writer.Write("}"); + expected = writer.ToString(); + } + else + { + expected = GetTokenText(parser, expectedTokens[0]); + } + + var offendingTokenType = mismatched.OffendingToken.Type; + var unexpected = GetTokenText(parser, offendingTokenType); + + var expecting = " expecting " + expected.Trim() + " but found " + unexpected.Trim(); + message = "Incorrect syntax near " + token + expecting + positionInfo + check; + } + + return new UniformPair(message, expression); + } + + private static bool CheckForInputMismatchWithNoExpected(RecognitionException e) + { + if (!(e is InputMismatchException)) + { + return false; + } + + var expectedTokens = e.GetExpectedTokens(); + if (expectedTokens.Count > 1) + { + return false; + } + return + expectedTokens.Count == 1 && + expectedTokens.ToList()[0] == -1; + } + + private static string GetTokenText(EsperEPL2GrammarParser parser, int tokenIndex) + { + var expected = END_OF_INPUT_TEXT; + + Vocabulary vocabulary = (Vocabulary) parser.Vocabulary; + + if ((tokenIndex >= 0) && (tokenIndex <= vocabulary.getMaxTokenType())) + { + expected = parser.Vocabulary.GetSymbolicName(tokenIndex); + } + var lexerTokenParaphrases = EsperEPL2GrammarLexer.GetLexerTokenParaphrases(); + if (lexerTokenParaphrases.Get(tokenIndex) != null) + { + expected = lexerTokenParaphrases.Get(tokenIndex); + } + var parserTokenParaphrases = EsperEPL2GrammarParser.GetParserTokenParaphrases(); + if (parserTokenParaphrases.Get(tokenIndex) != null) + { + expected = parserTokenParaphrases.Get(tokenIndex); + } + return expected; + } + + /// + /// Returns the position information string for a parser exception. + /// + /// the token to return the information for + /// is a string with line and column information + private static string GetPositionInfo(IToken t) + { + return t.Line > 0 && t.Column > 0 + ? " at line " + t.Line + " column " + t.Column + : ""; + } + + private static string GetSelectClauseAsText(IToken tBeforeBefore, IToken t) + { + if (tBeforeBefore != null && + tBeforeBefore.Type == EsperEPL2GrammarParser.IDENT && + t != null && + t.Type == EsperEPL2GrammarParser.IDENT) + { + return " (did you forget 'as'?)"; + } + return ""; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/parse/NoCaseSensitiveStream.cs b/NEsper.Core/NEsper.Core/epl/parse/NoCaseSensitiveStream.cs new file mode 100755 index 000000000..f5083822b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/parse/NoCaseSensitiveStream.cs @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +using Antlr4.Runtime; + +namespace com.espertech.esper.epl.parse +{ + /// + /// For use with ANTLR to create a case-insensitive token stream. + /// + public class NoCaseSensitiveStream : AntlrInputStream + { + /// Ctor. + /// string to be parsed + /// IOException to indicate IO errors + public NoCaseSensitiveStream(String s) + : base(new StringReader(s)) + { + } + + public override int LA(int i) + { + if (i == 0) + { + return 0; // undefined + } + if (i < 0) + { + i++; // e.g., translate LA(-1) to use offset i=0; then data[p+0-1] + if ((p + i - 1) < 0) + { + return IntStreamConstants.EOF; + } + } + // invalid; no char before first char + if ((p + i - 1) >= n) + { + return (int)IntStreamConstants.EOF; + } + return Char.ToLower(data[p + i - 1]); + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/epl/parse/ParseHelper.cs b/NEsper.Core/NEsper.Core/epl/parse/ParseHelper.cs new file mode 100755 index 000000000..2eeffe35b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/parse/ParseHelper.cs @@ -0,0 +1,497 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +using Antlr4.Runtime; +using Antlr4.Runtime.Tree; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.generated; + +namespace com.espertech.esper.epl.parse +{ + /// + /// Helper class for parsing an expression and walking a parse tree. + /// + public class ParseHelper + { + /// + /// Newline. + /// + public static readonly String NewLine = System.Environment.NewLine; + + /// + /// Walk parse tree starting at the rule the walkRuleSelector supplies. + /// + /// ast to walk + /// walker instance + /// the expression we are walking in string form + /// statement text for error messages + public static void Walk(ITree ast, EPLTreeWalkerListener listener, String expression, String eplStatementForErrorMsg) + { + // Walk tree + try + { + if (Log.IsDebugEnabled) + { + Log.Debug(".walk Walking AST using walker " + listener.GetType().FullName); + } + var walker = new ParseTreeWalker(); + walker.Walk(listener, (IParseTree)ast); + listener.End(); + } + catch (Exception e) + { + Log.Info("Error walking statement [" + expression + "]", e); + throw; + } + } + + /// + /// Parse expression using the rule the ParseRuleSelector instance supplies. + /// + /// text to parse + /// text for error + /// true to include depth paraphrase + /// parse rule to select + /// if set to true [rewrite script]. + /// + /// AST - syntax tree + /// + /// IOException parsing expression ' + expression + '\'' + /// EPException when the AST could not be parsed + public static ParseResult Parse(String expression, String eplStatementErrorMsg, bool addPleaseCheck, ParseRuleSelector parseRuleSelector, bool rewriteScript) + { + if (Log.IsDebugEnabled) + { + Log.Debug(".parse Parsing expr=" + expression); + } + + ICharStream input; + try + { + input = new NoCaseSensitiveStream(expression); + } + catch (IOException ex) + { + throw new EPException("IOException parsing expression '" + expression + '\'', ex); + } + + var lex = NewLexer(input); + + var tokens = new CommonTokenStream(lex); + var parser = ParseHelper.NewParser(tokens); + + ITree tree; + try + { + tree = parseRuleSelector.Invoke(parser); + } + catch (RecognitionException ex) + { + tokens.Fill(); + if (rewriteScript && IsContainsScriptExpression(tokens)) + { + return HandleScriptRewrite(tokens, eplStatementErrorMsg, addPleaseCheck, parseRuleSelector); + } + Log.Debug("Error parsing statement [" + expression + "]", ex); + throw ExceptionConvertor.ConvertStatement(ex, eplStatementErrorMsg, addPleaseCheck, parser); + } + catch (Exception e) + { + try + { + tokens.Fill(); + } + catch (Exception ex) + { + Log.Debug("Token-fill produced exception: " + ex.Message, ex); + } + + if (Log.IsDebugEnabled) + { + Log.Debug("Error parsing statement [" + eplStatementErrorMsg + "]", e); + } + if (e.InnerException is RecognitionException) + { + if (rewriteScript && IsContainsScriptExpression(tokens)) + { + return HandleScriptRewrite(tokens, eplStatementErrorMsg, addPleaseCheck, parseRuleSelector); + } + throw ExceptionConvertor.ConvertStatement((RecognitionException)e.InnerException, eplStatementErrorMsg, addPleaseCheck, parser); + } + else + { + throw; + } + } + + // if we are re-writing scripts and contain a script, then rewrite + if (rewriteScript && IsContainsScriptExpression(tokens)) + { + return HandleScriptRewrite(tokens, eplStatementErrorMsg, addPleaseCheck, parseRuleSelector); + } + + if (Log.IsDebugEnabled) + { + Log.Debug(".parse Dumping AST..."); + ASTUtil.DumpAST(tree); + } + + var expressionWithoutAnnotation = expression; + if (tree is EsperEPL2GrammarParser.StartEPLExpressionRuleContext) + { + var epl = (EsperEPL2GrammarParser.StartEPLExpressionRuleContext)tree; + expressionWithoutAnnotation = GetNoAnnotation(expression, epl.annotationEnum(), tokens); + } + else if (tree is EsperEPL2GrammarParser.StartPatternExpressionRuleContext) + { + var pattern = (EsperEPL2GrammarParser.StartPatternExpressionRuleContext)tree; + expressionWithoutAnnotation = GetNoAnnotation(expression, pattern.annotationEnum(), tokens); + } + + return new ParseResult(tree, expressionWithoutAnnotation, tokens, Collections.GetEmptyList()); + } + + private static ParseResult HandleScriptRewrite(CommonTokenStream tokens, String eplStatementErrorMsg, bool addPleaseCheck, ParseRuleSelector parseRuleSelector) + { + var rewriteExpression = RewriteTokensScript(tokens); + var result = Parse(rewriteExpression.RewrittenEPL, eplStatementErrorMsg, addPleaseCheck, parseRuleSelector, false); + return new ParseResult(result.Tree, result.ExpressionWithoutAnnotations, result.TokenStream, rewriteExpression.Scripts); + } + + private static String GetNoAnnotation(String expression, IList annos, CommonTokenStream tokens) + { + if (annos == null || annos.IsEmpty()) + { + return expression; + } + + var lastAnnotationToken = annos[annos.Count - 1].Stop; + if (lastAnnotationToken == null) + { + return null; + } + + try + { + int line = lastAnnotationToken.Line; + int charpos = lastAnnotationToken.Column; + var fromChar = charpos + lastAnnotationToken.Text.Length; + if (line == 1) + { + return expression.Substring(fromChar).Trim(); + } + + var lines = expression.RegexSplit("\r\n|\r|\n"); + var buf = new StringBuilder(); + buf.Append(lines[line - 1].Substring(fromChar)); + for (var i = line; i < lines.Length; i++) + { + buf.Append(lines[i]); + if (i < lines.Length - 1) + { + buf.Append(NewLine); + } + } + return buf.ToString().Trim(); + } + catch (Exception ex) + { + Log.Error("Error determining non-annotated expression sting: " + ex.Message, ex); + } + return null; + } + + private static ScriptResult RewriteTokensScript(CommonTokenStream tokens) + { + IList scripts = new List(); + + IList> scriptTokenIndexRanges = new List>(); + for (var i = 0; i < tokens.Size; i++) + { + if (tokens.Get(i).Type == EsperEPL2GrammarParser.EXPRESSIONDECL) + { + var tokenBefore = GetTokenBefore(i, tokens); + var isCreateExpressionClause = tokenBefore != null && tokenBefore.Type == EsperEPL2GrammarParser.CREATE; + var nameAndNameStart = FindScriptName(i + 1, tokens); + + var startIndex = FindStartTokenScript(nameAndNameStart.Second.Value, tokens, EsperEPL2GrammarParser.LBRACK); + if (startIndex != -1) + { + var endIndex = FindEndTokenScript(startIndex + 1, tokens, EsperEPL2GrammarParser.RBRACK, EsperEPL2GrammarParser.GetAfterScriptTokens(), !isCreateExpressionClause); + if (endIndex != -1) + { + + var writer = new StringWriter(); + for (var j = startIndex + 1; j < endIndex; j++) + { + writer.Write(tokens.Get(j).Text); + } + scripts.Add(writer.ToString()); + scriptTokenIndexRanges.Add(new UniformPair(startIndex, endIndex)); + } + } + } + } + + var rewrittenEPL = RewriteScripts(scriptTokenIndexRanges, tokens); + return new ScriptResult(rewrittenEPL, scripts); + } + + private static IToken GetTokenBefore(int i, CommonTokenStream tokens) + { + var position = i - 1; + while (position >= 0) + { + var t = tokens.Get(position); + if (t.Channel != 99 && t.Type != EsperEPL2GrammarLexer.WS) + { + return t; + } + position--; + } + return null; + } + + private static Pair FindScriptName(int start, CommonTokenStream tokens) + { + String lastIdent = null; + var lastIdentIndex = 0; + for (var i = start; i < tokens.Size; i++) + { + if (tokens.Get(i).Type == EsperEPL2GrammarParser.IDENT) + { + lastIdent = tokens.Get(i).Text; + lastIdentIndex = i; + } + if (tokens.Get(i).Type == EsperEPL2GrammarParser.LPAREN) + { + break; + } + // find beginning of script, ignore brackets + if (tokens.Get(i).Type == EsperEPL2GrammarParser.LBRACK && tokens.Get(i + 1).Type != EsperEPL2GrammarParser.RBRACK) + { + break; + } + } + if (lastIdent == null) + { + throw new IllegalStateException("Failed to parse expression name"); + } + return new Pair(lastIdent, lastIdentIndex); + } + + private static String RewriteScripts(IList> ranges, CommonTokenStream tokens) + { + if (ranges.IsEmpty()) + { + return tokens.GetText(); + } + var writer = new StringWriter(); + var rangeIndex = 0; + UniformPair current = ranges[rangeIndex]; + for (var i = 0; i < tokens.Size; i++) + { + var t = tokens.Get(i); + if (t.Type == EsperEPL2GrammarLexer.Eof) + { + break; + } + if (i < current.First) + { + writer.Write(t.Text); + } + else if (i == current.First) + { + writer.Write(t.Text); + writer.Write("'"); + } + else if (i == current.Second) + { + writer.Write("'"); + writer.Write(t.Text); + rangeIndex++; + if (ranges.Count > rangeIndex) + { + current = ranges[rangeIndex]; + } + else + { + current = new UniformPair(-1, -1); + } + } + else if (t.Type == EsperEPL2GrammarParser.SL_COMMENT || t.Type == EsperEPL2GrammarParser.ML_COMMENT) + { + WriteCommentEscapeSingleQuote(writer, t); + } + else + { + if (t.Type == EsperEPL2GrammarParser.QUOTED_STRING_LITERAL && i > current.First && i < current.Second) + { + writer.Write("\\'"); + writer.Write(t.Text.Substring(1, t.Text.Length - 2)); + writer.Write("\\'"); + } + else + { + writer.Write(t.Text); + } + } + } + return writer.ToString(); + } + + private static int FindEndTokenScript(int startIndex, CommonTokenStream tokens, int tokenTypeSearch, ISet afterScriptTokens, bool requireAfterScriptToken) + { + + // The next non-comment token must be among the afterScriptTokens, i.e. SELECT/INSERT/ON/DELETE/UPDATE + // Find next non-comment token. + if (requireAfterScriptToken) + { + int found = -1; + for (int i = startIndex; i < tokens.Size; i++) + { + if (tokens.Get(i).Type == tokenTypeSearch) + { + for (int j = i + 1; j < tokens.Size; j++) + { + var next = tokens.Get(j); + if (next.Channel == 0) + { + if (afterScriptTokens.Contains(next.Type)) + { + found = i; + } + break; + } + } + } + if (found != -1) + { + break; + } + } + return found; + } + + // Find the last token + int indexLast = -1; + for (int i = startIndex; i < tokens.Size; i++) + { + if (tokens.Get(i).Type == tokenTypeSearch) + { + indexLast = i; + } + } + + return indexLast; + } + + private static bool IsContainsScriptExpression(CommonTokenStream tokens) + { + for (var i = 0; i < tokens.Size; i++) + { + if (tokens.Get(i).Type == EsperEPL2GrammarParser.EXPRESSIONDECL) + { + var startTokenLcurly = FindStartTokenScript(i + 1, tokens, EsperEPL2GrammarParser.LCURLY); + var startTokenLbrack = FindStartTokenScript(i + 1, tokens, EsperEPL2GrammarParser.LBRACK); + // Handle: + // expression ABC { some[other] } + // expression boolean js:doit(...) [ {} ] + if (startTokenLbrack != -1 && (startTokenLcurly == -1 || startTokenLcurly > startTokenLbrack)) + { + return true; + } + } + } + return false; + } + + private static int FindStartTokenScript(int startIndex, CommonTokenStream tokens, int tokenTypeSearch) + { + var found = -1; + for (var i = startIndex; i < tokens.Size; i++) + { + if (tokens.Get(i).Type == tokenTypeSearch) + { + return i; + } + } + return found; + } + + public static EsperEPL2GrammarLexer NewLexer(ICharStream input) + { + var lex = new EsperEPL2GrammarLexer(input); + lex.RemoveErrorListeners(); + lex.AddErrorListener(Antlr4ErrorListener.INSTANCE); + return lex; + } + + public static EsperEPL2GrammarParser NewParser(CommonTokenStream tokens) + { + var g = new EsperEPL2GrammarParser(tokens); + g.RemoveErrorListeners(); + g.AddErrorListener(Antlr4ErrorListener.INSTANCE); + g.ErrorHandler = new Antlr4ErrorStrategy(); + return g; + } + + public static bool HasControlCharacters(String text) + { + String textWithoutControlCharacters = text.RegexReplaceAll("\\p{Cc}", ""); + return !textWithoutControlCharacters.Equals(text); + } + + private static void WriteCommentEscapeSingleQuote(TextWriter writer, IToken t) + { + String text = t.Text; + if (!text.Contains("'")) + { + return; + } + for (int i = 0; i < text.Length; i++) + { + char c = text[i]; + if (c == '\'') + { + writer.Write('\\'); + writer.Write(c); + } + else + { + writer.Write(c); + } + } + } + + internal class ScriptResult + { + internal ScriptResult(String rewrittenEPL, IList scripts) + { + RewrittenEPL = rewrittenEPL; + Scripts = scripts; + } + + public string RewrittenEPL { get; private set; } + + public IList Scripts { get; private set; } + } + + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/parse/ParseResult.cs b/NEsper.Core/NEsper.Core/epl/parse/ParseResult.cs new file mode 100755 index 000000000..a54cfbf46 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/parse/ParseResult.cs @@ -0,0 +1,59 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using Antlr4.Runtime; +using Antlr4.Runtime.Tree; + +namespace com.espertech.esper.epl.parse +{ + /// Result of a parse action. + public class ParseResult + { + /// + /// Ctor. + /// + /// parse tree + /// expression text no annotations, or null if same + /// The token stream. + /// The scripts. + public ParseResult(ITree tree, String expressionWithoutAnnotations, CommonTokenStream tokenStream, IList scripts) + { + Tree = tree; + ExpressionWithoutAnnotations = expressionWithoutAnnotations; + TokenStream = tokenStream; + Scripts = scripts; + } + + /// + /// AST. + /// + /// ast + public ITree Tree { get; private set; } + + /// + /// Returns the expression text no annotations. + /// + /// expression text no annotations. + public string ExpressionWithoutAnnotations { get; private set; } + + /// + /// Gets or sets the token stream. + /// + /// The token stream. + public CommonTokenStream TokenStream { get; private set; } + + /// + /// Gets or sets the scripts. + /// + /// The scripts. + public IList Scripts { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/parse/ParseRuleSelector.cs b/NEsper.Core/NEsper.Core/epl/parse/ParseRuleSelector.cs new file mode 100755 index 000000000..06ba262da --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/parse/ParseRuleSelector.cs @@ -0,0 +1,21 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using Antlr4.Runtime.Tree; + +using com.espertech.esper.epl.generated; + +namespace com.espertech.esper.epl.parse +{ + /// + /// Implementations can invoke a parse rule of their choice on the parser. + /// + /// parser to invoke parse rule on + + public delegate ITree ParseRuleSelector(EsperEPL2GrammarParser parser); +} diff --git a/NEsper.Core/NEsper.Core/epl/property/ContainedEventEval.cs b/NEsper.Core/NEsper.Core/epl/property/ContainedEventEval.cs new file mode 100755 index 000000000..0e46854cc --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/property/ContainedEventEval.cs @@ -0,0 +1,21 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.property +{ + public interface ContainedEventEval + { + Object GetFragment(EventBean eventBean, EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/property/ContainedEventEvalArrayToEvent.cs b/NEsper.Core/NEsper.Core/epl/property/ContainedEventEvalArrayToEvent.cs new file mode 100755 index 000000000..3874399c9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/property/ContainedEventEvalArrayToEvent.cs @@ -0,0 +1,46 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.property +{ + public class ContainedEventEvalArrayToEvent : ContainedEventEval + { + private readonly ExprEvaluator _evaluator; + private readonly EventBeanManufacturer _manufacturer; + + public ContainedEventEvalArrayToEvent(ExprEvaluator evaluator, EventBeanManufacturer manufacturer) + { + _evaluator = evaluator; + _manufacturer = manufacturer; + } + + public object GetFragment(EventBean eventBean, EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext) + { + var result = _evaluator.Evaluate(new EvaluateParams(eventsPerStream, true, exprEvaluatorContext)) as Array; + if (result == null) { + return null; + } + + var events = new EventBean[result.Length]; + for (int i = 0; i < events.Length; i++) + { + var column = result.GetValue(i); + if (column != null) { + events[i] = _manufacturer.Make(new object[] {column}); + } + } + return events; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/property/ContainedEventEvalEventBeanArray.cs b/NEsper.Core/NEsper.Core/epl/property/ContainedEventEvalEventBeanArray.cs new file mode 100755 index 000000000..e9e20437f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/property/ContainedEventEvalEventBeanArray.cs @@ -0,0 +1,33 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.property +{ + public class ContainedEventEvalEventBeanArray : ContainedEventEval + { + private readonly ExprEvaluator _evaluator; + + public ContainedEventEvalEventBeanArray(ExprEvaluator evaluator) + { + _evaluator = evaluator; + } + + public Object GetFragment( + EventBean eventBean, + EventBean[] eventsPerStream, + ExprEvaluatorContext exprEvaluatorContext) + { + return _evaluator.Evaluate(new EvaluateParams(eventsPerStream, true, exprEvaluatorContext)); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/property/ContainedEventEvalExprNode.cs b/NEsper.Core/NEsper.Core/epl/property/ContainedEventEvalExprNode.cs new file mode 100755 index 000000000..e663d4195 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/property/ContainedEventEvalExprNode.cs @@ -0,0 +1,67 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.property +{ + public class ContainedEventEvalExprNode : ContainedEventEval + { + private readonly ExprEvaluator _evaluator; + private readonly EventBeanFactory _eventBeanFactory; + + public ContainedEventEvalExprNode(ExprEvaluator evaluator, EventBeanFactory eventBeanFactory) { + _evaluator = evaluator; + _eventBeanFactory = eventBeanFactory; + } + + public Object GetFragment(EventBean eventBean, EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext) { + Object result = _evaluator.Evaluate(new EvaluateParams(eventsPerStream, true, exprEvaluatorContext)); + + if (result == null) { + return null; + } + + var asArray = result as Array; + if (asArray != null) + { + return asArray + .Cast() + .Where(item => item != null) + .Select(item => _eventBeanFactory.Wrap(item)) + .ToArray(); + } + + if (result is ICollection) { + var collection = (ICollection)result; + return collection + .Cast() + .Where(item => item != null) + .Select(item => _eventBeanFactory.Wrap(item)) + .ToArray(); + } + + if (result is IEnumerable) { + var enumerable = (IEnumerable) result; + return enumerable + .Cast() + .Where(item => item != null) + .Select(item => _eventBeanFactory.Wrap(item)) + .ToArray(); + } + + return null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/property/ContainedEventEvalGetter.cs b/NEsper.Core/NEsper.Core/epl/property/ContainedEventEvalGetter.cs new file mode 100755 index 000000000..844b0d7f5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/property/ContainedEventEvalGetter.cs @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.property +{ + public class ContainedEventEvalGetter : ContainedEventEval + { + + private readonly EventPropertyGetter _getter; + + public ContainedEventEvalGetter(EventPropertyGetter getter) + { + _getter = getter; + } + + public Object GetFragment(EventBean eventBean, EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext) + { + return _getter.GetFragment(eventBean); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/property/PropertyEvaluator.cs b/NEsper.Core/NEsper.Core/epl/property/PropertyEvaluator.cs new file mode 100755 index 000000000..ba8d311b3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/property/PropertyEvaluator.cs @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.property +{ + /// + /// Interface for a function that evaluates the properties of an event and returns event representing the properties. + /// + public interface PropertyEvaluator + { + /// + /// Returns the result events based on property values, or null if none found. + /// + /// to inspect + /// expression evaluation context + /// events representing Property(s) + EventBean[] GetProperty(EventBean theEvent, ExprEvaluatorContext exprEvaluatorContext); + + /// + /// Returns the result type of the events generated by evaluating a property expression. + /// + /// result event type + EventType FragmentEventType { get; } + + /// + /// Compare to another property evaluator. + /// + /// other + /// equals or not + bool CompareTo(PropertyEvaluator otherFilterPropertyEval); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/property/PropertyEvaluatorAccumulative.cs b/NEsper.Core/NEsper.Core/epl/property/PropertyEvaluatorAccumulative.cs new file mode 100755 index 000000000..5db5f382a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/property/PropertyEvaluatorAccumulative.cs @@ -0,0 +1,149 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.property +{ + /// + /// A property evaluator that returns a full row of events for each stream, i.e. flattened inner-join results for + /// property-upon-property. + /// + public class PropertyEvaluatorAccumulative + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly ContainedEventEval[] _containedEventEvals; + private readonly FragmentEventType[] _fragmentEventType; + private readonly ExprEvaluator[] _whereClauses; + private readonly int _lastLevel; + private readonly int _levels; + private readonly IList _propertyNames; + + /// + /// Ctor. + /// + /// property getters or other evaluators + /// property fragment types + /// filters, if any + /// the property names that are staggered + public PropertyEvaluatorAccumulative( + ContainedEventEval[] containedEventEvals, + FragmentEventType[] fragmentEventType, + ExprEvaluator[] whereClauses, + IList propertyNames) + { + _fragmentEventType = fragmentEventType; + _containedEventEvals = containedEventEvals; + _whereClauses = whereClauses; + _lastLevel = fragmentEventType.Length - 1; + _levels = fragmentEventType.Length + 1; + _propertyNames = propertyNames; + } + + /// + /// Returns the accumulative events for the input event. + /// + /// is the input event + /// expression evaluation context + /// events per stream for each row + public ArrayDeque GetAccumulative(EventBean theEvent, ExprEvaluatorContext exprEvaluatorContext) { + var resultEvents = new ArrayDeque(); + var eventsPerStream = new EventBean[_levels]; + eventsPerStream[0] = theEvent; + PopulateEvents(eventsPerStream, theEvent, 0, resultEvents, exprEvaluatorContext); + if (resultEvents.IsEmpty()) { + return null; + } + return resultEvents; + } + + private void PopulateEvents(EventBean[] eventsPerStream, EventBean branch, int level, ICollection events, ExprEvaluatorContext exprEvaluatorContext) { + try { + Object result = _containedEventEvals[level].GetFragment(branch, eventsPerStream, exprEvaluatorContext); + + if (_fragmentEventType[level].IsIndexed) { + EventBean[] fragments = (EventBean[]) result; + if (level == _lastLevel) { + if (_whereClauses[level] != null) { + foreach (EventBean theEvent in fragments) { + eventsPerStream[level + 1] = theEvent; + if (ExprNodeUtility.ApplyFilterExpression(_whereClauses[level], eventsPerStream, exprEvaluatorContext)) { + var eventsPerRow = new EventBean[_levels]; + Array.Copy(eventsPerStream, 0, eventsPerRow, 0, _levels); + events.Add(eventsPerRow); + } + } + } else { + foreach (EventBean theEvent in fragments) { + eventsPerStream[level + 1] = theEvent; + var eventsPerRow = new EventBean[_levels]; + Array.Copy(eventsPerStream, 0, eventsPerRow, 0, _levels); + events.Add(eventsPerRow); + } + } + } else { + if (_whereClauses[level] != null) { + foreach (EventBean next in fragments) { + eventsPerStream[level + 1] = next; + if (ExprNodeUtility.ApplyFilterExpression(_whereClauses[level], eventsPerStream, exprEvaluatorContext)) { + PopulateEvents(eventsPerStream, next, level + 1, events, exprEvaluatorContext); + } + } + } else { + foreach (EventBean next in fragments) { + eventsPerStream[level + 1] = next; + PopulateEvents(eventsPerStream, next, level + 1, events, exprEvaluatorContext); + } + } + } + } else { + EventBean fragment = (EventBean) result; + if (level == _lastLevel) { + if (_whereClauses[level] != null) { + eventsPerStream[level + 1] = fragment; + if (ExprNodeUtility.ApplyFilterExpression(_whereClauses[level], eventsPerStream, exprEvaluatorContext)) { + var eventsPerRow = new EventBean[_levels]; + Array.Copy(eventsPerStream, 0, eventsPerRow, 0, _levels); + events.Add(eventsPerRow); + } + } else { + eventsPerStream[level + 1] = fragment; + var eventsPerRow = new EventBean[_levels]; + Array.Copy(eventsPerStream, 0, eventsPerRow, 0, _levels); + events.Add(eventsPerRow); + } + } else { + if (_whereClauses[level] != null) { + eventsPerStream[level + 1] = fragment; + if (ExprNodeUtility.ApplyFilterExpression(_whereClauses[level], eventsPerStream, exprEvaluatorContext)) { + PopulateEvents(eventsPerStream, fragment, level + 1, events, exprEvaluatorContext); + } + } else { + eventsPerStream[level + 1] = fragment; + PopulateEvents(eventsPerStream, fragment, level + 1, events, exprEvaluatorContext); + } + } + } + } catch (Exception ex) + { + Log.Error( + string.Format( + "Unexpected error evaluating property expression for event of type '{0}' and property '{1}': {2}", + branch.EventType.Name, _propertyNames[level + 1], ex.Message), ex); + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/property/PropertyEvaluatorFactory.cs b/NEsper.Core/NEsper.Core/epl/property/PropertyEvaluatorFactory.cs new file mode 100755 index 000000000..101d65a58 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/property/PropertyEvaluatorFactory.cs @@ -0,0 +1,375 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.named; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.epl.variable; +using com.espertech.esper.events; +using com.espertech.esper.schedule; +using com.espertech.esper.script; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.property +{ + /// + /// Factory for property evaluators. + /// + public class PropertyEvaluatorFactory + { + /// + /// Makes the property evaluator. + /// + /// is the property specification + /// the event type + /// the source stream name + /// for event instances + /// The engine import service. + /// provides time + /// for resolving variables + /// The scripting service. + /// The table service. + /// engine URI + /// The statement identifier. + /// Name of the statement. + /// The annotations. + /// The assigned type number stack. + /// The configuration. + /// The named window service. + /// The statement extension SVC context. + /// + /// propert evaluator + /// + /// Missing @type(name) declaration providing the event type name of the return type for expression ' + + /// ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(atom.SplitterExpression) + ' + /// or + /// Event type by name ' + atom.OptionalResultEventType + ' could not be found + /// or + /// Event type ' + streamEventType.Name + ' underlying type + streamEventType.UnderlyingType.Name + + /// cannot be assigned a value of type + returnType.GetTypeNameFullyQualPretty() + /// or + /// Return type of expression ' + ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(atom.SplitterExpression) + ' is ' + returnType.Name + ', expected an Iterable or array result + /// or + /// Property rename ' + rawStreamSpec.MaskTypeName + ' not found in path + /// or + /// Expression in a property-selection may not utilize + isMinimal + /// Unknown select clause item: + raw + /// ExprValidationException if any expressions could not be verified + public static PropertyEvaluator MakeEvaluator( + PropertyEvalSpec spec, + EventType sourceEventType, + string optionalSourceStreamName, + EventAdapterService eventAdapterService, + EngineImportService engineImportService, + TimeProvider timeProvider, + VariableService variableService, + ScriptingService scriptingService, + TableService tableService, + string engineURI, + int statementId, + string statementName, + Attribute[] annotations, + ICollection assignedTypeNumberStack, + ConfigurationInformation configuration, + NamedWindowMgmtService namedWindowMgmtService, + StatementExtensionSvcContext statementExtensionSvcContext) + { + var length = spec.Atoms.Count; + var containedEventEvals = new ContainedEventEval[length]; + var fragmentEventTypes = new FragmentEventType[length]; + var currentEventType = sourceEventType; + var whereClauses = new ExprEvaluator[length]; + + var streamEventTypes = new List(); + var streamNames = new List(); + var streamNameAndNumber = new Dictionary().WithNullSupport(); + var expressionTexts = new List(); + var validateContext = new ExprEvaluatorContextTimeOnly(timeProvider); + + streamEventTypes.Add(sourceEventType); + streamNames.Add(optionalSourceStreamName); + streamNameAndNumber.Put(optionalSourceStreamName, 0); + expressionTexts.Add(sourceEventType.Name); + + IList cumulativeSelectClause = new List(); + for (var i = 0; i < length; i++) + { + var atom = spec.Atoms[i]; + ContainedEventEval containedEventEval = null; + string expressionText = null; + EventType streamEventType = null; + FragmentEventType fragmentEventType = null; + + // Resolve directly as fragment event type if possible + if (atom.SplitterExpression is ExprIdentNode) + { + var propertyName = ((ExprIdentNode)atom.SplitterExpression).FullUnresolvedName; + fragmentEventType = currentEventType.GetFragmentType(propertyName); + if (fragmentEventType != null) + { + var getter = currentEventType.GetGetter(propertyName); + if (getter != null) + { + containedEventEval = new ContainedEventEvalGetter(getter); + expressionText = propertyName; + streamEventType = fragmentEventType.FragmentType; + } + } + } + + // evaluate splitter expression + if (containedEventEval == null) + { + ExprNodeUtility.ValidatePlainExpression( + ExprNodeOrigin.CONTAINEDEVENT, + ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(atom.SplitterExpression), + atom.SplitterExpression); + + var availableTypes = streamEventTypes.ToArray(); + var availableStreamNames = streamNames.ToArray(); + var isIStreamOnly = new bool[streamNames.Count]; + isIStreamOnly.Fill(true); + StreamTypeService streamTypeService = new StreamTypeServiceImpl( + availableTypes, availableStreamNames, isIStreamOnly, engineURI, false); + var validationContext = new ExprValidationContext( + streamTypeService, engineImportService, statementExtensionSvcContext, null, timeProvider, variableService, tableService, + validateContext, eventAdapterService, statementName, statementId, annotations, null, scriptingService, + false, false, true, false, null, false); + var validatedExprNode = ExprNodeUtility.GetValidatedSubtree( + ExprNodeOrigin.CONTAINEDEVENT, atom.SplitterExpression, validationContext); + var evaluator = validatedExprNode.ExprEvaluator; + + // determine result type + if (atom.OptionalResultEventType == null) + { + throw new ExprValidationException( + "Missing @type(name) declaration providing the event type name of the return type for expression '" + + ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(atom.SplitterExpression) + "'"); + } + streamEventType = eventAdapterService.GetEventTypeByName(atom.OptionalResultEventType); + if (streamEventType == null) + { + throw new ExprValidationException( + "Event type by name '" + atom.OptionalResultEventType + "' could not be found"); + } + + var returnType = evaluator.ReturnType; + + // when the expression returns an array, allow array values to become the column of the single-column event type + if (returnType.IsArray && + streamEventType.PropertyNames.Length == 1 && + TypeHelper.IsSubclassOrImplementsInterface( + TypeHelper.GetBoxedType(returnType.GetElementType()), + TypeHelper.GetBoxedType(streamEventType.GetPropertyType(streamEventType.PropertyNames[0])))) + { + var writables = eventAdapterService.GetWriteableProperties(streamEventType, false); + if (!writables.IsEmpty()) + { + try + { + EventBeanManufacturer manufacturer = EventAdapterServiceHelper.GetManufacturer( + eventAdapterService, streamEventType, + new WriteablePropertyDescriptor[] { writables.First() }, + engineImportService, false, + eventAdapterService.EventAdapterAvroHandler); + containedEventEval = new ContainedEventEvalArrayToEvent(evaluator, manufacturer); + } + catch (EventBeanManufactureException e) + { + throw new ExprValidationException( + "Event type '" + streamEventType.Name + "' cannot be populated: " + e.Message, e); + } + } + else + { + throw new ExprValidationException("Event type '" + streamEventType.Name + "' cannot be written to"); + } + } + else if (returnType.IsArray() && returnType.GetElementType() == typeof(EventBean)) + { + containedEventEval = new ContainedEventEvalEventBeanArray(evaluator); + } + else + { + EventBeanFactory eventBeanFactory = EventAdapterServiceHelper.GetFactoryForType( + streamEventType, eventAdapterService); + // check expression result type against eventtype expected underlying type + if (returnType.IsArray()) + { + if (!TypeHelper.IsSubclassOrImplementsInterface(returnType.GetElementType(), streamEventType.UnderlyingType)) + { + throw new ExprValidationException( + "Event type '" + streamEventType.Name + "' underlying type " + + streamEventType.UnderlyingType.FullName + + " cannot be assigned a value of type " + TypeHelper.GetTypeNameFullyQualPretty(returnType)); + } + } + else if (GenericExtensions.IsGenericEnumerable(returnType) || returnType.IsImplementsInterface()) + { + // fine, assumed to return the right type + } + else + { + throw new ExprValidationException( + "Return type of expression '" + + ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(atom.SplitterExpression) + "' is '" + + returnType.FullName + "', expected an Iterable or array result"); + } + containedEventEval = new ContainedEventEvalExprNode(evaluator, eventBeanFactory); + } + expressionText = ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(validatedExprNode); + fragmentEventType = new FragmentEventType(streamEventType, true, false); + } + + // validate where clause, if any + streamEventTypes.Add(streamEventType); + streamNames.Add(atom.OptionalAsName); + streamNameAndNumber.Put(atom.OptionalAsName, i + 1); + expressionTexts.Add(expressionText); + + if (atom.OptionalWhereClause != null) + { + var whereTypes = streamEventTypes.ToArray(); + var whereStreamNames = streamNames.ToArray(); + var isIStreamOnly = new bool[streamNames.Count]; + isIStreamOnly.Fill(true); + StreamTypeService streamTypeService = new StreamTypeServiceImpl( + whereTypes, whereStreamNames, isIStreamOnly, engineURI, false); + var validationContext = new ExprValidationContext( + streamTypeService, engineImportService, statementExtensionSvcContext, null, timeProvider, variableService, tableService, + validateContext, eventAdapterService, statementName, statementId, annotations, null, scriptingService, + false, false, true, false, null, false); + whereClauses[i] = + ExprNodeUtility.GetValidatedSubtree( + ExprNodeOrigin.CONTAINEDEVENT, atom.OptionalWhereClause, validationContext).ExprEvaluator; + } + + // validate select clause + if (atom.OptionalSelectClause != null) + { + var whereTypes = streamEventTypes.ToArray(); + var whereStreamNames = streamNames.ToArray(); + var isIStreamOnly = new bool[streamNames.Count]; + isIStreamOnly.Fill(true); + StreamTypeService streamTypeService = new StreamTypeServiceImpl( + whereTypes, whereStreamNames, isIStreamOnly, engineURI, false); + var validationContext = new ExprValidationContext( + streamTypeService, engineImportService, statementExtensionSvcContext, null, timeProvider, variableService, tableService, + validateContext, eventAdapterService, statementName, statementId, annotations, null, scriptingService, + false, false, true, false, null, false); + + foreach (var raw in atom.OptionalSelectClause.SelectExprList) + { + if (raw is SelectClauseStreamRawSpec) + { + var rawStreamSpec = (SelectClauseStreamRawSpec)raw; + if (!streamNames.Contains(rawStreamSpec.StreamName)) + { + throw new ExprValidationException( + "Property rename '" + rawStreamSpec.StreamName + "' not found in path"); + } + var streamSpec = new SelectClauseStreamCompiledSpec( + rawStreamSpec.StreamName, rawStreamSpec.OptionalAsName); + int streamNumber = streamNameAndNumber.Get(rawStreamSpec.StreamName); + streamSpec.StreamNumber = streamNumber; + cumulativeSelectClause.Add(streamSpec); + } + else if (raw is SelectClauseExprRawSpec) + { + var exprSpec = (SelectClauseExprRawSpec)raw; + var exprCompiled = ExprNodeUtility.GetValidatedSubtree( + ExprNodeOrigin.CONTAINEDEVENT, exprSpec.SelectExpression, validationContext); + var resultName = exprSpec.OptionalAsName; + if (resultName == null) + { + resultName = ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(exprCompiled); + } + cumulativeSelectClause.Add( + new SelectClauseExprCompiledSpec( + exprCompiled, resultName, exprSpec.OptionalAsName, exprSpec.IsEvents)); + + var isMinimal = ExprNodeUtility.IsMinimalExpression(exprCompiled); + if (isMinimal != null) + { + throw new ExprValidationException( + "Expression in a property-selection may not utilize " + isMinimal); + } + } + else if (raw is SelectClauseElementWildcard) + { + // wildcards are stream selects: we assign a stream name (any) and add a stream wildcard select + var streamNameAtom = atom.OptionalAsName; + if (streamNameAtom == null) + { + streamNameAtom = UuidGenerator.Generate(); + } + + var streamSpec = new SelectClauseStreamCompiledSpec(streamNameAtom, atom.OptionalAsName); + var streamNumber = i + 1; + streamSpec.StreamNumber = streamNumber; + cumulativeSelectClause.Add(streamSpec); + } + else + { + throw new IllegalStateException("Unknown select clause item:" + raw); + } + } + } + + currentEventType = fragmentEventType.FragmentType; + fragmentEventTypes[i] = fragmentEventType; + containedEventEvals[i] = containedEventEval; + } + + if (cumulativeSelectClause.IsEmpty()) + { + if (length == 1) + { + return new PropertyEvaluatorSimple( + containedEventEvals[0], fragmentEventTypes[0], whereClauses[0], expressionTexts[0]); + } + else + { + return new PropertyEvaluatorNested(containedEventEvals, fragmentEventTypes, whereClauses, expressionTexts); + } + } + else + { + var accumulative = new PropertyEvaluatorAccumulative( + containedEventEvals, fragmentEventTypes, whereClauses, expressionTexts); + + var whereTypes = streamEventTypes.ToArray(); + var whereStreamNames = streamNames.ToArray(); + var isIStreamOnly = new bool[streamNames.Count]; + isIStreamOnly.Fill(true); + StreamTypeService streamTypeService = new StreamTypeServiceImpl( + whereTypes, whereStreamNames, isIStreamOnly, engineURI, false); + + var cumulativeSelectArr = cumulativeSelectClause.ToArray(); + var selectExpr = SelectExprProcessorFactory.GetProcessor( + assignedTypeNumberStack, cumulativeSelectArr, false, null, null, null, streamTypeService, + eventAdapterService, null, null, null, engineImportService, validateContext, variableService, + scriptingService, + tableService, timeProvider, engineURI, statementId, statementName, annotations, null, configuration, null, + namedWindowMgmtService, null, null, statementExtensionSvcContext); + return new PropertyEvaluatorSelect(selectExpr, accumulative); + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/property/PropertyEvaluatorNested.cs b/NEsper.Core/NEsper.Core/epl/property/PropertyEvaluatorNested.cs new file mode 100755 index 000000000..e1e44b03a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/property/PropertyEvaluatorNested.cs @@ -0,0 +1,183 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.property +{ + /// + /// A property evaluator that considers nested properties and that considers where-clauses + /// but does not consider select-clauses. + /// + public class PropertyEvaluatorNested : PropertyEvaluator + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly ContainedEventEval[] _containedEventEvals; + private readonly EventBean[] _eventsPerStream; + private readonly IList _expressionTexts; + private readonly FragmentEventType[] _fragmentEventType; + private readonly int _lastLevel; + private readonly ExprEvaluator[] _whereClauses; + + /// + /// Ctor. + /// + /// property getters or other evaluators + /// the fragments + /// the where clauses + /// the property names that are staggered + public PropertyEvaluatorNested( + ContainedEventEval[] containedEventEvals, + FragmentEventType[] fragmentEventType, + ExprEvaluator[] whereClauses, + IList expressionTexts) + { + _fragmentEventType = fragmentEventType; + _containedEventEvals = containedEventEvals; + _whereClauses = whereClauses; + _lastLevel = fragmentEventType.Length - 1; + _eventsPerStream = new EventBean[fragmentEventType.Length + 1]; + _expressionTexts = expressionTexts; + } + + public EventBean[] GetProperty(EventBean theEvent, ExprEvaluatorContext exprEvaluatorContext) + { + var resultEvents = new ArrayDeque(); + _eventsPerStream[0] = theEvent; + PopulateEvents(theEvent, 0, resultEvents, exprEvaluatorContext); + if (resultEvents.IsEmpty()) + { + return null; + } + return resultEvents.ToArray(); + } + + public EventType FragmentEventType + { + get { return _fragmentEventType[_lastLevel].FragmentType; } + } + + public bool CompareTo(PropertyEvaluator otherEval) + { + return false; + } + + private void PopulateEvents( + EventBean branch, + int level, + ICollection events, + ExprEvaluatorContext exprEvaluatorContext) + { + try + { + object result = _containedEventEvals[level].GetFragment(branch, _eventsPerStream, exprEvaluatorContext); + + if (_fragmentEventType[level].IsIndexed) + { + var fragments = (EventBean[]) result; + if (level == _lastLevel) + { + if (_whereClauses[level] != null) + { + foreach (EventBean theEvent in fragments) + { + _eventsPerStream[level + 1] = theEvent; + if (ExprNodeUtility.ApplyFilterExpression( + _whereClauses[level], _eventsPerStream, exprEvaluatorContext)) + { + events.Add(theEvent); + } + } + } + else + { + events.AddAll(fragments); + } + } + else + { + if (_whereClauses[level] != null) + { + foreach (EventBean next in fragments) + { + _eventsPerStream[level + 1] = next; + if (ExprNodeUtility.ApplyFilterExpression( + _whereClauses[level], _eventsPerStream, exprEvaluatorContext)) + { + PopulateEvents(next, level + 1, events, exprEvaluatorContext); + } + } + } + else + { + foreach (EventBean next in fragments) + { + _eventsPerStream[level + 1] = next; + PopulateEvents(next, level + 1, events, exprEvaluatorContext); + } + } + } + } + else + { + var fragment = (EventBean) result; + if (level == _lastLevel) + { + if (_whereClauses[level] != null) + { + _eventsPerStream[level + 1] = fragment; + if (ExprNodeUtility.ApplyFilterExpression( + _whereClauses[level], _eventsPerStream, exprEvaluatorContext)) + { + events.Add(fragment); + } + } + else + { + events.Add(fragment); + } + } + else + { + if (_whereClauses[level] != null) + { + _eventsPerStream[level + 1] = fragment; + if (ExprNodeUtility.ApplyFilterExpression( + _whereClauses[level], _eventsPerStream, exprEvaluatorContext)) + { + PopulateEvents(fragment, level + 1, events, exprEvaluatorContext); + } + } + else + { + _eventsPerStream[level + 1] = fragment; + PopulateEvents(fragment, level + 1, events, exprEvaluatorContext); + } + } + } + } + catch (Exception ex) + { + Log.Error( + "Unexpected error evaluating property expression for event of type '" + + branch.EventType.Name + + "' and property '" + + _expressionTexts[level + 1] + "': " + ex.Message, ex); + } + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/property/PropertyEvaluatorSelect.cs b/NEsper.Core/NEsper.Core/epl/property/PropertyEvaluatorSelect.cs new file mode 100755 index 000000000..34ce78b6f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/property/PropertyEvaluatorSelect.cs @@ -0,0 +1,64 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.epl.property +{ + /// + /// Property evaluator that considers a select-clauses and relies on an accumulative + /// property evaluator that presents events for all columns and rows. + /// + public class PropertyEvaluatorSelect : PropertyEvaluator + { + private readonly SelectExprProcessor _selectExprProcessor; + private readonly PropertyEvaluatorAccumulative _accumulative; + + /// Ctor. + /// evaluates the select clause + /// provides property events for input events + public PropertyEvaluatorSelect(SelectExprProcessor selectExprProcessor, PropertyEvaluatorAccumulative accumulative) + { + _selectExprProcessor = selectExprProcessor; + _accumulative = accumulative; + } + + public EventBean[] GetProperty(EventBean theEvent, ExprEvaluatorContext exprEvaluatorContext) + { + var rows = _accumulative.GetAccumulative(theEvent, exprEvaluatorContext); + if ((rows == null) || (rows.IsEmpty())) + { + return null; + } + var result = new LinkedList(); + foreach (EventBean[] row in rows) + { + EventBean bean = _selectExprProcessor.Process(row, true, false, exprEvaluatorContext); + result.AddLast(bean); + } + return result.ToArray(); + } + + public EventType FragmentEventType + { + get { return _selectExprProcessor.ResultEventType; } + } + + public bool CompareTo(PropertyEvaluator otherFilterPropertyEval) + { + return false; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/property/PropertyEvaluatorSimple.cs b/NEsper.Core/NEsper.Core/epl/property/PropertyEvaluatorSimple.cs new file mode 100755 index 000000000..7c45d7638 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/property/PropertyEvaluatorSimple.cs @@ -0,0 +1,126 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.property +{ + /// + /// Property evaluator that considers only level one and considers a where-clause, + /// but does not consider a select clause or N-level. + /// + public class PropertyEvaluatorSimple : PropertyEvaluator + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly ContainedEventEval _containedEventEval; + private readonly string _expressionText; + private readonly ExprEvaluator _filter; + private readonly FragmentEventType _fragmentEventType; + + /// + /// Ctor. + /// + /// property getter or other evaluator + /// property event type + /// optional where-clause expression + /// the property name + public PropertyEvaluatorSimple( + ContainedEventEval containedEventEval, + FragmentEventType fragmentEventType, + ExprEvaluator filter, + string expressionText) + { + _fragmentEventType = fragmentEventType; + _containedEventEval = containedEventEval; + _filter = filter; + _expressionText = expressionText; + } + + /// + /// Returns the property name. + /// + /// property name + public string ExpressionText + { + get { return _expressionText; } + } + + /// + /// Returns the filter. + /// + /// filter + public ExprEvaluator Filter + { + get { return _filter; } + } + + public EventBean[] GetProperty(EventBean theEvent, ExprEvaluatorContext exprEvaluatorContext) + { + try + { + Object result = _containedEventEval.GetFragment( + theEvent, new EventBean[] { theEvent }, exprEvaluatorContext); + + EventBean[] rows; + if (_fragmentEventType.IsIndexed) + { + rows = (EventBean[]) result; + } + else + { + rows = new EventBean[] { (EventBean) result }; + } + + if (_filter == null) + { + return rows; + } + return ExprNodeUtility.ApplyFilterExpression( + _filter, theEvent, (EventBean[]) result, exprEvaluatorContext); + } + catch (Exception ex) + { + Log.Error( + "Unexpected error evaluating property expression for event of type '" + + theEvent.EventType.Name + + "' and property '" + + _expressionText + "': " + ex.Message, ex); + } + return null; + } + + public EventType FragmentEventType + { + get { return _fragmentEventType.FragmentType; } + } + + public bool CompareTo(PropertyEvaluator otherEval) + { + if (!(otherEval is PropertyEvaluatorSimple)) + { + return false; + } + var other = (PropertyEvaluatorSimple) otherEval; + if (!other.ExpressionText.Equals(ExpressionText)) + { + return false; + } + if ((other.Filter == null) && (Filter == null)) + { + return true; + } + return false; + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/rettype/ClassEPType.cs b/NEsper.Core/NEsper.Core/epl/rettype/ClassEPType.cs new file mode 100755 index 000000000..83903e45c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/rettype/ClassEPType.cs @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.rettype +{ + /// + /// Any primitive type as well as any class and other non-array or non-collection type + /// + public class ClassEPType : EPType + { + private readonly Type _type; + + internal ClassEPType(Type type) { + _type = type; + } + + public Type Clazz + { + get { return _type; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/rettype/ClassMultiValuedEPType.cs b/NEsper.Core/NEsper.Core/epl/rettype/ClassMultiValuedEPType.cs new file mode 100755 index 000000000..5aa444cb9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/rettype/ClassMultiValuedEPType.cs @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.rettype +{ + /// + /// An array or collection of native values. Always has a component type. + /// Either: - array then "clazz.Array" returns true. - collection then clazz : collection + /// + public class ClassMultiValuedEPType : EPType + { + private readonly Type _container; + private readonly Type _component; + + internal ClassMultiValuedEPType(Type container, Type component) + { + _container = container; + _component = component; + } + + public Type Container + { + get { return _container; } + } + + public Type Component + { + get { return _component; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/rettype/EPType.cs b/NEsper.Core/NEsper.Core/epl/rettype/EPType.cs new file mode 100755 index 000000000..8eb4bfde6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/rettype/EPType.cs @@ -0,0 +1,14 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.rettype +{ + public interface EPType + { + } +} diff --git a/NEsper.Core/NEsper.Core/epl/rettype/EPTypeHelper.cs b/NEsper.Core/NEsper.Core/epl/rettype/EPTypeHelper.cs new file mode 100755 index 000000000..84dae4d88 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/rettype/EPTypeHelper.cs @@ -0,0 +1,325 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.rettype +{ + /// + /// Carries return type information related to the return values returned by expressions. + /// + /// Use factory methods to initialize return type information according to the return + /// values that your expression is going to provide. + /// + /// Use + /// to indicate that the expression returns a collection of events. + /// Use + /// to indicate that the expression returns a single event. + /// Use to indicate that + /// the expression returns a collection of single values. A single value can be any object including null. + /// Use to indicate that the expression returns an array + /// of single values. A single value can be any object including null. + /// Use to indicate that the expression + /// returns a single value. A single value can be any object including null. Such + /// expression results cannot be used as input to enumeration methods, for example. + /// + public static class EPTypeHelper + { + public static EventType GetEventTypeSingleValued(this EPType type) + { + if (type is EventEPType) + { + return ((EventEPType)type).EventType; + } + return null; + } + + public static EventType GetEventTypeMultiValued(this EPType type) + { + if (type is EventMultiValuedEPType) + { + return ((EventMultiValuedEPType)type).Component; + } + return null; + } + + public static Type GetClassMultiValued(this EPType type) + { + if (type is ClassMultiValuedEPType) + { + return ((ClassMultiValuedEPType)type).Component; + } + return null; + } + + public static Type GetClassSingleValued(this EPType type) + { + if (type is ClassEPType) + { + return ((ClassEPType)type).Clazz; + } + return null; + } + + public static bool IsCarryEvent(this EPType epType) + { + return epType is EventMultiValuedEPType || epType is EventEPType; + } + + public static EventType GetEventType(this EPType epType) + { + if (epType is EventMultiValuedEPType) + { + return ((EventMultiValuedEPType)epType).Component; + } + if (epType is EventEPType) + { + return ((EventEPType)epType).EventType; + } + return null; + } + + /// Indicate that the expression return type is an array of a given component type. + /// array component type + /// array of single value expression result type + public static EPType Array(Type arrayComponentType) + { + if (arrayComponentType == null) + { + throw new ArgumentException("Invalid null array component type"); + } + return new ClassMultiValuedEPType(TypeHelper.GetArrayType(arrayComponentType), arrayComponentType); + } + + /// + /// Indicate that the expression return type is a single (non-enumerable) value of the given type. + /// The expression can still return an array or collection or events however since the engine would + /// not know the type of such objects and may not use runtime reflection it may not allow certain + /// operations on expression results. + /// + /// type of single value returned, or null to indicate that the expression always returns null + /// single-value expression result type + public static EPType SingleValue(Type singleValueType) + { + // null value allowed + if (singleValueType != null && singleValueType.IsArray) + { + return new ClassMultiValuedEPType(singleValueType, singleValueType.GetElementType()); + } + return new ClassEPType(singleValueType); + } + + public static EPType NullValue + { + get { return NullEPType.INSTANCE; } + } + + /// + /// Indicate that the expression return type is a collection of a given component type. + /// + /// collection component type + /// + /// collection of single value expression result type + /// + public static EPType CollectionOfSingleValue(Type collectionComponentType) + { + if (collectionComponentType == null) + { + throw new ArgumentException("Invalid null collection component type"); + } + + var collectionType = typeof(ICollection<>).MakeGenericType(collectionComponentType); + return new ClassMultiValuedEPType(collectionType, collectionComponentType); + //return new ClassMultiValuedEPType(typeof(ICollection), collectionComponentType); + } + + /// + /// Indicate that the expression return type is a collection of a given type of events. + /// + /// the event type of the events that are part of the collection + /// + /// collection of events expression result type + /// + public static EPType CollectionOfEvents(EventType eventTypeOfCollectionEvents) + { + if (eventTypeOfCollectionEvents == null) + { + throw new ArgumentException("Invalid null event type"); + } + + return new EventMultiValuedEPType(typeof(ICollection), eventTypeOfCollectionEvents); + } + + /// Indicate that the expression return type is single event of a given event type. + /// the event type of the event returned + /// single-event expression result type + public static EPType SingleEvent(EventType eventTypeOfSingleEvent) + { + if (eventTypeOfSingleEvent == null) + { + throw new ArgumentException("Invalid null event type"); + } + return new EventEPType(eventTypeOfSingleEvent); + } + + /// Interrogate the provided method and determine whether it returns single-value, array of single-value or collection of single-value and their component type. + /// the class methods + /// expression return type + public static EPType FromMethod(MethodInfo method) + { + var returnType = method.ReturnType; + if (returnType.IsGenericCollection()) + { + var componentType = TypeHelper.GetGenericReturnType(method, true); + return CollectionOfSingleValue(componentType); + } + if (method.ReturnType.IsArray) + { + var componentType = method.ReturnType.GetElementType(); + return Array(componentType); + } + return SingleValue(method.ReturnType); + } + + /// Returns a nice text detailing the expression result type. + /// descriptive text + public static String ToTypeDescriptive(this EPType epType) + { + if (epType is EventEPType) + { + var type = (EventEPType)epType; + return "event type '" + type.EventType.Name + "'"; + } + else if (epType is EventMultiValuedEPType) + { + var type = (EventMultiValuedEPType)epType; + if (type.Container == typeof(EventType[])) + { + return "array of events of type '" + type.Component.Name + "'"; + } + else + { + return "collection of events of type '" + type.Component.Name + "'"; + } + } + else if (epType is ClassMultiValuedEPType) + { + var type = (ClassMultiValuedEPType)epType; + if (type.Container.IsArray) + { + return "array of " + type.Component.Name; + } + else + { + return "collection of " + type.Component.Name; + } + } + else if (epType is ClassEPType) + { + var type = (ClassEPType)epType; + return "class " + type.Clazz.GetTypeNameFullyQualPretty(); + } + else if (epType is NullEPType) + { + return "null type"; + } + else + { + throw new ArgumentException("Unrecognized type " + epType); + } + } + + public static Type GetNormalizedClass(this EPType theType) + { + if (theType is EventMultiValuedEPType) + { + var type = (EventMultiValuedEPType)theType; + return TypeHelper.GetArrayType(type.Component.UnderlyingType); + } + else if (theType is EventEPType) + { + var type = (EventEPType)theType; + return type.EventType.UnderlyingType; + } + else if (theType is ClassMultiValuedEPType) + { + var type = (ClassMultiValuedEPType)theType; + return type.Container; + } + else if (theType is ClassEPType) + { + var type = (ClassEPType)theType; + return type.Clazz; + } + else if (theType is NullEPType) + { + return null; + } + throw new ArgumentException("Unrecognized type " + theType); + } + + public static EPType OptionalFromEnumerationExpr(int statementId, EventAdapterService eventAdapterService, ExprNode exprNode) + { + if (!(exprNode is ExprEvaluatorEnumeration)) + { + return null; + } + var enumInfo = (ExprEvaluatorEnumeration)exprNode; + if (enumInfo.ComponentTypeCollection != null) + { + return EPTypeHelper.CollectionOfSingleValue(enumInfo.ComponentTypeCollection); + } + var eventTypeSingle = enumInfo.GetEventTypeSingle(eventAdapterService, statementId); + if (eventTypeSingle != null) + { + return EPTypeHelper.SingleEvent(eventTypeSingle); + } + var eventTypeColl = enumInfo.GetEventTypeCollection(eventAdapterService, statementId); + if (eventTypeColl != null) + { + return EPTypeHelper.CollectionOfEvents(eventTypeColl); + } + return null; + } + + public static EventType OptionalIsEventTypeColl(EPType type) + { + if (type != null && type is EventMultiValuedEPType) + { + return ((EventMultiValuedEPType)type).Component; + } + return null; + } + + public static Type OptionalIsComponentTypeColl(EPType type) + { + if (type != null && type is ClassMultiValuedEPType) + { + return ((ClassMultiValuedEPType)type).Component; + } + return null; + } + + public static EventType OptionalIsEventTypeSingle(EPType type) + { + if (type != null && type is EventEPType) + { + return ((EventEPType)type).EventType; + } + return null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/rettype/EventEPType.cs b/NEsper.Core/NEsper.Core/epl/rettype/EventEPType.cs new file mode 100755 index 000000000..d74dcb5bb --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/rettype/EventEPType.cs @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; + +namespace com.espertech.esper.epl.rettype +{ + public class EventEPType : EPType + { + private readonly EventType _type; + + internal EventEPType(EventType type) + { + _type = type; + } + + public EventType EventType + { + get { return _type; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/rettype/EventMultiValuedEPType.cs b/NEsper.Core/NEsper.Core/epl/rettype/EventMultiValuedEPType.cs new file mode 100755 index 000000000..3053c5759 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/rettype/EventMultiValuedEPType.cs @@ -0,0 +1,30 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.epl.rettype +{ + /// + /// Clazz can be either - Collection - Array i.e. "EventType[].class" + /// + public class EventMultiValuedEPType : EPType + { + internal EventMultiValuedEPType(Type container, EventType component) + { + Container = container; + Component = component; + } + + public Type Container { get; private set; } + + public EventType Component { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/rettype/NullEPType.cs b/NEsper.Core/NEsper.Core/epl/rettype/NullEPType.cs new file mode 100755 index 000000000..6ba0d90bc --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/rettype/NullEPType.cs @@ -0,0 +1,15 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.rettype +{ + public class NullEPType : EPType + { + public static readonly NullEPType INSTANCE = new NullEPType(); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/script/AgentInstanceScriptContext.cs b/NEsper.Core/NEsper.Core/epl/script/AgentInstanceScriptContext.cs new file mode 100755 index 000000000..637773a21 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/script/AgentInstanceScriptContext.cs @@ -0,0 +1,59 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client.hook; +using com.espertech.esper.compat.collections; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.script +{ + /// Context-partition local script context. + public class AgentInstanceScriptContext : EPLScriptContext + { + private readonly EventBeanService _eventBeanService; + private IDictionary _scriptProperties; + + private AgentInstanceScriptContext(EventBeanService eventBeanService) + { + _eventBeanService = eventBeanService; + } + + public static AgentInstanceScriptContext From(EventAdapterService eventAdapterService) + { + return new AgentInstanceScriptContext(eventAdapterService); + } + + public EventBeanService EventBeanService + { + get { return _eventBeanService; } + } + + public void SetScriptAttribute(string attribute, Object value) + { + AllocateScriptProperties(); + _scriptProperties.Put(attribute, value); + } + + public Object GetScriptAttribute(string attribute) + { + AllocateScriptProperties(); + return _scriptProperties.Get(attribute); + } + + private void AllocateScriptProperties() + { + if (_scriptProperties == null) + { + _scriptProperties = new Dictionary(); + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/script/ExprNodeScript.cs b/NEsper.Core/NEsper.Core/epl/script/ExprNodeScript.cs new file mode 100755 index 000000000..faa1c4faa --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/script/ExprNodeScript.cs @@ -0,0 +1,238 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; +using com.espertech.esper.events; +using com.espertech.esper.script; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.script +{ + [Serializable] + public class ExprNodeScript + : ExprNodeBase + , ExprNodeInnerNodeProvider + { + public const string CONTEXT_BINDING_NAME = "epl"; + + private readonly string _defaultDialect; + + [NonSerialized] private ExprNodeScriptEvaluator _evaluator; + + public ExprNodeScript(string defaultDialect, ExpressionScriptProvided script, IList parameters) + { + _defaultDialect = defaultDialect; + Script = script; + Parameters = parameters; + } + + public IList AdditionalNodes + { + get { return Parameters; } + } + + public override ExprEvaluator ExprEvaluator + { + get { return _evaluator; } + } + + public IList Parameters { get; private set; } + + public string EventTypeNameAnnotation + { + get { return Script.OptionalEventTypeName; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + writer.Write(Script.Name); + ExprNodeUtility.ToExpressionStringIncludeParen(Parameters, writer); + } + + public override ExprPrecedenceEnum Precedence + { + get { return ExprPrecedenceEnum.UNARY; } + } + + public ExpressionScriptProvided Script { get; private set; } + + public override bool IsConstantResult + { + get { return false; } + } + + public override bool EqualsNode(ExprNode node) + { + if (this == node) return true; + if (node == null || GetType() != node.GetType()) return false; + + var that = (ExprNodeScript) node; + + if (Script != null ? !Script.Equals(that.Script) : that.Script != null) return false; + return ExprNodeUtility.DeepEquals(Parameters, that.Parameters); + } + + public override ExprNode Validate(ExprValidationContext validationContext) + { + if (_evaluator != null) + { + return null; + } + + if (Script.ParameterNames.Count != Parameters.Count) + { + throw new ExprValidationException( + string.Format( + "Invalid number of parameters for script '{0}', expected {1} parameters but received {2} parameters", + Script.Name, + Script.ParameterNames.Count, + Parameters.Count)); + } + + // validate all expression parameters + var validatedParameters = Parameters + .Select( + expr => ExprNodeUtility.GetValidatedSubtree(ExprNodeOrigin.SCRIPTPARAMS, expr, validationContext)) + .ToList(); + + // set up map of input parameter names and evaluators + var inputParamNames = new string[Script.ParameterNames.Count]; + var evaluators = new ExprEvaluator[Script.ParameterNames.Count]; + + for (int i = 0; i < Script.ParameterNames.Count; i++) + { + inputParamNames[i] = Script.ParameterNames[i]; + evaluators[i] = validatedParameters[i].ExprEvaluator; + } + + // Compile script + if (Script.Compiled == null) + { + CompileScript(validationContext.ScriptingService, evaluators, validationContext.EngineImportService); + } + + // Determine declared return type + Type declaredReturnType = GetDeclaredReturnType(Script.OptionalReturnTypeName, validationContext); + if (Script.IsOptionalReturnTypeIsArray && declaredReturnType != null) + { + declaredReturnType = TypeHelper.GetArrayType(declaredReturnType); + } + Type returnType; + if (Script.Compiled.KnownReturnType == null && Script.OptionalReturnTypeName == null) + { + returnType = typeof (Object); + } + else if (Script.Compiled.KnownReturnType != null) + { + if (declaredReturnType == null) + { + returnType = Script.Compiled.KnownReturnType; + } + else + { + Type knownReturnType = Script.Compiled.KnownReturnType; + if (declaredReturnType.IsArray && knownReturnType.IsArray) + { + // we are fine + } + else if (!knownReturnType.IsAssignmentCompatible(declaredReturnType)) + { + throw new ExprValidationException( + "Return type and declared type not compatible for script '" + Script.Name + + "', known return type is " + knownReturnType.Name + " versus declared return type " + + declaredReturnType.Name); + } + returnType = declaredReturnType; + } + } + else + { + returnType = declaredReturnType; + } + if (returnType == null) + { + returnType = typeof (Object); + } + + EventType eventTypeCollection = null; + if (Script.OptionalEventTypeName != null) + { + if (returnType.IsArray && returnType.GetElementType() == typeof (EventBean)) + { + eventTypeCollection = EventTypeUtility.RequireEventType( + "Script", Script.Name, validationContext.EventAdapterService, Script.OptionalEventTypeName); + } + else + { + throw new ExprValidationException(EventTypeUtility.DisallowedAtTypeMessage()); + } + } + + // Prepare evaluator - this sets the evaluator + PrepareEvaluator( + validationContext.StatementName, inputParamNames, evaluators, returnType, eventTypeCollection); + return null; + } + + private void CompileScript(ScriptingService scriptingService, ExprEvaluator[] evaluators, EngineImportService engineImportService) + { + Script.Compiled = new ExpressionScriptCompiledImpl( + scriptingService.Compile( + Script.OptionalDialect ?? _defaultDialect, + Script)); + } + + private void PrepareEvaluator(string statementName, string[] inputParamNames, ExprEvaluator[] evaluators, Type returnType, EventType eventTypeCollection) + { + var scriptExpression = (ExpressionScriptCompiledImpl)Script.Compiled; + _evaluator = new ExprNodeScriptEvalImpl(Script.Name, statementName, inputParamNames, evaluators, returnType, eventTypeCollection, scriptExpression.ScriptAction); + } + + private Type GetDeclaredReturnType(string returnTypeName, ExprValidationContext validationContext) + { + if (returnTypeName == null) + { + return null; + } + + if (returnTypeName.Equals("void")) + { + return null; + } + + var returnType = TypeHelper.GetTypeForSimpleName(returnTypeName); + if (returnType != null) + { + return returnType; + } + + if (returnTypeName.Equals("EventBean")) + { + return typeof (EventBean); + } + + try + { + return validationContext.EngineImportService.ResolveType(returnTypeName, false); + } + catch (EngineImportException e1) + { + throw new ExprValidationException( + "Failed to resolve return type '" + returnTypeName + "' specified for script '" + Script.Name + "'"); + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/script/ExprNodeScriptEvalBase.cs b/NEsper.Core/NEsper.Core/epl/script/ExprNodeScriptEvalBase.cs new file mode 100755 index 000000000..7eeeaca52 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/script/ExprNodeScriptEvalBase.cs @@ -0,0 +1,126 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.script +{ + [Serializable] + public abstract class ExprNodeScriptEvalBase + : ExprEvaluator + , ExprEvaluatorEnumeration + { + protected readonly String ScriptName; + protected readonly String StatementName; + protected readonly String[] Names; + protected readonly ExprEvaluator[] Parameters; + private readonly Type _returnType; + protected readonly EventType EventTypeCollection; + protected readonly Coercer Coercer; + + protected ExprNodeScriptEvalBase( + String scriptName, + String statementName, + String[] names, + ExprEvaluator[] parameters, + Type returnType, + EventType eventTypeCollection) + { + ScriptName = scriptName; + StatementName = statementName; + Names = names; + Parameters = parameters; + _returnType = returnType; + EventTypeCollection = eventTypeCollection; + + if (returnType.IsNumeric()) + { + Coercer = CoercerFactory.GetCoercer(returnType.GetBoxedType()); + } + else + { + Coercer = null; + } + } + + public Type ReturnType + { + get { return _returnType; } + } + + public EventType GetEventTypeCollection(EventAdapterService eventAdapterService, int statementId) + { + return EventTypeCollection; + } + + public ICollection EvaluateGetROCollectionEvents(EvaluateParams evaluateParams) + { + var result = Evaluate(evaluateParams); + if (result == null) + { + return null; + } + + return result.Unwrap(); + } + + public Type ComponentTypeCollection + { + get + { + if (_returnType.IsArray) + { + return _returnType.GetElementType(); + } + return null; + } + } + + public ICollection EvaluateGetROCollectionScalar(EvaluateParams evaluateParams) + { + var result = Evaluate(evaluateParams); + if (result == null) + { + return null; + } + + if (result is ICollection) + { + return (ICollection)result; + } + + if (result is Array) + { + return ((Array)result).Cast().ToList(); + } + + throw new ArgumentException("invalid result type returned from evaluate; expected array"); + } + + public EventType GetEventTypeSingle(EventAdapterService eventAdapterService, int statementId) + { + return null; + } + + public EventBean EvaluateGetEventBean(EvaluateParams evaluateParams) + { + return null; + } + + public abstract object Evaluate(EvaluateParams evaluateParams); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/script/ExprNodeScriptEvalImpl.cs b/NEsper.Core/NEsper.Core/epl/script/ExprNodeScriptEvalImpl.cs new file mode 100755 index 000000000..914515186 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/script/ExprNodeScriptEvalImpl.cs @@ -0,0 +1,86 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.script; + +namespace com.espertech.esper.epl.script +{ + using ScriptAction = Func; + + public class ExprNodeScriptEvalImpl + : ExprNodeScriptEvalBase + , ExprNodeScriptEvaluator + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly ScriptAction _scriptAction; + + public ExprNodeScriptEvalImpl(String scriptName, String statementName, String[] names, ExprEvaluator[] parameters, Type returnType, EventType eventTypeCollection, Func scriptAction) + : base(scriptName, statementName, names, parameters, returnType, eventTypeCollection) + { + _scriptAction = scriptAction; + } + + public object Evaluate(object[] lookupValues, ExprEvaluatorContext exprEvaluatorContext) + { + var bindings = GetBindings(exprEvaluatorContext); + for (int i = 0; i < Names.Length; i++) + { + bindings.Put(Names[i], lookupValues[i]); + } + + return EvaluateInternal(new ScriptArgs { Bindings = bindings }); + } + + public override object Evaluate(EvaluateParams evaluateParams) + { + var bindings = GetBindings(evaluateParams.ExprEvaluatorContext); + for (int i = 0; i < Names.Length; i++) + { + bindings.Put(Names[i], Parameters[i].Evaluate(evaluateParams)); + } + + return EvaluateInternal(new ScriptArgs { Bindings = bindings }); + } + + private IDictionary GetBindings(ExprEvaluatorContext exprEvaluatorContext) + { + var bindings = new Dictionary(); + bindings.Put(ExprNodeScript.CONTEXT_BINDING_NAME, exprEvaluatorContext.AllocateAgentInstanceScriptContext); + return bindings; + } + + public object EvaluateInternal(ScriptArgs scriptArgs) + { + try + { + var result = _scriptAction.Invoke(scriptArgs); + + if (Coercer != null) + { + result = Coercer.Invoke(result); + } + + return result; + } + catch (Exception e) + { + string message = "Unexpected exception executing script '" + ScriptName + "' for statement '" + StatementName + "' : " + e.Message; + Log.Error(message, e); + throw new EPException(message, e); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/script/ExprNodeScriptEvaluator.cs b/NEsper.Core/NEsper.Core/epl/script/ExprNodeScriptEvaluator.cs new file mode 100755 index 000000000..768cf703e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/script/ExprNodeScriptEvaluator.cs @@ -0,0 +1,19 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.script +{ + public interface ExprNodeScriptEvaluator : ExprEvaluator + { + Object Evaluate(Object[] lookupValues, ExprEvaluatorContext exprEvaluatorContext); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/script/ExpressionScriptCompiledImpl.cs b/NEsper.Core/NEsper.Core/epl/script/ExpressionScriptCompiledImpl.cs new file mode 100755 index 000000000..3ba2cde8d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/script/ExpressionScriptCompiledImpl.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.spec; +using com.espertech.esper.script; + +namespace com.espertech.esper.epl.script +{ + public class ExpressionScriptCompiledImpl : ExpressionScriptCompiled + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// Initializes a new instance of the class. + /// + /// The script action. + public ExpressionScriptCompiledImpl(Func scriptAction) + { + ScriptAction = scriptAction; + } + + public Func ScriptAction { get; private set; } + + public Type KnownReturnType + { + get { return null; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/script/ScriptCompiler.cs b/NEsper.Core/NEsper.Core/epl/script/ScriptCompiler.cs new file mode 100755 index 000000000..8b5c4c0d8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/script/ScriptCompiler.cs @@ -0,0 +1,366 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.script +{ +#if false + public class ScriptCompiler + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// Default namespaces + /// + private static readonly string[] DefaultNamespaces = + { + "System", + "System.Collections", + "System.Collections.Generic", + "System.IO", + "System.Text", + "com.espertech.esper.client", + "com.espertech.esper.events" + }; + + /// + /// Gets or sets the language. + /// + /// The language. + public string Language { get; set; } + + /// + /// Gets or sets the references. + /// + /// The references. + public StringCollection References { get; set; } + + /// + /// Gets or sets the imports. + /// + /// The imports. + public StringCollection Imports { get; set; } + + /// + /// Gets or sets the main class containing the static ScriptMain. + /// + /// The main class. + public string MainClass { get; set; } + + /// + /// Gets or sets the name of the root class. + /// + /// The name of the root class. + public string RootClassName { get; set; } + + /// + /// Gets or sets the code to be executed. + /// + /// The code. + public string Code + { + get { return Script.Expression; } + } + + /// + /// Gets or sets the script. + /// + /// The script. + public ExpressionScriptProvided Script { get; set; } + + /// + /// Initializes a new instance of the class. + /// + public ScriptCompiler() + { + RootClassName = "esper" + Guid.NewGuid().ToString("Count", + CultureInfo.InvariantCulture); + References = new StringCollection(); + Imports = new StringCollection(); + } + + /// + /// Compiles the C# code. + /// + /// + public MethodInfo Compile() + { + var compilerInfo = CreateCompilerInfo(Language); + + var options = new CompilerParameters(); + options.GenerateExecutable = false; + options.GenerateInMemory = true; + options.MainClass = MainClass; + + // implicitly reference the NEsper assembly + options.ReferencedAssemblies.Add(typeof(EPServiceProvider).Assembly.Location); + + // add (and load) assemblies specified by user + foreach (var assemblyFile in References) + { + try + { + // load the assembly into current AppDomain to ensure it is + // available when executing the emitted assembly + var asm = Assembly.LoadFrom(assemblyFile); + + // Log the assembly being added to the CompilerParameters + Log.Debug("Adding assembly {0}", asm.GetName().Name); + + // add the location of the loaded assembly + if (!string.IsNullOrEmpty(asm.Location)) + { + options.ReferencedAssemblies.Add(asm.Location); + } + } + catch (Exception ex) + { + throw new ExprValidationException("unable to load assembly", ex); + } + } + + var imports = new StringCollection(); + foreach (var import in Imports) + { + imports.Add(import); + } + + // generate the code + var compileUnit = compilerInfo.GenerateCode(RootClassName, Script, imports); + + var stringWriter = new StringWriter(CultureInfo.InvariantCulture); + + compilerInfo.Provider.GenerateCodeFromCompileUnit(compileUnit, stringWriter, null); + string code = stringWriter.ToString(); + + Log.Debug("Generated script: {0}", code); + + var results = compilerInfo.Provider.CompileAssemblyFromDom(options, compileUnit); + + if (results.Errors.Count > 0) + { + throw new ScriptCompilationException("failed to compile script", results.Errors.Cast().ToList()); + } + + var compiled = results.CompiledAssembly; + + var mainClass = RootClassName; + if (!string.IsNullOrEmpty(MainClass)) + { + mainClass += "+" + MainClass; + } + + var mainType = compiled.GetType(mainClass); + if (mainType == null) + { + throw new ScriptCompilationException( + "failed to compile script: unable to find main type"); + } + + var entry = mainType.GetMethod("ScriptMain"); + // check for task or function definitions. + if (entry == null) + { + throw new ScriptCompilationException( + "failed to compile script: ScriptMain() not defined"); + } + + if (!entry.IsStatic) + { + throw new ScriptCompilationException( + "failed to compile script: ScriptMain() not defined as static"); + } + + var entryParams = entry.GetParameters(); + if (entryParams.Length != 1) + { + throw new ScriptCompilationException( + "failed to compile script: ScriptMain() should have only one parameter"); + } + + if (entryParams[0].ParameterType.FullName != typeof(ScriptArgs).FullName) + { + throw new ScriptCompilationException( + string.Format("failed to compile script: ScriptMain() takes one member of type {0}, should have only one parameter of type {1}", + entryParams[0].ParameterType.FullName, typeof (ScriptArgs).FullName)); + } + + if (entry.ReturnType.FullName != typeof(Object).FullName) + { + throw new ScriptCompilationException( + string.Format("failed to compile script: ScriptMain() must return value of type {0}", + typeof(Object).FullName)); + } + + return entry; + } + + /// + /// Creates the compiler INFO. + /// + /// The language. + /// + private static CompilerInfo CreateCompilerInfo(string language) + { + return new CompilerInfo(CreateCodeProvider(language)); + } + + private static CodeDomProvider CreateCodeProvider(string language) + { + CodeDomProvider provider; + + switch (language.ToLower()) + { + case "vb": + case "visualbasic": + provider = CreateCodeDomProvider( + "Microsoft.VisualBasic.VBCodeProvider", + "System, Culture=neutral"); + break; + case "c#": + case "csharp": + provider = CreateCodeDomProvider( + "Microsoft.CSharp.CSharpCodeProvider", + "System, Culture=neutral"); + break; + case "js": + case "jscript": + provider = CreateCodeDomProvider( + "Microsoft.JScript.JScriptCodeProvider", + "Microsoft.JScript, Culture=neutral"); + break; + case "vjs": + case "jsharp": + provider = CreateCodeDomProvider( + "Microsoft.VJSharp.VJSharpCodeProvider", + "VJSharpCodeProvider, Culture=neutral"); + break; + default: + // if its not one of the above then it must be a fully + // qualified provider class name + provider = CreateCodeDomProvider(language); + break; + } + return provider; + } + + private static CodeDomProvider CreateCodeDomProvider(string typeName, string assemblyName) + { + Assembly providerAssembly = Assembly.LoadWithPartialName(assemblyName); + if (providerAssembly == null) + { + throw new ArgumentException("unable to find assembly: " + assemblyName); + } + + Type providerType = providerAssembly.GetType(typeName, true, true); + return CreateCodeDomProvider(providerType); + } + + private static CodeDomProvider CreateCodeDomProvider(string assemblyQualifiedTypeName) + { + Type providerType = Type.GetType(assemblyQualifiedTypeName, true, true); + return CreateCodeDomProvider(providerType); + } + + private static CodeDomProvider CreateCodeDomProvider(Type providerType) + { + object provider = Activator.CreateInstance(providerType); + if (!(provider is CodeDomProvider)) + { + throw new ArgumentException("invalid provider type"); + } + return (CodeDomProvider) provider; + } + + internal class CompilerInfo + { + internal readonly CodeDomProvider Provider; + + internal CompilerInfo(CodeDomProvider provider) + { + Provider = provider; + } + + internal CodeCompileUnit GenerateCode(string typeName, ExpressionScriptProvided script, StringCollection imports) + { + var compileUnit = new CodeCompileUnit(); + + var typeDecl = new CodeTypeDeclaration(typeName); + typeDecl.IsClass = true; + typeDecl.TypeAttributes = TypeAttributes.Public; + typeDecl.BaseTypes.Add(typeof(ScriptBase)); + + var scriptMainMember = new CodeMemberMethod(); + scriptMainMember.Attributes = MemberAttributes.Public | MemberAttributes.Static; + scriptMainMember.Name = "ScriptMain"; + scriptMainMember.Parameters.Add( + new CodeParameterDeclarationExpression(typeof (ScriptArgs), "args")); + scriptMainMember.ReturnType = new CodeTypeReference(typeof (object)); + + foreach (var parameter in script.ParameterNames) + { + var variableDeclarationStatement = new CodeVariableDeclarationStatement(); + variableDeclarationStatement.Type = new CodeTypeReference(typeof (object)); + variableDeclarationStatement.Name = parameter; + variableDeclarationStatement.InitExpression = new CodeMethodInvokeExpression( + new CodeVariableReferenceExpression("args"), + "GetParameter", + new CodePrimitiveExpression(parameter) + ); + + scriptMainMember.Statements.Add(variableDeclarationStatement); + } + + scriptMainMember.Statements.Add( + new CodeVariableDeclarationStatement( + typeof (object), + "__rvalue", + new CodeSnippetExpression(script.Expression) + ) + ); + scriptMainMember.Statements.Add( + new CodeMethodReturnStatement( + new CodeVariableReferenceExpression("__rvalue") + ) + ); + + typeDecl.Members.Add(scriptMainMember); + + var nspace = new CodeNamespace(); + // add the default namespaces + foreach (string import in DefaultNamespaces) + { + nspace.Imports.Add(new CodeNamespaceImport(import)); + } + // add requested imports + foreach (string import in imports) + { + nspace.Imports.Add(new CodeNamespaceImport(import)); + } + + compileUnit.Namespaces.Add(nspace); + nspace.Types.Add(typeDecl); + + return compileUnit; + } + } + + public static void VerifyCompilerScript(ExpressionScriptProvided script, string dialect) + { + try + { + CreateCodeProvider(dialect); + } + catch(TypeLoadException e) + { + throw new ExprValidationException("Failed to obtain script engine for dialect '" + dialect + + "' for script '" + script.Name + "'"); + } + } + } +#endif +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/AnnotationDesc.cs b/NEsper.Core/NEsper.Core/epl/spec/AnnotationDesc.cs new file mode 100755 index 000000000..01427ced3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/AnnotationDesc.cs @@ -0,0 +1,60 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.spec +{ + /// + /// Describes an annotation. + /// + [Serializable] + public class AnnotationDesc : MetaDefItem + { + // Map of Identifier and value={constant, array of value (Object[]), AnnotationDesc} (exclusive with value) + + /// + /// Ctor. + /// + /// name of annotation + /// are the attribute values + public AnnotationDesc(String name, IList> attributes) + { + Name = name; + Attributes = attributes; + } + + /// + /// Initializes a new instance of the class. + /// + /// The name. + /// The value. + public AnnotationDesc(String name, String value) + : this(name, Collections.SingletonList>(new Pair("Value", value))) + { + } + + /// + /// Returns annotation interface class name. + /// + /// + /// name of class, can be fully qualified + /// + public string Name { get; private set; } + + /// + /// Returns annotation attributes. + /// + public IList> Attributes { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/ColumnDesc.cs b/NEsper.Core/NEsper.Core/epl/spec/ColumnDesc.cs new file mode 100755 index 000000000..c0f6fbcf7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/ColumnDesc.cs @@ -0,0 +1,56 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.spec +{ + /// + /// Describes a column name and type. + /// + [Serializable] + public class ColumnDesc : MetaDefItem + { + /// + /// Ctor. + /// + /// column name + /// type + /// true for array + /// if set to true [is primitive array]. + public ColumnDesc(string name, string type, bool array, bool isPrimitiveArray) + { + Name = name; + Type = type; + IsArray = array; + IsPrimitiveArray = isPrimitiveArray; + } + + /// Returns column name. + /// name + public string Name { get; private set; } + + /// Return column type + /// type + public string Type { get; private set; } + + /// Return true for array + /// array indicator + public bool IsArray { get; private set; } + + /// + /// Gets a value indicating whether this instance is primitive array. + /// + /// + /// true if this instance is primitive array; otherwise, false. + /// + public bool IsPrimitiveArray { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/ContextDetail.cs b/NEsper.Core/NEsper.Core/epl/spec/ContextDetail.cs new file mode 100755 index 000000000..c4d23ee68 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/ContextDetail.cs @@ -0,0 +1,19 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.filter; + +namespace com.espertech.esper.epl.spec +{ + public interface ContextDetail + { + IList ContextDetailFilterSpecs { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/ContextDetailCategory.cs b/NEsper.Core/NEsper.Core/epl/spec/ContextDetailCategory.cs new file mode 100755 index 000000000..2044ece27 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/ContextDetailCategory.cs @@ -0,0 +1,66 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.filter; + +namespace com.espertech.esper.epl.spec +{ + [Serializable] + public class ContextDetailCategory : ContextDetail + { + private readonly IList _items; + private readonly FilterSpecRaw _filterSpecRaw; + + [NonSerialized] private FilterSpecCompiled _filterSpecCompiled; + [NonSerialized] private FilterValueSetParam[][] _filterParamsCompiled; + + public ContextDetailCategory(IList items, FilterSpecRaw filterSpecRaw) + { + _items = items; + _filterSpecRaw = filterSpecRaw; + } + + public IList ContextDetailFilterSpecs + { + get + { + IList filters = new List(1); + filters.Add(_filterSpecCompiled); + return filters; + } + } + + public FilterSpecRaw FilterSpecRaw + { + get { return _filterSpecRaw; } + } + + public IList Items + { + get { return _items; } + } + + public FilterSpecCompiled FilterSpecCompiled + { + get { return _filterSpecCompiled; } + set + { + _filterSpecCompiled = value; + _filterParamsCompiled = _filterSpecCompiled.GetValueSet(null, null, null).Parameters; + } + } + + public FilterValueSetParam[][] FilterParamsCompiled + { + get { return _filterParamsCompiled; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/ContextDetailCategoryItem.cs b/NEsper.Core/NEsper.Core/epl/spec/ContextDetailCategoryItem.cs new file mode 100755 index 000000000..a28458726 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/ContextDetailCategoryItem.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.filter; + +namespace com.espertech.esper.epl.spec +{ + [Serializable] + public class ContextDetailCategoryItem + { + public ContextDetailCategoryItem(ExprNode expression, String name) + { + Expression = expression; + Name = name; + } + + public ExprNode Expression { get; private set; } + + public string Name { get; private set; } + + public FilterValueSetParam[][] CompiledFilterParam { get; private set; } + + public void SetCompiledFilter(FilterSpecCompiled filterSpec, AgentInstanceContext agentInstanceContext) + { + CompiledFilterParam = filterSpec.GetValueSet(null, agentInstanceContext, null).Parameters; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/ContextDetailCondition.cs b/NEsper.Core/NEsper.Core/epl/spec/ContextDetailCondition.cs new file mode 100755 index 000000000..3a66d0ace --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/ContextDetailCondition.cs @@ -0,0 +1,19 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.filter; + +namespace com.espertech.esper.epl.spec +{ + public interface ContextDetailCondition + { + IList FilterSpecIfAny { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/ContextDetailConditionCrontab.cs b/NEsper.Core/NEsper.Core/epl/spec/ContextDetailConditionCrontab.cs new file mode 100755 index 000000000..4d4ce2293 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/ContextDetailConditionCrontab.cs @@ -0,0 +1,39 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.filter; +using com.espertech.esper.schedule; + +namespace com.espertech.esper.epl.spec +{ + public class ContextDetailConditionCrontab : ContextDetailCondition + { + public ContextDetailConditionCrontab(IList crontab, bool immediate) + { + ScheduleCallbackId = -1; + Crontab = crontab; + IsImmediate = immediate; + } + + public IList Crontab { get; private set; } + + public ScheduleSpec Schedule { get; set; } + + public IList FilterSpecIfAny + { + get { return null; } + } + + public bool IsImmediate { get; private set; } + + public int ScheduleCallbackId { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/ContextDetailConditionFilter.cs b/NEsper.Core/NEsper.Core/epl/spec/ContextDetailConditionFilter.cs new file mode 100755 index 000000000..cf5558e9d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/ContextDetailConditionFilter.cs @@ -0,0 +1,49 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.filter; + + +namespace com.espertech.esper.epl.spec +{ + [Serializable] + public class ContextDetailConditionFilter : ContextDetailCondition + { + [NonSerialized] + private FilterSpecCompiled _filterSpecCompiled; + + public ContextDetailConditionFilter(FilterSpecRaw filterSpecRaw, String optionalFilterAsName) + { + FilterSpecRaw = filterSpecRaw; + OptionalFilterAsName = optionalFilterAsName; + } + + public FilterSpecRaw FilterSpecRaw { get; private set; } + + public string OptionalFilterAsName { get; private set; } + + public FilterSpecCompiled FilterSpecCompiled + { + get { return _filterSpecCompiled; } + set { _filterSpecCompiled = value; } + } + + public IList FilterSpecIfAny + { + get + { + var list = new List(1); + list.Add(_filterSpecCompiled); + return list; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/ContextDetailConditionImmediate.cs b/NEsper.Core/NEsper.Core/epl/spec/ContextDetailConditionImmediate.cs new file mode 100755 index 000000000..0cd8f9800 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/ContextDetailConditionImmediate.cs @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.filter; + +namespace com.espertech.esper.epl.spec +{ + public class ContextDetailConditionImmediate : ContextDetailCondition + { + public static readonly ContextDetailConditionImmediate INSTANCE = new ContextDetailConditionImmediate(); + + private ContextDetailConditionImmediate() + { + } + + public IList FilterSpecIfAny + { + get { return Collections.GetEmptyList(); } + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/spec/ContextDetailConditionNever.cs b/NEsper.Core/NEsper.Core/epl/spec/ContextDetailConditionNever.cs new file mode 100755 index 000000000..36d5377d7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/ContextDetailConditionNever.cs @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.filter; + +namespace com.espertech.esper.epl.spec +{ + public class ContextDetailConditionNever : ContextDetailCondition + { + public static readonly ContextDetailConditionNever INSTANCE = new ContextDetailConditionNever(); + + private ContextDetailConditionNever() + { + } + + public IList FilterSpecIfAny + { + get { return Collections.GetEmptyList(); } + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/spec/ContextDetailConditionPattern.cs b/NEsper.Core/NEsper.Core/epl/spec/ContextDetailConditionPattern.cs new file mode 100755 index 000000000..f138fba59 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/ContextDetailConditionPattern.cs @@ -0,0 +1,74 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.filter; +using com.espertech.esper.pattern; + +namespace com.espertech.esper.epl.spec +{ + [Serializable] + public class ContextDetailConditionPattern : ContextDetailCondition + { + private readonly bool _inclusive; + private readonly bool _immediate; + + [NonSerialized] + private PatternStreamSpecCompiled _patternCompiled; + + private readonly EvalFactoryNode _patternRaw; + + public ContextDetailConditionPattern(EvalFactoryNode patternRaw, bool inclusive, bool immediate) + { + _patternRaw = patternRaw; + _inclusive = inclusive; + _immediate = immediate; + } + + public EvalFactoryNode PatternRaw + { + get { return _patternRaw; } + } + + public PatternStreamSpecCompiled PatternCompiled + { + get { return _patternCompiled; } + set { _patternCompiled = value; } + } + + public bool IsInclusive + { + get { return _inclusive; } + } + + public bool IsImmediate + { + get { return _immediate; } + } + + public IList FilterSpecIfAny + { + get + { + var filters = new List(); + var evalNodeAnalysisResult = EvalNodeUtil.RecursiveAnalyzeChildNodes(_patternCompiled.EvalFactoryNode); + var filterNodes = evalNodeAnalysisResult.FilterNodes; + + foreach (EvalFilterFactoryNode filterNode in filterNodes) + { + filters.Add(filterNode.FilterSpec); + } + + return filters.IsEmpty() ? null : filters; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/ContextDetailConditionTimePeriod.cs b/NEsper.Core/NEsper.Core/epl/spec/ContextDetailConditionTimePeriod.cs new file mode 100755 index 000000000..08b065bf1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/ContextDetailConditionTimePeriod.cs @@ -0,0 +1,46 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.expression.time; +using com.espertech.esper.filter; + +namespace com.espertech.esper.epl.spec +{ + [Serializable] + public class ContextDetailConditionTimePeriod : ContextDetailCondition + { + public ContextDetailConditionTimePeriod(ExprTimePeriod timePeriod, bool immediate) + { + ScheduleCallbackId = -1; + TimePeriod = timePeriod; + IsImmediate = immediate; + } + + public ExprTimePeriod TimePeriod { get; set; } + + public IList FilterSpecIfAny + { + get { return null; } + } + + public bool IsImmediate { get; private set; } + + public int ScheduleCallbackId { get; set; } + + public long GetExpectedEndTime(AgentInstanceContext agentInstanceContext) + { + var current = agentInstanceContext.StatementContext.TimeProvider.Time; + var msec = TimePeriod.NonconstEvaluator().DeltaAdd(current, null, true, agentInstanceContext); + return current + msec; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/ContextDetailHash.cs b/NEsper.Core/NEsper.Core/epl/spec/ContextDetailHash.cs new file mode 100755 index 000000000..da3db0b27 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/ContextDetailHash.cs @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.filter; + +namespace com.espertech.esper.epl.spec +{ + [Serializable] + public class ContextDetailHash : ContextDetail + { + public ContextDetailHash(IList items, int granularity, bool preallocate) + { + Items = items; + IsPreallocate = preallocate; + Granularity = granularity; + } + + public IList Items { get; private set; } + + public bool IsPreallocate { get; private set; } + + public int Granularity { get; private set; } + + public IList ContextDetailFilterSpecs + { + get { return Items.Select(item => item.FilterSpecCompiled).ToList(); } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/spec/ContextDetailHashItem.cs b/NEsper.Core/NEsper.Core/epl/spec/ContextDetailHashItem.cs new file mode 100755 index 000000000..1ae799b0f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/ContextDetailHashItem.cs @@ -0,0 +1,63 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.filter; + +namespace com.espertech.esper.epl.spec +{ + [Serializable] + public class ContextDetailHashItem + { + private readonly FilterSpecRaw _filterSpecRaw; + private readonly ExprChainedSpec _function; + + [NonSerialized] private FilterSpecCompiled _filterSpecCompiled; + private FilterSpecLookupable _lookupable; + [NonSerialized] private FilterValueSetParam[][] _parametersCompiled; + + public ContextDetailHashItem(ExprChainedSpec function, FilterSpecRaw filterSpecRaw) + { + _function = function; + _filterSpecRaw = filterSpecRaw; + } + + public ExprChainedSpec Function + { + get { return _function; } + } + + public FilterSpecRaw FilterSpecRaw + { + get { return _filterSpecRaw; } + } + + public FilterSpecCompiled FilterSpecCompiled + { + get { return _filterSpecCompiled; } + set + { + _filterSpecCompiled = value; + _parametersCompiled = value.GetValueSet(null, null, null).Parameters; + } + } + + public FilterValueSetParam[][] ParametersCompiled + { + get { return _parametersCompiled; } + } + + public FilterSpecLookupable Lookupable + { + get { return _lookupable; } + set { _lookupable = value; } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/spec/ContextDetailInitiatedTerminated.cs b/NEsper.Core/NEsper.Core/epl/spec/ContextDetailInitiatedTerminated.cs new file mode 100755 index 000000000..de223c144 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/ContextDetailInitiatedTerminated.cs @@ -0,0 +1,79 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.filter; + +namespace com.espertech.esper.epl.spec +{ + [Serializable] + public class ContextDetailInitiatedTerminated : ContextDetail + { + private ContextDetailCondition _start; + private ContextDetailCondition _end; + private readonly bool _overlapping; + private readonly ExprNode[] _distinctExpressions; + + public ContextDetailInitiatedTerminated(ContextDetailCondition start, ContextDetailCondition end, bool overlapping, ExprNode[] distinctExpressions) + { + _start = start; + _end = end; + _overlapping = overlapping; + _distinctExpressions = distinctExpressions; + } + + public ContextDetailCondition Start + { + get { return _start; } + set { _start = value; } + } + + public ContextDetailCondition End + { + get { return _end; } + set { _end = value; } + } + + public bool IsOverlapping + { + get { return _overlapping; } + } + + public IList ContextDetailFilterSpecs + { + get + { + IList startFS = _start.FilterSpecIfAny; + IList endFS = _end.FilterSpecIfAny; + if (startFS == null && endFS == null) + { + return Collections.GetEmptyList(); + } + IList filters = new List(2); + if (startFS != null) + { + filters.AddAll(startFS); + } + if (endFS != null) + { + filters.AddAll(endFS); + } + return filters; + } + } + + public ExprNode[] DistinctExpressions + { + get { return _distinctExpressions; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/ContextDetailNested.cs b/NEsper.Core/NEsper.Core/epl/spec/ContextDetailNested.cs new file mode 100755 index 000000000..43ae90ccd --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/ContextDetailNested.cs @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.filter; + +namespace com.espertech.esper.epl.spec +{ + public class ContextDetailNested : ContextDetail + { + public ContextDetailNested(IList contexts) + { + Contexts = contexts; + } + + public IList Contexts { get; private set; } + + public IList ContextDetailFilterSpecs + { + get + { + var filterSpecs = new List(); + foreach (var desc in Contexts) + { + filterSpecs.AddAll(desc.ContextDetail.ContextDetailFilterSpecs); + } + return filterSpecs; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/ContextDetailPartitionItem.cs b/NEsper.Core/NEsper.Core/epl/spec/ContextDetailPartitionItem.cs new file mode 100755 index 000000000..c0ca1ae0a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/ContextDetailPartitionItem.cs @@ -0,0 +1,56 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.filter; + +namespace com.espertech.esper.epl.spec +{ + [Serializable] + public class ContextDetailPartitionItem + { + private readonly FilterSpecRaw _filterSpecRaw; + private readonly IList _propertyNames; + + [NonSerialized] private FilterSpecCompiled _filterSpecCompiled; + [NonSerialized] private FilterValueSetParam[][] _parametersCompiled; + + public ContextDetailPartitionItem(FilterSpecRaw filterSpecRaw, IList propertyNames) + { + _filterSpecRaw = filterSpecRaw; + _propertyNames = propertyNames; + } + + public FilterSpecRaw FilterSpecRaw + { + get { return _filterSpecRaw; } + } + + public IList PropertyNames + { + get { return _propertyNames; } + } + + public FilterSpecCompiled FilterSpecCompiled + { + get { return _filterSpecCompiled; } + set + { + _filterSpecCompiled = value; + _parametersCompiled = value.GetValueSet(null, null, null).Parameters; + } + } + + public FilterValueSetParam[][] ParametersCompiled + { + get { return _parametersCompiled; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/ContextDetailPartitioned.cs b/NEsper.Core/NEsper.Core/epl/spec/ContextDetailPartitioned.cs new file mode 100755 index 000000000..95b28d376 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/ContextDetailPartitioned.cs @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.filter; + +namespace com.espertech.esper.epl.spec +{ + [Serializable] + public class ContextDetailPartitioned : ContextDetail + { + public IList Items { get; private set; } + public ContextDetailPartitioned(IList items) + { + Items = items; + } + + public IList ContextDetailFilterSpecs + { + get + { + return Items.Select(item => item.FilterSpecCompiled).ToList(); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/CreateContextDesc.cs b/NEsper.Core/NEsper.Core/epl/spec/CreateContextDesc.cs new file mode 100755 index 000000000..0e20e7b6e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/CreateContextDesc.cs @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.filter; + +namespace com.espertech.esper.epl.spec +{ + [Serializable] + public class CreateContextDesc + { + public CreateContextDesc(String contextName, ContextDetail contextDetail) { + ContextName = contextName; + ContextDetail = contextDetail; + } + + public string ContextName { get; private set; } + public ContextDetail ContextDetail { get; private set; } + + public IList FilterSpecs + { + get { return ContextDetail.ContextDetailFilterSpecs; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/CreateDataFlowDesc.cs b/NEsper.Core/NEsper.Core/epl/spec/CreateDataFlowDesc.cs new file mode 100755 index 000000000..715a161ac --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/CreateDataFlowDesc.cs @@ -0,0 +1,30 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.epl.spec +{ + [Serializable] + public class CreateDataFlowDesc + { + public CreateDataFlowDesc(String graphName, IList operators, IList schemas) + { + GraphName = graphName; + Operators = operators; + Schemas = schemas; + } + + public string GraphName { get; private set; } + + public IList Operators { get; private set; } + + public IList Schemas { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/spec/CreateExpressionDesc.cs b/NEsper.Core/NEsper.Core/epl/spec/CreateExpressionDesc.cs new file mode 100755 index 000000000..e4617b1cf --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/CreateExpressionDesc.cs @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.collection; + +namespace com.espertech.esper.epl.spec +{ + [Serializable] + public class CreateExpressionDesc + { + public CreateExpressionDesc(ExpressionDeclItem expression) + { + Expression = expression; + Script = null; + } + + public CreateExpressionDesc(ExpressionScriptProvided script) + { + Script = script; + Expression = null; + } + + public CreateExpressionDesc(Pair pair) + { + Script = pair.Second; + Expression = pair.First; + } + + public ExpressionDeclItem Expression { get; private set; } + + public ExpressionScriptProvided Script { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/spec/CreateIndexDesc.cs b/NEsper.Core/NEsper.Core/epl/spec/CreateIndexDesc.cs new file mode 100755 index 000000000..ab4af7b43 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/CreateIndexDesc.cs @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.spec +{ + /// Specification for creating a named window. + [Serializable] + public class CreateIndexDesc : MetaDefItem + { + /// + /// Ctor. + /// + /// indicator whether unique or not + /// index name + /// window name + /// properties to index + public CreateIndexDesc(bool isUnique, String indexName, String windowName, IList columns) + { + IsUnique = isUnique; + IndexName = indexName; + WindowName = windowName; + Columns = columns; + } + + /// Returns index name. + /// index name + public string IndexName { get; private set; } + + /// Returns window name. + /// window name + public string WindowName { get; private set; } + + /// Returns columns. + /// columns + public IList Columns { get; private set; } + + /// + /// Gets or sets a value indicating whether this instance is unique. + /// + /// true if this instance is unique; otherwise, false. + public bool IsUnique { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/CreateIndexItem.cs b/NEsper.Core/NEsper.Core/epl/spec/CreateIndexItem.cs new file mode 100755 index 000000000..fe09bc0c2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/CreateIndexItem.cs @@ -0,0 +1,31 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.spec +{ + /// + /// Specification for creating a named window index column. + /// + [Serializable] + public class CreateIndexItem : MetaDefItem + { + public CreateIndexItem(String name, CreateIndexType type) + { + Name = name; + Type = type; + } + + public string Name { get; private set; } + + public CreateIndexType Type { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/CreateIndexType.cs b/NEsper.Core/NEsper.Core/epl/spec/CreateIndexType.cs new file mode 100755 index 000000000..ece9d19d8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/CreateIndexType.cs @@ -0,0 +1,19 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.spec +{ + /// + /// Specification for creating a named window index column type. + /// + public enum CreateIndexType + { + BTREE, + HASH + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/CreateSchemaDesc.cs b/NEsper.Core/NEsper.Core/epl/spec/CreateSchemaDesc.cs new file mode 100755 index 000000000..26f5bc3f1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/CreateSchemaDesc.cs @@ -0,0 +1,150 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.client.soda; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.spec +{ + /// + /// Specification for creating an event type/schema. + /// + [Serializable] + public class CreateSchemaDesc : MetaDefItem + { + /// + /// Ctor. + /// + /// name + /// event type Name(s) + /// column definition + /// supertypes + /// any type assignment such as Map, Object-array or variant or none-specified + /// name of start-interval prop + /// name of end-interval prop + /// copy-from + public CreateSchemaDesc( + string schemaName, + ICollection types, + IList columns, + ICollection inherits, + AssignedType assignedType, + string startTimestampProperty, + string endTimestampProperty, + ICollection copyFrom) + { + SchemaName = schemaName; + Types = types; + Columns = columns; + Inherits = inherits; + AssignedType = assignedType; + StartTimestampProperty = startTimestampProperty; + EndTimestampProperty = endTimestampProperty; + CopyFrom = copyFrom; + } + + /// + /// Returns schema name. + /// + /// schema name + public string SchemaName { get; private set; } + + /// + /// Returns column definitions. + /// + /// column defs + public IList Columns { get; private set; } + + /// + /// Returns supertypes. + /// + /// supertypes + public ICollection Inherits { get; private set; } + + /// + /// Returns type Name(s). + /// + /// types + public ICollection Types { get; private set; } + + public AssignedType AssignedType { get; private set; } + + public string StartTimestampProperty { get; private set; } + + public string EndTimestampProperty { get; private set; } + + public ICollection CopyFrom { get; private set; } + } + + public enum AssignedType + { + VARIANT, + MAP, + OBJECTARRAY, + AVRO, + NONE + } + + public static class AssignedTypeExtensions + { + public static AssignedType ParseKeyword(string keywordNodeText) + { + switch (keywordNodeText.ToLowerInvariant()) + { + case "variant": + return AssignedType.VARIANT; + case "map": + return AssignedType.MAP; + case "objectarray": + return AssignedType.OBJECTARRAY; + case "avro": + return AssignedType.AVRO; + } + + throw new EPException("Expected 'variant', 'map', 'objectarray' or 'avro' keyword after create-schema clause but encountered '" + keywordNodeText + "'"); + } + + public static AssignedType MapFrom(CreateSchemaClauseTypeDef typeDefinition) + { + switch (typeDefinition) + { + case CreateSchemaClauseTypeDef.NONE: + return AssignedType.NONE; + case CreateSchemaClauseTypeDef.MAP: + return AssignedType.MAP; + case CreateSchemaClauseTypeDef.OBJECTARRAY: + return AssignedType.OBJECTARRAY; + case CreateSchemaClauseTypeDef.AVRO: + return AssignedType.AVRO; + } + + return AssignedType.VARIANT; + } + + public static CreateSchemaClauseTypeDef MapToSoda(this AssignedType value) + { + switch (value) + { + case AssignedType.VARIANT: + return CreateSchemaClauseTypeDef.VARIANT; + case AssignedType.MAP: + return CreateSchemaClauseTypeDef.MAP; + case AssignedType.OBJECTARRAY: + return CreateSchemaClauseTypeDef.OBJECTARRAY; + case AssignedType.AVRO: + return CreateSchemaClauseTypeDef.AVRO; + default: + return CreateSchemaClauseTypeDef.NONE; + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/spec/CreateTableColumn.cs b/NEsper.Core/NEsper.Core/epl/spec/CreateTableColumn.cs new file mode 100755 index 000000000..2af4f3a90 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/CreateTableColumn.cs @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.spec +{ + [Serializable] + public class CreateTableColumn : MetaDefItem + { + public CreateTableColumn(string columnName, ExprNode optExpression, string optTypeName, bool? optTypeIsArray, bool? optTypeIsPrimitiveArray, IList annotations, bool? primaryKey) + { + ColumnName = columnName; + OptExpression = optExpression; + OptTypeName = optTypeName; + OptTypeIsArray = optTypeIsArray; + OptTypeIsPrimitiveArray = optTypeIsPrimitiveArray; + Annotations = annotations; + PrimaryKey = primaryKey; + } + + public string ColumnName { get; private set; } + + public ExprNode OptExpression { get; private set; } + + public string OptTypeName { get; private set; } + + public bool? OptTypeIsArray { get; private set; } + + public IList Annotations { get; private set; } + + public bool? PrimaryKey { get; private set; } + + public bool? OptTypeIsPrimitiveArray { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/CreateTableDesc.cs b/NEsper.Core/NEsper.Core/epl/spec/CreateTableDesc.cs new file mode 100755 index 000000000..75d82360a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/CreateTableDesc.cs @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.spec +{ + /// + /// Descriptor for create-table statements. + /// + [Serializable] + public class CreateTableDesc : MetaDefItem + { + public CreateTableDesc(string tableName, IList columns) + { + TableName = tableName; + Columns = columns; + } + + public string TableName { get; private set; } + + public IList Columns { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/spec/CreateVariableDesc.cs b/NEsper.Core/NEsper.Core/epl/spec/CreateVariableDesc.cs new file mode 100755 index 000000000..4a496b7e6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/CreateVariableDesc.cs @@ -0,0 +1,72 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.spec +{ + /// Descriptor for create-variable statements. + [Serializable] + public class CreateVariableDesc + : MetaDefItem + { + /// + /// Ctor. + /// + /// type of the variable + /// name of the variable + /// expression assigning the initial value, or null if none + /// if set to true [constant]. + /// if set to true [is array]. + /// + public CreateVariableDesc(string variableType, string variableName, ExprNode assignment, bool constant, bool isArray, bool isArrayOfPrimitive) + { + VariableType = variableType; + VariableName = variableName; + Assignment = assignment; + IsConstant = constant; + IsArray = isArray; + IsArrayOfPrimitive = isArrayOfPrimitive; + } + + /// Returns the variable type. + /// type of variable + public string VariableType { get; private set; } + + /// Returns the variable name + /// name + public string VariableName { get; private set; } + + /// Returns the assignment expression, or null if none + /// expression or null + public ExprNode Assignment { get; private set; } + + /// + /// Gets or sets a value indicating whether this is constant. + /// + /// true if constant; otherwise, false. + public bool IsConstant { get; private set; } + + /// + /// Gets or sets a value indicating whether this instance is array. + /// + /// true if this instance is array; otherwise, false. + public bool IsArray { get; private set; } + + /// + /// Gets a value indicating whether this instance is array of primitive. + /// + /// + /// true if this instance is array of primitive; otherwise, false. + /// + public bool IsArrayOfPrimitive { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/spec/CreateWindowDesc.cs b/NEsper.Core/NEsper.Core/epl/spec/CreateWindowDesc.cs new file mode 100755 index 000000000..dc58579c2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/CreateWindowDesc.cs @@ -0,0 +1,81 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.spec +{ + /// + /// Specification for creating a named window. + /// + [Serializable] + public class CreateWindowDesc : MetaDefItem + { + /// + /// Ctor. + /// + /// the window name + /// the view definitions + /// options such as retain-union etc + /// true for insert-INFO + /// optional filter expression + /// list of columns, if using column syntax + /// Name of as event type. + public CreateWindowDesc( + String windowName, + IList viewSpecs, + StreamSpecOptions streamSpecOptions, + bool insert, + ExprNode insertFilter, + IList columns, + String asEventTypeName) + { + WindowName = windowName; + ViewSpecs = viewSpecs; + IsInsert = insert; + InsertFilter = insertFilter; + StreamSpecOptions = streamSpecOptions; + Columns = columns; + AsEventTypeName = asEventTypeName; + } + + /// Returns the window name. + /// window name + public string WindowName { get; private set; } + + /// Returns the view specifications. + /// view specs + public IList ViewSpecs { get; private set; } + + /// Returns true for insert-from. + /// indicator to insert from another named window + public bool IsInsert { get; private set; } + + /// Returns the expression to filter insert-from events, or null if none supplied. + /// insert filter expression + public ExprNode InsertFilter { get; set; } + + /// Returns the window name to insert from. + /// window name to insert from + public string InsertFromWindow { get; set; } + + /// Returns the options for the stream such as unidirectional, retain-union etc. + /// stream options + public StreamSpecOptions StreamSpecOptions { get; private set; } + + /// Returns column names and types. + /// column descriptors + public IList Columns { get; private set; } + + public string AsEventTypeName { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/spec/DBStatementStreamSpec.cs b/NEsper.Core/NEsper.Core/epl/spec/DBStatementStreamSpec.cs new file mode 100755 index 000000000..951438f4d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/DBStatementStreamSpec.cs @@ -0,0 +1,62 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.core.service; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.spec +{ + /// + /// Specification object for historical data poll via database SQL statement. + /// + [Serializable] + public class DBStatementStreamSpec : StreamSpecBase, StreamSpecRaw, StreamSpecCompiled, MetaDefItem + { + /// Ctor. + /// is a stream name optionally given to stream + /// is a list of views onto the stream + /// is the database name to poll + /// is the SQL with placeholder parameters + /// is the sample SQL to retrieve statement metadata, if any was supplied + public DBStatementStreamSpec(String optionalStreamName, ViewSpec[] viewSpecs, String databaseName, String sqlWithSubsParams, String metadataSQL) + : base(optionalStreamName, viewSpecs, StreamSpecOptions.DEFAULT) + { + DatabaseName = databaseName; + SqlWithSubsParams = sqlWithSubsParams; + MetadataSQL = metadataSQL; + } + + /// Returns the database name. + /// name of database. + public string DatabaseName { get; private set; } + + /// Returns the SQL with substitution parameters. + /// SQL with parameters embedded as ${stream.param} + public string SqlWithSubsParams { get; private set; } + + /// Returns the optional sample metadata SQL + /// null if not supplied, or SQL to fire to retrieve metadata + public string MetadataSQL { get; private set; } + + public StreamSpecCompiled Compile( + StatementContext statementContext, + ICollection eventTypeReferences, + bool isInsertInto, + ICollection assignedTypeNumberStack, + bool isJoin, + bool isContextDeclaration, + bool isOnTrigger, + string optionalStreamName) + { + return this; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/ExpressionDeclDesc.cs b/NEsper.Core/NEsper.Core/epl/spec/ExpressionDeclDesc.cs new file mode 100755 index 000000000..14b74a2aa --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/ExpressionDeclDesc.cs @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.epl.spec +{ + [Serializable] + public class ExpressionDeclDesc + { + public IList Expressions { get; set; } + + /// + /// Initializes a new instance of the class. + /// + public ExpressionDeclDesc() + { + Expressions = new List(); + } + + public void Add(ExpressionDeclItem declNode) + { + Expressions.Add(declNode); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/ExpressionDeclItem.cs b/NEsper.Core/NEsper.Core/epl/spec/ExpressionDeclItem.cs new file mode 100755 index 000000000..9d418182d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/ExpressionDeclItem.cs @@ -0,0 +1,35 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.spec +{ + [Serializable] + public class ExpressionDeclItem + { + public ExpressionDeclItem(string name, IList parametersNames, ExprNode inner, bool isAlias) + { + Name = name; + ParametersNames = parametersNames; + Inner = inner; + IsAlias = isAlias; + } + + public String Name { get; private set; } + + public ExprNode Inner { get; private set; } + + public IList ParametersNames { get; private set; } + + public bool IsAlias { get; set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/spec/ExpressionScriptCompiled.cs b/NEsper.Core/NEsper.Core/epl/spec/ExpressionScriptCompiled.cs new file mode 100755 index 000000000..372e33d87 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/ExpressionScriptCompiled.cs @@ -0,0 +1,17 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.spec +{ + public interface ExpressionScriptCompiled + { + Type KnownReturnType { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/ExpressionScriptProvided.cs b/NEsper.Core/NEsper.Core/epl/spec/ExpressionScriptProvided.cs new file mode 100755 index 000000000..b4da17ff2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/ExpressionScriptProvided.cs @@ -0,0 +1,61 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.epl.spec +{ + [Serializable] + public class ExpressionScriptProvided + { + [NonSerialized] private ExpressionScriptCompiled _compiled; + + public ExpressionScriptProvided( + string name, + string expression, + IList parameterNames, + string optionalReturnTypeName, + bool optionalReturnTypeIsArray, + string optionalEventTypeName, + string optionalDialect) + { + Name = name; + Expression = expression; + ParameterNames = parameterNames; + OptionalReturnTypeName = optionalReturnTypeName; + IsOptionalReturnTypeIsArray = optionalReturnTypeIsArray; + OptionalEventTypeName = optionalEventTypeName; + OptionalDialect = optionalDialect; + + if (expression == null) { + throw new ArgumentException("Invalid null expression received"); + } + } + + public string Name { get; private set; } + + public string Expression { get; private set; } + + public IList ParameterNames { get; private set; } + + public string OptionalReturnTypeName { get; private set; } + + public string OptionalDialect { get; private set; } + + public ExpressionScriptCompiled Compiled + { + get { return _compiled; } + set { _compiled = value; } + } + + public bool IsOptionalReturnTypeIsArray { get; private set; } + + public string OptionalEventTypeName { get; private set; } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/spec/FilterSpecRaw.cs b/NEsper.Core/NEsper.Core/epl/spec/FilterSpecRaw.cs new file mode 100755 index 000000000..cd114ecb7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/FilterSpecRaw.cs @@ -0,0 +1,84 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.spec +{ + /// + /// Filter definition in an un-validated and un-resolved form. + /// + /// Event type and expression nodes in this filter specification are not yet + /// validated, optimized for resolved against actual streams. + /// + [Serializable] + public class FilterSpecRaw : MetaDefItem + { + private readonly String _eventTypeName; + private readonly IList _filterExpressions; + private readonly PropertyEvalSpec _optionalPropertyEvalSpec; + + /// + /// Ctor. + /// + /// is the name of the event type + /// is a list of expression nodes representing individual filter expressions + /// specification for a property select + public FilterSpecRaw(String eventTypeName, IList filterExpressions, PropertyEvalSpec optionalPropertyEvalSpec) + { + this._eventTypeName = eventTypeName; + this._filterExpressions = filterExpressions; + this._optionalPropertyEvalSpec = optionalPropertyEvalSpec; + } + + /// + /// Default ctor. + /// + public FilterSpecRaw() + { + } + + /// + /// Returns the event type name of the events we are looking for. + /// + /// + /// event name + /// + public string EventTypeName + { + get { return _eventTypeName; } + } + + /// + /// Returns the list of filter expressions. + /// + /// + /// filter expression list + /// + public IList FilterExpressions + { + get { return _filterExpressions; } + } + + /// + /// Returns the property evaluation specification, if any, or null if no properties + /// evaluated. + /// + /// + /// property eval spec + /// + public PropertyEvalSpec OptionalPropertyEvalSpec + { + get { return _optionalPropertyEvalSpec; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/FilterStreamSpecCompiled.cs b/NEsper.Core/NEsper.Core/epl/spec/FilterStreamSpecCompiled.cs new file mode 100755 index 000000000..f5c2497b7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/FilterStreamSpecCompiled.cs @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.filter; + +namespace com.espertech.esper.epl.spec +{ + /// + /// Specification for building an event stream out of a filter for events (supplying + /// type and basic filter criteria) and views onto these events which are staggered onto + /// each other to supply a readonly stream of events. + /// + [Serializable] + public class FilterStreamSpecCompiled : StreamSpecBase, StreamSpecCompiled + { + /// Ctor. + /// specifies what events we are interested in. + /// specifies what view to use to derive data + /// stream name, or null if none supplied + /// additional options such as unidirectional stream in a join + public FilterStreamSpecCompiled(FilterSpecCompiled filterSpec, ViewSpec[] viewSpecs, String optionalStreamName, StreamSpecOptions streamSpecOptions) + : base(optionalStreamName, viewSpecs, streamSpecOptions) + { + FilterSpec = filterSpec; + } + + /// Returns filter specification for which events the stream will getSelectListEvents. + /// filter spec + public FilterSpecCompiled FilterSpec { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/FilterStreamSpecRaw.cs b/NEsper.Core/NEsper.Core/epl/spec/FilterStreamSpecRaw.cs new file mode 100755 index 000000000..11cc58c7e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/FilterStreamSpecRaw.cs @@ -0,0 +1,221 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.property; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.events; +using com.espertech.esper.filter; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.spec +{ + /// + /// Unvalided filter-based stream specification. + /// + [Serializable] + public class FilterStreamSpecRaw + : StreamSpecBase + , StreamSpecRaw + , MetaDefItem + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly FilterSpecRaw _rawFilterSpec; + + /// Ctor. + /// is unvalidated filter specification + /// is the view definition + /// is the stream name if supplied, or null if not supplied + /// additional options, such as unidirectional stream in a join + public FilterStreamSpecRaw(FilterSpecRaw rawFilterSpec, ViewSpec[] viewSpecs, String optionalStreamName, StreamSpecOptions streamSpecOptions) + : base(optionalStreamName, viewSpecs, streamSpecOptions) + { + _rawFilterSpec = rawFilterSpec; + } + + /// Default ctor. + public FilterStreamSpecRaw() + { + } + + /// Returns the unvalided filter spec. + /// filter def + public FilterSpecRaw RawFilterSpec + { + get { return _rawFilterSpec; } + } + + public StreamSpecCompiled Compile(StatementContext context, ICollection eventTypeReferences, bool isInsertInto, ICollection assignedTypeNumberStack, bool isJoin, bool isContextDeclaration, bool isOnTrigger, String optionalStreamName) + { + StreamTypeService streamTypeService; + + // Determine the event type + var eventName = _rawFilterSpec.EventTypeName; + + if (context.TableService != null && context.TableService.GetTableMetadata(eventName) != null) + { + if (ViewSpecs != null && ViewSpecs.Length > 0) + { + throw new ExprValidationException("Views are not supported with tables"); + } + if (RawFilterSpec.OptionalPropertyEvalSpec != null) + { + throw new ExprValidationException("Contained-event expressions are not supported with tables"); + } + var tableMetadata = context.TableService.GetTableMetadata(eventName); + var streamTypeServiceX = new StreamTypeServiceImpl(new EventType[] { tableMetadata.InternalEventType }, new String[] { optionalStreamName }, new bool[] { true }, context.EngineURI, false); + var validatedNodes = FilterSpecCompiler.ValidateAllowSubquery(ExprNodeOrigin.FILTER, _rawFilterSpec.FilterExpressions, streamTypeServiceX, context, null, null); + return new TableQueryStreamSpec(OptionalStreamName, ViewSpecs, Options, eventName, validatedNodes); + } + + // Could be a named window + if (context.NamedWindowMgmtService.IsNamedWindow(eventName)) + { + var namedWindowType = context.NamedWindowMgmtService.GetProcessor(eventName).TailView.EventType; + streamTypeService = new StreamTypeServiceImpl(new EventType[] { namedWindowType }, new String[] { optionalStreamName }, new bool[] { true }, context.EngineURI, false); + + var validatedNodes = FilterSpecCompiler.ValidateAllowSubquery( + ExprNodeOrigin.FILTER, _rawFilterSpec.FilterExpressions, streamTypeService, context, null, null); + + PropertyEvaluator optionalPropertyEvaluator = null; + if (_rawFilterSpec.OptionalPropertyEvalSpec != null) + { + optionalPropertyEvaluator = + PropertyEvaluatorFactory.MakeEvaluator( + _rawFilterSpec.OptionalPropertyEvalSpec, namedWindowType, OptionalStreamName, + context.EventAdapterService, + context.EngineImportService, + context.TimeProvider, + context.VariableService, + context.ScriptingService, + context.TableService, + context.EngineURI, + context.StatementId, + context.StatementName, + context.Annotations, assignedTypeNumberStack, + context.ConfigSnapshot, + context.NamedWindowMgmtService, + context.StatementExtensionServicesContext); + } + eventTypeReferences.Add(((EventTypeSPI)namedWindowType).Metadata.PrimaryName); + return new NamedWindowConsumerStreamSpec(eventName, OptionalStreamName, ViewSpecs, validatedNodes, Options, optionalPropertyEvaluator); + } + + EventType eventType = null; + + if (context.ValueAddEventService.IsRevisionTypeName(eventName)) + { + eventType = context.ValueAddEventService.GetValueAddUnderlyingType(eventName); + eventTypeReferences.Add(((EventTypeSPI)eventType).Metadata.PrimaryName); + } + + if (eventType == null) + { + eventType = ResolveType(context.EngineURI, eventName, context.EventAdapterService, context.PlugInTypeResolutionURIs); + if (eventType is EventTypeSPI) + { + eventTypeReferences.Add(((EventTypeSPI)eventType).Metadata.PrimaryName); + } + } + + // Validate all nodes, make sure each returns a bool and types are good; + // Also decompose all AND super nodes into individual expressions + streamTypeService = new StreamTypeServiceImpl(new EventType[] { eventType }, new String[] { base.OptionalStreamName }, new bool[] { true }, context.EngineURI, false); + + var spec = FilterSpecCompiler.MakeFilterSpec(eventType, eventName, _rawFilterSpec.FilterExpressions, _rawFilterSpec.OptionalPropertyEvalSpec, + null, null, // no tags + streamTypeService, OptionalStreamName, context, assignedTypeNumberStack); + + return new FilterStreamSpecCompiled(spec, ViewSpecs, OptionalStreamName, Options); + } + + /// Resolves a given event name to an event type. + /// is the name to resolve + /// for resolving event types + /// the provider URI + /// is URIs for resolving the event name against plug-inn event representations, if any + /// event type + /// ExprValidationException if the info cannot be resolved + public static EventType ResolveType(String engineURI, String eventName, EventAdapterService eventAdapterService, IList optionalResolutionURIs) + { + var eventType = eventAdapterService.GetEventTypeByName(eventName); + + // may already be known + if (eventType != null) + { + return eventType; + } + + var engineURIQualifier = engineURI; + if (engineURI == null || EPServiceProviderConstants.DEFAULT_ENGINE_URI.Equals(engineURI)) + { + engineURIQualifier = EPServiceProviderConstants.DEFAULT_ENGINE_URI_QUALIFIER; + } + + // The event name can be prefixed by the engine URI, i.e. "select * from default.MyEvent" + if (eventName.StartsWith(engineURIQualifier)) + { + var indexDot = eventName.IndexOf('.'); + if (indexDot > 0) + { + var eventNameURI = eventName.Substring(0, indexDot); + var eventNameRemainder = eventName.Substring(indexDot + 1); + + if (engineURIQualifier.Equals(eventNameURI)) + { + eventType = eventAdapterService.GetEventTypeByName(eventNameRemainder); + } + } + } + + // may now be known + if (eventType != null) + { + return eventType; + } + + // The type is not known yet, attempt to add as an object type with the same name + String message = null; + try + { + eventType = eventAdapterService.AddBeanType(eventName, eventName, true, false, false, false); + } + catch (EventAdapterException ex) + { + Log.Debug(".resolveType Event type named '" + eventName + "' not resolved as Type event"); + message = "Failed to resolve event type: " + ex.Message; + } + + // Attempt to use plug-in event types + try + { + eventType = eventAdapterService.AddPlugInEventType(eventName, optionalResolutionURIs, null); + } + catch (EventAdapterException) + { + Log.Debug(".resolveType Event type named '" + eventName + "' not resolved by plug-in event representations"); + // remains unresolved + } + + if (eventType == null) + { + throw new ExprValidationException(message); + } + return eventType; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/FireAndForgetSpec.cs b/NEsper.Core/NEsper.Core/epl/spec/FireAndForgetSpec.cs new file mode 100755 index 000000000..5533b3d7a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/FireAndForgetSpec.cs @@ -0,0 +1,17 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.spec +{ + [Serializable] + public abstract class FireAndForgetSpec + { + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/FireAndForgetSpecDelete.cs b/NEsper.Core/NEsper.Core/epl/spec/FireAndForgetSpecDelete.cs new file mode 100755 index 000000000..18f32d049 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/FireAndForgetSpecDelete.cs @@ -0,0 +1,14 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.spec +{ + public class FireAndForgetSpecDelete : FireAndForgetSpec + { + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/FireAndForgetSpecInsert.cs b/NEsper.Core/NEsper.Core/epl/spec/FireAndForgetSpecInsert.cs new file mode 100755 index 000000000..be57173c5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/FireAndForgetSpecInsert.cs @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.spec +{ + [Serializable] + public class FireAndForgetSpecInsert : FireAndForgetSpec + { + public FireAndForgetSpecInsert(bool useValuesKeyword) + { + IsUseValuesKeyword = useValuesKeyword; + } + + public bool IsUseValuesKeyword { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/FireAndForgetSpecUpdate.cs b/NEsper.Core/NEsper.Core/epl/spec/FireAndForgetSpecUpdate.cs new file mode 100755 index 000000000..59f301693 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/FireAndForgetSpecUpdate.cs @@ -0,0 +1,24 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.epl.spec +{ + [Serializable] + public class FireAndForgetSpecUpdate : FireAndForgetSpec + { + public FireAndForgetSpecUpdate(IList assignments) + { + Assignments = assignments; + } + + public IList Assignments { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/ForClauseItemSpec.cs b/NEsper.Core/NEsper.Core/epl/spec/ForClauseItemSpec.cs new file mode 100755 index 000000000..d9c5a3b85 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/ForClauseItemSpec.cs @@ -0,0 +1,30 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.spec +{ + [Serializable] + public class ForClauseItemSpec : MetaDefItem + { + public ForClauseItemSpec(String keyword, IList expressions) + { + Keyword = keyword; + Expressions = expressions; + } + + public string Keyword { get; set; } + + public IList Expressions { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/ForClauseSpec.cs b/NEsper.Core/NEsper.Core/epl/spec/ForClauseSpec.cs new file mode 100755 index 000000000..776727454 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/ForClauseSpec.cs @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.spec +{ + [Serializable] + public class ForClauseSpec : MetaDefItem + { + public ForClauseSpec() + { + Clauses = new List(); + } + + public IList Clauses { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/GraphOperatorDetail.cs b/NEsper.Core/NEsper.Core/epl/spec/GraphOperatorDetail.cs new file mode 100755 index 000000000..e65a402f5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/GraphOperatorDetail.cs @@ -0,0 +1,24 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.epl.spec +{ + [Serializable] + public class GraphOperatorDetail + { + public GraphOperatorDetail(IDictionary configs) + { + Configs = configs; + } + + public IDictionary Configs { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/spec/GraphOperatorInput.cs b/NEsper.Core/NEsper.Core/epl/spec/GraphOperatorInput.cs new file mode 100755 index 000000000..3d4f84e44 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/GraphOperatorInput.cs @@ -0,0 +1,24 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.epl.spec +{ + [Serializable] + public class GraphOperatorInput + { + public GraphOperatorInput() + { + StreamNamesAndAliases = new List(); + } + + public IList StreamNamesAndAliases { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/GraphOperatorInputNamesAlias.cs b/NEsper.Core/NEsper.Core/epl/spec/GraphOperatorInputNamesAlias.cs new file mode 100755 index 000000000..a661d1a8d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/GraphOperatorInputNamesAlias.cs @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.spec +{ + [Serializable] + public class GraphOperatorInputNamesAlias + { + public GraphOperatorInputNamesAlias(String[] inputStreamNames, String optionalAsName) + { + InputStreamNames = inputStreamNames; + OptionalAsName = optionalAsName; + } + + public string[] InputStreamNames { get; private set; } + + public string OptionalAsName { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/spec/GraphOperatorOutput.cs b/NEsper.Core/NEsper.Core/epl/spec/GraphOperatorOutput.cs new file mode 100755 index 000000000..ab771bdbf --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/GraphOperatorOutput.cs @@ -0,0 +1,24 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.epl.spec +{ + [Serializable] + public class GraphOperatorOutput + { + public GraphOperatorOutput() + { + Items = new List(); + } + + public IList Items { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/spec/GraphOperatorOutputItem.cs b/NEsper.Core/NEsper.Core/epl/spec/GraphOperatorOutputItem.cs new file mode 100755 index 000000000..6c7441358 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/GraphOperatorOutputItem.cs @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.epl.spec +{ + [Serializable] + public class GraphOperatorOutputItem + { + public GraphOperatorOutputItem(String streamName, IList typeInfo) + { + StreamName = streamName; + TypeInfo = typeInfo; + } + + public string StreamName { get; private set; } + + public IList TypeInfo { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/spec/GraphOperatorOutputItemType.cs b/NEsper.Core/NEsper.Core/epl/spec/GraphOperatorOutputItemType.cs new file mode 100755 index 000000000..5fce10db6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/GraphOperatorOutputItemType.cs @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.epl.spec +{ + [Serializable] + public class GraphOperatorOutputItemType + { + public GraphOperatorOutputItemType(bool wildcard, + String typeOrClassname, + IList typeParameters) + { + IsWildcard = wildcard; + TypeOrClassname = typeOrClassname; + TypeParameters = typeParameters; + } + + public bool IsWildcard { get; private set; } + + public string TypeOrClassname { get; private set; } + + public IList TypeParameters { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/spec/GraphOperatorSpec.cs b/NEsper.Core/NEsper.Core/epl/spec/GraphOperatorSpec.cs new file mode 100755 index 000000000..65563def0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/GraphOperatorSpec.cs @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.epl.spec +{ + [Serializable] + public class GraphOperatorSpec + { + public GraphOperatorSpec(String operatorName, + GraphOperatorInput input, + GraphOperatorOutput output, + GraphOperatorDetail detail, + IList annotations) + { + OperatorName = operatorName; + Input = input; + Output = output; + Detail = detail; + Annotations = annotations; + } + + public string OperatorName { get; private set; } + + public GraphOperatorInput Input { get; private set; } + + public GraphOperatorOutput Output { get; private set; } + + public GraphOperatorDetail Detail { get; private set; } + + public IList Annotations { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/spec/GroupByClauseElement.cs b/NEsper.Core/NEsper.Core/epl/spec/GroupByClauseElement.cs new file mode 100755 index 000000000..bc4345dc1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/GroupByClauseElement.cs @@ -0,0 +1,14 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.spec +{ + public interface GroupByClauseElement + { + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/GroupByClauseElementCombinedExpr.cs b/NEsper.Core/NEsper.Core/epl/spec/GroupByClauseElementCombinedExpr.cs new file mode 100755 index 000000000..9f4da81fc --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/GroupByClauseElementCombinedExpr.cs @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.spec +{ + [Serializable] + public class GroupByClauseElementCombinedExpr : GroupByClauseElement + { + public GroupByClauseElementCombinedExpr(IList expressions) + { + Expressions = expressions; + } + + public IList Expressions { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/spec/GroupByClauseElementExpr.cs b/NEsper.Core/NEsper.Core/epl/spec/GroupByClauseElementExpr.cs new file mode 100755 index 000000000..607c9b86d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/GroupByClauseElementExpr.cs @@ -0,0 +1,30 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.spec +{ + [Serializable] + public class GroupByClauseElementExpr : GroupByClauseElement + { + private readonly ExprNode _expr; + + public GroupByClauseElementExpr(ExprNode expr) + { + _expr = expr; + } + + public ExprNode Expr + { + get { return _expr; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/GroupByClauseElementGroupingSet.cs b/NEsper.Core/NEsper.Core/epl/spec/GroupByClauseElementGroupingSet.cs new file mode 100755 index 000000000..aa93bf1b3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/GroupByClauseElementGroupingSet.cs @@ -0,0 +1,24 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.epl.spec +{ + [Serializable] + public class GroupByClauseElementGroupingSet : GroupByClauseElement + { + public GroupByClauseElementGroupingSet(IList elements) + { + Elements = elements; + } + + public IList Elements { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/spec/GroupByClauseElementRollupOrCube.cs b/NEsper.Core/NEsper.Core/epl/spec/GroupByClauseElementRollupOrCube.cs new file mode 100755 index 000000000..63b775db4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/GroupByClauseElementRollupOrCube.cs @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.epl.spec +{ + [Serializable] + public class GroupByClauseElementRollupOrCube : GroupByClauseElement + { + public GroupByClauseElementRollupOrCube(bool cube, IList rollupExpressions) + { + IsCube = cube; + RollupExpressions = rollupExpressions; + } + + public IList RollupExpressions { get; private set; } + + public bool IsCube { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/spec/GroupByClauseExpressions.cs b/NEsper.Core/NEsper.Core/epl/spec/GroupByClauseExpressions.cs new file mode 100755 index 000000000..60f4fc634 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/GroupByClauseExpressions.cs @@ -0,0 +1,44 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.spec +{ + public class GroupByClauseExpressions + { + public GroupByClauseExpressions(ExprNode[] groupByNodes) + : this(groupByNodes, null, null, null, null) + { + } + + public GroupByClauseExpressions( + ExprNode[] groupByNodes, + int[][] groupByRollupLevels, + ExprNode[][] selectClauseCopy, + ExprNode[] optHavingNodeCopy, + ExprNode[][] optOrderByPerLevel) + { + GroupByNodes = groupByNodes; + GroupByRollupLevels = groupByRollupLevels; + SelectClausePerLevel = selectClauseCopy; + OptHavingNodePerLevel = optHavingNodeCopy; + OptOrderByPerLevel = optOrderByPerLevel; + } + + public int[][] GroupByRollupLevels { get; private set; } + + public ExprNode[][] SelectClausePerLevel { get; private set; } + + public ExprNode[][] OptOrderByPerLevel { get; private set; } + + public ExprNode[] OptHavingNodePerLevel { get; private set; } + + public ExprNode[] GroupByNodes { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/spec/InsertIntoDesc.cs b/NEsper.Core/NEsper.Core/epl/spec/InsertIntoDesc.cs new file mode 100755 index 000000000..b4892a50a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/InsertIntoDesc.cs @@ -0,0 +1,76 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.spec +{ + /// + /// Descriptor generated by INSERT-INTO clauses specified in expressions to insert + /// the results of statement as a stream to further statements. + /// + [Serializable] + public class InsertIntoDesc : MetaDefItem + { + /// + /// Ctor. + /// + /// selects insert, remove or insert+remove stream + /// is the event type name + public InsertIntoDesc(SelectClauseStreamSelectorEnum streamSelector, String eventTypeName) + { + StreamSelector = streamSelector; + EventTypeName = eventTypeName; + ColumnNames = new List(); + } + + /// + /// Returns the stream(s) selected for inserting into. + /// + public SelectClauseStreamSelectorEnum StreamSelector { get; private set; } + + /// + /// Returns name of event type to use for insert-into stream. + /// + /// + /// event type name + /// + public string EventTypeName { get; private set; } + + /// + /// Returns a list of column names specified optionally in the insert-into clause, + /// or empty if none specified. + /// + /// + /// column names or empty list if none supplied + /// + public IList ColumnNames { get; private set; } + + /// + /// Add a column name to the insert-into clause. + /// + /// to add + public void Add(string columnName) + { + ColumnNames.Add(columnName); + } + + public static InsertIntoDesc FromColumns(String streamName, IList columns) + { + var insertIntoDesc = new InsertIntoDesc(SelectClauseStreamSelectorEnum.ISTREAM_ONLY, streamName); + foreach (var col in columns) + { + insertIntoDesc.Add(col); + } + return insertIntoDesc; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/IntoTableSpec.cs b/NEsper.Core/NEsper.Core/epl/spec/IntoTableSpec.cs new file mode 100755 index 000000000..d4c8a9714 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/IntoTableSpec.cs @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.spec +{ + [Serializable] + public class IntoTableSpec + { + public IntoTableSpec(string name) + { + Name = name; + } + + public string Name { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/MatchEventSpec.cs b/NEsper.Core/NEsper.Core/epl/spec/MatchEventSpec.cs new file mode 100755 index 000000000..58336b9bd --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/MatchEventSpec.cs @@ -0,0 +1,39 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.epl.spec +{ + /// + /// Specification of matches available. + /// + public class MatchEventSpec + { + public MatchEventSpec(IDictionary> taggedEventTypes, IDictionary> arrayEventTypes) + { + TaggedEventTypes = taggedEventTypes; + ArrayEventTypes = arrayEventTypes; + } + + public MatchEventSpec() + { + TaggedEventTypes = new LinkedHashMap>(); + ArrayEventTypes = new LinkedHashMap>(); + } + + public IDictionary> ArrayEventTypes { get; private set; } + + public IDictionary> TaggedEventTypes { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/MatchRecognizeDefineItem.cs b/NEsper.Core/NEsper.Core/epl/spec/MatchRecognizeDefineItem.cs new file mode 100755 index 000000000..547e32a14 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/MatchRecognizeDefineItem.cs @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.spec +{ + /// + /// Specification for a "define" construct within a match_recognize. + /// + [Serializable] + public class MatchRecognizeDefineItem : MetaDefItem + { + /// Ctor. + /// variable name + /// expression + public MatchRecognizeDefineItem(String identifier, ExprNode expression) { + Identifier = identifier; + Expression = expression; + } + + /// Returns the variable name. + /// name + public string Identifier { get; private set; } + + /// Returns the expression. + /// expression + public ExprNode Expression { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/MatchRecognizeInterval.cs b/NEsper.Core/NEsper.Core/epl/spec/MatchRecognizeInterval.cs new file mode 100755 index 000000000..f92145e30 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/MatchRecognizeInterval.cs @@ -0,0 +1,99 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.time; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.spec +{ + /// Interval specification within match_recognize. + [Serializable] + public class MatchRecognizeInterval : MetaDefItem + { + private ExprTimePeriod _timePeriodExpr; + private readonly bool _orTerminated; + private ExprTimePeriodEvalDeltaConst _timeDeltaComputation; + + /// + /// Ctor. + /// + /// time period + /// or-terminated indicator + public MatchRecognizeInterval(ExprTimePeriod timePeriodExpr, bool orTerminated) + { + _timePeriodExpr = timePeriodExpr; + _orTerminated = orTerminated; + } + + /// + /// Returns the time period. + /// + /// time period + public ExprTimePeriod TimePeriodExpr + { + get { return _timePeriodExpr; } + } + + /// + /// Returns the number of milliseconds. + /// + /// from-time + /// context + /// msec + public long GetScheduleForwardDelta(long fromTime, AgentInstanceContext agentInstanceContext) + { + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().QRegIntervalValue(_timePeriodExpr); + } + if (_timeDeltaComputation == null) { + _timeDeltaComputation = _timePeriodExpr.ConstEvaluator(new ExprEvaluatorContextStatement(agentInstanceContext.StatementContext, false)); + } + long result = _timeDeltaComputation.DeltaAdd(fromTime); + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().ARegIntervalValue(result); + } + return result; + } + + /// + /// Returns the number of milliseconds. + /// + /// from-time + /// context + /// msec + public long GetScheduleBackwardDelta(long fromTime, AgentInstanceContext agentInstanceContext) { + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().QRegIntervalValue(_timePeriodExpr); + } + if (_timeDeltaComputation == null) { + _timeDeltaComputation = _timePeriodExpr.ConstEvaluator(new ExprEvaluatorContextStatement(agentInstanceContext.StatementContext, false)); + } + long result = _timeDeltaComputation.DeltaSubtract(fromTime); + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().ARegIntervalValue(result); + } + return result; + } + + public bool IsOrTerminated + { + get { return _orTerminated; } + } + + public void Validate(ExprValidationContext validationContext) + { + _timePeriodExpr = (ExprTimePeriod) ExprNodeUtility.GetValidatedSubtree(ExprNodeOrigin.MATCHRECOGINTERVAL, _timePeriodExpr, validationContext); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/spec/MatchRecognizeMeasureItem.cs b/NEsper.Core/NEsper.Core/epl/spec/MatchRecognizeMeasureItem.cs new file mode 100755 index 000000000..12bf8120e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/MatchRecognizeMeasureItem.cs @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.spec +{ + /// + /// Specification for measure definition item within match_recognize. + /// + [Serializable] + public class MatchRecognizeMeasureItem : MetaDefItem + { + /// Ctor. + /// expression + /// as name + public MatchRecognizeMeasureItem(ExprNode expr, String name) { + Expr = expr; + Name = name; + } + + /// Returns the as-name. + /// name + public string Name { get; private set; } + + /// Gets or sets the validated expression. + /// expression + public ExprNode Expr { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/MatchRecognizeSkip.cs b/NEsper.Core/NEsper.Core/epl/spec/MatchRecognizeSkip.cs new file mode 100755 index 000000000..d753a8866 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/MatchRecognizeSkip.cs @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.spec +{ + /// + /// Specification for the skip-part of match_recognize. + /// + [Serializable] + public class MatchRecognizeSkip : MetaDefItem + { + /// + /// Ctor. + /// + /// enum + public MatchRecognizeSkip(MatchRecognizeSkipEnum skip) + { + Skip = skip; + } + + /// Skip enum. + /// skip value + public MatchRecognizeSkipEnum Skip { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/MatchRecognizeSkipEnum.cs b/NEsper.Core/NEsper.Core/epl/spec/MatchRecognizeSkipEnum.cs new file mode 100755 index 000000000..f73cc1bb8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/MatchRecognizeSkipEnum.cs @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.spec +{ + /// Skip-enum for match_recognize. + public enum MatchRecognizeSkipEnum + { + /// Skip to current row. + TO_CURRENT_ROW, + + /// Skip to next row. + TO_NEXT_ROW, + + /// Skip past last row. + PAST_LAST_ROW + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/spec/MatchRecognizeSpec.cs b/NEsper.Core/NEsper.Core/epl/spec/MatchRecognizeSpec.cs new file mode 100755 index 000000000..529eafb3c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/MatchRecognizeSpec.cs @@ -0,0 +1,109 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.rowregex; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.spec +{ + /// + /// Specification for match_recognize. + /// + [Serializable] + public class MatchRecognizeSpec : MetaDefItem + { + /// + /// Ctor. + /// + public MatchRecognizeSpec() + { + PartitionByExpressions = new List(); + Measures = new List(); + Defines = new List(); + Skip = new MatchRecognizeSkip(MatchRecognizeSkipEnum.PAST_LAST_ROW); + } + + /// + /// Interval part of null. + /// + /// + /// interval + /// + public MatchRecognizeInterval Interval { get; set; } + + /// + /// True for all-matches. + /// + /// + /// indicator all-matches + /// + public bool IsAllMatches { get; set; } + + /// + /// Returns partition expressions. + /// + /// + /// partition expressions + /// + public IList PartitionByExpressions { get; set; } + + /// + /// Returns the define items. + /// + /// + /// define items + /// + public IList Defines { get; set; } + + /// + /// Returns measures. + /// + /// + /// measures + /// + public IList Measures { get; set; } + + /// + /// Returns the pattern. + /// + /// + /// pattern + /// + public RowRegexExprNode Pattern { get; set; } + + /// + /// Returns the skip. + /// + /// + /// skip + /// + public MatchRecognizeSkip Skip { get; set; } + + /// + /// Add a measure item. + /// + /// to add + public void AddMeasureItem(MatchRecognizeMeasureItem item) + { + Measures.Add(item); + } + + /// + /// Adds a define item. + /// + /// to add + public void AddDefine(MatchRecognizeDefineItem define) + { + Defines.Add(define); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/MethodStreamSpec.cs b/NEsper.Core/NEsper.Core/epl/spec/MethodStreamSpec.cs new file mode 100755 index 000000000..02441dd98 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/MethodStreamSpec.cs @@ -0,0 +1,102 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.spec +{ + /// + /// Specification object for historical data poll via database SQL statement. + /// + [Serializable] + public class MethodStreamSpec + : StreamSpecBase + , StreamSpecRaw + , StreamSpecCompiled + , MetaDefItem + { + /// + /// Ctor. + /// + /// is the stream name or null if none defined + /// is an list of view specifications + /// the prefix in the clause + /// the class name + /// the method name + /// the parameter expressions + /// event type name if provided + public MethodStreamSpec( + string optionalStreamName, + ViewSpec[] viewSpecs, + string ident, + string className, + string methodName, + IList expressions, + string eventTypeName) + : base(optionalStreamName, viewSpecs, StreamSpecOptions.DEFAULT) + { + Ident = ident; + ClassName = className; + MethodName = methodName; + Expressions = expressions; + EventTypeName = eventTypeName; + } + + /// + /// Returns the prefix (method) for the method invocation syntax. + /// + /// identifier + public string Ident { get; private set; } + + /// + /// Returns the class name. + /// + /// class name + public string ClassName { get; private set; } + + /// + /// Returns the method name. + /// + /// method name + public string MethodName { get; private set; } + + public string EventTypeName { get; private set; } + + /// + /// Returns the parameter expressions. + /// + /// parameter expressions + public IList Expressions { get; private set; } + + public StreamSpecCompiled Compile( + StatementContext context, + ICollection eventTypeReferences, + bool isInsertInto, + ICollection assignedTypeNumberStack, + bool isJoin, + bool isContextDeclaration, + bool isOnTrigger, + string optionalStreamName) + { + if (!Ident.Equals("method")) + { + throw new ExprValidationException("Expecting keyword 'method', found '" + Ident + "'"); + } + if (MethodName == null) + { + throw new ExprValidationException("No method name specified for method-based join"); + } + return this; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/spec/NamedWindowConsumerStreamSpec.cs b/NEsper.Core/NEsper.Core/epl/spec/NamedWindowConsumerStreamSpec.cs new file mode 100755 index 000000000..364bd1fab --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/NamedWindowConsumerStreamSpec.cs @@ -0,0 +1,68 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.property; + +namespace com.espertech.esper.epl.spec +{ + /// + /// Specification for use of an existing named window. + /// + [Serializable] + public class NamedWindowConsumerStreamSpec : StreamSpecBase, StreamSpecCompiled + { + [NonSerialized] private readonly PropertyEvaluator _optPropertyEvaluator; + + /// + /// Ctor. + /// + /// specifies the name of the named window + /// a name or null if none defined + /// is the view specifications + /// the named window filters + /// additional options such as unidirectional stream in a join + /// The opt property evaluator. + public NamedWindowConsumerStreamSpec(String windowName, String optionalAsName, ViewSpec[] viewSpecs, IList filterExpressions, StreamSpecOptions streamSpecOptions, PropertyEvaluator optPropertyEvaluator) + : base(optionalAsName, viewSpecs, streamSpecOptions) + { + WindowName = windowName; + FilterExpressions = filterExpressions; + _optPropertyEvaluator = optPropertyEvaluator; + } + + /// Returns the window name. + /// window name + public string WindowName { get; private set; } + + /// Returns list of filter expressions onto the named window, or no filter expressions if none defined. + /// list of filter expressions + public IList FilterExpressions { get; private set; } + + public PropertyEvaluator OptPropertyEvaluator + { + get { return _optPropertyEvaluator; } + } + + public int GetConsumerSpecIndexAmongAll(NamedWindowConsumerStreamSpec[] consumerSpecAll) + { + for (int i = 0; i < consumerSpecAll.Length; i++) + { + if (this == consumerSpecAll[i]) + { + return i; + } + } + throw new EPException("Failed to find find named window consumer spec among list of known consumers"); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/NewItem.cs b/NEsper.Core/NEsper.Core/epl/spec/NewItem.cs new file mode 100755 index 000000000..506ee3f3d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/NewItem.cs @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.spec +{ + public class NewItem + { + public NewItem(String name, ExprNode optExpression) + { + Name = name; + OptExpression = optExpression; + } + + public String Name { get; private set; } + + public ExprNode OptExpression { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/spec/ObjectSpec.cs b/NEsper.Core/NEsper.Core/epl/spec/ObjectSpec.cs new file mode 100755 index 000000000..1b4430fe7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/ObjectSpec.cs @@ -0,0 +1,142 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Text; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.spec +{ + /// + /// Encapsulates the information required to specify an object identification and construction. + /// + /// Abstract class for use with any object, such as views, pattern guards or pattern observers. + /// + /// + /// A object construction specification can be equal to another specification. This information can be + /// important to determine reuse of any object. + /// + /// + [Serializable] + public abstract class ObjectSpec : MetaDefItem + { + /// Constructor. + /// if the namespace the object is in + /// is the name of the object + /// + /// is a list of values representing the object parameters + /// + public ObjectSpec(String _namespace, String objectName, IList objectParameters) + { + this.ObjectNamespace = _namespace; + this.ObjectName = objectName; + this.ObjectParameters = objectParameters; + } + + /// Returns namespace for view object. + /// namespace + public string ObjectNamespace { get; private set; } + + /// Returns the object name. + /// object name + public string ObjectName { get; private set; } + + /// Returns the list of object parameters. + /// list of values representing object parameters + public IList ObjectParameters { get; private set; } + + public override bool Equals(Object otherObject) + { + if (otherObject == this) + { + return true; + } + + if (otherObject == null) + { + return false; + } + + if (GetType() != otherObject.GetType()) + { + return false; + } + + var other = (ObjectSpec)otherObject; + if (ObjectName != other.ObjectName) + { + return false; + } + + if (ObjectParameters.Count != other.ObjectParameters.Count) + { + return false; + } + + // Compare object parameter by object parameter + int index = 0; + foreach (var thisParam in ObjectParameters) + { + var otherParam = other.ObjectParameters[index]; + index++; + + if (!ExprNodeUtility.DeepEquals(thisParam, otherParam)) + { + return false; + } + } + + return true; + } + + /// + /// Serves as a hash function for a particular type. is suitable for use in hashing algorithms and data structures like a hash table. + /// + /// + /// A hash code for the current . + /// + public override int GetHashCode() + { + return + (ObjectName.GetHashCode() * 397) + + (ObjectNamespace.GetHashCode()); + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override String ToString() + { + StringBuilder buffer = new StringBuilder(); + buffer.Append("objectName="); + buffer.Append(ObjectName); + buffer.Append(" objectParameters=("); + char delimiter = ' '; + + if (ObjectParameters != null) + { + foreach (var param in ObjectParameters) + { + buffer.Append(delimiter); + buffer.Append(param.ToExpressionStringMinPrecedenceSafe()); + delimiter = ','; + } + } + + buffer.Append(')'); + + return buffer.ToString(); + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/epl/spec/OnTriggerDesc.cs b/NEsper.Core/NEsper.Core/epl/spec/OnTriggerDesc.cs new file mode 100755 index 000000000..bcf8461a8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/OnTriggerDesc.cs @@ -0,0 +1,30 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.spec +{ + /// Specification for on-trigger statements. + [Serializable] + public abstract class OnTriggerDesc : MetaDefItem + { + /// Ctor. + /// the type of on-trigger + protected OnTriggerDesc(OnTriggerType onTriggerType) + { + OnTriggerType = onTriggerType; + } + + /// Returns the type of the on-trigger statement. + /// trigger type + public OnTriggerType OnTriggerType { get; private set; } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/epl/spec/OnTriggerMergeAction.cs b/NEsper.Core/NEsper.Core/epl/spec/OnTriggerMergeAction.cs new file mode 100755 index 000000000..07f2b78a0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/OnTriggerMergeAction.cs @@ -0,0 +1,30 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.spec +{ + /// Specification for the merge statement insert/Update/delete-part. + [Serializable] + public abstract class OnTriggerMergeAction + { + public ExprNode OptionalWhereClause { get; set; } + + /// + /// Initializes a new instance of the class. + /// + /// The optional where clause. + protected OnTriggerMergeAction(ExprNode optionalWhereClause) + { + OptionalWhereClause = optionalWhereClause; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/OnTriggerMergeActionDelete.cs b/NEsper.Core/NEsper.Core/epl/spec/OnTriggerMergeActionDelete.cs new file mode 100755 index 000000000..4b83da4e7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/OnTriggerMergeActionDelete.cs @@ -0,0 +1,24 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.spec +{ + /// Specification for the merge statement delete-part. + [Serializable] + public class OnTriggerMergeActionDelete : OnTriggerMergeAction + { + public OnTriggerMergeActionDelete(ExprNode optionalMatchCond) + : base(optionalMatchCond) + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/OnTriggerMergeActionInsert.cs b/NEsper.Core/NEsper.Core/epl/spec/OnTriggerMergeActionInsert.cs new file mode 100755 index 000000000..47db82b19 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/OnTriggerMergeActionInsert.cs @@ -0,0 +1,43 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.spec +{ + /// Specification for the merge statement insert-part. + [Serializable] + public class OnTriggerMergeActionInsert : OnTriggerMergeAction + { + [NonSerialized] private IList selectClauseCompiled; + + public OnTriggerMergeActionInsert(ExprNode optionalWhereClause, String optionalStreamName, IList columns, IList selectClause) + : base(optionalWhereClause) + { + OptionalStreamName = optionalStreamName; + Columns = columns; + SelectClause = selectClause; + } + + public string OptionalStreamName { get; private set; } + + public IList Columns { get; private set; } + + public IList SelectClause { get; private set; } + + public IList SelectClauseCompiled + { + get { return selectClauseCompiled; } + set { this.selectClauseCompiled = value; } + } + } + +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/OnTriggerMergeActionUpdate.cs b/NEsper.Core/NEsper.Core/epl/spec/OnTriggerMergeActionUpdate.cs new file mode 100755 index 000000000..6d8bdbd4a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/OnTriggerMergeActionUpdate.cs @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.spec +{ + /// Specification for the merge statement Update-part. + [Serializable] + public class OnTriggerMergeActionUpdate : OnTriggerMergeAction + { + /// + /// Initializes a new instance of the class. + /// + /// The optional match cond. + /// The assignments. + public OnTriggerMergeActionUpdate(ExprNode optionalMatchCond, IList assignments) + : base(optionalMatchCond) + { + Assignments = assignments; + } + + public IList Assignments { get; private set; } + } + +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/OnTriggerMergeDesc.cs b/NEsper.Core/NEsper.Core/epl/spec/OnTriggerMergeDesc.cs new file mode 100755 index 000000000..739c19f87 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/OnTriggerMergeDesc.cs @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.epl.spec +{ + /// + /// Specification for the merge statement. + /// + public class OnTriggerMergeDesc : OnTriggerWindowDesc + { + /// + /// Initializes a new instance of the class. + /// + /// Name of the window. + /// Name of the optional as. + /// The items. + public OnTriggerMergeDesc(String windowName, String optionalAsName, IList items) + : base(windowName, optionalAsName, OnTriggerType.ON_MERGE, false) + { + Items = items; + } + + public IList Items { get; private set; } + } + +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/OnTriggerMergeEntry.cs b/NEsper.Core/NEsper.Core/epl/spec/OnTriggerMergeEntry.cs new file mode 100755 index 000000000..a282eeeab --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/OnTriggerMergeEntry.cs @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.spec +{ + /// Specification for the merge statement insert/update/delete-part. + [Serializable] + public abstract class OnTriggerMergeEntry + { + protected OnTriggerMergeEntry(ExprNode optionalMatchCond) + { + OptionalMatchCond = optionalMatchCond; + } + + public ExprNode OptionalMatchCond { get; set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/spec/OnTriggerMergeMatched.cs b/NEsper.Core/NEsper.Core/epl/spec/OnTriggerMergeMatched.cs new file mode 100755 index 000000000..8b57132c0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/OnTriggerMergeMatched.cs @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.spec +{ + /// Specification for the merge statement insert/Update/delete-part. + [Serializable] + public class OnTriggerMergeMatched + { + public OnTriggerMergeMatched(bool matchedUnmatched, ExprNode optionalMatchCond, IList actions) + { + IsMatchedUnmatched = matchedUnmatched; + OptionalMatchCond = optionalMatchCond; + Actions = actions; + } + + public ExprNode OptionalMatchCond { get; set; } + + public bool IsMatchedUnmatched { get; private set; } + + public IList Actions { get; private set; } + } + +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/OnTriggerSetAssignment.cs b/NEsper.Core/NEsper.Core/epl/spec/OnTriggerSetAssignment.cs new file mode 100755 index 000000000..cee168482 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/OnTriggerSetAssignment.cs @@ -0,0 +1,33 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.spec +{ + /// + /// Descriptor for an on-set assignment. + /// + [Serializable] + public class OnTriggerSetAssignment : MetaDefItem + { + /// Ctor. + /// expression providing new variable value + public OnTriggerSetAssignment(ExprNode expression) + { + Expression = expression; + } + + /// Returns the expression providing the new variable value, or null if none + /// assignment expression + public ExprNode Expression { get; set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/spec/OnTriggerSetDesc.cs b/NEsper.Core/NEsper.Core/epl/spec/OnTriggerSetDesc.cs new file mode 100755 index 000000000..5022ed43a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/OnTriggerSetDesc.cs @@ -0,0 +1,33 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +namespace com.espertech.esper.epl.spec +{ + /// Specification for the on-set statement. + public class OnTriggerSetDesc : OnTriggerDesc + { + private readonly IList assignments; + + /// Ctor. + /// is a list of assignments + public OnTriggerSetDesc(IList assignments) + : base(OnTriggerType.ON_SET) + { + this.assignments = assignments; + } + + /// Returns a list of all variables assignment by the on-set + /// list of assignments + public IList Assignments + { + get { return assignments; } + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/epl/spec/OnTriggerSplitStream.cs b/NEsper.Core/NEsper.Core/epl/spec/OnTriggerSplitStream.cs new file mode 100755 index 000000000..c468725de --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/OnTriggerSplitStream.cs @@ -0,0 +1,54 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.spec +{ + /// Split-stream description. + [Serializable] + public class OnTriggerSplitStream + { + /// + /// Ctor. + /// + /// the insert-into clause + /// the select-clause + /// the from-clause + /// where-expression or null + public OnTriggerSplitStream(InsertIntoDesc insertInto, SelectClauseSpecRaw selectClause, OnTriggerSplitStreamFromClause fromClause, ExprNode whereClause) + { + InsertInto = insertInto; + SelectClause = selectClause; + FromClause = fromClause; + WhereClause = whereClause; + } + + /// + /// Returns the insert-into clause. + /// + /// insert-into + public InsertIntoDesc InsertInto { get; private set; } + + /// + /// Returns the select clause. + /// + /// select + public SelectClauseSpecRaw SelectClause { get; private set; } + + /// + /// Returns the where clause or null if not defined + /// + /// where clause + public ExprNode WhereClause { get; private set; } + + public OnTriggerSplitStreamFromClause FromClause { get; private set; } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/spec/OnTriggerSplitStreamDesc.cs b/NEsper.Core/NEsper.Core/epl/spec/OnTriggerSplitStreamDesc.cs new file mode 100755 index 000000000..cd35a0ce8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/OnTriggerSplitStreamDesc.cs @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +namespace com.espertech.esper.epl.spec +{ + /// + /// Specification for the on-select splitstream statement. + /// + public class OnTriggerSplitStreamDesc : OnTriggerDesc + { + /// + /// Ctor. + /// + /// type of trigger + /// true for use the first-matching where clause, false for all + /// streams + public OnTriggerSplitStreamDesc(OnTriggerType onTriggerType, bool isFirst, + IList splitStreams) + : base(onTriggerType) + { + IsFirst = isFirst; + SplitStreams = splitStreams; + } + + /// + /// Returns the remaining insert-into and select-clauses in the split-stream clause. + /// + /// + /// clauses. + /// + public IList SplitStreams { get; private set; } + + /// + /// Returns indicator whether only the first or all where-clauses are triggering. + /// + /// + /// first or all + /// + public bool IsFirst { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/OnTriggerSplitStreamFromClause.cs b/NEsper.Core/NEsper.Core/epl/spec/OnTriggerSplitStreamFromClause.cs new file mode 100755 index 000000000..3a61346e6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/OnTriggerSplitStreamFromClause.cs @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.spec +{ + [Serializable] + public class OnTriggerSplitStreamFromClause + { + public OnTriggerSplitStreamFromClause(PropertyEvalSpec propertyEvalSpec, string optionalStreamName) + { + PropertyEvalSpec = propertyEvalSpec; + OptionalStreamName = optionalStreamName; + } + + public PropertyEvalSpec PropertyEvalSpec { get; set; } + + public string OptionalStreamName { get; set; } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/spec/OnTriggerType.cs b/NEsper.Core/NEsper.Core/epl/spec/OnTriggerType.cs new file mode 100755 index 000000000..549ebd346 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/OnTriggerType.cs @@ -0,0 +1,72 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.spec +{ + /// Enum for the type of on-trigger statement. + public enum OnTriggerType + { + /// + /// For on-delete triggers that delete from a named window when a triggering event arrives. + /// + ON_DELETE, + + /// + /// For on-select triggers that selected from a named window when a triggering event arrives. + /// + ON_SELECT, + + /// + /// For the on-insert split-stream syntax allowing multiple insert-into streams. + /// + ON_SPLITSTREAM, + + /// + /// For on-set triggers that set variable values when a triggering event arrives. + /// + ON_SET, + + /// + /// For on-Update triggers that Update an event in a named window when a + /// triggering event arrives. + /// + ON_UPDATE, + + /// + /// For on-merge triggers that insert/Update an event in a named window when a + /// triggering event arrives. + /// + ON_MERGE + } + + public static class OnTriggerTypeExtensions + { + public static string GetTextual(this OnTriggerType triggerType) + { + switch (triggerType) + { + case OnTriggerType.ON_DELETE: + return "on-delete"; + case OnTriggerType.ON_SELECT: + return "on-select"; + case OnTriggerType.ON_SPLITSTREAM: + return "on-insert-multiple"; + case OnTriggerType.ON_SET: + return "on-set"; + case OnTriggerType.ON_UPDATE: + return "on-Update"; + case OnTriggerType.ON_MERGE: + return "on-merge"; + } + + throw new ArgumentException(); + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/epl/spec/OnTriggerWindowDesc.cs b/NEsper.Core/NEsper.Core/epl/spec/OnTriggerWindowDesc.cs new file mode 100755 index 000000000..2fc222340 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/OnTriggerWindowDesc.cs @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.spec +{ + /// + /// Specification for the on-select, on-delete and on-Update (via subclass) (no split-stream) statement. + /// + public class OnTriggerWindowDesc : OnTriggerDesc + { + /// + /// Ctor. + /// + /// the window name + /// the optional name + /// for indicationg on-delete, on-select or on-Update + /// if set to true [delete and select]. + public OnTriggerWindowDesc(String windowName, String optionalAsName, OnTriggerType onTriggerType, bool deleteAndSelect) + : base(onTriggerType) + { + WindowName = windowName; + OptionalAsName = optionalAsName; + IsDeleteAndSelect = deleteAndSelect; + } + + /// + /// Returns the window name. + /// + /// The name of the window. + public string WindowName { get; private set; } + + /// + /// Returns the name, or null if none defined. + /// + /// The name of the optional as. + public string OptionalAsName { get; private set; } + + /// + /// Gets or sets a value indicating whether [delete and select]. + /// + /// true if [delete and select]; otherwise, false. + public bool IsDeleteAndSelect { get; private set; } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/epl/spec/OnTriggerWindowUpdateDesc.cs b/NEsper.Core/NEsper.Core/epl/spec/OnTriggerWindowUpdateDesc.cs new file mode 100755 index 000000000..c829520da --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/OnTriggerWindowUpdateDesc.cs @@ -0,0 +1,35 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.epl.spec +{ + /// + /// Specification for the on-select and on-delete (no split-stream) statement. + /// + [Serializable] + public class OnTriggerWindowUpdateDesc : OnTriggerWindowDesc + { + /// Ctor. + /// the window name + /// the optional name + /// set-assignments + + public OnTriggerWindowUpdateDesc(String windowName, String optionalAsName, IList assignments) + : base(windowName, optionalAsName, spec.OnTriggerType.ON_UPDATE, false) + { + Assignments = assignments; + } + + /// Returns assignments. + /// assignments + public IList Assignments { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/OrderByItem.cs b/NEsper.Core/NEsper.Core/epl/spec/OrderByItem.cs new file mode 100755 index 000000000..46f15ca07 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/OrderByItem.cs @@ -0,0 +1,56 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.spec +{ + /// + /// Specification object to an element in the order-by expression. + /// + [Serializable] + public class OrderByItem : MetaDefItem + { + public static readonly OrderByItem[] EMPTY_ORDERBY_ARRAY = new OrderByItem[0]; + + /// Ctor. + /// is the order-by expression node + /// is true for ascending, or false for descending sort + public OrderByItem(ExprNode exprNode, bool ascending) + { + ExprNode = exprNode; + IsDescending = ascending; + } + + /// Returns the order-by expression node. + /// expression node. + public ExprNode ExprNode { get; private set; } + + /// Returns true for ascending, false for descending. + /// indicator if ascending or descending + public bool IsDescending { get; private set; } + + public OrderByItem Copy() + { + return new OrderByItem(ExprNode, IsDescending); + } + + public static OrderByItem[] ToArray(ICollection expressions) { + if (expressions.IsEmpty()) { + return EMPTY_ORDERBY_ARRAY; + } + return expressions.ToArray(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/OuterJoinDesc.cs b/NEsper.Core/NEsper.Core/epl/spec/OuterJoinDesc.cs new file mode 100755 index 000000000..a7636ce11 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/OuterJoinDesc.cs @@ -0,0 +1,132 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.ops; +using com.espertech.esper.type; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.spec +{ + /// + /// Contains the ON-clause criteria in an outer join. + /// + [Serializable] + public class OuterJoinDesc : MetaDefItem + { + public static readonly OuterJoinDesc[] EMPTY_OUTERJOIN_ARRAY = new OuterJoinDesc[0]; + + /// Ctor. + /// type of the outer join + /// left hand identifier node + /// right hand identifier node + /// additional optional left hand identifier nodes for the on-clause in a logical-and + /// additional optional right hand identifier nodes for the on-clause in a logical-and + public OuterJoinDesc(OuterJoinType outerJoinType, ExprIdentNode optLeftNode, ExprIdentNode optRightNode, ExprIdentNode[] optAddLeftNode, ExprIdentNode[] optAddRightNode) + { + OuterJoinType = outerJoinType; + OptLeftNode = optLeftNode; + OptRightNode = optRightNode; + AdditionalLeftNodes = optAddLeftNode; + AdditionalRightNodes = optAddRightNode; + } + + /// Returns the type of outer join (left/right/full). + /// outer join type + public OuterJoinType OuterJoinType { get; private set; } + + /// Returns left hand identifier node. + /// left hand + public ExprIdentNode OptLeftNode { get; private set; } + + /// Returns right hand identifier node. + /// right hand + public ExprIdentNode OptRightNode { get; private set; } + + /// Returns additional properties in the on-clause, if any, that are connected via logical-and + /// additional properties + public ExprIdentNode[] AdditionalLeftNodes { get; private set; } + + /// Returns additional properties in the on-clause, if any, that are connected via logical-and + /// additional properties + public ExprIdentNode[] AdditionalRightNodes { get; private set; } + + /// Make an expression node that represents the outer join criteria as specified in the on-clause. + /// context for expression evalauation + /// expression node for outer join criteria + public ExprNode MakeExprNode(ExprEvaluatorContext exprEvaluatorContext) + { + ExprNode representativeNode = new ExprEqualsNodeImpl(false, false); + representativeNode.AddChildNode(OptLeftNode); + representativeNode.AddChildNode(OptRightNode); + + if (AdditionalLeftNodes == null) + { + TopValidate(representativeNode, exprEvaluatorContext); + return representativeNode; + } + + ExprAndNode andNode = new ExprAndNodeImpl(); + TopValidate(representativeNode, exprEvaluatorContext); + andNode.AddChildNode(representativeNode); + representativeNode = andNode; + + for (int i = 0; i < AdditionalLeftNodes.Length; i++) + { + ExprEqualsNode eqNode = new ExprEqualsNodeImpl(false, false); + eqNode.AddChildNode(AdditionalLeftNodes[i]); + eqNode.AddChildNode(AdditionalRightNodes[i]); + TopValidate(eqNode, exprEvaluatorContext); + andNode.AddChildNode(eqNode); + } + + TopValidate(andNode, exprEvaluatorContext); + return representativeNode; + } + + public static bool ConsistsOfAllInnerJoins(OuterJoinDesc[] outerJoinDescList) + { + foreach (OuterJoinDesc desc in outerJoinDescList) + { + if (desc.OuterJoinType != OuterJoinType.INNER) + { + return false; + } + } + return true; + } + + public static OuterJoinDesc[] ToArray(ICollection expressions) + { + if (expressions.IsEmpty()) + { + return EMPTY_OUTERJOIN_ARRAY; + } + return expressions.ToArray(); + } + + private void TopValidate(ExprNode exprNode, ExprEvaluatorContext exprEvaluatorContext) + { + try + { + var validationContext = new ExprValidationContext(null, null, null, null, null, null, null, exprEvaluatorContext, null, null, -1, null, null, null, false, false, false, false, null, false); + exprNode.Validate(validationContext); + } + catch (ExprValidationException e) + { + throw new IllegalStateException("Failed to make representative node for outer join criteria"); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/OutputLimitLimitType.cs b/NEsper.Core/NEsper.Core/epl/spec/OutputLimitLimitType.cs new file mode 100755 index 000000000..f7ccf51ae --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/OutputLimitLimitType.cs @@ -0,0 +1,46 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.spec +{ + /// Enum for describing the type of output limit within an interval. + public enum OutputLimitLimitType + { + /// + /// Output first event, relative to the output batch. + /// + FIRST, + + /// + /// Output last event, relative to the output batch. + /// + LAST, + + /// + /// The ALL keyword has been explicitly specified: Output all events, + /// relative to the output batch. + /// + /// In the fully-grouped and aggregated case, the explicit ALL outputs one row for each group. + /// + ALL, + + /// + /// The ALL keyword has not been explicitly specified: Output all events, relative + /// to the output batch. + /// + /// In the fully-grouped and aggregated case, the + /// default ALL outputs all events of the batch row-by-row, multiple per group. + /// + DEFAULT, + + /// + /// Output a snapshot of the current state, relative to the full historical state of a statement. + /// + SNAPSHOT + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/epl/spec/OutputLimitRateType.cs b/NEsper.Core/NEsper.Core/epl/spec/OutputLimitRateType.cs new file mode 100755 index 000000000..8fb0c46b8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/OutputLimitRateType.cs @@ -0,0 +1,33 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.spec +{ + /// Enum for the type of rate for output-rate limiting. + public enum OutputLimitRateType + { + /// Output by number of events. + EVENTS, + + /// Output following a crontab-like schedule. + CRONTAB, + + /// Output when an expression turns true. + WHEN_EXPRESSION, + + /// Output based on a time period passing. + TIME_PERIOD, + + /// Output after a given time period + AFTER, + + /// Output upon context partition (agent instance) termination + TERM + + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/OutputLimitSpec.cs b/NEsper.Core/NEsper.Core/epl/spec/OutputLimitSpec.cs new file mode 100755 index 000000000..8047e4994 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/OutputLimitSpec.cs @@ -0,0 +1,98 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.time; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.spec +{ + /// Spec for defining an output rate + [Serializable] + public class OutputLimitSpec : MetaDefItem + { + /// + /// Ctor. For batching events by event count. + /// + /// is the fixed output rate, or null if by variable + /// an optional variable name instead of the rate + /// type of the rate + /// indicates whether to output only the first, only the last, or all events + /// for controlling output by a bool expression + /// variable assignments, if null if none + /// crontab parameters + /// the time period, or null if none + /// after-keyword time period + /// after-keyword number of events + /// if set to true [and after terminate]. + /// The and after terminate expr. + /// The and after terminate set expressions. + public OutputLimitSpec(double? rate, + String variableForRate, + OutputLimitRateType rateType, + OutputLimitLimitType displayLimit, + ExprNode whenExpressionNode, + IList thenExpressions, + IList crontabAtSchedule, + ExprTimePeriod timePeriodExpr, + ExprTimePeriod afterTimePeriodExpr, + int? afterNumberOfEvents, + bool isAndAfterTerminate, + ExprNode andAfterTerminateExpr, + IList andAfterTerminateSetExpressions) + { + Rate = rate; + DisplayLimit = displayLimit; + VariableName = variableForRate; + RateType = rateType; + CrontabAtSchedule = crontabAtSchedule; + WhenExpressionNode = whenExpressionNode; + ThenExpressions = thenExpressions; + TimePeriodExpr = timePeriodExpr; + AfterTimePeriodExpr = afterTimePeriodExpr; + AfterNumberOfEvents = afterNumberOfEvents; + IsAndAfterTerminate = isAndAfterTerminate; + AndAfterTerminateExpr = andAfterTerminateExpr; + AndAfterTerminateThenExpressions = andAfterTerminateSetExpressions; + } + + public OutputLimitSpec(OutputLimitLimitType displayLimit, OutputLimitRateType rateType) + : this(null, null, rateType, displayLimit, null, null, null, null, null, null, false, null, null) + { + } + + public OutputLimitLimitType DisplayLimit { get; private set; } + + public OutputLimitRateType RateType { get; private set; } + + public double? Rate { get; private set; } + + public string VariableName { get; private set; } + + public ExprNode WhenExpressionNode { get; set; } + + public IList ThenExpressions { get; private set; } + + public IList CrontabAtSchedule { get; private set; } + + public ExprTimePeriod TimePeriodExpr { get; set; } + + public ExprTimePeriod AfterTimePeriodExpr { get; set; } + + public int? AfterNumberOfEvents { get; private set; } + + public bool IsAndAfterTerminate { get; private set; } + + public ExprNode AndAfterTerminateExpr { get; set; } + + public IList AndAfterTerminateThenExpressions { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/PatternGuardSpec.cs b/NEsper.Core/NEsper.Core/epl/spec/PatternGuardSpec.cs new file mode 100755 index 000000000..d6541a40b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/PatternGuardSpec.cs @@ -0,0 +1,33 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.spec +{ + /// + /// Specification for a pattern guard object consists of a namespace, name and guard object parameters. + /// + [Serializable] + public sealed class PatternGuardSpec : ObjectSpec + { + /// Constructor. + /// if the namespace the object is in + /// is the name of the object + /// + /// is a list of values representing the object parameters + /// + public PatternGuardSpec(String _namespace, String objectName, IList objectParameters) + : base(_namespace, objectName, objectParameters) + { + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/epl/spec/PatternObserverSpec.cs b/NEsper.Core/NEsper.Core/epl/spec/PatternObserverSpec.cs new file mode 100755 index 000000000..bdb449f13 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/PatternObserverSpec.cs @@ -0,0 +1,33 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.spec +{ + /// + /// Specification for a pattern observer object consists of a namespace, name and object parameters. + /// + [Serializable] + public sealed class PatternObserverSpec : ObjectSpec + { + /// Constructor. + /// if the namespace the object is in + /// is the name of the object + /// + /// is a list of values representing the object parameters + /// + public PatternObserverSpec(String _namespace, String objectName, IList objectParameters) + : base(_namespace, objectName, objectParameters) + { + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/epl/spec/PatternStreamSpecCompiled.cs b/NEsper.Core/NEsper.Core/epl/spec/PatternStreamSpecCompiled.cs new file mode 100755 index 000000000..e06139b7b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/PatternStreamSpecCompiled.cs @@ -0,0 +1,110 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.pattern; + +namespace com.espertech.esper.epl.spec +{ + /// + /// Specification for building an event stream out of a pattern statement and views + /// staggered onto the pattern statement. + /// + /// The pattern statement is represented by the top EvalNode evaluation node. A pattern + /// statement contains tagged events (i.e. a=A -> b=B). Thus the resulting event + /// type is has properties "a" and "b" of the type of A and B. + /// + public class PatternStreamSpecCompiled : StreamSpecBase, StreamSpecCompiled + { + private readonly EvalFactoryNode _evalFactoryNode; + private readonly IDictionary> _taggedEventTypes; // Stores types for filters with tags, single event + private readonly IDictionary> _arrayEventTypes; // Stores types for filters with tags, array event + private readonly ISet _allTags; + private readonly bool _suppressSameEventMatches; + private readonly bool _discardPartialsOnMatch; + /// + /// Ctor. + /// + /// pattern evaluation node representing pattern statement + /// event tags and their types as specified in the pattern, copied to allow original collection to change + /// event tags and their types as specified in the pattern for any repeat-expressions that generate an array of events + /// All tags. + /// specifies what view to use to derive data + /// stream name, or null if none supplied + /// additional stream options such as unidirectional stream in a join, applicable for joins + /// if set to true [suppress same event matches]. + /// if set to true [discard partials on match]. + public PatternStreamSpecCompiled(EvalFactoryNode evalFactoryNode, IDictionary> taggedEventTypes, IDictionary> arrayEventTypes, ISet allTags, ViewSpec[] viewSpecs, String optionalStreamName, StreamSpecOptions streamSpecOptions, bool suppressSameEventMatches, bool discardPartialsOnMatch) + : base(optionalStreamName, viewSpecs, streamSpecOptions) + { + _suppressSameEventMatches = suppressSameEventMatches; + _discardPartialsOnMatch = discardPartialsOnMatch; + _evalFactoryNode = evalFactoryNode; + _allTags = allTags; + + var copy = new LinkedHashMap>(); + copy.PutAll(taggedEventTypes); + _taggedEventTypes = copy; + + copy = new LinkedHashMap>(); + copy.PutAll(arrayEventTypes); + _arrayEventTypes = copy; + } + + /// Returns the pattern expression evaluation node for the top pattern operator. + /// parent pattern expression node + public EvalFactoryNode EvalFactoryNode + { + get { return _evalFactoryNode; } + } + + /// Returns event types tagged in the pattern expression. + /// map of tag and event type tagged in pattern expression + public IDictionary> TaggedEventTypes + { + get { return _taggedEventTypes; } + } + + /// Returns event types tagged in the pattern expression under a repeat-operator. + /// map of tag and event type tagged in pattern expression, repeated an thus producing array events + public IDictionary> ArrayEventTypes + { + get { return _arrayEventTypes; } + } + + public MatchedEventMapMeta MatchedEventMapMeta + { + get + { + String[] tags = _allTags.ToArray(); + return new MatchedEventMapMeta(tags, !_arrayEventTypes.IsEmpty()); + } + } + + public ISet AllTags + { + get { return _allTags; } + } + + public bool IsSuppressSameEventMatches + { + get { return _suppressSameEventMatches; } + } + + public bool IsDiscardPartialsOnMatch + { + get { return _discardPartialsOnMatch; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/PatternStreamSpecRaw.cs b/NEsper.Core/NEsper.Core/epl/spec/PatternStreamSpecRaw.cs new file mode 100755 index 000000000..d20598ab8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/PatternStreamSpecRaw.cs @@ -0,0 +1,929 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.client.annotation; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.time; +using com.espertech.esper.epl.expression.visitor; +using com.espertech.esper.epl.property; +using com.espertech.esper.events; +using com.espertech.esper.filter; +using com.espertech.esper.pattern; +using com.espertech.esper.pattern.guard; +using com.espertech.esper.pattern.observer; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.spec +{ + /// + /// Pattern specification in unvalidated, unoptimized form. + /// + [Serializable] + public class PatternStreamSpecRaw + : StreamSpecBase + , StreamSpecRaw + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly EvalFactoryNode _evalFactoryNode; + private readonly bool _suppressSameEventMatches; + private readonly bool _discardPartialsOnMatch; + + /// + /// Ctor. + /// + /// pattern evaluation node representing pattern statement + /// specifies what view to use to derive data + /// stream name, or null if none supplied + /// additional options, such as unidirectional stream in a join + /// if set to true [suppress same event matches]. + /// if set to true [discard partials on match]. + public PatternStreamSpecRaw( + EvalFactoryNode evalFactoryNode, + ViewSpec[] viewSpecs, + string optionalStreamName, + StreamSpecOptions streamSpecOptions, + bool suppressSameEventMatches, + bool discardPartialsOnMatch) + : base(optionalStreamName, viewSpecs, streamSpecOptions) + { + _evalFactoryNode = evalFactoryNode; + _suppressSameEventMatches = suppressSameEventMatches; + _discardPartialsOnMatch = discardPartialsOnMatch; + } + + /// + /// Returns the pattern expression evaluation node for the top pattern operator. + /// + /// parent pattern expression node + public EvalFactoryNode EvalFactoryNode + { + get { return _evalFactoryNode; } + } + + public StreamSpecCompiled Compile( + StatementContext context, + ICollection eventTypeReferences, + bool isInsertInto, + ICollection assignedTypeNumberStack, + bool isJoin, + bool isContextDeclaration, + bool isOnTrigger, + string optionalStreamName) + { + return CompileInternal( + context, eventTypeReferences, isInsertInto, assignedTypeNumberStack, null, null, isJoin, + isContextDeclaration, isOnTrigger); + } + + public PatternStreamSpecCompiled Compile( + StatementContext context, + ICollection eventTypeReferences, + bool isInsertInto, + ICollection assignedTypeNumberStack, + MatchEventSpec priorTags, + ISet priorAllTags, + bool isJoin, + bool isContextDeclaration, + bool isOnTrigger) + { + return CompileInternal( + context, eventTypeReferences, isInsertInto, assignedTypeNumberStack, priorTags, priorAllTags, isJoin, + isContextDeclaration, isOnTrigger); + } + + private PatternStreamSpecCompiled CompileInternal( + StatementContext context, + ICollection eventTypeReferences, + bool isInsertInto, + ICollection assignedTypeNumberStack, + MatchEventSpec tags, + IEnumerable priorAllTags, + bool isJoin, + bool isContextDeclaration, + bool isOnTrigger) + { + // validate + if ((_suppressSameEventMatches || _discardPartialsOnMatch) && + (isJoin || isContextDeclaration || isOnTrigger)) + { + throw new ExprValidationException( + "Discard-partials and suppress-matches is not supported in a joins, context declaration and on-action"); + } + + if (tags == null) + { + tags = new MatchEventSpec(); + } + var subexpressionIdStack = new ArrayDeque(assignedTypeNumberStack); + var evaluatorContextStmt = new ExprEvaluatorContextStatement(context, false); + var nodeStack = new Stack(); + + // detemine ordered tags + var filterFactoryNodes = EvalNodeUtil.RecursiveGetChildNodes( + _evalFactoryNode, FilterForFilterFactoryNodes.INSTANCE); + var allTagNamesOrdered = new LinkedHashSet(); + if (priorAllTags != null) + { + allTagNamesOrdered.AddAll(priorAllTags); + } + foreach (var filterNode in filterFactoryNodes) + { + var factory = (EvalFilterFactoryNode)filterNode; + int tagNumber; + if (factory.EventAsName != null) + { + if (!allTagNamesOrdered.Contains(factory.EventAsName)) + { + allTagNamesOrdered.Add(factory.EventAsName); + tagNumber = allTagNamesOrdered.Count - 1; + } + else + { + tagNumber = FindTagNumber(factory.EventAsName, allTagNamesOrdered); + } + factory.EventAsTagNumber = tagNumber; + } + } + + RecursiveCompile( + _evalFactoryNode, context, evaluatorContextStmt, eventTypeReferences, isInsertInto, tags, + subexpressionIdStack, nodeStack, allTagNamesOrdered); + + var auditPattern = AuditEnum.PATTERN.GetAudit(context.Annotations); + var auditPatternInstance = AuditEnum.PATTERNINSTANCES.GetAudit(context.Annotations); + var compiledEvalFactoryNode = _evalFactoryNode; + if (context.PatternNodeFactory.IsAuditSupported && (auditPattern != null || auditPatternInstance != null)) + { + var instanceCount = new EvalAuditInstanceCount(); + compiledEvalFactoryNode = RecursiveAddAuditNode( + context.PatternNodeFactory, null, auditPattern != null, auditPatternInstance != null, + _evalFactoryNode, instanceCount); + } + + return new PatternStreamSpecCompiled( + compiledEvalFactoryNode, tags.TaggedEventTypes, tags.ArrayEventTypes, allTagNamesOrdered, ViewSpecs, + OptionalStreamName, Options, _suppressSameEventMatches, _discardPartialsOnMatch); + } + + private static void RecursiveCompile( + EvalFactoryNode evalNode, + StatementContext context, + ExprEvaluatorContext evaluatorContext, + ICollection eventTypeReferences, + bool isInsertInto, + MatchEventSpec tags, + Deque subexpressionIdStack, + Stack parentNodeStack, + ICollection allTagNamesOrdered) + { + var counter = 0; + parentNodeStack.Push(evalNode); + foreach (var child in evalNode.ChildNodes) + { + subexpressionIdStack.AddLast(counter++); + RecursiveCompile( + child, context, evaluatorContext, eventTypeReferences, isInsertInto, tags, subexpressionIdStack, + parentNodeStack, allTagNamesOrdered); + subexpressionIdStack.RemoveLast(); + } + parentNodeStack.Pop(); + + LinkedHashMap> newTaggedEventTypes = null; + LinkedHashMap> newArrayEventTypes = null; + + if (evalNode is EvalFilterFactoryNode) + { + var filterNode = (EvalFilterFactoryNode)evalNode; + var eventName = filterNode.RawFilterSpec.EventTypeName; + if (context.TableService.GetTableMetadata(eventName) != null) + { + throw new ExprValidationException("Tables cannot be used in pattern filter atoms"); + } + + var resolvedEventType = FilterStreamSpecRaw.ResolveType( + context.EngineURI, eventName, context.EventAdapterService, context.PlugInTypeResolutionURIs); + var finalEventType = resolvedEventType; + var optionalTag = filterNode.EventAsName; + var isPropertyEvaluation = false; + var isParentMatchUntil = IsParentMatchUntil(evalNode, parentNodeStack); + + // obtain property event type, if final event type is properties + if (filterNode.RawFilterSpec.OptionalPropertyEvalSpec != null) + { + var optionalPropertyEvaluator = + PropertyEvaluatorFactory.MakeEvaluator( + filterNode.RawFilterSpec.OptionalPropertyEvalSpec, + resolvedEventType, + filterNode.EventAsName, + context.EventAdapterService, + context.EngineImportService, + context.SchedulingService, + context.VariableService, + context.ScriptingService, + context.TableService, + context.EngineURI, + context.StatementId, + context.StatementName, + context.Annotations, + subexpressionIdStack, + context.ConfigSnapshot, + context.NamedWindowMgmtService, + context.StatementExtensionServicesContext); + finalEventType = optionalPropertyEvaluator.FragmentEventType; + isPropertyEvaluation = true; + } + + if (finalEventType is EventTypeSPI) + { + eventTypeReferences.Add(((EventTypeSPI)finalEventType).Metadata.PrimaryName); + } + + // If a tag was supplied for the type, the tags must stay with this type, i.e. a=BeanA -> b=BeanA -> a=BeanB is a no + if (optionalTag != null) + { + var pair = tags.TaggedEventTypes.Get(optionalTag); + EventType existingType = null; + if (pair != null) + { + existingType = pair.First; + } + if (existingType == null) + { + pair = tags.ArrayEventTypes.Get(optionalTag); + if (pair != null) + { + throw new ExprValidationException( + "Tag '" + optionalTag + "' for event '" + eventName + + "' used in the repeat-until operator cannot also appear in other filter expressions"); + } + } + if ((existingType != null) && (existingType != finalEventType)) + { + throw new ExprValidationException( + "Tag '" + optionalTag + "' for event '" + eventName + + "' has already been declared for events of type " + existingType.UnderlyingType.FullName); + } + pair = new Pair(finalEventType, eventName); + + // add tagged type + if (isPropertyEvaluation || isParentMatchUntil) + { + newArrayEventTypes = new LinkedHashMap>(); + newArrayEventTypes.Put(optionalTag, pair); + } + else + { + newTaggedEventTypes = new LinkedHashMap>(); + newTaggedEventTypes.Put(optionalTag, pair); + } + } + + // For this filter, filter types are all known tags at this time, + // and additionally stream 0 (self) is our event type. + // Stream type service allows resolution by property name event if that name appears in other tags. + // by defaulting to stream zero. + // Stream zero is always the current event type, all others follow the order of the map (stream 1 to N). + var selfStreamName = optionalTag; + if (selfStreamName == null) + { + selfStreamName = "s_" + UuidGenerator.Generate(); + } + var filterTypes = new LinkedHashMap>(); + var typePair = new Pair(finalEventType, eventName); + filterTypes.Put(selfStreamName, typePair); + filterTypes.PutAll(tags.TaggedEventTypes); + + // for the filter, specify all tags used + var filterTaggedEventTypes = new LinkedHashMap>(tags.TaggedEventTypes); + filterTaggedEventTypes.Remove(optionalTag); + + // handle array tags (match-until clause) + LinkedHashMap> arrayCompositeEventTypes = null; + if (tags.ArrayEventTypes != null && !tags.ArrayEventTypes.IsEmpty()) + { + arrayCompositeEventTypes = new LinkedHashMap>(); + var patternSubexEventType = GetPatternSubexEventType( + context.StatementId, "pattern", subexpressionIdStack); + + foreach (var entry in tags.ArrayEventTypes) + { + var specificArrayType = new LinkedHashMap>(); + specificArrayType.Put(entry.Key, entry.Value); + var arrayTagCompositeEventType = + context.EventAdapterService.CreateSemiAnonymousMapType( + patternSubexEventType, Collections.GetEmptyMap>(), + specificArrayType, isInsertInto); + context.StatementSemiAnonymousTypeRegistry.Register(arrayTagCompositeEventType); + + var tag = entry.Key; + if (!filterTypes.ContainsKey(tag)) + { + var pair = new Pair(arrayTagCompositeEventType, tag); + filterTypes.Put(tag, pair); + arrayCompositeEventTypes.Put(tag, pair); + } + } + } + + StreamTypeService streamTypeService = new StreamTypeServiceImpl( + filterTypes, context.EngineURI, true, false); + var exprNodes = filterNode.RawFilterSpec.FilterExpressions; + + var spec = FilterSpecCompiler.MakeFilterSpec( + resolvedEventType, eventName, exprNodes, + filterNode.RawFilterSpec.OptionalPropertyEvalSpec, + filterTaggedEventTypes, + arrayCompositeEventTypes, + streamTypeService, + null, context, subexpressionIdStack); + filterNode.FilterSpec = spec; + } + else if (evalNode is EvalObserverFactoryNode) + { + var observerNode = (EvalObserverFactoryNode)evalNode; + try + { + var observerFactory = context.PatternResolutionService.Create(observerNode.PatternObserverSpec); + + var streamTypeService = GetStreamTypeService( + context.EngineURI, context.StatementId, context.EventAdapterService, tags.TaggedEventTypes, + tags.ArrayEventTypes, subexpressionIdStack, "observer", context); + var validationContext = new ExprValidationContext( + streamTypeService, + context.EngineImportService, + context.StatementExtensionServicesContext, null, + context.SchedulingService, + context.VariableService, + context.TableService, evaluatorContext, + context.EventAdapterService, + context.StatementName, + context.StatementId, + context.Annotations, + context.ContextDescriptor, + context.ScriptingService, + false, false, false, false, null, false); + var validated = ValidateExpressions( + ExprNodeOrigin.PATTERNOBSERVER, observerNode.PatternObserverSpec.ObjectParameters, + validationContext); + + MatchedEventConvertor convertor = new MatchedEventConvertorImpl( + tags.TaggedEventTypes, tags.ArrayEventTypes, allTagNamesOrdered, context.EventAdapterService); + + observerNode.ObserverFactory = observerFactory; + observerFactory.SetObserverParameters(validated, convertor, validationContext); + } + catch (ObserverParameterException e) + { + throw new ExprValidationException( + "Invalid parameter for pattern observer '" + observerNode.ToPrecedenceFreeEPL() + "': " + + e.Message, e); + } + catch (PatternObjectException e) + { + throw new ExprValidationException( + "Failed to resolve pattern observer '" + observerNode.ToPrecedenceFreeEPL() + "': " + e.Message, + e); + } + } + else if (evalNode is EvalGuardFactoryNode) + { + var guardNode = (EvalGuardFactoryNode)evalNode; + try + { + var guardFactory = context.PatternResolutionService.Create(guardNode.PatternGuardSpec); + + var streamTypeService = GetStreamTypeService( + context.EngineURI, context.StatementId, context.EventAdapterService, tags.TaggedEventTypes, + tags.ArrayEventTypes, subexpressionIdStack, "guard", context); + var validationContext = new ExprValidationContext( + streamTypeService, + context.EngineImportService, + context.StatementExtensionServicesContext, null, + context.SchedulingService, + context.VariableService, + context.TableService, evaluatorContext, + context.EventAdapterService, + context.StatementName, + context.StatementId, + context.Annotations, + context.ContextDescriptor, + context.ScriptingService, + false, false, false, false, null, false); + var validated = ValidateExpressions( + ExprNodeOrigin.PATTERNGUARD, guardNode.PatternGuardSpec.ObjectParameters, validationContext); + + MatchedEventConvertor convertor = new MatchedEventConvertorImpl( + tags.TaggedEventTypes, tags.ArrayEventTypes, allTagNamesOrdered, context.EventAdapterService); + + guardNode.GuardFactory = guardFactory; + guardFactory.SetGuardParameters(validated, convertor); + } + catch (GuardParameterException e) + { + throw new ExprValidationException( + "Invalid parameter for pattern guard '" + guardNode.ToPrecedenceFreeEPL() + "': " + e.Message, e); + } + catch (PatternObjectException e) + { + throw new ExprValidationException( + "Failed to resolve pattern guard '" + guardNode.ToPrecedenceFreeEPL() + "': " + e.Message, e); + } + } + else if (evalNode is EvalEveryDistinctFactoryNode) + { + var distinctNode = (EvalEveryDistinctFactoryNode)evalNode; + var matchEventFromChildNodes = AnalyzeMatchEvent(distinctNode); + var streamTypeService = GetStreamTypeService( + context.EngineURI, context.StatementId, context.EventAdapterService, + matchEventFromChildNodes.TaggedEventTypes, matchEventFromChildNodes.ArrayEventTypes, + subexpressionIdStack, "every-distinct", context); + var validationContext = new ExprValidationContext( + streamTypeService, + context.EngineImportService, + context.StatementExtensionServicesContext, null, + context.SchedulingService, + context.VariableService, + context.TableService, evaluatorContext, + context.EventAdapterService, + context.StatementName, + context.StatementId, + context.Annotations, + context.ContextDescriptor, + context.ScriptingService, + false, false, false, false, null, false); + IList validated; + try + { + validated = ValidateExpressions( + ExprNodeOrigin.PATTERNEVERYDISTINCT, distinctNode.Expressions, validationContext); + } + catch (ExprValidationPropertyException ex) + { + throw new ExprValidationPropertyException( + ex.Message + + ", every-distinct requires that all properties resolve from sub-expressions to the every-distinct", + ex.InnerException); + } + + MatchedEventConvertor convertor = + new MatchedEventConvertorImpl( + matchEventFromChildNodes.TaggedEventTypes, matchEventFromChildNodes.ArrayEventTypes, + allTagNamesOrdered, context.EventAdapterService); + + distinctNode.Convertor = convertor; + + // Determine whether some expressions are constants or time period + IList distinctExpressions = new List(); + ExprTimePeriodEvalDeltaConst timeDeltaComputation = null; + ExprNode expiryTimeExp = null; + var count = -1; + var last = validated.Count - 1; + foreach (var expr in validated) + { + count++; + if (count == last && expr is ExprTimePeriod) + { + expiryTimeExp = expr; + var timePeriodExpr = (ExprTimePeriod)expiryTimeExp; + timeDeltaComputation = + timePeriodExpr.ConstEvaluator(new ExprEvaluatorContextStatement(context, false)); + } + else if (expr.IsConstantResult) + { + if (count == last) + { + var evaluateParams = new EvaluateParams(null, true, evaluatorContext); + var value = expr.ExprEvaluator.Evaluate(evaluateParams); + if (!(value.IsNumber())) + { + throw new ExprValidationException( + "Invalid parameter for every-distinct, expected number of seconds constant (constant not considered for distinct)"); + } + + var secondsExpire = expr.ExprEvaluator.Evaluate(evaluateParams); + + long? timeExpire; + if (secondsExpire == null) + { + timeExpire = null; + } + else + { + timeExpire = context.TimeAbacus.DeltaForSecondsNumber(secondsExpire); + } + + if (timeExpire != null && timeExpire > 0) + { + timeDeltaComputation = new ExprTimePeriodEvalDeltaConstGivenDelta(timeExpire.Value); + expiryTimeExp = expr; + } + else + { + Log.Warn("Invalid seconds-expire " + timeExpire + " for " + ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(expr)); + } + } + else + { + Log.Warn( + "Every-distinct node utilizes an expression returning a constant value, please check expression '{0}', not adding expression to distinct-value expression list", + expr.ToExpressionStringMinPrecedenceSafe()); + } + } + else + { + distinctExpressions.Add(expr); + } + } + if (distinctExpressions.IsEmpty()) + { + throw new ExprValidationException( + "Every-distinct node requires one or more distinct-value expressions that each return non-constant result values"); + } + distinctNode.SetDistinctExpressions(distinctExpressions, timeDeltaComputation, expiryTimeExp); + } + else if (evalNode is EvalMatchUntilFactoryNode) + { + var matchUntilNode = (EvalMatchUntilFactoryNode)evalNode; + + // compile bounds expressions, if any + var untilMatchEventSpec = new MatchEventSpec(tags.TaggedEventTypes, tags.ArrayEventTypes); + var streamTypeService = GetStreamTypeService( + context.EngineURI, context.StatementId, context.EventAdapterService, + untilMatchEventSpec.TaggedEventTypes, untilMatchEventSpec.ArrayEventTypes, subexpressionIdStack, + "until", context); + var validationContext = new ExprValidationContext( + streamTypeService, + context.EngineImportService, + context.StatementExtensionServicesContext, null, + context.SchedulingService, + context.VariableService, + context.TableService, evaluatorContext, + context.EventAdapterService, + context.StatementName, + context.StatementId, + context.Annotations, + context.ContextDescriptor, + context.ScriptingService, false, false, false, false, null, false); + + var lower = ValidateBounds(matchUntilNode.LowerBounds, validationContext); + matchUntilNode.LowerBounds = lower; + + var upper = ValidateBounds(matchUntilNode.UpperBounds, validationContext); + matchUntilNode.UpperBounds = upper; + + var single = ValidateBounds(matchUntilNode.SingleBound, validationContext); + matchUntilNode.SingleBound = single; + + var convertor = new MatchedEventConvertorImpl( + untilMatchEventSpec.TaggedEventTypes, untilMatchEventSpec.ArrayEventTypes, allTagNamesOrdered, + context.EventAdapterService); + matchUntilNode.Convertor = convertor; + + // compile new tag lists + ISet arrayTags = null; + var matchUntilAnalysisResult = EvalNodeUtil.RecursiveAnalyzeChildNodes(matchUntilNode.ChildNodes[0]); + foreach (var filterNode in matchUntilAnalysisResult.FilterNodes) + { + var optionalTag = filterNode.EventAsName; + if (optionalTag != null) + { + if (arrayTags == null) + { + arrayTags = new HashSet(); + } + arrayTags.Add(optionalTag); + } + } + + if (arrayTags != null) + { + foreach (var arrayTag in arrayTags) + { + if (!tags.ArrayEventTypes.ContainsKey(arrayTag)) + { + tags.ArrayEventTypes.Put(arrayTag, tags.TaggedEventTypes.Get(arrayTag)); + tags.TaggedEventTypes.Remove(arrayTag); + } + } + } + matchUntilNode.TagsArrayed = GetIndexesForTags(allTagNamesOrdered, arrayTags); + } + else if (evalNode is EvalFollowedByFactoryNode) + { + var followedByNode = (EvalFollowedByFactoryNode) evalNode; + StreamTypeService streamTypeService = new StreamTypeServiceImpl(context.EngineURI, false); + var validationContext = new ExprValidationContext( + streamTypeService, + context.EngineImportService, + context.StatementExtensionServicesContext, null, + context.SchedulingService, + context.VariableService, + context.TableService, + evaluatorContext, + context.EventAdapterService, + context.StatementName, + context.StatementId, + context.Annotations, + context.ContextDescriptor, + context.ScriptingService, false, false, false, false, null, false); + + if (followedByNode.OptionalMaxExpressions != null) + { + IList validated = new List(); + foreach (var maxExpr in followedByNode.OptionalMaxExpressions) + { + if (maxExpr == null) + { + validated.Add(null); + } + else + { + var visitor = new ExprNodeSummaryVisitor(); + maxExpr.Accept(visitor); + if (!visitor.IsPlain) + { + var errorMessage = "Invalid maximum expression in followed-by, " + visitor.GetMessage() + + " are not allowed within the expression"; + Log.Error(errorMessage); + throw new ExprValidationException(errorMessage); + } + + var validatedExpr = ExprNodeUtility.GetValidatedSubtree( + ExprNodeOrigin.FOLLOWEDBYMAX, maxExpr, validationContext); + validated.Add(validatedExpr); + if ((validatedExpr.ExprEvaluator.ReturnType == null) || + (!validatedExpr.ExprEvaluator.ReturnType.IsNumeric())) + { + var message = "Invalid maximum expression in followed-by, the expression must return an integer value"; + throw new ExprValidationException(message); + } + } + } + followedByNode.OptionalMaxExpressions = validated; + } + } + + if (newTaggedEventTypes != null) + { + tags.TaggedEventTypes.PutAll(newTaggedEventTypes); + } + if (newArrayEventTypes != null) + { + tags.ArrayEventTypes.PutAll(newArrayEventTypes); + } + } + + private static ExprNode ValidateBounds(ExprNode bounds, ExprValidationContext validationContext) + { + var message = "Match-until bounds value expressions must return a numeric value"; + if (bounds != null) + { + var validated = ExprNodeUtility.GetValidatedSubtree( + ExprNodeOrigin.PATTERNMATCHUNTILBOUNDS, bounds, validationContext); + if ((validated.ExprEvaluator.ReturnType == null) || + (!validated.ExprEvaluator.ReturnType.IsNumeric())) + { + throw new ExprValidationException(message); + } + return validated; + } + return null; + } + + private static int[] GetIndexesForTags(ICollection allTagNamesOrdered, ICollection arrayTags) + { + if (arrayTags == null || arrayTags.IsEmpty()) + { + return new int[0]; + } + var indexes = new int[arrayTags.Count]; + var count = 0; + foreach (var arrayTag in arrayTags) + { + var index = 0; + var found = FindTagNumber(arrayTag, allTagNamesOrdered); + indexes[count] = found; + count++; + } + return indexes; + } + + private static int FindTagNumber(string findTag, IEnumerable allTagNamesOrdered) + { + var index = 0; + foreach (var tag in allTagNamesOrdered) + { + if (findTag.Equals(tag)) + { + return index; + } + index++; + } + throw new EPException("Failed to find tag '" + findTag + "' among known tags"); + } + + private static bool IsParentMatchUntil(EvalFactoryNode currentNode, Stack parentNodeStack) + { + if (parentNodeStack.Count == 0) + { + return false; + } + + foreach (var deepParent in parentNodeStack.OfType()) + { + var matchUntilFactoryNode = deepParent; + if (matchUntilFactoryNode.ChildNodes[0] == currentNode) + { + return true; + } + } + return false; + } + + private static IList ValidateExpressions( + ExprNodeOrigin exprNodeOrigin, + IList objectParameters, + ExprValidationContext validationContext) + { + if (objectParameters == null) + { + return objectParameters; + } + IList validated = new List(); + foreach (var node in objectParameters) + { + validated.Add(ExprNodeUtility.GetValidatedSubtree(exprNodeOrigin, node, validationContext)); + } + return validated; + } + + private static StreamTypeService GetStreamTypeService( + string engineURI, + int statementId, + EventAdapterService eventAdapterService, + IDictionary> taggedEventTypes, + IDictionary> arrayEventTypes, + IEnumerable subexpressionIdStack, + string objectType, + StatementContext statementContext) + { + var filterTypes = new LinkedHashMap>(); + filterTypes.PutAll(taggedEventTypes); + + // handle array tags (match-until clause) + if (arrayEventTypes != null) + { + var patternSubexEventType = GetPatternSubexEventType(statementId, objectType, subexpressionIdStack); + var arrayTagCompositeEventType = eventAdapterService.CreateSemiAnonymousMapType( + patternSubexEventType, new Dictionary>(), arrayEventTypes, false); + statementContext.StatementSemiAnonymousTypeRegistry.Register(arrayTagCompositeEventType); + foreach (var entry in arrayEventTypes) + { + var tag = entry.Key; + if (!filterTypes.ContainsKey(tag)) + { + var pair = new Pair(arrayTagCompositeEventType, tag); + filterTypes.Put(tag, pair); + } + } + } + + return new StreamTypeServiceImpl(filterTypes, engineURI, true, false); + } + + private static string GetPatternSubexEventType( + int statementId, + string objectType, + IEnumerable subexpressionIdStack) + { + var writer = new StringWriter(); + writer.Write(statementId); + writer.Write("_"); + writer.Write(objectType); + foreach (var num in subexpressionIdStack) + { + writer.Write("_"); + writer.Write(num); + } + return writer.ToString(); + } + + private static EvalFactoryNode RecursiveAddAuditNode( + PatternNodeFactory patternNodeFactory, + EvalFactoryNode parentNode, + bool auditPattern, + bool auditPatternInstance, + EvalFactoryNode evalNode, + EvalAuditInstanceCount instanceCount) + { + var writer = new StringWriter(); + evalNode.ToEPL(writer, PatternExpressionPrecedenceEnum.MINIMUM); + var expressionText = writer.ToString(); + var filterChildNonQuitting = parentNode != null && parentNode.IsFilterChildNonQuitting; + EvalFactoryNode audit = patternNodeFactory.MakeAuditNode( + auditPattern, auditPatternInstance, expressionText, instanceCount, filterChildNonQuitting); + audit.AddChildNode(evalNode); + + IList newChildNodes = new List(); + foreach (var child in evalNode.ChildNodes) + { + newChildNodes.Add( + RecursiveAddAuditNode( + patternNodeFactory, evalNode, auditPattern, auditPatternInstance, child, instanceCount)); + } + + evalNode.ChildNodes.Clear(); + evalNode.AddChildNodes(newChildNodes); + + return audit; + } + + private static MatchEventSpec AnalyzeMatchEvent(EvalFactoryNode relativeNode) + { + var taggedEventTypes = new LinkedHashMap>(); + var arrayEventTypes = new LinkedHashMap>(); + + // Determine all the filter nodes used in the pattern + var evalNodeAnalysisResult = EvalNodeUtil.RecursiveAnalyzeChildNodes(relativeNode); + + // collect all filters underneath + foreach (var filterNode in evalNodeAnalysisResult.FilterNodes) + { + var optionalTag = filterNode.EventAsName; + if (optionalTag != null) + { + taggedEventTypes.Put( + optionalTag, + new Pair( + filterNode.FilterSpec.FilterForEventType, filterNode.FilterSpec.FilterForEventTypeName)); + } + } + + // collect those filters under a repeat since they are arrays + var arrayTags = new HashSet(); + foreach (var matchUntilNode in evalNodeAnalysisResult.RepeatNodes) + { + var matchUntilAnalysisResult = EvalNodeUtil.RecursiveAnalyzeChildNodes(matchUntilNode.ChildNodes[0]); + foreach (var filterNode in matchUntilAnalysisResult.FilterNodes) + { + var optionalTag = filterNode.EventAsName; + if (optionalTag != null) + { + arrayTags.Add(optionalTag); + } + } + } + + // for each array tag change collection + foreach (var arrayTag in arrayTags) + { + if (taggedEventTypes.Get(arrayTag) != null) + { + arrayEventTypes.Put(arrayTag, taggedEventTypes.Get(arrayTag)); + taggedEventTypes.Remove(arrayTag); + } + } + + return new MatchEventSpec(taggedEventTypes, arrayEventTypes); + } + + public bool IsSuppressSameEventMatches + { + get { return _suppressSameEventMatches; } + } + + public bool IsDiscardPartialsOnMatch + { + get { return _discardPartialsOnMatch; } + } + + public class FilterForFilterFactoryNodes : EvalNodeUtilFactoryFilter + { + public static readonly FilterForFilterFactoryNodes INSTANCE = new FilterForFilterFactoryNodes(); + + public bool Consider(EvalFactoryNode node) + { + return node is EvalFilterFactoryNode; + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/spec/PluggableObjectCollection.cs b/NEsper.Core/NEsper.Core/epl/spec/PluggableObjectCollection.cs new file mode 100755 index 000000000..e30445ce1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/PluggableObjectCollection.cs @@ -0,0 +1,222 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.core; + +namespace com.espertech.esper.epl.spec +{ + /// + /// Repository for pluggable objects of different types that follow a "namespace:name" notation. + /// + public class PluggableObjectCollection + { + /// + /// The underlying nested map of namespace keys and name-to-object maps. + /// + /// pluggable object collected + public IDictionary>> Pluggables { get; private set; } + + /// Ctor. + public PluggableObjectCollection() + { + Pluggables = new Dictionary>>(); + } + + /// + /// Add a plug-in view. + /// + /// is a list of configured plug-in view objects. + /// The configuration plug in virtual DW. + /// ConfigurationException if the configured views don't resolve + public void AddViews( + IList configurationPlugInViews, + IList configurationPlugInVirtualDW, + EngineImportService engineImportService) + { + InitViews(configurationPlugInViews, engineImportService); + InitVirtualDW(configurationPlugInVirtualDW, engineImportService); + } + + /// + /// Add a plug-in pattern object. + /// + /// is a list of configured plug-in pattern objects. + /// The engine import service. + /// ConfigurationException if the configured patterns don't resolve + public void AddPatternObjects( + IList configPattern, + EngineImportService engineImportService) + { + InitPatterns(configPattern, engineImportService); + } + + /// Add the plug-in objects for another collection. + /// is the collection to add + public void AddObjects(PluggableObjectCollection other) + { + foreach (KeyValuePair>> entry in other.Pluggables) + { + var namespaceMap = Pluggables.Get(entry.Key); + if (namespaceMap == null) + { + namespaceMap = new Dictionary>(); + Pluggables.Put(entry.Key, namespaceMap); + } + + foreach (var name in entry.Value.Keys) + { + if (namespaceMap.ContainsKey(name)) + { + throw new ConfigurationException("Duplicate object detected in namespace '" + entry.Key + + "' by name '" + name + "'"); + } + } + + namespaceMap.PutAll(entry.Value); + } + } + + /// Add a single object to the collection. + /// is the object's namespace + /// is the object's name + /// is the class the object resolves to + /// is the object type + public void AddObject(String @namespace, String name, Type clazz, PluggableObjectType type) + { + AddObject(@namespace, name, clazz, type, null); + } + + /// + /// Add a single object to the collection also adding additional configuration. + /// + /// is the object's namespace + /// is the object's name + /// is the class the object resolves to + /// is the object type + /// The configuration. + public void AddObject(String @namespace, String name, Type clazz, PluggableObjectType type, Object configuration) + { + var namespaceMap = Pluggables.Get(@namespace); + if (namespaceMap == null) + { + namespaceMap = new Dictionary>(); + Pluggables.Put(@namespace, namespaceMap); + } + namespaceMap.Put(name, new Pair(clazz, new PluggableObjectEntry(type, configuration))); + } + + private void InitViews(IEnumerable configurationPlugInViews, EngineImportService engineImportService) + { + if (configurationPlugInViews == null) + { + return; + } + + foreach (var entry in configurationPlugInViews) + { + HandleAddPluggableObject(entry.FactoryClassName, entry.Namespace, entry.Name, PluggableObjectType.VIEW, null, engineImportService); + } + } + + private void InitVirtualDW( + IEnumerable configurationPlugInVirtualDataWindows, + EngineImportService engineImportService) + { + if (configurationPlugInVirtualDataWindows == null) + { + return; + } + + foreach (var entry in configurationPlugInVirtualDataWindows) + { + HandleAddPluggableObject(entry.FactoryClassName, entry.Namespace, entry.Name, PluggableObjectType.VIRTUALDW, entry.Config, engineImportService); + } + } + + private void HandleAddPluggableObject( + String factoryClassName, + String @namespace, + String name, + PluggableObjectType type, + Object optionalCustomConfig, + EngineImportService engineImportService) + { + + if (factoryClassName == null) + { + throw new ConfigurationException("Factory class name has not been supplied for object '" + name + "'"); + } + if (@namespace == null) + { + throw new ConfigurationException("Namespace name has not been supplied for object '" + name + "'"); + } + if (name == null) + { + throw new ConfigurationException("Name has not been supplied for object in namespace '" + @namespace + "'"); + } + + try + { + var clazz = engineImportService.GetClassForNameProvider().ClassForName(factoryClassName); + + var namespaceMap = Pluggables.Get(@namespace); + if (namespaceMap == null) + { + namespaceMap = new Dictionary>(); + Pluggables.Put(@namespace, namespaceMap); + } + namespaceMap.Put( + name, + new Pair(clazz, new PluggableObjectEntry(type, optionalCustomConfig))); + } + catch (TypeLoadException e) + { + throw new ConfigurationException("View factory class " + factoryClassName + " could not be loaded", e); + } + } + + private void InitPatterns(IEnumerable configEntries, EngineImportService engineImportService) + { + if (configEntries == null) + { + return; + } + + foreach (var entry in configEntries) + { + if (entry.PatternObjectType == null) + { + throw new ConfigurationException("Pattern object type has not been supplied for object '" + entry.Name + "'"); + } + + PluggableObjectType typeEnum; + if (entry.PatternObjectType == ConfigurationPlugInPatternObject.PatternObjectTypeEnum.GUARD) + { + typeEnum = PluggableObjectType.PATTERN_GUARD; + } + else if (entry.PatternObjectType == ConfigurationPlugInPatternObject.PatternObjectTypeEnum.OBSERVER) + { + typeEnum = PluggableObjectType.PATTERN_OBSERVER; + } + else + { + throw new ArgumentException("Pattern object type '" + entry.PatternObjectType + "' not known"); + } + + HandleAddPluggableObject(entry.FactoryClassName, entry.Namespace, entry.Name, typeEnum, null, engineImportService); + } + } + + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/PluggableObjectEntry.cs b/NEsper.Core/NEsper.Core/epl/spec/PluggableObjectEntry.cs new file mode 100755 index 000000000..cbcb6478b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/PluggableObjectEntry.cs @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.spec +{ + public class PluggableObjectEntry + { + public PluggableObjectType PluggableType { get; private set; } + + public object CustomConfigs { get; private set; } + + public PluggableObjectEntry(PluggableObjectType type, Object customConfigs) + { + PluggableType = type; + CustomConfigs = customConfigs; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/PluggableObjectRegistry.cs b/NEsper.Core/NEsper.Core/epl/spec/PluggableObjectRegistry.cs new file mode 100755 index 000000000..249af66f7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/PluggableObjectRegistry.cs @@ -0,0 +1,19 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.collection; + +namespace com.espertech.esper.epl.spec +{ + public interface PluggableObjectRegistry + { + Pair Lookup(String nameSpace, String name); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/PluggableObjectRegistryImpl.cs b/NEsper.Core/NEsper.Core/epl/spec/PluggableObjectRegistryImpl.cs new file mode 100755 index 000000000..6c2ee302e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/PluggableObjectRegistryImpl.cs @@ -0,0 +1,73 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.view; + +namespace com.espertech.esper.epl.spec +{ + public class PluggableObjectRegistryImpl : PluggableObjectRegistry + { + private readonly PluggableObjectCollection[] _collections; + + public PluggableObjectRegistryImpl(PluggableObjectCollection[] collections) + { + _collections = collections; + } + + public Pair Lookup(string nameSpace, string name) + { + // Handle namespace-provided + if (nameSpace != null) { + for (int i = 0; i < _collections.Length; i++) { + IDictionary> names = _collections[i].Pluggables.Get(nameSpace); + if (names == null) { + continue; + } + Pair entry = names.Get(name); + if (entry == null) { + continue; + } + return entry; + } + return null; + } + + // Handle namespace-not-provided + ISet entriesDuplicate = null; + KeyValuePair>? found = null; + for (int i = 0; i < _collections.Length; i++) { + foreach (var collEntry in _collections[i].Pluggables) { + foreach (var viewEntry in collEntry.Value) { + if (viewEntry.Key.Equals(name)) { + if (found != null) { + if (entriesDuplicate == null) { + entriesDuplicate = new HashSet(); + } + entriesDuplicate.Add(viewEntry.Key); + } else { + found = viewEntry; + } + } + } + } + } + + if (entriesDuplicate != null) { + entriesDuplicate.Add(found.Value.Key); + throw new ViewProcessingException("Duplicate entries for view '" + name + "' found in namespaces " + entriesDuplicate.Render()); + } + + return found == null ? null : found.Value.Value; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/spec/PluggableObjectType.cs b/NEsper.Core/NEsper.Core/epl/spec/PluggableObjectType.cs new file mode 100755 index 000000000..03c532450 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/PluggableObjectType.cs @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.spec +{ + /// Enumeration for types of plug-in objects. + public enum PluggableObjectType + { + /// Pattern guard object type. + PATTERN_GUARD, + + /// Pattern observer object type. + PATTERN_OBSERVER, + + /// View object type. + VIEW, + + /// Virtual data window object type. + VIRTUALDW + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/PropertyEvalAtom.cs b/NEsper.Core/NEsper.Core/epl/spec/PropertyEvalAtom.cs new file mode 100755 index 000000000..46f9f3eba --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/PropertyEvalAtom.cs @@ -0,0 +1,55 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.spec +{ + /// + /// Atom in a specification for property evaluation. + /// + [Serializable] + public class PropertyEvalAtom : MetaDefItem + { + /// + /// Ctor. + /// + /// The splitter expression. + /// Type of the optional result event. + /// column name assigned, if any + /// select clause, if any + /// where clause, if any + public PropertyEvalAtom(ExprNode splitterExpression, String optionalResultEventType, String optionalAsName, SelectClauseSpecRaw optionalSelectClause, ExprNode optionalWhereClause) + { + SplitterExpression = splitterExpression; + OptionalResultEventType = optionalResultEventType; + OptionalAsName = optionalAsName; + OptionalSelectClause = optionalSelectClause; + OptionalWhereClause = optionalWhereClause; + } + + /// Returns the column name if assigned. + /// column name + public string OptionalAsName { get; private set; } + + /// Returns the select clause if specified. + /// select clause + public SelectClauseSpecRaw OptionalSelectClause { get; private set; } + + /// Returns the where clause, if specified. + /// filter expression + public ExprNode OptionalWhereClause { get; private set; } + + public ExprNode SplitterExpression { get; private set; } + + public string OptionalResultEventType { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/PropertyEvalSpec.cs b/NEsper.Core/NEsper.Core/epl/spec/PropertyEvalSpec.cs new file mode 100755 index 000000000..dbb51e986 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/PropertyEvalSpec.cs @@ -0,0 +1,43 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.spec +{ + /// + /// Specification for property evaluation. + /// + [Serializable] + public class PropertyEvalSpec : MetaDefItem + { + /// + /// Return a list of atoms. + /// + /// atoms + public IList Atoms { get; private set; } + + /// + /// Ctor. + /// + public PropertyEvalSpec() + { + Atoms = new List(); + } + + /// Add an atom. + /// to add + public void Add(PropertyEvalAtom atom) + { + Atoms.Add(atom); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/RowLimitSpec.cs b/NEsper.Core/NEsper.Core/epl/spec/RowLimitSpec.cs new file mode 100755 index 000000000..afbd56a97 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/RowLimitSpec.cs @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.spec +{ + /// Spec for defining a row limit. + [Serializable] + public class RowLimitSpec : MetaDefItem + { + /// Ctor. + /// max num rows constant, null if using variable + /// offset or null + /// max num rows variable, null if using constant + /// offset variable or null + public RowLimitSpec(int? numRows, + int? optionalOffset, + String numRowsVariable, + String optionalOffsetVariable) + { + NumRows = numRows; + OptionalOffset = optionalOffset; + NumRowsVariable = numRowsVariable; + OptionalOffsetVariable = optionalOffsetVariable; + } + + /// Returns max num rows constant or null if using variable. + /// limit + public int? NumRows { get; private set; } + + /// Returns offset constant or null. + /// offset + public int? OptionalOffset { get; private set; } + + /// Returns max num rows variable or null if using constant. + /// limit + public string NumRowsVariable { get; private set; } + + /// Returns offset variable or null + /// offset variable + public string OptionalOffsetVariable { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/spec/SelectClauseElementCompiled.cs b/NEsper.Core/NEsper.Core/epl/spec/SelectClauseElementCompiled.cs new file mode 100755 index 000000000..494027536 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/SelectClauseElementCompiled.cs @@ -0,0 +1,15 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.spec +{ + /// Marker interface for elements in a select clause that is in the compiled form. + public interface SelectClauseElementCompiled + { + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/SelectClauseElementRaw.cs b/NEsper.Core/NEsper.Core/epl/spec/SelectClauseElementRaw.cs new file mode 100755 index 000000000..4df5b7c7d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/SelectClauseElementRaw.cs @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.spec +{ + /// + /// Marker interface for elements in a select clause that is in the raw (storable) form. + /// + public interface SelectClauseElementRaw : MetaDefItem + { + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/SelectClauseElementWildcard.cs b/NEsper.Core/NEsper.Core/epl/spec/SelectClauseElementWildcard.cs new file mode 100755 index 000000000..ab472b9dd --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/SelectClauseElementWildcard.cs @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.spec +{ + /// + /// Represents a wildcard as a select clause element. + /// + [Serializable] + public class SelectClauseElementWildcard : SelectClauseElementRaw, SelectClauseElementCompiled + { + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/SelectClauseExprCompiledSpec.cs b/NEsper.Core/NEsper.Core/epl/spec/SelectClauseExprCompiledSpec.cs new file mode 100755 index 000000000..32b081ffb --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/SelectClauseExprCompiledSpec.cs @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.spec +{ + /// + /// Represents a single item in a SELECT-clause, with a name assigned either by the + /// engine or by the user specifying an "as" tag name. + /// + [Serializable] + public class SelectClauseExprCompiledSpec : SelectClauseElementCompiled + { + /// + /// Ctor. + /// + /// the expression node to evaluate for matching events + /// cannot be null as a name is always assigned orsystem-determined + /// Name of the provided. + /// if set to true [is events]. + public SelectClauseExprCompiledSpec( + ExprNode selectExpression, + String assignedName, + String providedName, + bool isEvents) + { + SelectExpression = selectExpression; + AssignedName = assignedName; + ProvidedName = providedName; + IsEvents = isEvents; + } + + /// Returns the expression node representing the item in the select clause. + /// expression node for item + public ExprNode SelectExpression { get; set; } + + /// Returns the name of the item in the select clause. + /// name of item + public string AssignedName { get; set; } + + public string ProvidedName { get; private set; } + + public bool IsEvents { get; set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/spec/SelectClauseExprRawSpec.cs b/NEsper.Core/NEsper.Core/epl/spec/SelectClauseExprRawSpec.cs new file mode 100755 index 000000000..58257b030 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/SelectClauseExprRawSpec.cs @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.parse; + +namespace com.espertech.esper.epl.spec +{ + /// + /// Represents a single item in a SELECT-clause, potentially unnamed as no "as" tag may + /// have been supplied in the syntax. + /// + /// Compare to which carries a determined name. + /// + [Serializable] + public class SelectClauseExprRawSpec : SelectClauseElementRaw + { + /// + /// Ctor. + /// + /// the expression node to evaluate for matching events + /// the name of the item, null if not name supplied + /// if set to true [is events]. + public SelectClauseExprRawSpec(ExprNode selectExpression, String optionalAsName, bool isEvents) + { + SelectExpression = selectExpression; + OptionalAsName = optionalAsName == null ? null : ASTConstantHelper.RemoveTicks(optionalAsName); + IsEvents = isEvents; + } + + /// Returns the expression node representing the item in the select clause. + /// expression node for item + public ExprNode SelectExpression { get; private set; } + + /// Returns the name of the item in the select clause. + /// name of item + public string OptionalAsName { get; private set; } + + public bool IsEvents { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/SelectClauseSpecCompiled.cs b/NEsper.Core/NEsper.Core/epl/spec/SelectClauseSpecCompiled.cs new file mode 100755 index 000000000..be981ee90 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/SelectClauseSpecCompiled.cs @@ -0,0 +1,67 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Linq; + +namespace com.espertech.esper.epl.spec +{ + /// + /// Encapsulates the parsed select expressions in a select-clause in an EPL statement. + /// + public class SelectClauseSpecCompiled + { + private readonly static SelectClauseElementCompiled[] EMPTY = new SelectClauseElementCompiled[0]; + + private readonly bool _isDistinct; + private SelectClauseElementCompiled[] _selectClauseElements; + + /// Ctor. + /// indicates distinct or not + public SelectClauseSpecCompiled(bool isDistinct) + { + _selectClauseElements = EMPTY; + _isDistinct = isDistinct; + } + + /// Ctor. + /// for a populates list of select expressions + /// indicates distinct or not + public SelectClauseSpecCompiled(SelectClauseElementCompiled[] selectList, bool isDistinct) + { + _selectClauseElements = selectList; + _isDistinct = isDistinct; + } + + public void SetSelectExprList(params SelectClauseElementWildcard[] selectClauseElements) + { + _selectClauseElements = selectClauseElements; + } + + /// Returns the list of select expressions. + /// list of expressions + public SelectClauseElementCompiled[] SelectExprList + { + get { return _selectClauseElements; } + set { _selectClauseElements = value; } + } + + /// Returns true if the select clause contains at least one wildcard. + /// true if clause contains wildcard, false if not + public bool IsUsingWildcard + { + get { return _selectClauseElements.OfType().Any(); } + } + + /// Returns indictor whether distinct or not. + /// distinct indicator + public bool IsDistinct + { + get { return _isDistinct; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/SelectClauseSpecRaw.cs b/NEsper.Core/NEsper.Core/epl/spec/SelectClauseSpecRaw.cs new file mode 100755 index 000000000..b91d91274 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/SelectClauseSpecRaw.cs @@ -0,0 +1,96 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.spec +{ + /// + /// Encapsulates the parsed select expressions in a select-clause in an EPL statement. + /// + [Serializable] + public class SelectClauseSpecRaw : MetaDefItem + { + private readonly List _selectClauseElements; + + /// + /// Ctor. + /// + public SelectClauseSpecRaw() + { + _selectClauseElements = new List(); + IsDistinct = false; + } + + /// + /// Adds an select expression within the select clause. + /// + /// is the expression to add + public void Add(SelectClauseElementRaw element) + { + _selectClauseElements.Add(element); + } + + /// + /// Adds select expressions within the select clause. + /// + /// is the expressions to add + public void AddAll(IEnumerable elements) + { + _selectClauseElements.AddRange(elements); + } + + /// + /// Gets a value indicating whether this instance is only wildcard. + /// + /// + /// true if this instance is only wildcard; otherwise, false. + /// + public bool IsOnlyWildcard { + get + { + return + (_selectClauseElements.Count == 1) && + (_selectClauseElements[0] is SelectClauseElementWildcard); + } + } + + /// + /// Gets or sets a value indicating whether this instance is distinct. + /// + /// + /// true if this instance is distinct; otherwise, false. + /// + public bool IsDistinct { get; set; } + + /// Returns the list of select expressions. + /// list of expressions + public IList SelectExprList + { + get { return _selectClauseElements; } + } + + /// Returns true if the select clause contains at least one wildcard. + /// true if clause contains wildcard, false if not + public bool IsUsingWildcard + { + get + { + foreach (SelectClauseElementRaw element in _selectClauseElements) { + if (element is SelectClauseElementWildcard) { + return true; + } + } + return false; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/SelectClauseStreamCompiledSpec.cs b/NEsper.Core/NEsper.Core/epl/spec/SelectClauseStreamCompiledSpec.cs new file mode 100755 index 000000000..b7745f515 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/SelectClauseStreamCompiledSpec.cs @@ -0,0 +1,124 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using com.espertech.esper.compat; +using com.espertech.esper.epl.table.mgmt; + +namespace com.espertech.esper.epl.spec +{ + /// + /// Mirror class to but added the stream + /// number for the name. + /// + public class SelectClauseStreamCompiledSpec : SelectClauseElementCompiled + { + private int streamNumber = -1; + private bool isFragmentEvent = false; + + /// + /// Ctor. + /// + /// is the stream name of the stream to select + /// is the column name + public SelectClauseStreamCompiledSpec(String streamName, String optionalColumnName) + { + IsProperty = false; + StreamName = streamName; + OptionalName = optionalColumnName; + } + + /// + /// Returns the stream name (e.g. select streamName from MyEvent as streamName). + /// + /// + /// name + /// + public string StreamName { get; private set; } + + /// + /// Returns the column name. + /// + /// + /// name + /// + public string OptionalName { get; private set; } + + /// + /// Returns the stream number of the stream for the stream name. + /// + /// + /// stream number + /// + public int StreamNumber + { + get + { + if (streamNumber == -1) { + throw new IllegalStateException("Not initialized for stream number and tagged event"); + } + return streamNumber; + } + set { streamNumber = value; } + } + + /// + /// Returns true to indicate that we are meaning to select a tagged event in a + /// pattern, or false if selecting an event from a stream. + /// + /// + /// true for tagged event in pattern, false for stream + /// + public bool IsFragmentEvent + { + get + { + if (streamNumber == -1) { + throw new IllegalStateException("Not initialized for stream number and tagged event"); + } + return isFragmentEvent; + } + set { isFragmentEvent = value; } + } + + /// + /// Sets an indicate that a property was selected with wildcard. + /// + /// selected + /// the return type + public void SetProperty(bool property, Type propertyType) + { + this.IsProperty = property; + this.PropertyType = propertyType; + } + + /// + /// True if selecting from a property, false if not + /// + /// + /// indicator whether property or not + /// + public bool IsProperty { get; set; } + + /// + /// Returns property type. + /// + /// + /// property type + /// + public Type PropertyType { get; set; } + + /// + /// Gets or sets the table metadata. + /// + /// + /// The table metadata. + /// + public TableMetadata TableMetadata { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/SelectClauseStreamRawSpec.cs b/NEsper.Core/NEsper.Core/epl/spec/SelectClauseStreamRawSpec.cs new file mode 100755 index 000000000..4a10dd92f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/SelectClauseStreamRawSpec.cs @@ -0,0 +1,47 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.spec +{ + /// + /// For use in select clauses for specifying a selected stream: + /// select a.* from MyEvent as a, MyOther as b + /// + public class SelectClauseStreamRawSpec : SelectClauseElementRaw + { + private readonly string streamName; + private readonly string optionalAsName; + + /// Ctor. + /// is the stream name of the stream to select + /// is the column name + public SelectClauseStreamRawSpec(string streamName, string optionalAsName) + { + this.streamName = streamName; + this.optionalAsName = optionalAsName; + } + + /// + /// Returns the stream name (e.g. select streamName from MyEvent as streamName). + /// + /// The name of the stream. + public string StreamName + { + get { return streamName; } + } + + /// + /// Returns the column alias (e.g. select streamName as mycol from MyEvent as streamName). + /// + /// The name of the optional as. + public string OptionalAsName + { + get { return optionalAsName; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/SelectClauseStreamSelectorEnum.cs b/NEsper.Core/NEsper.Core/epl/spec/SelectClauseStreamSelectorEnum.cs new file mode 100755 index 000000000..5fdf6319d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/SelectClauseStreamSelectorEnum.cs @@ -0,0 +1,78 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; + +using com.espertech.esper.client.soda; + +namespace com.espertech.esper.epl.spec +{ + /// + /// Enumeration for representing select-clause selection of the remove stream or the insert stream, or both. + /// + + public enum SelectClauseStreamSelectorEnum + { + /// Indicates selection of the remove stream only. + RSTREAM_ONLY, + /// Indicates selection of the insert stream only. + ISTREAM_ONLY, + /// Indicates selection of both the insert and the remove stream. + RSTREAM_ISTREAM_BOTH + } + + public static class SelectClauseStreamSelectorEnumExtensions + { + public static bool IsSelectsRStream(this SelectClauseStreamSelectorEnum selector) + { + return selector != SelectClauseStreamSelectorEnum.ISTREAM_ONLY; + } + + public static bool IsSelectsIStream(this SelectClauseStreamSelectorEnum selector) + { + return selector != SelectClauseStreamSelectorEnum.RSTREAM_ONLY; + } + + /// Maps the SODA-selector to the internal representation + /// is the SODA-selector to map + /// internal stream selector + public static SelectClauseStreamSelectorEnum MapFromSODA(this StreamSelector selector) + { + switch (selector) + { + case StreamSelector.ISTREAM_ONLY: + return SelectClauseStreamSelectorEnum.ISTREAM_ONLY; + case StreamSelector.RSTREAM_ONLY: + return SelectClauseStreamSelectorEnum.RSTREAM_ONLY; + case StreamSelector.RSTREAM_ISTREAM_BOTH: + return SelectClauseStreamSelectorEnum.RSTREAM_ISTREAM_BOTH; + default: + throw new ArgumentException("Invalid selector '" + selector + "' encountered"); + } + } + + /// Maps the internal stream selector to the SODA-representation + /// is the internal selector to map + /// SODA stream selector + public static StreamSelector MapFromSODA(this SelectClauseStreamSelectorEnum selector) + { + switch (selector) + { + case SelectClauseStreamSelectorEnum.ISTREAM_ONLY: + return StreamSelector.ISTREAM_ONLY; + case SelectClauseStreamSelectorEnum.RSTREAM_ONLY: + return StreamSelector.RSTREAM_ONLY; + case SelectClauseStreamSelectorEnum.RSTREAM_ISTREAM_BOTH: + return StreamSelector.RSTREAM_ISTREAM_BOTH; + default: + throw new ArgumentException("Invalid selector '" + selector + "' encountered"); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/StatementSpecCompiled.cs b/NEsper.Core/NEsper.Core/epl/spec/StatementSpecCompiled.cs new file mode 100755 index 000000000..ba587709a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/StatementSpecCompiled.cs @@ -0,0 +1,363 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.declexpr; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.subquery; +using com.espertech.esper.epl.expression.table; +using com.espertech.esper.filter; + +namespace com.espertech.esper.epl.spec +{ + /// + /// Specification object representing a complete EPL statement including all EPL constructs. + /// + public class StatementSpecCompiled + { + public static readonly StatementSpecCompiled DEFAULT_SELECT_ALL_EMPTY; + + static StatementSpecCompiled() + { + DEFAULT_SELECT_ALL_EMPTY = new StatementSpecCompiled(); + DEFAULT_SELECT_ALL_EMPTY.SelectClauseSpec.SetSelectExprList(new SelectClauseElementWildcard()); + } + + /// + /// Ctor. + /// + /// describes on-delete statements + /// describes create-window statements + /// when an index get + /// describes create-variable statements + /// The create table desc. + /// The create schema desc. + /// insert into def + /// stream selection + /// select clause + /// specs for streams + /// outer join def + /// where filter expr nodes + /// having expression + /// output limit + /// order by + /// list of subqueries + /// The declared expressions. + /// + /// variables referenced + /// row limit specification, or null if none supplied + /// event type names statically determined + /// statement annotations + /// update specification if used + /// if provided + /// For clause spec. + /// The SQL parameters. + /// The context desc. + /// Name of the optional context. + /// The create graph desc. + /// The create expression desc. + /// The fire and forget spec. + /// The group by expressions. + /// The into table spec. + /// The table nodes. + public StatementSpecCompiled( + OnTriggerDesc onTriggerDesc, + CreateWindowDesc createWindowDesc, + CreateIndexDesc createIndexDesc, + CreateVariableDesc createVariableDesc, + CreateTableDesc createTableDesc, + CreateSchemaDesc createSchemaDesc, + InsertIntoDesc insertIntoDesc, + SelectClauseStreamSelectorEnum selectClauseStreamSelectorEnum, + SelectClauseSpecCompiled selectClauseSpec, + StreamSpecCompiled[] streamSpecs, + OuterJoinDesc[] outerJoinDescList, + ExprNode filterExprRootNode, + ExprNode havingExprRootNode, + OutputLimitSpec outputLimitSpec, + OrderByItem[] orderByList, + ExprSubselectNode[] subSelectExpressions, + ExprDeclaredNode[] declaredExpressions, + ExpressionScriptProvided[] scripts, + ICollection variableReferences, + RowLimitSpec rowLimitSpec, + string[] eventTypeReferences, + Attribute[] annotations, + UpdateDesc updateSpec, + MatchRecognizeSpec matchRecognizeSpec, + ForClauseSpec forClauseSpec, + IDictionary> sqlParameters, + CreateContextDesc contextDesc, + string optionalContextName, + CreateDataFlowDesc createGraphDesc, + CreateExpressionDesc createExpressionDesc, + FireAndForgetSpec fireAndForgetSpec, + GroupByClauseExpressions groupByExpressions, + IntoTableSpec intoTableSpec, + ExprTableAccessNode[] tableNodes) + { + OnTriggerDesc = onTriggerDesc; + CreateWindowDesc = createWindowDesc; + CreateIndexDesc = createIndexDesc; + CreateVariableDesc = createVariableDesc; + CreateTableDesc = createTableDesc; + CreateSchemaDesc = createSchemaDesc; + InsertIntoDesc = insertIntoDesc; + SelectStreamDirEnum = selectClauseStreamSelectorEnum; + SelectClauseSpec = selectClauseSpec; + StreamSpecs = streamSpecs; + OuterJoinDescList = outerJoinDescList; + FilterExprRootNode = filterExprRootNode; + HavingExprRootNode = havingExprRootNode; + OutputLimitSpec = outputLimitSpec; + OrderByList = orderByList; + SubSelectExpressions = subSelectExpressions; + DeclaredExpressions = declaredExpressions; + Scripts = scripts; + VariableReferences = variableReferences; + RowLimitSpec = rowLimitSpec; + EventTypeReferences = eventTypeReferences; + Annotations = annotations; + UpdateSpec = updateSpec; + MatchRecognizeSpec = matchRecognizeSpec; + ForClauseSpec = forClauseSpec; + SqlParameters = sqlParameters; + ContextDesc = contextDesc; + OptionalContextName = optionalContextName; + CreateGraphDesc = createGraphDesc; + CreateExpressionDesc = createExpressionDesc; + FireAndForgetSpec = fireAndForgetSpec; + GroupByExpressions = groupByExpressions; + IntoTableSpec = intoTableSpec; + TableNodes = tableNodes; + } + + /// + /// Ctor. + /// + public StatementSpecCompiled() + { + OnTriggerDesc = null; + CreateWindowDesc = null; + CreateIndexDesc = null; + CreateVariableDesc = null; + CreateTableDesc = null; + CreateSchemaDesc = null; + InsertIntoDesc = null; + SelectStreamDirEnum = SelectClauseStreamSelectorEnum.RSTREAM_ISTREAM_BOTH; + SelectClauseSpec = new SelectClauseSpecCompiled(false); + StreamSpecs = StreamSpecCompiledConstants.EMPTY_STREAM_ARRAY; + OuterJoinDescList = OuterJoinDesc.EMPTY_OUTERJOIN_ARRAY; + FilterExprRootNode = null; + HavingExprRootNode = null; + OutputLimitSpec = null; + OrderByList = OrderByItem.EMPTY_ORDERBY_ARRAY; + SubSelectExpressions = ExprSubselectNode.EMPTY_SUBSELECT_ARRAY; + DeclaredExpressions = ExprNodeUtility.EMPTY_DECLARED_ARR; + Scripts = ExprNodeUtility.EMPTY_SCRIPTS; + VariableReferences = new HashSet(); + RowLimitSpec = null; + EventTypeReferences = new string[0]; + Annotations = new Attribute[0]; + UpdateSpec = null; + MatchRecognizeSpec = null; + ForClauseSpec = null; + SqlParameters = null; + ContextDesc = null; + OptionalContextName = null; + CreateGraphDesc = null; + CreateExpressionDesc = null; + FireAndForgetSpec = null; + GroupByExpressions = null; + IntoTableSpec = null; + TableNodes = null; + } + + /// + /// Returns the specification for an create-window statement. + /// + /// create-window spec, or null if not such a statement + public CreateWindowDesc CreateWindowDesc { get; private set; } + + /// + /// Returns the create-variable statement descriptor. + /// + /// create-variable spec + public CreateVariableDesc CreateVariableDesc { get; private set; } + + /// + /// Returns the FROM-clause stream definitions. + /// + /// list of stream specifications + public StreamSpecCompiled[] StreamSpecs { get; set; } + + /// + /// Returns SELECT-clause list of expressions. + /// + /// list of expressions and optional name + public SelectClauseSpecCompiled SelectClauseSpec { get; set; } + + /// + /// Returns the WHERE-clause root node of filter expression. + /// + /// filter expression root node + public ExprNode FilterRootNode + { + get { return FilterExprRootNode; } + } + + /// + /// Returns the LEFT/RIGHT/FULL OUTER JOIN-type and property name descriptor, if applicable. Returns null if regular join. + /// + /// outer join type, stream names and property names + public OuterJoinDesc[] OuterJoinDescList { get; private set; } + + /// + /// Returns expression root node representing the having-clause, if present, or null if no having clause was supplied. + /// + /// having-clause expression top node + public ExprNode HavingExprRootNode { get; set; } + + /// + /// Returns the output limit definition, if any. + /// + /// output limit spec + public OutputLimitSpec OutputLimitSpec { get; private set; } + + /// + /// Return a descriptor with the insert-into event name and optional list of columns. + /// + /// insert into specification + public InsertIntoDesc InsertIntoDesc { get; set; } + + /// + /// Returns the list of order-by expression as specified in the ORDER BY clause. + /// + /// Returns the orderByList. + public OrderByItem[] OrderByList { get; private set; } + + /// + /// Returns the stream selector (rstream/istream). + /// + /// stream selector + public SelectClauseStreamSelectorEnum SelectStreamSelectorEnum + { + get { return SelectStreamDirEnum; } + } + + /// + /// Set the where clause filter node. + /// + /// is the where-clause filter node + public ExprNode FilterExprRootNode { private get; set; } + + /// + /// Returns the list of lookup expression nodes. + /// + /// lookup nodes + public ExprSubselectNode[] SubSelectExpressions { get; private set; } + + /// + /// Returns the specification for an on-delete or on-select statement. + /// + /// on-trigger spec, or null if not such a statement + public OnTriggerDesc OnTriggerDesc { get; private set; } + + /// + /// Returns true to indicate the statement has variables. + /// + /// true for statements that use variables + public bool HasVariables + { + get { return VariableReferences != null && !VariableReferences.IsEmpty(); } + } + + /// + /// Sets the stream selection. + /// + /// stream selection + public SelectClauseStreamSelectorEnum SelectStreamDirEnum { private get; set; } + + /// + /// Returns the row limit specification, or null if none supplied. + /// + /// row limit spec if any + public RowLimitSpec RowLimitSpec { get; private set; } + + /// + /// Returns the event type name in used by the statement. + /// + /// set of event type name + public string[] EventTypeReferences { get; private set; } + + /// + /// Returns annotations or empty array if none. + /// + /// annotations + public Attribute[] Annotations { get; private set; } + + /// + /// Returns the update spec if update clause is used. + /// + /// update desc + public UpdateDesc UpdateSpec { get; private set; } + + /// + /// Returns the match recognize spec, if used + /// + /// match recognize spec + public MatchRecognizeSpec MatchRecognizeSpec { get; private set; } + + /// + /// Return variables referenced. + /// + /// variables + public ICollection VariableReferences { get; private set; } + + /// + /// Returns create index + /// + /// create index + public CreateIndexDesc CreateIndexDesc { get; private set; } + + public CreateSchemaDesc CreateSchemaDesc { get; private set; } + + public ForClauseSpec ForClauseSpec { get; private set; } + + public IDictionary> SqlParameters { get; private set; } + + public ExprDeclaredNode[] DeclaredExpressions { get; private set; } + + public ExpressionScriptProvided[] Scripts { get; private set; } + + public CreateContextDesc ContextDesc { get; private set; } + + public string OptionalContextName { get; private set; } + + public CreateDataFlowDesc CreateGraphDesc { get; private set; } + + public CreateExpressionDesc CreateExpressionDesc { get; private set; } + + public FireAndForgetSpec FireAndForgetSpec { get; private set; } + + public GroupByClauseExpressions GroupByExpressions { get; private set; } + + public IntoTableSpec IntoTableSpec { get; private set; } + + public ExprTableAccessNode[] TableNodes { get; private set; } + + public CreateTableDesc CreateTableDesc { get; private set; } + + public FilterSpecCompiled[] FilterSpecsOverall { get; set; } + + public NamedWindowConsumerStreamSpec[] NamedWindowConsumersAll { get; set; } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/spec/StatementSpecMapContext.cs b/NEsper.Core/NEsper.Core/epl/spec/StatementSpecMapContext.cs new file mode 100755 index 000000000..bbf7fc3ad --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/StatementSpecMapContext.cs @@ -0,0 +1,163 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.mgr; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.declexpr; +using com.espertech.esper.epl.expression.table; +using com.espertech.esper.epl.named; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.epl.variable; +using com.espertech.esper.pattern; +using com.espertech.esper.plugin; +using com.espertech.esper.schedule; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.spec +{ + /// + /// Context for mapping a SODA statement to a statement specification, or multiple for subqueries, + /// and obtaining certain optimization information from a statement. + /// + public class StatementSpecMapContext + { + private IDictionary _expressionDeclarations; + private IDictionary _scripts; + + /// + /// Ctor. + /// + /// engine imports + /// variable names + /// the configuration + /// The scheduling service. + /// The engine URI. + /// The pattern node factory. + /// The named window service. + /// The context management service. + /// The expr declared service. + /// optional context description + /// The table service. + public StatementSpecMapContext(EngineImportService engineImportService, VariableService variableService, ConfigurationInformation configuration, SchedulingService schedulingService, string engineURI, PatternNodeFactory patternNodeFactory, NamedWindowMgmtService namedWindowMgmtService, ContextManagementService contextManagementService, ExprDeclaredService exprDeclaredService, ContextDescriptor contextDescriptor, TableService tableService) + { + PlugInAggregations = new LazyAllocatedMap(); + TableExpressions = new HashSet(); + EngineImportService = engineImportService; + VariableService = variableService; + Configuration = configuration; + VariableNames = new HashSet(); + SchedulingService = schedulingService; + EngineURI = engineURI; + PatternNodeFactory = patternNodeFactory; + NamedWindowMgmtService = namedWindowMgmtService; + ContextManagementService = contextManagementService; + ExprDeclaredService = exprDeclaredService; + ContextDescriptor = contextDescriptor; + TableService = tableService; + } + + /// + /// Returns the engine import service. + /// + /// service + public EngineImportService EngineImportService { get; private set; } + + /// + /// Returns the variable service. + /// + /// service + public VariableService VariableService { get; private set; } + + /// + /// Returns true if a statement has variables. + /// + /// true for variables found + public bool HasVariables { get; set; } + + /// + /// Returns the configuration. + /// + /// config + public ConfigurationInformation Configuration { get; private set; } + + /// + /// Returns variables. + /// + /// variables + public ISet VariableNames { get; private set; } + + public SchedulingService SchedulingService { get; private set; } + + public string EngineURI { get; private set; } + + public PatternNodeFactory PatternNodeFactory { get; private set; } + + public NamedWindowMgmtService NamedWindowMgmtService { get; private set; } + + public IDictionary ExpressionDeclarations + { + get + { + if (_expressionDeclarations == null) + { + return Collections.GetEmptyMap(); + } + return _expressionDeclarations; + } + } + + public void AddExpressionDeclarations(ExpressionDeclItem item) + { + if (_expressionDeclarations == null) + { + _expressionDeclarations = new Dictionary(); + } + _expressionDeclarations.Put(item.Name, item); + } + + public IDictionary Scripts + { + get + { + if (_scripts == null) + { + return Collections.GetEmptyMap(); + } + return _scripts; + } + } + + public void AddScript(ExpressionScriptProvided item) + { + if (_scripts == null) + { + _scripts = new Dictionary(); + } + _scripts.Put(item.Name, item); + } + + public ContextManagementService ContextManagementService { get; private set; } + + public string ContextName { get; set; } + + public ExprDeclaredService ExprDeclaredService { get; private set; } + + public LazyAllocatedMap PlugInAggregations { get; private set; } + + public ContextDescriptor ContextDescriptor { get; private set; } + + public TableService TableService { get; private set; } + + public ISet TableExpressions { get; private set; } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/spec/StatementSpecMapper.cs b/NEsper.Core/NEsper.Core/epl/spec/StatementSpecMapper.cs new file mode 100755 index 000000000..f6b4aaafd --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/StatementSpecMapper.cs @@ -0,0 +1,4236 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.client.soda; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.mgr; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.db; +using com.espertech.esper.epl.declexpr; +using com.espertech.esper.epl.enummethod.dot; +using com.espertech.esper.epl.expression.accessagg; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.dot; +using com.espertech.esper.epl.expression.funcs; +using com.espertech.esper.epl.expression.methodagg; +using com.espertech.esper.epl.expression.ops; +using com.espertech.esper.epl.expression.prev; +using com.espertech.esper.epl.expression.prior; +using com.espertech.esper.epl.expression.subquery; +using com.espertech.esper.epl.expression.table; +using com.espertech.esper.epl.expression.time; +using com.espertech.esper.epl.named; +using com.espertech.esper.epl.parse; +using com.espertech.esper.epl.script; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.epl.variable; +using com.espertech.esper.pattern; +using com.espertech.esper.rowregex; +using com.espertech.esper.schedule; +using com.espertech.esper.type; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.spec +{ + using ContextDescriptor = client.soda.ContextDescriptor; + using OrderByElement = com.espertech.esper.client.soda.OrderByElement; + + /// + /// Helper for mapping internal representations of a statement to the SODA object model for statements. + /// + public class StatementSpecMapper + { + /// + /// Unmap expresission. + /// + /// to unmap + /// expression + public static Expression Unmap(ExprNode expression) + { + return UnmapExpressionDeep(expression, new StatementSpecUnMapContext()); + } + + /// + /// Unmap pattern. + /// + /// to unmap + /// pattern + public static PatternExpr Unmap(EvalFactoryNode node) + { + return UnmapPatternEvalDeep(node, new StatementSpecUnMapContext()); + } + + /// + /// Unmap annotation. + /// + /// to unmap + /// annotation + public static AnnotationPart Unmap(AnnotationDesc node) + { + var list = UnmapAnnotations(new List(Collections.SingletonList(node))); + return list[0]; + } + + /// + /// Unmap match recognize pattern. + /// + /// recognize pattern to unmap + /// match recognize pattern + public static MatchRecognizeRegEx Unmap(RowRegexExprNode pattern) + { + return UnmapExpressionDeepRowRegex(pattern, new StatementSpecUnMapContext()); + } + + public static StatementSpecRaw Map( + EPStatementObjectModel sodaStatement, + EngineImportService engineImportService, + VariableService variableService, + ConfigurationInformation configuration, + SchedulingService schedulingService, + string engineURI, + PatternNodeFactory patternNodeFactory, + NamedWindowMgmtService namedWindowMgmtService, + ContextManagementService contextManagementService, + ExprDeclaredService exprDeclaredService, + TableService tableService) + { + esper.core.context.util.ContextDescriptor contextDescriptor = null; + if (sodaStatement.ContextName != null) + { + contextDescriptor = contextManagementService.GetContextDescriptor(sodaStatement.ContextName); + } + + var mapContext = new StatementSpecMapContext( + engineImportService, variableService, configuration, schedulingService, engineURI, patternNodeFactory, + namedWindowMgmtService, contextManagementService, exprDeclaredService, contextDescriptor, tableService); + + var raw = Map(sodaStatement, mapContext); + if (mapContext.HasVariables) + { + raw.HasVariables = true; + } + raw.ReferencedVariables = mapContext.VariableNames; + raw.TableExpressions = mapContext.TableExpressions; + return raw; + } + + private static StatementSpecRaw Map(EPStatementObjectModel sodaStatement, StatementSpecMapContext mapContext) + { + var raw = new StatementSpecRaw(SelectClauseStreamSelectorEnum.ISTREAM_ONLY); + + var annotations = MapAnnotations(sodaStatement.Annotations); + raw.Annotations = annotations; + MapFireAndForget(sodaStatement.FireAndForgetClause, raw, mapContext); + MapExpressionDeclaration(sodaStatement.ExpressionDeclarations, raw, mapContext); + MapScriptExpressions(sodaStatement.ScriptExpressions, raw, mapContext); + MapContextName(sodaStatement.ContextName, raw, mapContext); + MapUpdateClause(sodaStatement.UpdateClause, raw, mapContext); + MapCreateContext(sodaStatement.CreateContext, raw, mapContext); + MapCreateWindow(sodaStatement.CreateWindow, sodaStatement.FromClause, raw, mapContext); + MapCreateIndex(sodaStatement.CreateIndex, raw, mapContext); + MapCreateVariable(sodaStatement.CreateVariable, raw, mapContext); + MapCreateTable(sodaStatement.CreateTable, raw, mapContext); + MapCreateSchema(sodaStatement.CreateSchema, raw, mapContext); + MapCreateExpression(sodaStatement.CreateExpression, raw, mapContext); + MapCreateGraph(sodaStatement.CreateDataFlow, raw, mapContext); + MapOnTrigger(sodaStatement.OnExpr, raw, mapContext); + var desc = MapInsertInto(sodaStatement.InsertInto); + raw.InsertIntoDesc = desc; + MapSelect(sodaStatement.SelectClause, raw, mapContext); + MapFrom(sodaStatement.FromClause, raw, mapContext); + MapWhere(sodaStatement.WhereClause, raw, mapContext); + MapGroupBy(sodaStatement.GroupByClause, raw, mapContext); + MapHaving(sodaStatement.HavingClause, raw, mapContext); + MapOutputLimit(sodaStatement.OutputLimitClause, raw, mapContext); + MapOrderBy(sodaStatement.OrderByClause, raw, mapContext); + MapRowLimit(sodaStatement.RowLimitClause, raw, mapContext); + MapMatchRecognize(sodaStatement.MatchRecognizeClause, raw, mapContext); + MapForClause(sodaStatement.ForClause, raw, mapContext); + MapSQLParameters(sodaStatement.FromClause, raw, mapContext); + MapIntoVariableClause(sodaStatement.IntoTableClause, raw, mapContext); + + // from clause is required for create-window + if (sodaStatement.CreateWindow != null && raw.StreamSpecs.Count == 0) + { + var spec = new FilterSpecRaw("System.Object", Collections.GetEmptyList(), null); + raw.StreamSpecs.Add( + new FilterStreamSpecRaw(spec, ViewSpec.EMPTY_VIEWSPEC_ARRAY, null, StreamSpecOptions.DEFAULT)); + } + return raw; + } + + private static void MapIntoVariableClause( + IntoTableClause intoClause, + StatementSpecRaw raw, + StatementSpecMapContext mapContext) + { + if (intoClause != null) + { + raw.IntoTableSpec = new IntoTableSpec(intoClause.TableName); + } + } + + private static void MapFireAndForget( + FireAndForgetClause fireAndForgetClause, + StatementSpecRaw raw, + StatementSpecMapContext mapContext) + { + if (fireAndForgetClause == null) + { + return; + } + else if (fireAndForgetClause is FireAndForgetDelete) + { + raw.FireAndForgetSpec = new FireAndForgetSpecDelete(); + } + else if (fireAndForgetClause is FireAndForgetInsert) + { + var insert = (FireAndForgetInsert) fireAndForgetClause; + raw.FireAndForgetSpec = new FireAndForgetSpecInsert(insert.IsUseValuesKeyword); + } + else if (fireAndForgetClause is FireAndForgetUpdate) + { + var upd = (FireAndForgetUpdate) fireAndForgetClause; + var assignments = + upd.Assignments.Select(pair => MapExpressionDeep(pair.Value, mapContext)) + .Select(expr => new OnTriggerSetAssignment(expr)) + .ToList(); + var updspec = new FireAndForgetSpecUpdate(assignments); + raw.FireAndForgetSpec = updspec; + } + else + { + throw new IllegalStateException("Unrecognized fire-and-forget clause " + fireAndForgetClause); + } + } + + /// + /// Maps the internal representation of a statement to the SODA object model. + /// + /// is the internal representation + /// object model of statement + public static StatementSpecUnMapResult Unmap(StatementSpecRaw statementSpec) + { + var unmapContext = new StatementSpecUnMapContext(); + var model = UnmapInternal(statementSpec, unmapContext); + return new StatementSpecUnMapResult(model, unmapContext.SubstitutionParams); + } + + private static EPStatementObjectModel UnmapInternal( + StatementSpecRaw statementSpec, + StatementSpecUnMapContext unmapContext) + { + var model = new EPStatementObjectModel(); + var annotations = UnmapAnnotations(statementSpec.Annotations); + model.Annotations = annotations; + UnmapFireAndForget(statementSpec.FireAndForgetSpec, model, unmapContext); + var expressionDeclarations = UnmapExpressionDeclarations(statementSpec.ExpressionDeclDesc, unmapContext); + model.ExpressionDeclarations = expressionDeclarations; + var scripts = UnmapScriptExpressions(statementSpec.ScriptExpressions, unmapContext); + model.ScriptExpressions = scripts; + UnmapContextName(statementSpec.OptionalContextName, model); + UnmapCreateContext(statementSpec.CreateContextDesc, model, unmapContext); + UnmapCreateWindow(statementSpec.CreateWindowDesc, model, unmapContext); + UnmapCreateIndex(statementSpec.CreateIndexDesc, model, unmapContext); + UnmapCreateVariable(statementSpec.CreateVariableDesc, model, unmapContext); + UnmapCreateTable(statementSpec.CreateTableDesc, model, unmapContext); + UnmapCreateSchema(statementSpec.CreateSchemaDesc, model, unmapContext); + UnmapCreateExpression(statementSpec.CreateExpressionDesc, model, unmapContext); + UnmapCreateGraph(statementSpec.CreateDataFlowDesc, model, unmapContext); + UnmapUpdateClause(statementSpec.StreamSpecs, statementSpec.UpdateDesc, model, unmapContext); + UnmapOnClause(statementSpec.OnTriggerDesc, model, unmapContext); + var insertIntoClause = UnmapInsertInto(statementSpec.InsertIntoDesc); + model.InsertInto = insertIntoClause; + var selectClause = UnmapSelect( + statementSpec.SelectClauseSpec, statementSpec.SelectStreamSelectorEnum, unmapContext); + model.SelectClause = selectClause; + UnmapFrom(statementSpec.StreamSpecs, statementSpec.OuterJoinDescList, model, unmapContext); + UnmapWhere(statementSpec.FilterRootNode, model, unmapContext); + UnmapGroupBy(statementSpec.GroupByExpressions, model, unmapContext); + UnmapHaving(statementSpec.HavingExprRootNode, model, unmapContext); + UnmapOutputLimit(statementSpec.OutputLimitSpec, model, unmapContext); + UnmapOrderBy(statementSpec.OrderByList, model, unmapContext); + UnmapRowLimit(statementSpec.RowLimitSpec, model, unmapContext); + UnmapMatchRecognize(statementSpec.MatchRecognizeSpec, model, unmapContext); + UnmapForClause(statementSpec.ForClauseSpec, model, unmapContext); + UnmapSQLParameters(statementSpec.SqlParameters, unmapContext); + UnmapIntoVariableClause(statementSpec.IntoTableSpec, model, unmapContext); + return model; + } + + private static void UnmapIntoVariableClause( + IntoTableSpec intoTableSpec, + EPStatementObjectModel model, + StatementSpecUnMapContext unmapContext) + { + if (intoTableSpec == null) + { + return; + } + model.IntoTableClause = new IntoTableClause(intoTableSpec.Name); + } + + private static void UnmapCreateTable( + CreateTableDesc desc, + EPStatementObjectModel model, + StatementSpecUnMapContext unmapContext) + { + if (desc == null) + { + return; + } + var clause = new CreateTableClause(desc.TableName); + var cols = new List(); + foreach (var col in desc.Columns) + { + var optExpr = col.OptExpression != null ? UnmapExpressionDeep(col.OptExpression, unmapContext) : null; + var annots = UnmapAnnotations(col.Annotations); + var coldesc = new com.espertech.esper.client.soda.CreateTableColumn( + col.ColumnName, optExpr, col.OptTypeName, col.OptTypeIsArray, col.OptTypeIsPrimitiveArray, annots, + col.PrimaryKey); + cols.Add(coldesc); + } + clause.Columns = cols; + model.CreateTable = clause; + } + + private static void UnmapFireAndForget( + FireAndForgetSpec fireAndForgetSpec, + EPStatementObjectModel model, + StatementSpecUnMapContext unmapContext) + { + if (fireAndForgetSpec == null) + { + return; + } + else if (fireAndForgetSpec is FireAndForgetSpecDelete) + { + model.FireAndForgetClause = new FireAndForgetDelete(); + } + else if (fireAndForgetSpec is FireAndForgetSpecInsert) + { + var insert = (FireAndForgetSpecInsert) fireAndForgetSpec; + model.FireAndForgetClause = new FireAndForgetInsert(insert.IsUseValuesKeyword); + } + else if (fireAndForgetSpec is FireAndForgetSpecUpdate) + { + var upd = (FireAndForgetSpecUpdate) fireAndForgetSpec; + var faf = new FireAndForgetUpdate(); + foreach (var assignment in upd.Assignments) + { + var expr = UnmapExpressionDeep(assignment.Expression, unmapContext); + faf.AddAssignment(new Assignment(expr)); + } + model.FireAndForgetClause = faf; + } + else + { + throw new IllegalStateException("Unrecognized type of fire-and-forget: " + fireAndForgetSpec); + } + } + + // Collect substitution parameters + private static void UnmapSQLParameters( + IEnumerable>> sqlParameters, + StatementSpecUnMapContext unmapContext) + { + if (sqlParameters == null) + { + return; + } + foreach (var pair in sqlParameters) + { + foreach (var node in pair.Value) + { + UnmapExpressionDeep(node, unmapContext); + } + } + } + + private static void UnmapOnClause( + OnTriggerDesc onTriggerDesc, + EPStatementObjectModel model, + StatementSpecUnMapContext unmapContext) + { + if (onTriggerDesc == null) + { + return; + } + if (onTriggerDesc.OnTriggerType == OnTriggerType.ON_DELETE) + { + var window = (OnTriggerWindowDesc) onTriggerDesc; + model.OnExpr = new OnDeleteClause(window.WindowName, window.OptionalAsName); + } + else if (onTriggerDesc.OnTriggerType == OnTriggerType.ON_UPDATE) + { + var window = (OnTriggerWindowUpdateDesc) onTriggerDesc; + var clause = new OnUpdateClause(window.WindowName, window.OptionalAsName); + foreach (var assignment in window.Assignments) + { + var expr = UnmapExpressionDeep(assignment.Expression, unmapContext); + clause.AddAssignment(expr); + } + model.OnExpr = clause; + } + else if (onTriggerDesc.OnTriggerType == OnTriggerType.ON_SELECT) + { + var window = (OnTriggerWindowDesc) onTriggerDesc; + var onSelect = new OnSelectClause(window.WindowName, window.OptionalAsName); + onSelect.IsDeleteAndSelect = window.IsDeleteAndSelect; + model.OnExpr = onSelect; + } + else if (onTriggerDesc.OnTriggerType == OnTriggerType.ON_SET) + { + var trigger = (OnTriggerSetDesc) onTriggerDesc; + var clause = new OnSetClause(); + foreach (var assignment in trigger.Assignments) + { + var expr = UnmapExpressionDeep(assignment.Expression, unmapContext); + clause.AddAssignment(expr); + } + model.OnExpr = clause; + } + else if (onTriggerDesc.OnTriggerType == OnTriggerType.ON_SPLITSTREAM) + { + var trigger = (OnTriggerSplitStreamDesc) onTriggerDesc; + var clause = OnInsertSplitStreamClause.Create(); + foreach (var stream in trigger.SplitStreams) + { + Expression whereClause = null; + if (stream.WhereClause != null) + { + whereClause = UnmapExpressionDeep(stream.WhereClause, unmapContext); + } + IList propertySelects = null; + string propertySelectStreamName = null; + if (stream.FromClause != null) + { + propertySelects = UnmapPropertySelects(stream.FromClause.PropertyEvalSpec, unmapContext); + propertySelectStreamName = stream.FromClause.OptionalStreamName; + } + var insertIntoClause = UnmapInsertInto(stream.InsertInto); + var selectClause = UnmapSelect( + stream.SelectClause, SelectClauseStreamSelectorEnum.ISTREAM_ONLY, unmapContext); + clause.AddItem( + OnInsertSplitStreamItem.Create( + insertIntoClause, selectClause, propertySelects, propertySelectStreamName, whereClause)); + } + model.OnExpr = clause; + clause.IsFirst = trigger.IsFirst; + } + else if (onTriggerDesc.OnTriggerType == OnTriggerType.ON_MERGE) + { + var trigger = (OnTriggerMergeDesc) onTriggerDesc; + var matchItems = new List(); + foreach (var matched in trigger.Items) + { + + var actions = new List(); + var matchCond = matched.OptionalMatchCond != null + ? UnmapExpressionDeep(matched.OptionalMatchCond, unmapContext) + : null; + var matchItem = new OnMergeMatchItem(matched.IsMatchedUnmatched, matchCond, actions); + foreach (var actionitem in matched.Actions) + { + OnMergeMatchedAction action; + if (actionitem is OnTriggerMergeActionDelete) + { + var delete = (OnTriggerMergeActionDelete) actionitem; + var optionalCondition = delete.OptionalWhereClause == null + ? null + : UnmapExpressionDeep(delete.OptionalWhereClause, unmapContext); + action = new OnMergeMatchedDeleteAction(optionalCondition); + } + else if (actionitem is OnTriggerMergeActionUpdate) + { + var merge = (OnTriggerMergeActionUpdate) actionitem; + var assignments = merge.Assignments + .Select(pair => UnmapExpressionDeep(pair.Expression, unmapContext)) + .Select(expr => new Assignment(expr)) + .ToList(); + var optionalCondition = merge.OptionalWhereClause == null + ? null + : UnmapExpressionDeep(merge.OptionalWhereClause, unmapContext); + action = new OnMergeMatchedUpdateAction(assignments, optionalCondition); + } + else if (actionitem is OnTriggerMergeActionInsert) + { + var insert = (OnTriggerMergeActionInsert) actionitem; + var columnNames = new List(insert.Columns); + var select = UnmapSelectClauseElements(insert.SelectClause, unmapContext); + var optionalCondition = insert.OptionalWhereClause == null + ? null + : UnmapExpressionDeep(insert.OptionalWhereClause, unmapContext); + action = new OnMergeMatchedInsertAction( + columnNames, select, optionalCondition, insert.OptionalStreamName); + } + else + { + throw new ArgumentException( + "Unrecognized merged action type '" + actionitem.GetType() + "'"); + } + actions.Add(action); + } + matchItems.Add(matchItem); + } + model.OnExpr = OnMergeClause.Create(trigger.WindowName, trigger.OptionalAsName, matchItems); + } + else + { + throw new ArgumentException("Type of on-clause not handled: " + onTriggerDesc.OnTriggerType); + } + } + + private static void UnmapUpdateClause( + IList desc, + UpdateDesc updateDesc, + EPStatementObjectModel model, + StatementSpecUnMapContext unmapContext) + { + if (updateDesc == null) + { + return; + } + var type = ((FilterStreamSpecRaw) desc[0]).RawFilterSpec.EventTypeName; + var clause = new UpdateClause(type, updateDesc.OptionalStreamName); + foreach (var assignment in updateDesc.Assignments) + { + var expr = UnmapExpressionDeep(assignment.Expression, unmapContext); + clause.AddAssignment(expr); + } + model.UpdateClause = clause; + + if (updateDesc.OptionalWhereClause != null) + { + var expr = UnmapExpressionDeep(updateDesc.OptionalWhereClause, unmapContext); + model.UpdateClause.OptionalWhereClause = expr; + } + } + + private static void UnmapContextName(string contextName, EPStatementObjectModel model) + { + model.ContextName = contextName; + } + + private static void UnmapCreateContext( + CreateContextDesc createContextDesc, + EPStatementObjectModel model, + StatementSpecUnMapContext unmapContext) + { + if (createContextDesc == null) + { + return; + } + + var desc = UnmapCreateContextDetail(createContextDesc.ContextDetail, unmapContext); + var clause = new CreateContextClause(createContextDesc.ContextName, desc); + model.CreateContext = clause; + } + + private static ContextDescriptor UnmapCreateContextDetail( + ContextDetail contextDetail, + StatementSpecUnMapContext unmapContext) + { + ContextDescriptor desc; + if (contextDetail is ContextDetailInitiatedTerminated) + { + var spec = (ContextDetailInitiatedTerminated) contextDetail; + var startCondition = UnmapCreateContextRangeCondition(spec.Start, unmapContext); + var endCondition = UnmapCreateContextRangeCondition(spec.End, unmapContext); + IList distinctExpressions = null; + if (spec.DistinctExpressions != null && spec.DistinctExpressions.Length > 0) + { + distinctExpressions = UnmapExpressionDeep(spec.DistinctExpressions, unmapContext); + } + desc = new ContextDescriptorInitiatedTerminated( + startCondition, endCondition, spec.IsOverlapping, distinctExpressions); + } + else if (contextDetail is ContextDetailPartitioned) + { + var seg = (ContextDetailPartitioned) contextDetail; + var segmentedItems = new List(); + foreach (var item in seg.Items) + { + var filter = UnmapFilter(item.FilterSpecRaw, unmapContext); + segmentedItems.Add(new ContextDescriptorKeyedSegmentedItem(item.PropertyNames, filter)); + } + desc = new ContextDescriptorKeyedSegmented(segmentedItems); + } + else if (contextDetail is ContextDetailCategory) + { + var category = (ContextDetailCategory) contextDetail; + var categoryItems = new List(); + var filter = UnmapFilter(category.FilterSpecRaw, unmapContext); + foreach (var item in category.Items) + { + var expr = UnmapExpressionDeep(item.Expression, unmapContext); + categoryItems.Add(new ContextDescriptorCategoryItem(expr, item.Name)); + } + desc = new ContextDescriptorCategory(categoryItems, filter); + } + else if (contextDetail is ContextDetailHash) + { + var init = (ContextDetailHash) contextDetail; + var hashes = new List(); + foreach (var item in init.Items) + { + var dot = + UnmapChains( + new List(Collections.SingletonList(item.Function)), unmapContext, false)[0]; + var dotExpression = + new SingleRowMethodExpression(new List(Collections.SingletonList(dot))); + var filter = UnmapFilter(item.FilterSpecRaw, unmapContext); + hashes.Add(new ContextDescriptorHashSegmentedItem(dotExpression, filter)); + } + desc = new ContextDescriptorHashSegmented(hashes, init.Granularity, init.IsPreallocate); + } + else + { + var nested = (ContextDetailNested) contextDetail; + var contexts = new List(); + foreach (var item in nested.Contexts) + { + var detail = UnmapCreateContextDetail(item.ContextDetail, unmapContext); + contexts.Add(new CreateContextClause(item.ContextName, detail)); + } + desc = new ContextDescriptorNested(contexts); + } + return desc; + } + + private static ContextDescriptorCondition UnmapCreateContextRangeCondition( + ContextDetailCondition endpoint, + StatementSpecUnMapContext unmapContext) + { + if (endpoint is ContextDetailConditionCrontab) + { + var crontab = (ContextDetailConditionCrontab) endpoint; + IList crontabExpr = UnmapExpressionDeep(crontab.Crontab, unmapContext); + return new ContextDescriptorConditionCrontab(crontabExpr, crontab.IsImmediate); + } + else if (endpoint is ContextDetailConditionPattern) + { + var pattern = (ContextDetailConditionPattern) endpoint; + var patternExpr = UnmapPatternEvalDeep(pattern.PatternRaw, unmapContext); + return new ContextDescriptorConditionPattern(patternExpr, pattern.IsInclusive, pattern.IsImmediate); + } + else if (endpoint is ContextDetailConditionFilter) + { + var filter = (ContextDetailConditionFilter) endpoint; + var filterExpr = UnmapFilter(filter.FilterSpecRaw, unmapContext); + return new ContextDescriptorConditionFilter(filterExpr, filter.OptionalFilterAsName); + } + else if (endpoint is ContextDetailConditionTimePeriod) + { + var period = (ContextDetailConditionTimePeriod) endpoint; + var expression = (TimePeriodExpression) UnmapExpressionDeep(period.TimePeriod, unmapContext); + return new ContextDescriptorConditionTimePeriod(expression, period.IsImmediate); + } + else if (endpoint is ContextDetailConditionImmediate) + { + return new ContextDescriptorConditionImmediate(); + } + else if (endpoint is ContextDetailConditionNever) + { + return new ContextDescriptorConditionNever(); + } + throw new IllegalStateException("Unrecognized endpoint " + endpoint); + } + + private static void UnmapCreateWindow( + CreateWindowDesc createWindowDesc, + EPStatementObjectModel model, + StatementSpecUnMapContext unmapContext) + { + if (createWindowDesc == null) + { + return; + } + Expression filter = null; + if (createWindowDesc.InsertFilter != null) + { + filter = UnmapExpressionDeep(createWindowDesc.InsertFilter, unmapContext); + } + + var clause = new CreateWindowClause( + createWindowDesc.WindowName, UnmapViews(createWindowDesc.ViewSpecs, unmapContext)); + clause.IsInsert = createWindowDesc.IsInsert; + clause.InsertWhereClause = filter; + clause.Columns = UnmapColumns(createWindowDesc.Columns); + model.CreateWindow = clause; + } + + private static void UnmapCreateIndex( + CreateIndexDesc createIndexDesc, + EPStatementObjectModel model, + StatementSpecUnMapContext unmapContext) + { + if (createIndexDesc == null) + { + return; + } + var cols = createIndexDesc.Columns + .Select(item => new CreateIndexColumn(item.Name, item.Type.Xlate())) + .ToList(); + model.CreateIndex = new CreateIndexClause( + createIndexDesc.IndexName, createIndexDesc.WindowName, cols, createIndexDesc.IsUnique); + } + + private static void UnmapCreateVariable( + CreateVariableDesc createVariableDesc, + EPStatementObjectModel model, + StatementSpecUnMapContext unmapContext) + { + if (createVariableDesc == null) + { + return; + } + Expression assignment = null; + if (createVariableDesc.Assignment != null) + { + assignment = UnmapExpressionDeep(createVariableDesc.Assignment, unmapContext); + } + var clause = new CreateVariableClause( + createVariableDesc.VariableType, createVariableDesc.VariableName, assignment, + createVariableDesc.IsConstant); + clause.IsArray = createVariableDesc.IsArray; + model.CreateVariable = clause; + } + + private static void UnmapCreateSchema( + CreateSchemaDesc desc, + EPStatementObjectModel model, + StatementSpecUnMapContext unmapContext) + { + if (desc == null) + { + return; + } + model.CreateSchema = UnmapCreateSchemaInternal(desc, unmapContext); + } + + private static void UnmapCreateExpression( + CreateExpressionDesc desc, + EPStatementObjectModel model, + StatementSpecUnMapContext unmapContext) + { + if (desc == null) + { + return; + } + CreateExpressionClause clause; + if (desc.Expression != null) + { + clause = new CreateExpressionClause(UnmapExpressionDeclItem(desc.Expression, unmapContext)); + } + else + { + clause = new CreateExpressionClause(UnmapScriptExpression(desc.Script, unmapContext)); + } + model.CreateExpression = clause; + } + + private static CreateSchemaClause UnmapCreateSchemaInternal( + CreateSchemaDesc desc, + StatementSpecUnMapContext unmapContext) + { + var columns = UnmapColumns(desc.Columns); + var clause = new CreateSchemaClause( + desc.SchemaName, desc.Types, columns, desc.Inherits, desc.AssignedType.MapToSoda()); + clause.StartTimestampPropertyName = desc.StartTimestampProperty; + clause.EndTimestampPropertyName = desc.EndTimestampProperty; + clause.CopyFrom = desc.CopyFrom; + return clause; + } + + private static void UnmapCreateGraph( + CreateDataFlowDesc desc, + EPStatementObjectModel model, + StatementSpecUnMapContext unmapContext) + { + if (desc == null) + { + return; + } + + var schemas = desc.Schemas.Select(schema => UnmapCreateSchemaInternal(schema, unmapContext)).ToList(); + var operators = desc.Operators.Select(spec => UnmapGraphOperator(spec, unmapContext)).ToList(); + var clause = new CreateDataFlowClause(desc.GraphName, schemas, operators); + model.CreateDataFlow = clause; + } + + private static DataFlowOperator UnmapGraphOperator( + GraphOperatorSpec spec, + StatementSpecUnMapContext unmapContext) + { + var op = new DataFlowOperator(); + op.OperatorName = spec.OperatorName; + op.Annotations = UnmapAnnotations(spec.Annotations); + + op.Input = spec.Input.StreamNamesAndAliases + .Select(@in => new DataFlowOperatorInput(@in.InputStreamNames, @in.OptionalAsName)) + .ToList(); + + var outputs = new List(); + foreach (var @out in spec.Output.Items) + { + IList types = @out.TypeInfo.IsEmpty() + ? null + : new List(Collections.SingletonList(UnmapTypeInfo(@out.TypeInfo[0]))); + outputs.Add(new DataFlowOperatorOutput(@out.StreamName, types)); + } + op.Output = outputs; + + if (spec.Detail != null) + { + var parameters = new List(); + foreach (var param in spec.Detail.Configs) + { + var value = param.Value; + if (value is StatementSpecRaw) + { + value = UnmapInternal((StatementSpecRaw) value, unmapContext); + } + if (value is ExprNode) + { + value = UnmapExpressionDeep((ExprNode) value, unmapContext); + } + parameters.Add(new DataFlowOperatorParameter(param.Key, value)); + } + op.Parameters = parameters; + } + else + { + op.Parameters = Collections.GetEmptyList(); + } + + return op; + } + + private static DataFlowOperatorOutputType UnmapTypeInfo(GraphOperatorOutputItemType typeInfo) + { + IList types = Collections.GetEmptyList(); + if (typeInfo.TypeParameters != null && !typeInfo.TypeParameters.IsEmpty()) + { + types = typeInfo.TypeParameters + .Select(UnmapTypeInfo) + .ToList(); + } + return new DataFlowOperatorOutputType(typeInfo.IsWildcard, typeInfo.TypeOrClassname, types); + } + + private static void UnmapOrderBy( + IList orderByList, + EPStatementObjectModel model, + StatementSpecUnMapContext unmapContext) + { + if ((orderByList == null) || (orderByList.Count == 0)) + { + return; + } + + var clause = new OrderByClause(); + foreach (var item in orderByList) + { + var expr = UnmapExpressionDeep(item.ExprNode, unmapContext); + clause.Add(expr, item.IsDescending); + } + model.OrderByClause = clause; + } + + private static void UnmapOutputLimit( + OutputLimitSpec outputLimitSpec, + EPStatementObjectModel model, + StatementSpecUnMapContext unmapContext) + { + if (outputLimitSpec == null) + { + return; + } + + var selector = OutputLimitSelector.DEFAULT; + if (outputLimitSpec.DisplayLimit == OutputLimitLimitType.FIRST) + { + selector = OutputLimitSelector.FIRST; + } + if (outputLimitSpec.DisplayLimit == OutputLimitLimitType.LAST) + { + selector = OutputLimitSelector.LAST; + } + if (outputLimitSpec.DisplayLimit == OutputLimitLimitType.SNAPSHOT) + { + selector = OutputLimitSelector.SNAPSHOT; + } + if (outputLimitSpec.DisplayLimit == OutputLimitLimitType.ALL) + { + selector = OutputLimitSelector.ALL; + } + + OutputLimitClause clause; + var unit = OutputLimitUnit.EVENTS; + if (outputLimitSpec.RateType == OutputLimitRateType.TIME_PERIOD) + { + unit = OutputLimitUnit.TIME_PERIOD; + var timePeriod = + (TimePeriodExpression) UnmapExpressionDeep(outputLimitSpec.TimePeriodExpr, unmapContext); + clause = new OutputLimitClause(selector, timePeriod); + } + else if (outputLimitSpec.RateType == OutputLimitRateType.AFTER) + { + unit = OutputLimitUnit.AFTER; + if (outputLimitSpec.AfterTimePeriodExpr != null) + { + var after = + (TimePeriodExpression) UnmapExpressionDeep(outputLimitSpec.AfterTimePeriodExpr, unmapContext); + clause = new OutputLimitClause(OutputLimitSelector.DEFAULT, OutputLimitUnit.AFTER, after, null); + } + else + { + clause = new OutputLimitClause( + OutputLimitSelector.DEFAULT, OutputLimitUnit.AFTER, null, outputLimitSpec.AfterNumberOfEvents); + } + } + else if (outputLimitSpec.RateType == OutputLimitRateType.WHEN_EXPRESSION) + { + unit = OutputLimitUnit.WHEN_EXPRESSION; + var whenExpression = UnmapExpressionDeep(outputLimitSpec.WhenExpressionNode, unmapContext); + var thenAssignments = new List(); + clause = new OutputLimitClause(selector, whenExpression, thenAssignments); + if (outputLimitSpec.ThenExpressions != null) + { + foreach (var assignment in outputLimitSpec.ThenExpressions) + { + var expr = UnmapExpressionDeep(assignment.Expression, unmapContext); + clause.AddThenAssignment(expr); + } + } + } + else if (outputLimitSpec.RateType == OutputLimitRateType.CRONTAB) + { + unit = OutputLimitUnit.CRONTAB_EXPRESSION; + IList timerAtExpressions = outputLimitSpec.CrontabAtSchedule; + var mappedExpr = UnmapExpressionDeep(timerAtExpressions, unmapContext); + clause = new OutputLimitClause(selector, mappedExpr.ToArray()); + } + else if (outputLimitSpec.RateType == OutputLimitRateType.TERM) + { + clause = new OutputLimitClause(selector, OutputLimitUnit.CONTEXT_PARTITION_TERM); + } + else + { + clause = new OutputLimitClause(selector, outputLimitSpec.Rate, outputLimitSpec.VariableName, unit); + } + + clause.AfterNumberOfEvents = outputLimitSpec.AfterNumberOfEvents; + if (outputLimitSpec.AfterTimePeriodExpr != null) + { + clause.AfterTimePeriodExpression = UnmapExpressionDeep( + outputLimitSpec.AfterTimePeriodExpr, unmapContext); + } + clause.IsAndAfterTerminate = outputLimitSpec.IsAndAfterTerminate; + if (outputLimitSpec.AndAfterTerminateExpr != null) + { + clause.AndAfterTerminateAndExpr = UnmapExpressionDeep( + outputLimitSpec.AndAfterTerminateExpr, unmapContext); + } + if (outputLimitSpec.AndAfterTerminateThenExpressions != null) + { + var thenAssignments = outputLimitSpec.AndAfterTerminateThenExpressions + .Select(assignment => UnmapExpressionDeep(assignment.Expression, unmapContext)) + .Select(expr => new Assignment(expr)) + .ToList(); + clause.AndAfterTerminateThenAssignments = thenAssignments; + } + model.OutputLimitClause = clause; + } + + private static void UnmapRowLimit( + RowLimitSpec rowLimitSpec, + EPStatementObjectModel model, + StatementSpecUnMapContext unmapContext) + { + if (rowLimitSpec == null) + { + return; + } + var spec = new RowLimitClause( + rowLimitSpec.NumRows, rowLimitSpec.OptionalOffset, + rowLimitSpec.NumRowsVariable, rowLimitSpec.OptionalOffsetVariable); + model.RowLimitClause = spec; + } + + private static void UnmapForClause( + ForClauseSpec spec, + EPStatementObjectModel model, + StatementSpecUnMapContext unmapContext) + { + if ((spec == null) || (spec.Clauses == null) || (spec.Clauses.Count == 0)) + { + return; + } + var clause = new ForClause(); + foreach (var itemSpec in spec.Clauses) + { + var item = new ForClauseItem(EnumHelper.Parse(itemSpec.Keyword)); + item.Expressions = UnmapExpressionDeep(itemSpec.Expressions, unmapContext); + clause.Items.Add(item); + } + model.ForClause = clause; + } + + private static void UnmapMatchRecognize( + MatchRecognizeSpec spec, + EPStatementObjectModel model, + StatementSpecUnMapContext unmapContext) + { + if (spec == null) + { + return; + } + var clause = new MatchRecognizeClause(); + clause.PartitionExpressions = UnmapExpressionDeep(spec.PartitionByExpressions, unmapContext); + + var measures = spec.Measures + .Select(item => new SelectClauseExpression(UnmapExpressionDeep(item.Expr, unmapContext), item.Name)) + .ToList(); + + clause.Measures = measures; + clause.IsAll = spec.IsAllMatches; + clause.SkipClause = spec.Skip.Skip.Xlate(); + + var defines = spec.Defines + .Select( + define => + new MatchRecognizeDefine( + define.Identifier, UnmapExpressionDeep(define.Expression, unmapContext))) + .ToList(); + + clause.Defines = defines; + + if (spec.Interval != null) + { + clause.IntervalClause = new MatchRecognizeIntervalClause( + (TimePeriodExpression) UnmapExpressionDeep( + spec.Interval.TimePeriodExpr, unmapContext), spec.Interval.IsOrTerminated); + } + clause.Pattern = UnmapExpressionDeepRowRegex(spec.Pattern, unmapContext); + model.MatchRecognizeClause = clause; + } + + private static void MapOrderBy( + OrderByClause orderByClause, + StatementSpecRaw raw, + StatementSpecMapContext mapContext) + { + if (orderByClause == null) + { + return; + } + foreach (var element in orderByClause.OrderByExpressions) + { + ExprNode orderExpr = MapExpressionDeep(element.Expression, mapContext); + var item = new OrderByItem(orderExpr, element.IsDescending); + raw.OrderByList.Add(item); + } + } + + private static void MapOutputLimit( + OutputLimitClause outputLimitClause, + StatementSpecRaw raw, + StatementSpecMapContext mapContext) + { + if (outputLimitClause == null) + { + return; + } + + OutputLimitLimitType displayLimit = + EnumHelper.Parse(outputLimitClause.Selector.ToString()); + + OutputLimitRateType rateType; + if (outputLimitClause.Unit == OutputLimitUnit.EVENTS) + { + rateType = OutputLimitRateType.EVENTS; + } + else if (outputLimitClause.Unit == OutputLimitUnit.TIME_PERIOD) + { + rateType = OutputLimitRateType.TIME_PERIOD; + } + else if (outputLimitClause.Unit == OutputLimitUnit.CRONTAB_EXPRESSION) + { + rateType = OutputLimitRateType.CRONTAB; + } + else if (outputLimitClause.Unit == OutputLimitUnit.WHEN_EXPRESSION) + { + rateType = OutputLimitRateType.WHEN_EXPRESSION; + } + else if (outputLimitClause.Unit == OutputLimitUnit.AFTER) + { + rateType = OutputLimitRateType.AFTER; + } + else if (outputLimitClause.Unit == OutputLimitUnit.CONTEXT_PARTITION_TERM) + { + rateType = OutputLimitRateType.TERM; + } + else + { + throw new ArgumentException("Unknown output limit unit " + outputLimitClause.Unit); + } + + double? frequency = outputLimitClause.Frequency; + string frequencyVariable = outputLimitClause.FrequencyVariable; + + if (frequencyVariable != null) + { + mapContext.HasVariables = true; + } + + ExprNode whenExpression = null; + IList assignments = null; + if (outputLimitClause.WhenExpression != null) + { + whenExpression = MapExpressionDeep(outputLimitClause.WhenExpression, mapContext); + + assignments = outputLimitClause.ThenAssignments + .Select(pair => MapExpressionDeep(pair.Value, mapContext)) + .Select(expr => new OnTriggerSetAssignment(expr)) + .ToList(); + } + + IList timerAtExprList = null; + if (outputLimitClause.CrontabAtParameters != null) + { + timerAtExprList = MapExpressionDeep(outputLimitClause.CrontabAtParameters, mapContext); + } + + ExprTimePeriod timePeriod = null; + if (outputLimitClause.TimePeriodExpression != null) + { + timePeriod = (ExprTimePeriod) MapExpressionDeep(outputLimitClause.TimePeriodExpression, mapContext); + } + + ExprTimePeriod afterTimePeriod = null; + if (outputLimitClause.AfterTimePeriodExpression != null) + { + afterTimePeriod = + (ExprTimePeriod) MapExpressionDeep(outputLimitClause.AfterTimePeriodExpression, mapContext); + } + + ExprNode andAfterTerminateAndExpr = null; + if (outputLimitClause.AndAfterTerminateAndExpr != null) + { + andAfterTerminateAndExpr = MapExpressionDeep(outputLimitClause.AndAfterTerminateAndExpr, mapContext); + } + + IList afterTerminateAssignments = null; + if (outputLimitClause.AndAfterTerminateThenAssignments != null) + { + afterTerminateAssignments = new List(); + foreach (Assignment pair in outputLimitClause.AndAfterTerminateThenAssignments) + { + var expr = MapExpressionDeep(pair.Value, mapContext); + afterTerminateAssignments.Add(new OnTriggerSetAssignment(expr)); + } + } + + var spec = new OutputLimitSpec( + frequency, frequencyVariable, rateType, displayLimit, whenExpression, assignments, timerAtExprList, + timePeriod, afterTimePeriod, outputLimitClause.AfterNumberOfEvents, + outputLimitClause.IsAndAfterTerminate, andAfterTerminateAndExpr, afterTerminateAssignments); + raw.OutputLimitSpec = spec; + } + + private static void MapOnTrigger(OnClause onExpr, StatementSpecRaw raw, StatementSpecMapContext mapContext) + { + if (onExpr == null) + { + return; + } + + if (onExpr is OnDeleteClause) + { + var onDeleteClause = (OnDeleteClause) onExpr; + raw.OnTriggerDesc = new OnTriggerWindowDesc( + onDeleteClause.WindowName, onDeleteClause.OptionalAsName, OnTriggerType.ON_DELETE, false); + } + else if (onExpr is OnSelectClause) + { + var onSelectClause = (OnSelectClause) onExpr; + raw.OnTriggerDesc = new OnTriggerWindowDesc( + onSelectClause.WindowName, onSelectClause.OptionalAsName, OnTriggerType.ON_SELECT, + onSelectClause.IsDeleteAndSelect); + } + else if (onExpr is OnSetClause) + { + var setClause = (OnSetClause) onExpr; + mapContext.HasVariables = true; + var assignments = + setClause.Assignments.Select(pair => MapExpressionDeep(pair.Value, mapContext)) + .Select(expr => new OnTriggerSetAssignment(expr)) + .ToList(); + var desc = new OnTriggerSetDesc(assignments); + raw.OnTriggerDesc = desc; + } + else if (onExpr is OnUpdateClause) + { + var updateClause = (OnUpdateClause) onExpr; + var assignments = + updateClause.Assignments.Select(pair => MapExpressionDeep(pair.Value, mapContext)) + .Select(expr => new OnTriggerSetAssignment(expr)) + .ToList(); + var desc = new OnTriggerWindowUpdateDesc( + updateClause.WindowName, updateClause.OptionalAsName, assignments); + raw.OnTriggerDesc = desc; + } + else if (onExpr is OnInsertSplitStreamClause) + { + var splitClause = (OnInsertSplitStreamClause) onExpr; + mapContext.HasVariables = true; + var streams = new List(); + foreach (var item in splitClause.Items) + { + OnTriggerSplitStreamFromClause fromClause = null; + if (item.PropertySelects != null) + { + var propertyEvalSpec = MapPropertySelects(item.PropertySelects, mapContext); + fromClause = new OnTriggerSplitStreamFromClause( + propertyEvalSpec, item.PropertySelectsStreamName); + } + + ExprNode whereClause = null; + if (item.WhereClause != null) + { + whereClause = MapExpressionDeep(item.WhereClause, mapContext); + } + + var insertDesc = MapInsertInto(item.InsertInto); + var selectDesc = MapSelectRaw(item.SelectClause, mapContext); + + streams.Add(new OnTriggerSplitStream(insertDesc, selectDesc, fromClause, whereClause)); + } + var desc = new OnTriggerSplitStreamDesc(OnTriggerType.ON_SPLITSTREAM, splitClause.IsFirst, streams); + raw.OnTriggerDesc = desc; + } + else if (onExpr is OnMergeClause) + { + var merge = (OnMergeClause) onExpr; + var matcheds = new List(); + foreach (var matchItem in merge.MatchItems) + { + var actions = new List(); + foreach (var action in matchItem.Actions) + { + OnTriggerMergeAction actionItem; + if (action is OnMergeMatchedDeleteAction) + { + var delete = (OnMergeMatchedDeleteAction) action; + var optionalCondition = delete.WhereClause == null + ? null + : MapExpressionDeep(delete.WhereClause, mapContext); + actionItem = new OnTriggerMergeActionDelete(optionalCondition); + } + else if (action is OnMergeMatchedUpdateAction) + { + var update = (OnMergeMatchedUpdateAction) action; + var assignments = update.Assignments + .Select(pair => MapExpressionDeep(pair.Value, mapContext)) + .Select(expr => new OnTriggerSetAssignment(expr)) + .ToList(); + var optionalCondition = update.WhereClause == null + ? null + : MapExpressionDeep(update.WhereClause, mapContext); + actionItem = new OnTriggerMergeActionUpdate(optionalCondition, assignments); + } + else if (action is OnMergeMatchedInsertAction) + { + var insert = (OnMergeMatchedInsertAction) action; + var columnNames = new List(insert.ColumnNames); + var select = MapSelectClauseElements(insert.SelectList, mapContext); + var optionalCondition = insert.WhereClause == null + ? null + : MapExpressionDeep(insert.WhereClause, mapContext); + actionItem = new OnTriggerMergeActionInsert( + optionalCondition, insert.OptionalStreamName, columnNames, select); + } + else + { + throw new ArgumentException("Unrecognized merged action type '" + action.GetType() + "'"); + } + actions.Add(actionItem); + } + var optionalConditionX = matchItem.OptionalCondition == null + ? null + : MapExpressionDeep(matchItem.OptionalCondition, mapContext); + matcheds.Add(new OnTriggerMergeMatched(matchItem.IsMatched, optionalConditionX, actions)); + } + var mergeDesc = new OnTriggerMergeDesc(merge.WindowName, merge.OptionalAsName, matcheds); + raw.OnTriggerDesc = mergeDesc; + } + else + { + throw new ArgumentException("Cannot map on-clause expression type : " + onExpr); + } + } + + private static void MapRowLimit( + RowLimitClause rowLimitClause, + StatementSpecRaw raw, + StatementSpecMapContext mapContext) + { + if (rowLimitClause == null) + { + return; + } + if (rowLimitClause.NumRowsVariable != null) + { + raw.HasVariables = true; + mapContext.VariableNames.Add(rowLimitClause.NumRowsVariable); + } + if (rowLimitClause.OptionalOffsetRowsVariable != null) + { + raw.HasVariables = true; + mapContext.VariableNames.Add(rowLimitClause.OptionalOffsetRowsVariable); + } + raw.RowLimitSpec = new RowLimitSpec( + rowLimitClause.NumRows, rowLimitClause.OptionalOffsetRows, + rowLimitClause.NumRowsVariable, rowLimitClause.OptionalOffsetRowsVariable); + } + + private static void MapForClause(ForClause clause, StatementSpecRaw raw, StatementSpecMapContext mapContext) + { + if ((clause == null) || (clause.Items.Count == 0)) + { + return; + } + raw.ForClauseSpec = new ForClauseSpec(); + foreach (var item in clause.Items) + { + var specItem = new ForClauseItemSpec( + item.Keyword.Value.GetName(), MapExpressionDeep(item.Expressions, mapContext)); + raw.ForClauseSpec.Clauses.Add(specItem); + } + } + + private static void MapMatchRecognize( + MatchRecognizeClause clause, + StatementSpecRaw raw, + StatementSpecMapContext mapContext) + { + if (clause == null) + { + return; + } + var spec = new MatchRecognizeSpec(); + spec.PartitionByExpressions = MapExpressionDeep(clause.PartitionExpressions, mapContext); + + var measures = clause.Measures + .Select( + item => new MatchRecognizeMeasureItem(MapExpressionDeep(item.Expression, mapContext), item.AsName)) + .ToList(); + + spec.Measures = measures; + spec.IsAllMatches = clause.IsAll; + spec.Skip = new MatchRecognizeSkip(clause.SkipClause.Xlate()); + + var defines = + clause.Defines.Select( + define => + new MatchRecognizeDefineItem(define.Name, MapExpressionDeep(define.Expression, mapContext))) + .ToList(); + spec.Defines = defines; + + if (clause.IntervalClause != null) + { + var timePeriod = (ExprTimePeriod) MapExpressionDeep(clause.IntervalClause.Expression, mapContext); + try + { + timePeriod.Validate( + new ExprValidationContext( + null, null, null, null, null, null, null, null, null, null, -1, null, null, null, false, + false, false, false, null, false)); + } + catch (ExprValidationException e) + { + throw new EPException("Error validating time-period expression: " + e.Message, e); + } + spec.Interval = new MatchRecognizeInterval(timePeriod, clause.IntervalClause.IsOrTerminated); + } + spec.Pattern = MapExpressionDeepRowRegex(clause.Pattern, mapContext); + raw.MatchRecognizeSpec = spec; + } + + private static void MapHaving(Expression havingClause, StatementSpecRaw raw, StatementSpecMapContext mapContext) + { + if (havingClause == null) + { + return; + } + var node = MapExpressionDeep(havingClause, mapContext); + raw.HavingExprRootNode = node; + } + + private static void UnmapHaving( + ExprNode havingExprRootNode, + EPStatementObjectModel model, + StatementSpecUnMapContext unmapContext) + { + if (havingExprRootNode == null) + { + return; + } + var expr = UnmapExpressionDeep(havingExprRootNode, unmapContext); + model.HavingClause = expr; + } + + private static void MapGroupBy( + GroupByClause groupByClause, + StatementSpecRaw raw, + StatementSpecMapContext mapContext) + { + if (groupByClause == null) + { + return; + } + foreach (var expr in groupByClause.GroupByExpressions) + { + var element = MapGroupByExpr(expr, mapContext); + raw.GroupByExpressions.Add(element); + } + } + + private static GroupByClauseElement MapGroupByExpr( + GroupByClauseExpression expr, + StatementSpecMapContext mapContext) + { + if (expr is GroupByClauseExpressionSingle) + { + var node = MapExpressionDeep(((GroupByClauseExpressionSingle) expr).Expression, mapContext); + return new GroupByClauseElementExpr(node); + } + if (expr is GroupByClauseExpressionCombination) + { + IList nodes = MapExpressionDeep( + ((GroupByClauseExpressionCombination) expr).Expressions, mapContext); + return new GroupByClauseElementCombinedExpr(nodes); + } + if (expr is GroupByClauseExpressionGroupingSet) + { + var set = (GroupByClauseExpressionGroupingSet) expr; + return new GroupByClauseElementGroupingSet(MapGroupByElements(set.Expressions, mapContext)); + } + if (expr is GroupByClauseExpressionRollupOrCube) + { + var rollup = (GroupByClauseExpressionRollupOrCube) expr; + return new GroupByClauseElementRollupOrCube( + rollup.IsCube, MapGroupByElements(rollup.Expressions, mapContext)); + } + throw new IllegalStateException("Group by expression not recognized: " + expr); + } + + private static IList MapGroupByElements( + IList elements, + StatementSpecMapContext mapContext) + { + var @out = new List(); + foreach (var element in elements) + { + @out.Add(MapGroupByExpr(element, mapContext)); + } + return @out; + } + + private static void UnmapGroupBy( + IList groupByExpressions, + EPStatementObjectModel model, + StatementSpecUnMapContext unmapContext) + { + if (groupByExpressions.Count == 0) + { + return; + } + var expressions = new List(); + foreach (var element in groupByExpressions) + { + expressions.Add(UnmapGroupByExpression(element, unmapContext)); + } + model.GroupByClause = new GroupByClause(expressions); + } + + private static GroupByClauseExpression UnmapGroupByExpression( + GroupByClauseElement element, + StatementSpecUnMapContext unmapContext) + { + if (element is GroupByClauseElementExpr) + { + var expr = (GroupByClauseElementExpr) element; + var unmapped = UnmapExpressionDeep(expr.Expr, unmapContext); + return new GroupByClauseExpressionSingle(unmapped); + } + if (element is GroupByClauseElementCombinedExpr) + { + var expr = (GroupByClauseElementCombinedExpr) element; + IList unmapped = UnmapExpressionDeep(expr.Expressions, unmapContext); + return new GroupByClauseExpressionCombination(unmapped); + } + else if (element is GroupByClauseElementRollupOrCube) + { + var rollup = (GroupByClauseElementRollupOrCube) element; + var elements = UnmapGroupByExpressions(rollup.RollupExpressions, unmapContext); + return new GroupByClauseExpressionRollupOrCube(rollup.IsCube, elements); + } + else if (element is GroupByClauseElementGroupingSet) + { + var set = (GroupByClauseElementGroupingSet) element; + var elements = UnmapGroupByExpressions(set.Elements, unmapContext); + return new GroupByClauseExpressionGroupingSet(elements); + } + else + { + throw new IllegalStateException("Unrecognized group-by element " + element); + } + } + + private static IList UnmapGroupByExpressions( + IList rollupExpressions, + StatementSpecUnMapContext unmapContext) + { + var @out = new List(); + foreach (var e in rollupExpressions) + { + @out.Add(UnmapGroupByExpression(e, unmapContext)); + } + return @out; + } + + + private static void MapWhere(Expression whereClause, StatementSpecRaw raw, StatementSpecMapContext mapContext) + { + if (whereClause == null) + { + return; + } + var node = MapExpressionDeep(whereClause, mapContext); + raw.FilterExprRootNode = node; + } + + private static void UnmapWhere( + ExprNode filterRootNode, + EPStatementObjectModel model, + StatementSpecUnMapContext unmapContext) + { + if (filterRootNode == null) + { + return; + } + var expr = UnmapExpressionDeep(filterRootNode, unmapContext); + model.WhereClause = expr; + } + + private static void UnmapFrom( + IList streamSpecs, + IList outerJoinDescList, + EPStatementObjectModel model, + StatementSpecUnMapContext unmapContext) + { + var from = new FromClause(); + model.FromClause = from; + + foreach (var stream in streamSpecs) + { + Stream targetStream; + if (stream is FilterStreamSpecRaw) + { + var filterStreamSpec = (FilterStreamSpecRaw) stream; + var filter = UnmapFilter(filterStreamSpec.RawFilterSpec, unmapContext); + var filterStream = new FilterStream(filter, filterStreamSpec.OptionalStreamName); + UnmapStreamOpts(stream.Options, filterStream); + targetStream = filterStream; + } + else if (stream is DBStatementStreamSpec) + { + var db = (DBStatementStreamSpec) stream; + targetStream = new SQLStream( + db.DatabaseName, db.SqlWithSubsParams, db.OptionalStreamName, db.MetadataSQL); + } + else if (stream is PatternStreamSpecRaw) + { + var pattern = (PatternStreamSpecRaw) stream; + var patternExpr = UnmapPatternEvalDeep(pattern.EvalFactoryNode, unmapContext); + var annotationParts = PatternLevelAnnotationUtil.AnnotationsFromSpec(pattern); + var patternStream = new PatternStream(patternExpr, pattern.OptionalStreamName, annotationParts); + UnmapStreamOpts(stream.Options, patternStream); + targetStream = patternStream; + } + else if (stream is MethodStreamSpec) + { + var method = (MethodStreamSpec) stream; + var methodStream = new MethodInvocationStream( + method.ClassName, method.MethodName, method.OptionalStreamName); + foreach (var exprNode in method.Expressions) + { + var expr = UnmapExpressionDeep(exprNode, unmapContext); + methodStream.AddParameter(expr); + } + methodStream.OptionalEventTypeName = method.EventTypeName; + targetStream = methodStream; + } + else + { + throw new ArgumentException("Stream modelled by " + stream.GetType() + " cannot be unmapped"); + } + + if (targetStream is ProjectedStream) + { + var projStream = (ProjectedStream) targetStream; + foreach (var viewSpec in stream.ViewSpecs) + { + IList viewExpressions = UnmapExpressionDeep(viewSpec.ObjectParameters, unmapContext); + projStream.AddView(View.Create(viewSpec.ObjectNamespace, viewSpec.ObjectName, viewExpressions)); + } + } + from.Add(targetStream); + } + + foreach (var desc in outerJoinDescList) + { + PropertyValueExpression left = null; + PropertyValueExpression right = null; + var additionalProperties = new List(); + + if (desc.OptLeftNode != null) + { + left = (PropertyValueExpression) UnmapExpressionFlat(desc.OptLeftNode, unmapContext); + right = (PropertyValueExpression) UnmapExpressionFlat(desc.OptRightNode, unmapContext); + + if (desc.AdditionalLeftNodes != null) + { + for (var i = 0; i < desc.AdditionalLeftNodes.Length; i++) + { + var leftNode = desc.AdditionalLeftNodes[i]; + var rightNode = desc.AdditionalRightNodes[i]; + var propLeft = (PropertyValueExpression) UnmapExpressionFlat(leftNode, unmapContext); + var propRight = (PropertyValueExpression) UnmapExpressionFlat(rightNode, unmapContext); + additionalProperties.Add(new PropertyValueExpressionPair(propLeft, propRight)); + } + } + } + from.Add(new OuterJoinQualifier(desc.OuterJoinType, left, right, additionalProperties)); + } + } + + private static void UnmapStreamOpts(StreamSpecOptions options, ProjectedStream stream) + { + stream.IsUnidirectional = options.IsUnidirectional; + stream.IsRetainUnion = options.IsRetainUnion; + stream.IsRetainIntersection = options.IsRetainIntersection; + } + + private static StreamSpecOptions MapStreamOpts(ProjectedStream stream) + { + return new StreamSpecOptions(stream.IsUnidirectional, stream.IsRetainUnion, stream.IsRetainIntersection); + } + + private static SelectClause UnmapSelect( + SelectClauseSpecRaw selectClauseSpec, + SelectClauseStreamSelectorEnum selectStreamSelectorEnum, + StatementSpecUnMapContext unmapContext) + { + var clause = SelectClause.Create(); + clause.StreamSelector = SelectClauseStreamSelectorEnumExtensions.MapFromSODA(selectStreamSelectorEnum); + clause.AddElements(UnmapSelectClauseElements(selectClauseSpec.SelectExprList, unmapContext)); + clause.Distinct(selectClauseSpec.IsDistinct); + return clause; + } + + private static IList UnmapSelectClauseElements( + IList selectExprList, + StatementSpecUnMapContext unmapContext) + { + var elements = new List(); + foreach (var raw in selectExprList) + { + if (raw is SelectClauseStreamRawSpec) + { + var streamSpec = (SelectClauseStreamRawSpec) raw; + elements.Add(new SelectClauseStreamWildcard(streamSpec.StreamName, streamSpec.OptionalAsName)); + } + else if (raw is SelectClauseElementWildcard) + { + elements.Add(new SelectClauseWildcard()); + } + else if (raw is SelectClauseExprRawSpec) + { + var rawSpec = (SelectClauseExprRawSpec) raw; + var expression = UnmapExpressionDeep(rawSpec.SelectExpression, unmapContext); + var selectExpr = new SelectClauseExpression(expression, rawSpec.OptionalAsName); + selectExpr.IsAnnotatedByEventFlag = rawSpec.IsEvents; + elements.Add(selectExpr); + } + else + { + throw new IllegalStateException("Unexpected select clause element typed " + raw.GetType().FullName); + } + } + return elements; + } + + private static InsertIntoClause UnmapInsertInto(InsertIntoDesc insertIntoDesc) + { + if (insertIntoDesc == null) + { + return null; + } + StreamSelector selector = SelectClauseStreamSelectorEnumExtensions.MapFromSODA( + insertIntoDesc.StreamSelector); + return InsertIntoClause.Create( + insertIntoDesc.EventTypeName, + insertIntoDesc.ColumnNames.ToArray(), selector); + } + + private static void MapCreateContext( + CreateContextClause createContext, + StatementSpecRaw raw, + StatementSpecMapContext mapContext) + { + if (createContext == null) + { + return; + } + + var detail = MapCreateContextDetail(createContext.Descriptor, mapContext); + + var desc = new CreateContextDesc(createContext.ContextName, detail); + raw.CreateContextDesc = desc; + } + + private static ContextDetail MapCreateContextDetail( + ContextDescriptor descriptor, + StatementSpecMapContext mapContext) + { + ContextDetail detail; + if (descriptor is ContextDescriptorInitiatedTerminated) + { + var desc = (ContextDescriptorInitiatedTerminated) descriptor; + var start = MapCreateContextRangeCondition(desc.StartCondition, mapContext); + var end = MapCreateContextRangeCondition(desc.EndCondition, mapContext); + ExprNode[] distinctExpressions = null; + if (desc.OptionalDistinctExpressions != null && desc.OptionalDistinctExpressions.Count > 0) + { + distinctExpressions = + ExprNodeUtility.ToArray(MapExpressionDeep(desc.OptionalDistinctExpressions, mapContext)); + } + detail = new ContextDetailInitiatedTerminated(start, end, desc.IsOverlapping, distinctExpressions); + } + else if (descriptor is ContextDescriptorKeyedSegmented) + { + var seg = (ContextDescriptorKeyedSegmented) descriptor; + var itemsdesc = new List(); + foreach (var item in seg.Items) + { + var rawSpec = MapFilter(item.Filter, mapContext); + itemsdesc.Add(new ContextDetailPartitionItem(rawSpec, item.PropertyNames)); + } + detail = new ContextDetailPartitioned(itemsdesc); + } + else if (descriptor is ContextDescriptorCategory) + { + var cat = (ContextDescriptorCategory) descriptor; + var rawSpec = MapFilter(cat.Filter, mapContext); + var itemsdesc = new List(); + foreach (var item in cat.Items) + { + var expr = MapExpressionDeep(item.Expression, mapContext); + itemsdesc.Add(new ContextDetailCategoryItem(expr, item.Label)); + } + detail = new ContextDetailCategory(itemsdesc, rawSpec); + } + else if (descriptor is ContextDescriptorHashSegmented) + { + var hash = (ContextDescriptorHashSegmented) descriptor; + var itemsdesc = new List(); + foreach (var item in hash.Items) + { + var rawSpec = MapFilter(item.Filter, mapContext); + var singleRowMethodExpression = (SingleRowMethodExpression) item.HashFunction; + var func = MapChains(Collections.SingletonList(singleRowMethodExpression.Chain[0]), mapContext)[0]; + itemsdesc.Add(new ContextDetailHashItem(func, rawSpec)); + } + detail = new ContextDetailHash(itemsdesc, hash.Granularity, hash.IsPreallocate); + } + else + { + var nested = (ContextDescriptorNested) descriptor; + var itemsdesc = new List(); + foreach (var item in nested.Contexts) + { + itemsdesc.Add( + new CreateContextDesc(item.ContextName, MapCreateContextDetail(item.Descriptor, mapContext))); + } + detail = new ContextDetailNested(itemsdesc); + } + return detail; + } + + private static ContextDetailCondition MapCreateContextRangeCondition( + ContextDescriptorCondition condition, + StatementSpecMapContext mapContext) + { + if (condition is ContextDescriptorConditionCrontab) + { + var crontab = (ContextDescriptorConditionCrontab) condition; + IList expr = MapExpressionDeep(crontab.CrontabExpressions, mapContext); + return new ContextDetailConditionCrontab(expr, crontab.IsNow); + } + else if (condition is ContextDescriptorConditionFilter) + { + var filter = (ContextDescriptorConditionFilter) condition; + var filterExpr = MapFilter(filter.Filter, mapContext); + return new ContextDetailConditionFilter(filterExpr, filter.OptionalAsName); + } + if (condition is ContextDescriptorConditionPattern) + { + var pattern = (ContextDescriptorConditionPattern) condition; + var patternExpr = MapPatternEvalDeep(pattern.Pattern, mapContext); + return new ContextDetailConditionPattern(patternExpr, pattern.IsInclusive, pattern.IsNow); + } + if (condition is ContextDescriptorConditionTimePeriod) + { + var timePeriod = (ContextDescriptorConditionTimePeriod) condition; + var expr = MapExpressionDeep(timePeriod.TimePeriod, mapContext); + return new ContextDetailConditionTimePeriod((ExprTimePeriod) expr, timePeriod.IsNow); + } + if (condition is ContextDescriptorConditionImmediate) + { + return ContextDetailConditionImmediate.INSTANCE; + } + if (condition is ContextDescriptorConditionNever) + { + return ContextDetailConditionNever.INSTANCE; + } + throw new IllegalStateException("Unrecognized condition " + condition); + } + + private static void MapCreateWindow( + CreateWindowClause createWindow, + FromClause fromClause, + StatementSpecRaw raw, + StatementSpecMapContext mapContext) + { + if (createWindow == null) + { + return; + } + + ExprNode insertFromWhereExpr = null; + if (createWindow.InsertWhereClause != null) + { + insertFromWhereExpr = MapExpressionDeep(createWindow.InsertWhereClause, mapContext); + } + var columns = MapColumns(createWindow.Columns); + + string asEventTypeName = null; + if (fromClause != null && !fromClause.Streams.IsEmpty() && fromClause.Streams[0] is FilterStream) + { + asEventTypeName = ((FilterStream) fromClause.Streams[0]).Filter.EventTypeName; + } + raw.CreateWindowDesc = new CreateWindowDesc( + createWindow.WindowName, + MapViews(createWindow.Views, mapContext), + StreamSpecOptions.DEFAULT, createWindow.IsInsert, insertFromWhereExpr, columns, asEventTypeName); + } + + private static void MapCreateIndex( + CreateIndexClause clause, + StatementSpecRaw raw, + StatementSpecMapContext mapContext) + { + if (clause == null) + { + return; + } + + var cols = clause.Columns + .Select(col => new CreateIndexItem(col.ColumnName, col.IndexColumnType.Xlate())) + .ToList(); + + var desc = new CreateIndexDesc(clause.IsUnique, clause.IndexName, clause.WindowName, cols); + raw.CreateIndexDesc = desc; + } + + private static void MapUpdateClause( + UpdateClause updateClause, + StatementSpecRaw raw, + StatementSpecMapContext mapContext) + { + if (updateClause == null) + { + return; + } + var assignments = new List(); + foreach (var pair in updateClause.Assignments) + { + var expr = MapExpressionDeep(pair.Value, mapContext); + assignments.Add(new OnTriggerSetAssignment(expr)); + } + ExprNode whereClause = null; + if (updateClause.OptionalWhereClause != null) + { + whereClause = MapExpressionDeep(updateClause.OptionalWhereClause, mapContext); + } + var desc = new UpdateDesc(updateClause.OptionalAsClauseStreamName, assignments, whereClause); + raw.UpdateDesc = desc; + var filterSpecRaw = new FilterSpecRaw(updateClause.EventType, Collections.GetEmptyList(), null); + raw.StreamSpecs.Add( + new FilterStreamSpecRaw(filterSpecRaw, ViewSpec.EMPTY_VIEWSPEC_ARRAY, null, StreamSpecOptions.DEFAULT)); + } + + private static void MapCreateVariable( + CreateVariableClause createVariable, + StatementSpecRaw raw, + StatementSpecMapContext mapContext) + { + if (createVariable == null) + { + return; + } + + ExprNode assignment = null; + if (createVariable.OptionalAssignment != null) + { + assignment = MapExpressionDeep(createVariable.OptionalAssignment, mapContext); + } + raw.CreateVariableDesc = new CreateVariableDesc( + createVariable.VariableType, createVariable.VariableName, assignment, createVariable.IsConstant, + createVariable.IsArray, createVariable.IsArrayOfPrimitive); + } + + private static void MapCreateTable( + CreateTableClause createTable, + StatementSpecRaw raw, + StatementSpecMapContext mapContext) + { + if (createTable == null) + { + return; + } + + var cols = new List(); + foreach (var desc in createTable.Columns) + { + var optNode = desc.OptionalExpression != null + ? MapExpressionDeep(desc.OptionalExpression, mapContext) + : null; + var annotations = MapAnnotations(desc.Annotations); + cols.Add( + new CreateTableColumn( + desc.ColumnName, optNode, desc.OptionalTypeName, desc.OptionalTypeIsArray, + desc.OptionalTypeIsPrimitiveArray, annotations, desc.PrimaryKey)); + } + + var agg = new CreateTableDesc(createTable.TableName, cols); + raw.CreateTableDesc = agg; + } + + private static void MapCreateSchema( + CreateSchemaClause clause, + StatementSpecRaw raw, + StatementSpecMapContext mapContext) + { + if (clause == null) + { + return; + } + var desc = MapCreateSchemaInternal(clause, raw, mapContext); + raw.CreateSchemaDesc = desc; + } + + private static void MapCreateExpression( + CreateExpressionClause clause, + StatementSpecRaw raw, + StatementSpecMapContext mapContext) + { + if (clause == null) + { + return; + } + + CreateExpressionDesc desc; + if (clause.ExpressionDeclaration != null) + { + var item = MapExpressionDeclItem(clause.ExpressionDeclaration, mapContext); + desc = new CreateExpressionDesc(item); + } + else + { + var item = MapScriptExpression(clause.ScriptExpression, mapContext); + desc = new CreateExpressionDesc(item); + } + raw.CreateExpressionDesc = desc; + } + + private static CreateSchemaDesc MapCreateSchemaInternal( + CreateSchemaClause clause, + StatementSpecRaw raw, + StatementSpecMapContext mapContext) + { + var columns = MapColumns(clause.Columns); + return new CreateSchemaDesc( + clause.SchemaName, clause.Types, columns, clause.Inherits, + AssignedTypeExtensions.MapFrom(clause.TypeDefinition.Value), + clause.StartTimestampPropertyName, + clause.EndTimestampPropertyName, clause.CopyFrom); + } + + private static void MapCreateGraph( + CreateDataFlowClause clause, + StatementSpecRaw raw, + StatementSpecMapContext mapContext) + { + if (clause == null) + { + return; + } + + var schemas = new List(); + foreach (var schema in clause.Schemas) + { + schemas.Add(MapCreateSchemaInternal(schema, raw, mapContext)); + } + + var ops = new List(); + foreach (var op in clause.Operators) + { + ops.Add(MapGraphOperator(op, mapContext)); + } + + var desc = new CreateDataFlowDesc(clause.DataFlowName, ops, schemas); + raw.CreateDataFlowDesc = desc; + } + + private static GraphOperatorSpec MapGraphOperator(DataFlowOperator op, StatementSpecMapContext mapContext) + { + var annotations = MapAnnotations(op.Annotations); + + var input = new GraphOperatorInput(); + foreach (DataFlowOperatorInput @in in op.Input) + { + input.StreamNamesAndAliases.Add( + new GraphOperatorInputNamesAlias(@in.InputStreamNames.ToArray(), @in.OptionalAsName)); + } + + var output = new GraphOperatorOutput(); + foreach (var @out in op.Output) + { + output.Items.Add(new GraphOperatorOutputItem(@out.StreamName, MapGraphOpType(@out.TypeInfo))); + } + + var detail = new LinkedHashMap(); + foreach (var entry in op.Parameters) + { + var value = entry.ParameterValue; + if (value is EPStatementObjectModel) + { + value = Map((EPStatementObjectModel) value, mapContext); + } + else if (value is Expression) + { + value = MapExpressionDeep((Expression) value, mapContext); + } + else + { + // no action + } + detail.Put(entry.ParameterName, value); + } + + return new GraphOperatorSpec(op.OperatorName, input, output, new GraphOperatorDetail(detail), annotations); + } + + private static IList MapGraphOpType(IList typeInfos) + { + if (typeInfos == null) + { + return Collections.GetEmptyList(); + } + var types = new List(); + foreach (var info in typeInfos) + { + var type = new GraphOperatorOutputItemType( + info.IsWildcard, info.TypeOrClassname, MapGraphOpType(info.TypeParameters)); + types.Add(type); + } + return types; + } + + private static IList MapColumns(IList columns) + { + if (columns == null) + { + return null; + } + var result = new List(); + foreach (var col in columns) + { + result.Add(new ColumnDesc(col.Name, col.Type, col.IsArray, col.IsPrimitiveArray)); + } + return result; + } + + private static IList UnmapColumns(IList columns) + { + if (columns == null) + { + return null; + } + var result = new List(); + foreach (var col in columns) + { + result.Add(new SchemaColumnDesc(col.Name, col.Type, col.IsArray, col.IsPrimitiveArray)); + } + return result; + } + + private static InsertIntoDesc MapInsertInto(InsertIntoClause insertInto) + { + if (insertInto == null) + { + return null; + } + + var eventTypeName = insertInto.StreamName; + var desc = + new InsertIntoDesc( + SelectClauseStreamSelectorEnumExtensions.MapFromSODA(insertInto.StreamSelector), eventTypeName); + + foreach (var name in insertInto.ColumnNames) + { + desc.Add(name); + } + return desc; + } + + private static void MapSelect( + SelectClause selectClause, + StatementSpecRaw raw, + StatementSpecMapContext mapContext) + { + if (selectClause == null) + { + return; + } + var spec = MapSelectRaw(selectClause, mapContext); + raw.SelectStreamDirEnum = SelectClauseStreamSelectorEnumExtensions.MapFromSODA(selectClause.StreamSelector); + raw.SelectClauseSpec = spec; + } + + private static IList MapSelectClauseElements( + IList elements, + StatementSpecMapContext mapContext) + { + var result = new List(); + foreach (var element in elements) + { + if (element is SelectClauseWildcard) + { + result.Add(new SelectClauseElementWildcard()); + } + else if (element is SelectClauseExpression) + { + var selectExpr = (SelectClauseExpression) element; + var expr = selectExpr.Expression; + var exprNode = MapExpressionDeep(expr, mapContext); + var rawElement = new SelectClauseExprRawSpec( + exprNode, selectExpr.AsName, selectExpr.IsAnnotatedByEventFlag); + result.Add(rawElement); + } + else if (element is SelectClauseStreamWildcard) + { + var streamWild = (SelectClauseStreamWildcard) element; + var rawElement = new SelectClauseStreamRawSpec(streamWild.StreamName, streamWild.OptionalColumnName); + result.Add(rawElement); + } + } + return result; + } + + private static SelectClauseSpecRaw MapSelectRaw(SelectClause selectClause, StatementSpecMapContext mapContext) + { + var spec = new SelectClauseSpecRaw(); + spec.AddAll(MapSelectClauseElements(selectClause.SelectList, mapContext)); + spec.IsDistinct = selectClause.IsDistinct; + return spec; + } + + private static Expression UnmapExpressionDeep(ExprNode exprNode, StatementSpecUnMapContext unmapContext) + { + if (exprNode == null) + { + return null; + } + var parent = UnmapExpressionFlat(exprNode, unmapContext); + UnmapExpressionRecursive(parent, exprNode, unmapContext); + return parent; + } + + private static IList MapExpressionDeep( + IList expressions, + StatementSpecMapContext mapContext) + { + var result = new List(); + if (expressions == null) + { + return result; + } + foreach (var expr in expressions) + { + if (expr == null) + { + result.Add(null); + continue; + } + result.Add(MapExpressionDeep(expr, mapContext)); + } + return result; + } + + private static MatchRecognizeRegEx UnmapExpressionDeepRowRegex( + RowRegexExprNode exprNode, + StatementSpecUnMapContext unmapContext) + { + var parent = UnmapExpressionFlatRowregex(exprNode, unmapContext); + UnmapExpressionRecursiveRowregex(parent, exprNode, unmapContext); + return parent; + } + + private static ExprNode MapExpressionDeep(Expression expr, StatementSpecMapContext mapContext) + { + if (expr == null) + { + return null; + } + var parent = MapExpressionFlat(expr, mapContext); + MapExpressionRecursive(parent, expr, mapContext); + return parent; + } + + private static RowRegexExprNode MapExpressionDeepRowRegex( + MatchRecognizeRegEx expr, + StatementSpecMapContext mapContext) + { + var parent = MapExpressionFlatRowregex(expr, mapContext); + MapExpressionRecursiveRowregex(parent, expr, mapContext); + return parent; + } + + private static ExprNode MapExpressionFlat(Expression expr, StatementSpecMapContext mapContext) + { + if (expr == null) + { + throw new ArgumentException("Null expression parameter"); + } + if (expr is ArithmaticExpression) + { + var arith = (ArithmaticExpression) expr; + return new ExprMathNode( + MathArithTypeEnumExtensions.ParseOperator(arith.Operator), + mapContext.Configuration.EngineDefaults.Expression.IsIntegerDivision, + mapContext.Configuration.EngineDefaults.Expression.IsDivisionByZeroReturnsNull); + } + else if (expr is PropertyValueExpression) + { + var prop = (PropertyValueExpression) expr; + var indexDot = ASTUtil.UnescapedIndexOfDot(prop.PropertyName); + + // handle without nesting + if (indexDot == -1) + { + + // maybe table + if (mapContext.TableService.GetTableMetadata(prop.PropertyName) != null) + { + var tableNodeX = new ExprTableAccessNodeTopLevel(prop.PropertyName); + mapContext.TableExpressions.Add(tableNodeX); + return tableNodeX; + } + + // maybe variable + var variableMetaDataX = mapContext.VariableService.GetVariableMetaData(prop.PropertyName); + if (variableMetaDataX != null) + { + mapContext.HasVariables = true; + var node = new ExprVariableNodeImpl(variableMetaDataX, null); + mapContext.VariableNames.Add(variableMetaDataX.VariableName); + var message = VariableServiceUtil.CheckVariableContextName( + mapContext.ContextName, variableMetaDataX); + if (message != null) + { + throw new EPException(message); + } + return node; + } + + return new ExprIdentNodeImpl(prop.PropertyName); + } + + var stream = prop.PropertyName.Substring(0, indexDot); + var property = prop.PropertyName.Substring(indexDot + 1); + + var tableNode = ASTTableExprHelper.CheckTableNameGetExprForSubproperty( + mapContext.TableService, stream, property); + if (tableNode != null) + { + mapContext.TableExpressions.Add(tableNode.First); + return tableNode.First; + } + + var variableMetaData = mapContext.VariableService.GetVariableMetaData(stream); + if (variableMetaData != null) + { + mapContext.HasVariables = true; + var node = new ExprVariableNodeImpl(variableMetaData, property); + mapContext.VariableNames.Add(variableMetaData.VariableName); + var message = VariableServiceUtil.CheckVariableContextName(mapContext.ContextName, variableMetaData); + if (message != null) + { + throw new EPException(message); + } + return node; + } + + if (mapContext.ContextName != null) + { + var contextDescriptor = + mapContext.ContextManagementService.GetContextDescriptor(mapContext.ContextName); + if (contextDescriptor != null && + contextDescriptor.ContextPropertyRegistry.IsContextPropertyPrefix(stream)) + { + return new ExprContextPropertyNode(property); + } + } + + return new ExprIdentNodeImpl(property, stream); + } + else if (expr is Conjunction) + { + return new ExprAndNodeImpl(); + } + else if (expr is Disjunction) + { + return new ExprOrNode(); + } + else if (expr is RelationalOpExpression) + { + var op = (RelationalOpExpression) expr; + if (op.Operator.Equals("=")) + { + return new ExprEqualsNodeImpl(false, false); + } + else if (op.Operator.Equals("!=")) + { + return new ExprEqualsNodeImpl(true, false); + } + else if (op.Operator.ToUpperInvariant().Trim().Equals("IS")) + { + return new ExprEqualsNodeImpl(false, true); + } + else if (op.Operator.ToUpperInvariant().Trim().Equals("IS NOT")) + { + return new ExprEqualsNodeImpl(true, true); + } + else + { + return new ExprRelationalOpNodeImpl(RelationalOpEnumExtensions.Parse(op.Operator)); + } + } + else if (expr is ConstantExpression) + { + var op = (ConstantExpression) expr; + Type constantType = null; + if (op.ConstantType != null) + { + try + { + constantType = mapContext.EngineImportService.GetClassForNameProvider().ClassForName(op.ConstantType); + } + catch (TypeLoadException e) + { + throw new EPException( + "Error looking up class name '" + op.ConstantType + "' to resolve as constant type", e); + } + } + return new ExprConstantNodeImpl(op.Constant, constantType); + } + else if (expr is ConcatExpression) + { + return new ExprConcatNode(); + } + else if (expr is SubqueryExpression) + { + var sub = (SubqueryExpression) expr; + var rawSubselect = Map(sub.Model, mapContext); + return new ExprSubselectRowNode(rawSubselect); + } + else if (expr is SubqueryInExpression) + { + var sub = (SubqueryInExpression) expr; + var rawSubselect = Map(sub.Model, mapContext); + var inSub = new ExprSubselectInNode(rawSubselect, sub.IsNotIn); + return inSub; + } + else if (expr is SubqueryExistsExpression) + { + var sub = (SubqueryExistsExpression) expr; + var rawSubselect = Map(sub.Model, mapContext); + return new ExprSubselectExistsNode(rawSubselect); + } + else if (expr is SubqueryQualifiedExpression) + { + var sub = (SubqueryQualifiedExpression) expr; + var rawSubselect = Map(sub.Model, mapContext); + var isNot = false; + RelationalOpEnum? relop = null; + if (sub.Operator.Equals("!=")) + { + isNot = true; + } + if (sub.Operator.Equals("=")) + { + } + else + { + relop = RelationalOpEnumExtensions.Parse(sub.Operator); + } + return new ExprSubselectAllSomeAnyNode(rawSubselect, isNot, sub.IsAll, relop); + } + else if (expr is CountStarProjectionExpression) + { + return new ExprCountNode(false); + } + else if (expr is CountProjectionExpression) + { + var count = (CountProjectionExpression) expr; + return new ExprCountNode(count.IsDistinct); + } + else if (expr is AvgProjectionExpression) + { + var avg = (AvgProjectionExpression) expr; + return new ExprAvgNode(avg.IsDistinct); + } + else if (expr is SumProjectionExpression) + { + var avg = (SumProjectionExpression) expr; + return new ExprSumNode(avg.IsDistinct); + } + else if (expr is BetweenExpression) + { + var between = (BetweenExpression) expr; + return new ExprBetweenNodeImpl( + between.IsLowEndpointIncluded, between.IsHighEndpointIncluded, between.IsNotBetween); + } + else if (expr is PriorExpression) + { + return new ExprPriorNode(); + } + else if (expr is PreviousExpression) + { + var prev = (PreviousExpression) expr; + return + new ExprPreviousNode(EnumHelper.Parse(prev.ExpressionType.ToString())); + } + else if (expr is StaticMethodExpression) + { + var method = (StaticMethodExpression) expr; + var chained = MapChains(method.Chain, mapContext); + chained.Insert(0, new ExprChainedSpec(method.ClassName, Collections.GetEmptyList(), false)); + return new ExprDotNodeImpl( + chained, + mapContext.Configuration.EngineDefaults.Expression.IsDuckTyping, + mapContext.Configuration.EngineDefaults.Expression.IsUdfCache); + } + else if (expr is MinProjectionExpression) + { + var method = (MinProjectionExpression) expr; + return new ExprMinMaxAggrNode( + method.IsDistinct, MinMaxTypeEnum.MIN, expr.Children.Count > 1, method.IsEver); + } + else if (expr is MaxProjectionExpression) + { + var method = (MaxProjectionExpression) expr; + return new ExprMinMaxAggrNode( + method.IsDistinct, MinMaxTypeEnum.MAX, expr.Children.Count > 1, method.IsEver); + } + else if (expr is NotExpression) + { + return new ExprNotNode(); + } + else if (expr is InExpression) + { + var inExpr = (InExpression) expr; + return new ExprInNodeImpl(inExpr.IsNotIn); + } + else if (expr is CoalesceExpression) + { + return new ExprCoalesceNode(); + } + else if (expr is CaseWhenThenExpression) + { + return new ExprCaseNode(false); + } + else if (expr is CaseSwitchExpression) + { + return new ExprCaseNode(true); + } + else if (expr is MaxRowExpression) + { + return new ExprMinMaxRowNode(MinMaxTypeEnum.MAX); + } + else if (expr is MinRowExpression) + { + return new ExprMinMaxRowNode(MinMaxTypeEnum.MIN); + } + else if (expr is BitwiseOpExpression) + { + var bit = (BitwiseOpExpression) expr; + return new ExprBitWiseNode(bit.BinaryOp); + } + else if (expr is ArrayExpression) + { + return new ExprArrayNode(); + } + else if (expr is LikeExpression) + { + var like = (LikeExpression) expr; + return new ExprLikeNode(like.IsNot); + } + else if (expr is RegExpExpression) + { + var regexp = (RegExpExpression) expr; + return new ExprRegexpNode(regexp.IsNot); + } + else if (expr is MedianProjectionExpression) + { + var median = (MedianProjectionExpression) expr; + return new ExprMedianNode(median.IsDistinct); + } + else if (expr is AvedevProjectionExpression) + { + var node = (AvedevProjectionExpression) expr; + return new ExprAvedevNode(node.IsDistinct); + } + else if (expr is StddevProjectionExpression) + { + var node = (StddevProjectionExpression) expr; + return new ExprStddevNode(node.IsDistinct); + } + else if (expr is LastEverProjectionExpression) + { + var node = (LastEverProjectionExpression) expr; + return new ExprLastEverNode(node.IsDistinct); + } + else if (expr is FirstEverProjectionExpression) + { + var node = (FirstEverProjectionExpression) expr; + return new ExprFirstEverNode(node.IsDistinct); + } + else if (expr is CountEverProjectionExpression) + { + var node = (CountEverProjectionExpression) expr; + return new ExprCountEverNode(node.IsDistinct); + } + else if (expr is InstanceOfExpression) + { + var node = (InstanceOfExpression) expr; + return new ExprInstanceofNode(node.TypeNames); + } + else if (expr is TypeOfExpression) + { + return new ExprTypeofNode(); + } + else if (expr is CastExpression) + { + var node = (CastExpression) expr; + return new ExprCastNode(node.TypeName); + } + else if (expr is PropertyExistsExpression) + { + return new ExprPropertyExistsNode(); + } + else if (expr is CurrentTimestampExpression) + { + return new ExprTimestampNode(); + } + else if (expr is CurrentEvaluationContextExpression) + { + return new ExprCurrentEvaluationContextNode(); + } + else if (expr is IStreamBuiltinExpression) + { + return new ExprIStreamNode(); + } + else if (expr is TimePeriodExpression) + { + var tpe = (TimePeriodExpression) expr; + return new ExprTimePeriodImpl( + mapContext.Configuration.EngineDefaults.Expression.TimeZone, + tpe.HasYears, tpe.HasMonths, tpe.HasWeeks, tpe.HasDays, tpe.HasHours, tpe.HasMinutes, tpe.HasSeconds, + tpe.HasMilliseconds, tpe.HasMicroseconds, + mapContext.EngineImportService.TimeAbacus); + } + else if (expr is NewOperatorExpression) + { + var noe = (NewOperatorExpression) expr; + return new ExprNewStructNode(noe.ColumnNames.ToArray()); + } + else if (expr is NewInstanceOperatorExpression) + { + var noe = (NewInstanceOperatorExpression) expr; + return new ExprNewInstanceNode(noe.ClassName); + } + else if (expr is CompareListExpression) + { + var exp = (CompareListExpression) expr; + if ((exp.Operator.Equals("=")) || (exp.Operator.Equals("!="))) + { + return new ExprEqualsAllAnyNode(exp.Operator.Equals("!="), exp.IsAll); + } + else + { + return new ExprRelationalOpAllAnyNode(RelationalOpEnumExtensions.Parse(exp.Operator), exp.IsAll); + } + } + else if (expr is SubstitutionParameterExpressionBase) + { + var node = (SubstitutionParameterExpressionBase) expr; + if (!(node.IsSatisfied)) + { + if (node is SubstitutionParameterExpressionIndexed) + { + var indexed = (SubstitutionParameterExpressionIndexed) node; + throw new EPException( + "Substitution parameter value for index " + indexed.Index + + " not set, please provide a value for this parameter"); + } + var named = (SubstitutionParameterExpressionNamed) node; + throw new EPException( + "Substitution parameter value for name '" + named.Name + + "' not set, please provide a value for this parameter"); + } + return new ExprConstantNodeImpl(node.Constant); + } + else if (expr is SingleRowMethodExpression) + { + var single = (SingleRowMethodExpression) expr; + if ((single.Chain == null) || (single.Chain.Count == 0)) + { + throw new ArgumentException("Single row method expression requires one or more method calls"); + } + var chain = MapChains(single.Chain, mapContext); + var functionName = chain[0].Name; + Pair pair; + try + { + pair = mapContext.EngineImportService.ResolveSingleRow(functionName); + } + catch (Exception e) + { + throw new ArgumentException( + "Function name '" + functionName + "' cannot be resolved to a single-row function: " + e.Message, + e); + } + chain[0].Name = pair.Second.MethodName; + return new ExprPlugInSingleRowNode(functionName, pair.First, chain, pair.Second); + } + else if (expr is PlugInProjectionExpression) + { + var node = (PlugInProjectionExpression) expr; + var exprNode = ASTAggregationHelper.TryResolveAsAggregation( + mapContext.EngineImportService, node.IsDistinct, node.FunctionName, mapContext.PlugInAggregations, + mapContext.EngineURI); + if (exprNode == null) + { + throw new EPException("Error resolving aggregation function named '" + node.FunctionName + "'"); + } + return exprNode; + } + else if (expr is OrderedObjectParamExpression) + { + var order = (OrderedObjectParamExpression) expr; + return new ExprOrderedExpr(order.IsDescending); + } + else if (expr is CrontabFrequencyExpression) + { + return new ExprNumberSetFrequency(); + } + else if (expr is CrontabRangeExpression) + { + return new ExprNumberSetRange(); + } + else if (expr is CrontabParameterSetExpression) + { + return new ExprNumberSetList(); + } + else if (expr is CrontabParameterExpression) + { + var cronParam = (CrontabParameterExpression) expr; + if (cronParam.ItemType == ScheduleItemType.WILDCARD) + { + return new ExprWildcardImpl(); + } + CronOperatorEnum @operator; + switch (cronParam.ItemType) + { + case ScheduleItemType.LASTDAY: + @operator = CronOperatorEnum.LASTDAY; + break; + case ScheduleItemType.WEEKDAY: + @operator = CronOperatorEnum.WEEKDAY; + break; + case ScheduleItemType.LASTWEEKDAY: + @operator = CronOperatorEnum.LASTWEEKDAY; + break; + default: + throw new ArgumentException("Cron parameter not recognized: " + cronParam.ItemType); + } + return new ExprNumberSetCronParam(@operator); + } + else if (expr is AccessProjectionExpressionBase) + { + var theBase = (AccessProjectionExpressionBase) expr; + AggregationStateType type; + if (expr is FirstProjectionExpression) + { + type = AggregationStateType.FIRST; + } + else if (expr is LastProjectionExpression) + { + type = AggregationStateType.LAST; + } + else + { + type = AggregationStateType.WINDOW; + } + return new ExprAggMultiFunctionLinearAccessNode(type); + } + else if (expr is DotExpression) + { + var theBase = (DotExpression) expr; + var chain = MapChains(theBase.Chain, mapContext); + + // determine table use + var workChain = new List(chain); + var tableNameCandidate = workChain[0].Name; + Pair> pair = + ASTTableExprHelper.CheckTableNameGetLibFunc( + mapContext.TableService, mapContext.EngineImportService, mapContext.PlugInAggregations, + mapContext.EngineURI, tableNameCandidate, workChain); + if (pair != null) + { + mapContext.TableExpressions.Add(pair.First); + return pair.First; + } + + if (chain.Count == 1) + { + var name = chain[0].Name; + var declared = ExprDeclaredHelper.GetExistsDeclaredExpr( + name, chain[0].Parameters, mapContext.ExpressionDeclarations.Values, + mapContext.ExprDeclaredService, mapContext.ContextDescriptor); + if (declared != null) + { + return declared; + } + var script = ExprDeclaredHelper.GetExistsScript( + mapContext.Configuration.EngineDefaults.Scripts.DefaultDialect, + name, chain[0].Parameters, mapContext.Scripts.Values, mapContext.ExprDeclaredService); + if (script != null) + { + return script; + } + } + var dotNode = new ExprDotNodeImpl( + chain, + mapContext.Configuration.EngineDefaults.Expression.IsDuckTyping, + mapContext.Configuration.EngineDefaults.Expression.IsUdfCache); + if (dotNode.IsVariableOpGetName(mapContext.VariableService) != null) + { + mapContext.HasVariables = true; + } + return dotNode; + } + else if (expr is LambdaExpression) + { + var theBase = (LambdaExpression) expr; + return new ExprLambdaGoesNode(new List(theBase.Parameters)); + } + else if (expr is StreamWildcardExpression) + { + var sw = (StreamWildcardExpression) expr; + return new ExprStreamUnderlyingNodeImpl(sw.StreamName, true); + } + else if (expr is GroupingExpression) + { + return new ExprGroupingNode(); + } + else if (expr is GroupingIdExpression) + { + return new ExprGroupingIdNode(); + } + else if (expr is TableAccessExpression) + { + var b = (TableAccessExpression) expr; + ExprTableAccessNode tableNode; + if (b.OptionalAggregate != null) + { + var exprNode = MapExpressionDeep(b.OptionalAggregate, mapContext); + tableNode = new ExprTableAccessNodeSubpropAccessor(b.TableName, b.OptionalColumn, exprNode); + } + else if (b.OptionalColumn != null) + { + tableNode = new ExprTableAccessNodeSubprop(b.TableName, b.OptionalColumn); + } + else + { + tableNode = new ExprTableAccessNodeTopLevel(b.TableName); + } + mapContext.TableExpressions.Add(tableNode); + return tableNode; + } + else if (expr is WildcardExpression) + { + return new ExprWildcardImpl(); + } + else if (expr is NamedParameterExpression) + { + var named = (NamedParameterExpression) expr; + return new ExprNamedParameterNodeImpl(named.Name); + } + throw new ArgumentException("Could not map expression node of type " + expr.GetType().Name); + } + + private static IList UnmapExpressionDeep( + IList expressions, + StatementSpecUnMapContext unmapContext) + { + var result = new List(); + if (expressions == null) + { + return result; + } + foreach (var expr in expressions) + { + if (expr == null) + { + result.Add(null); + continue; + } + result.Add(UnmapExpressionDeep(expr, unmapContext)); + } + return result; + } + + private static MatchRecognizeRegEx UnmapExpressionFlatRowregex( + RowRegexExprNode expr, + StatementSpecUnMapContext unmapContext) + { + if (expr is RowRegexExprNodeAlteration) + { + return new MatchRecognizeRegExAlteration(); + } + else if (expr is RowRegexExprNodeAtom) + { + var atom = (RowRegexExprNodeAtom) expr; + var repeat = UnmapRowRegexRepeat(atom.OptionalRepeat, unmapContext); + return new MatchRecognizeRegExAtom( + atom.Tag, atom.NFAType.Xlate(), repeat); + } + else if (expr is RowRegexExprNodeConcatenation) + { + return new MatchRecognizeRegExConcatenation(); + } + else if (expr is RowRegexExprNodePermute) + { + return new MatchRecognizeRegExPermutation(); + } + else + { + var nested = (RowRegexExprNodeNested) expr; + var repeat = UnmapRowRegexRepeat(nested.OptionalRepeat, unmapContext); + return new MatchRecognizeRegExNested(nested.NFAType.Xlate(), repeat); + } + } + + private static MatchRecognizeRegExRepeat UnmapRowRegexRepeat( + RowRegexExprRepeatDesc optionalRepeat, + StatementSpecUnMapContext unmapContext) + { + if (optionalRepeat == null) + { + return null; + } + return new MatchRecognizeRegExRepeat( + UnmapExpressionDeep(optionalRepeat.Lower, unmapContext), + UnmapExpressionDeep(optionalRepeat.Upper, unmapContext), + UnmapExpressionDeep(optionalRepeat.Single, unmapContext) + ); + } + + private static RowRegexExprNode MapExpressionFlatRowregex( + MatchRecognizeRegEx expr, + StatementSpecMapContext mapContext) + { + if (expr is MatchRecognizeRegExAlteration) + { + return new RowRegexExprNodeAlteration(); + } + else if (expr is MatchRecognizeRegExAtom) + { + var atom = (MatchRecognizeRegExAtom) expr; + var repeat = MapRowRegexRepeat(atom.OptionalRepeat, mapContext); + return new RowRegexExprNodeAtom(atom.Name, atom.ElementType.Xlate(), repeat); + } + else if (expr is MatchRecognizeRegExConcatenation) + { + return new RowRegexExprNodeConcatenation(); + } + else if (expr is MatchRecognizeRegExPermutation) + { + return new RowRegexExprNodePermute(); + } + else + { + var nested = (MatchRecognizeRegExNested) expr; + var repeat = MapRowRegexRepeat(nested.OptionalRepeat, mapContext); + return new RowRegexExprNodeNested(nested.ElementType.Xlate(), repeat); + } + } + + private static RowRegexExprRepeatDesc MapRowRegexRepeat( + MatchRecognizeRegExRepeat optionalRepeat, + StatementSpecMapContext mapContext) + { + if (optionalRepeat == null) + { + return null; + } + return new RowRegexExprRepeatDesc( + MapExpressionDeep(optionalRepeat.Low, mapContext), + MapExpressionDeep(optionalRepeat.High, mapContext), + MapExpressionDeep(optionalRepeat.Single, mapContext) + ); + } + + private static Expression UnmapExpressionFlat(ExprNode expr, StatementSpecUnMapContext unmapContext) + { + if (expr is ExprMathNode) + { + var math = (ExprMathNode) expr; + return new ArithmaticExpression(math.MathArithTypeEnum.GetExpressionText()); + } + else if (expr is ExprIdentNode) + { + var prop = (ExprIdentNode) expr; + var propertyName = prop.UnresolvedPropertyName; + if (prop.StreamOrPropertyName != null) + { + propertyName = prop.StreamOrPropertyName + "." + prop.UnresolvedPropertyName; + } + return new PropertyValueExpression(propertyName); + } + else if (expr is ExprVariableNode) + { + var prop = (ExprVariableNode) expr; + var propertyName = prop.VariableNameWithSubProp; + return new PropertyValueExpression(propertyName); + } + else if (expr is ExprContextPropertyNode) + { + var prop = (ExprContextPropertyNode) expr; + return + new PropertyValueExpression( + ContextPropertyRegistryConstants.CONTEXT_PREFIX + "." + prop.PropertyName); + } + else if (expr is ExprEqualsNode) + { + var equals = (ExprEqualsNode) expr; + string @operator; + if (!equals.IsIs) + { + @operator = "="; + if (equals.IsNotEquals) + { + @operator = "!="; + } + } + else + { + @operator = "is"; + if (equals.IsNotEquals) + { + @operator = "is not"; + } + } + return new RelationalOpExpression(@operator); + } + else if (expr is ExprRelationalOpNode) + { + var rel = (ExprRelationalOpNode) expr; + return new RelationalOpExpression(rel.RelationalOpEnum.GetExpressionText()); + } + else if (expr is ExprAndNode) + { + return new Conjunction(); + } + else if (expr is ExprOrNode) + { + return new Disjunction(); + } + else if (expr is ExprConstantNodeImpl) + { + var constNode = (ExprConstantNodeImpl) expr; + string constantType = null; + if (constNode.ConstantType != null) + { + constantType = constNode.ConstantType.AssemblyQualifiedName; + } + return new ConstantExpression(constNode.GetConstantValue(null), constantType); + } + else if (expr is ExprConcatNode) + { + return new ConcatExpression(); + } + else if (expr is ExprSubselectRowNode) + { + var sub = (ExprSubselectRowNode) expr; + var unmapped = Unmap(sub.StatementSpecRaw); + unmapContext.AddAll(unmapped.SubstitutionParams); + return new SubqueryExpression(unmapped.ObjectModel); + } + else if (expr is ExprSubselectInNode) + { + var sub = (ExprSubselectInNode) expr; + var unmapped = Unmap(sub.StatementSpecRaw); + unmapContext.AddAll(unmapped.SubstitutionParams); + return new SubqueryInExpression(unmapped.ObjectModel, sub.IsNotIn); + } + else if (expr is ExprSubselectExistsNode) + { + var sub = (ExprSubselectExistsNode) expr; + var unmapped = Unmap(sub.StatementSpecRaw); + unmapContext.AddAll(unmapped.SubstitutionParams); + return new SubqueryExistsExpression(unmapped.ObjectModel); + } + else if (expr is ExprSubselectAllSomeAnyNode) + { + var sub = (ExprSubselectAllSomeAnyNode) expr; + var unmapped = Unmap(sub.StatementSpecRaw); + unmapContext.AddAll(unmapped.SubstitutionParams); + var @operator = "="; + if (sub.IsNot) + { + @operator = "!="; + } + if (sub.RelationalOp != null) + { + @operator = sub.RelationalOp.Value.GetExpressionText(); + } + return new SubqueryQualifiedExpression(unmapped.ObjectModel, @operator, sub.IsAll); + } + else if (expr is ExprCountNode) + { + var sub = (ExprCountNode) expr; + if (sub.ChildNodes.Count == 0 || (sub.ChildNodes.Count == 1 && sub.HasFilter)) + { + return new CountStarProjectionExpression(); + } + else + { + return new CountProjectionExpression(sub.IsDistinct); + } + } + else if (expr is ExprAvgNode) + { + var sub = (ExprAvgNode) expr; + return new AvgProjectionExpression(sub.IsDistinct); + } + else if (expr is ExprSumNode) + { + var sub = (ExprSumNode) expr; + return new SumProjectionExpression(sub.IsDistinct); + } + else if (expr is ExprBetweenNode) + { + var between = (ExprBetweenNode) expr; + return new BetweenExpression( + between.IsLowEndpointIncluded, between.IsHighEndpointIncluded, between.IsNotBetween); + } + else if (expr is ExprPriorNode) + { + return new PriorExpression(); + } + else if (expr is ExprRateAggNode) + { + return new PlugInProjectionExpression("rate", false); + } + else if (expr is ExprNthAggNode) + { + return new PlugInProjectionExpression("nth", false); + } + else if (expr is ExprLeavingAggNode) + { + return new PlugInProjectionExpression("leaving", false); + } + else if (expr is ExprAggCountMinSketchNode) + { + var cmsNode = (ExprAggCountMinSketchNode) expr; + return new PlugInProjectionExpression(cmsNode.AggregationFunctionName, false); + } + else if (expr is ExprAggMultiFunctionSortedMinMaxByNode) + { + var node = (ExprAggMultiFunctionSortedMinMaxByNode) expr; + return new PlugInProjectionExpression(node.AggregationFunctionName, false); + } + else if (expr is ExprPreviousNode) + { + var prev = (ExprPreviousNode) expr; + var result = new PreviousExpression(); + result.ExpressionType = EnumHelper.Parse(prev.PreviousType.ToString()); + return result; + } + else if (expr is ExprMinMaxAggrNode) + { + var node = (ExprMinMaxAggrNode) expr; + if (node.MinMaxTypeEnum == MinMaxTypeEnum.MIN) + { + return new MinProjectionExpression(node.IsDistinct, node.IsEver); + } + else + { + return new MaxProjectionExpression(node.IsDistinct, node.IsEver); + } + } + else if (expr is ExprNotNode) + { + return new NotExpression(); + } + else if (expr is ExprInNode) + { + var inExpr = (ExprInNode) expr; + return new InExpression(inExpr.IsNotIn); + } + else if (expr is ExprCoalesceNode) + { + return new CoalesceExpression(); + } + else if (expr is ExprCaseNode) + { + var mycase = (ExprCaseNode) expr; + if (mycase.IsCase2) + { + return new CaseSwitchExpression(); + } + else + { + return new CaseWhenThenExpression(); + } + } + else if (expr is ExprMinMaxRowNode) + { + var node = (ExprMinMaxRowNode) expr; + if (node.MinMaxTypeEnum == MinMaxTypeEnum.MAX) + { + return new MaxRowExpression(); + } + return new MinRowExpression(); + } + else if (expr is ExprBitWiseNode) + { + var node = (ExprBitWiseNode) expr; + return new BitwiseOpExpression(node.BitWiseOpEnum); + } + else if (expr is ExprArrayNode) + { + return new ArrayExpression(); + } + else if (expr is ExprLikeNode) + { + var exprLikeNode = (ExprLikeNode) expr; + return new LikeExpression(exprLikeNode.IsNot); + } + else if (expr is ExprRegexpNode) + { + var exprRegexNode = (ExprRegexpNode) expr; + return new RegExpExpression(exprRegexNode.IsNot); + } + else if (expr is ExprMedianNode) + { + var median = (ExprMedianNode) expr; + return new MedianProjectionExpression(median.IsDistinct); + } + else if (expr is ExprLastEverNode) + { + var last = (ExprLastEverNode) expr; + return new LastEverProjectionExpression(last.IsDistinct); + } + else if (expr is ExprFirstEverNode) + { + var first = (ExprFirstEverNode) expr; + return new FirstEverProjectionExpression(first.IsDistinct); + } + else if (expr is ExprCountEverNode) + { + var countEver = (ExprCountEverNode) expr; + return new CountEverProjectionExpression(countEver.IsDistinct); + } + else if (expr is ExprAvedevNode) + { + var node = (ExprAvedevNode) expr; + return new AvedevProjectionExpression(node.IsDistinct); + } + else if (expr is ExprStddevNode) + { + var node = (ExprStddevNode) expr; + return new StddevProjectionExpression(node.IsDistinct); + } + else if (expr is ExprPlugInAggNode) + { + var node = (ExprPlugInAggNode) expr; + return new PlugInProjectionExpression(node.AggregationFunctionName, node.IsDistinct); + } + else if (expr is ExprPlugInAggMultiFunctionNode) + { + var node = (ExprPlugInAggMultiFunctionNode) expr; + return new PlugInProjectionExpression(node.AggregationFunctionName, node.IsDistinct); + } + else if (expr is ExprPlugInSingleRowNode) + { + var node = (ExprPlugInSingleRowNode) expr; + var chain = UnmapChains(node.ChainSpec, unmapContext, false); + chain[0].Name = node.FunctionName; // starts with actual function name not mapped on + return new SingleRowMethodExpression(chain); + } + else if (expr is ExprInstanceofNode) + { + var node = (ExprInstanceofNode) expr; + return new InstanceOfExpression(node.ClassIdentifiers); + } + else if (expr is ExprTypeofNode) + { + return new TypeOfExpression(); + } + else if (expr is ExprCastNode) + { + var node = (ExprCastNode) expr; + return new CastExpression(node.ClassIdentifier); + } + else if (expr is ExprPropertyExistsNode) + { + return new PropertyExistsExpression(); + } + else if (expr is ExprTimestampNode) + { + return new CurrentTimestampExpression(); + } + else if (expr is ExprCurrentEvaluationContextNode) + { + return new CurrentEvaluationContextExpression(); + } + else if (expr is ExprIStreamNode) + { + return new IStreamBuiltinExpression(); + } + else if (expr is ExprSubstitutionNode) + { + var node = (ExprSubstitutionNode) expr; + SubstitutionParameterExpressionBase subs; + if (node.Index == null) + { + subs = new SubstitutionParameterExpressionNamed(node.Name); + } + else + { + subs = new SubstitutionParameterExpressionIndexed(node.Index.Value); + } + unmapContext.Add(subs); + return subs; + } + else if (expr is ExprTimePeriod) + { + var node = (ExprTimePeriod) expr; + return new TimePeriodExpression( + node.HasYear, + node.HasMonth, + node.HasWeek, + node.HasDay, + node.HasHour, + node.HasMinute, + node.HasSecond, + node.HasMillisecond, + node.HasMicrosecond); + } + else if (expr is ExprWildcard) + { + return new CrontabParameterExpression(ScheduleItemType.WILDCARD); + } + else if (expr is ExprNumberSetFrequency) + { + return new CrontabFrequencyExpression(); + } + else if (expr is ExprNumberSetRange) + { + return new CrontabRangeExpression(); + } + else if (expr is ExprNumberSetList) + { + return new CrontabParameterSetExpression(); + } + else if (expr is ExprNewStructNode) + { + var newNode = (ExprNewStructNode) expr; + return new NewOperatorExpression(new List(newNode.ColumnNames)); + } + else if (expr is ExprNewInstanceNode) + { + var newNode = (ExprNewInstanceNode) expr; + return new NewInstanceOperatorExpression(newNode.ClassIdent); + } + else if (expr is ExprOrderedExpr) + { + var order = (ExprOrderedExpr) expr; + return new OrderedObjectParamExpression(order.IsDescending); + } + else if (expr is ExprEqualsAllAnyNode) + { + var node = (ExprEqualsAllAnyNode) expr; + var @operator = node.IsNot ? "!=" : "="; + return new CompareListExpression(node.IsAll, @operator); + } + else if (expr is ExprRelationalOpAllAnyNode) + { + var node = (ExprRelationalOpAllAnyNode) expr; + return new CompareListExpression(node.IsAll, node.RelationalOp.GetExpressionText()); + } + else if (expr is ExprNumberSetCronParam) + { + var cronParam = (ExprNumberSetCronParam) expr; + ScheduleItemType type; + if (cronParam.CronOperator == CronOperatorEnum.LASTDAY) + { + type = ScheduleItemType.LASTDAY; + } + else if (cronParam.CronOperator == CronOperatorEnum.LASTWEEKDAY) + { + type = ScheduleItemType.LASTWEEKDAY; + } + else if (cronParam.CronOperator == CronOperatorEnum.WEEKDAY) + { + type = ScheduleItemType.WEEKDAY; + } + else + { + throw new ArgumentException("Cron parameter not recognized: " + cronParam.CronOperator); + } + return new CrontabParameterExpression(type); + } + else if (expr is ExprAggMultiFunctionLinearAccessNode) + { + var accessNode = (ExprAggMultiFunctionLinearAccessNode) expr; + AccessProjectionExpressionBase ape; + if (accessNode.StateType == AggregationStateType.FIRST) + { + ape = new FirstProjectionExpression(); + } + else if (accessNode.StateType == AggregationStateType.WINDOW) + { + ape = new WindowProjectionExpression(); + } + else + { + ape = new LastProjectionExpression(); + } + return ape; + } + else if (expr is ExprDotNode) + { + var dotNode = (ExprDotNode) expr; + var dotExpr = new DotExpression(); + foreach (var chain in dotNode.ChainSpec) + { + dotExpr.Add(chain.Name, UnmapExpressionDeep(chain.Parameters, unmapContext), chain.IsProperty); + } + return dotExpr; + } + else if (expr is ExprDeclaredNode) + { + var declNode = (ExprDeclaredNode) expr; + var dotExpr = new DotExpression(); + dotExpr.Add( + declNode.Prototype.Name, + UnmapExpressionDeep(declNode.ChainParameters, unmapContext)); + return dotExpr; + } + else if (expr is ExprStreamUnderlyingNodeImpl) + { + var streamNode = (ExprStreamUnderlyingNodeImpl) expr; + return new StreamWildcardExpression(streamNode.StreamName); + } + else if (expr is ExprLambdaGoesNode) + { + var lambdaNode = (ExprLambdaGoesNode) expr; + var lambdaExpr = new LambdaExpression(new List(lambdaNode.GoesToNames)); + return lambdaExpr; + } + else if (expr is ExprNodeScript) + { + var scriptNode = (ExprNodeScript) expr; + var dotExpr = new DotExpression(); + dotExpr.Add(scriptNode.Script.Name, UnmapExpressionDeep(scriptNode.Parameters, unmapContext)); + return dotExpr; + } + else if (expr is ExprGroupingNode) + { + return new GroupingExpression(); + } + else if (expr is ExprGroupingIdNode) + { + return new GroupingIdExpression(); + } + else if (expr is ExprNamedParameterNode) + { + var named = (ExprNamedParameterNode) expr; + return new NamedParameterExpression(named.ParameterName); + } + else if (expr is ExprTableAccessNode) + { + var table = (ExprTableAccessNode) expr; + if (table is ExprTableAccessNodeTopLevel) + { + var topLevel = (ExprTableAccessNodeTopLevel) table; + return new TableAccessExpression( + topLevel.TableName, UnmapExpressionDeep(topLevel.ChildNodes, unmapContext), null, null); + } + if (table is ExprTableAccessNodeSubprop) + { + var sub = (ExprTableAccessNodeSubprop) table; + if (sub.ChildNodes.Count == 0) + { + return new PropertyValueExpression(table.TableName + "." + sub.SubpropName); + } + else + { + return new TableAccessExpression( + sub.TableName, UnmapExpressionDeep(sub.ChildNodes, unmapContext), sub.SubpropName, null); + } + } + if (table is ExprTableAccessNodeKeys) + { + var dotExpression = new DotExpression(); + dotExpression.Add(table.TableName, Collections.GetEmptyList(), true); + dotExpression.Add("keys", Collections.GetEmptyList()); + return dotExpression; + } + if (table is ExprTableAccessNodeSubpropAccessor) + { + var sub = (ExprTableAccessNodeSubpropAccessor) table; + if (sub.ChildNodes.Count == 0) + { + var dotExpression = new DotExpression(); + dotExpression.Add( + table.TableName + "." + sub.SubpropName, Collections.GetEmptyList(), true); + IList @params = UnmapExpressionDeep( + sub.AggregateAccessMultiValueNode.ChildNodes, unmapContext); + var functionName = sub.AggregateAccessMultiValueNode.AggregationFunctionName; + if (AggregationStateTypeExtensions.FromString(functionName) != null && @params.IsEmpty()) + { + @params.Add(new WildcardExpression()); + } + dotExpression.Add(functionName, @params); + return dotExpression; + } + else + { + var aggregate = UnmapExpressionDeep(sub.AggregateAccessMultiValueNode, unmapContext); + return new TableAccessExpression( + sub.TableName, UnmapExpressionDeep(sub.ChildNodes, unmapContext), sub.SubpropName, aggregate); + } + } + } + throw new ArgumentException("Could not map expression node of type " + expr.GetType().Name); + } + + private static void UnmapExpressionRecursive( + Expression parent, + ExprNode expr, + StatementSpecUnMapContext unmapContext) + { + foreach (var child in expr.ChildNodes) + { + var result = UnmapExpressionFlat(child, unmapContext); + parent.Children.Add(result); + UnmapExpressionRecursive(result, child, unmapContext); + } + } + + private static void UnmapExpressionRecursiveRowregex( + MatchRecognizeRegEx parent, + RowRegexExprNode expr, + StatementSpecUnMapContext unmapContext) + { + foreach (var child in expr.ChildNodes) + { + var result = UnmapExpressionFlatRowregex(child, unmapContext); + parent.Children.Add(result); + UnmapExpressionRecursiveRowregex(result, child, unmapContext); + } + } + + private static void MapExpressionRecursive(ExprNode parent, Expression expr, StatementSpecMapContext mapContext) + { + foreach (var child in expr.Children) + { + var result = MapExpressionFlat(child, mapContext); + parent.AddChildNode(result); + MapExpressionRecursive(result, child, mapContext); + } + } + + private static void MapExpressionRecursiveRowregex( + RowRegexExprNode parent, + MatchRecognizeRegEx expr, + StatementSpecMapContext mapContext) + { + foreach (var child in expr.Children) + { + var result = MapExpressionFlatRowregex(child, mapContext); + parent.AddChildNode(result); + MapExpressionRecursiveRowregex(result, child, mapContext); + } + } + + private static void MapFrom(FromClause fromClause, StatementSpecRaw raw, StatementSpecMapContext mapContext) + { + if (fromClause == null) + { + return; + } + + foreach (var stream in fromClause.Streams) + { + StreamSpecRaw spec; + + var views = ViewSpec.EMPTY_VIEWSPEC_ARRAY; + if (stream is ProjectedStream) + { + var projectedStream = (ProjectedStream) stream; + views = ViewSpec.ToArray(MapViews(projectedStream.Views, mapContext)); + } + + if (stream is FilterStream) + { + var filterStream = (FilterStream) stream; + var filterSpecRaw = MapFilter(filterStream.Filter, mapContext); + var options = MapStreamOpts(filterStream); + spec = new FilterStreamSpecRaw(filterSpecRaw, views, filterStream.StreamName, options); + } + else if (stream is SQLStream) + { + var sqlStream = (SQLStream) stream; + spec = new DBStatementStreamSpec( + sqlStream.StreamName, views, + sqlStream.DatabaseName, sqlStream.SqlWithSubsParams, sqlStream.OptionalMetadataSQL); + } + else if (stream is PatternStream) + { + var patternStream = (PatternStream) stream; + var child = MapPatternEvalDeep(patternStream.Expression, mapContext); + var options = MapStreamOpts(patternStream); + var flags = PatternLevelAnnotationUtil.AnnotationsToSpec(patternStream.Annotations); + spec = new PatternStreamSpecRaw( + child, views, patternStream.StreamName, options, flags.IsSuppressSameEventMatches, + flags.IsDiscardPartialsOnMatch); + } + else if (stream is MethodInvocationStream) + { + var methodStream = (MethodInvocationStream) stream; + var expressions = new List(); + foreach (Expression expr in methodStream.ParameterExpressions) + { + var exprNode = MapExpressionDeep(expr, mapContext); + expressions.Add(exprNode); + } + + if (mapContext.VariableService.GetVariableMetaData(methodStream.ClassName) != null) + { + mapContext.HasVariables = true; + } + + spec = new MethodStreamSpec( + methodStream.StreamName, views, "method", + methodStream.ClassName, methodStream.MethodName, expressions, methodStream.OptionalEventTypeName); + } + else + { + throw new ArgumentException( + "Could not map from stream " + stream + " to an internal representation"); + } + + raw.StreamSpecs.Add(spec); + } + + foreach (var qualifier in fromClause.OuterJoinQualifiers) + { + ExprIdentNode left = null; + ExprIdentNode right = null; + ExprIdentNode[] additionalLeft = null; + ExprIdentNode[] additionalRight = null; + + if (qualifier.Left != null) + { + + left = (ExprIdentNode) MapExpressionFlat(qualifier.Left, mapContext); + right = (ExprIdentNode) MapExpressionFlat(qualifier.Right, mapContext); + + if (qualifier.AdditionalProperties.Count != 0) + { + additionalLeft = new ExprIdentNode[qualifier.AdditionalProperties.Count]; + additionalRight = new ExprIdentNode[qualifier.AdditionalProperties.Count]; + var count = 0; + foreach (var pair in qualifier.AdditionalProperties) + { + additionalLeft[count] = (ExprIdentNode) MapExpressionFlat(pair.Left, mapContext); + additionalRight[count] = (ExprIdentNode) MapExpressionFlat(pair.Right, mapContext); + count++; + } + } + } + + raw.OuterJoinDescList.Add( + new OuterJoinDesc(qualifier.JoinType, left, right, additionalLeft, additionalRight)); + } + } + + private static IList MapViews(IList views, StatementSpecMapContext mapContext) + { + var viewSpecs = new List(); + foreach (var view in views) + { + IList viewExpressions = MapExpressionDeep(view.Parameters, mapContext); + viewSpecs.Add(new ViewSpec(view.Namespace, view.Name, viewExpressions)); + } + return viewSpecs; + } + + private static IList UnmapViews(IList viewSpecs, StatementSpecUnMapContext unmapContext) + { + var views = new List(); + foreach (var viewSpec in viewSpecs) + { + IList viewExpressions = UnmapExpressionDeep(viewSpec.ObjectParameters, unmapContext); + views.Add(View.Create(viewSpec.ObjectNamespace, viewSpec.ObjectName, viewExpressions)); + } + return views; + } + + private static EvalFactoryNode MapPatternEvalFlat(PatternExpr eval, StatementSpecMapContext mapContext) + { + if (eval == null) + { + throw new ArgumentException("Null expression parameter"); + } + if (eval is PatternAndExpr) + { + return mapContext.PatternNodeFactory.MakeAndNode(); + } + else if (eval is PatternOrExpr) + { + return mapContext.PatternNodeFactory.MakeOrNode(); + } + else if (eval is PatternFollowedByExpr) + { + var fb = (PatternFollowedByExpr) eval; + IList maxExpr = MapExpressionDeep(fb.OptionalMaxPerSubexpression, mapContext); + return mapContext.PatternNodeFactory.MakeFollowedByNode( + maxExpr, mapContext.Configuration.EngineDefaults.Patterns.MaxSubexpressions != null); + } + else if (eval is PatternEveryExpr) + { + return mapContext.PatternNodeFactory.MakeEveryNode(); + } + else if (eval is PatternFilterExpr) + { + var filterExpr = (PatternFilterExpr) eval; + var filterSpec = MapFilter(filterExpr.Filter, mapContext); + return mapContext.PatternNodeFactory.MakeFilterNode( + filterSpec, filterExpr.TagName, filterExpr.OptionalConsumptionLevel); + } + else if (eval is PatternObserverExpr) + { + var observer = (PatternObserverExpr) eval; + IList expressions = MapExpressionDeep(observer.Parameters, mapContext); + return + mapContext.PatternNodeFactory.MakeObserverNode( + new PatternObserverSpec(observer.Namespace, observer.Name, expressions)); + } + else if (eval is PatternGuardExpr) + { + var guard = (PatternGuardExpr) eval; + IList expressions = MapExpressionDeep(guard.Parameters, mapContext); + return + mapContext.PatternNodeFactory.MakeGuardNode( + new PatternGuardSpec(guard.Namespace, guard.Name, expressions)); + } + else if (eval is PatternNotExpr) + { + return mapContext.PatternNodeFactory.MakeNotNode(); + } + else if (eval is PatternMatchUntilExpr) + { + var until = (PatternMatchUntilExpr) eval; + var low = until.Low != null ? MapExpressionDeep(until.Low, mapContext) : null; + var high = until.High != null ? MapExpressionDeep(until.High, mapContext) : null; + var single = until.Single != null ? MapExpressionDeep(until.Single, mapContext) : null; + return mapContext.PatternNodeFactory.MakeMatchUntilNode(low, high, single); + } + else if (eval is PatternEveryDistinctExpr) + { + var everyDist = (PatternEveryDistinctExpr) eval; + IList expressions = MapExpressionDeep(everyDist.Expressions, mapContext); + return mapContext.PatternNodeFactory.MakeEveryDistinctNode(expressions); + } + throw new ArgumentException("Could not map pattern expression node of type " + eval.GetType().Name); + } + + private static PatternExpr UnmapPatternEvalFlat(EvalFactoryNode eval, StatementSpecUnMapContext unmapContext) + { + if (eval is EvalAndFactoryNode) + { + return new PatternAndExpr(); + } + else if (eval is EvalOrFactoryNode) + { + return new PatternOrExpr(); + } + else if (eval is EvalFollowedByFactoryNode) + { + var fb = (EvalFollowedByFactoryNode) eval; + IList expressions = UnmapExpressionDeep(fb.OptionalMaxExpressions, unmapContext); + return new PatternFollowedByExpr(expressions); + } + else if (eval is EvalEveryFactoryNode) + { + return new PatternEveryExpr(); + } + else if (eval is EvalNotFactoryNode) + { + return new PatternNotExpr(); + } + else if (eval is EvalFilterFactoryNode) + { + var filterNode = (EvalFilterFactoryNode) eval; + var filter = UnmapFilter(filterNode.RawFilterSpec, unmapContext); + var expr = new PatternFilterExpr(filter, filterNode.EventAsName); + expr.OptionalConsumptionLevel = filterNode.ConsumptionLevel; + return expr; + } + else if (eval is EvalObserverFactoryNode) + { + var observerNode = (EvalObserverFactoryNode) eval; + IList expressions = UnmapExpressionDeep( + observerNode.PatternObserverSpec.ObjectParameters, unmapContext); + return new PatternObserverExpr( + observerNode.PatternObserverSpec.ObjectNamespace, + observerNode.PatternObserverSpec.ObjectName, expressions); + } + else if (eval is EvalGuardFactoryNode) + { + var guardNode = (EvalGuardFactoryNode) eval; + IList expressions = UnmapExpressionDeep( + guardNode.PatternGuardSpec.ObjectParameters, unmapContext); + return new PatternGuardExpr( + guardNode.PatternGuardSpec.ObjectNamespace, + guardNode.PatternGuardSpec.ObjectName, expressions); + } + else if (eval is EvalMatchUntilFactoryNode) + { + var matchUntilNode = (EvalMatchUntilFactoryNode) eval; + var low = matchUntilNode.LowerBounds != null + ? UnmapExpressionDeep(matchUntilNode.LowerBounds, unmapContext) + : null; + var high = matchUntilNode.UpperBounds != null + ? UnmapExpressionDeep(matchUntilNode.UpperBounds, unmapContext) + : null; + var single = matchUntilNode.SingleBound != null + ? UnmapExpressionDeep(matchUntilNode.SingleBound, unmapContext) + : null; + return new PatternMatchUntilExpr(low, high, single); + } + else if (eval is EvalEveryDistinctFactoryNode) + { + var everyDistinctNode = (EvalEveryDistinctFactoryNode) eval; + IList expressions = UnmapExpressionDeep(everyDistinctNode.Expressions, unmapContext); + return new PatternEveryDistinctExpr(expressions); + } + else if (eval is EvalAuditFactoryNode) + { + return null; + } + throw new ArgumentException("Could not map pattern expression node of type " + eval.GetType().Name); + } + + private static void UnmapPatternEvalRecursive( + PatternExpr parent, + EvalFactoryNode eval, + StatementSpecUnMapContext unmapContext) + { + foreach (var child in eval.ChildNodes) + { + var result = UnmapPatternEvalFlat(child, unmapContext); + parent.Children.Add(result); + UnmapPatternEvalRecursive(result, child, unmapContext); + } + } + + private static void MapPatternEvalRecursive( + EvalFactoryNode parent, + PatternExpr expr, + StatementSpecMapContext mapContext) + { + foreach (var child in expr.Children) + { + var result = MapPatternEvalFlat(child, mapContext); + parent.AddChildNode(result); + MapPatternEvalRecursive(result, child, mapContext); + } + } + + private static PatternExpr UnmapPatternEvalDeep( + EvalFactoryNode exprNode, + StatementSpecUnMapContext unmapContext) + { + var parent = UnmapPatternEvalFlat(exprNode, unmapContext); + UnmapPatternEvalRecursive(parent, exprNode, unmapContext); + return parent; + } + + private static EvalFactoryNode MapPatternEvalDeep(PatternExpr expr, StatementSpecMapContext mapContext) + { + var parent = MapPatternEvalFlat(expr, mapContext); + MapPatternEvalRecursive(parent, expr, mapContext); + return parent; + } + + private static FilterSpecRaw MapFilter(Filter filter, StatementSpecMapContext mapContext) + { + var expr = new List(); + if (filter.FilterExpression != null) + { + ExprNode exprNode = MapExpressionDeep(filter.FilterExpression, mapContext); + expr.Add(exprNode); + } + + PropertyEvalSpec evalSpec = null; + if (filter.OptionalPropertySelects != null) + { + evalSpec = MapPropertySelects(filter.OptionalPropertySelects, mapContext); + } + + return new FilterSpecRaw(filter.EventTypeName, expr, evalSpec); + } + + private static PropertyEvalSpec MapPropertySelects( + IList propertySelects, + StatementSpecMapContext mapContext) + { + var evalSpec = new PropertyEvalSpec(); + foreach (var propertySelect in propertySelects) + { + SelectClauseSpecRaw selectSpec = null; + if (propertySelect.SelectClause != null) + { + selectSpec = MapSelectRaw(propertySelect.SelectClause, mapContext); + } + + ExprNode exprNodeWhere = null; + if (propertySelect.WhereClause != null) + { + exprNodeWhere = MapExpressionDeep(propertySelect.WhereClause, mapContext); + } + + ExprNode splitterExpr = null; + if (propertySelect.SplitExpression != null) + { + splitterExpr = MapExpressionDeep(propertySelect.SplitExpression, mapContext); + } + + evalSpec.Add( + new PropertyEvalAtom( + splitterExpr, propertySelect.OptionalSplitExpressionTypeName, propertySelect.OptionalAsName, + selectSpec, exprNodeWhere)); + } + return evalSpec; + } + + private static Filter UnmapFilter(FilterSpecRaw filter, StatementSpecUnMapContext unmapContext) + { + Expression expr = null; + if (filter.FilterExpressions.Count > 1) + { + expr = new Conjunction(); + foreach (var exprNode in filter.FilterExpressions) + { + var expression = UnmapExpressionDeep(exprNode, unmapContext); + expr.Children.Add(expression); + } + } + else if (filter.FilterExpressions.Count == 1) + { + expr = UnmapExpressionDeep(filter.FilterExpressions[0], unmapContext); + } + + var filterDef = new Filter(filter.EventTypeName, expr); + + if (filter.OptionalPropertyEvalSpec != null) + { + var propertySelects = UnmapPropertySelects(filter.OptionalPropertyEvalSpec, unmapContext); + filterDef.OptionalPropertySelects = propertySelects; + } + return filterDef; + } + + private static IList UnmapPropertySelects( + PropertyEvalSpec propertyEvalSpec, + StatementSpecUnMapContext unmapContext) + { + var propertySelects = new List(); + foreach (var atom in propertyEvalSpec.Atoms) + { + SelectClause selectClause = null; + if (atom.OptionalSelectClause != null && !atom.OptionalSelectClause.SelectExprList.IsEmpty()) + { + selectClause = UnmapSelect( + atom.OptionalSelectClause, SelectClauseStreamSelectorEnum.ISTREAM_ONLY, unmapContext); + } + + Expression filterExpression = null; + if (atom.OptionalWhereClause != null) + { + filterExpression = UnmapExpressionDeep(atom.OptionalWhereClause, unmapContext); + } + + var splitExpression = UnmapExpressionDeep(atom.SplitterExpression, unmapContext); + + var contained = new ContainedEventSelect(splitExpression); + contained.OptionalSplitExpressionTypeName = atom.OptionalResultEventType; + contained.SelectClause = selectClause; + contained.WhereClause = filterExpression; + contained.OptionalAsName = atom.OptionalAsName; + + if (atom.SplitterExpression != null) + { + contained.SplitExpression = UnmapExpressionDeep(atom.SplitterExpression, unmapContext); + } + propertySelects.Add(contained); + } + return propertySelects; + } + + private static IList UnmapAnnotations(IList annotations) + { + return annotations.Select(UnmapAnnotation).ToList(); + } + + private static IList UnmapExpressionDeclarations( + ExpressionDeclDesc expr, + StatementSpecUnMapContext unmapContext) + { + if (expr == null || expr.Expressions.IsEmpty()) + { + return Collections.GetEmptyList(); + } + return expr.Expressions.Select(desc => UnmapExpressionDeclItem(desc, unmapContext)).ToList(); + } + + private static ExpressionDeclaration UnmapExpressionDeclItem( + ExpressionDeclItem desc, + StatementSpecUnMapContext unmapContext) + { + return new ExpressionDeclaration( + desc.Name, desc.ParametersNames, UnmapExpressionDeep(desc.Inner, unmapContext), desc.IsAlias); + } + + private static IList UnmapScriptExpressions( + IList scripts, + StatementSpecUnMapContext unmapContext) + { + if (scripts == null || scripts.IsEmpty()) + { + return Collections.GetEmptyList(); + } + var result = new List(); + foreach (var script in scripts) + { + var e = UnmapScriptExpression(script, unmapContext); + result.Add(e); + } + return result; + } + + private static ScriptExpression UnmapScriptExpression( + ExpressionScriptProvided script, + StatementSpecUnMapContext unmapContext) + { + var returnType = script.OptionalReturnTypeName; + if (returnType != null && script.IsOptionalReturnTypeIsArray) + { + returnType = returnType + "[]"; + } + return new ScriptExpression( + script.Name, script.ParameterNames, script.Expression, returnType, script.OptionalDialect, + script.OptionalEventTypeName); + } + + private static AnnotationPart UnmapAnnotation(AnnotationDesc desc) + { + if ((desc.Attributes == null) || (desc.Attributes.IsEmpty())) + { + return new AnnotationPart(desc.Name); + } + + var attributes = new List(); + foreach (var pair in desc.Attributes) + { + if (pair.Second is AnnotationDesc) + { + attributes.Add(new AnnotationAttribute(pair.First, UnmapAnnotation((AnnotationDesc) pair.Second))); + } + else + { + attributes.Add(new AnnotationAttribute(pair.First, pair.Second)); + } + } + return new AnnotationPart(desc.Name, attributes); + } + + public static IList MapAnnotations(IList annotations) + { + IList result; + if (annotations != null) + { + result = annotations.Select(part => MapAnnotation(part)).ToList(); + } + else + { + result = Collections.GetEmptyList(); + } + return result; + } + + private static void MapContextName(string contextName, StatementSpecRaw raw, StatementSpecMapContext mapContext) + { + raw.OptionalContextName = contextName; + mapContext.ContextName = contextName; + } + + private static void MapExpressionDeclaration( + IList expressionDeclarations, + StatementSpecRaw raw, + StatementSpecMapContext mapContext) + { + if (expressionDeclarations == null || expressionDeclarations.IsEmpty()) + { + return; + } + + var desc = new ExpressionDeclDesc(); + raw.ExpressionDeclDesc = desc; + + foreach (var decl in expressionDeclarations) + { + var item = MapExpressionDeclItem(decl, mapContext); + desc.Expressions.Add(item); + mapContext.AddExpressionDeclarations(item); + } + } + + private static ExpressionDeclItem MapExpressionDeclItem( + ExpressionDeclaration decl, + StatementSpecMapContext mapContext) + { + return new ExpressionDeclItem( + decl.Name, + decl.IsAlias ? Collections.GetEmptyList() : decl.ParameterNames, + MapExpressionDeep(decl.Expression, mapContext), decl.IsAlias); + } + + private static void MapScriptExpressions( + IList scriptExpressions, + StatementSpecRaw raw, + StatementSpecMapContext mapContext) + { + if (scriptExpressions == null || scriptExpressions.IsEmpty()) + { + return; + } + + var scripts = new List(); + raw.ScriptExpressions = scripts; + + foreach (var decl in scriptExpressions) + { + var scriptProvided = MapScriptExpression(decl, mapContext); + scripts.Add(scriptProvided); + mapContext.AddScript(scriptProvided); + } + } + + private static ExpressionScriptProvided MapScriptExpression( + ScriptExpression decl, + StatementSpecMapContext mapContext) + { + string returnType = decl.OptionalReturnType != null ? decl.OptionalReturnType.Replace("[]", "") : null; + bool isArray = decl.OptionalReturnType != null && decl.OptionalReturnType.Contains("[]"); + return new ExpressionScriptProvided( + decl.Name, decl.ExpressionText, decl.ParameterNames, returnType, isArray, decl.OptionalEventTypeName, + decl.OptionalDialect); + } + + private static AnnotationDesc MapAnnotation(AnnotationPart part) + { + if ((part.Attributes == null) || (part.Attributes.IsEmpty())) + { + return new AnnotationDesc(part.Name, Collections.GetEmptyList>()); + } + + var attributes = new List>(); + foreach (var pair in part.Attributes) + { + if (pair.Value is AnnotationPart) + { + attributes.Add(new Pair(pair.Name, MapAnnotation((AnnotationPart) pair.Value))); + } + else + { + attributes.Add(new Pair(pair.Name, pair.Value)); + } + } + return new AnnotationDesc(part.Name, attributes); + } + + private static void MapSQLParameters( + FromClause fromClause, + StatementSpecRaw raw, + StatementSpecMapContext mapContext) + { + if ((fromClause == null) || (fromClause.Streams == null)) + { + return; + } + var streamNum = -1; + foreach (var stream in fromClause.Streams) + { + streamNum++; + if (!(stream is SQLStream)) + { + continue; + } + var sqlStream = (SQLStream) stream; + + IList sqlFragments = null; + try + { + sqlFragments = PlaceholderParser.ParsePlaceholder(sqlStream.SqlWithSubsParams); + } + catch (PlaceholderParseException) + { + throw new EPException( + "Error parsing SQL placeholder expression '" + sqlStream.SqlWithSubsParams + "': "); + } + + foreach (var fragment in sqlFragments) + { + if (!(fragment is PlaceholderParser.ParameterFragment)) + { + continue; + } + + // Parse expression, store for substitution parameters + var expression = fragment.Value; + if ( + expression.ToUpperInvariant() + .Equals(DatabasePollingViewableFactory.SAMPLE_WHERECLAUSE_PLACEHOLDER)) + { + continue; + } + + if (string.IsNullOrWhiteSpace(expression)) + { + throw ASTWalkException.From("Missing expression within ${...} in SQL statement"); + } + var toCompile = "select * from System.Object where " + expression; + var rawSqlExpr = EPAdministratorHelper.CompileEPL( + toCompile, expression, false, null, SelectClauseStreamSelectorEnum.ISTREAM_ONLY, + mapContext.EngineImportService, mapContext.VariableService, mapContext.SchedulingService, + mapContext.EngineURI, mapContext.Configuration, mapContext.PatternNodeFactory, + mapContext.ContextManagementService, mapContext.ExprDeclaredService, mapContext.TableService); + + if ((rawSqlExpr.SubstitutionParameters != null) && (rawSqlExpr.SubstitutionParameters.Count > 0)) + { + throw ASTWalkException.From( + "EPL substitution parameters are not allowed in SQL ${...} expressions, consider using a variable instead"); + } + + if (rawSqlExpr.HasVariables) + { + mapContext.HasVariables = true; + } + + // add expression + if (raw.SqlParameters == null) + { + raw.SqlParameters = new Dictionary>(); + } + IList listExp = raw.SqlParameters.Get(streamNum); + if (listExp == null) + { + listExp = new List(); + raw.SqlParameters.Put(streamNum, listExp); + } + listExp.Add(rawSqlExpr.FilterRootNode); + } + } + } + + private static IList MapChains( + IEnumerable pairs, + StatementSpecMapContext mapContext) + { + return pairs + .Select(item => new ExprChainedSpec(item.Name, MapExpressionDeep(item.Parameters, mapContext), item.IsProperty)) + .ToList(); + } + + private static IList UnmapChains( + IEnumerable pairs, + StatementSpecUnMapContext unmapContext, + bool isProperty) + { + return pairs + .Select(chain => new DotExpressionItem(chain.Name, UnmapExpressionDeep(chain.Parameters, unmapContext), chain.IsProperty)) + .ToList(); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/spec/StatementSpecRaw.cs b/NEsper.Core/NEsper.Core/epl/spec/StatementSpecRaw.cs new file mode 100755 index 000000000..bb0e1fba7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/StatementSpecRaw.cs @@ -0,0 +1,165 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.table; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.spec +{ + /// + /// Specification object representing a complete EPL statement including all EPL constructs. + /// + [Serializable] + public class StatementSpecRaw : MetaDefItem + { + /// Ctor. + /// stream selection for the statement + public StatementSpecRaw(SelectClauseStreamSelectorEnum defaultStreamSelector) + { + OrderByList = new List(); + StreamSpecs = new List(); + SelectClauseSpec = new SelectClauseSpecRaw(); + OuterJoinDescList = new List(); + GroupByExpressions = new List(); + Annotations = new List(1); + SelectStreamDirEnum = defaultStreamSelector; + } + + /// Returns the FROM-clause stream definitions. + /// list of stream specifications + public IList StreamSpecs { get; private set; } + + /// Returns SELECT-clause list of expressions. + /// list of expressions and optional name + public SelectClauseSpecRaw SelectClauseSpec { get; set; } + + /// Returns the WHERE-clause root node of filter expression. + /// filter expression root node + public ExprNode FilterRootNode + { + get { return FilterExprRootNode; } + } + + /// Returns the LEFT/RIGHT/FULL OUTER JOIN-type and property name descriptor, if applicable. Returns null if regular join. + /// outer join type, stream names and property names + public IList OuterJoinDescList { get; private set; } + + /// Returns list of group-by expressions. + /// group-by expression nodes as specified in group-by clause + public IList GroupByExpressions { get; private set; } + + /// Returns expression root node representing the having-clause, if present, or null if no having clause was supplied. + /// having-clause expression top node + public ExprNode HavingExprRootNode { get; set; } + + /// Returns the output limit definition, if any. + /// output limit spec + public OutputLimitSpec OutputLimitSpec { get; set; } + + /// Return a descriptor with the insert-into event name and optional list of columns. + /// insert into specification + public InsertIntoDesc InsertIntoDesc { get; set; } + + /// Returns the list of order-by expression as specified in the ORDER BY clause. + /// Returns the orderByList. + public IList OrderByList { get; private set; } + + /// Returns the stream selector (rstream/istream). + /// stream selector + public SelectClauseStreamSelectorEnum SelectStreamSelectorEnum + { + get { return SelectStreamDirEnum; } + } + + /// Sets the stream selector (rstream/istream/both etc). + /// to be set + public SelectClauseStreamSelectorEnum SelectStreamDirEnum { get; set; } + + /// Returns the create-window specification. + /// descriptor for creating a named window + public CreateWindowDesc CreateWindowDesc { get; set; } + + /// Returns the on-delete statement specification. + /// descriptor for creating a an on-delete statement + public OnTriggerDesc OnTriggerDesc { get; set; } + + /// Gets the where clause. + /// where clause or null if none + public ExprNode FilterExprRootNode { get; set; } + + /// Returns true if a statement (or subquery sub-statements) use variables. + /// indicator if variables are used + public bool HasVariables { get; set; } + + /// Returns the descriptor for create-variable statements. + /// create-variable INFO + public CreateVariableDesc CreateVariableDesc { get; set; } + + /// Gets or sets the desciptor for create-table statements. + /// create-table INFO + public CreateTableDesc CreateTableDesc { get; set; } + + /// Returns the row limit, or null if none. + /// row limit + public RowLimitSpec RowLimitSpec { get; set; } + + /// Returns a list of annotation descriptors. + /// annotation descriptors + public IList Annotations { get; set; } + + /// Returns the Update spec. + /// Update spec + public UpdateDesc UpdateDesc { get; set; } + + /// Returns the expression text without annotations. + /// expressionNoAnnotations text + public string ExpressionNoAnnotations { get; set; } + + /// Returns the match recognize spec. + /// spec + public MatchRecognizeSpec MatchRecognizeSpec { get; set; } + + /// Returns the variables referenced + /// vars + public ICollection ReferencedVariables { get; set; } + + /// Returns create-index if any. + /// index create + public CreateIndexDesc CreateIndexDesc { get; set; } + + public CreateSchemaDesc CreateSchemaDesc { get; set; } + + public ForClauseSpec ForClauseSpec { get; set; } + + public IDictionary> SqlParameters { get; set; } + + public IList SubstitutionParameters { get; set; } + + public ExpressionDeclDesc ExpressionDeclDesc { get; set; } + + public CreateContextDesc CreateContextDesc { get; set; } + + public string OptionalContextName { get; set; } + + public IList ScriptExpressions { get; set; } + + public CreateDataFlowDesc CreateDataFlowDesc { get; set; } + + public CreateExpressionDesc CreateExpressionDesc { get; set; } + + public FireAndForgetSpec FireAndForgetSpec { get; set; } + + public IntoTableSpec IntoTableSpec { get; set; } + + public ISet TableExpressions { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/StatementSpecUnMapContext.cs b/NEsper.Core/NEsper.Core/epl/spec/StatementSpecUnMapContext.cs new file mode 100755 index 000000000..3a06e52dc --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/StatementSpecUnMapContext.cs @@ -0,0 +1,43 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.epl.spec +{ + /// + /// Un-mapping context for mapping from an internal specifications to an SODA object model. + /// + public class StatementSpecUnMapContext + { + private readonly List _substitutionParams; + + public StatementSpecUnMapContext() + { + _substitutionParams = new List(); + } + + public void Add(SubstitutionParameterExpressionBase subsParam) + { + _substitutionParams.Add(subsParam); + } + + public IList SubstitutionParams + { + get { return _substitutionParams; } + } + + public void AddAll(IList inner) + { + _substitutionParams.AddRange(inner); + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/epl/spec/StatementSpecUnMapResult.cs b/NEsper.Core/NEsper.Core/epl/spec/StatementSpecUnMapResult.cs new file mode 100755 index 000000000..f3d0cd2b5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/StatementSpecUnMapResult.cs @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client.soda; + +namespace com.espertech.esper.epl.spec +{ + /// + /// Return result for unmap operators unmapping an intermal statement representation to the SODA object model. + /// + public class StatementSpecUnMapResult + { + private readonly EPStatementObjectModel _objectModel; + private readonly IList _substitutionParams; + + /// Ctor. + /// of the statement + /// a map of parameter index and parameter + public StatementSpecUnMapResult(EPStatementObjectModel objectModel, IList substitutionParams) + { + _objectModel = objectModel; + _substitutionParams = substitutionParams; + } + + /// Returns the object model. + /// object model + public EPStatementObjectModel ObjectModel + { + get { return _objectModel; } + } + + /// + /// Returns the substitution paremeters keyed by the parameter's index. + /// + /// map of index and parameter + public IList SubstitutionParams + { + get { return _substitutionParams; } + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/epl/spec/StreamSpec.cs b/NEsper.Core/NEsper.Core/epl/spec/StreamSpec.cs new file mode 100755 index 000000000..ba2a13a26 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/StreamSpec.cs @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.spec +{ + /// + /// Specification for a stream, consists simply of an optional stream name and a list + /// of views on to of the stream. Implementation classes for views and patterns + /// add additional information defining the stream of events. + /// + public interface StreamSpec : MetaDefItem + { + /// Returns the stream name, or null if undefined. + /// stream name + string OptionalStreamName { get; } + + /// Returns views definitions onto the stream + /// view defs + ViewSpec[] ViewSpecs { get; } + + /// Returns the options for the stream such as unidirectional, retain-union etc. + /// stream options + StreamSpecOptions Options { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/StreamSpecBase.cs b/NEsper.Core/NEsper.Core/epl/spec/StreamSpecBase.cs new file mode 100755 index 000000000..afe45f239 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/StreamSpecBase.cs @@ -0,0 +1,52 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.spec +{ + /// + /// Abstract base specification for a stream, consists simply of an optional stream name + /// and a list of views on to of the stream. Implementation classes for views and + /// patterns add additional information defining the stream of events. + /// + [Serializable] + public abstract class StreamSpecBase : MetaDefItem + { + /// Ctor. + /// stream name, or null if none supplied + /// specifies what view to use to derive data + /// indicates additional options such as unidirectional stream or retain-union or retain-intersection + protected StreamSpecBase(String optionalStreamName, ViewSpec[] viewSpecs, StreamSpecOptions streamSpecOptions) + { + OptionalStreamName = optionalStreamName; + ViewSpecs = viewSpecs; + Options = streamSpecOptions; + } + + /// Default ctor. + protected StreamSpecBase() + { + ViewSpecs = ViewSpec.EMPTY_VIEWSPEC_ARRAY; + } + + /// Returns the name assigned. + /// stream name or null if not assigned + public string OptionalStreamName { get; private set; } + + /// Returns view definitions to use to construct views to derive data on stream. + /// view defs + public ViewSpec[] ViewSpecs { get; private set; } + + /// Returns the options for the stream such as unidirectional, retain-union etc. + /// stream options + public StreamSpecOptions Options { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/StreamSpecCompiled.cs b/NEsper.Core/NEsper.Core/epl/spec/StreamSpecCompiled.cs new file mode 100755 index 000000000..9a33a6768 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/StreamSpecCompiled.cs @@ -0,0 +1,22 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.spec +{ + /// + /// Validated stream specifications generally have expression nodes that are valid and event types exist. + /// + public interface StreamSpecCompiled : StreamSpec + { + } + + public class StreamSpecCompiledConstants + { + public readonly static StreamSpecCompiled[] EMPTY_STREAM_ARRAY = new StreamSpecCompiled[0]; + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/StreamSpecOptions.cs b/NEsper.Core/NEsper.Core/epl/spec/StreamSpecOptions.cs new file mode 100755 index 000000000..9c647a637 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/StreamSpecOptions.cs @@ -0,0 +1,68 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.spec +{ + /// + /// Abstract base specification for a stream, consists simply of an optional stream name and a list of views + /// on to of the stream. + /// + /// Implementation classes for views and patterns add additional information defining the + /// stream of events. + /// + /// + [Serializable] + public class StreamSpecOptions : MetaDefItem + { + public static readonly StreamSpecOptions DEFAULT = new StreamSpecOptions(); + + /// Ctor, sets all options off. + private StreamSpecOptions() { + IsUnidirectional = false; + IsRetainUnion = false; + IsRetainIntersection = false; + } + + /// + /// Ctor. + /// + /// - true to indicate a unidirectional stream in a join, applicable for joins + /// - for retaining the union of multiple data windows + /// - for retaining the intersection of multiple data windows + public StreamSpecOptions(bool isUnidirectional, bool isRetainUnion, bool isRetainIntersection) { + if (isRetainUnion && isRetainIntersection) { + throw new ArgumentException("Invalid retain flags"); + } + IsUnidirectional = isUnidirectional; + IsRetainUnion = isRetainUnion; + IsRetainIntersection = isRetainIntersection; + } + + /// + /// Indicator for retaining the union of multiple expiry policies. + /// + /// true for retain union + public bool IsRetainUnion { get; private set; } + + /// + /// Indicator for retaining the intersection of multiple expiry policies. + /// + /// true for retain intersection + public bool IsRetainIntersection { get; private set; } + + /// + /// Returns true to indicate a unidirectional stream in a join, applicable for joins. + /// + /// indicator whether the stream is unidirectional in a join + public bool IsUnidirectional { get; private set; } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/spec/StreamSpecRaw.cs b/NEsper.Core/NEsper.Core/epl/spec/StreamSpecRaw.cs new file mode 100755 index 000000000..a47eca72f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/StreamSpecRaw.cs @@ -0,0 +1,46 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.core.service; + +namespace com.espertech.esper.epl.spec +{ + /// + /// An uncompiled, unoptimize for of stream specification created by a parser. + /// + public interface StreamSpecRaw : StreamSpec + { + /// + /// Compiles a raw stream specification consisting event type information and filter + /// expressions to an validated, optimized form for use with filter service + /// + /// statement-level services + /// event type names used by the statement + /// true for insert-into + /// The assigned type number stack. + /// indicates whether a join or not a join + /// indicates whether declared as part of the context declarations, if any + /// if set to true [is on trigger]. + /// + /// compiled stream + /// ExprValidationException to indicate validation errors + StreamSpecCompiled Compile( + StatementContext statementContext, + ICollection eventTypeReferences, + bool isInsertInto, + ICollection assignedTypeNumberStack, + bool isJoin, + bool isContextDeclaration, + bool isOnTrigger, + string optionalStreamName); + + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/SubstitutionParameterExpressionBase.cs b/NEsper.Core/NEsper.Core/epl/spec/SubstitutionParameterExpressionBase.cs new file mode 100755 index 000000000..37b0b1140 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/SubstitutionParameterExpressionBase.cs @@ -0,0 +1,67 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.IO; + +using com.espertech.esper.client.soda; +using com.espertech.esper.core.service; + +namespace com.espertech.esper.epl.spec +{ + /// + /// Substitution parameter that represents a node in an expression tree for which to supply a parameter value + /// before statement creation time. + /// + public abstract class SubstitutionParameterExpressionBase : ExpressionBase + { + private object _constant; + private bool _isSatisfied; + + protected abstract void ToPrecedenceFreeEPLUnsatisfied(TextWriter writer); + + public override ExpressionPrecedenceEnum Precedence + { + get { return ExpressionPrecedenceEnum.UNARY; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + if (!_isSatisfied) + { + ToPrecedenceFreeEPLUnsatisfied(writer); + } + else + { + EPStatementObjectModelHelper.RenderEPL(writer, _constant); + } + } + + /// + /// Returns the constant value that the expression represents. + /// + /// value of constant + public object Constant + { + get { return _constant; } + set + { + _constant = value; + _isSatisfied = true; + } + } + + /// + /// Returns true if the parameter is satisfied, or false if not. + /// + /// true if the actual value is supplied, false if not + public bool IsSatisfied + { + get { return _isSatisfied; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/spec/SubstitutionParameterExpressionIndexed.cs b/NEsper.Core/NEsper.Core/epl/spec/SubstitutionParameterExpressionIndexed.cs new file mode 100755 index 000000000..7d32bc9d7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/SubstitutionParameterExpressionIndexed.cs @@ -0,0 +1,33 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.epl.spec +{ + [Serializable] + public class SubstitutionParameterExpressionIndexed : SubstitutionParameterExpressionBase + { + /// + /// Ctor. + /// + /// is the index of the substitution parameter + public SubstitutionParameterExpressionIndexed(int index) + { + Index = index; + } + + protected override void ToPrecedenceFreeEPLUnsatisfied(TextWriter writer) + { + writer.Write("?"); + } + + public int Index { get; private set; } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/spec/SubstitutionParameterExpressionNamed.cs b/NEsper.Core/NEsper.Core/epl/spec/SubstitutionParameterExpressionNamed.cs new file mode 100755 index 000000000..3e2e7cdee --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/SubstitutionParameterExpressionNamed.cs @@ -0,0 +1,30 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.epl.spec +{ + [Serializable] + public class SubstitutionParameterExpressionNamed : SubstitutionParameterExpressionBase + { + public SubstitutionParameterExpressionNamed(string name) + { + Name = name; + } + + protected override void ToPrecedenceFreeEPLUnsatisfied(TextWriter writer) + { + writer.Write("?:"); + writer.Write(Name); + } + + public string Name { get; private set; } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/spec/TableQueryStreamSpec.cs b/NEsper.Core/NEsper.Core/epl/spec/TableQueryStreamSpec.cs new file mode 100755 index 000000000..eae736c91 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/TableQueryStreamSpec.cs @@ -0,0 +1,35 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.spec +{ + /// + /// Specification for use of an existing table. + /// + [Serializable] + public class TableQueryStreamSpec + : StreamSpecBase + , StreamSpecCompiled + { + public TableQueryStreamSpec(string optionalStreamName, ViewSpec[] viewSpecs, StreamSpecOptions streamSpecOptions, string tableName, IList filterExpressions) + : base(optionalStreamName, viewSpecs, streamSpecOptions) + { + TableName = tableName; + FilterExpressions = filterExpressions; + } + + public string TableName { get; private set; } + + public IList FilterExpressions { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/UpdateDesc.cs b/NEsper.Core/NEsper.Core/epl/spec/UpdateDesc.cs new file mode 100755 index 000000000..d0297477d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/UpdateDesc.cs @@ -0,0 +1,44 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.spec +{ + /// Specification for the Update statement. + [Serializable] + public class UpdateDesc : MetaDefItem + { + /// Ctor. + /// a stream name if provided for the Update + /// the individual assignments made + /// the where-clause expression if provided + public UpdateDesc(String optionalStreamName, IList assignments, ExprNode optionalWhereClause) + { + OptionalStreamName = optionalStreamName; + Assignments = assignments; + OptionalWhereClause = optionalWhereClause; + } + + /// Returns a list of all assignment + /// list of assignments + public IList Assignments { get; private set; } + + /// Returns the stream name if defined. + /// stream name + public string OptionalStreamName { get; private set; } + + /// Returns the where-clause if defined. + /// where clause + public ExprNode OptionalWhereClause { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/spec/ViewSpec.cs b/NEsper.Core/NEsper.Core/epl/spec/ViewSpec.cs new file mode 100755 index 000000000..25b4f63db --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/ViewSpec.cs @@ -0,0 +1,44 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.spec +{ + /// + /// Specification for a view object consists of a namespace, name and view object parameters. + /// + [Serializable] + public sealed class ViewSpec : ObjectSpec + { + public static readonly ViewSpec[] EMPTY_VIEWSPEC_ARRAY = new ViewSpec[0]; + + /// Constructor. + /// if the namespace the object is in + /// is the name of the object + /// is a list of expressions representing the view parameters + public ViewSpec(String @namespace, String objectName, IList viewParameters) + : base(@namespace, objectName, viewParameters) + { + } + + public static ViewSpec[] ToArray(IList viewSpecs) + { + if (viewSpecs.IsEmpty()) + { + return EMPTY_VIEWSPEC_ARRAY; + } + return viewSpecs.ToArray(); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/spec/util/StatementSpecCompiledAnalyzer.cs b/NEsper.Core/NEsper.Core/epl/spec/util/StatementSpecCompiledAnalyzer.cs new file mode 100755 index 000000000..3d9111851 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/util/StatementSpecCompiledAnalyzer.cs @@ -0,0 +1,64 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.epl.expression.subquery; +using com.espertech.esper.filter; +using com.espertech.esper.pattern; + +namespace com.espertech.esper.epl.spec.util +{ + public class StatementSpecCompiledAnalyzer + { + public static StatementSpecCompiledAnalyzerResult AnalyzeFilters(StatementSpecCompiled spec) + { + var filters = new List(); + var namedWindows = new List(); + + AddFilters(spec.StreamSpecs, filters, namedWindows); + + foreach (ExprSubselectNode subselect in spec.SubSelectExpressions) + { + AddFilters(subselect.StatementSpecCompiled.StreamSpecs, filters, namedWindows); + } + + return new StatementSpecCompiledAnalyzerResult(filters, namedWindows); + } + + private static void AddFilters( + StreamSpecCompiled[] streams, + List filters, + List namedWindows) + { + foreach (StreamSpecCompiled compiled in streams) + { + if (compiled is FilterStreamSpecCompiled) + { + var c = (FilterStreamSpecCompiled) compiled; + filters.Add(c.FilterSpec); + } + if (compiled is PatternStreamSpecCompiled) + { + var r = (PatternStreamSpecCompiled) compiled; + EvalNodeAnalysisResult evalNodeAnalysisResult = + EvalNodeUtil.RecursiveAnalyzeChildNodes(r.EvalFactoryNode); + IList filterNodes = evalNodeAnalysisResult.FilterNodes; + foreach (EvalFilterFactoryNode filterNode in filterNodes) + { + filters.Add(filterNode.FilterSpec); + } + } + if (compiled is NamedWindowConsumerStreamSpec) + { + namedWindows.Add((NamedWindowConsumerStreamSpec) compiled); + } + } + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/spec/util/StatementSpecCompiledAnalyzerResult.cs b/NEsper.Core/NEsper.Core/epl/spec/util/StatementSpecCompiledAnalyzerResult.cs new file mode 100755 index 000000000..f9c655811 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/util/StatementSpecCompiledAnalyzerResult.cs @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using com.espertech.esper.filter; + +namespace com.espertech.esper.epl.spec.util +{ + public class StatementSpecCompiledAnalyzerResult + { + public StatementSpecCompiledAnalyzerResult(IList filters, + IList namedWindowConsumers) + { + Filters = filters; + NamedWindowConsumers = namedWindowConsumers; + } + + public IList Filters { get; private set; } + + public IList NamedWindowConsumers { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/spec/util/StatementSpecRawAnalyzer.cs b/NEsper.Core/NEsper.Core/epl/spec/util/StatementSpecRawAnalyzer.cs new file mode 100755 index 000000000..95d345bba --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/spec/util/StatementSpecRawAnalyzer.cs @@ -0,0 +1,520 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.visitor; +using com.espertech.esper.pattern; + +namespace com.espertech.esper.epl.spec.util +{ + public class StatementSpecRawAnalyzer + { + public static IList AnalyzeFilters(StatementSpecRaw spec) { + IList result = new List(); + AddFilters(spec, result); + + var subselects = WalkSubselectAndDeclaredDotExpr(spec); + foreach (var subselect in subselects.Subselects) { + AddFilters(subselect.StatementSpecRaw, result); + } + return result; + } + + private static void AddFilters(StatementSpecRaw spec, IList filters) { + foreach (var raw in spec.StreamSpecs) { + if (raw is FilterStreamSpecRaw) { + var r = (FilterStreamSpecRaw) raw; + filters.Add(r.RawFilterSpec); + } + if (raw is PatternStreamSpecRaw) { + var r = (PatternStreamSpecRaw) raw; + var evalNodeAnalysisResult = EvalNodeUtil.RecursiveAnalyzeChildNodes(r.EvalFactoryNode); + var filterNodes = evalNodeAnalysisResult.FilterNodes; + foreach (var filterNode in filterNodes) + { + filters.Add(filterNode.RawFilterSpec); + } + } + } + } + + public static ExprNodeSubselectDeclaredDotVisitor WalkSubselectAndDeclaredDotExpr(StatementSpecRaw spec) + { + // Look for expressions with sub-selects in select expression list and filter expression + // Recursively compile the statement within the statement. + var visitor = new ExprNodeSubselectDeclaredDotVisitor(); + foreach (var raw in spec.SelectClauseSpec.SelectExprList) + { + if (raw is SelectClauseExprRawSpec) + { + var rawExpr = (SelectClauseExprRawSpec) raw; + rawExpr.SelectExpression.Accept(visitor); + } + } + if (spec.FilterRootNode != null) + { + spec.FilterRootNode.Accept(visitor); + } + if (spec.HavingExprRootNode != null) + { + spec.HavingExprRootNode.Accept(visitor); + } + if (spec.UpdateDesc != null) + { + if (spec.UpdateDesc.OptionalWhereClause != null) + { + spec.UpdateDesc.OptionalWhereClause.Accept(visitor); + } + foreach (var assignment in spec.UpdateDesc.Assignments) + { + assignment.Expression.Accept(visitor); + } + } + if (spec.OnTriggerDesc != null) { + VisitSubselectOnTrigger(spec.OnTriggerDesc, visitor); + } + // Determine pattern-filter subqueries + foreach (var streamSpecRaw in spec.StreamSpecs) { + if (streamSpecRaw is PatternStreamSpecRaw) { + var patternStreamSpecRaw = (PatternStreamSpecRaw) streamSpecRaw; + var analysisResult = EvalNodeUtil.RecursiveAnalyzeChildNodes(patternStreamSpecRaw.EvalFactoryNode); + foreach (var evalNode in analysisResult.ActiveNodes) { + if (evalNode is EvalFilterFactoryNode) { + var filterNode = (EvalFilterFactoryNode) evalNode; + foreach (var filterExpr in filterNode.RawFilterSpec.FilterExpressions) { + filterExpr.Accept(visitor); + } + } + else if (evalNode is EvalObserverFactoryNode) { + var beforeCount = visitor.Subselects.Count; + var observerNode = (EvalObserverFactoryNode) evalNode; + foreach (var param in observerNode.PatternObserverSpec.ObjectParameters) { + param.Accept(visitor); + } + if (visitor.Subselects.Count != beforeCount) { + throw new ExprValidationException("Subselects are not allowed within pattern observer parameters, please consider using a variable instead"); + } + } + } + } + } + // Determine filter streams + foreach (var rawSpec in spec.StreamSpecs) + { + if (rawSpec is FilterStreamSpecRaw) { + var raw = (FilterStreamSpecRaw) rawSpec; + foreach (var filterExpr in raw.RawFilterSpec.FilterExpressions) { + filterExpr.Accept(visitor); + } + } + } + + return visitor; + } + + private static void VisitSubselectOnTrigger(OnTriggerDesc onTriggerDesc, ExprNodeSubselectDeclaredDotVisitor visitor) { + if (onTriggerDesc is OnTriggerWindowUpdateDesc) { + var updates = (OnTriggerWindowUpdateDesc) onTriggerDesc; + foreach (var assignment in updates.Assignments) + { + assignment.Expression.Accept(visitor); + } + } + else if (onTriggerDesc is OnTriggerSetDesc) { + var sets = (OnTriggerSetDesc) onTriggerDesc; + foreach (var assignment in sets.Assignments) + { + assignment.Expression.Accept(visitor); + } + } + else if (onTriggerDesc is OnTriggerSplitStreamDesc) { + var splits = (OnTriggerSplitStreamDesc) onTriggerDesc; + foreach (var split in splits.SplitStreams) + { + if (split.WhereClause != null) { + split.WhereClause.Accept(visitor); + } + if (split.SelectClause.SelectExprList != null) { + foreach (var element in split.SelectClause.SelectExprList) { + if (element is SelectClauseExprRawSpec) { + var selectExpr = (SelectClauseExprRawSpec) element; + selectExpr.SelectExpression.Accept(visitor); + } + } + } + } + } + else if (onTriggerDesc is OnTriggerMergeDesc) { + var merge = (OnTriggerMergeDesc) onTriggerDesc; + foreach (var matched in merge.Items) { + if (matched.OptionalMatchCond != null) { + matched.OptionalMatchCond.Accept(visitor); + } + foreach (var action in matched.Actions) + { + if (action.OptionalWhereClause != null) { + action.OptionalWhereClause.Accept(visitor); + } + + if (action is OnTriggerMergeActionUpdate) { + var update = (OnTriggerMergeActionUpdate) action; + foreach (var assignment in update.Assignments) + { + assignment.Expression.Accept(visitor); + } + } + if (action is OnTriggerMergeActionInsert) { + var insert = (OnTriggerMergeActionInsert) action; + foreach (var element in insert.SelectClause) { + if (element is SelectClauseExprRawSpec) { + var selectExpr = (SelectClauseExprRawSpec) element; + selectExpr.SelectExpression.Accept(visitor); + } + } + } + } + } + } + } + + public static IList CollectExpressionsShallow(StatementSpecRaw raw) { + var expressions = new List(); + + if (raw.ExpressionDeclDesc != null) { + foreach (var decl in raw.ExpressionDeclDesc.Expressions) { + expressions.Add(decl.Inner); + } + } + + if (raw.CreateExpressionDesc != null) { + if (raw.CreateExpressionDesc.Expression != null) { + expressions.Add(raw.CreateExpressionDesc.Expression.Inner); + } + } + + if (raw.CreateContextDesc != null) { + var detail = raw.CreateContextDesc.ContextDetail; + if (detail is ContextDetailPartitioned) { + var ks = (ContextDetailPartitioned) detail; + foreach (var item in ks.Items) { + if (item.FilterSpecRaw.FilterExpressions != null) { + expressions.AddAll(item.FilterSpecRaw.FilterExpressions); + } + } + } + else if (detail is ContextDetailCategory) { + var cat = (ContextDetailCategory) detail; + foreach (var item in cat.Items) { + if (item.Expression != null) { + expressions.Add(item.Expression); + } + } + if (cat.FilterSpecRaw.FilterExpressions != null) { + expressions.AddAll(cat.FilterSpecRaw.FilterExpressions); + } + } + else if (detail is ContextDetailInitiatedTerminated) { + var ts = (ContextDetailInitiatedTerminated) detail; + CollectExpressions(expressions, ts.Start); + CollectExpressions(expressions, ts.End); + } + else { + throw new EPException("Failed to obtain expressions from context detail " + detail); + } + } + + if (raw.CreateVariableDesc != null) { + var expr = raw.CreateVariableDesc.Assignment; + if (expr != null) { + expressions.Add(expr); + } + } + + if (raw.CreateWindowDesc != null) { + var expr = raw.CreateWindowDesc.InsertFilter; + if (expr != null) { + expressions.Add(expr); + } + foreach (var view in raw.CreateWindowDesc.ViewSpecs) { + expressions.AddAll(view.ObjectParameters); + } + } + + if (raw.UpdateDesc != null) { + if (raw.UpdateDesc.OptionalWhereClause != null) { + expressions.Add(raw.UpdateDesc.OptionalWhereClause); + } + if (raw.UpdateDesc.Assignments != null) { + foreach (var pair in raw.UpdateDesc.Assignments) { + expressions.Add(pair.Expression); + } + } + } + + // on-expr + if (raw.OnTriggerDesc != null) { + if (raw.OnTriggerDesc is OnTriggerSplitStreamDesc) { + var onSplit = (OnTriggerSplitStreamDesc) raw.OnTriggerDesc; + foreach (var item in onSplit.SplitStreams) { + if (item.SelectClause != null) { + AddSelectClause(expressions, item.SelectClause.SelectExprList); + } + if (item.WhereClause != null) { + expressions.Add(item.WhereClause); + } + } + } + if (raw.OnTriggerDesc is OnTriggerSetDesc) { + var onSet = (OnTriggerSetDesc) raw.OnTriggerDesc; + if (onSet.Assignments != null) { + foreach (var aitem in onSet.Assignments) { + expressions.Add(aitem.Expression); + } + } + } + if (raw.OnTriggerDesc is OnTriggerWindowUpdateDesc) { + var onUpdate = (OnTriggerWindowUpdateDesc) raw.OnTriggerDesc; + if (onUpdate.Assignments != null) { + foreach (var bitem in onUpdate.Assignments) { + expressions.Add(bitem.Expression); + } + } + } + if (raw.OnTriggerDesc is OnTriggerMergeDesc) { + var onMerge = (OnTriggerMergeDesc) raw.OnTriggerDesc; + foreach (var item in onMerge.Items) { + if (item.OptionalMatchCond != null) { + expressions.Add(item.OptionalMatchCond); + } + foreach (var action in item.Actions) { + if (action is OnTriggerMergeActionDelete) { + var delete = (OnTriggerMergeActionDelete) action; + if (delete.OptionalWhereClause != null) { + expressions.Add(delete.OptionalWhereClause); + } + } + else if (action is OnTriggerMergeActionUpdate) { + var update = (OnTriggerMergeActionUpdate) action; + if (update.OptionalWhereClause != null) { + expressions.Add(update.OptionalWhereClause); + } + foreach (var assignment in update.Assignments) { + expressions.Add(assignment.Expression); + } + } + else if (action is OnTriggerMergeActionInsert) { + var insert = (OnTriggerMergeActionInsert) action; + if (insert.OptionalWhereClause != null) { + expressions.Add(insert.OptionalWhereClause); + } + AddSelectClause(expressions, insert.SelectClause); + } + } + } + } + } + + // select clause + if (raw.SelectClauseSpec != null) { + AddSelectClause(expressions, raw.SelectClauseSpec.SelectExprList); + } + + // from clause + if (raw.StreamSpecs != null) { + foreach (var stream in raw.StreamSpecs) { + // filter stream + if (stream is FilterStreamSpecRaw) { + var filterStream = (FilterStreamSpecRaw) stream; + var filter = filterStream.RawFilterSpec; + if ((filter != null) && (filter.FilterExpressions != null)){ + expressions.AddAll(filter.FilterExpressions); + } + if ((filter != null) && (filter.OptionalPropertyEvalSpec != null)) { + foreach (var contained in filter.OptionalPropertyEvalSpec.Atoms) { + AddSelectClause(expressions, contained.OptionalSelectClause == null ? null : contained.OptionalSelectClause.SelectExprList); + if (contained.OptionalWhereClause != null) { + expressions.Add(contained.OptionalWhereClause); + } + } + } + } + // pattern stream + if (stream is PatternStreamSpecRaw) { + var patternStream = (PatternStreamSpecRaw) stream; + CollectPatternExpressions(expressions, patternStream.EvalFactoryNode); + } + // method stream + if (stream is MethodStreamSpec) { + var methodStream = (MethodStreamSpec) stream; + if (methodStream.Expressions != null) { + expressions.AddAll(methodStream.Expressions); + } + } + if (stream.ViewSpecs != null) { + foreach (var view in stream.ViewSpecs) { + expressions.AddAll(view.ObjectParameters); + } + } + } + + if (raw.OuterJoinDescList != null) { + foreach (var q in raw.OuterJoinDescList) { + if (q.OptLeftNode != null) { + expressions.Add(q.OptLeftNode); + expressions.Add(q.OptRightNode); + foreach (var ident in q.AdditionalLeftNodes) { + expressions.Add(ident); + } + foreach (var ident in q.AdditionalRightNodes) { + expressions.Add(ident); + } + } + } + } + } + + if (raw.FilterRootNode != null) { + expressions.Add(raw.FilterRootNode); + } + + if (raw.GroupByExpressions != null) { + foreach (GroupByClauseElement element in raw.GroupByExpressions) { + if (element is GroupByClauseElementExpr) { + expressions.Add( ((GroupByClauseElementExpr) element).Expr); + } + else if (element is GroupByClauseElementRollupOrCube) { + var rollup = (GroupByClauseElementRollupOrCube) element; + AnalyzeRollup(rollup, expressions); + } + else { + var set = (GroupByClauseElementGroupingSet) element; + foreach (GroupByClauseElement inner in set.Elements) { + if (inner is GroupByClauseElementExpr) { + expressions.Add( ((GroupByClauseElementExpr) inner).Expr); + } + else if (inner is GroupByClauseElementCombinedExpr) + { + expressions.AddAll( ((GroupByClauseElementCombinedExpr) inner).Expressions); + } + else { + AnalyzeRollup((GroupByClauseElementRollupOrCube) inner, expressions); + } + } + } + } + } + + if (raw.HavingExprRootNode != null) { + expressions.Add(raw.HavingExprRootNode); + } + + if (raw.OutputLimitSpec != null) { + if (raw.OutputLimitSpec.WhenExpressionNode != null) { + expressions.Add(raw.OutputLimitSpec.WhenExpressionNode); + } + if (raw.OutputLimitSpec.ThenExpressions != null) { + foreach (var thenAssign in raw.OutputLimitSpec.ThenExpressions) { + expressions.Add(thenAssign.Expression); + } + } + if (raw.OutputLimitSpec.CrontabAtSchedule != null) { + expressions.AddAll(raw.OutputLimitSpec.CrontabAtSchedule); + } + if (raw.OutputLimitSpec.TimePeriodExpr != null) { + expressions.Add(raw.OutputLimitSpec.TimePeriodExpr); + } + if (raw.OutputLimitSpec.AfterTimePeriodExpr != null) { + expressions.Add(raw.OutputLimitSpec.AfterTimePeriodExpr); + } + } + + if (raw.OrderByList != null) { + foreach (var orderByElement in raw.OrderByList) { + expressions.Add(orderByElement.ExprNode); + } + } + + if (raw.MatchRecognizeSpec != null) { + if (raw.MatchRecognizeSpec.PartitionByExpressions != null) { + expressions.AddAll(raw.MatchRecognizeSpec.PartitionByExpressions); + } + foreach (var selectItemMR in raw.MatchRecognizeSpec.Measures) { + expressions.Add(selectItemMR.Expr); + } + foreach (var define in raw.MatchRecognizeSpec.Defines) { + expressions.Add(define.Expression); + } + if (raw.MatchRecognizeSpec.Interval != null) { + if (raw.MatchRecognizeSpec.Interval.TimePeriodExpr != null) { + expressions.Add(raw.MatchRecognizeSpec.Interval.TimePeriodExpr); + } + } + } + + if (raw.ForClauseSpec != null) { + foreach (var item in raw.ForClauseSpec.Clauses) { + if (item.Expressions != null) { + expressions.AddAll(item.Expressions); + } + } + } + + return expressions; + } + + private static void AnalyzeRollup(GroupByClauseElementRollupOrCube rollup, List expressions) { + foreach (GroupByClauseElement ex in rollup.RollupExpressions) { + if (ex is GroupByClauseElementExpr) { + expressions.Add( ((GroupByClauseElementExpr) ex).Expr); + } + else { + var combined = (GroupByClauseElementCombinedExpr) ex; + expressions.AddAll(combined.Expressions); + } + } + } + + private static void CollectExpressions(IList expressions, ContextDetailCondition endpoint) { + if (endpoint is ContextDetailConditionCrontab) { + var crontab = (ContextDetailConditionCrontab) endpoint; + expressions.AddAll(crontab.Crontab); + } + } + + private static void AddSelectClause(IList expressions, IList selectClause) { + if (selectClause == null) { + return; + } + foreach (var selement in selectClause) { + if (!(selement is SelectClauseExprRawSpec)) { + continue; + } + var sexpr = (SelectClauseExprRawSpec) selement; + expressions.Add(sexpr.SelectExpression); + } + } + + private static void CollectPatternExpressions(IList expressions, EvalFactoryNode patternExpression) { + + if (patternExpression is EvalFilterFactoryNode) { + var filter = (EvalFilterFactoryNode) patternExpression; + if (filter.RawFilterSpec.FilterExpressions != null) { + expressions.AddAll(filter.RawFilterSpec.FilterExpressions); + } + } + + foreach (var child in patternExpression.ChildNodes) { + CollectPatternExpressions(expressions, child); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/subquery/SubqueryStopCallback.cs b/NEsper.Core/NEsper.Core/epl/subquery/SubqueryStopCallback.cs new file mode 100755 index 000000000..678ac747c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/subquery/SubqueryStopCallback.cs @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.join.table; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.subquery +{ + /// + /// : a stop callback for use with subqueries to clear their indexes when a statement is stopped. + /// + public class SubqueryStopCallback : StopCallback + { + private readonly EventTable[] _eventIndex; + + /// Ctor. + /// index to clear + public SubqueryStopCallback(EventTable[] eventIndex) + { + _eventIndex = eventIndex; + } + + // Clear out index on statement stop + public void Stop() + { + if (_eventIndex != null) + { + foreach (EventTable table in _eventIndex) + { + table.Destroy(); + } + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/subquery/SubselectAggregationPreprocessorBase.cs b/NEsper.Core/NEsper.Core/epl/subquery/SubselectAggregationPreprocessorBase.cs new file mode 100755 index 000000000..3db91df0f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/subquery/SubselectAggregationPreprocessorBase.cs @@ -0,0 +1,64 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.subquery +{ + public abstract class SubselectAggregationPreprocessorBase + { + private readonly AggregationService _aggregationService; + private readonly ExprEvaluator _filterEval; + private readonly ExprEvaluator[] _groupKeys; + + protected SubselectAggregationPreprocessorBase(AggregationService aggregationService, ExprEvaluator filterEval, ExprEvaluator[] groupKeys) + { + _aggregationService = aggregationService; + _filterEval = filterEval; + _groupKeys = groupKeys; + } + + public AggregationService AggregationService + { + get { return _aggregationService; } + } + + public ExprEvaluator FilterEval + { + get { return _filterEval; } + } + + public ExprEvaluator[] GroupKeys + { + get { return _groupKeys; } + } + + public abstract void Evaluate(EventBean[] eventsPerStream, ICollection matchingEvents, ExprEvaluatorContext exprEvaluatorContext); + + protected Object GenerateGroupKey(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext exprEvaluatorContext) + { + var evaluateParams = new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext); + if (_groupKeys.Length == 1) + { + return _groupKeys[0].Evaluate(evaluateParams); + } + var keys = new Object[_groupKeys.Length]; + for (int i = 0; i < _groupKeys.Length; i++) + { + keys[i] = _groupKeys[i].Evaluate(evaluateParams); + } + return new MultiKeyUntyped(keys); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/subquery/SubselectAggregationPreprocessorFilteredGrouped.cs b/NEsper.Core/NEsper.Core/epl/subquery/SubselectAggregationPreprocessorFilteredGrouped.cs new file mode 100755 index 000000000..7ec463497 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/subquery/SubselectAggregationPreprocessorFilteredGrouped.cs @@ -0,0 +1,54 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.subquery +{ + public class SubselectAggregationPreprocessorFilteredGrouped : SubselectAggregationPreprocessorBase + { + public SubselectAggregationPreprocessorFilteredGrouped( + AggregationService aggregationService, + ExprEvaluator filterEval, + ExprEvaluator[] groupKeys) + : base(aggregationService, filterEval, groupKeys) + { + } + + public override void Evaluate( + EventBean[] eventsPerStream, + ICollection matchingEvents, + ExprEvaluatorContext exprEvaluatorContext) + { + AggregationService.ClearResults(exprEvaluatorContext); + if (matchingEvents == null) + { + return; + } + var events = new EventBean[eventsPerStream.Length + 1]; + Array.Copy(eventsPerStream, 0, events, 1, eventsPerStream.Length); + var evaluateParams = new EvaluateParams(events, true, exprEvaluatorContext); + + foreach (EventBean subselectEvent in matchingEvents) + { + events[0] = subselectEvent; + var pass = FilterEval.Evaluate(evaluateParams); + if (true.Equals(pass)) + { + Object groupKey = GenerateGroupKey(events, true, exprEvaluatorContext); + AggregationService.ApplyEnter(events, groupKey, exprEvaluatorContext); + } + } + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/subquery/SubselectAggregationPreprocessorFilteredUngrouped.cs b/NEsper.Core/NEsper.Core/epl/subquery/SubselectAggregationPreprocessorFilteredUngrouped.cs new file mode 100755 index 000000000..ebb4aa73f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/subquery/SubselectAggregationPreprocessorFilteredUngrouped.cs @@ -0,0 +1,56 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.subquery +{ + public class SubselectAggregationPreprocessorFilteredUngrouped : SubselectAggregationPreprocessorBase + { + public SubselectAggregationPreprocessorFilteredUngrouped( + AggregationService aggregationService, + ExprEvaluator filterEval, + ExprEvaluator[] groupKeys) + : base(aggregationService, filterEval, groupKeys) + { + } + + public override void Evaluate( + EventBean[] eventsPerStream, + ICollection matchingEvents, + ExprEvaluatorContext exprEvaluatorContext) + { + var aggregationService = AggregationService; + var filterEval = FilterEval; + + aggregationService.ClearResults(exprEvaluatorContext); + if (matchingEvents == null) + { + return; + } + var events = new EventBean[eventsPerStream.Length + 1]; + Array.Copy(eventsPerStream, 0, events, 1, eventsPerStream.Length); + var evaluateParams = new EvaluateParams(events, true, exprEvaluatorContext); + + foreach (EventBean subselectEvent in matchingEvents) + { + events[0] = subselectEvent; + var pass = filterEval.Evaluate(evaluateParams); + if (true.Equals(pass)) + { + aggregationService.ApplyEnter(events, null, exprEvaluatorContext); + } + } + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/subquery/SubselectAggregationPreprocessorUnfilteredGrouped.cs b/NEsper.Core/NEsper.Core/epl/subquery/SubselectAggregationPreprocessorUnfilteredGrouped.cs new file mode 100755 index 000000000..d1688c42e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/subquery/SubselectAggregationPreprocessorUnfilteredGrouped.cs @@ -0,0 +1,49 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.subquery +{ + public class SubselectAggregationPreprocessorUnfilteredGrouped : SubselectAggregationPreprocessorBase + { + public SubselectAggregationPreprocessorUnfilteredGrouped( + AggregationService aggregationService, + ExprEvaluator filterEval, + ExprEvaluator[] groupKeys) + : base(aggregationService, filterEval, groupKeys) + { + } + + public override void Evaluate( + EventBean[] eventsPerStream, + ICollection matchingEvents, + ExprEvaluatorContext exprEvaluatorContext) + { + AggregationService.ClearResults(exprEvaluatorContext); + if (matchingEvents == null) + { + return; + } + var events = new EventBean[eventsPerStream.Length + 1]; + Array.Copy(eventsPerStream, 0, events, 1, eventsPerStream.Length); + + foreach (EventBean subselectEvent in matchingEvents) + { + events[0] = subselectEvent; + Object groupKey = GenerateGroupKey(events, true, exprEvaluatorContext); + AggregationService.ApplyEnter(events, groupKey, exprEvaluatorContext); + } + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/subquery/SubselectAggregationPreprocessorUnfilteredUngrouped.cs b/NEsper.Core/NEsper.Core/epl/subquery/SubselectAggregationPreprocessorUnfilteredUngrouped.cs new file mode 100755 index 000000000..a6695f591 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/subquery/SubselectAggregationPreprocessorUnfilteredUngrouped.cs @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.subquery +{ + public class SubselectAggregationPreprocessorUnfilteredUngrouped : SubselectAggregationPreprocessorBase + { + public SubselectAggregationPreprocessorUnfilteredUngrouped( + AggregationService aggregationService, + ExprEvaluator filterEval, + ExprEvaluator[] groupKeys) + : base(aggregationService, filterEval, groupKeys) + { + } + + public override void Evaluate( + EventBean[] eventsPerStream, + ICollection matchingEvents, + ExprEvaluatorContext exprEvaluatorContext) + { + var aggregationService = AggregationService; + + aggregationService.ClearResults(exprEvaluatorContext); + if (matchingEvents == null) + { + return; + } + var events = new EventBean[eventsPerStream.Length + 1]; + Array.Copy(eventsPerStream, 0, events, 1, eventsPerStream.Length); + + foreach (EventBean subselectEvent in matchingEvents) + { + events[0] = subselectEvent; + aggregationService.ApplyEnter(events, null, exprEvaluatorContext); + } + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/subquery/SubselectAggregatorViewBase.cs b/NEsper.Core/NEsper.Core/epl/subquery/SubselectAggregatorViewBase.cs new file mode 100755 index 000000000..6f5eab0fc --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/subquery/SubselectAggregatorViewBase.cs @@ -0,0 +1,78 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.view; + +namespace com.espertech.esper.epl.subquery +{ + /// + /// View handling the insert and remove stream generated by a subselect for application to aggregation state. + /// + public abstract class SubselectAggregatorViewBase : ViewSupport + { + protected readonly AggregationService AggregationService; + protected readonly ExprEvaluator OptionalFilterExpr; + protected readonly ExprEvaluatorContext ExprEvaluatorContext; + protected readonly ExprEvaluator[] GroupKeys; + protected readonly EventBean[] EventsPerStream = new EventBean[1]; + + public SubselectAggregatorViewBase( + AggregationService aggregationService, + ExprEvaluator optionalFilterExpr, + ExprEvaluatorContext exprEvaluatorContext, + ExprEvaluator[] groupKeys) + { + AggregationService = aggregationService; + OptionalFilterExpr = optionalFilterExpr; + ExprEvaluatorContext = exprEvaluatorContext; + GroupKeys = groupKeys; + } + + public override EventType EventType + { + get { return Parent.EventType; } + } + + public override IEnumerator GetEnumerator() + { + return Parent.GetEnumerator(); + } + + protected bool Filter(bool isNewData) + { + var result = + (bool?) + OptionalFilterExpr.Evaluate(new EvaluateParams(EventsPerStream, isNewData, ExprEvaluatorContext)); + return result ?? false; + } + + protected Object GenerateGroupKey(bool isNewData) + { + var evaluateParams = new EvaluateParams(EventsPerStream, isNewData, ExprEvaluatorContext); + + if (GroupKeys.Length == 1) + { + return GroupKeys[0].Evaluate(evaluateParams); + } + var keys = new Object[GroupKeys.Length]; + for (int i = 0; i < GroupKeys.Length; i++) + { + keys[i] = GroupKeys[i].Evaluate(evaluateParams); + } + return new MultiKeyUntyped(keys); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/subquery/SubselectAggregatorViewFilteredGrouped.cs b/NEsper.Core/NEsper.Core/epl/subquery/SubselectAggregatorViewFilteredGrouped.cs new file mode 100755 index 000000000..86d147bbc --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/subquery/SubselectAggregatorViewFilteredGrouped.cs @@ -0,0 +1,73 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.subquery +{ + public class SubselectAggregatorViewFilteredGrouped : SubselectAggregatorViewBase + { + private readonly ExprNode _filterExprNode; + + public SubselectAggregatorViewFilteredGrouped( + AggregationService aggregationService, + ExprEvaluator optionalFilterExpr, + ExprEvaluatorContext exprEvaluatorContext, + ExprEvaluator[] groupKeys, + ExprNode filterExprNode) + : base(aggregationService, optionalFilterExpr, exprEvaluatorContext, groupKeys) + { + _filterExprNode = filterExprNode; + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().QSubselectAggregation(_filterExprNode); + } + if (newData != null) + { + foreach (EventBean theEvent in newData) + { + EventsPerStream[0] = theEvent; + bool isPass = Filter(true); + if (isPass) + { + Object groupKey = GenerateGroupKey(true); + AggregationService.ApplyEnter(EventsPerStream, groupKey, ExprEvaluatorContext); + } + } + } + + if (oldData != null) + { + foreach (EventBean theEvent in oldData) + { + EventsPerStream[0] = theEvent; + bool isPass = Filter(false); + if (isPass) + { + Object groupKey = GenerateGroupKey(false); + AggregationService.ApplyLeave(EventsPerStream, groupKey, ExprEvaluatorContext); + } + } + } + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().ASubselectAggregation(); + } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/subquery/SubselectAggregatorViewFilteredUngrouped.cs b/NEsper.Core/NEsper.Core/epl/subquery/SubselectAggregatorViewFilteredUngrouped.cs new file mode 100755 index 000000000..2141dd299 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/subquery/SubselectAggregatorViewFilteredUngrouped.cs @@ -0,0 +1,69 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.subquery +{ + public class SubselectAggregatorViewFilteredUngrouped : SubselectAggregatorViewBase + { + private readonly ExprNode _filterExprNode; + + public SubselectAggregatorViewFilteredUngrouped( + AggregationService aggregationService, + ExprEvaluator optionalFilterExpr, + ExprEvaluatorContext exprEvaluatorContext, + ExprEvaluator[] groupKeys, + ExprNode filterExprNode) + : base(aggregationService, optionalFilterExpr, exprEvaluatorContext, groupKeys) + { + _filterExprNode = filterExprNode; + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().QSubselectAggregation(_filterExprNode); + } + if (newData != null) + { + foreach (EventBean theEvent in newData) + { + EventsPerStream[0] = theEvent; + bool isPass = Filter(true); + if (isPass) + { + AggregationService.ApplyEnter(EventsPerStream, null, ExprEvaluatorContext); + } + } + } + + if (oldData != null) + { + foreach (EventBean theEvent in oldData) + { + EventsPerStream[0] = theEvent; + bool isPass = Filter(false); + if (isPass) + { + AggregationService.ApplyLeave(EventsPerStream, null, ExprEvaluatorContext); + } + } + } + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().ASubselectAggregation(); + } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/subquery/SubselectAggregatorViewUnfilteredGrouped.cs b/NEsper.Core/NEsper.Core/epl/subquery/SubselectAggregatorViewUnfilteredGrouped.cs new file mode 100755 index 000000000..9c3113abd --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/subquery/SubselectAggregatorViewUnfilteredGrouped.cs @@ -0,0 +1,61 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.subquery +{ + public class SubselectAggregatorViewUnfilteredGrouped : SubselectAggregatorViewBase + { + public SubselectAggregatorViewUnfilteredGrouped( + AggregationService aggregationService, + ExprEvaluator optionalFilterExpr, + ExprEvaluatorContext exprEvaluatorContext, + ExprEvaluator[] groupKeys) + : base(aggregationService, optionalFilterExpr, exprEvaluatorContext, groupKeys) + { + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().QSubselectAggregation(null); + } + if (newData != null) + { + foreach (EventBean theEvent in newData) + { + EventsPerStream[0] = theEvent; + Object groupKey = GenerateGroupKey(true); + AggregationService.ApplyEnter(EventsPerStream, groupKey, ExprEvaluatorContext); + } + } + + if (oldData != null) + { + foreach (EventBean theEvent in oldData) + { + EventsPerStream[0] = theEvent; + Object groupKey = GenerateGroupKey(false); + AggregationService.ApplyLeave(EventsPerStream, groupKey, ExprEvaluatorContext); + } + } + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().ASubselectAggregation(); + } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/subquery/SubselectAggregatorViewUnfilteredUngrouped.cs b/NEsper.Core/NEsper.Core/epl/subquery/SubselectAggregatorViewUnfilteredUngrouped.cs new file mode 100755 index 000000000..6274e80c4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/subquery/SubselectAggregatorViewUnfilteredUngrouped.cs @@ -0,0 +1,57 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.subquery +{ + public class SubselectAggregatorViewUnfilteredUngrouped : SubselectAggregatorViewBase + { + public SubselectAggregatorViewUnfilteredUngrouped( + AggregationService aggregationService, + ExprEvaluator optionalFilterExpr, + ExprEvaluatorContext exprEvaluatorContext, + ExprEvaluator[] groupKeys) + : base(aggregationService, optionalFilterExpr, exprEvaluatorContext, groupKeys) + { + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().QSubselectAggregation(null); + } + if (newData != null) + { + foreach (EventBean theEvent in newData) + { + EventsPerStream[0] = theEvent; + AggregationService.ApplyEnter(EventsPerStream, null, ExprEvaluatorContext); + } + } + + if (oldData != null) + { + foreach (EventBean theEvent in oldData) + { + EventsPerStream[0] = theEvent; + AggregationService.ApplyLeave(EventsPerStream, null, ExprEvaluatorContext); + } + } + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().ASubselectAggregation(); + } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/subquery/SubselectBufferObserver.cs b/NEsper.Core/NEsper.Core/epl/subquery/SubselectBufferObserver.cs new file mode 100755 index 000000000..5f07138c6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/subquery/SubselectBufferObserver.cs @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.epl.join.table; +using com.espertech.esper.view.internals; + +namespace com.espertech.esper.epl.subquery +{ + /// + /// Observer to a buffer that is filled by a subselect view when it posts events, + /// to be added and removed from indexes. + /// + public class SubselectBufferObserver : BufferObserver + { + private readonly EventTable[] _eventIndex; + + /// Ctor. + /// index to Update + public SubselectBufferObserver(EventTable[] eventIndex) + { + _eventIndex = eventIndex; + } + + public void NewData(int streamId, FlushedEventBuffer newEventBuffer, FlushedEventBuffer oldEventBuffer) + { + EventBean[] newData = newEventBuffer.GetAndFlush(); + EventBean[] oldData = oldEventBuffer.GetAndFlush(); + foreach (EventTable table in _eventIndex) { + table.AddRemove(newData, oldData); + } + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/epl/table/merge/TableOnMergeAction.cs b/NEsper.Core/NEsper.Core/epl/table/merge/TableOnMergeAction.cs new file mode 100755 index 000000000..ebb66a367 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/merge/TableOnMergeAction.cs @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.epl.table.onaction; + +namespace com.espertech.esper.epl.table.merge +{ + public abstract class TableOnMergeAction { + + private readonly ExprEvaluator optionalFilter; + + protected TableOnMergeAction(ExprEvaluator optionalFilter) { + this.optionalFilter = optionalFilter; + } + + public bool IsApplies(EventBean[] eventsPerStream, ExprEvaluatorContext context) { + if (optionalFilter == null) { + return true; + } + object result = optionalFilter.Evaluate(new EvaluateParams(eventsPerStream, true, context)); + return result != null && (Boolean) result; + } + + public abstract void Apply(EventBean matchingEvent, + EventBean[] eventsPerStream, + TableStateInstance tableStateInstance, + TableOnMergeViewChangeHandler changeHandlerAdded, + TableOnMergeViewChangeHandler changeHandlerRemoved, + ExprEvaluatorContext exprEvaluatorContext); + + public abstract string Name { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/table/merge/TableOnMergeActionDel.cs b/NEsper.Core/NEsper.Core/epl/table/merge/TableOnMergeActionDel.cs new file mode 100755 index 000000000..c1c81b172 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/merge/TableOnMergeActionDel.cs @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.epl.table.onaction; + +namespace com.espertech.esper.epl.table.merge +{ + public class TableOnMergeActionDel : TableOnMergeAction + { + public TableOnMergeActionDel(ExprEvaluator optionalFilter) + : base(optionalFilter) + { + } + + public override void Apply(EventBean matchingEvent, EventBean[] eventsPerStream, TableStateInstance tableStateInstance, TableOnMergeViewChangeHandler changeHandlerAdded, TableOnMergeViewChangeHandler changeHandlerRemoved, ExprEvaluatorContext exprEvaluatorContext) + { + tableStateInstance.DeleteEvent(matchingEvent); + if (changeHandlerRemoved != null) { + changeHandlerRemoved.Add(matchingEvent, eventsPerStream, false, exprEvaluatorContext); + } + } + + public override string Name + { + get { return "delete"; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/table/merge/TableOnMergeActionIns.cs b/NEsper.Core/NEsper.Core/epl/table/merge/TableOnMergeActionIns.cs new file mode 100755 index 000000000..439a37ec1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/merge/TableOnMergeActionIns.cs @@ -0,0 +1,84 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.epl.table.onaction; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.table.merge +{ + public class TableOnMergeActionIns : TableOnMergeAction + { + private readonly bool _audit; + private readonly SelectExprProcessor _insertHelper; + private readonly InternalEventRouteDest _internalEventRouteDest; + private readonly InternalEventRouter _internalEventRouter; + private readonly EPStatementHandle _statementHandle; + private readonly TableStateRowFactory _tableStateRowFactory; + + public TableOnMergeActionIns( + ExprEvaluator optionalFilter, + SelectExprProcessor insertHelper, + InternalEventRouter internalEventRouter, + EPStatementHandle statementHandle, + InternalEventRouteDest internalEventRouteDest, + bool audit, + TableStateRowFactory tableStateRowFactory) + : base(optionalFilter) + { + _insertHelper = insertHelper; + _internalEventRouter = internalEventRouter; + _statementHandle = statementHandle; + _internalEventRouteDest = internalEventRouteDest; + _audit = audit; + _tableStateRowFactory = tableStateRowFactory; + } + + public override string Name + { + get { return _internalEventRouter != null ? "insert-into" : "select"; } + } + + public bool IsInsertIntoBinding + { + get { return _internalEventRouter == null; } + } + + public override void Apply( + EventBean matchingEvent, + EventBean[] eventsPerStream, + TableStateInstance tableStateInstance, + TableOnMergeViewChangeHandler changeHandlerAdded, + TableOnMergeViewChangeHandler changeHandlerRemoved, + ExprEvaluatorContext exprEvaluatorContext) + { + var theEvent = _insertHelper.Process(eventsPerStream, true, true, exprEvaluatorContext); + if (_internalEventRouter == null) + { + var aggs = _tableStateRowFactory.MakeAggs(exprEvaluatorContext.AgentInstanceId, null, null, tableStateInstance.AggregationServicePassThru); + ((object[]) theEvent.Underlying)[0] = aggs; + tableStateInstance.AddEvent(theEvent); + if (changeHandlerAdded != null) + { + changeHandlerAdded.Add(theEvent, eventsPerStream, true, exprEvaluatorContext); + } + return; + } + + if (_audit) + { + AuditPath.AuditInsertInto(_internalEventRouteDest.EngineURI, _statementHandle.StatementName, theEvent); + } + _internalEventRouter.Route(theEvent, _statementHandle, _internalEventRouteDest, exprEvaluatorContext, false); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/table/merge/TableOnMergeActionUpd.cs b/NEsper.Core/NEsper.Core/epl/table/merge/TableOnMergeActionUpd.cs new file mode 100755 index 000000000..499c928d3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/merge/TableOnMergeActionUpd.cs @@ -0,0 +1,56 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.epl.table.onaction; +using com.espertech.esper.epl.table.upd; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.epl.table.merge +{ + public class TableOnMergeActionUpd + : TableOnMergeAction + , TableUpdateStrategyReceiver + { + private TableUpdateStrategy _tableUpdateStrategy; + + public TableOnMergeActionUpd(ExprEvaluator optionalFilter, TableUpdateStrategy tableUpdateStrategy) + : base(optionalFilter) + { + _tableUpdateStrategy = tableUpdateStrategy; + } + + public void Update(TableUpdateStrategy updateStrategy) + { + _tableUpdateStrategy = updateStrategy; + } + + public override void Apply(EventBean matchingEvent, EventBean[] eventsPerStream, TableStateInstance tableStateInstance, TableOnMergeViewChangeHandler changeHandlerAdded, TableOnMergeViewChangeHandler changeHandlerRemoved, ExprEvaluatorContext exprEvaluatorContext) + { + if (changeHandlerRemoved != null) { + changeHandlerRemoved.Add(matchingEvent, eventsPerStream, false, exprEvaluatorContext); + } + _tableUpdateStrategy.UpdateTable(Collections.SingletonList(matchingEvent), tableStateInstance, eventsPerStream, exprEvaluatorContext); + if (changeHandlerAdded != null) { + changeHandlerAdded.Add(matchingEvent, eventsPerStream, false, exprEvaluatorContext); + } + } + + public override string Name + { + get { return "update"; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/table/merge/TableOnMergeHelper.cs b/NEsper.Core/NEsper.Core/epl/table/merge/TableOnMergeHelper.cs new file mode 100755 index 000000000..4d8ef1798 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/merge/TableOnMergeHelper.cs @@ -0,0 +1,153 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.client.annotation; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.named; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.epl.updatehelper; +using com.espertech.esper.events; +using com.espertech.esper.events.map; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.table.merge +{ + /// Factory for handles for updates/inserts/deletes/select + public class TableOnMergeHelper + { + private readonly IList _matched; + private readonly IList _unmatched; + private readonly bool _requiresWriteLock; + + public TableOnMergeHelper( + StatementContext statementContext, + OnTriggerMergeDesc onTriggerDesc, + EventType triggeringEventType, + string triggeringStreamName, + InternalEventRouter internalEventRouter, + TableMetadata tableMetadata) + { + _matched = new List(); + _unmatched = new List(); + + var count = 1; + var hasDeleteAction = false; + var hasInsertIntoTableAction = false; + var hasUpdateAction = false; + foreach (var matchedItem in onTriggerDesc.Items) { + var actions = new List(); + foreach (var item in matchedItem.Actions) { + try { + if (item is OnTriggerMergeActionInsert) { + var insertDesc = (OnTriggerMergeActionInsert) item; + var action = SetupInsert(tableMetadata, internalEventRouter, count, insertDesc, triggeringEventType, triggeringStreamName, statementContext); + actions.Add(action); + hasInsertIntoTableAction = action.IsInsertIntoBinding; + } else if (item is OnTriggerMergeActionUpdate) { + var updateDesc = (OnTriggerMergeActionUpdate) item; + var updateHelper = EventBeanUpdateHelperFactory.Make(tableMetadata.TableName, tableMetadata.InternalEventType, updateDesc.Assignments, onTriggerDesc.OptionalAsName, triggeringEventType, false, statementContext.StatementName, statementContext.EngineURI, statementContext.EventAdapterService); + var filterEval = updateDesc.OptionalWhereClause == null ? null : updateDesc.OptionalWhereClause.ExprEvaluator; + var tableUpdateStrategy = statementContext.TableService.GetTableUpdateStrategy(tableMetadata, updateHelper, true); + var upd = new TableOnMergeActionUpd(filterEval, tableUpdateStrategy); + actions.Add(upd); + statementContext.TableService.AddTableUpdateStrategyReceiver(tableMetadata, statementContext.StatementName, upd, updateHelper, true); + hasUpdateAction = true; + } else if (item is OnTriggerMergeActionDelete) { + var deleteDesc = (OnTriggerMergeActionDelete) item; + var filterEval = deleteDesc.OptionalWhereClause == null ? null : deleteDesc.OptionalWhereClause.ExprEvaluator; + actions.Add(new TableOnMergeActionDel(filterEval)); + hasDeleteAction = true; + } else { + throw new ArgumentException("Invalid type of merge item '" + item.GetType() + "'"); + } + count++; + } catch (ExprValidationException ex) { + var isNot = item is OnTriggerMergeActionInsert; + var message = "Validation failed in when-" + (isNot ? "not-" : "") + "matched (clause " + count + "): " + ex.Message; + throw new ExprValidationException(message, ex); + } + } + + if (matchedItem.IsMatchedUnmatched) { + _matched.Add(new TableOnMergeMatch(matchedItem.OptionalMatchCond, actions)); + } else { + _unmatched.Add(new TableOnMergeMatch(matchedItem.OptionalMatchCond, actions)); + } + } + + // since updates may change future secondary keys + _requiresWriteLock = hasDeleteAction || hasInsertIntoTableAction || hasUpdateAction; + } + + private TableOnMergeActionIns SetupInsert(TableMetadata tableMetadata, InternalEventRouter internalEventRouter, int selectClauseNumber, OnTriggerMergeActionInsert desc, EventType triggeringEventType, string triggeringStreamName, StatementContext statementContext) + { + + // Compile insert-into INFO + var streamName = desc.OptionalStreamName ?? tableMetadata.TableName; + var insertIntoDesc = InsertIntoDesc.FromColumns(streamName, desc.Columns); + EventType insertIntoTargetType = streamName.Equals(tableMetadata.TableName) ? tableMetadata.InternalEventType : null; + + // rewrite any wildcards to use "stream.wildcard" + if (triggeringStreamName == null) { + triggeringStreamName = UuidGenerator.Generate(); + } + var selectNoWildcard = NamedWindowOnMergeHelper.CompileSelectNoWildcard(triggeringStreamName, desc.SelectClauseCompiled); + + // Set up event types for select-clause evaluation: The first type does not contain anything as its the named window row which is not present for insert + var dummyTypeNoProperties = new MapEventType(EventTypeMetadata.CreateAnonymous("merge_named_window_insert", ApplicationType.MAP), "merge_named_window_insert", 0, null, Collections.EmptyDataMap, null, null, null); + var eventTypes = new EventType[]{dummyTypeNoProperties, triggeringEventType}; + var streamNames = new string[]{UuidGenerator.Generate(), triggeringStreamName}; + var streamTypeService = new StreamTypeServiceImpl(eventTypes, streamNames, new bool[1], statementContext.EngineURI, false); + + // Get select expr processor + var selectExprEventTypeRegistry = new SelectExprEventTypeRegistry(statementContext.StatementName, statementContext.StatementEventTypeRef); + var exprEvaluatorContext = new ExprEvaluatorContextStatement(statementContext, false); + var insertHelper = + SelectExprProcessorFactory.GetProcessor( + Collections.SingletonList(selectClauseNumber), + selectNoWildcard.ToArray(), false, insertIntoDesc, insertIntoTargetType, null, streamTypeService, + statementContext.EventAdapterService, statementContext.StatementResultService, + statementContext.ValueAddEventService, selectExprEventTypeRegistry, + statementContext.EngineImportService, exprEvaluatorContext, statementContext.VariableService, + statementContext.ScriptingService, statementContext.TableService, statementContext.TimeProvider, + statementContext.EngineURI, statementContext.StatementId, statementContext.StatementName, + statementContext.Annotations, statementContext.ContextDescriptor, statementContext.ConfigSnapshot, null, + statementContext.NamedWindowMgmtService, null, null, + statementContext.StatementExtensionServicesContext); + var filterEval = desc.OptionalWhereClause == null ? null : desc.OptionalWhereClause.ExprEvaluator; + + var routerToUser = streamName.Equals(tableMetadata.TableName) ? null : internalEventRouter; + var audit = AuditEnum.INSERT.GetAudit(statementContext.Annotations) != null; + return new TableOnMergeActionIns(filterEval, insertHelper, routerToUser, statementContext.EpStatementHandle, statementContext.InternalEventEngineRouteDest, audit, tableMetadata.RowFactory); + } + + public IList Matched + { + get { return _matched; } + } + + public IList Unmatched + { + get { return _unmatched; } + } + + public bool IsRequiresWriteLock + { + get { return _requiresWriteLock; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/table/merge/TableOnMergeMatch.cs b/NEsper.Core/NEsper.Core/epl/table/merge/TableOnMergeMatch.cs new file mode 100755 index 000000000..3395e081f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/merge/TableOnMergeMatch.cs @@ -0,0 +1,70 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.epl.table.onaction; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.table.merge +{ + public class TableOnMergeMatch + { + private ExprEvaluator optionalCond; + private IList actions; + + public TableOnMergeMatch(ExprNode optionalCond, IList actions) { + this.optionalCond = optionalCond != null ? optionalCond.ExprEvaluator : null; + this.actions = actions; + } + + public bool IsApplies(EventBean[] eventsPerStream, ExprEvaluatorContext context) { + if (optionalCond == null) { + return true; + } + + object result = optionalCond.Evaluate(new EvaluateParams(eventsPerStream, true, context)); + return result != null && true.Equals(result); + } + + public void Apply(EventBean matchingEvent, + EventBean[] eventsPerStream, + TableStateInstance stateInstance, + TableOnMergeViewChangeHandler changeHandlerAdded, + TableOnMergeViewChangeHandler changeHandlerRemoved, + ExprEvaluatorContext context) { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QInfraMergeWhenThenActions(actions.Count);} + + int count = -1; + foreach (TableOnMergeAction action in actions) { + count++; + + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().QInfraMergeWhenThenActionItem(count, action.Name); + bool applies = action.IsApplies(eventsPerStream, context); + if (applies) { + action.Apply(matchingEvent, eventsPerStream, stateInstance, changeHandlerAdded, changeHandlerRemoved, context); + } + InstrumentationHelper.Get().AInfraMergeWhenThenActionItem(applies); + continue; + } + + if (action.IsApplies(eventsPerStream, context)) { + action.Apply(matchingEvent, eventsPerStream, stateInstance, changeHandlerAdded, changeHandlerRemoved, context); + } + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AInfraMergeWhenThenActions();} + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/table/mgmt/TableAggregationMethodFactory.cs b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableAggregationMethodFactory.cs new file mode 100755 index 000000000..c8cc8676a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableAggregationMethodFactory.cs @@ -0,0 +1,14 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.table.mgmt +{ + interface TableAggregationMethodFactory + { + } +} diff --git a/NEsper.Core/NEsper.Core/epl/table/mgmt/TableColumnDesc.cs b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableColumnDesc.cs new file mode 100755 index 000000000..4f7e0e07f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableColumnDesc.cs @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.table.mgmt +{ + public abstract class TableColumnDesc + { + protected TableColumnDesc(int positionInDeclaration, string columnName) + { + PositionInDeclaration = positionInDeclaration; + ColumnName = columnName; + } + + public string ColumnName { get; private set; } + + public int PositionInDeclaration { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/table/mgmt/TableColumnDescAgg.cs b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableColumnDescAgg.cs new file mode 100755 index 000000000..f5f8ceb5c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableColumnDescAgg.cs @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.baseagg; + +namespace com.espertech.esper.epl.table.mgmt +{ + public class TableColumnDescAgg : TableColumnDesc + { + public TableColumnDescAgg(int positionInDeclaration, string columnName, ExprAggregateNode aggregation, EventType optionalAssociatedType) + : base (positionInDeclaration, columnName) + { + Aggregation = aggregation; + OptionalAssociatedType = optionalAssociatedType; + } + + public ExprAggregateNode Aggregation { get; private set; } + + public EventType OptionalAssociatedType { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/table/mgmt/TableColumnDescTyped.cs b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableColumnDescTyped.cs new file mode 100755 index 000000000..4bc618dfa --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableColumnDescTyped.cs @@ -0,0 +1,24 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.table.mgmt +{ + public class TableColumnDescTyped : TableColumnDesc + { + public TableColumnDescTyped(int positionInDeclaration, string columnName, object unresolvedType, bool key) + : base(positionInDeclaration, columnName) + { + UnresolvedType = unresolvedType; + IsKey = key; + } + + public object UnresolvedType { get; private set; } + + public bool IsKey { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/table/mgmt/TableColumnMethodPair.cs b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableColumnMethodPair.cs new file mode 100755 index 000000000..82a4bfc44 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableColumnMethodPair.cs @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.table.mgmt +{ + public class TableColumnMethodPair + { + public TableColumnMethodPair(ExprEvaluator evaluator, int targetIndex, ExprNode aggregationNode) + { + Evaluator = evaluator; + TargetIndex = targetIndex; + AggregationNode = aggregationNode; + } + + public ExprEvaluator Evaluator { get; private set; } + + public int TargetIndex { get; private set; } + + public ExprNode AggregationNode { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/table/mgmt/TableExprEvaluatorContext.cs b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableExprEvaluatorContext.cs new file mode 100755 index 000000000..116a89161 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableExprEvaluatorContext.cs @@ -0,0 +1,62 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.threading; + +namespace com.espertech.esper.epl.table.mgmt +{ + public class TableExprEvaluatorContext + { + private readonly IThreadLocal> _threadLocal = ThreadLocalManager.Create>( + () => new Dictionary()); + + /// + /// Adds the acquired lock. If the lock does not already belong to the context, then it will be locked and + /// the disposable lock will be returned. + /// + /// The lock. + /// + public IDisposable AddAcquiredLock(ILockable @lock) + { + var table = _threadLocal.GetOrCreate(); + if (table.ContainsKey(@lock)) + return null; + + var latch = @lock.Acquire(); + return table[@lock] = latch; + } + + public void ReleaseAcquiredLocks() + { + var table = _threadLocal.Value; + if (table == null || table.IsEmpty()) { + return; + } + + foreach (var latch in table.Values) + { + latch.Dispose(); + } + + table.Clear(); + } + + public int LockHeldCount + { + get + { + var table = _threadLocal.Value; + return table != null ? table.Count : 0; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/table/mgmt/TableMetadata.cs b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableMetadata.cs new file mode 100755 index 000000000..003ddcb91 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableMetadata.cs @@ -0,0 +1,329 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.service; +using com.espertech.esper.core.service.resource; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.epl.table.upd; +using com.espertech.esper.epl.updatehelper; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.epl.table.mgmt +{ + public class TableMetadata + { + private readonly string _tableName; + private readonly string _eplExpression; + private readonly string _statementName; + private readonly Type[] _keyTypes; + private readonly IDictionary _tableColumns; + private readonly TableStateRowFactory _rowFactory; + private readonly int _numberMethodAggregations; + private readonly StatementContext _statementContextCreateTable; + private readonly ObjectArrayEventType _internalEventType; + private readonly ObjectArrayEventType _publicEventType; + private readonly TableMetadataInternalEventToPublic _eventToPublic; + private readonly bool _queryPlanLogging; + + private readonly IDictionary> _stmtNameToUpdateStrategyReceivers = new Dictionary>(); + private readonly EventTableIndexMetadata _eventTableIndexMetadataRepo = new EventTableIndexMetadata(); + + private TableStateFactory _tableStateFactory; + private TableMetadataContext _tableMetadataContext; + private readonly TableRowKeyFactory _tableRowKeyFactory; + + public TableMetadata( + string tableName, + string eplExpression, + string statementName, + Type[] keyTypes, + IDictionary tableColumns, + TableStateRowFactory rowFactory, + int numberMethodAggregations, + StatementContext createTableStatementContext, + ObjectArrayEventType internalEventType, + ObjectArrayEventType publicEventType, + TableMetadataInternalEventToPublic eventToPublic, + bool queryPlanLogging) + { + _tableName = tableName; + _eplExpression = eplExpression; + _statementName = statementName; + _keyTypes = keyTypes; + _tableColumns = tableColumns; + _rowFactory = rowFactory; + _numberMethodAggregations = numberMethodAggregations; + _statementContextCreateTable = createTableStatementContext; + _internalEventType = internalEventType; + _publicEventType = publicEventType; + _eventToPublic = eventToPublic; + _queryPlanLogging = queryPlanLogging; + + if (keyTypes.Length > 0) + { + var pair = TableServiceUtil.GetIndexMultikeyForKeys(tableColumns, internalEventType); + _eventTableIndexMetadataRepo.AddIndex(true, pair.Second, tableName, createTableStatementContext.StatementName, true, null); + _tableRowKeyFactory = new TableRowKeyFactory(pair.First); + } + } + + public Type[] KeyTypes + { + get { return _keyTypes; } + } + + public TableStateFactory TableStateFactory + { + get { return _tableStateFactory; } + set { _tableStateFactory = value; } + } + + public IDictionary TableColumns + { + get { return _tableColumns; } + } + + public TableStateRowFactory RowFactory + { + get { return _rowFactory; } + } + + public int NumberMethodAggregations + { + get { return _numberMethodAggregations; } + } + + public string ContextName + { + get { return _statementContextCreateTable.ContextName; } + } + + public ObjectArrayEventType InternalEventType + { + get { return _internalEventType; } + } + + public bool IsQueryPlanLogging + { + get { return _queryPlanLogging; } + } + + public ISet UniqueKeyProps + { + get + { + ISet keys = new LinkedHashSet(); + foreach (var entry in _tableColumns) + { + if (entry.Value.IsKey) + { + keys.Add(entry.Key); + } + } + return keys; + } + } + + public string TableName + { + get { return _tableName; } + } + + public EventTableIndexMetadata EventTableIndexMetadataRepo + { + get { return _eventTableIndexMetadataRepo; } + } + + public EventBean GetPublicEventBean(EventBean @event, EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + return _eventToPublic.Convert(@event, new EvaluateParams(eventsPerStream, isNewData, context)); + } + + public EventType PublicEventType + { + get { return _publicEventType; } + } + + public TableMetadataInternalEventToPublic EventToPublic + { + get { return _eventToPublic; } + } + + public void ValidateAddIndexAssignUpdateStrategies( + string createIndexStatementName, + IndexMultiKey imk, + string indexName) + { + // add index - for now + _eventTableIndexMetadataRepo.AddIndex(false, imk, indexName, createIndexStatementName, true, null); + + // validate strategies, rollback if required + foreach (var stmtEntry in _stmtNameToUpdateStrategyReceivers) + { + foreach (var strategyReceiver in stmtEntry.Value) + { + try + { + TableUpdateStrategyFactory.ValidateGetTableUpdateStrategy( + this, strategyReceiver.UpdateHelper, strategyReceiver.IsOnMerge); + } + catch (ExprValidationException ex) + { + _eventTableIndexMetadataRepo.RemoveIndex(imk); + throw new ExprValidationException( + "Failed to validate statement '" + stmtEntry.Key + "' as a recipient of the proposed index: " + + ex.Message); + } + } + } + + // assign new strategies + foreach (var stmtEntry in _stmtNameToUpdateStrategyReceivers) + { + foreach (var strategyReceiver in stmtEntry.Value) + { + var strategy = TableUpdateStrategyFactory.ValidateGetTableUpdateStrategy( + this, strategyReceiver.UpdateHelper, strategyReceiver.IsOnMerge); + strategyReceiver.Receiver.Update(strategy); + } + } + } + + public void AddTableUpdateStrategyReceiver(string statementName, TableUpdateStrategyReceiver receiver, EventBeanUpdateHelper updateHelper, bool onMerge) + { + var receivers = _stmtNameToUpdateStrategyReceivers.Get(statementName); + if (receivers == null) { + receivers = new List(); + _stmtNameToUpdateStrategyReceivers.Put(statementName, receivers); + } + receivers.Add(new TableUpdateStrategyReceiverDesc(receiver, updateHelper, onMerge)); + } + + public void RemoveTableUpdateStrategyReceivers(string statementName) + { + _stmtNameToUpdateStrategyReceivers.Remove(statementName); + } + + public void AddIndexReference(string indexName, string statementName) + { + _eventTableIndexMetadataRepo.AddIndexReference(indexName, statementName); + } + + public void RemoveIndexReferencesStatement(string statementName) + { + var indexesDereferenced = _eventTableIndexMetadataRepo.GetRemoveRefIndexesDereferenced(statementName); + foreach (var indexDereferenced in indexesDereferenced) + { + // remove tables + foreach (int agentInstanceId in AgentInstanceIds) + { + var state = GetState(agentInstanceId); + if (state != null) { + var mk = state.IndexRepository.GetIndexByName(indexDereferenced); + if (mk != null) { + state.IndexRepository.RemoveIndex(mk); + } + } + } + } + } + + public TableStateInstance GetState(int agentInstanceId) + { + var createTableResources = _statementContextCreateTable.StatementExtensionServicesContext.StmtResources; + + StatementResourceHolder holder = null; + if (_statementContextCreateTable.ContextName == null) + { + holder = createTableResources.ResourcesUnpartitioned; + } + else + { + if (createTableResources.ResourcesPartitioned != null) + { + holder = createTableResources.ResourcesPartitioned.Get(agentInstanceId); + } + } + if (holder == null) + { + return null; + } + + var aggsvc = (AggregationServiceTable) holder.AggregationService; + return aggsvc.TableState; + } + + public ICollection AgentInstanceIds + { + get + { + var createTableResources = _statementContextCreateTable.StatementExtensionServicesContext.StmtResources; + + if (_statementContextCreateTable.ContextName == null) + { + return Collections.SingletonList(-1); + } + if (createTableResources.ResourcesPartitioned != null) + { + return createTableResources.ResourcesPartitioned.Keys; + } + return Collections.SingletonList(-1); + } + } + + public string[][] UniqueIndexes + { + get { return _eventTableIndexMetadataRepo.UniqueIndexProps; } + } + + public TableMetadataContext TableMetadataContext + { + set { _tableMetadataContext = value; } + get { return _tableMetadataContext; } + } + + public TableRowKeyFactory TableRowKeyFactory + { + get { return _tableRowKeyFactory; } + } + + public void ClearTableInstances() + { + foreach (int agentInstanceId in AgentInstanceIds) + { + var state = GetState(agentInstanceId); + if (state != null) + { + state.DestroyInstance(); + } + } + } + + public string EplExpression + { + get { return _eplExpression; } + } + + public string StatementName + { + get { return _statementName; } + } + + public StatementContext StatementContextCreateTable + { + get { return _statementContextCreateTable; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/table/mgmt/TableMetadataColumn.cs b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableMetadataColumn.cs new file mode 100755 index 000000000..1d2403761 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableMetadataColumn.cs @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.table.mgmt +{ + public abstract class TableMetadataColumn + { + protected TableMetadataColumn(string columnName, bool key) + { + ColumnName = columnName; + IsKey = key; + } + + public bool IsKey { get; private set; } + + public string ColumnName { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/table/mgmt/TableMetadataColumnAggregation.cs b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableMetadataColumnAggregation.cs new file mode 100755 index 000000000..a355265ed --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableMetadataColumnAggregation.cs @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.rettype; + +namespace com.espertech.esper.epl.table.mgmt +{ + public class TableMetadataColumnAggregation : TableMetadataColumn + { + public TableMetadataColumnAggregation(string columnName, AggregationMethodFactory factory, int methodOffset, AggregationAccessorSlotPair accessAccessorSlotPair, EPType optionalEnumerationType, EventType optionalEventType) + : base(columnName, false) + { + Factory = factory; + MethodOffset = methodOffset; + AccessAccessorSlotPair = accessAccessorSlotPair; + OptionalEnumerationType = optionalEnumerationType; + OptionalEventType = optionalEventType; + } + + public AggregationMethodFactory Factory { get; private set; } + + public int MethodOffset { get; private set; } + + public AggregationAccessorSlotPair AccessAccessorSlotPair { get; private set; } + + public EPType OptionalEnumerationType { get; private set; } + + public EventType OptionalEventType { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/table/mgmt/TableMetadataColumnPairAggAccess.cs b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableMetadataColumnPairAggAccess.cs new file mode 100755 index 000000000..515f245e9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableMetadataColumnPairAggAccess.cs @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.agg.access; + +namespace com.espertech.esper.epl.table.mgmt +{ + public class TableMetadataColumnPairAggAccess : TableMetadataColumnPairBase + { + public TableMetadataColumnPairAggAccess(int dest, AggregationAccessor accessor) + : base(dest) + { + Accessor = accessor; + } + + public AggregationAccessor Accessor { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/table/mgmt/TableMetadataColumnPairAggMethod.cs b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableMetadataColumnPairAggMethod.cs new file mode 100755 index 000000000..fd520dc80 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableMetadataColumnPairAggMethod.cs @@ -0,0 +1,18 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.table.mgmt +{ + public class TableMetadataColumnPairAggMethod : TableMetadataColumnPairBase + { + public TableMetadataColumnPairAggMethod(int dest) + : base(dest) + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/table/mgmt/TableMetadataColumnPairBase.cs b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableMetadataColumnPairBase.cs new file mode 100755 index 000000000..9399a462e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableMetadataColumnPairBase.cs @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.table.mgmt +{ + public abstract class TableMetadataColumnPairBase + { + protected TableMetadataColumnPairBase(int dest) + { + Dest = dest; + } + + public int Dest { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/table/mgmt/TableMetadataColumnPairPlainCol.cs b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableMetadataColumnPairPlainCol.cs new file mode 100755 index 000000000..3da445415 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableMetadataColumnPairPlainCol.cs @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.table.mgmt +{ + public class TableMetadataColumnPairPlainCol : TableMetadataColumnPairBase + { + public TableMetadataColumnPairPlainCol(int dest, int source) : base(dest) + { + Source = source; + } + + public int Source { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/table/mgmt/TableMetadataColumnPlain.cs b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableMetadataColumnPlain.cs new file mode 100755 index 000000000..53a11e7c7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableMetadataColumnPlain.cs @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.table.mgmt +{ + public class TableMetadataColumnPlain : TableMetadataColumn + { + public TableMetadataColumnPlain(string columnName, bool key, int indexPlain) : base(columnName, key) + { + IndexPlain = indexPlain; + } + + public int IndexPlain { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/table/mgmt/TableMetadataContext.cs b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableMetadataContext.cs new file mode 100755 index 000000000..ddf5fa51b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableMetadataContext.cs @@ -0,0 +1,14 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.table.mgmt +{ + public interface TableMetadataContext + { + } +} diff --git a/NEsper.Core/NEsper.Core/epl/table/mgmt/TableMetadataInternalEventToPublic.cs b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableMetadataInternalEventToPublic.cs new file mode 100755 index 000000000..c6a9ab20a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableMetadataInternalEventToPublic.cs @@ -0,0 +1,62 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.table.strategy; +using com.espertech.esper.events; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.epl.table.mgmt +{ + public class TableMetadataInternalEventToPublic + { + private readonly ObjectArrayEventType _publicEventType; + private readonly TableMetadataColumnPairPlainCol[] _plains; + private readonly TableMetadataColumnPairAggMethod[] _methods; + private readonly TableMetadataColumnPairAggAccess[] _accessors; + private readonly EventAdapterService _eventAdapterService; + private readonly int _numColumns; + + public TableMetadataInternalEventToPublic(ObjectArrayEventType publicEventType, TableMetadataColumnPairPlainCol[] plains, TableMetadataColumnPairAggMethod[] methods, TableMetadataColumnPairAggAccess[] accessors, EventAdapterService eventAdapterService) + { + _publicEventType = publicEventType; + _plains = plains; + _methods = methods; + _accessors = accessors; + _eventAdapterService = eventAdapterService; + _numColumns = publicEventType.PropertyDescriptors.Count; + } + + public EventBean Convert(EventBean @event, EvaluateParams evalParams) + { + var data = ConvertToUnd(@event, evalParams); + return _eventAdapterService.AdapterForType(data, _publicEventType); + } + + public object[] ConvertToUnd(EventBean @event, EvaluateParams evalParams) + { + var bean = (ObjectArrayBackedEventBean) @event; + var row = ExprTableEvalStrategyUtil.GetRow(bean); + var data = new object[_numColumns]; + foreach (var plain in _plains) { + data[plain.Dest] = bean.Properties[plain.Source]; + } + var count = 0; + foreach (var access in _accessors) + { + data[access.Dest] = access.Accessor.GetValue(row.States[count++], evalParams); + } + count = 0; + foreach (var method in _methods) { + data[method.Dest] = row.Methods[count++].Value; + } + return data; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/table/mgmt/TableRowKeyFactory.cs b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableRowKeyFactory.cs new file mode 100755 index 000000000..0984dd88c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableRowKeyFactory.cs @@ -0,0 +1,42 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.collection; + +namespace com.espertech.esper.epl.table.mgmt +{ + public class TableRowKeyFactory + { + private readonly int[] _keyColIndexes; + + public TableRowKeyFactory(int[] keyColIndexes) + { + if (keyColIndexes.Length == 0) + { + throw new ArgumentException("No key indexed provided"); + } + _keyColIndexes = keyColIndexes; + } + + public object GetTableRowKey(object[] data) + { + if (_keyColIndexes.Length == 1) + { + return data[_keyColIndexes[0]]; + } + var key = new object[_keyColIndexes.Length]; + for (var i = 0; i < _keyColIndexes.Length; i++) + { + key[i] = data[_keyColIndexes[i]]; + } + return new MultiKeyUntyped(key); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/table/mgmt/TableService.cs b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableService.cs new file mode 100755 index 000000000..affbebd58 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableService.cs @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.table; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.epl.table.strategy; +using com.espertech.esper.epl.table.upd; +using com.espertech.esper.epl.updatehelper; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.epl.table.mgmt +{ + public interface TableService + { + string[] Tables { get; } + TableExprEvaluatorContext TableExprEvaluatorContext { get; } + TableMetadata GetTableMetadata(string tableName); + TableStateInstance GetState(string name, int agentInstanceId); + void RemoveTableIfFound(string tableName); + ExprTableIdentNode GetTableIdentNode(StreamTypeService streamTypeService, string unresolvedPropertyName, string streamOrPropertyName) ; + TableMetadata GetTableMetadataFromEventType(EventType type); + Pair> GetTableNodeChainable(StreamTypeService streamTypeService, IList chainSpec, EngineImportService engineImportService) ; + ICollection GetAgentInstanceIds(string tableName); + TableUpdateStrategy GetTableUpdateStrategy(TableMetadata tableMetadata, EventBeanUpdateHelper updateHelper, bool isOnMerge) ; + void AddTableUpdateStrategyReceiver(TableMetadata tableMetadata, string statementName, TableUpdateStrategyReceiver receiver, EventBeanUpdateHelper updateHelper, bool isOnMerge); + void RemoveTableUpdateStrategyReceivers(TableMetadata tableMetadata, string statementName); + void ValidateAddIndex(string createIndexStatementName, TableMetadata tableMetadata, string indexName, IndexMultiKey imk) ; + void RemoveIndexReferencesStmtMayRemoveIndex(string statementName, TableMetadata tableMetadata); + TableMetadata AddTable(string tableName, string eplExpression, string statementName, Type[] keyTypes, IDictionary tableColumns, TableStateRowFactory tableStateRowFactory, int numberMethodAggregations, StatementContext statementContext, ObjectArrayEventType internalEventType, ObjectArrayEventType publicEventType, TableMetadataInternalEventToPublic eventToPublic, bool queryPlanLogging) ; + TableAndLockProvider GetStateProvider(String tableName, int agentInstanceId, bool writesToTables); + } + + public class TableServiceConstants + { + public const string INTERNAL_RESERVED_PROPERTY = "internal-reserved"; + } +} diff --git a/NEsper.Core/NEsper.Core/epl/table/mgmt/TableServiceImpl.cs b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableServiceImpl.cs new file mode 100755 index 000000000..48457fe9b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableServiceImpl.cs @@ -0,0 +1,355 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.compat.threading; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.table; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.epl.parse; +using com.espertech.esper.epl.table.strategy; +using com.espertech.esper.epl.table.upd; +using com.espertech.esper.epl.updatehelper; +using com.espertech.esper.events.arr; +using com.espertech.esper.plugin; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.table.mgmt +{ + public class TableServiceImpl : TableService + { + internal static readonly ILog QueryPlanLog = LogManager.GetLogger(AuditPath.QUERYPLAN_LOG); + + private readonly IDictionary _tables = + new Dictionary().WithNullSupport(); + private readonly TableExprEvaluatorContext _tableExprEvaluatorContext = + new TableExprEvaluatorContext(); + + public TableServiceImpl() + { + } + + public void ValidateAddIndex(string createIndexStatementName, TableMetadata tableMetadata, string indexName, IndexMultiKey imk) + { + tableMetadata.ValidateAddIndexAssignUpdateStrategies(createIndexStatementName, imk, indexName); + } + + public TableUpdateStrategy GetTableUpdateStrategy(TableMetadata tableMetadata, EventBeanUpdateHelper updateHelper, bool isOnMerge) + { + return TableUpdateStrategyFactory.ValidateGetTableUpdateStrategy(tableMetadata, updateHelper, isOnMerge); + } + + public ICollection GetAgentInstanceIds(string name) + { + var metadata = _tables.Get(name); + if (metadata == null) + { + throw new ArgumentException("Failed to find table for name '" + name + "'"); + } + return metadata.AgentInstanceIds; + } + + public TableExprEvaluatorContext TableExprEvaluatorContext + { + get { return _tableExprEvaluatorContext; } + } + + public TableMetadata GetTableMetadata(string tableName) + { + return _tables.Get(tableName); + } + + public TableMetadata AddTable( + string tableName, + string eplExpression, + string statementName, + Type[] keyTypes, + IDictionary tableColumns, + TableStateRowFactory tableStateRowFactory, + int numberMethodAggregations, + StatementContext statementContext, + ObjectArrayEventType internalEventType, + ObjectArrayEventType publicEventType, + TableMetadataInternalEventToPublic eventToPublic, + bool queryPlanLogging) + { + var metadata = new TableMetadata( + tableName, + eplExpression, + statementName, + keyTypes, + tableColumns, + tableStateRowFactory, + numberMethodAggregations, + statementContext, + internalEventType, + publicEventType, + eventToPublic, + queryPlanLogging); + + // determine table state factory + TableStateFactory tableStateFactory; + if (keyTypes.Length == 0) + { // ungrouped + tableStateFactory = new ProxyTableStateFactory + { + ProcMakeTableState = agentInstanceContext => new TableStateInstanceUngroupedImpl(metadata, agentInstanceContext), + }; + } + else + { + tableStateFactory = new ProxyTableStateFactory + { + ProcMakeTableState = agentInstanceContext => new TableStateInstanceGroupedImpl(metadata, agentInstanceContext), + }; + } + metadata.TableStateFactory = tableStateFactory; + + _tables.Put(tableName, metadata); + return metadata; + } + + public void RemoveTableIfFound(string tableName) + { + var metadata = _tables.Delete(tableName); + if (metadata != null) + { + metadata.ClearTableInstances(); + } + } + + public TableStateInstance GetState(string name, int agentInstanceId) + { + return AssertGetState(name, agentInstanceId); + } + + private TableStateInstance AssertGetState(string name, int agentInstanceId) + { + var metadata = _tables.Get(name); + if (metadata == null) + { + throw new ArgumentException("Failed to find table for name '" + name + "'"); + } + return metadata.GetState(agentInstanceId); + } + + public TableMetadata GetTableMetadataFromEventType(EventType type) + { + var tableName = TableServiceUtil.GetTableNameFromEventType(type); + if (tableName == null) + { + return null; + } + return _tables.Get(tableName); + } + + public Pair> GetTableNodeChainable( + StreamTypeService streamTypeService, + IList chainSpec, + EngineImportService engineImportService) + { + chainSpec = new List(chainSpec); + + var unresolvedPropertyName = chainSpec[0].Name; + var col = FindTableColumnMayByPrefixed(streamTypeService, unresolvedPropertyName); + if (col == null) + { + return null; + } + var pair = col.Pair; + if (pair.Column is TableMetadataColumnAggregation) + { + var agg = (TableMetadataColumnAggregation)pair.Column; + + if (chainSpec.Count > 1) + { + var candidateAccessor = chainSpec[1].Name; + var exprNode = (ExprAggregateNodeBase)ASTAggregationHelper.TryResolveAsAggregation(engineImportService, false, candidateAccessor, new LazyAllocatedMap(), streamTypeService.EngineURIQualifier); + if (exprNode != null) + { + var identNode = new ExprTableIdentNodeSubpropAccessor(pair.StreamNum, col.OptionalStreamName, agg, exprNode); + exprNode.AddChildNodes(chainSpec[1].Parameters); + chainSpec.RemoveAt(0); + chainSpec.RemoveAt(0); + return new Pair>(identNode, chainSpec); + } + } + + var node = new ExprTableIdentNode(null, unresolvedPropertyName); + var eval = ExprTableEvalStrategyFactory.GetTableAccessEvalStrategy(node, pair.TableMetadata.TableName, pair.StreamNum, agg); + node.Eval = eval; + chainSpec.RemoveAt(0); + return new Pair>(node, chainSpec); + } + return null; + } + + public ExprTableIdentNode GetTableIdentNode(StreamTypeService streamTypeService, string unresolvedPropertyName, string streamOrPropertyName) + { + var propertyPrefixed = unresolvedPropertyName; + if (streamOrPropertyName != null) + { + propertyPrefixed = streamOrPropertyName + "." + unresolvedPropertyName; + } + var col = FindTableColumnMayByPrefixed(streamTypeService, propertyPrefixed); + if (col == null) + { + return null; + } + var pair = col.Pair; + if (pair.Column is TableMetadataColumnAggregation) + { + var agg = (TableMetadataColumnAggregation)pair.Column; + var node = new ExprTableIdentNode(streamOrPropertyName, unresolvedPropertyName); + var eval = ExprTableEvalStrategyFactory.GetTableAccessEvalStrategy(node, pair.TableMetadata.TableName, pair.StreamNum, agg); + node.Eval = eval; + return node; + } + return null; + } + + public void AddTableUpdateStrategyReceiver(TableMetadata tableMetadata, string statementName, TableUpdateStrategyReceiver receiver, EventBeanUpdateHelper updateHelper, bool isOnMerge) + { + tableMetadata.AddTableUpdateStrategyReceiver(statementName, receiver, updateHelper, isOnMerge); + } + + public void RemoveTableUpdateStrategyReceivers(TableMetadata tableMetadata, string statementName) + { + tableMetadata.RemoveTableUpdateStrategyReceivers(statementName); + } + + public string[] Tables + { + get { return CollectionUtil.ToArray(_tables.Keys); } + } + + public TableAndLockProvider GetStateProvider(String tableName, int agentInstanceId, bool writesToTables) + { + TableStateInstance instance = AssertGetState(tableName, agentInstanceId); + ILockable @lock = writesToTables ? instance.TableLevelRWLock.WriteLock : instance.TableLevelRWLock.ReadLock; + if (instance is TableStateInstanceGrouped) + { + return new TableAndLockProviderGroupedImpl(new TableAndLockGrouped(@lock, (TableStateInstanceGrouped)instance)); + } + else + { + return new TableAndLockProviderUngroupedImpl(new TableAndLockUngrouped(@lock, (TableStateInstanceUngrouped)instance)); + } + } + + private StreamTableColWStreamName FindTableColumnMayByPrefixed(StreamTypeService streamTypeService, string streamAndPropName) + { + var indexDot = streamAndPropName.IndexOf('.'); + if (indexDot == -1) + { + var pair = FindTableColumnAcrossStreams(streamTypeService, streamAndPropName); + if (pair != null) + { + return new StreamTableColWStreamName(pair, null); + } + } + else + { + var streamName = streamAndPropName.Substring(0, indexDot); + var colName = streamAndPropName.Substring(indexDot + 1); + var streamNum = streamTypeService.GetStreamNumForStreamName(streamName); + if (streamNum == -1) + { + return null; + } + var pair = FindTableColumnForType(streamNum, streamTypeService.EventTypes[streamNum], colName); + if (pair != null) + { + return new StreamTableColWStreamName(pair, streamName); + } + } + return null; + } + + public void RemoveIndexReferencesStmtMayRemoveIndex(string statementName, TableMetadata tableMetadata) + { + tableMetadata.RemoveIndexReferencesStatement(statementName); + } + + private StreamTableColPair FindTableColumnAcrossStreams(StreamTypeService streamTypeService, string columnName) + { + StreamTableColPair found = null; + for (var i = 0; i < streamTypeService.EventTypes.Length; i++) + { + var type = streamTypeService.EventTypes[i]; + var pair = FindTableColumnForType(i, type, columnName); + if (pair == null) + { + continue; + } + if (found != null) + { + if (streamTypeService.IsStreamZeroUnambigous && found.StreamNum == 0) + { + continue; + } + throw new ExprValidationException("Ambiguous table column '" + columnName + "' should be prefixed by a stream name"); + } + found = pair; + } + return found; + } + + private StreamTableColPair FindTableColumnForType(int streamNum, EventType type, string columnName) + { + var tableMetadata = GetTableMetadataFromEventType(type); + if (tableMetadata != null) + { + var column = tableMetadata.TableColumns.Get(columnName); + if (column != null) + { + return new StreamTableColPair(streamNum, column, tableMetadata); + } + } + return null; + } + + internal class StreamTableColPair + { + internal StreamTableColPair(int streamNum, TableMetadataColumn column, TableMetadata tableMetadata) + { + StreamNum = streamNum; + Column = column; + TableMetadata = tableMetadata; + } + + public int StreamNum { get; private set; } + + public TableMetadataColumn Column { get; private set; } + + public TableMetadata TableMetadata { get; private set; } + } + + internal class StreamTableColWStreamName + { + internal StreamTableColWStreamName(StreamTableColPair pair, string optionalStreamName) + { + Pair = pair; + OptionalStreamName = optionalStreamName; + } + + public StreamTableColPair Pair { get; private set; } + + public string OptionalStreamName { get; private set; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/table/mgmt/TableServiceUtil.cs b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableServiceUtil.cs new file mode 100755 index 000000000..1f18c767f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableServiceUtil.cs @@ -0,0 +1,66 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.events; +using com.espertech.esper.events.arr; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.table.mgmt +{ + public class TableServiceUtil + { + public static string GetTableNameFromEventType(EventType type) + { + if (!(type is EventTypeSPI)) + { + return null; + } + var spi = (EventTypeSPI) type; + if (spi.Metadata.TypeClass == TypeClass.TABLE) + { + return spi.Metadata.PrimaryName; + } + return null; + } + + public static StreamTypeServiceImpl StreamTypeFromTableColumn(TableMetadataColumnAggregation column, string engineURI) + { + if (column.OptionalEventType == null) + { + throw new ArgumentException("Required event type not provided"); + } + return new StreamTypeServiceImpl(column.OptionalEventType, column.OptionalEventType.Name, false, engineURI); + } + + public static Pair GetIndexMultikeyForKeys(IDictionary items, ObjectArrayEventType eventType) + { + IList indexFields = new List(); + IList keyIndexes = new List(); + var count = 0; + foreach (var entry in items) + { + if (entry.Value.IsKey) + { + indexFields.Add(new IndexedPropDesc(entry.Key, eventType.GetPropertyType(entry.Key))); + keyIndexes.Add(count+1); + } + count++; + } + var keyColIndexes = CollectionUtil.IntArray(keyIndexes); + return new Pair(keyColIndexes, new IndexMultiKey(true, indexFields, Collections.GetEmptyList())); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/table/mgmt/TableStateFactory.cs b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableStateFactory.cs new file mode 100755 index 000000000..a6931e5b5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableStateFactory.cs @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.core.context.util; + +namespace com.espertech.esper.epl.table.mgmt +{ + public interface TableStateFactory + { + TableStateInstance MakeTableState(AgentInstanceContext agentInstanceContext); + } + + public class ProxyTableStateFactory : TableStateFactory + { + public Func ProcMakeTableState { get; set; } + + /// + /// Initializes a new instance of the class. + /// + /// State of the proc make table. + public ProxyTableStateFactory(Func procMakeTableState) + { + ProcMakeTableState = procMakeTableState; + } + + /// + /// Initializes a new instance of the class. + /// + public ProxyTableStateFactory() + { + } + + public TableStateInstance MakeTableState(AgentInstanceContext agentInstanceContext) + { + return ProcMakeTableState(agentInstanceContext); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/table/mgmt/TableStateInstance.cs b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableStateInstance.cs new file mode 100755 index 000000000..143844561 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableStateInstance.cs @@ -0,0 +1,108 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.threading; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.join.table; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.epl.spec; +using com.espertech.esper.events; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.table.mgmt +{ + public abstract class TableStateInstance + { + protected readonly TableMetadata _tableMetadata; + private readonly AgentInstanceContext _agentInstanceContext; + + private readonly IReaderWriterLock _tableLevelRWLock = ReaderWriterLockManager.CreateLock(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + protected readonly EventTableIndexRepository _indexRepository = new EventTableIndexRepository(); + + public abstract IEnumerable IterableTableScan { get; } + public abstract void AddEvent(EventBean theEvent); + public abstract void DeleteEvent(EventBean matchingEvent); + public abstract void ClearInstance(); + public abstract void DestroyInstance(); + public abstract void AddExplicitIndex(CreateIndexDesc spec, bool isRecoveringResilient, bool allowIndexExists) ; + public abstract string[] SecondaryIndexes { get; } + public abstract EventTable GetIndex(string indexName); + public abstract ObjectArrayBackedEventBean GetCreateRowIntoTable(object groupByKey, ExprEvaluatorContext exprEvaluatorContext); + public abstract ICollection EventCollection { get; } + public abstract int RowCount { get; } + public abstract AggregationServicePassThru AggregationServicePassThru { get; } + + public void HandleRowUpdated(ObjectArrayBackedEventBean row) + { + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().QaTableUpdatedEvent(row); + } + } + + public void AddEventUnadorned(EventBean @event) + { + ObjectArrayBackedEventBean oa = (ObjectArrayBackedEventBean) @event; + AggregationRowPair aggs = _tableMetadata.RowFactory.MakeAggs(_agentInstanceContext.AgentInstanceId, null, null, AggregationServicePassThru); + oa.Properties[0] = aggs; + AddEvent(oa); + } + + protected TableStateInstance(TableMetadata tableMetadata, AgentInstanceContext agentInstanceContext) + { + this._tableMetadata = tableMetadata; + this._agentInstanceContext = agentInstanceContext; + } + + public virtual TableMetadata TableMetadata + { + get { return _tableMetadata; } + } + + public virtual AgentInstanceContext AgentInstanceContext + { + get { return _agentInstanceContext; } + } + + public virtual IReaderWriterLock TableLevelRWLock + { + get { return _tableLevelRWLock; } + } + + public virtual EventTableIndexRepository IndexRepository + { + get { return _indexRepository; } + } + + public virtual void HandleRowUpdateKeyBeforeUpdate(ObjectArrayBackedEventBean updatedEvent) + { + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().QaTableUpdatedEventWKeyBefore(updatedEvent); + } + // no action + } + + public virtual void HandleRowUpdateKeyAfterUpdate(ObjectArrayBackedEventBean updatedEvent) + { + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().QaTableUpdatedEventWKeyAfter(updatedEvent); + } + // no action + } + + public virtual void RemoveExplicitIndex(string indexName) + { + _indexRepository.RemoveExplicitIndex(indexName); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/table/mgmt/TableStateInstanceGrouped.cs b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableStateInstanceGrouped.cs new file mode 100755 index 000000000..6443f7080 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableStateInstanceGrouped.cs @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.compat.threading; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.table.mgmt +{ + public interface TableStateInstanceGrouped + { + IReaderWriterLock TableLevelRWLock { get; } + ObjectArrayBackedEventBean GetCreateRowIntoTable(object groupByKey, ExprEvaluatorContext exprEvaluatorContext); + void HandleRowUpdated(ObjectArrayBackedEventBean row); + ObjectArrayBackedEventBean GetRowForGroupKey(object groupKey); + ICollection GroupKeys { get; } + void Clear(); + EventTableIndexRepository IndexRepository { get; } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/table/mgmt/TableStateInstanceGroupedImpl.cs b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableStateInstanceGroupedImpl.cs new file mode 100755 index 000000000..9c52c04cf --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableStateInstanceGroupedImpl.cs @@ -0,0 +1,208 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.join.table; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.epl.spec; +using com.espertech.esper.events; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.table.mgmt +{ + public class TableStateInstanceGroupedImpl + : TableStateInstance + , TableStateInstanceGrouped + { + private readonly IDictionary _rows = + new Dictionary().WithNullSupport(); + private readonly IndexMultiKey _primaryIndexKey; + + public TableStateInstanceGroupedImpl(TableMetadata tableMetadata, AgentInstanceContext agentInstanceContext) + : base(tableMetadata, agentInstanceContext) + { + IList indexGetters = new List(); + IList keyNames = new List(); + foreach (var entry in tableMetadata.TableColumns) { + if (entry.Value.IsKey) { + keyNames.Add(entry.Key); + indexGetters.Add(tableMetadata.InternalEventType.GetGetter(entry.Key)); + } + } + + var tableName = "primary-" + tableMetadata.TableName; + var organization = new EventTableOrganization(tableName, true, false, 0, Collections.GetEmptyList(), EventTableOrganizationType.HASH); + + EventTable table; + if (indexGetters.Count == 1) + { + var tableMap = new TransformDictionary( + _rows, k => k, v => v, k => k, v => v as ObjectArrayBackedEventBean); + table = new PropertyIndexedEventTableSingleUnique(indexGetters[0], organization, tableMap); + } + else + { + var getters = indexGetters.ToArrayOrNull(); + var tableMap = new TransformDictionary( + _rows, k => k as MultiKeyUntyped, v => v, k => k, v => v as ObjectArrayBackedEventBean); + table = new PropertyIndexedEventTableUnique(getters, organization, tableMap); + } + + var pair = TableServiceUtil.GetIndexMultikeyForKeys(tableMetadata.TableColumns, tableMetadata.InternalEventType); + _primaryIndexKey = pair.Second; + _indexRepository.AddIndex(_primaryIndexKey, new EventTableIndexRepositoryEntry(tableName, table)); + } + + public override EventTable GetIndex(string indexName) + { + if (indexName == _tableMetadata.TableName) { + return _indexRepository.GetIndexByDesc(_primaryIndexKey); + } + return _indexRepository.GetExplicitIndexByName(indexName); + } + + public IDictionary Rows + { + get { return _rows; } + } + + public override void AddEvent(EventBean theEvent) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QTableAddEvent(theEvent); } + try + { + foreach (var table in _indexRepository.GetTables()) + { + table.Add(theEvent); + } + } + catch (EPException) + { + foreach (var table in _indexRepository.GetTables()) + { + table.Remove(theEvent); + } + throw; + } + finally + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().ATableAddEvent(); } + } + } + + public override void DeleteEvent(EventBean matchingEvent) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QTableDeleteEvent(matchingEvent); } + foreach (var table in _indexRepository.GetTables()) { + table.Remove(matchingEvent); + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().ATableDeleteEvent(); } + } + + public override IEnumerable IterableTableScan + { + get { return new PrimaryIndexIterable(_rows); } + } + + public override void AddExplicitIndex(CreateIndexDesc spec, bool isRecoveringResilient, bool allowIndexExists) + { + _indexRepository.ValidateAddExplicitIndex(spec.IsUnique, spec.IndexName, spec.Columns, _tableMetadata.InternalEventType, new PrimaryIndexIterable(_rows), AgentInstanceContext, isRecoveringResilient || allowIndexExists, null); + } + + public override string[] SecondaryIndexes + { + get { return _indexRepository.GetExplicitIndexNames(); } + } + + public override EventTableIndexRepository IndexRepository + { + get { return _indexRepository; } + } + + public override ICollection EventCollection + { + get { return _rows.Values.Unwrap(); } + } + + public ObjectArrayBackedEventBean GetRowForGroupKey(object groupKey) + { + return _rows.Get(groupKey); + } + + public ICollection GroupKeys + { + get { return _rows.Keys; } + } + + public void Clear() + { + ClearInstance(); + } + + public override void ClearInstance() + { + _rows.Clear(); + foreach (EventTable table in _indexRepository.GetTables()) { + table.Destroy(); + } + } + + public override void DestroyInstance() { + ClearInstance(); + } + + public override ObjectArrayBackedEventBean GetCreateRowIntoTable(object groupByKey, ExprEvaluatorContext exprEvaluatorContext) + { + var bean = _rows.Get(groupByKey); + if (bean != null) { + return bean; + } + var row = _tableMetadata.RowFactory.MakeOA(exprEvaluatorContext.AgentInstanceId, groupByKey, null, AggregationServicePassThru); + AddEvent(row); + return row; + } + + public override int RowCount + { + get { return _rows.Count; } + } + + public override AggregationServicePassThru AggregationServicePassThru + { + get { return null; } + } + + internal class PrimaryIndexIterable : IEnumerable + { + internal readonly IDictionary rows; + + internal PrimaryIndexIterable(IDictionary rows) + { + this.rows = rows; + } + + public IEnumerator GetEnumerator() + { + return rows.Values.UnwrapEnumerable().GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/table/mgmt/TableStateInstanceUngrouped.cs b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableStateInstanceUngrouped.cs new file mode 100755 index 000000000..de2e87ae5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableStateInstanceUngrouped.cs @@ -0,0 +1,22 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.compat.threading; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.table.mgmt +{ + public interface TableStateInstanceUngrouped + { + IReaderWriterLock TableLevelRWLock { get; } + ObjectArrayBackedEventBean GetCreateRowIntoTable(object groupByKey, ExprEvaluatorContext exprEvaluatorContext); + ObjectArrayBackedEventBean EventUngrouped { get; } + void HandleRowUpdated(ObjectArrayBackedEventBean row); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/table/mgmt/TableStateInstanceUngroupedImpl.cs b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableStateInstanceUngroupedImpl.cs new file mode 100755 index 000000000..486dfcd5f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableStateInstanceUngroupedImpl.cs @@ -0,0 +1,156 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.join.table; +using com.espertech.esper.epl.spec; +using com.espertech.esper.events; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.table.mgmt +{ + public class TableStateInstanceUngroupedImpl + : TableStateInstance + , TableStateInstanceUngrouped + , IEnumerable + { + private readonly Atomic _eventReference; + + public TableStateInstanceUngroupedImpl(TableMetadata tableMetadata, AgentInstanceContext agentInstanceContext) + : base(tableMetadata, agentInstanceContext) + { + _eventReference = new Atomic(null); + } + + public override IEnumerable IterableTableScan + { + get + { + EventBean value; + if (_eventReference != null && ((value = _eventReference.Get()) != null)) + yield return value; + } + } + + public override void AddEvent(EventBean theEvent) + { + if (_eventReference.Get() != null) { + throw new EPException("Unique index violation, table '" + TableMetadata.TableName + "' " + + "is a declared to hold a single un-keyed row"); + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QTableAddEvent(theEvent); } + _eventReference.Set((ObjectArrayBackedEventBean) theEvent); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().ATableAddEvent(); } + } + + public override void DeleteEvent(EventBean matchingEvent) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QTableDeleteEvent(matchingEvent); } + _eventReference.Set(null); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().ATableDeleteEvent(); } + } + + public Atomic EventReference + { + get { return _eventReference; } + } + + public ObjectArrayBackedEventBean EventUngrouped + { + get { return _eventReference.Get(); } + } + + public override void AddExplicitIndex(CreateIndexDesc spec, bool isRecoveringResilient, bool allowIndexExists) + { + throw new ExprValidationException("Tables without primary key column(s) do not allow creating an index"); + } + + public override EventTable GetIndex(string indexName) + { + var tableMetadata = TableMetadata; + if (indexName == tableMetadata.TableName) + { + var org = new EventTableOrganization(tableMetadata.TableName, true, false, 0, new string[0], EventTableOrganizationType.UNORGANIZED); + return new SingleReferenceEventTable(org, _eventReference); + } + throw new IllegalStateException("Invalid index requested '" + indexName + "'"); + } + + public override string[] SecondaryIndexes + { + get { return new string[0]; } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public IEnumerator GetEnumerator() + { + yield return _eventReference.Get(); + } + + public override void ClearInstance() + { + ClearEvents(); + } + + public override void DestroyInstance() + { + ClearEvents(); + } + + public override ICollection EventCollection + { + get + { + var theEvent = _eventReference.Get(); + if (theEvent == null) + { + return Collections.GetEmptyList(); + } + return Collections.SingletonList(theEvent); + } + } + + public override int RowCount + { + get { return _eventReference.Get() == null ? 0 : 1; } + } + + public override ObjectArrayBackedEventBean GetCreateRowIntoTable(object groupByKey, ExprEvaluatorContext exprEvaluatorContext) + { + var bean = _eventReference.Get(); + if (bean != null) { + return bean; + } + var row = TableMetadata.RowFactory.MakeOA(exprEvaluatorContext.AgentInstanceId, groupByKey, null, AggregationServicePassThru); + AddEvent(row); + return row; + } + + public override AggregationServicePassThru AggregationServicePassThru + { + get { return null; } + } + + public void ClearEvents() + { + _eventReference.Set(null); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/table/mgmt/TableStateRowFactory.cs b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableStateRowFactory.cs new file mode 100755 index 000000000..7827d752d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableStateRowFactory.cs @@ -0,0 +1,66 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.collection; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.aggregator; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.events; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.epl.table.mgmt +{ + public class TableStateRowFactory + { + private readonly ObjectArrayEventType _objectArrayEventType; + private readonly EngineImportService _engineImportService; + private readonly AggregationMethodFactory[] _methodFactories; + private readonly AggregationStateFactory[] _stateFactories; + private readonly int[] _groupKeyIndexes; + private readonly EventAdapterService _eventAdapterService; + + public TableStateRowFactory(ObjectArrayEventType objectArrayEventType, EngineImportService engineImportService, AggregationMethodFactory[] methodFactories, AggregationStateFactory[] stateFactories, int[] groupKeyIndexes, EventAdapterService eventAdapterService) + { + _objectArrayEventType = objectArrayEventType; + _engineImportService = engineImportService; + _methodFactories = methodFactories; + _stateFactories = stateFactories; + _groupKeyIndexes = groupKeyIndexes; + _eventAdapterService = eventAdapterService; + } + + public ObjectArrayBackedEventBean MakeOA(int agentInstanceId, object groupByKey, object groupKeyBinding, AggregationServicePassThru passThru) + { + var row = MakeAggs(agentInstanceId, groupByKey, groupKeyBinding, passThru); + var data = new object[_objectArrayEventType.PropertyDescriptors.Count]; + data[0] = row; + + if (_groupKeyIndexes.Length == 1) { + data[_groupKeyIndexes[0]] = groupByKey; + } + else { + if (_groupKeyIndexes.Length > 1) { + object[] keys = ((MultiKeyUntyped) groupByKey).Keys; + for (int i = 0; i < _groupKeyIndexes.Length; i++) { + data[_groupKeyIndexes[i]] = keys[i]; + } + } + } + + return (ObjectArrayBackedEventBean) _eventAdapterService.AdapterForType(data, _objectArrayEventType); + } + + public AggregationRowPair MakeAggs(int agentInstanceId, object groupByKey, object groupKeyBinding, AggregationServicePassThru passThru) + { + AggregationMethod[] methods = AggSvcGroupByUtil.NewAggregators(_methodFactories); + AggregationState[] states = AggSvcGroupByUtil.NewAccesses(agentInstanceId, false, _stateFactories, groupByKey, passThru); + return new AggregationRowPair(methods, states); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/table/mgmt/TableStateViewableInternal.cs b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableStateViewableInternal.cs new file mode 100755 index 000000000..0a72e2c55 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableStateViewableInternal.cs @@ -0,0 +1,52 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.view; + +namespace com.espertech.esper.epl.table.mgmt +{ + public class TableStateViewableInternal : ViewSupport + { + private readonly TableMetadata _tableMetadata; + private readonly TableStateInstance _tableStateInstance; + private readonly ExprEvaluator[] _optionalTableFilters; + + public TableStateViewableInternal(TableMetadata tableMetadata, TableStateInstance tableStateInstance, ExprEvaluator[] optionalTableFilters) + { + _tableMetadata = tableMetadata; + _tableStateInstance = tableStateInstance; + _optionalTableFilters = optionalTableFilters; + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + // no action required + } + + public override EventType EventType + { + get { return _tableMetadata.InternalEventType; } + } + + public override IEnumerator GetEnumerator() { + if (_optionalTableFilters != null) + { + return FilteredEventEnumerator.Enumerate( + _optionalTableFilters, + _tableStateInstance.EventCollection, + _tableStateInstance.AgentInstanceContext); + } + return _tableStateInstance.EventCollection.GetEnumerator(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/table/mgmt/TableStateViewablePublic.cs b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableStateViewablePublic.cs new file mode 100755 index 000000000..dc24a5df9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableStateViewablePublic.cs @@ -0,0 +1,83 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.view; + +namespace com.espertech.esper.epl.table.mgmt +{ + public class TableStateViewablePublic : ViewSupport { + + private readonly TableMetadata _tableMetadata; + private readonly TableStateInstance _tableStateInstance; + + public TableStateViewablePublic(TableMetadata tableMetadata, TableStateInstance tableStateInstance) + { + _tableMetadata = tableMetadata; + _tableStateInstance = tableStateInstance; + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + // no action required + } + + public override EventType EventType + { + get { return _tableMetadata.PublicEventType; } + } + + public override IEnumerator GetEnumerator() { + return new TableToPublicEnumerator(_tableStateInstance); + } + + internal class TableToPublicEnumerator : IEnumerator + { + private readonly TableMetadataInternalEventToPublic _eventToPublic; + private readonly IEnumerator _enumerator; + private readonly TableStateInstance _tableStateInstance; + + internal TableToPublicEnumerator(TableStateInstance tableStateInstance) { + _eventToPublic = tableStateInstance.TableMetadata.EventToPublic; + _enumerator = tableStateInstance.EventCollection.GetEnumerator(); + _tableStateInstance = tableStateInstance; + } + + public bool MoveNext() { + return _enumerator.MoveNext(); + } + + object IEnumerator.Current + { + get { return Current; } + } + + public EventBean Current + { + get + { + return _eventToPublic.Convert(_enumerator.Current, new EvaluateParams(null, true, _tableStateInstance.AgentInstanceContext)); + } + } + + public void Dispose() + { + } + + public void Reset() + { + throw new NotSupportedException(); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/table/mgmt/TableUpdateStrategyReceiverDesc.cs b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableUpdateStrategyReceiverDesc.cs new file mode 100755 index 000000000..8035e0d23 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/mgmt/TableUpdateStrategyReceiverDesc.cs @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.table.upd; +using com.espertech.esper.epl.updatehelper; + +namespace com.espertech.esper.epl.table.mgmt +{ + public class TableUpdateStrategyReceiverDesc + { + public TableUpdateStrategyReceiverDesc(TableUpdateStrategyReceiver receiver, EventBeanUpdateHelper updateHelper, bool onMerge) + { + Receiver = receiver; + UpdateHelper = updateHelper; + IsOnMerge = onMerge; + } + + public TableUpdateStrategyReceiver Receiver { get; private set; } + + public EventBeanUpdateHelper UpdateHelper { get; private set; } + + public bool IsOnMerge { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/table/onaction/TableOnDeleteView.cs b/NEsper.Core/NEsper.Core/epl/table/onaction/TableOnDeleteView.cs new file mode 100755 index 000000000..448fa9142 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/onaction/TableOnDeleteView.cs @@ -0,0 +1,62 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.table.onaction +{ + /// + /// View for the on-delete statement that handles removing events from a named window. + /// + public class TableOnDeleteView : TableOnViewBase + { + private readonly TableOnDeleteViewFactory parent; + + public TableOnDeleteView(SubordWMatchExprLookupStrategy lookupStrategy, TableStateInstance rootView, ExprEvaluatorContext exprEvaluatorContext, TableMetadata metadata, TableOnDeleteViewFactory parent) + : base(lookupStrategy, rootView, exprEvaluatorContext, metadata, true) + { + this.parent = parent; + } + + public override void HandleMatching(EventBean[] triggerEvents, EventBean[] matchingEvents) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QInfraOnAction(OnTriggerType.ON_DELETE, triggerEvents, matchingEvents);} + + if ((matchingEvents != null) && (matchingEvents.Length > 0)) + { + foreach (EventBean @event in matchingEvents) { + TableStateInstance.DeleteEvent(@event); + } + + // The on-delete listeners receive the events deleted, but only if there is interest + if (parent.StatementResultService.IsMakeNatural || parent.StatementResultService.IsMakeSynthetic) { + EventBean[] posted = TableOnViewUtil.ToPublic(matchingEvents, parent.TableMetadata, triggerEvents, true, base.ExprEvaluatorContext); + UpdateChildren(posted, null); + } + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AInfraOnAction();} + } + + public override EventType EventType + { + get { return parent.TableMetadata.PublicEventType; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/table/onaction/TableOnDeleteViewFactory.cs b/NEsper.Core/NEsper.Core/epl/table/onaction/TableOnDeleteViewFactory.cs new file mode 100755 index 000000000..4cc869d58 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/onaction/TableOnDeleteViewFactory.cs @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.epl.table.mgmt; + +namespace com.espertech.esper.epl.table.onaction +{ + public class TableOnDeleteViewFactory : TableOnViewFactory + { + public TableOnDeleteViewFactory(StatementResultService statementResultService, TableMetadata tableMetadata) + { + StatementResultService = statementResultService; + TableMetadata = tableMetadata; + } + + public StatementResultService StatementResultService { get; private set; } + + public TableMetadata TableMetadata { get; private set; } + + public TableOnViewBase Make( + SubordWMatchExprLookupStrategy lookupStrategy, + TableStateInstance tableState, + AgentInstanceContext agentInstanceContext, + ResultSetProcessor resultSetProcessor) + { + return new TableOnDeleteView(lookupStrategy, tableState, agentInstanceContext, TableMetadata, this); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/table/onaction/TableOnMergeView.cs b/NEsper.Core/NEsper.Core/epl/table/onaction/TableOnMergeView.cs new file mode 100755 index 000000000..c7e166aeb --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/onaction/TableOnMergeView.cs @@ -0,0 +1,118 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.table.merge; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.table.onaction +{ + public class TableOnMergeView : TableOnViewBase + { + private readonly TableOnMergeViewFactory parent; + + public TableOnMergeView( + SubordWMatchExprLookupStrategy lookupStrategy, + TableStateInstance rootView, + ExprEvaluatorContext exprEvaluatorContext, + TableMetadata metadata, + TableOnMergeViewFactory parent) + : base(lookupStrategy, rootView, exprEvaluatorContext, metadata, parent.OnMergeHelper.IsRequiresWriteLock) + { + this.parent = parent; + } + + public override void HandleMatching(EventBean[] triggerEvents, EventBean[] matchingEvents) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QInfraOnAction(OnTriggerType.ON_MERGE, triggerEvents, matchingEvents);} + + EventBean[] eventsPerStream = new EventBean[3]; // first:table, second: trigger, third:before-update (optional) + + bool postResultsToListeners = parent.StatementResultService.IsMakeNatural || parent.StatementResultService.IsMakeSynthetic; + TableOnMergeViewChangeHandler changeHandlerRemoved = null; + TableOnMergeViewChangeHandler changeHandlerAdded = null; + if (postResultsToListeners) { + changeHandlerRemoved = new TableOnMergeViewChangeHandler(parent.TableMetadata); + changeHandlerAdded = new TableOnMergeViewChangeHandler(parent.TableMetadata); + } + + if ((matchingEvents == null) || (matchingEvents.Length == 0)){ + + IList unmatched = parent.OnMergeHelper.Unmatched; + + foreach (EventBean triggerEvent in triggerEvents) { + eventsPerStream[1] = triggerEvent; + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QInfraMergeWhenThens(false, triggerEvent, unmatched.Count);} + + int count = -1; + foreach (TableOnMergeMatch action in unmatched) { + count++; + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QInfraMergeWhenThenItem(false, count);} + if (!action.IsApplies(eventsPerStream, base.ExprEvaluatorContext)) { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AInfraMergeWhenThenItem(false, false);} + continue; + } + action.Apply(null, eventsPerStream, TableStateInstance, changeHandlerAdded, changeHandlerRemoved, base.ExprEvaluatorContext); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AInfraMergeWhenThenItem(false, true);} + break; // apply no other actions + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AInfraMergeWhenThens(false);} + } + } + else + { + IList matched = parent.OnMergeHelper.Matched; + + foreach (EventBean triggerEvent in triggerEvents) { + eventsPerStream[1] = triggerEvent; + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QInfraMergeWhenThens(true, triggerEvent, matched.Count);} + + foreach (EventBean matchingEvent in matchingEvents) { + eventsPerStream[0] = matchingEvent; + + int count = -1; + foreach (TableOnMergeMatch action in matched) { + count++; + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QInfraMergeWhenThenItem(true, count);} + if (!action.IsApplies(eventsPerStream, base.ExprEvaluatorContext)) { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AInfraMergeWhenThenItem(true, false);} + continue; + } + action.Apply(matchingEvent, eventsPerStream, TableStateInstance, changeHandlerAdded, changeHandlerRemoved, base.ExprEvaluatorContext); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AInfraMergeWhenThenItem(true, true);} + break; // apply no other actions + } + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AInfraMergeWhenThens(true);} + } + } + + // The on-delete listeners receive the events deleted, but only if there is interest + if (postResultsToListeners) { + EventBean[] postedNew = changeHandlerAdded.Events; + EventBean[] postedOld = changeHandlerRemoved.Events; + if (postedNew != null || postedOld != null) { + UpdateChildren(postedNew, postedOld); + } + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AInfraOnAction();} + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/table/onaction/TableOnMergeViewChangeHandler.cs b/NEsper.Core/NEsper.Core/epl/table/onaction/TableOnMergeViewChangeHandler.cs new file mode 100755 index 000000000..f05572cd6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/onaction/TableOnMergeViewChangeHandler.cs @@ -0,0 +1,52 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.table.onaction +{ + public class TableOnMergeViewChangeHandler + { + private readonly TableMetadata _tableMetadata; + private OneEventCollection _coll; + + public TableOnMergeViewChangeHandler(TableMetadata tableMetadata) + { + _tableMetadata = tableMetadata; + } + + public EventBean[] Events + { + get + { + if (_coll == null) + { + return null; + } + return _coll.ToArray(); + } + } + + public void Add(EventBean theEvent, EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + if (_coll == null) + { + _coll = new OneEventCollection(); + } + if (theEvent is NaturalEventBean) + { + theEvent = ((NaturalEventBean) theEvent).OptionalSynthetic; + } + _coll.Add(_tableMetadata.EventToPublic.Convert(theEvent, new EvaluateParams(eventsPerStream, isNewData, context))); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/table/onaction/TableOnMergeViewFactory.cs b/NEsper.Core/NEsper.Core/epl/table/onaction/TableOnMergeViewFactory.cs new file mode 100755 index 000000000..cbd26c01b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/onaction/TableOnMergeViewFactory.cs @@ -0,0 +1,54 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.epl.metric; +using com.espertech.esper.epl.table.merge; +using com.espertech.esper.epl.table.mgmt; + +namespace com.espertech.esper.epl.table.onaction +{ + public class TableOnMergeViewFactory : TableOnViewFactory + { + public TableOnMergeViewFactory( + TableMetadata tableMetadata, + TableOnMergeHelper onMergeHelper, + StatementResultService statementResultService, + StatementMetricHandle metricsHandle, + MetricReportingServiceSPI metricReportingService) + { + TableMetadata = tableMetadata; + OnMergeHelper = onMergeHelper; + StatementResultService = statementResultService; + MetricsHandle = metricsHandle; + MetricReportingService = metricReportingService; + } + + public TableMetadata TableMetadata { get; private set; } + + public TableOnMergeHelper OnMergeHelper { get; private set; } + + public StatementResultService StatementResultService { get; private set; } + + public StatementMetricHandle MetricsHandle { get; private set; } + + public MetricReportingServiceSPI MetricReportingService { get; private set; } + + public TableOnViewBase Make( + SubordWMatchExprLookupStrategy lookupStrategy, + TableStateInstance tableState, + AgentInstanceContext agentInstanceContext, + ResultSetProcessor resultSetProcessor) + { + return new TableOnMergeView(lookupStrategy, tableState, agentInstanceContext, TableMetadata, this); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/table/onaction/TableOnSelectView.cs b/NEsper.Core/NEsper.Core/epl/table/onaction/TableOnSelectView.cs new file mode 100755 index 000000000..33097b00b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/onaction/TableOnSelectView.cs @@ -0,0 +1,122 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.epl.named; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.events; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.table.onaction +{ + public class TableOnSelectView : TableOnViewBase + { + private readonly TableOnSelectViewFactory _parent; + private readonly ResultSetProcessor _resultSetProcessor; + private readonly bool _audit; + private readonly bool _deleteAndSelect; + + public TableOnSelectView(SubordWMatchExprLookupStrategy lookupStrategy, TableStateInstance rootView, ExprEvaluatorContext exprEvaluatorContext, TableMetadata metadata, + TableOnSelectViewFactory parent, ResultSetProcessor resultSetProcessor, bool audit, bool deleteAndSelect) + : base(lookupStrategy, rootView, exprEvaluatorContext, metadata, deleteAndSelect) + { + _parent = parent; + _resultSetProcessor = resultSetProcessor; + _audit = audit; + _deleteAndSelect = deleteAndSelect; + } + + public override void HandleMatching(EventBean[] triggerEvents, EventBean[] matchingEvents) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QInfraOnAction(OnTriggerType.ON_SELECT, triggerEvents, matchingEvents); } + + EventBean[] newData; + + // clear state from prior results + _resultSetProcessor.Clear(); + + // build join result + // use linked hash set to retain order of join results for last/first/window to work most intuitively + ISet> newEvents = NamedWindowOnSelectView.BuildJoinResult(triggerEvents, matchingEvents); + + // process matches + UniformPair pair = _resultSetProcessor.ProcessJoinResult(newEvents, Collections.GetEmptySet>(), false); + newData = (pair != null ? pair.First : null); + + if (_parent.IsDistinct) + { + newData = EventBeanUtility.GetDistinctByProp(newData, _parent.EventBeanReader); + } + + if (_parent.InternalEventRouter != null) + { + if (newData != null) + { + for (int i = 0; i < newData.Length; i++) + { + if (_audit) + { + AuditPath.AuditInsertInto(ExprEvaluatorContext.EngineURI, ExprEvaluatorContext.StatementName, newData[i]); + } + _parent.InternalEventRouter.Route(newData[i], _parent.StatementHandle, _parent.InternalEventRouteDest, ExprEvaluatorContext, false); + } + } + } + + // The on-select listeners receive the events selected + if ((newData != null) && (newData.Length > 0)) + { + // And post only if we have listeners/subscribers that need the data + if (_parent.StatementResultService.IsMakeNatural || _parent.StatementResultService.IsMakeSynthetic) + { + UpdateChildren(newData, null); + } + } + + // clear state from prior results + _resultSetProcessor.Clear(); + + // Events to delete are indicated via old data + if (_deleteAndSelect) + { + foreach (EventBean @event in matchingEvents) + { + TableStateInstance.DeleteEvent(@event); + } + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AInfraOnAction(); } + } + + public override EventType EventType + { + get + { + if (_resultSetProcessor != null) + { + return _resultSetProcessor.ResultEventType; + } + else + { + return base.EventType; + } + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/table/onaction/TableOnSelectViewFactory.cs b/NEsper.Core/NEsper.Core/epl/table/onaction/TableOnSelectViewFactory.cs new file mode 100755 index 000000000..d0f354ca5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/onaction/TableOnSelectViewFactory.cs @@ -0,0 +1,58 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client.annotation; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.table.onaction +{ + public class TableOnSelectViewFactory : TableOnViewFactory + { + private readonly TableMetadata _tableMetadata; + private readonly bool _deleteAndSelect; + + public TableOnSelectViewFactory(TableMetadata tableMetadata, InternalEventRouter internalEventRouter, EPStatementHandle statementHandle, EventBeanReader eventBeanReader, bool distinct, StatementResultService statementResultService, InternalEventRouteDest internalEventRouteDest, bool deleteAndSelect) + { + _tableMetadata = tableMetadata; + InternalEventRouter = internalEventRouter; + StatementHandle = statementHandle; + EventBeanReader = eventBeanReader; + IsDistinct = distinct; + StatementResultService = statementResultService; + InternalEventRouteDest = internalEventRouteDest; + _deleteAndSelect = deleteAndSelect; + } + + public TableOnViewBase Make(SubordWMatchExprLookupStrategy lookupStrategy, TableStateInstance tableState, AgentInstanceContext agentInstanceContext, ResultSetProcessor resultSetProcessor) + { + bool audit = AuditEnum.INSERT.GetAudit(agentInstanceContext.StatementContext.Annotations) != null; + return new TableOnSelectView(lookupStrategy, tableState, agentInstanceContext, _tableMetadata, this, resultSetProcessor, audit, _deleteAndSelect); + } + + public InternalEventRouter InternalEventRouter { get; private set; } + + public EPStatementHandle StatementHandle { get; private set; } + + public EventBeanReader EventBeanReader { get; private set; } + + public bool IsDistinct { get; private set; } + + public StatementResultService StatementResultService { get; private set; } + + public InternalEventRouteDest InternalEventRouteDest { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/table/onaction/TableOnUpdateView.cs b/NEsper.Core/NEsper.Core/epl/table/onaction/TableOnUpdateView.cs new file mode 100755 index 000000000..407fea1bf --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/onaction/TableOnUpdateView.cs @@ -0,0 +1,62 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.epl.table.upd; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.table.onaction +{ + public class TableOnUpdateView : TableOnViewBase + { + private readonly TableOnUpdateViewFactory parent; + + public TableOnUpdateView(SubordWMatchExprLookupStrategy lookupStrategy, TableStateInstance rootView, ExprEvaluatorContext exprEvaluatorContext, TableMetadata metadata, TableOnUpdateViewFactory parent) + : base(lookupStrategy, rootView, exprEvaluatorContext, metadata, true) + { + this.parent = parent; + } + + public override void HandleMatching(EventBean[] triggerEvents, EventBean[] matchingEvents) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QInfraOnAction(OnTriggerType.ON_UPDATE, triggerEvents, matchingEvents);} + + EventBean[] eventsPerStream = new EventBean[3]; + + bool postUpdates = parent.StatementResultService.IsMakeNatural || parent.StatementResultService.IsMakeSynthetic; + EventBean[] postedOld = null; + if (postUpdates) { + postedOld = TableOnViewUtil.ToPublic(matchingEvents, parent.TableMetadata, triggerEvents, false, base.ExprEvaluatorContext); + } + + TableUpdateStrategy tableUpdateStrategy = parent.TableUpdateStrategy; + + foreach (EventBean triggerEvent in triggerEvents) { + eventsPerStream[1] = triggerEvent; + tableUpdateStrategy.UpdateTable(matchingEvents, TableStateInstance, eventsPerStream, exprEvaluatorContext); + } + + // The on-delete listeners receive the events deleted, but only if there is interest + if (postUpdates) { + EventBean[] postedNew = TableOnViewUtil.ToPublic(matchingEvents, parent.TableMetadata, triggerEvents, true, base.ExprEvaluatorContext); + UpdateChildren(postedNew, postedOld); + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AInfraOnAction();} + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/table/onaction/TableOnUpdateViewFactory.cs b/NEsper.Core/NEsper.Core/epl/table/onaction/TableOnUpdateViewFactory.cs new file mode 100755 index 000000000..379c210e2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/onaction/TableOnUpdateViewFactory.cs @@ -0,0 +1,46 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.epl.table.upd; +using com.espertech.esper.epl.updatehelper; + +namespace com.espertech.esper.epl.table.onaction +{ + public class TableOnUpdateViewFactory : TableOnViewFactory, TableUpdateStrategyReceiver + { + public TableOnUpdateViewFactory(StatementResultService statementResultService, TableMetadata tableMetadata, EventBeanUpdateHelper updateHelper, TableUpdateStrategy tableUpdateStrategy) + { + StatementResultService = statementResultService; + TableMetadata = tableMetadata; + UpdateHelper = updateHelper; + TableUpdateStrategy = tableUpdateStrategy; + } + + public TableOnViewBase Make(SubordWMatchExprLookupStrategy lookupStrategy, TableStateInstance tableState, AgentInstanceContext agentInstanceContext, ResultSetProcessor resultSetProcessor) + { + return new TableOnUpdateView(lookupStrategy, tableState, agentInstanceContext, TableMetadata, this); + } + + public StatementResultService StatementResultService { get; private set; } + + public TableMetadata TableMetadata { get; private set; } + + public EventBeanUpdateHelper UpdateHelper { get; private set; } + + public TableUpdateStrategy TableUpdateStrategy { get; private set; } + + public void Update(TableUpdateStrategy updateStrategy) { + TableUpdateStrategy = updateStrategy; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/table/onaction/TableOnViewBase.cs b/NEsper.Core/NEsper.Core/epl/table/onaction/TableOnViewBase.cs new file mode 100755 index 000000000..c603ae0f1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/onaction/TableOnViewBase.cs @@ -0,0 +1,94 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.epl.table.onaction +{ + public abstract class TableOnViewBase : ViewSupport + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + protected readonly SubordWMatchExprLookupStrategy LookupStrategy; + protected readonly TableStateInstance TableStateInstance; + protected readonly ExprEvaluatorContext exprEvaluatorContext; + protected readonly TableMetadata Metadata; + protected readonly bool AcquireWriteLock; + + protected TableOnViewBase(SubordWMatchExprLookupStrategy lookupStrategy, TableStateInstance tableStateInstance, ExprEvaluatorContext exprEvaluatorContext, TableMetadata metadata, bool acquireWriteLock) + { + this.LookupStrategy = lookupStrategy; + this.TableStateInstance = tableStateInstance; + this.exprEvaluatorContext = exprEvaluatorContext; + this.Metadata = metadata; + this.AcquireWriteLock = acquireWriteLock; + } + + public abstract void HandleMatching(EventBean[] triggerEvents, EventBean[] matchingEvents); + + public void Stop() + { + Log.Debug(".stop"); + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + if (newData == null) + { + return; + } + + if (AcquireWriteLock) + { + using (TableStateInstance.TableLevelRWLock.WriteLock.Acquire()) + { + EventBean[] eventsFound = LookupStrategy.Lookup(newData, exprEvaluatorContext); + HandleMatching(newData, eventsFound); + } + } + else + { + using (TableStateInstance.TableLevelRWLock.ReadLock.Acquire()) + { + EventBean[] eventsFound = LookupStrategy.Lookup(newData, exprEvaluatorContext); + HandleMatching(newData, eventsFound); + } + } + } + + /// + /// returns expr context. + /// + /// context + public ExprEvaluatorContext ExprEvaluatorContext + { + get { return exprEvaluatorContext; } + } + + public override IEnumerator GetEnumerator() + { + return CollectionUtil.NULL_EVENT_ITERATOR; + } + + public override EventType EventType + { + get { return Metadata.PublicEventType; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/table/onaction/TableOnViewFactory.cs b/NEsper.Core/NEsper.Core/epl/table/onaction/TableOnViewFactory.cs new file mode 100755 index 000000000..6a6fffb72 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/onaction/TableOnViewFactory.cs @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.epl.table.mgmt; + +namespace com.espertech.esper.epl.table.onaction +{ + public interface TableOnViewFactory + { + TableOnViewBase Make(SubordWMatchExprLookupStrategy lookupStrategy, + TableStateInstance tableState, + AgentInstanceContext agentInstanceContext, + ResultSetProcessor resultSetProcessor); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/table/onaction/TableOnViewFactoryFactory.cs b/NEsper.Core/NEsper.Core/epl/table/onaction/TableOnViewFactoryFactory.cs new file mode 100755 index 000000000..e23705d66 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/onaction/TableOnViewFactoryFactory.cs @@ -0,0 +1,67 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.metric; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.table.merge; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.epl.table.upd; +using com.espertech.esper.epl.updatehelper; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.table.onaction +{ + /// + /// View for the on-delete statement that handles removing events from a named window. + /// + public class TableOnViewFactoryFactory { + public static TableOnViewFactory Make(TableMetadata tableMetadata, + OnTriggerDesc onTriggerDesc, + EventType filterEventType, + string filterStreamName, + StatementContext statementContext, + StatementMetricHandle metricsHandle, + bool isDistinct, + InternalEventRouter internalEventRouter + ) + { + if (onTriggerDesc.OnTriggerType == OnTriggerType.ON_DELETE) { + return new TableOnDeleteViewFactory(statementContext.StatementResultService, tableMetadata); + } else if (onTriggerDesc.OnTriggerType == OnTriggerType.ON_SELECT) { + EventBeanReader eventBeanReader = null; + if (isDistinct) { + eventBeanReader = tableMetadata.InternalEventType.Reader; + } + OnTriggerWindowDesc windowDesc = (OnTriggerWindowDesc) onTriggerDesc; + return new TableOnSelectViewFactory(tableMetadata, internalEventRouter, statementContext.EpStatementHandle, + eventBeanReader, isDistinct, statementContext.StatementResultService, statementContext.InternalEventEngineRouteDest, windowDesc.IsDeleteAndSelect); + } else if (onTriggerDesc.OnTriggerType == OnTriggerType.ON_UPDATE) { + OnTriggerWindowUpdateDesc updateDesc = (OnTriggerWindowUpdateDesc) onTriggerDesc; + EventBeanUpdateHelper updateHelper = EventBeanUpdateHelperFactory.Make(tableMetadata.TableName, (EventTypeSPI) tableMetadata.InternalEventType, updateDesc.Assignments, updateDesc.OptionalAsName, filterEventType, false, statementContext.StatementName, statementContext.EngineURI, statementContext.EventAdapterService); + TableUpdateStrategy updateStrategy = statementContext.TableService.GetTableUpdateStrategy(tableMetadata, updateHelper, false); + var onUpdateViewFactory = new TableOnUpdateViewFactory(statementContext.StatementResultService, tableMetadata, updateHelper, updateStrategy); + statementContext.TableService.AddTableUpdateStrategyReceiver(tableMetadata, statementContext.StatementName, onUpdateViewFactory, updateHelper, false); + return onUpdateViewFactory; + } else if (onTriggerDesc.OnTriggerType == OnTriggerType.ON_MERGE) { + OnTriggerMergeDesc onMergeTriggerDesc = (OnTriggerMergeDesc) onTriggerDesc; + var onMergeHelper = new TableOnMergeHelper(statementContext, onMergeTriggerDesc, filterEventType, filterStreamName, internalEventRouter, tableMetadata); + return new TableOnMergeViewFactory(tableMetadata, onMergeHelper, statementContext.StatementResultService, metricsHandle, statementContext.MetricReportingService); + } else { + throw new IllegalStateException("Unknown trigger type " + onTriggerDesc.OnTriggerType); + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/table/onaction/TableOnViewUtil.cs b/NEsper.Core/NEsper.Core/epl/table/onaction/TableOnViewUtil.cs new file mode 100755 index 000000000..32b57aa69 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/onaction/TableOnViewUtil.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.table.mgmt; + +namespace com.espertech.esper.epl.table.onaction +{ + public class TableOnViewUtil + { + public static EventBean[] ToPublic( + EventBean[] matching, + TableMetadata tableMetadata, + EventBean[] triggers, + bool isNewData, + ExprEvaluatorContext context) + { + var eventsPerStream = new EventBean[2]; + eventsPerStream[0] = triggers[0]; + + var evaluateParams = new EvaluateParams(eventsPerStream, isNewData, context); + var events = new EventBean[matching.Length]; + for (var i = 0; i < events.Length; i++) + { + eventsPerStream[1] = matching[i]; + events[i] = tableMetadata.EventToPublic.Convert(matching[i], evaluateParams); + } + return events; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalLockUtil.cs b/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalLockUtil.cs new file mode 100755 index 000000000..bc2172bda --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalLockUtil.cs @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.compat.threading; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.table.mgmt; + +namespace com.espertech.esper.epl.table.strategy +{ + public class ExprTableEvalLockUtil + { + public static void ObtainLockUnless(ILockable @lock, ExprEvaluatorContext exprEvaluatorContext) + { + ObtainLockUnless(@lock, exprEvaluatorContext.TableExprEvaluatorContext); + } + + public static void ObtainLockUnless(ILockable @lock, TableExprEvaluatorContext tableExprEvaluatorContext) + { + tableExprEvaluatorContext.AddAcquiredLock(@lock); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyFactory.cs b/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyFactory.cs new file mode 100755 index 000000000..cf542ef31 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyFactory.cs @@ -0,0 +1,167 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.table; +using com.espertech.esper.epl.table.mgmt; + +namespace com.espertech.esper.epl.table.strategy +{ + public class ExprTableEvalStrategyFactory + { + public static ExprEvaluator GetTableAccessEvalStrategy( + ExprNode exprNode, + string tableName, + int streamNum, + TableMetadataColumnAggregation agg) + { + if (!agg.Factory.IsAccessAggregation) + { + return new ExprTableExprEvaluatorMethod( + exprNode, tableName, agg.ColumnName, streamNum, agg.Factory.ResultType, agg.MethodOffset); + } + else + { + return new ExprTableExprEvaluatorAccess( + exprNode, tableName, agg.ColumnName, streamNum, agg.Factory.ResultType, agg.AccessAccessorSlotPair, + agg.OptionalEventType); + } + } + + public static ExprTableAccessEvalStrategy GetTableAccessEvalStrategy( + ExprTableAccessNode tableNode, + TableAndLockProvider provider, + TableMetadata tableMetadata) + { + var groupKeyEvals = tableNode.GroupKeyEvaluators; + + TableAndLockProviderUngrouped ungrouped; + TableAndLockProviderGrouped grouped; + if (provider is TableAndLockProviderUngrouped) + { + ungrouped = (TableAndLockProviderUngrouped) provider; + grouped = null; + } + else + { + grouped = (TableAndLockProviderGrouped) provider; + ungrouped = null; + } + + // handle sub-property access + if (tableNode is ExprTableAccessNodeSubprop) + { + var subprop = (ExprTableAccessNodeSubprop) tableNode; + var column = tableMetadata.TableColumns.Get(subprop.SubpropName); + return GetTableAccessSubprop(subprop, column, ungrouped, grouped); + } + + // handle top-level access + if (tableNode is ExprTableAccessNodeTopLevel) + { + if (ungrouped != null) + { + return new ExprTableEvalStrategyUngroupedTopLevel(ungrouped, tableMetadata.TableColumns); + } + if (tableNode.GroupKeyEvaluators.Length > 1) + { + return new ExprTableEvalStrategyGroupByTopLevelMulti( + grouped, tableMetadata.TableColumns, groupKeyEvals); + } + return new ExprTableEvalStrategyGroupByTopLevelSingle( + grouped, tableMetadata.TableColumns, groupKeyEvals[0]); + } + + // handle "keys" function access + if (tableNode is ExprTableAccessNodeKeys) + { + return new ExprTableEvalStrategyGroupByKeys(grouped); + } + + // handle access-aggregator accessors + if (tableNode is ExprTableAccessNodeSubpropAccessor) + { + var accessorProvider = (ExprTableAccessNodeSubpropAccessor) tableNode; + var column = + (TableMetadataColumnAggregation) tableMetadata.TableColumns.Get(accessorProvider.SubpropName); + if (ungrouped != null) + { + var pairX = column.AccessAccessorSlotPair; + return new ExprTableEvalStrategyUngroupedAccess(ungrouped, pairX.Slot, accessorProvider.Accessor); + } + + var pair = new AggregationAccessorSlotPair( + column.AccessAccessorSlotPair.Slot, accessorProvider.Accessor); + if (tableNode.GroupKeyEvaluators.Length > 1) + { + return new ExprTableEvalStrategyGroupByAccessMulti(grouped, pair, groupKeyEvals); + } + return new ExprTableEvalStrategyGroupByAccessSingle(grouped, pair, groupKeyEvals[0]); + } + + throw new IllegalStateException("Unrecognized table access node " + tableNode); + } + + private static ExprTableAccessEvalStrategy GetTableAccessSubprop( + ExprTableAccessNodeSubprop subprop, + TableMetadataColumn column, + TableAndLockProviderUngrouped ungrouped, + TableAndLockProviderGrouped grouped) + { + if (column is TableMetadataColumnPlain) + { + var plain = (TableMetadataColumnPlain) column; + if (ungrouped != null) + { + return new ExprTableEvalStrategyUngroupedProp( + ungrouped, plain.IndexPlain, subprop.OptionalPropertyEnumEvaluator); + } + if (subprop.GroupKeyEvaluators.Length > 1) + { + return new ExprTableEvalStrategyGroupByPropMulti( + grouped, plain.IndexPlain, subprop.OptionalPropertyEnumEvaluator, subprop.GroupKeyEvaluators); + } + return new ExprTableEvalStrategyGroupByPropSingle( + grouped, plain.IndexPlain, subprop.OptionalPropertyEnumEvaluator, subprop.GroupKeyEvaluators[0]); + } + + var aggcol = (TableMetadataColumnAggregation) column; + if (ungrouped != null) + { + if (!aggcol.Factory.IsAccessAggregation) + { + return new ExprTableEvalStrategyUngroupedMethod(ungrouped, aggcol.MethodOffset); + } + var pair = aggcol.AccessAccessorSlotPair; + return new ExprTableEvalStrategyUngroupedAccess(ungrouped, pair.Slot, pair.Accessor); + } + + var columnAggregation = (TableMetadataColumnAggregation) column; + if (!columnAggregation.Factory.IsAccessAggregation) + { + if (subprop.GroupKeyEvaluators.Length > 1) + { + return new ExprTableEvalStrategyGroupByMethodMulti( + grouped, columnAggregation.MethodOffset, subprop.GroupKeyEvaluators); + } + return new ExprTableEvalStrategyGroupByMethodSingle( + grouped, columnAggregation.MethodOffset, subprop.GroupKeyEvaluators[0]); + } + if (subprop.GroupKeyEvaluators.Length > 1) + { + return new ExprTableEvalStrategyGroupByAccessMulti( + grouped, columnAggregation.AccessAccessorSlotPair, subprop.GroupKeyEvaluators); + } + return new ExprTableEvalStrategyGroupByAccessSingle( + grouped, columnAggregation.AccessAccessorSlotPair, subprop.GroupKeyEvaluators[0]); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyGroupByAccessBase.cs b/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyGroupByAccessBase.cs new file mode 100755 index 000000000..29426c546 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyGroupByAccessBase.cs @@ -0,0 +1,81 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.table; + +namespace com.espertech.esper.epl.table.strategy +{ + public abstract class ExprTableEvalStrategyGroupByAccessBase + : ExprTableEvalStrategyGroupByBase + , ExprTableAccessEvalStrategy + { + private readonly AggregationAccessorSlotPair _pair; + + public abstract object Evaluate(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext exprEvaluatorContext); + public abstract ICollection EvaluateGetROCollectionEvents(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context); + public abstract EventBean EvaluateGetEventBean(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context); + public abstract ICollection EvaluateGetROCollectionScalar(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context); + + protected ExprTableEvalStrategyGroupByAccessBase(TableAndLockProviderGrouped provider, AggregationAccessorSlotPair pair) + : base(provider) + { + _pair = pair; + } + + protected object EvaluateInternal(object group, EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + var row = LockTableReadAndGet(group, context); + if (row == null) + { + return null; + } + return ExprTableEvalStrategyUtil.EvalAccessorGetValue(ExprTableEvalStrategyUtil.GetRow(row), _pair, eventsPerStream, isNewData, context); + } + + public object[] EvaluateTypableSingle(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + throw new IllegalStateException("Not typable"); + } + + protected ICollection EvaluateGetROCollectionEventsInternal(object group, EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + var row = LockTableReadAndGet(group, context); + if (row == null) + { + return null; + } + return ExprTableEvalStrategyUtil.EvalGetROCollectionEvents(ExprTableEvalStrategyUtil.GetRow(row), _pair, eventsPerStream, isNewData, context); + } + + protected EventBean EvaluateGetEventBeanInternal(object group, EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + var row = LockTableReadAndGet(group, context); + if (row == null) + { + return null; + } + return ExprTableEvalStrategyUtil.EvalGetEventBean(ExprTableEvalStrategyUtil.GetRow(row), _pair, eventsPerStream, isNewData, context); + } + + protected ICollection EvaluateGetROCollectionScalarInternal(object group, EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + var row = LockTableReadAndGet(group, context); + if (row == null) + { + return null; + } + return ExprTableEvalStrategyUtil.EvalGetROCollectionScalar(ExprTableEvalStrategyUtil.GetRow(row), _pair, eventsPerStream, isNewData, context); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyGroupByAccessMulti.cs b/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyGroupByAccessMulti.cs new file mode 100755 index 000000000..2a8306fa0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyGroupByAccessMulti.cs @@ -0,0 +1,63 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.threading; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.table.strategy +{ + public class ExprTableEvalStrategyGroupByAccessMulti : ExprTableEvalStrategyGroupByAccessBase + { + private readonly ExprEvaluator[] _groupExpr; + + public ExprTableEvalStrategyGroupByAccessMulti(TableAndLockProviderGrouped provider, AggregationAccessorSlotPair pair, ExprEvaluator[] groupExpr) + : base(provider, pair) + { + _groupExpr = groupExpr; + } + + public override object Evaluate(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext exprEvaluatorContext) { + object group = GetKey(_groupExpr, eventsPerStream, isNewData, exprEvaluatorContext); + return EvaluateInternal(group, eventsPerStream, isNewData, exprEvaluatorContext); + } + + public override ICollection EvaluateGetROCollectionEvents(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) { + object group = GetKey(_groupExpr, eventsPerStream, isNewData, context); + return EvaluateGetROCollectionEventsInternal(group, eventsPerStream, isNewData, context); + } + + public override EventBean EvaluateGetEventBean(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + object group = GetKey(_groupExpr, eventsPerStream, isNewData, context); + return EvaluateGetEventBeanInternal(group, eventsPerStream, isNewData, context); + } + + public override ICollection EvaluateGetROCollectionScalar(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + object group = GetKey(_groupExpr, eventsPerStream, isNewData, context); + return EvaluateGetROCollectionScalarInternal(group, eventsPerStream, isNewData, context); + } + + internal static MultiKeyUntyped GetKey(ExprEvaluator[] evaluators, EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) { + object[] keys = new object[evaluators.Length]; + for (int i = 0; i < evaluators.Length; i++) { + keys[i] = evaluators[i].Evaluate(new EvaluateParams(eventsPerStream, isNewData, context)); + } + return new MultiKeyUntyped(keys); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyGroupByAccessSingle.cs b/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyGroupByAccessSingle.cs new file mode 100755 index 000000000..ae61e40d9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyGroupByAccessSingle.cs @@ -0,0 +1,52 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.threading; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.table.strategy +{ + public class ExprTableEvalStrategyGroupByAccessSingle : ExprTableEvalStrategyGroupByAccessBase + { + private readonly ExprEvaluator _groupExpr; + + public ExprTableEvalStrategyGroupByAccessSingle(TableAndLockProviderGrouped provider, AggregationAccessorSlotPair pair, ExprEvaluator groupExpr) + : base(provider, pair) + { + this._groupExpr = groupExpr; + } + + public override object Evaluate(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext exprEvaluatorContext) { + object group = _groupExpr.Evaluate(new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext)); + return EvaluateInternal(group, eventsPerStream, isNewData, exprEvaluatorContext); + } + + public override ICollection EvaluateGetROCollectionEvents(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) { + object group = _groupExpr.Evaluate(new EvaluateParams(eventsPerStream, isNewData, context)); + return EvaluateGetROCollectionEventsInternal(group, eventsPerStream, isNewData, context); + } + + public override EventBean EvaluateGetEventBean(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) { + object group = _groupExpr.Evaluate(new EvaluateParams(eventsPerStream, isNewData, context)); + return EvaluateGetEventBeanInternal(group, eventsPerStream, isNewData, context); + } + + public override ICollection EvaluateGetROCollectionScalar(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) { + object group = _groupExpr.Evaluate(new EvaluateParams(eventsPerStream, isNewData, context)); + return EvaluateGetROCollectionScalarInternal(group, eventsPerStream, isNewData, context); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyGroupByBase.cs b/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyGroupByBase.cs new file mode 100755 index 000000000..15b71e241 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyGroupByBase.cs @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.table.strategy +{ + public abstract class ExprTableEvalStrategyGroupByBase + { + private readonly TableAndLockProviderGrouped _provider; + + protected ExprTableEvalStrategyGroupByBase(TableAndLockProviderGrouped provider) + { + _provider = provider; + } + + protected ObjectArrayBackedEventBean LockTableReadAndGet(object group, ExprEvaluatorContext context) + { + TableAndLockGrouped tableAndLockGrouped = _provider.Get(); + ExprTableEvalLockUtil.ObtainLockUnless(tableAndLockGrouped.Lock, context); + return tableAndLockGrouped.Grouped.GetRowForGroupKey(group); + } + + protected TableStateInstanceGrouped LockTableRead(ExprEvaluatorContext context) + { + TableAndLockGrouped tableAndLockGrouped = _provider.Get(); + ExprTableEvalLockUtil.ObtainLockUnless(tableAndLockGrouped.Lock, context); + return tableAndLockGrouped.Grouped; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyGroupByKeys.cs b/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyGroupByKeys.cs new file mode 100755 index 000000000..b68ff8166 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyGroupByKeys.cs @@ -0,0 +1,61 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.table; +using com.espertech.esper.epl.table.mgmt; + +namespace com.espertech.esper.epl.table.strategy +{ + public class ExprTableEvalStrategyGroupByKeys + : ExprTableEvalStrategyGroupByBase, + ExprTableAccessEvalStrategy + { + public ExprTableEvalStrategyGroupByKeys(TableAndLockProviderGrouped provider) + : base(provider) + { + } + + public object Evaluate(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + TableStateInstanceGrouped grouped = LockTableRead(context); + ICollection keys = grouped.GroupKeys; + return keys.ToArray(); + } + + public ICollection EvaluateGetROCollectionEvents( + EventBean[] eventsPerStream, + bool isNewData, + ExprEvaluatorContext context) + { + return null; + } + + public EventBean EvaluateGetEventBean(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + return null; + } + + public ICollection EvaluateGetROCollectionScalar( + EventBean[] eventsPerStream, + bool isNewData, + ExprEvaluatorContext context) + { + return null; + } + + public object[] EvaluateTypableSingle(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + return null; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyGroupByMethodBase.cs b/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyGroupByMethodBase.cs new file mode 100755 index 000000000..c880f4beb --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyGroupByMethodBase.cs @@ -0,0 +1,70 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.threading; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.table; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.table.strategy +{ + public abstract class ExprTableEvalStrategyGroupByMethodBase + : ExprTableEvalStrategyGroupByBase + , + ExprTableAccessEvalStrategy + { + private readonly int _index; + + public abstract object Evaluate( + EventBean[] eventsPerStream, + bool isNewData, + ExprEvaluatorContext exprEvaluatorContext); + + protected ExprTableEvalStrategyGroupByMethodBase(TableAndLockProviderGrouped provider, int index) + : base(provider) + { + _index = index; + } + + protected object EvaluateInternal(object groupKey, ExprEvaluatorContext context) + { + var row = LockTableReadAndGet(groupKey, context); + if (row == null) + { + return null; + } + return ExprTableEvalStrategyUtil.EvalMethodGetValue(ExprTableEvalStrategyUtil.GetRow(row), _index); + } + + public object[] EvaluateTypableSingle(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + return null; + } + + public ICollection EvaluateGetROCollectionEvents(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + return null; + } + + public EventBean EvaluateGetEventBean(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + return null; + } + + public ICollection EvaluateGetROCollectionScalar(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + return null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyGroupByMethodMulti.cs b/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyGroupByMethodMulti.cs new file mode 100755 index 000000000..67a0ca8bd --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyGroupByMethodMulti.cs @@ -0,0 +1,30 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.table.strategy +{ + public class ExprTableEvalStrategyGroupByMethodMulti : ExprTableEvalStrategyGroupByMethodBase + { + private readonly ExprEvaluator[] _groupExpr; + + public ExprTableEvalStrategyGroupByMethodMulti(TableAndLockProviderGrouped provider, int index, ExprEvaluator[] groupExpr) + : base(provider, index) + { + _groupExpr = groupExpr; + } + + public override object Evaluate(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext exprEvaluatorContext) + { + object groupKey = ExprTableEvalStrategyGroupByAccessMulti.GetKey(_groupExpr, eventsPerStream, isNewData, exprEvaluatorContext); + return EvaluateInternal(groupKey, exprEvaluatorContext); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyGroupByMethodSingle.cs b/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyGroupByMethodSingle.cs new file mode 100755 index 000000000..64bccef6d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyGroupByMethodSingle.cs @@ -0,0 +1,30 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.table.strategy +{ + public class ExprTableEvalStrategyGroupByMethodSingle : ExprTableEvalStrategyGroupByMethodBase + { + private readonly ExprEvaluator _groupExpr; + + public ExprTableEvalStrategyGroupByMethodSingle(TableAndLockProviderGrouped provider, int index, ExprEvaluator groupExpr) + : base(provider, index) + { + _groupExpr = groupExpr; + } + + public override object Evaluate(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext exprEvaluatorContext) + { + object groupKey = _groupExpr.Evaluate(new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext)); + return EvaluateInternal(groupKey, exprEvaluatorContext); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyGroupByPropBase.cs b/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyGroupByPropBase.cs new file mode 100755 index 000000000..ce8074b4e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyGroupByPropBase.cs @@ -0,0 +1,73 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.threading; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.table; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.table.strategy +{ + public abstract class ExprTableEvalStrategyGroupByPropBase : ExprTableEvalStrategyGroupByBase , ExprTableAccessEvalStrategy + { + private readonly int _propertyIndex; + private readonly ExprEvaluatorEnumerationGivenEvent _optionalEnumEval; + + public abstract object Evaluate(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext exprEvaluatorContext); + public abstract ICollection EvaluateGetROCollectionEvents(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context); + public abstract EventBean EvaluateGetEventBean(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context); + public abstract ICollection EvaluateGetROCollectionScalar(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context); + + protected ExprTableEvalStrategyGroupByPropBase(TableAndLockProviderGrouped provider, int propertyIndex, ExprEvaluatorEnumerationGivenEvent optionalEnumEval) + : base(provider) + { + _propertyIndex = propertyIndex; + _optionalEnumEval = optionalEnumEval; + } + + public object EvaluateInternal(object groupKey, ExprEvaluatorContext context) { + var row = LockTableReadAndGet(groupKey, context); + if (row == null) { + return null; + } + return row.Properties[_propertyIndex]; + } + + public ICollection EvaluateGetROCollectionEventsInternal(object groupKey, ExprEvaluatorContext context) { + var row = LockTableReadAndGet(groupKey, context); + if (row == null) { + return null; + } + return _optionalEnumEval.EvaluateEventGetROCollectionEvents(row, context); + } + + public EventBean EvaluateGetEventBeanInternal(object groupKey, ExprEvaluatorContext context) { + var row = LockTableReadAndGet(groupKey, context); + if (row == null) { + return null; + } + return _optionalEnumEval.EvaluateEventGetEventBean(row, context); + } + + public ICollection EvaluateGetROCollectionScalarInternal(object groupKey, ExprEvaluatorContext context) { + var row = LockTableReadAndGet(groupKey, context); + if (row == null) { + return null; + } + return _optionalEnumEval.EvaluateEventGetROCollectionScalar(row, context); + } + + public object[] EvaluateTypableSingle(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) { + return null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyGroupByPropMulti.cs b/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyGroupByPropMulti.cs new file mode 100755 index 000000000..1a5fa4603 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyGroupByPropMulti.cs @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.threading; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.table.strategy +{ + public class ExprTableEvalStrategyGroupByPropMulti : ExprTableEvalStrategyGroupByPropBase + { + private readonly ExprEvaluator[] _groupExpr; + + public ExprTableEvalStrategyGroupByPropMulti(TableAndLockProviderGrouped provider, int propertyIndex, ExprEvaluatorEnumerationGivenEvent optionalEnumEval, ExprEvaluator[] groupExpr) + : base(provider, propertyIndex, optionalEnumEval) + { + this._groupExpr = groupExpr; + } + + public override object Evaluate(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext exprEvaluatorContext) + { + object groupKey = ExprTableEvalStrategyGroupByAccessMulti.GetKey(_groupExpr, eventsPerStream, isNewData, exprEvaluatorContext); + return EvaluateInternal(groupKey, exprEvaluatorContext); + } + + public override ICollection EvaluateGetROCollectionEvents(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + object groupKey = ExprTableEvalStrategyGroupByAccessMulti.GetKey(_groupExpr, eventsPerStream, isNewData, context); + return EvaluateGetROCollectionEventsInternal(groupKey, context); + } + + public override EventBean EvaluateGetEventBean(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + object groupKey = ExprTableEvalStrategyGroupByAccessMulti.GetKey(_groupExpr, eventsPerStream, isNewData, context); + return EvaluateGetEventBeanInternal(groupKey, context); + } + + public override ICollection EvaluateGetROCollectionScalar(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + object groupKey = ExprTableEvalStrategyGroupByAccessMulti.GetKey(_groupExpr, eventsPerStream, isNewData, context); + return EvaluateGetROCollectionScalarInternal(groupKey, context); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyGroupByPropSingle.cs b/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyGroupByPropSingle.cs new file mode 100755 index 000000000..a9ceb5405 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyGroupByPropSingle.cs @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.threading; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.table.strategy +{ + public class ExprTableEvalStrategyGroupByPropSingle : ExprTableEvalStrategyGroupByPropBase + { + private readonly ExprEvaluator _groupExpr; + + public ExprTableEvalStrategyGroupByPropSingle(TableAndLockProviderGrouped provider, int propertyIndex, ExprEvaluatorEnumerationGivenEvent optionalEnumEval, ExprEvaluator groupExpr) + : base(@provider, propertyIndex, optionalEnumEval) + { + this._groupExpr = groupExpr; + } + + public override object Evaluate(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext exprEvaluatorContext) + { + object groupKey = _groupExpr.Evaluate(new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext)); + return EvaluateInternal(groupKey, exprEvaluatorContext); + } + + public override ICollection EvaluateGetROCollectionEvents(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + object groupKey = _groupExpr.Evaluate(new EvaluateParams(eventsPerStream, isNewData, context)); + return EvaluateGetROCollectionEventsInternal(groupKey, context); + } + + public override EventBean EvaluateGetEventBean(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + object groupKey = _groupExpr.Evaluate(new EvaluateParams(eventsPerStream, isNewData, context)); + return EvaluateGetEventBeanInternal(groupKey, context); + } + + public override ICollection EvaluateGetROCollectionScalar(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + object groupKey = _groupExpr.Evaluate(new EvaluateParams(eventsPerStream, isNewData, context)); + return EvaluateGetROCollectionScalarInternal(groupKey, context); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyGroupByTopLevelBase.cs b/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyGroupByTopLevelBase.cs new file mode 100755 index 000000000..b4a49fb4f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyGroupByTopLevelBase.cs @@ -0,0 +1,71 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.threading; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.table; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.table.strategy +{ + public abstract class ExprTableEvalStrategyGroupByTopLevelBase + : ExprTableEvalStrategyGroupByBase + , ExprTableAccessEvalStrategy + { + private readonly IDictionary _items; + + protected ExprTableEvalStrategyGroupByTopLevelBase(TableAndLockProviderGrouped provider, IDictionary items) + : base(provider) + { + _items = items; + } + + public abstract object Evaluate(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext exprEvaluatorContext); + public abstract object[] EvaluateTypableSingle(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context); + + protected object EvaluateInternal(object groupKey, EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + var row = LockTableReadAndGet(groupKey, context); + if (row == null) { + return null; + } + return ExprTableEvalStrategyUtil.EvalMap(row, ExprTableEvalStrategyUtil.GetRow(row), _items, eventsPerStream, isNewData, context); + } + + protected object[] EvaluateTypableSingleInternal(object groupKey, EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + var row = LockTableReadAndGet(groupKey, context); + if (row == null) { + return null; + } + return ExprTableEvalStrategyUtil.EvalTypable(row, ExprTableEvalStrategyUtil.GetRow(row), _items, eventsPerStream, isNewData, context); + } + + public ICollection EvaluateGetROCollectionEvents(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + return null; + } + + public EventBean EvaluateGetEventBean(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + return null; + } + + public ICollection EvaluateGetROCollectionScalar(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + return null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyGroupByTopLevelMulti.cs b/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyGroupByTopLevelMulti.cs new file mode 100755 index 000000000..c1b89069c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyGroupByTopLevelMulti.cs @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.threading; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.events; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.epl.table.strategy +{ + public class ExprTableEvalStrategyGroupByTopLevelMulti : ExprTableEvalStrategyGroupByTopLevelBase + { + private readonly ExprEvaluator[] _groupExpr; + + public ExprTableEvalStrategyGroupByTopLevelMulti(TableAndLockProviderGrouped provider, IDictionary items, ExprEvaluator[] groupExpr) + : base(provider, items) + { + _groupExpr = groupExpr; + } + + public override object Evaluate(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext exprEvaluatorContext) + { + object groupKey = ExprTableEvalStrategyGroupByAccessMulti.GetKey(_groupExpr, eventsPerStream, isNewData, exprEvaluatorContext); + return base.EvaluateInternal(groupKey, eventsPerStream, isNewData, exprEvaluatorContext); + } + + public override object[] EvaluateTypableSingle(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + object groupKey = ExprTableEvalStrategyGroupByAccessMulti.GetKey(_groupExpr, eventsPerStream, isNewData, context); + return base.EvaluateTypableSingleInternal(groupKey, eventsPerStream, isNewData, context); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyGroupByTopLevelSingle.cs b/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyGroupByTopLevelSingle.cs new file mode 100755 index 000000000..97805a636 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyGroupByTopLevelSingle.cs @@ -0,0 +1,44 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.threading; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.table.strategy +{ + public class ExprTableEvalStrategyGroupByTopLevelSingle : ExprTableEvalStrategyGroupByTopLevelBase + { + private readonly ExprEvaluator _groupExpr; + + public ExprTableEvalStrategyGroupByTopLevelSingle(TableAndLockProviderGrouped provider, IDictionary items, ExprEvaluator groupExpr) + : base(provider, items) + { + this._groupExpr = groupExpr; + } + + public override object Evaluate(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext exprEvaluatorContext) + { + object groupKey = _groupExpr.Evaluate(new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext)); + return base.EvaluateInternal(groupKey, eventsPerStream, isNewData, exprEvaluatorContext); + } + + public override object[] EvaluateTypableSingle(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + object groupKey = _groupExpr.Evaluate(new EvaluateParams(eventsPerStream, isNewData, context)); + return base.EvaluateTypableSingleInternal(groupKey, eventsPerStream, isNewData, context); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyUngroupedAccess.cs b/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyUngroupedAccess.cs new file mode 100755 index 000000000..43c1fbcff --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyUngroupedAccess.cs @@ -0,0 +1,93 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.threading; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.table; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.table.strategy +{ + public class ExprTableEvalStrategyUngroupedAccess + : ExprTableEvalStrategyUngroupedBase + , ExprTableAccessEvalStrategy + { + private readonly int _slot; + private readonly AggregationAccessor _accessor; + + public ExprTableEvalStrategyUngroupedAccess(TableAndLockProviderUngrouped provider, int slot, AggregationAccessor accessor) + : base(provider) + { + _slot = slot; + _accessor = accessor; + } + + public object Evaluate(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + ObjectArrayBackedEventBean @event = LockTableReadAndGet(context); + if (@event == null) + { + return null; + } + AggregationState aggregationState = GetAndLock(@event, context); + return _accessor.GetValue(aggregationState, new EvaluateParams(eventsPerStream, isNewData, context)); + } + + public object[] EvaluateTypableSingle(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + throw new IllegalStateException("Not typable"); + } + + public ICollection EvaluateGetROCollectionEvents(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + ObjectArrayBackedEventBean @event = LockTableReadAndGet(context); + if (@event == null) + { + return null; + } + AggregationState aggregationState = GetAndLock(@event, context); + return _accessor.GetEnumerableEvents(aggregationState, new EvaluateParams(eventsPerStream, isNewData, context)); + } + + public EventBean EvaluateGetEventBean(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + ObjectArrayBackedEventBean @event = LockTableReadAndGet(context); + if (@event == null) + { + return null; + } + AggregationState aggregationState = GetAndLock(@event, context); + return _accessor.GetEnumerableEvent(aggregationState, new EvaluateParams(eventsPerStream, isNewData, context)); + } + + public ICollection EvaluateGetROCollectionScalar(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + ObjectArrayBackedEventBean @event = LockTableReadAndGet(context); + if (@event == null) + { + return null; + } + AggregationState aggregationState = GetAndLock(@event, context); + return _accessor.GetEnumerableScalar(aggregationState, new EvaluateParams(eventsPerStream, isNewData, context)); + } + + private AggregationState GetAndLock(ObjectArrayBackedEventBean @event, ExprEvaluatorContext exprEvaluatorContext) + { + AggregationRowPair row = ExprTableEvalStrategyUtil.GetRow(@event); + return row.States[_slot]; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyUngroupedBase.cs b/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyUngroupedBase.cs new file mode 100755 index 000000000..90e3b6adb --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyUngroupedBase.cs @@ -0,0 +1,30 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.table.strategy +{ + public abstract class ExprTableEvalStrategyUngroupedBase + { + private readonly TableAndLockProviderUngrouped _provider; + + protected ExprTableEvalStrategyUngroupedBase(TableAndLockProviderUngrouped provider) + { + _provider = provider; + } + + protected ObjectArrayBackedEventBean LockTableReadAndGet(ExprEvaluatorContext context) + { + var pair = _provider.Get(); + ExprTableEvalLockUtil.ObtainLockUnless(pair.Lock, context); + return pair.Ungrouped.EventUngrouped; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyUngroupedMethod.cs b/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyUngroupedMethod.cs new file mode 100755 index 000000000..8dcb59f32 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyUngroupedMethod.cs @@ -0,0 +1,60 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.table; + +namespace com.espertech.esper.epl.table.strategy +{ + public class ExprTableEvalStrategyUngroupedMethod : ExprTableEvalStrategyUngroupedBase , ExprTableAccessEvalStrategy { + + private readonly int _methodOffset; + + public ExprTableEvalStrategyUngroupedMethod(TableAndLockProviderUngrouped provider, int methodOffset) + : base(provider) + { + this._methodOffset = methodOffset; + } + + public object Evaluate(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + var @event = LockTableReadAndGet(context); + if (@event == null) + { + return null; + } + AggregationRowPair row = ExprTableEvalStrategyUtil.GetRow(@event); + return row.Methods[_methodOffset].Value; + } + + public object[] EvaluateTypableSingle(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + throw new IllegalStateException("Not typable"); + } + + public ICollection EvaluateGetROCollectionEvents(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + return null; + } + + public EventBean EvaluateGetEventBean(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + return null; + } + + public ICollection EvaluateGetROCollectionScalar(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + return null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyUngroupedProp.cs b/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyUngroupedProp.cs new file mode 100755 index 000000000..3f734f8bd --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyUngroupedProp.cs @@ -0,0 +1,72 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.table; + +namespace com.espertech.esper.epl.table.strategy +{ + public class ExprTableEvalStrategyUngroupedProp + : ExprTableEvalStrategyUngroupedBase + , ExprTableAccessEvalStrategy + { + private readonly int _propertyIndex; + private readonly ExprEvaluatorEnumerationGivenEvent _optionalEnumEval; + + public ExprTableEvalStrategyUngroupedProp(TableAndLockProviderUngrouped provider, int propertyIndex, ExprEvaluatorEnumerationGivenEvent optionalEnumEval) + : base(provider) + { + _propertyIndex = propertyIndex; + _optionalEnumEval = optionalEnumEval; + } + + public object Evaluate(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + var @event = LockTableReadAndGet(context); + if (@event == null) { + return null; + } + return @event.Properties[_propertyIndex]; + } + + public ICollection EvaluateGetROCollectionEvents(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + var @event = LockTableReadAndGet(context); + if (@event == null) { + return null; + } + return _optionalEnumEval.EvaluateEventGetROCollectionEvents(@event, context); + } + + public EventBean EvaluateGetEventBean(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + var @event = LockTableReadAndGet(context); + if (@event == null) { + return null; + } + return _optionalEnumEval.EvaluateEventGetEventBean(@event, context); + } + + public ICollection EvaluateGetROCollectionScalar(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + var @event = LockTableReadAndGet(context); + if (@event == null) { + return null; + } + return _optionalEnumEval.EvaluateEventGetROCollectionScalar(@event, context); + } + + public object[] EvaluateTypableSingle(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + return null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyUngroupedTopLevel.cs b/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyUngroupedTopLevel.cs new file mode 100755 index 000000000..434d0abea --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyUngroupedTopLevel.cs @@ -0,0 +1,80 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.table; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.table.strategy +{ + public class ExprTableEvalStrategyUngroupedTopLevel + : ExprTableEvalStrategyUngroupedBase + , ExprTableAccessEvalStrategy + { + private readonly IDictionary items; + + public ExprTableEvalStrategyUngroupedTopLevel( + TableAndLockProviderUngrouped provider, + IDictionary items) + : base(provider) + { + this.items = items; + } + + public object Evaluate(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + var @event = LockTableReadAndGet(context); + if (@event == null) + { + return null; + } + var row = ExprTableEvalStrategyUtil.GetRow(@event); + return ExprTableEvalStrategyUtil.EvalMap(@event, row, items, eventsPerStream, isNewData, context); + } + + public object[] EvaluateTypableSingle(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + var @event = LockTableReadAndGet(context); + if (@event == null) + { + return null; + } + var row = ExprTableEvalStrategyUtil.GetRow(@event); + return ExprTableEvalStrategyUtil.EvalTypable(@event, row, items, eventsPerStream, isNewData, context); + } + + public ICollection EvaluateGetROCollectionEvents( + EventBean[] eventsPerStream, + bool isNewData, + ExprEvaluatorContext context) + { + return null; + } + + public EventBean EvaluateGetEventBean(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + return null; + } + + public ICollection EvaluateGetROCollectionScalar( + EventBean[] eventsPerStream, + bool isNewData, + ExprEvaluatorContext context) + { + return null; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyUtil.cs b/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyUtil.cs new file mode 100755 index 000000000..d9e36937e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableEvalStrategyUtil.cs @@ -0,0 +1,115 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.table.strategy +{ + public class ExprTableEvalStrategyUtil + { + public static AggregationRowPair GetRow(ObjectArrayBackedEventBean eventBean) + { + return (AggregationRowPair) eventBean.Properties[0]; + } + + internal static IDictionary EvalMap(ObjectArrayBackedEventBean @event, AggregationRowPair row, IDictionary items, EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext exprEvaluatorContext) + { + var cols = new Dictionary(); + var evaluateParams = new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext); + foreach (var entry in items) + { + if (entry.Value is TableMetadataColumnPlain) { + var plain = (TableMetadataColumnPlain) entry.Value; + cols.Put(entry.Key, @event.Properties[plain.IndexPlain]); + } + else { + var aggcol = (TableMetadataColumnAggregation) entry.Value; + if (!aggcol.Factory.IsAccessAggregation) { + cols.Put(entry.Key, row.Methods[aggcol.MethodOffset].Value); + } + else { + var pair = aggcol.AccessAccessorSlotPair; + var value = pair.Accessor.GetValue(row.States[pair.Slot], evaluateParams); + cols.Put(entry.Key, value); + } + } + } + return cols; + } + + internal static object[] EvalTypable(ObjectArrayBackedEventBean @event, + AggregationRowPair row, + IDictionary items, + EventBean[] eventsPerStream, + bool isNewData, + ExprEvaluatorContext exprEvaluatorContext) + { + var values = new object[items.Count]; + var evaluateParams = new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext); + var count = 0; + foreach (var entry in items) + { + if (entry.Value is TableMetadataColumnPlain) + { + var plain = (TableMetadataColumnPlain) entry.Value; + values[count] = @event.Properties[plain.IndexPlain]; + } + else + { + var aggcol = (TableMetadataColumnAggregation) entry.Value; + if (!aggcol.Factory.IsAccessAggregation) + { + values[count] = row.Methods[aggcol.MethodOffset].Value; + } + else + { + var pair = aggcol.AccessAccessorSlotPair; + values[count] = pair.Accessor.GetValue(row.States[pair.Slot], evaluateParams); + } + } + count++; + } + return values; + } + + internal static object EvalAccessorGetValue(AggregationRowPair row, AggregationAccessorSlotPair pair, EventBean[] eventsPerStream, bool newData, ExprEvaluatorContext context) + { + return pair.Accessor.GetValue(row.States[pair.Slot], new EvaluateParams(eventsPerStream, newData, context)); + } + + internal static ICollection EvalGetROCollectionEvents(AggregationRowPair row, AggregationAccessorSlotPair pair, EventBean[] eventsPerStream, bool newData, ExprEvaluatorContext context) + { + return pair.Accessor.GetEnumerableEvents(row.States[pair.Slot], new EvaluateParams(eventsPerStream, newData, context)); + } + + internal static EventBean EvalGetEventBean(AggregationRowPair row, AggregationAccessorSlotPair pair, EventBean[] eventsPerStream, bool newData, ExprEvaluatorContext context) + { + return pair.Accessor.GetEnumerableEvent(row.States[pair.Slot], new EvaluateParams(eventsPerStream, newData, context)); + } + + internal static ICollection EvalGetROCollectionScalar(AggregationRowPair row, AggregationAccessorSlotPair pair, EventBean[] eventsPerStream, bool newData, ExprEvaluatorContext context) + { + return pair.Accessor.GetEnumerableScalar(row.States[pair.Slot], new EvaluateParams(eventsPerStream, newData, context)); + } + + internal static object EvalMethodGetValue(AggregationRowPair row, int index) + { + return row.Methods[index].Value; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableExprEvaluatorAccess.cs b/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableExprEvaluatorAccess.cs new file mode 100755 index 000000000..543f26871 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableExprEvaluatorAccess.cs @@ -0,0 +1,104 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.table.strategy +{ + public class ExprTableExprEvaluatorAccess + : ExprTableExprEvaluatorBase + , + ExprEvaluator + , + ExprEvaluatorEnumeration + { + private readonly AggregationAccessorSlotPair _accessAccessorSlotPair; + private readonly EventType _eventTypeColl; + + public ExprTableExprEvaluatorAccess( + ExprNode exprNode, + string tableName, + string subpropName, + int streamNum, + Type returnType, + AggregationAccessorSlotPair accessAccessorSlotPair, + EventType eventTypeColl) + : base(exprNode, tableName, subpropName, streamNum, returnType) + { + _accessAccessorSlotPair = accessAccessorSlotPair; + _eventTypeColl = eventTypeColl; + } + + public object Evaluate(EvaluateParams evaluateParams) + { + return Evaluate( + evaluateParams.EventsPerStream, + evaluateParams.IsNewData, + evaluateParams.ExprEvaluatorContext); + } + + public object Evaluate(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QExprTableSubproperty(exprNode, tableName, subpropName); } + + var oa = (ObjectArrayBackedEventBean) eventsPerStream[streamNum]; + var row = ExprTableEvalStrategyUtil.GetRow(oa); + var result = _accessAccessorSlotPair.Accessor.GetValue( + row.States[_accessAccessorSlotPair.Slot], new EvaluateParams(eventsPerStream, isNewData, context)); + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprTableSubproperty(result); } + + return result; + } + + public Type ReturnType + { + get { return returnType; } + } + + public EventType GetEventTypeCollection(EventAdapterService eventAdapterService, int statementId) + { + return _eventTypeColl; + } + + public ICollection EvaluateGetROCollectionEvents(EvaluateParams evaluateParams) + { + var oa = (ObjectArrayBackedEventBean) evaluateParams.EventsPerStream[streamNum]; + var row = ExprTableEvalStrategyUtil.GetRow(oa); + return _accessAccessorSlotPair.Accessor.GetEnumerableEvents( + row.States[_accessAccessorSlotPair.Slot], evaluateParams); + } + + public Type ComponentTypeCollection + { + get { return null; } + } + + public ICollection EvaluateGetROCollectionScalar(EvaluateParams evaluateParams) + { + return null; + } + + public EventType GetEventTypeSingle(EventAdapterService eventAdapterService, int statementId) + { + return null; + } + + public EventBean EvaluateGetEventBean(EvaluateParams evaluateParams) + { + return null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableExprEvaluatorBase.cs b/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableExprEvaluatorBase.cs new file mode 100755 index 000000000..6955fd1eb --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableExprEvaluatorBase.cs @@ -0,0 +1,33 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.table.strategy +{ + public class ExprTableExprEvaluatorBase { + + protected readonly ExprNode exprNode; + protected readonly string tableName; + protected readonly string subpropName; + protected readonly int streamNum; + protected readonly Type returnType; + + public ExprTableExprEvaluatorBase(ExprNode exprNode, string tableName, string subpropName, int streamNum, Type returnType) { + this.exprNode = exprNode; + this.tableName = tableName; + this.subpropName = subpropName; + this.streamNum = streamNum; + this.returnType = returnType; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableExprEvaluatorMethod.cs b/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableExprEvaluatorMethod.cs new file mode 100755 index 000000000..bdd8a9895 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/strategy/ExprTableExprEvaluatorMethod.cs @@ -0,0 +1,61 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.table.strategy +{ + public class ExprTableExprEvaluatorMethod + : ExprTableExprEvaluatorBase + , ExprEvaluator + { + private readonly int methodNum; + + public ExprTableExprEvaluatorMethod(ExprNode exprNode, string tableName, string subpropName, int streamNum, Type returnType, int methodNum) + : base(exprNode, tableName, subpropName, streamNum, returnType) + { + this.methodNum = methodNum; + } + + public object Evaluate(EvaluateParams evaluateParams) + { + return Evaluate( + evaluateParams.EventsPerStream, + evaluateParams.IsNewData, + evaluateParams.ExprEvaluatorContext); + } + + public object Evaluate(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) { + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().QExprTableSubproperty(exprNode, tableName, subpropName); + } + + var oa = (ObjectArrayBackedEventBean) eventsPerStream[streamNum]; + var row = ExprTableEvalStrategyUtil.GetRow(oa); + var result = row.Methods[methodNum].Value; + + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().AExprTableSubproperty(result); + } + return result; + } + + public Type ReturnType + { + get { return returnType; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/table/strategy/TableAndLockGrouped.cs b/NEsper.Core/NEsper.Core/epl/table/strategy/TableAndLockGrouped.cs new file mode 100755 index 000000000..8d3168875 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/strategy/TableAndLockGrouped.cs @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.compat.threading; +using com.espertech.esper.epl.table.mgmt; + +namespace com.espertech.esper.epl.table.strategy +{ + public class TableAndLockGrouped + { + public TableAndLockGrouped(ILockable ilock, TableStateInstanceGrouped grouped) + { + Lock = ilock; + Grouped = grouped; + } + + public ILockable Lock { get; private set; } + + public TableStateInstanceGrouped Grouped { get; private set; } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/table/strategy/TableAndLockProvider.cs b/NEsper.Core/NEsper.Core/epl/table/strategy/TableAndLockProvider.cs new file mode 100755 index 000000000..5ad645775 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/strategy/TableAndLockProvider.cs @@ -0,0 +1,14 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.table.strategy +{ + public interface TableAndLockProvider + { + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/table/strategy/TableAndLockProviderGrouped.cs b/NEsper.Core/NEsper.Core/epl/table/strategy/TableAndLockProviderGrouped.cs new file mode 100755 index 000000000..e12962842 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/strategy/TableAndLockProviderGrouped.cs @@ -0,0 +1,15 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.table.strategy +{ + public interface TableAndLockProviderGrouped : TableAndLockProvider + { + TableAndLockGrouped Get(); + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/table/strategy/TableAndLockProviderGroupedImpl.cs b/NEsper.Core/NEsper.Core/epl/table/strategy/TableAndLockProviderGroupedImpl.cs new file mode 100755 index 000000000..e96843da9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/strategy/TableAndLockProviderGroupedImpl.cs @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.table.strategy +{ + public class TableAndLockProviderGroupedImpl : TableAndLockProviderGrouped + { + private readonly TableAndLockGrouped _pair; + + public TableAndLockProviderGroupedImpl(TableAndLockGrouped pair) + { + _pair = pair; + } + + public TableAndLockGrouped Get() + { + return _pair; + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/table/strategy/TableAndLockProviderUngrouped.cs b/NEsper.Core/NEsper.Core/epl/table/strategy/TableAndLockProviderUngrouped.cs new file mode 100755 index 000000000..133d76f0d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/strategy/TableAndLockProviderUngrouped.cs @@ -0,0 +1,15 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.table.strategy +{ + public interface TableAndLockProviderUngrouped : TableAndLockProvider + { + TableAndLockUngrouped Get(); + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/table/strategy/TableAndLockProviderUngroupedImpl.cs b/NEsper.Core/NEsper.Core/epl/table/strategy/TableAndLockProviderUngroupedImpl.cs new file mode 100755 index 000000000..65bb029e4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/strategy/TableAndLockProviderUngroupedImpl.cs @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.table.strategy +{ + public class TableAndLockProviderUngroupedImpl : TableAndLockProviderUngrouped + { + private readonly TableAndLockUngrouped _pair; + + public TableAndLockProviderUngroupedImpl(TableAndLockUngrouped pair) + { + _pair = pair; + } + + public TableAndLockUngrouped Get() + { + return _pair; + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/table/strategy/TableAndLockUngrouped.cs b/NEsper.Core/NEsper.Core/epl/table/strategy/TableAndLockUngrouped.cs new file mode 100755 index 000000000..68e409108 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/strategy/TableAndLockUngrouped.cs @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.compat.threading; +using com.espertech.esper.epl.table.mgmt; + +namespace com.espertech.esper.epl.table.strategy +{ + public class TableAndLockUngrouped + { + public TableAndLockUngrouped(ILockable ilock, TableStateInstanceUngrouped ungrouped) + { + Lock = ilock; + Ungrouped = ungrouped; + } + + public ILockable Lock { get; private set; } + + public TableStateInstanceUngrouped Ungrouped { get; private set; } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/table/upd/TableUpdateStrategy.cs b/NEsper.Core/NEsper.Core/epl/table/upd/TableUpdateStrategy.cs new file mode 100755 index 000000000..4a9c00f9b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/upd/TableUpdateStrategy.cs @@ -0,0 +1,24 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.table.mgmt; + +namespace com.espertech.esper.epl.table.upd +{ + public interface TableUpdateStrategy + { + void UpdateTable(ICollection events, TableStateInstance instance, EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/table/upd/TableUpdateStrategyFactory.cs b/NEsper.Core/NEsper.Core/epl/table/upd/TableUpdateStrategyFactory.cs new file mode 100755 index 000000000..17773b37a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/upd/TableUpdateStrategyFactory.cs @@ -0,0 +1,84 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.epl.updatehelper; + +namespace com.espertech.esper.epl.table.upd +{ + public class TableUpdateStrategyFactory + { + public static TableUpdateStrategy ValidateGetTableUpdateStrategy(TableMetadata tableMetadata, EventBeanUpdateHelper updateHelper, bool isOnMerge) + { + // determine affected indexes + ISet affectedIndexNames = null; + bool uniqueIndexUpdated = false; + + foreach (var index in tableMetadata.EventTableIndexMetadataRepo.Indexes) + { + foreach (EventBeanUpdateItem updateItem in updateHelper.UpdateItems) + { + if (updateItem.OptionalPropertyName != null) + { + bool match = DetermineUpdatesIndex(updateItem, index.Key); + if (match) + { + if (affectedIndexNames == null) + { + affectedIndexNames = new LinkedHashSet(); + } + affectedIndexNames.Add(index.Value.OptionalIndexName); + uniqueIndexUpdated |= index.Key.IsUnique; + } + } + } + } + + // with affected indexes and with uniqueness : careful updates, may need to rollback + if (affectedIndexNames != null && uniqueIndexUpdated) + { + if (isOnMerge) + { + throw new ExprValidationException("On-merge statements may not update unique keys of tables"); + } + return new TableUpdateStrategyWUniqueConstraint(updateHelper, affectedIndexNames); + } + // with affected indexes and without uniqueness : update indexes without unique key violation and rollback + if (affectedIndexNames != null) + { + return new TableUpdateStrategyIndexNonUnique(updateHelper, affectedIndexNames); + } + // no affected indexes, the fasted means of updating + return new TableUpdateStrategyNonIndex(updateHelper); + } + + private static bool DetermineUpdatesIndex(EventBeanUpdateItem updateItem, IndexMultiKey key) + { + foreach (IndexedPropDesc prop in key.HashIndexedProps) + { + if (prop.IndexPropName.Equals(updateItem.OptionalPropertyName)) + { + return true; + } + } + foreach (IndexedPropDesc prop in key.RangeIndexedProps) + { + if (prop.IndexPropName.Equals(updateItem.OptionalPropertyName)) + { + return true; + } + } + return false; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/table/upd/TableUpdateStrategyIndexNonUnique.cs b/NEsper.Core/NEsper.Core/epl/table/upd/TableUpdateStrategyIndexNonUnique.cs new file mode 100755 index 000000000..924b6eb21 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/upd/TableUpdateStrategyIndexNonUnique.cs @@ -0,0 +1,82 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.@join.table; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.epl.updatehelper; +using com.espertech.esper.events; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.epl.table.upd +{ + public class TableUpdateStrategyIndexNonUnique : TableUpdateStrategy + { + private readonly ISet _affectedIndexNames; + private readonly EventBeanUpdateHelper _updateHelper; + + public TableUpdateStrategyIndexNonUnique(EventBeanUpdateHelper updateHelper, ISet affectedIndexNames) + { + _updateHelper = updateHelper; + _affectedIndexNames = affectedIndexNames; + } + + public void UpdateTable( + ICollection eventsUnsafeIter, + TableStateInstance instance, + EventBean[] eventsPerStream, + ExprEvaluatorContext exprEvaluatorContext) + { + // copy references to array - as it is allowed to pass an index-originating collection + // and those same indexes are being changed now + var events = new EventBean[eventsUnsafeIter.Count]; + int count = 0; + foreach (EventBean @event in eventsUnsafeIter) + { + events[count++] = @event; + } + + // remove from affected indexes + foreach (string affectedIndexName in _affectedIndexNames) + { + EventTable index = instance.GetIndex(affectedIndexName); + index.Remove(events); + } + + // update (no-copy unless original values required) + foreach (EventBean @event in events) + { + eventsPerStream[0] = @event; + var updatedEvent = (ObjectArrayBackedEventBean) @event; + + // if "initial.property" is part of the assignment expressions, provide initial value event + if (_updateHelper.IsRequiresStream2InitialValueEvent) + { + var prev = new object[updatedEvent.Properties.Length]; + Array.Copy(updatedEvent.Properties, 0, prev, 0, prev.Length); + eventsPerStream[2] = new ObjectArrayEventBean(prev, updatedEvent.EventType); + } + + // apply in-place updates + _updateHelper.UpdateNoCopy(updatedEvent, eventsPerStream, exprEvaluatorContext); + instance.HandleRowUpdated(updatedEvent); + } + + // add to affected indexes + foreach (string affectedIndexName in _affectedIndexNames) + { + EventTable index = instance.GetIndex(affectedIndexName); + index.Add(events); + } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/table/upd/TableUpdateStrategyNonIndex.cs b/NEsper.Core/NEsper.Core/epl/table/upd/TableUpdateStrategyNonIndex.cs new file mode 100755 index 000000000..39ae8d009 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/upd/TableUpdateStrategyNonIndex.cs @@ -0,0 +1,56 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.epl.updatehelper; +using com.espertech.esper.events; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.epl.table.upd +{ + public class TableUpdateStrategyNonIndex : TableUpdateStrategy + { + private readonly EventBeanUpdateHelper _updateHelper; + + public TableUpdateStrategyNonIndex(EventBeanUpdateHelper updateHelper) + { + _updateHelper = updateHelper; + } + + public void UpdateTable( + ICollection eventsUnsafeIter, + TableStateInstance instance, + EventBean[] eventsPerStream, + ExprEvaluatorContext exprEvaluatorContext) + { + // update (no-copy unless original values required) + foreach (EventBean @event in eventsUnsafeIter) + { + eventsPerStream[0] = @event; + var updatedEvent = (ObjectArrayBackedEventBean) @event; + + // if "initial.property" is part of the assignment expressions, provide initial value event + if (_updateHelper.IsRequiresStream2InitialValueEvent) + { + var prev = new object[updatedEvent.Properties.Length]; + Array.Copy(updatedEvent.Properties, 0, prev, 0, prev.Length); + eventsPerStream[2] = new ObjectArrayEventBean(prev, updatedEvent.EventType); + } + + // apply in-place updates + _updateHelper.UpdateNoCopy(updatedEvent, eventsPerStream, exprEvaluatorContext); + instance.HandleRowUpdated(updatedEvent); + } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/table/upd/TableUpdateStrategyReceiver.cs b/NEsper.Core/NEsper.Core/epl/table/upd/TableUpdateStrategyReceiver.cs new file mode 100755 index 000000000..b3ac8d1ae --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/upd/TableUpdateStrategyReceiver.cs @@ -0,0 +1,15 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.table.upd +{ + public interface TableUpdateStrategyReceiver + { + void Update(TableUpdateStrategy updateStrategy); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/table/upd/TableUpdateStrategyWUniqueConstraint.cs b/NEsper.Core/NEsper.Core/epl/table/upd/TableUpdateStrategyWUniqueConstraint.cs new file mode 100755 index 000000000..66173ceac --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/table/upd/TableUpdateStrategyWUniqueConstraint.cs @@ -0,0 +1,115 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.@join.table; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.epl.updatehelper; +using com.espertech.esper.events; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.epl.table.upd +{ + public class TableUpdateStrategyWUniqueConstraint : TableUpdateStrategy + { + private readonly ISet _affectedIndexNames; + private readonly EventBeanUpdateHelper _updateHelper; + + public TableUpdateStrategyWUniqueConstraint(EventBeanUpdateHelper updateHelper, ISet affectedIndexNames) + { + _updateHelper = updateHelper; + _affectedIndexNames = affectedIndexNames; + } + + public void UpdateTable( + ICollection eventsUnsafeIter, + TableStateInstance instance, + EventBean[] eventsPerStream, + ExprEvaluatorContext exprEvaluatorContext) + { + // copy references to array - as it is allowed to pass an index-originating collection + // and those same indexes are being changed now + var events = new EventBean[eventsUnsafeIter.Count]; + int count = 0; + foreach (EventBean @event in eventsUnsafeIter) + { + events[count++] = @event; + } + + // remove from affected indexes + foreach (string affectedIndexName in _affectedIndexNames) + { + EventTable index = instance.GetIndex(affectedIndexName); + index.Remove(events); + } + + // copy event data, since we are updating unique keys and must guarantee rollback (no half update) + var previousData = new object[events.Length][]; + + // copy and then update + for (int i = 0; i < events.Length; i++) + { + eventsPerStream[0] = events[i]; + + // copy non-aggregated value references + var updatedEvent = (ObjectArrayBackedEventBean) events[i]; + var prev = new object[updatedEvent.Properties.Length]; + Array.Copy(updatedEvent.Properties, 0, prev, 0, prev.Length); + previousData[i] = prev; + + // if "initial.property" is part of the assignment expressions, provide initial value event + if (_updateHelper.IsRequiresStream2InitialValueEvent) + { + eventsPerStream[2] = new ObjectArrayEventBean(prev, updatedEvent.EventType); + } + + // apply in-place updates + instance.HandleRowUpdateKeyBeforeUpdate(updatedEvent); + _updateHelper.UpdateNoCopy(updatedEvent, eventsPerStream, exprEvaluatorContext); + instance.HandleRowUpdateKeyAfterUpdate(updatedEvent); + } + + // add to affected indexes + try + { + foreach (string affectedIndexName in _affectedIndexNames) + { + EventTable index = instance.GetIndex(affectedIndexName); + index.Add(events); + } + } + catch (EPException ex) + { + // rollback + // remove updated events + foreach (string affectedIndexName in _affectedIndexNames) + { + EventTable index = instance.GetIndex(affectedIndexName); + index.Remove(events); + } + // rollback change to events + for (int i = 0; i < events.Length; i++) + { + var oa = (ObjectArrayBackedEventBean) events[i]; + oa.PropertyValues = previousData[i]; + } + // add old events + foreach (string affectedIndexName in _affectedIndexNames) + { + EventTable index = instance.GetIndex(affectedIndexName); + index.Add(events); + } + throw ex; + } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/updatehelper/EventBeanUpdateHelper.cs b/NEsper.Core/NEsper.Core/epl/updatehelper/EventBeanUpdateHelper.cs new file mode 100755 index 000000000..529fae471 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/updatehelper/EventBeanUpdateHelper.cs @@ -0,0 +1,102 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.updatehelper +{ + public class EventBeanUpdateHelper { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly EventBeanCopyMethod _copyMethod; + private readonly EventBeanUpdateItem[] _updateItems; + + public EventBeanUpdateHelper(EventBeanCopyMethod copyMethod, EventBeanUpdateItem[] updateItems) + { + _copyMethod = copyMethod; + _updateItems = updateItems; + } + + public EventBean UpdateWCopy(EventBean matchingEvent, EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext) + { + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().QInfraUpdate(matchingEvent, eventsPerStream, _updateItems.Length, true); + } + + EventBean copy = _copyMethod.Copy(matchingEvent); + eventsPerStream[0] = copy; + eventsPerStream[2] = matchingEvent; // initial value + + UpdateInternal(eventsPerStream, exprEvaluatorContext, copy); + + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().AInfraUpdate(copy); + } + return copy; + } + + public void UpdateNoCopy(EventBean matchingEvent, EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext) { + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().QInfraUpdate(matchingEvent, eventsPerStream, _updateItems.Length, false); + } + + UpdateInternal(eventsPerStream, exprEvaluatorContext, matchingEvent); + + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().AInfraUpdate(matchingEvent); + } + } + + public EventBeanUpdateItem[] UpdateItems + { + get { return _updateItems; } + } + + public bool IsRequiresStream2InitialValueEvent + { + get { return _copyMethod != null; } + } + + private void UpdateInternal(EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext, EventBean target) + { + var evaluateParams = new EvaluateParams(eventsPerStream, true, exprEvaluatorContext); + for (int i = 0; i < _updateItems.Length; i++) + { + EventBeanUpdateItem updateItem = _updateItems[i]; + + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().QInfraUpdateRHSExpr(i, updateItem); + } + Object result = updateItem.Expression.Evaluate(evaluateParams); + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().AInfraUpdateRHSExpr(result); + } + + if (updateItem.OptionalWriter != null) { + if (result == null && updateItem.IsNotNullableField) { + Log.Warn("Null value returned by expression for assignment to property '" + updateItem.OptionalPropertyName + " is ignored as the property type is not nullable for expression"); + continue; + } + + if (updateItem.OptionalWidener != null) { + result = updateItem.OptionalWidener.Invoke(result); + } + updateItem.OptionalWriter.Write(result, target); + } + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/updatehelper/EventBeanUpdateHelperFactory.cs b/NEsper.Core/NEsper.Core/epl/updatehelper/EventBeanUpdateHelperFactory.cs new file mode 100755 index 000000000..8e52f1950 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/updatehelper/EventBeanUpdateHelperFactory.cs @@ -0,0 +1,202 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.ops; +using com.espertech.esper.epl.expression.visitor; +using com.espertech.esper.epl.spec; +using com.espertech.esper.events; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.updatehelper +{ + public class EventBeanUpdateHelperFactory + { + public static EventBeanUpdateHelper Make( + string updatedWindowOrTableName, + EventTypeSPI eventTypeSPI, + IList assignments, + string updatedAlias, + EventType optionalTriggeringEventType, + bool isCopyOnWrite, + string statementName, + string engineURI, + EventAdapterService eventAdapterService) + { + var updateItems = new List(); + var properties = new List(); + + var typeWidenerCustomizer = eventAdapterService.GetTypeWidenerCustomizer(eventTypeSPI); + for (var i = 0; i < assignments.Count; i++) + { + var assignment = assignments[i]; + EventBeanUpdateItem updateItem; + + // determine whether this is a "property=value" assignment, we use property setters in this case + var possibleAssignment = + ExprNodeUtility.CheckGetAssignmentToProp(assignment.Expression); + + // handle assignment "property = value" + if (possibleAssignment != null) + { + + var propertyName = possibleAssignment.First; + var writableProperty = eventTypeSPI.GetWritableProperty(propertyName); + + // check assignment to indexed or mapped property + if (writableProperty == null) + { + var nameWriteablePair = + CheckIndexedOrMappedProp( + possibleAssignment.First, updatedWindowOrTableName, updatedAlias, eventTypeSPI); + propertyName = nameWriteablePair.First; + writableProperty = nameWriteablePair.Second; + } + + var evaluator = possibleAssignment.Second.ExprEvaluator; + var writers = eventTypeSPI.GetWriter(propertyName); + var notNullableField = writableProperty.PropertyType.IsPrimitive; + + properties.Add(propertyName); + var widener = + TypeWidenerFactory.GetCheckPropertyAssignType( + ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(possibleAssignment.Second), + possibleAssignment.Second.ExprEvaluator.ReturnType, + writableProperty.PropertyType, propertyName, false, typeWidenerCustomizer, statementName, + engineURI); + + // check event type assignment + if (optionalTriggeringEventType != null && possibleAssignment.Second is ExprIdentNode) + { + var node = (ExprIdentNode) possibleAssignment.Second; + var fragmentRHS = + optionalTriggeringEventType.GetFragmentType(node.ResolvedPropertyName); + var fragmentLHS = eventTypeSPI.GetFragmentType(possibleAssignment.First); + if (fragmentRHS != null && fragmentLHS != null && + !EventTypeUtility.IsTypeOrSubTypeOf(fragmentRHS.FragmentType, fragmentLHS.FragmentType)) + { + throw new ExprValidationException( + "Invalid assignment to property '" + + possibleAssignment.First + "' event type '" + fragmentLHS.FragmentType.Name + + "' from event type '" + fragmentRHS.FragmentType.Name + "'"); + } + } + + updateItem = new EventBeanUpdateItem(evaluator, propertyName, writers, notNullableField, widener); + } + else + { + // handle non-assignment, i.e. UDF or other expression + var evaluator = assignment.Expression.ExprEvaluator; + updateItem = new EventBeanUpdateItem(evaluator, null, null, false, null); + } + + updateItems.Add(updateItem); + } + + + // copy-on-write is the default event semantics as events are immutable + EventBeanCopyMethod copyMethod; + if (isCopyOnWrite) + { + // obtain copy method + var propertiesUniqueList = new List(new HashSet(properties)); + var propertiesArray = propertiesUniqueList.ToArray(); + copyMethod = eventTypeSPI.GetCopyMethod(propertiesArray); + if (copyMethod == null) + { + throw new ExprValidationException("Event type does not support event bean copy"); + } + } + else + { + // for in-place update, determine assignment expressions to use "initial" to access prior-change values + // the copy-method is optional + copyMethod = null; + var propertiesInitialValue = DeterminePropertiesInitialValue(assignments); + if (!propertiesInitialValue.IsEmpty()) + { + var propertiesInitialValueArray = + propertiesInitialValue.ToArray(); + copyMethod = eventTypeSPI.GetCopyMethod(propertiesInitialValueArray); + } + } + + var updateItemsArray = updateItems.ToArray(); + return new EventBeanUpdateHelper(copyMethod, updateItemsArray); + } + + private static ISet DeterminePropertiesInitialValue(IEnumerable assignments) + { + var props = new HashSet(); + var visitor = new ExprNodeIdentifierCollectVisitor(); + foreach (var assignment in assignments) + { + if (assignment.Expression is ExprEqualsNode) + { + assignment.Expression.ChildNodes[1].Accept(visitor); + } + else + { + assignment.Expression.Accept(visitor); + } + foreach (var node in visitor.ExprProperties) + { + if (node.StreamId == 2) + { + props.Add(node.ResolvedPropertyName); + } + } + } + return props; + } + + private static Pair CheckIndexedOrMappedProp( + string propertyName, + string updatedWindowOrTableName, + string namedWindowAlias, + EventTypeSPI eventTypeSPI) + { + + EventPropertyDescriptor writableProperty = null; + + var indexDot = propertyName.IndexOf('.'); + if ((namedWindowAlias != null) && (indexDot != -1)) + { + var prefix = propertyName.Substring(0, indexDot); + var name = propertyName.Substring(indexDot + 1); + if (prefix.Equals(namedWindowAlias)) + { + writableProperty = eventTypeSPI.GetWritableProperty(name); + propertyName = name; + } + } + if (writableProperty == null && indexDot != -1) + { + var prefix = propertyName.Substring(0, indexDot); + var name = propertyName.Substring(indexDot + 1); + if (prefix.Equals(updatedWindowOrTableName)) + { + writableProperty = eventTypeSPI.GetWritableProperty(name); + propertyName = name; + } + } + if (writableProperty == null) + { + throw new ExprValidationException("Property '" + propertyName + "' is not available for write access"); + } + return new Pair(propertyName, writableProperty); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/updatehelper/EventBeanUpdateItem.cs b/NEsper.Core/NEsper.Core/epl/updatehelper/EventBeanUpdateItem.cs new file mode 100755 index 000000000..54629a524 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/updatehelper/EventBeanUpdateItem.cs @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.updatehelper +{ + public class EventBeanUpdateItem + { + public EventBeanUpdateItem( + ExprEvaluator expression, + string optinalPropertyName, + EventPropertyWriter optionalWriter, + bool notNullableField, + TypeWidener optionalWidener) + { + Expression = expression; + OptionalPropertyName = optinalPropertyName; + OptionalWriter = optionalWriter; + IsNotNullableField = notNullableField; + OptionalWidener = optionalWidener; + } + + public ExprEvaluator Expression { get; private set; } + + public string OptionalPropertyName { get; private set; } + + public EventPropertyWriter OptionalWriter { get; private set; } + + public bool IsNotNullableField { get; private set; } + + public TypeWidener OptionalWidener { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/util/EPLValidationUtil.cs b/NEsper.Core/NEsper.Core/epl/util/EPLValidationUtil.cs new file mode 100755 index 000000000..5733f7317 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/util/EPLValidationUtil.cs @@ -0,0 +1,46 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.table.mgmt; + +namespace com.espertech.esper.epl.util +{ + public class EPLValidationUtil { + + public static void ValidateTableExists(TableService tableService, string name) { + if (tableService.GetTableMetadata(name) != null) { + throw new ExprValidationException("A table by name '" + name + "' already exists"); + } + } + + public static void ValidateContextName(bool table, string tableOrNamedWindowName, string tableOrNamedWindowContextName, string optionalContextName, bool mustMatchContext) + + { + if (tableOrNamedWindowContextName != null) { + if (optionalContextName == null || !optionalContextName.Equals(tableOrNamedWindowContextName)) { + throw GetCtxMessage(table, tableOrNamedWindowName, tableOrNamedWindowContextName); + } + } + else { + if (mustMatchContext && optionalContextName != null) { + throw GetCtxMessage(table, tableOrNamedWindowName, tableOrNamedWindowContextName); + } + } + } + + private static ExprValidationException GetCtxMessage(bool table, string tableOrNamedWindowName, string tableOrNamedWindowContextName) { + string prefix = table ? "Table": "Named window"; + return new ExprValidationException(prefix + " by name '" + tableOrNamedWindowName + "' has been declared for context '" + tableOrNamedWindowContextName + "' and can only be used within the same context"); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/variable/CreateVariableView.cs b/NEsper.Core/NEsper.Core/epl/variable/CreateVariableView.cs new file mode 100755 index 000000000..bc833dbad --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/variable/CreateVariableView.cs @@ -0,0 +1,97 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.service; +using com.espertech.esper.events; +using com.espertech.esper.view; + +namespace com.espertech.esper.epl.variable +{ + /// View for handling create-variable syntax. + /// The view posts to listeners when a variable changes, if it has subviews. + /// The view returns the current variable value for the iterator. + /// The event type for such posted events is a single field Map with the variable value. + /// + public class CreateVariableView + : ViewSupport + { + private readonly EventAdapterService _eventAdapterService; + private readonly VariableReader _reader; + private readonly EventType _eventType; + private readonly String _variableName; + private readonly StatementResultService _statementResultService; + + /// + /// Ctor. + /// + /// The statement id. + /// for creating events + /// for looking up variables + /// is the name of the variable to create + /// for coordinating on whether insert and remove stream events should be posted + /// + public CreateVariableView(int statementId, EventAdapterService eventAdapterService, VariableService variableService, string variableName, StatementResultService statementResultService, int agentInstanceId) + { + _eventAdapterService = eventAdapterService; + _variableName = variableName; + _statementResultService = statementResultService; + _reader = variableService.GetReader(variableName, agentInstanceId); + _eventType = GetEventType(statementId, eventAdapterService, _reader.VariableMetaData); + } + + public static EventType GetEventType(int statementId, EventAdapterService eventAdapterService, VariableMetaData variableMetaData) + { + var variableTypes = new Dictionary(); + variableTypes.Put(variableMetaData.VariableName, variableMetaData.VariableType); + String outputEventTypeName = statementId + "_outcreatevar"; + return eventAdapterService.CreateAnonymousMapType(outputEventTypeName, variableTypes, true); + } + + public void Update(Object newValue, Object oldValue) + { + if (_statementResultService.IsMakeNatural || _statementResultService.IsMakeSynthetic) + { + IDictionary valuesOld = new Dictionary(); + valuesOld.Put(_variableName, oldValue); + EventBean eventOld = _eventAdapterService.AdapterForTypedMap(valuesOld, _eventType); + + IDictionary valuesNew = new Dictionary(); + valuesNew.Put(_variableName, newValue); + EventBean eventNew = _eventAdapterService.AdapterForTypedMap(valuesNew, _eventType); + + UpdateChildren(new[] {eventNew}, new[] {eventOld}); + } + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + throw new UnsupportedOperationException("Update not supported"); + } + + public override EventType EventType + { + get { return _eventType; } + } + + public override IEnumerator GetEnumerator() + { + Object value = _reader.Value; + IDictionary values = new Dictionary(); + values.Put(_variableName, value); + EventBean theEvent = _eventAdapterService.AdapterForTypedMap(values, _eventType); + yield return theEvent; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/variable/CurrentValue.cs b/NEsper.Core/NEsper.Core/epl/variable/CurrentValue.cs new file mode 100755 index 000000000..0e6c46d75 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/variable/CurrentValue.cs @@ -0,0 +1,42 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.variable +{ + /// + /// A holder for versioned values that holds a current version-value and a prior version-value pair. + /// + public class CurrentValue + { + private readonly VersionedValue currentVersion; + private readonly VersionedValue priorVersion; + + /// Ctor. + /// current version and value + /// prior version and value + public CurrentValue(VersionedValue currentVersion, VersionedValue priorVersion) + { + this.currentVersion = currentVersion; + this.priorVersion = priorVersion; + } + + /// Returns the current version. + /// current version + public VersionedValue CurrentVersion + { + get { return currentVersion; } + } + + /// Returns the prior version. + /// prior version + public VersionedValue PriorVersion + { + get { return priorVersion; } + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/epl/variable/OnSetVariableView.cs b/NEsper.Core/NEsper.Core/epl/variable/OnSetVariableView.cs new file mode 100755 index 000000000..6cbcf7f69 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/variable/OnSetVariableView.cs @@ -0,0 +1,76 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.view; + +namespace com.espertech.esper.epl.variable +{ + /// + /// A view that handles the setting of variables upon receipt of a triggering event. + /// + /// Variables are updated atomically and thus a separate commit actually updates the new + /// variable values, or a rollback if an exception occured during validation. + /// + public class OnSetVariableView : ViewSupport + { + private readonly OnSetVariableViewFactory _factory; + private readonly ExprEvaluatorContext _exprEvaluatorContext; + + private readonly EventBean[] _eventsPerStream = new EventBean[1]; + + public OnSetVariableView(OnSetVariableViewFactory factory, ExprEvaluatorContext exprEvaluatorContext) + { + _factory = factory; + _exprEvaluatorContext = exprEvaluatorContext; + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + if ((newData == null) || (newData.Length == 0)) + { + return; + } + + IDictionary values = null; + var produceOutputEvents = (_factory.StatementResultService.IsMakeNatural || _factory.StatementResultService.IsMakeSynthetic); + + if (produceOutputEvents) + { + values = new Dictionary(); + } + + _eventsPerStream[0] = newData[newData.Length - 1]; + _factory.VariableReadWritePackage.WriteVariables(_factory.VariableService, _eventsPerStream, values, _exprEvaluatorContext); + + if (values != null) + { + EventBean[] newDataOut = new EventBean[1]; + newDataOut[0] = _factory.EventAdapterService.AdapterForTypedMap(values, _factory.EventType); + this.UpdateChildren(newDataOut, null); + } + } + + public override EventType EventType + { + get { return _factory.EventType; } + } + + public override IEnumerator GetEnumerator() + { + IDictionary values = _factory.VariableReadWritePackage.Iterate( + _exprEvaluatorContext.AgentInstanceId); + EventBean theEvent = _factory.EventAdapterService.AdapterForTypedMap(values, _factory.EventType); + yield return theEvent; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/variable/OnSetVariableViewFactory.cs b/NEsper.Core/NEsper.Core/epl/variable/OnSetVariableViewFactory.cs new file mode 100755 index 000000000..914f67c0d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/variable/OnSetVariableViewFactory.cs @@ -0,0 +1,74 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.variable +{ + /// + /// A factory for a view that handles the setting of variables upon receipt of a triggering event. + /// + public class OnSetVariableViewFactory + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// Ctor. + /// + /// specification for the on-set statement + /// for creating statements + /// for setting variables + /// for coordinating on whether insert and remove stream events should be posted + /// context for expression evalauation + /// statement id + /// + /// if the assignment expressions are + /// invalid + /// + public OnSetVariableViewFactory( + int statementId, + OnTriggerSetDesc desc, + EventAdapterService eventAdapterService, + VariableService variableService, + StatementResultService statementResultService, + ExprEvaluatorContext exprEvaluatorContext) + { + EventAdapterService = eventAdapterService; + VariableService = variableService; + StatementResultService = statementResultService; + + VariableReadWritePackage = new VariableReadWritePackage( + desc.Assignments, variableService, eventAdapterService); + string outputEventTypeName = statementId + "_outsetvar"; + EventType = eventAdapterService.CreateAnonymousMapType( + outputEventTypeName, VariableReadWritePackage.VariableTypes, true); + } + + public EventType EventType { get; private set; } + + public EventAdapterService EventAdapterService { get; private set; } + + public VariableService VariableService { get; private set; } + + public VariableReadWritePackage VariableReadWritePackage { get; private set; } + + public StatementResultService StatementResultService { get; private set; } + + public OnSetVariableView Instantiate(ExprEvaluatorContext exprEvaluatorContext) + { + return new OnSetVariableView(this, exprEvaluatorContext); + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/variable/VariableChangeCallback.cs b/NEsper.Core/NEsper.Core/epl/variable/VariableChangeCallback.cs new file mode 100755 index 000000000..3b3b254d8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/variable/VariableChangeCallback.cs @@ -0,0 +1,19 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.variable +{ + /// + /// A callback interface for indicating a change in variable value. + /// new value + /// old value + /// + public delegate void VariableChangeCallback(Object newValue, Object oldValue); +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/epl/variable/VariableDeclarationException.cs b/NEsper.Core/NEsper.Core/epl/variable/VariableDeclarationException.cs new file mode 100755 index 000000000..30f09ea01 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/variable/VariableDeclarationException.cs @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.variable +{ + /// + /// Exception indicating a problem in a variable declaration. + /// + public class VariableDeclarationException : Exception + { + /// Ctor. + /// the exception message. + public VariableDeclarationException(String msg) + : base(msg) + { + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/epl/variable/VariableExistsException.cs b/NEsper.Core/NEsper.Core/epl/variable/VariableExistsException.cs new file mode 100755 index 000000000..cfebb236b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/variable/VariableExistsException.cs @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.variable +{ + /// + /// Exception indicating a a variable already exists. + /// + public class VariableExistsException : VariableDeclarationException + { + /// Ctor. + /// the exception message. + public VariableExistsException(String msg) + : base(msg) + { + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/epl/variable/VariableMetaData.cs b/NEsper.Core/NEsper.Core/epl/variable/VariableMetaData.cs new file mode 100755 index 000000000..cecca9f5f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/variable/VariableMetaData.cs @@ -0,0 +1,60 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.epl.variable +{ + [Serializable] + public class VariableMetaData + { + public VariableMetaData( + String variableName, + String contextPartitionName, + int variableNumber, + Type type, + EventType eventType, + bool constant, + VariableStateFactory variableStateFactory) + { + VariableName = variableName; + ContextPartitionName = contextPartitionName; + VariableNumber = variableNumber; + VariableType = type; + EventType = eventType; + IsConstant = constant; + VariableStateFactory = variableStateFactory; + } + + /// Returns the variable name. + /// variable name + public string VariableName { get; private set; } + + public string ContextPartitionName { get; private set; } + + /// Returns the variable number. + /// variable index number + public int VariableNumber { get; private set; } + + /// Returns the type of the variable. + /// type + public Type VariableType { get; private set; } + + /// + /// Returns the event type if the variable hold Event(s). + /// + /// type + public EventType EventType { get; private set; } + + public bool IsConstant { get; private set; } + + public VariableStateFactory VariableStateFactory { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/variable/VariableNotFoundException.cs b/NEsper.Core/NEsper.Core/epl/variable/VariableNotFoundException.cs new file mode 100755 index 000000000..ac79ce5df --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/variable/VariableNotFoundException.cs @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.variable +{ + /// + /// Exception indicating a variable does not exists. + /// + public class VariableNotFoundException : VariableDeclarationException + { + /// Ctor. + /// the exception message. + public VariableNotFoundException(String msg) + : base(msg) + { + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/epl/variable/VariableReadWritePackage.cs b/NEsper.Core/NEsper.Core/epl/variable/VariableReadWritePackage.cs new file mode 100755 index 000000000..2d9614891 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/variable/VariableReadWritePackage.cs @@ -0,0 +1,390 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.start; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; +using com.espertech.esper.events; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.variable +{ + /// + /// A convenience class for dealing with reading and updating multiple variable values. + /// + public class VariableReadWritePackage + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly VariableTriggerSetDesc[] _assignments; + private readonly VariableMetaData[] _metaData; + private readonly VariableReader[] _readersForGlobalVars; + private readonly bool[] _mustCoerce; + private readonly WriteDesc[] _writers; + private readonly IDictionary _copyMethods; + + private readonly EventAdapterService _eventAdapterService; + private readonly IDictionary _variableTypes; + private readonly VariableService _variableService; + + /// + /// Ctor. + /// + /// the list of variable assignments + /// variable service + /// event adapters + /// when variables cannot be found + public VariableReadWritePackage(IList assignments, VariableService variableService, EventAdapterService eventAdapterService) + { + _metaData = new VariableMetaData[assignments.Count]; + _readersForGlobalVars = new VariableReader[assignments.Count]; + _mustCoerce = new bool[assignments.Count]; + _writers = new WriteDesc[assignments.Count]; + + _variableTypes = new Dictionary(); + _eventAdapterService = eventAdapterService; + _variableService = variableService; + + IDictionary eventTypeWrittenProps = new Dictionary(); + var count = 0; + IList assignmentList = new List(); + + foreach (var expressionWithAssignments in assignments) + { + var possibleVariableAssignment = ExprNodeUtility.CheckGetAssignmentToVariableOrProp(expressionWithAssignments.Expression); + if (possibleVariableAssignment == null) + { + throw new ExprValidationException("Missing variable assignment expression in assignment number " + count); + } + assignmentList.Add(new VariableTriggerSetDesc(possibleVariableAssignment.First, possibleVariableAssignment.Second.ExprEvaluator)); + + var fullVariableName = possibleVariableAssignment.First; + var variableName = fullVariableName; + String subPropertyName = null; + + var indexOfDot = variableName.IndexOf('.'); + if (indexOfDot != -1) + { + subPropertyName = variableName.Substring(indexOfDot + 1); + variableName = variableName.Substring(0, indexOfDot); + } + + VariableMetaData variableMetadata = variableService.GetVariableMetaData(variableName); + _metaData[count] = variableMetadata; + if (variableMetadata == null) + { + throw new ExprValidationException("Variable by name '" + variableName + "' has not been created or configured"); + } + if (variableMetadata.IsConstant) + { + throw new ExprValidationException("Variable by name '" + variableName + "' is declared constant and may not be set"); + } + if (variableMetadata.ContextPartitionName == null) + { + _readersForGlobalVars[count] = variableService.GetReader(variableName, EPStatementStartMethodConst.DEFAULT_AGENT_INSTANCE_ID); + } + + if (subPropertyName != null) + { + if (variableMetadata.EventType == null) + { + throw new ExprValidationException("Variable by name '" + variableName + "' does not have a property named '" + subPropertyName + "'"); + } + var type = variableMetadata.EventType; + if (!(type is EventTypeSPI)) + { + throw new ExprValidationException("Variable by name '" + variableName + "' event type '" + type.Name + "' not writable"); + } + var spi = (EventTypeSPI)type; + var writer = spi.GetWriter(subPropertyName); + var getter = spi.GetGetter(subPropertyName); + if (writer == null) + { + throw new ExprValidationException("Variable by name '" + variableName + "' the property '" + subPropertyName + "' is not writable"); + } + + _variableTypes.Put(fullVariableName, spi.GetPropertyType(subPropertyName)); + var writtenProps = eventTypeWrittenProps.Get(spi); + if (writtenProps == null) + { + writtenProps = new CopyMethodDesc(variableName, new List()); + eventTypeWrittenProps.Put(spi, writtenProps); + } + writtenProps.PropertiesCopied.Add(subPropertyName); + + _writers[count] = new WriteDesc(spi, variableName, writer, getter); + } + else + { + + // determine types + var expressionType = possibleVariableAssignment.Second.ExprEvaluator.ReturnType; + + if (variableMetadata.EventType != null) + { + if ((expressionType != null) && (!TypeHelper.IsSubclassOrImplementsInterface(expressionType, variableMetadata.EventType.UnderlyingType))) + { + throw new VariableValueException("Variable '" + variableName + + "' of declared event type '" + variableMetadata.EventType.Name + "' underlying type '" + variableMetadata.EventType.UnderlyingType.FullName + + "' cannot be assigned a value of type '" + expressionType.FullName + "'"); + } + _variableTypes.Put(variableName, variableMetadata.EventType.UnderlyingType); + } + else + { + + var variableType = variableMetadata.VariableType; + _variableTypes.Put(variableName, variableType); + + // determine if the expression type can be assigned + if (variableType != typeof(object)) + { + if ((TypeHelper.GetBoxedType(expressionType) != variableType) && + (expressionType != null)) + { + if ((!TypeHelper.IsNumeric(variableType)) || + (!TypeHelper.IsNumeric(expressionType))) + { + throw new ExprValidationException(VariableServiceUtil.GetAssigmentExMessage(variableName, variableType, expressionType)); + } + + if (!(TypeHelper.CanCoerce(expressionType, variableType))) + { + throw new ExprValidationException(VariableServiceUtil.GetAssigmentExMessage(variableName, variableType, expressionType)); + } + + _mustCoerce[count] = true; + } + } + } + } + + count++; + } + + _assignments = assignmentList.ToArray(); + + if (eventTypeWrittenProps.IsEmpty()) + { + _copyMethods = new Dictionary(); + return; + } + + _copyMethods = new Dictionary(); + foreach (var entry in eventTypeWrittenProps) + { + var propsWritten = entry.Value.PropertiesCopied; + var props = propsWritten.ToArray(); + var copyMethod = entry.Key.GetCopyMethod(props); + if (copyMethod == null) + { + throw new ExprValidationException("Variable '" + entry.Value.VariableName + + "' of declared type " + entry.Key.UnderlyingType.GetTypeNameFullyQualPretty() + + "' cannot be assigned to"); + } + _copyMethods.Put(entry.Key, copyMethod); + } + } + + /// + /// Write new variable values and commit, evaluating assignment expressions using the given + /// events per stream. + /// Populates an optional map of new values if a non-null map is passed. + /// + /// variable service + /// events per stream + /// null or an empty map to populate with written values + /// expression evaluation context + public void WriteVariables(VariableService variableService, + EventBean[] eventsPerStream, + IDictionary valuesWritten, + ExprEvaluatorContext exprEvaluatorContext) + { + ISet variablesBeansCopied = null; + if (!_copyMethods.IsEmpty()) + { + variablesBeansCopied = new HashSet(); + } + + // We obtain a write lock global to the variable space + // Since expressions can contain variables themselves, these need to be unchangeable for the duration + // as there could be multiple statements that do "var1 = var1 + 1". + using (variableService.ReadWriteLock.AcquireWriteLock()) + { + try + { + variableService.SetLocalVersion(); + + var count = 0; + foreach (var assignment in _assignments) + { + var variableMetaData = _metaData[count]; + int agentInstanceId = variableMetaData.ContextPartitionName == null ? EPStatementStartMethodConst.DEFAULT_AGENT_INSTANCE_ID : exprEvaluatorContext.AgentInstanceId; + var value = assignment.Evaluator.Evaluate( + new EvaluateParams(eventsPerStream, true, exprEvaluatorContext)); + + if (_writers[count] != null) + { + var reader = variableService.GetReader( + variableMetaData.VariableName, exprEvaluatorContext.AgentInstanceId); + var current = (EventBean)reader.Value; + if (current == null) + { + value = null; + } + else + { + var writeDesc = _writers[count]; + var copy = variablesBeansCopied.Add(writeDesc.VariableName); + if (copy) + { + var copied = _copyMethods.Get(writeDesc.Type).Copy(current); + current = copied; + } + variableService.Write(variableMetaData.VariableNumber, agentInstanceId, current); + writeDesc.Writer.Write(value, current); + } + } + else if (variableMetaData.EventType != null) + { + var eventBean = _eventAdapterService.AdapterForType(value, variableMetaData.EventType); + variableService.Write(variableMetaData.VariableNumber, agentInstanceId, eventBean); + } + else + { + if ((value != null) && (_mustCoerce[count])) + { + value = CoercerFactory.CoerceBoxed(value, variableMetaData.VariableType); + } + variableService.Write(variableMetaData.VariableNumber, agentInstanceId, value); + } + + count++; + + if (valuesWritten != null) + { + valuesWritten.Put(assignment.VariableName, value); + } + } + + variableService.Commit(); + } + catch (Exception ex) + { + Log.Error("Error evaluating on-set variable expressions: " + ex.Message, ex); + variableService.Rollback(); + } + } + } + + /// + /// Returns a map of variable names and type of variable. + /// + /// variables + public IDictionary VariableTypes + { + get { return _variableTypes; } + } + + /// + /// Iterate returning all values. + /// + /// map of values + public IDictionary Iterate(int agentInstanceId) + { + IDictionary values = new Dictionary(); + + var count = 0; + foreach (var assignment in _assignments) + { + Object value; + if (_readersForGlobalVars[count] == null) + { + var reader = _variableService.GetReader(assignment.VariableName, agentInstanceId); + if (reader == null) + { + continue; + } + value = reader.Value; + } + else + { + value = _readersForGlobalVars[count].Value; + } + + if (value == null) + { + values.Put(assignment.VariableName, null); + } + else if (_writers[count] != null) + { + var current = (EventBean)value; + values.Put(assignment.VariableName, _writers[count].Getter.Get(current)); + } + else if (value is EventBean) + { + values.Put(assignment.VariableName, ((EventBean)value).Underlying); + } + else + { + values.Put(assignment.VariableName, value); + } + count++; + } + return values; + } + + private class CopyMethodDesc + { + internal readonly String VariableName; + internal readonly IList PropertiesCopied; + + public CopyMethodDesc(String variableName, IList propertiesCopied) + { + VariableName = variableName; + PropertiesCopied = propertiesCopied; + } + } + + private class WriteDesc + { + public WriteDesc(EventTypeSPI type, String variableName, EventPropertyWriter writer, EventPropertyGetter getter) + { + Type = type; + VariableName = variableName; + Writer = writer; + Getter = getter; + } + + internal readonly EventTypeSPI Type; + internal readonly string VariableName; + internal readonly EventPropertyWriter Writer; + internal readonly EventPropertyGetter Getter; + } + + private class VariableTriggerSetDesc + { + internal readonly String VariableName; + internal readonly ExprEvaluator Evaluator; + + public VariableTriggerSetDesc(String variableName, ExprEvaluator evaluator) + { + VariableName = variableName; + Evaluator = evaluator; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/variable/VariableReader.cs b/NEsper.Core/NEsper.Core/epl/variable/VariableReader.cs new file mode 100755 index 000000000..bbf47cea6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/variable/VariableReader.cs @@ -0,0 +1,92 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.epl.variable +{ + /// + /// Reads and writes variable values. + /// Works closely with in determining the version to read. + /// + public class VariableReader + { + private readonly VariableMetaData _variableMetaData; + private readonly VariableVersionThreadLocal _versionThreadLocal; + private volatile VersionedValueList _versionsHigh; + private volatile VersionedValueList _versionsLow; + + public VariableReader(VariableMetaData variableMetaData, VariableVersionThreadLocal versionThreadLocal, VersionedValueList versionsLow) + { + _variableMetaData = variableMetaData; + _versionThreadLocal = versionThreadLocal; + _versionsLow = versionsLow; + _versionsHigh = null; + } + + /// + /// For roll-over (overflow) in version numbers, sets a new collection of versioned-values for the variable + /// to use when requests over the version rollover boundary are made. + /// + /// the list of versions for roll-over + public VersionedValueList VersionsHigh + { + set { _versionsHigh = value; } + get { return _versionsHigh; } + } + + /// + /// Returns the value of a variable. + /// Considers the version set via thread-local for the thread's atomic read of variable values. + /// + /// value of variable at the version applicable for the thead + public object Value + { + get + { + var entry = _versionThreadLocal.CurrentThread; + if (entry.Uncommitted != null) + { + // Check existance as null values are allowed + if (entry.Uncommitted.ContainsKey(_variableMetaData.VariableNumber)) + { + return entry.Uncommitted.Get(_variableMetaData.VariableNumber).Second; + } + } + + var myVersion = entry.Version; + var versions = _versionsLow; + if (myVersion >= VariableServiceImpl.ROLLOVER_READER_BOUNDARY) + { + if (_versionsHigh != null) + { + versions = _versionsHigh; + } + } + return versions.GetVersion(myVersion); + } + } + + public VariableMetaData VariableMetaData + { + get { return _variableMetaData; } + } + + /// + /// Sets a new list of versioned-values to inquire against, for use when version numbers roll-over. + /// + /// the list of versions for read + public VersionedValueList VersionsLow + { + get { return _versionsLow; } + set { _versionsLow = value; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/variable/VariableService.cs b/NEsper.Core/NEsper.Core/epl/variable/VariableService.cs new file mode 100755 index 000000000..188f4b504 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/variable/VariableService.cs @@ -0,0 +1,145 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; + +using com.espertech.esper.compat.threading; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.core; + +namespace com.espertech.esper.epl.variable +{ + /// + /// Variables service for reading and writing variables, and for setting a version number for the current thread to + /// consider variables for. + /// + /// See implementation class for further details. + /// + /// + public interface VariableService : IDisposable + { + /// Sets the variable version that subsequent reads consider. + void SetLocalVersion(); + + /// Lock for use in atomic writes to the variable space. + /// read write lock for external coordinated write + IReaderWriterLock ReadWriteLock { get; } + + /// + /// Creates a new variable. + /// + /// Name of the optional context. + /// name of the variable + /// variable type + /// if set to true [constant]. + /// if set to true [array]. + /// if set to true [array of primitive]. + /// initialization value; String values are allowed and parsed according to type + /// The engine import service. + /// VariableExistsException if the variable name is already in use + /// VariableTypeException if the variable type cannot be recognized + void CreateNewVariable( + string optionalContextName, + string variableName, + string type, + bool constant, + bool array, + bool arrayOfPrimitive, + object value, + EngineImportService engineImportService); + + /// + /// Creates a new variable. + /// + /// + /// Name of the optional context. + /// name of the variable + /// if set to true [constant]. + /// initialization value; String values are allowed and parsed according to type + /// The engine import service. + /// VariableExistsException if the variable name is already in use + /// VariableTypeException if the variable type cannot be recognized + void CreateNewVariable( + string optionalContextName, + string variableName, + bool constant, + T value, + EngineImportService engineImportService); + + /// + /// Returns a reader that provides access to variable values. The reader considers the + /// version currently set via setLocalVersion. + /// + /// the variable that the reader should read + /// The agent instance id accessor. + /// reader + VariableReader GetReader(String variableName, int agentInstanceIdAccessor); + + /// + /// Registers a callback invoked when the variable is written with a new value. + /// + /// Name of the variable. + /// The agent instance id. + /// a callback + void RegisterCallback(string variableName, int agentInstanceId, VariableChangeCallback variableChangeCallback); + + /// + /// Removes a callback. + /// + /// Name of the variable. + /// The agent instance id. + /// a callback + void UnregisterCallback(string variableName, int agentInstanceId, VariableChangeCallback variableChangeCallback); + + /// + /// Check type of the value supplied and writes the new variable value. + /// + /// Must be followed by either a commit or rollback. + /// + /// Name of the variable. + /// The agent instance id. + /// the new value + void CheckAndWrite(string variableName, int agentInstanceId, Object newValue); + + /// + /// Writes a new variable value. + /// + /// Must be followed by either a commit or rollback. + /// + /// + /// the index number of the variable to write (from VariableMetaData) + /// The agent instance id. + /// the new value + void Write(int variableNumber, int agentInstanceId, Object newValue); + + /// Commits the variable outstanding changes. + void Commit(); + + /// Rolls back the variable outstanding changes. + void Rollback(); + + /// Returns a map of variable name and reader, for thread-safe iteration. + /// variable names and readers + IDictionary VariableReadersNonCP { get; } + + VariableMetaData GetVariableMetaData(String variableName); + + /// Removes a variable. + /// to remove + void RemoveVariableIfFound(String name); + + String IsContextVariable(String propertyName); + + void AllocateVariableState(String variableName, int agentInstanceId, StatementExtensionSvcContext extensionServicesContext, bool isRecoveringResilient); + void DeallocateVariableState(String variableName, int agentInstanceId); + + ConcurrentDictionary GetReadersPerCP(String variableName); + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/epl/variable/VariableServiceConstants.cs b/NEsper.Core/NEsper.Core/epl/variable/VariableServiceConstants.cs new file mode 100755 index 000000000..9c7bfe992 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/variable/VariableServiceConstants.cs @@ -0,0 +1,17 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.variable +{ + [Obsolete] + public static class VariableServiceConstants + { + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/epl/variable/VariableServiceImpl.cs b/NEsper.Core/NEsper.Core/epl/variable/VariableServiceImpl.cs new file mode 100755 index 000000000..324bc5abb --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/variable/VariableServiceImpl.cs @@ -0,0 +1,760 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.compat.threading; +using com.espertech.esper.core.service; +using com.espertech.esper.core.start; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events; +using com.espertech.esper.schedule; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.variable +{ + /// + /// Variables service for reading and writing variables, and for setting a version number for the current thread to + /// consider variables for. + /// + /// Consider a statement as follows: select * from MyEvent as A where A.val > var1 and A.val2 > var1 and A.val3 > var2 + /// + /// Upon statement execution we need to guarantee that the same atomic value for all variables is applied for all + /// variable reads (by expressions typically) within the statement. + /// + /// Designed to support: + /// however writes are very fast (entry to collection plus increment an int) and therefore blocking should not be an issue + /// + /// As an alternative to a version-based design, a read-lock for the variable space could also be used, with the following + /// disadvantages: The write lock may just not be granted unless fair locks are used which are more expensive; And + /// a read-lock is more expensive to acquire for multiple CPUs; A thread-local is still need to deal with + /// "set var1=3, var2=var1+1" assignments where the new uncommitted value must be visible in the local evaluation. + /// + /// Every new write to a variable creates a new version. Thus when reading variables, readers can ignore newer versions + /// and a read lock is not required in most circumstances. + /// + /// This algorithm works as follows: + /// + /// A thread processing an event into the engine via sendEvent() calls the "setLocalVersion" method once + /// before processing a statement that has variables. + /// This places into a threadlocal variable the current version number, say version 570. + /// + /// A statement that reads a variable has an that has a handle + /// obtained during validation (example). + /// + /// The takes the version from the threadlocal (570) and compares the version number with the + /// version numbers held for the variable. + /// If the current version is same or lower (520, as old or older) then the threadlocal version, + /// then use the current value. + /// If the current version is higher (571, newer) then the threadlocal version, then go to the prior value. + /// Use the prior value until a version is found that as old or older then the threadlocal version. + /// + /// If no version can be found that is old enough, output a warning and return the newest version. + /// This should not happen, unless a thread is executing for very long within a single statement such that + /// lifetime-old-version time speriod passed before the thread asks for variable values. + /// + /// As version numbers are counted up they may reach a boundary. Any write transaction after the boundary + /// is reached performs a roll-over. In a roll-over, all variables version lists are + /// newly created and any existing threads that read versions go against a (old) high-collection, + /// while new threads reading the reset version go against a new low-collection. + /// + /// The class also allows an optional state handler to be plugged in to handle persistence for variable state. + /// The state handler gets invoked when a variable changes value, and when a variable gets created + /// to obtain the current value from persistence, if any. + /// + public class VariableServiceImpl : VariableService + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// Sets the boundary above which a reader considers the high-version list of variable values. + /// For use in roll-over when the current version number overflows the ROLLOVER_WRITER_BOUNDARY. + /// + public const int ROLLOVER_READER_BOUNDARY = int.MaxValue - 100000; + + /// + /// Applicable for each variable if more then the number of versions accumulated, check + /// timestamps to determine if a version can be expired. + /// + public const int HIGH_WATERMARK_VERSIONS = 50; + + // Each variable has an index number, a context-partition id, a current version and a list of values + private readonly List> _variableVersionsPerCP; + + // Each variable and a context-partition id may have a set of callbacks to invoke when the variable changes + private readonly List>> _changeCallbacksPerCP; + + // Keep the variable list + private readonly IDictionary _variables; + + // Write lock taken on write of any variable; and on read of older versions + private readonly IReaderWriterLock _readWriteLock; + + // Thread-local for the visible version per thread + private VariableVersionThreadLocal _versionThreadLocal = new VariableVersionThreadLocal(); + + // Number of milliseconds that old versions of a variable are allowed to live + private readonly long _millisecondLifetimeOldVersions; + private readonly TimeProvider _timeProvider; + private readonly EventAdapterService _eventAdapterService; + private readonly VariableStateHandler _optionalStateHandler; + + private volatile int _currentVersionNumber; + private int _currentVariableNumber; + + /// + /// Ctor. + /// + /// number of milliseconds a version may hang around before expiry + /// provides the current time + /// a optional plug-in that may store variable state and retrieve state upon creation + /// event adapters + public VariableServiceImpl(long millisecondLifetimeOldVersions, TimeProvider timeProvider, EventAdapterService eventAdapterService, VariableStateHandler optionalStateHandler) + : this(0, millisecondLifetimeOldVersions, timeProvider, eventAdapterService, optionalStateHandler) + { + } + + /// + /// Ctor. + /// + /// the first version number to start from + /// number of milliseconds a version may hang around before expiry + /// provides the current time + /// a optional plug-in that may store variable state and retrieve state upon creation + /// for finding event types + public VariableServiceImpl(int startVersion, long millisecondLifetimeOldVersions, TimeProvider timeProvider, EventAdapterService eventAdapterService, VariableStateHandler optionalStateHandler) + { + _millisecondLifetimeOldVersions = millisecondLifetimeOldVersions; + _timeProvider = timeProvider; + _eventAdapterService = eventAdapterService; + _optionalStateHandler = optionalStateHandler; + _variables = new Dictionary().WithNullSupport(); + _readWriteLock = ReaderWriterLockManager.CreateLock(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + _variableVersionsPerCP = new List>(); + _changeCallbacksPerCP = new List>>(); + _currentVersionNumber = startVersion; + } + + public void Dispose() + { + _versionThreadLocal = new VariableVersionThreadLocal(); + } + + public void RemoveVariableIfFound(String name) + { + lock (this) + { + var metaData = _variables.Get(name); + if (metaData == null) + { + return; + } + + if (Log.IsDebugEnabled) + { + Log.Debug("Removing variable '" + name + "'"); + } + _variables.Remove(name); + + if (_optionalStateHandler != null) + { + ConcurrentDictionary readers = _variableVersionsPerCP[metaData.VariableNumber]; + IEnumerable cps = Collections.GetEmptySet(); + if (readers != null) + { + cps = readers.Keys; + } + _optionalStateHandler.RemoveVariable(name, cps); + } + + var number = metaData.VariableNumber; + _variableVersionsPerCP[number] = null; + _changeCallbacksPerCP[number] = null; + } + } + + public void SetLocalVersion() + { + _versionThreadLocal.CurrentThread.Version = _currentVersionNumber; + } + + public void RegisterCallback(String variableName, int agentInstanceId, VariableChangeCallback variableChangeCallback) + { + var metaData = _variables.Get(variableName); + if (metaData == null) + { + return; + } + + IDictionary> cps = _changeCallbacksPerCP[metaData.VariableNumber]; + if (cps == null) + { + cps = new Dictionary>(); + _changeCallbacksPerCP[metaData.VariableNumber] = cps; + } + + if (metaData.ContextPartitionName == null) + { + agentInstanceId = EPStatementStartMethodConst.DEFAULT_AGENT_INSTANCE_ID; + } + + ICollection callbacks = cps.Get(agentInstanceId); + if (callbacks == null) + { + callbacks = new CopyOnWriteArraySet(); + cps.Put(agentInstanceId, callbacks); + } + callbacks.Add(variableChangeCallback); + } + + public void UnregisterCallback(String variableName, int agentInstanceId, VariableChangeCallback variableChangeCallback) + { + var metaData = _variables.Get(variableName); + if (metaData == null) + { + return; + } + + var cps = _changeCallbacksPerCP[metaData.VariableNumber]; + if (cps == null) + { + return; + } + + if (metaData.ContextPartitionName == null) + { + agentInstanceId = 0; + } + + ICollection callbacks = cps.Get(agentInstanceId); + if (callbacks != null) + { + callbacks.Remove(variableChangeCallback); + } + } + + public void CreateNewVariable( + string optionalContextName, + string variableName, + bool constant, + T value, + EngineImportService engineImportService) + { + CreateNewVariable( + optionalContextName, + variableName, + typeof(T).FullName, + constant, + typeof(T).IsArray, false, + value, + engineImportService); + } + + public void CreateNewVariable(string optionalContextName, string variableName, string variableType, bool constant, bool array, bool arrayOfPrimitive, object value, EngineImportService engineImportService) + { + // Determime the variable type + var primitiveType = TypeHelper.GetPrimitiveTypeForName(variableType); + var type = TypeHelper.GetTypeForSimpleName(variableType); + Type arrayType = null; + EventType eventType = null; + if (type == null) + { + if (variableType.ToLower() == "object") + { + type = typeof(Object); + } + if (type == null) + { + eventType = _eventAdapterService.GetEventTypeByName(variableType); + if (eventType != null) + { + type = eventType.UnderlyingType; + } + } + if (type == null) + { + try + { + type = engineImportService.ResolveType(variableType, false); + if (array) + { + arrayType = TypeHelper.GetArrayType(type.GetBoxedType()); + } + } + catch (EngineImportException e) + { + Log.Debug("Not found '" + type + "': " + e.Message, e); + // expected + } + } + if (type == null) + { + throw new VariableTypeException("Cannot create variable '" + variableName + "', type '" + + variableType + "' is not a recognized type"); + } + if (array && eventType != null) + { + throw new VariableTypeException("Cannot create variable '" + variableName + "', type '" + + variableType + "' cannot be declared as an array type"); + } + } + else + { + if (array) + { + if (arrayOfPrimitive) + { + if (primitiveType == null) + { + throw new VariableTypeException("Cannot create variable '" + variableName + "', type '" + + variableType + "' is not a primitive type"); + } + arrayType = TypeHelper.GetArrayType(primitiveType); + } + else + { + arrayType = TypeHelper.GetArrayType(type.GetBoxedType()); + } + } + } + + if ((eventType == null) && (!type.IsBuiltinDataType()) && (type != typeof(object)) && !type.IsArray && !type.IsEnum) + { + if (array) + { + throw new VariableTypeException("Cannot create variable '" + variableName + "', type '" + + variableType + "' cannot be declared as an array, only scalar types can be array"); + } + + eventType = _eventAdapterService.AddBeanType(type.GetDefaultTypeName(), type, false, false, false); + } + + if (arrayType != null) + { + type = arrayType; + } + + CreateNewVariable(variableName, optionalContextName, type, eventType, constant, value); + } + + private void CreateNewVariable(String variableName, String optionalContextName, Type type, EventType eventType, bool constant, Object value) + { + lock (this) + { + // check type + var variableType = type.GetBoxedType(); + + // check if it exists + var metaData = _variables.Get(variableName); + if (metaData != null) + { + throw new VariableExistsException(VariableServiceUtil.GetAlreadyDeclaredEx(variableName, false)); + } + + // find empty spot + var emptySpot = -1; + var count = 0; + foreach (var entry in _variableVersionsPerCP) + { + if (entry == null) + { + emptySpot = count; + break; + } + count++; + } + + int variableNumber; + if (emptySpot != -1) + { + variableNumber = emptySpot; + _variableVersionsPerCP[emptySpot] = new ConcurrentDictionary(); + _changeCallbacksPerCP[emptySpot] = null; + } + else + { + variableNumber = _currentVariableNumber; + _variableVersionsPerCP.Add(new ConcurrentDictionary()); + _changeCallbacksPerCP.Add(null); + _currentVariableNumber++; + } + + // check coercion + var coercedValue = value; + if (eventType != null) + { + if ((value != null) && (!TypeHelper.IsSubclassOrImplementsInterface(value.GetType(), eventType.UnderlyingType))) + { + throw new VariableTypeException("Variable '" + variableName + + "' of declared event type '" + eventType.Name + "' underlying type '" + eventType.UnderlyingType.FullName + + "' cannot be assigned a value of type '" + value.GetType().FullName + "'"); + } + coercedValue = _eventAdapterService.AdapterForType(value, eventType); + } + else if (variableType == typeof(object)) + { + // no validation + } + else + { + // allow string assignments to non-string variables + if ((coercedValue != null) && (coercedValue is String)) + { + try + { + coercedValue = TypeHelper.Parse(variableType, (String)coercedValue); + } + catch (Exception ex) + { + throw new VariableTypeException( + string.Format( + "Variable '{0}' of declared type {1} cannot be initialized by value '{2}': {3}: {4}", + variableName, + variableType.GetTypeNameFullyQualPretty(), + coercedValue, + ex.GetType().FullName, + ex.Message)); + } + } + + if ((coercedValue != null) && (!TypeHelper.IsSubclassOrImplementsInterface(coercedValue.GetType(), variableType))) + { + // if the declared type is not numeric or the init value is not numeric, fail + if ((!variableType.IsNumeric()) || (!(coercedValue.IsNumber()))) + { + throw GetVariableTypeException(variableName, variableType, coercedValue.GetType()); + } + + if (!(coercedValue.GetType().CanCoerce(variableType))) + { + throw GetVariableTypeException(variableName, variableType, coercedValue.GetType()); + } + // coerce + coercedValue = CoercerFactory.CoerceBoxed(coercedValue, variableType); + } + } + + var initialState = coercedValue; + VariableStateFactory stateFactory = new VariableStateFactoryConst(initialState); + + metaData = new VariableMetaData(variableName, optionalContextName, variableNumber, variableType, eventType, constant, stateFactory); + _variables.Put(variableName, metaData); + } + } + + public void AllocateVariableState(String variableName, int agentInstanceId, StatementExtensionSvcContext extensionServicesContext, bool isRecoveringResilient) + { + var metaData = _variables.Get(variableName); + if (metaData == null) + { + throw new ArgumentException("Failed to find variable '" + variableName + "'"); + } + + // Check current state - see if the variable exists in the state handler + var initialState = metaData.VariableStateFactory.InitialState; + if (_optionalStateHandler != null) + { + var priorValue = _optionalStateHandler.GetHasState( + variableName, + metaData.VariableNumber, agentInstanceId, + metaData.VariableType, + metaData.EventType, extensionServicesContext, + metaData.IsConstant); + if (isRecoveringResilient) + { + if (priorValue.First) + { + initialState = priorValue.Second; + } + } + else + { + _optionalStateHandler.SetState(variableName, metaData.VariableNumber, agentInstanceId, initialState); + } + } + + // create new holder for versions + var timestamp = _timeProvider.Time; + var valuePerVersion = new VersionedValueList(variableName, _currentVersionNumber, initialState, timestamp, _millisecondLifetimeOldVersions, _readWriteLock.ReadLock, HIGH_WATERMARK_VERSIONS, false); + var cps = _variableVersionsPerCP[metaData.VariableNumber]; + var reader = new VariableReader(metaData, _versionThreadLocal, valuePerVersion); + cps.Put(agentInstanceId, reader); + } + + public void DeallocateVariableState(String variableName, int agentInstanceId) + { + var metaData = _variables.Get(variableName); + if (metaData == null) + { + throw new ArgumentException("Failed to find variable '" + variableName + "'"); + } + + VariableReader tempVariableReader; + var cps = _variableVersionsPerCP[metaData.VariableNumber]; + cps.TryRemove(agentInstanceId, out tempVariableReader); + + if (_optionalStateHandler != null) + { + _optionalStateHandler.RemoveState(variableName, metaData.VariableNumber, agentInstanceId); + } + } + + public VariableMetaData GetVariableMetaData(String variableName) + { + return _variables.Get(variableName); + } + + public VariableReader GetReader(String variableName, int agentInstanceIdAccessor) + { + var metaData = _variables.Get(variableName); + if (metaData == null) + { + return null; + } + var cps = _variableVersionsPerCP[metaData.VariableNumber]; + if (metaData.ContextPartitionName == null) + { + return cps.Get(EPStatementStartMethodConst.DEFAULT_AGENT_INSTANCE_ID); + } + return cps.Get(agentInstanceIdAccessor); + } + + public String IsContextVariable(String variableName) + { + var metaData = _variables.Get(variableName); + if (metaData == null) + { + return null; + } + return metaData.ContextPartitionName; + } + + public void Write(int variableNumber, int agentInstanceId, Object newValue) + { + var entry = _versionThreadLocal.CurrentThread; + if (entry.Uncommitted == null) + { + entry.Uncommitted = new Dictionary>(); + } + entry.Uncommitted.Put(variableNumber, new Pair(agentInstanceId, newValue)); + } + + public IReaderWriterLock ReadWriteLock + { + get { return _readWriteLock; } + } + + public void Commit() + { + var entry = _versionThreadLocal.CurrentThread; + if (entry.Uncommitted == null) + { + return; + } + + // get new version for adding the new values (1 or many new values) + var newVersion = _currentVersionNumber + 1; + + if (_currentVersionNumber == ROLLOVER_READER_BOUNDARY) + { + // Roll over to new collections; + // This honors existing threads that will now use the "high" collection in the reader for high version requests + // and low collection (new and updated) for low version requests + RollOver(); + newVersion = 2; + } + var timestamp = _timeProvider.Time; + + // apply all uncommitted changes + foreach (KeyValuePair> uncommittedEntry in entry.Uncommitted.ToList()) + { + var cps = _variableVersionsPerCP[uncommittedEntry.Key]; + var reader = cps[uncommittedEntry.Value.First]; + var versions = reader.VersionsLow; + + // add new value as a new version + var newValue = uncommittedEntry.Value.Second; + var oldValue = versions.AddValue(newVersion, newValue, timestamp); + + // make a callback that the value changed + var cpsCallback = _changeCallbacksPerCP[uncommittedEntry.Key]; + if (cpsCallback != null) + { + var callbacks = cpsCallback.Get(uncommittedEntry.Value.First); + if (callbacks != null) + { + foreach (var callback in callbacks) + { + callback.Invoke(newValue, oldValue); + } + } + } + + // Check current state - see if the variable exists in the state handler + if (_optionalStateHandler != null) + { + var name = versions.Name; + int agentInstanceId = reader.VariableMetaData.ContextPartitionName == null + ? EPStatementStartMethodConst.DEFAULT_AGENT_INSTANCE_ID + : uncommittedEntry.Value.First; + _optionalStateHandler.SetState(name, uncommittedEntry.Key, agentInstanceId, newValue); + } + } + + // this makes the new values visible to other threads (not this thread unless set-version called again) + _currentVersionNumber = newVersion; + entry.Uncommitted = null; // clean out uncommitted variables + } + + public void Rollback() + { + var entry = _versionThreadLocal.CurrentThread; + entry.Uncommitted = null; + } + + /// + /// Rollover includes creating a new + /// + private void RollOver() + { + foreach (var entryCP in _variableVersionsPerCP) + { + foreach (KeyValuePair entry in entryCP) + { + String name = entry.Value.VariableMetaData.VariableName; + var timestamp = _timeProvider.Time; + + // Construct a new collection, forgetting the history + var versionsOld = entry.Value.VersionsLow; + var currentValue = versionsOld.CurrentAndPriorValue.CurrentVersion.Value; + var versionsNew = new VersionedValueList(name, 1, currentValue, timestamp, _millisecondLifetimeOldVersions, _readWriteLock.ReadLock, HIGH_WATERMARK_VERSIONS, false); + + // Tell the reader to use the high collection for old requests + entry.Value.VersionsHigh = versionsOld; + entry.Value.VersionsLow = versionsNew; + } + } + } + + public void CheckAndWrite(String variableName, int agentInstanceId, Object newValue) + { + var metaData = _variables.Get(variableName); + var variableNumber = metaData.VariableNumber; + + if (newValue == null) + { + Write(variableNumber, agentInstanceId, null); + return; + } + + var valueType = newValue.GetType(); + + if (metaData.EventType != null) + { + if ((!TypeHelper.IsSubclassOrImplementsInterface(newValue.GetType(), metaData.EventType.UnderlyingType))) + { + throw new VariableValueException("Variable '" + variableName + + "' of declared event type '" + metaData.EventType.Name + "' underlying type '" + metaData.EventType.UnderlyingType.FullName + + "' cannot be assigned a value of type '" + valueType.FullName + "'"); + } + var eventBean = _eventAdapterService.AdapterForType(newValue, metaData.EventType); + Write(variableNumber, agentInstanceId, eventBean); + return; + } + + var variableType = metaData.VariableType; + if ((valueType == variableType) || (variableType == typeof(object))) + { + Write(variableNumber, agentInstanceId, newValue); + return; + } + + // Look for simple boxing rules + var valueTypeBoxed = valueType.GetBoxedType(); + var variableTypeBoxed = variableType.GetBoxedType(); + if (((valueType != valueTypeBoxed) || (variableType != variableTypeBoxed)) && ((valueTypeBoxed == variableTypeBoxed))) + { + Write(variableNumber, agentInstanceId, newValue); + return; + } + + if ((!variableType.IsNumeric()) || (!valueType.IsNumeric())) + { + throw new VariableValueException(VariableServiceUtil.GetAssigmentExMessage(variableName, variableType, valueType)); + } + + // determine if the expression type can be assigned + if (!(TypeHelper.CanCoerce(valueType, variableType))) + { + throw new VariableValueException(VariableServiceUtil.GetAssigmentExMessage(variableName, variableType, valueType)); + } + + object valueCoerced = CoercerFactory.CoerceBoxed(newValue, variableType); + Write(variableNumber, agentInstanceId, valueCoerced); + } + + public override String ToString() + { + var writer = new StringWriter(); + foreach (var entryMeta in _variables) + { + var variableNum = entryMeta.Value.VariableNumber; + foreach (KeyValuePair entry in _variableVersionsPerCP[variableNum]) + { + var list = entry.Value.VersionsLow; + writer.Write("Variable '" + entry.Key + "' : " + list + "\n"); + } + } + return writer.ToString(); + } + + public IDictionary VariableReadersNonCP + { + get + { + IDictionary result = new Dictionary(); + foreach (var entryMeta in _variables) + { + var variableNum = entryMeta.Value.VariableNumber; + if (entryMeta.Value.ContextPartitionName == null) + { + foreach (KeyValuePair entry in _variableVersionsPerCP[variableNum]) + { + result.Put(entryMeta.Key, entry.Value); + } + } + } + return result; + } + } + + public ConcurrentDictionary GetReadersPerCP(String variableName) + { + var metaData = _variables.Get(variableName); + return _variableVersionsPerCP[metaData.VariableNumber]; + } + + private static VariableTypeException GetVariableTypeException(String variableName, Type variableType, Type initValueClass) + { + return new VariableTypeException("Variable '" + variableName + + "' of declared type " + variableType.GetTypeNameFullyQualPretty() + + " cannot be initialized by a value of type " + initValueClass.GetTypeNameFullyQualPretty()); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/variable/VariableServiceUtil.cs b/NEsper.Core/NEsper.Core/epl/variable/VariableServiceUtil.cs new file mode 100755 index 000000000..f274f8ded --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/variable/VariableServiceUtil.cs @@ -0,0 +1,95 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.variable +{ + public class VariableServiceUtil + { + public static string GetAssigmentExMessage(string variableName, Type variableType, Type initValueClass) + { + return string.Format( + "Variable '{0}' of declared type {1} cannot be assigned a value of type {2}", + variableName, + TypeHelper.GetTypeNameFullyQualPretty(variableType), + TypeHelper.GetTypeNameFullyQualPretty(initValueClass)); + } + + public static string CheckVariableContextName( + string optionalStatementContextName, + VariableMetaData variableMetaData) + { + if (optionalStatementContextName == null) + { + if (variableMetaData.ContextPartitionName != null) + { + return "Variable '" + variableMetaData.VariableName + "' defined for use with context '" + + variableMetaData.ContextPartitionName + "' can only be accessed within that context"; + } + } + else + { + if (variableMetaData.ContextPartitionName != null && + !variableMetaData.ContextPartitionName.Equals(optionalStatementContextName)) + { + return "Variable '" + variableMetaData.VariableName + "' defined for use with context '" + + variableMetaData.ContextPartitionName + "' is not available for use with context '" + + optionalStatementContextName + "'"; + } + } + return null; + } + + public static string CheckVariableContextName( + ContextDescriptor contextDescriptor, + VariableMetaData variableMetaData) + { + if (contextDescriptor == null) + { + return CheckVariableContextName((string) null, variableMetaData); + } + return CheckVariableContextName(contextDescriptor.ContextName, variableMetaData); + } + + public static void CheckAlreadyDeclaredVariable(string variableName, VariableService variableService) + + { + if (variableService.GetVariableMetaData(variableName) != null) + { + throw new ExprValidationException(GetAlreadyDeclaredEx(variableName, false)); + } + } + + public static void CheckAlreadyDeclaredTable(string tableName, TableService tableService) + + { + if (tableService.GetTableMetadata(tableName) != null) + { + throw new ExprValidationException(GetAlreadyDeclaredEx(tableName, true)); + } + } + + public static string GetAlreadyDeclaredEx(string variableOrTableName, bool isTable) + { + if (isTable) + { + return "Table by name '" + variableOrTableName + "' has already been created"; + } + else + { + return "Variable by name '" + variableOrTableName + "' has already been created"; + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/variable/VariableStateFactory.cs b/NEsper.Core/NEsper.Core/epl/variable/VariableStateFactory.cs new file mode 100755 index 000000000..cab8c1f79 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/variable/VariableStateFactory.cs @@ -0,0 +1,15 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.variable +{ + public interface VariableStateFactory + { + object InitialState { get; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/variable/VariableStateFactoryConst.cs b/NEsper.Core/NEsper.Core/epl/variable/VariableStateFactoryConst.cs new file mode 100755 index 000000000..5ee2d2d46 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/variable/VariableStateFactoryConst.cs @@ -0,0 +1,22 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.variable +{ + public class VariableStateFactoryConst : VariableStateFactory + { + public VariableStateFactoryConst(Object initialState) + { + InitialState = initialState; + } + + public object InitialState { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/variable/VariableStateHandler.cs b/NEsper.Core/NEsper.Core/epl/variable/VariableStateHandler.cs new file mode 100755 index 000000000..0b5ff7ec0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/variable/VariableStateHandler.cs @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.core.service; + +namespace com.espertech.esper.epl.variable +{ + /// + /// Interface for a plug-in to to handle variable persistent state. + /// + public interface VariableStateHandler + { + /// + /// Returns the current variable state plus true if there is a current state since the variable may have + /// the value of null; returns false and null if there is no current state + /// + /// variable name + /// number of the variable + /// The agent instance identifier. + /// type of the variable + /// event type or null if not a variable that represents an event + /// for caches etc. + /// if set to true [is constant]. + /// + /// indicator whether the variable is known and it's state, or whether it doesn't have state (false) + /// + Pair GetHasState(string variableName, int variableNumber, int agentInstanceId, Type type, EventType eventType, StatementExtensionSvcContext statementExtContext, bool isConstant); + + /// Sets the new variable value + /// name of the variable + /// number of the variable + /// new variable value, null values allowed + void SetState(String variableName, int variableNumber, int agentInstanceId, Object newValue); + + void RemoveState(String variableName, int variableNumber, int agentInstanceId); + + void RemoveVariable(String name, IEnumerable cps); + + } +} diff --git a/NEsper.Core/NEsper.Core/epl/variable/VariableTypeException.cs b/NEsper.Core/NEsper.Core/epl/variable/VariableTypeException.cs new file mode 100755 index 000000000..e010a3386 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/variable/VariableTypeException.cs @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.variable +{ + /// + /// Exception indicating a variable type error. + /// + public class VariableTypeException : VariableDeclarationException + { + /// Ctor. + /// the exception message. + public VariableTypeException(String msg) + : base(msg) + { + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/epl/variable/VariableVersionThreadEntry.cs b/NEsper.Core/NEsper.Core/epl/variable/VariableVersionThreadEntry.cs new file mode 100755 index 000000000..62c439fbd --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/variable/VariableVersionThreadEntry.cs @@ -0,0 +1,44 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.collection; + +namespace com.espertech.esper.epl.variable +{ + /// + /// Thread-specific state in regards to variable versions. + /// + public class VariableVersionThreadEntry + { + /// Ctor. + /// + /// current version number of the variables visible to thread + /// + /// + /// the uncommitted values of variables for the thread, if any + /// + public VariableVersionThreadEntry(int version, IDictionary> uncommitted) + { + Version = version; + Uncommitted = uncommitted; + } + + /// Gets or sets the version visible for a thread. + /// version number + public int Version { get; set; } + + /// + /// Gets or sets a map of variable number and uncommitted value, or empty + /// map or null if none exist + /// + /// uncommitted values + public IDictionary> Uncommitted { get; set; } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/epl/variable/VariableVersionThreadLocal.cs b/NEsper.Core/NEsper.Core/epl/variable/VariableVersionThreadLocal.cs new file mode 100755 index 000000000..07d2dac26 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/variable/VariableVersionThreadLocal.cs @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.compat.threading; + +namespace com.espertech.esper.epl.variable +{ + /// + /// A wrapper for a thread-local to hold the current version for variables visible + /// for a thread, as well as uncommitted values of variables for a thread. + /// + public class VariableVersionThreadLocal + { + private readonly IThreadLocal _vThreadLocal; + + /// + /// Creates a new variable version thread entry. + /// + /// + private static VariableVersionThreadEntry CreateEntry() + { + return new VariableVersionThreadEntry(0, null); + } + + /// + /// Ctor. + /// + public VariableVersionThreadLocal() + { + _vThreadLocal = ThreadLocalManager.Create(CreateEntry); + } + + /// + /// Returns the version and uncommitted values for the current thread. + /// + /// entry for current thread + public VariableVersionThreadEntry CurrentThread + { + get + { + VariableVersionThreadEntry entry = _vThreadLocal.GetOrCreate(); + return entry; + } + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/epl/variable/VersionedValue.cs b/NEsper.Core/NEsper.Core/epl/variable/VersionedValue.cs new file mode 100755 index 000000000..7f16ee2ca --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/variable/VersionedValue.cs @@ -0,0 +1,59 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.epl.variable +{ + /// + /// Holds a version of a value and a timestamp when that version is taken. + /// + public class VersionedValue + { + private readonly int version; + private readonly T value; + private readonly long timestamp; + + /// Ctor. + /// version number + /// value at that version + /// time when version was taken + public VersionedValue(int version, T value, long timestamp) + { + this.version = version; + this.value = value; + this.timestamp = timestamp; + } + + /// Returns the version. + /// version + public int Version + { + get { return version; } + } + + /// Returns the value. + /// value + public T Value + { + get { return value; } + } + + /// Returns the time the version was taken. + /// time of version + public long Timestamp + { + get { return timestamp; } + } + + public override String ToString() + { + return value + "@" + version + "@" + (new DateTime(timestamp)); + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/epl/variable/VersionedValueList.cs b/NEsper.Core/NEsper.Core/epl/variable/VersionedValueList.cs new file mode 100755 index 000000000..739663e5b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/variable/VersionedValueList.cs @@ -0,0 +1,260 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Text; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.logging; +using com.espertech.esper.compat.threading; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.variable +{ + /// + /// A self-cleaning list of versioned-values. + /// + /// The current and prior version are held for lock-less read access in a transient variable. + /// + /// The list relies on transient as well as a read-lock to guard against concurrent modification. However a read lock is only + /// taken when a list of old versions must be updated. + /// + /// When a high watermark is reached, the list on write access removes old versions up to the + /// number of milliseconds compared to current write timestamp. + /// + /// If an older version is requested then held by the list, the list can either throw an exception + /// or return the current value. + /// + public class VersionedValueList + where T : class + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + // Variables name and read lock; read lock used when older version then the prior version is requested + private readonly String _name; + private readonly ILockable _readLock; + private readonly int _highWatermark; // used for removing older versions + private readonly bool _errorWhenNotFound; + private readonly long _millisecondLifetimeOldVersions; + + // Hold the current and prior version for no-lock reading + private volatile CurrentValue _currentAndPriorValue; + + // Holds the older versions + private readonly List> _olderVersions; + + /// Ctor. + /// variable name + /// first version number + /// first value + /// timestamp of first version + /// + /// number of milliseconds after which older versions get expired and removed + /// + /// for coordinating Update to old versions + /// + /// when the number of old versions reached high watermark, the list inspects size on every write + /// + /// + /// true if an exception should be throw if the requested version cannot be found, + /// or false if the engine should log a warning + /// + public VersionedValueList(String name, int initialVersion, T initialValue, long timestamp, long millisecondLifetimeOldVersions, ILockable readLock, int highWatermark, bool errorWhenNotFound) + { + _name = name; + _readLock = readLock; + _highWatermark = highWatermark; + _olderVersions = new List>(); + _errorWhenNotFound = errorWhenNotFound; + _millisecondLifetimeOldVersions = millisecondLifetimeOldVersions; + _currentAndPriorValue = new CurrentValue(new VersionedValue(initialVersion, initialValue, timestamp), + new VersionedValue(-1, null, timestamp)); + } + + /// Returns the name of the value stored. + /// value name + public String Name + { + get { return _name; } + } + + /// + /// Retrieve a value for the given version or older then then given version. + /// + /// The implementaton only locks the read lock if an older version the the prior version is requested. + /// + /// the version we are looking for + /// + /// value for the version or the next older version, ignoring newer versions + /// + public T GetVersion(int versionAndOlder) + { + if (ExecutionPathDebugLog.IsEnabled && Log.IsDebugEnabled) + { + Log.Debug(".GetVersion Thread " + Thread.CurrentThread.ManagedThreadId + " for '" + _name + "' retrieving version " + versionAndOlder + " or older"); + } + + T resultValue = null; + CurrentValue current = _currentAndPriorValue; + + if (current.CurrentVersion.Version <= versionAndOlder) + { + resultValue = current.CurrentVersion.Value; + } + else if ((current.PriorVersion.Version != -1) && + (current.PriorVersion.Version <= versionAndOlder)) + { + resultValue = current.PriorVersion.Value; + } + else + { + using (_readLock.Acquire()) + { + current = _currentAndPriorValue; + + if (current.CurrentVersion.Version <= versionAndOlder) + { + resultValue = current.CurrentVersion.Value; + } + else if ((current.PriorVersion.Version != -1) && + (current.PriorVersion.Version <= versionAndOlder)) + { + resultValue = current.PriorVersion.Value; + } + else + { + bool found = false; + for (int i = _olderVersions.Count - 1; i >= 0; i--) + { + VersionedValue entry = _olderVersions[i]; + if (entry.Version <= versionAndOlder) + { + resultValue = entry.Value; + found = true; + break; + } + } + + if (!found) + { + int currentVersion = current.CurrentVersion.Version; + int priorVersion = current.PriorVersion.Version; + int? oldestVersion = null; + if (_olderVersions.Count > 0) + { + oldestVersion = _olderVersions[0].Version; + } + + T oldestValue = (_olderVersions.Count > 0) ? _olderVersions[0].Value : null; + + String text = "Variables value for version '" + versionAndOlder + + "' and older could not be found" + + " (currentVersion=" + currentVersion + " priorVersion=" + priorVersion + + " oldestVersion=" + oldestVersion + " numOldVersions=" + _olderVersions.Count + + " oldestValue=" + oldestValue + ")"; + if (_errorWhenNotFound) + { + throw new IllegalStateException(text); + } + Log.Warn(text); + return current.CurrentVersion.Value; + } + } + } + } + + if (ExecutionPathDebugLog.IsEnabled && Log.IsDebugEnabled) + { + Log.Debug(".getVersion Thread " + Thread.CurrentThread.ManagedThreadId + " for '" + _name + " version " + versionAndOlder + " or older result is " + resultValue); + } + + return resultValue; + } + + /// + /// Add a value and version to the list, returning the prior value of the variable. + /// + /// for the value to add + /// to add + /// the time associated with the version + /// prior value + public Object AddValue(int version, T value, long timestamp) + { + if (ExecutionPathDebugLog.IsEnabled && Log.IsDebugEnabled) + { + Log.Debug(".addValue Thread " + Thread.CurrentThread.ManagedThreadId + " for '" + _name + "' adding version " + version + " at value " + value); + } + + // push to prior if not already used + if (_currentAndPriorValue.PriorVersion.Version == -1) + { + _currentAndPriorValue = new CurrentValue(new VersionedValue(version, value, timestamp), + _currentAndPriorValue.CurrentVersion); + return _currentAndPriorValue.PriorVersion.Value; + } + + // add to list + VersionedValue priorVersion = _currentAndPriorValue.PriorVersion; + _olderVersions.Add(priorVersion); + + // check watermarks + if (_olderVersions.Count >= _highWatermark) + { + long expireBefore = timestamp - _millisecondLifetimeOldVersions; + while (_olderVersions.Count > 0) + { + VersionedValue oldestVersion = _olderVersions[0]; + if (oldestVersion.Timestamp <= expireBefore) + { + _olderVersions.RemoveAt(0); + } + else + { + break; + } + } + } + + _currentAndPriorValue = new CurrentValue(new VersionedValue(version, value, timestamp), + _currentAndPriorValue.CurrentVersion); + return _currentAndPriorValue.PriorVersion.Value; + } + + /// Returns the current and prior version. + /// value + public CurrentValue CurrentAndPriorValue + { + get { return _currentAndPriorValue; } + } + + /// Returns the list of old versions, for testing purposes. + /// list of versions older then current and prior version + public IList> OlderVersions + { + get { return _olderVersions; } + } + + public override String ToString() + { + StringBuilder buffer = new StringBuilder(); + buffer.Append("Variable '").Append(_name).Append("' "); + buffer.Append(" current=").Append(_currentAndPriorValue.CurrentVersion.ToString()); + buffer.Append(" prior=").Append(_currentAndPriorValue.CurrentVersion.ToString()); + + int count = 0; + foreach (VersionedValue old in _olderVersions) + { + buffer.Append(" Old(").Append(count).Append(")=").Append(old.ToString()).Append("\n"); + count++; + } + return buffer.ToString(); + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/epl/view/FilterExprView.cs b/NEsper.Core/NEsper.Core/epl/view/FilterExprView.cs new file mode 100755 index 000000000..859cd3c07 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/FilterExprView.cs @@ -0,0 +1,151 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.view; + +namespace com.espertech.esper.epl.view +{ + /// + /// Simple filter view filtering events using a filter expression tree. + /// + public class FilterExprView : ViewSupport + { + private readonly ExprNode _exprNode; + private readonly ExprEvaluator _exprEvaluator; + private readonly ExprEvaluatorContext _exprEvaluatorContext; + + /// + /// Ctor. + /// + /// The expr node. + /// Filter expression evaluation impl + /// context for expression evalauation + public FilterExprView(ExprNode exprNode, ExprEvaluator exprEvaluator, ExprEvaluatorContext exprEvaluatorContext) + { + _exprNode = exprNode; + _exprEvaluator = exprEvaluator; + _exprEvaluatorContext = exprEvaluatorContext; + } + + public override EventType EventType + { + get { return Parent.EventType; } + } + + /// + /// Gets the enumerator. + /// + /// The source. + /// The filter. + /// The expr evaluator context. + /// + + private static IEnumerator GetEnumerator(IEnumerator source, ExprEvaluator filter, ExprEvaluatorContext exprEvaluatorContext) + { + var evalEventArr = new EventBean[1]; + + while (source.MoveNext()) + { + var candidate = source.Current; + evalEventArr[0] = candidate; + + var pass = (bool?)filter.Evaluate(new EvaluateParams(evalEventArr, true, exprEvaluatorContext)); + if (pass ?? false) + { + yield return candidate; + } + } + } + + /// + /// Returns an enumerator that iterates through the collection. + /// + /// + /// A that can be used to iterate through the collection. + /// + + public override IEnumerator GetEnumerator() + { + return GetEnumerator(Parent.GetEnumerator(), _exprEvaluator, _exprEvaluatorContext); + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QWhereClauseFilter(_exprNode, newData, oldData); } + + var filteredNewData = FilterEvents(_exprEvaluator, newData, true, _exprEvaluatorContext); + var filteredOldData = FilterEvents(_exprEvaluator, oldData, false, _exprEvaluatorContext); + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AWhereClauseFilter(filteredNewData, filteredOldData); } + + if ((filteredNewData != null) || (filteredOldData != null)) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QWhereClauseIR(filteredNewData, filteredOldData); } + UpdateChildren(filteredNewData, filteredOldData); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AWhereClauseIR(); } + } + } + + /// Filters events using the supplied evaluator. + /// evaluator to use + /// events to filter + /// true to indicate filter new data (istream) and not old data (rstream) + /// context for expression evalauation + /// filtered events, or null if no events got through the filter + private EventBean[] FilterEvents(ExprEvaluator exprEvaluator, EventBean[] events, bool isNewData, ExprEvaluatorContext exprEvaluatorContext) + { + if (events == null) + { + return null; + } + + var evalEventArr = new EventBean[1]; + var passResult = new bool[events.Length]; + var passCount = 0; + + for (var i = 0; i < events.Length; i++) + { + evalEventArr[0] = events[i]; + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QWhereClauseFilterEval(i, events[i], isNewData); } + var pass = (bool?)exprEvaluator.Evaluate(new EvaluateParams(evalEventArr, isNewData, exprEvaluatorContext)); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AWhereClauseFilterEval(pass); } + if ((pass != null) && (pass.Value)) + { + passResult[i] = true; + passCount++; + } + } + + if (passCount == 0) + { + return null; + } + if (passCount == events.Length) + { + return events; + } + + var resultArray = new EventBean[passCount]; + var count = 0; + for (var i = 0; i < passResult.Length; i++) + { + if (passResult[i]) + { + resultArray[count] = events[i]; + count++; + } + } + return resultArray; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputCallback.cs b/NEsper.Core/NEsper.Core/epl/view/OutputCallback.cs new file mode 100755 index 000000000..e15dc687f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputCallback.cs @@ -0,0 +1,18 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.view +{ + /// Invoked to perform output processing. + /// true if the batched events should actually be output as well as processed, false if they should just be processed + /// + /// true if output should be made even when no updating events have arrived + /// + + public delegate void OutputCallback(bool doOutput, bool forceUpdate); +} diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputCondition.cs b/NEsper.Core/NEsper.Core/epl/view/OutputCondition.cs new file mode 100755 index 000000000..ed3536486 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputCondition.cs @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.view +{ + /// + /// A condition that must be satisfied before output processing is allowed to continue. + /// Once the condition is satisfied, it makes a callback to continue output processing. + /// + public interface OutputCondition + { + /// Update the output condition. + /// number of new events incoming + /// number of old events incoming + void UpdateOutputCondition(int newEventsCount, int oldEventsCount); + + void Terminated(); + + void Stop(); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputConditionBase.cs b/NEsper.Core/NEsper.Core/epl/view/OutputConditionBase.cs new file mode 100755 index 000000000..27f7a2e11 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputConditionBase.cs @@ -0,0 +1,33 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.view +{ + public abstract class OutputConditionBase : OutputCondition + { + protected readonly OutputCallback OutputCallback; + + protected OutputConditionBase(OutputCallback outputCallback) + { + OutputCallback = outputCallback; + } + + public virtual void Terminated() + { + OutputCallback.Invoke(true, true); + } + + /// Update the output condition. + /// number of new events incoming + /// number of old events incoming + public abstract void UpdateOutputCondition(int newEventsCount, int oldEventsCount); + + /// Stops this instance. + public abstract void Stop(); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputConditionCount.cs b/NEsper.Core/NEsper.Core/epl/view/OutputConditionCount.cs new file mode 100755 index 000000000..256de80a7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputConditionCount.cs @@ -0,0 +1,111 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.variable; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.view +{ + /// + /// Output limit condition that is satisfied when either the total number of new events arrived or the total number of old events arrived is greater than a preset value. + /// + public sealed class OutputConditionCount + : OutputConditionBase + , OutputCondition + { + private const bool DO_OUTPUT = true; + private const bool FORCE_UPDATE = false; + + private long _eventRate; + private int _newEventsCount; + private int _oldEventsCount; + + private VariableReader _variableReader; + + public OutputConditionCount(OutputCallback outputCallback, long eventRate, VariableReader variableReader) + : base(outputCallback) + { + _eventRate = eventRate; + _variableReader = variableReader; + } + + /// Returns the number of new events. + /// number of new events + public int NewEventsCount + { + get { return _newEventsCount; } + } + + /// Returns the number of old events. + /// number of old events + public int OldEventsCount + { + get { return _oldEventsCount; } + } + + public override void UpdateOutputCondition(int newDataCount, int oldDataCount) + { + if (_variableReader != null) + { + var value = _variableReader.Value; + if (value != null) + { + _eventRate = value.AsLong(); + } + } + + _newEventsCount += newDataCount; + _oldEventsCount += oldDataCount; + + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled)) + { + Log.Debug(".updateBatchCondition, " + + " newEventsCount==" + _newEventsCount + + " oldEventsCount==" + _oldEventsCount); + } + + if (IsSatisfied) + { + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled)) + { + Log.Debug(".UpdateOutputCondition() condition satisfied"); + } + _newEventsCount = 0; + _oldEventsCount = 0; + OutputCallback.Invoke(DO_OUTPUT, FORCE_UPDATE); + } + } + + public override String ToString() + { + return GetType().FullName + + " eventRate=" + _eventRate; + } + + private bool IsSatisfied + { + get { return (_newEventsCount >= _eventRate) || (_oldEventsCount >= _eventRate); } + } + + public override void Terminated() + { + OutputCallback.Invoke(true, true); + } + + public override void Stop() + { + // no action required + } + + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputConditionCountFactory.cs b/NEsper.Core/NEsper.Core/epl/view/OutputConditionCountFactory.cs new file mode 100755 index 000000000..3b688cad5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputConditionCountFactory.cs @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.variable; + +namespace com.espertech.esper.epl.view +{ + public sealed class OutputConditionCountFactory : OutputConditionFactory + { + internal long EventRate; + internal readonly VariableMetaData VariableMetaData; + + /// + /// Constructor. + /// + /// is the number of old or new events thatmust arrive in order for the condition to be satisfied + /// is the variable metadata, if a variable was supplied, else null + public OutputConditionCountFactory(int eventRate, VariableMetaData variableMetaData) + { + if ((eventRate < 1) && (variableMetaData == null)) + { + throw new ArgumentException("Limiting output by event count requires an event count of at least 1 or a variable name"); + } + EventRate = eventRate; + VariableMetaData = variableMetaData; + } + + public OutputCondition Make(AgentInstanceContext agentInstanceContext, OutputCallback outputCallback) + { + VariableReader variableReader = null; + if (VariableMetaData != null) + { + variableReader = agentInstanceContext.StatementContext.VariableService.GetReader( + VariableMetaData.VariableName, agentInstanceContext.AgentInstanceId); + } + + return new OutputConditionCount(outputCallback, EventRate, variableReader); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputConditionCrontab.cs b/NEsper.Core/NEsper.Core/epl/view/OutputConditionCrontab.cs new file mode 100755 index 000000000..7018670e9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputConditionCrontab.cs @@ -0,0 +1,113 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.schedule; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.view +{ + /// Output condition handling crontab-at schedule output. + public sealed class OutputConditionCrontab + : OutputConditionBase + , OutputCondition + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private const bool DO_OUTPUT = true; + private const bool FORCE_UPDATE = true; + + private readonly AgentInstanceContext _context; + private readonly ScheduleSpec _scheduleSpec; + private readonly long _scheduleSlot; + private long? _currentReferencePoint; + private bool _isCallbackScheduled; + + public OutputConditionCrontab(OutputCallback outputCallback, AgentInstanceContext context, bool isStartConditionOnCreation, ScheduleSpec scheduleSpec) + : base(outputCallback) + { + _context = context; + _scheduleSpec = scheduleSpec; + _scheduleSlot = context.StatementContext.ScheduleBucket.AllocateSlot(); + if (isStartConditionOnCreation) { + UpdateOutputCondition(0, 0); + } + } + + public override void UpdateOutputCondition(int newEventsCount, int oldEventsCount) + { + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled)) { + Log.Debug(".updateOutputCondition, " + + " newEventsCount==" + newEventsCount + + " oldEventsCount==" + oldEventsCount); + } + + if (_currentReferencePoint == null) { + _currentReferencePoint = _context.StatementContext.SchedulingService.Time; + } + + // Schedule the next callback if there is none currently scheduled + if (!_isCallbackScheduled) { + ScheduleCallback(); + } + } + + public override String ToString() { + return GetType().FullName + + " spec=" + _scheduleSpec; + } + + private void ScheduleCallback() { + _isCallbackScheduled = true; + var current = _context.StatementContext.SchedulingService.Time; + + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled)) { + Log.Debug(".scheduleCallback Scheduled new callback for " + + " now=" + current + + " currentReferencePoint=" + _currentReferencePoint + + " spec=" + _scheduleSpec); + } + + var callback = new ProxyScheduleHandleCallback + { + ProcScheduledTrigger = extensionServicesContext => { + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().QOutputRateConditionScheduledEval(); + } + _isCallbackScheduled = false; + OutputCallback.Invoke(DO_OUTPUT, FORCE_UPDATE); + ScheduleCallback(); + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().AOutputRateConditionScheduledEval(); + } + } + }; + var handle = new EPStatementHandleCallback(_context.EpStatementAgentInstanceHandle, callback); + var schedulingService = _context.StatementContext.SchedulingService; + var engineImportService = _context.StatementContext.EngineImportService; + var nextScheduledTime = ScheduleComputeHelper.ComputeDeltaNextOccurance(_scheduleSpec, schedulingService.Time, engineImportService.TimeZone, engineImportService.TimeAbacus); + schedulingService.Add(nextScheduledTime, handle, _scheduleSlot); + } + + public override void Terminated() + { + OutputCallback.Invoke(true, true); + } + + public override void Stop() + { + // no action required + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputConditionCrontabFactory.cs b/NEsper.Core/NEsper.Core/epl/view/OutputConditionCrontabFactory.cs new file mode 100755 index 000000000..a19f54c3b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputConditionCrontabFactory.cs @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.schedule; + +namespace com.espertech.esper.epl.view +{ + /// Output condition handling crontab-at schedule output. + public class OutputConditionCrontabFactory : OutputConditionFactory { + protected readonly ExprEvaluator[] scheduleSpecEvaluators; + protected readonly bool isStartConditionOnCreation; + + public OutputConditionCrontabFactory(IList scheduleSpecExpressionList, + StatementContext statementContext, + bool isStartConditionOnCreation) + { + this.scheduleSpecEvaluators = ExprNodeUtility.CrontabScheduleValidate(ExprNodeOrigin.OUTPUTLIMIT, scheduleSpecExpressionList, statementContext, false); + this.isStartConditionOnCreation = isStartConditionOnCreation; + } + + public OutputCondition Make(AgentInstanceContext agentInstanceContext, OutputCallback outputCallback) { + ScheduleSpec scheduleSpec = ExprNodeUtility.CrontabScheduleBuild(scheduleSpecEvaluators, agentInstanceContext); + return new OutputConditionCrontab(outputCallback, agentInstanceContext, isStartConditionOnCreation, scheduleSpec); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputConditionExpression.cs b/NEsper.Core/NEsper.Core/epl/view/OutputConditionExpression.cs new file mode 100755 index 000000000..74ac44bef --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputConditionExpression.cs @@ -0,0 +1,263 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events.arr; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.schedule; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.view +{ + /// + /// Output condition for output rate limiting that handles when-then expressions for controlling output. + /// + public class OutputConditionExpression + : OutputConditionBase + , OutputCondition + { + private static readonly ILog Log = + LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly AgentInstanceContext _agentInstanceContext; + private readonly OutputConditionExpressionFactory _parent; + + private readonly long _scheduleSlot; + private bool _isCallbackScheduled; + private bool _ignoreVariableCallbacks; + private readonly ObjectArrayEventBean _builtinProperties; + private readonly EventBean[] _eventsPerStream; + + // ongoing builtin properties + private int _totalNewEventsCount; + private int _totalOldEventsCount; + private int _totalNewEventsSum; + private int _totalOldEventsSum; + private long? _lastOutputTimestamp; + private EPStatementHandleCallback _scheduleHandle; + + public OutputConditionExpression( + OutputCallback outputCallback, + AgentInstanceContext agentInstanceContext, + OutputConditionExpressionFactory parent, + bool isStartConditionOnCreation) + : base(outputCallback) + { + _agentInstanceContext = agentInstanceContext; + _parent = parent; + + _scheduleSlot = agentInstanceContext.StatementContext.ScheduleBucket.AllocateSlot(); + _eventsPerStream = new EventBean[1]; + + if (parent.BuiltinPropertiesEventType != null) + { + _builtinProperties = new ObjectArrayEventBean( + OutputConditionExpressionTypeUtil.OAPrototype, parent.BuiltinPropertiesEventType); + _lastOutputTimestamp = agentInstanceContext.StatementContext.SchedulingService.Time; + } + + if (parent.VariableNames != null) + { + // if using variables, register a callback on the change of the variable + foreach (String variableName in parent.VariableNames) + { + var theVariableName = variableName; + agentInstanceContext.StatementContext.VariableService.RegisterCallback( + variableName, agentInstanceContext.AgentInstanceId, Update); + agentInstanceContext.AddTerminationCallback( + new ProxyStopCallback( + () => _agentInstanceContext.StatementContext.VariableService.UnregisterCallback( + theVariableName, agentInstanceContext.AgentInstanceId, Update))); + } + } + + if (isStartConditionOnCreation) + { + Update(0, 0); + } + } + + public override void UpdateOutputCondition(int newEventsCount, int oldEventsCount) + { + _totalNewEventsCount += newEventsCount; + _totalOldEventsCount += oldEventsCount; + _totalNewEventsSum += newEventsCount; + _totalOldEventsSum += oldEventsCount; + + bool isOutput = Evaluate(_parent.WhenExpressionNodeEval); + if (isOutput) + { + ExecuteThenAssignments(); + OutputCallback.Invoke(true, true); + ResetBuiltinProperties(); + } + } + + public void Update(Object newValue, Object oldValue) + { + if (_ignoreVariableCallbacks) + { + Log.Debug(".Update Ignoring variable callback"); + return; + } + + _agentInstanceContext.StatementContext.VariableService.SetLocalVersion(); + bool isOutput = Evaluate(_parent.WhenExpressionNodeEval); + if ((isOutput) && (!_isCallbackScheduled)) + { + ScheduleCallback(); + } + } + + public override void Stop() + { + if (_scheduleHandle != null) + { + _agentInstanceContext.StatementContext.SchedulingService.Remove(_scheduleHandle, _scheduleSlot); + _scheduleHandle = null; + } + } + + public override void Terminated() + { + bool output = true; + if (_parent.AndWhenTerminatedExpressionNodeEval != null) + { + output = Evaluate(_parent.AndWhenTerminatedExpressionNodeEval); + } + if (_parent.VariableReadWritePackageAfterTerminated != null) + { + if (_builtinProperties != null) + { + PopulateBuiltinProps(); + _eventsPerStream[0] = _builtinProperties; + } + + _ignoreVariableCallbacks = true; + try + { + _parent.VariableReadWritePackageAfterTerminated.WriteVariables( + _agentInstanceContext.StatementContext.VariableService, _eventsPerStream, null, + _agentInstanceContext); + } + finally + { + _ignoreVariableCallbacks = false; + } + } + if (output) + { + base.Terminated(); + } + } + + private bool Evaluate(ExprEvaluator evaluator) + { + if (_builtinProperties != null) + { + PopulateBuiltinProps(); + _eventsPerStream[0] = _builtinProperties; + } + + var result = false; + var output = (bool?)evaluator.Evaluate(new EvaluateParams(_eventsPerStream, true, _agentInstanceContext)); + if ((output != null) && (output.Value)) + { + result = true; + } + + return result; + } + + private void ScheduleCallback() + { + _isCallbackScheduled = true; + long current = _agentInstanceContext.StatementContext.SchedulingService.Time; + + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled)) + { + Log.Debug( + ".scheduleCallback Scheduled new callback for " + + " afterMsec=" + 0 + + " now=" + current); + } + + ScheduleHandleCallback callback = new ProxyScheduleHandleCallback + { + ProcScheduledTrigger = extensionServicesContext => Instrument.With( + i => i.QOutputRateConditionScheduledEval(), + i => i.AOutputRateConditionScheduledEval(), + () => + { + _isCallbackScheduled = false; + OutputCallback.Invoke(true, true); + ResetBuiltinProperties(); + }) + }; + _scheduleHandle = new EPStatementHandleCallback( + _agentInstanceContext.EpStatementAgentInstanceHandle, callback); + _agentInstanceContext.StatementContext.SchedulingService.Add(0, _scheduleHandle, _scheduleSlot); + _agentInstanceContext.AddTerminationCallback(new ProxyStopCallback(Stop)); + + // execute assignments + ExecuteThenAssignments(); + } + + private void ExecuteThenAssignments() + { + if (_parent.VariableReadWritePackage != null) + { + if (_builtinProperties != null) + { + PopulateBuiltinProps(); + _eventsPerStream[0] = _builtinProperties; + } + + _ignoreVariableCallbacks = true; + try + { + _parent.VariableReadWritePackage.WriteVariables( + _agentInstanceContext.StatementContext.VariableService, _eventsPerStream, null, + _agentInstanceContext); + } + finally + { + _ignoreVariableCallbacks = false; + } + } + } + + private void ResetBuiltinProperties() + { + if (_builtinProperties != null) + { + _totalNewEventsCount = 0; + _totalOldEventsCount = 0; + _lastOutputTimestamp = _agentInstanceContext.StatementContext.SchedulingService.Time; + } + } + + private void PopulateBuiltinProps() + { + OutputConditionExpressionTypeUtil.Populate( + _builtinProperties.Properties, + _totalNewEventsCount, + _totalOldEventsCount, + _totalNewEventsSum, + _totalOldEventsSum, + _lastOutputTimestamp); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputConditionExpressionFactory.cs b/NEsper.Core/NEsper.Core/epl/view/OutputConditionExpressionFactory.cs new file mode 100755 index 000000000..743cc5d87 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputConditionExpressionFactory.cs @@ -0,0 +1,159 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.visitor; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.variable; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.view +{ + /// + /// Output condition for output rate limiting that handles when-then expressions for controlling output. + /// + public class OutputConditionExpressionFactory : OutputConditionFactory + { + private readonly ExprEvaluator _andWhenTerminatedExpressionNodeEval; + private readonly EventType _builtinPropertiesEventType; + private readonly bool _isStartConditionOnCreation; + private readonly ISet _variableNames; + private readonly VariableReadWritePackage _variableReadWritePackage; + private readonly VariableReadWritePackage _variableReadWritePackageAfterTerminated; + private readonly ExprEvaluator _whenExpressionNodeEval; + + public OutputConditionExpressionFactory( + ExprNode whenExpressionNode, + IList assignments, + StatementContext statementContext, + ExprNode andWhenTerminatedExpr, + IList afterTerminateAssignments, + bool isStartConditionOnCreation) + { + _whenExpressionNodeEval = whenExpressionNode.ExprEvaluator; + _andWhenTerminatedExpressionNodeEval = andWhenTerminatedExpr != null ? andWhenTerminatedExpr.ExprEvaluator : null; + _isStartConditionOnCreation = isStartConditionOnCreation; + + // determine if using variables + var variableVisitor = new ExprNodeVariableVisitor(statementContext.VariableService); + whenExpressionNode.Accept(variableVisitor); + _variableNames = variableVisitor.VariableNames; + + // determine if using properties + bool containsBuiltinProperties = ContainsBuiltinProperties(whenExpressionNode); + if (!containsBuiltinProperties && assignments != null) + { + foreach (OnTriggerSetAssignment assignment in assignments) + { + if (ContainsBuiltinProperties(assignment.Expression)) + { + containsBuiltinProperties = true; + } + } + } + if (!containsBuiltinProperties && _andWhenTerminatedExpressionNodeEval != null) + { + containsBuiltinProperties = ContainsBuiltinProperties(andWhenTerminatedExpr); + } + if (!containsBuiltinProperties && afterTerminateAssignments != null) + { + foreach (OnTriggerSetAssignment assignment in afterTerminateAssignments) + { + if (ContainsBuiltinProperties(assignment.Expression)) + { + containsBuiltinProperties = true; + } + } + } + + if (containsBuiltinProperties) + { + _builtinPropertiesEventType = GetBuiltInEventType(statementContext.EventAdapterService); + } + + if (assignments != null) + { + _variableReadWritePackage = new VariableReadWritePackage( + assignments, statementContext.VariableService, statementContext.EventAdapterService); + } + else + { + _variableReadWritePackage = null; + } + + if (afterTerminateAssignments != null) + { + _variableReadWritePackageAfterTerminated = new VariableReadWritePackage( + afterTerminateAssignments, statementContext.VariableService, statementContext.EventAdapterService); + } + else + { + _variableReadWritePackageAfterTerminated = null; + } + } + + public OutputCondition Make(AgentInstanceContext agentInstanceContext, OutputCallback outputCallback) + { + return new OutputConditionExpression(outputCallback, agentInstanceContext, this, _isStartConditionOnCreation); + } + + /// + /// Build the event type for built-in properties. + /// + /// event adapters + /// event type + public static EventType GetBuiltInEventType(EventAdapterService eventAdapterService) + { + return eventAdapterService.CreateAnonymousObjectArrayType( + typeof (OutputConditionExpressionFactory).FullName, OutputConditionExpressionTypeUtil.TYPEINFO); + } + + public ExprEvaluator WhenExpressionNodeEval + { + get { return _whenExpressionNodeEval; } + } + + public ExprEvaluator AndWhenTerminatedExpressionNodeEval + { + get { return _andWhenTerminatedExpressionNodeEval; } + } + + public VariableReadWritePackage VariableReadWritePackage + { + get { return _variableReadWritePackage; } + } + + public VariableReadWritePackage VariableReadWritePackageAfterTerminated + { + get { return _variableReadWritePackageAfterTerminated; } + } + + public EventType BuiltinPropertiesEventType + { + get { return _builtinPropertiesEventType; } + } + + public ISet VariableNames + { + get { return _variableNames; } + } + + private bool ContainsBuiltinProperties(ExprNode expr) + { + var propertyVisitor = new ExprNodeIdentifierVisitor(false); + expr.Accept(propertyVisitor); + return !propertyVisitor.ExprProperties.IsEmpty(); + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputConditionExpressionTypeUtil.cs b/NEsper.Core/NEsper.Core/epl/view/OutputConditionExpressionTypeUtil.cs new file mode 100755 index 000000000..931780fc5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputConditionExpressionTypeUtil.cs @@ -0,0 +1,44 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.epl.view +{ + public class OutputConditionExpressionTypeUtil + { + public readonly static LinkedHashMap TYPEINFO; + + static OutputConditionExpressionTypeUtil() + { + TYPEINFO = new LinkedHashMap(); + TYPEINFO.Put("count_insert", typeof(int)); + TYPEINFO.Put("count_remove", typeof(int)); + TYPEINFO.Put("count_insert_total", typeof(int)); + TYPEINFO.Put("count_remove_total", typeof(int)); + TYPEINFO.Put("last_output_timestamp", typeof(long)); + } + + public static object[] OAPrototype + { + get { return new Object[TYPEINFO.Count]; } + } + + public static void Populate(Object[] builtinProperties, int totalNewEventsCount, int totalOldEventsCount, + int totalNewEventsSum, int totalOldEventsSum, long? lastOutputTimestamp) + { + builtinProperties[0] = totalNewEventsCount; + builtinProperties[1] = totalOldEventsCount; + builtinProperties[2] = totalNewEventsSum; + builtinProperties[3] = totalOldEventsSum; + builtinProperties[4] = lastOutputTimestamp; + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputConditionFactory.cs b/NEsper.Core/NEsper.Core/epl/view/OutputConditionFactory.cs new file mode 100755 index 000000000..3e9c8f2fe --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputConditionFactory.cs @@ -0,0 +1,17 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.context.util; + +namespace com.espertech.esper.epl.view +{ + public interface OutputConditionFactory + { + OutputCondition Make(AgentInstanceContext agentInstanceContext, OutputCallback outputCallback); + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputConditionFactoryFactory.cs b/NEsper.Core/NEsper.Core/epl/view/OutputConditionFactoryFactory.cs new file mode 100755 index 000000000..369ea6601 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputConditionFactoryFactory.cs @@ -0,0 +1,132 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.variable; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.view +{ + /// Factory for output condition instances. + public class OutputConditionFactoryFactory + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public static OutputConditionFactory CreateCondition( + OutputLimitSpec outputLimitSpec, + StatementContext statementContext, + bool isGrouped, + bool isWithHavingClause, + bool isStartConditionOnCreation, + ResultSetProcessorHelperFactory resultSetProcessorHelperFactory) + { + if (outputLimitSpec == null) + { + return new OutputConditionNullFactory(); + } + + // Check if a variable is present + VariableMetaData variableMetaData = null; + if (outputLimitSpec.VariableName != null) + { + variableMetaData = statementContext.VariableService.GetVariableMetaData(outputLimitSpec.VariableName); + if (variableMetaData == null) + { + throw new ExprValidationException( + "Variable named '" + outputLimitSpec.VariableName + "' has not been declared"); + } + string message = VariableServiceUtil.CheckVariableContextName( + statementContext.ContextDescriptor, variableMetaData); + if (message != null) + { + throw new ExprValidationException(message); + } + } + + if (outputLimitSpec.DisplayLimit == OutputLimitLimitType.FIRST && isGrouped) + { + return new OutputConditionNullFactory(); + } + + if (outputLimitSpec.RateType == OutputLimitRateType.CRONTAB) + { + return resultSetProcessorHelperFactory.MakeOutputConditionCrontab( + outputLimitSpec.CrontabAtSchedule, statementContext, isStartConditionOnCreation); + } + else if (outputLimitSpec.RateType == OutputLimitRateType.WHEN_EXPRESSION) + { + return resultSetProcessorHelperFactory.MakeOutputConditionExpression( + outputLimitSpec.WhenExpressionNode, outputLimitSpec.ThenExpressions, statementContext, + outputLimitSpec.AndAfterTerminateExpr, outputLimitSpec.AndAfterTerminateThenExpressions, + isStartConditionOnCreation); + } + else if (outputLimitSpec.RateType == OutputLimitRateType.EVENTS) + { + if (Log.IsDebugEnabled) + { + Log.Debug(".createCondition creating OutputConditionCount with event rate " + outputLimitSpec); + } + + if ((variableMetaData != null) && (!variableMetaData.VariableType.IsNumericNonFP())) + { + throw new ArgumentException( + "Variable named '" + outputLimitSpec.VariableName + "' must be type integer, long or short"); + } + + int rate = -1; + if (outputLimitSpec.Rate != null) + { + rate = outputLimitSpec.Rate.AsInt(); + } + return resultSetProcessorHelperFactory.MakeOutputConditionCount( + rate, variableMetaData, statementContext); + } + else if (outputLimitSpec.RateType == OutputLimitRateType.TERM) + { + if (outputLimitSpec.AndAfterTerminateExpr == null && + (outputLimitSpec.AndAfterTerminateThenExpressions == null || + outputLimitSpec.AndAfterTerminateThenExpressions.IsEmpty())) + { + return new OutputConditionTermFactory(); + } + else + { + return resultSetProcessorHelperFactory.MakeOutputConditionExpression( + new ExprConstantNodeImpl(false), Collections.GetEmptyList(), + statementContext, outputLimitSpec.AndAfterTerminateExpr, + outputLimitSpec.AndAfterTerminateThenExpressions, isStartConditionOnCreation); + } + } + else + { + if (Log.IsDebugEnabled) + { + Log.Debug( + ".createCondition creating OutputConditionTime with interval length " + outputLimitSpec.Rate); + } + if ((variableMetaData != null) && (!variableMetaData.VariableType.IsNumeric())) + { + throw new ArgumentException( + "Variable named '" + outputLimitSpec.VariableName + "' must be of numeric type"); + } + + return resultSetProcessorHelperFactory.MakeOutputConditionTime( + outputLimitSpec.TimePeriodExpr, isStartConditionOnCreation); + } + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputConditionFirst.cs b/NEsper.Core/NEsper.Core/epl/view/OutputConditionFirst.cs new file mode 100755 index 000000000..01edbe21a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputConditionFirst.cs @@ -0,0 +1,64 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.context.util; + +namespace com.espertech.esper.epl.view +{ + /// + /// An output condition that is satisfied at the first event of either a time-based or count-based batch. + /// + public class OutputConditionFirst : OutputConditionBase, OutputCondition + { + private readonly OutputCondition _innerCondition; + private bool _witnessedFirst; + + public OutputConditionFirst(OutputCallback outputCallback, AgentInstanceContext agentInstanceContext, OutputConditionFactory innerConditionFactory) + + : base(outputCallback) + { + OutputCallback localCallback = CreateCallbackToLocal(); + _innerCondition = innerConditionFactory.Make(agentInstanceContext, localCallback); + _witnessedFirst = false; + } + + public override void UpdateOutputCondition(int newEventsCount, int oldEventsCount) + { + if (!_witnessedFirst) + { + _witnessedFirst = true; + const bool doOutput = true; + const bool forceUpdate = false; + OutputCallback.Invoke(doOutput, forceUpdate); + } + _innerCondition.UpdateOutputCondition(newEventsCount, oldEventsCount); + } + + private OutputCallback CreateCallbackToLocal() + { + return (doOutput, forceUpdate) => ContinueOutputProcessing(forceUpdate); + } + + public override void Terminated() + { + OutputCallback.Invoke(true, true); + } + + public override void Stop() + { + // no action required + } + + private void ContinueOutputProcessing(bool forceUpdate) + { + var doOutput = !_witnessedFirst; + OutputCallback.Invoke(doOutput, forceUpdate); + _witnessedFirst = false; + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputConditionFirstFactory.cs b/NEsper.Core/NEsper.Core/epl/view/OutputConditionFirstFactory.cs new file mode 100755 index 000000000..1484a37ee --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputConditionFirstFactory.cs @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.epl.view +{ + /// + /// An output condition that is satisfied at the first event of either a time-based or count-based batch. + /// + public class OutputConditionFirstFactory : OutputConditionFactory + { + private readonly OutputConditionFactory _innerConditionFactory; + + public OutputConditionFirstFactory(OutputLimitSpec outputLimitSpec, StatementContext statementContext, bool isGrouped, bool isWithHavingClause, ResultSetProcessorHelperFactory resultSetProcessorHelperFactory) + { + var innerSpec = new OutputLimitSpec(outputLimitSpec.Rate, + outputLimitSpec.VariableName, + outputLimitSpec.RateType, OutputLimitLimitType.DEFAULT, + outputLimitSpec.WhenExpressionNode, + outputLimitSpec.ThenExpressions, + outputLimitSpec.CrontabAtSchedule, + outputLimitSpec.TimePeriodExpr, + outputLimitSpec.AfterTimePeriodExpr, + outputLimitSpec.AfterNumberOfEvents, + outputLimitSpec.IsAndAfterTerminate, + outputLimitSpec.AndAfterTerminateExpr, + outputLimitSpec.AndAfterTerminateThenExpressions); + + _innerConditionFactory = OutputConditionFactoryFactory.CreateCondition(innerSpec, statementContext, isGrouped, isWithHavingClause, false, resultSetProcessorHelperFactory); + } + + public OutputCondition Make(AgentInstanceContext agentInstanceContext, OutputCallback outputCallback) { + return new OutputConditionFirst(outputCallback, agentInstanceContext, _innerConditionFactory); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputConditionNull.cs b/NEsper.Core/NEsper.Core/epl/view/OutputConditionNull.cs new file mode 100755 index 000000000..60d91c7fe --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputConditionNull.cs @@ -0,0 +1,39 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.view +{ + /// An empty output condition that is always satisfied. + public class OutputConditionNull : OutputConditionBase, OutputCondition + { + private static readonly bool DO_OUTPUT = true; + private static readonly bool FORCE_UPDATE = false; + + /// Ctor. + /// is the callback to make once the condition is satisfied + public OutputConditionNull(OutputCallback outputCallback) + : base(outputCallback) + { + } + + public override void UpdateOutputCondition(int newEventsCount, int oldEventsCount) + { + OutputCallback.Invoke(DO_OUTPUT, FORCE_UPDATE); + } + + public override void Terminated() + { + OutputCallback.Invoke(true, true); + } + + public override void Stop() + { + // no action required + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputConditionNullFactory.cs b/NEsper.Core/NEsper.Core/epl/view/OutputConditionNullFactory.cs new file mode 100755 index 000000000..4e9cde4f4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputConditionNullFactory.cs @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.context.util; + + +namespace com.espertech.esper.epl.view +{ + public class OutputConditionNullFactory : OutputConditionFactory { + + public OutputCondition Make(AgentInstanceContext agentInstanceContext, OutputCallback outputCallback) { + return new OutputConditionNull(outputCallback); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputConditionPolled.cs b/NEsper.Core/NEsper.Core/epl/view/OutputConditionPolled.cs new file mode 100755 index 000000000..23b30684f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputConditionPolled.cs @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.view +{ + public interface OutputConditionPolled + { + OutputConditionPolledState State { get; } + + /// Update the output condition. + /// number of new events incoming + /// number of old events incoming + bool UpdateOutputCondition(int newEventsCount, int oldEventsCount); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputConditionPolledCount.cs b/NEsper.Core/NEsper.Core/epl/view/OutputConditionPolledCount.cs new file mode 100755 index 000000000..02ca5d5a4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputConditionPolledCount.cs @@ -0,0 +1,79 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.variable; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.view +{ + /// + /// Output limit condition that is satisfied when either + /// the total number of new events arrived or the total number + /// of old events arrived is greater than a preset value. + /// + public sealed class OutputConditionPolledCount : OutputConditionPolled + { + private readonly OutputConditionPolledCountFactory _factory; + private readonly OutputConditionPolledCountState _state; + private readonly VariableReader _optionalVariableReader; + + public OutputConditionPolledCount(OutputConditionPolledCountFactory factory, OutputConditionPolledCountState state, VariableReader optionalVariableReader) + { + _factory = factory; + _state = state; + _optionalVariableReader = optionalVariableReader; + } + + public OutputConditionPolledState State + { + get { return _state; } + } + + public bool UpdateOutputCondition(int newDataCount, int oldDataCount) + { + if (_optionalVariableReader != null) + { + var value = _optionalVariableReader.Value; + if (value != null) + { + _state.EventRate = value.AsLong(); + } + } + + _state.NewEventsCount = _state.NewEventsCount + newDataCount; + _state.OldEventsCount = _state.OldEventsCount + oldDataCount; + + if (IsSatisfied || _state.IsFirst) + { + if ((ExecutionPathDebugLog.IsEnabled) && (log.IsDebugEnabled)) + { + log.Debug(".updateOutputCondition() condition satisfied"); + } + _state.IsFirst = false; + _state.NewEventsCount = 0; + _state.OldEventsCount = 0; + return true; + } + + return false; + } + + private bool IsSatisfied + { + get { return (_state.NewEventsCount >= _state.EventRate) || (_state.OldEventsCount >= _state.EventRate); } + } + + private static readonly ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputConditionPolledCountFactory.cs b/NEsper.Core/NEsper.Core/epl/view/OutputConditionPolledCountFactory.cs new file mode 100755 index 000000000..0b15fff01 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputConditionPolledCountFactory.cs @@ -0,0 +1,61 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.variable; + +namespace com.espertech.esper.epl.view +{ + /// + /// Output limit condition that is satisfied when either + /// the total number of new events arrived or the total number + /// of old events arrived is greater than a preset value. + /// + public sealed class OutputConditionPolledCountFactory : OutputConditionPolledFactory + { + private readonly int _eventRate; + private readonly StatementContext _statementContext; + private readonly string _variableName; + + /// + /// Constructor. + /// + /// is the number of old or new events thatmust arrive in order for the condition to be satisfied + /// + public OutputConditionPolledCountFactory(int eventRate, StatementContext statementContext, string variableName) + { + if ((eventRate < 1) && (variableName == null)) { + throw new ArgumentException("Limiting output by event count requires an event count of at least 1 or a variable name"); + } + _eventRate = eventRate; + _statementContext = statementContext; + _variableName = variableName; + } + + public OutputConditionPolled MakeNew(AgentInstanceContext agentInstanceContext) + { + OutputConditionPolledCountState state = new OutputConditionPolledCountState(_eventRate, _eventRate, _eventRate, true); + return new OutputConditionPolledCount(this, state, GetVariableReader(agentInstanceContext)); + } + + public OutputConditionPolled MakeFromState(AgentInstanceContext agentInstanceContext, OutputConditionPolledState state) + { + return new OutputConditionPolledCount(this, (OutputConditionPolledCountState) state, GetVariableReader(agentInstanceContext)); + } + + private VariableReader GetVariableReader(AgentInstanceContext agentInstanceContext) + { + if (_variableName == null) + return null; + return _statementContext.VariableService.GetReader(_variableName, agentInstanceContext.AgentInstanceId); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputConditionPolledCountState.cs b/NEsper.Core/NEsper.Core/epl/view/OutputConditionPolledCountState.cs new file mode 100755 index 000000000..e6fcc761c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputConditionPolledCountState.cs @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.view +{ + public class OutputConditionPolledCountState : OutputConditionPolledState + { + public OutputConditionPolledCountState(long eventRate, int newEventsCount, int oldEventsCount, bool isFirst) { + EventRate = eventRate; + NewEventsCount = newEventsCount; + OldEventsCount = oldEventsCount; + IsFirst = isFirst; + } + + public long EventRate { get; set; } + + public int NewEventsCount { get; set; } + + public int OldEventsCount { get; set; } + + public bool IsFirst { get; set; } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputConditionPolledCrontab.cs b/NEsper.Core/NEsper.Core/epl/view/OutputConditionPolledCrontab.cs new file mode 100755 index 000000000..c7d7deb22 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputConditionPolledCrontab.cs @@ -0,0 +1,58 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.util; +using com.espertech.esper.schedule; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.view +{ + /// Output condition handling crontab-at schedule output. + public sealed class OutputConditionPolledCrontab : OutputConditionPolled + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly AgentInstanceContext _agentInstanceContext; + private readonly OutputConditionPolledCrontabState _state; + + public OutputConditionPolledCrontab(AgentInstanceContext agentInstanceContext, OutputConditionPolledCrontabState state) { + _agentInstanceContext = agentInstanceContext; + _state = state; + } + + public OutputConditionPolledState State + { + get { return _state; } + } + + public bool UpdateOutputCondition(int newEventsCount, int oldEventsCount) { + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled)) { + Log.Debug(".updateOutputCondition, " + + " newEventsCount==" + newEventsCount + + " oldEventsCount==" + oldEventsCount); + } + + var output = false; + var currentTime = _agentInstanceContext.StatementContext.SchedulingService.Time; + var engineImportService = _agentInstanceContext.StatementContext.EngineImportService; + if (_state.CurrentReferencePoint == null) { + _state.CurrentReferencePoint = currentTime; + _state.NextScheduledTime = ScheduleComputeHelper.ComputeNextOccurance(_state.ScheduleSpec, currentTime, engineImportService.TimeZone, engineImportService.TimeAbacus); + output = true; + } + + if (_state.NextScheduledTime <= currentTime) { + _state.NextScheduledTime = ScheduleComputeHelper.ComputeNextOccurance(_state.ScheduleSpec, currentTime, engineImportService.TimeZone, engineImportService.TimeAbacus); + output = true; + } + + return output; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputConditionPolledCrontabFactory.cs b/NEsper.Core/NEsper.Core/epl/view/OutputConditionPolledCrontabFactory.cs new file mode 100755 index 000000000..4a5c449e1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputConditionPolledCrontabFactory.cs @@ -0,0 +1,105 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.schedule; + +namespace com.espertech.esper.epl.view +{ + /// Output condition handling crontab-at schedule output. + public sealed class OutputConditionPolledCrontabFactory : OutputConditionPolledFactory + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly ExprEvaluator[] _expressions; + + public OutputConditionPolledCrontabFactory( + IList scheduleSpecExpressionList, + StatementContext statementContext) + { + var validationContext = new ExprValidationContext( + new StreamTypeServiceImpl(statementContext.EngineURI, false), + statementContext.EngineImportService, + statementContext.StatementExtensionServicesContext, null, + statementContext.SchedulingService, + statementContext.VariableService, + statementContext.TableService, + new ExprEvaluatorContextStatement(statementContext, false), + statementContext.EventAdapterService, + statementContext.StatementName, + statementContext.StatementId, + statementContext.Annotations, + statementContext.ContextDescriptor, + statementContext.ScriptingService, + false, false, false, false, null, false); + + _expressions = new ExprEvaluator[scheduleSpecExpressionList.Count]; + + int count = 0; + foreach (ExprNode parameters in scheduleSpecExpressionList) + { + ExprNode node = ExprNodeUtility.GetValidatedSubtree( + ExprNodeOrigin.OUTPUTLIMIT, parameters, validationContext); + _expressions[count++] = node.ExprEvaluator; + } + } + + private static Object[] Evaluate(ExprEvaluator[] parameters, ExprEvaluatorContext exprEvaluatorContext) + { + var results = new Object[parameters.Length]; + var count = 0; + var evaluateParams = new EvaluateParams(null, true, exprEvaluatorContext); + foreach (ExprEvaluator expr in parameters) + { + try + { + results[count] = expr.Evaluate(evaluateParams); + count++; + } + catch (Exception ex) + { + string message = "Failed expression evaluation in crontab timer-at for parameter " + count + ": " + + ex.Message; + Log.Error(message, ex); + throw new ArgumentException(message); + } + } + return results; + } + + public OutputConditionPolled MakeNew(AgentInstanceContext agentInstanceContext) + { + ScheduleSpec scheduleSpec; + try + { + Object[] scheduleSpecParameterList = Evaluate(_expressions, agentInstanceContext); + scheduleSpec = ScheduleSpecUtil.ComputeValues(scheduleSpecParameterList); + } + catch (ScheduleParameterException e) + { + throw new ArgumentException("Invalid schedule specification : " + e.Message, e); + } + var state = new OutputConditionPolledCrontabState(scheduleSpec, null, 0); + return new OutputConditionPolledCrontab(agentInstanceContext, state); + } + + public OutputConditionPolled MakeFromState( + AgentInstanceContext agentInstanceContext, + OutputConditionPolledState state) + { + return new OutputConditionPolledCrontab(agentInstanceContext, (OutputConditionPolledCrontabState) state); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputConditionPolledCrontabState.cs b/NEsper.Core/NEsper.Core/epl/view/OutputConditionPolledCrontabState.cs new file mode 100755 index 000000000..1f14936e6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputConditionPolledCrontabState.cs @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.schedule; + +namespace com.espertech.esper.epl.view +{ + public class OutputConditionPolledCrontabState : OutputConditionPolledState + { + public OutputConditionPolledCrontabState(ScheduleSpec scheduleSpec, long? currentReferencePoint, long nextScheduledTime) { + ScheduleSpec = scheduleSpec; + CurrentReferencePoint = currentReferencePoint; + NextScheduledTime = nextScheduledTime; + } + + public ScheduleSpec ScheduleSpec { get; private set; } + + public long? CurrentReferencePoint { get; set; } + + public long NextScheduledTime { get; set; } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputConditionPolledExpression.cs b/NEsper.Core/NEsper.Core/epl/view/OutputConditionPolledExpression.cs new file mode 100755 index 000000000..78803dcc5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputConditionPolledExpression.cs @@ -0,0 +1,108 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.epl.view +{ + /// + /// Output condition for output rate limiting that handles when-then expressions for controlling output. + /// + public class OutputConditionPolledExpression : OutputConditionPolled + { + private readonly OutputConditionPolledExpressionFactory _factory; + private readonly OutputConditionPolledExpressionState _state; + private readonly AgentInstanceContext _agentInstanceContext; + + private readonly ObjectArrayEventBean _builtinProperties; + private readonly EventBean[] _eventsPerStream = new EventBean[1]; + + public OutputConditionPolledExpression( + OutputConditionPolledExpressionFactory factory, + OutputConditionPolledExpressionState state, + AgentInstanceContext agentInstanceContext, + ObjectArrayEventBean builtinProperties) + { + _factory = factory; + _state = state; + _builtinProperties = builtinProperties; + _agentInstanceContext = agentInstanceContext; + } + + public OutputConditionPolledState State + { + get { return _state; } + } + + public virtual bool UpdateOutputCondition(int newEventsCount, int oldEventsCount) + { + _state.TotalNewEventsCount = _state.TotalNewEventsCount + newEventsCount; + _state.TotalOldEventsCount = _state.TotalOldEventsCount + oldEventsCount; + _state.TotalNewEventsSum = _state.TotalNewEventsSum + newEventsCount; + _state.TotalOldEventsSum = _state.TotalOldEventsCount + oldEventsCount; + + bool isOutput = Evaluate(); + if (isOutput) + { + ResetBuiltinProperties(); + + // execute assignments + if (_factory.VariableReadWritePackage != null) + { + if (_builtinProperties != null) + { + PopulateBuiltinProperties(); + _eventsPerStream[0] = _builtinProperties; + } + + _factory.VariableReadWritePackage.WriteVariables(_agentInstanceContext.StatementContext.VariableService, _eventsPerStream, null, _agentInstanceContext); + } + } + return isOutput; + } + + private void PopulateBuiltinProperties() + { + OutputConditionExpressionTypeUtil.Populate(_builtinProperties.Properties, _state.TotalNewEventsCount, + _state.TotalOldEventsCount, _state.TotalNewEventsSum, + _state.TotalOldEventsSum, _state.LastOutputTimestamp); + } + + private bool Evaluate() + { + if (_builtinProperties != null) + { + PopulateBuiltinProperties(); + _eventsPerStream[0] = _builtinProperties; + } + + var result = false; + var output = _factory.WhenExpressionNode.Evaluate(new EvaluateParams(_eventsPerStream, true, _agentInstanceContext)); + //if ((output != null) && (true.Equals(output))) { + if (true.Equals(output)) + { + result = true; + } + + return result; + } + + private void ResetBuiltinProperties() + { + if (_builtinProperties != null) + { + _state.TotalNewEventsCount = 0; + _state.TotalOldEventsCount = 0; + _state.LastOutputTimestamp = _agentInstanceContext.StatementContext.SchedulingService.Time; + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputConditionPolledExpressionFactory.cs b/NEsper.Core/NEsper.Core/epl/view/OutputConditionPolledExpressionFactory.cs new file mode 100755 index 000000000..95732fa08 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputConditionPolledExpressionFactory.cs @@ -0,0 +1,109 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.visitor; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.variable; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.epl.view +{ + /// + /// Output condition for output rate limiting that handles when-then expressions for controlling output. + /// + public class OutputConditionPolledExpressionFactory : OutputConditionPolledFactory + { + private readonly ExprEvaluator _whenExpressionNode; + private readonly VariableReadWritePackage _variableReadWritePackage; + private readonly EventType _oatypeBuiltinProperties; + + /// + /// Ctor. + /// + /// the expression to evaluate, returning true when to output + /// is the optional then-clause variable assignments, or null or empty if none + /// ExprValidationException when validation fails + public OutputConditionPolledExpressionFactory(ExprNode whenExpressionNode, IList assignments, StatementContext statementContext) + { + _whenExpressionNode = whenExpressionNode.ExprEvaluator; + + // determine if using properties + var containsBuiltinProperties = false; + if (ContainsBuiltinProperties(whenExpressionNode)) { + containsBuiltinProperties = true; + } + else { + if (assignments != null) { + foreach (var assignment in assignments) { + if (ContainsBuiltinProperties(assignment.Expression)) { + containsBuiltinProperties = true; + } + } + } + } + + if (containsBuiltinProperties) { + _oatypeBuiltinProperties = statementContext.EventAdapterService.CreateAnonymousObjectArrayType(typeof(OutputConditionPolledExpressionFactory).Name, OutputConditionExpressionTypeUtil.TYPEINFO); + } + else { + _oatypeBuiltinProperties = null; + } + + if (assignments != null) { + _variableReadWritePackage = new VariableReadWritePackage(assignments, statementContext.VariableService, statementContext.EventAdapterService); + } + else { + _variableReadWritePackage = null; + } + } + + public OutputConditionPolled MakeFromState(AgentInstanceContext agentInstanceContext, OutputConditionPolledState state) { + ObjectArrayEventBean builtinProperties = null; + if (_oatypeBuiltinProperties != null) { + builtinProperties = new ObjectArrayEventBean(OutputConditionExpressionTypeUtil.OAPrototype, _oatypeBuiltinProperties); + } + var expressionState = (OutputConditionPolledExpressionState) state; + return new OutputConditionPolledExpression(this, expressionState, agentInstanceContext, builtinProperties); + } + + public OutputConditionPolled MakeNew(AgentInstanceContext agentInstanceContext) { + ObjectArrayEventBean builtinProperties = null; + long? lastOutputTimestamp = null; + if (_oatypeBuiltinProperties != null) { + builtinProperties = new ObjectArrayEventBean(OutputConditionExpressionTypeUtil.OAPrototype, _oatypeBuiltinProperties); + lastOutputTimestamp = agentInstanceContext.StatementContext.SchedulingService.Time; + } + var state = new OutputConditionPolledExpressionState(0, 0, 0, 0, lastOutputTimestamp); + return new OutputConditionPolledExpression(this, state, agentInstanceContext, builtinProperties); + } + + public ExprEvaluator WhenExpressionNode + { + get { return _whenExpressionNode; } + } + + public VariableReadWritePackage VariableReadWritePackage + { + get { return _variableReadWritePackage; } + } + + private bool ContainsBuiltinProperties(ExprNode expr) + { + var propertyVisitor = new ExprNodeIdentifierVisitor(false); + expr.Accept(propertyVisitor); + return !propertyVisitor.ExprProperties.IsEmpty(); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputConditionPolledExpressionState.cs b/NEsper.Core/NEsper.Core/epl/view/OutputConditionPolledExpressionState.cs new file mode 100755 index 000000000..aa3aa9a86 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputConditionPolledExpressionState.cs @@ -0,0 +1,35 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.view +{ + /// + /// Output condition for output rate limiting that handles when-then expressions for controlling output. + /// + public class OutputConditionPolledExpressionState : OutputConditionPolledState + { + public OutputConditionPolledExpressionState(int totalNewEventsCount, int totalOldEventsCount, int totalNewEventsSum, int totalOldEventsSum, long? lastOutputTimestamp) + { + TotalNewEventsCount = totalNewEventsCount; + TotalOldEventsCount = totalOldEventsCount; + TotalNewEventsSum = totalNewEventsSum; + TotalOldEventsSum = totalOldEventsSum; + LastOutputTimestamp = lastOutputTimestamp; + } + + public int TotalNewEventsCount { get; set; } + + public int TotalOldEventsCount { get; set; } + + public int TotalNewEventsSum { get; set; } + + public int TotalOldEventsSum { get; set; } + + public long? LastOutputTimestamp { get; set; } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputConditionPolledFactory.cs b/NEsper.Core/NEsper.Core/epl/view/OutputConditionPolledFactory.cs new file mode 100755 index 000000000..5a7668969 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputConditionPolledFactory.cs @@ -0,0 +1,18 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.context.util; + +namespace com.espertech.esper.epl.view +{ + public interface OutputConditionPolledFactory + { + OutputConditionPolled MakeNew(AgentInstanceContext agentInstanceContext); + OutputConditionPolled MakeFromState(AgentInstanceContext agentInstanceContext, OutputConditionPolledState state); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputConditionPolledFactoryFactory.cs b/NEsper.Core/NEsper.Core/epl/view/OutputConditionPolledFactoryFactory.cs new file mode 100755 index 000000000..0491da208 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputConditionPolledFactoryFactory.cs @@ -0,0 +1,60 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.variable; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.view +{ + /// + /// Factory for output condition instances that are polled/queried only. + /// + public class OutputConditionPolledFactoryFactory + { + public static OutputConditionPolledFactory CreateConditionFactory(OutputLimitSpec outputLimitSpec, StatementContext statementContext) + { + if (outputLimitSpec == null) { + throw new ArgumentNullException("Output condition by count requires a non-null callback"); + } + + // check variable use + VariableMetaData variableMetaData = null; + if (outputLimitSpec.VariableName != null) { + variableMetaData = statementContext.VariableService.GetVariableMetaData(outputLimitSpec.VariableName); + if (variableMetaData == null) { + throw new ArgumentException("Variable named '" + outputLimitSpec.VariableName + "' has not been declared"); + } + } + + if (outputLimitSpec.RateType == OutputLimitRateType.CRONTAB) { + return new OutputConditionPolledCrontabFactory(outputLimitSpec.CrontabAtSchedule, statementContext); + } + else if (outputLimitSpec.RateType == OutputLimitRateType.WHEN_EXPRESSION) { + return new OutputConditionPolledExpressionFactory(outputLimitSpec.WhenExpressionNode, outputLimitSpec.ThenExpressions, statementContext); + } + else if (outputLimitSpec.RateType == OutputLimitRateType.EVENTS) { + int rate = -1; + if (outputLimitSpec.Rate != null) { + rate = outputLimitSpec.Rate.AsInt(); + } + return new OutputConditionPolledCountFactory(rate, statementContext, outputLimitSpec.VariableName); + } + else { + if (variableMetaData != null && (!variableMetaData.VariableType.IsNumeric())) { + throw new ArgumentException("Variable named '" + outputLimitSpec.VariableName + "' must be of numeric type"); + } + return new OutputConditionPolledTimeFactory(outputLimitSpec.TimePeriodExpr, statementContext); + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputConditionPolledState.cs b/NEsper.Core/NEsper.Core/epl/view/OutputConditionPolledState.cs new file mode 100755 index 000000000..970a7897a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputConditionPolledState.cs @@ -0,0 +1,14 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.view +{ + public interface OutputConditionPolledState + { + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputConditionPolledTime.cs b/NEsper.Core/NEsper.Core/epl/view/OutputConditionPolledTime.cs new file mode 100755 index 000000000..ce2cd0750 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputConditionPolledTime.cs @@ -0,0 +1,47 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.context.util; + +namespace com.espertech.esper.epl.view +{ + public sealed class OutputConditionPolledTime : OutputConditionPolled + { + private readonly OutputConditionPolledTimeFactory _factory; + private readonly AgentInstanceContext _context; + private readonly OutputConditionPolledTimeState _state; + + public OutputConditionPolledTime( + OutputConditionPolledTimeFactory factory, + AgentInstanceContext context, + OutputConditionPolledTimeState state) + { + _factory = factory; + _context = context; + _state = state; + } + + public OutputConditionPolledState State + { + get { return _state; } + } + + public bool UpdateOutputCondition(int newEventsCount, int oldEventsCount) + { + // If we pull the interval from a variable, then we may need to reschedule + var msecIntervalSize = _factory.TimePeriod.NonconstEvaluator().DeltaUseEngineTime(null, _context); + var current = _context.TimeProvider.Time; + if (_state.LastUpdate == null || current - _state.LastUpdate >= msecIntervalSize) + { + _state.LastUpdate = current; + return true; + } + return false; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputConditionPolledTimeFactory.cs b/NEsper.Core/NEsper.Core/epl/view/OutputConditionPolledTimeFactory.cs new file mode 100755 index 000000000..7cdac3a2b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputConditionPolledTimeFactory.cs @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.time; + +namespace com.espertech.esper.epl.view +{ + public sealed class OutputConditionPolledTimeFactory : OutputConditionPolledFactory + { + private readonly ExprTimePeriod _timePeriod; + + public OutputConditionPolledTimeFactory(ExprTimePeriod timePeriod, StatementContext statementContext) { + _timePeriod = timePeriod; + var numSeconds = timePeriod.EvaluateAsSeconds(null, true, new ExprEvaluatorContextStatement(statementContext, false)); + if ((numSeconds < 0.001) && (!timePeriod.HasVariable)) { + throw new ArgumentException("Output condition by time requires a interval size of at least 1 msec or a variable"); + } + } + + public OutputConditionPolled MakeNew(AgentInstanceContext agentInstanceContext) + { + return new OutputConditionPolledTime(this, agentInstanceContext, new OutputConditionPolledTimeState(null)); + } + + public OutputConditionPolled MakeFromState(AgentInstanceContext agentInstanceContext, OutputConditionPolledState state) + { + var timeState = (OutputConditionPolledTimeState) state; + return new OutputConditionPolledTime(this, agentInstanceContext, timeState); + } + + public ExprTimePeriod TimePeriod + { + get { return _timePeriod; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputConditionPolledTimeState.cs b/NEsper.Core/NEsper.Core/epl/view/OutputConditionPolledTimeState.cs new file mode 100755 index 000000000..65b46209c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputConditionPolledTimeState.cs @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.view +{ + public sealed class OutputConditionPolledTimeState : OutputConditionPolledState + { + public OutputConditionPolledTimeState(long? lastUpdate) + { + LastUpdate = lastUpdate; + } + + public long? LastUpdate { get; set; } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputConditionTerm.cs b/NEsper.Core/NEsper.Core/epl/view/OutputConditionTerm.cs new file mode 100755 index 000000000..4be0d6846 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputConditionTerm.cs @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.view +{ + public sealed class OutputConditionTerm : OutputConditionBase, OutputCondition + { + public OutputConditionTerm(OutputCallback outputCallback) + : base(outputCallback) + { + } + + public override void UpdateOutputCondition(int newEventsCount, int oldEventsCount) + { + } + + public override void Stop() + { + // no action required + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputConditionTermFactory.cs b/NEsper.Core/NEsper.Core/epl/view/OutputConditionTermFactory.cs new file mode 100755 index 000000000..b55a7c385 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputConditionTermFactory.cs @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.context.util; + +namespace com.espertech.esper.epl.view +{ + public sealed class OutputConditionTermFactory : OutputConditionFactory + { + public OutputCondition Make(AgentInstanceContext agentInstanceContext, OutputCallback outputCallback) + { + return new OutputConditionTerm(outputCallback); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputConditionTime.cs b/NEsper.Core/NEsper.Core/epl/view/OutputConditionTime.cs new file mode 100755 index 000000000..5ed4c7443 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputConditionTime.cs @@ -0,0 +1,129 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.schedule; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.view +{ + /// + /// Output condition that is satisfied at the end + /// of every time interval of a given length. + /// + public sealed class OutputConditionTime : OutputConditionBase, OutputCondition, StopCallback + { + private const bool DO_OUTPUT = true; + private const bool FORCE_UPDATE = true; + + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly AgentInstanceContext _context; + private readonly OutputConditionTimeFactory _parent; + private readonly long _scheduleSlot; + private long? _currentReferencePoint; + private bool _isCallbackScheduled; + private EPStatementHandleCallback _handle; + private long _currentScheduledTime; + + public OutputConditionTime( + OutputCallback outputCallback, + AgentInstanceContext context, + OutputConditionTimeFactory outputConditionTimeFactory, + bool isStartConditionOnCreation) + : base(outputCallback) + { + _context = context; + _parent = outputConditionTimeFactory; + + _scheduleSlot = context.StatementContext.ScheduleBucket.AllocateSlot(); + if (isStartConditionOnCreation) { + UpdateOutputCondition(0, 0); + } + } + + public override void UpdateOutputCondition(int newEventsCount, int oldEventsCount) { + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled)) { + Log.Debug(".updateOutputCondition, " + + " newEventsCount==" + newEventsCount + + " oldEventsCount==" + oldEventsCount); + } + + if (_currentReferencePoint == null) { + _currentReferencePoint = _context.StatementContext.SchedulingService.Time; + } + + // If we pull the interval from a variable, then we may need to reschedule + if (_parent.TimePeriod.HasVariable) { + var now = _context.StatementContext.SchedulingService.Time; + var delta = _parent.TimePeriod.NonconstEvaluator().DeltaAddWReference(now, _currentReferencePoint.Value, null, true, _context); + if (delta.Delta != _currentScheduledTime) { + if (_isCallbackScheduled) { + // reschedule + _context.StatementContext.SchedulingService.Remove(_handle, _scheduleSlot); + ScheduleCallback(); + } + } + } + + // Schedule the next callback if there is none currently scheduled + if (!_isCallbackScheduled) { + ScheduleCallback(); + } + } + + public override String ToString() { + return GetType().FullName; + } + + private void ScheduleCallback() { + _isCallbackScheduled = true; + var current = _context.StatementContext.SchedulingService.Time; + var delta = _parent.TimePeriod.NonconstEvaluator().DeltaAddWReference(current, _currentReferencePoint.Value, null, true, _context); + var deltaTime = delta.Delta; + _currentReferencePoint = delta.LastReference; + _currentScheduledTime = deltaTime; + + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled)) { + Log.Debug(".scheduleCallback Scheduled new callback for " + + " afterMsec=" + deltaTime + + " now=" + current + + " currentReferencePoint=" + _currentReferencePoint); + } + + var callback = new ProxyScheduleHandleCallback { + ProcScheduledTrigger = (extensionServicesContext) => { + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().QOutputRateConditionScheduledEval(); + } + _isCallbackScheduled = false; + OutputCallback.Invoke(DO_OUTPUT, FORCE_UPDATE); + ScheduleCallback(); + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().AOutputRateConditionScheduledEval(); + } + } + }; + _handle = new EPStatementHandleCallback(_context.EpStatementAgentInstanceHandle, callback); + _context.StatementContext.SchedulingService.Add(deltaTime, _handle, _scheduleSlot); + _context.AddTerminationCallback(this); + } + + public override void Stop() { + if (_handle != null) { + _context.StatementContext.SchedulingService.Remove(_handle, _scheduleSlot); + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputConditionTimeFactory.cs b/NEsper.Core/NEsper.Core/epl/view/OutputConditionTimeFactory.cs new file mode 100755 index 000000000..47fcb9ab6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputConditionTimeFactory.cs @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.expression.time; + +namespace com.espertech.esper.epl.view +{ + /// + /// Output condition that is satisfied at the end of every time interval of a given length. + /// + public class OutputConditionTimeFactory : OutputConditionFactory + { + private readonly ExprTimePeriod _timePeriod; + private readonly ExprTimePeriodEvalDeltaNonConst _timePeriodDeltaComputation; + protected readonly bool _isStartConditionOnCreation; + + /// + /// Constructor. + /// + /// is the number of minutes or seconds to batch events for, may include variables + /// if set to true [is start condition on creation]. + public OutputConditionTimeFactory(ExprTimePeriod timePeriod, bool isStartConditionOnCreation) + { + _timePeriod = timePeriod; + _timePeriodDeltaComputation = timePeriod.NonconstEvaluator(); + _isStartConditionOnCreation = isStartConditionOnCreation; + } + + public OutputCondition Make(AgentInstanceContext agentInstanceContext, OutputCallback outputCallback) + { + return new OutputConditionTime(outputCallback, agentInstanceContext, this, _isStartConditionOnCreation); + } + + public ExprTimePeriod TimePeriod + { + get { return _timePeriod; } + } + + public ExprTimePeriodEvalDeltaNonConst TimePeriodDeltaComputation + { + get { return _timePeriodDeltaComputation; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewAfterState.cs b/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewAfterState.cs new file mode 100755 index 000000000..968e2a764 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewAfterState.cs @@ -0,0 +1,24 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.core.service; + +namespace com.espertech.esper.epl.view +{ + public interface OutputProcessViewAfterState + { + bool CheckUpdateAfterCondition(EventBean[] newEvents, StatementContext statementContext); + bool CheckUpdateAfterCondition(ISet> newEvents, StatementContext statementContext); + bool CheckUpdateAfterCondition(UniformPair newOldEvents, StatementContext statementContext); + void Destroy(); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewAfterStateImpl.cs b/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewAfterStateImpl.cs new file mode 100755 index 000000000..61bd0b836 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewAfterStateImpl.cs @@ -0,0 +1,98 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.service; + +namespace com.espertech.esper.epl.view +{ + public class OutputProcessViewAfterStateImpl : OutputProcessViewAfterState + { + private readonly long? _afterConditionTime; + private readonly int? _afterConditionNumberOfEvents; + protected bool IsAfterConditionSatisfied; + private int _afterConditionEventsFound; + + public OutputProcessViewAfterStateImpl(long? afterConditionTime, int? afterConditionNumberOfEvents) + { + _afterConditionTime = afterConditionTime; + _afterConditionNumberOfEvents = afterConditionNumberOfEvents; + } + + /// + /// Returns true if the after-condition is satisfied. + /// + /// is the view new events + /// indicator for output condition + public bool CheckUpdateAfterCondition(EventBean[] newEvents, StatementContext statementContext) + { + return IsAfterConditionSatisfied || CheckAfterCondition(newEvents == null ? 0 : newEvents.Length, statementContext); + } + + /// + /// Returns true if the after-condition is satisfied. + /// + /// is the join new events + /// indicator for output condition + public bool CheckUpdateAfterCondition(ISet> newEvents, StatementContext statementContext) + { + return IsAfterConditionSatisfied || CheckAfterCondition(newEvents == null ? 0 : newEvents.Count, statementContext); + } + + /// + /// Returns true if the after-condition is satisfied. + /// + /// is the new and old events pair + /// indicator for output condition + public bool CheckUpdateAfterCondition(UniformPair newOldEvents, StatementContext statementContext) + { + return IsAfterConditionSatisfied || CheckAfterCondition(newOldEvents == null ? 0 : (newOldEvents.First == null ? 0 : newOldEvents.First.Length), statementContext); + } + + public void Destroy() { + // no action required + } + + private bool CheckAfterCondition(int numOutputEvents, StatementContext statementContext) + { + if (_afterConditionTime != null) + { + long time = statementContext.TimeProvider.Time; + if (time < _afterConditionTime) + { + return false; + } + + IsAfterConditionSatisfied = true; + return true; + } + else if (_afterConditionNumberOfEvents != null) + { + _afterConditionEventsFound += numOutputEvents; + if (_afterConditionEventsFound <= _afterConditionNumberOfEvents) + { + return false; + } + + IsAfterConditionSatisfied = true; + return true; + } + else + { + IsAfterConditionSatisfied = true; + return true; + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewAfterStateNone.cs b/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewAfterStateNone.cs new file mode 100755 index 000000000..c8d831fd0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewAfterStateNone.cs @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.core.service; + +namespace com.espertech.esper.epl.view +{ + public class OutputProcessViewAfterStateNone : OutputProcessViewAfterState + { + public readonly static OutputProcessViewAfterStateNone INSTANCE = new OutputProcessViewAfterStateNone(); + + private OutputProcessViewAfterStateNone() + { + } + + public bool CheckUpdateAfterCondition(EventBean[] newEvents, StatementContext statementContext) + { + return true; + } + + public bool CheckUpdateAfterCondition(ISet> newEvents, StatementContext statementContext) + { + return true; + } + + public bool CheckUpdateAfterCondition(UniformPair newOldEvents, StatementContext statementContext) + { + return true; + } + + public void Destroy() + { + // no action required + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewBase.cs b/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewBase.cs new file mode 100755 index 000000000..bd096ad1d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewBase.cs @@ -0,0 +1,125 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.@join.@base; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.epl.view +{ + public abstract class OutputProcessViewBase + : View + , JoinSetIndicator + , OutputProcessViewTerminable + , StopCallback + { + protected UpdateDispatchView ChildView; + protected Viewable ParentView; + + public abstract OutputCondition OptionalOutputCondition { get; } + public abstract void Stop(); + + protected OutputProcessViewBase(ResultSetProcessor resultSetProcessor) + { + ResultSetProcessor = resultSetProcessor; + } + + public virtual Viewable Parent + { + get { return ParentView; } + set { ParentView = value; } + } + + public ResultSetProcessor ResultSetProcessor { get; protected set; } + + public abstract int NumChangesetRows { get; } + + public abstract void Update(EventBean[] newData, EventBean[] oldData); + + public abstract OutputProcessViewConditionDeltaSet OptionalDeltaSet { get; } + public abstract OutputProcessViewAfterState OptionalAfterConditionState { get; } + + public View AddView(View view) + { + if (ChildView != null) + { + throw new IllegalStateException("Child view has already been supplied"); + } + ChildView = (UpdateDispatchView) view; + return this; + } + + public virtual View[] Views + { + get + { + if (ChildView == null) + { + return ViewSupport.EMPTY_VIEW_ARRAY; + } + return new View[] + { + ChildView + }; + } + } + + public void RemoveAllViews() + { + ChildView = null; + } + + public bool RemoveView(View view) + { + if (view != ChildView) + { + throw new IllegalStateException("Cannot remove child view, view has not been supplied"); + } + ChildView = null; + return true; + } + + public virtual bool HasViews + { + get { return ChildView != null; } + } + + public virtual EventType EventType + { + get + { + EventType eventType = ResultSetProcessor.ResultEventType; + return eventType ?? ParentView.EventType; + } + } + + /// + /// For joins, supplies the join execution strategy that provides iteration over statement results. + /// + /// executes joins including static (non-continuous) joins + public virtual JoinExecutionStrategy JoinExecutionStrategy { protected get; set; } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public abstract IEnumerator GetEnumerator(); + public abstract void Process(ISet> newEvents, ISet> oldEvents, ExprEvaluatorContext exprEvaluatorContext); + public abstract void Terminated(); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewBaseCallback.cs b/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewBaseCallback.cs new file mode 100755 index 000000000..f9c82be3f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewBaseCallback.cs @@ -0,0 +1,81 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.epl.view +{ + /// + /// Factory for output processing views. + /// + public class OutputProcessViewBaseCallback : OutputProcessViewBase + { + private readonly OutputProcessViewCallback _callback; + + public OutputProcessViewBaseCallback(ResultSetProcessor resultSetProcessor, OutputProcessViewCallback callback) + : base(resultSetProcessor) + { + _callback = callback; + } + + public override int NumChangesetRows + { + get { return 0; } + } + + public override OutputCondition OptionalOutputCondition + { + get { return null; } + } + + public override OutputProcessViewConditionDeltaSet OptionalDeltaSet + { + get { return null; } + } + + public override OutputProcessViewAfterState OptionalAfterConditionState + { + get { return null; } + } + + public override IEnumerator GetEnumerator() + { + return OutputStrategyUtil.GetEnumerator( + base.JoinExecutionStrategy, + base.ResultSetProcessor, + Parent, + false); + } + + public override void Process(ISet> newEvents, ISet> oldEvents, ExprEvaluatorContext exprEvaluatorContext) + { + UniformPair pair = ResultSetProcessor.ProcessJoinResult(newEvents, oldEvents, false); + _callback.OutputViaCallback(pair.First); + } + + public override void Terminated() + { + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + UniformPair pair = ResultSetProcessor.ProcessViewResult(newData, oldData, false); + _callback.OutputViaCallback(pair.First); + } + + public override void Stop() + { + // Not applicable + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewBaseWAfter.cs b/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewBaseWAfter.cs new file mode 100755 index 000000000..5efda712b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewBaseWAfter.cs @@ -0,0 +1,69 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.core; + +namespace com.espertech.esper.epl.view +{ + public abstract class OutputProcessViewBaseWAfter : OutputProcessViewBase + { + private readonly OutputProcessViewAfterState _afterConditionState; + + protected OutputProcessViewBaseWAfter(ResultSetProcessorHelperFactory resultSetProcessorHelperFactory, AgentInstanceContext agentInstanceContext, ResultSetProcessor resultSetProcessor, long? afterConditionTime, int? afterConditionNumberOfEvents, bool afterConditionSatisfied) + : base(resultSetProcessor) + { + _afterConditionState = resultSetProcessorHelperFactory.MakeOutputConditionAfter(afterConditionTime, afterConditionNumberOfEvents, afterConditionSatisfied, agentInstanceContext); + } + + public override OutputProcessViewAfterState OptionalAfterConditionState + { + get { return _afterConditionState; } + } + + /// + /// Returns true if the after-condition is satisfied. + /// + /// is the view new events + /// indicator for output condition + public bool CheckAfterCondition(EventBean[] newEvents, StatementContext statementContext) + { + return _afterConditionState.CheckUpdateAfterCondition(newEvents, statementContext); + } + + /// + /// Returns true if the after-condition is satisfied. + /// + /// is the join new events + /// indicator for output condition + public bool CheckAfterCondition(ISet> newEvents, StatementContext statementContext) + { + return _afterConditionState.CheckUpdateAfterCondition(newEvents, statementContext); + } + + /// + /// Returns true if the after-condition is satisfied. + /// + /// is the new and old events pair + /// indicator for output condition + public bool CheckAfterCondition(UniformPair newOldEvents, StatementContext statementContext) + { + return _afterConditionState.CheckUpdateAfterCondition(newOldEvents, statementContext); + } + + public override void Stop() + { + _afterConditionState.Destroy(); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewCallback.cs b/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewCallback.cs new file mode 100755 index 000000000..7b2a4c011 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewCallback.cs @@ -0,0 +1,18 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; + +namespace com.espertech.esper.epl.view +{ + /// Factory for output processing views. + public interface OutputProcessViewCallback + { + void OutputViaCallback(EventBean[] events); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewConditionDefault.cs b/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewConditionDefault.cs new file mode 100755 index 000000000..16f69c966 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewConditionDefault.cs @@ -0,0 +1,428 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.view +{ + /// + /// A view that prepares output events, batching incoming + /// events and invoking the result set processor as necessary. + /// + /// Handles output rate limiting or stabilizing. + /// + /// + public class OutputProcessViewConditionDefault : OutputProcessViewBaseWAfter + { + private static readonly ILog Log = + LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly OutputProcessViewConditionFactory _parent; + private readonly OutputCondition _outputCondition; + + // Posted events in ordered form (for applying to aggregates) and summarized per type + // Using List as random access is a requirement. + private readonly OutputProcessViewConditionDeltaSet _deltaSet; + + public OutputProcessViewConditionDefault( + ResultSetProcessorHelperFactory resultSetProcessorHelperFactory, + ResultSetProcessor resultSetProcessor, + long? afterConditionTime, + int? afterConditionNumberOfEvents, + bool afterConditionSatisfied, + OutputProcessViewConditionFactory parent, + AgentInstanceContext agentInstanceContext, + bool isJoin) + : base( + resultSetProcessorHelperFactory, agentInstanceContext, resultSetProcessor, afterConditionTime, + afterConditionNumberOfEvents, afterConditionSatisfied) + { + _parent = parent; + + OutputCallback outputCallback = GetCallbackToLocal(parent.StreamCount); + _outputCondition = parent.OutputConditionFactory.Make(agentInstanceContext, outputCallback); + _deltaSet = resultSetProcessorHelperFactory.MakeOutputConditionChangeSet(isJoin, agentInstanceContext); + } + + private static void AddToChangeset( + IEnumerable> newEvents, + IEnumerable> oldEvents, + OutputProcessViewConditionDeltaSet joinEventsSet) + { + // add the incoming events to the event batches + ISet> copyNew; + if (newEvents != null) + { + copyNew = new LinkedHashSet>(newEvents); + } + else + { + copyNew = new LinkedHashSet>(); + } + + ISet> copyOld; + if (oldEvents != null) + { + copyOld = new LinkedHashSet>(oldEvents); + } + else + { + copyOld = new LinkedHashSet>(); + } + + joinEventsSet.AddJoin(new UniformPair>>(copyNew, copyOld)); + } + + public override int NumChangesetRows + { + get { return _deltaSet.NumChangesetRows; } + } + + public override OutputProcessViewConditionDeltaSet OptionalDeltaSet + { + get { return _deltaSet; } + } + + public override OutputCondition OptionalOutputCondition + { + get { return _outputCondition; } + } + + public override OutputProcessViewAfterState OptionalAfterConditionState + { + get { return null; } + } + + /// + /// The update method is called if the view does not participate in a join. + /// + /// - new events + /// - old events + public override void Update(EventBean[] newData, EventBean[] oldData) + { + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().QOutputProcessWCondition(newData, oldData); + } + + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled)) + { + Log.Debug( + ".update Received update, " + + " newData.Length==" + ((newData == null) ? 0 : newData.Length) + + " oldData.Length==" + ((oldData == null) ? 0 : oldData.Length)); + } + + // add the incoming events to the event batches + if (_parent.HasAfter) + { + bool afterSatisfied = base.CheckAfterCondition(newData, _parent.StatementContext); + if (!afterSatisfied) + { + if (!_parent.IsUnaggregatedUngrouped) + { + _deltaSet.AddView(new UniformPair(newData, oldData)); + } + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().AOutputProcessWCondition(false); + } + return; + } + else + { + _deltaSet.AddView(new UniformPair(newData, oldData)); + } + } + else + { + _deltaSet.AddView(new UniformPair(newData, oldData)); + } + + int newDataLength = 0; + int oldDataLength = 0; + if (newData != null) + { + newDataLength = newData.Length; + } + if (oldData != null) + { + oldDataLength = oldData.Length; + } + + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().QOutputRateConditionUpdate(newDataLength, oldDataLength); + } + _outputCondition.UpdateOutputCondition(newDataLength, oldDataLength); + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().AOutputRateConditionUpdate(); + } + + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().AOutputProcessWCondition(true); + } + } + + /// + /// This process (update) method is for participation in a join. + /// + /// - new events + /// - old events + public override void Process( + ISet> newEvents, + ISet> oldEvents, + ExprEvaluatorContext exprEvaluatorContext) + { + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().QOutputProcessWConditionJoin(newEvents, oldEvents); + } + + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled)) + { + Log.Debug( + ".process Received update, " + + " newData.Length==" + ((newEvents == null) ? 0 : newEvents.Count) + + " oldData.Length==" + ((oldEvents == null) ? 0 : oldEvents.Count)); + } + + // add the incoming events to the event batches + if (_parent.HasAfter) + { + bool afterSatisfied = base.CheckAfterCondition(newEvents, _parent.StatementContext); + if (!afterSatisfied) + { + if (!_parent.IsUnaggregatedUngrouped) + { + AddToChangeset(newEvents, oldEvents, _deltaSet); + } + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().AOutputProcessWConditionJoin(false); + } + return; + } + else + { + AddToChangeset(newEvents, oldEvents, _deltaSet); + } + } + else + { + AddToChangeset(newEvents, oldEvents, _deltaSet); + } + + int newEventsSize = 0; + if (newEvents != null) + { + newEventsSize = newEvents.Count; + } + + int oldEventsSize = 0; + if (oldEvents != null) + { + oldEventsSize = oldEvents.Count; + } + + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().QOutputRateConditionUpdate(newEventsSize, oldEventsSize); + } + _outputCondition.UpdateOutputCondition(newEventsSize, oldEventsSize); + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().AOutputRateConditionUpdate(); + } + + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().AOutputProcessWConditionJoin(true); + } + } + + /// + /// Called once the output condition has been met. + /// Invokes the result set processor. + /// Used for non-join event data. + /// + /// - true if the batched events should actually be output as well as processed, false if they should just be processed + /// - true if output should be made even when no updating events have arrived + protected void ContinueOutputProcessingView(bool doOutput, bool forceUpdate) + { + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().QOutputRateConditionOutputNow(); + } + + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled)) + { + Log.Debug(".continueOutputProcessingView"); + } + + bool isGenerateSynthetic = _parent.StatementResultService.IsMakeSynthetic; + bool isGenerateNatural = _parent.StatementResultService.IsMakeNatural; + + // Process the events and get the result + UniformPair newOldEvents = ResultSetProcessor.ProcessOutputLimitedView( + _deltaSet.ViewEventsSet, isGenerateSynthetic, _parent.OutputLimitLimitType); + + if (_parent.IsDistinct && newOldEvents != null) + { + newOldEvents.First = EventBeanUtility.GetDistinctByProp(newOldEvents.First, _parent.EventBeanReader); + newOldEvents.Second = EventBeanUtility.GetDistinctByProp(newOldEvents.Second, _parent.EventBeanReader); + } + + if ((!isGenerateSynthetic) && (!isGenerateNatural)) + { + if (AuditPath.IsAuditEnabled) + { + OutputStrategyUtil.IndicateEarlyReturn(_parent.StatementContext, newOldEvents); + } + ResetEventBatches(); + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().AOutputRateConditionOutputNow(false); + } + return; + } + + if (doOutput) + { + Output(forceUpdate, newOldEvents); + } + ResetEventBatches(); + + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().AOutputRateConditionOutputNow(true); + } + } + + protected virtual void Output(bool forceUpdate, UniformPair results) + { + // Child view can be null in replay from named window + if (ChildView != null) + { + OutputStrategyUtil.Output(forceUpdate, results, ChildView); + } + } + + public override void Stop() + { + base.Stop(); + _deltaSet.Destroy(); + _outputCondition.Stop(); + } + + private void ResetEventBatches() + { + _deltaSet.Clear(); + } + + /// + /// Called once the output condition has been met. + /// Invokes the result set processor. + /// Used for join event data. + /// + /// - true if the batched events should actually be output as well as processed, false if they should just be processed + /// - true if output should be made even when no updating events have arrived + protected void ContinueOutputProcessingJoin(bool doOutput, bool forceUpdate) + { + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().QOutputRateConditionOutputNow(); + } + + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled)) + { + Log.Debug(".continueOutputProcessingJoin"); + } + + bool isGenerateSynthetic = _parent.StatementResultService.IsMakeSynthetic; + bool isGenerateNatural = _parent.StatementResultService.IsMakeNatural; + + // Process the events and get the result + UniformPair newOldEvents = ResultSetProcessor.ProcessOutputLimitedJoin( + _deltaSet.JoinEventsSet, isGenerateSynthetic, _parent.OutputLimitLimitType); + + if (_parent.IsDistinct && newOldEvents != null) + { + newOldEvents.First = EventBeanUtility.GetDistinctByProp(newOldEvents.First, _parent.EventBeanReader); + newOldEvents.Second = EventBeanUtility.GetDistinctByProp(newOldEvents.Second, _parent.EventBeanReader); + } + + if ((!isGenerateSynthetic) && (!isGenerateNatural)) + { + if (AuditPath.IsAuditEnabled) + { + OutputStrategyUtil.IndicateEarlyReturn(_parent.StatementContext, newOldEvents); + } + ResetEventBatches(); + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().AOutputRateConditionOutputNow(false); + } + return; + } + + if (doOutput) + { + Output(forceUpdate, newOldEvents); + } + ResetEventBatches(); + + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().AOutputRateConditionOutputNow(true); + } + } + + private OutputCallback GetCallbackToLocal(int streamCount) + { + // single stream means no join + // multiple streams means a join + if (streamCount == 1) + { + return ContinueOutputProcessingView; + } + else + { + return ContinueOutputProcessingJoin; + } + } + + public override IEnumerator GetEnumerator() + { + return OutputStrategyUtil.GetEnumerator( + JoinExecutionStrategy, ResultSetProcessor, ParentView, _parent.IsDistinct); + } + + public override void Terminated() + { + if (_parent.IsTerminable) + { + _outputCondition.Terminated(); + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewConditionDefaultPostProcess.cs b/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewConditionDefaultPostProcess.cs new file mode 100755 index 000000000..0c81a0d20 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewConditionDefaultPostProcess.cs @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.core; + +namespace com.espertech.esper.epl.view +{ + /// + /// A view that prepares output events, batching incoming events and invoking the + /// result set processor as necessary. + /// + /// Handles output rate limiting or stabilizing. + public class OutputProcessViewConditionDefaultPostProcess : OutputProcessViewConditionDefault + { + private readonly OutputStrategyPostProcess _postProcessor; + + public OutputProcessViewConditionDefaultPostProcess( + ResultSetProcessor resultSetProcessor, + long? afterConditionTime, + int? afterConditionNumberOfEvents, + bool afterConditionSatisfied, + OutputProcessViewConditionFactory parent, + AgentInstanceContext agentInstanceContext, + OutputStrategyPostProcess postProcessor, + bool isJoin, + ResultSetProcessorHelperFactory resultSetProcessorHelperFactory) + : base( + resultSetProcessorHelperFactory, resultSetProcessor, afterConditionTime, afterConditionNumberOfEvents, + afterConditionSatisfied, parent, agentInstanceContext, isJoin) + { + _postProcessor = postProcessor; + } + + protected override void Output(bool forceUpdate, UniformPair results) + { + // Child view can be null in replay from named window + if (ChildView != null) + { + _postProcessor.Output(forceUpdate, results, ChildView); + } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewConditionDeltaSet.cs b/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewConditionDeltaSet.cs new file mode 100755 index 000000000..f8dd208cd --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewConditionDeltaSet.cs @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; + +namespace com.espertech.esper.epl.view +{ + public interface OutputProcessViewConditionDeltaSet + { + int NumChangesetRows { get; } + void AddView(UniformPair events); + void AddJoin(UniformPair>> events); + void Clear(); + IList>>> JoinEventsSet { get; } + IList> ViewEventsSet { get; } + void Destroy(); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewConditionDeltaSetImpl.cs b/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewConditionDeltaSetImpl.cs new file mode 100755 index 000000000..bc3eceadd --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewConditionDeltaSetImpl.cs @@ -0,0 +1,73 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.epl.view +{ + public class OutputProcessViewConditionDeltaSetImpl : OutputProcessViewConditionDeltaSet + { + private readonly IList> _viewEventsList; + private readonly IList>>> _joinEventsSet; + + public OutputProcessViewConditionDeltaSetImpl(bool isJoin) + { + if (isJoin) + { + _joinEventsSet = new List>>>(); + _viewEventsList = Collections.GetEmptyList>(); + } + else + { + _viewEventsList = new List>(); + _joinEventsSet = Collections.GetEmptyList>>>(); + } + } + + public int NumChangesetRows + { + get { return Math.Max(_viewEventsList.Count, _joinEventsSet.Count); } + } + + public void AddView(UniformPair uniformPair) + { + _viewEventsList.Add(uniformPair); + } + + public void AddJoin(UniformPair>> setUniformPair) + { + _joinEventsSet.Add(setUniformPair); + } + + public void Clear() + { + _viewEventsList.Clear(); + _joinEventsSet.Clear(); + } + + public void Destroy() + { + Clear(); + } + + public IList>>> JoinEventsSet + { + get { return _joinEventsSet; } + } + + public IList> ViewEventsSet + { + get { return _viewEventsList; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewConditionFactory.cs b/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewConditionFactory.cs new file mode 100755 index 000000000..452952f04 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewConditionFactory.cs @@ -0,0 +1,180 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.time; +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.epl.view +{ + /// + /// A view that handles the "output snapshot" keyword in output rate stabilizing. + /// + public class OutputProcessViewConditionFactory : OutputProcessViewDirectDistinctOrAfterFactory + { + public enum ConditionType + { + SNAPSHOT, + POLICY_FIRST, + POLICY_LASTALL_UNORDERED, + POLICY_NONFIRST + } + + private readonly ConditionType _conditionType; + private readonly bool _hasAfter; + private readonly bool _isUnaggregatedUngrouped; + private readonly OutputConditionFactory _outputConditionFactory; + private readonly OutputLimitLimitType _outputLimitLimitType; + private readonly ResultSetProcessorHelperFactory _resultSetProcessorHelperFactory; + private readonly SelectClauseStreamSelectorEnum _selectClauseStreamSelectorEnum; + private readonly int _streamCount; + private readonly bool _terminable; + + public OutputProcessViewConditionFactory( + StatementContext statementContext, + OutputStrategyPostProcessFactory postProcessFactory, + bool distinct, + ExprTimePeriod afterTimePeriod, + int? afterConditionNumberOfEvents, + EventType resultEventType, + OutputConditionFactory outputConditionFactory, + int streamCount, + ConditionType conditionType, + OutputLimitLimitType outputLimitLimitType, + bool terminable, + bool hasAfter, + bool isUnaggregatedUngrouped, + SelectClauseStreamSelectorEnum selectClauseStreamSelectorEnum, + ResultSetProcessorHelperFactory resultSetProcessorHelperFactory) + : base( + statementContext, postProcessFactory, resultSetProcessorHelperFactory, distinct, afterTimePeriod, + afterConditionNumberOfEvents, resultEventType) + { + _outputConditionFactory = outputConditionFactory; + _streamCount = streamCount; + _conditionType = conditionType; + _outputLimitLimitType = outputLimitLimitType; + _terminable = terminable; + _hasAfter = hasAfter; + _isUnaggregatedUngrouped = isUnaggregatedUngrouped; + _selectClauseStreamSelectorEnum = selectClauseStreamSelectorEnum; + _resultSetProcessorHelperFactory = resultSetProcessorHelperFactory; + } + + public OutputConditionFactory OutputConditionFactory + { + get { return _outputConditionFactory; } + } + + public int StreamCount + { + get { return _streamCount; } + } + + public OutputLimitLimitType OutputLimitLimitType + { + get { return _outputLimitLimitType; } + } + + public bool IsTerminable + { + get { return _terminable; } + } + + public bool HasAfter + { + get { return _hasAfter; } + } + + public bool IsUnaggregatedUngrouped + { + get { return _isUnaggregatedUngrouped; } + } + + public SelectClauseStreamSelectorEnum SelectClauseStreamSelectorEnum + { + get { return _selectClauseStreamSelectorEnum; } + } + + public override OutputProcessViewBase MakeView( + ResultSetProcessor resultSetProcessor, + AgentInstanceContext agentInstanceContext) + { + // determine after-stuff + bool isAfterConditionSatisfied = true; + long? afterConditionTime = null; + if (AfterConditionNumberOfEvents != null) + { + isAfterConditionSatisfied = false; + } + else if (AfterTimePeriod != null) + { + isAfterConditionSatisfied = false; + long delta = AfterTimePeriod.NonconstEvaluator().DeltaUseEngineTime(null, agentInstanceContext); + afterConditionTime = agentInstanceContext.StatementContext.TimeProvider.Time + delta; + } + + if (_conditionType == ConditionType.SNAPSHOT) + { + if (base.PostProcessFactory == null) + { + return new OutputProcessViewConditionSnapshot( + _resultSetProcessorHelperFactory, resultSetProcessor, afterConditionTime, + AfterConditionNumberOfEvents, isAfterConditionSatisfied, this, agentInstanceContext); + } + OutputStrategyPostProcess postProcess = PostProcessFactory.Make(agentInstanceContext); + return new OutputProcessViewConditionSnapshotPostProcess( + _resultSetProcessorHelperFactory, resultSetProcessor, afterConditionTime, + AfterConditionNumberOfEvents, isAfterConditionSatisfied, this, agentInstanceContext, postProcess); + } + else if (_conditionType == ConditionType.POLICY_FIRST) + { + if (base.PostProcessFactory == null) + { + return new OutputProcessViewConditionFirst( + _resultSetProcessorHelperFactory, resultSetProcessor, afterConditionTime, + AfterConditionNumberOfEvents, isAfterConditionSatisfied, this, agentInstanceContext); + } + OutputStrategyPostProcess postProcess = PostProcessFactory.Make(agentInstanceContext); + return new OutputProcessViewConditionFirstPostProcess( + _resultSetProcessorHelperFactory, resultSetProcessor, afterConditionTime, + AfterConditionNumberOfEvents, isAfterConditionSatisfied, this, agentInstanceContext, postProcess); + } + else if (_conditionType == ConditionType.POLICY_LASTALL_UNORDERED) + { + if (base.PostProcessFactory == null) + { + return new OutputProcessViewConditionLastAllUnord( + _resultSetProcessorHelperFactory, resultSetProcessor, afterConditionTime, + AfterConditionNumberOfEvents, isAfterConditionSatisfied, this, agentInstanceContext); + } + OutputStrategyPostProcess postProcess = PostProcessFactory.Make(agentInstanceContext); + return new OutputProcessViewConditionLastAllUnordPostProcessAll( + _resultSetProcessorHelperFactory, resultSetProcessor, afterConditionTime, + AfterConditionNumberOfEvents, isAfterConditionSatisfied, this, agentInstanceContext, postProcess); + } + else + { + if (base.PostProcessFactory == null) + { + return new OutputProcessViewConditionDefault( + _resultSetProcessorHelperFactory, resultSetProcessor, afterConditionTime, + AfterConditionNumberOfEvents, isAfterConditionSatisfied, this, agentInstanceContext, + _streamCount > 1); + } + OutputStrategyPostProcess postProcess = PostProcessFactory.Make(agentInstanceContext); + return new OutputProcessViewConditionDefaultPostProcess( + resultSetProcessor, afterConditionTime, AfterConditionNumberOfEvents, isAfterConditionSatisfied, + this, agentInstanceContext, postProcess, _streamCount > 1, _resultSetProcessorHelperFactory); + } + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewConditionFirst.cs b/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewConditionFirst.cs new file mode 100755 index 000000000..1ad408bc6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewConditionFirst.cs @@ -0,0 +1,379 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; +using com.espertech.esper.events; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.view +{ + /// + /// Handles output rate limiting for FIRST, only applicable with a having-clause and no group-by clause. + /// + /// Without having-clause the order of processing won't matter therefore its handled by the + /// . With group-by the + /// handles the per-group first criteria. + /// + /// + public class OutputProcessViewConditionFirst : OutputProcessViewBaseWAfter + { + private static readonly ILog Log = + LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly List>>> _joinEventsSet = + new List>>>(); + + private readonly OutputCondition _outputCondition; + private readonly OutputProcessViewConditionFactory _parent; + // Posted events in ordered form (for applying to aggregates) and summarized per type + // Using List as random access is a requirement. + private readonly List> _viewEventsList = new List>(); + + private bool _witnessedFirst; + + public OutputProcessViewConditionFirst( + ResultSetProcessorHelperFactory resultSetProcessorHelperFactory, + ResultSetProcessor resultSetProcessor, + long? afterConditionTime, + int? afterConditionNumberOfEvents, + bool afterConditionSatisfied, + OutputProcessViewConditionFactory parent, + AgentInstanceContext agentInstanceContext) + : base( + resultSetProcessorHelperFactory, agentInstanceContext, resultSetProcessor, afterConditionTime, + afterConditionNumberOfEvents, afterConditionSatisfied) + { + _parent = parent; + + OutputCallback outputCallback = GetCallbackToLocal(parent.StreamCount); + _outputCondition = parent.OutputConditionFactory.Make(agentInstanceContext, outputCallback); + } + + public override int NumChangesetRows + { + get { return Math.Max(_viewEventsList.Count, _joinEventsSet.Count); } + } + + public override OutputCondition OptionalOutputCondition + { + get { return _outputCondition; } + } + + public override OutputProcessViewConditionDeltaSet OptionalDeltaSet + { + get { return null; } + } + + public override OutputProcessViewAfterState OptionalAfterConditionState + { + get { return null; } + } + + private static void AddToChangeSet( + ICollection>>> joinEventsSet, + IEnumerable> newEvents, + IEnumerable> oldEvents) + { + ISet> copyNew; + if (newEvents != null) + { + copyNew = new LinkedHashSet>(newEvents); + } + else + { + copyNew = new LinkedHashSet>(); + } + + ISet> copyOld; + if (oldEvents != null) + { + copyOld = new LinkedHashSet>(oldEvents); + } + else + { + copyOld = new LinkedHashSet>(); + } + joinEventsSet.Add(new UniformPair>>(copyNew, copyOld)); + } + + /// + /// The update method is called if the view does not participate in a join. + /// + /// - new events + /// - old events + public override void Update(EventBean[] newData, EventBean[] oldData) + { + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled)) + { + Log.Debug( + ".update Received update, " + + " newData.Length==" + ((newData == null) ? 0 : newData.Length) + + " oldData.Length==" + ((oldData == null) ? 0 : oldData.Length)); + } + + if (!base.CheckAfterCondition(newData, _parent.StatementContext)) + { + return; + } + + if (!_witnessedFirst) + { + bool isGenerateSynthetic = _parent.StatementResultService.IsMakeSynthetic; + + // Process the events and get the result + _viewEventsList.Add(new UniformPair(newData, oldData)); + UniformPair newOldEvents = ResultSetProcessor.ProcessOutputLimitedView( + _viewEventsList, isGenerateSynthetic, OutputLimitLimitType.FIRST); + _viewEventsList.Clear(); + + if (!HasRelevantResults(newOldEvents)) + { + return; + } + + _witnessedFirst = true; + + if (_parent.IsDistinct) + { + newOldEvents.First = EventBeanUtility.GetDistinctByProp(newOldEvents.First, _parent.EventBeanReader); + newOldEvents.Second = EventBeanUtility.GetDistinctByProp( + newOldEvents.Second, _parent.EventBeanReader); + } + + bool isGenerateNatural = _parent.StatementResultService.IsMakeNatural; + if ((!isGenerateSynthetic) && (!isGenerateNatural)) + { + if (AuditPath.IsAuditEnabled) + { + OutputStrategyUtil.IndicateEarlyReturn(_parent.StatementContext, newOldEvents); + } + return; + } + + Output(true, newOldEvents); + } + else + { + _viewEventsList.Add(new UniformPair(newData, oldData)); + ResultSetProcessor.ProcessOutputLimitedView(_viewEventsList, false, OutputLimitLimitType.FIRST); + _viewEventsList.Clear(); + } + + int newDataLength = 0; + int oldDataLength = 0; + if (newData != null) + { + newDataLength = newData.Length; + } + if (oldData != null) + { + oldDataLength = oldData.Length; + } + + _outputCondition.UpdateOutputCondition(newDataLength, oldDataLength); + } + + /// + /// This process (update) method is for participation in a join. + /// + /// - new events + /// - old events + public override void Process( + ISet> newEvents, + ISet> oldEvents, + ExprEvaluatorContext exprEvaluatorContext) + { + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled)) + { + Log.Debug( + ".process Received update, " + + " newData.Length==" + ((newEvents == null) ? 0 : newEvents.Count) + + " oldData.Length==" + ((oldEvents == null) ? 0 : oldEvents.Count)); + } + + if (!base.CheckAfterCondition(newEvents, _parent.StatementContext)) + { + return; + } + + // add the incoming events to the event batches + if (!_witnessedFirst) + { + AddToChangeSet(_joinEventsSet, newEvents, oldEvents); + bool isGenerateSynthetic = _parent.StatementResultService.IsMakeSynthetic; + UniformPair newOldEvents = ResultSetProcessor.ProcessOutputLimitedJoin( + _joinEventsSet, isGenerateSynthetic, OutputLimitLimitType.FIRST); + _joinEventsSet.Clear(); + + if (!HasRelevantResults(newOldEvents)) + { + return; + } + + _witnessedFirst = true; + + if (_parent.IsDistinct) + { + newOldEvents.First = EventBeanUtility.GetDistinctByProp(newOldEvents.First, _parent.EventBeanReader); + newOldEvents.Second = EventBeanUtility.GetDistinctByProp( + newOldEvents.Second, _parent.EventBeanReader); + } + + bool isGenerateNatural = _parent.StatementResultService.IsMakeNatural; + if ((!isGenerateSynthetic) && (!isGenerateNatural)) + { + if (AuditPath.IsAuditEnabled) + { + OutputStrategyUtil.IndicateEarlyReturn(_parent.StatementContext, newOldEvents); + } + return; + } + + Output(true, newOldEvents); + } + else + { + AddToChangeSet(_joinEventsSet, newEvents, oldEvents); + + // Process the events and get the result + ResultSetProcessor.ProcessOutputLimitedJoin(_joinEventsSet, false, OutputLimitLimitType.FIRST); + _joinEventsSet.Clear(); + } + + int newEventsSize = 0; + if (newEvents != null) + { + newEventsSize = newEvents.Count; + } + + int oldEventsSize = 0; + if (oldEvents != null) + { + oldEventsSize = oldEvents.Count; + } + + _outputCondition.UpdateOutputCondition(newEventsSize, oldEventsSize); + } + + /// + /// Called once the output condition has been met. + /// Invokes the result set processor. + /// Used for non-join event data. + /// + /// + /// - true if the batched events should actually be output as well as processed, false if they + /// should just be processed + /// + /// - true if output should be made even when no updating events have arrived + protected void ContinueOutputProcessingView(bool doOutput, bool forceUpdate) + { + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled)) + { + Log.Debug(".continueOutputProcessingView"); + } + _witnessedFirst = false; + } + + private void Output(bool forceUpdate, UniformPair results) + { + // Child view can be null in replay from named window + if (ChildView != null) + { + OutputStrategyUtil.Output(forceUpdate, results, ChildView); + } + } + + /// + /// Called once the output condition has been met. + /// Invokes the result set processor. + /// Used for join event data. + /// + /// + /// - true if the batched events should actually be output as well as processed, false if they + /// should just be processed + /// + /// - true if output should be made even when no updating events have arrived + protected void ContinueOutputProcessingJoin(bool doOutput, bool forceUpdate) + { + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled)) + { + Log.Debug(".continueOutputProcessingJoin"); + } + _witnessedFirst = false; + } + + private OutputCallback GetCallbackToLocal(int streamCount) + { + // single stream means no join + // multiple streams means a join + if (streamCount == 1) + { + return ContinueOutputProcessingView; + } + else + { + return ContinueOutputProcessingJoin; + } + } + + public override IEnumerator GetEnumerator() + { + return OutputStrategyUtil.GetEnumerator( + JoinExecutionStrategy, ResultSetProcessor, ParentView, _parent.IsDistinct); + } + + public override void Terminated() + { + if (_parent.IsTerminable) + { + _outputCondition.Terminated(); + } + } + + private bool HasRelevantResults(UniformPair newOldEvents) + { + if (newOldEvents == null) + { + return false; + } + if (_parent.SelectClauseStreamSelectorEnum == SelectClauseStreamSelectorEnum.ISTREAM_ONLY) + { + if (newOldEvents.First == null) + { + return false; // nothing to indicate + } + } + else if (_parent.SelectClauseStreamSelectorEnum == SelectClauseStreamSelectorEnum.RSTREAM_ISTREAM_BOTH) + { + if (newOldEvents.First == null && newOldEvents.Second == null) + { + return false; // nothing to indicate + } + } + else + { + if (newOldEvents.Second == null) + { + return false; // nothing to indicate + } + } + return true; + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewConditionFirstPostProcess.cs b/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewConditionFirstPostProcess.cs new file mode 100755 index 000000000..4aba4d379 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewConditionFirstPostProcess.cs @@ -0,0 +1,52 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.core; + +namespace com.espertech.esper.epl.view +{ + /// + /// Handles output rate limiting for FIRST, only applicable with a having-clause + /// and no group-by clause. + /// + /// Without having-clause the order of processing won't matter therefore its + /// handled by the . With + /// group-by the + /// handles the per-group first criteria. + /// + public class OutputProcessViewConditionFirstPostProcess : OutputProcessViewConditionFirst + { + private readonly OutputStrategyPostProcess _postProcessor; + + public OutputProcessViewConditionFirstPostProcess( + ResultSetProcessorHelperFactory resultSetProcessorHelperFactory, + ResultSetProcessor resultSetProcessor, + long? afterConditionTime, + int? afterConditionNumberOfEvents, + bool afterConditionSatisfied, + OutputProcessViewConditionFactory parent, + AgentInstanceContext agentInstanceContext, + OutputStrategyPostProcess postProcessor) + : base(resultSetProcessorHelperFactory, resultSetProcessor, afterConditionTime, afterConditionNumberOfEvents, afterConditionSatisfied, parent, agentInstanceContext) + { + _postProcessor = postProcessor; + } + + public void Output(bool forceUpdate, UniformPair results) + { + // Child view can be null in replay from named window + if (ChildView != null) + { + _postProcessor.Output(forceUpdate, results, ChildView); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewConditionLastAllUnord.cs b/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewConditionLastAllUnord.cs new file mode 100755 index 000000000..0ac4bb0cc --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewConditionLastAllUnord.cs @@ -0,0 +1,216 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; +using com.espertech.esper.events; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.view +{ + /// Handles output rate limiting for LAST and without order-by. + public class OutputProcessViewConditionLastAllUnord : OutputProcessViewBaseWAfter + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly OutputProcessViewConditionFactory _parent; + private readonly OutputCondition _outputCondition; + private readonly bool _isAll; + + public OutputProcessViewConditionLastAllUnord( + ResultSetProcessorHelperFactory resultSetProcessorHelperFactory, + ResultSetProcessor resultSetProcessor, + long? afterConditionTime, + int? afterConditionNumberOfEvents, + bool afterConditionSatisfied, + OutputProcessViewConditionFactory parent, + AgentInstanceContext agentInstanceContext) + : base(resultSetProcessorHelperFactory, agentInstanceContext, resultSetProcessor, afterConditionTime, afterConditionNumberOfEvents, afterConditionSatisfied) + { + _parent = parent; + _isAll = parent.OutputLimitLimitType == OutputLimitLimitType.ALL; + + var outputCallback = GetCallbackToLocal(parent.StreamCount); + _outputCondition = parent.OutputConditionFactory.Make(agentInstanceContext, outputCallback); + } + + public override int NumChangesetRows + { + get { return 0; } + } + + public override OutputCondition OptionalOutputCondition + { + get { return _outputCondition; } + } + + public override OutputProcessViewConditionDeltaSet OptionalDeltaSet + { + get { return null; } + } + + public override OutputProcessViewAfterState OptionalAfterConditionState + { + get { return null; } + } + + public override void Update(EventBean[] newData, EventBean[] oldData) { + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled)) { + Log.Debug(".update Received update, " + + " newData.Length==" + ((newData == null) ? 0 : newData.Length) + + " oldData.Length==" + ((oldData == null) ? 0 : oldData.Length)); + } + + var isGenerateSynthetic = _parent.StatementResultService.IsMakeSynthetic; + ResultSetProcessor.ProcessOutputLimitedLastAllNonBufferedView(newData, oldData, isGenerateSynthetic, _isAll); + + if (!base.CheckAfterCondition(newData, _parent.StatementContext)) { + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().AOutputProcessWCondition(false); + } + return; + } + + var newDataLength = 0; + var oldDataLength = 0; + if (newData != null) { + newDataLength = newData.Length; + } + if (oldData != null) { + oldDataLength = oldData.Length; + } + + _outputCondition.UpdateOutputCondition(newDataLength, oldDataLength); + } + + /// + /// This process (update) method is for participation in a join. + /// + /// - new events + /// - old events + public override void Process(ISet> newEvents, ISet> oldEvents, ExprEvaluatorContext exprEvaluatorContext) { + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled)) { + Log.Debug(".process Received update, " + + " newData.Length==" + ((newEvents == null) ? 0 : newEvents.Count) + + " oldData.Length==" + ((oldEvents == null) ? 0 : oldEvents.Count)); + } + + var isGenerateSynthetic = _parent.StatementResultService.IsMakeSynthetic; + ResultSetProcessor.ProcessOutputLimitedLastAllNonBufferedJoin(newEvents, oldEvents, isGenerateSynthetic, _isAll); + + if (!base.CheckAfterCondition(newEvents, _parent.StatementContext)) { + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().AOutputProcessWCondition(false); + } + return; + } + + var newEventsSize = 0; + if (newEvents != null) { + newEventsSize = newEvents.Count; + } + + var oldEventsSize = 0; + if (oldEvents != null) { + oldEventsSize = oldEvents.Count; + } + _outputCondition.UpdateOutputCondition(newEventsSize, oldEventsSize); + } + + /// + /// Called once the output condition has been met. + /// Invokes the result set processor. + /// Used for non-join event data. + /// + /// - true if the batched events should actually be output as well as processed, false if they should just be processed + /// - true if output should be made even when no updating events have arrived + protected void ContinueOutputProcessingView(bool doOutput, bool forceUpdate) { + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled)) { + Log.Debug(".continueOutputProcessingView"); + } + + var isGenerateSynthetic = _parent.StatementResultService.IsMakeSynthetic; + var newOldEvents = ResultSetProcessor.ContinueOutputLimitedLastAllNonBufferedView(isGenerateSynthetic, _isAll); + + ContinueOutputProcessingViewAndJoin(doOutput, forceUpdate, newOldEvents); + } + + protected virtual void Output(bool forceUpdate, UniformPair results) + { + // Child view can be null in replay from named window + if (ChildView != null) + { + OutputStrategyUtil.Output(forceUpdate, results, ChildView); + } + } + + /// + /// Called once the output condition has been met. + /// Invokes the result set processor. + /// Used for join event data. + /// + /// - true if the batched events should actually be output as well as processed, false if they should just be processed + /// - true if output should be made even when no updating events have arrived + protected void ContinueOutputProcessingJoin(bool doOutput, bool forceUpdate) + { + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled)) { + Log.Debug(".continueOutputProcessingJoin"); + } + + var isGenerateSynthetic = _parent.StatementResultService.IsMakeSynthetic; + var newOldEvents = ResultSetProcessor.ContinueOutputLimitedLastAllNonBufferedJoin(isGenerateSynthetic, _isAll); + + ContinueOutputProcessingViewAndJoin(doOutput, forceUpdate, newOldEvents); + } + + private OutputCallback GetCallbackToLocal(int streamCount) + { + // single stream means no join + // multiple streams means a join + if (streamCount == 1) { + return ContinueOutputProcessingView; + } else { + return ContinueOutputProcessingJoin; + } + } + + public override IEnumerator GetEnumerator() { + return OutputStrategyUtil.GetEnumerator(JoinExecutionStrategy, ResultSetProcessor, ParentView, _parent.IsDistinct); + } + + public override void Terminated() { + if (_parent.IsTerminable) { + _outputCondition.Terminated(); + } + } + + private void ContinueOutputProcessingViewAndJoin(bool doOutput, bool forceUpdate, UniformPair newOldEvents) + { + if (_parent.IsDistinct && newOldEvents != null) { + newOldEvents.First = EventBeanUtility.GetDistinctByProp(newOldEvents.First, _parent.EventBeanReader); + newOldEvents.Second = EventBeanUtility.GetDistinctByProp(newOldEvents.Second, _parent.EventBeanReader); + } + + if (doOutput) { + Output(forceUpdate, newOldEvents); + } + + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().AOutputRateConditionOutputNow(true); + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewConditionLastAllUnordPostProcessAll.cs b/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewConditionLastAllUnordPostProcessAll.cs new file mode 100755 index 000000000..c26f6c48d --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewConditionLastAllUnordPostProcessAll.cs @@ -0,0 +1,43 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.core; + +namespace com.espertech.esper.epl.view +{ + public class OutputProcessViewConditionLastAllUnordPostProcessAll : OutputProcessViewConditionLastAllUnord + { + private readonly OutputStrategyPostProcess _postProcessor; + + public OutputProcessViewConditionLastAllUnordPostProcessAll( + ResultSetProcessorHelperFactory resultSetProcessorHelperFactory, + ResultSetProcessor resultSetProcessor, + long? afterConditionTime, + int? afterConditionNumberOfEvents, + bool afterConditionSatisfied, + OutputProcessViewConditionFactory parent, + AgentInstanceContext agentInstanceContext, + OutputStrategyPostProcess postProcessor) + : base(resultSetProcessorHelperFactory, resultSetProcessor, afterConditionTime, afterConditionNumberOfEvents, afterConditionSatisfied, parent, agentInstanceContext) + { + _postProcessor = postProcessor; + } + + protected override void Output(bool forceUpdate, UniformPair results) + { + // Child view can be null in replay from named window + if (ChildView != null) + { + _postProcessor.Output(forceUpdate, results, ChildView); + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewConditionSnapshot.cs b/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewConditionSnapshot.cs new file mode 100755 index 000000000..d03090895 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewConditionSnapshot.cs @@ -0,0 +1,223 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.view +{ + /// + /// A view that handles the "output snapshot" keyword in output rate stabilizing. + /// + public class OutputProcessViewConditionSnapshot : OutputProcessViewBaseWAfter + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly OutputProcessViewConditionFactory _parent; + private readonly OutputCondition _outputCondition; + + public OutputProcessViewConditionSnapshot( + ResultSetProcessorHelperFactory resultSetProcessorHelperFactory, + ResultSetProcessor resultSetProcessor, + long? afterConditionTime, + int? afterConditionNumberOfEvents, + bool afterConditionSatisfied, + OutputProcessViewConditionFactory parent, + AgentInstanceContext agentInstanceContext) + : base(resultSetProcessorHelperFactory, agentInstanceContext, resultSetProcessor, afterConditionTime, afterConditionNumberOfEvents, afterConditionSatisfied) + { + _parent = parent; + + var outputCallback = GetCallbackToLocal(parent.StreamCount); + _outputCondition = parent.OutputConditionFactory.Make(agentInstanceContext, outputCallback); + } + + public override void Stop() + { + base.Stop(); + _outputCondition.Stop(); + } + + public override int NumChangesetRows + { + get { return 0; } + } + + public override OutputCondition OptionalOutputCondition + { + get { return _outputCondition; } + } + + public override OutputProcessViewConditionDeltaSet OptionalDeltaSet + { + get { return null; } + } + + public override OutputProcessViewAfterState OptionalAfterConditionState + { + get { return null; } + } + + /// + /// The update method is called if the view does not participate in a join. + /// + /// - new events + /// - old events + public override void Update(EventBean[] newData, EventBean[] oldData) { + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled)) { + Log.Debug(".update Received update, " + + " newData.Length==" + ((newData == null) ? 0 : newData.Length) + + " oldData.Length==" + ((oldData == null) ? 0 : oldData.Length)); + } + + ResultSetProcessor.ApplyViewResult(newData, oldData); + + if (!base.CheckAfterCondition(newData, _parent.StatementContext)) { + return; + } + + // add the incoming events to the event batches + int newDataLength = 0; + int oldDataLength = 0; + if (newData != null) { + newDataLength = newData.Length; + } + if (oldData != null) { + oldDataLength = oldData.Length; + } + + _outputCondition.UpdateOutputCondition(newDataLength, oldDataLength); + } + + /// + /// This process (update) method is for participation in a join. + /// + /// - new events + /// - old events + public override void Process(ISet> newEvents, ISet> oldEvents, ExprEvaluatorContext exprEvaluatorContext) + { + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled)) { + Log.Debug(".Process Received update, " + + " newData.Length==" + ((newEvents == null) ? 0 : newEvents.Count) + + " oldData.Length==" + ((oldEvents == null) ? 0 : oldEvents.Count)); + } + + ResultSetProcessor.ApplyJoinResult(newEvents, oldEvents); + + if (!base.CheckAfterCondition(newEvents, _parent.StatementContext)) { + return; + } + + int newEventsSize = 0; + if (newEvents != null) { + // add the incoming events to the event batches + newEventsSize = newEvents.Count; + } + + int oldEventsSize = 0; + if (oldEvents != null) { + oldEventsSize = oldEvents.Count; + } + + _outputCondition.UpdateOutputCondition(newEventsSize, oldEventsSize); + } + + /// + /// Called once the output condition has been met. + /// Invokes the result set processor. + /// Used for non-join event data. + /// + /// - true if the batched events should actually be output as well as processed, false if they should just be processed + /// - true if output should be made even when no updating events have arrived + protected void ContinueOutputProcessingView(bool doOutput, bool forceUpdate) + { + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled)) + { + Log.Debug(".continueOutputProcessingView"); + } + + EventBean[] newEvents = null; + EventBean[] oldEvents = null; + + IEnumerator en = GetEnumerator(); + + var snapshot = new List(); + while (en.MoveNext()) + { + EventBean @event = en.Current; + snapshot.Add(@event); + } + newEvents = snapshot.ToArray(); + oldEvents = null; + + var newOldEvents = new UniformPair(newEvents, oldEvents); + + if (doOutput) + { + Output(forceUpdate, newOldEvents); + } + } + + public virtual void Output(bool forceUpdate, UniformPair results) { + // Child view can be null in replay from named window + if (ChildView != null) { + OutputStrategyUtil.Output(forceUpdate, results, ChildView); + } + } + + /// + /// Called once the output condition has been met. + /// Invokes the result set processor. + /// Used for join event data. + /// + /// - true if the batched events should actually be output as well as processed, false if they should just be processed + /// - true if output should be made even when no updating events have arrived + protected void ContinueOutputProcessingJoin(bool doOutput, bool forceUpdate) { + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled)) { + Log.Debug(".continueOutputProcessingJoin"); + } + ContinueOutputProcessingView(doOutput, forceUpdate); + } + + private OutputCallback GetCallbackToLocal(int streamCount) + { + // single stream means no join + // multiple streams means a join + if (streamCount == 1) + { + return ContinueOutputProcessingView; + } + else + { + return ContinueOutputProcessingJoin; + } + } + + public override IEnumerator GetEnumerator() + { + return OutputStrategyUtil.GetEnumerator( + JoinExecutionStrategy, ResultSetProcessor, ParentView, _parent.IsDistinct); + } + + public override void Terminated() + { + if (_parent.IsTerminable) + { + _outputCondition.Terminated(); + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewConditionSnapshotPostProcess.cs b/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewConditionSnapshotPostProcess.cs new file mode 100755 index 000000000..2e601538a --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewConditionSnapshotPostProcess.cs @@ -0,0 +1,46 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.core; + +namespace com.espertech.esper.epl.view +{ + /// + /// A view that handles the "output snapshot" keyword in output rate stabilizing. + /// + public class OutputProcessViewConditionSnapshotPostProcess : OutputProcessViewConditionSnapshot + { + private readonly OutputStrategyPostProcess _postProcessor; + + public OutputProcessViewConditionSnapshotPostProcess( + ResultSetProcessorHelperFactory resultSetProcessorHelperFactory, + ResultSetProcessor resultSetProcessor, + long? afterConditionTime, + int? afterConditionNumberOfEvents, + bool afterConditionSatisfied, + OutputProcessViewConditionFactory parent, + AgentInstanceContext agentInstanceContext, + OutputStrategyPostProcess postProcessor) + : base(resultSetProcessorHelperFactory, resultSetProcessor, afterConditionTime, afterConditionNumberOfEvents, afterConditionSatisfied, parent, agentInstanceContext) + { + _postProcessor = postProcessor; + } + + public override void Output(bool forceUpdate, UniformPair results) + { + // Child view can be null in replay from named window + if (ChildView != null) + { + _postProcessor.Output(forceUpdate, results, ChildView); + } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewDirect.cs b/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewDirect.cs new file mode 100755 index 000000000..c54df5c02 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewDirect.cs @@ -0,0 +1,147 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.view +{ + /// + /// Output process view that does not enforce any output policies and may simply + /// hand over events to child views, does not handle distinct. + /// + public class OutputProcessViewDirect : OutputProcessViewBase + { + private readonly OutputProcessViewDirectFactory _parent; + + public OutputProcessViewDirect(ResultSetProcessor resultSetProcessor, OutputProcessViewDirectFactory parent) + : base(resultSetProcessor) + { + _parent = parent; + } + + public override int NumChangesetRows + { + get { return 0; } + } + + public override OutputCondition OptionalOutputCondition + { + get { return null; } + } + + public override OutputProcessViewConditionDeltaSet OptionalDeltaSet + { + get { return null; } + } + + public override OutputProcessViewAfterState OptionalAfterConditionState + { + get { return null; } + } + + /// The Update method is called if the view does not participate in a join. + /// new events + /// old events + public override void Update(EventBean[] newData, EventBean[] oldData) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QOutputProcessNonBuffered(newData, oldData);} + + bool isGenerateSynthetic = _parent.StatementResultService.IsMakeSynthetic; + bool isGenerateNatural = _parent.StatementResultService.IsMakeNatural; + + UniformPair newOldEvents = ResultSetProcessor.ProcessViewResult(newData, oldData, isGenerateSynthetic); + + if ((!isGenerateSynthetic) && (!isGenerateNatural)) + { + if (AuditPath.IsAuditEnabled) { + OutputStrategyUtil.IndicateEarlyReturn(_parent.StatementContext, newOldEvents); + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AOutputProcessNonBuffered();} + return; + } + + bool forceOutput = + (newData == null) && + (oldData == null) && + ((newOldEvents == null) || (newOldEvents.First == null && newOldEvents.Second == null)); + + // Child view can be null in replay from named window + if (ChildView != null) + { + PostProcess(forceOutput, newOldEvents, ChildView); + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AOutputProcessNonBuffered();} + } + + /// This process (Update) method is for participation in a join. + /// new events + /// old events + /// + public override void Process(ISet> newEvents, ISet> oldEvents, ExprEvaluatorContext exprEvaluatorContext) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QOutputProcessNonBufferedJoin(newEvents, oldEvents);} + + bool isGenerateSynthetic = _parent.StatementResultService.IsMakeSynthetic; + bool isGenerateNatural = _parent.StatementResultService.IsMakeNatural; + + UniformPair newOldEvents = ResultSetProcessor.ProcessJoinResult(newEvents, oldEvents, isGenerateSynthetic); + + if ((!isGenerateSynthetic) && (!isGenerateNatural)) + { + if (AuditPath.IsAuditEnabled) { + OutputStrategyUtil.IndicateEarlyReturn(_parent.StatementContext, newOldEvents); + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AOutputProcessNonBufferedJoin();} + return; + } + + if (newOldEvents == null) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AOutputProcessNonBufferedJoin();} + return; + } + + // Child view can be null in replay from named window + if (ChildView != null) + { + PostProcess(false, newOldEvents, ChildView); + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AOutputProcessNonBufferedJoin();} + } + + protected virtual void PostProcess(bool force, UniformPair newOldEvents, UpdateDispatchView childView) + { + OutputStrategyUtil.Output(force, newOldEvents, childView); + } + + public override IEnumerator GetEnumerator() + { + return OutputStrategyUtil.GetEnumerator(JoinExecutionStrategy, ResultSetProcessor, Parent, false); + } + + public override void Terminated() + { + // Not applicable + } + + public override void Stop() + { + // Not applicable + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewDirectDistinctOrAfter.cs b/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewDirectDistinctOrAfter.cs new file mode 100755 index 000000000..3a54e2c4b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewDirectDistinctOrAfter.cs @@ -0,0 +1,167 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.view +{ + /// + /// Output process view that does not enforce any output policies and may simply + /// hand over events to child views, but works with distinct and after-output policies + /// + public class OutputProcessViewDirectDistinctOrAfter : OutputProcessViewBaseWAfter + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly OutputProcessViewDirectDistinctOrAfterFactory _parent; + + public OutputProcessViewDirectDistinctOrAfter( + ResultSetProcessorHelperFactory resultSetProcessorHelperFactory, + AgentInstanceContext agentInstanceContext, + ResultSetProcessor resultSetProcessor, + long? afterConditionTime, + int? afterConditionNumberOfEvents, + bool afterConditionSatisfied, + OutputProcessViewDirectDistinctOrAfterFactory parent) + : base(resultSetProcessorHelperFactory, agentInstanceContext, resultSetProcessor, afterConditionTime, afterConditionNumberOfEvents, afterConditionSatisfied) + { + _parent = parent; + } + + public override int NumChangesetRows + { + get { return 0; } + } + + public override OutputCondition OptionalOutputCondition + { + get { return null; } + } + + public override OutputProcessViewConditionDeltaSet OptionalDeltaSet + { + get { return null; } + } + + /// + /// The update method is called if the view does not participate in a join. + /// + /// - new events + /// - old events + public override void Update(EventBean[] newData, EventBean[] oldData) + { + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled)) { + Log.Debug(".update Received update, " + + " newData.Length==" + ((newData == null) ? 0 : newData.Length) + + " oldData.Length==" + ((oldData == null) ? 0 : oldData.Length)); + } + + bool isGenerateSynthetic = _parent.StatementResultService.IsMakeSynthetic; + bool isGenerateNatural = _parent.StatementResultService.IsMakeNatural; + + UniformPair newOldEvents = ResultSetProcessor.ProcessViewResult(newData, oldData, isGenerateSynthetic); + + if (!base.CheckAfterCondition(newOldEvents, _parent.StatementContext)) { + return; + } + + if (_parent.IsDistinct && newOldEvents != null) { + newOldEvents.First = EventBeanUtility.GetDistinctByProp(newOldEvents.First, _parent.EventBeanReader); + newOldEvents.Second = EventBeanUtility.GetDistinctByProp(newOldEvents.Second, _parent.EventBeanReader); + } + + if ((!isGenerateSynthetic) && (!isGenerateNatural)) { + if (AuditPath.IsAuditEnabled) { + OutputStrategyUtil.IndicateEarlyReturn(_parent.StatementContext, newOldEvents); + } + return; + } + + bool forceOutput = false; + if ((newData == null) && (oldData == null) && + ((newOldEvents == null) || (newOldEvents.First == null && newOldEvents.Second == null))) + { + forceOutput = true; + } + + // Child view can be null in replay from named window + if (ChildView != null) { + PostProcess(forceOutput, newOldEvents, ChildView); + } + } + + /// + /// This process (update) method is for participation in a join. + /// + /// - new events + /// - old events + public override void Process(ISet> newEvents, ISet> oldEvents, ExprEvaluatorContext exprEvaluatorContext) + { + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled)) { + Log.Debug(".process Received update, " + + " newData.Length==" + ((newEvents == null) ? 0 : newEvents.Count) + + " oldData.Length==" + ((oldEvents == null) ? 0 : oldEvents.Count)); + } + + bool isGenerateSynthetic = _parent.StatementResultService.IsMakeSynthetic; + bool isGenerateNatural = _parent.StatementResultService.IsMakeNatural; + + UniformPair newOldEvents = ResultSetProcessor.ProcessJoinResult(newEvents, oldEvents, isGenerateSynthetic); + + if (!CheckAfterCondition(newOldEvents, _parent.StatementContext)) { + return; + } + + if (_parent.IsDistinct && newOldEvents != null) { + newOldEvents.First = EventBeanUtility.GetDistinctByProp(newOldEvents.First, _parent.EventBeanReader); + newOldEvents.Second = EventBeanUtility.GetDistinctByProp(newOldEvents.Second, _parent.EventBeanReader); + } + + if ((!isGenerateSynthetic) && (!isGenerateNatural)) { + if (AuditPath.IsAuditEnabled) { + OutputStrategyUtil.IndicateEarlyReturn(_parent.StatementContext, newOldEvents); + } + return; + } + + if (newOldEvents == null) { + return; + } + + // Child view can be null in replay from named window + if (ChildView != null) { + PostProcess(false, newOldEvents, ChildView); + } + } + + protected virtual void PostProcess(bool force, UniformPair newOldEvents, UpdateDispatchView childView) + { + OutputStrategyUtil.Output(force, newOldEvents, childView); + } + + public override IEnumerator GetEnumerator() + { + return OutputStrategyUtil.GetEnumerator(JoinExecutionStrategy, ResultSetProcessor, ParentView, _parent.IsDistinct); + } + + public override void Terminated() + { + // Not applicable + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewDirectDistinctOrAfterFactory.cs b/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewDirectDistinctOrAfterFactory.cs new file mode 100755 index 000000000..13b6a4b82 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewDirectDistinctOrAfterFactory.cs @@ -0,0 +1,96 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.time; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.view +{ + /// + /// Output process view that does not enforce any output policies and may simply + /// hand over events to child views, but works with distinct and after-output policies + /// + public class OutputProcessViewDirectDistinctOrAfterFactory : OutputProcessViewDirectFactory + { + protected readonly ExprTimePeriod AfterTimePeriod; + protected readonly int? AfterConditionNumberOfEvents; + private readonly bool _isDistinct; + private readonly EventBeanReader _eventBeanReader; + + public OutputProcessViewDirectDistinctOrAfterFactory( + StatementContext statementContext, + OutputStrategyPostProcessFactory postProcessFactory, + ResultSetProcessorHelperFactory resultSetProcessorHelperFactory, + bool distinct, + ExprTimePeriod afterTimePeriod, + int? afterConditionNumberOfEvents, + EventType resultEventType) + : base(statementContext, postProcessFactory, resultSetProcessorHelperFactory) + { + _isDistinct = distinct; + AfterTimePeriod = afterTimePeriod; + AfterConditionNumberOfEvents = afterConditionNumberOfEvents; + + if (_isDistinct) + { + if (resultEventType is EventTypeSPI) + { + EventTypeSPI eventTypeSPI = (EventTypeSPI) resultEventType; + _eventBeanReader = eventTypeSPI.Reader; + } + if (_eventBeanReader == null) + { + _eventBeanReader = new EventBeanReaderDefaultImpl(resultEventType); + } + } + } + + public override OutputProcessViewBase MakeView( + ResultSetProcessor resultSetProcessor, + AgentInstanceContext agentInstanceContext) + { + bool isAfterConditionSatisfied = true; + long? afterConditionTime = null; + if (AfterConditionNumberOfEvents != null) + { + isAfterConditionSatisfied = false; + } + else if (AfterTimePeriod != null) + { + isAfterConditionSatisfied = false; + long delta = AfterTimePeriod.NonconstEvaluator().DeltaUseEngineTime(null, agentInstanceContext); + afterConditionTime = agentInstanceContext.StatementContext.TimeProvider.Time + delta; + } + + if (base.PostProcessFactory == null) + { + return new OutputProcessViewDirectDistinctOrAfter( + ResultSetProcessorHelperFactory, agentInstanceContext, resultSetProcessor, afterConditionTime, + AfterConditionNumberOfEvents, isAfterConditionSatisfied, this); + } + OutputStrategyPostProcess postProcess = PostProcessFactory.Make(agentInstanceContext); + return new OutputProcessViewDirectDistinctOrAfterPostProcess( + ResultSetProcessorHelperFactory, agentInstanceContext, resultSetProcessor, afterConditionTime, + AfterConditionNumberOfEvents, isAfterConditionSatisfied, this, postProcess); + } + + public bool IsDistinct + { + get { return _isDistinct; } + } + + public EventBeanReader EventBeanReader + { + get { return _eventBeanReader; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewDirectDistinctOrAfterPostProcess.cs b/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewDirectDistinctOrAfterPostProcess.cs new file mode 100755 index 000000000..0fdc942a2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewDirectDistinctOrAfterPostProcess.cs @@ -0,0 +1,43 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.core; + +namespace com.espertech.esper.epl.view +{ + /// + /// Output process view that does not enforce any output policies and may simply hand over events to child views, but works with distinct and after-output policies + /// + public class OutputProcessViewDirectDistinctOrAfterPostProcess : OutputProcessViewDirectDistinctOrAfter + { + private readonly OutputStrategyPostProcess _postProcessor; + + public OutputProcessViewDirectDistinctOrAfterPostProcess( + ResultSetProcessorHelperFactory resultSetProcessorHelperFactory, + AgentInstanceContext agentInstanceContext, + ResultSetProcessor resultSetProcessor, + long? afterConditionTime, + int? afterConditionNumberOfEvents, + bool afterConditionSatisfied, + OutputProcessViewDirectDistinctOrAfterFactory parent, + OutputStrategyPostProcess postProcessor) + : base(resultSetProcessorHelperFactory, agentInstanceContext, resultSetProcessor, afterConditionTime, afterConditionNumberOfEvents, afterConditionSatisfied, parent) + { + _postProcessor = postProcessor; + } + + protected override void PostProcess(bool force, UniformPair newOldEvents, UpdateDispatchView childView) + { + _postProcessor.Output(force, newOldEvents, childView); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewDirectFactory.cs b/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewDirectFactory.cs new file mode 100755 index 000000000..f2d08f81b --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewDirectFactory.cs @@ -0,0 +1,59 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.core; + +namespace com.espertech.esper.epl.view +{ + /// + /// Factory for output process view that does not enforce any output policies and may simply + /// hand over events to child views, does not handle distinct. + /// + public class OutputProcessViewDirectFactory : OutputProcessViewFactory + { + private readonly StatementContext _statementContext; + private readonly StatementResultService _statementResultService; + protected readonly OutputStrategyPostProcessFactory PostProcessFactory; + protected readonly ResultSetProcessorHelperFactory ResultSetProcessorHelperFactory; + + public OutputProcessViewDirectFactory( + StatementContext statementContext, + OutputStrategyPostProcessFactory postProcessFactory, + ResultSetProcessorHelperFactory resultSetProcessorHelperFactory) + { + _statementContext = statementContext; + _statementResultService = statementContext.StatementResultService; + PostProcessFactory = postProcessFactory; + ResultSetProcessorHelperFactory = resultSetProcessorHelperFactory; + } + + public virtual OutputProcessViewBase MakeView( + ResultSetProcessor resultSetProcessor, + AgentInstanceContext agentInstanceContext) + { + if (PostProcessFactory == null) + { + return new OutputProcessViewDirect(resultSetProcessor, this); + } + OutputStrategyPostProcess postProcess = PostProcessFactory.Make(agentInstanceContext); + return new OutputProcessViewDirectPostProcess(resultSetProcessor, this, postProcess); + } + + public StatementResultService StatementResultService + { + get { return _statementResultService; } + } + + public StatementContext StatementContext + { + get { return _statementContext; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewDirectPostProcess.cs b/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewDirectPostProcess.cs new file mode 100755 index 000000000..705e09a94 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewDirectPostProcess.cs @@ -0,0 +1,31 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.core; + +namespace com.espertech.esper.epl.view +{ + public class OutputProcessViewDirectPostProcess : OutputProcessViewDirect + { + private readonly OutputStrategyPostProcess _postProcessor; + + public OutputProcessViewDirectPostProcess(ResultSetProcessor resultSetProcessor, OutputProcessViewDirectFactory parent, OutputStrategyPostProcess postProcessor) + : base(resultSetProcessor, parent) + { + _postProcessor = postProcessor; + } + + protected override void PostProcess(bool force, UniformPair newOldEvents, UpdateDispatchView childView) + { + _postProcessor.Output(force, newOldEvents, childView); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewFactory.cs b/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewFactory.cs new file mode 100755 index 000000000..ea837111c --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewFactory.cs @@ -0,0 +1,19 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.core; + +namespace com.espertech.esper.epl.view +{ + /// Factory for output processing views. + public interface OutputProcessViewFactory + { + OutputProcessViewBase MakeView(ResultSetProcessor resultSetProcessor, AgentInstanceContext agentInstanceContext); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewFactoryCallback.cs b/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewFactoryCallback.cs new file mode 100755 index 000000000..b7b10db31 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewFactoryCallback.cs @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.core; + +namespace com.espertech.esper.epl.view +{ + /// Factory for output processing views. + public class OutputProcessViewFactoryCallback : OutputProcessViewFactory + { + private readonly OutputProcessViewCallback _callback; + + public OutputProcessViewFactoryCallback(OutputProcessViewCallback callback) + { + _callback = callback; + } + + #region OutputProcessViewFactory Members + + public OutputProcessViewBase MakeView(ResultSetProcessor resultSetProcessor, + AgentInstanceContext agentInstanceContext) + { + return new OutputProcessViewBaseCallback(resultSetProcessor, _callback); + } + + #endregion + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewFactoryFactory.cs b/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewFactoryFactory.cs new file mode 100755 index 000000000..3b293e101 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewFactoryFactory.cs @@ -0,0 +1,191 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.client.annotation; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.epl.util; + +namespace com.espertech.esper.epl.view +{ + /// Factory for factories for output processing views. + public class OutputProcessViewFactoryFactory + { + public static OutputProcessViewFactory Make( + StatementSpecCompiled statementSpec, + InternalEventRouter internalEventRouter, + StatementContext statementContext, + EventType resultEventType, + OutputProcessViewCallback optionalOutputProcessViewCallback, + TableService tableService, + ResultSetProcessorType resultSetProcessorType, + ResultSetProcessorHelperFactory resultSetProcessorHelperFactory, + StatementVariableRef statementVariableRef) + { + // determine direct-callback + if (optionalOutputProcessViewCallback != null) + { + return new OutputProcessViewFactoryCallback(optionalOutputProcessViewCallback); + } + + // determine routing + bool isRouted = false; + bool routeToFront = false; + if (statementSpec.InsertIntoDesc != null) + { + isRouted = true; + routeToFront = + statementContext.NamedWindowMgmtService.IsNamedWindow(statementSpec.InsertIntoDesc.EventTypeName); + } + + OutputStrategyPostProcessFactory outputStrategyPostProcessFactory = null; + if ((statementSpec.InsertIntoDesc != null) || + (statementSpec.SelectStreamSelectorEnum == SelectClauseStreamSelectorEnum.RSTREAM_ONLY)) + { + SelectClauseStreamSelectorEnum? insertIntoStreamSelector = null; + string tableName = null; + + if (statementSpec.InsertIntoDesc != null) + { + insertIntoStreamSelector = statementSpec.InsertIntoDesc.StreamSelector; + TableMetadata tableMetadata = + tableService.GetTableMetadata(statementSpec.InsertIntoDesc.EventTypeName); + if (tableMetadata != null) + { + tableName = tableMetadata.TableName; + EPLValidationUtil.ValidateContextName( + true, tableName, tableMetadata.ContextName, statementSpec.OptionalContextName, true); + statementVariableRef.AddReferences(statementContext.StatementName, tableMetadata.TableName); + } + } + + outputStrategyPostProcessFactory = new OutputStrategyPostProcessFactory( + isRouted, insertIntoStreamSelector, statementSpec.SelectStreamSelectorEnum, internalEventRouter, + statementContext.EpStatementHandle, routeToFront, tableService, tableName); + } + + // Do we need to enforce an output policy? + int streamCount = statementSpec.StreamSpecs.Length; + OutputLimitSpec outputLimitSpec = statementSpec.OutputLimitSpec; + bool isDistinct = statementSpec.SelectClauseSpec.IsDistinct; + bool isGrouped = statementSpec.GroupByExpressions != null && + statementSpec.GroupByExpressions.GroupByNodes.Length > 0; + + OutputProcessViewFactory outputProcessViewFactory; + if (outputLimitSpec == null) + { + if (!isDistinct) + { + outputProcessViewFactory = new OutputProcessViewDirectFactory( + statementContext, outputStrategyPostProcessFactory, resultSetProcessorHelperFactory); + } + else + { + outputProcessViewFactory = new OutputProcessViewDirectDistinctOrAfterFactory( + statementContext, outputStrategyPostProcessFactory, resultSetProcessorHelperFactory, isDistinct, + null, null, resultEventType); + } + } + else if (outputLimitSpec.RateType == OutputLimitRateType.AFTER) + { + outputProcessViewFactory = new OutputProcessViewDirectDistinctOrAfterFactory( + statementContext, outputStrategyPostProcessFactory, resultSetProcessorHelperFactory, isDistinct, + outputLimitSpec.AfterTimePeriodExpr, outputLimitSpec.AfterNumberOfEvents, resultEventType); + } + else + { + try + { + bool isWithHavingClause = statementSpec.HavingExprRootNode != null; + bool isStartConditionOnCreation = HasOnlyTables(statementSpec.StreamSpecs); + OutputConditionFactory outputConditionFactory = + OutputConditionFactoryFactory.CreateCondition( + outputLimitSpec, statementContext, isGrouped, isWithHavingClause, isStartConditionOnCreation, + resultSetProcessorHelperFactory); + bool hasOrderBy = statementSpec.OrderByList != null && statementSpec.OrderByList.Length > 0; + OutputProcessViewConditionFactory.ConditionType conditionType; + bool hasAfter = outputLimitSpec.AfterNumberOfEvents != null || + outputLimitSpec.AfterTimePeriodExpr != null; + bool isUnaggregatedUngrouped = resultSetProcessorType == ResultSetProcessorType.HANDTHROUGH || + resultSetProcessorType == + ResultSetProcessorType.UNAGGREGATED_UNGROUPED; + + // hint checking with order-by + bool hasOptHint = HintEnum.ENABLE_OUTPUTLIMIT_OPT.GetHint(statementSpec.Annotations) != null; + if (hasOptHint && hasOrderBy) + { + throw new ExprValidationException( + "The " + HintEnum.ENABLE_OUTPUTLIMIT_OPT + " hint is not supported with order-by"); + } + + if (outputLimitSpec.DisplayLimit == OutputLimitLimitType.SNAPSHOT) + { + conditionType = OutputProcessViewConditionFactory.ConditionType.SNAPSHOT; + } + else if (outputLimitSpec.DisplayLimit == OutputLimitLimitType.FIRST && + statementSpec.GroupByExpressions == null) + { + // For FIRST without groups we are using a special logic that integrates the first-flag, in order to still conveniently use all sorts of output conditions. + // FIRST with group-by is handled by setting the output condition to null (OutputConditionNull) and letting the ResultSetProcessor handle first-per-group. + // Without having-clause there is no required order of processing, thus also use regular policy. + conditionType = OutputProcessViewConditionFactory.ConditionType.POLICY_FIRST; + } + else if (isUnaggregatedUngrouped && outputLimitSpec.DisplayLimit == OutputLimitLimitType.LAST) + { + conditionType = OutputProcessViewConditionFactory.ConditionType.POLICY_LASTALL_UNORDERED; + } + else if (hasOptHint && outputLimitSpec.DisplayLimit == OutputLimitLimitType.ALL && !hasOrderBy) + { + conditionType = OutputProcessViewConditionFactory.ConditionType.POLICY_LASTALL_UNORDERED; + } + else if (hasOptHint && outputLimitSpec.DisplayLimit == OutputLimitLimitType.LAST && !hasOrderBy) + { + conditionType = OutputProcessViewConditionFactory.ConditionType.POLICY_LASTALL_UNORDERED; + } + else + { + conditionType = OutputProcessViewConditionFactory.ConditionType.POLICY_NONFIRST; + } + + SelectClauseStreamSelectorEnum selectClauseStreamSelectorEnum = + statementSpec.SelectStreamSelectorEnum; + bool terminable = outputLimitSpec.RateType == OutputLimitRateType.TERM || + outputLimitSpec.IsAndAfterTerminate; + outputProcessViewFactory = new OutputProcessViewConditionFactory( + statementContext, outputStrategyPostProcessFactory, isDistinct, + outputLimitSpec.AfterTimePeriodExpr, outputLimitSpec.AfterNumberOfEvents, resultEventType, + outputConditionFactory, streamCount, conditionType, outputLimitSpec.DisplayLimit, terminable, + hasAfter, isUnaggregatedUngrouped, selectClauseStreamSelectorEnum, + resultSetProcessorHelperFactory); + } + catch (Exception ex) + { + throw new ExprValidationException("Error in the output rate limiting clause: " + ex.Message, ex); + } + } + + return outputProcessViewFactory; + } + + private static bool HasOnlyTables(StreamSpecCompiled[] streamSpecs) + { + if (streamSpecs.Length == 0) + { + return false; + } + return streamSpecs.All(streamSpec => streamSpec is TableQueryStreamSpec); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewTerminable.cs b/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewTerminable.cs new file mode 100755 index 000000000..6469a8a77 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputProcessViewTerminable.cs @@ -0,0 +1,15 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.view +{ + public interface OutputProcessViewTerminable + { + void Terminated(); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputStrategyPostProcess.cs b/NEsper.Core/NEsper.Core/epl/view/OutputStrategyPostProcess.cs new file mode 100755 index 000000000..53ed6382f --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputStrategyPostProcess.cs @@ -0,0 +1,112 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.client.annotation; +using com.espertech.esper.collection; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.epl.table.strategy; +using com.espertech.esper.events; +using com.espertech.esper.util; + +namespace com.espertech.esper.epl.view +{ + /// + /// An output strategy that handles routing (insert-into) and stream selection. + /// + public class OutputStrategyPostProcess + { + private readonly OutputStrategyPostProcessFactory _parent; + private readonly AgentInstanceContext _agentInstanceContext; + private readonly TableStateInstance _tableStateInstance; + private readonly bool _audit; + + public OutputStrategyPostProcess(OutputStrategyPostProcessFactory parent, AgentInstanceContext agentInstanceContext, TableStateInstance tableStateInstance) + { + _parent = parent; + _agentInstanceContext = agentInstanceContext; + _tableStateInstance = tableStateInstance; + _audit = AuditEnum.INSERT.GetAudit(agentInstanceContext.StatementContext.Annotations) != null; + } + + public void Output(bool forceUpdate, UniformPair result, UpdateDispatchView finalView) + { + var newEvents = result != null ? result.First : null; + var oldEvents = result != null ? result.Second : null; + + // route first + if (_parent.IsRoute) + { + if ((newEvents != null) && (_parent.InsertIntoStreamSelector.Value.IsSelectsIStream())) + { + Route(newEvents, _agentInstanceContext); + } + + if ((oldEvents != null) && (_parent.InsertIntoStreamSelector.Value.IsSelectsRStream())) + { + Route(oldEvents, _agentInstanceContext); + } + } + + // discard one side of results + if (_parent.SelectStreamDirEnum == SelectClauseStreamSelectorEnum.RSTREAM_ONLY) + { + newEvents = oldEvents; + oldEvents = null; + } + else if (_parent.SelectStreamDirEnum == SelectClauseStreamSelectorEnum.ISTREAM_ONLY) + { + oldEvents = null; // since the insert-into may require rstream + } + + // dispatch + if(newEvents != null || oldEvents != null) + { + finalView.NewResult(new UniformPair(newEvents, oldEvents)); + } + else if(forceUpdate) + { + finalView.NewResult(new UniformPair(null, null)); + } + } + + private void Route(EventBean[] events, ExprEvaluatorContext exprEvaluatorContext) + { + foreach (var routed in events) { + if (routed is NaturalEventBean) { + var natural = (NaturalEventBean) routed; + if (_audit) { + AuditPath.AuditInsertInto(_agentInstanceContext.EngineURI, _agentInstanceContext.StatementName, natural.OptionalSynthetic); + } + if (_tableStateInstance != null) { + _tableStateInstance.AddEventUnadorned(natural.OptionalSynthetic); + } + else { + _parent.InternalEventRouter.Route(natural.OptionalSynthetic, _parent.EpStatementHandle, _agentInstanceContext.StatementContext.InternalEventEngineRouteDest, exprEvaluatorContext, _parent.IsAddToFront); + } + } + else { + if (_audit) { + AuditPath.AuditInsertInto(_agentInstanceContext.EngineURI, _agentInstanceContext.StatementName, routed); + } + if (_tableStateInstance != null) { + ExprTableEvalLockUtil.ObtainLockUnless(_tableStateInstance.TableLevelRWLock.WriteLock, exprEvaluatorContext); + _tableStateInstance.AddEventUnadorned(routed); + } + else { + _parent.InternalEventRouter.Route(routed, _parent.EpStatementHandle, _agentInstanceContext.StatementContext.InternalEventEngineRouteDest, exprEvaluatorContext, _parent.IsAddToFront); + } + } + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputStrategyPostProcessFactory.cs b/NEsper.Core/NEsper.Core/epl/view/OutputStrategyPostProcessFactory.cs new file mode 100755 index 000000000..830eccaef --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputStrategyPostProcessFactory.cs @@ -0,0 +1,67 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.table.mgmt; + +namespace com.espertech.esper.epl.view +{ + /// + /// An output strategy that handles routing (insert-into) and stream selection. + /// + public class OutputStrategyPostProcessFactory + { + public OutputStrategyPostProcessFactory( + bool route, + SelectClauseStreamSelectorEnum? insertIntoStreamSelector, + SelectClauseStreamSelectorEnum selectStreamDirEnum, + InternalEventRouter internalEventRouter, + EPStatementHandle epStatementHandle, + bool addToFront, + TableService tableService, + string tableName) + { + IsRoute = route; + InsertIntoStreamSelector = insertIntoStreamSelector; + SelectStreamDirEnum = selectStreamDirEnum; + InternalEventRouter = internalEventRouter; + EpStatementHandle = epStatementHandle; + IsAddToFront = addToFront; + TableService = tableService; + TableName = tableName; + } + + public bool IsRoute { get; private set; } + + public SelectClauseStreamSelectorEnum? InsertIntoStreamSelector { get; private set; } + + public SelectClauseStreamSelectorEnum SelectStreamDirEnum { get; private set; } + + public InternalEventRouter InternalEventRouter { get; private set; } + + public EPStatementHandle EpStatementHandle { get; private set; } + + public bool IsAddToFront { get; private set; } + + public TableService TableService { get; private set; } + + public string TableName { get; private set; } + + public OutputStrategyPostProcess Make(AgentInstanceContext agentInstanceContext) + { + TableStateInstance tableStateInstance = null; + if (TableName != null) + { + tableStateInstance = TableService.GetState(TableName, agentInstanceContext.AgentInstanceId); + } + return new OutputStrategyPostProcess(this, agentInstanceContext, tableStateInstance); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputStrategyUtil.cs b/NEsper.Core/NEsper.Core/epl/view/OutputStrategyUtil.cs new file mode 100755 index 000000000..91051faed --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputStrategyUtil.cs @@ -0,0 +1,117 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.join.@base; +using com.espertech.esper.events; +using com.espertech.esper.view; + +namespace com.espertech.esper.epl.view +{ + public class OutputStrategyUtil + { + public static void Output(bool forceUpdate, UniformPair result, UpdateDispatchView finalView) + { + EventBean[] newEvents = result != null ? result.First : null; + EventBean[] oldEvents = result != null ? result.Second : null; + if(newEvents != null || oldEvents != null) + { + finalView.NewResult(result); + } + else if(forceUpdate) + { + finalView.NewResult(result); + } + } + + /// + /// Indicate statement result. + /// + /// The statement context. + /// result + public static void IndicateEarlyReturn(StatementContext statementContext, UniformPair newOldEvents) { + if (newOldEvents == null) { + return; + } + if ((statementContext.MetricReportingService != null) && + (statementContext.MetricReportingService.StatementOutputHooks != null) && + (!statementContext.MetricReportingService.StatementOutputHooks.IsEmpty())) { + foreach (StatementResultListener listener in statementContext.MetricReportingService.StatementOutputHooks) { + listener.Update(newOldEvents.First, newOldEvents.Second, statementContext.StatementName, null, null); + } + } + } + + public static IEnumerator GetEnumerator(JoinExecutionStrategy joinExecutionStrategy, ResultSetProcessor resultSetProcessor, Viewable parentView, bool distinct) + { + IEnumerator enumerator; + EventType eventType; + if (joinExecutionStrategy != null) + { + var joinSet = joinExecutionStrategy.StaticJoin(); + enumerator = resultSetProcessor.GetEnumerator(joinSet); + eventType = resultSetProcessor.ResultEventType; + } + else if (resultSetProcessor != null) + { + enumerator = resultSetProcessor.GetEnumerator(parentView); + eventType = resultSetProcessor.ResultEventType; + } + else + { + enumerator = parentView.GetEnumerator(); + eventType = parentView.EventType; + } + + if (!distinct) + { + return enumerator; + } + + return DistinctEnumeration(enumerator, eventType); + } + + private static IEnumerator DistinctEnumeration(IEnumerator source, EventType eventType) + { + EventBeanReader eventBeanReader = null; + if (eventType is EventTypeSPI) + { + eventBeanReader = ((EventTypeSPI) eventType).Reader; + } + if (eventBeanReader == null) + { + eventBeanReader = new EventBeanReaderDefaultImpl(eventType); + } + + if (source != null) + { + var events = new LinkedList(); + while(source.MoveNext()) + { + events.AddLast(source.Current); + } + + if (events.Count <= 1) + { + return events.GetEnumerator(); + } + + IList result = EventBeanUtility.GetDistinctByProp(events, eventBeanReader); + return result.GetEnumerator(); + } + + return EnumerationHelper.Empty(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/view/OutputTerminationCallback.cs b/NEsper.Core/NEsper.Core/epl/view/OutputTerminationCallback.cs new file mode 100755 index 000000000..4106c5dec --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/view/OutputTerminationCallback.cs @@ -0,0 +1,15 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.epl.view +{ + public interface OutputTerminationCallback + { + void Terminated(); + } +} diff --git a/NEsper.Core/NEsper.Core/epl/virtualdw/JoinExecTableLookupStrategyVirtualDW.cs b/NEsper.Core/NEsper.Core/epl/virtualdw/JoinExecTableLookupStrategyVirtualDW.cs new file mode 100755 index 000000000..b099832fd --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/virtualdw/JoinExecTableLookupStrategyVirtualDW.cs @@ -0,0 +1,152 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.client.hook; +using com.espertech.esper.compat; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.@join.exec.@base; +using com.espertech.esper.epl.@join.plan; +using com.espertech.esper.epl.@join.rep; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.epl.virtualdw +{ + public class JoinExecTableLookupStrategyVirtualDW : JoinExecTableLookupStrategy + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly String _namedWindowName; + private readonly VirtualDataWindowLookup _externalIndex; + private readonly ExternalEvaluator[] _evaluators; + private readonly EventBean[] _eventsPerStream; + private readonly int _lookupStream; + + public JoinExecTableLookupStrategyVirtualDW(String namedWindowName, VirtualDataWindowLookup externalIndex, TableLookupKeyDesc keyDescriptor, int lookupStream) + { + _namedWindowName = namedWindowName; + _externalIndex = externalIndex; + _evaluators = new ExternalEvaluator[keyDescriptor.Hashes.Count + keyDescriptor.Ranges.Count]; + _eventsPerStream = new EventBean[lookupStream + 1]; + _lookupStream = lookupStream; + + var count = 0; + foreach (var hashKey in keyDescriptor.Hashes) + { + var evaluator = hashKey.KeyExpr.ExprEvaluator; + _evaluators[count] = new ExternalEvaluatorHashRelOp(evaluator); + count++; + } + foreach (var rangeKey in keyDescriptor.Ranges) + { + if (rangeKey.RangeType.IsRange()) + { + var range = (QueryGraphValueEntryRangeIn)rangeKey; + var evaluatorStart = range.ExprStart.ExprEvaluator; + var evaluatorEnd = range.ExprEnd.ExprEvaluator; + _evaluators[count] = new ExternalEvaluatorBtreeRange(evaluatorStart, evaluatorEnd); + } + else + { + var relOp = (QueryGraphValueEntryRangeRelOp)rangeKey; + var evaluator = relOp.Expression.ExprEvaluator; + _evaluators[count] = new ExternalEvaluatorHashRelOp(evaluator); + } + count++; + } + } + + public ICollection Lookup(EventBean theEvent, Cursor cursor, ExprEvaluatorContext context) + { + var events = new Mutable>(); + + using (Instrument.With( + i => i.QIndexJoinLookup(this, null), + i => i.AIndexJoinLookup(events.Value, null))) + { + _eventsPerStream[_lookupStream] = theEvent; + + var keys = new Object[_evaluators.Length]; + for (var i = 0; i < _evaluators.Length; i++) + { + keys[i] = _evaluators[i].Evaluate(_eventsPerStream, context); + } + + try + { + events.Value = _externalIndex.Lookup(keys, _eventsPerStream); + } + catch (Exception ex) + { + Log.Warn( + "Exception encountered invoking virtual data window external index for window '" + + _namedWindowName + "': " + ex.Message, ex); + } + + return events.Value; + } + } + + public String ToQueryPlan() + { + return GetType().FullName + " external index " + _externalIndex; + } + + public LookupStrategyDesc StrategyDesc + { + get { return new LookupStrategyDesc(LookupStrategyType.VDW, null); } + } + + private interface ExternalEvaluator + { + Object Evaluate(EventBean[] events, ExprEvaluatorContext context); + } + + internal class ExternalEvaluatorHashRelOp : ExternalEvaluator + { + private readonly ExprEvaluator _hashKeysEval; + + internal ExternalEvaluatorHashRelOp(ExprEvaluator hashKeysEval) + { + _hashKeysEval = hashKeysEval; + } + + public Object Evaluate(EventBean[] events, ExprEvaluatorContext context) + { + return _hashKeysEval.Evaluate(new EvaluateParams(events, true, context)); + } + } + + internal class ExternalEvaluatorBtreeRange : ExternalEvaluator + { + private readonly ExprEvaluator _startEval; + private readonly ExprEvaluator _endEval; + + internal ExternalEvaluatorBtreeRange(ExprEvaluator startEval, ExprEvaluator endEval) + { + _startEval = startEval; + _endEval = endEval; + } + + public Object Evaluate(EventBean[] events, ExprEvaluatorContext context) + { + var evaluateParams = new EvaluateParams(events, true, context); + var start = _startEval.Evaluate(evaluateParams); + var end = _endEval.Evaluate(evaluateParams); + return new VirtualDataWindowKeyRange(start, end); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/virtualdw/SubordTableLookupStrategyVirtualDW.cs b/NEsper.Core/NEsper.Core/epl/virtualdw/SubordTableLookupStrategyVirtualDW.cs new file mode 100755 index 000000000..a3d24db4e --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/virtualdw/SubordTableLookupStrategyVirtualDW.cs @@ -0,0 +1,163 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.client.hook; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.@join.plan; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.events; + +namespace com.espertech.esper.epl.virtualdw +{ + public class SubordTableLookupStrategyVirtualDW : SubordTableLookupStrategy + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly String _namedWindowName; + private readonly VirtualDataWindowLookup _externalIndex; + private readonly ExternalEvaluator[] _evaluators; + private readonly bool _nwOnTrigger; + private readonly EventBean[] _eventsLocal; + + public SubordTableLookupStrategyVirtualDW( + String namedWindowName, + VirtualDataWindowLookup externalIndex, + IList hashKeys, + CoercionDesc hashKeyCoercionTypes, + IList rangeKeys, + CoercionDesc rangeKeyCoercionTypes, + bool nwOnTrigger, + int numOuterStreams) + { + _namedWindowName = namedWindowName; + _externalIndex = externalIndex; + _evaluators = new ExternalEvaluator[hashKeys.Count + rangeKeys.Count]; + _nwOnTrigger = nwOnTrigger; + _eventsLocal = new EventBean[numOuterStreams + 1]; + + var count = 0; + foreach (var hashKey in hashKeys) + { + var evaluator = hashKey.HashKey.KeyExpr.ExprEvaluator; + _evaluators[count] = new ExternalEvaluatorHashRelOp(evaluator, hashKeyCoercionTypes.CoercionTypes[count]); + count++; + } + for (var i = 0; i < rangeKeys.Count; i++) + { + SubordPropRangeKey rangeKey = rangeKeys[i]; + if (rangeKey.RangeInfo.RangeType.IsRange()) + { + var range = (QueryGraphValueEntryRangeIn)rangeKey.RangeInfo; + var evaluatorStart = range.ExprStart.ExprEvaluator; + var evaluatorEnd = range.ExprEnd.ExprEvaluator; + _evaluators[count] = new ExternalEvaluatorBtreeRange(evaluatorStart, evaluatorEnd, rangeKeyCoercionTypes.CoercionTypes[i]); + } + else + { + var relOp = (QueryGraphValueEntryRangeRelOp)rangeKey.RangeInfo; + var evaluator = relOp.Expression.ExprEvaluator; + _evaluators[count] = new ExternalEvaluatorHashRelOp(evaluator, rangeKeyCoercionTypes.CoercionTypes[i]); + } + count++; + } + } + + public ICollection Lookup(EventBean[] eventsPerStream, ExprEvaluatorContext context) + { + EventBean[] events; + if (_nwOnTrigger) + { + events = eventsPerStream; + } + else + { + Array.Copy(eventsPerStream, 0, _eventsLocal, 1, eventsPerStream.Length); + events = _eventsLocal; + } + var keys = new Object[_evaluators.Length]; + for (var i = 0; i < _evaluators.Length; i++) + { + keys[i] = _evaluators[i].Evaluate(events, context); + } + + ISet data = null; + try + { + data = _externalIndex.Lookup(keys, eventsPerStream); + } + catch (Exception ex) + { + Log.Warn("Exception encountered invoking virtual data window external index for window '" + _namedWindowName + "': " + ex.Message, ex); + } + return data; + } + + public String ToQueryPlan() + { + return GetType().FullName + " external index " + _externalIndex; + } + + public LookupStrategyDesc StrategyDesc + { + get { return new LookupStrategyDesc(LookupStrategyType.VDW, null); } + } + + internal interface ExternalEvaluator + { + Object Evaluate(EventBean[] events, ExprEvaluatorContext context); + } + + internal class ExternalEvaluatorHashRelOp : ExternalEvaluator + { + private readonly ExprEvaluator _hashKeysEval; + private readonly Type _coercionType; + + internal ExternalEvaluatorHashRelOp(ExprEvaluator hashKeysEval, Type coercionType) + { + _hashKeysEval = hashKeysEval; + _coercionType = coercionType; + } + + public Object Evaluate(EventBean[] events, ExprEvaluatorContext context) + { + return EventBeanUtility.Coerce( + _hashKeysEval.Evaluate(new EvaluateParams(events, true, context)), + _coercionType); + } + } + + internal class ExternalEvaluatorBtreeRange : ExternalEvaluator + { + private readonly ExprEvaluator _startEval; + private readonly ExprEvaluator _endEval; + private readonly Type _coercionType; + + internal ExternalEvaluatorBtreeRange(ExprEvaluator startEval, ExprEvaluator endEval, Type coercionType) + { + _startEval = startEval; + _endEval = endEval; + _coercionType = coercionType; + } + + public Object Evaluate(EventBean[] events, ExprEvaluatorContext context) + { + var evaluateParams = new EvaluateParams(events, true, context); + var start = EventBeanUtility.Coerce(_startEval.Evaluate(evaluateParams), _coercionType); + var end = EventBeanUtility.Coerce(_endEval.Evaluate(evaluateParams), _coercionType); + return new VirtualDataWindowKeyRange(start, end); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/virtualdw/VirtualDWEventTable.cs b/NEsper.Core/NEsper.Core/epl/virtualdw/VirtualDWEventTable.cs new file mode 100755 index 000000000..2c9c7e9c9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/virtualdw/VirtualDWEventTable.cs @@ -0,0 +1,129 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.client.hook; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.@join.table; + +namespace com.espertech.esper.epl.virtualdw +{ + public class VirtualDWEventTable : EventTable + { + private readonly bool _unique; + private readonly IList _hashAccess; + private readonly IList _btreeAccess; + private readonly EventTableOrganization _organization; + + public VirtualDWEventTable( + bool unique, + IList hashAccess, + IList btreeAccess, + EventTableOrganization organization) + { + _unique = unique; + _hashAccess = hashAccess.AsReadOnlyList(); + _btreeAccess = btreeAccess.AsReadOnlyList(); + _organization = organization; + } + + public void AddRemove(EventBean[] newData, EventBean[] oldData) + { + Add(newData); + Remove(oldData); + } + + public void Add(EventBean[] events) + { + } + + public void Remove(EventBean[] events) + { + } + + public void Add(EventBean theEvent) + { + } + + public void Remove(EventBean theEvent) + { + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public IEnumerator GetEnumerator() + { + return Collections.GetEmptyList().GetEnumerator(); + } + + public bool IsEmpty() + { + return true; + } + + public void Clear() + { + } + + public void Destroy() + { + } + + public String ToQueryPlan() + { + return "(external event table)"; + } + + public IList HashAccess + { + get { return _hashAccess; } + } + + public IList BtreeAccess + { + get { return _btreeAccess; } + } + + public bool IsUnique + { + get { return _unique; } + } + + public int? NumberOfEvents + { + get { return null; } + } + + public int NumKeys + { + get { return 0; } + } + + public object Index + { + get { return null; } + } + + public EventTableOrganization Organization + { + get { return _organization; } + } + + public Type ProviderClass + { + get { return typeof (VirtualDWEventTable); } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/virtualdw/VirtualDWView.cs b/NEsper.Core/NEsper.Core/epl/virtualdw/VirtualDWView.cs new file mode 100755 index 000000000..d19c402d5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/virtualdw/VirtualDWView.cs @@ -0,0 +1,71 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.client.hook; +using com.espertech.esper.collection; +using com.espertech.esper.epl.@join.exec.@base; +using com.espertech.esper.epl.@join.plan; +using com.espertech.esper.epl.@join.table; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.epl.named; +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.epl.virtualdw +{ + public interface VirtualDWView : IDisposable + { + VirtualDataWindow VirtualDataWindow { get; } + + Pair GetSubordinateQueryDesc( + Boolean unique, + IList hashedProps, + IList btreeProps); + + SubordTableLookupStrategy GetSubordinateLookupStrategy( + string accessedByStatementName, + int accessedByStatementId, + Attribute[] accessedByStmtAnnotations, + EventType[] outerStreamTypes, + IList hashKeys, + CoercionDesc hashKeyCoercionTypes, + IList rangeKeys, + CoercionDesc rangeKeyCoercionTypes, + bool nwOnTrigger, + EventTable eventTable, + SubordPropPlan joinDesc, + bool forceTableScan); + + EventTable GetJoinIndexTable(QueryPlanIndexItem queryPlanIndexItem); + + JoinExecTableLookupStrategy GetJoinLookupStrategy( + string accessedByStatementName, + int accessedByStatementId, + Attribute[] accessedByStmtAnnotations, + EventTable[] eventTable, + TableLookupKeyDesc keyDescriptor, + int lookupStreamNum); + + Pair GetFireAndForgetDesc( + ISet keysAvailable, + ISet rangesAvailable); + + ICollection GetFireAndForgetData( + EventTable eventTable, + Object[] keyValues, + RangeIndexLookupValue[] rangeValues, + Attribute[] accessedByStmtAnnotations); + + void HandleStartIndex(CreateIndexDesc spec); + void HandleStopIndex(CreateIndexDesc spec); + void HandleStopWindow(); + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/virtualdw/VirtualDWViewFactory.cs b/NEsper.Core/NEsper.Core/epl/virtualdw/VirtualDWViewFactory.cs new file mode 100755 index 000000000..8302999f0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/virtualdw/VirtualDWViewFactory.cs @@ -0,0 +1,18 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +namespace com.espertech.esper.epl.virtualdw +{ + public interface VirtualDWViewFactory + { + ICollection UniqueKeys { get; } + void DestroyNamedWindow(); + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/epl/virtualdw/VirtualDWViewFactoryImpl.cs b/NEsper.Core/NEsper.Core/epl/virtualdw/VirtualDWViewFactoryImpl.cs new file mode 100755 index 000000000..f20b9dfe7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/virtualdw/VirtualDWViewFactoryImpl.cs @@ -0,0 +1,153 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.client.hook; +using com.espertech.esper.compat; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.epl.virtualdw +{ + public class VirtualDWViewFactoryImpl + : ViewFactory + , DataWindowViewFactory + , VirtualDWViewFactory + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly object _customConfiguration; + private ViewFactoryContext _viewFactoryContext; + private IList _viewParameters; + private readonly string _namedWindowName; + private readonly VirtualDataWindowFactory _virtualDataWindowFactory; + private EventType _parentEventType; + private Object[] _viewParameterArr; + private ExprNode[] _viewParameterExp; + private EventBeanFactory _eventBeanFactory; + + public VirtualDWViewFactoryImpl(Type first, string namedWindowName, object customConfiguration) + { + if (!first.IsImplementsInterface(typeof(VirtualDataWindowFactory))) { + throw new ViewProcessingException("Virtual data window factory class " + Name.Of(first) + " does not implement the interface " + Name.Of()); + } + _customConfiguration = customConfiguration; + _namedWindowName = namedWindowName; + _virtualDataWindowFactory = TypeHelper.Instantiate(first); + } + + public ICollection UniqueKeys + { + get { return _virtualDataWindowFactory.UniqueKeyPropertyNames; } + } + + public void SetViewParameters(ViewFactoryContext viewFactoryContext, IList viewParameters) + { + _viewFactoryContext = viewFactoryContext; + _viewParameters = viewParameters; + } + + public void Attach( + EventType parentEventType, + StatementContext statementContext, + ViewFactory optionalParentFactory, + IList parentViewFactories) + { + _parentEventType = parentEventType; + + ExprNode[] validatedNodes = ViewFactorySupport.Validate( + _viewFactoryContext.ViewName, parentEventType, _viewFactoryContext.StatementContext, _viewParameters, true); + _viewParameterArr = new Object[validatedNodes.Length]; + var evaluatorContextStmt = new ExprEvaluatorContextStatement(_viewFactoryContext.StatementContext, false); + for (int i = 0; i < validatedNodes.Length; i++) + { + try + { + _viewParameterArr[i] = ViewFactorySupport.EvaluateAssertNoProperties( + _viewFactoryContext.ViewName, validatedNodes[i], i, evaluatorContextStmt); + } + catch (Exception ex) + { + // expected + } + } + + _viewParameterExp = ViewFactorySupport.Validate( + _viewFactoryContext.ViewName, parentEventType, _viewFactoryContext.StatementContext, _viewParameters, true); + + // initialize + try + { + _eventBeanFactory = EventAdapterServiceHelper.GetFactoryForType( + parentEventType, statementContext.EventAdapterService); + _virtualDataWindowFactory.Initialize( + new VirtualDataWindowFactoryContext( + parentEventType, _viewParameterArr, _viewParameterExp, _eventBeanFactory, _namedWindowName, + _viewFactoryContext, _customConfiguration)); + } + catch (Exception ex) + { + throw new ViewParameterException( + "Validation exception initializing virtual data window '" + _namedWindowName + "': " + ex.Message, ex); + } + } + + public View MakeView(AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext) + { + var outputStream = new VirtualDataWindowOutStreamImpl(); + var context = new VirtualDataWindowContext( + agentInstanceViewFactoryContext.AgentInstanceContext, _parentEventType, _viewParameterArr, + _viewParameterExp, _eventBeanFactory, outputStream, _namedWindowName, _viewFactoryContext, + _customConfiguration); + VirtualDataWindow window; + try + { + window = _virtualDataWindowFactory.Create(context); + } + catch (Exception ex) + { + throw new ViewProcessingException( + "Exception returned by virtual data window factory upon creation: " + ex.Message, ex); + } + var view = new VirtualDWViewImpl(window, _parentEventType, _namedWindowName); + outputStream.SetView(view); + return view; + } + + public EventType EventType + { + get { return _parentEventType; } + } + + public bool CanReuse(View view, AgentInstanceContext agentInstanceContext) + { + return false; + } + + public void DestroyNamedWindow() + { + if (_virtualDataWindowFactory != null) { + _virtualDataWindowFactory.DestroyAllContextPartitions(); + } + } + + public string ViewName + { + get { return "Virtual Data Window"; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/epl/virtualdw/VirtualDWViewImpl.cs b/NEsper.Core/NEsper.Core/epl/virtualdw/VirtualDWViewImpl.cs new file mode 100755 index 000000000..ca1365d21 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/virtualdw/VirtualDWViewImpl.cs @@ -0,0 +1,360 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.client.hook; +using com.espertech.esper.collection; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.@join.exec.@base; +using com.espertech.esper.epl.join.plan; +using com.espertech.esper.epl.join.table; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.epl.named; +using com.espertech.esper.epl.spec; +using com.espertech.esper.filter; +using com.espertech.esper.view; + +namespace com.espertech.esper.epl.virtualdw +{ + public class VirtualDWViewImpl + : ViewSupport + , VirtualDWView + { + private static readonly EventTableOrganization TABLE_ORGANIZATION = new EventTableOrganization( + null, false, false, 0, null, EventTableOrganizationType.VDW); + + private static readonly ILog Log = + LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly VirtualDataWindow _dataExternal; + private readonly EventType _eventType; + private readonly String _namedWindowName; + private String _lastAccessedByStatementName; + private int _lastAccessedByNum; + + public VirtualDWViewImpl(VirtualDataWindow dataExternal, EventType eventType, String namedWindowName) + { + _dataExternal = dataExternal; + _eventType = eventType; + _namedWindowName = namedWindowName; + _lastAccessedByStatementName = null; + } + + public VirtualDataWindow VirtualDataWindow + { + get { return _dataExternal; } + } + + public Pair GetSubordinateQueryDesc( + bool unique, + IList hashedProps, + IList btreeProps) + { + var hashFields = hashedProps + .Select(hashprop => new VirtualDataWindowLookupFieldDesc(hashprop.IndexPropName, VirtualDataWindowLookupOp.EQUALS, hashprop.CoercionType)) + .ToList(); + var btreeFields = btreeProps + .Select(btreeprop => new VirtualDataWindowLookupFieldDesc(btreeprop.IndexPropName, null, btreeprop.CoercionType)) + .ToList(); + var eventTable = new VirtualDWEventTable( + unique, hashFields, btreeFields, TABLE_ORGANIZATION); + var imk = new IndexMultiKey(unique, hashedProps, btreeProps); + return new Pair(imk, eventTable); + } + + public SubordTableLookupStrategy GetSubordinateLookupStrategy( + String accessedByStatementName, + int accessedByStatementId, + Attribute[] accessedByStmtAnnotations, + EventType[] outerStreamTypes, + IList hashKeys, + CoercionDesc hashKeyCoercionTypes, + IList rangeKeys, + CoercionDesc rangeKeyCoercionTypes, + bool nwOnTrigger, + EventTable eventTable, + SubordPropPlan joinDesc, + bool forceTableScan) + { + var noopTable = (VirtualDWEventTable)eventTable; + for (var i = 0; i < noopTable.BtreeAccess.Count; i++) + { + noopTable.BtreeAccess[i].Operator = rangeKeys[i].RangeInfo.RangeType.GetStringOp().FromOpString(); + } + + // allocate a number within the statement + if (_lastAccessedByStatementName == null || !_lastAccessedByStatementName.Equals(accessedByStatementName)) + { + _lastAccessedByNum = 0; + } + _lastAccessedByNum++; + + var context = new VirtualDataWindowLookupContextSPI( + accessedByStatementName, accessedByStatementId, accessedByStmtAnnotations, false, _namedWindowName, + noopTable.HashAccess, noopTable.BtreeAccess, joinDesc, forceTableScan, outerStreamTypes, + accessedByStatementName, _lastAccessedByNum); + var index = _dataExternal.GetLookup(context); + CheckIndex(index); + return new SubordTableLookupStrategyVirtualDW( + _namedWindowName, index, hashKeys, hashKeyCoercionTypes, rangeKeys, rangeKeyCoercionTypes, nwOnTrigger, + outerStreamTypes.Length); + } + + public EventTable GetJoinIndexTable(QueryPlanIndexItem queryPlanIndexItem) + { + + IList hashFields = new List(); + var count = 0; + if (queryPlanIndexItem.IndexProps != null) + { + foreach (var indexProp in queryPlanIndexItem.IndexProps) + { + var coercionType = queryPlanIndexItem.OptIndexCoercionTypes == null + ? null + : queryPlanIndexItem.OptIndexCoercionTypes[count]; + hashFields.Add( + new VirtualDataWindowLookupFieldDesc(indexProp, VirtualDataWindowLookupOp.EQUALS, coercionType)); + count++; + } + } + + IList btreeFields = new List(); + count = 0; + if (queryPlanIndexItem.RangeProps != null) + { + foreach (var btreeprop in queryPlanIndexItem.RangeProps) + { + var coercionType = queryPlanIndexItem.OptRangeCoercionTypes == null + ? null + : queryPlanIndexItem.OptRangeCoercionTypes[count]; + btreeFields.Add(new VirtualDataWindowLookupFieldDesc(btreeprop, null, coercionType)); + count++; + } + } + + return new VirtualDWEventTable(false, hashFields, btreeFields, TABLE_ORGANIZATION); + } + + public JoinExecTableLookupStrategy GetJoinLookupStrategy( + String accessedByStmtName, + int accessedByStmtId, + Attribute[] accessedByStmtAnnotations, + EventTable[] eventTables, + TableLookupKeyDesc keyDescriptor, + int lookupStreamNum) + { + var noopTable = (VirtualDWEventTable)eventTables[0]; + for (var i = 0; i < noopTable.HashAccess.Count; i++) + { + var hashKey = keyDescriptor.Hashes[i]; + noopTable.HashAccess[i].LookupValueType = hashKey.KeyExpr.ExprEvaluator.ReturnType; + } + for (var i = 0; i < noopTable.BtreeAccess.Count; i++) + { + var range = keyDescriptor.Ranges[i]; + var op = range.RangeType.GetStringOp().FromOpString(); + var rangeField = noopTable.BtreeAccess[i]; + rangeField.Operator = op; + if (range is QueryGraphValueEntryRangeRelOp) + { + rangeField.LookupValueType = + ((QueryGraphValueEntryRangeRelOp)range).Expression.ExprEvaluator.ReturnType; + } + else + { + rangeField.LookupValueType = + ((QueryGraphValueEntryRangeIn)range).ExprStart.ExprEvaluator.ReturnType; + } + } + + var index = + _dataExternal.GetLookup( + new VirtualDataWindowLookupContext( + accessedByStmtName, accessedByStmtId, accessedByStmtAnnotations, false, _namedWindowName, + noopTable.HashAccess, noopTable.BtreeAccess)); + CheckIndex(index); + return new JoinExecTableLookupStrategyVirtualDW(_namedWindowName, index, keyDescriptor, lookupStreamNum); + } + + public Pair GetFireAndForgetDesc( + ISet keysAvailable, + ISet rangesAvailable) + { + IList hashFields = new List(); + IList hashIndexedFields = new List(); + foreach (var hashprop in keysAvailable) + { + hashFields.Add(new VirtualDataWindowLookupFieldDesc(hashprop, VirtualDataWindowLookupOp.EQUALS, null)); + hashIndexedFields.Add(new IndexedPropDesc(hashprop, null)); + } + + IList btreeFields = new List(); + IList btreeIndexedFields = new List(); + foreach (var btreeprop in rangesAvailable) + { + btreeFields.Add(new VirtualDataWindowLookupFieldDesc(btreeprop, null, null)); + btreeIndexedFields.Add(new IndexedPropDesc(btreeprop, null)); + } + + var noopTable = new VirtualDWEventTable(false, hashFields, btreeFields, TABLE_ORGANIZATION); + var imk = new IndexMultiKey(false, hashIndexedFields, btreeIndexedFields); + + return new Pair(imk, noopTable); + } + + public ICollection GetFireAndForgetData( + EventTable eventTable, + Object[] keyValues, + RangeIndexLookupValue[] rangeValues, + Attribute[] annotations) + { + var noopTable = (VirtualDWEventTable)eventTable; + for (var i = 0; i < noopTable.BtreeAccess.Count; i++) + { + var range = (RangeIndexLookupValueRange)rangeValues[i]; + var op = range.Operator.GetStringOp().FromOpString(); + noopTable.BtreeAccess[i].Operator = op; + } + + var keys = new Object[keyValues.Length + rangeValues.Length]; + for (var i = 0; i < keyValues.Length; i++) + { + keys[i] = keyValues[i]; + noopTable.HashAccess[i].LookupValueType = keyValues[i] == null ? null : keyValues[i].GetType(); + } + var offset = keyValues.Length; + for (var j = 0; j < rangeValues.Length; j++) + { + var rangeValue = rangeValues[j].Value; + if (rangeValue is Range) + { + var range = (Range)rangeValue; + keys[j + offset] = new VirtualDataWindowKeyRange(range.LowEndpoint, range.HighEndpoint); + noopTable.BtreeAccess[j].LookupValueType = range.LowEndpoint == null + ? null + : range.LowEndpoint.GetType(); + } + else + { + keys[j + offset] = rangeValue; + noopTable.BtreeAccess[j].LookupValueType = rangeValue == null ? null : rangeValue.GetType(); + } + } + + var index = + _dataExternal.GetLookup( + new VirtualDataWindowLookupContext( + null, -1, annotations, true, _namedWindowName, noopTable.HashAccess, noopTable.BtreeAccess)); + CheckIndex(index); + if (index == null) + { + throw new EPException("Exception obtaining index from virtual data window '" + _namedWindowName + "'"); + } + + ISet events = null; + try + { + events = index.Lookup(keys, null); + } + catch (Exception ex) + { + Log.Warn( + "Exception encountered invoking virtual data window external index for window '" + _namedWindowName + + "': " + ex.Message, ex); + } + return events; + } + + private void CheckIndex(VirtualDataWindowLookup index) + { + if (index == null) + { + throw new EPException( + "Exception obtaining index lookup from virtual data window, the implementation has returned a null index"); + } + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + _dataExternal.Update(newData, oldData); + } + + public override EventType EventType + { + get { return _eventType; } + } + + public void Dispose() + { + _dataExternal.Dispose(); + } + + public override IEnumerator GetEnumerator() + { + return _dataExternal.GetEnumerator(); + } + + public void HandleStartIndex(CreateIndexDesc spec) + { + try + { + var fields = spec.Columns + .Select(col => new VirtualDataWindowEventStartIndex.VDWCreateIndexField(col.Name, col.Type == CreateIndexType.HASH)) + .ToList(); + var create = new VirtualDataWindowEventStartIndex( + spec.WindowName, spec.IndexName, fields, spec.IsUnique); + _dataExternal.HandleEvent(create); + } + catch (Exception ex) + { + var message = + "Exception encountered invoking virtual data window handle start-index event for window '" + + _namedWindowName + "': " + ex.Message; + Log.Warn(message, ex); + throw new EPException(message, ex); + } + } + + public void HandleStopIndex(CreateIndexDesc spec) + { + try + { + var theEvent = new VirtualDataWindowEventStopIndex( + spec.WindowName, spec.IndexName); + _dataExternal.HandleEvent(theEvent); + } + catch (Exception ex) + { + var message = + "Exception encountered invoking virtual data window handle stop-index event for window '" + + _namedWindowName + "': " + ex.Message; + Log.Warn(message, ex); + } + } + + public void HandleStopWindow() + { + try + { + var theEvent = new VirtualDataWindowEventStopWindow(_namedWindowName); + _dataExternal.HandleEvent(theEvent); + } + catch (Exception ex) + { + var message = + "Exception encountered invoking virtual data window handle stop-window event for window '" + + _namedWindowName + "': " + ex.Message; + Log.Warn(message, ex); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/virtualdw/VirtualDWViewProviderForAgentInstance.cs b/NEsper.Core/NEsper.Core/epl/virtualdw/VirtualDWViewProviderForAgentInstance.cs new file mode 100755 index 000000000..d5f0931b8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/virtualdw/VirtualDWViewProviderForAgentInstance.cs @@ -0,0 +1,14 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.context.util; + +namespace com.espertech.esper.epl.virtualdw +{ + public delegate VirtualDWView VirtualDWViewProviderForAgentInstance(AgentInstanceContext agentInstanceContext); +} diff --git a/NEsper.Core/NEsper.Core/epl/virtualdw/VirtualDataWindowLookupContextSPI.cs b/NEsper.Core/NEsper.Core/epl/virtualdw/VirtualDataWindowLookupContextSPI.cs new file mode 100755 index 000000000..f7e045849 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/virtualdw/VirtualDataWindowLookupContextSPI.cs @@ -0,0 +1,52 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.client.hook; +using com.espertech.esper.epl.lookup; + +namespace com.espertech.esper.epl.virtualdw +{ + public class VirtualDataWindowLookupContextSPI : VirtualDataWindowLookupContext + { + public VirtualDataWindowLookupContextSPI( + string statementName, + int statementId, + Attribute[] statementAnnotations, + bool fireAndForget, + String namedWindowName, + IList hashFields, + IList btreeFields, + SubordPropPlan joinDesc, + bool forceTableScan, + EventType[] outerTypePerStream, + string accessedByStatementName, + int accessedByStatementSequenceNum) + : base(statementName,statementId,statementAnnotations,fireAndForget,namedWindowName,hashFields,btreeFields) + { + JoinDesc = joinDesc; + IsForceTableScan = forceTableScan; + OuterTypePerStream = outerTypePerStream; + AccessedByStatementName = accessedByStatementName; + AccessedByStatementSequenceNum = accessedByStatementSequenceNum; + } + + public SubordPropPlan JoinDesc { get; private set; } + + public bool IsForceTableScan { get; private set; } + + public EventType[] OuterTypePerStream { get; private set; } + + public string AccessedByStatementName { get; private set; } + + public int AccessedByStatementSequenceNum { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/epl/virtualdw/VirtualDataWindowOutStreamImpl.cs b/NEsper.Core/NEsper.Core/epl/virtualdw/VirtualDataWindowOutStreamImpl.cs new file mode 100755 index 000000000..a588b4c70 --- /dev/null +++ b/NEsper.Core/NEsper.Core/epl/virtualdw/VirtualDataWindowOutStreamImpl.cs @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.client.hook; +using com.espertech.esper.view; + +namespace com.espertech.esper.epl.virtualdw +{ + public class VirtualDataWindowOutStreamImpl + : VirtualDataWindowOutStream + { + private ViewSupport _view; + + public void SetView(ViewSupport view) { + _view = view; + } + + public void Update(EventBean[] newData, EventBean[] oldData) { + _view.UpdateChildren(newData, oldData); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/AvroBackedBean.cs b/NEsper.Core/NEsper.Core/events/AvroBackedBean.cs new file mode 100755 index 000000000..b43a822ec --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/AvroBackedBean.cs @@ -0,0 +1,17 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; + +namespace com.espertech.esper.events +{ + public interface AvroBackedBean : EventBean + { + object GenericRecordDotData { get; } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/events/BaseConfigurableEventType.cs b/NEsper.Core/NEsper.Core/events/BaseConfigurableEventType.cs new file mode 100755 index 000000000..f3f814ed9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/BaseConfigurableEventType.cs @@ -0,0 +1,275 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.compat.threading; + +namespace com.espertech.esper.events +{ + /// + /// EventType than can be supplied with a preconfigured list of properties getters + /// (aka. explicit properties). + /// + public abstract class BaseConfigurableEventType : EventTypeSPI + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly EventAdapterService _eventAdapterService; + private readonly int _eventTypeId; + private readonly EventTypeMetadata _metadata; + private readonly Type _underlyngType; + private EventPropertyDescriptor[] _propertyDescriptors; + private String[] _propertyNames; + private IDictionary> _propertyFragmentTypes; + private readonly ILockable _iLock; + + /// + /// Getters for each known property. + /// + protected IDictionary PropertyGetters; + + /// + /// Descriptors for each known property. + /// + protected IDictionary PropertyDescriptorMap; + + /// + /// Ctor. + /// + /// for dynamic event type creation + /// event type metadata + /// The event type id. + /// is the underlying type returned by the event type + protected BaseConfigurableEventType(EventAdapterService eventAdapterService, EventTypeMetadata metadata, int eventTypeId, Type underlyngType) + { + _iLock = LockManager.CreateLock(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + _eventTypeId = eventTypeId; + _eventAdapterService = eventAdapterService; + _metadata = metadata; + _underlyngType = underlyngType; + } + + /// + /// Subclasses must implement this and supply a getter to a given property. + /// + /// is the property expression + /// + /// getter for property + /// + protected abstract EventPropertyGetter DoResolvePropertyGetter(String property); + + /// + /// Subclasses must implement this and return a type for a property. + /// + /// is the property expression + /// + /// property type + /// + protected abstract Type DoResolvePropertyType(String property); + + /// + /// Subclasses must implement this and return a fragment type for a property. + /// + /// is the property expression + /// + /// fragment property type + /// + protected abstract FragmentEventType DoResolveFragmentType(String property); + + public int EventTypeId + { + get { return _eventTypeId; } + } + + public string Name + { + get { return _metadata.PrimaryName; } + } + + /// + /// Returns the event adapter service. + /// + /// + /// event adapter service + /// + public EventAdapterService EventAdapterService + { + get { return _eventAdapterService; } + } + + /// + /// Sets explicit properties using a map of event property name and getter instance + /// for each property. + /// + /// property descriptors for explicit properties + protected void Initialize(ICollection explicitProperties) + { + PropertyGetters = new Dictionary(); + _propertyDescriptors = new EventPropertyDescriptor[explicitProperties.Count]; + _propertyNames = new String[explicitProperties.Count]; + PropertyDescriptorMap = new Dictionary(); + _propertyFragmentTypes = new Dictionary>(); + + int count = 0; + foreach (ExplicitPropertyDescriptor propertyDescriptor in explicitProperties) + { + _propertyNames[count] = propertyDescriptor.Descriptor.PropertyName; + PropertyGetters.Put(propertyDescriptor.Descriptor.PropertyName, propertyDescriptor.Getter); + EventPropertyDescriptor desc = propertyDescriptor.Descriptor; + _propertyDescriptors[count] = desc; + PropertyDescriptorMap.Put(desc.PropertyName, desc); + + if (propertyDescriptor.OptionalFragmentTypeName != null) + { + _propertyFragmentTypes.Put(propertyDescriptor.Descriptor.PropertyName, + new Pair( + propertyDescriptor, null)); + } + + if (!desc.IsFragment) + { + _propertyFragmentTypes.Put(propertyDescriptor.Descriptor.PropertyName, null); + } + + count++; + } + } + + public Type GetPropertyType(String propertyExpression) + { + EventPropertyDescriptor desc = PropertyDescriptorMap.Get(propertyExpression); + if (desc != null) + { + return desc.PropertyType; + } + + return DoResolvePropertyType(propertyExpression); + } + + public Type UnderlyingType + { + get { return _underlyngType; } + } + + public EventPropertyGetter GetGetter(String propertyExpression) + { + EventPropertyGetter getter = PropertyGetters.Get(propertyExpression); + if (getter != null) + { + return getter; + } + + return DoResolvePropertyGetter(propertyExpression); + } + + public EventPropertyGetterMapped GetGetterMapped(String mappedProperty) + { + EventPropertyGetter getter = GetGetter(mappedProperty); + if (getter is EventPropertyGetterMapped) + { + return (EventPropertyGetterMapped) getter; + } + + return null; + } + + public EventPropertyGetterIndexed GetGetterIndexed(string indexedProperty) + { + return null; + } + + public FragmentEventType GetFragmentType(String property) + { + using (_iLock.Acquire()) + { + Pair pair = _propertyFragmentTypes.Get(property); + if (pair == null) + { + return _propertyFragmentTypes.ContainsKey(property) ? null : DoResolveFragmentType(property); + } + + // if a type is assigned, use that + if (pair.Second != null) + { + return pair.Second; + } + + // resolve event type + EventType existingType = _eventAdapterService.GetEventTypeByName(pair.First.OptionalFragmentTypeName); + if (!(existingType is BaseConfigurableEventType)) + { + Log.Warn("Type configured for fragment event property '" + property + "' by name '" + pair.First + + "' could not be found"); + return null; + } + + var fragmentType = new FragmentEventType(existingType, pair.First.IsFragmentArray, false); + pair.Second = fragmentType; + return fragmentType; + } + } + + public string[] PropertyNames + { + get { return _propertyNames; } + } + + public bool IsProperty(String property) + { + return (GetGetter(property) != null); + } + + public IList PropertyDescriptors + { + get { return _propertyDescriptors; } + } + + public EventTypeMetadata Metadata + { + get { return _metadata; } + } + + public EventPropertyDescriptor GetPropertyDescriptor(String propertyName) + { + return PropertyDescriptorMap.Get(propertyName); + } + + /// + /// Returns an enumeration of event types that are super to this event type, from which this event type inherited event properties. + /// + /// For PONO instances underlying the event this method returns the event types for all superclasses extended by + /// the PONO and all interfaces implemented by the PONO. + /// + /// The super types. + /// an array of event types + public abstract EventType[] SuperTypes { get; } + + /// + /// Returns enumerator over all super types to event type, going up the hierarchy and including all interfaces (and their + /// extended interfaces) and superclasses as EventType instances. + /// + /// The deep super types. + public abstract EventType[] DeepSuperTypes { get; } + + public abstract EventPropertyWriter GetWriter(string propertyName); + public abstract EventPropertyDescriptor[] WriteableProperties { get; } + public abstract EventPropertyDescriptor GetWritableProperty(string propertyName); + public abstract EventBeanCopyMethod GetCopyMethod(string[] properties); + public abstract EventBeanWriter GetWriter(string[] properties); + public abstract EventBeanReader Reader { get; } + public abstract string StartTimestampPropertyName { get; } + public abstract string EndTimestampPropertyName { get; } + public abstract bool EqualsCompareType(EventType eventType); + } +} diff --git a/NEsper.Core/NEsper.Core/events/BaseNestableEventType.cs b/NEsper.Core/NEsper.Core/events/BaseNestableEventType.cs new file mode 100755 index 000000000..9159dbcd5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/BaseNestableEventType.cs @@ -0,0 +1,571 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.parse; +using com.espertech.esper.events.property; +using com.espertech.esper.util; + +namespace com.espertech.esper.events +{ + /// + /// Implementation of the interface for handling name value pairs. + /// + public abstract class BaseNestableEventType : EventTypeSPI + { + private readonly EventTypeMetadata _metadata; + private readonly String _typeName; + private readonly EventAdapterService _eventAdapterService; + private readonly EventType[] _optionalSuperTypes; + private readonly ICollection _optionalDeepSupertypes; + private readonly int _eventTypeId; + + internal readonly EventTypeNestableGetterFactory GetterFactory; + + // Simple (not-nested) properties are stored here + private String[] _propertyNames; // Cache an array of property names so not to construct one frequently + private EventPropertyDescriptor[] _propertyDescriptors; + + private readonly IDictionary _propertyItems; + + private IDictionary _propertyGetterCache; + // Mapping of all property names and getters + + // Nestable definition of Map contents is here + internal IDictionary NestableTypes; + // Deep definition of the map-type, containing nested maps and objects + + private readonly String _startTimestampPropertyName; + private readonly String _endTimestampPropertyName; + + protected abstract void PostUpdateNestableTypes(); + + public abstract Type UnderlyingType { get; } + public abstract EventPropertyDescriptor[] WriteableProperties { get; } + public abstract EventPropertyWriter GetWriter(string propertyName); + public abstract EventBeanWriter GetWriter(string[] properties); + public abstract EventPropertyDescriptor GetWritableProperty(string propertyName); + public abstract EventBeanCopyMethod GetCopyMethod(string[] properties); + public abstract EventBeanReader Reader { get; } + + public EventAdapterService EventAdapterService + { + get { return _eventAdapterService; } + } + + public IDictionary PropertyItems + { + get { return _propertyItems; } + } + + /// + /// Constructor takes a type name, map of property names and types, for use with nestable Map events. + /// + /// event type metadata + /// is the event type name used to distinquish map types that have the same property types,empty string for anonymous maps, or for insert-into statements generating map events the stream name + /// The event type id. + /// is required for access to objects properties within map values + /// is pairs of property name and type + /// the supertypes to this type if any, or null if there are no supertypes + /// the deep supertypes to this type if any, or null if there are no deep supertypes + /// The type config. + /// The getter factory. + protected BaseNestableEventType( + EventTypeMetadata metadata, + String typeName, + int eventTypeId, + EventAdapterService eventAdapterService, + IDictionary propertyTypes, + EventType[] optionalSuperTypes, + ICollection optionalDeepSupertypes, + ConfigurationEventTypeWithSupertype typeConfig, + EventTypeNestableGetterFactory getterFactory) + { + _metadata = metadata; + _eventTypeId = eventTypeId; + _typeName = typeName; + _eventAdapterService = eventAdapterService; + GetterFactory = getterFactory; + + _optionalSuperTypes = optionalSuperTypes; + _optionalDeepSupertypes = optionalDeepSupertypes ?? Collections.GetEmptySet(); + + // determine property set and prepare getters + var propertySet = EventTypeUtility.GetNestableProperties( + propertyTypes, eventAdapterService, getterFactory, optionalSuperTypes); + + NestableTypes = propertySet.NestableTypes; + _propertyNames = propertySet.PropertyNameArray; + _propertyItems = propertySet.PropertyItems; + _propertyDescriptors = propertySet.PropertyDescriptors.ToArray(); + + var desc = EventTypeUtility.ValidatedDetermineTimestampProps( + this, typeConfig == null ? null : typeConfig.StartTimestampPropertyName, + typeConfig == null ? null : typeConfig.EndTimestampPropertyName, optionalSuperTypes); + _startTimestampPropertyName = desc.Start; + _endTimestampPropertyName = desc.End; + } + + public string Name + { + get { return _typeName; } + } + + public int EventTypeId + { + get { return _eventTypeId; } + } + + public string StartTimestampPropertyName + { + get { return _startTimestampPropertyName; } + } + + public string EndTimestampPropertyName + { + get { return _endTimestampPropertyName; } + } + + public Type GetPropertyType(String propertyName) + { + return EventTypeUtility.GetNestablePropertyType( + propertyName, _propertyItems, NestableTypes, _eventAdapterService); + } + + public EventPropertyGetter GetGetter(String propertyName) + { + if (_propertyGetterCache == null) + { + _propertyGetterCache = new Dictionary(); + } + return EventTypeUtility.GetNestableGetter( + propertyName, _propertyItems, _propertyGetterCache, NestableTypes, _eventAdapterService, GetterFactory, + _metadata.OptionalApplicationType == ApplicationType.OBJECTARR); + } + + public EventPropertyGetterMapped GetGetterMapped(String mappedPropertyName) + { + var item = _propertyItems.Get(mappedPropertyName); + if (item == null || !item.PropertyDescriptor.IsMapped) + { + return null; + } + var mappedProperty = new MappedProperty(mappedPropertyName); + return GetterFactory.GetPropertyProvidedGetterMap( + NestableTypes, mappedPropertyName, mappedProperty, _eventAdapterService); + } + + public EventPropertyGetterIndexed GetGetterIndexed(String indexedPropertyName) + { + var item = _propertyItems.Get(indexedPropertyName); + if (item == null || !item.PropertyDescriptor.IsIndexed) + { + return null; + } + var indexedProperty = new IndexedProperty(indexedPropertyName); + return GetterFactory.GetPropertyProvidedGetterIndexed( + NestableTypes, indexedPropertyName, indexedProperty, _eventAdapterService); + } + + public string[] PropertyNames + { + get { return _propertyNames; } + } + + public bool IsProperty(String propertyName) + { + var propertyType = GetPropertyType(propertyName); + if (propertyType == null) + { + // Could be a native null type, such as "insert into A select null as field..." + if (_propertyItems.ContainsKey(ASTUtil.UnescapeDot(propertyName))) + { + return true; + } + } + return propertyType != null; + } + + public EventType[] SuperTypes + { + get { return _optionalSuperTypes; } + } + + public EventType[] DeepSuperTypes + { + get { return _optionalDeepSupertypes.ToArray(); } + } + + /// Returns the name-type map of map properties, each value in the map + /// is the property name and types + public IDictionary Types + { + get { return NestableTypes; } + } + + /// + /// Adds additional properties that do not yet exist on the given type. Ignores properties already present. Allows nesting. + /// + /// properties to add + /// for resolving further map event types that are property types + public void AddAdditionalProperties(IDictionary typeMap, EventAdapterService eventAdapterService) + { + // merge type graphs + NestableTypes = GraphUtil.MergeNestableMap(NestableTypes, typeMap); + + PostUpdateNestableTypes(); + + // construct getters and types for each property (new or old) + var propertySet = EventTypeUtility.GetNestableProperties( + typeMap, eventAdapterService, GetterFactory, _optionalSuperTypes); + + // add each new descriptor + var newPropertyDescriptors = new List(); + foreach (var propertyDesc in propertySet.PropertyDescriptors) + { + if (_propertyItems.ContainsKey(propertyDesc.PropertyName)) // not a new property + { + continue; + } + newPropertyDescriptors.Add(propertyDesc); + } + + // add each that is not already present + var newPropertyNames = new List(); + foreach (var propertyName in propertySet.PropertyNameList) + { + if (_propertyItems.ContainsKey(propertyName)) // not a new property + { + continue; + } + newPropertyNames.Add(propertyName); + _propertyItems.Put(propertyName, propertySet.PropertyItems.Get(propertyName)); + } + + // expand property name array + var allPropertyNames = new String[_propertyNames.Length + newPropertyNames.Count]; + Array.Copy(_propertyNames, 0, allPropertyNames, 0, _propertyNames.Length); + var count = _propertyNames.Length; + foreach (var newProperty in newPropertyNames) + { + allPropertyNames[count++] = newProperty; + } + _propertyNames = allPropertyNames; + + // expand descriptor array + var allPropertyDescriptors = + new EventPropertyDescriptor[_propertyDescriptors.Length + newPropertyNames.Count]; + Array.Copy(_propertyDescriptors, 0, allPropertyDescriptors, 0, _propertyDescriptors.Length); + count = _propertyDescriptors.Length; + foreach (var desc in newPropertyDescriptors) + { + allPropertyDescriptors[count++] = desc; + } + _propertyDescriptors = allPropertyDescriptors; + } + + public IList PropertyDescriptors + { + get { return _propertyDescriptors; } + } + + /// Compares two sets of properties and determines if they are the same, allowing for boxed/unboxed types, and nested map types. + /// is the first set of properties + /// is the second set of properties + /// name of the type compared to + /// null if the property set is equivalent or message if not + public static String IsDeepEqualsProperties( + String otherName, + IDictionary setOne, + IDictionary setTwo) + { + // Should have the same number of properties + if (setOne.Count != setTwo.Count) + { + return "Type by name '" + otherName + "' expects " + setOne.Count + " properties but receives " + + setTwo.Count + " properties"; + } + + // Compare property by property + foreach (var entry in setOne) + { + var propName = entry.Key; + var setTwoType = setTwo.Get(entry.Key); + var setTwoTypeFound = setTwo.ContainsKey(entry.Key); + var setOneType = entry.Value; + + var message = BaseNestableEventUtil.ComparePropType( + propName, setOneType, setTwoType, setTwoTypeFound, otherName); + if (message != null) + { + return message; + } + } + + return null; + } + + public EventPropertyDescriptor GetPropertyDescriptor(String propertyName) + { + var item = _propertyItems.Get(propertyName); + if (item == null) + { + return null; + } + return item.PropertyDescriptor; + } + + public EventTypeMetadata Metadata + { + get { return _metadata; } + } + + public FragmentEventType GetFragmentType(String propertyName) + { + var item = _propertyItems.Get(propertyName); + if (item != null) // may contain null values + { + return item.FragmentEventType; + } + + // see if this is a nested property + var index = ASTUtil.UnescapedIndexOfDot(propertyName); + if (index == -1) + { + // dynamic simple property + if (propertyName.EndsWith("?")) + { + return null; + } + + // parse, can be an indexed property + var property = PropertyParser.ParseAndWalkLaxToSimple(propertyName); + if (property is IndexedProperty) + { + var indexedProp = (IndexedProperty)property; + var type = NestableTypes.Get(indexedProp.PropertyNameAtomic); + if (type == null) + { + return null; + } + else if (type is EventType[]) + { + var eventType = ((EventType[])type)[0]; + return new FragmentEventType(eventType, false, false); + } + else if (type is String) + { + var propTypeName = type.ToString(); + var isArray = EventTypeUtility.IsPropertyArray(propTypeName); + if (!isArray) + { + return null; + } + propTypeName = EventTypeUtility.GetPropertyRemoveArray(propTypeName); + EventType innerType = _eventAdapterService.GetEventTypeByName(propTypeName); + if (!(innerType is BaseNestableEventType)) + { + return null; + } + return new FragmentEventType(innerType, false, false); // false since an index is present + } + if (!(type is Type)) + { + return null; + } + if (!((Type)type).IsArray) + { + return null; + } + // its an array + return EventBeanUtility.CreateNativeFragmentType(((Type)type).GetElementType(), null, _eventAdapterService); + } + else if (property is MappedProperty) + { + // No type information available for the inner event + return null; + } + else + { + return null; + } + } + + // Map event types allow 2 types of properties inside: + // - a property that is a object is interrogated via bean property getters and BeanEventType + // - a property that is a Map itself is interrogated via map property getters + // The property getters therefore act on + + // Take apart the nested property into a map key and a nested value class property name + var propertyMap = ASTUtil.UnescapeDot(propertyName.Substring(0, index)); + var propertyNested = propertyName.Substring(index + 1); + + // If the property is dynamic, it cannot be a fragment + if (propertyMap.EndsWith("?")) + { + return null; + } + + var nestedType = NestableTypes.Get(propertyMap); + if (nestedType == null) + { + // parse, can be an indexed property + var property = PropertyParser.ParseAndWalkLaxToSimple(propertyMap); + if (property is IndexedProperty) + { + var indexedProp = (IndexedProperty)property; + var type = NestableTypes.Get(indexedProp.PropertyNameAtomic); + if (type == null) + { + return null; + } + // handle map-in-map case + if (type is String) + { + var propTypeName = type.ToString(); + var isArray = EventTypeUtility.IsPropertyArray(propTypeName); + if (isArray) + { + propTypeName = EventTypeUtility.GetPropertyRemoveArray(propTypeName); + } + EventType innerType = _eventAdapterService.GetEventTypeByName(propTypeName); + if (!(innerType is BaseNestableEventType)) + { + return null; + } + return innerType.GetFragmentType(propertyNested); + } + // handle eventtype[] in map + else if (type is EventType[]) + { + var innerType = ((EventType[])type)[0]; + return innerType.GetFragmentType(propertyNested); + } + // handle array class in map case + else + { + if (!(type is Type)) + { + return null; + } + if (!((Type)type).IsArray) + { + return null; + } + var fragmentParent = EventBeanUtility.CreateNativeFragmentType( + (Type)type, null, _eventAdapterService); + if (fragmentParent == null) + { + return null; + } + return fragmentParent.FragmentType.GetFragmentType(propertyNested); + } + } + else if (property is MappedProperty) + { + // No type information available for the property's map value + return null; + } + else + { + return null; + } + } + + // If there is a map value in the map, return the Object value if this is a dynamic property + if (ReferenceEquals(nestedType, typeof(IDictionary))) + { + return null; + } + else if (nestedType is IDictionary) + { + return null; + } + else if (nestedType is Type) + { + var simpleClass = (Type)nestedType; + if (!TypeHelper.IsFragmentableType(simpleClass)) + { + return null; + } + EventType nestedEventType = + _eventAdapterService.BeanEventTypeFactory.CreateBeanTypeDefaultName(simpleClass); + return nestedEventType.GetFragmentType(propertyNested); + } + else if (nestedType is EventType) + { + var innerType = (EventType)nestedType; + return innerType.GetFragmentType(propertyNested); + } + else if (nestedType is EventType[]) + { + var innerType = (EventType[])nestedType; + return innerType[0].GetFragmentType(propertyNested); + } + else if (nestedType is String) + { + var nestedName = nestedType.ToString(); + var isArray = EventTypeUtility.IsPropertyArray(nestedName); + if (isArray) + { + nestedName = EventTypeUtility.GetPropertyRemoveArray(nestedName); + } + var innerType = _eventAdapterService.GetEventTypeByName(nestedName); + if (!(innerType is BaseNestableEventType)) + { + return null; + } + return innerType.GetFragmentType(propertyNested); + } + else + { + var message = "Nestable map type configuration encountered an unexpected value type of '" + + nestedType.GetType() + " for property '" + propertyName + + "', expected Class, typeof(Map) or IDictionary as value type"; + throw new PropertyAccessException(message); + } + } + + /// Returns a message if the type, compared to this type, is not compatible in regards to the property numbers and types. + /// to compare to + /// message + public String GetEqualsMessage(EventType otherType) + { + if (!(otherType is BaseNestableEventType)) + { + return string.Format("Type by name '{0}' is not a compatible type (target type underlying is '{1}')", otherType.Name, compat.Name.Of(otherType.UnderlyingType)); + } + + var other = (BaseNestableEventType)otherType; + + if ((_metadata.TypeClass != TypeClass.ANONYMOUS) && (!other._typeName.Equals(_typeName))) + { + return "Type by name '" + otherType.Name + "' is not the same name"; + } + + return IsDeepEqualsProperties(otherType.Name, other.NestableTypes, NestableTypes); + } + + public bool EqualsCompareType(EventType otherEventType) + { + if (Equals(this, otherEventType)) + { + return true; + } + + var message = GetEqualsMessage(otherEventType); + return message == null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/BaseNestableEventUtil.cs b/NEsper.Core/NEsper.Core/events/BaseNestableEventUtil.cs new file mode 100755 index 000000000..c0943dd4a --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/BaseNestableEventUtil.cs @@ -0,0 +1,723 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.magic; +using com.espertech.esper.events.arr; +using com.espertech.esper.events.bean; +using com.espertech.esper.events.map; +using com.espertech.esper.events.property; +using com.espertech.esper.util; + +namespace com.espertech.esper.events +{ + using DataMap = IDictionary; + + public class BaseNestableEventUtil + { + public static IDictionary CheckedCastUnderlyingMap(EventBean theEvent) + { +#if CHECKED_CAST + if (!(theEvent.Underlying is IDictionary)) + { + System.Diagnostics.Debug.Assert(true, "invalid dictionary"); + throw new InvalidCastException("checked cast to IDictionary failed"); + } +#endif + + return (IDictionary)theEvent.Underlying; + } + + public static Object[] CheckedCastUnderlyingObjectArray(EventBean theEvent) + { + return (Object[])theEvent.Underlying; + } + + public static Object HandleNestedValueArrayWithMap(Object value, int index, MapEventPropertyGetter getter) + { + var asArray = value as Array; + if (asArray == null) + { + return null; + } + + if (asArray.Length <= index) + { + return null; + } + + var valueMap = asArray.GetValue(index); + if (!(valueMap is DataMap)) + { + if (valueMap is EventBean) + { + return getter.Get((EventBean)valueMap); + } + return null; + } + return getter.GetMap((IDictionary)valueMap); + } + + public static bool HandleNestedValueArrayWithMapExists(Object value, int index, MapEventPropertyGetter getter) + { + var asArray = value as Array; + if (asArray == null) + { + return false; + } + + if (asArray.Length <= index) + { + return false; + } + + var valueMap = asArray.GetValue(index); + if (!(valueMap is DataMap)) + { + if (valueMap is EventBean) + { + return getter.IsExistsProperty((EventBean) valueMap); + } + return false; + } + return getter.IsMapExistsProperty((DataMap) valueMap); + } + + public static Object HandleNestedValueArrayWithMapFragment(Object value, int index, MapEventPropertyGetter getter, EventAdapterService eventAdapterService, EventType fragmentType) + { + var asArray = value as Array; + if (asArray == null) + { + return null; + } + + if (asArray.Length <= index) + { + return null; + } + + var valueMap = asArray.GetValue(index); + if (!(valueMap is DataMap)) + { + if (valueMap is EventBean) + { + return getter.GetFragment((EventBean)valueMap); + } + return null; + } + + // If the map does not contain the key, this is allowed and represented as null + var eventBean = eventAdapterService.AdapterForTypedMap((IDictionary)valueMap, fragmentType); + return getter.GetFragment(eventBean); + } + + public static Object HandleNestedValueArrayWithObjectArray(Object value, int index, ObjectArrayEventPropertyGetter getter) + { + var asArray = value as Array; + if (asArray == null) + { + return null; + } + + if (asArray.Length <= index) + { + return null; + } + + var valueArray = asArray.GetValue(index); + if (!(valueArray is Object[])) + { + if (valueArray is EventBean) + { + return getter.Get((EventBean)valueArray); + } + return null; + } + return getter.GetObjectArray((Object[])valueArray); + } + + public static bool HandleNestedValueArrayWithObjectArrayExists(Object value, int index, ObjectArrayEventPropertyGetter getter) + { + var asArray = value as Array; + if (asArray == null) + { + return false; + } + + if (asArray.Length <= index) + { + return false; + } + + var valueArray = asArray.GetValue(index); + if (!(valueArray is Object[])) + { + if (valueArray is EventBean) + { + return getter.IsExistsProperty((EventBean) valueArray); + } + return false; + } + return getter.IsObjectArrayExistsProperty((Object[]) valueArray); + } + + public static Object HandleNestedValueArrayWithObjectArrayFragment(Object value, int index, ObjectArrayEventPropertyGetter getter, EventType fragmentType, EventAdapterService eventAdapterService) + { + var asArray = value as Array; + if (asArray == null) + { + return null; + } + + if (asArray.Length <= index) + { + return null; + } + + var valueArray = asArray.GetValue(index); + if (!(valueArray is Object[])) + { + if (valueArray is EventBean) + { + return getter.GetFragment((EventBean)valueArray); + } + return null; + } + + // If the map does not contain the key, this is allowed and represented as null + var eventBean = eventAdapterService.AdapterForTypedObjectArray((Object[])valueArray, fragmentType); + return getter.GetFragment(eventBean); + } + + public static Object HandleCreateFragmentMap(Object value, EventType fragmentEventType, EventAdapterService eventAdapterService) + { + if (!(value is DataMap)) + { + if (value is EventBean) + { + return value; + } + return null; + } + var subEvent = (DataMap)value; + return eventAdapterService.AdapterForTypedMap(subEvent, fragmentEventType); + } + + public static Object HandleCreateFragmentObjectArray(Object value, EventType fragmentEventType, EventAdapterService eventAdapterService) + { + if (!(value is Object[])) + { + if (value is EventBean) + { + return value; + } + return null; + } + var subEvent = (Object[])value; + return eventAdapterService.AdapterForTypedObjectArray(subEvent, fragmentEventType); + } + + public static Object GetMappedPropertyValue(Object value, String key) + { + if (value == null) + { + return null; + } + if (value is DataMap) + { + return ((DataMap)value).Get(key); + } + if (value.GetType().IsGenericStringDictionary()) + { + return MagicMarker.GetStringDictionary(value).Get(key); + } + + return null; + } + + public static bool GetMappedPropertyExists(Object value, String key) + { + if (value == null) + { + return false; + } + if (value is DataMap) + { + return ((DataMap)value).ContainsKey(key); + } + if (value.GetType().IsGenericStringDictionary()) + { + return MagicMarker.GetStringDictionary(value).ContainsKey(key); + } + + return false; + } + + public static MapIndexedPropPair GetIndexedAndMappedProps(String[] properties) + { + ICollection mapPropertiesToCopy = new HashSet(); + ICollection arrayPropertiesToCopy = new HashSet(); + for (var i = 0; i < properties.Length; i++) + { + var prop = PropertyParser.ParseAndWalkLaxToSimple(properties[i]); + if (prop is MappedProperty) + { + var mappedProperty = (MappedProperty)prop; + mapPropertiesToCopy.Add(mappedProperty.PropertyNameAtomic); + } + if (prop is IndexedProperty) + { + var indexedProperty = (IndexedProperty)prop; + arrayPropertiesToCopy.Add(indexedProperty.PropertyNameAtomic); + } + } + return new MapIndexedPropPair(mapPropertiesToCopy, arrayPropertiesToCopy); + } + + public static Object GetIndexedValue(Object value, int index) + { + if (value == null) + { + return null; + } + + var asArray = value as Array; + if (asArray != null) + { + if (asArray.Length <= index) + { + return null; + } + + return asArray.GetValue(index); + } + + var asString = value as string; + if (asString != null) + { + if (asString.Length <= index) + { + return null; + } + + return asString[index]; + } + + var asType = value.GetType(); + var asGenericList = asType.FindGenericInterface(typeof(IList<>)); + if (asGenericList != null) + { + var asList = MagicMarker.GetListFactory(value.GetType()).Invoke(value); + if (asList.Count <= index) + { + return null; + } + + return asList[index]; + } + + return null; + } + + public static bool IsExistsIndexedValue(Object value, int index) + { + if (value == null) + { + return false; + } + + var asArray = value as Array; + if (asArray != null) + { + return asArray.Length > index; + } + + var asString = value as string; + if (asString != null) + { + return asString.Length > index; + } + + var asType = value.GetType(); + var asGenericList = asType.FindGenericInterface(typeof(IList<>)); + if (asGenericList != null) + { + var asList = MagicMarker.GetListFactory(value.GetType()).Invoke(value); + return asList.Count > index; + } + + return false; + } + + public static EventBean GetFragmentNonPono(EventAdapterService eventAdapterService, Object fragmentUnderlying, EventType fragmentEventType) + { + if (fragmentUnderlying == null) + { + return null; + } + if (fragmentEventType is MapEventType) + { + return eventAdapterService.AdapterForTypedMap((IDictionary)fragmentUnderlying, fragmentEventType); + } + return eventAdapterService.AdapterForTypedObjectArray(fragmentUnderlying.UnwrapIntoArray(true), fragmentEventType); + } + + public static Object GetFragmentArray(EventAdapterService eventAdapterService, Object value, EventType fragmentEventType) + { + var mapTypedSubEvents = value as DataMap[]; + if (mapTypedSubEvents != null) + { + var countNull = 0; + foreach (var map in mapTypedSubEvents) + { + if (map != null) + { + countNull++; + } + } + + var mapEvents = new EventBean[countNull]; + var count = 0; + foreach (var map in mapTypedSubEvents) + { + if (map != null) + { + mapEvents[count++] = eventAdapterService.AdapterForTypedMap(map, fragmentEventType); + } + } + + return mapEvents; + } + + var subEvents = value as Object[]; + if (subEvents != null) + { + var subCountNull = subEvents.Count(subEvent => subEvent != null); + + var outEvents = new EventBean[subCountNull]; + var outCount = 0; + + foreach (var item in subEvents) + { + if (item != null) + { + outEvents[outCount++] = BaseNestableEventUtil.GetFragmentNonPono(eventAdapterService, item, fragmentEventType); + } + } + + return outEvents; + } + + return null; + } + + public static Object GetBeanArrayValue(BeanEventPropertyGetter nestedGetter, Object value, int index) + { + var asArray = value as Array; + if (asArray == null) + { + return null; + } + + if (asArray.Length <= index) + { + return null; + } + + var arrayItem = asArray.GetValue(index); + if (arrayItem == null) + { + return null; + } + + return nestedGetter.GetBeanProp(arrayItem); + } + + public static Object GetFragmentPono(Object result, BeanEventType eventType, EventAdapterService eventAdapterService) + { + if (result == null) + { + return null; + } + if (result is EventBean[]) + { + return result; + } + if (result is EventBean) + { + return result; + } + if (result is Array) + { + var arrayX = (Array)result; + var len = arrayX.Length; + var events = new EventBean[len]; + for (var i = 0; i < events.Length; i++) + { + events[i] = eventAdapterService.AdapterForTypedObject(arrayX.GetValue(i), eventType); + } + return events; + } + return eventAdapterService.AdapterForTypedObject(result, eventType); + } + + public static Object GetArrayPropertyValue(EventBean[] wrapper, int index, EventPropertyGetter nestedGetter) + { + if (wrapper == null) + { + return null; + } + if (wrapper.Length <= index) + { + return null; + } + var innerArrayEvent = wrapper[index]; + return nestedGetter.Get(innerArrayEvent); + } + + public static Object GetArrayPropertyFragment(EventBean[] wrapper, int index, EventPropertyGetter nestedGetter) + { + if (wrapper == null) + { + return null; + } + if (wrapper.Length <= index) + { + return null; + } + var innerArrayEvent = wrapper[index]; + return nestedGetter.GetFragment(innerArrayEvent); + } + + public static Object GetArrayPropertyUnderlying(EventBean[] wrapper, int index) + { + if (wrapper == null) + { + return null; + } + if (wrapper.Length <= index) + { + return null; + } + + return wrapper[index].Underlying; + } + + public static Object GetArrayPropertyBean(EventBean[] wrapper, int index) + { + if (wrapper == null) + { + return null; + } + if (wrapper.Length <= index) + { + return null; + } + + return wrapper[index]; + } + + public static Object GetArrayPropertyAsUnderlyingsArray(Type underlyingType, EventBean[] wrapper) + { + if (wrapper != null) + { + var array = Array.CreateInstance(underlyingType, wrapper.Length); + for (var i = 0; i < wrapper.Length; i++) + { + array.SetValue(wrapper[i].Underlying, i); + } + return array; + } + + return null; + } + + public static String ComparePropType(String propName, Object setOneType, Object setTwoType, bool setTwoTypeFound, String otherName) + { + // allow null for nested event types + if ((setOneType is String || setOneType is EventType) && setTwoType == null) + { + return null; + } + if ((setTwoType is String || setTwoType is EventType) && setOneType == null) + { + return null; + } + if (!setTwoTypeFound) + { + return "The property '" + propName + "' is not provided but required"; + } + if (setTwoType == null) + { + return null; + } + if (setOneType == null) + { + return "Type by name '" + otherName + "' in property '" + propName + "' incompatible with null-type or property name not found in target"; + } + + if ((setTwoType is Type) && (setOneType is Type)) + { + var boxedOther = ((Type)setTwoType).GetBoxedType(); + var boxedThis = ((Type)setOneType).GetBoxedType(); + if (boxedOther != boxedThis) + { + if (!TypeHelper.IsSubclassOrImplementsInterface(boxedOther, boxedThis)) + { + return string.Format("Type by name '{0}' in property '{1}' expected {2} but receives {3}", + otherName, propName, Name.Of(boxedThis), Name.Of(boxedOther)); + } + } + } + else if ((setTwoType is BeanEventType) && (setOneType is Type)) + { + Type boxedOther = ((BeanEventType)setTwoType).UnderlyingType.GetBoxedType(); + var boxedThis = ((Type)setOneType).GetBoxedType(); + if (boxedOther != boxedThis) + { + return "Type by name '" + otherName + "' in property '" + propName + "' expected " + boxedThis + " but receives " + boxedOther; + } + } + else if (setTwoType is EventType[] && ((EventType[])setTwoType)[0] is BeanEventType && setOneType is Type && ((Type)setOneType).IsArray) + { + var boxedOther = (((EventType[])setTwoType)[0]).UnderlyingType.GetBoxedType(); + var boxedThis = ((Type)setOneType).GetElementType().GetBoxedType(); + if (boxedOther != boxedThis) + { + return "Type by name '" + otherName + "' in property '" + propName + "' expected " + boxedThis + " but receives " + boxedOther; + } + } + else if ((setTwoType is IDictionary) && (setOneType is IDictionary)) + { + var messageIsDeepEquals = BaseNestableEventType.IsDeepEqualsProperties(propName, (IDictionary)setOneType, (IDictionary)setTwoType); + if (messageIsDeepEquals != null) + { + return messageIsDeepEquals; + } + } + else if ((setTwoType is EventType) && (setOneType is EventType)) + { + bool mismatch; + if (setTwoType is EventTypeSPI && setOneType is EventTypeSPI) + { + mismatch = !((EventTypeSPI)setOneType).EqualsCompareType((EventTypeSPI)setTwoType); + } + else + { + mismatch = !setOneType.Equals(setTwoType); + } + if (mismatch) + { + var setOneEventType = (EventType)setOneType; + var setTwoEventType = (EventType)setTwoType; + return "Type by name '" + otherName + "' in property '" + propName + "' expected event type '" + setOneEventType.Name + "' but receives event type '" + setTwoEventType.Name + "'"; + } + } + else if ((setTwoType is String) && (setOneType is EventType)) + { + var setOneEventType = (EventType)setOneType; + var setTwoEventType = (String)setTwoType; + if (!EventTypeUtility.IsTypeOrSubTypeOf(setTwoEventType, setOneEventType)) + { + return "Type by name '" + otherName + "' in property '" + propName + "' expected event type '" + setOneEventType.Name + "' but receives event type '" + setTwoEventType + "'"; + } + } + else if ((setTwoType is EventType) && (setOneType is String)) + { + var setTwoEventType = (EventType)setTwoType; + var setOneEventType = (String)setOneType; + if (!EventTypeUtility.IsTypeOrSubTypeOf(setOneEventType, setTwoEventType)) + { + return "Type by name '" + otherName + "' in property '" + propName + "' expected event type '" + setOneEventType + "' but receives event type '" + setTwoEventType.Name + "'"; + } + } + else if ((setTwoType is String) && (setOneType is String)) + { + if (!setTwoType.Equals(setOneType)) + { + var setOneEventType = (String)setOneType; + var setTwoEventType = (String)setTwoType; + return "Type by name '" + otherName + "' in property '" + propName + "' expected event type '" + setOneEventType + "' but receives event type '" + setTwoEventType + "'"; + } + } + else if ((setTwoType is EventType[]) && (setOneType is String)) + { + var setTwoTypeArr = (EventType[])setTwoType; + var setTwoFragmentType = setTwoTypeArr[0]; + var setOneTypeString = (String)setOneType; + if (!(setOneTypeString.EndsWith("[]"))) + { + return "Type by name '" + otherName + "' in property '" + propName + "' expected event type '" + setOneType + "' but receives event type '" + setTwoFragmentType.Name + "[]'"; + } + var setOneTypeNoArray = (setOneTypeString).RegexReplaceAll("\\[\\]", ""); + if (!(setTwoFragmentType.Name.Equals(setOneTypeNoArray))) + { + return "Type by name '" + otherName + "' in property '" + propName + "' expected event type '" + setOneTypeNoArray + "[]' but receives event type '" + setTwoFragmentType.Name + "'"; + } + } + else + { + var typeOne = GetTypeName(setOneType); + var typeTwo = GetTypeName(setTwoType); + if (typeOne.Equals(typeTwo)) + { + return null; + } + return "Type by name '" + otherName + "' in property '" + propName + "' expected " + typeOne + " but receives " + typeTwo; + } + + return null; + } + + private static String GetTypeName(Object type) + { + if (type == null) + { + return "null"; + } + if (type is Type) + { + return ((Type)type).FullName; + } + if (type is EventType) + { + return "event type '" + ((EventType)type).Name + "'"; + } + if (type is String) + { + var boxedType = TypeHelper.GetPrimitiveTypeForName((String)type).GetBoxedType(); + if (boxedType != null) + { + return boxedType.FullName; + } + + return (string)type; + } + return type.GetType().FullName; + } + + public class MapIndexedPropPair + { + public MapIndexedPropPair(ICollection mapProperties, ICollection arrayProperties) + { + MapProperties = mapProperties; + ArrayProperties = arrayProperties; + } + + public ICollection MapProperties { get; private set; } + + public ICollection ArrayProperties { get; private set; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/DecoratingEventBean.cs b/NEsper.Core/NEsper.Core/events/DecoratingEventBean.cs new file mode 100755 index 000000000..6e2ea9e17 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/DecoratingEventBean.cs @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using com.espertech.esper.client; + +namespace com.espertech.esper.events +{ + /// + /// Interface for event types that provide decorating event properties as a name-value map. + /// + public interface DecoratingEventBean + { + /// Returns decorating properties. + /// property name and values + IDictionary DecoratingProperties { get; } + + /// + /// Returns the underlying event to the decorated event. + /// + EventBean UnderlyingEvent { get; } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/events/EventAdapterException.cs b/NEsper.Core/NEsper.Core/events/EventAdapterException.cs new file mode 100755 index 000000000..e17088433 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/EventAdapterException.cs @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using EPException = com.espertech.esper.client.EPException; + +namespace com.espertech.esper.events +{ + /// + /// This exception is thrown to indicate a problem resolving an event type by name. + /// + + [Serializable] + public class EventAdapterException : EPException + { + /// Ctor. + /// error message + /// + public EventAdapterException(String message):base(message) + { + } + + /// Ctor. + /// error message + /// + /// nested exception + /// + public EventAdapterException(String message, System.Exception nested):base(message, nested) + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/EventAdapterService.cs b/NEsper.Core/NEsper.Core/events/EventAdapterService.cs new file mode 100755 index 000000000..d227dbe00 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/EventAdapterService.cs @@ -0,0 +1,529 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.client.hook; +using com.espertech.esper.collection; +using com.espertech.esper.core.service; +using com.espertech.esper.core.thread; +using com.espertech.esper.epl.core; +using com.espertech.esper.events.avro; +using com.espertech.esper.events.bean; +using com.espertech.esper.events.map; +using com.espertech.esper.events.xml; +using com.espertech.esper.plugin; +using com.espertech.esper.util; + +using DataMap = System.Collections.Generic.IDictionary; +using TypeMap = System.Collections.Generic.IDictionary; + +namespace com.espertech.esper.events +{ + /// + /// Interface for a service to resolve event names to event type. + /// + public interface EventAdapterService : EventBeanService + { + /// + /// Returns descriptors for all writable properties. + /// + /// to reflect on + /// if set to true [allow any type]. + /// list of writable properties + ICollection GetWriteableProperties(EventType eventType, bool allowAnyType); + + /// + /// Returns a factory for creating and populating event object instances for the + /// given type. + /// + /// to create underlying objects for + /// to write + /// The engine import service. + /// if set to true [allow any type]. + /// factory + /// EventBeanManufactureException if a factory cannot be created for the type + EventBeanManufacturer GetManufacturer( + EventType eventType, + IList properties, + EngineImportService engineImportService, + bool allowAnyType); + +#if DUPLICATE_IN_EVENT_BEAN_SERVICE + /// + /// Creates a thin adaper for an event object given an event type. + /// + /// event object + /// event type + /// + /// event + /// + EventBean AdapterForTypedObject(Object bean, EventType eventType); +#endif + + /// + /// Adds an event type to the registery available for use, and originating outside + /// as a non-adapter. + /// + /// to add an event type under + /// the type to add + /// EventAdapterException if the name is already in used by another type + void AddTypeByName(String name, EventType eventType); + +#if DUPLICATE_IN_EVENT_BEAN_SERVICE + /// + /// Return the event type for a given event name, or null if none is registered for + /// that name. + /// + /// is the event type name to return type for + /// + /// event type for named event, or null if unknown/unnamed type + /// + EventType GetEventTypeByName(String eventTypeName); +#endif + + /// + /// Return all known event types. + /// + /// + /// event types + /// + ICollection AllTypes { get; } + + /// + /// Add an event type with the given name and a given set of properties, wherein properties may itself + /// be Maps, nested and strongly-typed. + /// + /// If the name already exists with the same event property information, returns the existing EventType instance. + /// + /// If the name already exists with different event property information, throws an exception. + /// + /// If the name does not already exists, adds the name and constructs a new . + /// + /// is the name for the event type + /// is the names and types of event properties + /// an optional set of Map event type names that are supertypes to the type + /// if set to true [is preconfigured static]. + /// if set to true [is preconfigured]. + /// if the type is application-configured + /// if the type is from a named window + /// if inserting into a stream + /// event type is the type added + /// EventAdapterException if name already exists and doesn't match property type info + EventType AddNestableMapType( + String eventTypeName, + IDictionary propertyTypes, + ConfigurationEventTypeMap optionalConfig, + bool isPreconfiguredStatic, + bool isPreconfigured, + bool isConfigured, + bool namedWindow, + bool insertInto); + + /// + /// Add an event type with the given name and the given underlying event type, as well as the additional given properties. + /// + /// is the name for the event type + /// is the event type for the event type that this wrapper wraps + /// is the names and types of any additional properties + /// if the type is from a named window + /// if inserting into a stream + /// eventType is the type added + /// EventAdapterException if name already exists and doesn't match this type's info + EventType AddWrapperType( + String eventTypeName, + EventType underlyingEventType, + DataMap propertyTypes, + bool isNamedWindow, + bool isInsertInto); + + /// + /// Creates a new anonymous EventType instance for an event type that contains a map + /// of name value pairs. The method accepts a Map that contains the property names + /// as keys and Class objects as the values. The Class instances represent the + /// property types. + /// + /// New instances are created Statement by this method on every invocation. Clients + /// to this method need to cache the returned EventType instance to reuse EventType's + /// for same-typed events. + /// + /// + /// Name of the type. + /// is a map of String to Class objects + /// isTransient transient types are not available by event type id lookup and recovery, they are always re-created on-the-fly + /// + /// EventType implementation for map field names and value types + /// + EventType CreateAnonymousMapType(string typeName, DataMap propertyTypes, bool isTransient); + + /// + /// Creata a wrapper around an event and some additional properties + /// + /// is the wrapped event + /// are the additional properties + /// os the type metadata for any wrappers of this type + /// wrapper event bean + EventBean AdapterForTypedWrapper( + EventBean theEvent, + IDictionary properties, + EventType eventType); + + /// + /// Add an event type with the given name and fully-qualified class name. + /// If the name already exists with the same class name, returns the existing EventType instance. + /// If the name already exists with different class name, throws an exception. + /// If the name does not already exists, adds the name and constructs a new . + /// Takes into account all event-type-auto-package names supplied and attempts to resolve the class name via the packages if the direct resolution failed. + /// + /// is the name for the event type + /// is the fully qualified class name + /// whether auto-name by namespaces should be considered + /// if set to true [is preconfigured static]. + /// if set to true [is preconfigured]. + /// if set to true [is configured]. + /// event type is the type added + /// EventAdapterException if name already exists and doesn't match class names + EventType AddBeanType( + String eventTypeName, + String fullyQualClassName, + bool considerAutoName, + bool isPreconfiguredStatic, + bool isPreconfigured, + bool isConfigured); + + /// + /// Add an event type with the given name and class. + /// If the name already exists with the same Class, returns the existing EventType instance. + /// If the name already exists with different Class name, throws an exception. + /// If the name does not already exists, adds the name and constructs a new . + /// + /// is the name for the event type + /// is the fully class + /// if set to true [is preconfigured static]. + /// if set to true [is preconfigured]. + /// if the class is application-configured + /// event type is the type added + /// EventAdapterException if name already exists and doesn't match class names + EventType AddBeanType( + String eventTypeName, + Type clazz, + bool isPreconfiguredStatic, + bool isPreconfigured, + bool isConfigured); + + EventType AddBeanTypeByName(String eventTypeName, Type clazz, bool isNamedWindow); + +#if DUPLICATE_IN_EVENT_BEAN_SERVICE + /// + /// Wrap the native event returning an . + /// + /// to be wrapped + /// + /// event bean wrapping native underlying event + /// + EventBean AdapterForObject(Object theEvent); +#endif + +#if DUPLICATE_IN_EVENT_BEAN_SERVICE + /// + /// Wrap the Map-type event returning an using the event type name to identify the EventType that the event should carry. + /// + /// to be wrapped + /// name for the event type of the event + /// + /// event bean wrapping native underlying event + /// + /// EventAdapterException if the name has not been declared, or the event cannot be wrapped using thatname's event type + EventBean AdapterForMap(DataMap theEvent, String eventTypeName); +#endif + +#if DUPLICATE_IN_EVENT_BEAN_SERVICE + EventBean AdapterForObjectArray(Object[] theEvent, String eventTypeName); +#endif + +#if DUPLICATE_IN_EVENT_BEAN_SERVICE + /// + /// Create an event map bean from a set of event properties (name and value + /// objectes) stored in a Map. + /// + /// is key-value pairs for the event properties + /// is the type metadata for any maps of that type + /// EventBean instance + EventBean AdapterForTypedMap(DataMap properties, EventType eventType); +#endif + +#if DUPLICATE_IN_EVENT_BEAN_SERVICE + /// + /// Returns an adapter for the LINQ element that exposes it's data as event + /// properties for use in statements. + /// + /// is the element to wrap + /// + /// event wrapper for document + /// + EventBean AdapterForDOM(XElement element); +#endif + +#if DUPLICATE_IN_EVENT_BEAN_SERVICE + /// + /// Returns an adapter for the XML DOM document that exposes it's data as event + /// properties for use in statements. + /// + /// is the node to wrap + /// event wrapper for document + EventBean AdapterForDOM(XmlNode node); +#endif + +#if DUPLICATE_IN_EVENT_BEAN_SERVICE + /// + /// Returns an adapter for the XML DOM document that exposes it's data as event + /// properties for use in statements. + /// + /// is the node to wrap + /// the event type associated with the node + /// event wrapper for document + EventBean AdapterForTypedDOM(XmlNode node, EventType eventType); +#endif + +#if DUPLICATE_IN_EVENT_BEAN_SERVICE + /// + /// Returns an adapter for the XML LINQ object that exposes it's data as event + /// properties for use in statements. + /// + /// is the node to wrap + /// the event type associated with the node + /// + /// event wrapper for document + /// + EventBean AdapterForTypedDOM(XObject node, EventType eventType); +#endif + +#if DUPLICATE_IN_EVENT_BEAN_SERVICE + /// + /// Returns an adapter for an event underlying object when the event type is known. + /// + /// underlying + /// type + /// event wrapper for object + EventBean AdapterForType(Object theEvent, EventType eventType); +#endif + + /// + /// Create a new anonymous event type with the given underlying event type, as well as the additional given properties. + /// + /// Name of the type. + /// is the event type for the event type that this wrapper wraps + /// is the names and types of any additional properties + /// eventType is the type createdStatement + /// EventAdapterException if name already exists and doesn't match this type's info + EventType CreateAnonymousWrapperType(String typeName, EventType underlyingEventType, DataMap propertyTypes); + + /// + /// Adds an XML DOM event type. + /// + /// is the name to add the type for + /// is the XML DOM config info + /// is the object model of the schema, or null in none provided + /// if set to true [is preconfigured static]. + /// event type + EventType AddXMLDOMType( + String eventTypeName, + ConfigurationEventTypeXMLDOM configurationEventTypeXMLDOM, + SchemaModel optionalSchemaModel, + bool isPreconfiguredStatic); + + /// + /// Gets or sets the configured legacy class information. + /// + IDictionary TypeLegacyConfigs { get; set; } + + /// + /// Returns the configured legacy class information or null if none defined. + /// + /// is the fully-qualified class name + /// + ConfigurationEventTypeLegacy GetTypeLegacyConfigs(String className); + + /// + /// Gets or sets the resolution style for case-sentitivity. + /// + /// for resolving properties. + PropertyResolutionStyle DefaultPropertyResolutionStyle { get; set; } + + /// + /// Adds a namespace within which event types reside. + /// + /// is the namespace within which event types reside + void AddAutoNamePackage(String @namespace); + + /// + /// Returns a subset of the functionality of the service specific to creating event types. + /// + /// bean event type factory + BeanEventTypeFactory BeanEventTypeFactory { get; } + + /// + /// Add a plug-in event representation. + /// + /// URI is the unique identifier for the event representation + /// is the instance + void AddEventRepresentation(Uri eventRepURI, PlugInEventRepresentation pluginEventRep); + + /// + /// Adds a plug-in event type. + /// + /// is the name of the event type + /// is the URIs of plug-in event representations, or child URIs of such + /// is configs for the type + /// type + EventType AddPlugInEventType(string name, IList resolutionURIs, object initializer); + + /// + /// Returns an event sender for a specific type, only generating events of that type. + /// + /// the runtime handle for sending the wrapped type + /// is the name of the event type to return the sender for + /// threading service + /// event sender that is static, single-type + EventSender GetStaticTypeEventSender( + EPRuntimeEventSender runtimeEventSender, + String eventTypeName, + ThreadingService threadingService); + + /// + /// Returns an event sender that dynamically decides what the event type for a given object is. + /// + /// the runtime handle for sending the wrapped type + /// is for plug-in event representations to provide implementations, if accepted, to make a wrapped event + /// threading service + /// + /// event sender that is dynamic, multi-type based on multiple event bean factories provided byplug-in event representations + /// + EventSender GetDynamicTypeEventSender( + EPRuntimeEventSender runtimeEventSender, + Uri[] uri, + ThreadingService threadingService); + + /// + /// Update a given Map event type. + /// + /// name to Update + /// additional properties to add, nesting allowed + /// EventAdapterException when the type is not found or is not a IDictionary + void UpdateMapEventType(String mapEventTypeName, DataMap typeMap); + + /// Casts event type of a list of events to either Wrapper or Map type. + /// to cast + /// target type + /// type casted event array + EventBean[] TypeCast(IList events, EventType targetType); + + /// + /// Removes an event type by a given name indicating by the return value whether the type was found or not. + /// + /// Does not uncache an existing class loaded by a JVM. Does remove XML root element names. Does not handle + /// value-add event types. + /// + /// to remove + /// + /// true if found and removed, false if not found + /// + bool RemoveType(String eventTypeName); + + /// + /// Creates an anonymous map that has no name, however in a fail-over scenario events of this type may be recoverable + /// and therefore the type is only semi-anonymous, identified by the tags and event type names used. + /// + /// Name of the type. + /// simple type per property name + /// array type per property name + /// if the type is going to be in used by child views + /// event type + EventType CreateSemiAnonymousMapType( + String typeName, + IDictionary> taggedEventTypes, + IDictionary> arrayEventTypes, + bool isUsedByChildViews); + + EventType ReplaceXMLEventType( + String xmlEventTypeName, + ConfigurationEventTypeXMLDOM config, + SchemaModel schemaModel); + + AccessorStyleEnum DefaultAccessorStyle { set; } + + IDictionary DeclaredEventTypes { get; } + +#if DUPLICATE_IN_EVENT_BEAN_SERVICE + EventBean AdapterForTypedObjectArray(Object[] props, EventType resultEventType); +#endif + + EventType CreateAnonymousObjectArrayType(String typeName, IDictionary propertyTypes); + EventType CreateAnonymousAvroType(String typeName, IDictionary properties, Attribute[] annotations, String statementName, String engineURI); + + EventType AddNestableObjectArrayType( + String eventTypeName, + IDictionary propertyTypes, + ConfigurationEventTypeObjectArray typeConfig, + bool isPreconfiguredStatic, + bool isPreconfigured, + bool isConfigured, + bool namedWindow, + bool insertInto, + bool table, + string tableName); + + void UpdateObjectArrayEventType(String objectArrayEventTypeName, IDictionary typeMap); + EventBeanSPI GetShellForType(EventType eventType); + EventBeanAdapterFactory GetAdapterFactoryForType(EventType eventType); + EventType CreateAnonymousBeanType(String schemaName, Type clazz); + + EventType AddAvroType( + String eventTypeName, + ConfigurationEventTypeAvro avro, + bool isPreconfiguredStatic, + bool isPreconfigured, + bool isConfigured, + bool isNamedWindow, + bool isInsertInto); + + EventType AddAvroType( + String eventTypeName, + IDictionary types, + bool isPreconfiguredStatic, + bool isPreconfigured, + bool isConfigured, + bool isNamedWindow, + bool isInsertInto, + Attribute[] annotations, + ConfigurationEventTypeAvro config, + String statementName, + String engineURI); + +#if DUPLICATE_IN_EVENT_BEAN_SERVICE + EventBean AdapterForAvro(Object avroGenericDataDotRecord, String eventTypeName); +#endif + + EventAdapterAvroHandler EventAdapterAvroHandler { get; } + +#if DUPLICATE_IN_EVENT_BEAN_SERVICE + EventBean AdapterForTypedAvro(Object avroGenericDataDotRecord, EventType eventType); +#endif + + TypeWidenerCustomizer GetTypeWidenerCustomizer(EventType resultEventType); + + EngineImportService EngineImportService { get; } + } + + public class EventAdapterServiceConstants + { + public readonly static String ANONYMOUS_TYPE_NAME_PREFIX = "anonymous_"; + } +} diff --git a/NEsper.Core/NEsper.Core/events/EventAdapterServiceAnonymousTypeCache.cs b/NEsper.Core/NEsper.Core/events/EventAdapterServiceAnonymousTypeCache.cs new file mode 100755 index 000000000..fba20cf27 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/EventAdapterServiceAnonymousTypeCache.cs @@ -0,0 +1,62 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.events +{ + public class EventAdapterServiceAnonymousTypeCache + { + private readonly int _size; + private readonly LinkedList _recentTypes; + + public EventAdapterServiceAnonymousTypeCache(int size) + { + _size = size; + _recentTypes = new LinkedList(); + } + + public EventType AddReturnExistingAnonymousType(EventType requiredType) + { + lock (this) + { + // only EventTypeSPI compliant implementations considered + if (!(requiredType is EventTypeSPI)) + { + return requiredType; + } + + // check recent types + foreach (EventTypeSPI existing in _recentTypes) + { + if (existing.GetType() == requiredType.GetType() && + Collections.AreEqual(requiredType.PropertyNames, existing.PropertyNames) && + Collections.AreEqual(requiredType.PropertyDescriptors, existing.PropertyDescriptors) && + existing.EqualsCompareType(requiredType)) + { + return existing; + } + } + + // add, removing the oldest + if (_recentTypes.Count == _size && !_recentTypes.IsEmpty()) + { + _recentTypes.RemoveFirst(); + } + if (_recentTypes.Count < _size) + { + _recentTypes.AddLast((EventTypeSPI) requiredType); + } + return requiredType; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/EventAdapterServiceHelper.cs b/NEsper.Core/NEsper.Core/events/EventAdapterServiceHelper.cs new file mode 100755 index 000000000..5024bae77 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/EventAdapterServiceHelper.cs @@ -0,0 +1,412 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Xml; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.core; +using com.espertech.esper.events.avro; +using com.espertech.esper.events.arr; +using com.espertech.esper.events.bean; +using com.espertech.esper.events.map; +using com.espertech.esper.events.xml; +using com.espertech.esper.util; + +namespace com.espertech.esper.events +{ + /// + /// Helper for writeable events. + /// + public class EventAdapterServiceHelper + { + public static string GetMessageExpecting(string eventTypeName, EventType existingType, string typeOfEventType) + { + var message = "Event type named '" + eventTypeName + "' has not been defined or is not a " + typeOfEventType + + " event type"; + if (existingType != null) + { + message += ", the name '" + eventTypeName + "' refers to a " + existingType.UnderlyingType.GetTypeNameFullyQualPretty() + " event type"; + } + else + { + message += ", the name '" + eventTypeName + "' has not been defined as an event type"; + } + return message; + } + + public static EventBeanFactory GetFactoryForType(EventType type, EventAdapterService eventAdapterService) + { + if (type is WrapperEventType) + { + var wrapperType = (WrapperEventType) type; + if (wrapperType.UnderlyingEventType is BeanEventType) + { + return new EventBeanFactoryBeanWrapped( + wrapperType.UnderlyingEventType, wrapperType, eventAdapterService); + } + } + if (type is BeanEventType) + { + return new EventBeanFactoryBean(type, eventAdapterService); + } + if (type is MapEventType) + { + return new EventBeanFactoryMap(type, eventAdapterService); + } + if (type is ObjectArrayEventType) + { + return new EventBeanFactoryObjectArray(type, eventAdapterService); + } + if (type is BaseXMLEventType) + { + return new EventBeanFactoryXML(type, eventAdapterService); + } + if (type is AvroSchemaEventType) + { + return eventAdapterService.EventAdapterAvroHandler.GetEventBeanFactory(type, eventAdapterService); + } + throw new ArgumentException( + "Cannot create event bean factory for event type '" + type.Name + "': " + type.GetType().FullName + + " is not a recognized event type or supported wrap event type"); + } + + /// + /// Returns descriptors for all writable properties. + /// + /// to reflect on + /// whether any type property can be populated + /// list of writable properties + public static ICollection GetWriteableProperties(EventType eventType, bool allowAnyType) + { + if (!(eventType is EventTypeSPI)) + { + return null; + } + if (eventType is BeanEventType) + { + var beanEventType = (BeanEventType) eventType; + return PropertyHelper.GetWritableProperties(beanEventType.UnderlyingType); + } + var typeSPI = (EventTypeSPI) eventType; + if (!allowAnyType && !AllowPropulate(typeSPI)) + { + return null; + } + if (eventType is BaseNestableEventType) + { + IDictionary mapdef = ((BaseNestableEventType) eventType).Types; + ISet writables = new LinkedHashSet(); + foreach (var types in mapdef) + { + if (types.Value is Type) + { + writables.Add(new WriteablePropertyDescriptor(types.Key, (Type) types.Value, null)); + } + if (types.Value is string) + { + var typeName = types.Value.ToString(); + Type clazz = TypeHelper.GetPrimitiveTypeForName(typeName); + if (clazz != null) + { + writables.Add(new WriteablePropertyDescriptor(types.Key, clazz, null)); + } + } + } + return writables; + } + else if (eventType is AvroSchemaEventType) + { + var writables = new LinkedHashSet(); + var desc = typeSPI.WriteableProperties; + foreach (var prop in desc) + { + writables.Add(new WriteablePropertyDescriptor(prop.PropertyName, prop.PropertyType, null)); + } + return writables; + } + else + { + return null; + } + } + + private static bool AllowPropulate(EventTypeSPI typeSPI) + { + if (!typeSPI.Metadata.IsApplicationConfigured && + typeSPI.Metadata.TypeClass != TypeClass.ANONYMOUS && + typeSPI.Metadata.TypeClass != TypeClass.TABLE) + { + return false; + } + return true; + } + + /// + /// Return an adapter for the given type of event using the pre-validated object. + /// + /// value object + /// type of event + /// service for instances + /// event adapter + public static EventBean AdapterForType( + Object theEvent, + EventType eventType, + EventAdapterService eventAdapterService) + { + if (theEvent == null) + { + return null; + } + if (eventType is BeanEventType) + { + return eventAdapterService.AdapterForTypedObject(theEvent, (BeanEventType)eventType); + } + else if (eventType is MapEventType) + { + return eventAdapterService.AdapterForTypedMap((IDictionary) theEvent, eventType); + } + else if (eventType is ObjectArrayEventType) + { + return eventAdapterService.AdapterForTypedObjectArray((Object[]) theEvent, eventType); + } + else if (eventType is BaseConfigurableEventType) + { + return eventAdapterService.AdapterForTypedDOM((XmlNode) theEvent, eventType); + } + else if (eventType is AvroSchemaEventType) + { + return eventAdapterService.AdapterForTypedAvro(theEvent, eventType); + } + else + { + return null; + } + } + + /// + /// Returns a factory for creating and populating event object instances for the given type. + /// + /// fatory for event + /// to create underlying objects for + /// to write + /// for resolving methods + /// whether any type property can be populated + /// avro handler + /// if a factory cannot be created for the type + /// factory + public static EventBeanManufacturer GetManufacturer( + EventAdapterService eventAdapterService, + EventType eventType, + IList properties, + EngineImportService engineImportService, + bool allowAnyType, + EventAdapterAvroHandler avroHandler) + { + if (!(eventType is EventTypeSPI)) + { + return null; + } + if (eventType is BeanEventType) + { + var beanEventType = (BeanEventType) eventType; + return new EventBeanManufacturerBean( + beanEventType, eventAdapterService, properties, engineImportService); + } + var typeSPI = (EventTypeSPI) eventType; + if (!allowAnyType && !AllowPropulate(typeSPI)) + { + return null; + } + if (eventType is MapEventType) + { + var mapEventType = (MapEventType) eventType; + return new EventBeanManufacturerMap(mapEventType, eventAdapterService, properties); + } + if (eventType is ObjectArrayEventType) + { + var objectArrayEventType = (ObjectArrayEventType) eventType; + return new EventBeanManufacturerObjectArray(objectArrayEventType, eventAdapterService, properties); + } + if (eventType is AvroSchemaEventType) + { + var avroSchemaEventType = (AvroSchemaEventType) eventType; + return avroHandler.GetEventBeanManufacturer(avroSchemaEventType, eventAdapterService, properties); + } + return null; + } + + public static EventBean[] TypeCast( + IList events, + EventType targetType, + EventAdapterService eventAdapterService) + { + var convertedArray = new EventBean[events.Count]; + var count = 0; + foreach (var theEvent in events) + { + EventBean converted; + if (theEvent is DecoratingEventBean) + { + var wrapper = (DecoratingEventBean) theEvent; + if (targetType is MapEventType) + { + var props = new Dictionary(); + props.PutAll(wrapper.DecoratingProperties); + foreach (var propDesc in wrapper.UnderlyingEvent.EventType.PropertyDescriptors) + { + props.Put(propDesc.PropertyName, wrapper.UnderlyingEvent.Get(propDesc.PropertyName)); + } + converted = eventAdapterService.AdapterForTypedMap(props, targetType); + } + else + { + converted = eventAdapterService.AdapterForTypedWrapper( + wrapper.UnderlyingEvent, wrapper.DecoratingProperties, targetType); + } + } + else if ((theEvent.EventType is MapEventType) && (targetType is MapEventType)) + { + var mapEvent = (MappedEventBean) theEvent; + converted = eventAdapterService.AdapterForTypedMap(mapEvent.Properties, targetType); + } + else if ((theEvent.EventType is MapEventType) && (targetType is WrapperEventType)) + { + converted = eventAdapterService.AdapterForTypedWrapper(theEvent, Collections.EmptyDataMap, targetType); + } + else if ((theEvent.EventType is BeanEventType) && (targetType is BeanEventType)) + { + converted = eventAdapterService.AdapterForTypedObject(theEvent.Underlying, targetType); + } + else if (theEvent.EventType is ObjectArrayEventType && targetType is ObjectArrayEventType) + { + var convertedObjectArray = ObjectArrayEventType.ConvertEvent( + theEvent, (ObjectArrayEventType) targetType); + converted = eventAdapterService.AdapterForTypedObjectArray(convertedObjectArray, targetType); + } + else if (theEvent.EventType is AvroSchemaEventType && targetType is AvroSchemaEventType) + { + Object convertedGenericRecord = eventAdapterService.EventAdapterAvroHandler.ConvertEvent( + theEvent, (AvroSchemaEventType) targetType); + converted = eventAdapterService.AdapterForTypedAvro(convertedGenericRecord, targetType); + } + else + { + throw new EPException("Unknown event type " + theEvent.EventType); + } + convertedArray[count] = converted; + count++; + } + return convertedArray; + } + + public static EventBeanSPI GetShellForType(EventType eventType) + { + if (eventType is BeanEventType) + { + return new BeanEventBean(null, eventType); + } + if (eventType is ObjectArrayEventType) + { + return new ObjectArrayEventBean(null, eventType); + } + if (eventType is MapEventType) + { + return new MapEventBean(null, eventType); + } + if (eventType is BaseXMLEventType) + { + return new XMLEventBean(null, eventType); + } + throw new EventAdapterException("Event type '" + eventType.Name + "' is not an engine-native event type"); + } + + public static EventBeanAdapterFactory GetAdapterFactoryForType(EventType eventType) + { + if (eventType is BeanEventType) + { + return new EventBeanAdapterFactoryBean(eventType); + } + if (eventType is ObjectArrayEventType) + { + return new EventBeanAdapterFactoryObjectArray(eventType); + } + if (eventType is MapEventType) + { + return new EventBeanAdapterFactoryMap(eventType); + } + if (eventType is BaseXMLEventType) + { + return new EventBeanAdapterFactoryXml(eventType); + } + throw new EventAdapterException("Event type '" + eventType.Name + "' is not an engine-native event type"); + } + + public class EventBeanAdapterFactoryBean : EventBeanAdapterFactory + { + private readonly EventType _eventType; + + public EventBeanAdapterFactoryBean(EventType eventType) + { + _eventType = eventType; + } + + public EventBean MakeAdapter(Object underlying) + { + return new BeanEventBean(underlying, _eventType); + } + } + + public class EventBeanAdapterFactoryMap : EventBeanAdapterFactory + { + private readonly EventType _eventType; + + public EventBeanAdapterFactoryMap(EventType eventType) + { + _eventType = eventType; + } + + public EventBean MakeAdapter(Object underlying) + { + return new MapEventBean((IDictionary) underlying, _eventType); + } + } + + public class EventBeanAdapterFactoryObjectArray : EventBeanAdapterFactory + { + private readonly EventType _eventType; + + public EventBeanAdapterFactoryObjectArray(EventType eventType) + { + _eventType = eventType; + } + + public EventBean MakeAdapter(Object underlying) + { + return new ObjectArrayEventBean((Object[]) underlying, _eventType); + } + } + + public class EventBeanAdapterFactoryXml : EventBeanAdapterFactory + { + private readonly EventType _eventType; + + public EventBeanAdapterFactoryXml(EventType eventType) + { + _eventType = eventType; + } + + public EventBean MakeAdapter(Object underlying) + { + return new XMLEventBean((XmlNode) underlying, _eventType); + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/events/EventAdapterServiceImpl.cs b/NEsper.Core/NEsper.Core/events/EventAdapterServiceImpl.cs new file mode 100755 index 000000000..46d70956f --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/EventAdapterServiceImpl.cs @@ -0,0 +1,1394 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Xml; +using System.Xml.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.client.util; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.compat.threading; +using com.espertech.esper.core.service; +using com.espertech.esper.core.thread; +using com.espertech.esper.epl.core; +using com.espertech.esper.events.arr; +using com.espertech.esper.events.avro; +using com.espertech.esper.events.bean; +using com.espertech.esper.events.map; +using com.espertech.esper.events.xml; +using com.espertech.esper.plugin; +using com.espertech.esper.util; + +namespace com.espertech.esper.events +{ + using DataMap = IDictionary; + using TypeMap = IDictionary; + + /// + /// Implementation for resolving event name to event type. + /// + /// The implementation assigned a unique identifier to each event type. For Class-based event types, only + /// one EventType instance and one event type id exists for the same class. + /// + /// Event type names must be unique, that is an name must resolve to a single event type. + /// + /// Each event type can have multiple names defined for it. For example, expressions such as "select * from A" + /// and "select * from B" in which A and B are names for the same class X the select clauses each fireStatementStopped + /// for events of type X. In summary, names A and B point to the same underlying event type and therefore event type id. + /// + public class EventAdapterServiceImpl : EventAdapterService + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly BeanEventAdapter _beanEventAdapter; + + private readonly ICache _beanEventTypeCache = + new Cache3D(); + + private readonly EventTypeIdGenerator _eventTypeIdGenerator; + private readonly EventAdapterServiceAnonymousTypeCache _anonymousTypeCache; + + private readonly EventAdapterAvroHandler _avroHandler; + private readonly EngineImportService _engineImportService; + + private readonly IDictionary _nameToHandlerMap; + private readonly IDictionary _nameToTypeMap; + + private readonly ICollection _namespaces; + private readonly IDictionary _plugInRepresentations; + + private readonly ILockable _syncLock = LockManager.CreateLock(MethodBase.GetCurrentMethod().DeclaringType); + private readonly IDictionary _typesPerBean; + private readonly IDictionary _xmldomRootElementNames; + private readonly IDictionary _xelementRootElementNames; + + /// Ctor. + public EventAdapterServiceImpl( + EventTypeIdGenerator eventTypeIdGenerator, + int anonymousTypeCacheSize, + EventAdapterAvroHandler avroHandler, + EngineImportService engineImportService) + { + _eventTypeIdGenerator = eventTypeIdGenerator; + _avroHandler = avroHandler; + _engineImportService = engineImportService; + + _nameToTypeMap = new Dictionary(); + _xmldomRootElementNames = new Dictionary(); + _xelementRootElementNames = new Dictionary(); + _namespaces = new LinkedHashSet(); + _nameToHandlerMap = new Dictionary(); + + // Share the mapping of class to type with the type creation for thread safety + _typesPerBean = new ConcurrentDictionary(); + _beanEventAdapter = new BeanEventAdapter(_typesPerBean, this, eventTypeIdGenerator); + _plugInRepresentations = new Dictionary(); + _anonymousTypeCache = new EventAdapterServiceAnonymousTypeCache(anonymousTypeCacheSize); + } + + /// Sets the default property resolution style. + /// is the default style + public PropertyResolutionStyle DefaultPropertyResolutionStyle + { + get { return _beanEventAdapter.DefaultPropertyResolutionStyle; } + set { _beanEventAdapter.DefaultPropertyResolutionStyle = value; } + } + + public AccessorStyleEnum DefaultAccessorStyle + { + set { _beanEventAdapter.DefaultAccessorStyle = value; } + } + + public IDictionary DeclaredEventTypes + { + get { return new Dictionary(_nameToTypeMap); } + } + + public EngineImportService EngineImportService + { + get { return _engineImportService; } + } + + public ICollection GetWriteableProperties(EventType eventType, bool allowAnyType) + { + return EventAdapterServiceHelper.GetWriteableProperties(eventType, allowAnyType); + } + + public EventBeanManufacturer GetManufacturer( + EventType eventType, + IList properties, + EngineImportService engineImportService, + bool allowAnyType) + { + return EventAdapterServiceHelper.GetManufacturer( + this, eventType, properties, engineImportService, allowAnyType, _avroHandler); + } + + public void AddTypeByName( + String name, + EventType eventType) + { + using (_syncLock.Acquire()) + { + if (_nameToTypeMap.ContainsKey(name)) + { + throw new EventAdapterException("Event type by name '" + name + "' already exists"); + } + _nameToTypeMap.Put(name, eventType); + } + } + + public void AddEventRepresentation(Uri eventRepURI, PlugInEventRepresentation pluginEventRep) + { + if (_plugInRepresentations.ContainsKey(eventRepURI)) + { + throw new EventAdapterException( + "Plug-in event representation URI by name " + eventRepURI + + " already exists"); + } + _plugInRepresentations.Put(eventRepURI, pluginEventRep); + } + + public EventType AddPlugInEventType(string eventTypeName, IList resolutionURIs, object initializer) + { + if (_nameToTypeMap.ContainsKey(eventTypeName)) + { + throw new EventAdapterException( + "Event type named '" + eventTypeName + + "' has already been declared"); + } + + PlugInEventRepresentation handlingFactory = null; + Uri handledEventTypeURI = null; + + if ((resolutionURIs == null) || (resolutionURIs.Count == 0)) + { + throw new EventAdapterException( + "Event type named '" + eventTypeName + "' could not be created as" + + " no resolution URIs for dynamic resolution of event type names through a plug-in event representation have been defined"); + } + + foreach (var eventTypeURI in resolutionURIs) + { + // Determine a list of event representations that may handle this type + var allFactories = new Dictionary(_plugInRepresentations); + var factories = URIUtil.FilterSort(eventTypeURI, allFactories); + + if (factories.IsEmpty()) + { + continue; + } + + // Ask each in turn to accept the type (the process of resolving the type) + foreach (var entry in factories) + { + var factory = entry.Value; + var context = new PlugInEventTypeHandlerContext( + eventTypeURI, initializer, eventTypeName, _eventTypeIdGenerator.GetTypeId(eventTypeName)); + if (factory.AcceptsType(context)) + { + handlingFactory = factory; + handledEventTypeURI = eventTypeURI; + break; + } + } + + if (handlingFactory != null) + { + break; + } + } + + if (handlingFactory == null) + { + throw new EventAdapterException( + "Event type named '" + eventTypeName + + "' could not be created as none of the " + + "registered plug-in event representations accepts any of the resolution URIs '" + + resolutionURIs.Render() + + "' and initializer"); + } + + var typeContext = new PlugInEventTypeHandlerContext( + handledEventTypeURI, + initializer, + eventTypeName, + _eventTypeIdGenerator.GetTypeId(eventTypeName)); + var handler = handlingFactory.GetTypeHandler(typeContext); + if (handler == null) + { + throw new EventAdapterException( + "Event type named '" + eventTypeName + + "' could not be created as no handler was returned"); + } + + var eventType = handler.EventType; + _nameToTypeMap.Put(eventTypeName, eventType); + _nameToHandlerMap.Put(eventTypeName, handler); + + return eventType; + } + + public EventSender GetStaticTypeEventSender( + EPRuntimeEventSender runtimeEventSender, + String eventTypeName, + ThreadingService threadingService) + { + var eventType = _nameToTypeMap.Get(eventTypeName); + if (eventType == null) + { + throw new EventTypeException("Event type named '" + eventTypeName + "' could not be found"); + } + + // handle built-in types + if (eventType is BeanEventType) + { + return new EventSenderBean(runtimeEventSender, (BeanEventType) eventType, this, threadingService); + } + else if (eventType is MapEventType) + { + return new EventSenderMap(runtimeEventSender, (MapEventType) eventType, this, threadingService); + } + else if (eventType is ObjectArrayEventType) + { + return new EventSenderObjectArray( + runtimeEventSender, (ObjectArrayEventType) eventType, this, threadingService); + } + else if (eventType is BaseXMLEventType) + { + return new EventSenderXMLDOM(runtimeEventSender, (BaseXMLEventType) eventType, this, threadingService); + } + else if (eventType is AvroSchemaEventType) + { + return new EventSenderAvro(runtimeEventSender, eventType, this, threadingService); + } + + var handlers = _nameToHandlerMap.Get(eventTypeName); + if (handlers != null) + { + return handlers.GetSender(runtimeEventSender); + } + throw new EventTypeException( + "An event sender for event type named '" + eventTypeName + + "' could not be created as the type is internal"); + } + + public void UpdateMapEventType( + String mapeventTypeName, + IDictionary typeMap) + { + var type = _nameToTypeMap.Get(mapeventTypeName); + if (type == null) + { + throw new EventAdapterException("Event type named '" + mapeventTypeName + "' has not been declared"); + } + if (!(type is MapEventType)) + { + throw new EventAdapterException("Event type by name '" + mapeventTypeName + "' is not a Map event type"); + } + + var mapEventType = (MapEventType) type; + mapEventType.AddAdditionalProperties(typeMap, this); + } + + public void UpdateObjectArrayEventType(String objectArrayEventTypeName, DataMap typeMap) + { + var type = _nameToTypeMap.Get(objectArrayEventTypeName); + if (type == null) + { + throw new EventAdapterException( + "Event type named '" + objectArrayEventTypeName + "' has not been declared"); + } + + var objectArrayEventType = type as ObjectArrayEventType; + if (objectArrayEventType == null) + { + throw new EventAdapterException( + "Event type by name '" + objectArrayEventTypeName + "' is not an Object-array event type"); + } + + objectArrayEventType.AddAdditionalProperties(typeMap, this); + } + + public EventSender GetDynamicTypeEventSender( + EPRuntimeEventSender epRuntime, + Uri[] uri, + ThreadingService threadingService) + { + var handlingFactories = new List(); + foreach (var resolutionURI in uri) + { + // Determine a list of event representations that may handle this type + var allFactories = new Dictionary(_plugInRepresentations); + var factories = URIUtil.FilterSort(resolutionURI, allFactories); + + if (factories.IsEmpty()) + { + continue; + } + + // Ask each in turn to accept the type (the process of resolving the type) + foreach (var entry in factories) + { + var factory = entry.Value; + var context = new PlugInEventBeanReflectorContext(resolutionURI); + if (factory.AcceptsEventBeanResolution(context)) + { + var beanFactory = factory.GetEventBeanFactory(context); + if (beanFactory == null) + { + Log.Warn("Plug-in event representation returned a null bean factory, ignoring entry"); + continue; + } + var desc = new EventSenderURIDesc(beanFactory, resolutionURI, entry.Key); + handlingFactories.Add(desc); + } + } + } + + if (handlingFactories.IsEmpty()) + { + throw new EventTypeException( + "Event sender for resolution URIs '" + uri.Render() + + "' did not return at least one event representation's event factory"); + } + + return new EventSenderImpl(handlingFactories, epRuntime, threadingService); + } + + public BeanEventTypeFactory BeanEventTypeFactory + { + get { return _beanEventAdapter; } + } + + public EventType AddBeanType( + String eventTypeName, + Type clazz, + bool isPreconfiguredStatic, + bool isPreconfigured, + bool isConfigured) + { + using (_syncLock.Acquire()) + { + if (Log.IsDebugEnabled) + { + Log.Debug(".addBeanType Adding " + eventTypeName + " for type " + clazz.FullName); + } + + var existingType = _nameToTypeMap.Get(eventTypeName); + if (existingType != null) + { + if (existingType.UnderlyingType.Equals(clazz)) + { + return existingType; + } + + throw new EventAdapterException( + "Event type named '" + eventTypeName + + "' has already been declared with differing underlying type information:" + + existingType.UnderlyingType.Name + + " versus " + clazz.Name); + } + + EventType eventType = _beanEventAdapter.CreateBeanType( + eventTypeName, clazz, isPreconfiguredStatic, + isPreconfigured, isConfigured); + _nameToTypeMap.Put(eventTypeName, eventType); + + return eventType; + } + } + + public EventType AddBeanTypeByName( + String eventTypeName, + Type clazz, + bool isNamedWindow) + { + using (_syncLock.Acquire()) + { + if (Log.IsDebugEnabled) + { + Log.Debug(".addBeanTypeNamedWindow Adding " + eventTypeName + " for type " + clazz.FullName); + } + + var existingType = _nameToTypeMap.Get(eventTypeName); + TypeClass typeClass; + if (existingType != null) + { + if (existingType is BeanEventType && + existingType.UnderlyingType == clazz && + existingType.Name.Equals(eventTypeName)) + { + typeClass = ((BeanEventType) existingType).Metadata.TypeClass; + if (isNamedWindow) + { + if (typeClass == TypeClass.NAMED_WINDOW) + { + return existingType; + } + } + else + { + if (typeClass == TypeClass.STREAM) + { + return existingType; + } + } + } + throw new EventAdapterException( + "An event type named '" + eventTypeName + + "' has already been declared"); + } + + typeClass = isNamedWindow + ? TypeClass.NAMED_WINDOW + : TypeClass.STREAM; + var beanEventType = + new BeanEventType( + EventTypeMetadata.CreateBeanType(eventTypeName, clazz, false, false, false, typeClass), + _eventTypeIdGenerator.GetTypeId(eventTypeName), clazz, this, + _beanEventAdapter.GetClassToLegacyConfigs(clazz.AssemblyQualifiedName)); + _nameToTypeMap.Put(eventTypeName, beanEventType); + + return beanEventType; + } + } + + /// Create an event bean given an event of object id. + /// is the event class + /// event + public EventBean AdapterForObject(Object theEvent) + { + var type = theEvent.GetType(); + BeanEventType eventType; + if (!_beanEventTypeCache.TryGet(type, out eventType)) + { + eventType = _beanEventTypeCache.Put( + type, + _typesPerBean.Get(type) ?? + _beanEventAdapter.CreateBeanType(type.FullName, type, false, false, false)); + } + + return new BeanEventBean(theEvent, eventType); + } + + /// + /// Add an event type for the given type name. + /// + /// is the name + /// is the type name + /// whether auto-name by namespaces should be considered + /// if set to true [is preconfigured static]. + /// if set to true [is preconfigured]. + /// if set to true [is configured]. + /// event type + /// EventAdapterException if the Class name cannot resolve or other error occured + public EventType AddBeanType( + String eventTypeName, + String fullyQualClassName, + bool considerAutoName, + bool isPreconfiguredStatic, + bool isPreconfigured, + bool isConfigured) + { + using (_syncLock.Acquire()) + { + if (Log.IsDebugEnabled) + { + Log.Debug(".addBeanType Adding " + eventTypeName + " for type " + fullyQualClassName); + } + + var existingType = _nameToTypeMap.Get(eventTypeName); + if (existingType != null) + { + if ((fullyQualClassName == existingType.UnderlyingType.AssemblyQualifiedName) || + (fullyQualClassName == existingType.UnderlyingType.FullName) || + (fullyQualClassName == existingType.UnderlyingType.Name)) + { + if (Log.IsDebugEnabled) + { + Log.Debug(".addBeanType Returning existing type for " + eventTypeName); + } + return existingType; + } + + throw new EventAdapterException( + "Event type named '" + eventTypeName + + "' has already been declared with differing underlying type information: Class " + + existingType.UnderlyingType.FullName + + " versus " + fullyQualClassName); + } + + // Try to resolve as a fully-qualified class name first + Type clazz = null; + try + { + clazz = _engineImportService.GetClassForNameProvider().ClassForName(fullyQualClassName); + } + catch (TypeLoadException ex) + { + if (!considerAutoName) + { + throw new EventAdapterException( + "Event type or class named '" + fullyQualClassName + "' was not found", ex); + } + + // Attempt to resolve from auto-name packages + foreach (var @namespace in _namespaces) + { + var generatedClassName = @namespace + "." + fullyQualClassName; + try + { + var resolvedClass = _engineImportService.GetClassForNameProvider().ClassForName(generatedClassName); + if (clazz != null) + { + throw new EventAdapterException( + "Failed to resolve name '" + eventTypeName + + "', the class was ambigously found both in " + + "namespace '" + clazz.Namespace + "' and in " + + "namespace '" + resolvedClass.Namespace + "'", ex); + } + clazz = resolvedClass; + } + catch (TypeLoadException) + { + // expected, class may not exists in all packages + } + } + if (clazz == null) + { + throw new EventAdapterException( + "Event type or class named '" + fullyQualClassName + "' was not found", ex); + } + } + + EventType eventType = _beanEventAdapter.CreateBeanType( + eventTypeName, clazz, isPreconfiguredStatic, + isPreconfigured, isConfigured); + _nameToTypeMap.Put(eventTypeName, eventType); + + return eventType; + } + } + + public EventType AddNestableMapType( + String eventTypeName, + IDictionary propertyTypes, + ConfigurationEventTypeMap optionalConfig, + bool isPreconfiguredStatic, + bool isPreconfigured, + bool isConfigured, + bool namedWindow, + bool insertInto) + { + using (_syncLock.Acquire()) + { + var mapSuperTypes = EventTypeUtility.GetSuperTypesDepthFirst( + optionalConfig, EventUnderlyingType.MAP, _nameToTypeMap); + var metadata = EventTypeMetadata.CreateNonPonoApplicationType( + ApplicationType.MAP, + eventTypeName, isPreconfiguredStatic, + isPreconfigured, isConfigured, + namedWindow, insertInto); + + var typeId = _eventTypeIdGenerator.GetTypeId(eventTypeName); + var newEventType = new MapEventType( + metadata, eventTypeName, typeId, this, propertyTypes, + mapSuperTypes.First, mapSuperTypes.Second, optionalConfig); + + var existingType = _nameToTypeMap.Get(eventTypeName); + if (existingType != null) + { + // The existing type must be the same as the type createdStatement + if (!newEventType.EqualsCompareType(existingType)) + { + var message = newEventType.GetEqualsMessage(existingType); + throw new EventAdapterException( + "Event type named '" + eventTypeName + + "' has already been declared with differing column name or type information: " + + message); + } + + // Since it's the same, return the existing type + return existingType; + } + + _nameToTypeMap.Put(eventTypeName, newEventType); + + return newEventType; + } + } + + public EventType AddNestableObjectArrayType( + String eventTypeName, + IDictionary propertyTypes, + ConfigurationEventTypeObjectArray optionalConfig, + bool isPreconfiguredStatic, + bool isPreconfigured, + bool isConfigured, + bool namedWindow, + bool insertInto, + bool table, + string tableName) + { + using (_syncLock.Acquire()) + { + if (optionalConfig != null && optionalConfig.SuperTypes.Count > 1) + { + throw new EventAdapterException(ConfigurationEventTypeObjectArray.SINGLE_SUPERTYPE_MSG); + } + + var mapSuperTypes = EventTypeUtility.GetSuperTypesDepthFirst( + optionalConfig, EventUnderlyingType.OBJECTARRAY, _nameToTypeMap); + + EventTypeMetadata metadata; + if (table) + { + metadata = EventTypeMetadata.CreateTable(tableName); + } + else + { + metadata = EventTypeMetadata.CreateNonPonoApplicationType( + ApplicationType.OBJECTARR, + eventTypeName, + isPreconfiguredStatic, + isPreconfigured, + isConfigured, + namedWindow, + insertInto); + } + + + var typeId = _eventTypeIdGenerator.GetTypeId(eventTypeName); + var newEventType = new ObjectArrayEventType( + metadata, eventTypeName, typeId, this, + propertyTypes, optionalConfig, + mapSuperTypes.First, + mapSuperTypes.Second); + + var existingType = _nameToTypeMap.Get(eventTypeName); + if (existingType != null) + { + // The existing type must be the same as the type createdStatement + if (!newEventType.EqualsCompareType(existingType)) + { + var message = newEventType.GetEqualsMessage(existingType); + throw new EventAdapterException( + "Event type named '" + eventTypeName + + "' has already been declared with differing column name or type information: " + + message); + } + + // Since it's the same, return the existing type + return existingType; + } + + _nameToTypeMap.Put(eventTypeName, newEventType); + + return newEventType; + } + } + + public EventBean AdapterForMap(IDictionary theEvent, String eventTypeName) + { + var existingType = _nameToTypeMap.Get(eventTypeName); + if (!(existingType is MapEventType)) + { + throw new EPException(EventAdapterServiceHelper.GetMessageExpecting(eventTypeName, existingType, "Map")); + } + + return AdapterForTypedMap(theEvent, existingType); + } + + public EventBean AdapterForObjectArray(Object[] theEvent, String eventTypeName) + { + var existingType = _nameToTypeMap.Get(eventTypeName); + if (!(existingType is ObjectArrayEventType)) + { + throw new EPException( + EventAdapterServiceHelper.GetMessageExpecting(eventTypeName, existingType, "Object-array")); + } + + return AdapterForTypedObjectArray(theEvent, existingType); + } + + public EventBean AdapterForDOM(XElement element) + { + var rootElementName = element.Name.LocalName; + var eventType = _xelementRootElementNames.Get(rootElementName); + if (eventType == null) + { + throw new EventAdapterException( + "DOM event root element name '" + rootElementName + + "' has not been configured"); + } + + return new XEventBean(element, eventType); + } + + public EventBean AdapterForDOM(XmlNode node) + { + XmlNode namedNode; + if (node is XmlDocument) + { + namedNode = ((XmlDocument) node).DocumentElement; + } + else if (node is XmlElement) + { + namedNode = node; + } + else + { + throw new EPException( + "Unexpected DOM node of type '" + node.GetType().Name + + "' encountered, please supply a XmlDocument or XmlElement node"); + } + + var rootElementName = namedNode.LocalName; + if (rootElementName == null) + { + rootElementName = namedNode.Name; + } + + var eventType = _xmldomRootElementNames.Get(rootElementName); + if (eventType == null) + { + throw new EventAdapterException( + "DOM event root element name '" + rootElementName + + "' has not been configured"); + } + + return new XMLEventBean(namedNode, eventType); + } + + public EventBean AdapterForTypedDOM(XmlNode node, EventType eventType) + { + return new XMLEventBean(node, eventType); + } + + public EventBean AdapterForTypedDOM(XObject node, EventType eventType) + { + return new XEventBean(node, eventType); + } + + public EventBean AdapterForType(Object theEvent, EventType eventType) + { + return EventAdapterServiceHelper.AdapterForType(theEvent, eventType, this); + } + + public EventBean AdapterForTypedMap(IDictionary properties, EventType eventType) + { + return new MapEventBean(properties, eventType); + } + + public EventBean AdapterForTypedObjectArray(Object[] properties, EventType eventType) + { + return new ObjectArrayEventBean(properties, eventType); + } + + public EventType AddWrapperType( + String eventTypeName, + EventType underlyingEventType, + IDictionary propertyTypes, + bool isNamedWindow, + bool isInsertInto) + { + using (_syncLock.Acquire()) + { + // If we are wrapping an underlying type that is itself a wrapper, then this is a special case + if (underlyingEventType is WrapperEventType) + { + var underlyingWrapperType = (WrapperEventType) underlyingEventType; + + // the underlying type becomes the type already wrapped + // properties are a superset of the wrapped properties and the additional properties + underlyingEventType = underlyingWrapperType.UnderlyingEventType; + IDictionary propertiesSuperset = new Dictionary(); + propertiesSuperset.PutAll(underlyingWrapperType.UnderlyingMapType.Types); + propertiesSuperset.PutAll(propertyTypes); + propertyTypes = propertiesSuperset; + } + + var isPropertyAgnostic = false; + if (underlyingEventType is EventTypeSPI) + { + isPropertyAgnostic = ((EventTypeSPI) underlyingEventType).Metadata.IsPropertyAgnostic; + } + + var metadata = EventTypeMetadata.CreateWrapper( + eventTypeName, isNamedWindow, isInsertInto, + isPropertyAgnostic); + var typeId = _eventTypeIdGenerator.GetTypeId(eventTypeName); + var newEventType = new WrapperEventType( + metadata, eventTypeName, typeId, + underlyingEventType, propertyTypes, this); + + var existingType = _nameToTypeMap.Get(eventTypeName); + if (existingType != null) + { + // The existing type must be the same as the type created + if (!newEventType.EqualsCompareType(existingType)) + { + // It is possible that the wrapped event type is compatible: a child type of the desired type + var message = IsCompatibleWrapper(existingType, underlyingEventType, propertyTypes); + if (message == null) + { + return existingType; + } + + throw new EventAdapterException( + "Event type named '" + eventTypeName + + "' has already been declared with differing column name or type information: " + + message); + } + + // Since it's the same, return the existing type + return existingType; + } + + _nameToTypeMap.Put(eventTypeName, newEventType); + + return newEventType; + } + } + + public EventType CreateAnonymousMapType(string typeName, DataMap propertyTypes, bool isTransient) + { + var assignedTypeName = EventAdapterServiceConstants.ANONYMOUS_TYPE_NAME_PREFIX + typeName; + var metadata = EventTypeMetadata.CreateAnonymous(assignedTypeName, ApplicationType.MAP); + var mapEventType = new MapEventType( + metadata, assignedTypeName, _eventTypeIdGenerator.GetTypeId(assignedTypeName), this, propertyTypes, null, + null, null); + return _anonymousTypeCache.AddReturnExistingAnonymousType(mapEventType); + } + + public EventType CreateAnonymousObjectArrayType(String typeName, IDictionary propertyTypes) + { + var assignedTypeName = EventAdapterServiceConstants.ANONYMOUS_TYPE_NAME_PREFIX + typeName; + var metadata = EventTypeMetadata.CreateAnonymous(assignedTypeName, ApplicationType.OBJECTARR); + var oaEventType = new ObjectArrayEventType( + metadata, assignedTypeName, _eventTypeIdGenerator.GetTypeId(assignedTypeName), this, propertyTypes, null, + null, null); + return _anonymousTypeCache.AddReturnExistingAnonymousType(oaEventType); + } + + public EventType CreateAnonymousAvroType( + String typeName, + IDictionary properties, + Attribute[] annotations, + String statementName, + String engineURI) + { + var assignedTypeName = EventAdapterServiceConstants.ANONYMOUS_TYPE_NAME_PREFIX + typeName; + var metadata = EventTypeMetadata.CreateAnonymous(assignedTypeName, ApplicationType.AVRO); + var typeId = _eventTypeIdGenerator.GetTypeId(assignedTypeName); + var newEventType = _avroHandler.NewEventTypeFromNormalized( + metadata, assignedTypeName, typeId, this, properties, annotations, null, null, null, statementName, + engineURI); + return _anonymousTypeCache.AddReturnExistingAnonymousType(newEventType); + } + + public EventType CreateSemiAnonymousMapType( + String typeName, + IDictionary> taggedEventTypes, + IDictionary> arrayEventTypes, + bool isUsedByChildViews) + { + IDictionary mapProperties = new LinkedHashMap(); + foreach (var entry in taggedEventTypes) + { + mapProperties.Put(entry.Key, entry.Value.First); + } + foreach (var entry in arrayEventTypes) + { + mapProperties.Put( + entry.Key, new[] + { + entry.Value.First + }); + } + return CreateAnonymousMapType(typeName, mapProperties, true); + } + + public EventType CreateAnonymousWrapperType( + String typeName, + EventType underlyingEventType, + IDictionary propertyTypes) + { + var assignedTypeName = EventAdapterServiceConstants.ANONYMOUS_TYPE_NAME_PREFIX + typeName; + var metadata = EventTypeMetadata.CreateAnonymous(assignedTypeName, ApplicationType.WRAPPER); + + // If we are wrapping an underlying type that is itself a wrapper, then this is a special case: unwrap + if (underlyingEventType is WrapperEventType) + { + var underlyingWrapperType = (WrapperEventType) underlyingEventType; + + // the underlying type becomes the type already wrapped + // properties are a superset of the wrapped properties and the additional properties + underlyingEventType = underlyingWrapperType.UnderlyingEventType; + IDictionary propertiesSuperset = new Dictionary(); + propertiesSuperset.PutAll(underlyingWrapperType.UnderlyingMapType.Types); + propertiesSuperset.PutAll(propertyTypes); + propertyTypes = propertiesSuperset; + } + + var wrapperEventType = new WrapperEventType( + metadata, assignedTypeName, _eventTypeIdGenerator.GetTypeId(assignedTypeName), underlyingEventType, + propertyTypes, this); + return _anonymousTypeCache.AddReturnExistingAnonymousType(wrapperEventType); + } + + public EventBean AdapterForTypedWrapper( + EventBean theEvent, + IDictionary properties, + EventType eventType) + { + if (theEvent is DecoratingEventBean) + { + var wrapper = (DecoratingEventBean) theEvent; + properties.PutAll(wrapper.DecoratingProperties); + return new WrapperEventBean(wrapper.UnderlyingEvent, properties, eventType); + } + + return new WrapperEventBean(theEvent, properties, eventType); + } + + public void AddAutoNamePackage(String @namespace) + { + _namespaces.Add(@namespace); + } + + public EventType CreateAnonymousBeanType(String eventTypeName, Type clazz) + { + var beanEventType = new BeanEventType( + EventTypeMetadata.CreateBeanType( + eventTypeName, clazz, false, false, false, TypeClass.ANONYMOUS), -1, clazz, this, + _beanEventAdapter.GetClassToLegacyConfigs(clazz.AssemblyQualifiedName)); + return _anonymousTypeCache.AddReturnExistingAnonymousType(beanEventType); + } + + private Pair> GetSuperTypesDepthFirst( + ICollection superTypesSet, + bool expectMapType) + { + if (superTypesSet == null || superTypesSet.IsEmpty()) + { + return new Pair>(null, null); + } + + var superTypes = new EventType[superTypesSet.Count]; + var deepSuperTypes = new LinkedHashSet(); + + var count = 0; + foreach (var superName in superTypesSet) + { + var type = _nameToTypeMap.Get(superName); + if (type == null) + { + throw new EventAdapterException("Supertype by name '" + superName + "' could not be found"); + } + if (expectMapType) + { + if (!(type is MapEventType)) + { + throw new EventAdapterException( + "Supertype by name '" + superName + + "' is not a Map, expected a Map event type as a supertype"); + } + } + else + { + if (!(type is ObjectArrayEventType)) + { + throw new EventAdapterException( + "Supertype by name '" + superName + + "' is not an Object-array type, expected a Object-array event type as a supertype"); + } + } + superTypes[count++] = type; + deepSuperTypes.Add(type); + AddRecursiveSupertypes(deepSuperTypes, type); + } + + var superTypesListDepthFirst = new List(deepSuperTypes); + superTypesListDepthFirst.Reverse(); + + return new Pair>( + superTypes, new LinkedHashSet(superTypesListDepthFirst)); + } + + public bool RemoveType(String name) + { + var eventType = _nameToTypeMap.Delete(name); + if (eventType == null) + { + return false; + } + + if (eventType is BaseXMLEventType) + { + var baseXML = (BaseXMLEventType) eventType; + _xmldomRootElementNames.Remove(baseXML.RootElementName); + _xelementRootElementNames.Remove(baseXML.RootElementName); + } + + _nameToHandlerMap.Remove(name); + return true; + } + + /// Set the legacy class type information. + /// is the legacy class configs + public IDictionary TypeLegacyConfigs + { + set { _beanEventAdapter.TypeToLegacyConfigs = value; } + get { return _beanEventAdapter.TypeToLegacyConfigs; } + } + + public ConfigurationEventTypeLegacy GetTypeLegacyConfigs(String className) + { + return _beanEventAdapter.GetClassToLegacyConfigs(className); + } + + public ICollection AllTypes + { + get { return _nameToTypeMap.Values.ToArray(); } + } + + public EventType GetEventTypeByName(String eventTypeName) + { + if (eventTypeName == null) + { + throw new IllegalStateException("Null event type name parameter"); + } + return _nameToTypeMap.Get(eventTypeName); + } + + /// + /// Add a configured XML DOM event type. + /// + /// is the name name of the event type + /// configures the event type schema and namespace and XPathproperty information. + /// The optional schema model. + /// if set to true [is preconfigured static]. + /// + public EventType AddXMLDOMType( + String eventTypeName, + ConfigurationEventTypeXMLDOM configurationEventTypeXMLDOM, + SchemaModel optionalSchemaModel, + bool isPreconfiguredStatic) + { + using (_syncLock.Acquire()) + { + return AddXMLDOMType( + eventTypeName, configurationEventTypeXMLDOM, optionalSchemaModel, + isPreconfiguredStatic, false); + } + } + + public EventType ReplaceXMLEventType( + String xmlEventTypeName, + ConfigurationEventTypeXMLDOM config, + SchemaModel schemaModel) + { + return AddXMLDOMType(xmlEventTypeName, config, schemaModel, false, true); + } + + /// + /// Add a configured XML DOM event type. + /// + /// is the name name of the event type + /// configures the event type schema and namespace and XPathproperty information. + /// The optional schema model. + /// if set to true [is preconfigured static]. + /// if set to true [allow override existing]. + /// + private EventType AddXMLDOMType( + String eventTypeName, + ConfigurationEventTypeXMLDOM configurationEventTypeXMLDOM, + SchemaModel optionalSchemaModel, + bool isPreconfiguredStatic, + bool allowOverrideExisting) + { + using (_syncLock.Acquire()) + { + if (configurationEventTypeXMLDOM.RootElementName == null) + { + throw new EventAdapterException("Required root element name has not been supplied"); + } + + if (!allowOverrideExisting) + { + var existingType = _nameToTypeMap.Get(eventTypeName); + if (existingType != null) + { + var message = "Event type named '" + eventTypeName + + "' has already been declared with differing column name or type information"; + if (!(existingType is BaseXMLEventType)) + { + throw new EventAdapterException(message); + } + var config = + ((BaseXMLEventType) existingType).ConfigurationEventTypeXMLDOM; + if (!config.Equals(configurationEventTypeXMLDOM)) + { + throw new EventAdapterException(message); + } + + return existingType; + } + } + + var metadata = EventTypeMetadata.CreateXMLType( + eventTypeName, isPreconfiguredStatic, + configurationEventTypeXMLDOM.SchemaResource == + null && + configurationEventTypeXMLDOM.SchemaText == + null); + EventType type; + if ((configurationEventTypeXMLDOM.SchemaResource == null) && + (configurationEventTypeXMLDOM.SchemaText == null)) + { + type = new SimpleXMLEventType( + metadata, + _eventTypeIdGenerator.GetTypeId(eventTypeName), + configurationEventTypeXMLDOM, + this); + } + else + { + if (optionalSchemaModel == null) + { + throw new EPException("Schema model has not been provided"); + } + type = new SchemaXMLEventType( + metadata, _eventTypeIdGenerator.GetTypeId(eventTypeName), + configurationEventTypeXMLDOM, optionalSchemaModel, this); + } + + EventType xelementType = new SimpleXElementType( + metadata, + _eventTypeIdGenerator.GetTypeId(eventTypeName), + configurationEventTypeXMLDOM, + this); + + _nameToTypeMap.Put(eventTypeName, type); + _xmldomRootElementNames.Put(configurationEventTypeXMLDOM.RootElementName, type); + _xelementRootElementNames.Put(configurationEventTypeXMLDOM.RootElementName, xelementType); + + return type; + } + } + + /// Returns true if the wrapper type is compatible with an existing wrapper type, for the reason that the underlying event is a subtype of the existing underlying wrapper's type. + /// is the existing wrapper type + /// is the proposed new wrapper type's underlying type + /// is the additional properties + /// true for compatible, or false if not + public static String IsCompatibleWrapper( + EventType existingType, + EventType underlyingType, + IDictionary propertyTypes) + { + if (!(existingType is WrapperEventType)) + { + return "Type '" + existingType.Name + "' is not compatible"; + } + var existingWrapper = (WrapperEventType) existingType; + + var message = MapEventType.IsDeepEqualsProperties( + existingType.Name, + existingWrapper.UnderlyingMapType.Types, + propertyTypes); + if (message != null) + { + return message; + } + var existingUnderlyingType = existingWrapper.UnderlyingEventType; + + // If one of the supertypes of the underlying type is the existing underlying type, we are compatible + if (underlyingType.SuperTypes == null) + { + return "Type '" + existingType.Name + "' is not compatible"; + } + + if (underlyingType.DeepSuperTypes.Any(superUnderlying => superUnderlying == existingUnderlyingType)) + { + return null; + } + return "Type '" + existingType.Name + "' is not compatible"; + } + + public EventBean AdapterForTypedObject(Object bean, EventType eventType) + { + return new BeanEventBean(bean, eventType); + } + + private Pair> GetMapSuperTypes(ConfigurationEventTypeMap optionalConfig) + { + if (optionalConfig == null || optionalConfig.SuperTypes == null || optionalConfig.SuperTypes.IsEmpty()) + { + return new Pair>(null, null); + } + + var superTypes = new EventType[optionalConfig.SuperTypes.Count]; + ICollection deepSuperTypes = new LinkedHashSet(); + + var count = 0; + foreach (var superName in optionalConfig.SuperTypes) + { + var type = _nameToTypeMap.Get(superName); + if (type == null) + { + throw new EventAdapterException("Map supertype by name '" + superName + "' could not be found"); + } + if (!(type is MapEventType)) + { + throw new EventAdapterException( + "Supertype by name '" + superName + + "' is not a Map, expected a Map event type as a supertype"); + } + superTypes[count++] = type; + deepSuperTypes.Add(type); + AddRecursiveSupertypes(deepSuperTypes, type); + } + return new Pair>(superTypes, deepSuperTypes); + } + + private static void AddRecursiveSupertypes( + ICollection superTypes, + EventType child) + { + if (child.SuperTypes != null) + { + for (var i = 0; i < child.SuperTypes.Length; i++) + { + superTypes.Add(child.SuperTypes[i]); + AddRecursiveSupertypes(superTypes, child.SuperTypes[i]); + } + } + } + + public EventBean[] TypeCast(IList events, EventType targetType) + { + return EventAdapterServiceHelper.TypeCast(events, targetType, this); + } + + public EventBeanSPI GetShellForType(EventType eventType) + { + return EventAdapterServiceHelper.GetShellForType(eventType); + } + + public EventBeanAdapterFactory GetAdapterFactoryForType(EventType eventType) + { + return EventAdapterServiceHelper.GetAdapterFactoryForType(eventType); + } + + public EventType AddAvroType( + String eventTypeName, + ConfigurationEventTypeAvro avro, + bool isPreconfiguredStatic, + bool isPreconfigured, + bool isConfigured, + bool isNamedWindow, + bool isInsertInto) + { + var metadata = EventTypeMetadata.CreateNonPonoApplicationType( + ApplicationType.AVRO, eventTypeName, isPreconfiguredStatic, isPreconfigured, isConfigured, isNamedWindow, + isInsertInto); + + var typeId = _eventTypeIdGenerator.GetTypeId(eventTypeName); + var avroSuperTypes = EventTypeUtility.GetSuperTypesDepthFirst( + avro, EventUnderlyingType.AVRO, _nameToTypeMap); + var newEventType = _avroHandler.NewEventTypeFromSchema( + metadata, eventTypeName, typeId, this, avro, avroSuperTypes.First, avroSuperTypes.Second); + + var existingType = _nameToTypeMap.Get(eventTypeName); + if (existingType != null) + { + _avroHandler.ValidateExistingType(existingType, newEventType); + return existingType; + } + + _nameToTypeMap.Put(eventTypeName, newEventType); + + return newEventType; + } + + public EventType AddAvroType( + String eventTypeName, + IDictionary types, + bool isPreconfiguredStatic, + bool isPreconfigured, + bool isConfigured, + bool isNamedWindow, + bool isInsertInto, + Attribute[] annotations, + ConfigurationEventTypeAvro config, + String statementName, + String engineURI) + { + var metadata = EventTypeMetadata.CreateNonPonoApplicationType( + ApplicationType.AVRO, eventTypeName, isPreconfiguredStatic, isPreconfigured, isConfigured, isNamedWindow, + isInsertInto); + + var typeId = _eventTypeIdGenerator.GetTypeId(eventTypeName); + var avroSuperTypes = EventTypeUtility.GetSuperTypesDepthFirst( + config, EventUnderlyingType.AVRO, _nameToTypeMap); + var newEventType = _avroHandler.NewEventTypeFromNormalized( + metadata, eventTypeName, typeId, this, types, annotations, config, avroSuperTypes.First, + avroSuperTypes.Second, statementName, engineURI); + + var existingType = _nameToTypeMap.Get(eventTypeName); + if (existingType != null) + { + _avroHandler.ValidateExistingType(existingType, newEventType); + return existingType; + } + + _nameToTypeMap.Put(eventTypeName, newEventType); + + return newEventType; + } + + public EventBean AdapterForAvro(Object avroGenericDataDotRecord, String eventTypeName) + { + EventType existingType = _nameToTypeMap.Get(eventTypeName); + if (!(existingType is AvroSchemaEventType)) + { + throw new EPException( + EventAdapterServiceHelper.GetMessageExpecting(eventTypeName, existingType, "Avro")); + } + return _avroHandler.AdapterForTypeAvro(avroGenericDataDotRecord, existingType); + } + + public EventBean AdapterForTypedAvro(Object avroGenericDataDotRecord, EventType eventType) + { + return _avroHandler.AdapterForTypeAvro(avroGenericDataDotRecord, eventType); + } + + public EventAdapterAvroHandler EventAdapterAvroHandler + { + get { return _avroHandler; } + } + + public TypeWidenerCustomizer GetTypeWidenerCustomizer(EventType resultEventType) + { + return resultEventType is AvroSchemaEventType + ? _avroHandler.GetTypeWidenerCustomizer(resultEventType) + : null; + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/events/EventBeanAdapterFactory.cs b/NEsper.Core/NEsper.Core/events/EventBeanAdapterFactory.cs new file mode 100755 index 000000000..e34f93b62 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/EventBeanAdapterFactory.cs @@ -0,0 +1,18 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using com.espertech.esper.client; + +namespace com.espertech.esper.events +{ + public interface EventBeanAdapterFactory + { + EventBean MakeAdapter(Object underlying); + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/events/EventBeanCopyMethod.cs b/NEsper.Core/NEsper.Core/events/EventBeanCopyMethod.cs new file mode 100755 index 000000000..10def463a --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/EventBeanCopyMethod.cs @@ -0,0 +1,21 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; + +namespace com.espertech.esper.events +{ + /// Implementations copy the event object for controlled modification (shallow copy). + public interface EventBeanCopyMethod + { + /// Copy the event bean returning a shallow copy. + /// to copy + /// shallow copy + EventBean Copy(EventBean theEvent); + } +} diff --git a/NEsper.Core/NEsper.Core/events/EventBeanFactoryBean.cs b/NEsper.Core/NEsper.Core/events/EventBeanFactoryBean.cs new file mode 100755 index 000000000..2bd63d0bf --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/EventBeanFactoryBean.cs @@ -0,0 +1,31 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events +{ + public class EventBeanFactoryBean : EventBeanFactory + { + private readonly EventType _type; + private readonly EventAdapterService _eventAdapterService; + + public EventBeanFactoryBean(EventType type, EventAdapterService eventAdapterService) + { + _type = type; + _eventAdapterService = eventAdapterService; + } + + public EventBean Wrap(Object underlying) + { + return _eventAdapterService.AdapterForTypedObject(underlying, _type); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/EventBeanFactoryBeanWrapped.cs b/NEsper.Core/NEsper.Core/events/EventBeanFactoryBeanWrapped.cs new file mode 100755 index 000000000..10a72e417 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/EventBeanFactoryBeanWrapped.cs @@ -0,0 +1,35 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.events +{ + public class EventBeanFactoryBeanWrapped : EventBeanFactory + { + private readonly EventType _beanEventType; + private readonly EventType _wrapperEventType; + private readonly EventAdapterService _eventAdapterService; + + public EventBeanFactoryBeanWrapped(EventType beanEventType, EventType wrapperEventType, EventAdapterService eventAdapterService) + { + _beanEventType = beanEventType; + _wrapperEventType = wrapperEventType; + _eventAdapterService = eventAdapterService; + } + + public EventBean Wrap(Object underlying) + { + EventBean bean = _eventAdapterService.AdapterForTypedObject(underlying, _beanEventType); + return _eventAdapterService.AdapterForTypedWrapper(bean, new EmptyDictionary(), _wrapperEventType); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/EventBeanFactoryMap.cs b/NEsper.Core/NEsper.Core/events/EventBeanFactoryMap.cs new file mode 100755 index 000000000..c6598b4e5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/EventBeanFactoryMap.cs @@ -0,0 +1,33 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events +{ + using DataMap = System.Collections.Generic.IDictionary; + + public class EventBeanFactoryMap : EventBeanFactory + { + private readonly EventType _type; + private readonly EventAdapterService _eventAdapterService; + + public EventBeanFactoryMap(EventType type, EventAdapterService eventAdapterService) + { + _type = type; + _eventAdapterService = eventAdapterService; + } + + public EventBean Wrap(Object underlying) + { + return _eventAdapterService.AdapterForTypedMap((DataMap)underlying, _type); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/EventBeanFactoryObjectArray.cs b/NEsper.Core/NEsper.Core/events/EventBeanFactoryObjectArray.cs new file mode 100755 index 000000000..b8f2897bd --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/EventBeanFactoryObjectArray.cs @@ -0,0 +1,39 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using com.espertech.esper.client; + +namespace com.espertech.esper.events +{ + public class EventBeanFactoryObjectArray : EventBeanFactory + { + private readonly EventAdapterService _eventAdapterService; + private readonly EventType _type; + + public EventBeanFactoryObjectArray(EventType type, EventAdapterService eventAdapterService) + { + _type = type; + _eventAdapterService = eventAdapterService; + } + + public Type UnderlyingType + { + get { return typeof (object[]); } + } + + #region EventBeanFactory Members + + public EventBean Wrap(Object underlying) + { + return _eventAdapterService.AdapterForTypedObjectArray((Object[]) underlying, _type); + } + + #endregion + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/events/EventBeanFactoryXML.cs b/NEsper.Core/NEsper.Core/events/EventBeanFactoryXML.cs new file mode 100755 index 000000000..271e7a42a --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/EventBeanFactoryXML.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Xml; +using System.Xml.Linq; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events +{ + public class EventBeanFactoryXML : EventBeanFactory + { + private readonly EventType _type; + private readonly EventAdapterService _eventAdapterService; + + public EventBeanFactoryXML(EventType type, EventAdapterService eventAdapterService) + { + _type = type; + _eventAdapterService = eventAdapterService; + } + + public EventBean Wrap(Object underlying) + { + if (underlying is XmlNode) { + return _eventAdapterService.AdapterForTypedDOM((XmlNode) underlying, _type); + } + + return _eventAdapterService.AdapterForTypedDOM((XElement) underlying, _type); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/EventBeanManufactureException.cs b/NEsper.Core/NEsper.Core/events/EventBeanManufactureException.cs new file mode 100755 index 000000000..64b9bcfb0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/EventBeanManufactureException.cs @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.events +{ + /// + /// Thrown to indicate a problem creating or populating an underlying event objects. + /// + public class EventBeanManufactureException : Exception + { + /// + /// Ctor. + /// + /// message + /// cause + public EventBeanManufactureException(String message, Exception innerException) + : base(message, innerException) + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/EventBeanManufacturer.cs b/NEsper.Core/NEsper.Core/events/EventBeanManufacturer.cs new file mode 100755 index 000000000..941bcdc17 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/EventBeanManufacturer.cs @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events +{ + /// + /// Factory for creating an event bean instance by writing property values to an + /// underlying object. + /// + public interface EventBeanManufacturer + { + /// + /// Make an event object populating property values. + /// + /// values to populate + /// + /// event object + /// + EventBean Make(Object[] properties); + + Object MakeUnderlying(Object[] properties); + } +} diff --git a/NEsper.Core/NEsper.Core/events/EventBeanManufacturerMap.cs b/NEsper.Core/NEsper.Core/events/EventBeanManufacturerMap.cs new file mode 100755 index 000000000..427d76da8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/EventBeanManufacturerMap.cs @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.events.map; + +namespace com.espertech.esper.events +{ + /// Factory for Map-underlying events. + public class EventBeanManufacturerMap : EventBeanManufacturer + { + private readonly MapEventType _mapEventType; + private readonly EventAdapterService _eventAdapterService; + private readonly IList _writables; + + /// Ctor. + /// type to create + /// event factory + /// written properties + public EventBeanManufacturerMap(MapEventType mapEventType, EventAdapterService eventAdapterService, IList properties) + { + _eventAdapterService = eventAdapterService; + _mapEventType = mapEventType; + _writables = properties; + } + + public EventBean Make(Object[] properties) + { + var values = (IDictionary) MakeUnderlying(properties); + return _eventAdapterService.AdapterForTypedMap(values, _mapEventType); + } + + public object MakeUnderlying(Object[] properties) + { + IDictionary values = new Dictionary(); + for (int i = 0; i < properties.Length; i++) + { + values.Put(_writables[i].PropertyName, properties[i]); + } + + return values; + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/EventBeanManufacturerObjectArray.cs b/NEsper.Core/NEsper.Core/events/EventBeanManufacturerObjectArray.cs new file mode 100755 index 000000000..513a94af6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/EventBeanManufacturerObjectArray.cs @@ -0,0 +1,84 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.events +{ + /// + /// Factory for ObjectArray-underlying events. + /// + public class EventBeanManufacturerObjectArray : EventBeanManufacturer + { + private readonly EventAdapterService _eventAdapterService; + private readonly ObjectArrayEventType _eventType; + private readonly int[] _indexPerWritable; + private readonly bool _oneToOne; + + /// Ctor. + /// type to create + /// event factory + /// written properties + public EventBeanManufacturerObjectArray(ObjectArrayEventType eventType, + EventAdapterService eventAdapterService, + IList properties) + { + _eventAdapterService = eventAdapterService; + _eventType = eventType; + + IDictionary indexes = eventType.PropertiesIndexes; + _indexPerWritable = new int[properties.Count]; + bool oneToOneMapping = true; + for (int i = 0; i < properties.Count; i++) + { + String propertyName = properties[i].PropertyName; + int index; + if (!indexes.TryGetValue(propertyName, out index)) + { + throw new IllegalStateException("Failed to find property '" + propertyName + + "' among the array indexes"); + } + _indexPerWritable[i] = index; + if (index != i) + { + oneToOneMapping = false; + } + } + _oneToOne = oneToOneMapping && properties.Count == eventType.PropertyNames.Length; + } + + #region EventBeanManufacturer Members + + public EventBean Make(Object[] properties) + { + var cols = MakeUnderlying(properties) as object[]; + return _eventAdapterService.AdapterForTypedObjectArray(cols, _eventType); + } + + #endregion + + public object MakeUnderlying(Object[] properties) + { + if (_oneToOne) + { + return properties; + } + var cols = new object[_eventType.PropertyNames.Length]; + for (int i = 0; i < properties.Length; i++) + { + int indexToWrite = _indexPerWritable[i]; + cols[indexToWrite] = properties[i]; + } + return cols; + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/events/EventBeanReader.cs b/NEsper.Core/NEsper.Core/events/EventBeanReader.cs new file mode 100755 index 000000000..fc341b023 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/EventBeanReader.cs @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events +{ + /// + /// Interface for reading all event properties of an event. + /// + public interface EventBeanReader + { + /// + /// Returns all event properties in the exact order they appear as properties. + /// + /// to read + /// + /// property values + /// + Object[] Read(EventBean theEvent); + } +} diff --git a/NEsper.Core/NEsper.Core/events/EventBeanReaderDefaultImpl.cs b/NEsper.Core/NEsper.Core/events/EventBeanReaderDefaultImpl.cs new file mode 100755 index 000000000..5430d9912 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/EventBeanReaderDefaultImpl.cs @@ -0,0 +1,56 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; + +namespace com.espertech.esper.events +{ + /// + /// Reader implementation that utilizes event property getters and thereby works with all + /// event types regardsless of whether a type returns an event reader when asked for. + /// + public class EventBeanReaderDefaultImpl : EventBeanReader + { + private readonly EventPropertyGetter[] _gettersArray; + + /// + /// Ctor. + /// + /// the type of events to read + public EventBeanReaderDefaultImpl(EventType eventType) + { + var properties = eventType.PropertyNames; + var getters = new List(); + foreach (var property in properties) + { + var getter = eventType.GetGetter(property); + if (getter != null) + { + getters.Add(getter); + } + } + _gettersArray = getters.ToArray(); + } + + public Object[] Read(EventBean theEvent) + { + var values = new Object[_gettersArray.Length]; + for (var i = 0; i < _gettersArray.Length; i++) + { + values[i] = _gettersArray[i].Get(theEvent); + } + return values; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/events/EventBeanSPI.cs b/NEsper.Core/NEsper.Core/events/EventBeanSPI.cs new file mode 100755 index 000000000..95558cf93 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/EventBeanSPI.cs @@ -0,0 +1,17 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; + +namespace com.espertech.esper.events +{ + public interface EventBeanSPI : EventBean + { + new object Underlying { get; set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/events/EventBeanUtility.cs b/NEsper.Core/NEsper.Core/events/EventBeanUtility.cs new file mode 100755 index 000000000..748846637 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/EventBeanUtility.cs @@ -0,0 +1,1076 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.util; + +namespace com.espertech.esper.events +{ + /// + /// Method to getSelectListEvents events in collections to other collections or other event types. + /// + public class EventBeanUtility + { + public static EventBean[] AllocatePerStreamShift(EventBean[] eventsPerStream) + { + var evalEvents = new EventBean[eventsPerStream.Length + 1]; + Array.Copy(eventsPerStream, 0, evalEvents, 1, eventsPerStream.Length); + return evalEvents; + } + + public static Object GetNonemptyFirstEventUnderlying(ICollection matchingEvents) + { + EventBean @event = GetNonemptyFirstEvent(matchingEvents); + return @event.Underlying; + } + + public static EventBean GetNonemptyFirstEvent(ICollection matchingEvents) + { + if (matchingEvents is IList) + { + return ((IList) matchingEvents)[0]; + } + if (matchingEvents is LinkedList) + { + return ((LinkedList) matchingEvents).First.Value; + } + return matchingEvents.First(); + } + + /// + /// Gets the assert property getter. + /// + /// The type. + /// Name of the property. + /// + public static EventPropertyGetter GetAssertPropertyGetter(EventType type, String propertyName) + { + EventPropertyGetter getter = type.GetGetter(propertyName); + if (getter == null) + { + throw new IllegalStateException("Property " + propertyName + " not found in type " + type.Name); + } + return getter; + } + + public static EventPropertyGetter GetAssertPropertyGetter(EventType[] eventTypes, int keyStreamNum, String property) + { + return GetAssertPropertyGetter(eventTypes[keyStreamNum], property); + } + + /// Resizes an array of events to a new size. + /// + /// Returns the same array reference if the size is the same. + /// array to resize + /// new array size + /// /// resized array + public static T[] ResizeArray(T[] oldArray, int newSize) + { + if (oldArray == null) + { + return null; + } + if (oldArray.Length == newSize) + { + return oldArray; + } + var newArray = new T[newSize]; + int preserveLength = Math.Min(oldArray.Length, newSize); + if (preserveLength > 0) + { + Array.Copy(oldArray, 0, newArray, 0, preserveLength); + } + return newArray; + } + + /// + /// Flatten the vector of arrays to an array. Return null if an empty vector was passed, else return an array containing all the events. + /// + /// vector + /// array with all events + public static UniformPair FlattenList(LinkedList> eventVector) + { + int count = eventVector.Count; + if (count == 0) + { + return null; + } + + if (count == 1) + { + return eventVector.First.Value; + } + + int totalNew = 0; + int totalOld = 0; + foreach (var pair in eventVector) + { + if (pair != null) + { + T[] first = pair.First; + if (first != null) + { + totalNew += first.Length; + } + + T[] second = pair.Second; + if (second != null) + { + totalOld += second.Length; + } + } + } + + if ((totalNew + totalOld) == 0) + { + return null; + } + + T[] resultNew = null; + if (totalNew > 0) + { + resultNew = new T[totalNew]; + } + + T[] resultOld = null; + if (totalOld > 0) + { + resultOld = new T[totalOld]; + } + + int destPosNew = 0; + int destPosOld = 0; + foreach (var pair in eventVector) + { + if (pair != null) + { + T[] first = pair.First; + if (first != null) + { + Array.Copy(first, 0, resultNew, destPosNew, first.Length); + destPosNew += first.Length; + } + + T[] second = pair.Second; + if (second != null) + { + Array.Copy(second, 0, resultOld, destPosOld, second.Length); + destPosOld += second.Length; + } + } + } + + return new UniformPair(resultNew, resultOld); + } + + /// + /// Flatten the vector of arrays to an array. Return null if an empty vector was passed, else return + /// an array containing all the events. + /// + /// + /// vector + /// array with all events + public static T[] Flatten(ICollection eventVector) + { + if (eventVector.IsEmpty()) + { + return null; + } + + if (eventVector.Count == 1) + { + return eventVector.First(); + } + + int totalElements = eventVector + .Where(arr => arr != null) + .Sum(arr => arr.Length); + + if (totalElements == 0) + { + return null; + } + + var result = new T[totalElements]; + int destPos = 0; + foreach (var arr in eventVector) + { + if (arr != null) + { + Array.Copy(arr, 0, result, destPos, arr.Length); + destPos += arr.Length; + } + } + + return result; + } + + /// + /// Flatten the vector of arrays to an array. Return null if an empty vector was passed, else return + /// an array containing all the events. + /// + /// + /// is a list of updates of old and new events + /// array with all events + public static UniformPair FlattenBatchStream(IList> updateVector) + { + if (updateVector.IsEmpty()) + { + return new UniformPair(null, null); + } + + if (updateVector.Count == 1) + { + return new UniformPair(updateVector[0].First, updateVector[0].Second); + } + + int totalNewEvents = 0; + int totalOldEvents = 0; + foreach (var pair in updateVector) + { + if (pair.First != null) + { + totalNewEvents += pair.First.Length; + } + if (pair.Second != null) + { + totalOldEvents += pair.Second.Length; + } + } + + if ((totalNewEvents == 0) && (totalOldEvents == 0)) + { + return new UniformPair(null, null); + } + + T[] newEvents = null; + T[] oldEvents = null; + if (totalNewEvents != 0) + { + newEvents = new T[totalNewEvents]; + } + if (totalOldEvents != 0) + { + oldEvents = new T[totalOldEvents]; + } + + int destPosNew = 0; + int destPosOld = 0; + foreach (var pair in updateVector) + { + T[] newData = pair.First; + T[] oldData = pair.Second; + + if (newData != null) + { + int newDataLen = newData.Length; + Array.Copy(newData, 0, newEvents, destPosNew, newDataLen); + destPosNew += newDataLen; + } + if (oldData != null) + { + int oldDataLen = oldData.Length; + Array.Copy(oldData, 0, oldEvents, destPosOld, oldDataLen); + destPosOld += oldDataLen; + } + } + + return new UniformPair(newEvents, oldEvents); + } + + /// + /// Returns object array containing property values of given properties, retrieved via EventPropertyGetter instances. + /// + /// The event. + /// getters to use for getting property values + /// object array with property values + public static Object[] GetPropertyArray(EventBean theEvent, IList propertyGetters) + { + unchecked + { + var propertyGettersLength = propertyGetters.Count; + var keyValues = new Object[propertyGettersLength]; + for (int ii = 0; ii < propertyGettersLength; ii++) + { + keyValues[ii] = propertyGetters[ii].Get(theEvent); + } + return keyValues; + } + } + + public static Object[] GetPropertyArray(EventBean[] eventsPerStream, IList propertyGetters, int[] streamNums) + { + unchecked + { + var propertyGettersLength = propertyGetters.Count; + var keyValues = new Object[propertyGettersLength]; + for (int i = 0; i < propertyGettersLength; i++) + { + keyValues[i] = propertyGetters[i].Get(eventsPerStream[streamNums[i]]); + } + return keyValues; + } + } + + /// + /// Returns Multikey instance for given event and getters. + /// + /// The event. + /// getters for access to properties + /// MultiKey with property values + public static MultiKeyUntyped GetMultiKey(EventBean theEvent, + IList propertyGetters) + { + Object[] keyValues = GetPropertyArray(theEvent, propertyGetters); + return new MultiKeyUntyped(keyValues); + } + + public static MultiKeyUntyped GetMultiKey(EventBean theEvent, + IList propertyGetters, + IList coercionTypes) + { + Object[] keyValues = GetPropertyArray(theEvent, propertyGetters); + if (coercionTypes == null) + { + return new MultiKeyUntyped(keyValues); + } + for (int i = 0; i < coercionTypes.Count; i++) + { + Object key = keyValues[i]; + if ((key != null) && (!Equals(key.GetType(), coercionTypes[i]))) + { + if (key.IsNumber()) + { + key = CoercerFactory.CoerceBoxed(key, coercionTypes[i]); + keyValues[i] = key; + } + } + } + return new MultiKeyUntyped(keyValues); + } + + public static MultiKeyUntyped GetMultiKey(EventBean[] eventsPerStream, + ExprEvaluator[] evaluators, + ExprEvaluatorContext context, + IList coercionTypes) + { + Object[] keyValues = GetPropertyArray(eventsPerStream, evaluators, context); + if (coercionTypes == null) + { + return new MultiKeyUntyped(keyValues); + } + for (int i = 0; i < coercionTypes.Count; i++) + { + Object key = keyValues[i]; + if ((key != null) && (!Equals(key.GetType(), coercionTypes[i]))) + { + if (key.IsNumber()) + { + key = CoercerFactory.CoerceBoxed(key, coercionTypes[i]); + keyValues[i] = key; + } + } + } + return new MultiKeyUntyped(keyValues); + } + + private static Object[] GetPropertyArray(EventBean[] eventsPerStream, + ExprEvaluator[] evaluators, + ExprEvaluatorContext context) + { + var keys = new Object[evaluators.Length]; + for (int i = 0; i < keys.Length; i++) + { + keys[i] = evaluators[i].Evaluate(new EvaluateParams(eventsPerStream, true, context)); + } + return keys; + } + + public static MultiKeyUntyped GetMultiKey(EventBean[] eventPerStream, + EventPropertyGetter[] propertyGetters, + int[] keyStreamNums, + Type[] coercionTypes) + { + Object[] keyValues = GetPropertyArray(eventPerStream, propertyGetters, keyStreamNums); + if (coercionTypes == null) + { + return new MultiKeyUntyped(keyValues); + } + + for (int ii = 0; ii < coercionTypes.Length; ii++) + { + var key = keyValues[ii]; + if ((key != null) && (key.GetType() != coercionTypes[ii])) + { + if (key.IsNumber()) + { + key = CoercerFactory.CoerceBoxed(key, coercionTypes[ii]); + keyValues[ii] = key; + } + } + } + + return new MultiKeyUntyped(keyValues); + } + + public static Object Coerce(Object target, Type coercionType) + { + if (coercionType == null) + { + return target; + } + + if ((target != null) && (target.GetType().GetBoxedType() != coercionType)) + { + if (target.IsNumber()) + { + return CoercerFactory.CoerceBoxed(target, coercionType); + } + } + return target; + } + + /// Format the event and return a string representation. + /// is the event to format. + /// string representation of event + public static String PrintEvent(EventBean theEvent) + { + var writer = new StringWriter(); + PrintEvent(writer, theEvent); + return writer.ToString(); + } + + private static void PrintEvent(TextWriter writer, EventBean theEvent) + { + IList properties = theEvent.EventType.PropertyNames; + for (int i = 0; i < properties.Count; i++) + { + string propName = properties[i]; + object property = theEvent.Get(propName); + String printProperty; + if (property == null) + { + printProperty = "null"; + } + else if (property.GetType().IsArray) + { + printProperty = "Array :" + ((Object[])property).Render(); + } + else + { + printProperty = property.ToString(); + } + writer.WriteLine("#" + i + " " + propName + " = " + printProperty); + } + } + + public static void AppendEvent(TextWriter writer, EventBean theEvent) + { + IList properties = theEvent.EventType.PropertyNames; + string delimiter = ""; + for (int i = 0; i < properties.Count; i++) + { + String propName = properties[i]; + Object property = theEvent.Get(propName); + String printProperty; + if (property == null) + { + printProperty = "null"; + } + else if (property.GetType().IsArray) + { + printProperty = "Array :" + ((Object[])property).Render(); + } + else + { + printProperty = property.ToString(); + } + writer.Write(delimiter); + writer.Write(propName); + writer.Write("="); + writer.Write(printProperty); + delimiter = ","; + } + } + + /// Flattens a list of pairs of join result sets. + /// is the list + /// is the consolidate sets + public static UniformPair>> FlattenBatchJoin( + IList>>> joinPostings) + { + if (joinPostings.IsEmpty()) + { + return new UniformPair>>(null, null); + } + + if (joinPostings.Count == 1) + { + return new UniformPair>>(joinPostings[0].First, joinPostings[0].Second); + } + + ISet> newEvents = new LinkedHashSet>(); + ISet> oldEvents = new LinkedHashSet>(); + + foreach (var pair in joinPostings) + { + ISet> newData = pair.First; + ISet> oldData = pair.Second; + + if (newData != null) + { + newEvents.AddAll(newData); + } + if (oldData != null) + { + oldEvents.AddAll(oldData); + } + } + + return new UniformPair>>(newEvents, oldEvents); + } + + /// Expand the array passed in by the single element to add. + /// to expand + /// element to add + /// resized array + public static EventBean[] AddToArray(EventBean[] array, + EventBean eventToAdd) + { + var newArray = new EventBean[array.Length + 1]; + Array.Copy(array, 0, newArray, 0, array.Length); + newArray[newArray.Length - 1] = eventToAdd; + return newArray; + } + + /// Expand the array passed in by the multiple elements to add. + /// to expand + /// elements to add + /// resized array + public static EventBean[] AddToArray(EventBean[] array, ICollection eventsToAdd) + { + var newArray = new EventBean[array.Length + eventsToAdd.Count]; + Array.Copy(array, 0, newArray, 0, array.Length); + + int counter = array.Length; + foreach (EventBean eventToAdd in eventsToAdd) + { + newArray[counter++] = eventToAdd; + } + return newArray; + } + + /// Create a fragment event type. + /// property return type + /// property generic type parameter, or null if none + /// for event types + /// fragment type + public static FragmentEventType CreateNativeFragmentType(Type propertyType, + Type genericType, + EventAdapterService eventAdapterService) + { + var isIndexed = false; + + if (propertyType.IsArray) + { + isIndexed = true; + propertyType = propertyType.GetElementType(); + } + else if (propertyType.IsGenericDictionary()) + { + } + else if (propertyType.IsImplementsInterface(typeof(IEnumerable))) + { + isIndexed = true; + if (genericType == null) + { + return null; + } + propertyType = genericType; + } + + if (!propertyType.IsFragmentableType()) + { + return null; + } + + var type = eventAdapterService.BeanEventTypeFactory.CreateBeanType( + propertyType.FullName, propertyType, false, false, false); + return new FragmentEventType(type, isIndexed, true); + } + + /// Returns the distinct events by properties. + /// to inspect + /// for retrieving properties + /// distinct events + public static EventBean[] GetDistinctByProp(LinkedList events, + EventBeanReader reader) + { + if (events == null || events.IsEmpty()) + { + return new EventBean[0]; + } + if (events.Count < 2) + { + return events.ToArray(); + } + + var set = new LinkedHashSet(); + if (events.First.Value is NaturalEventBean) + { + foreach (EventBean theEvent in events) + { + var inner = ((NaturalEventBean)theEvent).OptionalSynthetic; + var keys = reader.Read(inner); + var pair = new MultiKeyUntypedEventPair(keys, theEvent); + set.Add(pair); + } + } + else + { + foreach (EventBean theEvent in events) + { + Object[] keys = reader.Read(theEvent); + var pair = new MultiKeyUntypedEventPair(keys, theEvent); + set.Add(pair); + } + } + + var result = new EventBean[set.Count]; + var count = 0; + foreach (MultiKeyUntypedEventPair row in set) + { + result[count++] = row.EventBean; + } + return result; + } + + /// Returns the distinct events by properties. + /// to inspect + /// for retrieving properties + /// distinct events + public static EventBean[] GetDistinctByProp(EventBean[] events, + EventBeanReader reader) + { + if ((events == null) || (events.Length < 2)) + { + return events; + } + + var set = new LinkedHashSet(); + if (events[0] is NaturalEventBean) + { + foreach (EventBean theEvent in events) + { + var inner = ((NaturalEventBean)theEvent).OptionalSynthetic; + var keys = reader.Read(inner); + var pair = new MultiKeyUntypedEventPair(keys, theEvent); + set.Add(pair); + } + } + else + { + foreach (EventBean theEvent in events) + { + Object[] keys = reader.Read(theEvent); + var pair = new MultiKeyUntypedEventPair(keys, theEvent); + set.Add(pair); + } + } + + var result = new EventBean[set.Count]; + int count = 0; + foreach (MultiKeyUntypedEventPair row in set) + { + result[count++] = row.EventBean; + } + return result; + } + + public static EventBean[] Denaturalize(EventBean[] naturals) + { + if (naturals == null || naturals.Length == 0) + { + return null; + } + if (!(naturals[0] is NaturalEventBean)) + { + return naturals; + } + if (naturals.Length == 1) + { + return new[] { ((NaturalEventBean)naturals[0]).OptionalSynthetic }; + } + var result = new EventBean[naturals.Length]; + for (int i = 0; i < naturals.Length; i++) + { + result[i] = ((NaturalEventBean)naturals[i]).OptionalSynthetic; + } + return result; + } + + public static bool EventsAreEqualsAllowNull(EventBean first, EventBean second) + { + if (first == null) + { + return second == null; + } + return second != null && Equals(first, second); + } + + public static String Summarize(EventBean theEvent) + { + if (theEvent == null) + { + return "(null)"; + } + StringWriter writer = new StringWriter(); + Summarize(theEvent, writer); + return writer.ToString(); + } + + + public static void Summarize(EventBean theEvent, TextWriter writer) + { + if (theEvent == null) + { + writer.Write("(null)"); + return; + } + writer.Write(theEvent.EventType.Name); + writer.Write("["); + SummarizeUnderlying(theEvent.Underlying, writer); + writer.Write("]"); + } + + public static String SummarizeUnderlying(Object underlying) + { + if (underlying == null) + { + return "(null)"; + } + StringWriter writer = new StringWriter(); + SummarizeUnderlying(underlying, writer); + return writer.ToString(); + } + + public static void SummarizeUnderlying(Object underlying, TextWriter writer) + { + if (underlying.GetType().IsArray) + { + if (underlying is Object[]) + { + var asArray = (Object[]) underlying; + writer.Write(asArray.Render()); + } + else + { + var delimiter = ""; + var asArray = underlying as Array; + + writer.Write("["); + for (int i = 0; i < asArray.Length; i++) + { + writer.Write(delimiter); + delimiter = ","; + + var value = asArray.GetValue(i); + if (value != null) + { + writer.Write(value.ToString()); + } + else + { + writer.Write("(null)"); + } + } + writer.Write("]"); + } + } + else + { + writer.Write(underlying.ToString()); + } + } + + public static String Summarize(EventBean[] events) + { + if (events == null) + { + return "(null)"; + } + if (events.Length == 0) + { + return "(empty)"; + } + + var writer = new StringWriter(); + var delimiter = ""; + for (int i = 0; i < events.Length; i++) + { + writer.Write(delimiter); + writer.Write("event "); + writer.Write(i); + writer.Write(":"); + Summarize(events[i], writer); + delimiter = ", "; + } + return writer.ToString(); + } + + + public static void SafeArrayCopy(EventBean[] eventsPerStream, + EventBean[] eventsLambda) + { + if (eventsPerStream.Length <= eventsLambda.Length) + { + Array.Copy(eventsPerStream, 0, eventsLambda, 0, eventsPerStream.Length); + } + else + { + Array.Copy(eventsPerStream, 0, eventsLambda, 0, eventsLambda.Length); + } + } + + public static T[] GetNewDataNonRemoved(T[] newData, ICollection removedEvents) + { + var filter = false; + foreach (var newItem in newData) + { + if (removedEvents.Contains(newItem)) + { + filter = true; + } + } + + if (!filter) + { + return newData; + } + + if (newData.Length == 1) + { + return null; + } + + var events = new LinkedList(); + foreach (var newItem in newData) + { + if (!removedEvents.Contains(newItem)) + { + events.AddLast(newItem); + } + } + + if (events.IsEmpty()) + { + return null; + } + + return events.ToArray(); + } + + public static T[] GetNewDataNonRemoved(T[] newData, ICollection removedEvents, T[][] 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 LinkedList(); + foreach (var newItem in newData) + { + if (!removedEvents.Contains(newItem)) + { + var pass = FindEvent(newItem, newEventsPerView); + if (pass) + { + events.AddLast(newItem); + } + } + } + if (events.IsEmpty()) + { + return null; + } + return events.ToArray(); + } + + /// + /// Renders a map of elements, in which elements can be events or event arrays interspersed with other objects, + /// + /// The map. + /// + /// comma-separated list of map entry name-value pairs + /// + public static String ToString(IDictionary map) + { + if (map == null) + { + return "null"; + } + if (map.IsEmpty()) + { + return ""; + } + var buf = new StringBuilder(); + var delimiter = ""; + foreach (var entry in map) + { + buf.Append(delimiter); + buf.Append(entry.Key); + buf.Append("="); + if (entry.Value is EventBean) { + buf.Append(Summarize((EventBean) entry.Value)); + } + else if (entry.Value is EventBean[]) { + buf.Append(Summarize((EventBean[]) entry.Value)); + } + else if (entry.Value == null) { + buf.Append("null"); + } + else { + buf.Append(entry.Value); + } + delimiter = ", "; + } + return buf.ToString(); + } + + public static void AddToCollection(EventBean[] toAdd, ICollection events) + { + if (toAdd == null) + { + return; + } + events.AddAll(toAdd); + } + + public static void AddToCollection(ISet> toAdd, ICollection> events) + { + if (toAdd == null) + { + return; + } + events.AddAll(toAdd); + } + + public static EventBean[] ToArrayNullIfEmpty(ICollection events) + { + if (events == null || events.IsEmpty()) + { + return null; + } + return events.ToArray(); + } + + public static ISet> ToLinkedHashSetNullIfEmpty(ICollection> events) + { + if (events == null || events.IsEmpty()) + { + return null; + } + return new LinkedHashSet>(events); + } + + public static ISet> ToSingletonSetIfNotNull(MultiKey row) + { + if (row == null) + { + return null; + } + return Collections.SingletonSet(row); + } + + public static MultiKey GetLastInSet(ISet> events) + { + if (events.IsEmpty()) { + return null; + } + int count = 0; + foreach (MultiKey row in events) { + count++; + if (count == events.Count) { + return row; + } + } + throw new IllegalStateException("Cannot get last on empty collection"); + } + + public static EventBean[] ToArrayIfNotNull(EventBean optionalEvent) + { + if (optionalEvent == null) + { + return null; + } + return new EventBean[] { optionalEvent }; + } + + public static bool CompareEventReferences(EventBean[] firstNonNull, EventBean[] secondNonNull) + { + if (firstNonNull.Length != secondNonNull.Length) + { + return false; + } + for (int i = 0; i < firstNonNull.Length; i++) + { + if (firstNonNull[i] != secondNonNull[i]) + { + return false; + } + } + return true; + } + + public static T[] CopyArray(T[] events) + { + T[] copy = new T[events.Length]; + Array.Copy(events, 0, copy, 0, copy.Length); + return copy; + } + + private static bool FindEvent(T theEvent, T[][] eventsPerView) + { + for (int i = 0; i < eventsPerView.Length; i++) + { + if (eventsPerView[i] == null) + { + continue; + } + for (int j = 0; j < eventsPerView[i].Length; j++) + { + if (Equals(eventsPerView[i][j], theEvent)) + { + return true; + } + } + } + return false; + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/events/EventBeanWriter.cs b/NEsper.Core/NEsper.Core/events/EventBeanWriter.cs new file mode 100755 index 000000000..177e30103 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/EventBeanWriter.cs @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events +{ + /// + /// Interface for writing a set of event properties to an event. + /// + public interface EventBeanWriter + { + /// + /// Write property values to the event. + /// + /// to write + /// to write to + void Write(Object[] values, EventBean theEvent); + } +} diff --git a/NEsper.Core/NEsper.Core/events/EventPropertyGetterAndIndexed.cs b/NEsper.Core/NEsper.Core/events/EventPropertyGetterAndIndexed.cs new file mode 100755 index 000000000..c7e058ca4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/EventPropertyGetterAndIndexed.cs @@ -0,0 +1,18 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; + +namespace com.espertech.esper.events +{ + public interface EventPropertyGetterAndIndexed + : EventPropertyGetter + , EventPropertyGetterIndexed + { + } +} diff --git a/NEsper.Core/NEsper.Core/events/EventPropertyGetterAndMapped.cs b/NEsper.Core/NEsper.Core/events/EventPropertyGetterAndMapped.cs new file mode 100755 index 000000000..48f5af928 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/EventPropertyGetterAndMapped.cs @@ -0,0 +1,18 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; + +namespace com.espertech.esper.events +{ + public interface EventPropertyGetterAndMapped + : EventPropertyGetter + , EventPropertyGetterMapped + { + } +} diff --git a/NEsper.Core/NEsper.Core/events/EventPropertyType.cs b/NEsper.Core/NEsper.Core/events/EventPropertyType.cs new file mode 100755 index 000000000..9eb0d3b64 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/EventPropertyType.cs @@ -0,0 +1,22 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +namespace com.espertech.esper.events +{ + /// Enumeration of property types. + public enum EventPropertyType + { + /// Simple property. + SIMPLE, + /// Indexed property. + INDEXED, + /// Mapped property. + MAPPED + } +} diff --git a/NEsper.Core/NEsper.Core/events/EventPropertyWriter.cs b/NEsper.Core/NEsper.Core/events/EventPropertyWriter.cs new file mode 100755 index 000000000..f5e38b7fd --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/EventPropertyWriter.cs @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events +{ + /// + /// Writer for a single property value to an event. + /// + public interface EventPropertyWriter + { + /// + /// Value to write to a property. + /// + /// value to write + /// property to write to + void Write(Object value, EventBean target); + } + + public class ProxyEventPropertyWriter : EventPropertyWriter + { + public Action ProcWrite { get; set; } + + public ProxyEventPropertyWriter() + { + } + + public ProxyEventPropertyWriter(Action writeFunction) + { + ProcWrite = writeFunction; + } + + /// + /// Value to write to a property. + /// + /// value to write + /// property to write to + public void Write(Object value, EventBean target) + { + ProcWrite(value, target); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/EventSenderAvro.cs b/NEsper.Core/NEsper.Core/events/EventSenderAvro.cs new file mode 100755 index 000000000..d44e2678b --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/EventSenderAvro.cs @@ -0,0 +1,74 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.core.service; +using com.espertech.esper.core.thread; + +namespace com.espertech.esper.events +{ + /// + /// Event sender for avro-backed events. + /// + /// Allows sending only event objects of type GenericRecord, does not check contents. Any other event object generates an error. + /// + /// + public class EventSenderAvro : EventSender + { + private readonly EPRuntimeEventSender _runtimeEventSender; + private readonly EventAdapterService _eventAdapterService; + private readonly EventType _eventType; + private readonly ThreadingService _threadingService; + + /// + /// Ctor. + /// + /// for processing events + /// the event type + /// for inbound threading + /// for event bean creation + public EventSenderAvro( + EPRuntimeEventSender runtimeEventSender, + EventType eventType, + EventAdapterService eventAdapterService, + ThreadingService threadingService) + { + _runtimeEventSender = runtimeEventSender; + _eventType = eventType; + _threadingService = threadingService; + _eventAdapterService = eventAdapterService; + } + + public void SendEvent(Object theEvent) + { + EventBean eventBean = _eventAdapterService.AdapterForTypedAvro(theEvent, _eventType); + + if ((ThreadingOption.IsThreadingEnabled) && (_threadingService.IsInboundThreading)) + { + _threadingService.SubmitInbound(new InboundUnitSendWrapped(eventBean, _runtimeEventSender).Run); + } + else + { + _runtimeEventSender.ProcessWrappedEvent(eventBean); + } + } + + public void Route(Object theEvent) + { + if (!(theEvent.GetType().IsArray)) + { + throw new EPException( + "Unexpected event object of type " + theEvent.GetType().FullName + ", expected Object[]"); + } + EventBean eventBean = _eventAdapterService.AdapterForTypedAvro(theEvent, _eventType); + _runtimeEventSender.RouteEventBean(eventBean); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/events/EventSenderBean.cs b/NEsper.Core/NEsper.Core/events/EventSenderBean.cs new file mode 100755 index 000000000..a8b8276bc --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/EventSenderBean.cs @@ -0,0 +1,127 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.client.time; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.compat.threading; +using com.espertech.esper.core.service; +using com.espertech.esper.core.thread; +using com.espertech.esper.events.bean; +using com.espertech.esper.util; + +namespace com.espertech.esper.events +{ + /// + /// Event sender for object events. + /// + /// Allows sending only event objects of the underlying type matching the event type, or + /// implementing the interface or extending the type. Any other event object generates an error. + /// + /// + public class EventSenderBean : EventSender + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly EPRuntimeEventSender _runtime; + private readonly BeanEventType _beanEventType; + private readonly EventAdapterService _eventAdapterService; + private readonly ISet _compatibleClasses; + private readonly ThreadingService _threadingService; + private readonly ILockable _iLock; + + /// + /// Ctor. + /// + /// for processing events + /// the event type + /// factory for event beans and event types + /// for inbound threading + public EventSenderBean( + EPRuntimeEventSender runtime, + BeanEventType beanEventType, + EventAdapterService eventAdapterService, + ThreadingService threadingService) + { + _iLock = LockManager.CreateLock(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + _runtime = runtime; + _beanEventType = beanEventType; + _eventAdapterService = eventAdapterService; + _compatibleClasses = new HashSet(); + _threadingService = threadingService; + } + + public void SendEvent(Object theEvent) + { + if (theEvent == null) + { + throw new ArgumentNullException("theEvent", "No event object provided to sendEvent method"); + } + + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled)) + { + if ((!(theEvent is CurrentTimeEvent)) || (ExecutionPathDebugLog.IsTimerDebugEnabled)) + { + Log.Debug(".sendEvent Processing event " + theEvent); + } + } + + EventBean eventBean = GetEventBean(theEvent); + + // Process event + if ((ThreadingOption.IsThreadingEnabled) && (_threadingService.IsInboundThreading)) + { + _threadingService.SubmitInbound(new InboundUnitSendWrapped(eventBean, _runtime).Run); + } + else + { + _runtime.ProcessWrappedEvent(eventBean); + } + } + + public void Route(Object theEvent) + { + EventBean eventBean = GetEventBean(theEvent); + _runtime.RouteEventBean(eventBean); + } + + private EventBean GetEventBean(Object theEvent) + { + // type check + if (theEvent.GetType() != _beanEventType.UnderlyingType) + { + using (_iLock.Acquire()) + { + if (!_compatibleClasses.Contains(theEvent.GetType())) + { + if (TypeHelper.IsSubclassOrImplementsInterface( + theEvent.GetType(), _beanEventType.UnderlyingType)) + { + _compatibleClasses.Add(theEvent.GetType()); + } + else + { + throw new EPException( + "Event object of type " + theEvent.GetType().FullName + + " does not equal, extend or implement the type " + _beanEventType.UnderlyingType.FullName + + " of event type '" + _beanEventType.Name + "'"); + } + } + } + } + + return _eventAdapterService.AdapterForTypedObject(theEvent, _beanEventType); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/events/EventSenderImpl.cs b/NEsper.Core/NEsper.Core/events/EventSenderImpl.cs new file mode 100755 index 000000000..2955e73ee --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/EventSenderImpl.cs @@ -0,0 +1,95 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.service; +using com.espertech.esper.core.thread; + +namespace com.espertech.esper.events +{ + /// + /// Event sender for use with plug-in event representations. + /// + /// The implementation asks a list of event bean factoryies originating from + /// plug-in event representations to each reflect on the event and generate an event bean. + /// The first one to return an event bean wins. + /// + public class EventSenderImpl : EventSender + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly List _handlingFactories; + private readonly EPRuntimeEventSender _epRuntime; + private readonly ThreadingService _threadingService; + + /// + /// Ctor. + /// + /// list of factories + /// the runtime to use to process the event + /// for inbound threading + public EventSenderImpl(List handlingFactories, EPRuntimeEventSender epRuntime, ThreadingService threadingService) + { + _handlingFactories = handlingFactories; + _epRuntime = epRuntime; + _threadingService = threadingService; + } + + public void SendEvent(Object theEvent) + { + SendIn(theEvent, false); + } + + public void Route(Object theEvent) + { + SendIn(theEvent, true); + } + + private void SendIn(Object theEvent, bool isRoute) + { + // Ask each factory in turn to take care of it + foreach (EventSenderURIDesc entry in _handlingFactories) + { + EventBean eventBean = null; + + try + { + eventBean = entry.BeanFactory(theEvent, entry.ResolutionURI); + } + catch (Exception ex) + { + Log.Warn("Unexpected exception thrown by plug-in event bean factory '" + entry.BeanFactory + "' processing event " + theEvent, ex); + } + + if (eventBean != null) + { + if (isRoute) + { + _epRuntime.RouteEventBean(eventBean); + } + else + { + if ((ThreadingOption.IsThreadingEnabledValue) && (_threadingService.IsInboundThreading)) + { + _threadingService.SubmitInbound(() => _epRuntime.ProcessWrappedEvent(eventBean)); + } + else + { + _epRuntime.ProcessWrappedEvent(eventBean); + } + } + return; + } + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/EventSenderMap.cs b/NEsper.Core/NEsper.Core/events/EventSenderMap.cs new file mode 100755 index 000000000..8a1d2d2f5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/EventSenderMap.cs @@ -0,0 +1,82 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.core.service; +using com.espertech.esper.core.thread; +using com.espertech.esper.events.map; + +using DataMap = System.Collections.Generic.IDictionary; + +namespace com.espertech.esper.events +{ + /// + /// Event sender for map-backed events. + /// + /// Allows sending only event objects of type map, does not check map contents. Any + /// other event object generates an error. + /// + public class EventSenderMap : EventSender + { + private readonly EventAdapterService _eventAdapterService; + private readonly MapEventType _mapEventType; + private readonly EPRuntimeEventSender _runtimeEventSender; + private readonly ThreadingService _threadingService; + + /// + /// Ctor. + /// + /// for processing events + /// the event type + /// for inbound threading + /// for event bean creation + public EventSenderMap(EPRuntimeEventSender runtimeEventSender, MapEventType mapEventType, + EventAdapterService eventAdapterService, ThreadingService threadingService) + { + _runtimeEventSender = runtimeEventSender; + _mapEventType = mapEventType; + _threadingService = threadingService; + _eventAdapterService = eventAdapterService; + } + + public void SendEvent(Object theEvent) + { + if (!(theEvent is DataMap)) + { + throw new EPException("Unexpected event object of type " + theEvent.GetType().FullName + ", expected " + + typeof(DataMap).FullName); + } + + var map = (DataMap)theEvent; + EventBean mapEvent = _eventAdapterService.AdapterForTypedMap(map, _mapEventType); + + if ((ThreadingOption.IsThreadingEnabledValue) && (_threadingService.IsInboundThreading)) + { + _threadingService.SubmitInbound(() => _runtimeEventSender.ProcessWrappedEvent(mapEvent)); + } + else + { + _runtimeEventSender.ProcessWrappedEvent(mapEvent); + } + } + + public void Route(Object theEvent) + { + if (!(theEvent is DataMap)) + { + throw new EPException("Unexpected event object of type " + theEvent.GetType().FullName + ", expected " + + typeof(DataMap).FullName); + } + var map = (DataMap)theEvent; + EventBean mapEvent = _eventAdapterService.AdapterForTypedMap(map, _mapEventType); + _runtimeEventSender.RouteEventBean(mapEvent); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/EventSenderObjectArray.cs b/NEsper.Core/NEsper.Core/events/EventSenderObjectArray.cs new file mode 100755 index 000000000..1d3027614 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/EventSenderObjectArray.cs @@ -0,0 +1,82 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using com.espertech.esper.client; +using com.espertech.esper.core.service; +using com.espertech.esper.core.thread; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.events +{ + /// + /// Event sender for map-backed events. + /// + /// Allows sending only event objects of type map, does not check map contents. + /// Any other event object generates an error. + /// + /// + public class EventSenderObjectArray : EventSender + { + private readonly EventAdapterService _eventAdapterService; + private readonly ObjectArrayEventType _objectArrayEventType; + private readonly EPRuntimeEventSender _runtimeEventSender; + private readonly ThreadingService _threadingService; + + /// Ctor. + /// for processing events + /// the event type + /// for inbound threading + /// for event bean creation + public EventSenderObjectArray(EPRuntimeEventSender runtimeEventSender, + ObjectArrayEventType objectArrayEventType, + EventAdapterService eventAdapterService, + ThreadingService threadingService) + { + _runtimeEventSender = runtimeEventSender; + _objectArrayEventType = objectArrayEventType; + _threadingService = threadingService; + _eventAdapterService = eventAdapterService; + } + + #region EventSender Members + + public void SendEvent(Object theEvent) + { + if (!(theEvent.GetType().IsArray)) + { + throw new EPException("Unexpected event object of type " + theEvent.GetType().FullName + ", expected Object[]"); + } + + var arr = (Object[]) theEvent; + EventBean objectArrayEvent = _eventAdapterService.AdapterForTypedObjectArray(arr, _objectArrayEventType); + + if ((ThreadingOption.IsThreadingEnabledValue) && (_threadingService.IsInboundThreading)) + { + _threadingService.SubmitInbound(new InboundUnitSendWrapped(objectArrayEvent, _runtimeEventSender).Run); + } + else + { + _runtimeEventSender.ProcessWrappedEvent(objectArrayEvent); + } + } + + public void Route(Object theEvent) + { + if (!(theEvent.GetType().IsArray)) + { + throw new EPException("Unexpected event object of type " + theEvent.GetType().Name + ", expected Object[]"); + } + var arr = (Object[]) theEvent; + EventBean objectArrayEvent = _eventAdapterService.AdapterForTypedObjectArray(arr, _objectArrayEventType); + _runtimeEventSender.RouteEventBean(objectArrayEvent); + } + + #endregion + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/events/EventSenderURIDesc.cs b/NEsper.Core/NEsper.Core/events/EventSenderURIDesc.cs new file mode 100755 index 000000000..690be6856 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/EventSenderURIDesc.cs @@ -0,0 +1,56 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.plugin; + +namespace com.espertech.esper.events +{ + /// + /// Descriptor for URI-based event sender for plug-in event representations. + /// + public class EventSenderURIDesc + { + private readonly PlugInEventBeanFactory _beanFactory; + private readonly Uri _resolutionURI; + private readonly Uri _representationURI; + + /// Ctor. + /// factory for events + /// URI use for resolution + /// URI of event representation + public EventSenderURIDesc(PlugInEventBeanFactory beanFactory, Uri resolutionURI, Uri representationURI) + { + _beanFactory = beanFactory; + _resolutionURI = resolutionURI; + _representationURI = representationURI; + } + + /// URI used for resolution. + /// resolution URI + public Uri ResolutionURI + { + get { return _resolutionURI; } + } + + /// URI of event representation. + /// URI + public Uri RepresentationURI + { + get { return _representationURI; } + } + + /// Event wrapper for event objects. + /// factory for events + public PlugInEventBeanFactory BeanFactory + { + get { return _beanFactory; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/EventSenderXMLDOM.cs b/NEsper.Core/NEsper.Core/events/EventSenderXMLDOM.cs new file mode 100755 index 000000000..0e90e5979 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/EventSenderXMLDOM.cs @@ -0,0 +1,108 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Xml; + +using com.espertech.esper.client; +using com.espertech.esper.core.service; +using com.espertech.esper.core.thread; +using com.espertech.esper.events.xml; + +namespace com.espertech.esper.events +{ + /// + /// Event sender for XML DOM-backed events. + /// + /// Allows sending only event objects of type Node or Document, does check the root name + /// of the XML document which must match the event type root name as configured. Any other + /// event object generates an error. + /// + public class EventSenderXMLDOM : EventSender + { + private readonly EPRuntimeEventSender _runtimeEventSender; + private readonly BaseXMLEventType _baseXmlEventType; + private readonly bool _validateRootElement; + private readonly EventAdapterService _eventAdapterService; + private readonly ThreadingService _threadingService; + + /// + /// Ctor. + /// + /// for processing events + /// the event type + /// for event bean creation + /// for inbound threading + public EventSenderXMLDOM(EPRuntimeEventSender runtimeEventSender, BaseXMLEventType baseXMLEventType, EventAdapterService eventAdapterService, ThreadingService threadingService) + { + _runtimeEventSender = runtimeEventSender; + _baseXmlEventType = baseXMLEventType; + _validateRootElement = baseXMLEventType.ConfigurationEventTypeXMLDOM.IsEventSenderValidatesRoot; + _eventAdapterService = eventAdapterService; + _threadingService = threadingService; + } + + public void SendEvent(Object node) + { + SendEvent(node, false); + } + + public void Route(Object node) + { + SendEvent(node, true); + } + + private void SendEvent(Object node, bool isRoute) + { + XmlNode namedNode; + if (node is XmlDocument) + { + namedNode = ((XmlDocument) node).DocumentElement; + } + else if (node is XmlElement) + { + namedNode = (XmlElement) node; + } + else + { + throw new EPException("Unexpected event object type '" + node.GetType().FullName + "' encountered, please supply a XmlDocument or XmlElement node"); + } + + if (_validateRootElement) + { + var theNodeName = namedNode.LocalName; + if (theNodeName == null) + { + theNodeName = namedNode.Name; + } + + if (!theNodeName.Equals(_baseXmlEventType.RootElementName)) + { + throw new EPException("Unexpected root element name '" + theNodeName + "' encountered, expected a root element name of '" + _baseXmlEventType.RootElementName + "'"); + } + } + + EventBean theEvent = _eventAdapterService.AdapterForTypedDOM(namedNode, _baseXmlEventType); + if (isRoute) + { + _runtimeEventSender.RouteEventBean(theEvent); + } + else + { + if ((ThreadingOption.IsThreadingEnabledValue) && (_threadingService.IsInboundThreading)) + { + _threadingService.SubmitInbound(new InboundUnitSendWrapped(theEvent, _runtimeEventSender).Run); + } + else + { + _runtimeEventSender.ProcessWrappedEvent(theEvent); + } + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/EventTypeIdGenerator.cs b/NEsper.Core/NEsper.Core/events/EventTypeIdGenerator.cs new file mode 100755 index 000000000..990f10804 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/EventTypeIdGenerator.cs @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.events.bean; + +namespace com.espertech.esper.events +{ + public interface EventTypeIdGenerator + { + int GetTypeId(String eventTypeName); + void AssignedType(String name, BeanEventType eventType); + } +} diff --git a/NEsper.Core/NEsper.Core/events/EventTypeIdGeneratorContext.cs b/NEsper.Core/NEsper.Core/events/EventTypeIdGeneratorContext.cs new file mode 100755 index 000000000..f31ad4b0f --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/EventTypeIdGeneratorContext.cs @@ -0,0 +1,22 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.events +{ + public class EventTypeIdGeneratorContext + { + public EventTypeIdGeneratorContext(String engineURI) + { + EngineURI = engineURI; + } + + public string EngineURI { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/events/EventTypeIdGeneratorFactory.cs b/NEsper.Core/NEsper.Core/events/EventTypeIdGeneratorFactory.cs new file mode 100755 index 000000000..5330696df --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/EventTypeIdGeneratorFactory.cs @@ -0,0 +1,15 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.events +{ + public interface EventTypeIdGeneratorFactory + { + EventTypeIdGenerator Create(EventTypeIdGeneratorContext context); + } +} diff --git a/NEsper.Core/NEsper.Core/events/EventTypeIdGeneratorImpl.cs b/NEsper.Core/NEsper.Core/events/EventTypeIdGeneratorImpl.cs new file mode 100755 index 000000000..70d47c8b0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/EventTypeIdGeneratorImpl.cs @@ -0,0 +1,30 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Threading; + +using com.espertech.esper.events.bean; + +namespace com.espertech.esper.events +{ + public class EventTypeIdGeneratorImpl : EventTypeIdGenerator + { + private int _currentEventTypeId; + + public int GetTypeId(String typeName) + { + return Interlocked.Increment(ref _currentEventTypeId); + } + + public void AssignedType(String name, BeanEventType eventType) + { + // no op required + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/EventTypeMetadata.cs b/NEsper.Core/NEsper.Core/events/EventTypeMetadata.cs new file mode 100755 index 000000000..5e7e5256e --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/EventTypeMetadata.cs @@ -0,0 +1,348 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.events +{ + /// + /// Provides metadata for event types. + /// + public class EventTypeMetadata + { + /// + /// Ctor. + /// + /// the primary name by which the type became known. + /// a list of additional names for the type, such as fully-qualified class name + /// type of the type + /// true if configured by the application + /// type of application class or null if not an application type + /// true for types that accept any property name as a valid property (unchecked type) + /// preconfigured + /// preconfigured via static config + protected EventTypeMetadata( + string primaryName, + ICollection secondaryNames, + TypeClass typeClass, + bool isApplicationPreConfiguredStatic, + bool applicationPreConfigured, + bool applicationConfigured, + ApplicationType? applicationType, + bool isPropertyAgnostic) + { + PublicName = primaryName; + PrimaryName = primaryName; + OptionalSecondaryNames = secondaryNames; + TypeClass = typeClass; + IsApplicationConfigured = applicationConfigured; + IsApplicationPreConfigured = applicationPreConfigured; + IsApplicationPreConfiguredStatic = isApplicationPreConfiguredStatic; + OptionalApplicationType = applicationType; + IsPropertyAgnostic = isPropertyAgnostic; + } + + /// + /// Factory for a value-add type. + /// + /// type name + /// type of type + /// instance + public static EventTypeMetadata CreateValueAdd(string name, TypeClass typeClass) + { + if ((typeClass != TypeClass.VARIANT) && (typeClass != TypeClass.REVISION)) + { + throw new ArgumentException("Type class " + typeClass + " invalid"); + } + return new EventTypeMetadata(name, null, typeClass, true, true, true, null, false); + } + + /// + /// Factory for a bean type. + /// + /// type name + /// java class + /// whether the class was made known or is discovered + /// type of type + /// preconfigured + /// preconfigured via static config + /// instance + public static EventTypeMetadata CreateBeanType( + string name, + Type clazz, + bool isPreConfiguredStatic, + bool isPreConfigured, + bool isConfigured, + TypeClass typeClass) + { + ISet secondaryNames = null; + if (name == null) + { + name = clazz.Name; + } + else + { + if (!name.Equals(clazz.Name)) + { + secondaryNames = new LinkedHashSet(); + secondaryNames.Add(clazz.Name); + } + } + + return new EventTypeMetadata( + name, secondaryNames, typeClass, isPreConfiguredStatic, isPreConfigured, isConfigured, + ApplicationType.CLASS, false); + } + + /// + /// Factory for a XML type. + /// + /// type name + /// true for types that accept any property name as a valid property (unchecked type) + /// preconfigured via static config + /// instance + public static EventTypeMetadata CreateXMLType(string name, bool isPreconfiguredStatic, bool isPropertyAgnostic) + { + return new EventTypeMetadata( + name, null, TypeClass.APPLICATION, isPreconfiguredStatic, true, true, ApplicationType.XML, + isPropertyAgnostic); + } + + /// + /// Factory for an anonymous type. + /// + /// what the type is associated with + /// type INFO + /// instance + public static EventTypeMetadata CreateAnonymous(string associationName, ApplicationType applicationType) + { + return new EventTypeMetadata( + associationName, null, TypeClass.ANONYMOUS, false, false, false, applicationType, false); + } + + /// + /// Factory for an table type. + /// + /// what the type is associated with + /// instance + public static EventTypeMetadata CreateTable(string tableName) + { + return new EventTypeMetadata(tableName, null, TypeClass.TABLE, false, false, false, null, false); + } + + /// + /// Factory for a wrapper type. + /// + /// insert-into of create-window name + /// true for named window + /// true for insert-into + /// true for types that accept any property name as a valid property (unchecked type) + /// instance + public static EventTypeMetadata CreateWrapper( + string eventTypeName, + bool namedWindow, + bool insertInto, + bool isPropertyAgnostic) + { + TypeClass typeClass; + if (namedWindow) + { + typeClass = TypeClass.NAMED_WINDOW; + } + else if (insertInto) + { + typeClass = TypeClass.STREAM; + } + else + { + throw new IllegalStateException("Unknown Wrapper type, cannot create metadata"); + } + return new EventTypeMetadata(eventTypeName, null, typeClass, false, false, false, null, isPropertyAgnostic); + } + + /// + /// Factory for a map type. + /// + /// insert-into of create-window name + /// true for named window + /// true for insert-into + /// whether the made known or is discovered + /// preconfigured + /// preconfigured via static config + /// type + /// instance + public static EventTypeMetadata CreateNonPonoApplicationType( + ApplicationType providedType, + string name, + bool preconfiguredStatic, + bool preconfigured, + bool configured, + bool namedWindow, + bool insertInto) + { + TypeClass typeClass; + ApplicationType? applicationType = null; + if (configured) + { + typeClass = TypeClass.APPLICATION; + applicationType = providedType; + } + else if (namedWindow) + { + typeClass = TypeClass.NAMED_WINDOW; + } + else if (insertInto) + { + typeClass = TypeClass.STREAM; + } + else + { + typeClass = TypeClass.ANONYMOUS; + } + return new EventTypeMetadata( + name, null, typeClass, preconfiguredStatic, preconfigured, configured, applicationType, false); + } + + /// + /// Returns the name. + /// + /// name + public string PrimaryName { get; private set; } + + /// + /// Returns second names or null if none found. + /// + /// further names + public ICollection OptionalSecondaryNames { get; private set; } + + /// + /// Returns the type of the type. + /// + /// meta type + public TypeClass TypeClass { get; private set; } + + /// + /// Returns true if the type originates in a configuration. + /// + /// indicator whether configured or not + public bool IsApplicationConfigured { get; private set; } + + /// + /// The type of the application event type or null if not an application event type. + /// + /// application event type + public ApplicationType? OptionalApplicationType { get; private set; } + + /// + /// Returns the name provided through #EventType.getName. + /// + /// name or null if no public name + public string PublicName { get; private set; } + + /// + /// Returns true for types that accept any property name as a valid property (unchecked type). + /// + /// indicator whether type is unchecked (agnostic to property) + public bool IsPropertyAgnostic { get; private set; } + + /// + /// Returns true to indicate the type is pre-configured, i.e. added through static or runtime configuration. + /// + /// indicator + public bool IsApplicationPreConfigured { get; private set; } + + /// + /// Returns true to indicate the type is pre-configured, i.e. added through static configuration but not runtime + /// configuation. + /// + /// indicator + public bool IsApplicationPreConfiguredStatic { get; private set; } + } + + /// Metatype. + public enum TypeClass + { + /// + /// A type that represents the information made available via insert-into. + /// + STREAM, + + /// A revision event type. + REVISION, + + /// A variant stream event type. + VARIANT, + + /// + /// An application-defined event type such as legacy object, XML or Map. + /// + APPLICATION, + + /// A type representing a named window. + NAMED_WINDOW, + + /// A type representing a table. + TABLE, + + /// An anonymous event type. + ANONYMOUS + } + + public static class TypeClassExtensions + { + public static bool IsPublic(this TypeClass typeClass) + { + switch (typeClass) + { + case TypeClass.STREAM: + return (true); + case TypeClass.REVISION: + return (true); + case TypeClass.VARIANT: + return (true); + case TypeClass.APPLICATION: + return (true); + case TypeClass.NAMED_WINDOW: + return (true); + case TypeClass.ANONYMOUS: + return (false); + case TypeClass.TABLE: + return (false); + } + + throw new ArgumentException(); + } + } + + + /// Application type. + public enum ApplicationType + { + /// Xml type. + XML, + + /// Map type. + MAP, + + /// Object Array type. + OBJECTARR, + + /// Type type. + CLASS, + + /// Avro type. + AVRO, + + /// Wrapper type. + WRAPPER + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/events/EventTypeNestableGetterFactory.cs b/NEsper.Core/NEsper.Core/events/EventTypeNestableGetterFactory.cs new file mode 100755 index 000000000..5ad9a7765 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/EventTypeNestableGetterFactory.cs @@ -0,0 +1,46 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.events.bean; +using com.espertech.esper.events.map; +using com.espertech.esper.events.property; + +namespace com.espertech.esper.events +{ + public interface EventTypeNestableGetterFactory + { + EventPropertyGetter GetPropertyProvidedGetter(IDictionary nestableTypes, String propertyName, Property prop, EventAdapterService eventAdapterService); + EventPropertyGetterMapped GetPropertyProvidedGetterMap(IDictionary nestableTypes, String mappedPropertyName, MappedProperty mappedProperty, EventAdapterService eventAdapterService); + EventPropertyGetterIndexed GetPropertyProvidedGetterIndexed(IDictionary nestableTypes, String indexedPropertyName, IndexedProperty indexedProperty, EventAdapterService eventAdapterService); + + EventPropertyGetter GetGetterProperty(String name, BeanEventType nativeFragmentType, EventAdapterService eventAdapterService); + EventPropertyGetter GetGetterEventBean(String name); + EventPropertyGetter GetGetterEventBeanArray(String name, EventType eventType); + EventPropertyGetter GetGetterBeanNested(String name, EventType eventType, EventAdapterService eventAdapterService); + EventPropertyGetter GetGetterBeanNestedArray(String name, EventType eventType, EventAdapterService eventAdapterService); + EventPropertyGetter GetGetterIndexedEventBean(String propertyNameAtomic, int index); + + EventPropertyGetter GetGetterIndexedUnderlyingArray(String propertyNameAtomic, int index, EventAdapterService eventAdapterService, EventType innerType); + EventPropertyGetter GetGetterIndexedPONO(String propertyNameAtomic, int index, EventAdapterService eventAdapterService, Type componentType); + EventPropertyGetter GetGetterMappedProperty(String propertyNameAtomic, String key); + + EventPropertyGetter GetGetterNestedEntryBeanArray(String propertyNameAtomic, int index, EventPropertyGetter getter, EventType innerType, EventAdapterService eventAdapterService); + EventPropertyGetter GetGetterIndexedEntryEventBeanArrayElement(String propertyNameAtomic, int index, EventPropertyGetter nestedGetter); + EventPropertyGetter GetGetterIndexedEntryPONO(String propertyNameAtomic, int index, BeanEventPropertyGetter nestedGetter, EventAdapterService eventAdapterService, Type propertyTypeGetter); + EventPropertyGetter GetGetterNestedMapProp(String propertyName, MapEventPropertyGetter getterNestedMap); + EventPropertyGetter GetGetterNestedPONOProp(String propertyName, BeanEventPropertyGetter nestedGetter, EventAdapterService eventAdapterService, Type nTypeReturnType, Type nestedComponentType); + EventPropertyGetter GetGetterNestedEventBean(String propertyName, EventPropertyGetter nestedGetter); + + EventPropertyGetter GetGetterNestedPropertyProvidedGetterDynamic(IDictionary nestableTypes, string propertyName, EventPropertyGetter nestedGetter, EventAdapterService eventAdapterService); + EventPropertyGetter GetGetterNestedEntryBean(String propertyName, EventPropertyGetter innerGetter, EventType innerType, EventAdapterService eventAdapterService); + } +} diff --git a/NEsper.Core/NEsper.Core/events/EventTypeSPI.cs b/NEsper.Core/NEsper.Core/events/EventTypeSPI.cs new file mode 100755 index 000000000..458f17422 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/EventTypeSPI.cs @@ -0,0 +1,56 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; + +namespace com.espertech.esper.events +{ + /// + /// Service provider interface for internal use for event types. + /// + public interface EventTypeSPI : EventType + { + /// + /// Returns the type metadata. + /// + /// + /// type metadata + /// + EventTypeMetadata Metadata { get; } + + /// Return a writer for writing a single property value. + /// to write to + /// null or writer if writable + EventPropertyWriter GetWriter(string propertyName); + + /// Returns the writable properties. + /// properties that can be written + EventPropertyDescriptor[] WriteableProperties { get; } + + /// Returns the descriptor for a writable property. + /// to Get descriptor for + /// descriptor + EventPropertyDescriptor GetWritableProperty(string propertyName); + + /// Returns the copy method, considering only the attached properties for a write operation onto the copy + /// to write after copy + /// copy method + EventBeanCopyMethod GetCopyMethod(string[] properties); + + /// Returns the write for writing a set of properties. + /// to write + /// writer + EventBeanWriter GetWriter(string[] properties); + + /// Returns a reader for reading all properties of an event. This is completely optional and need only be implemented for performance. + /// reader + EventBeanReader Reader { get; } + + bool EqualsCompareType(EventType eventType); + } +} diff --git a/NEsper.Core/NEsper.Core/events/EventTypeUtility.cs b/NEsper.Core/NEsper.Core/events/EventTypeUtility.cs new file mode 100755 index 000000000..18ddbd7cf --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/EventTypeUtility.cs @@ -0,0 +1,1624 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.client.util; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.parse; +using com.espertech.esper.epl.spec; +using com.espertech.esper.events.arr; +using com.espertech.esper.events.avro; +using com.espertech.esper.events.bean; +using com.espertech.esper.events.map; +using com.espertech.esper.events.property; +using com.espertech.esper.util; + +namespace com.espertech.esper.events +{ + public class EventTypeUtility + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public static EventType RequireEventType( + string aspectCamel, + string aspectName, + EventAdapterService eventAdapterService, + string optionalEventTypeName) + { + if (optionalEventTypeName == null) + { + throw new ExprValidationException( + aspectCamel + " '" + aspectName + + "' returns EventBean-array but does not provide the event type name"); + } + EventType eventType = eventAdapterService.GetEventTypeByName(optionalEventTypeName); + if (eventType == null) + { + throw new ExprValidationException( + aspectCamel + " '" + aspectName + "' returns event type '" + optionalEventTypeName + + "' and the event type cannot be found"); + } + return eventType; + } + + public static Pair> GetSuperTypesDepthFirst( + ConfigurationEventTypeWithSupertype optionalConfig, + EventUnderlyingType representation, + IDictionary nameToTypeMap) + where T : EventType + { + return GetSuperTypesDepthFirst( + optionalConfig == null ? null : optionalConfig.SuperTypes, representation, nameToTypeMap); + } + + public static Pair> GetSuperTypesDepthFirst( + ICollection superTypesSet, + EventUnderlyingType representation, + IDictionary nameToTypeMap) + where T : EventType + { + if (superTypesSet == null || superTypesSet.IsEmpty()) + { + return new Pair>(null, null); + } + + var superTypes = new EventType[superTypesSet.Count]; + var deepSuperTypes = new LinkedHashSet(); + + int count = 0; + foreach (string superName in superTypesSet) + { + EventType type = nameToTypeMap.Get(superName); + if (type == null) + { + throw new EventAdapterException("Supertype by name '" + superName + "' could not be found"); + } + if (representation == EventUnderlyingType.MAP) + { + if (!(type is MapEventType)) + { + throw new EventAdapterException( + "Supertype by name '" + superName + + "' is not a Map, expected a Map event type as a supertype"); + } + } + else if (representation == EventUnderlyingType.OBJECTARRAY) + { + if (!(type is ObjectArrayEventType)) + { + throw new EventAdapterException( + "Supertype by name '" + superName + + "' is not an Object-array type, expected a Object-array event type as a supertype"); + } + } + else if (representation == EventUnderlyingType.AVRO) + { + if (!(type is AvroSchemaEventType)) + { + throw new EventAdapterException( + "Supertype by name '" + superName + + "' is not an Avro type, expected a Avro event type as a supertype"); + } + } + else + { + throw new IllegalStateException("Unrecognized enum " + representation); + } + superTypes[count++] = type; + deepSuperTypes.Add(type); + AddRecursiveSupertypes(deepSuperTypes, type); + } + + var superTypesListDepthFirst = new List(deepSuperTypes); + superTypesListDepthFirst.Reverse(); + + return new Pair>(superTypes, new LinkedHashSet(superTypesListDepthFirst)); + } + + public static EventPropertyDescriptor GetNestablePropertyDescriptor(EventType target, string propertyName) + { + EventPropertyDescriptor descriptor = target.GetPropertyDescriptor(propertyName); + if (descriptor != null) + { + return descriptor; + } + int index = ASTUtil.UnescapedIndexOfDot(propertyName); + if (index == -1) + { + return null; + } + // parse, can be an nested property + Property property = PropertyParser.ParseAndWalkLaxToSimple(propertyName); + if (property is PropertyBase) + { + return target.GetPropertyDescriptor(((PropertyBase)property).PropertyNameAtomic); + } + if (!(property is NestedProperty)) + { + return null; + } + var nested = (NestedProperty) property; + var properties = new ArrayDeque(nested.Properties); + return GetNestablePropertyDescriptor(target, properties); + } + + public static EventPropertyDescriptor GetNestablePropertyDescriptor(EventType target, Deque stack) + { + Property topProperty = stack.RemoveFirst(); + if (stack.IsEmpty()) + { + return target.GetPropertyDescriptor(((PropertyBase)topProperty).PropertyNameAtomic); + } + + if (!(topProperty is SimpleProperty)) + { + return null; + } + var simple = (SimpleProperty) topProperty; + + FragmentEventType fragmentEventType = target.GetFragmentType(simple.PropertyNameAtomic); + if (fragmentEventType == null || fragmentEventType.FragmentType == null) + { + return null; + } + return GetNestablePropertyDescriptor(fragmentEventType.FragmentType, stack); + } + + public static LinkedHashMap BuildType( + IList columns, + EventAdapterService eventAdapterService, + ICollection copyFrom, + EngineImportService engineImportService) + { + var typing = new LinkedHashMap(); + var columnNames = new HashSet(); + foreach (ColumnDesc column in columns) + { + bool added = columnNames.Add(column.Name); + if (!added) + { + throw new ExprValidationException("Duplicate column name '" + column.Name + "'"); + } + object columnType = BuildType(column, engineImportService); + typing.Put(column.Name, columnType); + } + + if (copyFrom != null && !copyFrom.IsEmpty()) + { + foreach (string copyFromName in copyFrom) + { + EventType type = eventAdapterService.GetEventTypeByName(copyFromName); + if (type == null) + { + throw new ExprValidationException("Type by name '" + copyFromName + "' could not be located"); + } + MergeType(typing, type); + } + } + return typing; + } + + public static Object BuildType(ColumnDesc column, EngineImportService engineImportService) + { + if (column.Type == null) + { + return null; + } + + if (column.IsPrimitiveArray) + { + Type primitive = TypeHelper.GetPrimitiveTypeForName(column.Type); + if (primitive != null) + { + return Array.CreateInstance(primitive, 0).GetType(); + } + throw new ExprValidationException("Type '" + column.Type + "' is not a primitive type"); + } + + Type plain = TypeHelper.GetTypeForSimpleName(column.Type); + if (plain != null) + { + if (column.IsArray) + { + plain = Array.CreateInstance(plain, 0).GetType(); + } + return plain; + } + + // try imports first + Type resolved = null; + try + { + resolved = engineImportService.ResolveType(column.Type, false); + } + catch (EngineImportException) + { + // expected + } + + // resolve from classpath when not found + if (resolved == null) + { + try + { + resolved = TypeHelper.GetClassForName(column.Type, engineImportService.GetClassForNameProvider()); + } + catch (TypeLoadException) + { + // expected + } + } + + // Handle resolved classes here + if (resolved != null) + { + if (column.IsArray) + { + resolved = Array.CreateInstance(resolved, 0).GetType(); + } + return resolved; + } + + // Event types fall into here + if (column.IsArray) + { + return column.Type + "[]"; + } + return column.Type; + } + + private static void MergeType(IDictionary typing, EventType typeToMerge) + { + foreach (EventPropertyDescriptor prop in typeToMerge.PropertyDescriptors) + { + object existing = typing.Get(prop.PropertyName); + + if (!prop.IsFragment) + { + Type assigned = prop.PropertyType; + Type existingType = existing as Type; + if (existing != null) + { + if (existingType.GetBoxedType() != assigned.GetBoxedType()) + { + throw new ExprValidationException( + string.Format( + "Type by name '{0}' contributes property '{1}' defined as '{2}' which overides the same property of type '{3}'", + typeToMerge.Name, prop.PropertyName, + assigned.GetTypeNameFullyQualPretty(), + existingType.GetTypeNameFullyQualPretty())); + } + } + typing.Put(prop.PropertyName, prop.PropertyType); + } + else + { + if (existing != null) + { + throw new ExprValidationException( + "Property by name '" + prop.PropertyName + "' is defined twice by adding type '" + + typeToMerge.Name + "'"); + } + + FragmentEventType fragment = typeToMerge.GetFragmentType(prop.PropertyName); + if (fragment == null) + { + continue; + } + if (fragment.IsIndexed) + { + typing.Put( + prop.PropertyName, new EventType[] + { + fragment.FragmentType + }); + } + else + { + typing.Put(prop.PropertyName, fragment.FragmentType); + } + } + } + } + + public static void ValidateTimestampProperties( + EventType eventType, + string startTimestampProperty, + string endTimestampProperty) + { + if (startTimestampProperty != null) + { + if (eventType.GetGetter(startTimestampProperty) == null) + { + throw new ConfigurationException( + "Declared start timestamp property name '" + startTimestampProperty + "' was not found"); + } + Type type = eventType.GetPropertyType(startTimestampProperty); + if (!TypeHelper.IsDateTime(type)) + { + throw new ConfigurationException( + "Declared start timestamp property '" + startTimestampProperty + + "' is expected to return a DateTime, DateTimeEx or long-typed value but returns '" + Name.Of(type) + "'"); + } + } + + if (endTimestampProperty != null) + { + if (startTimestampProperty == null) + { + throw new ConfigurationException( + "Declared end timestamp property requires that a start timestamp property is also declared"); + } + if (eventType.GetGetter(endTimestampProperty) == null) + { + throw new ConfigurationException( + "Declared end timestamp property name '" + endTimestampProperty + "' was not found"); + } + Type type = eventType.GetPropertyType(endTimestampProperty); + if (!TypeHelper.IsDateTime(type)) + { + throw new ConfigurationException( + "Declared end timestamp property '" + endTimestampProperty + + "' is expected to return a DateTime, DateTimeEx or long-typed value but returns '" + Name.Of(type) + "'"); + } + Type startType = eventType.GetPropertyType(startTimestampProperty); + if (startType.GetBoxedType() != type.GetBoxedType()) + { + throw new ConfigurationException( + "Declared end timestamp property '" + endTimestampProperty + + "' is expected to have the same property type as the start-timestamp property '" + + startTimestampProperty + "'"); + } + } + } + + public static bool IsTypeOrSubTypeOf(EventType candidate, EventType superType) + { + if (Equals(candidate, superType)) + { + return true; + } + + if (candidate.SuperTypes != null) + { + return candidate.DeepSuperTypes + .Any(eventType => Equals(eventType, superType)); + } + return false; + } + + /// + /// Determine among the Map-type properties which properties are Bean-type event type names, + /// rewrites these as Type-type instead so that they are configured as native property and do not require wrapping, + /// but may require unwrapping. + /// + /// properties of map type + /// event adapter service + /// + /// compiled properties, same as original unless Bean-type event type names were specified. + /// + public static IDictionary CompileMapTypeProperties( + IDictionary typing, + EventAdapterService eventAdapterService) + { + var compiled = new LinkedHashMap(typing); + foreach (var specifiedEntry in typing) + { + var typeSpec = specifiedEntry.Value; + var nameSpec = specifiedEntry.Key; + if (!(typeSpec is string)) + { + continue; + } + + var typeNameSpec = (string) typeSpec; + var isArray = IsPropertyArray(typeNameSpec); + if (isArray) + { + typeNameSpec = GetPropertyRemoveArray(typeNameSpec); + } + + EventType eventType = eventAdapterService.GetEventTypeByName(typeNameSpec); + if (!(eventType is BeanEventType)) + { + continue; + } + + var beanEventType = (BeanEventType) eventType; + Type underlyingType = beanEventType.UnderlyingType; + if (isArray) + { + underlyingType = TypeHelper.GetArrayType(underlyingType); + } + compiled.Put(nameSpec, underlyingType); + } + return compiled; + } + + /// + /// Returns true if the name indicates that the type is an array type. + /// + /// the property name + /// true if array type + public static bool IsPropertyArray(string name) + { + return name.Trim().EndsWith("[]"); + } + + public static bool IsTypeOrSubTypeOf(string typeName, EventType sameTypeOrSubtype) + { + if (sameTypeOrSubtype.Name.Equals(typeName)) + { + return true; + } + if (sameTypeOrSubtype.SuperTypes == null) + { + return false; + } + + return sameTypeOrSubtype.SuperTypes.Any(superType => superType.Name == typeName); + } + + /// + /// Returns the property name without the array type extension, if present. + /// + /// property name + /// property name with removed array extension name + public static string GetPropertyRemoveArray(string name) + { + return name + .RegexReplaceAll("\\[", "") + .RegexReplaceAll("\\]", ""); + } + + public static PropertySetDescriptor GetNestableProperties( + IDictionary propertiesToAdd, + EventAdapterService eventAdapterService, + EventTypeNestableGetterFactory factory, + EventType[] optionalSuperTypes) + { + var propertyNameList = new List(); + var propertyDescriptors = new List(); + var nestableTypes = new LinkedHashMap(); + var propertyItems = new Dictionary(); + + // handle super-types first, such that the order of properties is well-defined from super-type to subtype + if (optionalSuperTypes != null) + { + for (var i = 0; i < optionalSuperTypes.Length; i++) + { + var superType = (BaseNestableEventType) optionalSuperTypes[i]; + propertyNameList.AddRange( + superType.PropertyNames.Where(propertyName => !nestableTypes.ContainsKey(propertyName))); + propertyDescriptors.AddRange( + superType.PropertyDescriptors.Where(descriptor => !nestableTypes.ContainsKey(descriptor.PropertyName))); + + propertyItems.PutAll(superType.PropertyItems); + nestableTypes.PutAll(superType.NestableTypes); + } + } + + nestableTypes.PutAll(propertiesToAdd); + + // Initialize getters and names array: at this time we do not care about nested types, + // these are handled at the time someone is asking for them + foreach (var entry in propertiesToAdd) + { + var name = entry.Key; + if (name == null) + { + throw new EPException("Invalid type configuration: property name is not a string-type value"); + } + + // handle types that are string values + var entryValue = entry.Value; + if (entryValue is string) + { + var value = entryValue.ToString().Trim(); + var clazz = TypeHelper.GetPrimitiveTypeForName(value); + if (clazz != null) + { + entryValue = clazz; + } + } + + if (entryValue is Type) + { + var classType = (Type) entryValue; + + var componentType = classType.GetIndexType(); + var isIndexed = componentType != null; + + var isMapped = !isIndexed && classType.IsGenericStringDictionary(); + if (isMapped) + { + componentType = classType.GetGenericArguments()[1]; + } + + var isFragment = classType.IsFragmentableType(); + BeanEventType nativeFragmentType = null; + FragmentEventType fragmentType = null; + if (isFragment) + { + fragmentType = EventBeanUtility.CreateNativeFragmentType(classType, null, eventAdapterService); + if (fragmentType != null) + { + nativeFragmentType = (BeanEventType) fragmentType.FragmentType; + } + else + { + isFragment = false; + } + } + var getter = factory.GetGetterProperty(name, nativeFragmentType, eventAdapterService); + var descriptor = new EventPropertyDescriptor(name, classType, componentType, false, false, isIndexed, isMapped, isFragment); + var item = new PropertySetDescriptorItem(descriptor, classType, getter, fragmentType); + propertyNameList.Add(name); + propertyDescriptors.Add(descriptor); + propertyItems.Put(name, item); + continue; + } + + // A null-type is also allowed + if (entryValue == null) + { + var getter = factory.GetGetterProperty(name, null, null); + var descriptor = new EventPropertyDescriptor(name, null, null, false, false, false, false, false); + var item = new PropertySetDescriptorItem(descriptor, null, getter, null); + propertyNameList.Add(name); + propertyDescriptors.Add(descriptor); + propertyItems.Put(name, item); + continue; + } + + // Add Map itself as a property + if (entryValue is IDictionary) + { + var getter = factory.GetGetterProperty(name, null, null); + var descriptor = new EventPropertyDescriptor(name, typeof(IDictionary), null, false, false, false, true, false); + var item = new PropertySetDescriptorItem(descriptor, typeof(IDictionary), getter, null); + propertyNameList.Add(name); + propertyDescriptors.Add(descriptor); + propertyItems.Put(name, item); + continue; + } + + if (entryValue is EventType) + { + // Add EventType itself as a property + EventPropertyGetter getter = factory.GetGetterEventBean(name); + var eventType = (EventType) entryValue; + var descriptor = new EventPropertyDescriptor( + name, eventType.UnderlyingType, null, false, false, false, false, true); + var fragmentEventType = new FragmentEventType(eventType, false, false); + var item = new PropertySetDescriptorItem( + descriptor, eventType.UnderlyingType, getter, fragmentEventType); + propertyNameList.Add(name); + propertyDescriptors.Add(descriptor); + propertyItems.Put(name, item); + continue; + } + + if (entryValue is EventType[]) + { + // Add EventType array itself as a property, type is expected to be first array element + var eventType = ((EventType[]) entryValue)[0]; + var prototypeArray = Array.CreateInstance(eventType.UnderlyingType, 0); + var getter = factory.GetGetterEventBeanArray(name, eventType); + var descriptor = new EventPropertyDescriptor( + name, prototypeArray.GetType(), eventType.UnderlyingType, false, false, true, false, true); + var fragmentEventType = new FragmentEventType(eventType, true, false); + var item = new PropertySetDescriptorItem( + descriptor, prototypeArray.GetType(), getter, fragmentEventType); + propertyNameList.Add(name); + propertyDescriptors.Add(descriptor); + propertyItems.Put(name, item); + continue; + } + + if (entryValue is string) + { + var propertyName = entryValue.ToString(); + var isArray = IsPropertyArray(propertyName); + if (isArray) + { + propertyName = GetPropertyRemoveArray(propertyName); + } + + // Add EventType itself as a property + EventType eventType = eventAdapterService.GetEventTypeByName(propertyName); + if (!(eventType is BaseNestableEventType) && !(eventType is BeanEventType)) + { + throw new EPException( + "Nestable type configuration encountered an unexpected property type name '" + + entryValue + "' for property '" + name + + "', expected Type or DataMap or the name of a previously-declared Map or ObjectArray type"); + } + + Type underlyingType = eventType.UnderlyingType; + Type propertyComponentType = null; + if (isArray) + { + propertyComponentType = underlyingType; + if (underlyingType != typeof (Object[])) + { + //underlyingType = underlyingType.GetElementType(); + underlyingType = Array.CreateInstance(underlyingType, 0).GetType(); + } + } + + EventPropertyGetter getter; + if (!isArray) + { + getter = factory.GetGetterBeanNested(name, eventType, eventAdapterService); + } + else + { + getter = factory.GetGetterBeanNestedArray(name, eventType, eventAdapterService); + } + + var descriptor = new EventPropertyDescriptor( + name, underlyingType, propertyComponentType, false, false, isArray, false, true); + var fragmentEventType = new FragmentEventType(eventType, isArray, false); + var item = new PropertySetDescriptorItem(descriptor, underlyingType, getter, fragmentEventType); + propertyNameList.Add(name); + propertyDescriptors.Add(descriptor); + propertyItems.Put(name, item); + continue; + } + + GenerateExceptionNestedProp(name, entryValue); + } + + return new PropertySetDescriptor(propertyNameList, propertyDescriptors, propertyItems, nestableTypes); + } + + private static void GenerateExceptionNestedProp(string name, Object value) + { + string clazzName = (value == null) ? "null" : value.GetType().Name; + throw new EPException( + "Nestable type configuration encountered an unexpected property type of '" + + clazzName + "' for property '" + name + + "', expected Type or DataMap or the name of a previously-declared Map or ObjectArray type"); + } + + public static Type GetNestablePropertyType( + string propertyName, + IDictionary simplePropertyTypes, + IDictionary nestableTypes, + EventAdapterService eventAdapterService) + { + PropertySetDescriptorItem item = simplePropertyTypes.Get(ASTUtil.UnescapeDot(propertyName)); + if (item != null) + { + return item.SimplePropertyType; + } + + // see if this is a nested property + int index = ASTUtil.UnescapedIndexOfDot(propertyName); + if (index == -1) + { + // dynamic simple property + if (propertyName.EndsWith("?")) + { + return typeof (Object); + } + + // parse, can be an indexed property + Property property = PropertyParser.ParseAndWalkLaxToSimple(propertyName); + + if (property is SimpleProperty) + { + PropertySetDescriptorItem propitem = simplePropertyTypes.Get(propertyName); + if (propitem != null) + { + return propitem.SimplePropertyType; + } + return null; + } + + if (property is IndexedProperty) + { + var indexedProp = (IndexedProperty) property; + object type = nestableTypes.Get(indexedProp.PropertyNameAtomic); + if (type == null) + { + return null; + } + else if (type is EventType[]) + { + return ((EventType[]) type)[0].UnderlyingType; + } + else if (type is string) + { + var propTypeName = type.ToString(); + var propTypeNameIsArray = IsPropertyArray(propTypeName); + if (propTypeNameIsArray) + { + propTypeName = GetPropertyRemoveArray(propTypeName); + var innerTypeX = eventAdapterService.GetEventTypeByName(propTypeName); + return innerTypeX == null ? null : innerTypeX.UnderlyingType; + } + + var innerType = eventAdapterService.GetEventTypeByName(propTypeName); + if (innerType == null) + { + var propType = eventAdapterService.EngineImportService.ResolveType(propTypeName, false); + return propType != null ? propType.GetIndexType() : null; + } + return innerType == null ? null : innerType.UnderlyingType; + } + if (!(type is Type)) + { + return null; + } + + var asType = (Type) type; + return asType.GetIndexType(); + } + else if (property is MappedProperty) + { + var mappedProp = (MappedProperty) property; + var type = nestableTypes.Get(mappedProp.PropertyNameAtomic); + if (type == null) + { + return null; + } + if (type is Type) + { + var trueType = (Type)type; + if (trueType.IsGenericStringDictionary()) + { + return trueType.GetGenericArguments()[1]; + } + } + return null; + } + else + { + return null; + } + } + + // Map event types allow 2 types of properties inside: + // - a property that is a object is interrogated via bean property getters and BeanEventType + // - a property that is a Map itself is interrogated via map property getters + // The property getters therefore act on + + // Take apart the nested property into a map key and a nested value class property name + string propertyMap = ASTUtil.UnescapeDot(propertyName.Substring(0, index)); + string propertyNested = propertyName.Between(index + 1, propertyName.Length); + bool isRootedDynamic = false; + + // If the property is dynamic, remove the ? since the property type is defined without + if (propertyMap.EndsWith("?")) + { + propertyMap = propertyMap.Substring(0, propertyMap.Length - 1); + isRootedDynamic = true; + } + + object nestedType = nestableTypes.Get(propertyMap); + if (nestedType == null) + { + // parse, can be an indexed property + Property property = PropertyParser.ParseAndWalkLaxToSimple(propertyMap); + if (property is IndexedProperty) + { + var indexedProp = (IndexedProperty) property; + object type = nestableTypes.Get(indexedProp.PropertyNameAtomic); + if (type == null) + { + return null; + } + // handle map-in-map case + if (type is string) + { + string propTypeName = type.ToString(); + bool isArray = IsPropertyArray(propTypeName); + if (isArray) + { + propTypeName = GetPropertyRemoveArray(propTypeName); + } + EventType innerType = eventAdapterService.GetEventTypeByName(propTypeName); + if (!(innerType is BaseNestableEventType)) + { + return null; + } + return innerType.GetPropertyType(propertyNested); + } + else if (type is EventType[]) + { + // handle eventtype[] in map + EventType innerType = ((EventType[]) type)[0]; + return innerType.GetPropertyType(propertyNested); + } + else + { + // handle array class in map case + if (!(type is Type)) + { + return null; + } + if (!((Type) type).IsArray) + { + return null; + } + Type componentType = ((Type) type).GetElementType(); + EventType nestedEventType = eventAdapterService.AddBeanType( + componentType.FullName, componentType, false, false, false); + return nestedEventType.GetPropertyType(propertyNested); + } + } + else if (property is MappedProperty) + { + return null; // Since no type information is available for the property + } + else + { + return isRootedDynamic ? typeof (Object) : null; + } + } + + // If there is a map value in the map, return the Object value if this is a dynamic property + if (Equals(nestedType, typeof (IDictionary))) + { + Property prop = PropertyParser.ParseAndWalk(propertyNested, isRootedDynamic); + return isRootedDynamic ? typeof (Object) : prop.GetPropertyTypeMap(null, eventAdapterService); + // we don't have a definition of the nested props + } + else if (nestedType is IDictionary) + { + Property prop = PropertyParser.ParseAndWalk(propertyNested, isRootedDynamic); + var nestedTypes = (IDictionary)nestedType; + return isRootedDynamic ? typeof (Object) : prop.GetPropertyTypeMap(nestedTypes, eventAdapterService); + } + else if (nestedType is Type) + { + var simpleClass = (Type) nestedType; + if (simpleClass.IsBuiltinDataType()) + { + return null; + } + if (simpleClass.IsArray && + (simpleClass.GetElementType().IsBuiltinDataType() || + simpleClass.GetElementType() == typeof (Object))) + { + return null; + } + EventType nestedEventType = eventAdapterService.AddBeanType( + simpleClass.FullName, simpleClass, false, false, false); + return isRootedDynamic ? typeof (Object) : nestedEventType.GetPropertyType(propertyNested); + } + else if (nestedType is EventType) + { + var innerType = (EventType) nestedType; + return isRootedDynamic ? typeof (Object) : innerType.GetPropertyType(propertyNested); + } + else if (nestedType is EventType[]) + { + return null; // requires indexed property + } + else if (nestedType is string) + { + string nestedName = nestedType.ToString(); + bool isArray = IsPropertyArray(nestedName); + if (isArray) + { + nestedName = GetPropertyRemoveArray(nestedName); + } + EventType innerType = eventAdapterService.GetEventTypeByName(nestedName); + if (!(innerType is BaseNestableEventType)) + { + return null; + } + return isRootedDynamic ? typeof (Object) : innerType.GetPropertyType(propertyNested); + } + else + { + string message = "Nestable map type configuration encountered an unexpected value type of '" + + nestedType.GetType().FullName + " for property '" + propertyName + + "', expected Type, typeof(Map) or IDictionary as value type"; + throw new PropertyAccessException(message); + } + } + + public static EventPropertyGetter GetNestableGetter( + string propertyName, + IDictionary propertyGetters, + IDictionary propertyGetterCache, + IDictionary nestableTypes, + EventAdapterService eventAdapterService, + EventTypeNestableGetterFactory factory, + bool isObjectArray) + { + EventPropertyGetter cachedGetter = propertyGetterCache.Get(propertyName); + if (cachedGetter != null) + { + return cachedGetter; + } + + string unescapePropName = ASTUtil.UnescapeDot(propertyName); + PropertySetDescriptorItem item = propertyGetters.Get(unescapePropName); + if (item != null) + { + EventPropertyGetter getter = item.PropertyGetter; + propertyGetterCache.Put(propertyName, getter); + return getter; + } + + // see if this is a nested property + int index = ASTUtil.UnescapedIndexOfDot(propertyName); + if (index == -1) + { + Property prop = PropertyParser.ParseAndWalkLaxToSimple(propertyName); + if (prop is DynamicProperty) + { + EventPropertyGetter getterDyn = factory.GetPropertyProvidedGetter( + nestableTypes, propertyName, prop, eventAdapterService); + propertyGetterCache.Put(propertyName, getterDyn); + return getterDyn; + } + else if (prop is IndexedProperty) + { + var indexedProp = (IndexedProperty) prop; + var type = nestableTypes.Get(indexedProp.PropertyNameAtomic); + if (type == null) + { + return null; + } + else if (type is EventType[]) + { + EventPropertyGetter getterArr = factory.GetGetterIndexedEventBean( + indexedProp.PropertyNameAtomic, indexedProp.Index); + propertyGetterCache.Put(propertyName, getterArr); + return getterArr; + } + else if (type is string) + { + EventType innerType; + + var propTypeName = type.ToString(); + var propTypeNameIsArray = IsPropertyArray(propTypeName); + if (propTypeNameIsArray) + { + propTypeName = GetPropertyRemoveArray(propTypeName); + innerType = eventAdapterService.GetEventTypeByName(propTypeName); + } + else + { + innerType = eventAdapterService.GetEventTypeByName(propTypeName); + if (innerType == null) + { + var propType = eventAdapterService.EngineImportService.ResolveType(propTypeName, false); + if (propType != null) + { + var indexType = propType.GetIndexType(); + var indexGetter = factory.GetGetterIndexedPONO(indexedProp.PropertyNameAtomic, indexedProp.Index, eventAdapterService, indexType); + propertyGetterCache.Put(propertyName, indexGetter); + return indexGetter; + } + } + } + + if (innerType is BaseNestableEventType) + { + EventPropertyGetter typeGetter; + if (!propTypeNameIsArray) + { + typeGetter = factory.GetGetterBeanNested( + indexedProp.PropertyNameAtomic, innerType, eventAdapterService); + } + else + { + typeGetter = factory.GetGetterIndexedUnderlyingArray( + indexedProp.PropertyNameAtomic, indexedProp.Index, eventAdapterService, innerType); + } + + propertyGetterCache.Put(propertyName, typeGetter); + return typeGetter; + } + + return null; + } + + // handle map type name in map + var asType = (Type) type; + if (asType == null) + { + return null; + } + + // its a collection + var componentType = asType.GetIndexType(); + if (componentType == null) + { + return null; + } + + var indexedGetter = factory.GetGetterIndexedPONO( + indexedProp.PropertyNameAtomic, indexedProp.Index, eventAdapterService, componentType); + propertyGetterCache.Put(propertyName, indexedGetter); + return indexedGetter; + } + else if (prop is MappedProperty) + { + var mappedProp = (MappedProperty) prop; + object type = nestableTypes.Get(mappedProp.PropertyNameAtomic); + if (type == null) + { + return null; + } + if (type is Type) + { + if (((Type) type).IsGenericStringDictionary()) + { + return factory.GetGetterMappedProperty(mappedProp.PropertyNameAtomic, mappedProp.Key); + } + } + return null; + } + else + { + return null; + } + } + + // Take apart the nested property into a map key and a nested value class property name + string propertyMap = ASTUtil.UnescapeDot(propertyName.Substring(0, index)); + string propertyNested = propertyName.Between(index + 1, propertyName.Length); + bool isRootedDynamic = false; + + // If the property is dynamic, remove the ? since the property type is defined without + if (propertyMap.EndsWith("?")) + { + propertyMap = propertyMap.Substring(0, propertyMap.Length - 1); + isRootedDynamic = true; + } + + object nestedType = nestableTypes.Get(propertyMap); + if (nestedType == null) + { + // parse, can be an indexed property + Property property = PropertyParser.ParseAndWalkLaxToSimple(propertyMap); + if (property is IndexedProperty) + { + var indexedProp = (IndexedProperty) property; + object type = nestableTypes.Get(indexedProp.PropertyNameAtomic); + if (type == null) + { + return null; + } + if (type is string) + { + string nestedTypeName = type.ToString(); + bool isArray = IsPropertyArray(nestedTypeName); + if (isArray) + { + nestedTypeName = GetPropertyRemoveArray(nestedTypeName); + } + EventType innerType = eventAdapterService.GetEventTypeByName(nestedTypeName); + if (!(innerType is BaseNestableEventType)) + { + return null; + } + EventPropertyGetter typeGetter; + if (!isArray) + { + typeGetter = factory.GetGetterNestedEntryBean( + propertyMap, innerType.GetGetter(propertyNested), innerType, eventAdapterService); + } + else + { + EventPropertyGetter innerGetter = innerType.GetGetter(propertyNested); + if (innerGetter == null) + { + return null; + } + typeGetter = factory.GetGetterNestedEntryBeanArray( + indexedProp.PropertyNameAtomic, indexedProp.Index, innerGetter, innerType, + eventAdapterService); + } + propertyGetterCache.Put(propertyName, typeGetter); + return typeGetter; + } + else if (type is EventType[]) + { + EventType componentType = ((EventType[]) type)[0]; + EventPropertyGetter nestedGetter = componentType.GetGetter(propertyNested); + if (nestedGetter == null) + { + return null; + } + EventPropertyGetter typeGetter = + factory.GetGetterIndexedEntryEventBeanArrayElement( + indexedProp.PropertyNameAtomic, indexedProp.Index, nestedGetter); + propertyGetterCache.Put(propertyName, typeGetter); + return typeGetter; + } + else + { + if (!(type is Type)) + { + return null; + } + if (!((Type) type).IsArray) + { + return null; + } + Type componentType = ((Type) type).GetElementType(); + EventType nestedEventType = eventAdapterService.AddBeanType( + componentType.FullName, componentType, false, false, false); + + var nestedGetter = (BeanEventPropertyGetter) nestedEventType.GetGetter(propertyNested); + if (nestedGetter == null) + { + return null; + } + Type propertyTypeGetter = nestedEventType.GetPropertyType(propertyNested); + // construct getter for nested property + EventPropertyGetter indexGetter = + factory.GetGetterIndexedEntryPONO( + indexedProp.PropertyNameAtomic, indexedProp.Index, nestedGetter, eventAdapterService, + propertyTypeGetter); + propertyGetterCache.Put(propertyName, indexGetter); + return indexGetter; + } + } + else if (property is MappedProperty) + { + return null; // Since no type information is available for the property + } + else + { + if (isRootedDynamic) + { + Property prop = PropertyParser.ParseAndWalk(propertyNested, true); + if (!isObjectArray) + { + EventPropertyGetter getterNested = prop.GetGetterMap(null, eventAdapterService); + EventPropertyGetter dynamicGetter = + factory.GetGetterNestedPropertyProvidedGetterDynamic( + nestableTypes, propertyMap, getterNested, eventAdapterService); + propertyGetterCache.Put(propertyName, dynamicGetter); + return dynamicGetter; + } + return null; + } + return null; + } + } + + // The map contains another map, we resolve the property dynamically + if (Equals(nestedType, typeof (IDictionary))) + { + Property prop = PropertyParser.ParseAndWalkLaxToSimple(propertyNested); + MapEventPropertyGetter getterNestedMap = prop.GetGetterMap(null, eventAdapterService); + if (getterNestedMap == null) + { + return null; + } + EventPropertyGetter mapGetter = factory.GetGetterNestedMapProp(propertyMap, getterNestedMap); + propertyGetterCache.Put(propertyName, mapGetter); + return mapGetter; + } + else if (nestedType is IDictionary) + { + Property prop = PropertyParser.ParseAndWalkLaxToSimple(propertyNested); + var nestedTypes = (IDictionary) nestedType; + MapEventPropertyGetter getterNestedMap = prop.GetGetterMap(nestedTypes, eventAdapterService); + if (getterNestedMap == null) + { + return null; + } + EventPropertyGetter mapGetter = factory.GetGetterNestedMapProp(propertyMap, getterNestedMap); + propertyGetterCache.Put(propertyName, mapGetter); + return mapGetter; + } + else if (nestedType is Type) + { + // ask the nested class to resolve the property + var simpleClass = (Type) nestedType; + if (simpleClass.IsArray) + { + return null; + } + EventType nestedEventType = eventAdapterService.AddBeanType( + simpleClass.FullName, simpleClass, false, false, false); + var nestedGetter = (BeanEventPropertyGetter) nestedEventType.GetGetter(propertyNested); + if (nestedGetter == null) + { + return null; + } + + Type propertyType; + Type propertyComponentType; + EventPropertyDescriptor desc = nestedEventType.GetPropertyDescriptor(propertyNested); + if (desc == null) + { + propertyType = nestedEventType.GetPropertyType(propertyNested); + propertyComponentType = propertyType.IsArray + ? propertyType.GetElementType() + : propertyType.GetGenericType(0); + } + else + { + propertyType = desc.PropertyType; + propertyComponentType = desc.PropertyComponentType; + } + + // construct getter for nested property + EventPropertyGetter getter = factory.GetGetterNestedPONOProp( + propertyMap, nestedGetter, eventAdapterService, propertyType, propertyComponentType); + propertyGetterCache.Put(propertyName, getter); + return getter; + } + else if (nestedType is EventType) + { + // ask the nested class to resolve the property + var innerType = (EventType) nestedType; + EventPropertyGetter nestedGetter = innerType.GetGetter(propertyNested); + if (nestedGetter == null) + { + return null; + } + + // construct getter for nested property + EventPropertyGetter getter = factory.GetGetterNestedEventBean(propertyMap, nestedGetter); + propertyGetterCache.Put(propertyName, getter); + return getter; + } + else if (nestedType is EventType[]) + { + var typeArray = (EventType[]) nestedType; + EventPropertyGetter beanArrGetter = factory.GetGetterEventBeanArray(propertyMap, typeArray[0]); + propertyGetterCache.Put(propertyName, beanArrGetter); + return beanArrGetter; + } + else if (nestedType is string) + { + string nestedName = nestedType.ToString(); + bool isArray = IsPropertyArray(nestedName); + if (isArray) + { + nestedName = GetPropertyRemoveArray(nestedName); + } + EventType innerType = eventAdapterService.GetEventTypeByName(nestedName); + if (!(innerType is BaseNestableEventType)) + { + return null; + } + EventPropertyGetter innerGetter = innerType.GetGetter(propertyNested); + if (innerGetter == null) + { + return null; + } + EventPropertyGetter outerGetter; + if (!isArray) + { + outerGetter = factory.GetGetterNestedEntryBean( + propertyMap, innerGetter, innerType, eventAdapterService); + } + else + { + outerGetter = factory.GetGetterNestedEntryBeanArray( + propertyMap, 0, innerGetter, innerType, eventAdapterService); + } + propertyGetterCache.Put(propertyName, outerGetter); + return outerGetter; + } + else + { + string message = "Nestable type configuration encountered an unexpected value type of '" + + nestedType.GetType() + " for property '" + propertyName + + "', expected Type, typeof(Map) or IDictionary as value type"; + throw new PropertyAccessException(message); + } + } + + public static IDictionary ValidateObjectArrayDef( + string[] propertyNames, + Object[] propertyTypes) + { + if (propertyNames.Length != propertyTypes.Length) + { + throw new ConfigurationException( + "Number of property names and property types do not match, found " + propertyNames.Length + + " property names and " + + propertyTypes.Length + " property types"); + } + + // validate property names for no-duplicates + var propertyNamesSet = new HashSet(); + var propertyTypesMap = new Dictionary(); + for (int i = 0; i < propertyNames.Length; i++) + { + string propertyName = propertyNames[i]; + if (propertyNamesSet.Contains(propertyName)) + { + // duplicate prop check + throw new ConfigurationException( + "Property '" + propertyName + "' is listed twice in the type definition"); + } + propertyNamesSet.Add(propertyName); + propertyTypesMap.Put(propertyName, propertyTypes[i]); + } + return propertyTypesMap; + } + + public static EventType CreateNonVariantType( + bool isAnonymous, + CreateSchemaDesc spec, + Attribute[] annotations, + ConfigurationInformation configSnapshot, + EventAdapterService eventAdapterService, + EngineImportService engineImportService) + { + if (spec.AssignedType == AssignedType.VARIANT) + { + throw new IllegalStateException("Variant type is not allowed in this context"); + } + + EventType eventType; + if (spec.Types.IsEmpty()) + { + var representation = EventRepresentationUtil.GetRepresentation( + annotations, configSnapshot, spec.AssignedType); + IDictionary typing = BuildType( + spec.Columns, eventAdapterService, spec.CopyFrom, engineImportService); + IDictionary compiledTyping = CompileMapTypeProperties(typing, eventAdapterService); + + ConfigurationEventTypeWithSupertype config; + if (representation == EventUnderlyingType.MAP) + { + config = new ConfigurationEventTypeMap(); + } + else if (representation == EventUnderlyingType.OBJECTARRAY) + { + config = new ConfigurationEventTypeObjectArray(); + } + else if (representation == EventUnderlyingType.AVRO) + { + config = new ConfigurationEventTypeAvro(); + } + else + { + throw new IllegalStateException("Unrecognized representation '" + representation + "'"); + } + + if (spec.Inherits != null) + { + config.SuperTypes.AddAll(spec.Inherits); + } + config.StartTimestampPropertyName = spec.StartTimestampProperty; + config.EndTimestampPropertyName = spec.EndTimestampProperty; + + if (representation == EventUnderlyingType.MAP) + { + if (isAnonymous) + { + eventType = eventAdapterService.CreateAnonymousMapType(spec.SchemaName, compiledTyping, true); + } + else + { + eventType = eventAdapterService.AddNestableMapType( + spec.SchemaName, compiledTyping, (ConfigurationEventTypeMap) config, false, false, true, + false, false); + } + } + else if (representation == EventUnderlyingType.OBJECTARRAY) + { + if (isAnonymous) + { + eventType = eventAdapterService.CreateAnonymousObjectArrayType(spec.SchemaName, compiledTyping); + } + else + { + eventType = eventAdapterService.AddNestableObjectArrayType( + spec.SchemaName, compiledTyping, (ConfigurationEventTypeObjectArray) config, false, false, + true, false, false, false, null); + } + } + else if (representation == EventUnderlyingType.AVRO) + { + if (isAnonymous) + { + eventType = eventAdapterService.CreateAnonymousAvroType( + spec.SchemaName, compiledTyping, annotations, null, null); + } + else + { + eventType = eventAdapterService.AddAvroType( + spec.SchemaName, compiledTyping, false, false, true, false, false, annotations, + (ConfigurationEventTypeAvro) config, null, null); + } + } + else + { + throw new IllegalStateException("Unrecognized representation " + representation); + } + } + else + { + // object type definition + if (spec.CopyFrom != null && !spec.CopyFrom.IsEmpty()) + { + throw new ExprValidationException("Copy-from types are not allowed with class-provided types"); + } + if (spec.Types.Count != 1) + { + throw new IllegalStateException("Multiple types provided"); + } + string typeName = TypeHelper.TryResolveAbsoluteTypeName(spec.Types.First()); + try + { + // use the existing configuration, if any, possibly adding the start and end timestamps + ConfigurationEventTypeLegacy config = eventAdapterService.GetTypeLegacyConfigs(typeName); + if (spec.StartTimestampProperty != null || spec.EndTimestampProperty != null) + { + if (config == null) + { + config = new ConfigurationEventTypeLegacy(); + } + config.StartTimestampPropertyName = spec.StartTimestampProperty; + config.EndTimestampPropertyName = spec.EndTimestampProperty; + eventAdapterService.TypeLegacyConfigs = Collections.SingletonMap(typeName, config); + } + if (isAnonymous) + { + string className = spec.Types.First(); + Type clazz; + try + { + clazz = engineImportService.ResolveType(className, false); + } + catch (EngineImportException e) + { + throw new ExprValidationException( + "Failed to resolve class '" + className + "': " + e.Message, e); + } + eventType = eventAdapterService.CreateAnonymousBeanType(spec.SchemaName, clazz); + } + else + { + eventType = eventAdapterService.AddBeanType( + spec.SchemaName, spec.Types.First(), false, false, false, true); + } + } + catch (EventAdapterException ex) + { + Type clazz; + try + { + clazz = engineImportService.ResolveType(typeName, false); + if (isAnonymous) + { + eventType = eventAdapterService.CreateAnonymousBeanType(spec.SchemaName, clazz); + } + else + { + eventType = eventAdapterService.AddBeanType(spec.SchemaName, clazz, false, false, true); + } + } + catch (EngineImportException) + { + Log.Debug("Engine import failed to resolve event type '" + typeName + "'"); + throw ex; + } + } + } + return eventType; + } + + public static WriteablePropertyDescriptor FindWritable( + string propertyName, + IEnumerable writables) + { + foreach (WriteablePropertyDescriptor writable in writables) + { + if (writable.PropertyName == propertyName) + { + return writable; + } + } + return null; + } + + public static TimestampPropertyDesc ValidatedDetermineTimestampProps( + EventType type, + string startProposed, + string endProposed, + EventType[] superTypes) + { + // determine start&end timestamp as inherited + string startTimestampPropertyName = startProposed; + string endTimestampPropertyName = endProposed; + + if (superTypes != null && superTypes.Length > 0) + { + foreach (EventType superType in superTypes) + { + if (superType.StartTimestampPropertyName != null) + { + if (startTimestampPropertyName != null && + !startTimestampPropertyName.Equals(superType.StartTimestampPropertyName)) + { + throw GetExceptionTimestampInherited( + "start", startTimestampPropertyName, superType.StartTimestampPropertyName, superType); + } + startTimestampPropertyName = superType.StartTimestampPropertyName; + } + if (superType.EndTimestampPropertyName != null) + { + if (endTimestampPropertyName != null && + !endTimestampPropertyName.Equals(superType.EndTimestampPropertyName)) + { + throw GetExceptionTimestampInherited( + "end", endTimestampPropertyName, superType.EndTimestampPropertyName, superType); + } + endTimestampPropertyName = superType.EndTimestampPropertyName; + } + } + } + + ValidateTimestampProperties(type, startTimestampPropertyName, endTimestampPropertyName); + return new TimestampPropertyDesc(startTimestampPropertyName, endTimestampPropertyName); + } + + private static EPException GetExceptionTimestampInherited( + string tstype, + string firstName, + string secondName, + EventType superType) + { + string message = "Event type declares " + tstype + " timestamp as property '" + firstName + + "' however inherited event type '" + superType.Name + + "' declares " + tstype + " timestamp as property '" + secondName + "'"; + return new EPException(message); + } + + private static void AddRecursiveSupertypes(ISet superTypes, EventType child) + { + if (child.SuperTypes != null) + { + for (int i = 0; i < child.SuperTypes.Length; i++) + { + superTypes.Add(child.SuperTypes[i]); + AddRecursiveSupertypes(superTypes, child.SuperTypes[i]); + } + } + } + + public static string DisallowedAtTypeMessage() + { + return "The @type annotation is only allowed when the invocation target returns EventBean instances"; + } + + public class TimestampPropertyDesc + { + public TimestampPropertyDesc(string start, string end) + { + Start = start; + End = end; + } + + public string Start { get; private set; } + + public string End { get; private set; } + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/events/ExplicitPropertyDescriptor.cs b/NEsper.Core/NEsper.Core/events/ExplicitPropertyDescriptor.cs new file mode 100755 index 000000000..d8edd518f --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/ExplicitPropertyDescriptor.cs @@ -0,0 +1,56 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events +{ + /// + /// Descriptor for explicit properties for use with . + /// + public class ExplicitPropertyDescriptor + { + /// Ctor. + /// property descriptor + /// getter for values + /// true if array fragment + /// null if not a fragment, else fragment type name + public ExplicitPropertyDescriptor(EventPropertyDescriptor descriptor, EventPropertyGetter getter, bool fragmentArray, String optionalFragmentTypeName) + { + Descriptor = descriptor; + Getter = getter; + IsFragmentArray = fragmentArray; + OptionalFragmentTypeName = optionalFragmentTypeName; + } + + /// Returns the property descriptor. + /// property descriptor + public EventPropertyDescriptor Descriptor { get; private set; } + + /// Returns the getter. + /// getter + public EventPropertyGetter Getter { get; private set; } + + /// Returns the fragment event type name, or null if none defined. + /// fragment type name + public string OptionalFragmentTypeName { get; private set; } + + /// + /// Returns true if an indexed, or false if not indexed. + /// + /// fragment indicator + public bool IsFragmentArray { get; private set; } + + public override String ToString() + { + return Descriptor.PropertyName; + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/MappedEventBean.cs b/NEsper.Core/NEsper.Core/events/MappedEventBean.cs new file mode 100755 index 000000000..25423d756 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/MappedEventBean.cs @@ -0,0 +1,22 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events +{ + /// For events that are maps of properties. + public interface MappedEventBean : EventBean + { + /// Returns property map. + /// properties + IDictionary Properties { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/events/NativeEventType.cs b/NEsper.Core/NEsper.Core/events/NativeEventType.cs new file mode 100755 index 000000000..1a597f0e1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/NativeEventType.cs @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.events +{ + /// + /// Marker interface for event types that need not transpose their property. + /// + /// Transpose is the process of taking a fragment event property and adding the fragment + /// to the resulting type rather then the underlying property object. + /// + public interface NativeEventType + { + } +} diff --git a/NEsper.Core/NEsper.Core/events/NaturalEventBean.cs b/NEsper.Core/NEsper.Core/events/NaturalEventBean.cs new file mode 100755 index 000000000..c38af6b7d --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/NaturalEventBean.cs @@ -0,0 +1,115 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events +{ + /// + /// An event that is carries multiple representations of event properties: A + /// synthetic representation that is designed for delivery as to + /// client code, and a + /// natural representation as a bunch of Object-type properties for fast delivery to + /// client subscriber objects via method call. + /// + public class NaturalEventBean : EventBean, DecoratingEventBean + { + private readonly EventType _eventBeanType; + private readonly Object[] _natural; + private readonly EventBean _optionalSynthetic; + + /// + /// Ctor. + /// + /// the event type of the synthetic event + /// the properties of the event + /// the event bean that is the synthetic event, or null if no synthetic is packed in + public NaturalEventBean(EventType eventBeanType, Object[] natural, EventBean optionalSynthetic) { + _eventBeanType = eventBeanType; + _natural = natural; + _optionalSynthetic = optionalSynthetic; + } + + public EventType EventType + { + get { return _eventBeanType; } + } + + #region Implementation of EventBean + + public object this[string property] + { + get { return Get(property); } + } + + #endregion + + public Object Get(String property) + { + if (_optionalSynthetic != null) + { + return _optionalSynthetic.Get(property); + } + throw new PropertyAccessException("Property access not allowed for natural events without the synthetic event present"); + } + + public object Underlying + { + get + { + return _optionalSynthetic != null + ? _optionalSynthetic.Underlying + : _natural; + } + } + + public EventBean UnderlyingEvent + { + get { return ((DecoratingEventBean) _optionalSynthetic).UnderlyingEvent; } + } + + public IDictionary DecoratingProperties + { + get { return ((DecoratingEventBean) _optionalSynthetic).DecoratingProperties; } + } + + /// + /// Returns the column object result representation. + /// + /// + /// select column values + /// + public object[] Natural + { + get { return _natural; } + } + + /// + /// Returns the synthetic event that can be attached. + /// + /// + /// synthetic if attached, or null if none attached + /// + public EventBean OptionalSynthetic + { + get { return _optionalSynthetic; } + } + + public Object GetFragment(String propertyExpression) + { + if (_optionalSynthetic != null) + { + return _optionalSynthetic.GetFragment(propertyExpression); + } + throw new PropertyAccessException("Property access not allowed for natural events without the synthetic event present"); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/ObjectArrayBackedEventBean.cs b/NEsper.Core/NEsper.Core/events/ObjectArrayBackedEventBean.cs new file mode 100755 index 000000000..1870d58b8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/ObjectArrayBackedEventBean.cs @@ -0,0 +1,24 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; + +namespace com.espertech.esper.events +{ + /// + /// For events that are array of properties. + /// + public interface ObjectArrayBackedEventBean : EventBean + { + /// Returns property array. + /// properties + object[] Properties { get; } + + object[] PropertyValues { set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/events/PropertySetDescriptor.cs b/NEsper.Core/NEsper.Core/events/PropertySetDescriptor.cs new file mode 100755 index 000000000..fa127bdc5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/PropertySetDescriptor.cs @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events +{ + /// + /// Descriptor of a property set. + /// + public class PropertySetDescriptor + { + public PropertySetDescriptor( + IList propertyNameList, + IList propertyDescriptors, + IDictionary propertyItems, + IDictionary nestableTypes) + { + PropertyNameList = propertyNameList; + PropertyDescriptors = propertyDescriptors; + PropertyItems = propertyItems; + NestableTypes = nestableTypes; + } + + public IDictionary PropertyItems { get; private set; } + + /// Returns property name list. + /// property name list + public IList PropertyNameList { get; private set; } + + /// Returns the property descriptors. + /// property descriptors + public IList PropertyDescriptors { get; private set; } + + public IDictionary NestableTypes { get; private set; } + + public string[] PropertyNameArray + { + get { return PropertyNameList.ToArray(); } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/events/PropertySetDescriptorItem.cs b/NEsper.Core/NEsper.Core/events/PropertySetDescriptorItem.cs new file mode 100755 index 000000000..60dc351bf --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/PropertySetDescriptorItem.cs @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events +{ + /// + /// Descriptor of a property item. + /// + public class PropertySetDescriptorItem + { + public PropertySetDescriptorItem( + EventPropertyDescriptor propertyDescriptor, + Type simplePropertyType, + EventPropertyGetter propertyGetter, + FragmentEventType fragmentEventType) + { + PropertyDescriptor = propertyDescriptor; + SimplePropertyType = simplePropertyType; + PropertyGetter = propertyGetter; + FragmentEventType = fragmentEventType; + } + + public EventPropertyDescriptor PropertyDescriptor { get; private set; } + + public Type SimplePropertyType { get; private set; } + + public EventPropertyGetter PropertyGetter { get; private set; } + + public FragmentEventType FragmentEventType { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/events/SendableEvent.cs b/NEsper.Core/NEsper.Core/events/SendableEvent.cs new file mode 100755 index 000000000..3ab1e5432 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/SendableEvent.cs @@ -0,0 +1,18 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; + +namespace com.espertech.esper.events +{ + public interface SendableEvent + { + void Send(EPRuntime runtime); + object Underlying { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/events/TaggedCompositeEventBean.cs b/NEsper.Core/NEsper.Core/events/TaggedCompositeEventBean.cs new file mode 100755 index 000000000..88ba3e7d7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/TaggedCompositeEventBean.cs @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using com.espertech.esper.client; + +namespace com.espertech.esper.events +{ + /// + /// Interface for composite events in which a property is itself an event. + /// + /// For use with patterns in which pattern tags are properties in a result event and property values + /// are the event itself that is matching in a pattern. + /// + /// + public interface TaggedCompositeEventBean + { + /// Returns the event for the tag. + /// is the tag name + /// event + EventBean GetEventBean(String property); + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/events/TaggedCompositeEventType.cs b/NEsper.Core/NEsper.Core/events/TaggedCompositeEventType.cs new file mode 100755 index 000000000..54e3dd7c7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/TaggedCompositeEventType.cs @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using com.espertech.esper.client; +using com.espertech.esper.collection; + +namespace com.espertech.esper.events +{ + /// + /// Interface for composite event type in which each property is itself an event. + /// + /// For use with patterns in which pattern tags are properties in a result event and property values + /// are the event itself that is matching in a pattern. + /// + /// + public interface TaggedCompositeEventType + { + /// Returns the event types for each composing event. + /// map of tag name and event type + IDictionary> TaggedEventTypes { get;} + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/events/WrapperEventBean.cs b/NEsper.Core/NEsper.Core/events/WrapperEventBean.cs new file mode 100755 index 000000000..828694a59 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/WrapperEventBean.cs @@ -0,0 +1,167 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; + +namespace com.espertech.esper.events +{ + using DataMap = IDictionary; + + /// + /// Event bean that wraps another event bean adding additional properties. + /// + /// This can be useful for classes for which the statement adds derived values retaining the original class. + /// + /// + /// The event type of such events is always . Additional properties are stored in a + /// Map. + /// + /// + public class WrapperEventBean + : EventBean + , DecoratingEventBean + { + private readonly EventBean _theEvent; + private readonly DataMap _map; + private readonly EventType _eventType; + + /// Ctor. + /// is the wrapped event + /// + /// is zero or more property values that embellish the wrapped event + /// + /// is the . + public WrapperEventBean(EventBean theEvent, DataMap properties, EventType eventType) + { + _theEvent = theEvent; + _map = properties; + _eventType = eventType; + } + + /// + /// Returns the value of an event property. + /// + /// + /// the value of a simple property with the specified name. + /// + /// PropertyAccessException - if there is no property of the specified name, or the property cannot be accessed + public Object this[String property] + { + get + { + EventPropertyGetter getter = _eventType.GetGetter(property); + if (getter == null) + { + throw new PropertyAccessException("Property named '" + property + "' is not a valid property name for this type"); + } + return getter.Get(this); + } + } + + /// + /// Returns the value of an event property. This method is a proxy of the indexer. + /// + /// name of the property whose value is to be retrieved + /// + /// the value of a simple property with the specified name. + /// + /// PropertyAccessException - if there is no property of the specified name, or the property cannot be accessed + public virtual Object Get(String property) + { + return this[property]; + } + + /// + /// Return the instance that describes the set of properties available for this event. + /// + /// + /// event type + /// + public EventType EventType + { + get { return _eventType; } + } + + /// + /// Get the underlying data object to this event wrapper. + /// + /// + /// underlying data object, usually either a Map or a bean instance. + /// + public Object Underlying + { + get + { + // If wrapper is simply for the underlying with no additional properties, then return the underlying type + if (_map.Count == 0) + { + return _theEvent.Underlying; + } + else + { + return new Pair(_theEvent.Underlying, _map); + } + } + } + + /// + /// Returns the underlying map storing the additional properties, if any. + /// + /// event property IDictionary + public DataMap UnderlyingMap + { + get { return _map; } + } + + /// + /// Returns decorating properties. + /// + /// + /// property name and values + public DataMap DecoratingProperties + { + get { return _map; } + } + + /// Returns the wrapped event. + /// wrapped event + public EventBean UnderlyingEvent + { + get { return _theEvent; } + } + + + public Object GetFragment(String propertyExpression) + { + EventPropertyGetter getter = _eventType.GetGetter(propertyExpression); + if (getter == null) + { + throw PropertyAccessException.NotAValidProperty(propertyExpression); + } + return getter.GetFragment(this); + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override String ToString() + { + return + "WrapperEventBean " + + "[event=" + _theEvent + "] " + + "[properties=" + _map + "]"; + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/events/WrapperEventBeanCopyMethod.cs b/NEsper.Core/NEsper.Core/events/WrapperEventBeanCopyMethod.cs new file mode 100755 index 000000000..fb33f9c05 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/WrapperEventBeanCopyMethod.cs @@ -0,0 +1,47 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events +{ + /// Copy method for wrapper events. + public class WrapperEventBeanCopyMethod : EventBeanCopyMethod + { + private readonly WrapperEventType _wrapperEventType; + private readonly EventAdapterService _eventAdapterService; + private readonly EventBeanCopyMethod _underlyingCopyMethod; + + /// Ctor. + /// wrapper type + /// event adapter creation + /// copy method for the underlying event + public WrapperEventBeanCopyMethod(WrapperEventType wrapperEventType, EventAdapterService eventAdapterService, EventBeanCopyMethod underlyingCopyMethod) + { + _wrapperEventType = wrapperEventType; + _eventAdapterService = eventAdapterService; + _underlyingCopyMethod = underlyingCopyMethod; + } + + public EventBean Copy(EventBean theEvent) + { + DecoratingEventBean decorated = (DecoratingEventBean) theEvent; + EventBean decoratedUnderlying = decorated.UnderlyingEvent; + EventBean copiedUnderlying = _underlyingCopyMethod.Copy(decoratedUnderlying); + if (copiedUnderlying == null) + { + return null; + } + IDictionary copiedMap = new Dictionary(decorated.DecoratingProperties); + return _eventAdapterService.AdapterForTypedWrapper(copiedUnderlying, copiedMap, _wrapperEventType); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/WrapperEventBeanMapCopyMethod.cs b/NEsper.Core/NEsper.Core/events/WrapperEventBeanMapCopyMethod.cs new file mode 100755 index 000000000..93bbc26e3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/WrapperEventBeanMapCopyMethod.cs @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events +{ + /// + /// Copy method for wrapper events. + /// + public class WrapperEventBeanMapCopyMethod : EventBeanCopyMethod + { + private readonly WrapperEventType _wrapperEventType; + private readonly EventAdapterService _eventAdapterService; + + /// Ctor. + /// wrapper type + /// event adapter + public WrapperEventBeanMapCopyMethod(WrapperEventType wrapperEventType, EventAdapterService eventAdapterService) + { + _wrapperEventType = wrapperEventType; + _eventAdapterService = eventAdapterService; + } + + public EventBean Copy(EventBean theEvent) + { + DecoratingEventBean decorated = (DecoratingEventBean) theEvent; + EventBean decoratedUnderlying = decorated.UnderlyingEvent; + IDictionary copiedMap = new Dictionary(decorated.DecoratingProperties); + return _eventAdapterService.AdapterForTypedWrapper(decoratedUnderlying, copiedMap, _wrapperEventType); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/WrapperEventBeanMapWriter.cs b/NEsper.Core/NEsper.Core/events/WrapperEventBeanMapWriter.cs new file mode 100755 index 000000000..aa5514601 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/WrapperEventBeanMapWriter.cs @@ -0,0 +1,42 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.events +{ + /// + /// Writer for wrapper events. + /// + public class WrapperEventBeanMapWriter : EventBeanWriter + { + private readonly String[] _properties; + + /// Ctor. + /// to write + public WrapperEventBeanMapWriter(String[] properties) + { + _properties = properties; + } + + public void Write(Object[] values, EventBean theEvent) + { + DecoratingEventBean mappedEvent = (DecoratingEventBean) theEvent; + IDictionary map = mappedEvent.DecoratingProperties; + + for (int i = 0; i < _properties.Length; i++) + { + map.Put(_properties[i], values[i]); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/WrapperEventBeanPropertyWriter.cs b/NEsper.Core/NEsper.Core/events/WrapperEventBeanPropertyWriter.cs new file mode 100755 index 000000000..865732289 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/WrapperEventBeanPropertyWriter.cs @@ -0,0 +1,35 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events +{ + /// Writer for a set of wrapper event object values. + public class WrapperEventBeanPropertyWriter : EventBeanWriter + { + private readonly EventPropertyWriter[] _writerArr; + + /// Ctor. + /// writers are writing properties. + public WrapperEventBeanPropertyWriter(EventPropertyWriter[] writerArr) + { + _writerArr = writerArr; + } + + public void Write(Object[] values, EventBean theEvent) + { + for (int i = 0; i < values.Length; i++) + { + _writerArr[i].Write(values[i], theEvent); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/WrapperEventBeanUndCopyMethod.cs b/NEsper.Core/NEsper.Core/events/WrapperEventBeanUndCopyMethod.cs new file mode 100755 index 000000000..e78d06688 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/WrapperEventBeanUndCopyMethod.cs @@ -0,0 +1,43 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; + +namespace com.espertech.esper.events +{ + /// Copy method for underlying events. + public class WrapperEventBeanUndCopyMethod : EventBeanCopyMethod + { + private readonly WrapperEventType _wrapperEventType; + private readonly EventAdapterService _eventAdapterService; + private readonly EventBeanCopyMethod _underlyingCopyMethod; + + /// Ctor. + /// wrapper type + /// for creating events + /// for copying the underlying event + public WrapperEventBeanUndCopyMethod(WrapperEventType wrapperEventType, EventAdapterService eventAdapterService, EventBeanCopyMethod underlyingCopyMethod) + { + _wrapperEventType = wrapperEventType; + _eventAdapterService = eventAdapterService; + _underlyingCopyMethod = underlyingCopyMethod; + } + + public EventBean Copy(EventBean theEvent) + { + DecoratingEventBean decorated = (DecoratingEventBean) theEvent; + EventBean decoratedUnderlying = decorated.UnderlyingEvent; + EventBean copiedUnderlying = _underlyingCopyMethod.Copy(decoratedUnderlying); + if (copiedUnderlying == null) + { + return null; + } + return _eventAdapterService.AdapterForTypedWrapper(copiedUnderlying, decorated.DecoratingProperties, _wrapperEventType); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/WrapperEventBeanUndWriter.cs b/NEsper.Core/NEsper.Core/events/WrapperEventBeanUndWriter.cs new file mode 100755 index 000000000..731a5bfa6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/WrapperEventBeanUndWriter.cs @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events +{ + /// Writer for values to a wrapper event. + public class WrapperEventBeanUndWriter : EventBeanWriter + { + private readonly EventBeanWriter _undWriter; + + /// Ctor. + /// writer to the underlying object + public WrapperEventBeanUndWriter(EventBeanWriter undWriter) + { + _undWriter = undWriter; + } + + public void Write(Object[] values, EventBean theEvent) + { + DecoratingEventBean wrappedEvent = (DecoratingEventBean) theEvent; + EventBean eventWrapped = wrappedEvent.UnderlyingEvent; + _undWriter.Write(values, eventWrapped); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/WrapperEventType.cs b/NEsper.Core/NEsper.Core/events/WrapperEventType.cs new file mode 100755 index 000000000..bdc1b0064 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/WrapperEventType.cs @@ -0,0 +1,676 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.events.map; + +namespace com.espertech.esper.events +{ + using DataMap = IDictionary; + + /// + /// An event type that adds zero or more fields to an existing event type. + /// + /// The additional fields are represented as a Map. Any queries to event properties are + /// first held against the additional fields, and secondly are handed through to the + /// underlying event. + /// + /// + /// If this event type is to add information to another wrapper event type (wrapper to + /// wrapper), then it is the responsibility of the creating logic to use the existing event + /// type and add to it. + /// + /// + /// Uses a the map event type + /// to represent the mapped properties. This is because the additional properties can also be + /// beans or complex types and the Map event type handles these nicely. + /// + /// + public class WrapperEventType : EventTypeSPI + { + /// event type metadata + private readonly EventTypeMetadata _metadata; + + /// The underlying wrapped event type. + private readonly EventType _underlyingEventType; + + /// The map event type that provides the additional properties. + private readonly MapEventType _underlyingMapType; + + private String[] _propertyNames; + private EventPropertyDescriptor[] _propertyDesc; + private IDictionary _propertyDescriptorMap; + + private readonly bool _isNoMapProperties; + private readonly IDictionary _propertyGetterCache; + private readonly EventAdapterService _eventAdapterService; + private EventPropertyDescriptor[] _writableProperties; + private IDictionary> _writers; + + private int _numPropertiesUnderlyingType; + + /// + /// Ctor. + /// + /// event type metadata + /// is the event type name + /// The event type id. + /// is the event type of the wrapped events + /// is the additional properties this wrapper adds + /// is the service for resolving unknown wrapped types + public WrapperEventType(EventTypeMetadata metadata, + String typeName, + int eventTypeId, + EventType eventType, + IDictionary properties, + EventAdapterService eventAdapterService) + { + CheckForRepeatedPropertyNames(eventType, properties); + + _metadata = metadata; + _underlyingEventType = eventType; + EventTypeMetadata metadataMapType = EventTypeMetadata.CreateAnonymous(typeName, ApplicationType.MAP); + _underlyingMapType = new MapEventType(metadataMapType, typeName, 0, eventAdapterService, properties, null, null, null); + _isNoMapProperties = properties.IsEmpty(); + _eventAdapterService = eventAdapterService; + EventTypeId = eventTypeId; + _propertyGetterCache = new Dictionary(); + + UpdatePropertySet(); + + if (metadata.TypeClass == TypeClass.NAMED_WINDOW) + { + StartTimestampPropertyName = eventType.StartTimestampPropertyName; + EndTimestampPropertyName = eventType.EndTimestampPropertyName; + EventTypeUtility.ValidateTimestampProperties(this, StartTimestampPropertyName, EndTimestampPropertyName); + } + } + + private void CheckInitProperties() + { + if (_numPropertiesUnderlyingType != _underlyingEventType.PropertyDescriptors.Count) + { + UpdatePropertySet(); + } + } + + private void UpdatePropertySet() + { + PropertyDescriptorComposite compositeProperties = GetCompositeProperties(_underlyingEventType, _underlyingMapType); + _propertyNames = compositeProperties.PropertyNames; + _propertyDescriptorMap = compositeProperties.PropertyDescriptorMap; + _propertyDesc = compositeProperties.Descriptors; + _numPropertiesUnderlyingType = _underlyingEventType.PropertyDescriptors.Count; + } + + private static PropertyDescriptorComposite GetCompositeProperties(EventType underlyingEventType, MapEventType underlyingMapType) + { + var propertyNames = new List(); + propertyNames.AddAll(underlyingEventType.PropertyNames); + propertyNames.AddAll(underlyingMapType.PropertyNames); + String[] propertyNamesArr = propertyNames.ToArray(); + + var propertyDesc = new List(); + var propertyDescriptorMap = new Dictionary(); + foreach (EventPropertyDescriptor eventProperty in underlyingEventType.PropertyDescriptors) + { + propertyDesc.Add(eventProperty); + propertyDescriptorMap.Put(eventProperty.PropertyName, eventProperty); + } + foreach (EventPropertyDescriptor mapProperty in underlyingMapType.PropertyDescriptors) + { + propertyDesc.Add(mapProperty); + propertyDescriptorMap.Put(mapProperty.PropertyName, mapProperty); + } + EventPropertyDescriptor[] propertyDescArr = propertyDesc.ToArray(); + return new PropertyDescriptorComposite(propertyDescriptorMap, propertyNamesArr, propertyDescArr); + } + + public string StartTimestampPropertyName { get; private set; } + + public string EndTimestampPropertyName { get; private set; } + + public EventType[] DeepSuperTypes + { + get { return null; } + } + + public string Name + { + get { return _metadata.PublicName; } + } + + public int EventTypeId { get; private set; } + + public EventPropertyGetter GetGetter(String property) + { + EventPropertyGetter cachedGetter = _propertyGetterCache.Get(property); + if (cachedGetter != null) + { + return cachedGetter; + } + + if (_underlyingMapType.IsProperty(property) && (property.IndexOf('?') == -1)) + { + EventPropertyGetter mapGetter = _underlyingMapType.GetGetter(property); + EventPropertyGetter getter = new ProxyEventPropertyGetter + { + ProcGet = theEvent => + { + if (!(theEvent is DecoratingEventBean)) + { + throw new PropertyAccessException("Mismatched property getter to EventBean type"); + } + var wrapperEvent = (DecoratingEventBean)theEvent; + var map = wrapperEvent.DecoratingProperties; + return mapGetter.Get(_eventAdapterService.AdapterForTypedMap(map, _underlyingMapType)); + }, + ProcIsExistsProperty = eventBean => true, + ProcGetFragment = theEvent => + { + if (!(theEvent is DecoratingEventBean)) + { + throw new PropertyAccessException("Mismatched property getter to EventBean type"); + } + var wrapperEvent = (DecoratingEventBean)theEvent; + var map = wrapperEvent.DecoratingProperties; + return mapGetter.GetFragment(_eventAdapterService.AdapterForTypedMap(map, _underlyingMapType)); + } + }; + _propertyGetterCache.Put(property, getter); + return getter; + } + else if (_underlyingEventType.IsProperty(property)) + { + EventPropertyGetter getter = new ProxyEventPropertyGetter() + { + ProcGet = theEvent => + { + if (!(theEvent is DecoratingEventBean)) + { + throw new PropertyAccessException("Mismatched property getter to EventBean type"); + } + var wrapperEvent = (DecoratingEventBean)theEvent; + var wrappedEvent = wrapperEvent.UnderlyingEvent; + if (wrappedEvent == null) + { + return null; + } + + var underlyingGetter = _underlyingEventType.GetGetter(property); + return underlyingGetter.Get(wrappedEvent); + }, + ProcIsExistsProperty = eventBean => true, + ProcGetFragment = theEvent => + { + if (!(theEvent is DecoratingEventBean)) + { + throw new PropertyAccessException("Mismatched property getter to EventBean type"); + } + var wrapperEvent = (DecoratingEventBean)theEvent; + var wrappedEvent = wrapperEvent.UnderlyingEvent; + if (wrappedEvent == null) + { + return null; + } + + var underlyingGetter = _underlyingEventType.GetGetter(property); + return underlyingGetter.GetFragment(wrappedEvent); + } + }; + _propertyGetterCache.Put(property, getter); + return getter; + } + else + { + return null; + } + } + + public EventPropertyGetterMapped GetGetterMapped(String mappedProperty) + { + EventPropertyGetterMapped undMapped = _underlyingEventType.GetGetterMapped(mappedProperty); + if (undMapped != null) + { + return new ProxyEventPropertyGetterMapped + { + ProcGet = (theEvent, mapKey) => + { + if (!(theEvent is DecoratingEventBean)) + { + throw new PropertyAccessException("Mismatched property getter to EventBean type"); + } + var wrapperEvent = (DecoratingEventBean)theEvent; + var wrappedEvent = wrapperEvent.UnderlyingEvent; + return wrappedEvent == null ? null : undMapped.Get(wrappedEvent, mapKey); + } + }; + } + EventPropertyGetterMapped decoMapped = _underlyingMapType.GetGetterMapped(mappedProperty); + if (decoMapped != null) + { + return new ProxyEventPropertyGetterMapped + { + ProcGet = (theEvent, mapKey) => + { + if (!(theEvent is DecoratingEventBean)) + { + throw new PropertyAccessException("Mismatched property getter to EventBean type"); + } + var wrapperEvent = (DecoratingEventBean)theEvent; + var map = wrapperEvent.DecoratingProperties; + return decoMapped.Get(_eventAdapterService.AdapterForTypedMap(map, _underlyingMapType), mapKey); + } + }; + } + return null; + } + + public EventPropertyGetterIndexed GetGetterIndexed(String indexedProperty) + { + EventPropertyGetterIndexed undIndexed = _underlyingEventType.GetGetterIndexed(indexedProperty); + if (undIndexed != null) + { + return new ProxyEventPropertyGetterIndexed + { + ProcGet = (theEvent, index) => + { + if (!(theEvent is DecoratingEventBean)) + { + throw new PropertyAccessException("Mismatched property getter to EventBean type"); + } + var wrapperEvent = (DecoratingEventBean)theEvent; + var wrappedEvent = wrapperEvent.UnderlyingEvent; + return wrappedEvent == null ? null : undIndexed.Get(wrappedEvent, index); + } + }; + } + EventPropertyGetterIndexed decoIndexed = _underlyingMapType.GetGetterIndexed(indexedProperty); + if (decoIndexed != null) + { + return new ProxyEventPropertyGetterIndexed + { + ProcGet = (theEvent, index) => + { + if (!(theEvent is DecoratingEventBean)) + { + throw new PropertyAccessException("Mismatched property getter to EventBean type"); + } + var wrapperEvent = (DecoratingEventBean)theEvent; + var map = wrapperEvent.DecoratingProperties; + return decoIndexed.Get(_eventAdapterService.AdapterForTypedMap(map, _underlyingMapType), index); + } + }; + } + return null; + } + + public string[] PropertyNames + { + get + { + CheckInitProperties(); + return _propertyNames; + } + } + + public Type GetPropertyType(String property) + { + if (_underlyingEventType.IsProperty(property)) + { + return _underlyingEventType.GetPropertyType(property); + } + if (_underlyingMapType.IsProperty(property)) + { + return _underlyingMapType.GetPropertyType(property); + } + return null; + } + + public EventBeanReader Reader + { + get { return null; } + } + + public EventType[] SuperTypes + { + get { return null; } + } + + public Type UnderlyingType + { + get + { + // If the additional properties are empty, such as when wrapping a native event by means of wildcard-only select + // then the underlying type is simply the wrapped type. + if (_isNoMapProperties) + { + return _underlyingEventType.UnderlyingType; + } + else + { + return typeof(Pair); + } + } + } + + /// Returns the wrapped event type. + /// wrapped type + public EventType UnderlyingEventType + { + get { return _underlyingEventType; } + } + + /// Returns the map type. + /// map type providing additional properties. + public MapEventType UnderlyingMapType + { + get { return _underlyingMapType; } + } + + public bool IsProperty(String property) + { + return _underlyingEventType.IsProperty(property) || + _underlyingMapType.IsProperty(property); + } + + public override String ToString() + { + return "WrapperEventType " + + "underlyingEventType=" + _underlyingEventType + " " + + "underlyingMapType=" + _underlyingMapType; + } + + public bool EqualsCompareType(EventType otherEventType) + { + if (this == otherEventType) + { + return true; + } + + if (!(otherEventType is WrapperEventType)) + { + return false; + } + + var other = (WrapperEventType)otherEventType; + if (!other._underlyingMapType.EqualsCompareType(_underlyingMapType)) + { + return false; + } + + if (!(other._underlyingEventType is EventTypeSPI) || (!(_underlyingEventType is EventTypeSPI))) + { + return other._underlyingEventType.Equals(_underlyingEventType); + } + + var otherUnderlying = (EventTypeSPI)other._underlyingEventType; + var thisUnderlying = (EventTypeSPI)_underlyingEventType; + return otherUnderlying.EqualsCompareType(thisUnderlying); + } + + public EventTypeMetadata Metadata + { + get { return _metadata; } + } + + public IList PropertyDescriptors + { + get + { + CheckInitProperties(); + return _propertyDesc; + } + } + + public EventPropertyDescriptor GetPropertyDescriptor(String propertyName) + { + CheckInitProperties(); + return _propertyDescriptorMap.Get(propertyName); + } + + public FragmentEventType GetFragmentType(String property) + { + FragmentEventType fragment = _underlyingEventType.GetFragmentType(property); + if (fragment != null) + { + return fragment; + } + return _underlyingMapType.GetFragmentType(property); + } + + public EventPropertyWriter GetWriter(String propertyName) + { + if (_writableProperties == null) + { + InitializeWriters(); + } + Pair pair = _writers.Get(propertyName); + if (pair == null) + { + return null; + } + return pair.Second; + } + + public EventPropertyDescriptor GetWritableProperty(String propertyName) + { + if (_writableProperties == null) + { + InitializeWriters(); + } + Pair pair = _writers.Get(propertyName); + if (pair == null) + { + return null; + } + return pair.First; + } + + public EventPropertyDescriptor[] WriteableProperties + { + get + { + if (_writableProperties == null) + { + InitializeWriters(); + } + return _writableProperties; + } + } + + private void InitializeWriters() + { + var writables = new List(); + var writerMap = new Dictionary>(); + writables.AddAll(_underlyingMapType.WriteableProperties); + + foreach (EventPropertyDescriptor writableMapProp in _underlyingMapType.WriteableProperties) + { + var propertyName = writableMapProp.PropertyName; + writables.Add(writableMapProp); + var writer = new ProxyEventPropertyWriter + { + ProcWrite = (value, target) => + { + var decorated = (DecoratingEventBean)target; + decorated.DecoratingProperties.Put(propertyName, value); + } + }; + writerMap.Put(propertyName, new Pair(writableMapProp, writer)); + } + + if (_underlyingEventType is EventTypeSPI) + { + var spi = (EventTypeSPI)_underlyingEventType; + foreach (EventPropertyDescriptor writableUndProp in spi.WriteableProperties) + { + var propertyName = writableUndProp.PropertyName; + var innerWriter = spi.GetWriter(propertyName); + if (innerWriter == null) + { + continue; + } + + writables.Add(writableUndProp); + var writer = new ProxyEventPropertyWriter + { + ProcWrite = (value, target) => + { + var decorated = (DecoratingEventBean)target; + innerWriter.Write(value, decorated.UnderlyingEvent); + } + }; + writerMap.Put(propertyName, new Pair(writableUndProp, writer)); + } + } + + _writers = writerMap; + _writableProperties = writables.ToArray(); + } + + public EventBeanCopyMethod GetCopyMethod(String[] properties) + { + if (_writableProperties == null) + { + InitializeWriters(); + } + + bool isOnlyMap = true; + for (int i = 0; i < properties.Length; i++) + { + if (_underlyingMapType.GetWritableProperty(properties[i]) == null) + { + isOnlyMap = false; + } + } + + bool isOnlyUnderlying = true; + if (!isOnlyMap) + { + if (!(_underlyingEventType is EventTypeSPI)) + { + return null; + } + var spi = (EventTypeSPI)_underlyingEventType; + for (int i = 0; i < properties.Length; i++) + { + if (spi.GetWritableProperty(properties[i]) == null) + { + isOnlyUnderlying = false; + } + } + } + + if (isOnlyMap) + { + return new WrapperEventBeanMapCopyMethod(this, _eventAdapterService); + } + + EventBeanCopyMethod undCopyMethod = ((EventTypeSPI)_underlyingEventType).GetCopyMethod(properties); + if (undCopyMethod == null) + { + return null; + } + if (isOnlyUnderlying) + { + return new WrapperEventBeanUndCopyMethod(this, _eventAdapterService, undCopyMethod); + } + + return new WrapperEventBeanCopyMethod(this, _eventAdapterService, undCopyMethod); + } + + public EventBeanWriter GetWriter(String[] properties) + { + if (_writableProperties == null) + { + InitializeWriters(); + } + + bool isOnlyMap = true; + for (int i = 0; i < properties.Length; i++) + { + if (!_writers.ContainsKey(properties[i])) + { + return null; + } + if (_underlyingMapType.GetWritableProperty(properties[i]) == null) + { + isOnlyMap = false; + } + } + + bool isOnlyUnderlying = true; + if (!isOnlyMap) + { + var spi = (EventTypeSPI)_underlyingEventType; + for (int i = 0; i < properties.Length; i++) + { + if (spi.GetWritableProperty(properties[i]) == null) + { + isOnlyUnderlying = false; + } + } + } + + if (isOnlyMap) + { + return new WrapperEventBeanMapWriter(properties); + } + if (isOnlyUnderlying) + { + var spi = (EventTypeSPI)_underlyingEventType; + var undWriter = spi.GetWriter(properties); + if (undWriter == null) + { + return undWriter; + } + return new WrapperEventBeanUndWriter(undWriter); + } + + var writerArr = new EventPropertyWriter[properties.Length]; + for (int i = 0; i < properties.Length; i++) + { + writerArr[i] = _writers.Get(properties[i]).Second; + } + return new WrapperEventBeanPropertyWriter(writerArr); + } + + private static void CheckForRepeatedPropertyNames(EventType eventType, IDictionary properties) + { + foreach (String property in eventType.PropertyNames) + { + if (properties.Keys.Contains(property)) + { + throw new EPException("Property " + property + " occurs in both the underlying event and in the additional properties"); + } + } + } + + public class PropertyDescriptorComposite + { + public PropertyDescriptorComposite(Dictionary propertyDescriptorMap, String[] propertyNames, EventPropertyDescriptor[] descriptors) + { + PropertyDescriptorMap = propertyDescriptorMap; + PropertyNames = propertyNames; + Descriptors = descriptors; + } + + public Dictionary PropertyDescriptorMap { get; private set; } + + public string[] PropertyNames { get; private set; } + + public EventPropertyDescriptor[] Descriptors { get; private set; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/WriteablePropertyDescriptor.cs b/NEsper.Core/NEsper.Core/events/WriteablePropertyDescriptor.cs new file mode 100755 index 000000000..618730306 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/WriteablePropertyDescriptor.cs @@ -0,0 +1,76 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +namespace com.espertech.esper.events +{ + /// Descriptor for writable properties. + public class WriteablePropertyDescriptor + { + /// Ctor. + /// name of property + /// type + /// optional write methods + public WriteablePropertyDescriptor(String propertyName, Type type, MethodInfo writeMethod) + { + PropertyName = propertyName; + PropertyType = type; + WriteMethod = writeMethod; + } + + /// Returns property name. + /// property name + public string PropertyName { get; private set; } + + /// Returns property type. + /// property type + public Type PropertyType { get; private set; } + + /// Returns write methods. + /// write methods + public MethodInfo WriteMethod { get; private set; } + + public bool Equals(WriteablePropertyDescriptor other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return Equals(other.PropertyName, PropertyName); + } + + /// + /// Determines whether the specified is equal to the current . + /// + /// + /// true if the specified is equal to the current ; otherwise, false. + /// + /// The to compare with the current . + /// The parameter is null. + /// 2 + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != typeof (WriteablePropertyDescriptor)) return false; + return Equals((WriteablePropertyDescriptor) obj); + } + + /// + /// Serves as a hash function for a particular type. + /// + /// + /// A hash code for the current . + /// + /// 2 + public override int GetHashCode() + { + return (PropertyName != null ? PropertyName.GetHashCode() : 0); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/arr/EventTypeNestableGetterFactoryObjectArray.cs b/NEsper.Core/NEsper.Core/events/arr/EventTypeNestableGetterFactoryObjectArray.cs new file mode 100755 index 000000000..b00215bc6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/arr/EventTypeNestableGetterFactoryObjectArray.cs @@ -0,0 +1,169 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.events.bean; +using com.espertech.esper.events.map; +using com.espertech.esper.events.property; + +namespace com.espertech.esper.events.arr +{ + public class EventTypeNestableGetterFactoryObjectArray : EventTypeNestableGetterFactory + { + private readonly String _eventTypeName; + + public EventTypeNestableGetterFactoryObjectArray(String eventTypeName, IDictionary propertiesIndex) + { + _eventTypeName = eventTypeName; + PropertiesIndex = propertiesIndex; + } + + public IDictionary PropertiesIndex { get; private set; } + + public EventPropertyGetter GetPropertyProvidedGetter(IDictionary nestableTypes, String propertyName, Property prop, EventAdapterService eventAdapterService) + { + return prop.GetGetterObjectArray(PropertiesIndex, nestableTypes, eventAdapterService); + } + + public EventPropertyGetter GetGetterProperty(String name, BeanEventType nativeFragmentType, EventAdapterService eventAdapterService) + { + int index = GetAssertIndex(name); + return new ObjectArrayEntryPropertyGetter(index, nativeFragmentType, eventAdapterService); + } + + public EventPropertyGetter GetGetterEventBean(String name) + { + int index = GetAssertIndex(name); + return new ObjectArrayEventBeanPropertyGetter(index); + } + + public EventPropertyGetter GetGetterEventBeanArray(String name, EventType eventType) + { + int index = GetAssertIndex(name); + return new ObjectArrayEventBeanArrayPropertyGetter(index, eventType.UnderlyingType); + } + + public EventPropertyGetter GetGetterBeanNestedArray(String name, EventType eventType, EventAdapterService eventAdapterService) + { + int index = GetAssertIndex(name); + return new ObjectArrayFragmentArrayPropertyGetter(index, eventType, eventAdapterService); + } + + public EventPropertyGetter GetGetterIndexedEventBean(String propertyNameAtomic, int index) + { + int propertyIndex = GetAssertIndex(propertyNameAtomic); + return new ObjectArrayEventBeanArrayIndexedPropertyGetter(propertyIndex, index); + } + + public EventPropertyGetter GetGetterIndexedUnderlyingArray(String propertyNameAtomic, int index, EventAdapterService eventAdapterService, EventType innerType) + { + int propertyIndex = GetAssertIndex(propertyNameAtomic); + return new ObjectArrayArrayPropertyGetter(propertyIndex, index, eventAdapterService, innerType); + } + + public EventPropertyGetter GetGetterIndexedPONO(String propertyNameAtomic, int index, EventAdapterService eventAdapterService, Type componentType) + { + int propertyIndex = GetAssertIndex(propertyNameAtomic); + return new ObjectArrayArrayPONOEntryIndexedPropertyGetter(propertyIndex, index, eventAdapterService, componentType); + } + + public EventPropertyGetter GetGetterMappedProperty(String propertyNameAtomic, String key) + { + int propertyIndex = GetAssertIndex(propertyNameAtomic); + return new ObjectArrayMappedPropertyGetter(propertyIndex, key); + } + + public EventPropertyGetter GetGetterIndexedEntryEventBeanArrayElement(String propertyNameAtomic, int index, EventPropertyGetter nestedGetter) + { + int propertyIndex = GetAssertIndex(propertyNameAtomic); + return new ObjectArrayEventBeanArrayIndexedElementPropertyGetter(propertyIndex, index, nestedGetter); + } + + public EventPropertyGetter GetGetterIndexedEntryPONO(String propertyNameAtomic, int index, BeanEventPropertyGetter nestedGetter, EventAdapterService eventAdapterService, Type propertyTypeGetter) + { + int propertyIndex = GetAssertIndex(propertyNameAtomic); + return new ObjectArrayArrayPONOBeanEntryIndexedPropertyGetter(propertyIndex, index, nestedGetter, eventAdapterService, propertyTypeGetter); + } + + public EventPropertyGetter GetGetterNestedMapProp(String propertyName, MapEventPropertyGetter getterNested) + { + int index = GetAssertIndex(propertyName); + return new ObjectArrayMapPropertyGetter(index, getterNested); + } + + public EventPropertyGetter GetGetterNestedPONOProp(String propertyName, BeanEventPropertyGetter nestedGetter, EventAdapterService eventAdapterService, Type nestedReturnType, Type nestedComponentType) + { + int index = GetAssertIndex(propertyName); + return new ObjectArrayPONOEntryPropertyGetter(index, nestedGetter, eventAdapterService, nestedReturnType, nestedComponentType); + } + + public EventPropertyGetter GetGetterNestedEventBean(String propertyName, EventPropertyGetter nestedGetter) + { + int index = GetAssertIndex(propertyName); + return new ObjectArrayEventBeanEntryPropertyGetter(index, nestedGetter); + } + + public EventPropertyGetter GetGetterNestedEntryBean(String propertyName, EventPropertyGetter getter, EventType innerType, EventAdapterService eventAdapterService) + { + int propertyIndex = GetAssertIndex(propertyName); + if (getter is ObjectArrayEventPropertyGetter) + { + return new ObjectArrayNestedEntryPropertyGetterObjectArray(propertyIndex, innerType, eventAdapterService, (ObjectArrayEventPropertyGetter)getter); + } + return new ObjectArrayNestedEntryPropertyGetterMap(propertyIndex, innerType, eventAdapterService, (MapEventPropertyGetter)getter); + } + + public EventPropertyGetter GetGetterNestedEntryBeanArray(String propertyNameAtomic, int index, EventPropertyGetter getter, EventType innerType, EventAdapterService eventAdapterService) + { + int propertyIndex = GetAssertIndex(propertyNameAtomic); + if (getter is ObjectArrayEventPropertyGetter) + { + return new ObjectArrayNestedEntryPropertyGetterArrayObjectArray(propertyIndex, innerType, eventAdapterService, index, (ObjectArrayEventPropertyGetter)getter); + } + return new ObjectArrayNestedEntryPropertyGetterArrayMap(propertyIndex, innerType, eventAdapterService, index, (MapEventPropertyGetter)getter); + } + + public EventPropertyGetter GetGetterBeanNested(String name, EventType eventType, EventAdapterService eventAdapterService) + { + int index = GetAssertIndex(name); + if (eventType is ObjectArrayEventType) + { + return new ObjectArrayPropertyGetterDefaultObjectArray(index, eventType, eventAdapterService); + } + return new ObjectArrayPropertyGetterDefaultMap(index, eventType, eventAdapterService); + } + + private int GetAssertIndex(String propertyName) + { + int index; + if (!PropertiesIndex.TryGetValue(propertyName, out index)) + { + throw new PropertyAccessException("Property '" + propertyName + "' could not be found as a property of type '" + _eventTypeName + "'"); + } + return index; + } + + public EventPropertyGetterMapped GetPropertyProvidedGetterMap(IDictionary nestableTypes, String mappedPropertyName, MappedProperty mappedProperty, EventAdapterService eventAdapterService) + { + return (EventPropertyGetterMapped)mappedProperty.GetGetterObjectArray(PropertiesIndex, nestableTypes, eventAdapterService); + } + + public EventPropertyGetterIndexed GetPropertyProvidedGetterIndexed(IDictionary nestableTypes, String indexedPropertyName, IndexedProperty indexedProperty, EventAdapterService eventAdapterService) + { + return (EventPropertyGetterIndexed)indexedProperty.GetGetterObjectArray(PropertiesIndex, nestableTypes, eventAdapterService); + } + + public EventPropertyGetter GetGetterNestedPropertyProvidedGetterDynamic(IDictionary nestableTypes, String propertyName, EventPropertyGetter nestedGetter, EventAdapterService eventAdapterService) + { + return null; // this case is not supported + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/arr/ObjectArrayArrayPONOBeanEntryIndexedPropertyGetter.cs b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayArrayPONOBeanEntryIndexedPropertyGetter.cs new file mode 100755 index 000000000..d43362233 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayArrayPONOBeanEntryIndexedPropertyGetter.cs @@ -0,0 +1,73 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.events.bean; + +namespace com.espertech.esper.events.arr +{ + /// + /// A getter that works on PONO events residing within a Map as an event property. + /// + public class ObjectArrayArrayPONOBeanEntryIndexedPropertyGetter + : BaseNativePropertyGetter + , + ObjectArrayEventPropertyGetter + { + private readonly int _index; + private readonly BeanEventPropertyGetter _nestedGetter; + private readonly int _propertyIndex; + + /// Ctor. + /// the property to look at + /// the getter for the map entry + /// for producing wrappers to objects + /// the index to fetch the array element for + /// type of the entry returned + public ObjectArrayArrayPONOBeanEntryIndexedPropertyGetter(int propertyIndex, + int index, + BeanEventPropertyGetter nestedGetter, + EventAdapterService eventAdapterService, + Type returnType) + : base(eventAdapterService, returnType, null) + { + _propertyIndex = propertyIndex; + _index = index; + _nestedGetter = nestedGetter; + } + + #region ObjectArrayEventPropertyGetter Members + + public Object GetObjectArray(Object[] array) + { + // If the map does not contain the key, this is allowed and represented as null + Object value = array[_propertyIndex]; + return BaseNestableEventUtil.GetBeanArrayValue(_nestedGetter, value, _index); + } + + public bool IsObjectArrayExistsProperty(Object[] array) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + + public override Object Get(EventBean eventBean) + { + Object[] array = BaseNestableEventUtil.CheckedCastUnderlyingObjectArray(eventBean); + return GetObjectArray(array); + } + + public override bool IsExistsProperty(EventBean eventBean) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + + #endregion + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/events/arr/ObjectArrayArrayPONOEntryIndexedPropertyGetter.cs b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayArrayPONOEntryIndexedPropertyGetter.cs new file mode 100755 index 000000000..08fe1469c --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayArrayPONOEntryIndexedPropertyGetter.cs @@ -0,0 +1,82 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using com.espertech.esper.client; +using com.espertech.esper.events.bean; + +namespace com.espertech.esper.events.arr +{ + /// + /// A getter that works on arrays residing within a Map as an event property. + /// + public class ObjectArrayArrayPONOEntryIndexedPropertyGetter + : BaseNativePropertyGetter + , + ObjectArrayEventPropertyGetterAndIndexed + { + private readonly int _index; + private readonly int _propertyIndex; + + /// + /// Ctor. + /// + /// MapIndex of the property. + /// the index to fetch the array element for + /// factory for event beans and event types + /// type of the entry returned + public ObjectArrayArrayPONOEntryIndexedPropertyGetter(int propertyIndex, + int index, + EventAdapterService eventAdapterService, + Type returnType) + : base(eventAdapterService, returnType, null) + { + _propertyIndex = propertyIndex; + _index = index; + } + + #region ObjectArrayEventPropertyGetterAndIndexed Members + + public Object GetObjectArray(Object[] array) + { + return GetArrayInternal(array, _index); + } + + public bool IsObjectArrayExistsProperty(Object[] array) + { + return array.Length > _index; + } + + public Object Get(EventBean eventBean, int index) + { + Object[] array = BaseNestableEventUtil.CheckedCastUnderlyingObjectArray(eventBean); + return GetArrayInternal(array, index); + } + + public override Object Get(EventBean eventBean) + { + Object[] array = BaseNestableEventUtil.CheckedCastUnderlyingObjectArray(eventBean); + return GetObjectArray(array); + } + + public override bool IsExistsProperty(EventBean eventBean) + { + Object[] array = BaseNestableEventUtil.CheckedCastUnderlyingObjectArray(eventBean); + return array.Length > _index; + } + + #endregion + + public Object GetArrayInternal(Object[] array, int index) + { + // If the map does not contain the key, this is allowed and represented as null + Object value = array[_propertyIndex]; + return BaseNestableEventUtil.GetIndexedValue(value, index); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/events/arr/ObjectArrayArrayPropertyGetter.cs b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayArrayPropertyGetter.cs new file mode 100755 index 000000000..35f646a39 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayArrayPropertyGetter.cs @@ -0,0 +1,77 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.arr +{ + /// + /// Getter for Map-entries with well-defined fragment type. + /// + public class ObjectArrayArrayPropertyGetter : ObjectArrayEventPropertyGetterAndIndexed + { + private readonly int _propertyIndex; + private readonly int _index; + private readonly EventAdapterService _eventAdapterService; + private readonly EventType _fragmentType; + + /// Ctor. + /// property index + /// array index + /// factory for event beans and event types + /// type of the entry returned + public ObjectArrayArrayPropertyGetter(int propertyIndex, int index, EventAdapterService eventAdapterService, EventType fragmentType) + { + _propertyIndex = propertyIndex; + _index = index; + _fragmentType = fragmentType; + _eventAdapterService = eventAdapterService; + } + + public bool IsObjectArrayExistsProperty(Object[] array) + { + return true; + } + + public Object GetObjectArray(Object[] array) + { + return GetObjectArrayInternal(array, _index); + } + + public Object Get(EventBean eventBean, int index) + { + Object[] array = BaseNestableEventUtil.CheckedCastUnderlyingObjectArray(eventBean); + return GetObjectArrayInternal(array, index); + } + + public Object Get(EventBean eventBean) + { + Object[] array = BaseNestableEventUtil.CheckedCastUnderlyingObjectArray(eventBean); + return GetObjectArray(array); + } + + private Object GetObjectArrayInternal(Object[] array, int index) + { + Object value = array[_propertyIndex]; + return BaseNestableEventUtil.GetIndexedValue(value, index); + } + + public bool IsExistsProperty(EventBean eventBean) + { + return true; + } + + public Object GetFragment(EventBean obj) + { + Object fragmentUnderlying = Get(obj); + return BaseNestableEventUtil.GetFragmentNonPono(_eventAdapterService, fragmentUnderlying, _fragmentType); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/arr/ObjectArrayDynamicPropertyGetter.cs b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayDynamicPropertyGetter.cs new file mode 100755 index 000000000..3df71e667 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayDynamicPropertyGetter.cs @@ -0,0 +1,68 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.arr +{ + /// + /// Getter for a dynamic property (syntax field.inner?), using vanilla reflection. + /// + public class ObjectArrayDynamicPropertyGetter : ObjectArrayEventPropertyGetter + { + private readonly string _propertyName; + + public ObjectArrayDynamicPropertyGetter(string propertyName) + { + _propertyName = propertyName; + } + + public Object GetObjectArray(Object[] array) + { + return null; + } + + public bool IsObjectArrayExistsProperty(Object[] array) + { + return false; + } + + public Object Get(EventBean eventBean) + { + int index; + + var objectArrayEventType = (ObjectArrayEventType)eventBean.EventType; + if (!objectArrayEventType.PropertiesIndexes.TryGetValue(_propertyName, out index)) + { + return null; + } + + return ((Object[]) eventBean.Underlying)[index]; + } + + public bool IsExistsProperty(EventBean eventBean) + { + int index; + + var objectArrayEventType = (ObjectArrayEventType)eventBean.EventType; + if (objectArrayEventType.PropertiesIndexes.TryGetValue(_propertyName, out index)) + { + return true; + } + + return false; + } + + public Object GetFragment(EventBean eventBean) + { + return null; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/events/arr/ObjectArrayEntryPropertyGetter.cs b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayEntryPropertyGetter.cs new file mode 100755 index 000000000..d53c4fba9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayEntryPropertyGetter.cs @@ -0,0 +1,73 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.events.bean; + +namespace com.espertech.esper.events.arr +{ + /// + /// A getter for use with Map-based events simply returns the value for the key. + /// + public class ObjectArrayEntryPropertyGetter : ObjectArrayEventPropertyGetter + { + private readonly EventAdapterService _eventAdapterService; + private readonly BeanEventType _eventType; + private readonly int _propertyIndex; + + /// Ctor. + /// index + /// type of the entry returned + /// factory for event beans and event types + public ObjectArrayEntryPropertyGetter(int propertyIndex, + BeanEventType eventType, + EventAdapterService eventAdapterService) + { + _propertyIndex = propertyIndex; + _eventAdapterService = eventAdapterService; + _eventType = eventType; + } + + #region ObjectArrayEventPropertyGetter Members + + public Object GetObjectArray(Object[] array) + { + return array[_propertyIndex]; + } + + public bool IsObjectArrayExistsProperty(Object[] array) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + + public Object Get(EventBean eventBean) + { + Object[] arr = BaseNestableEventUtil.CheckedCastUnderlyingObjectArray(eventBean); + return GetObjectArray(arr); + } + + public bool IsExistsProperty(EventBean eventBean) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + + public Object GetFragment(EventBean eventBean) + { + if (_eventType == null) + { + return null; + } + Object result = Get(eventBean); + return BaseNestableEventUtil.GetFragmentPono(result, _eventType, _eventAdapterService); + } + + #endregion + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/events/arr/ObjectArrayEventBean.cs b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayEventBean.cs new file mode 100755 index 000000000..122b18655 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayEventBean.cs @@ -0,0 +1,88 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.arr +{ + public class ObjectArrayEventBean + : EventBeanSPI + , ObjectArrayBackedEventBean + { + private Object[] _propertyValues; + private EventType _eventType; + + public ObjectArrayEventBean(Object[] propertyValues, EventType eventType) + { + _propertyValues = propertyValues; + _eventType = eventType; + } + + public EventType EventType + { + get { return _eventType; } + set { _eventType = value; } + } + + public object[] Properties + { + get { return _propertyValues; } + } + + public object[] PropertyValues + { + set { _propertyValues = value; } + } + + /// + /// Gets the specified property. + /// + /// The property. + /// + public Object Get(String property) + { + var getter = _eventType.GetGetter(property); + if (getter == null) + { + throw new PropertyAccessException("Property named '" + property + "' is not a valid property name for this type"); + } + return getter.Get(this); + } + + /// + /// Returns the value of an event property for the given property name or property expression. + /// Returns null if the property value is null. Throws an exception if the expression is not valid against the event type. + /// The method takes a property name or property expression as a parameter. Property expressions may include indexed properties + /// via the syntax "name[index]", mapped properties via the syntax "name('key')", nested properties via the syntax "outer.inner" or + /// combinations thereof. + /// + /// + public object this[string property] + { + get { return Get(property); } + } + + public object Underlying + { + get { return _propertyValues; } + set { _propertyValues = (Object[])value; } + } + + public Object GetFragment(String propertyExpression) + { + var getter = _eventType.GetGetter(propertyExpression); + if (getter == null) + { + throw PropertyAccessException.NotAValidProperty(propertyExpression); + } + return getter.GetFragment(this); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/arr/ObjectArrayEventBeanArrayIndexedElementPropertyGetter.cs b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayEventBeanArrayIndexedElementPropertyGetter.cs new file mode 100755 index 000000000..bf59a4a0d --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayEventBeanArrayIndexedElementPropertyGetter.cs @@ -0,0 +1,64 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.arr +{ + /// + /// Getter for an array of event bean using a nested getter. + /// + public class ObjectArrayEventBeanArrayIndexedElementPropertyGetter + : ObjectArrayEventPropertyGetter + { + private readonly int _propertyIndex; + private readonly int _index; + private readonly EventPropertyGetter _nestedGetter; + + /// Ctor. + /// property index + /// array index + /// nested getter + public ObjectArrayEventBeanArrayIndexedElementPropertyGetter(int propertyIndex, int index, EventPropertyGetter nestedGetter) + { + _propertyIndex = propertyIndex; + _index = index; + _nestedGetter = nestedGetter; + } + + public Object GetObjectArray(Object[] array) + { + // If the map does not contain the key, this is allowed and represented as null + EventBean[] wrapper = (EventBean[]) array[_propertyIndex]; + return BaseNestableEventUtil.GetArrayPropertyValue(wrapper, _index, _nestedGetter); + } + + public bool IsObjectArrayExistsProperty(Object[] array) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + + public Object Get(EventBean eventBean) + { + return GetObjectArray(BaseNestableEventUtil.CheckedCastUnderlyingObjectArray(eventBean)); + } + + public bool IsExistsProperty(EventBean eventBean) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + + public Object GetFragment(EventBean obj) + { + EventBean[] wrapper = (EventBean[]) BaseNestableEventUtil.CheckedCastUnderlyingObjectArray(obj)[_propertyIndex]; + return BaseNestableEventUtil.GetArrayPropertyFragment(wrapper, _index, _nestedGetter); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/arr/ObjectArrayEventBeanArrayIndexedPropertyGetter.cs b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayEventBeanArrayIndexedPropertyGetter.cs new file mode 100755 index 000000000..31c181bfc --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayEventBeanArrayIndexedPropertyGetter.cs @@ -0,0 +1,60 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.arr +{ + /// Getter for array events. + public class ObjectArrayEventBeanArrayIndexedPropertyGetter : ObjectArrayEventPropertyGetter + { + private readonly int _propertyIndex; + private readonly int _index; + + /// Ctor. + /// property index + /// array index + public ObjectArrayEventBeanArrayIndexedPropertyGetter(int propertyIndex, int index) + { + _propertyIndex = propertyIndex; + _index = index; + } + + public Object GetObjectArray(Object[] array) + { + // If the map does not contain the key, this is allowed and represented as null + var wrapper = (EventBean[]) array[_propertyIndex]; + return BaseNestableEventUtil.GetArrayPropertyUnderlying(wrapper, _index); + } + + public bool IsObjectArrayExistsProperty(Object[] array) + { + return true; + } + + public Object Get(EventBean eventBean) + { + Object[] array = BaseNestableEventUtil.CheckedCastUnderlyingObjectArray(eventBean); + return GetObjectArray(array); + } + + public bool IsExistsProperty(EventBean eventBean) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + + public Object GetFragment(EventBean obj) + { + Object[] array = BaseNestableEventUtil.CheckedCastUnderlyingObjectArray(obj); + EventBean[] wrapper = (EventBean[]) array[_propertyIndex]; + return BaseNestableEventUtil.GetArrayPropertyBean(wrapper, _index); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/arr/ObjectArrayEventBeanArrayPropertyGetter.cs b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayEventBeanArrayPropertyGetter.cs new file mode 100755 index 000000000..a4b779b13 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayEventBeanArrayPropertyGetter.cs @@ -0,0 +1,60 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.arr +{ + /// + /// Returns the event bean or the underlying array. + /// + public class ObjectArrayEventBeanArrayPropertyGetter : ObjectArrayEventPropertyGetter + { + private readonly int _propertyIndex; + private readonly Type _underlyingType; + + /// Ctor. + /// property to get + /// type of property + public ObjectArrayEventBeanArrayPropertyGetter(int propertyIndex, Type underlyingType) + { + _propertyIndex = propertyIndex; + _underlyingType = underlyingType; + } + + public Object GetObjectArray(Object[] arrayEvent) + { + Object innerValue = arrayEvent[_propertyIndex]; + return BaseNestableEventUtil.GetArrayPropertyAsUnderlyingsArray(_underlyingType, (EventBean[]) innerValue); + } + + public bool IsObjectArrayExistsProperty(Object[] array) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + + public Object Get(EventBean eventBean) + { + Object[] array = BaseNestableEventUtil.CheckedCastUnderlyingObjectArray(eventBean); + return GetObjectArray(array); + } + + public bool IsExistsProperty(EventBean eventBean) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + + public Object GetFragment(EventBean obj) + { + Object[] array = BaseNestableEventUtil.CheckedCastUnderlyingObjectArray(obj); + return array[_propertyIndex]; + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/arr/ObjectArrayEventBeanCopyMethod.cs b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayEventBeanCopyMethod.cs new file mode 100755 index 000000000..8b5224c3a --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayEventBeanCopyMethod.cs @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.arr +{ + /// + /// Copy method for Object array-underlying events. + /// + public class ObjectArrayEventBeanCopyMethod : EventBeanCopyMethod + { + private readonly ObjectArrayEventType _objectArrayEventType; + private readonly EventAdapterService _eventAdapterService; + + /// Ctor. + /// map event type + /// for copying events + public ObjectArrayEventBeanCopyMethod(ObjectArrayEventType objectArrayEventType, EventAdapterService eventAdapterService) + { + _objectArrayEventType = objectArrayEventType; + _eventAdapterService = eventAdapterService; + } + + public EventBean Copy(EventBean theEvent) + { + Object[] array = ((ObjectArrayBackedEventBean) theEvent).Properties; + Object[] copy = new Object[array.Length]; + Array.Copy(array, 0, copy, 0, copy.Length); + return _eventAdapterService.AdapterForTypedObjectArray(copy, _objectArrayEventType); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/arr/ObjectArrayEventBeanCopyMethodWithArrayMap.cs b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayEventBeanCopyMethodWithArrayMap.cs new file mode 100755 index 000000000..998733ebf --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayEventBeanCopyMethodWithArrayMap.cs @@ -0,0 +1,91 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.events.arr +{ + /// + /// Copy method for Map-underlying events. + /// + public class ObjectArrayEventBeanCopyMethodWithArrayMap : EventBeanCopyMethod + { + private readonly ICollection _arrayIndexesToCopy; + private readonly EventAdapterService _eventAdapterService; + private readonly ObjectArrayEventType _eventType; + private readonly ICollection _mapIndexesToCopy; + + public ObjectArrayEventBeanCopyMethodWithArrayMap(ObjectArrayEventType eventType, + EventAdapterService eventAdapterService, + ICollection mapPropertiesToCopy, + ICollection arrayPropertiesToCopy, + IDictionary propertiesIndexes) + { + _eventType = eventType; + _eventAdapterService = eventAdapterService; + + _mapIndexesToCopy = new HashSet(); + foreach (String prop in mapPropertiesToCopy) + { + int index; + + if (propertiesIndexes.TryGetValue(prop, out index)) + { + _mapIndexesToCopy.Add(index); + } + } + + _arrayIndexesToCopy = new HashSet(); + foreach (String prop in arrayPropertiesToCopy) + { + int? index = propertiesIndexes.Get(prop); + if (index != null) + { + _arrayIndexesToCopy.Add(index.Value); + } + } + } + + #region EventBeanCopyMethod Members + + public EventBean Copy(EventBean theEvent) + { + var arrayBacked = (ObjectArrayBackedEventBean) theEvent; + object[] props = arrayBacked.Properties; + var shallowCopy = new Object[props.Length]; + Array.Copy(props, 0, shallowCopy, 0, props.Length); + + foreach (int index in _mapIndexesToCopy) + { + var innerMap = (IDictionary) shallowCopy[index]; + if (innerMap != null) + { + var copy = new Dictionary(innerMap); + shallowCopy[index] = copy; + } + } + + foreach (int index in _arrayIndexesToCopy) + { + var array = shallowCopy[index] as Array; + if (array != null && array.Length != 0) + { + Array copied = Array.CreateInstance(array.GetType().GetElementType(), array.Length); + Array.Copy(array, 0, copied, 0, array.Length); + shallowCopy[index] = copied; + } + } + return _eventAdapterService.AdapterForTypedObjectArray(shallowCopy, _eventType); + } + + #endregion + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/events/arr/ObjectArrayEventBeanEntryPropertyGetter.cs b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayEventBeanEntryPropertyGetter.cs new file mode 100755 index 000000000..ab460f095 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayEventBeanEntryPropertyGetter.cs @@ -0,0 +1,81 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.arr +{ + /// + /// A getter that works on EventBean events residing within a Map as an event property. + /// + public class ObjectArrayEventBeanEntryPropertyGetter : ObjectArrayEventPropertyGetter + { + private readonly EventPropertyGetter _eventBeanEntryGetter; + private readonly int _propertyIndex; + + /// Ctor. + /// the property to look at + /// the getter for the map entry + public ObjectArrayEventBeanEntryPropertyGetter(int propertyIndex, EventPropertyGetter eventBeanEntryGetter) + { + _propertyIndex = propertyIndex; + _eventBeanEntryGetter = eventBeanEntryGetter; + } + + #region ObjectArrayEventPropertyGetter Members + + public Object GetObjectArray(Object[] array) + { + // If the map does not contain the key, this is allowed and represented as null + Object value = array[_propertyIndex]; + + if (value == null) + { + return null; + } + + // Object within the map + var theEvent = (EventBean) value; + return _eventBeanEntryGetter.Get(theEvent); + } + + public bool IsObjectArrayExistsProperty(Object[] array) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + + public Object Get(EventBean eventBean) + { + return GetObjectArray(BaseNestableEventUtil.CheckedCastUnderlyingObjectArray(eventBean)); + } + + public bool IsExistsProperty(EventBean eventBean) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + + public Object GetFragment(EventBean obj) + { + // If the map does not contain the key, this is allowed and represented as null + Object value = BaseNestableEventUtil.CheckedCastUnderlyingObjectArray(obj)[_propertyIndex]; + + if (value == null) + { + return null; + } + + // Object within the map + var theEvent = (EventBean) value; + return _eventBeanEntryGetter.GetFragment(theEvent); + } + + #endregion + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/events/arr/ObjectArrayEventBeanPropertyGetter.cs b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayEventBeanPropertyGetter.cs new file mode 100755 index 000000000..e2b00e895 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayEventBeanPropertyGetter.cs @@ -0,0 +1,65 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.arr +{ + /// + /// A getter for use with Map-based events simply returns the value for the key. + /// + public class ObjectArrayEventBeanPropertyGetter : ObjectArrayEventPropertyGetter + { + private readonly int _propertyIndex; + + /// Ctor. + /// property to get + public ObjectArrayEventBeanPropertyGetter(int propertyIndex) + { + _propertyIndex = propertyIndex; + } + + #region ObjectArrayEventPropertyGetter Members + + public Object GetObjectArray(Object[] array) + { + Object eventBean = array[_propertyIndex]; + if (eventBean == null) + { + return null; + } + + var theEvent = (EventBean) eventBean; + return theEvent.Underlying; + } + + public bool IsObjectArrayExistsProperty(Object[] array) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + + public Object Get(EventBean eventBean) + { + return GetObjectArray(BaseNestableEventUtil.CheckedCastUnderlyingObjectArray(eventBean)); + } + + public bool IsExistsProperty(EventBean eventBean) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + + public Object GetFragment(EventBean obj) + { + return BaseNestableEventUtil.CheckedCastUnderlyingObjectArray(obj)[_propertyIndex]; + } + + #endregion + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/events/arr/ObjectArrayEventBeanPropertyWriter.cs b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayEventBeanPropertyWriter.cs new file mode 100755 index 000000000..ebf41a028 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayEventBeanPropertyWriter.cs @@ -0,0 +1,35 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.arr +{ + public class ObjectArrayEventBeanPropertyWriter : EventPropertyWriter + { + protected readonly int Index; + + public ObjectArrayEventBeanPropertyWriter(int index) + { + Index = index; + } + + public virtual void Write(Object value, EventBean target) + { + var arrayEvent = (ObjectArrayBackedEventBean)target; + Write(value, arrayEvent.Properties); + } + + public virtual void Write(Object value, Object[] array) + { + array[Index] = value; + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/arr/ObjectArrayEventBeanPropertyWriterIndexedProp.cs b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayEventBeanPropertyWriterIndexedProp.cs new file mode 100755 index 000000000..5f98cb178 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayEventBeanPropertyWriterIndexedProp.cs @@ -0,0 +1,33 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.events.arr +{ + public class ObjectArrayEventBeanPropertyWriterIndexedProp + : ObjectArrayEventBeanPropertyWriter + { + private readonly int _indexTarget; + + public ObjectArrayEventBeanPropertyWriterIndexedProp(int propertyIndex, int indexTarget) + : base(propertyIndex) + { + _indexTarget = indexTarget; + } + + public override void Write(Object value, Object[] array) + { + var arrayEntry = array[Index] as Array; + if (arrayEntry != null && arrayEntry.Length > _indexTarget) + { + arrayEntry.SetValue(value, _indexTarget); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/arr/ObjectArrayEventBeanPropertyWriterMapProp.cs b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayEventBeanPropertyWriterMapProp.cs new file mode 100755 index 000000000..5d6b0a8f5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayEventBeanPropertyWriterMapProp.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.events.arr +{ + using Map = IDictionary; + + public class ObjectArrayEventBeanPropertyWriterMapProp + : ObjectArrayEventBeanPropertyWriter + { + private readonly String _key; + + public ObjectArrayEventBeanPropertyWriterMapProp(int propertyIndex, String key) + : base(propertyIndex) + { + _key = key; + } + + public override void Write(Object value, Object[] array) + { + var mapEntry = (Map) array[Index]; + if (mapEntry != null) { + mapEntry.Put(_key, value); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/arr/ObjectArrayEventBeanWriterPerProp.cs b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayEventBeanWriterPerProp.cs new file mode 100755 index 000000000..9937e1b0b --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayEventBeanWriterPerProp.cs @@ -0,0 +1,43 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.arr +{ + /// + /// Writer method for writing to Object-array-type events. + /// + public class ObjectArrayEventBeanWriterPerProp : EventBeanWriter + { + private readonly ObjectArrayEventBeanPropertyWriter[] _writers; + + /// Ctor. + /// names of properties to write + public ObjectArrayEventBeanWriterPerProp(ObjectArrayEventBeanPropertyWriter[] writers) + { + _writers = writers; + } + + /// Write values to an event. + /// to write + /// to write to + public void Write(Object[] values, EventBean theEvent) + { + var arrayEvent = (ObjectArrayBackedEventBean) theEvent; + var arr = arrayEvent.Properties; + + for (int i = 0; i < _writers.Length; i++) + { + _writers[i].Write(values[i], arr); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/arr/ObjectArrayEventBeanWriterSimpleProps.cs b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayEventBeanWriterSimpleProps.cs new file mode 100755 index 000000000..893b03291 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayEventBeanWriterSimpleProps.cs @@ -0,0 +1,43 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.arr +{ + /// + /// Writer method for writing to Object-Array-type events. + /// + public class ObjectArrayEventBeanWriterSimpleProps : EventBeanWriter + { + private readonly int[] _indexes; + + /// Ctor. + /// indexes of properties to write + public ObjectArrayEventBeanWriterSimpleProps(int[] indexes) + { + _indexes = indexes; + } + + /// Write values to an event. + /// to write + /// to write to + public void Write(Object[] values, EventBean theEvent) + { + var arrayEvent = (ObjectArrayBackedEventBean) theEvent; + var array = arrayEvent.Properties; + + for (int i = 0; i < _indexes.Length; i++) + { + array[_indexes[i]] = values[i]; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/arr/ObjectArrayEventPropertyGetter.cs b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayEventPropertyGetter.cs new file mode 100755 index 000000000..d06463b7d --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayEventPropertyGetter.cs @@ -0,0 +1,31 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.arr +{ + /// + /// Property getter for Objectarray-underlying events. + /// + public interface ObjectArrayEventPropertyGetter : EventPropertyGetter + { + /// Returns a property of an event. + /// to interrogate + /// property value + /// com.espertech.esper.client.PropertyAccessException for property access errors + Object GetObjectArray(Object[] array); + + /// Exists-function for properties in a object array-type event. + /// to interrogate + /// indicator + bool IsObjectArrayExistsProperty(Object[] array); + } +} diff --git a/NEsper.Core/NEsper.Core/events/arr/ObjectArrayEventPropertyGetterAndIndexed.cs b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayEventPropertyGetterAndIndexed.cs new file mode 100755 index 000000000..ab2fe97cf --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayEventPropertyGetterAndIndexed.cs @@ -0,0 +1,21 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.arr +{ + /// + /// Property getter for Object-array-underlying events. + /// + public interface ObjectArrayEventPropertyGetterAndIndexed + : ObjectArrayEventPropertyGetter + , EventPropertyGetterIndexed + { + } +} diff --git a/NEsper.Core/NEsper.Core/events/arr/ObjectArrayEventPropertyGetterAndMapped.cs b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayEventPropertyGetterAndMapped.cs new file mode 100755 index 000000000..59bbb8193 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayEventPropertyGetterAndMapped.cs @@ -0,0 +1,21 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.arr +{ + /// + /// Property getter for Map-underlying events. + /// + public interface ObjectArrayEventPropertyGetterAndMapped + : ObjectArrayEventPropertyGetter + , EventPropertyGetterMapped + { + } +} diff --git a/NEsper.Core/NEsper.Core/events/arr/ObjectArrayEventType.cs b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayEventType.cs new file mode 100755 index 000000000..8f5efece5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayEventType.cs @@ -0,0 +1,333 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.events.property; +using com.espertech.esper.util; + +namespace com.espertech.esper.events.arr +{ + public class ObjectArrayEventType : BaseNestableEventType + { + private IDictionary> _propertyWriters; + private EventPropertyDescriptor[] _writablePropertyDescriptors; + + public ObjectArrayEventType(EventTypeMetadata metadata, String eventTypeName, int eventTypeId, EventAdapterService eventAdapterService, IDictionary propertyTypes, ConfigurationEventTypeObjectArray typeDef, EventType[] optionalSuperTypes, ICollection optionalDeepSupertypes) + : base(metadata, eventTypeName, eventTypeId, eventAdapterService, propertyTypes, optionalSuperTypes, optionalDeepSupertypes, typeDef, GetGetterFactory(eventTypeName, propertyTypes, optionalSuperTypes)) + { + } + + protected override void PostUpdateNestableTypes() + { + var factory = (EventTypeNestableGetterFactoryObjectArray)GetterFactory; + var indexPerProperty = factory.PropertiesIndex; + int index = FindMax(indexPerProperty) + 1; + foreach (KeyValuePair entry in NestableTypes) + { + if (indexPerProperty.ContainsKey(entry.Key)) + { + continue; + } + indexPerProperty.Put(entry.Key, index); + index++; + } + } + + public IDictionary PropertiesIndexes + { + get { return ((EventTypeNestableGetterFactoryObjectArray)GetterFactory).PropertiesIndex; } + } + + public override Type UnderlyingType + { + get { return typeof(object[]); } + } + + public override EventBeanCopyMethod GetCopyMethod(String[] properties) + { + BaseNestableEventUtil.MapIndexedPropPair pair = BaseNestableEventUtil.GetIndexedAndMappedProps(properties); + + if (pair.MapProperties.IsEmpty() && pair.ArrayProperties.IsEmpty()) + { + return new ObjectArrayEventBeanCopyMethod(this, EventAdapterService); + } + else + { + return new ObjectArrayEventBeanCopyMethodWithArrayMap( + this, EventAdapterService, pair.MapProperties, pair.ArrayProperties, PropertiesIndexes); + } + } + + public override EventBeanReader Reader + { + get { return null; } + } + + public override EventPropertyWriter GetWriter(String propertyName) + { + if (_writablePropertyDescriptors == null) + { + InitializeWriters(); + } + Pair pair = _propertyWriters.Get(propertyName); + if (pair != null) + { + return pair.Second; + } + + Property property = PropertyParser.ParseAndWalkLaxToSimple(propertyName); + if (property is MappedProperty) + { + var mapProp = (MappedProperty)property; + + int index; + if (!PropertiesIndexes.TryGetValue(mapProp.PropertyNameAtomic, out index)) + { + return null; + } + return new ObjectArrayEventBeanPropertyWriterMapProp(index, mapProp.Key); + } + + if (property is IndexedProperty) + { + var indexedProp = (IndexedProperty)property; + + int index; + if (!PropertiesIndexes.TryGetValue(indexedProp.PropertyNameAtomic, out index)) + { + return null; + } + + return new ObjectArrayEventBeanPropertyWriterIndexedProp(index, indexedProp.Index); + } + + return null; + } + + public override EventPropertyDescriptor GetWritableProperty(String propertyName) + { + if (_writablePropertyDescriptors == null) + { + InitializeWriters(); + } + + var pair = _propertyWriters.Get(propertyName); + if (pair != null) + { + return pair.First; + } + + Property property = PropertyParser.ParseAndWalkLaxToSimple(propertyName); + if (property is MappedProperty) + { + var writer = GetWriter(propertyName); + if (writer == null) + { + return null; + } + var mapProp = (MappedProperty)property; + return new EventPropertyDescriptor(mapProp.PropertyNameAtomic, typeof(Object), null, false, true, false, true, false); + } + if (property is IndexedProperty) + { + var writer = GetWriter(propertyName); + if (writer == null) + { + return null; + } + var indexedProp = (IndexedProperty)property; + return new EventPropertyDescriptor(indexedProp.PropertyNameAtomic, typeof(Object), null, true, false, true, false, false); + } + return null; + } + + public override EventPropertyDescriptor[] WriteableProperties + { + get + { + if (_writablePropertyDescriptors == null) + { + InitializeWriters(); + } + return _writablePropertyDescriptors; + } + } + + public override EventBeanWriter GetWriter(String[] properties) + { + if (_writablePropertyDescriptors == null) + { + InitializeWriters(); + } + + var allSimpleProps = true; + var writers = new ObjectArrayEventBeanPropertyWriter[properties.Length]; + var indexes = new List(); + var indexesPerProperty = PropertiesIndexes; + + for (int i = 0; i < properties.Length; i++) + { + var writerPair = _propertyWriters.Get(properties[i]); + if (writerPair != null) + { + writers[i] = writerPair.Second; + indexes.Add(indexesPerProperty.Get(writerPair.First.PropertyName)); + } + else + { + writers[i] = GetWriter(properties[i]) as ObjectArrayEventBeanPropertyWriter; + if (writers[i] == null) + { + return null; + } + allSimpleProps = false; + } + } + + if (allSimpleProps) + { + int[] propertyIndexes = CollectionUtil.IntArray(indexes); + return new ObjectArrayEventBeanWriterSimpleProps(propertyIndexes); + } + else + { + return new ObjectArrayEventBeanWriterPerProp(writers); + } + } + + private void InitializeWriters() + { + var writeableProps = new List(); + var propertWritersMap = new Dictionary>(); + foreach (var prop in PropertyDescriptors) + { + writeableProps.Add(prop); + var propertyName = prop.PropertyName; + int index; + + if (!PropertiesIndexes.TryGetValue(prop.PropertyName, out index)) + { + continue; + } + + var eventPropertyWriter = new ObjectArrayEventBeanPropertyWriter(index); + propertWritersMap.Put(propertyName, new Pair(prop, eventPropertyWriter)); + } + + _propertyWriters = propertWritersMap; + _writablePropertyDescriptors = writeableProps.ToArray(); + } + + private static EventTypeNestableGetterFactory GetGetterFactory(String eventTypeName, IDictionary propertyTypes, EventType[] optionalSupertypes) + { + IDictionary indexPerProperty = new Dictionary(); + + int index = 0; + if (optionalSupertypes != null) + { + foreach (EventType superType in optionalSupertypes) + { + var objectArraySuperType = (ObjectArrayEventType)superType; + foreach (String propertyName in objectArraySuperType.PropertyNames) + { + if (indexPerProperty.ContainsKey(propertyName)) + { + continue; + } + indexPerProperty.Put(propertyName, index); + index++; + } + } + } + + foreach (KeyValuePair entry in propertyTypes) + { + indexPerProperty.Put(entry.Key, index); + index++; + } + return new EventTypeNestableGetterFactoryObjectArray(eventTypeName, indexPerProperty); + } + + private static int FindMax(IDictionary indexPerProperty) + { + int max = -1; + foreach (var entry in indexPerProperty) + { + if (entry.Value > max) + { + max = entry.Value; + } + } + return max; + } + + public static Object[] ConvertEvent(EventBean theEvent, ObjectArrayEventType targetType) + { + var indexesTarget = targetType.PropertiesIndexes; + var indexesSource = ((ObjectArrayEventType)theEvent.EventType).PropertiesIndexes; + var dataTarget = new Object[indexesTarget.Count]; + var dataSource = (Object[])theEvent.Underlying; + + foreach (KeyValuePair sourceEntry in indexesSource) + { + string propertyName = sourceEntry.Key; + int targetIndex; + if (!indexesTarget.TryGetValue(propertyName, out targetIndex)) + { + continue; + } + + object value = dataSource[sourceEntry.Value]; + dataTarget[targetIndex] = value; + } + + return dataTarget; + } + + public bool IsDeepEqualsConsiderOrder(ObjectArrayEventType other) + { + var factoryOther = (EventTypeNestableGetterFactoryObjectArray)other.GetterFactory; + var factoryMe = (EventTypeNestableGetterFactoryObjectArray)GetterFactory; + + if (factoryOther.PropertiesIndex.Count != factoryMe.PropertiesIndex.Count) + { + return false; + } + + foreach (var propMeEntry in factoryMe.PropertiesIndex) + { + int otherIndex; + + if (!factoryOther.PropertiesIndex.TryGetValue(propMeEntry.Key, out otherIndex) || + (otherIndex != propMeEntry.Value)) + { + return false; + } + + var propName = propMeEntry.Key; + var setOneType = NestableTypes.Get(propName); + var setTwoType = other.NestableTypes.Get(propName); + var setTwoTypeFound = other.NestableTypes.ContainsKey(propName); + + var comparedMessage = BaseNestableEventUtil.ComparePropType( + propName, setOneType, setTwoType, setTwoTypeFound, other.Name); + if (comparedMessage != null) + { + return false; + } + } + + return true; + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/arr/ObjectArrayFragmentArrayPropertyGetter.cs b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayFragmentArrayPropertyGetter.cs new file mode 100755 index 000000000..017ce0bcc --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayFragmentArrayPropertyGetter.cs @@ -0,0 +1,77 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Linq; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.arr +{ + /// + /// Getter for map array. + /// + public class ObjectArrayFragmentArrayPropertyGetter : ObjectArrayEventPropertyGetter + { + private readonly int _propertyIndex; + private readonly EventType _fragmentEventType; + private readonly EventAdapterService _eventAdapterService; + + /// Ctor. + /// property index + /// event type of fragment + /// for creating event instances + public ObjectArrayFragmentArrayPropertyGetter(int propertyIndex, EventType fragmentEventType, EventAdapterService eventAdapterService) + { + _propertyIndex = propertyIndex; + _fragmentEventType = fragmentEventType; + _eventAdapterService = eventAdapterService; + } + + public Object GetObjectArray(Object[] array) + { + return array[_propertyIndex]; + } + + public bool IsObjectArrayExistsProperty(Object[] array) + { + return true; + } + + public Object Get(EventBean eventBean) + { + Object[] array = BaseNestableEventUtil.CheckedCastUnderlyingObjectArray(eventBean); + return GetObjectArray(array); + } + + public bool IsExistsProperty(EventBean eventBean) + { + return true; + } + + public Object GetFragment(EventBean eventBean) + { + var value = Get(eventBean); + if (value is EventBean[]) + { + return value; + } + + if (value is Object[]) + { + var objectArray = (Object[]) value; + if (Enumerable.All(objectArray, av => av == null || av is EventBean)) + { + return Enumerable.Cast(objectArray).ToArray(); + } + } + + return BaseNestableEventUtil.GetFragmentArray(_eventAdapterService, value, _fragmentEventType); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/arr/ObjectArrayIndexedPropertyGetter.cs b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayIndexedPropertyGetter.cs new file mode 100755 index 000000000..dad95aedc --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayIndexedPropertyGetter.cs @@ -0,0 +1,59 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.arr +{ + /// + /// Getter for a dynamic indexed property for maps. + /// + public class ObjectArrayIndexedPropertyGetter : ObjectArrayEventPropertyGetter + { + private readonly int _propertyIndex; + private readonly int _index; + + /// Ctor. + /// property index + /// index to get the element at + public ObjectArrayIndexedPropertyGetter(int propertyIndex, int index) + { + _propertyIndex = propertyIndex; + _index = index; + } + + public Object GetObjectArray(Object[] array) + { + Object value = array[_propertyIndex]; + return BaseNestableEventUtil.GetIndexedValue(value, _index); + } + + public bool IsObjectArrayExistsProperty(Object[] array) + { + Object value = array[_propertyIndex]; + return BaseNestableEventUtil.IsExistsIndexedValue(value, _index); + } + + public Object Get(EventBean eventBean) + { + return GetObjectArray(BaseNestableEventUtil.CheckedCastUnderlyingObjectArray(eventBean)); + } + + public bool IsExistsProperty(EventBean eventBean) + { + return IsObjectArrayExistsProperty(BaseNestableEventUtil.CheckedCastUnderlyingObjectArray(eventBean)); + } + + public Object GetFragment(EventBean eventBean) + { + return null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/arr/ObjectArrayMapPropertyGetter.cs b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayMapPropertyGetter.cs new file mode 100755 index 000000000..46af4fc72 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayMapPropertyGetter.cs @@ -0,0 +1,77 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.events.map; + +namespace com.espertech.esper.events.arr +{ + using Map = IDictionary; + + public class ObjectArrayMapPropertyGetter : ObjectArrayEventPropertyGetter + { + private readonly int _index; + private readonly MapEventPropertyGetter _getter; + + /// + /// Ctor. + /// + /// The index. + /// is the getter to use to interrogate the property in the map + public ObjectArrayMapPropertyGetter(int index, MapEventPropertyGetter getter) + { + if (getter == null) + { + throw new ArgumentException("Getter is a required parameter"); + } + _index = index; + _getter = getter; + } + + public Object GetObjectArray(Object[] array) + { + Object valueTopObj = array[_index]; + if (!(valueTopObj is Map)) + { + return null; + } + return _getter.GetMap((Map)valueTopObj); + } + + public bool IsObjectArrayExistsProperty(Object[] array) + { + Object valueTopObj = array[_index]; + if (!(valueTopObj is Map)) + { + return false; + } + + return _getter.IsMapExistsProperty((Map)valueTopObj); + } + + public Object Get(EventBean eventBean) + { + Object[] array = BaseNestableEventUtil.CheckedCastUnderlyingObjectArray(eventBean); + return GetObjectArray(array); + } + + public bool IsExistsProperty(EventBean eventBean) + { + Object[] array = BaseNestableEventUtil.CheckedCastUnderlyingObjectArray(eventBean); + return IsObjectArrayExistsProperty(array); + } + + public Object GetFragment(EventBean eventBean) + { + return null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/arr/ObjectArrayMappedPropertyGetter.cs b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayMappedPropertyGetter.cs new file mode 100755 index 000000000..3f64a918f --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayMappedPropertyGetter.cs @@ -0,0 +1,72 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.arr +{ + /// + /// Getter for a dynamic mappeds property for maps. + /// + public class ObjectArrayMappedPropertyGetter : ObjectArrayEventPropertyGetterAndMapped + { + private readonly int _propertyIndex; + private readonly String _key; + + /// Ctor. + /// property index + /// get the element at + public ObjectArrayMappedPropertyGetter(int propertyIndex, String key) + { + _propertyIndex = propertyIndex; + _key = key; + } + + public Object GetObjectArray(Object[] array) + { + return GetObjectArrayInternal(array, _key); + } + + public bool IsObjectArrayExistsProperty(Object[] array) + { + Object value = array[_propertyIndex]; + return BaseNestableEventUtil.GetMappedPropertyExists(value, _key); + } + + public Object Get(EventBean eventBean, String mapKey) + { + Object[] data = BaseNestableEventUtil.CheckedCastUnderlyingObjectArray(eventBean); + return GetObjectArrayInternal(data, mapKey); + } + + public Object Get(EventBean eventBean) + { + Object[] data = BaseNestableEventUtil.CheckedCastUnderlyingObjectArray(eventBean); + return GetObjectArray(data); + } + + public bool IsExistsProperty(EventBean eventBean) + { + Object[] data = BaseNestableEventUtil.CheckedCastUnderlyingObjectArray(eventBean); + return IsObjectArrayExistsProperty(data); + } + + public Object GetFragment(EventBean eventBean) + { + return null; + } + + private Object GetObjectArrayInternal(Object[] objectArray, String providedKey) + { + Object value = objectArray[_propertyIndex]; + return BaseNestableEventUtil.GetMappedPropertyValue(value, providedKey); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/arr/ObjectArrayNestedEntryPropertyGetterArrayMap.cs b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayNestedEntryPropertyGetterArrayMap.cs new file mode 100755 index 000000000..cd166a1e6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayNestedEntryPropertyGetterArrayMap.cs @@ -0,0 +1,44 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.events.map; + +namespace com.espertech.esper.events.arr +{ + /// A getter that works on EventBean events residing within a Map as an event property. + public class ObjectArrayNestedEntryPropertyGetterArrayMap : ObjectArrayNestedEntryPropertyGetterBase + { + private readonly int _index; + private readonly MapEventPropertyGetter _getter; + + public ObjectArrayNestedEntryPropertyGetterArrayMap(int propertyIndex, EventType fragmentType, EventAdapterService eventAdapterService, int index, MapEventPropertyGetter getter) + : base(propertyIndex, fragmentType, eventAdapterService) + { + _index = index; + _getter = getter; + } + + public override Object HandleNestedValue(Object value) + { + return BaseNestableEventUtil.HandleNestedValueArrayWithMap(value, _index, _getter); + } + + public override Object HandleNestedValueFragment(Object value) + { + return BaseNestableEventUtil.HandleNestedValueArrayWithMapFragment(value, _index, _getter, base.EventAdapterService, base.FragmentType); + } + + public override bool HandleNestedValueExists(Object value) + { + return BaseNestableEventUtil.HandleNestedValueArrayWithMapExists(value, _index, _getter); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/arr/ObjectArrayNestedEntryPropertyGetterArrayObjectArray.cs b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayNestedEntryPropertyGetterArrayObjectArray.cs new file mode 100755 index 000000000..ca25fe238 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayNestedEntryPropertyGetterArrayObjectArray.cs @@ -0,0 +1,42 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.arr +{ + public class ObjectArrayNestedEntryPropertyGetterArrayObjectArray : ObjectArrayNestedEntryPropertyGetterBase + { + private readonly int _index; + private readonly ObjectArrayEventPropertyGetter _getter; + + public ObjectArrayNestedEntryPropertyGetterArrayObjectArray(int propertyIndex, EventType fragmentType, EventAdapterService eventAdapterService, int index, ObjectArrayEventPropertyGetter getter) + : base(propertyIndex, fragmentType, eventAdapterService) + { + _index = index; + _getter = getter; + } + + public override Object HandleNestedValue(Object value) + { + return BaseNestableEventUtil.HandleNestedValueArrayWithObjectArray(value, _index, _getter); + } + + public override Object HandleNestedValueFragment(Object value) + { + return BaseNestableEventUtil.HandleNestedValueArrayWithObjectArrayFragment(value, _index, _getter, base.FragmentType, base.EventAdapterService); + } + + public override bool HandleNestedValueExists(Object value) + { + return BaseNestableEventUtil.HandleNestedValueArrayWithObjectArrayExists(value, _index, _getter); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/arr/ObjectArrayNestedEntryPropertyGetterBase.cs b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayNestedEntryPropertyGetterBase.cs new file mode 100755 index 000000000..27f26533c --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayNestedEntryPropertyGetterBase.cs @@ -0,0 +1,78 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.arr +{ + public abstract class ObjectArrayNestedEntryPropertyGetterBase : ObjectArrayEventPropertyGetter + { + protected readonly int PropertyIndex; + protected readonly EventType FragmentType; + protected readonly EventAdapterService EventAdapterService; + + /// Ctor. + /// the property to look at + /// factory for event beans and event types + /// type of the entry returned + protected ObjectArrayNestedEntryPropertyGetterBase(int propertyIndex, EventType fragmentType, EventAdapterService eventAdapterService) + { + PropertyIndex = propertyIndex; + FragmentType = fragmentType; + EventAdapterService = eventAdapterService; + } + + public abstract Object HandleNestedValue(Object value); + public abstract bool HandleNestedValueExists(Object value); + public abstract Object HandleNestedValueFragment(Object value); + + public Object GetObjectArray(Object[] array) + { + var value = array[PropertyIndex]; + if (value == null) + { + return null; + } + return HandleNestedValue(value); + } + + public bool IsObjectArrayExistsProperty(Object[] array) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + + public Object Get(EventBean eventBean) + { + return GetObjectArray(BaseNestableEventUtil.CheckedCastUnderlyingObjectArray(eventBean)); + } + + public bool IsExistsProperty(EventBean eventBean) + { + var array = BaseNestableEventUtil.CheckedCastUnderlyingObjectArray(eventBean); + var value = array[PropertyIndex]; + if (value == null) + { + return false; + } + return HandleNestedValueExists(value); + } + + public Object GetFragment(EventBean obj) + { + var array = BaseNestableEventUtil.CheckedCastUnderlyingObjectArray(obj); + var value = array[PropertyIndex]; + if (value == null) + { + return null; + } + return HandleNestedValueFragment(value); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/arr/ObjectArrayNestedEntryPropertyGetterMap.cs b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayNestedEntryPropertyGetterMap.cs new file mode 100755 index 000000000..65a003600 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayNestedEntryPropertyGetterMap.cs @@ -0,0 +1,74 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.events.map; + +namespace com.espertech.esper.events.arr +{ + using Map = IDictionary; + + /// + /// A getter that works on EventBean events residing within a Map as an event property. + /// + public class ObjectArrayNestedEntryPropertyGetterMap : ObjectArrayNestedEntryPropertyGetterBase + { + private readonly MapEventPropertyGetter mapGetter; + + public ObjectArrayNestedEntryPropertyGetterMap(int propertyIndex, EventType fragmentType, EventAdapterService eventAdapterService, MapEventPropertyGetter mapGetter) + : base(propertyIndex, fragmentType, eventAdapterService) + { + this.mapGetter = mapGetter; + } + + public override Object HandleNestedValue(Object value) + { + if (!(value is Map)) + { + if (value is EventBean) + { + return mapGetter.Get((EventBean)value); + } + return null; + } + return mapGetter.GetMap((Map)value); + } + + public override Object HandleNestedValueFragment(Object value) + { + if (!(value is Map)) + { + if (value is EventBean) + { + return mapGetter.GetFragment((EventBean)value); + } + return null; + } + + // If the map does not contain the key, this is allowed and represented as null + EventBean eventBean = EventAdapterService.AdapterForTypedMap((Map)value, FragmentType); + return mapGetter.GetFragment(eventBean); + } + + public override bool HandleNestedValueExists(Object value) + { + if (!(value is Map)) + { + if (value is EventBean) + { + return mapGetter.IsExistsProperty((EventBean) value); + } + return false; + } + return mapGetter.IsMapExistsProperty((Map) value); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/arr/ObjectArrayNestedEntryPropertyGetterObjectArray.cs b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayNestedEntryPropertyGetterObjectArray.cs new file mode 100755 index 000000000..1ddf1625a --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayNestedEntryPropertyGetterObjectArray.cs @@ -0,0 +1,70 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.arr +{ + /// + /// A getter that works on EventBean events residing within a Map as an event property. + /// + public class ObjectArrayNestedEntryPropertyGetterObjectArray : ObjectArrayNestedEntryPropertyGetterBase + { + private readonly ObjectArrayEventPropertyGetter _arrayGetter; + + public ObjectArrayNestedEntryPropertyGetterObjectArray(int propertyIndex, EventType fragmentType, EventAdapterService eventAdapterService, ObjectArrayEventPropertyGetter arrayGetter) + : base(propertyIndex, fragmentType, eventAdapterService) + { + _arrayGetter = arrayGetter; + } + + public override Object HandleNestedValue(Object value) + { + if (!(value is Object[])) + { + if (value is EventBean) + { + return _arrayGetter.Get((EventBean)value); + } + return null; + } + return _arrayGetter.GetObjectArray((Object[])value); + } + + public override Object HandleNestedValueFragment(Object value) + { + if (!(value is Object[])) + { + if (value is EventBean) + { + return _arrayGetter.GetFragment((EventBean)value); + } + return null; + } + + // If the map does not contain the key, this is allowed and represented as null + EventBean eventBean = EventAdapterService.AdapterForTypedObjectArray((Object[])value, FragmentType); + return _arrayGetter.GetFragment(eventBean); + } + + public override bool HandleNestedValueExists(Object value) + { + if (!(value is Object[])) + { + if (value is EventBean) + { + return _arrayGetter.IsExistsProperty((EventBean) value); + } + return false; + } + return _arrayGetter.IsObjectArrayExistsProperty((Object[]) value); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/arr/ObjectArrayPONOEntryPropertyGetter.cs b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayPONOEntryPropertyGetter.cs new file mode 100755 index 000000000..4ca2c54ae --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayPONOEntryPropertyGetter.cs @@ -0,0 +1,94 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.events.bean; + +namespace com.espertech.esper.events.arr +{ + /// + /// A getter that works on PONO events residing within a Map as an event property. + /// + public class ObjectArrayPONOEntryPropertyGetter + : BaseNativePropertyGetter + , + ObjectArrayEventPropertyGetter + { + private readonly int _propertyIndex; + private readonly BeanEventPropertyGetter _entryGetter; + + /// + /// Ctor. + /// + /// MapIndex of the property. + /// the getter for the map entry + /// for producing wrappers to objects + /// type of the entry returned + /// Type of the nested component. + public ObjectArrayPONOEntryPropertyGetter( + int propertyIndex, + BeanEventPropertyGetter entryGetter, + EventAdapterService eventAdapterService, + Type returnType, + Type nestedComponentType) + : base(eventAdapterService, returnType, nestedComponentType) + { + _propertyIndex = propertyIndex; + _entryGetter = entryGetter; + } + + public Object GetObjectArray(Object[] array) + { + // If the map does not contain the key, this is allowed and represented as null + var value = array[_propertyIndex]; + if (value == null) + { + return null; + } + + // Object within the map + if (value is EventBean) + { + return _entryGetter.Get((EventBean) value); + } + + return _entryGetter.GetBeanProp(value); + } + + public bool IsObjectArrayExistsProperty(Object[] array) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + + public override Object Get(EventBean eventBean) + { + var array = BaseNestableEventUtil.CheckedCastUnderlyingObjectArray(eventBean); + return GetObjectArray(array); + } + + public override bool IsExistsProperty(EventBean eventBean) + { + var array = BaseNestableEventUtil.CheckedCastUnderlyingObjectArray(eventBean); + var value = array[_propertyIndex]; + + if (value == null) + { + return false; + } + + // Object within the map + if (value is EventBean) + { + return _entryGetter.IsExistsProperty((EventBean) value); + } + return _entryGetter.IsBeanExistsProperty(value); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/arr/ObjectArrayPropertyGetterDefaultBase.cs b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayPropertyGetterDefaultBase.cs new file mode 100755 index 000000000..1946abbca --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayPropertyGetterDefaultBase.cs @@ -0,0 +1,64 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.arr +{ + /// + /// Getter for map entry. + /// + public abstract class ObjectArrayPropertyGetterDefaultBase : ObjectArrayEventPropertyGetter + { + private readonly int _propertyIndex; + protected readonly EventType FragmentEventType; + protected readonly EventAdapterService EventAdapterService; + + /// Ctor. + /// property index + /// fragment type + /// factory for event beans and event types + protected ObjectArrayPropertyGetterDefaultBase(int propertyIndex, EventType fragmentEventType, EventAdapterService eventAdapterService) + { + _propertyIndex = propertyIndex; + FragmentEventType = fragmentEventType; + EventAdapterService = eventAdapterService; + } + + protected abstract Object HandleCreateFragment(Object value); + + public Object GetObjectArray(Object[] array) + { + return array[_propertyIndex]; + } + + public bool IsObjectArrayExistsProperty(Object[] array) + { + return array.Length > _propertyIndex; + } + + public Object Get(EventBean eventBean) + { + var array = BaseNestableEventUtil.CheckedCastUnderlyingObjectArray(eventBean); + return GetObjectArray(array); + } + + public bool IsExistsProperty(EventBean eventBean) + { + return true; + } + + public Object GetFragment(EventBean eventBean) + { + var value = Get(eventBean); + return HandleCreateFragment(value); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/arr/ObjectArrayPropertyGetterDefaultMap.cs b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayPropertyGetterDefaultMap.cs new file mode 100755 index 000000000..e3094357a --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayPropertyGetterDefaultMap.cs @@ -0,0 +1,30 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.arr +{ + /// + /// Getter for map entry. + /// + public class ObjectArrayPropertyGetterDefaultMap : ObjectArrayPropertyGetterDefaultBase + { + public ObjectArrayPropertyGetterDefaultMap(int propertyIndex, EventType fragmentEventType, EventAdapterService eventAdapterService) + : base(propertyIndex, fragmentEventType, eventAdapterService) + { + } + + protected override Object HandleCreateFragment(Object value) + { + return BaseNestableEventUtil.HandleCreateFragmentMap(value, FragmentEventType, EventAdapterService); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/arr/ObjectArrayPropertyGetterDefaultObjectArray.cs b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayPropertyGetterDefaultObjectArray.cs new file mode 100755 index 000000000..a3045b415 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/arr/ObjectArrayPropertyGetterDefaultObjectArray.cs @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.arr +{ + /// Getter for map entry. + public class ObjectArrayPropertyGetterDefaultObjectArray : ObjectArrayPropertyGetterDefaultBase + { + public ObjectArrayPropertyGetterDefaultObjectArray(int propertyIndex, EventType fragmentEventType, EventAdapterService eventAdapterService) + : base(propertyIndex, fragmentEventType, eventAdapterService) + { + } + + protected override Object HandleCreateFragment(Object value) + { + if (FragmentEventType == null) + { + return null; + } + return BaseNestableEventUtil.HandleCreateFragmentObjectArray(value, FragmentEventType, EventAdapterService); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/arr/SendableEventObjectArray.cs b/NEsper.Core/NEsper.Core/events/arr/SendableEventObjectArray.cs new file mode 100755 index 000000000..26795c1b0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/arr/SendableEventObjectArray.cs @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.arr +{ + public class SendableEventObjectArray : SendableEvent + { + private readonly Object[] _event; + private readonly String _typeName; + + public SendableEventObjectArray(Object[] theEvent, String typeName) + { + _event = theEvent; + _typeName = typeName; + } + + #region SendableEvent Members + + public void Send(EPRuntime runtime) + { + runtime.SendEvent(_event, _typeName); + } + + public object Underlying + { + get { return _event; } + } + + #endregion + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/events/avro/AvroConstantsNoDep.cs b/NEsper.Core/NEsper.Core/events/avro/AvroConstantsNoDep.cs new file mode 100755 index 000000000..599b6eed2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/avro/AvroConstantsNoDep.cs @@ -0,0 +1,14 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.events.avro +{ + public class AvroConstantsNoDep { + public static readonly string GENERIC_RECORD_CLASSNAME = "Avro.Generic.GenericRecord"; + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/events/avro/AvroSchemaEventType.cs b/NEsper.Core/NEsper.Core/events/avro/AvroSchemaEventType.cs new file mode 100755 index 000000000..aa1516b32 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/avro/AvroSchemaEventType.cs @@ -0,0 +1,17 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.avro +{ + public interface AvroSchemaEventType : EventType + { + object Schema { get; } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/events/avro/EventAdapterAvroHandler.cs b/NEsper.Core/NEsper.Core/events/avro/EventAdapterAvroHandler.cs new file mode 100755 index 000000000..db7076dc6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/avro/EventAdapterAvroHandler.cs @@ -0,0 +1,68 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.core; +using com.espertech.esper.util; + +namespace com.espertech.esper.events.avro +{ + public interface EventAdapterAvroHandler + { + void Init(ConfigurationEngineDefaults.AvroSettings avroSettings, EngineImportService engineImportService); + + SelectExprProcessorRepresentationFactory GetOutputFactory(); + + AvroSchemaEventType NewEventTypeFromSchema( + EventTypeMetadata metadata, + string eventTypeName, + int typeId, + EventAdapterService eventAdapterService, + ConfigurationEventTypeAvro requiredConfig, + EventType[] superTypes, + ICollection deepSuperTypes); + + AvroSchemaEventType NewEventTypeFromNormalized( + EventTypeMetadata metadata, + string eventTypeName, + int typeId, + EventAdapterService eventAdapterService, + IDictionary properties, + Attribute[] annotations, + ConfigurationEventTypeAvro optionalConfig, + EventType[] superTypes, + ICollection deepSuperTypes, + string statementName, + string engineURI); + + EventBean AdapterForTypeAvro(Object avroGenericDataDotRecord, EventType existingType); + + EventBeanManufacturer GetEventBeanManufacturer( + AvroSchemaEventType avroSchemaEventType, + EventAdapterService eventAdapterService, + IList properties); + + EventBeanFactory GetEventBeanFactory(EventType type, EventAdapterService eventAdapterService); + + void ValidateExistingType(EventType existingType, AvroSchemaEventType proposedType); + + void AvroCompat(EventType existingType, IDictionary selPropertyTypes); + + Object ConvertEvent(EventBean theEvent, AvroSchemaEventType targetType); + + TypeWidenerCustomizer GetTypeWidenerCustomizer(EventType eventType); + } + + public class EventAdapterAvroHandlerConstants + { + public const string HANDLER_IMPL = "NEsper.Avro.Core.EventAdapterAvroHandlerImpl"; + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/events/avro/EventAdapterAvroHandlerUnsupported.cs b/NEsper.Core/NEsper.Core/events/avro/EventAdapterAvroHandlerUnsupported.cs new file mode 100755 index 000000000..2c2451726 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/avro/EventAdapterAvroHandlerUnsupported.cs @@ -0,0 +1,108 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.core; +using com.espertech.esper.util; + +namespace com.espertech.esper.events.avro +{ + public class EventAdapterAvroHandlerUnsupported : EventAdapterAvroHandler + { + public static readonly EventAdapterAvroHandlerUnsupported INSTANCE = new EventAdapterAvroHandlerUnsupported(); + + public EventAdapterAvroHandlerUnsupported() + { + } + + public void Init(ConfigurationEngineDefaults.AvroSettings avroSettings, EngineImportService engineImportService) + { + // no action, init is always done + } + + public AvroSchemaEventType NewEventTypeFromSchema( + EventTypeMetadata metadata, + string eventTypeName, + int typeId, + EventAdapterService eventAdapterService, + ConfigurationEventTypeAvro requiredConfig, + EventType[] supertypes, + ICollection deepSupertypes) + { + throw GetUnsupported(); + } + + public EventBean AdapterForTypeAvro(Object avroGenericDataDotRecord, EventType existingType) + { + throw GetUnsupported(); + } + + public AvroSchemaEventType NewEventTypeFromNormalized( + EventTypeMetadata metadata, + string eventTypeName, + int typeId, + EventAdapterService eventAdapterService, + IDictionary properties, + Attribute[] annotations, + ConfigurationEventTypeAvro optionalConfig, + EventType[] superTypes, + ICollection deepSuperTypes, + string statementName, + string engineURI) + { + throw GetUnsupported(); + } + + public EventBeanManufacturer GetEventBeanManufacturer( + AvroSchemaEventType avroSchemaEventType, + EventAdapterService eventAdapterService, + IList properties) + { + throw GetUnsupported(); + } + + public EventBeanFactory GetEventBeanFactory(EventType type, EventAdapterService eventAdapterService) + { + throw GetUnsupported(); + } + + public void ValidateExistingType(EventType existingType, AvroSchemaEventType proposedType) + { + throw GetUnsupported(); + } + + public SelectExprProcessorRepresentationFactory GetOutputFactory() + { + throw GetUnsupported(); + } + + public void AvroCompat(EventType existingType, IDictionary selPropertyTypes) + { + throw GetUnsupported(); + } + + public Object ConvertEvent(EventBean theEvent, AvroSchemaEventType targetType) + { + throw GetUnsupported(); + } + + public TypeWidenerCustomizer GetTypeWidenerCustomizer(EventType eventType) + { + throw GetUnsupported(); + } + + private UnsupportedOperationException GetUnsupported() + { + throw new UnsupportedOperationException("Esper-Avro is not part of your solution"); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/events/bean/ArrayFastPropertyGetter.cs b/NEsper.Core/NEsper.Core/events/bean/ArrayFastPropertyGetter.cs new file mode 100755 index 000000000..8df75df7c --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/ArrayFastPropertyGetter.cs @@ -0,0 +1,103 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using XLR8.CGLib; + +using com.espertech.esper.client; +using com.espertech.esper.events.vaevent; + +namespace com.espertech.esper.events.bean +{ + /// + /// Getter for an array property identified by a given index, using the CGLIB fast method. + /// + public class ArrayFastPropertyGetter + : BaseNativePropertyGetter + , BeanEventPropertyGetter + , EventPropertyGetterAndIndexed + { + private readonly FastMethod _fastMethod; + private readonly int _index; + + /// Constructor. + /// is the method to use to retrieve a value from the object + /// is tge index within the array to get the property from + /// factory for event beans and event types + public ArrayFastPropertyGetter(FastMethod fastMethod, int index, EventAdapterService eventAdapterService) + : base(eventAdapterService, fastMethod.ReturnType.GetElementType(), null) + { + _index = index; + _fastMethod = fastMethod; + + if (index < 0) + { + throw new ArgumentException("Invalid negative index value"); + } + } + + public Object GetBeanProp(Object @object) + { + return GetBeanPropInternal(@object, _index); + } + + private Object GetBeanPropInternal(Object @object, int index) + { + try + { + Array value = (Array)_fastMethod.Invoke(@object); + if (value.Length <= index) + { + return null; + } + + return value.GetValue(index); + } + catch (InvalidCastException e) + { + throw PropertyUtility.GetMismatchException(_fastMethod.Target, @object, e); + } + catch (PropertyAccessException) + { + throw; + } + catch (Exception e) + { + throw new PropertyAccessException(e); + } + } + + public bool IsBeanExistsProperty(Object @object) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + + public override Object Get(EventBean eventBean) + { + return GetBeanProp(eventBean.Underlying); + } + + public Object Get(EventBean eventBean, int index) + { + return GetBeanPropInternal(eventBean.Underlying, index); + } + + public override String ToString() + { + return "ArrayFastPropertyGetter " + + " fastMethod=" + _fastMethod + + " index=" + _index; + } + + public override bool IsExistsProperty(EventBean eventBean) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/bean/ArrayFieldPropertyGetter.cs b/NEsper.Core/NEsper.Core/events/bean/ArrayFieldPropertyGetter.cs new file mode 100755 index 000000000..a48f64b03 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/ArrayFieldPropertyGetter.cs @@ -0,0 +1,103 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.events.vaevent; + +namespace com.espertech.esper.events.bean +{ + /// + /// Getter for an array property backed by a field, identified by a given index, using + /// vanilla reflection. + /// + public class ArrayFieldPropertyGetter + : BaseNativePropertyGetter + , BeanEventPropertyGetter + , EventPropertyGetterAndIndexed + { + private readonly FieldInfo _field; + private readonly int _index; + + /// Constructor. + /// is the field to use to retrieve a value from the object + /// is tge index within the array to get the property from + /// factory for event beans and event types + public ArrayFieldPropertyGetter(FieldInfo field, int index, EventAdapterService eventAdapterService) + : base(eventAdapterService, field.FieldType.GetElementType(), null) + { + _index = index; + _field = field; + + if (index < 0) + { + throw new ArgumentException("Invalid negative index value"); + } + } + + public Object GetBeanProp(Object @object) + { + return GetBeanPropInternal(@object, _index); + } + + private Object GetBeanPropInternal(Object @object, int index) + { + try + { + var value = (Array) _field.GetValue(@object); + if (value.Length <= index) + { + return null; + } + + return value.GetValue(index); + } + catch (InvalidCastException e) + { + throw PropertyUtility.GetMismatchException(_field, @object, e); + } + catch (PropertyAccessException) + { + throw; + } + catch (Exception e) + { + throw new PropertyAccessException(e); + } + } + + public bool IsBeanExistsProperty(Object @object) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + + public override Object Get(EventBean eventBean) + { + return GetBeanProp(eventBean.Underlying); + } + + public Object Get(EventBean eventBean, int index) + { + return GetBeanPropInternal(eventBean.Underlying, index); + } + + public override String ToString() + { + return "ArrayFieldPropertyGetter " + + " field=" + _field + + " index=" + _index; + } + + public override bool IsExistsProperty(EventBean eventBean) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/bean/ArrayMethodPropertyGetter.cs b/NEsper.Core/NEsper.Core/events/bean/ArrayMethodPropertyGetter.cs new file mode 100755 index 000000000..6076fa576 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/ArrayMethodPropertyGetter.cs @@ -0,0 +1,104 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.events.vaevent; + +namespace com.espertech.esper.events.bean +{ + /// + /// Getter for an array property identified by a given index, using vanilla reflection. + /// + public class ArrayMethodPropertyGetter + : BaseNativePropertyGetter + , BeanEventPropertyGetter + , EventPropertyGetterAndIndexed + { + private readonly MethodInfo _method; + private readonly int _index; + + /// Constructor. + /// is the method to use to retrieve a value from the object + /// is tge index within the array to get the property from + /// factory for event beans and event types + public ArrayMethodPropertyGetter(MethodInfo method, int index, EventAdapterService eventAdapterService) + + : base(eventAdapterService, method.ReturnType.GetElementType(), null) + { + _index = index; + _method = method; + + if (index < 0) + { + throw new ArgumentException("Invalid negative index value"); + } + } + + public Object Get(EventBean eventBean, int index) + { + return GetBeanPropInternal(eventBean.Underlying, index); + } + + public Object GetBeanProp(Object @object) + { + return GetBeanPropInternal(@object, _index); + } + + private Object GetBeanPropInternal(Object @object, int index) + { + try + { + var value = (Array)_method.Invoke(@object, null); + if (value.Length <= index) + { + return null; + } + + return value.GetValue(index); + } + catch (InvalidCastException e) + { + throw PropertyUtility.GetMismatchException(_method, @object, e); + } + catch (PropertyAccessException) + { + throw; + } + catch (Exception e) + { + throw new PropertyAccessException(e); + } + } + + public bool IsBeanExistsProperty(Object @object) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + + public override Object Get(EventBean eventBean) + { + Object underlying = eventBean.Underlying; + return GetBeanProp(underlying); + } + + public override String ToString() + { + return "ArrayMethodPropertyGetter " + + " method=" + _method + + " index=" + _index; + } + + public override bool IsExistsProperty(EventBean eventBean) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/bean/BaseNativePropertyGetter.cs b/NEsper.Core/NEsper.Core/events/bean/BaseNativePropertyGetter.cs new file mode 100755 index 000000000..242f59876 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/BaseNativePropertyGetter.cs @@ -0,0 +1,222 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.util; + +namespace com.espertech.esper.events.bean +{ + /// Base getter for native fragments. + public abstract class BaseNativePropertyGetter : EventPropertyGetter + { + private readonly EventAdapterService _eventAdapterService; + private volatile BeanEventType _fragmentEventType; + private readonly Type _fragmentClassType; + private bool _isFragmentable; + private readonly bool _isArray; + private readonly bool _isIterable; + + public abstract object Get(EventBean eventBean); + public abstract bool IsExistsProperty(EventBean eventBean); + + /// Constructor. + /// factory for event beans and event types + /// type of the entry returned + /// type generic parameter, if any + protected BaseNativePropertyGetter(EventAdapterService eventAdapterService, Type returnType, Type genericType) + { + _eventAdapterService = eventAdapterService; + if (returnType.IsArray) + { + _fragmentClassType = returnType.GetElementType(); + _isArray = true; + _isIterable = false; + } + else if (returnType.IsImplementsInterface(typeof(IEnumerable))) + { + _fragmentClassType = genericType; + _isArray = false; + _isIterable = true; + } + else + { + _fragmentClassType = returnType; + _isArray = false; + _isIterable = false; + } + _isFragmentable = true; + } + + /// Returns the fragment for dynamic properties. + /// to inspect + /// factory for event beans and event types + /// fragment + public static Object GetFragmentDynamic(Object @object, EventAdapterService eventAdapterService) + { + if (@object == null) + { + return null; + } + + BeanEventType fragmentEventType = null; + bool isArray = false; + if (@object.GetType().IsArray) + { + if (@object.GetType().GetElementType().IsFragmentableType()) + { + isArray = true; + fragmentEventType = eventAdapterService.BeanEventTypeFactory.CreateBeanTypeDefaultName(@object.GetType().GetElementType()); + } + } + else + { + if (@object.GetType().IsFragmentableType()) + { + fragmentEventType = eventAdapterService.BeanEventTypeFactory.CreateBeanTypeDefaultName(@object.GetType()); + } + } + + if (fragmentEventType == null) + { + return null; + } + + if (isArray) + { + var asArray = (Array) @object; + var len = asArray.Length; + var events = new EventBean[len]; + int countFilled = 0; + + for (int i = 0; i < len; i++) + { + Object element = asArray.GetValue(i); + if (element == null) + { + continue; + } + + events[countFilled] = eventAdapterService.AdapterForTypedObject(element, fragmentEventType); + countFilled++; + } + + if (countFilled == len) + { + return events; + } + + if (countFilled == 0) + { + return new EventBean[0]; + } + + var returnVal = new EventBean[countFilled]; + Array.Copy(events, 0, returnVal, 0, countFilled); + return returnVal; + } + else + { + return eventAdapterService.AdapterForTypedObject(@object, fragmentEventType); + } + } + + public Object GetFragment(EventBean eventBean) + { + Object @object = Get(eventBean); + if (@object == null) + { + return null; + } + + if (!_isFragmentable) + { + return null; + } + + if (_fragmentEventType == null) + { + if (_fragmentClassType.IsFragmentableType()) + { + _fragmentEventType = _eventAdapterService.BeanEventTypeFactory.CreateBeanTypeDefaultName(_fragmentClassType); + } + else + { + _isFragmentable = false; + return null; + } + } + + if (_isArray) + { + var asArray = (Array) @object; + var len = asArray.Length; + var events = new EventBean[len]; + int countFilled = 0; + + for (int i = 0; i < len; i++) + { + Object element = asArray.GetValue(i); + if (element == null) + { + continue; + } + + events[countFilled] = _eventAdapterService.AdapterForTypedObject(element, _fragmentEventType); + countFilled++; + } + + if (countFilled == len) + { + return events; + } + + if (countFilled == 0) + { + return new EventBean[0]; + } + + var returnVal = new EventBean[countFilled]; + Array.Copy(events, 0, returnVal, 0, countFilled); + return returnVal; + } + else if (_isIterable) + { + if (!(@object is IEnumerable)) + { + return null; + } + IEnumerator enumerator = ((IEnumerable)@object).GetEnumerator(); + if (!enumerator.MoveNext()) + { + return new EventBean[0]; + } + var events = new List(); + do + { + Object next = enumerator.Current; + if (next == null) + { + continue; + } + + events.Add(_eventAdapterService.AdapterForTypedObject(next, _fragmentEventType)); + } while (enumerator.MoveNext()); + + return events.ToArray(); + } + else + { + return _eventAdapterService.AdapterForTypedObject(@object, _fragmentEventType); + } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/events/bean/BeanEventAdapter.cs b/NEsper.Core/NEsper.Core/events/bean/BeanEventAdapter.cs new file mode 100755 index 000000000..0594f4636 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/BeanEventAdapter.cs @@ -0,0 +1,143 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.threading; + +namespace com.espertech.esper.events.bean +{ + /// + /// A factory for instances based on type information and + /// using configured settings for + /// + public class BeanEventAdapter : BeanEventTypeFactory + { + private readonly IDictionary _typesPerObject; + private readonly ILockable _typesPerObjectLock; + private readonly EventAdapterService _eventAdapterService; + private readonly EventTypeIdGenerator _eventTypeIdGenerator; + + private readonly IDictionary _typeToLegacyConfigs; + private PropertyResolutionStyle _defaultPropertyResolutionStyle; + private AccessorStyleEnum _defaultAccessorStyle = AccessorStyleEnum.NATIVE; + + /// + /// Ctor. + /// + /// shareable collection that this adapter writes tofor caching bean types per class + /// factory for event beans and event types + /// The event type id generator. + public BeanEventAdapter(IDictionary typesPerObject, + EventAdapterService eventAdapterService, + EventTypeIdGenerator eventTypeIdGenerator) + { + _typesPerObject = typesPerObject; + _typesPerObjectLock = LockManager.CreateLock(GetType()); + _typeToLegacyConfigs = new Dictionary(); + _defaultPropertyResolutionStyle = PropertyResolutionStyle.DEFAULT; + _eventAdapterService = eventAdapterService; + _eventTypeIdGenerator = eventTypeIdGenerator; + } + + public BeanEventType[] CachedTypes + { + get + { + ICollection types = _typesPerObject.Values; + return types.ToArray(); + } + } + + /// Gets or sets the default accessor style. + /// style to set + public AccessorStyleEnum DefaultAccessorStyle + { + get { return _defaultAccessorStyle; } + set { _defaultAccessorStyle = value; } + } + + /// Set the additional mappings for legacy classes. + /// legacy class information + public IDictionary TypeToLegacyConfigs + { + get { return _typeToLegacyConfigs; } + set { _typeToLegacyConfigs.PutAll(value); } + } + + /// Gets the default property resolution style for class properties. + /// resolution style + public PropertyResolutionStyle DefaultPropertyResolutionStyle + { + get { return _defaultPropertyResolutionStyle; } + set { _defaultPropertyResolutionStyle = value; } + } + + /// Creates a new EventType object for a object of the specified class if this is the first time the class has been seen. Else uses a cached EventType instance, i.e. client classes do not need to cache. + /// is the class of the object. + /// EventType implementation for bean class + public BeanEventType CreateBeanTypeDefaultName(Type clazz) + { + return CreateBeanType(clazz.Name, clazz, false, false, false); + } + + /// + /// Creates a new EventType object for a object of the specified class if this is the first time the class has been seen. Else uses a cached EventType instance, i.e. client classes do not need to cache. + /// + /// is the name + /// is the class of the object. + /// if from static engine config + /// if configured before use + /// if the class is a configuration value, false if discovered + /// EventType implementation for bean class + public BeanEventType CreateBeanType(String name, Type clazz, bool isPreconfiguredStatic, bool isPreconfigured, bool isConfigured) + { + if (clazz == null) + { + throw new ArgumentNullException(nameof(clazz), "Null value passed as class"); + } + + BeanEventType eventType; + + // not created yet, thread-safe create + using (_typesPerObjectLock.Acquire()) + { + eventType = _typesPerObject.Get(clazz); + if (eventType != null) + { + _eventTypeIdGenerator.AssignedType(name, eventType); + return eventType; + } + + // Check if we have a legacy type definition for this class + ConfigurationEventTypeLegacy legacyDef = _typeToLegacyConfigs.Get(clazz.AssemblyQualifiedName); + if ((legacyDef == null) && (_defaultAccessorStyle != AccessorStyleEnum.NATIVE)) + { + legacyDef = new ConfigurationEventTypeLegacy(); + legacyDef.AccessorStyle = _defaultAccessorStyle; + } + + int typeId = _eventTypeIdGenerator.GetTypeId(name); + EventTypeMetadata metadata = EventTypeMetadata.CreateBeanType(name, clazz, isPreconfiguredStatic, isPreconfigured, isConfigured, TypeClass.APPLICATION); + eventType = new BeanEventType(metadata, typeId, clazz, _eventAdapterService, legacyDef); + _typesPerObject.Put(clazz, eventType); + } + + return eventType; + } + + public ConfigurationEventTypeLegacy GetClassToLegacyConfigs(String assemblyQualifiedTypeName) + { + return _typeToLegacyConfigs.Get(assemblyQualifiedTypeName); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/events/bean/BeanEventBean.cs b/NEsper.Core/NEsper.Core/events/bean/BeanEventBean.cs new file mode 100755 index 000000000..3d38860dc --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/BeanEventBean.cs @@ -0,0 +1,86 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.events; + +namespace com.espertech.esper.events.bean +{ + /// + /// Wrapper for vanilla objects Java objects the represent events. + /// Allows access to event properties, which is done through the getter supplied by the event type. + /// instances containing type information are obtained from . + /// Two BeanEventBean instances are equal if they have the same event type and refer to the same instance of event object. + /// Clients that need to compute equality between objects wrapped by this class need to obtain the underlying object. + /// + public class BeanEventBean : EventBeanSPI + { + private readonly EventType _eventType; + private object _theEvent; + + /// + /// Constructor. + /// + /// is the event object + /// is the schema information for the event object. + public BeanEventBean(Object theEvent, EventType eventType) + { + this._eventType = eventType; + this._theEvent = theEvent; + } + + public object Underlying + { + get { return _theEvent; } + set { _theEvent = value; } + } + + public EventType EventType + { + get { return _eventType; } + } + + public Object Get(string property) + { + EventPropertyGetter getter = _eventType.GetGetter(property); + if (getter == null) + { + throw new PropertyAccessException( + "Property named '" + property + "' is not a valid property name for this type"); + } + return getter.Get(this); + } + + public object this[string property] + { + get { return Get(property); } + } + + public override String ToString() + { + return "BeanEventBean" + + " eventType=" + _eventType + + " bean=" + _theEvent; + } + + public Object GetFragment(string propertyExpression) + { + EventPropertyGetter getter = _eventType.GetGetter(propertyExpression); + if (getter == null) + { + throw PropertyAccessException.NotAValidProperty(propertyExpression); + } + return getter.GetFragment(this); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/events/bean/BeanEventBeanConfiguredCopyMethod.cs b/NEsper.Core/NEsper.Core/events/bean/BeanEventBeanConfiguredCopyMethod.cs new file mode 100755 index 000000000..bb65a9f62 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/BeanEventBeanConfiguredCopyMethod.cs @@ -0,0 +1,65 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using XLR8.CGLib; + +using com.espertech.esper.client; +using com.espertech.esper.compat.logging; + +namespace com.espertech.esper.events.bean +{ + /// + /// Copies an event for modification. + /// + public class BeanEventBeanConfiguredCopyMethod : EventBeanCopyMethod + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly BeanEventType _beanEventType; + private readonly EventAdapterService _eventAdapterService; + private readonly FastMethod _copyMethod; + + /// + /// Ctor. + /// + /// type of bean to copy + /// for creating events + /// method to copy the event + public BeanEventBeanConfiguredCopyMethod(BeanEventType beanEventType, EventAdapterService eventAdapterService, FastMethod copyMethod) + { + _beanEventType = beanEventType; + _eventAdapterService = eventAdapterService; + _copyMethod = copyMethod; + } + + public EventBean Copy(EventBean theEvent) + { + Object underlying = theEvent.Underlying; + Object copied; + try + { + copied = _copyMethod.Invoke(underlying, null); + } + catch (TargetInvocationException e) + { + Log.Error("TargetInvocationException copying event object for Update: " + e.Message, e); + return null; + } + catch (Exception e) + { + Log.Error("RuntimeException copying event object for Update: " + e.Message, e); + return null; + } + + return _eventAdapterService.AdapterForTypedObject(copied, _beanEventType); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/bean/BeanEventBeanReader.cs b/NEsper.Core/NEsper.Core/events/bean/BeanEventBeanReader.cs new file mode 100755 index 000000000..d18a3cee5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/BeanEventBeanReader.cs @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.bean +{ + /// + /// Reader for fast access to all event properties for an event backed by an object. + /// + public class BeanEventBeanReader : EventBeanReader + { + private readonly BeanEventPropertyGetter[] _getterArray; + + /// + /// Ctor. + /// + /// the type of read + public BeanEventBeanReader(BeanEventType type) + { + var properties = type.PropertyNames; + var getters = new List(); + foreach (String property in properties) + { + var getter = (BeanEventPropertyGetter)type.GetGetter(property); + if (getter != null) + { + getters.Add(getter); + } + } + _getterArray = getters.ToArray(); + } + + public Object[] Read(EventBean theEvent) + { + var underlying = theEvent.Underlying; + var values = new Object[_getterArray.Length]; + for (int i = 0; i < _getterArray.Length; i++) + { + values[i] = _getterArray[i].GetBeanProp(underlying); + } + return values; + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/bean/BeanEventBeanSerializableCopyMethod.cs b/NEsper.Core/NEsper.Core/events/bean/BeanEventBeanSerializableCopyMethod.cs new file mode 100755 index 000000000..5bb72abf6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/BeanEventBeanSerializableCopyMethod.cs @@ -0,0 +1,61 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +using com.espertech.esper.client; +using com.espertech.esper.compat.logging; +using com.espertech.esper.util; + +namespace com.espertech.esper.events.bean +{ + /// + /// Copy method for bean events utilizing serializable. + /// + public class BeanEventBeanSerializableCopyMethod : EventBeanCopyMethod + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly BeanEventType _beanEventType; + private readonly EventAdapterService _eventAdapterService; + + /// + /// Ctor. + /// + /// event type + /// for creating the event object + public BeanEventBeanSerializableCopyMethod(BeanEventType beanEventType, EventAdapterService eventAdapterService) + { + _beanEventType = beanEventType; + _eventAdapterService = eventAdapterService; + } + + public EventBean Copy(EventBean theEvent) + { + Object underlying = theEvent.Underlying; + Object copied; + try + { + copied = SerializableObjectCopier.Copy(underlying); + } + catch (IOException e) + { + Log.Error("IOException copying event object for Update: " + e.Message, e); + return null; + } + catch (TypeLoadException e) + { + Log.Error("Exception copying event object for Update: " + e.Message, e); + return null; + } + + return _eventAdapterService.AdapterForTypedObject(copied, _beanEventType); + } + } +} diff --git a/NEsper/NEsper/epl/join/rep/SingleCursorIterator.cs b/NEsper.Core/NEsper.Core/events/bean/BeanEventBeanWriter.cs old mode 100644 new mode 100755 similarity index 52% rename from NEsper/NEsper/epl/join/rep/SingleCursorIterator.cs rename to NEsper.Core/NEsper.Core/events/bean/BeanEventBeanWriter.cs index afa5bd39b..ee0def06c --- a/NEsper/NEsper/epl/join/rep/SingleCursorIterator.cs +++ b/NEsper.Core/NEsper.Core/events/bean/BeanEventBeanWriter.cs @@ -7,42 +7,33 @@ /////////////////////////////////////////////////////////////////////////////////////// using System; -using System.Collections.Generic; +using com.espertech.esper.client; using com.espertech.esper.compat; using com.espertech.esper.compat.collections; using com.espertech.esper.compat.logging; +using com.espertech.esper.events; -namespace com.espertech.esper.epl.join.rep +namespace com.espertech.esper.events.bean { - /// A utility class for an iterator that has one element. - public class SingleCursorIterator : IEnumerator { - private Cursor cursor; + /// Writer for a set of event properties to a bean event. + public class BeanEventBeanWriter : EventBeanWriter { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly BeanEventPropertyWriter[] writers; /// - /// Ctor. + /// Writes to use. /// - /// is the single element. - public SingleCursorIterator(Cursor cursor) { - this.cursor = cursor; - } - - public bool HasNext() { - return cursor != null; + /// writers + public BeanEventBeanWriter(BeanEventPropertyWriter[] writers) { + this.writers = writers; } - public Cursor Next() { - if (cursor == null) { - throw new NoSuchElementException(); + public void Write(Object[] values, EventBean theEvent) { + for (int i = 0; i < values.Length; i++) { + writers[i].Write(values[i], theEvent); } - Cursor c = cursor; - this.cursor = null; - return c; - } - - public void Remove() { - throw new UnsupportedOperationException(); } } - } // end of namespace diff --git a/NEsper.Core/NEsper.Core/events/bean/BeanEventPropertyGetter.cs b/NEsper.Core/NEsper.Core/events/bean/BeanEventPropertyGetter.cs new file mode 100755 index 000000000..457bb4269 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/BeanEventPropertyGetter.cs @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using com.espertech.esper.client; + +namespace com.espertech.esper.events.bean +{ + /// + /// Shortcut-evaluator for use with PONO-backed events only. + /// + public interface BeanEventPropertyGetter : EventPropertyGetter + { + /// + /// Returns the property as an object. + /// + /// to evaluate + /// property of object + /// PropertyAccessException if access failed + Object GetBeanProp(Object @object); + + /// + /// Returns true if the dynamic property exists. + /// + /// to evaluate + /// indicator if property exists + bool IsBeanExistsProperty(Object @object); + } +} diff --git a/NEsper.Core/NEsper.Core/events/bean/BeanEventPropertyWriter.cs b/NEsper.Core/NEsper.Core/events/bean/BeanEventPropertyWriter.cs new file mode 100755 index 000000000..1fe4b08cc --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/BeanEventPropertyWriter.cs @@ -0,0 +1,71 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using com.espertech.esper.compat.collections; + +using XLR8.CGLib; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.logging; + +namespace com.espertech.esper.events.bean +{ + /// + /// Writer for a property to an event. + /// + public class BeanEventPropertyWriter : EventPropertyWriter + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly Type _type; + private readonly FastMethod _writerMethod; + private readonly bool _valueMustNotBeNull; + + /// + /// Ctor. + /// + /// to write to + /// write method + public BeanEventPropertyWriter(Type clazz, FastMethod writerMethod) + { + var parameterType = writerMethod.Target.GetParameters()[0].ParameterType; + + _type = clazz; + _writerMethod = writerMethod; + _valueMustNotBeNull = parameterType.IsValueType && !parameterType.IsNullable(); + } + + public virtual void Write(Object value, EventBean target) + { + Invoke(new[] { value }, target.Underlying); + } + + public virtual void WriteValue(Object value, Object target) + { + Invoke(new[] { value }, target); + } + + protected void Invoke(Object[] values, Object target) + { + try + { + _writerMethod.Invoke(target, values); + } + catch (TargetInvocationException e) + { + String message = "Unexpected exception encountered invoking setter-method '" + _writerMethod.Target + "' on class '" + + _type.FullName + "' : " + e.InnerException.Message; + Log.Error(message, e); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/bean/BeanEventPropertyWriterIndexedProp.cs b/NEsper.Core/NEsper.Core/events/bean/BeanEventPropertyWriterIndexedProp.cs new file mode 100755 index 000000000..8159215af --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/BeanEventPropertyWriterIndexedProp.cs @@ -0,0 +1,31 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using XLR8.CGLib; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.bean +{ + public class BeanEventPropertyWriterIndexedProp : BeanEventPropertyWriter { + private readonly int _index; + + public BeanEventPropertyWriterIndexedProp(Type clazz, FastMethod writerMethod, int index) + : base(clazz, writerMethod) + { + _index = index; + } + + public override void Write(Object value, EventBean target) + { + base.Invoke(new Object[] {_index, value}, target.Underlying); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/bean/BeanEventPropertyWriterMapProp.cs b/NEsper.Core/NEsper.Core/events/bean/BeanEventPropertyWriterMapProp.cs new file mode 100755 index 000000000..4c6f4522d --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/BeanEventPropertyWriterMapProp.cs @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using XLR8.CGLib; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.bean +{ + public class BeanEventPropertyWriterMapProp : BeanEventPropertyWriter + { + private readonly String _key; + + public BeanEventPropertyWriterMapProp(Type clazz, FastMethod writerMethod, String key) + : base(clazz, writerMethod) + { + _key = key; + } + + public override void Write(Object value, EventBean target) + { + Invoke(new Object[] {_key, value}, target.Underlying); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/bean/BeanEventType.cs b/NEsper.Core/NEsper.Core/events/bean/BeanEventType.cs new file mode 100755 index 000000000..9fc8496d0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/BeanEventType.cs @@ -0,0 +1,1015 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.core; +using com.espertech.esper.events.property; +using com.espertech.esper.util; + +using XLR8.CGLib; + +namespace com.espertech.esper.events.bean +{ + /// + /// Implementation of the EventType interface for handling JavaBean-type classes. + /// + public class BeanEventType + : EventTypeSPI + , NativeEventType + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly Type _clazz; + private readonly string _copyMethodName; + private readonly string _endTimestampPropertyName; + private readonly EventAdapterService _eventAdapterService; + private readonly int _eventTypeId; + private readonly string _factoryMethodName; + private readonly EventTypeMetadata _metadata; + private readonly ConfigurationEventTypeLegacy _optionalLegacyDef; + private readonly IDictionary _propertyGetterCache; + private readonly PropertyResolutionStyle _propertyResolutionStyle; + private readonly string _startTimestampPropertyName; + private ISet _deepSuperTypes; + private FastClass _fastClass; + private IDictionary _indexedPropertyDescriptors; + private IDictionary> _indexedSmartPropertyTable; + private IDictionary _mappedPropertyDescriptors; + private IDictionary> _mappedSmartPropertyTable; + private IDictionary _propertyDescriptorMap; + private EventPropertyDescriptor[] _propertyDescriptors; + private string[] _propertyNames; + private IDictionary _simpleProperties; + private IDictionary> _simpleSmartPropertyTable; + private EventType[] _superTypes; + private EventPropertyDescriptor[] _writeablePropertyDescriptors; + private IDictionary> _writerMap; + + /// + /// Constructor takes a class as an argument. + /// + /// is the class of a java bean or other POJO + /// optional configuration supplying legacy event type information + /// factory for event beans and event types + /// event type metadata + /// type id + public BeanEventType( + EventTypeMetadata metadata, + int eventTypeId, + Type clazz, + EventAdapterService eventAdapterService, + ConfigurationEventTypeLegacy optionalLegacyDef) + { + _metadata = metadata; + _clazz = clazz; + _eventAdapterService = eventAdapterService; + _optionalLegacyDef = optionalLegacyDef; + _eventTypeId = eventTypeId; + + if (optionalLegacyDef != null) + { + _factoryMethodName = optionalLegacyDef.FactoryMethod; + _copyMethodName = optionalLegacyDef.CopyMethod; + _propertyResolutionStyle = optionalLegacyDef.PropertyResolutionStyle; + } + else + { + _propertyResolutionStyle = eventAdapterService.BeanEventTypeFactory.DefaultPropertyResolutionStyle; + } + _propertyGetterCache = new Dictionary(); + + Initialize(false, eventAdapterService.EngineImportService); + + EventTypeUtility.TimestampPropertyDesc desc = EventTypeUtility.ValidatedDetermineTimestampProps( + this, + optionalLegacyDef == null ? null : optionalLegacyDef.StartTimestampPropertyName, + optionalLegacyDef == null ? null : optionalLegacyDef.EndTimestampPropertyName, _superTypes); + _startTimestampPropertyName = desc.Start; + _endTimestampPropertyName = desc.End; + } + + private bool UsesSmartResolutionStyle + { + get + { + return (PropertyResolutionStyle == PropertyResolutionStyle.CASE_INSENSITIVE) || + (PropertyResolutionStyle == PropertyResolutionStyle.DISTINCT_CASE_INSENSITIVE); + } + } + + public ConfigurationEventTypeLegacy OptionalLegacyDef + { + get { return _optionalLegacyDef; } + } + + /// + /// Returns the factory methods name, or null if none defined. + /// + /// factory methods name + public string FactoryMethodName + { + get { return _factoryMethodName; } + } + + /// + /// Returns the property resolution style. + /// + /// property resolution style + public PropertyResolutionStyle PropertyResolutionStyle + { + get { return _propertyResolutionStyle; } + } + + public EventType[] DeepSuperTypes + { + get { return _deepSuperTypes.ToArray(); } + } + + /// + /// Returns the fast class reference, if code generation is used for this type, else null. + /// + /// fast class, or null if no code generation + public FastClass FastClass + { + get { return _fastClass; } + } + + public string StartTimestampPropertyName + { + get { return _startTimestampPropertyName; } + } + + public string EndTimestampPropertyName + { + get { return _endTimestampPropertyName; } + } + + public string Name + { + get { return _metadata.PublicName; } + } + + public EventPropertyDescriptor GetPropertyDescriptor(string propertyName) + { + return _propertyDescriptorMap.Get(propertyName); + } + + public int EventTypeId + { + get { return _eventTypeId; } + } + + public Type GetPropertyType(string propertyName) + { + SimplePropertyInfo simpleProp = GetSimplePropertyInfo(propertyName); + if ((simpleProp != null) && (simpleProp.Clazz != null)) + { + return simpleProp.Clazz; + } + + Property prop = PropertyParser.ParseAndWalkLaxToSimple(propertyName); + if (prop is SimpleProperty) + { + // there is no such property since it wasn't in simplePropertyTypes + return null; + } + return prop.GetPropertyType(this, _eventAdapterService); + } + + public bool IsProperty(string propertyName) + { + if (GetPropertyType(propertyName) == null) + { + return false; + } + return true; + } + + public Type UnderlyingType + { + get { return _clazz; } + } + + public EventPropertyGetter GetGetter(string propertyName) + { + EventPropertyGetter cachedGetter = _propertyGetterCache.Get(propertyName); + if (cachedGetter != null) + { + return cachedGetter; + } + + SimplePropertyInfo simpleProp = GetSimplePropertyInfo(propertyName); + if ((simpleProp != null) && (simpleProp.Getter != null)) + { + EventPropertyGetter getterX = simpleProp.Getter; + _propertyGetterCache.Put(propertyName, getterX); + return getterX; + } + + Property prop = PropertyParser.ParseAndWalkLaxToSimple(propertyName); + if (prop is SimpleProperty) + { + // there is no such property since it wasn't in simplePropertyGetters + return null; + } + + EventPropertyGetter getter = prop.GetGetter(this, _eventAdapterService); + _propertyGetterCache.Put(propertyName, getter); + return getter; + } + + public EventPropertyGetterMapped GetGetterMapped(string mappedPropertyName) + { + EventPropertyDescriptor desc = _propertyDescriptorMap.Get(mappedPropertyName); + if (desc == null || !desc.IsMapped) + { + return null; + } + var mappedProperty = new MappedProperty(mappedPropertyName); + return (EventPropertyGetterMapped) mappedProperty.GetGetter(this, _eventAdapterService); + } + + public EventPropertyGetterIndexed GetGetterIndexed(string indexedPropertyName) + { + EventPropertyDescriptor desc = _propertyDescriptorMap.Get(indexedPropertyName); + if (desc == null || !desc.IsIndexed) + { + return null; + } + var indexedProperty = new IndexedProperty(indexedPropertyName); + return (EventPropertyGetterIndexed) indexedProperty.GetGetter(this, _eventAdapterService); + } + + public string[] PropertyNames + { + get { return _propertyNames; } + } + + public EventType[] SuperTypes + { + get { return _superTypes; } + } + + public EventTypeMetadata Metadata + { + get { return _metadata; } + } + + public IList PropertyDescriptors + { + get { return _propertyDescriptors; } + } + + public FragmentEventType GetFragmentType(string propertyExpression) + { + SimplePropertyInfo simpleProp = GetSimplePropertyInfo(propertyExpression); + if ((simpleProp != null) && (simpleProp.Clazz != null)) + { + GenericPropertyDesc genericProp = simpleProp.Descriptor.GetReturnTypeGeneric(); + return EventBeanUtility.CreateNativeFragmentType( + genericProp.GenericType, genericProp.Generic, _eventAdapterService); + } + + Property prop = PropertyParser.ParseAndWalkLaxToSimple(propertyExpression); + if (prop is SimpleProperty) + { + // there is no such property since it wasn't in simplePropertyTypes + return null; + } + + GenericPropertyDesc genericPropertyDesc = prop.GetPropertyTypeGeneric(this, _eventAdapterService); + if (genericPropertyDesc == null) + { + return null; + } + return EventBeanUtility.CreateNativeFragmentType( + genericPropertyDesc.GenericType, genericPropertyDesc.Generic, _eventAdapterService); + } + + public EventPropertyDescriptor GetWritableProperty(string propertyName) + { + if (_writeablePropertyDescriptors == null) + { + InitializeWriters(); + } + Pair pair = _writerMap.Get(propertyName); + if (pair != null) + { + return pair.First; + } + + Property property = PropertyParser.ParseAndWalkLaxToSimple(propertyName); + if (property is MappedProperty) + { + EventPropertyWriter writer = GetWriter(propertyName); + if (writer == null) + { + return null; + } + var mapProp = (MappedProperty) property; + return new EventPropertyDescriptor( + mapProp.PropertyNameAtomic, typeof (Object), null, false, true, false, true, false); + } + if (property is IndexedProperty) + { + EventPropertyWriter writer = GetWriter(propertyName); + if (writer == null) + { + return null; + } + var indexedProp = (IndexedProperty) property; + return new EventPropertyDescriptor( + indexedProp.PropertyNameAtomic, typeof (Object), null, true, false, true, false, false); + } + return null; + } + + public EventPropertyDescriptor[] WriteableProperties + { + get + { + if (_writeablePropertyDescriptors == null) + { + InitializeWriters(); + } + + return _writeablePropertyDescriptors; + } + } + + public EventBeanReader Reader + { + get { return new BeanEventBeanReader(this); } + } + + public EventBeanCopyMethod GetCopyMethod(String[] properties) + { + if (_copyMethodName == null) + { + if (_clazz.IsSerializable) + { + return new BeanEventBeanSerializableCopyMethod(this, _eventAdapterService); + } + + if (_clazz.IsInterface) + { + return new BeanEventBeanSerializableCopyMethod(this, _eventAdapterService); + } + + return null; + } + MethodInfo method = null; + try + { + method = _clazz.GetMethod(_copyMethodName); + } + catch (AmbiguousMatchException e) + { + Log.Error("Configured copy-method for class '" + UnderlyingType.Name + " not found by name '" + _copyMethodName + "': " + e.Message); + } + + if (method == null) + { + if (_clazz.IsSerializable) + { + return new BeanEventBeanSerializableCopyMethod(this, _eventAdapterService); + } + + throw new EPException("Configured copy-method for class '" + _clazz.Name + " not found by name '" + _copyMethodName + "' and type is not Serializable"); + } + + return new BeanEventBeanConfiguredCopyMethod(this, _eventAdapterService, FastClass.GetMethod(method)); + } + + public EventBeanWriter GetWriter(string[] properties) + { + if (_writeablePropertyDescriptors == null) + { + InitializeWriters(); + } + + var writers = new BeanEventPropertyWriter[properties.Length]; + for (int i = 0; i < properties.Length; i++) + { + var pair = _writerMap.Get(properties[i]); + if (pair != null) + { + writers[i] = pair.Second; + } + else + { + writers[i] = (BeanEventPropertyWriter) GetWriter(properties[i]); + } + } + return new BeanEventBeanWriter(writers); + } + + public bool EqualsCompareType(EventType eventType) + { + return this == eventType; + } + + private static EventType[] GetSuperTypes(Type clazz, BeanEventTypeFactory beanEventTypeFactory) + { + var superclasses = new List(); + + // add superclass + Type superClass = clazz.BaseType; + if (superClass != null) + { + superclasses.Add(superClass); + } + + // add interfaces + Type[] interfaces = clazz.GetInterfaces(); + superclasses.AddAll(interfaces); + + // Build event types, ignoring java language types + var superTypes = new List(); + foreach (Type superclass in superclasses) + { + if (!superclass.Name.StartsWith("java")) + { + EventType superType = beanEventTypeFactory.CreateBeanType( + superclass.Name, superclass, false, false, false); + superTypes.Add(superType); + } + } + + return superTypes.ToArray(); + } + + /// + /// Add the given class's implemented interfaces and superclasses to the result set of classes. + /// + /// to introspect + /// to add classes to + internal static void GetBase(Type clazz, ISet result) + { + GetSuperInterfaces(clazz, result); + GetSuperClasses(clazz, result); + } + + private static void GetSuperInterfaces(Type clazz, ISet result) + { + Type[] interfaces = clazz.GetInterfaces(); + + for (int i = 0; i < interfaces.Length; i++) + { + result.Add(interfaces[i]); + GetSuperInterfaces(interfaces[i], result); + } + } + + private static void GetSuperClasses(Type clazz, ISet result) + { + Type superClass = clazz.BaseType; + if (superClass == null) + { + return; + } + + result.Add(superClass); + GetBase(superClass, result); + } + + private static void RemoveLibInterfaces(ICollection types) + { + Assembly coreAssembly = typeof (Object).Assembly; + Type[] coreTypes = types + .Where(type => type.Assembly == coreAssembly) + .ToArray(); + + foreach (Type type in coreTypes) + { + types.Remove(type); + } + } + + /// + /// Looks up and returns a cached simple property's descriptor. + /// + /// to look up + /// property descriptor + public InternalEventPropDescriptor GetSimpleProperty(string propertyName) + { + SimplePropertyInfo simpleProp = GetSimplePropertyInfo(propertyName); + if (simpleProp != null) + { + return simpleProp.Descriptor; + } + return null; + } + + /// + /// Looks up and returns a cached mapped property's descriptor. + /// + /// to look up + /// property descriptor + public InternalEventPropDescriptor GetMappedProperty(string propertyName) + { + if (PropertyResolutionStyle.Equals(PropertyResolutionStyle.CASE_SENSITIVE)) + { + return _mappedPropertyDescriptors.Get(propertyName); + } + if (PropertyResolutionStyle.Equals(PropertyResolutionStyle.CASE_INSENSITIVE)) + { + IList propertyInfos = _mappedSmartPropertyTable.Get(propertyName.ToLowerInvariant()); + return propertyInfos != null + ? propertyInfos[0].Descriptor + : null; + } + if (PropertyResolutionStyle.Equals(PropertyResolutionStyle.DISTINCT_CASE_INSENSITIVE)) + { + IList propertyInfos = _mappedSmartPropertyTable.Get(propertyName.ToLowerInvariant()); + if (propertyInfos != null) + { + if (propertyInfos.Count != 1) + { + throw new EPException( + "Unable to determine which property to use for \"" + propertyName + + "\" because more than one property matched"); + } + + return propertyInfos[0].Descriptor; + } + } + return null; + } + + /// + /// Looks up and returns a cached indexed property's descriptor. + /// + /// to look up + /// property descriptor + public InternalEventPropDescriptor GetIndexedProperty(string propertyName) + { + if (PropertyResolutionStyle.Equals(PropertyResolutionStyle.CASE_SENSITIVE)) + { + return _indexedPropertyDescriptors.Get(propertyName); + } + if (PropertyResolutionStyle.Equals(PropertyResolutionStyle.CASE_INSENSITIVE)) + { + IList propertyInfos = _indexedSmartPropertyTable.Get(propertyName.ToLowerInvariant()); + return propertyInfos != null + ? propertyInfos[0].Descriptor + : null; + } + if (PropertyResolutionStyle.Equals(PropertyResolutionStyle.DISTINCT_CASE_INSENSITIVE)) + { + IList propertyInfos = _indexedSmartPropertyTable.Get(propertyName.ToLowerInvariant()); + if (propertyInfos != null) + { + if (propertyInfos.Count != 1) + { + throw new EPException( + "Unable to determine which property to use for \"" + propertyName + + "\" because more than one property matched"); + } + + return propertyInfos[0].Descriptor; + } + } + return null; + } + + public override String ToString() + { + return "BeanEventType" + + " name=" + Name + + " clazz=" + _clazz.Name; + } + + private void Initialize(bool isConfigured, EngineImportService engineImportService) + { + PropertyListBuilder propertyListBuilder = PropertyListBuilderFactory.CreateBuilder(_optionalLegacyDef); + IList properties = propertyListBuilder.AssessProperties(_clazz); + + _propertyDescriptors = new EventPropertyDescriptor[properties.Count]; + _propertyDescriptorMap = new Dictionary(); + _propertyNames = new string[properties.Count]; + _simpleProperties = new Dictionary(); + _mappedPropertyDescriptors = new Dictionary(); + _indexedPropertyDescriptors = new Dictionary(); + + if (UsesSmartResolutionStyle) + { + _simpleSmartPropertyTable = new Dictionary>(); + _mappedSmartPropertyTable = new Dictionary>(); + _indexedSmartPropertyTable = new Dictionary>(); + } + + if ((_optionalLegacyDef == null) || + (_optionalLegacyDef.CodeGeneration != CodeGenerationEnum.DISABLED)) + { + // get CGLib fast class using current thread class loader + _fastClass = null; + try + { + _fastClass = FastClass.Create(_clazz); + } + catch (Exception ex) + { + Log.Warn( + ".initialize Unable to obtain CGLib fast class and/or method implementation for class " + + _clazz.Name + ", error msg is " + ex.Message, ex); + Log.Warn( + ".initialize Not using the provided class loader, the error msg is: " + + ex.Message, ex); + _fastClass = null; + } + } + + int count = 0; + foreach (InternalEventPropDescriptor desc in properties) + { + string propertyName = desc.PropertyName; + Type underlyingType; + Type componentType; + bool isRequiresIndex; + bool isRequiresMapkey; + bool isIndexed; + bool isMapped; + bool isFragment; + + if (desc.PropertyType.Equals(EventPropertyType.SIMPLE)) + { + EventPropertyGetter getter; + Type type; + if (desc.ReadMethod != null) + { + getter = PropertyHelper.GetGetter(desc.PropertyName, desc.ReadMethod, _fastClass, _eventAdapterService); + type = desc.ReadMethod.ReturnType; + } + else + { + if (desc.AccessorField == null) + { + // Ignore property + continue; + } + getter = new ReflectionPropFieldGetter(desc.AccessorField, _eventAdapterService); + type = desc.AccessorField.FieldType; + } + + underlyingType = type; + componentType = null; + isRequiresIndex = false; + isRequiresMapkey = false; + isIndexed = false; + isMapped = false; + + if (type.IsGenericDictionary()) + { + isMapped = true; + // We do not yet allow to fragment maps entries. + // Type genericType = TypeHelper.GetGenericReturnTypeMap(desc.ReadMethod, desc.AccessorField); + isFragment = false; + + if (desc.ReadMethod != null) + { + componentType = TypeHelper.GetGenericReturnTypeMap(desc.ReadMethod, false); + } + else if (desc.AccessorField != null) + { + componentType = TypeHelper.GetGenericFieldTypeMap(desc.AccessorField, false); + } + else + { + componentType = typeof (object); + } + } + else if (type.IsArray) + { + isIndexed = true; + isFragment = TypeHelper.IsFragmentableType(type.GetElementType()); + componentType = type.GetElementType(); + } + else if (type.IsImplementsInterface(typeof (IEnumerable))) + { + isIndexed = true; + Type genericType = TypeHelper.GetGenericReturnType(desc.ReadMethod, desc.AccessorField, true); + isFragment = genericType.IsFragmentableType(); + if (genericType != null) + { + componentType = genericType; + } + else + { + componentType = typeof (Object); + } + } + else + { + isMapped = false; + isFragment = type.IsFragmentableType(); + } + _simpleProperties.Put(propertyName, new SimplePropertyInfo(type, getter, desc)); + + // Recognize that there may be properties with overlapping case-insentitive names + if (UsesSmartResolutionStyle) + { + // Find the property in the smart property table + string smartPropertyName = propertyName.ToLowerInvariant(); + IList propertyInfoList = _simpleSmartPropertyTable.Get(smartPropertyName); + if (propertyInfoList == null) + { + propertyInfoList = new List(); + _simpleSmartPropertyTable.Put(smartPropertyName, propertyInfoList); + } + + // Enter the property into the smart property list + var propertyInfo = new SimplePropertyInfo(type, getter, desc); + propertyInfoList.Add(propertyInfo); + } + } + else if (desc.PropertyType.Equals(EventPropertyType.MAPPED)) + { + _mappedPropertyDescriptors.Put(propertyName, desc); + + underlyingType = desc.ReturnType; + componentType = typeof (Object); + isRequiresIndex = false; + isRequiresMapkey = desc.ReadMethod.GetParameterTypes().Length > 0; + isIndexed = false; + isMapped = true; + isFragment = false; + + // Recognize that there may be properties with overlapping case-insentitive names + if (UsesSmartResolutionStyle) + { + // Find the property in the smart property table + string smartPropertyName = propertyName.ToLowerInvariant(); + IList propertyInfoList = _mappedSmartPropertyTable.Get(smartPropertyName); + if (propertyInfoList == null) + { + propertyInfoList = new List(); + _mappedSmartPropertyTable.Put(smartPropertyName, propertyInfoList); + } + + // Enter the property into the smart property list + var propertyInfo = new SimplePropertyInfo(desc.ReturnType, null, desc); + propertyInfoList.Add(propertyInfo); + } + } + else if (desc.PropertyType.Equals(EventPropertyType.INDEXED)) + { + _indexedPropertyDescriptors.Put(propertyName, desc); + + underlyingType = desc.ReturnType; + componentType = null; + isRequiresIndex = desc.ReadMethod.GetParameterTypes().Length > 0; + isRequiresMapkey = false; + isIndexed = true; + isMapped = false; + isFragment = desc.ReturnType.IsFragmentableType(); + + if (UsesSmartResolutionStyle) + { + // Find the property in the smart property table + string smartPropertyName = propertyName.ToLowerInvariant(); + IList propertyInfoList = _indexedSmartPropertyTable.Get(smartPropertyName); + if (propertyInfoList == null) + { + propertyInfoList = new List(); + _indexedSmartPropertyTable.Put(smartPropertyName, propertyInfoList); + } + + // Enter the property into the smart property list + var propertyInfo = new SimplePropertyInfo(desc.ReturnType, null, desc); + propertyInfoList.Add(propertyInfo); + } + } + else + { + continue; + } + + _propertyNames[count] = desc.PropertyName; + var descriptor = new EventPropertyDescriptor( + desc.PropertyName, underlyingType, componentType, isRequiresIndex, isRequiresMapkey, isIndexed, isMapped, isFragment); + _propertyDescriptors[count++] = descriptor; + _propertyDescriptorMap.Put(descriptor.PropertyName, descriptor); + } + + // Determine event type super types + _superTypes = GetSuperTypes(_clazz, _eventAdapterService.BeanEventTypeFactory); + if (_superTypes != null && _superTypes.Length == 0) + { + _superTypes = null; + } + if (_metadata != null && _metadata.TypeClass == TypeClass.NAMED_WINDOW) + { + _superTypes = null; + } + + // Determine deep supertypes + // Get super types (superclasses and interfaces), deep get of all in the tree + var supers = new HashSet(); + GetBase(_clazz, supers); + RemoveLibInterfaces(supers); // Remove "java." super types + + // Cache the supertypes of this event type for later use + _deepSuperTypes = new HashSet(); + foreach (Type superClass in supers) + { + EventType superType = _eventAdapterService.BeanEventTypeFactory.CreateBeanType( + superClass.Name, superClass, false, false, isConfigured); + _deepSuperTypes.Add(superType); + } + } + + private SimplePropertyInfo GetSimplePropertyInfo(string propertyName) + { + SimplePropertyInfo propertyInfo; + IList simplePropertyInfoList; + + if (PropertyResolutionStyle.Equals(PropertyResolutionStyle.CASE_SENSITIVE)) + { + return _simpleProperties.Get(propertyName); + } + if (PropertyResolutionStyle.Equals(PropertyResolutionStyle.CASE_INSENSITIVE)) + { + propertyInfo = _simpleProperties.Get(propertyName); + if (propertyInfo != null) + { + return propertyInfo; + } + + simplePropertyInfoList = _simpleSmartPropertyTable.Get(propertyName.ToLowerInvariant()); + return + simplePropertyInfoList != null + ? simplePropertyInfoList[0] + : null; + } + if (PropertyResolutionStyle.Equals(PropertyResolutionStyle.DISTINCT_CASE_INSENSITIVE)) + { + propertyInfo = _simpleProperties.Get(propertyName); + if (propertyInfo != null) + { + return propertyInfo; + } + + simplePropertyInfoList = _simpleSmartPropertyTable.Get(propertyName.ToLowerInvariant()); + if (simplePropertyInfoList != null) + { + if (simplePropertyInfoList.Count != 1) + { + throw new EPException( + "Unable to determine which property to use for \"" + propertyName + + "\" because more than one property matched"); + } + + return simplePropertyInfoList[0]; + } + } + + return null; + } + + public EventPropertyWriter GetWriter(string propertyName) + { + if (_writeablePropertyDescriptors == null) + { + InitializeWriters(); + } + Pair pair = _writerMap.Get(propertyName); + if (pair != null) + { + return pair.Second; + } + + Property property = PropertyParser.ParseAndWalkLaxToSimple(propertyName); + if (property is MappedProperty) + { + var mapProp = (MappedProperty) property; + var methodName = PropertyHelper.GetSetterMethodName(mapProp.PropertyNameAtomic); + MethodInfo setterMethod; + try + { + setterMethod = MethodResolver.ResolveMethod( + _clazz, methodName, new Type[] { typeof (string), typeof (object) }, true, new bool[2], new bool[2]); + } + catch (EngineNoSuchMethodException e) + { + Log.Info( + "Failed to find mapped property setter method '" + methodName + "' for writing to property '" + + propertyName + "' taking {string, Object} as parameters"); + return null; + } + if (setterMethod == null) + { + return null; + } + FastMethod fastMethod = _fastClass.GetMethod(setterMethod); + return new BeanEventPropertyWriterMapProp(_clazz, fastMethod, mapProp.Key); + } + + if (property is IndexedProperty) + { + var indexedProp = (IndexedProperty) property; + var methodName = PropertyHelper.GetSetterMethodName(indexedProp.PropertyNameAtomic); + MethodInfo setterMethod; + try + { + // setterMethod = UnderlyingType.GetMethod( + // methodName, BindingFlags.Public | BindingFlags.Instance, null, + // new Type[] { typeof(int), typeof(object) }, null); + + setterMethod = MethodResolver.ResolveMethod( + _clazz, methodName, new Type[] { typeof (int), typeof (object) }, true, new bool[2], new bool[2]); + } + catch (EngineNoSuchMethodException) + { + Log.Info( + "Failed to find indexed property setter method '" + methodName + "' for writing to property '" + + propertyName + "' taking {int, Object} as parameters"); + return null; + } + if (setterMethod == null) + { + return null; + } + FastMethod fastMethod = _fastClass.GetMethod(setterMethod); + return new BeanEventPropertyWriterIndexedProp(_clazz, fastMethod, indexedProp.Index); + } + + return null; + } + + private void InitializeWriters() + { + var writables = PropertyHelper.GetWritableProperties(FastClass.TargetType); + var desc = new EventPropertyDescriptor[writables.Count]; + var writers = new Dictionary>(); + + int count = 0; + foreach (WriteablePropertyDescriptor writable in writables) + { + var propertyDesc = new EventPropertyDescriptor( + writable.PropertyName, writable.PropertyType, null, false, false, false, false, false); + desc[count++] = propertyDesc; + + FastMethod fastMethod = FastClass.GetMethod(writable.WriteMethod); + writers.Put( + writable.PropertyName, + new Pair( + propertyDesc, new BeanEventPropertyWriter(_clazz, fastMethod))); + } + + _writerMap = writers; + _writeablePropertyDescriptors = desc; + } + + /// + /// Descriptor caching the getter, class and property INFO. + /// + public class SimplePropertyInfo + { + /// + /// Ctor. + /// + /// is the class + /// is the getter + /// is the property INFO + public SimplePropertyInfo(Type clazz, EventPropertyGetter getter, InternalEventPropDescriptor descriptor) + { + Clazz = clazz; + Getter = getter; + Descriptor = descriptor; + } + + /// + /// Returns the return type. + /// + /// return type + public Type Clazz { get; private set; } + + /// + /// Returns the getter. + /// + /// getter + public EventPropertyGetter Getter { get; private set; } + + /// + /// Returns the property INFO. + /// + /// property INFO + public InternalEventPropDescriptor Descriptor { get; private set; } + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/events/bean/BeanEventTypeFactory.cs b/NEsper.Core/NEsper.Core/events/bean/BeanEventTypeFactory.cs new file mode 100755 index 000000000..28802a42b --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/BeanEventTypeFactory.cs @@ -0,0 +1,42 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.bean +{ + /// + /// Interface for a factory for obtaining instances. + /// + public interface BeanEventTypeFactory + { + /// + /// Returns the bean event type for a given class assigning the given name. + /// + /// is the name + /// is the class for which to generate an event type + /// if from static engine config + /// if configured before use + /// if the class is a configuration value, false if discovered + /// is the event type for the class + BeanEventType CreateBeanType(String name, Type clazz, bool isPreconfiguredStatic, bool isPreconfigured, bool isConfigured); + + /// Returns the bean event type for a given class assigning the given name. + /// is the class for which to generate an event type + /// is the event type for the class + BeanEventType CreateBeanTypeDefaultName(Type clazz); + + /// Returns the default property resolution style. + /// property resolution style + PropertyResolutionStyle DefaultPropertyResolutionStyle { get; } + + BeanEventType[] CachedTypes { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/events/bean/BeanInstantiator.cs b/NEsper.Core/NEsper.Core/events/bean/BeanInstantiator.cs new file mode 100755 index 000000000..18b336541 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/BeanInstantiator.cs @@ -0,0 +1,17 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.events.bean +{ + public interface BeanInstantiator + { + Object Instantiate(); + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/events/bean/BeanInstantiatorByCtor.cs b/NEsper.Core/NEsper.Core/events/bean/BeanInstantiatorByCtor.cs new file mode 100755 index 000000000..1c7d7fd5a --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/BeanInstantiatorByCtor.cs @@ -0,0 +1,58 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using com.espertech.esper.compat.logging; + +namespace com.espertech.esper.events.bean +{ + public class BeanInstantiatorByCtor : BeanInstantiator + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly ConstructorInfo _ctor; + + public BeanInstantiatorByCtor(ConstructorInfo ctor) + { + _ctor = ctor; + } + + #region BeanInstantiator Members + + public Object Instantiate() + { + try + { + return _ctor.Invoke(new object[0]); + } + catch (TargetInvocationException e) + { + String message = "Unexpected exception encountered invoking constructor '" + _ctor.Name + "' on class '" + + _ctor.DeclaringType.FullName + "': " + e.InnerException.Message; + Log.Error(message, e); + return null; + } + catch (MemberAccessException ex) + { + return Handle(ex); + } + } + + #endregion + + private Object Handle(Exception e) + { + String message = "Unexpected exception encountered invoking newInstance on class '" + + _ctor.DeclaringType.FullName + "': " + e.Message; + Log.Error(message, e); + return null; + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/events/bean/BeanInstantiatorByFactoryFastClass.cs b/NEsper.Core/NEsper.Core/events/bean/BeanInstantiatorByFactoryFastClass.cs new file mode 100755 index 000000000..06656e14d --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/BeanInstantiatorByFactoryFastClass.cs @@ -0,0 +1,52 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using XLR8.CGLib; + +using com.espertech.esper.compat.logging; + +namespace com.espertech.esper.events.bean +{ + public class BeanInstantiatorByFactoryFastClass : BeanInstantiator + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly FastMethod _method; + + public BeanInstantiatorByFactoryFastClass(FastMethod method) + { + _method = method; + } + + #region BeanInstantiator Members + + public Object Instantiate() + { + try + { + return _method.Invoke(null, null); + } + catch (TargetInvocationException e) + { + string message = + string.Format( + "Unexpected exception encountered invoking factory method '{0}' on class '{1}': {2}", + _method.Name, + _method.Target.DeclaringType.Name, + e.InnerException.Message); + Log.Error(message, e); + return null; + } + } + + #endregion + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/events/bean/BeanInstantiatorByFactoryReflection.cs b/NEsper.Core/NEsper.Core/events/bean/BeanInstantiatorByFactoryReflection.cs new file mode 100755 index 000000000..2236f0d8f --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/BeanInstantiatorByFactoryReflection.cs @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using com.espertech.esper.compat.logging; + +namespace com.espertech.esper.events.bean +{ + public class BeanInstantiatorByFactoryReflection : BeanInstantiator + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly MethodInfo _method; + + public BeanInstantiatorByFactoryReflection(MethodInfo method) + { + _method = method; + } + + public Object Instantiate() { + try + { + return _method.Invoke(null, null); + } + catch (TargetInvocationException e) + { + String message = string.Format("Unexpected exception encountered invoking factory method '{0}' on class '{1}': {2}", + _method.Name, + _method.DeclaringType.FullName, + e.InnerException.Message); + Log.Error(message, e); + return null; + } + catch (MemberAccessException ex) { + String message = string.Format("Unexpected exception encountered invoking factory method '{0}' on class '{1}': {2}", + _method.Name, + _method.DeclaringType.FullName, + ex.Message); + Log.Error(message, ex); + return null; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/bean/BeanInstantiatorByNewInstanceFastClass.cs b/NEsper.Core/NEsper.Core/events/bean/BeanInstantiatorByNewInstanceFastClass.cs new file mode 100755 index 000000000..167abca3a --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/BeanInstantiatorByNewInstanceFastClass.cs @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using XLR8.CGLib; + +using com.espertech.esper.compat.logging; + +namespace com.espertech.esper.events.bean +{ + public class BeanInstantiatorByNewInstanceFastClass : BeanInstantiator + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly FastClass _fastClass; + + public BeanInstantiatorByNewInstanceFastClass(FastClass fastClass) + { + _fastClass = fastClass; + } + + public Object Instantiate() + { + try + { + return _fastClass.NewInstance(); + } + catch (TargetInvocationException e) + { + var message = string.Format("Unexpected exception encountered invoking newInstance on class '{0}': {1}", + _fastClass.TargetType.FullName, + e.InnerException.Message); + Log.Error(message, e); + return null; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/bean/BeanInstantiatorByNewInstanceReflection.cs b/NEsper.Core/NEsper.Core/events/bean/BeanInstantiatorByNewInstanceReflection.cs new file mode 100755 index 000000000..d20c2326d --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/BeanInstantiatorByNewInstanceReflection.cs @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat.logging; + +namespace com.espertech.esper.events.bean +{ + public class BeanInstantiatorByNewInstanceReflection : BeanInstantiator + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly Type _clazz; + + public BeanInstantiatorByNewInstanceReflection(Type clazz) + { + _clazz = clazz; + } + + public Object Instantiate() + { + try + { + return Activator.CreateInstance(_clazz); + } + catch (MemberAccessException e) + { + return Handle(e); + } + } + + private Object Handle(Exception e) + { + var message = "Unexpected exception encountered invoking newInstance on class '" + _clazz.Name + "': " + e.Message; + Log.Error(message, e); + return null; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/events/bean/BeanInstantiatorFactory.cs b/NEsper.Core/NEsper.Core/events/bean/BeanInstantiatorFactory.cs new file mode 100755 index 000000000..3a65f615e --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/BeanInstantiatorFactory.cs @@ -0,0 +1,138 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using XLR8.CGLib; + +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.core; + +namespace com.espertech.esper.events.bean +{ + public class BeanInstantiatorFactory + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public static BeanInstantiator MakeInstantiator(BeanEventType beanEventType, EngineImportService engineImportService) + { + // see if we use a factory method + if (beanEventType.FactoryMethodName != null) + { + return ResolveFactoryMethod(beanEventType, engineImportService); + } + + // find public ctor + EngineImportException ctorNotFoundEx; + try + { + engineImportService.ResolveCtor(beanEventType.UnderlyingType, new Type[0]); + if (beanEventType.FastClass != null) + { + return new BeanInstantiatorByNewInstanceFastClass(beanEventType.FastClass); + } + else + { + return new BeanInstantiatorByNewInstanceReflection(beanEventType.UnderlyingType); + } + } + catch (EngineImportException ex) + { + ctorNotFoundEx = ex; + } + + // not found ctor, see if FastClass can handle + if (beanEventType.FastClass != null) + { + var fastClass = beanEventType.FastClass; + try + { + fastClass.NewInstance(); + return new BeanInstantiatorByNewInstanceFastClass(beanEventType.FastClass); + } + catch (TargetInvocationException e) + { + var message = string.Format( + "Failed to instantiate class '{0}', define a factory method if the class has no suitable constructors: {1}", + fastClass.TargetType.FullName, + (e.InnerException ?? e).Message); + Log.Debug(message, e); + } + catch (ArgumentException e) + { + var message = + string.Format( + "Failed to instantiate class '{0}', define a factory method if the class has no suitable constructors", + fastClass.TargetType.FullName); + Log.Debug(message, e); + } + } + + throw new EventBeanManufactureException( + "Failed to find no-arg constructor and no factory method has been configured and cannot use reflection to instantiate object of type " + + beanEventType.UnderlyingType.Name, ctorNotFoundEx); + } + + private static BeanInstantiator ResolveFactoryMethod(BeanEventType beanEventType, + EngineImportService engineImportService) + { + var factoryMethodName = beanEventType.FactoryMethodName; + + var lastDotIndex = factoryMethodName.LastIndexOf('.'); + if (lastDotIndex == -1) + { + try + { + var method = engineImportService.ResolveMethod( + beanEventType.UnderlyingType, factoryMethodName, new Type[0], new bool[0], new bool[0]); + if (beanEventType.FastClass != null) + { + return new BeanInstantiatorByFactoryFastClass(beanEventType.FastClass.GetMethod(method)); + } + else + { + return new BeanInstantiatorByFactoryReflection(method); + } + } + catch (EngineImportException e) + { + var message = + string.Format( + "Failed to resolve configured factory method '{0}' expected to exist for class '{1}'", + factoryMethodName, beanEventType.UnderlyingType); + Log.Info(message, e); + throw new EventBeanManufactureException(message, e); + } + } + + var className = factoryMethodName.Substring(0, lastDotIndex); + var methodName = factoryMethodName.Substring(lastDotIndex + 1); + try + { + var method = engineImportService.ResolveMethodOverloadChecked(className, methodName, new Type[0], new bool[0], new bool[0]); + if (beanEventType.FastClass != null) + { + var fastClassFactory = FastClass.Create(method.DeclaringType); + return new BeanInstantiatorByFactoryFastClass(fastClassFactory.GetMethod(method)); + } + else + { + return new BeanInstantiatorByFactoryReflection(method); + } + } + catch (EngineImportException e) + { + var message = "Failed to resolve configured factory method '" + methodName + + "' expected to exist for class '" + className + "'"; + Log.Info(message, e); + throw new EventBeanManufactureException(message, e); + } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/events/bean/CGLibPropertyGetter.cs b/NEsper.Core/NEsper.Core/events/bean/CGLibPropertyGetter.cs new file mode 100755 index 000000000..26dcfb2f2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/CGLibPropertyGetter.cs @@ -0,0 +1,112 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using XLR8.CGLib; + +using com.espertech.esper.client; +using com.espertech.esper.events.vaevent; +using com.espertech.esper.util; + +namespace com.espertech.esper.events.bean +{ + /// + /// Property getter using XLR8.CGLib's FastMethod instance. + /// + public sealed class CGLibPropertyGetter + : BaseNativePropertyGetter + , BeanEventPropertyGetter + { + private readonly Getter _fastGetter; + private readonly PropertyInfo _propInfo; + + /// + /// Constructor. + /// + /// The property. + /// is the method to use to retrieve a value from the object. + /// factory for event beans and event types + public CGLibPropertyGetter(PropertyInfo property, FastProperty fastProp, EventAdapterService eventAdapterService) + : base(eventAdapterService, fastProp.PropertyType, TypeHelper.GetGenericPropertyType(property, true)) + { + _fastGetter = fastProp.GetGetInvoker(); + _propInfo = property; + } + + public Object GetBeanProp(Object obj) + { + try + { + return _fastGetter(obj); + } + +#if false + catch (InvalidCastException e) + { + throw PropertyUtility.GetMismatchException(_propInfo.GetGetMethod(), obj, e); + } + catch (PropertyAccessException) + { + throw; + } + catch (Exception e) + { + throw new PropertyAccessException(e); + } +#else + catch (Exception e) + { + if (e is InvalidCastException) + throw PropertyUtility.GetMismatchException(_propInfo.GetGetMethod(), obj, (InvalidCastException) e); + if (e is PropertyAccessException) + throw; + + throw new PropertyAccessException(e); + } +#endif + } + + public bool IsBeanExistsProperty(Object obj) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + + public override Object Get(EventBean obj) + { + try + { + return _fastGetter(obj.Underlying); + } + catch (InvalidCastException e) + { + throw PropertyUtility.GetMismatchException(_propInfo.GetGetMethod(), obj.Underlying, e); + } + catch (PropertyAccessException) + { + throw; + } + catch (Exception e) + { + throw new PropertyAccessException(e); + } + } + + public override String ToString() + { + return "CGLibPropertyGetter " + + "fastProp =" + _fastGetter; + } + + public override bool IsExistsProperty(EventBean eventBean) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/bean/CGLibPropertyMethodGetter.cs b/NEsper.Core/NEsper.Core/events/bean/CGLibPropertyMethodGetter.cs new file mode 100755 index 000000000..c89d99f67 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/CGLibPropertyMethodGetter.cs @@ -0,0 +1,82 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using XLR8.CGLib; + +using com.espertech.esper.client; +using com.espertech.esper.events.vaevent; +using com.espertech.esper.util; + +namespace com.espertech.esper.events.bean +{ + /// + /// Property getter using XLR8.CGLib's FastMethod instance. + /// + public sealed class CGLibPropertyMethodGetter + : BaseNativePropertyGetter + , BeanEventPropertyGetter + { + private readonly FastMethod _fastMethod; + + /// + /// Constructor. + /// + /// the underlying method + /// The fast method. + /// factory for event beans and event types + public CGLibPropertyMethodGetter(MethodInfo method, FastMethod fastMethod, EventAdapterService eventAdapterService) + : base(eventAdapterService, fastMethod.ReturnType, TypeHelper.GetGenericReturnType(method, true)) + { + _fastMethod = fastMethod; + } + + public Object GetBeanProp(Object obj) + { + try + { + return _fastMethod.Invoke(obj); + } + catch (InvalidCastException e) + { + throw PropertyUtility.GetMismatchException(_fastMethod.Target, obj, e); + } + catch (PropertyAccessException) + { + throw; + } + catch (Exception e) + { + throw new PropertyAccessException(e); + } + } + + public bool IsBeanExistsProperty(Object obj) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + + public override Object Get(EventBean obj) + { + return GetBeanProp(obj.Underlying); + } + + public override String ToString() + { + return "CGLibPropertyGetter " + + "fastMethod=" + _fastMethod; + } + + public override bool IsExistsProperty(EventBean eventBean) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/bean/DynamicIndexedPropertyGetter.cs b/NEsper.Core/NEsper.Core/events/bean/DynamicIndexedPropertyGetter.cs new file mode 100755 index 000000000..69c31a912 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/DynamicIndexedPropertyGetter.cs @@ -0,0 +1,99 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.compat.magic; +using com.espertech.esper.events.vaevent; + +namespace com.espertech.esper.events.bean +{ + /// + /// Getter for a dynamic indexed property (syntax field.indexed[0]?), using vanilla + /// reflection. + /// + public class DynamicIndexedPropertyGetter : DynamicPropertyGetterBase + { + private readonly String _propertyName; + private readonly Object[] _paramList; + private readonly int _index; + private readonly EventAdapterService _eventAdapterService; + + /// + /// Ctor. + /// + /// property name + /// index to get the element at + /// factory for event beans and event types + public DynamicIndexedPropertyGetter(String propertyName, int index, EventAdapterService eventAdapterService) + : base(eventAdapterService) + { + _propertyName = propertyName; + _paramList = new Object[] { index }; + _eventAdapterService = eventAdapterService; + _index = index; + } + + protected override MethodInfo DetermineMethod(Type type) + { + var property = MagicType + .GetCachedType(type) + .ResolveProperty(_propertyName, _eventAdapterService.DefaultPropertyResolutionStyle); + + if (property == null) + return null; + + if (property.EventPropertyType == EventPropertyType.SIMPLE) { + if (property.GetMethod.ReturnType.IsArray) { + return property.GetMethod; + } + } + else if (property.EventPropertyType == EventPropertyType.INDEXED) { + return property.GetMethod; + } + + return null; + } + + protected override Object Call(DynamicPropertyDescriptor descriptor, Object underlying) + { + try + { + if (descriptor.HasParameters) + { + return descriptor.GetMethod().Invoke(underlying, _paramList); + } + + var array = (Array) descriptor.GetMethod().Invoke(underlying, null); + if (array == null) + { + return null; + } + if (array.Length <= _index) + { + return null; + } + return array.GetValue(_index); + } + catch (InvalidCastException e) + { + throw PropertyUtility.GetMismatchException(descriptor.GetMethod().Target, underlying, e); + } + catch (PropertyAccessException) + { + throw; + } + catch (Exception e) + { + throw new PropertyAccessException(e); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/bean/DynamicMappedPropertyGetter.cs b/NEsper.Core/NEsper.Core/events/bean/DynamicMappedPropertyGetter.cs new file mode 100755 index 000000000..fe26e3792 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/DynamicMappedPropertyGetter.cs @@ -0,0 +1,124 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.magic; +using com.espertech.esper.events.vaevent; + +namespace com.espertech.esper.events.bean +{ + using DataMap = System.Collections.Generic.IDictionary; + + /// + /// Getter for a dynamic mapped property (syntax field.Mapped('key')?), using + /// vanilla reflection. + /// + public class DynamicMappedPropertyGetter + : DynamicPropertyGetterBase + { + private readonly String _propertyName; + private readonly String[] _paramList; + private readonly EventAdapterService _eventAdapterService; + + /// + /// Ctor. + /// + /// property name + /// mapped access key + /// factory for event beans and event types + public DynamicMappedPropertyGetter(String fieldName, String key, EventAdapterService eventAdapterService) + : base(eventAdapterService) + { + _propertyName = fieldName; + _paramList = new[] { key }; + _eventAdapterService = eventAdapterService; + } + + protected override MethodInfo DetermineMethod(Type type) + { + var property = MagicType + .GetCachedType(type) + .ResolveProperty(_propertyName, _eventAdapterService.DefaultPropertyResolutionStyle); + + if (property == null) + return null; + + if (property.EventPropertyType == EventPropertyType.SIMPLE) + { + if (property.GetMethod.ReturnType.IsGenericStringDictionary()) + { + return property.GetMethod; + } + } + else if (property.EventPropertyType == EventPropertyType.MAPPED) + { + return property.GetMethod; + } + + return null; + } + + private Type _lastResultType; + private Func _lastResultFactory; + + protected override Object Call(DynamicPropertyDescriptor descriptor, Object underlying) + { + try + { + if (descriptor.HasParameters) + { + return descriptor.GetMethod().Invoke(underlying, _paramList); + } + + var result = descriptor.GetMethod().Invoke(underlying, null); + if ( result == null ) { + return null; + } + + if (result is DataMap) { + var resultDictionary = (DataMap) result; + return resultDictionary.Get(_paramList[0]); + } + + Func resultFactory = null; + + var resultType = result.GetType(); + if (ReferenceEquals(resultType, _lastResultType)) { + resultFactory = _lastResultFactory; + } else { + _lastResultFactory = resultFactory = MagicMarker.GetStringDictionaryFactory(resultType); + _lastResultType = resultType; + } + + if (resultFactory != null) { + var resultDictionary = resultFactory.Invoke(result); + return resultDictionary.Get(_paramList[0]); + } + + return null; + } + catch (InvalidCastException e) + { + throw PropertyUtility.GetMismatchException(descriptor.GetMethod().Target, underlying, e); + } + catch (PropertyAccessException) + { + throw; + } + catch (Exception e) + { + throw new PropertyAccessException(e); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/bean/DynamicPropertyDescriptor.cs b/NEsper.Core/NEsper.Core/events/bean/DynamicPropertyDescriptor.cs new file mode 100755 index 000000000..db3fc57bc --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/DynamicPropertyDescriptor.cs @@ -0,0 +1,67 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using XLR8.CGLib; + +namespace com.espertech.esper.events.bean +{ + /// + /// Provides method information for dynamic (unchecked) properties of each class for + /// use in obtaining property values. + /// + public class DynamicPropertyDescriptor + { + private readonly Type _clazz; + private readonly FastMethod _method; + + /// + /// Ctor. + /// + /// the class to match when looking for a method + /// the fast method to call + /// true if the method takes parameters + public DynamicPropertyDescriptor(Type clazz, FastMethod method, bool hasParameters) + { + _clazz = clazz; + _method = method; + HasParameters = hasParameters; + } + + /// + /// Returns the class for the method. + /// + /// + /// class to match on + /// + public Type GetClazz() + { + return _clazz; + } + + /// + /// Returns the method to invoke. + /// + /// + /// method to invoke + /// + public FastMethod GetMethod() + { + return _method; + } + + /// + /// Returns true if the method takes parameters. + /// + /// + /// indicator if parameters are required + /// + public bool HasParameters { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/events/bean/DynamicPropertyGetterBase.cs b/NEsper.Core/NEsper.Core/events/bean/DynamicPropertyGetterBase.cs new file mode 100755 index 000000000..5204416ea --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/DynamicPropertyGetterBase.cs @@ -0,0 +1,151 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using XLR8.CGLib; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.threading; + +namespace com.espertech.esper.events.bean +{ + /// + /// Base class for getters for a dynamic property (syntax field.inner?), caches + /// methods to use for classes. + /// + public abstract class DynamicPropertyGetterBase : BeanEventPropertyGetter + { + private readonly EventAdapterService _eventAdapterService; + private readonly CopyOnWriteList _cache; + private readonly ILockable _iLock; + + /// + /// To be implemented to return the method required, or null to indicate an + /// appropriate method could not be found. + /// + /// to search for a matching method + /// + /// method if found, or null if no matching method exists + /// + protected abstract MethodInfo DetermineMethod(Type type); + + /// + /// Call the getter to obtains the return result object, or null if no such method + /// exists. + /// + /// provides method information for the class + /// is the underlying object to ask for the property value + /// + /// underlying + /// + protected abstract Object Call(DynamicPropertyDescriptor descriptor, Object underlying); + + /// + /// Ctor. + /// + /// factory for event beans and event types + protected DynamicPropertyGetterBase(EventAdapterService eventAdapterService) + { + this._iLock = LockManager.CreateLock(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + this._cache = new CopyOnWriteList(); + this._eventAdapterService = eventAdapterService; + } + + public Object GetBeanProp(Object @object) + { + DynamicPropertyDescriptor desc = GetPopulateCache(@object); + if (desc.GetMethod() == null) + { + return null; + } + return Call(desc, @object); + } + + public bool IsBeanExistsProperty(Object @object) + { + DynamicPropertyDescriptor desc = GetPopulateCache(@object); + if (desc.GetMethod() == null) + { + return false; + } + return true; + } + + public Object Get(EventBean eventBean) + { + DynamicPropertyDescriptor desc = GetPopulateCache(eventBean.Underlying); + if (desc.GetMethod() == null) + { + return null; + } + return Call(desc, eventBean.Underlying); + } + + public bool IsExistsProperty(EventBean eventBean) + { + DynamicPropertyDescriptor desc = GetPopulateCache(eventBean.Underlying); + if (desc.GetMethod() == null) + { + return false; + } + return true; + } + + private DynamicPropertyDescriptor GetPopulateCache(Object obj) + { + // Check if the method is already there + Type target = obj.GetType(); + foreach (DynamicPropertyDescriptor desc in _cache) + { + if (desc.GetClazz() == target) + { + return desc; + } + } + + // need to add it + using (_iLock.Acquire()) + { + foreach (DynamicPropertyDescriptor desc in _cache) + { + if (desc.GetClazz() == target) + { + return desc; + } + } + + // Lookup method to use + MethodInfo method = DetermineMethod(target); + + // Cache descriptor and create fast method + DynamicPropertyDescriptor propertyDescriptor; + if (method == null) + { + propertyDescriptor = new DynamicPropertyDescriptor(target, null, false); + } + else + { + FastClass fastClass = FastClass.Create(target); + FastMethod fastMethod = fastClass.GetMethod(method); + propertyDescriptor = new DynamicPropertyDescriptor(target, fastMethod, fastMethod.ParameterCount > 0); + } + _cache.Add(propertyDescriptor); + return propertyDescriptor; + } + } + + public Object GetFragment(EventBean eventBean) + { + Object result = Get(eventBean); + return BaseNativePropertyGetter.GetFragmentDynamic(result, _eventAdapterService); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/bean/DynamicSimplePropertyGetter.cs b/NEsper.Core/NEsper.Core/events/bean/DynamicSimplePropertyGetter.cs new file mode 100755 index 000000000..aab0aebe4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/DynamicSimplePropertyGetter.cs @@ -0,0 +1,65 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.compat.magic; +using com.espertech.esper.events.vaevent; + +namespace com.espertech.esper.events.bean +{ + /// + /// Getter for a dynamic property (syntax field.inner?), using vanilla reflection. + /// + public class DynamicSimplePropertyGetter : DynamicPropertyGetterBase + { + private readonly string _propertyName; + private readonly EventAdapterService _adapterService; + + /// + /// Ctor. + /// + /// the property name + /// factory for event beans and event types + public DynamicSimplePropertyGetter(String fieldName, EventAdapterService eventAdapterService) + : base(eventAdapterService) + { + _adapterService = eventAdapterService; + _propertyName = fieldName; + } + + protected override Object Call(DynamicPropertyDescriptor descriptor, Object underlying) + { + try + { + return descriptor.GetMethod().Invoke(underlying, null); + } + catch (InvalidCastException e) + { + throw PropertyUtility.GetMismatchException(descriptor.GetMethod().Target, underlying, e); + } + catch (PropertyAccessException) + { + throw; + } + catch (Exception e) + { + throw new PropertyAccessException(e); + } + } + + protected override MethodInfo DetermineMethod(Type type) + { + return MagicType + .GetCachedType(type) + .ResolvePropertyMethod(_propertyName, _adapterService.DefaultPropertyResolutionStyle); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/bean/EnumerableFastPropertyGetter.cs b/NEsper.Core/NEsper.Core/events/bean/EnumerableFastPropertyGetter.cs new file mode 100755 index 000000000..7dbb415ef --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/EnumerableFastPropertyGetter.cs @@ -0,0 +1,119 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections; +using System.Reflection; + +using XLR8.CGLib; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.events.vaevent; +using com.espertech.esper.util; + +namespace com.espertech.esper.events.bean +{ + /// + /// Getter for a enumerable property identified by a given index, using the CGLIB fast + /// method. + /// + public class EnumerableFastPropertyGetter + : BaseNativePropertyGetter + , BeanEventPropertyGetter + , EventPropertyGetterAndIndexed + { + private readonly FastMethod _fastMethod; + private readonly int _index; + + /// + /// Constructor. + /// + /// the underlying method + /// is the method to use to retrieve a value from the object + /// is tge index within the array to get the property from + /// factory for event beans and event types + public EnumerableFastPropertyGetter(MethodInfo method, FastMethod fastMethod, int index, EventAdapterService eventAdapterService) + : base(eventAdapterService, TypeHelper.GetGenericReturnType(method, false), null) + { + _index = index; + _fastMethod = fastMethod; + + if (index < 0) + { + throw new ArgumentException("Invalid negative index value"); + } + } + + public Object GetBeanProp(Object o) + { + try + { + Object value = _fastMethod.Invoke(o); + return GetEnumerable(value, _index); + } + catch (InvalidCastException e) + { + throw PropertyUtility.GetMismatchException(_fastMethod.Target, o, e); + } + catch (PropertyAccessException) + { + throw; + } + catch (Exception e) + { + throw new PropertyAccessException(e); + } + } + + public Object Get(EventBean eventBean, int index) + { + return GetEnumerable(eventBean.Underlying, index); + } + + /// + /// Returns the enumerable at a certain index, or null. + /// + /// the enumerable + /// index + /// + /// value at index + /// + internal static Object GetEnumerable(Object value, int index) + { + var enumerable = value as IEnumerable; + if (enumerable == null) + return null; + + return enumerable.AtIndex(index); + } + + public bool IsBeanExistsProperty(Object o) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + + public override Object Get(EventBean eventBean) + { + Object underlying = eventBean.Underlying; + return GetBeanProp(underlying); + } + + public override String ToString() + { + return "EnumerableFastPropertyGetter " + + " fastMethod=" + _fastMethod + + " index=" + _index; + } + + public override bool IsExistsProperty(EventBean eventBean) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/bean/EnumerableFieldPropertyGetter.cs b/NEsper.Core/NEsper.Core/events/bean/EnumerableFieldPropertyGetter.cs new file mode 100755 index 000000000..5e76910bd --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/EnumerableFieldPropertyGetter.cs @@ -0,0 +1,100 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.events.vaevent; +using com.espertech.esper.util; + +namespace com.espertech.esper.events.bean +{ + /// + /// Getter for an enumerable property backed by a field, identified by a given index, + /// using vanilla reflection. + /// + public class EnumerableFieldPropertyGetter + : BaseNativePropertyGetter + , BeanEventPropertyGetter + , EventPropertyGetterAndIndexed + { + private readonly FieldInfo _field; + private readonly int _index; + + /// + /// Constructor. + /// + /// is the field to use to retrieve a value from the object + /// is the index within the array to get the property from + /// factory for event beans and event types + public EnumerableFieldPropertyGetter(FieldInfo field, int index, EventAdapterService eventAdapterService) + : base(eventAdapterService, TypeHelper.GetGenericFieldType(field, false), null) + { + _index = index; + _field = field; + + if (index < 0) + { + throw new ArgumentException("Invalid negative index value"); + } + } + + public Object Get(EventBean eventBean, int index) { + return GetBeanPropInternal(eventBean.Underlying, index); + } + + public Object GetBeanProp(Object @object) { + return GetBeanPropInternal(@object, _index); + } + + public Object GetBeanPropInternal(Object o, int index) + { + try + { + Object value = _field.GetValue(o); + return EnumerableFastPropertyGetter.GetEnumerable(value, index); + } + catch (InvalidCastException e) + { + throw PropertyUtility.GetMismatchException(_field, o, e); + } + catch (PropertyAccessException) + { + throw; + } + catch (Exception e) + { + throw new PropertyAccessException(e); + } + } + + public bool IsBeanExistsProperty(Object o) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + + public override Object Get(EventBean eventBean) + { + Object underlying = eventBean.Underlying; + return GetBeanProp(underlying); + } + + public override String ToString() + { + return "EnumerableFieldPropertyGetter " + + " field=" + _field + + " index=" + _index; + } + + public override bool IsExistsProperty(EventBean eventBean) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/bean/EnumerableMethodPropertyGetter.cs b/NEsper.Core/NEsper.Core/events/bean/EnumerableMethodPropertyGetter.cs new file mode 100755 index 000000000..b9bf78f63 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/EnumerableMethodPropertyGetter.cs @@ -0,0 +1,102 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.events.vaevent; +using com.espertech.esper.util; + +namespace com.espertech.esper.events.bean +{ + /// + /// Getter for an enumerable property identified by a given index, using vanilla + /// reflection. + /// + public class EnumerableMethodPropertyGetter + : BaseNativePropertyGetter + , BeanEventPropertyGetter + , EventPropertyGetterAndIndexed + { + private readonly MethodInfo _method; + private readonly int _index; + + /// + /// Constructor. + /// + /// is the method to use to retrieve a value from the object + /// is tge index within the array to get the property from + /// factory for event beans and event types + public EnumerableMethodPropertyGetter(MethodInfo method, int index, EventAdapterService eventAdapterService) + : base(eventAdapterService, TypeHelper.GetGenericReturnType(method, false), null) + { + _index = index; + _method = method; + + if (index < 0) + { + throw new ArgumentException("Invalid negative index value"); + } + } + + public Object GetBeanProp(Object o) + { + return GetBeanPropInternal(o, _index); + } + + public Object Get(EventBean eventBean, int index) + { + return GetBeanPropInternal(eventBean.Underlying, index); + } + + private Object GetBeanPropInternal(Object o, int index) + { + try + { + Object value = _method.Invoke(o, null); + return EnumerableFastPropertyGetter.GetEnumerable(value, index); + } + catch (InvalidCastException e) + { + throw PropertyUtility.GetMismatchException(_method, o, e); + } + catch (PropertyAccessException) + { + throw; + } + catch (Exception e) + { + throw new PropertyAccessException(e); + } + } + + public bool IsBeanExistsProperty(Object o) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + + public override Object Get(EventBean eventBean) + { + Object underlying = eventBean.Underlying; + return GetBeanProp(underlying); + } + + public override String ToString() + { + return "EnumerableMethodPropertyGetter " + + " method=" + _method + + " index=" + _index; + } + + public override bool IsExistsProperty(EventBean eventBean) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/bean/EventBeanManufacturerBean.cs b/NEsper.Core/NEsper.Core/events/bean/EventBeanManufacturerBean.cs new file mode 100755 index 000000000..e87250dcb --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/EventBeanManufacturerBean.cs @@ -0,0 +1,204 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; + +using XLR8.CGLib; + +using com.espertech.esper.client; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.core; + +namespace com.espertech.esper.events.bean +{ + /// + /// Factory for event beans created and populate anew from a set of values. + /// + public class EventBeanManufacturerBean : EventBeanManufacturer + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly BeanInstantiator _beanInstantiator; + private readonly BeanEventType _beanEventType; + private readonly EventAdapterService _service; + private readonly FastMethod[] _writeMethodsFastClass; + private readonly MethodInfo[] _writeMethodsReflection; + private readonly bool _hasPrimitiveTypes; + private readonly bool[] _primitiveType; + + /// Ctor. + /// target type + /// factory for events + /// written properties + /// for resolving write methods + /// EventBeanManufactureException if the write method lookup fail + public EventBeanManufacturerBean(BeanEventType beanEventType, + EventAdapterService service, + IList properties, + EngineImportService engineImportService) + { + _beanEventType = beanEventType; + _service = service; + + _beanInstantiator = BeanInstantiatorFactory.MakeInstantiator(beanEventType, engineImportService); + + _writeMethodsReflection = new MethodInfo[properties.Count]; + if (beanEventType.FastClass != null) + { + _writeMethodsFastClass = new FastMethod[properties.Count]; + } + else + { + _writeMethodsFastClass = null; + } + + bool primitiveTypeCheck = false; + _primitiveType = new bool[properties.Count]; + for (int i = 0; i < properties.Count; i++) + { + _writeMethodsReflection[i] = properties[i].WriteMethod; + if (beanEventType.FastClass != null) + { + _writeMethodsFastClass[i] = beanEventType.FastClass.GetMethod(properties[i].WriteMethod); + } + + _primitiveType[i] = properties[i].PropertyType.IsPrimitive; + primitiveTypeCheck |= _primitiveType[i]; + } + _hasPrimitiveTypes = primitiveTypeCheck; + } + + public EventBean Make(Object[] propertyValues) + { + Object outObject = MakeUnderlying(propertyValues); + return _service.AdapterForTypedObject(outObject, _beanEventType); + } + + public Object MakeUnderlying(Object[] propertyValues) + { + Object outObject = _beanInstantiator.Instantiate(); + + if (_writeMethodsFastClass != null) + { + if (!_hasPrimitiveTypes) + { + var parameters = new Object[1]; + for (int i = 0; i < _writeMethodsFastClass.Length; i++) + { + parameters[0] = propertyValues[i]; + try + { + _writeMethodsFastClass[i].Invoke(outObject, parameters); + } + catch (Exception e) + { + HandleAny(e, _writeMethodsFastClass[i].Name); + } + } + } + else + { + Object[] parameters = new Object[1]; + for (int i = 0; i < _writeMethodsFastClass.Length; i++) + { + if (_primitiveType[i]) + { + if (propertyValues[i] == null) + { + continue; + } + } + parameters[0] = propertyValues[i]; + try + { + _writeMethodsFastClass[i].Invoke(outObject, parameters); + } + catch (Exception e) + { + HandleAny(e, _writeMethodsFastClass[i].Name); + } + } + } + } + else + { + + if (!_hasPrimitiveTypes) + { + Object[] parameters = new Object[1]; + for (int i = 0; i < _writeMethodsReflection.Length; i++) + { + parameters[0] = propertyValues[i]; + try + { + _writeMethodsReflection[i].Invoke(outObject, parameters); + } + catch (MemberAccessException e) + { + Handle(e, _writeMethodsReflection[i].Name); + } + catch (Exception e) + { + String message = "Unexpected exception encountered invoking setter-method '" + _writeMethodsReflection[i] + "' on class '" + + _beanEventType.UnderlyingType.Name + "' : " + e.InnerException.Message; + Log.Error(message, e); + } + } + } + else + { + Object[] parameters = new Object[1]; + for (int i = 0; i < _writeMethodsReflection.Length; i++) + { + if (_primitiveType[i]) + { + if (propertyValues[i] == null) + { + continue; + } + } + parameters[0] = propertyValues[i]; + try + { + _writeMethodsReflection[i].Invoke(outObject, parameters); + } + catch (MemberAccessException e) + { + Handle(e, _writeMethodsReflection[i].Name); + } + catch (Exception e) + { + HandleAny(e, _writeMethodsReflection[i].Name); + } + } + } + } + + return outObject; + } + + private void HandleAny(Exception ex, String methodName) + { + if (ex is TargetInvocationException) + ex = ex.InnerException; + + String message = "Unexpected exception encountered invoking setter-method '" + methodName + "' on class '" + + _beanEventType.UnderlyingType.Name + "' : " + ex.Message; + Log.Error(message, ex); + } + + private void Handle(MemberAccessException ex, String methodName) + { + String message = "Unexpected exception encountered invoking setter-method '" + methodName + "' on class '" + + _beanEventType.UnderlyingType.Name + "' : " + ex.Message; + Log.Error(message, ex); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/bean/EventBeanManufacturerCtor.cs b/NEsper.Core/NEsper.Core/events/bean/EventBeanManufacturerCtor.cs new file mode 100755 index 000000000..3ad224d00 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/EventBeanManufacturerCtor.cs @@ -0,0 +1,43 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Reflection; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.bean +{ + public class EventBeanManufacturerCtor : EventBeanManufacturer + { + private readonly BeanEventType _beanEventType; + private readonly EventAdapterService _eventAdapterService; + private readonly ConstructorInfo _constructor; + + public EventBeanManufacturerCtor( + ConstructorInfo constructorInfo, + BeanEventType beanEventType, + EventAdapterService eventAdapterService) + { + _constructor = constructorInfo; + _beanEventType = beanEventType; + _eventAdapterService = eventAdapterService; + } + + public EventBean Make(object[] properties) + { + object instance = MakeUnderlying(properties); + return _eventAdapterService.AdapterForTypedObject(instance, _beanEventType); + } + + public object MakeUnderlying(object[] properties) + { + return InstanceManufacturerFastCtor.MakeUnderlyingFromFastCtor( + properties, _constructor, _beanEventType.UnderlyingType); + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/events/bean/InstanceManufacturer.cs b/NEsper.Core/NEsper.Core/events/bean/InstanceManufacturer.cs new file mode 100755 index 000000000..85d5bc127 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/InstanceManufacturer.cs @@ -0,0 +1,17 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.events.bean +{ + public interface InstanceManufacturer + { + object Make(EvaluateParams evaluateParams); + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/events/bean/InstanceManufacturerFactory.cs b/NEsper.Core/NEsper.Core/events/bean/InstanceManufacturerFactory.cs new file mode 100755 index 000000000..19d916f2c --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/InstanceManufacturerFactory.cs @@ -0,0 +1,39 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.collection; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; + +using XLR8.CGLib; + +namespace com.espertech.esper.events.bean +{ + public class InstanceManufacturerFactory + { + public static InstanceManufacturer GetManufacturer( + Type targetClass, + EngineImportService engineImportService, + IList childNodes) + { + var evalsUnmodified = ExprNodeUtility.GetEvaluators(childNodes); + var returnTypes = new object[evalsUnmodified.Length]; + for (int i = 0; i < evalsUnmodified.Length; i++) + { + returnTypes[i] = evalsUnmodified[i].ReturnType; + } + + Pair ctor = InstanceManufacturerUtil.GetManufacturer( + targetClass, engineImportService, evalsUnmodified, returnTypes); + return new InstanceManufacturerFastCtor(targetClass, ctor.First, ctor.Second); + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/events/bean/InstanceManufacturerFastCtor.cs b/NEsper.Core/NEsper.Core/events/bean/InstanceManufacturerFastCtor.cs new file mode 100755 index 000000000..6f634de4f --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/InstanceManufacturerFastCtor.cs @@ -0,0 +1,54 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.events.bean +{ + public class InstanceManufacturerFastCtor : InstanceManufacturer + { + private readonly FastConstructor _ctor; + private readonly ExprEvaluator[] _expr; + private readonly Type _targetClass; + + public InstanceManufacturerFastCtor(Type targetClass, FastConstructor ctor, ExprEvaluator[] expr) + { + _targetClass = targetClass; + _ctor = ctor; + _expr = expr; + } + + public object Make(EvaluateParams evaluateParams) + { + var row = new object[_expr.Length]; + for (int i = 0; i < row.Length; i++) + { + row[i] = _expr[i].Evaluate(evaluateParams); + } + return MakeUnderlyingFromFastCtor(row, _ctor, _targetClass); + } + + public static object MakeUnderlyingFromFastCtor(object[] properties, FastConstructor ctor, Type target) + { + try + { + return ctor.New(properties); + } + catch (TargetException e) + { + throw new EPException( + "InvocationTargetException received invoking constructor for type '" + target.Name + "': " + + e.InnerException.Message, e.InnerException); + } + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/events/bean/InstanceManufacturerUtil.cs b/NEsper.Core/NEsper.Core/events/bean/InstanceManufacturerUtil.cs new file mode 100755 index 000000000..cc193cbae --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/InstanceManufacturerUtil.cs @@ -0,0 +1,118 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; + +using XLR8.CGLib; + +namespace com.espertech.esper.events.bean +{ + public class InstanceManufacturerUtil + { + + public static Pair GetManufacturer(Type targetClass, EngineImportService engineImportService, ExprEvaluator[] exprEvaluators, object[] expressionReturnTypes) + { + var ctorTypes = new Type[expressionReturnTypes.Length]; + var evaluators = new ExprEvaluator[exprEvaluators.Length]; + + for (var i = 0; i < expressionReturnTypes.Length; i++) + { + var columnType = expressionReturnTypes[i]; + + if (columnType is Type || columnType == null) + { + ctorTypes[i] = (Type)expressionReturnTypes[i]; + evaluators[i] = exprEvaluators[i]; + continue; + } + + if (columnType is EventType) + { + var columnEventType = (EventType)columnType; + var returnType = columnEventType.UnderlyingType; + var inner = exprEvaluators[i]; + evaluators[i] = new ProxyExprEvaluator + { + ProcEvaluate = evaluateParams => + { + var theEvent = (EventBean)inner.Evaluate(evaluateParams); + if (theEvent != null) + { + return theEvent.Underlying; + } + return null; + }, + + ProcReturnType = () => + { + return returnType; + }, + + }; + ctorTypes[i] = returnType; + continue; + } + + // handle case where the select-clause contains an fragment array + if (columnType is EventType[]) + { + var columnEventType = ((EventType[])columnType)[0]; + var componentReturnType = columnEventType.UnderlyingType; + + var inner = exprEvaluators[i]; + evaluators[i] = new ProxyExprEvaluator + { + ProcEvaluate = evaluateParams => + { + var result = inner.Evaluate(evaluateParams); + if (!(result is EventBean[])) + { + return null; + } + var events = (EventBean[])result; + var values = Array.CreateInstance(componentReturnType, events.Length); + for (var jj = 0; jj < events.Length; jj++) + { + values.SetValue(events[jj].Underlying, jj); + } + return values; + }, + + ProcReturnType = () => + { + return componentReturnType; + }, + + }; + continue; + } + + var message = "Invalid assignment of expression " + i + " returning type '" + columnType + + "', column and parameter types mismatch"; + throw new ExprValidationException(message); + } + + FastConstructor fctor; + try + { + var ctor = engineImportService.ResolveCtor(targetClass, ctorTypes); + var fastClass = FastClass.Create(targetClass); + return new Pair(fastClass.GetConstructor(ctor), evaluators); + } + catch (EngineImportException ex) + { + throw new ExprValidationException("Failed to find a suitable constructor for type '" + targetClass.Name + "': " + ex.Message, ex); + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/events/bean/InternalEventPropDescriptor.cs b/NEsper.Core/NEsper.Core/events/bean/InternalEventPropDescriptor.cs new file mode 100755 index 000000000..151e494f6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/InternalEventPropDescriptor.cs @@ -0,0 +1,208 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using com.espertech.esper.events.property; +using com.espertech.esper.util; + +namespace com.espertech.esper.events.bean +{ + /// + /// Encapsulates the event property information available after introspecting an + /// event's class members for getter methods. + /// + public class InternalEventPropDescriptor + { + private readonly String propertyName; + private readonly MethodInfo readMethod; + private readonly FieldInfo accessorField; + private readonly EventPropertyType? propertyType; + + /// + /// Ctor. + /// + /// name of property, from getter method + /// read method to get value + /// type of property + public InternalEventPropDescriptor(String propertyName, MethodInfo readMethod, EventPropertyType? propertyType) + { + this.propertyName = propertyName; + this.readMethod = readMethod; + this.propertyType = propertyType; + } + + /// + /// Ctor. + /// + /// name of property, from getter method + /// field to get value from + /// type of property + public InternalEventPropDescriptor(String propertyName, FieldInfo accessorField, EventPropertyType? propertyType) + { + this.propertyName = propertyName; + this.accessorField = accessorField; + this.propertyType = propertyType; + } + + /// + /// Gets the declaring type for the property. + /// + /// The type of the declaring. + public Type DeclaringType + { + get + { + if (readMethod != null) + return readMethod.DeclaringType; + if (accessorField != null) + return accessorField.DeclaringType; + return null; + } + } + + /// + /// Return the property name, for mapped and indexed properties this is just the + /// property name without parantheses or brackets. + /// + /// + /// property name + /// + public string PropertyName + { + get { return propertyName; } + } + + /// + /// Returns an enum indicating the type of property: simple, mapped, indexed. + /// + /// + /// enum with property type INFO + /// + public EventPropertyType? PropertyType + { + get { return propertyType; } + } + + /// + /// Returns the read method. Can return null if the property is backed by a field.. + /// + /// + /// read method of null if field property + /// + public MethodInfo ReadMethod + { + get { return readMethod; } + } + + /// + /// Returns the accessor field. Can return null if the property is backed by a + /// method. + /// + /// + /// accessor field of null if method property + /// + public FieldInfo AccessorField + { + get { return accessorField; } + } + + /// + /// Returns the type of the underlying method or field of the event property. + /// + /// + /// return type + /// + public Type ReturnType + { + get + { + if (readMethod != null) + return readMethod.ReturnType; + return accessorField.FieldType; + } + } + + /// + /// Returns the type of the underlying method or field of the event property. + /// + /// + /// return type + /// + public GenericPropertyDesc GetReturnTypeGeneric() + { + if (readMethod != null) + { + return new GenericPropertyDesc( + readMethod.ReturnType, + TypeHelper.GetGenericReturnType(readMethod, true)); + } + + return new GenericPropertyDesc( + accessorField.FieldType, + TypeHelper.GetGenericFieldType(accessorField, true)); + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override string ToString() + { + return string.Format( + "PropertyName: {0}, ReadMethod: {1}, AccessorField: {2}, EventPropertyType: {3}", + propertyName, + readMethod, + accessorField, + propertyType); + } + + public bool Equals(InternalEventPropDescriptor obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + return + Equals(obj.propertyName, propertyName) && + Equals(obj.readMethod, readMethod) && + Equals(obj.accessorField, accessorField) && + Equals(obj.propertyType, propertyType); + } + + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// + /// true if the specified is equal to the current ; otherwise, false. + /// + /// + /// The parameter is null. + /// + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != typeof (InternalEventPropDescriptor)) return false; + return Equals((InternalEventPropDescriptor) obj); + } + + public override int GetHashCode() + { + unchecked { + int result = (propertyName != null ? propertyName.GetHashCode() : 0); + result = (result*397) ^ (readMethod != null ? readMethod.GetHashCode() : 0); + result = (result*397) ^ (accessorField != null ? accessorField.GetHashCode() : 0); + result = (result*397) ^ propertyType.GetHashCode(); + return result; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/bean/KeyedFastPropertyGetter.cs b/NEsper.Core/NEsper.Core/events/bean/KeyedFastPropertyGetter.cs new file mode 100755 index 000000000..b0f81102c --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/KeyedFastPropertyGetter.cs @@ -0,0 +1,96 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using XLR8.CGLib; +using com.espertech.esper.client; +using com.espertech.esper.events.vaevent; + +namespace com.espertech.esper.events.bean +{ + /// + /// Getter for a key property identified by a given key value, using the CGLIB fast + /// method. + /// + public class KeyedFastPropertyGetter + : BaseNativePropertyGetter + , BeanEventPropertyGetter + , EventPropertyGetterAndMapped + , EventPropertyGetterAndIndexed + { + private readonly FastMethod _fastMethod; + private readonly Object _key; + + /// + /// Constructor. + /// + /// is the method to use to retrieve a value from the object. + /// is the key to supply as parameter to the mapped property getter + /// factory for event beans and event types + public KeyedFastPropertyGetter(FastMethod fastMethod, Object key, EventAdapterService eventAdapterService) + : base(eventAdapterService, fastMethod.ReturnType, null) + { + _key = key; + _fastMethod = fastMethod; + } + + public bool IsBeanExistsProperty(Object o) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + + public override Object Get(EventBean eventBean) { + return GetBeanProp(eventBean.Underlying); + } + + public Object GetBeanProp(Object o) { + return GetBeanPropInternal(o, _key); + } + + public Object Get(EventBean eventBean, String mapKey) { + return GetBeanPropInternal(eventBean.Underlying, mapKey); + } + + public Object Get(EventBean eventBean, int index) { + return GetBeanPropInternal(eventBean.Underlying, index); + } + + public Object GetBeanPropInternal(Object o, Object key) + { + try + { + return _fastMethod.Invoke(o, key); + } + catch (InvalidCastException e) + { + throw PropertyUtility.GetMismatchException(_fastMethod.Target, o, e); + } + catch (PropertyAccessException) + { + throw; + } + catch (Exception e) + { + throw new PropertyAccessException(e); + } + } + + public override String ToString() + { + return "KeyedFastPropertyGetter " + + " fastMethod=" + _fastMethod + + " key=" + _key; + } + + public override bool IsExistsProperty(EventBean eventBean) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/bean/KeyedMapFastPropertyGetter.cs b/NEsper.Core/NEsper.Core/events/bean/KeyedMapFastPropertyGetter.cs new file mode 100755 index 000000000..733d86590 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/KeyedMapFastPropertyGetter.cs @@ -0,0 +1,99 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.compat.collections; + +using XLR8.CGLib; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.events.vaevent; +using com.espertech.esper.util; + +namespace com.espertech.esper.events.bean +{ + using DataMap = IDictionary; + + /// + /// Getter for a key property identified by a given key value of a map, using the CGLIB fast method. + /// + public class KeyedMapFastPropertyGetter + : BaseNativePropertyGetter + , BeanEventPropertyGetter + , EventPropertyGetterAndMapped + { + private readonly FastMethod _fastMethod; + private readonly String _key; + + /// Constructor. + /// the underlying method + /// is the method to use to retrieve a value from the object. + /// is the key to supply as parameter to the mapped property getter + /// factory for event beans and event types + public KeyedMapFastPropertyGetter(MethodInfo method, FastMethod fastMethod, String key, EventAdapterService eventAdapterService) + : base(eventAdapterService, TypeHelper.GetGenericReturnTypeMap(method, false), null) + { + _key = key; + _fastMethod = fastMethod; + } + + public bool IsBeanExistsProperty(Object @object) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + + public Object GetBeanProp(Object @object) + { + return GetBeanPropInternal(@object, _key); + } + + public Object Get(EventBean eventBean, String mapKey) + { + return GetBeanPropInternal(eventBean.Underlying, mapKey); + } + + public Object GetBeanPropInternal(Object @object, String key) + { + try + { + var result = _fastMethod.Invoke(@object, null) ; + return GenericExtensions.FetchKeyedValue(result, key, null); + } + catch (InvalidCastException e) + { + throw PropertyUtility.GetMismatchException(_fastMethod.Target, @object, e); + } + catch (TargetInvocationException e) + { + throw PropertyUtility.GetInvocationTargetException(_fastMethod.Target, e); + } + } + + public override Object Get(EventBean eventBean) + { + Object underlying = eventBean.Underlying; + return GetBeanProp(underlying); + } + + public override String ToString() + { + return "KeyedMapFastPropertyGetter " + + " fastMethod=" + _fastMethod + + " key=" + _key; + } + + public override bool IsExistsProperty(EventBean eventBean) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/bean/KeyedMapFieldPropertyGetter.cs b/NEsper.Core/NEsper.Core/events/bean/KeyedMapFieldPropertyGetter.cs new file mode 100755 index 000000000..23f8cd7b2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/KeyedMapFieldPropertyGetter.cs @@ -0,0 +1,98 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.events.vaevent; +using com.espertech.esper.util; + +namespace com.espertech.esper.events.bean +{ + using DataMap = IDictionary; + + /// + /// Getter for a key property identified by a given key value, using vanilla reflection. + /// + public class KeyedMapFieldPropertyGetter + : BaseNativePropertyGetter + , EventPropertyGetterAndMapped + { + private readonly FieldInfo _field; + private readonly Object _key; + + /// Constructor. + /// is the field to use to retrieve a value from the object. + /// is the key to supply as parameter to the mapped property getter + /// factory for event beans and event types + public KeyedMapFieldPropertyGetter(FieldInfo field, Object key, EventAdapterService eventAdapterService) + : base(eventAdapterService, TypeHelper.GetGenericFieldTypeMap(field, false), null) + { + _key = key; + _field = field; + } + + public Object Get(EventBean eventBean, String mapKey) + { + return GetBeanPropInternal(eventBean.Underlying, mapKey); + } + + public Object GetBeanProp(Object @object) + { + return GetBeanPropInternal(@object, _key); + } + + public Object GetBeanPropInternal(Object @object, Object key) + { + try + { + var result = _field.GetValue(@object) as DataMap; + if (result == null) + { + return null; + } + + return result.Get(Convert.ToString(key)); + } + catch (InvalidCastException e) + { + throw PropertyUtility.GetMismatchException(_field, @object, e); + } + catch (ArgumentException e) + { + throw PropertyUtility.GetIllegalArgumentException(_field, e); + } + } + + public bool IsBeanExistsProperty(Object @object) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + + public override Object Get(EventBean eventBean) + { + Object underlying = eventBean.Underlying; + return GetBeanProp(underlying); + } + + public override String ToString() + { + return "KeyedMapFieldPropertyGetter " + + " field=" + _field + + " key=" + _key; + } + + public override bool IsExistsProperty(EventBean eventBean) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/bean/KeyedMapMethodPropertyGetter.cs b/NEsper.Core/NEsper.Core/events/bean/KeyedMapMethodPropertyGetter.cs new file mode 100755 index 000000000..390d058aa --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/KeyedMapMethodPropertyGetter.cs @@ -0,0 +1,101 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.events.vaevent; +using com.espertech.esper.util; + +namespace com.espertech.esper.events.bean +{ + using DataMap = IDictionary; + + /// + /// Getter for a key property identified by a given key value, using vanilla reflection. + /// + public class KeyedMapMethodPropertyGetter + : BaseNativePropertyGetter + , EventPropertyGetterAndMapped + { + private readonly MethodInfo _method; + private readonly Object _key; + + /// Constructor. + /// is the method to use to retrieve a value from the object. + /// is the key to supply as parameter to the mapped property getter + /// factory for event beans and event types + public KeyedMapMethodPropertyGetter(MethodInfo method, Object key, EventAdapterService eventAdapterService) + : base(eventAdapterService, TypeHelper.GetGenericReturnTypeMap(method, false), null) + { + _key = key; + _method = method; + } + + public Object Get(EventBean eventBean, String mapKey) + { + return GetBeanPropInternal(eventBean.Underlying, mapKey); + } + + public Object GetBeanProp(Object @object) + { + return GetBeanPropInternal(@object, _key); + } + + public Object GetBeanPropInternal(Object @object, Object key) + { + try + { + Object result = _method.Invoke(@object, null); + if (!(result is DataMap)) { + return null; + } + DataMap resultMap = (DataMap)result; + return resultMap.Get(key); + } + catch (InvalidCastException e) + { + throw PropertyUtility.GetMismatchException(_method, @object, e); + } + catch (TargetInvocationException e) + { + throw PropertyUtility.GetInvocationTargetException(_method, e); + } + catch (ArgumentException e) + { + throw new PropertyAccessException(e); + } + } + + public bool IsBeanExistsProperty(Object @object) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + + public override Object Get(EventBean eventBean) + { + Object underlying = eventBean.Underlying; + return GetBeanProp(underlying); + } + + public override String ToString() + { + return "KeyedMapMethodPropertyGetter " + + " method=" + _method + + " key=" + _key; + } + + public override bool IsExistsProperty(EventBean eventBean) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/bean/KeyedMethodPropertyGetter.cs b/NEsper.Core/NEsper.Core/events/bean/KeyedMethodPropertyGetter.cs new file mode 100755 index 000000000..b8fb3f986 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/KeyedMethodPropertyGetter.cs @@ -0,0 +1,98 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.events.vaevent; + +namespace com.espertech.esper.events.bean +{ + /// + /// Getter for a key property identified by a given key value, using vanilla reflection. + /// + public class KeyedMethodPropertyGetter : BaseNativePropertyGetter, BeanEventPropertyGetter, EventPropertyGetterAndMapped, EventPropertyGetterAndIndexed + { + private readonly MethodInfo _method; + private readonly Object _key; + + /// Constructor. + /// is the method to use to retrieve a value from the object. + /// is the key to supply as parameter to the mapped property getter + /// factory for event beans and event types + public KeyedMethodPropertyGetter(MethodInfo method, Object key, EventAdapterService eventAdapterService) + : base(eventAdapterService, method.ReturnType, null) + { + _key = key; + _method = method; + } + + public Object Get(EventBean eventBean, int index) + { + return GetBeanPropInternal(eventBean.Underlying, index); + } + + public Object Get(EventBean eventBean, String mapKey) + { + return GetBeanPropInternal(eventBean.Underlying, mapKey); + } + + public Object GetBeanProp(Object o) + { + return GetBeanPropInternal(o, _key); + } + + private Object GetBeanPropInternal(Object o, Object key) + { + try + { + return _method.Invoke(o, new[] {key}); + } + catch (InvalidCastException e) + { + throw PropertyUtility.GetMismatchException(_method, o, e); + } + catch(TargetException e) + { + throw PropertyUtility.GetTargetException(_method, e); + } + catch (TargetInvocationException e) + { + throw PropertyUtility.GetInvocationTargetException(_method, e); + } + catch (ArgumentException e) + { + throw PropertyUtility.GetIllegalArgumentException(_method, e); + } + } + + public bool IsBeanExistsProperty(Object o) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + + public override Object Get(EventBean eventBean) + { + Object underlying = eventBean.Underlying; + return GetBeanProp(underlying); + } + + public override String ToString() + { + return "KeyedMethodPropertyGetter " + + " method=" + _method + + " key=" + _key; + } + + public override bool IsExistsProperty(EventBean eventBean) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/bean/LambdaPropertyGetter.cs b/NEsper.Core/NEsper.Core/events/bean/LambdaPropertyGetter.cs new file mode 100755 index 000000000..ae1cc27d7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/LambdaPropertyGetter.cs @@ -0,0 +1,96 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.compat.magic; +using com.espertech.esper.events.vaevent; +using com.espertech.esper.util; + +namespace com.espertech.esper.events.bean +{ + /// + /// Property getter using lambda expressions + /// + public sealed class LambdaPropertyGetter + : BaseNativePropertyGetter + , BeanEventPropertyGetter + { + private readonly Func _lambdaFunc; + private readonly MethodInfo _lambdaMethod; + + /// + /// Constructor. + /// + /// the underlying method + /// factory for event beans and event types + public LambdaPropertyGetter(MethodInfo method, EventAdapterService eventAdapterService) + : base(eventAdapterService, method.ReturnType, TypeHelper.GetGenericReturnType(method, true)) + { + _lambdaMethod = method; + _lambdaFunc = MagicType.GetLambdaAccessor(method); + } + + public Object GetBeanProp(Object obj) + { + try + { + return _lambdaFunc.Invoke(obj); + } + catch (InvalidCastException e) + { + throw PropertyUtility.GetMismatchException(_lambdaMethod, obj, e); + } + catch (PropertyAccessException) + { + throw; + } + catch (Exception e) + { + throw new PropertyAccessException(e); + } + } + + public bool IsBeanExistsProperty(Object obj) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + + public override Object Get(EventBean eventBean) + { + try + { + return _lambdaFunc.Invoke(eventBean.Underlying); + } + catch (Exception e) + { + if (e is PropertyAccessException) + throw; + if (e is NullReferenceException) + throw; + if (e is InvalidCastException) + throw PropertyUtility.GetMismatchException(_lambdaMethod, eventBean.Underlying, (InvalidCastException) e); + + throw new PropertyAccessException(e); + } + } + + public override String ToString() + { + return "LambdaPropertyGetter " + + "lambdaFunc=" + _lambdaFunc; + } + + public override bool IsExistsProperty(EventBean eventBean) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/bean/ListFastPropertyGetter.cs b/NEsper.Core/NEsper.Core/events/bean/ListFastPropertyGetter.cs new file mode 100755 index 000000000..543d25667 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/ListFastPropertyGetter.cs @@ -0,0 +1,109 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using XLR8.CGLib; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.events.vaevent; +using com.espertech.esper.util; + +namespace com.espertech.esper.events.bean +{ + /// + /// Getter for a list property identified by a given index, using the CGLIB fast method. + /// + public class ListFastPropertyGetter + : BaseNativePropertyGetter + , BeanEventPropertyGetter + , EventPropertyGetterAndIndexed + { + private readonly FastMethod _fastMethod; + private readonly int _index; + + /// Constructor. + /// the underlying method + /// is the method to use to retrieve a value from the object + /// is tge index within the array to get the property from + /// factory for event beans and event types + public ListFastPropertyGetter(MethodInfo method, FastMethod fastMethod, int index, EventAdapterService eventAdapterService) + : base(eventAdapterService, TypeHelper.GetGenericReturnType(method, false), null) + { + _index = index; + _fastMethod = fastMethod; + + if (index < 0) + { + throw new ArgumentException("Invalid negative index value"); + } + } + + public Object Get(EventBean eventBean, int index) + { + return GetBeanPropInternal(eventBean.Underlying, index); + } + + public Object GetBeanProp(Object @object) + { + return GetBeanPropInternal(@object, _index); + } + + public Object GetBeanPropInternal(Object theObject, int index) + { + try + { + var value = _fastMethod.Invoke(theObject, null); + var valueAsList = value as System.Collections.IList; + if (valueAsList != null) + { + return valueAsList.AtIndex(index, i => null); + } + + return null; + } + catch (InvalidCastException e) + { + throw PropertyUtility.GetMismatchException(_fastMethod.Target, theObject, e); + } + catch (PropertyAccessException) + { + throw; + } + catch (Exception e) + { + throw new PropertyAccessException(e); + } + } + + public bool IsBeanExistsProperty(Object @object) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + + public override Object Get(EventBean eventBean) + { + Object underlying = eventBean.Underlying; + return GetBeanProp(underlying); + } + + public override String ToString() + { + return "ListFastPropertyGetter " + + " fastMethod=" + _fastMethod + + " index=" + _index; + } + + public override bool IsExistsProperty(EventBean eventBean) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/bean/ListFieldPropertyGetter.cs b/NEsper.Core/NEsper.Core/events/bean/ListFieldPropertyGetter.cs new file mode 100755 index 000000000..7fa04b69c --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/ListFieldPropertyGetter.cs @@ -0,0 +1,102 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.events.vaevent; +using com.espertech.esper.util; + +namespace com.espertech.esper.events.bean +{ + /// + /// Getter for a list property backed by a field, identified by a given index, using vanilla reflection. + /// + public class ListFieldPropertyGetter + : BaseNativePropertyGetter + , BeanEventPropertyGetter + , EventPropertyGetterAndIndexed + { + private readonly FieldInfo _field; + private readonly int _index; + + /// Constructor. + /// is the field to use to retrieve a value from the object + /// is tge index within the array to get the property from + /// factory for event beans and event types + public ListFieldPropertyGetter(FieldInfo field, int index, EventAdapterService eventAdapterService) + : base(eventAdapterService, TypeHelper.GetGenericFieldType(field, false), null) + { + _index = index; + _field = field; + + if (index < 0) + { + throw new ArgumentException("Invalid negative index value"); + } + } + + public Object Get(EventBean eventBean, int index) + { + return GetBeanPropInternal(eventBean.Underlying, index); + } + + public Object GetBeanProp(Object o) + { + return GetBeanPropInternal(o, _index); + } + + private Object GetBeanPropInternal(Object o, int index) + { + try + { + var value = _field.GetValue(o); + var valueAsList = value as System.Collections.IList; + if (valueAsList != null) + { + return valueAsList.AtIndex(index, i => null); + } + + return null; + } + catch (InvalidCastException e) + { + throw PropertyUtility.GetMismatchException(_field, o, e); + } + catch (Exception e) + { + throw new PropertyAccessException(e); + } + } + + public bool IsBeanExistsProperty(Object o) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + + public override Object Get(EventBean eventBean) + { + Object underlying = eventBean.Underlying; + return GetBeanProp(underlying); + } + + public override String ToString() + { + return "ListFieldPropertyGetter " + + " field=" + _field + + " index=" + _index; + } + + public override bool IsExistsProperty(EventBean eventBean) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/bean/ListMethodPropertyGetter.cs b/NEsper.Core/NEsper.Core/events/bean/ListMethodPropertyGetter.cs new file mode 100755 index 000000000..73b3654e1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/ListMethodPropertyGetter.cs @@ -0,0 +1,106 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.events.vaevent; +using com.espertech.esper.util; + +namespace com.espertech.esper.events.bean +{ + /// + /// Getter for a list property identified by a given index, using vanilla reflection. + /// + public class ListMethodPropertyGetter + : BaseNativePropertyGetter + , BeanEventPropertyGetter + , EventPropertyGetterAndIndexed + { + private readonly MethodInfo _method; + private readonly int _index; + + /// Constructor. + /// is the method to use to retrieve a value from the object + /// is tge index within the array to get the property from + /// factory for event beans and event types + public ListMethodPropertyGetter(MethodInfo method, int index, EventAdapterService eventAdapterService) + : base(eventAdapterService, TypeHelper.GetGenericReturnType(method, false), null) + { + _index = index; + _method = method; + + if (index < 0) + { + throw new ArgumentException("Invalid negative index value"); + } + } + + public Object Get(EventBean eventBean, int index) + { + return GetBeanPropInternal(eventBean.Underlying, index); + } + + public Object GetBeanProp(Object @object) + { + return GetBeanPropInternal(@object, _index); + } + + public Object GetBeanPropInternal(Object @object, int index) + { + try + { + var value = _method.Invoke(@object, null); + var valueAsList = value as System.Collections.IList; + if (valueAsList != null) + { + return valueAsList.AtIndex(index, i => null); + } + + return null; + } + catch (InvalidCastException e) + { + throw PropertyUtility.GetMismatchException(_method, @object, e); + } + catch (PropertyAccessException e) + { + throw new PropertyAccessException(e); + } + catch (Exception e) + { + throw new PropertyAccessException(e); + } + } + + public bool IsBeanExistsProperty(Object @object) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + + public override Object Get(EventBean eventBean) + { + Object underlying = eventBean.Underlying; + return GetBeanProp(underlying); + } + + public override String ToString() + { + return "ListMethodPropertyGetter " + + " method=" + _method + + " index=" + _index; + } + + public override bool IsExistsProperty(EventBean eventBean) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/bean/NestedPropertyGetter.cs b/NEsper.Core/NEsper.Core/events/bean/NestedPropertyGetter.cs new file mode 100755 index 000000000..b796964f2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/NestedPropertyGetter.cs @@ -0,0 +1,103 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.bean +{ + /// + /// Getter for one or more levels deep nested properties. + /// + public sealed class NestedPropertyGetter + : BaseNativePropertyGetter + , BeanEventPropertyGetter + { + private readonly BeanEventPropertyGetter[] _getterChain; + + /// + /// Ctor. + /// + /// is the chain of getters to retrieve each nested property + /// is the cache and factory for event bean types and event wrappers + /// type of the entry returned + /// generic type parameter of the entry returned, if any + public NestedPropertyGetter(IList getterChain, EventAdapterService eventAdapterService, Type finalPropertyType, Type finalGenericType) + : base(eventAdapterService, finalPropertyType, finalGenericType) + { + _getterChain = new BeanEventPropertyGetter[getterChain.Count]; + + unchecked + { + for (int ii = 0; ii < getterChain.Count; ii++) + { + _getterChain[ii] = (BeanEventPropertyGetter) getterChain[ii]; + } + } + } + + public Object GetBeanProp(Object value) + { + if (value == null) + { + return value; + } + + unchecked + { + var getterChain = _getterChain; + for (int ii = 0; ii < getterChain.Length; ii++) + { + value = getterChain[ii].GetBeanProp(value); + + if (value == null) + { + return null; + } + } + } + return value; + } + + public bool IsBeanExistsProperty(Object value) + { + if (value == null) + { + return false; + } + + int lastElementIndex = _getterChain.Length - 1; + + // walk the getter chain up to the previous-to-last element, returning its object value. + // any null values in between mean the property does not exists + for (int i = 0; i < _getterChain.Length - 1; i++) + { + value = _getterChain[i].GetBeanProp(value); + + if (value == null) + { + return false; + } + } + + return _getterChain[lastElementIndex].IsBeanExistsProperty(value); + } + + public override Object Get(EventBean eventBean) + { + return GetBeanProp(eventBean.Underlying); + } + + public override bool IsExistsProperty(EventBean eventBean) + { + return IsBeanExistsProperty(eventBean.Underlying); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/bean/PropertyHelper.cs b/NEsper.Core/NEsper.Core/events/bean/PropertyHelper.cs new file mode 100755 index 000000000..4425b1fba --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/PropertyHelper.cs @@ -0,0 +1,233 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; + +using XLR8.CGLib; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.compat.magic; + +namespace com.espertech.esper.events.bean +{ + /// + /// This class offers utililty methods around introspection and CGLIB interaction. + /// + public class PropertyHelper + { + /// + /// Return getter for the given method and CGLIB FastClass. + /// + /// Name of the property. + /// to return getter for + /// is the CGLIB fast classs to make FastMethod for + /// factory for event beans and event types + /// property getter + public static EventPropertyGetter GetGetter(String propertyName, MethodInfo method, FastClass fastClass, EventAdapterService eventAdapterService) + { + // Construct the appropriate property getter CGLib or reflect + if (fastClass != null) + { +#if true + var fastProp = fastClass.GetProperty(propertyName); + if (fastProp != null) + return new CGLibPropertyGetter(fastProp.Target, fastProp, eventAdapterService); + else + return new CGLibPropertyMethodGetter(method, fastClass.GetMethod(method), eventAdapterService); +#else + return new LambdaPropertyGetter(method, eventAdapterService); +#endif + } + else + { + return new ReflectionPropMethodGetter(method, eventAdapterService); + } + } + + /// + /// Introspects the given class and returns event property descriptors for each + /// property found in the class itself, it's superclasses and all interfaces this class + /// and the superclasses implements. + /// + /// is the Class to introspect + /// + /// list of properties + /// + public static IList GetProperties(Type type) + { + // Determine all interfaces implemented and the interface's parent interfaces if any + ICollection propertyOrigClasses = new HashSet(); + GetImplementedInterfaceParents(type, propertyOrigClasses); + + // Add class itself + propertyOrigClasses.Add(type); + + // Get the set of property names for all classes + return GetPropertiesForTypes(propertyOrigClasses); + } + + /// + /// Introspects the given class and returns event property descriptors for each + /// writable property found in the class itself, it's superclasses and all interfaces + /// this class and the superclasses implements. + /// + /// is the Class to introspect + /// + /// list of properties + /// + public static ICollection GetWritableProperties(Type type) + { + // Determine all interfaces implemented and the interface's parent interfaces if any + ICollection propertyOrigClasses = new HashSet(); + GetImplementedInterfaceParents(type, propertyOrigClasses); + + // Add class itself + propertyOrigClasses.Add(type); + + // Get the set of property names for all classes + return GetWritablePropertiesForClasses(propertyOrigClasses); + } + + private static void GetImplementedInterfaceParents(Type clazz, ICollection classesResult) + { + var interfaces = clazz.GetInterfaces(); + + for (int i = 0; i < interfaces.Length; i++) + { + classesResult.Add(interfaces[i]); + GetImplementedInterfaceParents(interfaces[i], classesResult); + } + } + + private static ICollection GetWritablePropertiesForClasses(ICollection propertyTypes) + { + ICollection result = new HashSet(); + + foreach (var type in propertyTypes) + { + AddIntrospectPropertiesWritable(type, result); + } + + return result; + } + + private static IList GetPropertiesForTypes(IEnumerable propertyClasses) + { + var result = new List(); + foreach (var type in propertyClasses) + { + var magicType = MagicType.GetCachedType(type); + + foreach (SimpleMagicPropertyInfo propertyInfo in magicType.GetAllProperties(true).Where(p => p.GetMethod != null)) + { + result.Add(new InternalEventPropDescriptor( + propertyInfo.Name, + propertyInfo.GetMethod, + propertyInfo.EventPropertyType)); + } + } + + RemoveDuplicateProperties(result); + RemoveCLRProperties(result); + + return result; + } + + /// + /// Remove language specific properties from the given list of property + /// descriptors. + /// + /// is the list of property descriptors + public static void RemoveCLRProperties(IList properties) + { + var toRemove = new List(); + + // add removed entries to separate list + foreach (var desc in properties) + { + if (desc.DeclaringType == typeof(object)) + { + toRemove.Add(desc); + } + } + + // remove + foreach (var desc in toRemove) + { + properties.Remove(desc); + } + } + + /// + /// Removed duplicate properties using the property name to find unique properties. + /// + /// is a list of property descriptors + public static void RemoveDuplicateProperties(IList properties) + { + var set = new Dictionary(); + var toRemove = new List(); + + // add duplicates to separate list + foreach (var desc in properties) + { + if (set.ContainsKey(desc.PropertyName)) + { + toRemove.Add(desc); + continue; + } + set.Put(desc.PropertyName, desc); + } + + // remove duplicates + foreach (InternalEventPropDescriptor desc in toRemove) + { + properties.Remove(desc); + } + } + + private static void AddIntrospectPropertiesWritable(Type clazz, ICollection result) + { + MagicType magic = MagicType.GetCachedType(clazz); + + foreach (var magicProperty in magic.GetAllProperties(true).Where(p => p.CanWrite)) + { + result.Add(new WriteablePropertyDescriptor( + magicProperty.Name, + magicProperty.PropertyType, + magicProperty.SetMethod)); + } + } + + public static String GetGetterMethodName(String propertyName) + { + return GetGetterSetterMethodName(propertyName, "Get"); + } + + public static String GetSetterMethodName(String propertyName) + { + return GetGetterSetterMethodName(propertyName, "Set"); + } + + private static String GetGetterSetterMethodName(String propertyName, String operation) + { + var writer = new StringWriter(); + writer.Write(operation); + writer.Write(Char.ToUpperInvariant(propertyName[0])); + writer.Write(propertyName.Substring(1)); + return writer.ToString(); + } + + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + } +} diff --git a/NEsper.Core/NEsper.Core/events/bean/PropertyListBuilder.cs b/NEsper.Core/NEsper.Core/events/bean/PropertyListBuilder.cs new file mode 100755 index 000000000..b04de3858 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/PropertyListBuilder.cs @@ -0,0 +1,49 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.events.bean +{ + /// + /// Interface for an introspector that generates a list of event property + /// descriptors given a clazz. + /// + /// Introspect the clazz and deterime exposed event properties. + /// + + public interface PropertyListBuilder + { + /// + /// Introspect the clazz and deterime exposed event properties. + /// + /// The clazz to introspect + /// + IList AssessProperties(Type clazz); + } + + public class ProxyPropertyListBuilder : PropertyListBuilder + { + public Func> ProcAssessProperties; + + public ProxyPropertyListBuilder() + { + } + + public ProxyPropertyListBuilder(Func> procAssessProperties) + { + ProcAssessProperties = procAssessProperties; + } + + public IList AssessProperties(Type clazz) + { + return ProcAssessProperties.Invoke(clazz); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/bean/PropertyListBuilderExplicit.cs b/NEsper.Core/NEsper.Core/events/bean/PropertyListBuilderExplicit.cs new file mode 100755 index 000000000..c25b875ce --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/PropertyListBuilderExplicit.cs @@ -0,0 +1,156 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.bean +{ + /// + /// Introspector that considers explicitly configured event properties only. + /// + public class PropertyListBuilderExplicit : PropertyListBuilder + { + private readonly ConfigurationEventTypeLegacy _legacyConfig; + + /// + /// Ctor. + /// + /// is a legacy type specification containinginformation about explicitly configured fields and methods + public PropertyListBuilderExplicit(ConfigurationEventTypeLegacy legacyConfig) + { + if (legacyConfig == null) + { + throw new ArgumentException("Required configuration not passed"); + } + _legacyConfig = legacyConfig; + } + + public IList AssessProperties(Type clazz) + { + var result = new List(); + GetExplicitProperties(result, clazz, _legacyConfig); + return result; + } + + /// + /// Populates explicitly-defined properties into the result list. + /// + /// is the resulting list of event property descriptors + /// is the class to introspect + /// supplies specification of explicit methods and fields to expose + public static void GetExplicitProperties(IList result, + Type type, + ConfigurationEventTypeLegacy legacyConfig) + { + foreach (ConfigurationEventTypeLegacy.LegacyFieldPropDesc desc in legacyConfig.FieldProperties) + { + result.Add(MakeDesc(type, desc)); + } + + foreach (ConfigurationEventTypeLegacy.LegacyMethodPropDesc desc in legacyConfig.MethodProperties) + { + result.Add(MakeDesc(type, desc)); + } + } + + private static InternalEventPropDescriptor MakeDesc(Type type, ConfigurationEventTypeLegacy.LegacyMethodPropDesc methodDesc) + { + MethodInfo[] methods = type.GetMethods(); + MethodInfo method = null; + + foreach (MethodInfo _mi in methods) + { + if (_mi.Name != methodDesc.AccessorMethodName) + { + continue; + } + if (_mi.ReturnType == typeof(void)) + { + continue; + } + if (_mi.GetParameters().Length >= 2) + { + continue; + } + if (_mi.GetParameters().Length == 0) + { + method = _mi; + break; + } + + var parameterType = _mi.GetParameters()[0].ParameterType; + if ((parameterType != typeof(int)) && ((parameterType != typeof(int?))) && (parameterType != typeof(string))) + { + continue; + } + + method = _mi; + break; + } + + if (method == null) + { + throw new ConfigurationException("Configured method named '" + + methodDesc.AccessorMethodName + "' not found for class " + type.Name); + } + + return MakeMethodDesc(method, methodDesc.Name); + } + + private static InternalEventPropDescriptor MakeDesc(Type type, ConfigurationEventTypeLegacy.LegacyFieldPropDesc fieldDesc) + { + FieldInfo field = type.GetField(fieldDesc.AccessorFieldName); + if ( field != null ) { + return MakeFieldDesc(field, fieldDesc.Name); + } + + throw new ConfigurationException( + "Configured field named '" + + fieldDesc.AccessorFieldName + "' not found for class " + type.Name); + } + + /// + /// Makes a simple-type event property descriptor based on a reflected field. + /// + /// is the public field + /// is the name of the event property + /// + /// property descriptor + /// + public static InternalEventPropDescriptor MakeFieldDesc(FieldInfo field, String name) + { + return new InternalEventPropDescriptor(name, field, EventPropertyType.SIMPLE); + } + + /// + /// Makes an event property descriptor based on a reflected method, considering the + /// methods parameters to determine if this is an indexed or mapped event property. + /// + /// is the public method + /// is the name of the event property + /// + /// property descriptor + /// + public static InternalEventPropDescriptor MakeMethodDesc(MethodInfo method, String name) + { + EventPropertyType propertyType = EventPropertyType.SIMPLE; + + ParameterInfo[] methodParameters = method.GetParameters(); + if ( methodParameters.Length == 1 ) { + var parameterType = methodParameters[0].ParameterType; + propertyType = parameterType == typeof(string) ? EventPropertyType.MAPPED : EventPropertyType.INDEXED; + } + + return new InternalEventPropDescriptor(name, method, propertyType); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/bean/PropertyListBuilderFactory.cs b/NEsper.Core/NEsper.Core/events/bean/PropertyListBuilderFactory.cs new file mode 100755 index 000000000..1ac507079 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/PropertyListBuilderFactory.cs @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.bean +{ + /// + /// Factory for creates a builder/introspector for determining event property + /// descriptors based on a given class. + /// + public class PropertyListBuilderFactory + { + /// + /// Creates an implementation for a builer considering the accessor style and code + /// generation flags for a given class. + /// + /// configures how event property listy is build + /// + /// builder/introspector implementation + /// + public static PropertyListBuilder CreateBuilder(ConfigurationEventTypeLegacy optionalLegacyClassConfigs) + { + if (optionalLegacyClassConfigs == null) + { + return new PropertyListBuilderNative(null); + } + if (optionalLegacyClassConfigs.AccessorStyle == AccessorStyleEnum.NATIVE) + { + return new PropertyListBuilderNative(optionalLegacyClassConfigs); + } + if (optionalLegacyClassConfigs.AccessorStyle == AccessorStyleEnum.EXPLICIT) + { + return new PropertyListBuilderExplicit(optionalLegacyClassConfigs); + } + if (optionalLegacyClassConfigs.AccessorStyle == AccessorStyleEnum.PUBLIC) + { + return new PropertyListBuilderPublic(optionalLegacyClassConfigs); + } + throw new ArgumentException("Cannot match accessor style to property list builder"); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/bean/PropertyListBuilderNative.cs b/NEsper.Core/NEsper.Core/events/bean/PropertyListBuilderNative.cs new file mode 100755 index 000000000..417a0462c --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/PropertyListBuilderNative.cs @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.bean +{ + /// + /// Implementation for a property list builder that considers PONO methods + /// and properties as the exposed event properties, plus any explicitly + /// configured props. + /// + public class PropertyListBuilderNative : PropertyListBuilder + { + private readonly ConfigurationEventTypeLegacy _optionalLegacyConfig; + + /// + /// Ctor. + /// + /// configures legacy type, or null informationhas been supplied. + public PropertyListBuilderNative(ConfigurationEventTypeLegacy optionalLegacyConfig) + { + this._optionalLegacyConfig = optionalLegacyConfig; + } + + public IList AssessProperties(Type type) + { + IList result = PropertyHelper.GetProperties(type); + if (_optionalLegacyConfig != null) { + PropertyListBuilderExplicit.GetExplicitProperties(result, type, _optionalLegacyConfig); + } + + return result; + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/bean/PropertyListBuilderPublic.cs b/NEsper.Core/NEsper.Core/events/bean/PropertyListBuilderPublic.cs new file mode 100755 index 000000000..66d2a0093 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/PropertyListBuilderPublic.cs @@ -0,0 +1,89 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; +using com.espertech.esper.client; + +namespace com.espertech.esper.events.bean +{ + /// + /// Implementation for a property list builder that considers any public method and + /// public field as the exposed event properties, plus any explicitly configured + /// props. + /// + public class PropertyListBuilderPublic : PropertyListBuilder + { + private readonly ConfigurationEventTypeLegacy _legacyConfig; + + /// + /// Ctor. + /// + /// configures legacy type + public PropertyListBuilderPublic(ConfigurationEventTypeLegacy legacyConfig) + { + if (legacyConfig == null) + { + throw new ArgumentException("Required configuration not passed"); + } + this._legacyConfig = legacyConfig; + } + + public IList AssessProperties(Type clazz) + { + var result = new List(); + PropertyListBuilderExplicit.GetExplicitProperties(result, clazz, _legacyConfig); + AddPublicFields(result, clazz); + AddPublicMethods(result, clazz); + return result; + } + + private static void AddPublicMethods(IList result, Type clazz) + { + var methods = clazz.GetMethods(); + for (int i = 0; i < methods.Length; i++) + { + if (methods[i].ReturnType == typeof(void)) + { + continue; + } + + var parameters = methods[i].GetParameters(); + if (parameters.Length >= 2) + { + continue; + } + if (parameters.Length == 1) + { + Type parameterType = parameters[0].ParameterType; + if ((parameterType != typeof(int)) && ((parameterType != typeof(int?))) && + (parameterType != typeof(String))) + { + continue; + } + } + + InternalEventPropDescriptor desc = PropertyListBuilderExplicit.MakeMethodDesc(methods[i], methods[i].Name); + result.Add(desc); + } + + PropertyHelper.RemoveCLRProperties(result); + } + + private static void AddPublicFields(ICollection result, Type clazz) + { + FieldInfo[] fields = clazz.GetFields(); + for (int i = 0; i < fields.Length; i++) + { + InternalEventPropDescriptor desc = PropertyListBuilderExplicit.MakeFieldDesc(fields[i], fields[i].Name); + result.Add(desc); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/bean/ReflectionPropFieldGetter.cs b/NEsper.Core/NEsper.Core/events/bean/ReflectionPropFieldGetter.cs new file mode 100755 index 000000000..cf46beafa --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/ReflectionPropFieldGetter.cs @@ -0,0 +1,80 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Reflection; +using com.espertech.esper.client; +using com.espertech.esper.events.vaevent; +using com.espertech.esper.util; + +namespace com.espertech.esper.events.bean +{ + /// + /// Property getter for fields using reflection. + /// + public sealed class ReflectionPropFieldGetter + : BaseNativePropertyGetter + , BeanEventPropertyGetter + { + private readonly FieldInfo field; + + /// + /// Constructor. + /// + /// is the regular reflection field to use to obtain values for a property + /// factory for event beans and event types + public ReflectionPropFieldGetter(FieldInfo field, EventAdapterService eventAdapterService) + : base(eventAdapterService, field.FieldType, TypeHelper.GetGenericFieldType(field, true)) + { + this.field = field; + } + + public Object GetBeanProp(Object o) + { + try + { + return field.GetValue(o); + } + catch (ArgumentException e) + { + throw PropertyUtility.GetIllegalArgumentException(field, e); + } + catch (PropertyAccessException) + { + throw; + } + catch (Exception e) + { + throw new PropertyAccessException(e); + } + } + + public bool IsBeanExistsProperty(Object o) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + + public override Object Get(EventBean eventBean) + { + Object underlying = eventBean.Underlying; + return GetBeanProp(underlying); + } + + public override String ToString() + { + return "ReflectionPropFieldGetter " + + "field=" + field.ToString(); + } + + public override bool IsExistsProperty(EventBean eventBean) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/bean/ReflectionPropMethodGetter.cs b/NEsper.Core/NEsper.Core/events/bean/ReflectionPropMethodGetter.cs new file mode 100755 index 000000000..ca9ebf0b6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/ReflectionPropMethodGetter.cs @@ -0,0 +1,81 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.events.vaevent; +using com.espertech.esper.util; + +namespace com.espertech.esper.events.bean +{ + /// + /// Property getter for methods using vanilla reflection. + /// + public sealed class ReflectionPropMethodGetter + : BaseNativePropertyGetter + , BeanEventPropertyGetter + { + private readonly MethodInfo _method; + + /// + /// Constructor. + /// + /// is the regular reflection method to use to obtain values for a field. + /// factory for event beans and event types + public ReflectionPropMethodGetter(MethodInfo method, EventAdapterService eventAdapterService) + : base(eventAdapterService, method.ReturnType, TypeHelper.GetGenericReturnType(method, false)) + { + _method = method; + } + + public Object GetBeanProp(Object o) + { + try + { + return _method.Invoke(o, null); + } + catch (ArgumentException e) + { + throw PropertyUtility.GetIllegalArgumentException(_method, e); + } + catch (PropertyAccessException) + { + throw; + } + catch (Exception e) + { + throw new PropertyAccessException(e); + } + } + + public bool IsBeanExistsProperty(Object o) + { + return true; + } + + public override Object Get(EventBean eventBean) + { + Object underlying = eventBean.Underlying; + return GetBeanProp(underlying); + } + + public override String ToString() + { + return "ReflectionPropMethodGetter " + + "method=" + _method; + } + + public override bool IsExistsProperty(EventBean eventBean) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/bean/SendableEventBean.cs b/NEsper.Core/NEsper.Core/events/bean/SendableEventBean.cs new file mode 100755 index 000000000..7fc38a3c7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/SendableEventBean.cs @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.bean +{ + public class SendableEventBean : SendableEvent + { + private readonly Object _event; + + public SendableEventBean(Object theEvent) + { + _event = theEvent; + } + + public void Send(EPRuntime runtime) + { + runtime.SendEvent(_event); + } + + public object Underlying + { + get { return _event; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/bean/StringFastPropertyGetter.cs b/NEsper.Core/NEsper.Core/events/bean/StringFastPropertyGetter.cs new file mode 100755 index 000000000..a897c45d6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/StringFastPropertyGetter.cs @@ -0,0 +1,102 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.events.vaevent; + +namespace com.espertech.esper.events.bean +{ + /// + /// Getter for a string property identified by a given index, using the CGLIB fast method. + /// + public class StringFastPropertyGetter + : BaseNativePropertyGetter + , BeanEventPropertyGetter + , EventPropertyGetterAndIndexed + { + private readonly MethodInfo _method; + private readonly int _index; + + /// Constructor. + /// is the method to use to retrieve a value from the object + /// is tge index within the array to get the property from + /// factory for event beans and event types + public StringFastPropertyGetter(MethodInfo method, int index, EventAdapterService eventAdapterService) + : base(eventAdapterService, typeof(char), null) + { + _index = index; + _method = method; + + if (index < 0) + { + throw new ArgumentException("Invalid negative index value"); + } + } + + public Object GetBeanProp(Object @object) + { + return GetBeanPropInternal(@object, _index); + } + + private Object GetBeanPropInternal(Object @object, int index) + { + try + { + String value = (String)_method.Invoke(@object); + if (value.Length <= index) + { + return null; + } + + return value[index]; + } + catch (InvalidCastException e) + { + throw PropertyUtility.GetMismatchException(_method.Target, @object, e); + } + catch (PropertyAccessException) + { + throw; + } + catch (Exception e) + { + throw new PropertyAccessException(e); + } + } + + public bool IsBeanExistsProperty(Object @object) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + + public override Object Get(EventBean eventBean) + { + return GetBeanProp(eventBean.Underlying); + } + + public Object Get(EventBean eventBean, int index) + { + return GetBeanPropInternal(eventBean.Underlying, index); + } + + public override String ToString() + { + return "StringFastPropertyGetter " + + " fastMethod=" + _method + + " index=" + _index; + } + + public override bool IsExistsProperty(EventBean eventBean) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/bean/StringFieldPropertyGetter.cs b/NEsper.Core/NEsper.Core/events/bean/StringFieldPropertyGetter.cs new file mode 100755 index 000000000..32275c07c --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/StringFieldPropertyGetter.cs @@ -0,0 +1,103 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.events.vaevent; + +namespace com.espertech.esper.events.bean +{ + /// + /// Getter for a string property backed by a field, identified by a given index, using + /// vanilla reflection. + /// + public class StringFieldPropertyGetter + : BaseNativePropertyGetter + , BeanEventPropertyGetter + , EventPropertyGetterAndIndexed + { + private readonly FieldInfo _field; + private readonly int _index; + + /// Constructor. + /// is the field to use to retrieve a value from the object + /// is tge index within the array to get the property from + /// factory for event beans and event types + public StringFieldPropertyGetter(FieldInfo field, int index, EventAdapterService eventAdapterService) + : base(eventAdapterService, typeof(char), null) + { + _index = index; + _field = field; + + if (index < 0) + { + throw new ArgumentException("Invalid negative index value"); + } + } + + public Object GetBeanProp(Object @object) + { + return GetBeanPropInternal(@object, _index); + } + + private Object GetBeanPropInternal(Object @object, int index) + { + try + { + var value = (String) _field.GetValue(@object); + if (value.Length <= index) + { + return null; + } + + return value[index]; + } + catch (InvalidCastException e) + { + throw PropertyUtility.GetMismatchException(_field, @object, e); + } + catch (PropertyAccessException) + { + throw; + } + catch (Exception e) + { + throw new PropertyAccessException(e); + } + } + + public bool IsBeanExistsProperty(Object @object) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + + public override Object Get(EventBean eventBean) + { + return GetBeanProp(eventBean.Underlying); + } + + public Object Get(EventBean eventBean, int index) + { + return GetBeanPropInternal(eventBean.Underlying, index); + } + + public override String ToString() + { + return "StringFieldPropertyGetter " + + " field=" + _field + + " index=" + _index; + } + + public override bool IsExistsProperty(EventBean eventBean) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/bean/StringMethodPropertyGetter.cs b/NEsper.Core/NEsper.Core/events/bean/StringMethodPropertyGetter.cs new file mode 100755 index 000000000..602a4cba3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/bean/StringMethodPropertyGetter.cs @@ -0,0 +1,103 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.events.vaevent; + +namespace com.espertech.esper.events.bean +{ + /// + /// Getter for a string property identified by a given index, using vanilla reflection. + /// + public class StringMethodPropertyGetter + : BaseNativePropertyGetter + , BeanEventPropertyGetter + , EventPropertyGetterAndIndexed + { + private readonly MethodInfo _method; + private readonly int _index; + + /// Constructor. + /// is the method to use to retrieve a value from the object + /// is tge index within the array to get the property from + /// factory for event beans and event types + public StringMethodPropertyGetter(MethodInfo method, int index, EventAdapterService eventAdapterService) + : base(eventAdapterService, typeof(char), null) + { + _index = index; + _method = method; + + if (index < 0) + { + throw new ArgumentException("Invalid negative index value"); + } + } + + public Object Get(EventBean eventBean, int index) + { + return GetBeanPropInternal(eventBean.Underlying, index); + } + + public Object GetBeanProp(Object @object) + { + return GetBeanPropInternal(@object, _index); + } + + private Object GetBeanPropInternal(Object @object, int index) + { + try + { + var value = (String)_method.Invoke(@object, null); + if (value.Length <= index) + { + return null; + } + + return value[index]; + } + catch (InvalidCastException e) + { + throw PropertyUtility.GetMismatchException(_method, @object, e); + } + catch (PropertyAccessException) + { + throw; + } + catch (Exception e) + { + throw new PropertyAccessException(e); + } + } + + public bool IsBeanExistsProperty(Object @object) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + + public override Object Get(EventBean eventBean) + { + Object underlying = eventBean.Underlying; + return GetBeanProp(underlying); + } + + public override String ToString() + { + return "StringMethodPropertyGetter " + + " method=" + _method + + " index=" + _index; + } + + public override bool IsExistsProperty(EventBean eventBean) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/map/EventTypeNestableGetterFactoryMap.cs b/NEsper.Core/NEsper.Core/events/map/EventTypeNestableGetterFactoryMap.cs new file mode 100755 index 000000000..bfaa83294 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/map/EventTypeNestableGetterFactoryMap.cs @@ -0,0 +1,195 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.events.arr; +using com.espertech.esper.events.bean; +using com.espertech.esper.events.property; + +namespace com.espertech.esper.events.map +{ + public class EventTypeNestableGetterFactoryMap : EventTypeNestableGetterFactory + { + public EventPropertyGetter GetPropertyProvidedGetter( + IDictionary nestableTypes, + String propertyName, + Property prop, + EventAdapterService eventAdapterService) + { + return prop.GetGetterMap(nestableTypes, eventAdapterService); + } + + public EventPropertyGetterMapped GetPropertyProvidedGetterMap( + IDictionary nestableTypes, + String mappedPropertyName, + MappedProperty mappedProperty, + EventAdapterService eventAdapterService) + { + return mappedProperty.GetGetterMap(nestableTypes, eventAdapterService) as EventPropertyGetterMapped; + } + + public EventPropertyGetterIndexed GetPropertyProvidedGetterIndexed( + IDictionary nestableTypes, + String indexedPropertyName, + IndexedProperty indexedProperty, + EventAdapterService eventAdapterService) + { + return indexedProperty.GetGetterMap(nestableTypes, eventAdapterService) as EventPropertyGetterIndexed; + } + + public EventPropertyGetter GetGetterProperty( + String name, + BeanEventType nativeFragmentType, + EventAdapterService eventAdapterService) + { + return new MapEntryPropertyGetter(name, nativeFragmentType, eventAdapterService); + } + + public EventPropertyGetter GetGetterEventBean(String name) + { + return new MapEventBeanPropertyGetter(name); + } + + public EventPropertyGetter GetGetterEventBeanArray(String name, EventType eventType) + { + return new MapEventBeanArrayPropertyGetter(name, eventType.UnderlyingType); + } + + public EventPropertyGetter GetGetterBeanNestedArray( + String name, + EventType eventType, + EventAdapterService eventAdapterService) + { + return new MapFragmentArrayPropertyGetter(name, eventType, eventAdapterService); + } + + public EventPropertyGetter GetGetterIndexedEventBean(String propertyNameAtomic, int index) + { + return new MapEventBeanArrayIndexedPropertyGetter(propertyNameAtomic, index); + } + + public EventPropertyGetter GetGetterIndexedUnderlyingArray( + String propertyNameAtomic, + int index, + EventAdapterService eventAdapterService, + EventType innerType) + { + return new MapArrayPropertyGetter(propertyNameAtomic, index, eventAdapterService, innerType); + } + + public EventPropertyGetter GetGetterIndexedPONO( + String propertyNameAtomic, + int index, + EventAdapterService eventAdapterService, + Type componentType) + { + return new MapArrayEntryIndexedPropertyGetter(propertyNameAtomic, index, eventAdapterService, componentType); + } + + public EventPropertyGetter GetGetterMappedProperty(String propertyNameAtomic, String key) + { + return new MapMappedPropertyGetter(propertyNameAtomic, key); + } + + public EventPropertyGetter GetGetterIndexedEntryEventBeanArrayElement( + String propertyNameAtomic, + int index, + EventPropertyGetter nestedGetter) + { + return new MapEventBeanArrayIndexedElementPropertyGetter(propertyNameAtomic, index, nestedGetter); + } + + public EventPropertyGetter GetGetterIndexedEntryPONO( + String propertyNameAtomic, + int index, + BeanEventPropertyGetter nestedGetter, + EventAdapterService eventAdapterService, + Type propertyTypeGetter) + { + return new MapArrayBeanEntryIndexedPropertyGetter( + propertyNameAtomic, index, nestedGetter, eventAdapterService, propertyTypeGetter); + } + + public EventPropertyGetter GetGetterNestedMapProp(String propertyName, MapEventPropertyGetter getterNestedMap) + { + return new MapMapPropertyGetter(propertyName, getterNestedMap); + } + + public EventPropertyGetter GetGetterNestedPONOProp( + String propertyName, + BeanEventPropertyGetter nestedGetter, + EventAdapterService eventAdapterService, + Type nestedReturnType, + Type nestedComponentType) + { + return new MapObjectEntryPropertyGetter( + propertyName, nestedGetter, eventAdapterService, nestedReturnType, nestedComponentType); + } + + public EventPropertyGetter GetGetterNestedEventBean(String propertyName, EventPropertyGetter nestedGetter) + { + return new MapEventBeanEntryPropertyGetter(propertyName, nestedGetter); + } + + public EventPropertyGetter GetGetterNestedEntryBean( + String propertyName, + EventPropertyGetter getter, + EventType innerType, + EventAdapterService eventAdapterService) + { + if (getter is ObjectArrayEventPropertyGetter) + { + return new MapNestedEntryPropertyGetterObjectArray( + propertyName, innerType, eventAdapterService, (ObjectArrayEventPropertyGetter) getter); + } + return new MapNestedEntryPropertyGetterMap( + propertyName, innerType, eventAdapterService, (MapEventPropertyGetter) getter); + } + + public EventPropertyGetter GetGetterNestedEntryBeanArray( + String propertyNameAtomic, + int index, + EventPropertyGetter getter, + EventType innerType, + EventAdapterService eventAdapterService) + { + if (getter is ObjectArrayEventPropertyGetter) + { + return new MapNestedEntryPropertyGetterArrayObjectArray( + propertyNameAtomic, innerType, eventAdapterService, index, (ObjectArrayEventPropertyGetter) getter); + } + return new MapNestedEntryPropertyGetterArrayMap( + propertyNameAtomic, innerType, eventAdapterService, index, (MapEventPropertyGetter) getter); + } + + public EventPropertyGetter GetGetterBeanNested( + String name, + EventType eventType, + EventAdapterService eventAdapterService) + { + if (eventType is ObjectArrayEventType) + { + return new MapPropertyGetterDefaultObjectArray(name, eventType, eventAdapterService); + } + return new MapPropertyGetterDefaultMap(name, eventType, eventAdapterService); + } + + public EventPropertyGetter GetGetterNestedPropertyProvidedGetterDynamic( + IDictionary nestableTypes, + String propertyName, + EventPropertyGetter nestedGetter, + EventAdapterService eventAdapterService) + { + return new MapNestedEntryPropertyGetterPropertyProvidedDynamic( + propertyName, null, eventAdapterService, nestedGetter); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/map/MapArrayBeanEntryIndexedPropertyGetter.cs b/NEsper.Core/NEsper.Core/events/map/MapArrayBeanEntryIndexedPropertyGetter.cs new file mode 100755 index 000000000..c7b21fc13 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/map/MapArrayBeanEntryIndexedPropertyGetter.cs @@ -0,0 +1,70 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.events.bean; + +namespace com.espertech.esper.events.map +{ + /// + /// A getter that works on PONO events residing within a Map as an event property. + /// + public class MapArrayBeanEntryIndexedPropertyGetter + : BaseNativePropertyGetter + , MapEventPropertyGetter + { + private readonly String _propertyMap; + private readonly int _index; + private readonly BeanEventPropertyGetter _nestedGetter; + + /// Ctor. + /// the property to look at + /// the getter for the map entry + /// for producing wrappers to objects + /// the index to fetch the array element for + /// type of the entry returned + public MapArrayBeanEntryIndexedPropertyGetter(String propertyMap, + int index, + BeanEventPropertyGetter nestedGetter, + EventAdapterService eventAdapterService, + Type returnType) + : base(eventAdapterService, returnType, null) + { + _propertyMap = propertyMap; + _index = index; + _nestedGetter = nestedGetter; + } + + public Object GetMap(IDictionary map) + { + // If the map does not contain the key, this is allowed and represented as null + Object value = map.Get(_propertyMap); + return BaseNestableEventUtil.GetBeanArrayValue(_nestedGetter, value, _index); + } + + public bool IsMapExistsProperty(IDictionary map) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + + public override Object Get(EventBean obj) + { + IDictionary map = BaseNestableEventUtil.CheckedCastUnderlyingMap(obj); + return GetMap(map); + } + + public override bool IsExistsProperty(EventBean eventBean) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/map/MapArrayEntryIndexedPropertyGetter.cs b/NEsper.Core/NEsper.Core/events/map/MapArrayEntryIndexedPropertyGetter.cs new file mode 100755 index 000000000..98deb88b1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/map/MapArrayEntryIndexedPropertyGetter.cs @@ -0,0 +1,78 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.events.bean; + +using DataMap = System.Collections.Generic.IDictionary; + +namespace com.espertech.esper.events.map +{ + /// + /// A getter that works on arrays residing within a Map as an event property. + /// + public class MapArrayEntryIndexedPropertyGetter + : BaseNativePropertyGetter + , MapEventPropertyGetter + , MapEventPropertyGetterAndIndexed + { + private readonly String _propertyMap; + private readonly int _index; + + /// + /// Ctor. + /// + /// the property to use for the map lookup + /// the index to fetch the array element for + /// factory for event beans and event types + /// type of the entry returned + public MapArrayEntryIndexedPropertyGetter(String propertyMap, int index, EventAdapterService eventAdapterService, Type returnType) + : base(eventAdapterService, returnType, null) + { + _propertyMap = propertyMap; + _index = index; + } + + public Object GetMap(DataMap map) + { + return GetMapInternal(map, _index); + } + + public Object GetMapInternal(DataMap map, int index) + { + var value = map.Get(_propertyMap); + return BaseNestableEventUtil.GetIndexedValue(value, index); + } + + + public bool IsMapExistsProperty(DataMap map) + { + return map.ContainsKey(_propertyMap); + } + + public Object Get(EventBean eventBean, int index) + { + var map = BaseNestableEventUtil.CheckedCastUnderlyingMap(eventBean); + return GetMapInternal(map, index); + } + + public override Object Get(EventBean eventBean) + { + return GetMap(BaseNestableEventUtil.CheckedCastUnderlyingMap(eventBean)); + } + + public override bool IsExistsProperty(EventBean eventBean) + { + var map = BaseNestableEventUtil.CheckedCastUnderlyingMap(eventBean); + return map.ContainsKey(_propertyMap); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/map/MapArrayPropertyGetter.cs b/NEsper.Core/NEsper.Core/events/map/MapArrayPropertyGetter.cs new file mode 100755 index 000000000..8c9885df8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/map/MapArrayPropertyGetter.cs @@ -0,0 +1,81 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.events.map +{ + /// + /// Getter for Map-entries with well-defined fragment type. + /// + public class MapArrayPropertyGetter + : MapEventPropertyGetter + , MapEventPropertyGetterAndIndexed + { + private readonly String _propertyName; + private readonly int _index; + private readonly EventAdapterService _eventAdapterService; + private readonly EventType _fragmentType; + + /// Ctor. + /// property name + /// array index + /// factory for event beans and event types + /// type of the entry returned + public MapArrayPropertyGetter(String propertyNameAtomic, int index, EventAdapterService eventAdapterService, EventType fragmentType) + { + _propertyName = propertyNameAtomic; + _index = index; + _fragmentType = fragmentType; + _eventAdapterService = eventAdapterService; + } + + public bool IsMapExistsProperty(IDictionary map) + { + return true; + } + + public Object GetMap(IDictionary map) + { + return GetMapInternal(map, _index); + } + + public Object Get(EventBean eventBean, int index) + { + IDictionary map = BaseNestableEventUtil.CheckedCastUnderlyingMap(eventBean); + return GetMapInternal(map, index); + } + + public Object Get(EventBean eventBean) + { + IDictionary map = BaseNestableEventUtil.CheckedCastUnderlyingMap(eventBean); + return GetMap(map); + } + + private Object GetMapInternal(IDictionary map, int index) + { + Object value = map.Get(_propertyName); + return BaseNestableEventUtil.GetIndexedValue(value, index); + } + + public bool IsExistsProperty(EventBean eventBean) + { + return true; + } + + public Object GetFragment(EventBean obj) + { + Object fragmentUnderlying = Get(obj); + return BaseNestableEventUtil.GetFragmentNonPono(_eventAdapterService, fragmentUnderlying, _fragmentType); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/map/MapDynamicPropertyGetter.cs b/NEsper.Core/NEsper.Core/events/map/MapDynamicPropertyGetter.cs new file mode 100755 index 000000000..d52b01cea --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/map/MapDynamicPropertyGetter.cs @@ -0,0 +1,56 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.events.map +{ + using Map = IDictionary; + + public class MapDynamicPropertyGetter : MapEventPropertyGetter + { + + private readonly String _propertyName; + + public MapDynamicPropertyGetter(String propertyName) + { + _propertyName = propertyName; + } + + public Object GetMap(IDictionary map) + { + return map.Get(_propertyName); + } + + public bool IsMapExistsProperty(IDictionary map) + { + return map.ContainsKey(_propertyName); + } + + public Object Get(EventBean eventBean) + { + var map = (Map)eventBean.Underlying; + return map.Get(_propertyName); + } + + public bool IsExistsProperty(EventBean eventBean) + { + var map = (Map)eventBean.Underlying; + return map.ContainsKey(_propertyName); + } + + public Object GetFragment(EventBean eventBean) + { + return null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/map/MapEntryPropertyGetter.cs b/NEsper.Core/NEsper.Core/events/map/MapEntryPropertyGetter.cs new file mode 100755 index 000000000..1a1f0738a --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/map/MapEntryPropertyGetter.cs @@ -0,0 +1,72 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.events.bean; + +namespace com.espertech.esper.events.map +{ + using DataMap = IDictionary; + + /// + /// A getter for use with Map-based events simply returns the value for the key. + /// + public class MapEntryPropertyGetter : MapEventPropertyGetter + { + private readonly String _propertyName; + private readonly EventAdapterService _eventAdapterService; + private readonly BeanEventType _eventType; + + /// + /// Ctor. + /// + /// property to get + /// factory for event beans and event types + /// type of the entry returned + public MapEntryPropertyGetter(String propertyName, BeanEventType eventType, EventAdapterService eventAdapterService) + { + _propertyName = propertyName; + _eventAdapterService = eventAdapterService; + _eventType = eventType; + } + + public Object GetMap(DataMap map) + { + return map.Get(_propertyName); + } + + public bool IsMapExistsProperty(DataMap map) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + + public Object Get(EventBean eventBean) + { + return GetMap(BaseNestableEventUtil.CheckedCastUnderlyingMap(eventBean)); + } + + public bool IsExistsProperty(EventBean eventBean) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + + public Object GetFragment(EventBean eventBean) + { + if (_eventType == null) + { + return null; + } + Object result = Get(eventBean); + return BaseNestableEventUtil.GetFragmentPono(result, _eventType, _eventAdapterService); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/map/MapEventBean.cs b/NEsper.Core/NEsper.Core/events/map/MapEventBean.cs new file mode 100755 index 000000000..362ebd878 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/map/MapEventBean.cs @@ -0,0 +1,102 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.map +{ + /// + /// Wrapper for events represented by a Map of key-value pairs that are the event properties. + /// MapEventBean instances are equal if they have the same and all property names + /// and values are reference-equal. + /// + public class MapEventBean + : EventBeanSPI + , MappedEventBean + { + private readonly EventType _eventType; + private IDictionary _properties; + + /// + /// Constructor for initialization with existing values. + /// Makes a shallow copy of the supplied values to not be surprised by changing property values. + /// + /// are the event property values + /// is the type of the event, i.e. describes the map entries + public MapEventBean(IDictionary properties, EventType eventType) + { + _properties = properties; + _eventType = eventType; + } + + /// + /// Constructor for the mutable functions, e.g. only the type of values is known but not the actual values. + /// + /// is the type of the event, i.e. describes the map entries + public MapEventBean(EventType eventType) + { + _properties = new Dictionary(); + _eventType = eventType; + } + + public EventType EventType + { + get { return _eventType; } + } + + public Object Get(string property) + { + EventPropertyGetter getter = _eventType.GetGetter(property); + if (getter == null) + { + throw new PropertyAccessException( + "Property named '" + property + "' is not a valid property name for this type"); + } + return getter.Get(this); + } + + public object this[string property] + { + get { return Get(property); } + } + + public object Underlying + { + get { return _properties; } + set { _properties = (IDictionary) value; } + } + + public Object GetFragment(string propertyExpression) + { + EventPropertyGetter getter = _eventType.GetGetter(propertyExpression); + if (getter == null) + { + throw PropertyAccessException.NotAValidProperty(propertyExpression); + } + return getter.GetFragment(this); + } + + /// + /// Returns the properties. + /// + /// properties + public IDictionary Properties + { + get { return _properties; } + } + + public override String ToString() + { + return "MapEventBean " + + "eventType=" + _eventType; + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/events/map/MapEventBeanArrayIndexedElementPropertyGetter.cs b/NEsper.Core/NEsper.Core/events/map/MapEventBeanArrayIndexedElementPropertyGetter.cs new file mode 100755 index 000000000..f4697e1bb --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/map/MapEventBeanArrayIndexedElementPropertyGetter.cs @@ -0,0 +1,66 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.events.map +{ + /// + /// Getter for an array of event bean using a nested getter. + /// + public class MapEventBeanArrayIndexedElementPropertyGetter : MapEventPropertyGetter + { + private readonly String _propertyName; + private readonly int _index; + private readonly EventPropertyGetter _nestedGetter; + + /// Ctor. + /// property name + /// array index + /// nested getter + public MapEventBeanArrayIndexedElementPropertyGetter(String propertyName, int index, EventPropertyGetter nestedGetter) + { + _propertyName = propertyName; + _index = index; + _nestedGetter = nestedGetter; + } + + public Object GetMap(IDictionary map) + { + // If the map does not contain the key, this is allowed and represented as null + var wrapper = (EventBean[]) map.Get(_propertyName); + return BaseNestableEventUtil.GetArrayPropertyValue(wrapper, _index, _nestedGetter); + } + + public bool IsMapExistsProperty(IDictionary map) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + + public Object Get(EventBean eventBean) + { + return GetMap(BaseNestableEventUtil.CheckedCastUnderlyingMap(eventBean)); + } + + public bool IsExistsProperty(EventBean eventBean) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + + public Object GetFragment(EventBean obj) + { + var map = BaseNestableEventUtil.CheckedCastUnderlyingMap(obj); + var wrapper = (EventBean[]) map.Get(_propertyName); + return BaseNestableEventUtil.GetArrayPropertyFragment(wrapper, _index, _nestedGetter); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/map/MapEventBeanArrayIndexedPropertyGetter.cs b/NEsper.Core/NEsper.Core/events/map/MapEventBeanArrayIndexedPropertyGetter.cs new file mode 100755 index 000000000..923f72ffe --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/map/MapEventBeanArrayIndexedPropertyGetter.cs @@ -0,0 +1,61 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.events.map +{ + /// Getter for array events. + public class MapEventBeanArrayIndexedPropertyGetter : MapEventPropertyGetter + { + private readonly String _propertyName; + private readonly int _index; + + /// Ctor. + /// property name + /// array index + public MapEventBeanArrayIndexedPropertyGetter(String propertyName, int index) + { + _propertyName = propertyName; + _index = index; + } + + public Object GetMap(IDictionary map) + { + // If the map does not contain the key, this is allowed and represented as null + var wrapper = (EventBean[])map.Get(_propertyName); + return BaseNestableEventUtil.GetArrayPropertyUnderlying(wrapper, _index); + } + + public bool IsMapExistsProperty(IDictionary map) + { + return true; + } + + public Object Get(EventBean eventBean) + { + return GetMap(BaseNestableEventUtil.CheckedCastUnderlyingMap(eventBean)); + } + + public bool IsExistsProperty(EventBean eventBean) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + + public Object GetFragment(EventBean obj) + { + var map = BaseNestableEventUtil.CheckedCastUnderlyingMap(obj); + var wrapper = (EventBean[])map.Get(_propertyName); + return BaseNestableEventUtil.GetArrayPropertyBean(wrapper, _index); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/map/MapEventBeanArrayPropertyGetter.cs b/NEsper.Core/NEsper.Core/events/map/MapEventBeanArrayPropertyGetter.cs new file mode 100755 index 000000000..b6604f355 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/map/MapEventBeanArrayPropertyGetter.cs @@ -0,0 +1,67 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.events.map +{ + using DataMap = IDictionary; + + /// + /// Returns the event bean or the underlying array. + /// + public class MapEventBeanArrayPropertyGetter : MapEventPropertyGetter + { + private readonly String _propertyName; + private readonly Type _underlyingType; + + /// + /// Ctor. + /// + /// property to get + /// type of property + public MapEventBeanArrayPropertyGetter(String propertyName, Type underlyingType) + { + _propertyName = propertyName; + _underlyingType = underlyingType; + } + + public Object GetMap(DataMap asMap) + { + // If the map does not contain the key, this is allowed and represented as null + var mapValue = asMap.Get(_propertyName); + return BaseNestableEventUtil.GetArrayPropertyAsUnderlyingsArray( + _underlyingType, (EventBean[]) mapValue); + } + + public bool IsMapExistsProperty(DataMap map) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + + public Object Get(EventBean eventBean) + { + return GetMap(BaseNestableEventUtil.CheckedCastUnderlyingMap(eventBean)); + } + + public bool IsExistsProperty(EventBean eventBean) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + + public Object GetFragment(EventBean obj) + { + var asMap = BaseNestableEventUtil.CheckedCastUnderlyingMap(obj); + return asMap.Get(_propertyName); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/map/MapEventBeanCopyMethod.cs b/NEsper.Core/NEsper.Core/events/map/MapEventBeanCopyMethod.cs new file mode 100755 index 000000000..1eb170b00 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/map/MapEventBeanCopyMethod.cs @@ -0,0 +1,46 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using com.espertech.esper.client; + +namespace com.espertech.esper.events.map +{ + /// + /// Copy method for Map-underlying events. + /// + public class MapEventBeanCopyMethod : EventBeanCopyMethod + { + private readonly EventAdapterService _eventAdapterService; + private readonly MapEventType _mapEventType; + + /// + /// Ctor. + /// + /// map event type + /// for copying events + public MapEventBeanCopyMethod(MapEventType mapEventType, + EventAdapterService eventAdapterService) + { + _mapEventType = mapEventType; + _eventAdapterService = eventAdapterService; + } + + #region EventBeanCopyMethod Members + + public EventBean Copy(EventBean theEvent) + { + var mapped = (MappedEventBean) theEvent; + IDictionary props = mapped.Properties; + return _eventAdapterService.AdapterForTypedMap(new Dictionary(props), _mapEventType); + } + + #endregion + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/events/map/MapEventBeanCopyMethodWithArrayMap.cs b/NEsper.Core/NEsper.Core/events/map/MapEventBeanCopyMethodWithArrayMap.cs new file mode 100755 index 000000000..3823e647f --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/map/MapEventBeanCopyMethodWithArrayMap.cs @@ -0,0 +1,67 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.events.map +{ + /// + /// Copy method for Map-underlying events. + /// + public class MapEventBeanCopyMethodWithArrayMap : EventBeanCopyMethod + { + private readonly MapEventType _mapEventType; + private readonly EventAdapterService _eventAdapterService; + private readonly ICollection _mapPropertiesToCopy; + private readonly ICollection _arrayPropertiesToCopy; + + /// Ctor. + /// map event type + /// for copying events + /// + /// + public MapEventBeanCopyMethodWithArrayMap(MapEventType mapEventType, EventAdapterService eventAdapterService, ICollection mapPropertiesToCopy, ICollection arrayPropertiesToCopy) { + _mapEventType = mapEventType; + _eventAdapterService = eventAdapterService; + _mapPropertiesToCopy = mapPropertiesToCopy; + _arrayPropertiesToCopy = arrayPropertiesToCopy; + } + + public EventBean Copy(EventBean theEvent) + { + var mapped = (MappedEventBean) theEvent; + var props = mapped.Properties; + var shallowCopy = new Dictionary(props); + + foreach (var name in _mapPropertiesToCopy) { + var innerMap = (IDictionary) props.Get(name); + if (innerMap != null) { + var copy = new Dictionary(innerMap); + shallowCopy.Put(name, copy); + } + } + + foreach (var name in _arrayPropertiesToCopy) + { + var array = props.Get(name) as Array; + if (array != null && (array.Length != 0)) + { + var copied = Array.CreateInstance(array.GetType().GetElementType(), array.Length); + Array.Copy(array, 0, copied, 0, array.Length); + shallowCopy.Put(name, copied); + } + } + + return _eventAdapterService.AdapterForTypedMap(shallowCopy, _mapEventType); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/map/MapEventBeanEntryPropertyGetter.cs b/NEsper.Core/NEsper.Core/events/map/MapEventBeanEntryPropertyGetter.cs new file mode 100755 index 000000000..f76c13a3e --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/map/MapEventBeanEntryPropertyGetter.cs @@ -0,0 +1,81 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.events.map +{ + using DataMap = IDictionary; + + /// + /// A getter that works on EventBean events residing within a Map as an event + /// property. + /// + public class MapEventBeanEntryPropertyGetter + : MapEventPropertyGetter + { + private readonly String _propertyMap; + private readonly EventPropertyGetter _eventBeanEntryGetter; + + /// + /// Ctor. + /// + /// the property to look at + /// the getter for the map entry + public MapEventBeanEntryPropertyGetter(String propertyMap, EventPropertyGetter eventBeanEntryGetter) + { + _propertyMap = propertyMap; + _eventBeanEntryGetter = eventBeanEntryGetter; + } + + public Object GetMap(DataMap asMap) + { + // If the map does not contain the key, this is allowed and represented as null + var value = asMap.Get(_propertyMap); + if (value == null) { + return null; + } + + // Object within the map + return _eventBeanEntryGetter.Get((EventBean) value); + } + + public bool IsMapExistsProperty(DataMap map) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + + public Object Get(EventBean eventBean) + { + return GetMap(BaseNestableEventUtil.CheckedCastUnderlyingMap(eventBean)); + } + + public bool IsExistsProperty(EventBean eventBean) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + + public Object GetFragment(EventBean obj) + { + var asMap = BaseNestableEventUtil.CheckedCastUnderlyingMap(obj); + + // If the map does not contain the key, this is allowed and represented as null + var eventBean = asMap.Get(_propertyMap) as EventBean; + if (eventBean == null) { + return null; + } + + // Object within the map + return _eventBeanEntryGetter.GetFragment(eventBean); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/map/MapEventBeanPropertyGetter.cs b/NEsper.Core/NEsper.Core/events/map/MapEventBeanPropertyGetter.cs new file mode 100755 index 000000000..b83eb831f --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/map/MapEventBeanPropertyGetter.cs @@ -0,0 +1,65 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.events.map +{ + using DataMap = IDictionary; + /// + /// A getter for use with Map-based events simply returns the value for the key. + /// + public class MapEventBeanPropertyGetter + : MapEventPropertyGetter + { + private readonly String _propertyName; + + /// + /// Ctor. + /// + /// property to get + public MapEventBeanPropertyGetter(String propertyName) { + _propertyName = propertyName; + } + + public Object GetMap(DataMap asMap) + { + var theEvent = asMap.Get(_propertyName) as EventBean; + if (theEvent == null) { + return null; + } + + // If the map does not contain the key, this is allowed and represented as null + return theEvent.Underlying; + } + + public bool IsMapExistsProperty(DataMap map) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + + public Object Get(EventBean eventBean) + { + return GetMap(BaseNestableEventUtil.CheckedCastUnderlyingMap(eventBean)); + } + + public bool IsExistsProperty(EventBean eventBean) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + + public Object GetFragment(EventBean obj) + { + return BaseNestableEventUtil.CheckedCastUnderlyingMap(obj).Get(_propertyName); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/map/MapEventBeanPropertyWriter.cs b/NEsper.Core/NEsper.Core/events/map/MapEventBeanPropertyWriter.cs new file mode 100755 index 000000000..94b131dfc --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/map/MapEventBeanPropertyWriter.cs @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.events.map +{ + public class MapEventBeanPropertyWriter : EventPropertyWriter + { + private readonly String _propertyName; + + public MapEventBeanPropertyWriter(String propertyName) { + _propertyName = propertyName; + } + + public string PropertyName + { + get { return _propertyName; } + } + + public virtual void Write(Object value, EventBean target) + { + MappedEventBean map = (MappedEventBean) target; + Write(value, map.Properties); + } + + public virtual void Write(Object value, IDictionary map) { + map.Put(_propertyName, value); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/map/MapEventBeanPropertyWriterIndexedProp.cs b/NEsper.Core/NEsper.Core/events/map/MapEventBeanPropertyWriterIndexedProp.cs new file mode 100755 index 000000000..3b695d88c --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/map/MapEventBeanPropertyWriterIndexedProp.cs @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.events.map +{ + public class MapEventBeanPropertyWriterIndexedProp : MapEventBeanPropertyWriter + { + private readonly int _index; + + public MapEventBeanPropertyWriterIndexedProp(String propertyName, int index) + : base(propertyName) + { + _index = index; + } + + public override void Write(Object value, IDictionary map) + { + var arrayEntry = map.Get(PropertyName) as Array; + if (arrayEntry != null && arrayEntry.Length > _index) { + arrayEntry.SetValue(value, _index); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/map/MapEventBeanPropertyWriterMapProp.cs b/NEsper.Core/NEsper.Core/events/map/MapEventBeanPropertyWriterMapProp.cs new file mode 100755 index 000000000..66b38d949 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/map/MapEventBeanPropertyWriterMapProp.cs @@ -0,0 +1,35 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.events.map +{ + public class MapEventBeanPropertyWriterMapProp : MapEventBeanPropertyWriter + { + private readonly String _key; + + public MapEventBeanPropertyWriterMapProp(String propertyName, String key) + : base(propertyName) + { + _key = key; + } + + public override void Write(Object value, IDictionary map) + { + var mapEntry = (IDictionary)map.Get(PropertyName); + if (mapEntry != null) + { + mapEntry.Put(_key, value); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/map/MapEventBeanReader.cs b/NEsper.Core/NEsper.Core/events/map/MapEventBeanReader.cs new file mode 100755 index 000000000..77c5ea12a --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/map/MapEventBeanReader.cs @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.map +{ + /// + /// Reader method for reading all properties of a Map event. + /// + public class MapEventBeanReader : EventBeanReader + { + private MapEventPropertyGetter[] getterArray; + + /// + /// Ctor. + /// + /// map to read + public MapEventBeanReader(MapEventType type) + { + var properties = type.PropertyNames; + var getters = new List(); + foreach (String property in properties) + { + var getter = type.GetGetter(property) as MapEventPropertyGetter; + if (getter != null) + { + getters.Add(getter); + } + } + getterArray = getters.ToArray(); + } + + public Object[] Read(EventBean theEvent) + { + var underlying = (IDictionary) theEvent.Underlying; + var values = new Object[getterArray.Length]; + for (int i = 0; i < getterArray.Length; i++) + { + values[i] = getterArray[i].GetMap(underlying); + } + return values; + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/map/MapEventBeanWriterPerProp.cs b/NEsper.Core/NEsper.Core/events/map/MapEventBeanWriterPerProp.cs new file mode 100755 index 000000000..9c0cb5b8a --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/map/MapEventBeanWriterPerProp.cs @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.map +{ + /// Writer method for writing to Map-type events. + public class MapEventBeanWriterPerProp : EventBeanWriter + { + private readonly MapEventBeanPropertyWriter[] _writers; + + /// Ctor. + /// names of properties to write + public MapEventBeanWriterPerProp(MapEventBeanPropertyWriter[] writers) + { + _writers = writers; + } + + /// Write values to an event. + /// to write + /// to write to + public void Write(Object[] values, EventBean theEvent) + { + var mappedEvent = (MappedEventBean) theEvent; + var map = mappedEvent.Properties; + + for (int i = 0; i < _writers.Length; i++) + { + _writers[i].Write(values[i], map); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/map/MapEventBeanWriterSimpleProps.cs b/NEsper.Core/NEsper.Core/events/map/MapEventBeanWriterSimpleProps.cs new file mode 100755 index 000000000..2711f7493 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/map/MapEventBeanWriterSimpleProps.cs @@ -0,0 +1,42 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.events.map +{ + /// Writer method for writing to Map-type events. + public class MapEventBeanWriterSimpleProps : EventBeanWriter + { + private readonly String[] _properties; + + /// Ctor. + /// names of properties to write + public MapEventBeanWriterSimpleProps(String[] properties) + { + _properties = properties; + } + + /// Write values to an event. + /// to write + /// to write to + public void Write(Object[] values, EventBean theEvent) + { + var mappedEvent = (MappedEventBean) theEvent; + var map = mappedEvent.Properties; + + for (int i = 0; i < _properties.Length; i++) + { + map.Put(_properties[i], values[i]); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/map/MapEventPropertyGetter.cs b/NEsper.Core/NEsper.Core/events/map/MapEventPropertyGetter.cs new file mode 100755 index 000000000..167b3a575 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/map/MapEventPropertyGetter.cs @@ -0,0 +1,160 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; + +using DataMap = System.Collections.Generic.IDictionary; + +namespace com.espertech.esper.events.map +{ + /// + /// Property getter for Map-underlying events. + /// + public interface MapEventPropertyGetter : EventPropertyGetter + { + /// + /// Returns a property of an event. + /// + /// to interrogate + /// + /// property value + /// + /// PropertyAccessException for property access errors + Object GetMap(DataMap map); + + /// + /// Exists-function for properties in a map-type event. + /// + /// to interrogate + /// + /// indicator + /// + bool IsMapExistsProperty(IDictionary map); + } + + public class ProxyMapEventPropertyGetter : MapEventPropertyGetter + { + public Func ProcGetMap { get; set; } + public Func ProcIsMapExistsProperty { get; set; } + + public Func ProcGet { get; set; } + public Func ProcGetFragment { get; set; } + public Func ProcIsExistsProperty { get; set; } + + /// + /// Initializes a new instance of the class. + /// + public ProxyMapEventPropertyGetter() + { + } + + /// + /// Initializes a new instance of the class. + /// + public ProxyMapEventPropertyGetter(Func procGetMap, + Func procIsMapExistsProperty, + Func procGet, + Func procGetFragment, + Func procIsExistsProperty) + { + ProcGetMap = procGetMap; + ProcIsMapExistsProperty = procIsMapExistsProperty; + ProcGet = procGet; + ProcGetFragment = procGetFragment; + ProcIsExistsProperty = procIsExistsProperty; + } + + /// + /// Returns a property of an event. + /// + /// to interrogate + /// property value + /// PropertyAccessException for property access errors + public Object GetMap(DataMap map) + { + return ProcGetMap.Invoke(map); + } + + /// + /// Exists-function for properties in a map-type event. + /// + /// to interrogate + /// indicator + public bool IsMapExistsProperty(DataMap map) + { + return ProcIsMapExistsProperty.Invoke(map); + } + + /// + /// Return the value for the property in the event object specified when the + /// instance was obtained. Useful for fast access to event properties. Throws a + /// PropertyAccessException if the getter instance doesn't match the EventType it was obtained + /// from, and to indicate other property access problems. + /// + /// is the event to get the value of a property from + /// + /// value of property in event + /// + /// PropertyAccessException to indicate that property access failed + public object Get(EventBean eventBean) + { + return ProcGet.Invoke(eventBean); + } + + /// + /// Returns true if the property exists, or false if the type does not have such a + /// property. + /// + /// Useful for dynamic properties of the syntax "property?" and the dynamic + /// nested/indexed/mapped versions. Dynamic nested properties follow the syntax + /// "property?.nested" which is equivalent to "property?.nested?". If any of the properties in + /// the path of a dynamic nested property return null, the dynamic nested property does + /// not exists and the method returns false. + /// + /// For non-dynamic properties, this method always returns true since a getter + /// would not be available unless + /// + /// is the event to check if the dynamic property exists + /// + /// indictor whether the property exists, always true for non-dynamic (default) + /// properties + /// + public bool IsExistsProperty(EventBean eventBean) + { + return ProcIsExistsProperty.Invoke(eventBean); + } + + /// + /// Returns or array of for + /// a property name or property expression. + /// + /// For use with properties whose value is itself an event or whose value can be + /// represented as an event by the underlying event representation. + /// + /// The of the Instance(s) + /// returned by this method can be determined by . + /// Use to obtain a list of + /// properties that return fragments from an event type. + /// + /// Returns null if the property value is null or the property value cannot be + /// represented as a fragment by the underlying representation. + /// + /// is the event to get the fragment value of a property + /// + /// the value of a property as an EventBean or array of EventBean + /// + /// PropertyAccessException - if there is no property of the specified name, or the property cannot be accessed + public object GetFragment(EventBean eventBean) + { + return ProcGetFragment.Invoke(eventBean); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/map/MapEventPropertyGetterAndIndexed.cs b/NEsper.Core/NEsper.Core/events/map/MapEventPropertyGetterAndIndexed.cs new file mode 100755 index 000000000..09b3a5c94 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/map/MapEventPropertyGetterAndIndexed.cs @@ -0,0 +1,21 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.map +{ + /// + /// Property getter for Map-underlying events. + /// + public interface MapEventPropertyGetterAndIndexed + : MapEventPropertyGetter + , EventPropertyGetterIndexed + { + } +} diff --git a/NEsper.Core/NEsper.Core/events/map/MapEventPropertyGetterAndMapped.cs b/NEsper.Core/NEsper.Core/events/map/MapEventPropertyGetterAndMapped.cs new file mode 100755 index 000000000..a30c7b738 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/map/MapEventPropertyGetterAndMapped.cs @@ -0,0 +1,21 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.map +{ + /// + /// Property getter for Map-underlying events. + /// + public interface MapEventPropertyGetterAndMapped + : MapEventPropertyGetter + , EventPropertyGetterMapped + { + } +} diff --git a/NEsper.Core/NEsper.Core/events/map/MapEventType.cs b/NEsper.Core/NEsper.Core/events/map/MapEventType.cs new file mode 100755 index 000000000..d0835506e --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/map/MapEventType.cs @@ -0,0 +1,210 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.events.property; + +namespace com.espertech.esper.events.map +{ + using Map = IDictionary; + + /// + /// Implementation of the interface for handling plain + /// Maps containing name value pairs. + /// + public class MapEventType : BaseNestableEventType + { + private static readonly EventTypeNestableGetterFactory GETTER_FACTORY = new EventTypeNestableGetterFactoryMap(); + + internal IDictionary> PropertyWriters; + internal EventPropertyDescriptor[] WritablePropertyDescriptors; + + public MapEventType(EventTypeMetadata metadata, + String typeName, + int eventTypeId, + EventAdapterService eventAdapterService, + IDictionary propertyTypes, + EventType[] optionalSuperTypes, + ICollection optionalDeepSupertypes, + ConfigurationEventTypeMap configMapType) + : base(metadata, typeName, eventTypeId, eventAdapterService, propertyTypes, optionalSuperTypes, optionalDeepSupertypes, configMapType, GETTER_FACTORY) + { + } + + protected override void PostUpdateNestableTypes() + { + } + + public override Type UnderlyingType + { + get { return typeof(Map); } + } + + public override EventBeanCopyMethod GetCopyMethod(String[] properties) + { + var pair = BaseNestableEventUtil.GetIndexedAndMappedProps(properties); + + if (pair.MapProperties.IsEmpty() && pair.ArrayProperties.IsEmpty()) + { + return new MapEventBeanCopyMethod(this, EventAdapterService); + } + else + { + return new MapEventBeanCopyMethodWithArrayMap( + this, EventAdapterService, pair.MapProperties, pair.ArrayProperties); + } + } + + public override EventBeanReader Reader + { + get { return new MapEventBeanReader(this); } + } + + public Object GetValue(String propertyName, Map values) + { + var getter = (MapEventPropertyGetter)GetGetter(propertyName); + return getter.GetMap(values); + } + + public override EventPropertyWriter GetWriter(String propertyName) + { + if (WritablePropertyDescriptors == null) + { + InitializeWriters(); + } + var pair = PropertyWriters.Get(propertyName); + if (pair != null) + { + return pair.Second; + } + + var property = PropertyParser.ParseAndWalkLaxToSimple(propertyName); + if (property is MappedProperty) + { + var mapProp = (MappedProperty)property; + return new MapEventBeanPropertyWriterMapProp(mapProp.PropertyNameAtomic, mapProp.Key); + } + + if (property is IndexedProperty) + { + var indexedProp = (IndexedProperty)property; + return new MapEventBeanPropertyWriterIndexedProp(indexedProp.PropertyNameAtomic, indexedProp.Index); + } + + return null; + } + + public override EventPropertyDescriptor GetWritableProperty(String propertyName) + { + if (WritablePropertyDescriptors == null) + { + InitializeWriters(); + } + var pair = PropertyWriters.Get(propertyName); + if (pair != null) + { + return pair.First; + } + + var property = PropertyParser.ParseAndWalkLaxToSimple(propertyName); + if (property is MappedProperty) + { + var writer = GetWriter(propertyName); + if (writer == null) + { + return null; + } + var mapProp = (MappedProperty)property; + return new EventPropertyDescriptor( + mapProp.PropertyNameAtomic, typeof(Object), null, false, true, false, true, false); + } + if (property is IndexedProperty) + { + var writer = GetWriter(propertyName); + if (writer == null) + { + return null; + } + var indexedProp = (IndexedProperty)property; + return new EventPropertyDescriptor( + indexedProp.PropertyNameAtomic, typeof(Object), null, true, false, true, false, false); + } + return null; + } + + public override EventPropertyDescriptor[] WriteableProperties + { + get + { + if (WritablePropertyDescriptors == null) + { + InitializeWriters(); + } + return WritablePropertyDescriptors; + } + } + + public override EventBeanWriter GetWriter(String[] properties) + { + if (WritablePropertyDescriptors == null) + { + InitializeWriters(); + } + + var allSimpleProps = true; + var writers = new MapEventBeanPropertyWriter[properties.Length]; + for (var i = 0; i < properties.Length; i++) + { + var writerPair = PropertyWriters.Get(properties[i]); + if (writerPair != null) + { + writers[i] = writerPair.Second; + } + else + { + writers[i] = (MapEventBeanPropertyWriter)GetWriter(properties[i]); + if (writers[i] == null) + { + return null; + } + allSimpleProps = false; + } + } + + if (allSimpleProps) + { + return new MapEventBeanWriterSimpleProps(properties); + } + else + { + return new MapEventBeanWriterPerProp(writers); + } + } + + private void InitializeWriters() + { + var writeableProps = new List(); + var propertWritersMap = new Dictionary>(); + foreach (EventPropertyDescriptor prop in PropertyDescriptors) + { + writeableProps.Add(prop); + var propertyName = prop.PropertyName; + var eventPropertyWriter = new MapEventBeanPropertyWriter(propertyName); + propertWritersMap.Put(propertyName, new Pair(prop, eventPropertyWriter)); + } + + PropertyWriters = propertWritersMap; + WritablePropertyDescriptors = writeableProps.ToArray(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/map/MapFragmentArrayPropertyGetter.cs b/NEsper.Core/NEsper.Core/events/map/MapFragmentArrayPropertyGetter.cs new file mode 100755 index 000000000..07a9875fd --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/map/MapFragmentArrayPropertyGetter.cs @@ -0,0 +1,67 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.events.map +{ + /// + /// Getter for map array. + /// + public class MapFragmentArrayPropertyGetter : MapEventPropertyGetter + { + private readonly String _propertyName; + private readonly EventType _fragmentEventType; + private readonly EventAdapterService _eventAdapterService; + + /// Ctor. + /// property type + /// event type of fragment + /// for creating event instances + public MapFragmentArrayPropertyGetter(String propertyNameAtomic, EventType fragmentEventType, EventAdapterService eventAdapterService) + { + _propertyName = propertyNameAtomic; + _fragmentEventType = fragmentEventType; + _eventAdapterService = eventAdapterService; + } + + public Object GetMap(IDictionary map) + { + return map.Get(_propertyName); + } + + public bool IsMapExistsProperty(IDictionary map) + { + return true; + } + + public Object Get(EventBean obj) + { + return GetMap(BaseNestableEventUtil.CheckedCastUnderlyingMap(obj)); + } + + public bool IsExistsProperty(EventBean eventBean) + { + return true; + } + + public Object GetFragment(EventBean eventBean) + { + var value = Get(eventBean); + if (value is EventBean[]) + { + return value; + } + return BaseNestableEventUtil.GetFragmentArray(_eventAdapterService, value, _fragmentEventType); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/map/MapIndexedPropertyGetter.cs b/NEsper.Core/NEsper.Core/events/map/MapIndexedPropertyGetter.cs new file mode 100755 index 000000000..15849a9f2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/map/MapIndexedPropertyGetter.cs @@ -0,0 +1,65 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.events.map +{ + using DataMap = IDictionary; + + /// + /// Getter for a dynamic indexed property for maps. + /// + public class MapIndexedPropertyGetter : MapEventPropertyGetter + { + private readonly int _index; + private readonly String _fieldName; + + /// + /// Ctor. + /// + /// property name + /// index to get the element at + public MapIndexedPropertyGetter(String fieldName, int index) + { + _index = index; + _fieldName = fieldName; + } + + public Object GetMap(DataMap map) + { + Object value = map.Get(_fieldName); + return BaseNestableEventUtil.GetIndexedValue(value, _index); + } + + public bool IsMapExistsProperty(DataMap map) + { + Object value = map.Get(_fieldName); + return BaseNestableEventUtil.IsExistsIndexedValue(value, _index); + } + + public Object Get(EventBean eventBean) + { + return GetMap(BaseNestableEventUtil.CheckedCastUnderlyingMap(eventBean)); + } + + public bool IsExistsProperty(EventBean eventBean) + { + return IsMapExistsProperty(BaseNestableEventUtil.CheckedCastUnderlyingMap(eventBean)); + } + + public Object GetFragment(EventBean eventBean) + { + return null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/map/MapMapPropertyGetter.cs b/NEsper.Core/NEsper.Core/events/map/MapMapPropertyGetter.cs new file mode 100755 index 000000000..1cc6cb069 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/map/MapMapPropertyGetter.cs @@ -0,0 +1,76 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.events.map +{ + using Map = IDictionary; + + /// + /// A getter that interrogates a given property in a map which may itself contain nested + /// maps or indexed entries. + /// + public class MapMapPropertyGetter : MapEventPropertyGetter + { + private readonly String _propertyMap; + private readonly MapEventPropertyGetter _getter; + + /// Ctor. + /// is the property returning the map to interrogate + /// is the getter to use to interrogate the property in the map + public MapMapPropertyGetter(String propertyMap, MapEventPropertyGetter getter) + { + if (getter == null) + { + throw new ArgumentException("Getter is a required parameter"); + } + _propertyMap = propertyMap; + _getter = getter; + } + + public Object GetMap(IDictionary map) + { + var valueTopObj = map.Get(_propertyMap) as Map; + if (valueTopObj == null) + { + return null; + } + return _getter.GetMap(valueTopObj); + } + + public bool IsMapExistsProperty(IDictionary map) + { + var valueTopObj = map.Get(_propertyMap) as Map; + if (valueTopObj == null) + { + return false; + } + return _getter.IsMapExistsProperty(valueTopObj); + } + + public Object Get(EventBean eventBean) + { + return GetMap(BaseNestableEventUtil.CheckedCastUnderlyingMap(eventBean)); + } + + public bool IsExistsProperty(EventBean eventBean) + { + return IsMapExistsProperty(BaseNestableEventUtil.CheckedCastUnderlyingMap(eventBean)); + } + + public Object GetFragment(EventBean eventBean) + { + return null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/map/MapMappedPropertyGetter.cs b/NEsper.Core/NEsper.Core/events/map/MapMappedPropertyGetter.cs new file mode 100755 index 000000000..e10469d59 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/map/MapMappedPropertyGetter.cs @@ -0,0 +1,79 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.events.map +{ + using DataMap = System.Collections.Generic.IDictionary; + + /// + /// Getter for a dynamic mappeds property for maps. + /// + public class MapMappedPropertyGetter + : MapEventPropertyGetter + , MapEventPropertyGetterAndMapped + { + private readonly String _key; + private readonly String _fieldName; + + /// + /// Ctor. + /// + /// property name + /// get the element at + public MapMappedPropertyGetter(String fieldName, String key) + { + _key = key; + _fieldName = fieldName; + } + + public Object GetMap(DataMap asMap) + { + return GetMapInternal(asMap, _key); + } + + public bool IsMapExistsProperty(DataMap asMap) + { + var value = asMap.Get(_fieldName); + return BaseNestableEventUtil.GetMappedPropertyExists(value, _key); + } + + public Object Get(EventBean eventBean, String mapKey) + { + DataMap data = BaseNestableEventUtil.CheckedCastUnderlyingMap(eventBean); + return GetMapInternal(data, mapKey); + } + + public Object Get(EventBean eventBean) + { + DataMap data = BaseNestableEventUtil.CheckedCastUnderlyingMap(eventBean); + return GetMap(data); + } + + public bool IsExistsProperty(EventBean eventBean) + { + DataMap data = BaseNestableEventUtil.CheckedCastUnderlyingMap(eventBean); + return IsMapExistsProperty(data); + } + + public Object GetFragment(EventBean eventBean) + { + return null; + } + + private Object GetMapInternal(DataMap map, String providedKey) + { + Object value = map.Get(_fieldName); + return BaseNestableEventUtil.GetMappedPropertyValue(value, providedKey); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/map/MapNestedEntryPropertyGetterArrayMap.cs b/NEsper.Core/NEsper.Core/events/map/MapNestedEntryPropertyGetterArrayMap.cs new file mode 100755 index 000000000..699d409fa --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/map/MapNestedEntryPropertyGetterArrayMap.cs @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.map +{ + /// + /// A getter that works on EventBean events residing within a Map as an event property. + /// + public class MapNestedEntryPropertyGetterArrayMap : MapNestedEntryPropertyGetterBase + { + private readonly int _index; + private readonly MapEventPropertyGetter _getter; + + public MapNestedEntryPropertyGetterArrayMap(String propertyMap, EventType fragmentType, EventAdapterService eventAdapterService, int index, MapEventPropertyGetter getter) + : base(propertyMap, fragmentType, eventAdapterService) + { + _index = index; + _getter = getter; + } + + public override Object HandleNestedValue(Object value) + { + return BaseNestableEventUtil.HandleNestedValueArrayWithMap(value, _index, _getter); + } + + public override Object HandleNestedValueFragment(Object value) + { + return BaseNestableEventUtil.HandleNestedValueArrayWithMapFragment(value, _index, _getter, EventAdapterService, FragmentType); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/map/MapNestedEntryPropertyGetterArrayObjectArray.cs b/NEsper.Core/NEsper.Core/events/map/MapNestedEntryPropertyGetterArrayObjectArray.cs new file mode 100755 index 000000000..168ef31b8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/map/MapNestedEntryPropertyGetterArrayObjectArray.cs @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.events.map +{ + public class MapNestedEntryPropertyGetterArrayObjectArray : MapNestedEntryPropertyGetterBase + { + private readonly int _index; + private readonly ObjectArrayEventPropertyGetter _getter; + + public MapNestedEntryPropertyGetterArrayObjectArray(String propertyMap, EventType fragmentType, EventAdapterService eventAdapterService, int index, ObjectArrayEventPropertyGetter getter) + : base(propertyMap, fragmentType, eventAdapterService) + { + _index = index; + _getter = getter; + } + + public override Object HandleNestedValue(Object value) + { + return BaseNestableEventUtil.HandleNestedValueArrayWithObjectArray(value, _index, _getter); + } + + public override Object HandleNestedValueFragment(Object value) + { + return BaseNestableEventUtil.HandleNestedValueArrayWithObjectArrayFragment(value, _index, _getter, FragmentType, EventAdapterService); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/map/MapNestedEntryPropertyGetterBase.cs b/NEsper.Core/NEsper.Core/events/map/MapNestedEntryPropertyGetterBase.cs new file mode 100755 index 000000000..7e5b1fda9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/map/MapNestedEntryPropertyGetterBase.cs @@ -0,0 +1,73 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.events.map +{ + public abstract class MapNestedEntryPropertyGetterBase : MapEventPropertyGetter + { + protected readonly String PropertyMap; + protected readonly EventType FragmentType; + protected readonly EventAdapterService EventAdapterService; + + /// Ctor. + /// the property to look at + /// factory for event beans and event types + /// type of the entry returned + protected MapNestedEntryPropertyGetterBase(String propertyMap, EventType fragmentType, EventAdapterService eventAdapterService) + { + PropertyMap = propertyMap; + FragmentType = fragmentType; + EventAdapterService = eventAdapterService; + } + + public abstract Object HandleNestedValue(Object value); + public abstract Object HandleNestedValueFragment(Object value); + + public Object GetMap(IDictionary map) + { + Object value = map.Get(PropertyMap); + if (value == null) + { + return null; + } + return HandleNestedValue(value); + } + + public bool IsMapExistsProperty(IDictionary map) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + + public Object Get(EventBean eventBean) + { + return GetMap(BaseNestableEventUtil.CheckedCastUnderlyingMap(eventBean)); + } + + public virtual bool IsExistsProperty(EventBean eventBean) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + + public Object GetFragment(EventBean obj) + { + IDictionary map = BaseNestableEventUtil.CheckedCastUnderlyingMap(obj); + Object value = map.Get(PropertyMap); + if (value == null) + { + return null; + } + return HandleNestedValueFragment(value); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/map/MapNestedEntryPropertyGetterMap.cs b/NEsper.Core/NEsper.Core/events/map/MapNestedEntryPropertyGetterMap.cs new file mode 100755 index 000000000..e2c0a2106 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/map/MapNestedEntryPropertyGetterMap.cs @@ -0,0 +1,56 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.map +{ + using Map = IDictionary; + + /// + /// A getter that works on EventBean events residing within a Map as an event property. + /// + public class MapNestedEntryPropertyGetterMap : MapNestedEntryPropertyGetterBase + { + private readonly MapEventPropertyGetter _mapGetter; + + public MapNestedEntryPropertyGetterMap(String propertyMap, EventType fragmentType, EventAdapterService eventAdapterService, MapEventPropertyGetter mapGetter) + : base(propertyMap, fragmentType, eventAdapterService) + { + _mapGetter = mapGetter; + } + + public override Object HandleNestedValue(Object value) { + if (!(value is Map)) + { + if (value is EventBean) { + return _mapGetter.Get((EventBean) value); + } + return null; + } + return _mapGetter.GetMap((IDictionary) value); + } + + public override Object HandleNestedValueFragment(Object value) { + if (!(value is Map)) + { + if (value is EventBean) { + return _mapGetter.GetFragment((EventBean) value); + } + return null; + } + + // If the map does not contain the key, this is allowed and represented as null + EventBean eventBean = EventAdapterService.AdapterForTypedMap((IDictionary) value, FragmentType); + return _mapGetter.GetFragment(eventBean); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/map/MapNestedEntryPropertyGetterObjectArray.cs b/NEsper.Core/NEsper.Core/events/map/MapNestedEntryPropertyGetterObjectArray.cs new file mode 100755 index 000000000..425ac67bc --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/map/MapNestedEntryPropertyGetterObjectArray.cs @@ -0,0 +1,54 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.events.map +{ + /// + /// A getter that works on EventBean events residing within a Map as an event property. + /// + public class MapNestedEntryPropertyGetterObjectArray : MapNestedEntryPropertyGetterBase { + + private readonly ObjectArrayEventPropertyGetter _arrayGetter; + + public MapNestedEntryPropertyGetterObjectArray(String propertyMap, EventType fragmentType, EventAdapterService eventAdapterService, ObjectArrayEventPropertyGetter arrayGetter) + : base(propertyMap, fragmentType, eventAdapterService) + { + _arrayGetter = arrayGetter; + } + + public override Object HandleNestedValue(Object value) { + if (!(value is Object[])) + { + if (value is EventBean) { + return _arrayGetter.Get((EventBean) value); + } + return null; + } + return _arrayGetter.GetObjectArray((Object[]) value); + } + + public override Object HandleNestedValueFragment(Object value) { + if (!(value is Object[])) + { + if (value is EventBean) { + return _arrayGetter.GetFragment((EventBean) value); + } + return null; + } + + // If the map does not contain the key, this is allowed and represented as null + EventBean eventBean = EventAdapterService.AdapterForTypedObjectArray((Object[]) value, FragmentType); + return _arrayGetter.GetFragment(eventBean); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/map/MapNestedEntryPropertyGetterPropertyProvidedDynamic.cs b/NEsper.Core/NEsper.Core/events/map/MapNestedEntryPropertyGetterPropertyProvidedDynamic.cs new file mode 100755 index 000000000..196ce8f5c --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/map/MapNestedEntryPropertyGetterPropertyProvidedDynamic.cs @@ -0,0 +1,61 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.events.map +{ + public class MapNestedEntryPropertyGetterPropertyProvidedDynamic : MapNestedEntryPropertyGetterBase + { + private readonly EventPropertyGetter _nestedGetter; + + public MapNestedEntryPropertyGetterPropertyProvidedDynamic( + string propertyMap, + EventType fragmentType, + EventAdapterService eventAdapterService, + EventPropertyGetter nestedGetter) + : base(propertyMap, fragmentType, eventAdapterService) + { + _nestedGetter = nestedGetter; + } + + public override Object HandleNestedValue(Object value) + { + if (!(value is IDictionary)) + { + return null; + } + if (_nestedGetter is MapEventPropertyGetter) { + return ((MapEventPropertyGetter) _nestedGetter).GetMap((IDictionary) value); + } + return null; + } + + public override bool IsExistsProperty(EventBean eventBean) + { + var map = BaseNestableEventUtil.CheckedCastUnderlyingMap(eventBean); + var value = map.Get(base.PropertyMap); + if (value == null || !(value is IDictionary)) + { + return false; + } + if (_nestedGetter is MapEventPropertyGetter) { + return ((MapEventPropertyGetter) _nestedGetter).IsMapExistsProperty((IDictionary) value); + } + return false; + } + + public override Object HandleNestedValueFragment(Object value) { + return null; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/events/map/MapNestedPropertyGetterMapOnly.cs b/NEsper.Core/NEsper.Core/events/map/MapNestedPropertyGetterMapOnly.cs new file mode 100755 index 000000000..eef25e448 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/map/MapNestedPropertyGetterMapOnly.cs @@ -0,0 +1,131 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.map +{ + using Map = IDictionary; + + /// + /// Getter for one or more levels deep nested properties of maps. + /// + public class MapNestedPropertyGetterMapOnly : MapEventPropertyGetter + { + private readonly MapEventPropertyGetter[] _mapGetterChain; + + /// Ctor. + /// is the chain of getters to retrieve each nested property + /// is a factory for PONO bean event types + public MapNestedPropertyGetterMapOnly(IList getterChain, EventAdapterService eventAdaperService) + { + _mapGetterChain = new MapEventPropertyGetter[getterChain.Count]; + for (int i = 0; i < getterChain.Count; i++) + { + _mapGetterChain[i] = (MapEventPropertyGetter) getterChain[i]; + } + } + + public Object GetMap(IDictionary map) + { + Object result = _mapGetterChain[0].GetMap(map); + return HandleGetterTrailingChain(result); + } + + public bool IsMapExistsProperty(IDictionary map) + { + if (!_mapGetterChain[0].IsMapExistsProperty(map)) { + return false; + } + Object result = _mapGetterChain[0].GetMap(map); + return HandleIsExistsTrailingChain(result); + } + + public Object Get(EventBean eventBean) + { + Object result = _mapGetterChain[0].Get(eventBean); + return HandleGetterTrailingChain(result); + } + + public bool IsExistsProperty(EventBean eventBean) + { + if (!_mapGetterChain[0].IsExistsProperty(eventBean)) { + return false; + } + Object result = _mapGetterChain[0].Get(eventBean); + return HandleIsExistsTrailingChain(result); + } + + public Object GetFragment(EventBean eventBean) + { + return null; + } + + private bool HandleIsExistsTrailingChain(Object result) { + for (int i = 1; i < _mapGetterChain.Length; i++) + { + if (result == null) { + return false; + } + + MapEventPropertyGetter getter = _mapGetterChain[i]; + + if (i == _mapGetterChain.Length - 1) { + if (!(result is Map)) { + if (result is EventBean) { + return getter.IsExistsProperty((EventBean) result); + } + return false; + } + else { + return getter.IsMapExistsProperty((IDictionary) result); + } + } + + if (!(result is Map)) { + if (result is EventBean) { + result = getter.Get((EventBean) result); + } + else { + return false; + } + } + else { + result = getter.GetMap((IDictionary) result); + } + } + return true; + } + + private Object HandleGetterTrailingChain(Object result) { + for (int i = 1; i < _mapGetterChain.Length; i++) + { + if (result == null) { + return null; + } + + MapEventPropertyGetter getter = _mapGetterChain[i]; + if (!(result is Map)) { + if (result is EventBean) { + result = getter.Get((EventBean) result); + } + else { + return null; + } + } + else { + result = getter.GetMap((IDictionary) result); + } + } + return result; + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/map/MapNestedPropertyGetterMixedType.cs b/NEsper.Core/NEsper.Core/events/map/MapNestedPropertyGetterMixedType.cs new file mode 100755 index 000000000..292f0a17d --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/map/MapNestedPropertyGetterMixedType.cs @@ -0,0 +1,135 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; +using com.espertech.esper.client; +using com.espertech.esper.events.bean; + +namespace com.espertech.esper.events.map +{ + using Map = IDictionary; + + /// + /// Getter for one or more levels deep nested properties of maps. + /// + public class MapNestedPropertyGetterMixedType : MapEventPropertyGetter + { + private readonly EventPropertyGetter[] _getterChain; + + /// Ctor. + /// is the chain of getters to retrieve each nested property + /// is a factory for PONO bean event types + public MapNestedPropertyGetterMixedType(IEnumerable getterChain, + EventAdapterService eventAdaperService) + { + _getterChain = getterChain.ToArray(); + } + + public Object GetMap(IDictionary map) + { + Object result = ((MapEventPropertyGetter) _getterChain[0]).GetMap(map); + return HandleGetterTrailingChain(result); + } + + public bool IsMapExistsProperty(IDictionary map) + { + if (!((MapEventPropertyGetter) _getterChain[0]).IsMapExistsProperty(map)) { + return false; + } + Object result = ((MapEventPropertyGetter) _getterChain[0]).GetMap(map); + return HandleIsExistsTrailingChain(result); + } + + public Object Get(EventBean eventBean) + { + Object result = _getterChain[0].Get(eventBean); + return HandleGetterTrailingChain(result); + } + + public bool IsExistsProperty(EventBean eventBean) + { + if (!_getterChain[0].IsExistsProperty(eventBean)) { + return false; + } + Object result = _getterChain[0].Get(eventBean); + return HandleIsExistsTrailingChain(result); + } + + private bool HandleIsExistsTrailingChain(Object result) { + for (int i = 1; i < _getterChain.Length; i++) + { + if (result == null) { + return false; + } + + EventPropertyGetter getter = _getterChain[i]; + + if (i == _getterChain.Length - 1) { + if (getter is BeanEventPropertyGetter) { + return ((BeanEventPropertyGetter) getter).IsBeanExistsProperty(result); + } + else if (result is Map && getter is MapEventPropertyGetter) { + return ((MapEventPropertyGetter) getter).IsMapExistsProperty((Map)result); + } + else if (result is EventBean) { + return getter.IsExistsProperty((EventBean) result); + } + else { + return false; + } + } + + if (getter is BeanEventPropertyGetter) { + result = ((BeanEventPropertyGetter) getter).GetBeanProp(result); + } + else if (result is Map && getter is MapEventPropertyGetter) { + result = ((MapEventPropertyGetter) getter).GetMap((Map)result); + } + else if (result is EventBean) { + result = getter.Get((EventBean) result); + } + else { + return false; + } + } + return false; + } + + public Object GetFragment(EventBean eventBean) + { + return null; + } + + + private Object HandleGetterTrailingChain(Object result) { + + for (int i = 1; i < _getterChain.Length; i++) + { + if (result == null) { + return null; + } + EventPropertyGetter getter = _getterChain[i]; + if (result is EventBean) { + result = getter.Get((EventBean) result); + } + else if (getter is BeanEventPropertyGetter) { + result = ((BeanEventPropertyGetter) getter).GetBeanProp(result); + } + else if (result is Map && getter is MapEventPropertyGetter) { + result = ((MapEventPropertyGetter) getter).GetMap((Map)result); + } + else { + return null; + } + } + return result; + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/map/MapObjectEntryPropertyGetter.cs b/NEsper.Core/NEsper.Core/events/map/MapObjectEntryPropertyGetter.cs new file mode 100755 index 000000000..f486ec470 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/map/MapObjectEntryPropertyGetter.cs @@ -0,0 +1,81 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.events.bean; + +namespace com.espertech.esper.events.map +{ + using DataMap = IDictionary; + + /// + /// A getter that works on PONO events residing within a Map as an event property. + /// + public class MapObjectEntryPropertyGetter : BaseNativePropertyGetter, MapEventPropertyGetter + { + private readonly String _propertyMap; + private readonly BeanEventPropertyGetter _mapEntryGetter; + + /// + /// Ctor. + /// + /// the property to look at + /// the getter for the map entry + /// for producing wrappers to objects + /// type of the entry returned + /// Type of the nested component. + public MapObjectEntryPropertyGetter( + String propertyMap, + BeanEventPropertyGetter mapEntryGetter, + EventAdapterService eventAdapterService, + Type returnType, + Type nestedComponentType) + : base(eventAdapterService, returnType, nestedComponentType) + { + _propertyMap = propertyMap; + _mapEntryGetter = mapEntryGetter; + } + + public object GetMap(DataMap map) + { + // If the map does not contain the key, this is allowed and represented as null + var value = map.Get(_propertyMap); + if (value == null) + { + return null; + } + + if (value is EventBean) + { + return _mapEntryGetter.Get((EventBean) value); + } + + // Object within the map + return _mapEntryGetter.GetBeanProp(value); + } + + public bool IsMapExistsProperty(DataMap map) + { + return true; + } + + public override Object Get(EventBean eventBean) + { + return GetMap(BaseNestableEventUtil.CheckedCastUnderlyingMap(eventBean)); + } + + public override bool IsExistsProperty(EventBean eventBean) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/map/MapPropertyGetterDefaultBase.cs b/NEsper.Core/NEsper.Core/events/map/MapPropertyGetterDefaultBase.cs new file mode 100755 index 000000000..f024add24 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/map/MapPropertyGetterDefaultBase.cs @@ -0,0 +1,65 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.events.map +{ + /// + /// Getter for map entry. + /// + public abstract class MapPropertyGetterDefaultBase : MapEventPropertyGetter + { + private readonly String _propertyName; + protected readonly EventType FragmentEventType; + protected readonly EventAdapterService EventAdapterService; + + /// Ctor. + /// property name + /// fragment type + /// factory for event beans and event types + protected MapPropertyGetterDefaultBase(String propertyNameAtomic, EventType fragmentEventType, EventAdapterService eventAdapterService) + { + _propertyName = propertyNameAtomic; + FragmentEventType = fragmentEventType; + EventAdapterService = eventAdapterService; + } + + protected abstract Object HandleCreateFragment(Object value); + + public Object GetMap(IDictionary map) + { + return map.Get(_propertyName); + } + + public bool IsMapExistsProperty(IDictionary map) + { + return true; + } + + public Object Get(EventBean eventBean) + { + return GetMap(BaseNestableEventUtil.CheckedCastUnderlyingMap(eventBean)); + } + + public bool IsExistsProperty(EventBean eventBean) + { + return true; + } + + public Object GetFragment(EventBean eventBean) + { + Object value = Get(eventBean); + return HandleCreateFragment(value); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/map/MapPropertyGetterDefaultMap.cs b/NEsper.Core/NEsper.Core/events/map/MapPropertyGetterDefaultMap.cs new file mode 100755 index 000000000..be65133f7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/map/MapPropertyGetterDefaultMap.cs @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.map +{ + /// Getter for map entry. + public class MapPropertyGetterDefaultMap : MapPropertyGetterDefaultBase + { + public MapPropertyGetterDefaultMap(String propertyName, EventType fragmentEventType, EventAdapterService eventAdapterService) + : base(propertyName, fragmentEventType, eventAdapterService) + { + } + + protected override Object HandleCreateFragment(Object value) + { + return BaseNestableEventUtil.HandleCreateFragmentMap(value, FragmentEventType, EventAdapterService); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/map/MapPropertyGetterDefaultNoFragment.cs b/NEsper.Core/NEsper.Core/events/map/MapPropertyGetterDefaultNoFragment.cs new file mode 100755 index 000000000..6768d2009 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/map/MapPropertyGetterDefaultNoFragment.cs @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.events.map +{ + /// Getter for map entry. + public class MapPropertyGetterDefaultNoFragment : MapPropertyGetterDefaultBase + { + public MapPropertyGetterDefaultNoFragment(String propertyName, EventAdapterService eventAdapterService) + : base(propertyName, null, eventAdapterService) + { + } + + protected override Object HandleCreateFragment(Object value) + { + return null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/map/MapPropertyGetterDefaultObjectArray.cs b/NEsper.Core/NEsper.Core/events/map/MapPropertyGetterDefaultObjectArray.cs new file mode 100755 index 000000000..78f9dec54 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/map/MapPropertyGetterDefaultObjectArray.cs @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.map +{ + /// Getter for map entry. + public class MapPropertyGetterDefaultObjectArray : MapPropertyGetterDefaultBase + { + public MapPropertyGetterDefaultObjectArray(String propertyName, EventType fragmentEventType, EventAdapterService eventAdapterService) + : base(propertyName, fragmentEventType, eventAdapterService) + { + } + + protected override Object HandleCreateFragment(Object value) + { + return BaseNestableEventUtil.HandleCreateFragmentObjectArray(value, FragmentEventType, EventAdapterService); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/map/SendableEventMap.cs b/NEsper.Core/NEsper.Core/events/map/SendableEventMap.cs new file mode 100755 index 000000000..96cbd1559 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/map/SendableEventMap.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.map +{ + public class SendableEventMap : SendableEvent + { + private readonly IDictionary _event; + private readonly String _typeName; + + public SendableEventMap(IDictionary theEvent, String typeName) + { + _event = theEvent; + _typeName = typeName; + } + + public void Send(EPRuntime runtime) + { + runtime.SendEvent(_event, _typeName); + } + + public object Underlying + { + get { return _event; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/property/DynamicIndexedProperty.cs b/NEsper.Core/NEsper.Core/events/property/DynamicIndexedProperty.cs new file mode 100755 index 000000000..7dc69dff7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/property/DynamicIndexedProperty.cs @@ -0,0 +1,139 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.events.arr; +using com.espertech.esper.events.bean; +using com.espertech.esper.events.map; +using com.espertech.esper.events.xml; + +namespace com.espertech.esper.events.property +{ + /// + /// Represents a dynamic indexed property of a given name. + /// + /// Dynamic properties always exist, have an Object type and are resolved to a method during runtime. + /// + /// + public class DynamicIndexedProperty + : PropertyBase + , DynamicProperty + , PropertyWithIndex + { + private readonly int _index; + + /// + /// Ctor. + /// + /// is the property name + /// is the index of the array or indexed property + public DynamicIndexedProperty(string propertyName, int index) + : base(propertyName) + { + _index = index; + } + + public override bool IsDynamic + { + get { return true; } + } + + public override string[] ToPropertyArray() + { + return new string[] + { + this.PropertyNameAtomic + }; + } + + public override EventPropertyGetter GetGetter(BeanEventType eventType, EventAdapterService eventAdapterService) + { + return new DynamicIndexedPropertyGetter(PropertyNameAtomic, _index, eventAdapterService); + } + + public override Type GetPropertyType(BeanEventType eventType, EventAdapterService eventAdapterService) + { + return typeof (Object); + } + + public override GenericPropertyDesc GetPropertyTypeGeneric( + BeanEventType beanEventType, + EventAdapterService eventAdapterService) + { + return GenericPropertyDesc.ObjectGeneric; + } + + public override Type GetPropertyTypeMap( + IDictionary optionalMapPropTypes, + EventAdapterService eventAdapterService) + { + return typeof (Object); + } + + public override MapEventPropertyGetter GetGetterMap( + IDictionary optionalMapPropTypes, + EventAdapterService eventAdapterService) + { + return new MapIndexedPropertyGetter(this.PropertyNameAtomic, _index); + } + + public override ObjectArrayEventPropertyGetter GetGetterObjectArray( + IDictionary indexPerProperty, + IDictionary nestableTypes, + EventAdapterService eventAdapterService) + { + int propertyIndex; + if (indexPerProperty.TryGetValue(PropertyNameAtomic, out propertyIndex)) + { + return new ObjectArrayIndexedPropertyGetter(propertyIndex, _index); + } + + return null; + } + + public override void ToPropertyEPL(TextWriter writer) + { + writer.Write(PropertyNameAtomic); + writer.Write('['); + writer.Write(_index); + writer.Write(']'); + writer.Write('?'); + } + + public override EventPropertyGetter GetGetterDOM( + SchemaElementComplex complexProperty, + EventAdapterService eventAdapterService, + BaseXMLEventType eventType, + string propertyExpression) + { + return new DOMIndexedGetter(PropertyNameAtomic, _index, null); + } + + public override SchemaItem GetPropertyTypeSchema( + SchemaElementComplex complexProperty, + EventAdapterService eventAdapterService) + { + return null; // dynamic properties always return Node + } + + public override EventPropertyGetter GetGetterDOM() + { + return new DOMIndexedGetter(PropertyNameAtomic, _index, null); + } + + public int Index + { + get { return _index; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/events/property/DynamicMappedProperty.cs b/NEsper.Core/NEsper.Core/events/property/DynamicMappedProperty.cs new file mode 100755 index 000000000..8d34141db --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/property/DynamicMappedProperty.cs @@ -0,0 +1,139 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.client; +using com.espertech.esper.events.arr; +using com.espertech.esper.events.bean; +using com.espertech.esper.events.map; +using com.espertech.esper.events.xml; + +namespace com.espertech.esper.events.property +{ + /// + /// Represents a dynamic mapped property of a given name. + /// + /// Dynamic properties always exist, have an Object type and are resolved to a method during runtime. + /// + /// + public class DynamicMappedProperty + : PropertyBase, + DynamicProperty, + PropertyWithKey + { + private readonly string _key; + + /// + /// Ctor. + /// + /// is the property name + /// is the mapped access key + public DynamicMappedProperty(string propertyName, string key) + : base(propertyName) + { + _key = key; + } + + public override bool IsDynamic + { + get { return true; } + } + + public override string[] ToPropertyArray() + { + return new string[] + { + PropertyNameAtomic + }; + } + + public override EventPropertyGetter GetGetter(BeanEventType eventType, EventAdapterService eventAdapterService) + { + return new DynamicMappedPropertyGetter(PropertyNameAtomic, _key, eventAdapterService); + } + + public override Type GetPropertyType(BeanEventType eventType, EventAdapterService eventAdapterService) + { + return typeof (Object); + } + + public override GenericPropertyDesc GetPropertyTypeGeneric( + BeanEventType beanEventType, + EventAdapterService eventAdapterService) + { + return GenericPropertyDesc.ObjectGeneric; + } + + public override Type GetPropertyTypeMap( + IDictionary optionalMapPropTypes, + EventAdapterService eventAdapterService) + { + return typeof (Object); + } + + public override MapEventPropertyGetter GetGetterMap( + IDictionary optionalMapPropTypes, + EventAdapterService eventAdapterService) + { + return new MapMappedPropertyGetter(PropertyNameAtomic, _key); + } + + public override void ToPropertyEPL(TextWriter writer) + { + writer.Write(PropertyNameAtomic); + writer.Write("('"); + writer.Write(_key); + writer.Write("')"); + writer.Write('?'); + } + + public override EventPropertyGetter GetGetterDOM( + SchemaElementComplex complexProperty, + EventAdapterService eventAdapterService, + BaseXMLEventType eventType, + string propertyExpression) + { + return new DOMMapGetter(PropertyNameAtomic, _key, null); + } + + public override SchemaItem GetPropertyTypeSchema( + SchemaElementComplex complexProperty, + EventAdapterService eventAdapterService) + { + return null; // always returns Node + } + + public override EventPropertyGetter GetGetterDOM() + { + return new DOMMapGetter(PropertyNameAtomic, _key, null); + } + + public override ObjectArrayEventPropertyGetter GetGetterObjectArray( + IDictionary indexPerProperty, + IDictionary nestableTypes, + EventAdapterService eventAdapterService) + { + int propertyIndex; + + if (indexPerProperty.TryGetValue(PropertyNameAtomic, out propertyIndex)) + { + return new ObjectArrayMappedPropertyGetter(propertyIndex, _key); + } + + return null; + } + + public string Key + { + get { return _key; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/events/property/DynamicProperty.cs b/NEsper.Core/NEsper.Core/events/property/DynamicProperty.cs new file mode 100755 index 000000000..9c47ca09e --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/property/DynamicProperty.cs @@ -0,0 +1,15 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.events.property +{ + /// Marker interface for dynamic properties. + public interface DynamicProperty + { + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/events/property/DynamicSimpleProperty.cs b/NEsper.Core/NEsper.Core/events/property/DynamicSimpleProperty.cs new file mode 100755 index 000000000..6f986d347 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/property/DynamicSimpleProperty.cs @@ -0,0 +1,100 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.client; +using com.espertech.esper.events.arr; +using com.espertech.esper.events.bean; +using com.espertech.esper.events.map; +using com.espertech.esper.events.xml; + +namespace com.espertech.esper.events.property +{ + /// + /// Represents a dynamic simple property of a given name. + /// + /// Dynamic properties always exist, have an Object type and are resolved to a method during runtime. + /// + /// + public class DynamicSimpleProperty : PropertyBase, DynamicProperty, PropertySimple { + /// L + /// Ctor. + /// + /// is the property name + public DynamicSimpleProperty(string propertyName) + : base(propertyName) + { + } + + public override EventPropertyGetter GetGetter(BeanEventType eventType, EventAdapterService eventAdapterService) { + return new DynamicSimplePropertyGetter(PropertyNameAtomic, eventAdapterService); + } + + public override bool IsDynamic + { + get { return true; } + } + + public override string[] ToPropertyArray() + { + return new string[]{this.PropertyNameAtomic}; + } + + public override Type GetPropertyType(BeanEventType eventType, EventAdapterService eventAdapterService) { + return typeof(Object); + } + + public override GenericPropertyDesc GetPropertyTypeGeneric(BeanEventType beanEventType, EventAdapterService eventAdapterService) { + return GenericPropertyDesc.ObjectGeneric; + } + + public override Type GetPropertyTypeMap(IDictionary optionalMapPropTypes, EventAdapterService eventAdapterService) + { + return typeof(Object); + } + + public override MapEventPropertyGetter GetGetterMap(IDictionary optionalMapPropTypes, EventAdapterService eventAdapterService) + { + return new MapDynamicPropertyGetter(PropertyNameAtomic); + } + + public override void ToPropertyEPL(TextWriter writer) { + writer.Write(PropertyNameAtomic); + } + + public override EventPropertyGetter GetGetterDOM(SchemaElementComplex complexProperty, EventAdapterService eventAdapterService, BaseXMLEventType eventType, string propertyExpression) { + return new DOMAttributeAndElementGetter(PropertyNameAtomic); + } + + public override EventPropertyGetter GetGetterDOM() { + return new DOMAttributeAndElementGetter(PropertyNameAtomic); + } + + public override SchemaItem GetPropertyTypeSchema(SchemaElementComplex complexProperty, EventAdapterService eventAdapterService) { + return null; // always returns Node + } + + public override ObjectArrayEventPropertyGetter GetGetterObjectArray(IDictionary indexPerProperty, IDictionary nestableTypes, EventAdapterService eventAdapterService) { + // The simple, none-dynamic property needs a definition of the map contents else no property + if (nestableTypes == null) { + return new ObjectArrayDynamicPropertyGetter(PropertyNameAtomic); + } + + int propertyIndex; + if (indexPerProperty.TryGetValue(PropertyNameAtomic, out propertyIndex)) + { + return new ObjectArrayPropertyGetterDefaultObjectArray(propertyIndex, null, eventAdapterService); + } + + return new ObjectArrayDynamicPropertyGetter(PropertyNameAtomic); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/events/property/FastAccessorPropertyDescriptor.cs b/NEsper.Core/NEsper.Core/events/property/FastAccessorPropertyDescriptor.cs new file mode 100755 index 000000000..341c070a5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/property/FastAccessorPropertyDescriptor.cs @@ -0,0 +1,175 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.ComponentModel; +using System.Reflection; + +using XLR8.CGLib; + +namespace com.espertech.esper.events.property +{ + /// + /// Provides a simple property descriptor that is obtained through a + /// method. The method should be a read method that has no parameters + /// and returns an object. + /// + + public class FastAccessorPropertyDescriptor : PropertyDescriptor + { + private readonly FastMethod accessorMethod ; + + /// + /// Indicates whether the value of this property should be + /// persisted. + /// + /// + /// + + public override bool ShouldSerializeValue(object component) + { + return false ; + } + + /// + /// Indicates whether or not the descriptor is readonly + /// + + public override bool IsReadOnly + { + get { return true ; } + } + + /// + /// Gets the type of component this property is bound to + /// + + public override Type ComponentType + { + get { return accessorMethod.Target.DeclaringType ; } + } + + /// + /// Gets the return type of the property + /// + + public override Type PropertyType + { + get { return accessorMethod.ReturnType ; } + } + + /// + /// Call the accessor method + /// + /// + /// + + public override Object GetValue(object component) + { + try + { + return accessorMethod.Invoke(component, null); + } + catch (TargetException) + { + throw new ArgumentException("component"); + } + } + + /// + /// Sets the value of the property + /// + /// + /// + + public override void SetValue(object component, object value) + { + throw new NotSupportedException() ; + } + + /// + /// Can not override values with the simple accessor model + /// + /// + /// + + public override bool CanResetValue(object component) + { + return false ; + } + + /// + /// Resets the value of the property + /// + /// + + public override void ResetValue(object component) + { + throw new NotSupportedException() ; + } + + /// + /// Returns true if the objects are equal. + /// + /// + /// + + public override bool Equals(object obj) + { + FastAccessorPropertyDescriptor temp = obj as FastAccessorPropertyDescriptor; + if (temp != null) + { + return + //Object.Equals(this.Name, temp.Name) && + Object.Equals(this.accessorMethod, temp.accessorMethod); + } + + return false; + } + + /// + /// Returns a hahscode for the object. + /// + /// + + public override int GetHashCode() + { + return Name.GetHashCode(); + } + + /// + /// Constructor + /// + + public FastAccessorPropertyDescriptor(String name, FastMethod accessorMethod) + : base( name, null ) + { + this.accessorMethod = accessorMethod; + } + + /// + /// Constructor + /// + + public FastAccessorPropertyDescriptor(String name, MethodInfo accessorMethod) + : this(name, FastClass.CreateMethod(accessorMethod)) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The accessor method. + public FastAccessorPropertyDescriptor(FastMethod accessorMethod) + : base(accessorMethod.Target.Name, null) + { + this.accessorMethod = accessorMethod; + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/property/FastFieldPropertyDescriptor.cs b/NEsper.Core/NEsper.Core/events/property/FastFieldPropertyDescriptor.cs new file mode 100755 index 000000000..b94c6daad --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/property/FastFieldPropertyDescriptor.cs @@ -0,0 +1,157 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.ComponentModel; + +using XLR8.CGLib; + +namespace com.espertech.esper.events.property +{ + /// + /// Provides a property descriptor that is obtained through a + /// field. + /// + + public class FastFieldPropertyDescriptor : PropertyDescriptor + { + private readonly FastField fieldInfo ; + + /// + /// Indicates whether the value of this property should be + /// persisted. + /// + /// + /// + + public override bool ShouldSerializeValue(object component) + { + return false ; + } + + /// + /// Indicates whether or not the descriptor is readonly + /// + + public override bool IsReadOnly + { + get { return true ; } + } + + /// + /// Gets the type of component this property is bound to + /// + + public override Type ComponentType + { + get { return fieldInfo.Target.DeclaringType ; } + } + + /// + /// Gets the return type of the property + /// + + public override Type PropertyType + { + get { return fieldInfo.FieldType ; } + } + + /// + /// Call the accessor method + /// + /// + /// + + public override Object GetValue(object component) + { + return fieldInfo.Get(component); + } + + /// + /// Sets the value of the property + /// + /// + /// + + public override void SetValue(object component, object value) + { + throw new NotSupportedException() ; + } + + /// + /// Can not override values with the simple accessor model + /// + /// + /// + + public override bool CanResetValue(object component) + { + return false ; + } + + /// + /// Resets the value of the property + /// + /// + + public override void ResetValue(object component) + { + throw new NotSupportedException() ; + } + + /// + /// Returns true if the objects are equal. + /// + /// + /// + + public override bool Equals(object obj) + { + FastFieldPropertyDescriptor temp = obj as FastFieldPropertyDescriptor; + if (temp != null) + { + return + Object.Equals( this.Name, temp.Name ) && + Object.Equals( this.fieldInfo, temp.fieldInfo ) ; + } + + return false; + } + + /// + /// Returns a hahscode for the object. + /// + /// + + public override int GetHashCode() + { + return Name.GetHashCode(); + } + + /// + /// Constructor + /// + + public FastFieldPropertyDescriptor( String name, FastField fieldInfo ) : + base( name, null ) + { + this.fieldInfo = fieldInfo; + } + + /// + /// Constructor + /// + + public FastFieldPropertyDescriptor(FastField fieldInfo) + : base(fieldInfo.Target.Name, null) + { + this.fieldInfo = fieldInfo; + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/property/FastPropertyDescriptor.cs b/NEsper.Core/NEsper.Core/events/property/FastPropertyDescriptor.cs new file mode 100755 index 000000000..1c933dfcb --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/property/FastPropertyDescriptor.cs @@ -0,0 +1,155 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.ComponentModel; +using System.Reflection; + +namespace com.espertech.esper.events.property +{ + /// + /// Provides a property descriptor that is obtained through a + /// property. + /// + + public class FastPropertyDescriptor : PropertyDescriptor + { + private readonly PropertyInfo propInfo; + + /// + /// Indicates whether the value of this property should be + /// persisted. + /// + /// + /// + + public override bool ShouldSerializeValue(object component) + { + return false ; + } + + /// + /// Indicates whether or not the descriptor is readonly + /// + + public override bool IsReadOnly + { + get { return true ; } + } + + /// + /// Gets the type of component this property is bound to + /// + + public override Type ComponentType + { + get { return propInfo.Target.DeclaringType; } + } + + /// + /// Gets the return type of the property + /// + + public override Type PropertyType + { + get { return propInfo.PropertyType; } + } + + /// + /// Call the accessor method + /// + /// + /// + + public override Object GetValue(object component) + { + return propInfo.Get(component); + } + + /// + /// Sets the value of the property + /// + /// + /// + + public override void SetValue(object component, object value) + { + throw new NotSupportedException() ; + } + + /// + /// Can not override values with the simple accessor model + /// + /// + /// + + public override bool CanResetValue(object component) + { + return false ; + } + + /// + /// Resets the value of the property + /// + /// + + public override void ResetValue(object component) + { + throw new NotSupportedException() ; + } + + /// + /// Returns true if the objects are equal. + /// + /// + /// + + public override bool Equals(object obj) + { + FastPropertyDescriptor temp = obj as FastPropertyDescriptor; + if (temp != null) + { + return + Object.Equals( this.Name, temp.Name ) && + Object.Equals( this.propInfo, temp.propInfo ); + } + + return false; + } + + /// + /// Returns a hahscode for the object. + /// + /// + + public override int GetHashCode() + { + return Name.GetHashCode(); + } + + /// + /// Constructor + /// + + public FastPropertyDescriptor(String name, PropertyInfo propInfo) + : base( name, null ) + { + this.propInfo = propInfo; + } + + /// + /// Constructor + /// + + public FastPropertyDescriptor(PropertyInfo propInfo) + : base(propInfo.Target.Name, null) + { + this.propInfo = propInfo; + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/property/GenericPropertyDesc.cs b/NEsper.Core/NEsper.Core/events/property/GenericPropertyDesc.cs new file mode 100755 index 000000000..6da3570fd --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/property/GenericPropertyDesc.cs @@ -0,0 +1,62 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.events.property +{ + /// + /// Descriptor for a type and its generic type, if any. + /// + public class GenericPropertyDesc + { + private static readonly GenericPropertyDesc OBJECT_GENERIC = new GenericPropertyDesc(typeof (Object)); + + /// + /// Ctor. + /// + /// the type + /// its generic type parameter, if any + public GenericPropertyDesc(Type type, Type generic) + { + GenericType = type; + Generic = generic; + } + + /// + /// Ctor. + /// + /// the type + public GenericPropertyDesc(Type type) + { + GenericType = type; + Generic = null; + } + + /// + /// typeof(Object) type. + /// + /// type descriptor + public static GenericPropertyDesc ObjectGeneric + { + get { return OBJECT_GENERIC; } + } + + /// + /// Returns the type. + /// + /// type + public Type GenericType { get; private set; } + + /// + /// Returns the generic parameter, or null if none. + /// + /// generic parameter + public Type Generic { get; private set; } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/events/property/IndexedProperty.cs b/NEsper.Core/NEsper.Core/events/property/IndexedProperty.cs new file mode 100755 index 000000000..29e6cd1a5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/property/IndexedProperty.cs @@ -0,0 +1,509 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Xml.Schema; + +using XLR8.CGLib; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.events.arr; +using com.espertech.esper.events.bean; +using com.espertech.esper.events.map; +using com.espertech.esper.events.xml; +using com.espertech.esper.util; + +namespace com.espertech.esper.events.property +{ + using DataMap = IDictionary; + + /// + /// Represents an indexed property or array property, ie. an 'value' property with read method getValue(int index) + /// or a 'array' property via read method Array returning an array. + /// + public class IndexedProperty : PropertyBase, PropertyWithIndex + { + private readonly int _index; + + /// + /// Initializes a new instance of the class. + /// + /// is the name of the property + public IndexedProperty(String propertyName) + : base(propertyName) + { + } + + /// Ctor. + /// is the property name + /// + /// is the index to use to access the property value + /// + public IndexedProperty(String propertyName, int index) + : base(propertyName) + { + _index = index; + } + + /// Returns index for indexed access. + /// index value + /// + public int Index + { + get { return _index; } + } + + public override bool IsDynamic + { + get { return false; } + } + + public override String[] ToPropertyArray() + { + return new[] { PropertyNameAtomic }; + } + + public override EventPropertyGetter GetGetter(BeanEventType eventType, EventAdapterService eventAdapterService) + { + FastClass fastClass = eventType.FastClass; + InternalEventPropDescriptor propertyDesc = eventType.GetIndexedProperty(PropertyNameAtomic); + if (propertyDesc != null) + { + if (fastClass != null) + { + MethodInfo method = propertyDesc.ReadMethod; + FastMethod fastMethod = fastClass.GetMethod(method); + return new KeyedFastPropertyGetter(fastMethod, _index, eventAdapterService); + } + else + { + return new KeyedMethodPropertyGetter(propertyDesc.ReadMethod, _index, eventAdapterService); + } + } + + // Try the array as a simple property + propertyDesc = eventType.GetSimpleProperty(PropertyNameAtomic); + if (propertyDesc == null) + { + return null; + } + + Type returnType = propertyDesc.ReturnType; + if (returnType == typeof(string)) + { + if (propertyDesc.ReadMethod != null) + { + MethodInfo method = propertyDesc.ReadMethod; + if (fastClass != null) + { + FastMethod fastMethod = fastClass.GetMethod(method); + return new StringFastPropertyGetter(fastMethod, _index, eventAdapterService); + } + else + { + return new StringMethodPropertyGetter(method, _index, eventAdapterService); + } + } + else + { + FieldInfo field = propertyDesc.AccessorField; + return new StringFieldPropertyGetter(field, _index, eventAdapterService); + } + } + else if (returnType.IsArray) + { + if (propertyDesc.ReadMethod != null) + { + MethodInfo method = propertyDesc.ReadMethod; + if (fastClass != null) + { + FastMethod fastMethod = fastClass.GetMethod(method); + return new ArrayFastPropertyGetter(fastMethod, _index, eventAdapterService); + } + else + { + return new ArrayMethodPropertyGetter(method, _index, eventAdapterService); + } + } + else + { + FieldInfo field = propertyDesc.AccessorField; + return new ArrayFieldPropertyGetter(field, _index, eventAdapterService); + } + } + else if (returnType.IsGenericDictionary()) + { + + } + else if (returnType.IsGenericList()) + { + if (propertyDesc.ReadMethod != null) + { + MethodInfo method = propertyDesc.ReadMethod; + if (fastClass != null) + { + FastMethod fastMethod = fastClass.GetMethod(method); + return new ListFastPropertyGetter(method, fastMethod, _index, eventAdapterService); + } + else + { + return new ListMethodPropertyGetter(method, _index, eventAdapterService); + } + } + else + { + FieldInfo field = propertyDesc.AccessorField; + return new ListFieldPropertyGetter(field, _index, eventAdapterService); + } + } + else if (returnType.IsImplementsInterface(typeof(IEnumerable))) + { + if (propertyDesc.ReadMethod != null) + { + MethodInfo method = propertyDesc.ReadMethod; + if (fastClass != null) + { + FastMethod fastMethod = fastClass.GetMethod(method); + return new EnumerableFastPropertyGetter(method, fastMethod, _index, eventAdapterService); + } + else + { + return new EnumerableMethodPropertyGetter(method, _index, eventAdapterService); + } + } + else + { + FieldInfo field = propertyDesc.AccessorField; + return new EnumerableFieldPropertyGetter(field, _index, eventAdapterService); + } + } + + return null; + } + + public override GenericPropertyDesc GetPropertyTypeGeneric(BeanEventType eventType, + EventAdapterService eventAdapterService) + { + InternalEventPropDescriptor descriptor = eventType.GetIndexedProperty(PropertyNameAtomic); + if (descriptor != null) + { + return new GenericPropertyDesc(descriptor.ReturnType); + } + + // Check if this is an method returning array which is a type of simple property + descriptor = eventType.GetSimpleProperty(PropertyNameAtomic); + if (descriptor == null) + { + return null; + } + + Type returnType = descriptor.ReturnType; + if (returnType.IsArray) + { + return new GenericPropertyDesc(returnType.GetElementType()); + } + else if (returnType.IsImplementsInterface(typeof(IEnumerable))) + { + if (descriptor.ReadMethod != null) + { + Type genericType = TypeHelper.GetGenericReturnType(descriptor.ReadMethod, false); + return new GenericPropertyDesc(genericType); + } + else if (descriptor.AccessorField != null) + { + Type genericType = TypeHelper.GetGenericFieldType(descriptor.AccessorField, false); + return new GenericPropertyDesc(genericType); + } + else + { + return null; + } + } + return null; + } + + public override Type GetPropertyType(BeanEventType eventType, EventAdapterService eventAdapterService) + { + var descriptor = eventType.GetIndexedProperty(PropertyNameAtomic); + if (descriptor != null) + { + return descriptor.ReturnType; + } + + // Check if this is an method returning array which is a type of simple property + descriptor = eventType.GetSimpleProperty(PropertyNameAtomic); + if (descriptor == null) + { + return null; + } + + return descriptor.ReturnType.GetIndexType(); + } + + public override Type GetPropertyTypeMap(DataMap optionalMapPropTypes, EventAdapterService eventAdapterService) + { + var type = optionalMapPropTypes.Get(PropertyNameAtomic); + if (type == null) + { + return null; + } + if (type is String) // resolve a property that is a map event type + { + var nestedName = type.ToString(); + var isArray = EventTypeUtility.IsPropertyArray(nestedName); + if (isArray) + { + nestedName = EventTypeUtility.GetPropertyRemoveArray(nestedName); + } + + var innerType = eventAdapterService.GetEventTypeByName(nestedName); + if (!(innerType is MapEventType)) + { + return null; + } + if (!isArray) + { + return null; // must be declared as an index to use array notation + } + else + { + return typeof(DataMap[]); + } + } + else + { + if (!(type is Type)) + { + return null; + } + if (!((Type)type).IsArray) + { + return null; + } + return ((Type)type).GetElementType().GetBoxedType(); + } + } + + public override MapEventPropertyGetter GetGetterMap(DataMap optionalMapPropTypes, EventAdapterService eventAdapterService) + { + Object type = optionalMapPropTypes.Get(PropertyNameAtomic); + if (type == null) + { + return null; + } + if (type is string) // resolve a property that is a map event type + { + String nestedName = type.ToString(); + bool isArray = EventTypeUtility.IsPropertyArray(nestedName); + if (isArray) + { + nestedName = EventTypeUtility.GetPropertyRemoveArray(nestedName); + } + EventType innerType = eventAdapterService.GetEventTypeByName(nestedName); + if (!(innerType is MapEventType)) + { + return null; + } + if (!isArray) + { + return null; // must be declared as an array to use an indexed notation + } + else + { + return new MapArrayPropertyGetter(PropertyNameAtomic, _index, eventAdapterService, innerType); + } + } + else + { + if (!(type is Type)) + { + return null; + } + if (!((Type)type).IsArray) + { + return null; + } + Type componentType = ((Type)type).GetElementType(); + // its an array + return new MapArrayEntryIndexedPropertyGetter(PropertyNameAtomic, _index, eventAdapterService, + componentType); + } + } + + public override void ToPropertyEPL(TextWriter writer) + { + writer.Write(PropertyNameAtomic); + writer.Write("["); + writer.Write(_index); + writer.Write("]"); + } + + public override EventPropertyGetter GetGetterDOM() + { + return new DOMIndexedGetter(PropertyNameAtomic, _index, null); + } + + public override EventPropertyGetter GetGetterDOM(SchemaElementComplex complexProperty, + EventAdapterService eventAdapterService, + BaseXMLEventType eventType, + String propertyExpression) + { + foreach (SchemaElementSimple simple in complexProperty.SimpleElements) + { + if (!simple.IsArray) + { + continue; + } + if (simple.Name != PropertyNameAtomic) + { + continue; + } + return new DOMIndexedGetter(PropertyNameAtomic, _index, null); + } + + foreach (SchemaElementComplex complex in complexProperty.ComplexElements) + { + if (!complex.IsArray) + { + continue; + } + if (complex.Name != PropertyNameAtomic) + { + continue; + } + return new DOMIndexedGetter(PropertyNameAtomic, _index, + new FragmentFactoryDOMGetter(eventAdapterService, eventType, + propertyExpression)); + } + + return null; + } + + public override SchemaItem GetPropertyTypeSchema(SchemaElementComplex complexProperty, + EventAdapterService eventAdapterService) + { + foreach (SchemaElementSimple simple in complexProperty.SimpleElements) + { + if (simple.Name != PropertyNameAtomic) + { + continue; + } + + if ((simple.IsArray) || + (simple.SimpleType == XmlSchemaSimpleType.GetBuiltInSimpleType(XmlTypeCode.String))) + { + // return the simple as a non-array since an index is provided + return new SchemaElementSimple( + simple.Name, + simple.Namespace, + simple.SimpleType, + simple.TypeName, + false, + simple.FractionDigits); + } + } + + foreach (SchemaElementComplex complex in complexProperty.ComplexElements) + { + if (complex.Name != PropertyNameAtomic) + { + continue; + } + if (complex.IsArray) + { + // return the complex as a non-array since an index is provided + return new SchemaElementComplex( + complex.Name, + complex.Namespace, + complex.Attributes, + complex.ComplexElements, + complex.SimpleElements, false, + null, + null); + } + } + + return null; + } + + /// + /// Returns the index number for an indexed property expression. + /// + /// property expression + /// index + + public static int GetIndex(String propertyName) + { + int start = propertyName.IndexOf('['); + int end = propertyName.IndexOf(']'); + String indexStr = propertyName.Substring(start, end - start); + return Int32.Parse(indexStr); + } + + public override ObjectArrayEventPropertyGetter GetGetterObjectArray( + IDictionary indexPerProperty, + IDictionary nestableTypes, + EventAdapterService eventAdapterService) + { + int propertyIndex; + if (!indexPerProperty.TryGetValue(PropertyNameAtomic, out propertyIndex)) + { + return null; + } + + var type = nestableTypes.Get(PropertyNameAtomic); + if (type == null) + { + return null; + } + + if (type is string) // resolve a property that is a map event type + { + var nestedName = type.ToString(); + var isArray = EventTypeUtility.IsPropertyArray(nestedName); + if (isArray) + { + nestedName = EventTypeUtility.GetPropertyRemoveArray(nestedName); + } + EventType innerType = eventAdapterService.GetEventTypeByName(nestedName); + if (!(innerType is MapEventType)) + { + return null; + } + if (!isArray) + { + return null; // must be declared as an array to use an indexed notation + } + else + { + return new ObjectArrayArrayPropertyGetter(propertyIndex, _index, eventAdapterService, innerType); + } + } + else + { + if (!(type is Type)) + { + return null; + } + if (!((Type)type).IsArray()) + { + return null; + } + Type componentType = ((Type)type).GetElementType(); + // its an array + return new ObjectArrayArrayPONOEntryIndexedPropertyGetter( + propertyIndex, _index, eventAdapterService, componentType); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/property/MappedProperty.cs b/NEsper.Core/NEsper.Core/events/property/MappedProperty.cs new file mode 100755 index 000000000..6ecf02b6c --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/property/MappedProperty.cs @@ -0,0 +1,307 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.events.arr; +using com.espertech.esper.events.bean; +using com.espertech.esper.events.map; +using com.espertech.esper.events.xml; +using com.espertech.esper.util; + +namespace com.espertech.esper.events.property +{ + using DataMap = IDictionary; + + /// + /// Represents a mapped property or array property, ie. an 'value' property with read method + /// GetValue(int index) or a 'array' property via read method GetArray() returning an array. + /// + public class MappedProperty : PropertyBase, PropertyWithKey + { + private readonly String _key; + + public MappedProperty(String propertyName) + : base(propertyName) + { + } + + /// Ctor. + /// is the property name of the mapped property + /// is the key value to access the mapped property + public MappedProperty(String propertyName, String key) + : base(propertyName) + { + _key = key; + } + + /// Returns the key value for mapped access. + /// key value + public string Key + { + get { return _key; } + } + + public override String[] ToPropertyArray() + { + return new[] { PropertyNameAtomic }; + } + + public override bool IsDynamic + { + get { return false; } + } + + public override EventPropertyGetter GetGetter(BeanEventType eventType, EventAdapterService eventAdapterService) + { + InternalEventPropDescriptor propertyDesc = eventType.GetMappedProperty(PropertyNameAtomic); + if (propertyDesc != null) + { + var method = propertyDesc.ReadMethod; + var fastClass = eventType.FastClass; + if (fastClass != null) + { + var fastMethod = fastClass.GetMethod(method); + return new KeyedFastPropertyGetter(fastMethod, _key, eventAdapterService); + } + else + { + return new KeyedMethodPropertyGetter(method, _key, eventAdapterService); + } + } + + // Try the array as a simple property + propertyDesc = eventType.GetSimpleProperty(PropertyNameAtomic); + if (propertyDesc == null) + { + return null; + } + + var returnType = propertyDesc.ReturnType; + // Unlike Java, CLR based maps are strongly typed ... the mechanics + // for identifying and extracting the map contents are also different. + if (!returnType.IsGenericStringDictionary()) + { + return null; + } + + if (propertyDesc.ReadMethod != null) + { + var fastClass = eventType.FastClass; + var method = propertyDesc.ReadMethod; + if (fastClass != null) + { + var fastMethod = fastClass.GetMethod(method); + return new KeyedMapFastPropertyGetter(method, fastMethod, _key, eventAdapterService); + } + else + { + return new KeyedMapMethodPropertyGetter(method, _key, eventAdapterService); + } + } + else + { + var field = propertyDesc.AccessorField; + return new KeyedMapFieldPropertyGetter(field, _key, eventAdapterService); + } + } + + public override Type GetPropertyType(BeanEventType eventType, EventAdapterService eventAdapterService) + { + var propertyDesc = eventType.GetMappedProperty(PropertyNameAtomic); + if (propertyDesc != null) + { + return propertyDesc.ReadMethod.ReturnType; + } + + // Check if this is an method returning array which is a type of simple property + var descriptor = eventType.GetSimpleProperty(PropertyNameAtomic); + if (descriptor == null) + { + return null; + } + + Type returnType = descriptor.ReturnType; + if (!returnType.IsGenericStringDictionary()) + { + return null; + } + + if (descriptor.ReadMethod != null) + { + return TypeHelper.GetGenericReturnTypeMap(descriptor.ReadMethod, false); + } + + if (descriptor.AccessorField != null) + { + return TypeHelper.GetGenericFieldTypeMap(descriptor.AccessorField, false); + } + + return null; + } + + public override GenericPropertyDesc GetPropertyTypeGeneric(BeanEventType eventType, EventAdapterService eventAdapterService) + { + var propertyDesc = eventType.GetMappedProperty(PropertyNameAtomic); + if (propertyDesc != null) + { + return new GenericPropertyDesc(propertyDesc.ReadMethod.ReturnType); + } + + // Check if this is an method returning array which is a type of simple property + var descriptor = eventType.GetSimpleProperty(PropertyNameAtomic); + if (descriptor == null) + { + return null; + } + + var returnType = descriptor.ReturnType; + if (!returnType.IsGenericStringDictionary()) + { + return null; + } + if (descriptor.ReadMethod != null) + { + var genericType = TypeHelper.GetGenericReturnTypeMap(descriptor.ReadMethod, false); + return new GenericPropertyDesc(genericType); + } + else if (descriptor.AccessorField != null) + { + var genericType = TypeHelper.GetGenericFieldTypeMap(descriptor.AccessorField, false); + return new GenericPropertyDesc(genericType); + } + else + { + return null; + } + } + + public override Type GetPropertyTypeMap(DataMap optionalMapPropTypes, EventAdapterService eventAdapterService) + { + var type = optionalMapPropTypes.Get(PropertyNameAtomic); + if (type == null) + { + return null; + } + if (type is Type) + { + var trueType = (Type)type; + if (trueType.IsGenericStringDictionary()) + { + return typeof(Object); + } + } + return null; // Mapped properties are not allowed in non-dynamic form in a map + } + + public override MapEventPropertyGetter GetGetterMap(DataMap optionalMapPropTypes, EventAdapterService eventAdapterService) + { + var type = optionalMapPropTypes.Get(PropertyNameAtomic); + if (type == null) + { + return null; + } + if (type is Type) + { + var trueType = (Type)type; + if (trueType.IsGenericStringDictionary()) + { + return new MapMappedPropertyGetter(PropertyNameAtomic, Key); + } + } + if (type.GetType().IsGenericStringDictionary()) + { + return new MapMappedPropertyGetter(PropertyNameAtomic, Key); + } + return null; + } + + public override void ToPropertyEPL(TextWriter writer) + { + writer.Write(PropertyNameAtomic); + writer.Write("('"); + writer.Write(_key); + writer.Write("')"); + } + + public override EventPropertyGetter GetGetterDOM(SchemaElementComplex complexProperty, EventAdapterService eventAdapterService, BaseXMLEventType eventType, String propertyExpression) + { + foreach (SchemaElementComplex complex in complexProperty.ComplexElements.Where(c => c.Name == PropertyNameAtomic)) + { + foreach (SchemaItemAttribute attribute in complex.Attributes) + { + if (!String.Equals(attribute.Name, "id", StringComparison.OrdinalIgnoreCase)) + { + continue; + } + } + + return new DOMMapGetter(PropertyNameAtomic, _key, null); + } + + return null; + } + + public override EventPropertyGetter GetGetterDOM() + { + return new DOMMapGetter(PropertyNameAtomic, _key, null); + } + + public override SchemaItem GetPropertyTypeSchema(SchemaElementComplex complexProperty, EventAdapterService eventAdapterService) + { + foreach (SchemaElementComplex complex in complexProperty.ComplexElements.Where(c => c.Name == PropertyNameAtomic)) + { + foreach (SchemaItemAttribute attribute in complex.Attributes) + { + if (!String.Equals(attribute.Name, "id", StringComparison.OrdinalIgnoreCase)) + { + continue; + } + } + + return complex; + } + + return null; + } + + public override ObjectArrayEventPropertyGetter GetGetterObjectArray( + IDictionary indexPerProperty, + IDictionary nestableTypes, + EventAdapterService eventAdapterService) + { + int index; + + if (!indexPerProperty.TryGetValue(PropertyNameAtomic, out index)) + { + return null; + } + + var type = nestableTypes.Get(PropertyNameAtomic); + var typeAsType = type as Type; + if (typeAsType != null) + { + if (typeAsType.IsGenericStringDictionary()) + { + return new ObjectArrayMappedPropertyGetter(index, Key); + } + } + if (type is DataMap) + { + return new ObjectArrayMappedPropertyGetter(index, Key); + } + return null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/property/NestedProperty.cs b/NEsper.Core/NEsper.Core/events/property/NestedProperty.cs new file mode 100755 index 000000000..bd87a42be --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/property/NestedProperty.cs @@ -0,0 +1,552 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.events.arr; +using com.espertech.esper.events.bean; +using com.espertech.esper.events.map; +using com.espertech.esper.events.xml; +using com.espertech.esper.util; + +namespace com.espertech.esper.events.property +{ + using Map = IDictionary; + + /// + /// This class represents a nested property, each nesting level made up of a property instance that + /// can be of type indexed, mapped or simple itself. + /// + /// The syntax for nested properties is as follows. + ///
    a.n a[1].n a('1').n 
    + ///
    + public class NestedProperty : Property + { + /// Ctor. + /// is the list of Property instances representing each nesting level + public NestedProperty(IList properties) + { + Properties = properties; + } + + /// Returns the list of property instances making up the nesting levels. + /// list of Property instances + public IList Properties { get; private set; } + + public bool IsDynamic + { + get { return Properties.Any(property => property.IsDynamic); } + } + + public EventPropertyGetter GetGetter(BeanEventType eventType, EventAdapterService eventAdapterService) + { + var getters = new List(); + + Property lastProperty = null; + for (var it = Properties.EnumerateWithLookahead(); it.HasNext(); ) + { + Property property = it.Next(); + lastProperty = property; + EventPropertyGetter getter = property.GetGetter(eventType, eventAdapterService); + if (getter == null) + { + return null; + } + + if (it.HasNext()) + { + var clazz = property.GetPropertyType(eventType, eventAdapterService); + if (clazz == null) + { + // if the property is not valid, return null + return null; + } + // Map cannot be used to further nest as the type cannot be determined + if (clazz == typeof(Map)) + { + return null; + } + if (clazz.IsArray) + { + return null; + } + eventType = eventAdapterService.BeanEventTypeFactory.CreateBeanType( + clazz.FullName, clazz, false, false, false); + } + getters.Add(getter); + } + + GenericPropertyDesc finalPropertyType = lastProperty.GetPropertyTypeGeneric(eventType, eventAdapterService); + return new NestedPropertyGetter(getters, eventAdapterService, finalPropertyType.GenericType, finalPropertyType.Generic); + } + + public Type GetPropertyType(BeanEventType eventType, EventAdapterService eventAdapterService) + { + Type result = null; + + for (var it = Properties.EnumerateWithLookahead(); it.HasNext(); ) + { + Property property = it.Next(); + result = property.GetPropertyType(eventType, eventAdapterService); + + if (result == null) + { + // property not found, return null + return null; + } + + if (it.HasNext()) + { + // Map cannot be used to further nest as the type cannot be determined + if (result == typeof(Map)) + { + return null; + } + + if (result.IsArray || result.IsPrimitive || result.IsBuiltinDataType()) + { + return null; + } + + eventType = eventAdapterService.BeanEventTypeFactory.CreateBeanType(result.Name, result, false, false, false); + } + } + + return result; + } + + public GenericPropertyDesc GetPropertyTypeGeneric(BeanEventType eventType, EventAdapterService eventAdapterService) + { + GenericPropertyDesc result = null; + + for (var it = Properties.EnumerateWithLookahead(); it.HasNext(); ) + { + Property property = it.Next(); + result = property.GetPropertyTypeGeneric(eventType, eventAdapterService); + + if (result == null) + { + // property not found, return null + return null; + } + + if (it.HasNext()) + { + // Map cannot be used to further nest as the type cannot be determined + if (result.GenericType == typeof(Map)) + { + return null; + } + + if (result.GenericType.IsArray) + { + return null; + } + + eventType = eventAdapterService.BeanEventTypeFactory.CreateBeanType( + result.GenericType.FullName, + result.GenericType, + false, false, false); + } + } + + return result; + } + + public Type GetPropertyTypeMap(Map optionalMapPropTypes, EventAdapterService eventAdapterService) + { + Map currentDictionary = optionalMapPropTypes; + + int count = 0; + for (var it = Properties.EnumerateWithLookahead(); it.HasNext(); ) + { + count++; + var property = it.Next(); + var propertyBase = (PropertyBase)property; + var propertyName = propertyBase.PropertyNameAtomic; + + Object nestedType = null; + if (currentDictionary != null) + { + nestedType = currentDictionary.Get(propertyName); + } + + if (nestedType == null) + { + if (property is DynamicProperty) + { + return typeof(Object); + } + else + { + return null; + } + } + + if (!it.HasNext()) + { + if (nestedType is Type) + { + return ((Type)nestedType).GetBoxedType(); + } + if (nestedType is Map) + { + return typeof(Map); + } + } + + if (ReferenceEquals(nestedType, typeof(Map))) + { + return typeof(Object); + } + + if (nestedType is Type) + { + Type pocoType = (Type)nestedType; + if (!pocoType.IsArray) + { + BeanEventType beanType = eventAdapterService.BeanEventTypeFactory.CreateBeanType(pocoType.Name, pocoType, false, false, false); + String remainingProps = ToPropertyEPL(Properties, count); + return beanType.GetPropertyType(remainingProps).GetBoxedType(); + } + else if (property is IndexedProperty) + { + Type componentType = pocoType.GetElementType(); + BeanEventType beanType = eventAdapterService.BeanEventTypeFactory.CreateBeanType(componentType.Name, componentType, false, false, false); + String remainingProps = ToPropertyEPL(Properties, count); + return beanType.GetPropertyType(remainingProps).GetBoxedType(); + } + } + + if (nestedType is String) // property type is the name of a map event type + { + String nestedName = nestedType.ToString(); + bool isArray = EventTypeUtility.IsPropertyArray(nestedName); + if (isArray) + { + nestedName = EventTypeUtility.GetPropertyRemoveArray(nestedName); + } + + EventType innerType = eventAdapterService.GetEventTypeByName(nestedName); + if (innerType == null) + { + return null; + } + + String remainingProps = ToPropertyEPL(Properties, count); + return innerType.GetPropertyType(remainingProps).GetBoxedType(); + } + else if (nestedType is EventType) // property type is the name of a map event type + { + var innerType = (EventType)nestedType; + var remainingProps = ToPropertyEPL(Properties, count); + return innerType.GetPropertyType(remainingProps).GetBoxedType(); + } + else + { + if (!(nestedType is Map)) + { + String message = "Nestable map type configuration encountered an unexpected value type of '" + + nestedType.GetType() + " for property '" + propertyName + "', expected Class, typeof(Map) or IDictionary as value type"; + throw new PropertyAccessException(message); + } + } + + currentDictionary = (Map)nestedType; + } + throw new IllegalStateException("Unexpected end of nested property"); + } + + public MapEventPropertyGetter GetGetterMap(Map optionalMapPropTypes, EventAdapterService eventAdapterService) + { + var getters = new List(); + var currentDictionary = optionalMapPropTypes; + + int count = 0; + for (var it = Properties.EnumerateWithLookahead(); it.HasNext(); ) + { + count++; + Property property = it.Next(); + + // manufacture a getter for getting the item out of the map + EventPropertyGetter getter = property.GetGetterMap(currentDictionary, eventAdapterService); + if (getter == null) + { + return null; + } + getters.Add(getter); + + var @base = (PropertyBase)property; + var propertyName = @base.PropertyNameAtomic; + + // For the next property if there is one, check how to property type is defined + if (!it.HasNext()) + { + continue; + } + + if (currentDictionary != null) + { + // check the type that this property will return + Object propertyReturnType = currentDictionary.Get(propertyName); + + if (propertyReturnType == null) + { + currentDictionary = null; + } + if (propertyReturnType != null) + { + if (propertyReturnType is Map) + { + currentDictionary = (Map)propertyReturnType; + } + else if (ReferenceEquals(propertyReturnType, typeof(Map))) + { + currentDictionary = null; + } + else if (propertyReturnType is String) + { + String nestedName = propertyReturnType.ToString(); + bool isArray = EventTypeUtility.IsPropertyArray(nestedName); + if (isArray) + { + nestedName = EventTypeUtility.GetPropertyRemoveArray(nestedName); + } + + EventType innerType = eventAdapterService.GetEventTypeByName(nestedName); + if (innerType == null) + { + return null; + } + + String remainingProps = ToPropertyEPL(Properties, count); + EventPropertyGetter getterInner = innerType.GetGetter(remainingProps); + if (getterInner == null) + { + return null; + } + + getters.Add(getterInner); + break; // the single getter handles the rest + } + else if (propertyReturnType is EventType) + { + var innerType = (EventType)propertyReturnType; + var remainingProps = ToPropertyEPL(Properties, count); + var getterInner = innerType.GetGetter(remainingProps); + if (getterInner == null) + { + return null; + } + + getters.Add(getterInner); + break; // the single getter handles the rest + } + else + { + // treat the return type of the map property as an object + var returnType = (Type)propertyReturnType; + if (!returnType.IsArray) + { + BeanEventType beanType = + eventAdapterService.BeanEventTypeFactory.CreateBeanType(returnType.Name, returnType, + false, false, false); + String remainingProps = ToPropertyEPL(Properties, count); + EventPropertyGetter getterInner = beanType.GetGetter(remainingProps); + if (getterInner == null) + { + return null; + } + getters.Add(getterInner); + break; // the single getter handles the rest + } + else + { + Type componentType = returnType.GetElementType(); + BeanEventType beanType = eventAdapterService.BeanEventTypeFactory.CreateBeanType( + componentType.Name, componentType, false, false, false); + String remainingProps = ToPropertyEPL(Properties, count); + EventPropertyGetter getterInner = beanType.GetGetter(remainingProps); + if (getterInner == null) + { + return null; + } + getters.Add(getterInner); + break; // the single pono getter handles the rest + } + } + } + } + } + + var hasNonmapGetters = false; + for (int i = 0; i < getters.Count; i++) + { + if (!(getters[i] is MapEventPropertyGetter)) + { + hasNonmapGetters = true; + } + } + if (!hasNonmapGetters) + { + return new MapNestedPropertyGetterMapOnly(getters, eventAdapterService); + } + else + { + return new MapNestedPropertyGetterMixedType(getters, eventAdapterService); + } + } + + public ObjectArrayEventPropertyGetter GetGetterObjectArray(IDictionary indexPerProperty, IDictionary nestableTypes, EventAdapterService eventAdapterService) + { + throw new UnsupportedOperationException("Object array nested property getter not implemented as not implicitly nestable"); + } + + public void ToPropertyEPL(TextWriter writer) + { + String delimiter = ""; + foreach (Property property in Properties) + { + writer.Write(delimiter); + property.ToPropertyEPL(writer); + delimiter = "."; + } + } + + public String[] ToPropertyArray() + { + var propertyNames = new List(); + foreach (Property property in Properties) + { + String[] nested = property.ToPropertyArray(); + propertyNames.AddAll(nested); + } + return propertyNames.ToArray(); + } + + public EventPropertyGetter GetGetterDOM() + { + var getters = new List(); + + for (var it = Properties.EnumerateWithLookahead(); it.HasNext(); ) + { + Property property = it.Next(); + EventPropertyGetter getter = property.GetGetterDOM(); + if (getter == null) + { + return null; + } + + getters.Add(getter); + } + + return new DOMNestedPropertyGetter(getters, null); + } + + public EventPropertyGetter GetGetterDOM(SchemaElementComplex parentComplexProperty, EventAdapterService eventAdapterService, BaseXMLEventType eventType, String propertyExpression) + { + List getters = new List(); + + SchemaElementComplex complexElement = parentComplexProperty; + + for (var it = Properties.EnumerateWithLookahead(); it.HasNext(); ) + { + Property property = it.Next(); + EventPropertyGetter getter = property.GetGetterDOM(complexElement, eventAdapterService, eventType, propertyExpression); + if (getter == null) + { + return null; + } + + if (it.HasNext()) + { + SchemaItem childSchemaItem = property.GetPropertyTypeSchema(complexElement, eventAdapterService); + if (childSchemaItem == null) + { + // if the property is not valid, return null + return null; + } + + if ((childSchemaItem is SchemaItemAttribute) || (childSchemaItem is SchemaElementSimple)) + { + return null; + } + + complexElement = (SchemaElementComplex)childSchemaItem; + + if (complexElement.IsArray) + { + if ((property is SimpleProperty) || (property is DynamicSimpleProperty)) + { + return null; + } + } + } + + getters.Add(getter); + } + + return new DOMNestedPropertyGetter(getters, new FragmentFactoryDOMGetter(eventAdapterService, eventType, propertyExpression)); + } + + public SchemaItem GetPropertyTypeSchema(SchemaElementComplex parentComplexProperty, EventAdapterService eventAdapterService) + { + Property lastProperty = null; + SchemaElementComplex complexElement = parentComplexProperty; + + for (var en = Properties.EnumerateWithLookahead(); en.HasNext(); ) + { + Property property = en.Next(); + lastProperty = property; + + if (en.HasNext()) + { + SchemaItem childSchemaItem = property.GetPropertyTypeSchema(complexElement, eventAdapterService); + if (childSchemaItem == null) + { + // if the property is not valid, return null + return null; + } + + if ((childSchemaItem is SchemaItemAttribute) || (childSchemaItem is SchemaElementSimple)) + { + return null; + } + + complexElement = (SchemaElementComplex)childSchemaItem; + } + } + + return lastProperty.GetPropertyTypeSchema(complexElement, eventAdapterService); + } + + public string PropertyNameAtomic + { + get { throw new UnsupportedOperationException("Nested properties do not provide an atomic property name"); } + } + + private static String ToPropertyEPL(IList property, int startFromIndex) + { + var delimiter = ""; + var writer = new StringWriter(); + for (int i = startFromIndex; i < property.Count; i++) + { + writer.Write(delimiter); + property[i].ToPropertyEPL(writer); + delimiter = "."; + } + return writer.ToString(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/property/Property.cs b/NEsper.Core/NEsper.Core/events/property/Property.cs new file mode 100755 index 000000000..960dccba5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/property/Property.cs @@ -0,0 +1,137 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.client; +using com.espertech.esper.events.arr; +using com.espertech.esper.events.bean; +using com.espertech.esper.events.map; +using com.espertech.esper.events.xml; + +namespace com.espertech.esper.events.property +{ + using DataMap = IDictionary; + + /// + /// Interface for a property of an event of type BeanEventType. + /// Properties are designed to handle the different types of properties for such events: + /// indexed, mapped, simple, nested, or a combination of those. + /// + public interface Property + { + /// + /// Returns the property type. + /// + /// is the event type representing the object + /// for event adapters + /// + /// property type class + /// + Type GetPropertyType(BeanEventType eventType, EventAdapterService eventAdapterService); + + /// + /// Returns the property type plus its generic type parameter, if any. + /// + /// is the event type representing the object + /// for event adapters + /// + /// type and generic descriptor + /// + GenericPropertyDesc GetPropertyTypeGeneric(BeanEventType eventType, EventAdapterService eventAdapterService); + + /// + /// Returns value getter for the property of an event of the given event type. + /// + /// is the type of event to make a getter for + /// factory for event beans and event types + /// + /// fast property value getter for property + /// + EventPropertyGetter GetGetter(BeanEventType eventType, EventAdapterService eventAdapterService); + + + ObjectArrayEventPropertyGetter GetGetterObjectArray(IDictionary indexPerProperty, IDictionary nestableTypes, EventAdapterService eventAdapterService); + + /// + /// Returns the property type for use with Map event representations. + /// + /// a map-within-map type definition, if supplied, or null if not supplied + /// for resolving further map event types that are property types + /// + /// property type @param optionalMapPropTypes + /// + Type GetPropertyTypeMap(DataMap optionalMapPropTypes, EventAdapterService eventAdapterService); + + /// + /// Returns the getter-method for use with Map event representations. + /// + /// a map-within-map type definition, if supplied, or null if not supplied + /// for resolving further map event types that are property types + /// + /// getter for maps + /// + MapEventPropertyGetter GetGetterMap(DataMap optionalMapPropTypes, EventAdapterService eventAdapterService); + + /// + /// Returns the property type for use with DOM event representations. + /// + /// a element-within-element type definition + /// for resolving further element event types if defined + /// + /// property type + /// + SchemaItem GetPropertyTypeSchema(SchemaElementComplex complexProperty, EventAdapterService eventAdapterService); + + /// + /// Returns the getter-method for use with XML DOM event representations. + /// + /// a element-within-element type definition + /// for resolving or creating further event types that are property types + /// the event type + /// the full property expression + /// + /// getter + /// + EventPropertyGetter GetGetterDOM(SchemaElementComplex complexProperty, EventAdapterService eventAdapterService, BaseXMLEventType xmlEventType, String propertyExpression); + + /// + /// Returns the getter-method for use with XML DOM event representations. + /// + /// + /// getter + /// + EventPropertyGetter GetGetterDOM(); + + /// + /// Write the EPL-representation of the property. + /// + /// to write to + void ToPropertyEPL(TextWriter writer); + + /// + /// Return a String-array of atomic property names. + /// + /// + /// array of atomic names in a property expression + /// + String[] ToPropertyArray(); + + /// + /// Returns true for dynamic properties. + /// + /// + /// false for not-dynamic properties, true for dynamic properties. + /// + bool IsDynamic { get; } + + string PropertyNameAtomic { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/events/property/PropertyBase.cs b/NEsper.Core/NEsper.Core/events/property/PropertyBase.cs new file mode 100755 index 000000000..84c953644 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/property/PropertyBase.cs @@ -0,0 +1,156 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.client; +using com.espertech.esper.events.arr; +using com.espertech.esper.events.bean; +using com.espertech.esper.events.map; +using com.espertech.esper.events.xml; + +namespace com.espertech.esper.events.property +{ + /// + /// All properties have a property name and this is the abstract base class + /// that serves up the property name. + /// + + public abstract class PropertyBase : Property + { + /// Returns the property name. + /// name of property + /// + public string PropertyNameAtomic { get; internal set; } + + /// Ctor. + /// is the name of the property + /// + protected PropertyBase(String propertyName) + { + PropertyNameAtomic = PropertyParser.UnescapeBacktick(propertyName); + } + + /// + /// Returns the property type. + /// + /// is the event type representing the object + /// for event adapters + /// + /// property type class + /// + public abstract Type GetPropertyType(BeanEventType eventType, EventAdapterService eventAdapterService); + + /// + /// Returns the property type plus its generic type parameter, if any. + /// + /// is the event type representing the object + /// for event adapters + /// + /// type and generic descriptor + /// + public abstract GenericPropertyDesc GetPropertyTypeGeneric(BeanEventType eventType, EventAdapterService eventAdapterService); + + /// + /// Returns value getter for the property of an event of the given event type. + /// + /// is the type of event to make a getter for + /// factory for event beans and event types + /// + /// fast property value getter for property + /// + public abstract EventPropertyGetter GetGetter(BeanEventType eventType, EventAdapterService eventAdapterService); + + /// + /// Returns the property type for use with Map event representations. + /// + /// a map-within-map type definition, if supplied, or null if not supplied + /// for resolving further map event types that are property types + /// + /// property type @param optionalMapPropTypes + /// + public abstract Type GetPropertyTypeMap(IDictionary optionalMapPropTypes, EventAdapterService eventAdapterService); + + /// + /// Returns the getter-method for use with Map event representations. + /// + /// a map-within-map type definition, if supplied, or null if not supplied + /// for resolving further map event types that are property types + /// + /// getter for maps + /// + public abstract MapEventPropertyGetter GetGetterMap(IDictionary optionalMapPropTypes, EventAdapterService eventAdapterService); + + /// + /// Returns the property type for use with DOM event representations. + /// + /// a element-within-element type definition + /// for resolving further element event types if defined + /// + /// property type + /// + public abstract SchemaItem GetPropertyTypeSchema(SchemaElementComplex complexProperty, EventAdapterService eventAdapterService); + + /// + /// Returns the getter-method for use with XML DOM event representations. + /// + /// a element-within-element type definition + /// for resolving or creating further event types that are property types + /// the event type + /// the full property expression + /// + /// getter + /// + public abstract EventPropertyGetter GetGetterDOM(SchemaElementComplex complexProperty, EventAdapterService eventAdapterService, BaseXMLEventType xmlEventType, string propertyExpression); + + /// + /// Returns the getter-method for use with XML DOM event representations. + /// + /// + /// getter + /// + public abstract EventPropertyGetter GetGetterDOM(); + + /// + /// Gets the getter object array. + /// + /// The index per property. + /// The nestable types. + /// The event adapter service. + /// + public abstract ObjectArrayEventPropertyGetter GetGetterObjectArray(IDictionary indexPerProperty, IDictionary nestableTypes, EventAdapterService eventAdapterService); + + /// + /// Return a String-array of atomic property names. + /// + /// + /// array of atomic names in a property expression + /// + public abstract string[] ToPropertyArray(); + + /// + /// Returns true for dynamic properties. + /// + /// + /// false for not-dynamic properties, true for dynamic properties. + /// + public virtual bool IsDynamic + { + get { return false; } + } + + /// Write the EPL-representation of the property. + /// to write to + public virtual void ToPropertyEPL(TextWriter writer) + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/property/PropertyParser.cs b/NEsper.Core/NEsper.Core/events/property/PropertyParser.cs new file mode 100755 index 000000000..683d04651 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/property/PropertyParser.cs @@ -0,0 +1,313 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +using Antlr4.Runtime; + +using com.espertech.esper.client; +using com.espertech.esper.compat.logging; +using com.espertech.esper.compat.threading; +using com.espertech.esper.epl.generated; +using com.espertech.esper.epl.parse; +using com.espertech.esper.type; + +namespace com.espertech.esper.events.property +{ + /// + /// Parser for property names that can be simple, nested, mapped or a combination of these. + /// Uses ANTLR parser to parse. + /// + public class PropertyParser + { + private static readonly ILog Log = + LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + private static readonly ILockable StaticLock = + LockManager.CreateLock(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private static ISet _keywordCache; + + public static Property ParseAndWalk(string property, bool isRootedDynamic) + { + return Walk(Parse(property), isRootedDynamic); + } + + /// + /// Parses property. + /// For cases when the property is not following the property syntax assume we act lax and assume its a simple property. + /// + /// property to parse + /// property or SimpleProperty if the property cannot be parsed + public static Property ParseAndWalkLaxToSimple(String property) + { + try + { + return Walk(Parse(property), false); + } + catch (PropertyAccessException) + { + return new SimpleProperty(property); + } + } + + /// + /// Parse the given property name returning a Property instance for the property. + /// + /// The tree. + /// is true to indicate that the property is already rooted in a dynamicproperty and therefore all child properties should be dynamic properties as well + /// + /// Property instance for property + /// + public static Property Walk(EsperEPL2GrammarParser.StartEventPropertyRuleContext tree, bool isRootedDynamic) + { + if (tree.eventProperty().eventPropertyAtomic().Length == 1) + { + return MakeProperty(tree.eventProperty().eventPropertyAtomic(0), isRootedDynamic); + } + + var propertyRoot = tree.eventProperty(); + + IList properties = new List(); + var isRootedInDynamic = isRootedDynamic; + foreach (var atomic in propertyRoot.eventPropertyAtomic()) + { + var property = MakeProperty(atomic, isRootedInDynamic); + if (property is DynamicSimpleProperty) + { + isRootedInDynamic = true; + } + properties.Add(property); + } + return new NestedProperty(properties); + } + + /// + /// Parses a given property name returning an AST. + /// + /// to parse + /// AST syntax tree + public static EsperEPL2GrammarParser.StartEventPropertyRuleContext Parse(string propertyName) + { + ICharStream input; + try + { + input = new NoCaseSensitiveStream(propertyName); + } + catch (IOException ex) + { + throw new PropertyAccessException("IOException parsing property name '" + propertyName + '\'', ex); + } + + var lex = ParseHelper.NewLexer(input); + var tokens = new CommonTokenStream(lex); + try + { + tokens.Fill(); + } + catch (Exception e) + { + if (ParseHelper.HasControlCharacters(propertyName)) + { + throw new PropertyAccessException("Unrecognized control characters found in text"); + } + throw new PropertyAccessException("Failed to parse text: " + e.Message); + } + + var g = ParseHelper.NewParser(tokens); + EsperEPL2GrammarParser.StartEventPropertyRuleContext r; + + try + { + r = g.startEventPropertyRule(); + } + catch (RecognitionException e) + { + return HandleRecognitionEx(e, tokens, propertyName, g); + } + catch (Exception e) + { + if (Log.IsDebugEnabled) + { + Log.Debug("Error parsing property expression [" + propertyName + "]", e); + } + if (e.InnerException is RecognitionException) + { + return HandleRecognitionEx((RecognitionException)e.InnerException, tokens, propertyName, g); + } + else + { + throw; + } + } + + return r; + } + + private static EsperEPL2GrammarParser.StartEventPropertyRuleContext HandleRecognitionEx(RecognitionException e, CommonTokenStream tokens, string propertyName, EsperEPL2GrammarParser g) + { + // Check for keywords and escape each, parse again + var escapedPropertyName = EscapeKeywords(tokens); + + ICharStream inputEscaped; + try + { + inputEscaped = new NoCaseSensitiveStream(escapedPropertyName); + } + catch (IOException ex) + { + throw new PropertyAccessException("IOException parsing property name '" + propertyName + '\'', ex); + } + + var lexEscaped = ParseHelper.NewLexer(inputEscaped); + var tokensEscaped = new CommonTokenStream(lexEscaped); + var gEscaped = ParseHelper.NewParser(tokensEscaped); + + try + { + return gEscaped.startEventPropertyRule(); + } + catch + { + } + + throw ExceptionConvertor.ConvertProperty(e, propertyName, true, g); + } + + private static string EscapeKeywords(CommonTokenStream tokens) + { + using (StaticLock.Acquire()) + { + if (_keywordCache == null) + { + _keywordCache = new HashSet(); + var keywords = ParseHelper.NewParser(tokens).GetKeywords(); + foreach (var keyword in keywords) + { + if (keyword[0] == '\'' && keyword[keyword.Length - 1] == '\'') + { + _keywordCache.Add(keyword.Substring(1, keyword.Length - 2)); + } + } + } + + var writer = new StringWriter(); + foreach (var token in tokens.GetTokens()) // Call getTokens first before invoking tokens.size! ANTLR problem + { + if (token.Type == EsperEPL2GrammarLexer.Eof) + { + break; + } + var isKeyword = _keywordCache.Contains(token.Text.ToLowerInvariant()); + if (isKeyword) + { + writer.Write('`'); + writer.Write(token.Text); + writer.Write('`'); + } + else + { + writer.Write(token.Text); + } + } + return writer.ToString(); + } + } + + /// + /// Returns true if the property is a dynamic property. + /// + /// property ast + /// dynamic or not + public static bool IsPropertyDynamic(EsperEPL2GrammarParser.StartEventPropertyRuleContext ast) + { + IList ctxs = ast.eventProperty().eventPropertyAtomic(); + return ctxs.Any(ctx => ctx.q != null || ctx.q1 != null); + } + + private static Property MakeProperty(EsperEPL2GrammarParser.EventPropertyAtomicContext atomic, bool isRootedInDynamic) + { + var prop = ASTUtil.UnescapeDot(atomic.eventPropertyIdent().GetText()); + if (prop.Length == 0) + { + throw new PropertyAccessException("Invalid zero-length string provided as an event property name"); + } + if (atomic.lb != null) + { + var index = IntValue.ParseString(atomic.ni.GetText()); + if (!isRootedInDynamic && atomic.q == null) + { + return new IndexedProperty(prop, index); + } + else + { + return new DynamicIndexedProperty(prop, index); + } + } + else if (atomic.lp != null) + { + var key = StringValue.ParseString(atomic.s.Text); + if (!isRootedInDynamic && atomic.q == null) + { + return new MappedProperty(prop, key); + } + else + { + return new DynamicMappedProperty(prop, key); + } + } + else + { + if (!isRootedInDynamic && atomic.q1 == null) + { + return new SimpleProperty(prop); + } + else + { + return new DynamicSimpleProperty(prop); + } + } + } + + public static string UnescapeBacktick(string unescapedPropertyName) + { + if (unescapedPropertyName.StartsWith("`") && unescapedPropertyName.EndsWith("`")) + { + return unescapedPropertyName.Substring(1, unescapedPropertyName.Length - 2); + } + + if (!unescapedPropertyName.Contains("`")) + { + return unescapedPropertyName; + } + + // parse and render + var property = PropertyParser.ParseAndWalkLaxToSimple(unescapedPropertyName); + if (property is NestedProperty) + { + var writer = new StringWriter(); + property.ToPropertyEPL(writer); + return writer.ToString(); + } + + return unescapedPropertyName; + } + + public static bool IsNestedPropertyWithNonSimpleLead(EsperEPL2GrammarParser.EventPropertyContext ctx) + { + if (ctx.eventPropertyAtomic().Length == 1) + { + return false; + } + var atomic = ctx.eventPropertyAtomic()[0]; + return atomic.lb != null || atomic.lp != null || atomic.q1 != null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/property/PropertySimple.cs b/NEsper.Core/NEsper.Core/events/property/PropertySimple.cs new file mode 100755 index 000000000..ddbdbd900 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/property/PropertySimple.cs @@ -0,0 +1,19 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; + +namespace com.espertech.esper.events.property +{ + public interface PropertySimple { + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/events/property/PropertyWithIndex.cs b/NEsper.Core/NEsper.Core/events/property/PropertyWithIndex.cs new file mode 100755 index 000000000..93f8670e3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/property/PropertyWithIndex.cs @@ -0,0 +1,15 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.events.property +{ + public interface PropertyWithIndex + { + int Index { get; } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/events/property/PropertyWithKey.cs b/NEsper.Core/NEsper.Core/events/property/PropertyWithKey.cs new file mode 100755 index 000000000..d65e8596a --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/property/PropertyWithKey.cs @@ -0,0 +1,15 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.events.property +{ + public interface PropertyWithKey + { + string Key { get; } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/events/property/SimpleProperty.cs b/NEsper.Core/NEsper.Core/events/property/SimpleProperty.cs new file mode 100755 index 000000000..e56683f74 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/property/SimpleProperty.cs @@ -0,0 +1,218 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.events.arr; +using com.espertech.esper.events.bean; +using com.espertech.esper.events.map; +using com.espertech.esper.events.xml; +using com.espertech.esper.util; + +namespace com.espertech.esper.events.property +{ + /// + /// Represents a simple property of a given name. + /// + public class SimpleProperty + : PropertyBase + , PropertySimple + { + /// + /// Ctor. + /// + /// is the property name + public SimpleProperty(String propertyName) + : base(propertyName) + { + } + + public override String[] ToPropertyArray() + { + return new[] { PropertyNameAtomic }; + } + + public override EventPropertyGetter GetGetter(BeanEventType eventType, EventAdapterService eventAdapterService) + { + var propertyDesc = eventType.GetSimpleProperty(PropertyNameAtomic); + if (propertyDesc == null) + { + return null; + } + if (!propertyDesc.PropertyType.Equals(EventPropertyType.SIMPLE)) + { + return null; + } + return eventType.GetGetter(PropertyNameAtomic); + } + + public override Type GetPropertyType(BeanEventType eventType, EventAdapterService eventAdapterService) + { + var propertyDesc = eventType.GetSimpleProperty(PropertyNameAtomic); + if (propertyDesc == null) + { + return null; + } + return propertyDesc.ReturnType.GetBoxedType(); + } + + public override GenericPropertyDesc GetPropertyTypeGeneric(BeanEventType eventType, EventAdapterService eventAdapterService) + { + var propertyDesc = eventType.GetSimpleProperty(PropertyNameAtomic); + if (propertyDesc == null) + { + return null; + } + return propertyDesc.GetReturnTypeGeneric(); + } + + public override Type GetPropertyTypeMap(IDictionary optionalMapPropTypes, EventAdapterService eventAdapterService) + { + // The simple, none-dynamic property needs a definition of the map contents else no property + if (optionalMapPropTypes == null) + { + return null; + } + var def = optionalMapPropTypes.Get(PropertyNameAtomic); + if (def == null) + { + return null; + } + if (def is Type) + { + return ((Type)def).GetBoxedType(); + } + else if (def is IDictionary) + { + return typeof(IDictionary); + } + else if (def is String) + { + String propertyName = def.ToString(); + bool isArray = EventTypeUtility.IsPropertyArray(propertyName); + if (isArray) + { + propertyName = EventTypeUtility.GetPropertyRemoveArray(propertyName); + } + + var eventType = eventAdapterService.GetEventTypeByName(propertyName); + if (eventType is MapEventType) + { + if (isArray) + { + return typeof(IDictionary[]); + } + else + { + return typeof(IDictionary); + } + } + + if (eventType is ObjectArrayEventType) + { + if (isArray) + { + return typeof(Object[][]); + } + else + { + return typeof(Object[]); + } + } + } + String message = "Nestable map type configuration encountered an unexpected value type of '" + + def.GetType() + " for property '" + PropertyNameAtomic + "', expected Map or Class"; + throw new PropertyAccessException(message); + } + + public override MapEventPropertyGetter GetGetterMap(IDictionary optionalMapPropTypes, EventAdapterService eventAdapterService) + { + // The simple, none-dynamic property needs a definition of the map contents else no property + if (optionalMapPropTypes == null) + { + return null; + } + var def = optionalMapPropTypes.Get(PropertyNameAtomic); + if (def == null) + { + return null; + } + + return new MapPropertyGetterDefaultNoFragment(PropertyNameAtomic, eventAdapterService); + } + + public override void ToPropertyEPL(TextWriter writer) + { + writer.Write(PropertyNameAtomic); + } + + public override EventPropertyGetter GetGetterDOM() + { + return new DOMAttributeAndElementGetter(PropertyNameAtomic); + } + + public override EventPropertyGetter GetGetterDOM(SchemaElementComplex complexProperty, EventAdapterService eventAdapterService, BaseXMLEventType xmlEventType, String propertyExpression) + { + if (complexProperty.Attributes.Any(attribute => attribute.Name == PropertyNameAtomic)) + { + return new DOMSimpleAttributeGetter(PropertyNameAtomic); + } + + foreach (var simple in complexProperty.SimpleElements.Where(simple => simple.Name == PropertyNameAtomic)) + { + return new DOMComplexElementGetter(PropertyNameAtomic, null, simple.IsArray); + } + + foreach (SchemaElementComplex complex in complexProperty.ComplexElements) + { + var complexFragmentFactory = new FragmentFactoryDOMGetter(eventAdapterService, xmlEventType, propertyExpression); + if (complex.Name == PropertyNameAtomic) + { + return new DOMComplexElementGetter(PropertyNameAtomic, complexFragmentFactory, complex.IsArray); + } + } + + return null; + } + + public override SchemaItem GetPropertyTypeSchema(SchemaElementComplex complexProperty, EventAdapterService eventAdapterService) + { + return SchemaUtil.FindPropertyMapping(complexProperty, PropertyNameAtomic); + } + + public override bool IsDynamic + { + get { return false; } + } + + public override ObjectArrayEventPropertyGetter GetGetterObjectArray( + IDictionary indexPerProperty, + IDictionary nestableTypes, + EventAdapterService eventAdapterService) + { + // The simple, none-dynamic property needs a definition of the map contents else no property + if (nestableTypes == null) + { + return null; + } + + int propertyIndex; + if (!indexPerProperty.TryGetValue(PropertyNameAtomic, out propertyIndex)) + { + return null; + } + + return new ObjectArrayPropertyGetterDefaultObjectArray(propertyIndex, null, eventAdapterService); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/util/EventPropertyRendererDefault.cs b/NEsper.Core/NEsper.Core/events/util/EventPropertyRendererDefault.cs new file mode 100755 index 000000000..ef2dfec18 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/util/EventPropertyRendererDefault.cs @@ -0,0 +1,21 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client.util; + +namespace com.espertech.esper.events.util +{ + public class EventPropertyRendererDefault : EventPropertyRenderer + { + public static EventPropertyRendererDefault INSTANCE = new EventPropertyRendererDefault(); + + public void Render(EventPropertyRendererContext context) + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/util/EventRendererImpl.cs b/NEsper.Core/NEsper.Core/events/util/EventRendererImpl.cs new file mode 100755 index 000000000..d99ef3fab --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/util/EventRendererImpl.cs @@ -0,0 +1,154 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.client.util; + +namespace com.espertech.esper.events.util +{ + /// + /// Provider for rendering services of events. + /// + public class EventRendererImpl : EventRenderer + { + /// + /// Returns a render for the JSON format, valid only for the given event type and + /// its subtypes. + /// + /// to return renderer for + /// rendering options + /// + /// JSON format renderer + /// + public JSONEventRenderer GetJSONRenderer(EventType eventType, JSONRenderingOptions options) + { + return new JSONRendererImpl(eventType, options); + } + + /// + /// Returns a render for the JSON format, valid only for the given event type and + /// its subtypes. + /// + /// to return renderer for + /// + /// JSON format renderer + /// + public JSONEventRenderer GetJSONRenderer(EventType eventType) + { + return new JSONRendererImpl(eventType, new JSONRenderingOptions()); + } + + /// + /// Quick-access method to render a given event in the JSON format. + /// + /// Use the #getJSONRenderer to obtain a renderer instance that allows repeated + /// rendering of the same type of event. For performance reasons obtaining a dedicated + /// renderer instance is the preferred method compared to repeated rendering via this + /// method. + /// + /// the JSON root title + /// the event to render + /// + /// JSON formatted text + /// + public String RenderJSON(String title, EventBean theEvent) + { + return RenderJSON(title, theEvent, new JSONRenderingOptions()); + } + + /// + /// Quick-access method to render a given event in the JSON format. + /// + /// Use the #getJSONRenderer to obtain a renderer instance that allows repeated + /// rendering of the same type of event. For performance reasons obtaining a dedicated + /// renderer instance is the preferred method compared to repeated rendering via this + /// method. + /// + /// the JSON root title + /// the event to render + /// are JSON rendering options + /// + /// JSON formatted text + /// + public String RenderJSON(String title, EventBean theEvent, JSONRenderingOptions options) + { + if (theEvent == null) + { + return null; + } + return GetJSONRenderer(theEvent.EventType, options).Render(title, theEvent); + } + + /// + /// Returns a render for the XML format, valid only for the given event type and its + /// subtypes. + /// + /// to return renderer for + /// + /// XML format renderer + /// + public XMLEventRenderer GetXMLRenderer(EventType eventType) + { + return new XMLRendererImpl(eventType, new XMLRenderingOptions()); + } + + /// + /// Returns a render for the XML format, valid only for the given event type and its + /// subtypes. + /// + /// to return renderer for + /// rendering options + /// + /// XML format renderer + /// + public XMLEventRenderer GetXMLRenderer(EventType eventType, XMLRenderingOptions options) + { + return new XMLRendererImpl(eventType, options); + } + + /// + /// Quick-access method to render a given event in the XML format. + /// + /// Use the #getXMLRenderer to obtain a renderer instance that allows repeated + /// rendering of the same type of event. For performance reasons obtaining a dedicated + /// renderer instance is the preferred method compared to repeated rendering via this + /// method. + /// + /// the root element name that may also include namespace information + /// the event to render + /// + /// XML formatted text + /// + public String RenderXML(String rootElementName, EventBean theEvent) + { + return RenderXML(rootElementName, theEvent, new XMLRenderingOptions()); + } + + /// + /// Quick-access method to render a given event in the XML format. + /// + /// Use the #getXMLRenderer to obtain a renderer instance that allows repeated + /// rendering of the same type of event. For performance reasons obtaining a dedicated + /// renderer instance is the preferred method compared to repeated rendering via this + /// method. + /// + /// the root element name that may also include namespace information + /// the event to render + /// are XML rendering options + /// + /// XML formatted text + /// + public String RenderXML(String rootElementName, EventBean theEvent, XMLRenderingOptions options) + { + return theEvent != null ? GetXMLRenderer(theEvent.EventType, options).Render(rootElementName, theEvent) : null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/util/EventTypePropertyPair.cs b/NEsper.Core/NEsper.Core/events/util/EventTypePropertyPair.cs new file mode 100755 index 000000000..b1c7006fc --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/util/EventTypePropertyPair.cs @@ -0,0 +1,72 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.util +{ + /// + /// Pair of event type and property. + /// + public class EventTypePropertyPair + { + private readonly String propertyName; + private readonly EventType eventType; + + /// + /// Ctor. + /// + /// event type + /// property + public EventTypePropertyPair(EventType eventType, String propertyName) + { + this.eventType = eventType; + this.propertyName = propertyName; + } + + public bool Equals(EventTypePropertyPair obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + return Equals(obj.propertyName, propertyName) && Equals(obj.eventType, eventType); + } + + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// + /// true if the specified is equal to the current ; otherwise, false. + /// + /// + /// The parameter is null. + /// + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != typeof (EventTypePropertyPair)) return false; + return Equals((EventTypePropertyPair) obj); + } + + /// + /// Serves as a hash function for a particular type. + /// + /// + /// A hash code for the current . + /// + public override int GetHashCode() + { + unchecked { + return ((propertyName != null ? propertyName.GetHashCode() : 0)*397) ^ (eventType != null ? eventType.GetHashCode() : 0); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/util/GetterPair.cs b/NEsper.Core/NEsper.Core/events/util/GetterPair.cs new file mode 100755 index 000000000..612aae43c --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/util/GetterPair.cs @@ -0,0 +1,57 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.util +{ + /// + /// Value-object for rendering support of a simple property value (non-nested). + /// + public class GetterPair + { + /// + /// Ctor. + /// + /// for retrieving the value + /// property name + /// for rendering the getter result + public GetterPair(EventPropertyGetter getter, String name, OutputValueRenderer output) + { + Getter = getter; + Name = name; + Output = output; + } + + /// + /// Returns the getter. + /// + /// + /// getter + /// + public EventPropertyGetter Getter { get; private set; } + + /// + /// Returns the property name. + /// + /// + /// property name + /// + public string Name { get; private set; } + + /// + /// Returns the renderer for the getter return value. + /// + /// + /// renderer for result value + /// + public OutputValueRenderer Output { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/events/util/JSONRendererImpl.cs b/NEsper.Core/NEsper.Core/events/util/JSONRendererImpl.cs new file mode 100755 index 000000000..5356e6e64 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/util/JSONRendererImpl.cs @@ -0,0 +1,360 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using com.espertech.esper.client; +using com.espertech.esper.client.util; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.compat.magic; + +namespace com.espertech.esper.events.util +{ + using Map = IDictionary; + + /// RenderAny for the JSON format. + public class JSONRendererImpl : JSONEventRenderer + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly static String NEWLINE = Environment.NewLine; + private readonly static String COMMA_DELIMITER_NEWLINE = "," + NEWLINE; + + private readonly RendererMeta _meta; + private readonly RendererMetaOptions _rendererOptions; + + /// Ctor. + /// type of Event(s) + /// rendering options + public JSONRendererImpl(EventType eventType, JSONRenderingOptions options) + { + EventPropertyRenderer propertyRenderer = null; + EventPropertyRendererContext propertyRendererContext = null; + if (options.Renderer != null) + { + propertyRenderer = options.Renderer; + propertyRendererContext = new EventPropertyRendererContext(eventType, true); + } + + _rendererOptions = new RendererMetaOptions(options.PreventLooping, false, propertyRenderer, propertyRendererContext); + _meta = new RendererMeta(eventType, new Stack(), _rendererOptions); + } + + public String Render(String title, EventBean theEvent) + { + var buf = new StringBuilder(); + buf.Append('{'); + buf.Append(NEWLINE); + + Ident(buf, 1); + buf.Append('\"'); + buf.Append(title); + buf.Append("\": {"); + buf.Append(NEWLINE); + + RecursiveRender(theEvent, buf, 2, _meta, _rendererOptions); + + Ident(buf, 1); + buf.Append('}'); + buf.Append(NEWLINE); + + buf.Append('}'); + buf.Append(NEWLINE); + + return buf.ToString(); + } + + public String Render(EventBean theEvent) + { + StringBuilder buf = new StringBuilder(); + buf.Append('{'); + RecursiveRender(theEvent, buf, 2, _meta, _rendererOptions); + buf.Append('}'); + return buf.ToString(); + } + + private static void Ident(StringBuilder buf, int level) + { + for (int i = 0; i < level; i++) + { + IndentChar(buf); + } + } + + private static void IndentChar(StringBuilder buf) + { + buf.Append(' '); + buf.Append(' '); + } + + private static void RecursiveRender(EventBean theEvent, StringBuilder buf, int level, RendererMeta meta, RendererMetaOptions rendererOptions) + { + String delimiter = ""; + + // simple properties + GetterPair[] simpleProps = meta.SimpleProperties; + if (rendererOptions.Renderer == null) + { + foreach (GetterPair simpleProp in simpleProps.OrderBy(prop => prop.Name)) + { + Object value = simpleProp.Getter.Get(theEvent); + WriteDelimitedIndentedProp(buf, delimiter, level, simpleProp.Name); + simpleProp.Output.Render(value, buf); + delimiter = COMMA_DELIMITER_NEWLINE; + } + } + else + { + EventPropertyRendererContext context = rendererOptions.RendererContext; + context.SetStringBuilderAndReset(buf); + foreach (GetterPair simpleProp in simpleProps.OrderBy(prop => prop.Name)) + { + Object value = simpleProp.Getter.Get(theEvent); + WriteDelimitedIndentedProp(buf, delimiter, level, simpleProp.Name); + context.DefaultRenderer = simpleProp.Output; + context.PropertyName = simpleProp.Name; + context.PropertyValue = value; + rendererOptions.Renderer.Render(context); + delimiter = COMMA_DELIMITER_NEWLINE; + } + } + + GetterPair[] indexProps = meta.IndexProperties; + foreach (GetterPair indexProp in indexProps.OrderBy(prop => prop.Name)) + { + WriteDelimitedIndentedProp(buf, delimiter, level, indexProp.Name); + + var value = indexProp.Getter.Get(theEvent); + if (value == null) + { + buf.Append("null"); + } + else if (value is string) + { + if (rendererOptions.Renderer == null) + { + indexProp.Output.Render(value, buf); + } + else + { + EventPropertyRendererContext context = rendererOptions.RendererContext; + context.SetStringBuilderAndReset(buf); + context.DefaultRenderer = indexProp.Output; + context.PropertyName = indexProp.Name; + context.PropertyValue = value; + rendererOptions.Renderer.Render(context); + } + } + else + { + var asArray = value as Array; + if (asArray == null) + { + buf.Append("[]"); + } + else + { + buf.Append('['); + + var arrayDelimiter = ""; + + if (rendererOptions.Renderer == null) + { + for (int i = 0; i < asArray.Length; i++) + { + var arrayItem = asArray.GetValue(i); + buf.Append(arrayDelimiter); + indexProp.Output.Render(arrayItem, buf); + arrayDelimiter = ", "; + } + } + else + { + EventPropertyRendererContext context = rendererOptions.RendererContext; + context.SetStringBuilderAndReset(buf); + for (int i = 0; i < asArray.Length; i++) + { + Object arrayItem = asArray.GetValue(i); + buf.Append(arrayDelimiter); + context.PropertyName = indexProp.Name; + context.PropertyValue = arrayItem; + context.IndexedPropertyIndex = i; + context.DefaultRenderer = indexProp.Output; + rendererOptions.Renderer.Render(context); + arrayDelimiter = ", "; + } + } + buf.Append(']'); + } + } + delimiter = COMMA_DELIMITER_NEWLINE; + } + + GetterPair[] mappedProps = meta.MappedProperties; + foreach (GetterPair mappedProp in mappedProps.OrderBy(prop => prop.Name)) + { + var value = mappedProp.Getter.Get(theEvent); + + if ((value != null) && (!(value.GetType().IsGenericStringDictionary()))) + { + Log.Warn("Property '" + mappedProp.Name + "' expected to return Map and returned " + value.GetType() + " instead"); + continue; + } + + WriteDelimitedIndentedProp(buf, delimiter, level, mappedProp.Name); + + if (value == null) + { + buf.Append("null"); + buf.Append(NEWLINE); + } + else + { + var map = MagicMarker.GetStringDictionary(value); + if (map.IsEmpty()) + { + buf.Append("{}"); + buf.Append(NEWLINE); + } + else + { + buf.Append('{'); + buf.Append(NEWLINE); + + var localDelimiter = ""; + foreach (var entry in map) + { + if (entry.Key == null) + { + continue; + } + + buf.Append(localDelimiter); + Ident(buf, level + 1); + buf.Append('\"'); + buf.Append(entry.Key); + buf.Append("\": "); + + if (entry.Value == null) + { + buf.Append("null"); + } + else + { + OutputValueRenderer outRenderer = OutputValueRendererFactory.GetOutputValueRenderer(entry.Value.GetType(), rendererOptions); + if (rendererOptions.Renderer == null) + { + outRenderer.Render(entry.Value, buf); + } + else + { + EventPropertyRendererContext context = rendererOptions.RendererContext; + context.SetStringBuilderAndReset(buf); + context.PropertyName = mappedProp.Name; + context.PropertyValue = entry.Value; + context.MappedPropertyKey = entry.Key; + context.DefaultRenderer = outRenderer; + rendererOptions.Renderer.Render(context); + } + } + localDelimiter = COMMA_DELIMITER_NEWLINE; + } + + buf.Append(NEWLINE); + Ident(buf, level); + buf.Append('}'); + } + } + + delimiter = COMMA_DELIMITER_NEWLINE; + } + + NestedGetterPair[] nestedProps = meta.NestedProperties; + foreach (NestedGetterPair nestedProp in nestedProps.OrderBy(prop => prop.Name)) + { + var value = nestedProp.Getter.GetFragment(theEvent); + + WriteDelimitedIndentedProp(buf, delimiter, level, nestedProp.Name); + + if (value == null) + { + buf.Append("null"); + } + else if (!nestedProp.IsArray) + { + if (!(value is EventBean)) + { + Log.Warn("Property '" + nestedProp.Name + "' expected to return EventBean and returned " + value.GetType() + " instead"); + buf.Append("null"); + continue; + } + var nestedEventBean = (EventBean)value; + buf.Append('{'); + buf.Append(NEWLINE); + + RecursiveRender(nestedEventBean, buf, level + 1, nestedProp.Metadata, rendererOptions); + + Ident(buf, level); + buf.Append('}'); + } + else + { + if (!(value is EventBean[])) + { + Log.Warn("Property '" + nestedProp.Name + "' expected to return EventBean[] and returned " + value.GetType() + " instead"); + buf.Append("null"); + continue; + } + + + StringBuilder arrayDelimiterBuf = new StringBuilder(); + arrayDelimiterBuf.Append(','); + arrayDelimiterBuf.Append(NEWLINE); + Ident(arrayDelimiterBuf, level + 1); + + EventBean[] nestedEventArray = (EventBean[])value; + String arrayDelimiter = ""; + buf.Append('['); + + for (int i = 0; i < nestedEventArray.Length; i++) + { + EventBean arrayItem = nestedEventArray[i]; + buf.Append(arrayDelimiter); + arrayDelimiter = arrayDelimiterBuf.ToString(); + + buf.Append('{'); + buf.Append(NEWLINE); + + RecursiveRender(arrayItem, buf, level + 2, nestedProp.Metadata, rendererOptions); + + Ident(buf, level + 1); + buf.Append('}'); + } + buf.Append(']'); + } + delimiter = COMMA_DELIMITER_NEWLINE; + } + + buf.Append(NEWLINE); + } + + private static void WriteDelimitedIndentedProp(StringBuilder buf, String delimiter, int level, String name) + { + buf.Append(delimiter); + Ident(buf, level); + buf.Append('\"'); + buf.Append(name); + buf.Append("\": "); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/util/NestedGetterPair.cs b/NEsper.Core/NEsper.Core/events/util/NestedGetterPair.cs new file mode 100755 index 000000000..ba794736e --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/util/NestedGetterPair.cs @@ -0,0 +1,84 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.util +{ + /// + /// Value-object for rendering support of a nested property value. + /// + public class NestedGetterPair + { + private readonly String name; + private readonly EventPropertyGetter getter; + private readonly RendererMeta metadata; + private readonly bool isArray; + + /// + /// Ctor. + /// + /// for retrieving the value + /// property name + /// the nested properties metadata + /// indicates whether this is an indexed property + public NestedGetterPair(EventPropertyGetter getter, String name, RendererMeta metadata, bool isArray) + { + this.getter = getter; + this.name = name; + this.metadata = metadata; + this.isArray = isArray; + } + + /// + /// Returns the getter. + /// + /// + /// getter + /// + public EventPropertyGetter Getter + { + get { return getter; } + } + + /// + /// Returns the property name. + /// + /// + /// property name + /// + public string Name + { + get { return name; } + } + + /// + /// Returns the nested property's metadata. + /// + /// + /// metadata + /// + public RendererMeta Metadata + { + get { return metadata; } + } + + /// + /// Returns true if an indexed nested property. + /// + /// + /// indicator whether indexed + /// + public bool IsArray + { + get { return isArray; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/util/OutputValueRenderer.cs b/NEsper.Core/NEsper.Core/events/util/OutputValueRenderer.cs new file mode 100755 index 000000000..ccab11f3e --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/util/OutputValueRenderer.cs @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Text; + +namespace com.espertech.esper.events.util +{ + /// + /// For rendering an output value returned by a property. + /// + public interface OutputValueRenderer + { + /// + /// Renders the value to the buffer. + /// + /// object to render + /// buffer to populate + void Render(Object o, StringBuilder buf); + } +} diff --git a/NEsper.Core/NEsper.Core/events/util/OutputValueRendererBase.cs b/NEsper.Core/NEsper.Core/events/util/OutputValueRendererBase.cs new file mode 100755 index 000000000..e0e2320fe --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/util/OutputValueRendererBase.cs @@ -0,0 +1,89 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Text; + +namespace com.espertech.esper.events.util +{ + /// + /// Renderer for a Object values that can simply be output via to-string. + /// + public class OutputValueRendererBase : OutputValueRenderer + { + public void Render(Object o, StringBuilder buf) + { + if (o == null) + { + buf.Append("null"); + return; + } + + if ((o is decimal) || + (o is decimal?) || + (o is double) || + (o is double?) || + (o is float) || + (o is float?)) + { + var text = o.ToString(); + buf.Append(text); + if (text.IndexOf('.') == -1) + { + buf.Append(".0"); + } + } + else if (o is DateTimeOffset) + { + var dateTime = (DateTimeOffset)o; + var dateOnly = dateTime.Date; + if (dateTime == dateOnly) + { + buf.Append(dateTime.ToString("yyyy-MM-dd")); + } + else if (dateTime.Millisecond == 0) + { + buf.Append(dateTime.ToString("yyyy-MM-dd hh:mm:ss")); + } + else + { + buf.Append(dateTime.ToString("yyyy-MM-dd hh:mm:ss.ffff")); + } + } + else if (o is DateTime) + { + var dateTime = (DateTime) o; + var dateOnly = dateTime.Date; + if (dateTime == dateOnly) + { + buf.Append(dateTime.ToString("yyyy-MM-dd")); + } + else if (dateTime.Millisecond == 0) + { + buf.Append(dateTime.ToString("yyyy-MM-dd hh:mm:ss")); + } + else + { + buf.Append(dateTime.ToString("yyyy-MM-dd hh:mm:ss.ffff")); + } + } + else if (o is bool) + { + buf.Append(o.ToString().ToLowerInvariant()); + } + else if (o.GetType().IsEnum) + { + buf.AppendFormat("{0}", o); + } + else + { + buf.Append(o.ToString()); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/util/OutputValueRendererFactory.cs b/NEsper.Core/NEsper.Core/events/util/OutputValueRendererFactory.cs new file mode 100755 index 000000000..69ddf87c1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/util/OutputValueRendererFactory.cs @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.events.util +{ + /// For rendering an output value returned by a property. + public class OutputValueRendererFactory + { + private static readonly OutputValueRenderer JsonStringOutput = new OutputValueRendererJSONString(); + private static readonly OutputValueRenderer XmlStringOutput = new OutputValueRendererXMLString(); + private static readonly OutputValueRenderer JsonEnumOutput = new OutputValueRendererJSONString(); + private static readonly OutputValueRenderer BaseOutput = new OutputValueRendererBase(); + + /// Returns a renderer for an output value. + /// to render + /// options + /// renderer + protected internal static OutputValueRenderer GetOutputValueRenderer(Type type, RendererMetaOptions options) + { + if (type.IsArray) + { + type = type.GetElementType(); + } + if ((type == typeof(string)) || (type == typeof(char?)) || (type == typeof(char)) || type.IsEnum) + { + return options.IsXmlOutput ? XmlStringOutput : JsonStringOutput; + } + if (type.IsEnum || (type.IsNullable() && Nullable.GetUnderlyingType(type).IsEnum)) + { + return options.IsXmlOutput ? BaseOutput : JsonEnumOutput; + } + else + { + return BaseOutput; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/util/OutputValueRendererJSONEnum.cs b/NEsper.Core/NEsper.Core/events/util/OutputValueRendererJSONEnum.cs new file mode 100755 index 000000000..b19b17b27 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/util/OutputValueRendererJSONEnum.cs @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Text; + +namespace com.espertech.esper.events.util +{ + /// + /// Renderer for a String-value into JSON enum. + /// + public class OutputValueRendererJSONEnum : OutputValueRenderer + { + public void Render(Object o, StringBuilder buf) + { + buf.Append('"'); + OutputValueRendererJSONString.Enquote(o.ToString(), buf); + buf.Append('"'); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/util/OutputValueRendererJSONString.cs b/NEsper.Core/NEsper.Core/events/util/OutputValueRendererJSONString.cs new file mode 100755 index 000000000..4e60b4fcf --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/util/OutputValueRendererJSONString.cs @@ -0,0 +1,94 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Text; + +namespace com.espertech.esper.events.util +{ + /// + /// Renderer for a String-value into JSON strings. + /// + public class OutputValueRendererJSONString : OutputValueRenderer + { + public void Render(Object o, StringBuilder buf) + { + if (o == null) + { + buf.Append("null"); + return; + } + + Enquote(o.ToString(), buf); + } + + /// + /// JSON-Enquote the passed string. + /// + /// string to enqoute + /// buffer to populate + public static void Enquote(String s, StringBuilder sb) + { + if (string.IsNullOrEmpty(s)) + { + sb.Append("\"\""); + return; + } + + char c; + int i; + int len = s.Length; + String t; + + sb.Append('"'); + for (i = 0; i < len; i += 1) + { + c = s[i]; + if ((c == '\\') || (c == '"')) + { + sb.Append('\\'); + sb.Append(c); + } + else if (c == '\b') + { + sb.Append("\\b"); + } + else if (c == '\t') + { + sb.Append("\\t"); + } + else if (c == '\n') + { + sb.Append("\\n"); + } + else if (c == '\f') + { + sb.Append("\\f"); + } + else if (c == '\r') + { + sb.Append("\\r"); + } + else + { + if (c < ' ') + { + t = "000" + ((short) c).ToString("X2"); + sb.Append("\\u"); + sb.Append(t.Substring(t.Length - 4)); + } + else + { + sb.Append(c); + } + } + } + sb.Append('"'); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/util/OutputValueRendererXMLString.cs b/NEsper.Core/NEsper.Core/events/util/OutputValueRendererXMLString.cs new file mode 100755 index 000000000..1aee9d399 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/util/OutputValueRendererXMLString.cs @@ -0,0 +1,89 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Text; + +namespace com.espertech.esper.events.util +{ + /// + /// Renderer for a String-value into XML strings. + /// + public class OutputValueRendererXMLString : OutputValueRenderer + { + public void Render(Object o, StringBuilder buf) + { + if (o == null) + { + buf.Append("null"); + return; + } + + XmlEncode(o.ToString(), buf, true); + } + + /// + /// XML-Encode the passed string. + /// + /// string to encode + /// string buffer to populate + /// true for encoding of special characters below ' ', false for leaving special chars + public static void XmlEncode(String s, StringBuilder sb, bool isEncodeSpecialChar) + { + if (string.IsNullOrEmpty(s)) + { + return; + } + + char c; + int i; + int len = s.Length; + String t; + + for (i = 0; i < len; i += 1) + { + c = s[i]; + // replace literal values with entities + + if (c == '&') + { + sb.Append("&"); + } + else if (c == '<') + { + sb.Append("<"); + } + else if (c == '>') + { + sb.Append(">"); + } + else if (c == '\'') + { + sb.Append("'"); + } + else if (c == '\"') + { + sb.Append("""); + } + else + { + if ((c < ' ') && (isEncodeSpecialChar)) + { + t = "000" + ((short)c).ToString("x2"); + sb.Append("\\u"); + sb.Append(t.Substring(t.Length - 4)); + } + else + { + sb.Append(c); + } + } + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/util/RendererMeta.cs b/NEsper.Core/NEsper.Core/events/util/RendererMeta.cs new file mode 100755 index 000000000..19bd8cc9f --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/util/RendererMeta.cs @@ -0,0 +1,178 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.logging; + +namespace com.espertech.esper.events.util +{ + /// + /// Renderer cache for event type metadata allows fast rendering of a given type of + /// events. + /// + public class RendererMeta + { + private static readonly ILog log = + LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly GetterPair[] simpleProperties; + private readonly GetterPair[] indexProperties; + private readonly GetterPair[] mappedProperties; + private readonly NestedGetterPair[] nestedProperties; + + /// + /// Ctor. + /// + /// to render + /// the stack of properties to avoid looping + /// rendering options + public RendererMeta(EventType eventType, Stack stack, RendererMetaOptions options) + { + var gettersSimple = new List(); + var gettersIndexed = new List(); + var gettersMapped = new List(); + var gettersNested = new List(); + + var descriptors = eventType.PropertyDescriptors; + foreach (EventPropertyDescriptor desc in descriptors) + { + String propertyName = desc.PropertyName; + + if ((!desc.IsIndexed) && (!desc.IsMapped) && (!desc.IsFragment)) + { + var getter = eventType.GetGetter(propertyName); + if (getter == null) + { + log.Warn("No getter returned for event type '" + eventType.Name + "' and property '" + + propertyName + "'"); + continue; + } + gettersSimple.Add(new GetterPair(getter, propertyName, + OutputValueRendererFactory.GetOutputValueRenderer( + desc.PropertyType, options))); + } + + if (desc.IsIndexed && !desc.RequiresIndex && (!desc.IsFragment)) + { + var getter = eventType.GetGetter(propertyName); + if (getter == null) + { + log.Warn("No getter returned for event type '" + eventType.Name + "' and property '" + + propertyName + "'"); + continue; + } + gettersIndexed.Add(new GetterPair(getter, propertyName, + OutputValueRendererFactory.GetOutputValueRenderer( + desc.PropertyType, options))); + } + + if (desc.IsMapped && !desc.RequiresMapKey && (!desc.IsFragment)) + { + var getter = eventType.GetGetter(propertyName); + if (getter == null) + { + log.Warn("No getter returned for event type '" + eventType.Name + "' and property '" + + propertyName + "'"); + continue; + } + gettersMapped.Add(new GetterPair(getter, propertyName, + OutputValueRendererFactory.GetOutputValueRenderer( + desc.PropertyType, options))); + } + + if (desc.IsFragment) + { + var getter = eventType.GetGetter(propertyName); + var fragmentType = eventType.GetFragmentType(propertyName); + if (getter == null) + { + log.Warn("No getter returned for event type '" + eventType.Name + "' and property '" + + propertyName + "'"); + continue; + } + if (fragmentType == null) + { + log.Warn("No fragment type returned for event type '" + eventType.Name + "' and property '" + + propertyName + "'"); + continue; + } + + var pair = new EventTypePropertyPair(fragmentType.FragmentType, propertyName); + if ((options.PreventLooping && stack.Contains(pair))) + { + continue; // prevent looping behavior on self-references + } + + stack.Push(pair); + var fragmentMetaData = new RendererMeta(fragmentType.FragmentType, stack, options); + stack.Pop(); + + gettersNested.Add(new NestedGetterPair(getter, propertyName, fragmentMetaData, + fragmentType.IsIndexed)); + } + } + + gettersSimple.Sort((gp1, gp2) => gp1.Name.CompareTo(gp2.Name)); + gettersIndexed.Sort((gp1, gp2) => gp1.Name.CompareTo(gp2.Name)); + gettersMapped.Sort((gp1, gp2) => gp1.Name.CompareTo(gp2.Name)); + gettersNested.Sort((gp1, gp2) => gp1.Name.CompareTo(gp2.Name)); + + simpleProperties = gettersSimple.ToArray(); + indexProperties = gettersIndexed.ToArray(); + mappedProperties = gettersMapped.ToArray(); + nestedProperties = gettersNested.ToArray(); + } + + /// + /// Returns simple properties. + /// + /// + /// properties + /// + public GetterPair[] SimpleProperties + { + get { return simpleProperties; } + } + + /// + /// Returns index properties. + /// + /// + /// properties + /// + public GetterPair[] IndexProperties + { + get { return indexProperties; } + } + + /// + /// Returns nested properties. + /// + /// + /// properties + /// + public NestedGetterPair[] NestedProperties + { + get { return nestedProperties; } + } + + /// + /// Returns mapped properties. + /// + /// + /// mapped props + /// + public GetterPair[] MappedProperties + { + get { return mappedProperties; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/util/RendererMetaOptions.cs b/NEsper.Core/NEsper.Core/events/util/RendererMetaOptions.cs new file mode 100755 index 000000000..b205823f4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/util/RendererMetaOptions.cs @@ -0,0 +1,64 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client.util; + +namespace com.espertech.esper.events.util +{ + /// + /// Options for use by with rendering metadata. + /// + public class RendererMetaOptions + { + /// + /// Ctor. + /// + /// true to prevent looping + /// true for XML output + /// The renderer. + /// The renderer context. + public RendererMetaOptions(bool preventLooping, + bool isXmlOutput, + EventPropertyRenderer renderer, + EventPropertyRendererContext rendererContext) + { + PreventLooping = preventLooping; + IsXmlOutput = isXmlOutput; + Renderer = renderer; + RendererContext = rendererContext; + } + + /// + /// Returns true to prevent looping. + /// + /// + /// prevent looping indicator + /// + public bool PreventLooping { get; private set; } + + /// + /// Returns true for XML output. + /// + /// + /// XML output flag + /// + public bool IsXmlOutput { get; private set; } + + /// + /// Gets or sets the renderer. + /// + /// The renderer. + public EventPropertyRenderer Renderer { get; private set; } + + /// + /// Gets or sets the renderer context. + /// + /// The renderer context. + public EventPropertyRendererContext RendererContext { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/events/util/XElementRendererImpl.cs b/NEsper.Core/NEsper.Core/events/util/XElementRendererImpl.cs new file mode 100755 index 000000000..2e8964418 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/util/XElementRendererImpl.cs @@ -0,0 +1,463 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Xml.Linq; +using System.Xml.Schema; +using com.espertech.esper.client; +using com.espertech.esper.client.util; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.util; + +namespace com.espertech.esper.events.util +{ + using DataMap = IDictionary; + + public class XElementRendererImpl + { + private readonly RendererMeta _meta; + private readonly XMLRenderingOptions _options; + private readonly RendererMetaOptions _rendererMetaOptions; + + /// + /// Ctor. + /// + /// type of event to render + /// rendering options + public XElementRendererImpl(EventType eventType, XMLRenderingOptions options) + { + EventPropertyRenderer propertyRenderer = null; + EventPropertyRendererContext propertyRendererContext = null; + if (options.Renderer != null) + { + propertyRenderer = options.Renderer; + propertyRendererContext = new EventPropertyRendererContext(eventType, false); + } + + _rendererMetaOptions = new RendererMetaOptions(options.PreventLooping, true, propertyRenderer, propertyRendererContext); + _meta = new RendererMeta(eventType, new Stack(), _rendererMetaOptions); + _options = options; + } + + public XElement Render(String rootElementName, EventBean theEvent) + { + return _options.IsDefaultAsAttribute + ? RenderWithAttributes(rootElementName, theEvent) + : RenderWithElements(rootElementName, theEvent); + } + + private XElement RenderWithElements(String rootElementName, EventBean theEvent) + { + var element = new XElement( + rootElementName, + RenderRecursive(theEvent, _meta)); + return element; + } + + private XElement RenderWithAttributes(String rootElementName, EventBean theEvent) + { + var attributes = RenderAttributes(theEvent, _meta).Cast(); + var attributeElements = RenderAttributeElements(theEvent, _meta).Cast().ToArray(); + var element = new XElement( + rootElementName, + attributes.Concat(attributeElements).ToArray()); + + return element; + } + + private static IEnumerable RenderAttributeElements( + EventBean theEvent, RendererMeta meta) + { + var indexProps = meta.IndexProperties; + foreach (var indexProp in indexProps) { + var value = indexProp.Getter.Get(theEvent); + if ((value == null) || (value is string)) { + continue; + } + + var asArray = value as Array; + if (asArray == null) { + Log.Warn("Property '{0}' returned a non-array object", indexProp.Name); + continue; + } + + for (int ii = 0; ii < asArray.Length; ii++) { + var arrayItem = asArray.GetValue(ii); + if (arrayItem == null) { + continue; + } + + yield return new XElement(indexProp.Name, arrayItem); + } + } + + var mappedProps = meta.MappedProperties; + foreach (var mappedProp in mappedProps) { + var value = mappedProp.Getter.Get(theEvent); + if ((value != null) && (!(value is DataMap))) { + Log.Warn("Property '" + mappedProp.Name + "' expected to return Map and returned " + value.GetType() + + " instead"); + continue; + } + + var mapElementChildren = RenderDataMap(value).Cast().ToArray(); + var mapElement = new XElement(mappedProp.Name, mapElementChildren); + yield return mapElement; + } + + var nestedProps = meta.NestedProperties; + foreach (var nestedProp in nestedProps) { + var value = nestedProp.Getter.GetFragment(theEvent); + if (value == null) { + continue; + } + + if (!nestedProp.IsArray) { + if (!(value is EventBean)) { + Log.Warn("Property '{0}' expected to return EventBean and returned '{1}' instead", + nestedProp.Name, value.GetType()); + yield return new XElement("null"); + //buf.Append("null"); + continue; + } + + var nestedEventBean = (EventBean) value; + yield return RenderAttributeInner(nestedEventBean, nestedProp); + } + else { + if (!(value is EventBean[])) { + Log.Warn("Property '{0}' expected to return EventBean[] and returned '{1}' instead", + nestedProp.Name, value.GetType()); + yield return new XElement("null"); + //buf.Append("null"); + continue; + } + + var nestedEventArray = (EventBean[]) value; + for (int ii = 0; ii < nestedEventArray.Length; ii++) { + var arrayItem = nestedEventArray[ii]; + yield return RenderAttributeInner(arrayItem, nestedProp); + } + } + } + } + + /// + /// Renders the value assuming that it is a data map. If it is not a data map or + /// if the data map is empty, then no elements are returned. + /// + /// The value. + /// + private static IEnumerable RenderDataMap(Object value) + { + if (value != null) + { + var map = (DataMap)value; + if (map.IsNotEmpty()) + { + foreach (var entry in map.Where(entry => entry.Key != null & entry.Value != null)) + { + yield return new XElement( + entry.Key, + entry.Value); + } + } + } + } + + private static IEnumerable RenderAttributes( + EventBean theEvent, RendererMeta meta) + { + var simpleProps = meta.SimpleProperties; + foreach (GetterPair simpleProp in simpleProps) { + var value = simpleProp.Getter.Get(theEvent); + if (value == null) { + continue; + } + + yield return new XAttribute(simpleProp.Name, value); + } + + var indexProps = meta.IndexProperties; + foreach (GetterPair indexProp in indexProps) { + var value = indexProp.Getter.Get(theEvent); + if (value == null) { + continue; + } + + var asString = value as string; + if (asString != null) { + yield return new XAttribute(indexProp.Name, value); + } + } + } + + /// + /// Renders the event recursively passing through the structure. + /// + /// The theEvent. + /// The meta. + /// + private static IEnumerable RenderRecursive( + EventBean theEvent, RendererMeta meta) + { + return RenderSimpleProperties(theEvent, meta) + .Concat(RenderIndexProperties(theEvent, meta)) + .Concat(RenderMappedProperties(theEvent, meta)) + .Concat(RenderNestedProperties(theEvent, meta)); + } + + /// + /// Renders the nested properties. + /// + /// The theEvent. + /// The meta. + /// + private static IEnumerable RenderNestedProperties( + EventBean theEvent, RendererMeta meta) + { + NestedGetterPair[] nestedProps = meta.NestedProperties; + foreach (NestedGetterPair nestedProp in nestedProps) { + var value = nestedProp.Getter.GetFragment(theEvent); + if (value == null) { + continue; + } + + if (!nestedProp.IsArray) + { + if (!(value is EventBean)) + { + Log.Warn("Property '{0}' expected to return EventBean and returned {1} instead", + nestedProp.Name, value.GetType()); + yield return new XElement("null"); + } + else + { + yield return RenderElementFragment((EventBean) value, nestedProp); + } + } + else + { + if (!(value is EventBean[])) + { + Log.Warn("Property '{0}' expected to return EventBean[] and returned {1} instead", + nestedProp.Name, value.GetType()); + yield return new XElement("null"); + } + else + { + var nestedEventArray = (EventBean[])value; + foreach (EventBean arrayItem in nestedEventArray.Where(arrayItem => arrayItem != null)) + { + yield return RenderElementFragment(arrayItem, nestedProp); + } + } + } + } + } + + /// + /// Renders the simple properties. + /// + /// The theEvent. + /// The meta. + /// + private static IEnumerable RenderSimpleProperties( + EventBean theEvent, RendererMeta meta) + { + var simpleProps = meta.SimpleProperties; + foreach (GetterPair simpleProp in simpleProps) + { + var value = simpleProp.Getter.Get(theEvent); + if (value != null) + { + var valueType = value.GetType(); + var valueTypeCode = GetTypeCode(valueType); + yield return new XElement( + simpleProp.Name, + new XAttribute("type", valueTypeCode), + value); + } + } + } + + /// + /// Renders the index properties. + /// + /// The theEvent. + /// The meta. + /// + private static IEnumerable RenderIndexProperties( + EventBean theEvent, RendererMeta meta) + { + GetterPair[] indexProps = meta.IndexProperties; + foreach (GetterPair indexProp in indexProps) { + var value = indexProp.Getter.Get(theEvent); + if (value == null) { + continue; + } + + if (value is string) { + yield return new XElement(indexProp.Name, new XAttribute("type", XmlTypeCode.String), value); + continue; + } + + var asArray = value as Array; + if (asArray == null) { + Log.Warn("Property '{0}' returned a non-array object", indexProp.Name); + continue; + } + + for (int ii = 0; ii < asArray.Length; ii++) { + var arrayItem = asArray.GetValue(ii); + if (arrayItem == null) { + continue; + } + + yield return new XElement(indexProp.Name, arrayItem); + } + } + } + + /// + /// Renders the mapped properties. + /// + /// The theEvent. + /// The meta. + /// + private static IEnumerable RenderMappedProperties( + EventBean theEvent, RendererMeta meta) + { + GetterPair[] mappedProps = meta.MappedProperties; + foreach (GetterPair mappedProp in mappedProps) + { + var value = mappedProp.Getter.Get(theEvent); + if ((value != null) && (!(value is DataMap))) + { + Log.Warn("Property '{0}' expected to return Map and returned {1} instead", + mappedProp.Name, value.GetType()); + continue; + } + + if (value != null) { + var map = (DataMap) value; + if (map.IsNotEmpty()) { + var mapElements = RenderMappedProperty(map).Cast().ToArray(); + yield return new XElement(mappedProp.Name, mapElements); + } else { + yield return new XElement(mappedProp.Name); + } + } else { + yield return new XElement(mappedProp.Name); + } + } + } + + /// + /// Renders the mapped property. + /// + /// The map. + /// + private static IEnumerable RenderMappedProperty(DataMap map) + { + foreach (var entry in map.Where(entry => entry.Key != null)) { + if (entry.Value == null) { + yield return new XElement(entry.Key); + } else { + yield return new XElement(entry.Key, entry.Value); + } + } + } + + private static XElement RenderElementFragment( + EventBean eventBean, NestedGetterPair nestedProp) + { + return new XElement( + nestedProp.Name, + RenderRecursive(eventBean, nestedProp.Metadata) + .Cast() + .ToArray()); + } + + private static XElement RenderAttributeInner(EventBean nestedEventBean, NestedGetterPair nestedProp) + { + var attributes = RenderAttributes( + nestedEventBean, nestedProp.Metadata).Cast(); + var attributeElements = RenderAttributeElements( + nestedEventBean, nestedProp.Metadata).Cast(); + var elementParts = attributes + .Concat(attributeElements) + .ToArray(); + + return new XElement( + nestedProp.Name, + elementParts); + } + + private static XmlTypeCode GetTypeCode(Type type) + { + type = type.GetBoxedType(); + + if (type == typeof(int?)) + { + return XmlTypeCode.Integer; + } + else if (type == typeof(long?)) + { + return XmlTypeCode.Long; + } + else if (type == typeof(short?)) + { + return XmlTypeCode.Short; + } + else if (type == typeof(byte?)) + { + return XmlTypeCode.Byte; + } + else if (type == typeof(float?)) + { + return XmlTypeCode.Float; + } + else if (type == typeof(double?)) + { + return XmlTypeCode.Double; + } + else if (type == typeof(decimal?)) + { + return XmlTypeCode.Decimal; + } + else if (type == typeof(bool?)) + { + return XmlTypeCode.Boolean; + } + else if (type == typeof(DateTime?)) + { + return XmlTypeCode.DateTime; + } + else if (type == typeof(DateTimeOffset?)) + { + return XmlTypeCode.DateTime; + } + else if (type == typeof(string)) + { + return XmlTypeCode.String; + } + else + { + return XmlTypeCode.None; + } + } + + private static readonly ILog Log = + LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + } +} diff --git a/NEsper.Core/NEsper.Core/events/util/XMLRendererImpl.cs b/NEsper.Core/NEsper.Core/events/util/XMLRendererImpl.cs new file mode 100755 index 000000000..3265e63dc --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/util/XMLRendererImpl.cs @@ -0,0 +1,666 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; + +using com.espertech.esper.client; +using com.espertech.esper.client.util; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; + +using DataMap = System.Collections.Generic.IDictionary; + +namespace com.espertech.esper.events.util +{ + /// + /// Renderer for XML-formatted properties. + /// + public class XMLRendererImpl : XMLEventRenderer + { + private static readonly ILog Log = + LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private static readonly String NEWLINE = Environment.NewLine; + + private readonly RendererMeta _meta; + private readonly XMLRenderingOptions _options; + private readonly RendererMetaOptions _rendererMetaOptions; + + /// + /// Ctor. + /// + /// type of event to render + /// rendering options + public XMLRendererImpl(EventType eventType, XMLRenderingOptions options) + { + EventPropertyRenderer propertyRenderer = null; + EventPropertyRendererContext propertyRendererContext = null; + if (options.Renderer != null) + { + propertyRenderer = options.Renderer; + propertyRendererContext = new EventPropertyRendererContext(eventType, false); + } + + _rendererMetaOptions = new RendererMetaOptions(options.PreventLooping, true, propertyRenderer, propertyRendererContext); + _meta = new RendererMeta(eventType, new Stack(), _rendererMetaOptions); + _options = options; + } + + #region XMLEventRenderer Members + + public String Render(String rootElementName, EventBean theEvent) + { + if (_options.IsDefaultAsAttribute) + { + return RenderAttributeXML(rootElementName, theEvent); + } + return RenderElementXML(rootElementName, theEvent); + } + + #endregion + + private String RenderElementXML(String rootElementName, EventBean theEvent) + { + var buf = new StringBuilder(); + + buf.Append(""); + buf.Append(NEWLINE); + + buf.Append('<'); + buf.Append(rootElementName); + buf.Append('>'); + buf.Append(NEWLINE); + + RecursiveRender(theEvent, buf, 1, _meta, _rendererMetaOptions); + + buf.Append("'); + + return buf.ToString(); + } + + private String RenderAttributeXML(String rootElementName, EventBean theEvent) + { + var buf = new StringBuilder(); + + buf.Append(""); + buf.Append(NEWLINE); + + buf.Append('<'); + buf.Append(rootElementName); + RenderAttributes(theEvent, buf, _meta); + + String inner = RenderAttElements(theEvent, 1, _meta); + + if ((inner == null) || (inner.Trim().Length == 0)) + { + buf.Append("/>"); + buf.Append(NEWLINE); + } + else + { + buf.Append(">"); + buf.Append(NEWLINE); + buf.Append(inner); + buf.Append("'); + } + + return buf.ToString(); + } + + private String RenderAttElements(EventBean theEvent, int level, RendererMeta meta) + { + var buf = new StringBuilder(); + + GetterPair[] indexProps = meta.IndexProperties; + foreach (GetterPair indexProp in indexProps.OrderBy(prop => prop.Name)) + { + var value = indexProp.Getter.Get(theEvent); + if (value == null) + { + continue; + } + + if (value is string) + { + continue; + } + + var asArray = value as Array; + if (asArray == null) + { + Log.Warn("Property '" + indexProp.Name + "' returned a non-array object"); + continue; + } + + for (int i = 0; i < asArray.Length; i++) + { + object arrayItem = asArray.GetValue(i); + if (arrayItem == null) + { + continue; + } + + Ident(buf, level); + buf.Append('<'); + buf.Append(indexProp.Name); + buf.Append('>'); + + if (_rendererMetaOptions.Renderer == null) + { + indexProp.Output.Render(arrayItem, buf); + } + else + { + EventPropertyRendererContext context = _rendererMetaOptions.RendererContext; + context.SetStringBuilderAndReset(buf); + context.PropertyName = indexProp.Name; + context.PropertyValue = arrayItem; + context.IndexedPropertyIndex = i; + context.DefaultRenderer = indexProp.Output; + _rendererMetaOptions.Renderer.Render(context); + } + + buf.Append("'); + buf.Append(NEWLINE); + } + } + + GetterPair[] mappedProps = meta.MappedProperties; + foreach (GetterPair mappedProp in mappedProps.OrderBy(prop => prop.Name)) + { + object value = mappedProp.Getter.Get(theEvent); + + if ((value != null) && (!(value is DataMap))) + { + Log.Warn("Property '" + mappedProp.Name + "' expected to return Map and returned " + value.GetType() + + " instead"); + continue; + } + + Ident(buf, level); + buf.Append('<'); + buf.Append(mappedProp.Name); + + if (value != null) + { + var map = (DataMap)value; + if (map.IsNotEmpty()) + { + foreach (var entry in map) + { + if ((entry.Key == null) || (entry.Value == null)) + { + continue; + } + + buf.Append(" "); + buf.Append(entry.Key); + buf.Append("=\""); + + OutputValueRenderer outputValueRenderer = + OutputValueRendererFactory.GetOutputValueRenderer(entry.Value.GetType(), _rendererMetaOptions); + + if (_rendererMetaOptions.Renderer == null) + { + outputValueRenderer.Render(entry.Value, buf); + } + else + { + EventPropertyRendererContext context = _rendererMetaOptions.RendererContext; + context.SetStringBuilderAndReset(buf); + context.PropertyName = mappedProp.Name; + context.PropertyValue = entry.Value; + context.MappedPropertyKey = entry.Key; + context.DefaultRenderer = outputValueRenderer; + _rendererMetaOptions.Renderer.Render(context); + } + + buf.Append("\""); + } + } + } + + buf.Append("/>"); + buf.Append(NEWLINE); + } + + NestedGetterPair[] nestedProps = meta.NestedProperties; + foreach (NestedGetterPair nestedProp in nestedProps.OrderBy(prop => prop.Name)) + { + object value = nestedProp.Getter.GetFragment(theEvent); + + if (value == null) + { + continue; + } + + if (!nestedProp.IsArray) + { + if (!(value is EventBean)) + { + Log.Warn("Property '" + nestedProp.Name + "' expected to return EventBean and returned " + + value.GetType() + " instead"); + buf.Append("null"); + continue; + } + var nestedEventBean = (EventBean)value; + RenderAttInner(buf, level, nestedEventBean, nestedProp); + } + else + { + if (!(value is EventBean[])) + { + Log.Warn("Property '" + nestedProp.Name + "' expected to return EventBean[] and returned " + + value.GetType() + " instead"); + buf.Append("null"); + continue; + } + + var nestedEventArray = (EventBean[])value; + for (int i = 0; i < nestedEventArray.Length; i++) + { + EventBean arrayItem = nestedEventArray[i]; + RenderAttInner(buf, level, arrayItem, nestedProp); + } + } + } + + return buf.ToString(); + } + + private void RenderAttributes(EventBean theEvent, StringBuilder buf, RendererMeta meta) + { + const string delimiter = " "; + GetterPair[] simpleProps = meta.SimpleProperties; + foreach (GetterPair simpleProp in simpleProps.OrderBy(prop => prop.Name)) + { + var value = simpleProp.Getter.Get(theEvent); + if (value == null) + { + continue; + } + + buf.Append(delimiter); + buf.Append(simpleProp.Name); + buf.Append("=\""); + + if (_rendererMetaOptions.Renderer == null) + { + simpleProp.Output.Render(value, buf); + } + else + { + EventPropertyRendererContext context = _rendererMetaOptions.RendererContext; + context.SetStringBuilderAndReset(buf); + context.PropertyName = simpleProp.Name; + context.PropertyValue = value; + context.DefaultRenderer = simpleProp.Output; + _rendererMetaOptions.Renderer.Render(context); + } + + buf.Append('"'); + } + + GetterPair[] indexProps = meta.IndexProperties; + foreach (GetterPair indexProp in indexProps.OrderBy(prop => prop.Name)) + { + var value = indexProp.Getter.Get(theEvent); + if (value == null) + { + continue; + } + + var asString = value as string; + if (asString != null) + { + buf.Append(delimiter); + buf.Append(indexProp.Name); + buf.Append("=\""); + + if (_rendererMetaOptions.Renderer == null) + { + indexProp.Output.Render(value, buf); + } + else + { + EventPropertyRendererContext context = _rendererMetaOptions.RendererContext; + context.SetStringBuilderAndReset(buf); + context.PropertyName = indexProp.Name; + context.PropertyValue = value; + context.DefaultRenderer = indexProp.Output; + _rendererMetaOptions.Renderer.Render(context); + } + + buf.Append('"'); + } + } + } + + private static void Ident(StringBuilder buf, int level) + { + for (int i = 0; i < level; i++) + { + IndentChar(buf); + } + } + + private static void IndentChar(StringBuilder buf) + { + buf.Append(' '); + buf.Append(' '); + } + + private static void RecursiveRender(EventBean theEvent, StringBuilder buf, int level, RendererMeta meta, + RendererMetaOptions rendererMetaOptions) + { + GetterPair[] simpleProps = meta.SimpleProperties; + foreach (GetterPair simpleProp in simpleProps.OrderBy(prop => prop.Name)) + { + var value = simpleProp.Getter.Get(theEvent); + if (value == null) + { + continue; + } + + Ident(buf, level); + buf.Append('<'); + buf.Append(simpleProp.Name); + buf.Append('>'); + + if (rendererMetaOptions.Renderer == null) + { + simpleProp.Output.Render(value, buf); + } + else + { + EventPropertyRendererContext context = rendererMetaOptions.RendererContext; + context.SetStringBuilderAndReset(buf); + context.PropertyName = simpleProp.Name; + context.PropertyValue = value; + context.DefaultRenderer = simpleProp.Output; + rendererMetaOptions.Renderer.Render(context); + } + + buf.Append("'); + buf.Append(NEWLINE); + } + + GetterPair[] indexProps = meta.IndexProperties; + foreach (GetterPair indexProp in indexProps.OrderBy(prop => prop.Name)) + { + object value = indexProp.Getter.Get(theEvent); + + if (value == null) + { + continue; + } + + if (value is string) + { + Ident(buf, level); + + buf.Append('<'); + buf.Append(indexProp.Name); + buf.Append('>'); + + if (rendererMetaOptions.Renderer == null) + { + indexProp.Output.Render(value, buf); + } + else + { + EventPropertyRendererContext context = rendererMetaOptions.RendererContext; + context.SetStringBuilderAndReset(buf); + context.PropertyName = indexProp.Name; + context.PropertyValue = value; + context.DefaultRenderer = indexProp.Output; + rendererMetaOptions.Renderer.Render(context); + } + + buf.Append("'); + buf.Append(NEWLINE); + + continue; + } + + var asArray = value as Array; + if (asArray == null) + { + Log.Warn("Property '" + indexProp.Name + "' returned a non-array object"); + continue; + } + + for (int i = 0; i < asArray.Length; i++) + { + object arrayItem = asArray.GetValue(i); + + if (arrayItem == null) + { + continue; + } + + Ident(buf, level); + buf.Append('<'); + buf.Append(indexProp.Name); + buf.Append('>'); + + if (rendererMetaOptions.Renderer == null) + { + indexProp.Output.Render(arrayItem, buf); + } + else + { + EventPropertyRendererContext context = rendererMetaOptions.RendererContext; + context.SetStringBuilderAndReset(buf); + context.PropertyName = indexProp.Name; + context.PropertyValue = arrayItem; + context.IndexedPropertyIndex = i; + context.DefaultRenderer = indexProp.Output; + rendererMetaOptions.Renderer.Render(context); + } + + buf.Append("'); + buf.Append(NEWLINE); + } + } + + GetterPair[] mappedProps = meta.MappedProperties; + foreach (GetterPair mappedProp in mappedProps.OrderBy(prop => prop.Name)) + { + object value = mappedProp.Getter.Get(theEvent); + + if ((value != null) && (!(value is DataMap))) + { + Log.Warn("Property '" + mappedProp.Name + "' expected to return Map and returned " + value.GetType() + + " instead"); + continue; + } + + Ident(buf, level); + buf.Append('<'); + buf.Append(mappedProp.Name); + buf.Append('>'); + buf.Append(NEWLINE); + + if (value != null) + { + var map = (DataMap)value; + if (map.IsNotEmpty()) + { + String localDelimiter = ""; + foreach (var entry in map) + { + if (entry.Key == null) + { + continue; + } + + buf.Append(localDelimiter); + Ident(buf, level + 1); + buf.Append('<'); + buf.Append(entry.Key); + buf.Append('>'); + + if (entry.Value != null) + { + OutputValueRenderer outputValueRenderer = OutputValueRendererFactory.GetOutputValueRenderer( + entry.Value.GetType(), rendererMetaOptions); + if (rendererMetaOptions.Renderer == null) + { + outputValueRenderer.Render(entry.Value, buf); + } + else + { + EventPropertyRendererContext context = rendererMetaOptions.RendererContext; + context.SetStringBuilderAndReset(buf); + context.PropertyName = mappedProp.Name; + context.PropertyValue = entry.Value; + context.MappedPropertyKey = entry.Key; + context.DefaultRenderer = outputValueRenderer; + rendererMetaOptions.Renderer.Render(context); + } + } + + buf.Append("'); + localDelimiter = NEWLINE; + } + } + } + + buf.Append(NEWLINE); + Ident(buf, level); + buf.Append("'); + buf.Append(NEWLINE); + } + + var nestedProps = meta.NestedProperties; + foreach (NestedGetterPair nestedProp in nestedProps.OrderBy(prop => prop.Name)) + { + var value = nestedProp.Getter.GetFragment(theEvent); + if (value == null) + { + continue; + } + + if (!nestedProp.IsArray) + { + if (!(value is EventBean)) + { + Log.Warn("Property '" + nestedProp.Name + "' expected to return EventBean and returned " + + value.GetType() + " instead"); + buf.Append("null"); + continue; + } + RenderElementFragment((EventBean)value, buf, level, nestedProp, rendererMetaOptions); + } + else + { + if (!(value is EventBean[])) + { + Log.Warn("Property '" + nestedProp.Name + "' expected to return EventBean[] and returned " + + value.GetType() + " instead"); + buf.Append("null"); + continue; + } + + var nestedEventArray = (EventBean[])value; + for (int i = 0; i < nestedEventArray.Length; i++) + { + EventBean arrayItem = nestedEventArray[i]; + if (arrayItem == null) + { + continue; + } + RenderElementFragment(arrayItem, buf, level, nestedProp, rendererMetaOptions); + } + } + } + } + + private static void RenderElementFragment(EventBean eventBean, StringBuilder buf, int level, + NestedGetterPair nestedProp, RendererMetaOptions rendererMetaOptions) + { + Ident(buf, level); + buf.Append('<'); + buf.Append(nestedProp.Name); + buf.Append('>'); + buf.Append(NEWLINE); + + RecursiveRender(eventBean, buf, level + 1, nestedProp.Metadata, rendererMetaOptions); + + Ident(buf, level); + buf.Append("'); + buf.Append(NEWLINE); + } + + private void RenderAttInner(StringBuilder buf, int level, EventBean nestedEventBean, NestedGetterPair nestedProp) + { + Ident(buf, level); + buf.Append('<'); + buf.Append(nestedProp.Name); + + RenderAttributes(nestedEventBean, buf, nestedProp.Metadata); + + String inner = RenderAttElements(nestedEventBean, level + 1, nestedProp.Metadata); + + if ((inner == null) || (inner.Trim().Length == 0)) + { + buf.Append("/>"); + buf.Append(NEWLINE); + } + else + { + buf.Append(">"); + buf.Append(NEWLINE); + buf.Append(inner); + + Ident(buf, level); + buf.Append("'); + buf.Append(NEWLINE); + } + } + + private static String GetFirstWord(String rootElementName) + { + if ((rootElementName == null) || (rootElementName.Trim().Length == 0)) + { + return rootElementName; + } + int index = rootElementName.IndexOf(' '); + if (index < 0) + { + return rootElementName; + } + return rootElementName.Substring(0, index); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/vaevent/PropertyGroupDesc.cs b/NEsper.Core/NEsper.Core/events/vaevent/PropertyGroupDesc.cs new file mode 100755 index 000000000..cd491e88e --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/vaevent/PropertyGroupDesc.cs @@ -0,0 +1,67 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.events.vaevent +{ + /// + /// For use with building groups of event properties to reduce overhead in maintaining versions. + /// + public class PropertyGroupDesc + { + private readonly int groupNum; + private readonly IDictionary types; + private readonly String[] properties; + + /// + /// Ctor. + /// + /// the group number + /// the event types and their names whose totality of properties fully falls within this group. + /// is the properties in the group + public PropertyGroupDesc(int groupNum, IDictionary nameTypeSet, String[] properties) + { + this.groupNum = groupNum; + this.types = nameTypeSet; + this.properties = properties; + } + + /// Returns the group number. + /// group number + public int GroupNum + { + get { return groupNum; } + } + + /// Returns the types. + /// types + public IDictionary Types + { + get { return types; } + } + + /// Returns the properties. + /// properties + public ICollection Properties + { + get { return properties; } + } + + public override String ToString() + { + return "groupNum=" + groupNum + + " properties=" + properties.Render() + + " nameTypes=" + types; + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/vaevent/PropertyUtility.cs b/NEsper.Core/NEsper.Core/events/vaevent/PropertyUtility.cs new file mode 100755 index 000000000..582e83ac2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/vaevent/PropertyUtility.cs @@ -0,0 +1,294 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.logging; +using com.espertech.esper.compat.collections; +using com.espertech.esper.util; + +namespace com.espertech.esper.events.vaevent +{ + /// + /// Utility for handling properties for the purpose of merging and versioning by revision + /// event types. + /// + public class PropertyUtility + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// Returns a multi-key for an event and key property getters + /// to get keys for + /// getters to use + /// key + public static Object GetKeys(EventBean theEvent, EventPropertyGetter[] keyPropertyGetters) + { + if (keyPropertyGetters.Length == 1) + { + return keyPropertyGetters[0].Get(theEvent); + } + + var keys = new Object[keyPropertyGetters.Length]; + for (int i = 0; i < keys.Length; i++) + { + keys[i] = keyPropertyGetters[i].Get(theEvent); + } + return new MultiKeyUntyped(keys); + } + + /// From a list of property groups that include contributing event types, build a map of contributing event types and their type descriptor. + /// property groups + /// properties that change between groups + /// key properties + /// map of event type and type information + public static IDictionary GetPerType(PropertyGroupDesc[] groups, + String[] changesetProperties, + String[] keyProperties) + { + IDictionary perType = new Dictionary(); + foreach (PropertyGroupDesc group in groups) + { + foreach (EventType type in group.Types.Keys) + { + EventPropertyGetter[] changesetGetters = GetGetters(type, changesetProperties); + EventPropertyGetter[] keyGetters = GetGetters(type, keyProperties); + var pair = new RevisionTypeDesc(keyGetters, changesetGetters, group); + perType.Put(type, pair); + } + } + return perType; + } + + /// From a list of property groups that include multiple group numbers for each property, make a map of group numbers per property. + /// property groups + /// map of property name and group number + public static IDictionary GetGroupsPerProperty(PropertyGroupDesc[] groups) + { + IDictionary groupsNumsPerProp = new Dictionary(); + foreach (PropertyGroupDesc group in groups) + { + foreach (String property in group.Properties) + { + int[] value = groupsNumsPerProp.Get(property); + if (value == null) + { + value = new int[1]; + groupsNumsPerProp.Put(property, value); + value[0] = group.GroupNum; + } + else + { + var copy = new int[value.Length + 1]; + Array.Copy(value, 0, copy, 0, value.Length); + copy[value.Length] = group.GroupNum; + Array.Sort(copy); + groupsNumsPerProp.Put(property, copy); + } + } + } + return groupsNumsPerProp; + } + + /// Analyze multiple event types and determine common property sets that form property groups. + /// property names to look at + /// all types contributing + /// names of properies + /// groups + public static PropertyGroupDesc[] AnalyzeGroups(String[] allProperties, EventType[] deltaEventTypes, + String[] names) + { + if (deltaEventTypes.Length != names.Length) + { + throw new ArgumentException("Delta event type number and name number of elements don't match"); + } + allProperties = CopyAndSort(allProperties); + + var result = new LinkedHashMap, PropertyGroupDesc>(); + var currentGroupNum = 0; + + for (int i = 0; i < deltaEventTypes.Length; i++) + { + MultiKey props = GetPropertiesContributed(deltaEventTypes[i], allProperties); + if (props.Array.Length == 0) + { + Log.Warn("Event type named '" + names[i] + + "' does not contribute (or override) any properties of the revision event type"); + continue; + } + + PropertyGroupDesc propertyGroup = result.Get(props); + IDictionary typesForGroup; + if (propertyGroup == null) + { + typesForGroup = new Dictionary(); + propertyGroup = new PropertyGroupDesc(currentGroupNum++, typesForGroup, props.Array); + result.Put(props, propertyGroup); + } + else + { + typesForGroup = propertyGroup.Types; + } + typesForGroup.Put(deltaEventTypes[i], names[i]); + } + + PropertyGroupDesc[] array = Collections.ToArray(result.Values); + + if (Log.IsDebugEnabled) + { + Log.Debug(".analyzeGroups " + array.Render()); + } + return array; + } + + private static MultiKey GetPropertiesContributed(EventType deltaEventType, + ICollection allPropertiesSorted) + { + var props = new SortedSet(); + foreach (String property in deltaEventType.PropertyNames) + { + if (allPropertiesSorted.Contains(property)) + { + props.Add(property); + } + } + return new MultiKey(props.ToArray()); + } + + /// Copy an sort the input array. + /// to sort + /// sorted copied array + protected internal static String[] CopyAndSort(ICollection input) + { + String[] result = Collections.ToArray(input); + Array.Sort(result); + return result; + } + + /// Return getters for property names. + /// type to get getters from + /// names to get + /// getters + public static EventPropertyGetter[] GetGetters(EventType eventType, String[] propertyNames) + { + var getters = new EventPropertyGetter[propertyNames.Length]; + for (int i = 0; i < getters.Length; i++) + { + getters[i] = eventType.GetGetter(propertyNames[i]); + } + return getters; + } + + /// + /// Remove from values all removeValues and build a unique sorted result array. + /// + /// to consider + /// values to remove from values + /// sorted unique + public static string[] UniqueExclusiveSort(ICollection values, string[] removeValues) + { + var unique = new HashSet(); + foreach (var value in values) + unique.Add(value); + foreach (var value in removeValues) + unique.Remove(value); + + String[] uniqueArr = unique.ToArray(); + Array.Sort(uniqueArr); + return uniqueArr; + } + + public static PropertyAccessException GetMismatchException(MethodInfo method, Object @object, InvalidCastException e) + { + return GetMismatchException(method.DeclaringType, @object, e); + } + + public static PropertyAccessException GetMismatchException(FieldInfo field, Object @object, InvalidCastException e) + { + return GetMismatchException(field.DeclaringType, @object, e); + } + + public static PropertyAccessException GetTargetException(MethodInfo method, TargetException e) + { + Type declaring = method.DeclaringType; + String eMessage = e.InnerException != null ? e.InnerException.Message : e.Message; + String message = "Failed to invoke method " + method.Name + " on class " + + TypeHelper.GetTypeNameFullyQualPretty(declaring) + ": " + + eMessage; + throw new PropertyAccessException(message, e); + } + + public static PropertyAccessException GetInvocationTargetException(MethodInfo method, TargetInvocationException e) + { + Type declaring = method.DeclaringType; + String message = "Failed to invoke method " + method.Name + " on class " + TypeHelper.GetTypeNameFullyQualPretty(declaring) + ": " + e.InnerException.Message; + throw new PropertyAccessException(message, e); + } + + public static PropertyAccessException GetIllegalAccessException(FieldInfo field, MemberAccessException e) + { + return GetAccessExceptionField(field, e); + } + + public static PropertyAccessException GetIllegalArgumentException(FieldInfo field, ArgumentException e) + { + return GetAccessExceptionField(field, e); + } + + private static PropertyAccessException GetAccessExceptionField(FieldInfo field, Exception e) + { + Type declaring = field.DeclaringType; + String message = "Failed to obtain field value for field " + field.Name + " on class " + TypeHelper.GetTypeNameFullyQualPretty(declaring) + ": " + e.Message; + throw new PropertyAccessException(message, e); + } + + private static PropertyAccessException GetMismatchException(Type declared, Object @object, InvalidCastException e) + { + String classNameExpected = TypeHelper.GetTypeNameFullyQualPretty(declared); + String classNameReceived; + if (@object != null) + { + classNameReceived = TypeHelper.GetTypeNameFullyQualPretty(@object.GetType()); + } + else + { + classNameReceived = "null"; + } + + if (classNameExpected.Equals(classNameReceived)) + { + classNameExpected = TypeHelper.GetTypeNameFullyQualPretty(declared); + classNameReceived = @object != null ? TypeHelper.GetTypeNameFullyQualPretty(@object.GetType()) : "null"; + } + + var message = "Mismatched getter instance to event bean type, expected " + classNameExpected + " but received " + classNameReceived; + throw new PropertyAccessException(message, e); + } + + public static PropertyAccessException GetIllegalAccessException(MethodInfo method, MemberAccessException e) + { + return GetAccessExceptionMethod(method, e); + } + + public static PropertyAccessException GetIllegalArgumentException(MethodInfo method, ArgumentException e) + { + return GetAccessExceptionMethod(method, e); + } + + private static PropertyAccessException GetAccessExceptionMethod(MethodInfo method, Exception e) + { + Type declaring = method.DeclaringType; + String message = "Failed to invoke method " + method.Name + " on class " + TypeHelper.GetTypeNameFullyQualPretty(declaring) + ": " + e.Message; + throw new PropertyAccessException(message, e); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/vaevent/RevisionBeanHolder.cs b/NEsper.Core/NEsper.Core/events/vaevent/RevisionBeanHolder.cs new file mode 100755 index 000000000..b74422779 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/vaevent/RevisionBeanHolder.cs @@ -0,0 +1,62 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.vaevent +{ + /// Holds revisions for property groups in an overlay strategy. + public class RevisionBeanHolder + { + private readonly long version; + private readonly EventBean eventBean; + private readonly EventPropertyGetter[] getters; + + /// Ctor. + /// the current version + /// the new event + /// the getters + public RevisionBeanHolder(long version, EventBean eventBean, EventPropertyGetter[] getters) + { + this.version = version; + this.eventBean = eventBean; + this.getters = getters; + } + + /// Returns current version number. + /// version + public long Version + { + get { return version; } + } + + /// Returns the contributing event. + /// event + public EventBean EventBean + { + get { return eventBean; } + } + + /// Returns getters for event property access. + /// getters + public EventPropertyGetter[] Getters + { + get { return getters; } + } + + /// Returns a property value. + /// number of property + /// value + public Object GetValueForProperty(int propertyNumber) + { + return getters[propertyNumber].Get(eventBean); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/vaevent/RevisionEventBeanDeclared.cs b/NEsper.Core/NEsper.Core/events/vaevent/RevisionEventBeanDeclared.cs new file mode 100755 index 000000000..94b08fa34 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/vaevent/RevisionEventBeanDeclared.cs @@ -0,0 +1,130 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.vaevent +{ + /// + /// Revision event bean for the overlayed scheme. + /// + public class RevisionEventBeanDeclared : EventBean + { + /// Ctor. + /// revision event type + /// event wrapped + public RevisionEventBeanDeclared(RevisionEventType eventType, EventBean underlying) + { + RevisionEventType = eventType; + UnderlyingFullOrDelta = underlying; + } + + /// Returns true if latest event, or false if not. + /// indicator if latest + public bool IsLatest { get; set; } + + /// Sets versions. + /// versions + public RevisionBeanHolder[] Holders { private get; set; } + + /// Returns last base event. + /// base event + public EventBean LastBaseEvent { get; set; } + + /// Returns wrapped event. + /// wrapped event + public EventBean UnderlyingFullOrDelta { get; private set; } + + /// Returns the key. + /// key + public object Key { get; set; } + + /// Returns the revision event type. + /// type + public RevisionEventType RevisionEventType { get; private set; } + + public EventType EventType + { + get { return RevisionEventType; } + } + + public Object Get(String property) + { + EventPropertyGetter getter = RevisionEventType.GetGetter(property); + if (getter == null) + { + return null; + } + return getter.Get(this); + } + + public object this[string property] + { + get { return Get(property); } + } + + public object Underlying + { + get { return typeof(RevisionEventBeanDeclared); } + } + + public Object GetFragment(String propertyExpression) + { + EventPropertyGetter getter = RevisionEventType.GetGetter(propertyExpression); + if (getter == null) + { + throw PropertyAccessException.NotAValidProperty(propertyExpression); + } + return getter.GetFragment(this); + } + + /// Returns a versioned value. + /// getter parameters + /// value + public Object GetVersionedValue(RevisionGetterParameters parameters) + { + RevisionBeanHolder holderMostRecent = null; + + if (Holders != null) + { + foreach (int numSet in parameters.PropertyGroups) + { + RevisionBeanHolder holder = Holders[numSet]; + if (holder != null) + { + if (holderMostRecent == null) + { + holderMostRecent = holder; + } + else + { + if (holder.Version > holderMostRecent.Version) + { + holderMostRecent = holder; + } + } + } + } + } + + // none found, use last full event + if (holderMostRecent == null) + { + if (LastBaseEvent == null) + { + return null; + } + return parameters.BaseGetter.Get(LastBaseEvent); + } + + return holderMostRecent.GetValueForProperty(parameters.PropertyNumber); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/vaevent/RevisionEventBeanMerge.cs b/NEsper.Core/NEsper.Core/events/vaevent/RevisionEventBeanMerge.cs new file mode 100755 index 000000000..ab6330e64 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/vaevent/RevisionEventBeanMerge.cs @@ -0,0 +1,127 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.util; + +namespace com.espertech.esper.events.vaevent +{ + /// + /// Merge-event for event revisions. + /// + public class RevisionEventBeanMerge : EventBean + { + private readonly RevisionEventType _revisionEventType; + private NullableObject[] _overlay; + + /// Ctor. + /// type + /// event wrapped + public RevisionEventBeanMerge(RevisionEventType revisionEventType, EventBean underlyingFull) + { + _revisionEventType = revisionEventType; + UnderlyingFullOrDelta = underlyingFull; + } + + /// Returns overlay values. + /// overlay + public NullableObject[] Overlay + { + get { return _overlay; } + set { _overlay = value; } + } + + /// Returns flag indicated latest or not. + /// latest flag + public bool IsLatest { get; set; } + + /// Returns the key. + /// key + public object Key { get; set; } + + /// Returns last base event. + /// base event + public EventBean LastBaseEvent { get; set; } + + public EventType EventType + { + get { return _revisionEventType; } + } + + public object this[string property] + { + get { return Get(property); } + } + + public Object Get(String property) + { + var getter = _revisionEventType.GetGetter(property); + if (getter == null) + { + return null; + } + return getter.Get(this); + } + + public object Underlying + { + get { return typeof (RevisionEventBeanMerge); } + } + + /// Returns wrapped event + /// event + public EventBean UnderlyingFullOrDelta { get; private set; } + + /// Returns base event value. + /// supplies getter + /// value + public Object GetBaseEventValue(RevisionGetterParameters parameters) + { + return parameters.BaseGetter.Get(LastBaseEvent); + } + + /// Returns a versioned value. + /// getter and indexes + /// value + public Object GetVersionedValue(RevisionGetterParameters parameters) + { + int propertyNumber = parameters.PropertyNumber; + + if (_overlay != null) + { + var value = _overlay[propertyNumber]; + if (value != null) + { + return value.Value; + } + } + + var getter = parameters.BaseGetter; + if (getter == null) + { + return null; // The property was added by a delta event and only exists on a delta + } + if (LastBaseEvent != null) { + return getter.Get(LastBaseEvent); + } + return null; + } + + public Object GetFragment(String propertyExpression) + { + var getter = _revisionEventType.GetGetter(propertyExpression); + if (getter == null) + { + return null; + } + return getter.GetFragment(this); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/vaevent/RevisionEventType.cs b/NEsper.Core/NEsper.Core/events/vaevent/RevisionEventType.cs new file mode 100755 index 000000000..803e453d9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/vaevent/RevisionEventType.cs @@ -0,0 +1,311 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.parse; +using com.espertech.esper.events.bean; +using com.espertech.esper.events.property; +using com.espertech.esper.util; + +namespace com.espertech.esper.events.vaevent +{ + /// Event type of revision events. + public class RevisionEventType : EventTypeSPI + { + private readonly EventTypeMetadata _metadata; + private readonly int _eventTypeId; + private readonly string[] _propertyNames; + private readonly EventPropertyDescriptor[] _propertyDescriptors; + private readonly IDictionary _propertyDescriptorMap; + private readonly IDictionary _propertyDesc; + private readonly EventAdapterService _eventAdapterService; + + /// + /// Ctor. + /// + /// describes each properties type + /// for nested property handling + /// - event type metadata + /// type id + public RevisionEventType( + EventTypeMetadata metadata, + int eventTypeId, + IDictionary propertyDesc, + EventAdapterService eventAdapterService) + { + _metadata = metadata; + _eventTypeId = eventTypeId; + _propertyDesc = propertyDesc; + _propertyNames = propertyDesc.Keys.ToArray(); + _eventAdapterService = eventAdapterService; + + _propertyDescriptors = new EventPropertyDescriptor[propertyDesc.Count]; + _propertyDescriptorMap = new Dictionary(); + var count = 0; + foreach (var desc in propertyDesc) + { + var type = (Type) desc.Value.PropertyType; + var descriptor = new EventPropertyDescriptor( + desc.Key, type, null, false, false, false, false, type.IsFragmentableType()); + _propertyDescriptors[count] = descriptor; + _propertyDescriptorMap.Put(desc.Key, descriptor); + count++; + } + } + + public int EventTypeId + { + get { return _eventTypeId; } + } + + public string StartTimestampPropertyName + { + get { return null; } + } + + public string EndTimestampPropertyName + { + get { return null; } + } + + public EventPropertyGetter GetGetter(string propertyName) + { + var desc = _propertyDesc.Get(propertyName); + if (desc != null) + { + return desc.RevisionGetter; + } + + // dynamic property names note allowed + if (propertyName.IndexOf('?') != -1) + { + return null; + } + + // see if this is a nested property + var index = ASTUtil.UnescapedIndexOfDot(propertyName); + if (index == -1) + { + var prop = PropertyParser.ParseAndWalkLaxToSimple(propertyName); + if (prop is SimpleProperty) + { + // there is no such property since it wasn't found earlier + return null; + } + string atomic = null; + if (prop is IndexedProperty) + { + var indexedprop = (IndexedProperty) prop; + atomic = indexedprop.PropertyNameAtomic; + } + if (prop is MappedProperty) + { + var indexedprop = (MappedProperty) prop; + atomic = indexedprop.PropertyNameAtomic; + } + desc = _propertyDesc.Get(atomic); + if (desc == null) + { + return null; + } + if (!(desc.PropertyType is Type)) + { + return null; + } + var nestedClass = (Type) desc.PropertyType; + var complexProperty = (BeanEventType) _eventAdapterService.AddBeanType(nestedClass.GetDefaultTypeName(), nestedClass, false, false, false); + return prop.GetGetter(complexProperty, _eventAdapterService); + } + + // Map event types allow 2 types of properties inside: + // - a property that is a Java object is interrogated via bean property getters and BeanEventType + // - a property that is a Map itself is interrogated via map property getters + + // Take apart the nested property into a map key and a nested value class property name + var propertyMap = ASTUtil.UnescapeDot(propertyName.Substring(0, index)); + var propertyNested = propertyName.Substring(index + 1); + + desc = _propertyDesc.Get(propertyMap); + if (desc == null) + { + return null; // prefix not a known property + } + + // only nested classes supported for revision event types since deep property information not currently exposed by EventType + if (desc.PropertyType is Type) + { + // ask the nested class to resolve the property + var simpleClass = (Type) desc.PropertyType; + var nestedEventType = _eventAdapterService.AddBeanType( + simpleClass.Name, simpleClass, false, false, false); + var nestedGetter = nestedEventType.GetGetter(propertyNested); + if (nestedGetter == null) + { + return null; + } + + // construct getter for nested property + return new RevisionNestedPropertyGetter(desc.RevisionGetter, nestedGetter, _eventAdapterService); + } + else + { + return null; + } + } + + public string Name + { + get { return _metadata.PublicName; } + } + + public Type GetPropertyType(string propertyName) + { + var desc = _propertyDesc.Get(propertyName); + if (desc != null) + { + if (desc.PropertyType is Type) + { + return (Type) desc.PropertyType; + } + return null; + } + + // dynamic property names note allowed + if (propertyName.IndexOf('?') != -1) + { + return null; + } + + // see if this is a nested property + var index = ASTUtil.UnescapedIndexOfDot(propertyName); + if (index == -1) + { + return null; + } + + // Map event types allow 2 types of properties inside: + // - a property that is a Java object is interrogated via bean property getters and BeanEventType + // - a property that is a Map itself is interrogated via map property getters + + // Take apart the nested property into a map key and a nested value class property name + var propertyMap = ASTUtil.UnescapeDot(propertyName.Substring(0, index)); + var propertyNested = propertyName.Substring(index + 1); + + desc = _propertyDesc.Get(propertyMap); + if (desc == null) + { + return null; // prefix not a known property + } + else if (desc.PropertyType is Type) + { + var simpleClass = (Type) desc.PropertyType; + var nestedEventType = _eventAdapterService.AddBeanType(simpleClass.GetDefaultTypeName(), simpleClass, false, false, false); + return nestedEventType.GetPropertyType(propertyNested); + } + else + { + return null; + } + } + + public Type UnderlyingType + { + get { return typeof (RevisionEventType); } + } + + public string[] PropertyNames + { + get { return _propertyNames; } + } + + public bool IsProperty(string property) + { + return GetPropertyType(property) != null; + } + + public EventType[] SuperTypes + { + get { return null; } + } + + public EventType[] DeepSuperTypes + { + get { return null; } + } + + public EventTypeMetadata Metadata + { + get { return _metadata; } + } + + public IList PropertyDescriptors + { + get { return _propertyDescriptors; } + } + + public FragmentEventType GetFragmentType(string property) + { + return null; + } + + public EventPropertyDescriptor GetPropertyDescriptor(string propertyName) + { + return _propertyDescriptorMap.Get(propertyName); + } + + public EventPropertyWriter GetWriter(string propertyName) + { + return null; + } + + public EventPropertyDescriptor[] WriteableProperties + { + get { return new EventPropertyDescriptor[0]; } + } + + public EventBeanCopyMethod GetCopyMethod(string[] properties) + { + return null; + } + + public EventPropertyDescriptor GetWritableProperty(string propertyName) + { + return null; + } + + public EventBeanWriter GetWriter(string[] properties) + { + return null; + } + + public EventBeanReader Reader + { + get { return null; } + } + + public EventPropertyGetterMapped GetGetterMapped(string mappedProperty) + { + return null; + } + + public EventPropertyGetterIndexed GetGetterIndexed(string indexedProperty) + { + return null; + } + + public bool EqualsCompareType(EventType eventType) + { + return this == eventType; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/events/vaevent/RevisionGetterParameters.cs b/NEsper.Core/NEsper.Core/events/vaevent/RevisionGetterParameters.cs new file mode 100755 index 000000000..badc9a6fd --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/vaevent/RevisionGetterParameters.cs @@ -0,0 +1,58 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using com.espertech.esper.client; + +namespace com.espertech.esper.events.vaevent +{ + /// + /// Getter parameters for revision events. + /// + public class RevisionGetterParameters + { + private readonly String propertyName; + private readonly int propertyNumber; + private readonly EventPropertyGetter baseGetter; + private readonly int[] propertyGroups; + + /// Ctor. + /// the property this gets + /// the property number + /// the getter of the base event to use, if any + /// is the group numbers that the getter may access to obtain a property value + public RevisionGetterParameters(String propertyName, int propertyNumber, EventPropertyGetter fullGetter, int[] authoritySets) + { + this.propertyName = propertyName; + this.propertyNumber = propertyNumber; + this.baseGetter = fullGetter; + this.propertyGroups = authoritySets; + } + + /// Returns the group numbers to look for updated properties comparing version numbers. + /// groups + public int[] PropertyGroups + { + get { return propertyGroups; } + } + + /// Returns the property number. + /// property number + public int PropertyNumber + { + get { return propertyNumber; } + } + + /// Returns the getter for the base event type. + /// base getter + public EventPropertyGetter BaseGetter + { + get { return baseGetter; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/vaevent/RevisionNestedPropertyGetter.cs b/NEsper.Core/NEsper.Core/events/vaevent/RevisionNestedPropertyGetter.cs new file mode 100755 index 000000000..616b81f3d --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/vaevent/RevisionNestedPropertyGetter.cs @@ -0,0 +1,58 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.vaevent +{ + /// + /// A getter that works on events residing within a Map as an event property. + /// + public class RevisionNestedPropertyGetter : EventPropertyGetter + { + private readonly EventPropertyGetter revisionGetter; + private readonly EventPropertyGetter nestedGetter; + private readonly EventAdapterService eventAdapterService; + + /// + /// Ctor. + /// + /// getter for revision value + /// getter to apply to revision value + /// for handling object types + public RevisionNestedPropertyGetter(EventPropertyGetter revisionGetter, EventPropertyGetter nestedGetter, EventAdapterService eventAdapterService) { + this.revisionGetter = revisionGetter; + this.eventAdapterService = eventAdapterService; + this.nestedGetter = nestedGetter; + } + + public Object Get(EventBean eventBean) + { + Object result = revisionGetter.Get(eventBean); + if (result == null) + { + return result; + } + + // Object within the map + return nestedGetter.Get(eventAdapterService.AdapterForObject(result)); + } + + public bool IsExistsProperty(EventBean eventBean) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + + public Object GetFragment(EventBean eventBean) + { + return null; // no fragments provided by revision events + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/vaevent/RevisionPropertyTypeDesc.cs b/NEsper.Core/NEsper.Core/events/vaevent/RevisionPropertyTypeDesc.cs new file mode 100755 index 000000000..76bac18c4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/vaevent/RevisionPropertyTypeDesc.cs @@ -0,0 +1,58 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using com.espertech.esper.client; + +namespace com.espertech.esper.events.vaevent +{ + /// + /// Property descriptor for use by revision event types to maintain access to + /// revision event properties. + /// + public class RevisionPropertyTypeDesc + { + private readonly EventPropertyGetter revisionGetter; + private readonly RevisionGetterParameters revisionGetterParams; + private readonly Object propertyType; // Can be the {Type|Map|EventType} + + /// Ctor. + /// getter to use + /// getter parameters + /// type of the property + public RevisionPropertyTypeDesc(EventPropertyGetter revisionGetter, + RevisionGetterParameters revisionGetterParams, + Type propertyType) + { + this.revisionGetter = revisionGetter; + this.revisionGetterParams = revisionGetterParams; + this.propertyType = propertyType; + } + + /// Returns the getter for the property on the revision event type. + /// getter + public EventPropertyGetter RevisionGetter + { + get { return revisionGetter; } + } + + /// Returns parameters for the getter for the property on the revision event type. + /// getter parameters + public RevisionGetterParameters RevisionGetterParams + { + get { return revisionGetterParams; } + } + + /// Returns property type. + /// type + public object PropertyType + { + get { return propertyType; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/vaevent/RevisionSpec.cs b/NEsper.Core/NEsper.Core/events/vaevent/RevisionSpec.cs new file mode 100755 index 000000000..20bae92e7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/vaevent/RevisionSpec.cs @@ -0,0 +1,90 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.vaevent +{ + /// + /// Specification for how to build a revision event type. + /// + /// Compiled from the information provided via configuration, which has already been validated + /// before building this specification. + /// + public class RevisionSpec + { + /// Ctor. + /// strategy to use + /// base type + /// delta types + /// aliases of delta types + /// names of key properties + /// names of properties that change + /// properties only available on the base event + /// bool to indicate delta types add additional properties. + /// flag for each property indicating whether its contributed only by a delta event + public RevisionSpec(PropertyRevisionEnum propertyRevision, + EventType baseEventType, + EventType[] deltaTypes, + String[] deltaNames, + String[] keyPropertyNames, + String[] changesetPropertyNames, + String[] baseEventOnlyPropertyNames, + bool deltaTypesAddProperties, + bool[] changesetPropertyDeltaContributed) + { + PropertyRevision = propertyRevision; + BaseEventType = baseEventType; + DeltaTypes = deltaTypes; + DeltaNames = deltaNames; + KeyPropertyNames = keyPropertyNames; + ChangesetPropertyNames = changesetPropertyNames; + BaseEventOnlyPropertyNames = baseEventOnlyPropertyNames; + IsDeltaTypesAddProperties = deltaTypesAddProperties; + ChangesetPropertyDeltaContributed = changesetPropertyDeltaContributed; + } + + /// Flag for each changeset property to indicate if only the delta contributes the property. + /// flag per property + public bool[] ChangesetPropertyDeltaContributed { get; private set; } + + /// Returns the stratgegy for revisioning. + /// enum + public PropertyRevisionEnum PropertyRevision { get; private set; } + + /// Returns the base event type. + /// base type + public EventType BaseEventType { get; private set; } + + /// Returns the delta event types. + /// types + public EventType[] DeltaTypes { get; private set; } + + /// Returns aliases for delta events. + /// event type alias names for delta events + public string[] DeltaNames { get; private set; } + + /// Returns property names for key properties. + /// property names + public string[] KeyPropertyNames { get; private set; } + + /// Returns property names of properties that change by deltas + /// prop names + public string[] ChangesetPropertyNames { get; private set; } + + /// Returns the properies only found on the base event. + /// base props + public string[] BaseEventOnlyPropertyNames { get; private set; } + + /// Returns true if delta types add properties. + /// flag indicating if delta event types add properties + public bool IsDeltaTypesAddProperties { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/events/vaevent/RevisionStateDeclared.cs b/NEsper.Core/NEsper.Core/events/vaevent/RevisionStateDeclared.cs new file mode 100755 index 000000000..53f3b5ed5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/vaevent/RevisionStateDeclared.cs @@ -0,0 +1,74 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.vaevent +{ + /// + /// State for the overlay (non-merge) strategy. + /// + public class RevisionStateDeclared + { + private long revisionNumber; + private EventBean baseEventUnderlying; + private RevisionBeanHolder[] holders; + private RevisionEventBeanDeclared lastEvent; + + /// Ctor. + /// base event + /// revisions + /// prior event + public RevisionStateDeclared(EventBean baseEventUnderlying, + RevisionBeanHolder[] holders, + RevisionEventBeanDeclared lastEvent) + { + this.baseEventUnderlying = baseEventUnderlying; + this.holders = holders; + this.lastEvent = lastEvent; + } + + /// Returns revision number. + /// version number + public long RevisionNumber + { + get { return revisionNumber; } + } + + /// Increments version number. + /// incremented version number + public long IncRevisionNumber() + { + return ++revisionNumber; + } + + /// Gets or sets base event. + /// base event + public EventBean BaseEventUnderlying + { + get { return baseEventUnderlying; } + set { this.baseEventUnderlying = value; } + } + + /// Gets or sets versions. + /// versions + public RevisionBeanHolder[] Holders + { + get { return holders; } + set { this.holders = value; } + } + + /// Gets or sets the last event. + /// last event + public RevisionEventBeanDeclared LastEvent + { + get { return lastEvent; } + set { this.lastEvent = value; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/vaevent/RevisionStateMerge.cs b/NEsper.Core/NEsper.Core/events/vaevent/RevisionStateMerge.cs new file mode 100755 index 000000000..c2fd3f12c --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/vaevent/RevisionStateMerge.cs @@ -0,0 +1,57 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using com.espertech.esper.client; +using com.espertech.esper.util; + +namespace com.espertech.esper.events.vaevent +{ + /// State for merge stratgies. + public class RevisionStateMerge + { + private EventBean baseEventUnderlying; + private NullableObject[] overlays; + private RevisionEventBeanMerge lastEvent; + + /// Ctor. + /// base event + /// merged values + /// last event + public RevisionStateMerge(EventBean baseEventUnderlying, NullableObject[] overlays, RevisionEventBeanMerge lastEvent) + { + this.baseEventUnderlying = baseEventUnderlying; + this.overlays = overlays; + this.lastEvent = lastEvent; + } + + /// Gets or sets base event. + /// base event + public EventBean BaseEventUnderlying + { + get { return baseEventUnderlying; } + set { this.baseEventUnderlying = value; } + } + + /// Gets or sets the merged values. + /// merged values + public NullableObject[] Overlays + { + get { return overlays; } + set { this.overlays = value; } + } + + /// Gets or sets the last event. + /// last event + public RevisionEventBeanMerge LastEvent + { + get { return lastEvent; } + set { this.lastEvent = value; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/vaevent/RevisionTypeDesc.cs b/NEsper.Core/NEsper.Core/events/vaevent/RevisionTypeDesc.cs new file mode 100755 index 000000000..8c1abd3b1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/vaevent/RevisionTypeDesc.cs @@ -0,0 +1,71 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.vaevent +{ + /// Per-event-type descriptor for fast access to getters for key values and changes properties. + public class RevisionTypeDesc + { + private readonly EventPropertyGetter[] keyPropertyGetters; + private readonly EventPropertyGetter[] changesetPropertyGetters; + private readonly PropertyGroupDesc group; + private readonly int[] changesetPropertyIndex; + + /// Ctor. + /// key getters + /// property getters + /// group this belongs to + public RevisionTypeDesc(EventPropertyGetter[] keyPropertyGetters, EventPropertyGetter[] changesetPropertyGetters, PropertyGroupDesc group) + { + this.keyPropertyGetters = keyPropertyGetters; + this.changesetPropertyGetters = changesetPropertyGetters; + this.group = group; + } + + /// Ctor. + /// key getters + /// property getters + /// indexes of properties contributed + public RevisionTypeDesc(EventPropertyGetter[] keyPropertyGetters, EventPropertyGetter[] changesetPropertyGetters, int[] changesetPropertyIndex) + { + this.keyPropertyGetters = keyPropertyGetters; + this.changesetPropertyGetters = changesetPropertyGetters; + this.changesetPropertyIndex = changesetPropertyIndex; + } + + /// Returns key getters. + /// getters + public EventPropertyGetter[] KeyPropertyGetters + { + get { return keyPropertyGetters; } + } + + /// Returns property getters. + /// getters + public EventPropertyGetter[] ChangesetPropertyGetters + { + get { return changesetPropertyGetters; } + } + + /// Returns group, or null if not using property groups. + /// group + public PropertyGroupDesc Group + { + get { return group; } + } + + /// Returns indexes of properties contributed, or null if not using indexes. + /// indexes + public int[] ChangesetPropertyIndex + { + get { return changesetPropertyIndex; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/vaevent/UpdateStrategy.cs b/NEsper.Core/NEsper.Core/events/vaevent/UpdateStrategy.cs new file mode 100755 index 000000000..2ae21b334 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/vaevent/UpdateStrategy.cs @@ -0,0 +1,24 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.events.vaevent +{ + /// Strategy for merging updates or additional properties. + public interface UpdateStrategy + { + /// Merge properties. + /// true if the event is a base event type + /// the current state, to be updated. + /// the new event to merge + /// descriptor for event type of the new event to merge + void HandleUpdate(bool isBaseEventType, + RevisionStateMerge revisionState, + RevisionEventBeanMerge revisionEvent, + RevisionTypeDesc typesDesc); + } +} diff --git a/NEsper.Core/NEsper.Core/events/vaevent/UpdateStrategyBase.cs b/NEsper.Core/NEsper.Core/events/vaevent/UpdateStrategyBase.cs new file mode 100755 index 000000000..189be956b --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/vaevent/UpdateStrategyBase.cs @@ -0,0 +1,52 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.util; + +namespace com.espertech.esper.events.vaevent +{ + /// Base strategy implementation holds the specification object. + public abstract class UpdateStrategyBase : UpdateStrategy + { + /// The specification. + protected readonly RevisionSpec spec; + + /// Ctor. + /// is the specification + protected UpdateStrategyBase(RevisionSpec spec) + { + this.spec = spec; + } + + /// Array copy. + /// to copy + /// copied array + internal static NullableObject[] ArrayCopy(NullableObject[] array) + { + if (array == null) + { + return null; + } + NullableObject[] result = new NullableObject[array.Length]; + Array.Copy(array, 0, result, 0, array.Length); + return result; + } + + /// Merge properties. + /// true if the event is a base event type + /// the current state, to be updated. + /// the new event to merge + /// descriptor for event type of the new event to merge + public abstract void HandleUpdate(bool isBaseEventType, + RevisionStateMerge revisionState, + RevisionEventBeanMerge revisionEvent, + RevisionTypeDesc typesDesc); + } +} diff --git a/NEsper.Core/NEsper.Core/events/vaevent/UpdateStrategyDeclared.cs b/NEsper.Core/NEsper.Core/events/vaevent/UpdateStrategyDeclared.cs new file mode 100755 index 000000000..4285be803 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/vaevent/UpdateStrategyDeclared.cs @@ -0,0 +1,99 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.util; + +namespace com.espertech.esper.events.vaevent +{ + /// + /// Strategy for merging Update properties using all declared property's values. + /// + public class UpdateStrategyDeclared : UpdateStrategyBase + { + /// Ctor. + /// the specification + public UpdateStrategyDeclared(RevisionSpec spec) + : base(spec) + { + } + + public override void HandleUpdate(bool isBaseEventType, + RevisionStateMerge revisionState, + RevisionEventBeanMerge revisionEvent, + RevisionTypeDesc typesDesc) + { + EventBean underlyingEvent = revisionEvent.UnderlyingFullOrDelta; + + // Previously-seen full event + if (isBaseEventType) + { + // If delta types don't add properties, simply set the overlay to null + NullableObject[] changeSetValues; + if (!spec.IsDeltaTypesAddProperties) + { + changeSetValues = null; + } + // If delta types do add properties, set a new overlay + else + { + changeSetValues = revisionState.Overlays; + if (changeSetValues == null) + { + changeSetValues = new NullableObject[spec.ChangesetPropertyNames.Length]; + } + else + { + changeSetValues = ArrayCopy(changeSetValues); // preserve the last revisions + } + + // reset properties not contributed by any delta, leaving all delta-contributed properties in place + bool[] changesetPropertyDeltaContributed = spec.ChangesetPropertyDeltaContributed; + for (int i = 0; i < changesetPropertyDeltaContributed.Length; i++) + { + // if contributed then leave the value, else override + if (!changesetPropertyDeltaContributed[i]) + { + changeSetValues[i] = null; + } + } + } + revisionState.Overlays = changeSetValues; + revisionState.BaseEventUnderlying = underlyingEvent; + } + // Delta event to existing full event merge + else + { + NullableObject[] changeSetValues = revisionState.Overlays; + + if (changeSetValues == null) + { + changeSetValues = new NullableObject[spec.ChangesetPropertyNames.Length]; + } + else + { + changeSetValues = ArrayCopy(changeSetValues); // preserve the last revisions + } + + // apply all properties of the delta event + int[] indexes = typesDesc.ChangesetPropertyIndex; + EventPropertyGetter[] getters = typesDesc.ChangesetPropertyGetters; + for (int i = 0; i < indexes.Length; i++) + { + int index = indexes[i]; + Object value = getters[i].Get(underlyingEvent); + changeSetValues[index] = new NullableObject(value); + } + + revisionState.Overlays = changeSetValues; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/vaevent/UpdateStrategyExists.cs b/NEsper.Core/NEsper.Core/events/vaevent/UpdateStrategyExists.cs new file mode 100755 index 000000000..047bc26e0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/vaevent/UpdateStrategyExists.cs @@ -0,0 +1,61 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using com.espertech.esper.client; +using com.espertech.esper.util; + +namespace com.espertech.esper.events.vaevent +{ + /// Strategy for merging Update properties using only existing property's values. + public class UpdateStrategyExists : UpdateStrategyBase + { + /// Ctor. + /// the specification + public UpdateStrategyExists(RevisionSpec spec) + : base(spec) + { + } + + public override void HandleUpdate(bool isBaseEventType, + RevisionStateMerge revisionState, + RevisionEventBeanMerge revisionEvent, + RevisionTypeDesc typesDesc) + { + EventBean underlyingEvent = revisionEvent.UnderlyingFullOrDelta; + + NullableObject[] changeSetValues = revisionState.Overlays; + if (changeSetValues == null) // optimization - the full event sets it to null, deltas all get a new one + { + changeSetValues = new NullableObject[spec.ChangesetPropertyNames.Length]; + } + else + { + changeSetValues = ArrayCopy(changeSetValues); // preserve the last revisions + } + + // apply all properties of the delta event + int[] indexes = typesDesc.ChangesetPropertyIndex; + EventPropertyGetter[] getters = typesDesc.ChangesetPropertyGetters; + for (int i = 0; i < indexes.Length; i++) + { + int index = indexes[i]; + + if (!getters[i].IsExistsProperty(underlyingEvent)) + { + continue; + } + + Object value = getters[i].Get(underlyingEvent); + changeSetValues[index] = new NullableObject(value); + } + + revisionState.Overlays = changeSetValues; + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/vaevent/UpdateStrategyNonNull.cs b/NEsper.Core/NEsper.Core/events/vaevent/UpdateStrategyNonNull.cs new file mode 100755 index 000000000..f8f9d9b87 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/vaevent/UpdateStrategyNonNull.cs @@ -0,0 +1,64 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.util; + +namespace com.espertech.esper.events.vaevent +{ + /// + /// Strategy for merging Update properties using only non-null values. + /// + public class UpdateStrategyNonNull : UpdateStrategyBase + { + /// Ctor. + /// the specification + public UpdateStrategyNonNull(RevisionSpec spec) + : base(spec) + { + } + + public override void HandleUpdate( + bool isBaseEventType, + RevisionStateMerge revisionState, + RevisionEventBeanMerge revisionEvent, + RevisionTypeDesc typesDesc) + { + EventBean underlyingEvent = revisionEvent.UnderlyingFullOrDelta; + + NullableObject[] changeSetValues = revisionState.Overlays; + if (changeSetValues == null) // optimization - the full event sets it to null, deltas all get a new one + { + changeSetValues = new NullableObject[spec.ChangesetPropertyNames.Length]; + } + else + { + changeSetValues = ArrayCopy(changeSetValues); // preserve the last revisions + } + + // apply all properties of the delta event + int[] indexes = typesDesc.ChangesetPropertyIndex; + EventPropertyGetter[] getters = typesDesc.ChangesetPropertyGetters; + for (int i = 0; i < indexes.Length; i++) + { + int index = indexes[i]; + + Object value = getters[i].Get(underlyingEvent); + if (value == null) + { + continue; + } + changeSetValues[index] = new NullableObject(value); + } + + revisionState.Overlays = changeSetValues; + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/events/vaevent/VAERevisionProcessorBase.cs b/NEsper.Core/NEsper.Core/events/vaevent/VAERevisionProcessorBase.cs new file mode 100755 index 000000000..5b7b60e84 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/vaevent/VAERevisionProcessorBase.cs @@ -0,0 +1,120 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.epl.named; +using com.espertech.esper.view; + +namespace com.espertech.esper.events.vaevent +{ + /// + /// Base revision processor. + /// + public abstract class VAERevisionProcessorBase : ValueAddEventProcessor + { + /// Revision type specification. + protected readonly RevisionSpec RevisionSpec; + + /// Name of type. + protected readonly String RevisionEventTypeName; + + /// Revision event type. + protected RevisionEventType RevisionEventType; + + /// For interogating nested properties. + protected EventAdapterService EventAdapterService; + + /// Map of participating type to descriptor. + protected IDictionary TypeDescriptors; + + /// Ctor. + /// specification + /// name of event type + /// for nested property handling + protected VAERevisionProcessorBase(RevisionSpec revisionSpec, String revisionEventTypeName, EventAdapterService eventAdapterService) + { + RevisionSpec = revisionSpec; + RevisionEventTypeName = revisionEventTypeName; + EventAdapterService = eventAdapterService; + TypeDescriptors = new Dictionary(); + } + + public virtual EventType ValueAddEventType + { + get { return RevisionEventType; } + } + + public virtual void ValidateEventType(EventType eventType) + { + if (eventType == RevisionSpec.BaseEventType) + { + return; + } + if (TypeDescriptors.ContainsKey(eventType)) + { + return; + } + + if (eventType == null) + { + throw new ExprValidationException(GetMessage()); + } + + // Check all the supertypes to see if one of the matches the full or delta types + IEnumerable deepSupers = eventType.DeepSuperTypes; + if (deepSupers != null) { + foreach (EventType type in deepSupers) { + if (type == RevisionSpec.BaseEventType) { + return; + } + if (TypeDescriptors.ContainsKey(type)) { + return; + } + } + } + + throw new ExprValidationException(GetMessage()); + } + + private String GetMessage() + { + return "Selected event type is not a valid base or delta event type of revision event type '" + + RevisionEventTypeName + "'"; + } + + /// For use in executing an insert-into, wraps the given event applying the revision event type, but not yet computing a new revision. + /// to wrap + /// revision event bean + public abstract EventBean GetValueAddEventBean(EventBean theEvent); + + /// Upon new events arriving into a named window (new data), and upon events being deleted via on-delete (old data), Update child views of the root view and apply to index repository as required (fast deletion). + /// new events + /// remove stream + /// the root view + /// delete and select indexes + public abstract void OnUpdate(EventBean[] newData, EventBean[] oldData, NamedWindowRootViewInstance namedWindowRootView, EventTableIndexRepository indexRepository); + + /// Handle iteration over revision event contents. + /// statement handle for safe iteration + /// the provider of data + /// collection to iterate + public abstract ICollection GetSnapshot(EPStatementAgentInstanceHandle createWindowStmtHandle, Viewable parent); + + /// Called each time a data window posts a remove stream event, to indicate that a data window remove an event as it expired according to a specified expiration policy. + /// to remove + /// the indexes to Update + public abstract void RemoveOldData(EventBean[] oldData, EventTableIndexRepository indexRepository); + } +} diff --git a/NEsper.Core/NEsper.Core/events/vaevent/VAERevisionProcessorDeclared.cs b/NEsper.Core/NEsper.Core/events/vaevent/VAERevisionProcessorDeclared.cs new file mode 100755 index 000000000..0394c1f20 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/vaevent/VAERevisionProcessorDeclared.cs @@ -0,0 +1,369 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.join.table; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.epl.named; +using com.espertech.esper.view; + +namespace com.espertech.esper.events.vaevent +{ + /// + /// Provides overlay strategy for property group-based versioning. + /// + public class VAERevisionProcessorDeclared + : VAERevisionProcessorBase + , ValueAddEventProcessor + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly PropertyGroupDesc[] _groups; + private readonly EventType _baseEventType; + private readonly EventPropertyGetter[] _fullKeyGetters; + private readonly IDictionary _statePerKey; + + /// + /// Ctor. + /// + /// name + /// specification + /// for stop handling + /// for nested property handling + /// The event type id generator. + public VAERevisionProcessorDeclared(String revisionEventTypeName, RevisionSpec spec, StatementStopService statementStopService, EventAdapterService eventAdapterService, EventTypeIdGenerator eventTypeIdGenerator) + + : base(spec, revisionEventTypeName, eventAdapterService) + { + + // on statement stop, remove versions + statementStopService.StatementStopped += () => _statePerKey.Clear(); + + _statePerKey = new Dictionary().WithNullSupport(); + _baseEventType = spec.BaseEventType; + _fullKeyGetters = PropertyUtility.GetGetters(_baseEventType, spec.KeyPropertyNames); + + // sort non-key properties, removing keys + _groups = PropertyUtility.AnalyzeGroups(spec.ChangesetPropertyNames, spec.DeltaTypes, spec.DeltaNames); + IDictionary propertyDesc = CreatePropertyDescriptors(spec, _groups); + + TypeDescriptors = PropertyUtility.GetPerType(_groups, spec.ChangesetPropertyNames, spec.KeyPropertyNames); + EventTypeMetadata metadata = EventTypeMetadata.CreateValueAdd(revisionEventTypeName, TypeClass.REVISION); + RevisionEventType = new RevisionEventType(metadata, eventTypeIdGenerator.GetTypeId(revisionEventTypeName), propertyDesc, eventAdapterService); + } + + public override EventBean GetValueAddEventBean(EventBean theEvent) + { + return new RevisionEventBeanDeclared(RevisionEventType, theEvent); + } + + public override void OnUpdate(EventBean[] newData, EventBean[] oldData, NamedWindowRootViewInstance namedWindowRootView, EventTableIndexRepository indexRepository) + { + // If new data is filled, it is not a delete + RevisionEventBeanDeclared revisionEvent; + Object key; + if ((newData == null) || (newData.Length == 0)) + { + // we are removing an event + revisionEvent = (RevisionEventBeanDeclared)oldData[0]; + key = revisionEvent.Key; + _statePerKey.Remove(key); + + // Insert into indexes for fast deletion, if there are any + foreach (EventTable table in indexRepository.GetTables()) + { + table.Remove(oldData); + } + + // make as not the latest event since its due for removal + revisionEvent.IsLatest = false; + + namedWindowRootView.UpdateChildren(null, oldData); + return; + } + + revisionEvent = (RevisionEventBeanDeclared)newData[0]; + EventBean underlyingEvent = revisionEvent.UnderlyingFullOrDelta; + EventType underyingEventType = underlyingEvent.EventType; + + // obtain key values + key = null; + RevisionTypeDesc typesDesc = null; + bool isBaseEventType = false; + if (underyingEventType == _baseEventType) + { + key = PropertyUtility.GetKeys(underlyingEvent, _fullKeyGetters); + isBaseEventType = true; + } + else + { + typesDesc = TypeDescriptors.Get(underyingEventType); + + // if this type cannot be found, check all supertypes, if any + if (typesDesc == null) + { + IEnumerable superTypes = underyingEventType.DeepSuperTypes; + if (superTypes != null) + { + foreach (var superType in superTypes) + { + if (superType == _baseEventType) + { + key = PropertyUtility.GetKeys(underlyingEvent, _fullKeyGetters); + isBaseEventType = true; + break; + } + typesDesc = TypeDescriptors.Get(superType); + if (typesDesc != null) + { + TypeDescriptors.Put(underyingEventType, typesDesc); + key = PropertyUtility.GetKeys(underlyingEvent, typesDesc.KeyPropertyGetters); + break; + } + } + } + } + else + { + key = PropertyUtility.GetKeys(underlyingEvent, typesDesc.KeyPropertyGetters); + } + } + + // get the state for this key value + RevisionStateDeclared revisionState = _statePerKey.Get(key); + + // Delta event and no full + if ((!isBaseEventType) && (revisionState == null)) + { + return; // Ignore the event, its a delta and we don't currently have a full event for it + } + + // New full event + if (revisionState == null) + { + revisionState = new RevisionStateDeclared(underlyingEvent, null, null); + _statePerKey.Put(key, revisionState); + + // prepare revison event + revisionEvent.LastBaseEvent = underlyingEvent; + revisionEvent.Key = key; + revisionEvent.Holders = null; + revisionEvent.IsLatest = true; + + // Insert into indexes for fast deletion, if there are any + foreach (EventTable table in indexRepository.GetTables()) + { + table.Add(newData); + } + + // post to data window + revisionState.LastEvent = revisionEvent; + namedWindowRootView.UpdateChildren(new EventBean[] { revisionEvent }, null); + return; + } + + // new version + long versionNumber = revisionState.IncRevisionNumber(); + + // Previously-seen full event + if (isBaseEventType) + { + revisionState.Holders = null; + revisionState.BaseEventUnderlying = underlyingEvent; + } + // Delta event to existing full event + else + { + var groupNum = typesDesc.Group.GroupNum; + var holders = revisionState.Holders; + if (holders == null) // optimization - the full event sets it to null, deltas all get a new one + { + holders = new RevisionBeanHolder[_groups.Length]; + } + else + { + holders = ArrayCopy(holders); // preserve the last revisions + } + + // add the new revision for a property group on top + holders[groupNum] = new RevisionBeanHolder(versionNumber, underlyingEvent, typesDesc.ChangesetPropertyGetters); + revisionState.Holders = holders; + } + + // prepare revision event + revisionEvent.LastBaseEvent = revisionState.BaseEventUnderlying; + revisionEvent.Holders = revisionState.Holders; + revisionEvent.Key = key; + revisionEvent.IsLatest = true; + + // get prior event + RevisionEventBeanDeclared lastEvent = revisionState.LastEvent; + lastEvent.IsLatest = false; + + // data to post + var newDataPost = new EventBean[] { revisionEvent }; + var oldDataPost = new EventBean[] { lastEvent }; + + // Update indexes + foreach (EventTable table in indexRepository.GetTables()) + { + table.Remove(oldDataPost); + table.Add(newDataPost); + } + + // keep reference to last event + revisionState.LastEvent = revisionEvent; + + namedWindowRootView.UpdateChildren(newDataPost, oldDataPost); + } + + public override ICollection GetSnapshot(EPStatementAgentInstanceHandle createWindowStmtHandle, Viewable parent) + { + using (createWindowStmtHandle.StatementAgentInstanceLock.AcquireReadLock()) + { + var it = parent.GetEnumerator(); + var list = new LinkedList(); + while (it.MoveNext()) + { + var fullRevision = (RevisionEventBeanDeclared)it.Current; + var key = fullRevision.Key; + var state = _statePerKey.Get(key); + list.AddLast(state.LastEvent); + } + return list; + } + } + + public override void RemoveOldData(EventBean[] oldData, EventTableIndexRepository indexRepository) + { + for (int i = 0; i < oldData.Length; i++) + { + var theEvent = (RevisionEventBeanDeclared)oldData[i]; + + // If the remove event is the latest event, remove from all caches + if (theEvent.IsLatest) + { + var key = theEvent.Key; + _statePerKey.Remove(key); + + foreach (EventTable table in indexRepository.GetTables()) + { + table.Remove(oldData); + } + } + } + } + + private static RevisionBeanHolder[] ArrayCopy(RevisionBeanHolder[] array) + { + if (array == null) + { + return null; + } + var result = new RevisionBeanHolder[array.Length]; + Array.Copy(array, 0, result, 0, array.Length); + return result; + } + + /// Creates property descriptors for revision. + /// specifies revision + /// the groups that group properties + /// map of property and descriptor + public static IDictionary CreatePropertyDescriptors(RevisionSpec spec, PropertyGroupDesc[] groups) + { + IDictionary propsPerGroup = PropertyUtility.GetGroupsPerProperty(groups); + + IDictionary propertyDesc = new Dictionary(); + int count = 0; + + foreach (String property in spec.ChangesetPropertyNames) + { + var fullGetter = spec.BaseEventType.GetGetter(property); + var propertyNumber = count; + var propGroupsProperty = propsPerGroup.Get(property); + var paramList = new RevisionGetterParameters(property, propertyNumber, fullGetter, propGroupsProperty); + + // if there are no groups (full event property only), then simply use the full event getter + EventPropertyGetter revisionGetter = new ProxyEventPropertyGetter( + eventBean => ((RevisionEventBeanDeclared)eventBean).GetVersionedValue(paramList), + eventBean => null, + eventBean => true); + + var type = spec.BaseEventType.GetPropertyType(property); + var propertyTypeDesc = new RevisionPropertyTypeDesc(revisionGetter, paramList, type); + propertyDesc.Put(property, propertyTypeDesc); + count++; + } + + foreach (String property in spec.BaseEventOnlyPropertyNames) + { + EventPropertyGetter fullGetter = spec.BaseEventType.GetGetter(property); + + // if there are no groups (full event property only), then simply use the full event getter + EventPropertyGetter revisionGetter = new ProxyEventPropertyGetter( + eventBean => + { + var riv = (RevisionEventBeanDeclared)eventBean; + var bean = riv.LastBaseEvent; + return bean == null ? null : fullGetter.Get(bean); + }, + eventBean => null, + eventBean => true); + + var type = spec.BaseEventType.GetPropertyType(property); + var propertyTypeDesc = new RevisionPropertyTypeDesc(revisionGetter, null, type); + propertyDesc.Put(property, propertyTypeDesc); + count++; + } + + count = 0; + foreach (String property in spec.KeyPropertyNames) + { + int keyPropertyNumber = count; + + EventPropertyGetter revisionGetter; + if (spec.KeyPropertyNames.Length == 1) + { + revisionGetter = new ProxyEventPropertyGetter + { + ProcGet = eventBean => ((RevisionEventBeanDeclared)eventBean).Key, + ProcIsExistsProperty = eventBean => true, + ProcGetFragment = eventBean => null + }; + } + else + { + revisionGetter = new ProxyEventPropertyGetter + { + ProcGet = eventBean => + { + var riv = (RevisionEventBeanDeclared)eventBean; + return ((MultiKeyUntyped)riv.Key).Keys[keyPropertyNumber]; + }, + ProcIsExistsProperty = eventBean => true, + ProcGetFragment = eventBean => null + }; + } + + var type = spec.BaseEventType.GetPropertyType(property); + var propertyTypeDesc = new RevisionPropertyTypeDesc(revisionGetter, null, type); + propertyDesc.Put(property, propertyTypeDesc); + count++; + } + + return propertyDesc; + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/vaevent/VAERevisionProcessorMerge.cs b/NEsper.Core/NEsper.Core/events/vaevent/VAERevisionProcessorMerge.cs new file mode 100755 index 000000000..6e4dbc68e --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/vaevent/VAERevisionProcessorMerge.cs @@ -0,0 +1,399 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.join.table; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.epl.named; +using com.espertech.esper.view; + +namespace com.espertech.esper.events.vaevent +{ + /// + /// Provides a set of merge-strategies for merging individual properties (rather then overlaying groups). + /// + public class VAERevisionProcessorMerge + : VAERevisionProcessorBase + , ValueAddEventProcessor + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly RevisionTypeDesc _infoFullType; + private readonly IDictionary _statePerKey; + private readonly UpdateStrategy _updateStrategy; + + /// + /// Ctor. + /// + /// name + /// specification + /// for stop handling + /// for nested property handling + /// The event type id generator. + public VAERevisionProcessorMerge(String revisioneventTypeName, RevisionSpec spec, StatementStopService statementStopService, EventAdapterService eventAdapterService, EventTypeIdGenerator eventTypeIdGenerator) + : base(spec, revisioneventTypeName, eventAdapterService) + { + // on statement stop, remove versions + statementStopService.StatementStopped += () => _statePerKey.Clear(); + _statePerKey = new Dictionary(); + + // For all changeset properties, add type descriptors (property number, getter etc) + var propertyDesc = new Dictionary(); + var count = 0; + + foreach (String property in spec.ChangesetPropertyNames) + { + var fullGetter = spec.BaseEventType.GetGetter(property); + var propertyNumber = count; + var paramList = new RevisionGetterParameters(property, propertyNumber, fullGetter, null); + + // if there are no groups (full event property only), then simply use the full event getter + EventPropertyGetter revisionGetter = new ProxyEventPropertyGetter( + eventBean => + { + var riv = (RevisionEventBeanMerge)eventBean; + return riv.GetVersionedValue(paramList); + }, + eventBean => null, + eventBean => true); + + var type = spec.BaseEventType.GetPropertyType(property); + if (type == null) + { + foreach (EventType deltaType in spec.DeltaTypes) + { + var dtype = deltaType.GetPropertyType(property); + if (dtype != null) + { + type = dtype; + break; + } + } + } + + var propertyTypeDesc = new RevisionPropertyTypeDesc(revisionGetter, paramList, type); + propertyDesc.Put(property, propertyTypeDesc); + count++; + } + + count = 0; + foreach (String property in spec.KeyPropertyNames) + { + var keyPropertyNumber = count; + EventPropertyGetter revisionGetter; + if (spec.KeyPropertyNames.Length == 1) + { + revisionGetter = new ProxyEventPropertyGetter + { + ProcGet = eventBean => ((RevisionEventBeanMerge)eventBean).Key, + ProcIsExistsProperty = eventBean => true, + ProcGetFragment = eventBean => null + }; + } + else + { + revisionGetter = new ProxyEventPropertyGetter + { + ProcGet = eventBean => + { + var riv = (RevisionEventBeanMerge)eventBean; + return ((MultiKeyUntyped)riv.Key).Keys[keyPropertyNumber]; + }, + ProcIsExistsProperty = eventBean => true, + ProcGetFragment = eventBean => null + }; + } + + var type = spec.BaseEventType.GetPropertyType(property); + if (type == null) + { + foreach (EventType deltaType in spec.DeltaTypes) + { + var dtype = deltaType.GetPropertyType(property); + if (dtype != null) + { + type = dtype; + break; + } + } + } + var propertyTypeDesc = new RevisionPropertyTypeDesc(revisionGetter, null, type); + propertyDesc.Put(property, propertyTypeDesc); + count++; + } + + // compile for each event type a list of getters and indexes within the overlay + foreach (EventType deltaType in spec.DeltaTypes) + { + RevisionTypeDesc typeDesc = MakeTypeDesc(deltaType, spec.PropertyRevision); + TypeDescriptors.Put(deltaType, typeDesc); + } + _infoFullType = MakeTypeDesc(spec.BaseEventType, spec.PropertyRevision); + + // how to handle updates to a full event + if (spec.PropertyRevision == PropertyRevisionEnum.MERGE_DECLARED) + { + _updateStrategy = new UpdateStrategyDeclared(spec); + } + else if (spec.PropertyRevision == PropertyRevisionEnum.MERGE_NON_NULL) + { + _updateStrategy = new UpdateStrategyNonNull(spec); + } + else if (spec.PropertyRevision == PropertyRevisionEnum.MERGE_EXISTS) + { + _updateStrategy = new UpdateStrategyExists(spec); + } + else + { + throw new ArgumentException("Unknown revision type '" + spec.PropertyRevision + "'"); + } + + EventTypeMetadata metadata = EventTypeMetadata.CreateValueAdd(revisioneventTypeName, TypeClass.REVISION); + RevisionEventType = new RevisionEventType(metadata, eventTypeIdGenerator.GetTypeId(revisioneventTypeName), propertyDesc, eventAdapterService); + } + + public override EventBean GetValueAddEventBean(EventBean theEvent) + { + return new RevisionEventBeanMerge(RevisionEventType, theEvent); + } + + public override void OnUpdate(EventBean[] newData, EventBean[] oldData, NamedWindowRootViewInstance namedWindowRootView, EventTableIndexRepository indexRepository) + { + // If new data is filled, it is not a delete + RevisionEventBeanMerge revisionEvent; + Object key; + if ((newData == null) || (newData.Length == 0)) + { + // we are removing an event + revisionEvent = (RevisionEventBeanMerge)oldData[0]; + key = revisionEvent.Key; + _statePerKey.Remove(key); + + // Insert into indexes for fast deletion, if there are any + foreach (EventTable table in indexRepository.GetTables()) + { + table.Remove(oldData); + } + + // make as not the latest event since its due for removal + revisionEvent.IsLatest = false; + + namedWindowRootView.UpdateChildren(null, oldData); + return; + } + + revisionEvent = (RevisionEventBeanMerge)newData[0]; + EventBean underlyingEvent = revisionEvent.UnderlyingFullOrDelta; + EventType underyingEventType = underlyingEvent.EventType; + + // obtain key values + key = null; + RevisionTypeDesc typesDesc; + Boolean isBaseEventType = false; + if (underyingEventType == RevisionSpec.BaseEventType) + { + typesDesc = _infoFullType; + key = PropertyUtility.GetKeys(underlyingEvent, _infoFullType.KeyPropertyGetters); + isBaseEventType = true; + } + else + { + typesDesc = TypeDescriptors.Get(underyingEventType); + + // if this type cannot be found, check all supertypes, if any + if (typesDesc == null) + { + EventType[] superTypes = underyingEventType.DeepSuperTypes; + if (superTypes != null) + { + foreach (var superType in superTypes) + { + if (superType == RevisionSpec.BaseEventType) + { + typesDesc = _infoFullType; + key = PropertyUtility.GetKeys(underlyingEvent, _infoFullType.KeyPropertyGetters); + isBaseEventType = true; + break; + } + typesDesc = TypeDescriptors.Get(superType); + if (typesDesc != null) + { + TypeDescriptors.Put(underyingEventType, typesDesc); + key = PropertyUtility.GetKeys(underlyingEvent, typesDesc.KeyPropertyGetters); + break; + } + } + } + } + else + { + key = PropertyUtility.GetKeys(underlyingEvent, typesDesc.KeyPropertyGetters); + } + } + + // get the state for this key value + RevisionStateMerge revisionState = _statePerKey.Get(key); + + // Delta event and no full + if ((!isBaseEventType) && (revisionState == null)) + { + return; // Ignore the event, its a delta and we don't currently have a full event for it + } + + // New full event + if (revisionState == null) + { + revisionState = new RevisionStateMerge(underlyingEvent, null, null); + _statePerKey.Put(key, revisionState); + + // prepare revison event + revisionEvent.LastBaseEvent = underlyingEvent; + revisionEvent.Key = key; + revisionEvent.Overlay = null; + revisionEvent.IsLatest = true; + + // Insert into indexes for fast deletion, if there are any + foreach (EventTable table in indexRepository.GetTables()) + { + table.Add(newData); + } + + // post to data window + revisionState.LastEvent = revisionEvent; + namedWindowRootView.UpdateChildren(new EventBean[] { revisionEvent }, null); + return; + } + + // handle Update, changing revision state and event as required + _updateStrategy.HandleUpdate(isBaseEventType, revisionState, revisionEvent, typesDesc); + + // prepare revision event + revisionEvent.LastBaseEvent = revisionState.BaseEventUnderlying; + revisionEvent.Overlay = revisionState.Overlays; + revisionEvent.Key = key; + revisionEvent.IsLatest = true; + + // get prior event + RevisionEventBeanMerge lastEvent = revisionState.LastEvent; + lastEvent.IsLatest = false; + + // data to post + var newDataPost = new EventBean[] { revisionEvent }; + var oldDataPost = new EventBean[] { lastEvent }; + + // Update indexes + foreach (EventTable table in indexRepository.GetTables()) + { + table.Remove(oldDataPost); + table.Add(newDataPost); + } + + // keep reference to last event + revisionState.LastEvent = revisionEvent; + + namedWindowRootView.UpdateChildren(newDataPost, oldDataPost); + } + + public override ICollection GetSnapshot(EPStatementAgentInstanceHandle createWindowStmtHandle, Viewable parent) + { + using (createWindowStmtHandle.StatementAgentInstanceLock.AcquireReadLock()) + { + IEnumerator it = parent.GetEnumerator(); + if (!it.MoveNext()) + { + return new List(); + } + + var list = new LinkedList(); + do + { + var fullRevision = (RevisionEventBeanMerge)it.Current; + var key = fullRevision.Key; + var state = _statePerKey.Get(key); + list.AddLast(state.LastEvent); + } while (it.MoveNext()); + return list; + } + } + + public override void RemoveOldData(EventBean[] oldData, EventTableIndexRepository indexRepository) + { + foreach (EventBean anOldData in oldData) + { + var theEvent = (RevisionEventBeanMerge)anOldData; + + // If the remove event is the latest event, remove from all caches + if (theEvent.IsLatest) + { + var key = theEvent.Key; + _statePerKey.Remove(key); + + foreach (EventTable table in indexRepository.GetTables()) + { + table.Remove(oldData); + } + } + } + } + + private RevisionTypeDesc MakeTypeDesc(EventType eventType, PropertyRevisionEnum propertyRevision) + { + EventPropertyGetter[] keyPropertyGetters = PropertyUtility.GetGetters(eventType, RevisionSpec.KeyPropertyNames); + + var len = RevisionSpec.ChangesetPropertyNames.Length; + var listOfGetters = new List(); + var listOfIndexes = new List(); + + for (int i = 0; i < len; i++) + { + String propertyName = RevisionSpec.ChangesetPropertyNames[i]; + EventPropertyGetter getter = null; + + if (propertyRevision != PropertyRevisionEnum.MERGE_EXISTS) + { + getter = eventType.GetGetter(RevisionSpec.ChangesetPropertyNames[i]); + } + else + { + // only declared properties may be used a dynamic properties to avoid confusion of properties suddenly appearing + foreach (String propertyNamesDeclared in eventType.PropertyNames) + { + if (propertyNamesDeclared == propertyName) + { + // use dynamic properties + getter = eventType.GetGetter(RevisionSpec.ChangesetPropertyNames[i] + "?"); + break; + } + } + } + + if (getter != null) + { + listOfGetters.Add(getter); + listOfIndexes.Add(i); + } + } + + var changesetPropertyGetters = listOfGetters.ToArray(); + var changesetPropertyIndex = new int[listOfIndexes.Count]; + for (int i = 0; i < listOfIndexes.Count; i++) + { + changesetPropertyIndex[i] = listOfIndexes[i]; + } + + return new RevisionTypeDesc(keyPropertyGetters, changesetPropertyGetters, changesetPropertyIndex); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/vaevent/VAEVariantProcessor.cs b/NEsper.Core/NEsper.Core/events/vaevent/VAEVariantProcessor.cs new file mode 100755 index 000000000..7a58a5a10 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/vaevent/VAEVariantProcessor.cs @@ -0,0 +1,129 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.epl.named; +using com.espertech.esper.view; + +namespace com.espertech.esper.events.vaevent +{ + /// + /// Represents a variant event stream, allowing events of disparate event types to be treated + /// polymophically. + /// + public class VAEVariantProcessor : ValueAddEventProcessor + { + /// Specification for the variant stream. + protected readonly VariantSpec VariantSpec; + + /// The event type representing the variant stream. + protected VariantEventType VariantEventType; + + /// + /// Ctor. + /// + /// specifies how to handle the disparate events + /// The event type id generator. + /// The config. + public VAEVariantProcessor(VariantSpec variantSpec, EventTypeIdGenerator eventTypeIdGenerator, ConfigurationVariantStream config) + { + VariantSpec = variantSpec; + + VariantPropResolutionStrategy strategy; + if (variantSpec.TypeVariance == TypeVarianceEnum.ANY) + { + strategy = new VariantPropResolutionStrategyAny(variantSpec); + } + else + { + strategy = new VariantPropResolutionStrategyDefault(variantSpec); + } + + EventTypeMetadata metadata = EventTypeMetadata.CreateValueAdd(variantSpec.VariantStreamName, TypeClass.VARIANT); + VariantEventType = new VariantEventType(metadata, eventTypeIdGenerator.GetTypeId(variantSpec.VariantStreamName), variantSpec, strategy, config); + } + + public EventType ValueAddEventType + { + get { return VariantEventType; } + } + + public void ValidateEventType(EventType eventType) + { + if (VariantSpec.TypeVariance == TypeVarianceEnum.ANY) + { + return; + } + + if (eventType == null) + { + throw new ExprValidationException(GetMessage()); + } + + // try each permitted type + if (VariantSpec.EventTypes.Any(variant => variant == eventType)) + { + return; + } + + // test if any of the supertypes of the eventtype is a variant type + foreach (EventType variant in VariantSpec.EventTypes) + { + // Check all the supertypes to see if one of the matches the full or delta types + IEnumerable deepSupers = eventType.DeepSuperTypes; + if (deepSupers == null) + { + continue; + } + + foreach (var superType in deepSupers) + { + if (superType == variant) + { + return; + } + } + } + + throw new ExprValidationException(GetMessage()); + } + + public EventBean GetValueAddEventBean(EventBean theEvent) + { + return new VariantEventBean(VariantEventType, theEvent); + } + + public void OnUpdate(EventBean[] newData, EventBean[] oldData, NamedWindowRootViewInstance namedWindowRootView, EventTableIndexRepository indexRepository) + { + throw new UnsupportedOperationException(); + } + + public ICollection GetSnapshot(EPStatementAgentInstanceHandle createWindowStmtHandle, Viewable parent) + { + throw new UnsupportedOperationException(); + } + + public void RemoveOldData(EventBean[] oldData, EventTableIndexRepository indexRepository) + { + throw new UnsupportedOperationException(); + } + + private String GetMessage() + { + return "Selected event type is not a valid event type of the variant stream '" + VariantSpec.VariantStreamName + "'"; + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/vaevent/ValueAddEventProcessor.cs b/NEsper.Core/NEsper.Core/events/vaevent/ValueAddEventProcessor.cs new file mode 100755 index 000000000..529eb3f6e --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/vaevent/ValueAddEventProcessor.cs @@ -0,0 +1,76 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.epl.named; +using com.espertech.esper.view; + +namespace com.espertech.esper.events.vaevent +{ + /// + /// Interface for a processor of base and delta events in a revision event type. + /// + public interface ValueAddEventProcessor + { + /// + /// Returns the event type that this revision processor generates. + /// + /// + /// event type + /// + EventType ValueAddEventType { get; } + + /// + /// For use in checking insert-into statements, validates that the given type is eligible for revision event. + /// + /// the type of the event participating in revision event type (or not) + /// ExprValidationException if the validation fails + void ValidateEventType(EventType eventType); + + /// + /// For use in executing an insert-into, wraps the given event applying the revision event type, + /// but not yet computing a new revision. + /// + /// to wrap + /// + /// revision event bean + /// + EventBean GetValueAddEventBean(EventBean theEvent); + + /// + /// Upon new events arriving into a named window (new data), and upon events being deleted via on-delete (old data), + /// Update child views of the root view and apply to index repository as required (fast deletion). + /// + /// new events + /// remove stream + /// the root view + /// delete and select indexes + void OnUpdate(EventBean[] newData, EventBean[] oldData, NamedWindowRootViewInstance namedWindowRootView, EventTableIndexRepository indexRepository); + + /// + /// Handle iteration over revision event contents. + /// + /// statement handle for safe iteration + /// the provider of data + /// + /// collection to iterate + /// + ICollection GetSnapshot(EPStatementAgentInstanceHandle createWindowStmtHandle, Viewable parent); + + /// + /// Called each time a data window posts a remove stream event, to indicate that a data window remove + /// an event as it expired according to a specified expiration policy. + /// + /// to remove + /// the indexes to Update + void RemoveOldData(EventBean[] oldData, EventTableIndexRepository indexRepository); + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/events/vaevent/ValueAddEventService.cs b/NEsper.Core/NEsper.Core/events/vaevent/ValueAddEventService.cs new file mode 100755 index 000000000..d6c37e7e5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/vaevent/ValueAddEventService.cs @@ -0,0 +1,90 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.view; + +namespace com.espertech.esper.events.vaevent +{ + /// + /// Service associating handling vaue-added event types, such a revision event types and variant stream event types. + /// + /// Associates named windows and revision event types. + public interface ValueAddEventService + { + /// + /// Called at initialization time, verifies configurations provided. + /// + /// is the revision types to add + /// is the variant streams to add + /// for obtaining event type information for each name + /// The event type id generator. + void Init(IDictionary revisionTypes, + IDictionary variantStreams, + EventAdapterService eventAdapterService, + EventTypeIdGenerator eventTypeIdGenerator); + + /// Adds a new revision event types. + /// to add + /// the revision event type configuration + /// for obtaining event type information for each name + void AddRevisionEventType(String name, + ConfigurationRevisionEventType config, + EventAdapterService eventAdapterService); + + /// + /// Adds a new variant stream. + /// + /// the name of the type + /// the configs + /// for handling nested events + /// The event type id generator. + /// ConfigurationException if the configuration is invalid + void AddVariantStream(String variantEventTypeName, + ConfigurationVariantStream variantStreamConfig, + EventAdapterService eventAdapterService, + EventTypeIdGenerator eventTypeIdGenerator); + + /// Upon named window creation, and during resolution of type specified as part of a named window create statement, returns looks up the revision event type name provided and return the revision event type if found, or null if not found. + /// to look up + /// null if not found, of event type + EventType GetValueAddUnderlyingType(String name); + + /// + /// Upon named window creation, create a unique revision event type that this window processes. + /// + /// name of window + /// name to use + /// for handling stops + /// for event type INFO + /// The event type id generator. + /// revision event type + EventType CreateRevisionType(String namedWindowName, + String typeName, + StatementStopService statementStopService, + EventAdapterService eventAdapterService, + EventTypeIdGenerator eventTypeIdGenerator); + + /// Upon named window creation, check if the name used is a revision event type name. + /// to check + /// true if revision event type, false if not + bool IsRevisionTypeName(String name); + + /// Gets a value-added event processor. + /// of the value-add events + /// processor + ValueAddEventProcessor GetValueAddProcessor(String name); + + /// Returns all event types representing value-add event types. + /// value-add event type + EventType[] ValueAddedTypes { get; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/events/vaevent/ValueAddEventServiceImpl.cs b/NEsper.Core/NEsper.Core/events/vaevent/ValueAddEventServiceImpl.cs new file mode 100755 index 000000000..37770c32d --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/vaevent/ValueAddEventServiceImpl.cs @@ -0,0 +1,332 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.view; + +namespace com.espertech.esper.events.vaevent +{ + /// + /// Service for handling revision event types. + /// + /// Each named window instance gets a dedicated revision processor. + /// + public class ValueAddEventServiceImpl : ValueAddEventService + { + /// Map of revision event name and revision compiled specification. + protected readonly IDictionary SpecificationsByRevisionName; + + /// Map of named window name and processor. + protected readonly IDictionary ProcessorsByNamedWindow; + + /// Map of revision event stream and variant stream processor. + protected readonly IDictionary VariantProcessors; + + /// Ctor. + public ValueAddEventServiceImpl() + { + SpecificationsByRevisionName = new Dictionary().WithNullSupport(); + ProcessorsByNamedWindow = new Dictionary().WithNullSupport(); + VariantProcessors = new Dictionary().WithNullSupport(); + } + + public EventType[] ValueAddedTypes + { + get + { + var types = ProcessorsByNamedWindow.Select(revisionNamedWindow => revisionNamedWindow.Value.ValueAddEventType).ToList(); + types.AddRange(VariantProcessors.Select(variantProcessor => variantProcessor.Value.ValueAddEventType)); + + return types.ToArray(); + } + } + + public void Init(IDictionary configRevision, IDictionary configVariant, EventAdapterService eventAdapterService, EventTypeIdGenerator eventTypeIdGenerator) + { + foreach (KeyValuePair entry in configRevision) + { + AddRevisionEventType(entry.Key, entry.Value, eventAdapterService); + } + foreach (KeyValuePair entry in configVariant) + { + AddVariantStream(entry.Key, entry.Value, eventAdapterService, eventTypeIdGenerator); + } + } + + public void AddRevisionEventType(String revisioneventTypeName, ConfigurationRevisionEventType config, EventAdapterService eventAdapterService) + { + RevisionSpec specification = ValidateRevision(revisioneventTypeName, config, eventAdapterService); + SpecificationsByRevisionName.Put(revisioneventTypeName, specification); + } + + public void AddVariantStream(String variantStreamname, ConfigurationVariantStream variantStreamConfig, EventAdapterService eventAdapterService, EventTypeIdGenerator eventTypeIdGenerator) + { + var variantSpec = ValidateVariantStream(variantStreamname, variantStreamConfig, eventAdapterService); + var processor = new VAEVariantProcessor(variantSpec, eventTypeIdGenerator, variantStreamConfig); + eventAdapterService.AddTypeByName(variantStreamname, processor.ValueAddEventType); + VariantProcessors.Put(variantStreamname, processor); + } + + /// Validate the variant stream definition. + /// the stream name + /// the configuration information + /// the event adapters + /// specification for variant streams + public static VariantSpec ValidateVariantStream(String variantStreamname, ConfigurationVariantStream variantStreamConfig, EventAdapterService eventAdapterService) + { + if (variantStreamConfig.TypeVariance == TypeVarianceEnum.PREDEFINED) + { + if (variantStreamConfig.VariantTypeNames.IsEmpty()) + { + throw new ConfigurationException("Invalid variant stream configuration, no event type name has been added and default type variance requires at least one type, for name '" + variantStreamname + "'"); + } + } + + ICollection types = new LinkedHashSet(); + foreach (String typeName in variantStreamConfig.VariantTypeNames) + { + EventType type = eventAdapterService.GetEventTypeByName(typeName); + if (type == null) + { + throw new ConfigurationException("Event type by name '" + typeName + "' could not be found for use in variant stream configuration by name '" + variantStreamname + "'"); + } + types.Add(type); + } + + EventType[] eventTypes = types.ToArray(); + return new VariantSpec(variantStreamname, eventTypes, variantStreamConfig.TypeVariance); + } + + public EventType CreateRevisionType(String namedWindowName, String name, StatementStopService statementStopService, EventAdapterService eventAdapterService, EventTypeIdGenerator eventTypeIdGenerator) + { + RevisionSpec spec = SpecificationsByRevisionName.Get(name); + ValueAddEventProcessor processor; + if (spec.PropertyRevision == PropertyRevisionEnum.OVERLAY_DECLARED) + { + processor = new VAERevisionProcessorDeclared(name, spec, statementStopService, eventAdapterService, eventTypeIdGenerator); + } + else + { + processor = new VAERevisionProcessorMerge(name, spec, statementStopService, eventAdapterService, eventTypeIdGenerator); + } + + ProcessorsByNamedWindow.Put(namedWindowName, processor); + return processor.ValueAddEventType; + } + + public ValueAddEventProcessor GetValueAddProcessor(String name) + { + ValueAddEventProcessor proc = ProcessorsByNamedWindow.Get(name); + if (proc != null) + { + return proc; + } + return VariantProcessors.Get(name); + } + + public EventType GetValueAddUnderlyingType(String name) + { + RevisionSpec spec = SpecificationsByRevisionName.Get(name); + if (spec == null) + { + return null; + } + return spec.BaseEventType; + } + + public bool IsRevisionTypeName(String revisionTypeName) + { + return SpecificationsByRevisionName.ContainsKey(revisionTypeName); + } + + /// Valiate the revision configuration. + /// name of revision types + /// configures revision type + /// event adapters + /// revision specification + /// ConfigurationException if the configs are invalid + internal static RevisionSpec ValidateRevision(String revisioneventTypeName, ConfigurationRevisionEventType config, EventAdapterService eventAdapterService) + { + if ((config.NameBaseEventTypes == null) || (config.NameBaseEventTypes.Count == 0)) + { + throw new ConfigurationException("Required base event type name is not set in the configuration for revision event type '" + revisioneventTypeName + "'"); + } + + if (config.NameBaseEventTypes.Count > 1) + { + throw new ConfigurationException("Only one base event type name may be added to revision event type '" + revisioneventTypeName + "', multiple base types are not yet supported"); + } + + // get base types + String baseeventTypeName = config.NameBaseEventTypes.FirstOrDefault(); + EventType baseEventType = eventAdapterService.GetEventTypeByName(baseeventTypeName); + if (baseEventType == null) + { + throw new ConfigurationException("Could not locate event type for name '" + baseeventTypeName + "' in the configuration for revision event type '" + revisioneventTypeName + "'"); + } + + // get name types + var deltaTypes = new EventType[config.NameDeltaEventTypes.Count]; + var deltaNames = new String[config.NameDeltaEventTypes.Count]; + int count = 0; + foreach (String deltaName in config.NameDeltaEventTypes) + { + EventType deltaEventType = eventAdapterService.GetEventTypeByName(deltaName); + if (deltaEventType == null) + { + throw new ConfigurationException("Could not locate event type for name '" + deltaName + "' in the configuration for revision event type '" + revisioneventTypeName + "'"); + } + deltaTypes[count] = deltaEventType; + deltaNames[count] = deltaName; + count++; + } + + // the key properties must be set + if ((config.KeyPropertyNames == null) || (config.KeyPropertyNames.Length == 0)) + { + throw new ConfigurationException("Required key properties are not set in the configuration for revision event type '" + revisioneventTypeName + "'"); + } + + // make sure the key properties exist the base type and all delta types + CheckKeysExist(baseEventType, baseeventTypeName, config.KeyPropertyNames, revisioneventTypeName); + for (int i = 0; i < deltaTypes.Length; i++) + { + CheckKeysExist(deltaTypes[i], deltaNames[i], config.KeyPropertyNames, revisioneventTypeName); + } + + // key property names shared between base and delta must have the same type + String[] keyPropertyNames = PropertyUtility.CopyAndSort(config.KeyPropertyNames); + foreach (String key in keyPropertyNames) + { + var typeProperty = baseEventType.GetPropertyType(key); + foreach (EventType dtype in deltaTypes) + { + var dtypeProperty = dtype.GetPropertyType(key); + if ((dtypeProperty != null) && (typeProperty != dtypeProperty)) + { + throw new ConfigurationException("Key property named '" + key + "' does not have the same type for base and delta types of revision event type '" + revisioneventTypeName + "'"); + } + } + } + + // In the "declared" type the change set properties consist of only : + // (base event type properties) minus (key properties) minus (properties only on base event type) + if (config.PropertyRevision == PropertyRevisionEnum.OVERLAY_DECLARED) + { + // determine non-key properties: those overridden by any delta, and those simply only present on the base event type + String[] nonkeyPropertyNames = PropertyUtility.UniqueExclusiveSort(baseEventType.PropertyNames, keyPropertyNames); + ICollection baseEventOnlyProperties = new HashSet(); + ICollection changesetPropertyNames = new HashSet(); + foreach (String nonKey in nonkeyPropertyNames) + { + var overriddenProperty = false; + foreach (EventType type in deltaTypes) + { + if (type.IsProperty(nonKey)) + { + changesetPropertyNames.Add(nonKey); + overriddenProperty = true; + break; + } + } + if (!overriddenProperty) + { + baseEventOnlyProperties.Add(nonKey); + } + } + + String[] changesetProperties = changesetPropertyNames.ToArray(); + String[] baseEventOnlyPropertyNames = baseEventOnlyProperties.ToArray(); + + // verify that all changeset properties match event type + foreach (String changesetProperty in changesetProperties) + { + var typeProperty = baseEventType.GetPropertyType(changesetProperty); + foreach (EventType dtype in deltaTypes) + { + var dtypeProperty = dtype.GetPropertyType(changesetProperty); + if ((dtypeProperty != null) && (typeProperty != dtypeProperty)) + { + throw new ConfigurationException("Property named '" + changesetProperty + "' does not have the same type for base and delta types of revision event type '" + revisioneventTypeName + "'"); + } + } + } + + return new RevisionSpec(config.PropertyRevision, baseEventType, deltaTypes, deltaNames, keyPropertyNames, changesetProperties, baseEventOnlyPropertyNames, false, null); + } + else + { + // In the "exists" type the change set properties consist of all properties: base event properties plus delta types properties + ICollection allProperties = new HashSet(); + allProperties.AddAll(baseEventType.PropertyNames); + foreach (EventType deltaType in deltaTypes) + { + allProperties.AddAll(deltaType.PropertyNames); + } + + String[] allPropertiesArr = allProperties.ToArray(); + String[] changesetProperties = PropertyUtility.UniqueExclusiveSort(allPropertiesArr, keyPropertyNames); + + // All properties must have the same type, if a property exists for any given type + bool hasContributedByDelta = false; + bool[] contributedByDelta = new bool[changesetProperties.Length]; + count = 0; + foreach (String property in changesetProperties) + { + Type basePropertyType = baseEventType.GetPropertyType(property); + Type typeTemp = null; + if (basePropertyType != null) + { + typeTemp = basePropertyType; + } + else + { + hasContributedByDelta = true; + contributedByDelta[count] = true; + } + foreach (EventType dtype in deltaTypes) + { + Type dtypeProperty = dtype.GetPropertyType(property); + if (dtypeProperty != null) + { + if ((typeTemp != null) && (dtypeProperty != typeTemp)) + { + throw new ConfigurationException("Property named '" + property + "' does not have the same type for base and delta types of revision event type '" + revisioneventTypeName + "'"); + } + + } + typeTemp = dtypeProperty; + } + count++; + } + + // Compile changeset + return new RevisionSpec(config.PropertyRevision, baseEventType, deltaTypes, deltaNames, keyPropertyNames, changesetProperties, new String[0], hasContributedByDelta, contributedByDelta); + } + } + + private static void CheckKeysExist(EventType baseEventType, String name, IEnumerable keyProperties, String revisioneventTypeName) + { + IList propertyNames = baseEventType.PropertyNames; + foreach (var keyProperty in keyProperties) + { + var property = keyProperty; + var exists = propertyNames.Any(propertyName => propertyName == property); + if (!exists) + { + throw new ConfigurationException("Key property '" + keyProperty + "' as defined in the configuration for revision event type '" + revisioneventTypeName + "' does not exists in event type '" + name + "'"); + } + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/vaevent/VariantEvent.cs b/NEsper.Core/NEsper.Core/events/vaevent/VariantEvent.cs new file mode 100755 index 000000000..1c128c15d --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/vaevent/VariantEvent.cs @@ -0,0 +1,22 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.vaevent +{ + /// + /// A variant event is a type that can represent many event types. + /// + public interface VariantEvent + { + /// Returns the underlying event. + /// underlying event + EventBean UnderlyingEventBean { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/events/vaevent/VariantEventBean.cs b/NEsper.Core/NEsper.Core/events/vaevent/VariantEventBean.cs new file mode 100755 index 000000000..370226f87 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/vaevent/VariantEventBean.cs @@ -0,0 +1,81 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.vaevent +{ + /// + /// An event bean that represents multiple potentially disparate underlying events and presents a unified face + /// across each such types or even any type. + /// + public class VariantEventBean + : EventBean + , VariantEvent + { + private readonly VariantEventType _variantEventType; + private readonly EventBean _underlyingEventBean; + + /// + /// Ctor. + /// + /// the event type + /// the event + public VariantEventBean(VariantEventType variantEventType, EventBean underlying) + { + _variantEventType = variantEventType; + _underlyingEventBean = underlying; + } + + public EventType EventType + { + get { return _variantEventType; } + } + + public Object Get(string property) + { + EventPropertyGetter getter = _variantEventType.GetGetter(property); + if (getter == null) + { + return null; + } + return getter.Get(this); + } + + public object this[string property] + { + get { return Get(property); } + } + + public object Underlying + { + get { return _underlyingEventBean.Underlying; } + } + + /// + /// Returns the underlying event. + /// + /// underlying event + public EventBean UnderlyingEventBean + { + get { return _underlyingEventBean; } + } + + public Object GetFragment(string propertyExpression) + { + EventPropertyGetter getter = _variantEventType.GetGetter(propertyExpression); + if (getter == null) + { + throw PropertyAccessException.NotAValidProperty(propertyExpression); + } + return getter.GetFragment(this); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/events/vaevent/VariantEventType.cs b/NEsper.Core/NEsper.Core/events/vaevent/VariantEventType.cs new file mode 100755 index 000000000..1631eefc0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/vaevent/VariantEventType.cs @@ -0,0 +1,252 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.util; + +namespace com.espertech.esper.events.vaevent +{ + /// + /// Event type for variant event streams. + /// + /// Caches properties after having resolved a property via a resolution strategy. + /// + public class VariantEventType : EventTypeSPI + { + private readonly EventTypeMetadata _metadata; + private readonly EventType[] _variants; + private readonly VariantPropResolutionStrategy _propertyResStrategy; + private readonly IDictionary _propertyDesc; + private readonly String[] _propertyNames; + private readonly EventPropertyDescriptor[] _propertyDescriptors; + private readonly IDictionary _propertyDescriptorMap; + private readonly int _eventTypeId; + private readonly ConfigurationVariantStream _config; + + /// + /// Ctor. + /// + /// event type metadata + /// The event type id. + /// the variant specification + /// stragegy for resolving properties + /// The config. + public VariantEventType(EventTypeMetadata metadata, int eventTypeId, VariantSpec variantSpec, VariantPropResolutionStrategy propertyResStrategy, ConfigurationVariantStream config) + { + _metadata = metadata; + _eventTypeId = eventTypeId; + _variants = variantSpec.EventTypes; + _propertyResStrategy = propertyResStrategy; + _config = config; + + _propertyDesc = new Dictionary(); + + foreach (EventType type in _variants) + { + IList properties = type.PropertyNames; + properties = PropertyUtility.CopyAndSort(properties); + foreach (String property in properties) + { + if (!_propertyDesc.ContainsKey(property)) + { + FindProperty(property); + } + } + } + + ICollection propertyNameKeySet = _propertyDesc.Keys; + _propertyNames = propertyNameKeySet.ToArray(); + + // for each of the properties in each type, attempt to load the property to build a property list + _propertyDescriptors = new EventPropertyDescriptor[_propertyDesc.Count]; + _propertyDescriptorMap = new Dictionary(); + int count = 0; + foreach (var desc in _propertyDesc) + { + var type = desc.Value.PropertyType; + var indexType = type.GetIndexType(); + var isIndexed = indexType != null; + var descriptor = new EventPropertyDescriptor(desc.Key, type, indexType, false, false, isIndexed, false, desc.Value.PropertyType.IsFragmentableType()); + _propertyDescriptors[count++] = descriptor; + _propertyDescriptorMap.Put(desc.Key, descriptor); + } + } + + public string StartTimestampPropertyName + { + get { return null; } + } + + public string EndTimestampPropertyName + { + get { return null; } + } + + public Type GetPropertyType(String property) + { + VariantPropertyDesc entry = _propertyDesc.Get(property); + if (entry != null) + { + return entry.PropertyType; + } + entry = FindProperty(property); + if (entry != null) + { + return entry.PropertyType; + } + return null; + } + + public Type UnderlyingType + { + get { return typeof (Object); } + } + + public string Name + { + get { return _metadata.PublicName; } + } + + public int EventTypeId + { + get { return _eventTypeId; } + } + + public ConfigurationVariantStream Config + { + get { return _config; } + } + + public EventPropertyGetter GetGetter(String property) + { + VariantPropertyDesc entry = _propertyDesc.Get(property); + if (entry != null) + { + return entry.Getter; + } + entry = FindProperty(property); + if (entry != null) + { + return entry.Getter; + } + return null; + } + + public string[] PropertyNames + { + get { return _propertyNames; } + } + + public bool IsProperty(String property) + { + VariantPropertyDesc entry = _propertyDesc.Get(property); + if (entry != null) + { + return entry.IsProperty; + } + entry = FindProperty(property); + if (entry != null) + { + return entry.IsProperty; + } + return false; + } + + public EventType[] SuperTypes + { + get { return null; } + } + + public EventType[] DeepSuperTypes + { + get { return null; } + } + + private VariantPropertyDesc FindProperty(String propertyName) + { + var desc = _propertyResStrategy.ResolveProperty(propertyName, _variants); + if (desc != null) + { + _propertyDesc.Put(propertyName, desc); + } + + return desc; + } + + public EventTypeMetadata Metadata + { + get { return _metadata; } + } + + public IList PropertyDescriptors + { + get { return _propertyDescriptors; } + } + + public EventPropertyDescriptor GetPropertyDescriptor(String propertyName) + { + return _propertyDescriptorMap.Get(propertyName); + } + + public FragmentEventType GetFragmentType(String property) + { + return null; + } + + public EventPropertyWriter GetWriter(String propertyName) + { + return null; + } + + public EventPropertyDescriptor GetWritableProperty(String propertyName) + { + return null; + } + + public EventPropertyDescriptor[] WriteableProperties + { + get { return new EventPropertyDescriptor[0]; } + } + + public EventBeanCopyMethod GetCopyMethod(String[] properties) + { + return null; + } + + public EventBeanWriter GetWriter(String[] properties) + { + return null; + } + + public EventBeanReader Reader + { + get { return null; } + } + + public EventPropertyGetterMapped GetGetterMapped(String mappedProperty) + { + return null; + } + + public EventPropertyGetterIndexed GetGetterIndexed(string indexedProperty) + { + return null; + } + + public bool EqualsCompareType(EventType eventType) + { + return this == eventType; + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/vaevent/VariantPropResolutionStrategy.cs b/NEsper.Core/NEsper.Core/events/vaevent/VariantPropResolutionStrategy.cs new file mode 100755 index 000000000..545fe93bc --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/vaevent/VariantPropResolutionStrategy.cs @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using com.espertech.esper.client; + +namespace com.espertech.esper.events.vaevent +{ + /// + /// Strategy for resolving a property against any of the variant types. + /// + public interface VariantPropResolutionStrategy + { + /// + /// Resolve the property for each of the types. + /// + /// to resolve + /// the variants to resolve the property for + /// property descriptor + VariantPropertyDesc ResolveProperty(String propertyName, EventType[] variants); + } +} diff --git a/NEsper.Core/NEsper.Core/events/vaevent/VariantPropResolutionStrategyAny.cs b/NEsper.Core/NEsper.Core/events/vaevent/VariantPropResolutionStrategyAny.cs new file mode 100755 index 000000000..67ab9030d --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/vaevent/VariantPropResolutionStrategyAny.cs @@ -0,0 +1,61 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using com.espertech.esper.client; + +namespace com.espertech.esper.events.vaevent +{ + /// + /// A property resolution strategy that allows any type, wherein all properties + /// are Object type. + /// + public class VariantPropResolutionStrategyAny : VariantPropResolutionStrategy + { + private int currentPropertyNumber; + private readonly VariantPropertyGetterCache propertyGetterCache; + + /// Ctor. + /// specified the preconfigured types + public VariantPropResolutionStrategyAny(VariantSpec variantSpec) + { + propertyGetterCache = new VariantPropertyGetterCache(variantSpec.EventTypes); + } + + public VariantPropertyDesc ResolveProperty(String propertyName, EventType[] variants) + { + // property numbers should start at zero since the serve as array index + int assignedPropertyNumber = currentPropertyNumber; + currentPropertyNumber++; + propertyGetterCache.AddGetters(assignedPropertyNumber, propertyName); + + EventPropertyGetter getter = new ProxyEventPropertyGetter( + delegate(EventBean eventBean) { + var variant = (VariantEvent) eventBean; + var _getter = propertyGetterCache.GetGetter(assignedPropertyNumber, variant.UnderlyingEventBean.EventType); + if (_getter == null) { + return null; + } + return _getter.Get(variant.UnderlyingEventBean); + }, + delegate { + return null; // no fragments provided as the type is not known in advance + }, + delegate(EventBean eventBean) { + var variant = (VariantEvent)eventBean; + var _getter = propertyGetterCache.GetGetter(assignedPropertyNumber, variant.UnderlyingEventBean.EventType); + if (_getter == null) { + return false; + } + return _getter.IsExistsProperty(variant.UnderlyingEventBean); + }); + + return new VariantPropertyDesc(typeof(Object), getter, true); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/vaevent/VariantPropResolutionStrategyDefault.cs b/NEsper.Core/NEsper.Core/events/vaevent/VariantPropResolutionStrategyDefault.cs new file mode 100755 index 000000000..51fc80a6e --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/vaevent/VariantPropResolutionStrategyDefault.cs @@ -0,0 +1,215 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.util; + +namespace com.espertech.esper.events.vaevent +{ + /// + /// A property resolution strategy that allows only the preconfigured types, wherein + /// all properties that are common (name and type) to all properties are considered. + /// + public class VariantPropResolutionStrategyDefault : VariantPropResolutionStrategy + { + private int currentPropertyNumber; + private readonly VariantPropertyGetterCache propertyGetterCache; + + /// + /// Ctor. + /// + /// specified the preconfigured types + public VariantPropResolutionStrategyDefault(VariantSpec variantSpec) + { + propertyGetterCache = new VariantPropertyGetterCache(variantSpec.EventTypes); + } + + public VariantPropertyDesc ResolveProperty(String propertyName, EventType[] variants) + { + bool existsInAll = true; + Type commonType = null; + bool mustCoerce = false; + for (int i = 0; i < variants.Length; i++) + { + Type type = variants[i].GetPropertyType(propertyName); //.GetBoxedType(); + if (type == null) + { + existsInAll = false; + continue; + } + + if (commonType == null) + { + commonType = type; + continue; + } + + // compare types + if (type == commonType) + { + continue; + } + + if (type.GetBoxedType() == commonType.GetBoxedType()) + { + commonType = commonType.GetBoxedType(); + continue; + } + + // coercion + if (type.IsNumeric()) + { + if (TypeHelper.CanCoerce(type, commonType)) + { + mustCoerce = true; + continue; + } + if (TypeHelper.CanCoerce(commonType, type)) + { + mustCoerce = true; + commonType = type; + } + } + else if (commonType == typeof(Object)) + { + continue; + } + // common interface or base class + else if (!type.IsBuiltinDataType()) + { + var supersForType = new FIFOHashSet(); + TypeHelper.GetBase(type, supersForType); + supersForType.Remove(typeof(Object)); + + if (supersForType.Contains(commonType)) + { + continue; // type, or : common type + } + if (TypeHelper.IsSubclassOrImplementsInterface(commonType, type)) + { + commonType = type; // common type : type + continue; + } + + // find common interface or type both implement + var supersForCommonType = new FIFOHashSet(); + TypeHelper.GetBase(commonType, supersForCommonType); + supersForCommonType.Remove(typeof(Object)); + + // Take common classes first, ignoring interfaces + bool found = false; + foreach (Type superClassType in supersForType) + { + if (!superClassType.IsInterface && (supersForCommonType.Contains(superClassType))) + { + break; + } + } + if (found) + { + continue; + } + // Take common interfaces + foreach (var superClassType in supersForType) + { + if (superClassType.IsInterface && supersForCommonType.Contains(superClassType)) + { + commonType = superClassType; + found = true; + break; + } + } + } + + commonType = typeof(Object); + } + + if (!existsInAll) + { + return null; + } + + if (commonType == null) + { + return null; + } + + // property numbers should start at zero since the serve as array index + var assignedPropertyNumber = currentPropertyNumber; + currentPropertyNumber++; + propertyGetterCache.AddGetters(assignedPropertyNumber, propertyName); + + EventPropertyGetter getter; + if (mustCoerce) + { + SimpleTypeCaster caster = SimpleTypeCasterFactory.GetCaster(null, commonType); + getter = new ProxyEventPropertyGetter + { + ProcGet = eventBean => + { + var variant = (VariantEvent)eventBean; + var propertyGetter = propertyGetterCache.GetGetter(assignedPropertyNumber, variant.UnderlyingEventBean.EventType); + if (propertyGetter == null) + { + return null; + } + var value = propertyGetter.Get(variant.UnderlyingEventBean); + if (value == null) + { + return value; + } + return caster.Invoke(value); + }, + ProcGetFragment = eventBean => null, + ProcIsExistsProperty = eventBean => + { + var variant = (VariantEvent)eventBean; + var propertyGetter = propertyGetterCache.GetGetter(assignedPropertyNumber, variant.UnderlyingEventBean.EventType); + if (propertyGetter == null) + { + return false; + } + return propertyGetter.IsExistsProperty(variant.UnderlyingEventBean); + } + }; + } + else + { + getter = new ProxyEventPropertyGetter + { + ProcGet = eventBean => + { + var variant = (VariantEvent)eventBean; + var propertyGetter = propertyGetterCache.GetGetter(assignedPropertyNumber, variant.UnderlyingEventBean.EventType); + if (propertyGetter == null) + { + return null; + } + return propertyGetter.Get(variant.UnderlyingEventBean); + }, + ProcGetFragment = eventBean => null, + ProcIsExistsProperty = eventBean => + { + var variant = (VariantEvent)eventBean; + var propertyGetter = propertyGetterCache.GetGetter(assignedPropertyNumber, variant.UnderlyingEventBean.EventType); + if (propertyGetter == null) + { + return false; + } + return propertyGetter.IsExistsProperty(variant.UnderlyingEventBean); + } + }; + } + + return new VariantPropertyDesc(commonType, getter, true); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/vaevent/VariantPropertyDesc.cs b/NEsper.Core/NEsper.Core/events/vaevent/VariantPropertyDesc.cs new file mode 100755 index 000000000..570cf8ecc --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/vaevent/VariantPropertyDesc.cs @@ -0,0 +1,44 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.util; + +namespace com.espertech.esper.events.vaevent +{ + /// + /// Descriptor for a variant stream property. + /// + public class VariantPropertyDesc + { + /// Ctor. + /// type or null if not exists + /// the getter or null if not exists + /// the bool indicating whether it exists or not + public VariantPropertyDesc(Type propertyType, EventPropertyGetter getter, bool property) + { + PropertyType = propertyType.GetBoxedType(); + Getter = getter; + IsProperty = property; + } + + /// True if the property exists, false if not. + /// indicator whether property exists + public bool IsProperty { get; private set; } + + /// Returns the property type. + /// property type + public Type PropertyType { get; private set; } + + /// Returns the getter for the property. + /// property getter + public EventPropertyGetter Getter { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/events/vaevent/VariantPropertyGetterCache.cs b/NEsper.Core/NEsper.Core/events/vaevent/VariantPropertyGetterCache.cs new file mode 100755 index 000000000..fa5dba253 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/vaevent/VariantPropertyGetterCache.cs @@ -0,0 +1,159 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.threading; + +namespace com.espertech.esper.events.vaevent +{ + /// + /// A thread-safe cache for property getters per event type. + /// + /// Since most often getters are used in a row for the same type, keeps a row of last + /// used getters for fast lookup based on type. + /// + public class VariantPropertyGetterCache + { + private volatile EventType[] _knownTypes; + private volatile VariantPropertyGetterRow _lastUsedGetters; + private readonly List _properties; + private IDictionary _allGetters; + private readonly ILockable _iLock; + + /// Ctor. + /// types known at cache construction type, may be an empty list for the ANY type variance. + public VariantPropertyGetterCache(EventType[] knownTypes) + { + _iLock = LockManager.CreateLock(GetType()); + _knownTypes = knownTypes; + _allGetters = new Dictionary(); + _properties = new List(); + } + + /// Adds the getters for a property that is identified by a property number which indexes into array of getters per type. + /// number of property + /// to add + public void AddGetters(int assignedPropertyNumber, String propertyName) + { + foreach (EventType type in _knownTypes) + { + EventPropertyGetter getter = type.GetGetter(propertyName); + + VariantPropertyGetterRow row = _allGetters.Get(type); + if (row == null) + { + using(_iLock.Acquire()) + { + row = new VariantPropertyGetterRow(type, new EventPropertyGetter[assignedPropertyNumber + 1]); + _allGetters.Put(type, row); + } + } + row.AddGetter(assignedPropertyNumber, getter); + } + _properties.Add(propertyName); + } + + /// Fast lookup of a getter for a property and type. + /// number of property to use as index + /// type of underlying event + /// getter + public EventPropertyGetter GetGetter(int assignedPropertyNumber, EventType eventType) + { + VariantPropertyGetterRow lastGetters = _lastUsedGetters; + if ((lastGetters != null) && (lastGetters.EventType == eventType)) + { + return lastGetters.GetterPerProp[assignedPropertyNumber]; + } + + VariantPropertyGetterRow row = _allGetters.Get(eventType); + + // newly seen type (Using ANY type variance or as a subtype of an existing variance type) + // synchronized add, if added twice then that is ok too + if (row == null) + { + lock(this) + { + row = _allGetters.Get(eventType); + if (row == null) + { + row = AddType(eventType); + } + } + } + + EventPropertyGetter getter = row.GetterPerProp[assignedPropertyNumber]; + _lastUsedGetters = row; + return getter; + } + + private VariantPropertyGetterRow AddType(EventType eventType) + { + var newKnownTypes = (EventType[]) ResizeArray(_knownTypes, _knownTypes.Length + 1); + newKnownTypes[newKnownTypes.Length - 1] = eventType; + + // create getters + var getters = new EventPropertyGetter[_properties.Count]; + for (int i = 0; i < _properties.Count; i++) + { + getters[i] = eventType.GetGetter(_properties[i]); + } + + var row = new VariantPropertyGetterRow(eventType, getters); + + var newAllGetters = new Dictionary(); + newAllGetters.PutAll(_allGetters); + newAllGetters.Put(eventType, row); + + // overlay volatiles + _knownTypes = newKnownTypes; + _allGetters = newAllGetters; + + return row; + } + + private static Array ResizeArray(Array oldArray, int newSize) + { + var elementType = oldArray.GetType().GetElementType(); + var newArray = Array.CreateInstance(elementType, newSize); + var oldSize = oldArray.Length; + + Array.Copy( + oldArray, + newArray, + Math.Min(oldSize, newSize)); + + return newArray; + } + + private class VariantPropertyGetterRow + { + public VariantPropertyGetterRow(EventType eventType, EventPropertyGetter[] getterPerProp) + { + EventType = eventType; + GetterPerProp = getterPerProp; + } + + public EventType EventType { get; private set; } + + public EventPropertyGetter[] GetterPerProp { get; private set; } + + public void AddGetter(int assignedPropertyNumber, EventPropertyGetter getter) + { + if (assignedPropertyNumber > (GetterPerProp.Length - 1)) + { + GetterPerProp = (EventPropertyGetter[]) ResizeArray(GetterPerProp, GetterPerProp.Length + 10); + } + GetterPerProp[assignedPropertyNumber] = getter; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/vaevent/VariantSpec.cs b/NEsper.Core/NEsper.Core/events/vaevent/VariantSpec.cs new file mode 100755 index 000000000..d0fe62424 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/vaevent/VariantSpec.cs @@ -0,0 +1,57 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.vaevent +{ + /// + /// Specification for a variant event stream. + /// + public class VariantSpec + { + private readonly String variantStreamName; + private readonly EventType[] eventTypes; + private readonly TypeVarianceEnum typeVariance; + + /// Ctor. + /// name of variant stream + /// types of events for variant stream, or empty list + /// enum specifying type variance + public VariantSpec(String variantStreamName, EventType[] eventTypes, TypeVarianceEnum typeVariance) + { + this.variantStreamName = variantStreamName; + this.eventTypes = eventTypes; + this.typeVariance = typeVariance; + } + + /// Returns name of variant stream. + /// name + public string VariantStreamName + { + get { return variantStreamName; } + } + + /// Returns types allowed for variant streams. + /// types + public EventType[] EventTypes + { + get { return eventTypes; } + } + + /// Returns the type variance enum. + /// type variance + public TypeVarianceEnum TypeVariance + { + get { return typeVariance; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/xml/BaseXMLEventType.cs b/NEsper.Core/NEsper.Core/events/xml/BaseXMLEventType.cs new file mode 100755 index 000000000..a8782f507 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/xml/BaseXMLEventType.cs @@ -0,0 +1,278 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Xml; +using System.Xml.XPath; +using System.Xml.Xsl; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.compat.xml; +using com.espertech.esper.util; + +namespace com.espertech.esper.events.xml +{ + /// + /// Base class for XML event types. + /// + public abstract class BaseXMLEventType : BaseConfigurableEventType + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly IXPathFunctionResolver _functionResolver; + private readonly IXPathVariableResolver _variableResolver; + + private string _startTimestampPropertyName; + private string _endTimestampPropertyName; + + /// + /// Ctor. + /// + /// event type metadata + /// The event type id. + /// is the XML DOM configuration such as root element and schema names + /// for registration and lookup of types + protected BaseXMLEventType(EventTypeMetadata metadata, + int eventTypeId, + ConfigurationEventTypeXMLDOM configurationEventTypeXMLDOM, + EventAdapterService eventAdapterService) + : base(eventAdapterService, metadata, eventTypeId, typeof(XmlNode)) + { + RootElementName = configurationEventTypeXMLDOM.RootElementName; + ConfigurationEventTypeXMLDOM = configurationEventTypeXMLDOM; + + if (configurationEventTypeXMLDOM.XPathFunctionResolver != null) + { + try + { + var fresolver = TypeHelper.Instantiate( + configurationEventTypeXMLDOM.XPathFunctionResolver); + _functionResolver = fresolver; + } + catch (TypeInstantiationException ex) + { + throw new ConfigurationException("Error configuring XPath function resolver for XML type '" + configurationEventTypeXMLDOM.RootElementName + "' : " + ex.Message, ex); + } + } + + if (configurationEventTypeXMLDOM.XPathVariableResolver != null) + { + try + { + var vresolver = TypeHelper.Instantiate( + configurationEventTypeXMLDOM.XPathVariableResolver); + _variableResolver = vresolver; + } + catch (TypeInstantiationException ex) + { + throw new ConfigurationException("Error configuring XPath variable resolver for XML type '" + configurationEventTypeXMLDOM.RootElementName + "' : " + ex.Message, ex); + } + } + } + + /// + /// Returns the name of the root element. + /// + /// + /// root element name + /// + public string RootElementName { get; private set; } + + /// + /// Sets the namespace context for use in XPath expression resolution. + /// + protected XPathNamespaceContext NamespaceContext { get; set; } + + /// + /// Gets the extended context. + /// + /// The extended context. + protected XsltContext GetExtendedContext() + { + if (NamespaceContext == null) + return null; + if ((_functionResolver == null) && (_variableResolver == null)) + return NamespaceContext; + return new ExtendedContext(NamespaceContext, _functionResolver, _variableResolver); + } + + /// + /// Set the preconfigured event properties resolved by XPath expression. + /// + /// are preconfigured event properties + /// the explicit properties + protected void Initialize(ICollection explicitXPathProperties, + IList additionalSchemaProperties) + { + // make sure we override those explicitly provided with those derived from a metadataz + var namedProperties = new LinkedHashMap(); + foreach (ExplicitPropertyDescriptor desc in additionalSchemaProperties) + { + namedProperties[desc.Descriptor.PropertyName] = desc; + } + + String xPathExpression = null; + try + { + foreach (ConfigurationEventTypeXMLDOM.XPathPropertyDesc property in explicitXPathProperties) + { + xPathExpression = property.XPath; + if (Log.IsInfoEnabled) + { + Log.Info("Compiling XPath expression for property '" + property.Name + "' as '" + + xPathExpression + "'"); + } + + var expressionContext = NamespaceContext ?? GetExtendedContext(); + var expression = XPathExpression.Compile(xPathExpression, expressionContext); + + FragmentFactoryXPathPredefinedGetter fragmentFactory = null; + + var isFragment = false; + if (property.OptionalEventTypeName != null) + { + fragmentFactory = new FragmentFactoryXPathPredefinedGetter( + EventAdapterService, + property.OptionalEventTypeName, + property.Name); + isFragment = true; + } + + var getter = new XPathPropertyGetter( + property.Name, + xPathExpression, + expression, + property.ResultType, + property.OptionalCastToType, + fragmentFactory); + var returnType = SchemaUtil.ToReturnType( + property.ResultType, + property.OptionalCastToType.GetBoxedType()); + var indexType = returnType.GetIndexType(); + var isIndexed = indexType != null; + + if (property.ResultType == XPathResultType.NodeSet) + { + isIndexed = true; + } + + var desc = new EventPropertyDescriptor( + property.Name, returnType, indexType, false, false, + isIndexed, false, isFragment); + var @explicit = new ExplicitPropertyDescriptor( + desc, getter, isIndexed, + property.OptionalEventTypeName); + + namedProperties[desc.PropertyName] = @explicit; + } + } + catch (XPathException ex) + { + throw new EPException( + "XPath expression could not be compiled for expression '" + xPathExpression + '\'', ex); + } + + Initialize(namedProperties.Values); + + // evaluate start and end timestamp properties if any + _startTimestampPropertyName = ConfigurationEventTypeXMLDOM.StartTimestampPropertyName; + _endTimestampPropertyName = ConfigurationEventTypeXMLDOM.EndTimestampPropertyName; + EventTypeUtility.ValidateTimestampProperties(this, _startTimestampPropertyName, _endTimestampPropertyName); + } + + public override EventType[] SuperTypes + { + get { return null; } + } + + public override EventType[] DeepSuperTypes + { + get { return null; } + } + + /// + /// Returns the configuration XML for the XML type. + /// + /// + /// config XML + /// + public ConfigurationEventTypeXMLDOM ConfigurationEventTypeXMLDOM { get; private set; } + + public override bool EqualsCompareType(EventType eventType) + { + var other = eventType as BaseXMLEventType; + return other != null && Equals(ConfigurationEventTypeXMLDOM, other.ConfigurationEventTypeXMLDOM); + } + + public override bool Equals(Object otherObj) + { + var other = otherObj as BaseXMLEventType; + if (other == null) + { + return false; + } + + return Equals(ConfigurationEventTypeXMLDOM, other.ConfigurationEventTypeXMLDOM); + } + + /// + /// Serves as a hash function for a particular type. + /// + /// + /// A hash code for the current . + /// + public override int GetHashCode() + { + return ConfigurationEventTypeXMLDOM.GetHashCode(); + } + + public override EventPropertyWriter GetWriter(String propertyName) + { + return null; + } + + public override EventPropertyDescriptor[] WriteableProperties + { + get { return new EventPropertyDescriptor[0]; } + } + + public override EventBeanCopyMethod GetCopyMethod(String[] properties) + { + return null; + } + + public override EventPropertyDescriptor GetWritableProperty(String propertyName) + { + return null; + } + + public override EventBeanWriter GetWriter(String[] properties) + { + return null; + } + + public override EventBeanReader Reader + { + get { return null; } + } + + public override string StartTimestampPropertyName + { + get { return _startTimestampPropertyName; } + } + + public override string EndTimestampPropertyName + { + get { return _endTimestampPropertyName; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/xml/DOMAttributeAndElementGetter.cs b/NEsper.Core/NEsper.Core/events/xml/DOMAttributeAndElementGetter.cs new file mode 100755 index 000000000..6362d9c57 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/xml/DOMAttributeAndElementGetter.cs @@ -0,0 +1,236 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Linq; +using System.Xml; +using System.Xml.Linq; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.xml +{ + /// + /// Getter for both attribute and element values, attributes are checked first. + /// + public class DOMAttributeAndElementGetter + : EventPropertyGetter + , DOMPropertyGetter + { + private readonly String _propertyName; + + /// + /// Ctor. + /// + /// property name + public DOMAttributeAndElementGetter(String propertyName) + { + _propertyName = propertyName; + } + + public Object GetValueAsFragment(XmlNode node) + { + return null; + } + + public object GetValueAsFragment(XObject node) + { + return null; + } + + public XmlNode[] GetValueAsNodeArray(XmlNode node) + { + return null; + } + + public XObject[] GetValueAsNodeArray(XObject node) + { + return null; + } + + public XmlNode GetValueAsNode(XmlNode node) + { + var namedNodeMap = node.Attributes; + if (namedNodeMap != null) + { + for (int i = 0; i < namedNodeMap.Count; i++) + { + var attrNode = namedNodeMap[i]; + if (!string.IsNullOrEmpty(attrNode.LocalName)) + { + if (_propertyName == attrNode.LocalName) + { + return attrNode; + } + continue; + } + + if (_propertyName == attrNode.Name) + { + return attrNode; + } + } + } + + var list = node.ChildNodes; + for (int i = 0; i < list.Count; i++) + { + var childNode = list.Item(i); + if (childNode == null) + { + continue; + } + + if (childNode.NodeType != XmlNodeType.Element) + { + continue; + } + + if (!string.IsNullOrEmpty(childNode.LocalName)) + { + if (_propertyName == childNode.LocalName) + { + return childNode; + } + continue; + } + + if (childNode.Name == _propertyName) + { + return childNode; + } + } + + return null; + } + + public XObject GetValueAsNode(XObject node) + { + var element = (XElement)node; + var namedNodeMap = element.Attributes(); + foreach (var attrNode in namedNodeMap) + { + if (!string.IsNullOrEmpty(attrNode.Name.LocalName)) + { + if (_propertyName == attrNode.Name.LocalName) + { + return attrNode; + } + continue; + } + + if (_propertyName == attrNode.Name) + { + return attrNode; + } + } + + var list = element.Nodes() + .Where(c => c != null) + .Where(c => c.NodeType == XmlNodeType.Element) + .Cast(); + + foreach (var childNode in list) + { + if (!string.IsNullOrEmpty(childNode.Name.LocalName)) + { + if (_propertyName == childNode.Name.LocalName) + { + return childNode; + } + continue; + } + + if (childNode.Name == _propertyName) + { + return childNode; + } + } + + return null; + } + + public Object Get(EventBean eventBean) + { + XmlNode node = eventBean.Underlying as XmlNode; + if (node == null) + { + throw new PropertyAccessException("Mismatched property getter to event bean type, " + + "the underlying data object is not of type Node"); + } + + return GetValueAsNode(node); + } + + public bool IsExistsProperty(EventBean eventBean) + { + // The underlying is expected to be a map + if (!(eventBean.Underlying is XmlNode)) + { + throw new PropertyAccessException( + "Mismatched property getter to event bean type, " + + "the underlying data object is not of type Node"); + } + + var node = (XmlNode) eventBean.Underlying; + var namedNodeMap = node.Attributes; + if (namedNodeMap != null) + { + for (int i = 0; i < namedNodeMap.Count; i++) + { + var attrNode = namedNodeMap.Item(i); + if (!string.IsNullOrEmpty(attrNode.LocalName)) + { + if (_propertyName.Equals(attrNode.LocalName)) + { + return true; + } + continue; + } + if (_propertyName.Equals(attrNode.Name)) + { + return true; + } + } + } + + var list = node.ChildNodes; + for (int i = 0; i < list.Count; i++) + { + var childNode = list.Item(i); + if (childNode == null) + { + continue; + } + if (childNode.NodeType != XmlNodeType.Element) + { + continue; + } + if (!string.IsNullOrEmpty(childNode.LocalName)) + { + if (_propertyName.Equals(childNode.LocalName)) + { + return true; + } + continue; + } + if (childNode.Name.Equals(_propertyName)) + { + return true; + } + } + + return false; + } + + public Object GetFragment(EventBean eventBean) + { + return null; // Never a fragment + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/xml/DOMComplexElementGetter.cs b/NEsper.Core/NEsper.Core/events/xml/DOMComplexElementGetter.cs new file mode 100755 index 000000000..280d961c1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/xml/DOMComplexElementGetter.cs @@ -0,0 +1,236 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Linq; +using System.Xml; +using System.Xml.Linq; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.xml +{ + /// + /// Getter for a DOM complex element. + /// + public class DOMComplexElementGetter + : EventPropertyGetter + , DOMPropertyGetter + { + private readonly FragmentFactory _fragmentFactory; + private readonly bool _isArray; + private readonly String _propertyName; + + /// + /// Ctor. + /// + /// property name + /// for creating fragments + /// if this is an array property + public DOMComplexElementGetter(String propertyName, FragmentFactory fragmentFactory, bool isArray) + { + _propertyName = propertyName; + _fragmentFactory = fragmentFactory; + _isArray = isArray; + } + + #region EventPropertyGetter Members + + public Object Get(EventBean eventBean) + { + var asXNode = eventBean.Underlying as XNode; + if (asXNode == null) { + var asXml = eventBean.Underlying as XmlNode; + if (asXml == null) { + throw new PropertyAccessException("Mismatched property getter to event bean type, " + + "the underlying data object is not of type Node"); + } + + return _isArray ? (object) GetValueAsNodeArray(asXml) : GetValueAsNode(asXml); + } + + return _isArray ? (object)GetValueAsNodeArray(asXNode) : GetValueAsNode(asXNode); + } + + public bool IsExistsProperty(EventBean eventBean) + { + return true; + } + + public Object GetFragment(EventBean obj) + { + var asXNode = obj.Underlying as XNode; + if (asXNode == null) { + var asXml = obj.Underlying as XmlNode; + if (asXml == null) { + throw new PropertyAccessException("Mismatched property getter to event bean type, " + + "the underlying data object is not of type Node"); + } + + return GetValueAsFragment(asXml); + } + + return GetValueAsFragment(asXNode); + } + + #endregion + + public Object GetValueAsFragment(XmlNode node) + { + if (!_isArray) { + var result = GetValueAsNode(node); + if (result == null) { + return result; + } + + return _fragmentFactory.GetEvent(result); + } + else { + var result = GetValueAsNodeArray(node); + if ((result == null) || (result.Length == 0)) { + return new EventBean[0]; + } + + var events = new EventBean[result.Length]; + int count = 0; + for (int i = 0; i < result.Length; i++) { + events[count++] = _fragmentFactory.GetEvent(result[i]); + } + return events; + } + } + + public object GetValueAsFragment(XObject node) + { + if (!_isArray) + { + var result = GetValueAsNode(node); + if (result == null) + { + return result; + } + + return _fragmentFactory.GetEvent(result); + } + else + { + var result = GetValueAsNodeArray(node); + if ((result == null) || (result.Length == 0)) + { + return new EventBean[0]; + } + + var events = new EventBean[result.Length]; + int count = 0; + for (int i = 0; i < result.Length; i++) + { + events[count++] = _fragmentFactory.GetEvent(result[i]); + } + return events; + } + } + + public XmlNode GetValueAsNode(XmlNode node) + { + var list = node.ChildNodes; + for (int i = 0; i < list.Count; i++) { + var childNode = list.Item(i); + if (childNode == null) { + continue; + } + + if (childNode.NodeType != XmlNodeType.Element) { + continue; + } + + if (_propertyName == childNode.LocalName) { + return childNode; + } + } + return null; + } + + public XObject GetValueAsNode(XObject node) + { + var element = node as XElement; + if (element != null) { + foreach(var childNode in element.Elements()) { + if (childNode.Name.LocalName != null) { + if (_propertyName == childNode.Name.LocalName) { + return childNode; + } + continue; + } + if (_propertyName == childNode.Name) { + return childNode; + } + } + } + + return null; + } + + public XmlNode[] GetValueAsNodeArray(XmlNode node) + { + var list = node.ChildNodes; + + int count = 0; + for (int i = 0; i < list.Count; i++) { + var childNode = list.Item(i); + if (childNode == null) { + continue; + } + + if (childNode.NodeType == XmlNodeType.Element) { + count++; + } + } + + if (count == 0) { + return new XmlNode[0]; + } + + var nodes = new XmlNode[count]; + int realized = 0; + for (int i = 0; i < list.Count; i++) { + var childNode = list.Item(i); + if (childNode.NodeType != XmlNodeType.Element) { + continue; + } + + if (_propertyName.Equals(childNode.LocalName)) { + nodes[realized++] = childNode; + } + } + + if (realized == count) { + return nodes; + } + if (realized == 0) { + return new XmlNode[0]; + } + + var shrunk = new XmlNode[realized]; + Array.Copy(nodes, 0, shrunk, 0, realized); + return shrunk; + } + + public XObject[] GetValueAsNodeArray(XObject node) + { + var element = node as XElement; + if (element != null) { + return element.Elements() + .Where(childNode => childNode.Name.LocalName == _propertyName) + .Cast() + .ToArray(); + } + + return null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/xml/DOMConvertingArrayGetter.cs b/NEsper.Core/NEsper.Core/events/xml/DOMConvertingArrayGetter.cs new file mode 100755 index 000000000..17c31b1e4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/xml/DOMConvertingArrayGetter.cs @@ -0,0 +1,82 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +#region + +using System; +using System.Xml; +using com.espertech.esper.client; +using com.espertech.esper.util; + +#endregion + +namespace com.espertech.esper.events.xml +{ + /// + /// Getter for converting a Node child nodes into an array. + /// + public class DOMConvertingArrayGetter : EventPropertyGetter + { + private readonly Type _componentType; + private readonly DOMPropertyGetter _getter; + private readonly SimpleTypeParser _parser; + + /// + /// Ctor. + /// + /// getter + /// component type + public DOMConvertingArrayGetter(DOMPropertyGetter domPropertyGetter, Type returnType) + { + _getter = domPropertyGetter; + _componentType = returnType; + _parser = SimpleTypeParserFactory.GetParser(returnType); + } + + #region EventPropertyGetter Members + + public Object Get(EventBean eventBean) + { + var asXml = eventBean.Underlying as XmlNode; + if (asXml == null) { + throw new PropertyAccessException("Mismatched property getter to event bean type, " + + "the underlying data object is not of type Node"); + } + + var result = _getter.GetValueAsNodeArray(asXml); + if (result == null) { + return null; + } + + var array = Array.CreateInstance(_componentType, result.Length); + for (int i = 0; i < result.Length; i++) { + var text = result[i].InnerText; + if (string.IsNullOrEmpty(text)) { + continue; + } + + var parseResult = _parser.Invoke(text); + array.SetValue(parseResult, i); + } + + return array; + } + + public bool IsExistsProperty(EventBean eventBean) + { + return true; + } + + public Object GetFragment(EventBean eventBean) + { + return null; + } + + #endregion + } +} diff --git a/NEsper.Core/NEsper.Core/events/xml/DOMConvertingGetter.cs b/NEsper.Core/NEsper.Core/events/xml/DOMConvertingGetter.cs new file mode 100755 index 000000000..bfbf53de2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/xml/DOMConvertingGetter.cs @@ -0,0 +1,82 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Xml; +using System.Xml.Linq; +using com.espertech.esper.client; +using com.espertech.esper.util; + +namespace com.espertech.esper.events.xml +{ + /// + /// Getter for parsing node content to a desired type. + /// + public class DOMConvertingGetter : EventPropertyGetter + { + private readonly DOMPropertyGetter _getter; + private readonly SimpleTypeParser _parser; + + /// + /// Ctor. + /// + /// property name + /// getter + /// desired result type + public DOMConvertingGetter(String propertyExpression, DOMPropertyGetter domPropertyGetter, Type returnType) + { + _getter = domPropertyGetter; + _parser = SimpleTypeParserFactory.GetParser(returnType); + } + + public Object Get(EventBean eventBean) + { + var asXNode = eventBean.Underlying as XNode; + if (asXNode == null) { + var asXml = eventBean.Underlying as XmlNode; + if (asXml == null) + { + throw new PropertyAccessException("Mismatched property getter to event bean type, " + + "the underlying data object is not of type Node"); + } + + XmlNode result = _getter.GetValueAsNode(asXml); + if (result == null) + { + return null; + } + + return _parser.Invoke(result.InnerText); + } + else + { + XObject result = _getter.GetValueAsNode(asXNode); + if (result == null) + { + return null; + } + + if (result is XElement) { + return _parser.Invoke(((XElement) result).Value); + } + + return _parser.Invoke(result.ToString()); + } + } + + public bool IsExistsProperty(EventBean eventBean) + { + return true; + } + + public Object GetFragment(EventBean eventBean) + { + return null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/xml/DOMIndexedGetter.cs b/NEsper.Core/NEsper.Core/events/xml/DOMIndexedGetter.cs new file mode 100755 index 000000000..fd741fa1c --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/xml/DOMIndexedGetter.cs @@ -0,0 +1,172 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Linq; +using System.Xml; +using System.Xml.Linq; +using com.espertech.esper.client; + +namespace com.espertech.esper.events.xml +{ + /// + /// Getter for retrieving a value at a certain index. + /// + public class DOMIndexedGetter + : EventPropertyGetter + , DOMPropertyGetter + { + private readonly String _propertyName; + private readonly int _index; + private readonly FragmentFactory _fragmentFactory; + + /// + /// Ctor. + /// + /// property name + /// index + /// for creating fragments if required + public DOMIndexedGetter(String propertyName, int index, FragmentFactory fragmentFactory) + { + _propertyName = propertyName; + _index = index; + _fragmentFactory = fragmentFactory; + } + + public XmlNode[] GetValueAsNodeArray(XmlNode node) + { + return null; + } + + public XObject[] GetValueAsNodeArray(XObject node) + { + return null; + } + + public Object GetValueAsFragment(XmlNode node) + { + if (_fragmentFactory != null) + { + var result = GetValueAsNode(node); + if (result != null) + { + return _fragmentFactory.GetEvent(result); + } + } + + return null; + } + + public object GetValueAsFragment(XObject node) + { + if (_fragmentFactory != null) + { + var result = GetValueAsNode(node); + if (result != null) + { + return _fragmentFactory.GetEvent(result); + } + } + + return null; + } + + public XmlNode GetValueAsNode(XmlNode node) + { + var list = node.ChildNodes; + int count = 0; + for (int i = 0; i < list.Count; i++) + { + var childNode = list.Item(i); + if (childNode == null) { + continue; + } + + if (childNode.NodeType != XmlNodeType.Element) { + continue; + } + + var elementName = childNode.LocalName; + if (elementName != _propertyName) + { + continue; + } + + if (count == _index) + { + return childNode; + } + count++; + } + + return null; + } + + public XObject GetValueAsNode(XObject node) + { + // #1 reason why LINQ beats traditional methods + var element = node as XElement; + if (element != null) { + return element.Nodes() + .Where(c => c != null) + .Where(c => c.NodeType == XmlNodeType.Element) + .Cast() + .Where(c => c.Name.LocalName == _propertyName) + .Skip(_index) + .FirstOrDefault(); + } + + return null; + } + + public Object Get(EventBean eventBean) + { + var xnode = eventBean.Underlying as XNode; + if (xnode == null) { + var node = eventBean.Underlying as XmlNode; + if (node == null) { + return null; + } + + return GetValueAsNode(node); + } + + return GetValueAsNode(xnode); + } + + public bool IsExistsProperty(EventBean eventBean) + { + var xnode = eventBean.Underlying as XNode; + if (xnode == null) { + var node = eventBean.Underlying as XmlNode; + if (node == null) { + return false; + } + + return GetValueAsNode(node) != null; + } + + return GetValueAsNode(xnode) != null; + } + + public Object GetFragment(EventBean eventBean) + { + var xnode = eventBean.Underlying as XNode; + if (xnode == null) { + var node = eventBean.Underlying as XmlNode; + if (node == null) { + return null; + } + + return GetValueAsFragment(node); + } + + return GetValueAsFragment(xnode); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/xml/DOMMapGetter.cs b/NEsper.Core/NEsper.Core/events/xml/DOMMapGetter.cs new file mode 100755 index 000000000..434d02c5b --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/xml/DOMMapGetter.cs @@ -0,0 +1,203 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +#region + +using System; +using System.Linq; +using System.Xml; +using System.Xml.Linq; +using com.espertech.esper.client; + +#endregion + +namespace com.espertech.esper.events.xml +{ + /// + /// DOM getter for Map-property. + /// + public class DOMMapGetter + : EventPropertyGetter + , DOMPropertyGetter + { + private readonly FragmentFactory _fragmentFactory; + private readonly String _mapKey; + private readonly String _propertyMap; + + /// + /// Ctor. + /// + /// property name + /// key in map + /// for creating fragments + public DOMMapGetter(String propertyName, String mapKey, FragmentFactory fragmentFactory) + { + _propertyMap = propertyName; + _mapKey = mapKey; + _fragmentFactory = fragmentFactory; + } + + #region EventPropertyGetter Members + + public Object Get(EventBean eventBean) + { + var asXNode = eventBean.Underlying as XNode; + if (asXNode == null) + { + var node = eventBean.Underlying as XmlNode; + return node == null ? null : GetValueAsNode(node); + } + + return GetValueAsNode(asXNode); + } + + public bool IsExistsProperty(EventBean eventBean) + { + var asXNode = eventBean.Underlying as XElement; + if (asXNode == null) + { + var node = eventBean.Underlying as XmlNode; + if (node == null) + { + return false; + } + + XmlNodeList list = node.ChildNodes; + for (int i = 0; i < list.Count; i++) + { + XmlNode childNode = list.Item(i); + if (childNode == null) + { + continue; + } + + if (childNode.NodeType != XmlNodeType.Element) + { + continue; + } + + if (childNode.Name != _propertyMap) + { + continue; + } + + XmlNode attribute = childNode.Attributes.GetNamedItem("id"); + if (attribute == null) + { + continue; + } + if (attribute.InnerText != _mapKey) + { + continue; + } + + return true; + } + } + else + { + return asXNode.Elements() + .Where(e => e.Name.LocalName == _propertyMap) + .Select(element => element.Attributes().FirstOrDefault(attr => attr.Name.LocalName == _mapKey)) + .Where(attribute => attribute != null) + .Any(attribute => attribute.Value == _mapKey); + } + + return false; + } + + public Object GetFragment(EventBean eventBean) + { + return null; + } + + #endregion + + public XmlNode[] GetValueAsNodeArray(XmlNode node) + { + return null; + } + + public XObject[] GetValueAsNodeArray(XObject node) + { + return null; + } + + public Object GetValueAsFragment(XmlNode node) + { + if (_fragmentFactory == null) + { + return null; + } + + XmlNode result = GetValueAsNode(node); + return result == null ? null : _fragmentFactory.GetEvent(result); + } + + public object GetValueAsFragment(XObject node) + { + if (_fragmentFactory == null) + { + return null; + } + + XObject result = GetValueAsNode(node); + return result == null ? null : _fragmentFactory.GetEvent(result); + } + + public XmlNode GetValueAsNode(XmlNode node) + { + XmlNodeList list = node.ChildNodes; + for (int i = 0; i < list.Count; i++) + { + XmlNode childNode = list.Item(i); + if (childNode == null) + { + continue; + } + if (childNode.NodeType != XmlNodeType.Element) + { + continue; + } + if (childNode.Name != _propertyMap) + { + continue; + } + + XmlNode attribute = childNode.Attributes.GetNamedItem("id"); + if (attribute == null) + { + continue; + } + if (attribute.InnerText != _mapKey) + { + continue; + } + + return childNode; + } + return null; + } + + public XObject GetValueAsNode(XObject node) + { + var element = node as XElement; + if (element != null) + { + var list = element.Elements().Where(e => e.Name.LocalName == _propertyMap); + return (from subElement in list + let attribute = subElement.Attributes(XName.Get("id")).FirstOrDefault() + where attribute != null + where attribute.Value == _mapKey + select subElement).FirstOrDefault(); + } + + return null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/xml/DOMNestedPropertyGetter.cs b/NEsper.Core/NEsper.Core/events/xml/DOMNestedPropertyGetter.cs new file mode 100755 index 000000000..eee2d23ef --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/xml/DOMNestedPropertyGetter.cs @@ -0,0 +1,208 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Xml; +using System.Xml.Linq; +using com.espertech.esper.client; + +namespace com.espertech.esper.events.xml +{ + /// + /// Getter for nested properties in a DOM tree. + /// + public class DOMNestedPropertyGetter + : EventPropertyGetter + , DOMPropertyGetter + { + private readonly DOMPropertyGetter[] _domGetterChain; + private readonly FragmentFactory _fragmentFactory; + + /// + /// Ctor. + /// + /// is the chain of getters to retrieve each nested property + /// for creating fragments + public DOMNestedPropertyGetter(ICollection getterChain, FragmentFactory fragmentFactory) + { + this._domGetterChain = new DOMPropertyGetter[getterChain.Count]; + this._fragmentFactory = fragmentFactory; + + int count = 0; + foreach (EventPropertyGetter getter in getterChain) + { + _domGetterChain[count++] = (DOMPropertyGetter) getter; + } + } + + public Object GetValueAsFragment(XmlNode node) + { + var result = GetValueAsNode(node); + return result == null ? null : _fragmentFactory.GetEvent(result); + } + + public object GetValueAsFragment(XObject node) + { + var result = GetValueAsNode(node); + return result == null ? null : _fragmentFactory.GetEvent(result); + } + + public XmlNode[] GetValueAsNodeArray(XmlNode node) + { + var value = node; + for (int i = 0; i < _domGetterChain.Length - 1; i++) + { + value = _domGetterChain[i].GetValueAsNode(value); + + if (value == null) + { + return null; + } + } + + return _domGetterChain[_domGetterChain.Length - 1].GetValueAsNodeArray(value); + } + + public XObject[] GetValueAsNodeArray(XObject node) + { + XObject value = node; + for (int i = 0; i < _domGetterChain.Length - 1; i++) + { + value = _domGetterChain[i].GetValueAsNode(value); + + if (value == null) + { + return null; + } + } + + return _domGetterChain[_domGetterChain.Length - 1].GetValueAsNodeArray(value); + } + + public XmlNode GetValueAsNode(XmlNode node) + { + XmlNode value = node; + + for (int i = 0; i < _domGetterChain.Length; i++) + { + value = _domGetterChain[i].GetValueAsNode(value); + if (value == null) + { + return null; + } + } + + return value; + } + + public XObject GetValueAsNode(XObject node) + { + XObject value = node; + + for (int i = 0; i < _domGetterChain.Length; i++) + { + value = _domGetterChain[i].GetValueAsNode(value); + if (value == null) + { + return null; + } + } + + return value; + } + + public Object Get(EventBean eventBean) + { + var xnode = eventBean.Underlying as XNode; + if (xnode == null) { + var node = eventBean.Underlying as XmlNode; + if (node == null) { + throw new PropertyAccessException( + "Mismatched property getter to event bean type, " + + "the underlying data object is not of type Node"); + } + + return GetValueAsNode(node); + } + + return GetValueAsNode(xnode); + } + + public bool IsExistsProperty(EventBean obj) + { + var xnode = obj.Underlying as XNode; + if (xnode == null) { + var node = obj.Underlying as XmlNode; + if (node == null) { + throw new PropertyAccessException( + "Mismatched property getter to event bean type, " + + "the underlying data object is not of type Node"); + } + + var value = node; + for (int i = 0; i < _domGetterChain.Length; i++) { + value = _domGetterChain[i].GetValueAsNode(value); + if (value == null) { + return false; + } + } + + return true; + } + + XObject valueX = xnode; + for (int i = 0; i < _domGetterChain.Length; i++) { + valueX = _domGetterChain[i].GetValueAsNode(valueX); + if (valueX == null) { + return false; + } + } + + return true; + } + + public Object GetFragment(EventBean obj) + { + var xnode = obj.Underlying as XNode; + if (xnode == null) { + var node = obj.Underlying as XmlNode; + if (node == null) + { + throw new PropertyAccessException("Mismatched property getter to event bean type, " + + "the underlying data object is not of type Node"); + } + + var value = node; + for (int i = 0; i < _domGetterChain.Length - 1; i++) { + value = _domGetterChain[i].GetValueAsNode(value); + + if (value == null) { + return false; + } + } + + return _domGetterChain[_domGetterChain.Length - 1].GetValueAsFragment(value); + } + else + { + XObject value = xnode; + for (int i = 0; i < _domGetterChain.Length - 1; i++) + { + value = _domGetterChain[i].GetValueAsNode(value); + if (value == null) + { + return false; + } + } + + return _domGetterChain[_domGetterChain.Length - 1].GetValueAsFragment(value); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/xml/DOMPropertyGetter.cs b/NEsper.Core/NEsper.Core/events/xml/DOMPropertyGetter.cs new file mode 100755 index 000000000..6adb0c846 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/xml/DOMPropertyGetter.cs @@ -0,0 +1,76 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Xml; +using System.Xml.Linq; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.xml +{ + /// + /// Shortcut-getter for DOM underlying objects. + /// + public interface DOMPropertyGetter : EventPropertyGetter + { + /// + /// Returns a property value as a node. + /// + /// to evaluate + /// + /// value node + /// + XmlNode GetValueAsNode(XmlNode node); + + /// + /// Returns a property value as a node. + /// + /// to evaluate + /// + /// value node + /// + XObject GetValueAsNode(XObject node); + + /// + /// Returns a property value that is indexed as a node array. + /// + /// to evaluate + /// + /// nodes + /// + XmlNode[] GetValueAsNodeArray(XmlNode node); + + /// + /// Returns a property value that is indexed as a node array. + /// + /// to evaluate + /// + /// nodes + /// + XObject[] GetValueAsNodeArray(XObject node); + + /// + /// Returns a property value as a fragment. + /// + /// to evaluate + /// + /// fragment + /// + Object GetValueAsFragment(XmlNode node); + + /// + /// Returns a property value as a fragment. + /// + /// to evaluate + /// + /// fragment + /// + Object GetValueAsFragment(XObject node); + } +} diff --git a/NEsper.Core/NEsper.Core/events/xml/DOMSimpleAttributeGetter.cs b/NEsper.Core/NEsper.Core/events/xml/DOMSimpleAttributeGetter.cs new file mode 100755 index 000000000..8f28efccc --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/xml/DOMSimpleAttributeGetter.cs @@ -0,0 +1,127 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Xml; +using System.Xml.Linq; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.xml +{ + /// + /// Getter for simple attributes in a DOM node. + /// + public class DOMSimpleAttributeGetter + : EventPropertyGetter + , DOMPropertyGetter + { + private readonly String _propertyName; + + /// + /// Ctor. + /// + /// property name + public DOMSimpleAttributeGetter(String propertyName) + { + _propertyName = propertyName; + } + + public Object GetValueAsFragment(XmlNode node) + { + return null; + } + + public object GetValueAsFragment(XObject node) + { + return null; + } + + public XmlNode[] GetValueAsNodeArray(XmlNode node) + { + return null; + } + + public XObject[] GetValueAsNodeArray(XObject node) + { + return null; + } + + public XmlNode GetValueAsNode(XmlNode node) + { + var namedNodeMap = node.Attributes; + for (int i = 0; i < namedNodeMap.Count ; i++) + { + var attrNode = namedNodeMap.Item(i); + if (! string.IsNullOrEmpty(attrNode.LocalName)) + { + if (_propertyName == attrNode.LocalName) + { + return attrNode; + } + continue; + } + + if (_propertyName == attrNode.Name) + { + return attrNode; + } + } + return null; + } + + public XObject GetValueAsNode(XObject node) + { + var element = node as XElement; + if (element != null) { + var namedNodeMap = element.Attributes(); + foreach(var attrNode in namedNodeMap) { + if (!string.IsNullOrEmpty(attrNode.Name.LocalName)) { + if (_propertyName == attrNode.Name.LocalName) { + return attrNode; + } + continue; + } + + if (_propertyName == attrNode.Name) { + return attrNode; + } + } + } + + return null; + } + + public Object Get(EventBean eventBean) + { + var xnode = eventBean.Underlying as XNode; + if (xnode == null) { + var node = eventBean.Underlying as XmlNode; + if (node == null) { + throw new PropertyAccessException( + "Mismatched property getter to event bean type, " + + "the underlying data object is not of type Node"); + } + + return GetValueAsNode(node); + } + + return GetValueAsNode(xnode); + } + + public bool IsExistsProperty(EventBean eventBean) + { + return true; + } + + public Object GetFragment(EventBean eventBean) + { + return null; // Never a fragment + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/xml/ElementPathNode.cs b/NEsper.Core/NEsper.Core/events/xml/ElementPathNode.cs new file mode 100755 index 000000000..9803a0d16 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/xml/ElementPathNode.cs @@ -0,0 +1,64 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.Xml; + +namespace com.espertech.esper.events.xml +{ + /// + /// A simple node for the creation of a tree, intended in this case to mirror an XML model. + /// + public class ElementPathNode + { + private IList _children; + + public ElementPathNode(ElementPathNode parent, XmlQualifiedName name) + { + Name = name; + Parent = parent; + } + + public XmlQualifiedName Name { get; private set; } + + public ElementPathNode Parent { get; private set; } + + public ElementPathNode AddChild(XmlQualifiedName name) + { + if (_children == null) + { + _children = new List(); + } + var newChild = new ElementPathNode(this, name); + _children.Add(newChild); + return newChild; + } + + public bool DoesNameAlreadyExistInHierarchy() + { + return DoesNameAlreadyExistInHierarchy(Name); + } + + private bool DoesNameAlreadyExistInHierarchy(XmlQualifiedName nameToFind) + { + var doesNameAlreadyExistInHierarchy = false; + if (Parent != null) + { + if (Parent.Name.Equals(nameToFind)) + { + doesNameAlreadyExistInHierarchy = true; + } + else + { + return Parent.DoesNameAlreadyExistInHierarchy(nameToFind); + } + } + return doesNameAlreadyExistInHierarchy; + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/events/xml/ExtendedContext.cs b/NEsper.Core/NEsper.Core/events/xml/ExtendedContext.cs new file mode 100755 index 000000000..872c28f9b --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/xml/ExtendedContext.cs @@ -0,0 +1,298 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections; +using System.Collections.Generic; +using System.Data; +using System.Xml; +using System.Xml.XPath; +using System.Xml.Xsl; + +using com.espertech.esper.compat.xml; + +namespace com.espertech.esper.events.xml +{ + public class ExtendedContext : XsltContext + { + private readonly XsltContext _baseContext; + private readonly IXPathFunctionResolver _functionResolver; + private readonly IXPathVariableResolver _variableResolver; + + /// + /// Initializes a new instance of the class. + /// + /// The base context. + /// The function resolver. + /// The variable resolver. + public ExtendedContext(XsltContext baseContext, IXPathFunctionResolver functionResolver, IXPathVariableResolver variableResolver) + { + _baseContext = baseContext; + _functionResolver = functionResolver; + _variableResolver = variableResolver; + } + + /// + /// Adds the given namespace to the collection. + /// + /// The prefix to associate with the namespace being added. Use String.EmptyFalse to add a default namespace. + /// Note: + /// If the + /// will be used for resolving namespaces in an XML Path Language (XPath) + /// expression, a prefix must be specified. If an XPath expression does not include a prefix, it is assumed that the namespace + /// Uniform Resource Identifier (URI) is the empty namespace. For more information about XPath expressions and the + /// , refer to the and + /// methods. + /// + /// The namespace to add. + /// + /// The value for is "xml" or "xmlns". + /// + /// + /// The value for or is null. + /// + public override void AddNamespace(string prefix, string uri) + { + throw new ReadOnlyException(); + } + + /// + /// When overridden in a derived class, compares the base Uniform Resource Identifiers (URIs) of two documents based upon the + /// order the documents were loaded by the XSLT processor (that is, the class). + /// + /// The base URI of the first document to compare. + /// The base URI of the second document to compare. + /// + /// An integer value describing the relative order of the two base URIs: -1 if occurs before ; 0 if the two base URIs are identical; and 1 if occurs after . + /// + public override int CompareDocument(string baseUri, string nextbaseUri) + { + return _baseContext.CompareDocument(baseUri, nextbaseUri); + } + + /// + /// Gets the namespace URI for the default namespace. + /// + /// + /// + /// Returns the namespace URI for the default namespace, or String.EmptyFalse if there is no default namespace. + /// + public override string DefaultNamespace + { + get { return _baseContext.DefaultNamespace; } + } + + /// + /// When overridden in a derived class, resolves a variable reference and returns an + /// representing the variable. + /// + /// The prefix of the variable as it appears in the XPath expression. + /// The name of the variable. + /// + /// An representing the variable at runtime. + /// + public override IXsltContextVariable ResolveVariable(string prefix, string name) + { + if (_variableResolver != null) { + var variable = _variableResolver.ResolveVariable(prefix, name); + if (variable != null) { + return variable; + } + } + + return _baseContext.ResolveVariable(prefix, name); + } + + /// + /// When overridden in a derived class, resolves a function reference and returns an + /// representing the function. The + /// is used at execution time to get the return value of the function. + /// + /// The prefix of the function as it appears in the XPath expression. + /// The name of the function. + /// An array of argument types for the function being resolved. This allows you to select between methods with the same name (for example, overloaded methods). + /// + /// An representing the function. + /// + public override IXsltContextFunction ResolveFunction(string prefix, string name, XPathResultType[] argTypes) + { + if (_functionResolver != null) { + var function = _functionResolver.ResolveFunction(prefix, name, argTypes); + if (function != null) { + return function; + } + } + + return _baseContext.ResolveFunction(prefix, name, argTypes); + } + + /// + /// When overridden in a derived class, evaluates whether to preserve white space nodes or strip them for the given context. + /// + /// The white space node that is to be preserved or stripped in the current context. + /// + /// Returns true if the white space is to be preserved or false if the white space is to be stripped. + /// + public override bool PreserveWhitespace(XPathNavigator node) + { + return _baseContext.PreserveWhitespace(node); + } + + /// + /// When overridden in a derived class, gets a value indicating whether to include white space nodes in the output. + /// + /// + /// true to check white space nodes in the source document for inclusion in the output; false to not evaluate white space nodes. The default is true. + /// + public override bool Whitespace + { + get { return _baseContext.Whitespace; } + } + + /// + /// Serves as a hash function for a particular type. + /// + /// + /// A hash code for the current . + /// + public override int GetHashCode() + { + return _baseContext.GetHashCode(); + } + + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// + /// true if the specified is equal to the current ; otherwise, false. + /// + /// + /// The parameter is null. + /// + public override bool Equals(object obj) + { + return _baseContext.Equals(obj); + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override string ToString() + { + return _baseContext.ToString(); + } + + /// + /// Pushes a namespace scope onto the stack. + /// + public override void PushScope() + { + _baseContext.PushScope(); + } + + /// + /// Pops a namespace scope off the stack. + /// + /// + /// true if there are namespace scopes left on the stack; false if there are no more namespaces to pop. + /// + public override bool PopScope() + { + return _baseContext.PopScope(); + } + + /// + /// Removes the given namespace for the given prefix. + /// + /// The prefix for the namespace + /// The namespace to remove for the given prefix. The namespace removed is from the current namespace scope. Namespaces outside the current scope are ignored. + /// + /// The value of or is null. + /// + public override void RemoveNamespace(string prefix, string uri) + { + throw new ReadOnlyException(); + } + + /// + /// Returns an enumerator to use to iterate through the namespaces in the . + /// + /// + /// An containing the prefixes stored by the . + /// + public override IEnumerator GetEnumerator() + { + return _baseContext.GetEnumerator(); + } + + /// + /// Gets a collection of namespace names keyed by prefix which can be used to enumerate the namespaces currently in scope. + /// + /// An value that specifies the type of namespace nodes to return. + /// + /// A object containing a collection of namespace and prefix pairs currently in scope. + /// + public override IDictionary GetNamespacesInScope(XmlNamespaceScope scope) + { + return _baseContext.GetNamespacesInScope(scope); + } + + /// + /// Gets the namespace URI for the specified prefix. + /// + /// The prefix whose namespace URI you want to resolve. To match the default namespace, pass String.EmptyFalse. + /// + /// Returns the namespace URI for or null if there is no mapped namespace. The returned string is atomized. + /// For more information on atomized strings, see . + /// + public override string LookupNamespace(string prefix) + { + return _baseContext.LookupNamespace(prefix); + } + + /// + /// Finds the prefix declared for the given namespace URI. + /// + /// The namespace to resolve for the prefix. + /// + /// The matching prefix. If there is no mapped prefix, the method returns String.EmptyFalse. If a null value is supplied, then null is returned. + /// + public override string LookupPrefix(string uri) + { + return _baseContext.LookupPrefix(uri); + } + + /// + /// Gets a value indicating whether the supplied prefix has a namespace defined for the current pushed scope. + /// + /// The prefix of the namespace you want to find. + /// + /// true if there is a namespace defined; otherwise, false. + /// + public override bool HasNamespace(string prefix) + { + return _baseContext.HasNamespace(prefix); + } + + /// + /// Gets the associated with this object. + /// + /// + /// + /// The used by this object. + /// + public override XmlNameTable NameTable + { + get { return _baseContext.NameTable; } + } + } + +} diff --git a/NEsper.Core/NEsper.Core/events/xml/FragmentFactory.cs b/NEsper.Core/NEsper.Core/events/xml/FragmentFactory.cs new file mode 100755 index 000000000..289e42f55 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/xml/FragmentFactory.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Xml; +using System.Xml.Linq; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.xml +{ + /// + /// Factory for event fragments for use with DOM getters. + /// + public interface FragmentFactory + { + /// + /// Gets the event. + /// + /// The result. + /// + EventBean GetEvent(XObject result); + + /// + /// Returns a fragment for the node. + /// + /// node to fragment + /// + /// fragment + /// + EventBean GetEvent(XmlNode result); + } +} diff --git a/NEsper.Core/NEsper.Core/events/xml/FragmentFactoryDOMGetter.cs b/NEsper.Core/NEsper.Core/events/xml/FragmentFactoryDOMGetter.cs new file mode 100755 index 000000000..0938a923e --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/xml/FragmentFactoryDOMGetter.cs @@ -0,0 +1,70 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Xml; +using System.Xml.Linq; +using com.espertech.esper.client; + +namespace com.espertech.esper.events.xml +{ + /// + /// Factory for fragments for DOM getters. + /// + public class FragmentFactoryDOMGetter : FragmentFactory + { + private readonly EventAdapterService _eventAdapterService; + private readonly BaseXMLEventType _xmlEventType; + private readonly String _propertyExpression; + + private volatile EventType _fragmentType; + + /// + /// Ctor. + /// + /// for event type lookup + /// the originating type + /// property expression + public FragmentFactoryDOMGetter(EventAdapterService eventAdapterService, BaseXMLEventType xmlEventType, String propertyExpression) + { + _eventAdapterService = eventAdapterService; + _xmlEventType = xmlEventType; + _propertyExpression = propertyExpression; + } + + public EventBean GetEvent(XObject result) + { + if (_fragmentType == null) + { + FragmentEventType type = _xmlEventType.GetFragmentType(_propertyExpression); + if (type == null) + { + return null; + } + _fragmentType = type.FragmentType; + } + + return _eventAdapterService.AdapterForTypedDOM(result, _fragmentType); + } + + public EventBean GetEvent(XmlNode result) + { + if (_fragmentType == null) + { + FragmentEventType type = _xmlEventType.GetFragmentType(_propertyExpression); + if (type == null) + { + return null; + } + _fragmentType = type.FragmentType; + } + + return _eventAdapterService.AdapterForTypedDOM(result, _fragmentType); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/xml/FragmentFactoryXPathPredefinedGetter.cs b/NEsper.Core/NEsper.Core/events/xml/FragmentFactoryXPathPredefinedGetter.cs new file mode 100755 index 000000000..0dd5b4d4b --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/xml/FragmentFactoryXPathPredefinedGetter.cs @@ -0,0 +1,85 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Xml; +using System.Xml.Linq; +using com.espertech.esper.client; +using com.espertech.esper.compat.logging; + +namespace com.espertech.esper.events.xml +{ + /// + /// Fragment factory for use with XPath explicit properties. + /// + public class FragmentFactoryXPathPredefinedGetter : FragmentFactory + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly EventAdapterService _eventAdapterService; + private readonly String _eventTypeName; + private readonly String _propertyName; + + private volatile EventType _eventType; + + /// + /// Ctor. + /// + /// for event type lookup + /// name to look up + /// property + public FragmentFactoryXPathPredefinedGetter(EventAdapterService eventAdapterService, String eventTypeName, String propertyName) + { + _eventAdapterService = eventAdapterService; + _eventTypeName = eventTypeName; + _propertyName = propertyName; + } + + public EventBean GetEvent(XmlNode result) + { + if (_eventType == null) + { + EventType candidateEventType = _eventAdapterService.GetEventTypeByName(_eventTypeName); + if (candidateEventType == null) + { + Log.Warn("Event type by name '" + _eventTypeName + "' was not found for property '" + _propertyName + "'"); + return null; + } + if (!(candidateEventType is BaseXMLEventType)) + { + Log.Warn("Event type by name '" + _eventTypeName + "' is not an XML event type for property '" + _propertyName + "'"); + return null; + } + _eventType = candidateEventType; + } + + return _eventAdapterService.AdapterForTypedDOM(result, _eventType); + } + + public EventBean GetEvent(XObject result) + { + if (_eventType == null) + { + EventType candidateEventType = _eventAdapterService.GetEventTypeByName(_eventTypeName); + if (candidateEventType == null) + { + Log.Warn("Event type by name '" + _eventTypeName + "' was not found for property '" + _propertyName + "'"); + return null; + } + if (!(candidateEventType is BaseXMLEventType)) + { + Log.Warn("Event type by name '" + _eventTypeName + "' is not an XML event type for property '" + _propertyName + "'"); + return null; + } + _eventType = candidateEventType; + } + + return _eventAdapterService.AdapterForTypedDOM(result, _eventType); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/xml/NamespaceNamePair.cs b/NEsper.Core/NEsper.Core/events/xml/NamespaceNamePair.cs new file mode 100755 index 000000000..08f94f59a --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/xml/NamespaceNamePair.cs @@ -0,0 +1,72 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.events.xml +{ + /// + /// Pair of namespace and name. + /// + public class NamespaceNamePair + { + /// + /// Ctor. + /// + /// namespace + /// name + public NamespaceNamePair(String @namespace, String name) + { + Namespace = @namespace; + Name = name; + } + + /// + /// Returns the name. + /// + /// + /// name part + /// + public string Name { get; private set; } + + /// + /// Returns the namespace. + /// + /// + /// namespace part + /// + public string Namespace { get; private set; } + + public override String ToString() + { + return Namespace + " " + Name; + } + + public bool Equals(NamespaceNamePair obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + return Equals(obj.Namespace, Namespace) && Equals(obj.Name, Name); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != typeof (NamespaceNamePair)) return false; + return Equals((NamespaceNamePair) obj); + } + + public override int GetHashCode() + { + unchecked { + return ((Namespace != null ? Namespace.GetHashCode() : 0)*397) ^ (Name != null ? Name.GetHashCode() : 0); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/xml/SchemaElement.cs b/NEsper.Core/NEsper.Core/events/xml/SchemaElement.cs new file mode 100755 index 000000000..8a8df4cab --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/xml/SchemaElement.cs @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.events.xml +{ + /// + /// Schema element is a simple or complex element. + /// + public interface SchemaElement : SchemaItem + { + /// + /// Returns the namespace. + /// + /// + /// namespace + /// + string Namespace { get; } + + /// + /// Returns the name. + /// + /// + /// name + /// + string Name { get; } + + /// + /// Returns true for unbounded or max>1 + /// + /// + /// array indicator + /// + bool IsArray { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/events/xml/SchemaElementComplex.cs b/NEsper.Core/NEsper.Core/events/xml/SchemaElementComplex.cs new file mode 100755 index 000000000..025629f4f --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/xml/SchemaElementComplex.cs @@ -0,0 +1,142 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Xml; +using System.Xml.Schema; + +namespace com.espertech.esper.events.xml +{ + /// + /// Represents a complex element possibly with attributes, simple elements, other + /// complex child elements. + /// + [Serializable] + public class SchemaElementComplex : SchemaElement + { + /// + /// Ctor. + /// + /// the element name + /// the element namespace + /// the attributes or empty if none + /// the child complex elements or empty if none + /// the simple elements or empty if none + /// if unbound or max>1 + /// if the element does itself have a type + /// if the element does itself have a type + public SchemaElementComplex( + String name, + String @namespace, + IList attributes, + IList children, + IList simpleElements, + bool isArray, + XmlSchemaSimpleType optionalSimpleType, + XmlQualifiedName optionalSimpleTypeName) + { + Name = name; + Namespace = @namespace; + Attributes = attributes; + ComplexElements = children; + SimpleElements = simpleElements; + IsArray = isArray; + OptionalSimpleType = optionalSimpleType; + OptionalSimpleTypeName = optionalSimpleTypeName; + } + + /// + /// Returns attributes. + /// + /// + /// attributes + /// + public IList Attributes { get; private set; } + + /// + /// Returns complex child elements. + /// + /// + /// attributes + /// + public IList ComplexElements { get; private set; } + + /// + /// Returns simple child elements. + /// + /// + /// simple child elements + /// + public IList SimpleElements { get; private set; } + + #region SchemaElement Members + + /// + /// Returns the name. + /// + /// + /// name + /// + public string Name { get; private set; } + + /// + /// Returns the namespace of the element. + /// + /// + /// + /// namespace + /// + public string Namespace { get; private set; } + + /// + /// Returns true if unbound or max greater one. + /// + /// + /// true if array + /// + public bool IsArray { get; private set; } + + /// + /// Gets or sets the type of the optional simple. If not null, then the + /// complex element itself has a type defined for it. + /// + /// The type of the optional simple. + public XmlSchemaSimpleType OptionalSimpleType { get; set; } + + /// + /// Gets or sets the name of the optional simple type. If not null then + /// the complex element itself has a type defined for it. + /// + /// The name of the optional simple type. + public XmlQualifiedName OptionalSimpleTypeName { get; set; } + + #endregion + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override string ToString() + { + return + string.Format( + "SchemaElementComplex{{Attributes: {0}, ComplexElements: {1}, SimpleElements: {2}, Name: {3}, Namespace: {4}, IsArray: {5}, OptionalSimpleType: {6}, OptionalSimpleTypeName: {7}}}", + Attributes, + ComplexElements, + SimpleElements, + Name, + Namespace, + IsArray, + OptionalSimpleType, + OptionalSimpleTypeName); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/xml/SchemaElementSimple.cs b/NEsper.Core/NEsper.Core/events/xml/SchemaElementSimple.cs new file mode 100755 index 000000000..c7143af99 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/xml/SchemaElementSimple.cs @@ -0,0 +1,83 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Xml.Schema; + +namespace com.espertech.esper.events.xml +{ + /// + /// Represents a simple value in a schema. + /// + [Serializable] + public class SchemaElementSimple : SchemaElement + { + /// + /// Ctor. + /// + /// name + /// namespace + /// is the simple element type + /// name of type + /// if unbound + /// The fraction digits. + public SchemaElementSimple( + String name, + String @namespace, + XmlSchemaSimpleType type, + String typeName, + bool isArray, + int? fractionDigits) + { + Name = name; + Namespace = @namespace; + SimpleType = type; + IsArray = isArray; + TypeName = typeName; + } + + /// + /// Returns type. + /// + /// + /// type + /// + public XmlSchemaSimpleType SimpleType { get; private set; } + + /// + /// Returns the type name. + /// + /// + /// type name + /// + public string TypeName { get; private set; } + + #region SchemaElement Members + + /// + /// Returns element name. + /// + /// + /// element name + /// + public string Name { get; private set; } + + public string Namespace { get; private set; } + + public bool IsArray { get; private set; } + + public int? FractionDigits { get; private set; } + + #endregion + + public override String ToString() + { + return "Simple " + Namespace + " " + Name; + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/xml/SchemaItem.cs b/NEsper.Core/NEsper.Core/events/xml/SchemaItem.cs new file mode 100755 index 000000000..ecfdd8059 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/xml/SchemaItem.cs @@ -0,0 +1,17 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.events.xml +{ + /// + /// Marker interface for a schema (simple or complex) element or attribute. + /// + public interface SchemaItem + { + } +} diff --git a/NEsper.Core/NEsper.Core/events/xml/SchemaItemAttribute.cs b/NEsper.Core/NEsper.Core/events/xml/SchemaItemAttribute.cs new file mode 100755 index 000000000..5d7b2c4f0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/xml/SchemaItemAttribute.cs @@ -0,0 +1,72 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Xml.Schema; + +namespace com.espertech.esper.events.xml +{ + /// + /// Represents an attribute in a schema. + /// + [Serializable] + public class SchemaItemAttribute : SchemaItem + { + /// + /// Ctor. + /// + /// namespace + /// name + /// attribute type + /// attribute type name + public SchemaItemAttribute(String @namespace, String name, XmlSchemaSimpleType type, String typeName) + { + Name = name; + Namespace = @namespace; + SimpleType = type; + TypeName = typeName; + } + + /// + /// Returns the namespace. + /// + /// + /// namespace + /// + public string Namespace { get; private set; } + + /// + /// Returns the name. + /// + /// + /// name + /// + public string Name { get; private set; } + + /// + /// Returns the type. + /// + /// + /// type + /// + public XmlSchemaSimpleType SimpleType { get; private set; } + + /// + /// Returns the type name. + /// + /// + /// type name + /// + public string TypeName { get; private set; } + + public override String ToString() + { + return "Attribute " + Namespace + " " + Name; + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/xml/SchemaModel.cs b/NEsper.Core/NEsper.Core/events/xml/SchemaModel.cs new file mode 100755 index 000000000..a91d2d58f --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/xml/SchemaModel.cs @@ -0,0 +1,59 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.events.xml +{ + /// + /// Represents a XSD schema or other metadata for a class of XML documents. + /// + [Serializable] + public class SchemaModel + { + /// + /// Ctor. + /// + /// the top level components. + /// list of namespaces + public SchemaModel(IList components, IList namespaces) + { + Components = components; + Namespaces = namespaces; + } + + /// + /// Ctor. + /// + /// top level component + /// list of namespaces + public SchemaModel(SchemaElementComplex component, IList namespaces) + { + Components = new List(1); + Components.Add(component); + Namespaces = namespaces; + } + + /// + /// Returns top-level components. + /// + /// + /// components + /// + public IList Components { get; private set; } + + /// + /// Returns namespaces. + /// + /// + /// namespaces + /// + public IList Namespaces { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/events/xml/SchemaUtil.cs b/NEsper.Core/NEsper.Core/events/xml/SchemaUtil.cs new file mode 100755 index 000000000..bb588a48e --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/xml/SchemaUtil.cs @@ -0,0 +1,408 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; +using System.Xml; +using System.Xml.Schema; +using System.Xml.XPath; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.util; + +namespace com.espertech.esper.events.xml +{ + /// Utility class for querying schema information. + /// pablo + /// + public class SchemaUtil + { + private static readonly IDictionary TypeMap; + + static SchemaUtil() + { + TypeMap = new Dictionary(); + TypeMap["nonPositiveInteger"] = typeof (int?); + TypeMap["nonNegativeInteger"] = typeof(int?); + TypeMap["negativeInteger"] = typeof(int?); + TypeMap["positiveInteger"] = typeof(int?); + TypeMap["long"] = typeof(long?); + TypeMap["unsignedLong"] = typeof(ulong?); + TypeMap["int"] = typeof(int?); + TypeMap["unsignedInt"] = typeof(uint?); + TypeMap["decimal"] = typeof(double?); + TypeMap["integer"] = typeof(int?); + TypeMap["float"] = typeof(float?); + TypeMap["double"] = typeof(double?); + TypeMap["string"] = typeof (string); + TypeMap["short"] = typeof(short?); + TypeMap["unsignedShort"] = typeof(ushort?); + TypeMap["byte"] = typeof(byte?); + TypeMap["unsignedByte"] = typeof(byte?); + TypeMap["bool"] = typeof(bool?); + TypeMap["boolean"] = typeof(bool?); + TypeMap["dateTime"] = typeof (string); + TypeMap["date"] = typeof (string); + TypeMap["time"] = typeof (string); + } + + + private static XmlSchemaSimpleType _SchemaTypeString = XmlSchemaSimpleType.GetBuiltInSimpleType(XmlTypeCode.String); + private static XmlSchemaSimpleType _SchemaTypeBoolean = XmlSchemaSimpleType.GetBuiltInSimpleType(XmlTypeCode.Boolean); + private static XmlSchemaSimpleType _SchemaTypeInteger = XmlSchemaSimpleType.GetBuiltInSimpleType(XmlTypeCode.Int); + private static XmlSchemaSimpleType _SchemaTypeDecimal = XmlSchemaSimpleType.GetBuiltInSimpleType(XmlTypeCode.Decimal); + private static XmlSchemaSimpleType _SchemaTypeId = XmlSchemaSimpleType.GetBuiltInSimpleType(XmlTypeCode.Id); + private static XmlSchemaSimpleType _SchemaTypeToken = XmlSchemaSimpleType.GetBuiltInSimpleType(XmlTypeCode.Token); + + public static XPathResultType ToXPathResultType(XmlSchemaSimpleType simpleType) + { + if (Equals(simpleType, _SchemaTypeString)) + return XPathResultType.String; + if (Equals(simpleType, _SchemaTypeBoolean)) + return XPathResultType.Boolean; + if (Equals(simpleType, _SchemaTypeInteger) || + Equals(simpleType, _SchemaTypeDecimal)) + return XPathResultType.Number; + if (Equals(simpleType, _SchemaTypeId)) + return XPathResultType.String; + if (Equals(simpleType, _SchemaTypeToken)) + return XPathResultType.String; + return XPathResultType.Any; + } + + /// + /// Returns the Type-type of the schema item. + /// + /// to to determine type for + /// + /// type + /// + public static Type ToReturnType(SchemaItem item) + { + if (item is SchemaItemAttribute) { + var att = (SchemaItemAttribute) item; + return ToReturnType(att.SimpleType, att.TypeName); + } + + if (item is SchemaElementSimple) { + var simple = (SchemaElementSimple) item; + var returnType = ToReturnType(simple.SimpleType, simple.TypeName); + if (simple.IsArray) { + returnType = Array.CreateInstance(returnType, 0).GetType(); + } + return returnType; + } + + if (item is SchemaElementComplex) { + var complex = (SchemaElementComplex) item; + if (complex.OptionalSimpleType != null) { + return ToReturnType(ToXPathResultType(complex.OptionalSimpleType), + complex.OptionalSimpleTypeName.Name); + } + if (complex.IsArray) { + return typeof (XmlNodeList); + } + return typeof (XmlNode); + } + + throw new PropertyAccessException("Invalid schema return type:" + item); + } + + public static Type ToReturnType(XmlQualifiedName qname) + { + if (qname.Namespace == XMLConstants.W3C_XML_SCHEMA_NS_URI) + { + switch (qname.Name) + { + case "ID": + case "string": + return typeof(string); + case "bool": + return typeof(bool?); + case "nonPositiveInteger": + return typeof(int?); + case "nonNegativeInteger": + return typeof(int?); + case "negativeInteger": + return typeof(int?); + case "positiveInteger": + return typeof(int?); + case "unsignedLong": + return typeof(ulong?); + case "unsignedInt": + return typeof(uint?); + case "int": + case "integer": + return typeof(int?); + case "short": + return typeof(short?); + case "unsignedShort": + return typeof(ushort?); + case "byte": + return typeof(byte?); + case "unsignedByte": + return typeof(byte?); + case "long": + return typeof(long?); + case "decimal": + return typeof(double?); + case "double": + return typeof(double?); + case "float": + return typeof(float?); + case "anyURI": + return typeof(XmlNode); + case "anySimpleType": + return typeof (object); + } + } + + return null; + } + + public static Type ToReturnType(XmlSchemaSimpleType simpleType, string typeName) + { + if (typeName != null) { + var type = TypeMap.Get(typeName); + if ( type != null ) { + return type; + } + } + + var qualified = simpleType.QualifiedName; + var returnType = ToReturnType(qualified); + if (returnType != null) + { + return returnType; + } + + var asRestrictedType = simpleType.Content as XmlSchemaSimpleTypeRestriction; + if (asRestrictedType != null) + { + if (asRestrictedType.BaseTypeName != null) + { + var baseReturnType = ToReturnType(qualified = asRestrictedType.BaseTypeName); + if (baseReturnType != null) + { + return baseReturnType; + } + } + } + + throw new EPException("Unable to convert qualified type '" + qualified + "' to return result"); + } + + /// + /// Returns the native type based on XPathConstants qname and an optional cast-to + /// type, if provided. + /// + /// qname + /// Name of the optional cast to type. + /// return type + public static Type ToReturnType(XPathResultType resultType, String optionalCastToTypeName) + { + Type optionalCastToType = null; + if (optionalCastToTypeName != null) { + optionalCastToType = TypeHelper.GetPrimitiveTypeForName(optionalCastToTypeName); + if (optionalCastToType == null) { + optionalCastToType = TypeHelper.ResolveType(optionalCastToTypeName, false); + } + } + + return ToReturnType(resultType, optionalCastToType); + } + + /// + /// Returns the native type based on XPathConstants qname and an optional cast-to + /// type, if provided. + /// + /// qname + /// null or cast-to type + /// + /// return type + /// + public static Type ToReturnType(XPathResultType resultType, Type optionalCastToType) + { + if (optionalCastToType != null) { + return optionalCastToType; + } + + if (resultType == XPathResultType.NodeSet) + return typeof (XmlNodeList); + if (resultType == XPathResultType.Any) + return typeof (XmlNode); + if (resultType == XPathResultType.Boolean) + return typeof (bool?); + if (resultType == XPathResultType.Number) + return typeof (double?); + if (resultType == XPathResultType.String) + return typeof (string); + + return typeof (string); + } + + public static XPathResultType SimpleTypeToResultType(XmlSchemaSimpleType definition) + { + var qname = definition.QualifiedName; + if ( qname.Namespace == XMLConstants.W3C_XML_SCHEMA_NS_URI ) { + switch( qname.Name ) { + case "ID": + case "string": + return XPathResultType.String; + case "bool": + case "boolean": + return XPathResultType.Boolean; + + case "nonPositiveInteger": + case "nonNegativeInteger": + case "negativeInteger": + case "positiveInteger": + case "unsignedLong": + case "unsignedInt": + case "integer": + case "short": + case "unsignedShort": + case "byte": + case "unsignedByte": + case "int": + case "long": + case "decimal": + case "double": + case "float": + return XPathResultType.Number; + case "anyURI": + return XPathResultType.Any; + } + } + + throw new EPException("Unable to convert qualified type '" + qname + "' to path result"); + } + + /// + /// Returns the XPathConstants type for a given Xerces type definition. + /// + /// the schema element definition. + /// XPathConstants type + public static XmlQualifiedName SimpleTypeToQName(XmlSchemaSimpleType definition) + { + return definition.QualifiedName; + } + + public static SchemaElementComplex FindRootElement(SchemaModel schema, String @namespace, String elementName) + { + if (!string.IsNullOrEmpty(@namespace)) { + foreach (SchemaElementComplex complexElement in schema.Components) { + if ((complexElement.Namespace.Equals(@namespace)) && (complexElement.Name.Equals(elementName))) { + return complexElement; + } + } + } + else { + foreach (SchemaElementComplex complexElement in schema.Components) { + if (complexElement.Name.Equals(elementName)) { + return complexElement; + } + } + } + + if (elementName.StartsWith("//")) { + elementName = elementName.Substring(2); + foreach (SchemaElementComplex complexElement in schema.Components) { + SchemaElementComplex match = RecursiveDeepMatch(complexElement, @namespace, elementName); + if (match != null) { + return match; + } + } + } + + String text = "Could not find root element declaration in schema for element name '" + elementName + '\''; + if (@namespace != null) { + text = text + " in namespace '" + @namespace + '\''; + } + throw new EPException(text); + } + + private static SchemaElementComplex RecursiveDeepMatch(SchemaElementComplex parent, String @namespace, + String elementName) + { + if (!string.IsNullOrEmpty(@namespace)) { + foreach (SchemaElementComplex complexElement in parent.ComplexElements) { + if ((complexElement.Namespace.Equals(@namespace)) && (complexElement.Name.Equals(elementName))) { + return complexElement; + } + } + } + else { + foreach (SchemaElementComplex complexElement in parent.ComplexElements) { + if (complexElement.Name.Equals(elementName)) { + return complexElement; + } + } + } + + foreach (SchemaElementComplex complexElement in parent.ComplexElements) { + SchemaElementComplex found = RecursiveDeepMatch(complexElement, @namespace, elementName); + if (found != null) { + return found; + } + } + + return null; + } + + + /// + /// Finds an apropiate definition for the given property, starting at the * given + /// definition. First look if the property es an attribute. If not, look at simple and + /// then child element definitions. + /// + /// the definition to start looking + /// the property to look for + /// + /// schema element or null if not found + /// + public static SchemaItem FindPropertyMapping(SchemaElementComplex def, String property) + { + foreach (SchemaItemAttribute attribute in def.Attributes) { + if (attribute.Name == property) { + return attribute; + } + } + + foreach (SchemaElementSimple simple in def.SimpleElements) { + if (simple.Name == property) { + return simple; + } + } + + foreach (SchemaElementComplex complex in def.ComplexElements) { + if (complex.Name == property) { + return complex; + } + } + + //property not found in schema + return null; + } + + /// + /// Serialize the given node. + /// + /// node to serialize + /// + /// serialized node string + /// + public static String Serialize(XmlNode doc) + { + return doc.OuterXml; + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/xml/SchemaXMLEventType.cs b/NEsper.Core/NEsper.Core/events/xml/SchemaXMLEventType.cs new file mode 100755 index 000000000..ef85faf8a --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/xml/SchemaXMLEventType.cs @@ -0,0 +1,391 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Text; +using System.Xml; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.parse; +using com.espertech.esper.events.property; + + +namespace com.espertech.esper.events.xml +{ + /// + /// EventType for xml events that have a Schema. Mapped and Indexed properties are + /// supported. All property types resolved via the declared xsd types. Can access + /// attributes. Validates the property string at construction time. + /// + /// pablo + public class SchemaXMLEventType : BaseXMLEventType + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly bool _isPropertyExpressionXPath; + private readonly IDictionary _propertyGetterCache; + private readonly String _rootElementNamespace; + + private readonly SchemaModel _schemaModel; + private readonly SchemaElementComplex _schemaModelRoot; + + /// + /// Ctor. + /// + /// event type metadata + /// The event type id. + /// configuration for type + /// the schema representation + /// type lookup and registration + public SchemaXMLEventType(EventTypeMetadata eventTypeMetadata, int eventTypeId, ConfigurationEventTypeXMLDOM config, SchemaModel schemaModel, EventAdapterService eventAdapterService) + : base(eventTypeMetadata, eventTypeId, config, eventAdapterService) + { + _propertyGetterCache = new Dictionary(); + _schemaModel = schemaModel; + _rootElementNamespace = config.RootElementNamespace; + _schemaModelRoot = SchemaUtil.FindRootElement(schemaModel, _rootElementNamespace, RootElementName); + _isPropertyExpressionXPath = config.IsXPathPropertyExpr; + + // Set of namespace context for XPath expressions + var ctx = new XPathNamespaceContext(); + if (config.DefaultNamespace != null) + { + ctx.SetDefaultNamespace(config.DefaultNamespace); + } + + foreach (var entry in config.NamespacePrefixes) + { + ctx.AddNamespace(entry.Key, entry.Value); + } + + NamespaceContext = ctx; + + // add properties for the root element + var additionalSchemaProps = new List(); + + // Add a property for each complex child element + foreach (SchemaElementComplex complex in _schemaModelRoot.ComplexElements) + { + var propertyName = complex.Name; + var returnType = typeof(XmlNode); + Type propertyComponentType = null; + + if (complex.OptionalSimpleType != null) + { + returnType = SchemaUtil.ToReturnType(complex); + } + if (complex.IsArray) + { + // We use XmlNode[] for arrays and NodeList for XPath-Expressions returning Nodeset + returnType = typeof(XmlNode[]); + propertyComponentType = typeof (XmlNode); + } + + bool isFragment = false; + if (ConfigurationEventTypeXMLDOM.IsAutoFragment && (!ConfigurationEventTypeXMLDOM.IsXPathPropertyExpr)) + { + isFragment = CanFragment(complex); + } + + var indexType = returnType.GetIndexType(); + var isIndexed = indexType != null; + var getter = DoResolvePropertyGetter(propertyName, true); + var desc = new EventPropertyDescriptor(propertyName, returnType, indexType, false, false, isIndexed, false, isFragment); + var @explicit = new ExplicitPropertyDescriptor(desc, getter, false, null); + additionalSchemaProps.Add(@explicit); + } + + // Add a property for each simple child element + foreach (SchemaElementSimple simple in _schemaModelRoot.SimpleElements) + { + var propertyName = simple.Name; + var returnType = SchemaUtil.ToReturnType(simple); + var getter = DoResolvePropertyGetter(propertyName, true); + var indexType = returnType.GetIndexType(); + var isIndexed = indexType != null; + var desc = new EventPropertyDescriptor(propertyName, returnType, indexType, false, false, isIndexed, false, false); + var @explicit = new ExplicitPropertyDescriptor(desc, getter, false, null); + additionalSchemaProps.Add(@explicit); + } + + // Add a property for each attribute + foreach (SchemaItemAttribute attribute in _schemaModelRoot.Attributes) + { + var propertyName = attribute.Name; + var returnType = SchemaUtil.ToReturnType(attribute); + var getter = DoResolvePropertyGetter(propertyName, true); + var indexType = returnType.GetIndexType(); + var isIndexed = indexType != null; + var desc = new EventPropertyDescriptor(propertyName, returnType, indexType, false, false, isIndexed, false, false); + var @explicit = new ExplicitPropertyDescriptor(desc, getter, false, null); + additionalSchemaProps.Add(@explicit); + } + + // Finally add XPath properties as that may depend on the rootElementNamespace + Initialize(config.XPathProperties.Values, additionalSchemaProps); + } + + public SchemaModel SchemaModel + { + get { return _schemaModel; } + } + + public SchemaElementComplex SchemaModelRoot + { + get { return _schemaModelRoot; } + } + + public string RootElementNamespace + { + get { return _rootElementNamespace; } + } + + protected override FragmentEventType DoResolveFragmentType(String property) + { + if ((!ConfigurationEventTypeXMLDOM.IsAutoFragment) || (ConfigurationEventTypeXMLDOM.IsXPathPropertyExpr)) + { + return null; + } + + Property prop = PropertyParser.ParseAndWalkLaxToSimple(property); + + SchemaItem item = prop.GetPropertyTypeSchema(_schemaModelRoot, EventAdapterService); + if ((item == null) || (!CanFragment(item))) + { + return null; + } + var complex = (SchemaElementComplex)item; + + // build name of event type + String[] atomicProps = prop.ToPropertyArray(); + String delimiterDot = "."; + var eventTypeNameBuilder = new StringBuilder(Name); + foreach (String atomic in atomicProps) + { + eventTypeNameBuilder.Append(delimiterDot); + eventTypeNameBuilder.Append(atomic); + } + String eventTypeName = eventTypeNameBuilder.ToString(); + + // check if the type exists, use the existing type if found + EventType existingType = EventAdapterService.GetEventTypeByName(eventTypeName); + if (existingType != null) + { + return new FragmentEventType(existingType, complex.IsArray, false); + } + + // add a new type + var xmlDom = new ConfigurationEventTypeXMLDOM(); + xmlDom.RootElementName = "//" + complex.Name; // such the reload of the type can resolve it + xmlDom.RootElementNamespace = complex.Namespace; + xmlDom.IsAutoFragment = ConfigurationEventTypeXMLDOM.IsAutoFragment; + xmlDom.IsEventSenderValidatesRoot = ConfigurationEventTypeXMLDOM.IsEventSenderValidatesRoot; + xmlDom.IsXPathPropertyExpr = ConfigurationEventTypeXMLDOM.IsXPathPropertyExpr; + xmlDom.IsXPathResolvePropertiesAbsolute = ConfigurationEventTypeXMLDOM.IsXPathResolvePropertiesAbsolute; + xmlDom.SchemaResource = ConfigurationEventTypeXMLDOM.SchemaResource; + xmlDom.SchemaText = ConfigurationEventTypeXMLDOM.SchemaText; + xmlDom.XPathFunctionResolver = ConfigurationEventTypeXMLDOM.XPathFunctionResolver; + xmlDom.XPathVariableResolver = ConfigurationEventTypeXMLDOM.XPathVariableResolver; + xmlDom.DefaultNamespace = ConfigurationEventTypeXMLDOM.DefaultNamespace; + xmlDom.AddNamespacePrefixes(ConfigurationEventTypeXMLDOM.NamespacePrefixes); + + EventType newType; + try + { + newType = EventAdapterService.AddXMLDOMType(eventTypeName, xmlDom, _schemaModel, true); + } + catch (Exception ex) + { + Log.Error( + "Failed to add dynamic event type for fragment of XML schema for property '" + property + "' :" + + ex.Message, ex); + return null; + } + return new FragmentEventType(newType, complex.IsArray, false); + } + + protected override Type DoResolvePropertyType(String propertyExpression) + { + return DoResolvePropertyType(propertyExpression, false); + } + + private Type DoResolvePropertyType(String propertyExpression, bool allowSimpleProperties) + { + // see if this is an indexed property + int index = ASTUtil.UnescapedIndexOfDot(propertyExpression); + if ((!allowSimpleProperties) && (index == -1)) + { + // parse, can be an indexed property + Property property = PropertyParser.ParseAndWalkLaxToSimple(propertyExpression); + if (!property.IsDynamic) + { + if (!(property is IndexedProperty)) + { + return null; + } + var indexedProp = (IndexedProperty)property; + EventPropertyDescriptor descriptor = PropertyDescriptorMap.Get(indexedProp.PropertyNameAtomic); + if (descriptor == null) + { + return null; + } + return descriptor.PropertyType; + } + } + + Property prop = PropertyParser.ParseAndWalkLaxToSimple(propertyExpression); + if (prop.IsDynamic) + { + return typeof(XmlNode); + } + + SchemaItem item = prop.GetPropertyTypeSchema(_schemaModelRoot, EventAdapterService); + if (item == null) + { + return null; + } + + return SchemaUtil.ToReturnType(item); + } + + protected override EventPropertyGetter DoResolvePropertyGetter(String property) + { + return DoResolvePropertyGetter(property, false); + } + + private EventPropertyGetter DoResolvePropertyGetter(String propertyExpression, bool allowSimpleProperties) + { + EventPropertyGetter getter = _propertyGetterCache.Get(propertyExpression); + if (getter != null) + { + return getter; + } + + if (!allowSimpleProperties) + { + // see if this is an indexed property + int index = ASTUtil.UnescapedIndexOfDot(propertyExpression); + if (index == -1) + { + // parse, can be an indexed property + Property property = PropertyParser.ParseAndWalkLaxToSimple(propertyExpression); + if (!property.IsDynamic) + { + if (!(property is IndexedProperty)) + { + return null; + } + var indexedProp = (IndexedProperty)property; + getter = PropertyGetters.Get(indexedProp.PropertyNameAtomic); + if (null == getter) + { + return null; + } + EventPropertyDescriptor descriptor = PropertyDescriptorMap.Get(indexedProp.PropertyNameAtomic); + if (descriptor == null) + { + return null; + } + if (!descriptor.IsIndexed) + { + return null; + } + if (descriptor.PropertyType == typeof(XmlNodeList)) + { + FragmentFactory fragmentFactory = new FragmentFactoryDOMGetter( + EventAdapterService, this, indexedProp.PropertyNameAtomic); + return new XPathPropertyArrayItemGetter(getter, indexedProp.Index, fragmentFactory); + } + if (descriptor.PropertyType == typeof(string)) + { + FragmentFactory fragmentFactory = new FragmentFactoryDOMGetter( + EventAdapterService, this, indexedProp.PropertyNameAtomic); + return new XPathPropertyArrayItemGetter(getter, indexedProp.Index, fragmentFactory); + } + } + } + } + + if (!_isPropertyExpressionXPath) + { + Property prop = PropertyParser.ParseAndWalkLaxToSimple(propertyExpression); + bool isDynamic = prop.IsDynamic; + + if (!isDynamic) + { + SchemaItem item = prop.GetPropertyTypeSchema(_schemaModelRoot, EventAdapterService); + if (item == null) + { + return null; + } + + getter = prop.GetGetterDOM(_schemaModelRoot, EventAdapterService, this, propertyExpression); + if (getter == null) + { + return null; + } + + Type returnType = SchemaUtil.ToReturnType(item); + if ((returnType != typeof(XmlNode)) && (returnType != typeof(XmlNodeList))) + { + if (!returnType.IsArray) + { + getter = new DOMConvertingGetter(propertyExpression, (DOMPropertyGetter)getter, returnType); + } + else + { + getter = new DOMConvertingArrayGetter((DOMPropertyGetter)getter, returnType.GetElementType()); + } + } + } + else + { + return prop.GetGetterDOM(); + } + } + else + { + bool allowFragments = !ConfigurationEventTypeXMLDOM.IsXPathPropertyExpr; + getter = SchemaXMLPropertyParser.GetXPathResolution( + propertyExpression, + NamespaceContext, + RootElementName, + _rootElementNamespace, + _schemaModel, + EventAdapterService, + this, + allowFragments, + ConfigurationEventTypeXMLDOM.DefaultNamespace); + } + + _propertyGetterCache.Put(propertyExpression, getter); + return getter; + } + + private static bool CanFragment(SchemaItem item) + { + if (!(item is SchemaElementComplex)) + { + return false; + } + + var complex = (SchemaElementComplex)item; + if (complex.OptionalSimpleType != null) + { + return false; // no transposing if the complex type also has a simple value else that is hidden + } + + return true; + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/xml/SchemaXMLPropertyParser.cs b/NEsper.Core/NEsper.Core/events/xml/SchemaXMLPropertyParser.cs new file mode 100755 index 000000000..1be24be6e --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/xml/SchemaXMLPropertyParser.cs @@ -0,0 +1,316 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Text; +using System.Xml; +using System.Xml.XPath; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.logging; +using com.espertech.esper.events.property; +using com.espertech.esper.util; + +namespace com.espertech.esper.events.xml +{ + /// + /// Parses event property names and transforms to XPath expressions using the schema + /// information supplied. Supports the nested, indexed and mapped event properties. + /// + public class SchemaXMLPropertyParser + { + /// + /// Return the xPath corresponding to the given property. The PropertyName String + /// may be simple, nested, indexed or mapped. + /// + /// is the event property name + /// is the default namespace + /// is the schema model + /// is the xpath factory instance to use + /// is the name of the root element + /// for type lookup and creation + /// the resolving type + /// whether fragmenting is allowed + /// default namespace + /// + /// xpath expression + /// + /// EPException is there are XPath errors + public static EventPropertyGetter GetXPathResolution(String propertyName, + XPathNamespaceContext xPathContext, + String rootElementName, + String @namespace, + SchemaModel schemaModel, + EventAdapterService eventAdapterService, + BaseXMLEventType xmlEventType, + bool isAllowFragment, + String defaultNamespace) + { + if (Log.IsDebugEnabled) + { + Log.Debug("Determining XPath expression for property '" + propertyName + "'"); + } + + var ctx = new XPathNamespaceContext(); + var namespaces = schemaModel.Namespaces; + + string defaultNamespacePrefix = null; + for (int i = 0; i < namespaces.Count; i++) + { + var namespacePrefix = "n" + i; + ctx.AddNamespace(namespacePrefix, namespaces[i]); + if ((defaultNamespace != null) && (defaultNamespace == namespaces[i])) + { + defaultNamespacePrefix = namespacePrefix; + } + } + + var property = PropertyParser.ParseAndWalkLaxToSimple(propertyName); + var isDynamic = property.IsDynamic; + + var rootComplexElement = SchemaUtil.FindRootElement(schemaModel, @namespace, rootElementName); + string prefix = ctx.LookupPrefix(rootComplexElement.Namespace); + if (prefix == null) + { + prefix = ""; + } + else + { + prefix += ':'; + } + + var xPathBuf = new StringBuilder(); + xPathBuf.Append('/'); + xPathBuf.Append(prefix); + if (rootElementName.StartsWith("//")) + { + xPathBuf.Append(rootElementName.Substring(2)); + } + else + { + xPathBuf.Append(rootElementName); + } + + var parentComplexElement = rootComplexElement; + Pair pair = null; + + if (!(property is NestedProperty)) + { + pair = MakeProperty(rootComplexElement, property, ctx, true, isDynamic, defaultNamespacePrefix); + if (pair == null) + { + throw new PropertyAccessException("Failed to locate property '" + propertyName + "' in schema"); + } + xPathBuf.Append(pair.First); + } + else + { + NestedProperty nestedProperty = (NestedProperty)property; + int indexLast = nestedProperty.Properties.Count - 1; + + for (int i = 0; i < indexLast + 1; i++) + { + var isLast = i == indexLast; + var propertyNested = nestedProperty.Properties[i]; + pair = MakeProperty(parentComplexElement, propertyNested, ctx, isLast, isDynamic, defaultNamespacePrefix); + if (pair == null) + { + throw new PropertyAccessException("Failed to locate property '" + propertyName + "' nested property part '" + property.PropertyNameAtomic + "' in schema"); + } + + var text = propertyNested.PropertyNameAtomic; + var obj = SchemaUtil.FindPropertyMapping(parentComplexElement, text); + if (obj is SchemaElementComplex) + { + parentComplexElement = (SchemaElementComplex)obj; + } + xPathBuf.Append(pair.First); + } + } + + var xPath = xPathBuf.ToString(); + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled)) + { + Log.Debug(".parse XPath for property '" + propertyName + "' is expression=" + xPath); + } + + // Compile assembled XPath expression + if (Log.IsDebugEnabled) + { + Log.Debug("Compiling XPath expression '" + xPath + "' for property '" + propertyName + "' using namespace context :" + ctx); + } + + XPathExpression expr; + try + { + expr = XPathExpression.Compile(xPath, ctx); + } + catch (XPathException e) + { + String detail = "Error constructing XPath expression from property expression '" + propertyName + "' expression '" + xPath + "'"; + if (e.Message != null) + { + throw new EPException(detail + " :" + e.Message, e); + } + throw new EPException(detail, e); + } + + // get type + var item = property.GetPropertyTypeSchema(rootComplexElement, eventAdapterService); + if ((item == null) && (!isDynamic)) + { + return null; + } + + var resultType = isDynamic ? typeof(XmlNode) : SchemaUtil.ToReturnType(item); + + FragmentFactory fragmentFactory = null; + if (isAllowFragment) + { + fragmentFactory = new FragmentFactoryDOMGetter(eventAdapterService, xmlEventType, propertyName); + } + + return new XPathPropertyGetter(propertyName, xPath, expr, pair.Second, resultType, fragmentFactory); + } + + private static Pair MakeProperty(SchemaElementComplex parent, Property property, XPathNamespaceContext ctx, bool isLast, bool isDynamic, String defaultNamespacePrefix) + { + var text = property.PropertyNameAtomic; + var obj = SchemaUtil.FindPropertyMapping(parent, text); + if ((obj is SchemaElementSimple) || (obj is SchemaElementComplex)) + { + return MakeElementProperty((SchemaElement)obj, property, ctx, isLast, isDynamic, defaultNamespacePrefix); + } + if (obj != null) + { + return MakeAttributeProperty((SchemaItemAttribute)obj, property, ctx); + } + if (isDynamic) + { + return MakeElementProperty(null, property, ctx, isLast, isDynamic, defaultNamespacePrefix); + } + + return null; + } + + private static Pair MakeAttributeProperty(SchemaItemAttribute attribute, Property property, XPathNamespaceContext ctx) + { + var prefix = ctx.LookupPrefix(attribute.Namespace); + if (String.IsNullOrEmpty(prefix)) + { + prefix = ""; + } + else + { + prefix += ':'; + } + + if (IsAnySimpleProperty(property)) + { + XPathResultType type = SchemaUtil.SimpleTypeToResultType(attribute.SimpleType); + String path = "/@" + prefix + property.PropertyNameAtomic; + return new Pair(path, type); + } + + if (IsAnyMappedProperty(property)) + { + throw new Exception("Mapped properties not applicable to attributes"); + } + + throw new Exception("Indexed properties not applicable to attributes"); + } + + private static Pair MakeElementProperty(SchemaElement schemaElement, Property property, XPathNamespaceContext ctx, bool isAlone, bool isDynamic, String defaultNamespacePrefix) + { + XPathResultType type; + if (isDynamic) + { + type = XPathResultType.Any; + } + else if (schemaElement is SchemaElementSimple) + { + var element = (SchemaElementSimple)schemaElement; + type = SchemaUtil.SimpleTypeToResultType(element.SimpleType); + } + else + { + var complex = (SchemaElementComplex)schemaElement; + type = XPathResultType.Any; + //if (complex.OptionalSimpleType != null) + //{ + // type = SchemaUtil.SimpleTypeToQName(complex.OptionalSimpleType); + //} + //else + //{ + // // The result is a node + // type = XPathResultType.Any; + //} + } + + var prefix = isDynamic ? defaultNamespacePrefix : ctx.LookupPrefix(schemaElement.Namespace); + if (String.IsNullOrEmpty(prefix)) + { + prefix = String.Empty; + } + else + { + prefix += ':'; + } + + if (IsAnySimpleProperty(property)) + { + if (!isDynamic && schemaElement.IsArray && !isAlone) + { + throw new PropertyAccessException("Simple property not allowed in repeating elements at '" + schemaElement.Name + "'"); + } + return new Pair('/' + prefix + property.PropertyNameAtomic, type); + } + + if (IsAnyMappedProperty(property)) + { + if (!isDynamic && !schemaElement.IsArray) + { + throw new PropertyAccessException("Element " + property.PropertyNameAtomic + " is not a collection, cannot be used as mapped property"); + } + String key = GetMappedPropertyKey(property); + return new Pair('/' + prefix + property.PropertyNameAtomic + "[@id='" + key + "']", type); + } + + if (!isDynamic && !schemaElement.IsArray) + { + throw new PropertyAccessException("Element " + property.PropertyNameAtomic + " is not a collection, cannot be used as mapped property"); + } + int index = GetIndexedPropertyIndex(property); + int xPathPosition = index + 1; + return new Pair('/' + prefix + property.PropertyNameAtomic + "[position() = " + xPathPosition + ']', type); + } + + private static bool IsAnySimpleProperty(Property property) + { + return property is SimpleProperty || property is DynamicSimpleProperty; + } + + private static bool IsAnyMappedProperty(Property property) + { + return property is MappedProperty || property is DynamicMappedProperty; + } + + private static int GetIndexedPropertyIndex(Property property) + { + return property is IndexedProperty ? ((IndexedProperty)property).Index : ((DynamicIndexedProperty)property).Index; + } + + private static String GetMappedPropertyKey(Property property) + { + return property is MappedProperty ? ((MappedProperty)property).Key : ((DynamicMappedProperty)property).Key; + } + + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + } +} diff --git a/NEsper.Core/NEsper.Core/events/xml/SendableEventXML.cs b/NEsper.Core/NEsper.Core/events/xml/SendableEventXML.cs new file mode 100755 index 000000000..76bbfe58d --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/xml/SendableEventXML.cs @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Xml; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.xml +{ + public class SendableEventXML : SendableEvent + { + private readonly XmlNode _event; + + public SendableEventXML(XmlNode theEvent) + { + _event = theEvent; + } + + public void Send(EPRuntime runtime) + { + runtime.SendEvent(_event); + } + + public object Underlying + { + get { return _event; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/xml/SimpleXElementType.cs b/NEsper.Core/NEsper.Core/events/xml/SimpleXElementType.cs new file mode 100755 index 000000000..99c45aab5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/xml/SimpleXElementType.cs @@ -0,0 +1,148 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Xml.Linq; +using System.Xml.XPath; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.generated; +using com.espertech.esper.events.property; + +namespace com.espertech.esper.events.xml +{ + public class SimpleXElementType : BaseXMLEventType + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly IDictionary _propertyGetterCache; + private readonly string _defaultNamespacePrefix; + private readonly bool _isResolvePropertiesAbsolute; + + /// + /// Ctor. + /// + /// event type metadata + /// The event type id. + /// configures the event type + /// for type looking and registration + public SimpleXElementType(EventTypeMetadata eventTypeMetadata, + int eventTypeId, + ConfigurationEventTypeXMLDOM configurationEventTypeXMLDOM, + EventAdapterService eventAdapterService) + : base(eventTypeMetadata, eventTypeId, configurationEventTypeXMLDOM, eventAdapterService) + { + _isResolvePropertiesAbsolute = configurationEventTypeXMLDOM.IsXPathResolvePropertiesAbsolute; + + // Set of namespace context for XPath expressions + var namespaceContext = new XPathNamespaceContext(); + foreach (var entry in configurationEventTypeXMLDOM.NamespacePrefixes) + { + namespaceContext.AddNamespace(entry.Key, entry.Value); + } + + if (configurationEventTypeXMLDOM.DefaultNamespace != null) + { + var defaultNamespace = configurationEventTypeXMLDOM.DefaultNamespace; + namespaceContext.AddNamespace(String.Empty, defaultNamespace); + + // determine a default namespace prefix to use to construct XPath expressions from pure property names + _defaultNamespacePrefix = null; + foreach (var entry in configurationEventTypeXMLDOM.NamespacePrefixes) + { + if (Equals(entry.Value, defaultNamespace)) + { + _defaultNamespacePrefix = entry.Key; + break; + } + } + } + + NamespaceContext = namespaceContext; + Initialize( + configurationEventTypeXMLDOM.XPathProperties.Values, + Collections.GetEmptyList()); + + _propertyGetterCache = new Dictionary(); + } + + protected override Type DoResolvePropertyType(String propertyExpression) + { + var ast = PropertyParser.Parse(propertyExpression); + return PropertyParser.IsPropertyDynamic(ast) + ? typeof(XNode) + : typeof(string); + } + + protected override EventPropertyGetter DoResolvePropertyGetter(String propertyExpression) + { + var getter = _propertyGetterCache.Get(propertyExpression); + if (getter != null) + { + return getter; + } + + if (!ConfigurationEventTypeXMLDOM.IsXPathPropertyExpr) + { + var prop = PropertyParser.ParseAndWalkLaxToSimple(propertyExpression); + getter = prop.GetGetterDOM(); + if (!prop.IsDynamic) + { + getter = new DOMConvertingGetter(propertyExpression, (DOMPropertyGetter)getter, typeof(string)); + } + } + else + { + try + { + var ast = PropertyParser.Parse(propertyExpression); + var isDynamic = PropertyParser.IsPropertyDynamic(ast); + var xPathExpr = SimpleXMLPropertyParser.Walk( + ast, + propertyExpression, + RootElementName, + _defaultNamespacePrefix, + _isResolvePropertiesAbsolute); + + if (Log.IsInfoEnabled) + { + Log.Info("Compiling XPath expression for property '" + propertyExpression + "' as '" + xPathExpr + + "'"); + } + + var xPathExpression = XPathExpression.Compile(xPathExpr, NamespaceContext); + var xPathReturnType = isDynamic ? XPathResultType.Any : XPathResultType.String; + getter = new XPathPropertyGetter( + propertyExpression, + xPathExpr, + xPathExpression, + xPathReturnType, + null, + null); + } + catch (XPathException e) + { + throw new EPException( + "Error constructing XPath expression from property name '" + propertyExpression + '\'', e); + } + } + + // no fragment factory, fragments not allowed + _propertyGetterCache.Put(propertyExpression, getter); + return getter; + } + + protected override FragmentEventType DoResolveFragmentType(String property) + { + return null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/xml/SimpleXMLEventType.cs b/NEsper.Core/NEsper.Core/events/xml/SimpleXMLEventType.cs new file mode 100755 index 000000000..05832b1bd --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/xml/SimpleXMLEventType.cs @@ -0,0 +1,158 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Xml; +using System.Xml.XPath; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.generated; +using com.espertech.esper.events.property; + +namespace com.espertech.esper.events.xml +{ + /// + /// Optimistic try to resolve the property string into an appropiate xPath, + /// and use it as getter. + /// Mapped and Indexed properties supported. + /// Because no type information is given, all property are resolved to String. + /// No namespace support. + /// Cannot access to xml attributes, only elements content. + /// + /// If an xsd is present, then use + /// + /// + public class SimpleXMLEventType : BaseXMLEventType + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly IDictionary _propertyGetterCache; + private readonly string _defaultNamespacePrefix; + private readonly bool _isResolvePropertiesAbsolute; + + /// + /// Ctor. + /// + /// event type metadata + /// The event type id. + /// configures the event type + /// for type looking and registration + public SimpleXMLEventType(EventTypeMetadata eventTypeMetadata, + int eventTypeId, + ConfigurationEventTypeXMLDOM configurationEventTypeXMLDOM, + EventAdapterService eventAdapterService) + : base(eventTypeMetadata, eventTypeId, configurationEventTypeXMLDOM, eventAdapterService) + { + _isResolvePropertiesAbsolute = configurationEventTypeXMLDOM.IsXPathResolvePropertiesAbsolute; + _propertyGetterCache = new Dictionary(); + + // Set of namespace context for XPath expressions + var namespaceContext = new XPathNamespaceContext(); + foreach (var entry in configurationEventTypeXMLDOM.NamespacePrefixes) + { + namespaceContext.AddNamespace(entry.Key, entry.Value); + } + + if (configurationEventTypeXMLDOM.DefaultNamespace != null) + { + var defaultNamespace = configurationEventTypeXMLDOM.DefaultNamespace; + namespaceContext.AddNamespace(String.Empty, defaultNamespace); + + // determine a default namespace prefix to use to construct XPath expressions from pure property names + _defaultNamespacePrefix = null; + foreach (var entry in configurationEventTypeXMLDOM.NamespacePrefixes) + { + if (Equals(entry.Value, defaultNamespace)) + { + _defaultNamespacePrefix = entry.Key; + break; + } + } + } + + NamespaceContext = namespaceContext; + Initialize( + configurationEventTypeXMLDOM.XPathProperties.Values, + Collections.GetEmptyList()); + } + + protected override Type DoResolvePropertyType(String propertyExpression) + { + EsperEPL2GrammarParser.StartEventPropertyRuleContext ast = PropertyParser.Parse(propertyExpression); + return PropertyParser.IsPropertyDynamic(ast) + ? typeof(XmlNode) + : typeof(string); + } + + protected override EventPropertyGetter DoResolvePropertyGetter(String propertyExpression) + { + var getter = _propertyGetterCache.Get(propertyExpression); + if (getter != null) + { + return getter; + } + + if (!ConfigurationEventTypeXMLDOM.IsXPathPropertyExpr) + { + Property prop = PropertyParser.ParseAndWalkLaxToSimple(propertyExpression); + getter = prop.GetGetterDOM(); + if (!prop.IsDynamic) + { + getter = new DOMConvertingGetter(propertyExpression, (DOMPropertyGetter)getter, typeof(string)); + } + } + else + { + try + { + var ast = PropertyParser.Parse(propertyExpression); + var isDynamic = PropertyParser.IsPropertyDynamic(ast); + var xPathExpr = SimpleXMLPropertyParser.Walk( + ast, + propertyExpression, + RootElementName, + _defaultNamespacePrefix, + _isResolvePropertiesAbsolute); + + if (Log.IsInfoEnabled) + { + Log.Info("Compiling XPath expression for property '" + propertyExpression + "' as '" + xPathExpr + + "'"); + } + + var xPathExpression = XPathExpression.Compile(xPathExpr, NamespaceContext); + var xPathReturnType = isDynamic ? XPathResultType.Any : XPathResultType.String; + getter = new XPathPropertyGetter( + propertyExpression, + xPathExpr, + xPathExpression, + xPathReturnType, + null, + null); + } + catch (XPathException e) + { + throw new EPException( + "Error constructing XPath expression from property name '" + propertyExpression + '\'', e); + } + } + + // no fragment factory, fragments not allowed + _propertyGetterCache.Put(propertyExpression, getter); + return getter; + } + + protected override FragmentEventType DoResolveFragmentType(String property) + { + return null; // Since we have no type information, the fragments are not allowed unless explicitly configured via XPath getter + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/xml/SimpleXMLPropertyParser.cs b/NEsper.Core/NEsper.Core/events/xml/SimpleXMLPropertyParser.cs new file mode 100755 index 000000000..e0e3ccc79 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/xml/SimpleXMLPropertyParser.cs @@ -0,0 +1,105 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Text; + +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.generated; +using com.espertech.esper.epl.parse; +using com.espertech.esper.type; +using com.espertech.esper.util; + +namespace com.espertech.esper.events.xml +{ + /// + /// Parses event property names and transforms to XPath expressions. Supports + /// nested, indexed and mapped event properties. + /// + + public class SimpleXMLPropertyParser + { + /// + /// Return the xPath corresponding to the given property. The PropertyName String + /// may be simple, nested, indexed or mapped. + /// + /// is the property tree AST + /// is the property name to parse + /// is the name of the root element for generating the XPath expression + /// is the prefix of the default namespace + /// is true to indicate to resolve XPath properties as absolute propsor relative props + /// + /// xpath expression + /// + public static String Walk(EsperEPL2GrammarParser.StartEventPropertyRuleContext ast, String propertyName, String rootElementName, String defaultNamespacePrefix, bool isResolvePropertiesAbsolute) + { + var xPathBuf = new StringBuilder(); + xPathBuf.Append('/'); + if (isResolvePropertiesAbsolute) + { + if (defaultNamespacePrefix != null) + { + xPathBuf.Append(defaultNamespacePrefix); + xPathBuf.Append(':'); + } + xPathBuf.Append(rootElementName); + } + + IList ctxs = ast.eventProperty().eventPropertyAtomic(); + if (ctxs.Count == 1) + { + xPathBuf.Append(MakeProperty(ctxs[0], defaultNamespacePrefix)); + } + else + { + foreach (EsperEPL2GrammarParser.EventPropertyAtomicContext ctx in ctxs) + { + xPathBuf.Append(MakeProperty(ctx, defaultNamespacePrefix)); + } + } + + String xPath = xPathBuf.ToString(); + + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled)) + { + Log.Debug(".parse For property '" + propertyName + "' the xpath is '" + xPath + "'"); + } + + return xPath; + //return XPathExpression.Compile( xPath ); + } + + private static String MakeProperty(EsperEPL2GrammarParser.EventPropertyAtomicContext ctx, String defaultNamespacePrefix) + { + String prefix = ""; + if (defaultNamespacePrefix != null) + { + prefix = defaultNamespacePrefix + ":"; + } + + String unescapedIdent = ASTUtil.UnescapeDot(ctx.eventPropertyIdent().GetText()); + if (ctx.lb != null) + { + int index = IntValue.ParseString(ctx.number().GetText()); + int xPathPosition = index + 1; + return '/' + prefix + unescapedIdent + "[position() = " + xPathPosition + ']'; + } + + if (ctx.lp != null) + { + String key = StringValue.ParseString(ctx.s.Text); + return '/' + prefix + unescapedIdent + "[@id='" + key + "']"; + } + + return '/' + prefix + unescapedIdent; + } + + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + } +} diff --git a/NEsper.Core/NEsper.Core/events/xml/XEventBean.cs b/NEsper.Core/NEsper.Core/events/xml/XEventBean.cs new file mode 100755 index 000000000..d59159795 --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/xml/XEventBean.cs @@ -0,0 +1,89 @@ +using System; +using System.Xml.Linq; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.xml +{ + public class XEventBean : EventBeanSPI + { + private XObject _event; + + /// + /// Ctor. + /// + /// is the node with event property information + /// is the event type for this event wrapper + + public XEventBean(XObject theEvent, EventType type) + { + _event = theEvent; + EventType = type; + } + + /// + /// Return the instance that describes the set of properties available for this event. + /// + /// + /// event type + /// + public EventType EventType { get; private set; } + + /// + /// Get the underlying data object to this event wrapper. + /// + /// + /// underlying data object, usually either a Map or a bean instance. + /// + public Object Underlying + { + get { return _event; } + set { _event = value as XObject; } + } + + /// + /// Returns the value of an event property. + /// + /// + /// the value of a simple property with the specified name. + /// + /// PropertyAccessException - if there is no property of the specified name, or the property cannot be accessed + public Object this[String property] + { + get + { + EventPropertyGetter getter = EventType.GetGetter(property); + if (getter == null) + { + throw new PropertyAccessException("Property named '" + property + "' is not a valid property name for this type"); + } + + return getter.Get(this); + } + } + + /// + /// Returns the value of an event property. This method is a proxy of the indexer. + /// + /// name of the property whose value is to be retrieved + /// + /// the value of a simple property with the specified name. + /// + /// PropertyAccessException - if there is no property of the specified name, or the property cannot be accessed + public Object Get(String property) + { + return this[property]; + } + + public Object GetFragment(String propertyExpression) + { + EventPropertyGetter getter = EventType.GetGetter(propertyExpression); + if (getter == null) + { + throw new PropertyAccessException( + "Property named '" + propertyExpression + "' is not a valid property name for this type"); + } + return getter.GetFragment(this); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/xml/XMLEventBean.cs b/NEsper.Core/NEsper.Core/events/xml/XMLEventBean.cs new file mode 100755 index 000000000..beb76c81d --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/xml/XMLEventBean.cs @@ -0,0 +1,106 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Xml; + +using com.espertech.esper.client; + +namespace com.espertech.esper.events.xml +{ + /// + /// EventBean wrapper for XML documents. Currently only instances of System.Xml.XmlNode can be used + /// + + public class XMLEventBean : EventBeanSPI + { + private XmlNode _event; + + /// + /// Ctor. + /// + /// is the node with event property information + /// is the event type for this event wrapper + + public XMLEventBean(XmlNode theEvent, EventType type) + { + _event = theEvent; + EventType = type; + } + + /// + /// Return the instance that describes the set of properties available for this event. + /// + /// + /// event type + /// + public EventType EventType { get; private set; } + + /// + /// Get the underlying data object to this event wrapper. + /// + /// + /// underlying data object, usually either a Map or a bean instance. + /// + public Object Underlying + { + get { return _event; } + set { _event = value as XmlNode; } + } + + /// + /// Returns the value of an event property. + /// + /// + /// the value of a simple property with the specified name. + /// + /// PropertyAccessException - if there is no property of the specified name, or the property cannot be accessed + public Object this[String property] + { + get + { + var getter = EventType.GetGetter(property); + if (getter == null) + { + throw new PropertyAccessException("Property named '" + property + "' is not a valid property name for this type"); + } + + return getter.Get(this); + } + } + + /// + /// Returns the value of an event property. This method is a proxy of the indexer. + /// + /// name of the property whose value is to be retrieved + /// + /// the value of a simple property with the specified name. + /// + /// PropertyAccessException - if there is no property of the specified name, or the property cannot be accessed + public Object Get(String property) + { + var getter = EventType.GetGetter(property); + if (getter == null) + { + throw new PropertyAccessException("Property named '" + property + "' is not a valid property name for this type"); + } + + return getter.Get(this); + } + + public Object GetFragment(String propertyExpression) + { + EventPropertyGetter getter = EventType.GetGetter(propertyExpression); + if (getter == null) + { + throw PropertyAccessException.NotAValidProperty(propertyExpression); + } + return getter.GetFragment(this); + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/xml/XPathNamespaceContext.cs b/NEsper.Core/NEsper.Core/events/xml/XPathNamespaceContext.cs new file mode 100755 index 000000000..a79b4fe8c --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/xml/XPathNamespaceContext.cs @@ -0,0 +1,146 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Text; +using System.Xml; +using System.Xml.XPath; +using System.Xml.Xsl; + +using com.espertech.esper.compat; + +namespace com.espertech.esper.events.xml +{ + /// + /// Provides the namespace context information for compiling XPath expressions. + /// + public class XPathNamespaceContext : XsltContext + { + /// + /// Ctor. + /// + + public XPathNamespaceContext() + { + base.AddNamespace( + XMLConstants.XML_NS_PREFIX, + XMLConstants.XML_NS_URI); + + // Prefix "xmlns" is reserved for use by XML + + //base.AddNamespace( + // XMLConstants.XMLNS_ATTRIBUTE, + // XMLConstants.XMLNS_ATTRIBUTE_NS_URI); + } + + /// + /// Sets the default namespace. + /// + /// The value. + public void SetDefaultNamespace( string value ) + { + var prefix = XMLConstants.DEFAULT_NS_PREFIX; + if ( HasNamespace(prefix)) { + RemoveNamespace(prefix, LookupNamespace(prefix)); + } + + AddNamespace(prefix, value); + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override string ToString() + { + var builder = new StringBuilder("XPathNamespaceContext default namespace '" + DefaultNamespace + "' maps {"); + var delimiter = ""; + + var namespaceTable = GetNamespacesInScope(XmlNamespaceScope.All); + foreach (var entry in namespaceTable) + { + builder.Append(delimiter); + builder.Append(entry.Key); + builder.Append("="); + builder.Append(entry.Value); + delimiter = ","; + } + + builder.Append("}"); + return builder.ToString(); + } + + /// + /// When overridden in a derived class, resolves a variable reference and returns an representing the variable. + /// + /// The prefix of the variable as it appears in the XPath expression. + /// The name of the variable. + /// + /// An representing the variable at runtime. + /// + public override IXsltContextVariable ResolveVariable(string prefix, string name) + { + return null; + } + + /// + /// When overridden in a derived class, resolves a function reference and returns an representing the function. The is used at execution time to get the return value of the function. + /// + /// The prefix of the function as it appears in the XPath expression. + /// The name of the function. + /// An array of argument types for the function being resolved. This allows you to select between methods with the same name (for example, overloaded methods). + /// + /// An representing the function. + /// + public override IXsltContextFunction ResolveFunction(string prefix, string name, XPathResultType[] ArgTypes) + { + return null; + } + + /// + /// When overridden in a derived class, evaluates whether to preserve white space nodes or strip them for the given context. + /// + /// The white space node that is to be preserved or stripped in the current context. + /// + /// Returns true if the white space is to be preserved or false if the white space is to be stripped. + /// + public override bool PreserveWhitespace(XPathNavigator node) + { + return false; + } + + /// + /// When overridden in a derived class, compares the base Uniform Resource Identifiers (URIs) of two documents based upon the order the documents were loaded by the XSLT processor (that is, the class). + /// + /// The base URI of the first document to compare. + /// The base URI of the second document to compare. + /// + /// An integer value describing the relative order of the two base URIs: -1 if occurs before ; 0 if the two base URIs are identical; and 1 if occurs after . + /// + public override int CompareDocument(string baseUri, string nextbaseUri) + { + throw new NotSupportedException(); + } + + /// + /// When overridden in a derived class, gets a value indicating whether to include white space nodes in the output. + /// + /// + /// + /// true to check white space nodes in the source document for inclusion in the output; false to not evaluate white space nodes. The default is true. + /// + public override bool Whitespace + { + get { return false; } + } + } + + public delegate XPathNamespaceContext XPathNamespaceContextFactory(); +} diff --git a/NEsper.Core/NEsper.Core/events/xml/XPathPropertyArrayItemGetter.cs b/NEsper.Core/NEsper.Core/events/xml/XPathPropertyArrayItemGetter.cs new file mode 100755 index 000000000..1967fa51a --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/xml/XPathPropertyArrayItemGetter.cs @@ -0,0 +1,81 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Xml; +using com.espertech.esper.client; + +namespace com.espertech.esper.events.xml +{ + /// + /// Getter for XPath explicit properties returning an element in an array. + /// + public class XPathPropertyArrayItemGetter : EventPropertyGetter + { + private readonly FragmentFactory fragmentFactory; + private readonly EventPropertyGetter getter; + private readonly int index; + + /// + /// Ctor. + /// + /// property getter returning the parent node + /// to get item at + /// for creating fragments, or null if not creating fragments + public XPathPropertyArrayItemGetter(EventPropertyGetter getter, int index, FragmentFactory fragmentFactory) + { + this.getter = getter; + this.index = index; + this.fragmentFactory = fragmentFactory; + } + + #region EventPropertyGetter Members + + public Object Get(EventBean eventBean) + { + Object result = getter.Get(eventBean); + if (result is XmlNodeList) { + var nodeList = (XmlNodeList) result; + if (nodeList.Count <= index) { + return null; + } + return nodeList.Item(index); + } + + if (result is string) { + var asString = (string) result; + if (asString.Length <= index) { + return null; + } + + return asString[index]; + } + + return null; + } + + public Object GetFragment(EventBean eventBean) + { + if (fragmentFactory == null) { + return null; + } + var result = (XmlNode) Get(eventBean); + if (result == null) { + return null; + } + return fragmentFactory.GetEvent(result); + } + + public bool IsExistsProperty(EventBean eventBean) + { + return true; + } + + #endregion + } +} diff --git a/NEsper.Core/NEsper.Core/events/xml/XPathPropertyGetter.cs b/NEsper.Core/NEsper.Core/events/xml/XPathPropertyGetter.cs new file mode 100755 index 000000000..6f63b843c --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/xml/XPathPropertyGetter.cs @@ -0,0 +1,409 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Xml; +using System.Xml.Linq; +using System.Xml.XPath; + +using com.espertech.esper.client; +using com.espertech.esper.compat.logging; +using com.espertech.esper.compat.xml; +using com.espertech.esper.util; + +namespace com.espertech.esper.events.xml +{ + /// + /// Getter for properties of DOM xml events. + /// + public class XPathPropertyGetter : EventPropertyGetter + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly XPathExpression _expression; + private readonly String _expressionText; + private readonly String _property; + private readonly XPathResultType _resultType; + private readonly SimpleTypeParser _simpleTypeParser; + private readonly Type _optionalCastToType; + private readonly bool _isCastToArray; + private readonly FragmentFactory _fragmentFactory; + + /// + /// Ctor. + /// + /// is the name of the event property for which this getter gets values + /// is the property expression itself + /// is a compile XPath expression + /// is the resulting type + /// if non-null then the return value of the xpath expression is cast to this value + /// for creating fragments, or null in none to be created + public XPathPropertyGetter(String propertyName, + String expressionText, + XPathExpression xPathExpression, + XPathResultType resultType, + Type optionalCastToType, + FragmentFactory fragmentFactory) + { + _expression = xPathExpression; + _expressionText = expressionText; + _property = propertyName; + _resultType = resultType; + _fragmentFactory = fragmentFactory; + + if ((optionalCastToType != null) && (optionalCastToType.IsArray)) + { + _isCastToArray = true; + + if (resultType != XPathResultType.NodeSet) + { + throw new ArgumentException("Array cast-to types require XPathResultType.NodeSet as the XPath result type"); + } + optionalCastToType = optionalCastToType.GetElementType(); + } + else + { + _isCastToArray = false; + } + + if (optionalCastToType != null) + { + _simpleTypeParser = SimpleTypeParserFactory.GetParser(optionalCastToType); + } + else + { + _simpleTypeParser = null; + } + if (optionalCastToType == typeof(XmlNode)) + { + _optionalCastToType = null; + } + else + { + _optionalCastToType = optionalCastToType; + } + } + + public Object Get(EventBean eventBean) + { + var und = eventBean.Underlying; + if (und == null) + { + throw new PropertyAccessException("Unexpected null underlying event encountered, expecting System.Xml.XmlNode instance as underlying"); + } + + XPathNavigator navigator; + + var xnode = und as XElement; + if (xnode == null) + { + var node = und as XmlNode; + if (node == null) + { + throw new PropertyAccessException("Unexpected underlying event of type '" + und.GetType().FullName + + "' encountered, expecting System.Xml.XmlNode as underlying"); + } + + if (Log.IsDebugEnabled) + { + Log.Debug( + "Running XPath '{0}' for property '{1}' against Node XML : {2}", + _expressionText, + _property, + SchemaUtil.Serialize((XmlNode)und)); + } + + navigator = node.CreateNavigator(); + } + else + { + navigator = xnode.CreateNavigator(); + } + + try + { + var result = navigator.Evaluate(_expression); + if (result == null) + { + return null; + } + + // if there is no parser, return xpath expression type + if (_optionalCastToType == null) + { + var nodeIterator = result as XPathNodeIterator; + if (nodeIterator != null) + { + if (nodeIterator.Count == 0) + { + return null; + } + if (nodeIterator.Count == 1) + { + nodeIterator.MoveNext(); + switch (_resultType) + { + case XPathResultType.Any: + return ((System.Xml.IHasXmlNode)nodeIterator.Current).GetNode(); + case XPathResultType.String: + return nodeIterator.Current.TypedValue; + case XPathResultType.Boolean: + return nodeIterator.Current.ValueAsBoolean; + case XPathResultType.Number: + return nodeIterator.Current.ValueAsDouble; + default: + return nodeIterator.Current.TypedValue; + } + } + else + { + return new XPathIteratorNodeList(nodeIterator); + } + } + + return result; + } + + if (_isCastToArray) + { + return CastToArray(result); + } + + if (result is XPathNodeIterator) + { + var nodeIterator = result as XPathNodeIterator; + if (nodeIterator.Count == 0) return null; + if (nodeIterator.Count == 1) + { + nodeIterator.MoveNext(); + result = nodeIterator.Current.TypedValue; + } + else + { + if (_simpleTypeParser == null) + { + var resultList = new List(); + while (nodeIterator.MoveNext()) + { + result = nodeIterator.Current.TypedValue; + resultList.Add(result); + } + + return resultList.ToArray(); + } + else + { + throw new NotImplementedException(); + } + } + } + + // string results get parsed + if (result is String) + { + try + { + return _simpleTypeParser.Invoke((string)result); + } + catch + { + Log.Warn("Error parsing XPath property named '" + _property + "' expression result '" + result + " as type " + _optionalCastToType.Name); + return null; + } + } + + // coercion + if (result is Double) + { + try + { + return CoercerFactory.CoerceBoxed(result, _optionalCastToType); + } + catch + { + Log.Warn("Error coercing XPath property named '" + _property + "' expression result '" + result + " as type " + _optionalCastToType.Name); + return null; + } + } + + // check bool type + if (result is Boolean) + { + if (_optionalCastToType != typeof(bool?)) + { + Log.Warn("Error coercing XPath property named '" + _property + "' expression result '" + result + " as type " + _optionalCastToType.Name); + return null; + } + return result; + } + + Log.Warn("Error processing XPath property named '" + _property + "' expression result '" + result + ", not a known type"); + return null; + } + catch (XPathException e) + { + throw new PropertyAccessException("Error getting property " + _property, e); + } + } + + public bool IsExistsProperty(EventBean eventBean) + { + return true; // Property exists as the property is not dynamic (unchecked) + } + + public Object GetFragment(EventBean eventBean) + { + if (_fragmentFactory == null) + { + return null; + } + + Object und = eventBean.Underlying; + if (und == null) + { + throw new PropertyAccessException("Unexpected null underlying event encountered, expecting System.Xml.XmlNode instance as underlying"); + } + + var node = und as XmlNode; + if (node == null) + { + throw new PropertyAccessException("Unexpected underlying event of type '" + und.GetType().FullName + "' encountered, expecting System.Xml.XmlNode as underlying"); + } + + try + { + if (Log.IsDebugEnabled) + { + Log.Debug( + "Running XPath '{0}' for property '{1}' against Node XML : {2}", + _expressionText, + _property, + SchemaUtil.Serialize((XmlNode)und)); + } + + var navigator = node.CreateNavigator(); + var result = navigator.Evaluate(_expression); + if (result == null) + { + return null; + } + + if (result is XPathNodeIterator) + { + var nodeIterator = result as XPathNodeIterator; + if (nodeIterator.Count == 0) return null; + if (nodeIterator.Count == 1) + { + nodeIterator.MoveNext(); + return _fragmentFactory.GetEvent(((IHasXmlNode)nodeIterator.Current).GetNode()); + } + + var events = new List(); + while (nodeIterator.MoveNext()) + { + events.Add(_fragmentFactory.GetEvent(((IHasXmlNode)nodeIterator.Current).GetNode())); + } + + return events.ToArray(); + } + + Log.Warn("Error processing XPath property named '" + _property + "' expression result is not of type Node or Nodeset"); + return null; + } + catch (XPathException e) + { + throw new PropertyAccessException("Error getting property " + _property, e); + } + } + + private Object CastToArray(XPathNodeIterator nodeIterator) + { + var itemList = new List(); + while (nodeIterator.MoveNext()) + { + var item = nodeIterator.Current; + if (item != null) + { + try + { + if ((item.NodeType == XPathNodeType.Attribute) || + (item.NodeType == XPathNodeType.Element)) + { + var textContent = item.InnerXml; + itemList.Add(_simpleTypeParser.Invoke(textContent)); + } + } + catch + { + if (Log.IsInfoEnabled) + { + Log.Info("Parse error for text content {0} for expression {1}", item.InnerXml, _expression); + } + } + } + } + + var array = Array.CreateInstance(_optionalCastToType, itemList.Count); + for (int ii = 0; ii < itemList.Count; ii++) + { + array.SetValue(itemList[ii], ii); + } + + return array; + } + + private Object CastToArray(Object result) + { + if (result is XPathNodeIterator) + { + return CastToArray((XPathNodeIterator)result); + } + + if (!(result is XmlNodeList)) + { + return null; + } + + var nodeList = (XmlNodeList)result; + var array = Array.CreateInstance(_optionalCastToType, nodeList.Count); + + for (int i = 0; i < nodeList.Count; i++) + { + Object arrayItem = null; + try + { + XmlNode item = nodeList.Item(i); + String textContent; + if ((item.NodeType == XmlNodeType.Attribute) || + (item.NodeType == XmlNodeType.Element)) + { + textContent = item.InnerText; + } + else + { + continue; + } + + arrayItem = _simpleTypeParser.Invoke(textContent); + } + catch + { + if (Log.IsInfoEnabled) + { + Log.Info("Parse error for text content {0} for expression {1}", nodeList[i].InnerText, _expression); + } + } + + array.SetValue(arrayItem, i); + } + + return array; + } + } +} diff --git a/NEsper.Core/NEsper.Core/events/xml/XSDSchemaMapper.cs b/NEsper.Core/NEsper.Core/events/xml/XSDSchemaMapper.cs new file mode 100755 index 000000000..5f6a41d6d --- /dev/null +++ b/NEsper.Core/NEsper.Core/events/xml/XSDSchemaMapper.cs @@ -0,0 +1,774 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Xml; +using System.Xml.Schema; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.core; + +namespace com.espertech.esper.events.xml +{ + /// + /// Helper class for mapping a XSD schema model to an internal representation. + /// + public class XSDSchemaMapper + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + public const int DEFAULT_MAX_RECURSIVE_DEPTH = 10; + + /// + /// Loads a schema from the provided Uri. + /// + /// The URI. + /// The schema text. + /// + public static XmlSchema LoadSchema(Uri uri, String schemaText) + { + if (uri == null) + { + var stringReader = new StringReader(schemaText); + var schema = XmlSchema.Read(stringReader, null); + if (schema == null) + { + throw new ConfigurationException("Failed to read schema from schemaText"); + } + + return schema; + } + + using (var client = new WebClient()) + { + using (var resourceStream = client.OpenRead(uri)) + { + var schema = XmlSchema.Read(resourceStream, null); + if (schema == null) + { + throw new ConfigurationException("Failed to read schema via URL '" + uri + '\''); + } + + return schema; + } + } + } + + /// + /// Loading and mapping of the schema to the internal representation. + /// + /// schema to load and map. + /// The schema text. + /// The engine import service. + /// depth of maximal recursive element + /// + /// model + /// + /// Failed to read schema ' + schemaResource + ' : + ex.Message + public static SchemaModel LoadAndMap( + String schemaResource, + String schemaText, + EngineImportService engineImportService, + int maxRecusiveDepth = DEFAULT_MAX_RECURSIVE_DEPTH) + { + // Load schema + try + { + var schemaLocation = string.IsNullOrEmpty(schemaResource) ? null : ResourceManager.ResolveResourceURL(schemaResource); + var schema = LoadSchema(schemaLocation, schemaText); + return Map(schema, schemaLocation, maxRecusiveDepth); + } + catch (ConfigurationException) + { + throw; + } + catch (Exception ex) + { + throw new ConfigurationException("Failed to read schema '" + schemaResource + "' : " + ex.Message, ex); + } + } + + /// + /// Dictionary that maps qualified names to schema types. + /// + private readonly IDictionary _schemaTypeDictionary = + new Dictionary(); + /// + /// Dictionary that maps qualified names to schema elements. + /// + private readonly IDictionary _schemaElementDictionary = + new Dictionary(); + /// + /// Namespace list + /// + private readonly IList _namespaceList = + new List(); + /// + /// Component list + /// + private readonly IList _components = + new List(); + + /// + /// Resolves a schema type. + /// + /// The xs model. + /// The name. + /// + private XmlSchemaType ResolveSchemaType(XmlSchema xsModel, XmlQualifiedName name) + { + var schemaType = _schemaTypeDictionary.Get(name); + if (schemaType == null) + { + schemaType = ResolveSimpleType(xsModel, name); + } + + return schemaType; + } + + /// + /// Resolves a simple schema type. + /// + /// The xs model. + /// The name. + /// + private static XmlSchemaType ResolveSimpleType(XmlSchema xsModel, XmlQualifiedName name) + { + XmlSchemaType schemaType = XmlSchemaSimpleType.GetBuiltInSimpleType(name); + if (schemaType == null) + { + schemaType = XmlSchemaSimpleType.GetBuiltInComplexType(name); + if (schemaType == null) + { + return xsModel.SchemaTypes[name] as XmlSchemaSimpleType; + } + } + + return schemaType; + } + + /// + /// Resolves an element. + /// + /// The name. + /// + private XmlSchemaElement ResolveElement(XmlQualifiedName name) + { + return _schemaElementDictionary.Get(name); + } + + /// + /// Maps the specified XSD schema into the internal model for the schema. + /// + /// The xs model. + /// The schema location. + /// The max recursive depth. + /// + private static SchemaModel Map(XmlSchema xsModel, Uri schemaLocation, int maxRecursiveDepth) + { + XSDSchemaMapper mapper = new XSDSchemaMapper(); + mapper.Import(xsModel, schemaLocation); + return mapper.CreateModel(); + } + + /// + /// Creates the model. + /// + /// + private SchemaModel CreateModel() + { + return new SchemaModel(_components, _namespaceList); + } + + private void Import(XmlSchema xsModel, Uri schemaLocation) + { + ImportNamespaces(xsModel); + ImportIncludes(xsModel, schemaLocation, Import); + + BuildElementDictionary(xsModel); + BuildTypeDictionary(xsModel); + + // get top-level complex elements + foreach (var schemaElement in xsModel.Items.OfType()) + { + var schemaType = schemaElement.SchemaType; + if (schemaType == null) + { + var schemaTypeName = schemaElement.SchemaTypeName; + if (!Equals(schemaTypeName, XmlQualifiedName.Empty)) + { + schemaType = ResolveSchemaType(xsModel, schemaTypeName); + } + } + + var complexElementType = schemaType as XmlSchemaComplexType; + if (complexElementType != null) + { + var complexActualName = schemaElement.QualifiedName; + if (Equals(complexActualName, XmlQualifiedName.Empty)) + complexActualName = new XmlQualifiedName( + schemaElement.Name, xsModel.TargetNamespace); + + var rootNode = new ElementPathNode(null, complexActualName); + + if (Log.IsDebugEnabled) + { + Log.Debug(string.Format("Processing component {0}", complexActualName)); + } + + SchemaElementComplex complexElement = Process( + xsModel, complexActualName, complexElementType, false, rootNode); + + if (Log.IsDebugEnabled) + { + Log.Debug("Adding component {0}", complexActualName); + } + + _components.Add(complexElement); + } + } + } + + private void ImportIncludes(XmlSchema xsModel, Uri schemaLocation, Action importAction) + { + foreach (var include in xsModel.Includes) + { + var asImport = include as XmlSchemaImport; + if (asImport != null) + { + try + { + String importLocation = asImport.SchemaLocation; + Uri importSchemaLocation; + if (Uri.IsWellFormedUriString(importLocation, UriKind.Absolute)) + { + importSchemaLocation = new Uri(importLocation, UriKind.RelativeOrAbsolute); + } + else + { + importSchemaLocation = new Uri(schemaLocation, importLocation); + } + + var importSchema = LoadSchema(importSchemaLocation, null); + importAction(importSchema, importSchemaLocation); + } + catch (ConfigurationException) + { + throw; + } + catch (Exception ex) + { + throw new ConfigurationException( + "Failed to read schema '" + asImport.SchemaLocation + "' : " + ex.Message, ex); + } + } + } + } + + private void BuildTypeDictionary(XmlSchema xsModel) + { + // get top-level complex elements + foreach (var oelement in xsModel.Items) + { + var complexType = oelement as XmlSchemaComplexType; + if (complexType != null) + { + var qualifiedName = complexType.QualifiedName; + if (Equals(qualifiedName, XmlQualifiedName.Empty)) + { + qualifiedName = new XmlQualifiedName( + complexType.Name, xsModel.TargetNamespace); + } + + _schemaTypeDictionary[qualifiedName] = complexType; + } + } + } + + private void BuildElementDictionary(XmlSchema xsModel) + { + // Organize all of the elements + foreach (var oelement in xsModel.Items) + { + var element = oelement as XmlSchemaElement; + if (element != null) + { + XmlQualifiedName elementName = element.QualifiedName; + if (Equals(elementName, XmlQualifiedName.Empty)) + { + elementName = new XmlQualifiedName( + element.Name, + xsModel.TargetNamespace); + } + + _schemaElementDictionary[elementName] = element; + } + } + } + + /// + /// Imports the namespaces. + /// + /// The namespaces. + private void ImportNamespaces(IEnumerable namespaces) + { + foreach (var @namespace in namespaces) + { + if (!_namespaceList.Contains(@namespace.Namespace)) + { + _namespaceList.Add(@namespace.Namespace); + } + } + } + + /// + /// Imports the namespaces. + /// + /// The xs model. + private void ImportNamespaces(XmlSchema xsModel) + { + ImportNamespaces(xsModel.Namespaces.ToArray()); + } + + private void DetermineOptionalSimpleType( + XmlSchema xsModel, + XmlSchemaComplexType complexActualElement, + out XmlSchemaSimpleType optionalSimpleType, + out XmlQualifiedName optionalSimpleTypeName) + { + optionalSimpleType = null; + optionalSimpleTypeName = null; + + // For complex types, the simple type information can be embedded as an extension + // of the complex type. + if (complexActualElement != null) + { + var contentModel = complexActualElement.ContentModel; + if (contentModel is XmlSchemaSimpleContent) + { + var simpleContentModel = (XmlSchemaSimpleContent)contentModel; + var simpleContentExtension = simpleContentModel.Content as XmlSchemaSimpleContentExtension; + if (simpleContentExtension != null) + { + var simpleContentBaseTypeName = simpleContentExtension.BaseTypeName; + if (!simpleContentBaseTypeName.IsEmpty) + { + var simpleType = ResolveSchemaType(xsModel, simpleContentBaseTypeName) as XmlSchemaSimpleType; + if (simpleType != null) + { + optionalSimpleType = simpleType; + optionalSimpleTypeName = simpleType.QualifiedName; + } + } + } + } + } + } + + private SchemaElementComplex Process( + XmlSchema xsModel, + XmlQualifiedName complexElementName, + XmlSchemaComplexType complexActualElement, + bool isArray, + ElementPathNode node) + { + if (Log.IsDebugEnabled) + { + Log.Debug( + "Processing complex {0} {1} stack {2}", + complexElementName.Namespace, + complexElementName.Name, + node); + } + + var attributes = new List(); + var simpleElements = new List(); + var complexElements = new List(); + + XmlSchemaSimpleType optionalSimpleType = null; + XmlQualifiedName optionalSimpleTypeName = null; + + DetermineOptionalSimpleType( + xsModel, + complexActualElement, + out optionalSimpleType, + out optionalSimpleTypeName + ); + + var complexElement = new SchemaElementComplex( + complexElementName.Name, + complexElementName.Namespace, + attributes, + complexElements, + simpleElements, + isArray, + optionalSimpleType, + optionalSimpleTypeName); + + // add attributes + attributes.AddRange(GetAttributes(xsModel, complexActualElement)); + + var complexParticles = GetContentModelParticles( + xsModel, complexActualElement); + complexElement = ProcessModelGroup( + xsModel, complexParticles, simpleElements, complexElements, node, + complexActualElement, complexElement); + + return complexElement; + } + + internal static readonly SchemaItemAttribute[] EMPTY_SCHEMA_ATTRIBUTES = new SchemaItemAttribute[0]; + + /// + /// Gets the attributes from the complex type. Searches embedded and extended + /// content for additional attributes. + /// + /// The xs model. + /// Type of the complex. + /// + internal IEnumerable GetAttributes( + XmlSchema xsModel, + XmlSchemaComplexType complexType) + { + if (complexType != null) + { + var attributes = GetAttributes(xsModel, complexType.Attributes); + + var contentModel = complexType.ContentModel; + if (contentModel is XmlSchemaSimpleContent) + { + var simpleContentModel = (XmlSchemaSimpleContent)contentModel; + var simpleContentExtension = simpleContentModel.Content as XmlSchemaSimpleContentExtension; + if (simpleContentExtension != null) + { + attributes = attributes.Concat( + GetAttributes(xsModel, simpleContentExtension.Attributes)); + } + } + + return attributes; + } + + return EMPTY_SCHEMA_ATTRIBUTES; + } + + /// + /// Returns the attributes within a collection. Most of the time, this will + /// simply normalize the name and convert the structure into the resultant + /// output type. + /// + /// The xs model. + /// The attributes. + /// + internal IEnumerable GetAttributes( + XmlSchema xsModel, + XmlSchemaObjectCollection attributes) + { + foreach (var attr in attributes.Cast()) + { + var name = attr.QualifiedName; + if (name.IsEmpty) + { + if (attr.Form == XmlSchemaForm.Qualified) + { + name = new XmlQualifiedName(attr.Name, xsModel.TargetNamespace); + } + else + { + name = new XmlQualifiedName(attr.Name, null); + } + } + + var schemaType = ResolveSchemaType(xsModel, attr.SchemaTypeName); + var itemAttribute = new SchemaItemAttribute( + name.Namespace, + name.Name, + schemaType as XmlSchemaSimpleType, + attr.SchemaTypeName.Name); + + yield return itemAttribute; + } + } + + internal static readonly XmlSchemaObject[] EMPTY_SCHEMA_OBJECTS = new XmlSchemaObject[0]; + + /// + /// Gets the content model particles. + /// + /// The xs model. + /// Type of the complex. + /// + internal IEnumerable GetContentModelParticles( + XmlSchema xsModel, + XmlSchemaComplexType complexType) + { + if (complexType != null) + { + var complexGroupBase = complexType.Particle as XmlSchemaGroupBase; + var contentModel = complexType.ContentModel; + if (contentModel is XmlSchemaComplexContent) + { + var complexContentExtension = contentModel.Content as XmlSchemaComplexContentExtension; + if (complexContentExtension != null) + { + return GetContentExtensionParticles(xsModel, complexContentExtension); + } + } + + if (complexGroupBase != null) + { + return complexGroupBase.Items.Cast(); + } + } + + return EMPTY_SCHEMA_OBJECTS; + } + + /// + /// Gets the content model particles associated with a content extension element. These + /// objects can be a little difficult because of the nesting of base types that occurs + /// within them. We use a cofunction to build an iterator that recursively up through + /// the hierarchy. + /// + /// The xs model. + /// The content extension. + /// + internal IEnumerable GetContentExtensionParticles( + XmlSchema xsModel, + XmlSchemaComplexContentExtension contentExtension) + { + IEnumerable result = EMPTY_SCHEMA_OBJECTS; + + var baseTypeName = contentExtension.BaseTypeName; + if (baseTypeName.IsEmpty == false) + { + var baseType = ResolveSchemaType(xsModel, baseTypeName) as XmlSchemaComplexType; + if (baseType != null) + { + result = GetContentModelParticles(xsModel, baseType); + } + } + + var complexParticleGroup = contentExtension.Particle as XmlSchemaGroupBase; + if (complexParticleGroup != null) + { + result = result.Concat(complexParticleGroup.Items.Cast()); + } + + return result; + } + + /// + /// Processes the model group. + /// + /// The schema model. + /// The schema objects in this model group. + /// The simple elements. + /// The complex elements. + /// The node. + /// The complex actual element. + /// The complex element. + /// + private SchemaElementComplex ProcessModelGroup( + XmlSchema xsModel, + IEnumerable childParticles, + IList simpleElements, + IList complexElements, + ElementPathNode node, + XmlSchemaComplexType complexActualElement, + SchemaElementComplex complexElement) + { + foreach (var childParticle in childParticles) + { + if (childParticle is XmlSchemaElement) + { + var schemaElement = (XmlSchemaElement)childParticle; + var isArrayFlag = IsArray(schemaElement); + + // the name for this element + XmlQualifiedName elementName; + // the type for this element ... this may take different paths + // depending upon how the type is provided to us. + XmlSchemaType schemaType; + XmlQualifiedName schemaTypeName; + + if (schemaElement.RefName.IsEmpty) + { + elementName = schemaElement.QualifiedName; + if (Equals(elementName, XmlQualifiedName.Empty)) + elementName = new XmlQualifiedName( + schemaElement.Name, xsModel.TargetNamespace); + + schemaType = schemaElement.SchemaType; + schemaTypeName = schemaElement.SchemaTypeName; + if ((schemaType == null) && (!schemaTypeName.IsEmpty)) + { + schemaType = ResolveSchemaType(xsModel, schemaTypeName); + } + } + else + { + // this element contains a reference to another element... the element will + // share the name of the reference and the type of the reference. the reference + // type should be a complex type. + var referenceElement = ResolveElement(schemaElement.RefName); + var referenceElementType = referenceElement.SchemaType; + + var elementNamespace = string.IsNullOrEmpty(schemaElement.RefName.Namespace) + ? xsModel.TargetNamespace + : schemaElement.RefName.Namespace; + elementName = new XmlQualifiedName( + schemaElement.RefName.Name, elementNamespace); + + schemaType = referenceElementType; + schemaTypeName = referenceElement.SchemaTypeName; + // TODO + } + + var simpleType = schemaType as XmlSchemaSimpleType; + if (simpleType != null) + { + var fractionDigits = GetFractionRestriction(simpleType); + var simpleElement = new SchemaElementSimple( + elementName.Name, + elementName.Namespace, + simpleType, + schemaTypeName.Name, + isArrayFlag, + fractionDigits); + simpleElements.Add(simpleElement); + } + else + { + var complexType = schemaType as XmlSchemaComplexType; + var newChild = node.AddChild(elementName); + if (newChild.DoesNameAlreadyExistInHierarchy()) + { + continue; + } + + complexActualElement = complexType; + + SchemaElementComplex innerComplex = Process( + xsModel, + elementName, + complexActualElement, + isArrayFlag, + newChild + ); + + if (Log.IsDebugEnabled) + { + Log.Debug("Adding complex {0}", complexElement); + } + complexElements.Add(innerComplex); + } + } + + ProcessModelGroup( + xsModel, childParticle, simpleElements, complexElements, node, complexActualElement, + complexElement); + } + + return complexElement; + } + + /// + /// Processes the model group. + /// + /// The schema model. + /// The schema that represents the model group. + /// The simple elements. + /// The complex elements. + /// The node. + /// The complex actual element. + /// The complex element. + /// + private SchemaElementComplex ProcessModelGroup( + XmlSchema xsModel, + XmlSchemaObject xsObject, + IList simpleElements, + IList complexElements, + ElementPathNode node, + XmlSchemaComplexType complexActualElement, + SchemaElementComplex complexElement) + { + var xsGroup = xsObject as XmlSchemaGroupBase; + if (xsGroup != null) + { + return ProcessModelGroup( + xsModel, + xsGroup.Items.Cast(), + simpleElements, + complexElements, + node, + complexActualElement, + complexElement); + } + + return complexElement; + } + + private static bool IsArray(XmlSchemaElement element) + { + return element.MaxOccursString == "unbounded" || element.MaxOccurs > 1; + } + + private static int? GetFractionRestriction(XmlSchemaSimpleType simpleType) + { + var simpleTypeRestriction = simpleType.Content as XmlSchemaSimpleTypeRestriction; + if (simpleTypeRestriction != null) + { + foreach (XmlSchemaObject facet in simpleTypeRestriction.Facets) + { + //Console.WriteLine(facet); + } + } +#if false + + + if ((simpleType.getDefinedFacets() & XSSimpleType.FACET_FRACTIONDIGITS) != 0) + { + XSObjectList facets = simpleType.getFacets(); + Integer digits = null; + for (int f = 0; f < facets.getLength(); f++) + { + XSObject item = facets.item(f); + if (item + instanceof XSFacet) + { + XSFacet facet = (XSFacet) item; + if (facet.getFacetKind() == XSSimpleType.FACET_FRACTIONDIGITS) + { + try + { + digits = Integer.parseInt(facet.getLexicalFacetValue()); + } + catch (RuntimeException ex) + { + Log.warn( + "Error parsing fraction facet value '" + facet.getLexicalFacetValue() + "' : " + + ex.getMessage(), ex); + } + } + } + } + return digits; + } +#endif + return null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/filter/DoubleRange.cs b/NEsper.Core/NEsper.Core/filter/DoubleRange.cs new file mode 100755 index 000000000..39c5672ae --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/DoubleRange.cs @@ -0,0 +1,104 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.filter +{ + /// Holds a range of double values with a minimum (start) value and a maximum (end) value. + public sealed class DoubleRange : Range + { + private readonly int _hashCode; + + /// Constructor - takes range endpoints. + /// is the low endpoint + /// is the high endpoint + public DoubleRange(double? min, double? max) + { + Min = min; + Max = max; + + if ((min != null) && (max != null)) + { + if (min > max) + { + Max = min; + Min = max; + } + } + + _hashCode = (min != null) + ? min.GetHashCode() + : 0; + + if (max != null) + { + _hashCode *= 31; + _hashCode ^= max.GetHashCode(); + } + } + + /// Returns low endpoint. + /// low endpoint + public double? Min { get; private set; } + + public object HighEndpoint + { + get { return Max; } + } + + public object LowEndpoint + { + get { return Min; } + } + + /// Returns high endpoint. + /// high endpoint + public double? Max { get; private set; } + + public bool Equals(DoubleRange other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return other.Min.Equals(Min) && other.Max.Equals(Max); + } + + /// + /// Determines whether the specified is equal to the current . + /// + /// + /// true if the specified is equal to the current ; otherwise, false. + /// + /// The to compare with the current . + /// The parameter is null. + /// 2 + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != typeof(DoubleRange)) return false; + return Equals((DoubleRange)obj); + } + + public override int GetHashCode() + { + unchecked + { + return _hashCode; + //return ((Min.HasValue ? Min.Value.GetHashCode() : 0) * 397) ^ (Max.HasValue ? Max.Value.GetHashCode() : 0); + } + } + + public override String ToString() + { + return "DoubleRange" + + " min=" + Min + + " max=" + Max; + } + } +} diff --git a/NEsper.Core/NEsper.Core/filter/DoubleRangeComparator.cs b/NEsper.Core/NEsper.Core/filter/DoubleRangeComparator.cs new file mode 100755 index 000000000..e59865567 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/DoubleRangeComparator.cs @@ -0,0 +1,62 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; + +using com.espertech.esper.util; + +namespace com.espertech.esper.filter +{ + + /// + /// Comparator for DoubleRange values. + /// + /// Sorts double ranges as this: sort by min asc, max asc. + /// I.e. same minimum value sorts maximum value ascending. + /// + /// + + [Serializable] + public sealed class DoubleRangeComparator : IComparer, MetaDefItem + { + /// + /// Compares the specified double ranges. + /// + /// The r1. + /// The r2. + /// + public int Compare(DoubleRange r1, DoubleRange r2) + { + double? minOne = r1.Min; + double? minTwo = r2.Min; + double? maxOne = r1.Max; + double? maxTwo = r2.Max; + + if (minOne < minTwo) + { + return - 1; + } + if (minOne > minTwo) + { + return 1; + } + if (maxOne < maxTwo) + { + return - 1; + } + if (maxOne > maxTwo) + { + return 1; + } + + return 0; + } + } +} diff --git a/NEsper.Core/NEsper.Core/filter/EventEvaluator.cs b/NEsper.Core/NEsper.Core/filter/EventEvaluator.cs new file mode 100755 index 000000000..9344e9b5f --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/EventEvaluator.cs @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; + +namespace com.espertech.esper.filter +{ + /// + /// Interface for matching an event instance based on the event's property values to + /// filters, specifically filter parameter constants or ranges. + /// + public interface EventEvaluator + { + /// Perform the matching of an event based on the event property values, adding any callbacks for matches found to the matches list. + /// is the event object wrapper to obtain event property values from + /// accumulates the matching filter callbacks + void MatchEvent(EventBean theTheEvent, ICollection matches); + } +} diff --git a/NEsper.Core/NEsper.Core/filter/EventTypeIndex.cs b/NEsper.Core/NEsper.Core/filter/EventTypeIndex.cs new file mode 100755 index 000000000..3e475e2b3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/EventTypeIndex.cs @@ -0,0 +1,154 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.Linq; +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.threading; + +namespace com.espertech.esper.filter +{ + /// + /// Mapping of event type to a tree-like structure containing filter parameter constants + /// in indexes and filter callbacks in + /// . + /// + /// This class evaluates events for the purpose of filtering by (1) looking up the event's + /// and (2) asking the subtree for this event type to evaluate + /// the event. + /// + /// The class performs all the locking required for multithreaded access. + /// + public class EventTypeIndex : EventEvaluator + { + private readonly IDictionary _eventTypes; + + private readonly IReaderWriterLock _eventTypesLock; + + /// + /// Constructor. + /// + public EventTypeIndex(FilterServiceGranularLockFactory lockFactory) + { + _eventTypes = new Dictionary(); + _eventTypesLock = lockFactory.ObtainNew(); + } + + /// + /// Dispose the service. + /// + public void Dispose() + { + _eventTypes.Clear(); + } + + /// + /// Add a new event type to the index and use the specified node for the root node of + /// its subtree. If the event type already existed, the method will throw an + /// IllegalStateException. + /// + /// is the event type to be added to the index + /// is the root node of the subtree for filter constant indizes and callbacks + public void Add(EventType eventType, FilterHandleSetNode rootNode) + { + using (_eventTypesLock.AcquireWriteLock()) + { + if (_eventTypes.ContainsKey(eventType)) + { + throw new IllegalStateException("Event type already in index, add not performed, type=" + eventType); + } + _eventTypes.Put(eventType, rootNode); + } + } + + + public void RemoveType(EventType type) + { + using (_eventTypesLock.AcquireWriteLock()) + { + _eventTypes.Remove(type); + } + } + + /// Returns the root node for the given event type, or null if this event type has not been seen before. + /// is an event type + /// the subtree's root node + public FilterHandleSetNode Get(EventType eventType) + { + using (_eventTypesLock.AcquireReadLock()) + { + FilterHandleSetNode result = _eventTypes.Get(eventType); + return result; + } + } + + public void MatchEvent(EventBean theTheEvent, ICollection matches) + { + EventType eventType = theTheEvent.EventType; + + // Attempt to match exact type + MatchType(eventType, theTheEvent, matches); + + // No supertype means we are done + if (eventType.SuperTypes == null) + { + return; + } + + var deepSuperTypes = eventType.DeepSuperTypes; + var deepSuperTypesLength = deepSuperTypes.Length; + for (int ii = 0; ii < deepSuperTypesLength; ii++) + { + MatchType(deepSuperTypes[ii], theTheEvent, matches); + } + } + + /// Returns the current size of the known event types. + /// collection size + internal int Count + { + get { return _eventTypes.Count; } + } + + internal int FilterCountApprox + { + get + { + using (_eventTypesLock.AcquireReadLock()) + { + int count = 0; + foreach (var entry in _eventTypes) + { + count += entry.Value.FilterCallbackCount; + count += entry.Value.Indizes.Sum(index => index.Count); + } + return count; + } + } + } + + private void MatchType(EventType eventType, EventBean eventBean, ICollection matches) + { + FilterHandleSetNode rootNode; + + using (_eventTypesLock.AcquireWriteLock()) + { + rootNode = _eventTypes.Get(eventType); + } + + // If the top class node is null, no filters have yet been registered for this event type. + // In this case, log a message and done. + if (rootNode != null) + { + rootNode.MatchEvent(eventBean, matches); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/filter/EventTypeIndexBuilder.cs b/NEsper.Core/NEsper.Core/filter/EventTypeIndexBuilder.cs new file mode 100755 index 000000000..fdba95df1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/EventTypeIndexBuilder.cs @@ -0,0 +1,203 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.threading; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.filter +{ + /// + /// This class is responsible for changes to for addition + /// and removal of filters. It delegates the work to make modifications to the filter parameter + /// tree to an . It enforces a policy that a filter + /// callback can only be added once. + /// + public class EventTypeIndexBuilder + { + private readonly IDictionary _isolatableCallbacks; + private readonly ILockable _callbacksLock; + private readonly EventTypeIndex _eventTypeIndex; + + /// + /// Constructor - takes the event type index to manipulate as its parameter. + /// + /// index to manipulate + public EventTypeIndexBuilder(EventTypeIndex eventTypeIndex, bool allowIsolation) + { + _eventTypeIndex = eventTypeIndex; + _callbacksLock = LockManager.CreateLock(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + _isolatableCallbacks = allowIsolation ? new Dictionary() : null; + } + + /// + /// Dispose the service. + /// + public void Destroy() + { + _eventTypeIndex.Dispose(); + if (_isolatableCallbacks != null) + _isolatableCallbacks.Clear(); + } + + /// + /// Add a filter to the event type index structure, and to the filter subtree. + /// Throws an IllegalStateException exception if the callback is already registered. + /// + /// is the filter information + /// is the callback + /// The lock factory. + /// Callback for filter specification already exists in collection + public FilterServiceEntry Add(FilterValueSet filterValueSet, FilterHandle filterCallback, FilterServiceGranularLockFactory lockFactory) + { + using (Instrument.With( + i => i.QFilterAdd(filterValueSet, filterCallback), + i => i.AFilterAdd())) + { + var eventType = filterValueSet.EventType; + + // Check if a filter tree exists for this event type + var rootNode = _eventTypeIndex.Get(eventType); + + // Make sure we have a root node + if (rootNode == null) + { + using(_callbacksLock.Acquire()) + { + rootNode = _eventTypeIndex.Get(eventType); + if (rootNode == null) + { + rootNode = new FilterHandleSetNode(lockFactory.ObtainNew()); + _eventTypeIndex.Add(eventType, rootNode); + } + } + } + + // GetInstance add to tree + var path = IndexTreeBuilder.Add(filterValueSet, filterCallback, rootNode, lockFactory); + var pathArray = path.Select(p => p.ToArray()).ToArray(); + var pair = new EventTypeIndexBuilderValueIndexesPair(filterValueSet, pathArray); + + // for non-isolatable callbacks the consumer keeps track of tree location + if (_isolatableCallbacks == null) { + return pair; + } + + // for isolatable callbacks this class is keeping track of tree location + using (_callbacksLock.Acquire()) { + _isolatableCallbacks.Put(filterCallback, pair); + } + + return null; + } + } + + /// + /// Remove a filter callback from the given index node. + /// + /// is the callback to remove + public void Remove(FilterHandle filterCallback, FilterServiceEntry filterServiceEntry) + { + EventTypeIndexBuilderValueIndexesPair pair; + if (_isolatableCallbacks != null) + { + using (_callbacksLock.Acquire()) + { + pair = _isolatableCallbacks.Delete(filterCallback); + } + if (pair == null) + { + return; + } + } + else + { + pair = (EventTypeIndexBuilderValueIndexesPair) filterServiceEntry; + } + + using (Instrument.With( + i => i.QFilterRemove(filterCallback, pair), + i => i.AFilterRemove())) + { + var eventType = pair.FilterValueSet.EventType; + var rootNode = _eventTypeIndex.Get(eventType); + + // GetInstance remove from tree + if (rootNode != null) + { + pair.IndexPairs.ForEach( + indexPair => IndexTreeBuilder.Remove(eventType, filterCallback, indexPair, rootNode)); + } + } + } + + /// + /// Returns filters for the statement ids. + /// + /// ids to take + /// set of filters for taken statements + public FilterSet Take(ICollection statementIds) + { + if (_isolatableCallbacks == null) + { + throw new EPException("Operation not supported, please enable isolation in the engine configuration"); + } + + IList list = new List(); + using (_callbacksLock.Acquire()) + { + foreach (var entry in _isolatableCallbacks) + { + var pair = entry.Value; + if (statementIds.Contains(entry.Key.StatementId)) + { + list.Add(new FilterSetEntry(entry.Key, pair.FilterValueSet)); + + var eventType = pair.FilterValueSet.EventType; + var rootNode = _eventTypeIndex.Get(eventType); + + // GetInstance remove from tree + pair.IndexPairs.ForEvery( + indexPair => IndexTreeBuilder.Remove(eventType, entry.Key, indexPair, rootNode)); + } + } + + foreach (var removed in list) + { + _isolatableCallbacks.Remove(removed.Handle); + } + } + + return new FilterSet(list); + } + + /// + /// Add the filters, from previously-taken filters. + /// + /// to add + /// The lock factory. + public void Apply(FilterSet filterSet, FilterServiceGranularLockFactory lockFactory) + { + foreach (var entry in filterSet.Filters) + { + Add(entry.FilterValueSet, entry.Handle, lockFactory); + } + } + + public bool IsSupportsTakeApply + { + get { return _isolatableCallbacks != null; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/filter/EventTypeIndexBuilderIndexLookupablePair.cs b/NEsper.Core/NEsper.Core/filter/EventTypeIndexBuilderIndexLookupablePair.cs new file mode 100755 index 000000000..667525090 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/EventTypeIndexBuilderIndexLookupablePair.cs @@ -0,0 +1,24 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.filter +{ + public class EventTypeIndexBuilderIndexLookupablePair + { + public EventTypeIndexBuilderIndexLookupablePair(FilterParamIndexBase index, Object lookupable) + { + Index = index; + Lookupable = lookupable; + } + + public readonly FilterParamIndexBase Index; + public readonly object Lookupable; + } +} diff --git a/NEsper.Core/NEsper.Core/filter/EventTypeIndexBuilderValueIndexesPair.cs b/NEsper.Core/NEsper.Core/filter/EventTypeIndexBuilderValueIndexesPair.cs new file mode 100755 index 000000000..543099534 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/EventTypeIndexBuilderValueIndexesPair.cs @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.filter +{ + public class EventTypeIndexBuilderValueIndexesPair : FilterServiceEntry + { + public EventTypeIndexBuilderValueIndexesPair( + FilterValueSet filterValueSet, + EventTypeIndexBuilderIndexLookupablePair[][] indexPairs) + { + FilterValueSet = filterValueSet; + IndexPairs = indexPairs; + } + + public FilterValueSet FilterValueSet { get; private set; } + + public EventTypeIndexBuilderIndexLookupablePair[][] IndexPairs { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/filter/ExprNodeAdapter.cs b/NEsper.Core/NEsper.Core/filter/ExprNodeAdapter.cs new file mode 100755 index 000000000..fda296eca --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/ExprNodeAdapter.cs @@ -0,0 +1,100 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using com.espertech.esper.client; +using com.espertech.esper.compat.logging; +using com.espertech.esper.compat.threading; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.variable; + +namespace com.espertech.esper.filter +{ + /// + /// Adapter for use by to evaluate bool expressions, providing + /// events per stream to expression nodes. Generated by @{link FilterSpecParamExprNode} for + /// bool expression filter parameters. + /// + public class ExprNodeAdapter + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly IThreadLocal _arrayPerThread; + private readonly ExprNode _exprNode; + private readonly ExprEvaluator _exprNodeEval; + private readonly EventBean[] _prototype; + private readonly VariableService _variableService; + + /// + /// Ctor. + /// + /// is the bool expression + /// is the row of events the we are matching on + /// for setting variable version for evaluating variables, if required + public ExprNodeAdapter(ExprNode exprNode, EventBean[] prototype, + VariableService variableService) + { + _exprNode = exprNode; + _exprNodeEval = exprNode.ExprEvaluator; + _variableService = variableService; + _arrayPerThread = ThreadLocalManager.Create(CreateLocalData); + + if (prototype == null) { + _prototype = new EventBean[1]; + } + else { + _prototype = prototype; + } + } + + /// + /// Provides the _prototype events-per-stream where stream zero is the current stream + /// and is filled when the expression is evaluated. + /// + public EventBean[] Prototype + { + get { return _prototype; } + } + + /// + /// Creates a local data object. + /// + /// + private EventBean[] CreateLocalData() + { + var eventsPerStream = new EventBean[_prototype.Length]; + Array.Copy(_prototype, 0, eventsPerStream, 0, _prototype.Length); + return eventsPerStream; + } + + /// + /// Evaluate the bool expression given the event as a stream zero event. + /// + /// is the stream zero event (current event) + /// The expression evaluator context. + /// bool result of the expression + public bool Evaluate(EventBean theEvent, ExprEvaluatorContext exprEvaluatorContext) + { + if (_variableService != null) { + _variableService.SetLocalVersion(); + } + EventBean[] eventsPerStream = _arrayPerThread.GetOrCreate(); + eventsPerStream[0] = theEvent; + + try { + var result = (bool?) _exprNodeEval.Evaluate(new EvaluateParams(eventsPerStream, true, exprEvaluatorContext)); + return result ?? false; + } + catch (Exception ex) { + Log.Error("Error evaluating expression '" + _exprNode.ToExpressionStringMinPrecedenceSafe() + "': " + ex.Message, ex); + return false; + } + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/filter/ExprNodeAdapterBase.cs b/NEsper.Core/NEsper.Core/filter/ExprNodeAdapterBase.cs new file mode 100755 index 000000000..b9ab7be0a --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/ExprNodeAdapterBase.cs @@ -0,0 +1,106 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.filter +{ + public class ExprNodeAdapterBase + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly int _filterSpecId; + private readonly int _filterSpecParamPathNum; + private readonly ExprNode _exprNode; + private readonly ExprEvaluator _exprNodeEval; + private readonly ExprEvaluatorContext _evaluatorContext; + + /// + /// Ctor. + /// + /// The filter spec identifier. + /// The filter spec parameter path number. + /// is the bool expression + /// The evaluator context. + public ExprNodeAdapterBase(int filterSpecId, int filterSpecParamPathNum, ExprNode exprNode, ExprEvaluatorContext evaluatorContext) + { + _filterSpecId = filterSpecId; + _filterSpecParamPathNum = filterSpecParamPathNum; + _exprNode = exprNode; + _exprNodeEval = exprNode.ExprEvaluator; + _evaluatorContext = evaluatorContext; + } + + /// Evaluate the bool expression given the event as a stream zero event. + /// is the stream zero event (current event) + /// bool result of the expression + public virtual bool Evaluate(EventBean theEvent) + { + return EvaluatePerStream(new EventBean[] { theEvent }); + } + + protected virtual bool EvaluatePerStream(EventBean[] eventsPerStream) + { + try + { + var result = (bool?)_exprNodeEval.Evaluate(new EvaluateParams(eventsPerStream, true, _evaluatorContext)); + if (result == null) + { + return false; + } + return result.Value; + } + catch (Exception ex) + { + Log.Error("Error evaluating expression '" + ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(_exprNode) + "' statement '" + StatementName + "': " + ex.Message, ex); + return false; + } + } + + public string StatementName + { + get { return _evaluatorContext.StatementName; ; } + } + + public int StatementId + { + get { return _evaluatorContext.StatementId; } + } + + public ExprNode ExprNode + { + get { return _exprNode; } + } + + public int FilterSpecId + { + get { return _filterSpecId; } + } + + public int FilterSpecParamPathNum + { + get { return _filterSpecParamPathNum; } + } + + public ExprEvaluator ExprNodeEval + { + get { return _exprNodeEval; } + } + + public ExprEvaluatorContext EvaluatorContext + { + get { return _evaluatorContext; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/filter/ExprNodeAdapterBaseStmtLock.cs b/NEsper.Core/NEsper.Core/filter/ExprNodeAdapterBaseStmtLock.cs new file mode 100755 index 000000000..7896ffe11 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/ExprNodeAdapterBaseStmtLock.cs @@ -0,0 +1,42 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.variable; + +namespace com.espertech.esper.filter +{ + public class ExprNodeAdapterBaseStmtLock : ExprNodeAdapterBase + { + private readonly VariableService _variableService; + + public VariableService VariableService + { + get { return _variableService; } + } + + public ExprNodeAdapterBaseStmtLock(int filterSpecId, int filterSpecParamPathNum, ExprNode exprNode, ExprEvaluatorContext evaluatorContext, VariableService variableService) + : base (filterSpecId, filterSpecParamPathNum, exprNode, evaluatorContext) + { + _variableService = variableService; + } + + public override bool Evaluate(EventBean theEvent) + { + using(base.EvaluatorContext.AgentInstanceLock.AcquireWriteLock()) + { + _variableService.SetLocalVersion(); + return EvaluatePerStream(new[] {theEvent}); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/filter/ExprNodeAdapterBaseVariables.cs b/NEsper.Core/NEsper.Core/filter/ExprNodeAdapterBaseVariables.cs new file mode 100755 index 000000000..f113d03a6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/ExprNodeAdapterBaseVariables.cs @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.variable; + +namespace com.espertech.esper.filter +{ + public class ExprNodeAdapterBaseVariables : ExprNodeAdapterBase + { + private readonly VariableService _variableService; + + /// + /// Gets the variable service. + /// + /// The variable service. + protected VariableService VariableService + { + get { return _variableService; } + } + + public ExprNodeAdapterBaseVariables(int filterSpecId, int filterSpecParamPathNum, ExprNode exprNode, ExprEvaluatorContext evaluatorContext, VariableService variableService) + : base(filterSpecId, filterSpecParamPathNum, exprNode, evaluatorContext) + { + _variableService = variableService; + } + + public override bool Evaluate(EventBean theEvent) + { + _variableService.SetLocalVersion(); + return EvaluatePerStream(new [] {theEvent}); + } + } +} diff --git a/NEsper.Core/NEsper.Core/filter/ExprNodeAdapterBaseWTableAccess.cs b/NEsper.Core/NEsper.Core/filter/ExprNodeAdapterBaseWTableAccess.cs new file mode 100755 index 000000000..b7e2486ac --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/ExprNodeAdapterBaseWTableAccess.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.table.mgmt; + +namespace com.espertech.esper.filter +{ + public class ExprNodeAdapterBaseWTableAccess : ExprNodeAdapterBase + { + private readonly ExprNodeAdapterBase _evalBase; + private readonly TableService _tableService; + + public ExprNodeAdapterBaseWTableAccess(int filterSpecId, int filterSpecParamPathNum, ExprNode exprNode, ExprEvaluatorContext evaluatorContext, ExprNodeAdapterBase evalBase, TableService tableService) + : base (filterSpecId, filterSpecParamPathNum, exprNode, evaluatorContext) + { + _evalBase = evalBase; + _tableService = tableService; + } + + public override bool Evaluate(EventBean theEvent) + { + try { + return _evalBase.Evaluate(theEvent); + } + finally { + _tableService.TableExprEvaluatorContext.ReleaseAcquiredLocks(); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/filter/ExprNodeAdapterMultiStream.cs b/NEsper.Core/NEsper.Core/filter/ExprNodeAdapterMultiStream.cs new file mode 100755 index 000000000..72492366e --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/ExprNodeAdapterMultiStream.cs @@ -0,0 +1,57 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat.threading; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.variable; + +namespace com.espertech.esper.filter +{ + /// + /// Adapter for use by to evaluate bool expressions, providing events per stream to expression nodes. Generated by @{link FilterSpecParamExprNode} for bool expression filter parameters. + /// + public class ExprNodeAdapterMultiStream : ExprNodeAdapterBaseVariables + { + private readonly EventBean[] _prototypeArray; + + private readonly IThreadLocal _arrayPerThread; + + public ExprNodeAdapterMultiStream(int filterSpecId, int filterSpecParamPathNum, ExprNode exprNode, ExprEvaluatorContext evaluatorContext, VariableService variableService, EventBean[] prototype) + : base(filterSpecId, filterSpecParamPathNum, exprNode, evaluatorContext, variableService) + { + _prototypeArray = prototype; + _arrayPerThread = ThreadLocalManager.Create( + () => + { + var eventsPerStream = new EventBean[_prototypeArray.Length]; + Array.Copy(_prototypeArray, 0, eventsPerStream, 0, _prototypeArray.Length); + return eventsPerStream; + }); + } + + protected EventBean[] PrototypeArray + { + get { return _prototypeArray; } + } + + public override bool Evaluate(EventBean theEvent) + { + if (VariableService != null) + { + VariableService.SetLocalVersion(); + } + EventBean[] eventsPerStream = _arrayPerThread.GetOrCreate(); + eventsPerStream[0] = theEvent; + return EvaluatePerStream(eventsPerStream); + } + } +} diff --git a/NEsper.Core/NEsper.Core/filter/ExprNodeAdapterMultiStreamNoTL.cs b/NEsper.Core/NEsper.Core/filter/ExprNodeAdapterMultiStreamNoTL.cs new file mode 100755 index 000000000..5c4984b65 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/ExprNodeAdapterMultiStreamNoTL.cs @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.variable; + +namespace com.espertech.esper.filter +{ + public class ExprNodeAdapterMultiStreamNoTL : ExprNodeAdapterMultiStream + { + public ExprNodeAdapterMultiStreamNoTL(int filterSpecId, int filterSpecParamPathNum, ExprNode exprNode, ExprEvaluatorContext evaluatorContext, VariableService variableService, EventBean[] prototype) + : base(filterSpecId, filterSpecParamPathNum, exprNode, evaluatorContext, variableService, prototype) + { + } + + public override bool Evaluate(EventBean theEvent) + { + if (VariableService != null) + { + VariableService.SetLocalVersion(); + } + + var eventsPerStream = new EventBean[PrototypeArray.Length]; + Array.Copy(PrototypeArray, 0, eventsPerStream, 0, PrototypeArray.Length); + eventsPerStream[0] = theEvent; + return base.EvaluatePerStream(eventsPerStream); + } + } +} diff --git a/NEsper.Core/NEsper.Core/filter/ExprNodeAdapterMultiStreamNoTLStmtLock.cs b/NEsper.Core/NEsper.Core/filter/ExprNodeAdapterMultiStreamNoTLStmtLock.cs new file mode 100755 index 000000000..0c351ab51 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/ExprNodeAdapterMultiStreamNoTLStmtLock.cs @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.variable; + +namespace com.espertech.esper.filter +{ + public class ExprNodeAdapterMultiStreamNoTLStmtLock : ExprNodeAdapterMultiStreamNoTL + { + public ExprNodeAdapterMultiStreamNoTLStmtLock(int filterSpecId, int filterSpecParamPathNum, ExprNode exprNode, ExprEvaluatorContext evaluatorContext, VariableService variableService, EventBean[] prototype) + : base(filterSpecId, filterSpecParamPathNum, exprNode, evaluatorContext, variableService, prototype) + { + } + + protected override bool EvaluatePerStream(EventBean[] eventsPerStream) + { + try + { + using (EvaluatorContext.AgentInstanceLock.WriteLock.Acquire(ExprNodeAdapterMultiStreamStmtLock.LOCK_BACKOFF_MSEC)) + { + return base.EvaluatePerStream(eventsPerStream); + } + } + catch (TimeoutException) + { + throw new FilterLockBackoffException(); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/filter/ExprNodeAdapterMultiStreamStmtLock.cs b/NEsper.Core/NEsper.Core/filter/ExprNodeAdapterMultiStreamStmtLock.cs new file mode 100755 index 000000000..e68e67938 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/ExprNodeAdapterMultiStreamStmtLock.cs @@ -0,0 +1,42 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.variable; + +namespace com.espertech.esper.filter +{ + public class ExprNodeAdapterMultiStreamStmtLock : ExprNodeAdapterMultiStream + { + public static readonly int LOCK_BACKOFF_MSEC = 10; + + public ExprNodeAdapterMultiStreamStmtLock(int filterSpecId, int filterSpecParamPathNum, ExprNode exprNode, ExprEvaluatorContext evaluatorContext, VariableService variableService, EventBean[] prototype) + : base(filterSpecId, filterSpecParamPathNum, exprNode, evaluatorContext, variableService, prototype) + { + } + + protected override bool EvaluatePerStream(EventBean[] eventsPerStream) + { + try + { + using (EvaluatorContext.AgentInstanceLock.WriteLock.Acquire(LOCK_BACKOFF_MSEC)) + { + return base.EvaluatePerStream(eventsPerStream); + } + } + catch (TimeoutException) + { + throw new FilterLockBackoffException(); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/filter/FilterBooleanExpressionFactory.cs b/NEsper.Core/NEsper.Core/filter/FilterBooleanExpressionFactory.cs new file mode 100755 index 000000000..5cbc4077b --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterBooleanExpressionFactory.cs @@ -0,0 +1,24 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.filter +{ + public interface FilterBooleanExpressionFactory + { + ExprNodeAdapterBase Make( + FilterSpecParamExprNode filterSpecParamExprNode, + EventBean[] events, + ExprEvaluatorContext exprEvaluatorContext, + StatementContext statementContext, + int agentInstanceId); + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/filter/FilterBooleanExpressionFactoryImpl.cs b/NEsper.Core/NEsper.Core/filter/FilterBooleanExpressionFactoryImpl.cs new file mode 100755 index 000000000..8194d3e07 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterBooleanExpressionFactoryImpl.cs @@ -0,0 +1,85 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.variable; + +namespace com.espertech.esper.filter +{ + public class FilterBooleanExpressionFactoryImpl : FilterBooleanExpressionFactory { + + public ExprNodeAdapterBase Make(FilterSpecParamExprNode node, EventBean[] events, ExprEvaluatorContext exprEvaluatorContext, StatementContext statementContext, int agentInstanceId) { + + int filterSpecId = node.FilterSpecId; + int filterSpecParamPathNum = node.FilterSpecParamPathNum; + ExprNode exprNode = node.ExprNode; + VariableService variableService = node.VariableService; + + // handle table evaluator context + if (node.HasTableAccess) { + exprEvaluatorContext = new ExprEvaluatorContextWTableAccess(exprEvaluatorContext, node.TableService); + } + + // non-pattern case + ExprNodeAdapterBase adapter; + if (events == null) { + + // if a subquery is present in a filter stream acquire the agent instance lock + if (node.HasFilterStreamSubquery) { + adapter = GetLockableSingle(filterSpecId, filterSpecParamPathNum, exprNode, exprEvaluatorContext, variableService, statementContext, agentInstanceId); + } else if (!node.HasVariable) { + // no-variable no-prior event evaluation + adapter = new ExprNodeAdapterBase(filterSpecId, filterSpecParamPathNum, exprNode, exprEvaluatorContext); + } else { + // with-variable no-prior event evaluation + adapter = new ExprNodeAdapterBaseVariables(filterSpecId, filterSpecParamPathNum, exprNode, exprEvaluatorContext, variableService); + } + } else { + // pattern cases + VariableService variableServiceToUse = node.HasVariable ? variableService : null; + if (node.UseLargeThreadingProfile) { + // no-threadlocal evaluation + // if a subquery is present in a pattern filter acquire the agent instance lock + if (node.HasFilterStreamSubquery) { + adapter = GetLockableMultiStreamNoTL(filterSpecId, filterSpecParamPathNum, exprNode, exprEvaluatorContext, variableServiceToUse, events); + } else { + adapter = new ExprNodeAdapterMultiStreamNoTL(filterSpecId, filterSpecParamPathNum, exprNode, exprEvaluatorContext, variableServiceToUse, events); + } + } else { + if (node.HasFilterStreamSubquery) { + adapter = GetLockableMultiStream(filterSpecId, filterSpecParamPathNum, exprNode, exprEvaluatorContext, variableServiceToUse, events); + } else { + // evaluation with threadlocal cache + adapter = new ExprNodeAdapterMultiStream(filterSpecId, filterSpecParamPathNum, exprNode, exprEvaluatorContext, variableServiceToUse, events); + } + } + } + + if (!node.HasTableAccess) { + return adapter; + } + + // handle table + return new ExprNodeAdapterBaseWTableAccess(filterSpecId, filterSpecParamPathNum, exprNode, exprEvaluatorContext, adapter, node.TableService); + } + + protected ExprNodeAdapterBase GetLockableSingle(int filterSpecId, int filterSpecParamPathNum, ExprNode exprNode, ExprEvaluatorContext exprEvaluatorContext, VariableService variableService, StatementContext statementContext, int agentInstanceId) { + return new ExprNodeAdapterBaseStmtLock(filterSpecId, filterSpecParamPathNum, exprNode, exprEvaluatorContext, variableService); + } + + protected ExprNodeAdapterBase GetLockableMultiStreamNoTL(int filterSpecId, int filterSpecParamPathNum, ExprNode exprNode, ExprEvaluatorContext exprEvaluatorContext, VariableService variableServiceToUse, EventBean[] events) { + return new ExprNodeAdapterMultiStreamNoTLStmtLock(filterSpecId, filterSpecParamPathNum, exprNode, exprEvaluatorContext, variableServiceToUse, events); + } + + protected ExprNodeAdapterBase GetLockableMultiStream(int filterSpecId, int filterSpecParamPathNum, ExprNode exprNode, ExprEvaluatorContext exprEvaluatorContext, VariableService variableServiceToUse, EventBean[] events) { + return new ExprNodeAdapterMultiStreamStmtLock(filterSpecId, filterSpecParamPathNum, exprNode, exprEvaluatorContext, variableServiceToUse, events); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/filter/FilterEventHandler.cs b/NEsper.Core/NEsper.Core/filter/FilterEventHandler.cs new file mode 100755 index 000000000..c0f6a844a --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterEventHandler.cs @@ -0,0 +1,21 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; + +namespace com.espertech.esper.filter +{ + /// + /// Indicate that an event was evaluated by the + /// which matches the filter specification + /// associated with this callback. + /// + /// the event received that matches the filter specification + + public delegate void FilterEventHandler(EventBean theEvent); +} diff --git a/NEsper.Core/NEsper.Core/filter/FilterFaultHandler.cs b/NEsper.Core/NEsper.Core/filter/FilterFaultHandler.cs new file mode 100755 index 000000000..68cdd10ec --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterFaultHandler.cs @@ -0,0 +1,17 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; + +namespace com.espertech.esper.filter +{ + public interface FilterFaultHandler + { + bool HandleFilterFault(EventBean theEvent, long version); + } +} diff --git a/NEsper.Core/NEsper.Core/filter/FilterFaultHandlerFactory.cs b/NEsper.Core/NEsper.Core/filter/FilterFaultHandlerFactory.cs new file mode 100755 index 000000000..8dd294b8a --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterFaultHandlerFactory.cs @@ -0,0 +1,15 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.filter +{ + public interface FilterFaultHandlerFactory + { + FilterFaultHandler MakeFilterFaultHandler(); + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/filter/FilterHandle.cs b/NEsper.Core/NEsper.Core/filter/FilterHandle.cs new file mode 100755 index 000000000..66da089b3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterHandle.cs @@ -0,0 +1,24 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.filter +{ + /// + /// Marker interface for use with . Implementations serve as a filter match values when + /// events match filters, and also serve to enter and remove a filter from the filter subscription set. + /// + public interface FilterHandle + { + /// + /// Gets the statement id. + /// + /// The statement id. + int StatementId { get; } + } + +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/filter/FilterHandleCallback.cs b/NEsper.Core/NEsper.Core/filter/FilterHandleCallback.cs new file mode 100755 index 000000000..18be6854a --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterHandleCallback.cs @@ -0,0 +1,106 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; + +namespace com.espertech.esper.filter +{ + /// + /// Interface for a callback method to be called when an event matches a filter specification. Provided as a convenience + /// for use as a filter handle for registering with the . + /// + public interface FilterHandleCallback : FilterHandle + { + /// + /// Indicate that an event was evaluated by the which + /// matches the filter specification associated with this callback. + /// + /// the event received that matches the filter specification + /// All STMT matches. + void MatchFound(EventBean theEvent, ICollection allStmtMatches); + + /// + /// Returns true if the filter applies to subselects. + /// + /// subselect filter + bool IsSubSelect { get; } + } + + public sealed class ProxyFilterHandleCallback : FilterHandleCallback + { + public Action> ProcMatchFound { get; set; } + public Func ProcIsSubselect { get; set; } + public Func ProcStatementId { get; set; } + + public ProxyFilterHandleCallback() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The match found. + /// The is sub select. + /// The statement id. + public ProxyFilterHandleCallback(Action> matchFound, + bool isSubSelect, + int statementId) + { + ProcMatchFound = matchFound; + ProcIsSubselect = () => isSubSelect; + ProcStatementId = () => statementId; + } + + /// + /// Initializes a new instance of the class. + /// + /// The match found. + /// The is sub select. + /// The statement id. + public ProxyFilterHandleCallback(Action> matchFound, + Func isSubSelect, + Func statementId) + { + ProcMatchFound = matchFound; + ProcIsSubselect = isSubSelect; + ProcStatementId = statementId; + } + + /// + /// Gets the statement id. + /// + /// The statement id. + public int StatementId + { + get { return ProcStatementId.Invoke(); } + } + + /// + /// Indicate that an event was evaluated by the which + /// matches the filter specification associated with this callback. + /// + /// the event received that matches the filter specification + /// All STMT matches. + public void MatchFound(EventBean theEvent, ICollection allStmtMatches) + { + ProcMatchFound.Invoke(theEvent, allStmtMatches); + } + + /// + /// Returns true if the filter applies to subselects. + /// + /// subselect filter + public bool IsSubSelect + { + get { return ProcIsSubselect.Invoke(); } + } + } +} diff --git a/NEsper.Core/NEsper.Core/filter/FilterHandleSetNode.cs b/NEsper.Core/NEsper.Core/filter/FilterHandleSetNode.cs new file mode 100755 index 000000000..edc0e750e --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterHandleSetNode.cs @@ -0,0 +1,193 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.threading; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.filter +{ + /// + /// This class holds a list of indizes storing filter constants in + /// nodes and a set of . An instance of this class represents a leaf-node + /// (no indizes stored, just filter callbacks) but can also be non-leaf (some indizes exist) in a filter + /// evaluation tree. Events are evaluated by asking each of the indizes to evaluate the event and by adding + /// any filter callbacks in this node to the "matches" list of callbacks. + /// + public sealed class FilterHandleSetNode : EventEvaluator + { + private readonly LinkedHashSet _callbackSet; + private readonly List _indizes; + private readonly IReaderWriterLock _nodeRwLock; + + /// Constructor. + public FilterHandleSetNode(IReaderWriterLock readWriteLock) + { + _callbackSet = new LinkedHashSet(); + _indizes = new List(); + _nodeRwLock = readWriteLock; + } + + /// + /// Returns an indication of whether there are any callbacks or index nodes at all in this set. + /// NOTE: the client to this method must use the read-write lock of this object to lock, if + /// required by the client code. + /// + /// + /// true if there are neither indizes nor filter callbacks stored, false if either exist. + /// + public bool IsEmpty() + { + return _callbackSet.IsEmpty() && _indizes.Count == 0; + } + + /// + /// Returns the number of filter callbacks stored. NOTE: the client to this method must use + /// the read-write lock of this object to lock, if required by the client code. + /// + /// number of filter callbacks stored + public int FilterCallbackCount + { + get { return _callbackSet.Count; } + } + + /// + /// Returns to lock to use for making changes to the filter callback or inzides collections stored by this node. + /// + /// lock to use in multithreaded environment + public IReaderWriterLock NodeRWLock + { + get { return _nodeRwLock; } + } + + /// Returns list of indexes - not returning an iterator. Client classes should not change this collection. + /// list of indizes + public IList Indizes + { + get { return _indizes; } + } + + /// Evaluate an event by asking each index to match the event. Any filter callbacks at this node automatically match the event and do not need to be further evaluated, and are thus added to the "matches" list of callbacks. NOTE: This client should not use the lock before calling this method. + /// is the event wrapper supplying the event property values + /// is the list of callbacks to add to for any matches found + public void MatchEvent(EventBean theEvent, ICollection matches) + { + using (_nodeRwLock.AcquireReadLock()) + { + if (InstrumentationHelper.ENABLED) + { + if (_indizes.IsNotEmpty()) + { + InstrumentationHelper.Get().QFilterHandleSetIndexes(_indizes); + } + } + + // Ask each of the indizes to match against the attribute values + var length = _indizes.Count; + for (int ii = 0; ii < length; ii++) + { + _indizes[ii].MatchEvent(theEvent, matches); + } + + if (InstrumentationHelper.ENABLED) + { + if (_indizes.IsNotEmpty()) + { + InstrumentationHelper.Get().AFilterHandleSetIndexes(); + } + } + + if (InstrumentationHelper.ENABLED) + { + if (_callbackSet.IsNotEmpty()) + { + InstrumentationHelper.Get().QaFilterHandleSetCallbacks(_callbackSet); + } + } + + // Add each filter callback stored in this node to the matching list + _callbackSet.AddTo(matches); + + //foreach (FilterHandle filterCallback in _callbackSet) + //{ + // matches.Add(filterCallback); + //} + } + } + + /// + /// Returns an indication whether the filter callback exists in this node. + /// NOTE: the client to this method must use the read-write lock of this object + /// to lock, if required by the client code. + /// + /// is the filter callback to check for + /// true if callback found, false if not + public bool Contains(FilterHandle filterCallback) + { + return _callbackSet.Contains(filterCallback); + } + + /// + /// Add an index. The same index can be added twice - there is no checking done. + /// NOTE: the client to this method must use the read-write lock of this object + /// to lock, if required by the client code. + /// + /// index to add + public void Add(FilterParamIndexBase index) + { + _indizes.Add(index); + } + + /// + /// Remove an index, returning true if it was found and removed or false if not in collection. + /// NOTE: the client to this method must use the read-write lock of this object to lock, if + /// required by the client code. + /// + /// is the index to remove + /// true if found, false if not existing + public bool Remove(FilterParamIndexBase index) + { + return _indizes.Remove(index); + } + + /// + /// Add a filter callback. The filter callback set allows adding the same callback twice with no effect. + /// If a client to the class needs to check that the callback already existed, the contains method does that. + /// NOTE: the client to this method must use the read-write lock of this object to lock, if required + /// by the client code. + /// + /// is the callback to add + public void Add(FilterHandle filterCallback) + { + _callbackSet.Add(filterCallback); + } + + /// + /// Remove a filter callback, returning true if it was found and removed or false if not in collection. + /// NOTE: the client to this method must use the read-write lock of this object to lock, if required by the client code. + /// + /// is the callback to remove + /// true if found, false if not existing + public bool Remove(FilterHandle filterCallback) + { + return _callbackSet.Remove(filterCallback); + } + + /// + /// Gets the callback set. + /// + /// + public ICollection CallbackSet + { + get { return _callbackSet; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/filter/FilterLockBackoffException.cs b/NEsper.Core/NEsper.Core/filter/FilterLockBackoffException.cs new file mode 100755 index 000000000..61d80480b --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterLockBackoffException.cs @@ -0,0 +1,33 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Runtime.Serialization; + +namespace com.espertech.esper.filter +{ + [Serializable] + public class FilterLockBackoffException : Exception + { + public FilterLockBackoffException() + { + } + + public FilterLockBackoffException(string message) : base(message) + { + } + + public FilterLockBackoffException(string message, Exception innerException) : base(message, innerException) + { + } + + protected FilterLockBackoffException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/filter/FilterNonPropertyRegisteryService.cs b/NEsper.Core/NEsper.Core/filter/FilterNonPropertyRegisteryService.cs new file mode 100755 index 000000000..64a8224e8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterNonPropertyRegisteryService.cs @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; + +namespace com.espertech.esper.filter +{ + /// + /// Service to provide engine-wide access to filter expressions that do not originate from + /// event property values, i.e. expressions that cannot be reproduced by obtaining a getter from the event type. + /// + public interface FilterNonPropertyRegisteryService + { + /// + /// Register expression. + /// + /// statement name + /// event type + /// filter expression + void RegisterNonPropertyExpression(string statementName, EventType eventType, FilterSpecLookupable lookupable); + + /// + /// Obtain expression + /// + /// event type name + /// expression text + /// lookupable + FilterSpecLookupable GetNonPropertyExpression(string eventTypeName, string expression); + + /// + /// Remove references to expression + /// + /// statement name + void RemoveReferencesStatement(string statementName); + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/filter/FilterNonPropertyRegisteryServiceImpl.cs b/NEsper.Core/NEsper.Core/filter/FilterNonPropertyRegisteryServiceImpl.cs new file mode 100755 index 000000000..c036a7543 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterNonPropertyRegisteryServiceImpl.cs @@ -0,0 +1,35 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.compat; + +namespace com.espertech.esper.filter +{ + public class FilterNonPropertyRegisteryServiceImpl : FilterNonPropertyRegisteryService + { + public void RegisterNonPropertyExpression( + string statementName, + EventType eventType, + FilterSpecLookupable lookupable) + { + // default implementation, no action required + } + + public FilterSpecLookupable GetNonPropertyExpression(string eventTypeName, string expression) + { + // default implementation, no action required + throw new UnsupportedOperationException(); + } + + public void RemoveReferencesStatement(string statementName) + { + // default implementation, no action required + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/filter/FilterOperator.cs b/NEsper.Core/NEsper.Core/filter/FilterOperator.cs new file mode 100755 index 000000000..451a8eb21 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterOperator.cs @@ -0,0 +1,208 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.filter +{ + /// + /// Defines the different operator types available for event filters. + /// + /// Mathematical notation for defining ranges of floating point numbers is used as defined below: + /// + /// [a,b] a closed range from value a to value b with the end-points a and b included in the range + /// (a,b) an open range from value a to value b with the end-points a and b not included in the range + /// [a,b) a half-open range from value a to value b with the end-point a included and end-point b not included in the range + /// (a,b] a half-open range from value a to value b with the end-point a not included and end-point b included in the range + /// + /// + /// + public enum FilterOperator + { + /// Exact matches (=). + EQUAL, + /// Exact not matches (!=). + NOT_EQUAL, + /// Exact matches allowing null (is). + IS, + /// Exact not matches allowing null (is not) + IS_NOT, + /// Less (<). + LESS, + /// Less or equal (<=). + LESS_OR_EQUAL, + /// Greater or equal (>=). + GREATER_OR_EQUAL, + /// Greater (>). + GREATER, + /// Range contains neither endpoint, i.e. (a,b) + RANGE_OPEN, + /// Range contains low and high endpoint, i.e. [a,b] + RANGE_CLOSED, + /// Range includes low endpoint but not high endpoint, i.e. [a,b) + RANGE_HALF_OPEN, + /// Range includes high endpoint but not low endpoint, i.e. (a,b] + RANGE_HALF_CLOSED, + /// Inverted-Range contains neither endpoint, i.e. (a,b) + NOT_RANGE_OPEN, + /// Inverted-Range contains low and high endpoint, i.e. [a,b] + NOT_RANGE_CLOSED, + /// Inverted-Range includes low endpoint but not high endpoint, i.e. [a,b) + NOT_RANGE_HALF_OPEN, + /// Inverted-Range includes high endpoint but not low endpoint, i.e. (a,b] + NOT_RANGE_HALF_CLOSED, + /// List of values using the 'in' operator + IN_LIST_OF_VALUES, + /// Not-in list of values using the 'not in' operator + NOT_IN_LIST_OF_VALUES, + /// Boolean expression filter operator + BOOLEAN_EXPRESSION + }; + + /// + /// Contains static methods useful for help with FilterOperators. + /// + + public static class FilterOperatorExtensions + { + /// Returns true for all range operators, false if not a range operator. + /// true for ranges, false for anyting else + /// + public static bool IsRangeOperator(this FilterOperator op) + { + if ((op == FilterOperator.RANGE_CLOSED) || + (op == FilterOperator.RANGE_OPEN) || + (op == FilterOperator.RANGE_HALF_OPEN) || + (op == FilterOperator.RANGE_HALF_CLOSED)) + { + return true; + } + return false; + } + + /// + /// Returns true for inverted range operators, false if not an inverted range operator. + /// + /// true for inverted ranges, false for anyting else + public static bool IsInvertedRangeOperator(this FilterOperator op) + { + if ((op == FilterOperator.NOT_RANGE_CLOSED) || + (op == FilterOperator.NOT_RANGE_OPEN) || + (op == FilterOperator.NOT_RANGE_HALF_OPEN) || + (op == FilterOperator.NOT_RANGE_HALF_CLOSED)) + { + return true; + } + return false; + } + + /// Returns true for relational comparison operators which excludes the = equals operator, else returns false. + /// true for lesser or greater -type operators, false for anyting else + /// + public static bool IsComparisonOperator(this FilterOperator op) + { + if ((op == FilterOperator.LESS) || + (op == FilterOperator.LESS_OR_EQUAL) || + (op == FilterOperator.GREATER) || + (op == FilterOperator.GREATER_OR_EQUAL)) + { + return true; + } + return false; + } + + /// + /// Parse the range operator from booleans describing whether the Start or end values are exclusive. + /// + /// true if low endpoint is inclusive, false if not + /// true if high endpoint is inclusive, false if not + /// if set to true [is not]. + /// + /// FilterOperator for the combination inclusive or exclusive + /// + public static FilterOperator ParseRangeOperator(bool isInclusiveFirst, bool isInclusiveLast, bool isNot) + { + if (isInclusiveFirst && isInclusiveLast) + { + return isNot ? FilterOperator.NOT_RANGE_CLOSED : FilterOperator.RANGE_CLOSED; + } + if (isInclusiveFirst) + { + return isNot ? FilterOperator.NOT_RANGE_HALF_OPEN : FilterOperator.RANGE_HALF_OPEN; + } + if (isInclusiveLast) + { + return isNot ? FilterOperator.NOT_RANGE_HALF_CLOSED : FilterOperator.RANGE_HALF_CLOSED; + } + return isNot ? FilterOperator.NOT_RANGE_OPEN : FilterOperator.RANGE_OPEN; + } + + public static string GetTextualOp(this FilterOperator op) + { + switch (op) + { + case FilterOperator.EQUAL: + return ("="); + case FilterOperator.NOT_EQUAL: + return ("!="); + case FilterOperator.IS: + return ("is"); + case FilterOperator.IS_NOT: + return ("is not"); + case FilterOperator.LESS: + return ("<"); + case FilterOperator.LESS_OR_EQUAL: + return ("<="); + case FilterOperator.GREATER_OR_EQUAL: + return (">="); + case FilterOperator.GREATER: + return (">"); + case FilterOperator.RANGE_OPEN: + return (": return(,)"); + case FilterOperator.RANGE_CLOSED: + return ("[,]"); + case FilterOperator.RANGE_HALF_OPEN: + return ("[,)"); + case FilterOperator.RANGE_HALF_CLOSED: + return (": return(,]"); + case FilterOperator.NOT_RANGE_OPEN: + return ("-: return(,)"); + case FilterOperator.NOT_RANGE_CLOSED: + return ("-[,]"); + case FilterOperator.NOT_RANGE_HALF_OPEN: + return ("-[,)"); + case FilterOperator.NOT_RANGE_HALF_CLOSED: + return ("-: return(,]"); + case FilterOperator.IN_LIST_OF_VALUES: + return ("in"); + case FilterOperator.NOT_IN_LIST_OF_VALUES: + return ("!in"); + case FilterOperator.BOOLEAN_EXPRESSION: + return ("boolean_expr"); + } + + throw new ArgumentException(); + } + + public static FilterOperator ReversedRelationalOp(this FilterOperator op) + { + switch (op) + { + case FilterOperator.LESS: + return FilterOperator.GREATER; + case FilterOperator.LESS_OR_EQUAL: + return FilterOperator.GREATER_OR_EQUAL; + case FilterOperator.GREATER: + return FilterOperator.LESS; + case FilterOperator.GREATER_OR_EQUAL: + return FilterOperator.LESS_OR_EQUAL; + } + throw new ArgumentException("Not a relational operator: " + op); + } + } +} diff --git a/NEsper.Core/NEsper.Core/filter/FilterParamExprMap.cs b/NEsper.Core/NEsper.Core/filter/FilterParamExprMap.cs new file mode 100755 index 000000000..2e00087be --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterParamExprMap.cs @@ -0,0 +1,121 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.filter +{ + /// + /// A two-sided map for filter parameters mapping filter expression nodes to filter + /// parameters and back. For use in optimizing filter expressions. + /// + public class FilterParamExprMap + { + private readonly IDictionary _exprNodes; + private readonly IDictionary _specParams; + + /// Ctor. + public FilterParamExprMap() + { + _exprNodes = new LinkedHashMap(); + _specParams = new LinkedHashMap(); + } + + /// Add a node and filter param. + /// is the node to add + /// is null if the expression node has not optimized form + public void Put(ExprNode exprNode, FilterSpecParam param) + { + _exprNodes.Put(exprNode, param); + if (param != null) + { + _specParams.Put(param, exprNode); + } + } + + /// Returns all expression nodes for which no filter parameter exists. + /// list of expression nodes + public IList UnassignedExpressions + { + get + { + return _exprNodes.Where(entry => entry.Value == null).Select(entry => entry.Key).ToList(); + } + } + + public int CountUnassignedExpressions() + { + return _exprNodes.Where(entry => entry.Value == null).Select(entry => entry.Key).Count(); + } + + /// Returns all filter parameters. + /// filter parameters + public ICollection FilterParams + { + get { return _specParams.Keys; } + } + + public void RemoveNode(ExprNode node) + { + var param = _exprNodes.Delete(node); + if (param != null) + { + _specParams.Remove(param); + } + } + + /// Removes a filter parameter and it's associated expression node + /// is the parameter to remove + /// expression node removed + public ExprNode RemoveEntry(FilterSpecParam param) + { + ExprNode exprNode = _specParams.Get(param); + if (exprNode == null) + { + throw new IllegalStateException("Not found in collection param: " + param); + } + + _specParams.Remove(param); + _exprNodes.Remove(exprNode); + + return exprNode; + } + + /// Remove a filter parameter leaving the expression node in place. + /// filter parameter to remove + public void RemoveValue(FilterSpecParam param) + { + ExprNode exprNode = _specParams.Get(param); + if (exprNode == null) + { + throw new IllegalStateException("Not found in collection param: " + param); + } + + _specParams.Remove(param); + _exprNodes.Put(exprNode, null); + } + + public void Clear() + { + _exprNodes.Clear(); + _specParams.Clear(); + } + + public void Add(FilterParamExprMap other) + { + _exprNodes.PutAll(other._exprNodes); + _specParams.PutAll(other._specParams); + } + } +} diff --git a/NEsper.Core/NEsper.Core/filter/FilterParamIndexBase.cs b/NEsper.Core/NEsper.Core/filter/FilterParamIndexBase.cs new file mode 100755 index 000000000..1d69922a7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterParamIndexBase.cs @@ -0,0 +1,105 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.threading; + +namespace com.espertech.esper.filter +{ + /// + /// Each implementation of this abstract class represents an index of filter parameter constants supplied in filter + /// parameters in filter specifications that feature the same event property and operator. + ///

    + /// For example, a filter with a parameter of "count EQUALS 10" would be represented as index + /// for a property named "count" and for a filter operator typed "EQUALS". The index + /// would store a value of "10" in its internal structure. + ///

    + ///

    + /// Implementations make sure that the type of the Object constant in get and put calls matches the event property type. + ///

    + ///
    + public abstract class FilterParamIndexBase : EventEvaluator + { + private readonly FilterOperator _filterOperator; + + /// Constructor. + /// is the type of comparison performed. + protected FilterParamIndexBase(FilterOperator filterOperator) + { + _filterOperator = filterOperator; + } + + /// + /// Get the event evaluation instance associated with the constant. Returns null if no entry found for the constant. + /// The calling class must make sure that access to the underlying resource is protected + /// for multi-threaded access, the ReadWriteLock property must supply a lock for this purpose. + /// + /// Store the event evaluation instance for the given constant. Can override an existing value + /// for the same constant. + /// The calling class must make sure that access to the underlying resource is protected + /// for multi-threaded access, the ReadWriteLock property must supply a lock for this purpose. + /// + /// + /// is the constant supplied in the event filter parameter + /// + /// + /// event evaluator stored for the filter constant, or null if not found + /// + public abstract EventEvaluator this[Object filterConstant] { get; set; } + + /// + /// Remove the event evaluation instance for the given constant. Returns true if + /// the constant was found, or false if not. + /// The calling class must make sure that access to the underlying resource is protected + /// for multi-threaded writes, the ReadWriteLock property must supply a lock for this purpose. + /// + /// is the value supplied in the filter paremeter + /// true if found and removed, false if not found + public abstract bool Remove(Object filterConstant); + + /// + /// Return the number of distinct filter parameter constants stored. + /// The calling class must make sure that access to the underlying resource is protected + /// for multi-threaded writes, the ReadWriteLock property must supply a lock for this purpose. + /// + /// Number of entries in index + public abstract int Count { get ; } + + /// Supplies the lock for protected access. + /// lock + public abstract IReaderWriterLock ReadWriteLock { get; } + + /// Returns the filter operator that the index matches for. + /// filter operator + public FilterOperator FilterOperator + { + get { return _filterOperator; } + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override String ToString() + { + return "filterOperator=" + _filterOperator; + } + + /// + /// Perform the matching of an event based on the event property values, adding any callbacks for matches found to the matches list. + /// + /// is the event object wrapper to obtain event property values from + /// accumulates the matching filter callbacks + public abstract void MatchEvent(EventBean theTheEvent, ICollection matches); + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/filter/FilterParamIndexBooleanExpr.cs b/NEsper.Core/NEsper.Core/filter/FilterParamIndexBooleanExpr.cs new file mode 100755 index 000000000..6e92fdb26 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterParamIndexBooleanExpr.cs @@ -0,0 +1,109 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Diagnostics; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.threading; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.filter +{ + /// + /// MapIndex that simply maintains a list of bool expressions. + /// + public sealed class FilterParamIndexBooleanExpr : FilterParamIndexBase + { + private readonly IDictionary _evaluatorsMap; + private readonly IReaderWriterLock _constantsMapRwLock; + + /// + /// Constructs the index for multiple-exact matches. + /// + public FilterParamIndexBooleanExpr(IReaderWriterLock readWriteLock) + : base(FilterOperator.BOOLEAN_EXPRESSION) + { + _evaluatorsMap = new LinkedHashMap(); + _constantsMapRwLock = readWriteLock; + } + + public override EventEvaluator this[object filterConstant] + { + get { return Get(filterConstant); } + set { Put(filterConstant, value); } + } + + public EventEvaluator Get(Object filterConstant) + { + var keyValues = (ExprNodeAdapterBase)filterConstant; + return _evaluatorsMap.Get(keyValues); + } + + public void Put(Object filterConstant, EventEvaluator evaluator) + { + var keys = (ExprNodeAdapterBase)filterConstant; + _evaluatorsMap.Put(keys, evaluator); + } + + public override bool Remove(Object filterConstant) + { + var keys = (ExprNodeAdapterBase)filterConstant; + return _evaluatorsMap.Delete(keys) != null; + } + + public override int Count + { + get { return _evaluatorsMap.Count; } + } + + public override IReaderWriterLock ReadWriteLock + { + get { return _constantsMapRwLock; } + } + + public override void MatchEvent(EventBean theEvent, ICollection matches) + { + using (Instrument.With( + i => i.QFilterBoolean(this), + i => i.AFilterBoolean())) + { + using (_constantsMapRwLock.AcquireReadLock()) + { + if (InstrumentationHelper.ENABLED) + { + var i = -1; + foreach (var evals in _evaluatorsMap) + { + i++; + InstrumentationHelper.Get().QFilterBooleanExpr(i, evals); + var result = evals.Key.Evaluate(theEvent); + InstrumentationHelper.Get().AFilterBooleanExpr(result); + if (result) + { + evals.Value.MatchEvent(theEvent, matches); + } + } + } + else + { + foreach (KeyValuePair evals in _evaluatorsMap) + { + if (evals.Key.Evaluate(theEvent)) + { + evals.Value.MatchEvent(theEvent, matches); + } + } + } + } + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/filter/FilterParamIndexCompare.cs b/NEsper.Core/NEsper.Core/filter/FilterParamIndexCompare.cs new file mode 100755 index 000000000..c483c74ad --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterParamIndexCompare.cs @@ -0,0 +1,202 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.compat.threading; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.filter +{ + /// + /// MapIndex for filter parameter constants for the comparison operators (less, greater, etc). + /// The implementation is based on the SortedMap implementation of TreeMap. The index only + /// accepts numeric constants. It keeps a lower and upper bounds of all constants in the + /// index for fast range checking, since the assumption is that frequently values fall + /// within a range. + /// + public sealed class FilterParamIndexCompare : FilterParamIndexLookupableBase + { + private readonly OrderedDictionary _constantsMap; + private readonly IReaderWriterLock _constantsMapRwLock; + + private double? _lowerBounds; + private double? _upperBounds; + + public FilterParamIndexCompare(FilterSpecLookupable lookupable, IReaderWriterLock readWriteLock, FilterOperator filterOperator) + : base(filterOperator, lookupable) + { + _constantsMap = new OrderedDictionary(); + _constantsMapRwLock = readWriteLock; + + if ((filterOperator != FilterOperator.GREATER) && + (filterOperator != FilterOperator.GREATER_OR_EQUAL) && + (filterOperator != FilterOperator.LESS) && + (filterOperator != FilterOperator.LESS_OR_EQUAL)) + { + throw new ArgumentException("Invalid filter operator for index of " + filterOperator); + } + } + + public override EventEvaluator Get(Object filterConstant) + { + return _constantsMap.Get(filterConstant); + } + + public override void Put(Object filterConstant, EventEvaluator matcher) + { + _constantsMap.Put(filterConstant, matcher); + + // Update bounds + var constant = filterConstant.AsDouble(); + if ((_lowerBounds == null) || (constant < _lowerBounds)) + { + _lowerBounds = constant; + } + if ((_upperBounds == null) || (constant > _upperBounds)) + { + _upperBounds = constant; + } + } + + public override bool Remove(Object filterConstant) + { + if (_constantsMap.Delete(filterConstant) == null) + { + return false; + } + + UpdateBounds(); + + return true; + } + + public override int Count + { + get { return _constantsMap.Count; } + } + + public override IReaderWriterLock ReadWriteLock + { + get { return _constantsMapRwLock; } + } + + public override void MatchEvent(EventBean theEvent, ICollection matches) + { + var propertyValue = Lookupable.Getter.Get(theEvent); + var returnValue = new Mutable(false); + + using (Instrument.With( + i => i.QFilterReverseIndex(this, propertyValue), + i => i.AFilterReverseIndex(returnValue.Value))) + { + if (propertyValue == null) + { + return; + } + + // A undefine lower bound indicates an empty index + if (_lowerBounds == null) + { + return; + } + + var filterOperator = FilterOperator; + var propertyValueDouble = propertyValue.AsDouble(); + + // Based on current lower and upper bounds check if the property value falls outside - shortcut submap generation + if ((filterOperator == FilterOperator.GREATER) && (propertyValueDouble <= _lowerBounds)) + { + return; + } + else if ((filterOperator == FilterOperator.GREATER_OR_EQUAL) && (propertyValueDouble < _lowerBounds)) + { + return; + } + else if ((filterOperator == FilterOperator.LESS) && (propertyValueDouble >= _upperBounds)) + { + return; + } + else if ((filterOperator == FilterOperator.LESS_OR_EQUAL) && (propertyValueDouble > _upperBounds)) + { + return; + } + + // Look up in table + using (_constantsMapRwLock.AcquireReadLock()) + { + // Get the head or tail end of the map depending on comparison type + IDictionary subMap; + + if ((filterOperator == FilterOperator.GREATER) || + (filterOperator == FilterOperator.GREATER_OR_EQUAL)) + { + // At the head of the map are those with a lower numeric constants + subMap = _constantsMap.Head(propertyValue); + } + else + { + subMap = _constantsMap.Tail(propertyValue); + } + + // All entries in the subMap are elgibile, with an exception + EventEvaluator exactEquals = null; + if (filterOperator == FilterOperator.LESS) + { + exactEquals = _constantsMap.Get(propertyValue); + } + + foreach (EventEvaluator matcher in subMap.Values) + { + // For the LESS comparison type we ignore the exactly equal case + // The subMap is sorted ascending, thus the exactly equals case is the first + if (exactEquals != null) + { + exactEquals = null; + continue; + } + + matcher.MatchEvent(theEvent, matches); + } + + if (filterOperator == FilterOperator.GREATER_OR_EQUAL) + { + EventEvaluator matcher = _constantsMap.Get(propertyValue); + if (matcher != null) + { + matcher.MatchEvent(theEvent, matches); + } + } + } + + returnValue.Value = null; + } + } + + private void UpdateBounds() + { + if (_constantsMap.IsEmpty()) + { + _lowerBounds = null; + _upperBounds = null; + return; + } + _lowerBounds = (_constantsMap.Keys.First()).AsDouble(); + _upperBounds = (_constantsMap.Keys.Last()).AsDouble(); + } + + private static readonly ILog log = + LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + } +} diff --git a/NEsper.Core/NEsper.Core/filter/FilterParamIndexCompareString.cs b/NEsper.Core/NEsper.Core/filter/FilterParamIndexCompareString.cs new file mode 100755 index 000000000..6f8bd0668 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterParamIndexCompareString.cs @@ -0,0 +1,142 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.compat.threading; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.filter +{ + /// + /// MapIndex for filter parameter constants for the comparison operators (less, greater, etc). + /// The implementation is based on the SortedMap implementation of TreeMap. The index only + /// accepts String constants. It keeps a lower and upper bounds of all constants in the index + /// for fast range checking, since the assumption is that frequently values fall within a range. + /// + public sealed class FilterParamIndexCompareString : FilterParamIndexLookupableBase + { + private readonly OrderedDictionary _constantsMap; + private readonly IReaderWriterLock _constantsMapRwLock; + + public FilterParamIndexCompareString(FilterSpecLookupable lookupable, IReaderWriterLock readWriteLock, FilterOperator filterOperator) + : base(filterOperator, lookupable) + { + _constantsMap = new OrderedDictionary(); + _constantsMapRwLock = readWriteLock; + + if ((filterOperator != FilterOperator.GREATER) && + (filterOperator != FilterOperator.GREATER_OR_EQUAL) && + (filterOperator != FilterOperator.LESS) && + (filterOperator != FilterOperator.LESS_OR_EQUAL)) + { + throw new ArgumentException("Invalid filter operator for index of " + filterOperator); + } + } + + public override EventEvaluator Get(Object filterConstant) + { + return _constantsMap.Get(filterConstant); + } + + public override void Put(Object filterConstant, EventEvaluator matcher) + { + _constantsMap.Put(filterConstant, matcher); + } + + public override bool Remove(Object filterConstant) + { + return _constantsMap.Delete(filterConstant) != null; + } + + public override int Count + { + get { return _constantsMap.Count; } + } + + public override IReaderWriterLock ReadWriteLock + { + get { return _constantsMapRwLock; } + } + + public override void MatchEvent(EventBean theEvent, ICollection matches) + { + var propertyValue = Lookupable.Getter.Get(theEvent); + var returnValue = new Mutable(false); + + using (Instrument.With( + i => i.QFilterReverseIndex(this, propertyValue), + i => i.AFilterReverseIndex(returnValue.Value))) + { + if (propertyValue == null) + { + return; + } + + var filterOperator = FilterOperator; + + // Look up in table + using (_constantsMapRwLock.AcquireReadLock()) + { + // Get the head or tail end of the map depending on comparison type + IDictionary subMap; + + if ((filterOperator == FilterOperator.GREATER) || + (filterOperator == FilterOperator.GREATER_OR_EQUAL)) + { + // At the head of the map are those with a lower numeric constants + subMap = _constantsMap.Head(propertyValue); + } + else + { + subMap = _constantsMap.Tail(propertyValue); + } + + // All entries in the subMap are elgibile, with an exception + EventEvaluator exactEquals = null; + if (filterOperator == FilterOperator.LESS) + { + exactEquals = _constantsMap.Get(propertyValue); + } + + foreach (var matcher in subMap.Values) + { + // For the LESS comparison type we ignore the exactly equal case + // The subMap is sorted ascending, thus the exactly equals case is the first + if (exactEquals != null) + { + exactEquals = null; + continue; + } + + matcher.MatchEvent(theEvent, matches); + } + + if (filterOperator == FilterOperator.GREATER_OR_EQUAL) + { + var matcher = _constantsMap.Get(propertyValue); + if (matcher != null) + { + matcher.MatchEvent(theEvent, matches); + } + } + } + + returnValue.Value = null; + } + } + + private static readonly ILog Log = + LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + } +} diff --git a/NEsper.Core/NEsper.Core/filter/FilterParamIndexDoubleRange.cs b/NEsper.Core/NEsper.Core/filter/FilterParamIndexDoubleRange.cs new file mode 100755 index 000000000..93bb2029c --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterParamIndexDoubleRange.cs @@ -0,0 +1,117 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.logging; +using com.espertech.esper.compat.threading; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.filter +{ + /// + /// Index for filter parameter constants for the range operators (range open/closed/half). + /// The implementation is based on the SortedMap implementation of TreeMap and stores only + /// expression parameter values of type DoubleRange. + /// + public sealed class FilterParamIndexDoubleRange : FilterParamIndexDoubleRangeBase + { + public FilterParamIndexDoubleRange(FilterSpecLookupable lookupable, IReaderWriterLock readWriteLock, FilterOperator filterOperator) + : base(lookupable, readWriteLock, filterOperator) + { + if (!(filterOperator.IsRangeOperator())) + { + throw new ArgumentException("Invalid filter operator " + filterOperator); + } + } + + public override void MatchEvent(EventBean theEvent, ICollection matches) + { + var objAttributeValue = Lookupable.Getter.Get(theEvent); + var returnValue = new Mutable(false); + + using (Instrument.With( + i => i.QFilterReverseIndex(this, objAttributeValue), + i => i.AFilterReverseIndex(returnValue.Value))) + { + if (objAttributeValue == null) + { + return; + } + + var attributeValue = objAttributeValue.AsDouble(); + + var rangeStart = new DoubleRange(attributeValue - LargestRangeValueDouble, attributeValue); + var rangeEnd = new DoubleRange(attributeValue, Double.MaxValue); + + var subMap = Ranges.Between(rangeStart, true, rangeEnd, true); + + // For not including either endpoint + // A bit awkward to duplicate the loop code, however better than checking the bool many times over + // This may be a bit of an early performance optimization - the optimizer after all may do this better + if (FilterOperator == FilterOperator.RANGE_OPEN) // include neither endpoint + { + foreach (var entry in subMap) + { + if ((attributeValue > entry.Key.Min) && + (attributeValue < entry.Key.Max)) + { + entry.Value.MatchEvent(theEvent, matches); + } + } + } + else if (FilterOperator == FilterOperator.RANGE_CLOSED) // include all endpoints + { + foreach (var entry in subMap) + { + if ((attributeValue >= entry.Key.Min) && + (attributeValue <= entry.Key.Max)) + { + entry.Value.MatchEvent(theEvent, matches); + } + } + } + else if (FilterOperator == FilterOperator.RANGE_HALF_CLOSED) // include high endpoint not low endpoint + { + foreach (var entry in subMap) + { + if ((attributeValue > entry.Key.Min) && + (attributeValue <= entry.Key.Max)) + { + entry.Value.MatchEvent(theEvent, matches); + } + } + } + else if (FilterOperator == FilterOperator.RANGE_HALF_OPEN) // include low endpoint not high endpoint + { + foreach (var entry in subMap) + { + if ((attributeValue >= entry.Key.Min) && + (attributeValue < entry.Key.Max)) + { + entry.Value.MatchEvent(theEvent, matches); + } + } + } + else + { + throw new IllegalStateException("Invalid filter operator " + FilterOperator); + } + + returnValue.Value = null; + } + } + + private static readonly ILog Log = + LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + } +} diff --git a/NEsper.Core/NEsper.Core/filter/FilterParamIndexDoubleRangeBase.cs b/NEsper.Core/NEsper.Core/filter/FilterParamIndexDoubleRangeBase.cs new file mode 100755 index 000000000..db7f4fad0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterParamIndexDoubleRangeBase.cs @@ -0,0 +1,97 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.threading; + +namespace com.espertech.esper.filter +{ + /// + /// MapIndex for filter parameter constants for the range operators (range open/closed/half). + /// The implementation is based on the SortedMap implementation of TreeMap and stores only + /// expression parameter values of type DoubleRange. + /// + public abstract class FilterParamIndexDoubleRangeBase : FilterParamIndexLookupableBase + { + private readonly IDictionary _rangesNullEndpoints; + private readonly IReaderWriterLock _rangesRwLock; + + protected readonly OrderedDictionary Ranges; + protected double LargestRangeValueDouble = Double.MinValue; + + protected FilterParamIndexDoubleRangeBase(FilterSpecLookupable lookupable, IReaderWriterLock readWriteLock, FilterOperator filterOperator) + : base(filterOperator, lookupable) + { + Ranges = new OrderedDictionary(new DoubleRangeComparator()); + _rangesNullEndpoints = new Dictionary(); + _rangesRwLock = readWriteLock; + } + + public override EventEvaluator Get(Object expressionValue) + { + if (!(expressionValue is DoubleRange)) + { + throw new ArgumentException("Supplied expressionValue must be of type DoubleRange"); + } + + var range = (DoubleRange)expressionValue; + if ((range.Max == null) || (range.Min == null)) + { + return _rangesNullEndpoints.Get(range); + } + + return Ranges.Get(range); + } + + public override void Put(Object expressionValue, EventEvaluator matcher) + { + if (!(expressionValue is DoubleRange)) + { + throw new ArgumentException("Supplied expressionValue must be of type DoubleRange"); + } + + var range = (DoubleRange)expressionValue; + if ((range.Max == null) || (range.Min == null)) + { + _rangesNullEndpoints.Put(range, matcher); // endpoints null - we don't enter + return; + } + + if (Math.Abs(range.Max.Value - range.Min.Value) > LargestRangeValueDouble) + { + LargestRangeValueDouble = Math.Abs(range.Max.Value - range.Min.Value); + } + + Ranges.Put(range, matcher); + } + + public override bool Remove(Object filterConstant) + { + var range = (DoubleRange)filterConstant; + + if ((range.Max == null) || (range.Min == null)) + { + return _rangesNullEndpoints.Delete(range) != null; + } + + return Ranges.Delete(range) != null; + } + + public override int Count + { + get { return Ranges.Count; } + } + + public override IReaderWriterLock ReadWriteLock + { + get { return _rangesRwLock; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/filter/FilterParamIndexDoubleRangeInverted.cs b/NEsper.Core/NEsper.Core/filter/FilterParamIndexDoubleRangeInverted.cs new file mode 100755 index 000000000..7cf17643a --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterParamIndexDoubleRangeInverted.cs @@ -0,0 +1,113 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.logging; +using com.espertech.esper.compat.threading; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.filter +{ + /// + /// Index for filter parameter constants for the not range operators (range open/closed/half). + /// The implementation is based on the SortedMap implementation of TreeMap and stores only + /// expression parameter values of type DoubleRange. + /// + public sealed class FilterParamIndexDoubleRangeInverted : FilterParamIndexDoubleRangeBase + { + public FilterParamIndexDoubleRangeInverted(FilterSpecLookupable lookupable, IReaderWriterLock readWriteLock, FilterOperator filterOperator) + : base(lookupable, readWriteLock, filterOperator) + { + if (!(filterOperator.IsInvertedRangeOperator())) + { + throw new ArgumentException("Invalid filter operator " + filterOperator); + } + } + + public override void MatchEvent(EventBean theEvent, ICollection matches) + { + var objAttributeValue = Lookupable.Getter.Get(theEvent); + var returnValue = new Mutable(false); + + using (Instrument.With( + i => i.QFilterReverseIndex(this, objAttributeValue), + i => i.AFilterReverseIndex(returnValue.Value))) + { + if (objAttributeValue == null) + { + return; + } + + var attributeValue = objAttributeValue.AsDouble(); + + if (FilterOperator == FilterOperator.NOT_RANGE_CLOSED) + { + // include all endpoints + foreach (var entry in Ranges) + { + if ((attributeValue < entry.Key.Min) || + (attributeValue > entry.Key.Max)) + { + entry.Value.MatchEvent(theEvent, matches); + } + } + } + else if (FilterOperator == FilterOperator.NOT_RANGE_OPEN) + { + // include neither endpoint + foreach (var entry in Ranges) + { + if ((attributeValue <= entry.Key.Min) || + (attributeValue >= entry.Key.Max)) + { + entry.Value.MatchEvent(theEvent, matches); + } + } + } + else if (FilterOperator == FilterOperator.NOT_RANGE_HALF_CLOSED) + { + // include high endpoint not low endpoint + foreach (var entry in Ranges) + { + if ((attributeValue <= entry.Key.Min) || + (attributeValue > entry.Key.Max)) + { + entry.Value.MatchEvent(theEvent, matches); + } + } + } + else if (FilterOperator == FilterOperator.NOT_RANGE_HALF_OPEN) + { + // include low endpoint not high endpoint + foreach (var entry in Ranges) + { + if ((attributeValue < entry.Key.Min) || + (attributeValue >= entry.Key.Max)) + { + entry.Value.MatchEvent(theEvent, matches); + } + } + } + else + { + throw new IllegalStateException("Invalid filter operator " + FilterOperator); + } + + returnValue.Value = null; + } + } + + private static readonly ILog Log = + LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + } +} diff --git a/NEsper.Core/NEsper.Core/filter/FilterParamIndexEquals.cs b/NEsper.Core/NEsper.Core/filter/FilterParamIndexEquals.cs new file mode 100755 index 000000000..ed80ed581 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterParamIndexEquals.cs @@ -0,0 +1,63 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.threading; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.filter +{ + /// + /// Index for filter parameter constants to match using the equals (=) operator. + /// The implementation is based on a regular HashMap. + /// + public sealed class FilterParamIndexEquals : FilterParamIndexEqualsBase + { + public FilterParamIndexEquals(FilterSpecLookupable lookupable, IReaderWriterLock readWriteLock) + : base(lookupable, readWriteLock, FilterOperator.EQUAL) + { + } + + public override void MatchEvent(EventBean theEvent, ICollection matches) + { + var attributeValue = Lookupable.Getter.Get(theEvent); + var returnValue = new Mutable(false); + + using (Instrument.With( + i => i.QFilterReverseIndex(this, attributeValue), + i => i.AFilterReverseIndex(returnValue.Value))) + { + if (attributeValue == null) + { + // null cannot match, not even null: requires use of "is" + return; + } + + // Look up in hashtable + EventEvaluator evaluator = null; + using (ConstantsMapRwLock.AcquireReadLock()) + { + evaluator = ConstantsMap.Get(attributeValue); + } + + // No listener found for the value, return + if (evaluator == null) + { + return; + } + + evaluator.MatchEvent(theEvent, matches); + returnValue.Value = true; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/filter/FilterParamIndexEqualsBase.cs b/NEsper.Core/NEsper.Core/filter/FilterParamIndexEqualsBase.cs new file mode 100755 index 000000000..ad55566ab --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterParamIndexEqualsBase.cs @@ -0,0 +1,58 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.threading; + +namespace com.espertech.esper.filter +{ + /// + /// Index for filter parameter constants to match using the equals (=) operator. The implementation is based on a + /// regular HashMap. + /// + public abstract class FilterParamIndexEqualsBase : FilterParamIndexLookupableBase + { + protected readonly IDictionary ConstantsMap; + protected readonly IReaderWriterLock ConstantsMapRwLock; + + protected FilterParamIndexEqualsBase(FilterSpecLookupable lookupable, IReaderWriterLock readWriteLock, FilterOperator filterOperator) + : base(filterOperator, lookupable) + { + ConstantsMap = new Dictionary().WithNullSupport(); + ConstantsMapRwLock = readWriteLock; + } + + public override EventEvaluator Get(Object filterConstant) + { + return ConstantsMap.Get(filterConstant); + } + + public override void Put(Object filterConstant, EventEvaluator evaluator) + { + ConstantsMap.Put(filterConstant, evaluator); + } + + public override bool Remove(Object filterConstant) + { + return ConstantsMap.Remove(filterConstant); + } + + public override int Count + { + get { return ConstantsMap.Count; } + } + + public override IReaderWriterLock ReadWriteLock + { + get { return ConstantsMapRwLock; } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/filter/FilterParamIndexEqualsIs.cs b/NEsper.Core/NEsper.Core/filter/FilterParamIndexEqualsIs.cs new file mode 100755 index 000000000..92d818ca9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterParamIndexEqualsIs.cs @@ -0,0 +1,58 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.threading; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.filter +{ + /// + /// MapIndex for filter parameter constants to match using the equals (=) operator. + /// The implementation is based on a regular HashMap. + /// + public sealed class FilterParamIndexEqualsIs : FilterParamIndexEqualsBase + { + public FilterParamIndexEqualsIs(FilterSpecLookupable lookupable, IReaderWriterLock readWriteLock) + : base(lookupable, readWriteLock, FilterOperator.IS) + { + } + + public override void MatchEvent(EventBean theEvent, ICollection matches) + { + var attributeValue = Lookupable.Getter.Get(theEvent); + var returnValue = new Mutable(false); + + using (Instrument.With( + i => i.QFilterReverseIndex(this, attributeValue), + i => i.AFilterReverseIndex(returnValue.Value))) + { + EventEvaluator evaluator = null; + using (ConstantsMapRwLock.AcquireReadLock()) + { + evaluator = ConstantsMap.Get(attributeValue); + } + + // No listener found for the value, return + if (evaluator == null) + { + returnValue.Value = false; + } + else + { + evaluator.MatchEvent(theEvent, matches); + returnValue.Value = true; + } + } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/filter/FilterParamIndexIn.cs b/NEsper.Core/NEsper.Core/filter/FilterParamIndexIn.cs new file mode 100755 index 000000000..c5b5bf052 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterParamIndexIn.cs @@ -0,0 +1,149 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.compat.threading; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.filter +{ + /// + /// MapIndex for filter parameter constants to match using the 'in' operator to match against + /// a supplied set of values (i.e. multiple possible exact matches). The implementation is + /// based on a regular HashMap. + /// + public sealed class FilterParamIndexIn : FilterParamIndexLookupableBase + { + private readonly IDictionary> _constantsMap; + private readonly IDictionary _evaluatorsMap; + private readonly IReaderWriterLock _constantsMapRwLock; + + public FilterParamIndexIn(FilterSpecLookupable lookupable, IReaderWriterLock readWriteLock) + : base(FilterOperator.IN_LIST_OF_VALUES, lookupable) + { + _constantsMap = new Dictionary>().WithNullSupport(); + _evaluatorsMap = new Dictionary(); + _constantsMapRwLock = readWriteLock; + } + + public override EventEvaluator Get(Object filterConstant) + { + var keyValues = (MultiKeyUntyped)filterConstant; + return _evaluatorsMap.Get(keyValues); + } + + public override void Put(Object filterConstant, EventEvaluator evaluator) + { + // Store evaluator keyed to set of values + var keys = (MultiKeyUntyped)filterConstant; + + // make sure to remove the old evaluator for this constant + var oldEvaluator = _evaluatorsMap.Push(keys, evaluator); + + // Store each value to match against in Map with it's evaluator as a list + var keyValues = keys.Keys; + for (var i = 0; i < keyValues.Length; i++) + { + var evaluators = _constantsMap.Get(keyValues[i]); + if (evaluators == null) + { + evaluators = new List(); + _constantsMap.Put(keyValues[i], evaluators); + } + else + { + if (oldEvaluator != null) + { + evaluators.Remove(oldEvaluator); + } + } + evaluators.Add(evaluator); + } + } + + public override bool Remove(Object filterConstant) + { + var keys = (MultiKeyUntyped)filterConstant; + + // remove the mapping of value set to evaluator + var eval = _evaluatorsMap.Delete(keys); + var isRemoved = eval != null; + + var keyValues = keys.Keys; + for (var i = 0; i < keyValues.Length; i++) + { + var evaluators = _constantsMap.Get(keyValues[i]); + if (evaluators != null) // could be removed already as same-value constants existed + { + evaluators.Remove(eval); + if (evaluators.IsEmpty()) + { + _constantsMap.Remove(keyValues[i]); + } + } + } + + return isRemoved; + } + + public override int Count + { + get { return _constantsMap.Count; } + } + + public override IReaderWriterLock ReadWriteLock + { + get { return _constantsMapRwLock; } + } + + public override void MatchEvent(EventBean theEvent, ICollection matches) + { + var attributeValue = Lookupable.Getter.Get(theEvent); + var returnValue = new Mutable(false); + + using (Instrument.With( + i => i.QFilterReverseIndex(this, attributeValue), + i => i.AFilterReverseIndex(returnValue.Value))) + { + if (attributeValue == null) + { + return; + } + + // Look up in hashtable + using (_constantsMapRwLock.AcquireReadLock()) + { + var evaluators = _constantsMap.Get(attributeValue); + + // No listener found for the value, return + if (evaluators == null) + { + return; + } + + foreach (var evaluator in evaluators) + { + evaluator.MatchEvent(theEvent, matches); + } + + returnValue.Value = null; + } + } + } + + private static readonly ILog Log = + LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + } +} diff --git a/NEsper.Core/NEsper.Core/filter/FilterParamIndexLookupableBase.cs b/NEsper.Core/NEsper.Core/filter/FilterParamIndexLookupableBase.cs new file mode 100755 index 000000000..c1e90b860 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterParamIndexLookupableBase.cs @@ -0,0 +1,73 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.filter +{ + /// + /// Each implementation of this abstract class represents an index of filter parameter constants supplied in + /// filter parameters in filter specifications that feature the same event property and operator. + /// + /// For example, a filter with a parameter of "count EQUALS 10" would be represented as index for a property + /// named "count" and for a filter operator typed "EQUALS". The index would store a value of "10" in its + /// internal structure. + /// + /// Implementations make sure that the type of the Object constant in get and put calls matches the event property type. + /// + public abstract class FilterParamIndexLookupableBase : FilterParamIndexBase + { + /// Constructor. + /// is the type of comparison performed. + /// is the lookupable + protected FilterParamIndexLookupableBase(FilterOperator filterOperator, FilterSpecLookupable lookupable) + : base(filterOperator) + { + Lookupable = lookupable; + } + + /// + /// Get the event evaluation instance associated with the constant. Returns null if no entry found + /// for the constant. The calling class must make sure that access to the underlying resource is + /// protected for multi-threaded access, the GetReadWriteLock() method must supply a lock for this + /// purpose. + /// + /// is the constant supplied in the event filter parameter + /// + /// event evaluator stored for the filter constant, or null if not found + /// + public abstract EventEvaluator Get(Object filterConstant); + + /// + /// Store the event evaluation instance for the given constant. Can override an existing value for + /// the same constant. The calling class must make sure that access to the underlying resource is + /// protected for multi-threaded access, the GetReadWriteLock() method must supply a lock for this + /// purpose. + /// + /// is the constant supplied in the filter parameter + /// to be stored for the constant + public abstract void Put(Object filterConstant, EventEvaluator evaluator); + + public override EventEvaluator this[Object filterConstant] + { + get { return Get(filterConstant); } + set { Put(filterConstant, value); } + } + + public override String ToString() + { + return string.Format("{0} lookupable={1}", base.ToString(), Lookupable); + } + + /// + /// Gets or sets the lookupable. + /// + /// The lookupable. + public FilterSpecLookupable Lookupable { get; set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/filter/FilterParamIndexNotEquals.cs b/NEsper.Core/NEsper.Core/filter/FilterParamIndexNotEquals.cs new file mode 100755 index 000000000..7cbb1403b --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterParamIndexNotEquals.cs @@ -0,0 +1,70 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.logging; +using com.espertech.esper.compat.threading; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.filter +{ + /// + /// Index for filter parameter constants to match using the equals (=) operator. + /// The implementation is based on a regular dictionary. + /// + public sealed class FilterParamIndexNotEquals : FilterParamIndexNotEqualsBase + { + public FilterParamIndexNotEquals(FilterSpecLookupable lookupable, IReaderWriterLock readWriteLock) + : base(lookupable, readWriteLock, FilterOperator.NOT_EQUAL) + { + } + + public override void MatchEvent(EventBean theEvent, ICollection matches) + { + var attributeValue = Lookupable.Getter.Get(theEvent); + var returnValue = new Mutable(false); + + using (Instrument.With( + i => i.QFilterReverseIndex(this, attributeValue), + i => i.AFilterReverseIndex(returnValue.Value))) + { + if (attributeValue == null) + { + // null cannot match any other value, not even null (use "is" or "is not", i.e. null != null returns null) + return; + } + + // Look up in hashtable + using (ConstantsMapRwLock.AcquireReadLock()) + { + foreach (var entry in ConstantsMap) + { + if (entry.Key == null) + { + continue; + // null-value cannot match, not even null (use "is" or "is not", i.e. null != null returns null) + } + + if (!entry.Key.Equals(attributeValue)) + { + entry.Value.MatchEvent(theEvent, matches); + } + } + + returnValue.Value = null; + } + } + } + + private static readonly ILog Log = + LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + } +} diff --git a/NEsper.Core/NEsper.Core/filter/FilterParamIndexNotEqualsBase.cs b/NEsper.Core/NEsper.Core/filter/FilterParamIndexNotEqualsBase.cs new file mode 100755 index 000000000..edda59c78 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterParamIndexNotEqualsBase.cs @@ -0,0 +1,62 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.threading; + +namespace com.espertech.esper.filter +{ + /// + /// MapIndex for filter parameter constants to match using the equals (=) operator. The + /// implementation is based on a regular HashMap. + /// + public abstract class FilterParamIndexNotEqualsBase : FilterParamIndexLookupableBase + { + protected readonly IDictionary ConstantsMap; + protected readonly IReaderWriterLock ConstantsMapRwLock; + + protected FilterParamIndexNotEqualsBase(FilterSpecLookupable lookupable, IReaderWriterLock readWriteLock, FilterOperator filterOperator) + : base(filterOperator, lookupable) + { + ConstantsMap = new Dictionary().WithNullSupport(); + ConstantsMapRwLock = readWriteLock; + } + + public override EventEvaluator Get(Object filterConstant) + { + return ConstantsMap.Get(filterConstant); + } + + public override void Put(Object filterConstant, EventEvaluator evaluator) + { + ConstantsMap.Put(filterConstant, evaluator); + } + + public override bool Remove(Object filterConstant) + { + if (ConstantsMap.Delete(filterConstant) == null) + { + return false; + } + return true; + } + + public override int Count + { + get { return ConstantsMap.Count; } + } + + public override IReaderWriterLock ReadWriteLock + { + get { return ConstantsMapRwLock; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/filter/FilterParamIndexNotEqualsIs.cs b/NEsper.Core/NEsper.Core/filter/FilterParamIndexNotEqualsIs.cs new file mode 100755 index 000000000..8916e3648 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterParamIndexNotEqualsIs.cs @@ -0,0 +1,66 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.logging; +using com.espertech.esper.compat.threading; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.filter +{ + /// + /// MapIndex for filter parameter constants to match using the equals (=) operator. The + /// implementation is based on a regular HashMap. + /// + public sealed class FilterParamIndexNotEqualsIs : FilterParamIndexNotEqualsBase + { + public FilterParamIndexNotEqualsIs(FilterSpecLookupable lookupable, IReaderWriterLock readWriteLock) + : base(lookupable, readWriteLock, FilterOperator.IS_NOT) + { + } + + public override void MatchEvent(EventBean theEvent, ICollection matches) + { + var attributeValue = Lookupable.Getter.Get(theEvent); + var returnValue = new Mutable(false); + + using (Instrument.With( + i => i.QFilterReverseIndex(this, attributeValue), + i => i.AFilterReverseIndex(returnValue.Value))) + { + // Look up in hashtable + using (ConstantsMapRwLock.AcquireReadLock()) + { + foreach (var entry in ConstantsMap) + { + if (entry.Key == null) + { + if (attributeValue != null) + { + entry.Value.MatchEvent(theEvent, matches); + } + continue; + } + + if (!entry.Key.Equals(attributeValue)) + { + entry.Value.MatchEvent(theEvent, matches); + } + } + } + + returnValue.Value = null; + } + } + + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + } +} diff --git a/NEsper.Core/NEsper.Core/filter/FilterParamIndexNotIn.cs b/NEsper.Core/NEsper.Core/filter/FilterParamIndexNotIn.cs new file mode 100755 index 000000000..f7e6c3c6f --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterParamIndexNotIn.cs @@ -0,0 +1,159 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.compat.threading; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.filter +{ + /// + /// Index for filter parameter constants to match using the 'not in' operator to match + /// against a all other values then the supplied set of values. + /// + public sealed class FilterParamIndexNotIn : FilterParamIndexLookupableBase + { + private readonly IDictionary> _constantsMap; + private readonly IDictionary _filterValueEvaluators; + private readonly ICollection _evaluatorsSet; + private readonly IReaderWriterLock _constantsMapRwLock; + + public FilterParamIndexNotIn(FilterSpecLookupable lookupable, IReaderWriterLock readWriteLock) + : base(FilterOperator.NOT_IN_LIST_OF_VALUES, lookupable) + { + _constantsMap = new Dictionary>().WithNullSupport(); + _filterValueEvaluators = new Dictionary(); + _evaluatorsSet = new HashSet(); + _constantsMapRwLock = readWriteLock; + } + + public override EventEvaluator Get(Object filterConstant) + { + var keyValues = (MultiKeyUntyped)filterConstant; + return _filterValueEvaluators.Get(keyValues); + } + + public override void Put(Object filterConstant, EventEvaluator evaluator) + { + // Store evaluator keyed to set of values + var keys = (MultiKeyUntyped)filterConstant; + _filterValueEvaluators.Put(keys, evaluator); + _evaluatorsSet.Add(evaluator); + + // Store each value to match against in Map with it's evaluator as a list + var keyValues = keys.Keys; + foreach (var keyValue in keyValues) + { + var evaluators = _constantsMap.Get(keyValue); + if (evaluators == null) + { + evaluators = new HashSet(); + _constantsMap.Put(keyValue, evaluators); + } + evaluators.Add(evaluator); + } + } + + public override bool Remove(Object filterConstant) + { + var keys = (MultiKeyUntyped)filterConstant; + + // remove the mapping of value set to evaluator + var eval = _filterValueEvaluators.Delete(keys); + _evaluatorsSet.Remove(eval); + var isRemoved = eval != null; + + var keyValues = keys.Keys; + foreach (var keyValue in keyValues) + { + var evaluators = _constantsMap.Get(keyValue); + if (evaluators != null) // could already be removed as constants may be the same + { + evaluators.Remove(eval); + if (evaluators.IsEmpty()) + { + _constantsMap.Remove(keyValue); + } + } + } + return isRemoved; + } + + public override int Count + { + get { return _constantsMap.Count; } + } + + public override IReaderWriterLock ReadWriteLock + { + get { return _constantsMapRwLock; } + } + + public override void MatchEvent(EventBean theEvent, ICollection matches) + { + var attributeValue = Lookupable.Getter.Get(theEvent); + var returnValue = new Mutable(false); + + using (Instrument.With( + i => i.QFilterReverseIndex(this, attributeValue), + i => i.AFilterReverseIndex(returnValue.Value))) + { + if (attributeValue == null) + { + return; + } + + // Look up in hashtable the set of not-in evaluators + using (_constantsMapRwLock.AcquireReadLock()) + { + ICollection evalNotMatching = _constantsMap.Get(attributeValue); + + // if all known evaluators are matching, invoke all + if (evalNotMatching == null) + { + foreach (var eval in _evaluatorsSet) + { + eval.MatchEvent(theEvent, matches); + } + + returnValue.Value = true; + return; + } + + // if none are matching, we are done + if (evalNotMatching.Count == _evaluatorsSet.Count) + { + returnValue.Value = false; + return; + } + + // handle partial matches: loop through all evaluators and see which one should not be matching, match all else + foreach (var eval in _evaluatorsSet) + { + if (!(evalNotMatching.Contains(eval))) + { + eval.MatchEvent(theEvent, matches); + } + } + + returnValue.Value = null; + } + } + } + + private static readonly ILog Log = + LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + } +} diff --git a/NEsper.Core/NEsper.Core/filter/FilterParamIndexStringRange.cs b/NEsper.Core/NEsper.Core/filter/FilterParamIndexStringRange.cs new file mode 100755 index 000000000..41b6ce4d8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterParamIndexStringRange.cs @@ -0,0 +1,118 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.logging; +using com.espertech.esper.compat.threading; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.filter +{ + /// + /// MapIndex for filter parameter constants for the range operators (range open/closed/half). + /// The implementation is based on the SortedMap implementation of TreeMap and stores only + /// expression parameter values of type DoubleRange. + /// + public sealed class FilterParamIndexStringRange : FilterParamIndexStringRangeBase + { + public FilterParamIndexStringRange(FilterSpecLookupable lookupable, IReaderWriterLock readWriteLock, FilterOperator filterOperator) + : base(lookupable, readWriteLock, filterOperator) + { + if (!(filterOperator.IsRangeOperator())) + { + throw new ArgumentException("Invalid filter operator " + filterOperator); + } + } + + public override void MatchEvent(EventBean theEvent, ICollection matches) + { + var objAttributeValue = Lookupable.Getter.Get(theEvent); + var returnValue = new Mutable(false); + + using (Instrument.With( + i => i.QFilterReverseIndex(this, objAttributeValue), + i => i.AFilterReverseIndex(returnValue.Value))) + { + if (objAttributeValue == null) + { + return; + } + + var attributeValue = (String)objAttributeValue; + + var rangeStart = new StringRange(null, attributeValue); + var rangeEnd = new StringRange(attributeValue, null); + var subMap = Ranges.Between(rangeStart, true, rangeEnd, true); + + // For not including either endpoint + // A bit awkward to duplicate the loop code, however better than checking the bool many times over + // This may be a bit of an early performance optimization - the optimizer after all may do this better + if (FilterOperator == FilterOperator.RANGE_OPEN) // include neither endpoint + { + foreach (var entry in subMap) + { + if (String.Compare(entry.Key.Min, attributeValue, StringComparison.Ordinal) < 0 && + String.Compare(entry.Key.Max, attributeValue, StringComparison.Ordinal) > 0) + { + entry.Value.MatchEvent(theEvent, matches); + } + } + } + else if (FilterOperator == FilterOperator.RANGE_CLOSED) // include all endpoints + { + foreach (var entry in subMap) + { + if (String.Compare(entry.Key.Min, attributeValue, StringComparison.Ordinal) <= 0 && + String.Compare(entry.Key.Max, attributeValue, StringComparison.Ordinal) >= 0) + { + entry.Value.MatchEvent(theEvent, matches); + } + } + } + else if (FilterOperator == FilterOperator.RANGE_HALF_CLOSED) + // include high endpoint not low endpoint + { + foreach (var entry in subMap) + { + if (String.Compare(entry.Key.Min, attributeValue, StringComparison.Ordinal) < 0 && + String.Compare(entry.Key.Max, attributeValue, StringComparison.Ordinal) >= 0) + { + entry.Value.MatchEvent(theEvent, matches); + } + } + } + else if (FilterOperator == FilterOperator.RANGE_HALF_OPEN) + // include low endpoint not high endpoint + { + foreach (var entry in subMap) + { + if (String.Compare(entry.Key.Min, attributeValue, StringComparison.Ordinal) <= 0 && + String.Compare(entry.Key.Max, attributeValue, StringComparison.Ordinal) > 0) + { + entry.Value.MatchEvent(theEvent, matches); + } + } + } + else + { + throw new IllegalStateException("Invalid filter operator " + FilterOperator); + } + + returnValue.Value = null; + } + } + + private static readonly ILog Log = + LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + } +} diff --git a/NEsper.Core/NEsper.Core/filter/FilterParamIndexStringRangeBase.cs b/NEsper.Core/NEsper.Core/filter/FilterParamIndexStringRangeBase.cs new file mode 100755 index 000000000..c3a7c9bdd --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterParamIndexStringRangeBase.cs @@ -0,0 +1,85 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.threading; + +namespace com.espertech.esper.filter +{ + public abstract class FilterParamIndexStringRangeBase : FilterParamIndexLookupableBase + { + protected readonly OrderedDictionary Ranges; + private readonly IDictionary _rangesNullEndpoints; + private readonly IReaderWriterLock _rangesRwLock; + + protected FilterParamIndexStringRangeBase(FilterSpecLookupable lookupable, IReaderWriterLock readWriteLock, FilterOperator filterOperator) + : base(filterOperator, lookupable) + { + Ranges = new OrderedDictionary(new StringRangeComparator()); + _rangesNullEndpoints = new Dictionary(); + _rangesRwLock = readWriteLock; + } + + public override EventEvaluator Get(Object expressionValue) + { + if (!(expressionValue is StringRange)) + { + throw new ArgumentException("Supplied expressionValue must be of type StringRange"); + } + + var range = (StringRange)expressionValue; + if ((range.Max == null) || (range.Min == null)) + { + return _rangesNullEndpoints.Get(range); + } + + return Ranges.Get(range); + } + + public override void Put(Object expressionValue, EventEvaluator matcher) + { + if (!(expressionValue is StringRange)) + { + throw new ArgumentException("Supplied expressionValue must be of type DoubleRange"); + } + + var range = (StringRange)expressionValue; + if ((range.Max == null) || (range.Min == null)) + { + _rangesNullEndpoints.Put(range, matcher); // endpoints null - we don't enter + return; + } + + Ranges.Put(range, matcher); + } + + public override bool Remove(Object filterConstant) + { + var range = (StringRange)filterConstant; + if ((range.Max == null) || (range.Min == null)) + { + return _rangesNullEndpoints.Delete(range) != null; + } + + return Ranges.Delete(range) != null; + } + + public override int Count + { + get { return Ranges.Count; } + } + + public override IReaderWriterLock ReadWriteLock + { + get { return _rangesRwLock; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/filter/FilterParamIndexStringRangeInverted.cs b/NEsper.Core/NEsper.Core/filter/FilterParamIndexStringRangeInverted.cs new file mode 100755 index 000000000..afcdbfd86 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterParamIndexStringRangeInverted.cs @@ -0,0 +1,112 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.logging; +using com.espertech.esper.compat.threading; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.filter +{ + /// + /// Index for filter parameter constants for the not range operators (range open/closed/half). + /// The implementation is based on the SortedMap implementation of TreeMap and stores only + /// expression parameter values of type StringRange. + /// + public sealed class FilterParamIndexStringRangeInverted : FilterParamIndexStringRangeBase + { + public FilterParamIndexStringRangeInverted(FilterSpecLookupable lookupable, IReaderWriterLock readWriteLock, FilterOperator filterOperator) + : base(lookupable, readWriteLock, filterOperator) + { + if (!(filterOperator.IsInvertedRangeOperator())) + { + throw new ArgumentException("Invalid filter operator " + filterOperator); + } + } + + public override void MatchEvent(EventBean theEvent, ICollection matches) + { + var objAttributeValue = Lookupable.Getter.Get(theEvent); + var returnValue = new Mutable(false); + + using (Instrument.With( + i => i.QFilterReverseIndex(this, objAttributeValue), + i => i.AFilterReverseIndex(returnValue.Value))) + { + if (objAttributeValue == null) + { + return; + } + + var attributeValue = (String)objAttributeValue; + + if (FilterOperator == FilterOperator.NOT_RANGE_CLOSED) // include all endpoints + { + foreach (var entry in Ranges) + { + if (String.Compare(entry.Key.Min, attributeValue, StringComparison.Ordinal) > 0 || + String.Compare(entry.Key.Max, attributeValue, StringComparison.Ordinal) < 0) + { + entry.Value.MatchEvent(theEvent, matches); + } + } + } + else if (FilterOperator == FilterOperator.NOT_RANGE_OPEN) + { + // include neither endpoint + foreach (var entry in Ranges) + { + if (String.Compare(entry.Key.Min, attributeValue, StringComparison.Ordinal) >= 0 || + String.Compare(entry.Key.Max, attributeValue, StringComparison.Ordinal) <= 0) + { + entry.Value.MatchEvent(theEvent, matches); + } + } + } + else if (FilterOperator == FilterOperator.NOT_RANGE_HALF_CLOSED) + // include high endpoint not low endpoint + { + foreach (var entry in Ranges) + { + if (String.Compare(entry.Key.Min, attributeValue, StringComparison.Ordinal) >= 0 || + String.Compare(entry.Key.Max, attributeValue, StringComparison.Ordinal) < 0) + { + entry.Value.MatchEvent(theEvent, matches); + } + } + } + else if (FilterOperator == FilterOperator.NOT_RANGE_HALF_OPEN) + // include low endpoint not high endpoint + { + foreach (var entry in Ranges) + { + if (String.Compare(entry.Key.Min, attributeValue, StringComparison.Ordinal) > 0 || + String.Compare(entry.Key.Max, attributeValue, StringComparison.Ordinal) <= 0) + { + entry.Value.MatchEvent(theEvent, matches); + } + } + } + else + { + throw new IllegalStateException("Invalid filter operator " + FilterOperator); + } + + returnValue.Value = null; + } + } + + private static readonly ILog Log = + LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + } +} diff --git a/NEsper.Core/NEsper.Core/filter/FilterService.cs b/NEsper.Core/NEsper.Core/filter/FilterService.cs new file mode 100755 index 000000000..c2611fdd8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterService.cs @@ -0,0 +1,76 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; + +namespace com.espertech.esper.filter +{ + /// + /// Interface for filtering events by event type and event property values. Allows adding and removing filters. + /// + /// Filters are defined by a and are associated with a + /// callback. Implementations may decide if the same filter callback can be + /// registered twice for different or some filter specifications. + /// + /// The performance of an implementation of this service is crucial in achieving a high overall event throughput. + /// + public interface FilterService : IDisposable + { + /// + /// Finds matching filters to the event passed in and collects their associated callback method. + /// + /// is the event to be matched against filters + /// is a collection that is populated via add method with any handles for matching filters + /// filter current version + long Evaluate(EventBean theEvent, ICollection matches); + + /// + /// Finds matching filters to the event passed in and collects their associated callback method, for a particular statement only + /// + /// is the event to be matched against filters + /// is a collection that is populated via add method with any handles for matching filters + /// statement for which to return results for + /// filter current version + long Evaluate(EventBean theEvent, ICollection matches, int statementId); + + /// + /// Add a filter for events as defined by the filter specification, and register a callback to be invoked upon evaluation of an event that matches the filter spec. + /// + /// is a specification of filter parameters, containsevent type information, event property values and operators + /// is the callback to be invoked when the filter matches an event + FilterServiceEntry Add(FilterValueSet filterValueSet, FilterHandle callback); + + /// + /// Remove a filter callback. + /// + /// is the callback to be removed + void Remove(FilterHandle callback, FilterServiceEntry filterServiceEntry); + + /// + /// Return a count of the number of events evaluated by this service. + /// + /// count of invocations of evaluate method + long NumEventsEvaluated { get; } + + /// + /// Reset the number of events evaluated + /// + void ResetStats(); + + /// + /// Returns filter version. + /// + /// filter version + long FiltersVersion { get; } + + void RemoveType(EventType type); + } +} diff --git a/NEsper.Core/NEsper.Core/filter/FilterServiceBase.cs b/NEsper.Core/NEsper.Core/filter/FilterServiceBase.cs new file mode 100755 index 000000000..3553f4b8e --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterServiceBase.cs @@ -0,0 +1,242 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Threading; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.compat.threading; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.util; + +namespace com.espertech.esper.filter +{ + /// + /// Implementation of the filter service interface. Does not allow the same filter callback + /// to be added more then once. + /// + public abstract class FilterServiceBase : FilterServiceSPI + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly FilterServiceGranularLockFactory _lockFactory; + private readonly EventTypeIndexBuilder _indexBuilder; + private readonly EventTypeIndex _eventTypeIndex; + private long _numEventsEvaluated = 0; + private long _filtersVersion = 1; + private readonly CopyOnWriteArraySet _filterServiceListeners; + + /// Constructor. + protected FilterServiceBase(FilterServiceGranularLockFactory lockFactory, bool allowIsolation) + { + _lockFactory = lockFactory; + _eventTypeIndex = new EventTypeIndex(lockFactory); + _indexBuilder = new EventTypeIndexBuilder(_eventTypeIndex, allowIsolation); + _filterServiceListeners = new CopyOnWriteArraySet(); + } + + public bool IsSupportsTakeApply + { + get { return _indexBuilder.IsSupportsTakeApply; } + } + + public long FiltersVersion + { + get { return _filtersVersion; } + } + + public void Dispose() + { + Log.Debug("Destroying filter service"); + _eventTypeIndex.Dispose(); + _indexBuilder.Destroy(); + } + + protected FilterServiceEntry AddInternal(FilterValueSet filterValueSet, FilterHandle filterCallback) + { + var entry = _indexBuilder.Add(filterValueSet, filterCallback, _lockFactory); + _filtersVersion++; + return entry; + } + + protected void RemoveInternal(FilterHandle filterCallback, FilterServiceEntry filterServiceEntry) + { + _indexBuilder.Remove(filterCallback, filterServiceEntry); + _filtersVersion++; + } + + protected long EvaluateInternal(EventBean theEvent, ICollection matches) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QFilter(theEvent); } + + long version = _filtersVersion; + Interlocked.Increment(ref _numEventsEvaluated); + //_numEventsEvaluated.IncrementAndGet(); + + // Finds all matching filters and return their callbacks. + RetryableMatchEvent(theEvent, matches); + + if ((AuditPath.IsAuditEnabled) && (_filterServiceListeners.IsNotEmpty())) + { + foreach (FilterServiceListener listener in _filterServiceListeners) + { + listener.Filtering(theEvent, matches, null); + } + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AFilter(matches); } + + return version; + } + + protected long EvaluateInternal(EventBean theEvent, ICollection matches, int statementId) + { + long version = _filtersVersion; + Interlocked.Increment(ref _numEventsEvaluated); + //_numEventsEvaluated.IncrementAndGet(); + + ArrayDeque allMatches = new ArrayDeque(); + + // Finds all matching filters + RetryableMatchEvent(theEvent, allMatches); + + // Add statement matches to collection passed + foreach (FilterHandle match in allMatches) + { + if (match.StatementId == statementId) + { + matches.Add(match); + } + } + + if ((AuditPath.IsAuditEnabled) && (_filterServiceListeners.IsNotEmpty())) + { + foreach (FilterServiceListener listener in _filterServiceListeners) + { + listener.Filtering(theEvent, matches, statementId); + } + } + + return version; + } + + public long NumEventsEvaluated + { + get { return Interlocked.Read(ref _numEventsEvaluated); } + } + + public void ResetStats() + { + Interlocked.Exchange(ref _numEventsEvaluated, 0); + } + + public void AddFilterServiceListener(FilterServiceListener filterServiceListener) + { + _filterServiceListeners.Add(filterServiceListener); + } + + public void RemoveFilterServiceListener(FilterServiceListener filterServiceListener) + { + _filterServiceListeners.Remove(filterServiceListener); + } + + protected FilterSet TakeInternal(ICollection statementIds) + { + _filtersVersion++; + return _indexBuilder.Take(statementIds); + } + + protected void ApplyInternal(FilterSet filterSet) + { + _filtersVersion++; + _indexBuilder.Apply(filterSet, _lockFactory); + } + + //@JmxGetter(name="NumFiltersApprox", description = "Number of filters managed (approximately)") + public int FilterCountApprox + { + get { return _eventTypeIndex.FilterCountApprox; } + } + + + public int CountTypes + { + get { return _eventTypeIndex.Count; } + } + + public void Init() + { + // no initialization required + } + + protected void RemoveTypeInternal(EventType type) + { + _eventTypeIndex.RemoveType(type); + } + + private void RetryableMatchEvent(EventBean theEvent, ICollection matches) + { + // Install lock backoff exception handler that retries the evaluation. + try + { + _eventTypeIndex.MatchEvent(theEvent, matches); + } + catch (FilterLockBackoffException ex) + { + // retry on lock back-off + // lock-backoff may occur when stateful evaluations take place such as bool expressions that are subqueries + // statements that contain subqueries in pattern filter expression can themselves modify filters, leading to a theoretically possible deadlock + long delayNs = 10; + while (true) + { + try + { + // yield + try + { + Thread.Sleep(0); + } + catch (ThreadInterruptedException e) + { + Thread.CurrentThread.Interrupt(); + } + + // delay + MicroThread.SleepNano(delayNs); + if (delayNs < 1000000000) + { + delayNs = delayNs * 2; + } + + // evaluate + matches.Clear(); + _eventTypeIndex.MatchEvent(theEvent, matches); + break; + } + catch (FilterLockBackoffException ex2) + { + // retried + } + } + } + } + + public abstract long Evaluate(EventBean theEvent, ICollection matches, int statementId); + public abstract long Evaluate(EventBean theEvent, ICollection matches); + public abstract FilterServiceEntry Add(FilterValueSet filterValueSet, FilterHandle callback); + public abstract void Remove(FilterHandle callback, FilterServiceEntry filterServiceEntry); + public abstract void RemoveType(EventType type); + public abstract FilterSet Take(ICollection statementId); + public abstract void Apply(FilterSet filterSet); + public abstract ILockable WriteLock { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/filter/FilterServiceEntry.cs b/NEsper.Core/NEsper.Core/filter/FilterServiceEntry.cs new file mode 100755 index 000000000..27e67b527 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterServiceEntry.cs @@ -0,0 +1,14 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.filter +{ + public interface FilterServiceEntry + { + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/filter/FilterServiceGranularLockFactory.cs b/NEsper.Core/NEsper.Core/filter/FilterServiceGranularLockFactory.cs new file mode 100755 index 000000000..8574f2b82 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterServiceGranularLockFactory.cs @@ -0,0 +1,49 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat.threading; + +namespace com.espertech.esper.filter +{ + public interface FilterServiceGranularLockFactory + { + IReaderWriterLock ObtainNew(); + } + + public class ProxyFilterServiceGranularLockFactory : FilterServiceGranularLockFactory + { + public Func ProcObtainNew { get; set; } + + /// + /// Initializes a new instance of the class. + /// + public ProxyFilterServiceGranularLockFactory() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The proc obtain new. + public ProxyFilterServiceGranularLockFactory(Func procObtainNew) + { + ProcObtainNew = procObtainNew; + } + + /// + /// Obtains the new. + /// + /// + public IReaderWriterLock ObtainNew() + { + return ProcObtainNew.Invoke(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/filter/FilterServiceGranularLockFactoryNone.cs b/NEsper.Core/NEsper.Core/filter/FilterServiceGranularLockFactoryNone.cs new file mode 100755 index 000000000..d25bc11b7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterServiceGranularLockFactoryNone.cs @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.compat.threading; + +namespace com.espertech.esper.filter +{ + public class FilterServiceGranularLockFactoryNone : FilterServiceGranularLockFactory + { + public static readonly FilterServiceGranularLockFactoryNone Instance = + new FilterServiceGranularLockFactoryNone(); + private static readonly IReaderWriterLock InstanceLock = + new VoidReaderWriterLock(); + + public IReaderWriterLock ObtainNew() + { + return InstanceLock; + } + } +} diff --git a/NEsper.Core/NEsper.Core/filter/FilterServiceGranularLockFactoryReentrant.cs b/NEsper.Core/NEsper.Core/filter/FilterServiceGranularLockFactoryReentrant.cs new file mode 100755 index 000000000..048033385 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterServiceGranularLockFactoryReentrant.cs @@ -0,0 +1,21 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.compat.threading; + +namespace com.espertech.esper.filter +{ + public class FilterServiceGranularLockFactoryReentrant : FilterServiceGranularLockFactory + { + public IReaderWriterLock ObtainNew() + { + return ReaderWriterLockManager.CreateDefaultLock(); + //return new SlimReaderWriterLock(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/filter/FilterServiceListener.cs b/NEsper.Core/NEsper.Core/filter/FilterServiceListener.cs new file mode 100755 index 000000000..691ca3ce7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterServiceListener.cs @@ -0,0 +1,24 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; + +namespace com.espertech.esper.filter +{ + /// Listener to filter activity. + public interface FilterServiceListener + { + /// Indicates an event being filtered. + /// event + /// matches found + /// optional statement id if for a statement + void Filtering(EventBean theEvent, ICollection matches, int? statementId); + } +} diff --git a/NEsper.Core/NEsper.Core/filter/FilterServiceLockCoarse.cs b/NEsper.Core/NEsper.Core/filter/FilterServiceLockCoarse.cs new file mode 100755 index 000000000..1b2567642 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterServiceLockCoarse.cs @@ -0,0 +1,88 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.threading; + +namespace com.espertech.esper.filter +{ + public sealed class FilterServiceLockCoarse : FilterServiceBase + { + private readonly IReaderWriterLock _iLock = + ReaderWriterLockManager.CreateDefaultLock(); + + public FilterServiceLockCoarse(bool allowIsolation) + : base(FilterServiceGranularLockFactoryNone.Instance, allowIsolation) + { + } + + public override ILockable WriteLock + { + get { return _iLock.WriteLock; } + } + + public override FilterSet Take(ICollection statementId) + { + using(_iLock.AcquireWriteLock()) + { + return base.TakeInternal(statementId); + } + } + + public override void Apply(FilterSet filterSet) + { + using(_iLock.AcquireWriteLock()) + { + base.ApplyInternal(filterSet); + } + } + + public override long Evaluate(EventBean theEvent, ICollection matches) + { + using (_iLock.AcquireReadLock()) + { + return base.EvaluateInternal(theEvent, matches); + } + } + + public override long Evaluate(EventBean theEvent, ICollection matches, int statementId) + { + using (_iLock.AcquireReadLock()) + { + return base.EvaluateInternal(theEvent, matches, statementId); + } + } + + public override FilterServiceEntry Add(FilterValueSet filterValueSet, FilterHandle callback) + { + using (_iLock.AcquireWriteLock()) + { + return base.AddInternal(filterValueSet, callback); + } + } + + public override void Remove(FilterHandle callback, FilterServiceEntry filterServiceEntry) + { + using (_iLock.AcquireWriteLock()) + { + base.RemoveInternal(callback, filterServiceEntry); + } + } + + public override void RemoveType(EventType type) + { + using (_iLock.AcquireWriteLock()) + { + base.RemoveTypeInternal(type); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/filter/FilterServiceLockFine.cs b/NEsper.Core/NEsper.Core/filter/FilterServiceLockFine.cs new file mode 100755 index 000000000..2fec4fa42 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterServiceLockFine.cs @@ -0,0 +1,79 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.threading; + +namespace com.espertech.esper.filter +{ + public sealed class FilterServiceLockFine : FilterServiceBase + { + private readonly IReaderWriterLock _iLock = + ReaderWriterLockManager.CreateDefaultLock(); + + public FilterServiceLockFine(bool allowIsolation) + : base(new FilterServiceGranularLockFactoryReentrant(), allowIsolation) + { + } + + public override ILockable WriteLock + { + get { return _iLock.WriteLock; } + } + + public override FilterSet Take(ICollection statementId) + { + using (_iLock.AcquireReadLock()) + { + return base.TakeInternal(statementId); + } + } + + public override void Apply(FilterSet filterSet) + { + using (_iLock.AcquireReadLock()) + { + base.ApplyInternal(filterSet); + } + } + + public override long Evaluate(EventBean theEvent, ICollection matches) + { + using (_iLock.AcquireReadLock()) + { + return base.EvaluateInternal(theEvent, matches); + } + } + + public override long Evaluate(EventBean theEvent, ICollection matches, int statementId) + { + using (_iLock.AcquireReadLock()) + { + return base.EvaluateInternal(theEvent, matches, statementId); + } + } + + public override FilterServiceEntry Add(FilterValueSet filterValueSet, FilterHandle callback) + { + return base.AddInternal(filterValueSet, callback); + } + + public override void Remove(FilterHandle callback, FilterServiceEntry filterServiceEntry) + { + base.RemoveInternal(callback, filterServiceEntry); + } + + public override void RemoveType(EventType type) + { + base.RemoveTypeInternal(type); + } + } +} diff --git a/NEsper.Core/NEsper.Core/filter/FilterServiceProvider.cs b/NEsper.Core/NEsper.Core/filter/FilterServiceProvider.cs new file mode 100755 index 000000000..b08791daf --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterServiceProvider.cs @@ -0,0 +1,33 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; + +namespace com.espertech.esper.filter +{ + /// + /// Static factory for implementations of the interface. + /// + public sealed class FilterServiceProvider + { + /// Creates an implementation of the FilterEvaluationService interface. + /// implementation + /// + public static FilterServiceSPI NewService(ConfigurationEngineDefaults.FilterServiceProfile filterServiceProfile, bool allowIsolation) + { + if (filterServiceProfile == ConfigurationEngineDefaults.FilterServiceProfile.READMOSTLY) + { + return new FilterServiceLockCoarse(allowIsolation); + } + else + { + return new FilterServiceLockFine(allowIsolation); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/filter/FilterServiceSPI.cs b/NEsper.Core/NEsper.Core/filter/FilterServiceSPI.cs new file mode 100755 index 000000000..a7f8bc11b --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterServiceSPI.cs @@ -0,0 +1,49 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.compat.threading; + +namespace com.espertech.esper.filter +{ + /// Service provider interface for filter service. + public interface FilterServiceSPI : FilterService + { + bool IsSupportsTakeApply { get; } + + /// Take a set of statements of out the active filters, returning a save-set of filters. + /// statement ids to remove + /// filters + FilterSet Take(ICollection statementId); + + /// Apply a set of previously taken filters. + /// to apply + void Apply(FilterSet filterSet); + + /// Add activity listener. + /// to add + void AddFilterServiceListener(FilterServiceListener filterServiceListener); + + /// Remove activity listener. + /// to remove + void RemoveFilterServiceListener(FilterServiceListener filterServiceListener); + + int FilterCountApprox { get; } + + int CountTypes { get; } + + ILockable WriteLock { get; } + + /// + /// Initialization is optional and provides a chance to preload things after statements are available. + /// + void Init(); + } +} diff --git a/NEsper.Core/NEsper.Core/filter/FilterSet.cs b/NEsper.Core/NEsper.Core/filter/FilterSet.cs new file mode 100755 index 000000000..ec1f2c1e4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterSet.cs @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.IO; + +namespace com.espertech.esper.filter +{ + /// Holder object for a set of filters for one or more statements. + public class FilterSet + { + /// Ctor. + /// set of filters + public FilterSet(IList filters) + { + Filters = filters; + } + + /// Returns the filters. + /// filters + public IList Filters { get; private set; } + + public override string ToString() + { + var filterTexts = new List(); + foreach (FilterSetEntry entry in Filters) + { + var writer = new StringWriter(); + entry.AppendTo(writer); + filterTexts.Add(writer.ToString()); + } + + filterTexts.Sort(); + + var writerX = new StringWriter(); + var delimiter = ""; + foreach (var filterText in filterTexts) + { + writerX.Write(delimiter); + writerX.Write(filterText); + delimiter = ","; + } + return writerX.ToString(); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/filter/FilterSetEntry.cs b/NEsper.Core/NEsper.Core/filter/FilterSetEntry.cs new file mode 100755 index 000000000..4045d0cce --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterSetEntry.cs @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.IO; + +namespace com.espertech.esper.filter +{ + /// + /// Record to a filter set taken from a . + /// + public class FilterSetEntry + { + /// Ctor. + /// handle + /// values + public FilterSetEntry(FilterHandle handle, + FilterValueSet filterValueSet) + { + Handle = handle; + FilterValueSet = filterValueSet; + } + + /// Returns the handle. + /// handle + public FilterHandle Handle { get; private set; } + + /// Returns filters. + /// filters + public FilterValueSet FilterValueSet { get; private set; } + + public void AppendTo(TextWriter writer) + { + FilterValueSet.AppendTo(writer); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/filter/FilterSpecCompiled.cs b/NEsper.Core/NEsper.Core/filter/FilterSpecCompiled.cs new file mode 100755 index 000000000..9379de910 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterSpecCompiled.cs @@ -0,0 +1,312 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.mgr; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.property; +using com.espertech.esper.pattern; + +namespace com.espertech.esper.filter +{ + /// + /// Contains the filter criteria to sift through events. The filter criteria are the event + /// class to look for and a set of parameters (attribute names, operators and constant/range + /// values). + /// + public sealed class FilterSpecCompiled + { + private static readonly FilterSpecParamComparator COMPARATOR_PARAMETERS = new FilterSpecParamComparator(); + + private readonly EventType _filterForEventType; + private readonly String _filterForEventTypeName; + private readonly FilterSpecParam[][] _parameters; + private readonly PropertyEvaluator _optionalPropertyEvaluator; + + /// Constructor - validates parameter list against event type, throws exception if invalid property names or mismatcing filter operators are found. + /// is the event type + /// is a list of filter parameters + /// is the name of the event type + /// optional if evaluating properties returned by filtered events + /// ArgumentException if validation invalid + public FilterSpecCompiled(EventType eventType, String eventTypeName, IList[] filterParameters, PropertyEvaluator optionalPropertyEvaluator) + { + _filterForEventType = eventType; + _filterForEventTypeName = eventTypeName; + _parameters = SortRemoveDups(filterParameters); + _optionalPropertyEvaluator = optionalPropertyEvaluator; + } + + /// + /// Returns type of event to filter for. + /// + /// event type + public EventType FilterForEventType + { + get { return _filterForEventType; } + } + + /// + /// Returns list of filter parameters. + /// + /// list of filter params + public FilterSpecParam[][] Parameters + { + get { return _parameters; } + } + + /// + /// Returns the event type name. + /// + /// event type name + public string FilterForEventTypeName + { + get { return _filterForEventTypeName; } + } + + /// + /// Return the evaluator for property value if any is attached, or none if none attached. + /// + /// property evaluator + public PropertyEvaluator OptionalPropertyEvaluator + { + get { return _optionalPropertyEvaluator; } + } + + /// + /// Returns the result event type of the filter specification. + /// + /// event type + public EventType ResultEventType + { + get + { + if (_optionalPropertyEvaluator != null) + { + return _optionalPropertyEvaluator.FragmentEventType; + } + else + { + return _filterForEventType; + } + } + } + + /// + /// Returns the values for the filter, using the supplied result events to ask filter parameters for the value to filter for. + /// + /// contains the result events to use for determining filter values + /// The agent instance context. + /// The addendum. + /// + /// filter values + /// + public FilterValueSet GetValueSet( + MatchedEventMap matchedEvents, + AgentInstanceContext agentInstanceContext, + FilterValueSetParam[][] addendum) + { + var valueList = new FilterValueSetParam[Parameters.Length][]; + for (int i = 0; i < Parameters.Length; i++) + { + valueList[i] = new FilterValueSetParam[Parameters[i].Length]; + PopulateValueSet(valueList[i], matchedEvents, agentInstanceContext, Parameters[i]); + } + + if (addendum != null) + { + valueList = ContextControllerAddendumUtil.MultiplyAddendum(addendum, valueList); + } + + return new FilterValueSetImpl(_filterForEventType, valueList); + } + + /// + /// Populates the value set. + /// + /// The value list. + /// The matched events. + /// The agent instance context. + /// The spec parameters. + private static void PopulateValueSet( + FilterValueSetParam[] valueList, + MatchedEventMap matchedEvents, + AgentInstanceContext agentInstanceContext, + FilterSpecParam[] specParams) + { + // Ask each filter specification parameter for the actual value to filter for + var count = 0; + foreach (var specParam in specParams) + { + var filterForValue = specParam.GetFilterValue(matchedEvents, agentInstanceContext); + + FilterValueSetParam valueParam = new FilterValueSetParamImpl(specParam.Lookupable, specParam.FilterOperator, filterForValue); + valueList[count] = valueParam; + count++; + } + } + + public override String ToString() + { + var buffer = new StringBuilder(); + buffer.Append("FilterSpecCompiled type=" + _filterForEventType); + buffer.Append(" parameters=" + _parameters.Render()); + return buffer.ToString(); + } + + public override bool Equals(Object obj) + { + if (this == obj) + { + return true; + } + + if (!(obj is FilterSpecCompiled)) + { + return false; + } + + var other = (FilterSpecCompiled) obj; + if (!EqualsTypeAndFilter(other)) + { + return false; + } + + if ((_optionalPropertyEvaluator == null) && (other._optionalPropertyEvaluator == null)) + { + return true; + } + if ((_optionalPropertyEvaluator != null) && (other._optionalPropertyEvaluator == null)) + { + return false; + } + if ((_optionalPropertyEvaluator == null) && (other._optionalPropertyEvaluator != null)) + { + return false; + } + + return _optionalPropertyEvaluator.CompareTo(other._optionalPropertyEvaluator); + } + + /// + /// Compares only the type and filter portion and not the property evaluation portion. + /// + /// filter to compare + /// + /// true if same + /// + public bool EqualsTypeAndFilter(FilterSpecCompiled other) + { + if (_filterForEventType != other._filterForEventType) + { + return false; + } + + if (_parameters.Length != other._parameters.Length) + { + return false; + } + + for (var i = 0; i < _parameters.Length; i++) + { + FilterSpecParam[] lineThis = this.Parameters[i]; + FilterSpecParam[] lineOther = other.Parameters[i]; + if (lineThis.Length != lineOther.Length) + { + return false; + } + + for (int j = 0; j < lineThis.Length; j++) + { + if (!Equals(lineThis[j], lineOther[j])) + { + return false; + } + } + } + return true; + } + + public override int GetHashCode() + { + int hashCode = FilterForEventType.GetHashCode(); + + foreach (FilterSpecParam[] paramLine in Parameters) { + foreach (FilterSpecParam param in paramLine) { + hashCode ^= 31 * param.GetHashCode(); + } + } + + return hashCode; + } + + public int GetFilterSpecIndexAmongAll(FilterSpecCompiled[] filterSpecAll) + { + for (int i = 0; i < filterSpecAll.Length; i++) + { + if (ReferenceEquals(this, filterSpecAll[i])) + { + return i; + } + } + throw new EPException("Failed to find find filter spec among list of known filters"); + } + + public static FilterSpecParam[][] SortRemoveDups(IList[] parameters) + { + var processed = new FilterSpecParam[parameters.Length][]; + for (int i = 0; i < parameters.Length; i++) + { + processed[i] = SortRemoveDups(parameters[i]); + } + return processed; + } + + internal static FilterSpecParam[] SortRemoveDups(IList parameters) + { + if (parameters.IsEmpty()) { + return FilterSpecParam.EMPTY_PARAM_ARRAY; + } + + if (parameters.Count == 1) { + return new FilterSpecParam[] {parameters[0]}; + } + + var result = new ArrayDeque(); + var map = new SortedDictionary>(COMPARATOR_PARAMETERS); + foreach (var parameter in parameters) { + + var list = map.Get(parameter.FilterOperator); + if (list == null) { + list = new List(); + map.Put(parameter.FilterOperator, list); + } + + var hasDuplicate = list.Any(existing => existing.Lookupable.Equals(parameter.Lookupable)); + if (hasDuplicate) { + continue; + } + + list.Add(parameter); + } + + foreach (var entry in map) { + result.AddAll(entry.Value); + } + return FilterSpecParam.ToArray(result); + } + } +} diff --git a/NEsper.Core/NEsper.Core/filter/FilterSpecCompiler.cs b/NEsper.Core/NEsper.Core/filter/FilterSpecCompiler.cs new file mode 100755 index 000000000..8ab594814 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterSpecCompiler.cs @@ -0,0 +1,441 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.core.start; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.subquery; +using com.espertech.esper.epl.expression.visitor; +using com.espertech.esper.epl.named; +using com.espertech.esper.epl.property; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.epl.util; +using com.espertech.esper.epl.variable; +using com.espertech.esper.events; +using com.espertech.esper.schedule; +using com.espertech.esper.script; +using com.espertech.esper.view; + +namespace com.espertech.esper.filter +{ + /// + /// Helper to compile (validate and optimize) filter expressions as used in pattern and filter-based streams. + /// + public class FilterSpecCompiler + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// Assigned for filter parameters that are based on boolean expression and not on + /// any particular property name. + /// Keeping this artificial property name is a simplification as optimized filter parameters + /// generally keep a property name. + /// + public const string PROPERTY_NAME_BOOLEAN_EXPRESSION = ".boolean_expression"; + + /// + /// Factory method for compiling filter expressions into a filter specification + /// for use with filter service. + /// + /// is the filtered-out event type + /// is the name of the event type + /// is a list of filter expressions + /// specification for evaluating properties + /// is a map of stream names (tags) and event types available + /// is a map of name tags and event type per tag for repeat-expressions that generate an array of events + /// is used to set rules for resolving properties + /// the stream name, if provided + /// context for statement + /// The assigned type number stack. + /// + /// compiled filter specification + /// + /// ExprValidationException if the expression or type validations failed + public static FilterSpecCompiled MakeFilterSpec( + EventType eventType, + string eventTypeName, + IList filterExpessions, + PropertyEvalSpec optionalPropertyEvalSpec, + IDictionary> taggedEventTypes, + IDictionary> arrayEventTypes, + StreamTypeService streamTypeService, + string optionalStreamName, + StatementContext statementContext, + ICollection assignedTypeNumberStack) + { + // Validate all nodes, make sure each returns a boolean and types are good; + // Also decompose all AND super nodes into individual expressions + var validatedNodes = ValidateAllowSubquery(ExprNodeOrigin.FILTER, filterExpessions, streamTypeService, statementContext, taggedEventTypes, arrayEventTypes); + return Build(validatedNodes, eventType, eventTypeName, optionalPropertyEvalSpec, taggedEventTypes, arrayEventTypes, streamTypeService, optionalStreamName, statementContext, assignedTypeNumberStack); + } + + public static FilterSpecCompiled Build( + IList validatedNodes, + EventType eventType, + string eventTypeName, + PropertyEvalSpec optionalPropertyEvalSpec, + IDictionary> taggedEventTypes, + IDictionary> arrayEventTypes, + StreamTypeService streamTypeService, + string optionalStreamName, + StatementContext stmtContext, + ICollection assignedTypeNumberStack) + { + var evaluatorContextStmt = new ExprEvaluatorContextStatement(stmtContext, false); + + return BuildNoStmtCtx(validatedNodes, eventType, eventTypeName, optionalPropertyEvalSpec, taggedEventTypes, arrayEventTypes, streamTypeService, + optionalStreamName, assignedTypeNumberStack, + evaluatorContextStmt, + stmtContext.StatementId, + stmtContext.StatementName, + stmtContext.Annotations, + stmtContext.ContextDescriptor, + stmtContext.EngineImportService, + stmtContext.EventAdapterService, + stmtContext.FilterBooleanExpressionFactory, + stmtContext.TimeProvider, + stmtContext.VariableService, + stmtContext.ScriptingService, + stmtContext.TableService, + stmtContext.ConfigSnapshot, + stmtContext.NamedWindowMgmtService, + stmtContext.StatementExtensionServicesContext); + } + + public static FilterSpecCompiled BuildNoStmtCtx( + IList validatedFilterNodes, + EventType eventType, + string eventTypeName, + PropertyEvalSpec optionalPropertyEvalSpec, + IDictionary> taggedEventTypes, + IDictionary> arrayEventTypes, + StreamTypeService streamTypeService, + string optionalStreamName, + ICollection assignedTypeNumberStack, + ExprEvaluatorContext exprEvaluatorContext, + int statementId, + string statementName, + Attribute[] annotations, + ContextDescriptor contextDescriptor, + EngineImportService engineImportService, + EventAdapterService eventAdapterService, + FilterBooleanExpressionFactory filterBooleanExpressionFactory, + TimeProvider timeProvider, + VariableService variableService, + ScriptingService scriptingService, + TableService tableService, + ConfigurationInformation configurationInformation, + NamedWindowMgmtService namedWindowMgmtService, + StatementExtensionSvcContext statementExtensionSvcContext) + { + var args = new FilterSpecCompilerArgs( + taggedEventTypes, + arrayEventTypes, + exprEvaluatorContext, + statementName, + statementId, + streamTypeService, + engineImportService, + timeProvider, + variableService, + tableService, + eventAdapterService, + filterBooleanExpressionFactory, + scriptingService, + annotations, + contextDescriptor, + configurationInformation, + statementExtensionSvcContext); + var parameters = FilterSpecCompilerPlanner.PlanFilterParameters(validatedFilterNodes, args); + + PropertyEvaluator optionalPropertyEvaluator = null; + if (optionalPropertyEvalSpec != null) + { + optionalPropertyEvaluator = PropertyEvaluatorFactory.MakeEvaluator( + optionalPropertyEvalSpec, + eventType, + optionalStreamName, + eventAdapterService, + engineImportService, + timeProvider, + variableService, + scriptingService, + tableService, + streamTypeService.EngineURIQualifier, + statementId, + statementName, + annotations, + assignedTypeNumberStack, + configurationInformation, + namedWindowMgmtService, + statementExtensionSvcContext); + } + + var spec = new FilterSpecCompiled(eventType, eventTypeName, parameters, optionalPropertyEvaluator); + + if (Log.IsDebugEnabled) + { + Log.Debug(".makeFilterSpec spec=" + spec); + } + + return spec; + } + + /// + /// Validates expression nodes and returns a list of validated nodes. + /// + /// The expr node origin. + /// is the nodes to validate + /// is provding type information for each stream + /// context + /// pattern tagged types + /// @return list of validated expression nodes + /// + /// expr nodes + /// + /// + /// Failed to validate + EPStatementStartMethodHelperSubselect.GetSubqueryInfoText(count, subselect) + : + ex.Message + /// or + /// Filter expression not returning a boolean value: ' + ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(validated) + ' + /// + /// ExprValidationException for validation errors + public static IList ValidateAllowSubquery( + ExprNodeOrigin exprNodeOrigin, + IList exprNodes, + StreamTypeService streamTypeService, + StatementContext statementContext, + IDictionary> taggedEventTypes, + IDictionary> arrayEventTypes) + { + IList validatedNodes = new List(); + + var evaluatorContextStmt = new ExprEvaluatorContextStatement(statementContext, false); + var validationContext = new ExprValidationContext( + streamTypeService, + statementContext.EngineImportService, + statementContext.StatementExtensionServicesContext, null, + statementContext.TimeProvider, + statementContext.VariableService, + statementContext.TableService, + evaluatorContextStmt, + statementContext.EventAdapterService, + statementContext.StatementName, + statementContext.StatementId, + statementContext.Annotations, + statementContext.ContextDescriptor, + statementContext.ScriptingService, + false, false, true, false, null, true); + foreach (var node in exprNodes) + { + // Determine subselects + var visitor = new ExprNodeSubselectDeclaredDotVisitor(); + node.Accept(visitor); + + // Compile subselects + if (!visitor.Subselects.IsEmpty()) + { + + // The outer event type is the filtered-type itself + var subselectStreamNumber = 2048; + var count = -1; + foreach (var subselect in visitor.Subselects) + { + count++; + subselectStreamNumber++; + try + { + HandleSubselectSelectClauses(subselectStreamNumber, statementContext, subselect, + streamTypeService.EventTypes[0], streamTypeService.StreamNames[0], streamTypeService.StreamNames[0], + taggedEventTypes, arrayEventTypes); + } + catch (ExprValidationException ex) + { + throw new ExprValidationException("Failed to validate " + EPStatementStartMethodHelperSubselect.GetSubqueryInfoText(count, subselect) + ": " + ex.Message, ex); + } + } + } + + var validated = ExprNodeUtility.GetValidatedSubtree(exprNodeOrigin, node, validationContext); + validatedNodes.Add(validated); + + if ((validated.ExprEvaluator.ReturnType != typeof(bool?)) && ((validated.ExprEvaluator.ReturnType != typeof(bool)))) + { + throw new ExprValidationException("Filter expression not returning a boolean value: '" + ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(validated) + "'"); + } + } + + return validatedNodes; + } + + private static void HandleSubselectSelectClauses( + int subselectStreamNumber, + StatementContext statementContext, + ExprSubselectNode subselect, + EventType outerEventType, + string outerEventTypeName, + string outerStreamName, + IDictionary> taggedEventTypes, + IDictionary> arrayEventTypes) + { + var statementSpec = subselect.StatementSpecCompiled; + var filterStreamSpec = statementSpec.StreamSpecs[0]; + + ViewFactoryChain viewFactoryChain; + string subselecteventTypeName = null; + + // construct view factory chain + try + { + if (statementSpec.StreamSpecs[0] is FilterStreamSpecCompiled) + { + var filterStreamSpecCompiled = (FilterStreamSpecCompiled)statementSpec.StreamSpecs[0]; + subselecteventTypeName = filterStreamSpecCompiled.FilterSpec.FilterForEventTypeName; + + // A child view is required to limit the stream + if (filterStreamSpec.ViewSpecs.Length == 0) + { + throw new ExprValidationException("Subqueries require one or more views to limit the stream, consider declaring a length or time window"); + } + + // Register filter, create view factories + viewFactoryChain = statementContext.ViewService.CreateFactories(subselectStreamNumber, filterStreamSpecCompiled.FilterSpec.ResultEventType, filterStreamSpec.ViewSpecs, filterStreamSpec.Options, statementContext, true, subselect.SubselectNumber); + subselect.RawEventType = viewFactoryChain.EventType; + } + else + { + var namedSpec = (NamedWindowConsumerStreamSpec)statementSpec.StreamSpecs[0]; + var processor = statementContext.NamedWindowMgmtService.GetProcessor(namedSpec.WindowName); + viewFactoryChain = statementContext.ViewService.CreateFactories(0, processor.NamedWindowType, namedSpec.ViewSpecs, namedSpec.Options, statementContext, true, subselect.SubselectNumber); + subselecteventTypeName = namedSpec.WindowName; + EPLValidationUtil.ValidateContextName(false, processor.NamedWindowName, processor.ContextName, statementContext.ContextName, true); + subselect.RawEventType = processor.NamedWindowType; + } + } + catch (ViewProcessingException ex) + { + throw new ExprValidationException("Error validating subexpression: " + ex.Message, ex); + } + + // the final event type + var eventType = viewFactoryChain.EventType; + + // determine a stream name unless one was supplied + var subexpressionStreamName = filterStreamSpec.OptionalStreamName; + if (subexpressionStreamName == null) + { + subexpressionStreamName = "$subselect_" + subselectStreamNumber; + } + + // Named windows don't allow data views + if (filterStreamSpec is NamedWindowConsumerStreamSpec) + { + EPStatementStartMethodHelperValidate.ValidateNoDataWindowOnNamedWindow(viewFactoryChain.FactoryChain); + } + + // Streams event types are the original stream types with the stream zero the subselect stream + var namesAndTypes = new LinkedHashMap>(); + namesAndTypes.Put(subexpressionStreamName, new Pair(eventType, subselecteventTypeName)); + namesAndTypes.Put(outerStreamName, new Pair(outerEventType, outerEventTypeName)); + if (taggedEventTypes != null) + { + foreach (KeyValuePair> entry in taggedEventTypes) + { + namesAndTypes.Put(entry.Key, new Pair(entry.Value.First, entry.Value.Second)); + } + } + if (arrayEventTypes != null) + { + foreach (KeyValuePair> entry in arrayEventTypes) + { + namesAndTypes.Put(entry.Key, new Pair(entry.Value.First, entry.Value.Second)); + } + } + StreamTypeService subselectTypeService = new StreamTypeServiceImpl(namesAndTypes, statementContext.EngineURI, true, true); + var viewResourceDelegateSubselect = new ViewResourceDelegateUnverified(); + subselect.FilterSubqueryStreamTypes = subselectTypeService; + + // Validate select expression + var selectClauseSpec = subselect.StatementSpecCompiled.SelectClauseSpec; + if (selectClauseSpec.SelectExprList.Length > 0) + { + if (selectClauseSpec.SelectExprList.Length > 1) + { + throw new ExprValidationException("Subquery multi-column select is not allowed in this context."); + } + + var element = selectClauseSpec.SelectExprList[0]; + if (element is SelectClauseExprCompiledSpec) + { + // validate + var compiled = (SelectClauseExprCompiledSpec)element; + var selectExpression = compiled.SelectExpression; + var evaluatorContextStmt = new ExprEvaluatorContextStatement(statementContext, false); + var validationContext = new ExprValidationContext( + subselectTypeService, + statementContext.EngineImportService, + statementContext.StatementExtensionServicesContext, + viewResourceDelegateSubselect, + statementContext.SchedulingService, + statementContext.VariableService, + statementContext.TableService, + evaluatorContextStmt, + statementContext.EventAdapterService, + statementContext.StatementName, + statementContext.StatementId, + statementContext.Annotations, + statementContext.ContextDescriptor, + statementContext.ScriptingService, + false, false, true, false, null, false); + selectExpression = ExprNodeUtility.GetValidatedSubtree(ExprNodeOrigin.SUBQUERYSELECT, selectExpression, validationContext); + subselect.SelectClause = new ExprNode[] { selectExpression }; + subselect.SelectAsNames = new string[] { compiled.AssignedName }; + + // handle aggregation + var aggExprNodes = new List(); + ExprAggregateNodeUtil.GetAggregatesBottomUp(selectExpression, aggExprNodes); + if (aggExprNodes.Count > 0) + { + // Other stream properties, if there is aggregation, cannot be under aggregation. + foreach (var aggNode in aggExprNodes) + { + var propertiesNodesAggregated = ExprNodeUtility.GetExpressionProperties(aggNode, true); + foreach (var pair in propertiesNodesAggregated) + { + if (pair.First != 0) + { + throw new ExprValidationException("Subselect aggregation function cannot aggregate across correlated properties"); + } + } + } + + // This stream (stream 0) properties must either all be under aggregation, or all not be. + var propertiesNotAggregated = ExprNodeUtility.GetExpressionProperties(selectExpression, false); + foreach (var pair in propertiesNotAggregated) + { + if (pair.First == 0) + { + throw new ExprValidationException("Subselect properties must all be within aggregation functions"); + } + } + } + } + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/filter/FilterSpecCompilerArgs.cs b/NEsper.Core/NEsper.Core/filter/FilterSpecCompilerArgs.cs new file mode 100755 index 000000000..153085ef8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterSpecCompilerArgs.cs @@ -0,0 +1,85 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.epl.variable; +using com.espertech.esper.events; +using com.espertech.esper.schedule; +using com.espertech.esper.script; + +namespace com.espertech.esper.filter +{ + public class FilterSpecCompilerArgs + { + public readonly Attribute[] Annotations; + public readonly IDictionary> ArrayEventTypes; + public readonly ConfigurationInformation ConfigurationInformation; + public readonly ContextDescriptor ContextDescriptor; + public readonly EventAdapterService EventAdapterService; + public readonly FilterBooleanExpressionFactory FilterBooleanExpressionFactory; + public readonly ExprEvaluatorContext ExprEvaluatorContext; + public readonly EngineImportService EngineImportService; + public readonly ScriptingService ScriptingService; + public readonly int StatementId; + public readonly string StatementName; + public readonly StreamTypeService StreamTypeService; + public readonly TableService TableService; + public readonly IDictionary> TaggedEventTypes; + public readonly TimeProvider TimeProvider; + public readonly VariableService VariableService; + public readonly StatementExtensionSvcContext StatementExtensionSvcContext; + + public FilterSpecCompilerArgs( + IDictionary> taggedEventTypes, + IDictionary> arrayEventTypes, + ExprEvaluatorContext exprEvaluatorContext, + string statementName, + int statementId, + StreamTypeService streamTypeService, + EngineImportService engineImportService, + TimeProvider timeProvider, + VariableService variableService, + TableService tableService, + EventAdapterService eventAdapterService, + FilterBooleanExpressionFactory filterBooleanExpressionFactory, + ScriptingService scriptingService, + Attribute[] annotations, + ContextDescriptor contextDescriptor, + ConfigurationInformation configurationInformation, + StatementExtensionSvcContext statementExtensionSvcContext) + { + TaggedEventTypes = taggedEventTypes; + ArrayEventTypes = arrayEventTypes; + ExprEvaluatorContext = exprEvaluatorContext; + StatementName = statementName; + StatementId = statementId; + StreamTypeService = streamTypeService; + EngineImportService = engineImportService; + TimeProvider = timeProvider; + VariableService = variableService; + TableService = tableService; + EventAdapterService = eventAdapterService; + FilterBooleanExpressionFactory = filterBooleanExpressionFactory; + ScriptingService = scriptingService; + Annotations = annotations; + ContextDescriptor = contextDescriptor; + ConfigurationInformation = configurationInformation; + StatementExtensionSvcContext = statementExtensionSvcContext; + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/filter/FilterSpecCompilerConsolidateUtil.cs b/NEsper.Core/NEsper.Core/filter/FilterSpecCompilerConsolidateUtil.cs new file mode 100755 index 000000000..b5bcbb8f3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterSpecCompilerConsolidateUtil.cs @@ -0,0 +1,125 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.util; + +namespace com.espertech.esper.filter +{ + /// + /// Helper to compile (validate and optimize) filter expressions as used in pattern and filter-based streams. + /// + public class FilterSpecCompilerConsolidateUtil + { + public static void Consolidate(FilterParamExprMap filterParamExprMap, string statementName) + { + // consolidate or place in a boolean expression (by removing filter spec param from the map) + // any filter parameter that feature the same property name and filter operator, + // i.e. we are looking for "a!=5 and a!=6" to transform to "a not in (5,6)" which can match faster + // considering that "a not in (5,6) and a not in (7,8)" is "a not in (5, 6, 7, 8)" therefore + // we need to consolidate until there is no more work to do + IDictionary, IList> mapOfParams = new Dictionary, IList>(); + + bool haveConsolidated; + do + { + haveConsolidated = false; + mapOfParams.Clear(); + + // sort into buckets of propertyName + filterOperator combination + foreach (var currentParam in filterParamExprMap.FilterParams) + { + var lookupable = currentParam.Lookupable; + var op = currentParam.FilterOperator; + var key = new Pair(lookupable, op); + + var existingParam = mapOfParams.Get(key); + if (existingParam == null) + { + existingParam = new List(); + mapOfParams.Put(key, existingParam); + } + existingParam.Add(currentParam); + } + + foreach (var entry in mapOfParams.Values) + { + if (entry.Count > 1) + { + haveConsolidated = true; + Consolidate(entry, filterParamExprMap, statementName); + } + } + } + while(haveConsolidated); + } + + // remove duplicate propertyName + filterOperator items making a judgement to optimize or simply remove the optimized form + private static void Consolidate(IList items, FilterParamExprMap filterParamExprMap, string statementName) + { + var op = items[0].FilterOperator; + if (op == FilterOperator.NOT_EQUAL) + { + HandleConsolidateNotEqual(items, filterParamExprMap, statementName); + } + else + { + // for all others we simple remove the second optimized form (filter param with same prop name and filter op) + // and thus the boolean expression that started this is included + for (var i = 1; i < items.Count; i++) + { + filterParamExprMap.RemoveValue(items[i]); + } + } + } + + // consolidate "val != 3 and val != 4 and val != 5" + // to "val not in (3, 4, 5)" + private static void HandleConsolidateNotEqual(IList parameters, FilterParamExprMap filterParamExprMap, string statementName) + { + IList values = new List(); + + ExprNode lastNotEqualsExprNode = null; + foreach (var param in parameters) + { + if (param is FilterSpecParamConstant) + { + var constantParam = (FilterSpecParamConstant) param; + var constant = constantParam.FilterConstant; + values.Add(new InSetOfValuesConstant(constant)); + } + else if (param is FilterSpecParamEventProp) + { + var eventProp = (FilterSpecParamEventProp) param; + values.Add(new InSetOfValuesEventProp(eventProp.ResultEventAsName, eventProp.ResultEventProperty, + eventProp.IsMustCoerce, TypeHelper.GetBoxedType(eventProp.CoercionType))); + } + else if (param is FilterSpecParamEventPropIndexed) + { + var eventProp = (FilterSpecParamEventPropIndexed) param; + values.Add(new InSetOfValuesEventPropIndexed(eventProp.ResultEventAsName, eventProp.ResultEventIndex, eventProp.ResultEventProperty, + eventProp.IsMustCoerce, TypeHelper.GetBoxedType(eventProp.CoercionType), statementName)); + } + else + { + throw new ArgumentException("Unknown filter parameter:" + param.ToString()); + } + + lastNotEqualsExprNode = filterParamExprMap.RemoveEntry(param); + } + + var paramIn = new FilterSpecParamIn(parameters[0].Lookupable, FilterOperator.NOT_IN_LIST_OF_VALUES, values); + filterParamExprMap.Put(lastNotEqualsExprNode, paramIn); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/filter/FilterSpecCompilerMakeParamUtil.cs b/NEsper.Core/NEsper.Core/filter/FilterSpecCompilerMakeParamUtil.cs new file mode 100755 index 000000000..3cb70f94a --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterSpecCompilerMakeParamUtil.cs @@ -0,0 +1,723 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.funcs; +using com.espertech.esper.epl.expression.ops; +using com.espertech.esper.events.property; +using com.espertech.esper.type; +using com.espertech.esper.util; + +namespace com.espertech.esper.filter +{ + /// + /// Helper to compile (validate and optimize) filter expressions as used in pattern and filter-based streams. + /// + public sealed class FilterSpecCompilerMakeParamUtil + { + /// + /// For a given expression determine if this is optimizable and create the filter parameter + /// representing the expression, or null if not optimizable. + /// + /// is the expression to look at + /// event types that provide array values + /// statement name + /// context + /// if the expression is invalid + /// filter parameter representing the expression, or null + internal static FilterSpecParam MakeFilterParam( + ExprNode constituent, + IDictionary> arrayEventTypes, + ExprEvaluatorContext exprEvaluatorContext, + string statementName) + { + // Is this expresson node a simple compare, i.e. a=5 or b<4; these can be indexed + if ((constituent is ExprEqualsNode) || + (constituent is ExprRelationalOpNode)) + { + var param = HandleEqualsAndRelOp( + constituent, arrayEventTypes, exprEvaluatorContext, statementName); + if (param != null) + { + return param; + } + } + + constituent = RewriteOrToInIfApplicable(constituent); + + // Is this expression node a simple compare, i.e. a=5 or b<4; these can be indexed + if (constituent is ExprInNode) + { + var param = HandleInSetNode( + (ExprInNode) constituent, arrayEventTypes, exprEvaluatorContext, statementName); + if (param != null) + { + return param; + } + } + + if (constituent is ExprBetweenNode) + { + var param = HandleRangeNode( + (ExprBetweenNode) constituent, arrayEventTypes, exprEvaluatorContext, statementName); + if (param != null) + { + return param; + } + } + + if (constituent is ExprPlugInSingleRowNode) + { + var param = HandlePlugInSingleRow((ExprPlugInSingleRowNode) constituent); + if (param != null) + { + return param; + } + } + + return null; + } + + public static ExprNode RewriteOrToInIfApplicable(ExprNode constituent) + { + if (!(constituent is ExprOrNode) || constituent.ChildNodes.Count < 2) + { + return constituent; + } + + // check eligibility + var childNodes = constituent.ChildNodes; + foreach (var child in childNodes) + { + if (!(child is ExprEqualsNode)) + { + return constituent; + } + var equalsNode = (ExprEqualsNode) child; + if (equalsNode.IsIs || equalsNode.IsNotEquals) + { + return constituent; + } + } + + // find common-expression node + ExprNode commonExpressionNode; + var lhs = childNodes[0].ChildNodes[0]; + var rhs = childNodes[0].ChildNodes[1]; + if (ExprNodeUtility.DeepEquals(lhs, rhs)) + { + return constituent; + } + if (IsExprExistsInAllEqualsChildNodes(childNodes, lhs)) + { + commonExpressionNode = lhs; + } + else if (IsExprExistsInAllEqualsChildNodes(childNodes, rhs)) + { + commonExpressionNode = rhs; + } + else + { + return constituent; + } + + // build node + var @in = new ExprInNodeImpl(false); + @in.AddChildNode(commonExpressionNode); + for (var i = 0; i < constituent.ChildNodes.Count; i++) + { + var child = constituent.ChildNodes[i]; + var nodeindex = ExprNodeUtility.DeepEquals(commonExpressionNode, childNodes[i].ChildNodes[0]) ? 1 : 0; + @in.AddChildNode(child.ChildNodes[nodeindex]); + } + + // validate + try + { + @in.ValidateWithoutContext(); + } + catch (ExprValidationException) + { + return constituent; + } + + return @in; + } + + private static FilterSpecParam HandlePlugInSingleRow(ExprPlugInSingleRowNode constituent) + { + if (constituent.ExprEvaluator.ReturnType.GetBoxedType() != typeof (bool?)) + { + return null; + } + if (!constituent.IsFilterLookupEligible) + { + return null; + } + var lookupable = constituent.FilterLookupable; + return new FilterSpecParamConstant(lookupable, FilterOperator.EQUAL, true); + } + + private static FilterSpecParam HandleRangeNode( + ExprBetweenNode betweenNode, + IDictionary> arrayEventTypes, + ExprEvaluatorContext exprEvaluatorContext, + string statementName) + { + var left = betweenNode.ChildNodes[0]; + if (left is ExprFilterOptimizableNode) + { + var filterOptimizableNode = (ExprFilterOptimizableNode) left; + var lookupable = filterOptimizableNode.FilterLookupable; + var op = FilterOperatorExtensions.ParseRangeOperator( + betweenNode.IsLowEndpointIncluded, betweenNode.IsHighEndpointIncluded, + betweenNode.IsNotBetween); + + var low = HandleRangeNodeEndpoint( + betweenNode.ChildNodes[1], arrayEventTypes, exprEvaluatorContext, statementName); + var high = HandleRangeNodeEndpoint( + betweenNode.ChildNodes[2], arrayEventTypes, exprEvaluatorContext, statementName); + + if ((low != null) && (high != null)) + { + return new FilterSpecParamRange(lookupable, op, low, high); + } + } + return null; + } + + private static FilterSpecParamRangeValue HandleRangeNodeEndpoint( + ExprNode endpoint, + IDictionary> arrayEventTypes, + ExprEvaluatorContext exprEvaluatorContext, + string statementName) + { + // constant + if (ExprNodeUtility.IsConstantValueExpr(endpoint)) + { + var node = (ExprConstantNode) endpoint; + var value = node.GetConstantValue(exprEvaluatorContext); + if (value == null) + { + return null; + } + if (value is string) + { + return new RangeValueString((string) value); + } + else + { + return new RangeValueDouble(value.AsDouble()); + } + } + + if (endpoint is ExprContextPropertyNode) + { + var node = (ExprContextPropertyNode) endpoint; + return new RangeValueContextProp(node.Getter); + } + + // or property + if (endpoint is ExprIdentNode) + { + var identNodeInner = (ExprIdentNode) endpoint; + if (identNodeInner.StreamId == 0) + { + return null; + } + + if (arrayEventTypes != null && !arrayEventTypes.IsEmpty() && + arrayEventTypes.ContainsKey(identNodeInner.ResolvedStreamName)) + { + var indexAndProp = GetStreamIndex(identNodeInner.ResolvedPropertyName); + return new RangeValueEventPropIndexed( + identNodeInner.ResolvedStreamName, indexAndProp.First, indexAndProp.Second, statementName); + } + else + { + return new RangeValueEventProp( + identNodeInner.ResolvedStreamName, identNodeInner.ResolvedPropertyName); + } + } + + return null; + } + + private static FilterSpecParam HandleInSetNode( + ExprInNode constituent, + IDictionary> arrayEventTypes, + ExprEvaluatorContext exprEvaluatorContext, + string statementName) + { + var left = constituent.ChildNodes[0]; + if (!(left is ExprFilterOptimizableNode)) + { + return null; + } + + var filterOptimizableNode = (ExprFilterOptimizableNode) left; + var lookupable = filterOptimizableNode.FilterLookupable; + var op = FilterOperator.IN_LIST_OF_VALUES; + if (constituent.IsNotIn) + { + op = FilterOperator.NOT_IN_LIST_OF_VALUES; + } + + var expectedNumberOfConstants = constituent.ChildNodes.Count - 1; + var listofValues = new List(); + IEnumerator it = constituent.ChildNodes.GetEnumerator(); + it.MoveNext(); // ignore the first node as it's the identifier + while (it.MoveNext()) + { + ExprNode subNode = it.Current; + if (ExprNodeUtility.IsConstantValueExpr(subNode)) + { + var constantNode = (ExprConstantNode) subNode; + var constant = constantNode.GetConstantValue(exprEvaluatorContext); + + if (constant != null && !constant.GetType().IsArray) + { + var constantType = constant.GetType(); + if (constantType.IsGenericCollection()) + return null; + if (constantType.IsGenericDictionary()) + return null; + } + + var constantAsArray = constant as Array; + if (constantAsArray != null) + { + for (var i = 0; i < constantAsArray.Length; i++) + { + var arrayElement = constantAsArray.GetValue(i); + var arrayElementCoerced = HandleConstantsCoercion(lookupable, arrayElement); + listofValues.Add(new InSetOfValuesConstant(arrayElementCoerced)); + if (i > 0) + { + expectedNumberOfConstants++; + } + } + } + else + { + constant = HandleConstantsCoercion(lookupable, constant); + listofValues.Add(new InSetOfValuesConstant(constant)); + } + } + if (subNode is ExprContextPropertyNode) + { + var contextPropertyNode = (ExprContextPropertyNode) subNode; + var returnType = contextPropertyNode.ReturnType; + Coercer coercer; + Type coercerType; + + if (returnType.IsCollectionMapOrArray()) + { + CheckArrayCoercion(returnType, lookupable.ReturnType, lookupable.Expression); + coercer = null; + coercerType = returnType; + } + else + { + coercer = GetNumberCoercer( + left.ExprEvaluator.ReturnType, contextPropertyNode.ReturnType, lookupable.Expression, out coercerType); + } + var finalReturnType = coercerType; + listofValues.Add( + new InSetOfValuesContextProp( + contextPropertyNode.PropertyName, contextPropertyNode.Getter, coercer, finalReturnType)); + } + if (subNode is ExprIdentNode) + { + var identNodeInner = (ExprIdentNode) subNode; + if (identNodeInner.StreamId == 0) + { + break; // for same event evals use the bool expression, via count compare failing below + } + + var isMustCoerce = false; + var coerceToType = lookupable.ReturnType.GetBoxedType(); + var identReturnType = identNodeInner.ExprEvaluator.ReturnType; + + if (identReturnType.IsCollectionMapOrArray()) + { + CheckArrayCoercion(identReturnType, lookupable.ReturnType, lookupable.Expression); + coerceToType = identReturnType; + // no action + } + else if (identReturnType != lookupable.ReturnType) + { + if (lookupable.ReturnType.IsNumeric()) + { + if (!identReturnType.CanCoerce(lookupable.ReturnType)) + { + ThrowConversionError(identReturnType, lookupable.ReturnType, lookupable.Expression); + } + isMustCoerce = true; + } + else + { + break; // assumed not compatible + } + } + + FilterSpecParamInValue inValue; + var streamName = identNodeInner.ResolvedStreamName; + if (arrayEventTypes != null && !arrayEventTypes.IsEmpty() && arrayEventTypes.ContainsKey(streamName)) + { + var indexAndProp = GetStreamIndex(identNodeInner.ResolvedPropertyName); + inValue = new InSetOfValuesEventPropIndexed( + identNodeInner.ResolvedStreamName, indexAndProp.First, + indexAndProp.Second, isMustCoerce, coerceToType, statementName); + } + else + { + inValue = new InSetOfValuesEventProp( + identNodeInner.ResolvedStreamName, identNodeInner.ResolvedPropertyName, isMustCoerce, + coerceToType); + } + + listofValues.Add(inValue); + } + } + + // Fallback if not all values in the in-node can be resolved to properties or constants + if (listofValues.Count == expectedNumberOfConstants) + { + return new FilterSpecParamIn(lookupable, op, listofValues); + } + return null; + } + + private static void CheckArrayCoercion(Type returnTypeValue, Type returnTypeLookupable, string propertyName) + { + if (returnTypeValue == null || !returnTypeValue.IsArray) + { + return; + } + if (!returnTypeLookupable.IsArrayTypeCompatible(returnTypeValue.GetElementType())) + { + ThrowConversionError(returnTypeValue.GetElementType(), returnTypeLookupable, propertyName); + } + } + + private static FilterSpecParam HandleEqualsAndRelOp( + ExprNode constituent, + IDictionary> arrayEventTypes, + ExprEvaluatorContext exprEvaluatorContext, + string statementName) + { + FilterOperator op; + if (constituent is ExprEqualsNode) + { + var equalsNode = (ExprEqualsNode) constituent; + if (!equalsNode.IsIs) + { + op = FilterOperator.EQUAL; + if (equalsNode.IsNotEquals) + { + op = FilterOperator.NOT_EQUAL; + } + } + else + { + op = FilterOperator.IS; + if (equalsNode.IsNotEquals) + { + op = FilterOperator.IS_NOT; + } + } + } + else + { + var relNode = (ExprRelationalOpNode) constituent; + if (relNode.RelationalOpEnum == RelationalOpEnum.GT) + { + op = FilterOperator.GREATER; + } + else if (relNode.RelationalOpEnum == RelationalOpEnum.LT) + { + op = FilterOperator.LESS; + } + else if (relNode.RelationalOpEnum == RelationalOpEnum.LE) + { + op = FilterOperator.LESS_OR_EQUAL; + } + else if (relNode.RelationalOpEnum == RelationalOpEnum.GE) + { + op = FilterOperator.GREATER_OR_EQUAL; + } + else + { + throw new IllegalStateException("Opertor '" + relNode.RelationalOpEnum + "' not mapped"); + } + } + + var left = constituent.ChildNodes[0]; + var right = constituent.ChildNodes[1]; + + // check identifier and constant combination + if ((ExprNodeUtility.IsConstantValueExpr(right)) && (left is ExprFilterOptimizableNode)) + { + var filterOptimizableNode = (ExprFilterOptimizableNode) left; + if (filterOptimizableNode.IsFilterLookupEligible) + { + var constantNode = (ExprConstantNode) right; + var lookupable = filterOptimizableNode.FilterLookupable; + var constant = constantNode.GetConstantValue(exprEvaluatorContext); + constant = HandleConstantsCoercion(lookupable, constant); + return new FilterSpecParamConstant(lookupable, op, constant); + } + } + if ((ExprNodeUtility.IsConstantValueExpr(left)) && (right is ExprFilterOptimizableNode)) + { + var filterOptimizableNode = (ExprFilterOptimizableNode) right; + if (filterOptimizableNode.IsFilterLookupEligible) + { + var constantNode = (ExprConstantNode) left; + var lookupable = filterOptimizableNode.FilterLookupable; + var constant = constantNode.GetConstantValue(exprEvaluatorContext); + constant = HandleConstantsCoercion(lookupable, constant); + var opReversed = op.IsComparisonOperator() ? op.ReversedRelationalOp() : op; + return new FilterSpecParamConstant(lookupable, opReversed, constant); + } + } + // check identifier and expression containing other streams + if ((left is ExprIdentNode) && (right is ExprIdentNode)) + { + var identNodeLeft = (ExprIdentNode) left; + var identNodeRight = (ExprIdentNode) right; + + if ((identNodeLeft.StreamId == 0) && (identNodeLeft.IsFilterLookupEligible) && + (identNodeRight.StreamId != 0)) + { + return HandleProperty(op, identNodeLeft, identNodeRight, arrayEventTypes, statementName); + } + if ((identNodeRight.StreamId == 0) && (identNodeRight.IsFilterLookupEligible) && + (identNodeLeft.StreamId != 0)) + { + op = GetReversedOperator(constituent, op); + // reverse operators, as the expression is "stream1.prop xyz stream0.prop" + return HandleProperty(op, identNodeRight, identNodeLeft, arrayEventTypes, statementName); + } + } + + if ((left is ExprFilterOptimizableNode) && (right is ExprContextPropertyNode)) + { + var filterOptimizableNode = (ExprFilterOptimizableNode) left; + var ctxNode = (ExprContextPropertyNode) right; + var lookupable = filterOptimizableNode.FilterLookupable; + if (filterOptimizableNode.IsFilterLookupEligible) + { + var numberCoercer = GetNumberCoercer(lookupable.ReturnType, ctxNode.ReturnType, lookupable.Expression); + return new FilterSpecParamContextProp( + lookupable, op, ctxNode.PropertyName, ctxNode.Getter, numberCoercer); + } + } + if ((left is ExprContextPropertyNode) && (right is ExprFilterOptimizableNode)) + { + var filterOptimizableNode = (ExprFilterOptimizableNode) right; + var ctxNode = (ExprContextPropertyNode) left; + var lookupable = filterOptimizableNode.FilterLookupable; + if (filterOptimizableNode.IsFilterLookupEligible) + { + op = GetReversedOperator(constituent, op); + // reverse operators, as the expression is "stream1.prop xyz stream0.prop" + var numberCoercer = GetNumberCoercer(lookupable.ReturnType, ctxNode.ReturnType, lookupable.Expression); + return new FilterSpecParamContextProp( + lookupable, op, ctxNode.PropertyName, ctxNode.Getter, numberCoercer); + } + } + return null; + } + + private static FilterOperator GetReversedOperator(ExprNode constituent, FilterOperator op) + { + if (!(constituent is ExprRelationalOpNode)) + { + return op; + } + + var relNode = (ExprRelationalOpNode) constituent; + var relationalOpEnum = relNode.RelationalOpEnum; + + if (relationalOpEnum == RelationalOpEnum.GT) + { + return FilterOperator.LESS; + } + else if (relationalOpEnum == RelationalOpEnum.LT) + { + return FilterOperator.GREATER; + } + else if (relationalOpEnum == RelationalOpEnum.LE) + { + return FilterOperator.GREATER_OR_EQUAL; + } + else if (relationalOpEnum == RelationalOpEnum.GE) + { + return FilterOperator.LESS_OR_EQUAL; + } + return op; + } + + private static FilterSpecParam HandleProperty( + FilterOperator op, + ExprIdentNode identNodeLeft, + ExprIdentNode identNodeRight, + IDictionary> arrayEventTypes, + string statementName) + { + var propertyName = identNodeLeft.ResolvedPropertyName; + + var leftType = identNodeLeft.ExprEvaluator.ReturnType; + var rightType = identNodeRight.ExprEvaluator.ReturnType; + + var numberCoercer = GetNumberCoercer(leftType, rightType, propertyName); + var isMustCoerce = numberCoercer != null; + var numericCoercionType = leftType.GetBoxedType(); + + var streamName = identNodeRight.ResolvedStreamName; + if (arrayEventTypes != null && !arrayEventTypes.IsEmpty() && arrayEventTypes.ContainsKey(streamName)) + { + var indexAndProp = GetStreamIndex(identNodeRight.ResolvedPropertyName); + return new FilterSpecParamEventPropIndexed( + identNodeLeft.FilterLookupable, op, identNodeRight.ResolvedStreamName, indexAndProp.First, + indexAndProp.Second, isMustCoerce, numberCoercer, numericCoercionType, statementName); + } + return new FilterSpecParamEventProp( + identNodeLeft.FilterLookupable, op, identNodeRight.ResolvedStreamName, + identNodeRight.ResolvedPropertyName, + isMustCoerce, numberCoercer, numericCoercionType, statementName); + } + + private static Coercer GetNumberCoercer(Type leftType, Type rightType, string expression) + { + var numericCoercionType = leftType.GetBoxedType(); + if (rightType != leftType) + { + if (rightType.IsNumeric()) + { + if (!rightType.CanCoerce(leftType)) + { + ThrowConversionError(rightType, leftType, expression); + } + return CoercerFactory.GetCoercer(rightType, numericCoercionType); + } + } + + return null; + } + + private static Coercer GetNumberCoercer(Type leftType, Type rightType, string expression, out Type coercionType) + { + var numericCoercionType = leftType.GetBoxedType(); + if (rightType != leftType) + { + if (rightType.IsNumeric()) + { + if (!rightType.CanCoerce(leftType)) + { + ThrowConversionError(rightType, leftType, expression); + } + coercionType = numericCoercionType; + return CoercerFactory.GetCoercer(rightType, numericCoercionType); + } + } + + coercionType = null; + return null; + } + + private static Pair GetStreamIndex(string resolvedPropertyName) + { + var property = PropertyParser.ParseAndWalkLaxToSimple(resolvedPropertyName); + if (!(property is NestedProperty)) + { + throw new IllegalStateException( + "Expected a nested property providing an index for array match '" + resolvedPropertyName + "'"); + } + var nested = (NestedProperty) property; + if (nested.Properties.Count < 2) + { + throw new IllegalStateException( + "Expected a nested property name for array match '" + resolvedPropertyName + "', none found"); + } + if (!(nested.Properties[0] is IndexedProperty)) + { + throw new IllegalStateException( + "Expected an indexed property for array match '" + resolvedPropertyName + + "', please provide an index"); + } + var index = ((IndexedProperty) nested.Properties[0]).Index; + nested.Properties.DeleteAt(0); + var writer = new StringWriter(); + nested.ToPropertyEPL(writer); + return new Pair(index, writer.ToString()); + } + + private static void ThrowConversionError(Type fromType, Type toType, string propertyName) + { + var text = + string.Format( + "Implicit conversion from datatype '{0}' to '{1}' for property '{2}' is not allowed (strict filter type coercion)", + Name.Of(fromType), Name.Of(toType), propertyName); + throw new ExprValidationException(text); + } + + // expressions automatically coerce to the most upwards type + // filters require the same type + private static Object HandleConstantsCoercion(FilterSpecLookupable lookupable, Object constant) + { + var identNodeType = lookupable.ReturnType; + if (!identNodeType.IsNumeric()) + { + return constant; // no coercion required, other type checking performed by expression this comes from + } + + if (constant == null) + { + // null constant type + return null; + } + + if (!constant.GetType().CanCoerce(identNodeType)) + { + ThrowConversionError(constant.GetType(), identNodeType, lookupable.Expression); + } + + var identNodeTypeBoxed = identNodeType.GetBoxedType(); + return CoercerFactory.CoerceBoxed(constant, identNodeTypeBoxed); + } + + private static bool IsExprExistsInAllEqualsChildNodes(IEnumerable childNodes, ExprNode search) + { + foreach (var child in childNodes) + { + var lhs = child.ChildNodes[0]; + var rhs = child.ChildNodes[1]; + if (!ExprNodeUtility.DeepEquals(lhs, search) && !ExprNodeUtility.DeepEquals(rhs, search)) + { + return false; + } + if (ExprNodeUtility.DeepEquals(lhs, rhs)) + { + return false; + } + } + return true; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/filter/FilterSpecCompilerPlanner.cs b/NEsper.Core/NEsper.Core/filter/FilterSpecCompilerPlanner.cs new file mode 100755 index 000000000..37ca2cf41 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterSpecCompilerPlanner.cs @@ -0,0 +1,338 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client.annotation; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.ops; +using com.espertech.esper.epl.expression.subquery; +using com.espertech.esper.epl.expression.visitor; + +namespace com.espertech.esper.filter +{ + public class FilterSpecCompilerPlanner + { + internal static IList[] PlanFilterParameters( + IList validatedNodes, + FilterSpecCompilerArgs args) + { + if (validatedNodes.IsEmpty()) + { + return AllocateListArray(0); + } + + var filterParamExprMap = new FilterParamExprMap(); + + // Make filter parameter for each expression node, if it can be optimized + DecomposePopulateConsolidate(filterParamExprMap, validatedNodes, args); + + // Use all filter parameter and unassigned expressions + IList filterParams = new List(); + filterParams.AddAll(filterParamExprMap.FilterParams); + int countUnassigned = filterParamExprMap.CountUnassignedExpressions(); + + // we are done if there are no remaining nodes + if (countUnassigned == 0) + { + return AllocateListArraySizeOne(filterParams); + } + + // determine max-width + int filterServiceMaxFilterWidth = + args.ConfigurationInformation.EngineDefaults.Execution.FilterServiceMaxFilterWidth; + HintAttribute hint = HintEnum.MAX_FILTER_WIDTH.GetHint(args.Annotations); + if (hint != null) + { + string hintValue = HintEnum.MAX_FILTER_WIDTH.GetHintAssignedValue(hint); + filterServiceMaxFilterWidth = int.Parse(hintValue); + } + + IList[] plan = null; + if (filterServiceMaxFilterWidth > 0) + { + plan = PlanRemainingNodesIfFeasible(filterParamExprMap, args, filterServiceMaxFilterWidth); + } + + if (plan != null) + { + return plan; + } + + // handle no-plan + FilterSpecParamExprNode node = MakeRemainingNode(filterParamExprMap.UnassignedExpressions, args); + filterParams.Add(node); + return AllocateListArraySizeOne(filterParams); + } + + private static IList[] PlanRemainingNodesIfFeasible( + FilterParamExprMap overallExpressions, + FilterSpecCompilerArgs args, + int filterServiceMaxFilterWidth) + + { + IList unassigned = overallExpressions.UnassignedExpressions; + IList orNodes = new List(unassigned.Count); + + foreach (ExprNode node in unassigned) + { + if (node is ExprOrNode) + { + orNodes.Add((ExprOrNode) node); + } + } + + var expressionsWithoutOr = new FilterParamExprMap(); + expressionsWithoutOr.Add(overallExpressions); + + // first dimension: or-node index + // second dimension: or child node index + var orNodesMaps = new FilterParamExprMap[orNodes.Count][]; + int countOr = 0; + int sizeFactorized = 1; + var sizePerOr = new int[orNodes.Count]; + foreach (ExprOrNode orNode in orNodes) + { + expressionsWithoutOr.RemoveNode(orNode); + orNodesMaps[countOr] = new FilterParamExprMap[orNode.ChildNodes.Count]; + int len = orNode.ChildNodes.Count; + + for (int i = 0; i < len; i++) + { + var map = new FilterParamExprMap(); + orNodesMaps[countOr][i] = map; + IList nodes = Collections.SingletonList(orNode.ChildNodes[i]); + DecomposePopulateConsolidate(map, nodes, args); + } + + sizePerOr[countOr] = len; + sizeFactorized = sizeFactorized*len; + countOr++; + } + + // we become too large + if (sizeFactorized > filterServiceMaxFilterWidth) + { + return null; + } + + // combine + var result = new IList[sizeFactorized]; + IEnumerable permutations = CombinationEnumeration.FromZeroBasedRanges(sizePerOr); + int count = 0; + foreach (var permutation in permutations) + { + result[count] = ComputePermutation(expressionsWithoutOr, permutation, orNodesMaps, args); + count++; + } + return result; + } + + private static IList ComputePermutation( + FilterParamExprMap filterParamExprMap, + object[] permutation, + FilterParamExprMap[][] orNodesMaps, + FilterSpecCompilerArgs args) + { + var mapAll = new FilterParamExprMap(); + mapAll.Add(filterParamExprMap); + + // combine + for (int orNodeNum = 0; orNodeNum < permutation.Length; orNodeNum++) + { + var orChildNodeNum = permutation[orNodeNum].AsInt(); + FilterParamExprMap mapOrSub = orNodesMaps[orNodeNum][orChildNodeNum]; + mapAll.Add(mapOrSub); + } + + // consolidate across + FilterSpecCompilerConsolidateUtil.Consolidate(mapAll, args.StatementName); + + IList filterParams = new List(); + filterParams.AddAll(mapAll.FilterParams); + int countUnassigned = mapAll.CountUnassignedExpressions(); + + if (countUnassigned == 0) + { + return filterParams; + } + + FilterSpecParamExprNode node = MakeRemainingNode(mapAll.UnassignedExpressions, args); + filterParams.Add(node); + return filterParams; + } + + private static void DecomposePopulateConsolidate( + FilterParamExprMap filterParamExprMap, + IList validatedNodes, + FilterSpecCompilerArgs args) + + { + IList constituents = DecomposeCheckAggregation(validatedNodes); + + // Make filter parameter for each expression node, if it can be optimized + foreach (ExprNode constituent in constituents) + { + FilterSpecParam param = FilterSpecCompilerMakeParamUtil.MakeFilterParam( + constituent, args.ArrayEventTypes, args.ExprEvaluatorContext, args.StatementName); + filterParamExprMap.Put(constituent, param); + // accepts null values as the expression may not be optimized + } + + // Consolidate entries as possible, i.e. (a != 5 and a != 6) is (a not in (5,6)) + // Removes duplicates for same property and same filter operator for filter service index optimizations + FilterSpecCompilerConsolidateUtil.Consolidate(filterParamExprMap, args.StatementName); + } + + private static FilterSpecParamExprNode MakeRemainingNode( + IList unassignedExpressions, + FilterSpecCompilerArgs args) + + { + if (unassignedExpressions.IsEmpty()) + { + throw new ArgumentException(); + } + + // any unoptimized expression nodes are put under one AND + ExprNode exprNode; + if (unassignedExpressions.Count == 1) + { + exprNode = unassignedExpressions[0]; + } + else + { + exprNode = MakeValidateAndNode(unassignedExpressions, args); + } + return MakeBooleanExprParam(exprNode, args); + } + + private static IList[] AllocateListArraySizeOne(IList @params) + { + IList[] arr = AllocateListArray(1); + arr[0] = @params; + return arr; + } + + private static IList[] AllocateListArray(int i) + { + return new IList[i]; + } + + private static FilterSpecParamExprNode MakeBooleanExprParam(ExprNode exprNode, FilterSpecCompilerArgs args) + { + bool hasSubselectFilterStream = DetermineSubselectFilterStream(exprNode); + bool hasTableAccess = DetermineTableAccessFilterStream(exprNode); + var lookupable = new FilterSpecLookupable(FilterSpecCompiler.PROPERTY_NAME_BOOLEAN_EXPRESSION, null, exprNode.ExprEvaluator.ReturnType, false); + return new FilterSpecParamExprNode( + lookupable, FilterOperator.BOOLEAN_EXPRESSION, exprNode, + args.TaggedEventTypes, + args.ArrayEventTypes, + args.VariableService, + args.TableService, + args.EventAdapterService, + args.FilterBooleanExpressionFactory, + args.ConfigurationInformation, + hasSubselectFilterStream, hasTableAccess); + } + + private static ExprAndNode MakeValidateAndNode(IList remainingExprNodes, FilterSpecCompilerArgs args) + { + ExprAndNode andNode = ExprNodeUtility.ConnectExpressionsByLogicalAnd(remainingExprNodes); + var validationContext = new ExprValidationContext( + args.StreamTypeService, + args.EngineImportService, + args.StatementExtensionSvcContext, null, + args.TimeProvider, + args.VariableService, + args.TableService, + args.ExprEvaluatorContext, + args.EventAdapterService, + args.StatementName, + args.StatementId, + args.Annotations, + args.ContextDescriptor, + args.ScriptingService, + false, false, true, false, null, false); + andNode.Validate(validationContext); + return andNode; + } + + private static bool DetermineTableAccessFilterStream(ExprNode exprNode) + { + var visitor = new ExprNodeTableAccessFinderVisitor(); + exprNode.Accept(visitor); + return visitor.HasTableAccess; + } + + private static bool DetermineSubselectFilterStream(ExprNode exprNode) + { + var visitor = new ExprNodeSubselectDeclaredDotVisitor(); + exprNode.Accept(visitor); + if (visitor.Subselects.IsEmpty()) + { + return false; + } + foreach (ExprSubselectNode subselectNode in visitor.Subselects) + { + if (subselectNode.IsFilterStreamSubselect) + { + return true; + } + } + return false; + } + + private static IList DecomposeCheckAggregation(IList validatedNodes) + { + // Break a top-level AND into constituent expression nodes + IList constituents = new List(); + foreach (ExprNode validated in validatedNodes) + { + if (validated is ExprAndNode) + { + RecursiveAndConstituents(constituents, validated); + } + else + { + constituents.Add(validated); + } + + // Ensure there is no aggregation nodes + var aggregateExprNodes = new List(); + ExprAggregateNodeUtil.GetAggregatesBottomUp(validated, aggregateExprNodes); + if (!aggregateExprNodes.IsEmpty()) + { + throw new ExprValidationException("Aggregation functions not allowed within filters"); + } + } + + return constituents; + } + + private static void RecursiveAndConstituents(IList constituents, ExprNode exprNode) + { + foreach (ExprNode inner in exprNode.ChildNodes) + { + if (inner is ExprAndNode) + { + RecursiveAndConstituents(constituents, inner); + } + else + { + constituents.Add(inner); + } + } + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/filter/FilterSpecLookupable.cs b/NEsper.Core/NEsper.Core/filter/FilterSpecLookupable.cs new file mode 100755 index 000000000..334cc4a92 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterSpecLookupable.cs @@ -0,0 +1,84 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +using com.espertech.esper.client; +using com.espertech.esper.util; + +namespace com.espertech.esper.filter +{ + [Serializable] + public class FilterSpecLookupable : MetaDefItem + { + [NonSerialized] + private EventPropertyGetter _getter; + + public FilterSpecLookupable(String expression, EventPropertyGetter getter, Type returnType, bool isNonPropertyGetter) + { + Expression = expression; + Getter = getter; + ReturnType = returnType; + IsNonPropertyGetter = isNonPropertyGetter; + } + + public string Expression { get; private set; } + + public Type ReturnType { get; private set; } + + public bool IsNonPropertyGetter { get; private set; } + + public EventPropertyGetter Getter + { + get { return _getter; } + private set { _getter = value; } + } + + public bool Equals(FilterSpecLookupable other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return Expression.Equals(other.Expression) && Equals(other.ReturnType, ReturnType); + } + + /// + /// Determines whether the specified is equal to the current . + /// + /// + /// true if the specified is equal to the current ; otherwise, false. + /// + /// The to compare with the current . 2 + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != typeof (FilterSpecLookupable)) return false; + return Equals((FilterSpecLookupable) obj); + } + + public override int GetHashCode() + { + unchecked + { + return ((Expression != null ? Expression.GetHashCode() : 0)*397) ^ + (ReturnType != null ? ReturnType.GetHashCode() : 0); + } + } + + public void AppendTo(TextWriter writer) + { + writer.Write(Expression); + } + + public override string ToString() + { + return "expression='" + Expression + '\''; + } + } +} diff --git a/NEsper.Core/NEsper.Core/filter/FilterSpecParam.cs b/NEsper.Core/NEsper.Core/filter/FilterSpecParam.cs new file mode 100755 index 000000000..7103459fc --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterSpecParam.cs @@ -0,0 +1,103 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.pattern; +using com.espertech.esper.util; + +namespace com.espertech.esper.filter +{ + /// + /// This class represents one filter parameter in an filter specification. + /// + /// Each filerting parameter has an attribute name and operator type. + /// + [Serializable] + public abstract class FilterSpecParam + : MetaDefItem + { + public static readonly FilterSpecParam[] EMPTY_PARAM_ARRAY = new FilterSpecParam[0]; + + protected FilterSpecParam(FilterSpecLookupable lookupable, FilterOperator filterOperator) + { + Lookupable = lookupable; + FilterOperator = filterOperator; + } + + public FilterSpecLookupable Lookupable { get; private set; } + + /// Returns the filter operator type. + /// filter operator type + public FilterOperator FilterOperator { get; private set; } + + /// + /// Return the filter parameter constant to filter for. + /// + /// is the prior results that can be used to determine filter parameters + /// The agent instance context. + /// + /// filter parameter constant's value + /// + public abstract object GetFilterValue(MatchedEventMap matchedEvents, AgentInstanceContext agentInstanceContext); + + public override String ToString() + { + return "FilterSpecParam" + + " lookupable=" + Lookupable + + " filterOp=" + FilterOperator; + } + + public override bool Equals(Object obj) + { + if (this == obj) + { + return true; + } + + if (!(obj is FilterSpecParam)) + { + return false; + } + + var other = (FilterSpecParam) obj; + if (!(Lookupable.Equals(other.Lookupable))) + { + return false; + } + if (FilterOperator != other.FilterOperator) + { + return false; + } + return true; + } + + public override int GetHashCode() + { + int result; + result = Lookupable.GetHashCode(); + result = 31*result + FilterOperator.GetHashCode(); + return result; + } + + public static FilterSpecParam[] ToArray(ICollection coll) + { + if (coll.IsEmpty()) + { + return EMPTY_PARAM_ARRAY; + } + return coll.ToArray(); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/filter/FilterSpecParamComparator.cs b/NEsper.Core/NEsper.Core/filter/FilterSpecParamComparator.cs new file mode 100755 index 000000000..cf147230e --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterSpecParamComparator.cs @@ -0,0 +1,91 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.util; + +namespace com.espertech.esper.filter +{ + /// + /// Sort comparator for filter parameters that sorts filter parameters according to filter operator type. + /// + [Serializable] + public class FilterSpecParamComparator + : IComparer + , MetaDefItem + { + /// + /// Defines the sort order among filter operator types. The idea is to sort EQUAL-type operators first + /// then RANGE then other operators, ie. sorting from a more restrictive (usually, not necessarily, really + /// depends on the client application) to a less restrictive operand. + /// + private static readonly FilterOperator[] SORT_ORDER = + { + FilterOperator.EQUAL, + FilterOperator.IS, + FilterOperator.IN_LIST_OF_VALUES, + FilterOperator.RANGE_OPEN, + FilterOperator.RANGE_HALF_OPEN, + FilterOperator.RANGE_HALF_CLOSED, + FilterOperator.RANGE_CLOSED, + FilterOperator.LESS, + FilterOperator.LESS_OR_EQUAL, + FilterOperator.GREATER_OR_EQUAL, + FilterOperator.GREATER, + FilterOperator.NOT_RANGE_CLOSED, + FilterOperator.NOT_RANGE_HALF_CLOSED, + FilterOperator.NOT_RANGE_HALF_OPEN, + FilterOperator.NOT_RANGE_OPEN, + FilterOperator.NOT_IN_LIST_OF_VALUES, + FilterOperator.NOT_EQUAL, + FilterOperator.IS_NOT, + FilterOperator.BOOLEAN_EXPRESSION + }; + + private static readonly int[] FilterSortOrder; + + static FilterSpecParamComparator() + { + var values = Enum.GetValues(typeof(FilterOperator)); + FilterSortOrder = new int[values.Length]; + for (int i = 0; i < FilterSortOrder.Length; i++) + { + FilterSortOrder[i] = IndexOf((FilterOperator)values.GetValue(i)); + } + } + + public int Compare(FilterOperator param1, FilterOperator param2) + { + // Within the same filter operator type sort by attribute name + if (param1 == param2) + { + return 0; + } + + // Within different filter operator types sort by the table above + var opIndex1 = FilterSortOrder[(int)param1]; + var opIndex2 = FilterSortOrder[(int)param2]; + return opIndex1 < opIndex2 ? -1 : 1; + } + + private static int IndexOf(FilterOperator filterOperator) + { + for (int i = 0; i < SORT_ORDER.Length; i++) + { + if (SORT_ORDER[i] == filterOperator) + { + return i; + } + } + + return SORT_ORDER.Length; + } + } +} diff --git a/NEsper.Core/NEsper.Core/filter/FilterSpecParamConstant.cs b/NEsper.Core/NEsper.Core/filter/FilterSpecParamConstant.cs new file mode 100755 index 000000000..d14c9e3cc --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterSpecParamConstant.cs @@ -0,0 +1,101 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.pattern; + +namespace com.espertech.esper.filter +{ + /// + /// This class represents a single, constant value filter parameter in an filter specification. + /// + public sealed class FilterSpecParamConstant : FilterSpecParam + { + private readonly Object _filterConstant; + + /// Constructor. + /// is the lookupable + /// is the type of compare + /// contains the value to match against the event's property value + /// ArgumentException if an operator was supplied that does not take a single constant value + public FilterSpecParamConstant(FilterSpecLookupable lookupable, FilterOperator filterOperator, Object filterConstant) + : base(lookupable, filterOperator) + { + _filterConstant = filterConstant; + + if (filterOperator.IsRangeOperator()) + { + throw new ArgumentException("Illegal filter operator " + filterOperator + " supplied to " + + "constant filter parameter"); + } + } + + /// + /// Return the filter parameter constant to filter for. + /// + /// is the prior results that can be used to determine filter parameters + /// + /// filter parameter constant's value + public override object GetFilterValue(MatchedEventMap matchedEvents, AgentInstanceContext agentInstanceContext) + { + return _filterConstant; + } + + /// Returns the constant value. + /// constant value + public object FilterConstant + { + get { return _filterConstant; } + } + + public override String ToString() + { + return base.ToString() + " filterConstant=" + _filterConstant; + } + + public bool Equals(FilterSpecParamConstant other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return base.Equals(other) && Equals(other._filterConstant, _filterConstant); + } + + /// + /// Determines whether the specified is equal to the current . + /// + /// + /// true if the specified is equal to the current ; otherwise, false. + /// + /// The to compare with the current . 2 + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + return Equals(obj as FilterSpecParamConstant); + } + + /// + /// Serves as a hash function for a particular type. + /// + /// + /// A hash code for the current . + /// + /// 2 + public override int GetHashCode() + { + unchecked + { + return (base.GetHashCode()*397) ^ (_filterConstant != null ? _filterConstant.GetHashCode() : 0); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/filter/FilterSpecParamContextProp.cs b/NEsper.Core/NEsper.Core/filter/FilterSpecParamContextProp.cs new file mode 100755 index 000000000..ca18b3393 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterSpecParamContextProp.cs @@ -0,0 +1,91 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.pattern; +using com.espertech.esper.util; + +namespace com.espertech.esper.filter +{ + /// + /// This class represents a filter parameter containing a reference to a context property. + /// + [Serializable] + public sealed class FilterSpecParamContextProp : FilterSpecParam + { + private readonly string _contextPropertyName; + [NonSerialized] + private readonly EventPropertyGetter _getter; + [NonSerialized] + private readonly Coercer _numberCoercer; + + public FilterSpecParamContextProp(FilterSpecLookupable lookupable, FilterOperator filterOperator, String contextPropertyName, EventPropertyGetter getter, Coercer numberCoercer) + : base(lookupable, filterOperator) + { + _contextPropertyName = contextPropertyName; + _getter = getter; + _numberCoercer = numberCoercer; + } + + public override object GetFilterValue(MatchedEventMap matchedEvents, AgentInstanceContext agentInstanceContext) + { + if (agentInstanceContext.ContextProperties == null) + { + return null; + } + + var result = _getter.Get(agentInstanceContext.ContextProperties); + if (_numberCoercer == null) + { + return result; + } + return _numberCoercer.Invoke(result); + } + + public bool Equals(FilterSpecParamContextProp other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return base.Equals(other) && Equals(other._contextPropertyName, _contextPropertyName); + } + + /// + /// Determines whether the specified is equal to the current . + /// + /// + /// true if the specified is equal to the current ; otherwise, false. + /// + /// The to compare with the current . 2 + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + return Equals(obj as FilterSpecParamContextProp); + } + + /// + /// Serves as a hash function for a particular type. + /// + /// + /// A hash code for the current . + /// + /// 2 + public override int GetHashCode() + { + unchecked + { + return (base.GetHashCode() * 397) ^ (_contextPropertyName != null ? _contextPropertyName.GetHashCode() : 0); + } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/filter/FilterSpecParamEventProp.cs b/NEsper.Core/NEsper.Core/filter/FilterSpecParamEventProp.cs new file mode 100755 index 000000000..b21d3456c --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterSpecParamEventProp.cs @@ -0,0 +1,155 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.util; +using com.espertech.esper.pattern; +using com.espertech.esper.util; + +namespace com.espertech.esper.filter +{ + /// + /// This class represents a filter parameter containing a reference to another event's property + /// in the event pattern result, for use to describe a filter parameter in a filter specification. + /// + public sealed class FilterSpecParamEventProp : FilterSpecParam + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly string _resultEventAsName; + private readonly string _resultEventProperty; + private readonly bool _isMustCoerce; + [NonSerialized] + private readonly Coercer _numberCoercer; + private readonly Type _coercionType; + private readonly string _statementName; + + /// + /// Constructor. + /// + /// is the property or function to get a lookup value + /// is the type of compare + /// is the name of the result event from which to get a property value to compare + /// is the name of the property to get from the named result event + /// indicates on whether numeric coercion must be performed + /// indicates the numeric coercion type to use + /// interface to use to perform coercion + /// statement name + /// if an operator was supplied that does not take a single constant value + public FilterSpecParamEventProp(FilterSpecLookupable lookupable, FilterOperator filterOperator, string resultEventAsName, + string resultEventProperty, bool isMustCoerce, + Coercer numberCoercer, Type coercionType, + string statementName) + : base(lookupable, filterOperator) + { + _resultEventAsName = resultEventAsName; + _resultEventProperty = resultEventProperty; + _isMustCoerce = isMustCoerce; + _numberCoercer = numberCoercer; + _coercionType = coercionType; + _statementName = statementName; + + if (filterOperator.IsRangeOperator()) { + throw new ArgumentException("Illegal filter operator " + filterOperator + " supplied to " + + "event property filter parameter"); + } + } + + /// + /// Returns true if numeric coercion is required, or false if not + /// + /// true to coerce at runtime + public bool IsMustCoerce + { + get { return _isMustCoerce; } + } + + /// + /// Returns the numeric coercion type. + /// + /// type to coerce to + public Type CoercionType + { + get { return _coercionType; } + } + + /// + /// Returns tag for result event. + /// + /// tag + public string ResultEventAsName + { + get { return _resultEventAsName; } + } + + /// + /// Returns the property of the result event. + /// + /// property name + public string ResultEventProperty + { + get { return _resultEventProperty; } + } + + public override Object GetFilterValue(MatchedEventMap matchedEvents, AgentInstanceContext agentInstanceContext) + { + EventBean theEvent = matchedEvents.GetMatchingEventByTag(_resultEventAsName); + Object value = null; + if (theEvent == null) { + Log.Warn("Matching events for tag '{0}' returned a null result, using null value in filter criteria, for statement '{1}'", _resultEventAsName, _statementName); + } else { + value = theEvent.Get(_resultEventProperty); + } + + // Coerce if necessary + if (_isMustCoerce) { + value = _numberCoercer.Invoke(value); + } + return value; + } + + public override String ToString() + { + return base.ToString() + + " resultEventAsName=" + _resultEventAsName + + " resultEventProperty=" + _resultEventProperty; + } + + public override bool Equals(object obj) + { + if (this == obj) { + return true; + } + + if (!(obj is FilterSpecParamEventProp)) { + return false; + } + + var other = (FilterSpecParamEventProp) obj; + if (!base.Equals(other)) { + return false; + } + + if ((!_resultEventAsName.Equals(other._resultEventAsName)) || + (!_resultEventProperty.Equals(other._resultEventProperty))) + { + return false; + } + return true; + } + + public override int GetHashCode() + { + return 31 * base.GetHashCode() + _resultEventProperty.GetHashCode(); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/filter/FilterSpecParamEventPropIndexed.cs b/NEsper.Core/NEsper.Core/filter/FilterSpecParamEventPropIndexed.cs new file mode 100755 index 000000000..7de1d2114 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterSpecParamEventPropIndexed.cs @@ -0,0 +1,186 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.pattern; +using com.espertech.esper.util; + +namespace com.espertech.esper.filter +{ + /// + /// This class represents a filter parameter containing a reference to another event's property + /// in the event pattern result, for use to describe a filter parameter + /// in a filter specification. + /// + public sealed class FilterSpecParamEventPropIndexed : FilterSpecParam + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly String _resultEventAsName; + private readonly int _resultEventIndex; + private readonly String _resultEventProperty; + private readonly bool _isMustCoerce; + [NonSerialized] + private readonly Coercer _numberCoercer; + private readonly Type _coercionType; + private readonly String _statementName; + + /// + /// Constructor. + /// + /// is the lookupable + /// is the type of compare + /// is the name of the result event from which to get a property value to compare + /// index + /// is the name of the property to get from the named result event + /// indicates on whether numeric coercion must be performed + /// interface to use to perform coercion + /// indicates the numeric coercion type to use + /// Name of the statement. + /// ArgumentException if an operator was supplied that does not take a single constant value + public FilterSpecParamEventPropIndexed(FilterSpecLookupable lookupable, + FilterOperator filterOperator, + String resultEventAsName, + int resultEventIndex, + String resultEventProperty, + bool isMustCoerce, + Coercer numberCoercer, + Type coercionType, + String statementName) + : base(lookupable, filterOperator) + { + _resultEventAsName = resultEventAsName; + _resultEventIndex = resultEventIndex; + _resultEventProperty = resultEventProperty; + _isMustCoerce = isMustCoerce; + _numberCoercer = numberCoercer; + _coercionType = coercionType; + _statementName = statementName; + + if (filterOperator.IsRangeOperator()) + { + throw new ArgumentException("Illegal filter operator " + filterOperator + " supplied to " + + "event property filter parameter"); + } + } + + /// Returns true if numeric coercion is required, or false if not + /// true to coerce at runtime + public bool IsMustCoerce + { + get { return _isMustCoerce; } + } + + /// Returns the numeric coercion type. + /// type to coerce to + public Type CoercionType + { + get { return _coercionType; } + } + + /// Returns tag for result event. + /// tag + public string ResultEventAsName + { + get { return _resultEventAsName; } + } + + /// Returns the property of the result event. + /// property name + public string ResultEventProperty + { + get { return _resultEventProperty; } + } + + public override object GetFilterValue(MatchedEventMap matchedEvents, AgentInstanceContext agentInstanceContext) + { + EventBean[] events = (EventBean[])matchedEvents.GetMatchingEventAsObjectByTag(_resultEventAsName); + + Object value = null; + if (events == null) + { + Log.Warn("Matching events for tag '" + _resultEventAsName + "' returned a null result, using null value in filter criteria, for statement '" + _statementName + "'"); + } + else if (_resultEventIndex > (events.Length - 1)) + { + Log.Warn("Matching events for tag '" + _resultEventAsName + "' returned no result for index " + _resultEventIndex + " at array length " + events.Length + ", using null value in filter criteria, for statement '" + _statementName + "'"); + } + else + { + value = events[_resultEventIndex].Get(_resultEventProperty); + } + + // Coerce if necessary + if (_isMustCoerce) + { + value = _numberCoercer.Invoke(value); + } + return value; + } + + /// Returns the index. + /// index + public int ResultEventIndex + { + get { return _resultEventIndex; } + } + + public override String ToString() + { + return base.ToString() + + " resultEventAsName=" + _resultEventAsName + + " resultEventProperty=" + _resultEventProperty; + } + + public bool Equals(FilterSpecParamEventPropIndexed other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return base.Equals(other) && Equals(other._resultEventAsName, _resultEventAsName) && Equals(other._resultEventProperty, _resultEventProperty) && other._resultEventIndex == _resultEventIndex; + } + + /// + /// Determines whether the specified is equal to the current . + /// + /// + /// true if the specified is equal to the current ; otherwise, false. + /// + /// The to compare with the current . 2 + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + return Equals(obj as FilterSpecParamEventPropIndexed); + } + + /// + /// Serves as a hash function for a particular type. + /// + /// + /// A hash code for the current . + /// + /// 2 + public override int GetHashCode() + { + unchecked + { + int result = base.GetHashCode(); + result = (result * 397) ^ (_resultEventAsName != null ? _resultEventAsName.GetHashCode() : 0); + result = (result * 397) ^ (_resultEventProperty != null ? _resultEventProperty.GetHashCode() : 0); + result = (result * 397) ^ _resultEventIndex; + return result; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/filter/FilterSpecParamExprNode.cs b/NEsper.Core/NEsper.Core/filter/FilterSpecParamExprNode.cs new file mode 100755 index 000000000..fb0707921 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterSpecParamExprNode.cs @@ -0,0 +1,234 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.visitor; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.epl.variable; +using com.espertech.esper.events; +using com.espertech.esper.pattern; + +namespace com.espertech.esper.filter +{ + /// + /// This class represents an arbitrary expression node returning a bool value as a filter parameter in an filter specification. + /// + public sealed class FilterSpecParamExprNode : FilterSpecParam + { + private readonly ExprNode _exprNode; + private readonly IDictionary> _taggedEventTypes; + private readonly IDictionary> _arrayEventTypes; + [NonSerialized] private readonly EventAdapterService _eventAdapterService; + [NonSerialized] private readonly FilterBooleanExpressionFactory _filterBooleanExpressionFactory; + [NonSerialized] private readonly VariableService _variableService; + [NonSerialized] private readonly TableService _tableService; + private readonly bool _hasVariable; + private readonly bool _useLargeThreadingProfile; + private readonly bool _hasFilterStreamSubquery; + private readonly bool _hasTableAccess; + private int _filterSpecId; + private int _filterSpecParamPathNum; + + public FilterSpecParamExprNode( + FilterSpecLookupable lookupable, + FilterOperator filterOperator, + ExprNode exprNode, + IDictionary> taggedEventTypes, + IDictionary> arrayEventTypes, + VariableService variableService, + TableService tableService, + EventAdapterService eventAdapterService, + FilterBooleanExpressionFactory filterBooleanExpressionFactory, + ConfigurationInformation configurationInformation, + bool hasSubquery, + bool hasTableAccess) + : base(lookupable, filterOperator) + { + if (filterOperator != FilterOperator.BOOLEAN_EXPRESSION) + { + throw new ArgumentException("Invalid filter operator for filter expression node"); + } + _exprNode = exprNode; + _taggedEventTypes = taggedEventTypes; + _arrayEventTypes = arrayEventTypes; + _variableService = variableService; + _tableService = tableService; + _eventAdapterService = eventAdapterService; + _filterBooleanExpressionFactory = filterBooleanExpressionFactory; + _useLargeThreadingProfile = configurationInformation.EngineDefaults.Execution.ThreadingProfile == + ConfigurationEngineDefaults.ThreadingProfile.LARGE; + _hasFilterStreamSubquery = hasSubquery; + _hasTableAccess = hasTableAccess; + + var visitor = new ExprNodeVariableVisitor(variableService); + exprNode.Accept(visitor); + _hasVariable = visitor.HasVariables; + } + + /// + /// Returns the expression node of the bool expression this filter parameter represents. + /// + /// expression node + public ExprNode ExprNode + { + get { return _exprNode; } + } + + /// + /// Returns the map of tag/stream names to event types that the filter expressions map use (for patterns) + /// + /// map + public IDictionary> TaggedEventTypes + { + get { return _taggedEventTypes; } + } + + public override object GetFilterValue( + MatchedEventMap matchedEvents, + AgentInstanceContext agentInstanceContext) + { + EventBean[] events = null; + + if ((_taggedEventTypes != null && !_taggedEventTypes.IsEmpty()) || + (_arrayEventTypes != null && !_arrayEventTypes.IsEmpty())) + { + int size = 0; + size += (_taggedEventTypes != null) ? _taggedEventTypes.Count : 0; + size += (_arrayEventTypes != null) ? _arrayEventTypes.Count : 0; + events = new EventBean[size + 1]; + + int count = 1; + if (_taggedEventTypes != null) + { + foreach (string tag in _taggedEventTypes.Keys) + { + events[count] = matchedEvents.GetMatchingEventByTag(tag); + count++; + } + } + + if (_arrayEventTypes != null) + { + foreach (var entry in _arrayEventTypes) + { + EventType compositeEventType = entry.Value.First; + events[count] = _eventAdapterService.AdapterForTypedMap( + matchedEvents.MatchingEventsAsMap, compositeEventType); + count++; + } + } + } + + return _filterBooleanExpressionFactory.Make( + this, events, agentInstanceContext, agentInstanceContext.StatementContext, + agentInstanceContext.AgentInstanceId); + } + + public override String ToString() + { + return base.ToString() + " exprNode=" + _exprNode; + } + + public override bool Equals(object obj) + { + if (this == obj) + { + return true; + } + + if (!(obj is FilterSpecParamExprNode)) + { + return false; + } + + FilterSpecParamExprNode other = (FilterSpecParamExprNode) obj; + if (!base.Equals(other)) + { + return false; + } + + if (_exprNode != other._exprNode) + { + return false; + } + + return true; + } + + public override int GetHashCode() + { + int result = base.GetHashCode(); + result = 31*result + _exprNode.GetHashCode(); + return result; + } + + public int FilterSpecId + { + get { return _filterSpecId; } + set { _filterSpecId = value; } + } + + public int FilterSpecParamPathNum + { + get { return _filterSpecParamPathNum; } + set { _filterSpecParamPathNum = value; } + } + + public IDictionary> ArrayEventTypes + { + get { return _arrayEventTypes; } + } + + public EventAdapterService EventAdapterService + { + get { return _eventAdapterService; } + } + + public FilterBooleanExpressionFactory FilterBooleanExpressionFactory + { + get { return _filterBooleanExpressionFactory; } + } + + public VariableService VariableService + { + get { return _variableService; } + } + + public TableService TableService + { + get { return _tableService; } + } + + public bool HasVariable + { + get { return _hasVariable; } + } + + public bool UseLargeThreadingProfile + { + get { return _useLargeThreadingProfile; } + } + + public bool HasFilterStreamSubquery + { + get { return _hasFilterStreamSubquery; } + } + + public bool HasTableAccess + { + get { return _hasTableAccess; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/filter/FilterSpecParamIn.cs b/NEsper.Core/NEsper.Core/filter/FilterSpecParamIn.cs new file mode 100755 index 000000000..62c116e27 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterSpecParamIn.cs @@ -0,0 +1,272 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.pattern; +using com.espertech.esper.util; +using com.espertech.esper.compat.magic; + +namespace com.espertech.esper.filter +{ + /// + /// This class represents a 'in' filter parameter in an filter specification. + /// + /// The 'in' checks for a list of values. + /// + /// + public sealed class FilterSpecParamIn : FilterSpecParam{ + private readonly IList _listOfValues; + private readonly MultiKeyUntyped _inListConstantsOnly; + private readonly bool _hasCollMapOrArray; + private readonly InValueAdder[] _adders; + + /// + /// Ctor. + /// + /// is the event property or function + /// is expected to be the IN-list operator + /// is a list of constants and event property names + /// for illegal args + public FilterSpecParamIn( + FilterSpecLookupable lookupable, + FilterOperator filterOperator, + IList listofValues) + : base(lookupable, filterOperator) + { + _listOfValues = listofValues; + + foreach (FilterSpecParamInValue value in listofValues) + { + Type returnType = value.ReturnType; + if (TypeHelper.IsCollectionMapOrArray(returnType)) + { + _hasCollMapOrArray = true; + break; + } + } + + if (_hasCollMapOrArray) + { + _adders = new InValueAdder[listofValues.Count]; + for (int i = 0; i < listofValues.Count; i++) + { + Type returnType = listofValues[0].ReturnType; + if (returnType == null) + { + _adders[i] = InValueAdderPlain.INSTANCE; + } + else if (returnType.IsArray) + { + _adders[i] = InValueAdderArray.INSTANCE; + } + else if (returnType.IsGenericDictionary()) + { + _adders[i] = InValueAdderMap.INSTANCE; + } + else if (returnType.IsGenericCollection()) + { + _adders[i] = InValueAdderColl.INSTANCE; + } + else + { + _adders[i] = InValueAdderPlain.INSTANCE; + } + } + } + + bool isAllConstants = true; + foreach (FilterSpecParamInValue value in listofValues) + { + if (!value.IsConstant) + { + isAllConstants = false; + break; + } + } + + if (isAllConstants) + { + _inListConstantsOnly = GetFilterValues(null, null); + } + + if ((filterOperator != FilterOperator.IN_LIST_OF_VALUES) && + ((filterOperator != FilterOperator.NOT_IN_LIST_OF_VALUES))) + { + throw new ArgumentException( + "Illegal filter operator " + filterOperator + " supplied to " + + "in-values filter parameter"); + } + } + + public override Object GetFilterValue(MatchedEventMap matchedEvents, AgentInstanceContext agentInstanceContext) + { + // If the list of values consists of all-constants and no event properties, then use cached version + if (_inListConstantsOnly != null) { + return _inListConstantsOnly; + } + return GetFilterValues(matchedEvents, agentInstanceContext); + } + + /// + /// Returns the list of values we are asking to match. + /// + /// list of filter values + public IList ListOfValues + { + get { return _listOfValues; } + } + + public override String ToString() + { + return base.ToString() + " in=(listOfValues=" + _listOfValues.ToString() + ')'; + } + + public override bool Equals(object obj) + { + if (this == obj) { + return true; + } + + if (!(obj is FilterSpecParamIn)) { + return false; + } + + var other = (FilterSpecParamIn) obj; + if (!base.Equals(other)) { + return false; + } + + if (_listOfValues.Count != other._listOfValues.Count) { + return false; + } + + if (!(CompatExtensions.DeepEquals(_listOfValues, other._listOfValues))) { + return false; + } + return true; + } + + public override int GetHashCode() + { + int result = base.GetHashCode(); + result = 31 * result + (_listOfValues != null ? ListOfValues.GetHashCode() : 0); + return result; + } + + private MultiKeyUntyped GetFilterValues(MatchedEventMap matchedEvents, AgentInstanceContext agentInstanceContext) + { + if (!_hasCollMapOrArray) { + var constantsX = new Object[_listOfValues.Count]; + int countX = 0; + foreach (FilterSpecParamInValue valuePlaceholder in _listOfValues) { + constantsX[countX++] = valuePlaceholder.GetFilterValue(matchedEvents, agentInstanceContext); + } + return new MultiKeyUntyped(constantsX); + } + + var constants = new ArrayDeque(_listOfValues.Count); + int count = 0; + foreach (FilterSpecParamInValue valuePlaceholder in _listOfValues) { + Object value = valuePlaceholder.GetFilterValue(matchedEvents, agentInstanceContext); + if (value != null) { + _adders[count].Add(constants, value); + } + count++; + } + return new MultiKeyUntyped(constants.ToArray()); + } + + private interface InValueAdder + { + void Add(ICollection constants, Object value); + } + + private class InValueAdderArray : InValueAdder + { + internal static readonly InValueAdderArray INSTANCE = new InValueAdderArray(); + + private InValueAdderArray() + { + } + + public void Add(ICollection constants, Object value) + { + var asArray = (Array) value; + var len = asArray.Length; + for (int i = 0; i < len; i++) + { + constants.Add(asArray.GetValue(i)); + } + } + } + + private class InValueAdderMap : InValueAdder + { + internal static readonly InValueAdderMap INSTANCE = new InValueAdderMap(); + + private InValueAdderMap() + { + } + + public void Add(ICollection constants, Object value) + { + IEnumerable mapKeys; + + if (value.GetType().IsGenericDictionary()) + mapKeys = MagicMarker.GetDictionaryFactory(value.GetType()).Invoke(value).Keys; + else + throw new ArgumentException("invalid value", nameof(value)); + + constants.AddAll(mapKeys); + } + } + + private class InValueAdderColl : InValueAdder + { + internal static readonly InValueAdderColl INSTANCE = new InValueAdderColl(); + + private InValueAdderColl() + { + } + + public void Add(ICollection constants, Object value) + { + ICollection collection; + + if (value is ICollection) + collection = (ICollection)value; + else if (value.GetType().IsGenericCollection()) + collection = MagicMarker.GetCollectionFactory(value.GetType()).Invoke(value); + else + throw new ArgumentException("invalid value", nameof(value)); + + constants.AddAll(collection); + } + } + + private class InValueAdderPlain : InValueAdder + { + internal static readonly InValueAdderPlain INSTANCE = new InValueAdderPlain(); + + private InValueAdderPlain() + { + } + + public void Add(ICollection constants, Object value) + { + constants.Add(value); + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/filter/FilterSpecParamInValue.cs b/NEsper.Core/NEsper.Core/filter/FilterSpecParamInValue.cs new file mode 100755 index 000000000..2fb6e2c48 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterSpecParamInValue.cs @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.pattern; +using com.espertech.esper.util; + +namespace com.espertech.esper.filter +{ + /// + /// Denotes a value for use by the in-keyword within a list of values + /// + public interface FilterSpecParamInValue : MetaDefItem + { + /// + /// Returns the actual value to filter for from prior matching events + /// + /// is a map of matching events + /// eval context + /// filter-for value + Object GetFilterValue(MatchedEventMap matchedEvents, ExprEvaluatorContext evaluatorContext); + + Type ReturnType { get; } + + bool IsConstant { get; } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/filter/FilterSpecParamRange.cs b/NEsper.Core/NEsper.Core/filter/FilterSpecParamRange.cs new file mode 100755 index 000000000..c3d19c5c2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterSpecParamRange.cs @@ -0,0 +1,115 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.pattern; + +namespace com.espertech.esper.filter +{ + /// + /// This class represents a range filter parameter in an + /// filter specification. + /// + public sealed class FilterSpecParamRange : FilterSpecParam + { + private readonly FilterSpecParamRangeValue _min; + private readonly FilterSpecParamRangeValue _max; + + /// Constructor. + /// is the lookupable + /// is the type of range operator + /// is the begin point of the range + /// is the end point of the range + /// ArgumentException if an operator was supplied that does not take a double range value + public FilterSpecParamRange(FilterSpecLookupable lookupable, FilterOperator filterOperator, FilterSpecParamRangeValue min, FilterSpecParamRangeValue max) + : base(lookupable, filterOperator) + { + _min = min; + _max = max; + + if (!(filterOperator.IsRangeOperator()) && (!filterOperator.IsInvertedRangeOperator())) + { + throw new ArgumentException("Illegal filter operator " + filterOperator + " supplied to " + + "range filter parameter"); + } + } + + public override object GetFilterValue(MatchedEventMap matchedEvents, AgentInstanceContext agentInstanceContext) + { + if (Lookupable.ReturnType == typeof(String)) + { + return new StringRange((String)_min.GetFilterValue(matchedEvents, agentInstanceContext), (String)_max.GetFilterValue(matchedEvents, agentInstanceContext)); + } + var begin = (double?)_min.GetFilterValue(matchedEvents, agentInstanceContext); + var end = (double?)_max.GetFilterValue(matchedEvents, agentInstanceContext); + return new DoubleRange(begin, end); + } + + /// Returns the lower endpoint. + /// lower endpoint + public FilterSpecParamRangeValue Min + { + get { return _min; } + } + + /// Returns the upper endpoint. + /// upper endpoint + public FilterSpecParamRangeValue Max + { + get { return _max; } + } + + public override String ToString() + { + return base.ToString() + " range=(min=" + _min + ",max=" + _max + ')'; + } + + public bool Equals(FilterSpecParamRange other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return base.Equals(other) && Equals(other._min, _min) && Equals(other._max, _max); + } + + /// + /// Determines whether the specified is equal to the current . + /// + /// + /// true if the specified is equal to the current ; otherwise, false. + /// + /// The to compare with the current . 2 + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + return Equals(obj as FilterSpecParamRange); + } + + /// + /// Serves as a hash function for a particular type. + /// + /// + /// A hash code for the current . + /// + /// 2 + public override int GetHashCode() + { + unchecked + { + int result = base.GetHashCode(); + result = (result * 397) ^ (_min != null ? _min.GetHashCode() : 0); + result = (result * 397) ^ (_max != null ? _max.GetHashCode() : 0); + return result; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/filter/FilterSpecParamRangeValue.cs b/NEsper.Core/NEsper.Core/filter/FilterSpecParamRangeValue.cs new file mode 100755 index 000000000..d2f8e75b2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterSpecParamRangeValue.cs @@ -0,0 +1,31 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.pattern; +using com.espertech.esper.util; + +namespace com.espertech.esper.filter +{ + /// + /// Interface for range-type filter parameters for type checking and to obtain the filter values for endpoints based on prior results. + /// + public interface FilterSpecParamRangeValue : MetaDefItem + { + /// + /// Returns the filter value representing the endpoint. + /// + /// is the prior results + /// The expr evaluator context. + /// filter value + Object GetFilterValue(MatchedEventMap matchedEvents, ExprEvaluatorContext exprEvaluatorContext); + } +} diff --git a/NEsper.Core/NEsper.Core/filter/FilterValueSet.cs b/NEsper.Core/NEsper.Core/filter/FilterValueSet.cs new file mode 100755 index 000000000..a66d80170 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterValueSet.cs @@ -0,0 +1,31 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.IO; + +using com.espertech.esper.client; + +namespace com.espertech.esper.filter +{ + /// + /// Contains the filter criteria to sift through events. The filter criteria are the event class + /// to look for and a set of parameters (property names, operators and constant/range values). + /// + public interface FilterValueSet + { + /// Returns type of event to filter for. + /// event type + EventType EventType { get; } + + /// Returns list of filter parameters. + /// list of filter params + FilterValueSetParam[][] Parameters { get; } + + void AppendTo(TextWriter writer); + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/filter/FilterValueSetImpl.cs b/NEsper.Core/NEsper.Core/filter/FilterValueSetImpl.cs new file mode 100755 index 000000000..6d845d01c --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterValueSetImpl.cs @@ -0,0 +1,81 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.filter +{ + /// + /// Container for filter values for use by the to filter and distribute incoming events. + /// + public class FilterValueSetImpl : FilterValueSet + { + private readonly EventType _eventType; + private readonly FilterValueSetParam[][] _parameters; + + /// Ctor. + /// type of event to filter for + /// list of filter parameters + public FilterValueSetImpl(EventType eventType, FilterValueSetParam[][] parameters) + { + _eventType = eventType; + _parameters = parameters; + } + + /// Returns event type to filter for. + /// event type to filter for + public EventType EventType + { + get { return _eventType; } + } + + /// Returns list of filter parameters. + /// list of filter parameters + public FilterValueSetParam[][] Parameters + { + get { return _parameters; } + } + + public override String ToString() + { + return "FilterValueSetImpl{" + + "eventType=" + _eventType.Name + + ", parameters=" + _parameters.Render() + + '}'; + } + + public void AppendTo(TextWriter writer) + { + writer.Write(_eventType.Name); + writer.Write("("); + String delimiter = ""; + foreach (FilterValueSetParam[] param in _parameters) + { + writer.Write(delimiter); + AppendTo(writer, param); + delimiter = " or "; + } + writer.Write(")"); + } + + private void AppendTo(TextWriter writer, FilterValueSetParam[] parameters) + { + String delimiter = ""; + foreach (FilterValueSetParam param in parameters) + { + writer.Write(delimiter); + param.AppendTo(writer); + delimiter = ","; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/filter/FilterValueSetParam.cs b/NEsper.Core/NEsper.Core/filter/FilterValueSetParam.cs new file mode 100755 index 000000000..3bf2e019c --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterValueSetParam.cs @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.IO; + +namespace com.espertech.esper.filter +{ + /// + /// This interface represents one filter parameter in an filter + /// specification. + /// + /// Each filtering parameter has a lookup-able and operator type, and a value to filter for. + public interface FilterValueSetParam + { + /// Returns the lookup-able for the filter parameter. + /// lookup-able + FilterSpecLookupable Lookupable { get; } + + /// Returns the filter operator type. + /// filter operator type + FilterOperator FilterOperator { get; } + + /// Return the filter parameter constant to filter for. + /// filter parameter constant's value + object FilterForValue { get; } + + void AppendTo(TextWriter writer); + } +} diff --git a/NEsper.Core/NEsper.Core/filter/FilterValueSetParamImpl.cs b/NEsper.Core/NEsper.Core/filter/FilterValueSetParamImpl.cs new file mode 100755 index 000000000..946fb8d8d --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/FilterValueSetParamImpl.cs @@ -0,0 +1,64 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.filter +{ + /// + /// Filter parameter value defining the event property to filter, the filter operator, and the filter value. + /// + public class FilterValueSetParamImpl : FilterValueSetParam + { + private readonly FilterSpecLookupable _lookupable; + private readonly FilterOperator _filterOperator; + private readonly Object _filterValue; + + /// Ctor. + /// stuff to use to interrogate + /// operator to apply + /// value to look for + public FilterValueSetParamImpl(FilterSpecLookupable lookupable, FilterOperator filterOperator, Object filterValue) + { + _lookupable = lookupable; + _filterOperator = filterOperator; + _filterValue = filterValue; + } + + public FilterSpecLookupable Lookupable + { + get { return _lookupable; } + } + + public FilterOperator FilterOperator + { + get { return _filterOperator; } + } + + public object FilterForValue + { + get { return _filterValue; } + } + + public override String ToString() { + return "FilterValueSetParamImpl{" + + "lookupable='" + _lookupable + '\'' + + ", filterOperator=" + _filterOperator + + ", filterValue=" + _filterValue + + '}'; + } + + public void AppendTo(TextWriter writer) + { + _lookupable.AppendTo(writer); + writer.Write(_filterOperator.GetTextualOp()); + writer.Write(_filterValue == null ? "null" : _filterValue.ToString()); + } + } +} diff --git a/NEsper.Core/NEsper.Core/filter/InSetOfValuesConstant.cs b/NEsper.Core/NEsper.Core/filter/InSetOfValuesConstant.cs new file mode 100755 index 000000000..6ea7ea213 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/InSetOfValuesConstant.cs @@ -0,0 +1,83 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.pattern; + +namespace com.espertech.esper.filter +{ + /// Constant value in a list of values following an in-keyword. + public class InSetOfValuesConstant : FilterSpecParamInValue + { + /// Ctor. + /// is the constant value + public InSetOfValuesConstant(Object constant) + { + Constant = constant; + } + + public Object GetFilterValue(MatchedEventMap matchedEvents, ExprEvaluatorContext evaluatorContext) + { + return Constant; + } + + public Type ReturnType + { + get { return Constant == null ? null : Constant.GetType(); } + } + + public bool IsConstant + { + get { return true; } + } + + /// + /// Returns the constant value. + /// + /// + /// constant + /// + public object Constant { get; private set; } + + public bool Equals(InSetOfValuesConstant other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return Equals(other.Constant, Constant); + } + + /// + /// Determines whether the specified is equal to the current . + /// + /// + /// true if the specified is equal to the current ; otherwise, false. + /// + /// The to compare with the current . 2 + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != typeof(InSetOfValuesConstant)) return false; + return Equals((InSetOfValuesConstant)obj); + } + + /// + /// Serves as a hash function for a particular type. + /// + /// + /// A hash code for the current . + /// + /// 2 + public override int GetHashCode() + { + return (Constant != null ? Constant.GetHashCode() : 0); + } + } +} diff --git a/NEsper.Core/NEsper.Core/filter/InSetOfValuesContextProp.cs b/NEsper.Core/NEsper.Core/filter/InSetOfValuesContextProp.cs new file mode 100755 index 000000000..2ab7d9374 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/InSetOfValuesContextProp.cs @@ -0,0 +1,99 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.pattern; +using com.espertech.esper.util; + +namespace com.espertech.esper.filter +{ + /// + /// Event property value in a list of values following an in-keyword. + /// + [Serializable] + public class InSetOfValuesContextProp : FilterSpecParamInValue + { + [NonSerialized] + private readonly EventPropertyGetter _getter; + [NonSerialized] + private readonly Coercer _numberCoercer; + private readonly string _propertyName; + [NonSerialized] + private readonly Type _returnType; + + public InSetOfValuesContextProp(String propertyName, EventPropertyGetter getter, Coercer coercer, Type returnType) + { + _propertyName = propertyName; + _getter = getter; + _numberCoercer = coercer; + _returnType = returnType; + } + + public Type ReturnType + { + get { return _returnType; } + } + + public bool IsConstant + { + get { return false; } + } + + public Object GetFilterValue(MatchedEventMap matchedEvents, ExprEvaluatorContext evaluatorContext) + { + if (evaluatorContext.ContextProperties == null) + { + return null; + } + Object result = _getter.Get(evaluatorContext.ContextProperties); + + if (_numberCoercer == null) + { + return result; + } + return _numberCoercer.Invoke(result); + } + + public bool Equals(InSetOfValuesContextProp other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return Equals(other._propertyName, _propertyName); + } + + /// + /// Determines whether the specified is equal to the current . + /// + /// + /// true if the specified is equal to the current ; otherwise, false. + /// + /// The to compare with the current . 2 + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != typeof(InSetOfValuesContextProp)) return false; + return Equals((InSetOfValuesContextProp)obj); + } + + /// + /// Serves as a hash function for a particular type. + /// + /// + /// A hash code for the current . + /// + /// 2 + public override int GetHashCode() + { + return (_propertyName != null ? _propertyName.GetHashCode() : 0); + } + } +} diff --git a/NEsper.Core/NEsper.Core/filter/InSetOfValuesEventProp.cs b/NEsper.Core/NEsper.Core/filter/InSetOfValuesEventProp.cs new file mode 100755 index 000000000..2165f9660 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/InSetOfValuesEventProp.cs @@ -0,0 +1,122 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.pattern; +using com.espertech.esper.util; + +namespace com.espertech.esper.filter +{ + /// + /// Event property value in a list of values following an in-keyword. + /// + public class InSetOfValuesEventProp : FilterSpecParamInValue + { + private readonly bool _isMustCoerce; + private readonly Type _coercionType; + + /// Ctor. + /// is the event tag + /// is the event property name + /// indicates on whether numeric coercion must be performed + /// indicates the numeric coercion type to use + public InSetOfValuesEventProp(String resultEventAsName, String resultEventProperty, bool isMustCoerce, Type coercionType) + { + ResultEventAsName = resultEventAsName; + ResultEventProperty = resultEventProperty; + _coercionType = coercionType; + _isMustCoerce = isMustCoerce; + } + + public Type ReturnType + { + get { return _coercionType; } + } + + public bool IsConstant + { + get { return false; } + } + + public Object GetFilterValue(MatchedEventMap matchedEvents, ExprEvaluatorContext evaluatorContext) + { + EventBean theEvent = matchedEvents.GetMatchingEventByTag(ResultEventAsName); + if (theEvent == null) + { + throw new IllegalStateException("Matching event named " + + '\'' + ResultEventAsName + "' not found in event result set"); + } + + Object value = theEvent.Get(ResultEventProperty); + + // Coerce if necessary + if (_isMustCoerce) + { + if (value != null) + { + value = CoercerFactory.CoerceBoxed(value, _coercionType); + } + } + return value; + } + + /// Returns the tag used for the event property. + /// tag + public string ResultEventAsName { get; private set; } + + /// Returns the event property name. + /// property name + public string ResultEventProperty { get; private set; } + + public override String ToString() + { + return "resultEventProp=" + ResultEventAsName + '.' + ResultEventProperty; + } + + public bool Equals(InSetOfValuesEventProp other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return Equals(other.ResultEventAsName, ResultEventAsName) && Equals(other.ResultEventProperty, ResultEventProperty); + } + + /// + /// Determines whether the specified is equal to the current . + /// + /// + /// true if the specified is equal to the current ; otherwise, false. + /// + /// The to compare with the current . 2 + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != typeof(InSetOfValuesEventProp)) return false; + return Equals((InSetOfValuesEventProp)obj); + } + + /// + /// Serves as a hash function for a particular type. + /// + /// + /// A hash code for the current . + /// + /// 2 + public override int GetHashCode() + { + unchecked + { + return ((ResultEventAsName != null ? ResultEventAsName.GetHashCode() : 0) * 397) ^ (ResultEventProperty != null ? ResultEventProperty.GetHashCode() : 0); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/filter/InSetOfValuesEventPropIndexed.cs b/NEsper.Core/NEsper.Core/filter/InSetOfValuesEventPropIndexed.cs new file mode 100755 index 000000000..f836772a6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/InSetOfValuesEventPropIndexed.cs @@ -0,0 +1,148 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.pattern; +using com.espertech.esper.util; + +namespace com.espertech.esper.filter +{ + /// Event property value in a list of values following an in-keyword. + public class InSetOfValuesEventPropIndexed : FilterSpecParamInValue + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly String _resultEventAsName; + private readonly int _resultEventIndex; + private readonly String _resultEventProperty; + private readonly bool _isMustCoerce; + private readonly Type _coercionType; + private readonly String _statementName; + + /// + /// Ctor. + /// + /// is the event tag + /// index + /// is the event property name + /// indicates on whether numeric coercion must be performed + /// indicates the numeric coercion type to use + /// Name of the statement. + public InSetOfValuesEventPropIndexed( + String resultEventAsName, + int resultEventindex, + String resultEventProperty, + bool isMustCoerce, + Type coercionType, + String statementName) + { + _resultEventAsName = resultEventAsName; + _resultEventProperty = resultEventProperty; + _resultEventIndex = resultEventindex; + _coercionType = coercionType; + _isMustCoerce = isMustCoerce; + _statementName = statementName; + } + + public Type ReturnType + { + get { return _coercionType; } + } + + public bool IsConstant + { + get { return false; } + } + + public Object GetFilterValue(MatchedEventMap matchedEvents, ExprEvaluatorContext evaluatorContext) + { + EventBean[] events = (EventBean[])matchedEvents.GetMatchingEventAsObjectByTag(_resultEventAsName); + + Object value = null; + if (events == null) + { + Log.Warn("Matching events for tag '" + _resultEventAsName + "' returned a null result, using null value in filter criteria, for statement '" + _statementName + "'"); + } + else if (_resultEventIndex > (events.Length - 1)) + { + Log.Warn("Matching events for tag '" + _resultEventAsName + "' returned no result for index " + _resultEventIndex + " at array length " + events.Length + ", using null value in filter criteria, for statement '" + _statementName + "'"); + } + else + { + value = events[_resultEventIndex].Get(_resultEventProperty); + } + + // Coerce if necessary + if (_isMustCoerce) + { + value = CoercerFactory.CoerceBoxed(value, _coercionType); + } + return value; + } + + /// Returns the tag used for the event property. + /// tag + public string ResultEventAsName + { + get { return _resultEventAsName; } + } + + /// Returns the event property name. + /// property name + public string ResultEventProperty + { + get { return _resultEventProperty; } + } + + public override String ToString() + { + return "resultEventProp=" + _resultEventAsName + '.' + _resultEventProperty; + } + + public bool Equals(InSetOfValuesEventPropIndexed other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return Equals(other._resultEventAsName, _resultEventAsName) && Equals(other._resultEventProperty, _resultEventProperty); + } + + /// + /// Determines whether the specified is equal to the current . + /// + /// + /// true if the specified is equal to the current ; otherwise, false. + /// + /// The to compare with the current . 2 + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != typeof(InSetOfValuesEventPropIndexed)) return false; + return Equals((InSetOfValuesEventPropIndexed)obj); + } + + /// + /// Serves as a hash function for a particular type. + /// + /// + /// A hash code for the current . + /// + /// 2 + public override int GetHashCode() + { + unchecked + { + return ((_resultEventAsName != null ? _resultEventAsName.GetHashCode() : 0) * 397) ^ (_resultEventProperty != null ? _resultEventProperty.GetHashCode() : 0); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/filter/IndexFactory.cs b/NEsper.Core/NEsper.Core/filter/IndexFactory.cs new file mode 100755 index 000000000..25d72de69 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/IndexFactory.cs @@ -0,0 +1,116 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.filter +{ + /// + /// Factory for instances based on event property name and filter operator type. + /// + public class IndexFactory + { + /// + /// Factory for indexes that store filter parameter constants for a given event property and filter operator. + /// + /// Does not perform any check of validity of property name. + /// + /// The lookupable. + /// The lock factory. + /// is the type of index to use + /// + /// the proper index based on the filter operator type + /// + /// Cannot create filter index instance for filter operator + filterOperator + public static FilterParamIndexBase CreateIndex(FilterSpecLookupable lookupable, FilterServiceGranularLockFactory lockFactory, FilterOperator filterOperator) + { + FilterParamIndexBase index; + Type returnValueType = lookupable.ReturnType; + + // Handle all EQUAL comparisons + if (filterOperator == FilterOperator.EQUAL) + { + index = new FilterParamIndexEquals(lookupable, lockFactory.ObtainNew()); + return index; + } + + // Handle all NOT-EQUAL comparisons + if (filterOperator == FilterOperator.NOT_EQUAL) + { + index = new FilterParamIndexNotEquals(lookupable, lockFactory.ObtainNew()); + return index; + } + + if (filterOperator == FilterOperator.IS) + { + index = new FilterParamIndexEqualsIs(lookupable, lockFactory.ObtainNew()); + return index; + } + + if (filterOperator == FilterOperator.IS_NOT) + { + index = new FilterParamIndexNotEqualsIs(lookupable, lockFactory.ObtainNew()); + return index; + } + + // Handle all GREATER, LESS etc. comparisons + if ((filterOperator == FilterOperator.GREATER) || + (filterOperator == FilterOperator.GREATER_OR_EQUAL) || + (filterOperator == FilterOperator.LESS) || + (filterOperator == FilterOperator.LESS_OR_EQUAL)) + { + if (returnValueType != typeof(String)) { + index = new FilterParamIndexCompare(lookupable, lockFactory.ObtainNew(), filterOperator); + } + else { + index = new FilterParamIndexCompareString(lookupable, lockFactory.ObtainNew(), filterOperator); + } + return index; + } + + // Handle all normal and inverted RANGE comparisons + if (filterOperator.IsRangeOperator()) + { + if (returnValueType != typeof(String)) { + index = new FilterParamIndexDoubleRange(lookupable, lockFactory.ObtainNew(), filterOperator); + } + else { + index = new FilterParamIndexStringRange(lookupable, lockFactory.ObtainNew(), filterOperator); + } + return index; + } + if (filterOperator.IsInvertedRangeOperator()) + { + if (returnValueType != typeof(String)) { + return new FilterParamIndexDoubleRangeInverted(lookupable, lockFactory.ObtainNew(), filterOperator); + } + else { + return new FilterParamIndexStringRangeInverted(lookupable, lockFactory.ObtainNew(), filterOperator); + } + } + + // Handle all IN and NOT IN comparisons + if (filterOperator == FilterOperator.IN_LIST_OF_VALUES) + { + return new FilterParamIndexIn(lookupable, lockFactory.ObtainNew()); + } + if (filterOperator == FilterOperator.NOT_IN_LIST_OF_VALUES) + { + return new FilterParamIndexNotIn(lookupable, lockFactory.ObtainNew()); + } + + // Handle all bool expression + if (filterOperator == FilterOperator.BOOLEAN_EXPRESSION) + { + return new FilterParamIndexBooleanExpr(lockFactory.ObtainNew()); + } + throw new ArgumentException("Cannot create filter index instance for filter operator " + filterOperator); + } + } + +} diff --git a/NEsper.Core/NEsper.Core/filter/IndexHelper.cs b/NEsper.Core/NEsper.Core/filter/IndexHelper.cs new file mode 100755 index 000000000..ce7e9949f --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/IndexHelper.cs @@ -0,0 +1,119 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.collection; + +namespace com.espertech.esper.filter +{ + /// + /// Utility class for matching filter parameters to indizes. Matches are indicated by the index + /// + /// and the filter parameter featuring the same event property name and filter + /// operator. + /// + public class IndexHelper + { + /// + /// Find an index that matches one of the filter parameters passed. + /// The parameter type and index type match up if the property name and + /// filter operator are the same for the index and the filter parameter. + /// For instance, for a filter parameter of "count EQUALS 10", the index against property "count" with + /// operator type EQUALS will be returned, if present. + /// NOTE: The caller is expected to obtain locks, if necessary, on the collections passed in. + /// NOTE: Doesn't match non-property based index - thus bool expressions don't get found and are always entered as a + /// new index + /// + /// is the list of sorted filter parameters + /// is the collection of indexes + /// + /// A matching pair of filter parameter and index, if any matches were found. Null if no matches were found. + /// + public static Pair FindIndex( + ICollection parameters, + IList indizes) + { + foreach (FilterValueSetParam parameter in parameters) + { + FilterSpecLookupable lookupable = parameter.Lookupable; + FilterOperator @operator = parameter.FilterOperator; + + foreach (FilterParamIndexBase index in indizes) + { + // if property-based index, we prefer this in matching + if (index is FilterParamIndexLookupableBase) + { + var propBasedIndex = (FilterParamIndexLookupableBase) index; + if ((lookupable.Equals(propBasedIndex.Lookupable)) && + (@operator.Equals(propBasedIndex.FilterOperator))) + { + return new Pair(parameter, index); + } + } + else if (index is FilterParamIndexBooleanExpr && parameters.Count == 1) + { + // if bool-expression then match only if this is the last parameter, + // all others considered are higher order and sort ahead + if (@operator.Equals(FilterOperator.BOOLEAN_EXPRESSION)) + { + return new Pair(parameter, index); + } + } + } + } + + return null; + } + + /// + /// Determine among the passed in filter parameters any parameter that matches the given index on property name and + /// filter operator type. Returns null if none of the parameters matches the index. + /// + /// is the filter parameter list + /// is a filter parameter constant value index + /// filter parameter, or null if no matching parameter found. + public static FilterValueSetParam FindParameter( + ICollection parameters, + FilterParamIndexBase index) + { + if (index is FilterParamIndexLookupableBase) + { + var propBasedIndex = (FilterParamIndexLookupableBase) index; + FilterSpecLookupable indexLookupable = propBasedIndex.Lookupable; + FilterOperator indexOperator = propBasedIndex.FilterOperator; + + foreach (FilterValueSetParam parameter in parameters) + { + FilterSpecLookupable lookupable = parameter.Lookupable; + FilterOperator paramOperator = parameter.FilterOperator; + + if ((lookupable.Equals(indexLookupable)) && + (paramOperator.Equals(indexOperator))) + { + return parameter; + } + } + } + else + { + foreach (FilterValueSetParam parameter in parameters) + { + FilterOperator paramOperator = parameter.FilterOperator; + + if (paramOperator.Equals(index.FilterOperator)) + { + return parameter; + } + } + } + + return null; + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/filter/IndexTreeBuilder.cs b/NEsper.Core/NEsper.Core/filter/IndexTreeBuilder.cs new file mode 100755 index 000000000..1c731ceb7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/IndexTreeBuilder.cs @@ -0,0 +1,459 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; +using System.Threading; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.util; + +namespace com.espertech.esper.filter +{ + /// + /// Builder manipulates a tree structure consisting of + /// and instances. Filters can be added to a top node + /// (an instance of FilterHandleSetNode) via the add method. This method returns an instance + /// of which represents an element + /// in the tree path (list of indizes) that the filter callback was added to. To remove filters + /// the same IndexTreePath instance must be passed in. + /// + /// The implementation is designed to be multithread-safe in conjunction with the node classes + /// manipulated by this class. + /// + public sealed class IndexTreeBuilder + { + private IndexTreeBuilder() { } + + /// + /// Add a filter callback according to the filter specification to the top node returning information to be used to remove the filter callback. + /// + /// is the filter definition + /// is the callback to be added + /// node to be added to any subnode beneath it + /// The lock factory. + /// + /// an encapsulation of information need to allow for safe removal of the filter tree. + /// + public static ArrayDeque[] Add( + FilterValueSet filterValueSet, + FilterHandle filterCallback, + FilterHandleSetNode topNode, + FilterServiceGranularLockFactory lockFactory) + { + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled)) + { + Log.Debug(".add (" + Thread.CurrentThread.ManagedThreadId + ") Adding filter callback, " + + " topNode=" + topNode + + " filterCallback=" + filterCallback); + } + + ArrayDeque[] treePathInfo; + if (filterValueSet.Parameters.Length == 0) + { + treePathInfo = AllocateTreePath(1); + treePathInfo[0] = new ArrayDeque(1); + AddToNode(new ArrayDeque(1), filterCallback, topNode, treePathInfo[0], lockFactory); + } + else + { + treePathInfo = AllocateTreePath(filterValueSet.Parameters.Length); + var remainingParameters = new ArrayDeque(4); + for (int i = 0; i < filterValueSet.Parameters.Length; i++) + { + treePathInfo[i] = new ArrayDeque(filterValueSet.Parameters[i].Length); + remainingParameters.Clear(); + remainingParameters.AddAll(filterValueSet.Parameters[i]); + AddToNode(remainingParameters, filterCallback, topNode, treePathInfo[i], lockFactory); + } + } + + return treePathInfo; + } + + /// + /// Remove an filterCallback from the given top node. The IndexTreePath instance passed in must be the same as obtained when the same filterCallback was added. + /// + /// Type of the event. + /// filter callback to be removed + /// encapsulates information need to allow for safe removal of the filterCallback + /// The top tree node beneath which the filterCallback was added + public static void Remove( + EventType eventType, + FilterHandle filterCallback, + EventTypeIndexBuilderIndexLookupablePair[] treePathInfo, + FilterHandleSetNode topNode) + { + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled)) + { + Log.Debug(".remove (" + Thread.CurrentThread.ManagedThreadId + ") Removing filterCallback " + + " type " + eventType.Name + + " topNode=" + topNode + + " filterCallback=" + filterCallback); + } + + RemoveFromNode(filterCallback, topNode, treePathInfo, 0); + } + + /// + /// Add to the current node building up the tree path information. + /// + /// The remaining parameters. + /// The filter callback. + /// is the node to add to + /// is filled with information about which indizes were chosen to add the filter to + /// The lock factory. + private static void AddToNode( + ArrayDeque remainingParameters, + FilterHandle filterCallback, + FilterHandleSetNode currentNode, + ArrayDeque 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 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); + } + } + + // Remove an filterCallback from the current node, return true if the node is the node is empty now + private static bool RemoveFromNode( + FilterHandle filterCallback, + FilterHandleSetNode currentNode, + EventTypeIndexBuilderIndexLookupablePair[] treePathInfo, + int treePathPosition) + { + var nextPair = treePathPosition < treePathInfo.Length ? treePathInfo[treePathPosition++] : null; + + // No remaining filter parameters + if (nextPair == null) + { + using (currentNode.NodeRWLock.AcquireWriteLock()) + { + var isRemoved = currentNode.Remove(filterCallback); + var isEmpty = currentNode.IsEmpty(); + + if (!isRemoved) + { + Log.Warn(".removeFromNode (" + Thread.CurrentThread.ManagedThreadId + ") Could not find the filterCallback to be removed within the supplied node , node=" + + currentNode + " filterCallback=" + filterCallback); + } + + return isEmpty; + } + } + + // Remove from index + var nextIndex = nextPair.Index; + var filteredForValue = nextPair.Lookupable; + + using (currentNode.NodeRWLock.AcquireWriteLock()) + { + var isEmpty = RemoveFromIndex(filterCallback, nextIndex, treePathInfo, treePathPosition, filteredForValue); + + if (!isEmpty) + { + return false; + } + + // Remove the index if the index is now empty + if (nextIndex.Count == 0) + { + var isRemoved = currentNode.Remove(nextIndex); + + if (!isRemoved) + { + Log.Warn(".removeFromNode (" + Thread.CurrentThread.ManagedThreadId + ") Could not find the index in index list for removal, index=" + + nextIndex + " filterCallback=" + filterCallback); + return false; + } + } + + return currentNode.IsEmpty(); + } + } + + // Remove filterCallback from index, returning true if index empty after removal + private static bool RemoveFromIndex( + FilterHandle filterCallback, + FilterParamIndexBase index, + EventTypeIndexBuilderIndexLookupablePair[] treePathInfo, + int treePathPosition, + Object filterForValue) + { + using (index.ReadWriteLock.AcquireWriteLock()) + { + EventEvaluator eventEvaluator = index[filterForValue]; + + if (eventEvaluator == null) + { + Log.Warn(".removeFromIndex ({0}) Could not find the filterCallback value in index, index={1} value={2} filterCallback={3}", + Thread.CurrentThread.ManagedThreadId, index, filterForValue, filterCallback); + return false; + } + + if (eventEvaluator is FilterHandleSetNode) + { + var node = (FilterHandleSetNode)eventEvaluator; + var isEmptyX = RemoveFromNode(filterCallback, node, treePathInfo, treePathPosition); + if (isEmptyX) + { + // Since we are holding a write lock to this index, there should not be a chance that + // another thread had been adding anything to this FilterHandleSetNode + index.Remove(filterForValue); + } + + return (index.Count == 0); + } + + var nextIndex = (FilterParamIndexBase)eventEvaluator; + var nextPair = treePathPosition < treePathInfo.Length ? treePathInfo[treePathPosition++] : null; + + if (nextPair == null) + { + Log.Fatal(".removeFromIndex Expected an inner index to this index, this=" + filterCallback); + Debug.Assert(false); + return false; + } + + if (nextPair.Index != nextIndex) + { + Log.Fatal(".removeFromIndex Expected an index for filterCallback that differs from the found index, this=" + filterCallback + + " expected=" + nextPair.Index); + Debug.Assert(false); + return false; + } + + var nextExpressionValue = nextPair.Lookupable; + + var isEmpty = RemoveFromIndex(filterCallback, nextPair.Index, treePathInfo, treePathPosition, nextExpressionValue); + if (isEmpty) + { + // Since we are holding a write lock to this index, there should not be a chance that + // another thread had been adding anything to this FilterHandleSetNode + index.Remove(filterForValue); + } + var size = index.Count; + + return (size == 0); + } + } + + /// + /// Add to an index the value to filter for. + /// + /// The remaining parameters. + /// The filter callback. + /// is the index to add to + /// is the filter parameter value to add + /// is the specification to fill on where is was added + /// The lock factory. + private static void AddToIndex( + ArrayDeque remainingParameters, + FilterHandle filterCallback, + FilterParamIndexBase index, + Object filterForValue, + ArrayDeque 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); + } + } + + /// + /// Add filter callback to an event evaluator, which could be either an index node or a set node. + /// + /// The remaining parameters. + /// The filter callback. + /// to add the filterCallback to. + /// is for holding the information on where the add occured + /// The lock factory. + /// + /// bool indicating if the eventEvaluator was successfully added + /// + private static bool AddToEvaluator( + ArrayDeque remainingParameters, + FilterHandle filterCallback, + EventEvaluator eventEvaluator, + ArrayDeque treePathInfo, + FilterServiceGranularLockFactory lockFactory) + { + if (eventEvaluator is FilterHandleSetNode) + { + var node = (FilterHandleSetNode)eventEvaluator; + AddToNode(remainingParameters, filterCallback, node, treePathInfo, lockFactory); + return true; + } + + // Check if the next index matches any of the remaining filterCallback parameters + var nextIndex = (FilterParamIndexBase)eventEvaluator; + + var parameter = IndexHelper.FindParameter(remainingParameters, nextIndex); + if (parameter != null) + { + remainingParameters.Remove(parameter); + treePathInfo.Add(new EventTypeIndexBuilderIndexLookupablePair(nextIndex, parameter.FilterForValue)); + AddToIndex(remainingParameters, filterCallback, nextIndex, parameter.FilterForValue, treePathInfo, lockFactory); + return true; + } + + // This eventEvaluator does not work with any of the remaining filter parameters + return false; + } + + private static String PrintRemainingParameters(IEnumerable remainingParameters) + { + var buffer = new StringBuilder(); + + var count = 0; + foreach (var parameter in remainingParameters) + { + buffer.Append(" Param(").Append(count).Append(')'); + buffer.Append(" property=").Append(parameter.Lookupable); + buffer.Append(" operator=").Append(parameter.FilterOperator); + buffer.Append(" value=").Append(parameter.FilterForValue); + count++; + } + + return buffer.ToString(); + } + + private static ArrayDeque[] AllocateTreePath(int size) + { + return new ArrayDeque[size]; + } + + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + } +} diff --git a/NEsper.Core/NEsper.Core/filter/Range.cs b/NEsper.Core/NEsper.Core/filter/Range.cs new file mode 100755 index 000000000..c29c4c154 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/Range.cs @@ -0,0 +1,16 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.filter +{ + public interface Range + { + object LowEndpoint { get; } + object HighEndpoint { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/filter/RangeValueContextProp.cs b/NEsper.Core/NEsper.Core/filter/RangeValueContextProp.cs new file mode 100755 index 000000000..34d54452c --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/RangeValueContextProp.cs @@ -0,0 +1,44 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.pattern; + +namespace com.espertech.esper.filter +{ + [Serializable] + public class RangeValueContextProp : FilterSpecParamRangeValue { + [NonSerialized] + private readonly EventPropertyGetter _getter; + + public RangeValueContextProp(EventPropertyGetter getter) { + _getter = getter; + } + + public Object GetFilterValue(MatchedEventMap matchedEvents, ExprEvaluatorContext exprEvaluatorContext) { + if (exprEvaluatorContext.ContextProperties == null) { + return null; + } + Object @object = _getter.Get(exprEvaluatorContext.ContextProperties); + if (@object == null) { + return null; + } + + if (@object is String) { + return @object; + } + + return @object.AsDouble(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/filter/RangeValueDouble.cs b/NEsper.Core/NEsper.Core/filter/RangeValueDouble.cs new file mode 100755 index 000000000..4829ad334 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/RangeValueDouble.cs @@ -0,0 +1,71 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.pattern; + + +namespace com.espertech.esper.filter +{ + /// + /// A Double-typed value as a filter parameter representing a range. + /// + public class RangeValueDouble : FilterSpecParamRangeValue + { + private readonly double _doubleValue; + + /// Ctor. + /// is the value of the range endpoint + public RangeValueDouble(double doubleValue) + { + _doubleValue = doubleValue; + } + + public object GetFilterValue(MatchedEventMap matchedEvents, ExprEvaluatorContext exprEvaluatorContext) + { + return _doubleValue; + } + + /// Returns the constant value. + /// constant + public double DoubleValue + { + get { return _doubleValue; } + } + + public override String ToString() + { + return _doubleValue.ToString(); + } + + public override bool Equals(Object obj) + { + if (this == obj) + { + return true; + } + + if (!(obj is RangeValueDouble)) + { + return false; + } + + RangeValueDouble other = (RangeValueDouble) obj; + return other._doubleValue == this._doubleValue; + } + + public override int GetHashCode() + { + long temp = _doubleValue != +0.0d ? BitConverter.DoubleToInt64Bits(_doubleValue) : 0L; + return (int) (temp ^ (temp >> 32)); + } + } +} diff --git a/NEsper.Core/NEsper.Core/filter/RangeValueEventProp.cs b/NEsper.Core/NEsper.Core/filter/RangeValueEventProp.cs new file mode 100755 index 000000000..124c4da68 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/RangeValueEventProp.cs @@ -0,0 +1,101 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.pattern; + +namespace com.espertech.esper.filter +{ + /// + /// An event property as a filter parameter representing a range. + /// + public class RangeValueEventProp : FilterSpecParamRangeValue + { + private readonly String _resultEventAsName; + private readonly String _resultEventProperty; + + /// Ctor. + /// is the event tag + /// is the event property name + public RangeValueEventProp(String resultEventAsName, String resultEventProperty) + { + _resultEventAsName = resultEventAsName; + _resultEventProperty = resultEventProperty; + } + + public Object GetFilterValue(MatchedEventMap matchedEvents, ExprEvaluatorContext exprEvaluatorContext) + { + var theEvent = matchedEvents.GetMatchingEventByTag(_resultEventAsName); + if (theEvent == null) + { + throw new IllegalStateException( + "Matching event named " + + '\'' + _resultEventAsName + "' not found in event result set"); + } + + var value = theEvent.Get(_resultEventProperty); + if (value == null) + { + return null; + } + return value.AsDouble(); + } + + /// + /// Returns the tag name or stream name to use for the event property. + /// + /// tag name + public string ResultEventAsName + { + get { return _resultEventAsName; } + } + + /// Returns the name of the event property. + /// event property name + public string ResultEventProperty + { + get { return _resultEventProperty; } + } + + public override String ToString() + { + return "resultEventProp=" + _resultEventAsName + '.' + _resultEventProperty; + } + + public override bool Equals(Object obj) + { + if (this == obj) + { + return true; + } + + if (!(obj is RangeValueEventProp)) + { + return false; + } + + var other = (RangeValueEventProp) obj; + if ( (other._resultEventAsName.Equals(_resultEventAsName)) && + (other._resultEventProperty.Equals(_resultEventProperty))) + { + return true; + } + + return false; + } + + public override int GetHashCode() + { + return _resultEventProperty.GetHashCode(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/filter/RangeValueEventPropIndexed.cs b/NEsper.Core/NEsper.Core/filter/RangeValueEventPropIndexed.cs new file mode 100755 index 000000000..d2f162533 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/RangeValueEventPropIndexed.cs @@ -0,0 +1,124 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.pattern; + +namespace com.espertech.esper.filter +{ + /// + /// An event property as a filter parameter representing a range. + /// + public class RangeValueEventPropIndexed : FilterSpecParamRangeValue + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly String _resultEventAsName; + private readonly int _resultEventIndex; + private readonly String _resultEventProperty; + private readonly String _statementName; + + /// + /// Ctor. + /// + /// is the event tag + /// index for event + /// is the event property name + /// Name of the statement. + public RangeValueEventPropIndexed(String resultEventAsName, int resultEventIndex, String resultEventProperty, String statementName) + { + _resultEventAsName = resultEventAsName; + _resultEventIndex = resultEventIndex; + _resultEventProperty = resultEventProperty; + _statementName = statementName; + } + + /// Returns the index. + /// index + public int ResultEventIndex + { + get { return _resultEventIndex; } + } + + public object GetFilterValue(MatchedEventMap matchedEvents, ExprEvaluatorContext exprEvaluatorContext) + { + EventBean[] events = (EventBean[])matchedEvents.GetMatchingEventAsObjectByTag(_resultEventAsName); + + if (events == null) + { + Log.Warn("Matching events for tag '" + _resultEventAsName + "' returned a null result, using null value in filter criteria, for statement '" + _statementName + "'"); + return null; + } + if (_resultEventIndex > (events.Length - 1)) + { + Log.Warn("Matching events for tag '" + _resultEventAsName + "' returned no result for index " + _resultEventIndex + " at array length " + events.Length + ", using null value in filter criteria, for statement '" + _statementName + "'"); + return null; + } + + var value = events[_resultEventIndex].Get(_resultEventProperty); + if (value == null) + { + return null; + } + return value.AsDouble(); + } + + /// Returns the tag name or stream name to use for the event property. + /// tag name + public string ResultEventAsName + { + get { return _resultEventAsName; } + } + + /// Returns the name of the event property. + /// event property name + public string ResultEventProperty + { + get { return _resultEventProperty; } + } + + public override String ToString() + { + return "resultEventProp=" + _resultEventAsName + '.' + _resultEventProperty; + } + + public override bool Equals(Object obj) + { + if (this == obj) + { + return true; + } + + if (!(obj is RangeValueEventPropIndexed)) + { + return false; + } + + RangeValueEventPropIndexed other = (RangeValueEventPropIndexed)obj; + if ((other._resultEventAsName.Equals(this._resultEventAsName)) && + (other._resultEventProperty.Equals(this._resultEventProperty) && + (other._resultEventIndex == _resultEventIndex))) + { + return true; + } + + return false; + } + + public override int GetHashCode() + { + return _resultEventProperty.GetHashCode(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/filter/RangeValueString.cs b/NEsper.Core/NEsper.Core/filter/RangeValueString.cs new file mode 100755 index 000000000..4e76d57bd --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/RangeValueString.cs @@ -0,0 +1,67 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.pattern; + +namespace com.espertech.esper.filter +{ + /// + /// A String-typed value as a filter parameter representing a range. + /// + [Serializable] + public class RangeValueString : FilterSpecParamRangeValue + { + private readonly String _theStringValue; + + /// Ctor. + /// is the value of the range endpoint + public RangeValueString(String theStringValue) + { + _theStringValue = theStringValue; + } + + public object GetFilterValue(MatchedEventMap matchedEvents, + ExprEvaluatorContext exprEvaluatorContext) + { + return _theStringValue; + } + + public int FilterHash + { + get { return _theStringValue.GetHashCode(); } + } + + public override String ToString() + { + return _theStringValue; + } + + public bool Equals(RangeValueString other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return Equals(other._theStringValue, _theStringValue); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != typeof (RangeValueString)) return false; + return Equals((RangeValueString) obj); + } + + public override int GetHashCode() + { + return (_theStringValue != null ? _theStringValue.GetHashCode() : 0); + } + } +} diff --git a/NEsper.Core/NEsper.Core/filter/StringRange.cs b/NEsper.Core/NEsper.Core/filter/StringRange.cs new file mode 100755 index 000000000..81d6c996b --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/StringRange.cs @@ -0,0 +1,99 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.filter +{ + /// Holds a range of double values with a minimum (start) value and a maximum (end) value. + public sealed class StringRange : Range + { + private readonly String _min; + private readonly String _max; + private readonly int _hashCode; + + /// Constructor - takes range endpoints. + /// is the low endpoint + /// is the high endpoint + public StringRange(String min, String max) + { + _min = min; + _max = max; + + if ((min != null) && (max != null)) + { + if (min.CompareTo(max) > 0) + { + _max = min; + _min = max; + } + } + + unchecked + { + _hashCode = (min != null ? min.GetHashCode() : 0); + _hashCode = (_hashCode * 397) ^ (max != null ? max.GetHashCode() : 0); + _hashCode = (_hashCode * 397) ^ _hashCode; + } + } + + public object LowEndpoint + { + get { return _min; } + } + + public object HighEndpoint + { + get { return _max; } + } + + /// Returns low endpoint. + /// low endpoint + public string Min + { + get { return _min; } + } + + /// Returns high endpoint. + /// high endpoint + public string Max + { + get { return _max; } + } + + public bool Equals(StringRange other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return Equals(other._min, _min) && Equals(other._max, _max); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != typeof (StringRange)) return false; + return Equals((StringRange) obj); + } + + public override int GetHashCode() + { + unchecked { + int result = (_min != null ? _min.GetHashCode() : 0); + result = (result*397) ^ (_max != null ? _max.GetHashCode() : 0); + result = (result*397) ^ _hashCode; + return result; + } + } + + public override String ToString() + { + return string.Format("StringRange min={0} max={1}", _min, _max); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/filter/StringRangeComparator.cs b/NEsper.Core/NEsper.Core/filter/StringRangeComparator.cs new file mode 100755 index 000000000..9fe651c18 --- /dev/null +++ b/NEsper.Core/NEsper.Core/filter/StringRangeComparator.cs @@ -0,0 +1,61 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +namespace com.espertech.esper.filter +{ + /// + /// Comparator for DoubleRange values. + /// + /// Sorts double ranges as this: + /// sort by min asc, max asc. I.e. same minimum value sorts maximum value ascending. + /// + public sealed class StringRangeComparator : IComparer + { + public int Compare(StringRange r1, StringRange r2) + { + if (r1.Min == null) + { + if (r2.Min != null) + { + return -1; + } + } + else + { + if (r2.Min == null) + { + return 1; + } + int comp = r1.Min.CompareTo(r2.Min); + if (comp != 0) + { + return comp; + } + } + + if (r1.Max == null) + { + if (r2.Max != null) + { + return 1; + } + return 0; + } + else + { + if (r2.Max == null) + { + return 0; + } + return r1.Max.CompareTo(r2.Max); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/linq/CascadeObservableCollection.cs b/NEsper.Core/NEsper.Core/linq/CascadeObservableCollection.cs new file mode 100755 index 000000000..6ae5855f4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/linq/CascadeObservableCollection.cs @@ -0,0 +1,137 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.ComponentModel; + +namespace com.espertech.esper.linq +{ + public class CascadeObservableCollection : DisposableObservableCollection + { + /// + /// Gets or sets a value indicating whether [dispose source]. + /// + /// true if [dispose source]; otherwise, false. + public bool DisposeSource { get; private set; } + + /// + /// Gets or sets the source collection. + /// + /// The source collection. + public ObservableCollection SourceCollection { get; private set; } + + /// + /// Gets or sets the transform. + /// + /// The transform. + public Func Transform { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// The source collection. + /// The transform. + /// if set to true [dispose source]. + public CascadeObservableCollection(ObservableCollection sourceCollection, Func transform, bool disposeSource) + { + DisposeSource = disposeSource; + Transform = transform; + SourceCollection = sourceCollection; + SourceCollection.CollectionChanged += OnSourceCollectionChanged; + + foreach( var item in SourceCollection ) { + Add(transform.Invoke(item)); + } + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public override void Dispose() + { + if ( DisposeSource ) { + if ( SourceCollection != null ) { + if (SourceCollection is IDisposable) { + ((IDisposable) SourceCollection).Dispose(); + SourceCollection = null; + } + } + } + } + + /// + /// Called when [source collection changed]. + /// + /// The sender. + /// The instance containing the event data. + private void OnSourceCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + switch (e.Action) { + case NotifyCollectionChangedAction.Add: + { + var newItemList = new List(); + for (int ii = e.NewItems.Count - 1; ii >= 0; ii--) { + var newItem = (TSource) e.NewItems[ii]; + var newItemTransform = Transform(newItem); + InsertItem(e.NewStartingIndex + ii, newItemTransform); + newItemList.Add(newItemTransform); + } + + newItemList.Reverse(); + OnPropertyChanged(new PropertyChangedEventArgs("Count")); + OnPropertyChanged(new PropertyChangedEventArgs("Item[]")); + OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, newItemList, e.NewStartingIndex)); + break; + } + case NotifyCollectionChangedAction.Remove: + { + var oldItemList = new List(); + for (int ii = e.OldItems.Count - 1; ii >= 0; ii--) { + oldItemList.Add(Items[ii]); + RemoveAt(e.OldStartingIndex + ii); + } + + oldItemList.Reverse(); + OnPropertyChanged(new PropertyChangedEventArgs("Count")); + OnPropertyChanged(new PropertyChangedEventArgs("Item[]")); + OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, oldItemList, e.OldStartingIndex)); + break; + } + case NotifyCollectionChangedAction.Replace: + { + for (int ii = e.NewItems.Count - 1; ii >= 0; ii--) { + var newItem = Transform((TSource)e.NewItems[ii]); + var oldItem = Items[e.NewStartingIndex + ii]; + Items[e.NewStartingIndex + ii] = newItem; + OnCollectionChanged(new NotifyCollectionChangedEventArgs( + NotifyCollectionChangedAction.Replace, + newItem, + oldItem, + e.NewStartingIndex + ii)); + } + break; + } + case NotifyCollectionChangedAction.Move: + throw new NotSupportedException(); + case NotifyCollectionChangedAction.Reset: + Items.Clear(); + foreach( var item in SourceCollection ) { + Items.Add(Transform(item)); + } + + OnPropertyChanged(new PropertyChangedEventArgs("Count")); + OnPropertyChanged(new PropertyChangedEventArgs("Item[]")); + OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); + break; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/linq/DisposableObservableCollection.cs b/NEsper.Core/NEsper.Core/linq/DisposableObservableCollection.cs new file mode 100755 index 000000000..edf9fdaac --- /dev/null +++ b/NEsper.Core/NEsper.Core/linq/DisposableObservableCollection.cs @@ -0,0 +1,24 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.ObjectModel; + +namespace com.espertech.esper.linq +{ + public class DisposableObservableCollection : ObservableCollection, IDisposable + { + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public virtual void Dispose() + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/linq/EsperQuery.cs b/NEsper.Core/NEsper.Core/linq/EsperQuery.cs new file mode 100755 index 000000000..b982a1cb2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/linq/EsperQuery.cs @@ -0,0 +1,77 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.client.soda; + +namespace com.espertech.esper.linq +{ + /// + /// Each variation of the EsperQuery must result in a statement that can be submitted + /// to the administrator for compilation and execution. + /// + + public class EsperQuery + { + /// + /// Gets the service provider associated with this request. + /// + /// The service provider. + public EPServiceProvider ServiceProvider { get; private set; } + /// + /// Gets the statement associated with this request. + /// + /// The statement. + public EPStatement Statement { get; private set; } + /// + /// Gets the statement object model. + /// + /// The object model. + public EPStatementObjectModel ObjectModel { get; private set;} + + /// + /// Returns true if the query has been compiled into a statement. + /// + public bool IsCompiled + { + get { return Statement != null; } + } + + /// + /// Compiles the statement object model into a true statement. + /// + public void Compile() + { + if (Statement == null) { + Statement = ServiceProvider.EPAdministrator.Create(ObjectModel); + } + } + + /// + /// Initializes a new instance of the class. + /// + /// The service provider. + /// The object model. + public EsperQuery(EPServiceProvider serviceProvider, EPStatementObjectModel objectModel) + { + ServiceProvider = serviceProvider; + ObjectModel = objectModel; + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override string ToString() + { + return string.Format("ServiceProvider: {0}, Statement: {1}, ObjectModel: {2}", ServiceProvider, Statement, ObjectModel.ToEPL()); + } + } +} diff --git a/NEsper.Core/NEsper.Core/linq/EsperQueryExtensions.cs b/NEsper.Core/NEsper.Core/linq/EsperQueryExtensions.cs new file mode 100755 index 000000000..44173c84f --- /dev/null +++ b/NEsper.Core/NEsper.Core/linq/EsperQueryExtensions.cs @@ -0,0 +1,616 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; + +using com.espertech.esper.client; +using com.espertech.esper.client.soda; +using com.espertech.esper.compat; +using com.espertech.esper.type; + +namespace com.espertech.esper.linq +{ + public static class EsperQueryExtensions + { + #region Select + /// + /// Selects the specified "item" from the esper query. + /// + /// + /// The esper query. + /// + public static DisposableObservableCollection Select(this EsperQuery esperQuery) + { + esperQuery.Compile(); + return esperQuery.Statement.AsObservableCollection(true); + } + + /// + /// Selects the specified "item" from the esper query. + /// + /// The type of the 1. + /// The type of the 2. + /// The esper query. + /// The expression. + /// + public static DisposableObservableCollection Select(this EsperQuery esperQuery, Expression> expression) + { + var transform = expression.Compile(); + esperQuery.Compile(); + if (typeof(T1) == typeof(T2)) { + return esperQuery.Statement.AsObservableCollection(true); + } + + return new CascadeObservableCollection( + esperQuery.Statement.AsObservableCollection(true), + transform, + true); + } + #endregion + + #region AddProperty + /// + /// Add a property / column to the query. + /// + /// + /// The esper query. + /// The property. + /// + public static EsperQuery AddProperty(this EsperQuery esperQuery, String property) + { + var parentObjectModel = esperQuery.ObjectModel; + var deriveObjectModel = parentObjectModel.ShallowCopy(); + using (ScopedInstance.Set(deriveObjectModel)) { + deriveObjectModel.SelectClause.Add(property); + return new EsperQuery(esperQuery.ServiceProvider, deriveObjectModel); + } + } + + /// + /// Add a property / column to the query. + /// + /// + /// The esper query. + /// The property. + /// Name of as property. + /// + public static EsperQuery AddProperty(this EsperQuery esperQuery, String property, String asPropertyName) + { + var parentObjectModel = esperQuery.ObjectModel; + var deriveObjectModel = parentObjectModel.ShallowCopy(); + using (ScopedInstance.Set(deriveObjectModel)) + { + deriveObjectModel.SelectClause.AddWithAsProvidedName(property, asPropertyName); + return new EsperQuery(esperQuery.ServiceProvider, deriveObjectModel); + } + } + + /// + /// Adds the property. + /// + /// + /// The esper query. + /// Name of the property. + /// The expression. + /// + public static EsperQuery AddProperty(this EsperQuery esperQuery, String propertyName, System.Linq.Expressions.Expression> expression) + { + var parentObjectModel = esperQuery.ObjectModel; + var deriveObjectModel = parentObjectModel.ShallowCopy(); + using (ScopedInstance.Set(deriveObjectModel)) + { + deriveObjectModel.SelectClause.Add( + LinqToSoda.LinqToSodaExpression(expression), + propertyName); + return new EsperQuery(esperQuery.ServiceProvider, deriveObjectModel); + } + } + #endregion + + /// + /// Joins the specified outer. + /// + /// The type of the outer. + /// The type of the inner. + /// The type of the key. + /// The type of the result. + /// The outer. + /// The inner. + /// The outer key selector. + /// The inner key selector. + /// The result selector. + /// + public static EsperQuery Join( + this EsperQuery outer, EsperQuery inner, + System.Linq.Expressions.Expression> outerKeySelector, + System.Linq.Expressions.Expression> innerKeySelector, + System.Linq.Expressions.Expression> resultSelector) + { + var parentObjectModel = outer.ObjectModel; + var deriveObjectModel = new EPStatementObjectModel(); + + using (ScopedInstance.Set(deriveObjectModel)) { + deriveObjectModel.Annotations = parentObjectModel.Annotations; + + var innerKey = LinqToSoda.LinqToSodaExpression(innerKeySelector); + var outerKey = LinqToSoda.LinqToSodaExpression(outerKeySelector); + + deriveObjectModel.FromClause = new FromClause( + outer.ObjectModel.FromClause.Streams.Concat( + inner.ObjectModel.FromClause.Streams).ToArray()); + + var parametersArray = resultSelector.Parameters.ToArray(); + for (int ii = 0; ii < parametersArray.Length ; ii++ ) { + deriveObjectModel.FromClause.Streams[ii].StreamName = + parametersArray[ii].Name; + } + + deriveObjectModel.FromClause.OuterJoinQualifiers = new List(); + deriveObjectModel.FromClause.OuterJoinQualifiers.Add( + new OuterJoinQualifier( + OuterJoinType.LEFT, + outerKey, + innerKey, + new PropertyValueExpressionPair[0])); + + deriveObjectModel.SelectClause = LinqToSoda.LinqToSelectClause(resultSelector); + + var toEPL = deriveObjectModel.ToEPL(); + + return new EsperQuery(outer.ServiceProvider, deriveObjectModel); + } + } + + #region Where + /// + /// Constrains the specified esper query. + /// + /// + /// The esper query. + /// The expression. + /// + public static EsperQuery Where(this EsperQuery esperQuery, System.Linq.Expressions.Expression> expression) + { + var parentObjectModel = esperQuery.ObjectModel; + var deriveObjectModel = parentObjectModel.ShallowCopy(); + + // Adapt or set the where clause according to the expression contents + using (ScopedInstance.Set(deriveObjectModel)) + { + var sodaExpression = LinqToSoda.LinqToSodaExpression(expression); + deriveObjectModel.WhereClause = deriveObjectModel.WhereClause == null + ? sodaExpression + : Expressions.And(deriveObjectModel.WhereClause, sodaExpression); + deriveObjectModel.FromClause.Streams[0].StreamName = + expression.Parameters[0].Name; + + return new EsperQuery(esperQuery.ServiceProvider, deriveObjectModel); + } + } + #endregion + + #region Having + /// + /// Constrains the specified esper query. + /// + /// + /// The esper query. + /// The expression. + /// + public static EsperQuery Having(this EsperQuery esperQuery, System.Linq.Expressions.Expression> expression) + { + var parentObjectModel = esperQuery.ObjectModel; + var deriveObjectModel = parentObjectModel.ShallowCopy(); + + // Adapt or set the where clause according to the expression contents + using (ScopedInstance.Set(deriveObjectModel)) + { + var sodaExpression = LinqToSoda.LinqToSodaExpression(expression); + deriveObjectModel.HavingClause = deriveObjectModel.HavingClause == null + ? sodaExpression + : Expressions.And(deriveObjectModel.WhereClause, sodaExpression); + deriveObjectModel.FromClause.Streams[0].StreamName = + expression.Parameters[0].Name; + + return new EsperQuery(esperQuery.ServiceProvider, deriveObjectModel); + } + } + #endregion + + #region OutputLimit + + /// + /// Limit the output of the query. + /// + /// + /// The esper query. + /// The time period expression. + /// + public static EsperQuery OutputLimit(this EsperQuery esperQuery, TimePeriodExpression timePeriodExpression) + { + var parentObjectModel = esperQuery.ObjectModel; + var deriveObjectModel = parentObjectModel.ShallowCopy(); + deriveObjectModel.OutputLimitClause = OutputLimitClause.Create(timePeriodExpression); + return new EsperQuery(esperQuery.ServiceProvider, deriveObjectModel); + } + + /// + /// Limit the output of the query. + /// + /// + /// The esper query. + /// The selector. + /// The time period expression. + /// + public static EsperQuery OutputLimit(this EsperQuery esperQuery, OutputLimitSelector selector, TimePeriodExpression timePeriodExpression) + { + var parentObjectModel = esperQuery.ObjectModel; + var deriveObjectModel = parentObjectModel.ShallowCopy(); + deriveObjectModel.OutputLimitClause = OutputLimitClause.Create(selector, timePeriodExpression); + return new EsperQuery(esperQuery.ServiceProvider, deriveObjectModel); + } + + /// + /// Limit the output of the query. + /// + /// + /// The esper query. + /// The selector. + /// The frequency. + /// + public static EsperQuery OutputLimit(this EsperQuery esperQuery, OutputLimitSelector selector, double frequency) + { + var parentObjectModel = esperQuery.ObjectModel; + var deriveObjectModel = parentObjectModel.ShallowCopy(); + deriveObjectModel.OutputLimitClause = OutputLimitClause.Create(selector, frequency); + return new EsperQuery(esperQuery.ServiceProvider, deriveObjectModel); + } + + /// + /// Limit the output of the query. + /// + /// + /// The esper query. + /// is the events to select + /// is the variable providing the output limit frequency + /// clause + public static EsperQuery OutputLimit(this EsperQuery esperQuery, OutputLimitSelector selector, String frequencyVariable) + { + var parentObjectModel = esperQuery.ObjectModel; + var deriveObjectModel = parentObjectModel.ShallowCopy(); + deriveObjectModel.OutputLimitClause = OutputLimitClause.Create(selector, frequencyVariable); + return new EsperQuery(esperQuery.ServiceProvider, deriveObjectModel); + } + + /// + /// Limit the output of the query. + /// + /// + /// The esper query. + /// a frequency to output at + /// clause + public static EsperQuery OutputLimit(this EsperQuery esperQuery, double frequency) + { + var parentObjectModel = esperQuery.ObjectModel; + var deriveObjectModel = parentObjectModel.ShallowCopy(); + deriveObjectModel.OutputLimitClause = OutputLimitClause.Create(frequency); + return new EsperQuery(esperQuery.ServiceProvider, deriveObjectModel); + } + + /// + /// Limit the output of the query. + /// + /// + /// The esper query. + /// is the variable name providing output rate frequency values + /// clause + public static EsperQuery OutputLimit(this EsperQuery esperQuery, String frequencyVariable) + { + var parentObjectModel = esperQuery.ObjectModel; + var deriveObjectModel = parentObjectModel.ShallowCopy(); + deriveObjectModel.OutputLimitClause = OutputLimitClause.Create(frequencyVariable); + return new EsperQuery(esperQuery.ServiceProvider, deriveObjectModel); + } + + /// + /// Limit the output of the query. + /// + /// + /// The esper query. + /// the expression that returns true to trigger output + /// clause + public static EsperQuery OutputLimit(this EsperQuery esperQuery, client.soda.Expression whenExpression) + { + var parentObjectModel = esperQuery.ObjectModel; + var deriveObjectModel = parentObjectModel.ShallowCopy(); + deriveObjectModel.OutputLimitClause = OutputLimitClause.Create(whenExpression); + return new EsperQuery(esperQuery.ServiceProvider, deriveObjectModel); + } + + /// + /// Limit the output of the query. + /// + /// + /// The esper query. + /// the crontab schedule parameters + /// clause + public static EsperQuery OutputLimit(this EsperQuery esperQuery, client.soda.Expression[] scheduleParameters) + { + var parentObjectModel = esperQuery.ObjectModel; + var deriveObjectModel = parentObjectModel.ShallowCopy(); + deriveObjectModel.OutputLimitClause = OutputLimitClause.CreateSchedule(scheduleParameters); + return new EsperQuery(esperQuery.ServiceProvider, deriveObjectModel); + } + + #endregion + + #region RowLimit + /// + /// Limits the number of rows. + /// + /// + /// The esper query. + /// The num rows variable. + /// + public static EsperQuery RowLimit(this EsperQuery esperQuery, String numRowsVariable) + { + var parentObjectModel = esperQuery.ObjectModel; + var deriveObjectModel = parentObjectModel.ShallowCopy(); + deriveObjectModel.RowLimitClause = RowLimitClause.Create(numRowsVariable); + return new EsperQuery(esperQuery.ServiceProvider, deriveObjectModel); + } + + /// + /// Limits the number of rows. + /// + /// + /// The esper query. + /// The num rows variable. + /// The offset variable. + /// + public static EsperQuery RowLimit(this EsperQuery esperQuery, String numRowsVariable, String offsetVariable) + { + var parentObjectModel = esperQuery.ObjectModel; + var deriveObjectModel = parentObjectModel.ShallowCopy(); + deriveObjectModel.RowLimitClause = RowLimitClause.Create(numRowsVariable, offsetVariable); + return new EsperQuery(esperQuery.ServiceProvider, deriveObjectModel); + } + + /// + /// Limits the number of rows. + /// + /// + /// The esper query. + /// The num rows. + /// + public static EsperQuery RowLimit(this EsperQuery esperQuery, int numRows) + { + var parentObjectModel = esperQuery.ObjectModel; + var deriveObjectModel = parentObjectModel.ShallowCopy(); + deriveObjectModel.RowLimitClause = RowLimitClause.Create(numRows); + return new EsperQuery(esperQuery.ServiceProvider, deriveObjectModel); + } + + /// + /// Limits the number of rows. + /// + /// + /// The esper query. + /// The num rows. + /// The offset. + /// + public static EsperQuery RowLimit(this EsperQuery esperQuery, int numRows, int offset) + { + var parentObjectModel = esperQuery.ObjectModel; + var deriveObjectModel = parentObjectModel.ShallowCopy(); + deriveObjectModel.RowLimitClause = RowLimitClause.Create(numRows, offset); + return new EsperQuery(esperQuery.ServiceProvider, deriveObjectModel); + } + #endregion + + #region OrderBy + /// + /// Orders the results of the expression. + /// + /// The type of the source. + /// The type of the key. + /// The esper query. + /// The key selection expression. + /// + public static EsperQuery OrderBy(this EsperQuery esperQuery, + Expression> keySelectionExpression) + { + var parentObjectModel = esperQuery.ObjectModel; + var deriveObjectModel = parentObjectModel.ShallowCopy(); + + // Adapt or set the where clause according to the expression contents + using (ScopedInstance.Set(deriveObjectModel)) + { + var sodaExpression = LinqToSoda.LinqToSodaExpression(keySelectionExpression); + deriveObjectModel.OrderByClause = new OrderByClause(); + deriveObjectModel.OrderByClause.Add(sodaExpression, false); + return new EsperQuery(esperQuery.ServiceProvider, deriveObjectModel); + } + } + + /// + /// Orders the results of the expression. + /// + /// The type of the source. + /// The type of the key. + /// The esper query. + /// The key selection expression. + /// + public static EsperQuery OrderByDescending(this EsperQuery esperQuery, + Expression> keySelectionExpression) + { + var parentObjectModel = esperQuery.ObjectModel; + var deriveObjectModel = parentObjectModel.ShallowCopy(); + + // Adapt or set the where clause according to the expression contents + using (ScopedInstance.Set(deriveObjectModel)) + { + var sodaExpression = LinqToSoda.LinqToSodaExpression(keySelectionExpression); + deriveObjectModel.OrderByClause = new OrderByClause(); + deriveObjectModel.OrderByClause.Add(sodaExpression, true); + return new EsperQuery(esperQuery.ServiceProvider, deriveObjectModel); + } + } + #endregion + + #region GroupBy + + /// + /// Groups the results of the expression. + /// + /// The type of the source. + /// The type of the key. + /// The esper query. + /// The key selection expression. + /// + public static EsperQuery GroupBy(this EsperQuery esperQuery, + Expression> keySelectionExpression) + { + var parentObjectModel = esperQuery.ObjectModel; + var deriveObjectModel = parentObjectModel.ShallowCopy(); + + // Adapt or set the where clause according to the expression contents + using (ScopedInstance.Set(deriveObjectModel)) { + var sodaExpression = LinqToSoda.LinqToSodaExpression(keySelectionExpression); + deriveObjectModel.GroupByClause = GroupByClause.Create(sodaExpression); + return new EsperQuery(esperQuery.ServiceProvider, deriveObjectModel); + } + } + + #endregion + + #region FromTypeAs + /// + /// Creates a query view from the service provider. + /// + /// + /// The service provider. + /// As name. + /// + public static EsperQuery FromTypeAs(this EPServiceProvider serviceProvider, string asName) + { + var selectClause = SelectClause.Create(); + selectClause.AddWildcard(); + + var objectModel = new EPStatementObjectModel(); + objectModel.SelectClause = selectClause; + objectModel.FromClause = FromClause.Create(); + objectModel.FromClause.Add(FilterStream.Create(typeof(T).FullName, asName)); + objectModel.MakeIterableUnbound(); + + return new EsperQuery(serviceProvider, objectModel); + } + #endregion + + #region FromStreamAs + /// + /// Creates a query view from the service provider. + /// + /// + /// The service provider. + /// The stream. + /// The name of the stream. + /// + public static EsperQuery FromStreamAs(this EPServiceProvider serviceProvider, string stream, string asName) + { + var selectClause = SelectClause.Create(); + selectClause.AddWildcard(); + + var objectModel = new EPStatementObjectModel(); + objectModel.SelectClause = selectClause; + objectModel.FromClause = FromClause.Create(); + objectModel.FromClause.Add(FilterStream.Create(stream, asName)); + objectModel.MakeIterableUnbound(); + + return new EsperQuery(serviceProvider, objectModel); + } + #endregion + + #region From + /// + /// Creates a query view from the service provider. + /// + /// + /// The service provider. + /// + public static EsperQuery From(this EPServiceProvider serviceProvider) + { + var selectClause = SelectClause.Create(); + selectClause.AddWildcard(); + + var objectModel = new EPStatementObjectModel(); + objectModel.SelectClause = selectClause; + objectModel.FromClause = FromClause.Create(); + objectModel.FromClause.Add(FilterStream.Create(typeof (T).FullName)); + objectModel.MakeIterableUnbound(); + + return new EsperQuery(serviceProvider, objectModel); + } + + /// + /// Creates a query view from the service provider. + /// + /// The service provider. + /// The type list. + /// + public static EsperQuery From(this EPServiceProvider serviceProvider, params Type[] typeList) + { + var selectClause = SelectClause.Create(); + selectClause.AddWildcard(); + + var objectModel = new EPStatementObjectModel(); + objectModel.SelectClause = selectClause; + objectModel.FromClause = FromClause.Create(); + objectModel.MakeIterableUnbound(); + + for (int ii = 0; ii < typeList.Length; ii++) + { + var type = typeList[ii]; + var streamName = String.Format("s{0}", ii); + objectModel.FromClause.Add(FilterStream.Create(type.FullName, streamName)); + } + + return new EsperQuery(serviceProvider, objectModel); + } + + /// + /// Creates a query view from the service provider. + /// + /// + /// The service provider. + /// The stream names. + /// + public static EsperQuery From(this EPServiceProvider serviceProvider, params string[] streamNames) + { + var selectClause = SelectClause.Create(); + selectClause.AddWildcard(); + + var objectModel = new EPStatementObjectModel(); + objectModel.SelectClause = selectClause; + objectModel.FromClause = FromClause.Create(); + objectModel.MakeIterableUnbound(); + + for (int ii = 0; ii < streamNames.Length; ii++) + { + objectModel.FromClause.Add(FilterStream.Create(streamNames[ii])); + } + + return new EsperQuery(serviceProvider, objectModel); + } + + #endregion + } +} diff --git a/NEsper.Core/NEsper.Core/linq/EventBeanExtensions.cs b/NEsper.Core/NEsper.Core/linq/EventBeanExtensions.cs new file mode 100755 index 000000000..734b24fa3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/linq/EventBeanExtensions.cs @@ -0,0 +1,163 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.linq +{ + public static class EventBeanExtensions + { + public static sbyte? GetSByte(this EventBean eventBean, string name) + { + return (sbyte?)eventBean.Get(name); + } + + public static sbyte GetSByte(this EventBean eventBean, string name, sbyte defaultValue) + { + return GetSByte(eventBean, name) ?? defaultValue; + } + + public static byte? GetByte(this EventBean eventBean, string name) + { + return (byte?)eventBean.Get(name); + } + + public static byte GetByte(this EventBean eventBean, string name, byte defaultValue) + { + return GetByte(eventBean, name) ?? defaultValue; + } + + public static char? GetChar(this EventBean eventBean, string name) + { + return (char?)eventBean.Get(name); + } + + public static char GetChar(this EventBean eventBean, string name, char defaultValue) + { + return GetChar(eventBean, name) ?? defaultValue; + } + + public static short? GetInt16(this EventBean eventBean, string name) + { + return (short?)eventBean.Get(name); + } + + public static short GetInt16(this EventBean eventBean, string name, short defaultValue) + { + return GetInt16(eventBean, name) ?? defaultValue; + } + + public static int? GetInt32(this EventBean eventBean, string name) + { + return (int?) eventBean.Get(name); + } + + public static int GetInt32(this EventBean eventBean, string name, int defaultValue) + { + return GetInt32(eventBean, name) ?? defaultValue; + } + + public static long? GetInt64(this EventBean eventBean, string name) + { + return (long?)eventBean.Get(name); + } + + public static long GetInt64(this EventBean eventBean, string name, long defaultValue) + { + return GetInt64(eventBean, name) ?? defaultValue; + } + + public static ushort? GetUInt16(this EventBean eventBean, string name) + { + return (ushort?)eventBean.Get(name); + } + + public static ushort GetUInt16(this EventBean eventBean, string name, ushort defaultValue) + { + return GetUInt16(eventBean, name) ?? defaultValue; + } + + public static uint? GetUInt32(this EventBean eventBean, string name) + { + return (uint?)eventBean.Get(name); + } + + public static uint GetUInt32(this EventBean eventBean, string name, uint defaultValue) + { + return GetUInt32(eventBean, name) ?? defaultValue; + } + + public static ulong? GetUInt64(this EventBean eventBean, string name) + { + return (ulong?)eventBean.Get(name); + } + + public static ulong GetUInt64(this EventBean eventBean, string name, ulong defaultValue) + { + return GetUInt64(eventBean, name) ?? defaultValue; + } + + public static float? GetFloat(this EventBean eventBean, string name) + { + return (float?)eventBean.Get(name); + } + + public static float GetFloat(this EventBean eventBean, string name, float defaultValue) + { + return GetFloat(eventBean, name) ?? defaultValue; + } + + public static double? GetDouble(this EventBean eventBean, string name) + { + return (double?)eventBean.Get(name); + } + + public static double GetDouble(this EventBean eventBean, string name, double defaultValue) + { + return GetDouble(eventBean, name) ?? defaultValue; + } + + public static decimal? GetDecimal(this EventBean eventBean, string name) + { + return (decimal?)eventBean.Get(name); + } + + public static decimal GetDecimal(this EventBean eventBean, string name, decimal defaultValue) + { + return GetDecimal(eventBean, name) ?? defaultValue; + } + + public static String GetString(this EventBean eventBean, string name) + { + return (String)eventBean.Get(name); + } + + public static DateTime? GetDateTime(this EventBean eventBean, string name) + { + return (DateTime?)eventBean.Get(name); + } + + public static DateTime GetDateTime(this EventBean eventBean, string name, DateTime defaultValue) + { + return GetDateTime(eventBean, name) ?? defaultValue; + } + + public static Guid? GetGuid(this EventBean eventBean, string name) + { + return (Guid?)eventBean.Get(name); + } + + public static Guid GetGuid(this EventBean eventBean, string name, Guid defaultValue) + { + return GetGuid(eventBean, name) ?? defaultValue; + } + } +} diff --git a/NEsper.Core/NEsper.Core/linq/EventTransformationFactory.cs b/NEsper.Core/NEsper.Core/linq/EventTransformationFactory.cs new file mode 100755 index 000000000..01a332508 --- /dev/null +++ b/NEsper.Core/NEsper.Core/linq/EventTransformationFactory.cs @@ -0,0 +1,67 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat.magic; + +namespace com.espertech.esper.linq +{ + /// + /// Creates event transformation. + /// + public class EventTransformationFactory + { + /// + /// Returns the defaults the transformation from an eventBean to a type. + /// + /// + /// + public static Func DefaultTransformation() + { + if (typeof(T) == typeof(EventBean)) { + return eventBean => (T) eventBean; + } + if (typeof(T).IsInterface || typeof(T).IsAbstract) { + throw new ArgumentException( + "Can not create default transformation for interfaces or abstract classes"); + } + + var magicType = MagicType.GetCachedType(typeof(T)); + return eventBean => DefaultTransformation(magicType, eventBean); + } + + /// + /// Defaults the transformation. + /// + /// + /// Type of the magic. + /// The event bean. + /// + private static T DefaultTransformation(MagicType magicType, EventBean eventBean) + { + if ( eventBean.Underlying is T ) { + return (T) eventBean.Underlying; + } + + var eventType = eventBean.EventType; + var instance = (T) Activator.CreateInstance(typeof (T)); + + foreach( var propertyName in eventType.PropertyNames ) { + var magicProperty = magicType.ResolveProperty(propertyName, PropertyResolutionStyle.CASE_SENSITIVE); + if (magicProperty != null) { + var propertyValue = eventBean.Get(propertyName); + magicProperty.SetFunction(instance, propertyValue); + } + } + + return instance; + } + } +} diff --git a/NEsper.Core/NEsper.Core/linq/ExtViewExtensions.cs b/NEsper.Core/NEsper.Core/linq/ExtViewExtensions.cs new file mode 100755 index 000000000..5b359535d --- /dev/null +++ b/NEsper.Core/NEsper.Core/linq/ExtViewExtensions.cs @@ -0,0 +1,89 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client.soda; + +namespace com.espertech.esper.linq +{ + public static class ExtViewExtensions + { + /// + /// Generates a view that sorts by values returned by the specified expression or list of expressions and keeps + /// only the top (or bottom) events up to the given size. + /// + /// + /// The esper query. + /// The size. + /// The sort criteria. + /// + /// at least one property must be provided + public static EsperQuery Sorted(this EsperQuery esperQuery, int size, params SortCriteria[] sortCriteria) + { + if (sortCriteria == null || sortCriteria.Length < 1) + throw new ArgumentException("at least one sort criteria must be provided"); + + var expressionList = new List(); + expressionList.Add( + new ConstantExpression(size)); + expressionList.AddRange( + sortCriteria.Select(s => s.ToSodaExpression())); + + return esperQuery.FilterView(() => View.Create("sort", expressionList)); + } + + /// + /// Generates a view that orders events that arrive out-of-order, using timestamp-values + /// provided by an expression, and by comparing that timestamp value to engine system time. + /// + /// + /// The esper query. + /// The property to use for the timestamp. + /// the time period specifying the time interval that an arriving event should maximally be held, in order to consider older events arriving at a later time + /// + /// at least one property must be provided + public static EsperQuery TimeOrdered(this EsperQuery esperQuery, string property, TimeSpan timePeriod) + { + return esperQuery.FilterView(() => View.Create("time_order", + new PropertyValueExpression(property), + timePeriod.ToTimePeriodExpression())); + } + + /// + /// Generates a view retains only the most recent among events having the same value for the criteria + /// expression(s), sorted by sort criteria expressions and keeps only the top events up to the given size. + /// + /// + /// The esper query. + /// The unique properties. + /// The size. + /// The sort criteria. + /// + /// at least one property must be provided + public static EsperQuery Ranked(this EsperQuery esperQuery, IEnumerable uniqueProperties, int size, params SortCriteria[] sortCriteria) + { + if (uniqueProperties == null || uniqueProperties.Count() < 1) + throw new ArgumentException("at least one unique property must be provided"); + if (sortCriteria == null || sortCriteria.Length < 1) + throw new ArgumentException("at least one sort criteria must be provided"); + + var expressionList = new List(); + expressionList.AddRange( + uniqueProperties.Select(p => new PropertyValueExpression(p)).Cast()); + expressionList.Add( + new ConstantExpression(size)); + expressionList.AddRange( + sortCriteria.Select(s => s.ToSodaExpression())); + + return esperQuery.FilterView(() => View.Create("rank", expressionList)); + } + } +} diff --git a/NEsper.Core/NEsper.Core/linq/FilterViewExtensions.cs b/NEsper.Core/NEsper.Core/linq/FilterViewExtensions.cs new file mode 100755 index 000000000..499a6543d --- /dev/null +++ b/NEsper.Core/NEsper.Core/linq/FilterViewExtensions.cs @@ -0,0 +1,86 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Linq; + +using com.espertech.esper.client.soda; +using com.espertech.esper.compat; + +namespace com.espertech.esper.linq +{ + public static class FilterViewExtensions + { + /// + /// Boilerplate for creating views on filter streams. Make your own if you'd like. + /// + /// + /// The esper query. + /// The viewFactory. + /// + /// + /// no stream available to use for window + /// or + /// no stream available to use for window + /// + public static EsperQuery FilterView(this EsperQuery esperQuery, Func viewFactory) + { + var parentObjectModel = esperQuery.ObjectModel; + var deriveObjectModel = parentObjectModel.ShallowCopy(); + var deriveFromClause = deriveObjectModel.FromClause; + + using (ScopedInstance.Set(deriveObjectModel)) + { + var streams = deriveFromClause.Streams; + if (streams == null) + throw new ArgumentException("no stream available to use for window"); + + var filter = streams.OfType().Last(); + if (filter == null) + throw new ArgumentException("no stream available to use for window"); + + filter.AddView(viewFactory.Invoke()); + } + + return new EsperQuery(esperQuery.ServiceProvider, deriveObjectModel); + } + + /// + /// Boilerplate for creating views on filter streams. Make your own if you'd like. + /// + /// The type of the in. + /// The type of the out. + /// The esper query. + /// The viewFactory. + /// + /// no stream available to use for window + /// or + /// no stream available to use for window + public static EsperQuery FilterView(this EsperQuery esperQuery, Func viewFactory) + { + var parentObjectModel = esperQuery.ObjectModel; + var deriveObjectModel = parentObjectModel.ShallowCopy(); + var deriveFromClause = deriveObjectModel.FromClause; + + using (ScopedInstance.Set(deriveObjectModel)) + { + var streams = deriveFromClause.Streams; + if (streams == null) + throw new ArgumentException("no stream available to use for window"); + + var filter = streams.OfType().Last(); + if (filter == null) + throw new ArgumentException("no stream available to use for window"); + + filter.AddView(viewFactory.Invoke()); + } + + return new EsperQuery(esperQuery.ServiceProvider, deriveObjectModel); + } + } +} diff --git a/NEsper.Core/NEsper.Core/linq/LinqToSoda.cs b/NEsper.Core/NEsper.Core/linq/LinqToSoda.cs new file mode 100755 index 000000000..cb040fa45 --- /dev/null +++ b/NEsper.Core/NEsper.Core/linq/LinqToSoda.cs @@ -0,0 +1,319 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.client.soda; +using com.espertech.esper.compat; +using com.espertech.esper.util; + +namespace com.espertech.esper.linq +{ + using Expression = client.soda.Expression; + + public class LinqToSoda + { + /// + /// Converts a LINQ expression to a SODA expression. + /// + /// The expression. + /// + public static Expression LinqToSodaExpression(System.Linq.Expressions.Expression expression) + { + if ( expression == null ) { + return null; + } + + switch (expression.NodeType) + { + case ExpressionType.And: + return Expressions.And( + LinqToSodaExpression(((BinaryExpression)expression).Left), + LinqToSodaExpression(((BinaryExpression)expression).Right)); + case ExpressionType.AndAlso: + return Expressions.And( + LinqToSodaExpression(((BinaryExpression)expression).Left), + LinqToSodaExpression(((BinaryExpression)expression).Right)); + case ExpressionType.Or: + return Expressions.Or( + LinqToSodaExpression(((BinaryExpression)expression).Left), + LinqToSodaExpression(((BinaryExpression)expression).Right)); + case ExpressionType.OrElse: + return Expressions.Or( + LinqToSodaExpression(((BinaryExpression)expression).Left), + LinqToSodaExpression(((BinaryExpression)expression).Right)); + case ExpressionType.Not: + return Expressions.Not( + LinqToSodaExpression(((UnaryExpression)expression).Operand)); + case ExpressionType.NotEqual: + return Expressions.Not( + Expressions.Eq( + LinqToSodaExpression(((BinaryExpression)expression).Left), + LinqToSodaExpression(((BinaryExpression)expression).Right))); + case ExpressionType.Equal: + return Expressions.Eq( + LinqToSodaExpression(((BinaryExpression)expression).Left), + LinqToSodaExpression(((BinaryExpression)expression).Right)); + case ExpressionType.LessThan: + return Expressions.Lt( + LinqToSodaExpression(((BinaryExpression)expression).Left), + LinqToSodaExpression(((BinaryExpression)expression).Right)); + case ExpressionType.LessThanOrEqual: + return Expressions.Le( + LinqToSodaExpression(((BinaryExpression)expression).Left), + LinqToSodaExpression(((BinaryExpression)expression).Right)); + case ExpressionType.GreaterThan: + return Expressions.Gt( + LinqToSodaExpression(((BinaryExpression)expression).Left), + LinqToSodaExpression(((BinaryExpression)expression).Right)); + case ExpressionType.GreaterThanOrEqual: + return Expressions.Ge( + LinqToSodaExpression(((BinaryExpression)expression).Left), + LinqToSodaExpression(((BinaryExpression)expression).Right)); + case ExpressionType.MemberAccess: + return MemberToSoda(expression); + case ExpressionType.Lambda: + return LambdaToSoda(expression); + + case ExpressionType.Convert: + { + var unary = (UnaryExpression)expression; + return Expressions.Cast( + LinqToSodaExpression(unary.Operand), + unary.Type.GetSimpleTypeName()); + } + + case ExpressionType.Constant: + return Expressions.Constant( + ((System.Linq.Expressions.ConstantExpression)expression).Value); + + case ExpressionType.Call: + return CallToSoda(expression); + } + + throw new ArgumentException( + String.Format("Expression of type {0} is not supported", expression.NodeType), "expression"); + } + + /// + /// Converts a lambda expression to a soda expression. + /// + /// The expression. + /// + private static Expression LambdaToSoda(System.Linq.Expressions.Expression expression) + { + var lambda = (System.Linq.Expressions.LambdaExpression)expression; + using (ScopedInstance.Set(lambda)) + { + return LinqToSodaExpression(lambda.Body); + } + } + + /// + /// Converts a member expression to a soda expression. + /// + /// The expression. + /// + private static PropertyValueExpression MemberToSoda(System.Linq.Expressions.Expression expression) + { + // Get the current model - this defines the streams that are available + var model = ScopedInstance.Current; + // Get the current lambda - this defines the parameter that was used in the query + var lambda = ScopedInstance.Current; + // Get the member expression + var memberExpr = (MemberExpression)expression; + var memberInfo = memberExpr.Member; // Field, Method or Property + var memberData = memberExpr.Expression; // Parameter expression - hopefully + + if (memberInfo is PropertyInfo) + { + } + else if (memberInfo is FieldInfo) + { + } + else + { + throw new NotSupportedException("Linq support only handles properties and fields"); + } + + var asParameter = memberData as ParameterExpression; + if (asParameter != null) + { + var param = asParameter.Name; + if (!lambda.Parameters.Contains(asParameter)) + { + throw new ArgumentException( + String.Format("Expression unable to find parameter named '{0}'", param)); + } + + // Parameter was located ... see if we can match the parameter against any of the stream names + if (model.FromClause != null) { + var fromClauseStreams = model.FromClause.Streams; + var matchingStream = fromClauseStreams.FirstOrDefault( + stream => stream.StreamName == param); + if (matchingStream == null) { + do { + // Object was not found in the 'from' clause + var asOnDeleteClause = model.OnExpr as OnDeleteClause; + if (asOnDeleteClause != null) { + if ((param == asOnDeleteClause.WindowName) || + (param == asOnDeleteClause.OptionalAsName)) { + return Expressions.Property(String.Format("{0}.{1}", param, memberInfo.Name)); + } + } + + var asOnSelectClause = model.OnExpr as OnSelectClause; + if (asOnSelectClause != null) { + if ((param == asOnSelectClause.WindowName) || + (param == asOnSelectClause.OptionalAsName)) { + return Expressions.Property(String.Format("{0}.{1}", param, memberInfo.Name)); + } + } + + matchingStream = fromClauseStreams.FirstOrDefault(); + if (matchingStream == null) { + throw new IllegalStateException("Object model does not have a stream in from clause"); + } + + return Expressions.Property(String.Format("{0}.{1}", param, memberInfo.Name)); + } while (false); + } + } + + // We should have a matching clause at this point + return Expressions.Property(String.Format("{0}.{1}", param, memberInfo.Name)); + } + + throw new ArgumentException( + String.Format("Member expression of type {0} is not supported", memberData.GetType().Name), "expression"); + } + + /// + /// Converts a call expression to soda expression. + /// + /// The expression. + /// + private static Expression CallToSoda(System.Linq.Expressions.Expression expression) + { + var methodCall = (MethodCallExpression)expression; + var method = methodCall.Method; + var declaringType = method.DeclaringType; + + if (method.IsPublic && method.IsStatic) + { + if (declaringType == typeof(EventBeanExtensions)) { + // Masquerade extensions for properties ... + return UnmasqProperty(methodCall.Arguments[1]); + } + + var paramList = new List(); + foreach (var argument in methodCall.Arguments) + { + paramList.Add(LinqToSodaExpression(argument)); + } + + return Expressions.StaticMethod( + method.DeclaringType.FullName, + method.Name, + paramList.ToArray()); + } + + if (method.IsPublic && !method.IsStatic) { + if (declaringType == typeof (EventBean)) { + if (method.Name == "Get") { + // Masquerade extensions for properties ... + return UnmasqProperty(methodCall.Arguments[1]); + } + } + } + + throw new NotSupportedException("Instance methods are not supported in this version"); + } + + /// + /// Unmasqs a property. + /// + /// The property name expr. + /// + private static Expression UnmasqProperty(System.Linq.Expressions.Expression propertyNameExpr) + { + var propertyNameConst = LinqToSodaExpression(propertyNameExpr); + if (propertyNameConst is client.soda.ConstantExpression) { + var propertyName = ((client.soda.ConstantExpression) propertyNameConst).Constant; + if (propertyName is string) { + return Expressions.Property((string) propertyName); + } + } + + throw new ArgumentException("Constant property name expression must yield a constant string"); + } + + /// + /// Converts the new invocation call into a select clause. + /// + /// The new expression. + /// + public static SelectClause NewToSelectClause(System.Linq.Expressions.NewExpression newExpression) + { + var selectClause = new SelectClause(); + selectClause.IsDistinct = false; + selectClause.StreamSelector = StreamSelector.RSTREAM_ISTREAM_BOTH; + selectClause.SelectList = new List(); + + foreach (var argExpression in newExpression.Arguments) { + if (argExpression is MemberExpression) { + var memberExpression = (MemberExpression) argExpression; + var propertyExpression = MemberToSoda(memberExpression); + var selectClauseElement = new SelectClauseExpression(propertyExpression); + selectClause.SelectList.Add(selectClauseElement); + } else { + throw new ArgumentException( + String.Format("Expression of type {0} is not supported", argExpression.NodeType)); + } + } + + return selectClause; + } + + /// + /// Converts a LINQ expression to a select clause expression. + /// + /// The expression. + /// + public static SelectClause LinqToSelectClause(System.Linq.Expressions.Expression expression) + { + if (expression == null) + { + return null; + } + + switch (expression.NodeType) + { + case ExpressionType.Lambda: + do { + var lambda = (System.Linq.Expressions.LambdaExpression)expression; + using (ScopedInstance.Set(lambda)) + { + return LinqToSelectClause(lambda.Body); + } + } while (false); + case ExpressionType.New: + return NewToSelectClause( + (System.Linq.Expressions.NewExpression)expression); + } + + throw new ArgumentException( + String.Format("Expression of type {0} is not supported", expression.NodeType), "expression"); + } + } +} diff --git a/NEsper.Core/NEsper.Core/linq/ServiceProviderExtensions.cs b/NEsper.Core/NEsper.Core/linq/ServiceProviderExtensions.cs new file mode 100755 index 000000000..ef234e8dd --- /dev/null +++ b/NEsper.Core/NEsper.Core/linq/ServiceProviderExtensions.cs @@ -0,0 +1,388 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.client.soda; +using com.espertech.esper.compat; + +namespace com.espertech.esper.linq +{ + /// + /// Set of extensions for use with NEsper EPServiceProviders. + /// + + public static class ServiceProviderExtensions + { + #region CreateVariable + /// + /// Creates the variable. + /// + /// The service provider. + /// Name of the variable. + /// Type of the variable. + public static void CreateVariable(this EPServiceProvider serviceProvider, + string variableName, + Type variableType) + { + CreateVariable(serviceProvider, variableName, variableType.FullName); + } + + /// + /// Creates the variable. + /// + /// The service provider. + /// Name of the variable. + /// Type of the variable. + public static void CreateVariable(this EPServiceProvider serviceProvider, + string variableName, + string variableType) + { + CreateVariable(serviceProvider, variableName, variableType, null); + } + + /// + /// Creates the variable. + /// + /// The service provider. + /// Name of the variable. + /// Type of the variable. + /// The assignment. + public static void CreateVariable(this EPServiceProvider serviceProvider, + string variableName, + Type variableType, + System.Linq.Expressions.Expression> assignment) + { + CreateVariable(serviceProvider, variableName, variableType.FullName, assignment); + } + + /// + /// Creates the variable. + /// + /// The service provider. + /// Name of the variable. + /// Type of the variable. + /// The assignment. + public static void CreateVariable(this EPServiceProvider serviceProvider, + string variableName, + string variableType, + System.Linq.Expressions.Expression> assignment) + { + var objectModel = new EPStatementObjectModel(); + objectModel.CreateVariable = CreateVariableClause.Create(variableType, variableName); + objectModel.CreateVariable.OptionalAssignment = LinqToSoda.LinqToSodaExpression(assignment); + serviceProvider.EPAdministrator.Create(objectModel); + } + #endregion + + #region CreateSelectTrigger + + /// + /// Creates the select trigger. + /// + /// + /// The service provider. + /// Name of the window. + /// As name. + /// From clause. + /// + public static EPStatement CreateSelectTrigger(this EPServiceProvider serviceProvider, + string windowName, + string asName, + EsperQuery fromClause) + { + return CreateSelectTrigger( + serviceProvider, + windowName, + asName, + fromClause, + (Func) null); + } + + /// + /// Creates the select trigger with one stream expression capability. + /// + /// + /// The service provider. + /// Name of the window. + /// As name. + /// From clause. + /// The where clause. + /// + public static EPStatement CreateSelectTrigger(this EPServiceProvider serviceProvider, + string windowName, + string asName, + EsperQuery fromClause, + System.Linq.Expressions.Expression> whereClause) + { + return CreateSelectTrigger( + serviceProvider, + windowName, + asName, + fromClause, + () => LinqToSoda.LinqToSodaExpression(whereClause)); + } + + /// + /// Creates a select trigger with two stream expression capability. + /// + /// The type of the 1. + /// The type of the 2. + /// The service provider. + /// Name of the window. + /// As name. + /// From clause. + /// The where clause. + /// + public static EPStatement CreateSelectTrigger(this EPServiceProvider serviceProvider, + string windowName, + string asName, + EsperQuery fromClause, + System.Linq.Expressions.Expression> whereClause) + { + return CreateSelectTrigger( + serviceProvider, + windowName, + asName, + fromClause, + () => LinqToSoda.LinqToSodaExpression(whereClause)); + } + + /// + /// Creates the select trigger. + /// + /// + /// The service provider. + /// Name of the window. + /// As name. + /// From clause. + /// The deferred where clause. + /// + public static EPStatement CreateSelectTrigger(EPServiceProvider serviceProvider, + string windowName, + string asName, + EsperQuery fromClause, + Func deferredWhereClause) + { + var deriveObjectModel = DeriveObjectModel(fromClause); + using (ScopedInstance.Set(deriveObjectModel)) + { + deriveObjectModel.OnExpr = OnClause.CreateOnSelect(windowName, asName); + + if (deferredWhereClause != null) + { + var whereClause = deferredWhereClause.Invoke(); + if (whereClause != null) + { + deriveObjectModel.WhereClause = whereClause; + } + } + + return serviceProvider.EPAdministrator.Create(deriveObjectModel); + } + } + + #endregion + + #region CreateDeleteTrigger + + /// + /// Creates the delete trigger with no where clause. + /// + /// + /// The service provider. + /// Name of the window. + /// As name. + /// From clause. + /// + public static EPStatement CreateDeleteTrigger(this EPServiceProvider serviceProvider, + string windowName, + string asName, + EsperQuery fromClause) + { + return CreateDeleteTrigger( + serviceProvider, + windowName, + asName, + fromClause, + (Func) null); + } + + /// + /// Creates the delete trigger with one stream expression capability. + /// + /// + /// The service provider. + /// Name of the window. + /// As name. + /// From clause. + /// The where clause. + /// + public static EPStatement CreateDeleteTrigger(this EPServiceProvider serviceProvider, + string windowName, + string asName, + EsperQuery fromClause, + System.Linq.Expressions.Expression> whereClause) + { + return CreateDeleteTrigger( + serviceProvider, + windowName, + asName, + fromClause, + () => LinqToSoda.LinqToSodaExpression(whereClause)); + } + + /// + /// Creates a delete trigger with two stream expression capability. + /// + /// The type of the 1. + /// The type of the 2. + /// The service provider. + /// Name of the window. + /// As name. + /// From clause. + /// The where clause. + /// + public static EPStatement CreateDeleteTrigger(this EPServiceProvider serviceProvider, + string windowName, + string asName, + EsperQuery fromClause, + System.Linq.Expressions.Expression> whereClause) + { + return CreateDeleteTrigger( + serviceProvider, + windowName, + asName, + fromClause, + () => LinqToSoda.LinqToSodaExpression(whereClause)); + } + + /// + /// Creates the delete trigger. + /// + /// + /// The service provider. + /// Name of the window. + /// As name. + /// From clause. + /// The deferred where clause. + /// + public static EPStatement CreateDeleteTrigger(EPServiceProvider serviceProvider, + string windowName, + string asName, + EsperQuery fromClause, + Func deferredWhereClause) + { + var deriveObjectModel = DeriveObjectModel(fromClause); + using (ScopedInstance.Set(deriveObjectModel)) { + deriveObjectModel.OnExpr = OnClause.CreateOnDelete(windowName, asName); + + if (deferredWhereClause != null) + { + var whereClause = deferredWhereClause.Invoke(); + if (whereClause != null) { + deriveObjectModel.WhereClause = whereClause; + } + } + + return serviceProvider.EPAdministrator.Create(deriveObjectModel); + } + } + + #endregion + + #region CreateWindow + /// + /// Creates a window. + /// + /// + /// The service provider. + /// Name of the window. + /// The view. + /// The esper query. + /// + public static EPStatement CreateWindow(this EPServiceProvider serviceProvider, string windowName, View view, EsperQuery esperQuery) + { + return CreateWindow(serviceProvider, windowName, view, esperQuery, null); + } + + /// + /// Creates a window. + /// + /// + /// The service provider. + /// Name of the window. + /// The view. + /// The esper query. + /// The insert where expression. + /// + public static EPStatement CreateWindow( + this EPServiceProvider serviceProvider, + string windowName, + View view, + EsperQuery esperQuery, + System.Linq.Expressions.Expression> insertWhereExpression) + { + var statementObjectModel = CreateWindowAsObjectModel( + serviceProvider, + windowName, + view, + esperQuery, + insertWhereExpression); + return serviceProvider.EPAdministrator.Create(statementObjectModel); + } + + /// + /// Creates a window. + /// + /// + /// The service provider. + /// Name of the window. + /// The view. + /// The esper query. + /// The insert where expression. + /// + public static EPStatementObjectModel CreateWindowAsObjectModel( + this EPServiceProvider serviceProvider, + string windowName, + View view, + EsperQuery esperQuery, + System.Linq.Expressions.Expression> insertWhereExpression) + { + var deriveObjectModel = DeriveObjectModel(esperQuery); + + using (ScopedInstance.Set(deriveObjectModel)) { + deriveObjectModel.CreateWindow = CreateWindowClause.Create(windowName, view); + deriveObjectModel.CreateWindow.IsInsert = false; + if (insertWhereExpression != null) { + deriveObjectModel.CreateWindow.InsertWhereClause = + LinqToSoda.LinqToSodaExpression(insertWhereExpression); + deriveObjectModel.CreateWindow.IsInsert = true; + } + + return deriveObjectModel; + } + } + + private static EPStatementObjectModel DeriveObjectModel(EsperQuery esperQuery) + { + EPStatementObjectModel deriveObjectModel; + + if (esperQuery == null) { + deriveObjectModel = new EPStatementObjectModel(); + } else { + var parentObjectModel = esperQuery.ObjectModel; + deriveObjectModel = parentObjectModel.ShallowCopy(); + } + return deriveObjectModel; + } + + #endregion + } +} diff --git a/NEsper.Core/NEsper.Core/linq/SortCriteria.cs b/NEsper.Core/NEsper.Core/linq/SortCriteria.cs new file mode 100755 index 000000000..bec6faaf8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/linq/SortCriteria.cs @@ -0,0 +1,71 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client.soda; + +namespace com.espertech.esper.linq +{ + public class SortCriteria + { + /// + /// Gets or sets the property. + /// + /// + /// The property. + /// + public string Property { get; set; } + + /// + /// Gets or sets a value indicating whether this is ascending. + /// + /// + /// true if ascending; otherwise, false. + /// + public bool Ascending { get; set; } + + /// + /// Returns the SODA variant of the object. + /// + /// + public OrderedObjectParamExpression ToSodaExpression() + { + var expression = new OrderedObjectParamExpression(!Ascending); + expression.AddChild(new PropertyValueExpression(Property)); + return expression; + } + + /// + /// Initializes a new instance of the class. + /// + /// The property. + /// if set to true [ascending]. + public SortCriteria(string property, bool @ascending) + { + Property = property; + Ascending = @ascending; + } + + /// + /// Initializes a new instance of the class. + /// + /// The property. + public SortCriteria(string property) + { + Property = property; + Ascending = true; + } + + /// + /// Initializes a new instance of the class. + /// + public SortCriteria() + { + Ascending = true; + } + } +} diff --git a/NEsper.Core/NEsper.Core/linq/StatViewExtensions.cs b/NEsper.Core/NEsper.Core/linq/StatViewExtensions.cs new file mode 100755 index 000000000..6723197bb --- /dev/null +++ b/NEsper.Core/NEsper.Core/linq/StatViewExtensions.cs @@ -0,0 +1,91 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Linq; + +using com.espertech.esper.linq.statistics; +using com.espertech.esper.client.soda; + +namespace com.espertech.esper.linq +{ + public static class StatViewExtensions + { + /// + /// Generates a view that calculates univariate statistics on a numeric expression. The view takes a + /// single value property as a parameter plus any number of optional additional properties to return properties + /// of the last event. + /// + /// + /// The esper query. + /// The property or expression. + /// + public static EsperQuery Univariate(this EsperQuery esperQuery, params string[] properties) + { + if (properties == null || properties.Length < 1) + throw new ArgumentException("at least one property must be provided"); + + return esperQuery.FilterView(() => View.Create("stat", "uni", + properties.Select(p => new PropertyValueExpression(p)).Cast().ToArray())); + } + + /// + /// Generates a view that calculates regression and related intermediate results on the values returned by two expressions. + /// The view takes two value properties as parameters plus any number of optional additional properties to return properties + /// of the last event. The value expressions must return a numeric value. + /// + /// + /// The esper query. + /// The property or expression. + /// + public static EsperQuery Regression(this EsperQuery esperQuery, params string[] properties) + { + if (properties == null || properties.Length < 2) + throw new ArgumentException("at least two property must be provided"); + + return esperQuery.FilterView(() => View.Create("stat", "linest", + properties.Select(p => new PropertyValueExpression(p)).Cast().ToArray())); + } + + /// + /// Generates a view that calculates the correlation value on the value returned by two expressions. + /// The view takes two value properties as parameters plus any number of optional additional properties to return properties + /// of the last event. The value expressions must be return a numeric value. + /// + /// + /// The esper query. + /// The property or expression. + /// + public static EsperQuery Correlation(this EsperQuery esperQuery, params string[] properties) + { + if (properties == null || properties.Length < 2) + throw new ArgumentException("at least two property must be provided"); + + return esperQuery.FilterView(() => View.Create("stat", "correl", + properties.Select(p => new PropertyValueExpression(p)).Cast().ToArray())); + } + + /// + /// Generates a view that calculates the weighted average given a property returning values to compute the average + /// for and a property returning weight. The view takes two value properties as parameters plus any number of optional + /// additional properties to return properties of the last event. The value expressions must return numeric values. + /// + /// + /// The esper query. + /// The property or expression. + /// + public static EsperQuery WeightedAverage(this EsperQuery esperQuery, params string[] properties) + { + if (properties == null || properties.Length < 2) + throw new ArgumentException("at least two property must be provided"); + + return esperQuery.FilterView(() => View.Create("stat", "weighted_average", + properties.Select(p => new PropertyValueExpression(p)).Cast().ToArray())); + } + } +} diff --git a/NEsper.Core/NEsper.Core/linq/StatementExtensions.cs b/NEsper.Core/NEsper.Core/linq/StatementExtensions.cs new file mode 100755 index 000000000..7e8bc2439 --- /dev/null +++ b/NEsper.Core/NEsper.Core/linq/StatementExtensions.cs @@ -0,0 +1,62 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.linq +{ + public static class StatementExtensions + { + /// + /// Creates a typed observable collection from the statement. All events are forwarded to + /// the observerable collection and are transformed into the typed class using the default + /// event transformer. + /// + /// + /// The statement. + /// + public static StatementObservableCollection AsObservableCollection(this EPStatement statement) + { + return AsObservableCollection(statement, false); + } + + /// + /// Creates a typed observable collection from the statement. All events are forwarded to + /// the observerable collection and are transformed into the typed class using the default + /// event transformer. + /// + /// + /// The statement. + /// if set to true [dispose statement]. + /// + public static StatementObservableCollection AsObservableCollection(this EPStatement statement, bool disposeStatement) + { + return AsObservableCollection(statement, EventTransformationFactory.DefaultTransformation(), disposeStatement); + } + + /// + /// Creates a typed observable collection from the statement. All events are forwarded to + /// the observerable collection and are transformed into the typed class using the provided + /// event transformer. + /// + /// + /// The statement. + /// The event transformer. + /// if set to true [dispose statement]. + /// + public static StatementObservableCollection AsObservableCollection(this EPStatement statement, + Func eventTransformer, + bool disposeStatement) + { + var observerableCollection = new StatementObservableCollection(statement, eventTransformer, disposeStatement); + return observerableCollection; + } + } +} diff --git a/NEsper.Core/NEsper.Core/linq/StatementObjectModelExtensions.cs b/NEsper.Core/NEsper.Core/linq/StatementObjectModelExtensions.cs new file mode 100755 index 000000000..8dba4156e --- /dev/null +++ b/NEsper.Core/NEsper.Core/linq/StatementObjectModelExtensions.cs @@ -0,0 +1,52 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System.Collections.Generic; + +using com.espertech.esper.client.soda; + +namespace com.espertech.esper.linq +{ + public static class StatementObjectModelExtensions + { + /// + /// Creates a shallow copy of the object model. + /// + /// The source. + /// + public static EPStatementObjectModel ShallowCopy( this EPStatementObjectModel source ) + { + return new EPStatementObjectModel + { + Annotations = source.Annotations, + InsertInto = source.InsertInto, + SelectClause = source.SelectClause, + FromClause = source.FromClause, + WhereClause = source.WhereClause, + GroupByClause = source.GroupByClause, + HavingClause = source.HavingClause, + OutputLimitClause = source.OutputLimitClause, + OrderByClause = source.OrderByClause, + CreateVariable = source.CreateVariable, + CreateWindow = source.CreateWindow, + OnExpr = source.OnExpr, + RowLimitClause = source.RowLimitClause + }; + } + + public static void MakeIterableUnbound(this EPStatementObjectModel objectModel) + { + objectModel.Annotations = new List() + { + new AnnotationPart("IterableUnbound") + }; + } + + } +} diff --git a/NEsper.Core/NEsper.Core/linq/StatementObservableCollection.cs b/NEsper.Core/NEsper.Core/linq/StatementObservableCollection.cs new file mode 100755 index 000000000..3650ce6ee --- /dev/null +++ b/NEsper.Core/NEsper.Core/linq/StatementObservableCollection.cs @@ -0,0 +1,216 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.ComponentModel; + +using com.espertech.esper.client; + +namespace com.espertech.esper.linq +{ + public class StatementObservableCollection : DisposableObservableCollection + { + public const int MaxComparisonCount = 1000; + + /// + /// Gets the function that converts the event bean into a properly formed object. + /// + public Func EventTransform { get; private set; } + + /// + /// Gets the statement the collection is bound to. + /// + /// The statement. + public EPStatement Statement { get; private set; } + + /// + /// Gets or sets a value indicating whether [dispose statement]. + /// + /// true if [dispose statement]; otherwise, false. + public bool DisposeStatement { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// The statement. + /// The event transform. + /// if set to true [dispose statement]. + public StatementObservableCollection(EPStatement statement, Func eventTransform, bool disposeStatement) + { + if (statement == null) { + throw new ArgumentNullException("statement"); + } + + if (eventTransform == null) { + throw new ArgumentNullException("eventTransform"); + } + + DisposeStatement = disposeStatement; + EventTransform = eventTransform; + Statement = statement; + Statement.Events += OnEvent; + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public override void Dispose() + { + if (Statement != null) { + Statement.Events -= OnEvent; + if ( DisposeStatement ) { + Statement.Dispose(); + } + + Statement = null; + } + } + + /// + /// Called when an Update [event] occurs on the statement. + /// + /// The sender. + /// The instance containing the event data. + protected virtual void OnEvent(Object sender, UpdateEventArgs e) + { + var newEventSet = CreateTypedEventList(e.Statement); + + // Handle the case where the events are null or where there are + // no events at all. + if (newEventSet.Count == 0) { + ClearItems(); + return; + } + + // Create the typedEventList + SetItems(newEventSet); + } + + + /// + /// Sets the items. + /// + /// The item list. + protected virtual void SetItems( List itemList ) + { + CheckReentrancy(); + + // Compare the itemList against the items curerntly in the collection. + // We would really prefer to generate the smallest event possible. + + switch (Count) { + case 0: + SetItemsWhenEmpty(itemList); + return; + case 1: + SetItemsWhenSingleOccupant(itemList); + return; + default: + Items.Clear(); + + var count = itemList.Count; + for (var ii = 0; ii < count; ii++) { + Items.Add(itemList[ii]); + } + + OnPropertyChanged(new PropertyChangedEventArgs("Count")); + OnPropertyChanged(new PropertyChangedEventArgs("Item[]")); + OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); + + break; + } + } + + /// + /// Sets the items when there is only a single item currently in the list. + /// + /// The item list. + private void SetItemsWhenSingleOccupant(IList itemList) + { + CheckReentrancy(); + + if (itemList.Count == 0) + { + var oldValue = Items[0]; + Items.RemoveAt(0); + OnPropertyChanged(new PropertyChangedEventArgs("Item[]")); + OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, oldValue, 0)); + } + else if (itemList.Count == 1) + { + if (Equals(itemList[0], Items[0])) + { + return; + } + + var oldValue = Items[0]; + var newValue = itemList[0]; + + Items[0] = newValue; + OnPropertyChanged(new PropertyChangedEventArgs("Item[]")); + OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, newValue, oldValue, 0)); + } + else + { + Items.Clear(); + + var count = itemList.Count; + for (var ii = 0; ii < count; ii++) + { + Items.Add(itemList[ii]); + } + + OnPropertyChanged(new PropertyChangedEventArgs("Count")); + OnPropertyChanged(new PropertyChangedEventArgs("Item[]")); + OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); + } + } + + /// + /// Sets the items when there are no occupants in the list. + /// + /// The item list. + private void SetItemsWhenEmpty(IList itemList) + { + CheckReentrancy(); + + if ( itemList.Count == 0 ) { + return; + } + + var count = itemList.Count; + for( var ii = 0 ; ii < count ; ii++ ) { + Items.Add(itemList[ii]); + } + + // Consider this a complete replacement. + + OnPropertyChanged(new PropertyChangedEventArgs("Count")); + OnPropertyChanged(new PropertyChangedEventArgs("Item[]")); + OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); + } + + /// + /// Creates the typed event list. + /// + /// The event list. + /// + protected virtual List CreateTypedEventList( IEnumerable eventList ) + { + var typedEventList = new List(); + var transform = EventTransform; + foreach( var eventBean in eventList ) { + typedEventList.Add(transform.Invoke(eventBean)); + } + + return typedEventList; + } + } +} diff --git a/NEsper.Core/NEsper.Core/linq/StdViewExtensions.cs b/NEsper.Core/NEsper.Core/linq/StdViewExtensions.cs new file mode 100755 index 000000000..eece4d36f --- /dev/null +++ b/NEsper.Core/NEsper.Core/linq/StdViewExtensions.cs @@ -0,0 +1,135 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Linq; + +using com.espertech.esper.client.soda; + +namespace com.espertech.esper.linq +{ + public static class StdViewExtensions + { + /// + /// Generates a view that includes only the most recent among events having the same value for + /// the result of the sepcified list of properties. + /// + /// This view acts as a length window of size 1 for each distinct value returned by a property, or combination + /// of values returned by multiple properties. It thus posts as old events the prior event of the same value(s), + /// if any. + /// + /// + /// The esper query. + /// The property or expression. + /// + public static EsperQuery Unique(this EsperQuery esperQuery, params string[] properties) + { + if (properties == null) + throw new ArgumentException("at least one property must be provided"); + + return esperQuery.FilterView(() => View.Create("unique", + properties.Select(p => new PropertyValueExpression(p)).Cast().ToArray())); + } + + /// + /// Generates a view that includes only the most recent among events having the same value for + /// the result of the sepcified list of properties. + /// + /// This view acts as a length window of size 1 for each distinct value returned by a property, or combination + /// of values returned by multiple properties. It thus posts as old events the prior event of the same value(s), + /// if any. + /// + /// + /// The esper query. + /// The expressions. + /// + /// at least one property must be provided + public static EsperQuery Unique(this EsperQuery esperQuery, params System.Linq.Expressions.Expression>[] expressions) + { + if (expressions == null) + throw new ArgumentException("at least one property must be provided"); + + return esperQuery.FilterView(() => View.Create("unique", + expressions.Select(e => LinqToSoda.LinqToSodaExpression(e)).ToArray())); + } + + /// + /// Generates a view that groups events into sub-views by the value returned by the + /// combination of values returned by a list of properties. + /// + /// The properties return one or more group keys, by which the view creates sub-views for each distinct group key. + /// + /// + /// The esper query. + /// The property or expression. + /// + public static EsperQuery GroupData(this EsperQuery esperQuery, params string[] properties) + { + if (properties == null) + throw new ArgumentException("at least one property must be provided"); + + return esperQuery.FilterView(() => View.Create("group", + properties.Select(p => new PropertyValueExpression(p)).Cast().ToArray())); + } + + /// + /// Generates a view that counts the number of events received from a stream or view plus + /// any additional event properties or expression values listed as parameters. The count is + /// then output to the observer. + /// + /// + /// The esper query. + /// + public static EsperQuery Counting(this EsperQuery esperQuery) + { + return esperQuery.FilterView(() => View.Create("size")); + } + + /// + /// Generates a view that retains only the most recent arriving event. + /// + /// + /// The esper query. + /// + public static EsperQuery Last(this EsperQuery esperQuery) + { + return esperQuery.FilterView(() => View.Create("lastevent")); + } + + /// + /// Generates a view that retains only the first arriving event. + /// + /// + /// The esper query. + /// + public static EsperQuery First(this EsperQuery esperQuery) + { + return esperQuery.FilterView(() => View.Create("firstevent")); + } + + /// + /// Generates a view that retains only the first among events having the same value for the specified + /// properties. If used within a named window and an on-delete clause deletes the event, the view + /// resets and will retain the next arriving event for the expression result values of the deleted events. + /// + /// + /// The esper query. + /// The properties. + /// + /// at least one property must be provided + public static EsperQuery FirstUnique(this EsperQuery esperQuery, params string[] properties) + { + if (properties == null) + throw new ArgumentException("at least one property must be provided"); + + return esperQuery.FilterView(() => View.Create("firstunique", + properties.Select(p => new PropertyValueExpression(p)).Cast().ToArray())); + } + + } +} diff --git a/NEsper.Core/NEsper.Core/linq/TimeSpanExtensions.cs b/NEsper.Core/NEsper.Core/linq/TimeSpanExtensions.cs new file mode 100755 index 000000000..8707a2e4b --- /dev/null +++ b/NEsper.Core/NEsper.Core/linq/TimeSpanExtensions.cs @@ -0,0 +1,44 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client.soda; + +namespace com.espertech.esper.linq +{ + public static class TimeSpanExtensions + { + /// + /// Gets the time period expression. + /// + /// The time span. + /// + public static TimePeriodExpression ToTimePeriodExpression(this TimeSpan timeSpan) + { + var timePeriodExpression = new TimePeriodExpression( + ExpressionWhenNonZero(timeSpan.Days), + ExpressionWhenNonZero(timeSpan.Hours), + ExpressionWhenNonZero(timeSpan.Minutes), + ExpressionWhenNonZero(timeSpan.Seconds), + ExpressionWhenNonZero(timeSpan.Milliseconds) + ); + return timePeriodExpression; + } + + /// + /// Returns a constant expression when the underlying value is not zero. + /// + /// The value. + /// + public static ConstantExpression ExpressionWhenNonZero(int value) + { + return value != 0 ? new ConstantExpression(value) : null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/linq/WinViewExtensions.cs b/NEsper.Core/NEsper.Core/linq/WinViewExtensions.cs new file mode 100755 index 000000000..fb71bf7b4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/linq/WinViewExtensions.cs @@ -0,0 +1,177 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client.soda; + +namespace com.espertech.esper.linq +{ + public static class WinViewExtensions + { + /// + /// Expands the view to keep the first Count events. + /// + /// + /// The esper query. + /// The length. + /// + public static EsperQuery KeepFirst(this EsperQuery esperQuery, int length) + { + return esperQuery.FilterView(() => View.Create("firstlength", new ConstantExpression(length))); + } + + /// + /// Expands the view to keep events that occur within the specified duration. + /// + /// + /// The esper query. + /// The duration. + /// + public static EsperQuery KeepFirst(this EsperQuery esperQuery, TimeSpan duration) + { + var timePeriodExpression = duration.ToTimePeriodExpression(); + return esperQuery.FilterView(() => View.Create("firsttime", timePeriodExpression)); + } + + /// + /// Expands the view to keep events that satisfy the expression condition. The expiry expression can + /// be any expression including expressions on event properties, variables, aggregation functions or + /// user-defined functions. The view applies this expression to the oldest event(s) currently in the + /// view. + /// + /// + /// The esper query. + /// The expression. + /// + public static EsperQuery KeepWhile(this EsperQuery esperQuery, System.Linq.Expressions.Expression> expression) + { + return esperQuery.FilterView(() => View.Create("expr", LinqToSoda.LinqToSodaExpression(expression))); + } + + /// + /// Expands the view to keep events (tumbling window) until the given expression is satisfied. + /// + /// + /// The esper query. + /// The expression. + /// + public static EsperQuery KeepUntil(this EsperQuery esperQuery, System.Linq.Expressions.Expression> expression) + { + return esperQuery.FilterView(() => View.Create("expr_batch", LinqToSoda.LinqToSodaExpression(expression))); + } + + /// + /// Expands the view to keep all events. The view does not remove events from the + /// data window, unless used with a named window and the on delete clause. + /// + /// + /// The esper query. + /// + public static EsperQuery KeepAll(this EsperQuery esperQuery) + { + return esperQuery.FilterView(() => View.Create("keepall")); + } + + /// + /// Expands the view to keep a sliding window of events. This view is a moving (sliding) length + /// window extending the specified number of elements into the past. The view takes a single + /// expression as a parameter providing a numeric size value that defines the window size. + /// + /// If batch is specified, then the window buffers events (tumbling window) and releases them + /// when the given number of events has been collected. + /// + /// + /// The esper query. + /// The length. + /// if set to true [batched]. + /// + public static EsperQuery WithLength(this EsperQuery esperQuery, int length, bool batched = false) + { + var windowName = batched + ? "length_batch" + : "length"; + + return esperQuery.FilterView(() => View.Create(windowName, new ConstantExpression(length))); + } + + /// + /// Expands the view to use a time bound window. TimeInMillis bound windows are sliding windows that extend the + /// specified time interval into the past based on the system time. Provide a time period as parameter. + /// + /// If batch is specified, then the window buffers events (tumbling window) and releases them + /// after the given time interval has occurred. + /// + /// + /// The esper query. + /// The seconds. + /// if set to true [batched]. + /// + public static EsperQuery WithDuration(this EsperQuery esperQuery, int seconds, bool batched = false) + { + return WithDuration(esperQuery, TimeSpan.FromSeconds(seconds), batched); + } + + /// + /// Expands the view to use a time bound window. TimeInMillis bound windows are sliding windows that extend the + /// specified time interval into the past based on the system time. Provide a time period as parameter. + /// + /// + /// The esper query. + /// The time span. + /// if set to true [batched]. + /// + public static EsperQuery WithDuration(this EsperQuery esperQuery, TimeSpan timeSpan, bool batched = false) + { + var timePeriodExpression = timeSpan.ToTimePeriodExpression(); + var windowName = batched ? "time_batch" : "time"; + + return esperQuery.FilterView(() => View.Create(windowName, timePeriodExpression)); + } + + /// + /// Expands the view to use a time-accumulating. This data window view is a specialized moving (sliding) + /// time window that differs from the regular time window in that it accumulates events until no more events + /// arrive within a given time interval, and only then releases the accumulated events as a remove stream. + /// + /// + /// The esper query. + /// The time span. + /// + public static EsperQuery WithAccumlation(this EsperQuery esperQuery, TimeSpan timeSpan) + { + var timePeriodExpression = timeSpan.ToTimePeriodExpression(); + return esperQuery.FilterView(() => View.Create("time_accum", timePeriodExpression)); + } + + /// + /// Expands the view to use a time and length bound window. + /// + /// + /// The esper query. + /// The time span. + /// The length. + /// The flow control keywords. + /// + public static EsperQuery WithDurationAndLength(this EsperQuery esperQuery, TimeSpan timeSpan, int length, string flowControlKeywords = null) + { + var timePeriodExpression = timeSpan.ToTimePeriodExpression(); + var lengthExpression = new ConstantExpression(length); + + if (flowControlKeywords == null) + return esperQuery.FilterView(() => View.Create("time_length_batch", timePeriodExpression, lengthExpression)); + + // we want to take this apart and turn this into something people can use without having to know + // the keywords. an enumeration might work well here with the values being accepted as params on + // function call. + var flowControlExpression = new ConstantExpression(flowControlKeywords); + + return esperQuery.FilterView(() => View.Create("time_length_batch", timePeriodExpression, lengthExpression, flowControlExpression)); + } + } +} diff --git a/NEsper.Core/NEsper.Core/linq/statistics/CorrelationStatistics.cs b/NEsper.Core/NEsper.Core/linq/statistics/CorrelationStatistics.cs new file mode 100755 index 000000000..f2b6889c7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/linq/statistics/CorrelationStatistics.cs @@ -0,0 +1,15 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.linq.statistics +{ + public class CorrelationStatistics + { + public int Correlation { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/linq/statistics/RegressionStatistics.cs b/NEsper.Core/NEsper.Core/linq/statistics/RegressionStatistics.cs new file mode 100755 index 000000000..1f7252618 --- /dev/null +++ b/NEsper.Core/NEsper.Core/linq/statistics/RegressionStatistics.cs @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.linq.statistics +{ + public class RegressionStatistics + { + public double Slope { get; set; } + public double YIntercept { get; set; } + public double XAverage { get; set; } + public double XStandardDeviationPop { get; set; } + public double XStandardDeviationSample { get; set; } + public double XSum { get; set; } + public double XVariance { get; set; } + public double YAverage { get; set; } + public double YStandardDeviationPop { get; set; } + public double YStandardDeviationSample { get; set; } + public double YSum { get; set; } + public double YVariance { get; set; } + public int DataPoints { get; set; } + public int N { get; set; } + public double SumXsumXSq { get; set; } + public double SumXY { get; set; } + public double SumY { get; set; } + public double SumYSq { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/linq/statistics/UnivariateStatistics.cs b/NEsper.Core/NEsper.Core/linq/statistics/UnivariateStatistics.cs new file mode 100755 index 000000000..f521157a3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/linq/statistics/UnivariateStatistics.cs @@ -0,0 +1,24 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.linq.statistics +{ + /// + /// This class is not really output by the engine, but we do this to generate a + /// placeholder so that properties can be bound in expressions. + /// + public class UnivariateStatistics + { + public int Datapoints { get; set; } + public int Total { get; set; } + public double Average { get; set; } + public double Variance { get; set; } + public double Stddev { get; set; } + public double Stddevpa { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/metrics/instrumentation/Instrument.cs b/NEsper.Core/NEsper.Core/metrics/instrumentation/Instrument.cs new file mode 100755 index 000000000..d26a231c7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/metrics/instrumentation/Instrument.cs @@ -0,0 +1,103 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.metrics.instrumentation +{ + public class Instrument : IDisposable + { + private readonly Action _onExit; + private readonly Instrumentation _instrumentation; + + private static readonly Instrument VOID = new Instrument( + i => { }, + i => { }); + + public static IDisposable With(Action onEntry, Action onExit) + { + if (InstrumentationHelper.ENABLED) + { + return new Instrument(onEntry, onExit); + } + else + { + return VOID; + } + } + + public static void With(Action onEntry, Action onExit, Action action) + { + if (InstrumentationHelper.ENABLED) + { + var instrumentation = InstrumentationHelper.Get(); + onEntry(instrumentation); + try + { + action(); + } + finally + { + onExit(instrumentation); + } + } + else + { + action(); + } + } + + public static T With(Action onEntry, Action onExit, Func action) + { + if (InstrumentationHelper.ENABLED) + { + var instrumentation = InstrumentationHelper.Get(); + onEntry(instrumentation); + try + { + return action(); + } + finally + { + onExit(instrumentation); + } + } + else + { + return action(); + } + } + + + /// + /// Initializes a new instance of the class. + /// + /// The on entry. + /// The on exit. + public Instrument(Action onEntry, Action onExit) + { + if (InstrumentationHelper.ENABLED) + { + _instrumentation = InstrumentationHelper.Get(); + _onExit = onExit; + onEntry.Invoke(_instrumentation); + } + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + if (InstrumentationHelper.ENABLED) + { + _onExit.Invoke(_instrumentation); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/metrics/instrumentation/Instrumentation.cs b/NEsper.Core/NEsper.Core/metrics/instrumentation/Instrumentation.cs new file mode 100755 index 000000000..4c434236e --- /dev/null +++ b/NEsper.Core/NEsper.Core/metrics/instrumentation/Instrumentation.cs @@ -0,0 +1,825 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.aggregator; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.dot; +using com.espertech.esper.epl.expression.funcs; +using com.espertech.esper.epl.expression.ops; +using com.espertech.esper.epl.expression.prev; +using com.espertech.esper.epl.expression.prior; +using com.espertech.esper.epl.expression.subquery; +using com.espertech.esper.epl.expression.time; +using com.espertech.esper.epl.join.exec.@base; +using com.espertech.esper.epl.join.table; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.epl.named; +using com.espertech.esper.epl.rettype; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.updatehelper; +using com.espertech.esper.filter; +using com.espertech.esper.pattern; +using com.espertech.esper.rowregex; +using com.espertech.esper.schedule; +using com.espertech.esper.type; +using com.espertech.esper.view; + +namespace com.espertech.esper.metrics.instrumentation +{ + public interface Instrumentation + { + void QStimulantEvent(EventBean eventBean, string engineURI); + + void AStimulantEvent(); + + void QStimulantTime(long currentTime, string engineURI); + + void AStimulantTime(); + + void QEvent(EventBean eventBean, string engineURI, bool providedBySendEvent); + + void AEvent(); + + void QEventCP(EventBean theEvent, EPStatementAgentInstanceHandle handle, long engineTime); + + void AEventCP(); + + void QTime(long engineTime, string engineURI); + + void ATime(); + + void QTimeCP(EPStatementAgentInstanceHandle handle, long engineTime); + + void ATimeCP(); + + void QNamedWindowDispatch(string engineURI); + + void ANamedWindowDispatch(); + + void QNamedWindowCPSingle(string engineURI, IList value, EventBean[] newData, EventBean[] oldData, EPStatementAgentInstanceHandle handle, long time); + + void ANamedWindowCPSingle(); + + void QNamedWindowCPMulti(string engineURI, IDictionary deltaPerConsumer, EPStatementAgentInstanceHandle handle, long time); + + void ANamedWindowCPMulti(); + + void QRegEx(EventBean newEvent, RegexPartitionState partitionState); + + void ARegEx(RegexPartitionState partitionState, IList endStates, IList terminationStates); + + void QRegExState(RegexNFAStateEntry currentState, IDictionary> variableStreams, int[] multimatchStreamNumToVariable); + + void ARegExState(IList next, IDictionary> variableStreams, int[] multimatchStreamNumToVariable); + + void QRegExStateStart(RegexNFAState startState, IDictionary> variableStreams, int[] multimatchStreamNumToVariable); + + void ARegExStateStart(IList nextStates, IDictionary> variableStreams, int[] multimatchStreamNumToVariable); + + void QRegExPartition(ExprNode[] partitionExpressionNodes); + + void ARegExPartition(bool exists, RegexPartitionState state); + + void QRegIntervalValue(ExprNode exprNode); + + void ARegIntervalValue(long result); + + void QRegIntervalState(RegexNFAStateEntry endState, IDictionary> variableStreams, int[] multimatchStreamNumToVariable, long engineTime); + + void ARegIntervalState(bool scheduled); + + void QRegOut(EventBean[] outBeans); + + void ARegOut(); + + void QRegMeasure(RegexNFAStateEntry endState, IDictionary> variableStreams, int[] multimatchStreamNumToVariable); + + void ARegMeasure(EventBean outBean); + + void QRegExScheduledEval(); + + void ARegExScheduledEval(); + + void QExprBool(ExprNode exprNode, EventBean[] eventsPerStream); + + void AExprBool(bool? result); + + void QExprValue(ExprNode exprNode, EventBean[] eventsPerStream); + + void AExprValue(Object result); + + void QExprEquals(ExprNode exprNode); + + void AExprEquals(bool? result); + + void QExprAnd(ExprNode exprNode); + + void AExprAnd(bool? result); + + void QExprLike(ExprNode exprNode); + + void AExprLike(bool? result); + + void QExprBitwise(ExprNode exprNode, BitWiseOpEnum bitWiseOpEnum); + + void AExprBitwise(Object result); + + void QExprMath(ExprMathNode exprMathNode, string op); + + void AExprMath(Object result); + + void QExprRegexp(ExprRegexpNode exprRegexpNode); + + void AExprRegexp(bool? result); + + void QExprIdent(string fullUnresolvedName); + + void AExprIdent(Object result); + + void QExprTypeof(); + + void AExprTypeof(string typeName); + + void QExprOr(ExprOrNode exprOrNode); + + void AExprOr(bool? result); + + void QExprIn(ExprInNodeImpl exprInNode); + + void AExprIn(bool? result); + + void QExprCoalesce(ExprCoalesceNode exprCoalesceNode); + + void AExprCoalesce(Object value); + + void QExprConcat(ExprConcatNode exprConcatNode); + + void AExprConcat(string result); + + void QaExprConst(Object result); + + void QaExprTimestamp(ExprTimestampNode exprTimestampNode, long value); + + void QExprBetween(ExprBetweenNodeImpl exprBetweenNode); + + void AExprBetween(bool? result); + + void QExprCast(ExprCastNode exprCastNode); + + void AExprCast(Object result); + + void QExprCase(ExprCaseNode exprCaseNode); + + void AExprCase(Object result); + + void QExprArray(ExprArrayNode exprArrayNode); + + void AExprArray(Object result); + + void QExprEqualsAnyOrAll(ExprEqualsAllAnyNode exprEqualsAllAnyNode); + + void AExprEqualsAnyOrAll(bool? result); + + void QExprMinMaxRow(ExprMinMaxRowNode exprMinMaxRowNode); + + void AExprMinMaxRow(Object result); + + void QExprNew(ExprNewStructNode exprNewNode); + + void AExprNew(IDictionary props); + + void QExprNot(ExprNotNode exprNotNode); + + void AExprNot(bool? result); + + void QExprPropExists(ExprPropertyExistsNode exprPropertyExistsNode); + + void AExprPropExists(bool exists); + + void QExprRelOpAnyOrAll(ExprRelationalOpAllAnyNode exprRelationalOpAllAnyNode, string op); + + void AExprRelOpAnyOrAll(bool? result); + + void QExprRelOp(ExprRelationalOpNodeImpl exprRelationalOpNode, string op); + + void AExprRelOp(bool? result); + + void QExprStreamUnd(ExprStreamUnderlyingNodeImpl exprStreamUnderlyingNode); + + void AExprStreamUnd(Object result); + + void QExprStreamUndSelectClause(ExprStreamUnderlyingNode undNode); + + void AExprStreamUndSelectClause(EventBean @event); + + void QExprIs(ExprEqualsNodeImpl exprNode); + + void AExprIs(bool result); + + void QExprVariable(ExprVariableNode exprVariableNode); + + void AExprVariable(Object value); + + void QExprTimePeriod(ExprTimePeriodImpl exprTimePeriod); + + void AExprTimePeriod(Object result); + + void QExprInstanceof(ExprInstanceofNode exprInstanceofNode); + + void AExprInstanceof(bool? result); + + void QExprContextProp(ExprContextPropertyNode exprContextPropertyNode); + + void AExprContextProp(Object result); + + void QExprPlugInSingleRow(MethodInfo method); + + void AExprPlugInSingleRow(Object result); + + void QaExprAggValue(ExprAggregateNodeBase exprAggregateNodeBase, Object value); + + void QExprSubselect(ExprSubselectNode exprSubselectNode); + + void AExprSubselect(Object result); + + void QExprDot(ExprDotNode exprDotNode); + + void AExprDot(Object result); + + void QExprDotChain(EPType targetTypeInfo, Object target, ExprDotEval[] evalUnpacking); + + void AExprDotChain(); + + void QExprDotChainElement(int num, ExprDotEval methodEval); + + void AExprDotChainElement(EPType typeInfo, Object result); + + void QaExprIStream(ExprIStreamNode exprIStreamNode, bool newData); + + void QExprDeclared(ExpressionDeclItem parent); + + void AExprDeclared(Object value); + + void QExprPrev(ExprPreviousNode exprPreviousNode, bool newData); + + void AExprPrev(Object result); + + void QExprPrior(ExprPriorNode exprPriorNode); + + void AExprPrior(Object result); + + void QExprStreamUndMethod(ExprDotNode exprDotEvalStreamMethod); + + void AExprStreamUndMethod(Object result); + + void QExprStreamEventMethod(ExprDotNode exprDotNode); + + void AExprStreamEventMethod(Object result); + + void QExprTableSubproperty(ExprNode exprNode, string tableName, string subpropName); + + void AExprTableSubproperty(Object result); + + void QExprTableTop(ExprNode exprNode, string tableName); + + void AExprTableTop(Object result); + + void QExprTableSubpropAccessor(ExprNode exprNode, string tableName, string subpropName, ExprAggregateNode aggregationExpression); + + void AExprTableSubpropAccessor(Object result); + + void QScheduleAdd(long currentTime, long afterMSec, ScheduleHandle handle, long slot); + + void AScheduleAdd(); + + void QScheduleRemove(ScheduleHandle handle, long slot); + + void AScheduleRemove(); + + void QScheduleEval(long currentTime); + + void AScheduleEval(ICollection handles); + + void QPatternAndEvaluateTrue(EvalAndNode evalAndNode, MatchedEventMap passUp); + + void APatternAndEvaluateTrue(bool quitted); + + void QPatternAndQuit(EvalAndNode evalAndNode); + + void APatternAndQuit(); + + void QPatternAndEvaluateFalse(EvalAndNode evalAndNode); + + void APatternAndEvaluateFalse(); + + void QPatternAndStart(EvalAndNode evalAndNode, MatchedEventMap beginState); + + void APatternAndStart(); + + void QPatternFollowedByEvaluateTrue(EvalFollowedByNode evalFollowedByNode, MatchedEventMap matchEvent, int? index); + + void APatternFollowedByEvaluateTrue(bool quitted); + + void QPatternFollowedByQuit(EvalFollowedByNode evalFollowedByNode); + + void APatternFollowedByQuit(); + + void QPatternFollowedByEvalFalse(EvalFollowedByNode evalFollowedByNode); + + void APatternFollowedByEvalFalse(); + + void QPatternFollowedByStart(EvalFollowedByNode evalFollowedByNode, MatchedEventMap beginState); + + void APatternFollowedByStart(); + + void QPatternOrEvaluateTrue(EvalOrNode evalOrNode, MatchedEventMap matchEvent); + + void APatternOrEvaluateTrue(bool quitted); + + void QPatternOrEvalFalse(EvalOrNode evalOrNode); + + void APatternOrEvalFalse(); + + void QPatternOrQuit(EvalOrNode evalOrNode); + + void APatternOrQuit(); + + void APatternOrStart(); + + void QPatternOrStart(EvalOrNode evalOrNode, MatchedEventMap beginState); + + void QPatternFilterMatch(EvalFilterNode filterNode, EventBean theEvent); + + void APatternFilterMatch(bool quitted); + + void QPatternFilterStart(EvalFilterNode evalFilterNode, MatchedEventMap beginState); + + void APatternFilterStart(); + + void QPatternFilterQuit(EvalFilterNode evalFilterNode, MatchedEventMap beginState); + + void APatternFilterQuit(); + + void QPatternRootEvaluateTrue(MatchedEventMap matchEvent); + + void APatternRootEvaluateTrue(bool quitted); + + void QPatternRootStart(MatchedEventMap root); + + void APatternRootStart(); + + void QPatternRootQuit(); + + void APatternRootQuit(); + + void QPatternRootEvalFalse(); + + void APatternRootEvalFalse(); + + void QPatternEveryEvaluateTrue(EvalEveryNode evalEveryNode, MatchedEventMap matchEvent); + + void APatternEveryEvaluateTrue(); + + void QPatternEveryStart(EvalEveryNode evalEveryNode, MatchedEventMap beginState); + + void APatternEveryStart(); + + void QPatternEveryEvalFalse(EvalEveryNode evalEveryNode); + + void APatternEveryEvalFalse(); + + void QPatternEveryQuit(EvalEveryNode evalEveryNode); + + void APatternEveryQuit(); + + void QPatternEveryDistinctEvaluateTrue(EvalEveryDistinctNode everyDistinctNode, MatchedEventMap matchEvent); + + void APatternEveryDistinctEvaluateTrue(ISet keysFromNodeNoExpire, IDictionary keysFromNodeExpire, Object matchEventKey, bool haveSeenThis); + + void QPatternEveryDistinctQuit(EvalEveryDistinctNode everyNode); + + void APatternEveryDistinctQuit(); + + void QPatternEveryDistinctEvalFalse(EvalEveryDistinctNode everyNode); + + void APatternEveryDistinctEvalFalse(); + + void QPatternEveryDistinctStart(EvalEveryDistinctNode everyNode, MatchedEventMap beginState); + + void APatternEveryDistinctStart(); + + void QPatternGuardEvaluateTrue(EvalGuardNode evalGuardNode, MatchedEventMap matchEvent); + + void APatternGuardEvaluateTrue(bool quitted); + + void QPatternGuardStart(EvalGuardNode evalGuardNode, MatchedEventMap beginState); + + void APatternGuardStart(); + + void QPatternGuardQuit(EvalGuardNode evalGuardNode); + + void APatternGuardQuit(); + + void QPatternGuardGuardQuit(EvalGuardNode evalGuardNode); + + void APatternGuardGuardQuit(); + + void QPatternGuardScheduledEval(); + + void APatternGuardScheduledEval(); + + void QPatternMatchUntilEvaluateTrue(EvalMatchUntilNode evalMatchUntilNode, MatchedEventMap matchEvent, bool matchFromUntil); + + void APatternMatchUntilEvaluateTrue(bool quitted); + + void QPatternMatchUntilStart(EvalMatchUntilNode evalMatchUntilNode, MatchedEventMap beginState); + + void APatternMatchUntilStart(); + + void QPatternMatchUntilEvalFalse(EvalMatchUntilNode evalMatchUntilNode, bool matchFromUntil); + + void APatternMatchUntilEvalFalse(); + + void QPatternMatchUntilQuit(EvalMatchUntilNode evalMatchUntilNode); + + void APatternMatchUntilQuit(); + + void QPatternNotEvaluateTrue(EvalNotNode evalNotNode, MatchedEventMap matchEvent); + + void APatternNotEvaluateTrue(bool quitted); + + void APatternNotQuit(); + + void QPatternNotQuit(EvalNotNode evalNotNode); + + void QPatternNotStart(EvalNotNode evalNotNode, MatchedEventMap beginState); + + void APatternNotStart(); + + void QPatternNotEvalFalse(EvalNotNode evalNotNode); + + void APatternNotEvalFalse(); + + void QPatternObserverEvaluateTrue(EvalObserverNode evalObserverNode, MatchedEventMap matchEvent); + + void APatternObserverEvaluateTrue(); + + void QPatternObserverStart(EvalObserverNode evalObserverNode, MatchedEventMap beginState); + + void APatternObserverStart(); + + void QPatternObserverQuit(EvalObserverNode evalObserverNode); + + void APatternObserverQuit(); + + void QPatternObserverScheduledEval(); + + void APatternObserverScheduledEval(); + + void QContextPartitionAllocate(AgentInstanceContext agentInstanceContext); + + void AContextPartitionAllocate(); + + void QContextPartitionDestroy(AgentInstanceContext agentInstanceContext); + + void AContextPartitionDestroy(); + + void QContextScheduledEval(ContextDescriptor contextDescriptor); + + void AContextScheduledEval(); + + void QOutputProcessNonBuffered(EventBean[] newData, EventBean[] oldData); + + void AOutputProcessNonBuffered(); + + void QOutputProcessNonBufferedJoin(ISet> newEvents, ISet> oldEvents); + + void AOutputProcessNonBufferedJoin(); + + void QOutputProcessWCondition(EventBean[] newData, EventBean[] oldData); + + void AOutputProcessWCondition(bool buffered); + + void QOutputProcessWConditionJoin(ISet> newEvents, ISet> oldEvents); + + void AOutputProcessWConditionJoin(bool buffered); + + void QOutputRateConditionUpdate(int newDataLength, int oldDataLength); + + void AOutputRateConditionUpdate(); + + void QOutputRateConditionOutputNow(); + + void AOutputRateConditionOutputNow(bool generate); + + void QOutputRateConditionScheduledEval(); + + void AOutputRateConditionScheduledEval(); + + void QResultSetProcessSimple(); + + void AResultSetProcessSimple(EventBean[] selectNewEvents, EventBean[] selectOldEvents); + + void QResultSetProcessUngroupedFullyAgg(); + + void AResultSetProcessUngroupedFullyAgg(EventBean[] selectNewEvents, EventBean[] selectOldEvents); + + void QResultSetProcessUngroupedNonfullyAgg(); + + void AResultSetProcessUngroupedNonfullyAgg(EventBean[] selectNewEvents, EventBean[] selectOldEvents); + + void QResultSetProcessGroupedRowPerGroup(); + + void AResultSetProcessGroupedRowPerGroup(EventBean[] selectNewEvents, EventBean[] selectOldEvents); + + void QResultSetProcessGroupedRowPerEvent(); + + void AResultSetProcessGroupedRowPerEvent(EventBean[] selectNewEvents, EventBean[] selectOldEvents); + + void QResultSetProcessComputeGroupKeys(bool enter, ExprNode[] groupKeyNodeExpressions, EventBean[] eventsPerStream); + + void AResultSetProcessComputeGroupKeys(bool enter, Object groupKeysPerEvent); + + void QAggregationUngroupedApplyEnterLeave(bool enter, int numAggregators, int numAccessStates); + + void AAggregationUngroupedApplyEnterLeave(bool enter); + + void QAggregationGroupedApplyEnterLeave(bool enter, int numAggregators, int numAccessStates, Object groupKey); + + void AAggregationGroupedApplyEnterLeave(bool enter); + + void QAggregationGroupedRollupEvalParam(bool enter, int length); + + void AAggregationGroupedRollupEvalParam(Object result); + + void QAggNoAccessEnterLeave(bool enter, int index, AggregationMethod aggregationMethod, ExprNode aggExpr); + + void AAggNoAccessEnterLeave(bool enter, int index, AggregationMethod aggregationMethod); + + void QAggAccessEnterLeave(bool enter, int index, AggregationState state, ExprNode aggExpr); + + void AAggAccessEnterLeave(bool enter, int index, AggregationState state); + + void QSelectClause(EventBean[] eventsPerStream, bool newData, bool synthesize, ExprEvaluatorContext exprEvaluatorContext); + + void ASelectClause(bool newData, EventBean @event, Object[] subscriberParameters); + + void QViewProcessIRStream(View view, string viewName, EventBean[] newData, EventBean[] oldData); + + void AViewProcessIRStream(); + + void QViewScheduledEval(View view, string viewName); + + void AViewScheduledEval(); + + void QViewIndicate(View view, string viewName, EventBean[] newData, EventBean[] oldData); + + void AViewIndicate(); + + void QSubselectAggregation(ExprNode optionalFilterExprNode); + + void ASubselectAggregation(); + + void QFilterActivationSubselect(string eventTypeName, ExprSubselectNode subselectNode); + + void AFilterActivationSubselect(); + + void QFilterActivationStream(string eventTypeName, int streamNumber); + + void AFilterActivationStream(); + + void QFilterActivationNamedWindowInsert(string namedWindowName); + + void AFilterActivationNamedWindowInsert(); + + void QFilterActivationOnTrigger(string eventTypeName); + + void AFilterActivationOnTrigger(); + + void QRouteBetweenStmt(EventBean theEvent, EPStatementHandle epStatementHandle, bool addToFront); + + void ARouteBetweenStmt(); + + void QIndexAddRemove(EventTable eventTable, EventBean[] newData, EventBean[] oldData); + + void AIndexAddRemove(); + + void QIndexAdd(EventTable eventTable, EventBean[] addEvents); + + void AIndexAdd(); + + void QIndexRemove(EventTable eventTable, EventBean[] removeEvents); + + void AIndexRemove(); + + void QIndexSubordLookup(SubordTableLookupStrategy subordTableLookupStrategy, EventTable optionalEventIndex, int[] keyStreamNums); + + void AIndexSubordLookup(ICollection events, Object keys); + + void QIndexJoinLookup(JoinExecTableLookupStrategy strategy, EventTable index); + + void AIndexJoinLookup(ICollection result, object keys); + + void QFilter(EventBean theEvent); + + void AFilter(ICollection matches); + + void QFilterHandleSetIndexes(IList indizes); + + void AFilterHandleSetIndexes(); + + void QaFilterHandleSetCallbacks(ICollection callbackSet); + + void QFilterReverseIndex(FilterParamIndexLookupableBase filterParamIndex, Object propertyValue); + + void AFilterReverseIndex(bool? match); + + void QFilterBoolean(FilterParamIndexBooleanExpr filterParamIndexBooleanExpr); + + void AFilterBoolean(); + + void QFilterBooleanExpr(int num, KeyValuePair evals); + + void AFilterBooleanExpr(bool result); + + void QFilterAdd(FilterValueSet filterValueSet, FilterHandle filterCallback); + + void AFilterAdd(); + + void QFilterRemove(FilterHandle filterCallback, EventTypeIndexBuilderValueIndexesPair pair); + + void AFilterRemove(); + + void QWhereClauseFilter(ExprNode exprNode, EventBean[] newData, EventBean[] oldData); + + void AWhereClauseFilter(EventBean[] filteredNewData, EventBean[] filteredOldData); + + void QWhereClauseFilterEval(int num, EventBean @event, bool newData); + + void AWhereClauseFilterEval(bool? pass); + + void QWhereClauseIR(EventBean[] filteredNewData, EventBean[] filteredOldData); + + void AWhereClauseIR(); + + void QHavingClauseNonJoin(EventBean theEvent); + + void AHavingClauseNonJoin(bool? pass); + + void QHavingClauseJoin(EventBean[] eventsPerStream); + + void AHavingClauseJoin(bool? pass); + + void QOrderBy(EventBean[] evalEventsPerStream, OrderByElement[] orderBy); + + void AOrderBy(Object values); + + void QJoinDispatch(EventBean[][] newDataPerStream, EventBean[][] oldDataPerStream); + + void AJoinDispatch(); + + void QJoinExexStrategy(); + + void AJoinExecStrategy(UniformPair>> joinSet); + + void QJoinExecFilter(); + + void AJoinExecFilter(ISet> newEvents, ISet> oldEvents); + + void QJoinExecProcess(UniformPair>> joinSet); + + void AJoinExecProcess(); + + void QJoinCompositionStreamToWin(); + + void AJoinCompositionStreamToWin(ISet> newResults); + + void QJoinCompositionWinToWin(); + + void AJoinCompositionWinToWin(ISet> newResults, ISet> oldResults); + + void QJoinCompositionHistorical(); + + void AJoinCompositionHistorical(ISet> newResults, ISet> oldResults); + + void QJoinCompositionStepUpdIndex(int stream, EventBean[] added, EventBean[] removed); + + void AJoinCompositionStepUpdIndex(); + + void QJoinCompositionQueryStrategy(bool insert, int streamNum, EventBean[] events); + + void AJoinCompositionQueryStrategy(); + + void QInfraTriggeredLookup(SubordWMatchExprLookupStrategyType lookupStrategy); + + void AInfraTriggeredLookup(EventBean[] result); + + void QInfraOnAction(OnTriggerType triggerType, EventBean[] triggerEvents, EventBean[] matchingEvents); + + void AInfraOnAction(); + + void QInfraUpdate(EventBean beforeUpdate, EventBean[] eventsPerStream, int length, bool copy); + + void AInfraUpdate(EventBean afterUpdate); + + void QInfraUpdateRHSExpr(int index, EventBeanUpdateItem updateItem); + + void AInfraUpdateRHSExpr(Object result); + + void QInfraMergeWhenThens(bool matched, EventBean triggerEvent, int numWhenThens); + + void AInfraMergeWhenThens(bool matched); + + void QInfraMergeWhenThenItem(bool matched, int count); + + void AInfraMergeWhenThenItem(bool matched, bool actionsApplied); + + void QInfraMergeWhenThenActions(int numActions); + + void AInfraMergeWhenThenActions(); + + void QInfraMergeWhenThenActionItem(int count, string actionName); + + void AInfraMergeWhenThenActionItem(bool applies); + + void QEngineManagementStmtCompileStart(string engineURI, int statementId, string statementName, string epl, long engineTime); + + void AEngineManagementStmtCompileStart(bool success, string message); + + void QaEngineManagementStmtStarted(string engineURI, int statementId, string statementName, string epl, long engineTime); + + void QEngineManagementStmtStop(EPStatementState targetState, string engineURI, int statementId, string statementName, string epl, long engineTime); + + void AEngineManagementStmtStop(); + + void QaStatementResultExecute(UniformPair events, int statementId, string statementName, int agentInstanceId, long threadId); + + void QSplitStream(bool all, EventBean theEvent, ExprEvaluator[] whereClauses); + + void ASplitStream(bool all, bool handled); + + void QSplitStreamWhere(int index); + + void ASplitStreamWhere(bool? pass); + + void QSplitStreamRoute(int index); + + void ASplitStreamRoute(); + + void QUpdateIStream(InternalEventRouterEntry[] entries); + + void AUpdateIStream(EventBean finalEvent, bool haveCloned); + + void QUpdateIStreamApply(int index, InternalEventRouterEntry entry); + + void AUpdateIStreamApply(EventBean updated, bool applied); + + void QUpdateIStreamApplyWhere(); + + void AUpdateIStreamApplyWhere(bool? result); + + void QUpdateIStreamApplyAssignments(InternalEventRouterEntry entry); + + void AUpdateIStreamApplyAssignments(Object[] values); + + void QUpdateIStreamApplyAssignmentItem(int index); + + void AUpdateIStreamApplyAssignmentItem(Object value); + + void QHistoricalScheduledEval(); + + void AHistoricalScheduledEval(); + + void QTableAddEvent(EventBean theEvent); + + void ATableAddEvent(); + + void QTableDeleteEvent(EventBean theEvent); + + void ATableDeleteEvent(); + + void QaTableUpdatedEvent(EventBean theEvent); + + void QaTableUpdatedEventWKeyBefore(EventBean theEvent); + + void QaTableUpdatedEventWKeyAfter(EventBean theEvent); + } + +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/metrics/instrumentation/InstrumentationAgent.cs b/NEsper.Core/NEsper.Core/metrics/instrumentation/InstrumentationAgent.cs new file mode 100755 index 000000000..bcea1efa7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/metrics/instrumentation/InstrumentationAgent.cs @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.metrics.instrumentation +{ + public interface InstrumentationAgent + { + void IndicateQ(); + void IndicateA(); + } + + public class ProxyInstrumentationAgent : InstrumentationAgent + { + public Action ProcIndicateQ { get; set; } + public Action ProcIndicateA { get; set; } + + public void IndicateQ() { ProcIndicateQ(); } + public void IndicateA() { ProcIndicateA(); } + } +} diff --git a/NEsper.Core/NEsper.Core/metrics/instrumentation/InstrumentationAssertionService.cs b/NEsper.Core/NEsper.Core/metrics/instrumentation/InstrumentationAssertionService.cs new file mode 100755 index 000000000..d2c325653 --- /dev/null +++ b/NEsper.Core/NEsper.Core/metrics/instrumentation/InstrumentationAssertionService.cs @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.metrics.instrumentation +{ + public interface InstrumentationAssertionService + { + void StartTest(EPServiceProvider engine, Type testClass, string testName); + void EndTest(); + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/metrics/instrumentation/InstrumentationDefault.cs b/NEsper.Core/NEsper.Core/metrics/instrumentation/InstrumentationDefault.cs new file mode 100755 index 000000000..20dc605ef --- /dev/null +++ b/NEsper.Core/NEsper.Core/metrics/instrumentation/InstrumentationDefault.cs @@ -0,0 +1,1600 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.agg.aggregator; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.dot; +using com.espertech.esper.epl.expression.funcs; +using com.espertech.esper.epl.expression.ops; +using com.espertech.esper.epl.expression.prev; +using com.espertech.esper.epl.expression.prior; +using com.espertech.esper.epl.expression.subquery; +using com.espertech.esper.epl.expression.time; +using com.espertech.esper.epl.join.exec.@base; +using com.espertech.esper.epl.join.table; +using com.espertech.esper.epl.lookup; +using com.espertech.esper.epl.named; +using com.espertech.esper.epl.rettype; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.updatehelper; +using com.espertech.esper.filter; +using com.espertech.esper.pattern; +using com.espertech.esper.rowregex; +using com.espertech.esper.schedule; +using com.espertech.esper.type; +using com.espertech.esper.view; + +namespace com.espertech.esper.metrics.instrumentation +{ + public class InstrumentationDefault : Instrumentation + { + public void QStimulantEvent(EventBean eventBean, string engineURI) { + + } + + public void AStimulantEvent() { + + } + + public void QStimulantTime(long currentTime, string engineURI) { + + } + + public void AStimulantTime() { + + } + + public void QEvent(EventBean eventBean, string engineURI, bool providedBySendEvent) { + + } + + public void AEvent() { + + } + + public void QEventCP(EventBean theEvent, EPStatementAgentInstanceHandle handle, long engineTime) { + + } + + public void AEventCP() { + + } + + public void QTime(long engineTime, string engineURI) { + + } + + public void ATime() { + + } + + public void QTimeCP(EPStatementAgentInstanceHandle handle, long engineTime) { + + } + + public void ATimeCP() { + + } + + public void QNamedWindowDispatch(string engineURI) { + + } + + public void ANamedWindowDispatch() { + + } + + public void QNamedWindowCPSingle(string engineURI, IList value, EventBean[] newData, EventBean[] oldData, EPStatementAgentInstanceHandle handle, long time) { + + } + + public void ANamedWindowCPSingle() { + + } + + public void QNamedWindowCPMulti(string engineURI, IDictionary deltaPerConsumer, EPStatementAgentInstanceHandle handle, long time) { + + } + + public void ANamedWindowCPMulti() { + + } + + public void QRegEx(EventBean newEvent, RegexPartitionState partitionState) { + + } + + public void ARegEx(RegexPartitionState partitionState, IList endStates, IList terminationStates) { + + } + + public void QRegExState(RegexNFAStateEntry currentState, IDictionary> variableStreams, int[] multimatchStreamNumToVariable) { + + } + + public void ARegExState(IList next, IDictionary> variableStreams, int[] multimatchStreamNumToVariable) { + + } + + public void QRegExStateStart(RegexNFAState startState, IDictionary> variableStreams, int[] multimatchStreamNumToVariable) { + + } + + public void ARegExStateStart(IList nextStates, IDictionary> variableStreams, int[] multimatchStreamNumToVariable) { + + } + + public void QRegExPartition(ExprNode[] partitionExpressionNodes) { + + } + + public void ARegExPartition(bool exists, RegexPartitionState state) { + + } + + public void QRegIntervalValue(ExprNode exprNode) { + + } + + public void ARegIntervalValue(long result) { + + } + + public void QRegIntervalState(RegexNFAStateEntry endState, IDictionary> variableStreams, int[] multimatchStreamNumToVariable, long engineTime) { + + } + + public void ARegIntervalState(bool scheduled) { + + } + + public void QRegOut(EventBean[] outBeans) { + + } + + public void ARegOut() { + + } + + public void QRegMeasure(RegexNFAStateEntry endState, IDictionary> variableStreams, int[] multimatchStreamNumToVariable) { + + } + + public void ARegMeasure(EventBean outBean) { + + } + + public void QRegExScheduledEval() { + + } + + public void ARegExScheduledEval() { + + } + + public void QExprBool(ExprNode exprNode, EventBean[] eventsPerStream) { + + } + + public void AExprBool(bool? result) { + + } + + public void QExprValue(ExprNode exprNode, EventBean[] eventsPerStream) { + + } + + public void AExprValue(object result) { + + } + + public void QExprEquals(ExprNode exprNode) { + + } + + public void AExprEquals(bool? result) { + + } + + public void QExprAnd(ExprNode exprNode) { + + } + + public void AExprAnd(bool? result) { + + } + + public void QExprLike(ExprNode exprNode) { + + } + + public void AExprLike(bool? result) { + + } + + public void QExprBitwise(ExprNode exprNode, BitWiseOpEnum bitWiseOpEnum) { + + } + + public void AExprBitwise(object result) { + + } + + public void QExprMath(ExprMathNode exprMathNode, string op) { + + } + + public void AExprMath(object result) { + + } + + public void QExprRegexp(ExprRegexpNode exprRegexpNode) { + + } + + public void AExprRegexp(bool? result) { + + } + + public void QExprIdent(string fullUnresolvedName) { + + } + + public void AExprIdent(object result) { + + } + + public void QExprTypeof() { + + } + + public void AExprTypeof(string typeName) { + + } + + public void QExprOr(ExprOrNode exprOrNode) { + + } + + public void AExprOr(bool? result) { + + } + + public void QExprIn(ExprInNodeImpl exprInNode) { + + } + + public void AExprIn(bool? result) { + + } + + public void QExprCoalesce(ExprCoalesceNode exprCoalesceNode) { + + } + + public void AExprCoalesce(object value) { + + } + + public void QExprConcat(ExprConcatNode exprConcatNode) { + + } + + public void AExprConcat(string result) { + + } + + public void QaExprConst(object result) { + + } + + public void QaExprTimestamp(ExprTimestampNode exprTimestampNode, long value) { + + } + + public void QExprBetween(ExprBetweenNodeImpl exprBetweenNode) { + + } + + public void AExprBetween(bool? result) { + + } + + public void QExprCast(ExprCastNode exprCastNode) { + + } + + public void AExprCast(object result) { + + } + + public void QExprCase(ExprCaseNode exprCaseNode) { + + } + + public void AExprCase(object result) { + + } + + public void QExprArray(ExprArrayNode exprArrayNode) { + + } + + public void AExprArray(object result) { + + } + + public void QExprEqualsAnyOrAll(ExprEqualsAllAnyNode exprEqualsAllAnyNode) { + + } + + public void AExprEqualsAnyOrAll(bool? result) { + + } + + public void QExprMinMaxRow(ExprMinMaxRowNode exprMinMaxRowNode) { + + } + + public void AExprMinMaxRow(object result) { + + } + + public void QExprNew(ExprNewStructNode exprNewNode) { + + } + + public void AExprNew(IDictionary props) { + + } + + public void QExprNot(ExprNotNode exprNotNode) { + + } + + public void AExprNot(bool? result) { + + } + + public void QExprPropExists(ExprPropertyExistsNode exprPropertyExistsNode) { + + } + + public void AExprPropExists(bool exists) { + + } + + public void QExprRelOpAnyOrAll(ExprRelationalOpAllAnyNode exprRelationalOpAllAnyNode, string op) { + + } + + public void AExprRelOpAnyOrAll(bool? result) { + + } + + public void QExprRelOp(ExprRelationalOpNodeImpl exprRelationalOpNode, string op) { + + } + + public void AExprRelOp(bool? result) { + + } + + public void QExprStreamUnd(ExprStreamUnderlyingNodeImpl exprStreamUnderlyingNode) { + + } + + public void AExprStreamUnd(object result) { + + } + + public void QExprStreamUndSelectClause(ExprStreamUnderlyingNode undNode) { + + } + + public void AExprStreamUndSelectClause(EventBean @event) { + + } + + public void QExprIs(ExprEqualsNodeImpl exprNode) { + + } + + public void AExprIs(bool result) { + + } + + public void QExprVariable(ExprVariableNode exprVariableNode) { + + } + + public void AExprVariable(object value) { + + } + + public void QExprTimePeriod(ExprTimePeriodImpl exprTimePeriod) { + + } + + public void AExprTimePeriod(object result) { + + } + + public void QExprInstanceof(ExprInstanceofNode exprInstanceofNode) { + + } + + public void AExprInstanceof(bool? result) { + + } + + public void QExprContextProp(ExprContextPropertyNode exprContextPropertyNode) { + + } + + public void AExprContextProp(object result) { + + } + + public void QExprPlugInSingleRow(MethodInfo method) { + + } + + public void AExprPlugInSingleRow(object result) { + + } + + public void QaExprAggValue(ExprAggregateNodeBase exprAggregateNodeBase, object value) { + + } + + public void QExprSubselect(ExprSubselectNode exprSubselectNode) { + + } + + public void AExprSubselect(object result) { + + } + + public void QExprDot(ExprDotNode exprDotNode) { + + } + + public void AExprDot(object result) { + + } + + public void QExprDotChain(EPType targetTypeInfo, object target, ExprDotEval[] evalUnpacking) { + + } + + public void AExprDotChain() { + + } + + public void QExprDotChainElement(int num, ExprDotEval methodEval) { + + } + + public void AExprDotChainElement(EPType typeInfo, object result) { + + } + + public void QaExprIStream(ExprIStreamNode exprIStreamNode, bool newData) { + + } + + public void QExprDeclared(ExpressionDeclItem parent) { + + } + + public void AExprDeclared(object value) { + + } + + public void QExprPrev(ExprPreviousNode exprPreviousNode, bool newData) { + + } + + public void AExprPrev(object result) { + + } + + public void QExprPrior(ExprPriorNode exprPriorNode) { + + } + + public void AExprPrior(object result) { + + } + + public void QExprStreamUndMethod(ExprDotNode exprDotEvalStreamMethod) { + + } + + public void AExprStreamUndMethod(object result) { + + } + + public void QExprStreamEventMethod(ExprDotNode exprDotNode) { + + } + + public void AExprStreamEventMethod(object result) { + + } + + public void QScheduleAdd(long currentTime, long afterMSec, ScheduleHandle handle, long slot) { + + } + + public void AScheduleAdd() { + + } + + public void QScheduleRemove(ScheduleHandle handle, long slot) { + + } + + public void AScheduleRemove() { + + } + + public void QScheduleEval(long currentTime) { + + } + + public void AScheduleEval(ICollection handles) { + + } + + public void QPatternAndEvaluateTrue(EvalAndNode evalAndNode, MatchedEventMap passUp) { + + } + + public void APatternAndEvaluateTrue(bool quitted) { + + } + + public void QPatternAndQuit(EvalAndNode evalAndNode) { + + } + + public void APatternAndQuit() { + + } + + public void QPatternAndEvaluateFalse(EvalAndNode evalAndNode) { + + } + + public void APatternAndEvaluateFalse() { + + } + + public void QPatternAndStart(EvalAndNode evalAndNode, MatchedEventMap beginState) { + + } + + public void APatternAndStart() { + + } + + public void QPatternFollowedByEvaluateTrue(EvalFollowedByNode evalFollowedByNode, MatchedEventMap matchEvent, int? index) { + + } + + public void APatternFollowedByEvaluateTrue(bool quitted) { + + } + + public void QPatternFollowedByQuit(EvalFollowedByNode evalFollowedByNode) { + + } + + public void APatternFollowedByQuit() { + + } + + public void QPatternFollowedByEvalFalse(EvalFollowedByNode evalFollowedByNode) { + + } + + public void APatternFollowedByEvalFalse() { + + } + + public void QPatternFollowedByStart(EvalFollowedByNode evalFollowedByNode, MatchedEventMap beginState) { + + } + + public void APatternFollowedByStart() { + + } + + public void QPatternOrEvaluateTrue(EvalOrNode evalOrNode, MatchedEventMap matchEvent) { + + } + + public void APatternOrEvaluateTrue(bool quitted) { + + } + + public void QPatternOrEvalFalse(EvalOrNode evalOrNode) { + + } + + public void APatternOrEvalFalse() { + + } + + public void QPatternOrQuit(EvalOrNode evalOrNode) { + + } + + public void APatternOrQuit() { + + } + + public void APatternOrStart() { + + } + + public void QPatternOrStart(EvalOrNode evalOrNode, MatchedEventMap beginState) { + + } + + public void QPatternFilterMatch(EvalFilterNode filterNode, EventBean theEvent) { + + } + + public void APatternFilterMatch(bool quitted) { + + } + + public void QPatternFilterStart(EvalFilterNode evalFilterNode, MatchedEventMap beginState) { + + } + + public void APatternFilterStart() { + + } + + public void QPatternFilterQuit(EvalFilterNode evalFilterNode, MatchedEventMap beginState) { + + } + + public void APatternFilterQuit() { + + } + + public void QPatternRootEvaluateTrue(MatchedEventMap matchEvent) { + + } + + public void APatternRootEvaluateTrue(bool quitted) { + + } + + public void QPatternRootStart(MatchedEventMap root) { + + } + + public void APatternRootStart() { + + } + + public void QPatternRootQuit() { + + } + + public void APatternRootQuit() { + + } + + public void QPatternRootEvalFalse() { + + } + + public void APatternRootEvalFalse() { + + } + + public void QPatternEveryEvaluateTrue(EvalEveryNode evalEveryNode, MatchedEventMap matchEvent) { + + } + + public void APatternEveryEvaluateTrue() { + + } + + public void QPatternEveryStart(EvalEveryNode evalEveryNode, MatchedEventMap beginState) { + + } + + public void APatternEveryStart() { + + } + + public void QPatternEveryEvalFalse(EvalEveryNode evalEveryNode) { + + } + + public void APatternEveryEvalFalse() { + + } + + public void QPatternEveryQuit(EvalEveryNode evalEveryNode) { + + } + + public void APatternEveryQuit() { + + } + + public void QPatternEveryDistinctEvaluateTrue(EvalEveryDistinctNode everyDistinctNode, MatchedEventMap matchEvent) { + + } + + public void APatternEveryDistinctEvaluateTrue(ISet keysFromNodeNoExpire, IDictionary keysFromNodeExpire, object matchEventKey, bool haveSeenThis) { + + } + + public void QPatternEveryDistinctQuit(EvalEveryDistinctNode everyNode) { + + } + + public void APatternEveryDistinctQuit() { + + } + + public void QPatternEveryDistinctEvalFalse(EvalEveryDistinctNode everyNode) { + + } + + public void APatternEveryDistinctEvalFalse() { + + } + + public void QPatternEveryDistinctStart(EvalEveryDistinctNode everyNode, MatchedEventMap beginState) { + + } + + public void APatternEveryDistinctStart() { + + } + + public void QPatternGuardEvaluateTrue(EvalGuardNode evalGuardNode, MatchedEventMap matchEvent) { + + } + + public void APatternGuardEvaluateTrue(bool quitted) { + + } + + public void QPatternGuardStart(EvalGuardNode evalGuardNode, MatchedEventMap beginState) { + + } + + public void APatternGuardStart() { + + } + + public void QPatternGuardQuit(EvalGuardNode evalGuardNode) { + + } + + public void APatternGuardQuit() { + + } + + public void QPatternGuardGuardQuit(EvalGuardNode evalGuardNode) { + + } + + public void APatternGuardGuardQuit() { + + } + + public void QPatternGuardScheduledEval() { + + } + + public void APatternGuardScheduledEval() { + + } + + public void QPatternMatchUntilEvaluateTrue(EvalMatchUntilNode evalMatchUntilNode, MatchedEventMap matchEvent, bool matchFromUntil) { + + } + + public void APatternMatchUntilEvaluateTrue(bool quitted) { + + } + + public void QPatternMatchUntilStart(EvalMatchUntilNode evalMatchUntilNode, MatchedEventMap beginState) { + + } + + public void APatternMatchUntilStart() { + + } + + public void QPatternMatchUntilEvalFalse(EvalMatchUntilNode evalMatchUntilNode, bool matchFromUntil) { + + } + + public void APatternMatchUntilEvalFalse() { + + } + + public void QPatternMatchUntilQuit(EvalMatchUntilNode evalMatchUntilNode) { + + } + + public void APatternMatchUntilQuit() { + + } + + public void QPatternNotEvaluateTrue(EvalNotNode evalNotNode, MatchedEventMap matchEvent) { + + } + + public void APatternNotEvaluateTrue(bool quitted) { + + } + + public void APatternNotQuit() { + + } + + public void QPatternNotQuit(EvalNotNode evalNotNode) { + + } + + public void QPatternNotStart(EvalNotNode evalNotNode, MatchedEventMap beginState) { + + } + + public void APatternNotStart() { + + } + + public void QPatternNotEvalFalse(EvalNotNode evalNotNode) { + + } + + public void APatternNotEvalFalse() { + + } + + public void QPatternObserverEvaluateTrue(EvalObserverNode evalObserverNode, MatchedEventMap matchEvent) { + + } + + public void APatternObserverEvaluateTrue() { + + } + + public void QPatternObserverStart(EvalObserverNode evalObserverNode, MatchedEventMap beginState) { + + } + + public void APatternObserverStart() { + + } + + public void QPatternObserverQuit(EvalObserverNode evalObserverNode) { + + } + + public void APatternObserverQuit() { + + } + + public void QPatternObserverScheduledEval() { + + } + + public void APatternObserverScheduledEval() { + + } + + public void QContextPartitionAllocate(AgentInstanceContext agentInstanceContext) { + + } + + public void AContextPartitionAllocate() { + + } + + public void QContextPartitionDestroy(AgentInstanceContext agentInstanceContext) { + + } + + public void AContextPartitionDestroy() { + + } + + public void QContextScheduledEval(ContextDescriptor contextDescriptor) { + + } + + public void AContextScheduledEval() { + + } + + public void QOutputProcessNonBuffered(EventBean[] newData, EventBean[] oldData) { + + } + + public void AOutputProcessNonBuffered() { + + } + + public void QOutputProcessNonBufferedJoin(ISet> newEvents, ISet> oldEvents) { + + } + + public void AOutputProcessNonBufferedJoin() { + + } + + public void QOutputProcessWCondition(EventBean[] newData, EventBean[] oldData) { + + } + + public void AOutputProcessWCondition(bool buffered) { + + } + + public void QOutputProcessWConditionJoin(ISet> newEvents, ISet> oldEvents) { + + } + + public void AOutputProcessWConditionJoin(bool buffered) { + + } + + public void QOutputRateConditionUpdate(int newDataLength, int oldDataLength) { + + } + + public void AOutputRateConditionUpdate() { + + } + + public void QOutputRateConditionOutputNow() { + + } + + public void AOutputRateConditionOutputNow(bool generate) { + + } + + public void QOutputRateConditionScheduledEval() { + + } + + public void AOutputRateConditionScheduledEval() { + + } + + public void QResultSetProcessSimple() { + + } + + public void AResultSetProcessSimple(EventBean[] selectNewEvents, EventBean[] selectOldEvents) { + + } + + public void QResultSetProcessUngroupedFullyAgg() { + + } + + public void AResultSetProcessUngroupedFullyAgg(EventBean[] selectNewEvents, EventBean[] selectOldEvents) { + + } + + public void QResultSetProcessUngroupedNonfullyAgg() { + + } + + public void AResultSetProcessUngroupedNonfullyAgg(EventBean[] selectNewEvents, EventBean[] selectOldEvents) { + + } + + public void QResultSetProcessGroupedRowPerGroup() { + + } + + public void AResultSetProcessGroupedRowPerGroup(EventBean[] selectNewEvents, EventBean[] selectOldEvents) { + + } + + public void QResultSetProcessGroupedRowPerEvent() { + + } + + public void AResultSetProcessGroupedRowPerEvent(EventBean[] selectNewEvents, EventBean[] selectOldEvents) { + + } + + public void QResultSetProcessComputeGroupKeys(bool enter, ExprNode[] groupKeyNodeExpressions, EventBean[] eventsPerStream) { + + } + + public void AResultSetProcessComputeGroupKeys(bool enter, object groupKeysPerEvent) { + + } + + public void QAggregationUngroupedApplyEnterLeave(bool enter, int numAggregators, int numAccessStates) { + + } + + public void AAggregationUngroupedApplyEnterLeave(bool enter) { + + } + + public void QAggregationGroupedApplyEnterLeave(bool enter, int numAggregators, int numAccessStates, object groupKey) { + + } + + public void AAggregationGroupedApplyEnterLeave(bool enter) { + + } + + public void QAggNoAccessEnterLeave(bool enter, int index, AggregationMethod aggregationMethod, ExprNode aggExpr) { + + } + + public void QAggAccessEnterLeave(bool enter, int index, AggregationState state, ExprNode aggExpr) { + + } + + public void AAggNoAccessEnterLeave(bool enter, int index, AggregationMethod aggregationMethod) { + + } + + public void AAggAccessEnterLeave(bool enter, int index, AggregationState state) { + + } + + public void QSelectClause(EventBean[] eventsPerStream, bool newData, bool synthesize, ExprEvaluatorContext exprEvaluatorContext) { + + } + + public void ASelectClause(bool newData, EventBean @event, object[] subscriberParameters) { + + } + + public void QViewProcessIRStream(View view, string viewName, EventBean[] newData, EventBean[] oldData) { + + } + + public void AViewProcessIRStream() { + + } + + public void QViewScheduledEval(View view, string viewName) { + + } + + public void AViewScheduledEval() { + + } + + public void QViewIndicate(View view, string viewName, EventBean[] newData, EventBean[] oldData) { + + } + + public void AViewIndicate() { + + } + + public void QSubselectAggregation(ExprNode optionalFilterExprNode) { + + } + + public void ASubselectAggregation() { + + } + + public void QFilterActivationSubselect(string eventTypeName, ExprSubselectNode subselectNode) { + + } + + public void AFilterActivationSubselect() { + + } + + public void QFilterActivationStream(string eventTypeName, int streamNumber) { + + } + + public void AFilterActivationStream() { + + } + + public void QFilterActivationNamedWindowInsert(string namedWindowName) { + + } + + public void AFilterActivationNamedWindowInsert() { + + } + + public void QFilterActivationOnTrigger(string eventTypeName) { + + } + + public void AFilterActivationOnTrigger() { + + } + + public void QRouteBetweenStmt(EventBean theEvent, EPStatementHandle epStatementHandle, bool addToFront) { + + } + + public void ARouteBetweenStmt() { + + } + + public void QIndexAddRemove(EventTable eventTable, EventBean[] newData, EventBean[] oldData) { + + } + + public void AIndexAddRemove() { + + } + + public void QIndexAdd(EventTable eventTable, EventBean[] addEvents) { + + } + + public void AIndexAdd() { + + } + + public void QIndexRemove(EventTable eventTable, EventBean[] removeEvents) { + + } + + public void AIndexRemove() { + + } + + public void QIndexSubordLookup(SubordTableLookupStrategy subordTableLookupStrategy, EventTable optionalEventIndex, int[] keyStreamNums) { + + } + + public void AIndexSubordLookup(ICollection events, object keys) { + + } + + public void QIndexJoinLookup(JoinExecTableLookupStrategy strategy, EventTable index) { + + } + + public void AIndexJoinLookup(ICollection result, object keys) { + + } + + public void QFilter(EventBean theEvent) { + + } + + public void AFilter(ICollection matches) { + + } + + public void QFilterHandleSetIndexes(IList indizes) { + + } + + public void AFilterHandleSetIndexes() { + + } + + public void QaFilterHandleSetCallbacks(ICollection callbackSet) { + + } + + public void QFilterReverseIndex(FilterParamIndexLookupableBase filterParamIndex, object propertyValue) { + + } + + public void AFilterReverseIndex(bool? match) { + + } + + public void QFilterBoolean(FilterParamIndexBooleanExpr filterParamIndexBooleanExpr) { + + } + + public void AFilterBoolean() { + + } + + public void QFilterBooleanExpr(int num, KeyValuePair evals) { + + } + + public void AFilterBooleanExpr(bool result) { + + } + + public void QFilterAdd(FilterValueSet filterValueSet, FilterHandle filterCallback) { + + } + + public void AFilterAdd() { + + } + + public void QFilterRemove(FilterHandle filterCallback, EventTypeIndexBuilderValueIndexesPair pair) { + + } + + public void AFilterRemove() { + + } + + public void QWhereClauseFilter(ExprNode exprNode, EventBean[] newData, EventBean[] oldData) { + + } + + public void AWhereClauseFilter(EventBean[] filteredNewData, EventBean[] filteredOldData) { + + } + + public void QWhereClauseFilterEval(int num, EventBean @event, bool newData) { + + } + + public void AWhereClauseFilterEval(bool? pass) { + + } + + public void QWhereClauseIR(EventBean[] filteredNewData, EventBean[] filteredOldData) { + + } + + public void AWhereClauseIR() { + + } + + public void QHavingClauseNonJoin(EventBean theEvent) { + + } + + public void AHavingClauseNonJoin(bool? pass) { + + } + + public void QHavingClauseJoin(EventBean[] eventsPerStream) { + + } + + public void AHavingClauseJoin(bool? pass) { + + } + + public void QOrderBy(EventBean[] evalEventsPerStream, OrderByElement[] orderBy) { + + } + + public void AOrderBy(object values) { + + } + + public void QJoinDispatch(EventBean[][] newDataPerStream, EventBean[][] oldDataPerStream) { + + } + + public void AJoinDispatch() { + + } + + public void QJoinExexStrategy() { + + } + + public void AJoinExecStrategy(UniformPair>> joinSet) { + + } + + public void QJoinExecFilter() { + + } + + public void AJoinExecFilter(ISet> newEvents, ISet> oldEvents) { + + } + + public void QJoinExecProcess(UniformPair>> joinSet) { + + } + + public void AJoinExecProcess() { + + } + + public void QJoinCompositionStreamToWin() { + + } + + public void AJoinCompositionStreamToWin(ISet> newResults) { + + } + + public void QJoinCompositionWinToWin() { + + } + + public void AJoinCompositionWinToWin(ISet> newResults, ISet> oldResults) { + + } + + public void QJoinCompositionHistorical() { + + } + + public void AJoinCompositionHistorical(ISet> newResults, ISet> oldResults) { + + } + + public void QJoinCompositionStepUpdIndex(int stream, EventBean[] added, EventBean[] removed) { + + } + + public void AJoinCompositionStepUpdIndex() { + + } + + public void QJoinCompositionQueryStrategy(bool insert, int streamNum, EventBean[] events) { + + } + + public void AJoinCompositionQueryStrategy() { + + } + + public void QInfraTriggeredLookup(SubordWMatchExprLookupStrategyType lookupStrategy) { + + } + + public void AInfraTriggeredLookup(EventBean[] result) { + + } + + public void QInfraOnAction(OnTriggerType triggerType, EventBean[] triggerEvents, EventBean[] matchingEvents) { + + } + + public void AInfraOnAction() { + + } + + public void QInfraUpdate(EventBean beforeUpdate, EventBean[] eventsPerStream, int length, bool copy) { + + } + + public void AInfraUpdate(EventBean afterUpdate) { + + } + + public void QInfraUpdateRHSExpr(int index, EventBeanUpdateItem updateItem) { + + } + + public void AInfraUpdateRHSExpr(object result) { + + } + + public void QInfraMergeWhenThens(bool matched, EventBean triggerEvent, int numWhenThens) { + + } + + public void AInfraMergeWhenThens(bool matched) { + + } + + public void QInfraMergeWhenThenItem(bool matched, int count) { + + } + + public void AInfraMergeWhenThenItem(bool matched, bool actionsApplied) { + + } + + public void QInfraMergeWhenThenActions(int numActions) { + + } + + public void AInfraMergeWhenThenActions() { + + } + + public void QInfraMergeWhenThenActionItem(int count, string actionName) { + + } + + public void AInfraMergeWhenThenActionItem(bool applies) { + + } + + public void QEngineManagementStmtCompileStart(string engineURI, int statementId, string statementName, string epl, long engineTime) { + + } + + public void AEngineManagementStmtCompileStart(bool success, string message) { + + } + + public void QaEngineManagementStmtStarted(string engineURI, int statementId, string statementName, string epl, long engineTime) { + + } + + public void QEngineManagementStmtStop(EPStatementState targetState, string engineURI, int statementId, string statementName, string epl, long engineTime) { + + } + + public void AEngineManagementStmtStop() { + + } + + public void QaStatementResultExecute(UniformPair events, int statementId, string statementName, int agentInstanceId, long threadId) { + + } + + public void QSplitStream(bool all, EventBean theEvent, ExprEvaluator[] whereClauses) { + + } + + public void ASplitStream(bool all, bool handled) { + + } + + public void QSplitStreamWhere(int index) { + + } + + public void ASplitStreamWhere(bool? pass) { + + } + + public void QSplitStreamRoute(int index) { + + } + + public void ASplitStreamRoute() { + + } + + public void QUpdateIStream(InternalEventRouterEntry[] entries) { + + } + + public void AUpdateIStream(EventBean finalEvent, bool haveCloned) { + + } + + public void QUpdateIStreamApply(int index, InternalEventRouterEntry entry) { + + } + + public void AUpdateIStreamApply(EventBean updated, bool applied) { + + } + + public void QUpdateIStreamApplyWhere() { + + } + + public void AUpdateIStreamApplyWhere(bool? result) { + + } + + public void QUpdateIStreamApplyAssignments(InternalEventRouterEntry entry) { + + } + + public void AUpdateIStreamApplyAssignments(object[] values) { + + } + + public void QUpdateIStreamApplyAssignmentItem(int index) { + + } + + public void AUpdateIStreamApplyAssignmentItem(object value) { + + } + + public void QHistoricalScheduledEval() { + + } + + public void AHistoricalScheduledEval() { + + } + + public void QAggregationGroupedRollupEvalParam(bool enter, int length) { + + } + + public void AAggregationGroupedRollupEvalParam(object result) { + + } + + public void QExprTableSubproperty(ExprNode exprNode, string tableName, string subpropName) { + + } + + public void AExprTableSubproperty(object result) { + + } + + public void QExprTableTop(ExprNode exprNode, string tableName) { + + } + + public void AExprTableTop(object result) { + + } + + public void QExprTableSubpropAccessor(ExprNode exprNode, string tableName, string subpropName, ExprAggregateNode aggregationExpression) { + + } + + public void AExprTableSubpropAccessor(object result) { + + } + + public void QTableAddEvent(EventBean theEvent) { + + } + + public void ATableAddEvent() { + + } + + public void QTableDeleteEvent(EventBean theEvent) { + + } + + public void ATableDeleteEvent() { + + } + + public void QaTableUpdatedEvent(EventBean theEvent) { + + } + + public void QaTableUpdatedEventWKeyBefore(EventBean theEvent) { + + } + + public void QaTableUpdatedEventWKeyAfter(EventBean theEvent) { + + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/metrics/instrumentation/InstrumentationHelper.cs b/NEsper.Core/NEsper.Core/metrics/instrumentation/InstrumentationHelper.cs new file mode 100755 index 000000000..85aac49f2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/metrics/instrumentation/InstrumentationHelper.cs @@ -0,0 +1,91 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.service; +using com.espertech.esper.util; + +namespace com.espertech.esper.metrics.instrumentation +{ + public class InstrumentationHelper + { + public const bool ENABLED = false; + public const bool ASSERTIONENABLED = false; + + private const string PROVIDER_PROPERTY = "instrumentation_provider"; + + public static Instrumentation DefaultInstrumentation = new InstrumentationDefault(); + public static Instrumentation Instrumentation = DefaultInstrumentation; + + public static InstrumentationAssertionService AssertionService; + + public static Instrumentation Get() + { + return Instrumentation; + } + + public static void StartTest(EPServiceProvider engine, Type testClass, string testName) + { + if (!ASSERTIONENABLED) + { + return; + } + if (AssertionService == null) + { + ResolveAssertionService(engine); + } + AssertionService.StartTest(engine, testClass, testName); + } + + public static void EndTest() + { + if (!ASSERTIONENABLED) + { + return; + } + AssertionService.EndTest(); + } + + private static void ResolveAssertionService(EPServiceProvider epServiceProvider) + { + string provider = Environment.GetEnvironmentVariable(PROVIDER_PROPERTY); + if (provider == null) + { + throw new EPException("Failed to find '" + PROVIDER_PROPERTY + "' system property"); + } + if (provider.ToLowerInvariant().Trim().Equals("default")) + { + AssertionService = new DefaultInstrumentationAssertionService(); + } + else + { + var spi = (EPServiceProviderSPI) epServiceProvider; + AssertionService = TypeHelper.Instantiate( + provider, spi.EngineImportService.GetClassForNameProvider()); + } + } + + private class DefaultInstrumentationAssertionService : InstrumentationAssertionService + { + public void StartTest(EPServiceProvider engine, Type testClass, string testName) + { + + } + + public void EndTest() + { + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/pattern/EvalAndFactoryNode.cs b/NEsper.Core/NEsper.Core/pattern/EvalAndFactoryNode.cs new file mode 100755 index 000000000..42df3b935 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/EvalAndFactoryNode.cs @@ -0,0 +1,55 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +using com.espertech.esper.compat.logging; + +namespace com.espertech.esper.pattern +{ + + /// + /// This class represents an 'and' operator in the evaluation tree representing an event expressions. + /// + public class EvalAndFactoryNode : EvalNodeFactoryBase{ + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + public EvalAndFactoryNode() { + } + + public override EvalNode MakeEvalNode(PatternAgentInstanceContext agentInstanceContext, EvalNode parentNode) + { + EvalNode[] children = EvalNodeUtil.MakeEvalNodeChildren(this.ChildNodes, agentInstanceContext, parentNode); + return new EvalAndNode(agentInstanceContext, this, children); + } + + public override String ToString() { + return "EvalAndFactoryNode children=" + this.ChildNodes.Count; + } + + public override bool IsFilterChildNonQuitting + { + get { return false; } + } + + public override bool IsStateful + { + get { return true; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) { + PatternExpressionUtil.ToPrecedenceFreeEPL(writer, "and", ChildNodes, Precedence); + } + + public override PatternExpressionPrecedenceEnum Precedence + { + get { return PatternExpressionPrecedenceEnum.AND; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/pattern/EvalAndNode.cs b/NEsper.Core/NEsper.Core/pattern/EvalAndNode.cs new file mode 100755 index 000000000..ff60689b8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/EvalAndNode.cs @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +namespace com.espertech.esper.pattern +{ + /// + /// This class represents an 'and' operator in the evaluation tree representing an event expressions. + /// + public class EvalAndNode : EvalNodeBase + { + public EvalAndNode(PatternAgentInstanceContext context, EvalAndFactoryNode factoryNode, IList childNodes) + : base(context) + { + FactoryNode = factoryNode; + ChildNodes = childNodes; + } + + public EvalAndFactoryNode FactoryNode { get; private set; } + + public IList ChildNodes { get; private set; } + + public override EvalStateNode NewState(Evaluator parentNode, EvalStateNodeNumber stateNodeNumber, long stateNodeId) + { + return new EvalAndStateNode(parentNode, this); + } + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/EvalAndStateNode.cs b/NEsper.Core/NEsper.Core/pattern/EvalAndStateNode.cs new file mode 100755 index 000000000..eb9aee844 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/EvalAndStateNode.cs @@ -0,0 +1,384 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.pattern +{ + /// + /// This class represents the state of an "and" operator in the evaluation state tree. + /// + public class EvalAndStateNode : EvalStateNode, Evaluator + { + private readonly EvalAndNode _evalAndNode; + private readonly IList _activeChildNodes; + private Object[] _eventsPerChild; + + /// Constructor. + /// is the parent evaluator to call to indicate truth value + /// is the factory node associated to the state + public EvalAndStateNode(Evaluator parentNode, EvalAndNode evalAndNode) + : base(parentNode) + { + _evalAndNode = evalAndNode; + _activeChildNodes = new EvalStateNode[evalAndNode.ChildNodes.Count]; + _eventsPerChild = new Object[evalAndNode.ChildNodes.Count]; + } + + public override void RemoveMatch(ISet matchEvent) + { + bool quit = false; + if (_eventsPerChild != null) + { + foreach (Object entry in _eventsPerChild) + { + if (entry is MatchedEventMap) + { + quit = PatternConsumptionUtil.ContainsEvent(matchEvent, (MatchedEventMap)entry); + } + else if (entry != null) + { + var list = (IList)entry; + foreach (MatchedEventMap map in list) + { + quit = PatternConsumptionUtil.ContainsEvent(matchEvent, map); + if (quit) + { + break; + } + } + } + if (quit) + { + break; + } + } + } + if (!quit && _activeChildNodes != null) + { + foreach (EvalStateNode child in _activeChildNodes) + { + if (child != null) + { + child.RemoveMatch(matchEvent); + } + } + } + if (quit) + { + Quit(); + ParentEvaluator.EvaluateFalse(this, true); + } + } + + public override EvalNode FactoryNode + { + get { return _evalAndNode; } + } + + public override void Start(MatchedEventMap beginState) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QPatternAndStart(_evalAndNode, beginState); } + // In an "and" expression we need to create a state for all child listeners + int count = 0; + foreach (EvalNode node in _evalAndNode.ChildNodes) + { + EvalStateNode childState = node.NewState(this, null, 0L); + _activeChildNodes[count++] = childState; + } + + // Start all child nodes + foreach (EvalStateNode child in _activeChildNodes) + { + if (child != null) + { + child.Start(beginState); + } + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().APatternAndStart(); } + } + + public override bool IsFilterStateNode + { + get { return false; } + } + + public override bool IsNotOperator + { + get { return false; } + } + + public bool IsFilterChildNonQuitting + { + get { return false; } + } + + public override bool IsObserverStateNodeNonRestarting + { + get { return false; } + } + + public void EvaluateTrue(MatchedEventMap matchEvent, EvalStateNode fromNode, bool isQuitted) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QPatternAndEvaluateTrue(_evalAndNode, matchEvent); } + + int? indexFrom = null; + for (int i = 0; i < _activeChildNodes.Count; i++) + { + if (_activeChildNodes[i] == fromNode) + { + indexFrom = i; + } + } + + // If one of the children quits, remove the child + if (isQuitted && indexFrom != null) + { + _activeChildNodes[indexFrom.Value] = null; + } + + if (_eventsPerChild == null || indexFrom == null) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().APatternAndEvaluateTrue(true); } + return; + } + + // If all nodes have events received, the AND expression turns true + var allHaveEventsExcludingFromChild = true; + for (int i = 0; i < _eventsPerChild.Length; i++) + { + if (indexFrom != i && _eventsPerChild[i] == null) + { + allHaveEventsExcludingFromChild = false; + break; + } + } + + // if we don't have events from all child nodes, add event and done + if (!allHaveEventsExcludingFromChild) + { + AddMatchEvent(_eventsPerChild, indexFrom.Value, matchEvent); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().APatternAndEvaluateTrue(false); } + return; + } + + // if all other nodes have quit other then the from-node, don't retain matching event + var allOtherNodesQuit = true; + var hasActive = false; + for (int i = 0; i < _eventsPerChild.Length; i++) + { + if (_activeChildNodes[i] != null) + { + hasActive = true; + if (i != indexFrom) + { + allOtherNodesQuit = false; + } + } + } + + // if not all other nodes have quit, add event to received list + if (!allOtherNodesQuit) + { + AddMatchEvent(_eventsPerChild, indexFrom.Value, matchEvent); + } + + // For each combination in eventsPerChild for all other state nodes generate an event to the parent + List result = GenerateMatchEvents(matchEvent, _eventsPerChild, indexFrom.Value); + + // Check if this is quitting + bool quitted = true; + if (hasActive) + { + foreach (EvalStateNode stateNode in _activeChildNodes) + { + if (stateNode != null && !(stateNode.IsNotOperator)) + { + quitted = false; + } + } + } + + // So we are quitting if all non-not child nodes have quit, since the not-node wait for evaluate false + if (quitted) + { + QuitInternal(); + } + + // Send results to parent + foreach (MatchedEventMap theEvent in result) + { + ParentEvaluator.EvaluateTrue(theEvent, this, quitted); + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().APatternAndEvaluateTrue(_eventsPerChild == null); } + } + + public void EvaluateFalse(EvalStateNode fromNode, bool restartable) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QPatternAndEvaluateFalse(_evalAndNode); } + int? indexFrom = null; + for (int i = 0; i < _activeChildNodes.Count; i++) + { + if (_activeChildNodes[i] == fromNode) + { + _activeChildNodes[i] = null; + indexFrom = i; + } + } + + if (indexFrom != null) + { + _eventsPerChild[indexFrom.Value] = null; + } + + // The and node cannot turn true anymore, might as well quit all child nodes + QuitInternal(); + ParentEvaluator.EvaluateFalse(this, restartable ? true : false); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().APatternAndEvaluateFalse(); } + } + + /// + /// Generate a list of matching event combinations constisting of the events per child that are passed in. + /// + /// can be populated with prior events that must be passed on + /// is the list of events for each child node to the "And" node. + /// The index from. + /// + /// list of events populated with all possible combinations + /// + public static List GenerateMatchEvents(MatchedEventMap matchEvent, + Object[] eventsPerChild, + int indexFrom) + { + // Place event list for each child state node into an array, excluding the node where the event came from + var listArray = new List>(); + int index = 0; + for (int i = 0; i < eventsPerChild.Length; i++) + { + var eventsChild = eventsPerChild[i]; + if (indexFrom != i && eventsChild != null) + { + if (eventsChild is MatchedEventMap) + { + listArray.Insert(index++, Collections.SingletonList((MatchedEventMap)eventsChild)); + } + else + { + listArray.Insert(index++, (IList)eventsChild); + } + } + } + + // Recusively generate MatchedEventMap instances for all accumulated events + var results = new List(); + GenerateMatchEvents(listArray, 0, results, matchEvent); + + return results; + } + + /// For each combination of MatchedEventMap instance in all collections, add an entry to the list. Recursive method. + /// is an array of lists containing MatchedEventMap instances to combine + /// is the current index into the array + /// is the resulting list of MatchedEventMap + /// is the start MatchedEventMap to generate from + internal static void GenerateMatchEvents( + IList> eventList, + int index, + IList result, + MatchedEventMap matchEvent) + { + IList events = eventList[index]; + + foreach (MatchedEventMap theEvent in events) + { + MatchedEventMap current = matchEvent.ShallowCopy(); + current.Merge(theEvent); + + // If this is the very last list in the array of lists, add accumulated MatchedEventMap events to result + if ((index + 1) == eventList.Count) + { + result.Add(current); + } + else + { + // make a copy of the event collection and hand to next list of events + GenerateMatchEvents(eventList, index + 1, result, current); + } + } + } + + public override void Quit() + { + if (_eventsPerChild == null) + { + return; + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QPatternAndQuit(_evalAndNode); } + QuitInternal(); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().APatternAndQuit(); } + } + + public override void Accept(EvalStateNodeVisitor visitor) + { + visitor.VisitAnd(_evalAndNode.FactoryNode, this, _eventsPerChild); + foreach (EvalStateNode node in _activeChildNodes) + { + if (node != null) + { + node.Accept(visitor); + } + } + } + + public override String ToString() + { + return "EvalAndStateNode"; + } + + public static void AddMatchEvent(Object[] eventsPerChild, int indexFrom, MatchedEventMap matchEvent) + { + var matchEventHolder = eventsPerChild[indexFrom]; + if (matchEventHolder == null) + { + eventsPerChild[indexFrom] = matchEvent; + } + else if (matchEventHolder is MatchedEventMap) + { + var list = new List(4); + list.Add((MatchedEventMap)matchEventHolder); + list.Add(matchEvent); + eventsPerChild[indexFrom] = list; + } + else + { + var list = (IList)matchEventHolder; + list.Add(matchEvent); + } + } + + private void QuitInternal() + { + foreach (EvalStateNode child in _activeChildNodes) + { + if (child != null) + { + child.Quit(); + } + } + + _activeChildNodes.Fill((EvalStateNode)null); + _eventsPerChild = null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/EvalAuditFactoryNode.cs b/NEsper.Core/NEsper.Core/pattern/EvalAuditFactoryNode.cs new file mode 100755 index 000000000..ec0128e5d --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/EvalAuditFactoryNode.cs @@ -0,0 +1,100 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.pattern +{ + /// + /// This class represents an 'or' operator in the evaluation tree representing any event expressions. + /// + [Serializable] + public class EvalAuditFactoryNode : EvalNodeFactoryBase + { + private readonly bool _auditPattern; + private readonly bool _auditPatternInstance; + private readonly string _patternExpr; + [NonSerialized] private readonly EvalAuditInstanceCount _instanceCount; + private readonly bool _filterChildNonQuitting; + + public EvalAuditFactoryNode( + bool auditPattern, + bool auditPatternInstance, + string patternExpr, + EvalAuditInstanceCount instanceCount, + bool filterChildNonQuitting) + { + _auditPattern = auditPattern; + _auditPatternInstance = auditPatternInstance; + _patternExpr = patternExpr; + _instanceCount = instanceCount; + _filterChildNonQuitting = filterChildNonQuitting; + } + + public override EvalNode MakeEvalNode(PatternAgentInstanceContext agentInstanceContext, EvalNode parentNode) + { + EvalNode child = EvalNodeUtil.MakeEvalNodeSingleChild(ChildNodes, agentInstanceContext, parentNode); + return new EvalAuditNode(agentInstanceContext, this, child); + } + + public bool IsAuditPattern + { + get { return _auditPattern; } + } + + public string PatternExpr + { + get { return _patternExpr; } + } + + public override String ToString() { + return "EvalAuditFactoryNode children=" + ChildNodes.Count; + } + + public void DecreaseRefCount(EvalAuditStateNode current, PatternContext patternContext) + { + if (!_auditPatternInstance) + { + return; + } + _instanceCount.DecreaseRefCount( + ChildNodes[0], current, _patternExpr, patternContext.StatementName, patternContext.EngineURI); + } + + public void IncreaseRefCount(EvalAuditStateNode current, PatternContext patternContext) + { + if (!_auditPatternInstance) + { + return; + } + _instanceCount.IncreaseRefCount( + ChildNodes[0], current, _patternExpr, patternContext.StatementName, patternContext.EngineURI); + } + + public override bool IsFilterChildNonQuitting + { + get { return _filterChildNonQuitting; } + } + + public override bool IsStateful + { + get { return ChildNodes[0].IsStateful; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + ChildNodes[0].ToEPL(writer, Precedence); + } + + public override PatternExpressionPrecedenceEnum Precedence + { + get { return ChildNodes[0].Precedence; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/pattern/EvalAuditInstanceCount.cs b/NEsper.Core/NEsper.Core/pattern/EvalAuditInstanceCount.cs new file mode 100755 index 000000000..eca7b601b --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/EvalAuditInstanceCount.cs @@ -0,0 +1,86 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; +using com.espertech.esper.client.annotation; +using com.espertech.esper.compat.collections; +using com.espertech.esper.util; + +namespace com.espertech.esper.pattern +{ + public class EvalAuditInstanceCount + { + + private readonly IDictionary _counts; + + public EvalAuditInstanceCount() + { + _counts = new Dictionary(); + } + + public void DecreaseRefCount(EvalFactoryNode evalNode, EvalAuditStateNode current, String patternExpr, String statementName, String engineURI) + { + int? count = _counts.Get(evalNode); + if (count == null) + { + return; + } + count--; + if (count <= 0) + { + _counts.Remove(evalNode); + Print(current, patternExpr, engineURI, statementName, false, 0); + return; + } + _counts.Put(evalNode, count); + Print(current, patternExpr, engineURI, statementName, false, count); + + + } + + public void IncreaseRefCount(EvalFactoryNode evalNode, EvalAuditStateNode current, String patternExpr, String statementName, String engineURI) + { + int? count = _counts.Get(evalNode); + if (count == null) + { + count = 1; + } + else + { + count++; + } + _counts.Put(evalNode, count); + Print(current, patternExpr, engineURI, statementName, true, count); + } + + private static void Print(EvalAuditStateNode current, String patternExpression, String engineURI, String statementName, bool added, int? count) + { + if (!AuditPath.IsAuditEnabled) + { + return; + } + + var writer = new StringWriter(); + + EvalAuditStateNode.WritePatternExpr(current, patternExpression, writer); + + if (added) + { + writer.Write(" increased to " + count); + } + else + { + writer.Write(" decreased to " + count); + } + + AuditPath.AuditLog(engineURI, statementName, AuditEnum.PATTERNINSTANCES, writer.ToString()); + } + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/EvalAuditNode.cs b/NEsper.Core/NEsper.Core/pattern/EvalAuditNode.cs new file mode 100755 index 000000000..a29c831bd --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/EvalAuditNode.cs @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.pattern +{ + /// + /// This class represents an 'or' operator in the evaluation tree representing any event expressions. + /// + public class EvalAuditNode : EvalNodeBase + { + private readonly EvalAuditFactoryNode _factoryNode; + private readonly EvalNode _childNode; + + public EvalAuditNode(PatternAgentInstanceContext context, EvalAuditFactoryNode factoryNode, EvalNode childNode) + : base(context) + { + _factoryNode = factoryNode; + _childNode = childNode; + } + + public EvalAuditFactoryNode FactoryNode + { + get { return _factoryNode; } + } + + public EvalNode ChildNode + { + get { return _childNode; } + } + + public override EvalStateNode NewState(Evaluator parentNode, EvalStateNodeNumber stateNodeNumber, long stateNodeId) + { + return new EvalAuditStateNode(parentNode, this, stateNodeNumber, stateNodeId); + } + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/EvalAuditStateNode.cs b/NEsper.Core/NEsper.Core/pattern/EvalAuditStateNode.cs new file mode 100755 index 000000000..87db68798 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/EvalAuditStateNode.cs @@ -0,0 +1,219 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.client; +using com.espertech.esper.client.annotation; +using com.espertech.esper.compat.logging; +using com.espertech.esper.events; +using com.espertech.esper.util; + +namespace com.espertech.esper.pattern +{ + /// + /// This class represents the state of a followed-by operator in the evaluation state tree. + /// + public sealed class EvalAuditStateNode : EvalStateNode, Evaluator + { + private readonly EvalAuditNode _evalAuditNode; + private readonly EvalStateNode _childState; + + /// + /// Constructor. + /// + /// is the parent evaluator to call to indicate truth value + /// is the factory node associated to the state + /// The state node number. + /// The state node id. + public EvalAuditStateNode(Evaluator parentNode, + EvalAuditNode evalAuditNode, + EvalStateNodeNumber stateNodeNumber, + long stateNodeId) + : base(parentNode) + { + _evalAuditNode = evalAuditNode; + _childState = evalAuditNode.ChildNode.NewState(this, stateNodeNumber, stateNodeId); + } + + public override void RemoveMatch(ISet matchEvent) + { + if (_childState != null) + { + _childState.RemoveMatch(matchEvent); + } + } + + public override EvalNode FactoryNode + { + get { return _evalAuditNode; } + } + + public override void Start(MatchedEventMap beginState) + { + _childState.Start(beginState); + _evalAuditNode.FactoryNode.IncreaseRefCount(this, _evalAuditNode.Context.PatternContext); + } + + public void EvaluateTrue(MatchedEventMap matchEvent, EvalStateNode fromNode, bool isQuitted) + { + if (_evalAuditNode.FactoryNode.IsAuditPattern && AuditPath.IsInfoEnabled) + { + String message = ToStringEvaluateTrue(this, _evalAuditNode.FactoryNode.PatternExpr, matchEvent, fromNode, isQuitted); + AuditPath.AuditLog(_evalAuditNode.Context.StatementContext.EngineURI, _evalAuditNode.Context.PatternContext.StatementName, AuditEnum.PATTERN, message); + } + + ParentEvaluator.EvaluateTrue(matchEvent, this, isQuitted); + + if (isQuitted) + { + _evalAuditNode.FactoryNode.DecreaseRefCount(this, _evalAuditNode.Context.PatternContext); + } + } + + public void EvaluateFalse(EvalStateNode fromNode, bool restartable) + { + if (_evalAuditNode.FactoryNode.IsAuditPattern && AuditPath.IsInfoEnabled) + { + String message = ToStringEvaluateFalse(this, _evalAuditNode.FactoryNode.PatternExpr, fromNode); + AuditPath.AuditLog(_evalAuditNode.Context.StatementContext.EngineURI, _evalAuditNode.Context.PatternContext.StatementName, AuditEnum.PATTERN, message); + } + + _evalAuditNode.FactoryNode.DecreaseRefCount(this, _evalAuditNode.Context.PatternContext); + ParentEvaluator.EvaluateFalse(this, restartable); + } + + public override void Quit() + { + if (_childState != null) + { + _childState.Quit(); + } + _evalAuditNode.FactoryNode.DecreaseRefCount(this, _evalAuditNode.Context.PatternContext); + } + + public override void Accept(EvalStateNodeVisitor visitor) + { + visitor.VisitAudit(); + if (_childState != null) + { + _childState.Accept(visitor); + } + } + + public EvalStateNode ChildState + { + get { return _childState; } + } + + public override String ToString() + { + return "EvalAuditStateNode"; + } + + public override bool IsNotOperator + { + get + { + EvalNode evalNode = _evalAuditNode.ChildNode; + return evalNode is EvalNotNode; + } + } + + public bool IsFilterChildNonQuitting + { + get { return _evalAuditNode.FactoryNode.IsFilterChildNonQuitting; } + } + + public override bool IsFilterStateNode + { + get { return _evalAuditNode.ChildNode is EvalFilterNode; } + } + + public override bool IsObserverStateNodeNonRestarting + { + get + { + if (_childState != null) + { + return _childState.IsObserverStateNodeNonRestarting; + } + return false; + } + } + + private static String ToStringEvaluateTrue(EvalAuditStateNode current, String patternExpression, MatchedEventMap matchEvent, EvalStateNode fromNode, bool isQuitted) + { + var writer = new StringWriter(); + + WritePatternExpr(current, patternExpression, writer); + writer.Write(" evaluate-true {"); + + writer.Write(" from: "); + TypeHelper.WriteInstance(writer, fromNode, false); + + writer.Write(" map: {"); + var delimiter = ""; + var data = matchEvent.MatchingEvents; + for (int i = 0; i < data.Length; i++) + { + var name = matchEvent.Meta.TagsPerIndex[i]; + var value = matchEvent.GetMatchingEventAsObject(i); + writer.Write(delimiter); + writer.Write(name); + writer.Write("="); + if (value is EventBean) + { + writer.Write(((EventBean)value).Underlying.ToString()); + } + else if (value is EventBean[]) + { + writer.Write(EventBeanUtility.Summarize((EventBean[])value)); + } + delimiter = ", "; + } + + writer.Write("} quitted: "); + writer.Write(isQuitted); + + writer.Write("}"); + return writer.ToString(); + } + + private String ToStringEvaluateFalse(EvalAuditStateNode current, String patternExpression, EvalStateNode fromNode) + { + var writer = new StringWriter(); + WritePatternExpr(current, patternExpression, writer); + writer.Write(" evaluate-false {"); + + writer.Write(" from "); + TypeHelper.WriteInstance(writer, fromNode, false); + + writer.Write("}"); + return writer.ToString(); + } + + internal static void WritePatternExpr(EvalAuditStateNode current, String patternExpression, TextWriter writer) + { + if (patternExpression != null) + { + writer.Write('('); + writer.Write(patternExpression); + writer.Write(')'); + } + else + { + TypeHelper.WriteInstance(writer, "subexr", current); + } + } + + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/EvalEveryDistinctFactoryNode.cs b/NEsper.Core/NEsper.Core/pattern/EvalEveryDistinctFactoryNode.cs new file mode 100755 index 000000000..e621488f2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/EvalEveryDistinctFactoryNode.cs @@ -0,0 +1,139 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; + +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.time; + +namespace com.espertech.esper.pattern +{ + /// + /// This class represents an 'every-distinct' operator in the evaluation tree representing an event expression. + /// + public class EvalEveryDistinctFactoryNode : EvalNodeFactoryBase + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly IList _expressions; + [NonSerialized] private MatchedEventConvertor _convertor; + private IList _distinctExpressions; + [NonSerialized] private ExprEvaluator[] _distinctExpressionsArray; + private ExprNode _expiryTimeExp; + private ExprTimePeriodEvalDeltaConst _timeDeltaComputation; + + /// + /// Ctor. + /// + /// distinct-value expressions + public EvalEveryDistinctFactoryNode(IList expressions) + { + _expressions = expressions; + } + + public ExprEvaluator[] DistinctExpressionsArray + { + get { return _distinctExpressionsArray; } + } + + /// + /// Gets or sets the convertor for matching events to events-per-stream. + /// + public MatchedEventConvertor Convertor + { + get { return _convertor; } + set { _convertor = value; } + } + + /// + /// Returns all expressions. + /// + /// expressions + public IList Expressions + { + get { return _expressions; } + } + + /// + /// Returns distinct expressions. + /// + /// expressions + public IList DistinctExpressions + { + get { return _distinctExpressions; } + } + + public override bool IsFilterChildNonQuitting + { + get { return true; } + } + + public override bool IsStateful + { + get { return true; } + } + + public ExprTimePeriodEvalDeltaConst TimeDeltaComputation + { + get { return _timeDeltaComputation; } + } + + public override PatternExpressionPrecedenceEnum Precedence + { + get { return PatternExpressionPrecedenceEnum.UNARY; } + } + + public override EvalNode MakeEvalNode(PatternAgentInstanceContext agentInstanceContext, EvalNode parentNode) + { + if (_distinctExpressionsArray == null) + { + _distinctExpressionsArray = ExprNodeUtility.GetEvaluators(_distinctExpressions); + } + EvalNode child = EvalNodeUtil.MakeEvalNodeSingleChild(ChildNodes, agentInstanceContext, parentNode); + return new EvalEveryDistinctNode(this, child, agentInstanceContext); + } + + public override String ToString() + { + return "EvalEveryNode children=" + ChildNodes.Count; + } + + public void SetDistinctExpressions( + IList distinctExpressions, + ExprTimePeriodEvalDeltaConst timeDeltaComputation, + ExprNode expiryTimeExp) + { + _distinctExpressions = distinctExpressions; + _timeDeltaComputation = timeDeltaComputation; + _expiryTimeExp = expiryTimeExp; + } + + public long AbsExpiry(PatternAgentInstanceContext context) + { + long current = context.StatementContext.SchedulingService.Time; + return current + _timeDeltaComputation.DeltaAdd(current); + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + writer.Write("every-distinct("); + ExprNodeUtility.ToExpressionStringParameterList(_distinctExpressions, writer); + if (_expiryTimeExp != null) + { + writer.Write(","); + writer.Write(ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(_expiryTimeExp)); + } + writer.Write(") "); + ChildNodes[0].ToEPL(writer, Precedence); + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/pattern/EvalEveryDistinctNode.cs b/NEsper.Core/NEsper.Core/pattern/EvalEveryDistinctNode.cs new file mode 100755 index 000000000..a3d305509 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/EvalEveryDistinctNode.cs @@ -0,0 +1,49 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.pattern +{ + /// + /// This class represents an 'every-distinct' operator in the evaluation tree representing an event expression. + /// + [Serializable] + public class EvalEveryDistinctNode : EvalNodeBase + { + private readonly EvalEveryDistinctFactoryNode _factoryNode; + private readonly EvalNode _childNode; + + public EvalEveryDistinctNode(EvalEveryDistinctFactoryNode factoryNode, EvalNode childNode, PatternAgentInstanceContext agentInstanceContext) + : base(agentInstanceContext) + { + _factoryNode = factoryNode; + _childNode = childNode; + } + + public EvalEveryDistinctFactoryNode FactoryNode + { + get { return _factoryNode; } + } + + public EvalNode ChildNode + { + get { return _childNode; } + } + + public override EvalStateNode NewState(Evaluator parentNode, EvalStateNodeNumber stateNodeNumber, long stateNodeId) + { + if (_factoryNode.TimeDeltaComputation == null) { + return new EvalEveryDistinctStateNode(parentNode, this); + } + else { + return new EvalEveryDistinctStateExpireKeyNode(parentNode, this); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/EvalEveryDistinctStateExpireKeyNode.cs b/NEsper.Core/NEsper.Core/pattern/EvalEveryDistinctStateExpireKeyNode.cs new file mode 100755 index 000000000..eeadd3f8e --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/EvalEveryDistinctStateExpireKeyNode.cs @@ -0,0 +1,240 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.pattern +{ + /// + /// Contains the state collected by an "every" operator. The state includes handles + /// to any sub-listeners started by the operator. + /// + public class EvalEveryDistinctStateExpireKeyNode : EvalStateNode, Evaluator + { + protected readonly EvalEveryDistinctNode EveryNode; + protected readonly IDictionary> SpawnedNodes; + protected MatchedEventMap BeginState; + + /// Constructor. + /// is the parent evaluator to call to indicate truth value + /// is the factory node associated to the state + public EvalEveryDistinctStateExpireKeyNode(Evaluator parentNode, EvalEveryDistinctNode everyNode) + : base(parentNode) + { + EveryNode = everyNode; + SpawnedNodes = new LinkedHashMap>(); + } + + public override void RemoveMatch(ISet matchEvent) + { + if (PatternConsumptionUtil.ContainsEvent(matchEvent, BeginState)) + { + Quit(); + ParentEvaluator.EvaluateFalse(this, true); + } + else + { + PatternConsumptionUtil.ChildNodeRemoveMatches(matchEvent, SpawnedNodes.Keys); + } + } + + public override EvalNode FactoryNode + { + get { return EveryNode; } + } + + public override void Start(MatchedEventMap beginState) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QPatternEveryDistinctStart(EveryNode, beginState); } + + BeginState = beginState.ShallowCopy(); + var childState = EveryNode.ChildNode.NewState(this, null, 0L); + SpawnedNodes.Put(childState, new LinkedHashMap()); + + // During the start of the child we need to use the temporary evaluator to catch any event created during a start. + // Events created during the start would likely come from the "not" operator. + // Quit the new child again if + var spawnEvaluator = new EvalEveryStateSpawnEvaluator(EveryNode.Context.PatternContext.StatementName); + childState.ParentEvaluator = spawnEvaluator; + childState.Start(beginState); + + // If the spawned expression turned true already, just quit it + if (spawnEvaluator.IsEvaluatedTrue) + { + childState.Quit(); + } + else + { + childState.ParentEvaluator = this; + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().APatternEveryDistinctStart(); } + } + + public void EvaluateFalse(EvalStateNode fromNode, bool restartable) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QPatternEveryDistinctEvalFalse(EveryNode); } + + fromNode.Quit(); + SpawnedNodes.Remove(fromNode); + + // Spawn all nodes below this EVERY node + // During the start of a child we need to use the temporary evaluator to catch any event created during a start + // Such events can be raised when the "not" operator is used. + var spawnEvaluator = new EvalEveryStateSpawnEvaluator(EveryNode.Context.PatternContext.StatementName); + var spawned = EveryNode.ChildNode.NewState(spawnEvaluator, null, 0L); + spawned.Start(BeginState); + + // If the whole spawned expression already turned true, quit it again + if (spawnEvaluator.IsEvaluatedTrue) + { + spawned.Quit(); + } + else + { + SpawnedNodes.Put(spawned, new LinkedHashMap()); + spawned.ParentEvaluator = this; + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().APatternEveryDistinctEvalFalse(); } + } + + public void EvaluateTrue(MatchedEventMap matchEvent, EvalStateNode fromNode, bool isQuitted) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QPatternEveryDistinctEvaluateTrue(EveryNode, matchEvent); } + + // determine if this evaluation has been seen before from the same node + var matchEventKey = PatternExpressionUtil.GetKeys(matchEvent, EveryNode.FactoryNode.Convertor, EveryNode.FactoryNode.DistinctExpressionsArray, EveryNode.Context.AgentInstanceContext); + var haveSeenThis = false; + var keysFromNode = SpawnedNodes.Get(fromNode); + if (keysFromNode != null) + { + // Clean out old keys + var currentTime = EveryNode.Context.PatternContext.TimeProvider.Time; + var entries = new List(); + + foreach (var entry in keysFromNode) + { + if (currentTime >= entry.Value) + { + entries.Add(entry.Key); + } + else + { + break; + } + } + + entries.ForEach(k => keysFromNode.Remove(k)); + + if (keysFromNode.ContainsKey(matchEventKey)) + { + haveSeenThis = true; + } + else + { + long expiryTime = EveryNode.FactoryNode.AbsExpiry(EveryNode.Context); + keysFromNode.Put(matchEventKey, expiryTime); + } + } + + if (isQuitted) + { + SpawnedNodes.Remove(fromNode); + } + + // See explanation in EvalFilterStateNode for the type check + if (fromNode.IsFilterStateNode) + { + // We do not need to newState new listeners here, since the filter state node below this node did not quit + } + else + { + // Spawn all nodes below this EVERY node + // During the start of a child we need to use the temporary evaluator to catch any event created during a start + // Such events can be raised when the "not" operator is used. + var spawnEvaluator = new EvalEveryStateSpawnEvaluator(EveryNode.Context.PatternContext.StatementName); + var spawned = EveryNode.ChildNode.NewState(spawnEvaluator, null, 0L); + spawned.Start(BeginState); + + // If the whole spawned expression already turned true, quit it again + if (spawnEvaluator.IsEvaluatedTrue) + { + spawned.Quit(); + } + else + { + var keyset = new LinkedHashMap(); + if (keysFromNode != null) + { + keyset.PutAll(keysFromNode); + } + SpawnedNodes.Put(spawned, keyset); + spawned.ParentEvaluator = this; + } + } + + if (!haveSeenThis) + { + ParentEvaluator.EvaluateTrue(matchEvent, this, false); + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().APatternEveryDistinctEvaluateTrue(null, keysFromNode, matchEventKey, haveSeenThis); } + } + + public override void Quit() + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QPatternEveryDistinctQuit(EveryNode); } + // Stop all child nodes + foreach (EvalStateNode child in SpawnedNodes.Keys) + { + child.Quit(); + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().APatternEveryDistinctQuit(); } + } + + public override void Accept(EvalStateNodeVisitor visitor) + { + visitor.VisitEveryDistinct(EveryNode.FactoryNode, this, BeginState, SpawnedNodes.Values); + foreach (EvalStateNode spawnedNode in SpawnedNodes.Keys) + { + spawnedNode.Accept(visitor); + } + } + + public override bool IsNotOperator + { + get { return false; } + } + + public override bool IsFilterStateNode + { + get { return false; } + } + + public bool IsFilterChildNonQuitting + { + get { return true; } + } + + public override bool IsObserverStateNodeNonRestarting + { + get { return false; } + } + + public override String ToString() + { + return "EvalEveryStateNode spawnedChildren=" + SpawnedNodes.Count; + } + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/EvalEveryDistinctStateNode.cs b/NEsper.Core/NEsper.Core/pattern/EvalEveryDistinctStateNode.cs new file mode 100755 index 000000000..c2d1d6c03 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/EvalEveryDistinctStateNode.cs @@ -0,0 +1,223 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.pattern +{ + /// + /// Contains the state collected by an "every" operator. The state includes handles + /// to any sub-listeners started by the operator. + /// + public class EvalEveryDistinctStateNode : EvalStateNode, Evaluator + { + protected readonly EvalEveryDistinctNode EveryDistinctNode; + protected readonly IDictionary> SpawnedNodes; + protected MatchedEventMap BeginState; + + /// Constructor. + /// is the parent evaluator to call to indicate truth value + /// is the factory node associated to the state + public EvalEveryDistinctStateNode(Evaluator parentNode, EvalEveryDistinctNode everyDistinctNode) + : base(parentNode) + { + EveryDistinctNode = everyDistinctNode; + SpawnedNodes = new LinkedHashMap>(); + } + + public override void RemoveMatch(ISet matchEvent) + { + if (PatternConsumptionUtil.ContainsEvent(matchEvent, BeginState)) + { + Quit(); + ParentEvaluator.EvaluateFalse(this, true); + } + else + { + PatternConsumptionUtil.ChildNodeRemoveMatches(matchEvent, SpawnedNodes.Keys); + } + } + + public override EvalNode FactoryNode + { + get { return EveryDistinctNode; } + } + + public override void Start(MatchedEventMap beginState) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QPatternEveryDistinctStart(EveryDistinctNode, beginState); } + + BeginState = beginState.ShallowCopy(); + var childState = EveryDistinctNode.ChildNode.NewState(this, null, 0L); + SpawnedNodes.Put(childState, new HashSet()); + + if (SpawnedNodes.Count != 1) + { + throw new IllegalStateException("EVERY state node is expected to have single child state node"); + } + + // During the start of the child we need to use the temporary evaluator to catch any event created during a start. + // Events created during the start would likely come from the "not" operator. + // Quit the new child again if + var spawnEvaluator = new EvalEveryStateSpawnEvaluator(EveryDistinctNode.Context.PatternContext.StatementName); + childState.ParentEvaluator = spawnEvaluator; + childState.Start(beginState); + + // If the spawned expression turned true already, just quit it + if (spawnEvaluator.IsEvaluatedTrue) + { + childState.Quit(); + } + else + { + childState.ParentEvaluator = this; + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().APatternEveryDistinctStart(); } + } + + public void EvaluateFalse(EvalStateNode fromNode, bool restartable) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QPatternEveryDistinctEvalFalse(EveryDistinctNode); } + fromNode.Quit(); + SpawnedNodes.Remove(fromNode); + + // Spawn all nodes below this EVERY node + // During the start of a child we need to use the temporary evaluator to catch any event created during a start + // Such events can be raised when the "not" operator is used. + var spawnEvaluator = new EvalEveryStateSpawnEvaluator(EveryDistinctNode.Context.PatternContext.StatementName); + var spawned = EveryDistinctNode.ChildNode.NewState(spawnEvaluator, null, 0L); + spawned.Start(BeginState); + + // If the whole spawned expression already turned true, quit it again + if (spawnEvaluator.IsEvaluatedTrue) + { + spawned.Quit(); + } + else + { + SpawnedNodes.Put(spawned, new HashSet()); + spawned.ParentEvaluator = this; + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().APatternEveryDistinctEvalFalse(); } + } + + public void EvaluateTrue(MatchedEventMap matchEvent, EvalStateNode fromNode, bool isQuitted) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QPatternEveryDistinctEvaluateTrue(EveryDistinctNode, matchEvent); } + + // determine if this evaluation has been seen before from the same node + var matchEventKey = PatternExpressionUtil.GetKeys(matchEvent, EveryDistinctNode.FactoryNode.Convertor, EveryDistinctNode.FactoryNode.DistinctExpressionsArray, EveryDistinctNode.Context.AgentInstanceContext); + var haveSeenThis = false; + var keysFromNode = SpawnedNodes.Get(fromNode); + if (keysFromNode != null) + { + if (keysFromNode.Contains(matchEventKey)) + { + haveSeenThis = true; + } + else + { + keysFromNode.Add(matchEventKey); + } + } + + if (isQuitted) + { + SpawnedNodes.Remove(fromNode); + } + + // See explanation in EvalFilterStateNode for the type check + if (fromNode.IsFilterStateNode) + { + // We do not need to newState new listeners here, since the filter state node below this node did not quit + } + else + { + // Spawn all nodes below this EVERY node + // During the start of a child we need to use the temporary evaluator to catch any event created during a start + // Such events can be raised when the "not" operator is used. + var spawnEvaluator = new EvalEveryStateSpawnEvaluator(EveryDistinctNode.Context.PatternContext.StatementName); + var spawned = EveryDistinctNode.ChildNode.NewState(spawnEvaluator, null, 0L); + spawned.Start(BeginState); + + // If the whole spawned expression already turned true, quit it again + if (spawnEvaluator.IsEvaluatedTrue) + { + spawned.Quit(); + } + else + { + var keyset = new HashSet(); + if (keysFromNode != null) + { + keyset.AddAll(keysFromNode); + } + SpawnedNodes.Put(spawned, keyset); + spawned.ParentEvaluator = this; + } + } + + if (!haveSeenThis) + { + ParentEvaluator.EvaluateTrue(matchEvent, this, false); + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().APatternEveryDistinctEvaluateTrue(keysFromNode, null, matchEventKey, haveSeenThis); } + } + + public override void Quit() + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QPatternEveryDistinctQuit(EveryDistinctNode); } + // Stop all child nodes + foreach (var child in SpawnedNodes.Keys) + { + child.Quit(); + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().APatternEveryDistinctQuit(); } + } + + public override void Accept(EvalStateNodeVisitor visitor) + { + visitor.VisitEveryDistinct(EveryDistinctNode.FactoryNode, this, BeginState, SpawnedNodes.Values); + foreach (var spawnedNode in SpawnedNodes.Keys) + { + spawnedNode.Accept(visitor); + } + } + + public override bool IsFilterStateNode + { + get { return false; } + } + + public override bool IsNotOperator + { + get { return false; } + } + + public bool IsFilterChildNonQuitting + { + get { return true; } + } + + public override bool IsObserverStateNodeNonRestarting + { + get { return false; } + } + + public override String ToString() + { + return "EvalEveryStateNode spawnedChildren=" + SpawnedNodes.Count; + } + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/EvalEveryFactoryNode.cs b/NEsper.Core/NEsper.Core/pattern/EvalEveryFactoryNode.cs new file mode 100755 index 000000000..3174fd00c --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/EvalEveryFactoryNode.cs @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +using com.espertech.esper.compat.logging; + +namespace com.espertech.esper.pattern +{ + /// + /// This class represents an 'every' operator in the evaluation tree representing an event expression. + /// + public class EvalEveryFactoryNode : EvalNodeFactoryBase + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + public override EvalNode MakeEvalNode(PatternAgentInstanceContext agentInstanceContext, EvalNode parentNode) { + EvalNode child = EvalNodeUtil.MakeEvalNodeSingleChild(ChildNodes, agentInstanceContext, parentNode); + return new EvalEveryNode(agentInstanceContext, this, child); + } + + public override String ToString() + { + return "EvalEveryNode children=" + ChildNodes.Count; + } + + public override bool IsFilterChildNonQuitting + { + get { return true; } + } + + public override bool IsStateful + { + get { return true; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) { + writer.Write("every "); + ChildNodes[0].ToEPL(writer, Precedence); + } + + public override PatternExpressionPrecedenceEnum Precedence + { + get { return PatternExpressionPrecedenceEnum.UNARY; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/pattern/EvalEveryNode.cs b/NEsper.Core/NEsper.Core/pattern/EvalEveryNode.cs new file mode 100755 index 000000000..ecbf02156 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/EvalEveryNode.cs @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Reflection; + +using com.espertech.esper.compat.logging; + +namespace com.espertech.esper.pattern +{ + /// + /// This class represents an 'every' operator in the evaluation tree representing an event expression. + /// + public class EvalEveryNode : EvalNodeBase + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly EvalEveryFactoryNode _factoryNode; + private readonly EvalNode _childNode; + + public EvalEveryNode(PatternAgentInstanceContext context, EvalEveryFactoryNode factoryNode, EvalNode childNode) + : base(context) + { + _factoryNode = factoryNode; + _childNode = childNode; + } + + public EvalEveryFactoryNode FactoryNode + { + get { return _factoryNode; } + } + + public EvalNode ChildNode + { + get { return _childNode; } + } + + public override EvalStateNode NewState( + Evaluator parentNode, + EvalStateNodeNumber stateNodeNumber, + long stateNodeId) + { + return new EvalEveryStateNode(parentNode, this); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/pattern/EvalEveryStateNode.cs b/NEsper.Core/NEsper.Core/pattern/EvalEveryStateNode.cs new file mode 100755 index 000000000..2fcd15a67 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/EvalEveryStateNode.cs @@ -0,0 +1,198 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.pattern +{ + /// + /// Contains the state collected by an "every" operator. The state includes + /// handles to any sub-listeners started by the operator. + /// + public class EvalEveryStateNode : EvalStateNode, Evaluator + { + private readonly EvalEveryNode _evalEveryNode; + private readonly IList _spawnedNodes; + private MatchedEventMap _beginState; + + /// Constructor. + /// is the parent evaluator to call to indicate truth value + /// is the factory node associated to the state + public EvalEveryStateNode(Evaluator parentNode, EvalEveryNode evalEveryNode) + : base(parentNode) + { + _evalEveryNode = evalEveryNode; + _spawnedNodes = new List(); + } + + public override void RemoveMatch(ISet matchEvent) + { + if (PatternConsumptionUtil.ContainsEvent(matchEvent, _beginState)) + { + Quit(); + ParentEvaluator.EvaluateFalse(this, true); + } + else + { + PatternConsumptionUtil.ChildNodeRemoveMatches(matchEvent, _spawnedNodes); + } + } + + public override EvalNode FactoryNode + { + get { return _evalEveryNode; } + } + + public override void Start(MatchedEventMap beginState) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QPatternEveryStart(_evalEveryNode, beginState); } + _beginState = beginState.ShallowCopy(); + var childState = _evalEveryNode.ChildNode.NewState(this, null, 0L); + _spawnedNodes.Add(childState); + + // During the start of the child we need to use the temporary evaluator to catch any event created during a start. + // Events created during the start would likely come from the "not" operator. + // Quit the new child again if + var spawnEvaluator = new EvalEveryStateSpawnEvaluator(_evalEveryNode.Context.PatternContext.StatementName); + childState.ParentEvaluator = spawnEvaluator; + childState.Start(beginState); + + // If the spawned expression turned true already, just quit it + if (spawnEvaluator.IsEvaluatedTrue) + { + childState.Quit(); + } + else + { + childState.ParentEvaluator = this; + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().APatternEveryStart(); } + } + + public void EvaluateFalse(EvalStateNode fromNode, bool restartable) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QPatternEveryEvalFalse(_evalEveryNode); } + fromNode.Quit(); + _spawnedNodes.Remove(fromNode); + + if (!restartable) + { + ParentEvaluator.EvaluateFalse(this, false); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().APatternEveryEvalFalse(); } + return; + } + + // Spawn all nodes below this EVERY node + // During the start of a child we need to use the temporary evaluator to catch any event created during a start + // Such events can be raised when the "not" operator is used. + var spawnEvaluator = new EvalEveryStateSpawnEvaluator(_evalEveryNode.Context.PatternContext.StatementName); + var spawned = _evalEveryNode.ChildNode.NewState(spawnEvaluator, null, 0L); + spawned.Start(_beginState); + + // If the whole spawned expression already turned true, quit it again + if (spawnEvaluator.IsEvaluatedTrue) + { + spawned.Quit(); + } + else + { + _spawnedNodes.Add(spawned); + spawned.ParentEvaluator = this; + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().APatternEveryEvalFalse(); } + } + + public void EvaluateTrue(MatchedEventMap matchEvent, EvalStateNode fromNode, bool isQuitted) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QPatternEveryEvaluateTrue(_evalEveryNode, matchEvent); } + if (isQuitted) + { + _spawnedNodes.Remove(fromNode); + } + + // See explanation in EvalFilterStateNode for the type check + if (fromNode.IsFilterStateNode || fromNode.IsObserverStateNodeNonRestarting) + { + // We do not need to newState new listeners here, since the filter state node below this node did not quit + } + else + { + // Spawn all nodes below this EVERY node + // During the start of a child we need to use the temporary evaluator to catch any event created during a start + // Such events can be raised when the "not" operator is used. + var spawnEvaluator = new EvalEveryStateSpawnEvaluator(_evalEveryNode.Context.PatternContext.StatementName); + var spawned = _evalEveryNode.ChildNode.NewState(spawnEvaluator, null, 0L); + spawned.Start(_beginState); + + // If the whole spawned expression already turned true, quit it again + if (spawnEvaluator.IsEvaluatedTrue) + { + spawned.Quit(); + } + else + { + _spawnedNodes.Add(spawned); + spawned.ParentEvaluator = this; + } + } + + // All nodes indicate to their parents that their child node did not quit, therefore a false for isQuitted + ParentEvaluator.EvaluateTrue(matchEvent, this, false); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().APatternEveryEvaluateTrue(); } + } + + public override void Quit() + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QPatternEveryQuit(_evalEveryNode); } + // Stop all child nodes + foreach (var child in _spawnedNodes) + { + child.Quit(); + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().APatternEveryQuit(); } + } + + public override void Accept(EvalStateNodeVisitor visitor) + { + visitor.VisitEvery(_evalEveryNode.FactoryNode, this, _beginState); + foreach (var spawnedNode in _spawnedNodes) + { + spawnedNode.Accept(visitor); + } + } + + public override bool IsNotOperator + { + get { return false; } + } + + public override bool IsFilterStateNode + { + get { return false; } + } + + public bool IsFilterChildNonQuitting + { + get { return true; } + } + + public override bool IsObserverStateNodeNonRestarting + { + get { return false; } + } + + public override String ToString() + { + return "EvalEveryStateNode spawnedChildren=" + _spawnedNodes.Count; + } + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/EvalEveryStateSpawnEvaluator.cs b/NEsper.Core/NEsper.Core/pattern/EvalEveryStateSpawnEvaluator.cs new file mode 100755 index 000000000..0a856aa94 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/EvalEveryStateSpawnEvaluator.cs @@ -0,0 +1,58 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat.logging; + +namespace com.espertech.esper.pattern +{ + /// + /// This class contains the state of an 'every' operator in the evaluation state tree. + /// EVERY nodes work as a factory for new state subnodes. When a child node of an + /// EVERY node calls the evaluateTrue method on the EVERY node, the EVERY node will call + /// newState on its child node BEFORE it calls evaluateTrue on its parent node. It keeps + /// a reference to the new child in its list. (BEFORE because the root node could call + /// quit on child nodes for stopping all listeners). + /// + public sealed class EvalEveryStateSpawnEvaluator : Evaluator + { + private bool _isEvaluatedTrue; + + private readonly String _statementName; + + public EvalEveryStateSpawnEvaluator(String statementName) + { + _statementName = statementName; + } + + public bool IsEvaluatedTrue + { + get { return _isEvaluatedTrue; } + } + + public void EvaluateTrue(MatchedEventMap matchEvent, EvalStateNode fromNode, bool isQuitted) + { + Log.Warn("Event/request processing: Uncontrolled pattern matching of \"every\" operator - infinite loop when using EVERY operator on Expression(s) containing a not operator, for statement '" + _statementName + "'"); + _isEvaluatedTrue = true; + } + + public void EvaluateFalse(EvalStateNode fromNode, bool restartable) + { + Log.Warn("Event/request processing: Uncontrolled pattern matching of \"every\" operator - infinite loop when using EVERY operator on Expression(s) containing a not operator, for statement '" + _statementName + "'"); + _isEvaluatedTrue = true; + } + + public bool IsFilterChildNonQuitting + { + get { return true; } + } + + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/EvalFactoryNode.cs b/NEsper.Core/NEsper.Core/pattern/EvalFactoryNode.cs new file mode 100755 index 000000000..f9eae4fd8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/EvalFactoryNode.cs @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.IO; + +namespace com.espertech.esper.pattern +{ + /// + /// Superclass of all nodes in an evaluation tree representing an event pattern expression. + /// Follows the Composite pattern. Child nodes do not carry references to parent nodes, + /// the tree is unidirectional. + /// + public interface EvalFactoryNode + { + /// Adds a child node. + /// is the child evaluation tree node to add + void AddChildNode(EvalFactoryNode childNode); + + /// Returns list of child nodes + /// list of child nodes + IList ChildNodes { get; } + + void AddChildNodes(IEnumerable childNodes); + + EvalNode MakeEvalNode(PatternAgentInstanceContext agentInstanceContext, EvalNode parentNode); + + bool IsFilterChildNonQuitting { get; } + + short FactoryNodeId { get; set; } + + bool IsStateful { get; } + + /// Returns precendence. + /// precendence + PatternExpressionPrecedenceEnum Precedence { get; } + + /// Write expression considering precendence. + /// to use + /// precendence + void ToEPL(TextWriter writer, PatternExpressionPrecedenceEnum parentPrecedence); + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/EvalFilterConsumptionHandler.cs b/NEsper.Core/NEsper.Core/pattern/EvalFilterConsumptionHandler.cs new file mode 100755 index 000000000..78afc853b --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/EvalFilterConsumptionHandler.cs @@ -0,0 +1,17 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; + +namespace com.espertech.esper.pattern +{ + public class EvalFilterConsumptionHandler + { + public EventBean LastEvent { get; set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/pattern/EvalFilterFactoryNode.cs b/NEsper.Core/NEsper.Core/pattern/EvalFilterFactoryNode.cs new file mode 100755 index 000000000..6a4d06064 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/EvalFilterFactoryNode.cs @@ -0,0 +1,126 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; +using System.Reflection; +using System.Text; + +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; +using com.espertech.esper.filter; + +namespace com.espertech.esper.pattern +{ + /// + /// This class represents a filter of events in the evaluation tree representing any event expressions. + /// + public class EvalFilterFactoryNode : EvalNodeFactoryBase + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + [NonSerialized] private FilterSpecCompiled _filterSpec; + + /// + /// Constructor. + /// + /// specifies the filter properties + /// + /// is the name to use for adding matching events to the MatchedEventMap + /// table used when indicating truth value of true. + /// + /// when using @consume + public EvalFilterFactoryNode(FilterSpecRaw filterSpecification, + string eventAsName, + int? consumptionLevel) + { + EventAsTagNumber = -1; + RawFilterSpec = filterSpecification; + EventAsName = eventAsName; + ConsumptionLevel = consumptionLevel; + } + + public override EvalNode MakeEvalNode(PatternAgentInstanceContext agentInstanceContext, EvalNode parentNode) + { + return new EvalFilterNode(agentInstanceContext, this); + } + + /// + /// Returns the raw (unoptimized/validated) filter definition. + /// + /// filter def + public FilterSpecRaw RawFilterSpec { get; private set; } + + /// + /// Returns filter specification. + /// + /// filter definition + public FilterSpecCompiled FilterSpec + { + get { return _filterSpec; } + set { _filterSpec = value; } + } + + /// + /// Returns the tag for any matching events to this filter, or null since tags are optional. + /// + /// tag string for event + public string EventAsName { get; private set; } + + public int? ConsumptionLevel { get; private set; } + + public override String ToString() + { + var buffer = new StringBuilder(); + buffer.Append("EvalFilterNode rawFilterSpec=" + RawFilterSpec); + buffer.Append(" filterSpec=" + _filterSpec); + buffer.Append(" eventAsName=" + EventAsName); + return buffer.ToString(); + } + + public override bool IsFilterChildNonQuitting + { + get { return false; } + } + + public int EventAsTagNumber { get; set; } + + public override bool IsStateful + { + get { return false; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + if (EventAsName != null) { + writer.Write(EventAsName); + writer.Write("="); + } + writer.Write(RawFilterSpec.EventTypeName); + if (RawFilterSpec.FilterExpressions != null && RawFilterSpec.FilterExpressions.Count > 0) { + writer.Write("("); + ExprNodeUtility.ToExpressionStringParameterList(RawFilterSpec.FilterExpressions, writer); + writer.Write(")"); + } + if (ConsumptionLevel != null) { + writer.Write("@consume"); + if (ConsumptionLevel != 1) { + writer.Write("("); + writer.Write(ConsumptionLevel); + writer.Write(")"); + } + } + } + + public override PatternExpressionPrecedenceEnum Precedence + { + get { return PatternExpressionPrecedenceEnum.ATOM; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/pattern/EvalFilterNode.cs b/NEsper.Core/NEsper.Core/pattern/EvalFilterNode.cs new file mode 100755 index 000000000..56bd36c0c --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/EvalFilterNode.cs @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Reflection; + +using com.espertech.esper.compat.logging; +using com.espertech.esper.filter; + +namespace com.espertech.esper.pattern +{ + /// + /// This class represents a filter of events in the evaluation tree representing any event expressions. + /// + public class EvalFilterNode : EvalNodeBase + { + public EvalFilterNode(PatternAgentInstanceContext context, EvalFilterFactoryNode factoryNode) + : base(context) + { + FactoryNode = factoryNode; + if (context.AgentInstanceContext.AgentInstanceFilterProxy != null) + { + AddendumFilters = context.AgentInstanceContext.AgentInstanceFilterProxy.GetAddendumFilters(factoryNode.FilterSpec); + } + else + { + AddendumFilters = null; + } + } + + public EvalFilterFactoryNode FactoryNode { get; private set; } + + public FilterValueSetParam[][] AddendumFilters { get; private set; } + + public override EvalStateNode NewState(Evaluator parentNode, EvalStateNodeNumber stateNodeNumber, long stateNodeId) + { + if (Context.ConsumptionHandler != null) + { + return new EvalFilterStateNodeConsumeImpl(parentNode, this); + } + return new EvalFilterStateNode(parentNode, this); + } + + private static readonly ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/EvalFilterStateNode.cs b/NEsper.Core/NEsper.Core/pattern/EvalFilterStateNode.cs new file mode 100755 index 000000000..51b008d76 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/EvalFilterStateNode.cs @@ -0,0 +1,211 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Text; + +using com.espertech.esper.client; +using com.espertech.esper.client.soda; +using com.espertech.esper.compat; +using com.espertech.esper.core.service; +using com.espertech.esper.filter; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.pattern +{ + /// + /// This class contains the state of a single filter expression in the evaluation state tree. + /// + public class EvalFilterStateNode : EvalStateNode, FilterHandleCallback + { + private readonly EvalFilterNode _evalFilterNode; + + protected bool IsStarted; + protected EPStatementHandleCallback Handle; + protected FilterServiceEntry FilterServiceEntry; + protected MatchedEventMap BeginState; + + /// Constructor. + /// is the parent evaluator to call to indicate truth value + /// is the factory node associated to the state + public EvalFilterStateNode(Evaluator parentNode, EvalFilterNode evalFilterNode) + : base(parentNode) + { + _evalFilterNode = evalFilterNode; + } + + public override EvalNode FactoryNode + { + get { return _evalFilterNode; } + } + + public int StatementId + { + get { return _evalFilterNode.Context.PatternContext.StatementId; } + } + + public override void Start(MatchedEventMap beginState) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QPatternFilterStart(_evalFilterNode, beginState); } + BeginState = beginState; + if (IsStarted) + { + throw new IllegalStateException("Filter state node already active"); + } + + // Start the filter + IsStarted = true; + StartFiltering(); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().APatternFilterStart(); } + } + + public override void Quit() + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QPatternFilterQuit(_evalFilterNode, BeginState); } + IsStarted = false; + StopFiltering(); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().APatternFilterQuit(); } + } + + private void EvaluateTrue(MatchedEventMap theEvent, bool isQuitted) + { + ParentEvaluator.EvaluateTrue(theEvent, this, isQuitted); + } + + public EvalFilterNode EvalFilterNode + { + get { return _evalFilterNode; } + } + + public virtual void MatchFound(EventBean theEvent, ICollection allStmtMatches) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QPatternFilterMatch(_evalFilterNode, theEvent); } + + if (!IsStarted) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().APatternFilterMatch(true); } + return; + } + + MatchedEventMap passUp = BeginState.ShallowCopy(); + + if (_evalFilterNode.FactoryNode.FilterSpec.OptionalPropertyEvaluator != null) + { + EventBean[] propertyEvents = _evalFilterNode.FactoryNode.FilterSpec.OptionalPropertyEvaluator.GetProperty(theEvent, _evalFilterNode.Context.AgentInstanceContext); + if (propertyEvents == null) + { + return; // no results, ignore match + } + // Add event itself to the match event structure if a tag was provided + if (_evalFilterNode.FactoryNode.EventAsName != null) + { + passUp.Add(_evalFilterNode.FactoryNode.EventAsTagNumber, propertyEvents); + } + } + else + { + // Add event itself to the match event structure if a tag was provided + if (_evalFilterNode.FactoryNode.EventAsName != null) + { + passUp.Add(_evalFilterNode.FactoryNode.EventAsTagNumber, theEvent); + } + } + + // Explanation for the type cast... + // Each state node stops listening if it resolves to true, and all nodes newState + // new listeners again. However this would be a performance drain since + // and expression such as "on all b()" would remove the listener for b() for every match + // and the all node would newState a new listener. The remove operation and the add operation + // therefore don't take place if the EvalEveryStateNode node sits on top of a EvalFilterStateNode node. + bool isQuitted = false; + if (!(ParentEvaluator.IsFilterChildNonQuitting)) + { + StopFiltering(); + isQuitted = true; + } + + EvaluateTrue(passUp, isQuitted); + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().APatternFilterMatch(isQuitted); } + } + + public override void Accept(EvalStateNodeVisitor visitor) + { + visitor.VisitFilter(_evalFilterNode.FactoryNode, this, Handle, BeginState); + } + + public bool IsSubSelect + { + get { return false; } + } + + public override String ToString() + { + var buffer = new StringBuilder(); + buffer.Append("EvalFilterStateNode"); + buffer.Append(" tag="); + buffer.Append(_evalFilterNode.FactoryNode.FilterSpec); + buffer.Append(" spec="); + buffer.Append(_evalFilterNode.FactoryNode.FilterSpec); + return buffer.ToString(); + } + + public override bool IsFilterStateNode + { + get { return true; } + } + + public override bool IsNotOperator + { + get { return false; } + } + + public override bool IsObserverStateNodeNonRestarting + { + get { return false; } + } + + public override void RemoveMatch(ISet matchEvent) + { + if (!IsStarted) + { + return; + } + if (PatternConsumptionUtil.ContainsEvent(matchEvent, BeginState)) + { + Quit(); + ParentEvaluator.EvaluateFalse(this, true); + } + } + + protected void StartFiltering() + { + FilterService filterService = _evalFilterNode.Context.PatternContext.FilterService; + Handle = new EPStatementHandleCallback(_evalFilterNode.Context.AgentInstanceContext.EpStatementAgentInstanceHandle, this); + FilterValueSet filterValues = _evalFilterNode.FactoryNode.FilterSpec.GetValueSet(BeginState, _evalFilterNode.Context.AgentInstanceContext, _evalFilterNode.AddendumFilters); + FilterServiceEntry = filterService.Add(filterValues, Handle); + long filtersVersion = filterService.FiltersVersion; + _evalFilterNode.Context.AgentInstanceContext.EpStatementAgentInstanceHandle.StatementFilterVersion.StmtFilterVersion = filtersVersion; + } + + private void StopFiltering() + { + PatternContext context = _evalFilterNode.Context.PatternContext; + if (Handle != null) + { + context.FilterService.Remove(Handle, FilterServiceEntry); + } + Handle = null; + FilterServiceEntry = null; + IsStarted = false; + long filtersVersion = context.FilterService.FiltersVersion; + _evalFilterNode.Context.AgentInstanceContext.EpStatementAgentInstanceHandle.StatementFilterVersion.StmtFilterVersion = filtersVersion; + } + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/EvalFilterStateNodeConsume.cs b/NEsper.Core/NEsper.Core/pattern/EvalFilterStateNodeConsume.cs new file mode 100755 index 000000000..9abfa0035 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/EvalFilterStateNodeConsume.cs @@ -0,0 +1,15 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.pattern +{ + public interface EvalFilterStateNodeConsume + { + EvalFilterNode EvalFilterNode { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/EvalFilterStateNodeConsumeImpl.cs b/NEsper.Core/NEsper.Core/pattern/EvalFilterStateNodeConsumeImpl.cs new file mode 100755 index 000000000..76effd423 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/EvalFilterStateNodeConsumeImpl.cs @@ -0,0 +1,88 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.filter; + +namespace com.espertech.esper.pattern +{ + /// + /// This class contains the state of a single filter expression in the evaluation state tree. + /// + [Serializable] + public sealed class EvalFilterStateNodeConsumeImpl + : EvalFilterStateNode + , EvalFilterStateNodeConsume + { + public EvalFilterStateNodeConsumeImpl(Evaluator parentNode, EvalFilterNode evalFilterNode) + : base(parentNode, evalFilterNode) + { + } + + public override void MatchFound(EventBean theEvent, ICollection allStmtMatches) + { + // not receiving the remaining matches simply means we evaluate the event + if (allStmtMatches == null) + { + base.MatchFound(theEvent, null); + return; + } + + EvalFilterConsumptionHandler handler = EvalFilterNode.Context.ConsumptionHandler; + ProcessMatches(handler, theEvent, allStmtMatches); + } + + public static void ProcessMatches(EvalFilterConsumptionHandler handler, EventBean theEvent, ICollection allStmtMatches) + { + + // ignore all other callbacks for the same event + if (handler.LastEvent == theEvent) + { + return; + } + handler.LastEvent = theEvent; + + // evaluate consumption for all same-pattern filters + var matches = new LinkedList(); + + var currentConsumption = int.MinValue; + foreach (FilterHandleCallback callback in allStmtMatches) + { + if (!(callback is EvalFilterStateNodeConsume)) + { + continue; + } + var node = (EvalFilterStateNodeConsume)callback; + var consumption = node.EvalFilterNode.FactoryNode.ConsumptionLevel; + if (consumption == null) + { + consumption = 0; + } + + if (consumption > currentConsumption) + { + matches.Clear(); + currentConsumption = consumption.Value; + } + if (consumption == currentConsumption) + { + matches.AddLast(callback); + } + } + + // execute matches + foreach (FilterHandleCallback match in matches) + { + match.MatchFound(theEvent, null); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/EvalFollowedByFactoryNode.cs b/NEsper.Core/NEsper.Core/pattern/EvalFollowedByFactoryNode.cs new file mode 100755 index 000000000..bafd0d1ba --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/EvalFollowedByFactoryNode.cs @@ -0,0 +1,175 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.pattern +{ + /// + /// This class represents a followed-by operator in the evaluation tree representing any event expressions. + /// + [Serializable] + public class EvalFollowedByFactoryNode : EvalNodeFactoryBase + { + private IList _optionalMaxExpressions; + private readonly bool _hasEngineWidePatternCount; + + private EvalFollowedByNodeOpType? _opType; + private int?[] _cachedMaxPerChild; + [NonSerialized] + private ExprEvaluator[] _cachedMaxEvaluatorPerChild; + + public EvalFollowedByFactoryNode(IList optionalMaxExpressions, bool hasEngineWidePatternCount) + { + _optionalMaxExpressions = optionalMaxExpressions; + _hasEngineWidePatternCount = hasEngineWidePatternCount; + } + + public override EvalNode MakeEvalNode(PatternAgentInstanceContext agentInstanceContext, EvalNode parentNode) + { + if (_opType == null) + { + InitOpType(); + } + + EvalNode[] children = EvalNodeUtil.MakeEvalNodeChildren(ChildNodes, agentInstanceContext, parentNode); + return new EvalFollowedByNode(agentInstanceContext, this, children); + } + + public IList OptionalMaxExpressions + { + get { return _optionalMaxExpressions; } + set { _optionalMaxExpressions = value; } + } + + public override String ToString() + { + return ("EvalFollowedByNode children=" + ChildNodes.Count); + } + + protected void InitOpType() + { + bool hasMax = _optionalMaxExpressions != null && !_optionalMaxExpressions.IsEmpty(); + if (!hasMax) + { + _opType = _hasEngineWidePatternCount ? EvalFollowedByNodeOpType.NOMAX_POOL : EvalFollowedByNodeOpType.NOMAX_PLAIN; + return; + } + + _cachedMaxPerChild = new int?[ChildNodes.Count - 1]; + _cachedMaxEvaluatorPerChild = new ExprEvaluator[ChildNodes.Count - 1]; + + for (int i = 0; i < ChildNodes.Count - 1; i++) + { + if (_optionalMaxExpressions.Count <= i) + { + continue; + } + var optionalMaxExpression = _optionalMaxExpressions[i]; + if (optionalMaxExpression == null) + { + continue; + } + if (optionalMaxExpression.IsConstantResult) + { + var result = optionalMaxExpression.ExprEvaluator.Evaluate(new EvaluateParams(null, true, null)); + if (result != null) + { + _cachedMaxPerChild[i] = result.AsInt(); + } + } + else + { + _cachedMaxEvaluatorPerChild[i] = _optionalMaxExpressions[i].ExprEvaluator; + } + } + + _opType = _hasEngineWidePatternCount ? EvalFollowedByNodeOpType.MAX_POOL : EvalFollowedByNodeOpType.MAX_PLAIN; + } + + public EvalFollowedByNodeOpType OpType + { + get { return _opType.Value; } + } + + public int GetMax(int position) + { + var cached = _cachedMaxPerChild[position]; + if (cached != null) + { + return cached.Value; // constant value cached + } + + var cachedExpr = _cachedMaxEvaluatorPerChild[position]; + if (cachedExpr == null) + { + return -1; // no limit defined for this sub-expression + } + + var result = cachedExpr.Evaluate(new EvaluateParams(null, true, null)); + if (result != null) + { + return result.AsInt(); + } + return -1; // no limit + } + + public override bool IsFilterChildNonQuitting + { + get { return false; } + } + + public override bool IsStateful + { + get { return true; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + if (_optionalMaxExpressions == null || _optionalMaxExpressions.IsEmpty()) + { + PatternExpressionUtil.ToPrecedenceFreeEPL(writer, "->", ChildNodes, Precedence); + } + else + { + ChildNodes[0].ToEPL(writer, PatternExpressionPrecedenceEnum.MINIMUM); + for (int i = 1; i < ChildNodes.Count; i++) + { + ExprNode optionalMaxExpression = null; + if (_optionalMaxExpressions.Count > (i - 1)) + { + optionalMaxExpression = _optionalMaxExpressions[i - 1]; + } + if (optionalMaxExpression == null) + { + writer.Write(" -> "); + } + else + { + writer.Write(" -["); + writer.Write(ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(optionalMaxExpression)); + writer.Write("]> "); + } + ChildNodes[i].ToEPL(writer, PatternExpressionPrecedenceEnum.MINIMUM); + } + } + } + + public override PatternExpressionPrecedenceEnum Precedence + { + get { return PatternExpressionPrecedenceEnum.FOLLOWEDBY; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/EvalFollowedByNode.cs b/NEsper.Core/NEsper.Core/pattern/EvalFollowedByNode.cs new file mode 100755 index 000000000..f54c9f726 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/EvalFollowedByNode.cs @@ -0,0 +1,69 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.pattern +{ + /// + /// This class represents a followed-by operator in the evaluation tree representing any event expressions. + /// + [Serializable] + public class EvalFollowedByNode : EvalNodeBase + { + private readonly EvalFollowedByFactoryNode _factoryNode; + private readonly IList _childNodes; + + public EvalFollowedByNode(PatternAgentInstanceContext context, EvalFollowedByFactoryNode factoryNode, IList childNodes) + : base(context) + { + _factoryNode = factoryNode; + _childNodes = childNodes; + } + + public IList ChildNodes + { + get { return _childNodes; } + } + + public EvalFollowedByFactoryNode FactoryNode + { + get { return _factoryNode; } + } + + public override EvalStateNode NewState(Evaluator parentNode, EvalStateNodeNumber stateNodeNumber, long stateNodeId) + { + switch (_factoryNode.OpType) + { + case EvalFollowedByNodeOpType.NOMAX_PLAIN: + return new EvalFollowedByStateNode(parentNode, this); + default: + return new EvalFollowedByWithMaxStateNodeManaged(parentNode, this); + } + } + + public bool IsTrackWithPool + { + get + { + return _factoryNode.OpType == EvalFollowedByNodeOpType.NOMAX_POOL || + _factoryNode.OpType == EvalFollowedByNodeOpType.MAX_POOL; + } + } + + public bool IsTrackWithMax + { + get + { + return _factoryNode.OpType == EvalFollowedByNodeOpType.MAX_PLAIN || + _factoryNode.OpType == EvalFollowedByNodeOpType.MAX_POOL; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/EvalFollowedByNodeOpType.cs b/NEsper.Core/NEsper.Core/pattern/EvalFollowedByNodeOpType.cs new file mode 100755 index 000000000..6d8bdd395 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/EvalFollowedByNodeOpType.cs @@ -0,0 +1,18 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.pattern +{ + /// Followed-by operator types in the evaluation tree representing any event expressions. + public enum EvalFollowedByNodeOpType { + NOMAX_PLAIN, + MAX_PLAIN, + NOMAX_POOL, + MAX_POOL + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/EvalFollowedByStateNode.cs b/NEsper.Core/NEsper.Core/pattern/EvalFollowedByStateNode.cs new file mode 100755 index 000000000..fd4362949 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/EvalFollowedByStateNode.cs @@ -0,0 +1,177 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.pattern +{ + /// + /// This class represents the state of a followed-by operator in the evaluation state tree. + /// + public class EvalFollowedByStateNode : EvalStateNode, Evaluator + { + protected readonly EvalFollowedByNode EvalFollowedByNode; + protected readonly Dictionary Nodes; + + /// Constructor. + /// is the parent evaluator to call to indicate truth value + /// is the factory node associated to the state + public EvalFollowedByStateNode(Evaluator parentNode, EvalFollowedByNode evalFollowedByNode) + : base(parentNode) + { + EvalFollowedByNode = evalFollowedByNode; + Nodes = new Dictionary(); + } + + public override void RemoveMatch(ISet matchEvent) + { + PatternConsumptionUtil.ChildNodeRemoveMatches(matchEvent, Nodes.Keys); + } + + public override EvalNode FactoryNode + { + get { return EvalFollowedByNode; } + } + + public override void Start(MatchedEventMap beginState) + { + Instrument.With( + i => i.QPatternFollowedByStart(EvalFollowedByNode, beginState), + i => i.APatternFollowedByStart(), + () => + { + EvalNode child = EvalFollowedByNode.ChildNodes[0]; + EvalStateNode childState = child.NewState(this, null, 0L); + Nodes.Put(childState, 0); + childState.Start(beginState); + }); + } + + public void EvaluateTrue(MatchedEventMap matchEvent, EvalStateNode fromNode, bool isQuitted) + { + int index; + var hasIndex = Nodes.TryGetValue(fromNode, out index); + + bool[] isFollowedByQuitted = { false }; + + using (Instrument.With( + i => i.QPatternFollowedByEvaluateTrue(EvalFollowedByNode, matchEvent, index), + i => i.APatternFollowedByEvaluateTrue(isFollowedByQuitted[0]))) + { + if (isQuitted) + { + Nodes.Remove(fromNode); + } + + // the node may already have quit as a result of an outer state quitting this state, + // however the callback may still be received; It is fine to ignore this callback. + if (!hasIndex) + { + return; + } + + // If the match came from the very last filter, need to escalate + int numChildNodes = EvalFollowedByNode.ChildNodes.Count; + if (index == (numChildNodes - 1)) + { + if (Nodes.IsEmpty()) + { + isFollowedByQuitted[0] = true; + } + + ParentEvaluator.EvaluateTrue(matchEvent, this, isFollowedByQuitted[0]); + } + // Else start a new sub-expression for the next-in-line filter + else + { + EvalNode child = EvalFollowedByNode.ChildNodes[index + 1]; + EvalStateNode childState = child.NewState(this, null, 0L); + Nodes.Put(childState, index + 1); + childState.Start(matchEvent); + } + } + } + + public void EvaluateFalse(EvalStateNode fromNode, bool restartable) + { + Instrument.With( + i => i.QPatternFollowedByEvalFalse(EvalFollowedByNode), + i => i.APatternFollowedByEvalFalse(), + () => + { + fromNode.Quit(); + Nodes.Remove(fromNode); + + if (Nodes.IsEmpty()) + { + ParentEvaluator.EvaluateFalse(this, true); + QuitInternal(); + } + }); + } + + public override void Quit() + { + if (Nodes.IsEmpty()) + { + return; + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QPatternFollowedByQuit(EvalFollowedByNode); } + QuitInternal(); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().APatternFollowedByQuit(); } + } + + public override void Accept(EvalStateNodeVisitor visitor) + { + visitor.VisitFollowedBy(EvalFollowedByNode.FactoryNode, this, Nodes); + foreach (EvalStateNode node in Nodes.Keys) + { + node.Accept(visitor); + } + } + + public override bool IsNotOperator + { + get { return false; } + } + + public override bool IsFilterStateNode + { + get { return false; } + } + + public bool IsFilterChildNonQuitting + { + get { return false; } + } + + public override bool IsObserverStateNodeNonRestarting + { + get { return false; } + } + + public override String ToString() + { + return "EvalFollowedByStateNode nodes=" + Nodes.Count; + } + + private void QuitInternal() + { + foreach (EvalStateNode child in Nodes.Keys) + { + child.Quit(); + } + Nodes.Clear(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/EvalFollowedByWithMaxStateNodeManaged.cs b/NEsper.Core/NEsper.Core/pattern/EvalFollowedByWithMaxStateNodeManaged.cs new file mode 100755 index 000000000..9af1be087 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/EvalFollowedByWithMaxStateNodeManaged.cs @@ -0,0 +1,216 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.client.hook; +using com.espertech.esper.compat.collections; +using com.espertech.esper.pattern.pool; + +namespace com.espertech.esper.pattern +{ + /// + /// This class represents the state of a followed-by operator in the evaluation state tree, + /// with a maximum number of instances provided, and with the additional capability to + /// engine-wide report on pattern instances. + /// + public class EvalFollowedByWithMaxStateNodeManaged : EvalStateNode, Evaluator + { + protected readonly EvalFollowedByNode EvalFollowedByNode; + protected readonly Dictionary Nodes; + protected readonly int[] CountActivePerChild; + + /// Constructor. + /// is the parent evaluator to call to indicate truth value + /// is the factory node associated to the state + public EvalFollowedByWithMaxStateNodeManaged(Evaluator parentNode, EvalFollowedByNode evalFollowedByNode) + : base(parentNode) + { + EvalFollowedByNode = evalFollowedByNode; + Nodes = new Dictionary(); + CountActivePerChild = evalFollowedByNode.IsTrackWithMax ? new int[evalFollowedByNode.ChildNodes.Count - 1] : null; + } + + public override void RemoveMatch(ISet matchEvent) + { + PatternConsumptionUtil.ChildNodeRemoveMatches(matchEvent, Nodes.Keys); + } + + public override EvalNode FactoryNode + { + get { return EvalFollowedByNode; } + } + + public override void Start(MatchedEventMap beginState) + { + EvalNode child = EvalFollowedByNode.ChildNodes[0]; + EvalStateNode childState = child.NewState(this, null, 0L); + Nodes.Put(childState, 0); + childState.Start(beginState); + } + + public void EvaluateTrue(MatchedEventMap matchEvent, EvalStateNode fromNode, bool isQuitted) + { + int index; + var hasIndex = Nodes.TryGetValue(fromNode, out index); + + if (isQuitted) + { + Nodes.Remove(fromNode); + if (hasIndex && index > 0) + { + if (EvalFollowedByNode.IsTrackWithMax) + { + CountActivePerChild[index - 1]--; + } + if (EvalFollowedByNode.IsTrackWithPool) + { + PatternSubexpressionPoolStmtSvc poolSvc = EvalFollowedByNode.Context.StatementContext.PatternSubexpressionPoolSvc; + poolSvc.EngineSvc.DecreaseCount(EvalFollowedByNode, EvalFollowedByNode.Context.AgentInstanceContext); + poolSvc.StmtHandler.DecreaseCount(); + } + } + } + + // the node may already have quit as a result of an outer state quitting this state, + // however the callback may still be received; It is fine to ignore this callback. + if (!hasIndex) + { + return; + } + + // If the match came from the very last filter, need to escalate + int numChildNodes = EvalFollowedByNode.ChildNodes.Count; + if (index == (numChildNodes - 1)) + { + bool isFollowedByQuitted = Nodes.IsEmpty(); + ParentEvaluator.EvaluateTrue(matchEvent, this, isFollowedByQuitted); + } + // Else start a new sub-expression for the next-in-line filter + else + { + if (EvalFollowedByNode.IsTrackWithMax) + { + int max = EvalFollowedByNode.FactoryNode.GetMax(index); + if ((max != -1) && (max >= 0)) + { + if (CountActivePerChild[index] >= max) + { + EvalFollowedByNode.Context.AgentInstanceContext.StatementContext.ExceptionHandlingService.HandleCondition(new ConditionPatternSubexpressionMax(max), EvalFollowedByNode.Context.AgentInstanceContext.StatementContext.EpStatementHandle); + return; + } + } + } + + if (EvalFollowedByNode.IsTrackWithPool) + { + PatternSubexpressionPoolStmtSvc poolSvc = EvalFollowedByNode.Context.StatementContext.PatternSubexpressionPoolSvc; + bool allow = poolSvc.EngineSvc.TryIncreaseCount(EvalFollowedByNode, EvalFollowedByNode.Context.AgentInstanceContext); + if (!allow) + { + return; + } + poolSvc.StmtHandler.IncreaseCount(); + } + + if (EvalFollowedByNode.IsTrackWithMax) + { + CountActivePerChild[index]++; + } + + EvalNode child = EvalFollowedByNode.ChildNodes[index + 1]; + EvalStateNode childState = child.NewState(this, null, 0L); + Nodes.Put(childState, index + 1); + childState.Start(matchEvent); + } + } + + public void EvaluateFalse(EvalStateNode fromNode, bool restartable) + { + int index; + + fromNode.Quit(); + + var hasIndex = Nodes.TryGetValue(fromNode, out index); + Nodes.Remove(fromNode); + + if (hasIndex && index > 0) + { + if (EvalFollowedByNode.IsTrackWithMax) + { + CountActivePerChild[index - 1]--; + } + if (EvalFollowedByNode.IsTrackWithPool) + { + PatternSubexpressionPoolStmtSvc poolSvc = EvalFollowedByNode.Context.StatementContext.PatternSubexpressionPoolSvc; + poolSvc.EngineSvc.DecreaseCount(EvalFollowedByNode, EvalFollowedByNode.Context.AgentInstanceContext); + poolSvc.StmtHandler.DecreaseCount(); + } + } + + if (Nodes.IsEmpty()) + { + ParentEvaluator.EvaluateFalse(this, true); + Quit(); + } + } + + public override bool IsNotOperator + { + get { return false; } + } + + public override bool IsFilterStateNode + { + get { return false; } + } + + public bool IsFilterChildNonQuitting + { + get { return false; } + } + + public override bool IsObserverStateNodeNonRestarting + { + get { return false; } + } + + public override void Quit() + { + foreach (var entry in Nodes) + { + entry.Key.Quit(); + if (EvalFollowedByNode.IsTrackWithPool) + { + if (entry.Value > 0) + { + PatternSubexpressionPoolStmtSvc poolSvc = EvalFollowedByNode.Context.StatementContext.PatternSubexpressionPoolSvc; + poolSvc.EngineSvc.DecreaseCount(EvalFollowedByNode, EvalFollowedByNode.Context.AgentInstanceContext); + poolSvc.StmtHandler.DecreaseCount(); + } + } + } + } + + public override void Accept(EvalStateNodeVisitor visitor) + { + visitor.VisitFollowedBy(EvalFollowedByNode.FactoryNode, this, Nodes, CountActivePerChild); + foreach (EvalStateNode node in Nodes.Keys) + { + node.Accept(visitor); + } + } + + public override String ToString() + { + return "EvalFollowedByStateNode nodes=" + Nodes.Count; + } + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/EvalGuardFactoryNode.cs b/NEsper.Core/NEsper.Core/pattern/EvalGuardFactoryNode.cs new file mode 100755 index 000000000..74ce4c487 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/EvalGuardFactoryNode.cs @@ -0,0 +1,104 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; +using com.espertech.esper.pattern.guard; + +namespace com.espertech.esper.pattern +{ + /// + /// This class represents a guard in the evaluation tree representing an event expressions. + /// + [Serializable] + public class EvalGuardFactoryNode : EvalNodeFactoryBase + { + private readonly PatternGuardSpec _patternGuardSpec; + [NonSerialized] + private GuardFactory _guardFactory; + + /// Constructor. + /// factory for guard construction + public EvalGuardFactoryNode(PatternGuardSpec patternGuardSpec) + { + _patternGuardSpec = patternGuardSpec; + } + + public override EvalNode MakeEvalNode(PatternAgentInstanceContext agentInstanceContext, EvalNode parentNode) + { + EvalNode child = EvalNodeUtil.MakeEvalNodeSingleChild(ChildNodes, agentInstanceContext, parentNode); + return new EvalGuardNode(agentInstanceContext, this, child); + } + + /// Returns the guard object specification to use for instantiating the guard factory and guard. + /// guard specification + public PatternGuardSpec PatternGuardSpec + { + get { return _patternGuardSpec; } + } + + /// Supplies the guard factory to the node. + /// is the guard factory + public GuardFactory GuardFactory + { + set { _guardFactory = value; } + get { return _guardFactory; } + } + + public override String ToString() + { + return ("EvalGuardNode guardFactory=" + _guardFactory + + " children=" + ChildNodes.Count); + } + + public override bool IsFilterChildNonQuitting + { + get { return false; } + } + + public override bool IsStateful + { + get { return true; } + } + + public String ToPrecedenceFreeEPL() + { + var writer = new StringWriter(); + ToPrecedenceFreeEPL(writer); + return writer.ToString(); + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + ChildNodes[0].ToEPL(writer, Precedence); + if (_patternGuardSpec.ObjectNamespace == GuardEnum.WHILE_GUARD.GetNamespace() && + _patternGuardSpec.ObjectName == GuardEnum.WHILE_GUARD.GetName()) + { + writer.Write(" while "); + } + else + { + writer.Write(" where "); + writer.Write(_patternGuardSpec.ObjectNamespace); + writer.Write(":"); + writer.Write(_patternGuardSpec.ObjectName); + } + writer.Write("("); + ExprNodeUtility.ToExpressionStringParameterList(_patternGuardSpec.ObjectParameters, writer); + writer.Write(")"); + } + + public override PatternExpressionPrecedenceEnum Precedence + { + get { return PatternExpressionPrecedenceEnum.GUARD_POSTFIX; } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/pattern/EvalGuardNode.cs b/NEsper.Core/NEsper.Core/pattern/EvalGuardNode.cs new file mode 100755 index 000000000..12ea8bf7e --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/EvalGuardNode.cs @@ -0,0 +1,44 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.pattern +{ + /// + /// This class represents a guard in the evaluation tree representing an event expressions. + /// + [Serializable] + public class EvalGuardNode : EvalNodeBase + { + private readonly EvalGuardFactoryNode _factoryNode; + private readonly EvalNode _childNode; + + public EvalGuardNode(PatternAgentInstanceContext context, EvalGuardFactoryNode factoryNode, EvalNode childNode) + : base(context) + { + _factoryNode = factoryNode; + _childNode = childNode; + } + + public EvalGuardFactoryNode FactoryNode + { + get { return _factoryNode; } + } + + public EvalNode ChildNode + { + get { return _childNode; } + } + + public override EvalStateNode NewState(Evaluator parentNode, EvalStateNodeNumber stateNodeNumber, long stateNodeId) + { + return new EvalGuardStateNode(parentNode, this); + } + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/EvalGuardStateNode.cs b/NEsper.Core/NEsper.Core/pattern/EvalGuardStateNode.cs new file mode 100755 index 000000000..6a5958c58 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/EvalGuardStateNode.cs @@ -0,0 +1,180 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.pattern.guard; + +namespace com.espertech.esper.pattern +{ + /// + /// This class represents the state of a "within" operator in the evaluation state tree. + /// The within operator applies to a subexpression and is thus expected to only have one + /// child node. + /// + public class EvalGuardStateNode : EvalStateNode, Evaluator, Quitable + { + private readonly EvalGuardNode _evalGuardNode; + private EvalStateNode _activeChildNode; + private Guard _guard; + private MatchedEventMap _beginState; + + /// Constructor. + /// is the parent evaluator to call to indicate truth value + /// is the factory node associated to the state + public EvalGuardStateNode(Evaluator parentNode, EvalGuardNode evalGuardNode) + : base(parentNode) + { + _evalGuardNode = evalGuardNode; + } + + public override void RemoveMatch(ISet matchEvent) + { + if (PatternConsumptionUtil.ContainsEvent(matchEvent, _beginState)) + { + Quit(); + ParentEvaluator.EvaluateFalse(this, true); + } + else + { + if (_activeChildNode != null) + { + _activeChildNode.RemoveMatch(matchEvent); + } + } + } + + public override EvalNode FactoryNode + { + get { return _evalGuardNode; } + } + + public PatternAgentInstanceContext Context + { + get { return _evalGuardNode.Context; } + } + + public override void Start(MatchedEventMap beginState) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QPatternGuardStart(_evalGuardNode, beginState); } + _beginState = beginState; + _guard = _evalGuardNode.FactoryNode.GuardFactory.MakeGuard(_evalGuardNode.Context, beginState, this, null, null); + _activeChildNode = _evalGuardNode.ChildNode.NewState(this, null, 0L); + + // Start the single child state + _activeChildNode.Start(beginState); + + // Start the guard + _guard.StartGuard(); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().APatternGuardStart(); } + } + + public void EvaluateTrue(MatchedEventMap matchEvent, EvalStateNode fromNode, bool isQuitted) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QPatternGuardEvaluateTrue(_evalGuardNode, matchEvent); } + bool haveQuitted = _activeChildNode == null; + + // If one of the children quits, remove the child + if (isQuitted) + { + _activeChildNode = null; + + // Stop guard, since associated subexpression is gone + _guard.StopGuard(); + } + + if (!(haveQuitted)) + { + bool guardPass = _guard.Inspect(matchEvent); + if (guardPass) + { + ParentEvaluator.EvaluateTrue(matchEvent, this, isQuitted); + } + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().APatternGuardEvaluateTrue(isQuitted); } + } + + public void EvaluateFalse(EvalStateNode fromNode, bool restartable) + { + _activeChildNode = null; + ParentEvaluator.EvaluateFalse(this, true); + } + + public override void Quit() + { + if (_activeChildNode == null) + { + return; + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QPatternGuardQuit(_evalGuardNode); } + if (_activeChildNode != null) + { + _activeChildNode.Quit(); + _guard.StopGuard(); + } + + _activeChildNode = null; + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().APatternGuardQuit(); } + } + + public override void Accept(EvalStateNodeVisitor visitor) + { + visitor.VisitGuard(_evalGuardNode.FactoryNode, this, _guard); + if (_activeChildNode != null) + { + _activeChildNode.Accept(visitor); + } + } + + public override String ToString() + { + return "EvaluationWitinStateNode activeChildNode=" + _activeChildNode + + " guard=" + _guard; + } + + public override bool IsNotOperator + { + get { return false; } + } + + public override bool IsFilterStateNode + { + get { return false; } + } + + public bool IsFilterChildNonQuitting + { + get { return false; } + } + + public override bool IsObserverStateNodeNonRestarting + { + get { return false; } + } + + public void GuardQuit() + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QPatternGuardGuardQuit(_evalGuardNode); } + // It is possible that the child node has already been quit such as when the parent wait time was shorter. + // 1. parent node's guard indicates quit to all children + // 2. this node's guards also indicates quit, however that already occured + if (_activeChildNode != null) + { + _activeChildNode.Quit(); + } + _activeChildNode = null; + + // Indicate to parent state that this is permanently false. + ParentEvaluator.EvaluateFalse(this, true); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().APatternGuardGuardQuit(); } + } + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/EvalMatchUntilFactoryNode.cs b/NEsper.Core/NEsper.Core/pattern/EvalMatchUntilFactoryNode.cs new file mode 100755 index 000000000..75c14d775 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/EvalMatchUntilFactoryNode.cs @@ -0,0 +1,115 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.pattern +{ + /// + /// This class represents a match-until observer in the evaluation tree representing any event expressions. + /// + [Serializable] + public class EvalMatchUntilFactoryNode : EvalNodeFactoryBase + { + [NonSerialized] + private MatchedEventConvertor _convertor; + + /// Ctor. + public EvalMatchUntilFactoryNode(ExprNode lowerBounds, ExprNode upperBounds, ExprNode singleBound) + { + if (singleBound != null && (lowerBounds != null || upperBounds != null)) + { + throw new ArgumentException("Invalid bounds, specify either single bound or range bounds"); + } + LowerBounds = lowerBounds; + UpperBounds = upperBounds; + SingleBound = singleBound; + } + + public override EvalNode MakeEvalNode(PatternAgentInstanceContext agentInstanceContext, EvalNode parentNode) + { + EvalNode[] children = EvalNodeUtil.MakeEvalNodeChildren(ChildNodes, agentInstanceContext, parentNode); + return new EvalMatchUntilNode(agentInstanceContext, this, children[0], children.Length == 1 ? null : children[1]); + } + + /// Returns an array of tags for events, which is all tags used within the repeat-operator. + /// array of tags + public int[] TagsArrayed { get; set; } + + public ExprNode LowerBounds { get; set; } + + public ExprNode UpperBounds { get; set; } + + public ExprNode SingleBound { get; set; } + + /// Sets the convertor for matching events to events-per-stream. + /// convertor + public MatchedEventConvertor Convertor + { + get { return _convertor; } + set { _convertor = value; } + } + + public override String ToString() + { + return ("EvalMatchUntilNode children=" + ChildNodes.Count); + } + + public override bool IsFilterChildNonQuitting + { + get { return true; } + } + + public override bool IsStateful + { + get { return true; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + if (SingleBound != null) + { + writer.Write("["); + writer.Write(ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(SingleBound)); + writer.Write("] "); + } + else + { + if (LowerBounds != null || UpperBounds != null) + { + writer.Write("["); + if (LowerBounds != null) + { + writer.Write(ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(LowerBounds)); + } + writer.Write(":"); + if (UpperBounds != null) + { + writer.Write(ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(UpperBounds)); + } + writer.Write("] "); + } + } + ChildNodes[0].ToEPL(writer, Precedence); + if (ChildNodes.Count > 1) + { + writer.Write(" until "); + ChildNodes[1].ToEPL(writer, Precedence); + } + } + + public override PatternExpressionPrecedenceEnum Precedence + { + get { return PatternExpressionPrecedenceEnum.REPEAT_UNTIL; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/EvalMatchUntilNode.cs b/NEsper.Core/NEsper.Core/pattern/EvalMatchUntilNode.cs new file mode 100755 index 000000000..19c137f06 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/EvalMatchUntilNode.cs @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.pattern +{ + /// + /// This class represents a match-until observer in the evaluation tree representing any event expressions. + /// + [Serializable] + public class EvalMatchUntilNode : EvalNodeBase + { + public EvalMatchUntilNode(PatternAgentInstanceContext context, EvalMatchUntilFactoryNode factoryNode, EvalNode childNodeSub, EvalNode childNodeUntil) + : base(context) + { + FactoryNode = factoryNode; + ChildNodeSub = childNodeSub; + ChildNodeUntil = childNodeUntil; + } + + public EvalMatchUntilFactoryNode FactoryNode { get; private set; } + + public EvalNode ChildNodeSub { get; private set; } + + public EvalNode ChildNodeUntil { get; private set; } + + public override EvalStateNode NewState(Evaluator parentNode, EvalStateNodeNumber stateNodeNumber, long stateNodeId) + { + return new EvalMatchUntilStateNode(parentNode, this); + } + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/EvalMatchUntilStateBounds.cs b/NEsper.Core/NEsper.Core/pattern/EvalMatchUntilStateBounds.cs new file mode 100755 index 000000000..a3290cfbd --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/EvalMatchUntilStateBounds.cs @@ -0,0 +1,74 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.pattern +{ + public class EvalMatchUntilStateBounds + { + private readonly int? _lowerbounds; + private readonly int? _upperbounds; + + public EvalMatchUntilStateBounds(int? lowerbounds, int? upperbounds) + { + _lowerbounds = lowerbounds; + _upperbounds = upperbounds; + } + + public int? Lowerbounds + { + get { return _lowerbounds; } + } + + public int? Upperbounds + { + get { return _upperbounds; } + } + + public static EvalMatchUntilStateBounds InitBounds( + EvalMatchUntilFactoryNode factoryNode, + MatchedEventMap beginState, + PatternAgentInstanceContext context) + { + int? lowerbounds = null; + int? upperbounds = null; + var eventsPerStream = factoryNode.Convertor.Convert(beginState); + var evaluateParams = new EvaluateParams(eventsPerStream, true, context.AgentInstanceContext); + if (factoryNode.SingleBound != null) + { + var bounds = (int?) factoryNode.SingleBound.ExprEvaluator.Evaluate(evaluateParams); + lowerbounds = bounds; + upperbounds = bounds; + } + else + { + if (factoryNode.LowerBounds != null) + { + lowerbounds = (int?) factoryNode.LowerBounds.ExprEvaluator.Evaluate(evaluateParams); + } + if (factoryNode.UpperBounds != null) + { + upperbounds = (int?) factoryNode.UpperBounds.ExprEvaluator.Evaluate(evaluateParams); + } + if (upperbounds != null && lowerbounds != null) + { + if (upperbounds < lowerbounds) + { + int? lbounds = lowerbounds; + lowerbounds = upperbounds; + upperbounds = lbounds; + } + } + } + + return new EvalMatchUntilStateBounds(lowerbounds, upperbounds); + } + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/EvalMatchUntilStateNode.cs b/NEsper.Core/NEsper.Core/pattern/EvalMatchUntilStateNode.cs new file mode 100755 index 000000000..b83677ad1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/EvalMatchUntilStateNode.cs @@ -0,0 +1,354 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.pattern +{ + /// + /// This class represents the state of a match-until node in the evaluation state tree. + /// + public class EvalMatchUntilStateNode : EvalStateNode, Evaluator + { + private readonly EvalMatchUntilNode _evalMatchUntilNode; + private MatchedEventMap _beginState; + private readonly IList[] _matchedEventArrays; + + private EvalStateNode _stateMatcher; + private EvalStateNode _stateUntil; + private int _numMatches; + private int? _lowerbounds; + private int? _upperbounds; + + /// + /// Constructor. + /// + /// is the parent evaluator to call to indicate truth value + /// is the factory node associated to the state + public EvalMatchUntilStateNode(Evaluator parentNode, EvalMatchUntilNode evalMatchUntilNode) + : base(parentNode) + { + _matchedEventArrays = new IList[evalMatchUntilNode.FactoryNode.TagsArrayed.Length]; + _evalMatchUntilNode = evalMatchUntilNode; + } + + public override void RemoveMatch(ISet matchEvent) + { + bool quit = PatternConsumptionUtil.ContainsEvent(matchEvent, _beginState); + if (!quit) + { + foreach (List list in _matchedEventArrays) + { + if (list == null) + { + continue; + } + foreach (EventBean @event in list) + { + if (matchEvent.Contains(@event)) + { + quit = true; + break; + } + } + if (quit) + { + break; + } + } + } + if (quit) + { + Quit(); + ParentEvaluator.EvaluateFalse(this, true); + } + else + { + if (_stateMatcher != null) + { + _stateMatcher.RemoveMatch(matchEvent); + } + if (_stateUntil != null) + { + _stateUntil.RemoveMatch(matchEvent); + } + } + } + + public override EvalNode FactoryNode + { + get { return _evalMatchUntilNode; } + } + + public override void Start(MatchedEventMap beginState) + { + Instrument.With( + i => i.QPatternMatchUntilStart(_evalMatchUntilNode, beginState), + i => i.APatternMatchUntilStart(), + () => + { + _beginState = beginState; + + EvalNode childMatcher = _evalMatchUntilNode.ChildNodeSub; + _stateMatcher = childMatcher.NewState(this, null, 0L); + + if (_evalMatchUntilNode.ChildNodeUntil != null) + { + EvalNode childUntil = _evalMatchUntilNode.ChildNodeUntil; + _stateUntil = childUntil.NewState(this, null, 0L); + } + + // start until first, it controls the expression + // if the same event fires both match and until, the match should not count + if (_stateUntil != null) + { + _stateUntil.Start(beginState); + } + + EvalMatchUntilStateBounds bounds = + EvalMatchUntilStateBounds.InitBounds( + _evalMatchUntilNode.FactoryNode, beginState, _evalMatchUntilNode.Context); + _lowerbounds = bounds.Lowerbounds; + _upperbounds = bounds.Upperbounds; + + if (_stateMatcher != null) + { + _stateMatcher.Start(beginState); + } + }); + } + + public void EvaluateTrue(MatchedEventMap matchEvent, EvalStateNode fromNode, bool isQuitted) + { + Instrument.With( + i => i.QPatternMatchUntilEvaluateTrue(_evalMatchUntilNode, matchEvent, fromNode == _stateUntil), + i => i.APatternMatchUntilEvaluateTrue(_stateMatcher == null && _stateUntil == null), + () => + { + bool isMatcher = false; + if (fromNode == _stateMatcher) + { + // Add the additional tagged events to the list for later posting + isMatcher = true; + _numMatches++; + int[] tags = _evalMatchUntilNode.FactoryNode.TagsArrayed; + for (int i = 0; i < tags.Length; i++) + { + var theEvent = matchEvent.GetMatchingEventAsObject(tags[i]); + if (theEvent != null) + { + if (_matchedEventArrays[i] == null) + { + _matchedEventArrays[i] = new List(); + } + if (theEvent is EventBean) + { + _matchedEventArrays[i].Add((EventBean)theEvent); + } + else + { + EventBean[] arrayEvents = (EventBean[])theEvent; + _matchedEventArrays[i].AddAll(arrayEvents); + } + + } + } + } + + if (isQuitted) + { + if (isMatcher) + { + _stateMatcher = null; + } + else + { + _stateUntil = null; + } + } + + // handle matcher evaluating true + if (isMatcher) + { + if ((IsTightlyBound) && (_numMatches == _lowerbounds)) + { + QuitInternal(); + MatchedEventMap consolidated = Consolidate( + matchEvent, _matchedEventArrays, _evalMatchUntilNode.FactoryNode.TagsArrayed); + ParentEvaluator.EvaluateTrue(consolidated, this, true); + } + else + { + // restart or keep started if not bounded, or not upper bounds, or upper bounds not reached + bool restart = (!IsBounded) || + (_upperbounds == null) || + (_upperbounds > _numMatches); + if (_stateMatcher == null) + { + if (restart) + { + EvalNode childMatcher = _evalMatchUntilNode.ChildNodeSub; + _stateMatcher = childMatcher.NewState(this, null, 0L); + _stateMatcher.Start(_beginState); + } + } + else + { + if (!restart) + { + _stateMatcher.Quit(); + _stateMatcher = null; + } + } + } + } + else + // handle until-node + { + QuitInternal(); + + // consolidate multiple matched events into a single event + MatchedEventMap consolidated = Consolidate( + matchEvent, _matchedEventArrays, _evalMatchUntilNode.FactoryNode.TagsArrayed); + + if ((_lowerbounds != null) && (_numMatches < _lowerbounds)) + { + ParentEvaluator.EvaluateFalse(this, true); + } + else + { + ParentEvaluator.EvaluateTrue(consolidated, this, true); + } + } + }); + } + + public static MatchedEventMap Consolidate(MatchedEventMap beginState, IList[] matchedEventList, int[] tagsArrayed) + { + if (tagsArrayed == null) + { + return beginState; + } + + for (int i = 0; i < tagsArrayed.Length; i++) + { + if (matchedEventList[i] == null) + { + continue; + } + EventBean[] eventsForTag = matchedEventList[i].ToArray(); + beginState.Add(tagsArrayed[i], eventsForTag); + } + + return beginState; + } + + public void EvaluateFalse(EvalStateNode fromNode, bool restartable) + { + Instrument.With( + i => i.QPatternMatchUntilEvalFalse(_evalMatchUntilNode, fromNode == _stateUntil), + i => i.APatternMatchUntilEvalFalse(), + () => + { + var isMatcher = fromNode == _stateMatcher; + if (isMatcher) + { + _stateMatcher.Quit(); + _stateMatcher = null; + } + else + { + _stateUntil.Quit(); + _stateUntil = null; + } + ParentEvaluator.EvaluateFalse(this, true); + }); + } + + public override void Quit() + { + if (_stateMatcher == null && _stateUntil == null) + { + return; + } + + Instrument.With( + i => i.QPatternMatchUntilQuit(_evalMatchUntilNode), + i => i.APatternMatchUntilQuit(), + QuitInternal); + } + + public override void Accept(EvalStateNodeVisitor visitor) + { + visitor.VisitMatchUntil(_evalMatchUntilNode.FactoryNode, this, _matchedEventArrays, _beginState); + if (_stateMatcher != null) + { + _stateMatcher.Accept(visitor); + } + if (_stateUntil != null) + { + _stateUntil.Accept(visitor); + } + } + + public override String ToString() + { + return "EvalMatchUntilStateNode"; + } + + public override bool IsNotOperator + { + get { return false; } + } + + public override bool IsFilterStateNode + { + get { return false; } + } + + public bool IsFilterChildNonQuitting + { + get { return true; } + } + + public override bool IsObserverStateNodeNonRestarting + { + get { return false; } + } + + private bool IsTightlyBound + { + get { return _lowerbounds != null && _upperbounds != null && _upperbounds.Equals(_lowerbounds); } + } + + private bool IsBounded + { + get { return _lowerbounds != null || _upperbounds != null; } + } + + private void QuitInternal() + { + if (_stateMatcher != null) + { + _stateMatcher.Quit(); + _stateMatcher = null; + } + if (_stateUntil != null) + { + _stateUntil.Quit(); + _stateUntil = null; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/EvalNode.cs b/NEsper.Core/NEsper.Core/pattern/EvalNode.cs new file mode 100755 index 000000000..409dfa07e --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/EvalNode.cs @@ -0,0 +1,22 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.pattern +{ + /// + /// Superclass of all nodes in an evaluation tree representing an event pattern expression. + /// Follows the Composite pattern. Child nodes do not carry references to parent nodes, the + /// tree is unidirectional. + /// + public interface EvalNode + { + EvalStateNode NewState(Evaluator parentNode, + EvalStateNodeNumber stateNodeNumber, + long stateNodeId); + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/EvalNodeAnalysisResult.cs b/NEsper.Core/NEsper.Core/pattern/EvalNodeAnalysisResult.cs new file mode 100755 index 000000000..63616bd68 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/EvalNodeAnalysisResult.cs @@ -0,0 +1,55 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.Linq; + +namespace com.espertech.esper.pattern +{ + /// + /// Result of analysis of pattern expression node tree. + /// + public class EvalNodeAnalysisResult + { + private readonly IList _activeNodes = new List(); + + /// Add a node found. + /// found + public void AddNode(EvalFactoryNode node) + { + _activeNodes.Add(node); + } + + /// Returns all nodes found. + /// pattern nodes + public IList ActiveNodes + { + get { return _activeNodes; } + } + + /// Returns filter nodes. + /// filter nodes + public IList FilterNodes + { + get + { + return _activeNodes.OfType().ToList(); + } + } + + /// Returns the repeat-nodes. + /// repeat nodes + public IList RepeatNodes + { + get + { + return _activeNodes.OfType().ToList(); + } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/pattern/EvalNodeBase.cs b/NEsper.Core/NEsper.Core/pattern/EvalNodeBase.cs new file mode 100755 index 000000000..12efe9c60 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/EvalNodeBase.cs @@ -0,0 +1,46 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.pattern +{ + /// + /// Superclass of all nodes in an evaluation tree representing an event pattern expression. + /// Follows the Composite pattern. Child nodes do not carry references to parent nodes, the + /// tree is unidirectional. + /// + [Serializable] + public abstract class EvalNodeBase : EvalNode + { + /// + /// Initializes a new instance of the class. + /// + /// The context. + protected EvalNodeBase(PatternAgentInstanceContext context) + { + Context = context; + } + + /// + /// Create the evaluation state node containing the truth value state for each operator in an + /// event expression. + /// + /// the parent evaluator node that this node indicates a change in truth value to + /// The state node number. + /// the new state object's identifier + /// + /// state node containing the truth value state for the operator + /// + public abstract EvalStateNode NewState(Evaluator parentNode, + EvalStateNodeNumber stateNodeNumber, + long stateNodeId); + + public virtual PatternAgentInstanceContext Context { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/EvalNodeFactoryBase.cs b/NEsper.Core/NEsper.Core/pattern/EvalNodeFactoryBase.cs new file mode 100755 index 000000000..997a90d56 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/EvalNodeFactoryBase.cs @@ -0,0 +1,79 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +namespace com.espertech.esper.pattern +{ + /// + /// Superclass of all nodes in an evaluation tree representing an event pattern expression. + /// Follows the Composite pattern. Child nodes do not carry references to parent nodes, + /// the tree is unidirectional. + /// + [Serializable] + public abstract class EvalNodeFactoryBase + : EvalFactoryNode + { + private readonly List _childNodes; + private short _factoryNodeId; + + public abstract EvalNode MakeEvalNode(PatternAgentInstanceContext agentInstanceContext, EvalNode parentNode); + public abstract void ToPrecedenceFreeEPL(TextWriter writer); + + /// Constructor creates a list of child nodes. + protected EvalNodeFactoryBase() + { + _childNodes = new List(); + } + + /// Adds a child node. + /// is the child evaluation tree node to add + public void AddChildNode(EvalFactoryNode childNode) + { + _childNodes.Add(childNode); + } + + public void AddChildNodes(IEnumerable childNodesToAdd) + { + _childNodes.AddRange(childNodesToAdd); + } + + /// Returns list of child nodes. + /// list of child nodes + public IList ChildNodes + { + get { return _childNodes; } + } + + public short FactoryNodeId + { + get { return _factoryNodeId; } + set { _factoryNodeId = value; } + } + + public void ToEPL(TextWriter writer, PatternExpressionPrecedenceEnum parentPrecedence) + { + if (Precedence.GetLevel() < parentPrecedence.GetLevel()) + { + writer.Write("("); + ToPrecedenceFreeEPL(writer); + writer.Write(")"); + } + else + { + ToPrecedenceFreeEPL(writer); + } + } + + public abstract bool IsFilterChildNonQuitting { get; } + public abstract bool IsStateful { get; } + public abstract PatternExpressionPrecedenceEnum Precedence { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/EvalNodeUtil.cs b/NEsper.Core/NEsper.Core/pattern/EvalNodeUtil.cs new file mode 100755 index 000000000..d598b2bd9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/EvalNodeUtil.cs @@ -0,0 +1,92 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.pattern +{ + public class EvalNodeUtil + { + /// Searched recursivly for pattern evaluation filter nodes. + /// is the root node + /// list of filter nodes + public static EvalNodeAnalysisResult RecursiveAnalyzeChildNodes(EvalFactoryNode currentNode) + { + var evalNodeAnalysisResult = new EvalNodeAnalysisResult(); + RecursiveAnalyzeChildNodes(evalNodeAnalysisResult, currentNode); + return evalNodeAnalysisResult; + } + + private static void RecursiveAnalyzeChildNodes(EvalNodeAnalysisResult evalNodeAnalysisResult, EvalFactoryNode currentNode) + { + if ((currentNode is EvalFilterFactoryNode) || + (currentNode is EvalGuardFactoryNode) || + (currentNode is EvalObserverFactoryNode) || + (currentNode is EvalMatchUntilFactoryNode) || + (currentNode is EvalEveryDistinctFactoryNode)) + { + evalNodeAnalysisResult.AddNode(currentNode); + } + + foreach (EvalFactoryNode node in currentNode.ChildNodes) + { + RecursiveAnalyzeChildNodes(evalNodeAnalysisResult, node); + } + } + + /// + /// Returns all child nodes as a set. + /// + /// parent node + /// The filter. + /// all child nodes + public static ICollection RecursiveGetChildNodes(EvalFactoryNode currentNode, EvalNodeUtilFactoryFilter filter) + { + ICollection result = new LinkedHashSet(); + if (filter.Consider(currentNode)) + { + result.Add(currentNode); + } + RecursiveGetChildNodes(result, currentNode, filter); + return result; + } + + private static void RecursiveGetChildNodes(ICollection set, EvalFactoryNode currentNode, EvalNodeUtilFactoryFilter filter) + { + foreach (var node in currentNode.ChildNodes) + { + if (filter.Consider(node)) + { + set.Add(node); + } + RecursiveGetChildNodes(set, node, filter); + } + } + + public static EvalRootNode MakeRootNodeFromFactory(EvalRootFactoryNode rootFactoryNode, PatternAgentInstanceContext patternAgentInstanceContext) + { + return (EvalRootNode)rootFactoryNode.MakeEvalNode(patternAgentInstanceContext, null); + } + + public static EvalNode MakeEvalNodeSingleChild(IList childNodes, PatternAgentInstanceContext agentInstanceContext, EvalNode parentNode) + { + return childNodes[0].MakeEvalNode(agentInstanceContext, parentNode); + } + + public static EvalNode[] MakeEvalNodeChildren(IList childNodes, PatternAgentInstanceContext agentInstanceContext, EvalNode parentNode) + { + var children = new EvalNode[childNodes.Count]; + for (int i = 0; i < childNodes.Count; i++) + { + children[i] = childNodes[i].MakeEvalNode(agentInstanceContext, parentNode); + } + return children; + } + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/EvalNodeUtilFactoryFilter.cs b/NEsper.Core/NEsper.Core/pattern/EvalNodeUtilFactoryFilter.cs new file mode 100755 index 000000000..d3c6d36af --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/EvalNodeUtilFactoryFilter.cs @@ -0,0 +1,15 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.pattern +{ + public interface EvalNodeUtilFactoryFilter + { + bool Consider(EvalFactoryNode node); + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/EvalNotFactoryNode.cs b/NEsper.Core/NEsper.Core/pattern/EvalNotFactoryNode.cs new file mode 100755 index 000000000..cd3b0b792 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/EvalNotFactoryNode.cs @@ -0,0 +1,57 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +using com.espertech.esper.compat.logging; + +namespace com.espertech.esper.pattern +{ + /// + /// This class represents an 'not' operator in the evaluation tree representing any event expressions. + /// + public class EvalNotFactoryNode : EvalNodeFactoryBase{ + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + public EvalNotFactoryNode() { + } + + public override EvalNode MakeEvalNode(PatternAgentInstanceContext agentInstanceContext, EvalNode parentNode) + { + EvalNode child = EvalNodeUtil.MakeEvalNodeSingleChild(this.ChildNodes, agentInstanceContext, parentNode); + return new EvalNotNode(agentInstanceContext, this, child); + } + + public override String ToString() + { + return "EvalNotNode children=" + this.ChildNodes.Count; + } + + public override bool IsFilterChildNonQuitting + { + get { return false; } + } + + public override bool IsStateful + { + get { return true; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + writer.Write("not "); + ChildNodes[0].ToEPL(writer, Precedence); + } + + public override PatternExpressionPrecedenceEnum Precedence + { + get { return PatternExpressionPrecedenceEnum.UNARY; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/pattern/EvalNotNode.cs b/NEsper.Core/NEsper.Core/pattern/EvalNotNode.cs new file mode 100755 index 000000000..f98c81ae2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/EvalNotNode.cs @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.compat.logging; + +namespace com.espertech.esper.pattern +{ + /// + /// This class represents an 'not' operator in the evaluation tree representing any event expressions. + /// + public class EvalNotNode : EvalNodeBase + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly EvalNotFactoryNode _factoryNode; + private readonly EvalNode _childNode; + + public EvalNotNode(PatternAgentInstanceContext context, EvalNotFactoryNode factoryNode, EvalNode childNode) + : base(context) + { + _factoryNode = factoryNode; + _childNode = childNode; + } + + public EvalNotFactoryNode FactoryNode + { + get { return _factoryNode; } + } + + public EvalNode ChildNode + { + get { return _childNode; } + } + + public override EvalStateNode NewState( + Evaluator parentNode, + EvalStateNodeNumber stateNodeNumber, + long stateNodeId) + { + return new EvalNotStateNode(parentNode, this); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/pattern/EvalNotStateNode.cs b/NEsper.Core/NEsper.Core/pattern/EvalNotStateNode.cs new file mode 100755 index 000000000..711915e3c --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/EvalNotStateNode.cs @@ -0,0 +1,144 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.logging; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.pattern +{ + /// + /// This class contains the state of an 'not' operator in the evaluation state tree. + /// The not operator inverts the truth of the subexpression under it. It defaults to being true rather than + /// being false at startup. True at startup means it will generate an event on newState such that parent expressions + /// may turn true. It turns permenantly false when it receives an event from a subexpression and the subexpression + /// quitted. It indicates the false state via an evaluateFalse call on its parent evaluator. + /// + public class EvalNotStateNode : EvalStateNode, Evaluator + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly EvalNotNode _evalNotNode; + private EvalStateNode _childNode; + + /// + /// Constructor. + /// + /// is the parent evaluator to call to indicate truth value + /// is the factory node associated to the state + public EvalNotStateNode(Evaluator parentNode, EvalNotNode evalNotNode) + : base(parentNode) + { + _evalNotNode = evalNotNode; + } + + public override void RemoveMatch(ISet matchEvent) + { + // The not-operator does not pass along the matches + } + + public override EvalNode FactoryNode + { + get { return _evalNotNode; } + } + + public override void Start(MatchedEventMap beginState) + { + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().QPatternNotStart(_evalNotNode, beginState); + } + _childNode = _evalNotNode.ChildNode.NewState(this, null, 0L); + _childNode.Start(beginState); + + // The not node acts by inverting the truth + // By default the child nodes are false. This not node acts inverts the truth and pretends the child is true, + // raising an event up. + this.ParentEvaluator.EvaluateTrue(beginState, this, false); + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().APatternNotStart(); + } + } + + public void EvaluateFalse(EvalStateNode fromNode, bool restartable) + { + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().QPatternNotEvalFalse(_evalNotNode); + } + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().APatternNotEvalFalse(); + } + } + + public void EvaluateTrue(MatchedEventMap matchEvent, EvalStateNode fromNode, bool isQuitted) + { + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().QPatternNotEvaluateTrue(_evalNotNode, matchEvent); + } + // Only is the subexpression stopped listening can we tell the parent evaluator that this + // turned permanently false. + if (isQuitted) { + _childNode = null; + this.ParentEvaluator.EvaluateFalse(this, true); + } else { + // If the subexpression did not quit, we stay in the "true" state + } + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().APatternNotEvaluateTrue(isQuitted); + } + } + + public override void Quit() + { + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().QPatternNotQuit(_evalNotNode); + } + if (_childNode != null) { + _childNode.Quit(); + } + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().APatternNotQuit(); + } + } + + public override void Accept(EvalStateNodeVisitor visitor) + { + visitor.VisitNot(_evalNotNode.FactoryNode, this); + if (_childNode != null) { + _childNode.Accept(visitor); + } + } + + public override bool IsNotOperator + { + get { return true; } + } + + public override bool IsFilterStateNode + { + get { return false; } + } + + public bool IsFilterChildNonQuitting + { + get { return false; } + } + + public override bool IsObserverStateNodeNonRestarting + { + get { return false; } + } + + public override String ToString() + { + return "EvalNotStateNode child=" + _childNode; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/pattern/EvalObserverFactoryNode.cs b/NEsper.Core/NEsper.Core/pattern/EvalObserverFactoryNode.cs new file mode 100755 index 000000000..0d9e17c4e --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/EvalObserverFactoryNode.cs @@ -0,0 +1,98 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; +using com.espertech.esper.pattern.observer; + +namespace com.espertech.esper.pattern +{ + /// + /// This class represents an observer expression in the evaluation tree representing an pattern expression. + /// + public class EvalObserverFactoryNode : EvalNodeFactoryBase + { + private readonly PatternObserverSpec _patternObserverSpec; + [NonSerialized] + private ObserverFactory _observerFactory; + + /// Constructor. + /// is the factory to use to get an observer instance + public EvalObserverFactoryNode(PatternObserverSpec patternObserverSpec) + { + _patternObserverSpec = patternObserverSpec; + } + + public override EvalNode MakeEvalNode(PatternAgentInstanceContext agentInstanceContext, EvalNode parentNode) + { + return new EvalObserverNode(agentInstanceContext, this); + } + + /// Returns the observer object specification to use for instantiating the observer factory and observer. + /// observer specification + public PatternObserverSpec PatternObserverSpec + { + get { return _patternObserverSpec; } + } + + /// Returns the observer factory. + /// factory for observer instances + public ObserverFactory ObserverFactory + { + get { return _observerFactory; } + set { _observerFactory = value; } + } + + public override String ToString() + { + return ("EvalObserverNode observerFactory=" + _observerFactory + + " children=" + ChildNodes.Count); + } + + public override bool IsFilterChildNonQuitting + { + get { return false; } + } + + public override bool IsStateful + { + get { return false; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + writer.Write(_patternObserverSpec.ObjectNamespace); + writer.Write(":"); + writer.Write(_patternObserverSpec.ObjectName); + writer.Write("("); + ExprNodeUtility.ToExpressionStringParameterList(_patternObserverSpec.ObjectParameters, writer); + writer.Write(")"); + } + + public String ToPrecedenceFreeEPL() + { + var writer = new StringWriter(); + ToPrecedenceFreeEPL(writer); + return writer.ToString(); + } + + public override PatternExpressionPrecedenceEnum Precedence + { + get { return PatternExpressionPrecedenceEnum.ATOM; } + } + + public virtual bool IsObserverStateNodeNonRestarting + { + get { return _observerFactory.IsNonRestarting; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/EvalObserverNode.cs b/NEsper.Core/NEsper.Core/pattern/EvalObserverNode.cs new file mode 100755 index 000000000..15f009256 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/EvalObserverNode.cs @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.pattern +{ + /// + /// This class represents an observer expression in the evaluation tree representing an pattern expression. + /// + public class EvalObserverNode : EvalNodeBase + { + private readonly EvalObserverFactoryNode _factoryNode; + + public EvalObserverNode(PatternAgentInstanceContext context, EvalObserverFactoryNode factoryNode) + : base(context) + { + _factoryNode = factoryNode; + } + + public EvalObserverFactoryNode FactoryNode + { + get { return _factoryNode; } + } + + public override EvalStateNode NewState(Evaluator parentNode, EvalStateNodeNumber stateNodeNumber, long stateNodeId) + { + return new EvalObserverStateNode(parentNode, this); + } + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/EvalObserverStateNode.cs b/NEsper.Core/NEsper.Core/pattern/EvalObserverStateNode.cs new file mode 100755 index 000000000..7c1db780f --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/EvalObserverStateNode.cs @@ -0,0 +1,107 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.pattern.observer; + +namespace com.espertech.esper.pattern +{ + /// + /// This class represents the state of an eventObserver sub-expression in the evaluation state tree. + /// + public class EvalObserverStateNode : EvalStateNode, ObserverEventEvaluator + { + private readonly EvalObserverNode _evalObserverNode; + private EventObserver _eventObserver; + + /// Constructor. + /// is the parent evaluator to call to indicate truth value + /// is the factory node associated to the state + public EvalObserverStateNode(Evaluator parentNode, EvalObserverNode evalObserverNode) + : base(parentNode) + { + + _evalObserverNode = evalObserverNode; + } + + public override void RemoveMatch(ISet matchEvent) + { + if (PatternConsumptionUtil.ContainsEvent(matchEvent, _eventObserver.BeginState)) + { + Quit(); + ParentEvaluator.EvaluateFalse(this, true); + } + } + + public override EvalNode FactoryNode + { + get { return _evalObserverNode; } + } + + public virtual PatternAgentInstanceContext Context + { + get { return _evalObserverNode.Context; } + } + + public void ObserverEvaluateTrue(MatchedEventMap matchEvent, bool quitted) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QPatternObserverEvaluateTrue(_evalObserverNode, matchEvent); } + ParentEvaluator.EvaluateTrue(matchEvent, this, quitted); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().APatternObserverEvaluateTrue(); } + } + + public void ObserverEvaluateFalse(bool restartable) + { + ParentEvaluator.EvaluateFalse(this, restartable); + } + + public override void Start(MatchedEventMap beginState) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QPatternObserverStart(_evalObserverNode, beginState); } + _eventObserver = _evalObserverNode.FactoryNode.ObserverFactory.MakeObserver(_evalObserverNode.Context, beginState, this, null, null, ParentEvaluator.IsFilterChildNonQuitting); + _eventObserver.StartObserve(); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().APatternObserverStart(); } + } + + public override void Quit() + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QPatternObserverQuit(_evalObserverNode); } + _eventObserver.StopObserve(); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().APatternObserverQuit(); } + } + + public override void Accept(EvalStateNodeVisitor visitor) + { + visitor.VisitObserver(_evalObserverNode.FactoryNode, this, _eventObserver); + } + + public override bool IsNotOperator + { + get { return false; } + } + + public override bool IsFilterStateNode + { + get { return false; } + } + + public override bool IsObserverStateNodeNonRestarting + { + get { return _evalObserverNode.FactoryNode.IsObserverStateNodeNonRestarting; } + } + + public override String ToString() + { + return "EvalObserverStateNode eventObserver=" + _eventObserver; + } + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/EvalOrFactoryNode.cs b/NEsper.Core/NEsper.Core/pattern/EvalOrFactoryNode.cs new file mode 100755 index 000000000..6cc46da46 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/EvalOrFactoryNode.cs @@ -0,0 +1,59 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.pattern +{ + /// + /// This class represents an 'or' operator in the evaluation tree representing any event expressions. + /// + public class EvalOrFactoryNode : EvalNodeFactoryBase + { + public override EvalNode MakeEvalNode(PatternAgentInstanceContext agentInstanceContext, EvalNode parentNode) + { + EvalNode[] children = EvalNodeUtil.MakeEvalNodeChildren(this.ChildNodes, agentInstanceContext, parentNode); + return new EvalOrNode(agentInstanceContext, this, children); + } + + public override String ToString() { + return "EvalOrNode children=" + this.ChildNodes.Count; + } + + public override bool IsFilterChildNonQuitting + { + get { return false; } + } + + public override bool IsStateful + { + get + { + foreach (EvalFactoryNode child in this.ChildNodes) + { + if (child.IsStateful) + { + return true; + } + } + return false; + } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + PatternExpressionUtil.ToPrecedenceFreeEPL(writer, "or", ChildNodes, Precedence); + } + + public override PatternExpressionPrecedenceEnum Precedence + { + get { return PatternExpressionPrecedenceEnum.OR; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/pattern/EvalOrNode.cs b/NEsper.Core/NEsper.Core/pattern/EvalOrNode.cs new file mode 100755 index 000000000..ab175692d --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/EvalOrNode.cs @@ -0,0 +1,47 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.compat.logging; + +namespace com.espertech.esper.pattern +{ + /// + /// This class represents an 'or' operator in the evaluation tree representing any event expressions. + /// + public class EvalOrNode : EvalNodeBase + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly EvalOrFactoryNode _factoryNode; + private readonly IList _childNodes; + + public EvalOrNode(PatternAgentInstanceContext context, EvalOrFactoryNode factoryNode, IList childNodes) + : base(context) + { + _factoryNode = factoryNode; + _childNodes = childNodes; + } + + public EvalOrFactoryNode FactoryNode + { + get { return _factoryNode; } + } + + public IList ChildNodes + { + get { return _childNodes; } + } + + public override EvalStateNode NewState(Evaluator parentNode, EvalStateNodeNumber stateNodeNumber, long stateNodeId) + { + return new EvalOrStateNode(parentNode, this); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/pattern/EvalOrStateNode.cs b/NEsper.Core/NEsper.Core/pattern/EvalOrStateNode.cs new file mode 100755 index 000000000..7418bc0f6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/EvalOrStateNode.cs @@ -0,0 +1,178 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.pattern +{ + /// + /// This class represents the state of a "or" operator in the evaluation state tree. + /// + public class EvalOrStateNode : EvalStateNode, Evaluator + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + private readonly EvalOrNode _evalOrNode; + private readonly EvalStateNode[] _childNodes; + + /// + /// Constructor. + /// + /// is the parent evaluator to call to indicate truth value + /// is the factory node associated to the state + public EvalOrStateNode(Evaluator parentNode, EvalOrNode evalOrNode) + : base(parentNode) + { + _childNodes = new EvalStateNode[evalOrNode.ChildNodes.Count]; + _evalOrNode = evalOrNode; + } + + public override void RemoveMatch(ISet matchEvent) + { + foreach (EvalStateNode node in _childNodes) + { + if (node != null) + { + node.RemoveMatch(matchEvent); + } + } + } + + public override EvalNode FactoryNode + { + get { return _evalOrNode; } + } + + public override void Start(MatchedEventMap beginState) + { + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().QPatternOrStart(_evalOrNode, beginState); + } + // In an "or" expression we need to create states for all child expressions/listeners, + // since all are going to be started + int count = 0; + foreach (EvalNode node in _evalOrNode.ChildNodes) { + EvalStateNode childState = node.NewState(this, null, 0L); + _childNodes[count++] = childState; + } + + // In an "or" expression we start all child listeners + var childNodeCopy = new EvalStateNode[_childNodes.Length]; + Array.Copy(_childNodes, 0, childNodeCopy, 0, _childNodes.Length); + foreach (EvalStateNode child in childNodeCopy) { + child.Start(beginState); + } + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().APatternOrStart(); + } + } + + public void EvaluateTrue(MatchedEventMap matchEvent, EvalStateNode fromNode, bool isQuitted) { + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().QPatternOrEvaluateTrue(_evalOrNode, matchEvent); + } + // If one of the children quits, the whole or expression turns true and all subexpressions must quit + if (isQuitted) { + for (int i = 0; i < _childNodes.Length; i++) { + if (_childNodes[i] == fromNode) { + _childNodes[i] = null; + } + } + QuitInternal(); // Quit the remaining listeners + } + + this.ParentEvaluator.EvaluateTrue(matchEvent, this, isQuitted); + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().APatternOrEvaluateTrue(isQuitted); + } + } + + public void EvaluateFalse(EvalStateNode fromNode, bool restartable) { + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().QPatternOrEvalFalse(_evalOrNode); + } + for (int i = 0; i < _childNodes.Length; i++) { + if (_childNodes[i] == fromNode) { + _childNodes[i] = null; + } + } + + bool allEmpty = true; + for (int i = 0; i < _childNodes.Length; i++) { + if (_childNodes[i] != null) { + allEmpty = false; + break; + } + } + + if (allEmpty) { + this.ParentEvaluator.EvaluateFalse(this, true); + } + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().APatternOrEvalFalse(); + } + } + + public override void Quit() { + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().QPatternOrQuit(_evalOrNode); + } + QuitInternal(); + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().APatternOrQuit(); + } + } + + public override void Accept(EvalStateNodeVisitor visitor) { + visitor.VisitOr(_evalOrNode.FactoryNode, this); + foreach (EvalStateNode node in _childNodes) { + if (node != null) { + node.Accept(visitor); + } + } + } + + public override bool IsNotOperator + { + get { return false; } + } + + public override bool IsFilterStateNode + { + get { return false; } + } + + public bool IsFilterChildNonQuitting + { + get { return false; } + } + + public override bool IsObserverStateNodeNonRestarting + { + get { return false; } + } + + public override String ToString() { + return "EvalOrStateNode"; + } + + private void QuitInternal() { + foreach (EvalStateNode child in _childNodes) { + if (child != null) { + child.Quit(); + } + } + CompatExtensions.Fill(_childNodes, (EvalStateNode) null); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/pattern/EvalRootFactoryNode.cs b/NEsper.Core/NEsper.Core/pattern/EvalRootFactoryNode.cs new file mode 100755 index 000000000..3b5ef010c --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/EvalRootFactoryNode.cs @@ -0,0 +1,94 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.pattern +{ + /// + /// This class is always the root node in the evaluation tree representing an event expression. + /// It hold the handle to the EPStatement implementation for notifying when matches are found. + /// + public class EvalRootFactoryNode : EvalNodeFactoryBase + { + public readonly int numTreeChildNodes; + + public EvalRootFactoryNode(EvalFactoryNode childNode) { + AddChildNode(childNode); + this.numTreeChildNodes = AssignFactoryNodeIds(); + } + + private static IList CollectFactories(EvalRootFactoryNode rootFactory) { + var factories = new List(8); + foreach (EvalFactoryNode factoryNode in rootFactory.ChildNodes) { + CollectFactoriesRecursive(factoryNode, factories); + } + return factories; + } + + private static void CollectFactoriesRecursive(EvalFactoryNode factoryNode, List factories) { + factories.Add(factoryNode); + foreach (EvalFactoryNode childNode in factoryNode.ChildNodes) { + CollectFactoriesRecursive(childNode, factories); + } + } + + public override EvalNode MakeEvalNode(PatternAgentInstanceContext agentInstanceContext, EvalNode parentNode) { + EvalNode child = EvalNodeUtil.MakeEvalNodeSingleChild(this.ChildNodes, agentInstanceContext, parentNode); + return new EvalRootNode(agentInstanceContext, this, child); + } + + public override String ToString() + { + return "EvalRootNode children=" + this.ChildNodes.Count; + } + + public override bool IsFilterChildNonQuitting + { + get { return false; } + } + + public override bool IsStateful + { + get { return this.ChildNodes[0].IsStateful; } + } + + public int NumTreeChildNodes + { + get { return numTreeChildNodes; } + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) { + if (!ChildNodes.IsEmpty()) { + ChildNodes[0].ToEPL(writer, Precedence); + } + } + + public override PatternExpressionPrecedenceEnum Precedence + { + get { return PatternExpressionPrecedenceEnum.MINIMUM; } + } + + // assign factory ids, a short-type number assigned once-per-statement to each pattern node + // return the count of all ids + private int AssignFactoryNodeIds() { + short count = 0; + FactoryNodeId = count; + IList factories = CollectFactories(this); + foreach (EvalFactoryNode factoryNode in factories) { + count++; + factoryNode.FactoryNodeId = count; + } + return count; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/pattern/EvalRootMatchRemover.cs b/NEsper.Core/NEsper.Core/pattern/EvalRootMatchRemover.cs new file mode 100755 index 000000000..ece81b62e --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/EvalRootMatchRemover.cs @@ -0,0 +1,22 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; + +namespace com.espertech.esper.pattern +{ + /// + /// Interface for a root pattern node for removing matches. + /// + public interface EvalRootMatchRemover + { + void RemoveMatch(ISet matchEvent); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/pattern/EvalRootNode.cs b/NEsper.Core/NEsper.Core/pattern/EvalRootNode.cs new file mode 100755 index 000000000..5bb2011cd --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/EvalRootNode.cs @@ -0,0 +1,77 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.pattern +{ + /// + /// This class is always the root node in the evaluation tree representing an event + /// expression. It hold the handle to the EPStatement implementation for notifying + /// when matches are found. + /// + public class EvalRootNode + : EvalNodeBase + , PatternStarter + { + private readonly EvalRootFactoryNode _factoryNode; + private readonly EvalNode _childNode; + + public EvalRootNode(PatternAgentInstanceContext context, EvalRootFactoryNode factoryNode, EvalNode childNode) + : base(context) + { + _factoryNode = factoryNode; + _childNode = childNode; + } + + public EvalNode ChildNode + { + get { return _childNode; } + } + + public EvalRootFactoryNode FactoryNode + { + get { return _factoryNode; } + } + + public PatternStopCallback Start(PatternMatchCallback callback, PatternContext context, bool isRecoveringResilient) + { + MatchedEventMap beginState = new MatchedEventMapImpl(context.MatchedEventMapMeta); + return StartInternal(callback, context, beginState, isRecoveringResilient); + } + + public EvalRootState Start(PatternMatchCallback callback, + PatternContext context, + MatchedEventMap beginState, + bool isRecoveringResilient) + { + return StartInternal(callback, context, beginState, isRecoveringResilient); + } + + protected EvalRootState StartInternal(PatternMatchCallback callback, + PatternContext context, + MatchedEventMap beginState, + bool isRecoveringResilient) + { + if (beginState == null) + { + throw new ArgumentException("No pattern begin-state has been provided"); + } + var rootStateNode = NewState(null, null, 0L); + var rootState = (EvalRootState)rootStateNode; + rootState.Callback = callback; + rootState.StartRecoverable(isRecoveringResilient, beginState); + return rootState; + } + + public override EvalStateNode NewState(Evaluator parentNode, EvalStateNodeNumber stateNodeNumber, long stateNodeId) + { + return new EvalRootStateNode(_childNode); + } + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/EvalRootState.cs b/NEsper.Core/NEsper.Core/pattern/EvalRootState.cs new file mode 100755 index 000000000..4b0d6b24e --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/EvalRootState.cs @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; + +namespace com.espertech.esper.pattern +{ + /// + /// Interface for a root state node accepting a callback to use to indicate pattern results. + /// + public interface EvalRootState : PatternStopCallback, EvalRootMatchRemover + { + /// Accept callback to indicate pattern results. + /// is a pattern result call + PatternMatchCallback Callback { set; } + + void StartRecoverable(bool startRecoverable, MatchedEventMap beginState); + + void Accept(EvalStateNodeVisitor visitor); + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/EvalRootStateNode.cs b/NEsper.Core/NEsper.Core/pattern/EvalRootStateNode.cs new file mode 100755 index 000000000..30fc3bfce --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/EvalRootStateNode.cs @@ -0,0 +1,168 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.pattern +{ + /// + /// This class is always the root node in the evaluation state tree representing any + /// activated event expression. It hold the handle to a further state node with subnodes + /// making up a whole evaluation state tree. + /// + [Serializable] + public class EvalRootStateNode : EvalStateNode, Evaluator, PatternStopCallback, EvalRootState + { + protected EvalNode RootSingleChildNode; + private EvalStateNode _topStateNode; + private PatternMatchCallback _callback; + + /// Constructor. + /// is the root nodes single child node + public EvalRootStateNode(EvalNode rootSingleChildNode) + : base(null) + { + RootSingleChildNode = rootSingleChildNode; + } + + public override EvalNode FactoryNode + { + get { return RootSingleChildNode; } + } + + /// Hands the callback to use to indicate matching events. + /// is invoked when the event expressions turns true. + public PatternMatchCallback Callback + { + set { _callback = value; } + } + + public void StartRecoverable(bool startRecoverable, MatchedEventMap beginState) + { + Start(beginState); + } + + public override void Start(MatchedEventMap beginState) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QPatternRootStart(beginState); } + _topStateNode = RootSingleChildNode.NewState(this, null, 0L); + _topStateNode.Start(beginState); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().APatternRootStart(); } + } + + public void Stop() + { + Quit(); + } + + public override void Quit() + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QPatternRootQuit(); } + if (_topStateNode != null) + { + _topStateNode.Quit(); + HandleQuitEvent(); + } + _topStateNode = null; + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().APatternRootQuit(); } + } + + public void HandleQuitEvent() + { + // no action + } + + public void HandleChildQuitEvent() + { + // no action + } + + public void HandleEvaluateFalseEvent() + { + // no action + } + + public void EvaluateTrue(MatchedEventMap matchEvent, EvalStateNode fromNode, bool isQuitted) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QPatternRootEvaluateTrue(matchEvent); } + + if (isQuitted) + { + _topStateNode = null; + HandleChildQuitEvent(); + } + + _callback.Invoke(matchEvent.MatchingEventsAsMap); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().APatternRootEvaluateTrue(_topStateNode == null); } + } + + public void EvaluateFalse(EvalStateNode fromNode, bool restartable) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QPatternRootEvalFalse(); } + if (_topStateNode != null) + { + _topStateNode.Quit(); + _topStateNode = null; + HandleEvaluateFalseEvent(); + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().APatternRootEvalFalse(); } + } + + public override void Accept(EvalStateNodeVisitor visitor) + { + visitor.VisitRoot(this); + if (_topStateNode != null) + { + _topStateNode.Accept(visitor); + } + } + + public override bool IsFilterStateNode + { + get { return false; } + } + + public override bool IsNotOperator + { + get { return false; } + } + + public bool IsFilterChildNonQuitting + { + get { return false; } + } + + public override bool IsObserverStateNodeNonRestarting + { + get { return false; } + } + + public override String ToString() + { + return "EvalRootStateNode topStateNode=" + _topStateNode; + } + + public EvalStateNode TopStateNode + { + get { return _topStateNode; } + protected set { _topStateNode = value; } + } + + public override void RemoveMatch(ISet matchEvent) + { + if (_topStateNode != null) + { + _topStateNode.RemoveMatch(matchEvent); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/EvalStateNode.cs b/NEsper.Core/NEsper.Core/pattern/EvalStateNode.cs new file mode 100755 index 000000000..b776eaa6c --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/EvalStateNode.cs @@ -0,0 +1,69 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; + +namespace com.espertech.esper.pattern +{ + /// + /// Superclass of all state nodes in an evaluation node tree representing an event expressions. + /// Follows the Composite pattern. Subclasses are expected to keep their own collection containing + /// child nodes as needed. + /// + public abstract class EvalStateNode + { + /// + /// Starts the event expression or an instance of it. Child classes are expected to initialize + /// and start any event listeners or schedule any time-based callbacks as needed. + /// + /// State of the begin. + public abstract void Start(MatchedEventMap beginState); + + /// + /// Stops the event expression or an instance of it. Child classes are expected to free resources + /// and stop any event listeners or remove any time-based callbacks. + /// + public abstract void Quit(); + + /// + /// Accept a visitor. Child classes are expected to invoke the visit method on the visitor instance + /// passed in. + /// + /// on which the visit method is invoked by each node + public abstract void Accept(EvalStateNodeVisitor visitor); + + /// + /// Returns the factory node for the state node. + /// + /// factory node + public abstract EvalNode FactoryNode { get; } + + public abstract bool IsNotOperator { get; } + + public abstract bool IsFilterStateNode { get; } + + public abstract bool IsObserverStateNodeNonRestarting { get; } + + /// Remove matches that overlap with the provided events. + /// set of events to check for + public abstract void RemoveMatch(ISet matchEvent); + + /// Constructor. + /// is the evaluator for this node on which to indicate a change in truth value + protected EvalStateNode(Evaluator parentNode) + { + ParentEvaluator = parentNode; + } + + /// Returns the parent evaluator. + /// parent evaluator instance + public Evaluator ParentEvaluator { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/EvalStateNodeNumber.cs b/NEsper.Core/NEsper.Core/pattern/EvalStateNodeNumber.cs new file mode 100755 index 000000000..c17e1bc83 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/EvalStateNodeNumber.cs @@ -0,0 +1,113 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.pattern +{ + [Serializable] + public class EvalStateNodeNumber + { + private readonly int[] _stateNumber; + private int _hashCode; + + /// Ctor - constructs a top-level node number. + public EvalStateNodeNumber() + { + _stateNumber = new int[0]; + ComputeHashCode(); + } + + /// Contructs a given node number. + /// to contruct + public EvalStateNodeNumber(int[] number) + { + _stateNumber = number; + ComputeHashCode(); + } + + /// Get the depth of the node number. + /// ordinal + public int OrdinalNumber + { + get { return _stateNumber[_stateNumber.Length - 1]; } + } + + /// Generates a new child node number to the current node number with the given child id. + /// child's node num + /// child node num + public EvalStateNodeNumber NewChildNum(int newStateNumber) + { + int[] num = new int[_stateNumber.Length + 1]; + Array.Copy(_stateNumber, 0, num, 0, _stateNumber.Length); + num[_stateNumber.Length] = newStateNumber; + return new EvalStateNodeNumber(num); + } + + /// Generates a new sibling node number to the current node. + /// sibling node + public EvalStateNodeNumber NewSiblingState() + { + int size = _stateNumber.Length; + int[] num = new int[size]; + Array.Copy(_stateNumber, 0, num, 0, size); + num[size - 1] = _stateNumber[size - 1] + 1; + return new EvalStateNodeNumber(num); + } + + public override String ToString() + { + return _stateNumber.Render(); + } + + /// Returns the internal number representation. + /// state number + public int[] StateNumber + { + get { return _stateNumber; } + } + + public override int GetHashCode() + { + return _hashCode; + } + + public override bool Equals(Object otherObj) + { + if (!(otherObj is EvalStateNodeNumber)) + { + return false; + } + + EvalStateNodeNumber other = (EvalStateNodeNumber) otherObj; + int[] otherNum = other.StateNumber; + if (otherNum.Length != _stateNumber.Length) + { + return false; + } + for (int i = 0; i < _stateNumber.Length; i++) + { + if (otherNum[i] != _stateNumber[i]) + { + return false; + } + } + return true; + } + + private void ComputeHashCode() + { + _hashCode = 7; + for (int i = 0; i < _stateNumber.Length; i++) + { + _hashCode ^= _stateNumber[i]; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/EvalStateNodeVisitor.cs b/NEsper.Core/NEsper.Core/pattern/EvalStateNodeVisitor.cs new file mode 100755 index 000000000..d1ff61126 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/EvalStateNodeVisitor.cs @@ -0,0 +1,35 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.core.service; +using com.espertech.esper.pattern.guard; +using com.espertech.esper.pattern.observer; + +namespace com.espertech.esper.pattern +{ + /// + /// Interface for visiting each element in the evaluation node tree for an event expression (see Visitor pattern). + /// + public interface EvalStateNodeVisitor + { + void VisitGuard(EvalGuardFactoryNode factoryNode, EvalStateNode stateNode, Guard guard); + void VisitFollowedBy(EvalFollowedByFactoryNode factoryNode, EvalStateNode stateNode, params object[] stateFlat); + void VisitFilter(EvalFilterFactoryNode factoryNode, EvalStateNode stateNode, EPStatementHandleCallback handle, MatchedEventMap beginState); + void VisitMatchUntil(EvalMatchUntilFactoryNode factoryNode, EvalStateNode stateNode, params object[] stateDeep); + void VisitObserver(EvalObserverFactoryNode factoryNode, EvalStateNode stateNode, EventObserver eventObserver); + void VisitNot(EvalNotFactoryNode factoryNode, EvalStateNode stateNode); + void VisitOr(EvalOrFactoryNode factoryNode, EvalStateNode stateNode); + void VisitRoot(EvalStateNode stateNode); + void VisitAnd(EvalAndFactoryNode factoryNode, EvalStateNode stateNode, params object[] stateDeep); + void VisitEvery(EvalEveryFactoryNode factoryNode, EvalStateNode stateNode, MatchedEventMap beginState, params object[]stateFlat); + void VisitEveryDistinct(EvalEveryDistinctFactoryNode factoryNode, EvalStateNode stateNode, MatchedEventMap beginState, IEnumerable keySetCollection); + void VisitAudit(); + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/Evaluator.cs b/NEsper.Core/NEsper.Core/pattern/Evaluator.cs new file mode 100755 index 000000000..ee6e94e8b --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/Evaluator.cs @@ -0,0 +1,35 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.pattern +{ + /// + /// Interface for nodes in an expression evaluation state tree that are being informed by a child that the + /// event expression fragments (subtrees) which the child represents has turned true (evaluateTrue method) + /// or false (evaluateFalse). + /// + public interface Evaluator + { + /// + /// Indicate a change in truth value to true. + /// + /// is the container for events that caused the change in truth value + /// is the node that indicates the change + /// is an indication of whether the node continues listenening or stops listening + void EvaluateTrue(MatchedEventMap matchEvent, EvalStateNode fromNode, bool isQuitted); + + /// + /// Indicate a change in truth value to false. + /// + /// is the node that indicates the change + /// if set to true [restartable]. + void EvaluateFalse(EvalStateNode fromNode, bool restartable); + + bool IsFilterChildNonQuitting { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/MatchedEventConvertor.cs b/NEsper.Core/NEsper.Core/pattern/MatchedEventConvertor.cs new file mode 100755 index 000000000..8ba32e6aa --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/MatchedEventConvertor.cs @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; + +namespace com.espertech.esper.pattern +{ + /// + /// Converts from a map of prior matching events to a events per stream for resolution + /// by expressions. + /// + public interface MatchedEventConvertor + { + /// + /// Converts pattern matching events to events per stream. + /// + /// pattern partial matches + /// events per stream + EventBean[] Convert(MatchedEventMap events); + + MatchedEventMapMeta MatchedEventMapMeta { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/MatchedEventConvertorImpl.cs b/NEsper.Core/NEsper.Core/pattern/MatchedEventConvertorImpl.cs new file mode 100755 index 000000000..75ee77426 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/MatchedEventConvertorImpl.cs @@ -0,0 +1,90 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.events; +using com.espertech.esper.events.map; + +namespace com.espertech.esper.pattern +{ + /// + /// Implements a convertor for pattern partial results to events per stream. + /// + public class MatchedEventConvertorImpl + : MatchedEventConvertor + { + private readonly IDictionary> _arrayEventTypes; + private readonly EventBean[] _eventsPerStream; + private readonly IDictionary> _filterTypes; + private readonly MatchedEventMapMeta _matchedEventMapMeta; + + public MatchedEventMapMeta MatchedEventMapMeta + { + get { return _matchedEventMapMeta; } + } + + /// + /// Ctor. + /// + /// the filter one-event types + /// the filter many-event types + /// All tags. + /// for creating wrappers if required + public MatchedEventConvertorImpl( + ICollection>> filterTypes, + ICollection>> arrayEventTypes, + IEnumerable allTags, + EventAdapterService eventAdapterService) + { + int size = filterTypes.Count; + if (arrayEventTypes != null) + { + size += arrayEventTypes.Count; + } + + _eventsPerStream = new EventBean[size]; + _filterTypes = new LinkedHashMap>(); + _filterTypes.PutAll(filterTypes); + _arrayEventTypes = new LinkedHashMap>(); + if (arrayEventTypes != null) + { + _arrayEventTypes.PutAll(arrayEventTypes); + } + + _matchedEventMapMeta = new MatchedEventMapMeta(allTags.ToArray(), _arrayEventTypes.IsNotEmpty()); + } + + public EventBean[] Convert(MatchedEventMap events) + { + int count = 0; + foreach (var entry in _filterTypes) + { + EventBean theEvent = events.GetMatchingEventByTag(entry.Key); + _eventsPerStream[count++] = theEvent; + } + if (_arrayEventTypes != null) + { + foreach (var entry in _arrayEventTypes) + { + var eventArray = (EventBean[])events.GetMatchingEventAsObjectByTag(entry.Key); + var map = new Dictionary(); + map.Put(entry.Key, eventArray); + EventBean theEvent = new MapEventBean(map, null); + _eventsPerStream[count++] = theEvent; + } + } + return _eventsPerStream; + } + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/MatchedEventMap.cs b/NEsper.Core/NEsper.Core/pattern/MatchedEventMap.cs new file mode 100755 index 000000000..ae0372866 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/MatchedEventMap.cs @@ -0,0 +1,69 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using com.espertech.esper.client; + +namespace com.espertech.esper.pattern +{ + /// + /// Collection for internal use similar to the MatchedEventMap class in the client + /// package that holds the one or more events that could match any defined event + /// expressions. The optional tag value supplied when an event expression is created + /// is used as a key for placing matching event objects into this collection. + /// + public interface MatchedEventMap + { + /// Add an event to the collection identified by the given tag. + /// is an identifier to retrieve the event from + /// is the event object or array of event object to be added + void Add(int tag, object theEvent); + + /// + /// Returns a map containing the events where the key is the event tag string and + /// the value is the event instance. + /// + /// Map containing event instances + object[] MatchingEvents { get; } + + /// + /// Returns a single event instance given the tag identifier, or null if + /// the tag could not be located. + /// + /// is the identifier to look for + /// event instances for the tag + EventBean GetMatchingEvent(int tag); + + /// + /// Returns the object for the matching event, be it the event bean array or the event bean. + /// + /// is the tag to return the object for + /// event bean or event bean array + Object GetMatchingEventAsObject(int tag); + + /// Make a shallow copy of this collection. + /// shallow copy + MatchedEventMap ShallowCopy(); + + /// + /// Merge the state of an other match event structure into this one by adding + /// all entries within the MatchedEventMap to this match event. + /// + /// is the other instance to merge in. + void Merge(MatchedEventMap other); + + IDictionary MatchingEventsAsMap { get; } + + EventBean GetMatchingEventByTag(String resultEventAsName); + + Object GetMatchingEventAsObjectByTag(String key); + + MatchedEventMapMeta Meta { get; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/pattern/MatchedEventMapImpl.cs b/NEsper.Core/NEsper.Core/pattern/MatchedEventMapImpl.cs new file mode 100755 index 000000000..fb1e96e28 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/MatchedEventMapImpl.cs @@ -0,0 +1,166 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Text; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.pattern +{ + /// + /// Collection for internal use similar to the MatchedEventMap class in the client + /// package that holds the one or more events that could match any defined event expressions. + /// The optional tag value supplied when an event expression is created is used as a key for + /// placing matching event objects into this collection. + /// + public sealed class MatchedEventMapImpl : MatchedEventMap + { + private readonly Object[] _matches; + + /// Constructor creates an empty collection of events. + /// metadata + public MatchedEventMapImpl(MatchedEventMapMeta meta) + { + Meta = meta; + _matches = new Object[meta.TagsPerIndex.Length]; + } + + public MatchedEventMapImpl(MatchedEventMapMeta meta, Object[] matches) + { + Meta = meta; + _matches = matches; + } + + /// Add an event to the collection identified by the given tag. + /// is an identifier to retrieve the event from + /// is the event object or array of event object to be added + public void Add(int tag, Object theEvent) + { + _matches[tag] = theEvent; + } + + /// Returns a map containing the events where the key is the event tag string and the value is the event instance. + /// Hashtable containing event instances + public object[] MatchingEvents + { + get { return _matches; } + } + + /// Returns a single event instance given the tag identifier, or null if the tag could not be located. + /// is the identifier to look for + /// event instances for the tag + public EventBean GetMatchingEvent(int tag) + { + return (EventBean)_matches[tag]; + } + + public Object GetMatchingEventAsObject(int tag) + { + return _matches[tag]; + } + + public override String ToString() + { + var buffer = new StringBuilder(); + var count = 0; + + for (int i = 0; i < _matches.Length; i++) + { + buffer.Append(" ("); + buffer.Append(count++); + buffer.Append(") "); + buffer.Append("tag="); + buffer.Append(Meta.TagsPerIndex[i]); + buffer.Append(" event="); + buffer.Append(_matches[i]); + } + + return buffer.ToString(); + } + + /// Make a shallow copy of this collection. + /// shallow copy + public MatchedEventMap ShallowCopy() + { + if (_matches.Length == 0) + { + return this; + } + + var copy = new Object[_matches.Length]; + if (_matches.Length > 1) + { + Array.Copy(_matches, 0, copy, 0, _matches.Length); + } + else + { + copy[0] = _matches[0]; + } + return new MatchedEventMapImpl(Meta, copy); + } + + /// Merge the state of an other match event structure into this one by adding all entries within the MatchedEventMap to this match event. + /// is the other instance to merge in. + public void Merge(MatchedEventMap other) + { + if (!(other is MatchedEventMapImpl)) + { + throw new UnsupportedOperationException("Merge requires same types"); + } + + var otherImpl = (MatchedEventMapImpl)other; + for (int i = 0; i < _matches.Length; i++) + { + if (otherImpl._matches[i] == null) + { + continue; + } + _matches[i] = otherImpl._matches[i]; + } + } + + public IDictionary MatchingEventsAsMap + { + get + { + IDictionary map = new Dictionary(); + for (int i = 0; i < Meta.TagsPerIndex.Length; i++) + { + if (_matches[i] == null) + { + continue; + } + map.Put(Meta.TagsPerIndex[i], _matches[i]); + } + return map; + } + } + + public MatchedEventMapMeta Meta { get; private set; } + + public EventBean GetMatchingEventByTag(String resultEventAsName) + { + Object obj = GetMatchingEventAsObjectByTag(resultEventAsName); + return (EventBean)obj; + } + + public Object GetMatchingEventAsObjectByTag(String key) + { + int index = Meta.GetTagFor(key); + if (index == -1) + { + return null; + } + return _matches[index]; + } + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/MatchedEventMapMeta.cs b/NEsper.Core/NEsper.Core/pattern/MatchedEventMapMeta.cs new file mode 100755 index 000000000..978e67c4c --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/MatchedEventMapMeta.cs @@ -0,0 +1,80 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.pattern +{ + public sealed class MatchedEventMapMeta + { + private readonly static int MIN_MAP_LOOKUP = 3; + + private readonly String[] _tagsPerIndex; + private readonly bool _hasArrayProperties; + private readonly IDictionary _tagsPerIndexMap; + + public MatchedEventMapMeta(String[] tagsPerIndex, bool hasArrayProperties) + { + _tagsPerIndex = tagsPerIndex; + _hasArrayProperties = hasArrayProperties; + _tagsPerIndexMap = GetMap(tagsPerIndex); + } + + public MatchedEventMapMeta(ICollection allTags, bool hasArrayProperties) + { + _tagsPerIndex = allTags.ToArray(); + _hasArrayProperties = hasArrayProperties; + _tagsPerIndexMap = GetMap(_tagsPerIndex); + } + + public string[] TagsPerIndex + { + get { return _tagsPerIndex; } + } + + public int GetTagFor(String key) + { + if (_tagsPerIndexMap != null) + { + return _tagsPerIndexMap.Get(key, -1); + } + for (int i = 0; i < _tagsPerIndex.Length; i++) + { + if (_tagsPerIndex[i].Equals(key)) + { + return i; + } + } + return -1; + } + + private IDictionary GetMap(String[] tagsPerIndex) + { + if (tagsPerIndex.Length < MIN_MAP_LOOKUP) + { + return null; + } + + var map = new Dictionary(); + for (int i = 0; i < tagsPerIndex.Length; i++) + { + map.Put(tagsPerIndex[i], i); + } + return map; + } + + public bool IsHasArrayProperties() + { + return _hasArrayProperties; + } + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/PatternAgentInstanceContext.cs b/NEsper.Core/NEsper.Core/pattern/PatternAgentInstanceContext.cs new file mode 100755 index 000000000..c3b94910e --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/PatternAgentInstanceContext.cs @@ -0,0 +1,36 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; + +namespace com.espertech.esper.pattern +{ + /// + /// Contains handles to implementations of services needed by evaluation nodes. + /// + public class PatternAgentInstanceContext + { + public PatternAgentInstanceContext(PatternContext patternContext, AgentInstanceContext agentInstanceContext, bool hasConsumingFilter) { + PatternContext = patternContext; + AgentInstanceContext = agentInstanceContext; + ConsumptionHandler = hasConsumingFilter ? new EvalFilterConsumptionHandler() : null; + } + + public PatternContext PatternContext { get; private set; } + + public AgentInstanceContext AgentInstanceContext { get; private set; } + + public EvalFilterConsumptionHandler ConsumptionHandler { get; private set; } + + public StatementContext StatementContext + { + get { return AgentInstanceContext.StatementContext; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/PatternConsumptionUtil.cs b/NEsper.Core/NEsper.Core/pattern/PatternConsumptionUtil.cs new file mode 100755 index 000000000..07c956e79 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/PatternConsumptionUtil.cs @@ -0,0 +1,61 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; + +namespace com.espertech.esper.pattern +{ + public class PatternConsumptionUtil + { + public static bool ContainsEvent(ICollection matchEvent, MatchedEventMap beginState) + { + if (beginState == null) { + return false; + } + var partial = beginState.MatchingEvents; + var quit = false; + foreach (var aPartial in partial) { + if (aPartial == null) { + continue; + } + else if (aPartial is EventBean) { + if (matchEvent.Contains(aPartial)) { + quit = true; + break; + } + } + else if (aPartial is EventBean[]) { + var events = (EventBean[]) aPartial; + foreach (var @event in events) { + if (matchEvent.Contains(@event)) { + quit = true; + break; + } + } + } + if (quit) { + break; + } + } + return quit; + } + + public static void ChildNodeRemoveMatches(ISet matchEvent, ICollection evalStateNodes) + where TK : EvalStateNode + { + var nodesArray = evalStateNodes.ToArray(); + foreach (var child in nodesArray) + { + child.RemoveMatch(matchEvent); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/PatternContext.cs b/NEsper.Core/NEsper.Core/pattern/PatternContext.cs new file mode 100755 index 000000000..783be539d --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/PatternContext.cs @@ -0,0 +1,163 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.service; +using com.espertech.esper.epl.variable; +using com.espertech.esper.events; +using com.espertech.esper.filter; +using com.espertech.esper.schedule; + +namespace com.espertech.esper.pattern +{ + /// + /// Contains handles to implementations of services needed by evaluation nodes. + /// + public class PatternContext + { + private readonly int _streamNumber; + private readonly StatementContext _statementContext; + private readonly MatchedEventMapMeta _matchedEventMapMeta; + private readonly bool _isResilient; + + public PatternContext( + StatementContext statementContext, + int streamNumber, + MatchedEventMapMeta matchedEventMapMeta, + bool isResilient) + { + _streamNumber = streamNumber; + _statementContext = statementContext; + _matchedEventMapMeta = matchedEventMapMeta; + _isResilient = isResilient; + } + + /// + /// Returns service to use for filter evaluation. + /// + /// filter evaluation service implemetation + public FilterService FilterService + { + get { return _statementContext.FilterService; } + } + + /// + /// Returns service to use for schedule evaluation. + /// + /// schedule evaluation service implemetation + public SchedulingService SchedulingService + { + get { return _statementContext.SchedulingService; } + } + + /// + /// Returns the schedule bucket for ordering schedule callbacks within this pattern. + /// + /// schedule bucket + public ScheduleBucket ScheduleBucket + { + get { return _statementContext.ScheduleBucket; } + } + + /// + /// Returns teh service providing event adaptering or wrapping. + /// + /// event adapter service + public EventAdapterService EventAdapterService + { + get { return _statementContext.EventAdapterService; } + } + + /// + /// Returns the statement's resource handle for locking. + /// + /// handle of statement + public EPStatementHandle EpStatementHandle + { + get { return _statementContext.EpStatementHandle; } + } + + /// + /// Returns the statement id. + /// + /// statement id + public int StatementId + { + get { return _statementContext.StatementId; } + } + + /// + /// Returns the statement name. + /// + /// statement name + public string StatementName + { + get { return _statementContext.StatementName; } + } + + /// + /// Returns the stream number. + /// + /// stream number + public int StreamNumber + { + get { return _streamNumber; } + } + + /// + /// Returns the engine URI. + /// + /// engine URI + public string EngineURI + { + get { return _statementContext.EngineURI; } + } + + /// + /// Returns extension services context for statement (statement-specific). + /// + /// extension services + public StatementExtensionSvcContext StatementExtensionServicesContext + { + get { return _statementContext.StatementExtensionServicesContext; } + } + + /// + /// Returns the variable service. + /// + /// variable service + public VariableService VariableService + { + get { return _statementContext.VariableService; } + } + + public TimeProvider TimeProvider + { + get { return _statementContext.TimeProvider; } + } + + public ExceptionHandlingService ExceptionHandlingService + { + get { return _statementContext.ExceptionHandlingService; } + } + + public StatementContext StatementContext + { + get { return _statementContext; } + } + + public MatchedEventMapMeta MatchedEventMapMeta + { + get { return _matchedEventMapMeta; } + } + + public bool IsResilient + { + get { return _isResilient; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/pattern/PatternContextFactory.cs b/NEsper.Core/NEsper.Core/pattern/PatternContextFactory.cs new file mode 100755 index 000000000..cdafd2139 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/PatternContextFactory.cs @@ -0,0 +1,39 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; + +namespace com.espertech.esper.pattern +{ + /// + /// Factory for pattern context instances, creating context objects for each distinct + /// pattern based on the patterns root node and stream id. + /// + public interface PatternContextFactory + { + /// + /// Create a pattern context. + /// + /// is the statement information and services + /// is the stream id + /// is the pattern root node + /// The matched event map meta. + /// if set to true [allow resilient]. + /// pattern context + PatternContext CreateContext(StatementContext statementContext, + int streamId, + EvalRootFactoryNode rootNode, + MatchedEventMapMeta matchedEventMapMeta, + bool allowResilient); + + PatternAgentInstanceContext CreatePatternAgentContext(PatternContext patternContext, + AgentInstanceContext agentInstanceContext, + bool hasConsumingFilter); + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/PatternContextFactoryDefault.cs b/NEsper.Core/NEsper.Core/pattern/PatternContextFactoryDefault.cs new file mode 100755 index 000000000..1956c8ffb --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/PatternContextFactoryDefault.cs @@ -0,0 +1,42 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; + +namespace com.espertech.esper.pattern +{ + /// + /// Default pattern context factory. + /// + public class PatternContextFactoryDefault : PatternContextFactory + { + /// + /// Create a pattern context. + /// + /// is the statement information and services + /// is the stream id + /// is the pattern root node + /// + /// + /// pattern context + public PatternContext CreateContext(StatementContext statementContext, + int streamId, + EvalRootFactoryNode rootNode, + MatchedEventMapMeta matchedEventMapMeta, + bool allowResilient) + { + return new PatternContext(statementContext, streamId, matchedEventMapMeta, false); + } + + public PatternAgentInstanceContext CreatePatternAgentContext(PatternContext patternContext, AgentInstanceContext agentInstanceContext, bool hasConsumingFilter) + { + return new PatternAgentInstanceContext(patternContext, agentInstanceContext, hasConsumingFilter); + } + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/PatternExpressionPrecedenceEnum.cs b/NEsper.Core/NEsper.Core/pattern/PatternExpressionPrecedenceEnum.cs new file mode 100755 index 000000000..efdf52eaa --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/PatternExpressionPrecedenceEnum.cs @@ -0,0 +1,67 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.pattern +{ + public enum PatternExpressionPrecedenceEnum + { + /// Precedence. + MINIMUM, + + /// Precedence. + FOLLOWEDBY, + + /// Precedence. + OR, + + /// Precedence. + AND, + + /// Precedence. + REPEAT_UNTIL, + + /// Precedence. + UNARY, + + /// Precedence. + GUARD_POSTFIX, + + /// Precedence. + ATOM + } + + public static class PatternExpressionPrecedenceEnumExtensions + { + public static int GetLevel(this PatternExpressionPrecedenceEnum value) + { + switch (value) + { + case PatternExpressionPrecedenceEnum.MINIMUM: + return int.MinValue; + case PatternExpressionPrecedenceEnum.FOLLOWEDBY: + return 1; + case PatternExpressionPrecedenceEnum.OR: + return 2; + case PatternExpressionPrecedenceEnum.AND: + return 3; + case PatternExpressionPrecedenceEnum.REPEAT_UNTIL: + return 4; + case PatternExpressionPrecedenceEnum.UNARY: + return 5; + case PatternExpressionPrecedenceEnum.GUARD_POSTFIX: + return 6; + case PatternExpressionPrecedenceEnum.ATOM: + return int.MaxValue; + } + + throw new ArgumentException("invalid value"); + } + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/PatternExpressionUtil.cs b/NEsper.Core/NEsper.Core/pattern/PatternExpressionUtil.cs new file mode 100755 index 000000000..e472c6d10 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/PatternExpressionUtil.cs @@ -0,0 +1,173 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.time; + +namespace com.espertech.esper.pattern +{ + /// Utility for evaluating pattern expressions. + public class PatternExpressionUtil + { + private static readonly ILog Log = + LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + public static Object GetKeys( + MatchedEventMap matchEvent, + MatchedEventConvertor convertor, + ExprEvaluator[] expressions, + AgentInstanceContext agentInstanceContext) + { + var eventsPerStream = convertor.Convert(matchEvent); + var evaluateParams = new EvaluateParams(eventsPerStream, true, agentInstanceContext); + if (expressions.Length == 1) + { + return expressions[0].Evaluate(evaluateParams); + } + + var keys = new Object[expressions.Length]; + for (int i = 0; i < keys.Length; i++) + { + keys[i] = expressions[i].Evaluate(evaluateParams); + } + return new MultiKeyUntyped(keys); + } + + /// + /// Ctor. + /// + /// is the pattern object name + /// the pattern begin state + /// object parameters + /// for converting to a event-per-stream view for use to evaluate expressions + /// expression evaluation context + /// if the evaluate failed + /// expression results + public static IList Evaluate( + string objectName, + MatchedEventMap beginState, + IList parameters, + MatchedEventConvertor convertor, + ExprEvaluatorContext exprEvaluatorContext) + { + var results = new List(); + int count = 0; + EventBean[] eventsPerStream = convertor.Convert(beginState); + foreach (ExprNode expr in parameters) + { + try + { + Object result = Evaluate(objectName, expr, eventsPerStream, exprEvaluatorContext); + results.Add(result); + count++; + } + catch (Exception ex) + { + string message = objectName + " invalid parameter in expression " + count; + if (!string.IsNullOrEmpty(ex.Message)) + { + message += ": " + ex.Message; + } + Log.Error(message, ex); + throw new EPException(message); + } + } + return results; + } + + /// + /// Evaluate the pattern expression. + /// + /// pattern object name + /// pattern state + /// to converting from pattern match to event-per-stream + /// expression evaluation context + /// time period + /// if the evaluation failed + /// evaluation result + public static Object EvaluateTimePeriod( + string objectName, + MatchedEventMap beginState, + ExprTimePeriod timePeriod, + MatchedEventConvertor convertor, + ExprEvaluatorContext exprEvaluatorContext) + { + EventBean[] eventsPerStream = convertor.Convert(beginState); + try + { + return timePeriod.EvaluateGetTimePeriod(new EvaluateParams(eventsPerStream, true, exprEvaluatorContext)); + } + catch (Exception ex) + { + throw HandleRuntimeEx(ex, objectName); + } + } + + public static Object Evaluate( + string objectName, + MatchedEventMap beginState, + ExprNode parameter, + MatchedEventConvertor convertor, + ExprEvaluatorContext exprEvaluatorContext) + { + EventBean[] eventsPerStream = convertor.Convert(beginState); + return Evaluate(objectName, parameter, eventsPerStream, exprEvaluatorContext); + } + + private static Object Evaluate( + string objectName, + ExprNode expression, + EventBean[] eventsPerStream, + ExprEvaluatorContext exprEvaluatorContext) + { + try + { + return expression.ExprEvaluator.Evaluate( + new EvaluateParams(eventsPerStream, true, exprEvaluatorContext)); + } + catch (Exception ex) + { + throw HandleRuntimeEx(ex, objectName); + } + } + + private static EPException HandleRuntimeEx(Exception ex, string objectName) + { + string message = objectName + " failed to evaluate expression"; + if (!string.IsNullOrEmpty(ex.Message)) + { + message += ": " + ex.Message; + } + Log.Error(message, ex); + throw new EPException(message); + } + + public static void ToPrecedenceFreeEPL( + TextWriter writer, + string delimiterText, + IList childNodes, + PatternExpressionPrecedenceEnum precedence) + { + string delimiter = ""; + foreach (EvalFactoryNode child in childNodes) + { + writer.Write(delimiter); + child.ToEPL(writer, precedence); + delimiter = " " + delimiterText + " "; + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/pattern/PatternLevelAnnotationFlags.cs b/NEsper.Core/NEsper.Core/pattern/PatternLevelAnnotationFlags.cs new file mode 100755 index 000000000..678df22ef --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/PatternLevelAnnotationFlags.cs @@ -0,0 +1,16 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.pattern +{ + public class PatternLevelAnnotationFlags + { + public bool IsSuppressSameEventMatches { get; set; } + public bool IsDiscardPartialsOnMatch { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/PatternLevelAnnotationUtil.cs b/NEsper.Core/NEsper.Core/pattern/PatternLevelAnnotationUtil.cs new file mode 100755 index 000000000..7f978de06 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/PatternLevelAnnotationUtil.cs @@ -0,0 +1,79 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Linq; + +using com.espertech.esper.client.soda; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.pattern +{ + public class PatternLevelAnnotationUtil + { + private const string DISCARDPARTIALSONMATCH = "DiscardPartialsOnMatch"; + private const string SUPPRESSOVERLAPPINGMATCHES = "SuppressOverlappingMatches"; + + public static AnnotationPart[] AnnotationsFromSpec(PatternStreamSpecRaw pattern) + { + Deque parts = null; + + if (pattern.IsDiscardPartialsOnMatch) + { + parts = new ArrayDeque(); + parts.Add(new AnnotationPart(DISCARDPARTIALSONMATCH)); + } + + if (pattern.IsSuppressSameEventMatches) + { + if (parts == null) + { + parts = new ArrayDeque(); + } + parts.Add(new AnnotationPart(SUPPRESSOVERLAPPINGMATCHES)); + } + + if (parts == null) + { + return null; + } + return parts.ToArray(); + } + + public static PatternLevelAnnotationFlags AnnotationsToSpec(AnnotationPart[] parts) + { + var flags = new PatternLevelAnnotationFlags(); + if (parts == null) + { + return flags; + } + foreach (AnnotationPart part in parts) + { + ValidateSetFlags(flags, part.Name); + } + return flags; + } + + public static void ValidateSetFlags(PatternLevelAnnotationFlags flags, string annotation) + { + if (string.Equals(annotation, DISCARDPARTIALSONMATCH, StringComparison.InvariantCultureIgnoreCase)) + { + flags.IsDiscardPartialsOnMatch = true; + } + else if (string.Equals(annotation, SUPPRESSOVERLAPPINGMATCHES, StringComparison.InvariantCultureIgnoreCase)) + { + flags.IsSuppressSameEventMatches = true; + } + else + { + throw new ArgumentException("Unrecognized pattern-level annotation '" + annotation + "'"); + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/pattern/PatternMatchCallback.cs b/NEsper.Core/NEsper.Core/pattern/PatternMatchCallback.cs new file mode 100755 index 000000000..23f0726bf --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/PatternMatchCallback.cs @@ -0,0 +1,21 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.pattern +{ + /// + /// Used for anything that requires to be informed of matching events which would be stored + /// in the MatchedEventMap structure passed to the implementation. + /// + + public delegate void PatternMatchCallback(IDictionary matchEvent); +} diff --git a/NEsper.Core/NEsper.Core/pattern/PatternNodeFactory.cs b/NEsper.Core/NEsper.Core/pattern/PatternNodeFactory.cs new file mode 100755 index 000000000..b26f2718d --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/PatternNodeFactory.cs @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.pattern +{ + public interface PatternNodeFactory + { + EvalFactoryNode MakeAndNode(); + EvalFactoryNode MakeEveryDistinctNode(IList expressions); + EvalFactoryNode MakeEveryNode(); + EvalFactoryNode MakeFilterNode(FilterSpecRaw filterSpecification,String eventAsName, int? consumptionLevel); + EvalFactoryNode MakeFollowedByNode(IList maxExpressions, bool hasEngineWideMax); + EvalFactoryNode MakeGuardNode(PatternGuardSpec patternGuardSpec); + EvalFactoryNode MakeMatchUntilNode(ExprNode lowerBounds, ExprNode upperBounds, ExprNode singleBounds); + EvalFactoryNode MakeNotNode(); + EvalFactoryNode MakeObserverNode(PatternObserverSpec patternObserverSpec); + EvalFactoryNode MakeOrNode(); + EvalRootFactoryNode MakeRootNode(EvalFactoryNode childNode); + EvalFactoryNode MakeAuditNode(bool auditPattern, bool auditPatternInstance, String expressionText, EvalAuditInstanceCount instanceCount, bool filterChildNonQuitting); + bool IsAuditSupported { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/PatternNodeFactoryImpl.cs b/NEsper.Core/NEsper.Core/pattern/PatternNodeFactoryImpl.cs new file mode 100755 index 000000000..b96e0bbca --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/PatternNodeFactoryImpl.cs @@ -0,0 +1,93 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.pattern +{ + public class PatternNodeFactoryImpl : PatternNodeFactory + { + public EvalFactoryNode MakeAndNode() + { + return new EvalAndFactoryNode(); + } + + public EvalFactoryNode MakeEveryDistinctNode(IList expressions) + { + return new EvalEveryDistinctFactoryNode(expressions); + } + + public EvalFactoryNode MakeEveryNode() + { + return new EvalEveryFactoryNode(); + } + + public EvalFactoryNode MakeFilterNode( + FilterSpecRaw filterSpecification, + String eventAsName, + int? consumptionLevel) + { + return new EvalFilterFactoryNode(filterSpecification, eventAsName, consumptionLevel); + } + + public EvalFactoryNode MakeFollowedByNode(IList maxExpressions, bool hasEngineWideMax) + { + return new EvalFollowedByFactoryNode(maxExpressions, hasEngineWideMax); + } + + public EvalFactoryNode MakeGuardNode(PatternGuardSpec patternGuardSpec) + { + return new EvalGuardFactoryNode(patternGuardSpec); + } + + public EvalFactoryNode MakeMatchUntilNode(ExprNode lowerBounds, ExprNode upperBounds, ExprNode singleBounds) + { + return new EvalMatchUntilFactoryNode(lowerBounds, upperBounds, singleBounds); + } + + public EvalFactoryNode MakeNotNode() + { + return new EvalNotFactoryNode(); + } + + public EvalFactoryNode MakeObserverNode(PatternObserverSpec patternObserverSpec) + { + return new EvalObserverFactoryNode(patternObserverSpec); + } + + public EvalFactoryNode MakeOrNode() + { + return new EvalOrFactoryNode(); + } + + public EvalRootFactoryNode MakeRootNode(EvalFactoryNode childNode) + { + return new EvalRootFactoryNode(childNode); + } + + public EvalFactoryNode MakeAuditNode( + bool auditPattern, + bool auditPatternInstance, + String expressionText, + EvalAuditInstanceCount instanceCount, + bool filterChildNonQuitting) + { + return new EvalAuditFactoryNode( + auditPattern, auditPatternInstance, expressionText, instanceCount, filterChildNonQuitting); + } + + public bool IsAuditSupported + { + get { return true; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/PatternObjectException.cs b/NEsper.Core/NEsper.Core/pattern/PatternObjectException.cs new file mode 100755 index 000000000..db5607bde --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/PatternObjectException.cs @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.pattern +{ + /// + /// This exception is thrown to indicate a problem with a view expression. + /// + public sealed class PatternObjectException : Exception + { + /// Constructor. + /// is the error message + public PatternObjectException(String message) + : base(message) + { + } + + /// Constructor for an inner exception and message. + /// is the error message + /// is the inner exception + public PatternObjectException(String message, Exception cause) + : base(message, cause) + { + } + + /// Constructor. + /// is the inner exception + public PatternObjectException(Exception cause) + : base("", cause) + { + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/pattern/PatternObjectHelper.cs b/NEsper.Core/NEsper.Core/pattern/PatternObjectHelper.cs new file mode 100755 index 000000000..930b73a6e --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/PatternObjectHelper.cs @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.compat; +using com.espertech.esper.epl.spec; +using com.espertech.esper.pattern.guard; +using com.espertech.esper.pattern.observer; + +namespace com.espertech.esper.pattern +{ + /// + /// Helper producing a repository of built-in pattern objects. + /// + public class PatternObjectHelper + { + private static PluggableObjectCollection _builtinPatternObjects; + + static PatternObjectHelper() + { + BuiltinPatternObjects = new PluggableObjectCollection(); + foreach (GuardEnum guardEnum in EnumHelper.GetValues()) + { + _builtinPatternObjects.AddObject( + guardEnum.GetNamespace(), + guardEnum.GetName(), + guardEnum.GetClazz(), + PluggableObjectType.PATTERN_GUARD); + } + + foreach (ObserverEnum observerEnum in EnumHelper.GetValues()) + { + _builtinPatternObjects.AddObject( + observerEnum.GetNamespace(), + observerEnum.GetName(), + observerEnum.GetImplementationType(), + PluggableObjectType.PATTERN_OBSERVER); + } + } + + /// Returns the built-in pattern objects. + /// collection of built-in pattern objects. + public static PluggableObjectCollection BuiltinPatternObjects + { + get { return _builtinPatternObjects; } + private set { _builtinPatternObjects = value; } + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/pattern/PatternObjectResolutionService.cs b/NEsper.Core/NEsper.Core/pattern/PatternObjectResolutionService.cs new file mode 100755 index 000000000..a4c3f4b7f --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/PatternObjectResolutionService.cs @@ -0,0 +1,36 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.spec; +using com.espertech.esper.pattern.guard; +using com.espertech.esper.pattern.observer; + +namespace com.espertech.esper.pattern +{ + /// + /// Factory service for resolving pattern objects such as guards and observers. + /// + public interface PatternObjectResolutionService + { + /// + /// Creates an observer factory considering configured plugged-in resources. + /// + /// is the namespace, name and parameters for the observer + /// observer factory + /// PatternObjectException if the observer cannot be resolved + ObserverFactory Create(PatternObserverSpec spec); + + /// + /// Creates a guard factory considering configured plugged-in resources. + /// + /// is the namespace, name and parameters for the guard + /// guard factory + /// PatternObjectException if the guard cannot be resolved + GuardFactory Create(PatternGuardSpec spec); + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/PatternObjectResolutionServiceImpl.cs b/NEsper.Core/NEsper.Core/pattern/PatternObjectResolutionServiceImpl.cs new file mode 100755 index 000000000..2dc2f0a6e --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/PatternObjectResolutionServiceImpl.cs @@ -0,0 +1,167 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.spec; +using com.espertech.esper.pattern.guard; +using com.espertech.esper.pattern.observer; +using com.espertech.esper.util; + +namespace com.espertech.esper.pattern +{ + /// + /// Resolves pattern object namespace and name to guard or observer factory class, using configuration. + /// + public class PatternObjectResolutionServiceImpl : PatternObjectResolutionService + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly PluggableObjectCollection _patternObjects; + + /// Ctor. + /// is the pattern plug-in objects configured + public PatternObjectResolutionServiceImpl(PluggableObjectCollection patternObjects) + { + _patternObjects = patternObjects; + } + + public ObserverFactory Create(PatternObserverSpec spec) + { + Object result = CreateFactory(spec, PluggableObjectType.PATTERN_OBSERVER); + ObserverFactory factory; + try + { + factory = (ObserverFactory)result; + + if (Log.IsDebugEnabled) + { + Log.Debug(".create Successfully instantiated observer"); + } + } + catch (InvalidCastException e) + { + String message = "Error casting observer factory instance to " + typeof(ObserverFactory).FullName + " interface for observer '" + spec.ObjectName + "'"; + throw new PatternObjectException(message, e); + } + return factory; + } + + public GuardFactory Create(PatternGuardSpec spec) + { + Object result = CreateFactory(spec, PluggableObjectType.PATTERN_GUARD); + GuardFactory factory; + try + { + factory = (GuardFactory)result; + + if (Log.IsDebugEnabled) + { + Log.Debug(".create Successfully instantiated guard"); + } + } + catch (InvalidCastException e) + { + String message = "Error casting guard factory instance to " + typeof(GuardFactory).FullName + " interface for guard '" + spec.ObjectName + "'"; + throw new PatternObjectException(message, e); + } + return factory; + } + + private Object CreateFactory(ObjectSpec spec, PluggableObjectType type) + { + if (Log.IsDebugEnabled) + { + Log.Debug(".create Creating factory, spec=" + spec); + } + + // Find the factory class for this pattern object + Type factoryClass = null; + + IDictionary> namespaceMap = _patternObjects.Pluggables.Get(spec.ObjectNamespace); + if (namespaceMap != null) + { + Pair pair = namespaceMap.Get(spec.ObjectName); + if (pair != null) + { + if (pair.Second.PluggableType == type) + { + factoryClass = pair.First; + } + else + { + // invalid type: expecting observer, got guard + if (type == PluggableObjectType.PATTERN_GUARD) + { + throw new PatternObjectException("Pattern observer function '" + spec.ObjectName + "' cannot be used as a pattern guard"); + } + else + { + throw new PatternObjectException("Pattern guard function '" + spec.ObjectName + "' cannot be used as a pattern observer"); + } + } + } + } + + if (factoryClass == null) + { + if (type == PluggableObjectType.PATTERN_GUARD) + { + String message = "Pattern guard name '" + spec.ObjectName + "' is not a known pattern object name"; + throw new PatternObjectException(message); + } + else if (type == PluggableObjectType.PATTERN_OBSERVER) + { + String message = "Pattern observer name '" + spec.ObjectName + "' is not a known pattern object name"; + throw new PatternObjectException(message); + } + else + { + throw new PatternObjectException("Pattern object type '" + type + "' not known"); + } + } + + Object result; + try + { + result = Activator.CreateInstance(factoryClass); + } + catch (TypeInstantiationException ex) + { + String message = "Error invoking pattern object factory constructor for object '" + spec.ObjectName; + message += "' using Activator.CreateInstance"; + throw new PatternObjectException(message, ex); + } + catch (TargetInvocationException ex) + { + String message = "Error invoking pattern object factory constructor for object '" + spec.ObjectName; + message += "' using Activator.CreateInstance"; + throw new PatternObjectException(message, ex); + } + catch (MethodAccessException ex) + { + String message = "Error invoking pattern object factory constructor for object '" + spec.ObjectName; + message += "', no invocation access for Activator.CreateInstance"; + throw new PatternObjectException(message, ex); + } + catch (MemberAccessException ex) + { + String message = "Error invoking pattern object factory constructor for object '" + spec.ObjectName; + message += "', no invocation access for Activator.CreateInstance"; + throw new PatternObjectException(message, ex); + } + + return result; + } + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/PatternStarter.cs b/NEsper.Core/NEsper.Core/pattern/PatternStarter.cs new file mode 100755 index 000000000..487b78a67 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/PatternStarter.cs @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.pattern +{ + /// + /// Interface for observing when an event expression needs to start (by adding the first listener). + /// The publishing event expression supplies the callback used for indicating matches. The implementation + /// supplies as a return value the callback to use to stop the event expression. + /// + public interface PatternStarter + { + /// + /// An event expression was started and supplies the callback to use when matching events appear. Returns the callback to use to stop the event expression. + /// + /// must be supplied to indicate what to call when the expression turns true + /// is the context for handles to services required for evaluation. + /// if set to true [is recovering resilient]. + /// a callback to stop the expression again + PatternStopCallback Start(PatternMatchCallback matchCallback, + PatternContext context, + bool isRecoveringResilient); + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/PatternStopCallback.cs b/NEsper.Core/NEsper.Core/pattern/PatternStopCallback.cs new file mode 100755 index 000000000..79fd7edda --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/PatternStopCallback.cs @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; + +using com.espertech.esper.util; + +namespace com.espertech.esper.pattern +{ + /// + /// Interface for executing a Stop on an active event expression. + /// + public interface PatternStopCallback : StopCallback + { + } + + public class ProxyPatternStopCallback : ProxyStopCallback, PatternStopCallback + { + public ProxyPatternStopCallback() { } + public ProxyPatternStopCallback(Action procStop) : base(procStop) { } + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/guard/EventGuardVisitor.cs b/NEsper.Core/NEsper.Core/pattern/guard/EventGuardVisitor.cs new file mode 100755 index 000000000..ab74e7c23 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/guard/EventGuardVisitor.cs @@ -0,0 +1,15 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.pattern.guard +{ + public interface EventGuardVisitor + { + void VisitGuard(int numBytes, params object[] stateFlat); + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/guard/ExpressionGuard.cs b/NEsper.Core/NEsper.Core/pattern/guard/ExpressionGuard.cs new file mode 100755 index 000000000..27d507f83 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/guard/ExpressionGuard.cs @@ -0,0 +1,87 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.pattern.guard +{ + /// + /// Guard implementation that keeps a timer instance and quits when the timer expired, + /// and also keeps a count of the number of matches so far, checking both count and timer, + /// letting all instances pass until then. + /// + public class ExpressionGuard : Guard + { + private static readonly ILog Log = + LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly MatchedEventConvertor _convertor; + private readonly ExprEvaluator _expression; + private readonly Quitable _quitable; + + public ExpressionGuard(MatchedEventConvertor convertor, ExprEvaluator expression, Quitable quitable) + { + _quitable = quitable; + _convertor = convertor; + _expression = expression; + } + + public void StartGuard() + { + } + + public bool Inspect(MatchedEventMap matchEvent) + { + EventBean[] eventsPerStream = _convertor.Convert(matchEvent); + + try + { + Object result = + _expression.Evaluate( + new EvaluateParams(eventsPerStream, true, _quitable.Context.AgentInstanceContext)); + if (result == null) + { + return false; + } + + if (true.Equals(result)) + { + return true; + } + + _quitable.GuardQuit(); + return false; + } + catch (Exception ex) + { + string message = "Failed to evaluate expression for pattern-guard for statement '" + + _quitable.Context.PatternContext.StatementName + "'"; + if (!string.IsNullOrWhiteSpace(ex.Message)) + { + message += ": " + ex.Message; + } + Log.Error(message, ex); + throw new EPException(message); + } + } + + public void StopGuard() + { + } + + public void Accept(EventGuardVisitor visitor) + { + visitor.VisitGuard(0); + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/pattern/guard/ExpressionGuardFactory.cs b/NEsper.Core/NEsper.Core/pattern/guard/ExpressionGuardFactory.cs new file mode 100755 index 000000000..d5112e2be --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/guard/ExpressionGuardFactory.cs @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.util; + +namespace com.espertech.esper.pattern.guard +{ + /// + /// Factory for instances. + /// + [Serializable] + public class ExpressionGuardFactory + : GuardFactory + , MetaDefItem + { + protected ExprNode Expression; + + /// For converting matched-events maps to events-per-stream. + [NonSerialized] protected MatchedEventConvertor Convertor; + + public void SetGuardParameters(IList paramList, MatchedEventConvertor convertor) + { + const string errorMessage = "Expression pattern guard requires a single expression as a parameter returning a true or false (bool) value"; + if (paramList.Count != 1) + { + throw new GuardParameterException(errorMessage); + } + Expression = paramList[0]; + + if (paramList[0].ExprEvaluator.ReturnType.GetBoxedType() != typeof(bool?)) + { + throw new GuardParameterException(errorMessage); + } + + Convertor = convertor; + } + + public Guard MakeGuard(PatternAgentInstanceContext context, MatchedEventMap beginState, Quitable quitable, EvalStateNodeNumber stateNodeId, Object guardState) + { + return new ExpressionGuard(Convertor, Expression.ExprEvaluator, quitable); + } + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/guard/Guard.cs b/NEsper.Core/NEsper.Core/pattern/guard/Guard.cs new file mode 100755 index 000000000..5029acc38 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/guard/Guard.cs @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.pattern.guard +{ + /// Guard instances inspect a matched events and makes a determination on whether to let it pass or not. + public interface Guard + { + /// Start the guard operation. + void StartGuard(); + + /// Called when sub-expression quits, or when the pattern stopped. + void StopGuard(); + + /// Returns true if inspection shows that the match events can pass, or false to not pass. + /// is the map of matching events + /// true to pass, false to not pass + bool Inspect(MatchedEventMap matchEvent); + + void Accept(EventGuardVisitor visitor); + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/guard/GuardEnum.cs b/NEsper.Core/NEsper.Core/pattern/guard/GuardEnum.cs new file mode 100755 index 000000000..f3a6713f2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/guard/GuardEnum.cs @@ -0,0 +1,112 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; + +namespace com.espertech.esper.pattern.guard +{ + public enum GuardEnum + { + TIMER_WITHIN, + TIMER_WITHINMAX, + WHILE_GUARD + } + + /// + /// Enum for all build-in guards. + /// + + public static class GuardEnumExtensions + { + /// + /// Gets the namespace. + /// + /// The namespace. + public static string GetNamespace(this GuardEnum value) + { + switch (value) + { + case GuardEnum.TIMER_WITHIN: + return "timer"; + case GuardEnum.TIMER_WITHINMAX: + return "timer"; + case GuardEnum.WHILE_GUARD: + return "internal"; + } + + throw new ArgumentException(); + } + + /// + /// Gets the name. + /// + /// The name. + public static string GetName(this GuardEnum value) + { + switch (value) + { + case GuardEnum.TIMER_WITHIN: + return "within"; + case GuardEnum.TIMER_WITHINMAX: + return "withinmax"; + case GuardEnum.WHILE_GUARD: + return "while"; + } + + throw new ArgumentException(); + } + + /// Returns the enum for the given namespace and name. + /// guard namespace + /// guard name + /// enum + + public static GuardEnum? ForName(String nspace, String name) + { + foreach (var value in EnumHelper.GetValues()) + { + if ((value.GetNamespace() == nspace) && + (value.GetName() == name)) + { + return value; + } + } + + return null; + } + + public static bool IsWhile(String nspace, String name) + { + return + (GetNamespace(GuardEnum.WHILE_GUARD) == nspace) && + (GetName(GuardEnum.WHILE_GUARD) == name); + } + + /// + /// Gets the class associated with the guard enum. + /// + /// The guard enum. + /// + public static Type GetClazz(this GuardEnum guardEnum) + { + switch (guardEnum) + { + case GuardEnum.TIMER_WITHIN: + return typeof(TimerWithinGuardFactory); + case GuardEnum.TIMER_WITHINMAX: + return typeof(TimerWithinOrMaxCountGuardFactory); + case GuardEnum.WHILE_GUARD: + return typeof(ExpressionGuardFactory); + } + + throw new ArgumentException("invalid value", "guardEnum"); + } + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/guard/GuardFactory.cs b/NEsper.Core/NEsper.Core/pattern/guard/GuardFactory.cs new file mode 100755 index 000000000..ec515d9bb --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/guard/GuardFactory.cs @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.pattern.guard +{ + /// Interface for a factory for instances. + public interface GuardFactory + { + /// Sets the guard object parameters. + /// is a list of parameters + /// for converting a + /// GuardParameterException thrown to indicate a parameter problem + void SetGuardParameters(IList guardParameters, + MatchedEventConvertor convertor); + + /// Constructs a guard instance. + /// services for use by guard + /// the prior matching events + /// to use for indicating the guard has quit + /// a node id for the state object + /// state node for guard + /// guard instance + Guard MakeGuard(PatternAgentInstanceContext context, + MatchedEventMap beginState, + Quitable quitable, + EvalStateNodeNumber stateNodeId, + Object guardState); + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/guard/GuardFactorySupport.cs b/NEsper.Core/NEsper.Core/pattern/guard/GuardFactorySupport.cs new file mode 100755 index 000000000..3b636d5c8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/guard/GuardFactorySupport.cs @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.pattern.guard +{ + /// + /// Abstract class for applications to extend to implement pattern guard objects. + /// + public abstract class GuardFactorySupport : GuardFactory + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + public abstract void SetGuardParameters(IList guardParameters, MatchedEventConvertor convertor); + + public abstract Guard MakeGuard( + PatternAgentInstanceContext context, + MatchedEventMap beginState, + Quitable quitable, + EvalStateNodeNumber stateNodeId, + object guardState); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/pattern/guard/GuardParameterException.cs b/NEsper.Core/NEsper.Core/pattern/guard/GuardParameterException.cs new file mode 100755 index 000000000..b7cc3da61 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/guard/GuardParameterException.cs @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.pattern.guard +{ + /// Thrown to indicate a validation error in guard parameterization. + public class GuardParameterException : Exception + { + /// Ctor. + /// validation error message + public GuardParameterException(String message) + : base(message) + { + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/pattern/guard/GuardSupport.cs b/NEsper.Core/NEsper.Core/pattern/guard/GuardSupport.cs new file mode 100755 index 000000000..a69208ecc --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/guard/GuardSupport.cs @@ -0,0 +1,36 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.pattern.guard +{ + /// + /// Abstract class for applications to extend to implement a pattern guard. + /// + public abstract class GuardSupport : Guard + { + /// + /// Start the guard operation. + /// + public abstract void StartGuard(); + /// + /// Called when sub-expression quits, or when the pattern stopped. + /// + public abstract void StopGuard(); + /// + /// Returns true if inspection shows that the match events can pass, or false to not pass. + /// + /// is the map of matching events + /// true to pass, false to not pass + public abstract bool Inspect(MatchedEventMap matchEvent); + /// + /// Accepts the specified visitor. + /// + /// The visitor. + public abstract void Accept(EventGuardVisitor visitor); + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/pattern/guard/Quitable.cs b/NEsper.Core/NEsper.Core/pattern/guard/Quitable.cs new file mode 100755 index 000000000..2464921ab --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/guard/Quitable.cs @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.pattern.guard +{ + /// Receiver for quit events for use by guards. + public interface Quitable + { + /// Indicate guard quitted. + void GuardQuit(); + + /// + /// Retains the pattern context with relevant pattern and statement-level services. + /// + /// The pattern context is the same context as provided to the guard factory and is + /// provided by the quitable so the guard instance does not need to retain the pattern + /// context. + /// + /// pattern context + PatternAgentInstanceContext Context { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/guard/TimerWithinGuard.cs b/NEsper.Core/NEsper.Core/pattern/guard/TimerWithinGuard.cs new file mode 100755 index 000000000..ffbc8169b --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/guard/TimerWithinGuard.cs @@ -0,0 +1,84 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.service; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.pattern; +using com.espertech.esper.schedule; + +namespace com.espertech.esper.pattern.guard +{ + /// + /// Guard implementation that keeps a timer instance and quits when the timer expired, + /// letting all instances pass until then. + /// + public class TimerWithinGuard : Guard, ScheduleHandleCallback { + private readonly long deltaTime; + private readonly Quitable quitable; + private readonly long scheduleSlot; + + private bool isTimerActive; + private EPStatementHandleCallback scheduleHandle; + + /// + /// Ctor. + /// + /// - number of millisecond to guard expiration + /// - to use to indicate that the gaurd quitted + public TimerWithinGuard(long delta, Quitable quitable) { + this.deltaTime = delta; + this.quitable = quitable; + this.scheduleSlot = quitable.Context.PatternContext.ScheduleBucket.AllocateSlot(); + } + + public void StartGuard() { + if (isTimerActive) { + throw new IllegalStateException("Timer already active"); + } + + // Start the stopwatch timer + scheduleHandle = new EPStatementHandleCallback(quitable.Context.AgentInstanceContext.EpStatementAgentInstanceHandle, this); + quitable.Context.PatternContext.SchedulingService.Add(deltaTime, scheduleHandle, scheduleSlot); + isTimerActive = true; + } + + public void StopGuard() { + if (isTimerActive) { + quitable.Context.PatternContext.SchedulingService.Remove(scheduleHandle, scheduleSlot); + scheduleHandle = null; + isTimerActive = false; + } + } + + public bool Inspect(MatchedEventMap matchEvent) { + // no need to test: for timing only, if the timer expired the guardQuit stops any events from coming here + return true; + } + + public void ScheduledTrigger(EngineLevelExtensionServicesContext engineLevelExtensionServicesContext) { + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().QPatternGuardScheduledEval(); + } + // Timer callback is automatically removed when triggering + isTimerActive = false; + quitable.GuardQuit(); + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().APatternGuardScheduledEval(); + } + } + + public void Accept(EventGuardVisitor visitor) { + visitor.VisitGuard(10, scheduleSlot); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/pattern/guard/TimerWithinGuardFactory.cs b/NEsper.Core/NEsper.Core/pattern/guard/TimerWithinGuardFactory.cs new file mode 100755 index 000000000..724ecef2b --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/guard/TimerWithinGuardFactory.cs @@ -0,0 +1,79 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.time; +using com.espertech.esper.util; + +namespace com.espertech.esper.pattern.guard +{ + /// Factory for instances. + [Serializable] + public class TimerWithinGuardFactory + : GuardFactory + , + MetaDefItem + { + /// Number of milliseconds. + private ExprNode _timeExpr; + + /// For converting matched-events maps to events-per-stream. + [NonSerialized] private MatchedEventConvertor _convertor; + + public void SetGuardParameters(IList parameters, MatchedEventConvertor convertor) + { + const string errorMessage = "Timer-within guard requires a single numeric or time period parameter"; + if (parameters.Count != 1) + { + throw new GuardParameterException(errorMessage); + } + + if (!TypeHelper.IsNumeric(parameters[0].ExprEvaluator.ReturnType)) + { + throw new GuardParameterException(errorMessage); + } + + _convertor = convertor; + _timeExpr = parameters[0]; + } + + public long ComputeTime(MatchedEventMap beginState, PatternAgentInstanceContext context) + { + if (_timeExpr is ExprTimePeriod) + { + var timePeriod = (ExprTimePeriod) _timeExpr; + return timePeriod.NonconstEvaluator() + .DeltaUseEngineTime(_convertor.Convert(beginState), context.AgentInstanceContext); + } + else + { + var time = PatternExpressionUtil.Evaluate( + "Timer-within guard", beginState, _timeExpr, _convertor, context.AgentInstanceContext); + if (time == null) + { + throw new EPException("Timer-within guard expression returned a null-value"); + } + return context.StatementContext.TimeAbacus.DeltaForSecondsNumber(time); + } + } + + public Guard MakeGuard( + PatternAgentInstanceContext context, + MatchedEventMap matchedEventMap, + Quitable quitable, + EvalStateNodeNumber stateNodeId, + Object guardState) + { + return new TimerWithinGuard(ComputeTime(matchedEventMap, context), quitable); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/pattern/guard/TimerWithinOrMaxCountGuard.cs b/NEsper.Core/NEsper.Core/pattern/guard/TimerWithinOrMaxCountGuard.cs new file mode 100755 index 000000000..5349792fd --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/guard/TimerWithinOrMaxCountGuard.cs @@ -0,0 +1,100 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.service; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.pattern; +using com.espertech.esper.schedule; + +namespace com.espertech.esper.pattern.guard +{ + /// + /// Guard implementation that keeps a timer instance and quits when the timer expired, + /// and also keeps a count of the number of matches so far, checking both count and timer, + /// letting all instances pass until then. + /// + public class TimerWithinOrMaxCountGuard : Guard, ScheduleHandleCallback { + private readonly long deltaTime; + private readonly int numCountTo; + private readonly Quitable quitable; + private readonly long scheduleSlot; + + private int counter; + private bool isTimerActive; + private EPStatementHandleCallback scheduleHandle; + + /// + /// Ctor. + /// + /// - number of millisecond to guard expiration + /// - max number of counts + /// - to use to indicate that the gaurd quitted + public TimerWithinOrMaxCountGuard(long deltaTime, int numCountTo, Quitable quitable) { + this.deltaTime = deltaTime; + this.numCountTo = numCountTo; + this.quitable = quitable; + this.scheduleSlot = quitable.Context.PatternContext.ScheduleBucket.AllocateSlot(); + } + + public void StartGuard() { + if (isTimerActive) { + throw new IllegalStateException("Timer already active"); + } + + scheduleHandle = new EPStatementHandleCallback(quitable.Context.AgentInstanceContext.EpStatementAgentInstanceHandle, this); + quitable.Context.PatternContext.SchedulingService.Add(deltaTime, scheduleHandle, scheduleSlot); + isTimerActive = true; + counter = 0; + } + + public bool Inspect(MatchedEventMap matchEvent) { + counter++; + if (counter > numCountTo) { + quitable.GuardQuit(); + DeactivateTimer(); + return false; + } + return true; + } + + public void StopGuard() { + if (isTimerActive) { + DeactivateTimer(); + } + } + + public void ScheduledTrigger(EngineLevelExtensionServicesContext engineLevelExtensionServicesContext) { + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().QPatternGuardScheduledEval(); + } + // Timer callback is automatically removed when triggering + isTimerActive = false; + quitable.GuardQuit(); + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().APatternGuardScheduledEval(); + } + } + + public void Accept(EventGuardVisitor visitor) { + visitor.VisitGuard(20, scheduleSlot); + } + + private void DeactivateTimer() { + if (scheduleHandle != null) { + quitable.Context.PatternContext.SchedulingService.Remove(scheduleHandle, scheduleSlot); + } + scheduleHandle = null; + isTimerActive = false; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/pattern/guard/TimerWithinOrMaxCountGuardFactory.cs b/NEsper.Core/NEsper.Core/pattern/guard/TimerWithinOrMaxCountGuardFactory.cs new file mode 100755 index 000000000..052b952fe --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/guard/TimerWithinOrMaxCountGuardFactory.cs @@ -0,0 +1,101 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.time; +using com.espertech.esper.util; + +namespace com.espertech.esper.pattern.guard +{ + [Serializable] + public class TimerWithinOrMaxCountGuardFactory + : GuardFactory + , MetaDefItem + { + /// For converting matched-events maps to events-per-stream. + [NonSerialized] private MatchedEventConvertor _convertor; + + /// Number of count-to max. + private ExprNode _numCountToExpr; + + /// Number of milliseconds. + private ExprNode _timeExpr; + + public Guard MakeGuard( + PatternAgentInstanceContext context, + MatchedEventMap beginState, + Quitable quitable, + EvalStateNodeNumber stateNodeId, + Object guardState) + { + return new TimerWithinOrMaxCountGuard( + ComputeTime(beginState, context), ComputeNumCountTo(beginState, context), quitable); + } + + public void SetGuardParameters(IList parameters, MatchedEventConvertor convertor) + { + const string message = "Timer-within-or-max-count guard requires two parameters: " + + "numeric or time period parameter and an integer-value expression parameter"; + + if (parameters.Count != 2) + { + throw new GuardParameterException(message); + } + + if (!parameters[0].ExprEvaluator.ReturnType.IsNumeric()) + { + throw new GuardParameterException(message); + } + + if (parameters[1].ExprEvaluator.ReturnType != typeof (int?)) + { + throw new GuardParameterException(message); + } + + _timeExpr = parameters[0]; + _numCountToExpr = parameters[1]; + _convertor = convertor; + } + + public long ComputeTime(MatchedEventMap beginState, PatternAgentInstanceContext context) + { + if (_timeExpr is ExprTimePeriod) + { + var timePeriod = (ExprTimePeriod) _timeExpr; + return timePeriod.NonconstEvaluator() + .DeltaUseEngineTime(_convertor.Convert(beginState), context.AgentInstanceContext); + } + else + { + Object time = PatternExpressionUtil.Evaluate( + "Timer-Within-Or-Max-Count guard", beginState, _timeExpr, _convertor, context.AgentInstanceContext); + if (null == time) + { + throw new EPException("Timer-within-or-max first parameter evaluated to a null-value"); + } + return context.StatementContext.TimeAbacus.DeltaForSecondsNumber(time); + } + } + + public int ComputeNumCountTo(MatchedEventMap beginState, PatternAgentInstanceContext context) + { + Object numCountToVal = PatternExpressionUtil.Evaluate( + "Timer-Within-Or-Max-Count guard", beginState, _numCountToExpr, _convertor, context.AgentInstanceContext); + if (null == numCountToVal) + { + throw new EPException("Timer-within-or-max second parameter evaluated to a null-value"); + } + return numCountToVal.AsInt(); + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/pattern/observer/EventObserver.cs b/NEsper.Core/NEsper.Core/pattern/observer/EventObserver.cs new file mode 100755 index 000000000..4a99d346c --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/observer/EventObserver.cs @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.pattern.observer +{ + /// + /// Observers observe and indicate other external events such as timing events. + /// + public interface EventObserver + { + /// Start observing. + void StartObserve(); + + /// Stop observing. + void StopObserve(); + + void Accept(EventObserverVisitor visitor); + + MatchedEventMap BeginState { get; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/pattern/observer/EventObserverSupport.cs b/NEsper.Core/NEsper.Core/pattern/observer/EventObserverSupport.cs new file mode 100755 index 000000000..883326682 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/observer/EventObserverSupport.cs @@ -0,0 +1,21 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.pattern.observer +{ + /// + /// Abstract class for applications to extend to implement a pattern observer. + /// + public abstract class EventObserverSupport : EventObserver + { + public abstract void StartObserve(); + public abstract void StopObserve(); + public abstract void Accept(EventObserverVisitor visitor); + public abstract MatchedEventMap BeginState { get; } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/pattern/observer/EventObserverVisitor.cs b/NEsper.Core/NEsper.Core/pattern/observer/EventObserverVisitor.cs new file mode 100755 index 000000000..d0bc71e65 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/observer/EventObserverVisitor.cs @@ -0,0 +1,15 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.pattern.observer +{ + public interface EventObserverVisitor + { + void VisitObserver(MatchedEventMap beginState, int numBytes, params object[] stateFlat); + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/observer/ObserverEnum.cs b/NEsper.Core/NEsper.Core/pattern/observer/ObserverEnum.cs new file mode 100755 index 000000000..d0aedd247 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/observer/ObserverEnum.cs @@ -0,0 +1,114 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; + +namespace com.espertech.esper.pattern.observer +{ + /// + /// Enum for all build-in observers. + /// + public enum ObserverEnum + { + /// + /// Observer for letting pass/waiting an interval amount of time. + /// + TIMER_INTERVAL, + /// + /// Observer for 'at' (crontab) observation of timer events. + /// + TIMER_CRON, + /// + /// Observer for iso8601 date observation of timer events. + /// + TIMER_ISO8601 + } + + public static class ObserverEnumExtensions + { + /// + /// Gets the observer namespace name. + /// + /// The observer namespace name. + + public static string GetNamespace(this ObserverEnum observerEnum) + { + switch (observerEnum) + { + case ObserverEnum.TIMER_INTERVAL: + case ObserverEnum.TIMER_CRON: + case ObserverEnum.TIMER_ISO8601: + return "timer"; + } + + throw new ArgumentException(); + } + + /// + /// Gets the name. + /// + /// The name. + + public static string GetName(this ObserverEnum observerEnum) + { + switch (observerEnum) + { + case ObserverEnum.TIMER_INTERVAL: + return "interval"; + case ObserverEnum.TIMER_CRON: + return "at"; + case ObserverEnum.TIMER_ISO8601: + return "schedule"; + } + + throw new ArgumentException(); + } + + /// + /// Gets the implementation clazz. + /// + /// The implementation clazz. + + public static Type GetImplementationType(this ObserverEnum observerEnum) + { + switch (observerEnum) + { + case ObserverEnum.TIMER_INTERVAL: + return typeof(TimerIntervalObserverFactory); + case ObserverEnum.TIMER_CRON: + return typeof (TimerAtObserverFactory); + case ObserverEnum.TIMER_ISO8601: + return typeof (TimerScheduleObserverFactory); + } + + throw new ArgumentException(); + } + + /// + /// Returns observer enum for namespace name and observer name. + /// + /// namespace name + /// observer name + + public static ObserverEnum? ForName(String nspace, String name) + { + foreach (var observerEnum in EnumHelper.GetValues()) + { + if ((observerEnum.GetNamespace() == nspace) && + (observerEnum.GetName() == name)) + { + return observerEnum; + } + } + + return null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/observer/ObserverEventEvaluator.cs b/NEsper.Core/NEsper.Core/pattern/observer/ObserverEventEvaluator.cs new file mode 100755 index 000000000..637f043a2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/observer/ObserverEventEvaluator.cs @@ -0,0 +1,31 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.pattern.observer +{ + /// + /// For use by instances to place an event for processing/evaluation. + /// + public interface ObserverEventEvaluator + { + PatternAgentInstanceContext Context { get; } + + /// + /// Indicate an event for evaluation (sub-expression the observer represents has turned true). + /// + /// is the matched events so far + /// whether the observer quit, usually "true" for most observers + void ObserverEvaluateTrue(MatchedEventMap matchEvent, bool quitted); + + /// + /// Indicate that the observer turned permanently false. + /// + /// if set to true [restartable]. + void ObserverEvaluateFalse(bool restartable); + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/pattern/observer/ObserverFactory.cs b/NEsper.Core/NEsper.Core/pattern/observer/ObserverFactory.cs new file mode 100755 index 000000000..ad777f9f5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/observer/ObserverFactory.cs @@ -0,0 +1,52 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.pattern.observer +{ + /// + /// Interface for factories for making observer instances. + /// + public interface ObserverFactory + { + /// Sets the observer object parameters. + /// is a list of parameters + /// for converting partial pattern matches to event-per-stream for expressions + /// ObserverParameterException thrown to indicate a parameter problem + void SetObserverParameters( + IList observerParameters, + MatchedEventConvertor convertor, + ExprValidationContext validationContext); + + /// + /// Make an observer instance. + /// + /// services that may be required by observer implementation + /// start state for observer + /// receiver for events observed + /// optional id for the associated pattern state node + /// state node for observer + /// if set to true [is filter child non quitting]. + /// + /// observer instance + /// + EventObserver MakeObserver( + PatternAgentInstanceContext context, + MatchedEventMap beginState, + ObserverEventEvaluator observerEventEvaluator, + EvalStateNodeNumber stateNodeId, + Object observerState, + bool isFilterChildNonQuitting); + + bool IsNonRestarting { get; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/pattern/observer/ObserverFactorySupport.cs b/NEsper.Core/NEsper.Core/pattern/observer/ObserverFactorySupport.cs new file mode 100755 index 000000000..809f4c532 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/observer/ObserverFactorySupport.cs @@ -0,0 +1,59 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.pattern.observer +{ + /// + /// Abstract class for applications to extend to implement a pattern observer factory. + /// + public abstract class ObserverFactorySupport : ObserverFactory + { + /// + /// Sets the observer object parameters. + /// + /// is a list of parameters + /// for converting partial pattern matches to event-per-stream for expressions + /// ObserverParameterException thrown to indicate a parameter problem + public abstract void SetObserverParameters( + IList paramList, + MatchedEventConvertor convertor, + ExprValidationContext validationContext); + + /// + /// Make an observer instance. + /// + /// services that may be required by observer implementation + /// start state for observer + /// receiver for events observed + /// optional id for the associated pattern state node + /// state node for observer + /// if set to true [is filter child non quitting]. + /// + /// observer instance + /// + /// + public abstract EventObserver MakeObserver( + PatternAgentInstanceContext context, + MatchedEventMap beginState, + ObserverEventEvaluator observerEventEvaluator, + EvalStateNodeNumber stateNodeId, + object observerState, + bool isFilterChildNonQuitting); + + /// + /// Determines whether [is non restarting]. + /// + /// + public abstract bool IsNonRestarting { get; } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/pattern/observer/ObserverParameterException.cs b/NEsper.Core/NEsper.Core/pattern/observer/ObserverParameterException.cs new file mode 100755 index 000000000..2ee797534 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/observer/ObserverParameterException.cs @@ -0,0 +1,49 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Runtime.Serialization; + +namespace com.espertech.esper.pattern.observer +{ + /// Thrown to indicate a validation error in guard parameterization. + public class ObserverParameterException : Exception + { + /// + /// Initializes a new instance of the class. + /// + /// The message. + public ObserverParameterException(string message) : base(message) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The message. + /// The inner exception. + public ObserverParameterException(string message, Exception innerException) : base(message, innerException) + { + } + /// + /// Initializes a new instance of the class. + /// + /// The that holds the serialized object data about the exception being thrown. + /// The that contains contextual information about the source or destination. + /// + /// The parameter is null. + /// + /// + /// The class name is null or is zero (0). + /// + + protected ObserverParameterException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/pattern/observer/ObserverParameterUtil.cs b/NEsper.Core/NEsper.Core/pattern/observer/ObserverParameterUtil.cs new file mode 100755 index 000000000..e93d0bc03 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/observer/ObserverParameterUtil.cs @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.pattern.observer +{ + public class ObserverParameterUtil + { + public static void ValidateNoNamedParameters(string name, IList parameter) + { + foreach (ExprNode node in parameter) + { + if (node is ExprNamedParameterNode) + { + throw new ObserverParameterException(name + " does not allow named parameters"); + } + } + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/pattern/observer/TimerAtObserver.cs b/NEsper.Core/NEsper.Core/pattern/observer/TimerAtObserver.cs new file mode 100755 index 000000000..1f78673b5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/observer/TimerAtObserver.cs @@ -0,0 +1,104 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Reflection; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.service; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.schedule; + +namespace com.espertech.esper.pattern.observer +{ + /// + /// Observer implementation for indicating that a certain time arrived, similar to "crontab". + /// + public class TimerAtObserver + : EventObserver + , ScheduleHandleCallback + { + private static readonly ILog Log = + LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly ScheduleSpec _scheduleSpec; + private readonly long _scheduleSlot; + private readonly MatchedEventMap _beginState; + private readonly ObserverEventEvaluator _observerEventEvaluator; + private bool _isTimerActive = false; + private EPStatementHandleCallback _scheduleHandle; + + /// + /// Ctor. + /// + /// - specification containing the crontab schedule + /// - start state + /// - receiver for events + public TimerAtObserver( + ScheduleSpec scheduleSpec, + MatchedEventMap beginState, + ObserverEventEvaluator observerEventEvaluator) + { + _scheduleSpec = scheduleSpec; + _beginState = beginState; + _observerEventEvaluator = observerEventEvaluator; + _scheduleSlot = observerEventEvaluator.Context.PatternContext.ScheduleBucket.AllocateSlot(); + } + + public MatchedEventMap BeginState + { + get { return _beginState; } + } + + public void ScheduledTrigger(EngineLevelExtensionServicesContext engineLevelExtensionServicesContext) + { + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().QPatternObserverScheduledEval(); + } + _observerEventEvaluator.ObserverEvaluateTrue(_beginState, true); + _isTimerActive = false; + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().APatternObserverScheduledEval(); + } + } + + public void StartObserve() + { + if (_isTimerActive) + { + throw new IllegalStateException("Timer already active"); + } + + _scheduleHandle = new EPStatementHandleCallback(_observerEventEvaluator.Context.AgentInstanceContext.EpStatementAgentInstanceHandle, this); + var schedulingService = _observerEventEvaluator.Context.PatternContext.SchedulingService; + var engineImportService = + _observerEventEvaluator.Context.StatementContext.EngineImportService; + var nextScheduledTime = ScheduleComputeHelper.ComputeDeltaNextOccurance( + _scheduleSpec, schedulingService.Time, engineImportService.TimeZone, engineImportService.TimeAbacus); + schedulingService.Add(nextScheduledTime, _scheduleHandle, _scheduleSlot); + _isTimerActive = true; + } + + public void StopObserve() + { + if (_isTimerActive) + { + _observerEventEvaluator.Context.PatternContext.SchedulingService.Remove(_scheduleHandle, _scheduleSlot); + _isTimerActive = false; + _scheduleHandle = null; + } + } + + public void Accept(EventObserverVisitor visitor) + { + visitor.VisitObserver(_beginState, 2, _scheduleSlot, _scheduleSpec); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/pattern/observer/TimerAtObserverFactory.cs b/NEsper.Core/NEsper.Core/pattern/observer/TimerAtObserverFactory.cs new file mode 100755 index 000000000..2b8f81525 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/observer/TimerAtObserverFactory.cs @@ -0,0 +1,121 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.schedule; +using com.espertech.esper.util; + +namespace com.espertech.esper.pattern.observer +{ + /// + /// Factory for 'crontab' observers that indicate truth when a time point was reached. + /// + [Serializable] + public class TimerAtObserverFactory + : ObserverFactory + , MetaDefItem + { + private static readonly ILog Log = + LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + /// Parameters. + private IList _parameters; + + /// Convertor. + [NonSerialized] private MatchedEventConvertor _convertor; + + /// The schedule specification for the timer-at. + private ScheduleSpec _spec = null; + + public void SetObserverParameters( + IList parameters, + MatchedEventConvertor convertor, + ExprValidationContext validationContext) + { + ObserverParameterUtil.ValidateNoNamedParameters("timer:at", parameters); + if (Log.IsDebugEnabled) + { + Log.Debug(".setObserverParameters " + parameters); + } + + if ((parameters.Count < 5) || (parameters.Count > 7)) + { + throw new ObserverParameterException("Invalid number of parameters for timer:at"); + } + + _parameters = parameters; + _convertor = convertor; + + // if all parameters are constants, lets try to evaluate and build a schedule for early validation + bool allConstantResult = true; + foreach (ExprNode param in parameters) + { + if (!param.IsConstantResult) + { + allConstantResult = false; + } + } + + if (allConstantResult) + { + try + { + var observerParameters = PatternExpressionUtil.Evaluate( + "Timer-at observer", new MatchedEventMapImpl(convertor.MatchedEventMapMeta), parameters, + convertor, null); + _spec = ScheduleSpecUtil.ComputeValues(observerParameters.ToArray()); + } + catch (ScheduleParameterException e) + { + throw new ObserverParameterException( + "Error computing crontab schedule specification: " + e.Message, e); + } + } + } + + public ScheduleSpec ComputeSpec(MatchedEventMap beginState, PatternAgentInstanceContext context) + { + if (_spec != null) + { + return _spec; + } + var observerParameters = PatternExpressionUtil.Evaluate( + "Timer-at observer", beginState, _parameters, _convertor, context.AgentInstanceContext); + try + { + return ScheduleSpecUtil.ComputeValues(observerParameters.ToArray()); + } + catch (ScheduleParameterException e) + { + throw new EPException("Error computing crontab schedule specification: " + e.Message, e); + } + } + + public EventObserver MakeObserver( + PatternAgentInstanceContext context, + MatchedEventMap beginState, + ObserverEventEvaluator observerEventEvaluator, + EvalStateNodeNumber stateNodeId, + Object observerState, + bool isFilterChildNonQuitting) + { + return new TimerAtObserver(ComputeSpec(beginState, context), beginState, observerEventEvaluator); + } + + public bool IsNonRestarting + { + get { return false; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/pattern/observer/TimerIntervalObserver.cs b/NEsper.Core/NEsper.Core/pattern/observer/TimerIntervalObserver.cs new file mode 100755 index 000000000..6a89397c8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/observer/TimerIntervalObserver.cs @@ -0,0 +1,88 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.compat; +using com.espertech.esper.core.service; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.schedule; + +namespace com.espertech.esper.pattern.observer +{ + /// + /// Observer that will wait a certain interval before indicating true (raising an event). + /// + public class TimerIntervalObserver : EventObserver, ScheduleHandleCallback + { + private readonly long _deltaTime; + private readonly MatchedEventMap _beginState; + private readonly ObserverEventEvaluator _observerEventEvaluator; + private readonly long _scheduleSlot; + + private bool _isTimerActive = false; + private EPStatementHandleCallback _scheduleHandle; + + /// Ctor. + /// number of milliseconds + /// start state + /// receiver for events + public TimerIntervalObserver(long deltaTime, MatchedEventMap beginState, ObserverEventEvaluator observerEventEvaluator) + { + _deltaTime = deltaTime; + _beginState = beginState; + _observerEventEvaluator = observerEventEvaluator; + _scheduleSlot = observerEventEvaluator.Context.PatternContext.ScheduleBucket.AllocateSlot(); + } + + public void ScheduledTrigger(EngineLevelExtensionServicesContext engineLevelExtensionServicesContext) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QPatternObserverScheduledEval(); } + _observerEventEvaluator.ObserverEvaluateTrue(_beginState, true); + _isTimerActive = false; + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().APatternObserverScheduledEval(); } + } + + public MatchedEventMap BeginState + { + get { return _beginState; } + } + + public void StartObserve() + { + if (_isTimerActive) + { + throw new IllegalStateException("Timer already active"); + } + + if (_deltaTime <= 0) + { + _observerEventEvaluator.ObserverEvaluateTrue(_beginState, true); + } + else + { + _scheduleHandle = new EPStatementHandleCallback(_observerEventEvaluator.Context.AgentInstanceContext.EpStatementAgentInstanceHandle, this); + _observerEventEvaluator.Context.PatternContext.SchedulingService.Add(_deltaTime, _scheduleHandle, _scheduleSlot); + _isTimerActive = true; + } + } + + public void StopObserve() + { + if (_isTimerActive) + { + _observerEventEvaluator.Context.PatternContext.SchedulingService.Remove(_scheduleHandle, _scheduleSlot); + _isTimerActive = false; + _scheduleHandle = null; + } + } + + public void Accept(EventObserverVisitor visitor) + { + visitor.VisitObserver(_beginState, 10, _scheduleSlot); + } + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/observer/TimerIntervalObserverFactory.cs b/NEsper.Core/NEsper.Core/pattern/observer/TimerIntervalObserverFactory.cs new file mode 100755 index 000000000..8c68334b2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/observer/TimerIntervalObserverFactory.cs @@ -0,0 +1,93 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.time; +using com.espertech.esper.util; + +namespace com.espertech.esper.pattern.observer +{ + /// Factory for making observer instances. + [Serializable] + public class TimerIntervalObserverFactory + : ObserverFactory, + MetaDefItem + { + private const string NAME = "Timer-interval observer"; + + /// Parameters. + private ExprNode _parameter; + + /// Convertor to events-per-stream. + [NonSerialized] private MatchedEventConvertor _convertor; + + public void SetObserverParameters( + IList parameters, + MatchedEventConvertor convertor, + ExprValidationContext validationContext) + { + ObserverParameterUtil.ValidateNoNamedParameters(NAME, parameters); + const string errorMessage = NAME + " requires a single numeric or time period parameter"; + if (parameters.Count != 1) + { + throw new ObserverParameterException(errorMessage); + } + if (!(parameters[0] is ExprTimePeriod)) + { + var returnType = parameters[0].ExprEvaluator.ReturnType; + if (!returnType.IsNumeric()) + { + throw new ObserverParameterException(errorMessage); + } + } + + _parameter = parameters[0]; + _convertor = convertor; + } + + public long ComputeDelta(MatchedEventMap beginState, PatternAgentInstanceContext context) + { + if (_parameter is ExprTimePeriod) + { + var timePeriod = (ExprTimePeriod) _parameter; + return timePeriod.NonconstEvaluator() + .DeltaUseEngineTime(_convertor.Convert(beginState), context.AgentInstanceContext); + } + else + { + var result = _parameter.ExprEvaluator.Evaluate( + new EvaluateParams(_convertor.Convert(beginState), true, context.AgentInstanceContext)); + if (result == null) + { + throw new EPException("Null value returned for guard expression"); + } + return context.StatementContext.TimeAbacus.DeltaForSecondsNumber(result); + } + } + + public EventObserver MakeObserver( + PatternAgentInstanceContext context, + MatchedEventMap beginState, + ObserverEventEvaluator observerEventEvaluator, + EvalStateNodeNumber stateNodeId, + Object observerState, + bool isFilterChildNonQuitting) + { + return new TimerIntervalObserver(ComputeDelta(beginState, context), beginState, observerEventEvaluator); + } + + public bool IsNonRestarting + { + get { return false; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/pattern/observer/TimerScheduleISO8601Parser.cs b/NEsper.Core/NEsper.Core/pattern/observer/TimerScheduleISO8601Parser.cs new file mode 100755 index 000000000..7fee34ac3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/observer/TimerScheduleISO8601Parser.cs @@ -0,0 +1,233 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Text.RegularExpressions; + +using com.espertech.esper.client.util; +using com.espertech.esper.compat; +using com.espertech.esper.compat.logging; +using com.espertech.esper.schedule; + +namespace com.espertech.esper.pattern.observer +{ + /// + /// Factory for ISO8601 repeating interval observers that indicate truth when a time point was reached. + /// + public class TimerScheduleISO8601Parser + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + public static TimerScheduleSpec Parse(string iso) + { + if (iso == null) + { + throw new ScheduleParameterException("Received a null value"); + } + iso = iso.Trim(); + if (string.IsNullOrEmpty(iso)) + { + throw new ScheduleParameterException("Received an empty string"); + } + + var split = iso.Split('/'); + + long? optionalRepeats = null; + DateTimeEx optionalDate = null; + TimePeriod optionalTimePeriod = null; + + try + { + if (iso.Equals("/")) + { + throw new ScheduleParameterException("Invalid number of parts"); + } + if (iso.EndsWith("/")) + { + throw new ScheduleParameterException("Missing the period part"); + } + + if (split.Length == 3) + { + optionalRepeats = ParseRepeat(split[0]); + optionalDate = ParseDate(split[1]); + optionalTimePeriod = ParsePeriod(split[2]); + } + else if (split.Length == 2) + { + // there are two forms: + // partial-form-1: "R/P" + // partial-form-2: "/P" + if (string.IsNullOrEmpty(split[0])) + { + throw new ScheduleParameterException("Expected either a recurrence or a date but received an empty string"); + } + if (split[0][0] == 'R') + { + optionalRepeats = ParseRepeat(split[0]); + } + else + { + optionalDate = ParseDate(split[0]); + } + optionalTimePeriod = ParsePeriod(split[1]); + } + else if (split.Length == 1) + { + // there are two forms: + // just date: "" + // just period: "P" + if (split[0][0] == 'P') + { + optionalTimePeriod = ParsePeriod(split[0]); + } + else + { + optionalDate = ParseDate(split[0]); + } + } + } + catch (Exception ex) + { + throw new ScheduleParameterException("Failed to parse '" + iso + "': " + ex.Message, ex); + } + + // parse repeating interval + return new TimerScheduleSpec(optionalDate, null, optionalRepeats, optionalTimePeriod); + } + + public static DateTimeEx ParseDate(string dateText) + { + try + { + return DateTimeParser.ParseDefaultEx(dateText); + //return javax.xml.datatype.DatatypeFactory.NewInstance().NewXMLGregorianCalendar(dateText).ToGregorianCalendar(); + } + catch (Exception e) + { + var message = "Exception parsing date '" + dateText + "', the date is not a supported ISO 8601 date"; + Log.Debug(message, e); + throw new ScheduleParameterException(message); + } + } + + private static long ParseRepeat(string repeat) + { + if (repeat[0] != 'R') + { + throw new ScheduleParameterException("Invalid repeat '" + repeat + "', expecting 'R' but received '" + repeat[0] + "'"); + } + long numRepeats = -1; + if (repeat.Length > 1) + { + if (!long.TryParse(repeat.Substring(1), out numRepeats)) + { + var message = "Invalid repeat '" + repeat + "', expecting an long-typed value but received '" + repeat.Substring(1) + "'"; + throw new ScheduleParameterException(message); + } + } + return numRepeats; + } + + private static TimePeriod ParsePeriod(string period) + { + var p = new Regex("^P((\\d+Y)?(\\d+M)?(\\d+W)?(\\d+D)?)?(T(\\d+H)?(\\d+M)?(\\d+S)?)?$"); + var matcher = p.Match(period); + if (matcher == Match.Empty) + { + throw new ScheduleParameterException("Invalid period '" + period + "'"); + } + + var timePeriod = new TimePeriod(); + var indexOfT = period.IndexOf('T'); + if (indexOfT < 1) + { + ParsePeriodDatePart(period.Substring(1), timePeriod); + } + else + { + ParsePeriodDatePart(period.Substring(1, indexOfT - 1), timePeriod); + ParsePeriodTimePart(period.Substring(indexOfT + 1), timePeriod); + } + + var largestAbsolute = timePeriod.LargestAbsoluteValue(); + if (largestAbsolute == null || largestAbsolute == 0) + { + throw new ScheduleParameterException("Invalid period '" + period + "'"); + } + return timePeriod; + } + + private static void ParsePeriodDatePart(string datePart, TimePeriod timePeriod) + { + var pattern = new Regex("^(\\d+Y)?(\\d+M)?(\\d+W)?(\\d+D)?$"); + var matcher = pattern.Match(datePart); + if (matcher == Match.Empty) + { + throw new IllegalStateException(); + } + for (var i = 0; i < matcher.Groups.Count; i++) + { + var group = matcher.Groups[i + 1].Value; + if (string.IsNullOrWhiteSpace(group)) + { + } + else if (group.EndsWith("Y")) + { + timePeriod.Years = SafeParsePrefixedInt(group); + } + else if (group.EndsWith("M")) + { + timePeriod.Months = SafeParsePrefixedInt(group); + } + else if (group.EndsWith("D")) + { + timePeriod.Days = SafeParsePrefixedInt(group); + } + else if (group.EndsWith("W")) + { + timePeriod.Weeks = SafeParsePrefixedInt(group); + } + } + } + + private static int? SafeParsePrefixedInt(string group) + { + return int.Parse(group.Substring(0, group.Length - 1)); + } + + private static void ParsePeriodTimePart(string timePart, TimePeriod timePeriod) + { + var pattern = new Regex("^(\\d+H)?(\\d+M)?(\\d+S)?$"); + var matcher = pattern.Match(timePart); + if (matcher == Match.Empty) + { + throw new IllegalStateException(); + } + for (var i = 0; i < matcher.Groups.Count; i++) + { + string group = matcher.Groups[i + 1].Value; + if (string.IsNullOrWhiteSpace(group)) + { + } + else if (group.EndsWith("H")) + { + timePeriod.Hours = SafeParsePrefixedInt(group); + } + else if (group.EndsWith("M")) + { + timePeriod.Minutes = SafeParsePrefixedInt(group); + } + else if (group.EndsWith("S")) + { + timePeriod.Seconds = SafeParsePrefixedInt(group); + } + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/pattern/observer/TimerScheduleObserver.cs b/NEsper.Core/NEsper.Core/pattern/observer/TimerScheduleObserver.cs new file mode 100755 index 000000000..42d00337a --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/observer/TimerScheduleObserver.cs @@ -0,0 +1,229 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.datetime.calop; +using com.espertech.esper.epl.expression.time; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.schedule; + +namespace com.espertech.esper.pattern.observer +{ + /// + /// Observer implementation for indicating that a certain time arrived, similar to "crontab". + /// + public class TimerScheduleObserver + : EventObserver + , + ScheduleHandleCallback + { + private readonly long _scheduleSlot; + private readonly ObserverEventEvaluator _observerEventEvaluator; + private readonly TimerScheduleSpec _spec; + private readonly bool _isFilterChildNonQuitting; + private readonly MatchedEventMap _beginState; + // we always keep the anchor time, which could be engine time or the spec time, and never changes in computations + private DateTimeEx _anchorTime; + private long _anchorRemainder; + + // for fast computation, keep some last-value information around for the purpose of caching + private bool _isTimerActive = false; + private EPStatementHandleCallback _scheduleHandle; + private DateTimeEx _cachedLastScheduled; + private long _cachedCountRepeated = 0; + + public TimerScheduleObserver( + TimerScheduleSpec spec, + MatchedEventMap beginState, + ObserverEventEvaluator observerEventEvaluator, + bool isFilterChildNonQuitting) + { + _beginState = beginState; + _observerEventEvaluator = observerEventEvaluator; + _scheduleSlot = observerEventEvaluator.Context.PatternContext.ScheduleBucket.AllocateSlot(); + _spec = spec; + _isFilterChildNonQuitting = isFilterChildNonQuitting; + } + + public MatchedEventMap BeginState + { + get { return _beginState; } + } + + public void ScheduledTrigger(EngineLevelExtensionServicesContext engineLevelExtensionServicesContext) + { + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().QPatternObserverScheduledEval(); + } + + // compute reschedule time + _isTimerActive = false; + var schedulingService = _observerEventEvaluator.Context.PatternContext.SchedulingService; + var nextScheduledTime = ComputeNextSetLastScheduled( + schedulingService.Time, _observerEventEvaluator.Context.StatementContext.TimeAbacus); + + var quit = !_isFilterChildNonQuitting || nextScheduledTime == -1; + _observerEventEvaluator.ObserverEvaluateTrue(_beginState, quit); + + // handle no more invocations planned + if (nextScheduledTime == -1) + { + StopObserve(); + _observerEventEvaluator.ObserverEvaluateFalse(false); + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().APatternObserverScheduledEval(); + } + return; + } + + schedulingService.Add(nextScheduledTime, _scheduleHandle, _scheduleSlot); + _isTimerActive = true; + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().APatternObserverScheduledEval(); + } + } + + public void StartObserve() + { + if (_isTimerActive) + { + throw new IllegalStateException("Timer already active"); + } + + var schedulingService = _observerEventEvaluator.Context.PatternContext.SchedulingService; + var timeAbacus = _observerEventEvaluator.Context.StatementContext.TimeAbacus; + + if (_anchorTime == null) + { + if (_spec.OptionalDate == null) + { + _anchorTime = + DateTimeEx.GetInstance( + _observerEventEvaluator.Context.StatementContext.EngineImportService.TimeZone); + _anchorRemainder = timeAbacus.CalendarSet(schedulingService.Time, _anchorTime); + } + else + { + _anchorTime = _spec.OptionalDate; + _anchorRemainder = _spec.OptionalRemainder.GetValueOrDefault(0); + } + } + + var nextScheduledTime = ComputeNextSetLastScheduled(schedulingService.Time, timeAbacus); + if (nextScheduledTime == -1) + { + StopObserve(); + _observerEventEvaluator.ObserverEvaluateFalse(false); + return; + } + + _scheduleHandle = + new EPStatementHandleCallback( + _observerEventEvaluator.Context.AgentInstanceContext.EpStatementAgentInstanceHandle, this); + schedulingService.Add(nextScheduledTime, _scheduleHandle, _scheduleSlot); + _isTimerActive = true; + } + + public void StopObserve() + { + if (_isTimerActive) + { + _observerEventEvaluator.Context.PatternContext.SchedulingService.Remove(_scheduleHandle, _scheduleSlot); + } + _isTimerActive = false; + _scheduleHandle = null; + _cachedCountRepeated = Int64.MaxValue; + _cachedLastScheduled = null; + _anchorTime = null; + } + + public void Accept(EventObserverVisitor visitor) + { + visitor.VisitObserver( + _beginState, 2, _scheduleSlot, _spec, _anchorTime, _cachedCountRepeated, _cachedLastScheduled, + _isTimerActive); + } + + private long ComputeNextSetLastScheduled(long currentTime, TimeAbacus timeAbacus) + { + + // handle already-stopped + if (_cachedCountRepeated == Int64.MaxValue) + { + return -1; + } + + // handle date-only-form: "" + if (_spec.OptionalRepeatCount == null && _spec.OptionalDate != null && _spec.OptionalTimePeriod == null) + { + _cachedCountRepeated = Int64.MaxValue; + var computed = timeAbacus.CalendarGet(_anchorTime, _anchorRemainder); + if (computed > currentTime) + { + return computed - currentTime; + } + return -1; + } + + // handle period-only-form: "P" + // handle partial-form-2: "/" (non-recurring) + if (_spec.OptionalRepeatCount == null && _spec.OptionalTimePeriod != null) + { + _cachedCountRepeated = Int64.MaxValue; + _cachedLastScheduled = _anchorTime.Clone(); + CalendarOpPlusMinus.Action(_cachedLastScheduled, 1, _spec.OptionalTimePeriod); + var computed = timeAbacus.CalendarGet(_cachedLastScheduled, _anchorRemainder); + if (computed > currentTime) + { + return computed - currentTime; + } + return -1; + } + + // handle partial-form-1: "R/" + // handle full form + if (_cachedLastScheduled == null) + { + _cachedLastScheduled = _anchorTime.Clone(); + if (_spec.OptionalDate != null) + { + _cachedCountRepeated = 1; + } + } + + var nextDue = CalendarOpPlusFastAddHelper.ComputeNextDue( + currentTime, _spec.OptionalTimePeriod, _cachedLastScheduled, timeAbacus, _anchorRemainder); + + if (_spec.OptionalRepeatCount == -1) + { + _cachedLastScheduled = nextDue.Scheduled; + var computed = timeAbacus.CalendarGet(_cachedLastScheduled, _anchorRemainder); + return computed - currentTime; + } + + _cachedCountRepeated += nextDue.Factor; + if (_cachedCountRepeated <= _spec.OptionalRepeatCount) + { + _cachedLastScheduled = nextDue.Scheduled; + var computed = timeAbacus.CalendarGet(_cachedLastScheduled, _anchorRemainder); + if (computed > currentTime) + { + return computed - currentTime; + } + } + _cachedCountRepeated = Int64.MaxValue; + return -1; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/pattern/observer/TimerScheduleObserverFactory.cs b/NEsper.Core/NEsper.Core/pattern/observer/TimerScheduleObserverFactory.cs new file mode 100755 index 000000000..f4f8e78c9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/observer/TimerScheduleObserverFactory.cs @@ -0,0 +1,330 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.client.util; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.time; +using com.espertech.esper.schedule; +using com.espertech.esper.util; + +namespace com.espertech.esper.pattern.observer +{ + /// + /// Factory for ISO8601 repeating interval observers that indicate truth when a time point was reached. + /// + [Serializable] + public class TimerScheduleObserverFactory + : ObserverFactory + , MetaDefItem + { + private const string NAME_OBSERVER = "Timer-schedule observer"; + + private const string ISO_NAME = "iso"; + private const string REPETITIONS_NAME = "repetitions"; + private const string DATE_NAME = "date"; + private const string PERIOD_NAME = "period"; + + private static readonly string[] NAMED_PARAMETERS = + { + ISO_NAME, + REPETITIONS_NAME, + DATE_NAME, + PERIOD_NAME + }; + + [NonSerialized] protected MatchedEventConvertor Convertor; + + /// Convertor. + [NonSerialized] protected TimerScheduleSpecCompute ScheduleComputer; + + protected TimerScheduleSpec Spec; + + public EventObserver MakeObserver( + PatternAgentInstanceContext context, + MatchedEventMap beginState, + ObserverEventEvaluator observerEventEvaluator, + EvalStateNodeNumber stateNodeId, + Object observerState, + bool isFilterChildNonQuitting) + { + return new TimerScheduleObserver( + ComputeSpecDynamic(beginState, context), beginState, observerEventEvaluator, isFilterChildNonQuitting); + } + + public bool IsNonRestarting + { + get { return true; } + } + + public void SetObserverParameters( + IList parameters, + MatchedEventConvertor convertor, + ExprValidationContext validationContext) + { + Convertor = convertor; + + // obtains name parameters + IDictionary namedExpressions; + try + { + namedExpressions = ExprNodeUtility.GetNamedExpressionsHandleDups(parameters); + ExprNodeUtility.ValidateNamed(namedExpressions, NAMED_PARAMETERS); + } + catch (ExprValidationException e) + { + throw new ObserverParameterException(e.Message, e); + } + + bool allConstantResult; + ExprNamedParameterNode isoStringExpr = namedExpressions.Get(ISO_NAME); + if (namedExpressions.Count == 1 && isoStringExpr != null) + { + try + { + allConstantResult = ExprNodeUtility.ValidateNamedExpectType( + isoStringExpr, new Type[] + { + typeof (string) + }); + } + catch (ExprValidationException ex) + { + throw new ObserverParameterException(ex.Message, ex); + } + ScheduleComputer = new TimerScheduleSpecComputeISOString(isoStringExpr.ChildNodes[0]); + } + else if (isoStringExpr != null) + { + throw new ObserverParameterException( + "The '" + ISO_NAME + "' parameter is exclusive of other parameters"); + } + else if (namedExpressions.Count == 0) + { + throw new ObserverParameterException("No parameters provided"); + } + else + { + allConstantResult = true; + ExprNamedParameterNode dateNamedNode = namedExpressions.Get(DATE_NAME); + ExprNamedParameterNode repetitionsNamedNode = namedExpressions.Get(REPETITIONS_NAME); + ExprNamedParameterNode periodNamedNode = namedExpressions.Get(PERIOD_NAME); + if (dateNamedNode == null && periodNamedNode == null) + { + throw new ObserverParameterException("Either the date or period parameter is required"); + } + try + { + if (dateNamedNode != null) + { + allConstantResult = ExprNodeUtility.ValidateNamedExpectType( + dateNamedNode, new Type[] + { + typeof (string), + typeof (DateTime), + typeof (DateTimeOffset), + typeof (long?), + typeof (DateTimeEx) + }); + } + if (repetitionsNamedNode != null) + { + allConstantResult &= ExprNodeUtility.ValidateNamedExpectType( + repetitionsNamedNode, new Type[] + { + typeof (int?), + typeof (long?) + }); + } + if (periodNamedNode != null) + { + allConstantResult &= ExprNodeUtility.ValidateNamedExpectType( + periodNamedNode, new Type[] + { + typeof (TimePeriod) + }); + } + } + catch (ExprValidationException ex) + { + throw new ObserverParameterException(ex.Message, ex); + } + ExprNode dateNode = dateNamedNode == null ? null : dateNamedNode.ChildNodes[0]; + ExprNode repetitionsNode = repetitionsNamedNode == null ? null : repetitionsNamedNode.ChildNodes[0]; + ExprTimePeriod periodNode = periodNamedNode == null + ? null + : (ExprTimePeriod) periodNamedNode.ChildNodes[0]; + ScheduleComputer = new TimerScheduleSpecComputeFromExpr(dateNode, repetitionsNode, periodNode); + } + + if (allConstantResult) + { + try + { + Spec = ScheduleComputer.Compute( + convertor, new MatchedEventMapImpl(convertor.MatchedEventMapMeta), null, + validationContext.EngineImportService.TimeZone, validationContext.EngineImportService.TimeAbacus); + } + catch (ScheduleParameterException ex) + { + throw new ObserverParameterException(ex.Message, ex); + } + } + } + + public TimerScheduleSpec ComputeSpecDynamic(MatchedEventMap beginState, PatternAgentInstanceContext context) + { + if (Spec != null) + { + return Spec; + } + try + { + return ScheduleComputer.Compute( + Convertor, beginState, context.AgentInstanceContext, + context.StatementContext.EngineImportService.TimeZone, + context.StatementContext.EngineImportService.TimeAbacus); + } + catch (ScheduleParameterException e) + { + throw new EPException("Error computing iso8601 schedule specification: " + e.Message, e); + } + } + + protected internal interface TimerScheduleSpecCompute + { + TimerScheduleSpec Compute( + MatchedEventConvertor convertor, + MatchedEventMap beginState, + ExprEvaluatorContext exprEvaluatorContext, + TimeZoneInfo timeZone, + TimeAbacus timeAbacus); + } + + private class TimerScheduleSpecComputeFromExpr : TimerScheduleSpecCompute + { + private readonly ExprNode _dateNode; + private readonly ExprTimePeriod _periodNode; + private readonly ExprNode _repetitionsNode; + + internal TimerScheduleSpecComputeFromExpr( + ExprNode dateNode, + ExprNode repetitionsNode, + ExprTimePeriod periodNode) + { + _dateNode = dateNode; + _repetitionsNode = repetitionsNode; + _periodNode = periodNode; + } + + public TimerScheduleSpec Compute( + MatchedEventConvertor convertor, + MatchedEventMap beginState, + ExprEvaluatorContext exprEvaluatorContext, + TimeZoneInfo timeZone, + TimeAbacus timeAbacus) + { + DateTimeEx optionalDate = null; + long? optionalRemainder = null; + if (_dateNode != null) + { + Object param = PatternExpressionUtil.Evaluate( + NAME_OBSERVER, beginState, _dateNode, convertor, exprEvaluatorContext); + if (param is string) + { + optionalDate = TimerScheduleISO8601Parser.ParseDate((string) param); + } + else if (param.IsLong() || param.IsInt()) + { + long msec = param.AsLong(); + optionalDate = DateTimeEx.GetInstance(timeZone); + timeAbacus.CalendarSet(msec, optionalDate); + //optionalDate = new DateTimeEx(msec.AsDateTimeOffset(timeZone), timeZone); + optionalRemainder = timeAbacus.CalendarSet(msec, optionalDate); + } + else if (param is DateTimeOffset || param is DateTime) + { + optionalDate = new DateTimeEx(param.AsDateTimeOffset(timeZone), timeZone); + } + else if (param is DateTimeEx) + { + optionalDate = (DateTimeEx) param; + } + else if (param == null) + { + throw new EPException( + "Null date-time value returned from " + + ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(_dateNode)); + } + else + { + throw new EPException("Unrecognized date-time value " + param.GetType()); + } + } + + TimePeriod optionalTimePeriod = null; + if (_periodNode != null) + { + object param = PatternExpressionUtil.EvaluateTimePeriod( + NAME_OBSERVER, beginState, _periodNode, convertor, exprEvaluatorContext); + optionalTimePeriod = (TimePeriod) param; + } + + long? optionalRepeatCount = null; + if (_repetitionsNode != null) + { + object param = PatternExpressionUtil.Evaluate( + NAME_OBSERVER, beginState, _repetitionsNode, convertor, exprEvaluatorContext); + if (param != null) + { + optionalRepeatCount = param.AsLong(); + } + } + + if (optionalDate == null && optionalTimePeriod == null) + { + throw new EPException("Required date or time period are both null for " + NAME_OBSERVER); + } + + return new TimerScheduleSpec(optionalDate, optionalRemainder, optionalRepeatCount, optionalTimePeriod); + } + } + + private class TimerScheduleSpecComputeISOString : TimerScheduleSpecCompute + { + private readonly ExprNode _parameter; + + internal TimerScheduleSpecComputeISOString(ExprNode parameter) + { + _parameter = parameter; + } + + public TimerScheduleSpec Compute( + MatchedEventConvertor convertor, + MatchedEventMap beginState, + ExprEvaluatorContext exprEvaluatorContext, + TimeZoneInfo timeZone, + TimeAbacus timeAbacus) + { + object param = PatternExpressionUtil.Evaluate( + NAME_OBSERVER, beginState, _parameter, convertor, exprEvaluatorContext); + var iso = (string) param; + if (iso == null) + { + throw new ScheduleParameterException("Received null parameter value"); + } + return TimerScheduleISO8601Parser.Parse(iso); + } + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/pattern/observer/TimerScheduleSpec.cs b/NEsper.Core/NEsper.Core/pattern/observer/TimerScheduleSpec.cs new file mode 100755 index 000000000..19e4c6707 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/observer/TimerScheduleSpec.cs @@ -0,0 +1,39 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client.util; +using com.espertech.esper.compat; + +namespace com.espertech.esper.pattern.observer +{ + [Serializable] + public class TimerScheduleSpec + { + public TimerScheduleSpec( + DateTimeEx optionalDate, + long? optionalRemainder, + long? optionalRepeatCount, + TimePeriod optionalTimePeriod) + { + OptionalDate = optionalDate; + OptionalRepeatCount = optionalRepeatCount; + OptionalTimePeriod = optionalTimePeriod; + OptionalRemainder = optionalRemainder; + } + + public DateTimeEx OptionalDate { get; private set; } + + public long? OptionalRepeatCount { get; private set; } + + public TimePeriod OptionalTimePeriod { get; private set; } + + public long? OptionalRemainder { get; private set; } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/pattern/pool/PatternSubexpressionPoolEngineSvc.cs b/NEsper.Core/NEsper.Core/pattern/pool/PatternSubexpressionPoolEngineSvc.cs new file mode 100755 index 000000000..f13cd9daa --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/pool/PatternSubexpressionPoolEngineSvc.cs @@ -0,0 +1,171 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.client.hook; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.util; +using com.espertech.esper.util; + +namespace com.espertech.esper.pattern.pool +{ + public class PatternSubexpressionPoolEngineSvc + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly ICollection _patternContexts; + private readonly AtomicLong _poolCount; + private readonly bool _preventStart; + private long _maxPoolCountConfigured; + + public PatternSubexpressionPoolEngineSvc(long maxPoolCountConfigured, + bool preventStart) + { + _maxPoolCountConfigured = maxPoolCountConfigured; + _preventStart = preventStart; + _poolCount = new AtomicLong(); + _patternContexts = new HashSet(); + } + + public long? PatternMaxSubexpressions + { + get { return _maxPoolCountConfigured; } + set { _maxPoolCountConfigured = value.GetValueOrDefault(-1); } + } + + public void AddPatternContext(String statementName, + PatternSubexpressionPoolStmtHandler stmtCounts) + { + lock (_patternContexts) + { + _patternContexts.Add(new StatementEntry(statementName, stmtCounts)); + } + } + + public void RemoveStatement(String name) + { + ICollection removed = new HashSet(); + lock (_patternContexts) + { + foreach (StatementEntry context in _patternContexts) + { + if (context.StatementName.Equals(name)) + { + removed.Add(context); + } + } + _patternContexts.RemoveAll(removed); + } + } + + public bool TryIncreaseCount(EvalNode evalNode, AgentInstanceContext agentInstanceContext) + { + // test pool max + long newMax = _poolCount.IncrementAndGet(); + if (newMax > _maxPoolCountConfigured && _maxPoolCountConfigured >= 0) + { + var counts = Counts; + agentInstanceContext.StatementContext.ExceptionHandlingService.HandleCondition( + new ConditionPatternEngineSubexpressionMax(_maxPoolCountConfigured, counts), agentInstanceContext.StatementContext.EpStatementHandle); + if ((ExecutionPathDebugLog.IsEnabled) && + (Log.IsDebugEnabled && (ExecutionPathDebugLog.IsTimerDebugEnabled))) + { + PatternSubexpressionPoolStmtHandler stmtHandler = agentInstanceContext.StatementContext.PatternSubexpressionPoolSvc.StmtHandler; + String stmtName = agentInstanceContext.StatementContext.StatementName; + Log.Debug(".tryIncreaseCount For statement '" + stmtName + "' pool count overflow at " + newMax + + " statement count was " + stmtHandler.Count + " preventStart=" + _preventStart); + } + + if (_preventStart) + { + _poolCount.DecrementAndGet(); + return false; + } + + return true; + } + if ((ExecutionPathDebugLog.IsEnabled) && Log.IsDebugEnabled) + { + PatternSubexpressionPoolStmtHandler stmtHandler = agentInstanceContext.StatementContext.PatternSubexpressionPoolSvc.StmtHandler; + String stmtName = agentInstanceContext.StatementContext.StatementName; + Log.Debug(".tryIncreaseCount For statement '" + stmtName + "' pool count increases to " + newMax + + " statement count was " + stmtHandler.Count); + } + return true; + } + + // Relevant for recovery of state + public void ForceIncreaseCount(EvalNode evalNode, AgentInstanceContext agentInstanceContext) + { + long newMax = _poolCount.IncrementAndGet(); + if ((ExecutionPathDebugLog.IsEnabled) && Log.IsDebugEnabled) + { + PatternSubexpressionPoolStmtHandler stmtHandler = agentInstanceContext.StatementContext.PatternSubexpressionPoolSvc.StmtHandler; + String stmtName = agentInstanceContext.StatementContext.StatementName; + Log.Debug(".forceIncreaseCount For statement '" + stmtName + "' pool count increases to " + newMax + + " statement count was " + stmtHandler.Count); + } + } + + public void DecreaseCount(EvalNode evalNode, AgentInstanceContext agentInstanceContext) + { + long newMax = _poolCount.DecrementAndGet(); + if ((ExecutionPathDebugLog.IsEnabled) && Log.IsDebugEnabled) + { + PatternSubexpressionPoolStmtHandler stmtHandler = agentInstanceContext.StatementContext.PatternSubexpressionPoolSvc.StmtHandler; + String stmtName = agentInstanceContext.StatementContext.StatementName; + Log.Debug(".decreaseCount For statement '" + stmtName + "' pool count decreases to " + newMax + + " statement count was " + stmtHandler.Count); + } + } + + private IDictionary Counts + { + get + { + lock (_patternContexts) + { + IDictionary counts = new Dictionary(); + foreach (StatementEntry context in _patternContexts) + { + long? count = counts.Get(context.StatementName); + if (count == null) + { + count = 0L; + } + count += context.StmtCounts.Count; + counts.Put(context.StatementName, count); + } + return counts; + } + } + } + + #region Nested type: StatementEntry + + public class StatementEntry + { + public StatementEntry(String statementName, + PatternSubexpressionPoolStmtHandler stmtCounts) + { + StatementName = statementName; + StmtCounts = stmtCounts; + } + + public string StatementName { get; private set; } + public PatternSubexpressionPoolStmtHandler StmtCounts { get; private set; } + } + + #endregion + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/pattern/pool/PatternSubexpressionPoolStmtHandler.cs b/NEsper.Core/NEsper.Core/pattern/pool/PatternSubexpressionPoolStmtHandler.cs new file mode 100755 index 000000000..7974fd5fb --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/pool/PatternSubexpressionPoolStmtHandler.cs @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.pattern.pool +{ + public class PatternSubexpressionPoolStmtHandler + { + private int _count; + + public int Count + { + get { return _count; } + } + + public void DecreaseCount() + { + _count--; + if (_count < 0) + { + _count = 0; + } + } + + public void IncreaseCount() + { + _count++; + } + } +} diff --git a/NEsper.Core/NEsper.Core/pattern/pool/PatternSubexpressionPoolStmtSvc.cs b/NEsper.Core/NEsper.Core/pattern/pool/PatternSubexpressionPoolStmtSvc.cs new file mode 100755 index 000000000..2f391f7f0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/pattern/pool/PatternSubexpressionPoolStmtSvc.cs @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.pattern.pool +{ + public class PatternSubexpressionPoolStmtSvc + { + public PatternSubexpressionPoolStmtSvc(PatternSubexpressionPoolEngineSvc engineSvc, PatternSubexpressionPoolStmtHandler stmtHandler) + { + EngineSvc = engineSvc; + StmtHandler = stmtHandler; + } + + public PatternSubexpressionPoolEngineSvc EngineSvc { get; private set; } + + public PatternSubexpressionPoolStmtHandler StmtHandler { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/plugin/PlugInAggregationMultiFunctionAgentContext.cs b/NEsper.Core/NEsper.Core/plugin/PlugInAggregationMultiFunctionAgentContext.cs new file mode 100755 index 000000000..02513b264 --- /dev/null +++ b/NEsper.Core/NEsper.Core/plugin/PlugInAggregationMultiFunctionAgentContext.cs @@ -0,0 +1,24 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.plugin +{ + public class PlugInAggregationMultiFunctionAgentContext + { + public PlugInAggregationMultiFunctionAgentContext(IList childNodes) + { + ChildNodes = childNodes; + } + + public IList ChildNodes { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/plugin/PlugInAggregationMultiFunctionDeclarationContext.cs b/NEsper.Core/NEsper.Core/plugin/PlugInAggregationMultiFunctionDeclarationContext.cs new file mode 100755 index 000000000..07be622ae --- /dev/null +++ b/NEsper.Core/NEsper.Core/plugin/PlugInAggregationMultiFunctionDeclarationContext.cs @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.plugin +{ + /// + /// Context for use with provides + /// information about an aggregation function at the time of declaration. + /// + /// Declaration means when the aggregation function is discovered at the time of parsing an + /// EPL statement. Or when using statement object model then at the time of mapping the object + /// model to the internal statement representation. + /// + public class PlugInAggregationMultiFunctionDeclarationContext + { + /// Ctor. + /// provides the aggregation multi-function name + /// flag whether the "distinct" keyword was provided. + /// the engine URI + /// the configuration provided when the aggregation multi-functions where registered + public PlugInAggregationMultiFunctionDeclarationContext(String functionName, bool distinct, String engineURI, ConfigurationPlugInAggregationMultiFunction configuration) { + FunctionName = functionName; + IsDistinct = distinct; + EngineURI = engineURI; + Configuration = configuration; + } + + /// Returns a flag whether the "distinct" keyword was provided. + /// distinct flag + public bool IsDistinct { get; private set; } + + /// Returns the engine uri. + /// engine uri + public string EngineURI { get; private set; } + + /// Returns the aggregation function name. + /// function name + public string FunctionName { get; private set; } + + /// Returns the configuration provided when the aggregation multi-functions where registered. + /// configuration + public ConfigurationPlugInAggregationMultiFunction Configuration { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/plugin/PlugInAggregationMultiFunctionFactory.cs b/NEsper.Core/NEsper.Core/plugin/PlugInAggregationMultiFunctionFactory.cs new file mode 100755 index 000000000..80a6d5dad --- /dev/null +++ b/NEsper.Core/NEsper.Core/plugin/PlugInAggregationMultiFunctionFactory.cs @@ -0,0 +1,66 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.plugin +{ + /// + /// Record point for the extension API for aggregation multi-functions. + /// + /// This API allows adding one or more related aggregation functions that can share state, + /// share parameters or exhibit related behavior. + /// + /// + /// Please use + /// to register this factory class in the engine together with one or more function names. + /// + /// + /// The engine instantiates a single instance of this class at the time it encounters the first + /// aggregation multi-function in a given statement at the time of statement parsing or compilation + /// from statement object model. + /// + /// + /// At the time of statement parsing, each aggregation multi-function encountered during parsing of + /// EPL statement text results in an invocation to + /// . The + /// same upon statement compilation for statement object model. For multiple aggregation functions, + /// the order in which such calls occur is not well defined and should not be relied on by the implementation. + /// + /// + /// The engine invokes + /// at the time of expression node validation. Validation occurs after statement parsing and when type information + /// is established. For multiple aggregation functions, the order in which such calls occur is not well defined + /// and should not be relied on by the implementation. + /// + /// + /// Usually a single handler class can handle + /// the needs of all related aggregation functions. Usually you have a single handler class and return + /// one handler object for each aggregation function expression, where the handler object takes the + /// validation context as a parameter. Use multiple different handler classes when your aggregation + /// functions have sufficiently different execution contexts or behaviors. Your application may want + /// to use the expression and type information available in + /// to decide what behavior to provide. + /// + /// + public interface PlugInAggregationMultiFunctionFactory + { + /// + /// Called for each instance of use of any of the aggregation functions at declaration discovery time and before any expression validation takes place. + /// + /// context + void AddAggregationFunction(PlugInAggregationMultiFunctionDeclarationContext declarationContext); + + /// + /// Called for each instance of use of any of the aggregation functions at validation time after all declared aggregation have been added. + /// + /// validationContext + /// + /// handler for providing type information, accessor and provider factory + /// + PlugInAggregationMultiFunctionHandler ValidateGetHandler(PlugInAggregationMultiFunctionValidationContext validationContext); + } +} diff --git a/NEsper.Core/NEsper.Core/plugin/PlugInAggregationMultiFunctionHandler.cs b/NEsper.Core/NEsper.Core/plugin/PlugInAggregationMultiFunctionHandler.cs new file mode 100755 index 000000000..e96e2c316 --- /dev/null +++ b/NEsper.Core/NEsper.Core/plugin/PlugInAggregationMultiFunctionHandler.cs @@ -0,0 +1,115 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.agg.access; +using com.espertech.esper.epl.rettype; + +namespace com.espertech.esper.plugin +{ + /// + /// Part of the aggregation multi-function extension API, this class represents + /// one of more aggregation function expression instances. This class is responsible for providing + /// a state reader (called accessor) for returning value from aggregation state, and for + /// providing return type information of the accessor, and for providing state factory + /// information. + /// Note the information returned by must match the + /// value objects returned by . + /// + /// For example, assuming you have an EPL statement such as + /// select search(), query() from MyEvent + /// then you would likely use one handler class and two handler objects (one for search and one for query). + /// + public interface PlugInAggregationMultiFunctionHandler + { + /// + /// Returns the read function (an 'accessor'). + /// + /// Typically your application creates one accessor class + /// for each aggregation function name. So if you have two aggregation + /// functions such as "query" and "search" you would have two + /// accessor classes, one for "query" and one for "search". + /// + /// Each aggregation function as it occurs in an EPL statement + /// obtains its own accessor. Your application can + /// return the same accessor object for all aggregation functions, + /// or different accessor objects for each aggregation function. + /// + /// The objects returned by your accessor must match the + /// return type declared through . + /// + /// accessor + AggregationAccessor Accessor { get; } + + /// + /// Provide return type. + /// + /// The accessor return values must match the return type declared herein. + /// + /// Use to indicate that the accessor + /// returns a single value. The accessor should return the single value upon invocation of + /// . + /// The accessor should return a null value for all other accessor methods. + /// + /// Use to indicate that the accessor + /// returns a collection of events. The accessor should return a value in + /// . + /// The accessor can also return an array of underlying event objects in + /// . + /// The accessor should return a null value for all other accessor methods. + /// + /// Use to indicate that the accessor + /// returns a single event. The accessor should return a value in + /// . + /// The accessor can also return the underlying event object in + /// . + /// The accessor should return a null value for all other accessor methods. + /// + /// Use to indicate that the accessor + /// returns a collection of single values (scalar, object etc.). The accessor should return a Collection in + /// . + /// The accessor should return a null value for all other accessor methods. + /// + /// Use to indicate that the accessor + /// returns an array of single values. The accessor should return an array in + /// . + /// The accessor should return a null value for all other accessor methods. + /// + /// expression result type + EPType ReturnType { get; } + + /// + /// Return a state-key object that determines how the engine shares aggregation state + /// between multiple aggregation functions that may appear in the same EPL statement. + /// + /// The engine applies equals-semantics to determine state sharing. If + /// two instances are equal (implement hashCode and equals) + /// then the engine shares a single aggregation state instance for the two + /// aggregation function expressions. + /// + /// If your aggregation function never needs shared state + /// simple return new AggregationStateKey(){}. + /// + /// If your aggregation function always shares state + /// simple declare private static final AggregationStateKey MY_KEY = new AggregationStateKey() {};and return MY_KEY; (if using multiple handlers declare the key on the factory level). + /// + /// state key + AggregationStateKey AggregationStateUniqueKey { get; } + + /// + /// Return the state factory for the sharable aggregation function state. + /// + /// The engine only obtains a state factory once for all shared aggregation state. + /// + /// state factory + PlugInAggregationMultiFunctionStateFactory StateFactory { get; } + + AggregationAgent GetAggregationAgent(PlugInAggregationMultiFunctionAgentContext agentContext); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/plugin/PlugInAggregationMultiFunctionStateContext.cs b/NEsper.Core/NEsper.Core/plugin/PlugInAggregationMultiFunctionStateContext.cs new file mode 100755 index 000000000..0fff612a2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/plugin/PlugInAggregationMultiFunctionStateContext.cs @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.plugin +{ + /// + /// Context for use with + /// provides contextual information at the time an aggregation state is allocated. + /// + public class PlugInAggregationMultiFunctionStateContext + { + private readonly int _agentInstanceId; + private readonly Object _groupKey; + + /// Ctor. + /// agent instance id + /// group key, or null if there are no group-by criteria + public PlugInAggregationMultiFunctionStateContext(int agentInstanceId, Object groupKey) + { + _agentInstanceId = agentInstanceId; + _groupKey = groupKey; + } + + /// Returns the agent instance id. + /// context partition id + public int AgentInstanceId + { + get { return _agentInstanceId; } + } + + /// Returns the group key or null if no group-by criteria are defined + /// group key + public object GroupKey + { + get { return _groupKey; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/plugin/PlugInAggregationMultiFunctionStateFactory.cs b/NEsper.Core/NEsper.Core/plugin/PlugInAggregationMultiFunctionStateFactory.cs new file mode 100755 index 000000000..d75bddc2e --- /dev/null +++ b/NEsper.Core/NEsper.Core/plugin/PlugInAggregationMultiFunctionStateFactory.cs @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.agg.access; + +namespace com.espertech.esper.plugin +{ + /// State factory responsible for allocating a state object for each group when used with group-by. + public interface PlugInAggregationMultiFunctionStateFactory { + /// Return a new aggregation state holder for a given group (or ungrouped if there is no group-by). + /// context includes group key + /// state holder, cannot be a null value + AggregationState MakeAggregationState(PlugInAggregationMultiFunctionStateContext stateContext); + } + + public class ProxyPlugInAggregationMultiFunctionStateFactory : PlugInAggregationMultiFunctionStateFactory + { + public Func ProcMakeAggregationState; + + public AggregationState MakeAggregationState(PlugInAggregationMultiFunctionStateContext stateContext) + { + return ProcMakeAggregationState.Invoke(stateContext); + } + } +} diff --git a/NEsper.Core/NEsper.Core/plugin/PlugInAggregationMultiFunctionValidationContext.cs b/NEsper.Core/NEsper.Core/plugin/PlugInAggregationMultiFunctionValidationContext.cs new file mode 100755 index 000000000..4a7d78e5a --- /dev/null +++ b/NEsper.Core/NEsper.Core/plugin/PlugInAggregationMultiFunctionValidationContext.cs @@ -0,0 +1,137 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.table.mgmt; + +namespace com.espertech.esper.plugin +{ + /// + /// Context for use with provides + /// information about an aggregation function at the time of validation. + /// + /// At validation time the event type information, parameter expressions + /// and other statement-specific services are available. + /// + /// + public class PlugInAggregationMultiFunctionValidationContext + { + private readonly string _functionName; + private readonly EventType[] _eventTypes; + private readonly ExprNode[] _parameterExpressions; + private readonly string _engineURI; + private readonly string _statementName; + private readonly ExprValidationContext _validationContext; + private readonly ConfigurationPlugInAggregationMultiFunction _config; + private readonly TableMetadataColumnAggregation _optionalTableColumnAccessed; + private readonly IList _allParameterExpressions; + + public PlugInAggregationMultiFunctionValidationContext( + string functionName, + EventType[] eventTypes, + ExprNode[] parameterExpressions, + string engineURI, + string statementName, + ExprValidationContext validationContext, + ConfigurationPlugInAggregationMultiFunction config, + TableMetadataColumnAggregation optionalTableColumnAccessed, + IList allParameterExpressions) + { + _functionName = functionName; + _eventTypes = eventTypes; + _parameterExpressions = parameterExpressions; + _engineURI = engineURI; + _statementName = statementName; + _validationContext = validationContext; + _config = config; + _optionalTableColumnAccessed = optionalTableColumnAccessed; + _allParameterExpressions = allParameterExpressions; + } + + /// + /// Returns the aggregation function name + /// + /// aggregation function name + public string FunctionName + { + get { return _functionName; } + } + + /// + /// Returns the event types of all events in the select clause + /// + /// types + public EventType[] EventTypes + { + get { return _eventTypes; } + } + + /// + /// Returns positional parameters expressions to this aggregation function. + /// Use {@link #GetAllParameterExpressions()} for a list of all parameters including non-positional parameters. + /// + /// positional parameter expressions + public ExprNode[] ParameterExpressions + { + get { return _parameterExpressions; } + } + + /// + /// Returns the engine URI. + /// + /// engine URI. + public string EngineURI + { + get { return _engineURI; } + } + + /// + /// Returns the statement name. + /// + /// statement name + public string StatementName + { + get { return _statementName; } + } + + /// + /// Returns additional validation contextual services. + /// + /// validation context + public ExprValidationContext ValidationContext + { + get { return _validationContext; } + } + + /// + /// Returns the original configuration object for the aggregation multi-function + /// + /// config + public ConfigurationPlugInAggregationMultiFunction Config + { + get { return _config; } + } + + public TableMetadataColumnAggregation OptionalTableColumnAccessed + { + get { return _optionalTableColumnAccessed; } + } + + /// + /// Returns positional and non-positional parameters. + /// + /// all parameters + public IList AllParameterExpressions + { + get { return _allParameterExpressions; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/plugin/PlugInEventBeanFactory.cs b/NEsper.Core/NEsper.Core/plugin/PlugInEventBeanFactory.cs new file mode 100755 index 000000000..5a3bec6b7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/plugin/PlugInEventBeanFactory.cs @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using com.espertech.esper.client; + +namespace com.espertech.esper.plugin +{ + /// + /// Factory for creating event object wrapper for a plug-in event representation. + /// + /// Implementations typically reflect on the event object to be processed and decides on the proper + /// to assign. If the implementation finds that it cannot + /// handle the event object, it should return null. Returning null gives another instance of this + /// class as specified by the list of URI to handle the event object. + /// + /// Returns an event wrapper for the event object specific to the plug-in event representation or + /// using one of the built-in types, or null if the event object is unknown and cannot be handled. + /// + /// + /// is the event object to reflect upon and wrap + /// is the URI used originally for obtaining the event sender + /// + /// wrapped event object, or null if the event is of unknown type or content + /// + public delegate EventBean PlugInEventBeanFactory(Object theEvent, Uri resolutionURI); +} diff --git a/NEsper.Core/NEsper.Core/plugin/PlugInEventBeanReflectorContext.cs b/NEsper.Core/NEsper.Core/plugin/PlugInEventBeanReflectorContext.cs new file mode 100755 index 000000000..bd47b9519 --- /dev/null +++ b/NEsper.Core/NEsper.Core/plugin/PlugInEventBeanReflectorContext.cs @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.plugin +{ + /// + /// Context for use in to provide information to + /// help decide whether an event representation can handle the requested resolution + /// URI for creating event object wrappers. + /// + public class PlugInEventBeanReflectorContext + { + private readonly Uri resolutionURI; + + /// + /// Ctor. + /// + /// is the resolution URI provided as part of + public PlugInEventBeanReflectorContext(Uri uri) + { + this.resolutionURI = uri; + } + + /// Returns the resolution URI. + /// resolution URI + public Uri ResolutionURI + { + get { return resolutionURI; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/plugin/PlugInEventRepresentation.cs b/NEsper.Core/NEsper.Core/plugin/PlugInEventRepresentation.cs new file mode 100755 index 000000000..a16bcd3c2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/plugin/PlugInEventRepresentation.cs @@ -0,0 +1,109 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.plugin +{ + /// + /// Plug-in event representation that can dynamically create event types and event + /// instances based on information available elsewhere. + /// + /// A plug-in event representation can be useful when your application has existing + /// types that carry event metadata and event property values and your application + /// does not want to (or cannot) extract or transform such event metadata and event + /// data into one of the built-in event representations (objects, DataMap or XML DOM). + /// + /// + /// Further use of a plug-in event representation is to provide a faster or short-cut + /// access path to event data. For example, the access to XML event data through a StAX + /// Streaming API for XML (SAX) is known to be very efficient. + /// + /// + /// Further, a plug-in event representation can provide network lookup and general + /// abstraction of event typing and event sourcing. + /// + /// + /// Before use, an implementation of this interface must be registered via configuration. + /// Upon engine initialization, the engine invokes the method passing + /// configuration information. + /// + /// + /// When a plug-in event type name is registered via configuration (runtime or configuration + /// time), the engine first asks the implementation whether the type is accepted via + /// . If accepted, the engine follows with a call to + /// for creating and handling the type. + /// + /// + /// An implementation can participate in dynamic resolution of new (unseen) event type + /// names if the application configures the URI of the event representation, or a child URI + /// (parameters possible) via . + /// + /// + /// Last, see . An + /// event sender allows dynamic reflection on an incoming event object. At the time such + /// an event sender is obtained and a matching URI specified, the + /// method indicates that the event representation can + /// or cannot inspect events, and the returned is used by + /// the event sender to wrap event objects for processing. + /// + /// + public interface PlugInEventRepresentation + { + /// + /// Initializes the event representation. + /// + /// URI and optional configuration information + void Init(PlugInEventRepresentationContext eventRepresentationContext); + + /// + /// Returns true to indicate that the event representation can handle the requested event type. + /// + /// Called when a new plug-in event type and name is registered and the its resolution URI + /// matches or is a child URI of the event representation URI. + /// + /// Also called when a new EPL statement is created with an unseen event type name and the + /// URIs for resolution have been configured. + /// + /// provides the URI specified for resolving the type, and configuration INFO. + /// + /// true to accept the type, false such that another event representation may handle the type request + /// + bool AcceptsType(PlugInEventTypeHandlerContext acceptTypeContext); + + /// + /// Returns the event type handler that provides the event type and, upon request, event sender, + /// for this type. + /// + /// provides the URI specified for resolving the type, and configuration INFO. + /// provides event type and event sender + PlugInEventTypeHandler GetTypeHandler(PlugInEventTypeHandlerContext eventTypeContext); + + /// + /// For use with , returns + /// true if the event representation intends to provide event wrappers for event objects passed in. + /// + /// provides the URI specified for resolving the event object reflection + /// + /// true to accept the requested URI, false such that another event representation may handle the request + /// + bool AcceptsEventBeanResolution(PlugInEventBeanReflectorContext acceptBeanContext); + + /// + /// For use with , returns + /// the factory that can inspect event objects and provide an event wrapper. + /// + /// provides the URI specified for resolving the event object reflection + /// + /// true to accept the requested URI, false such that another event representation may handle the request + /// + PlugInEventBeanFactory GetEventBeanFactory(PlugInEventBeanReflectorContext eventBeanContext); + } +} diff --git a/NEsper.Core/NEsper.Core/plugin/PlugInEventRepresentationContext.cs b/NEsper.Core/NEsper.Core/plugin/PlugInEventRepresentationContext.cs new file mode 100755 index 000000000..ba1e8fbc1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/plugin/PlugInEventRepresentationContext.cs @@ -0,0 +1,56 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.events; + +namespace com.espertech.esper.plugin +{ + /// Context for use in to initialize an implementation. + public class PlugInEventRepresentationContext + { + private readonly EventAdapterService eventAdapterService; + private readonly Uri eventRepresentationRootURI; + private readonly Object representationInitializer; + + /// Ctor. + /// for creating further event types or wrapping event objects + /// URI of the event representation + /// initializer objects + public PlugInEventRepresentationContext(EventAdapterService eventAdapterService, + Uri eventRepresentationRootURI, + Object representationInitializer) + { + this.eventAdapterService = eventAdapterService; + this.eventRepresentationRootURI = eventRepresentationRootURI; + this.representationInitializer = representationInitializer; + } + + /// Ctor. + /// URI of event representation instance + public Uri EventRepresentationRootURI + { + get { return eventRepresentationRootURI; } + } + + /// Returns optional configuration for the event representation, or null if none supplied. An String XML document if the configuration was read from an XML file. + /// configuration, or null if none supplied + public object RepresentationInitializer + { + get { return representationInitializer; } + } + + /// Returns the service for for creating further event types or wrapping event objects. + /// event adapter service + public EventAdapterService EventAdapterService + { + get { return eventAdapterService; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/plugin/PlugInEventTypeHandler.cs b/NEsper.Core/NEsper.Core/plugin/PlugInEventTypeHandler.cs new file mode 100755 index 000000000..085acbc63 --- /dev/null +++ b/NEsper.Core/NEsper.Core/plugin/PlugInEventTypeHandler.cs @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using com.espertech.esper.client; +using com.espertech.esper.core.service; + +namespace com.espertech.esper.plugin +{ + /// + /// Provided once by an for any event type it creates. + /// + public interface PlugInEventTypeHandler + { + /// Returns the event type. + /// event type. + EventType EventType { get; } + + /// Returns a facility responsible for converting or wrapping event objects. + /// for sending events into the engine + /// sender + EventSender GetSender(EPRuntimeEventSender runtimeEventSender); + } + + public class ProxyPlugInEventTypeHandler : PlugInEventTypeHandler + { + public Func EventTypeFunc { get; set; } + public Func GetSenderFunc { get; set; } + + public EventType EventType + { + get { return EventTypeFunc(); } + } + + public EventSender GetSender(EPRuntimeEventSender runtimeEventSender) + { + return GetSenderFunc(runtimeEventSender); + } + } +} diff --git a/NEsper.Core/NEsper.Core/plugin/PlugInEventTypeHandlerContext.cs b/NEsper.Core/NEsper.Core/plugin/PlugInEventTypeHandlerContext.cs new file mode 100755 index 000000000..d62a713cb --- /dev/null +++ b/NEsper.Core/NEsper.Core/plugin/PlugInEventTypeHandlerContext.cs @@ -0,0 +1,52 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.plugin +{ + /// + /// Context for use in to provide information + /// to help decide whether an event representation can handle the requested event type. + /// + public class PlugInEventTypeHandlerContext + { + /// + /// Ctor. + /// + /// the URI specified for resolving the event type, may be a child URI of the event representation URI and may carry additional parameters + /// optional configuration for the type, or null if none supplied + /// the name of the event + /// The event type id. + public PlugInEventTypeHandlerContext(Uri eventTypeResolutionURI, Object typeInitializer, String eventTypeName, int eventTypeId) + { + EventTypeResolutionURI = eventTypeResolutionURI; + TypeInitializer = typeInitializer; + EventTypeName = eventTypeName; + EventTypeId = eventTypeId; + } + + /// + /// Gets or sets the event type id. + /// + /// The event type id. + public int EventTypeId { get; private set; } + + /// Returns the URI specified for resolving the event type, may be a child URI of the event representation URI and may carry additional parameters + /// URI + public Uri EventTypeResolutionURI { get; private set; } + + /// Returns optional configuration for the type, or null if none supplied. An String XML document if the configuration was read from an XML file. + /// configuration, or null if none supplied + public object TypeInitializer { get; private set; } + + /// Returns the name assigned to the event type. + /// name + public string EventTypeName { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/plugin/PluginLoader.cs b/NEsper.Core/NEsper.Core/plugin/PluginLoader.cs new file mode 100755 index 000000000..c78331568 --- /dev/null +++ b/NEsper.Core/NEsper.Core/plugin/PluginLoader.cs @@ -0,0 +1,36 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using com.espertech.esper.client; + +namespace com.espertech.esper.plugin +{ + /// + /// Interface for loaders of input/output adapters or any other adapter that may participate + /// in an engine lifecycle. + /// + public interface PluginLoader : IDisposable + { + /// + /// Initializes the adapter loader. + /// + /// Invoked before the engine instance is fully initialized. Thereby this is not the place + /// to look up an engine instance from + /// and use it. Use the {@link #postInitialize} method instead. + /// + /// the plug in context + void Init(PluginLoaderInitContext context); + + /// + /// Called after an engine instances has fully initialized and is already registered + /// with . + /// + void PostInitialize(); + } +} diff --git a/NEsper.Core/NEsper.Core/plugin/PluginLoaderInitContext.cs b/NEsper.Core/NEsper.Core/plugin/PluginLoaderInitContext.cs new file mode 100755 index 000000000..7d7a90110 --- /dev/null +++ b/NEsper.Core/NEsper.Core/plugin/PluginLoaderInitContext.cs @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat; + +namespace com.espertech.esper.plugin +{ + /// Context for plugin initialization. + public class PluginLoaderInitContext + { + /// Initialization context for use with the adapter loader. + /// is the loader name + /// is a set of properties from the configuration + /// is the SPI of the engine itself for sending events to + /// config xml + public PluginLoaderInitContext(String name, Properties properties, String configXml, EPServiceProvider epService) + { + Name = name; + Properties = properties; + ConfigXml = configXml; + EpServiceProvider = epService; + } + + /// Returns plugin name. + /// plugin name + public string Name { get; private set; } + + /// Returns plugin properties. + /// plugin properties + public Properties Properties { get; private set; } + + /// Returns plugin configuration XML, if any. + /// configuration XML + public string ConfigXml { get; private set; } + + /// Returns the engine loading the plugin. + /// engine + public EPServiceProvider EpServiceProvider { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/rowregex/EventRowRegexHelper.cs b/NEsper.Core/NEsper.Core/rowregex/EventRowRegexHelper.cs new file mode 100755 index 000000000..3cf342ef6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/rowregex/EventRowRegexHelper.cs @@ -0,0 +1,432 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.view; + +namespace com.espertech.esper.rowregex +{ + /// + /// Helper for match recognize. + /// + public class EventRowRegexHelper + { + public static EventRowRegexNFAViewService RecursiveFindRegexService(Viewable top) { + if (top is EventRowRegexNFAViewService) { + return (EventRowRegexNFAViewService) top; + } + + return top.Views + .Select(RecursiveFindRegexService) + .FirstOrDefault(); + } + + public static readonly IComparer END_STATE_COMPARATOR = new ProxyComparer + { + ProcCompare = (o1, o2) => + { + if (o1.MatchEndEventSeqNo > o2.MatchEndEventSeqNo) + return -1; + if (o1.MatchEndEventSeqNo < o2.MatchEndEventSeqNo) + return 1; + return 0; + } + }; + + /// + /// Inspect variables recursively. + /// + /// parent regex expression node + /// if the variable in the stack is multiple of single + /// single variables list + /// group variables list + public static void RecursiveInspectVariables(RowRegexExprNode parent, bool isMultiple, ISet variablesSingle, ISet variablesMultiple) + { + if (parent is RowRegexExprNodeNested) + { + var nested = (RowRegexExprNodeNested) parent; + foreach (var child in parent.ChildNodes) + { + RecursiveInspectVariables(child, nested.NFAType.IsMultipleMatches() || isMultiple, variablesSingle, variablesMultiple); + } + } + else if (parent is RowRegexExprNodeAlteration) + { + foreach (var childAlteration in parent.ChildNodes) + { + var singles = new LinkedHashSet(); + var multiples = new LinkedHashSet(); + + RecursiveInspectVariables(childAlteration, isMultiple, singles, multiples); + + variablesMultiple.AddAll(multiples); + variablesSingle.AddAll(singles); + } + variablesSingle.RemoveAll(variablesMultiple); + } + else if (parent is RowRegexExprNodeAtom) + { + var atom = (RowRegexExprNodeAtom) parent; + var name = atom.Tag; + if (variablesMultiple.Contains(name)) + { + return; + } + if (variablesSingle.Contains(name)) + { + variablesSingle.Remove(name); + variablesMultiple.Add(name); + return; + } + if (atom.NFAType.IsMultipleMatches()) + { + variablesMultiple.Add(name); + return; + } + if (isMultiple) + { + variablesMultiple.Add(name); + } + else + { + variablesSingle.Add(name); + } + } + else + { + foreach (var child in parent.ChildNodes) + { + RecursiveInspectVariables(child, isMultiple, variablesSingle, variablesMultiple); + } + } + } + + /// + /// Build a list of start states from the parent node. + /// + /// to build start state for + /// each variable and its expressions + /// variable name and its stream number + /// State of the expr requires multimatch. + /// strand of regex state nodes + public static RegexNFAStrandResult RecursiveBuildStartStates( + RowRegexExprNode parent, + IDictionary variableDefinitions, + IDictionary> variableStreams, + bool[] exprRequiresMultimatchState) + { + var nodeNumStack = new Stack(); + + var strand = RecursiveBuildStatesInternal( + parent, + variableDefinitions, + variableStreams, + nodeNumStack, + exprRequiresMultimatchState); + + // add end state + var end = new RegexNFAStateEnd(); + foreach (var endStates in strand.EndStates) + { + endStates.AddState(end); + } + + // assign node num as a counter + var nodeNumberFlat = 0; + foreach (var theBase in strand.AllStates) + { + theBase.NodeNumFlat = nodeNumberFlat++; + } + + return new RegexNFAStrandResult(new List(strand.StartStates), strand.AllStates); + } + + private static RegexNFAStrand RecursiveBuildStatesInternal( + RowRegexExprNode node, + IDictionary variableDefinitions, + IDictionary> variableStreams, + Stack nodeNumStack, + bool[] exprRequiresMultimatchState) + { + if (node is RowRegexExprNodeAlteration) + { + var nodeNum = 0; + + var cumulativeStartStates = new List(); + var cumulativeStates = new List(); + var cumulativeEndStates = new List(); + + var isPassthrough = false; + foreach (var child in node.ChildNodes) + { + nodeNumStack.Push(nodeNum); + var strand = RecursiveBuildStatesInternal(child, + variableDefinitions, + variableStreams, + nodeNumStack, + exprRequiresMultimatchState); + nodeNumStack.Pop(); + + cumulativeStartStates.AddAll(strand.StartStates); + cumulativeStates.AddAll(strand.AllStates); + cumulativeEndStates.AddAll(strand.EndStates); + if (strand.IsPassthrough) + { + isPassthrough = true; + } + + nodeNum++; + } + + return new RegexNFAStrand(cumulativeStartStates, cumulativeEndStates, cumulativeStates, isPassthrough); + } + else if (node is RowRegexExprNodeConcatenation) + { + var nodeNum = 0; + + var isPassthrough = true; + var cumulativeStates = new List(); + var strands = new RegexNFAStrand[node.ChildNodes.Count]; + + foreach (var child in node.ChildNodes) + { + nodeNumStack.Push(nodeNum); + strands[nodeNum] = RecursiveBuildStatesInternal(child, + variableDefinitions, + variableStreams, + nodeNumStack, + exprRequiresMultimatchState); + nodeNumStack.Pop(); + + cumulativeStates.AddAll(strands[nodeNum].AllStates); + if (!strands[nodeNum].IsPassthrough) + { + isPassthrough = false; + } + + nodeNum++; + } + + // determine start states: all states until the first non-passthrough start state + var startStates = new List(); + for (var i = 0; i < strands.Length; i++) + { + startStates.AddAll(strands[i].StartStates); + if (!strands[i].IsPassthrough) + { + break; + } + } + + // determine end states: all states from the back until the last non-passthrough end state + var endStates = new List(); + for (var i = strands.Length - 1; i >= 0; i--) + { + endStates.AddAll(strands[i].EndStates); + if (!strands[i].IsPassthrough) + { + break; + } + } + + // hook up the end state of each strand with the start states of each next strand + for (var i = strands.Length - 1; i >= 1; i--) + { + var current = strands[i]; + for (var j = i - 1; j >= 0; j--) + { + var prior = strands[j]; + + foreach (var endState in prior.EndStates) + { + foreach (var startState in current.StartStates) + { + endState.AddState(startState); + } + } + + if (!prior.IsPassthrough) + { + break; + } + } + } + + return new RegexNFAStrand(startStates, endStates, cumulativeStates, isPassthrough); + } + else if (node is RowRegexExprNodeNested) + { + var nested = (RowRegexExprNodeNested) node; + nodeNumStack.Push(0); + var strand = RecursiveBuildStatesInternal(node.ChildNodes[0], + variableDefinitions, + variableStreams, + nodeNumStack, + exprRequiresMultimatchState); + nodeNumStack.Pop(); + + var isPassthrough = strand.IsPassthrough || nested.NFAType.IsOptional(); + + // if this is a repeating node then pipe back each end state to each begin state + if (nested.NFAType.IsMultipleMatches()) + { + foreach (var endstate in strand.EndStates) + { + foreach (var startstate in strand.StartStates) + { + if (!endstate.NextStates.Contains(startstate)) + { + endstate.NextStates.Add(startstate); + } + } + } + } + return new RegexNFAStrand(strand.StartStates, strand.EndStates, strand.AllStates, isPassthrough); + } + else + { + var atom = (RowRegexExprNodeAtom) node; + + // assign stream number for single-variables for most direct expression eval; multiple-variable gets -1 + var streamNum = variableStreams.Get(atom.Tag).First; + var multiple = variableStreams.Get(atom.Tag).Second; + var expressionDef = variableDefinitions.Get(atom.Tag); + var exprRequiresMultimatch = exprRequiresMultimatchState[streamNum]; + + RegexNFAStateBase nextState; + if ((atom.NFAType == RegexNFATypeEnum.ZERO_TO_MANY) || (atom.NFAType == RegexNFATypeEnum.ZERO_TO_MANY_RELUCTANT)) + { + nextState = new RegexNFAStateZeroToMany(ToString(nodeNumStack), atom.Tag, streamNum, multiple, atom.NFAType.IsGreedy(), expressionDef, exprRequiresMultimatch); + } + else if ((atom.NFAType == RegexNFATypeEnum.ONE_TO_MANY) || (atom.NFAType == RegexNFATypeEnum.ONE_TO_MANY_RELUCTANT)) + { + nextState = new RegexNFAStateOneToMany(ToString(nodeNumStack), atom.Tag, streamNum, multiple, atom.NFAType.IsGreedy(), expressionDef, exprRequiresMultimatch); + } + else if ((atom.NFAType == RegexNFATypeEnum.ONE_OPTIONAL) || (atom.NFAType == RegexNFATypeEnum.ONE_OPTIONAL_RELUCTANT)) + { + nextState = new RegexNFAStateOneOptional(ToString(nodeNumStack), atom.Tag, streamNum, multiple, atom.NFAType.IsGreedy(), expressionDef, exprRequiresMultimatch); + } + else if (expressionDef == null) + { + nextState = new RegexNFAStateAnyOne(ToString(nodeNumStack), atom.Tag, streamNum, multiple); + } + else + { + nextState = new RegexNFAStateFilter(ToString(nodeNumStack), atom.Tag, streamNum, multiple, expressionDef, exprRequiresMultimatch); + } + + return new RegexNFAStrand( + Collections.SingletonList(nextState), + Collections.SingletonList(nextState), + Collections.SingletonList(nextState), + atom.NFAType.IsOptional()); + } + } + + private static String ToString(IEnumerable nodeNumStack) + { + var builder = new StringBuilder(); + var delimiter = ""; + foreach (int? atom in nodeNumStack) + { + builder.Append(delimiter); + builder.Append(Convert.ToString(atom)); + delimiter = "."; + } + return builder.ToString(); + } + + public static IDictionary> DetermineVisibility(RowRegexExprNode pattern) + { + IDictionary> map = new Dictionary>(); + var path = new ArrayDeque(); + RecursiveFindPatternAtoms(pattern, path, map); + return map; + } + + private static void RecursiveFindPatternAtoms(RowRegexExprNode parent, ArrayDeque path, IDictionary> map) + { + path.Add(parent); + foreach (var child in parent.ChildNodes) { + if (child is RowRegexExprNodeAtom) { + HandleAtom((RowRegexExprNodeAtom) child, path, map); + } + else { + RecursiveFindPatternAtoms(child, path, map); + } + } + path.RemoveLast(); + } + + private static void HandleAtom(RowRegexExprNodeAtom atom, IEnumerable path, IDictionary> map) + { + + var patharr = path.ToArray(); + ISet identifiers = null; + + for (var i = 0; i < patharr.Length; i++) { + var parent = patharr[i]; + if (!(parent is RowRegexExprNodeConcatenation)) { + continue; + } + + var concat = (RowRegexExprNodeConcatenation) parent; + int indexWithinConcat; + if (i == patharr.Length - 1) { + indexWithinConcat = parent.ChildNodes.IndexOf(atom); + } + else { + indexWithinConcat = parent.ChildNodes.IndexOf(patharr[i + 1]); + } + + if (identifiers == null && indexWithinConcat > 0) { + identifiers = new HashSet(); + } + + for (var j = 0; j < indexWithinConcat; j++) + { + var concatChildNode = concat.ChildNodes[j]; + RecursiveCollectAtomsWExclude(concatChildNode, identifiers, atom.Tag); + } + } + + if (identifiers == null) { + return; + } + + var existingVisibility = map.Get(atom.Tag); + if (existingVisibility == null) { + map.Put(atom.Tag, identifiers); + } + else { + existingVisibility.AddAll(identifiers); + } + } + + private static void RecursiveCollectAtomsWExclude(RowRegexExprNode node, ISet identifiers, String excludedTag) + { + if (node is RowRegexExprNodeAtom) { + var atom = (RowRegexExprNodeAtom) node; + if (!excludedTag.Equals(atom.Tag)) { + identifiers.Add(atom.Tag); + } + } + foreach (var child in node.ChildNodes) { + RecursiveCollectAtomsWExclude(child, identifiers, excludedTag); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/rowregex/EventRowRegexIteratorResult.cs b/NEsper.Core/NEsper.Core/rowregex/EventRowRegexIteratorResult.cs new file mode 100755 index 000000000..acfb00014 --- /dev/null +++ b/NEsper.Core/NEsper.Core/rowregex/EventRowRegexIteratorResult.cs @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +namespace com.espertech.esper.rowregex +{ + /// + /// Iteration result for row regex. + /// + internal class EventRowRegexIteratorResult + { + /// + /// Ctor. + /// + /// end states + /// seq num of event + internal EventRowRegexIteratorResult(IList endStates, int eventSequenceNum) + { + EndStates = endStates; + EventSequenceNum = eventSequenceNum; + } + + /// + /// Returns the end states + /// + /// + /// end states + /// + internal IList EndStates { get; private set; } + + /// + /// Returns the event seq num. + /// + /// + /// seq num + /// + internal int EventSequenceNum { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/rowregex/EventRowRegexNFAView.cs b/NEsper.Core/NEsper.Core/rowregex/EventRowRegexNFAView.cs new file mode 100755 index 000000000..6f098e547 --- /dev/null +++ b/NEsper.Core/NEsper.Core/rowregex/EventRowRegexNFAView.cs @@ -0,0 +1,1378 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.prev; +using com.espertech.esper.epl.spec; +using com.espertech.esper.events; +using com.espertech.esper.events.arr; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.rowregex +{ + /// + /// View for match recognize support. + /// + public class EventRowRegexNFAView + : ViewSupport + , EventRowRegexNFAViewService + , EventRowRegexNFAViewScheduleCallback + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private const bool IS_DEBUG = false; + + private static readonly IEnumerator NULL_ITERATOR = + EnumerationHelper.Empty(); + + private readonly EventRowRegexNFAViewFactory _factory; + private readonly MatchRecognizeSpec _matchRecognizeSpec; + private readonly bool _isUnbound; + private readonly bool _isIterateOnly; + private readonly bool _isCollectMultimatches; + private readonly bool _isTrackMaxStates; + + private readonly EventType _rowEventType; + private readonly AgentInstanceContext _agentInstanceContext; + private readonly AggregationServiceMatchRecognize _aggregationService; + + // for interval-handling + private readonly EventRowRegexNFAViewScheduler _scheduler; + private readonly bool _isOrTerminated; + + private readonly ExprEvaluator[] _columnEvaluators; + private readonly string[] _columnNames; + + private readonly RegexNFAState[] _startStates; + private readonly RegexNFAState[] _allStates; + + private readonly string[] _multimatchVariablesArray; + private readonly int[] _multimatchStreamNumToVariable; + private readonly int[] _multimatchVariableToStreamNum; + private readonly LinkedHashMap> _variableStreams; + private readonly IDictionary _streamsVariables; + private readonly int _numEventsEventsPerStreamDefine; + private readonly bool _isDefineAsksMultimatches; + private readonly ObjectArrayBackedEventBean _defineMultimatchEventBean; + + private readonly RegexPartitionStateRandomAccessGetter _prevGetter; + private readonly ObjectArrayBackedEventBean _compositeEventBean; + + // state + private RegexPartitionStateRepo _regexPartitionStateRepo; + private readonly LinkedHashSet _windowMatchedEventset; // this is NOT per partition - some optimizations are done for batch-processing (minus is out-of-sequence in partition) + + /// + /// Gets the regex partition state repo. + /// + /// + /// The regex partition state repo. + /// + protected internal RegexPartitionStateRepo RegexPartitionStateRepo + { + get { return _regexPartitionStateRepo; } + } + + /// + /// Ctor. + /// + /// The factory. + /// final event type + /// event type for input rows + /// specification + /// variables and their assigned stream number + /// stream number and the assigned variable + /// single variables + /// The agent instance context. + /// for handling the 'prev' function + /// handles aggregations + /// if set to true [is define asks multimatches]. + /// The define multimatch event bean. + /// State of the is expr requires multimatch. + /// true if unbound stream + /// true for iterate-only + /// if asking for multimatches + /// the expanded pattern node + /// the match recognition configuration + /// the scheduler + public EventRowRegexNFAView( + EventRowRegexNFAViewFactory factory, + ObjectArrayEventType compositeEventType, + EventType rowEventType, + MatchRecognizeSpec matchRecognizeSpec, + LinkedHashMap> variableStreams, + IDictionary streamsVariables, + ISet variablesSingle, + AgentInstanceContext agentInstanceContext, + IDictionary> callbacksPerIndex, + AggregationServiceMatchRecognize aggregationService, + bool isDefineAsksMultimatches, + ObjectArrayBackedEventBean defineMultimatchEventBean, + bool[] isExprRequiresMultimatchState, + bool isUnbound, + bool isIterateOnly, + bool isCollectMultimatches, + RowRegexExprNode expandedPatternNode, + ConfigurationEngineDefaults.MatchRecognizeConfig matchRecognizeConfig, + EventRowRegexNFAViewScheduler scheduler) + { + _factory = factory; + _matchRecognizeSpec = matchRecognizeSpec; + _isTrackMaxStates = matchRecognizeConfig != null && matchRecognizeConfig.MaxStates != null; + _compositeEventBean = new ObjectArrayEventBean(new object[variableStreams.Count], compositeEventType); + _rowEventType = rowEventType; + _variableStreams = variableStreams; + _scheduler = scheduler; + + // determine names of multimatching variables + if (variablesSingle.Count == variableStreams.Count) + { + _multimatchVariablesArray = new string[0]; + _multimatchStreamNumToVariable = new int[0]; + _multimatchVariableToStreamNum = new int[0]; + } + else + { + _multimatchVariablesArray = new string[variableStreams.Count - variablesSingle.Count]; + _multimatchVariableToStreamNum = new int[_multimatchVariablesArray.Length]; + _multimatchStreamNumToVariable = new int[variableStreams.Count]; + CompatExtensions.Fill(_multimatchStreamNumToVariable, -1); + var count = 0; + foreach (var entry in variableStreams) + { + if (entry.Value.Second) + { + var index = count; + _multimatchVariablesArray[index] = entry.Key; + _multimatchVariableToStreamNum[index] = entry.Value.First; + _multimatchStreamNumToVariable[entry.Value.First] = index; + count++; + } + } + } + + _streamsVariables = streamsVariables; + _aggregationService = aggregationService; + _isDefineAsksMultimatches = isDefineAsksMultimatches; + _defineMultimatchEventBean = defineMultimatchEventBean; + _numEventsEventsPerStreamDefine = isDefineAsksMultimatches ? variableStreams.Count + 1 : variableStreams.Count; + _isUnbound = isUnbound; + _isIterateOnly = isIterateOnly; + _agentInstanceContext = agentInstanceContext; + _isCollectMultimatches = isCollectMultimatches; + + if (matchRecognizeSpec.Interval != null) + { + agentInstanceContext.AddTerminationCallback(Stop); + _isOrTerminated = matchRecognizeSpec.Interval.IsOrTerminated; + } + else + { + _isOrTerminated = false; + } + + _windowMatchedEventset = new LinkedHashSet(); + + // handle "previous" function nodes (performance-optimized for direct index access) + if (!callbacksPerIndex.IsEmpty()) + { + // Build an array of indexes + var randomAccessIndexesRequested = new int[callbacksPerIndex.Count]; + var count = 0; + foreach (var entry in callbacksPerIndex) + { + randomAccessIndexesRequested[count] = entry.Key; + count++; + } + _prevGetter = new RegexPartitionStateRandomAccessGetter(randomAccessIndexesRequested, isUnbound); + } + else + { + _prevGetter = null; + } + + IDictionary variableDefinitions = new LinkedHashMap(); + foreach (var defineItem in matchRecognizeSpec.Defines) + { + variableDefinitions.Put(defineItem.Identifier, defineItem.Expression); + } + + // build states + var strand = EventRowRegexHelper.RecursiveBuildStartStates(expandedPatternNode, variableDefinitions, variableStreams, isExprRequiresMultimatchState); + _startStates = strand.StartStates.ToArray(); + _allStates = strand.AllStates.ToArray(); + + if (Log.IsDebugEnabled || IS_DEBUG) + { + Log.Info("NFA tree:\n" + EventRowRegexNFAViewUtil.Print(_startStates)); + } + + // create evaluators + _columnNames = new string[matchRecognizeSpec.Measures.Count]; + _columnEvaluators = new ExprEvaluator[matchRecognizeSpec.Measures.Count]; + var countX = 0; + foreach (var measureItem in matchRecognizeSpec.Measures) + { + _columnNames[countX] = measureItem.Name; + _columnEvaluators[countX] = measureItem.Expr.ExprEvaluator; + countX++; + } + + // create state repository + var repoFactory = agentInstanceContext.StatementContext.RegexPartitionStateRepoFactory; + var terminationStateCompare = new RegexPartitionTerminationStateComparator(_multimatchStreamNumToVariable, variableStreams); + if (_matchRecognizeSpec.PartitionByExpressions.IsEmpty()) + { + _regexPartitionStateRepo = repoFactory.MakeSingle(_prevGetter, agentInstanceContext, this, matchRecognizeSpec.Interval != null, terminationStateCompare); + } + else + { + var stateRepoGroupMeta = new RegexPartitionStateRepoGroupMeta(matchRecognizeSpec.Interval != null, + ExprNodeUtility.ToArray(matchRecognizeSpec.PartitionByExpressions), + ExprNodeUtility.GetEvaluators(matchRecognizeSpec.PartitionByExpressions), agentInstanceContext); + _regexPartitionStateRepo = repoFactory.MakePartitioned(_prevGetter, stateRepoGroupMeta, agentInstanceContext, this, matchRecognizeSpec.Interval != null, terminationStateCompare); + } + } + + public void Stop() + { + if (_scheduler != null) + { + _scheduler.RemoveSchedule(); + } + if (_isTrackMaxStates) + { + int size = _regexPartitionStateRepo.StateCount; + MatchRecognizeStatePoolStmtSvc poolSvc = _agentInstanceContext.StatementContext.MatchRecognizeStatePoolStmtSvc; + poolSvc.EngineSvc.DecreaseCount(_agentInstanceContext, size); + poolSvc.StmtHandler.DecreaseCount(size); + } + + _regexPartitionStateRepo.Dispose(); + } + + public void Init(EventBean[] newEvents) + { + UpdateInternal(newEvents, null, false); + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + UpdateInternal(newData, oldData, true); + } + + private void UpdateInternal(EventBean[] newData, EventBean[] oldData, bool postOutput) + { + if (_isIterateOnly) + { + if (oldData != null) + { + _regexPartitionStateRepo.RemoveOld(oldData, false, new bool[oldData.Length]); + } + if (newData != null) + { + foreach (var newEvent in newData) + { + var partitionState = _regexPartitionStateRepo.GetState(newEvent, true); + if ((partitionState != null) && (partitionState.RandomAccess != null)) + { + partitionState.RandomAccess.NewEventPrepare(newEvent); + } + } + } + return; + } + + if (oldData != null) + { + var isOutOfSequenceRemove = false; + + EventBean first = null; + if (!_windowMatchedEventset.IsEmpty()) + { + first = _windowMatchedEventset.First(); + } + + // remove old data, if found in set + var found = new bool[oldData.Length]; + var count = 0; + + // detect out-of-sequence removes + foreach (var oldEvent in oldData) + { + var removed = _windowMatchedEventset.Remove(oldEvent); + if (removed) + { + if ((oldEvent != first) && (first != null)) + { + isOutOfSequenceRemove = true; + } + found[count++] = true; + if (!_windowMatchedEventset.IsEmpty()) + { + first = _windowMatchedEventset.First(); + } + } + } + + // reset, rebuilding state + if (isOutOfSequenceRemove) + { + if (_isTrackMaxStates) + { + int size = _regexPartitionStateRepo.StateCount; + MatchRecognizeStatePoolStmtSvc poolSvc = _agentInstanceContext.StatementContext.MatchRecognizeStatePoolStmtSvc; + poolSvc.EngineSvc.DecreaseCount(_agentInstanceContext, size); + poolSvc.StmtHandler.DecreaseCount(size); + } + + _regexPartitionStateRepo = _regexPartitionStateRepo.CopyForIterate(true); + var parentEvents = Parent.GetEnumerator(); + EventRowRegexIteratorResult iteratorResult = ProcessIterator(true, parentEvents, _regexPartitionStateRepo); + _regexPartitionStateRepo.EventSequenceNum = iteratorResult.EventSequenceNum; + } + else + { + // remove old events from repository - and let the repository know there are no interesting events left + int numRemoved = _regexPartitionStateRepo.RemoveOld(oldData, _windowMatchedEventset.IsEmpty(), found); + + if (_isTrackMaxStates) + { + MatchRecognizeStatePoolStmtSvc poolSvc = _agentInstanceContext.StatementContext.MatchRecognizeStatePoolStmtSvc; + poolSvc.EngineSvc.DecreaseCount(_agentInstanceContext, numRemoved); + poolSvc.StmtHandler.DecreaseCount(numRemoved); + } + } + } + + if (newData == null) + { + return; + } + + IList endStates = new List(); + IList terminationStatesAll = null; + + foreach (var newEvent in newData) + { + var nextStates = new List(); + int eventSequenceNumber = _regexPartitionStateRepo.IncrementAndGetEventSequenceNum(); + + // get state holder for this event + var partitionState = _regexPartitionStateRepo.GetState(newEvent, true); + var currentStatesIterator = partitionState.CurrentStatesEnumerator; + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QRegEx(newEvent, partitionState); } + + if (partitionState.RandomAccess != null) + { + partitionState.RandomAccess.NewEventPrepare(newEvent); + } + + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled) || (IS_DEBUG)) + { + Log.Info("Evaluating event " + newEvent.Underlying + "\n" + + "current : " + EventRowRegexNFAViewUtil.PrintStates(partitionState.CurrentStatesForPrint, _streamsVariables, _variableStreams, _multimatchStreamNumToVariable)); + } + + var terminationStates = Step(false, currentStatesIterator, newEvent, nextStates, endStates, !_isUnbound, eventSequenceNumber, partitionState.OptionalKeys); + + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled) || (IS_DEBUG)) + { + Log.Info("Evaluated event " + newEvent.Underlying + "\n" + + "next : " + EventRowRegexNFAViewUtil.PrintStates(nextStates, _streamsVariables, _variableStreams, _multimatchStreamNumToVariable) + "\n" + + "end : " + EventRowRegexNFAViewUtil.PrintStates(endStates, _streamsVariables, _variableStreams, _multimatchStreamNumToVariable)); + } + + // add termination states, for use with interval and "or terminated" + if (terminationStates != null) + { + if (terminationStatesAll == null) + { + terminationStatesAll = terminationStates; + } + else + { + terminationStatesAll.AddAll(terminationStates); + } + } + + partitionState.CurrentStates = nextStates; + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().ARegEx(partitionState, endStates, terminationStates); } + } + + if (endStates.IsEmpty() && (!_isOrTerminated || terminationStatesAll == null)) + { + return; + } + + // perform inter-ranking and elimination of duplicate matches + if (!_matchRecognizeSpec.IsAllMatches) + { + endStates = RankEndStatesMultiPartition(endStates); + } + + // handle interval for the set of matches + if (_matchRecognizeSpec.Interval != null) + { + for (int ii = 0; ii < endStates.Count; ii++) + { + RegexNFAStateEntry endState = endStates[ii]; + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QRegIntervalState(endState, _variableStreams, _multimatchStreamNumToVariable, _agentInstanceContext.StatementContext.SchedulingService.Time); } + var partitionState = _regexPartitionStateRepo.GetState(endState.PartitionKey); + if (partitionState == null) + { + Log.Warn("Null partition state encountered, skipping row"); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().ARegIntervalState(false); } + continue; + } + + // determine whether to schedule + bool scheduleDelivery; + if (!_isOrTerminated) + { + scheduleDelivery = true; + } + else + { + // determine whether there can be more matches + if (endState.State.NextStates.Count == 1 && + endState.State.NextStates[0] is RegexNFAStateEnd) + { + scheduleDelivery = false; + } + else + { + scheduleDelivery = true; + } + } + + // only schedule if not an end-state or not or-terminated + if (scheduleDelivery) + { + var matchBeginTime = endState.MatchBeginEventTime; + var current = _agentInstanceContext.StatementContext.SchedulingService.Time; + var deltaFromStart = current - matchBeginTime; + var deltaUntil = + _matchRecognizeSpec.Interval.GetScheduleForwardDelta(current, _agentInstanceContext) - + deltaFromStart; + + if (_regexPartitionStateRepo.ScheduleState.ContainsKey(matchBeginTime)) + { + ScheduleCallback(deltaUntil, endState); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().ARegIntervalState(true); } + endStates.RemoveAt(ii--); + } + else + { + if (deltaFromStart < deltaUntil) + { + ScheduleCallback(deltaUntil, endState); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().ARegIntervalState(true); } + endStates.RemoveAt(ii--); + } + else + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().ARegIntervalState(false); } + } + } + } + else + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().ARegIntervalState(false); } + } + } + + // handle termination states - those that terminated the pattern and remove the callback + if (_isOrTerminated && terminationStatesAll != null) + { + foreach (var terminationState in terminationStatesAll) + { + var partitionState = _regexPartitionStateRepo.GetState(terminationState.PartitionKey); + if (partitionState == null) + { + Log.Warn("Null partition state encountered, skipping row"); + continue; + } + + RemoveScheduleAddEndState(terminationState, endStates); + } + + // rank + if (!_matchRecognizeSpec.IsAllMatches) + { + endStates = RankEndStatesMultiPartition(endStates); + } + } + + if (endStates.IsEmpty()) + { + return; + } + } + // handle skip for incremental mode + else if (_matchRecognizeSpec.Skip.Skip == MatchRecognizeSkipEnum.PAST_LAST_ROW) + { + var endStateIter = endStates.GetEnumerator(); + while (endStateIter.MoveNext()) + { + var endState = endStateIter.Current; + var partitionState = _regexPartitionStateRepo.GetState(endState.PartitionKey); + if (partitionState == null) + { + Log.Warn("Null partition state encountered, skipping row"); + continue; + } + + partitionState.CurrentStates.RemoveWhere( + state => state.MatchBeginEventSeqNo <= endState.MatchEndEventSeqNo); + + } + } + else if (_matchRecognizeSpec.Skip.Skip == MatchRecognizeSkipEnum.TO_NEXT_ROW) + { + var endStateIter = endStates.GetEnumerator(); + while (endStateIter.MoveNext()) + { + var endState = endStateIter.Current; + var partitionState = _regexPartitionStateRepo.GetState(endState.PartitionKey); + if (partitionState == null) + { + Log.Warn("Null partition state encountered, skipping row"); + continue; + } + + partitionState.CurrentStates.RemoveWhere( + state => state.MatchBeginEventSeqNo <= endState.MatchBeginEventSeqNo); + } + } + + var outBeans = new EventBean[endStates.Count]; + var countX = 0; + foreach (var endState in endStates) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QRegMeasure(endState, _variableStreams, _multimatchStreamNumToVariable); } + outBeans[countX] = GenerateOutputRow(endState); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().ARegMeasure(outBeans[countX]); } + countX++; + + // check partition state - if empty delete (no states and no random access) + if (endState.PartitionKey != null) + { + var state = _regexPartitionStateRepo.GetState(endState.PartitionKey); + if (state.IsEmptyCurrentState && state.RandomAccess == null) + { + _regexPartitionStateRepo.RemoveState(endState.PartitionKey); + } + } + } + + if (postOutput) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QRegOut(outBeans); } + UpdateChildren(outBeans, null); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().ARegOut(); } + } + } + + private RegexNFAStateEntry RankEndStates(IList endStates) + { + // sort by end-event descending (newest first) + endStates.SortInPlace(EventRowRegexHelper.END_STATE_COMPARATOR); + + // find the earliest begin-event + RegexNFAStateEntry found = null; + int min = int.MaxValue; + var multipleMinimums = false; + foreach (var state in endStates) + { + if (state.MatchBeginEventSeqNo < min) + { + found = state; + min = state.MatchBeginEventSeqNo; + } + else if (state.MatchBeginEventSeqNo == min) + { + multipleMinimums = true; + } + } + + if (!multipleMinimums) + { + Collections.SingletonList(found); + } + + // compare greedy counts + int[] best = null; + found = null; + foreach (var state in endStates) + { + if (state.MatchBeginEventSeqNo != min) + { + continue; + } + if (best == null) + { + best = state.GreedyCountPerState; + found = state; + } + else + { + int[] current = state.GreedyCountPerState; + if (Compare(current, best)) + { + best = current; + found = state; + } + } + } + + return found; + } + + private bool Compare(int[] current, int[] best) + { + foreach (var state in _allStates) + { + if (state.IsGreedy == null) + { + continue; + } + if (state.IsGreedy.Value) + { + if (current[state.NodeNumFlat] > best[state.NodeNumFlat]) + { + return true; + } + } + else + { + if (current[state.NodeNumFlat] < best[state.NodeNumFlat]) + { + return true; + } + } + } + + return false; + } + + private EventRowRegexIteratorResult ProcessIterator( + bool isOutOfSeqDelete, + IEnumerator events, + RegexPartitionStateRepo regexPartitionStateRepo) + { + IList endStates = new List(); + IEnumerator currentStates; + var eventSequenceNumber = 0; + + while (events.MoveNext()) + { + var nextStates = new List(); + var theEvent = events.Current; + eventSequenceNumber++; + + var partitionState = regexPartitionStateRepo.GetState(theEvent, false); + currentStates = partitionState.CurrentStatesEnumerator; + + if (partitionState.RandomAccess != null) + { + partitionState.RandomAccess.ExistingEventPrepare(theEvent); + } + + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled) || (IS_DEBUG)) + { + Log.Info("Evaluating event " + theEvent.Underlying + "\n" + + "current : " + EventRowRegexNFAViewUtil.PrintStates(partitionState.CurrentStatesForPrint, _streamsVariables, _variableStreams, _multimatchStreamNumToVariable)); + } + + Step(!isOutOfSeqDelete, currentStates, theEvent, nextStates, endStates, false, eventSequenceNumber, partitionState.OptionalKeys); + + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled) || (IS_DEBUG)) + { + Log.Info("Evaluating event " + theEvent.Underlying + "\n" + + "next : " + EventRowRegexNFAViewUtil.PrintStates(nextStates, _streamsVariables, _variableStreams, _multimatchStreamNumToVariable) + "\n" + + "end : " + EventRowRegexNFAViewUtil.PrintStates(endStates, _streamsVariables, _variableStreams, _multimatchStreamNumToVariable)); + } + + partitionState.CurrentStates = nextStates; + } + + return new EventRowRegexIteratorResult(endStates, eventSequenceNumber); + } + + public override EventType EventType + { + get { return _rowEventType; } + } + + public override IEnumerator GetEnumerator() + { + if (_isUnbound) + { + return NULL_ITERATOR; + } + + IEnumerator it = Parent.GetEnumerator(); + + var regexPartitionStateRepoNew = _regexPartitionStateRepo.CopyForIterate(false); + + var iteratorResult = ProcessIterator(false, it, regexPartitionStateRepoNew); + var endStates = iteratorResult.EndStates; + if (endStates.IsEmpty()) + { + return NULL_ITERATOR; + } + else + { + endStates = RankEndStatesMultiPartition(endStates); + } + + var output = endStates.Select(GenerateOutputRow).ToList(); + return output.GetEnumerator(); + } + + public void Accept(EventRowRegexNFAViewServiceVisitor visitor) + { + _regexPartitionStateRepo.Accept(visitor); + } + + private IList RankEndStatesMultiPartition(IList endStates) + { + if (endStates.IsEmpty()) + { + return endStates; + } + if (endStates.Count == 1) + { + return endStates; + } + + // unpartitioned case - + if (_matchRecognizeSpec.PartitionByExpressions.IsEmpty()) + { + return RankEndStatesWithinPartitionByStart(endStates); + } + + // partitioned case - structure end states by partition + IDictionary perPartition = new LinkedHashMap(); + foreach (var endState in endStates) + { + var value = perPartition.Get(endState.PartitionKey); + if (value == null) + { + perPartition.Put(endState.PartitionKey, endState); + } + else if (value is IList) + { + var entries = (IList)value; + entries.Add(endState); + } + else + { + IList entries = new List(); + entries.Add((RegexNFAStateEntry)value); + entries.Add(endState); + perPartition.Put(endState.PartitionKey, entries); + } + } + + List finalEndStates = new List(); + foreach (var entry in perPartition) + { + if (entry.Value is RegexNFAStateEntry) + { + finalEndStates.Add((RegexNFAStateEntry)entry.Value); + } + else + { + var entries = (IList)entry.Value; + finalEndStates.AddAll(RankEndStatesWithinPartitionByStart(entries)); + } + } + return finalEndStates; + } + + private IList RankEndStatesWithinPartitionByStart(IList endStates) + { + if (endStates.IsEmpty()) + { + return endStates; + } + if (endStates.Count == 1) + { + return endStates; + } + + var endStatesPerBeginEvent = new SortedDictionary(); + foreach (var entry in endStates) + { + int beginNum = entry.MatchBeginEventSeqNo; + var value = endStatesPerBeginEvent.Get(beginNum); + if (value == null) + { + endStatesPerBeginEvent.Put(beginNum, entry); + } + else if (value is IList) + { + var entries = (IList)value; + entries.Add(entry); + } + else + { + List entries = new List(); + entries.Add((RegexNFAStateEntry)value); + entries.Add(entry); + endStatesPerBeginEvent.Put(beginNum, entries); + } + } + + if (endStatesPerBeginEvent.Count == 1) + { + var endStatesUnranked = (List)endStatesPerBeginEvent.Values.First(); + if (_matchRecognizeSpec.IsAllMatches) + { + return endStatesUnranked; + } + var chosen = RankEndStates(endStatesUnranked); + return Collections.SingletonList(chosen); + } + + IList endStatesRanked = new List(); + ICollection keyset = endStatesPerBeginEvent.Keys; + var keys = keyset.ToArray(); + foreach (var key in keys) + { + var value = endStatesPerBeginEvent.Delete(key); + if (value == null) + { + continue; + } + + RegexNFAStateEntry entryTaken; + if (value is IList) + { + var endStatesUnranked = (IList)value; + if (endStatesUnranked.IsEmpty()) + { + continue; + } + entryTaken = RankEndStates(endStatesUnranked); + + if (_matchRecognizeSpec.IsAllMatches) + { + endStatesRanked.AddAll(endStatesUnranked); // we take all matches and don't rank except to determine skip-past + } + else + { + endStatesRanked.Add(entryTaken); + } + } + else + { + entryTaken = (RegexNFAStateEntry)value; + endStatesRanked.Add(entryTaken); + } + // could be null as removals take place + + if (entryTaken != null) + { + if (_matchRecognizeSpec.Skip.Skip == MatchRecognizeSkipEnum.PAST_LAST_ROW) + { + var skipPastRow = entryTaken.MatchEndEventSeqNo; + RemoveSkippedEndStates(endStatesPerBeginEvent, skipPastRow); + } + else if (_matchRecognizeSpec.Skip.Skip == MatchRecognizeSkipEnum.TO_NEXT_ROW) + { + var skipPastRow = entryTaken.MatchBeginEventSeqNo; + RemoveSkippedEndStates(endStatesPerBeginEvent, skipPastRow); + } + } + } + + return endStatesRanked; + } + + private void RemoveSkippedEndStates(IDictionary endStatesPerEndEvent, int skipPastRow) + { + foreach (var entry in endStatesPerEndEvent.ToList()) + { + var value = entry.Value; + if (value is IList) + { + var endStatesUnranked = (IList)value; + + endStatesUnranked.RemoveWhere( + endState => endState.MatchBeginEventSeqNo <= skipPastRow); + } + else + { + var endState = (RegexNFAStateEntry)value; + if (endState.MatchBeginEventSeqNo <= skipPastRow) + { + endStatesPerEndEvent.Put(entry.Key, null); + } + } + } + } + + private IList Step( + bool skipTrackMaxState, + IEnumerator currentStatesIterator, + EventBean theEvent, + IList nextStates, + IList endStates, + bool isRetainEventSet, + int currentEventSequenceNumber, + Object partitionKey) + { + IList terminationStates = null; // always null or a list of entries (no singleton list) + + // handle current state matching + while (currentStatesIterator.MoveNext()) + { + RegexNFAStateEntry currentState = currentStatesIterator.Current; + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QRegExState(currentState, _variableStreams, _multimatchStreamNumToVariable); } + + if (_isTrackMaxStates && !skipTrackMaxState) + { + MatchRecognizeStatePoolStmtSvc poolSvc = _agentInstanceContext.StatementContext.MatchRecognizeStatePoolStmtSvc; + poolSvc.EngineSvc.DecreaseCount(_agentInstanceContext); + poolSvc.StmtHandler.DecreaseCount(); + } + + var eventsPerStream = currentState.EventsPerStream; + var currentStateStreamNum = currentState.State.StreamNum; + eventsPerStream[currentStateStreamNum] = theEvent; + if (_isDefineAsksMultimatches) + { + eventsPerStream[_numEventsEventsPerStreamDefine - 1] = GetMultimatchState(currentState); + } + + if (currentState.State.Matches(eventsPerStream, _agentInstanceContext)) + { + if (isRetainEventSet) + { + _windowMatchedEventset.Add(theEvent); + } + var nextStatesFromHere = currentState.State.NextStates; + + // save state for each next state + var copy = nextStatesFromHere.Count > 1; + foreach (var next in nextStatesFromHere) + { + var eventsForState = eventsPerStream; + var multimatches = currentState.OptionalMultiMatches; + int[] greedyCounts = currentState.GreedyCountPerState; + + if (copy) + { + eventsForState = new EventBean[eventsForState.Length]; + Array.Copy(eventsPerStream, 0, eventsForState, 0, eventsForState.Length); + + var greedyCountsCopy = new int[greedyCounts.Length]; + Array.Copy(greedyCounts, 0, greedyCountsCopy, 0, greedyCounts.Length); + greedyCounts = greedyCountsCopy; + + if (_isCollectMultimatches) + { + multimatches = DeepCopy(multimatches); + } + } + + if ((_isCollectMultimatches) && (currentState.State.IsMultiple)) + { + multimatches = AddTag(currentState.State.StreamNum, theEvent, multimatches); + eventsForState[currentStateStreamNum] = null; // remove event from evaluation list + } + + if ((currentState.State.IsGreedy != null) && (currentState.State.IsGreedy.Value)) + { + greedyCounts[currentState.State.NodeNumFlat]++; + } + + var entry = new RegexNFAStateEntry(currentState.MatchBeginEventSeqNo, currentState.MatchBeginEventTime, currentState.State, eventsForState, greedyCounts, multimatches, partitionKey); + if (next is RegexNFAStateEnd) + { + entry.MatchEndEventSeqNo = currentEventSequenceNumber; + endStates.Add(entry); + } + else + { + if (_isTrackMaxStates && !skipTrackMaxState) + { + var poolSvc = _agentInstanceContext.StatementContext.MatchRecognizeStatePoolStmtSvc; + var allow = poolSvc.EngineSvc.TryIncreaseCount(_agentInstanceContext); + if (allow) + { + poolSvc.StmtHandler.IncreaseCount(); + entry.State = next; + nextStates.Add(entry); + } + } + else + { + entry.State = next; + nextStates.Add(entry); + } + } + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().ARegExState(nextStates, _variableStreams, _multimatchStreamNumToVariable); } + } + // when not-matches + else + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().ARegExState(Collections.GetEmptyList(), _variableStreams, _multimatchStreamNumToVariable); } + + // determine interval and or-terminated + if (_isOrTerminated) + { + eventsPerStream[currentStateStreamNum] = null; // deassign + var nextStatesFromHere = currentState.State.NextStates; + + // save state for each next state + RegexNFAState theEndState = null; + foreach (var next in nextStatesFromHere) + { + if (next is RegexNFAStateEnd) + { + theEndState = next; + } + } + if (theEndState != null) + { + var entry = new RegexNFAStateEntry(currentState.MatchBeginEventSeqNo, currentState.MatchBeginEventTime, theEndState, eventsPerStream, currentState.GreedyCountPerState, currentState.OptionalMultiMatches, partitionKey); + if (terminationStates == null) + { + terminationStates = new List(); + } + terminationStates.Add(entry); + } + } + } + } + + // handle start states for the event + foreach (RegexNFAState startState in _startStates) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QRegExStateStart(startState, _variableStreams, _multimatchStreamNumToVariable); } + + var eventsPerStream = new EventBean[_numEventsEventsPerStreamDefine]; + int currentStateStreamNum = startState.StreamNum; + eventsPerStream[currentStateStreamNum] = theEvent; + + if (startState.Matches(eventsPerStream, _agentInstanceContext)) + { + if (isRetainEventSet) + { + _windowMatchedEventset.Add(theEvent); + } + var nextStatesFromHere = startState.NextStates; + + // save state for each next state + var copy = nextStatesFromHere.Count > 1; + foreach (RegexNFAState next in nextStatesFromHere) + { + if (_isTrackMaxStates && !skipTrackMaxState) + { + var poolSvc = _agentInstanceContext.StatementContext.MatchRecognizeStatePoolStmtSvc; + var allow = poolSvc.EngineSvc.TryIncreaseCount(_agentInstanceContext); + if (!allow) + { + continue; + } + poolSvc.StmtHandler.IncreaseCount(); + } + + var eventsForState = eventsPerStream; + var multimatches = _isCollectMultimatches ? new MultimatchState[_multimatchVariablesArray.Length] : null; + var greedyCounts = new int[_allStates.Length]; + + if (copy) + { + eventsForState = new EventBean[eventsForState.Length]; + Array.Copy(eventsPerStream, 0, eventsForState, 0, eventsForState.Length); + + var greedyCountsCopy = new int[greedyCounts.Length]; + Array.Copy(greedyCounts, 0, greedyCountsCopy, 0, greedyCounts.Length); + greedyCounts = greedyCountsCopy; + } + + if ((_isCollectMultimatches) && (startState.IsMultiple)) + { + multimatches = AddTag(startState.StreamNum, theEvent, multimatches); + eventsForState[currentStateStreamNum] = null; // remove event from evaluation list + } + + if ((startState.IsGreedy != null) && (startState.IsGreedy.Value)) + { + greedyCounts[startState.NodeNumFlat]++; + } + + long time = 0; + if (_matchRecognizeSpec.Interval != null) + { + time = _agentInstanceContext.StatementContext.SchedulingService.Time; + } + + var entry = new RegexNFAStateEntry(currentEventSequenceNumber, time, startState, eventsForState, greedyCounts, multimatches, partitionKey); + if (next is RegexNFAStateEnd) + { + entry.MatchEndEventSeqNo = currentEventSequenceNumber; + endStates.Add(entry); + } + else + { + entry.State = next; + nextStates.Add(entry); + } + } + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().ARegExStateStart(nextStates, _variableStreams, _multimatchStreamNumToVariable); } + } + + return terminationStates; // only for immediate use, not for scheduled use as no copy of state + } + + private ObjectArrayBackedEventBean GetMultimatchState(RegexNFAStateEntry currentState) + { + if (currentState.OptionalMultiMatches == null || !currentState.State.IsExprRequiresMultimatchState) + { + return null; + } + var props = _defineMultimatchEventBean.Properties; + var states = currentState.OptionalMultiMatches; + for (var i = 0; i < props.Length; i++) + { + var state = states[i]; + if (state == null) + { + props[i] = null; + } + else + { + props[i] = state.ShrinkEventArray; + } + } + return _defineMultimatchEventBean; + } + + private MultimatchState[] DeepCopy(MultimatchState[] multimatchStates) + { + if (multimatchStates == null) + { + return null; + } + + var copy = new MultimatchState[multimatchStates.Length]; + for (var i = 0; i < copy.Length; i++) + { + if (multimatchStates[i] != null) + { + copy[i] = new MultimatchState(multimatchStates[i]); + } + } + + return copy; + } + + private MultimatchState[] AddTag(int streamNum, EventBean theEvent, MultimatchState[] multimatches) + { + if (multimatches == null) + { + multimatches = new MultimatchState[_multimatchVariablesArray.Length]; + } + + var index = _multimatchStreamNumToVariable[streamNum]; + var state = multimatches[index]; + if (state == null) + { + multimatches[index] = new MultimatchState(theEvent); + return multimatches; + } + + multimatches[index].Add(theEvent); + return multimatches; + } + + private EventBean GenerateOutputRow(RegexNFAStateEntry entry) + { + var rowDataRaw = _compositeEventBean.Properties; + + // we first generate a raw row of for each variable name. + foreach (var variableDef in _variableStreams) + { + if (!variableDef.Value.Second) + { + int index = variableDef.Value.First; + rowDataRaw[index] = entry.EventsPerStream[index]; + } + } + if (_aggregationService != null) + { + _aggregationService.ClearResults(); + } + if (entry.OptionalMultiMatches != null) + { + var multimatchState = entry.OptionalMultiMatches; + for (var i = 0; i < multimatchState.Length; i++) + { + if (multimatchState[i] == null) + { + rowDataRaw[_multimatchVariableToStreamNum[i]] = null; + continue; + } + EventBean[] multimatchEvents = multimatchState[i].ShrinkEventArray; + rowDataRaw[_multimatchVariableToStreamNum[i]] = multimatchEvents; + + if (_aggregationService != null) + { + var eventsPerStream = entry.EventsPerStream; + var streamNum = _multimatchVariableToStreamNum[i]; + + foreach (var multimatchEvent in multimatchEvents) + { + eventsPerStream[streamNum] = multimatchEvent; + _aggregationService.ApplyEnter(eventsPerStream, streamNum, _agentInstanceContext); + } + } + } + } + else + { + foreach (var index in _multimatchVariableToStreamNum) + { + rowDataRaw[index] = null; + } + } + + IDictionary row = new Dictionary(); + var columnNum = 0; + var eventsPerStreamX = new EventBean[1]; + foreach (var expression in _columnEvaluators) + { + eventsPerStreamX[0] = _compositeEventBean; + var result = expression.Evaluate(new EvaluateParams(eventsPerStreamX, true, _agentInstanceContext)); + row.Put(_columnNames[columnNum], result); + columnNum++; + } + + return _agentInstanceContext.StatementContext.EventAdapterService.AdapterForTypedMap(row, _rowEventType); + } + + private void ScheduleCallback(long timeDelta, RegexNFAStateEntry endState) + { + var matchBeginTime = endState.MatchBeginEventTime; + if (_regexPartitionStateRepo.ScheduleState.IsEmpty()) + { + _regexPartitionStateRepo.ScheduleState.PutOrAdd(matchBeginTime, endState); + _scheduler.AddSchedule(timeDelta); + } + else + { + var newEntry = _regexPartitionStateRepo.ScheduleState.PutOrAdd(matchBeginTime, endState); + if (newEntry) + { + long currentFirstKey = _regexPartitionStateRepo.ScheduleState.FirstKey(); + if (currentFirstKey > matchBeginTime) + { + _scheduler.ChangeSchedule(timeDelta); + } + } + } + } + + private void RemoveScheduleAddEndState(RegexNFAStateEntry terminationState, IList foundEndStates) + { + var matchBeginTime = terminationState.MatchBeginEventTime; + var removedOne = _regexPartitionStateRepo.ScheduleState.FindRemoveAddToList(matchBeginTime, terminationState, foundEndStates); + if (removedOne && _regexPartitionStateRepo.ScheduleState.IsEmpty()) + { + _scheduler.RemoveSchedule(); + } + } + + public void Triggered() + { + var currentTime = _agentInstanceContext.StatementContext.SchedulingService.Time; + long intervalMSec = _matchRecognizeSpec.Interval.GetScheduleBackwardDelta(currentTime, _agentInstanceContext); + if (_regexPartitionStateRepo.ScheduleState.IsEmpty()) + { + return; + } + + IList indicatables = new List(); + while (true) + { + var firstKey = _regexPartitionStateRepo.ScheduleState.FirstKey(); + var cutOffTime = currentTime - intervalMSec; + if (firstKey > cutOffTime) + { + break; + } + + _regexPartitionStateRepo.ScheduleState.RemoveAddRemoved(firstKey, indicatables); + if (_regexPartitionStateRepo.ScheduleState.IsEmpty()) + { + break; + } + } + + // schedule next + if (!_regexPartitionStateRepo.ScheduleState.IsEmpty()) + { + long msecAfterCurrentTime = _regexPartitionStateRepo.ScheduleState.FirstKey() + intervalMSec - _agentInstanceContext.StatementContext.SchedulingService.Time; + _scheduler.AddSchedule(msecAfterCurrentTime); + } + + if (!_matchRecognizeSpec.IsAllMatches) + { + indicatables = RankEndStatesMultiPartition(indicatables); + } + + var outBeans = new EventBean[indicatables.Count]; + var count = 0; + foreach (var endState in indicatables) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QRegMeasure(endState, _variableStreams, _multimatchStreamNumToVariable); } + outBeans[count] = GenerateOutputRow(endState); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().ARegMeasure(outBeans[count]); } + count++; + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QRegOut(outBeans); } + UpdateChildren(outBeans, null); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().ARegOut(); } + } + + public RegexExprPreviousEvalStrategy PreviousEvaluationStrategy + { + get { return _prevGetter; } + } + + public EventRowRegexNFAViewFactory Factory + { + get { return _factory; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/rowregex/EventRowRegexNFAViewFactory.cs b/NEsper.Core/NEsper.Core/rowregex/EventRowRegexNFAViewFactory.cs new file mode 100755 index 000000000..d59eddb74 --- /dev/null +++ b/NEsper.Core/NEsper.Core/rowregex/EventRowRegexNFAViewFactory.cs @@ -0,0 +1,616 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.client.annotation; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.prev; +using com.espertech.esper.epl.expression.visitor; +using com.espertech.esper.epl.spec; +using com.espertech.esper.events; +using com.espertech.esper.events.arr; +using com.espertech.esper.util; +using com.espertech.esper.view; + +namespace com.espertech.esper.rowregex +{ + /// + /// View factory for match-recognize view. + /// + public class EventRowRegexNFAViewFactory : ViewFactorySupport + { + private readonly MatchRecognizeSpec _matchRecognizeSpec; + private readonly LinkedHashMap> _variableStreams; + private readonly IDictionary _streamVariables; + private readonly ISet _variablesSingle; + private readonly ObjectArrayEventType _compositeEventType; + private readonly EventType _rowEventType; + private readonly AggregationServiceMatchRecognize _aggregationService; + private readonly IList _aggregationExpressions; + private readonly SortedDictionary> _callbacksPerIndex = new SortedDictionary>(); + private readonly bool _isUnbound; + private readonly bool _isIterateOnly; + private readonly bool _isCollectMultimatches; + private readonly bool _isDefineAsksMultimatches; + private readonly ObjectArrayBackedEventBean _defineMultimatchEventBean; + private readonly bool[] _isExprRequiresMultimatchState; + private readonly RowRegexExprNode _expandedPatternNode; + private readonly ConfigurationEngineDefaults.MatchRecognizeConfig _matchRecognizeConfig; + + /// + /// Ctor. + /// + /// views + /// specification + /// The agent instance context. + /// true for unbound stream + /// annotations + /// + /// Variable ' + defineItem.Identifier + ' has already been defined + /// or + /// An aggregate function may not appear in a DEFINE clause + /// or + /// Failed to validate condition expression for variable ' + defineItem.Identifier + ': + ex.Message + /// or + /// Aggregation functions in the measure-clause must only refer to properties of exactly one group variable returning multiple events + /// or + /// Aggregation functions in the measure-clause must refer to one or more properties of exactly one group variable returning multiple events + /// or + /// The measures clause requires that each expression utilizes the AS keyword to assign a column name + /// + /// ExprValidationException if validation fails + public EventRowRegexNFAViewFactory( + ViewFactoryChain viewChain, + MatchRecognizeSpec matchRecognizeSpec, + AgentInstanceContext agentInstanceContext, + bool isUnbound, + Attribute[] annotations, + ConfigurationEngineDefaults.MatchRecognizeConfig matchRecognizeConfig) + { + var parentViewType = viewChain.EventType; + _matchRecognizeSpec = matchRecognizeSpec; + _isUnbound = isUnbound; + _isIterateOnly = HintEnum.ITERATE_ONLY.GetHint(annotations) != null; + _matchRecognizeConfig = matchRecognizeConfig; + + var statementContext = agentInstanceContext.StatementContext; + + // Expand repeats and permutations + _expandedPatternNode = RegexPatternExpandUtil.Expand(matchRecognizeSpec.Pattern); + + // Determine single-row and multiple-row variables + _variablesSingle = new LinkedHashSet(); + ISet variablesMultiple = new LinkedHashSet(); + EventRowRegexHelper.RecursiveInspectVariables(_expandedPatternNode, false, _variablesSingle, variablesMultiple); + + // each variable gets associated with a stream number (multiple-row variables as well to hold the current event for the expression). + var streamNum = 0; + _variableStreams = new LinkedHashMap>(); + foreach (var variableSingle in _variablesSingle) + { + _variableStreams.Put(variableSingle, new Pair(streamNum, false)); + streamNum++; + } + foreach (var variableMultiple in variablesMultiple) + { + _variableStreams.Put(variableMultiple, new Pair(streamNum, true)); + streamNum++; + } + + // mapping of stream to variable + _streamVariables = new SortedDictionary(); + foreach (var entry in _variableStreams) + { + _streamVariables.Put(entry.Value.First, entry.Key); + } + + // determine visibility rules + var visibility = EventRowRegexHelper.DetermineVisibility(_expandedPatternNode); + + // assemble all single-row variables for expression validation + var allStreamNames = new string[_variableStreams.Count]; + var allTypes = new EventType[_variableStreams.Count]; + + streamNum = 0; + foreach (var variableSingle in _variablesSingle) + { + allStreamNames[streamNum] = variableSingle; + allTypes[streamNum] = parentViewType; + streamNum++; + } + foreach (var variableMultiple in variablesMultiple) + { + allStreamNames[streamNum] = variableMultiple; + allTypes[streamNum] = parentViewType; + streamNum++; + } + + // determine type service for use with DEFINE + // validate each DEFINE clause expression + ISet definedVariables = new HashSet(); + IList aggregateNodes = new List(); + var exprEvaluatorContext = new ExprEvaluatorContextStatement(statementContext, false); + _isExprRequiresMultimatchState = new bool[_variableStreams.Count]; + + for (var defineIndex = 0; defineIndex < matchRecognizeSpec.Defines.Count; defineIndex++) + { + var defineItem = matchRecognizeSpec.Defines[defineIndex]; + if (definedVariables.Contains(defineItem.Identifier)) + { + throw new ExprValidationException("Variable '" + defineItem.Identifier + "' has already been defined"); + } + definedVariables.Add(defineItem.Identifier); + + // stream-type visibilities handled here + var typeServiceDefines = EventRowRegexNFAViewFactoryHelper.BuildDefineStreamTypeServiceDefine(statementContext, _variableStreams, defineItem, visibility, parentViewType); + + var exprNodeResult = HandlePreviousFunctions(defineItem.Expression); + var validationContext = new ExprValidationContext( + typeServiceDefines, + statementContext.EngineImportService, + statementContext.StatementExtensionServicesContext, null, + statementContext.SchedulingService, + statementContext.VariableService, + statementContext.TableService, exprEvaluatorContext, + statementContext.EventAdapterService, + statementContext.StatementName, + statementContext.StatementId, + statementContext.Annotations, + statementContext.ContextDescriptor, + statementContext.ScriptingService, + true, false, true, false, null, false); + + ExprNode validated; + try { + // validate + validated = ExprNodeUtility.GetValidatedSubtree(ExprNodeOrigin.MATCHRECOGDEFINE, exprNodeResult, validationContext); + + // check aggregates + defineItem.Expression = validated; + ExprAggregateNodeUtil.GetAggregatesBottomUp(validated, aggregateNodes); + if (!aggregateNodes.IsEmpty()) { + throw new ExprValidationException("An aggregate function may not appear in a DEFINE clause"); + } + } + catch (ExprValidationException ex) { + throw new ExprValidationException("Failed to validate condition expression for variable '" + defineItem.Identifier + "': " + ex.Message, ex); + } + + // determine access to event properties from multi-matches + var visitor = new ExprNodeStreamRequiredVisitor(); + validated.Accept(visitor); + var streamsRequired = visitor.StreamsRequired; + foreach (var streamRequired in streamsRequired) { + if (streamRequired >= _variableStreams.Count) { + var streamNumIdent = _variableStreams.Get(defineItem.Identifier).First; + _isExprRequiresMultimatchState[streamNumIdent] = true; + break; + } + } + } + _isDefineAsksMultimatches = CollectionUtil.IsAnySet(_isExprRequiresMultimatchState); + _defineMultimatchEventBean = _isDefineAsksMultimatches ? EventRowRegexNFAViewFactoryHelper.GetDefineMultimatchBean(statementContext, _variableStreams, parentViewType) : null; + + // assign "prev" node indexes + // Since an expression such as "prior(2, price), prior(8, price)" translates into {2, 8} the relative index is {0, 1}. + // Map the expression-supplied index to a relative index + var countPrev = 0; + foreach (var entry in _callbacksPerIndex) { + foreach (var callback in entry.Value) { + callback.AssignedIndex = countPrev; + } + countPrev++; + } + + // determine type service for use with MEASURE + IDictionary measureTypeDef = new LinkedHashMap(); + foreach (var variableSingle in _variablesSingle) + { + measureTypeDef.Put(variableSingle, parentViewType); + } + foreach (var variableMultiple in variablesMultiple) + { + measureTypeDef.Put(variableMultiple, new EventType[] {parentViewType}); + } + var outputEventTypeName = statementContext.StatementId + "_rowrecog"; + _compositeEventType = (ObjectArrayEventType) statementContext.EventAdapterService.CreateAnonymousObjectArrayType(outputEventTypeName, measureTypeDef); + StreamTypeService typeServiceMeasure = new StreamTypeServiceImpl(_compositeEventType, "MATCH_RECOGNIZE", true, statementContext.EngineURI); + + // find MEASURE clause aggregations + var measureReferencesMultivar = false; + IList measureAggregateExprNodes = new List(); + foreach (var measureItem in matchRecognizeSpec.Measures) + { + ExprAggregateNodeUtil.GetAggregatesBottomUp(measureItem.Expr, measureAggregateExprNodes); + } + if (!measureAggregateExprNodes.IsEmpty()) + { + var isIStreamOnly = new bool[allStreamNames.Length]; + CompatExtensions.Fill(isIStreamOnly, true); + var typeServiceAggregateMeasure = new StreamTypeServiceImpl(allTypes, allStreamNames, isIStreamOnly, statementContext.EngineURI, false); + var measureExprAggNodesPerStream = new Dictionary>(); + + foreach (var aggregateNode in measureAggregateExprNodes) + { + // validate absence of group-by + aggregateNode.ValidatePositionals(); + if (aggregateNode.OptionalLocalGroupBy != null) + { + throw new ExprValidationException("Match-recognize does not allow aggregation functions to specify a group-by"); + } + + // validate node and params + var count = 0; + var visitor = new ExprNodeIdentifierVisitor(true); + + var validationContext = new ExprValidationContext( + typeServiceAggregateMeasure, + statementContext.EngineImportService, + statementContext.StatementExtensionServicesContext, null, + statementContext.SchedulingService, + statementContext.VariableService, + statementContext.TableService, + exprEvaluatorContext, + statementContext.EventAdapterService, + statementContext.StatementName, + statementContext.StatementId, + statementContext.Annotations, + statementContext.ContextDescriptor, + statementContext.ScriptingService, + false, false, true, false, null, false); + for (int ii = 0 ; ii < aggregateNode.ChildNodes.Count ; ii++) + { + var child = aggregateNode.ChildNodes[ii]; + var validated = ExprNodeUtility.GetValidatedSubtree(ExprNodeOrigin.MATCHRECOGMEASURE, child, validationContext); + validated.Accept(visitor); + aggregateNode.SetChildNode(count++, new ExprNodeValidated(validated)); + } + validationContext = new ExprValidationContext( + typeServiceMeasure, + statementContext.EngineImportService, + statementContext.StatementExtensionServicesContext, null, + statementContext.SchedulingService, + statementContext.VariableService, + statementContext.TableService, + exprEvaluatorContext, + statementContext.EventAdapterService, + statementContext.StatementName, + statementContext.StatementId, + statementContext.Annotations, + statementContext.ContextDescriptor, + statementContext.ScriptingService, + false, false, true, false, null, false); + aggregateNode.Validate(validationContext); + + // verify properties used within the aggregation + var aggregatedStreams = new HashSet(); + foreach (var pair in visitor.ExprProperties) + { + aggregatedStreams.Add(pair.First); + } + + int? multipleVarStream = null; + foreach (int streamNumAggregated in aggregatedStreams) + { + var variable = _streamVariables.Get(streamNumAggregated); + if (variablesMultiple.Contains(variable)) + { + measureReferencesMultivar = true; + if (multipleVarStream == null) + { + multipleVarStream = streamNumAggregated; + continue; + } + throw new ExprValidationException("Aggregation functions in the measure-clause must only refer to properties of exactly one group variable returning multiple events"); + } + } + + if (multipleVarStream == null) + { + throw new ExprValidationException("Aggregation functions in the measure-clause must refer to one or more properties of exactly one group variable returning multiple events"); + } + + var aggNodesForStream = measureExprAggNodesPerStream.Get(multipleVarStream.Value); + if (aggNodesForStream == null) + { + aggNodesForStream = new List(); + measureExprAggNodesPerStream.Put(multipleVarStream.Value, aggNodesForStream); + } + aggNodesForStream.Add(aggregateNode); + } + + var factoryDesc = AggregationServiceFactoryFactory.GetServiceMatchRecognize(_streamVariables.Count, measureExprAggNodesPerStream, typeServiceAggregateMeasure.EventTypes); + _aggregationService = factoryDesc.AggregationServiceFactory.MakeService(agentInstanceContext); + _aggregationExpressions = factoryDesc.Expressions; + } + else + { + _aggregationService = null; + _aggregationExpressions = Collections.GetEmptyList(); + } + + // validate each MEASURE clause expression + IDictionary rowTypeDef = new LinkedHashMap(); + var streamRefVisitor = new ExprNodeStreamUseCollectVisitor(); + foreach (var measureItem in matchRecognizeSpec.Measures) + { + if (measureItem.Name == null) + { + throw new ExprValidationException("The measures clause requires that each expression utilizes the AS keyword to assign a column name"); + } + var validated = ValidateMeasureClause(measureItem.Expr, typeServiceMeasure, variablesMultiple, _variablesSingle, statementContext); + measureItem.Expr = validated; + rowTypeDef.Put(measureItem.Name, validated.ExprEvaluator.ReturnType); + validated.Accept(streamRefVisitor); + } + + // Determine if any of the multi-var streams are referenced in the measures (non-aggregated only) + foreach (var @ref in streamRefVisitor.Referenced) { + var rootPropName = @ref.RootPropertyNameIfAny; + if (rootPropName != null) { + if (variablesMultiple.Contains(rootPropName)) { + measureReferencesMultivar = true; + break; + } + } + + var streamRequired = @ref.StreamReferencedIfAny; + if (streamRequired != null) { + var streamVariable = _streamVariables.Get(streamRequired.Value); + if (streamVariable != null) { + var def = _variableStreams.Get(streamVariable); + if (def != null && def.Second) { + measureReferencesMultivar = true; + break; + } + } + } + } + _isCollectMultimatches = measureReferencesMultivar || _isDefineAsksMultimatches; + + // create rowevent type + var rowEventTypeName = statementContext.StatementId + "_rowrecogrow"; + _rowEventType = statementContext.EventAdapterService.CreateAnonymousMapType(rowEventTypeName, rowTypeDef, true); + + // validate partition-by expressions, if any + if (!matchRecognizeSpec.PartitionByExpressions.IsEmpty()) + { + var typeServicePartition = new StreamTypeServiceImpl(parentViewType, "MATCH_RECOGNIZE_PARTITION", true, statementContext.EngineURI); + var validated = new List(); + var validationContext = new ExprValidationContext( + typeServicePartition, + statementContext.EngineImportService, + statementContext.StatementExtensionServicesContext, null, + statementContext.SchedulingService, statementContext.VariableService, statementContext.TableService, + exprEvaluatorContext, statementContext.EventAdapterService, statementContext.StatementName, + statementContext.StatementId, statementContext.Annotations, statementContext.ContextDescriptor, + statementContext.ScriptingService, + false, false, true, false, null, false); + foreach (var partitionExpr in matchRecognizeSpec.PartitionByExpressions) + { + validated.Add(ExprNodeUtility.GetValidatedSubtree(ExprNodeOrigin.MATCHRECOGPARTITION, partitionExpr, validationContext)); + } + matchRecognizeSpec.PartitionByExpressions = validated; + } + + // validate interval if present + if (matchRecognizeSpec.Interval != null) + { + var validationContext = + new ExprValidationContext( + new StreamTypeServiceImpl(statementContext.EngineURI, false), + statementContext.EngineImportService, + statementContext.StatementExtensionServicesContext, null, + statementContext.SchedulingService, + statementContext.VariableService, statementContext.TableService, exprEvaluatorContext, + statementContext.EventAdapterService, statementContext.StatementName, statementContext.StatementId, + statementContext.Annotations, statementContext.ContextDescriptor, statementContext.ScriptingService, + false, false, true, false, null, false); + matchRecognizeSpec.Interval.Validate(validationContext); + } + } + + private ExprNode ValidateMeasureClause( + ExprNode measureNode, + StreamTypeService typeServiceMeasure, + ISet variablesMultiple, + ISet variablesSingle, + StatementContext statementContext) + { + try + { + var exprEvaluatorContext = new ExprEvaluatorContextStatement(statementContext, false); + var validationContext = new ExprValidationContext( + typeServiceMeasure, + statementContext.EngineImportService, + statementContext.StatementExtensionServicesContext, null, + statementContext.SchedulingService, + statementContext.VariableService, statementContext.TableService, exprEvaluatorContext, + statementContext.EventAdapterService, statementContext.StatementName, statementContext.StatementId, + statementContext.Annotations, statementContext.ContextDescriptor, statementContext.ScriptingService, + false, false, true, false, null, false); + return ExprNodeUtility.GetValidatedSubtree(ExprNodeOrigin.MATCHRECOGMEASURE, measureNode, validationContext); + } + catch (ExprValidationPropertyException e) + { + var grouped = CollectionUtil.ToString(variablesMultiple); + var single = CollectionUtil.ToString(variablesSingle); + var message = e.Message; + if (!variablesMultiple.IsEmpty()) + { + message += ", ensure that grouped variables (variables " + grouped + ") are accessed via index (i.e. variable[0].property) or appear within an aggregation"; + } + if (!variablesSingle.IsEmpty()) + { + message += ", ensure that singleton variables (variables " + single + ") are not accessed via index"; + } + throw new ExprValidationPropertyException(message, e); + } + } + + private ExprNode HandlePreviousFunctions(ExprNode defineItemExpression) + { + var previousVisitor = new ExprNodePreviousVisitorWParent(); + defineItemExpression.Accept(previousVisitor); + + if (previousVisitor.Previous == null) + { + return defineItemExpression; + } + + foreach (var previousNodePair in previousVisitor.Previous) + { + var previousNode = previousNodePair.Second; + var matchRecogPrevNode = new ExprPreviousMatchRecognizeNode(); + + if (previousNodePair.Second.ChildNodes.Count == 1) + { + matchRecogPrevNode.AddChildNode(previousNode.ChildNodes[0]); + matchRecogPrevNode.AddChildNode(new ExprConstantNodeImpl(1)); + } + else if (previousNodePair.Second.ChildNodes.Count == 2) + { + var first = previousNode.ChildNodes[0]; + var second = previousNode.ChildNodes[1]; + if ((first.IsConstantResult) && (!second.IsConstantResult)) + { + matchRecogPrevNode.AddChildNode(second); + matchRecogPrevNode.AddChildNode(first); + } + else if ((!first.IsConstantResult) && (second.IsConstantResult)) + { + matchRecogPrevNode.AddChildNode(first); + matchRecogPrevNode.AddChildNode(second); + } + else + { + throw new ExprValidationException("PREV operator requires a constant index"); + } + } + + if (previousNodePair.First == null) + { + defineItemExpression = matchRecogPrevNode; + } + else + { + ExprNodeUtility.ReplaceChildNode(previousNodePair.First, previousNodePair.Second, matchRecogPrevNode); + } + + // store in a list per index such that we can consolidate this into a single buffer + var index = matchRecogPrevNode.ConstantIndexNumber; + var callbackList = _callbacksPerIndex.Get(index); + if (callbackList == null) + { + callbackList = new List(); + _callbacksPerIndex.Put(index, callbackList); + } + callbackList.Add(matchRecogPrevNode); + } + + return defineItemExpression; + } + + public override void SetViewParameters(ViewFactoryContext viewFactoryContext, IList viewParameters) + { + } + + public override void Attach(EventType parentEventType, StatementContext statementContext, ViewFactory optionalParentFactory, IList parentViewFactories) + { + } + + public override View MakeView(AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext) + { + EventRowRegexNFAViewScheduler scheduler = null; + if (_matchRecognizeSpec.Interval != null) + { + scheduler = new EventRowRegexNFAViewSchedulerImpl(); + } + + EventRowRegexNFAView view = new EventRowRegexNFAView( + this, + _compositeEventType, + _rowEventType, + _matchRecognizeSpec, + _variableStreams, + _streamVariables, + _variablesSingle, + agentInstanceViewFactoryContext.AgentInstanceContext, + _callbacksPerIndex, + _aggregationService, + _isDefineAsksMultimatches, + _defineMultimatchEventBean, + _isExprRequiresMultimatchState, + _isUnbound, + _isIterateOnly, + _isCollectMultimatches, + _expandedPatternNode, + _matchRecognizeConfig, + scheduler + ); + + if (scheduler != null) + { + scheduler.SetScheduleCallback(agentInstanceViewFactoryContext.AgentInstanceContext, view); + } + + return view; + } + + public override EventType EventType + { + get { return _rowEventType; } + } + + public IList AggregationExpressions + { + get { return _aggregationExpressions; } + } + + public AggregationServiceMatchRecognize AggregationService + { + get { return _aggregationService; } + } + + public ISet PreviousExprNodes + { + get + { + if (_callbacksPerIndex.IsEmpty()) + { + return Collections.GetEmptySet(); + } + var nodes = new HashSet(); + foreach (IList list in _callbacksPerIndex.Values) + { + foreach (var node in list) + { + nodes.Add(node); + } + } + return nodes; + } + } + + public override string ViewName + { + get { return "Match-recognize"; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/rowregex/EventRowRegexNFAViewFactoryHelper.cs b/NEsper.Core/NEsper.Core/rowregex/EventRowRegexNFAViewFactoryHelper.cs new file mode 100755 index 000000000..5808d3179 --- /dev/null +++ b/NEsper.Core/NEsper.Core/rowregex/EventRowRegexNFAViewFactoryHelper.cs @@ -0,0 +1,118 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; +using com.espertech.esper.events; + +namespace com.espertech.esper.rowregex +{ + public class EventRowRegexNFAViewFactoryHelper + { + public static ObjectArrayBackedEventBean GetDefineMultimatchBean( + StatementContext statementContext, + LinkedHashMap> variableStreams, + EventType parentViewType) + { + IDictionary multievent = new LinkedHashMap(); + foreach (var entry in variableStreams) + { + if (entry.Value.Second) + { + multievent.Put(entry.Key, new EventType[] {parentViewType}); + } + } + var multimatch = statementContext.EventAdapterService.CreateAnonymousObjectArrayType( + "esper_matchrecog_internal", multievent); + return (ObjectArrayBackedEventBean) statementContext.EventAdapterService.AdapterForTypedObjectArray(new Object[multievent.Count], multimatch); + } + + public static StreamTypeService BuildDefineStreamTypeServiceDefine( + StatementContext statementContext, + LinkedHashMap> variableStreams, + MatchRecognizeDefineItem defineItem, + IDictionary> visibilityByIdentifier, + EventType parentViewType) + { + if (!variableStreams.ContainsKey(defineItem.Identifier)) + { + throw new ExprValidationException("Variable '" + defineItem.Identifier + "' does not occur in pattern"); + } + + var streamNamesDefine = new String[variableStreams.Count + 1]; + var typesDefine = new EventType[variableStreams.Count + 1]; + var isIStreamOnly = new bool[variableStreams.Count + 1]; + CompatExtensions.Fill(isIStreamOnly, true); + + var streamNumDefine = variableStreams.Get(defineItem.Identifier).First; + streamNamesDefine[streamNumDefine] = defineItem.Identifier; + typesDefine[streamNumDefine] = parentViewType; + + // add visible single-value + var visibles = visibilityByIdentifier.Get(defineItem.Identifier); + var hasVisibleMultimatch = false; + if (visibles != null) + { + foreach (var visible in visibles) + { + var def = variableStreams.Get(visible); + if (!def.Second) + { + streamNamesDefine[def.First] = visible; + typesDefine[def.First] = parentViewType; + } + else + { + hasVisibleMultimatch = true; + } + } + } + + // compile multi-matching event type (in last position), if any are used + if (hasVisibleMultimatch) + { + IDictionary multievent = new LinkedHashMap(); + foreach (var entry in variableStreams) + { + var identifier = entry.Key; + if (entry.Value.Second) + { + if (visibles.Contains(identifier)) + { + multievent.Put( + identifier, new EventType[] + { + parentViewType + }); + } + else + { + multievent.Put("esper_matchrecog_internal", null); + } + } + } + var multimatch = statementContext.EventAdapterService.CreateAnonymousObjectArrayType( + "esper_matchrecog_internal", multievent); + typesDefine[typesDefine.Length - 1] = multimatch; + streamNamesDefine[streamNamesDefine.Length - 1] = multimatch.Name; + } + + return new StreamTypeServiceImpl( + typesDefine, streamNamesDefine, isIStreamOnly, statementContext.EngineURI, false); + } + } +} diff --git a/NEsper.Core/NEsper.Core/rowregex/EventRowRegexNFAViewScheduleCallback.cs b/NEsper.Core/NEsper.Core/rowregex/EventRowRegexNFAViewScheduleCallback.cs new file mode 100755 index 000000000..aa097b1c3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/rowregex/EventRowRegexNFAViewScheduleCallback.cs @@ -0,0 +1,15 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.rowregex +{ + public interface EventRowRegexNFAViewScheduleCallback + { + void Triggered(); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/rowregex/EventRowRegexNFAViewScheduler.cs b/NEsper.Core/NEsper.Core/rowregex/EventRowRegexNFAViewScheduler.cs new file mode 100755 index 000000000..274340c8c --- /dev/null +++ b/NEsper.Core/NEsper.Core/rowregex/EventRowRegexNFAViewScheduler.cs @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.context.util; + +namespace com.espertech.esper.rowregex +{ + public interface EventRowRegexNFAViewScheduler + { + void SetScheduleCallback(AgentInstanceContext agentInstanceContext, EventRowRegexNFAViewScheduleCallback scheduleCallback); + void AddSchedule(long msecAfterCurrentTime); + void ChangeSchedule(long msecAfterCurrentTime); + void RemoveSchedule(); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/rowregex/EventRowRegexNFAViewSchedulerImpl.cs b/NEsper.Core/NEsper.Core/rowregex/EventRowRegexNFAViewSchedulerImpl.cs new file mode 100755 index 000000000..59356c4e4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/rowregex/EventRowRegexNFAViewSchedulerImpl.cs @@ -0,0 +1,58 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.schedule; + +namespace com.espertech.esper.rowregex +{ + public class EventRowRegexNFAViewSchedulerImpl : EventRowRegexNFAViewScheduler + { + private AgentInstanceContext _agentInstanceContext; + private long _scheduleSlot; + private EPStatementHandleCallback _handle; + + public void SetScheduleCallback(AgentInstanceContext agentInstanceContext, EventRowRegexNFAViewScheduleCallback scheduleCallback) + { + _agentInstanceContext = agentInstanceContext; + _scheduleSlot = agentInstanceContext.StatementContext.ScheduleBucket.AllocateSlot(); + var callback = new ProxyScheduleHandleCallback + { + ProcScheduledTrigger = extensionServicesContext => + { + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().QRegExScheduledEval(); + } + scheduleCallback.Triggered(); + if (InstrumentationHelper.ENABLED) { + InstrumentationHelper.Get().ARegExScheduledEval(); + } + } + }; + _handle = new EPStatementHandleCallback(agentInstanceContext.EpStatementAgentInstanceHandle, callback); + } + + public void AddSchedule(long timeDelta) + { + _agentInstanceContext.StatementContext.SchedulingService.Add(timeDelta, _handle, _scheduleSlot); + } + + public void ChangeSchedule(long timeDelta) + { + _agentInstanceContext.StatementContext.SchedulingService.Remove(_handle, _scheduleSlot); + _agentInstanceContext.StatementContext.SchedulingService.Add(timeDelta, _handle, _scheduleSlot); + } + + public void RemoveSchedule() + { + _agentInstanceContext.StatementContext.SchedulingService.Remove(_handle, _scheduleSlot); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/rowregex/EventRowRegexNFAViewService.cs b/NEsper.Core/NEsper.Core/rowregex/EventRowRegexNFAViewService.cs new file mode 100755 index 000000000..c378833bd --- /dev/null +++ b/NEsper.Core/NEsper.Core/rowregex/EventRowRegexNFAViewService.cs @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; + +namespace com.espertech.esper.rowregex +{ + /// + /// Service interface for match recognize. + /// + public interface EventRowRegexNFAViewService + { + void Init(EventBean[] newEvents); + RegexExprPreviousEvalStrategy PreviousEvaluationStrategy { get; } + void Accept(EventRowRegexNFAViewServiceVisitor visitor); + void Stop(); + } +} diff --git a/NEsper.Core/NEsper.Core/rowregex/EventRowRegexNFAViewServiceVisitor.cs b/NEsper.Core/NEsper.Core/rowregex/EventRowRegexNFAViewServiceVisitor.cs new file mode 100755 index 000000000..0e441c4e3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/rowregex/EventRowRegexNFAViewServiceVisitor.cs @@ -0,0 +1,18 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +namespace com.espertech.esper.rowregex +{ + public interface EventRowRegexNFAViewServiceVisitor + { + void VisitUnpartitioned(RegexPartitionState state); + void VisitPartitioned(IDictionary states); + } +} diff --git a/NEsper.Core/NEsper.Core/rowregex/EventRowRegexNFAViewUtil.cs b/NEsper.Core/NEsper.Core/rowregex/EventRowRegexNFAViewUtil.cs new file mode 100755 index 000000000..7eb998617 --- /dev/null +++ b/NEsper.Core/NEsper.Core/rowregex/EventRowRegexNFAViewUtil.cs @@ -0,0 +1,153 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.IO; +using System.Text; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.rowregex +{ + public class EventRowRegexNFAViewUtil + { + internal static EventBean[] GetMultimatchArray(int[] multimatchStreamNumToVariable, RegexNFAStateEntry state, int stream) + { + if (state.OptionalMultiMatches == null) { + return null; + } + var index = multimatchStreamNumToVariable[stream]; + var multiMatches = state.OptionalMultiMatches[index]; + if (multiMatches == null) { + return null; + } + return multiMatches.ShrinkEventArray; + } + + internal static string PrintStates(IEnumerable states, IDictionary streamsVariables, LinkedHashMap> variableStreams, int[] multimatchStreamNumToVariable) + { + var buf = new StringBuilder(); + var delimiter = ""; + foreach (var state in states) + { + buf.Append(delimiter); + buf.Append(state.State.NodeNumNested); + + buf.Append("{"); + var eventsPerStream = state.EventsPerStream; + if (eventsPerStream == null) + { + buf.Append("null"); + } + else + { + var eventDelimiter = ""; + foreach (var streamVariable in streamsVariables) + { + buf.Append(eventDelimiter); + buf.Append(streamVariable.Value); + buf.Append('='); + var single = !variableStreams.Get(streamVariable.Value).Second; + if (single) + { + if (eventsPerStream[streamVariable.Key] == null) + { + buf.Append("null"); + } + else + { + buf.Append(eventsPerStream[streamVariable.Key].Underlying); + } + } + else + { + var streamNum = state.State.StreamNum; + var index = multimatchStreamNumToVariable[streamNum]; + if (state.OptionalMultiMatches == null) { + buf.Append("null-mm"); + } + else if (state.OptionalMultiMatches[index] == null) { + buf.Append("no-entry"); + } + else + { + buf.Append("{"); + var arrayEventDelimiter = ""; + var multiMatch = state.OptionalMultiMatches[index].Buffer; + var count = state.OptionalMultiMatches[index].Count; + for (var i = 0; i < count; i++) + { + buf.Append(arrayEventDelimiter); + buf.Append(multiMatch[i].Underlying); + arrayEventDelimiter = ", "; + } + buf.Append("}"); + } + } + eventDelimiter = ", "; + } + } + buf.Append("}"); + + delimiter = ", "; + } + return buf.ToString(); + } + + internal static string Print(RegexNFAState[] states) + { + var writer = new StringWriter(); + var currentStack = new Stack(); + Print(states, writer, 0, currentStack); + return writer.ToString(); + } + + internal static void Print(IList states, TextWriter writer, int indent, Stack currentStack) + { + + foreach (var state in states) + { + Indent(writer, indent); + if (currentStack.Contains(state)) + { + writer.WriteLine("(self)"); + } + else + { + writer.WriteLine(PrintState(state)); + + currentStack.Push(state); + Print(state.NextStates, writer, indent + 4, currentStack); + currentStack.Pop(); + } + } + } + + private static string PrintState(RegexNFAState state) + { + if (state is RegexNFAStateEnd) + { + return "#" + state.NodeNumNested; + } + else + { + return "#" + state.NodeNumNested + " " + state.VariableName + " s" + state.StreamNum + " defined as " + state; + } + } + + private static void Indent(TextWriter writer, int indent) + { + for (var i = 0; i < indent; i++) + { + writer.Write(' '); + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/rowregex/MatchRecognizeStatePoolEngineSvc.cs b/NEsper.Core/NEsper.Core/rowregex/MatchRecognizeStatePoolEngineSvc.cs new file mode 100755 index 000000000..6be29c316 --- /dev/null +++ b/NEsper.Core/NEsper.Core/rowregex/MatchRecognizeStatePoolEngineSvc.cs @@ -0,0 +1,158 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +using com.espertech.esper.client.hook; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.util; +using com.espertech.esper.util; + +namespace com.espertech.esper.rowregex +{ + public class MatchRecognizeStatePoolEngineSvc + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private long _maxPoolCountConfigured; + private readonly bool _preventStart; + private readonly AtomicLong _poolCount; + private readonly ISet _matchRecognizeContexts; + + public MatchRecognizeStatePoolEngineSvc(long maxPoolCountConfigured, bool preventStart) + { + _maxPoolCountConfigured = maxPoolCountConfigured; + _preventStart = preventStart; + _poolCount = new AtomicLong(); + _matchRecognizeContexts = new HashSet().AsSyncSet(); + } + + public long? MatchRecognizeMaxStates + { + get { return _maxPoolCountConfigured; } + set + { + if (value == null) + { + _maxPoolCountConfigured = -1; + } + else + { + _maxPoolCountConfigured = value.GetValueOrDefault(); + } + } + } + + public void AddPatternContext(string statementName, MatchRecognizeStatePoolStmtHandler stmtCounts) + { + _matchRecognizeContexts.Add(new StatementEntry(statementName, stmtCounts)); + } + + public void RemoveStatement(string name) + { + // counts get reduced upon view stop + var removed = new HashSet(_matchRecognizeContexts.Where(c => c.StatementName == name)); + _matchRecognizeContexts.RemoveAll(removed); + } + + public bool TryIncreaseCount(AgentInstanceContext agentInstanceContext) + { + // test pool max + long newMax = _poolCount.IncrementAndGet(); + if (newMax > _maxPoolCountConfigured && _maxPoolCountConfigured >= 0) + { + var counts = GetCounts(); + agentInstanceContext.StatementContext.ExceptionHandlingService.HandleCondition(new ConditionMatchRecognizeStatesMax(_maxPoolCountConfigured, counts), agentInstanceContext.StatementContext.EpStatementHandle); + if (ExecutionPathDebugLog.IsEnabled && Log.IsDebugEnabled && ExecutionPathDebugLog.IsTimerDebugEnabled) + { + MatchRecognizeStatePoolStmtHandler stmtHandler = agentInstanceContext.StatementContext.MatchRecognizeStatePoolStmtSvc.StmtHandler; + string stmtName = agentInstanceContext.StatementContext.StatementName; + Log.Debug(".tryIncreaseCount For statement '" + stmtName + "' pool count overflow at " + newMax + " statement count was " + stmtHandler.Count + " preventStart=" + _preventStart); + } + + if (_preventStart) + { + _poolCount.DecrementAndGet(); + return false; + } + else + { + return true; + } + } + if (ExecutionPathDebugLog.IsEnabled && Log.IsDebugEnabled) + { + MatchRecognizeStatePoolStmtHandler stmtHandler = agentInstanceContext.StatementContext.MatchRecognizeStatePoolStmtSvc.StmtHandler; + string stmtName = agentInstanceContext.StatementContext.StatementName; + Log.Debug(".tryIncreaseCount For statement '" + stmtName + "' pool count increases to " + newMax + " statement count was " + stmtHandler.Count); + } + return true; + } + + public void DecreaseCount(AgentInstanceContext agentInstanceContext) + { + DecreaseCount(agentInstanceContext, 1); + } + + public void DecreaseCount(AgentInstanceContext agentInstanceContext, int numRemoved) + { + long newMax = _poolCount.IncrementAndGet(-1 * numRemoved); + if (newMax < 0) + { + _poolCount.Set(0); + } + LogDecrease(agentInstanceContext, newMax); + } + + private void LogDecrease(AgentInstanceContext agentInstanceContext, long newMax) + { + if (ExecutionPathDebugLog.IsEnabled && Log.IsDebugEnabled) + { + MatchRecognizeStatePoolStmtHandler stmtHandler = agentInstanceContext.StatementContext.MatchRecognizeStatePoolStmtSvc.StmtHandler; + string stmtName = agentInstanceContext.StatementContext.StatementName; + Log.Debug(".decreaseCount For statement '" + stmtName + "' pool count decreases to " + newMax + " statement count was " + stmtHandler.Count); + } + } + + private IDictionary GetCounts() + { + var counts = new Dictionary(); + foreach (StatementEntry context in _matchRecognizeContexts) + { + long count; + + if (!counts.TryGetValue(context.StatementName, out count)) + { + count = 0L; + } + + count += context.StmtCounts.Count; + counts.Put(context.StatementName, count); + } + return counts; + } + + public class StatementEntry + { + public StatementEntry(string statementName, MatchRecognizeStatePoolStmtHandler stmtCounts) + { + StatementName = statementName; + StmtCounts = stmtCounts; + } + + public string StatementName { get; private set; } + + public MatchRecognizeStatePoolStmtHandler StmtCounts { get; private set; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/rowregex/MatchRecognizeStatePoolStmtHandler.cs b/NEsper.Core/NEsper.Core/rowregex/MatchRecognizeStatePoolStmtHandler.cs new file mode 100755 index 000000000..7342cdc8c --- /dev/null +++ b/NEsper.Core/NEsper.Core/rowregex/MatchRecognizeStatePoolStmtHandler.cs @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.rowregex +{ + public class MatchRecognizeStatePoolStmtHandler + { + private int _count; + + public int Count + { + get { return _count; } + } + + public void DecreaseCount() { + _count--; + if (_count < 0) { + _count = 0; + } + } + + public void DecreaseCount(int num) { + _count-=num; + if (_count < 0) { + _count = 0; + } + } + + public void IncreaseCount() { + _count++; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/rowregex/MatchRecognizeStatePoolStmtSvc.cs b/NEsper.Core/NEsper.Core/rowregex/MatchRecognizeStatePoolStmtSvc.cs new file mode 100755 index 000000000..117b75892 --- /dev/null +++ b/NEsper.Core/NEsper.Core/rowregex/MatchRecognizeStatePoolStmtSvc.cs @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.rowregex +{ + public class MatchRecognizeStatePoolStmtSvc + { + private readonly MatchRecognizeStatePoolEngineSvc _engineSvc; + private readonly MatchRecognizeStatePoolStmtHandler _stmtHandler; + + public MatchRecognizeStatePoolStmtSvc(MatchRecognizeStatePoolEngineSvc engineSvc, MatchRecognizeStatePoolStmtHandler stmtHandler) + { + _engineSvc = engineSvc; + _stmtHandler = stmtHandler; + } + + public MatchRecognizeStatePoolEngineSvc EngineSvc + { + get { return _engineSvc; } + } + + public MatchRecognizeStatePoolStmtHandler StmtHandler + { + get { return _stmtHandler; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/rowregex/MultimatchState.cs b/NEsper.Core/NEsper.Core/rowregex/MultimatchState.cs new file mode 100755 index 000000000..bf992757d --- /dev/null +++ b/NEsper.Core/NEsper.Core/rowregex/MultimatchState.cs @@ -0,0 +1,114 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.util; + +namespace com.espertech.esper.rowregex +{ + /// + /// State holder for matches, backed by an array, for fast copying and writing. + /// + public class MultimatchState + { + private int _count; + private EventBean[] _events; + + public MultimatchState(int count, EventBean[] events) + { + _count = count; + _events = events; + } + + /// Ctor. + /// first event to hold + public MultimatchState(EventBean theEvent) + { + _events = new EventBean[3]; + Add(theEvent); + } + + /// Ctor. + /// to copy + public MultimatchState(MultimatchState state) + { + var copyArray = new EventBean[state.Buffer.Length]; + Array.Copy(state.Buffer, 0, copyArray, 0, state.Count); + + _count = state.Count; + _events = copyArray; + } + + /// Add an event. + /// to add + public void Add(EventBean theEvent) + { + if (_count == _events.Length) + { + var buf = new EventBean[_events.Length * 2]; + Array.Copy(_events, 0, buf, 0, _events.Length); + _events = buf; + } + _events[_count++] = theEvent; + } + + /// Returns the count of events. + /// count + public int Count + { + get { return _count; } + } + + /// Returns the raw buffer. + /// buffer + public EventBean[] Buffer + { + get { return _events; } + } + + /// Determines if an event is in the collection. + /// to check + /// indicator + public bool ContainsEvent(EventBean theEvent) + { + for (var i = 0; i < _count; i++) + { + if (Equals(_events[i], theEvent)) + { + return true; + } + } + return false; + } + + /// + /// Returns the buffer sized to only the contained events, and shrinks the event array unless it is empty + /// + /// events + public EventBean[] ShrinkEventArray + { + get + { + if (_count == 0) + { + return CollectionUtil.EVENTBEANARRAY_EMPTY; + } + if (_count == _events.Length) + { + return _events; + } + var array = new EventBean[_count]; + Array.Copy(_events, 0, array, 0, _count); + _events = array; // we hold on to the result, avoiding future shrinking + return array; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/rowregex/RegexExprPreviousEvalStrategy.cs b/NEsper.Core/NEsper.Core/rowregex/RegexExprPreviousEvalStrategy.cs new file mode 100755 index 000000000..106b811f2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/rowregex/RegexExprPreviousEvalStrategy.cs @@ -0,0 +1,18 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.rowregex +{ + public interface RegexExprPreviousEvalStrategy + { + RegexPartitionStateRandomAccess GetAccess(ExprEvaluatorContext exprEvaluatorContext); + } +} diff --git a/NEsper.Core/NEsper.Core/rowregex/RegexHandlerFactory.cs b/NEsper.Core/NEsper.Core/rowregex/RegexHandlerFactory.cs new file mode 100755 index 000000000..c2026a87a --- /dev/null +++ b/NEsper.Core/NEsper.Core/rowregex/RegexHandlerFactory.cs @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.spec; +using com.espertech.esper.view; + +namespace com.espertech.esper.rowregex +{ + /// + /// Service for creating match-recognize factory and state services. + /// + public interface RegexHandlerFactory + { + EventRowRegexNFAViewFactory MakeViewFactory(ViewFactoryChain viewFactoryChain, MatchRecognizeSpec matchRecognizeSpec, AgentInstanceContext agentInstanceContext, bool isUnbound, Attribute[] annotations, ConfigurationEngineDefaults.MatchRecognizeConfig matchRecognizeConfigs) ; + RegexPartitionStateRepo MakeSingle(RegexPartitionStateRandomAccessGetter prevGetter, AgentInstanceContext agentInstanceContext, EventRowRegexNFAView view, bool keepScheduleState, RegexPartitionTerminationStateComparator terminationStateCompare); + RegexPartitionStateRepo MakePartitioned(RegexPartitionStateRandomAccessGetter prevGetter, RegexPartitionStateRepoGroupMeta stateRepoGroupMeta, AgentInstanceContext agentInstanceContext, EventRowRegexNFAView view, bool keepScheduleState, RegexPartitionTerminationStateComparator terminationStateCompare); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/rowregex/RegexHandlerFactoryDefault.cs b/NEsper.Core/NEsper.Core/rowregex/RegexHandlerFactoryDefault.cs new file mode 100755 index 000000000..a7e2eb05a --- /dev/null +++ b/NEsper.Core/NEsper.Core/rowregex/RegexHandlerFactoryDefault.cs @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; +using com.espertech.esper.view; + +namespace com.espertech.esper.rowregex +{ + public class RegexHandlerFactoryDefault : RegexHandlerFactory + { + public EventRowRegexNFAViewFactory MakeViewFactory(ViewFactoryChain viewFactoryChain, MatchRecognizeSpec matchRecognizeSpec, AgentInstanceContext agentInstanceContext, bool isUnbound, Attribute[] annotations, ConfigurationEngineDefaults.MatchRecognizeConfig matchRecognizeConfigs) + { + return new EventRowRegexNFAViewFactory(viewFactoryChain, matchRecognizeSpec, agentInstanceContext, isUnbound, annotations, matchRecognizeConfigs); + } + + public RegexPartitionStateRepo MakeSingle(RegexPartitionStateRandomAccessGetter prevGetter, AgentInstanceContext agentInstanceContext, EventRowRegexNFAView view, bool keepScheduleState, RegexPartitionTerminationStateComparator terminationStateCompare) + { + return new RegexPartitionStateRepoNoGroup(prevGetter, keepScheduleState, terminationStateCompare); + } + + public RegexPartitionStateRepo MakePartitioned(RegexPartitionStateRandomAccessGetter prevGetter, RegexPartitionStateRepoGroupMeta stateRepoGroupMeta, AgentInstanceContext agentInstanceContext, EventRowRegexNFAView view, bool keepScheduleState, RegexPartitionTerminationStateComparator terminationStateCompare) + { + return new RegexPartitionStateRepoGroup(prevGetter, stateRepoGroupMeta, keepScheduleState, terminationStateCompare); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/rowregex/RegexNFAState.cs b/NEsper.Core/NEsper.Core/rowregex/RegexNFAState.cs new file mode 100755 index 000000000..40779b636 --- /dev/null +++ b/NEsper.Core/NEsper.Core/rowregex/RegexNFAState.cs @@ -0,0 +1,78 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.rowregex +{ + /// + /// Match-recognize NFA states provides this information. + /// + public interface RegexNFAState + { + /// + /// For multiple-quantifiers. + /// + /// indicator + bool IsMultiple { get; } + + /// + /// Returns the nested node number. + /// + /// num + string NodeNumNested { get; } + + /// + /// Returns the absolute node num. + /// + /// num + int NodeNumFlat { get; } + + /// + /// Returns the variable name. + /// + /// name + string VariableName { get; } + + /// + /// Returns stream number. + /// + /// stream num + int StreamNum { get; } + + /// + /// Returns greedy indicator. + /// + /// greedy indicator + bool? IsGreedy { get; } + + /// + /// Evaluate a match. + /// + /// variabele values + /// expression evaluation context + /// match indicator + bool Matches(EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext); + + /// + /// Returns the next states. + /// + /// states + IList NextStates { get; } + + /// + /// Whether or not the match-expression requires multimatch state + /// + /// indicator + bool IsExprRequiresMultimatchState { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/rowregex/RegexNFAStateAnyOne.cs b/NEsper.Core/NEsper.Core/rowregex/RegexNFAStateAnyOne.cs new file mode 100755 index 000000000..ac0429a90 --- /dev/null +++ b/NEsper.Core/NEsper.Core/rowregex/RegexNFAStateAnyOne.cs @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.rowregex +{ + /// + /// Any-quantifier. + /// + public class RegexNFAStateAnyOne + : RegexNFAStateBase + , RegexNFAState + { + /// Ctor. + /// node num + /// variable + /// stream num + /// indicator + public RegexNFAStateAnyOne(String nodeNum, String variableName, int streamNum, bool multiple) + + : base(nodeNum, variableName, streamNum, multiple, null) + { + } + + public override bool Matches(EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext) + { + return true; + } + + public override String ToString() + { + return "AnyEvent"; + } + + public override bool IsExprRequiresMultimatchState + { + get { return false; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/rowregex/RegexNFAStateBase.cs b/NEsper.Core/NEsper.Core/rowregex/RegexNFAStateBase.cs new file mode 100755 index 000000000..84383519d --- /dev/null +++ b/NEsper.Core/NEsper.Core/rowregex/RegexNFAStateBase.cs @@ -0,0 +1,65 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.rowregex +{ + /// + /// Base for states. + /// + public abstract class RegexNFAStateBase : RegexNFAState + { + /// Ctor. + /// node num + /// variable + /// stream num + /// indicator + /// greedy indicator + protected RegexNFAStateBase(String nodeNum, String variableName, int streamNum, bool multiple, bool? isGreedy) + { + NodeNumNested = nodeNum; + VariableName = variableName; + StreamNum = streamNum; + IsMultiple = multiple; + IsGreedy = isGreedy; + NextStates = new List(); + } + + /// Assign a node number. + /// flat number + public int NodeNumFlat { get; set; } + + public string NodeNumNested { get; private set; } + + public IList NextStates { get; private set; } + + /// Add a next state. + /// state to add + public void AddState(RegexNFAState next) + { + NextStates.Add(next); + } + + public bool IsMultiple { get; private set; } + + public string VariableName { get; private set; } + + public int StreamNum { get; private set; } + + public bool? IsGreedy { get; private set; } + + public abstract bool Matches(EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext); + public abstract bool IsExprRequiresMultimatchState { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/rowregex/RegexNFAStateEnd.cs b/NEsper.Core/NEsper.Core/rowregex/RegexNFAStateEnd.cs new file mode 100755 index 000000000..2dadd0722 --- /dev/null +++ b/NEsper.Core/NEsper.Core/rowregex/RegexNFAStateEnd.cs @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.rowregex +{ + /// + /// End state in the regex NFA states. + /// + public class RegexNFAStateEnd + : RegexNFAStateBase + { + /// + /// Ctor. + /// + public RegexNFAStateEnd() + : base("endstate", null, -1, false, null) + { + } + + public override bool Matches(EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext) + { + throw new UnsupportedOperationException(); + } + + public IList GetNextStates() + { + return Collections.GetEmptyList(); + } + + public override bool IsExprRequiresMultimatchState + { + get { throw new UnsupportedOperationException(); } + } + } +} diff --git a/NEsper.Core/NEsper.Core/rowregex/RegexNFAStateEntry.cs b/NEsper.Core/NEsper.Core/rowregex/RegexNFAStateEntry.cs new file mode 100755 index 000000000..35e44e85d --- /dev/null +++ b/NEsper.Core/NEsper.Core/rowregex/RegexNFAStateEntry.cs @@ -0,0 +1,83 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.rowregex +{ + /// + /// State for a partial NFA match. + /// + public class RegexNFAStateEntry + { + /// Ctor. + /// the event number where the match started + /// the time the first match occured + /// the current match state + /// events for each single-match variable + /// number of greedy matches over all variables + /// matches for multirow-variables + /// key of partition + public RegexNFAStateEntry( + int matchBeginEventSeqNo, + long matchBeginEventTime, + RegexNFAState state, + EventBean[] eventsPerStream, + int[] greedyCountPerState, + MultimatchState[] optionalMultiMatches, + Object partitionKey) + { + MatchBeginEventSeqNo = matchBeginEventSeqNo; + MatchBeginEventTime = matchBeginEventTime; + State = state; + EventsPerStream = eventsPerStream; + GreedyCountPerState = greedyCountPerState; + OptionalMultiMatches = optionalMultiMatches; + PartitionKey = partitionKey; + } + + /// Returns the event number of the first matching event. + /// event number + public int MatchBeginEventSeqNo { get; private set; } + + /// Returns the time of the first matching event. + /// time + public long MatchBeginEventTime { get; private set; } + + /// Returns the partial matches. + /// state + public RegexNFAState State { get; set; } + + /// Returns the single-variable matches. + /// match events + public EventBean[] EventsPerStream { get; private set; } + + /// Returns the multirow-variable matches, if any. + /// matches + public MultimatchState[] OptionalMultiMatches { get; private set; } + + /// Returns the count of greedy matches per state. + /// greedy counts + public int[] GreedyCountPerState { get; private set; } + + /// Returns the match end event number. + /// num + public int MatchEndEventSeqNo { get; set; } + + /// Returns the partition key. + /// key + public object PartitionKey { get; private set; } + + public override String ToString() + { + return "Record " + State; + } + } +} diff --git a/NEsper.Core/NEsper.Core/rowregex/RegexNFAStateFilter.cs b/NEsper.Core/NEsper.Core/rowregex/RegexNFAStateFilter.cs new file mode 100755 index 000000000..415486992 --- /dev/null +++ b/NEsper.Core/NEsper.Core/rowregex/RegexNFAStateFilter.cs @@ -0,0 +1,75 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.rowregex +{ + /// + /// NFA state for a single match that applies a filter. + /// + public class RegexNFAStateFilter + : RegexNFAStateBase + , RegexNFAState + { + private readonly ExprEvaluator _exprNode; + private readonly ExprNode _expression; + private readonly bool _exprRequiresMultimatchState; + + /// + /// Ctor. + /// + /// node num + /// variable name + /// stream number + /// true for multiple matches + /// filter expression + /// if set to true [expr requires multimatch state]. + public RegexNFAStateFilter(String nodeNum, String variableName, int streamNum, bool multiple, ExprNode exprNode, bool exprRequiresMultimatchState) + : base(nodeNum, variableName, streamNum, multiple, null) + { + _exprNode = exprNode.ExprEvaluator; + _expression = exprNode; + _exprRequiresMultimatchState = exprRequiresMultimatchState; + } + + public override bool Matches(EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext) + { + var result = new Mutable(false); + + using (Instrument.With( + i => i.QExprBool(_expression, eventsPerStream), + i => i.AExprBool(result.Value))) + { + var temp = (bool?) _exprNode.Evaluate(new EvaluateParams(eventsPerStream, true, exprEvaluatorContext)); + if (temp != null) + { + return result.Value = temp.Value; + } + + return result.Value = false; + } + } + + public override String ToString() + { + return "FilterEvent"; + } + + public override bool IsExprRequiresMultimatchState + { + get { return _exprRequiresMultimatchState; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/rowregex/RegexNFAStateOneOptional.cs b/NEsper.Core/NEsper.Core/rowregex/RegexNFAStateOneOptional.cs new file mode 100755 index 000000000..35d7eb07b --- /dev/null +++ b/NEsper.Core/NEsper.Core/rowregex/RegexNFAStateOneOptional.cs @@ -0,0 +1,73 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.rowregex +{ + /// + /// The '?' state in the regex NFA states. + /// + public class RegexNFAStateOneOptional + : RegexNFAStateBase + , RegexNFAState + { + private readonly ExprEvaluator _exprNode; + private readonly bool _exprRequiresMultimatchState; + + /// + /// Ctor. + /// + /// node num + /// variable name + /// stream number + /// true for multiple matches + /// true for greedy + /// filter expression + /// if set to true [expr requires multimatch state]. + public RegexNFAStateOneOptional(String nodeNum, String variableName, int streamNum, bool multiple, bool? isGreedy, ExprNode exprNode, bool exprRequiresMultimatchState) + : base(nodeNum, variableName, streamNum, multiple, isGreedy) + { + _exprNode = exprNode == null ? null : exprNode.ExprEvaluator; + _exprRequiresMultimatchState = exprRequiresMultimatchState; + } + + public override bool Matches(EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext) + { + if (_exprNode == null) + { + return true; + } + + var result = (bool?) _exprNode.Evaluate(new EvaluateParams(eventsPerStream, true, exprEvaluatorContext)); + if (result != null) + { + return result.Value; + } + return false; + } + + public override String ToString() + { + if (_exprNode == null) + { + return "OptionalFilterEvent"; + } + return "OptionalFilterEvent-Filtered"; + } + + public override bool IsExprRequiresMultimatchState + { + get { return _exprRequiresMultimatchState; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/rowregex/RegexNFAStateOneToMany.cs b/NEsper.Core/NEsper.Core/rowregex/RegexNFAStateOneToMany.cs new file mode 100755 index 000000000..5d629d241 --- /dev/null +++ b/NEsper.Core/NEsper.Core/rowregex/RegexNFAStateOneToMany.cs @@ -0,0 +1,73 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.rowregex +{ + /// + /// The '+' state in the regex NFA states. + /// + public class RegexNFAStateOneToMany + : RegexNFAStateBase + , RegexNFAState + { + private readonly ExprEvaluator _exprNode; + private readonly bool _exprRequiresMultimatchState; + + /// + /// Ctor. + /// + /// node num + /// variable name + /// stream number + /// true for multiple matches + /// true for greedy + /// filter expression + /// if set to true [expr requires multimatch state]. + public RegexNFAStateOneToMany(String nodeNum, String variableName, int streamNum, bool multiple, bool? isGreedy, ExprNode exprNode, bool exprRequiresMultimatchState) + : base(nodeNum, variableName, streamNum, multiple, isGreedy) + { + _exprNode = exprNode == null ? null : exprNode.ExprEvaluator; + _exprRequiresMultimatchState = exprRequiresMultimatchState; + AddState(this); + } + + public override bool Matches(EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext) + { + if (_exprNode == null) + { + return true; + } + var result = (bool?) _exprNode.Evaluate(new EvaluateParams(eventsPerStream, true, exprEvaluatorContext)); + if (result != null) + { + return result.Value; + } + return false; + } + + public override String ToString() + { + if (_exprNode == null) + { + return "OneMany-Unfiltered"; + } + return "OneMany-Filtered"; + } + + public override bool IsExprRequiresMultimatchState + { + get { return _exprRequiresMultimatchState; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/rowregex/RegexNFAStateZeroToMany.cs b/NEsper.Core/NEsper.Core/rowregex/RegexNFAStateZeroToMany.cs new file mode 100755 index 000000000..0fb00ca05 --- /dev/null +++ b/NEsper.Core/NEsper.Core/rowregex/RegexNFAStateZeroToMany.cs @@ -0,0 +1,73 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.rowregex +{ + /// + /// The '*' state in the regex NFA states. + /// + public class RegexNFAStateZeroToMany + : RegexNFAStateBase + , RegexNFAState + { + private readonly ExprEvaluator _exprNode; + private readonly bool _exprRequiresMultimatchState; + + /// + /// Ctor. + /// + /// node num + /// variable name + /// stream number + /// true for multiple matches + /// true for greedy + /// filter expression + /// if set to true [expr requires multimatch state]. + public RegexNFAStateZeroToMany(String nodeNum, String variableName, int streamNum, bool multiple, bool? isGreedy, ExprNode exprNode, bool exprRequiresMultimatchState) + : base(nodeNum, variableName, streamNum, multiple, isGreedy) + { + _exprNode = exprNode == null ? null : exprNode.ExprEvaluator; + _exprRequiresMultimatchState = exprRequiresMultimatchState; + AddState(this); + } + + public override bool Matches(EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext) + { + if (_exprNode == null) + { + return true; + } + var result = (bool?) _exprNode.Evaluate(new EvaluateParams(eventsPerStream, true, exprEvaluatorContext)); + if (result != null) + { + return result.Value; + } + return false; + } + + public override String ToString() + { + if (_exprNode == null) + { + return "ZeroMany-Unfiltered"; + } + return "ZeroMany-Filtered"; + } + + public override bool IsExprRequiresMultimatchState + { + get { return _exprRequiresMultimatchState; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/rowregex/RegexNFAStrand.cs b/NEsper.Core/NEsper.Core/rowregex/RegexNFAStrand.cs new file mode 100755 index 000000000..f82b80a4e --- /dev/null +++ b/NEsper.Core/NEsper.Core/rowregex/RegexNFAStrand.cs @@ -0,0 +1,68 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +namespace com.espertech.esper.rowregex +{ + /// + /// A strand of one or more NFA states that has a list of start states, end states + /// and a list of all states in the strand. + /// + public class RegexNFAStrand + { + /// + /// Ctor. + /// + /// start states + /// end states + /// all states + /// true if this strand passes through (zero-or-more multiplicity for all NFA in strand) + public RegexNFAStrand(IList startStates, IList endStates, + IList allStates, bool passthrough) + { + StartStates = startStates; + EndStates = endStates; + AllStates = allStates; + IsPassthrough = passthrough; + } + + /// + /// Returns the start states. + /// + /// + /// start states + /// + public IList StartStates { get; private set; } + + /// + /// Returns the end states. + /// + /// + /// end states + /// + public IList EndStates { get; private set; } + + /// + /// Returns all states. + /// + /// + /// all states + /// + public IList AllStates { get; private set; } + + /// + /// Returns indicator if passing-through (zero-or-more multiplicity for all NFA + /// states in strand). + /// + /// + /// pass-through + /// + public bool IsPassthrough { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/rowregex/RegexNFAStrandResult.cs b/NEsper.Core/NEsper.Core/rowregex/RegexNFAStrandResult.cs new file mode 100755 index 000000000..ea9855f65 --- /dev/null +++ b/NEsper.Core/NEsper.Core/rowregex/RegexNFAStrandResult.cs @@ -0,0 +1,55 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +namespace com.espertech.esper.rowregex +{ + /// + /// A result of computing a strand of one or more NFA states that has a list of + /// start states and a list of all states in the strand. + /// + public class RegexNFAStrandResult + { + private IList startStates; + private IList allStates; + + /// + /// Ctor. + /// + /// NFA start states + /// all states + public RegexNFAStrandResult(IList startStates, IList allStates) + { + this.startStates = startStates; + this.allStates = allStates; + } + + /// + /// Returns start states. + /// + /// + /// start states + /// + public IList StartStates + { + get { return startStates; } + } + + /// + /// Returns all states. + /// + /// + /// all states + /// + public IList AllStates + { + get { return allStates; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/rowregex/RegexNFATypeEnum.cs b/NEsper.Core/NEsper.Core/rowregex/RegexNFATypeEnum.cs new file mode 100755 index 000000000..41b88a637 --- /dev/null +++ b/NEsper.Core/NEsper.Core/rowregex/RegexNFATypeEnum.cs @@ -0,0 +1,212 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.rowregex +{ + /// + /// Enum for NFA types. + /// + public enum RegexNFATypeEnum + { + /// + /// For greedy '?' multiplicity. + /// + ONE_OPTIONAL, + + /// + /// For reluctant '?' multiplicity. + /// + ONE_OPTIONAL_RELUCTANT, + + /// + /// For greedy '+' multiplicity. + /// + ONE_TO_MANY, + + /// + /// For reluctant '+' multiplicity. + /// + ONE_TO_MANY_RELUCTANT, + + /// + /// For single multiplicity. + /// + SINGLE, + + /// + /// For greedy '*' multiplicity. + /// + ZERO_TO_MANY, + + /// + /// For reluctant '*' multiplicity. + /// + ZERO_TO_MANY_RELUCTANT + } + + public static class RegexNFATypeEnumExtensions + { + /// + /// Returns indicator if single or multiple matches. + /// + /// + /// indicator + /// + public static bool IsMultipleMatches(this RegexNFATypeEnum value) + { + switch (value) + { + case RegexNFATypeEnum.ONE_OPTIONAL: + return false; + case RegexNFATypeEnum.ONE_OPTIONAL_RELUCTANT: + return false; + case RegexNFATypeEnum.ONE_TO_MANY: + return true; + case RegexNFATypeEnum.ONE_TO_MANY_RELUCTANT: + return true; + case RegexNFATypeEnum.SINGLE: + return false; + case RegexNFATypeEnum.ZERO_TO_MANY: + return true; + case RegexNFATypeEnum.ZERO_TO_MANY_RELUCTANT: + return true; + } + + throw new ArgumentException(); + } + + /// + /// Returns indicator if optional matches. + /// + /// + /// indicator + /// + public static bool IsOptional(this RegexNFATypeEnum value) + { + switch (value) + { + case RegexNFATypeEnum.ONE_OPTIONAL: + return true; + case RegexNFATypeEnum.ONE_OPTIONAL_RELUCTANT: + return true; + case RegexNFATypeEnum.ONE_TO_MANY: + return false; + case RegexNFATypeEnum.ONE_TO_MANY_RELUCTANT: + return false; + case RegexNFATypeEnum.SINGLE: + return false; + case RegexNFATypeEnum.ZERO_TO_MANY: + return true; + case RegexNFATypeEnum.ZERO_TO_MANY_RELUCTANT: + return true; + } + + throw new ArgumentException(); + } + + /// + /// Returns indicator if greedy or reluctant. + /// + /// + /// indicator + /// + public static bool? IsGreedy(this RegexNFATypeEnum value) + { + switch (value) + { + case RegexNFATypeEnum.ONE_OPTIONAL: + return true; + case RegexNFATypeEnum.ONE_OPTIONAL_RELUCTANT: + return false; + case RegexNFATypeEnum.ONE_TO_MANY: + return true; + case RegexNFATypeEnum.ONE_TO_MANY_RELUCTANT: + return false; + case RegexNFATypeEnum.SINGLE: + return null; + case RegexNFATypeEnum.ZERO_TO_MANY: + return true; + case RegexNFATypeEnum.ZERO_TO_MANY_RELUCTANT: + return false; + } + + throw new ArgumentException(); + } + + /// + /// Return postfix. + /// + /// + /// postfix + /// + public static string OptionalPostfix(this RegexNFATypeEnum value) + { + switch (value) + { + case RegexNFATypeEnum.ONE_OPTIONAL: + return "?"; + case RegexNFATypeEnum.ONE_OPTIONAL_RELUCTANT: + return "??"; + case RegexNFATypeEnum.ONE_TO_MANY: + return "+"; + case RegexNFATypeEnum.ONE_TO_MANY_RELUCTANT: + return "+?"; + case RegexNFATypeEnum.SINGLE: + return ""; + case RegexNFATypeEnum.ZERO_TO_MANY: + return "*"; + case RegexNFATypeEnum.ZERO_TO_MANY_RELUCTANT: + return "*?"; + } + + throw new ArgumentException("Invalid pattern type: " + value); + } + + /// + /// Inspect code and return enum for code. + /// + /// to inspect + /// null for greedy or questionmark for reluctant + /// + /// enum + /// + public static RegexNFATypeEnum FromString(String code, String reluctantQuestion) + { + bool reluctant = false; + if (reluctantQuestion != null) + { + if (!reluctantQuestion.Equals("?")) + { + throw new ArgumentException("Invalid code for pattern type: " + code + " reluctant '" + + reluctantQuestion + "'"); + } + reluctant = true; + } + + if (code == null) + { + return RegexNFATypeEnum.SINGLE; + } + if (code.Equals("*")) + { + return reluctant ? RegexNFATypeEnum.ZERO_TO_MANY_RELUCTANT : RegexNFATypeEnum.ZERO_TO_MANY; + } + if (code.Equals("+")) + { + return reluctant ? RegexNFATypeEnum.ONE_TO_MANY_RELUCTANT : RegexNFATypeEnum.ONE_TO_MANY; + } + if (code.Equals("?")) + { + return reluctant ? RegexNFATypeEnum.ONE_OPTIONAL_RELUCTANT : RegexNFATypeEnum.ONE_OPTIONAL; + } + throw new ArgumentException("Invalid code for pattern type: " + code); + } + } +} diff --git a/NEsper.Core/NEsper.Core/rowregex/RegexPartitionState.cs b/NEsper.Core/NEsper.Core/rowregex/RegexPartitionState.cs new file mode 100755 index 000000000..d825c3971 --- /dev/null +++ b/NEsper.Core/NEsper.Core/rowregex/RegexPartitionState.cs @@ -0,0 +1,24 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.rowregex +{ + public interface RegexPartitionState + { + RegexPartitionStateRandomAccess RandomAccess { get; } + IEnumerator CurrentStatesEnumerator { get; } + IList CurrentStates { get; set; } + object OptionalKeys { get; } + int NumStates { get; } + ICollection CurrentStatesForPrint { get; } + bool IsEmptyCurrentState { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/rowregex/RegexPartitionStateImpl.cs b/NEsper.Core/NEsper.Core/rowregex/RegexPartitionStateImpl.cs new file mode 100755 index 000000000..73b8ea5ad --- /dev/null +++ b/NEsper.Core/NEsper.Core/rowregex/RegexPartitionStateImpl.cs @@ -0,0 +1,201 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.rowregex +{ + /// + /// All current state holding partial NFA matches. + /// + public class RegexPartitionStateImpl : RegexPartitionState + { + private readonly RegexPartitionStateRandomAccess _randomAccess; + private IList _currentStates = new List(); + private readonly object _optionalKeys; + + /// + /// Ctor. + /// + /// for handling "prev" functions, if any + /// keys for "partition", if any + public RegexPartitionStateImpl(RegexPartitionStateRandomAccess randomAccess, object optionalKeys) + { + this._randomAccess = randomAccess; + this._optionalKeys = optionalKeys; + } + + /// + /// Ctor. + /// + /// for "prev" access + /// existing state + public RegexPartitionStateImpl(RegexPartitionStateRandomAccessGetter getter, ICollection currentStates) + : this(getter, currentStates, null) + { + } + + /// + /// Ctor. + /// + /// for "prev" access + /// existing state + /// partition keys if any + public RegexPartitionStateImpl(RegexPartitionStateRandomAccessGetter getter, ICollection currentStates, object optionalKeys) + { + if (getter != null) + { + _randomAccess = new RegexPartitionStateRandomAccessImpl(getter); + } + + this._currentStates = currentStates.AsList(); + this._optionalKeys = optionalKeys; + } + + /// + /// Returns the random access for "prev". + /// + /// access + public RegexPartitionStateRandomAccess RandomAccess + { + get { return _randomAccess; } + } + + /// + /// Returns partial matches. + /// + /// state + public IEnumerator CurrentStatesEnumerator + { + get { return _currentStates.GetEnumerator(); } + } + + /// + /// Sets partial matches. + /// + /// state to set + public virtual IList CurrentStates + { + get { return this._currentStates; } + set { this._currentStates = value.AsList(); } + } + + /// + /// Returns partition keys, if any. + /// + /// keys + public virtual object OptionalKeys + { + get { return _optionalKeys; } + } + + /// + /// Remove an event from random access for "prev". + /// + /// to remove + public virtual void RemoveEventFromPrev(EventBean[] oldEvents) + { + if (_randomAccess != null) + { + _randomAccess.Remove(oldEvents); + } + } + + /// + /// Remove an event from random access for "prev". + /// + /// to remove + public virtual void RemoveEventFromPrev(EventBean oldEvent) + { + if (_randomAccess != null) + { + _randomAccess.Remove(oldEvent); + } + } + + /// + /// Remove an event from state. + /// + /// to remove + /// true for removed, false for not found + public virtual int RemoveEventFromState(EventBean oldEvent) + { + int currentSize = _currentStates.Count; + var keepList = RemoveEventFromState(oldEvent, _currentStates.GetEnumerator()); + if (_randomAccess != null) { + _randomAccess.Remove(oldEvent); + } + _currentStates = keepList; + return currentSize - keepList.Count; + } + + public virtual int NumStates + { + get { return _currentStates.Count; } + } + + public void ClearCurrentStates() + { + _currentStates.Clear(); + } + + public ICollection CurrentStatesForPrint + { + get { return _currentStates; } + } + + public bool IsEmptyCurrentState + { + get { return _currentStates.IsEmpty(); } + } + + public static IList RemoveEventFromState(EventBean oldEvent, IEnumerator states) + { + IList keepList = new List(); + for (;states.MoveNext();) + { + RegexNFAStateEntry entry = states.Current; + var keep = true; + + EventBean[] state = entry.EventsPerStream; + foreach (EventBean aState in state) + { + if (aState != null && aState.Equals(oldEvent)) + { + keep = false; + break; + } + } + + if (keep) + { + MultimatchState[] multimatch = entry.OptionalMultiMatches; + if (multimatch != null) { + foreach (MultimatchState aMultimatch in multimatch) { + if ((aMultimatch != null) && (aMultimatch.ContainsEvent(oldEvent))) { + keep = false; + break; + } + } + } + } + + if (keep) + { + keepList.Add(entry); + } + } + return keepList; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/rowregex/RegexPartitionStateRandomAccess.cs b/NEsper.Core/NEsper.Core/rowregex/RegexPartitionStateRandomAccess.cs new file mode 100755 index 000000000..2a2d115e1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/rowregex/RegexPartitionStateRandomAccess.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; + +namespace com.espertech.esper.rowregex +{ + /// + /// Interface for random access to a previous event. + /// + public interface RegexPartitionStateRandomAccess + { + /// + /// Returns an new data event given an index. + /// + /// to return new data for + /// + /// new data event + /// + EventBean GetPreviousEvent(int index); + + void NewEventPrepare(EventBean newEvent); + + void ExistingEventPrepare(EventBean theEvent); + + void Remove(EventBean[] oldEvents); + + void Remove(EventBean oldEvent); + + bool IsEmpty { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/rowregex/RegexPartitionStateRandomAccessGetter.cs b/NEsper.Core/NEsper.Core/rowregex/RegexPartitionStateRandomAccessGetter.cs new file mode 100755 index 000000000..0715ad503 --- /dev/null +++ b/NEsper.Core/NEsper.Core/rowregex/RegexPartitionStateRandomAccessGetter.cs @@ -0,0 +1,92 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.rowregex +{ + /// + /// Getter that provides an index at runtime. + /// + public class RegexPartitionStateRandomAccessGetter : RegexExprPreviousEvalStrategy + { + private readonly int[] _randomAccessIndexesRequested; + private readonly int _maxPriorIndex; + + private RegexPartitionStateRandomAccess _randomAccess; + private readonly bool _isUnbound; + + public RegexPartitionStateRandomAccess GetAccess(ExprEvaluatorContext exprEvaluatorContext) { + return _randomAccess; + } + + /// Ctor. + /// requested indexes + /// true if unbound + public RegexPartitionStateRandomAccessGetter(int[] randomAccessIndexesRequested, bool isUnbound) + { + _randomAccessIndexesRequested = randomAccessIndexesRequested; + _isUnbound = isUnbound; + + // Determine the maximum prior index to retain + int maxPriorIndex = 0; + foreach (int priorIndex in randomAccessIndexesRequested) + { + if (priorIndex > maxPriorIndex) + { + maxPriorIndex = priorIndex; + } + } + + _maxPriorIndex = maxPriorIndex; + } + + /// Returns max index. + /// index + public int MaxPriorIndex + { + get { return _maxPriorIndex; } + } + + /// Returns indexs. + /// indexes. + public int[] IndexesRequested + { + get { return _randomAccessIndexesRequested; } + } + + /// Returns length of indexes. + /// index len + public int IndexesRequestedLen + { + get { return _randomAccessIndexesRequested.Length; } + } + + /// Returns true for unbound. + /// unbound indicator + public bool IsUnbound + { + get { return _isUnbound; } + } + + /// Returns the index for access. + /// index + public RegexPartitionStateRandomAccess Accessor + { + get { return _randomAccess; } + } + + /// Sets the random access. + /// to use + public RegexPartitionStateRandomAccess RandomAccess + { + set { _randomAccess = value; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/rowregex/RegexPartitionStateRandomAccessImpl.cs b/NEsper.Core/NEsper.Core/rowregex/RegexPartitionStateRandomAccessImpl.cs new file mode 100755 index 000000000..a2e172d64 --- /dev/null +++ b/NEsper.Core/NEsper.Core/rowregex/RegexPartitionStateRandomAccessImpl.cs @@ -0,0 +1,148 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.rowregex +{ + /// + /// "Prev" state for random access to event history. + /// + public class RegexPartitionStateRandomAccessImpl : RegexPartitionStateRandomAccess + { + private readonly RegexPartitionStateRandomAccessGetter _getter; + private readonly IDictionary _priorEventMap; + private readonly RollingEventBuffer _newEvents; + private EventBean[] _lastNew; + + /// + /// Ctor. + /// + /// for access + public RegexPartitionStateRandomAccessImpl(RegexPartitionStateRandomAccessGetter getter) + { + _getter = getter; + + // Construct a rolling buffer of new data for holding max index + 1 (position 1 requires 2 events to keep) + _newEvents = new RollingEventBuffer(getter.MaxPriorIndex + 1); + if (!getter.IsUnbound) + { + _priorEventMap = new Dictionary(); + } + else + { + _priorEventMap = null; + } + } + + /// + /// Add new event. + /// + /// to add + public void NewEventPrepare(EventBean newEvent) + { + // Add new event + _newEvents.Add(newEvent); + + // Save prior index events in array + EventBean[] priorEvents = new EventBean[_getter.IndexesRequestedLen]; + for (int j = 0; j < priorEvents.Length; j++) + { + int priorIndex = _getter.IndexesRequested[j]; + priorEvents[j] = _newEvents.Get(priorIndex); + } + + if (_priorEventMap != null) + { + _priorEventMap.Put(newEvent, priorEvents); + } + + _lastNew = priorEvents; + _getter.RandomAccess = this; + } + + /// + /// Prepare relative to existing event, for iterating. + /// + /// to consider for index + public void ExistingEventPrepare(EventBean newEvent) + { + if (_priorEventMap != null) + { + _lastNew = _priorEventMap.Get(newEvent); + } + _getter.RandomAccess = this; + } + + /// + /// Returns a previous event. Always immediatly preceded by #newEventPrepare. + /// + /// index + /// + /// event + /// + public EventBean GetPreviousEvent(int assignedRelativeIndex) + { + if (_lastNew == null) + { + return null; + } + return _lastNew[assignedRelativeIndex]; + } + + /// + /// Remove events. + /// + /// to remove + public void Remove(EventBean[] oldEvents) + { + if (oldEvents == null) + { + return; + } + for (int i = 0; i < oldEvents.Length; i++) + { + Remove(oldEvents[i]); + } + } + + /// + /// Remove event. + /// + /// to remove + public void Remove(EventBean oldEvent) + { + if (_priorEventMap != null) + { + _priorEventMap.Remove(oldEvent); + } + } + + /// + /// Returns true for empty collection. + /// + /// + /// indicator if empty + /// + public bool IsEmpty + { + get + { + if (_priorEventMap != null) + { + _priorEventMap.IsEmpty(); + } + return true; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/rowregex/RegexPartitionStateRepo.cs b/NEsper.Core/NEsper.Core/rowregex/RegexPartitionStateRepo.cs new file mode 100755 index 000000000..d7ade61b7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/rowregex/RegexPartitionStateRepo.cs @@ -0,0 +1,63 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.rowregex +{ + /// Service for holding partition state. + public interface RegexPartitionStateRepo : IDisposable + { + /// + /// Return state for key or create state if not found. + /// + /// to look up + /// state + RegexPartitionState GetState(Object key); + + /// + /// Return state for event or create state if not found. + /// + /// to look up + /// true if a collection of unused state can occur + /// state + RegexPartitionState GetState(EventBean theEvent, bool isCollect); + + /// + /// Remove old events from the state, applicable for "prev" function and partial NFA state. + /// + /// to remove + /// indicator if there are not matches + /// indicator if any partial matches exist to be deleted + /// number removed + int RemoveOld(EventBean[] events, bool isEmpty, bool[] found); + + /// + /// Copy state for iteration. + /// + /// indicator whether we are processing out-of-order events + /// copied state + RegexPartitionStateRepo CopyForIterate(bool forOutOfOrderReprocessing); + + void RemoveState(Object partitionKey); + + void Accept(EventRowRegexNFAViewServiceVisitor visitor); + + bool IsPartitioned { get; } + + int StateCount { get; } + + int IncrementAndGetEventSequenceNum(); + + int EventSequenceNum { set; } + + RegexPartitionStateRepoScheduleState ScheduleState { get; } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/rowregex/RegexPartitionStateRepoGroup.cs b/NEsper.Core/NEsper.Core/rowregex/RegexPartitionStateRepoGroup.cs new file mode 100755 index 000000000..46a0c554f --- /dev/null +++ b/NEsper.Core/NEsper.Core/rowregex/RegexPartitionStateRepoGroup.cs @@ -0,0 +1,259 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.rowregex +{ + /// + /// Partition-by implementation for partition state. + /// + public class RegexPartitionStateRepoGroup : RegexPartitionStateRepo + { + /// EmptyFalse state collection initial threshold. + public readonly static int INITIAL_COLLECTION_MIN = 100; + + private readonly RegexPartitionStateRepoGroupMeta _meta; + private readonly RegexPartitionStateRandomAccessGetter _getter; + private readonly IDictionary _states; + private readonly RegexPartitionStateRepoScheduleStateImpl _optionalIntervalSchedules; + + private int _currentCollectionSize = INITIAL_COLLECTION_MIN; + private int _eventSequenceNumber; + + /// + /// Ctor. + /// + /// for "prev" function access + /// general metadata for grouping + public RegexPartitionStateRepoGroup(RegexPartitionStateRandomAccessGetter getter, + RegexPartitionStateRepoGroupMeta meta, + bool keepScheduleState, + RegexPartitionTerminationStateComparator terminationStateCompare) + { + _getter = getter; + _meta = meta; + _states = new NullableDictionary(); + _optionalIntervalSchedules = keepScheduleState ? new RegexPartitionStateRepoScheduleStateImpl(terminationStateCompare) : null; + } + + public int IncrementAndGetEventSequenceNum() + { + ++_eventSequenceNumber; + return _eventSequenceNumber; + } + + public int EventSequenceNum + { + get { return _eventSequenceNumber; } + set { _eventSequenceNumber = value; } + } + + public RegexPartitionStateRepoScheduleState ScheduleState + { + get { return _optionalIntervalSchedules; } + } + + public void RemoveState(Object partitionKey) + { + _states.Remove(partitionKey); + } + + public RegexPartitionStateRepo CopyForIterate(bool forOutOfOrderReprocessing) + { + var copy = new RegexPartitionStateRepoGroup(_getter, _meta, false, null); + foreach (var entry in _states) + { + copy._states[entry.Key] = new RegexPartitionStateImpl(entry.Value.RandomAccess, entry.Key); + } + return copy; + } + + public int RemoveOld(EventBean[] oldData, bool isEmpty, bool[] found) + { + int countRemoved = 0; + + if (isEmpty) + { + if (_getter == null) + { + // no "prev" used, clear all state + countRemoved = StateCount; + _states.Clear(); + } + else + { + foreach (var entry in _states) + { + countRemoved += entry.Value.NumStates; + entry.Value.CurrentStates = Collections.GetEmptyList(); + } + } + + // clear "prev" state + if (_getter != null) + { + // we will need to remove event-by-event + for (var i = 0; i < oldData.Length; i++) + { + var partitionState = GetState(oldData[i], true) as RegexPartitionStateImpl; + if (partitionState == null) + { + continue; + } + partitionState.RemoveEventFromPrev(oldData); + } + } + + return countRemoved; + } + + // we will need to remove event-by-event + for (var i = 0; i < oldData.Length; i++) + { + var partitionState = GetState(oldData[i], true) as RegexPartitionStateImpl; + if (partitionState == null) + { + continue; + } + + if (found[i]) + { + countRemoved += partitionState.RemoveEventFromState(oldData[i]); + var cleared = partitionState.NumStates == 0; + if (cleared) + { + if (_getter == null) + { + _states.Remove(partitionState.OptionalKeys); + } + } + } + + partitionState.RemoveEventFromPrev(oldData[i]); + } + + return countRemoved; + } + + public RegexPartitionState GetState(Object key) + { + return _states.Get(key); + } + + public RegexPartitionState GetState(EventBean theEvent, bool isCollect) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QRegExPartition(_meta.PartitionExpressionNodes); } + + // collect unused states + if ((isCollect) && (_states.Count >= _currentCollectionSize)) + { + IList removeList = new List(); + foreach (var entry in _states) + { + if ((entry.Value.IsEmptyCurrentState) && + (entry.Value.RandomAccess == null || entry.Value.RandomAccess.IsEmpty)) + { + removeList.Add(entry.Key); + } + } + + foreach (var removeKey in removeList) + { + _states.Remove(removeKey); + } + + if (removeList.Count < (_currentCollectionSize / 5)) + { + _currentCollectionSize *= 2; + } + } + + var key = GetKeys(theEvent, _meta); + + var state = _states.Get(key); + if (state != null) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().ARegExPartition(true, state); } + return state; + } + + state = new RegexPartitionStateImpl(_getter, new List(), key); + _states.Put(key, state); + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().ARegExPartition(false, state); } + return state; + } + + public void Accept(EventRowRegexNFAViewServiceVisitor visitor) + { + visitor.VisitPartitioned((IDictionary)_states); + } + + public bool IsPartitioned + { + get { return true; } + } + + public IDictionary States + { + get { return _states; } + } + + public int StateCount + { + get { return _states.Sum(entry => entry.Value.NumStates); } + } + + public static Object GetKeys(EventBean theEvent, RegexPartitionStateRepoGroupMeta meta) + { + var eventsPerStream = meta.EventsPerStream; + eventsPerStream[0] = theEvent; + + var partitionExpressions = meta.PartitionExpressions; + if (partitionExpressions.Length == 1) + { + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().QExprValue(meta.PartitionExpressionNodes[0], eventsPerStream); + var value = partitionExpressions[0].Evaluate(new EvaluateParams(eventsPerStream, true, meta.ExprEvaluatorContext)); + InstrumentationHelper.Get().AExprValue(value); + return value; + } + else + { + return partitionExpressions[0].Evaluate(new EvaluateParams(eventsPerStream, true, meta.ExprEvaluatorContext)); + } + } + + var keys = new Object[partitionExpressions.Length]; + var count = 0; + var exprEvaluatorContext = meta.ExprEvaluatorContext; + foreach (var node in partitionExpressions) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QExprValue(meta.PartitionExpressionNodes[count], eventsPerStream); } + keys[count] = node.Evaluate(new EvaluateParams(eventsPerStream, true, exprEvaluatorContext)); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AExprValue(keys[count]); } + count++; + } + return new MultiKeyUntyped(keys); + } + + public void Dispose() + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/rowregex/RegexPartitionStateRepoGroupMeta.cs b/NEsper.Core/NEsper.Core/rowregex/RegexPartitionStateRepoGroupMeta.cs new file mode 100755 index 000000000..dd4b4336f --- /dev/null +++ b/NEsper.Core/NEsper.Core/rowregex/RegexPartitionStateRepoGroupMeta.cs @@ -0,0 +1,36 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.rowregex +{ + public class RegexPartitionStateRepoGroupMeta + { + public RegexPartitionStateRepoGroupMeta(bool hasInterval, ExprNode[] partitionExpressionNodes, ExprEvaluator[] partitionExpressions, ExprEvaluatorContext exprEvaluatorContext) + { + EventsPerStream = new EventBean[1]; + HasInterval = hasInterval; + PartitionExpressionNodes = partitionExpressionNodes; + PartitionExpressions = partitionExpressions; + ExprEvaluatorContext = exprEvaluatorContext; + } + + public bool HasInterval { get; private set; } + + public ExprNode[] PartitionExpressionNodes { get; private set; } + + public ExprEvaluator[] PartitionExpressions { get; private set; } + + public ExprEvaluatorContext ExprEvaluatorContext { get; private set; } + + public EventBean[] EventsPerStream { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/rowregex/RegexPartitionStateRepoNoGroup.cs b/NEsper.Core/NEsper.Core/rowregex/RegexPartitionStateRepoNoGroup.cs new file mode 100755 index 000000000..d298c2bc3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/rowregex/RegexPartitionStateRepoNoGroup.cs @@ -0,0 +1,130 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.rowregex +{ + /// + /// State for when no partitions (single partition) is required. + /// + public class RegexPartitionStateRepoNoGroup : RegexPartitionStateRepo + { + private readonly RegexPartitionStateImpl _singletonState; + private readonly RegexPartitionStateRepoScheduleStateImpl _optionalIntervalSchedules; + private int _eventSequenceNumber; + + /// + /// Ctor. + /// + /// state + public RegexPartitionStateRepoNoGroup(RegexPartitionStateImpl singletonState) + { + _singletonState = singletonState; + _optionalIntervalSchedules = null; + } + + /// + /// Ctor. + /// + /// The getter. + /// if set to true [keep schedule state]. + /// The termination state compare. + public RegexPartitionStateRepoNoGroup(RegexPartitionStateRandomAccessGetter getter, bool keepScheduleState, RegexPartitionTerminationStateComparator terminationStateCompare) + { + _singletonState = new RegexPartitionStateImpl(getter, new List()); + _optionalIntervalSchedules = keepScheduleState ? new RegexPartitionStateRepoScheduleStateImpl(terminationStateCompare) : null; + } + + public int IncrementAndGetEventSequenceNum() + { + ++_eventSequenceNumber; + return _eventSequenceNumber; + } + + public int EventSequenceNum + { + get { return _eventSequenceNumber; } + set { _eventSequenceNumber = value; } + } + + public RegexPartitionStateRepoScheduleState ScheduleState + { + get { return _optionalIntervalSchedules; } + } + + + public void RemoveState(Object partitionKey) + { + // not an operation + } + + /// + /// Copy state for iteration. + /// + /// For out of order reprocessing. + /// + public RegexPartitionStateRepo CopyForIterate(bool forOutOfOrderReprocessing) + { + var state = new RegexPartitionStateImpl(_singletonState.RandomAccess, null); + return new RegexPartitionStateRepoNoGroup(state); + } + + public int RemoveOld(EventBean[] oldEvents, bool isEmpty, bool[] found) + { + int countRemoved = 0; + if (isEmpty) + { + countRemoved = _singletonState.NumStates; + _singletonState.CurrentStates = Collections.GetEmptyList(); + } + else + { + foreach (EventBean oldEvent in oldEvents) + { + countRemoved += _singletonState.RemoveEventFromState(oldEvent); + } + } + _singletonState.RemoveEventFromPrev(oldEvents); + return countRemoved; + } + + public RegexPartitionState GetState(EventBean theEvent, bool collect) + { + return _singletonState; + } + + public RegexPartitionState GetState(Object key) + { + return _singletonState; + } + + public void Accept(EventRowRegexNFAViewServiceVisitor visitor) + { + visitor.VisitUnpartitioned(_singletonState); + } + + public bool IsPartitioned + { + get { return false; } + } + + public int StateCount + { + get { return _singletonState.NumStates; } + } + + public void Dispose() + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/rowregex/RegexPartitionStateRepoScheduleState.cs b/NEsper.Core/NEsper.Core/rowregex/RegexPartitionStateRepoScheduleState.cs new file mode 100755 index 000000000..e7906b1f7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/rowregex/RegexPartitionStateRepoScheduleState.cs @@ -0,0 +1,42 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +namespace com.espertech.esper.rowregex +{ + /// + /// Service for holding schedule state. + /// + public interface RegexPartitionStateRepoScheduleState + { + bool IsEmpty(); + + /// + /// Add entry returning true if the key did not exist. + /// + /// key + /// entry + /// indicator + bool PutOrAdd(long matchBeginTime, RegexNFAStateEntry state); + + long FirstKey(); + void RemoveAddRemoved(long matchBeginTime, IList foundStates); + bool ContainsKey(long matchBeginTime); + + /// + /// Find and remove operation, wherein removed items are added to the found list, + /// returning an indicator whether the item was found and removed + /// + /// key + /// entry + /// list to be added to + /// indicator whether any item was found and removed + bool FindRemoveAddToList(long matchBeginTime, RegexNFAStateEntry state, IList foundStates); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/rowregex/RegexPartitionStateRepoScheduleStateImpl.cs b/NEsper.Core/NEsper.Core/rowregex/RegexPartitionStateRepoScheduleStateImpl.cs new file mode 100755 index 000000000..aa59786b4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/rowregex/RegexPartitionStateRepoScheduleStateImpl.cs @@ -0,0 +1,107 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.rowregex +{ + public class RegexPartitionStateRepoScheduleStateImpl : RegexPartitionStateRepoScheduleState + { + private readonly RegexPartitionTerminationStateComparator terminationStateCompare; + private readonly SortedDictionary schedule = new SortedDictionary(); + + public RegexPartitionStateRepoScheduleStateImpl(RegexPartitionTerminationStateComparator terminationStateCompare) { + this.terminationStateCompare = terminationStateCompare; + } + + public bool IsEmpty() { + return schedule.IsEmpty(); + } + + public bool PutOrAdd(long matchBeginTime, RegexNFAStateEntry state) { + object value = schedule.Get(matchBeginTime); + if (value == null) { + schedule.Put(matchBeginTime, state); + return true; + } + + if (value is RegexNFAStateEntry) + { + RegexNFAStateEntry valueEntry = (RegexNFAStateEntry) value; + IList list = new List(); + list.Add(valueEntry); + list.Add(state); + schedule.Put(matchBeginTime, list); + } + else + { + IList list = (IList) value; + list.Add(state); + } + + return false; + } + + public object Get(long matchBeginTime) { + return schedule.Get(matchBeginTime); + } + + public long FirstKey() + { + return schedule.Keys.First().Value; + } + + public void RemoveAddRemoved(long matchBeginTime, IList foundStates) { + var found = schedule.Delete(matchBeginTime); + if (found == null) { + return; + } + if (found is RegexNFAStateEntry) { + foundStates.Add((RegexNFAStateEntry) found); + } + else { + foundStates.AddAll((IList) found); + } + } + + public bool ContainsKey(long matchBeginTime) { + return schedule.ContainsKey(matchBeginTime); + } + + public bool FindRemoveAddToList(long matchBeginTime, RegexNFAStateEntry state, IList foundStates) { + object entry = schedule.Get(matchBeginTime); + if (entry == null) { + return false; + } + if (entry is RegexNFAStateEntry) { + RegexNFAStateEntry single = (RegexNFAStateEntry) entry; + if (terminationStateCompare.CompareTerminationStateToEndState(state, single)) { + schedule.Remove(matchBeginTime); + foundStates.Add(single); + return true; + } + return false; + } + + var entries = (IList) entry; + var removed = entries.RemoveWhere( + endState => terminationStateCompare.CompareTerminationStateToEndState(state, endState), + endState => foundStates.Add(endState)) > 0; + + if (entries.IsEmpty()) { + schedule.Remove(matchBeginTime); + } + return removed; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/rowregex/RegexPartitionTerminationStateComparator.cs b/NEsper.Core/NEsper.Core/rowregex/RegexPartitionTerminationStateComparator.cs new file mode 100755 index 000000000..81d29b394 --- /dev/null +++ b/NEsper.Core/NEsper.Core/rowregex/RegexPartitionTerminationStateComparator.cs @@ -0,0 +1,77 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.events; + +namespace com.espertech.esper.rowregex +{ + public class RegexPartitionTerminationStateComparator : IComparer + { + private readonly int[] _multimatchStreamNumToVariable; + private readonly LinkedHashMap> _variableStreams; + + public RegexPartitionTerminationStateComparator(int[] multimatchStreamNumToVariable, LinkedHashMap> variableStreams) + { + _multimatchStreamNumToVariable = multimatchStreamNumToVariable; + _variableStreams = variableStreams; + } + + public int Compare(RegexNFAStateEntry o1, RegexNFAStateEntry o2) + { + return CompareTerminationStateToEndState(o1, o2) ? 0 : 1; + } + + // End-state may have less events then the termination state + public bool CompareTerminationStateToEndState(RegexNFAStateEntry terminationState, RegexNFAStateEntry endState) + { + if (terminationState.MatchBeginEventSeqNo != endState.MatchBeginEventSeqNo) + { + return false; + } + foreach (var entry in _variableStreams) + { + int stream = entry.Value.First; + bool multi = entry.Value.Second; + if (multi) + { + EventBean[] termStreamEvents = EventRowRegexNFAViewUtil.GetMultimatchArray(_multimatchStreamNumToVariable, terminationState, stream); + EventBean[] endStreamEvents = EventRowRegexNFAViewUtil.GetMultimatchArray(_multimatchStreamNumToVariable, endState, stream); + if (endStreamEvents != null) + { + if (termStreamEvents == null) + { + return false; + } + for (int i = 0; i < endStreamEvents.Length; i++) + { + if (termStreamEvents.Length > i && !EventBeanUtility.EventsAreEqualsAllowNull(endStreamEvents[i], termStreamEvents[i])) + { + return false; + } + } + } + } + else + { + EventBean termStreamEvent = terminationState.EventsPerStream[stream]; + EventBean endStreamEvent = endState.EventsPerStream[stream]; + if (!EventBeanUtility.EventsAreEqualsAllowNull(endStreamEvent, termStreamEvent)) + { + return false; + } + } + } + return true; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/rowregex/RegexPatternExpandUtil.cs b/NEsper.Core/NEsper.Core/rowregex/RegexPatternExpandUtil.cs new file mode 100755 index 000000000..9703a5d1f --- /dev/null +++ b/NEsper.Core/NEsper.Core/rowregex/RegexPatternExpandUtil.cs @@ -0,0 +1,331 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.util; + +namespace com.espertech.esper.rowregex +{ + public class RegexPatternExpandUtil + { + private static readonly RowRegexExprNodeCopierAtom ATOM_HANDLER = new RowRegexExprNodeCopierAtom(); + private static readonly RowRegexExprNodeCopierNested NESTED_HANDLER = new RowRegexExprNodeCopierNested(); + + public static RowRegexExprNode Expand(RowRegexExprNode pattern) + { + var visitor = new RowRegexExprNodeVisitorRepeat(); + pattern.Accept(visitor); + var newParentNode = pattern; + + // expand permutes + var permutes = visitor.Permutes; + permutes.SortInPlace((o1, o2) => + { + if (o1.Level > o2.Level) + { + return -1; + } + return o1.Level == o2.Level ? 0 : 1; + }); + + foreach (var permute in permutes) + { + var alteration = ExpandPermute(permute.Permute); + RowRegexExprNode optionalNewParent = Replace(permute.OptionalParent, permute.Permute, Collections.SingletonList(alteration)); + if (optionalNewParent != null) + { + newParentNode = optionalNewParent; + } + } + + // expand atoms + var atomPairs = visitor.Atoms; + foreach (var pair in atomPairs) + { + var atom = pair.First; + var expandedRepeat = ExpandRepeat(atom, atom.OptionalRepeat, atom.NFAType, ATOM_HANDLER); + var optionalNewParent = Replace(pair.Second, pair.First, expandedRepeat); + if (optionalNewParent != null) + { + newParentNode = optionalNewParent; + } + } + + // expand nested + var nestedPairs = visitor.Nesteds; + nestedPairs.SortInPlace((o1, o2) => + { + if (o1.Level > o2.Level) + { + return -1; + } + return o1.Level == o2.Level ? 0 : 1; + }); + + foreach (var pair in nestedPairs) + { + var nested = pair.Nested; + var expandedRepeat = ExpandRepeat(nested, nested.OptionalRepeat, nested.NFAType, NESTED_HANDLER); + var optionalNewParent = Replace(pair.OptionalParent, pair.Nested, expandedRepeat); + if (optionalNewParent != null) + { + newParentNode = optionalNewParent; + } + } + + return newParentNode; + } + + private static RowRegexExprNodeAlteration ExpandPermute(RowRegexExprNodePermute permute) + { + var e = PermutationEnumerator.Create(permute.ChildNodes.Count); + var parent = new RowRegexExprNodeAlteration(); + foreach (int[] indexes in e) + { + var concat = new RowRegexExprNodeConcatenation(); + parent.AddChildNode(concat); + for (var i = 0; i < indexes.Length; i++) + { + RowRegexExprNode toCopy = permute.ChildNodes[indexes[i]]; + var copy = CheckedCopy(toCopy); + concat.AddChildNode(copy); + } + } + return parent; + } + + private static RowRegexExprNode Replace(RowRegexExprNode optionalParent, RowRegexExprNode originalNode, IList expandedRepeat) + { + if (optionalParent == null) + { + var newParentNode = new RowRegexExprNodeConcatenation(); + newParentNode.ChildNodes.AddAll(expandedRepeat); + return newParentNode; + } + + // for nested nodes, use a concatenation instead + if (optionalParent is RowRegexExprNodeNested || + optionalParent is RowRegexExprNodeAlteration) + { + var concatenation = new RowRegexExprNodeConcatenation(); + concatenation.ChildNodes.AddAll(expandedRepeat); + optionalParent.ReplaceChildNode(originalNode, Collections.SingletonList(concatenation)); + } + // concatenations are simply changed + else + { + optionalParent.ReplaceChildNode(originalNode, expandedRepeat); + } + + return null; + } + + private static IList ExpandRepeat(RowRegexExprNode node, + RowRegexExprRepeatDesc repeat, + RegexNFATypeEnum type, + RowRegexExprNodeCopier copier) + { + var evaluateParams = new EvaluateParams(null, true, null); + // handle single-bounds (no ranges) + IList repeated = new List(); + if (repeat.Single != null) + { + ValidateExpression(repeat.Single); + int numRepeated = repeat.Single.ExprEvaluator.Evaluate(evaluateParams).AsInt(); + ValidateRange(numRepeated, 1, int.MaxValue); + for (var i = 0; i < numRepeated; i++) + { + var copy = copier.Copy(node, type); + repeated.Add(copy); + } + return repeated; + } + + // evaluate bounds + int? lower = null; + int? upper = null; + if (repeat.Lower != null) + { + ValidateExpression(repeat.Lower); + lower = (int?)repeat.Lower.ExprEvaluator.Evaluate(evaluateParams); + } + if (repeat.Upper != null) + { + ValidateExpression(repeat.Upper); + upper = (int?)repeat.Upper.ExprEvaluator.Evaluate(evaluateParams); + } + + // handle range + if (lower != null && upper != null) + { + ValidateRange(lower.Value, 1, int.MaxValue); + ValidateRange(upper.Value, 1, int.MaxValue); + ValidateRange(lower.Value, 1, upper.Value); + for (var i = 0; i < lower; i++) + { + var copy = copier.Copy(node, type); + repeated.Add(copy); + } + for (int i = lower.Value; i < upper; i++) + { + // make type optional + var newType = type; + if (type == RegexNFATypeEnum.SINGLE) + { + newType = RegexNFATypeEnum.ONE_OPTIONAL; + } + else if (type == RegexNFATypeEnum.ONE_TO_MANY) + { + newType = RegexNFATypeEnum.ZERO_TO_MANY; + } + else if (type == RegexNFATypeEnum.ONE_TO_MANY_RELUCTANT) + { + newType = RegexNFATypeEnum.ZERO_TO_MANY_RELUCTANT; + } + var copy = copier.Copy(node, newType); + repeated.Add(copy); + } + return repeated; + } + + // handle lower-bounds only + if (upper == null) + { + ValidateRange(lower.Value, 1, int.MaxValue); + for (var i = 0; i < lower; i++) + { + var copyInner = copier.Copy(node, type); + repeated.Add(copyInner); + } + // make type optional + var newType = type; + if (type == RegexNFATypeEnum.SINGLE) + { + newType = RegexNFATypeEnum.ZERO_TO_MANY; + } + else if (type == RegexNFATypeEnum.ONE_OPTIONAL) + { + newType = RegexNFATypeEnum.ZERO_TO_MANY; + } + else if (type == RegexNFATypeEnum.ONE_OPTIONAL_RELUCTANT) + { + newType = RegexNFATypeEnum.ZERO_TO_MANY_RELUCTANT; + } + else if (type == RegexNFATypeEnum.ONE_TO_MANY) + { + newType = RegexNFATypeEnum.ZERO_TO_MANY; + } + else if (type == RegexNFATypeEnum.ONE_TO_MANY_RELUCTANT) + { + newType = RegexNFATypeEnum.ZERO_TO_MANY_RELUCTANT; + } + var copy = copier.Copy(node, newType); + repeated.Add(copy); + return repeated; + } + + // handle upper-bounds only + ValidateRange(upper.Value, 1, int.MaxValue); + for (var i = 0; i < upper; i++) + { + // make type optional + var newType = type; + if (type == RegexNFATypeEnum.SINGLE) + { + newType = RegexNFATypeEnum.ONE_OPTIONAL; + } + else if (type == RegexNFATypeEnum.ONE_TO_MANY) + { + newType = RegexNFATypeEnum.ZERO_TO_MANY; + } + else if (type == RegexNFATypeEnum.ONE_TO_MANY_RELUCTANT) + { + newType = RegexNFATypeEnum.ZERO_TO_MANY_RELUCTANT; + } + var copy = copier.Copy(node, newType); + repeated.Add(copy); + } + return repeated; + } + + private static RowRegexExprNode CheckedCopy(RowRegexExprNode inner) + { + try + { + return (RowRegexExprNode)SerializableObjectCopier.Copy(inner); + } + catch (Exception e) + { + throw new EPException("Failed to repeat nested match-recognize: " + e.Message, e); + } + } + + private static void ValidateRange(int value, int min, int maxValue) + { + if (value < min || value > maxValue) + { + var message = "Invalid pattern quantifier value " + value + ", expecting a minimum of " + min; + if (maxValue != int.MaxValue) + { + message += " and maximum of " + maxValue; + } + throw new ExprValidationException(message); + } + } + + private static void ValidateExpression(ExprNode repeat) + { + var expression = "pattern quantifier '" + repeat.ToExpressionStringMinPrecedenceSafe() + "'"; + ExprNodeUtility.ValidatePlainExpression(ExprNodeOrigin.MATCHRECOGPATTERN, expression, repeat); + if (!repeat.IsConstantResult) + { + throw new ExprValidationException(expression + " must return a constant value"); + } + if (repeat.ExprEvaluator.ReturnType.GetBoxedType() != typeof(int?)) + { + throw new ExprValidationException(expression + " must return an integer-type value"); + } + } + + private interface RowRegexExprNodeCopier + { + RowRegexExprNode Copy(RowRegexExprNode nodeToCopy, RegexNFATypeEnum newType); + } + + private class RowRegexExprNodeCopierAtom : RowRegexExprNodeCopier + { + public RowRegexExprNode Copy(RowRegexExprNode nodeToCopy, RegexNFATypeEnum newType) + { + var atom = (RowRegexExprNodeAtom)nodeToCopy; + return new RowRegexExprNodeAtom(atom.Tag, newType, null); + } + } + + private class RowRegexExprNodeCopierNested : RowRegexExprNodeCopier + { + public RowRegexExprNode Copy(RowRegexExprNode nodeToCopy, RegexNFATypeEnum newType) + { + var nested = (RowRegexExprNodeNested)nodeToCopy; + var nestedCopy = new RowRegexExprNodeNested(newType, null); + foreach (var inner in nested.ChildNodes) + { + var innerCopy = CheckedCopy(inner); + nestedCopy.AddChildNode(innerCopy); + } + return nestedCopy; + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/rowregex/RowRegexExprNode.cs b/NEsper.Core/NEsper.Core/rowregex/RowRegexExprNode.cs new file mode 100755 index 000000000..f620faa8c --- /dev/null +++ b/NEsper.Core/NEsper.Core/rowregex/RowRegexExprNode.cs @@ -0,0 +1,116 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.compat.logging; +using com.espertech.esper.util; + +namespace com.espertech.esper.rowregex +{ + /// + /// Base node for + /// + [Serializable] + public abstract class RowRegexExprNode : MetaDefItem + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + public abstract RowRegexExprNodePrecedenceEnum Precedence { get; } + public abstract void ToPrecedenceFreeEPL(TextWriter writer); + + /// + /// Constructor creates a list of child nodes. + /// + protected RowRegexExprNode() + { + ChildNodes = new List(); + } + + public virtual void ToEPL(TextWriter writer, RowRegexExprNodePrecedenceEnum parentPrecedence) + { + if (Precedence.GetLevel() < parentPrecedence.GetLevel()) + { + writer.Write("("); + ToPrecedenceFreeEPL(writer); + writer.Write(")"); + } + else + { + ToPrecedenceFreeEPL(writer); + } + } + + /// + /// Adds a child node. + /// + /// is the child evaluation tree node to add + public void AddChildNode(RowRegexExprNode childNode) + { + ChildNodes.Add(childNode); + } + + /// + /// Returns list of child nodes. + /// + /// + /// list of child nodes + /// + public IList ChildNodes { get; private set; } + + /// + /// Recursively print out all nodes. + /// + /// is printed out for naming the printed info + public void DumpDebug(String prefix) + { + if (Log.IsDebugEnabled) + { + Log.Debug(".DumpDebug {0}{1}", prefix, this); + } + foreach (RowRegexExprNode node in ChildNodes) + { + node.DumpDebug(prefix + " "); + } + } + + public virtual void Accept(RowRegexExprNodeVisitor visitor) + { + AcceptChildnodes(visitor, null, 0); + } + + public virtual void AcceptChildnodes(RowRegexExprNodeVisitor visitor, RowRegexExprNode parent, int level) + { + visitor.Visit(this, parent, level); + foreach (RowRegexExprNode childNode in ChildNodes) + { + childNode.AcceptChildnodes(visitor, this, level + 1); + } + } + + public virtual void ReplaceChildNode(RowRegexExprNode nodeToReplace, IList replacementNodes) + { + var newChildNodes = new List(ChildNodes.Count - 1 + replacementNodes.Count); + foreach (RowRegexExprNode node in ChildNodes) + { + if (node != nodeToReplace) + { + newChildNodes.Add(node); + } + else + { + newChildNodes.AddRange(replacementNodes); + } + } + + ChildNodes = newChildNodes; + } + } +} diff --git a/NEsper.Core/NEsper.Core/rowregex/RowRegexExprNodeAlteration.cs b/NEsper.Core/NEsper.Core/rowregex/RowRegexExprNodeAlteration.cs new file mode 100755 index 000000000..35e449214 --- /dev/null +++ b/NEsper.Core/NEsper.Core/rowregex/RowRegexExprNodeAlteration.cs @@ -0,0 +1,43 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.rowregex +{ + /// + /// Or-condition in a regex expression tree. + /// + [Serializable] + public class RowRegexExprNodeAlteration : RowRegexExprNode + { + /// + /// Ctor. + /// + public RowRegexExprNodeAlteration() + { + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + String delimiter = ""; + foreach (RowRegexExprNode node in this.ChildNodes) + { + writer.Write(delimiter); + node.ToEPL(writer, Precedence); + delimiter = "|"; + } + } + + public override RowRegexExprNodePrecedenceEnum Precedence + { + get { return RowRegexExprNodePrecedenceEnum.ALTERNATION; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/rowregex/RowRegexExprNodeAtom.cs b/NEsper.Core/NEsper.Core/rowregex/RowRegexExprNodeAtom.cs new file mode 100755 index 000000000..c36870ff7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/rowregex/RowRegexExprNodeAtom.cs @@ -0,0 +1,76 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +using com.espertech.esper.compat.logging; + +namespace com.espertech.esper.rowregex +{ + /// + /// Atom in a regex expression tree. + /// + [Serializable] + public class RowRegexExprNodeAtom : RowRegexExprNode + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// Ctor. + /// + /// variable name + /// multiplicity and greedy indicator + /// optional repeating information + public RowRegexExprNodeAtom(String tag, RegexNFATypeEnum type, RowRegexExprRepeatDesc optionalRepeat) + { + Tag = tag; + NFAType = type; + OptionalRepeat = optionalRepeat; + } + + /// + /// Returns the variable name. + /// + /// + /// variable + /// + public string Tag { get; private set; } + + /// + /// Returns multiplicity and greedy indicator. + /// + /// + /// type + /// + public RegexNFATypeEnum NFAType { get; private set; } + + /// + /// Gets the optional repeat. + /// + /// + /// The optional repeat. + /// + public RowRegexExprRepeatDesc OptionalRepeat { get; private set; } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + writer.Write(Tag); + writer.Write(NFAType.OptionalPostfix()); + if (OptionalRepeat != null) + { + OptionalRepeat.ToExpressionString(writer); + } + } + + public override RowRegexExprNodePrecedenceEnum Precedence + { + get { return RowRegexExprNodePrecedenceEnum.UNARY; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/rowregex/RowRegexExprNodeConcatenation.cs b/NEsper.Core/NEsper.Core/rowregex/RowRegexExprNodeConcatenation.cs new file mode 100755 index 000000000..c566fb0e8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/rowregex/RowRegexExprNodeConcatenation.cs @@ -0,0 +1,47 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; +using System.Text; +using com.espertech.esper.compat.logging; + +namespace com.espertech.esper.rowregex +{ + /// + /// Concatenation of atoms in a regular expression tree. + /// + [Serializable] + public class RowRegexExprNodeConcatenation : RowRegexExprNode + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// Ctor. + /// + public RowRegexExprNodeConcatenation() + { + } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + String delimiter = ""; + foreach (RowRegexExprNode node in ChildNodes) + { + writer.Write(delimiter); + node.ToEPL(writer, Precedence); + delimiter = " "; + } + } + + public override RowRegexExprNodePrecedenceEnum Precedence + { + get { return RowRegexExprNodePrecedenceEnum.CONCATENATION; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/rowregex/RowRegexExprNodeNested.cs b/NEsper.Core/NEsper.Core/rowregex/RowRegexExprNodeNested.cs new file mode 100755 index 000000000..dfb80da48 --- /dev/null +++ b/NEsper.Core/NEsper.Core/rowregex/RowRegexExprNodeNested.cs @@ -0,0 +1,43 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.rowregex +{ + /// Nested () regular expression in a regex expression tree. + [Serializable] + public class RowRegexExprNodeNested : RowRegexExprNode + { + public RowRegexExprNodeNested(RegexNFATypeEnum type, RowRegexExprRepeatDesc optionalRepeat) + { + NFAType = type; + OptionalRepeat = optionalRepeat; + } + + /// + /// Returns multiplicity and greedy. + /// + /// type + public RegexNFATypeEnum NFAType { get; private set; } + + public RowRegexExprRepeatDesc OptionalRepeat { get; private set; } + + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + ChildNodes[0].ToEPL(writer, Precedence); + writer.Write(NFAType.OptionalPostfix()); + } + + public override RowRegexExprNodePrecedenceEnum Precedence + { + get { return RowRegexExprNodePrecedenceEnum.GROUPING; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/rowregex/RowRegexExprNodePermute.cs b/NEsper.Core/NEsper.Core/rowregex/RowRegexExprNodePermute.cs new file mode 100755 index 000000000..d5653550c --- /dev/null +++ b/NEsper.Core/NEsper.Core/rowregex/RowRegexExprNodePermute.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.rowregex +{ + /// + /// Permute () regular expression in a regex expression tree. + /// + [Serializable] + public class RowRegexExprNodePermute : RowRegexExprNode + { + public override void ToPrecedenceFreeEPL(TextWriter writer) + { + string delimiter = ""; + writer.Write("match_recognize_permute("); + foreach (RowRegexExprNode node in this.ChildNodes) { + writer.Write(delimiter); + node.ToEPL(writer, Precedence); + delimiter = ", "; + } + writer.Write(")"); + } + + public override RowRegexExprNodePrecedenceEnum Precedence + { + get { return RowRegexExprNodePrecedenceEnum.UNARY; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/rowregex/RowRegexExprNodePrecedenceEnum.cs b/NEsper.Core/NEsper.Core/rowregex/RowRegexExprNodePrecedenceEnum.cs new file mode 100755 index 000000000..e442c337c --- /dev/null +++ b/NEsper.Core/NEsper.Core/rowregex/RowRegexExprNodePrecedenceEnum.cs @@ -0,0 +1,66 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.rowregex +{ + /// + /// Precendence levels for expressions. + /// + public enum RowRegexExprNodePrecedenceEnum + { + /// + /// Precedence. + /// + UNARY = 4, + /// + /// Precedence. + /// + GROUPING = 3, + /// + /// Precedence. + /// + CONCATENATION = 2, + /// + /// Precedence. + /// + ALTERNATION = 1, + + /// + /// Precedence. + /// + MINIMUM = int.MinValue + }; + + public static class RowRegexExprNodePrecedenceEnumExtensions + { + /// + /// Level. + /// + /// level + public static int GetLevel(this RowRegexExprNodePrecedenceEnum value) + { + switch (value) + { + case RowRegexExprNodePrecedenceEnum.UNARY: + return 4; + case RowRegexExprNodePrecedenceEnum.GROUPING: + return 3; + case RowRegexExprNodePrecedenceEnum.CONCATENATION: + return 2; + case RowRegexExprNodePrecedenceEnum.ALTERNATION: + return 1; + case RowRegexExprNodePrecedenceEnum.MINIMUM: + return int.MinValue; + } + + throw new ArgumentException(); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/rowregex/RowRegexExprNodeVisitor.cs b/NEsper.Core/NEsper.Core/rowregex/RowRegexExprNodeVisitor.cs new file mode 100755 index 000000000..2d6c580fd --- /dev/null +++ b/NEsper.Core/NEsper.Core/rowregex/RowRegexExprNodeVisitor.cs @@ -0,0 +1,15 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.rowregex +{ + public interface RowRegexExprNodeVisitor + { + void Visit(RowRegexExprNode node, RowRegexExprNode optionalParent, int level); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/rowregex/RowRegexExprNodeVisitorRepeat.cs b/NEsper.Core/NEsper.Core/rowregex/RowRegexExprNodeVisitorRepeat.cs new file mode 100755 index 000000000..0f0901458 --- /dev/null +++ b/NEsper.Core/NEsper.Core/rowregex/RowRegexExprNodeVisitorRepeat.cs @@ -0,0 +1,115 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.rowregex +{ + public class RowRegexExprNodeVisitorRepeat : RowRegexExprNodeVisitor + { + private IList> _atoms; + private IList _nesteds; + private IList _permutes; + + public void Visit(RowRegexExprNode node, RowRegexExprNode optionalParent, int level) + { + if (node is RowRegexExprNodeAtom) { + var atom = (RowRegexExprNodeAtom) node; + if (atom.OptionalRepeat != null) { + if (_atoms == null) { + _atoms = new List>(); + } + _atoms.Add(new Pair(atom, optionalParent)); + } + } + if (node is RowRegexExprNodeNested) { + var nested = (RowRegexExprNodeNested) node; + if (nested.OptionalRepeat != null) { + if (_nesteds == null) { + _nesteds = new List(); + } + _nesteds.Add(new RowRegexNestedDesc(nested, optionalParent, level)); + } + } + if (node is RowRegexExprNodePermute) { + var permute = (RowRegexExprNodePermute) node; + if (_permutes == null) { + _permutes = new List(); + } + _permutes.Add(new RowRegexPermuteDesc(permute, optionalParent, level)); + } + } + + public IList> Atoms + { + get + { + if (_atoms == null) + { + return Collections.GetEmptyList>(); + } + return _atoms; + } + } + + public IList Nesteds + { + get + { + if (_nesteds == null) + { + return Collections.GetEmptyList(); + } + return _nesteds; + } + } + + public IList Permutes + { + get + { + if (_permutes == null) + { + return Collections.GetEmptyList(); + } + return _permutes; + } + } + + public struct RowRegexPermuteDesc + { + public RowRegexPermuteDesc(RowRegexExprNodePermute permute, RowRegexExprNode optionalParent, int level) + { + Permute = permute; + OptionalParent = optionalParent; + Level = level; + } + + public RowRegexExprNodePermute Permute; + public RowRegexExprNode OptionalParent; + public int Level; + } + + public struct RowRegexNestedDesc + { + public RowRegexNestedDesc(RowRegexExprNodeNested nested, RowRegexExprNode optionalParent, int level) + { + Nested = nested; + OptionalParent = optionalParent; + Level = level; + } + + public RowRegexExprNodeNested Nested; + public RowRegexExprNode OptionalParent; + public int Level; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/rowregex/RowRegexExprRepeatDesc.cs b/NEsper.Core/NEsper.Core/rowregex/RowRegexExprRepeatDesc.cs new file mode 100755 index 000000000..8a7775920 --- /dev/null +++ b/NEsper.Core/NEsper.Core/rowregex/RowRegexExprRepeatDesc.cs @@ -0,0 +1,55 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.util; + +namespace com.espertech.esper.rowregex +{ + [Serializable] + public class RowRegexExprRepeatDesc : MetaDefItem + { + public RowRegexExprRepeatDesc(ExprNode lower, ExprNode upper, ExprNode single) + { + Lower = lower; + Upper = upper; + Single = single; + } + + public ExprNode Lower { get; private set; } + + public ExprNode Upper { get; private set; } + + public ExprNode Single { get; private set; } + + public void ToExpressionString(TextWriter writer) + { + writer.Write("{"); + if (Single != null) + { + writer.Write(ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(Single)); + } + else + { + if (Lower != null) + { + writer.Write(ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(Lower)); + } + writer.Write(","); + if (Upper != null) + { + writer.Write(ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(Upper)); + } + } + writer.Write("}"); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/schedule/ScheduleAdjustmentCallback.cs b/NEsper.Core/NEsper.Core/schedule/ScheduleAdjustmentCallback.cs new file mode 100755 index 000000000..e39ac251b --- /dev/null +++ b/NEsper.Core/NEsper.Core/schedule/ScheduleAdjustmentCallback.cs @@ -0,0 +1,18 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.schedule +{ + /// Callback for views that adjust an expiration date on event objects. + public interface ScheduleAdjustmentCallback + { + /// Adjust expiration date. + /// to adjust + void Adjust(long delta); + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/schedule/ScheduleAdjustmentService.cs b/NEsper.Core/NEsper.Core/schedule/ScheduleAdjustmentService.cs new file mode 100755 index 000000000..83d2a6fe3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/schedule/ScheduleAdjustmentService.cs @@ -0,0 +1,55 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +namespace com.espertech.esper.schedule +{ + /// Service for holding expiration dates to adjust. + public class ScheduleAdjustmentService + { + private ICollection _callbacks; + + /// Add a callback + /// to add + public void AddCallback(ScheduleAdjustmentCallback callback) + { + lock (this) + { + if (_callbacks == null) + { + _callbacks = new HashSet(); + } + _callbacks.Add(callback); + } + } + + /// Make callbacks to adjust expiration dates. + /// to adjust for + public void Adjust(long delta) + { + if (_callbacks == null) + { + return; + } + foreach (ScheduleAdjustmentCallback callback in _callbacks) + { + callback.Adjust(delta); + } + } + + public void RemoveCallback(ScheduleAdjustmentCallback callback) + { + if (_callbacks == null) + { + return; + } + _callbacks.Remove(callback); + } + } +} diff --git a/NEsper.Core/NEsper.Core/schedule/ScheduleBucket.cs b/NEsper.Core/NEsper.Core/schedule/ScheduleBucket.cs new file mode 100755 index 000000000..15f3f02e7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/schedule/ScheduleBucket.cs @@ -0,0 +1,56 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; + +namespace com.espertech.esper.schedule +{ + /// + /// This class acts as a buckets for sorting schedule service callbacks that are scheduled to occur at the same + /// time. Each buckets constists of slots that callbacks are assigned to. + /// + /// At the time of timer evaluation, callbacks that become triggerable are ordered using the bucket + /// as the first-level order, and slot as the second-level order. + /// + /// + /// Each statement at statement creation time allocates a buckets, and each timer within the + /// statement allocates a slot. Thus statements that depend on other statements (such as for insert-into), + /// and timers within their statement (such as time window or output rate limit timers) behave + /// deterministically. + /// + /// + public class ScheduleBucket { + private readonly int bucketNum; + private int lastSlot; + + /// + /// Ctor. + /// + /// is a simple integer number for this bucket by which buckets can be sorted + public ScheduleBucket(int bucketNum) { + this.bucketNum = bucketNum; + lastSlot = 0; + } + + public static long ToLong(int bucket, int slot) { + return ((long) bucket << 32) | slot & 0xFFFFFFFFL; + } + + public long AllocateSlot() { + return ToLong(bucketNum, lastSlot++); + } + + public long AllocateSlot(int slotNumber) { + return ToLong(bucketNum, slotNumber); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/schedule/ScheduleCalendar.cs b/NEsper.Core/NEsper.Core/schedule/ScheduleCalendar.cs new file mode 100755 index 000000000..ab6ccde1f --- /dev/null +++ b/NEsper.Core/NEsper.Core/schedule/ScheduleCalendar.cs @@ -0,0 +1,44 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +namespace com.espertech.esper.schedule +{ + /// + /// Calendar class for use in scheduling, specifically for use in computing the next invocation time. + /// + + public class ScheduleCalendar + { + public int Milliseconds { get; set; } + + public int Second { get; set; } + + public int Minute { get; set; } + + public int Hour { get; set; } + + public int DayOfMonth { get; set; } + + public int Month { get; set; } + + internal ScheduleCalendar(int milliseconds, int second, int minute, int hour, int dayOfMonth, int month) + { + Milliseconds = milliseconds; + Second = second; + Minute = minute; + Hour = hour; + DayOfMonth = dayOfMonth; + Month = month; + } + + internal ScheduleCalendar() + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/schedule/ScheduleComputeHelper.cs b/NEsper.Core/NEsper.Core/schedule/ScheduleComputeHelper.cs new file mode 100755 index 000000000..f248e3d91 --- /dev/null +++ b/NEsper.Core/NEsper.Core/schedule/ScheduleComputeHelper.cs @@ -0,0 +1,760 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Reflection; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.datetime.calop; +using com.espertech.esper.epl.expression.time; +using com.espertech.esper.util; +using com.espertech.esper.type; + +namespace com.espertech.esper.schedule +{ + /// + /// For a crontab-like schedule, this class computes the next occurance given a Start time and a specification of + /// what the schedule looks like. + /// The resolution at which this works is at the second level. The next occurance + /// is always at least 1 second ahead. + /// The class implements an algorithm that Starts at the highest precision (seconds) and + /// continues to the lowest precicion (month). For each precision level the + /// algorithm looks at the list of valid values and finds a value for each that is equal to or greater then + /// the valid values supplied. If no equal or + /// greater value was supplied, it will reset all higher precision elements to its minimum value. + /// + + public sealed class ScheduleComputeHelper + { + /// + /// Computes the next lowest date in milliseconds based on a specification and the + /// from-time passed in. + /// + /// defines the schedule + /// defines the start time + /// The time zone. + /// The time abacus. + /// + /// a long date tick value for the next schedule occurance matching the spec + /// + + public static long ComputeNextOccurance( + ScheduleSpec spec, + long afterTimeInMillis, + TimeZoneInfo timeZone, + TimeAbacus timeAbacus) + { + if (ExecutionPathDebugLog.IsEnabled && Log.IsDebugEnabled) + { + Log.Debug( + ".computeNextOccurance Computing next occurance," + + " afterTimeInTicks=" + afterTimeInMillis.TimeFromMillis(timeZone) + + " as long=" + afterTimeInMillis + + " spec=" + spec); + } + + // Add the minimum resolution to the Start time to ensure we don't get the same exact time + if (spec.UnitValues.ContainsKey(ScheduleUnit.SECONDS)) + { + afterTimeInMillis += timeAbacus.GetOneSecond(); + } + else + { + afterTimeInMillis += 60 * timeAbacus.GetOneSecond(); + } + + return Compute(spec, afterTimeInMillis, timeZone, timeAbacus); + } + + /// + /// Computes the next lowest date in milliseconds based on a specification and the + /// from-time passed in and returns the delta from the current time. + /// + /// The schedule. + /// defines the start time. + /// The time zone. + /// The time abacus. + /// + /// a long millisecond value representing the delta between current time and the next schedule occurance matching the spec + /// + public static long ComputeDeltaNextOccurance( + ScheduleSpec spec, + long afterTimeInMillis, + TimeZoneInfo timeZone, + TimeAbacus timeAbacus) + { + return ComputeNextOccurance(spec, afterTimeInMillis, timeZone, timeAbacus) - afterTimeInMillis; + } + + private static long Compute( + ScheduleSpec spec, + long afterTimeInMillis, + TimeZoneInfo timeZone, + TimeAbacus timeAbacus) + { + long remainderMicros = -1; + + while (true) + { + DateTimeEx after; + + if (spec.OptionalTimeZone != null) + { + try + { + timeZone = TimeZoneHelper.GetTimeZoneInfo(spec.OptionalTimeZone); + after = DateTimeEx.GetInstance(timeZone); + } + catch (TimeZoneNotFoundException) + { + // this behavior ensures we are consistent with Java, but IMO, it's bad behavior... + // basically, if the timezone is not found, we default to UTC. + timeZone = TimeZoneInfo.Utc; + after = DateTimeEx.GetInstance(timeZone); + } + } + else + { + after = DateTimeEx.GetInstance(timeZone); + } + + var remainder = timeAbacus.CalendarSet(afterTimeInMillis, after); + if (remainderMicros == -1) + { + remainderMicros = remainder; + } + + var result = new ScheduleCalendar { Milliseconds = after.Millisecond }; + + ICollection minutesSet = spec.UnitValues.Get(ScheduleUnit.MINUTES); + ICollection hoursSet = spec.UnitValues.Get(ScheduleUnit.HOURS); + ICollection monthsSet = spec.UnitValues.Get(ScheduleUnit.MONTHS); + ICollection secondsSet = null; + + bool isSecondsSpecified = false; + + if (spec.UnitValues.ContainsKey(ScheduleUnit.SECONDS)) + { + isSecondsSpecified = true; + secondsSet = spec.UnitValues.Get(ScheduleUnit.SECONDS); + } + + if (isSecondsSpecified) + { + result.Second = NextValue(secondsSet, after.Second); + if (result.Second == -1) + { + result.Second = NextValue(secondsSet, 0); + after.AddMinutes(1); + } + } + + result.Minute = NextValue(minutesSet, after.Minute); + if (result.Minute != after.Minute) + { + result.Second = NextValue(secondsSet, 0); + } + if (result.Minute == -1) + { + result.Minute = NextValue(minutesSet, 0); + after.AddHours(1); + } + + result.Hour = NextValue(hoursSet, after.Hour); + if (result.Hour != after.Hour) + { + result.Second = NextValue(secondsSet, 0); + result.Minute = NextValue(minutesSet, 0); + } + if (result.Hour == -1) + { + result.Hour = NextValue(hoursSet, 0); + after.AddDays(1, DateTimeMathStyle.Java); + } + + // This call may change second, minute and/or hour parameters + // They may be reset to minimum values if the day rolled + result.DayOfMonth = DetermineDayOfMonth(spec, after, result); + + bool dayMatchRealDate = false; + while (!dayMatchRealDate) + { + if (CheckDayValidInMonth(timeZone, result.DayOfMonth, after.Month, after.Year)) + { + dayMatchRealDate = true; + } + else + { + after.AddMonths(1, DateTimeMathStyle.Java); + } + } + + int currentMonth = after.Month; + result.Month = NextValue(monthsSet, currentMonth); + if (result.Month != currentMonth) + { + result.Second = NextValue(secondsSet, 0); + result.Minute = NextValue(minutesSet, 0); + result.Hour = NextValue(hoursSet, 0); + result.DayOfMonth = DetermineDayOfMonth(spec, after, result); + } + if (result.Month == -1) + { + result.Month = NextValue(monthsSet, 0); + after.AddYears(1); + } + + // Perform a last valid date check, if failing, try to compute a new date based on this altered after date + int year = after.Year; + if (!CheckDayValidInMonth(timeZone, result.DayOfMonth, result.Month, year)) + { + afterTimeInMillis = timeAbacus.CalendarGet(after, remainder); + continue; + } + + return GetTime(result, after.Year, spec.OptionalTimeZone, timeZone, timeAbacus, remainder); + } + } + + /// + /// Determine the next valid day of month based on the given specification of valid days in month and + /// valid days in week. If both days in week and days in month are supplied, the days are OR-ed. + /// + /// + /// + /// + /// + + private static int DetermineDayOfMonth(ScheduleSpec spec, DateTimeEx after, ScheduleCalendar result) + { + ICollection daysOfMonthSet = spec.UnitValues.Get(ScheduleUnit.DAYS_OF_MONTH); + ICollection daysOfWeekSet = spec.UnitValues.Get(ScheduleUnit.DAYS_OF_WEEK); + ICollection secondsSet = spec.UnitValues.Get(ScheduleUnit.SECONDS); + ICollection minutesSet = spec.UnitValues.Get(ScheduleUnit.MINUTES); + ICollection hoursSet = spec.UnitValues.Get(ScheduleUnit.HOURS); + + int dayOfMonth; + + // If days of week is a wildcard, just go by days of month + if (spec.OptionalDayOfMonthOperator != null || spec.OptionalDayOfWeekOperator != null) + { + var isWeek = false; + var op = spec.OptionalDayOfMonthOperator; + if (spec.OptionalDayOfMonthOperator == null) + { + op = spec.OptionalDayOfWeekOperator; + isWeek = true; + } + + // may return the current day or a future day in the same month, + // and may advance the "after" date to the next month + int currentYYMMDD = GetTimeYYYYMMDD(after); + IncreaseAfterDayOfMonthSpecialOp(op.Operator, op.Day, op.Month, isWeek, after); + int rolledYYMMDD = GetTimeYYYYMMDD(after); + + // if rolled then reset time portion + if (rolledYYMMDD > currentYYMMDD) + { + result.Second = (NextValue(secondsSet, 0)); + result.Minute = (NextValue(minutesSet, 0)); + result.Hour = (NextValue(hoursSet, 0)); + return after.GetFieldValue(DateTimeFieldEnum.DAY_OF_MONTH); + } + // rolling backwards is not allowed + else if (rolledYYMMDD < currentYYMMDD) + { + throw new IllegalStateException("Failed to evaluate special date op, rolled date less then current date"); + } + else + { + var work = new DateTimeEx(after); + work.SetFieldValue(DateTimeFieldEnum.SECOND, result.Second); + work.SetFieldValue(DateTimeFieldEnum.MINUTE, result.Minute); + work.SetFieldValue(DateTimeFieldEnum.HOUR_OF_DAY, result.Hour); + if (work <= after) + { // new date is not after current date, so bump + after.AddUsingField(DateTimeFieldEnum.DAY_OF_MONTH, 1); + result.Second = NextValue(secondsSet, 0); + result.Minute = NextValue(minutesSet, 0); + result.Hour = NextValue(hoursSet, 0); + IncreaseAfterDayOfMonthSpecialOp(op.Operator, op.Day, op.Month, isWeek, after); + } + return after.GetFieldValue(DateTimeFieldEnum.DAY_OF_MONTH); + } + } + else if (daysOfWeekSet == null) + { + dayOfMonth = NextValue(daysOfMonthSet, after.Day); + if (dayOfMonth != after.Day) + { + result.Second = NextValue(secondsSet, 0); + result.Minute = NextValue(minutesSet, 0); + result.Hour = NextValue(hoursSet, 0); + } + if (dayOfMonth == -1) + { + dayOfMonth = NextValue(daysOfMonthSet, 0); + after.AddMonths(1, DateTimeMathStyle.Java); + } + } + // If days of weeks is not a wildcard and days of month is a wildcard, go by days of week only + else if (daysOfMonthSet == null) + { + // Loop to find the next day of month that works for the specified day of week values + while (true) + { + dayOfMonth = after.Day; + int dayOfWeek = (int)after.DayOfWeek; + + // TODO + // + // Check the DayOfWeek logic in this section. The former code reads something + // like the following: + // + // Calendar.Get(after, SupportClass.CalendarManager.DAY_OF_WEEK) - 1; + // + // Java calendars are one based which means that subtracting one makes them + // zero-based. CLR DateTimes are zero-based so there should be no need to + // tweak the dates to make this work. + + // If the day matches neither the day of month nor the day of week + if (!daysOfWeekSet.Contains(dayOfWeek)) + { + result.Second = NextValue(secondsSet, 0); + result.Minute = NextValue(minutesSet, 0); + result.Hour = NextValue(hoursSet, 0); + after.AddDays(1, DateTimeMathStyle.Java); + } + else + { + break; + } + } + } + // Both days of weeks and days of month are not a wildcard + else + { + // Loop to find the next day of month that works for either day of month OR day of week + while (true) + { + dayOfMonth = after.Day; + int dayOfWeek = (int)after.DayOfWeek; + + // TODO + // + // See my discussion above about day of week conversion + + // If the day matches neither the day of month nor the day of week + if ((!daysOfWeekSet.Contains(dayOfWeek)) && (!daysOfMonthSet.Contains(dayOfMonth))) + { + result.Second = NextValue(secondsSet, 0); + result.Minute = NextValue(minutesSet, 0); + result.Hour = NextValue(hoursSet, 0); + after.AddDays(1, DateTimeMathStyle.Java); + } + else + { + break; + } + } + } + + return dayOfMonth; + } + + private static long GetTime( + ScheduleCalendar result, + int year, + string optionalTimeZone, + TimeZoneInfo timeZone, + TimeAbacus timeAbacus, + long remainder) + { + // Here again we have a case of 1-based vs. 0-based indexing. + // Java months are 0-based. + // CLR months are 1-based. + + if (optionalTimeZone != null) + { + try + { + timeZone = TimeZoneHelper.GetTimeZoneInfo(optionalTimeZone); + } + catch (TimeZoneNotFoundException) + { + // this behavior ensures we are consistent with Java, but IMO, it's bad behavior... + // basically, if the timezone is not found, we default to UTC. + timeZone = TimeZoneInfo.Utc; + } + } + + if (timeZone == null) + { + timeZone = TimeZoneInfo.Local; + } + + var baseDateTime = new DateTime( + year, + result.Month, + result.DayOfMonth, + result.Hour, + result.Minute, + result.Second, + result.Milliseconds, + new GregorianCalendar() + ); + + var baseDateTimeOffset = timeZone.GetUtcOffset(baseDateTime); + + var dateTime = new DateTimeOffset( + year, + result.Month, + result.DayOfMonth, + result.Hour, + result.Minute, + result.Second, + result.Milliseconds, + new GregorianCalendar(), + baseDateTimeOffset); + + var dateTimeEx = new DateTimeEx(dateTime, timeZone); + + return timeAbacus.CalendarGet(dateTimeEx, remainder); + } + + /// + /// Check if this is a valid date. + /// + /// + /// + /// + /// + + private static bool CheckDayValidInMonth(TimeZoneInfo timeZone, int day, int month, int year) + { + try + { + DateTimeOffsetHelper.CreateDateTime(year, month, day, 0, 0, 0, 0, timeZone); + return true; + } + catch (ArgumentException) + { + return false; + } + } + + /// + /// Determine if in the supplied valueSet there is a value after the given Start value. + /// Return -1 to indicate that there is no value after the given StartValue. + /// If the valueSet passed is null it is treated as a wildcard and the same StartValue is returned + /// + /// + /// + /// + + private static int NextValue(ICollection valueSet, int startValue) + { + if (valueSet == null) + { + return startValue; + } + + if (valueSet.Contains(startValue)) + { + return startValue; + } + + var minValue = startValue + 1; + var tail = valueSet.Where(value => value >= minValue).GetEnumerator(); + return tail.MoveNext() ? tail.Current : -1; + } + + private static int GetTimeYYYYMMDD(DateTimeEx calendar) + { + return 10000 * calendar.Year + 100 * (calendar.Month + 1) + calendar.Day; + } + + private static void IncreaseAfterDayOfMonthSpecialOp(CronOperatorEnum @operator, int? day, int? month, bool week, DateTimeEx after) + { + DateChecker checker; + if (@operator == CronOperatorEnum.LASTDAY) + { + if (!week) + { + checker = new DateCheckerLastDayOfMonth(day, month); + } + else + { + if (day == null) + { + checker = new DateCheckerLastDayOfWeek(month); + } + else + { + checker = new DateCheckerLastSpecificDayWeek(day.Value, month); + } + } + } + else if (@operator == CronOperatorEnum.LASTWEEKDAY) + { + checker = new DateCheckerLastWeekday(day, month); + } + else + { + checker = new DateCheckerMonthWeekday(day, month); + } + + int dayCount = 0; + while (!checker.Fits(after)) + { + after.AddUsingField(DateTimeFieldEnum.DAY_OF_MONTH, 1); + dayCount++; + if (dayCount > 10000) + { + throw new ArgumentException("Invalid crontab expression: failed to find match day"); + } + } + } + + internal interface DateChecker + { + bool Fits(DateTimeEx dateTime); + } + + internal class DateCheckerLastSpecificDayWeek : DateChecker + { + private readonly DayOfWeek _dayCode; + private readonly int? _month; + + internal DateCheckerLastSpecificDayWeek(int day, int? month) + { + if (day < 0 || day > 7) + { + throw new ArgumentException("Last xx day of the month has to be a day of week (0-7)"); + } + _dayCode = (DayOfWeek)day; + _month = month; + } + + public bool Fits(DateTimeEx dateTime) + { + if (_dayCode != dateTime.DayOfWeek) + { + return false; + } + if (_month != null && _month != dateTime.Month) + { + return false; + } + // e.g. 31=Sun,30=Sat,29=Fri,28=Thu,27=Wed,26=Tue,25=Mon + // e.g. 31-7 = 24 + return dateTime.Day > dateTime.GetActualMaximum(DateTimeFieldEnum.DAY_OF_MONTH) - 7; + } + } + + internal class DateCheckerLastDayOfMonth : DateChecker + { + private readonly DayOfWeek? _dayCode; + private readonly int? _month; + + internal DateCheckerLastDayOfMonth(int? day, int? month) + { + if (day != null) + { + if (day < 0 || day > 7) + { + throw new ArgumentException("Last xx day of the month has to be a day of week (0-7)"); + } + _dayCode = (DayOfWeek)day; + } + else + { + _dayCode = null; + } + _month = month; + } + + public bool Fits(DateTimeEx dateTime) + { + if (_dayCode != null && _dayCode != dateTime.DayOfWeek) + { + return false; + } + if (_month != null && _month != dateTime.Month) + { + return false; + } + return (dateTime.Day == dateTime.GetActualMaximum(DateTimeFieldEnum.DAY_OF_MONTH)); + } + } + + internal class DateCheckerLastDayOfWeek : DateChecker + { + private readonly int? _month; + + internal DateCheckerLastDayOfWeek(int? month) + { + _month = month; + } + + public bool Fits(DateTimeEx dateTime) + { + if (_month != null && _month != dateTime.Month) + { + return false; + } + return (dateTime.DayOfWeek == DayOfWeek.Saturday); + } + } + + internal class DateCheckerLastWeekday : DateChecker + { + private readonly DayOfWeek? _dayCode; + private readonly int? _month; + + internal DateCheckerLastWeekday(int? day, int? month) + { + if (day != null) + { + if (day < 0 || day > 7) + { + throw new ArgumentException("Last xx day of the month has to be a day of week (0-7)"); + } + _dayCode = (DayOfWeek)day; + } + else + { + _dayCode = null; + } + _month = month; + } + + public bool Fits(DateTimeEx dateTime) + { + if (_dayCode != null && _dayCode != dateTime.DayOfWeek) + { + return false; + } + if (_month != null && _month != dateTime.Month) + { + return false; + } + if (!IsWeekday(dateTime)) + { + return false; + } + int day = dateTime.Day; + int max = dateTime.GetActualMaximum(DateTimeFieldEnum.DAY_OF_MONTH); + if (day == max) + { + return true; + } + var dayOfWeek = dateTime.DayOfWeek; + return day >= max - 2 && dayOfWeek == DayOfWeek.Friday; + } + } + + internal class DateCheckerMonthWeekday : DateChecker + { + private readonly int? _day; + private readonly int? _month; + + internal DateCheckerMonthWeekday(int? day, int? month) + { + if (day != null) + { + if (day < 1 || day > 31) + { + throw new ArgumentException("xx day of the month has to be a in range (1-31)"); + } + } + _day = day; + _month = month; + } + + public bool Fits(DateTimeEx dateTime) + { + if (_month != null && _month != dateTime.Month) + { + return false; + } + if (!IsWeekday(dateTime)) + { + return false; + } + if (_day == null) + { + return true; + } + + var work = new DateTimeEx(dateTime); + var target = ComputeNearestWeekdayDay(_day.Value, work); + return dateTime.Day == target; + } + + private static int ComputeNearestWeekdayDay(int day, DateTimeEx work) + { + int max = work.GetActualMaximum(DateTimeFieldEnum.DAY_OF_MONTH); + if (day <= max) + { + work = work.SetFieldValue(DateTimeFieldEnum.DAY_OF_MONTH, day); + } + else + { + work = work.SetFieldValue(DateTimeFieldEnum.DAY_OF_MONTH, max); + } + + if (IsWeekday(work)) + { + return work.Day; + } + if (work.DayOfWeek == DayOfWeek.Saturday) + { + if (work.Day > 1) + { + work = work.AddUsingField(DateTimeFieldEnum.DAY_OF_MONTH, -1); + return work.Day; + } + else + { + work = work.AddUsingField(DateTimeFieldEnum.DAY_OF_MONTH, 2); + return work.Day; + } + } + else + { + // handle Sunday + if (max == work.Day) + { + work = work.AddUsingField(DateTimeFieldEnum.DAY_OF_MONTH, -2); + return work.Day; + } + else + { + work = work.AddUsingField(DateTimeFieldEnum.DAY_OF_MONTH, 1); + return work.Day; + } + } + } + } + + private static bool IsWeekday(DateTimeEx dateTime) + { + var dayOfWeek = dateTime.DayOfWeek; + return !(dayOfWeek < DayOfWeek.Monday || dayOfWeek > DayOfWeek.Friday); + } + + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + } +} diff --git a/NEsper.Core/NEsper.Core/schedule/ScheduleHandle.cs b/NEsper.Core/NEsper.Core/schedule/ScheduleHandle.cs new file mode 100755 index 000000000..e4882386d --- /dev/null +++ b/NEsper.Core/NEsper.Core/schedule/ScheduleHandle.cs @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.schedule +{ + /// + /// Marker interface for use with . Implementations + /// serve as a schedule trigger values when the schedule is reached to trigger or return + /// the handle. + /// + public interface ScheduleHandle + { + /// Returns the statement id. + /// statement id + int StatementId { get; } + + /// Returns the agent instance id. + /// agent instance id + int AgentInstanceId { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/schedule/ScheduleHandleCallback.cs b/NEsper.Core/NEsper.Core/schedule/ScheduleHandleCallback.cs new file mode 100755 index 000000000..fb425b998 --- /dev/null +++ b/NEsper.Core/NEsper.Core/schedule/ScheduleHandleCallback.cs @@ -0,0 +1,49 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.core.service; + +namespace com.espertech.esper.schedule +{ + /// Interface for scheduled callbacks. + public interface ScheduleHandleCallback + { + /// Callback that is invoked as indicated by a schedule added to the scheduling service. + /// + void ScheduledTrigger(EngineLevelExtensionServicesContext engineLevelExtensionServicesContext); + } + + public class ProxyScheduleHandleCallback : ScheduleHandleCallback + { + public Action ProcScheduledTrigger { get; set; } + + public ProxyScheduleHandleCallback() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The dg. + public ProxyScheduleHandleCallback(Action dg) + { + ProcScheduledTrigger = dg; + } + + /// + /// Callback that is invoked as indicated by a schedule added to the scheduling service. + /// + /// + public void ScheduledTrigger(EngineLevelExtensionServicesContext engineLevelExtensionServicesContext) + { + ProcScheduledTrigger(engineLevelExtensionServicesContext); + } + } +} diff --git a/NEsper.Core/NEsper.Core/schedule/ScheduleHandleCallbackProxy.cs b/NEsper.Core/NEsper.Core/schedule/ScheduleHandleCallbackProxy.cs new file mode 100755 index 000000000..2464010ec --- /dev/null +++ b/NEsper.Core/NEsper.Core/schedule/ScheduleHandleCallbackProxy.cs @@ -0,0 +1,76 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; +using System.Linq; +using System.Reflection; + +using Castle.DynamicProxy; + +using com.espertech.esper.client.annotation; +using com.espertech.esper.util; + +namespace com.espertech.esper.schedule +{ + public class ScheduleHandleCallbackProxy : IInterceptor + { + private static readonly MethodInfo ScheduledTriggerMethod = typeof(ScheduleHandleCallback).GetMethod("ScheduledTrigger"); + + private readonly String _engineURI; + private readonly String _statementName; + private readonly ScheduleHandleCallback _scheduleHandleCallback; + + public static ScheduleHandleCallback NewInstance(String engineURI, String statementName, ScheduleHandleCallback scheduleHandleCallback) + { + var generator = new ProxyGenerator(); + var interfaces = scheduleHandleCallback + .GetType() + .GetInterfaces() + .Where(ii => ii != typeof(IProxyTargetAccessor)) + .ToArray(); + + return (ScheduleHandleCallback) generator.CreateInterfaceProxyWithoutTarget( + typeof(ScheduleHandleCallback), interfaces, new ScheduleHandleCallbackProxy(engineURI, statementName, scheduleHandleCallback)); + } + + /// + /// Initializes a new instance of the class. + /// + /// The engine URI. + /// Name of the statement. + /// The schedule handle callback. + public ScheduleHandleCallbackProxy(String engineURI, String statementName, ScheduleHandleCallback scheduleHandleCallback) + { + _engineURI = engineURI; + _statementName = statementName; + _scheduleHandleCallback = scheduleHandleCallback; + } + + /// + /// Intercepts the specified invocation. + /// + /// The invocation. + public void Intercept(IInvocation invocation) + { + if (invocation.Method == ScheduledTriggerMethod) + { + if (AuditPath.IsAuditEnabled) + { + var message = new StringWriter(); + message.Write("trigger handle "); + TypeHelper.WriteInstance(message, _scheduleHandleCallback, true); + AuditPath.AuditLog(_engineURI, _statementName, AuditEnum.SCHEDULE, message.ToString()); + } + } + + invocation.ReturnValue = + invocation.Method.Invoke(_scheduleHandleCallback, invocation.Arguments); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/schedule/ScheduleParameterException.cs b/NEsper.Core/NEsper.Core/schedule/ScheduleParameterException.cs new file mode 100755 index 000000000..afb273b21 --- /dev/null +++ b/NEsper.Core/NEsper.Core/schedule/ScheduleParameterException.cs @@ -0,0 +1,52 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Runtime.Serialization; + +namespace com.espertech.esper.schedule +{ + /// + /// This exception is thrown to indicate a problem with schedule parameters. + /// + public class ScheduleParameterException : Exception + { + /// + /// Initializes a new instance of the class. + /// + /// The that holds the serialized object data about the exception being thrown. + /// The that contains contextual information about the source or destination. + /// The parameter is null. + /// The class name is null or is zero (0). + protected ScheduleParameterException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + + /// Constructor. + /// is the error message + public ScheduleParameterException(String message) + : base(message) + { + } + + /// Constructor for an inner exception and message. + /// is the error message + /// is the inner exception + public ScheduleParameterException(String message, Exception innerException) + : base(message, innerException) + { + } + + /// Constructor. + /// is the inner exception + public ScheduleParameterException(Exception innerException) + : base(String.Empty, innerException) + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/schedule/ScheduleServiceException.cs b/NEsper.Core/NEsper.Core/schedule/ScheduleServiceException.cs new file mode 100755 index 000000000..407bb2c47 --- /dev/null +++ b/NEsper.Core/NEsper.Core/schedule/ScheduleServiceException.cs @@ -0,0 +1,47 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; + +namespace com.espertech.esper.schedule +{ + /// + /// This exception is thrown to indicate a problem with scheduling. + /// + + [Serializable] + public class ScheduleServiceException : Exception + { + /// Constructor. + /// is the error message + /// + public ScheduleServiceException(String message) + : base(message) + { + } + + /// Constructor for an inner exception and message. + /// is the error message + /// + /// is the inner exception + /// + public ScheduleServiceException(String message, System.Exception cause) + : base(message, cause) + { + } + + /// Constructor. + /// is the inner exception + /// + public ScheduleServiceException(System.Exception cause) + : base(String.Empty, cause) + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/schedule/ScheduleSet.cs b/NEsper.Core/NEsper.Core/schedule/ScheduleSet.cs new file mode 100755 index 000000000..883fa67c4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/schedule/ScheduleSet.cs @@ -0,0 +1,24 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + + +namespace com.espertech.esper.schedule +{ + /// Set of schedules. + public class ScheduleSet : List + { + /// Ctor. + /// schedules + public ScheduleSet(List list) + : base(list) + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/schedule/ScheduleSetEntry.cs b/NEsper.Core/NEsper.Core/schedule/ScheduleSetEntry.cs new file mode 100755 index 000000000..465fc478c --- /dev/null +++ b/NEsper.Core/NEsper.Core/schedule/ScheduleSetEntry.cs @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.schedule +{ + /// Record for a schedule item. + public class ScheduleSetEntry + { + /// Ctor. + /// of schedule + /// slot + /// handle to use + public ScheduleSetEntry( + long time, + long slot, + ScheduleHandle handle) + { + Time = time; + Slot = slot; + Handle = handle; + } + + /// Gets or sets the time. + /// time + public long Time { get; set; } + + /// Returns schedule slot. + /// slot + public long Slot { get; private set; } + + /// Returns the schedule handle. + /// handle + public ScheduleHandle Handle { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/schedule/ScheduleSpec.cs b/NEsper.Core/NEsper.Core/schedule/ScheduleSpec.cs new file mode 100755 index 000000000..80c67ad74 --- /dev/null +++ b/NEsper.Core/NEsper.Core/schedule/ScheduleSpec.cs @@ -0,0 +1,273 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.type; +using com.espertech.esper.util; + +namespace com.espertech.esper.schedule +{ + /// + /// Holds a schedule specification which consists of a set of integer values or a null + /// value for each schedule unit to indicate a wildcard. There is always an element in + /// the specification for each unit minutes, hours, day of month, month, and day of week. + /// There is optionally an element in the specification for the unit seconds. + /// + [Serializable] + public sealed class ScheduleSpec : MetaDefItem + { + // Per unit hold the set of valid integer values, or null if wildcarded. + // The seconds unit is optional. + private readonly IDictionary> _unitValues; + private String _optionalTimeZone; + private readonly CronParameter _optionalDayOfMonthOperator; + private readonly CronParameter _optionalDayOfWeekOperator; + + /// + /// Constructor - validates that all mandatory schedule. + /// + /// are the values for each minute, hour, day, month etc. + /// The optional time zone. + /// The optional day of month operator. + /// The optional day of week operator. + /// ArgumentException - if validation of value set per unit fails + public ScheduleSpec(IDictionary> unitValues, String optionalTimeZone, CronParameter optionalDayOfMonthOperator, CronParameter optionalDayOfWeekOperator) + { + Validate(unitValues); + + // Reduce to wildcards any unit's values set, if possible + Compress(unitValues); + + _unitValues = unitValues; + _optionalTimeZone = optionalTimeZone; + _optionalDayOfMonthOperator = optionalDayOfMonthOperator; + _optionalDayOfWeekOperator = optionalDayOfWeekOperator; + } + + /// + /// Constructor - for unit testing, initialize to all wildcards but leave seconds empty. + /// + public ScheduleSpec() + { + _unitValues = new Dictionary>(); + _unitValues.Put(ScheduleUnit.MINUTES, null); + _unitValues.Put(ScheduleUnit.HOURS, null); + _unitValues.Put(ScheduleUnit.DAYS_OF_MONTH, null); + _unitValues.Put(ScheduleUnit.MONTHS, null); + _unitValues.Put(ScheduleUnit.DAYS_OF_WEEK, null); + _optionalTimeZone = null; + } + + public CronParameter OptionalDayOfMonthOperator + { + get { return _optionalDayOfMonthOperator; } + } + + public CronParameter OptionalDayOfWeekOperator + { + get { return _optionalDayOfWeekOperator; } + } + + /// + /// Return map of ordered set of valid schedule values for minute, hour, day, month etc. units + /// + /// map of 5 or 6 entries each with a set of integers + public IDictionary> UnitValues + { + get { return _unitValues; } + } + + public string OptionalTimeZone + { + get { return _optionalTimeZone; } + set { _optionalTimeZone = value; } + } + + /// For unit testing, add a single value, changing wildcards to value sets. + /// to add + /// to add + public void AddValue(ScheduleUnit element, int value) + { + var set = _unitValues.Get(element); + if (set == null) + { + set = new SortedSet(); + _unitValues.Put(element, set); + } + set.Add(value); + } + + public override String ToString() + { + var buffer = new StringBuilder(); + foreach (ScheduleUnit element in ScheduleUnit.Values) + { + if (!_unitValues.ContainsKey(element)) + { + continue; + } + + ICollection valueSet = _unitValues.Get(element); + buffer.Append(element + "={"); + if (valueSet == null) + { + buffer.Append("null"); + } + else + { + String delimiter = ""; + foreach (int i in valueSet) + { + buffer.Append(delimiter + i); + delimiter = ","; + } + } + buffer.Append("} "); + } + return buffer.ToString(); + } + + public override bool Equals(Object otherObject) + { + if (otherObject == this) + { + return true; + } + + if (otherObject == null) + { + return false; + } + + if (GetType() != otherObject.GetType()) + { + return false; + } + + var other = (ScheduleSpec)otherObject; + if (_unitValues.Count != other._unitValues.Count) + { + return false; + } + + foreach (var entry in _unitValues) + { + ICollection mySet = entry.Value; + ICollection otherSet = other._unitValues.Get(entry.Key); + + if ((otherSet == null) && (mySet != null)) + { + return false; + } + if ((otherSet != null) && (mySet == null)) + { + return false; + } + if ((otherSet == null) && (mySet == null)) + { + continue; + } + if (mySet.Count != otherSet.Count) + { + return false; + } + + // Commpare value by value + if (mySet.Any(i => !(otherSet.Contains(i)))) + { + return false; + } + } + + return true; + } + + public override int GetHashCode() + { + int hashCode = 0; + foreach (var entry in _unitValues) + { + if (entry.Value != null) + { + hashCode *= 31; + hashCode ^= entry.Value.First(); + } + } + return hashCode; + } + + /// + /// Function to reduce value sets for unit that cover the whole range down to a wildcard. + /// I.e. reduce 0,1,2,3,4,5,6 for week value to 'null' indicating the wildcard. + /// + /// is the set of valid values per unit + internal static void Compress(IDictionary> unitValues) + { + var termList = new List(); + + foreach (var entry in unitValues) + { + int elementValueSetSize = entry.Key.Max() - entry.Key.Min() + 1; + if (entry.Value != null) + { + if (entry.Value.Count == elementValueSetSize) + { + termList.Add(entry.Key); + } + } + } + + foreach (var term in termList) + { + unitValues[term] = null; + } + } + + /// Validate units and their value sets. + /// is the set of valid values per unit + internal static void Validate(IDictionary> unitValues) + { + if ((!unitValues.ContainsKey(ScheduleUnit.MONTHS)) || + (!unitValues.ContainsKey(ScheduleUnit.DAYS_OF_WEEK)) || + (!unitValues.ContainsKey(ScheduleUnit.HOURS)) || + (!unitValues.ContainsKey(ScheduleUnit.MINUTES)) || + (!unitValues.ContainsKey(ScheduleUnit.DAYS_OF_MONTH))) + { + throw new ArgumentException("Incomplete information for schedule specification, only the following keys are supplied=" + unitValues.Keys.Render()); + } + + foreach (ScheduleUnit unit in ScheduleUnit.Values) + { + if ((unit == ScheduleUnit.SECONDS) && (!unitValues.ContainsKey(unit))) // Seconds are optional + { + continue; + } + + if (unitValues.Get(unit) == null) // Wildcard - no validation for unit + { + continue; + } + + ICollection values = unitValues.Get(unit); + foreach (int? value in values) + { + if ((value < unit.Min()) || (value > unit.Max())) + { + throw new ArgumentException("Invalid value found for schedule unit, value of " + + value + " is not valid for unit " + unit); + } + } + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/schedule/ScheduleSpecUtil.cs b/NEsper.Core/NEsper.Core/schedule/ScheduleSpecUtil.cs new file mode 100755 index 000000000..10aa6a598 --- /dev/null +++ b/NEsper.Core/NEsper.Core/schedule/ScheduleSpecUtil.cs @@ -0,0 +1,163 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.type; + +namespace com.espertech.esper.schedule +{ + /// + /// Utility for computing from a set of parameter objects a schedule specification carry a + /// crontab-like schedule definition. + /// + public class ScheduleSpecUtil + { + /// + /// Compute from parameters a crontab schedule. + /// + /// parameters + /// if the parameters are invalid + /// crontab schedule + public static ScheduleSpec ComputeValues(Object[] args) + { + if (args.Length <= 4 || args.Length >= 8) + { + throw new ScheduleParameterException(GetExpressionCountException(args.Length)); + } + var unitMap = new Dictionary>(); + var minutes = args[0]; + var hours = args[1]; + var daysOfMonth = args[2]; + var months = args[3]; + var daysOfWeek = args[4]; + unitMap.Put(ScheduleUnit.MINUTES, ComputeValues(minutes, ScheduleUnit.MINUTES)); + unitMap.Put(ScheduleUnit.HOURS, ComputeValues(hours, ScheduleUnit.HOURS)); + var resultMonths = ComputeValues(months, ScheduleUnit.MONTHS); + if (daysOfWeek is CronParameter && daysOfMonth is CronParameter) + { + throw new ScheduleParameterException( + "Invalid combination between days of week and days of month fields for timer:at"); + } + if (resultMonths != null && resultMonths.Count == 1 && (resultMonths.First().IsInt())) + { + // If other arguments are cronParameters, use it for later computations + CronParameter parameter = null; + if (daysOfMonth is CronParameter) + { + parameter = (CronParameter) daysOfMonth; + } + else if (daysOfWeek is CronParameter) + { + parameter = (CronParameter) daysOfWeek; + } + if (parameter != null) + { + parameter.Month = resultMonths.First(); + } + } + var resultDaysOfWeek = ComputeValues(daysOfWeek, ScheduleUnit.DAYS_OF_WEEK); + var resultDaysOfMonth = ComputeValues(daysOfMonth, ScheduleUnit.DAYS_OF_MONTH); + if (resultDaysOfWeek != null && resultDaysOfWeek.Count == 1 && (resultDaysOfWeek.First().IsInt())) + { + // The result is in the form "last xx of the month + // Days of week is replaced by a wildcard and days of month is updated with + // the computation of "last xx day of month". + // In this case "days of month" parameter has to be a wildcard. + if (resultDaysOfWeek.First() > 6) + { + if (resultDaysOfMonth != null) + { + throw new ScheduleParameterException( + "Invalid combination between days of week and days of month fields for timer:at"); + } + resultDaysOfMonth = resultDaysOfWeek; + resultDaysOfWeek = null; + } + } + if (resultDaysOfMonth != null && resultDaysOfMonth.Count == 1 && (resultDaysOfMonth.First().IsInt())) + { + if (resultDaysOfWeek != null) + { + throw new ScheduleParameterException( + "Invalid combination between days of week and days of month fields for timer:at"); + } + } + unitMap.Put(ScheduleUnit.DAYS_OF_WEEK, resultDaysOfWeek); + unitMap.Put(ScheduleUnit.DAYS_OF_MONTH, resultDaysOfMonth); + unitMap.Put(ScheduleUnit.MONTHS, resultMonths); + if (args.Length > 5) + { + unitMap.Put(ScheduleUnit.SECONDS, ComputeValues(args[5], ScheduleUnit.SECONDS)); + } + string timezone = null; + if (args.Length > 6) + { + if (!(args[6] is WildcardParameter)) + { + if (!(args[6] is string)) + { + throw new ScheduleParameterException( + "Invalid timezone parameter '" + args[6] + "' for timer:at, expected a string-type value"); + } + timezone = (string) args[6]; + } + } + var optionalDayOfMonthOp = GetOptionalSpecialOp(daysOfMonth); + var optionalDayOfWeekOp = GetOptionalSpecialOp(daysOfWeek); + return new ScheduleSpec(unitMap, timezone, optionalDayOfMonthOp, optionalDayOfWeekOp); + } + + public static string GetExpressionCountException(int length) + { + return "Invalid number of crontab parameters, expecting between 5 and 7 parameters, received " + length; + } + + private static CronParameter GetOptionalSpecialOp(Object unitParameter) + { + if (!(unitParameter is CronParameter)) + { + return null; + } + return (CronParameter) unitParameter; + } + + private static ICollection ComputeValues(Object unitParameter, ScheduleUnit unit) + { + ICollection result; + if (unitParameter is int) + { + result = new SortedSet(); + result.Add((int) unitParameter); + return result; + } + + // cron parameters not handled as number sets + if (unitParameter is CronParameter) + { + return null; + } + + var numberSet = (NumberSetParameter) unitParameter; + if (numberSet.IsWildcard(unit.Min(), unit.Max())) + { + return null; + } + + result = numberSet.GetValuesInRange(unit.Min(), unit.Max()); + var resultSorted = new SortedSet(); + resultSorted.AddAll(result); + + return resultSorted; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/schedule/ScheduleVisit.cs b/NEsper.Core/NEsper.Core/schedule/ScheduleVisit.cs new file mode 100755 index 000000000..3ac7ed747 --- /dev/null +++ b/NEsper.Core/NEsper.Core/schedule/ScheduleVisit.cs @@ -0,0 +1,17 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.schedule +{ + public struct ScheduleVisit + { + public int AgentInstanceId; + public long Timestamp; + public int StatementId; + } +} diff --git a/NEsper.Core/NEsper.Core/schedule/ScheduleVisitor.cs b/NEsper.Core/NEsper.Core/schedule/ScheduleVisitor.cs new file mode 100755 index 000000000..b4cb3f393 --- /dev/null +++ b/NEsper.Core/NEsper.Core/schedule/ScheduleVisitor.cs @@ -0,0 +1,16 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.schedule +{ + public delegate void ScheduleVisitor(ScheduleVisit visit); + + //public interface ScheduleVisitor { + // void Visit(ScheduleVisit visit); + //} +} diff --git a/NEsper.Core/NEsper.Core/schedule/SchedulingMgmtService.cs b/NEsper.Core/NEsper.Core/schedule/SchedulingMgmtService.cs new file mode 100755 index 000000000..52b8e7c1a --- /dev/null +++ b/NEsper.Core/NEsper.Core/schedule/SchedulingMgmtService.cs @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.schedule +{ + /// + /// Interface for a service that allocated schedule buckets for statements, for + /// controlling timer callback orders. + /// + public interface SchedulingMgmtService : IDisposable + { + /// + /// Returns a bucket from which slots can be allocated for ordering concurrent + /// callbacks. + /// + /// + /// bucket + /// + ScheduleBucket AllocateBucket(); + } +} diff --git a/NEsper.Core/NEsper.Core/schedule/SchedulingMgmtServiceImpl.cs b/NEsper.Core/NEsper.Core/schedule/SchedulingMgmtServiceImpl.cs new file mode 100755 index 000000000..1762049a3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/schedule/SchedulingMgmtServiceImpl.cs @@ -0,0 +1,46 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Reflection; +using System.Threading; +using com.espertech.esper.compat.logging; + +namespace com.espertech.esper.schedule +{ + /// + /// Implements the schedule service by simply keeping a sorted set of long + /// millisecond values and a set of handles for each. + /// + /// Synchronized since statement creation and event evaluation by multiple (event + /// send) threads can lead to callbacks added/removed asynchronously. + /// + public sealed class SchedulingMgmtServiceImpl : SchedulingMgmtService + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// Current bucket number - for use in ordering handles by bucket + /// + private int _curBucketNum; + + #region SchedulingMgmtService Members + + public void Dispose() + { + Log.Debug("Destroying scheduling management service"); + } + + public ScheduleBucket AllocateBucket() + { + int bucket = Interlocked.Increment(ref _curBucketNum); + return new ScheduleBucket(bucket); + } + + #endregion + } +} diff --git a/NEsper.Core/NEsper.Core/schedule/SchedulingService.cs b/NEsper.Core/NEsper.Core/schedule/SchedulingService.cs new file mode 100755 index 000000000..e7df1b676 --- /dev/null +++ b/NEsper.Core/NEsper.Core/schedule/SchedulingService.cs @@ -0,0 +1,72 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.schedule +{ + /// + /// Interface for a service that allows to add and remove handles (typically storing callbacks) + /// for a certain time which are returned when + /// the evaluate method is invoked and the current time is on or after the handle's registered time. + /// It is the expectation that the setTime method is called + /// with same or ascending values for each subsequent call. Handles with are triggered are automatically removed + /// by implementations. + /// + + public interface SchedulingService : TimeProvider, IDisposable + { + /// + /// Add a callback for after the given milliseconds from the current time. + /// If the same callback (equals) was already added before, the method will not add a new + /// callback or change the existing callback to a new time, but throw an exception. + /// + /// number of millisec to get a callback + /// to add + /// allows ordering of concurrent callbacks + /// ScheduleServiceException thrown if the add operation did not complete + void Add(long afterTime, ScheduleHandle handle, long slot); + + /// + /// Remove a callback. + /// If the callback to be removed was not found an exception is thrown. + /// + /// to remove + /// for which the callback was added + /// ScheduleServiceException thrown if the callback was not located + void Remove(ScheduleHandle handle, long scheduleSlot); + + /// + /// Evaluate the current time and perform any callbacks. + /// + /// The handles. + void Evaluate(ICollection handles); + + /// Returns time handle count. + /// count + int TimeHandleCount { get; } + + /// Returns furthest in the future handle. + /// future handle + long? FurthestTimeHandle { get; } + + /// Returns count of handles. + /// count + int ScheduleHandleCount { get; } + + /// + /// Returns true if the handle has been scheduled already. + /// + /// The handle. + /// + /// true if the specified handle is scheduled; otherwise, false. + /// + bool IsScheduled(ScheduleHandle handle); + } +} diff --git a/NEsper.Core/NEsper.Core/schedule/SchedulingServiceImpl.cs b/NEsper.Core/NEsper.Core/schedule/SchedulingServiceImpl.cs new file mode 100755 index 000000000..0693979cf --- /dev/null +++ b/NEsper.Core/NEsper.Core/schedule/SchedulingServiceImpl.cs @@ -0,0 +1,311 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.compat.threading; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.timer; + +namespace com.espertech.esper.schedule +{ + /// + /// Implements the schedule service by simply keeping a sorted set of long + /// millisecond values and a set of handles for each. + /// + /// Synchronized since statement creation and event evaluation by multiple (event + /// send) threads can lead to callbacks added/removed asynchronously. + /// + public sealed class SchedulingServiceImpl : SchedulingServiceSPI + { + private readonly ILockable _uLock; + + // Map of time and handle + private readonly IDictionary> _timeHandleMap; + + // Map of handle and handle list for faster removal + private readonly IDictionary> _handleSetMap; + + // Current time - used for evaluation as well as for adding new handles + private long _currentTime; + + /// + /// Constructor. + /// + /// time source provider + public SchedulingServiceImpl(TimeSourceService timeSourceService) + { + _uLock = LockManager.CreateLock(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + _timeHandleMap = new SortedList>(); + _handleSetMap = new Dictionary>(); + // initialize time to just before now as there is a check for duplicate external time events + _currentTime = timeSourceService.GetTimeMillis() - 1; + } + + public void Dispose() + { + Log.Debug("Destroying scheduling service"); + _handleSetMap.Clear(); + _timeHandleMap.Clear(); + } + + public long Time + { + get { return _currentTime; } + set + { + using (_uLock.Acquire()) + { + _currentTime = value; + } + } + } + + public void Add(long afterTime, ScheduleHandle handle, long slot) + { + using (Instrument.With( + i => i.QScheduleAdd(_currentTime, afterTime, handle, slot), + i => i.AScheduleAdd())) + { + using (_uLock.Acquire()) + { + if (_handleSetMap.ContainsKey(handle)) + { + Remove(handle, slot); + } + + long triggerOnTime = _currentTime + afterTime; + + AddTrigger(slot, handle, triggerOnTime); + } + } + } + + public void Remove(ScheduleHandle handle, long scheduleSlot) + { + using (Instrument.With( + i => i.QScheduleRemove(handle, scheduleSlot), + i => i.AScheduleRemove())) + { + using (_uLock.Acquire()) + { + var handleSet = _handleSetMap.Get(handle); + if (handleSet == null) + { + // If it already has been removed then that's fine; + // Such could be the case when 2 timers fireStatementStopped at the same time, and one stops the other + return; + } + handleSet.Remove(scheduleSlot); + _handleSetMap.Remove(handle); + } + } + } + + public void Evaluate(ICollection handles) + { + using (Instrument.With( + i => i.QScheduleEval(_currentTime), + i => i.AScheduleEval(handles))) + { + using (_uLock.Acquire()) + { + // Get the values on or before the current time - to get those that are exactly on the + // current time we just add one to the current time for getting the head map + var current = _currentTime + 1; + if (_timeHandleMap.Count == 0) + return; + + var headMap = _timeHandleMap.Where(keyValuePair => keyValuePair.Key < current); + + // First determine all triggers to shoot + var removeKeys = new List(); + foreach (var entry in headMap) + { + var key = entry.Key; + var value = entry.Value; + + removeKeys.Add(key); + foreach (ScheduleHandle handle in value.Values) + { + handles.Add(handle); + } + } + + // Next remove all handles + foreach (var entry in headMap) + { + foreach (var handle in entry.Value.Values) + { + _handleSetMap.Remove(handle); + } + } + + // Remove all triggered msec values + int removeKeyCount = removeKeys.Count; + if (removeKeyCount != 0) + { + for (int ii = 0; ii < removeKeyCount; ii++) + { + long key = removeKeys[ii]; + _timeHandleMap.Remove(key); + } + } + } + } + } + + public ScheduleSet Take(ICollection statementIds) + { + var list = new List(); + var currentTime = Time; + foreach (var schedule in _timeHandleMap) + { + foreach (var entry in schedule.Value) + { + if (statementIds.Contains(entry.Value.StatementId)) + { + var relative = schedule.Key - currentTime; + list.Add(new ScheduleSetEntry(relative, entry.Key, entry.Value)); + } + } + } + + list.ForEach(entry => Remove(entry.Handle, entry.Slot)); + + return new ScheduleSet(list); + } + + public void Apply(ScheduleSet scheduleSet) + { + var list = scheduleSet; + var listCount = list.Count; + + for (int ii = 0; ii < listCount; ii++) + { + var entry = list[ii]; + Add(entry.Time, entry.Handle, entry.Slot); + } + } + + public void Init() + { + // no action required + } + + private void AddTrigger(long slot, ScheduleHandle handle, long triggerTime) + { + var handleSet = _timeHandleMap.Get(triggerTime); + if (handleSet == null) + { + //handleSet = new NullableDictionary( + // new OrderedDictionary()); + handleSet = new OrderedDictionary(); + _timeHandleMap.Put(triggerTime, handleSet); + } + + handleSet.Put(slot, handle); + _handleSetMap.Put(handle, handleSet); + } + + public int TimeHandleCount + { + get { return _timeHandleMap.Count; } + } + + public string FurthestTimeHandleDate + { + get + { + var handle = FurthestTimeHandle; + if (handle != null) + { + return handle.Value.TimeFromMillis(null).ToString(); + } + return null; + } + } + + public string NearestTimeHandleDate + { + get + { + var handle = NearestTimeHandle; + if (handle != null) + { + return handle.Value.TimeFromMillis(null).ToString(); + } + return null; + } + } + + public long? FurthestTimeHandle + { + get + { + if (_timeHandleMap.IsNotEmpty()) + { + return _timeHandleMap.Keys.Last(); + } + return null; + } + } + + public int ScheduleHandleCount + { + get { return _handleSetMap.Count; } + } + + public bool IsScheduled(ScheduleHandle handle) + { + return _handleSetMap.ContainsKey(handle); + } + + /// + /// Returns the nearest time handle. + /// + /// The nearest time handle. + public long? NearestTimeHandle + { + get + { + foreach (var entry in _timeHandleMap) + { + if (!entry.Value.IsEmpty()) + { + return entry.Key; + } + } + + return null; + } + } + + public void VisitSchedules(ScheduleVisitor visitor) + { + var visit = new ScheduleVisit(); + foreach (var entry in _timeHandleMap) + { + visit.Timestamp = entry.Key; + foreach (var inner in entry.Value) + { + visit.StatementId = inner.Value.StatementId; + visit.AgentInstanceId = inner.Value.AgentInstanceId; + visitor.Invoke(visit); + } + } + } + + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + } +} diff --git a/NEsper.Core/NEsper.Core/schedule/SchedulingServiceProvider.cs b/NEsper.Core/NEsper.Core/schedule/SchedulingServiceProvider.cs new file mode 100755 index 000000000..201d19eaa --- /dev/null +++ b/NEsper.Core/NEsper.Core/schedule/SchedulingServiceProvider.cs @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.timer; + +namespace com.espertech.esper.schedule +{ + /// + /// Static factory for implementations of the SchedulingService interface. + /// + + public sealed class SchedulingServiceProvider + { + /// + /// Creates an implementation of the SchedulingService interface. + /// + /// time source provider + /// implementation + public static SchedulingServiceSPI NewService(TimeSourceService timeSourceService) + { + return new SchedulingServiceImpl(timeSourceService); + } + } +} diff --git a/NEsper.Core/NEsper.Core/schedule/SchedulingServiceSPI.cs b/NEsper.Core/NEsper.Core/schedule/SchedulingServiceSPI.cs new file mode 100755 index 000000000..f2cb3913c --- /dev/null +++ b/NEsper.Core/NEsper.Core/schedule/SchedulingServiceSPI.cs @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +namespace com.espertech.esper.schedule +{ + /// + /// Service provider interface for scheduling service. + /// + public interface SchedulingServiceSPI : SchedulingService + { + /// + /// Take a statement's schedules out of the currently active set of schedules. + /// + /// statements to take out + /// schedules + ScheduleSet Take(ICollection statementId); + + /// + /// Apply the set of schedules. + /// + /// to apply + void Apply(ScheduleSet scheduleSet); + + long? NearestTimeHandle { get; } + + void VisitSchedules(ScheduleVisitor visitor); + + /// + /// Initialization is optional and provides a chance to preload things after statements are available. + /// + void Init(); + } +} diff --git a/NEsper.Core/NEsper.Core/schedule/TimeProvider.cs b/NEsper.Core/NEsper.Core/schedule/TimeProvider.cs new file mode 100755 index 000000000..5f520291a --- /dev/null +++ b/NEsper.Core/NEsper.Core/schedule/TimeProvider.cs @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.schedule +{ + /// + /// Provider of internal system time. + /// + /// Internal system time is controlled either by a timer function or by external time events. + /// + /// + public interface TimeProvider + { + /// Returns the current engine time. + /// time that has last been set + long Time { get; set; } + } + + /// + /// A proxy implementation of the time provider + /// + public class ProxyTimeProvider : TimeProvider + { + public Func Get { get; set; } + public Action Set { get; set; } + + public long Time + { + get { return Get(); } + set { Set(value); } + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/script/ScriptArgs.cs b/NEsper.Core/NEsper.Core/script/ScriptArgs.cs new file mode 100755 index 000000000..7de8b91ca --- /dev/null +++ b/NEsper.Core/NEsper.Core/script/ScriptArgs.cs @@ -0,0 +1,33 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.script +{ + public class ScriptArgs + { + /// + /// Gets or sets the bindings. + /// + /// The bindings. + public IDictionary Bindings { get; set; } + + /// + /// Gets the parameter. + /// + /// Name of the parameter. + /// + public object GetParameter(string parameterName) + { + return Bindings.Get(parameterName); + } + } +} diff --git a/NEsper.Core/NEsper.Core/script/ScriptBase.cs b/NEsper.Core/NEsper.Core/script/ScriptBase.cs new file mode 100755 index 000000000..adb4d1808 --- /dev/null +++ b/NEsper.Core/NEsper.Core/script/ScriptBase.cs @@ -0,0 +1,14 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.script +{ + public class ScriptBase + { + } +} diff --git a/NEsper.Core/NEsper.Core/script/ScriptingEngine.cs b/NEsper.Core/NEsper.Core/script/ScriptingEngine.cs new file mode 100755 index 000000000..faaf5ccfa --- /dev/null +++ b/NEsper.Core/NEsper.Core/script/ScriptingEngine.cs @@ -0,0 +1,46 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.script +{ + /// + /// Scripting implementations must implement the ScriptingEngine interface. + /// + + public interface ScriptingEngine + { + /// + /// Gets the language associated with this engine. e.g. Javascript + /// + /// The language. + string Language { get; } + + /// + /// Gets the language prefix to use with this engine. e.g. js + /// + /// The language prefix. + string LanguagePrefix { get; } + + /// + /// Compiles the code. + /// + /// The expression script. + /// + Func Compile(ExpressionScriptProvided expressionScript); + + /// + /// Verifies the specified script. + /// + /// The script. + void Verify(ExpressionScriptProvided script); + } +} diff --git a/NEsper.Core/NEsper.Core/script/ScriptingEngineException.cs b/NEsper.Core/NEsper.Core/script/ScriptingEngineException.cs new file mode 100755 index 000000000..565d4b3d7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/script/ScriptingEngineException.cs @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Runtime.Serialization; + +namespace com.espertech.esper.script +{ + public class ScriptingEngineException : Exception + { + /// + /// Initializes a new instance of the class. + /// + public ScriptingEngineException() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The message. + public ScriptingEngineException(string message) : base(message) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The message. + /// The inner exception. + public ScriptingEngineException(string message, Exception innerException) : base(message, innerException) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The that holds the serialized object data about the exception being thrown. + /// The that contains contextual information about the source or destination. + /// The parameter is null. + /// The class name is null or is zero (0). + protected ScriptingEngineException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/script/ScriptingService.cs b/NEsper.Core/NEsper.Core/script/ScriptingService.cs new file mode 100755 index 000000000..9dd171c92 --- /dev/null +++ b/NEsper.Core/NEsper.Core/script/ScriptingService.cs @@ -0,0 +1,35 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.script +{ + /// + /// ScriptingService is a wrapper around the scripting engine and it's abstractions. + /// + public interface ScriptingService : IDisposable + { + /// + /// Compiles the specified script given the specified dialect. + /// + /// The dialect. + /// The script. + /// + Func Compile(String dialect, ExpressionScriptProvided script); + + /// + /// Verifies the script given the specified dialect. + /// + /// The dialect. + /// The script. + void VerifyScript(string dialect, ExpressionScriptProvided script); + } +} diff --git a/NEsper.Core/NEsper.Core/script/ScriptingServiceImpl.cs b/NEsper.Core/NEsper.Core/script/ScriptingServiceImpl.cs new file mode 100755 index 000000000..64e19ecf2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/script/ScriptingServiceImpl.cs @@ -0,0 +1,133 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; +using com.espertech.esper.util; + +namespace com.espertech.esper.script +{ + public class ScriptingServiceImpl : ScriptingService + { + /// + /// Scripting engines indexed by language prefix + /// + private readonly IDictionary _scriptingEngines = + new Dictionary(); + + /// + /// + /// + public void DiscoverEngines() + { + DiscoverEngines(type => true); + } + + /// + /// Attempts to discover engine instances in the AppDomain. + /// + public void DiscoverEngines(Predicate isEngine) + { + foreach(var assembly in AppDomain.CurrentDomain.GetAssemblies()) + { + DiscoverEngines(assembly, isEngine); + } + } + + /// + /// Discovers the engines. + /// + /// The assembly. + /// The is engine. + public void DiscoverEngines(Assembly assembly, Predicate isEngine) + { + Type[] types; + try + { + types = assembly.GetTypes(); + } + catch + { + // ignore assemblies that cannot be loaded + return; + } + foreach (var type in types) + { + if (type.IsInterface || type.IsAbstract) + continue; + + if (IsEngineType(type) && isEngine(type)) + { + var scriptingEngine = (ScriptingEngine) Activator.CreateInstance(type); + var scriptingEngineCurr = _scriptingEngines.Get(scriptingEngine.LanguagePrefix); + if (scriptingEngineCurr != null) + { + if (scriptingEngineCurr.GetType() == type) + { + continue; + } + + throw new ScriptingEngineException( + string.Format("duplicate language prefix \"{0}\" detected", scriptingEngine.LanguagePrefix)); + } + + _scriptingEngines.Add(scriptingEngine.LanguagePrefix, scriptingEngine); + } + } + } + + /// + /// Determines whether the type is a valid scripting engine type. + /// + /// The type. + /// + /// true if [is engine type] [the specified type]; otherwise, false. + /// + public virtual bool IsEngineType(Type type) + { + return type.IsSubclassOrImplementsInterface(); + } + + /// + /// Compiles the specified language prefix. + /// + /// The language prefix. + /// The script. + /// + public Func Compile(string dialect, ExpressionScriptProvided script) + { + var scriptingEngine = _scriptingEngines.Get(dialect); + if (scriptingEngine == null) + throw new ExprValidationException("Failed to obtain script engine for dialect '" + dialect + "' for script '" + script.Name + "'"); + + return scriptingEngine.Compile(script); + } + + public void VerifyScript(string dialect, ExpressionScriptProvided script) + { + var scriptingEngine = _scriptingEngines.Get(dialect); + if (scriptingEngine == null) + throw new ExprValidationException("Failed to obtain script engine for dialect '" + dialect + "' for script '" + script.Name + "'"); + + scriptingEngine.Verify(script); + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/timer/EPLTimerTask.cs b/NEsper.Core/NEsper.Core/timer/EPLTimerTask.cs new file mode 100755 index 000000000..9a252bf0f --- /dev/null +++ b/NEsper.Core/NEsper.Core/timer/EPLTimerTask.cs @@ -0,0 +1,83 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Timers; +using com.espertech.esper.compat.logging; + + +namespace com.espertech.esper.timer +{ + /// + /// Timer task to simply invoke the callback when triggered. + /// + + sealed class EPLTimerTask + { + private readonly TimerCallback _timerCallback; + private bool _isCancelled; + + internal bool EnableStats; + internal long LastDrift; + internal long MaxDrift; + internal long TotalDrift; + internal long InvocationCount; + + public bool Cancelled + { + set { _isCancelled = value; } + } + + public EPLTimerTask(TimerCallback callback) + { + _timerCallback = callback; + EnableStats = false; + LastDrift = 0; + MaxDrift = 0; + TotalDrift = 0; + InvocationCount = 0; + } + + public void Run(object sender, ElapsedEventArgs e) + { + if (!_isCancelled) + { + if (EnableStats) + { + // If we are called early, then delay will be positive. If we are called late, then the delay will be negative. + // NOTE: don't allow _enableStats to be set until future has been set + LastDrift = 0; // no drift detection + TotalDrift += LastDrift; + InvocationCount++; + if (LastDrift > MaxDrift) + MaxDrift = LastDrift; + } + + try + { + _timerCallback(); + } + catch (Exception ex) + { + Log.Error("Timer thread caught unhandled exception: " + ex.Message, ex); + } + + } + } + + internal void ResetStats() + { + InvocationCount = 0; + LastDrift = 0; + TotalDrift = 0; + MaxDrift = 0; + } + + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + } +} diff --git a/NEsper.Core/NEsper.Core/timer/TimeSourceService.cs b/NEsper.Core/NEsper.Core/timer/TimeSourceService.cs new file mode 100755 index 000000000..0d07b9b5c --- /dev/null +++ b/NEsper.Core/NEsper.Core/timer/TimeSourceService.cs @@ -0,0 +1,18 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.timer +{ + public interface TimeSourceService + { + /// + /// Returns time in millis. + /// + long GetTimeMillis(); + } +} diff --git a/NEsper.Core/NEsper.Core/timer/TimeSourceServiceHighResolution.cs b/NEsper.Core/NEsper.Core/timer/TimeSourceServiceHighResolution.cs new file mode 100755 index 000000000..c735dba1b --- /dev/null +++ b/NEsper.Core/NEsper.Core/timer/TimeSourceServiceHighResolution.cs @@ -0,0 +1,28 @@ +using com.espertech.esper.compat; + +namespace com.espertech.esper.timer +{ + /// + /// Allow for different strategies for getting VM (wall clock) time. + /// + public class TimeSourceServiceHighResolution : TimeSourceService + { + private readonly HighResolutionTimeProvider _highResolutionTimeProvider; + + /// + /// Initializes a new instance of the class. + /// + public TimeSourceServiceHighResolution() + { + _highResolutionTimeProvider = HighResolutionTimeProvider.Instance; + } + + /// + /// Returns time in millis. + /// + public long GetTimeMillis() + { + return _highResolutionTimeProvider.CurrentTime; + } + } +} diff --git a/NEsper.Core/NEsper.Core/timer/TimeSourceServiceImpl.cs b/NEsper.Core/NEsper.Core/timer/TimeSourceServiceImpl.cs new file mode 100755 index 000000000..ab13d8875 --- /dev/null +++ b/NEsper.Core/NEsper.Core/timer/TimeSourceServiceImpl.cs @@ -0,0 +1,85 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.compat; + +namespace com.espertech.esper.timer +{ + /// + /// Allow for different strategies for getting VM (wall clock) time. + /// See JIRA issue ESPER-191 Support nano/microsecond resolution for more + /// information on Java system time-call performance, accuracy and drift. + /// + /// Jerry Shea + public class TimeSourceServiceImpl : TimeSourceService + { + private const long MICROS_TO_MILLIS = 1000; + private const long NANOS_TO_MICROS = 1000; + + /// + /// A public variable indicating whether to use the System millisecond time or + /// nano time, to be configured through the engine settings. + /// + public static bool IsSystemCurrentTime = true; + + private readonly long _wallClockOffset; + private readonly string _description; + + /// Ctor. + public TimeSourceServiceImpl() + { + _wallClockOffset = DateTimeHelper.CurrentTimeMillis * MICROS_TO_MILLIS - GetTimeMicros(); + _description = string.Format("{0}: resolution {1} microsecs", GetType().FullName, CalculateResolution()); + } + + /// + /// Convenience method to get time in milliseconds + /// + /// wall-clock time in milliseconds + public long GetTimeMillis() + { + if (IsSystemCurrentTime) + { + return DateTimeHelper.CurrentTimeMillis; + } + return GetTimeMicros()/MICROS_TO_MILLIS; + } + + private long GetTimeMicros() + { + return (DateTimeHelper.CurrentTimeNanos/NANOS_TO_MICROS) + _wallClockOffset; + } + + + /// + /// Calculate resolution of this timer in microseconds i.e. what is the resolution + /// of the underlying platform's timer. + /// + /// timer resolution + protected long CalculateResolution() + { + const int loops = 5; + long totalResolution = 0; + long time = this.GetTimeMicros(), prevTime = time; + for (int i = 0; i < loops; i++) + { + // wait until time changes + while (time == prevTime) + time = GetTimeMicros(); + totalResolution += time - prevTime; + prevTime = time; + } + return totalResolution / loops; + } + + public override string ToString() + { + return _description; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/timer/TimerCallback.cs b/NEsper.Core/NEsper.Core/timer/TimerCallback.cs new file mode 100755 index 000000000..2355b69af --- /dev/null +++ b/NEsper.Core/NEsper.Core/timer/TimerCallback.cs @@ -0,0 +1,22 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +namespace com.espertech.esper.timer +{ + /// + /// Invoked by the internal clocking service at regular intervals. + /// + + public delegate void TimerCallback(); + + public interface ITimerCallback + { + void TimerCallback(); + } +} diff --git a/NEsper.Core/NEsper.Core/timer/TimerService.cs b/NEsper.Core/NEsper.Core/timer/TimerService.cs new file mode 100755 index 000000000..b6c9eb039 --- /dev/null +++ b/NEsper.Core/NEsper.Core/timer/TimerService.cs @@ -0,0 +1,60 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +namespace com.espertech.esper.timer +{ + /// + /// Service interface for repeated callbacks at regular intervals. + /// + + public interface TimerService + { + /// Set the callback method to invoke for clock ticks. + TimerCallback Callback { set; } + + /// Start clock expecting callbacks at regular intervals and a fixed rate. + /// Catch-up callbacks are possible should the callback fall behind. + /// + void StartInternalClock(); + + /// Stop internal clock. + /// use true to indicate whether to warn if the clock is not Started, use false to not warn + /// and expect the clock to be not Started. + /// + void StopInternalClock(bool warnIfNotStarted); + + /// + /// Returns a flag indicating whether statistics are enabled. + /// + bool AreStatsEnabled { get; set; } + + /// + /// Gets the maximum drift. + /// + long MaxDrift { get; } + + /// + /// Gets the last drift. + /// + + long LastDrift { get; } + + /// + /// Gets the total drift. + /// + + long TotalDrift { get; } + + /// + /// Gets the number of times the timer has been invoked. + /// + + long InvocationCount { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/timer/TimerServiceImpl.cs b/NEsper.Core/NEsper.Core/timer/TimerServiceImpl.cs new file mode 100755 index 000000000..b87518f58 --- /dev/null +++ b/NEsper.Core/NEsper.Core/timer/TimerServiceImpl.cs @@ -0,0 +1,212 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Threading; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.logging; + +namespace com.espertech.esper.timer +{ + /// + /// Implementation of the internal clocking service interface. + /// + + public class TimerServiceImpl : TimerService + { + private ITimer _timer; + private TimerCallback _timerCallback; + private EPLTimerTask _timerTask; + private bool _timerTaskCancelled; + + /// + /// Set the callback method to invoke for clock ticks. + /// + /// + + public TimerCallback Callback + { + set { this._timerCallback = value; } + } + + /// + /// Gets the msec timer resolution. + /// + /// The msec timer resolution. + public long MsecTimerResolution { get; private set; } + + /// + /// Returns a flag indicating whether statistics are enabled. + /// + + public bool AreStatsEnabled + { + get { return _timerTask.EnableStats; } + set + { + if (value) + { + EnableStats(); + } + else + { + DisableStats(); + } + } + } + + public long MaxDrift + { + get { return _timerTask.MaxDrift; } + } + + public long LastDrift + { + get { return _timerTask.LastDrift; } + } + + public long TotalDrift + { + get { return _timerTask.TotalDrift; } + } + + /// + /// Gets the number of times the timer has been invoked. + /// + public long InvocationCount + { + get { return _timerTask.InvocationCount; } + } + + /// + /// Gets the unique id for the timer. + /// + /// The id. + public Guid Id { get; private set; } + + /// + /// Gets the engine URI. + /// + /// The engine URI. + public string EngineURI { get; private set; } + + /// Constructor. + /// is the millisecond resolution or interval the internal timer thread processes schedules + /// engine URI + public TimerServiceImpl(String engineURI, long msecTimerResolution) + { + Id = Guid.NewGuid(); + EngineURI = engineURI; + MsecTimerResolution = msecTimerResolution; + _timerTaskCancelled = false; + } + + /// + /// Handles the timer event + /// + /// The user state object. + + private void OnTimerElapsed(Object state) + { + if (!_timerTaskCancelled) + { + if (_timerCallback != null) + { + _timerCallback(); + } + } + } + + /// + /// Start clock expecting callbacks at regular intervals and a fixed rate. + /// Catch-up callbacks are possible should the callback fall behind. + /// + public void StartInternalClock() + { + if (_timer != null) + { + Log.Warn(".StartInternalClock Internal clock is already started, stop first before starting, operation not completed"); + return; + } + + if (Log.IsDebugEnabled) + { + Log.Debug(".StartInternalClock Starting internal clock daemon thread, resolution=" + MsecTimerResolution); + } + + if (_timerCallback == null) + { + throw new IllegalStateException("Timer callback not set"); + } + + _timerTask = new EPLTimerTask(_timerCallback); + + _timerTaskCancelled = false; + _timer = TimerFactory.DefaultTimerFactory.CreateTimer( + OnTimerElapsed, MsecTimerResolution); + } + + /// + /// Stop internal clock. + /// + /// use true to indicate whether to warn if the clock is not Started, use false to not warn + /// and expect the clock to be not Started. + public void StopInternalClock(bool warnIfNotStarted) + { + if (_timer == null) + { + if (warnIfNotStarted) + { + Log.Warn(".StopInternalClock Internal clock is already Stopped, Start first before Stopping, operation not completed"); + } + return; + } + + if (Log.IsDebugEnabled) + { + Log.Debug(".StopInternalClock Stopping internal clock daemon thread"); + } + + _timerTaskCancelled = true; + _timer.Dispose(); + + try + { + // Sleep for at least 100 ms to await the internal timer + Thread.Sleep(100); + } + catch (ThreadInterruptedException) + { + } + + _timer = null; + } + + public void EnableStats() + { + if (_timerTask != null) + { + _timerTask.EnableStats = true; + } + } + + public void DisableStats() + { + if (_timerTask != null) + { + _timerTask.EnableStats = false; + // now it is safe to reset stats without any synchronization + _timerTask.ResetStats(); + } + } + + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + } +} diff --git a/NEsper.Core/NEsper.Core/type/BitWiseOpEnum.cs b/NEsper.Core/NEsper.Core/type/BitWiseOpEnum.cs new file mode 100755 index 000000000..7b86a4480 --- /dev/null +++ b/NEsper.Core/NEsper.Core/type/BitWiseOpEnum.cs @@ -0,0 +1,338 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; + +using com.espertech.esper.collection; + +namespace com.espertech.esper.type +{ + /// + /// Enum representing relational types of operation. + /// + public enum BitWiseOpEnum + { + /// + /// Bitwise and. + /// + BAND, + + /// + /// Bitwise or. + /// + BOR, + + /// + /// Bitwise xor. + /// + BXOR + } + + [Serializable] + public static class BitWiseOpEnumExtensions + { + private static readonly IDictionary Computers; + + /// Returns string rendering of enum. + /// bitwise operator string + + public static String GetComputeDescription(this BitWiseOpEnum value) + { + return GetExpressionText(value); + } + + + public static String GetExpressionText(this BitWiseOpEnum value) + { + switch(value) + { + case BitWiseOpEnum.BAND: + return "&"; + case BitWiseOpEnum.BOR: + return "|"; + case BitWiseOpEnum.BXOR: + return "^"; + } + + throw new ArgumentException("invalid value", "value"); + } + + /// + /// Initializes the class. + /// + + static BitWiseOpEnumExtensions() + { + Computers = new Dictionary(); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(byte?), BitWiseOpEnum.BAND }), BAndByte); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(short?), BitWiseOpEnum.BAND }), BAndShort); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(int?), BitWiseOpEnum.BAND }), BAndInt); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(long?), BitWiseOpEnum.BAND }), BAndLong); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(bool?), BitWiseOpEnum.BAND }), BAndBoolean); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(byte?), BitWiseOpEnum.BOR }), BOrByte); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(short?), BitWiseOpEnum.BOR }), BOrShort); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(int?), BitWiseOpEnum.BOR }), BOrInt); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(long?), BitWiseOpEnum.BOR }), BOrLong); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(bool?), BitWiseOpEnum.BOR }), BOrBoolean); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(byte?), BitWiseOpEnum.BXOR }), BXorByte); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(short?), BitWiseOpEnum.BXOR }), BXorShort); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(int?), BitWiseOpEnum.BXOR }), BXorInt); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(long?), BitWiseOpEnum.BXOR }), BXorLong); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(bool?), BitWiseOpEnum.BXOR }), BXorBoolean); + } + + /// + /// Returns number or bool computation for the target coercion type. + /// + /// The value. + /// target type + /// number cruncher + + public static Computer GetComputer(this BitWiseOpEnum value, Type coercedType) + { + if ((coercedType != typeof(byte?)) && + (coercedType != typeof(byte?)) && + (coercedType != typeof(short?)) && + (coercedType != typeof(int?)) && + (coercedType != typeof(long?)) && + (coercedType != typeof(bool?))) + { + throw new ArgumentException( "Expected base numeric or bool type for computation result but got type " + coercedType ); + } + + MultiKeyUntyped key = new MultiKeyUntyped( new Object[] { coercedType, value } ); + return Computers[key]; + } + + /// Computer for relational op. + + public delegate Object Computer( Object objOne, Object objTwo ); + + /// + /// Bit Wise And. + /// + /// The obj one. + /// The obj two. + /// + + public static Object BAndByte( Object objOne, Object objTwo ) + { + byte? n1 = (byte?) objOne; + byte? n2 = (byte?) objTwo; + byte? result = (byte?) ( n1 & n2 ); + return result; + } + + /// + /// Bit Wise Or. + /// + /// The obj one. + /// The obj two. + /// + + public static Object BOrByte(Object objOne, Object objTwo) + { + byte? n1 = (byte?) objOne; + byte? n2 = (byte?) objTwo; + byte? result = (byte?) ( n1 | n2); + return result; + } + + /// + /// Bit Wise Xor. + /// + /// The obj one. + /// The obj two. + /// + + public static Object BXorByte(Object objOne, Object objTwo) + { + byte? n1 = (byte?) objOne; + byte? n2 = (byte?) objTwo; + byte? result = (byte?) ( n1 ^ n2 ); + return result; + } + + /// + /// Bit Wise And. + /// + /// The obj one. + /// The obj two. + /// + + public static Object BAndShort(Object objOne, Object objTwo) + { + short? n1 = (short?) objOne; + short? n2 = (short?) objTwo; + short? result = (short?) ( n1 & n2 ); + return result; + } + + /// + /// Bit Wise Or. + /// + /// The obj one. + /// The obj two. + /// + + public static Object BOrShort(Object objOne, Object objTwo) + { + short? n1 = (short?) objOne; + short? n2 = (short?) objTwo; + short? result = (short?) ( n1 | n2 ); + return result; + } + + /// + /// Bit Wise Xor. + /// + /// The obj one. + /// The obj two. + /// + + public static Object BXorShort(Object objOne, Object objTwo) + { + short? n1 = (short?) objOne; + short? n2 = (short?) objTwo; + short? result = (short?) ( n1 ^ n2 ); + return result; + } + + /// + /// Bit Wise And. + /// + /// The obj one. + /// The obj two. + /// + public static Object BAndInt(Object objOne, Object objTwo) + { + int? n1 = (int?) objOne; + int? n2 = (int?) objTwo; + int? result = n1 & n2; + return result; + } + + /// + /// Bit Wise Or. + /// + /// The obj one. + /// The obj two. + /// + + public static Object BOrInt(Object objOne, Object objTwo) + { + int? n1 = (int?) objOne; + int? n2 = (int?) objTwo; + int? result = n1 | n2; + return result; + } + + /// + /// Bit Wise Xor. + /// + /// The obj one. + /// The obj two. + /// + public static Object BXorInt(Object objOne, Object objTwo) + { + int? n1 = (int?) objOne; + int? n2 = (int?) objTwo; + int? result = n1 ^ n2; + return result; + } + + /// + /// Bit Wise And. + /// + /// The obj one. + /// The obj two. + /// + public static Object BAndLong(Object objOne, Object objTwo) + { + long? n1 = (long?) objOne; + long? n2 = (long?) objTwo; + long? result = n1 & n2; + return result; + } + + /// + /// Bit Wise Or. + /// + /// The obj one. + /// The obj two. + /// + + public static Object BOrLong(Object objOne, Object objTwo) + { + long? n1 = (long?) objOne; + long? n2 = (long?) objTwo; + long? result = n1 | n2; + return result; + } + + /// + /// Bit Wise Xor. + /// + /// The obj one. + /// The obj two. + /// + public static Object BXorLong(Object objOne, Object objTwo) + { + long? n1 = (long?) objOne; + long? n2 = (long?) objTwo; + long? result = n1 ^ n2; + return result; + } + + /// + /// Bit Wise And. + /// + /// The obj one. + /// The obj two. + /// + public static Object BAndBoolean(Object objOne, Object objTwo) + { + bool? b1 = (bool?) objOne; + bool? b2 = (bool?) objTwo; + bool? result = b1 & b2; + return result; + } + + /// + /// Bit Wise Or. + /// + /// The obj one. + /// The obj two. + /// + + public static Object BOrBoolean(Object objOne, Object objTwo) + { + bool? b1 = (bool?) objOne; + bool? b2 = (bool?) objTwo; + bool? result = b1 | b2; + return result; + } + + /// + /// Bit Wise Xor. + /// + /// The obj one. + /// The obj two. + /// + + public static Object BXorBoolean(Object objOne, Object objTwo) + { + bool? b1 = (bool?) objOne; + bool? b2 = (bool?) objTwo; + bool? result = b1 ^ b2; + return result; + } + } +} diff --git a/NEsper.Core/NEsper.Core/type/BoolValue.cs b/NEsper.Core/NEsper.Core/type/BoolValue.cs new file mode 100755 index 000000000..558fa1b3f --- /dev/null +++ b/NEsper.Core/NEsper.Core/type/BoolValue.cs @@ -0,0 +1,124 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; + +namespace com.espertech.esper.type +{ + /// + /// Placeholder for a bool value in an event expression. + /// + + [Serializable] + public sealed class BoolValue : PrimitiveValueBase + { + /// + /// Returns the type of primitive value this instance represents. + /// + /// + /// enum type of primitive + /// + override public PrimitiveValueType Type + { + get { return PrimitiveValueType.BOOL; } + } + + /// + /// Returns a value object. + /// + /// + /// value object + /// + override public Object ValueObject + { + get { return boolValue; } + } + + /// + /// Set a bool value. + /// + /// + override public bool _Boolean + { + set { this.boolValue = value; } + } + + private bool? boolValue; + + /// Constructor. + /// sets the value. + /// + public BoolValue(Boolean boolValue) + { + this.boolValue = boolValue; + } + + /// Constructor. + public BoolValue() + { + } + + /// Parse the bool string. + /// is a bool value + /// + /// parsed bool + /// + public static bool ParseString(String value) + { + bool rvalue; + value = value.ToLower(); + if (!Boolean.TryParse(value, out rvalue)) + { + throw new ArgumentException("Boolean value '" + value + "' cannot be converted to bool"); + } + + return rvalue; + } + + /// Parse the string array returning a bool array. + /// string array + /// + /// typed array + /// + public static bool[] ParseString(String[] values) + { + bool[] result = new bool[values.Length]; + for (int i = 0; i < result.Length; i++) + { + result[i] = ParseString(values[i]); + } + return result; + } + + /// + /// Parse the string literal value into the specific data type. + /// + /// is the textual value to parse + public override void Parse(String value) + { + boolValue = ParseString(value); + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override String ToString() + { + if (boolValue == null) + { + return "null"; + } + + return boolValue.ToString(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/type/ByteValue.cs b/NEsper.Core/NEsper.Core/type/ByteValue.cs new file mode 100755 index 000000000..5dcb773ef --- /dev/null +++ b/NEsper.Core/NEsper.Core/type/ByteValue.cs @@ -0,0 +1,99 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Globalization; + +namespace com.espertech.esper.type +{ + /// + /// Placeholder for a byte value in an event expression. + /// + + [Serializable] + public sealed class ByteValue : PrimitiveValueBase + { + /// + /// Returns the type of primitive value this instance represents. + /// + /// + /// enum type of primitive + /// + override public PrimitiveValueType Type + { + get { return PrimitiveValueType.BYTE; } + } + + /// + /// Returns a value object. + /// + /// + /// value object + /// + override public Object ValueObject + { + get { return _byteValue; } + } + + /// + /// Set a byte value. + /// + /// + override public byte _Byte + { + set { _byteValue = value; } + } + + private byte? _byteValue; + + /// Parses a string value as a byte. + /// to parse + /// byte value + public static byte ParseString(String value) + { + if (value.ToUpper().StartsWith("0X")) + { + return Byte.Parse(value.Substring(2), NumberStyles.HexNumber); + } + + return Byte.Parse(value); + } + + /// + /// Parse the string literal value into the specific data type. + /// + /// is the textual value to parse + public override void Parse(String value) + { + if (value.ToUpper().StartsWith("0X")) + { + _byteValue = Byte.Parse(value.Substring(2), NumberStyles.HexNumber); + } + else + { + _byteValue = Byte.Parse(value); + } + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override String ToString() + { + if (_byteValue == null) + { + return "null"; + } + return _byteValue.ToString(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/type/CronOperatorEnum.cs b/NEsper.Core/NEsper.Core/type/CronOperatorEnum.cs new file mode 100755 index 000000000..458649c18 --- /dev/null +++ b/NEsper.Core/NEsper.Core/type/CronOperatorEnum.cs @@ -0,0 +1,46 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.type +{ + /// + /// Enumeration for special keywords in crontab timer. + /// + public enum CronOperatorEnum + { + /// Last day of week or month. + LASTDAY, + + /// Weekday (nearest to a date) + WEEKDAY, + + /// Last weekday in a month + LASTWEEKDAY + } + + public static class CronOperatorEnumExtensions { + /// Returns the syntax string for the operator. + /// syntax string + public static string GetSyntax(this CronOperatorEnum value) + { + switch (value) + { + case CronOperatorEnum.LASTDAY: + return ("last"); + case CronOperatorEnum.WEEKDAY: + return ("weekday"); + case CronOperatorEnum.LASTWEEKDAY: + return ("lastweekday"); + } + + throw new ArgumentException(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/type/CronParameter.cs b/NEsper.Core/NEsper.Core/type/CronParameter.cs new file mode 100755 index 000000000..77294aebf --- /dev/null +++ b/NEsper.Core/NEsper.Core/type/CronParameter.cs @@ -0,0 +1,59 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.type +{ + /// + /// Hold parameters for timer:at. + /// + [Serializable] + public class CronParameter + { + private readonly CronOperatorEnum _operator; + private readonly int? _day; + private int? _month; + + /// Ctor. + /// is the operator as text + /// is the day text + public CronParameter(CronOperatorEnum @operator, int? day) + { + _operator = @operator; + _day = day; + } + + public CronOperatorEnum Operator + { + get { return _operator; } + } + + public int? Day + { + get { return _day; } + } + + public int? Month + { + get { return _month; } + set { _month = value; } + } + + public String Formatted() + { + return string.Format("{0}(day {1} month {2})", + _operator, + _day.FormatInt(), + _month.FormatInt() + ); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/type/DecimalValue.cs b/NEsper.Core/NEsper.Core/type/DecimalValue.cs new file mode 100755 index 000000000..2ff3733a4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/type/DecimalValue.cs @@ -0,0 +1,128 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; + +using com.espertech.esper.compat; + +namespace com.espertech.esper.type +{ + /// + /// Placeholder for a decimal value in an event expression. + /// + + [Serializable] + public class DecimalValue : PrimitiveValueBase + { + /// + /// Returns the type of primitive value this instance represents. + /// + /// + /// enum type of primitive + /// + override public PrimitiveValueType Type + { + get { return PrimitiveValueType.DECIMAL; } + } + + /// + /// Returns a value object. + /// + /// + /// value object + /// + override public Object ValueObject + { + get { return decimalValue; } + } + + private decimal? decimalValue; + + /// Constructor. + public DecimalValue() + { + } + + /// + /// Constructor setting the value. + /// + /// The decimal value. + public DecimalValue(decimal decimalValue) + { + this.decimalValue = decimalValue; + } + + /// + /// Parse string value returning a decimal. + /// + /// value to parse + /// parsed value + public static decimal ParseString(String value) + { + if (value.EndsWith("m")) + { + value = value.Substring(0, value.Length - 1); + } + + return Decimal.Parse(value); + } + + /// + /// Parse the string literal value into the specific data type. + /// + /// is the textual value to parse + public override void Parse(String value) + { + decimalValue = ParseString(value); + } + + /// Parse the string array returning a decimal array. + /// string array + /// + /// typed array + /// + public static decimal[] ParseString(String[] values) + { + decimal[] result = new decimal[values.Length]; + for (int i = 0; i < result.Length; i++) + { + result[i] = ParseString(values[i]); + } + return result; + } + + /// Return the value as an unboxed. + /// value + /// + + public decimal GetDecimal() + { + if (decimalValue == null) + { + throw new IllegalStateException(); + } + return decimalValue.Value; + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override String ToString() + { + if (decimalValue == null) + { + return "null"; + } + return decimalValue.ToString(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/type/DoubleValue.cs b/NEsper.Core/NEsper.Core/type/DoubleValue.cs new file mode 100755 index 000000000..9012fdaa4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/type/DoubleValue.cs @@ -0,0 +1,140 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; + +using com.espertech.esper.compat; + +namespace com.espertech.esper.type +{ + /// + /// Placeholder for a double value in an event expression. + /// + + [Serializable] + public class DoubleValue : PrimitiveValueBase + { + /// + /// Returns the type of primitive value this instance represents. + /// + /// + /// enum type of primitive + /// + override public PrimitiveValueType Type + { + get { return PrimitiveValueType.DOUBLE; } + } + + /// + /// Returns a value object. + /// + /// + /// value object + /// + override public Object ValueObject + { + get { return _doubleValue; } + } + + private double? _doubleValue; + + /// Constructor. + public DoubleValue() + { + } + + /// Constructor setting the value. + /// value to set. + /// + public DoubleValue(Double doubleValue) + { + _doubleValue = doubleValue; + } + + /// Parse string value returning a double. + /// to parse + /// + /// parsed value + /// + public static double ParseString(String value) + { + // Double strings are terminated with the character 'd' in Java. This + // appears to have propogated itself to the grammar which also uses this + // syntax. Trim the 'd' so that it can be parsed. + + if (value.EndsWith("d") || value.EndsWith("D")) + { + value = value.Substring(0, value.Length - 1); + } + + return Double.Parse(value); + } + + /// + /// Parse the string literal value into the specific data type. + /// + /// is the textual value to parse + public override void Parse(String value) + { + _doubleValue = ParseString(value); + } + + /// Parse the string array returning a double array. + /// string array + /// + /// typed array + /// + public static double[] ParseString(String[] values) + { + double[] result = new double[values.Length]; + for (int i = 0; i < result.Length; i++) + { + result[i] = ParseString(values[i]); + } + return result; + } + + /// Return the value as an unboxed. + /// value + /// + + public double GetDouble() + { + if (_doubleValue == null) + { + throw new IllegalStateException(); + } + return _doubleValue.Value; + } + + /// + /// Set a double value. + /// + /// + public override double _Double + { + set { _doubleValue = value; } + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override String ToString() + { + if (_doubleValue == null) + { + return "null"; + } + return _doubleValue.ToString(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/type/FloatValue.cs b/NEsper.Core/NEsper.Core/type/FloatValue.cs new file mode 100755 index 000000000..5a3c25d7b --- /dev/null +++ b/NEsper.Core/NEsper.Core/type/FloatValue.cs @@ -0,0 +1,118 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; + +namespace com.espertech.esper.type +{ + /// + /// Placeholder for a float value in an event expression. + /// + + [Serializable] + public sealed class FloatValue : PrimitiveValueBase + { + /// + /// Returns the type of primitive value this instance represents. + /// + /// + /// enum type of primitive + /// + override public PrimitiveValueType Type + { + get + { + return PrimitiveValueType.FLOAT; + } + + } + /// + /// Returns a value object. + /// + /// + /// value object + /// + override public Object ValueObject + { + get + { + return floatValue; + } + + } + /// + /// Set a float value. + /// + /// + override public float _Float + { + set + { + this.floatValue = value; + } + + } + private float? floatValue; + + /// Parse string value returning a float. + /// to parse + /// + /// parsed value + /// + public static float ParseString(String value) + { + if (value.EndsWith("f") || value.EndsWith("F")) + { + value = value.Substring(0, value.Length - 1); + } + + return Single.Parse(value); + } + + /// Parse the string array returning a float array. + /// string array + /// + /// typed array + /// + public static float[] ParseString(String[] values) + { + float[] result = new float[values.Length]; + for (int i = 0; i < result.Length; i++) + { + result[i] = ParseString(values[i]); + } + return result; + } + + /// + /// Parse the string literal value into the specific data type. + /// + /// is the textual value to parse + public override void Parse(String value) + { + floatValue = ParseString(value); + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override String ToString() + { + if (floatValue == null) + { + return "null"; + } + + return floatValue.ToString(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/type/FrequencyParameter.cs b/NEsper.Core/NEsper.Core/type/FrequencyParameter.cs new file mode 100755 index 000000000..18e0831cf --- /dev/null +++ b/NEsper.Core/NEsper.Core/type/FrequencyParameter.cs @@ -0,0 +1,97 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.type +{ + /// + /// Encapsulates a parameter specifying a frequency, i.e. '* / 5'. + /// + + [Serializable] + public class FrequencyParameter : NumberSetParameter + { + private readonly int _frequency; + + /// Returns frequency. + /// frequency divisor + /// + public int Frequency + { + get { return _frequency; } + } + + /// Ctor. + /// divisor specifying frequency + /// + + public FrequencyParameter(int frequency) + { + _frequency = frequency; + if (frequency <= 0) + { + throw new ArgumentException("Zero or negative value supplied as freqeuncy"); + } + } + + /// + /// Returns true if all values between and including min and max are supplied by the parameter. + /// + /// lower end of range + /// upper end of range + /// + /// true if parameter specifies all int values between min and max, false if not + /// + public virtual bool IsWildcard(int min, int max) + { + if (_frequency == 1) + { + return true; + } + return false; + } + + /// + /// Return a set of int values representing the value of the parameter for the given range. + /// + /// lower end of range + /// upper end of range + /// set of integer + public ICollection GetValuesInRange(int min, int max) + { + ICollection values = new HashSet(); + int start = min - min % _frequency; + + do + { + if (start >= min) + { + values.Add(start); + } + start += _frequency; + } + while (start <= max); + + return values; + } + + + public bool ContainsPoint(int point) + { + return point % _frequency == 0; + } + + public String Formatted() + { + return "*/" + _frequency; + } + } +} diff --git a/NEsper.Core/NEsper.Core/type/IntParameter.cs b/NEsper.Core/NEsper.Core/type/IntParameter.cs new file mode 100755 index 000000000..a7e7fcafa --- /dev/null +++ b/NEsper.Core/NEsper.Core/type/IntParameter.cs @@ -0,0 +1,88 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.type +{ + /// + /// Parameter supplying a single int value is a set of numbers. + /// + + [Serializable] + public class IntParameter : NumberSetParameter + { + /// Returns int value. + /// int value + /// + public int IntValue { get; set; } + + /// + /// Initializes a new instance of the class. + /// + public IntParameter() + { + } + + /// Ctor. + /// single in value + /// + + public IntParameter(int intValue) + { + IntValue = intValue; + } + + /// + /// Returns true if all values between and including min and max are supplied by the parameter. + /// + /// lower end of range + /// upper end of range + /// + /// true if parameter specifies all int values between min and max, false if not + /// + public virtual bool IsWildcard(int min, int max) + { + if ((IntValue == min) && (IntValue == max)) + { + return true; + } + return false; + } + + /// + /// Return a set of int values representing the value of the parameter for the given range. + /// + /// lower end of range + /// upper end of range + /// set of integer + public ICollection GetValuesInRange(int min, int max) + { + ICollection values = new HashSet(); + + if ((IntValue >= min) && (IntValue <= max)) + { + values.Add(IntValue); + } + + return values; + } + + public bool ContainsPoint(int point) + { + return IntValue == point; + } + + public String Formatted() + { + return IntValue.ToString(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/type/IntValue.cs b/NEsper.Core/NEsper.Core/type/IntValue.cs new file mode 100755 index 000000000..297ed94e2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/type/IntValue.cs @@ -0,0 +1,124 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Globalization; + +namespace com.espertech.esper.type +{ + /// + /// Placeholder for an integer value in an event expression. + /// + + [Serializable] + public sealed class IntValue : PrimitiveValueBase + { + /// + /// Returns the type of primitive value this instance represents. + /// + /// + /// enum type of primitive + /// + override public PrimitiveValueType Type + { + get { return PrimitiveValueType.INTEGER; } + } + + /// + /// Returns a value object. + /// + /// + /// value object + /// + override public Object ValueObject + { + get { return _intValue; } + } + + /// + /// Set an int value. + /// + /// + override public int _Int + { + set { _intValue = value; } + } + + private int? _intValue; + + /// + /// Constructor. + /// + + public IntValue() + { + } + + /// Constructor. + /// is the value to set to + /// + + public IntValue(int intValue) + { + _intValue = intValue; + } + + /// Parse the string array returning a int array. + /// string array + /// + /// typed array + /// + + public static int[] ParseString(String[] values) + { + int[] result = new int[values.Length]; + for (int i = 0; i < result.Length; i++) + { + result[i] = ParseString(values[i]); + } + + return result; + } + + /// Parse string value returning a int. + /// to parse + /// + /// parsed value + /// + + public static int ParseString(String value) + { + return Int32.Parse(value); + } + + /// + /// Parse the string literal value into the specific data type. + /// + /// is the textual value to parse + public override void Parse(String value) + { + _intValue = Int32.Parse(value); + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override String ToString() + { + if (_intValue == null) + { + return "null"; + } + return _intValue.ToString(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/type/ListParameter.cs b/NEsper.Core/NEsper.Core/type/ListParameter.cs new file mode 100755 index 000000000..9a0c32b0b --- /dev/null +++ b/NEsper.Core/NEsper.Core/type/ListParameter.cs @@ -0,0 +1,127 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; +using System.IO; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.type +{ + /// + /// Represents a list of values in a set of numeric parameters. + /// + + [Serializable] + public class ListParameter : NumberSetParameter + { + private readonly IList _parameters; + + /// + /// Initializes a new instance of the class. + /// + public ListParameter() + { + _parameters = new List(); + } + + /// + /// Initializes a new instance of the class. + /// + /// The parameters. + public ListParameter(IList parameters) + { + _parameters = parameters; + } + + /// Add to the list a further parameter. + /// is the parameter to add + /// + public virtual void Add( NumberSetParameter numberSetParameter ) + { + _parameters.Add( numberSetParameter ); + } + + /// Returns list of parameters. + /// list of parameters + /// + public IList Parameters + { + get { return _parameters; } + } + + /// + /// Returns true if all values between and including min and max are supplied by the parameter. + /// + /// lower end of range + /// upper end of range + /// + /// true if parameter specifies all int values between min and max, false if not + /// + public virtual bool IsWildcard( int min, int max ) + { + foreach ( NumberSetParameter param in _parameters ) + { + if ( param.IsWildcard( min, max ) ) + { + return true; + } + } + return false; + } + + /// + /// Return a set of int values representing the value of the parameter for the given range. + /// + /// lower end of range + /// upper end of range + /// set of integer + public ICollection GetValuesInRange( int min, int max ) + { + ICollection result = new HashSet(); + + foreach ( NumberSetParameter param in _parameters ) + { + result.AddAll( param.GetValuesInRange( min, max ) ); + } + + return result; + } + + public bool ContainsPoint(int point) + { + return ContainsPoint(_parameters, point); + } + + public String Formatted() + { + var writer = new StringWriter(); + var delimiter = ""; + foreach (var param in _parameters) + { + writer.Write(delimiter); + writer.Write(param.Formatted()); + delimiter = ", "; + } + return writer.ToString(); + } + + public static Boolean ContainsPoint(IList parameters, int point) + { + foreach (var param in parameters) + { + if (param.ContainsPoint(point)) + { + return true; + } + } + return false; + } + } +} diff --git a/NEsper.Core/NEsper.Core/type/LongValue.cs b/NEsper.Core/NEsper.Core/type/LongValue.cs new file mode 100755 index 000000000..cd480cf44 --- /dev/null +++ b/NEsper.Core/NEsper.Core/type/LongValue.cs @@ -0,0 +1,125 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; + +using com.espertech.esper.compat; + +namespace com.espertech.esper.type +{ + /// + /// Placeholder for a long-typed value in an event expression. + /// + + [Serializable] + public sealed class LongValue : PrimitiveValueBase + { + /// + /// Returns the type of primitive value this instance represents. + /// + /// + /// enum type of primitive + /// + override public PrimitiveValueType Type + { + get { return PrimitiveValueType.LONG; } + } + + /// + /// Returns a value object. + /// + /// + /// value object + /// + override public Object ValueObject + { + get { return longValue; } + } + + private long? longValue; + + /// + /// Parse the string literal value into the specific data type. + /// + /// is the textual value to parse + public override void Parse(String value) + { + longValue = ParseString(value); + } + + /// Parse the string containing a long value. + /// is the textual long value + /// + /// long value + /// + public static long ParseString(String value) + { + if ((value.EndsWith("L")) || ((value.EndsWith("l")))) + { + value = value.Substring(0, value.Length - 1); + } + if (value.StartsWith("+")) + { + value = value.Substring(1); + } + return long.Parse(value); + } + + /// Parse the string array returning a long array. + /// string array + /// + /// typed array + /// + public static long[] ParseString(String[] values) + { + long[] result = new long[values.Length]; + for (int i = 0; i < result.Length; i++) + { + result[i] = ParseString(values[i]); + } + return result; + } + + /// + /// Set a long value. + /// + /// + public override long _Long + { + set { this.longValue = value; } + } + + /// Returns the long value. + /// long value + /// + public long GetLong() + { + if (longValue == null) + { + throw new IllegalStateException(); + } + return longValue.Value; + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override String ToString() + { + if (longValue == null) + { + return "null"; + } + return longValue.ToString(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/type/MathArithTypeEnum.cs b/NEsper.Core/NEsper.Core/type/MathArithTypeEnum.cs new file mode 100755 index 000000000..7df264029 --- /dev/null +++ b/NEsper.Core/NEsper.Core/type/MathArithTypeEnum.cs @@ -0,0 +1,709 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Numerics; + +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.type +{ + public enum MathArithTypeEnum + { + /// + /// Plus. + /// + ADD, + /// + /// Minus + /// + SUBTRACT, + /// + /// Divide + /// + DIVIDE, + /// + /// Multiply. + /// + MULTIPLY, + /// + /// Modulo. + /// + MODULO + } + + /// + /// Enumeration for the type of arithmatic to use. + /// + + [Serializable] + public static class MathArithTypeEnumExtensions + { + private static readonly IDictionary Computers; + + static MathArithTypeEnumExtensions() + { + Computers = new Dictionary(); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(BigInteger), MathArithTypeEnum.ADD }), AddBigInteger); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(decimal), MathArithTypeEnum.ADD }), AddDecimal); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(double), MathArithTypeEnum.ADD }), AddDouble); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(float), MathArithTypeEnum.ADD }), AddSingle); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(long), MathArithTypeEnum.ADD }), AddInt64); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(int), MathArithTypeEnum.ADD }), AddInt32); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(ulong), MathArithTypeEnum.ADD }), AddUInt64); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(uint), MathArithTypeEnum.ADD }), AddUInt32); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(BigInteger), MathArithTypeEnum.SUBTRACT }), SubtractBigInteger); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(decimal), MathArithTypeEnum.SUBTRACT }), SubtractDecimal); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(double), MathArithTypeEnum.SUBTRACT }), SubtractDouble); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(float), MathArithTypeEnum.SUBTRACT }), SubtractSingle); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(long), MathArithTypeEnum.SUBTRACT }), SubtractInt64); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(int), MathArithTypeEnum.SUBTRACT }), SubtractInt32); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(ulong), MathArithTypeEnum.SUBTRACT }), SubtractUInt64); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(uint), MathArithTypeEnum.SUBTRACT }), SubtractUInt32); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(BigInteger), MathArithTypeEnum.MULTIPLY }), MultiplyBigInteger); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(decimal), MathArithTypeEnum.MULTIPLY }), MultiplyDecimal); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(double), MathArithTypeEnum.MULTIPLY }), MultiplyDouble); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(float), MathArithTypeEnum.MULTIPLY }), MultiplySingle); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(long), MathArithTypeEnum.MULTIPLY }), MultiplyInt64); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(int), MathArithTypeEnum.MULTIPLY }), MultiplyInt32); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(ulong), MathArithTypeEnum.MULTIPLY }), MultiplyUInt64); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(uint), MathArithTypeEnum.MULTIPLY }), MultiplyUInt32); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(BigInteger), MathArithTypeEnum.MODULO }), ModuloBigInteger); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(decimal), MathArithTypeEnum.MODULO }), ModuloDecimal); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(double), MathArithTypeEnum.MODULO }), ModuloDouble); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(float), MathArithTypeEnum.MODULO }), ModuloSingle); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(long), MathArithTypeEnum.MODULO }), ModuloInt64); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(int), MathArithTypeEnum.MODULO }), ModuloInt32); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(ulong), MathArithTypeEnum.MODULO }), ModuloUInt64); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(uint), MathArithTypeEnum.MODULO }), ModuloUInt32); + } + + /// Returns string representation of enum. + /// text for enum + + public static String GetExpressionText(this MathArithTypeEnum value) + { + switch (value) + { + case MathArithTypeEnum.ADD: + return "+"; + case MathArithTypeEnum.SUBTRACT: + return "-"; + case MathArithTypeEnum.DIVIDE: + return "/"; + case MathArithTypeEnum.MULTIPLY: + return "*"; + case MathArithTypeEnum.MODULO: + return "%"; + } + + throw new ArgumentException("invalid value", "value"); + } + + /// + /// Returns the math operator for the string. + /// + /// operator to parse + /// math enum + public static MathArithTypeEnum ParseOperator(string value) + { + switch (value) + { + case "+": + return MathArithTypeEnum.ADD; + case "-": + return MathArithTypeEnum.SUBTRACT; + case "/": + return MathArithTypeEnum.DIVIDE; + case "*": + return MathArithTypeEnum.MULTIPLY; + case "%": + return MathArithTypeEnum.MODULO; + } + + throw new ArgumentException("Unknown operator '" + value + "'"); + } + + /// + /// Interface for number cruncher. + /// + + public delegate Object Computer(Object d1, Object d2); + + /// + /// Returns number cruncher for the target coercion type. + /// + /// The value. + /// target type + /// the LHS type + /// the RHS type + /// false for division returns double, true for using standard integer division + /// false for division-by-zero returns infinity, true for null + /// The optional math context. + /// + /// + /// Expected base numeric type for computation result but got type + coercedType + /// or + /// Could not determine process or type + value + type + coercedType + /// or + /// Could not determine process or type + value + type + coercedType + /// + public static Computer GetComputer(this MathArithTypeEnum value, Type coercedType, Type typeOne, Type typeTwo, bool isIntegerDivision, bool isDivisionByZeroReturnsNull, MathContext optionalMathContext) + { + Type t = Nullable.GetUnderlyingType(coercedType); + if (t != null) + coercedType = t; + + if ((coercedType != typeof(BigInteger)) && + (coercedType != typeof(decimal)) && + (coercedType != typeof(double)) && + (coercedType != typeof(float)) && + (coercedType != typeof(long)) && + (coercedType != typeof(int))) + { + throw new ArgumentException("Expected base numeric type for computation result but got type " + coercedType); + } + + if (value != MathArithTypeEnum.DIVIDE) + { + var key = new MultiKeyUntyped(new Object[] { coercedType, value }); + var computer = Computers.Get(key); + if (computer == null) + { + throw new ArgumentException("Could not determine process or type " + value + " type " + coercedType); + } + return computer; + } + + if (!isIntegerDivision) + { + if (coercedType == typeof(decimal)) + return isDivisionByZeroReturnsNull ? (Computer)DivideDecimalChecked : DivideDecimalUnchecked; + + return isDivisionByZeroReturnsNull ? (Computer)DivideDoubleChecked : DivideDoubleUnchecked; + } + + if (coercedType == typeof(BigInteger)) + return DivideBigInteger; + if (coercedType == typeof(double)) + return isDivisionByZeroReturnsNull ? (Computer)DivideDoubleChecked : DivideDoubleUnchecked; + if (coercedType == typeof(float)) + return DivideSingle; + if (coercedType == typeof(long)) + return DivideInt64; + if (coercedType == typeof(int)) + return DivideInt32; + if (coercedType == typeof(decimal)) + return isDivisionByZeroReturnsNull ? (Computer)DivideDecimalChecked : DivideDecimalUnchecked; + + throw new ArgumentException("Could not determine process or type " + value + " type " + coercedType); + } + + /// + /// Adds big integers. + /// + /// The d1. + /// The d2. + /// + public static Object AddBigInteger(Object d1, Object d2) + { + var nd1 = d1.AsBigInteger(); + var nd2 = d2.AsBigInteger(); + return nd1 + nd2; + } + + /// + /// Adds decimals. + /// + /// The d1. + /// The d2. + /// + public static Object AddDecimal(Object d1, Object d2) + { + return d1.AsDecimal() + d2.AsDecimal(); + } + + /// + /// Adds doubles. + /// + /// The d1. + /// The d2. + /// + public static Object AddDouble(Object d1, Object d2) + { + return d1.AsDouble() + d2.AsDouble(); + } + + /// + /// Adds singles. + /// + /// The d1. + /// The d2. + /// + public static Object AddSingle(Object d1, Object d2) + { + return d1.AsFloat() + d2.AsFloat(); + } + + /// + /// Adds int64s. + /// + /// The d1. + /// The d2. + /// + public static Object AddInt64(Object d1, Object d2) + { + return d1.AsLong() + d2.AsLong(); + } + + /// + /// Adds int32s. + /// + /// The d1. + /// The d2. + /// + public static Object AddInt32(Object d1, Object d2) + { + return d1.AsInt() + d2.AsInt(); + } + + /// + /// Adds unsigned int64s. + /// + /// The d1. + /// The d2. + /// + public static Object AddUInt64(Object d1, Object d2) + { + return Convert.ToUInt64(d1) + Convert.ToUInt64(d2); + } + + /// + /// Adds unsigned 32-bit ints. + /// + /// The d1. + /// The d2. + /// + public static Object AddUInt32(Object d1, Object d2) + { + return Convert.ToUInt32(d1) + Convert.ToUInt32(d2); + } + + /// + /// Subtracts big integers. + /// + /// The d1. + /// The d2. + /// + public static Object SubtractBigInteger(Object d1, Object d2) + { + var nd1 = d1.AsBigInteger(); + var nd2 = d2.AsBigInteger(); + return nd1 - nd2; + } + + /// + /// Subtracts decimals. + /// + /// The d1. + /// The d2. + /// + public static Object SubtractDecimal(Object d1, Object d2) + { + return d1.AsDecimal() - d2.AsDecimal(); + } + + /// + /// Subtracts doubles. + /// + /// The d1. + /// The d2. + /// + public static Object SubtractDouble(Object d1, Object d2) + { + return d1.AsDouble() - d2.AsDouble(); + } + + /// + /// Subtracts singles. + /// + /// The d1. + /// The d2. + /// + public static Object SubtractSingle(Object d1, Object d2) + { + return d1.AsFloat() - d2.AsFloat(); + } + + /// + /// Subtracts 64-bit ints. + /// + /// The d1. + /// The d2. + /// + public static Object SubtractInt64(Object d1, Object d2) + { + return d1.AsLong() - d2.AsLong(); + } + + /// + /// Subtracts 32-bit ints. + /// + /// The d1. + /// The d2. + /// + public static Object SubtractInt32(Object d1, Object d2) + { + return d1.AsInt() - d2.AsInt(); + } + + /// + /// Subtracts unsigned 64-bit ints. + /// + /// The d1. + /// The d2. + /// + public static Object SubtractUInt64(Object d1, Object d2) + { + return Convert.ToUInt64(d1) - Convert.ToUInt64(d2); + } + + /// + /// Subtracts unsigned 32-bit ints. + /// + /// The d1. + /// The d2. + /// + public static Object SubtractUInt32(Object d1, Object d2) + { + return Convert.ToUInt32(d1) - Convert.ToUInt32(d2); + } + + /// + /// Divides big integers. + /// + /// The d1. + /// The d2. + /// + public static Object DivideBigInteger(Object d1, Object d2) + { + var nd1 = d1.AsBigInteger(); + var nd2 = d2.AsBigInteger(); + if (nd2 == 0) return null; + return nd1 / nd2; + } + + /// + /// Divides decimals. + /// + /// The d1. + /// The d2. + /// + public static Object DivideDecimalChecked(Object d1, Object d2) + { + var nd1 = d1.AsDecimal(); + var nd2 = d2.AsDecimal(); + if (nd2 == 0) return null; + return nd1 / nd2; + } + + /// + /// Divides decimals. + /// + /// The d1. + /// The d2. + /// + public static Object DivideDecimalUnchecked(Object d1, Object d2) + { + var nd1 = d1.AsDecimal(); + var nd2 = d2.AsDecimal(); + return nd1 / nd2; + } + + /// + /// Divides doubles. + /// + /// The d1. + /// The d2. + /// + public static Object DivideDoubleUnchecked(Object d1, Object d2) + { + var nd1 = d1.AsDouble(); + var nd2 = d2.AsDouble(); + return nd1 / nd2; + } + + /// + /// Divides doubles. + /// + /// The d1. + /// The d2. + /// + public static Object DivideDoubleChecked(Object d1, Object d2) + { + var nd1 = d1.AsDouble(); + var nd2 = d2.AsDouble(); + if (nd2 == 0) return null; + return nd1 / nd2; + } + + /// + /// Divides singles. + /// + /// The d1. + /// The d2. + /// + public static Object DivideSingle(Object d1, Object d2) + { + var nd1 = d1.AsFloat(); + var nd2 = d2.AsFloat(); + if (nd2 == 0) return null; + return nd1 / nd2; + } + + /// + /// Divides 64-bit ints. + /// + /// The d1. + /// The d2. + /// + public static Object DivideInt64(Object d1, Object d2) + { + var nd1 = d1.AsLong(); + var nd2 = d2.AsLong(); + if (nd2 == 0) return null; + return nd1 / nd2; + } + + /// + /// Divides 32-bit ints. + /// + /// The d1. + /// The d2. + /// + public static Object DivideInt32(Object d1, Object d2) + { + var nd1 = d1.AsInt(); + var nd2 = d2.AsInt(); + if (nd2 == 0) return null; + return nd1 / nd2; + + } + + /// + /// Divides unsigned 64-bit ints. + /// + /// The d1. + /// The d2. + /// + public static Object DivideUInt64(Object d1, Object d2) + { + var nd1 = Convert.ToUInt64(d1); + var nd2 = Convert.ToUInt64(d2); + if (nd2 == 0) return null; + return nd1 / nd2; + } + + /// + /// Divides unsigned 32-bit ints. + /// + /// The d1. + /// The d2. + /// + public static Object DivideUInt32(Object d1, Object d2) + { + var nd1 = Convert.ToUInt32(d1); + var nd2 = Convert.ToUInt32(d2); + if (nd2 == 0) return null; + return nd1 / nd2; + } + + /// + /// Multiplies big integers. + /// + /// The d1. + /// The d2. + /// + public static Object MultiplyBigInteger(Object d1, Object d2) + { + var nd1 = d1.AsBigInteger(); + var nd2 = d2.AsBigInteger(); + return nd1 * nd2; + } + + /// + /// Multiplies decimals. + /// + /// The d1. + /// The d2. + /// + public static Object MultiplyDecimal(Object d1, Object d2) + { + return d1.AsDecimal() * d2.AsDecimal(); + } + + /// + /// Multiplies doubles. + /// + /// The d1. + /// The d2. + /// + public static Object MultiplyDouble(Object d1, Object d2) + { + return d1.AsDouble() * d2.AsDouble(); + } + + /// + /// Multiplies singles. + /// + /// The d1. + /// The d2. + /// + public static Object MultiplySingle(Object d1, Object d2) + { + return d1.AsFloat() * d2.AsFloat(); + } + + /// + /// Multiplies 64-bit ints. + /// + /// The d1. + /// The d2. + /// + public static Object MultiplyInt64(Object d1, Object d2) + { + return d1.AsLong() * d2.AsLong(); + } + + /// + /// Multiplies 32-bit ints. + /// + /// The d1. + /// The d2. + /// + public static Object MultiplyInt32(Object d1, Object d2) + { + return d1.AsInt() * d2.AsInt(); + } + + /// + /// Multiplies unsigned 64-bit ints. + /// + /// The d1. + /// The d2. + /// + public static Object MultiplyUInt64(Object d1, Object d2) + { + return Convert.ToUInt64(d1) * Convert.ToUInt64(d2); + } + + /// + /// Multiplies unsigned 32-bit ints. + /// + /// The d1. + /// The d2. + /// + public static Object MultiplyUInt32(Object d1, Object d2) + { + return Convert.ToUInt32(d1) * Convert.ToUInt32(d2); + } + + /// + /// Moduloes big integers. + /// + /// The d1. + /// The d2. + /// + public static Object ModuloBigInteger(Object d1, Object d2) + { + var nd1 = d1.AsBigInteger(); + var nd2 = d2.AsBigInteger(); + return nd1 % nd2; + } + + /// + /// Moduloes decimals. + /// + /// The d1. + /// The d2. + /// + public static Object ModuloDecimal(Object d1, Object d2) + { + return d1.AsDecimal() % d2.AsDecimal(); + } + + /// + /// Moduloes doubles. + /// + /// The d1. + /// The d2. + /// + public static Object ModuloDouble(Object d1, Object d2) + { + return d1.AsDouble() % d2.AsDouble(); + } + + /// + /// Moduloes singles. + /// + /// The d1. + /// The d2. + /// + public static Object ModuloSingle(Object d1, Object d2) + { + return d1.AsFloat() % d2.AsFloat(); + } + + /// + /// Moduloes 64-bit ints. + /// + /// The d1. + /// The d2. + /// + public static Object ModuloInt64(Object d1, Object d2) + { + return d1.AsLong() % d2.AsLong(); + } + + /// + /// Moduloes 32-bit ints. + /// + /// The d1. + /// The d2. + /// + public static Object ModuloInt32(Object d1, Object d2) + { + return d1.AsInt() % d2.AsInt(); + } + + /// + /// Moduloes unsigned 64-bit ints. + /// + /// The d1. + /// The d2. + /// + public static Object ModuloUInt64(Object d1, Object d2) + { + return Convert.ToUInt64(d1) % Convert.ToUInt64(d2); + } + + /// + /// Moduloes unsigned 32-bit ints. + /// + /// The d1. + /// The d2. + /// + public static Object ModuloUInt32(Object d1, Object d2) + { + return Convert.ToUInt32(d1) % Convert.ToUInt32(d2); + } + } +} diff --git a/NEsper.Core/NEsper.Core/type/MinMaxTypeEnum.cs b/NEsper.Core/NEsper.Core/type/MinMaxTypeEnum.cs new file mode 100755 index 000000000..9050c041a --- /dev/null +++ b/NEsper.Core/NEsper.Core/type/MinMaxTypeEnum.cs @@ -0,0 +1,243 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Numerics; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.type +{ + /// + /// Enumeration for the type of arithmatic to use. + /// + + public enum MinMaxTypeEnum + { + MAX, + MIN + } + + public static class MinMaxTypeEnumExtensions + { + public static string GetExpressionText(this MinMaxTypeEnum @enum) + { + switch(@enum) + { + case MinMaxTypeEnum.MAX: + return "max"; + case MinMaxTypeEnum.MIN: + return "min"; + } + + throw new ArgumentException("invalid value for enum"); + } + + /// Executes child expression nodes and compares results, returning the min/max. + /// events per stream + /// true if new data + /// the expression evaluator context + /// result + public delegate Object Computer(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext exprEvaluatorContext); + + /// + /// Determines minimum using a conversion to normalize type. + /// + public static Computer CreateMinDoubleComputer(ExprEvaluator[] childNodes) + { + var typeCaster = CastHelper.GetCastConverter(); + + return delegate(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext exprEvaluatorContext) + { + Object valueResult = null; + Double typedResult = Double.MaxValue; + + for (int ii = 0; ii < childNodes.Length; ii++) + { + var valueChild = childNodes[ii].Evaluate(new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext)); + if (valueChild == null) + { + return null; + } + + var typedChild = typeCaster.Invoke(valueChild); + if (typedChild < typedResult) + { + valueResult = valueChild; + typedResult = typedChild; + } + } + + return valueResult; + }; + } + + /// + /// Determines maximum using a conversion to normalize type. + /// + public static Computer CreateMaxDoubleComputer(ExprEvaluator[] childNodes) + { + var typeCaster = CastHelper.GetCastConverter(); + + return delegate(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext exprEvaluatorContext) + { + Object valueResult = null; + Double typedResult = Double.MinValue; + + for (int ii = 0; ii < childNodes.Length; ii++) + { + var valueChild = childNodes[ii].Evaluate(new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext)); + if (valueChild == null) + { + return null; + } + + var typedChild = typeCaster(valueChild); + if (typedChild > typedResult) + { + valueResult = valueChild; + typedResult = typedChild; + } + } + + return valueResult; + }; + } + + /// + /// Determines minimum using a conversion to normalize type. + /// + public static Computer CreateMinDecimalComputer(ExprEvaluator[] childNodes) + { + var typeCaster = CastHelper.GetCastConverter(); + + return delegate(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext exprEvaluatorContext) + { + Object valueResult = null; + Decimal typedResult = Decimal.MaxValue; + + for (int ii = 0; ii < childNodes.Length; ii++) + { + var valueChild = childNodes[ii].Evaluate(new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext)); + if (valueChild == null) + { + return null; + } + + var typedChild = typeCaster.Invoke(valueChild); + if (typedChild < typedResult) + { + valueResult = valueChild; + typedResult = typedChild; + } + } + + return valueResult; + }; + } + + /// + /// Determines maximum using a conversion to normalize type. + /// + public static Computer CreateMaxDecimalComputer(ExprEvaluator[] childNodes) + { + var typeCaster = CastHelper.GetCastConverter(); + + return delegate(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext exprEvaluatorContext) + { + Object valueResult = null; + Decimal typedResult = Decimal.MinValue; + + for (int ii = 0; ii < childNodes.Length; ii++) + { + var valueChild = childNodes[ii].Evaluate(new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext)); + if (valueChild == null) + { + return null; + } + + var typedChild = typeCaster(valueChild); + if (typedChild > typedResult) + { + valueResult = valueChild; + typedResult = typedChild; + } + } + + return valueResult; + }; + } + + /// + /// Determines minimum using a conversion to normalize type. + /// + public static Computer CreateMinBigIntComputer(ExprEvaluator[] childNodes) + { + var typeCaster = CastHelper.GetCastConverter(); + + return delegate(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext exprEvaluatorContext) + { + Object valueResult = null; + BigInteger? typedResult = null; + + for (int ii = 0; ii < childNodes.Length; ii++) + { + var valueChild = childNodes[ii].Evaluate(new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext)); + if (valueChild == null) + { + return null; + } + + var typedChild = typeCaster.Invoke(valueChild); + if ((typedResult == null) || (typedChild < typedResult.Value)) + { + valueResult = valueChild; + typedResult = typedChild; + } + } + + return valueResult; + }; + } + + /// + /// Determines maximum using a conversion to normalize type. + /// + public static Computer CreateMaxBigIntComputer(ExprEvaluator[] childNodes) + { + var typeCaster = CastHelper.GetCastConverter(); + + return delegate(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext exprEvaluatorContext) + { + Object valueResult = null; + BigInteger? typedResult = null; + + for (int ii = 0; ii < childNodes.Length; ii++) + { + var valueChild = childNodes[ii].Evaluate(new EvaluateParams(eventsPerStream, isNewData, exprEvaluatorContext)); + if (valueChild == null) + { + return null; + } + + var typedChild = typeCaster(valueChild); + if ((typedResult == null) || (typedChild > typedResult.Value)) + { + valueResult = valueChild; + typedResult = typedChild; + } + } + + return valueResult; + }; + } + } +} diff --git a/NEsper.Core/NEsper.Core/type/NumberSetParameter.cs b/NEsper.Core/NEsper.Core/type/NumberSetParameter.cs new file mode 100755 index 000000000..eb71311e2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/type/NumberSetParameter.cs @@ -0,0 +1,46 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.util; + +namespace com.espertech.esper.type +{ + /// + /// Interface to generate a set of integers from parameters that include ranges, lists and frequencies. + /// + + public interface NumberSetParameter : MetaDefItem + { + /// Returns true if all values between and including min and max are supplied by the parameter. + /// lower end of range + /// + /// upper end of range + /// + /// true if parameter specifies all int values between min and max, false if not + /// + + bool IsWildcard(int min, int max); + + /// Return a set of int values representing the value of the parameter for the given range. + /// lower end of range + /// + /// upper end of range + /// + /// set of integer + /// + + ICollection GetValuesInRange(int min, int max) ; + + Boolean ContainsPoint(int point); + + String Formatted(); + } +} diff --git a/NEsper.Core/NEsper.Core/type/OuterJoinType.cs b/NEsper.Core/NEsper.Core/type/OuterJoinType.cs new file mode 100755 index 000000000..4091de697 --- /dev/null +++ b/NEsper.Core/NEsper.Core/type/OuterJoinType.cs @@ -0,0 +1,46 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; + +namespace com.espertech.esper.type +{ + /// Enum for the type of outer join. + public enum OuterJoinType + { + /// Left outer join. + LEFT, + /// Right outer join. + RIGHT, + /// Full outer join. + FULL, + /// Inner join. + INNER + } + + public static class OuterJoinTypeExtensions + { + public static String GetText(this OuterJoinType joinType) + { + switch (joinType) + { + case OuterJoinType.LEFT: + return "left"; + case OuterJoinType.RIGHT: + return "right"; + case OuterJoinType.FULL: + return "full"; + case OuterJoinType.INNER: + return "inner"; + default: + throw new ArgumentException("Unknown joinType " + joinType); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/type/PrimitiveValue.cs b/NEsper.Core/NEsper.Core/type/PrimitiveValue.cs new file mode 100755 index 000000000..857fb597d --- /dev/null +++ b/NEsper.Core/NEsper.Core/type/PrimitiveValue.cs @@ -0,0 +1,69 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +namespace com.espertech.esper.type +{ + /// + /// Classes implementing this interface are responsible for parsing, setting and getting + /// the value of the different basic data types that occur in an event expression. + /// + /// Placeholders represent all literal values in event expressions and set values in + /// prepared event expressions. + /// + /// + public interface PrimitiveValue + { + /// Returns a value object. + /// value object + /// + Object ValueObject { get; } + + /// Returns the type of primitive value this instance represents. + /// enum type of primitive + /// + PrimitiveValueType Type { get; } + + /// Set a bool value. + bool _Boolean { set; } + + /// Set a byte value. + byte _Byte { set; } + + /// Set an sbyte value. + sbyte _SByte { set; } + + /// Set a float value. + float _Float { set; } + + /// Set an int value. + int _Int { set; } + + /// Set a short value. + short _Short { set; } + + /// Set a string value. + String _String { set; } + + /// Parse the string literal value into the specific data type. + /// is the textual value to parse + /// + void Parse(String value); + + /// Parse the string literal values supplied in the array into the specific data type. + /// are the textual values to parse + void Parse(String[] values); + + /// Set a double value. + Double _Double { set; } + + /// Set a long value. + long _Long { set; } + } +} diff --git a/NEsper.Core/NEsper.Core/type/PrimitiveValueBase.cs b/NEsper.Core/NEsper.Core/type/PrimitiveValueBase.cs new file mode 100755 index 000000000..f5db47c22 --- /dev/null +++ b/NEsper.Core/NEsper.Core/type/PrimitiveValueBase.cs @@ -0,0 +1,138 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; + +namespace com.espertech.esper.type +{ + /// + /// Abstract class for literal values supplied in an event expression string and prepared expression values supplied + /// by set methods. + /// + [Serializable] + public abstract class PrimitiveValueBase : PrimitiveValue + { + /// + /// Set a bool value. + /// + /// + virtual public bool _Boolean + { + set { throw new NotSupportedException(); } + } + + /// + /// Set a byte value. + /// + /// + virtual public byte _Byte + { + set { throw new NotSupportedException(); } + } + + /// + /// Sets the sbyte value. + /// + virtual public sbyte _SByte + { + set { throw new NotSupportedException(); } + } + + /// + /// Set a float value. + /// + /// + virtual public float _Float + { + set { throw new NotSupportedException(); } + } + + /// + /// Set an int value. + /// + /// + virtual public int _Int + { + set { throw new NotSupportedException(); } + } + + /// + /// Set a short value. + /// + /// + virtual public short _Short + { + set { throw new NotSupportedException(); } + } + + /// + /// Set a string value. + /// + /// + virtual public String _String + { + set { throw new NotSupportedException(); } + } + + /// + /// Set a double value. + /// + /// + virtual public Double _Double + { + set { throw new NotSupportedException(); } + } + + /// + /// Set a long value. + /// + /// + virtual public long _Long + { + set { throw new NotSupportedException(); } + } + + /// + /// Returns the type of primitive value this instance represents. + /// + /// + /// enum type of primitive + /// + + public abstract PrimitiveValueType Type { get;} + + /// + /// Returns a value object. + /// + /// + /// value object + /// + + public abstract Object ValueObject { get;} + + /// + /// Parse the string literal values supplied in the array into the + /// specific data type. + /// + /// are the textual values to parse + + public virtual void Parse(String[] values) + { + Parse(values[0]); + } + + /// + /// Parse the string literal value supplied into the specific + /// the specific data type. + /// + /// + + public abstract void Parse(String param1); + } +} diff --git a/NEsper.Core/NEsper.Core/type/PrimitiveValueFactory.cs b/NEsper.Core/NEsper.Core/type/PrimitiveValueFactory.cs new file mode 100755 index 000000000..239d17138 --- /dev/null +++ b/NEsper.Core/NEsper.Core/type/PrimitiveValueFactory.cs @@ -0,0 +1,75 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; + +namespace com.espertech.esper.type +{ + /// + /// Factory class for PrimitiveValue for all fundamental types. + /// + + public sealed class PrimitiveValueFactory + { + /// + /// Create a placeholder instance for the primitive type passed in. + /// Returns null if the type passed in is not a primitive type. + /// + /// a fundamental type + /// + /// instance of placeholder representing the type, or null if not a primitive type + /// + + public static PrimitiveValue Create(Type type) + { + if ((type == typeof(bool)) || type == typeof(bool?)) + { + return new BoolValue(); + } + if ((type == typeof(byte)) || (type == typeof(byte?))) + { + return new ByteValue(); + } + if ((type == typeof(sbyte)) || (type == typeof(sbyte?))) + { + return new SByteValue(); + } + if ((type == typeof(decimal)) || (type == typeof(decimal?))) + { + return new DecimalValue(); + } + if ((type == typeof(double)) || (type == typeof(double?))) + { + return new DoubleValue(); + } + if ((type == typeof(float)) || (type == typeof(float?))) + { + return new FloatValue(); + } + if ((type == typeof(int)) || (type == typeof(int?))) + { + return new IntValue(); + } + if ((type == typeof(long)) || (type == typeof(long?))) + { + return new LongValue(); + } + if ((type == typeof(short)) || (type == typeof(short?))) + { + return new ShortValue(); + } + if (type == typeof(String)) + { + return new StringValue(); + } + + return null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/type/PrimitiveValueType.cs b/NEsper.Core/NEsper.Core/type/PrimitiveValueType.cs new file mode 100755 index 000000000..a946f27fc --- /dev/null +++ b/NEsper.Core/NEsper.Core/type/PrimitiveValueType.cs @@ -0,0 +1,75 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; + +namespace com.espertech.esper.type +{ + /// + /// Enumeration of types of primitive values. + /// + + [Serializable] + public class PrimitiveValueType + { + /// Byte. + public readonly static PrimitiveValueType BYTE = new PrimitiveValueType( "Byte" ) ; + + /// Short. + public readonly static PrimitiveValueType SHORT = new PrimitiveValueType( "Short" ); + + /// Integer. + public readonly static PrimitiveValueType INTEGER = new PrimitiveValueType( "Int" ) ; + + /// Long. + public readonly static PrimitiveValueType LONG = new PrimitiveValueType( "Long" ) ; + + /// Float. + public readonly static PrimitiveValueType FLOAT = new PrimitiveValueType( "Single" ); + + /// Double. + public readonly static PrimitiveValueType DOUBLE = new PrimitiveValueType( "Double" ) ; + + /// Double. + public readonly static PrimitiveValueType DECIMAL = new PrimitiveValueType("Decimal"); + + /// Boolean. + public readonly static PrimitiveValueType BOOL = new PrimitiveValueType( "Boolean" ) ; + + /// String. + public readonly static PrimitiveValueType STRING = new PrimitiveValueType( "String" ); + + private readonly String typeName; + + private PrimitiveValueType( String typeName ) + { + this.typeName = typeName; + } + + /// Returns the name of the type. + /// type name + /// + + public String TypeName + { + get { return typeName; } + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override String ToString() + { + return typeName; + } + } +} diff --git a/NEsper.Core/NEsper.Core/type/RangeParameter.cs b/NEsper.Core/NEsper.Core/type/RangeParameter.cs new file mode 100755 index 000000000..ac767fb51 --- /dev/null +++ b/NEsper.Core/NEsper.Core/type/RangeParameter.cs @@ -0,0 +1,77 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +namespace com.espertech.esper.type +{ + /// + /// Represents a range of numbers as a parameter. + /// + public class RangeParameter : NumberSetParameter + { + /// + /// Initializes a new instance of the class. + /// + public RangeParameter() + { + } + + /// Ctor. + /// start of range + /// end of range + public RangeParameter(int low, int high) + { + Low = low; + High = high; + } + + /// Returns start of range. + /// start of range + public int Low { get; set; } + + /// Returns end of range. + /// end of range + public int High { get; set; } + + public bool IsWildcard(int min, int max) + { + if ((min >= Low) && (max <= High)) + { + return true; + } + return false; + } + + public ICollection GetValuesInRange(int min, int max) + { + ICollection values = new HashSet(); + + int start = (min > Low) ? min : Low; + int end = (max > High) ? High : max; + + while (start <= end) + { + values.Add(start); + start++; + } + + return values; + } + + public bool ContainsPoint(int point) + { + return (Low <= point && point <= High); + } + + public string Formatted() + { + return Low + "-" + High; + } + } +} diff --git a/NEsper.Core/NEsper.Core/type/RelationalOpEnum.cs b/NEsper.Core/NEsper.Core/type/RelationalOpEnum.cs new file mode 100755 index 000000000..ba856c20b --- /dev/null +++ b/NEsper.Core/NEsper.Core/type/RelationalOpEnum.cs @@ -0,0 +1,982 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; +using System.Numerics; + +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.util; + +namespace com.espertech.esper.type +{ + using Computer = Func; + + /// + /// Enum representing relational types of operation. + /// + + public enum RelationalOpEnum + { + /// Greater then. + GT, + /// Greater equals. + GE, + /// Lesser then. + LT, + /// Lesser equals. + LE + } + + public static class RelationalOpEnumExtensions + { + public static string GetExpressionText(this RelationalOpEnum value) + { + switch(value) + { + case RelationalOpEnum.GT: + return ">"; + case RelationalOpEnum.GE: + return ">="; + case RelationalOpEnum.LT: + return "<"; + case RelationalOpEnum.LE: + return "<="; + } + + throw new ArgumentException("invalid value", "value"); + } + + public static int GetOrdinal(this RelationalOpEnum value) + { + return (int) value; + } + + private static readonly IDictionary Computers; + + /// + /// Reverses this instance. + /// + /// + public static RelationalOpEnum Reversed(this RelationalOpEnum value) + { + switch(value) + { + case RelationalOpEnum.GT: + return RelationalOpEnum.LT; + case RelationalOpEnum.GE: + return RelationalOpEnum.LE; + case RelationalOpEnum.LT: + return RelationalOpEnum.GT; + case RelationalOpEnum.LE: + return RelationalOpEnum.GE; + } + + throw new ArgumentException("invalid value", "value"); + } + + /// + /// Parses the operator and returns an enum for the operator. + /// + /// operand to parse + /// enum representing relational operation + public static RelationalOpEnum Parse(string op) + { + switch (op) + { + case "<": + return RelationalOpEnum.LT; + case ">": + return RelationalOpEnum.GT; + case ">=": + case "=>": + return RelationalOpEnum.GE; + case "<=": + case "=<": + return RelationalOpEnum.LE; + } + + throw new ArgumentException("Invalid relational operator '" + op + "'"); + } + + static RelationalOpEnumExtensions() + { + Computers = new Dictionary(); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(string), RelationalOpEnum.GT }), GTStringComputer); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(string), RelationalOpEnum.GE }), GEStringComputer); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(string), RelationalOpEnum.LT }), LTStringComputer); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(string), RelationalOpEnum.LE }), LEStringComputer); + + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(DateTime), RelationalOpEnum.GT }), GTDateTimeComputer); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(DateTime), RelationalOpEnum.GE }), GEDateTimeComputer); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(DateTime), RelationalOpEnum.LT }), LTDateTimeComputer); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(DateTime), RelationalOpEnum.LE }), LEDateTimeComputer); + + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(DateTimeOffset), RelationalOpEnum.GT }), GTDateTimeOffsetComputer); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(DateTimeOffset), RelationalOpEnum.GE }), GEDateTimeOffsetComputer); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(DateTimeOffset), RelationalOpEnum.LT }), LTDateTimeOffsetComputer); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(DateTimeOffset), RelationalOpEnum.LE }), LEDateTimeOffsetComputer); + + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(short), RelationalOpEnum.GT }), GTInt16Computer); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(short), RelationalOpEnum.GE }), GEInt16Computer); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(short), RelationalOpEnum.LT }), LTInt16Computer); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(short), RelationalOpEnum.LE }), LEInt16Computer); + + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(int), RelationalOpEnum.GT }), GTInt32Computer); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(int), RelationalOpEnum.GE }), GEInt32Computer); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(int), RelationalOpEnum.LT }), LTInt32Computer); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(int), RelationalOpEnum.LE }), LEInt32Computer); + + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(ushort), RelationalOpEnum.GT }), GTUInt16Computer); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(ushort), RelationalOpEnum.GE }), GEUInt16Computer); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(ushort), RelationalOpEnum.LT }), LTUInt16Computer); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(ushort), RelationalOpEnum.LE }), LEUInt16Computer); + + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(uint), RelationalOpEnum.GT }), GTUInt32Computer); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(uint), RelationalOpEnum.GE }), GEUInt32Computer); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(uint), RelationalOpEnum.LT }), LTUInt32Computer); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(uint), RelationalOpEnum.LE }), LEUInt32Computer); + + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(long), RelationalOpEnum.GT }), GTLongComputer); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(long), RelationalOpEnum.GE }), GELongComputer); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(long), RelationalOpEnum.LT }), LTLongComputer); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(long), RelationalOpEnum.LE }), LELongComputer); + + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(ulong), RelationalOpEnum.GT }), GTULongComputer); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(ulong), RelationalOpEnum.GE }), GEULongComputer); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(ulong), RelationalOpEnum.LT }), LTULongComputer); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(ulong), RelationalOpEnum.LE }), LEULongComputer); + + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(float), RelationalOpEnum.GT }), GTSingleComputer); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(float), RelationalOpEnum.GE }), GESingleComputer); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(float), RelationalOpEnum.LT }), LTSingleComputer); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(float), RelationalOpEnum.LE }), LESingleComputer); + + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(double), RelationalOpEnum.GT }), GTDoubleComputer); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(double), RelationalOpEnum.GE }), GEDoubleComputer); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(double), RelationalOpEnum.LT }), LTDoubleComputer); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(double), RelationalOpEnum.LE }), LEDoubleComputer); + + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(decimal), RelationalOpEnum.GT }), GTDecimalComputer); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(decimal), RelationalOpEnum.GE }), GEDecimalComputer); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(decimal), RelationalOpEnum.LT }), LTDecimalComputer); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(decimal), RelationalOpEnum.LE }), LEDecimalComputer); + + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(BigInteger), RelationalOpEnum.GT }), GTBigIntegerComputer); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(BigInteger), RelationalOpEnum.GE }), GEBigIntegerComputer); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(BigInteger), RelationalOpEnum.LT }), LTBigIntegerComputer); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(BigInteger), RelationalOpEnum.LE }), LEBigIntegerComputer); + + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(IComparable), RelationalOpEnum.GT }), GTComparableComputer); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(IComparable), RelationalOpEnum.GE }), GEComparableComputer); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(IComparable), RelationalOpEnum.LT }), LTComparableComputer); + Computers.Add(new MultiKeyUntyped(new Object[] { typeof(IComparable), RelationalOpEnum.LE }), LEComparableComputer); + } + + /// + /// Returns the computer to use for the relational operation based on the + /// coercion type. + /// + /// The value. + /// is the object type + /// The type one. + /// The type two. + /// + /// computer for performing the relational op + /// + + public static Computer GetComputer(this RelationalOpEnum value, Type coercedType, Type typeOne, Type typeTwo) + { + var t = Nullable.GetUnderlyingType(coercedType); + if (t != null) { + coercedType = t; + } + + var key = new MultiKeyUntyped(new Object[] { coercedType, value }); + var computer = Computers.Get(key, null); + if (computer == null) + { + if (typeOne.IsComparable() && typeTwo.IsComparable()) + { + key = new MultiKeyUntyped(new object[]{ typeof(IComparable), value }); + computer = Computers.Get(key, null); + if (computer != null) + { + return computer; + } + } + + throw new ArgumentException("Unsupported type for relational op compare, type " + coercedType); + } + + return computer; + } + + #region String + /// + /// Greater than string computer. + /// + /// The obj one. + /// The obj two. + /// + public static bool GTStringComputer(Object objOne, Object objTwo) + { + var s1 = (String)objOne; + var s2 = (String)objTwo; + int result = s1.CompareTo(s2); + return result > 0; + } + + /// + /// Greater-than or equal to string computer. + /// + /// The obj one. + /// The obj two. + /// + public static bool GEStringComputer(Object objOne, Object objTwo) + { + var s1 = (String)objOne; + var s2 = (String)objTwo; + return s1.CompareTo(s2) >= 0; + } + + /// + /// Less-than or equal to string computer. + /// + /// The obj one. + /// The obj two. + /// + public static bool LEStringComputer(Object objOne, Object objTwo) + { + var s1 = (String)objOne; + var s2 = (String)objTwo; + return s1.CompareTo(s2) <= 0; + } + + /// + /// Less-than string computer. + /// + /// The obj one. + /// The obj two. + /// + public static bool LTStringComputer(Object objOne, Object objTwo) + { + var s1 = (String)objOne; + var s2 = (String)objTwo; + return s1.CompareTo(s2) < 0; + } + #endregion + + #region Int16 + /// + /// Greater-than int16 computer. + /// + /// The obj one. + /// The obj two. + /// + public static bool GTInt16Computer(Object objOne, Object objTwo) + { + Int16 s1 = Convert.ToInt16(objOne); + Int16 s2 = Convert.ToInt16(objTwo); + return s1 > s2; + } + + /// + /// Greater-than or equal to int16 computer. + /// + /// The obj one. + /// The obj two. + /// + public static bool GEInt16Computer(Object objOne, Object objTwo) + { + Int16 s1 = Convert.ToInt16(objOne); + Int16 s2 = Convert.ToInt16(objTwo); + return s1 >= s2; + } + + /// + /// Less-than int16 computer. + /// + /// The obj one. + /// The obj two. + /// + public static bool LTInt16Computer(Object objOne, Object objTwo) + { + Int16 s1 = Convert.ToInt16(objOne); + Int16 s2 = Convert.ToInt16(objTwo); + return s1 < s2; + } + + /// + /// Less-than or equal to int16 computer. + /// + /// The obj one. + /// The obj two. + /// + public static bool LEInt16Computer(Object objOne, Object objTwo) + { + Int16 s1 = Convert.ToInt16(objOne); + Int16 s2 = Convert.ToInt16(objTwo); + return s1 <= s2; + } + #endregion + + #region UInt16 + /// + /// Greater-than int16 computer. + /// + /// The obj one. + /// The obj two. + /// + public static bool GTUInt16Computer(Object objOne, Object objTwo) + { + UInt16 s1 = Convert.ToUInt16(objOne); + UInt16 s2 = Convert.ToUInt16(objTwo); + return s1 > s2; + } + + /// + /// Greater-than or equal to int16 computer. + /// + /// The obj one. + /// The obj two. + /// + public static bool GEUInt16Computer(Object objOne, Object objTwo) + { + UInt16 s1 = Convert.ToUInt16(objOne); + UInt16 s2 = Convert.ToUInt16(objTwo); + return s1 >= s2; + } + + /// + /// Less-than int16 computer. + /// + /// The obj one. + /// The obj two. + /// + public static bool LTUInt16Computer(Object objOne, Object objTwo) + { + UInt16 s1 = Convert.ToUInt16(objOne); + UInt16 s2 = Convert.ToUInt16(objTwo); + return s1 < s2; + } + + /// + /// Less-than or equal to int16 computer. + /// + /// The obj one. + /// The obj two. + /// + public static bool LEUInt16Computer(Object objOne, Object objTwo) + { + UInt16 s1 = Convert.ToUInt16(objOne); + UInt16 s2 = Convert.ToUInt16(objTwo); + return s1 <= s2; + } + #endregion + + #region Int32 + /// + /// Greater-than int computer. + /// + /// The obj one. + /// The obj two. + /// + public static bool GTInt32Computer(Object objOne, Object objTwo) + { + Int32 s1 = Convert.ToInt32(objOne); + Int32 s2 = Convert.ToInt32(objTwo); + return s1 > s2; + } + + /// + /// Greater-than or equal to int computer. + /// + /// The obj one. + /// The obj two. + /// + public static bool GEInt32Computer(Object objOne, Object objTwo) + { + Int32 s1 = Convert.ToInt32(objOne); + Int32 s2 = Convert.ToInt32(objTwo); + return s1 >= s2; + } + + /// + /// Less-than int computer. + /// + /// The obj one. + /// The obj two. + /// + public static bool LTInt32Computer(Object objOne, Object objTwo) + { + Int32 s1 = Convert.ToInt32(objOne); + Int32 s2 = Convert.ToInt32(objTwo); + return s1 < s2; + } + + /// + /// Less-than or equal to int computer. + /// + /// The obj one. + /// The obj two. + /// + public static bool LEInt32Computer(Object objOne, Object objTwo) + { + Int32 s1 = Convert.ToInt32(objOne); + Int32 s2 = Convert.ToInt32(objTwo); + return s1 <= s2; + } + #endregion + + #region UInt32 + /// + /// Greater-than int computer. + /// + /// The obj one. + /// The obj two. + /// + public static bool GTUInt32Computer(Object objOne, Object objTwo) + { + UInt32 s1 = Convert.ToUInt32(objOne); + UInt32 s2 = Convert.ToUInt32(objTwo); + return s1 > s2; + } + + /// + /// Greater-than or equal to int computer. + /// + /// The obj one. + /// The obj two. + /// + public static bool GEUInt32Computer(Object objOne, Object objTwo) + { + UInt32 s1 = Convert.ToUInt32(objOne); + UInt32 s2 = Convert.ToUInt32(objTwo); + return s1 >= s2; + } + + /// + /// Less-than int computer. + /// + /// The obj one. + /// The obj two. + /// + public static bool LTUInt32Computer(Object objOne, Object objTwo) + { + UInt32 s1 = Convert.ToUInt32(objOne); + UInt32 s2 = Convert.ToUInt32(objTwo); + return s1 < s2; + } + + /// + /// Less-than or equal to int computer. + /// + /// The obj one. + /// The obj two. + /// + public static bool LEUInt32Computer(Object objOne, Object objTwo) + { + UInt32 s1 = Convert.ToUInt32(objOne); + UInt32 s2 = Convert.ToUInt32(objTwo); + return s1 <= s2; + } + #endregion + + #region Int64 + /// + /// Greater-than long computer. + /// + /// The obj one. + /// The obj two. + /// + public static bool GTLongComputer(Object objOne, Object objTwo) + { + Int64 s1 = Convert.ToInt64(objOne); + Int64 s2 = Convert.ToInt64(objTwo); + return s1 > s2; + } + + /// + /// Greater-than or equal to long computer. + /// + /// The obj one. + /// The obj two. + /// + public static bool GELongComputer(Object objOne, Object objTwo) + { + Int64 s1 = Convert.ToInt64(objOne); + Int64 s2 = Convert.ToInt64(objTwo); + return s1 >= s2; + } + + /// + /// Less-than long computer. + /// + /// The obj one. + /// The obj two. + /// + public static bool LTLongComputer(Object objOne, Object objTwo) + { + Int64 s1 = Convert.ToInt64(objOne); + Int64 s2 = Convert.ToInt64(objTwo); + return s1 < s2; + } + + /// + /// Less-than or equal to long computer. + /// + /// The obj one. + /// The obj two. + /// + public static bool LELongComputer(Object objOne, Object objTwo) + { + Int64 s1 = Convert.ToInt64(objOne); + Int64 s2 = Convert.ToInt64(objTwo); + return s1 <= s2; + } + #endregion + + #region UInt64 + /// + /// Greater-than unsigned long computer. + /// + /// The obj one. + /// The obj two. + /// + public static bool GTULongComputer(Object objOne, Object objTwo) + { + UInt64 s1 = Convert.ToUInt64(objOne); + UInt64 s2 = Convert.ToUInt64(objTwo); + return s1 > s2; + } + + /// + /// Greater-than or equal to unsigned long computer. + /// + /// The obj one. + /// The obj two. + /// + public static bool GEULongComputer(Object objOne, Object objTwo) + { + UInt64 s1 = Convert.ToUInt64(objOne); + UInt64 s2 = Convert.ToUInt64(objTwo); + return s1 >= s2; + } + + /// + /// Less-than unsigned long computer. + /// + /// The obj one. + /// The obj two. + /// + public static bool LTULongComputer(Object objOne, Object objTwo) + { + UInt64 s1 = Convert.ToUInt64(objOne); + UInt64 s2 = Convert.ToUInt64(objTwo); + return s1 < s2; + } + + /// + /// Less-than or equal to unsigned long computer. + /// + /// The obj one. + /// The obj two. + /// + public static bool LEULongComputer(Object objOne, Object objTwo) + { + UInt64 s1 = Convert.ToUInt64(objOne); + UInt64 s2 = Convert.ToUInt64(objTwo); + return s1 <= s2; + } + #endregion + + #region Float + /// + /// Greater-than float computer. + /// + /// The obj one. + /// The obj two. + /// + public static bool GTSingleComputer(Object objOne, Object objTwo) + { + float s1 = Convert.ToSingle(objOne); + float s2 = Convert.ToSingle(objTwo); + return s1 > s2; + } + + /// + /// Greater-than or equal to float computer. + /// + /// The obj one. + /// The obj two. + /// + public static bool GESingleComputer(Object objOne, Object objTwo) + { + float s1 = Convert.ToSingle(objOne); + float s2 = Convert.ToSingle(objTwo); + return s1 >= s2; + } + + /// + /// Less-than float computer. + /// + /// The obj one. + /// The obj two. + /// + public static bool LTSingleComputer(Object objOne, Object objTwo) + { + float s1 = Convert.ToSingle(objOne); + float s2 = Convert.ToSingle(objTwo); + return s1 < s2; + } + + /// + /// Less-than or equal to float computer. + /// + /// The obj one. + /// The obj two. + /// + public static bool LESingleComputer(Object objOne, Object objTwo) + { + float s1 = Convert.ToSingle(objOne); + float s2 = Convert.ToSingle(objTwo); + return s1 <= s2; + } + #endregion + + #region Double + /// + /// Greater-than double computer. + /// + /// The obj one. + /// The obj two. + /// + public static bool GTDoubleComputer(Object objOne, Object objTwo) + { + double s1 = Convert.ToDouble(objOne); + double s2 = Convert.ToDouble(objTwo); + return s1 > s2; + } + + /// + /// Greater-than or equal to double computer. + /// + /// The obj one. + /// The obj two. + /// + public static bool GEDoubleComputer(Object objOne, Object objTwo) + { + double s1 = Convert.ToDouble(objOne); + double s2 = Convert.ToDouble(objTwo); + return s1 >= s2; + } + + /// + /// Less-than double computer. + /// + /// The obj one. + /// The obj two. + /// + public static bool LTDoubleComputer(Object objOne, Object objTwo) + { + double s1 = Convert.ToDouble(objOne); + double s2 = Convert.ToDouble(objTwo); + return s1 < s2; + } + + /// + /// Less-than or equal to double computer. + /// + /// The obj one. + /// The obj two. + /// + public static bool LEDoubleComputer(Object objOne, Object objTwo) + { + double s1 = Convert.ToDouble(objOne); + double s2 = Convert.ToDouble(objTwo); + return s1 <= s2; + } + #endregion + + #region Decimal + /// + /// Greater-than decimal computer. + /// + /// The obj one. + /// The obj two. + /// + public static bool GTDecimalComputer(Object objOne, Object objTwo) + { + decimal s1 = Convert.ToDecimal(objOne); + decimal s2 = Convert.ToDecimal(objTwo); + return s1 > s2; + } + + /// + /// Greater-than or equal to decimal computer. + /// + /// The obj one. + /// The obj two. + /// + public static bool GEDecimalComputer(Object objOne, Object objTwo) + { + decimal s1 = Convert.ToDecimal(objOne); + decimal s2 = Convert.ToDecimal(objTwo); + return s1 >= s2; + } + + /// + /// Less-than decimal computer. + /// + /// The obj one. + /// The obj two. + /// + public static bool LTDecimalComputer(Object objOne, Object objTwo) + { + decimal s1 = Convert.ToDecimal(objOne); + decimal s2 = Convert.ToDecimal(objTwo); + return s1 < s2; + } + + /// + /// Less-than or equal to decimal computer. + /// + /// The obj one. + /// The obj two. + /// + public static bool LEDecimalComputer(Object objOne, Object objTwo) + { + decimal s1 = Convert.ToDecimal(objOne); + decimal s2 = Convert.ToDecimal(objTwo); + return s1 <= s2; + } + #endregion + + #region DateTime + /// + /// Greater-than datetime computer. + /// + /// The obj one. + /// The obj two. + /// + public static bool GTDateTimeComputer(Object objOne, Object objTwo) + { + DateTime s1 = Convert.ToDateTime(objOne); + DateTime s2 = Convert.ToDateTime(objTwo); + return s1 > s2; + } + + /// + /// Greater-than or equal to datetime computer. + /// + /// The obj one. + /// The obj two. + /// + public static bool GEDateTimeComputer(Object objOne, Object objTwo) + { + DateTime s1 = Convert.ToDateTime(objOne); + DateTime s2 = Convert.ToDateTime(objTwo); + return s1 >= s2; + } + + /// + /// Less-than datetime computer. + /// + /// The obj one. + /// The obj two. + /// + public static bool LTDateTimeComputer(Object objOne, Object objTwo) + { + DateTime s1 = Convert.ToDateTime(objOne); + DateTime s2 = Convert.ToDateTime(objTwo); + return s1 < s2; + } + + /// + /// Less-than or equal to datetime computer. + /// + /// The obj one. + /// The obj two. + /// + public static bool LEDateTimeComputer(Object objOne, Object objTwo) + { + DateTime s1 = Convert.ToDateTime(objOne); + DateTime s2 = Convert.ToDateTime(objTwo); + return s1 <= s2; + } + #endregion + + #region DateTimeOffset + /// + /// Greater-than datetime offset computer. + /// + /// The obj one. + /// The obj two. + /// + public static bool GTDateTimeOffsetComputer(Object objOne, Object objTwo) + { + DateTimeOffset s1 = objOne.AsDateTimeOffset(); + DateTimeOffset s2 = objTwo.AsDateTimeOffset(); + return s1 > s2; + } + + /// + /// Greater-than or equal to datetime offset computer. + /// + /// The obj one. + /// The obj two. + /// + public static bool GEDateTimeOffsetComputer(Object objOne, Object objTwo) + { + DateTimeOffset s1 = objOne.AsDateTimeOffset(); + DateTimeOffset s2 = objTwo.AsDateTimeOffset(); + return s1 >= s2; + } + + /// + /// Less-than datetime offset computer. + /// + /// The obj one. + /// The obj two. + /// + public static bool LTDateTimeOffsetComputer(Object objOne, Object objTwo) + { + DateTimeOffset s1 = objOne.AsDateTimeOffset(); + DateTimeOffset s2 = objTwo.AsDateTimeOffset(); + return s1 < s2; + } + + /// + /// Less-than or equal to datetime offset computer. + /// + /// The obj one. + /// The obj two. + /// + public static bool LEDateTimeOffsetComputer(Object objOne, Object objTwo) + { + DateTimeOffset s1 = objOne.AsDateTimeOffset(); + DateTimeOffset s2 = objTwo.AsDateTimeOffset(); + return s1 <= s2; + } + #endregion + + #region BigInteger + /// + /// Greater than BigInteger computer. + /// + /// The obj one. + /// The obj two. + /// + public static bool GTBigIntegerComputer(Object objOne, Object objTwo) + { + var s1 = objOne.AsBigInteger(); + var s2 = objTwo.AsBigInteger(); + return s1 > s2; + } + + /// + /// Greater-than or equal to BigInteger computer. + /// + /// The obj one. + /// The obj two. + /// + public static bool GEBigIntegerComputer(Object objOne, Object objTwo) + { + var s1 = objOne.AsBigInteger(); + var s2 = objTwo.AsBigInteger(); + return s1 >= s2; + } + + /// + /// Less-than or equal to BigInteger computer. + /// + /// The obj one. + /// The obj two. + /// + public static bool LEBigIntegerComputer(Object objOne, Object objTwo) + { + var s1 = objOne.AsBigInteger(); + var s2 = objTwo.AsBigInteger(); + return s1 <= s2; + } + + /// + /// Less-than BigInteger computer. + /// + /// The obj one. + /// The obj two. + /// + public static bool LTBigIntegerComputer(Object objOne, Object objTwo) + { + var s1 = objOne.AsBigInteger(); + var s2 = objTwo.AsBigInteger(); + return s1 < s2; + } + #endregion + + #region IComparable + /// + /// Greater than IComparable computer. + /// + /// The obj one. + /// The obj two. + /// + public static bool GTComparableComputer(Object objOne, Object objTwo) + { + var s1 = (IComparable)objOne; + var s2 = (IComparable)objTwo; + int result = s1.CompareTo(s2); + return result > 0; + } + + /// + /// Greater-than or equal to IComparable computer. + /// + /// The obj one. + /// The obj two. + /// + public static bool GEComparableComputer(Object objOne, Object objTwo) + { + var s1 = (IComparable)objOne; + var s2 = (IComparable)objTwo; + return s1.CompareTo(s2) >= 0; + } + + /// + /// Less-than or equal to IComparable computer. + /// + /// The obj one. + /// The obj two. + /// + public static bool LEComparableComputer(Object objOne, Object objTwo) + { + var s1 = (IComparable)objOne; + var s2 = (IComparable)objTwo; + return s1.CompareTo(s2) <= 0; + } + + /// + /// Less-than IComparable computer. + /// + /// The obj one. + /// The obj two. + /// + public static bool LTComparableComputer(Object objOne, Object objTwo) + { + var s1 = (IComparable)objOne; + var s2 = (IComparable)objTwo; + return s1.CompareTo(s2) < 0; + } + #endregion + + } +} diff --git a/NEsper.Core/NEsper.Core/type/SByteValue.cs b/NEsper.Core/NEsper.Core/type/SByteValue.cs new file mode 100755 index 000000000..ea9b61ef8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/type/SByteValue.cs @@ -0,0 +1,99 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Globalization; + +namespace com.espertech.esper.type +{ + /// + /// Placeholder for a sbyte value in an event expression. + /// + + [Serializable] + public sealed class SByteValue : PrimitiveValueBase + { + /// + /// Returns the type of primitive value this instance represents. + /// + /// + /// enum type of primitive + /// + override public PrimitiveValueType Type + { + get { return PrimitiveValueType.BYTE; } + } + + /// + /// Returns a value object. + /// + /// + /// value object + /// + override public Object ValueObject + { + get { return _byteValue; } + } + + /// + /// Set a byte value. + /// + /// + override public sbyte _SByte + { + set { _byteValue = value; } + } + + private sbyte? _byteValue; + + /// Parses a string value as a byte. + /// to parse + /// byte value + public static sbyte ParseString(String value) + { + if (value.ToUpper().StartsWith("0X")) + { + return SByte.Parse(value.Substring(2), NumberStyles.HexNumber); + } + + return SByte.Parse(value); + } + + /// + /// Parse the string literal value into the specific data type. + /// + /// is the textual value to parse + public override void Parse(String value) + { + if (value.ToUpper().StartsWith("0X")) + { + _byteValue = SByte.Parse(value.Substring(2), NumberStyles.HexNumber); + } + else + { + _byteValue = SByte.Parse(value); + } + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override String ToString() + { + if (_byteValue == null) + { + return "null"; + } + return _byteValue.ToString(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/type/ScheduleUnit.cs b/NEsper.Core/NEsper.Core/type/ScheduleUnit.cs new file mode 100755 index 000000000..4660aab9a --- /dev/null +++ b/NEsper.Core/NEsper.Core/type/ScheduleUnit.cs @@ -0,0 +1,74 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; + +namespace com.espertech.esper.type +{ + /// + /// Enumeration of units in a specification of schedule, which contains elements for each of the following units: + /// minute, hour, day of month, month, day of week and seconds. + /// Notice: value ranges are the same as the "crontab" standard values. + /// + [Serializable] + public class ScheduleUnit + { + /// Second. + public static readonly ScheduleUnit SECONDS = new ScheduleUnit(0, 59); + /// Minute. + public static readonly ScheduleUnit MINUTES = new ScheduleUnit(0, 59); + /// Hour. + public static readonly ScheduleUnit HOURS = new ScheduleUnit(0, 23); + /// Day of month. + public static readonly ScheduleUnit DAYS_OF_MONTH = new ScheduleUnit(1, 31); + /// Month. + public static readonly ScheduleUnit MONTHS = new ScheduleUnit(1, 12); + /// Day of week. + public static readonly ScheduleUnit DAYS_OF_WEEK = new ScheduleUnit(0, 6); + + /// + /// Available constant values from this class + /// + + public static readonly ScheduleUnit[] Values = new ScheduleUnit[] + { + SECONDS, + MINUTES, + HOURS, + DAYS_OF_MONTH, + MONTHS, + DAYS_OF_WEEK + }; + + private readonly int min_; + private readonly int max_; + + ScheduleUnit(int min, int max) + { + this.min_ = min; + this.max_ = max; + } + + /// Returns minimum valid value for the unit. + /// minimum unit value + /// + public int Min() + { + return min_; + } + + /// Returns minimum valid value for the unit. + /// maximum unit value + /// + public int Max() + { + return max_; + } + } +} diff --git a/NEsper.Core/NEsper.Core/type/ShortValue.cs b/NEsper.Core/NEsper.Core/type/ShortValue.cs new file mode 100755 index 000000000..1d40712bb --- /dev/null +++ b/NEsper.Core/NEsper.Core/type/ShortValue.cs @@ -0,0 +1,86 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; + +namespace com.espertech.esper.type +{ + /// + /// Placeholder for a short-typed value in an event expression. + /// + + [Serializable] + public sealed class ShortValue : PrimitiveValueBase + { + /// + /// Returns the type of primitive value this instance represents. + /// + /// + /// enum type of primitive + /// + override public PrimitiveValueType Type + { + get { return PrimitiveValueType.SHORT; } + } + + /// + /// Returns a value object. + /// + /// + /// value object + /// + override public Object ValueObject + { + get { return shortValue; } + } + + /// + /// Set a short value. + /// + /// + override public short _Short + { + set { this.shortValue = value; } + } + + private Nullable shortValue; + + /// Parses a string value as a short. + /// to parse + /// short value + public static short ParseString(String value) + { + return Int16.Parse(value); + } + + /// + /// Parse the string literal value into the specific data type. + /// + /// is the textual value to parse + public override void Parse(String value) + { + shortValue = short.Parse(value); + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override String ToString() + { + if (shortValue == null) + { + return "null"; + } + return shortValue.ToString(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/type/StringPatternSet.cs b/NEsper.Core/NEsper.Core/type/StringPatternSet.cs new file mode 100755 index 000000000..c2ffc3176 --- /dev/null +++ b/NEsper.Core/NEsper.Core/type/StringPatternSet.cs @@ -0,0 +1,21 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.type +{ + /// Implementation match a string against a pattern. + public interface StringPatternSet + { + /// Returns true for a match, false for no-match. + /// value to match + /// match result + bool Match(String stringToMatch); + } +} diff --git a/NEsper.Core/NEsper.Core/type/StringPatternSetLike.cs b/NEsper.Core/NEsper.Core/type/StringPatternSetLike.cs new file mode 100755 index 000000000..2a64ce42b --- /dev/null +++ b/NEsper.Core/NEsper.Core/type/StringPatternSetLike.cs @@ -0,0 +1,80 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.util; + +namespace com.espertech.esper.type +{ + [Serializable] + public class StringPatternSetLike : StringPatternSet + { + private readonly String likeString; + private readonly LikeUtil likeUtil; + + /// Ctor. + /// pattern to match + public StringPatternSetLike(String likeString) + { + this.likeString = likeString; + likeUtil = new LikeUtil(likeString, '\\', false); + } + + /// Match the string returning true for a match, using SQL-like semantics. + /// string to match + /// true for match + public bool Match(String stringToMatch) + { + if (stringToMatch == null) + { + return false; + } + return likeUtil.Compare(stringToMatch); + } + + /// + /// Equalses the specified obj. + /// + /// The obj. + /// + public bool Equals(StringPatternSetLike obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + return Equals(obj.likeString, likeString); + } + + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// + /// true if the specified is equal to the current ; otherwise, false. + /// + /// The parameter is null. + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != typeof (StringPatternSetLike)) return false; + return Equals((StringPatternSetLike) obj); + } + + /// + /// Serves as a hash function for a particular type. + /// + /// + /// A hash code for the current . + /// + public override int GetHashCode() + { + return (likeString != null ? likeString.GetHashCode() : 0); + } + } +} diff --git a/NEsper.Core/NEsper.Core/type/StringPatternSetRegex.cs b/NEsper.Core/NEsper.Core/type/StringPatternSetRegex.cs new file mode 100755 index 000000000..4a14f2002 --- /dev/null +++ b/NEsper.Core/NEsper.Core/type/StringPatternSetRegex.cs @@ -0,0 +1,85 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Text.RegularExpressions; + +namespace com.espertech.esper.type +{ + /// Regular expression matcher. + [Serializable] + public class StringPatternSetRegex : StringPatternSet + { + private readonly Regex _pattern; + private readonly String _patternText; + + /// + /// Ctor. + /// + /// regex to match + public StringPatternSetRegex(String patternText) + { + _patternText = patternText; + _pattern = new Regex(patternText); + } + + #region StringPatternSet Members + + /// + /// Match the string returning true for a match, using regular expression semantics. + /// + /// string to match + /// true for match + public bool Match(String stringToMatch) + { + return _pattern.IsMatch(stringToMatch); + } + + #endregion + + /// + /// Equalses the specified obj. + /// + /// The obj. + /// + public bool Equals(StringPatternSetRegex obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + return Equals(obj._patternText, _patternText); + } + + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// + /// true if the specified is equal to the current ; otherwise, false. + /// + /// The parameter is null. + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != typeof (StringPatternSetRegex)) return false; + return Equals((StringPatternSetRegex) obj); + } + + /// + /// Serves as a hash function for a particular type. + /// + /// + /// A hash code for the current . + /// + public override int GetHashCode() + { + return (_patternText != null ? _patternText.GetHashCode() : 0); + } + } +} diff --git a/NEsper.Core/NEsper.Core/type/StringPatternSetUtil.cs b/NEsper.Core/NEsper.Core/type/StringPatternSetUtil.cs new file mode 100755 index 000000000..eec32103d --- /dev/null +++ b/NEsper.Core/NEsper.Core/type/StringPatternSetUtil.cs @@ -0,0 +1,62 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.collection; + +namespace com.espertech.esper.type +{ + /// + /// Pattern matching utility. + /// + public class StringPatternSetUtil + { + /// + /// Executes a seriers of include/exclude patterns against a match string, + /// returning the last pattern match result as bool in/out. + /// + /// the default value if there are no patterns or no matches change the value + /// to match against, true in the pair for include, false for exclude + /// to match + /// true for included, false for excluded + public static bool Evaluate(bool defaultValue, IEnumerable> patterns, String literal) + { + bool result = defaultValue; + + foreach (var item in patterns) + { + if (result) + { + if (!item.Second) + { + bool testResult = item.First.Match(literal); + if (testResult) + { + result = false; + } + } + } + else + { + if (item.Second) + { + bool testResult = item.First.Match(literal); + if (testResult) + { + result = true; + } + } + } + } + + return result; + } + } +} diff --git a/NEsper.Core/NEsper.Core/type/StringValue.cs b/NEsper.Core/NEsper.Core/type/StringValue.cs new file mode 100755 index 000000000..a5a56f439 --- /dev/null +++ b/NEsper.Core/NEsper.Core/type/StringValue.cs @@ -0,0 +1,158 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Globalization; +using System.Text; + +namespace com.espertech.esper.type +{ + /// + /// Placeholder for a String value in an event expression. + /// + + [Serializable] + public sealed class StringValue : PrimitiveValueBase + { + /// + /// Returns the type of primitive value this instance represents. + /// + /// + /// enum type of primitive + /// + override public PrimitiveValueType Type + { + get { return PrimitiveValueType.STRING; } + } + + /// + /// Returns a value object. + /// + /// + /// value object + /// + override public Object ValueObject + { + get { return stringValue; } + } + + /// + /// Set a string value. + /// + /// + override public String _String + { + set { this.stringValue = value; } + } + + private String stringValue; + + /// Constructor. + /// sets initial value + /// + public StringValue(String stringValue) + { + this.stringValue = stringValue; + } + + /// Constructor. + public StringValue() + { + } + + /// Parse the string array returning a string array. + /// string array + /// + /// typed array + /// + public static String[] ParseString(String[] values) + { + String[] result = new String[values.Length]; + for (int i = 0; i < result.Length; i++) + { + result[i] = ParseString(values[i]); + } + return result; + } + + /// + /// Parse the string literal value into the specific data type. + /// + /// is the textual value to parse + public override void Parse(String value) + { + stringValue = ParseString(value); + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override String ToString() + { + if (stringValue == null) + { + return "null"; + } + return stringValue; + } + + /// Parse the string literal consisting of text between double-quotes or single-quotes. + /// is the text wthin double or single quotes + /// + /// parsed value + /// + public static String ParseString(String value) + { + if ((value.StartsWith("\"")) & (value.EndsWith("\"")) || (value.StartsWith("'")) & (value.EndsWith("'"))) + { + if (value.Length > 1) + { + if (value.IndexOf('\\') != -1) + { + return Unescape(value.Substring(1, value.Length - 2)); + } + + return value.Substring(1, value.Length - 2); + } + } + + throw new ArgumentException("String value of '" + value + "' cannot be parsed"); + } + + private static String Unescape(String s) + { + var provider = System.Threading.Thread.CurrentThread.CurrentCulture; + + int i = 0, len = s.Length; + char c; + StringBuilder sb = new StringBuilder(len); + while (i < len) + { + c = s[i++]; + if (c == '\\') + { + if (i < len) + { + c = s[i++]; + if (c == 'u') + { + c = (char) Int16.Parse(s.Substring(i, 4), NumberStyles.HexNumber, provider); + i += 4; + } // add other cases here as desired... + } + } // fall through: \ escapes itself, quotes any character but u + sb.Append(c); + } + return sb.ToString(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/type/WildcardParameter.cs b/NEsper.Core/NEsper.Core/type/WildcardParameter.cs new file mode 100755 index 000000000..b39c81c06 --- /dev/null +++ b/NEsper.Core/NEsper.Core/type/WildcardParameter.cs @@ -0,0 +1,61 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.type +{ + /// + /// Represents a wildcard as a parameter. + /// + + [Serializable] + public class WildcardParameter : NumberSetParameter + { + /// + /// Returns true if all values between and including min and max are supplied by the parameter. + /// + /// lower end of range + /// upper end of range + /// + /// true if parameter specifies all int values between min and max, false if not + /// + public bool IsWildcard(int min, int max) + { + return true; + } + + /// + /// Return a set of int values representing the value of the parameter for the given range. + /// + /// lower end of range + /// upper end of range + /// set of integer + public ICollection GetValuesInRange(int min, int max) + { + ICollection result = new HashSet(); + for (int i = min; i <= max; i++) + { + result.Add(i); + } + return result; + } + + public bool ContainsPoint(int point) + { + return true; + } + + public String Formatted() + { + return "*"; + } + } +} diff --git a/NEsper.Core/NEsper.Core/util/AttributeUtil.cs b/NEsper.Core/NEsper.Core/util/AttributeUtil.cs new file mode 100755 index 000000000..a35f5b0e2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/AttributeUtil.cs @@ -0,0 +1,107 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.util +{ + public class AttributeUtil + { + public static bool IsListed(String list, String lookedForValue) + { + if (list == null) + { + return false; + } + + lookedForValue = lookedForValue.Trim().ToUpper(); + list = list.Trim().ToUpper(); + + if (list == lookedForValue) + { + return true; + } + + if (list.IndexOf('=') != -1) + { + var hintName = list.Substring(0, list.IndexOf('=')); + if (hintName.Trim().ToUpper() == lookedForValue) + { + return true; + } + } + + var items = list.Split(','); + foreach (var item in items) + { + var listItem = item.Trim().ToUpper(); + if (listItem == lookedForValue) + { + return true; + } + + if (listItem.IndexOf('=') != -1) + { + var listItemName = listItem.Substring(0, listItem.IndexOf('=')); + if (listItemName.Trim().ToUpper() == lookedForValue) + { + return true; + } + } + } + return false; + } + + public static String GetAssignedValue(String value, String enumValue) + { + + var valMixed = value.Trim(); + var val = valMixed.ToUpper(); + + if (val.IndexOf(",") == -1) + { + if (val.IndexOf('=') == -1) + { + return null; + } + + var hintName = val.Substring(0, val.IndexOf('=')); + if (!hintName.Equals(enumValue)) + { + return null; + } + return valMixed.Substring(val.IndexOf('=') + 1, val.Length); + } + + var hints = valMixed.Split(','); + foreach (var hint in hints) + { + var indexOfEquals = hint.IndexOf('='); + if (indexOfEquals == -1) + { + continue; + } + + val = hint.Substring(0, indexOfEquals).Trim().ToUpper(); + if (!val.Equals(enumValue)) + { + continue; + } + + var strValue = hint.Substring(indexOfEquals + 1).Trim(); + if (strValue.Length == 0) + { + return null; + } + return strValue; + } + + return null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/util/AuditCallback.cs b/NEsper.Core/NEsper.Core/util/AuditCallback.cs new file mode 100755 index 000000000..7224abb62 --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/AuditCallback.cs @@ -0,0 +1,12 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.util +{ + public delegate void AuditCallback(AuditContext auditContext); +} diff --git a/NEsper.Core/NEsper.Core/util/AuditContext.cs b/NEsper.Core/NEsper.Core/util/AuditContext.cs new file mode 100755 index 000000000..af0c0a955 --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/AuditContext.cs @@ -0,0 +1,68 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Text; + +using com.espertech.esper.client.annotation; + +namespace com.espertech.esper.util +{ + public class AuditContext { + + private readonly String _engineURI; + private readonly String _statementName; + private readonly AuditEnum _category; + private readonly String _message; + + public AuditContext(String engineURI, String statementName, AuditEnum category, String message) + { + _engineURI = engineURI; + _statementName = statementName; + _category = category; + _message = message; + } + + public string EngineURI + { + get { return _engineURI; } + } + + public string StatementName + { + get { return _statementName; } + } + + public AuditEnum Category + { + get { return _category; } + } + + public string Message + { + get { return _message; } + } + + public String Format() + { + return DefaultFormat(_statementName, _category, _message); + } + + public static String DefaultFormat(String statementName, AuditEnum category, String message) + { + StringBuilder buf = new StringBuilder(); + buf.Append("Statement "); + buf.Append(statementName); + buf.Append(" "); + buf.Append(category.GetPrettyPrintText()); + buf.Append(" "); + buf.Append(message); + return buf.ToString(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/util/AuditPath.cs b/NEsper.Core/NEsper.Core/util/AuditPath.cs new file mode 100755 index 000000000..2931c2e81 --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/AuditPath.cs @@ -0,0 +1,96 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.IO; + +using com.espertech.esper.client; +using com.espertech.esper.client.annotation; +using com.espertech.esper.compat.logging; +using com.espertech.esper.events; + +namespace com.espertech.esper.util +{ + /// + /// Global bool for enabling and disable audit path reporting. + /// + public class AuditPath + { + /// Logger destination for the query plan logging. + public static readonly string QUERYPLAN_LOG = "com.espertech.esper.queryplan"; + + /// Logger destination for the JDBC logging. + public static readonly string JDBC_LOG = "com.espertech.esper.jdbc"; + + /// Logger destination for the audit logging. + public static readonly string AUDIT_LOG = "com.espertech.esper.audit"; + + private static readonly ILog AUDIT_LOG_DESTINATION = LogManager.GetLogger(AuditPath.AUDIT_LOG); + + /// Public access. + public static bool IsAuditEnabled = false; + + private static volatile AuditCallback _auditCallback; + private static string _auditPattern; + + public static string AuditPattern + { + set { _auditPattern = value; } + } + + public static void AuditInsertInto(string engineURI, string statementName, EventBean theEvent) + { + AuditLog(engineURI, statementName, AuditEnum.INSERT, EventBeanUtility.Summarize(theEvent)); + } + + public static void AuditContextPartition( + string engineURI, + string statementName, + bool allocate, + int agentInstanceId) + { + var writer = new StringWriter(); + writer.Write(allocate ? "Allocate" : "Destroy"); + writer.Write(" cpid "); + writer.Write(agentInstanceId); + AuditLog(engineURI, statementName, AuditEnum.CONTEXTPARTITION, writer.ToString()); + } + + public static void AuditLog(string engineURI, string statementName, AuditEnum category, string message) + { + if (_auditPattern == null) + { + string text = AuditContext.DefaultFormat(statementName, category, message); + AUDIT_LOG_DESTINATION.Info(text); + } + else + { + string result = + _auditPattern.Replace("%s", statementName) + .Replace("%u", engineURI) + .Replace("%c", category.GetValue()) + .Replace("%m", message); + AUDIT_LOG_DESTINATION.Info(result); + } + if (_auditCallback != null) + { + _auditCallback.Invoke(new AuditContext(engineURI, statementName, category, message)); + } + } + + public static bool IsInfoEnabled + { + get { return AUDIT_LOG_DESTINATION.IsInfoEnabled || _auditCallback != null; } + } + + public static AuditCallback AuditCallback + { + get { return _auditCallback; } + set { _auditCallback = value; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/util/CoercerFactory.cs b/NEsper.Core/NEsper.Core/util/CoercerFactory.cs new file mode 100755 index 000000000..e3b5a60ac --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/CoercerFactory.cs @@ -0,0 +1,113 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; +using System.Numerics; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.util +{ + public sealed class CoercerFactory + { + private static readonly IDictionary CoercerTable; + + static CoercerFactory() + { + CoercerTable = new Dictionary(); + CoercerTable[typeof (decimal?)] = + (itemToCoerce => itemToCoerce.AsDecimal()); + CoercerTable[typeof (double?)] = + (itemToCoerce => itemToCoerce.AsDouble()); + CoercerTable[typeof (ulong?)] = + (itemToCoerce => Convert.ToUInt64(itemToCoerce)); + CoercerTable[typeof (long?)] = + (itemToCoerce => itemToCoerce.AsLong()); + CoercerTable[typeof (float?)] = + (itemToCoerce => itemToCoerce.AsFloat()); + CoercerTable[typeof (uint?)] = + (itemToCoerce => Convert.ToUInt32(itemToCoerce)); + CoercerTable[typeof (int?)] = + (itemToCoerce => itemToCoerce.AsInt()); + CoercerTable[typeof (ushort?)] = + (itemToCoerce => Convert.ToUInt16(itemToCoerce)); + CoercerTable[typeof (short?)] = + (itemToCoerce => itemToCoerce.AsShort()); + CoercerTable[typeof (byte?)] = + (itemToCoerce => Convert.ToByte(itemToCoerce)); + CoercerTable[typeof (sbyte?)] = + (itemToCoerce => Convert.ToSByte(itemToCoerce)); + CoercerTable[typeof (BigInteger?)] = + (itemToCoerce => itemToCoerce.AsBigInteger()); + CoercerTable[typeof (string)] = + (itemToCoerce => Convert.ToString(itemToCoerce)); + } + + /// + /// Gets the type coercer from any object to the target type. + /// + /// Type of the target. + /// + public static Coercer GetCoercer(Type targetType) + { + return GetCoercer(typeof (Object), targetType); + } + + /// + /// Gets the type coercer between two types. + /// + /// From type. + /// Boxed target type. + /// + public static Coercer GetCoercer(Type fromType, Type targetType) + { + targetType = targetType.GetBoxedType(); + + if (fromType.GetBoxedType() == targetType) + return NullCoercion; + + if (targetType == typeof(Object)) + return NullCoercion; + + Coercer coercer = CoercerTable.Get(targetType); + if (coercer != null) + return coercer; + + throw new ArgumentException("Cannot coerce to number subtype " + targetType.FullName); + } + + /// + /// CoerceIndex the given number to the given type. Allows coerce to lower resultion number. + /// Doesn't coerce to primitive types. + /// numToCoerce is the number to coerce to the given type + /// the result type to return + /// the itemToCoerce as a value in the given result type + /// + + public static Object CoerceBoxed(Object itemToCoerce, Type resultBoxedType) + { + Coercer coercer = GetCoercer(itemToCoerce.GetType(), resultBoxedType); + return coercer.Invoke(itemToCoerce); + } + + /// + /// Performs a null coercion. + /// + /// The item to coerce. + /// + public static Object NullCoercion(Object itemToCoerce) + { + return itemToCoerce; + } + } + + public delegate Object Coercer(Object itemToCoerce); +} diff --git a/NEsper.Core/NEsper.Core/util/CoercionException.cs b/NEsper.Core/NEsper.Core/util/CoercionException.cs new file mode 100755 index 000000000..4e198658a --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/CoercionException.cs @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.util +{ + /// + /// Exception to represent a Mismatch in types in an expression. + /// + [Serializable] + public class CoercionException:EPException + { + /// Ctor. + /// supplies the detailed description + /// + public CoercionException(String message):base(message) + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/util/CollectionUtil.cs b/NEsper.Core/NEsper.Core/util/CollectionUtil.cs new file mode 100755 index 000000000..b4f283544 --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/CollectionUtil.cs @@ -0,0 +1,543 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.util +{ + /// + /// Utility for handling collection or array tasks. + /// + public class CollectionUtil + { + public readonly static IEnumerator NULL_EVENT_ITERATOR = new NullEnumerator(); + public readonly static IEnumerable NULL_EVENT_ITERABLE = new ProxyEnumerable { ProcEnumerator = () => NULL_EVENT_ITERATOR }; + public readonly static ISet> EMPTY_ROW_SET = new HashSet>(); + public readonly static EventBean[] EVENTBEANARRAY_EMPTY = new EventBean[0]; + public readonly static ICollection SINGLE_NULL_ROW_EVENT_SET = new HashSet(); + public readonly static string[] EMPTY_STRING_ARRAY = new string[0]; + + public static readonly StopCallback STOP_CALLBACK_NONE; + + static CollectionUtil() + { + SINGLE_NULL_ROW_EVENT_SET.Add(null); + STOP_CALLBACK_NONE = new ProxyStopCallback(() => { }); + } + + public static IComparer GetComparator( + ExprEvaluator[] sortCriteriaEvaluators, + bool isSortUsingCollator, + bool[] isDescendingValues) + { + // determine string-type sorting + var hasStringTypes = false; + var stringTypes = new bool[sortCriteriaEvaluators.Length]; + + int count = 0; + foreach (ExprEvaluator node in sortCriteriaEvaluators) + { + if (node.ReturnType == typeof (string)) + { + hasStringTypes = true; + stringTypes[count] = true; + } + count++; + } + + if (sortCriteriaEvaluators.Length > 1) + { + if ((!hasStringTypes) || (!isSortUsingCollator)) + { + var comparatorMK = new MultiKeyComparator(isDescendingValues); + return new MultiKeyCastingComparator(comparatorMK); + } + else + { + var comparatorMk = new MultiKeyCollatingComparator(isDescendingValues, stringTypes); + return new MultiKeyCastingComparator(comparatorMk); + } + } + else + { + if ((!hasStringTypes) || (!isSortUsingCollator)) + { + return new ObjectComparator(isDescendingValues[0]); + } + else + { + return new ObjectCollatingComparator(isDescendingValues[0]); + } + } + } + + public static string ToString(ICollection stack, string delimiterChars) + { + if (stack.IsEmpty()) + { + return ""; + } + if (stack.Count == 1) + { + return Convert.ToString(stack.First()); + } + var writer = new StringWriter(); + var delimiter = ""; + foreach (int? item in stack) + { + writer.Write(delimiter); + writer.Write(Convert.ToString(item)); + delimiter = delimiterChars; + } + return writer.ToString(); + } + + public static object ArrayExpandAddElements(Array array, T[] elementsToAdd) + { + var length = array.Length; + var newLength = length + elementsToAdd.Length; + var componentType = array.GetType().GetElementType(); + var newArray = Array.CreateInstance(componentType, newLength); + + Array.Copy(array, 0, newArray, 0, length); + for (int i = 0; i < elementsToAdd.Length; i++) + { + newArray.SetValue(elementsToAdd[i], length + i); + } + return newArray; + } + + public static object ArrayExpandAddElements(object array, T[] elementsToAdd) + { + var cl = array.GetType(); + if (!cl.IsArray) return null; + return ArrayExpandAddElements((Array) array, elementsToAdd); + } + + public static object ArrayShrinkRemoveSingle(Array array, int index) + { + var length = array.Length; + var newLength = length - 1; + var componentType = array.GetType().GetElementType(); + var newArray = Array.CreateInstance(componentType, newLength); + + if (index > 0) + { + Array.Copy(array, 0, newArray, 0, index); + } + + if (index < newLength) + { + Array.Copy(array, index + 1, newArray, index, newLength - index); + } + + return newArray; + } + + public static object ArrayShrinkRemoveSingle(object array, int index) + { + Type cl = array.GetType(); + if (!cl.IsArray) return null; + return ArrayShrinkRemoveSingle((Array) array, index); + } + + public static object ArrayExpandAddElements(Array array, ICollection elementsToAdd) + { + var length = array.Length; + var newLength = length + elementsToAdd.Count; + var componentType = array.GetType().GetElementType(); + var newArray = Array.CreateInstance(componentType, newLength); + + Array.Copy(array, 0, newArray, 0, length); + int count = 0; + foreach (var element in elementsToAdd) { + newArray.SetValue(element, length + count); + count++; + } + return newArray; + } + + public static object ArrayExpandAddElements(object array, int index) + { + Type cl = array.GetType(); + if (!cl.IsArray) return null; + return ArrayExpandAddElements((Array)array, index); + } + + public static object ArrayExpandAddSingle(Array array, object elementsToAdd) + { + var length = array.Length; + int newLength = length + 1; + var componentType = array.GetType().GetElementType(); + var newArray = Array.CreateInstance(componentType, newLength); + + Array.Copy(array, 0, newArray, 0, length); + newArray.SetValue(elementsToAdd, length); + return newArray; + } + + public static object ArrayExpandAddSingle(object array, object elementsToAdd) + { + Type cl = array.GetType(); + if (!cl.IsArray) return null; + return ArrayExpandAddSingle((Array)array, elementsToAdd); + } + + public static int[] AddValue(int[] ints, int i) { + int[] copy = new int[ints.Length + 1]; + Array.Copy(ints, 0, copy, 0, ints.Length); + copy[ints.Length] = i; + return copy; + } + + public static int FindItem(string[] items, string item) { + for (int i = 0; i < items.Length; i++) { + if (items[i].Equals(item)) { + return i; + } + } + return -1; + } + + /// Returns an array of integer values from the set of integer values + /// to return array for + /// array + public static int[] IntArray(ICollection set) + { + if (set == null) + { + return new int[0]; + } + + return set.ToArray(); + } + + public static string[] CopySortArray(IEnumerable values) + { + if (values == null) + { + return null; + } + + return values.OrderBy(v => v).ToArray(); + } + + public static bool SortCompare(string[] valuesOne, string[] valuesTwo) + { + if (valuesOne == null) { + return valuesTwo == null; + } + if (valuesTwo == null) { + return false; + } + string[] copyOne = CopySortArray(valuesOne); + string[] copyTwo = CopySortArray(valuesTwo); + return Collections.AreEqual(copyOne, copyTwo); + } + + /// Returns a list of the elements invoking toString on non-null elements. + /// to render + /// comma-separate list of values (no escape) + public static string ToString(ICollection collection) + { + if (collection == null) + { + return "null"; + } + if (collection.IsEmpty()) + { + return ""; + } + + var buf = new StringBuilder(); + var delimiter = ""; + foreach (T t in collection) + { + if (t == null) + { + continue; + } + buf.Append(delimiter); + buf.Append(t); + delimiter = ", "; + } + return buf.ToString(); + } + + public static bool Compare(string[] otherIndexProps, string[] thisIndexProps) + { + if (otherIndexProps != null && thisIndexProps != null) + { + return Collections.AreEqual(otherIndexProps, thisIndexProps); + } + return otherIndexProps == null && thisIndexProps == null; + } + + public static bool IsAllNullArray(object array) + { + if (array == null) + { + throw new ArgumentNullException("array"); + } + + var asArray = array as Array; + if (asArray == null) + { + throw new ArgumentException("Expected array but received " + array.GetType()); + } + + for (int i = 0; i < asArray.Length; i++) + { + if (asArray.GetValue(i) != null) + { + return false; + } + } + return true; + } + + public static string ToStringArray(Array received) + { + var buf = new StringBuilder(); + var delimiter = ""; + buf.Append("["); + foreach (object t in received) + { + buf.Append(delimiter); + if (t == null) + { + buf.Append("null"); + } + else if (t is Array) + { + buf.Append(ToStringArray((Array) t)); + } + else + { + buf.Append(t); + } + delimiter = ", "; + } + buf.Append("]"); + return buf.ToString(); + } + + public static IDictionary PopulateNameValueMap(params object[] values) + { + var result = new LinkedHashMap(); + var count = values.Length/2; + if (values.Length != count*2) + { + throw new ArgumentException( + "Expected an event number of name-value pairs"); + } + for (int i = 0; i < count; i++) + { + var index = i*2; + var keyValue = values[index]; + if (!(keyValue is string)) + { + throw new ArgumentException( + "Expected string-type key value at index " + index + " but found " + keyValue); + } + var key = (string) keyValue; + var value = values[index + 1]; + if (result.ContainsKey(key)) + { + throw new ArgumentException( + "Found two or more values for key '" + key + "'"); + } + result[key] = value; + } + return result; + } + + public static object AddArrays(object first, object second) + { + var firstAsArray = first as Array; + var secondAsArray = second as Array; + + if ((first != null) && (firstAsArray == null)) + throw new ArgumentException("Parameter is not an array: " + first); + if ((second != null) && (secondAsArray == null)) + throw new ArgumentException("Parameter is not an array: " + second); + if (firstAsArray == null) + return secondAsArray; + if (secondAsArray == null) + return firstAsArray; + + var firstType = firstAsArray.GetType().GetElementType(); + var firstLength = firstAsArray.Length; + var secondLength = secondAsArray.Length; + var total = firstLength + secondLength; + var dest = Array.CreateInstance(firstType, total); + + Array.Copy(firstAsArray, 0, dest, 0, firstLength); + Array.Copy(secondAsArray, 0, dest, firstLength, secondLength); + + return dest; + } + + public static EventBean[] AddArrayWithSetSemantics(EventBean[] arrayOne, EventBean[] arrayTwo) + { + if (arrayOne.Length == 0) { + return arrayTwo; + } + if (arrayTwo.Length == 0) { + return arrayOne; + } + if (arrayOne.Length == 1 && arrayTwo.Length == 1) { + if (arrayOne[0].Equals(arrayTwo[0])) { + return arrayOne; + } + else { + return new EventBean[] {arrayOne[0], arrayOne[0]}; + } + } + if (arrayOne.Length == 1 && arrayTwo.Length > 1) { + if (SearchArray(arrayTwo, arrayOne[0]) != -1) { + return arrayTwo; + } + } + if (arrayOne.Length > 1 && arrayTwo.Length == 1) { + if (SearchArray(arrayOne, arrayTwo[0]) != -1) { + return arrayOne; + } + } + ICollection set = new HashSet(); + foreach (EventBean @event in arrayOne) { + set.Add(@event); + } + foreach (EventBean @event in arrayTwo) { + set.Add(@event); + } + return set.ToArray(); + } + + public static string[] ToArray(ICollection strings) + { + if (strings.IsEmpty()) + { + return EMPTY_STRING_ARRAY; + } + return strings.ToArray(); + } + + public static int SearchArray(T[] array, T item) + { + for (int i = 0; i < array.Length; i++) + { + if (array[i].Equals(item)) + { + return i; + } + } + return -1; + } + + public static bool RemoveEventByKeyLazyListMap(object key, EventBean bean, IDictionary eventMap) + { + var listOfBeans = eventMap.Get(key); + if (listOfBeans == null) + { + return false; + } + + if (listOfBeans is IList) + { + var events = (IList) listOfBeans; + var result = events.Remove(bean); + if (events.IsEmpty()) + { + eventMap.Remove(key); + } + return result; + } + else if (listOfBeans.Equals(bean)) + { + eventMap.Remove(key); + return true; + } + + return false; + } + + public static void AddEventByKeyLazyListMapBack(object sortKey, EventBean eventBean, IDictionary eventMap) + { + var existing = eventMap.Get(sortKey); + if (existing == null) + { + eventMap.Put(sortKey, eventBean); + } + else + { + if (existing is IList) + { + var existingList = (IList) existing; + existingList.Add(eventBean); + } + else + { + var existingList = new List(); + existingList.Add((EventBean) existing); + existingList.Add(eventBean); + eventMap.Put(sortKey, existingList); + } + } + } + + public static void AddEventByKeyLazyListMapFront(object key, EventBean bean, IDictionary eventMap) + { + var current = eventMap.Get(key); + if (current != null) + { + if (current is IList) + { + var events = (IList) current; + events.Insert(0, bean); // add to front, newest are listed first + } + else + { + var theEvent = (EventBean) current; + var events = new List(); + events.Add(bean); + events.Add(theEvent); + eventMap.Put(key, events); + } + } + else + { + eventMap.Put(key, bean); + } + } + + public static bool IsAnySet(bool[] array) + { + return array.Any(t => t); + } + + public static IDictionary TwoEntryMap(TKey keyOne, TValue valueOne, TKey keyTwo, TValue valueTwo) + { + var map = new Dictionary(); + map.Put(keyOne, valueOne); + map.Put(keyTwo, valueTwo); + return map; + } + + } +} diff --git a/NEsper.Core/NEsper.Core/util/ConstructorHelper.cs b/NEsper.Core/NEsper.Core/util/ConstructorHelper.cs new file mode 100755 index 000000000..65899de96 --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/ConstructorHelper.cs @@ -0,0 +1,111 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +namespace com.espertech.esper.util +{ + /// + /// Helper class to find and invoke a class constructors that matches the types of arguments supplied. + /// + public class ConstructorHelper + { + private static Type[] EMPTY_OBJECT_ARRAY_TYPE = new[] { typeof(Object[]) }; + + /// + /// Find and invoke constructor matching the argument number and types returning an instance of given class. + /// + /// is the class of instance to construct + /// is the arguments for the constructor to match in number and type + /// instance of class + /// IllegalAccessException thrown if no access to class + /// NoSuchMethodException thrown when the constructor is not found + /// TargetInvocationException thrown when the ctor throws and exception + /// InstantiationException thrown when the class cannot be loaded + public static Object InvokeConstructor(Type type, Object[] arguments) + { + Type[] parameterTypes = new Type[arguments.Length]; + for (int i = 0; i < arguments.Length; i++) + { + parameterTypes[i] = arguments[i].GetType(); + } + + // Find a constructor that matches exactly + ConstructorInfo ctor = GetRegularConstructor(type, parameterTypes); + if (ctor != null) + { + return ctor.Invoke(arguments); + } + + // Find a constructor with the same number of assignable parameters (such as int -> Integer). + ctor = FindMatchingConstructor(type, parameterTypes); + if (ctor != null) + { + return ctor.Invoke(arguments); + } + + // Find an Object[] constructor, which always matches (throws an exception if not found) + ctor = GetObjectArrayConstructor(type); + if (ctor == null) + { + throw new MissingMethodException(); + } + + return ctor.Invoke(new Object[] { arguments }); + } + + private static ConstructorInfo FindMatchingConstructor(Type type, Type[] parameterTypes) + { + ConstructorInfo[] ctors = type.GetConstructors(); + + for (int i = 0; i < ctors.Length; i++) + { + ParameterInfo[] ctorParams = ctors[i].GetParameters(); + + if (IsAssignmentCompatible(parameterTypes, ctorParams)) + { + return ctors[i]; + } + } + + return null; + } + + private static bool IsAssignmentCompatible(Type[] parameterTypes, ParameterInfo[] ctorParams) + { + if (parameterTypes.Length != ctorParams.Length) + { + return false; + } + + for (int i = 0; i < parameterTypes.Length; i++) + { + if (!ctorParams[i].ParameterType.IsAssignmentCompatible(parameterTypes[i])) + { + return false; + } + } + + return true; + } + + private static ConstructorInfo GetRegularConstructor(Type type, Type[] parameterTypes) + { + // Try to find the matching constructor + ConstructorInfo ctor = type.GetConstructor(parameterTypes); + return ctor; + } + + // Try to find an Object[] constructor + private static ConstructorInfo GetObjectArrayConstructor(Type clazz) + { + return clazz.GetConstructor(EMPTY_OBJECT_ARRAY_TYPE); + } + } +} diff --git a/NEsper.Core/NEsper.Core/util/DatabaseTypeBinding.cs b/NEsper.Core/NEsper.Core/util/DatabaseTypeBinding.cs new file mode 100755 index 000000000..dd9fd091f --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/DatabaseTypeBinding.cs @@ -0,0 +1,76 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.util +{ + /// + /// Binding from database output column type to object. + /// + public interface DatabaseTypeBinding + { + /// + /// Returns the object for the given column. + /// + /// The raw object. + /// is the column name + /// object + /// SQLException if the mapping cannot be performed + Object GetValue(Object rawObject, String columnName); + + /// Returns the target data type. + /// Data type + Type DataType { get; } + } + + /// + /// Returns the object for the given column. + /// + public delegate Object DataRetriever(Object rawObject, String columnName); + + /// + /// Implementation of the DataTypeBinding that uses delegates + /// + /// + + [Serializable] + public class ProxyDatabaseTypeBinding : DatabaseTypeBinding + { + private readonly DataRetriever dataRetriever; + + /// + /// Initializes a new instance of the class. + /// + /// The retriever. + public ProxyDatabaseTypeBinding( DataRetriever retriever ) + { + this.dataRetriever = retriever; + } + + /// + /// Returns the object for the given column. + /// + /// The raw object. + /// is the column name + /// object + /// SQLException if the mapping cannot be performed + public Object GetValue(Object rawObject, String columnName) + { + return dataRetriever.Invoke(rawObject, columnName); + } + + /// Returns the target data type. + /// Data type + public Type DataType + { + get { return typeof(T); } + } + } + +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/util/DatabaseTypeEnum.cs b/NEsper.Core/NEsper.Core/util/DatabaseTypeEnum.cs new file mode 100755 index 000000000..12e2a8103 --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/DatabaseTypeEnum.cs @@ -0,0 +1,233 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.util +{ + /// + /// Enumeration of the different built-in types that are used to represent database output column values. + /// + /// Assigns a name to each type that serves as a short name in mapping, and a type. + /// + /// + /// Provides binding implementations that use the correct ResultSet.get method to pull the correct type + /// out of a statement's result set. + /// + /// + [Serializable] + public class DatabaseTypeEnum + { + private static readonly IDictionary bindings; + + /// Boolean type. + public static readonly DatabaseTypeEnum Boolean = new DatabaseTypeEnum(typeof(Boolean)); + + /// Byte type. + public static readonly DatabaseTypeEnum Byte = new DatabaseTypeEnum(typeof(Byte)); + + /// Byte array type. + public static readonly DatabaseTypeEnum ByteArray = new DatabaseTypeEnum(typeof(Byte[])); + + /// Big decimal. + public static readonly DatabaseTypeEnum Decimal = new DatabaseTypeEnum(typeof(Decimal)); + + /// Double type. + public static readonly DatabaseTypeEnum Double = new DatabaseTypeEnum(typeof(Double)); + + /// Float type. + public static readonly DatabaseTypeEnum Float = new DatabaseTypeEnum(typeof(Single)); + + /// Integer type. + public static readonly DatabaseTypeEnum Int = new DatabaseTypeEnum(typeof(Int32)); + + /// Long type. + public static readonly DatabaseTypeEnum Long = new DatabaseTypeEnum(typeof(Int64)); + + /// Short type. + public static readonly DatabaseTypeEnum Short = new DatabaseTypeEnum(typeof(Int16)); + + /// String type. + public static readonly DatabaseTypeEnum String = new DatabaseTypeEnum(typeof(String)); + + /// timestamp type. + public static readonly DatabaseTypeEnum Timestamp = new DatabaseTypeEnum(typeof(DateTime)); + + public static readonly DatabaseTypeEnum[] Values = new[] + { + String, + Decimal, + Boolean, + Byte, + Short, + Int, + Long, + Float, + Double, + ByteArray, + Timestamp + }; + + private readonly Type boxedtype; + private readonly Type datatype; + + static DatabaseTypeEnum() + { + bindings = new Dictionary(); + + bindings.Put( + String, + new ProxyDatabaseTypeBinding( + (rawData, columnName) => Convert.ToString(rawData))); + + bindings.Put( + Decimal, + new ProxyDatabaseTypeBinding( + (rawData, columnName) => Convert.ToDecimal(rawData))); + + bindings.Put( + Boolean, + new ProxyDatabaseTypeBinding( + (rawData, columnName) => Convert.ToBoolean(rawData))); + + bindings.Put( + Byte, + new ProxyDatabaseTypeBinding( + (rawData, columnName) => Convert.ToByte(rawData))); + + bindings.Put( + ByteArray, + new ProxyDatabaseTypeBinding( + (rawData, columnName) => Convert.ChangeType(rawData, typeof(byte[])))); + + bindings.Put( + Double, + new ProxyDatabaseTypeBinding( + (rawData, columnName) => Convert.ToDouble(rawData))); + + bindings.Put( + Float, + new ProxyDatabaseTypeBinding( + (rawData, columnName) => Convert.ToSingle(rawData))); + + + bindings.Put( + Int, + new ProxyDatabaseTypeBinding( + (rawData, columnName) => Convert.ToInt32(rawData))); + + bindings.Put( + Long, + new ProxyDatabaseTypeBinding( + (rawData, columnName) => Convert.ToInt64(rawData))); + + bindings.Put( + Short, + new ProxyDatabaseTypeBinding( + (rawData, columnName) => Convert.ToInt16(rawData))); + + //bindings.Put( + // SqlDate, + // new ProxyDatabaseTypeBinding( + // delegate(Object rawData, String columnName) + // { + // return Convert.ToDateTime(rawData); + // })); + + //bindings.Put( + // SqlTime, + // new ProxyDatabaseTypeBinding( + // delegate(Object rawData, String columnName) + // { + // return Convert.ToDateTime(rawData); + // })); + + //bindings.Put( + // SqlTimestamp, + // new ProxyDatabaseTypeBinding( + // delegate(Object rawData, String columnName) + // { + // return Convert.ToDateTime(rawData); + // })); + } + + private DatabaseTypeEnum(Type type) + { + datatype = type; + boxedtype = TypeHelper.GetBoxedType(type); + } + + /// Retuns the type for the name. + public Type DataType + { + get { return datatype; } + } + + /// + /// Gets the boxed data type. + /// + /// The type of the boxed. + public Type BoxedType + { + get { return boxedtype; } + } + + /// + /// Returns the binding for this enumeration value for + /// reading the database result set and returning the right type. + /// + /// The binding. + /// mapping of output column type to built-in + public DatabaseTypeBinding Binding + { + get { return bindings.Get(this); } + } + + /// + /// Given a type name, matches for simple and fully-qualified type name (case-insensitive) + /// as well as case-insensitive type name. + /// + /// is the named type + /// type enumeration value for type + public static DatabaseTypeEnum GetEnum(String type) + { + String boxedType = TypeHelper.GetBoxedTypeName(type).ToLower(); + String sourceName1 = boxedType.ToLower(); + + foreach (DatabaseTypeEnum val in Values) + { + String targetName1 = val.BoxedType.FullName.ToLower(); + if (targetName1 == sourceName1) + { + return val; + } + + String targetName2 = val.DataType.FullName.ToLower(); + if (targetName2 == sourceName1) + { + return val; + } + + if (targetName2 == boxedType) + { + return val; + } + + String targetName3 = val.DataType.Name; + if (targetName3 == boxedType) + { + return val; + } + } + return null; + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/util/DependencyGraph.cs b/NEsper.Core/NEsper.Core/util/DependencyGraph.cs new file mode 100755 index 000000000..685358402 --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/DependencyGraph.cs @@ -0,0 +1,263 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.util +{ + /// + /// Model of dependency of lookup, in which one stream supplies values for lookup in another stream. + /// + public class DependencyGraph + { + private readonly int _numStreams; + private readonly bool _allowDependencySame; + private readonly IDictionary> _dependencies; + + /// + /// Ctor. + /// + /// number of streams + /// if set to true [allow dependency same]. + public DependencyGraph(int numStreams, bool allowDependencySame) + { + _numStreams = numStreams; + _allowDependencySame = allowDependencySame; + _dependencies = new Dictionary>(); + } + + /// Returns the number of streams. + /// number of streams + public int NumStreams + { + get { return _numStreams; } + } + + public override String ToString() + { + var writer = new StringWriter(); + + int count = 0; + foreach (var entry in _dependencies) + { + count++; + writer.WriteLine("Record {0}: from={1} to={2}", count, entry.Key, entry.Value.Render()); + } + + return writer.ToString(); + } + + /// + /// Adds dependencies that a target may have on required streams. + /// + /// the stream having dependencies on one or more other streams + /// the streams that the target stream has a dependency on + public void AddDependency(int target, ICollection requiredStreams) + { + if (requiredStreams.Contains(target)) + { + throw new ArgumentException("Dependency between same streams is not allowed for stream " + target); + } + + var toSet = _dependencies.Get(target); + if (toSet != null) + { + throw new ArgumentException("Dependencies from stream " + target + " already in collection"); + } + + _dependencies.Put(target, requiredStreams); + } + + /// + /// Adds a single dependency of target on a required streams. + /// + /// the stream having dependencies on one or more other streams + /// a single required streams that the target stream has a dependency on + public void AddDependency(int target, int from) + { + if (target == from && !_allowDependencySame) + { + throw new ArgumentException("Dependency between same streams is not allowed for stream " + target); + } + + var toSet = _dependencies.Get(target); + if (toSet == null) + { + toSet = new SortedSet(); + _dependencies.Put(target, toSet); + } + + toSet.Add(from); + } + + /// + /// Returns true if the stream asked for has a dependency. + /// + /// to check dependency for + /// true if a dependency exist, false if not + public bool HasDependency(int stream) + { + var dep = _dependencies.Get(stream); + if (dep != null) + { + return dep.IsNotEmpty(); + } + return false; + } + + /// Returns the set of dependent streams for a given stream. + /// to return dependent streams for + /// set of stream numbers of stream providing properties + public ICollection GetDependenciesForStream(int stream) + { + var dep = _dependencies.Get(stream); + if (dep != null) + { + return dep; + } + return new int[0]; + } + + /// + /// Returns a map of stream number and the streams dependencies. + /// + /// map of dependencies + public IDictionary> Dependencies + { + get { return _dependencies; } + } + + /// + /// Returns a set of stream numbers that are the root dependencies, i.e. the dependencies with the deepest graph. + /// + /// set of stream number of streams + public ICollection RootNodes + { + get + { + var rootNodes = new HashSet(); + + for (int i = 0; i < _numStreams; i++) + { + bool found = _dependencies.Any(entry => entry.Value.Contains(i)); + if (!found) + { + rootNodes.Add(i); + } + } + + return rootNodes; + } + } + + /// + /// Return the root nodes ignoring the nodes provided. + /// + /// nodes to be ignored + /// root nodes + public ICollection GetRootNodes(ICollection ignoreList) + { + var rootNodes = new HashSet(); + + for (int i = 0; i < _numStreams; i++) + { + if (ignoreList.Contains(i)) + { + continue; + } + bool found = _dependencies + .Where(entry => entry.Value.Contains(i)) + .Any(entry => !ignoreList.Contains(entry.Key)); + if (!found) + { + rootNodes.Add(i); + } + } + + return rootNodes; + } + + /// Returns any circular dependency as a stack of stream numbers, or null if none exist. + /// circular dependency stack + public IEnumerable FirstCircularDependency + { + get + { + for (int i = 0; i < _numStreams; i++) + { + var deepDependencies = new Stack(); + deepDependencies.Push(i); + + bool isCircular = RecursiveDeepDepends(deepDependencies, i); + if (isCircular) + { + return deepDependencies.Reverse(); + } + } + return null; + } + } + + private bool RecursiveDeepDepends(Stack deepDependencies, int currentStream) + { + var required = _dependencies.Get(currentStream); + if (required == null) + { + return false; + } + + foreach (int stream in required) + { + if (deepDependencies.Contains(stream)) + { + return true; + } + deepDependencies.Push(stream); + bool isDeep = RecursiveDeepDepends(deepDependencies, stream); + if (isDeep) + { + return true; + } + deepDependencies.Pop(); + } + + return false; + } + + /// Check if the given stream has any dependencies, direct or indirect, to any of the streams that are not in the ignore list. + public bool HasUnsatisfiedDependency(int navigableStream, ICollection ignoreList) + { + var deepDependencies = new HashSet(); + RecursivePopulateDependencies(navigableStream, deepDependencies); + + foreach (int dependency in deepDependencies) + { + if (!ignoreList.Contains(dependency)) + { + return true; + } + } + return false; + } + + private void RecursivePopulateDependencies(int navigableStream, ICollection deepDependencies) + { + var dependencies = GetDependenciesForStream(navigableStream); + deepDependencies.AddAll(dependencies); + foreach (int dependency in dependencies) + { + RecursivePopulateDependencies(dependency, deepDependencies); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/util/DestroyCallback.cs b/NEsper.Core/NEsper.Core/util/DestroyCallback.cs new file mode 100755 index 000000000..0dc137e63 --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/DestroyCallback.cs @@ -0,0 +1,39 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.util +{ + /// + /// General pupose callback to destroy a resource and free it's underlying resources. + /// + public interface DestroyCallback + { + /// + /// Destroys the underlying resources. + /// + void Destroy(); + } + + public sealed class ProxyDestroyCallback : DestroyCallback + { + public Action ProcDestroy; + + public ProxyDestroyCallback() { } + public ProxyDestroyCallback(Action procDestroy) + { + ProcDestroy = procDestroy; + } + + public void Destroy() + { + ProcDestroy.Invoke(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/util/DisplayExtensions.cs b/NEsper.Core/NEsper.Core/util/DisplayExtensions.cs new file mode 100755 index 000000000..32aaa804f --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/DisplayExtensions.cs @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.util +{ + public static class DisplayExtensions + { + public static string Render(this T? value) where T : struct + { + if (value.HasValue) + return value.ToString(); + return "null"; + } + } +} diff --git a/NEsper.Core/NEsper.Core/util/EsperSectionHandler.cs b/NEsper.Core/NEsper.Core/util/EsperSectionHandler.cs new file mode 100755 index 000000000..525c594fc --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/EsperSectionHandler.cs @@ -0,0 +1,42 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System.Configuration; +using System.Xml; + +using com.espertech.esper.client; +using Configuration = com.espertech.esper.client.Configuration; + +namespace com.espertech.esper.util +{ + /// + /// Handles custom configuration sections for Esper. + /// + + public class EsperSectionHandler : IConfigurationSectionHandler + { + #region IConfigurationSectionHandler Members + + /// + /// Creates the section from the node information. + /// + /// + /// Configuration context object. + /// + /// The created section handler object. + public object Create(object parent, object configContext, XmlNode section) + { + Configuration configuration = new Configuration(); + ConfigurationParser.DoConfigure(configuration, (XmlElement) section); + return configuration; + } + + #endregion + } +} diff --git a/NEsper.Core/NEsper.Core/util/EventRepresentationChoice.cs b/NEsper.Core/NEsper.Core/util/EventRepresentationChoice.cs new file mode 100755 index 000000000..61bb55107 --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/EventRepresentationChoice.cs @@ -0,0 +1,174 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.client.annotation; +using com.espertech.esper.client.soda; +using com.espertech.esper.client.util; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.service; + +namespace com.espertech.esper.util +{ + public enum EventRepresentationChoice + { + ARRAY, + MAP, + AVRO, + DEFAULT + } + + public static class EventRepresentationChoiceExtensions + { + public static string GetAnnotationText(this EventRepresentationChoice enumValue) + { + switch (enumValue) + { + case EventRepresentationChoice.ARRAY: + return "@EventRepresentation('objectarray')"; + case EventRepresentationChoice.MAP: + return "@EventRepresentation('map')"; + case EventRepresentationChoice.AVRO: + return "@EventRepresentation('avro')"; + case EventRepresentationChoice.DEFAULT: + return ""; + } + + throw new ArgumentException("invalid value for enumValue", "enumValue"); + } + + public static string GetOutputTypeCreateSchemaName(this EventRepresentationChoice enumValue) + { + switch (enumValue) + { + case EventRepresentationChoice.ARRAY: + return " objectarray"; + case EventRepresentationChoice.MAP: + return " map"; + case EventRepresentationChoice.AVRO: + return " avro"; + case EventRepresentationChoice.DEFAULT: + return ""; + } + + throw new ArgumentException("invalid value for enumValue", "enumValue"); + } + + public static string GetOutputTypeClassName(this EventRepresentationChoice enumValue) + { + switch (enumValue) + { + case EventRepresentationChoice.ARRAY: + return EventUnderlyingType.OBJECTARRAY.GetUnderlyingClassName(); + case EventRepresentationChoice.MAP: + return EventUnderlyingType.MAP.GetUnderlyingClassName(); + case EventRepresentationChoice.AVRO: + return EventUnderlyingType.AVRO.GetUnderlyingClassName(); + case EventRepresentationChoice.DEFAULT: + return EventUnderlyingTypeExtensions.GetDefault().GetUnderlyingClassName(); + } + + throw new ArgumentException("invalid value for enumValue", "enumValue"); + } + + + public static EventUnderlyingType GetUnderlyingType(this EventRepresentationChoice enumValue) + { + switch (enumValue) + { + case EventRepresentationChoice.ARRAY: + return EventUnderlyingType.OBJECTARRAY; + case EventRepresentationChoice.MAP: + return EventUnderlyingType.MAP; + case EventRepresentationChoice.AVRO: + return EventUnderlyingType.AVRO; + case EventRepresentationChoice.DEFAULT: + return EventUnderlyingTypeExtensions.GetDefault(); + } + + throw new ArgumentException("invalid value for enumValue", "enumValue"); + } + + + public static bool MatchesClass(this EventRepresentationChoice enumValue, Type representationType) + { + var outputTypeClassName = GetOutputTypeClassName(enumValue); + var supers = new HashSet(); + TypeHelper.GetBase(representationType, supers); + supers.Add(representationType); + foreach (Type clazz in supers) + { + if (clazz.FullName == outputTypeClassName) + { + return true; + } + } + return false; + } + + public static EventRepresentationChoice GetEngineDefault(EPServiceProvider engine) + { + var spi = (EPServiceProviderSPI) engine; + var configured = spi.ConfigurationInformation.EngineDefaults.EventMeta.DefaultEventRepresentation; + if (configured == EventUnderlyingType.OBJECTARRAY) + { + return EventRepresentationChoice.ARRAY; + } + else if (configured == EventUnderlyingType.AVRO) + { + return EventRepresentationChoice.AVRO; + } + return EventRepresentationChoice.MAP; + } + + public static bool IsObjectArrayEvent(this EventRepresentationChoice enumValue) + { + return enumValue == EventRepresentationChoice.ARRAY; + } + + public static bool IsMapEvent(this EventRepresentationChoice enumValue) + { + return enumValue == EventRepresentationChoice.DEFAULT || enumValue == EventRepresentationChoice.MAP; + } + + public static string GetAnnotationTextForNonMap(this EventRepresentationChoice enumValue) + { + if (enumValue == EventRepresentationChoice.DEFAULT || enumValue == EventRepresentationChoice.MAP) + { + return ""; + } + return GetAnnotationText(enumValue); + } + + public static void AddAnnotationForNonMap(this EventRepresentationChoice enumValue, EPStatementObjectModel model) + { + if (enumValue == EventRepresentationChoice.DEFAULT || enumValue == EventRepresentationChoice.MAP) + { + return; + } + var part = new AnnotationPart("EventRepresentation"); + if (enumValue == EventRepresentationChoice.ARRAY) + { + part.AddValue("objectarray"); + } + if (enumValue == EventRepresentationChoice.AVRO) + { + part.AddValue("avro"); + } + model.Annotations = Collections.SingletonList(part); + } + + public static bool IsAvroEvent(this EventRepresentationChoice enumValue) + { + return enumValue == EventRepresentationChoice.AVRO; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/util/EventRepresentationUtil.cs b/NEsper.Core/NEsper.Core/util/EventRepresentationUtil.cs new file mode 100755 index 000000000..a5aaaa330 --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/EventRepresentationUtil.cs @@ -0,0 +1,87 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.client.annotation; +using com.espertech.esper.client.util; +using com.espertech.esper.compat; +using com.espertech.esper.epl.annotation; +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.util +{ + public class EventRepresentationUtil + { + + public static EventUnderlyingType GetRepresentation( + Attribute[] annotations, + ConfigurationInformation configs, + AssignedType assignedType) + { + // assigned type has priority + if (assignedType == AssignedType.OBJECTARRAY) + { + return EventUnderlyingType.OBJECTARRAY; + } + else if (assignedType == AssignedType.MAP) + { + return EventUnderlyingType.MAP; + } + else if (assignedType == AssignedType.AVRO) + { + return EventUnderlyingType.AVRO; + } + if (assignedType == AssignedType.VARIANT || + assignedType != AssignedType.NONE) + { + throw new IllegalStateException("Not handled by event representation: " + assignedType); + } + + // annotation has second priority + var annotation = AnnotationUtil.FindAnnotation(annotations, typeof (EventRepresentationAttribute)); + if (annotation != null) + { + EventRepresentationAttribute eventRepresentation = (EventRepresentationAttribute) annotation; + if (eventRepresentation.Value == EventUnderlyingType.AVRO) + { + return EventUnderlyingType.AVRO; + } + else if (eventRepresentation.Value == EventUnderlyingType.OBJECTARRAY) + { + return EventUnderlyingType.OBJECTARRAY; + } + else if (eventRepresentation.Value == EventUnderlyingType.MAP) + { + return EventUnderlyingType.MAP; + } + else + { + throw new IllegalStateException("Unrecognized enum " + eventRepresentation.Value); + } + } + + // use engine-wide default + EventUnderlyingType configured = configs.EngineDefaults.EventMeta.DefaultEventRepresentation; + if (configured == EventUnderlyingType.OBJECTARRAY) + { + return EventUnderlyingType.OBJECTARRAY; + } + else if (configured == EventUnderlyingType.MAP) + { + return EventUnderlyingType.MAP; + } + else if (configured == EventUnderlyingType.AVRO) + { + return EventUnderlyingType.AVRO; + } + return EventUnderlyingType.MAP; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/util/ExecutionPathDebugLog.cs b/NEsper.Core/NEsper.Core/util/ExecutionPathDebugLog.cs new file mode 100755 index 000000000..f8bd881f5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/ExecutionPathDebugLog.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.util +{ + /// + /// Utility class that control debug-level logging in the execution path + /// beyond which is controlled by logging infrastructure. + /// + + public class ExecutionPathDebugLog + { + /// + /// Gets or sets a flag that allows execution path debug logging. + /// + public static bool IsEnabled { get; set; } + + /// + /// Public access. + /// + public static bool IsTimerDebugEnabled { get; set; } + + /// + /// Initializes the class. + /// + static ExecutionPathDebugLog() + { + IsEnabled = false; + IsTimerDebugEnabled = true; + } + } +} diff --git a/NEsper.Core/NEsper.Core/util/GraphCircularDependencyException.cs b/NEsper.Core/NEsper.Core/util/GraphCircularDependencyException.cs new file mode 100755 index 000000000..6ce8817a5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/GraphCircularDependencyException.cs @@ -0,0 +1,31 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.util +{ + /// Exception to represent a circular dependency. + public class GraphCircularDependencyException : Exception + { + /// Ctor. + /// supplies the detailed description + public GraphCircularDependencyException(String message) + : base(message) + { + } + + /// Ctor. + /// supplies the detailed description + /// the exception cause + public GraphCircularDependencyException(String message, Exception innerException) + : base(message, innerException) + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/util/GraphUtil.cs b/NEsper.Core/NEsper.Core/util/GraphUtil.cs new file mode 100755 index 000000000..ffe2f08fb --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/GraphUtil.cs @@ -0,0 +1,227 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.util +{ + using DataMap = IDictionary; + + /// + /// Utility for working with acyclic graph: determines cyclic dependency and dependency-satisfying processing order. + /// + public class GraphUtil + { + /// + /// Deep-merge a map into another map returning a result map. + /// + /// Copies all values present in the original map to a new map, adding additional value present in + /// the second map passed in, ignoring same-key values in the second map that are present in the original. + /// + /// If the value is a Map itself, repeats the operation on the Map value. + /// + /// nestable Map of entries to retain and not overwrite + /// nestable Map of entries to add to the original + /// + /// merge of original and additional nestable map + /// + public static IDictionary MergeNestableMap(IDictionary original, IDictionary additional) + { + var result = new LinkedHashMap(original); + + foreach (var additionalEntry in additional) + { + var name = additionalEntry.Key; + var additionalValue = additionalEntry.Value; + + var originalValue = original.Get(name); + + if ((originalValue is DataMap) && + (additionalValue is DataMap)) + { + var innerAdditional = (DataMap) additionalValue; + var innerOriginal = (DataMap) originalValue; + Object newValue = MergeNestableMap(innerOriginal, innerAdditional); + result.Put(name, newValue); + continue; + } + + if (original.ContainsKey(name)) + { + continue; + } + result.Put(name, additionalValue); + } + return result; + } + + /// Check cyclic dependency and determine processing order for the given graph. + /// is represented as child nodes that have one or more parent nodes that they are dependent on + /// set of parent and child nodes in order such that no node's dependency is not satisfiedby a prior nodein the set + /// GraphCircularDependencyException if a dependency has been detected + public static ICollection GetTopDownOrder(IDictionary> graph) + { + var circularDependency = GetFirstCircularDependency(graph); + if (circularDependency != null) + { + throw new GraphCircularDependencyException("Circular dependency detected between " + circularDependency.Render()); + } + + var reversedGraph = new Dictionary>(); + + // Reversed the graph - build a list of children per parent + foreach (var entry in graph) + { + var parents = entry.Value; + var child = entry.Key; + + foreach (var parent in parents) + { + var childList = reversedGraph.Get(parent); + if (childList == null) + { + childList = new FIFOHashSet(); + reversedGraph.Put(parent, childList); + } + childList.Add(child); + } + } + + // Determine all root nodes, which are those without parent + var roots = new SortedSet(); + foreach (var parents in graph.Values) + { + if (parents == null) + { + continue; + } + foreach (String parent in parents) + { + // node not itself a child + if (!graph.ContainsKey(parent)) + { + roots.Add(parent); + } + } + } + + // for each root, recursively add its child nodes, this becomes the default order + ICollection graphFlattened = new FIFOHashSet(); + foreach (String root in roots) + { + RecusiveAdd(graphFlattened, root, reversedGraph); + } + + // now walk down the default order and for each node ensure all parents are created + ICollection created = new FIFOHashSet(); + ICollection removeList = new HashSet(); + while (graphFlattened.IsNotEmpty()) + { + removeList.Clear(); + foreach (String node in graphFlattened) + { + if (!RecursiveParentsCreated(node, created, graph)) + { + continue; + } + created.Add(node); + removeList.Add(node); + } + graphFlattened.RemoveAll(removeList); + } + + return created; + } + + // Determine if all the node's parents and their parents have been added to the created set + private static bool RecursiveParentsCreated(String node, ICollection created, IDictionary> graph) + { + var parents = graph.Get(node); + if (parents == null) + { + return true; + } + foreach (String parent in parents) + { + if (!created.Contains(parent)) + { + return false; + } + bool allParentsCreated = RecursiveParentsCreated(parent, created, graph); + if (!allParentsCreated) + { + return false; + } + } + return true; + } + + private static void RecusiveAdd(ICollection graphFlattened, String root, IDictionary> reversedGraph) + { + graphFlattened.Add(root); + ICollection childNodes = reversedGraph.Get(root); + if (childNodes == null) + { + return; + } + foreach (String child in childNodes) + { + RecusiveAdd(graphFlattened, child, reversedGraph); + } + } + + /// Returns any circular dependency as a stack of stream numbers, or null if none exist. + /// the dependency graph + /// circular dependency stack + private static Stack GetFirstCircularDependency(IDictionary> graph) + { + foreach (String child in graph.Keys) + { + Stack deepDependencies = new Stack(); + deepDependencies.Push(child); + + bool isCircular = RecursiveDeepDepends(deepDependencies, child, graph); + if (isCircular) + { + return deepDependencies; + } + } + return null; + } + + private static bool RecursiveDeepDepends(Stack deepDependencies, String currentChild, IDictionary> graph) + { + var required = graph.Get(currentChild); + if (required == null) + { + return false; + } + + foreach (String parent in required) + { + if (deepDependencies.Contains(parent)) + { + return true; + } + deepDependencies.Push(parent); + bool isDeep = RecursiveDeepDepends(deepDependencies, parent, graph); + if (isDeep) + { + return true; + } + deepDependencies.Pop(); + } + + return false; + } + } +} diff --git a/NEsper.Core/NEsper.Core/util/HashUtil.cs b/NEsper.Core/NEsper.Core/util/HashUtil.cs new file mode 100755 index 000000000..e2d37fa22 --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/HashUtil.cs @@ -0,0 +1,13 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.util +{ + public class HashUtil { + } +} diff --git a/NEsper.Core/NEsper.Core/util/Indent.cs b/NEsper.Core/NEsper.Core/util/Indent.cs new file mode 100755 index 000000000..618a5b0b4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/Indent.cs @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.util +{ + /// + /// Utility class around indenting and formatting text. + /// + + public class Indent + { + /// Utility method to indent a text for a number of characters. + /// is the number of character to indent with spaces + /// + /// the formatted string + /// + + public static String CreateIndent(int numChars) + { + if (numChars < 0) + { + throw new ArgumentException("Number of characters less then zero"); + } + + char[] buf = new char[numChars]; + for (int ii = 0; ii < buf.Length; ii++) + { + buf[ii] = ' '; + } + + return (new String(buf)); + } + } +} diff --git a/NEsper.Core/NEsper.Core/util/IndentWriter.cs b/NEsper.Core/NEsper.Core/util/IndentWriter.cs new file mode 100755 index 000000000..a18fb8fba --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/IndentWriter.cs @@ -0,0 +1,73 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; + +namespace com.espertech.esper.util +{ + /// + /// Writer that uses an underlying PrintWriter to indent output text for easy reading. + /// + + public class IndentWriter + { + private readonly TextWriter _writer; + private readonly int _deltaIndent; + private int _currentIndent; + + /// Ctor. + /// to output to + /// + /// is the depth of indent to Start + /// + /// is the number of characters to indent for every incrIndent() call + /// + public IndentWriter(TextWriter writer, int startIndent, int deltaIndent) + { + if (startIndent < 0) + { + throw new ArgumentException("Invalid Start indent"); + } + if (deltaIndent < 0) + { + throw new ArgumentException("Invalid delta indent"); + } + + _writer = writer; + _deltaIndent = deltaIndent; + _currentIndent = startIndent; + } + + /// Increase the indentation one level. + public virtual void IncrIndent() + { + _currentIndent += _deltaIndent; + } + + /// Decrease the indentation one level. + public virtual void DecrIndent() + { + _currentIndent -= _deltaIndent; + } + + /// Print text to the underlying writer. + /// to print + /// + public virtual void WriteLine(String text) + { + int indent = _currentIndent; + if (indent < 0) + { + indent = 0; + } + + _writer.WriteLine(Indent.CreateIndent(indent) + text); + } + } +} diff --git a/NEsper.Core/NEsper.Core/util/JsonUtil.cs b/NEsper.Core/NEsper.Core/util/JsonUtil.cs new file mode 100755 index 000000000..ec224e260 --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/JsonUtil.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.generated; +using com.espertech.esper.epl.parse; + +namespace com.espertech.esper.util +{ + public class JsonUtil + { + public static Object ParsePopulate(String json, Type topClass, ExprNodeOrigin exprNodeOrigin, ExprValidationContext exprValidationContext) + { + var startRuleSelector = new ParseRuleSelector(parser => parser.startJsonValueRule()); + var parseResult = ParseHelper.Parse(json, json, true, startRuleSelector, false); + var tree = (EsperEPL2GrammarParser.StartJsonValueRuleContext)parseResult.Tree; + var parsed = ASTJsonHelper.Walk(parseResult.TokenStream, tree.jsonvalue()); + + if (!(parsed is IDictionary)) + { + throw new ExprValidationException( + "Failed to map value to object of type " + topClass.FullName + + ", expected Json Map/Object format, received " + (parsed != null ? parsed.GetType().Name : "null")); + } + var objectProperties = (IDictionary)parsed; + return PopulateUtil.InstantiatePopulateObject(objectProperties, topClass, exprNodeOrigin, exprValidationContext); + } + } +} diff --git a/NEsper.Core/NEsper.Core/util/JsonUtilParameterOverides.cs b/NEsper.Core/NEsper.Core/util/JsonUtilParameterOverides.cs new file mode 100755 index 000000000..cc171068d --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/JsonUtilParameterOverides.cs @@ -0,0 +1,17 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.util +{ + public interface JsonUtilParameterOverides + { + Object GetValue(String parameterName); + } +} diff --git a/NEsper.Core/NEsper.Core/util/KeyOfEventComputation.cs b/NEsper.Core/NEsper.Core/util/KeyOfEventComputation.cs new file mode 100755 index 000000000..aa6653e19 --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/KeyOfEventComputation.cs @@ -0,0 +1,19 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.util +{ + public interface KeyOfEventComputation + { + Object ComputeKey(EventBean @event); + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/util/LazyAllocatedMap.cs b/NEsper.Core/NEsper.Core/util/LazyAllocatedMap.cs new file mode 100755 index 000000000..7c3ba3c5c --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/LazyAllocatedMap.cs @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +namespace com.espertech.esper.util +{ + public class LazyAllocatedMap + { + private IDictionary _inner; + + public IDictionary Map + { + get + { + lock (this) + { + return _inner ?? (_inner = new Dictionary()); + } + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/util/LevenshteinDistance.cs b/NEsper.Core/NEsper.Core/util/LevenshteinDistance.cs new file mode 100755 index 000000000..44508053b --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/LevenshteinDistance.cs @@ -0,0 +1,73 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.util +{ + /// + /// Utility for string comparison based on the Levenshtein algo. + /// + public class LevenshteinDistance + { + /// Make 3 characters an acceptable distance for reporting. + public readonly static int ACCEPTABLE_DISTANCE = 3; + + /// + /// Compute the distance between two strins using the Levenshtein algorithm, including a case-insensitive string comparison. + /// + /// first string + /// second string + /// + /// distance or zero if case-insensitive string comparison found equal stringsor int.MaxValue for invalid comparison because of null values. + /// + public static int ComputeLevenshteinDistance(string str1, string str2) + { + if ((str1 == null) || (str2 == null)) + { + return int.MaxValue; + } + + if (str1.ToLowerInvariant() == str2.ToLowerInvariant()) + { + return 0; + } + + var distance = new int[str1.Length + 1, str2.Length + 1]; + + for (int i = 0; i <= str1.Length; i++) + { + distance[i, 0] = i; + } + for (int j = 0; j <= str2.Length; j++) + { + distance[0, j] = j; + } + + for (int i = 1; i <= str1.Length; i++) + { + for (int j = 1; j <= str2.Length; j++) + { + distance[i, j] = Minimum( + distance[i - 1, j] + 1, + distance[i, j - 1] + 1, + distance[i - 1, j - 1] + + ((str1[i - 1] == str2[j - 1]) ? 0 + : 1)); + } + } + + return distance[str1.Length, str2.Length]; + } + + private static int Minimum(int a, int b, int c) + { + return Math.Min(Math.Min(a, b), c); + } + } +} diff --git a/NEsper.Core/NEsper.Core/util/LikeUtil.cs b/NEsper.Core/NEsper.Core/util/LikeUtil.cs new file mode 100755 index 000000000..20b45797a --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/LikeUtil.cs @@ -0,0 +1,245 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.util +{ + /// + /// Utility for performing a SQL Like comparsion. + /// + + [Serializable] + public class LikeUtil + { + internal bool EquivalentToFalsePredicate + { + get { return _isNull; } + } + + internal bool EquivalentToEqualsPredicate + { + get { return _iFirstWildCard == -1; } + } + + internal bool EquivalentToNotNullPredicate + { + get + { + if (_isNull || !HasWildcards) + { + return false; + } + + for (int i = 0; i < _wildCardType.Length; i++) + { + if (_wildCardType[i] != LikeUtil.PERCENT_CHAR) + { + return false; + } + } + + return true; + } + } + + internal bool EquivalentToBetweenPredicate + { + get { return _iFirstWildCard > 0 && _iFirstWildCard == _wildCardType.Length - 1 && _cLike[_iFirstWildCard] == '%'; } + } + + internal bool EquivalentToBetweenPredicateAugmentedWithLike + { + get { return _iFirstWildCard > 0 && _cLike[_iFirstWildCard] == '%'; } + } + + private const int UNDERSCORE_CHAR = 1; + private const int PERCENT_CHAR = 2; + + private char[] _cLike; + private int[] _wildCardType; + private int _iLen; + private readonly bool _isIgnoreCase; + private int _iFirstWildCard; + private bool _isNull; + private char? _escapeChar; + + /// Ctor. + /// is the SQL-like pattern to + /// is the escape character + /// is true to ignore the case, or false if not + + public LikeUtil(String pattern, char? escape, bool ignorecase) + { + _escapeChar = escape; + _isIgnoreCase = ignorecase; + Normalize(pattern); + } + + /// Execute the string. + /// is the string to compare + /// + /// true if pattern matches, or false if not + /// + + public virtual bool Compare(String compareString) + { + if (_isIgnoreCase) + { + compareString = compareString.ToUpper(); + } + + return CompareAt(compareString, 0, 0, compareString.Length) ? true : false; + } + + /// Resets the search pattern. + /// is the new pattern to match against + /// + + public virtual void ResetPattern(String pattern) + { + Normalize(pattern); + } + + private bool CompareAt(String s, int i, int j, int jLen) + { + for (; i < _iLen; i++) + { + switch (_wildCardType[i]) + { + case 0: // general character + if ((j >= jLen) || (_cLike[i] != s[j++])) + { + return false; + } + break; + + + case UNDERSCORE_CHAR: // underscore: do not test this character + if (j++ >= jLen) + { + return false; + } + break; + + + case PERCENT_CHAR: // percent: none or any character(s) + if (++i >= _iLen) + { + return true; + } + + while (j < jLen) + { + if ((_cLike[i] == s[j]) && CompareAt(s, i, j, jLen)) + { + return true; + } + + j++; + } + + return false; + } + } + + if (j != jLen) + { + return false; + } + + return true; + } + + private void Normalize(String pattern) + { + + _isNull = pattern == null; + + if (!_isNull && _isIgnoreCase) + { + pattern = pattern.ToUpper(); + } + + _iLen = 0; + _iFirstWildCard = -1; + + int l = pattern == null ? 0 : pattern.Length; + + _cLike = new char[l]; + _wildCardType = new int[l]; + + bool bEscaping = false, bPercent = false; + + for (int i = 0; i < l; i++) + { + char c = pattern[i]; + + if (!bEscaping) + { + if (_escapeChar != null && _escapeChar.Value == c) + { + bEscaping = true; + + continue; + } + else if (c == '_') + { + _wildCardType[_iLen] = LikeUtil.UNDERSCORE_CHAR; + + if (_iFirstWildCard == -1) + { + _iFirstWildCard = _iLen; + } + } + else if (c == '%') + { + if (bPercent) + { + continue; + } + + bPercent = true; + _wildCardType[_iLen] = PERCENT_CHAR; + + if (_iFirstWildCard == -1) + { + _iFirstWildCard = _iLen; + } + } + else + { + bPercent = false; + } + } + else + { + bPercent = false; + bEscaping = false; + } + + _cLike[_iLen++] = c; + } + + for (int i = 0; i < _iLen - 1; i++) + { + if ((_wildCardType[i] == PERCENT_CHAR) && + (_wildCardType[i + 1] == UNDERSCORE_CHAR)) + { + _wildCardType[i] = UNDERSCORE_CHAR; + _wildCardType[i + 1] = PERCENT_CHAR; + } + } + } + + internal bool HasWildcards + { + get { return _iFirstWildCard != -1; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/util/MetaDefItem.cs b/NEsper.Core/NEsper.Core/util/MetaDefItem.cs new file mode 100755 index 000000000..0a6d48650 --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/MetaDefItem.cs @@ -0,0 +1,17 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.util +{ + /// + /// Marker interface for use with statement and engine-level specifications. + /// + public interface MetaDefItem + { + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/util/MethodResolver.cs b/NEsper.Core/NEsper.Core/util/MethodResolver.cs new file mode 100755 index 000000000..974a49793 --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/MethodResolver.cs @@ -0,0 +1,668 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using System.Reflection; +using System.Text; + +using com.espertech.esper.client; +using com.espertech.esper.client.hook; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.core; + +namespace com.espertech.esper.util +{ + /// + /// Used for retrieving static and instance method objects. It provides two points of added functionality + /// over the standard reflection mechanism of retrieving methods. First, class names can be partial, and + /// if the class name is partial then System is searched for the class. Second, invocation parameter + /// types don't have to match the declaration parameter types exactly when the standard conversion mechanisms + /// (currently autoboxing and widening conversions) will make the invocation valid. Preference is given to + /// those methods that require the fewest widening conversions. + /// + public class MethodResolver + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private static readonly IDictionary> WrappingConversions = + new Dictionary>(); + + static HashSet InitWrappingConversions() + { + var wrappers = new HashSet(); + wrappers.Add(typeof(TX)); + wrappers.Add(typeof(TXN)); + WrappingConversions.Put(typeof(TX), wrappers); + WrappingConversions.Put(typeof(TXN), wrappers); + return wrappers; + } + + static MethodResolver() + { + WIDENING_CONVERSIONS = new Dictionary>(); + + // Initialize the map of wrapper conversions + var boolWrappers = InitWrappingConversions(); + var charWrappers = InitWrappingConversions(); + var byteWrappers = InitWrappingConversions(); + var sbyteWrappers = InitWrappingConversions(); + var shortWrappers = InitWrappingConversions(); + var ushortWrappers = InitWrappingConversions(); + var intWrappers = InitWrappingConversions(); + var uintWrappers = InitWrappingConversions(); + var longWrappers = InitWrappingConversions(); + var ulongWrappers = InitWrappingConversions(); + var floatWrappers = InitWrappingConversions(); + var doubleWrappers = InitWrappingConversions(); + var decimalWrappers = InitWrappingConversions(); + var bigIntWrappers = InitWrappingConversions(); + + // Initialize the map of widening conversions + var wideningConversions = new HashSet(byteWrappers); + WIDENING_CONVERSIONS.Put(typeof(short), new HashSet(wideningConversions)); + WIDENING_CONVERSIONS.Put(typeof(short?), new HashSet(wideningConversions)); + + wideningConversions.AddAll(shortWrappers); + wideningConversions.AddAll(charWrappers); + WIDENING_CONVERSIONS.Put(typeof(int), new HashSet(wideningConversions)); + WIDENING_CONVERSIONS.Put(typeof(int?), new HashSet(wideningConversions)); + + wideningConversions.AddAll(intWrappers); + WIDENING_CONVERSIONS.Put(typeof(long), new HashSet(wideningConversions)); + WIDENING_CONVERSIONS.Put(typeof(long?), new HashSet(wideningConversions)); + + wideningConversions.AddAll(longWrappers); + WIDENING_CONVERSIONS.Put(typeof(float), new HashSet(wideningConversions)); + WIDENING_CONVERSIONS.Put(typeof(float?), new HashSet(wideningConversions)); + + wideningConversions.AddAll(floatWrappers); + WIDENING_CONVERSIONS.Put(typeof(double), new HashSet(wideningConversions)); + WIDENING_CONVERSIONS.Put(typeof(double?), new HashSet(wideningConversions)); + + wideningConversions.AddAll(doubleWrappers); + WIDENING_CONVERSIONS.Put(typeof(decimal), new HashSet(wideningConversions)); + WIDENING_CONVERSIONS.Put(typeof(decimal?), new HashSet(wideningConversions)); + } + + /// + /// Returns the allowable widening conversions. + /// + /// + /// map where key is the class that we are asking to be widened into, anda set of classes that can be widened from + /// + public static IDictionary> WIDENING_CONVERSIONS { get; private set; } + + /// + /// Attempts to find the static or instance method described by the parameters, or a method of the same name that will accept the same type of parameters. + /// + /// the class to search for the method + /// the name of the method + /// the parameter types for the method + /// true to allow instance methods as well, false to allow only static method + /// Type of the allow event bean. + /// Type of the allow event bean coll. + /// - the Method object for this method + /// EngineNoSuchMethodException if the method could not be found + public static MethodInfo ResolveMethod( + Type declaringClass, + String methodName, + Type[] paramTypes, + bool allowInstance, + bool[] allowEventBeanType, + bool[] allowEventBeanCollType) + { + // Get all the methods for this class + MethodInfo[] methods = declaringClass.GetMethods() + .OrderBy(m => m.IsVarArgs() ? 1 : 0) + .ToArray(); + + MethodInfo bestMatch = null; + var bestConversionCount = -1; + + // Examine each method, checking if the signature is compatible + MethodInfo conversionFailedMethod = null; + + for (int mm = 0; mm < methods.Length; mm++) + { + var method = methods[mm]; + + // Check the modifiers: we only want public and static, if required + if (!IsPublicAndStatic(method, allowInstance)) + { + continue; + } + + // Check the name + if (method.Name != methodName) + { + continue; + } + + var parameterTypes = method.GetParameters() + .Select(p => p.ParameterType) + .ToArray(); + + if (method.IsGenericMethod && method.IsVarArgs()) + { + // we need to what arguments have been supplied for the + // remaining arguments since we need to coerce the remaining + // arguments to the same type + var commonArgs = paramTypes.Skip(paramTypes.Length - 1).ToArray(); + var commonType = GetCommonCoersion(commonArgs); + if (commonArgs.Length == 1 && commonArgs[0].IsArray) + { + // this is an annoying case where the inputs are an argument array... + // in this case we want to unpack the common coercion type from the + // underlying common args themselves. + commonType = commonArgs[0].GetElementType(); + } + + method = method.MakeGenericMethod(commonType); + parameterTypes = method.GetParameters() + .Select(p => p.ParameterType) + .ToArray(); + } + + // Check the parameter list + int conversionCount = CompareParameterTypesAllowContext( + parameterTypes, + paramTypes, + allowEventBeanType, + allowEventBeanCollType, + parameterTypes, // method.GetGenericArguments(), + method.IsVarArgs() + ); + + // Parameters don't match + if (conversionCount == -1) + { + conversionFailedMethod = method; + continue; + } + + // Parameters match exactly + if (conversionCount == 0) + { + bestMatch = method; + break; + } + + // No previous match + if (bestMatch == null) + { + bestMatch = method; + bestConversionCount = conversionCount; + } + else + { + // Current match is better + if (conversionCount < bestConversionCount) + { + bestMatch = method; + bestConversionCount = conversionCount; + } + } + } + + if (bestMatch != null) + { + LogWarnBoxedToPrimitiveType(declaringClass, methodName, bestMatch, paramTypes); + return bestMatch; + } + + var paramList = new StringBuilder(); + if (paramTypes != null && paramTypes.Length != 0) + { + var appendString = ""; + foreach (var param in paramTypes) + { + paramList.Append(appendString); + if (param == null) + { + paramList.Append("(null)"); + } + else + { + paramList.Append(param.ToString()); + } + appendString = ", "; + } + } + + throw new EngineNoSuchMethodException("Unknown method " + declaringClass.Name + '.' + methodName + '(' + paramList + ')', conversionFailedMethod); + } + + public static MethodInfo ResolveExtensionMethod(Type declaringClass, String methodName, Type[] paramTypes, bool allowInstance, bool[] allowEventBeanType, bool[] allowEventBeanCollType) + { + var extensionMethods = TypeHelper.GetExtensionMethods(declaringClass) + .Where(m => m.Name == methodName); + foreach (var method in extensionMethods) + { + var parameterTypes = method.GetParameters().Select(p => p.ParameterType).Skip(1).ToArray(); + + // Check the parameter list + int conversionCount = CompareParameterTypesAllowContext( + parameterTypes, + paramTypes, + allowEventBeanType, + allowEventBeanCollType, + parameterTypes, // method.GetGenericArguments(), + method.IsVarArgs() + ); + + // Parameters match exactly + if (conversionCount == 0) + { + return method; + } + } + + return null; + } + + private static void LogWarnBoxedToPrimitiveType(Type declaringClass, String methodName, MethodInfo bestMatch, Type[] paramTypes) + { + var parametersMethod = bestMatch.GetParameters().Select(p => p.ParameterType).ToArray(); + for (int i = 0; i < parametersMethod.Length; i++) + { + if (!parametersMethod[i].IsPrimitive) + { + continue; + } + // if null-type parameter, or non-CLR class and boxed type matches + if (paramTypes[i] == null || (!declaringClass.GetType().FullName.StartsWith("System.") && (parametersMethod[i].GetBoxedType()) == paramTypes[i])) + { + String paramTypeStr = paramTypes[i] == null ? "null" : paramTypes[i].Name; + Log.Info( + "Method '{0}' in class '{1}' expects primitive type '{2}' as parameter {3}, but receives a nullable (boxed) type {4}. This may cause null pointer exception at runtime if the actual value is null, please consider using boxed types for method parameters.", + methodName, declaringClass.Name, parametersMethod[i], i, paramTypeStr); + return; + } + } + } + + private static Type GetCommonCoersion(IList typeList) + { + var typeHash = new HashSet(); + + typeList[0].Visit(t => typeHash.Add(t)); + + for (int ii = 1; ii < typeList.Count; ii++) + { + var moreTypes = new HashSet(); + typeList[ii].Visit(t => moreTypes.Add(t)); + typeHash.IntersectWith(moreTypes); + } + + // What we are left with is a set of coercable types. This will include + // System.Object which is the defacto fallback when there are no other + // types that have a stronger claim. + var interfaces = typeHash + .Where(t => t.IsInterface) + .ToList(); + + var concretes = typeHash + .Where(t => t.IsInterface == false) + .ToList(); + + // We should never have a case where the concrete count is zero. This would + // indicate that even System.Object was not found as a common class... + if (concretes.Count == 0) + throw new EPRuntimeException("Unable to find common concrete root for type"); + + // Concrete commonality with a count of one is going to be fairly common + // and almost always reflects the case where System.Object is only class + // that could be found. + concretes.Remove(typeof(object)); + if (concretes.Count == 0) + { + // Look for an interface that might provide a better binding ... if none can + // be found then use System.Object as the common coercion. + if (interfaces.Count == 0) + { + return typeof(object); + } + + // Now the only thing to be concerned about with interfaces are constraints + // that might be set somewhere else, like the parameters. We will revisit + // that bit of code should it become something we need to handle. + return interfaces.First(); + } + + // We have multiple concrete classes ... none of which are System.Object. As with + // interfaces, what we have to concern ourselves with is a constraint that may + // be in play elsewhere. We will revisit that bit of code should it become + // something we need to handle. + + return concretes.First(); + } + + private static bool IsWideningConversion(Type declarationType, Type invocationType) + { + return + WIDENING_CONVERSIONS.ContainsKey(declarationType) && + WIDENING_CONVERSIONS.Get(declarationType).Contains(invocationType); + } + + private static bool IsPublicAndStatic(MethodInfo method, bool allowInstance) + { + if (allowInstance) + { + return method.IsPublic; + } + return method.IsPublic && method.IsStatic; + } + + private static int CompareParameterTypesAllowContext( + Type[] declarationParameters, + Type[] invocationParameters, + Boolean[] optionalAllowEventBeanType, + Boolean[] optionalAllowEventBeanCollType, + Type[] genericParameterTypes, + bool isVarArgs) + { + // determine if the last parameter is EPLMethodInvocationContext (no varargs) + var declaredNoContext = declarationParameters; + if (!isVarArgs && + declarationParameters.Length > 0 && + declarationParameters[declarationParameters.Length - 1] == typeof(EPLMethodInvocationContext)) + { + declaredNoContext = declarationParameters.Take(declarationParameters.Length - 1).ToArray(); + } + + // determine if the previous-to-last parameter is EPLMethodInvocationContext (varargs-only) + if (isVarArgs && + declarationParameters.Length > 1 && + declarationParameters[declarationParameters.Length - 2] == typeof(EPLMethodInvocationContext)) + { + var rewritten = new Type[declarationParameters.Length - 1]; + Array.Copy(declarationParameters, 0, rewritten, 0, declarationParameters.Length - 2); + rewritten[rewritten.Length - 1] = declarationParameters[declarationParameters.Length - 1]; + declaredNoContext = rewritten; + } + + return CompareParameterTypesNoContext( + declaredNoContext, + invocationParameters, + optionalAllowEventBeanType, + optionalAllowEventBeanCollType, + genericParameterTypes, + isVarArgs); + } + + // Returns -1 if the invocation parameters aren't applicable + // to the method. Otherwise returns the number of parameters + // that have to be converted + private static int CompareParameterTypesNoContext( + Type[] declarationParameters, + Type[] invocationParameters, + bool[] optionalAllowEventBeanType, + bool[] optionalAllowEventBeanCollType, + Type[] genericParameterTypes, + bool isVarArgs) + { + if (invocationParameters == null) + { + return declarationParameters.Length == 0 ? 0 : -1; + } + + AtomicLong conversionCount; + + // handle varargs + if (isVarArgs) + { + if (invocationParameters.Length < declarationParameters.Length - 1) + { + return -1; + } + if (invocationParameters.Length == 0) + { + return 0; + } + + conversionCount = new AtomicLong(); + + // check declared types (non-vararg) + for (int i = 0; i < declarationParameters.Length - 1; i++) + { + var compatible = CompareParameterTypeCompatible( + invocationParameters[i], + declarationParameters[i], + optionalAllowEventBeanType == null ? (bool?)null : optionalAllowEventBeanType[i], + optionalAllowEventBeanCollType == null ? (bool?)null : optionalAllowEventBeanCollType[i], + genericParameterTypes[i], + conversionCount + ); + + if (!compatible) + { + return -1; + } + } + + var varargDeclarationParameter = declarationParameters[declarationParameters.Length - 1].GetElementType(); + + // handle array of compatible type passed into vararg + if (invocationParameters.Length == declarationParameters.Length) + { + var providedType = invocationParameters[invocationParameters.Length - 1]; + if (providedType != null && providedType.IsArray()) + { + if (providedType.GetElementType() == varargDeclarationParameter) + { + return (int)conversionCount.Get(); + } + if (TypeHelper.IsSubclassOrImplementsInterface(providedType.GetElementType(), varargDeclarationParameter)) + { + conversionCount.IncrementAndGet(); + return (int)conversionCount.Get(); + } + } + } + + // handle compatible types passed into vararg + Type varargGenericParameterTypes = genericParameterTypes[genericParameterTypes.Length - 1]; + for (int i = declarationParameters.Length - 1; i < invocationParameters.Length; i++) + { + var compatible = CompareParameterTypeCompatible( + invocationParameters[i], + varargDeclarationParameter, + optionalAllowEventBeanType == null ? (bool?)null : optionalAllowEventBeanType[i], + optionalAllowEventBeanCollType == null ? (bool?)null : optionalAllowEventBeanCollType[i], + varargGenericParameterTypes, + conversionCount); + if (!compatible) + { + return -1; + } + } + return (int)conversionCount.Get(); + } + + // handle non-varargs + if (declarationParameters.Length != invocationParameters.Length) + { + return -1; + } + + conversionCount = new AtomicLong(); + for (int i = 0; i < declarationParameters.Length; i++) + { + var compatible = CompareParameterTypeCompatible( + invocationParameters[i], + declarationParameters[i], + optionalAllowEventBeanType == null ? (bool?)null : optionalAllowEventBeanType[i], + optionalAllowEventBeanCollType == null ? (bool?)null : optionalAllowEventBeanCollType[i], + genericParameterTypes[i], + conversionCount); + if (!compatible) + { + return -1; + } + } + return (int)conversionCount.Get(); + } + + private static bool CompareParameterTypeCompatible( + Type invocationParameter, + Type declarationParameter, + bool? optionalAllowEventBeanType, + bool? optionalAllowEventBeanCollType, + Type genericParameterType, + AtomicLong conversionCount) + { + if ((invocationParameter == null) && !(declarationParameter.IsPrimitive)) + { + return true; + } + + if (optionalAllowEventBeanType != null && + declarationParameter == typeof(EventBean) && + optionalAllowEventBeanType.GetValueOrDefault()) + { + return true; + } + + if (optionalAllowEventBeanCollType != null && + declarationParameter == typeof(ICollection) && + optionalAllowEventBeanCollType.GetValueOrDefault(false) && + genericParameterType.GetGenericType(0) == typeof(EventBean)) + { + return true; + } + + if (!IsIdentityConversion(declarationParameter, invocationParameter)) + { + conversionCount.IncrementAndGet(); + if (!IsWideningConversion(declarationParameter, invocationParameter)) + { + return false; + } + } + + return true; + } + + // Identity conversion means no conversion, wrapper conversion, + // or conversion to a supertype + private static bool IsIdentityConversion(Type declarationType, Type invocationType) + { + if (WrappingConversions.ContainsKey(declarationType)) + { + return WrappingConversions.Get(declarationType).Contains(invocationType) || declarationType.IsAssignableFrom(invocationType); + } + if (invocationType == null) + { + return !declarationType.IsPrimitive; + } + return declarationType.IsAssignableFrom(invocationType); + } + + public static ConstructorInfo ResolveCtor(Type declaringClass, Type[] paramTypes) + { + // Get all the methods for this class + ConstructorInfo[] ctors = declaringClass.GetConstructors(); + + ConstructorInfo bestMatch = null; + int bestConversionCount = -1; + + // Examine each method, checking if the signature is compatible + ConstructorInfo conversionFailedCtor = null; + foreach (ConstructorInfo ctor in ctors) + { + // Check the modifiers: we only want public + if (!ctor.IsPublic) + { + continue; + } + + // Check the parameter list + var constructorParameters = ctor.GetParameters().Select(p => p.ParameterType).ToArray(); + int conversionCount = CompareParameterTypesNoContext( + constructorParameters, + paramTypes, null, null, + constructorParameters, // ctor.GetGenericArguments()); + ctor.IsVarArgs()); + + // MSDN + // + // NotSupportedException - The current object is a ConstructorInfo. Generic constructors are not + // supported in the .NET Framework version 2.0. This exception is the default behavior if this method + // is not overridden in a derived class. + + // Parameters don't match + if (conversionCount == -1) + { + conversionFailedCtor = ctor; + continue; + } + + // Parameters match exactly + if (conversionCount == 0) + { + bestMatch = ctor; + break; + } + + // No previous match + if (bestMatch == null) + { + bestMatch = ctor; + bestConversionCount = conversionCount; + } + else + { + // Current match is better + if (conversionCount < bestConversionCount) + { + bestMatch = ctor; + bestConversionCount = conversionCount; + } + } + + } + + if (bestMatch != null) + { + return bestMatch; + } + + var paramList = new StringBuilder(); + var message = "Constructor not found for " + declaringClass.Name + " taking "; + if (paramTypes != null && paramTypes.Length != 0) + { + var appendString = ""; + foreach (var param in paramTypes) + { + paramList.Append(appendString); + if (param == null) + { + paramList.Append("(null)"); + } + else + { + paramList.Append(param.ToString()); + } + appendString = ", "; + } + message += "('" + paramList + "')'"; + } + else + { + message += "no parameters"; + } + throw new EngineNoSuchCtorException(message, conversionFailedCtor); + } + } +} diff --git a/NEsper.Core/NEsper.Core/util/MetricUtil.cs b/NEsper.Core/NEsper.Core/util/MetricUtil.cs new file mode 100755 index 000000000..b0ce97ad4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/MetricUtil.cs @@ -0,0 +1,68 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Diagnostics; +using System.Threading; + +namespace com.espertech.esper.util +{ + /// + /// Utility for CPU and wall time metrics. + /// + public class MetricUtil + { + [ThreadStatic] + public static ProcessThread _currentProcessThread; + + /// + /// Gets the current process thread. + /// + /// The current process thread. + public static ProcessThread CurrentProcessThread + { + get + { + if (_currentProcessThread == null) + { + int id = Thread.CurrentThread.ManagedThreadId; + + Process process = Process.GetCurrentProcess(); + foreach (ProcessThread processThread in process.Threads) + { + if (processThread.Id == id) + { + _currentProcessThread = processThread; + break; + } + } + } + + return _currentProcessThread; + } + } + + /// + /// Gets the user processor time for the current thread. + /// + /// The user processor time. + public static TimeSpan UserProcessorTime + { + get { return CurrentProcessThread != null ? CurrentProcessThread.UserProcessorTime : TimeSpan.Zero; } + } + + /// + /// Gets the total processor time for the current thread. + /// + /// The total processor time. + public static TimeSpan TotalProcessorTime + { + get { return CurrentProcessThread != null ? CurrentProcessThread.TotalProcessorTime : TimeSpan.Zero; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/util/MultiKeyCastingComparator.cs b/NEsper.Core/NEsper.Core/util/MultiKeyCastingComparator.cs new file mode 100755 index 000000000..a9b016d78 --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/MultiKeyCastingComparator.cs @@ -0,0 +1,36 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.collection; + +namespace com.espertech.esper.util +{ + /// + /// A comparator on multikeys. The multikeys must contain the same number of values. + /// + [Serializable] + public sealed class MultiKeyCastingComparator + : IComparer + , MetaDefItem + { + private readonly IComparer _comparator; + + public MultiKeyCastingComparator(IComparer comparator) + { + _comparator = comparator; + } + + public int Compare(Object firstValues, Object secondValues) + { + return _comparator.Compare((MultiKeyUntyped)firstValues, (MultiKeyUntyped)secondValues); + } + } +} diff --git a/NEsper.Core/NEsper.Core/util/MultiKeyCollatingComparator.cs b/NEsper.Core/NEsper.Core/util/MultiKeyCollatingComparator.cs new file mode 100755 index 000000000..925eece42 --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/MultiKeyCollatingComparator.cs @@ -0,0 +1,76 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.collection; + +namespace com.espertech.esper.util +{ + /// + /// A comparator on multikeys with string values and using the Collator for + /// comparing. The multikeys must contain the same number of values. + /// + [Serializable] + public sealed class MultiKeyCollatingComparator + : IComparer + , MetaDefItem + { + private readonly bool[] _isDescendingValues; + private readonly bool[] _stringTypedValue; + private readonly StringComparison _stringComparison; + + /// + /// Ctor. + /// + /// each value is true if the corresponding (same index)entry in the multi-keys is to be sorted in descending order. The multikeys to be compared must have the same number of values as this array. + /// true for each string-typed column + public MultiKeyCollatingComparator(bool[] isDescendingValues, bool[] stringTypeValues) + { + _isDescendingValues = isDescendingValues; + _stringTypedValue = stringTypeValues; + _stringComparison = StringComparison.OrdinalIgnoreCase; + } + + public int Compare(MultiKeyUntyped firstValues, MultiKeyUntyped secondValues) + { + if (firstValues.Count != _isDescendingValues.Length || secondValues.Count != _isDescendingValues.Length) + { + throw new ArgumentException("Incompatible size MultiKey sizes for comparison"); + } + + for (int i = 0; i < firstValues.Count; i++) + { + var valueOne = firstValues.Get(i); + var valueTwo = secondValues.Get(i); + var isDescending = _isDescendingValues[i]; + + if (!_stringTypedValue[i]) + { + int comparisonResult = MultiKeyComparator.CompareValues(valueOne, valueTwo, isDescending); + if (comparisonResult != 0) + { + return comparisonResult; + } + } + else + { + int comparisonResult = MultiKeyComparator.CompareValues(valueOne, valueTwo, isDescending, _stringComparison); + if (comparisonResult != 0) + { + return comparisonResult; + } + } + } + + // Make the comparator compatible with equals + return !Equals(firstValues, secondValues) ? -1 : 0; + } + } +} diff --git a/NEsper.Core/NEsper.Core/util/MultiKeyComparator.cs b/NEsper.Core/NEsper.Core/util/MultiKeyComparator.cs new file mode 100755 index 000000000..51e317f60 --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/MultiKeyComparator.cs @@ -0,0 +1,178 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; + +using com.espertech.esper.collection; + +namespace com.espertech.esper.util +{ + /// A comparator on multikeys. The multikeys must contain the same + /// number of values. + /// + + [Serializable] + public sealed class MultiKeyComparator : IComparer, MetaDefItem + { + private readonly bool[] _isDescendingValues; + + /// Ctor. + /// each value is true if the corresponding (same index) + /// entry in the multi-keys is to be sorted in descending order. The multikeys + /// to be compared must have the same number of values as this array. + /// + + public MultiKeyComparator(bool[] isDescendingValues) + { + _isDescendingValues = isDescendingValues; + } + + /// + /// Compares the specified first values. + /// + /// The first values. + /// The second values. + /// + public int Compare(MultiKeyUntyped firstValues, MultiKeyUntyped secondValues) + { + if (firstValues.Count != _isDescendingValues.Length || secondValues.Count != _isDescendingValues.Length) + { + throw new ArgumentException("Incompatible size MultiKey sizes for comparison"); + } + + for (int i = 0; i < firstValues.Count; i++) + { + var valueOne = firstValues.Get(i); + var valueTwo = secondValues.Get(i); + var isDescending = _isDescendingValues[i]; + + int comparisonResult = CompareValues(valueOne, valueTwo, isDescending); + if (comparisonResult != 0) + { + return comparisonResult; + } + } + + // Make the comparator compatible with equals + return !Equals(firstValues, secondValues) ? - 1 : 0; + } + + /// + /// Compares two nullable values. + /// + /// first value to compare + /// second value to compare + /// true for descending + /// + /// compare result + /// + public static int CompareValues(Object valueOne, Object valueTwo, bool isDescending) + { + return CompareValues(valueOne, valueTwo, isDescending, StringComparison.Ordinal); + } + + /// + /// Compares two nullable values using string options for use with string-typed values. + /// + /// first value to compare + /// second value to compare + /// true for descending + /// the options string for comparison + /// compare result + public static int CompareValues(Object valueOne, Object valueTwo, bool isDescending, StringComparison comparisonOptions) + { + if (valueOne == null || valueTwo == null) + { + // A null value is considered equal to another null + // value and smaller than any nonnull value + if (valueOne == null && valueTwo == null) + { + return 0; + } + if (valueOne == null) + { + if (isDescending) + { + return 1; + } + return -1; + } + if (isDescending) + { + return -1; + } + return 1; + } + + int multiplier = isDescending ? -1 : 1; + if ((valueOne is string) && (valueTwo is string)) + { + return multiplier * String.Compare((string)valueOne, (string)valueTwo, comparisonOptions); + } + + IComparable comparable1 = valueOne as IComparable; + if (comparable1 != null) + { + return multiplier * ((IComparable)valueOne).CompareTo(valueTwo); + } + + throw new InvalidCastException("Cannot sort objects of type " + valueOne.GetType()); + } + + /// + /// Compares two nullable values using string options for use with string-typed values. + /// + /// first value to compare + /// second value to compare + /// true for descending + /// the options string for comparison + /// compare result + public static int CompareValues(Object valueOne, Object valueTwo, bool isDescending, StringComparer comparer) + { + if (valueOne == null || valueTwo == null) + { + // A null value is considered equal to another null + // value and smaller than any nonnull value + if (valueOne == null && valueTwo == null) + { + return 0; + } + if (valueOne == null) + { + if (isDescending) + { + return 1; + } + return -1; + } + if (isDescending) + { + return -1; + } + return 1; + } + + int multiplier = isDescending ? -1 : 1; + if ((valueOne is string) && (valueTwo is string)) + { + return multiplier * comparer.Compare((string)valueOne, (string)valueTwo); + } + + IComparable comparable1 = valueOne as IComparable; + if (comparable1 != null) + { + return multiplier * ((IComparable)valueOne).CompareTo(valueTwo); + } + + throw new InvalidCastException("Cannot sort objects of type " + valueOne.GetType()); + } + + } +} diff --git a/NEsper.Core/NEsper.Core/util/MurmurHash.cs b/NEsper.Core/NEsper.Core/util/MurmurHash.cs new file mode 100755 index 000000000..551ee976d --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/MurmurHash.cs @@ -0,0 +1,87 @@ +// +// Copyright 2010 The Apache Software Foundation +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// +// Original file: org/apache/hadoop/hbase/util/MurmurHash.java +// + +namespace com.espertech.esper.util +{ + /// + /// This is a very fast, non-cryptographic hash suitable for general hash-based + /// lookup. See http://murmurhash.googlepages.com/ for more details. + /// The C version of MurmurHash 2.0 found at that site was ported + /// + public class MurmurHash + { + public static int Hash(byte[] data, int offset, int length, int seed) + { + const int r = 24; + + uint m = 0x5bd1e995; + uint h = (uint)(seed ^ length); + + int len_4 = length >> 2; + + for (int i = 0; i < len_4; i++) + { + uint i_4 = (uint)((i << 2) + offset); + uint k = data[i_4 + 3]; + k = k << 8; + k = k | (uint)(data[i_4 + 2] & 0xff); + k = k << 8; + k = k | (uint)(data[i_4 + 1] & 0xff); + k = k << 8; + //noinspection PointlessArithmeticExpression + k = k | (uint)(data[i_4 + 0] & 0xff); + k *= m; + k ^= k >> r; + k *= m; + h *= m; + h ^= k; + } + + // avoid calculating modulo + int len_m = len_4 << 2; + int left = length - len_m; + int i_m = len_m + offset; + + if (left != 0) + { + if (left >= 3) + { + h ^= ((uint)data[i_m + 2]) << 16; + } + if (left >= 2) + { + h ^= ((uint)data[i_m + 1]) << 8; + } + if (left >= 1) + { + h ^= data[i_m]; + } + + h *= m; + } + + h ^= h >> 13; + h *= m; + h ^= h >> 15; + + return (int)h; + } + } +} diff --git a/NEsper.Core/NEsper.Core/util/NullableObject.cs b/NEsper.Core/NEsper.Core/util/NullableObject.cs new file mode 100755 index 000000000..a6b3f3517 --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/NullableObject.cs @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.util +{ + /// + /// A generic class to hold an object that may itself be a null value versus an + /// undefined (not existing) value. + /// + /// The presence of a reference indicates that a value exists, the absence of a reference + /// to this object indicates that there is no value (similar to a Pair<Object, Boolean>). + /// + [Serializable] + public class NullableObject + { + /// Ctor. + /// the value to contain + public NullableObject(T value) + { + Value = value; + } + + /// Returns the contained value. + /// contained value + public T Value { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/util/ObjectCollatingComparator.cs b/NEsper.Core/NEsper.Core/util/ObjectCollatingComparator.cs new file mode 100755 index 000000000..c156b2298 --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/ObjectCollatingComparator.cs @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.util +{ + /// + /// A comparator on objects that takes a bool array for ascending/descending. + /// + [Serializable] + public sealed class ObjectCollatingComparator + : IComparer + , MetaDefItem + { + private readonly bool _isDescendingValue; + + [NonSerialized] + private readonly StringComparer _collator = null; + + /// Ctor. + /// ascending or descending + public ObjectCollatingComparator(bool isDescendingValue) + { + _isDescendingValue = isDescendingValue; + _collator = StringComparer.CurrentCulture; + } + + public int Compare(Object firstValue, Object secondValue) + { + return MultiKeyComparator.CompareValues(firstValue, secondValue, _isDescendingValue, _collator); + } + } +} diff --git a/NEsper.Core/NEsper.Core/util/ObjectComparator.cs b/NEsper.Core/NEsper.Core/util/ObjectComparator.cs new file mode 100755 index 000000000..3235c6351 --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/ObjectComparator.cs @@ -0,0 +1,36 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.util +{ + /// + /// A comparator on objects that takes a bool array for ascending/descending. + /// + [Serializable] + public sealed class ObjectComparator + : IComparer + , MetaDefItem + { + private readonly bool _isDescendingValue; + + /// Ctor. + /// ascending or descending + public ObjectComparator(bool isDescendingValue) + { + _isDescendingValue = isDescendingValue; + } + + public int Compare(Object firstValue, Object secondValue) + { + return MultiKeyComparator.CompareValues(firstValue, secondValue, _isDescendingValue); + } + } +} diff --git a/NEsper.Core/NEsper.Core/util/PlaceholderParseException.cs b/NEsper.Core/NEsper.Core/util/PlaceholderParseException.cs new file mode 100755 index 000000000..a7180fd4b --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/PlaceholderParseException.cs @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; + +namespace com.espertech.esper.util +{ + /// Exception to indicate a parse error in parsing placeholders. + [Serializable] + public class PlaceholderParseException:System.Exception + { + /// Ctor. + /// is the error message + /// + public PlaceholderParseException(String message) + : base(message) + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/util/PlaceholderParser.cs b/NEsper.Core/NEsper.Core/util/PlaceholderParser.cs new file mode 100755 index 000000000..fc946ca28 --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/PlaceholderParser.cs @@ -0,0 +1,291 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; +using System.Collections.Generic; + +namespace com.espertech.esper.util +{ + /// + /// Parser for strings with substitution parameters of the form ${parameter}. + /// + + public class PlaceholderParser + { + /// Parses a string to find placeholders of format ${placeholder}. + /// + /// Example: "My ${thing} is ${color}" + /// + /// + /// The example above parses into 4 fragements: a text fragment of value "My ", + /// a parameter fragment "thing", a text fragement " is " and a parameter + /// fragment "color". + /// + /// + /// is the string to parse + /// + /// list of fragements that can be either text fragments or placeholder fragments + /// + /// PlaceholderParseException if the string cannot be parsed to indicate syntax errors + + public static IList ParsePlaceholder(String parseString) + { + List result = new List(); + int currOutputIndex = 0; + int currSearchIndex = 0; + + while (true) + { + if (currSearchIndex == parseString.Length) + { + break; + } + + int startIndex = parseString.IndexOf("${", currSearchIndex); + if (startIndex == -1) + { + // no more parameters, add any remainder of string + if (currOutputIndex < parseString.Length) + { + String endString = parseString.Substring(currOutputIndex); + TextFragment textFragment = new TextFragment(endString); + result.Add(textFragment); + } + break; + } + // add text so far + if (startIndex > 0) + { + String textSoFar = parseString.Substring(currOutputIndex, startIndex - currOutputIndex); + if (textSoFar.Length != 0) + { + result.Add(new TextFragment(textSoFar)); + } + } + // check if the parameter is escaped + if ((startIndex > 0) && (parseString[startIndex - 1] == '$')) + { + currOutputIndex = startIndex + 1; + currSearchIndex = startIndex + 1; + continue; + } + + int endIndex = parseString.IndexOf("}", startIndex); + if (endIndex == -1) + { + throw new PlaceholderParseException("Syntax error in property or variable: '" + parseString.Substring(startIndex) + "'"); + } + + // add placeholder + String between = parseString.Substring(startIndex + 2, endIndex - startIndex - 2); + ParameterFragment parameterFragment = new ParameterFragment(between); + result.Add(parameterFragment); + currOutputIndex = endIndex + 1; + currSearchIndex = endIndex; + } + + // Combine adjacent text fragements + var fragments = new LinkedList(); + fragments.AddLast(result[0]); + for (int i = 1; i < result.Count; i++) + { + Fragment fragment = result[i]; + if (!(result[i] is TextFragment)) + { + fragments.AddLast(fragment); + continue; + } + if (!(fragments.Last.Value is TextFragment)) + { + fragments.AddLast(fragment); + continue; + } + TextFragment textFragment = (TextFragment)fragments.Last.Value; + fragments.RemoveLast(); + fragments.AddLast(new TextFragment(textFragment.Value + fragment.Value)); + } + + return new List(fragments); + } + + /// + /// Fragment is a parse result, a parse results in an ordered list of fragments. + /// + public abstract class Fragment + { + /// Returns the string text of the fragment. + /// fragment string + /// + virtual public String Value + { + get { return value; } + } + /// Returns true to indicate this is a parameter and not a text fragment. + /// true if parameter fragement, false if text fragment. + /// + public abstract bool IsParameter + { + get; + } + + private readonly String value; + + /// Ctor. + /// is the fragment text + /// + internal Fragment(String value) + { + this.value = value; + } + + /// + /// Serves as a hash function for a particular type. is suitable for use in hashing algorithms and data structures like a hash table. + /// + /// + /// A hash code for the current . + /// + public override int GetHashCode() + { + return (value != null ? value.GetHashCode() : 0); + } + } + + /// + /// Represents a piece of text in a parse string with placeholder values. + /// + + public class TextFragment : Fragment + { + /// + /// Returns true to indicate this is a parameter and not a text fragment. + /// + /// + /// true if parameter fragement, false if text fragment. + /// + override public bool IsParameter + { + get { return false; } + } + + /// Ctor. + /// is the text + /// + + public TextFragment(String value) + : base(value) + { + } + + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// + /// true if the specified is equal to the current ; otherwise, false. + /// + public override bool Equals(Object obj) + { + if (!(obj is TextFragment)) + { + return false; + } + TextFragment other = (TextFragment)obj; + return other.Value.Equals(this.Value); + } + + + /// + /// Serves as a hash function for a particular type. is suitable for use in hashing algorithms and data structures like a hash table. + /// + /// + /// A hash code for the current . + /// + public override int GetHashCode() + { + return Value.GetHashCode(); + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override String ToString() + { + return "text=" + Value; + } + } + + /// + /// Represents a parameter in a parsed string of texts and parameters. + /// + + public class ParameterFragment : Fragment + { + /// + /// Returns true to indicate this is a parameter and not a text fragment. + /// + /// + /// true if parameter fragement, false if text fragment. + /// + override public bool IsParameter + { + get { return true; } + } + + /// Ctor. + /// is the parameter name + /// + public ParameterFragment(String value) + : base(value) + { + } + + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// + /// true if the specified is equal to the current ; otherwise, false. + /// + public override bool Equals(Object obj) + { + if (!(obj is ParameterFragment)) + { + return false; + } + ParameterFragment other = (ParameterFragment)obj; + return other.Value.Equals(this.Value); + } + + /// + /// Serves as a hash function for a particular type. is suitable for use in hashing algorithms and data structures like a hash table. + /// + /// + /// A hash code for the current . + /// + public override int GetHashCode() + { + return Value.GetHashCode(); + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override String ToString() + { + return "param=" + Value; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/util/PopulateFieldValueSetter.cs b/NEsper.Core/NEsper.Core/util/PopulateFieldValueSetter.cs new file mode 100755 index 000000000..e598856c7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/PopulateFieldValueSetter.cs @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.util +{ +#if true + public delegate void PopulateFieldValueSetter(object value); +#else + public interface PopulateFieldValueSetter + { + void Insert(object value) ; + } +#endif +} diff --git a/NEsper.Core/NEsper.Core/util/PopulateFieldWValueDescriptor.cs b/NEsper.Core/NEsper.Core/util/PopulateFieldWValueDescriptor.cs new file mode 100755 index 000000000..4097c1f84 --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/PopulateFieldWValueDescriptor.cs @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.util +{ + public class PopulateFieldWValueDescriptor + { + public PopulateFieldWValueDescriptor(string propertyName, Type fieldType, Type containerType, PopulateFieldValueSetter setter, bool forceNumeric) + { + PropertyName = propertyName; + FieldType = fieldType; + ContainerType = containerType; + Setter = setter; + IsForceNumeric = forceNumeric; + } + + public string PropertyName { get; private set; } + + public Type FieldType { get; private set; } + + public Type ContainerType { get; private set; } + + public PopulateFieldValueSetter Setter { get; private set; } + + public bool IsForceNumeric { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/util/PopulateUtil.cs b/NEsper.Core/NEsper.Core/util/PopulateUtil.cs new file mode 100755 index 000000000..b9a025117 --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/PopulateUtil.cs @@ -0,0 +1,543 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +using com.espertech.esper.client.dataflow; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.dataflow.annotations; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events; +using com.espertech.esper.events.bean; +using com.espertech.esper.events.property; + +namespace com.espertech.esper.util +{ + public class PopulateUtil + { + private const string CLASS_PROPERTY_NAME = "class"; + private const string SYSTEM_PROPETIES_NAME = "systemProperties"; + + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + public static Object InstantiatePopulateObject(IDictionary objectProperties, Type topClass, ExprNodeOrigin exprNodeOrigin, ExprValidationContext exprValidationContext) { + + var applicableClass = topClass; + if (topClass.IsInterface) { + applicableClass = FindInterfaceImplementation(objectProperties, topClass, exprValidationContext.EngineImportService); + } + + Object top; + try { + top = TypeHelper.Instantiate(applicableClass); + } + catch (TypeLoadException e) + { + throw new ExprValidationException( + GetMessageExceptionInstantiating(applicableClass), e); + } + catch (TypeInstantiationException e) + { + if (e.InnerException is MissingMethodException) + { + throw new ExprValidationException( + GetMessageExceptionInstantiating(applicableClass), e); + } + else if (e.InnerException is MemberAccessException) + { + throw new ExprValidationException( + "Illegal access to construct class " + applicableClass.FullName + ": " + e.InnerException.Message, e.InnerException); + } + else if (e.InnerException is TargetInvocationException) + { + throw new ExprValidationException( + "Exception instantiating class " + applicableClass.FullName + ": " + e.InnerException.InnerException.Message, e.InnerException.InnerException); + } + else + { + throw new ExprValidationException( + "Exception instantiating class " + applicableClass.FullName + ": " + e.InnerException.Message, e.InnerException); + } + } + catch (MemberAccessException e) + { + throw new ExprValidationException("Illegal access to construct class " + applicableClass.FullName + ": " + e.Message, e); + } + catch (Exception e) + { + throw new ExprValidationException("Exception instantiating class " + applicableClass.FullName + ": " + e.Message, e); + } + + PopulateObject(topClass.Name, 0, topClass.Name, objectProperties, top, exprNodeOrigin, exprValidationContext, null, null); + + return top; + } + + public static void PopulateObject( + string operatorName, + int operatorNum, + string dataFlowName, + IDictionary objectProperties, + Object top, + ExprNodeOrigin exprNodeOrigin, + ExprValidationContext exprValidationContext, + EPDataFlowOperatorParameterProvider optionalParameterProvider, + IDictionary optionalParameterURIs) + { + var applicableClass = top.GetType(); + var writables = PropertyHelper.GetWritableProperties(applicableClass); + var annotatedFields = TypeHelper.FindAnnotatedFields(top.GetType(), typeof(DataFlowOpParameterAttribute)); + var annotatedMethods = TypeHelper.FindAnnotatedMethods(top.GetType(), typeof(DataFlowOpParameterAttribute)); + + // find catch-all methods + var catchAllMethods = new LinkedHashSet(); + if (annotatedMethods != null) { + foreach (var method in annotatedMethods) + { + var anno = (DataFlowOpParameterAttribute) TypeHelper.GetAnnotations( + typeof (DataFlowOpParameterAttribute), + method.GetCustomAttributes(true).Cast().ToArray())[0]; + if (anno.All) { + var parameterTypes = method.GetParameterTypes(); + if ((parameterTypes.Length == 2) && + (parameterTypes[0] == typeof(string)) && + (parameterTypes[1] == typeof(object))) + { + catchAllMethods.Add(method); + continue; + } + throw new ExprValidationException("Invalid annotation for catch-call"); + } + } + } + + // map provided values + foreach (var property in objectProperties) { + var found = false; + var propertyName = property.Key; + + // invoke catch-all setters + foreach (var method in catchAllMethods) { + try + { + method.Invoke(top, new Object[] { propertyName, property.Value }); + } + catch (MemberAccessException e) + { + throw new ExprValidationException("Illegal access invoking method for property '" + propertyName + "' for class " + applicableClass.Name + " method " + method.Name, e); + } + catch (TargetInvocationException e) + { + throw new ExprValidationException("Exception invoking method for property '" + propertyName + "' for class " + applicableClass.Name + " method " + method.Name + ": " + e.InnerException.Message, e); + } + found = true; + } + + if (propertyName.ToLowerInvariant() == CLASS_PROPERTY_NAME) { + continue; + } + + // use the writeable property descriptor (appropriate setter method) from writing the property + var descriptor = FindDescriptor(applicableClass, propertyName, writables); + if (descriptor != null) { + var coerceProperty = CoerceProperty(propertyName, applicableClass, property.Value, descriptor.PropertyType, exprNodeOrigin, exprValidationContext, false, true); + + try { + descriptor.WriteMethod.Invoke(top, new Object[]{coerceProperty}); + } + catch (ArgumentException e) + { + throw new ExprValidationException("Illegal argument invoking setter method for property '" + propertyName + "' for class " + applicableClass.Name + " method " + descriptor.WriteMethod.Name + " provided value " + coerceProperty, e); + } + catch (MemberAccessException e) + { + throw new ExprValidationException("Illegal access invoking setter method for property '" + propertyName + "' for class " + applicableClass.Name + " method " + descriptor.WriteMethod.Name, e); + } + catch (TargetInvocationException e) + { + throw new ExprValidationException("Exception invoking setter method for property '" + propertyName + "' for class " + applicableClass.Name + " method " + descriptor.WriteMethod.Name + ": " + e.InnerException.Message, e); + } + continue; + } + + // in .NET, it's common to name fields with an underscore prefix, this modified + // notation is preserved in the modPropertyName + var modPropertyName = "_" + propertyName; + // find the field annotated with + foreach (var annotatedField in annotatedFields) + { + var anno = (DataFlowOpParameterAttribute) TypeHelper.GetAnnotations( + typeof (DataFlowOpParameterAttribute), + annotatedField.GetCustomAttributes(true).Cast().ToArray())[0]; + if ((anno.Name == propertyName) || (annotatedField.Name == propertyName) || (annotatedField.Name == modPropertyName)) + { + var coerceProperty = CoerceProperty(propertyName, applicableClass, property.Value, annotatedField.FieldType, exprNodeOrigin, exprValidationContext, true, true); + try + { + annotatedField.SetValue(top, coerceProperty); + } + catch (Exception e) + { + throw new ExprValidationException( + "Failed to set field '" + annotatedField.Name + "': " + e.Message, e); + } + found = true; + break; + } + } + if (found) { + continue; + } + + throw new ExprValidationException("Failed to find writable property '" + propertyName + "' for class " + applicableClass.FullName); + } + + // second pass: if a parameter URI - value pairs were provided, check that + if (optionalParameterURIs != null) + { + foreach (var annotatedField in annotatedFields) + { + try + { + var uri = operatorName + "/" + annotatedField.Name; + if (optionalParameterURIs.ContainsKey(uri)) + { + var value = optionalParameterURIs.Get(uri); + annotatedField.SetValue(top, value); + if (Log.IsDebugEnabled) + { + Log.Debug( + "Found parameter '" + uri + "' for data flow " + dataFlowName + " setting " + value); + } + } + else + { + if (Log.IsDebugEnabled) + { + Log.Debug("Not found parameter '" + uri + "' for data flow " + dataFlowName); + } + } + } + catch (Exception e) + { + throw new ExprValidationException( + "Failed to set field '" + annotatedField.Name + "': " + e.Message, e); + } + } + + foreach (var method in annotatedMethods) + { + var anno = method.GetCustomAttributes(typeof (DataFlowOpParameterAttribute), false) + .Cast() + .First(); + + if (anno.All) + { + var parameterTypes = method.GetParameterTypes(); + if (parameterTypes.Length == 2 && parameterTypes[0] == typeof (string) && + parameterTypes[1] == typeof (Object)) + { + foreach (var entry in optionalParameterURIs) + { + var uri = new Uri(entry.Key, UriKind.RelativeOrAbsolute); + var elements = URIUtil.ParsePathElements(uri); + if (elements.Length < 2) + { + throw new ExprValidationException( + "Failed to parse URI '" + entry.Key + "', expected " + + "'operator_name/property_name' format"); + } + if (elements[0].Equals(operatorName)) + { + try + { + method.Invoke( + top, new Object[] + { + elements[1], + entry.Value + }); + } + catch (ArgumentException e) + { + throw new ExprValidationException( + "Illegal argument invoking setter method for property '" + entry.Key + + "' for class " + applicableClass.Name + " method " + method.Name, e); + } + catch (MemberAccessException e) + { + throw new ExprValidationException( + "Illegal access invoking setter method for property '" + entry.Key + + "' for class " + applicableClass.Name + " method " + method.Name, e); + } + catch (TargetInvocationException e) + { + throw new ExprValidationException( + "Exception invoking setter method for property '" + entry.Key + + "' for class " + applicableClass.Name + " method " + method.Name + ": " + + e.InnerException.Message, e); + } + } + } + } + } + } + } + + // third pass: if a parameter provider is provided, use that + if (optionalParameterProvider != null) + { + foreach (var annotatedField in annotatedFields) + { + try + { + var provided = annotatedField.GetValue(top); + var value = optionalParameterProvider.Provide(new EPDataFlowOperatorParameterProviderContext(operatorName, annotatedField.Name, top, operatorNum, provided, dataFlowName)); + if (value != null) + { + annotatedField.SetValue(top, value); + } + } + catch (Exception e) + { + throw new ExprValidationException( + "Failed to set field '" + annotatedField.Name + "': " + e.Message, e); + } + } + } + } + + private static Type FindInterfaceImplementation( + IDictionary properties, + Type topClass, + EngineImportService engineImportService) + { + var message = "Failed to find implementation for interface " + Name.Of(topClass); + + // Allow to populate the special "class" field + if (!properties.ContainsKey(CLASS_PROPERTY_NAME)) + { + throw new ExprValidationException( + message + ", for interfaces please specified the '" + CLASS_PROPERTY_NAME + + "' field that provides the class name either as a simple class name or fully qualified"); + } + + Type clazz = null; + var className = (string) properties.Get(CLASS_PROPERTY_NAME); + try + { + clazz = TypeHelper.GetClassForName(className, engineImportService.GetClassForNameProvider()); + } + catch (TypeLoadException e) + { + + if (!className.Contains(".")) + { + className = topClass.Namespace + "." + className; + try + { + clazz = TypeHelper.GetClassForName(className, engineImportService.GetClassForNameProvider()); + } + catch (TypeLoadException) + { + } + } + + if (clazz == null) + { + throw new ExprValidationPropertyException(message + ", could not find class by name '" + className + "'"); + } + } + + if (!TypeHelper.IsSubclassOrImplementsInterface(clazz, topClass)) + { + throw new ExprValidationException( + message + ", class " + clazz.GetTypeNameFullyQualPretty() + + " does not implement the interface"); + } + return clazz; + } + + public static void PopulateSpecCheckParameters( + PopulateFieldWValueDescriptor[] descriptors, + IDictionary jsonRaw, + Object spec, + ExprNodeOrigin exprNodeOrigin, + ExprValidationContext exprValidationContext) + { + // lowercase keys + var lowerCaseJsonRaw = new LinkedHashMap(); + foreach (var entry in jsonRaw) + { + lowerCaseJsonRaw.Put(entry.Key.ToLowerInvariant(), entry.Value); + } + jsonRaw = lowerCaseJsonRaw; + + // apply values + foreach (var desc in descriptors) + { + var value = jsonRaw.Delete(desc.PropertyName.ToLowerInvariant()); + var coerced = CoerceProperty( + desc.PropertyName, desc.ContainerType, value, desc.FieldType, exprNodeOrigin, exprValidationContext, + desc.IsForceNumeric, false); + desc.Setter.Invoke(coerced); + } + + // should not have remaining parameters + if (!jsonRaw.IsEmpty()) + { + throw new ExprValidationException( + "Unrecognized parameter '" + jsonRaw.Keys.First() + "'"); + } + } + + public static Object CoerceProperty( + string propertyName, + Type containingType, + Object value, + Type type, + ExprNodeOrigin exprNodeOrigin, + ExprValidationContext exprValidationContext, + bool forceNumeric, + bool includeClassNameInEx) + { + if (value is ExprNode && type != typeof (ExprNode)) + { + if (value is ExprIdentNode) + { + var identNode = (ExprIdentNode) value; + Property prop; + try + { + prop = PropertyParser.ParseAndWalkLaxToSimple(identNode.FullUnresolvedName); + } + catch (Exception) + { + throw new ExprValidationException( + "Failed to parse property '" + identNode.FullUnresolvedName + "'"); + } + if (!(prop is MappedProperty)) + { + throw new ExprValidationException( + "Unrecognized property '" + identNode.FullUnresolvedName + "'"); + } + var mappedProperty = (MappedProperty) prop; + if (string.Equals( + mappedProperty.PropertyNameAtomic, SYSTEM_PROPETIES_NAME, + StringComparison.InvariantCultureIgnoreCase)) + { + return Environment.GetEnvironmentVariable(mappedProperty.Key); + } + } + else + { + var exprNode = (ExprNode) value; + var validated = ExprNodeUtility.GetValidatedSubtree(exprNodeOrigin, exprNode, exprValidationContext); + exprValidationContext.VariableService.SetLocalVersion(); + var evaluator = validated.ExprEvaluator; + if (evaluator == null) + { + throw new ExprValidationException( + "Failed to evaluate expression '" + + ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(exprNode) + "'"); + } + value = evaluator.Evaluate(EvaluateParams.EmptyTrue); + } + } + + if (value == null) + { + return null; + } + + var valueType = value.GetType(); + if (valueType == type) + { + return value; + } + if (valueType.IsAssignmentCompatible(type)) + { + if (forceNumeric + && (valueType.GetBoxedType() != type.GetBoxedType()) + && type.IsNumeric() + && valueType.IsNumeric()) + { + value = CoercerFactory.CoerceBoxed(value, type.GetBoxedType()); + } + return value; + } + if (TypeHelper.IsSubclassOrImplementsInterface(valueType, type)) + { + return value; + } + if (type.IsArray) + { + if (!(valueType.IsGenericCollection())) + { + var detail = "expects an array but receives a value of type " + valueType.GetTypeNameFullyQualPretty(); + throw new ExprValidationException( + GetExceptionText(propertyName, containingType, includeClassNameInEx, detail)); + } + var items = value.UnwrapIntoArray(); + var coercedArray = Array.CreateInstance(type.GetElementType(), items.Length); + for (var i = 0; i < items.Length; i++) + { + var coercedValue = CoerceProperty( + propertyName + " (array element)", type, items[i], type.GetElementType(), exprNodeOrigin, + exprValidationContext, false, includeClassNameInEx); + coercedArray.SetValue(coercedValue, i); + } + return coercedArray; + } + if (!(value is IDictionary)) + { + var detail = "expects an " + type.GetTypeNameFullyQualPretty() + " but receives a value of type " + valueType.GetTypeNameFullyQualPretty(); + throw new ExprValidationException( + GetExceptionText(propertyName, containingType, includeClassNameInEx, detail)); + } + var props = (IDictionary) value; + return InstantiatePopulateObject(props, type, exprNodeOrigin, exprValidationContext); + } + + private static string GetExceptionText( + string propertyName, + Type containingType, + bool includeClassNameInEx, + string detailText) + { + var msg = "Property '" + propertyName + "'"; + if (includeClassNameInEx) + { + msg += " of class " + containingType.GetTypeNameFullyQualPretty(); + } + msg += " " + detailText; + return msg; + } + + private static WriteablePropertyDescriptor FindDescriptor( + Type clazz, + string propertyName, + IEnumerable writables) + { + return writables + .FirstOrDefault(desc => string.Equals(desc.PropertyName, propertyName, StringComparison.InvariantCultureIgnoreCase)); + } + + private static string GetMessageExceptionInstantiating(Type clazz) + { + return "Exception instantiating class " + clazz.FullName + + ", please make sure the class has a public no-arg constructor (and for inner classes is declared static)"; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/util/SQLTypeMapUtil.cs b/NEsper.Core/NEsper.Core/util/SQLTypeMapUtil.cs new file mode 100755 index 000000000..11104d7d0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/SQLTypeMapUtil.cs @@ -0,0 +1,31 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.util +{ + /// + /// Utility for mapping SQL types to native types. + /// + public class SQLTypeMapUtil + { + /// + /// Converts a SQLType to a native type. + /// + /// to return type for + /// + /// is the classname that result metadata returns for a column + /// + /// Type for sql types + public static Type SqlTypeToClass(int sqlType, String className) + { + throw new ArgumentException("Logic path is not active"); + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/util/SerializableObjectCopier.cs b/NEsper.Core/NEsper.Core/util/SerializableObjectCopier.cs new file mode 100755 index 000000000..d9a2255ec --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/SerializableObjectCopier.cs @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; +using System.Runtime.Serialization; +using System.Runtime.Serialization.Formatters; +using System.Runtime.Serialization.Formatters.Binary; + +namespace com.espertech.esper.util +{ + /// + /// Utility class for copying serializable objects via object input and output streams. + /// + public class SerializableObjectCopier + { + /// Deep copies the input object. + /// is the object to be copied, must be serializable + /// copied object + /// IOException if the streams returned an exception + /// ClassNotFoundException if the de-serialize fails + public static Object Copy(Object orig) + { + // Create the formatter + var formatter = new BinaryFormatter(); + formatter.FilterLevel = TypeFilterLevel.Full; + formatter.AssemblyFormat = FormatterAssemblyStyle.Full; + + using (MemoryStream stream = new MemoryStream()) + { + // Serialize the object graph to the stream + formatter.Serialize(stream, orig); + // Rewind the stream + stream.Seek(0, SeekOrigin.Begin); + // Deserialize the object graph from the stream + return formatter.Deserialize(stream); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/util/Serializer.cs b/NEsper.Core/NEsper.Core/util/Serializer.cs new file mode 100755 index 000000000..91f244044 --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/Serializer.cs @@ -0,0 +1,91 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.IO; +using com.espertech.esper.compat; + +namespace com.espertech.esper.util +{ + public interface Serializer + { + bool Accepts(Type c); + void SerializeAny(Object value, Stream stream); + Object DeserializeAny(Stream stream); + } + + public class StreamSerializer : Serializer + { + public Action ProcSerialize { get; set; } + public Func ProcDeserialize { get; set; } + + /// + /// Acceptses the specified c. + /// + /// The c. + /// + public bool Accepts(Type c) + { + throw new UnsupportedOperationException("Not supported for serializer"); + } + + /// + /// Serializes any. + /// + /// The value. + /// The output stream. + public void SerializeAny(object value, Stream stream) + { + ProcSerialize.Invoke(value, stream); + } + + /// + /// Deserializes any. + /// + /// The input stream. + /// + public object DeserializeAny(Stream stream) + { + return ProcDeserialize.Invoke(stream); + } + } + + public class SmartSerializer : Serializer + { + public Action ProcSerialize { get; set; } + public Func ProcDeserialize { get; set; } + + public bool Accepts(Type c) + { + return c.IsAssignableFrom(typeof (T)); + } + + public void SerializeAny(object value, Stream stream) + { + if (value is T) + { + Serialize((T) value, new BinaryWriter(stream)); + } + } + + public void Serialize(T value, BinaryWriter writer) + { + ProcSerialize.Invoke(value, writer); + } + + public object DeserializeAny(Stream stream) + { + return Deserialize(new BinaryReader(stream)); + } + + public T Deserialize(BinaryReader reader) + { + return ProcDeserialize.Invoke(reader); + } + } +} diff --git a/NEsper.Core/NEsper.Core/util/SerializerFactory.cs b/NEsper.Core/NEsper.Core/util/SerializerFactory.cs new file mode 100755 index 000000000..70a83a6ed --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/SerializerFactory.cs @@ -0,0 +1,156 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Runtime.Serialization.Formatters.Binary; + +namespace com.espertech.esper.util +{ + public class SerializerFactory + { + private static readonly List Serializers; + private static readonly BinaryFormatter BinaryFormatter = new BinaryFormatter(); + + public static readonly Serializer NULL_SERIALIZER = new StreamSerializer + { + ProcSerialize = (obj, writer) => { }, + ProcDeserialize = stream => null + }; + + public static readonly Serializer OBJECT_SERIALIZER = new StreamSerializer + { + ProcSerialize = (obj, stream) => BinaryFormatter.Serialize(stream, obj), + ProcDeserialize = stream => + { + try + { + return BinaryFormatter.Deserialize(stream); + } + catch (TypeLoadException e) + { + throw new IOException("unable to deserialize object", e); + } + } + }; + + static SerializerFactory() + { + Serializers = new List(); + Serializers.Add(new SmartSerializer + { + ProcSerialize = (obj, writer) => writer.Write(obj), + ProcDeserialize = reader => reader.ReadBoolean() + }); + Serializers.Add(new SmartSerializer + { + ProcSerialize = (obj, writer) => writer.Write(obj), + ProcDeserialize = reader => reader.ReadChar() + }); + Serializers.Add(new SmartSerializer + { + ProcSerialize = (obj, writer) => writer.Write(obj), + ProcDeserialize = reader => reader.ReadByte() + }); + Serializers.Add(new SmartSerializer + { + ProcSerialize = (obj, writer) => writer.Write(obj), + ProcDeserialize = reader => reader.ReadInt16() + }); + Serializers.Add(new SmartSerializer + { + ProcSerialize = (obj, writer) => writer.Write(obj), + ProcDeserialize = reader => reader.ReadInt32() + }); + Serializers.Add(new SmartSerializer + { + ProcSerialize = (obj, writer) => writer.Write(obj), + ProcDeserialize = reader => reader.ReadInt64() + }); + Serializers.Add(new SmartSerializer + { + ProcSerialize = (obj, writer) => writer.Write(obj), + ProcDeserialize = reader => reader.ReadSingle() + }); + Serializers.Add(new SmartSerializer + { + ProcSerialize = (obj, writer) => writer.Write(obj), + ProcDeserialize = reader => reader.ReadDouble() + }); + Serializers.Add(new SmartSerializer + { + ProcSerialize = (obj, writer) => writer.Write(obj), + ProcDeserialize = reader => reader.ReadDecimal() + }); + Serializers.Add(new SmartSerializer + { + ProcSerialize = (obj, writer) => writer.Write(obj), + ProcDeserialize = reader => reader.ReadString() + }); + } + + public static Serializer[] GetDefaultSerializers() + { + List serializer = new List(); + serializer.Add(NULL_SERIALIZER); + serializer.AddRange(Serializers); + serializer.Add(OBJECT_SERIALIZER); + return serializer.ToArray(); + } + + public static Serializer[] GetSerializers(IEnumerable types) + { + return types.Select(GetSerializer).ToArray(); + } + + public static Serializer GetSerializer(Type type) + { + if (type == null) + { + return NULL_SERIALIZER; + } + + foreach (var serializer in Serializers.Where(serializer => serializer.Accepts(type.GetBoxedType()))) + { + return serializer; + } + + return OBJECT_SERIALIZER; + } + + public static byte[] Serialize(Serializer[] serializers, Object[] objects) + { + Debug.Assert(serializers.Length == objects.Length); + + using (var binaryStream = new MemoryStream()) + { + for (int ii = 0; ii < objects.Length; ii++) + { + serializers[ii].SerializeAny(objects[ii], binaryStream); + } + + return binaryStream.ToArray(); + } + } + + public static Object[] Deserialize(int numObjects, byte[] bytes, Serializer[] serializers) + { + Debug.Assert(serializers.Length == numObjects); + + using (var binaryStream = new MemoryStream(bytes)) + { + var result = serializers.Select(serializer => + serializer.DeserializeAny(binaryStream)).ToArray(); + return result; + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/util/SerializerUtil.cs b/NEsper.Core/NEsper.Core/util/SerializerUtil.cs new file mode 100755 index 000000000..c2b7fad94 --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/SerializerUtil.cs @@ -0,0 +1,39 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.util +{ + public class SerializerUtil + { + /// Serialize object to byte array. + /// to serialize + /// byte array + public static byte[] ObjectToByteArr(Object underlying) + { + return SerializerFactory.Serialize( + new[] {SerializerFactory.OBJECT_SERIALIZER}, + new[] {underlying}); + } + + /// Deserialize byte arry to object. + /// to read + /// object + public static Object ByteArrToObject(byte[] bytes) + { + if (bytes == null) + { + return null; + } + + return SerializerFactory.Deserialize( + 1, bytes, new[] { SerializerFactory.OBJECT_SERIALIZER })[0]; + } + } +} diff --git a/NEsper.Core/NEsper.Core/util/SimpleTypeCaster.cs b/NEsper.Core/NEsper.Core/util/SimpleTypeCaster.cs new file mode 100755 index 000000000..2af0071c5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/SimpleTypeCaster.cs @@ -0,0 +1,21 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.util +{ + /// + /// Casts an object to another type, typically for numeric types. + /// + /// May performs a compatibility check and returns null if not compatible. + /// + /// to cast + /// casted or transformed object, possibly the same, or null if the cast cannot be made + public delegate Object SimpleTypeCaster(Object value); +} diff --git a/NEsper.Core/NEsper.Core/util/SimpleTypeCasterFactory.cs b/NEsper.Core/NEsper.Core/util/SimpleTypeCasterFactory.cs new file mode 100755 index 000000000..998bfe148 --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/SimpleTypeCasterFactory.cs @@ -0,0 +1,157 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using com.espertech.esper.compat; + +namespace com.espertech.esper.util +{ + /// + /// Factory for casters, which take an object and safely cast to a given type, + /// performing coercion or dropping precision if required. + /// + public class SimpleTypeCasterFactory + { + /// + /// Returns a caster that casts to a target type. + /// + /// can be null, if not known + /// to cast to + /// + /// caster for casting objects to the required type + /// + public static SimpleTypeCaster GetCaster(Type fromType, Type targetType) + { + if (fromType == targetType) + { + return x => x; // null cast + } + + bool isUnused; + return GetCaster(targetType, out isUnused); + } + + /// + /// Returns a caster that casts to a target type. + /// + /// to cast to + /// + /// caster for casting objects to the required type + /// + public static SimpleTypeCaster GetCaster(Type targetType) + { + bool isUnused; + return GetCaster(targetType, out isUnused); + } + + /// + /// Returns a caster that casts to a target type. + /// + /// to cast to + /// if set to true [is numeric]. + /// + /// caster for casting objects to the required type + /// + public static SimpleTypeCaster GetCaster(Type targetType, out bool isNumeric) + { + isNumeric = true; + + Type baseType = Nullable.GetUnderlyingType(targetType); + if (baseType != null) + { + targetType = baseType; + } + + if (targetType == typeof(Int32)) + { + return CastHelper.PrimitiveCastInt32; + } + if (targetType == typeof(Int64)) + { + return CastHelper.PrimitiveCastInt64; + } + if (targetType == typeof(Int16)) + { + return CastHelper.PrimitiveCastInt16; + } + if (targetType == typeof(SByte)) + { + return CastHelper.PrimitiveCastSByte; + } + if (targetType == typeof(Single)) + { + return CastHelper.PrimitiveCastSingle; + } + if (targetType == typeof(Double)) + { + return CastHelper.PrimitiveCastDouble; + } + if (targetType == typeof(Decimal)) + { + return CastHelper.PrimitiveCastDecimal; + } + if (targetType == typeof(UInt32)) + { + return CastHelper.PrimitiveCastUInt32; + } + if (targetType == typeof(UInt64)) + { + return CastHelper.PrimitiveCastUInt64; + } + if (targetType == typeof(UInt16)) + { + return CastHelper.PrimitiveCastUInt16; + } + if (targetType == typeof(Char)) + { + return CastHelper.PrimitiveCastChar; + } + if (targetType == typeof(Byte)) + { + return CastHelper.PrimitiveCastByte; + } + if (targetType == typeof(System.Numerics.BigInteger)) + { + return CastHelper.PrimitiveCastBigInteger; + } + + isNumeric = false; + + if (targetType == typeof(bool?)) + { + return sourceObj => sourceObj == null ? (object) null : Convert.ToBoolean(sourceObj); + } + + if ( targetType == typeof(string) ) { + return delegate(Object sourceObj) + { + if (sourceObj == null) + { + return null; + } + else if (sourceObj is string) + { + return (string)sourceObj; + } + else + { + return sourceObj.ToString(); + } + }; + } + + return delegate(Object sourceObj) { + if (sourceObj == null) { + return null; + } + var sourceObjType = sourceObj.GetType(); + return targetType.IsAssignableFrom(sourceObjType) ? sourceObj : null; + }; + } + } +} diff --git a/NEsper.Core/NEsper.Core/util/SimpleTypeParser.cs b/NEsper.Core/NEsper.Core/util/SimpleTypeParser.cs new file mode 100755 index 000000000..bc084592d --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/SimpleTypeParser.cs @@ -0,0 +1,15 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.util +{ + /// Parser of a String input to an object. + public delegate Object SimpleTypeParser(String text); +} diff --git a/NEsper.Core/NEsper.Core/util/SimpleTypeParserFactory.cs b/NEsper.Core/NEsper.Core/util/SimpleTypeParserFactory.cs new file mode 100755 index 000000000..f0f54d870 --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/SimpleTypeParserFactory.cs @@ -0,0 +1,131 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Globalization; + +namespace com.espertech.esper.util +{ + /// + /// A factory for creating an instance of a parser that parses a String and returns a target type. + /// + public class SimpleTypeParserFactory + { + /// + /// Returns a parsers for the String value using the given built-in class for parsing. + /// + /// is the type to parse the value to + /// value matching the type passed in + public static SimpleTypeParser GetParser(Type type) + { + Type typeBoxed = TypeHelper.GetBoxedType(type); + + if (typeBoxed == typeof(string)) + { + return value => value; + } + if (typeBoxed == typeof(char?)) + { + return value => value[0]; + } + if (typeBoxed == typeof(bool?)) + { + return value => Boolean.Parse(value); + } + if (typeBoxed == typeof(Guid?)) + { + return value => new Guid(value); + } + + // -- Integers + if (typeBoxed == typeof(byte?)) + { + return delegate(String value) + { + value = value.Trim(); + if (value.StartsWith("0x")) + { + return Byte.Parse(value.Substring(2), NumberStyles.HexNumber); + } + + return Byte.Parse(value.Trim()); + }; + } + if (typeBoxed == typeof(sbyte?)) + { + return value => SByte.Parse(value.Trim()); + } + if (typeBoxed == typeof(short?)) + { + return value => Int16.Parse(value); + } + if (typeBoxed == typeof(ushort?)) + { + return value => UInt16.Parse(value); + } + if (typeBoxed == typeof(int?)) + { + return value => Int32.Parse(value); + } + if (typeBoxed == typeof(uint?)) + { + return value => UInt32.Parse(value); + } + if (typeBoxed == typeof(long?)) + { + return delegate(String value) + { + value = value.TrimEnd('l', 'L', ' '); + return Int64.Parse(value); + }; + } + if (typeBoxed == typeof(ulong?)) + { + return delegate(String value) + { + value = value.TrimEnd('l', 'L', ' '); + return UInt64.Parse(value); + }; + } + + // -- Floating Point + if (typeBoxed == typeof(float?)) + { + return delegate(String value) + { + value = value.TrimEnd('f', 'F', ' '); + return Single.Parse(value); + }; + } + if (typeBoxed == typeof(double?)) + { + return delegate(String value) + { + value = value.TrimEnd('f', 'F', 'd', 'D', ' '); + return Double.Parse(value); + }; + } + if (typeBoxed == typeof(decimal?)) + { + return delegate(String value) + { + value = value.TrimEnd('f', 'F', 'd', 'D', 'M', 'm', ' '); + return Decimal.Parse(value); + }; + } + + var untyped = Nullable.GetUnderlyingType(typeBoxed); + if ((untyped != null) && untyped.IsEnum) + { + return value => Enum.Parse(untyped, value, true); + } + + return null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/util/StatementSelectionUtil.cs b/NEsper.Core/NEsper.Core/util/StatementSelectionUtil.cs new file mode 100755 index 000000000..c6c609024 --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/StatementSelectionUtil.cs @@ -0,0 +1,279 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.client.annotation; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.service; +using com.espertech.esper.core.support; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events.bean; + +namespace com.espertech.esper.util +{ + public class StatementSelectionUtil { + private static readonly BeanEventType STATEMENT_META_EVENT_TYPE; + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + static StatementSelectionUtil() + { + STATEMENT_META_EVENT_TYPE = (BeanEventType) SupportEventAdapterService.Service.AddBeanType("StatementRow", typeof(StatementRow), true, true, true); + } + + public static void ApplyExpressionToStatements( + EPServiceProviderSPI engine, + string filter, + Action consumer) + { + // compile filter + ExprNode filterExpr = null; + var isUseFilter = false; + + if (!String.IsNullOrWhiteSpace(filter)) + { + isUseFilter = true; + var statementExprNode = CompileValidateStatementFilterExpr(engine, filter); + if (statementExprNode.Second == null) + { + filterExpr = statementExprNode.First; + } + } + + IList statementNames = engine.EPAdministrator.StatementNames; + foreach (var statementName in statementNames) + { + var epStmt = engine.EPAdministrator.GetStatement(statementName); + if (epStmt == null) + { + continue; + } + + if (isUseFilter) + { + if (filterExpr != null) + { + if (!EvaluateStatement(filterExpr, epStmt)) + { + continue; + } + } + else + { + var match = false; + string searchString = filter.ToLowerInvariant(); + if ((epStmt.Name != null) && (epStmt.Name.ToLowerInvariant().Contains(searchString))) + { + match = true; + } + if (!match) + { + if ((epStmt.Text != null) && (epStmt.Text.ToLowerInvariant().Contains(searchString))) + { + match = true; + } + if ((epStmt.State != null) && + (epStmt.State.ToString().ToLowerInvariant().Contains(searchString))) + { + match = true; + } + } + if (!match) + { + continue; + } + } + } + + consumer.Invoke(engine, epStmt); + } + } + + public static bool EvaluateStatement(ExprNode expression, EPStatement stmt) + { + if (expression == null) + { + return true; + } + + var evaluator = expression.ExprEvaluator; + if (evaluator.ReturnType.GetBoxedType() != typeof (bool?)) + { + throw new EPException( + "Invalid expression, expected a bool return type for expression and received '" + + evaluator.ReturnType.GetTypeNameFullyQualPretty() + + "' for expression '" + ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(expression) + "'"); + } + + try + { + var row = GetRow(stmt); + var rowBean = SupportEventAdapterService.Service.AdapterForTypedObject(row, STATEMENT_META_EVENT_TYPE); + var evaluateParams = new EvaluateParams(new EventBean[] { rowBean }, true, null); + var pass = (bool?) evaluator.Evaluate(evaluateParams); + return !pass.GetValueOrDefault(false); + } + catch (Exception ex) + { + Log.Error( + "Unexpected exception filtering statements by expression, skipping statement: " + ex.Message, ex); + } + return false; + } + + public static Pair CompileValidateStatementFilterExpr( + EPServiceProviderSPI engine, + string filterExpression) + { + ExprNode exprNode; + try + { + var spi = (EPAdministratorSPI) engine.EPAdministrator; + exprNode = spi.CompileExpression(filterExpression); + } + catch (Exception ex) + { + return new Pair(null, "Error compiling expression: " + ex.Message); + } + + try + { + var streamTypeService = new StreamTypeServiceImpl(STATEMENT_META_EVENT_TYPE, null, true, engine.URI); + exprNode = ExprNodeUtility.GetValidatedSubtree( + ExprNodeOrigin.SCRIPTPARAMS, exprNode, + new ExprValidationContext( + streamTypeService, + engine.EngineImportService, + null, null, + engine.TimeProvider, null, null, null, + engine.EventAdapterService, "no-name-assigned", -1, + null, null, null, true, false, false, false, null, true)); + } + catch (Exception ex) + { + return new Pair(null, "Error validating expression: " + ex.Message); + } + return new Pair(exprNode, null); + } + + // Predefined properties available: + // - name (string) + // - description (string) + // - epl (string) + // - each tag individually (string) + // - priority + // - drop (bool) + // - hint (string) + private static StatementRow GetRow(EPStatement statement) + { + string description = null; + string hint = null; + var hintDelimiter = ""; + var priority = 0; + IDictionary tags = null; + var drop = false; + + var annotations = statement.Annotations; + foreach (var anno in annotations) + { + if (anno is HintAttribute) { + if (hint == null) { + hint = ""; + } + hint += hintDelimiter + ((HintAttribute) anno).Value; + hintDelimiter = ","; + } + else if (anno is TagAttribute) + { + var tag = (TagAttribute)anno; + if (tags == null) { + tags = new Dictionary(); + } + tags.Put(tag.Name, tag.Value); + } + else if (anno is PriorityAttribute) + { + priority = ((PriorityAttribute) anno).Value; + } + else if (anno is DropAttribute) + { + drop = true; + } + else if (anno is DescriptionAttribute) + { + description = ((DescriptionAttribute)anno).Value; + } + } + + var name = statement.Name; + var text = statement.Text; + var state = statement.State.ToString(); + var userObject = statement.UserObject; + + return new StatementRow( + name, + text, + state, + userObject, + description, + hint, + priority, + drop, + tags + ); + } + + public class StatementRow + { + public StatementRow( + string name, + string epl, + string state, + Object userObject, + string description, + string hint, + int priority, + bool? drop, + IDictionary tag) + { + Name = name; + Epl = epl; + State = state; + UserObject = userObject; + Description = description; + Hint = hint; + Priority = priority; + IsDrop = drop; + Tag = tag; + } + + public string Name { get; set; } + + public string Epl { get; set; } + + public string State { get; set; } + + public object UserObject { get; set; } + + public string Description { get; set; } + + public string Hint { get; set; } + + public int Priority { get; set; } + + public bool? IsDrop { get; set; } + + public IDictionary Tag { get; set; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/util/StopCallback.cs b/NEsper.Core/NEsper.Core/util/StopCallback.cs new file mode 100755 index 000000000..4f3709707 --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/StopCallback.cs @@ -0,0 +1,56 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.util +{ + /// + /// General purpose callback to Stop a resource and free it's underlying resources. + /// + + public interface StopCallback + { + void Stop(); + } + + public class ProxyStopCallback : StopCallback + { + public Action ProcStop; + + public ProxyStopCallback() { } + public ProxyStopCallback(Action procStop) + { + ProcStop = procStop; + } + + public void Stop() + { + ProcStop.Invoke(); + } + + private bool Equals(ProxyStopCallback other) + { + return Equals(ProcStop, other.ProcStop); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) + return false; + if (ReferenceEquals(this, obj)) + return true; + return obj is ProxyStopCallback && Equals((ProxyStopCallback) obj); + } + + public override int GetHashCode() + { + return (ProcStop != null ? ProcStop.GetHashCode() : 0); + } + } +} diff --git a/NEsper.Core/NEsper.Core/util/StopCallbackNoAction.cs b/NEsper.Core/NEsper.Core/util/StopCallbackNoAction.cs new file mode 100755 index 000000000..e46cf055b --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/StopCallbackNoAction.cs @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; + +namespace com.espertech.esper.util +{ + /// + /// Stop callback that performs no action. + /// + public class StopCallbackNoAction : StopCallback + { + public readonly static StopCallbackNoAction INSTANCE = new StopCallbackNoAction(); + + /// + /// Stops this instance. + /// + public void Stop() + { + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/util/ThreadLogUtil.cs b/NEsper.Core/NEsper.Core/util/ThreadLogUtil.cs new file mode 100755 index 000000000..3b8e7b2b9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/ThreadLogUtil.cs @@ -0,0 +1,129 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.compat.threading; + +namespace com.espertech.esper.util +{ + /// + /// Utility class for logging threading-related messages. + /// + /// Prints thread information and lock-specific INFO. + /// + /// + public class ThreadLogUtil + { + /// Enable TRACE logging. + public static readonly bool ENABLED_TRACE = false; + + /// Enable INFO logging. + public static readonly bool ENABLED_INFO = false; + + private static readonly ILog Log = + LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + /// Set TRACE log level. + public static int TRACE = 0; + + /// Set INFO log level. + public static int INFO = 1; + + /// + /// If enabled, logs for TRACE level the given objects and text + /// + /// to log + /// to write + public static void Trace(string text, params Object[] objects) + { + if (!ENABLED_TRACE) + { + return; + } + Write(text, objects); + } + + /// + /// If enabled, logs for INFO level the given objects and text + /// + /// to log + /// to write + public static void Info(string text, params Object[] objects) + { + if (!ENABLED_INFO) + { + return; + } + Write(text, objects); + } + + /// + /// Logs the lock and action. + /// + /// is the action towards the lock + /// is the lock instance + public static void TraceLock(string lockAction, IReaderWriterLock @lock) + { + if (!ENABLED_TRACE) + { + return; + } + Write(lockAction + " " + GetLockInfo(@lock)); + } + + private static String GetLockInfo(Object lockObj) + { + String lockid = String.Format("Lock@{0:X8}", Marshal.GetIUnknownForObject(lockObj).ToInt64()); + return "lock " + lockid; + //return "lock " + lockid + " held=" + lockObj.HoldCount + " isHeldMe=" + lockObj.IsHeldByCurrentThread() + + // " hasQueueThreads=" + lockObj.HasQueuedThreads(); + } + + private static String GetLockInfo(IReaderWriterLock @lockObj) + { + String lockid = String.Format("RWLock@{0:X}", lockObj.GetHashCode()); + return lockid + + //" readLockCount=" + lockObj.ReadLockCount + + " isWriteLocked=" + lockObj.IsWriterLockHeld; + } + + private static void Write(String text, params Object[] objects) + { + StringBuilder buf = new StringBuilder(); + buf.Append(text); + buf.Append(' '); + foreach (Object obj in objects) + { + if ((obj is String) || (obj is ValueType)) + { + buf.Append(obj.ToString()); + } + else + { + buf.Append(obj.GetType().FullName); + buf.Append('@'); + buf.Append(String.Format("{0:X2}", obj.GetHashCode())); + } + buf.Append(' '); + } + Write(buf.ToString()); + } + + private static void Write(String text) + { + Log.Info(".write Thread " + Thread.CurrentThread.ManagedThreadId + " " + text); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/util/TransientConfigurationResolver.cs b/NEsper.Core/NEsper.Core/util/TransientConfigurationResolver.cs new file mode 100755 index 000000000..e374f9a38 --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/TransientConfigurationResolver.cs @@ -0,0 +1,75 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.client.util; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; + +namespace com.espertech.esper.util +{ + public class TransientConfigurationResolver + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public static ClassForNameProvider ResolveClassForNameProvider( + IDictionary transientConfiguration) + { + return Resolve( + transientConfiguration, + ClassForNameProviderDefault.INSTANCE, + ClassForNameProviderDefault.NAME, + typeof (ClassForNameProvider)); + } + + public static FastClassClassLoaderProvider ResolveFastClassClassLoaderProvider( + IDictionary transientConfiguration) + { + return Resolve( + transientConfiguration, + FastClassClassLoaderProviderDefault.INSTANCE, + FastClassClassLoaderProviderDefault.NAME, + typeof (FastClassClassLoaderProvider)); + } + + public static ClassLoaderProvider ResolveClassLoader(IDictionary transientConfiguration) + { + return Resolve( + transientConfiguration, ClassLoaderProviderDefault.INSTANCE, ClassLoaderProviderDefault.NAME, + typeof (ClassLoaderProvider)); + } + + private static T Resolve( + IDictionary transientConfiguration, + T defaultProvider, + string name, + Type interfaceClass) + { + if (transientConfiguration == null) + { + return defaultProvider; + } + object value = transientConfiguration.Get(name); + if (value == null) + { + return defaultProvider; + } + if (!value.GetType().IsImplementsInterface(interfaceClass)) + { + Log.Warn( + "For transient configuration '" + name + "' expected an object implementing " + interfaceClass.Name + + " but received " + value.GetType() + ", using default provider"); + return defaultProvider; + } + return (T) value; + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/util/TriFunction.cs b/NEsper.Core/NEsper.Core/util/TriFunction.cs new file mode 100755 index 000000000..05fa7fcdc --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/TriFunction.cs @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; + +namespace com.espertech.esper.util +{ + public interface TriFunction { + R Apply(A a, B b, C c); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/util/TypeHelper.cs b/NEsper.Core/NEsper.Core/util/TypeHelper.cs new file mode 100755 index 000000000..86315c1c5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/TypeHelper.cs @@ -0,0 +1,3135 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Numerics; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Text; +using System.Xml; + +using com.espertech.esper.client; +using com.espertech.esper.client.annotation; +using com.espertech.esper.client.util; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.compat.magic; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events.avro; +using com.espertech.esper.type; + +namespace com.espertech.esper.util +{ + using DataMap = IDictionary; + using TypeParser = Func; + + /// + /// Helper for questions about types. + /// what is the boxed type for a primitive type + /// is this a numeric type. + /// + public static class TypeHelper + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private static readonly IDictionary BoxedTable; + private static readonly IDictionary ParserTable; + + /// + /// When provided allows for applications to define the search path for assemblies. + /// In the absence of this, the AppDomain is queried for assemblies to search. + /// + public static Func> AssemblySearchPath { get; set; } + + /// + /// When provided allows for applications to define the entire mechanism for + /// resolving a type. In the absence, the default search algorithm is used which + /// employs the AssemblySearchPath. + /// + public static Func TypeResolver { get; set; } + + /// + /// Integral types (used for testing) + /// + + private static readonly Type[] IntegralTypes = new[] + { + typeof (sbyte?), typeof(sbyte), + typeof (byte?), typeof(byte), + typeof (short?), typeof(short), + typeof (ushort?), typeof(ushort), + typeof (int?), typeof(int), + typeof (uint?), typeof(uint), + typeof (long?), typeof(long), + typeof (ulong?), typeof(ulong) + }; + + private static readonly Dictionary IntegralTable; + + /// + /// Initializes the class. + /// + static TypeHelper() + { + BoxedTable = new Dictionary(); + BoxedTable[typeof(int)] = typeof(int?); + BoxedTable[typeof(long)] = typeof(long?); + BoxedTable[typeof(bool)] = typeof(bool?); + BoxedTable[typeof(char)] = typeof(char?); + BoxedTable[typeof(decimal)] = typeof(decimal?); + BoxedTable[typeof(double)] = typeof(double?); + BoxedTable[typeof(float)] = typeof(float?); + BoxedTable[typeof(sbyte)] = typeof(sbyte?); + BoxedTable[typeof(byte)] = typeof(byte?); + BoxedTable[typeof(short)] = typeof(short?); + BoxedTable[typeof(ushort)] = typeof(ushort?); + BoxedTable[typeof(uint)] = typeof(uint?); + BoxedTable[typeof(ulong)] = typeof(ulong?); + BoxedTable[typeof(BigInteger)] = typeof (BigInteger?); + + IntegralTable = new Dictionary(); + for (int ii = 0; ii < IntegralTypes.Length; ii++) + { + IntegralTable[IntegralTypes[ii]] = ii; + } + + // Build the type parser table; converts a string to its correct type. + // Could the Converter be used to do facilitate this? + + ParserTable = new Dictionary(); + ParserTable[typeof(string)] = s => s; + ParserTable[typeof(bool?)] = s => BoolValue.ParseString(s.Trim()); + ParserTable[typeof(char?)] = s => s[0]; + ParserTable[typeof(DateTime?)] = s => DateTime.Parse(s.Trim()); + ParserTable[typeof(DateTimeOffset?)] = s => DateTimeParser.ParseDefault(s.Trim()); + + ParserTable[typeof(decimal?)] = s => DecimalValue.ParseString(s.Trim()); + ParserTable[typeof(double?)] = s => DoubleValue.ParseString(s.Trim()); + ParserTable[typeof(float?)] = s => FloatValue.ParseString(s.Trim()); + + ParserTable[typeof(sbyte?)] = s => SByteValue.ParseString(s.Trim()); + ParserTable[typeof(byte?)] = s => ByteValue.ParseString(s.Trim()); + ParserTable[typeof(short?)] = s => ShortValue.ParseString(s.Trim()); + ParserTable[typeof(int?)] = s => IntValue.ParseString(s.Trim()); + ParserTable[typeof(long?)] = s => LongValue.ParseString(s.Trim()); + + ParserTable[typeof(ushort?)] = s => UInt16.Parse(s.Trim()); + ParserTable[typeof(uint?)] = s => UInt32.Parse(s.Trim()); + ParserTable[typeof(ulong?)] = s => + { + s = s.Trim(); + if ((s.EndsWith("L")) || ((s.EndsWith("l")))) + { + s = s.Substring(0, s.Length - 1); + } + return UInt64.Parse(s); + }; + + ParserTable[typeof(BigInteger?)] = s => BigInteger.Parse(s.Trim()); + } + + /// + /// Gets the parameter as string. + /// + /// The parameters. + /// + public static String GetParameterAsString(IEnumerable parameters) + { + return GetParameterAsString(parameters.Select(param => param.ParameterType)); + } + + /// + /// Returns a comma-separated parameter type list in readable form,considering arrays and null-type parameters. + /// + /// is the parameter types to render + /// if set to true [use full name]. + /// rendered list of parameters + public static String GetParameterAsString(IEnumerable parameters, bool useFullName = true) + { + var builder = new StringBuilder(); + var delimiterComma = ", "; + var delimiter = ""; + foreach (Type param in parameters) + { + builder.Append(delimiter); + builder.Append(GetParameterAsString(param, useFullName)); + delimiter = delimiterComma; + } + return builder.ToString(); + } + + /// + /// Returns a parameter as a string text, allowing null values to represent a nullselect expression type. + /// + /// is the parameter type + /// if set to true [use full name]. + /// string representation of parameter + public static String GetParameterAsString(this Type param, bool useFullName = true) + { + return GetCleanName(param, useFullName); + } + + public static string GetCleanName(this Type type, bool useFullName = true) + { + if (type == null) + { + return "null (any type)"; + } + + if (type.IsGenericType) + { + var genericName = useFullName + ? type.FullName ?? type.Name + : type.Name; + var index = genericName.IndexOf('`'); + if (index != -1) + { + genericName = genericName.Substring(0, index); + } + + var separator = ""; + var builder = new StringBuilder(); + builder.Append(genericName); + builder.Append('<'); + foreach (var genericType in type.GetGenericArguments()) + { + builder.Append(separator); + builder.Append(GetCleanName(genericType, useFullName)); + separator = ", "; + } + builder.Append('>'); + return builder.ToString(); + } + + return useFullName ? type.FullName : type.Name; + } + + public static string GetCleanName(bool useFullName = true) + { + return GetCleanName(typeof (T), useFullName); + } + + /// + /// Determines whether the specified type is comparable. + /// + /// The type. + /// + public static bool IsComparable(this Type type) + { + return type.GetUnboxedType().IsImplementsInterface(); + } + + /// + /// Gets the unboxed type for the value. + /// + /// The type. + /// + public static Type GetUnboxedType(this Type type) + { + var unboxed = Nullable.GetUnderlyingType(type); + if (unboxed == null) + return type; + return unboxed; + } + + /// + /// Gets the boxed type for the value. + /// + /// Any. + /// + public static Type GetBoxedType(this object any) + { + if (any is Type) + return GetBoxedType((Type)any); + else if (any == null) + return null; + + return any.GetType().GetBoxedType(); + } + + /// + /// Returns the boxed class for the given class, or the class itself if already boxed or not a primitive type. + /// For primitive unboxed types returns the boxed types, e.g. returns typeof(int?) for passing typeof(int). + /// For type other class, returns the class passed. + /// + /// is the type to return the boxed type for + + public static Type GetBoxedType(this Type type) + { + if (type == null) return null; + if (type == typeof(void)) return null; + if (type == typeof(int) || type == typeof(int?)) return typeof(int?); + if (type == typeof(long) || type == typeof(long?)) return typeof(long?); + if (type == typeof(bool) || type == typeof(bool?)) return typeof(bool?); + if (type == typeof(double) || type == typeof(double?)) return typeof(double?); + if (type == typeof(decimal) || type == typeof(decimal?)) return typeof(decimal?); + if (type == typeof(float) || type == typeof(float?)) return typeof(float?); + if (type == typeof(BigInteger) || type == typeof(BigInteger)) return typeof (BigInteger?); + + Type boxed; + if (BoxedTable.TryGetValue(type, out boxed)) + { + return boxed; + } + + if (type.IsNullable()) + { + return type; + } + + if (type.IsValueType) + { + boxed = typeof(Nullable<>).MakeGenericType(type); + BoxedTable[type] = boxed; + return boxed; + } + + return type; + } + + public static bool IsBoxedType(this Type type) + { + return (type.GetBoxedType() == typeof(T).GetBoxedType()); + } + + /// + /// Returns the un-boxed class for the given class, or the class itself if already un-boxed or not a primitive type. + /// For primitive boxed types returns the unboxed primitive type, e.g. returns typeof(int) for passing typeof(int?). + /// For type other class, returns the class passed. + /// + /// + /// is the class to return the unboxed (or primitive) class for + /// + /// primitive variant of the same class + public static Type GetPrimitiveType(this Type type) + { + if (type == typeof(bool?)) + { + return typeof(bool); + } + if (type == typeof(char?)) + { + return typeof(char); + } + if (type == typeof(decimal?)) + { + return typeof(decimal); + } + if (type == typeof(double?)) + { + return typeof(double); + } + if (type == typeof(float?)) + { + return typeof(float); + } + if (type == typeof(sbyte?)) + { + return typeof(sbyte); + } + if (type == typeof(byte?)) + { + return typeof(byte); + } + if (type == typeof(short?)) + { + return typeof(short); + } + if (type == typeof(ushort?)) + { + return typeof(ushort); + } + if (type == typeof(int?)) + { + return typeof(int); + } + if (type == typeof(uint?)) + { + return typeof(uint); + } + if (type == typeof(long?)) + { + return typeof(long); + } + if (type == typeof(ulong?)) + { + return typeof(ulong); + } + return type; + } + + /// + /// Returns for the class name given the class name of the boxed (wrapped) type if + /// the class name is one of the CLR primitive types. + /// + /// a type name, a CLR primitive type or other class + /// boxed type name if CLR primitive type, or just same class name passed in if not a primitive type + + public static String GetBoxedTypeName(String typeName) + { + if (typeName == typeof(char).FullName) + { + return typeof(char?).FullName; + } + if (typeName == typeof(sbyte).FullName) + { + return typeof(sbyte?).FullName; + } + if (typeName == typeof(byte).FullName) + { + return typeof(byte?).FullName; + } + if ((typeName == typeof(short).FullName) || + (typeName == "short")) + { + return typeof(short?).FullName; + } + if ((typeName == typeof(ushort).FullName) || + (typeName == "ushort")) + { + return typeof(ushort?).FullName; + } + if ((typeName == typeof(int).FullName) || + (typeName == "int")) + { + return typeof(int?).FullName; + } + if ((typeName == typeof(uint).FullName) || + (typeName == "uint")) + { + return typeof(uint?).FullName; + } + if ((typeName == typeof(long).FullName) || + (typeName == "long")) + { + return typeof(long?).FullName; + } + if ((typeName == typeof(ulong).FullName) || + (typeName == "ulong")) + { + return typeof(ulong?).FullName; + } + if ((typeName == typeof(float).FullName) || + (typeName == "float")) + { + return typeof(float?).FullName; + } + if ((typeName == typeof(double).FullName) || + (typeName == "double")) + { + return typeof(double?).FullName; + } + if ((typeName == typeof(decimal).FullName) || + (typeName == "decimal")) + { + return typeof(decimal?).FullName; + } + if ((typeName == typeof(bool).FullName) || + (typeName == "bool") || + (typeName == "bool")) + { + return typeof(bool?).FullName; + } + if ((typeName == typeof(BigInteger).FullName) || + (typeName == "biginteger") || + (typeName == "bigint")) + { + return typeof(BigInteger?).FullName; + } + if (String.Equals(typeName, "string", StringComparison.OrdinalIgnoreCase)) + { + typeName = typeof(string).FullName; + } + return typeName; + } + + /// + /// Determines whether the specified type is bool. + /// + /// The type. + /// + /// true if the specified type is bool; otherwise, false. + /// + public static bool IsBoolean(this Type type) + { + return + (type == typeof(bool)) || + (type == typeof(bool?)) + ; + } + + /// + /// Returns true if the type represents a character type. + /// + /// + /// + + public static bool IsCharacter(this Type type) + { + return + (type == typeof(char)) || + (type == typeof(char?)) + ; + } + + /// + /// Returns true if the type represents a floating point numeric type. + /// + /// + /// + + public static bool IsFloatingPoint(this Type type) + { + return + (type == typeof(float)) || + (type == typeof(float?)) || + (type == typeof(double)) || + (type == typeof(double?)) || + (type == typeof(decimal)) || + (type == typeof(decimal?)) + ; + } + + /// + /// Determines whether the specified type is integral. + /// + /// The type. + /// Widest integral type. + /// + /// true if the specified type is integral; otherwise, false. + /// + public static bool IsIntegral(this Type type, Type maxIntegralType) + { + int intIndex; + if (IntegralTable.TryGetValue(type, out intIndex)) + { + int maxIndex; + if (IntegralTable.TryGetValue(maxIntegralType, out maxIndex)) + { + return intIndex <= maxIndex; + } + } + + return false; + } + + /// Determines if the type passed in is one of the integral numeric types. + /// to check + public static bool IsIntegral(this Type type) + { + return + (type == typeof(int?)) || + (type == typeof(int)) || + (type == typeof(long?)) || + (type == typeof(long)) || + (type == typeof(short?)) || + (type == typeof(short)) || + (type == typeof(sbyte?)) || + (type == typeof(sbyte)) || + (type == typeof(byte?)) || + (type == typeof(byte)) || + (type == typeof(ushort?)) || + (type == typeof(ushort)) || + (type == typeof(uint?)) || + (type == typeof(uint)) || + (type == typeof(ulong?)) || + (type == typeof(ulong)); + } + + /// + /// Determines if the type passed in is one of the integral numeric types. + /// + /// to check + /// Type of the max integral. + /// + /// true if [is integral number] [the specified value]; otherwise, false. + /// + public static bool IsIntegralNumber(this Object value, Type maxIntegralType) + { + return (value != null) && (IsIntegral(value.GetType(), maxIntegralType)); + } + + /// Determines if the type passed in is one of the integral numeric types. + /// to check + public static bool IsIntegralNumber(this Object value) + { + return (value != null) && (IsIntegral(value.GetType())); + } + + /// Determines if the type passed in is one of the numeric types. + /// to check + /// true if numeric, false if not + /// + public static bool IsNumeric(this Type type) + { + if (type == null) + return false; + + if (type == typeof(int) || + type == typeof(long) || + type == typeof(double) || + type == typeof(float) || + type == typeof(decimal) || + type == typeof(BigInteger)) + return true; + + if (type.IsGenericType) + return + (type == typeof(int?)) || + (type == typeof(long?)) || + (type == typeof(decimal?)) || + (type == typeof(double?)) || + (type == typeof(float?)) || + (type == typeof(short?)) || + (type == typeof(ushort?)) || + (type == typeof(uint?)) || + (type == typeof(ulong?)) || + (type == typeof(sbyte?)) || + (type == typeof(byte?)) || + (type == typeof(BigInteger?)); + + return + (type == typeof(short)) || + (type == typeof(ushort)) || + (type == typeof(uint)) || + (type == typeof(ulong)) || + (type == typeof(sbyte)) || + (type == typeof(byte)); + } + + /// Determines if the class passed in is one of the numeric classes and not a floating point. + /// type to check + /// true if numeric and not a floating point, false if not + public static bool IsNumericNonFP(this Type type) + { + return IsNumeric(type) && !IsFloatingPoint(type); + } + + /// Determines if the value passed in is one of the numeric types. + /// to check + /// true if numeric, false if not + + public static bool IsNumber(this Object value) + { + return (value != null) && (IsNumeric(value.GetType())); + } + + public static bool IsDecimal(this Type type) + { + return (type == typeof (decimal)) || + (type == typeof (decimal?)); + } + + public static bool IsBigInteger(this Type type) + { + return (type == typeof(BigInteger)) || + (type == typeof(BigInteger?)); + } + + /// + /// Returns the coercion type for the 2 numeric types for use in arithmatic. + /// Note: byte and short types always result in integer. + /// + /// The first type. + /// The second type. + /// coerced type + /// CoercionException if types don'type allow coercion + + public static Type GetArithmaticCoercionType(this Type typeOne, Type typeTwo) + { + Type boxedOne = GetBoxedType(typeOne); + Type boxedTwo = GetBoxedType(typeTwo); + + if (!IsNumeric(boxedOne) || !IsNumeric(boxedTwo)) + { + throw new CoercionException("Cannot coerce types " + typeOne.FullName + " and " + typeTwo.FullName); + } + + if ((boxedOne == typeof(decimal?)) || + (boxedTwo == typeof(decimal?))) + { + return typeof(decimal?); + } + if (((boxedOne == typeof(BigInteger?) && IsFloatingPointClass(boxedTwo)) || + ((boxedTwo == typeof(BigInteger?) && IsFloatingPointClass(boxedOne))))) + { + return typeof(decimal?); + } + if ((boxedOne == typeof(BigInteger?)) || + (boxedTwo == typeof(BigInteger?))) + { + return typeof(BigInteger?); + } + + if ((boxedOne == typeof(double?)) || + (boxedTwo == typeof(double?))) + { + return typeof(double?); + } + + if ((boxedOne == typeof(float?)) && (!IsFloatingPointClass(typeTwo))) + { + return typeof(double?); + } + if ((boxedTwo == typeof(float?)) && (!IsFloatingPointClass(typeOne))) + { + return typeof(double?); + } + + if ((boxedOne == typeof(float?)) || + (boxedTwo == typeof(float?))) + { + return typeof(float?); + } + + if ((boxedOne == typeof(ulong?)) || + (boxedTwo == typeof(ulong?))) + { + return typeof(ulong?); + } + if ((boxedOne == typeof(long?)) || + (boxedTwo == typeof(long?))) + { + return typeof(long?); + } + if ((boxedOne == typeof(uint?)) || + (boxedTwo == typeof(uint?))) + { + return typeof(uint?); + } + return typeof(int?); + } + + public static bool IsLongNumber(this Object number) + { + return + (number is long) || + (number is ulong); + } + + /// + /// Returns true if the Number instance is a floating point number. + /// + /// to check + /// true if number is Float or double type + + public static bool IsFloatingPointNumber(this Object number) + { + return + (number is float) || + (number is double) || + (number is decimal); + } + + /// + /// Returns true if the supplied type is a floating point number. + /// + /// to check + /// + /// true if primitive or boxed float or double + /// + public static bool IsFloatingPointClass(this Type type) + { + return + (type == typeof(float?)) || + (type == typeof(float)) || + (type == typeof(double?)) || + (type == typeof(double)) + ; + } + + /// + /// Returns for 2 classes to be compared via relational operator the Class type of + /// common comparison. The output is always typeof(long?), typeof(double), typeof(String) or typeof(bool) + /// depending on whether the passed types are numeric and floating-point. + /// Accepts primitive as well as boxed types. + /// + /// The first type. + /// The second type. + /// + /// One of typeof(long?), typeof(double) or typeof(String) + /// + /// ArgumentException if the types cannot be compared + + public static Type GetCompareToCoercionType(this Type typeOne, Type typeTwo) + { + if (typeOne == typeTwo) + { + return typeOne; + } + else if (typeOne == null) + { + return typeTwo; + } + else if (typeTwo == null) + { + return typeOne; + } + else if (typeOne.GetBoxedType() == typeTwo.GetBoxedType()) + { + return typeOne.GetBoxedType(); + } + + if (IsNumeric(typeOne) && IsNumeric(typeTwo)) + { + return GetArithmaticCoercionType(typeOne, typeTwo); + } + + if (!IsBuiltinDataType(typeOne) && !IsBuiltinDataType(typeTwo) && (typeOne != typeTwo)) + { + return typeof(object); + } + + throw new CoercionException(string.Format("Types cannot be compared: {0} and {1}", + typeOne.FullName, + typeTwo.FullName)); + } + + /// + /// Determines if a number can be coerced upwards to another number class without loss. + /// + /// Clients must pass in two classes that are numeric types. + /// + /// + /// Any number class can be coerced to double, while only double cannot be coerced to float. + /// Any non-floating point number can be coerced to long. + /// Integer can be coerced to Byte and Short even though loss is possible, for convenience. + /// + /// + /// the number class to be coerced + /// the number class to coerce to + /// true if numbers can be coerced without loss, false if not + public static bool CanCoerce(this Type numberClassToBeCoerced, Type numberClassToCoerceTo) + { + Type boxedFrom = GetBoxedType(numberClassToBeCoerced); + Type boxedTo = GetBoxedType(numberClassToCoerceTo); + + if (!IsNumeric(numberClassToBeCoerced)) + { + throw new CoercionException("Type '" + numberClassToBeCoerced + "' is not a numeric type'"); + } + + if (boxedTo == typeof(float?)) + { + return ((boxedFrom == typeof(byte?)) || + (boxedFrom == typeof(sbyte?)) || + (boxedFrom == typeof(short?)) || + (boxedFrom == typeof(ushort?)) || + (boxedFrom == typeof(int?)) || + (boxedFrom == typeof(uint?)) || + (boxedFrom == typeof(long?)) || + (boxedFrom == typeof(ulong?)) || + (boxedFrom == typeof(float?))); + } + else if (boxedTo == typeof(double?)) + { + return ((boxedFrom == typeof(byte?)) || + (boxedFrom == typeof(sbyte?)) || + (boxedFrom == typeof(short?)) || + (boxedFrom == typeof(ushort?)) || + (boxedFrom == typeof(int?)) || + (boxedFrom == typeof(uint?)) || + (boxedFrom == typeof(long?)) || + (boxedFrom == typeof(ulong?)) || + (boxedFrom == typeof(float?)) || + (boxedFrom == typeof(double?))); + } + else if (boxedTo == typeof(decimal?)) + { + return ((boxedFrom == typeof(byte?)) || + (boxedFrom == typeof(sbyte?)) || + (boxedFrom == typeof(short?)) || + (boxedFrom == typeof(ushort?)) || + (boxedFrom == typeof(int?)) || + (boxedFrom == typeof(uint?)) || + (boxedFrom == typeof(long?)) || + (boxedFrom == typeof(ulong?)) || + (boxedFrom == typeof(float?)) || + (boxedFrom == typeof(double?)) || + (boxedFrom == typeof(decimal?))); + } + else if (boxedTo == typeof(long?)) + { + return ((boxedFrom == typeof(byte?)) || + (boxedFrom == typeof(sbyte?)) || + (boxedFrom == typeof(short?)) || + (boxedFrom == typeof(ushort?)) || + (boxedFrom == typeof(int?)) || + (boxedFrom == typeof(uint?)) || + (boxedFrom == typeof(long?))); + } + else if ((boxedTo == typeof(int?)) || + (boxedTo == typeof(short?)) || + (boxedTo == typeof(ushort?)) || + (boxedTo == typeof(byte?)) || + (boxedTo == typeof(sbyte?))) + { + return ((boxedFrom == typeof(byte?)) || + (boxedFrom == typeof(sbyte?)) || + (boxedFrom == typeof(short?)) || + (boxedFrom == typeof(ushort?)) || + (boxedFrom == typeof(int?))); + } + else if (boxedTo == typeof (BigInteger?)) + { + return ((boxedFrom == typeof(byte?)) || + (boxedFrom == typeof(sbyte?)) || + (boxedFrom == typeof(short?)) || + (boxedFrom == typeof(ushort?)) || + (boxedFrom == typeof(int?)) || + (boxedFrom == typeof(uint?)) || + (boxedFrom == typeof(long?)) || + (boxedFrom == typeof(ulong?)) || + (boxedFrom == typeof(float?)) || + (boxedFrom == typeof(double?)) || + (boxedFrom == typeof(decimal?))); + } + else + { + throw new CoercionException("Type '" + numberClassToCoerceTo + "' is not a numeric type'"); + } + } + /// + /// Returns true if the class passed in is a built-in data type (primitive or wrapper) + /// including String. + /// + /// The type. + /// + /// true if built-in data type, or false if not + /// + public static bool IsBuiltinDataType(this Type type) + { + if (type == null) + { + return true; + } + + Type typeBoxed = GetBoxedType(type); + + if (IsNumeric(typeBoxed) || + IsBoolean(typeBoxed) || + IsCharacter(typeBoxed) || + (type == typeof(string))) + { + return true; + } + + return false; + } + + /// + /// Returns true if 2 classes are assignment compatible. + /// + /// type to assign from + /// type to assign to + /// + /// true if assignment compatible, false if not + /// + + public static bool IsAssignmentCompatible(this Type invocationType, Type declarationType) + { + if (invocationType == null) + { + return true; + } + + if (invocationType.IsAssignableFrom(declarationType)) + { + return true; + } + + if (invocationType.IsValueType) + { + Type parameterWrapperType = GetBoxedType(invocationType); + if (parameterWrapperType != null) + { + if (parameterWrapperType.Equals(declarationType)) + { + return true; + } + } + } + + if (GetBoxedType(invocationType) == declarationType) + { + return true; + } + + ICollection widenings = MethodResolver.WIDENING_CONVERSIONS.Get(declarationType); + if (widenings != null) + { + return widenings.Contains(invocationType); + } + + if (declarationType.IsInterface) + { + if (IsImplementsInterface(invocationType, declarationType)) + { + return true; + } + } + + return RecursiveIsSuperClass(invocationType, declarationType); + } + + /// + /// Determines a common denominator type to which one or more types can be casted or coerced. + /// For use in determining the result type in certain expressions (coalesce, case). + /// + /// Null values are allowed as part of the input and indicate a 'null' constant value + /// in an expression tree. Such as value doesn'type have type type and can be ignored in + /// determining a result type. + /// + /// + /// For numeric types, determines a coercion type that all types can be converted to + /// via the method GetArithmaticCoercionType. + /// + /// + /// Indicates that there is no common denominator type by throwing . + /// + /// + /// is an array of one or more types, which can be built-in (primitive or wrapper) + /// or user types + /// + /// common denominator type if type can be found, for use in comparison + /// + /// CoercionException + + public static Type GetCommonCoercionType(IList types) + { + if (types.Count < 1) + { + throw new ArgumentException("Unexpected zero length array"); + } + + if (types.Count == 1) + { + return GetBoxedType(types[0]); + } + + // Reduce to non-null types + List nonNullTypes = new List(); + for (int i = 0; i < types.Count; i++) + { + if (types[i] != null) + { + nonNullTypes.Add(types[i]); + } + } + + types = nonNullTypes.ToArray(); + if (types.Count == 0) + { + return null; // only null types, result is null + } + + if (types.Count == 1) + { + return GetBoxedType(types[0]); + } + + // Check if all String + if (types[0] == typeof(String)) + { + for (int i = 0; i < types.Count; i++) + { + if (types[i] != typeof(String)) + { + throw new CoercionException("Cannot coerce to String type " + types[i].FullName); + } + } + + return typeof(String); + } + + // Convert to boxed types + for (int i = 0; i < types.Count; i++) + { + types[i] = GetBoxedType(types[i]); + } + + // Check if all bool + if (types[0] == typeof(bool?)) + { + for (int i = 0; i < types.Count; i++) + { + if (types[i] != typeof(bool?)) + { + throw new CoercionException("Cannot coerce to bool type " + types[i].FullName); + } + } + + return typeof(bool?); + } + + // Check if all char + if (types[0] == typeof(char?)) + { + for (int i = 0; i < types.Count; i++) + { + if (types[i] != typeof(char?)) + { + throw new CoercionException("Cannot coerce to bool type " + types[i].FullName); + } + } + + return typeof(char?); + } + + // Check if all the same builtin type + bool isAllBuiltinTypes = true; + bool isAllNumeric = true; + foreach (var type in types) + { + if (!IsNumeric(type) && (!IsBuiltinDataType(type))) + { + isAllBuiltinTypes = false; + } + } + + // handle all built-in types + if (!isAllBuiltinTypes) + { + foreach (var type in types) + { + if (IsBuiltinDataType(type)) + { + throw new CoercionException("Cannot coerce to " + types[0].FullName + " type " + type.FullName); + } + if (type != types[0]) + { + return typeof(object); + } + } + return types[0]; + } + + // test for numeric + if (!isAllNumeric) + { + throw new CoercionException("Cannot coerce to numeric type " + types[0].FullName); + } + + // Use arithmatic coercion type as the final authority, considering all types + Type result = GetArithmaticCoercionType(types[0], types[1]); + for (int ii = 2; ii < types.Count; ii++) + { + result = GetArithmaticCoercionType(result, types[ii]); + } + return result; + } + + public static String GetSimpleTypeName(this Type type) + { + type = GetBoxedType(type); + if (type == typeof(byte?)) + return "byte"; + if (type == typeof(short?)) + return "short"; + if (type == typeof(int?)) + return "int"; + if (type == typeof(long?)) + return "long"; + if (type == typeof(ushort?)) + return "ushort"; + if (type == typeof(uint?)) + return "uint"; + if (type == typeof(ulong?)) + return "ulong"; + if (type == typeof(string)) + return "string"; + if (type == typeof(bool?)) + return "bool"; + if (type == typeof(char?)) + return "char"; + if (type == typeof(float?)) + return "float"; + if (type == typeof(double?)) + return "double"; + if (type == typeof(decimal?)) + return "decimal"; + if (type == typeof(Guid?)) + return "guid"; + if (type == typeof (BigInteger)) + return "bigint"; + return type.FullName; + } + + public static String GetExtendedTypeName(this Type type) + { + type = GetBoxedType(type); + if (type == typeof(byte?)) + return "byte"; + if (type == typeof(short?)) + return "short"; + if (type == typeof(int?)) + return "integer"; + if (type == typeof(long?)) + return "long"; + if (type == typeof(ushort?)) + return "ushort"; + if (type == typeof(uint?)) + return "uinteger"; + if (type == typeof(ulong?)) + return "ulong"; + if (type == typeof(string)) + return "string"; + if (type == typeof(bool?)) + return "bool"; + if (type == typeof(char?)) + return "char"; + if (type == typeof(float?)) + return "float"; + if (type == typeof(double?)) + return "double"; + if (type == typeof(decimal?)) + return "decimal"; + if (type == typeof(Guid?)) + return "guid"; + if (type == typeof(BigInteger)) + return "bigint"; + return type.FullName; + } + + /// + /// Returns the boxed class for the given type name, recognizing all primitive and abbreviations, + /// uppercase and lowercase. + /// + /// Recognizes "int" as System.Int32 and "strIng" as System.String, and "Integer" as System.Int32, + /// and so on. + /// + /// is the name to recognize + /// if set to true [boxed]. + /// if set to true [throw on error]. + /// + /// class + /// + /// EventAdapterException is throw if the class cannot be identified + public static Type GetTypeForSimpleName(String typeName, bool boxed = false, bool throwOnError = false) + { + switch (typeName.ToLower().Trim()) + { + case "string": + case "varchar": + case "varchar2": + return typeof (string); + case "bool": + case "boolean": + return boxed + ? typeof (bool?) + : typeof (bool); + case "byte": + return boxed + ? typeof (byte?) + : typeof (byte); + case "char": + case "character": + return boxed + ? typeof (char?) + : typeof (char); + case "int16": + case "short": + return boxed + ? typeof (short?) + : typeof (short); + case "uint16": + case "ushort": + return boxed + ? typeof (ushort?) + : typeof (ushort); + case "int": + case "int32": + case "integer": + return boxed + ? typeof (int?) + : typeof (int); + case "uint": + case "uint32": + case "uinteger": + return boxed + ? typeof (uint?) + : typeof (uint); + case "int64": + case "long": + return boxed + ? typeof (long?) + : typeof (long); + case "uint64": + case "ulong": + return boxed + ? typeof (ulong?) + : typeof (ulong); + case "double": + return boxed + ? typeof (double?) + : typeof (double); + case "float": + case "single": + return boxed + ? typeof (float?) + : typeof (float); + case "decimal": + return boxed + ? typeof (decimal?) + : typeof (decimal); + case "guid": + return boxed + ? typeof (Guid?) + : typeof (Guid); + case "date": + case "datetime": + return boxed + ? typeof (DateTime?) + : typeof (DateTime); + case "dto": + case "datetimeoffet": + return boxed + ? typeof (DateTimeOffset?) + : typeof (DateTimeOffset); + case "dtx": + return typeof (DateTimeEx); + case "bigint": + case "biginteger": + return boxed + ? typeof (BigInteger?) + : typeof (BigInteger); + } + + var type = ResolveType(typeName.Trim(), throwOnError); + if (type == null) + { + return null; + } + + return boxed ? GetBoxedType(type) : type; + } + + public static String GetSimpleNameForType(Type clazz) + { + if (clazz == null) { + return "(null)"; + } + if (clazz == typeof(string)) { + return "string"; + } + var boxed = GetBoxedType(clazz); + if (boxed == typeof(int?)) { + return "int"; + } + if (boxed == typeof(uint?)) { + return "uint"; + } + if (boxed == typeof(bool?)) { + return "boolean"; + } + if (boxed == typeof(char?)) { + return "character"; + } + if (boxed == typeof(decimal?)) { + return "decimal"; + } + if (boxed == typeof(double?)) { + return "double"; + } + if (boxed == typeof(float?)) { + return "float"; + } + if (boxed == typeof(byte?)) { + return "byte"; + } + if (boxed == typeof(short?)) { + return "short"; + } + if (boxed == typeof(ushort?)) { + return "ushort"; + } + if (boxed == typeof(long?)) { + return "long"; + } + if (boxed == typeof (ulong?)) { + return "ulong"; + } + if (boxed == typeof(Guid?)) { + return "guid"; + } + if (boxed == typeof (BigInteger?)) { + return "bigint"; + } + return clazz.Name; + } + + /// + /// Gets the primitive type for the given name. + /// + /// Name of the type. + /// + public static Type GetPrimitiveTypeForName(String typeName) + { + switch (typeName.ToLower()) + { + case "bool": + case "boolean": + case "system.bool": + return typeof(bool); + case "char": + case "character": + case "system.character": + return typeof(char); + case "float": + case "single": + case "system.single": + return typeof(float); + case "double": + case "system.double": + return typeof(double); + case "decimal": + case "system.decimal": + return typeof(decimal); + case "sbyte": + case "system.sbyte": + return typeof(sbyte); + case "byte": + case "system.byte": + return typeof(byte); + case "short": + case "int16": + case "system.int16": + return typeof(short); + case "ushort": + case "uint16": + case "system.uint16": + return typeof(ushort); + case "int": + case "int32": + case "system.int32": + return typeof(int); + case "uint": + case "uint32": + case "system.uint32": + return typeof(uint); + case "long": + case "int64": + case "system.int64": + return typeof(long); + case "ulong": + case "uint64": + case "system.uint64": + return typeof(ulong); + case "string": + case "system.string": + return typeof(string); + case "datetime": + case "system.datetime": + return typeof(DateTime); + case "dto": + case "datetimeoffset": + case "system.datetimeoffset": + return typeof(DateTimeOffset); + case "dtx": + return typeof(DateTimeEx); + case "bigint": + case "biginteger": + return typeof(BigInteger?); + default: + return null; + } + } + + public static TypeParser GetParser(Type clazz) + { + Type classBoxed = GetBoxedType(clazz); + TypeParser typeParser = ParserTable[classBoxed]; + return typeParser; + } + + public static TypeParser GetParser() + { + Type classBoxed = GetBoxedType(typeof(T)); + TypeParser typeParser = ParserTable[classBoxed]; + return typeParser; + } + + public static object Parse(string text) + { + Type classBoxed = GetBoxedType(typeof(T)); + TypeParser typeParser = ParserTable[classBoxed]; + return + (typeParser != null) ? + (typeParser.Invoke(text)) : + (null); + } + + /// Parse the String using the given built-in class for parsing. + /// is the class to parse the value to + /// is the text to parse + /// value matching the type passed in + public static Object Parse(Type clazz, String text) + { + Type classBoxed = GetBoxedType(clazz); + TypeParser typeParser = ParserTable[classBoxed]; + return + (typeParser != null) ? + (typeParser.Invoke(text)) : + (null); + } + + public static bool IsImplementsInterface(this Type clazz) + { + return IsImplementsInterface(clazz, typeof(T)); + } + + /// + /// Method to check if a given class, and its superclasses and interfaces (deep), implement a given interface. + /// + /// to check, including all its superclasses and their interfaces and extends + /// is the interface class to look for + /// + /// true if such interface is implemented by type of the clazz or its superclasses orextends by type interface and superclasses (deep check) + /// + public static bool IsImplementsInterface(this Type clazz, Type interfaceClass) + { + if (!(interfaceClass.IsInterface)) + { + throw new ArgumentException("Interface class passed in is not an interface"); + } + bool resultThisClass = RecursiveIsImplementsInterface(clazz, interfaceClass); + if (resultThisClass) + { + return true; + } + return RecursiveSuperclassImplementsInterface(clazz, interfaceClass); + } + + // NOTE: Java vs. CLR interface model + // Java needs recursive functions to introspect + // classes, but I dont think the CLR behaves this way. I think that it flattens + // the entire structure. + + private static bool RecursiveSuperclassImplementsInterface(this Type clazz, Type interfaceClass) + { + Type baseType = clazz.BaseType; + if ((baseType == null) || (baseType == typeof(Object))) + { + return false; + } + bool result = RecursiveIsImplementsInterface(baseType, interfaceClass); + return result || RecursiveSuperclassImplementsInterface(baseType, interfaceClass); + } + + private static bool RecursiveIsSuperClass(Type clazz, Type superClass) + { + if (clazz == null) + { + return false; + } + if (clazz.IsPrimitive) + { + return false; + } + Type mySuperClass = clazz.BaseType; + if (mySuperClass == superClass) + { + return true; + } + if (mySuperClass == typeof(Object)) + { + return false; + } + return RecursiveIsSuperClass(mySuperClass, superClass); + } + + private static bool RecursiveIsImplementsInterface(Type clazz, Type interfaceClass) + { + if (clazz == interfaceClass) + { + return true; + } + + var interfaces = clazz.GetInterfaces(); + if (interfaces.Length == 0) + { + return false; + } + + return interfaces + .Select(interfaceX => RecursiveIsImplementsInterface(interfaceX, interfaceClass)) + .Any(result => result); + } + + public static String ResolveAbsoluteTypeName(String assemblyQualifiedTypeName) + { + var trueTypeName = ResolveType(assemblyQualifiedTypeName, false); + if (trueTypeName == null) + { + throw new EPException("unable to determine assembly qualified class for " + assemblyQualifiedTypeName); + } + + return trueTypeName.AssemblyQualifiedName; + } + + public static String TryResolveAbsoluteTypeName(String assemblyQualifiedTypeName) + { + var trueTypeName = ResolveType(assemblyQualifiedTypeName, false); + if (trueTypeName == null) + { + return assemblyQualifiedTypeName; + } + + return trueTypeName.AssemblyQualifiedName; + } + + /// + /// Resolves a type using the assembly qualified type name. If the type + /// can not be resolved using a simple Type.GetType() [which many can not], + /// then the method will check all assemblies in the assembly search path. + /// + /// Name of the assembly qualified type. + /// The assembly search path. + /// if set to true [throw on missing]. + /// + + public static Type ResolveType(String assemblyQualifiedTypeName, IEnumerable assemblySearchPath, bool throwOnError) + { + Exception coreException = null; + + bool isHandled = false; + + // as part of the process, we want to unwind type esperized type names + assemblyQualifiedTypeName = assemblyQualifiedTypeName.Replace('$', '+'); + + if (TypeResolver != null) + { + try + { + var typeResolverEventArgs = new TypeResolverEventArgs(assemblyQualifiedTypeName); + var typeResult = TypeResolver.Invoke(typeResolverEventArgs); + if (typeResult != null || typeResolverEventArgs.Handled) + return typeResult; + } + catch (Exception e) + { + coreException = e; + isHandled = true; + } + } + + if (!isHandled) + { + // Attempt to find the type by using the Type object to resolve + // the type. If its fully qualified this will work, if its not, + // then this will likely fail. + + try + { + return Type.GetType(assemblyQualifiedTypeName, true, false); + } + catch (Exception e) + { + coreException = e; + } + + // Search the assembly path to resolve the type + + foreach (Assembly assembly in assemblySearchPath) + { + Type type = assembly.GetType(assemblyQualifiedTypeName, false, false); + if (type != null) + { + return type; + } + } + } + + // Type was not found in type of our search points + + if (throwOnError) + { + throw coreException; + } + + return null; + } + + /// + /// Resolves a type using the assembly qualified type name. If the type + /// can not be resolved using a simple Type.GetType() [which many can not], + /// then the method will check all assemblies currently loaded into the + /// AppDomain. + /// + /// Name of the assembly qualified type. + /// if set to true [throw on missing]. + /// + + public static Type ResolveType(String assemblyQualifiedTypeName, bool throwOnError = true) + { + var assemblySearchPath = + AssemblySearchPath != null ? + AssemblySearchPath.Invoke() : + AppDomain.CurrentDomain.GetAssemblies(); + + return ResolveType(assemblyQualifiedTypeName, assemblySearchPath, throwOnError); + } + + public static Type ResolveType(String assemblyQualifiedTypeName, String assemblyName) + { + if (assemblyName == null) + { + return ResolveType(assemblyQualifiedTypeName); + } + + Assembly assembly = ResolveAssembly(assemblyName); + if (assembly != null) + { + return assembly.GetType(assemblyQualifiedTypeName); + } + + if (Log.IsWarnEnabled) + { + Log.Warn("Assembly {0} not found while resolving type: {1}", + assemblyName, + assemblyQualifiedTypeName); + } + + return null; + } + + public static Type GetClassForName(String typeName, ClassForNameProvider classForNameProvider) + { + return classForNameProvider.ClassForName(typeName); + } + + private static Type MakeArrayType(int arrayRank, Type typeInstance) + { + for (; arrayRank > 0; arrayRank--) + { + typeInstance = typeInstance.MakeArrayType(); + } + + return typeInstance; + } + + private static readonly Type BaseGenericDictionary = + typeof(IDictionary).GetGenericTypeDefinition(); + + /// + /// Determines whether the type is usable as an dictionary. + /// + /// The type. + /// + /// true if [is dictionary type] [the specified type]; otherwise, false. + /// + public static bool IsOpenDictionary(Type t) + { + if (typeof(System.Collections.IDictionary).IsAssignableFrom(t)) + { + return true; + } + + // Look for implementation of the System.Collections.Generic.IDictionary + // interface. Once found, we just need to check the key type... the return + // type is irrelevant. + + + foreach (Type iface in t.GetInterfaces()) + { + if (iface.IsGenericType) + { + Type baseT1 = iface.GetGenericTypeDefinition(); + if (baseT1 == BaseGenericDictionary) + { + Type[] genericParameterTypes = iface.GetGenericArguments(); + if ((genericParameterTypes[0] == typeof(string)) || + (genericParameterTypes[1] == typeof(object))) + { + return true; + } + } + } + } + + return false; + } + + public static bool IsSubclassOrImplementsInterface(this Type extendorOrImplementor) + { + return IsSubclassOrImplementsInterface(extendorOrImplementor, typeof(T)); + } + + /// Method to check if a given class, and its superclasses and interfaces (deep), implement a given interface or extend a given class. + /// is the class to inspects its : and : clauses + /// is the potential interface, or superclass, to check + /// true if such interface is implemented by type of the clazz or its superclasses orextends by type interface and superclasses (deep check) + public static bool IsSubclassOrImplementsInterface(Type extendorOrImplementor, Type extendedOrImplemented) + { + return extendedOrImplemented.IsAssignableFrom(extendorOrImplementor); + } + + /// + /// Instantiates the specified type. + /// + /// The type. + /// + public static object Instantiate(Type type) + { + try + { + return Activator.CreateInstance(type); + } + catch (TypeInstantiationException ex) + { + throw new TypeInstantiationException( + "Unable to instantiate from class '" + type.FullName + "' via default constructor", ex); + } + catch (TargetInvocationException ex) + { + throw new TypeInstantiationException( + "Invocation exception when instantiating class '" + type.FullName + "' via default constructor", ex); + } + catch (MethodAccessException ex) + { + throw new TypeInstantiationException( + "Method access when instantiating class '" + type.FullName + "' via default constructor", ex); + } + catch (MemberAccessException ex) + { + throw new TypeInstantiationException( + "Member access when instantiating class '" + type.FullName + "' via default constructor", ex); + } + } + + /// + /// Looks up the given class and checks that it : or : the required interface,and instantiates an object. + /// + /// is the type that the looked-up class should extend or implement + /// of the class to load, check type and instantiate + /// instance of given class, via newInstance + public static T Instantiate(Type type) where T : class + { + var implementedOrExtendedType = typeof(T); + var typeName = type.FullName; + + if (!IsSubclassOrImplementsInterface(type, implementedOrExtendedType)) + { + if (implementedOrExtendedType.IsInterface) + { + throw new TypeInstantiationException("Class '" + typeName + "' does not implement interface '" + + implementedOrExtendedType.FullName + "'"); + } + throw new TypeInstantiationException("Class '" + typeName + "' does not extend '" + + implementedOrExtendedType.FullName + "'"); + } + + try + { + return (T) Activator.CreateInstance(type); + } + catch (TypeInstantiationException ex) + { + throw new TypeInstantiationException( + "Unable to instantiate from class '" + typeName + "' via default constructor", ex); + } + catch (TargetInvocationException ex) + { + throw new TypeInstantiationException( + "Invocation exception when instantiating class '" + typeName + "' via default constructor", ex); + } + catch (MethodAccessException ex) + { + throw new TypeInstantiationException( + "Method access when instantiating class '" + typeName + "' via default constructor", ex); + } + catch (MemberAccessException ex) + { + throw new TypeInstantiationException( + "Member access when instantiating class '" + typeName + "' via default constructor", ex); + } + } + + /// + /// Looks up the given class and checks that it : or : the required interface,and instantiates an object. + /// + /// is the type that the looked-up class should extend or implement + /// of the class to load, check type and instantiate + /// instance of given class, via newInstance + public static T Instantiate(String typeName) where T : class + { + var implementedOrExtendedType = typeof(T); + + Type type; + try + { + type = ResolveType(typeName); + } + catch (Exception ex) + { + throw new TypeInstantiationException("Unable to load class '" + typeName + "', class not found", ex); + } + + if (!IsSubclassOrImplementsInterface(type, implementedOrExtendedType)) + { + if (implementedOrExtendedType.IsInterface) + { + throw new TypeInstantiationException("Class '" + typeName + "' does not implement interface '" + + implementedOrExtendedType.FullName + "'"); + } + throw new TypeInstantiationException("Class '" + typeName + "' does not extend '" + + implementedOrExtendedType.FullName + "'"); + } + + try + { + return (T)Activator.CreateInstance(type); + } + catch (TypeInstantiationException ex) + { + throw new TypeInstantiationException( + "Unable to instantiate from class '" + typeName + "' via default constructor", ex); + } + catch (TargetInvocationException ex) + { + throw new TypeInstantiationException( + "Invocation exception when instantiating class '" + typeName + "' via default constructor", ex); + } + catch (MethodAccessException ex) + { + throw new TypeInstantiationException( + "Method access when instantiating class '" + typeName + "' via default constructor", ex); + } + catch (MemberAccessException ex) + { + throw new TypeInstantiationException( + "Member access when instantiating class '" + typeName + "' via default constructor", ex); + } + } + + /// + /// Looks up the given class and checks that it : or : the required interface,and instantiates an object. + /// + /// is the type that the looked-up class should extend or implement + /// Name of the type. + /// The class for name provider. + /// + /// instance of given class, via newInstance + /// + public static T Instantiate(string typeName, ClassForNameProvider classForNameProvider) + { + var implementedOrExtendedType = typeof(T); + + Type type; + try + { + type = classForNameProvider.ClassForName(typeName); + } + catch (Exception ex) + { + throw new TypeInstantiationException("Unable to load class '" + typeName + "', class not found", ex); + } + + if (!IsSubclassOrImplementsInterface(type, implementedOrExtendedType)) + { + if (implementedOrExtendedType.IsInterface) + { + throw new TypeInstantiationException("Class '" + typeName + "' does not implement interface '" + + implementedOrExtendedType.FullName + "'"); + } + throw new TypeInstantiationException("Class '" + typeName + "' does not extend '" + + implementedOrExtendedType.FullName + "'"); + } + + try + { + return (T)Activator.CreateInstance(type); + } + catch (TypeInstantiationException ex) + { + throw new TypeInstantiationException( + "Unable to instantiate from class '" + typeName + "' via default constructor", ex); + } + catch (TargetInvocationException ex) + { + throw new TypeInstantiationException( + "Invocation exception when instantiating class '" + typeName + "' via default constructor", ex); + } + catch (MethodAccessException ex) + { + throw new TypeInstantiationException( + "Method access when instantiating class '" + typeName + "' via default constructor", ex); + } + catch (MemberAccessException ex) + { + throw new TypeInstantiationException( + "Member access when instantiating class '" + typeName + "' via default constructor", ex); + } + } + + + /// + /// Applies a visitor pattern to the base interfaces for the provided type. + /// + /// The type. + /// The visitor. + public static void VisitBaseInterfaces(this Type type, Action visitor) + { + if (type != null) + { + var interfaces = type.GetInterfaces(); + for (int ii = 0; ii < interfaces.Length; ii++) + { + visitor.Invoke(interfaces[ii]); + VisitBaseInterfaces(interfaces[ii], visitor); + } + } + } + + /// + /// Gets the base interfaces for the provided type and store them + /// in the result set. + /// + /// The type. + /// The result. + public static void GetBaseInterfaces(Type type, ICollection result) + { + VisitBaseInterfaces(type, result.Add); + } + + /// + /// Visits the base classes for the provided type. + /// + /// The type. + public static void VisitBaseClasses(this Type type, Action visitor) + { + if (type != null) + { + var baseType = type.BaseType; + if (baseType != null) + { + visitor.Invoke(baseType); + VisitBaseClasses(baseType, visitor); + } + } + } + + /// + /// Gets the base classes for the provided type and store them + /// in the result set. + /// + /// The type. + /// The result. + public static void GetBaseClasses(Type type, ICollection result) + { + VisitBaseClasses(type, result.Add); + } + + /// + /// Visits the base class and all interfaces. + /// + /// The type. + /// The visitor. + public static void VisitBase(this Type type, Action visitor) + { + VisitBaseInterfaces(type, visitor); + VisitBaseClasses(type, visitor); + } + + /// + /// Populates all interface and superclasses for the given class, recursivly. + /// + /// to reflect upon + /// set of classes to populate + public static void GetBase(Type type, ICollection result) + { + GetBaseInterfaces(type, result); + GetBaseClasses(type, result); + } + + /// + /// Visits the specified type. + /// + /// The type. + /// The visitor. + public static void Visit(this Type type, Action visitor) + { + if (type != null) + { + visitor.Invoke(type); + VisitBase(type, visitor); + } + } + + public static bool IsSimpleNameFullyQualfied(String simpleTypeName, String fullyQualifiedTypename) + { + if ((fullyQualifiedTypename.EndsWith("." + simpleTypeName)) || + (fullyQualifiedTypename.Equals(simpleTypeName))) + { + return true; + } + return false; + } + + public static String GetTypeNameFullyQualPretty(this Type clazz) + { + if (clazz == null) + { + return "null"; + } + if (clazz.IsArray) + { + return clazz.GetElementType().FullName + "(Array)"; + } + return clazz.FullName; + } + + public static string GetTypeNameFullyQualPretty() + { + return GetTypeNameFullyQualPretty(typeof (T)); + } + + /// + /// Returns true if the Class is a fragmentable type, i.e. not a primitive or boxed + /// type or type of the common built-in types or does not implement Map. + /// + /// type to check + /// + /// true if fragmentable + /// + public static bool IsFragmentableType(this Type propertyType) + { + if (propertyType == null) + { + return false; + } + if (propertyType.IsArray) + { + return IsFragmentableType(propertyType.GetElementType()); + } + if (propertyType.IsNullable()) + { + propertyType = Nullable.GetUnderlyingType(propertyType); + } + if (IsBuiltinDataType(propertyType)) + { + return false; + } + if (propertyType.IsEnum) + { + return false; + } + if (propertyType.IsGenericDictionary()) + { + return false; + } + if (propertyType == typeof(XmlNode)) + { + return false; + } + if (propertyType == typeof(XmlNodeList)) + { + return false; + } + if (propertyType == typeof(Object)) + { + return false; + } + if (propertyType == typeof(DateTimeOffset)) + { + return false; + } + if (propertyType == typeof(DateTime)) + { + return false; + } + if (propertyType.FullName == AvroConstantsNoDep.GENERIC_RECORD_CLASSNAME) + { + return false; + } + return true; + } + + /// + /// Returns the generic type parameter of a return value by a field, property or method. + /// + /// The member INFO. + /// if set to true [is allow null]. + /// generic type parameter + public static Type GetGenericReturnType(MemberInfo memberInfo, bool isAllowNull) + { + if (memberInfo is MethodInfo) + return GetGenericReturnType(memberInfo as MethodInfo, isAllowNull); + if (memberInfo is PropertyInfo) + return GetGenericPropertyType(memberInfo as PropertyInfo, isAllowNull); + + return GetGenericFieldType(memberInfo as FieldInfo, isAllowNull); + } + + /// + /// Returns the second generic type parameter of a return value by a field or + /// method. + /// + /// The member INFO. + /// whether null is allowed as a return value or expected typeof(object) + /// generic type parameter + public static Type GetGenericReturnTypeMap(MemberInfo memberInfo, bool isAllowNull) + { + if (memberInfo is MethodInfo) + return GetGenericReturnTypeMap(memberInfo as MethodInfo, isAllowNull); + if (memberInfo is PropertyInfo) + return GetGenericPropertyTypeMap(memberInfo as PropertyInfo, isAllowNull); + + return GetGenericFieldTypeMap(memberInfo as FieldInfo, isAllowNull); + } + + /// + /// Returns the generic type parameter of a return value by a method. + /// + /// method or null if field + /// whether null is allowed as a return value or expected typeof(object) + /// + /// generic type parameter + /// + public static Type GetGenericReturnType(MethodInfo method, bool isAllowNull) + { + Type t = method.ReturnType; + Type result = GetGenericType(t, 0); + if (!isAllowNull && result == null) + { + return typeof(object); + } + return result; + } + + /// + /// Returns the second generic type parameter of a return value by a field or + /// method. + /// + /// method or null if field + /// whether null is allowed as a return value or expected typeof(object) + /// + /// generic type parameter + /// + public static Type GetGenericReturnTypeMap(MethodInfo method, bool isAllowNull) + { + Type t = method.ReturnType; + Type result = GetGenericMapType(t); + if (!isAllowNull && result == null) + { + return typeof(object); + } + return result; + } + + /// + /// Returns the generic type parameter of a return value by a property. + /// + /// property or null if method + /// whether null is allowed as a return value or expected typeof(object) + /// + /// generic type parameter + /// + public static Type GetGenericPropertyType(PropertyInfo property, bool isAllowNull) + { + Type t = property.PropertyType; + Type result = GetGenericType(t, 0); + if (!isAllowNull && result == null) + { + return typeof(object); + } + return result; + } + + /// + /// Returns the generic type parameter of a return value by a property. + /// + /// property or null if method + /// whether null is allowed as a return value or expected typeof(object) + /// + /// generic type parameter + /// + public static Type GetGenericPropertyTypeMap(PropertyInfo property, bool isAllowNull) + { + Type t = property.PropertyType; + Type result = GetGenericMapType(t); + if (!isAllowNull && result == null) + { + return typeof(object); + } + return result; + } + + /// + /// Returns the generic type parameter of a return value by a field. + /// + /// field or null if method + /// whether null is allowed as a return value or expected typeof(object) + /// + /// generic type parameter + /// + public static Type GetGenericFieldType(FieldInfo field, bool isAllowNull) + { + Type t = field.FieldType; + Type result = GetGenericType(t, 0); + if (!isAllowNull && result == null) + { + return typeof(object); + } + return result; + } + + /// + /// Returns the generic type parameter of a return value by a field or method. + /// + /// field or null if method + /// whether null is allowed as a return value or expected typeof(object) + /// + /// generic type parameter + /// + public static Type GetGenericFieldTypeMap(FieldInfo field, bool isAllowNull) + { + Type t = field.FieldType; + Type result = GetGenericMapType(t); + if (!isAllowNull && result == null) + { + return typeof(object); + } + return result; + } + + /// Returns the generic type parameter of a return value by a field or method. + /// method or null if field + /// field or null if method + /// whether null is allowed as a return value or expected typeof(Object) + /// generic type parameter + public static Type GetGenericReturnType(MethodInfo method, FieldInfo field, bool isAllowNull) + { + if (method == null) + { + return GetGenericFieldType(field, isAllowNull); + } + else + { + return GetGenericReturnType(method, isAllowNull); + } + } + + private static Type GetGenericMapType(this Type t) + { + if (t == null) + { + return null; + } + + if (!t.IsGenericType) + { + // See if we are a dictionary + var dictionaryType = GenericExtensions.FindGenericDictionaryInterface(t); + if (dictionaryType != null) + { + t = dictionaryType; + } + } + + if (!t.IsGenericType) + { + return null; + } + + // See if we're dealing with a nullable ... nullables register + // as generics. + if (Nullable.GetUnderlyingType(t) != null) + { + return null; + } + + var genericArguments = t.GetGenericArguments(); + if ((genericArguments == null) || + (genericArguments.Length < 2)) + { + return typeof(object); + } + + return genericArguments[1]; + } + + public static Type GetGenericType(this Type t, int index) + { + if (t == null) + { + return null; + } + + if (!t.IsGenericType) + { + var enumType = GenericExtensions.FindGenericEnumerationInterface(t); + if (enumType != null) + { + t = enumType; + } + } + + if (!t.IsGenericType) + { + return null; + } + + // See if we're dealing with a nullable ... nullables register + // as generics. + if (Nullable.GetUnderlyingType(t) != null) + { + return null; + } + + var genericArguments = t.GetGenericArguments(); + if ((genericArguments == null) || + (genericArguments.Length < (index + 1))) + { + return typeof(object); + } + + return genericArguments[index]; + } + + /// + /// Resolves the ident as enum const. + /// + /// The constant. + /// The engine import service. + /// + public static Object ResolveIdentAsEnumConst(String constant, EngineImportService engineImportService, bool isAnnotation) + { + Func engineResolution = null; + if (engineImportService != null) + engineResolution = engineImportService.ResolveType; + + return ResolveIdentAsEnumConst(constant, engineResolution, isAnnotation); + } + + /// + /// Resolve a string constant as a possible enumeration value, returning null if not + /// resolved. + /// + /// to resolve + /// for engine-level use to resolve enums, can be null + /// null or enumeration value + /// ExprValidationException if there is an error accessing the enum + public static Object ResolveIdentAsEnumConst( + string constant, + Func engineImportService, + bool isAnnotation) + { + int lastDotIndex = constant.LastIndexOf('.'); + if (lastDotIndex == -1) + { + return null; + } + + var className = constant.Substring(0, lastDotIndex); + var constName = constant.Substring(lastDotIndex + 1); + + // un-escape + className = Unescape(className); + constName = Unescape(constName); + + Type clazz; + try + { + clazz = engineImportService.Invoke(className, isAnnotation); + } + catch (EngineImportException) + { + return null; + } + + FieldInfo field; + try + { + field = clazz.GetField(constName); + if (field == null) + { + return null; + } + } + catch (MissingFieldException) + { + return null; + } + + if (field.IsPublic && field.IsStatic) + { + try + { + return field.GetValue(null); + } + catch (FieldAccessException e) + { + throw new ArgumentException( + "Exception accessing field '" + field.Name + "': " + e.Message, e); + } + } + + return null; + } + + public static Assembly ResolveAssembly(string assemblyNameOrFile) + { + try + { + return Assembly.Load(assemblyNameOrFile); + } + catch (FileNotFoundException) + { + } + catch (FileLoadException) + { + } + + try + { + FileInfo fileInfo = new FileInfo(assemblyNameOrFile); + if (fileInfo.Exists) + { + return Assembly.LoadFile(fileInfo.FullName); + } + } + catch (FileLoadException) + { + } + + return null; + } + + public static Type GetArrayType(Type resultType) + { + return Array.CreateInstance(resultType, 0).GetType(); + } + + public static String PrintInstance(Object instance, bool fullyQualified) + { + if (instance == null) + { + return "(null)"; + } + + using (var writer = new StringWriter()) + { + WriteInstance(writer, instance, fullyQualified); + return writer.ToString(); + } + } + + public static void WriteInstance(TextWriter writer, Object instance, bool fullyQualified) + { + if (instance == null) + { + writer.Write("(null)"); + return; + } + + string className = fullyQualified ? instance.GetType().FullName : instance.GetType().Name; + WriteInstance(writer, className, instance); + } + + public static void WriteInstance(TextWriter writer, String title, Object instance) + { + writer.Write(title); + writer.Write("@"); + if (instance == null) + { + writer.Write("(null)"); + } + else + { + writer.Write("{0:X}", System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(instance)); + } + } + + /// Returns an instance of a hook as specified by an annotation. + /// to search + /// type to look for + /// interface required + /// for resolving references, optional, if not provided then using ResolveType + /// hook instance + /// ExprValidationException if instantiation failed + public static Object GetAnnotationHook(Attribute[] annotations, HookType hookType, Type interfaceExpected, EngineImportService optionalResolver) + { + if (annotations == null) + { + return null; + } + String hookClass = null; + for (int i = 0; i < annotations.Length; i++) + { + if (!(annotations[i] is HookAttribute)) + { + continue; + } + + var hook = (HookAttribute) annotations[i]; + if (hook.Type != hookType) + { + continue; + } + hookClass = hook.Hook; + } + + if (hookClass == null) + { + return null; + } + + Type clazz; + try + { + if (optionalResolver == null) + { + clazz = ResolveType(hookClass); + } + else + { + clazz = optionalResolver.ResolveType(hookClass, true); + } + } + catch (Exception e) + { + throw new ExprValidationException("Failed to resolve hook provider of hook type '" + hookType + + "' import '" + hookClass + "' :" + e.Message); + } + + if (!IsImplementsInterface(clazz, interfaceExpected)) + { + throw new ExprValidationException("Hook provider for hook type '" + hookType + "' " + + "class '" + clazz.FullName + "' does not implement the required '" + interfaceExpected.Name + + "' interface"); + } + + try + { + return Activator.CreateInstance(clazz); + } + catch (Exception e) + { + throw new ExprValidationException("Failed to instantiate hook provider of hook type '" + hookType + "' " + + "class '" + clazz.FullName + "' :" + e.Message); + } + } + + public static string GetInvocationMessage(String statementName, MethodInfo method, String classOrPropertyName, Object[] args, Exception e) + { + string parameters = + args == null ? "null" : + args.Length == 0 ? "[]" : + string.Join(" ", args); + + if (args != null) + { + var methodParameters = method.GetParameterTypes(); + for (int i = 0; i < methodParameters.Length; i++) + { + if (methodParameters[i].IsPrimitive && args[i] == null) + { + return "NullPointerException invoking method '" + method.Name + + "' of class '" + classOrPropertyName + + "' in parameter " + i + + " passing parameters " + parameters + + " for statement '" + statementName + "': The method expects a primitive " + + methodParameters[i].Name + + " value but received a null value"; + } + } + } + + return "Invocation exception when invoking method '" + method.Name + + "' of class '" + classOrPropertyName + + "' passing parameters " + parameters + + " for statement '" + statementName + "': " + e.GetType().FullName + " : " + + e.Message; + } + + public static String GetMessageInvocationTarget(String statementName, MethodInfo method, String classOrPropertyName, Object[] args, Exception e) + { + if (e is TargetInvocationException) + { + if (e.InnerException != null) + { + e = e.InnerException; + } + } + + var parameters = args == null ? "null" : args.Render(",", "[]"); + if (args != null) + { + var methodParameters = method.GetParameterTypes(); + for (int i = 0; i < methodParameters.Length; i++) + { + if (methodParameters[i].IsPrimitive && args[i] == null) + { + return "NullPointerException invoking method '" + method.Name + + "' of class '" + classOrPropertyName + + "' in parameter " + i + + " passing parameters " + parameters + + " for statement '" + statementName + "': The method expects a primitive " + + methodParameters[i].Name + + " value but received a null value"; + } + } + } + + return "Invocation exception when invoking method '" + method.Name + + "' of class '" + classOrPropertyName + + "' passing parameters " + parameters + + " for statement '" + statementName + "': " + e.GetType().FullName + " : " + + e.Message; + } + + public static IDictionary GetClassObjectFromPropertyTypeNames( + Properties properties, + Func typeResolver) + { + IDictionary propertyTypes = new Dictionary(); + foreach (var entry in properties) + { + propertyTypes.Put(entry.Key, typeResolver.Invoke(entry.Value)); + } + return propertyTypes; + } + + public static IDictionary GetClassObjectFromPropertyTypeNames( + Properties properties, + ClassForNameProvider classForNameProvider) + { + return GetClassObjectFromPropertyTypeNames( + properties, classForNameProvider.ClassForName); + } + + public static IDictionary GetClassObjectFromPropertyTypeNames(Properties properties) + { + return GetClassObjectFromPropertyTypeNames( + properties, className => + { + if (className == "string") + { + className = typeof (string).FullName; + } + + // use the boxed type for primitives + var boxedClassName = GetBoxedTypeName(className); + + try + { + return ResolveType(boxedClassName, true); + } + catch (TypeLoadException ex) + { + throw new ConfigurationException( + "Unable to load class '" + boxedClassName + "', class not found", + ex); + } + }); + } + + public static object Boxed(this object value) + { + if (value == null) + return null; + if (value is Type) + return ((Type)value).GetBoxedType(); + return value; + } + + public static ICollection AsObjectCollection(this object value) + { + if (value == null) + return null; + if (value is ICollection) + return (ICollection)value; + if (value.GetType().IsGenericCollection()) + return MagicMarker.GetCollectionFactory(value.GetType()).Invoke(value); + if (value is ICollection) + return ((ICollection)value).Cast().ToList(); + throw new ArgumentException("value is not a collection", "value"); + } + + public static Boolean IsArray(this Type type) + { + return type == typeof(Array) || type.IsArray; + } + + public static bool IsSignatureCompatible(Type[] one, Type[] two) + { + if (one == two) + { + return true; + } + + if (one.Length != two.Length) + { + return false; + } + + for (int i = 0; i < one.Length; i++) + { + var oneClass = one[i]; + var twoClass = two[i]; + if ((oneClass != twoClass) && (!oneClass.IsAssignmentCompatible(twoClass))) + { + return false; + } + } + + return true; + } + + public static MethodInfo FindRequiredMethod(Type clazz, String methodName) + { + var found = clazz.GetMethods().FirstOrDefault(m => m.Name == methodName); + if (found == null) + { + throw new ArgumentException("Not found method '" + methodName + "'"); + } + return found; + } + + public static IList FindMethodsByNameStartsWith(Type clazz, String methodName) + { + var methods = clazz.GetMethods(); + return methods.Where(method => method.Name.StartsWith(methodName)).ToList(); + } + + + public static IList GetAnnotations(Attribute[] annotations) where T : Attribute + { + return GetAnnotations(typeof (T), annotations); + } + + public static IList GetAnnotations(Type annotationClass, Attribute[] annotations) + { + //return annotations + // .Where(a => a.GetType() == annotationClass) + // .ToList(); + + List result = null; + foreach (Attribute annotation in annotations) + { + if (annotation.GetType() == annotationClass) + { + if (result == null) + { + result = new List(); + } + result.Add(annotation); + } + } + + return result ?? Collections.GetEmptyList(); + } + + public static bool IsAnnotationListed(Type annotationClass, Attribute[] annotations) + { + return !GetAnnotations(annotationClass, annotations).IsEmpty(); + } + + public static ICollection FindAnnotatedFields(Type targetClass, Type annotation) + { + var fields = new LinkedHashSet(); + FindFieldInternal(targetClass, annotation, fields); + + // superclass fields + Type clazz = targetClass; + while (true) + { + clazz = clazz.BaseType; + if (clazz == typeof(object) || clazz == null) + { + break; + } + FindFieldInternal(clazz, annotation, fields); + } + return fields; + } + + private static void FindFieldInternal(Type currentClass, Type annotation, ICollection fields) + { + foreach (FieldInfo field in currentClass.GetFields(BindingFlags.Public|BindingFlags.NonPublic|BindingFlags.Instance)) + { + if (IsAnnotationListed(annotation, field.GetCustomAttributes(true).OfType().ToArray())) + { + fields.Add(field); + } + } + } + + public static ICollection FindAnnotatedMethods(Type targetClass, Type annotation) + { + ICollection methods = new LinkedHashSet(); + FindAnnotatedMethodsInternal(targetClass, annotation, methods); + + // superclass fields + Type clazz = targetClass; + while (true) + { + clazz = clazz.BaseType; + if (clazz == typeof(object) || clazz == null) + { + break; + } + FindAnnotatedMethodsInternal(clazz, annotation, methods); + } + return methods; + } + + private static void FindAnnotatedMethodsInternal(Type currentClass, Type annotation, ICollection methods) + { + foreach (MethodInfo method in currentClass.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) + { + if (IsAnnotationListed(annotation, method.GetCustomAttributes(true).OfType().ToArray())) + { + methods.Add(method); + } + } + } + + public static void SetFieldForAnnotation(Object target, Type annotation, Object value) + { + bool found = SetFieldForAnnotation(target, annotation, value, target.GetType()); + if (!found) + { + var superClass = target.GetType().BaseType; + while (!found) + { + found = SetFieldForAnnotation(target, annotation, value, superClass); + if (!found) + { + superClass = superClass.BaseType; + } + if (superClass == typeof(object) || superClass == null) + { + break; + } + } + } + } + + private static bool SetFieldForAnnotation(Object target, Type annotation, Object value, Type currentClass) + { + foreach (FieldInfo field in currentClass.GetFields(BindingFlags.NonPublic|BindingFlags.Public|BindingFlags.Instance)) + { + if (IsAnnotationListed(annotation, field.GetCustomAttributes(true).OfType().ToArray())) + { + field.SetValue(target, value); + return true; + } + } + return false; + } + + public static Pair IsGetArrayType(String type) + { + var index = type.IndexOf("[]", System.StringComparison.Ordinal); + if (index == -1) + { + return new Pair(type, false); + } + var typeOnly = type.Substring(0, index); + return new Pair(typeOnly.Trim(), true); + } + + public static bool Is(this object o) + { + return o is T; + } + + public static bool Is(this object o) + { + return o is T1 || o is T2; + } + + public static bool Is(this object o) + { + return o is T1 || o is T2 || o is T3; + } + + private static List ExtensionMethods; + + public static void InvalidateExtensionMethodsCache() + { + ExtensionMethods = null; + } + + public static IEnumerable GetExtensionMethods() + { + if (ExtensionMethods == null) + { + ExtensionMethods = AppDomain.CurrentDomain.GetAssemblies() + .SelectMany(assembly => assembly.GetTypes().Where(t => t.IsDefined(typeof(ExtensionAttribute), true)) + .SelectMany(type => type.GetMethods().Where(m => m.IsDefined(typeof(ExtensionAttribute), true)))) + .ToList(); + } + + return ExtensionMethods; + } + + public static IEnumerable GetExtensionMethods(Type declaringType) + { + var extensionMethods = GetExtensionMethods() + .Where(m => m.GetParameters()[0].ParameterType == declaringType); + return extensionMethods; + } + + public static bool IsExtensionMethod(this MethodInfo method) + { + return method.IsDefined(typeof (ExtensionAttribute), true); + } + + public static bool IsAttribute(this Type type) + { + return IsSubclassOrImplementsInterface(type); + } + + public static bool IsDateTime(Type clazz) + { + if (clazz == null) + return false; + + var clazzBoxed = clazz.GetBoxedType(); + return + (clazzBoxed == typeof (DateTimeEx)) || + (clazzBoxed == typeof (DateTimeOffset?)) || + (clazzBoxed == typeof (DateTime?)) || + (clazzBoxed == typeof (long?)); + } + + private static String Unescape(String name) + { + if (name.StartsWith("`") && name.EndsWith("`")) + { + return name.Substring(1, name.Length - 2); + } + return name; + } + + /// + /// Determines whether the specified type is delegate. + /// + /// The type. + /// + public static bool IsDelegate(this Type type) + { + return typeof (Delegate).IsAssignableFrom(type.BaseType); + } + + /// + /// Determines whether [is collection map or array] [the specified type]. + /// + /// The type. + /// + public static bool IsCollectionMapOrArray(this Type type) + { + return (type != null) && (type.IsGenericCollection() || (type.IsGenericDictionary() || IsArray(type))); + } + + /// + /// Determines whether the target type and provided type are compatible in an array. + /// + /// The target. + /// The provided. + /// + public static bool IsArrayTypeCompatible(this Type target, Type provided) + { + if (target == provided || target == typeof(object)) + { + return true; + } + var targetBoxed = GetBoxedType(target); + var providedBoxed = GetBoxedType(provided); + return targetBoxed == providedBoxed || IsSubclassOrImplementsInterface(providedBoxed, targetBoxed); + } + + /// + /// Returns the esper name for a type. These names should be used when + /// refering to a type in a stream. Normally, this is just the standard + /// type name. However, for nested classes, we convert the '+' which is + /// embedded in the name into a '$' - this prevents the parser from being + /// unable to determine the difference between A+B which is an additive + /// function and A+B where B is a nested class of A. + /// + /// + public static string MaskTypeName() + { + return MaskTypeName(typeof (T)); + } + + /// + /// Returns the esper name for a type. These names should be used when + /// refering to a type in a stream. Normally, this is just the standard + /// type name. However, for nested classes, we convert the '+' which is + /// embedded in the name into a '$' - this prevents the parser from being + /// unable to determine the difference between A+B which is an additive + /// function and A+B where B is a nested class of A. + /// + /// The type. + /// + public static string MaskTypeName(this Type type) + { + return type.FullName.Replace('+', '$'); + } + + public static string MaskTypeName(string typename) + { + return typename.Replace('+', '$'); + } + + /// + /// Unmasks the name of the stream. + /// + /// The name. + /// + public static string UnmaskTypeName(this string name) + { + return name.Replace('$', '+'); + } + + public static string GetDefaultTypeName(this Type type) + { +#if false + if (type.IsNullable()) + { + // Nullables have been causing problems because their names arent properly + // unique. We generally dont want nullables automatically binding to public + // namespaces, so be sure to provide an opaque name when they are automatically + // bound so we can find them. + + return string.Format("__NULLABLE_{0}", Nullable.GetUnderlyingType(type).Name); + } +#endif + + return type.FullName; + } + + } + + public class TypeResolverEventArgs : EventArgs + { + public string TypeName { get; private set; } + public bool Handled { get; set; } + + /// + /// Constructs an event args object. + /// + /// + public TypeResolverEventArgs(string typeName) + { + TypeName = typeName; + Handled = false; + } + } +} diff --git a/NEsper.Core/NEsper.Core/util/TypeInstantiationException.cs b/NEsper.Core/NEsper.Core/util/TypeInstantiationException.cs new file mode 100755 index 000000000..f30943ce3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/TypeInstantiationException.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.util +{ + /// + /// Exception to represent an error instantiating a class from a class name. + /// + public class TypeInstantiationException : EPException + { + /// + /// Ctor. + /// + /// supplies the detailed description + public TypeInstantiationException(String message) + : base(message) + { + } + + /// Ctor. + /// supplies the detailed description + /// the exception cause + public TypeInstantiationException(String message, Exception cause) + : base(message, cause) + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/util/TypeWidener.cs b/NEsper.Core/NEsper.Core/util/TypeWidener.cs new file mode 100755 index 000000000..19a5e4ab4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/TypeWidener.cs @@ -0,0 +1,15 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.util +{ + /// + /// Widens a type. + /// + public delegate object TypeWidener(object input); +} diff --git a/NEsper.Core/NEsper.Core/util/TypeWidenerBoxedNumeric.cs b/NEsper.Core/NEsper.Core/util/TypeWidenerBoxedNumeric.cs new file mode 100755 index 000000000..594d9cae3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/TypeWidenerBoxedNumeric.cs @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.util +{ + /// + /// Widerner that coerces to a widened boxed number. + /// + public class TypeWidenerBoxedNumeric + { + private readonly Coercer _coercer; + + /// Ctor. + /// the coercer + public TypeWidenerBoxedNumeric(Coercer coercer) + { + _coercer = coercer; + } + + public Object Widen(Object input) + { + return _coercer.Invoke(input); + } + } +} diff --git a/NEsper.Core/NEsper.Core/util/TypeWidenerCustomizer.cs b/NEsper.Core/NEsper.Core/util/TypeWidenerCustomizer.cs new file mode 100755 index 000000000..c1e5b29a9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/TypeWidenerCustomizer.cs @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.util +{ + public interface TypeWidenerCustomizer + { + TypeWidener WidenerFor( + string columnName, + Type columnType, + Type writeablePropertyType, + string writeablePropertyName, + string statementName, + string engineURI); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/util/TypeWidenerFactory.cs b/NEsper.Core/NEsper.Core/util/TypeWidenerFactory.cs new file mode 100755 index 000000000..acea8b01f --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/TypeWidenerFactory.cs @@ -0,0 +1,245 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Linq.Expressions; +using System.Reflection; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.magic; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.util +{ + /// Factory for type widening. + public class TypeWidenerFactory + { + public static readonly TypeWidener OBJECT_ARRAY_TO_COLLECTION_COERCER = TypeWidenerObjectArrayToCollectionCoercer.Widen; + public static readonly TypeWidener STRING_TO_CHAR_COERCER = TypeWidenerStringToCharCoercer.Widen; + public static readonly TypeWidener BYTE_ARRAY_TO_COLLECTION_COERCER = TypeWidenerByteArrayToCollectionCoercer; + public static readonly TypeWidener SHORT_ARRAY_TO_COLLECTION_COERCER = TypeWidenerShortArrayToCollectionCoercer; + public static readonly TypeWidener INT_ARRAY_TO_COLLECTION_COERCER = TypeWidenerIntArrayToCollectionCoercer; + public static readonly TypeWidener LONG_ARRAY_TO_COLLECTION_COERCER = TypeWidenerLongArrayToCollectionCoercer; + public static readonly TypeWidener FLOAT_ARRAY_TO_COLLECTION_COERCER = TypeWidenerFloatArrayToCollectionCoercer; + public static readonly TypeWidener DOUBLE_ARRAY_TO_COLLECTION_COERCER = TypeWidenerDoubleArrayToCollectionCoercer; + public static readonly TypeWidener BOOLEAN_ARRAY_TO_COLLECTION_COERCER = TypeWidenerBooleanArrayToCollectionCoercer; + public static readonly TypeWidener CHAR_ARRAY_TO_COLLECTION_COERCER = TypeWidenerCharArrayToCollectionCoercer; + + /// Returns the widener. + /// name of column + /// type of column + /// property type + /// propery name + /// whether we widen object-array to collection + /// customization if any + /// engine URI + /// statement name + /// if type validation fails + /// type widender + /// ExprValidationException if type validation fails + public static TypeWidener GetCheckPropertyAssignType( + String columnName, + Type columnType, + Type writeablePropertyType, + String writeablePropertyName, + bool allowObjectArrayToCollectionConversion, + TypeWidenerCustomizer customizer, + string statementName, + string engineURI) + { + Type columnClassBoxed = TypeHelper.GetBoxedType(columnType); + Type targetClassBoxed = TypeHelper.GetBoxedType(writeablePropertyType); + + if (customizer != null) + { + TypeWidener custom = customizer.WidenerFor(columnName, columnType, writeablePropertyType, writeablePropertyName, statementName, engineURI); + if (custom != null) + { + return custom; + } + } + + if (columnType == null) + { + if (writeablePropertyType.IsPrimitive) + { + String message = "Invalid assignment of column '" + columnName + + "' of null type to event property '" + writeablePropertyName + + "' typed as '" + writeablePropertyType.FullName + + "', nullable type mismatch"; + throw new ExprValidationException(message); + } + } + else if (columnClassBoxed != targetClassBoxed) + { + if (columnClassBoxed == typeof(string) && targetClassBoxed == typeof(char?)) + { + return TypeWidenerStringToCharCoercer.Widen; + } + + if (allowObjectArrayToCollectionConversion + && columnClassBoxed.IsArray + && !columnClassBoxed.GetElementType().IsPrimitive + && targetClassBoxed.IsGenericCollection()) + { + return OBJECT_ARRAY_TO_COLLECTION_COERCER; + } + + if (columnClassBoxed.IsGenericDictionary() && targetClassBoxed.IsGenericDictionary()) + { + var columnClassGenerics = columnClassBoxed.GetGenericArguments(); + var targetClassGenerics = targetClassBoxed.GetGenericArguments(); + var transformMethod = typeof(TransformDictionaryFactory) + .GetMethod("Create", new[] { typeof(object) }) + .MakeGenericMethod(targetClassGenerics[0], targetClassGenerics[1], columnClassGenerics[0], columnClassGenerics[1]); + + return source => + { + var parameters = new object[] { source }; + return transformMethod.Invoke(null, BindingFlags.Static | BindingFlags.Public, null, parameters, null); + }; + } + + if ((columnClassBoxed == typeof(string)) && + (targetClassBoxed == typeof(char[]))) + { + return source => + { + var sourceAsString = (string)source; + return sourceAsString != null ? sourceAsString.ToCharArray() : null; + }; + } + + if ((columnClassBoxed == typeof(char[])) && + (targetClassBoxed == typeof(string))) + { + return source => + { + var sourceAsCharArray = (char[])source; + return sourceAsCharArray != null ? new string(sourceAsCharArray) : null; + }; + } + + if (columnClassBoxed.IsArray && targetClassBoxed.IsArray) + { + var columnClassElement = columnClassBoxed.GetElementType(); + var targetClassElement = targetClassBoxed.GetElementType(); + if (columnClassElement.IsAssignmentCompatible(targetClassElement)) + { + // By definition, columnClassElement and targetClassElement should be + // incompatible. Question is, can we find a coercer between them? + var coercer = CoercerFactory.GetCoercer(columnClassElement, targetClassElement); + return source => WidenArray(source, targetClassElement, coercer); + } + } + + if (!columnClassBoxed.IsAssignmentCompatible(targetClassBoxed)) + { + var writablePropName = writeablePropertyType.FullName; + if (writeablePropertyType.IsArray) + { + writablePropName = writeablePropertyType.GetElementType().FullName + "[]"; + } + + var columnTypeName = columnType.FullName; + if (columnType.IsArray) + { + columnTypeName = columnType.GetElementType().FullName + "[]"; + } + + String message = "Invalid assignment of column '" + columnName + + "' of type '" + columnTypeName + + "' to event property '" + writeablePropertyName + + "' typed as '" + writablePropName + + "', column and parameter types mismatch"; + throw new ExprValidationException(message); + } + + if (writeablePropertyType.IsNumeric()) + { + var instance = new TypeWidenerBoxedNumeric( + CoercerFactory.GetCoercer(columnClassBoxed, targetClassBoxed)); + return instance.Widen; + } + } + + return null; + } + + public static Object WidenArray(Object source, Type targetElementType, Coercer coercer) + { + var sourceArray = (Array)source; + var length = sourceArray.Length; + var targetArray = Array.CreateInstance(targetElementType, length); + + for (int ii = 0; ii < length; ii++) + { + targetArray.SetValue(coercer.Invoke(sourceArray.GetValue(ii)), ii); + } + + return targetArray; + } + + public static TypeWidener GetArrayToCollectionCoercer(Type componentType) { + if (!componentType.IsPrimitive) { + return OBJECT_ARRAY_TO_COLLECTION_COERCER; + } else if (componentType == typeof(byte)) { + return BYTE_ARRAY_TO_COLLECTION_COERCER; + } else if (componentType == typeof(short)) { + return SHORT_ARRAY_TO_COLLECTION_COERCER; + } else if (componentType == typeof(int)) { + return INT_ARRAY_TO_COLLECTION_COERCER; + } else if (componentType == typeof(long)) { + return LONG_ARRAY_TO_COLLECTION_COERCER; + } else if (componentType == typeof(float)) { + return FLOAT_ARRAY_TO_COLLECTION_COERCER; + } else if (componentType == typeof(double)) { + return DOUBLE_ARRAY_TO_COLLECTION_COERCER; + } else if (componentType == typeof(bool)) { + return BOOLEAN_ARRAY_TO_COLLECTION_COERCER; + } else if (componentType == typeof(char)) { + return CHAR_ARRAY_TO_COLLECTION_COERCER; + } + throw new IllegalStateException("Unrecognized class " + componentType); + } + + internal static object TypeWidenerByteArrayToCollectionCoercer(object input) + { + return input.Unwrap(); + } + + internal static object TypeWidenerShortArrayToCollectionCoercer(object input) { + return input.Unwrap(); + } + + internal static object TypeWidenerIntArrayToCollectionCoercer(object input) { + return input.Unwrap(); + } + + internal static object TypeWidenerLongArrayToCollectionCoercer(object input) { + return input.Unwrap(); + } + + internal static object TypeWidenerFloatArrayToCollectionCoercer(object input) { + return input.Unwrap(); + } + + internal static object TypeWidenerDoubleArrayToCollectionCoercer(object input) { + return input.Unwrap(); + } + + internal static object TypeWidenerBooleanArrayToCollectionCoercer(object input) { + return input.Unwrap(); + } + + internal static object TypeWidenerCharArrayToCollectionCoercer(object input) { + return input.Unwrap(); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/util/TypeWidenerObjectArrayToCollectionCoercer.cs b/NEsper.Core/NEsper.Core/util/TypeWidenerObjectArrayToCollectionCoercer.cs new file mode 100755 index 000000000..01c6946e2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/TypeWidenerObjectArrayToCollectionCoercer.cs @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.util +{ + /// + /// Type widner that coerces from string to char if required. + /// + public class TypeWidenerObjectArrayToCollectionCoercer + { + public static object Widen(Object input) + { + return input.Unwrap(true); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/util/TypeWidenerStringToCharCoercer.cs b/NEsper.Core/NEsper.Core/util/TypeWidenerStringToCharCoercer.cs new file mode 100755 index 000000000..e5695be39 --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/TypeWidenerStringToCharCoercer.cs @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.util +{ + /// + /// Type widener that coerces from String to char if required. + /// + public class TypeWidenerStringToCharCoercer + { + public static Object Widen(Object input) + { + string result = input.ToString(); + if ((result != null) && (result.Length > 0)) + { + return result[0]; + } + return null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/util/URIUtil.cs b/NEsper.Core/NEsper.Core/util/URIUtil.cs new file mode 100755 index 000000000..f413f7633 --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/URIUtil.cs @@ -0,0 +1,168 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.util +{ + /// + /// Utility for inspecting and comparing URI. + /// + public class URIUtil + { + /// + /// Determines whether the specified URI is opaque. A URI is opaque if + /// is not hierarchical. An example of an opaque URL is the mailto URI. + /// + /// The URI. + /// + /// true if the specified URI is opaque; otherwise, false. + /// + public static bool IsOpaque(Uri uri) + { + return false; + } + + + /// + /// Given a child URI and a map of factory URIs, inspect the child URI against the factory + /// URIs and return a collection of entries for which the child URI falls within or is equals + /// to the factory URI. + /// + /// is the child URI to match against factory URIs + /// is a map of factory URI and an object + /// matching factory URIs, if any + public static ICollection> FilterSort(Uri child, IDictionary uris) + { + var childPathIsOpaque = IsOpaque(child); + var childPathIsRelative = !child.IsAbsoluteUri; + var childPathElements = ParsePathElements(child); + + var result = new OrderedDictionary>(); + foreach (var entry in uris) + { + var factoryUri = entry.Key; + + // handle opaque (mailto:) and relative (a/b) using equals + if (childPathIsOpaque || childPathIsRelative || !factoryUri.IsAbsoluteUri || IsOpaque(factoryUri)) + { + if (factoryUri.Equals(child)) + { + result.Put(int.MinValue, entry); // Equals is a perfect match + } + continue; + } + + // handle absolute URIs, compare scheme and authority if present + if ( ((child.Scheme != null) && (factoryUri.Scheme == null)) || + ((child.Scheme == null) && (factoryUri.Scheme != null)) ) + { + continue; + } + if ((child.Scheme != null) && (!child.Scheme.Equals(factoryUri.Scheme))) + { + continue; + } + if ( ((child.Authority != null) && (factoryUri.Authority == null)) || + ((child.Authority == null) && (factoryUri.Authority != null)) ) + { + continue; + } + if ((child.Authority != null) && (child.Authority != factoryUri.Authority)) + { + continue; + } + + // Match the child + String[] factoryPathElements = ParsePathElements(factoryUri); + int score = ComputeScore(childPathElements, factoryPathElements); + if (score > 0) + { + result.Put(score, entry); // Partial match if score is positive + } + } + + return result.Values; + } + + private static String GetPath(Uri uri) + { + try + { + return uri.AbsolutePath; + } + catch (InvalidOperationException) + { + return uri.OriginalString; + } + } + + public static String[] ParsePathElements(Uri uri) + { + var path = GetPath(uri); + if (path == null) + { + return new String[0]; + } + while (path.StartsWith("/")) + { + path = path.Substring(1); + } + var split = path.Split('/'); + if ((split.Length > 0) && (split[0].Length == 0)) + { + return new String[0]; + } + return split; + } + + private static int ComputeScore(String[] childPathElements, String[] factoryPathElements) { + int index = 0; + + if (factoryPathElements.Length == 0) + { + return int.MaxValue; // the most general factory scores the lowest + } + + while(true) + { + if ((childPathElements.Length > index) && + (factoryPathElements.Length > index)) + { + if (!(childPathElements[index].Equals(factoryPathElements[index]))) + { + return 0; + } + } + else + { + if (childPathElements.Length <= index) + { + if (factoryPathElements.Length > index) + { + return 0; + } + return int.MaxValue - index - 1; + } + } + + if (factoryPathElements.Length <= index) + { + break; + } + + index++; + } + + return int.MaxValue - index - 1; + } + } +} diff --git a/NEsper.Core/NEsper.Core/util/UuidGenerator.cs b/NEsper.Core/NEsper.Core/util/UuidGenerator.cs new file mode 100755 index 000000000..ef7f13a15 --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/UuidGenerator.cs @@ -0,0 +1,24 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.util +{ + public class UuidGenerator + { + /// + /// Returns a unique uuid. + /// + /// a unique uuid + public static String Generate() + { + return Guid.NewGuid().ToString() ; + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/util/ValidationUtil.cs b/NEsper.Core/NEsper.Core/util/ValidationUtil.cs new file mode 100755 index 000000000..2dcabcb94 --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/ValidationUtil.cs @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.util +{ + public class ValidationUtil + { + public static void ValidateRequiredPropString(String value, String operatorName, String propertyName) + { + if (string.IsNullOrWhiteSpace(value)) + { + throw GetRequiredPropException(propertyName, operatorName); + } + } + + public static void ValidateRequiredProp(Object value, String operatorName, String propertyName) + { + if (value == null) + { + throw GetRequiredPropException(propertyName, operatorName); + } + } + + private static EPException GetRequiredPropException(String propertyName, String operatorName) + { + return new EPException("Required property '" + propertyName + "' for operator " + operatorName + "' is not provided"); + } + } +} diff --git a/NEsper.Core/NEsper.Core/util/Version.cs b/NEsper.Core/NEsper.Core/util/Version.cs new file mode 100755 index 000000000..28c5813bd --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/Version.cs @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; + +namespace com.espertech.esper.util +{ + public class Version { + public static readonly string VERSION = "6.0.1"; + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/util/support/SupportEventTypeAssertionEnum.cs b/NEsper.Core/NEsper.Core/util/support/SupportEventTypeAssertionEnum.cs new file mode 100755 index 000000000..88be06e81 --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/support/SupportEventTypeAssertionEnum.cs @@ -0,0 +1,72 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.util.support +{ + public enum SupportEventTypeAssertionEnum + { + NAME, + TYPE, + FRAGEMENT_TYPE_NAME, + FRAGMENT_IS_INDEXED + } + + public static class SupportEventTypeAssertionEnumExtensions + { + public static Extractor GetExtractor( + this SupportEventTypeAssertionEnum enumValue) + { + switch (enumValue) + { + case SupportEventTypeAssertionEnum.NAME: + return (desc, eventType) => desc.PropertyName; + case SupportEventTypeAssertionEnum.TYPE: + return (desc, eventType) => desc.PropertyType; + case SupportEventTypeAssertionEnum.FRAGEMENT_TYPE_NAME: + return (desc, eventType) => + { + var fragType = eventType.GetFragmentType(desc.PropertyName); + if (fragType == null) + { + return null; + } + return fragType.FragmentType.Name; + }; + case SupportEventTypeAssertionEnum.FRAGMENT_IS_INDEXED: + return (desc, eventType) => + { + var fragType = eventType.GetFragmentType(desc.PropertyName); + if (fragType == null) + { + return null; + } + return fragType.IsIndexed; + }; + } + + throw new ArgumentException("value out of bounds", "enumValue"); + } + + public static SupportEventTypeAssertionEnum[] GetSetWithFragment() + { + return new SupportEventTypeAssertionEnum[] + { + SupportEventTypeAssertionEnum.NAME, + SupportEventTypeAssertionEnum.TYPE, + SupportEventTypeAssertionEnum.FRAGEMENT_TYPE_NAME, + SupportEventTypeAssertionEnum.FRAGMENT_IS_INDEXED + }; + } + } + + public delegate object Extractor(EventPropertyDescriptor desc, EventType eventType); +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/util/support/SupportEventTypeAssertionUtil.cs b/NEsper.Core/NEsper.Core/util/support/SupportEventTypeAssertionUtil.cs new file mode 100755 index 000000000..2756d4de2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/support/SupportEventTypeAssertionUtil.cs @@ -0,0 +1,551 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.IO; +using System.Xml; + +using com.espertech.esper.client; +using com.espertech.esper.client.scopetest; +using com.espertech.esper.compat; + +namespace com.espertech.esper.util.support +{ + public class SupportEventTypeAssertionUtil + { + public static void AssertFragments(EventBean @event, bool isNative, bool array, string propertyExpressions) + { + string[] names = propertyExpressions.SplitCsv(); + foreach (string name in names) + { + if (!array) + { + AssertFragmentNonArray(@event, isNative, name); + } + else + { + AssertFragmentArray(@event, isNative, name); + } + } + } + + public static void AssertConsistency(EventBean eventBean) + { + AssertConsistencyRecusive(eventBean, new HashSet()); + } + + public static void AssertConsistency(EventType eventType) + { + AssertConsistencyRecursive(eventType, new HashSet()); + } + + public static string Print(EventBean theEvent) + { + var writer = new StringWriter(); + Print(theEvent, writer, 0, new Stack()); + return writer.ToString(); + } + + private static void Print(EventBean theEvent, StringWriter writer, int indent, Stack propertyStack) + { + WriteIndent(writer, indent); + writer.Write("Properties : \n"); + PrintProperties(theEvent, writer, indent + 2, propertyStack); + + // count fragments + int countFragments = 0; + foreach (EventPropertyDescriptor desc in theEvent.EventType.PropertyDescriptors) + { + if (desc.IsFragment) + { + countFragments++; + } + } + if (countFragments == 0) + { + return; + } + + WriteIndent(writer, indent); + writer.Write("Fragments : (" + countFragments + ") \n"); + foreach (EventPropertyDescriptor desc in theEvent.EventType.PropertyDescriptors) + { + if (!desc.IsFragment) + { + continue; + } + + WriteIndent(writer, indent + 2); + writer.Write(desc.PropertyName); + writer.Write(" : "); + + if (desc.RequiresIndex) + { + writer.Write("\n"); + int count = 0; + while (true) + { + try + { + WriteIndent(writer, indent + 4); + writer.Write("bean #"); + writer.Write(count); + var result = (EventBean) theEvent.GetFragment(desc.PropertyName + "[" + count + "]"); + if (result == null) + { + writer.Write("(null EventBean)\n"); + } + else + { + writer.Write("\n"); + propertyStack.Push(desc.PropertyName); + Print(result, writer, indent + 6, propertyStack); + propertyStack.Pop(); + } + count++; + } + catch (PropertyAccessException) + { + writer.Write("-- no access --\n"); + break; + } + } + } + else + { + object fragment = theEvent.GetFragment(desc.PropertyName); + if (fragment == null) + { + writer.Write("(null)\n"); + continue; + } + + if (fragment is EventBean) + { + var fragmentBean = (EventBean) fragment; + writer.Write("EventBean type "); + writer.Write(fragmentBean.EventType.Name); + writer.Write("...\n"); + + // prevent GetThis() loops + if (fragmentBean.EventType == theEvent.EventType) + { + WriteIndent(writer, indent + 2); + writer.Write("Skipping"); + } + else + { + propertyStack.Push(desc.PropertyName); + Print(fragmentBean, writer, indent + 4, propertyStack); + propertyStack.Pop(); + } + } + else + { + var fragmentBeans = (EventBean[]) fragment; + writer.Write("EventBean[] type "); + if (fragmentBeans.Length == 0) + { + writer.Write("(empty array)\n"); + } + else + { + writer.Write(fragmentBeans[0].EventType.Name); + writer.Write("...\n"); + for (int i = 0; i < fragmentBeans.Length; i++) + { + WriteIndent(writer, indent + 4); + writer.Write("bean #" + i + "...\n"); + + propertyStack.Push(desc.PropertyName); + Print(fragmentBeans[i], writer, indent + 6, propertyStack); + propertyStack.Pop(); + } + } + } + } + } + } + + private static void PrintProperties( + EventBean eventBean, + StringWriter writer, + int indent, + Stack propertyStack) + { + IList properties = eventBean.EventType.PropertyDescriptors; + + // write simple properties + for (int i = 0; i < properties.Count; i++) + { + string propertyName = properties[i].PropertyName; + + if (properties[i].IsIndexed || properties[i].IsMapped) + { + continue; + } + + WriteIndent(writer, indent); + writer.Write(propertyName); + writer.Write(" : "); + + object resultGet = eventBean.Get(propertyName); + WriteValue(writer, resultGet); + writer.Write("\n"); + } + + // write indexed properties + for (int i = 0; i < properties.Count; i++) + { + string propertyName = properties[i].PropertyName; + + if (!properties[i].IsIndexed) + { + continue; + } + + WriteIndent(writer, indent); + writer.Write(propertyName); + string type = "array"; + if (properties[i].RequiresIndex) + { + type = type + " requires-index"; + } + writer.Write(" (" + type + ") : "); + + if (properties[i].RequiresIndex) + { + int count = 0; + writer.Write("\n"); + while (true) + { + try + { + WriteIndent(writer, indent + 2); + writer.Write("#"); + writer.Write(count); + writer.Write(" "); + object result = eventBean.Get(propertyName + "[" + count + "]"); + WriteValue(writer, result); + writer.Write("\n"); + count++; + } + catch (PropertyAccessException) + { + writer.Write("-- no access --\n"); + break; + } + } + } + else + { + object result = eventBean.Get(propertyName); + WriteValue(writer, result); + writer.Write("\n"); + } + } + + // write mapped properties + for (int i = 0; i < properties.Count; i++) + { + string propertyName = properties[i].PropertyName; + + if (!properties[i].IsMapped) + { + continue; + } + + WriteIndent(writer, indent); + writer.Write(propertyName); + string type = "mapped"; + if (properties[i].RequiresMapKey) + { + type = type + " requires-mapkey"; + } + writer.Write(" (" + type + ") : "); + + if (!properties[i].RequiresMapKey) + { + object result = eventBean.Get(propertyName); + WriteValue(writer, result); + writer.Write("\n"); + } + else + { + writer.Write("??map key unknown??\n"); + } + } + } + + private static void AssertConsistencyRecusive(EventBean eventBean, ISet alreadySeenTypes) + { + AssertConsistencyRecursive(eventBean.EventType, alreadySeenTypes); + + IList properties = eventBean.EventType.PropertyDescriptors; + for (int i = 0; i < properties.Count; i++) + { + string failedMessage = "failed assertion for property '" + properties[i].PropertyName + "' "; + string propertyName = properties[i].PropertyName; + + // assert getter + if ((!properties[i].RequiresIndex) && (!properties[i].RequiresMapKey)) + { + EventPropertyGetter getter = eventBean.EventType.GetGetter(propertyName); + object resultGetter = getter.Get(eventBean); + object resultGet = eventBean.Get(propertyName); + + if ((resultGetter == null) && (resultGet == null)) + { + // fine + } + else if (resultGet is XmlNodeList) + { + ScopeTestHelper.AssertEquals( + failedMessage, + ((XmlNodeList) resultGet).Count, + ((XmlNodeList) resultGetter).Count); + } + else if (resultGet is Array) + { + ScopeTestHelper.AssertEquals( + failedMessage, + ((Array) resultGet).Length, + ((Array) resultGetter).Length); + } + else + { + ScopeTestHelper.AssertEquals(failedMessage, resultGet, resultGetter); + } + + if (resultGet != null) + { + if (resultGet is EventBean[] || resultGet is EventBean) + { + ScopeTestHelper.AssertTrue(properties[i].IsFragment); + } + else + { + ScopeTestHelper.AssertTrue( + failedMessage, + TypeHelper.IsSubclassOrImplementsInterface( + resultGet.GetType(), + properties[i].PropertyType.GetBoxedType())); + } + } + } + + // fragment + if (!properties[i].IsFragment) + { + ScopeTestHelper.AssertNull(failedMessage, eventBean.GetFragment(propertyName)); + continue; + } + + object fragment = eventBean.GetFragment(propertyName); + ScopeTestHelper.AssertNotNull(failedMessage, fragment); + + FragmentEventType fragmentType = eventBean.EventType.GetFragmentType(propertyName); + ScopeTestHelper.AssertNotNull(failedMessage, fragmentType); + + if (!fragmentType.IsIndexed) + { + ScopeTestHelper.AssertTrue(failedMessage, fragment is EventBean); + var fragmentEvent = (EventBean) fragment; + AssertConsistencyRecusive(fragmentEvent, alreadySeenTypes); + } + else + { + ScopeTestHelper.AssertTrue(failedMessage, fragment is EventBean[]); + var events = (EventBean[]) fragment; + ScopeTestHelper.AssertTrue(failedMessage, events.Length > 0); + foreach (EventBean theEvent in events) + { + AssertConsistencyRecusive(theEvent, alreadySeenTypes); + } + } + } + } + + private static void AssertConsistencyRecursive(EventType eventType, ISet alreadySeenTypes) + { + if (alreadySeenTypes.Contains(eventType)) + { + return; + } + alreadySeenTypes.Add(eventType); + + AssertConsistencyProperties(eventType); + + // test fragments + foreach (EventPropertyDescriptor descriptor in eventType.PropertyDescriptors) + { + string failedMessage = "failed assertion for property '" + descriptor.PropertyName + "' "; + if (!descriptor.IsFragment) + { + ScopeTestHelper.AssertNull(failedMessage, eventType.GetFragmentType(descriptor.PropertyName)); + continue; + } + + FragmentEventType fragment = eventType.GetFragmentType(descriptor.PropertyName); + if (!descriptor.RequiresIndex) + { + ScopeTestHelper.AssertNotNull(failedMessage, fragment); + if (fragment.IsIndexed) + { + ScopeTestHelper.AssertTrue(descriptor.IsIndexed); + } + AssertConsistencyRecursive(fragment.FragmentType, alreadySeenTypes); + } + else + { + fragment = eventType.GetFragmentType(descriptor.PropertyName + "[0]"); + ScopeTestHelper.AssertNotNull(failedMessage, fragment); + ScopeTestHelper.AssertTrue(descriptor.IsIndexed); + AssertConsistencyRecursive(fragment.FragmentType, alreadySeenTypes); + } + } + } + + private static void AssertConsistencyProperties(EventType eventType) + { + var propertyNames = new List(); + + IList properties = eventType.PropertyDescriptors; + for (int i = 0; i < properties.Count; i++) + { + string propertyName = properties[i].PropertyName; + propertyNames.Add(propertyName); + string failedMessage = "failed assertion for property '" + propertyName + "' "; + + // assert presence of descriptor + ScopeTestHelper.AssertSame(properties[i], eventType.GetPropertyDescriptor(propertyName)); + + // test properties that can simply be in a property expression + if ((!properties[i].RequiresIndex) && (!properties[i].RequiresMapKey)) + { + ScopeTestHelper.AssertTrue(failedMessage, eventType.IsProperty(propertyName)); + ScopeTestHelper.AssertSame( + failedMessage, eventType.GetPropertyType(propertyName), properties[i].PropertyType); + ScopeTestHelper.AssertNotNull(failedMessage, eventType.GetGetter(propertyName)); + } + + // test indexed property + if (properties[i].IsIndexed) + { + string propertyNameIndexed = propertyName + "[0]"; + ScopeTestHelper.AssertTrue(failedMessage, eventType.IsProperty(propertyNameIndexed)); + ScopeTestHelper.AssertNotNull(failedMessage, eventType.GetPropertyType(propertyNameIndexed)); + ScopeTestHelper.AssertNotNull(failedMessage, eventType.GetGetter(propertyNameIndexed)); + } + + // test mapped property + if (properties[i].RequiresMapKey) + { + string propertyNameMapped = propertyName + "('a')"; + ScopeTestHelper.AssertTrue(failedMessage, eventType.IsProperty(propertyNameMapped)); + ScopeTestHelper.AssertNotNull(failedMessage, eventType.GetPropertyType(propertyNameMapped)); + ScopeTestHelper.AssertNotNull(failedMessage, eventType.GetGetter(propertyNameMapped)); + } + + // consistent flags + ScopeTestHelper.AssertFalse(failedMessage, properties[i].IsIndexed && properties[i].IsMapped); + if (properties[i].RequiresIndex) + { + ScopeTestHelper.AssertTrue(failedMessage, properties[i].IsIndexed); + } + if (properties[i].RequiresMapKey) + { + ScopeTestHelper.AssertTrue(failedMessage, properties[i].IsMapped); + } + } + + // assert same property names + EPAssertionUtil.AssertEqualsAnyOrder(eventType.PropertyNames, propertyNames.ToArray()); + } + + private static void WriteIndent(TextWriter writer, int indent) + { + for (int i = 0; i < indent; i++) + { + writer.Write(' '); + } + } + + private static void WriteValue(TextWriter writer, Object result) + { + if (result == null) + { + writer.Write("(null)"); + return; + } + + var asArray = result as Array; + if (asArray != null) + { + writer.Write("Array len="); + writer.Write(asArray.Length); + writer.Write("{"); + string delimiter = ""; + for (int i = 0; i < asArray.Length; i++) + { + writer.Write(delimiter); + WriteValue(writer, asArray.GetValue(i)); + delimiter = ", "; + } + writer.Write("}"); + } + else + { + writer.Write(result.ToString()); + } + } + + public static void AssertEventTypeProperties( + Object[][] expectedArr, + EventType eventType, + params SupportEventTypeAssertionEnum[] assertions) + { + for (int propNum = 0; propNum < expectedArr.Length; propNum++) + { + string message = "Failed assertion for property " + propNum; + EventPropertyDescriptor prop = eventType.PropertyDescriptors[propNum]; + + for (int i = 0; i < assertions.Length; i++) + { + SupportEventTypeAssertionEnum assertion = assertions[i]; + object expected = expectedArr[propNum][i]; + object value = assertion.GetExtractor().Invoke(prop, eventType); + ScopeTestHelper.AssertEquals(message + " at assertion " + assertion, expected, value); + } + } + } + + private static void AssertFragmentNonArray(EventBean @event, bool isNative, string propertyExpression) + { + var fragmentBean = (EventBean) @event.GetFragment(propertyExpression); + FragmentEventType fragmentType = @event.EventType.GetFragmentType(propertyExpression); + ScopeTestHelper.AssertFalse("failed for " + propertyExpression, fragmentType.IsIndexed); + ScopeTestHelper.AssertEquals("failed for " + propertyExpression, isNative, fragmentType.IsNative); + ScopeTestHelper.AssertSame( + "failed for " + propertyExpression, fragmentBean.EventType, fragmentType.FragmentType); + AssertConsistency(fragmentBean); + } + + private static void AssertFragmentArray(EventBean @event, bool isNative, string propertyExpression) + { + var fragmentBean = (EventBean[]) @event.GetFragment(propertyExpression); + FragmentEventType fragmentType = @event.EventType.GetFragmentType(propertyExpression); + ScopeTestHelper.AssertTrue("failed for " + propertyExpression, fragmentType.IsIndexed); + ScopeTestHelper.AssertEquals("failed for " + propertyExpression, isNative, fragmentType.IsNative); + ScopeTestHelper.AssertSame( + "failed for " + propertyExpression, fragmentBean[0].EventType, fragmentType.FragmentType); + AssertConsistency(fragmentBean[0]); + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/util/support/SupportExprEvaluatorContext.cs b/NEsper.Core/NEsper.Core/util/support/SupportExprEvaluatorContext.cs new file mode 100755 index 000000000..9c543eb96 --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/support/SupportExprEvaluatorContext.cs @@ -0,0 +1,88 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.compat.threading; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.script; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.schedule; + +namespace com.espertech.esper.util.support +{ + public class SupportExprEvaluatorContext : ExprEvaluatorContext + { + private readonly TimeProvider _timeProvider; + + public SupportExprEvaluatorContext(TimeProvider timeProvider) + { + _timeProvider = timeProvider; + } + + public TimeProvider TimeProvider + { + get { return _timeProvider; } + } + + public ExpressionResultCacheService ExpressionResultCacheService + { + get { return null; } + } + + public int AgentInstanceId + { + get { return -1; } + } + + public EventBean ContextProperties + { + get { return null; } + } + + public AgentInstanceScriptContext AllocateAgentInstanceScriptContext + { + get { return null; } + } + + public string StatementName + { + get { return null; } + } + + public string EngineURI + { + get { return null; } + } + + public int StatementId + { + get { return 1; } + } + + public IReaderWriterLock AgentInstanceLock + { + get { return null; } + } + + public StatementType? StatementType + { + get { return core.service.StatementType.SELECT; } + } + + public TableExprEvaluatorContext TableExprEvaluatorContext + { + get { return null; } + } + + public object StatementUserObject + { + get { return null; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/util/support/SupportExprValidationContextFactory.cs b/NEsper.Core/NEsper.Core/util/support/SupportExprValidationContextFactory.cs new file mode 100755 index 000000000..a2c89515a --- /dev/null +++ b/NEsper.Core/NEsper.Core/util/support/SupportExprValidationContextFactory.cs @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.epl.agg.factory; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.time; + +namespace com.espertech.esper.util.support +{ + public class SupportExprValidationContextFactory + { + public static ExprValidationContext MakeEmpty() + { + return MakeEmpty(ConfigurationEngineDefaults.ThreadingProfile.NORMAL); + } + + public static ExprValidationContext MakeEmpty(ConfigurationEngineDefaults.ThreadingProfile threadingProfile) + { + return new ExprValidationContext( + null, + new EngineImportServiceImpl( + false, false, false, false, null, + TimeZoneInfo.Local, + TimeAbacusMilliseconds.INSTANCE, + threadingProfile, null, AggregationFactoryFactoryDefault.INSTANCE), + null, null, null, null, null, + new SupportExprEvaluatorContext(null), null, null, 1, null, null, null, + false, false, false, false, null, false); + } + + public static ExprValidationContext Make(StreamTypeService streamTypeService) + { + return new ExprValidationContext( + streamTypeService, + null, null, null, null, null, null, + new SupportExprEvaluatorContext(null), null, null, + -1, null, null, null, + false, false, false, false, null, false); + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/view/AsymetricDataWindowViewFactory.cs b/NEsper.Core/NEsper.Core/view/AsymetricDataWindowViewFactory.cs new file mode 100755 index 000000000..67b18f6d7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/AsymetricDataWindowViewFactory.cs @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.view +{ + /// + /// Marker interface for use with view factories that create data window views that + /// are asymetric in posting insert and remove stream data: Data windows that post + /// only a partial insert and remove stream as output when compared to the insert + /// and remove stream received. + /// + /// Please for details on + /// views that meet data window requirements. + /// + public interface AsymetricDataWindowViewFactory : DataWindowViewFactory + { + } +} diff --git a/NEsper.Core/NEsper.Core/view/CloneableView.cs b/NEsper.Core/NEsper.Core/view/CloneableView.cs new file mode 100755 index 000000000..064f77ba4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/CloneableView.cs @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.view +{ + /// + /// Views that can work under a group-by must be able to duplicate and are required to implement this interface. + /// + public interface CloneableView + { + /// + /// Duplicates the view. + /// + /// Expected to return a same view in initialized state for grouping. + /// + /// + /// cloned view + View CloneView(); + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/view/DataWindowBatchingViewFactory.cs b/NEsper.Core/NEsper.Core/view/DataWindowBatchingViewFactory.cs new file mode 100755 index 000000000..bddaa9ee1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/DataWindowBatchingViewFactory.cs @@ -0,0 +1,21 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.view +{ + /// + /// Tag interface for data window view factories that express a batch expiry policy. + /// + /// Such data windows allow iteration through the currently batched events, and such + /// data windows post insert stream events only when batching conditions have been met + /// and the batch is released. + /// + public interface DataWindowBatchingViewFactory + { + } +} diff --git a/NEsper.Core/NEsper.Core/view/DataWindowView.cs b/NEsper.Core/NEsper.Core/view/DataWindowView.cs new file mode 100755 index 000000000..1df8ebde7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/DataWindowView.cs @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.view +{ + /// + /// Tag interface for data window views. Data window views follow the view interface but + /// keep a window over the data received by their parent view. Data window view may keep + /// length windows or time windows or other windows. + /// + /// Data window views generally follow the following behavior: + /// + /// They publish the data that was received as new data from their parent view directly or at a later time as new data to child views. + /// + /// They publish the data that expires out of the window (for length or time reasons or other reasons) as old data to their child views. + /// + /// They do not change event type compared to their parent view, since they only hold events temporarily. + /// + /// They remove the data they receive as old data from their parent view out of the window and report the data removed as old data to + /// child views (this is an optional capability for performance reasons). + /// + /// Certain views may decide to attach only to data window views directly. One reason for this is that window limit the number of event + /// instances kept in a collection. Without this limitation some views may not work correctly over time as events accumulate but are + /// not removed from the view by means old data updates received from a parent data window. + /// + public interface DataWindowView : View, ViewDataVisitable, GroupableView + { + } +} diff --git a/NEsper.Core/NEsper.Core/view/DataWindowViewFactory.cs b/NEsper.Core/NEsper.Core/view/DataWindowViewFactory.cs new file mode 100755 index 000000000..f3e831ac7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/DataWindowViewFactory.cs @@ -0,0 +1,19 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.view +{ + /// + /// Marker interface for use with view factories that create data window views only. + /// + /// Please for details on views that meet data window requirements. + /// + public interface DataWindowViewFactory : ViewFactory + { + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/view/DataWindowViewFactoryUniqueCandidate.cs b/NEsper.Core/NEsper.Core/view/DataWindowViewFactoryUniqueCandidate.cs new file mode 100755 index 000000000..1d6eb3525 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/DataWindowViewFactoryUniqueCandidate.cs @@ -0,0 +1,17 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +namespace com.espertech.esper.view +{ + public interface DataWindowViewFactoryUniqueCandidate + { + ICollection UniquenessCandidatePropertyNames { get; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/view/DataWindowViewWithPrevious.cs b/NEsper.Core/NEsper.Core/view/DataWindowViewWithPrevious.cs new file mode 100755 index 000000000..812faa6ff --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/DataWindowViewWithPrevious.cs @@ -0,0 +1,15 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.view +{ + public interface DataWindowViewWithPrevious + { + object MakePreviousGetter(); + } +} diff --git a/NEsper.Core/NEsper.Core/view/DerivedValueView.cs b/NEsper.Core/NEsper.Core/view/DerivedValueView.cs new file mode 100755 index 000000000..6e17192ee --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/DerivedValueView.cs @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Text.RegularExpressions; + +namespace com.espertech.esper.view +{ + /// + /// Tag interface for derived-value views. Derived-value views follow the view interface and do not keep a window over the + /// data received by their parent view. They simply derive a set of data points from a stream and + /// do not retain events. + /// + /// Derived-Value views generally follow the following behavior: + /// + /// They publish the output data when receiving insert or remove stream data from their parent view, directly and not time-driven. + /// + /// They typically change event type compared to their parent view, since they derive new information or add information to events. + /// + public interface DerivedValueView : View, GroupableView + { + } +} diff --git a/NEsper.Core/NEsper.Core/view/EventCollection.cs b/NEsper.Core/NEsper.Core/view/EventCollection.cs new file mode 100755 index 000000000..1ac397b96 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/EventCollection.cs @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; + +namespace com.espertech.esper.view +{ + /// + /// Interface that marks an event collection. Every event in the event collection must be + /// of the same event type, as defined by the EventType property. + /// + public interface EventCollection : IEnumerable + { + /// Provides metadata information about the type of object the event collection contains. + /// metadata for the objects in the collection + /// + + EventType EventType { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/view/EventStream.cs b/NEsper.Core/NEsper.Core/view/EventStream.cs new file mode 100755 index 000000000..d23da996f --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/EventStream.cs @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using com.espertech.esper.client; + +namespace com.espertech.esper.view +{ + /// + /// A streams is a conduct for incoming events. Incoming data is placed into streams for consumption by queries. + /// + public interface EventStream : Viewable + { + /// Set a new event onto the stream. + /// to insert + void Insert(EventBean theEvent); + + /// + /// Insert new events onto the stream. + /// + /// to insert + void Insert(EventBean[] events); + } +} diff --git a/NEsper.Core/NEsper.Core/view/GroupableView.cs b/NEsper.Core/NEsper.Core/view/GroupableView.cs new file mode 100755 index 000000000..5cc5258a0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/GroupableView.cs @@ -0,0 +1,15 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.view +{ + public interface GroupableView + { + ViewFactory ViewFactory { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/view/HistoricalEventViewable.cs b/NEsper.Core/NEsper.Core/view/HistoricalEventViewable.cs new file mode 100755 index 000000000..69fb5cc6f --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/HistoricalEventViewable.cs @@ -0,0 +1,75 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.threading; +using com.espertech.esper.epl.db; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.join.pollindex; +using com.espertech.esper.epl.join.table; +using com.espertech.esper.util; + +namespace com.espertech.esper.view +{ + /// + /// Interface for views that poll data based on information from other streams. + /// + public interface HistoricalEventViewable + : Viewable + , ValidatedView + , StopCallback + { + /// + /// Returns true if the parameters expressions to the historical require other stream's data, or + /// false if there are no parameters or all parameter expressions are only contants and variables + /// without properties of other stream events. + /// + /// + /// indicator whether properties are required for parameter evaluation + /// + bool HasRequiredStreams { get; } + + /// + /// Returns the a set of stream numbers of all streams that provide property values in any of the + /// parameter expressions to the stream. + /// + /// set of stream numbers + ICollection RequiredStreams { get; } + + /// + /// Historical views are expected to provide a thread-local data cache for use in keeping row + /// ( references) returned during iteration + /// stable, since the concept of a primary key does not exist. + /// + /// The data cache thread local. + /// thread-local cache, can be null for any thread to indicate no caching + IThreadLocal DataCacheThreadLocal { get; } + + /// + /// Poll for stored historical or reference data using events per stream and returing for each + /// event-per-stream row a separate list with events representing the poll result. + /// + /// is the events per stream where thefirst dimension is a number of rows (often 1 depending on windows used) and the second dimension is the number of streams participating in a join. + /// the strategy to use for converting poll results into a indexed table for fast lookup + /// context for expression evalauation + /// + /// array of lists with one list for each event-per-stream row + /// + EventTable[][] Poll(EventBean[][] lookupEventsPerStream, PollResultIndexingStrategy indexingStrategy, ExprEvaluatorContext exprEvaluatorContext); + + /// + /// Gets the optional data cache. + /// + /// + /// The optional data cache. + /// + DataCache OptionalDataCache { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/view/InitializableView.cs b/NEsper.Core/NEsper.Core/view/InitializableView.cs new file mode 100755 index 000000000..ec9d143a3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/InitializableView.cs @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.view +{ + /// + /// Views that require initialization after view instantiation and after view hook-up with the parent view + /// can impleeent this interface and get invoked to initialize. + /// + public interface InitializableView + { + /// Initializes a view. + void Initialize(); + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/view/PropertyCheckHelper.cs b/NEsper.Core/NEsper.Core/view/PropertyCheckHelper.cs new file mode 100755 index 000000000..4a588b7d3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/PropertyCheckHelper.cs @@ -0,0 +1,142 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.client; +using com.espertech.esper.util; + +namespace com.espertech.esper.view +{ + /// + /// Utility class for checking in a schema if fields exist and/or have an + /// expected type. + /// + public sealed class PropertyCheckHelper + { + /// Check if the field identified by the field name exists according to the schema. + /// contains metadata about fields + /// is the field's field name to test + /// a String error message if the field doesn't exist, or null to indicate success + + public static String Exists(EventType type, String fieldName) + { + Type clazz = GetClass(type, fieldName); + + if (clazz == null) + { + return "Parent view does not contain a field named '" + fieldName + "'"; + } + + return null; + } + + /// Check if the fields identified by the field names both exists according to the schema. + /// contains metadata about fields + /// is the first field's field name to test + /// is the first field's field name to test + /// a String error message if either of the fields doesn't exist, or null to indicate success + /// + public static String Exists(EventType type, String fieldNameOne, String fieldNameTwo) + { + Type clazz = GetClass(type, fieldNameOne); + + if (clazz == null) + { + return "Parent view does not contain a field named '" + fieldNameOne + "'"; + } + + clazz = GetClass(type, fieldNameTwo); + + if (clazz == null) + { + return "Parent view does not contain a field named '" + fieldNameTwo + "'"; + } + + return null; + } + + /// Check if the field identified by the field name is a valid numeric field according to the schema. + /// contains metadata about fields + /// is the field's field name to test + /// a String error message if the field doesn't exist or is not numeric, or null to indicate success + + public static String CheckNumeric(EventType type, String numericFieldName) + { + return CheckFieldNumeric(type, numericFieldName); + } + + /// Check if the fields identified by their field names are valid numeric field according to the schema. + /// contains metadata about fields + /// is the first field's field name to test + /// is the second field's field name to test + /// a String error message if the field doesn't exist or is not numeric, or null to indicate success + /// + public static String CheckNumeric(EventType type, String numericFieldNameX, String numericFieldNameY) + { + String error = CheckFieldNumeric(type, numericFieldNameX); + if (error != null) + { + return error; + } + + return CheckFieldNumeric(type, numericFieldNameY); + } + + /// Check if the field identified by the field name is of type long according to the schema. + /// contains metadata about fields + /// is the field's field name to test + /// a String error message if the field doesn't exist or is not a long, or null to indicate success + public static String CheckLong(EventType type, String longFieldName) + { + Type clazz = GetClass(type, longFieldName); + + if (clazz == null) + { + return "Parent view does not contain a field named '" + longFieldName + "'"; + } + + if ((clazz != typeof(long)) && (clazz != typeof(long?))) + { + return "Parent view field named '" + longFieldName + "' is not of type long"; + } + + return CheckFieldNumeric(type, longFieldName); + } + + /// Returns the class for the field as defined in the schema. + /// contains metadata about fields + /// + /// is the field's name to return the type for + /// + /// type of field. + /// + private static Type GetClass(EventType type, String fieldName) + { + return type.GetPropertyType(fieldName); + } + + // Perform the schema checking for if a field exists and is numeric + private static String CheckFieldNumeric(EventType type, String numericFieldName) + { + Type clazz = GetClass(type, numericFieldName); + + if (clazz == null) + { + return "Parent view does not contain a field named '" + numericFieldName + "'"; + } + + if (!TypeHelper.IsNumeric(clazz)) + { + return "Parent view field named '" + numericFieldName + "' is not a number"; + } + + return null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/StatementStopCallback.cs b/NEsper.Core/NEsper.Core/view/StatementStopCallback.cs new file mode 100755 index 000000000..ca8d7afb5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/StatementStopCallback.cs @@ -0,0 +1,13 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.view +{ + /// Callback to indicate that a statement has stopped. + public delegate void StatementStopCallback(); +} diff --git a/NEsper.Core/NEsper.Core/view/StatementStopService.cs b/NEsper.Core/NEsper.Core/view/StatementStopService.cs new file mode 100755 index 000000000..c8078a20b --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/StatementStopService.cs @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.view +{ + /// + /// Provides statement resources with the means to register a callback and be informed when a statement stopped + /// and resources for the statement must be release. + /// + public interface StatementStopService + { + /// + /// Callback that is performed for a stop of a statement. + /// + event StatementStopCallback StatementStopped; + + /// + /// Used by the engine to indicate a statement stopped, invoking any callbacks registered. + /// + void FireStatementStopped(); + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/view/StatementStopServiceImpl.cs b/NEsper.Core/NEsper.Core/view/StatementStopServiceImpl.cs new file mode 100755 index 000000000..bf5c1dcd4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/StatementStopServiceImpl.cs @@ -0,0 +1,24 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.view +{ + /// Provides subscription list for statement stop callbacks. + public class StatementStopServiceImpl : StatementStopService + { + public event StatementStopCallback StatementStopped; + + public void FireStatementStopped() + { + if (StatementStopped != null) + { + StatementStopped.Invoke(); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/StoppableView.cs b/NEsper.Core/NEsper.Core/view/StoppableView.cs new file mode 100755 index 000000000..74dd90050 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/StoppableView.cs @@ -0,0 +1,16 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.util; + +namespace com.espertech.esper.view +{ + public interface StoppableView : StopCallback + { + } +} diff --git a/NEsper.Core/NEsper.Core/view/ValidatedView.cs b/NEsper.Core/NEsper.Core/view/ValidatedView.cs new file mode 100755 index 000000000..38ca92fcf --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/ValidatedView.cs @@ -0,0 +1,62 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.epl.variable; +using com.espertech.esper.events; +using com.espertech.esper.schedule; +using com.espertech.esper.script; + +namespace com.espertech.esper.view +{ + /// + /// Interface for views that require validation against stream event types. + /// + public interface ValidatedView + { + /// + /// Validate the view. + /// + /// The engine import service. + /// supplies the types of streams against which to validate + /// for providing current time + /// for access to variables + /// + /// The scripting service. + /// context for expression evaluation + /// The config snapshot. + /// The scheduling service. + /// The engine URI. + /// The SQL parameters. + /// The event adapter service. + /// + /// ExprValidationException is thrown to indicate an exception in validating the view + void Validate( + EngineImportService engineImportService, + StreamTypeService streamTypeService, + TimeProvider timeProvider, + VariableService variableService, + TableService tableService, + ScriptingService scriptingService, + ExprEvaluatorContext exprEvaluatorContext, + ConfigurationInformation configSnapshot, + SchedulingService schedulingService, + string engineURI, + IDictionary> sqlParameters, + EventAdapterService eventAdapterService, + StatementContext statementContext); + } +} diff --git a/NEsper.Core/NEsper.Core/view/View.cs b/NEsper.Core/NEsper.Core/view/View.cs new file mode 100755 index 000000000..f0226aa4e --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/View.cs @@ -0,0 +1,78 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using com.espertech.esper.client; + +namespace com.espertech.esper.view +{ + /// The View interface provides a way for a stream, data provider, or another view, + /// to notify an object of additions and deletions to its data set. + /// Views are themselves Viewable by other Views, and can implement their own set of cached data internally. + /// The contract is that a View is wholly derived from the object (its parent) to which it is attached. + /// That is, it must be able to reconstitute its cached state from a playback of source data passed to it + /// through the Update() method. + /// + /// A view's job is to derive some data from the data in the Viewable object to which it is attached. + /// This can happen by a 'push' mechanism whereby new data in the underlying collection is pushed to the view + /// through the Update method. A view that operates in this mode incrementally updates its derived data and + /// then provides this data to any queries or requesters through its Data interface and potentially through + /// other customized methods it exposes. When these methods are called, the view in push mode does not contact + /// its parent: it just supplies the requester with the data it already derived. The push mode is efficient + /// when data in a view is slow-changing with respect to how much its data is requested. For example, a view + /// calculating the mean of an intermittent signal over time may be queried very frequently. It incrementally + /// updates its statistic and then provides that quantity to callers whenever they want it, which may be much + /// more frequently than the incoming signal occurs. + /// + /// The 'pull' mechanism is driven by requests to the view's Data interface or other customized data access methods. + /// A view operating in 'pull' mode may know whether it is "clean" or "dirty" by listening to its Update method, or + /// it may not get any calls to its Update method, and have to consult its parent to re-derive data when it is called. + /// This mode is efficient when requests to a view for its data are infrequent compared to the Update frequency of its + /// parent's data. For example, a temperature sensor may be changing on a near-continuous basis, and a view which + /// derives some quantity from that sensor may be queried irregularly. It is most efficient for that view to operate + /// in pull mode, and only Update itself when it is asked by some consumer for its derived quantity. It then asks the + /// temperature sensor for the current temperature, does its derivation, and returns to the requester. + /// + /// To feed views that are registered with it, a view should only call the Update method on its child views when its own + /// data has changed. If it receives an Update which results in no change to its data, it should not Update any children + /// views. + /// + + public interface View : EventCollection, Viewable + { + /// Gets or sets the View's parent Viewable. + /// viewable + /// + Viewable Parent { get; set; } + + /// + /// Notify that data has been added or removed from the Viewable parent. + /// The last object in the newData array of objects would be the newest object added to the parent view. + /// The first object of the oldData array of objects would be the oldest object removed from the parent view. + /// + /// If the call to Update contains new (inserted) data, then the first argument will be a non-empty list and the + /// second will be empty. Similarly, if the call is a notification of deleted data, then the first argument will be + /// empty and the second will be non-empty. Either the newData or oldData will be non-null. + /// This method won't be called with both arguments being null, but either one could be null. + /// The same is true for zero-length arrays. Either newData or oldData will be non-empty. + /// If both are non-empty, then the Update is a modification notification. + /// + /// + /// When Update() is called on a view by the parent object, the data in newData will be in the collection of the + /// parent, and its data structures will be arranged to reflect that. + /// The data in oldData will not be in the parent's data structures, and any access to the parent will indicate that + /// that data is no longer there. + /// + /// + /// is the new data that has been added to the parent view + /// + /// is the old data that has been removed from the parent view + /// + void Update(EventBean[] newData, EventBean[] oldData); + } +} diff --git a/NEsper.Core/NEsper.Core/view/ViewDataVisitable.cs b/NEsper.Core/NEsper.Core/view/ViewDataVisitable.cs new file mode 100755 index 000000000..45e22bca4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/ViewDataVisitable.cs @@ -0,0 +1,15 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.view +{ + public interface ViewDataVisitable + { + void VisitView(ViewDataVisitor viewDataVisitor); + } +} diff --git a/NEsper.Core/NEsper.Core/view/ViewDataVisitableContainer.cs b/NEsper.Core/NEsper.Core/view/ViewDataVisitableContainer.cs new file mode 100755 index 000000000..0f986cc4b --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/ViewDataVisitableContainer.cs @@ -0,0 +1,15 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.view +{ + public interface ViewDataVisitableContainer + { + void VisitViewContainer(ViewDataVisitorContained viewDataVisitor); + } +} diff --git a/NEsper.Core/NEsper.Core/view/ViewDataVisitor.cs b/NEsper.Core/NEsper.Core/view/ViewDataVisitor.cs new file mode 100755 index 000000000..481985537 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/ViewDataVisitor.cs @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; + +namespace com.espertech.esper.view +{ + public interface ViewDataVisitor + { + void VisitPrimary(EventBean @event, string viewName); + void VisitPrimary(EventBean[] events, string viewName); + void VisitPrimary(ICollection primary, bool countsEvents, String viewName, int? count); + void VisitPrimary(IDictionary currentBatch, bool countsEvents, String viewName, int? count, int? keyCountWhenAvailable); + void VisitPrimary(ViewUpdatedCollection buffer, String viewName); + } +} diff --git a/NEsper.Core/NEsper.Core/view/ViewDataVisitorContained.cs b/NEsper.Core/NEsper.Core/view/ViewDataVisitorContained.cs new file mode 100755 index 000000000..9aa747551 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/ViewDataVisitorContained.cs @@ -0,0 +1,18 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.view +{ + public interface ViewDataVisitorContained + { + void VisitPrimary(String viewName, int numContained); + void VisitContained(Object containedKey, View containedView); + } +} diff --git a/NEsper.Core/NEsper.Core/view/ViewEnum.cs b/NEsper.Core/NEsper.Core/view/ViewEnum.cs new file mode 100755 index 000000000..ef81d2730 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/ViewEnum.cs @@ -0,0 +1,378 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.rowregex; +using com.espertech.esper.view.internals; +using com.espertech.esper.view.std; +using com.espertech.esper.view.ext; +using com.espertech.esper.view.window; +using com.espertech.esper.view.stat; + +namespace com.espertech.esper.view +{ + public enum ViewEnum + { + /// Length window. + LENGTH, + /// Length first window. + FIRST_LENGTH_WINDOW, + /// Length batch window. + LENGTH_BATCH, + /// Time window. + TIME_WINDOW, + /// Time first window. + FIRST_TIME_WINDOW, + /// Time batch. + TIME_BATCH, + /// Time length batch. + TIME_LENGTH_BATCH, + /// Time accumulating view. + TIME_ACCUM, + /// Externally timed window. + EXT_TIMED_WINDOW, + /// Externally timed window. + EXT_TIMED_BATCH, + /// Keep-all data window. + KEEPALL_WINDOW, + /// Count view. + SIZE, + /// Last event. + LAST_EVENT, + /// First event. + FIRST_EVENT, + /// Unique. + UNIQUE_BY_PROPERTY, + /// Unique. + UNIQUE_FIRST_BY_PROPERTY, + /// Group-by merge. + GROUP_MERGE, + /// Group-by. + GROUP_PROPERTY, + /// Univariate statistics. + UNIVARIATE_STATISTICS, + /// Weighted avg. + WEIGHTED_AVERAGE, + /// CorrelationStatistics. + CORRELATION, + /// Linest. + REGRESSION_LINEST, + /// Sorted window. + SORT_WINDOW, + /// Rank window. + RANK_WINDOW, + /// Time order event window. + TIME_ORDER, + /// Prior event view. + PRIOR_EVENT_VIEW, + /// For retain-union policy. + INTERNAL_UNION, + /// For retain-intersection policy. + INTERNAL_INTERSECT, + /// Match-recognize. + INTERNAL_MATCH_RECOG, + /// Length window. + EXPRESSION_WINDOW, + /// Expression batch window. + EXPRESSION_BATCH_WINDOW, + /// No-op window. + NOOP_WINDOW + } + + public static class ViewEnumExtensions + { + /// Returns namespace that the object belongs to. + /// namespace + /// + public static string GetNamespace(this ViewEnum viewEnum) + { + switch (viewEnum) + { + case ViewEnum.LENGTH: + case ViewEnum.FIRST_LENGTH_WINDOW: + case ViewEnum.LENGTH_BATCH: + case ViewEnum.TIME_WINDOW: + case ViewEnum.FIRST_TIME_WINDOW: + case ViewEnum.TIME_BATCH: + case ViewEnum.TIME_LENGTH_BATCH: + case ViewEnum.TIME_ACCUM: + case ViewEnum.EXT_TIMED_WINDOW: + case ViewEnum.EXT_TIMED_BATCH: + case ViewEnum.KEEPALL_WINDOW: + return "win"; + case ViewEnum.SIZE: + case ViewEnum.LAST_EVENT: + case ViewEnum.FIRST_EVENT: + case ViewEnum.UNIQUE_BY_PROPERTY: + case ViewEnum.UNIQUE_FIRST_BY_PROPERTY: + case ViewEnum.GROUP_MERGE: + case ViewEnum.GROUP_PROPERTY: + return "std"; + case ViewEnum.UNIVARIATE_STATISTICS: + case ViewEnum.WEIGHTED_AVERAGE: + case ViewEnum.CORRELATION: + case ViewEnum.REGRESSION_LINEST: + return "stat"; + case ViewEnum.SORT_WINDOW: + case ViewEnum.RANK_WINDOW: + case ViewEnum.TIME_ORDER: + return "ext"; + case ViewEnum.PRIOR_EVENT_VIEW: + return "int"; + case ViewEnum.INTERNAL_UNION: + case ViewEnum.INTERNAL_INTERSECT: + case ViewEnum.INTERNAL_MATCH_RECOG: + case ViewEnum.NOOP_WINDOW: + return "internal"; + case ViewEnum.EXPRESSION_WINDOW: + case ViewEnum.EXPRESSION_BATCH_WINDOW: + return "win"; + } + + throw new ArgumentException("invalid value", "viewEnum"); + } + + /// Returns name of the view that can be used to reference the view in a view expression. + /// short name of view + /// + public static string GetName(this ViewEnum viewEnum) + { + switch (viewEnum) + { + case ViewEnum.LENGTH: + return "length"; + case ViewEnum.FIRST_LENGTH_WINDOW: + return "firstlength"; + case ViewEnum.LENGTH_BATCH: + return "length_batch"; + case ViewEnum.TIME_WINDOW: + return "time"; + case ViewEnum.FIRST_TIME_WINDOW: + return "firsttime"; + case ViewEnum.TIME_BATCH: + return "time_batch"; + case ViewEnum.TIME_LENGTH_BATCH: + return "time_length_batch"; + case ViewEnum.TIME_ACCUM: + return "time_accum"; + case ViewEnum.EXT_TIMED_WINDOW: + return "ext_timed"; + case ViewEnum.EXT_TIMED_BATCH: + return "ext_timed_batch"; + case ViewEnum.KEEPALL_WINDOW: + return "keepall"; + case ViewEnum.SIZE: + return "size"; + case ViewEnum.LAST_EVENT: + return "lastevent"; + case ViewEnum.FIRST_EVENT: + return "firstevent"; + case ViewEnum.UNIQUE_BY_PROPERTY: + return "unique"; + case ViewEnum.UNIQUE_FIRST_BY_PROPERTY: + return "firstunique"; + case ViewEnum.GROUP_MERGE: + return "merge"; + case ViewEnum.GROUP_PROPERTY: + return "groupwin"; + case ViewEnum.UNIVARIATE_STATISTICS: + return "uni"; + case ViewEnum.WEIGHTED_AVERAGE: + return "weighted_avg"; + case ViewEnum.CORRELATION: + return "correl"; + case ViewEnum.REGRESSION_LINEST: + return "linest"; + case ViewEnum.SORT_WINDOW: + return "sort"; + case ViewEnum.RANK_WINDOW: + return "rank"; + case ViewEnum.TIME_ORDER: + return "time_order"; + case ViewEnum.PRIOR_EVENT_VIEW: + return "prioreventinternal"; + case ViewEnum.INTERNAL_UNION: + return "union"; + case ViewEnum.INTERNAL_INTERSECT: + return "intersect"; + case ViewEnum.INTERNAL_MATCH_RECOG: + return "match_recognize"; + case ViewEnum.EXPRESSION_WINDOW: + return "expr"; + case ViewEnum.EXPRESSION_BATCH_WINDOW: + return "expr_batch"; + case ViewEnum.NOOP_WINDOW: + return "noop"; + } + + throw new ArgumentException("invalid value", "viewEnum"); + } + + + /// Gets the view's factory class. + /// view's factory class + /// + public static Type GetFactoryType(this ViewEnum viewEnum) + { + switch (viewEnum) + { + case ViewEnum.LENGTH: + return typeof(LengthWindowViewFactory); + case ViewEnum.FIRST_LENGTH_WINDOW: + return typeof(FirstLengthWindowViewFactory); + case ViewEnum.LENGTH_BATCH: + return typeof(LengthBatchViewFactory); + case ViewEnum.TIME_WINDOW: + return typeof(TimeWindowViewFactory); + case ViewEnum.FIRST_TIME_WINDOW: + return typeof(FirstTimeViewFactory); + case ViewEnum.TIME_BATCH: + return typeof(TimeBatchViewFactory); + case ViewEnum.TIME_LENGTH_BATCH: + return typeof(TimeLengthBatchViewFactory); + case ViewEnum.TIME_ACCUM: + return typeof(TimeAccumViewFactory); + case ViewEnum.EXT_TIMED_WINDOW: + return typeof(ExternallyTimedWindowViewFactory); + case ViewEnum.EXT_TIMED_BATCH: + return typeof(ExternallyTimedBatchViewFactory); + case ViewEnum.KEEPALL_WINDOW: + return typeof(KeepAllViewFactory); + case ViewEnum.SIZE: + return typeof(SizeViewFactory); + case ViewEnum.LAST_EVENT: + return typeof(LastElementViewFactory); + case ViewEnum.FIRST_EVENT: + return typeof(FirstElementViewFactory); + case ViewEnum.UNIQUE_BY_PROPERTY: + return typeof(UniqueByPropertyViewFactory); + case ViewEnum.UNIQUE_FIRST_BY_PROPERTY: + return typeof(FirstUniqueByPropertyViewFactory); + case ViewEnum.GROUP_MERGE: + return typeof(MergeViewFactory); + case ViewEnum.GROUP_PROPERTY: + return typeof(GroupByViewFactory); + case ViewEnum.UNIVARIATE_STATISTICS: + return typeof(UnivariateStatisticsViewFactory); + case ViewEnum.WEIGHTED_AVERAGE: + return typeof(WeightedAverageViewFactory); + case ViewEnum.CORRELATION: + return typeof(CorrelationViewFactory); + case ViewEnum.REGRESSION_LINEST: + return typeof(RegressionLinestViewFactory); + case ViewEnum.SORT_WINDOW: + return typeof(SortWindowViewFactory); + case ViewEnum.RANK_WINDOW: + return typeof(RankWindowViewFactory); + case ViewEnum.TIME_ORDER: + return typeof(TimeOrderViewFactory); + case ViewEnum.PRIOR_EVENT_VIEW: + return typeof(PriorEventViewFactory); + case ViewEnum.INTERNAL_UNION: + return typeof(UnionViewFactory); + case ViewEnum.INTERNAL_INTERSECT: + return typeof(IntersectViewFactory); + case ViewEnum.INTERNAL_MATCH_RECOG: + return typeof(EventRowRegexNFAViewFactory); + case ViewEnum.EXPRESSION_WINDOW: + return typeof(ExpressionWindowViewFactory); + case ViewEnum.EXPRESSION_BATCH_WINDOW: + return typeof(ExpressionBatchViewFactory); + case ViewEnum.NOOP_WINDOW: + return typeof(NoopViewFactory); + } + + throw new ArgumentException("invalid value", "viewEnum"); + } + + /// Returns the enumeration value of the view for merging the data generated by another view. + /// view enum for the merge view + /// + public static ViewEnum? GetMergeView(this ViewEnum viewEnum) + { + switch (viewEnum) + { + case ViewEnum.LENGTH: + case ViewEnum.FIRST_LENGTH_WINDOW: + case ViewEnum.LENGTH_BATCH: + case ViewEnum.TIME_WINDOW: + case ViewEnum.FIRST_TIME_WINDOW: + case ViewEnum.TIME_BATCH: + case ViewEnum.TIME_LENGTH_BATCH: + case ViewEnum.TIME_ACCUM: + case ViewEnum.EXT_TIMED_WINDOW: + case ViewEnum.EXT_TIMED_BATCH: + case ViewEnum.KEEPALL_WINDOW: + case ViewEnum.SIZE: + case ViewEnum.LAST_EVENT: + case ViewEnum.FIRST_EVENT: + case ViewEnum.UNIQUE_BY_PROPERTY: + case ViewEnum.UNIQUE_FIRST_BY_PROPERTY: + case ViewEnum.GROUP_MERGE: + return null; + case ViewEnum.GROUP_PROPERTY: + return ViewEnum.GROUP_MERGE; + case ViewEnum.UNIVARIATE_STATISTICS: + case ViewEnum.WEIGHTED_AVERAGE: + case ViewEnum.CORRELATION: + case ViewEnum.REGRESSION_LINEST: + case ViewEnum.SORT_WINDOW: + case ViewEnum.RANK_WINDOW: + case ViewEnum.TIME_ORDER: + case ViewEnum.PRIOR_EVENT_VIEW: + case ViewEnum.INTERNAL_UNION: + case ViewEnum.INTERNAL_INTERSECT: + case ViewEnum.INTERNAL_MATCH_RECOG: + case ViewEnum.EXPRESSION_WINDOW: + case ViewEnum.EXPRESSION_BATCH_WINDOW: + case ViewEnum.NOOP_WINDOW: + return null; + } + + throw new ArgumentException("invalid value", "viewEnum"); + } + + /// + /// Returns the view enumeration value given the name of the view. + /// + /// The nspace. + /// is the short name of the view as used in view expressions + /// + /// view enumeration value, or null if no such view name is among the enumerated values + /// + + public static ViewEnum? ForName(String nspace, String name) + { + if (string.IsNullOrEmpty(nspace)) + { + foreach (ViewEnum viewEnum in Enum.GetValues(typeof(ViewEnum))) + { + if (Equals(viewEnum.GetName(), name)) + { + return viewEnum; + } + } + } + else + { + foreach (ViewEnum viewEnum in Enum.GetValues(typeof(ViewEnum))) + { + if (Equals(viewEnum.GetNamespace(), nspace) && + Equals(viewEnum.GetName(), name)) + { + return viewEnum; + } + } + } + + + return null; + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/ViewEnumHelper.cs b/NEsper.Core/NEsper.Core/view/ViewEnumHelper.cs new file mode 100755 index 000000000..8a6e4d4e2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/ViewEnumHelper.cs @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.compat; +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.view +{ + /// Helper producing a repository of built-in views. + public class ViewEnumHelper + { + private static readonly PluggableObjectCollection BUILTIN_VIEWS; + + static ViewEnumHelper() + { + BUILTIN_VIEWS = new PluggableObjectCollection(); + foreach (ViewEnum viewEnum in EnumHelper.GetValues()) + { + BUILTIN_VIEWS.AddObject( + viewEnum.GetNamespace(), viewEnum.GetName(), viewEnum.GetFactoryType(), PluggableObjectType.VIEW); + } + } + + /// + /// Returns a collection of plug-in views. + /// + /// built-in view definitions + public static PluggableObjectCollection BuiltinViews + { + get { return BUILTIN_VIEWS; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/view/ViewFactory.cs b/NEsper.Core/NEsper.Core/view/ViewFactory.cs new file mode 100755 index 000000000..15a8c3097 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/ViewFactory.cs @@ -0,0 +1,80 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.view +{ + /// + /// Factory interface for a factory responsible for creating a instance and for determining if an existing view meets requirements. + /// + public interface ViewFactory + { + /// + /// Indicates user EPL query view parameters to the view factory. + /// + /// supplied context information for the view factory + /// is the objects representing the view parameters + /// ViewParameterException if the parameters don't match view parameter needs + void SetViewParameters(ViewFactoryContext viewFactoryContext, IList viewParameters); + + /// + /// Attaches the factory to a parent event type such that the factory can validate attach requirements + /// and determine an event type for resulting views. + /// + /// is the parent event stream's or view factory's event type + /// contains the services needed for creating a new event type + /// is null when there is no parent view factory, or contains theparent view factory + /// is a list of all the parent view factories or empty list if there are none + /// ViewParameterException is thrown to indicate that this view factories's view would not playwith the parent view factories view + void Attach( + EventType parentEventType, + StatementContext statementContext, + ViewFactory optionalParentFactory, + IList parentViewFactories); + + /// + /// Create a new view. + /// + /// The agent instance view factory context. + /// + View MakeView(AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext); + + /// + /// Returns the event type that the view that is created by the view factory would create for events posted by the view. + /// + /// + /// event type of view's created by the view factory + /// + EventType EventType { get; } + + /// + /// Determines if the given view could be used instead of creating a new view, requires the view factory to compare view type, parameters + /// and other capabilities provided. + /// + /// is the candidate view to compare to + /// The agent instance context. + /// + /// true if the given view can be reused instead of creating a new view, or false to indicate the view is not right for reuse + /// + bool CanReuse(View view, AgentInstanceContext agentInstanceContext); + + /// + /// Returns the name of the view, not namespace+name but readable name. + /// + /// + /// readable name + /// + string ViewName { get; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/view/ViewFactoryChain.cs b/NEsper.Core/NEsper.Core/view/ViewFactoryChain.cs new file mode 100755 index 000000000..e0cde21dd --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/ViewFactoryChain.cs @@ -0,0 +1,86 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.view +{ + /// + /// Holder for the logical chain of view factories. + /// + public class ViewFactoryChain + { + private readonly IList _viewFactoryChain; + private readonly EventType _streamEventType; + + /// Ctor. + /// is the event type of the event stream + /// is the chain of view factories + public ViewFactoryChain(EventType streamEventType, IList viewFactoryChain) + { + _streamEventType = streamEventType; + _viewFactoryChain = viewFactoryChain; + } + + /// + /// Returns the final event type which is the event type of the last view factory in the chain, + /// or if the chain is empty then the stream's event type. + /// + /// final event type of the last view or stream + public EventType EventType + { + get + { + if (_viewFactoryChain.Count == 0) + { + return _streamEventType; + } + else + { + return _viewFactoryChain[_viewFactoryChain.Count - 1].EventType; + } + } + } + + /// Returns the chain of view factories. + /// view factory list + public IList FactoryChain + { + get { return _viewFactoryChain; } + } + + + /// + /// Returns the number of data window factories for the chain. + /// + /// + /// number of data window factories + /// + public int DataWindowViewFactoryCount + { + get + { + int count = 0; + foreach (var chainElement in _viewFactoryChain) { + if (chainElement is DataWindowViewFactory) { + count++; + } + } + return count; + } + } + + public static ViewFactoryChain FromTypeNoViews(EventType eventType) + { + return new ViewFactoryChain(eventType, Collections.GetEmptyList()); + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/view/ViewFactoryContext.cs b/NEsper.Core/NEsper.Core/view/ViewFactoryContext.cs new file mode 100755 index 000000000..15ca08efe --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/ViewFactoryContext.cs @@ -0,0 +1,134 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.core.service; +using com.espertech.esper.events; +using com.espertech.esper.schedule; + +namespace com.espertech.esper.view +{ + /// + /// Context calss for specific views within a statement. Each view in a statement gets it's own context + /// containing the statement context. + /// + public class ViewFactoryContext + { + /// + /// Ctor. + /// + /// is the statement-level services + /// is the stream number from zero to N + /// is the view namespace + /// is the view name + /// if set to true [is subquery]. + /// The subquery number. + /// if set to true [is grouped]. + public ViewFactoryContext(StatementContext statementContext, int streamNum, string namespaceName, string viewName, bool isSubquery, int subqueryNumber, bool isGrouped) + { + StatementContext = statementContext; + StreamNum = streamNum; + NamespaceName = namespaceName; + ViewName = viewName; + IsSubquery = isSubquery; + SubqueryNumber = subqueryNumber; + IsGrouped = isGrouped; + } + + /// + /// Returns service to use for schedule evaluation. + /// + /// schedule evaluation service implemetation + public SchedulingService SchedulingService + { + get { return StatementContext.SchedulingService; } + } + + /// + /// Returns service for generating events and handling event types. + /// + /// event adapter service + public EventAdapterService EventAdapterService + { + get { return StatementContext.EventAdapterService; } + } + + /// + /// Returns the schedule bucket for ordering schedule callbacks within this pattern. + /// + /// schedule bucket + public ScheduleBucket ScheduleBucket + { + get { return StatementContext.ScheduleBucket; } + } + + /// + /// Returns the statement's resource locks. + /// + /// statement resource lock/handle + public EPStatementHandle EpStatementHandle + { + get { return StatementContext.EpStatementHandle; } + } + + /// + /// Returns extension svc. + /// + /// svc + public StatementExtensionSvcContext StatementExtensionServicesContext + { + get { return StatementContext.StatementExtensionServicesContext; } + } + + /// + /// Returns the statement id. + /// + /// statement id + public int StatementId + { + get { return StatementContext.StatementId; } + } + + /// + /// Returns the stream number. + /// + /// stream number + public int StreamNum { get; private set; } + + /// + /// Returns the view namespace name. + /// + /// namespace name + public string NamespaceName { get; private set; } + + /// + /// Returns the view name. + /// + /// view name + public string ViewName { get; private set; } + + /// + /// Returns the statement context. + /// + /// statement context + public StatementContext StatementContext { get; private set; } + + public bool IsSubquery { get; private set; } + + public int SubqueryNumber { get; private set; } + + public bool IsGrouped { get; private set; } + + public override string ToString() + { + return StatementContext + + " streamNum=" + StreamNum + + " namespaceName=" + NamespaceName + + " viewName=" + ViewName; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/view/ViewFactoryProxy.cs b/NEsper.Core/NEsper.Core/view/ViewFactoryProxy.cs new file mode 100755 index 000000000..04b03e344 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/ViewFactoryProxy.cs @@ -0,0 +1,65 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; + +using Castle.DynamicProxy; + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.view +{ + public class ViewFactoryProxy : IInterceptor + { + private static readonly MethodInfo MakeViewMethod = typeof(ViewFactory).GetMethod("MakeView"); + + private readonly String _engineURI; + private readonly String _statementName; + private readonly ViewFactory _viewFactory; + private readonly String _viewName; + + public static ViewFactory NewInstance(String engineURI, String statementName, ViewFactory viewFactory, String viewName) + { + var generator = new ProxyGenerator(); + return (ViewFactory) generator.CreateInterfaceProxyWithoutTarget( + typeof(ViewFactory), + viewFactory.GetType().GetInterfaces(), + new ViewFactoryProxy(engineURI, statementName, viewFactory, viewName)); + } + + /// + /// Intercepts the specified invocation. + /// + /// The invocation. + public void Intercept(IInvocation invocation) + { + if (invocation.Method != MakeViewMethod) + { + invocation.ReturnValue = invocation.Method.Invoke( + _viewFactory, invocation.Arguments); + return; + } + + var view = (View)invocation.Method.Invoke(_viewFactory, invocation.Arguments); + invocation.ReturnValue = ViewProxy.NewInstance(_engineURI, _statementName, _viewName, view); + } + + public ViewFactoryProxy(String engineURI, String statementName, ViewFactory viewFactory, String viewName) + { + _engineURI = engineURI; + _statementName = statementName; + _viewFactory = viewFactory; + _viewName = viewName; + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/ViewFactorySupport.cs b/NEsper.Core/NEsper.Core/view/ViewFactorySupport.cs new file mode 100755 index 000000000..a6342568b --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/ViewFactorySupport.cs @@ -0,0 +1,337 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.visitor; +using com.espertech.esper.util; + +namespace com.espertech.esper.view +{ + /// + /// Abstract base class for view factories that do not make re-useable views and that do + /// not share view resources with expression nodes. + /// + public abstract class ViewFactorySupport : ViewFactory + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// Validate the view parameter expression and evaluate the expression returning the result object. + /// + /// textual name of view + /// context with statement services + /// view expression parameter to validate + /// if the expressions fail to validate + /// object result value of parameter expression + public static Object ValidateAndEvaluate( + string viewName, + StatementContext statementContext, + ExprNode expression) + { + return ValidateAndEvaluateExpr( + viewName, statementContext, expression, new StreamTypeServiceImpl(statementContext.EngineURI, false), 0); + } + + public static ExprNode[] Validate( + string viewName, + StatementContext statementContext, + IList expressions) + { + var results = new ExprNode[expressions.Count]; + int expressionNumber = 0; + var streamTypeService = new StreamTypeServiceImpl(statementContext.EngineURI, false); + foreach (ExprNode expr in expressions) + { + results[expressionNumber] = ValidateExpr( + viewName, statementContext, expr, streamTypeService, expressionNumber); + expressionNumber++; + } + return results; + } + + /// + /// Validate the view parameter expressions and return the validated expression for later execution. + /// + /// Does not evaluate the expression. + /// + /// + /// textual name of view + /// is the event type of the parent view or stream attached. + /// context with statement services + /// view expression parameter to validate + /// + /// true to indicate whether expressions that return a constant + /// result should be allowed; false to indicate that if an expression is known to return a constant result + /// the expression is considered invalid + /// + /// if the expressions fail to validate + /// object result value of parameter expressions + public static ExprNode[] Validate( + string viewName, + EventType eventType, + StatementContext statementContext, + IList expressions, + bool allowConstantResult) + { + var results = new List(); + int expressionNumber = 0; + var streamTypeService = new StreamTypeServiceImpl(eventType, null, false, statementContext.EngineURI); + foreach (ExprNode expr in expressions) + { + ExprNode validated = ValidateExpr(viewName, statementContext, expr, streamTypeService, expressionNumber); + results.Add(validated); + + if ((!allowConstantResult) && (validated.IsConstantResult)) + { + string message = "Invalid view parameter expression " + expressionNumber + GetViewDesc(viewName) + + ", the expression returns a constant result value, are you sure?"; + Log.Error(message); + throw new ViewParameterException(message); + } + + expressionNumber++; + } + return results.ToArray(); + } + + /// + /// Assert and throws an exception if the expression passed returns a non-constant value. + /// + /// textual name of view + /// expression to check + /// number offset of expression in view parameters + /// if assertion fails + public static void AssertReturnsNonConstant(string viewName, ExprNode expression, int index) + { + if (expression.IsConstantResult) + { + string message = "Invalid view parameter expression " + index + GetViewDesc(viewName) + + ", the expression returns a constant result value, are you sure?"; + Log.Error(message); + throw new ViewParameterException(message); + } + } + + public static Object EvaluateAssertNoProperties( + string viewName, + ExprNode expression, + int index, + ExprEvaluatorContext exprEvaluatorContext) + { + ValidateNoProperties(viewName, expression, index); + return expression.ExprEvaluator.Evaluate(new EvaluateParams(null, false, exprEvaluatorContext)); + } + + public static void ValidateNoProperties(string viewName, ExprNode expression, int index) + { + var visitor = new ExprNodeSummaryVisitor(); + expression.Accept(visitor); + if (!visitor.IsPlain) + { + string message = "Invalid view parameter expression " + index + GetViewDesc(viewName) + ", " + + visitor.GetMessage() + " are not allowed within the expression"; + throw new ViewParameterException(message); + } + } + + public static Object ValidateAndEvaluateExpr( + string viewName, + StatementContext statementContext, + ExprNode expression, + StreamTypeService streamTypeService, + int expressionNumber) + { + ExprNode validated = ValidateExpr( + viewName, statementContext, expression, streamTypeService, expressionNumber); + + try + { + return validated.ExprEvaluator.Evaluate( + new EvaluateParams(null, true, new ExprEvaluatorContextStatement(statementContext, false))); + } + catch (Exception ex) + { + string message = "Failed to evaluate parameter expression " + expressionNumber + GetViewDesc(viewName); + if (!string.IsNullOrWhiteSpace(ex.Message)) + { + message += ": " + ex.Message; + } + Log.Error(message, ex); + throw new ViewParameterException(message, ex); + } + } + + public static Object Evaluate( + ExprEvaluator evaluator, + int expressionNumber, + string viewName, + StatementContext statementContext) + { + try + { + return evaluator.Evaluate(new EvaluateParams(null, true, new ExprEvaluatorContextStatement(statementContext, false))); + } + catch (Exception ex) + { + string message = "Failed to evaluate parameter expression " + expressionNumber + GetViewDesc(viewName); + if (!string.IsNullOrWhiteSpace(ex.Message)) + { + message += ": " + ex.Message; + } + Log.Error(message, ex); + throw new ViewParameterException(message, ex); + } + } + + public static ExprNode ValidateExpr( + string viewName, + StatementContext statementContext, + ExprNode expression, + StreamTypeService streamTypeService, + int expressionNumber) + { + ExprNode validated; + try + { + var exprEvaluatorContext = new ExprEvaluatorContextStatement(statementContext, false); + var validationContext = new ExprValidationContext( + streamTypeService, + statementContext.EngineImportService, + statementContext.StatementExtensionServicesContext, null, + statementContext.SchedulingService, + statementContext.VariableService, + statementContext.TableService, exprEvaluatorContext, + statementContext.EventAdapterService, + statementContext.StatementName, + statementContext.StatementId, + statementContext.Annotations, + statementContext.ContextDescriptor, + statementContext.ScriptingService, + false, false, false, false, null, false); + validated = ExprNodeUtility.GetValidatedSubtree( + ExprNodeOrigin.VIEWPARAMETER, expression, validationContext); + } + catch (ExprValidationException ex) + { + string message = "Invalid parameter expression " + expressionNumber + GetViewDesc(viewName); + if (!string.IsNullOrWhiteSpace(ex.Message)) + { + message += ": " + ex.Message; + } + Log.Error(message, ex); + throw new ViewParameterException(message, ex); + } + return validated; + } + + private static string GetViewDesc(string viewName) + { + return " for " + viewName + " view"; + } + + public static ExprEvaluator ValidateSizeSingleParam( + string viewName, + ViewFactoryContext viewFactoryContext, + IList expressionParameters) + { + ExprNode[] validated = ViewFactorySupport.Validate( + viewName, viewFactoryContext.StatementContext, expressionParameters); + if (validated.Length != 1) + { + throw new ViewParameterException(GetViewParamMessage(viewName)); + } + return ValidateSizeParam(viewName, viewFactoryContext.StatementContext, validated[0], 0); + } + + public static ExprEvaluator ValidateSizeParam( + string viewName, + StatementContext statementContext, + ExprNode sizeNode, + int expressionNumber) + { + var sizeEvaluator = sizeNode.ExprEvaluator; + var returnType = sizeEvaluator.ReturnType.GetBoxedType(); + if (!returnType.IsNumeric() || + returnType.IsFloatingPointClass() || + returnType.IsBoxedType()) + { + throw new ViewParameterException(GetViewParamMessage(viewName)); + } + if (sizeNode.IsConstantResult) + { + var size = ViewFactorySupport.Evaluate(sizeEvaluator, expressionNumber, viewName, statementContext); + if (!ValidateSize(size)) + { + throw new ViewParameterException(GetSizeValidationMsg(viewName, size)); + } + } + return sizeEvaluator; + } + + public static int EvaluateSizeParam(string viewName, ExprEvaluator sizeEvaluator, AgentInstanceContext context) + { + var size = sizeEvaluator.Evaluate(new EvaluateParams(null, true, context)); + if (!ValidateSize(size)) + { + throw new EPException(GetSizeValidationMsg(viewName, size)); + } + return size.AsInt(); + } + + private static bool ValidateSize(object size) + { + return !(size == null || size.AsInt() <= 0); + } + + private static string GetViewParamMessage(string viewName) + { + return viewName + " view requires a single integer-type parameter"; + } + + private static string GetSizeValidationMsg(string viewName, object size) + { + return viewName + " view requires a positive integer for size but received " + size; + } + + public static void ValidateNoParameters(string viewName, IList expressionParameters) + { + if (!expressionParameters.IsEmpty()) + { + string errorMessage = viewName + " view requires an empty parameter list"; + throw new ViewParameterException(errorMessage); + } + } + + public bool CanReuse(View view, AgentInstanceContext agentInstanceContext) + { + return false; + } + + public abstract void SetViewParameters(ViewFactoryContext viewFactoryContext, IList viewParameters); + + public abstract void Attach( + EventType parentEventType, + StatementContext statementContext, + ViewFactory optionalParentFactory, + IList parentViewFactories); + + public abstract View MakeView(AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext); + public abstract EventType EventType { get; } + public abstract string ViewName { get; } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/view/ViewFactoryTimePeriodHelper.cs b/NEsper.Core/NEsper.Core/view/ViewFactoryTimePeriodHelper.cs new file mode 100755 index 000000000..113fdbaf2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/ViewFactoryTimePeriodHelper.cs @@ -0,0 +1,64 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.core.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.time; +using com.espertech.esper.util; + +namespace com.espertech.esper.view +{ + public class ViewFactoryTimePeriodHelper + { + public static ExprTimePeriodEvalDeltaConstFactory ValidateAndEvaluateTimeDeltaFactory( + string viewName, + StatementContext statementContext, + ExprNode expression, + string expectedMessage, + int expressionNumber) + { + var streamTypeService = new StreamTypeServiceImpl(statementContext.EngineURI, false); + ExprTimePeriodEvalDeltaConstFactory factory; + if (expression is ExprTimePeriod) + { + var validated = (ExprTimePeriod) ViewFactorySupport.ValidateExpr( + viewName, statementContext, expression, streamTypeService, expressionNumber); + factory = validated.ConstEvaluator(new ExprEvaluatorContextStatement(statementContext, false)); + } + else + { + var validated = ViewFactorySupport.ValidateExpr( + viewName, statementContext, expression, streamTypeService, expressionNumber); + var secondsEvaluator = validated.ExprEvaluator; + var returnType = secondsEvaluator.ReturnType.GetBoxedType(); + if (!returnType.IsNumeric()) + { + throw new ViewParameterException(expectedMessage); + } + if (validated.IsConstantResult) + { + var time = ViewFactorySupport.Evaluate(secondsEvaluator, 0, viewName, statementContext); + if (!ExprTimePeriodUtil.ValidateTime(time, statementContext.TimeAbacus)) + { + throw new ViewParameterException(ExprTimePeriodUtil.GetTimeInvalidMsg(viewName, "view", time)); + } + var msec = statementContext.TimeAbacus.DeltaForSecondsNumber(time); + factory = new ExprTimePeriodEvalDeltaConstGivenDelta(msec); + } + else + { + factory = new ExprTimePeriodEvalDeltaConstFactoryMsec(secondsEvaluator, statementContext.TimeAbacus); + } + } + return factory; + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/view/ViewFieldEnum.cs b/NEsper.Core/NEsper.Core/view/ViewFieldEnum.cs new file mode 100755 index 000000000..4b07d1caa --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/ViewFieldEnum.cs @@ -0,0 +1,172 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; + +namespace com.espertech.esper.view +{ + /// + /// Enumerates the valid values for each view's public fields. The name of the field or property can be used + /// to obtain values from the view rather than using the hardcoded String value for the field. + /// + public enum ViewFieldEnum + { + /// Count. + UNIVARIATE_STATISTICS__DATAPOINTS, + + /// Sum. + UNIVARIATE_STATISTICS__TOTAL, + + /// Average. + UNIVARIATE_STATISTICS__AVERAGE, + + /// Standard dev population. + UNIVARIATE_STATISTICS__STDDEVPA, + + /// Standard dev. + UNIVARIATE_STATISTICS__STDDEV, + + /// Variance. + UNIVARIATE_STATISTICS__VARIANCE, + + /// Weighted average. + WEIGHTED_AVERAGE__AVERAGE, + + /// CorrelationStatistics. + CORRELATION__CORRELATION, + + /// Slope. + REGRESSION__SLOPE, + + /// Y-intercept. + REGRESSION__YINTERCEPT, + + /// Count. + SIZE_VIEW__SIZE, + + /// XAverage + REGRESSION__XAVERAGE, + + /// XStandardDeviationPop + REGRESSION__XSTANDARDDEVIATIONPOP, + + /// XStandardDeviationSample + REGRESSION__XSTANDARDDEVIATIONSAMPLE, + + /// XSum + REGRESSION__XSUM, + + /// XVariance + REGRESSION__XVARIANCE, + + /// YAverage + REGRESSION__YAVERAGE, + + /// YStandardDeviationPop + REGRESSION__YSTANDARDDEVIATIONPOP, + + /// YStandardDeviationSample + REGRESSION__YSTANDARDDEVIATIONSAMPLE, + + /// YSum + REGRESSION__YSUM, + + /// YVariance + REGRESSION__YVARIANCE, + + /// dataPoints + REGRESSION__DATAPOINTS, + + /// n + REGRESSION__N, + + /// sumX + REGRESSION__SUMX, + + /// sumXSq + REGRESSION__SUMXSQ, + + /// sumXY + REGRESSION__SUMXY, + + /// sumY + REGRESSION__SUMY, + + /// sumYSq + REGRESSION__SUMYSQ + } + + public static class ViewFieldEnumExtensions + { + public static string GetName(this ViewFieldEnum value) + { + switch (value) + { + case ViewFieldEnum.UNIVARIATE_STATISTICS__DATAPOINTS: + return ("datapoints"); + case ViewFieldEnum.UNIVARIATE_STATISTICS__TOTAL: + return ("total"); + case ViewFieldEnum.UNIVARIATE_STATISTICS__AVERAGE: + return ("average"); + case ViewFieldEnum.UNIVARIATE_STATISTICS__STDDEVPA: + return ("stddevpa"); + case ViewFieldEnum.UNIVARIATE_STATISTICS__STDDEV: + return ("stddev"); + case ViewFieldEnum.UNIVARIATE_STATISTICS__VARIANCE: + return ("variance"); + case ViewFieldEnum.WEIGHTED_AVERAGE__AVERAGE: + return ("average"); + case ViewFieldEnum.CORRELATION__CORRELATION: + return ("correlation"); + case ViewFieldEnum.REGRESSION__SLOPE: + return ("slope"); + case ViewFieldEnum.REGRESSION__YINTERCEPT: + return ("YIntercept"); + case ViewFieldEnum.SIZE_VIEW__SIZE: + return ("size"); + case ViewFieldEnum.REGRESSION__XAVERAGE: + return ("XAverage"); + case ViewFieldEnum.REGRESSION__XSTANDARDDEVIATIONPOP: + return ("XStandardDeviationPop"); + case ViewFieldEnum.REGRESSION__XSTANDARDDEVIATIONSAMPLE: + return ("XStandardDeviationSample"); + case ViewFieldEnum.REGRESSION__XSUM: + return ("XSum"); + case ViewFieldEnum.REGRESSION__XVARIANCE: + return ("XVariance"); + case ViewFieldEnum.REGRESSION__YAVERAGE: + return ("YAverage"); + case ViewFieldEnum.REGRESSION__YSTANDARDDEVIATIONPOP: + return ("YStandardDeviationPop"); + case ViewFieldEnum.REGRESSION__YSTANDARDDEVIATIONSAMPLE: + return ("YStandardDeviationSample"); + case ViewFieldEnum.REGRESSION__YSUM: + return ("YSum"); + case ViewFieldEnum.REGRESSION__YVARIANCE: + return ("YVariance"); + case ViewFieldEnum.REGRESSION__DATAPOINTS: + return ("dataPoints"); + case ViewFieldEnum.REGRESSION__N: + return ("n"); + case ViewFieldEnum.REGRESSION__SUMX: + return ("sumX"); + case ViewFieldEnum.REGRESSION__SUMXSQ: + return ("sumXSq"); + case ViewFieldEnum.REGRESSION__SUMXY: + return ("sumXY"); + case ViewFieldEnum.REGRESSION__SUMY: + return ("sumY"); + case ViewFieldEnum.REGRESSION__SUMYSQ: + return ("sumYSq"); + } + + throw new ArgumentException(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/ViewParameterException.cs b/NEsper.Core/NEsper.Core/view/ViewParameterException.cs new file mode 100755 index 000000000..b32a171f8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/ViewParameterException.cs @@ -0,0 +1,35 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.view +{ + /// + /// Thrown to indicate a validation error in view parameterization. + /// + public class ViewParameterException : Exception + { + /// + /// Initializes a new instance of the class. + /// + /// The message. + /// The inner exception. + public ViewParameterException(string message, Exception innerException) + : base(message, innerException) + { + } + + /// Ctor. + /// validation error message + public ViewParameterException(String message) + : base(message) + { + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/view/ViewProcessingException.cs b/NEsper.Core/NEsper.Core/view/ViewProcessingException.cs new file mode 100755 index 000000000..0cd7c7e20 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/ViewProcessingException.cs @@ -0,0 +1,43 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.view +{ + /// + /// This exception is thrown to indicate a problem with a view expression. + /// + public sealed class ViewProcessingException : Exception + { + /// + /// Initializes a new instance of the class. + /// + public ViewProcessingException() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The message. + public ViewProcessingException(string message) : base(message) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The message. + /// The inner exception. + public ViewProcessingException(string message, + Exception innerException) : base(message, innerException) + { + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/ViewProxy.cs b/NEsper.Core/NEsper.Core/view/ViewProxy.cs new file mode 100755 index 000000000..030e8d58b --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/ViewProxy.cs @@ -0,0 +1,70 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; + +using Castle.DynamicProxy; + +using com.espertech.esper.client; +using com.espertech.esper.client.annotation; +using com.espertech.esper.events; +using com.espertech.esper.util; + +namespace com.espertech.esper.view +{ + public class ViewProxy : IInterceptor + { + private static readonly MethodInfo UpdateMethod = typeof(View).GetMethod("Update"); + + private readonly String _engineURI; + private readonly String _statementName; + private readonly String _viewName; + private readonly View _view; + + public static Object NewInstance(String engineURI, String statementName, String viewName, View view) + { + var generator = new ProxyGenerator(); + return generator.CreateInterfaceProxyWithoutTarget( + typeof(View), + view.GetType().GetInterfaces(), + new ViewProxy(engineURI, statementName, viewName, view)); + } + + public ViewProxy(String engineURI, String statementName, String viewName, View view) + { + _engineURI = engineURI; + _statementName = statementName; + _viewName = viewName; + _view = view; + } + + /// + /// Intercepts the specified invocation. + /// + /// The invocation. + public void Intercept(IInvocation invocation) + { + invocation.ReturnValue = invocation.Method.Invoke(_view, invocation.Arguments); + + if (invocation.Method == UpdateMethod) + { + if (AuditPath.IsAuditEnabled) + { + var newData = (EventBean[])invocation.Arguments[0]; + var oldData = (EventBean[])invocation.Arguments[1]; + AuditPath.AuditLog( + _engineURI, _statementName, AuditEnum.VIEW, + _viewName + " insert {" + EventBeanUtility.Summarize(newData) + "} remove {" + EventBeanUtility.Summarize(oldData) + "}"); + } + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/ViewResolutionService.cs b/NEsper.Core/NEsper.Core/view/ViewResolutionService.cs new file mode 100755 index 000000000..79dea1378 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/ViewResolutionService.cs @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.view +{ + /// + /// Factory service for resolving view names and for creating view instances based on a view specification including view name and namespace. + /// + public interface ViewResolutionService + { + /// + /// Instantiates a based on the view namespace and name stored in the view spec. + /// + /// Does not actually use the view factory object created. + /// + /// + /// is the view namespace + /// is the view name + /// ViewProcessingException if the view namespace or name cannot resolve + ViewFactory Create(String _namespace, String name); + } +} diff --git a/NEsper.Core/NEsper.Core/view/ViewResolutionServiceImpl.cs b/NEsper.Core/NEsper.Core/view/ViewResolutionServiceImpl.cs new file mode 100755 index 000000000..6bba3c2c8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/ViewResolutionServiceImpl.cs @@ -0,0 +1,124 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.virtualdw; +using com.espertech.esper.util; + +namespace com.espertech.esper.view +{ + /// + /// Resolves view namespace and name to view factory class, using configuration. + /// + public class ViewResolutionServiceImpl : ViewResolutionService + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly PluggableObjectRegistry _viewObjects; + private readonly string _optionalNamedWindowName; + private readonly Type _virtualDataWindowViewFactory; + + public ViewResolutionServiceImpl(PluggableObjectRegistry viewObjects, string optionalNamedWindowName, Type virtualDataWindowViewFactory) + { + _viewObjects = viewObjects; + _optionalNamedWindowName = optionalNamedWindowName; + _virtualDataWindowViewFactory = virtualDataWindowViewFactory; + } + + public ViewFactory Create(string nameSpace, string name) + { + if (Log.IsDebugEnabled) { + Log.Debug(".create Creating view factory, @namespace =" + nameSpace + " name=" + name); + } + + Type viewFactoryClass = null; + + var pair = _viewObjects.Lookup(nameSpace, name); + if (pair != null) + { + if (pair.Second.PluggableType == PluggableObjectType.VIEW) + { + // Handle named windows in a configuration that always declares a system-wide virtual view factory + if (_optionalNamedWindowName != null && _virtualDataWindowViewFactory != null) + { + return new VirtualDWViewFactoryImpl(_virtualDataWindowViewFactory, _optionalNamedWindowName, null); + } + + viewFactoryClass = pair.First; + } + else if (pair.Second.PluggableType == PluggableObjectType.VIRTUALDW) + { + if (_optionalNamedWindowName == null) + { + throw new ViewProcessingException( + "Virtual data window requires use with a named window in the create-window syntax"); + } + return new VirtualDWViewFactoryImpl(pair.First, _optionalNamedWindowName, pair.Second.CustomConfigs); + } + else + { + throw new ViewProcessingException( + "Invalid object type '" + pair.Second + "' for view '" + name + "'"); + } + } + + if (viewFactoryClass == null) + { + var message = nameSpace == null ? + "View name '" + name + "' is not a known view name" : + "View name '" + nameSpace + ":" + name + "' is not a known view name"; + throw new ViewProcessingException(message); + } + + ViewFactory viewFactory; + try + { + viewFactory = (ViewFactory)Activator.CreateInstance(viewFactoryClass); + + if (Log.IsDebugEnabled) { + Log.Debug(".create Successfully instantiated view"); + } + } + catch (InvalidCastException e) + { + var message = "Error casting view factory instance to " + typeof(ViewFactory).FullName + " interface for view '" + name + "'"; + throw new ViewProcessingException(message, e); + } + catch (TypeInstantiationException ex) + { + var message = "Error invoking view factory constructor for view '" + name; + message += "' using Activator.CreateInstance"; + throw new ViewProcessingException(message, ex); + } + catch (TargetInvocationException ex) + { + var message = "Error invoking view factory constructor for view '" + name; + message += "' using Activator.CreateInstance"; + throw new ViewProcessingException(message, ex); + } + catch (MethodAccessException ex) + { + var message = "Error invoking view factory constructor for view '" + name; + message += "', no invocation access for Activator.CreateInstance"; + throw new ViewProcessingException(message, ex); + } + catch (MemberAccessException ex) + { + var message = "Error invoking view factory constructor for view '" + name; + message += "', no invocation access for Activator.CreateInstance"; + throw new ViewProcessingException(message, ex); + } + + return viewFactory; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/view/ViewService.cs b/NEsper.Core/NEsper.Core/view/ViewService.cs new file mode 100755 index 000000000..651f31f5f --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/ViewService.cs @@ -0,0 +1,74 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.spec; + +namespace com.espertech.esper.view +{ + /// Service interface for creating views. + public interface ViewService + { + /// + /// Returns a chain of view factories that can be used to obtain the readonly event type, and that can later be used + /// to actually create the chain of views or reuse existing views. + /// + /// Does not actually hook up the view factories or views against the event stream, but creates view factories and + /// sets parameters on each view factory as supplied. Determines if view factories are compatible in the chain via + /// the attach method. + /// + /// the stream number starting at zero, a join would have Count streams + /// is the event type of the event stream that originates the raw events + /// the specification for each view factory in the chain to be created + /// stream options such as unidirectional, retain-union etc + /// dependent services + /// if set to true [is subquery]. + /// The subquery number. + /// + /// chain of view factories + /// + /// ViewProcessingException thrown if a view factory doesn't take parameters as supplied,or cannot hook onto it's parent view or event stream + ViewFactoryChain CreateFactories( + int streamNum, + EventType parentEventType, + ViewSpec[] viewSpecList, + StreamSpecOptions options, + StatementContext context, + bool isSubquery, + int subqueryNumber); + + /// + /// Creates the views given a chain of view factories. + /// Attempts to reuse compatible views under then parent event stream viewable as indicated by + /// each view factories reuse method. + /// + /// is the event stream to hook into + /// defines the list of view factorys to call makeView or canReuse on + /// provides services + /// if set to true [has previous node]. + /// + /// last viewable in chain, or the eventStreamViewable if no view factories are supplied + /// + ViewServiceCreateResult CreateViews( + Viewable eventStreamViewable, + IList viewFactoryChain, + AgentInstanceViewFactoryChainContext viewFactoryChainContext, + bool hasPreviousNode); + + /// + /// Removes a view discoupling the view and any of it's parent views up the tree to the last shared parent view. + /// + /// the event stream that originates the raw events + /// the view (should be the last in a chain) to remove + void Remove(EventStream eventStream, Viewable view); + } +} diff --git a/NEsper.Core/NEsper.Core/view/ViewServiceCreateResult.cs b/NEsper.Core/NEsper.Core/view/ViewServiceCreateResult.cs new file mode 100755 index 000000000..b2a0a999e --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/ViewServiceCreateResult.cs @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +namespace com.espertech.esper.view +{ + public class ViewServiceCreateResult + { + public ViewServiceCreateResult(Viewable finalViewable, Viewable topViewable, IList newViews) + { + FinalViewable = finalViewable; + TopViewable = topViewable; + NewViews = newViews; + } + + public Viewable FinalViewable { get; private set; } + + public Viewable TopViewable { get; private set; } + + public IList NewViews { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/view/ViewServiceHelper.cs b/NEsper.Core/NEsper.Core/view/ViewServiceHelper.cs new file mode 100755 index 000000000..eee1fbeee --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/ViewServiceHelper.cs @@ -0,0 +1,395 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client.annotation; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.spec; +using com.espertech.esper.epl.virtualdw; +using com.espertech.esper.view.std; + +namespace com.espertech.esper.view +{ + /// + /// Utility methods to deal with chains of views, and for merge/group-by views. + /// + public class ViewServiceHelper + { + private static readonly ILog Log = + LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + public static ICollection GetUniqueCandidateProperties( + IList viewFactory, + Attribute[] annotations) + { + var disableUniqueImplicit = HintEnum.DISABLE_UNIQUE_IMPLICIT_IDX.GetHint(annotations) != null; + if (viewFactory == null || viewFactory.IsEmpty()) + { + return null; + } + if (viewFactory[0] is GroupByViewFactoryMarker) + { + var criteria = ((GroupByViewFactoryMarker) viewFactory[0]).CriteriaExpressions; + var groupedCriteria = ExprNodeUtility.GetPropertyNamesIfAllProps(criteria); + if (groupedCriteria == null) + { + return null; + } + if (viewFactory[1] is DataWindowViewFactoryUniqueCandidate && !disableUniqueImplicit) + { + var uniqueFactory = (DataWindowViewFactoryUniqueCandidate) viewFactory[1]; + var uniqueCandidates = uniqueFactory.UniquenessCandidatePropertyNames; + if (uniqueCandidates != null) + { + uniqueCandidates.AddAll(groupedCriteria); + } + return uniqueCandidates; + } + return null; + } + else if (viewFactory[0] is DataWindowViewFactoryUniqueCandidate && !disableUniqueImplicit) + { + var uniqueFactory = (DataWindowViewFactoryUniqueCandidate) viewFactory[0]; + return uniqueFactory.UniquenessCandidatePropertyNames; + } + else if (viewFactory[0] is VirtualDWViewFactory) + { + var vdw = (VirtualDWViewFactory) viewFactory[0]; + return vdw.UniqueKeys; + } + return null; + } + + /// + /// Add merge views for any views in the chain requiring a merge (group view). + /// Appends to the list of view specifications passed in one ore more + /// new view specifications that represent merge views. + /// Merge views have the same parameter list as the (group) view they merge data for. + /// + /// is a list of view definitions defining the chain of views. + /// indicating that the view chain configuration is invalid + internal static void AddMergeViews(IList specifications) + { + if (Log.IsDebugEnabled) + { + Log.Debug(".addMergeViews Incoming specifications=" + specifications.Render()); + } + + // A grouping view requires a merge view and cannot be last since it would not group sub-views + if (specifications.Count > 0) + { + var lastView = specifications[specifications.Count - 1]; + var viewEnum = ViewEnumExtensions.ForName(lastView.ObjectNamespace, lastView.ObjectName); + if ((viewEnum != null) && (viewEnum.Value.GetMergeView() != null)) + { + throw new ViewProcessingException( + "Invalid use of the '" + + lastView.ObjectName + + "' view, the view requires one or more child views to group, or consider using the group-by clause"); + } + } + + var mergeViewSpecs = new LinkedList(); + + foreach (var spec in specifications) + { + var viewEnum = ViewEnumExtensions.ForName(spec.ObjectNamespace, spec.ObjectName); + if (viewEnum == null) + { + continue; + } + + var mergeView = viewEnum.Value.GetMergeView(); + if (mergeView == null) + { + continue; + } + + // The merge view gets the same parameters as the view that requires the merge + var mergeViewSpec = new ViewSpec( + mergeView.Value.GetNamespace(), mergeView.Value.GetName(), + spec.ObjectParameters); + + // The merge views are added to the beginning of the list. + // This enables group views to stagger ie. Marketdata.Group("symbol").Group("feed").xxx.Merge(...).Merge(...) + mergeViewSpecs.AddFirst(mergeViewSpec); + } + + specifications.AddAll(mergeViewSpecs); + + if (Log.IsDebugEnabled) + { + Log.Debug(".addMergeViews Outgoing specifications=" + specifications.Render()); + } + } + + /// + /// Instantiate a chain of views. + /// + /// - parent view to add the chain to + /// - is the view factories to use to make each view, or reuse and existing view + /// context + /// chain of views instantiated + public static IList InstantiateChain( + Viewable parentViewable, + IList viewFactories, + AgentInstanceViewFactoryChainContext viewFactoryChainContext) + { + var newViews = new List(); + var parent = parentViewable; + + for (var i = 0; i < viewFactories.Count; i++) + { + var viewFactory = viewFactories[i]; + + // Create the new view object + var currentView = viewFactory.MakeView(viewFactoryChainContext); + + newViews.Add(currentView); + parent.AddView(currentView); + + // Next parent is the new view + parent = currentView; + } + + return newViews; + } + + public static void RemoveFirstUnsharedView(IList childViews) + { + for (var i = childViews.Count - 1; i >= 0; i--) + { + var child = childViews[i]; + var parent = child.Parent; + if (parent == null) + { + return; + } + parent.RemoveView(child); + if (parent.HasViews) + { + return; + } + } + } + + /// + /// Removes a view from a parent view returning the orphaned parent views in a list. + /// + /// - parent to remove view from + /// - view to remove + /// chain of orphaned views + internal static IList RemoveChainLeafView( + Viewable parentViewable, + Viewable viewToRemove) + { + var removedViews = new List(); + + // The view to remove must be a leaf node - non-leaf views are just not removed + if (viewToRemove.HasViews) + { + return removedViews; + } + + // Find child viewToRemove among descendent views + var viewPath = ViewSupport.FindDescendent(parentViewable, viewToRemove); + + if (viewPath == null) + { + var message = "Viewable not found when removing view " + viewToRemove; + throw new ArgumentException(message); + } + + // The viewToRemove is a direct child view of the stream + if (viewPath.IsEmpty()) + { + var isViewRemoved = parentViewable.RemoveView((View) viewToRemove); + + if (!isViewRemoved) + { + var message = "Failed to remove immediate child view " + viewToRemove; + Log.Error(".remove " + message); + throw new IllegalStateException(message); + } + + removedViews.Add((View) viewToRemove); + return removedViews; + } + + var viewPathArray = viewPath.ToArray(); + var currentView = (View) viewToRemove; + + // Remove child from parent views until a parent view has more children, + // or there are no more parents (index=0). + for (var index = viewPathArray.Length - 1; index >= 0; index--) + { + var isViewRemoved = viewPathArray[index].RemoveView(currentView); + removedViews.Add(currentView); + + if (!isViewRemoved) + { + var message = "Failed to remove view " + currentView; + Log.Error(".remove " + message); + throw new IllegalStateException(message); + } + + // If the parent views has more child views, we are done + if (viewPathArray[index].HasViews) + { + break; + } + + // The parent of the top parent is the stream, remove from stream + if (index == 0) + { + parentViewable.RemoveView(viewPathArray[0]); + removedViews.Add(viewPathArray[0]); + } + else + { + currentView = viewPathArray[index]; + } + } + + return removedViews; + } + + /// + /// Match the views under the stream to the list of view specications passed in. + /// The method changes the view specifications list passed in and removes those + /// specifications for which matcing views have been found. + /// If none of the views under the stream matches the first view specification passed in, + /// the method returns the stream itself and leaves the view specification list unchanged. + /// If one view under the stream matches, the view's specification is removed from the list. + /// The method will then attempt to determine if any child views of that view also match + /// specifications. + /// + /// + /// is the top rootViewable event stream to which all views are attached as child views + /// This parameter is changed by this method, ie. specifications are removed if they match existing views. + /// + /// is the view specifications for making views + /// agent instance context + /// + /// a pair of (A) the stream if no views matched, or the last child view that matched (B) the full list + /// of parent views + /// + internal static Pair> MatchExistingViews( + Viewable rootViewable, + IList viewFactories, + AgentInstanceContext agentInstanceContext) + { + var currentParent = rootViewable; + IList matchedViewList = new List(); + + bool foundMatch; + + if (viewFactories.IsEmpty()) + { + return new Pair>(rootViewable, Collections.GetEmptyList()); + } + + do + { + foundMatch = false; + + foreach (var childView in currentParent.Views) + { + var currentFactory = viewFactories[0]; + + if (!(currentFactory.CanReuse(childView, agentInstanceContext))) + { + continue; + } + + // The specifications match, check current data window size + viewFactories.RemoveAt(0); + currentParent = childView; + foundMatch = true; + matchedViewList.Add(childView); + break; + } + } while (foundMatch && (!viewFactories.IsEmpty())); + + return new Pair>(currentParent, matchedViewList); + } + + /// + /// Given a list of view specifications obtained from by parsing this method instantiates a list of view factories. + /// The view factories are not yet aware of each other after leaving this method (so not yet chained logically). + /// They are simply instantiated and assigned view parameters. + /// + /// is the stream number + /// is the view definition + /// is statement service context and statement INFO + /// subquery indicator + /// for subqueries + /// if the factory cannot be creates such as for invalid view spec + /// list of view factories + public static IList InstantiateFactories( + int streamNum, + IList viewSpecList, + StatementContext statementContext, + bool isSubquery, + int subqueryNumber) + { + var factoryChain = new List(); + + var grouped = false; + foreach (var spec in viewSpecList) + { + // Create the new view factory + var viewFactory = statementContext.ViewResolutionService.Create( + spec.ObjectNamespace, spec.ObjectName); + + var audit = AuditEnum.VIEW.GetAudit(statementContext.Annotations); + if (audit != null) + { + viewFactory = ViewFactoryProxy.NewInstance( + statementContext.EngineURI, statementContext.StatementName, viewFactory, spec.ObjectName); + } + factoryChain.Add(viewFactory); + + // Set view factory parameters + try + { + var context = new ViewFactoryContext( + statementContext, streamNum, spec.ObjectNamespace, spec.ObjectName, isSubquery, subqueryNumber, + grouped); + viewFactory.SetViewParameters(context, spec.ObjectParameters); + } + catch (ViewParameterException e) + { + throw new ViewProcessingException( + "Error in view '" + spec.ObjectName + + "', " + e.Message, e); + } + + if (viewFactory is GroupByViewFactoryMarker) + { + grouped = true; + } + if (viewFactory is MergeViewFactoryMarker) + { + grouped = false; + } + } + + return factoryChain; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/view/ViewServiceImpl.cs b/NEsper.Core/NEsper.Core/view/ViewServiceImpl.cs new file mode 100755 index 000000000..3da24098c --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/ViewServiceImpl.cs @@ -0,0 +1,270 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.spec; +using com.espertech.esper.view.internals; +using com.espertech.esper.view.std; + +namespace com.espertech.esper.view +{ + /// + /// Implementation of the view evaluation service business interface. + /// + public sealed class ViewServiceImpl : ViewService + { + /// Ctor. + public ViewServiceImpl() + { + } + + public ViewFactoryChain CreateFactories(int streamNum, EventType parentEventType, ViewSpec[] viewSpecDefinitions, StreamSpecOptions options, StatementContext context, bool isSubquery, int subqueryNumber) + { + // Clone the view spec list to prevent parameter modification + IList viewSpecList = new List(viewSpecDefinitions); + + // Inspect views and add merge views if required + ViewServiceHelper.AddMergeViews(viewSpecList); + + // Instantiate factories, not making them aware of each other yet + var viewFactories = ViewServiceHelper.InstantiateFactories(streamNum, viewSpecList, context, isSubquery, subqueryNumber); + + ViewFactory parentViewFactory = null; + IList attachedViewFactories = new List(); + for (var i = 0; i < viewFactories.Count; i++) + { + var factoryToAttach = viewFactories[i]; + try + { + factoryToAttach.Attach(parentEventType, context, parentViewFactory, attachedViewFactories); + attachedViewFactories.Add(viewFactories[i]); + parentEventType = factoryToAttach.EventType; + } + catch (ViewParameterException ex) + { + var text = "Error attaching view to parent view"; + if (i == 0) + { + text = "Error attaching view to event stream"; + } + throw new ViewProcessingException(text + ": " + ex.Message, ex); + } + } + + // obtain count of data windows + var dataWindowCount = 0; + var firstNonDataWindowIndex = -1; + for (var i = 0; i < viewFactories.Count; i++) + { + var factory = viewFactories[i]; + if (factory is DataWindowViewFactory) + { + dataWindowCount++; + continue; + } + if ((factory is GroupByViewFactoryMarker) || (factory is MergeViewFactory)) + { + continue; + } + if (firstNonDataWindowIndex == -1) + { + firstNonDataWindowIndex = i; + } + } + + var isAllowMultipleExpiry = context.ConfigSnapshot.EngineDefaults.ViewResources.IsAllowMultipleExpiryPolicies; + var isRetainIntersection = options.IsRetainIntersection; + var isRetainUnion = options.IsRetainUnion; + + // Set the default to retain-intersection unless allow-multiple-expiry is turned on + if ((!isAllowMultipleExpiry) && (!isRetainUnion)) + { + isRetainIntersection = true; + } + + // handle multiple data windows with retain union. + // wrap view factories into the union view factory and handle a group-by, if present + if ((isRetainUnion || isRetainIntersection) && dataWindowCount > 1) + { + viewFactories = GetRetainViewFactories(parentEventType, viewFactories, isRetainUnion, context); + } + + return new ViewFactoryChain(parentEventType, viewFactories); + } + + private IList GetRetainViewFactories(EventType parentEventType, IList viewFactories, bool isUnion, StatementContext context) + { + ICollection groupByFactory = new HashSet(); + ICollection mergeFactory = new HashSet(); + IList derivedValueViews = new List(); + IList dataWindowViews = new List(); + for (var i = 0; i < viewFactories.Count; i++) + { + var factory = viewFactories[i]; + if (factory is GroupByViewFactoryMarker) + { + groupByFactory.Add(i); + } + else if (factory is MergeViewFactoryMarker) + { + mergeFactory.Add(i); + } + else if (factory is DataWindowViewFactory) + { + dataWindowViews.Add(factory); + } + else + { + derivedValueViews.Add(factory); + } + } + + if (groupByFactory.Count > 1) + { + throw new ViewProcessingException("Multiple groupwin views are not allowed in conjuntion with multiple data windows"); + } + if ((groupByFactory.IsNotEmpty()) && (groupByFactory.First() != 0)) + { + throw new ViewProcessingException("The groupwin view must occur in the first position in conjuntion with multiple data windows"); + } + if ((groupByFactory.IsNotEmpty()) && (mergeFactory.First() != (viewFactories.Count - 1))) + { + throw new ViewProcessingException("The merge view cannot be used in conjuntion with multiple data windows"); + } + + GroupByViewFactoryMarker groupByViewFactory = null; + MergeViewFactoryMarker mergeViewFactory = null; + if (groupByFactory.IsNotEmpty()) + { + groupByViewFactory = (GroupByViewFactoryMarker)viewFactories[0]; + mergeViewFactory = (MergeViewFactoryMarker)viewFactories[viewFactories.Count - 1]; + viewFactories.RemoveAt(0); + viewFactories.RemoveAt(viewFactories.Count - 1); + + } + + ViewFactory retainPolicy; + if (isUnion) + { + var viewFactory = (UnionViewFactory)context.ViewResolutionService.Create("internal", "union"); + viewFactory.ParentEventType = parentEventType; + viewFactory.ViewFactories = dataWindowViews; + retainPolicy = viewFactory; + } + else + { + var viewFactory = (IntersectViewFactory)context.ViewResolutionService.Create("internal", "intersect"); + viewFactory.ParentEventType = parentEventType; + viewFactory.ViewFactories = dataWindowViews; + retainPolicy = viewFactory; + } + + IList nonRetainViewFactories = new List(); + nonRetainViewFactories.Add(retainPolicy); + if (groupByViewFactory != null) + { + nonRetainViewFactories.Insert(0, (ViewFactory)groupByViewFactory); + nonRetainViewFactories.AddAll(derivedValueViews); + nonRetainViewFactories.Add((ViewFactory)mergeViewFactory); + } + else + { + nonRetainViewFactories.AddAll(derivedValueViews); + } + + return nonRetainViewFactories; + } + + public ViewServiceCreateResult CreateViews( + Viewable eventStreamViewable, + IList viewFactories, + AgentInstanceViewFactoryChainContext viewFactoryChainContext, + bool hasPreviousNode) + { + // Attempt to find existing views under the stream that match specs. + // The viewSpecList may have been changed by this method. + Pair> resultPair; + if (hasPreviousNode) + { + resultPair = new Pair>(eventStreamViewable, Collections.GetEmptyList()); + } + else + { + resultPair = ViewServiceHelper.MatchExistingViews(eventStreamViewable, viewFactories, viewFactoryChainContext.AgentInstanceContext); + } + + var parentViewable = resultPair.First; + + if (viewFactories.IsEmpty()) + { + if (Log.IsDebugEnabled) + { + Log.Debug(".createView No new views created, dumping stream ... " + eventStreamViewable); + ViewSupport.DumpChildViews("EventStream ", eventStreamViewable); + } + + return new ViewServiceCreateResult(parentViewable, parentViewable, Collections.GetEmptyList()); // we know its a view here since the factory list is empty + } + + // Instantiate remaining chain of views from the remaining factories which didn't match to existing views. + var views = ViewServiceHelper.InstantiateChain(parentViewable, viewFactories, viewFactoryChainContext); + + // Initialize any views that need initializing after the chain is complete + foreach (var view in views) + { + if (view is InitializableView) + { + var initView = (InitializableView)view; + initView.Initialize(); + } + } + + if (Log.IsDebugEnabled) + { + Log.Debug(".createView New views created for stream, all views ... " + eventStreamViewable); + ViewSupport.DumpChildViews("EventStream ", eventStreamViewable); + } + + return new ViewServiceCreateResult(views[views.Count - 1], views[0], views); + } + + public void Remove(EventStream eventStream, Viewable viewToRemove) + { + // If the viewToRemove to remove has child viewToRemove, don't disconnect - the child ViewToRemove(s) need this + if (viewToRemove.HasViews) + { + return; + } + + if (Log.IsDebugEnabled) + { + Log.Debug(".remove Views before the remove of view " + viewToRemove + ", for event stream " + eventStream); + ViewSupport.DumpChildViews("EventStream ", eventStream); + } + + // Remove views in chain leaving only non-empty parent views to the child view to be removed + ViewServiceHelper.RemoveChainLeafView(eventStream, viewToRemove); + + if (Log.IsDebugEnabled) + { + Log.Debug(".remove Views after the remove, for event stream " + eventStream); + ViewSupport.DumpChildViews("EventStream ", eventStream); + } + } + + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + } +} diff --git a/NEsper.Core/NEsper.Core/view/ViewServicePreviousFactory.cs b/NEsper.Core/NEsper.Core/view/ViewServicePreviousFactory.cs new file mode 100755 index 000000000..e15336cde --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/ViewServicePreviousFactory.cs @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.collection; +using com.espertech.esper.core.context.util; +using com.espertech.esper.view.ext; + +namespace com.espertech.esper.view +{ + public interface ViewServicePreviousFactory + { + ViewUpdatedCollection GetOptPreviousExprRandomAccess( + AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext); + + ViewUpdatedCollection GetOptPreviousExprRelativeAccess( + AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext); + + IStreamSortRankRandomAccess GetOptPreviousExprSortedRankedAccess( + AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext); + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/view/ViewServicePreviousFactoryImpl.cs b/NEsper.Core/NEsper.Core/view/ViewServicePreviousFactoryImpl.cs new file mode 100755 index 000000000..56fc9d3e2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/ViewServicePreviousFactoryImpl.cs @@ -0,0 +1,60 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.collection; +using com.espertech.esper.core.context.util; +using com.espertech.esper.view.ext; +using com.espertech.esper.view.window; + +namespace com.espertech.esper.view +{ + public class ViewServicePreviousFactoryImpl : ViewServicePreviousFactory + { + public ViewUpdatedCollection GetOptPreviousExprRandomAccess( + AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext) + { + IStreamRandomAccess randomAccess = null; + if (agentInstanceViewFactoryContext.PreviousNodeGetter != null) + { + var getter = (RandomAccessByIndexGetter) agentInstanceViewFactoryContext.PreviousNodeGetter; + randomAccess = new IStreamRandomAccess(getter); + getter.Updated(randomAccess); + } + return randomAccess; + } + + public ViewUpdatedCollection GetOptPreviousExprRelativeAccess( + AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext) + { + IStreamRelativeAccess relativeAccessByEvent = null; + if (agentInstanceViewFactoryContext.PreviousNodeGetter != null) + { + var getter = (RelativeAccessByEventNIndexGetter) agentInstanceViewFactoryContext.PreviousNodeGetter; + var observer = (IStreamRelativeAccessUpdateObserver) getter; + relativeAccessByEvent = new IStreamRelativeAccess(observer); + observer.Updated(relativeAccessByEvent, null); + } + + return relativeAccessByEvent; + } + + public IStreamSortRankRandomAccess GetOptPreviousExprSortedRankedAccess( + AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext) + { + IStreamSortRankRandomAccess rankedRandomAccess = null; + if (agentInstanceViewFactoryContext.PreviousNodeGetter != null) + { + var getter = (RandomAccessByIndexGetter) agentInstanceViewFactoryContext.PreviousNodeGetter; + rankedRandomAccess = new IStreamSortRankRandomAccessImpl(getter); + getter.Updated(rankedRandomAccess); + } + + return rankedRandomAccess; + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/view/ViewServiceProvider.cs b/NEsper.Core/NEsper.Core/view/ViewServiceProvider.cs new file mode 100755 index 000000000..339b9b0fd --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/ViewServiceProvider.cs @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +namespace com.espertech.esper.view +{ + /// + /// Static factory for implementations of the interface. + /// + + public sealed class ViewServiceProvider + { + /// Creates an implementation of the ViewService interface. + /// implementation + /// + public static ViewService NewService() + { + return new ViewServiceImpl(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/ViewSupport.cs b/NEsper.Core/NEsper.Core/view/ViewSupport.cs new file mode 100755 index 000000000..f045da56d --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/ViewSupport.cs @@ -0,0 +1,307 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.logging; +using com.espertech.esper.util; + +namespace com.espertech.esper.view +{ + /// + /// A helper class for View implementations that provides generic implementation for some of the + /// methods. Methods that contain the actual logic of the view are not implemented in this class. + /// A common implementation normally does not need to override any of the methods implemented here, + /// their implementation is generic and should suffice. The class provides a convenience method for + /// updating it's children data UpdateChildren(Object[], Object[]). This method should be called + /// from within the View.Update(Object[], Object[]) methods in the subclasses. + /// + public abstract class ViewSupport : View + { + public readonly static View[] EMPTY_VIEW_ARRAY = new View[0]; + + /// Parent viewable to this view - directly accessible by subclasses. + private Viewable _parent; + + private View[] _children; + + /// Constructor. + protected ViewSupport() + { + _children = EMPTY_VIEW_ARRAY; + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public abstract IEnumerator GetEnumerator(); + public abstract EventType EventType { get; } + public abstract void Update(EventBean[] newData, EventBean[] oldData); + + public virtual Viewable Parent + { + get { return _parent; } + set { _parent = value; } + } + + public virtual View AddView(View view) + { + _children = AddView(_children, view); + view.Parent = this; + return view; + } + + public virtual bool RemoveView(View view) + { + int index = FindViewIndex(_children, view); + if (index == -1) + { + return false; + } + _children = RemoveView(_children, index); + view.Parent = null; + return true; + } + + public virtual void RemoveAllViews() + { + _children = EMPTY_VIEW_ARRAY; + } + + public virtual View[] Views + { + get { return _children; } + } + + public virtual bool HasViews + { + get { return _children.Length > 0; } + } + + /// Updates all the children with new data. Views may want to use the hasViews method on the Viewable interface to determine if there are any child views attached at all, and save the work of constructing the arrays and making the call to UpdateChildren() in case there aren't any children attached. + /// is the array of new event data + /// is the array of old event data + public virtual void UpdateChildren(EventBean[] newData, EventBean[] oldData) + { + int size = _children.Length; + + // Provide a shortcut for a single child view since this is a very common case. + // No iteration required here. + if (size == 0) + { + return; + } + if (size == 1) + { + _children[0].Update(newData, oldData); + } + else + { + // since there often is zero or one view underneath, the iteration case is slower + foreach (View child in _children) + { + child.Update(newData, oldData); + } + } + } + + /// Updates all the children with new data. Static convenience method that accepts the list of child views as a parameter. + /// is the list of child views to send the data to + /// is the array of new event data + /// is the array of old event data + internal static void UpdateChildren(ICollection childViews, EventBean[] newData, EventBean[] oldData) + { + foreach (View child in childViews) + { + child.Update(newData, oldData); + } + } + + /// Convenience method for logging the parameters passed to the Update method. Only logs if debug is enabled. + /// is a prefix text to output for each line + /// is the data in an Update call + public static void DumpUpdateParams(String prefix, UniformPair result) + { + EventBean[] newEventArr = result != null ? result.First : null; + EventBean[] oldEventArr = result != null ? result.Second : null; + DumpUpdateParams(prefix, newEventArr, oldEventArr); + } + + /// Convenience method for logging the parameters passed to the Update method. Only logs if debug is enabled. + /// is a prefix text to output for each line + /// is the new data in an Update call + /// is the old data in an Update call + public static void DumpUpdateParams(String prefix, Object[] newData, Object[] oldData) + { + if (!Log.IsDebugEnabled) + { + return; + } + + var writer = new StringWriter(); + if (newData == null) + { + writer.WriteLine(prefix + " newData=null "); + } + else + { + writer.WriteLine(prefix + " newData.size=" + newData.Length + "..."); + PrintObjectArray(prefix, writer, newData); + } + + if (oldData == null) + { + writer.WriteLine(prefix + " oldData=null "); + } + else + { + writer.WriteLine(prefix + " oldData.size=" + oldData.Length + "..."); + PrintObjectArray(prefix, writer, oldData); + } + } + + private static void PrintObjectArray(String prefix, TextWriter writer, Object[] objects) + { + int count = 0; + foreach (Object @object in objects) + { + var objectToString = (@object == null) ? "null" : @object.ToString(); + writer.WriteLine(prefix + " #" + count + " = " + objectToString); + } + } + + /// Convenience method for logging the child views of a Viewable. Only logs if debug is enabled. This is a recursive method. + /// is a text to print for each view printed + /// is the parent for which the child views are displayed. + public static void DumpChildViews(String prefix, Viewable parentViewable) + { + if (Log.IsDebugEnabled) + { + if (parentViewable != null && parentViewable.Views != null) + { + foreach (View child in parentViewable.Views) + { + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled)) + { + Log.Debug(".dumpChildViews " + prefix + ' ' + child); + } + DumpChildViews(prefix + " ", child); + } + } + } + } + + /// + /// Find the descendent view in the view tree under the parent view returning the list of view nodes between the parent view and the descendent view. Returns null if the descendent view is not found. Returns an empty list if the descendent view is a child view of the parent view. + /// + /// is the view to start searching under + /// is the view to find + /// + /// list of Viewable nodes between parent and descendent view. + /// + public static IList FindDescendent(Viewable parentView, Viewable descendentView) + { + var stack = new Stack(); + + foreach (View view in parentView.Views) + { + if (view == descendentView) + { + var viewList = new List(stack); + viewList.Reverse(); + return viewList; + } + + bool found = FindDescendentRecusive(view, descendentView, stack); + + if (found) + { + var viewList = new List(stack); + viewList.Reverse(); + return viewList; + } + } + + return null; + } + + private static bool FindDescendentRecusive(View parentView, Viewable descendentView, Stack stack) + { + stack.Push(parentView); + + bool found = false; + foreach (View view in parentView.Views) + { + if (view == descendentView) + { + return true; + } + + found = FindDescendentRecusive(view, descendentView, stack); + + if (found) + { + break; + } + } + + if (!found) + { + stack.Pop(); + return false; + } + + return true; + } + + public static View[] AddView(View[] children, View view) + { + if (children.Length == 0) + { + return new View[] { view }; + } + else + { + return (View[])(CollectionUtil.ArrayExpandAddSingle(children, view)); + } + } + + public static int FindViewIndex(View[] children, View view) + { + for (int i = 0; i < children.Length; i++) + { + if (children[i] == view) + { + return i; + } + } + return -1; + } + + public static View[] RemoveView(View[] children, int index) + { + if (children.Length == 1) + { + return EMPTY_VIEW_ARRAY; + } + else + { + return (View[])(CollectionUtil.ArrayShrinkRemoveSingle(children, index)); + } + } + + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + } +} diff --git a/NEsper.Core/NEsper.Core/view/Viewable.cs b/NEsper.Core/NEsper.Core/view/Viewable.cs new file mode 100755 index 000000000..91d2863d6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/Viewable.cs @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.view +{ + /// + /// The Viewable interface marks an object as supporting zero, one or more View instances. + /// All implementing classes must call each view's 'Update' method when new data enters it. + /// Implementations must take care to synchronize methods of this interface with other methods + /// such that data flow is threadsafe. + /// + public interface Viewable : EventCollection + { + /// Add a view to the viewable object. + /// to add + /// view to add + View AddView(View view); + + /// Returns all added views. + /// list of added views + View[] Views { get; } + + /// Remove a view. + /// to remove + /// true to indicate that the view to be removed existed within this view, false if the view toremove could not be found + bool RemoveView(View view); + + /// Remove all views. + void RemoveAllViews(); + + /// Test is there are any views to the Viewable. + /// true indicating there are child views, false indicating there are no child views + bool HasViews { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/view/ViewableDefaultImpl.cs b/NEsper.Core/NEsper.Core/view/ViewableDefaultImpl.cs new file mode 100755 index 000000000..42cfae48d --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/ViewableDefaultImpl.cs @@ -0,0 +1,66 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.view +{ + public class ViewableDefaultImpl : Viewable + { + private readonly EventType _eventType; + + public ViewableDefaultImpl(EventType eventType) + { + _eventType = eventType; + } + + public View AddView(View view) + { + return null; + } + + public View[] Views + { + get { return ViewSupport.EMPTY_VIEW_ARRAY; } + } + + public bool RemoveView(View view) + { + return false; + } + + public void RemoveAllViews() + { + } + + public bool HasViews + { + get { return false; } + } + + public EventType EventType + { + get { return _eventType; } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public IEnumerator GetEnumerator() + { + return EnumerationHelper.Empty(); + //return ((IEnumerable) null).GetEnumerator(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/ZeroDepthStreamIterable.cs b/NEsper.Core/NEsper.Core/view/ZeroDepthStreamIterable.cs new file mode 100755 index 000000000..84b203a48 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/ZeroDepthStreamIterable.cs @@ -0,0 +1,114 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.view +{ + /// + /// Event stream implementation that does not keep any window by itself of the events + /// coming into the stream, however is itself iterable and keeps the last event. + /// + public sealed class ZeroDepthStreamIterable : EventStream + { + private View[] _children = ViewSupport.EMPTY_VIEW_ARRAY; + private readonly EventType _eventType; + private EventBean _lastInsertedEvent; + private EventBean[] _lastInsertedEvents; + + /// Ctor. + /// type of event + public ZeroDepthStreamIterable(EventType eventType) + { + _eventType = eventType; + } + + public void Insert(EventBean[] events) + { + foreach (View childView in _children) + { + childView.Update(events, null); + } + + _lastInsertedEvents = events; + } + + public void Insert(EventBean theEvent) + { + // Get a new array created rather then re-use the old one since some client listeners + // to this view may keep reference to the new data + var row = new EventBean[]{theEvent}; + foreach (View childView in _children) + { + childView.Update(row, null); + } + + _lastInsertedEvent = theEvent; + } + + public EventType EventType + { + get { return _eventType; } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public IEnumerator GetEnumerator() + { + if (_lastInsertedEvents != null) + { + for (int ii = 0; ii < _lastInsertedEvents.Length; ii++) + yield return _lastInsertedEvents[ii]; + } + else if (_lastInsertedEvent != null) + { + yield return _lastInsertedEvent; + } + } + + public View AddView(View view) + { + _children = ViewSupport.AddView(_children, view); + view.Parent = this; + return view; + } + + public View[] Views + { + get { return _children; } + } + + public bool RemoveView(View view) + { + int index = ViewSupport.FindViewIndex(_children, view); + if (index == -1) { + return false; + } + _children = ViewSupport.RemoveView(_children, index); + view.Parent = null; + return true; + } + + public bool HasViews + { + get { return _children.Length > 0; } + } + + public void RemoveAllViews() + { + _children = ViewSupport.EMPTY_VIEW_ARRAY; + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/ZeroDepthStreamNoIterate.cs b/NEsper.Core/NEsper.Core/view/ZeroDepthStreamNoIterate.cs new file mode 100755 index 000000000..a84b74d01 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/ZeroDepthStreamNoIterate.cs @@ -0,0 +1,102 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.util; + +namespace com.espertech.esper.view +{ + /// + /// Event stream implementation that does not keep any window by itself of the events + /// coming into the stream, without the possibility to iterate the last event. + /// + public sealed class ZeroDepthStreamNoIterate : EventStream + { + private View[] _children = ViewSupport.EMPTY_VIEW_ARRAY; + private readonly EventType _eventType; + + /// Ctor. + /// type of event + public ZeroDepthStreamNoIterate(EventType eventType) + { + _eventType = eventType; + } + + public void Insert(EventBean[] events) + { + var length = _children.Length; + for (int ii = 0; ii < length; ii++) + { + _children[ii].Update(events, null); + } + } + + public void Insert(EventBean theEvent) + { + // Get a new array created rather then re-use the old one since some client listeners + // to this view may keep reference to the new data + var row = new EventBean[]{theEvent}; + var length = _children.Length; + for (int ii = 0; ii < length; ii++) + { + _children[ii].Update(row, null); + } + } + + public EventType EventType + { + get { return _eventType; } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public IEnumerator GetEnumerator() + { + return CollectionUtil.NULL_EVENT_ITERATOR; + } + + public View AddView(View view) + { + _children = ViewSupport.AddView(_children, view); + view.Parent = this; + return view; + } + + public View[] Views + { + get { return _children; } + } + + public bool RemoveView(View view) + { + int index = ViewSupport.FindViewIndex(_children, view); + if (index == -1) { + return false; + } + _children = ViewSupport.RemoveView(_children, index); + view.Parent = null; + return true; + } + + public bool HasViews + { + get { return _children.Length > 0; } + } + + public void RemoveAllViews() + { + _children = ViewSupport.EMPTY_VIEW_ARRAY; + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/ext/IStreamSortRankRandomAccess.cs b/NEsper.Core/NEsper.Core/view/ext/IStreamSortRankRandomAccess.cs new file mode 100755 index 000000000..9a7cf2b14 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/ext/IStreamSortRankRandomAccess.cs @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.compat.collections; +using com.espertech.esper.view.window; + +namespace com.espertech.esper.view.ext +{ + /// + /// Provides random access into a rank-window's data. + /// + public interface IStreamSortRankRandomAccess : RandomAccessByIndex + { + void Refresh(OrderedDictionary sortedEvents, int currentSize, int maxSize); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/view/ext/IStreamSortRankRandomAccessImpl.cs b/NEsper.Core/NEsper.Core/view/ext/IStreamSortRankRandomAccessImpl.cs new file mode 100755 index 000000000..de0ccafb2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/ext/IStreamSortRankRandomAccessImpl.cs @@ -0,0 +1,197 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.view.window; + +namespace com.espertech.esper.view.ext +{ + /// + /// Provides random access into a rank-window's data. + /// + public class IStreamSortRankRandomAccessImpl : RandomAccessByIndex, IStreamSortRankRandomAccess + { + private readonly RandomAccessByIndexObserver _updateObserver; + + private OrderedDictionary _sortedEvents; + private int _currentSize; + + private IEnumerator _enumerator; + private EventBean[] _cache; + private int _cacheFilledTo; + + /// + /// Ctor. + /// + /// for indicating updates to + public IStreamSortRankRandomAccessImpl(RandomAccessByIndexObserver updateObserver) + { + _updateObserver = updateObserver; + } + + /// + /// Refreshes the random access data with the updated information. + /// + /// is the sorted window contents + /// is the current size of the window + /// is the maximum size of the window + public void Refresh(OrderedDictionary sortedEvents, int currentSize, int maxSize) + { + _updateObserver.Updated(this); + _sortedEvents = sortedEvents; + _currentSize = currentSize; + + _enumerator = null; + _cacheFilledTo = 0; + if (_cache == null || _cache.Length < maxSize) + { + _cache = new EventBean[maxSize]; + } + } + + public EventBean GetNewData(int index) + { + if (_enumerator == null) + { + _enumerator = _sortedEvents.Values.GetEnumerator(); + } + + // if asking for more then the sorted window currently holds, return no data + if (index >= _currentSize) + { + return null; + } + + // If we have it in cache, serve from cache + if (index < _cacheFilledTo) + { + return _cache[index]; + } + + // Load more into cache + while(true) + { + if (_cacheFilledTo == _currentSize) + { + break; + } + if (!_enumerator.MoveNext()) + { + break; + } + var entry = _enumerator.Current; + if (entry is IList) { + var events = (IList) entry; + foreach (var theEvent in events) + { + _cache[_cacheFilledTo] = theEvent; + _cacheFilledTo++; + } + } + else { + var theEvent = (EventBean) entry; + _cache[_cacheFilledTo] = theEvent; + _cacheFilledTo++; + } + + if (_cacheFilledTo > index) + { + break; + } + } + + // If we have it in cache, serve from cache + if (index <= _cacheFilledTo) + { + return _cache[index]; + } + + return null; + } + + public EventBean GetOldData(int index) + { + return null; + } + + public EventBean GetNewDataTail(int index) + { + InitCache(); + + if ((index < _cacheFilledTo) && (index >= 0)) + { + return _cache[_cacheFilledTo - index - 1]; + } + + return null; + } + + public IEnumerator GetWindowEnumerator() + { + InitCache(); + return _cache.Take(_cacheFilledTo).GetEnumerator(); + } + + public ICollection WindowCollectionReadOnly + { + get + { + InitCache(); + return _cache.Take(_cacheFilledTo).ToArray(); + } + } + + public int WindowCount + { + get { return _currentSize; } + } + + private void InitCache() + { + if (_enumerator == null) + { + _enumerator = _sortedEvents.Values.GetEnumerator(); + } + + // Load more into cache + while(true) + { + if (_cacheFilledTo == _currentSize) + { + break; + } + if (!_enumerator.MoveNext()) + { + break; + } + + var entry = _enumerator.Current; + if (entry is IList) { + var events = (IList) entry; + foreach (EventBean theEvent in events) + { + _cache[_cacheFilledTo] = theEvent; + _cacheFilledTo++; + } + } + else { + var theEvent = (EventBean) entry; + _cache[_cacheFilledTo] = theEvent; + _cacheFilledTo++; + } + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/view/ext/RankWindowEnumerator.cs b/NEsper.Core/NEsper.Core/view/ext/RankWindowEnumerator.cs new file mode 100755 index 000000000..19a28673c --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/ext/RankWindowEnumerator.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.view.ext +{ + /// + /// GetEnumerator for use by . + /// + public sealed class RankWindowEnumerator : MixedEventBeanAndCollectionEnumeratorBase + { + private readonly IDictionary _window; + + /// Ctor. + /// sorted map with events + public RankWindowEnumerator(IDictionary window) + : base(window.Keys) + { + _window = window; + } + + protected override Object GetValue(Object keyValue) + { + return _window.Get(keyValue); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/view/ext/RankWindowView.cs b/NEsper.Core/NEsper.Core/view/ext/RankWindowView.cs new file mode 100755 index 000000000..24af5a869 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/ext/RankWindowView.cs @@ -0,0 +1,418 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.util; + +namespace com.espertech.esper.view.ext +{ + /// + /// Window sorting by values in the specified field extending a specified number of elements + /// from the lowest value up or the highest value down and retaining only the last unique + /// value per key. The type of the field to be sorted in the event must implement the Comparable + /// interface. The natural order in which events arrived is used as the second sorting criteria. + /// Thus should events arrive with equal sort values the oldest event leaves the sort window first. + /// Old values removed from a another view are removed from the sort view. + /// + public class RankWindowView + : ViewSupport + , DataWindowView + , CloneableView + { + private readonly RankWindowViewFactory _rankWindowViewFactory; + private readonly ExprEvaluator[] _sortCriteriaEvaluators; + private readonly ExprNode[] _sortCriteriaExpressions; + private readonly ExprEvaluator[] _uniqueCriteriaEvaluators; + private readonly ExprNode[] _uniqueCriteriaExpressions; + private readonly EventBean[] _eventsPerStream = new EventBean[1]; + private readonly bool[] _isDescendingValues; + private readonly int _sortWindowSize; + private readonly IStreamSortRankRandomAccess _optionalRankedRandomAccess; + private readonly AgentInstanceViewFactoryChainContext _agentInstanceViewFactoryContext; + + private readonly IComparer _comparator; + + private readonly OrderedDictionary _sortedEvents; // key is computed sort-key, value is either List or EventBean + private readonly IDictionary _uniqueKeySortKeys; // key is computed unique-key, value is computed sort-key + private int _numberOfEvents; + + /// + /// Ctor. + /// + /// for copying this view in a group-by + /// The unique criteria expressions. + /// The unique criteria evaluators. + /// is the event property names to sort + /// The sort criteria evaluators. + /// indicates whether to sort ascending or descending for each field + /// is the window size + /// is the friend class handling the random access, if required byexpressions + /// for string value sorting using compare or Collator + /// The agent instance view factory context. + public RankWindowView(RankWindowViewFactory rankWindowViewFactory, + ExprNode[] uniqueCriteriaExpressions, + ExprEvaluator[] uniqueCriteriaEvaluators, + ExprNode[] sortCriteriaExpressions, + ExprEvaluator[] sortCriteriaEvaluators, + bool[] descendingValues, + int sortWindowSize, + IStreamSortRankRandomAccess optionalRankedRandomAccess, + bool isSortUsingCollator, + AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext) + { + _rankWindowViewFactory = rankWindowViewFactory; + _uniqueCriteriaExpressions = uniqueCriteriaExpressions; + _uniqueCriteriaEvaluators = uniqueCriteriaEvaluators; + _sortCriteriaExpressions = sortCriteriaExpressions; + _sortCriteriaEvaluators = sortCriteriaEvaluators; + _isDescendingValues = descendingValues; + _sortWindowSize = sortWindowSize; + _optionalRankedRandomAccess = optionalRankedRandomAccess; + _agentInstanceViewFactoryContext = agentInstanceViewFactoryContext; + + _comparator = CollectionUtil.GetComparator(sortCriteriaEvaluators, isSortUsingCollator, _isDescendingValues); + _sortedEvents = new OrderedDictionary(_comparator); + _uniqueKeySortKeys = new Dictionary(); + } + + public View CloneView() + { + return _rankWindowViewFactory.MakeView(_agentInstanceViewFactoryContext); + } + + public override EventType EventType + { + get + { + // The schema is the parent view's schema + return Parent.EventType; + } + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QViewProcessIRStream(this, _rankWindowViewFactory.ViewName, newData, oldData); } + + var removedEvents = new OneEventCollection(); + + // Remove old data + if (oldData != null) + { + for (var i = 0; i < oldData.Length; i++) + { + var uniqueKey = GetUniqueValues(oldData[i]); + var existingSortKey = _uniqueKeySortKeys.Get(uniqueKey); + + if (existingSortKey == null) + { + continue; + } + + var theEvent = RemoveFromSortedEvents(existingSortKey, uniqueKey); + if (theEvent != null) + { + _numberOfEvents--; + _uniqueKeySortKeys.Remove(uniqueKey); + removedEvents.Add(theEvent); + InternalHandleRemovedKey(existingSortKey, oldData[i]); + } + } + } + + // Add new data + if (newData != null) + { + for (var i = 0; i < newData.Length; i++) + { + var uniqueKey = GetUniqueValues(newData[i]); + var newSortKey = GetSortValues(newData[i]); + var existingSortKey = _uniqueKeySortKeys.Get(uniqueKey); + + // not currently found: its a new entry + if (existingSortKey == null) + { + CompareAndAddOrPassthru(newData[i], uniqueKey, newSortKey, removedEvents); + } + // same unique-key event found already, remove and add again + else + { + // key did not change, perform in-place substitute of event + if (existingSortKey.Equals(newSortKey)) + { + var replaced = InplaceReplaceSortedEvents(existingSortKey, uniqueKey, newData[i]); + if (replaced != null) + { + removedEvents.Add(replaced); + } + InternalHandleReplacedKey(newSortKey, newData[i], replaced); + } + else + { + var removed = RemoveFromSortedEvents(existingSortKey, uniqueKey); + if (removed != null) + { + _numberOfEvents--; + removedEvents.Add(removed); + InternalHandleRemovedKey(existingSortKey, removed); + } + CompareAndAddOrPassthru(newData[i], uniqueKey, newSortKey, removedEvents); + } + } + } + } + + // Remove data that sorts to the bottom of the window + if (_numberOfEvents > _sortWindowSize) + { + while (_numberOfEvents > _sortWindowSize) + { + var lastKey = _sortedEvents.Keys.Last(); + var existing = _sortedEvents.Get(lastKey); + if (existing is IList) + { + var existingList = (IList)existing; + while (_numberOfEvents > _sortWindowSize && !existingList.IsEmpty()) + { + var newestEvent = existingList.DeleteAt(0); + var uniqueKey = GetUniqueValues(newestEvent); + _uniqueKeySortKeys.Remove(uniqueKey); + _numberOfEvents--; + removedEvents.Add(newestEvent); + InternalHandleRemovedKey(existing, newestEvent); + } + if (existingList.IsEmpty()) + { + _sortedEvents.Remove(lastKey); + } + } + else + { + var lastSortedEvent = (EventBean)existing; + var uniqueKey = GetUniqueValues(lastSortedEvent); + _uniqueKeySortKeys.Remove(uniqueKey); + _numberOfEvents--; + removedEvents.Add(lastSortedEvent); + _sortedEvents.Remove(lastKey); + InternalHandleRemovedKey(lastKey, lastSortedEvent); + } + } + } + + // If there are child views, fireStatementStopped Update method + if (_optionalRankedRandomAccess != null) + { + _optionalRankedRandomAccess.Refresh(_sortedEvents, _numberOfEvents, _sortWindowSize); + } + if (HasViews) + { + EventBean[] expiredArr = null; + if (!removedEvents.IsEmpty()) + { + expiredArr = removedEvents.ToArray(); + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QViewIndicate(this, _rankWindowViewFactory.ViewName, newData, expiredArr); } + UpdateChildren(newData, expiredArr); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AViewIndicate(); } + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AViewProcessIRStream(); } + } + + public void InternalHandleReplacedKey(Object newSortKey, EventBean newEvent, EventBean oldEvent) + { + // no action + } + + public void InternalHandleRemovedKey(Object sortKey, EventBean eventBean) + { + // no action + } + + public void InternalHandleAddedKey(Object sortKey, EventBean eventBean) + { + // no action + } + + private void CompareAndAddOrPassthru(EventBean eventBean, Object uniqueKey, Object newSortKey, OneEventCollection removedEvents) + { + // determine full or not + if (_numberOfEvents >= _sortWindowSize) + { + var compared = _comparator.Compare(_sortedEvents.Keys.Last(), newSortKey); + + // this new event will fall outside of the ranks or coincides with the last entry, so its an old event already + if (compared < 0) + { + removedEvents.Add(eventBean); + } + // this new event is higher in sort key then the last entry so we are interested + else + { + _uniqueKeySortKeys.Put(uniqueKey, newSortKey); + _numberOfEvents++; + CollectionUtil.AddEventByKeyLazyListMapBack(newSortKey, eventBean, _sortedEvents); + InternalHandleAddedKey(newSortKey, eventBean); + } + } + // not yet filled, need to add + else + { + _uniqueKeySortKeys.Put(uniqueKey, newSortKey); + _numberOfEvents++; + CollectionUtil.AddEventByKeyLazyListMapBack(newSortKey, eventBean, _sortedEvents); + InternalHandleAddedKey(newSortKey, eventBean); + } + } + + private EventBean RemoveFromSortedEvents(Object sortKey, Object uniqueKeyToRemove) + { + var existing = _sortedEvents.Get(sortKey); + + EventBean removedOldEvent = null; + if (existing != null) + { + if (existing is IList) + { + var existingList = (IList)existing; + for (int ii = existingList.Count - 1; ii >= 0; ii--) + { + var eventForRank = existingList[ii]; + if (Equals(GetUniqueValues(eventForRank), uniqueKeyToRemove)) + { + existingList.RemoveAt(ii); + removedOldEvent = eventForRank; + break; + } + } + + if (existingList.IsEmpty()) + { + _sortedEvents.Remove(sortKey); + } + } + else + { + removedOldEvent = (EventBean)existing; + _sortedEvents.Remove(sortKey); + } + } + return removedOldEvent; + } + + private EventBean InplaceReplaceSortedEvents(Object sortKey, Object uniqueKeyToReplace, EventBean newData) + { + var existing = _sortedEvents.Get(sortKey); + + EventBean replaced = null; + if (existing != null) + { + if (existing is IList) + { + var existingList = (IList)existing; + for (int ii = existingList.Count - 1; ii >= 0; ii--) + { + var eventForRank = existingList[ii]; + if (Equals(GetUniqueValues(eventForRank), uniqueKeyToReplace)) + { + existingList.RemoveAt(ii); + replaced = eventForRank; + break; + } + } + existingList.Add(newData); // add to back as this is now the newest event + } + else + { + replaced = (EventBean)existing; + _sortedEvents.Put(sortKey, newData); + } + } + return replaced; + } + + public override IEnumerator GetEnumerator() + { + return new RankWindowEnumerator(_sortedEvents); + } + + public override String ToString() + { + return GetType().FullName + + " uniqueFieldName=" + _uniqueCriteriaExpressions.Render() + + " sortFieldName=" + _sortCriteriaExpressions.Render() + + " isDescending=" + _isDescendingValues.Render() + + " sortWindowSize=" + _sortWindowSize; + } + + public Object GetUniqueValues(EventBean theEvent) + { + return GetCriteriaKey(_eventsPerStream, _uniqueCriteriaEvaluators, theEvent, _agentInstanceViewFactoryContext); + } + + public Object GetSortValues(EventBean theEvent) + { + return GetCriteriaKey(_eventsPerStream, _sortCriteriaEvaluators, theEvent, _agentInstanceViewFactoryContext); + } + + public static Object GetCriteriaKey(EventBean[] eventsPerStream, ExprEvaluator[] evaluators, EventBean theEvent, ExprEvaluatorContext evalContext) + { + eventsPerStream[0] = theEvent; + if (evaluators.Length > 1) + { + return GetCriteriaMultiKey(eventsPerStream, evaluators, evalContext); + } + else + { + return evaluators[0].Evaluate(new EvaluateParams(eventsPerStream, true, evalContext)); + } + } + + public static MultiKeyUntyped GetCriteriaMultiKey(EventBean[] eventsPerStream, ExprEvaluator[] evaluators, ExprEvaluatorContext evalContext) + { + var result = new Object[evaluators.Length]; + var count = 0; + foreach (var expr in evaluators) + { + result[count++] = expr.Evaluate(new EvaluateParams(eventsPerStream, true, evalContext)); + } + return new MultiKeyUntyped(result); + } + + /// True to indicate the sort window is empty, or false if not empty. + /// true if empty sort window + public bool IsEmpty() + { + if (_sortedEvents == null) + { + return true; + } + return _sortedEvents.IsEmpty(); + } + + public void VisitView(ViewDataVisitor viewDataVisitor) + { + viewDataVisitor.VisitPrimary(_sortedEvents, false, _rankWindowViewFactory.ViewName, _numberOfEvents, _sortedEvents.Count); + } + + public ViewFactory ViewFactory + { + get { return _rankWindowViewFactory; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/ext/RankWindowViewFactory.cs b/NEsper.Core/NEsper.Core/view/ext/RankWindowViewFactory.cs new file mode 100755 index 000000000..fd041ed4c --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/ext/RankWindowViewFactory.cs @@ -0,0 +1,185 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.view.window; + +namespace com.espertech.esper.view.ext +{ + /// Factory for rank window views. + public class RankWindowViewFactory + : DataWindowViewFactory + , DataWindowViewWithPrevious + { + private const string NAME = "Rank"; + + /// The unique-by expressions. + private ExprNode[] _uniqueCriteriaExpressions; + /// The sort-by expressions. + private ExprNode[] _sortCriteriaExpressions; + /// The flags defining the ascending or descending sort order. + private bool[] _isDescendingValues; + private ExprEvaluator[] _uniqueEvals; + private ExprEvaluator[] _sortEvals; + /// The sort window size. + private ExprEvaluator _sizeEvaluator; + private bool _useCollatorSort; + private IList _viewParameters; + private EventType _eventType; + + public void SetViewParameters(ViewFactoryContext viewFactoryContext, IList viewParams) + { + _viewParameters = viewParams; + } + + public void Attach(EventType parentEventType, StatementContext statementContext, ViewFactory optionalParentFactory, IList parentViewFactories) + { + _eventType = parentEventType; + const string message = NAME + " view requires a list of expressions providing unique keys, a numeric size parameter and a list of expressions providing sort keys"; + if (_viewParameters.Count < 3) { + throw new ViewParameterException(message); + } + + // validate + ExprNode[] validated = ViewFactorySupport.Validate(NAME, parentEventType, statementContext, _viewParameters, true); + + // find size-parameter index + int indexNumericSize = -1; + for (int i = 0; i < validated.Length; i++) { + if (validated[i] is ExprConstantNode || validated[i] is ExprContextPropertyNode) { + indexNumericSize = i; + break; + } + } + if (indexNumericSize == -1) { + throw new ViewParameterException("Failed to find constant value for the numeric size parameter"); + } + if (indexNumericSize == 0) { + throw new ViewParameterException("Failed to find unique value expressions that are expected to occur before the numeric size parameter"); + } + if (indexNumericSize == validated.Length - 1) { + throw new ViewParameterException("Failed to find sort key expressions after the numeric size parameter"); + } + + // validate non-constant for unique-keys and sort-keys + for (int i = 0; i < indexNumericSize; i++) { + ViewFactorySupport.AssertReturnsNonConstant(NAME, validated[i], i); + } + for (int i = indexNumericSize + 1; i < validated.Length; i++) { + ViewFactorySupport.AssertReturnsNonConstant(NAME, validated[i], i); + } + + // get sort size + ViewFactorySupport.ValidateNoProperties(ViewName, validated[indexNumericSize], indexNumericSize); + _sizeEvaluator = ViewFactorySupport.ValidateSizeParam(ViewName, statementContext, validated[indexNumericSize], indexNumericSize); + + // compile unique expressions + _uniqueCriteriaExpressions = new ExprNode[indexNumericSize]; + Array.Copy(validated, 0, _uniqueCriteriaExpressions, 0, indexNumericSize); + + // compile sort expressions + _sortCriteriaExpressions = new ExprNode[validated.Length - indexNumericSize - 1]; + _isDescendingValues = new bool[_sortCriteriaExpressions.Length]; + + int count = 0; + for (int i = indexNumericSize + 1; i < validated.Length; i++) { + if (validated[i] is ExprOrderedExpr) { + _isDescendingValues[count] = ((ExprOrderedExpr) validated[i]).IsDescending; + _sortCriteriaExpressions[count] = validated[i].ChildNodes[0]; + } else { + _sortCriteriaExpressions[count] = validated[i]; + } + count++; + } + + _uniqueEvals = ExprNodeUtility.GetEvaluators(_uniqueCriteriaExpressions); + _sortEvals = ExprNodeUtility.GetEvaluators(_sortCriteriaExpressions); + + if (statementContext.ConfigSnapshot != null) { + _useCollatorSort = statementContext.ConfigSnapshot.EngineDefaults.Language.IsSortUsingCollator; + } + } + + public View MakeView(AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext) { + int sortWindowSize = ViewFactorySupport.EvaluateSizeParam(ViewName, _sizeEvaluator, agentInstanceViewFactoryContext.AgentInstanceContext); + IStreamSortRankRandomAccess rankedRandomAccess = agentInstanceViewFactoryContext.StatementContext.ViewServicePreviousFactory.GetOptPreviousExprSortedRankedAccess(agentInstanceViewFactoryContext); + return new RankWindowView(this, _uniqueCriteriaExpressions, _uniqueEvals, _sortCriteriaExpressions, _sortEvals, _isDescendingValues, sortWindowSize, rankedRandomAccess, _useCollatorSort, agentInstanceViewFactoryContext); + } + + public Object MakePreviousGetter() { + return new RandomAccessByIndexGetter(); + } + + public EventType EventType + { + get { return _eventType; } + } + + public bool CanReuse(View view, AgentInstanceContext agentInstanceContext) + { + if (!(view is SortWindowView)) { + return false; + } + + SortWindowView other = (SortWindowView) view; + int sortWindowSize = ViewFactorySupport.EvaluateSizeParam(ViewName, _sizeEvaluator, agentInstanceContext); + if ((other.SortWindowSize != sortWindowSize) || + (!Compare(other.IsDescendingValues, _isDescendingValues)) || + (!ExprNodeUtility.DeepEquals(other.SortCriteriaExpressions, _sortCriteriaExpressions))) { + return false; + } + + return other.IsEmpty(); + } + + private bool Compare(bool[] one, bool[] two) { + if (one.Length != two.Length) { + return false; + } + + for (int i = 0; i < one.Length; i++) { + if (one[i] != two[i]) { + return false; + } + } + + return true; + } + + public string ViewName + { + get { return NAME; } + } + + public bool[] IsDescendingValues + { + get { return _isDescendingValues; } + } + + public ExprEvaluator[] UniqueEvals + { + get { return _uniqueEvals; } + } + + public ExprEvaluator[] SortEvals + { + get { return _sortEvals; } + } + + public bool UseCollatorSort + { + get { return _useCollatorSort; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/view/ext/SortWindowEnumerator.cs b/NEsper.Core/NEsper.Core/view/ext/SortWindowEnumerator.cs new file mode 100755 index 000000000..e4e34317e --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/ext/SortWindowEnumerator.cs @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.view.ext +{ + public class SortWindowEnumerator : MixedEventBeanAndCollectionEnumeratorBase + { + private readonly IDictionary _window; + + public SortWindowEnumerator(IDictionary window) + : base(window.Keys.GetEnumerator()) + { + _window = window; + } + + protected override Object GetValue(Object iteratorKeyValue) + { + return _window.Get(iteratorKeyValue); + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/ext/SortWindowView.cs b/NEsper.Core/NEsper.Core/view/ext/SortWindowView.cs new file mode 100755 index 000000000..2caba6056 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/ext/SortWindowView.cs @@ -0,0 +1,282 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.util; + +namespace com.espertech.esper.view.ext +{ + /// + /// Window sorting by values in the specified field extending a specified number of + /// elements from the lowest value up or the highest value down. The view accepts 3 + /// parameters. The first parameter is the field name to get the values to sort for, + /// the second parameter defines whether to sort ascending or descending, the third + /// parameter is the number of elements to keep in the sorted list. The type of the + /// field to be sorted in the event must implement the Comparable interface. The natural + /// order in which events arrived is used as the second sorting criteria. Thus should + /// events arrive with equal sort values the oldest event leaves the sort window first. + /// Old values removed from a prior view are removed from the sort view. + /// + public class SortWindowView : ViewSupport, DataWindowView, CloneableView + { + private readonly SortWindowViewFactory _sortWindowViewFactory; + private readonly ExprEvaluator[] _sortCriteriaEvaluators; + private readonly ExprNode[] _sortCriteriaExpressions; + private readonly EventBean[] _eventsPerStream = new EventBean[1]; + private readonly bool[] _isDescendingValues; + private readonly int _sortWindowSize; + private readonly IStreamSortRankRandomAccess _optionalSortedRandomAccess; + protected readonly AgentInstanceViewFactoryChainContext AgentInstanceViewFactoryContext; + + private readonly OrderedDictionary _sortedEvents; + private int _eventCount; + + /// + /// Ctor. + /// + /// for copying this view in a group-by + /// is the event property names to sort + /// The sort criteria evaluators. + /// indicates whether to sort ascending or descending for each field + /// is the window size + /// is the friend class handling the random access, if required byexpressions + /// for string value sorting using compare or Collator + /// The agent instance view factory context. + public SortWindowView(SortWindowViewFactory sortWindowViewFactory, + ExprNode[] sortCriteriaExpressions, + ExprEvaluator[] sortCriteriaEvaluators, + bool[] descendingValues, + int sortWindowSize, + IStreamSortRankRandomAccess optionalSortedRandomAccess, + bool isSortUsingCollator, + AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext) + { + _sortWindowViewFactory = sortWindowViewFactory; + _sortCriteriaExpressions = sortCriteriaExpressions; + _sortCriteriaEvaluators = sortCriteriaEvaluators; + _isDescendingValues = descendingValues; + _sortWindowSize = sortWindowSize; + _optionalSortedRandomAccess = optionalSortedRandomAccess; + AgentInstanceViewFactoryContext = agentInstanceViewFactoryContext; + + var comparator = CollectionUtil.GetComparator(sortCriteriaEvaluators, isSortUsingCollator, _isDescendingValues); + _sortedEvents = new OrderedDictionary(comparator); + } + + /// Returns the field names supplying the values to sort by. + /// field names to sort by + protected internal ExprNode[] SortCriteriaExpressions + { + get { return _sortCriteriaExpressions; } + } + + /// Returns the flags indicating whether to sort in descending order on each property. + /// the isDescending value for each sort property + protected internal bool[] IsDescendingValues + { + get { return _isDescendingValues; } + } + + /// Returns the number of elements kept by the sort window. + /// size of window + protected internal int SortWindowSize + { + get { return _sortWindowSize; } + } + + public View CloneView() + { + return _sortWindowViewFactory.MakeView(AgentInstanceViewFactoryContext); + } + + public override EventType EventType + { + get + { + // The schema is the parent view's schema + return Parent.EventType; + } + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QViewProcessIRStream(this, _sortWindowViewFactory.ViewName, newData, oldData); } + + OneEventCollection removedEvents = null; + + // Remove old data + if (oldData != null) + { + for (var i = 0; i < oldData.Length; i++) + { + var oldDataItem = oldData[i]; + var sortValues = GetSortValues(oldDataItem); + var result = CollectionUtil.RemoveEventByKeyLazyListMap(sortValues, oldDataItem, _sortedEvents); + if (result) + { + _eventCount--; + if (removedEvents == null) + { + removedEvents = new OneEventCollection(); + } + removedEvents.Add(oldDataItem); + InternalHandleRemoved(sortValues, oldDataItem); + } + } + } + + // Add new data + if (newData != null) + { + for (var i = 0; i < newData.Length; i++) + { + var newDataItem = newData[i]; + var sortValues = GetSortValues(newDataItem); + CollectionUtil.AddEventByKeyLazyListMapFront(sortValues, newDataItem, _sortedEvents); + _eventCount++; + InternalHandleAdd(sortValues, newDataItem); + } + } + + // Remove data that sorts to the bottom of the window + if (_eventCount > _sortWindowSize) + { + var removeCount = _eventCount - _sortWindowSize; + for (var i = 0; i < removeCount; i++) + { + // Remove the last element of the last key - sort order is key and then natural order of arrival + var lastKey = _sortedEvents.Keys.Last(); + var lastEntry = _sortedEvents.Get(lastKey); + if (lastEntry is IList) + { + var events = (IList)lastEntry; + var theEvent = events.DeleteAt(events.Count - 1); // remove oldest event, newest events are first in list + _eventCount--; + if (events.IsEmpty()) + { + _sortedEvents.Remove(lastKey); + } + if (removedEvents == null) + { + removedEvents = new OneEventCollection(); + } + removedEvents.Add(theEvent); + InternalHandleRemoved(lastKey, theEvent); + } + else + { + var theEvent = (EventBean)lastEntry; + _eventCount--; + _sortedEvents.Remove(lastKey); + if (removedEvents == null) + { + removedEvents = new OneEventCollection(); + } + removedEvents.Add(theEvent); + InternalHandleRemoved(lastKey, theEvent); + } + } + } + + // If there are child views, fireStatementStopped Update method + if (_optionalSortedRandomAccess != null) + { + _optionalSortedRandomAccess.Refresh(_sortedEvents, _eventCount, _sortWindowSize); + } + + if (HasViews) + { + EventBean[] expiredArr = null; + if (removedEvents != null) + { + expiredArr = removedEvents.ToArray(); + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QViewIndicate(this, _sortWindowViewFactory.ViewName, newData, expiredArr); } + UpdateChildren(newData, expiredArr); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AViewIndicate(); } + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AViewProcessIRStream(); } + } + + public void InternalHandleAdd(Object sortValues, EventBean newDataItem) + { + // no action required + } + + public void InternalHandleRemoved(Object sortValues, EventBean oldDataItem) + { + // no action required + } + + public override IEnumerator GetEnumerator() + { + return new SortWindowEnumerator(_sortedEvents); + } + + public override String ToString() + { + return GetType().FullName + + " sortFieldName=" + CompatExtensions.Render(_sortCriteriaExpressions) + + " isDescending=" + CompatExtensions.Render(_isDescendingValues) + + " sortWindowSize=" + _sortWindowSize; + } + + protected Object GetSortValues(EventBean theEvent) + { + var evaluateParams = new EvaluateParams(_eventsPerStream, true, AgentInstanceViewFactoryContext); + + _eventsPerStream[0] = theEvent; + if (_sortCriteriaExpressions.Length == 1) + { + return _sortCriteriaEvaluators[0].Evaluate(evaluateParams); + } + + var result = new Object[_sortCriteriaExpressions.Length]; + var count = 0; + foreach (var expr in _sortCriteriaEvaluators) + { + result[count++] = expr.Evaluate(evaluateParams); + } + return new MultiKeyUntyped(result); + } + + /// + /// True to indicate the sort window is empty, or false if not empty. + /// + /// true if empty sort window + public bool IsEmpty() + { + if (_sortedEvents == null) + { + return true; + } + return _sortedEvents.IsEmpty(); + } + + public void VisitView(ViewDataVisitor viewDataVisitor) + { + viewDataVisitor.VisitPrimary(_sortedEvents, false, _sortWindowViewFactory.ViewName, _eventCount, null); + } + + public ViewFactory ViewFactory + { + get { return _sortWindowViewFactory; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/ext/SortWindowViewFactory.cs b/NEsper.Core/NEsper.Core/view/ext/SortWindowViewFactory.cs new file mode 100755 index 000000000..f166f3d61 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/ext/SortWindowViewFactory.cs @@ -0,0 +1,173 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.view.window; + +namespace com.espertech.esper.view.ext +{ + /// Factory for sort window views. + public class SortWindowViewFactory + : DataWindowViewFactory, + DataWindowViewWithPrevious + { + private const string NAME = "Sort"; + + /// The sort-by expressions. + private ExprNode[] _sortCriteriaExpressions; + + private ExprEvaluator[] _sortCriteriaEvaluators; + + /// The flags defining the ascending or descending sort order. + private bool[] _isDescendingValues; + + /// The sort window size. + private ExprEvaluator _sizeEvaluator; + + private IList _viewParameters; + private EventType _eventType; + private bool _useCollatorSort = false; + + public void SetViewParameters(ViewFactoryContext viewFactoryContext, IList viewParams) + { + _viewParameters = viewParams; + } + + public void Attach( + EventType parentEventType, + StatementContext statementContext, + ViewFactory optionalParentFactory, + IList parentViewFactories) + { + _eventType = parentEventType; + const string message = + NAME + " window requires a numeric size parameter and a list of expressions providing sort keys"; + if (_viewParameters.Count < 2) + { + throw new ViewParameterException(message); + } + + var validated = ViewFactorySupport.Validate( + NAME + " window", parentEventType, statementContext, _viewParameters, true); + for (var i = 1; i < validated.Length; i++) + { + ViewFactorySupport.AssertReturnsNonConstant(NAME + " window", validated[i], i); + } + + ViewFactorySupport.ValidateNoProperties(ViewName, validated[0], 0); + _sizeEvaluator = ViewFactorySupport.ValidateSizeParam(ViewName, statementContext, validated[0], 0); + + _sortCriteriaExpressions = new ExprNode[validated.Length - 1]; + _isDescendingValues = new bool[_sortCriteriaExpressions.Length]; + + for (var i = 1; i < validated.Length; i++) + { + if (validated[i] is ExprOrderedExpr) + { + _isDescendingValues[i - 1] = ((ExprOrderedExpr) validated[i]).IsDescending; + _sortCriteriaExpressions[i - 1] = validated[i].ChildNodes[0]; + } + else + { + _sortCriteriaExpressions[i - 1] = validated[i]; + } + } + _sortCriteriaEvaluators = ExprNodeUtility.GetEvaluators(_sortCriteriaExpressions); + + if (statementContext.ConfigSnapshot != null) + { + _useCollatorSort = statementContext.ConfigSnapshot.EngineDefaults.Language.IsSortUsingCollator; + } + } + + public View MakeView(AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext) + { + var sortWindowSize = ViewFactorySupport.EvaluateSizeParam( + ViewName, _sizeEvaluator, agentInstanceViewFactoryContext.AgentInstanceContext); + var sortedRandomAccess = + agentInstanceViewFactoryContext.StatementContext.ViewServicePreviousFactory + .GetOptPreviousExprSortedRankedAccess(agentInstanceViewFactoryContext); + return new SortWindowView( + this, _sortCriteriaExpressions, _sortCriteriaEvaluators, _isDescendingValues, sortWindowSize, + sortedRandomAccess, _useCollatorSort, agentInstanceViewFactoryContext); + } + + public Object MakePreviousGetter() + { + return new RandomAccessByIndexGetter(); + } + + public EventType EventType + { + get { return _eventType; } + } + + public bool CanReuse(View view, AgentInstanceContext agentInstanceContext) + { + if (!(view is SortWindowView)) + { + return false; + } + + var other = (SortWindowView) view; + var sortWindowSize = ViewFactorySupport.EvaluateSizeParam(ViewName, _sizeEvaluator, agentInstanceContext); + if ((other.SortWindowSize != sortWindowSize) || + (!Compare(other.IsDescendingValues, _isDescendingValues)) || + (!ExprNodeUtility.DeepEquals(other.SortCriteriaExpressions, _sortCriteriaExpressions))) + { + return false; + } + + return other.IsEmpty(); + } + + public string ViewName + { + get { return NAME; } + } + + public ExprEvaluator[] SortCriteriaEvaluators + { + get { return _sortCriteriaEvaluators; } + } + + public bool[] IsDescendingValues + { + get { return _isDescendingValues; } + } + + public bool UseCollatorSort + { + get { return _useCollatorSort; } + } + + private bool Compare(bool[] one, bool[] two) + { + if (one.Length != two.Length) + { + return false; + } + + for (var i = 0; i < one.Length; i++) + { + if (one[i] != two[i]) + { + return false; + } + } + + return true; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/view/ext/TimeOrderView.cs b/NEsper.Core/NEsper.Core/view/ext/TimeOrderView.cs new file mode 100755 index 000000000..71ca0b50f --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/ext/TimeOrderView.cs @@ -0,0 +1,412 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.time; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.schedule; +using com.espertech.esper.util; + +namespace com.espertech.esper.view.ext +{ + /// + /// Window retaining timestamped events up to a given number of seconds such that older events + /// that arrive later are sorted into the window and released in timestamp order. + /// + /// The insert stream consists of all arriving events. The remove stream consists of events in + /// order of timestamp value as supplied by each event. Timestamp values on events should + /// match engine time. The window compares engine time to timestamp value and releases events + /// when the event's timestamp is less then engine time minus interval size (the event is older + /// then the window tail). + /// + /// The view accepts 2 parameters. The first parameter is the field name to get the event timestamp + /// value from, the second parameter defines the interval size. + /// + public class TimeOrderView + : ViewSupport + , DataWindowView + , CloneableView + , StoppableView + , StopCallback + { + private readonly AgentInstanceViewFactoryChainContext _agentInstanceContext; + private readonly TimeOrderViewFactory _timeOrderViewFactory; + private readonly ExprNode _timestampExpression; + private readonly ExprEvaluator _timestampEvaluator; + private readonly ExprTimePeriodEvalDeltaConst _timeDeltaComputation; + private readonly IStreamSortRankRandomAccess _optionalSortedRandomAccess; + private readonly long _scheduleSlot; + private readonly EPStatementHandleCallback _handle; + + private readonly EventBean[] _eventsPerStream = new EventBean[1]; + private readonly OrderedDictionary _sortedEvents; + private bool _isCallbackScheduled; + private int _eventCount; + + /// + /// Ctor. + /// + /// The agent instance context. + /// for copying this view in a group-by + /// the property name of the event supplying timestamp values + /// The timestamp evaluator. + /// The time delta computation. + /// is the friend class handling the random access, if required byexpressions + public TimeOrderView( + AgentInstanceViewFactoryChainContext agentInstanceContext, + TimeOrderViewFactory timeOrderViewFactory, + ExprNode timestampExpr, + ExprEvaluator timestampEvaluator, + ExprTimePeriodEvalDeltaConst timeDeltaComputation, + IStreamSortRankRandomAccess optionalSortedRandomAccess) + { + _agentInstanceContext = agentInstanceContext; + _timeOrderViewFactory = timeOrderViewFactory; + _timestampExpression = timestampExpr; + _timestampEvaluator = timestampEvaluator; + _timeDeltaComputation = timeDeltaComputation; + _optionalSortedRandomAccess = optionalSortedRandomAccess; + _scheduleSlot = agentInstanceContext.StatementContext.ScheduleBucket.AllocateSlot(); + + _sortedEvents = new OrderedDictionary(); + + ScheduleHandleCallback callback = new ProxyScheduleHandleCallback + { + ProcScheduledTrigger = extensionServicesContext => Instrument.With( + i => i.QViewScheduledEval(this, _timeOrderViewFactory.ViewName), + i => i.AViewScheduledEval(), + Expire) + }; + + _handle = new EPStatementHandleCallback(agentInstanceContext.EpStatementAgentInstanceHandle, callback); + agentInstanceContext.AddTerminationCallback(Stop); + } + + /// Returns the timestamp property name. + /// property name supplying timestamp values + public ExprNode TimestampExpression + { + get { return _timestampExpression; } + } + + /// Returns the time interval size. + /// interval size + public ExprTimePeriodEvalDeltaConst TimeDeltaComputation + { + get { return _timeDeltaComputation; } + } + + public View CloneView() + { + return _timeOrderViewFactory.MakeView(_agentInstanceContext); + } + + public override EventType EventType + { + get + { + // The schema is the parent view's schema + return Parent.EventType; + } + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + using (Instrument.With( + i => i.QViewProcessIRStream(this, _timeOrderViewFactory.ViewName, newData, oldData), + i => i.AViewProcessIRStream())) + { + EventBean[] postOldEventsArray = null; + + // Remove old data + if (oldData != null) + { + for (int i = 0; i < oldData.Length; i++) + { + var oldDataItem = oldData[i]; + var sortValues = GetTimestamp(oldDataItem); + var result = CollectionUtil.RemoveEventByKeyLazyListMap(sortValues, oldDataItem, _sortedEvents); + if (result) + { + _eventCount--; + if (postOldEventsArray == null) + { + postOldEventsArray = oldData; + } + else + { + postOldEventsArray = CollectionUtil.AddArrayWithSetSemantics( + postOldEventsArray, oldData); + } + InternalHandleRemoved(sortValues, oldDataItem); + } + } + } + + if ((newData != null) && (newData.Length > 0)) + { + // figure out the current tail time + long engineTime = _agentInstanceContext.StatementContext.SchedulingService.Time; + long windowTailTime = engineTime - _timeDeltaComputation.DeltaAdd(engineTime) + 1; + long oldestEvent = long.MaxValue; + if (_sortedEvents.IsNotEmpty()) + { + oldestEvent = (long)_sortedEvents.Keys.First(); + } + bool addedOlderEvent = false; + + // add events or post events as remove stream if already older then tail time + List postOldEvents = null; + for (int i = 0; i < newData.Length; i++) + { + // get timestamp of event + var newEvent = newData[i]; + var timestamp = GetTimestamp(newEvent); + + // if the event timestamp indicates its older then the tail of the window, release it + if (timestamp < windowTailTime) + { + if (postOldEvents == null) + { + postOldEvents = new List(2); + } + postOldEvents.Add(newEvent); + } + else + { + if (timestamp < oldestEvent) + { + addedOlderEvent = true; + oldestEvent = timestamp.Value; + } + + // add to list + CollectionUtil.AddEventByKeyLazyListMapBack(timestamp, newEvent, _sortedEvents); + _eventCount++; + InternalHandleAdd(timestamp, newEvent); + } + } + + // If we do have data, check the callback + if (_sortedEvents.IsNotEmpty()) + { + // If we haven't scheduled a callback yet, schedule it now + if (!_isCallbackScheduled) + { + long callbackWait = oldestEvent - windowTailTime + 1; + _agentInstanceContext.StatementContext.SchedulingService.Add( + callbackWait, _handle, _scheduleSlot); + _isCallbackScheduled = true; + } + else + { + // We may need to reschedule, and older event may have been added + if (addedOlderEvent) + { + oldestEvent = (long)_sortedEvents.Keys.First(); + long callbackWait = oldestEvent - windowTailTime + 1; + _agentInstanceContext.StatementContext.SchedulingService.Remove(_handle, _scheduleSlot); + _agentInstanceContext.StatementContext.SchedulingService.Add( + callbackWait, _handle, _scheduleSlot); + _isCallbackScheduled = true; + } + } + } + + if (postOldEvents != null) + { + postOldEventsArray = postOldEvents.ToArray(); + } + + if (_optionalSortedRandomAccess != null) + { + _optionalSortedRandomAccess.Refresh(_sortedEvents, _eventCount, _eventCount); + } + } + + // Update child views + if (HasViews) + { + Instrument.With( + i => i.QViewIndicate(this, _timeOrderViewFactory.ViewName, newData, postOldEventsArray), + i => i.AViewIndicate(), + () => UpdateChildren(newData, postOldEventsArray)); + } + } + } + + public void InternalHandleAdd(Object sortValues, EventBean newDataItem) + { + // no action required + } + + public void InternalHandleRemoved(Object sortValues, EventBean oldDataItem) + { + // no action required + } + + public void InternalHandleExpired(Object sortValues, EventBean oldDataItem) + { + // no action required + } + + public void InternalHandleExpired(Object sortValues, IList oldDataItems) + { + // no action required + } + + protected long? GetTimestamp(EventBean newEvent) + { + _eventsPerStream[0] = newEvent; + return + (long?)_timestampEvaluator.Evaluate(new EvaluateParams(_eventsPerStream, true, _agentInstanceContext)); + } + + /// True to indicate the sort window is empty, or false if not empty. + /// true if empty sort window + public bool IsEmpty() + { + return _sortedEvents.IsEmpty(); + } + + public override IEnumerator GetEnumerator() + { + return new SortWindowEnumerator(_sortedEvents); + } + + public override String ToString() + { + return GetType().FullName + + " timestampExpression=" + _timestampExpression; + } + + public void VisitView(ViewDataVisitor viewDataVisitor) + { + viewDataVisitor.VisitPrimary(_sortedEvents, false, _timeOrderViewFactory.ViewName, _eventCount, null); + } + + /// + /// This method removes (expires) objects from the window and schedules a new callback for the + /// time when the next oldest message would expire from the window. + /// + protected void Expire() + { + long currentTime = _agentInstanceContext.StatementContext.SchedulingService.Time; + long expireBeforeTimestamp = currentTime - _timeDeltaComputation.DeltaSubtract(currentTime) + 1; + _isCallbackScheduled = false; + + IList releaseEvents = null; + long? oldestKey; + while (true) + { + if (_sortedEvents.IsEmpty()) + { + oldestKey = null; + break; + } + + oldestKey = (long)_sortedEvents.Keys.First(); + if (oldestKey >= expireBeforeTimestamp) + { + break; + } + + var released = _sortedEvents.Delete(oldestKey); + if (released != null) + { + if (released is IList) + { + var releasedEventList = (IList)released; + if (releaseEvents == null) + { + releaseEvents = releasedEventList; + } + else + { + releaseEvents.AddAll(releasedEventList); + } + _eventCount -= releasedEventList.Count; + InternalHandleExpired(oldestKey, releasedEventList); + } + else + { + var releasedEvent = (EventBean)released; + if (releaseEvents == null) + { + releaseEvents = new List(4); + } + releaseEvents.Add(releasedEvent); + _eventCount--; + InternalHandleExpired(oldestKey, releasedEvent); + } + } + } + + if (_optionalSortedRandomAccess != null) + { + _optionalSortedRandomAccess.Refresh(_sortedEvents, _eventCount, _eventCount); + } + + // If there are child views, do the Update method + if (HasViews) + { + if ((releaseEvents != null) && (releaseEvents.IsNotEmpty())) + { + EventBean[] oldEvents = releaseEvents.ToArray(); + Instrument.With( + i => i.QViewIndicate(this, _timeOrderViewFactory.ViewName, null, oldEvents), + i => i.AViewIndicate(), + () => UpdateChildren(null, oldEvents)); + } + } + + // If we still have events in the window, schedule new callback + if (oldestKey == null) + { + return; + } + + // Next callback + long callbackWait = oldestKey.Value - expireBeforeTimestamp + 1; + _agentInstanceContext.StatementContext.SchedulingService.Add(callbackWait, _handle, _scheduleSlot); + _isCallbackScheduled = true; + } + + public void StopView() + { + StopSchedule(); + _agentInstanceContext.RemoveTerminationCallback(Stop); + } + + public void Stop() + { + StopSchedule(); + } + + public void StopSchedule() + { + if (_handle != null) + { + _agentInstanceContext.StatementContext.SchedulingService.Remove(_handle, _scheduleSlot); + } + } + + public ViewFactory ViewFactory + { + get { return _timeOrderViewFactory; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/ext/TimeOrderViewFactory.cs b/NEsper.Core/NEsper.Core/view/ext/TimeOrderViewFactory.cs new file mode 100755 index 000000000..bf3c28b40 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/ext/TimeOrderViewFactory.cs @@ -0,0 +1,128 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.time; +using com.espertech.esper.util; +using com.espertech.esper.view.window; + +namespace com.espertech.esper.view.ext +{ + /// Factory for views for time-ordering events. + public class TimeOrderViewFactory + : DataWindowViewFactory + , DataWindowViewWithPrevious + { + /// The timestamp expression. + private ExprNode _timestampExpression; + + private ExprEvaluator _timestampExpressionEvaluator; + + /// The interval to wait for newer events to arrive. + private ExprTimePeriodEvalDeltaConstFactory _timeDeltaComputationFactory; + + private IList _viewParameters; + private EventType _eventType; + + public void SetViewParameters(ViewFactoryContext viewFactoryContext, IList expressionParameters) + { + _viewParameters = expressionParameters; + } + + public void Attach( + EventType parentEventType, + StatementContext statementContext, + ViewFactory optionalParentFactory, + IList parentViewFactories) + { + var validated = ViewFactorySupport.Validate( + ViewName, parentEventType, statementContext, _viewParameters, true); + + if (_viewParameters.Count != 2) + { + throw new ViewParameterException(ViewParamMessage); + } + + if (!validated[0].ExprEvaluator.ReturnType.IsNumeric()) + { + throw new ViewParameterException(ViewParamMessage); + } + _timestampExpression = validated[0]; + _timeDeltaComputationFactory = ViewFactoryTimePeriodHelper.ValidateAndEvaluateTimeDeltaFactory( + ViewName, statementContext, _viewParameters[1], ViewParamMessage, 1); + _timestampExpressionEvaluator = _timestampExpression.ExprEvaluator; + _eventType = parentEventType; + } + + public Object MakePreviousGetter() + { + return new RandomAccessByIndexGetter(); + } + + public View MakeView(AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext) + { + var timeDeltaComputation = _timeDeltaComputationFactory.Make( + ViewName, "view", agentInstanceViewFactoryContext.AgentInstanceContext); + var sortedRandomAccess = + agentInstanceViewFactoryContext.StatementContext.ViewServicePreviousFactory + .GetOptPreviousExprSortedRankedAccess(agentInstanceViewFactoryContext); + return new TimeOrderView( + agentInstanceViewFactoryContext, this, _timestampExpression, _timestampExpression.ExprEvaluator, + timeDeltaComputation, sortedRandomAccess); + } + + public EventType EventType + { + get { return _eventType; } + } + + public bool CanReuse(View view, AgentInstanceContext agentInstanceContext) + { + if (!(view is TimeOrderView)) + { + return false; + } + + var other = (TimeOrderView) view; + var timeDeltaComputation = _timeDeltaComputationFactory.Make( + ViewName, "view", agentInstanceContext); + if ((!timeDeltaComputation.EqualsTimePeriod(other.TimeDeltaComputation)) || + (!ExprNodeUtility.DeepEquals(other.TimestampExpression, _timestampExpression))) + { + return false; + } + + return other.IsEmpty(); + } + + public string ViewName + { + get { return "Time-Order"; } + } + + public ExprEvaluator TimestampExpressionEvaluator + { + get { return _timestampExpressionEvaluator; } + } + + private string ViewParamMessage + { + get + { + return ViewName + + " view requires the expression supplying timestamp values, and a numeric or time period parameter for interval size"; + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/view/internals/BufferObserver.cs b/NEsper.Core/NEsper.Core/view/internals/BufferObserver.cs new file mode 100755 index 000000000..13b32651b --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/internals/BufferObserver.cs @@ -0,0 +1,58 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using com.espertech.esper.collection; + +namespace com.espertech.esper.view.internals +{ + /// + /// Observer interface to a stream publishing new and old events. + /// + public interface BufferObserver + { + /// + /// Receive new and old events from a stream. + /// + /// the stream number sending the events + /// buffer for new events + /// buffer for old events + void NewData(int streamId, FlushedEventBuffer newEventBuffer, FlushedEventBuffer oldEventBuffer); + } + + public delegate void BufferObserverDelegate(int streamId, FlushedEventBuffer newEventBuffer, FlushedEventBuffer oldEventBuffer); + + public class ProxyBufferObserver : BufferObserver + { + private readonly BufferObserverDelegate m_delegate; + + /// + /// Initializes a new instance of the class. + /// + /// The d. + public ProxyBufferObserver(BufferObserverDelegate d) + { + m_delegate = d; + } + + #region BufferObserver Members + + /// + /// Receive new and old events from a stream. + /// + /// the stream number sending the events + /// buffer for new events + /// buffer for old events + public void NewData(int streamId, FlushedEventBuffer newEventBuffer, FlushedEventBuffer oldEventBuffer) + { + m_delegate(streamId, newEventBuffer, oldEventBuffer); + } + + #endregion + } +} diff --git a/NEsper.Core/NEsper.Core/view/internals/BufferView.cs b/NEsper.Core/NEsper.Core/view/internals/BufferView.cs new file mode 100755 index 000000000..5236341c1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/internals/BufferView.cs @@ -0,0 +1,105 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; + +namespace com.espertech.esper.view.internals +{ + /// + /// A view that acts as an adapter between views and Update listeners. + /// The view can be added to a parent view. When the parent view publishes data, the view will forward the + /// data to the UpdateListener implementation that has been supplied. If no UpdateListener has been supplied, + /// then the view will cache the last data published by the parent view. + /// + + public sealed class BufferView : ViewSupport + { + /// Set the observer for indicating new and old data. + + public BufferObserver Observer + { + set { _observer = value; } + } + + private readonly int _streamId; + + private BufferObserver _observer; + private readonly FlushedEventBuffer _newDataBuffer = new FlushedEventBuffer(); + private readonly FlushedEventBuffer _oldDataBuffer = new FlushedEventBuffer(); + + /// Ctor. + /// number of the stream for which the view buffers the generated events. + /// + + public BufferView(int streamId) + { + _streamId = streamId; + } + + /// + /// Provides metadata information about the type of object the event collection contains. + /// + /// + /// + /// metadata for the objects in the collection + /// + public override EventType EventType + { + get { return Parent.EventType; } + } + + /// + /// Returns an enumerator that iterates through the collection. + /// + /// + /// A that can be used to iterate through the collection. + /// + public override IEnumerator GetEnumerator() + { + return Parent.GetEnumerator(); + } + + /// + /// Notify that data has been added or removed from the Viewable parent. + /// The last object in the newData array of objects would be the newest object added to the parent view. + /// The first object of the oldData array of objects would be the oldest object removed from the parent view. + /// + /// If the call to Update contains new (inserted) data, then the first argument will be a non-empty list and the + /// second will be empty. Similarly, if the call is a notification of deleted data, then the first argument will be + /// empty and the second will be non-empty. Either the newData or oldData will be non-null. + /// This method won't be called with both arguments being null, but either one could be null. + /// The same is true for zero-length arrays. Either newData or oldData will be non-empty. + /// If both are non-empty, then the Update is a modification notification. + /// + /// + /// When Update() is called on a view by the parent object, the data in newData will be in the collection of the + /// parent, and its data structures will be arranged to reflect that. + /// The data in oldData will not be in the parent's data structures, and any access to the parent will indicate that + /// that data is no longer there. + /// + /// + /// is the new data that has been added to the parent view + /// is the old data that has been removed from the parent view + public override void Update(EventBean[] newData, EventBean[] oldData) + { + _newDataBuffer.Add(newData); + _oldDataBuffer.Add(oldData); + _observer.NewData(_streamId, _newDataBuffer, _oldDataBuffer); + } + + /// Returns the buffer for new data. + /// new data buffer + public FlushedEventBuffer NewDataBuffer + { + get { return _newDataBuffer; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/internals/IntersectAsymetricView.cs b/NEsper.Core/NEsper.Core/view/internals/IntersectAsymetricView.cs new file mode 100755 index 000000000..920e4ef12 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/internals/IntersectAsymetricView.cs @@ -0,0 +1,308 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; + +namespace com.espertech.esper.view.internals +{ + /// + /// A view that represents an intersection of multiple data windows. + /// The view is parameterized by two or more data windows. From an external viewpoint, the + /// view retains all events that is in all of the data windows at the same time (an intersection) + /// and removes all events that leave any of the data windows. + /// + public class IntersectAsymetricView + : ViewSupport + , LastPostObserver + , CloneableView + , StoppableView + , DataWindowView + , IntersectViewMarker + , ViewDataVisitableContainer + , ViewContainer + { + private readonly AgentInstanceViewFactoryChainContext _agentInstanceViewFactoryContext; + private readonly IntersectViewFactory _factory; + private readonly View[] _views; + + /// + /// Ctor. + /// + /// The agent instance view factory context. + /// the view factory + /// the list of data window views + public IntersectAsymetricView( + AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext, + IntersectViewFactory factory, + IList viewList) + { + _agentInstanceViewFactoryContext = agentInstanceViewFactoryContext; + _factory = factory; + _views = viewList.ToArray(); + + for (int i = 0; i < viewList.Count; i++) + { + var view = new LastPostObserverView(i); + _views[i].RemoveAllViews(); + _views[i].AddView(view); + view.Observer = this; + } + } + + public View[] ViewContained + { + get { return _views; } + } + + public View CloneView() + { + return _factory.MakeView(_agentInstanceViewFactoryContext); + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + IntersectAsymetricViewLocalState localState = _factory.GetAsymetricViewLocalStatePerThread(); + + localState.OldEvents.Clear(); + EventBean[] newDataPosted = null; + + // handle remove stream + if (oldData != null) + { + localState.IsDiscardObserverEvents = true; // disable reaction logic in observer + try + { + foreach (View view in _views) + { + view.Update(null, oldData); + } + } + finally + { + localState.IsDiscardObserverEvents = false; + } + + for (int i = 0; i < oldData.Length; i++) + { + localState.OldEvents.Add(oldData[i]); + } + } + + if (newData != null) + { + localState.RemovalEvents.Clear(); + + // new events must go to all views + // old events, such as when removing from a named window, get removed from all views + localState.HasRemovestreamData = false; // changed by observer logic to indicate new data + localState.IsRetainObserverEvents = true; // enable retain logic in observer + try + { + foreach (View view in _views) + { + localState.NewDataChildView = null; + view.Update(newData, oldData); + + // first-X asymetric view post no insert stream for events that get dropped, remove these + if (localState.NewDataChildView != null) + { + for (int i = 0; i < newData.Length; i++) + { + bool found = false; + for (int j = 0; j < localState.NewDataChildView.Length; j++) + { + if (localState.NewDataChildView[i] == newData[i]) + { + found = true; + break; + } + } + if (!found) + { + localState.RemovalEvents.Add(newData[i]); + } + } + } + else + { + for (int i = 0; i < newData.Length; i++) + { + localState.RemovalEvents.Add(newData[i]); + } + } + } + } + finally + { + localState.IsRetainObserverEvents = false; + } + + if (!localState.RemovalEvents.IsEmpty()) + { + localState.IsDiscardObserverEvents = true; + EventBean[] viewOldData = localState.RemovalEvents.ToArray(); + try + { + for (int j = 0; j < _views.Length; j++) + { + _views[j].Update(null, viewOldData); + } + } + finally + { + localState.IsDiscardObserverEvents = false; + } + } + + // see if any child view has removed any events. + // if there was an insert stream, handle pushed-out events + if (localState.HasRemovestreamData) + { + // process each buffer + for (int i = 0; i < localState.OldEventsPerView.Length; i++) + { + if (localState.OldEventsPerView[i] == null) + { + continue; + } + + EventBean[] viewOldData = localState.OldEventsPerView[i]; + localState.OldEventsPerView[i] = null; // clear entry + + // add each event to the set of events removed + foreach (EventBean oldEvent in viewOldData) + { + localState.RemovalEvents.Add(oldEvent); + } + + localState.IsDiscardObserverEvents = true; + try + { + for (int j = 0; j < _views.Length; j++) + { + if (i != j) + { + _views[j].Update(null, viewOldData); + } + } + } + finally + { + localState.IsDiscardObserverEvents = false; + } + } + + localState.OldEvents.AddAll(localState.RemovalEvents); + } + + localState.NewEvents.Clear(); + for (int i = 0; i < newData.Length; i++) + { + if (!localState.RemovalEvents.Contains(newData[i])) + { + localState.NewEvents.Add(newData[i]); + } + } + + if (!localState.NewEvents.IsEmpty()) + { + newDataPosted = localState.NewEvents.ToArray(); + } + + } + + // indicate new and, possibly, old data + EventBean[] oldDataPosted = null; + if (!localState.OldEvents.IsEmpty()) + { + oldDataPosted = localState.OldEvents.ToArray(); + } + if ((newDataPosted != null) || (oldDataPosted != null)) + { + UpdateChildren(newDataPosted, oldDataPosted); + } + localState.OldEvents.Clear(); + } + + public override EventType EventType + { + get { return _factory.EventType; } + } + + public override IEnumerator GetEnumerator() + { + return _views[0].GetEnumerator(); + } + + public void NewData(int streamId, EventBean[] newEvents, EventBean[] oldEvents) + { + IntersectAsymetricViewLocalState localState = _factory.GetAsymetricViewLocalStatePerThread(); + localState.NewDataChildView = newEvents; + + if ((oldEvents == null) || (localState.IsDiscardObserverEvents)) + { + return; + } + + if (localState.IsRetainObserverEvents) + { + localState.OldEventsPerView[streamId] = oldEvents; + localState.HasRemovestreamData = true; + return; + } + + // remove old data from all other views + localState.IsDiscardObserverEvents = true; + try + { + for (int i = 0; i < _views.Length; i++) + { + if (i != streamId) + { + _views[i].Update(null, oldEvents); + } + } + } + finally + { + localState.IsDiscardObserverEvents = false; + } + + UpdateChildren(null, oldEvents); + } + + public void Stop() + { + foreach (var view in _views.OfType()) + { + view.Stop(); + } + } + + public void VisitViewContainer(ViewDataVisitorContained viewDataVisitor) + { + IntersectDefaultView.VisitViewContained(viewDataVisitor, _factory, _views); + } + + public void VisitView(ViewDataVisitor viewDataVisitor) + { + throw new UnsupportedOperationException(); + } + + public ViewFactory ViewFactory + { + get { return _factory; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/view/internals/IntersectAsymetricViewLocalState.cs b/NEsper.Core/NEsper.Core/view/internals/IntersectAsymetricViewLocalState.cs new file mode 100755 index 000000000..78bd45db0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/internals/IntersectAsymetricViewLocalState.cs @@ -0,0 +1,78 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.view.internals +{ + public class IntersectAsymetricViewLocalState + { + private readonly EventBean[][] _oldEventsPerView; + private readonly ISet _removalEvents = new HashSet(); + private readonly ArrayDeque _newEvents = new ArrayDeque(); + + private EventBean[] _newDataChildView; + private bool _hasRemovestreamData; + private bool _retainObserverEvents; + private bool _discardObserverEvents; + private ISet _oldEvents = new HashSet(); + + public IntersectAsymetricViewLocalState(EventBean[][] oldEventsPerView) + { + _oldEventsPerView = oldEventsPerView; + } + + public EventBean[][] OldEventsPerView + { + get { return _oldEventsPerView; } + } + + public ISet RemovalEvents + { + get { return _removalEvents; } + } + + public ArrayDeque NewEvents + { + get { return _newEvents; } + } + + public EventBean[] NewDataChildView + { + get { return _newDataChildView; } + set { _newDataChildView = value; } + } + + public bool HasRemovestreamData + { + get { return _hasRemovestreamData; } + set { _hasRemovestreamData = value; } + } + + public bool IsRetainObserverEvents + { + get { return _retainObserverEvents; } + set { _retainObserverEvents = value; } + } + + public bool IsDiscardObserverEvents + { + get { return _discardObserverEvents; } + set { _discardObserverEvents = value; } + } + + public ISet OldEvents + { + get { return _oldEvents; } + set { _oldEvents = value; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/view/internals/IntersectBatchView.cs b/NEsper.Core/NEsper.Core/view/internals/IntersectBatchView.cs new file mode 100755 index 000000000..dc935f28e --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/internals/IntersectBatchView.cs @@ -0,0 +1,265 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.core.context.util; +using com.espertech.esper.events; + +namespace com.espertech.esper.view.internals +{ + /// + /// A view that represents an intersection of multiple data windows. + /// + /// The view is parameterized by two or more data windows. From an external viewpoint, the + /// view retains all events that is in all of the data windows at the same time (an intersection) + /// and removes all events that leave any of the data windows. + /// + /// + /// This special batch-version has the following logic: + /// - only one batching view allowed as sub-view + /// - all externally-received newData events are inserted into each view + /// - all externally-received oldData events are removed from each view + /// - any non-batch view has its newData output ignored + /// - the single batch-view has its newData posted to child views, and removed from all non-batch views + /// - all oldData events received from all non-batch views are removed from each view + /// + /// + public class IntersectBatchView + : ViewSupport + , LastPostObserver + , CloneableView + , StoppableView + , DataWindowView + , IntersectViewMarker + , ViewDataVisitableContainer + , ViewContainer + { + private readonly AgentInstanceViewFactoryChainContext _agentInstanceViewFactoryContext; + private readonly IntersectViewFactory _factory; + private readonly View[] _views; + + /// + /// Ctor. + /// + /// The agent instance view factory context. + /// the view factory + /// the list of data window views + public IntersectBatchView( + AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext, + IntersectViewFactory factory, + IList viewList) + { + _agentInstanceViewFactoryContext = agentInstanceViewFactoryContext; + _factory = factory; + _views = viewList.ToArray(); + + for (int i = 0; i < viewList.Count; i++) + { + LastPostObserverView view = new LastPostObserverView(i); + _views[i].RemoveAllViews(); + _views[i].AddView(view); + view.Observer = this; + } + } + + public View[] ViewContained + { + get { return _views; } + } + + public View CloneView() + { + return _factory.MakeView(_agentInstanceViewFactoryContext); + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + IntersectBatchViewLocalState localState = _factory.GetBatchViewLocalStatePerThread(); + + // handle remove stream: post oldData to all views + if (oldData != null && oldData.Length != 0) + { + try + { + localState.IsIgnoreViewIRStream = true; + for (int i = 0; i < _views.Length; i++) + { + _views[i].Update(newData, oldData); + } + } + finally + { + localState.IsIgnoreViewIRStream = false; + } + } + + if (newData != null) + { + // post to all non-batch views first to let them decide the remove stream, if any + try + { + localState.IsCaptureIRNonBatch = true; + for (int i = 0; i < _views.Length; i++) + { + if (i != _factory.BatchViewIndex) + { + _views[i].Update(newData, oldData); + } + } + } + finally + { + localState.IsCaptureIRNonBatch = false; + } + + // if there is any data removed from non-batch views, remove from all views + // collect removed events + localState.RemovedEvents.Clear(); + for (int i = 0; i < _views.Length; i++) + { + if (localState.OldEventsPerView[i] != null) + { + for (int j = 0; j < _views.Length; j++) + { + if (i == j) + { + continue; + } + _views[j].Update(null, localState.OldEventsPerView[i]); + + for (int k = 0; k < localState.OldEventsPerView[i].Length; k++) + { + localState.RemovedEvents.Add(localState.OldEventsPerView[i][k]); + } + } + localState.OldEventsPerView[i] = null; + } + } + + // post only new events to the batch view that have not been removed + EventBean[] newDataNonRemoved; + if (_factory.IsAsymmetric()) + { + newDataNonRemoved = EventBeanUtility.GetNewDataNonRemoved( + newData, localState.RemovedEvents, localState.NewEventsPerView); + } + else + { + newDataNonRemoved = EventBeanUtility.GetNewDataNonRemoved(newData, localState.RemovedEvents); + } + if (newDataNonRemoved != null) + { + _views[_factory.BatchViewIndex].Update(newDataNonRemoved, null); + } + } + } + + public override EventType EventType + { + get { return _factory.EventType; } + } + + public override IEnumerator GetEnumerator() + { + return _views[_factory.BatchViewIndex].GetEnumerator(); + } + + public void NewData(int streamId, EventBean[] newEvents, EventBean[] oldEvents) + { + IntersectBatchViewLocalState localState = _factory.GetBatchViewLocalStatePerThread(); + + if (localState.IsIgnoreViewIRStream) + { + return; + } + + if (localState.IsCaptureIRNonBatch) + { + localState.OldEventsPerView[streamId] = oldEvents; + if (_factory.IsAsymmetric()) + { + localState.NewEventsPerView[streamId] = newEvents; + } + return; + } + + // handle case where irstream originates from view, i.e. timer-based + if (streamId == _factory.BatchViewIndex) + { + UpdateChildren(newEvents, oldEvents); + if (newEvents != null) + { + try + { + localState.IsIgnoreViewIRStream = true; + for (int i = 0; i < _views.Length; i++) + { + if (i != streamId) + { + _views[i].Update(null, newEvents); + } + } + } + finally + { + localState.IsIgnoreViewIRStream = false; + } + } + } + // post remove stream to all other views + else + { + if (oldEvents != null) + { + try + { + localState.IsIgnoreViewIRStream = true; + for (int i = 0; i < _views.Length; i++) + { + if (i != streamId) + { + _views[i].Update(null, oldEvents); + } + } + } + finally + { + localState.IsIgnoreViewIRStream = false; + } + } + } + } + + public void Stop() + { + foreach (var view in _views.OfType()) + { + view.Stop(); + } + } + + public void VisitViewContainer(ViewDataVisitorContained viewDataVisitor) + { + IntersectDefaultView.VisitViewContained(viewDataVisitor, _factory, _views); + } + + public void VisitView(ViewDataVisitor viewDataVisitor) + { + throw new UnsupportedOperationException(); + } + + public ViewFactory ViewFactory + { + get { return _factory; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/view/internals/IntersectBatchViewLocalState.cs b/NEsper.Core/NEsper.Core/view/internals/IntersectBatchViewLocalState.cs new file mode 100755 index 000000000..559f847cb --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/internals/IntersectBatchViewLocalState.cs @@ -0,0 +1,57 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.view.internals +{ + public class IntersectBatchViewLocalState + { + private readonly EventBean[][] _oldEventsPerView; + private readonly EventBean[][] _newEventsPerView; + private readonly ISet _removedEvents = new LinkedHashSet(); + private bool _captureIrNonBatch; + private bool _ignoreViewIrStream; + + public IntersectBatchViewLocalState(EventBean[][] oldEventsPerView, EventBean[][] newEventsPerView) + { + _oldEventsPerView = oldEventsPerView; + _newEventsPerView = newEventsPerView; + } + + public EventBean[][] OldEventsPerView + { + get { return _oldEventsPerView; } + } + + public EventBean[][] NewEventsPerView + { + get { return _newEventsPerView; } + } + + public ISet RemovedEvents + { + get { return _removedEvents; } + } + + public bool IsCaptureIRNonBatch + { + get { return _captureIrNonBatch; } + set { _captureIrNonBatch = value; } + } + + public bool IsIgnoreViewIRStream + { + get { return _ignoreViewIrStream; } + set { _ignoreViewIrStream = value; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/view/internals/IntersectDefaultView.cs b/NEsper.Core/NEsper.Core/view/internals/IntersectDefaultView.cs new file mode 100755 index 000000000..6f23bdee5 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/internals/IntersectDefaultView.cs @@ -0,0 +1,271 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.core.context.util; +using com.espertech.esper.metrics.instrumentation; +using System.Collections.Generic; +using System.Linq; + +namespace com.espertech.esper.view.internals +{ + /// + /// A view that represents an intersection of multiple data windows. + /// The view is parameterized by two or more data windows. From an external viewpoint, the + /// view retains all events that is in all of the data windows at the same time (an intersection) + /// and removes all events that leave any of the data windows. + /// + public class IntersectDefaultView + : ViewSupport + , LastPostObserver + , CloneableView + , StoppableView + , DataWindowView + , IntersectViewMarker + , ViewDataVisitableContainer + , ViewContainer + { + protected readonly AgentInstanceViewFactoryChainContext AgentInstanceViewFactoryContext; + private readonly IntersectViewFactory _factory; + protected readonly View[] mViews; + + /// + /// Ctor. + /// + /// The agent instance view factory context. + /// the view _factory + /// the list of data window views + public IntersectDefaultView( + AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext, + IntersectViewFactory factory, + IList viewList) + { + AgentInstanceViewFactoryContext = agentInstanceViewFactoryContext; + _factory = factory; + mViews = viewList.ToArray(); + + for (int i = 0; i < viewList.Count; i++) + { + LastPostObserverView view = new LastPostObserverView(i); + mViews[i].RemoveAllViews(); + mViews[i].AddView(view); + view.Observer = this; + } + } + + public View[] ViewContained + { + get { return mViews; } + } + + public View CloneView() + { + return _factory.MakeView(AgentInstanceViewFactoryContext); + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().QViewProcessIRStream(this, _factory.ViewName, newData, oldData); + } + + IntersectDefaultViewLocalState localState = _factory.GetDefaultViewLocalStatePerThread(); + + if (newData != null) + { + // new events must go to all views + // old events, such as when removing from a named window, get removed from all views + localState.HasRemovestreamData = false; // changed by observer logic to indicate new data + localState.IsRetainObserverEvents = true; // enable retain logic in observer + try + { + foreach (View view in mViews) + { + view.Update(newData, oldData); + } + } + finally + { + localState.IsRetainObserverEvents = false; + } + + // see if any child view has removed any events. + // if there was an insert stream, handle pushed-out events + if (localState.HasRemovestreamData) + { + localState.RemovalEvents.Clear(); + + // process each buffer + for (int i = 0; i < localState.OldEventsPerView.Length; i++) + { + if (localState.OldEventsPerView[i] == null) + { + continue; + } + + EventBean[] viewOldData = localState.OldEventsPerView[i]; + localState.OldEventsPerView[i] = null; // clear entry + + // add each event to the set of events removed + foreach (EventBean oldEvent in viewOldData) + { + localState.RemovalEvents.Add(oldEvent); + } + + localState.IsDiscardObserverEvents = true; + try + { + for (int j = 0; j < mViews.Length; j++) + { + if (i != j) + { + mViews[j].Update(null, viewOldData); + } + } + } + finally + { + localState.IsDiscardObserverEvents = false; + } + } + + oldData = localState.RemovalEvents.ToArray(); + } + + // indicate new and, possibly, old data + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().QViewIndicate(this, _factory.ViewName, newData, oldData); + } + UpdateChildren(newData, oldData); + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().AViewIndicate(); + } + } + + // handle remove stream + else if (oldData != null) + { + localState.IsDiscardObserverEvents = true; // disable reaction logic in observer + try + { + foreach (View view in mViews) + { + view.Update(null, oldData); + } + } + finally + { + localState.IsDiscardObserverEvents = false; + } + + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().QViewIndicate(this, _factory.ViewName, null, oldData); + } + UpdateChildren(null, oldData); + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().AViewIndicate(); + } + } + + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().AViewProcessIRStream(); + } + } + + public override EventType EventType + { + get { return _factory.EventType; } + } + + public override IEnumerator GetEnumerator() + { + return mViews[0].GetEnumerator(); + } + + public void NewData(int streamId, EventBean[] newEvents, EventBean[] oldEvents) + { + IntersectDefaultViewLocalState localState = _factory.GetDefaultViewLocalStatePerThread(); + + if ((oldEvents == null) || (localState.IsDiscardObserverEvents)) + { + return; + } + + if (localState.IsRetainObserverEvents) + { + localState.OldEventsPerView[streamId] = oldEvents; + localState.HasRemovestreamData = true; + return; + } + + // remove old data from all other views + localState.IsDiscardObserverEvents = true; + try + { + for (int i = 0; i < mViews.Length; i++) + { + if (i != streamId) + { + mViews[i].Update(null, oldEvents); + } + } + } + finally + { + localState.IsDiscardObserverEvents = false; + } + + UpdateChildren(null, oldEvents); + } + + public void Stop() + { + foreach (View view in mViews) + { + if (view is StoppableView) + { + ((StoppableView)view).Stop(); + } + } + } + + public void VisitViewContainer(ViewDataVisitorContained viewDataVisitor) + { + VisitViewContained(viewDataVisitor, _factory, mViews); + } + + public void VisitView(ViewDataVisitor viewDataVisitor) + { + throw new UnsupportedOperationException(); + } + + public static void VisitViewContained( + ViewDataVisitorContained viewDataVisitor, + ViewFactory viewFactory, + View[] views) + { + viewDataVisitor.VisitPrimary(viewFactory.ViewName, views.Length); + for (int i = 0; i < views.Length; i++) + { + viewDataVisitor.VisitContained(i, views[i]); + } + } + + public ViewFactory ViewFactory + { + get { return _factory; } + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/view/internals/IntersectDefaultViewLocalState.cs b/NEsper.Core/NEsper.Core/view/internals/IntersectDefaultViewLocalState.cs new file mode 100755 index 000000000..79926d318 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/internals/IntersectDefaultViewLocalState.cs @@ -0,0 +1,33 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; + +namespace com.espertech.esper.view.internals +{ + public class IntersectDefaultViewLocalState + { + public IntersectDefaultViewLocalState(EventBean[][] oldEventsPerView) + { + RemovalEvents = new HashSet(); + OldEventsPerView = oldEventsPerView; + } + + public EventBean[][] OldEventsPerView { get; private set; } + + public ISet RemovalEvents { get; private set; } + + public bool HasRemovestreamData { get; set; } + + public bool IsRetainObserverEvents { get; set; } + + public bool IsDiscardObserverEvents { get; set; } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/view/internals/IntersectViewFactory.cs b/NEsper.Core/NEsper.Core/view/internals/IntersectViewFactory.cs new file mode 100755 index 000000000..47c2bca4a --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/internals/IntersectViewFactory.cs @@ -0,0 +1,207 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using com.espertech.esper.client; +using com.espertech.esper.compat.threading; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.view.internals +{ + /// + /// Factory for union-views. + /// + public class IntersectViewFactory + : ViewFactory + , DataWindowViewFactory + , DataWindowViewFactoryUniqueCandidate + , ViewFactoryContainer + { + private EventType _parentEventType; + private IList _viewFactories; + private int _batchViewIndex; + private bool _isAsymmetric; + private IThreadLocal _batchViewLocalState; + private IThreadLocal _defaultViewLocalState; + private IThreadLocal _asymetricViewLocalState; + + /// + /// Sets the view factories. + /// + /// factories + public IList ViewFactories + { + set + { + _viewFactories = value; + + var batchCount = 0; + for (var i = 0; i < value.Count; i++) + { + ViewFactory viewFactory = value[i]; + _isAsymmetric |= viewFactory is AsymetricDataWindowViewFactory; + if (viewFactory is DataWindowBatchingViewFactory) + { + batchCount++; + _batchViewIndex = i; + } + } + if (batchCount > 1) + { + throw new ViewProcessingException("Cannot combined multiple batch data windows into an intersection"); + } + + if (batchCount == 1) + { + _batchViewLocalState = ThreadLocalManager.Create( + () => new IntersectBatchViewLocalState( + new EventBean[value.Count][], + new EventBean[value.Count][])); + } + else if (_isAsymmetric) + { + _asymetricViewLocalState = ThreadLocalManager.Create( + () => new IntersectAsymetricViewLocalState( + new EventBean[value.Count][])); + } + else + { + _defaultViewLocalState = ThreadLocalManager.Create( + () => new IntersectDefaultViewLocalState( + new EventBean[value.Count][])); + } + } + } + + public void SetViewParameters(ViewFactoryContext viewFactoryContext, IList viewParameters) + { + } + + public void Attach( + EventType parentEventType, + StatementContext statementContext, + ViewFactory optionalParentFactory, + IList parentViewFactories) + { + } + + public View MakeView(AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext) + { + IList views = new List(); + var hasBatch = false; + foreach (var viewFactory in _viewFactories) + { + agentInstanceViewFactoryContext.IsRemoveStream = true; + views.Add(viewFactory.MakeView(agentInstanceViewFactoryContext)); + hasBatch |= viewFactory is DataWindowBatchingViewFactory; + } + if (hasBatch) + { + return new IntersectBatchView(agentInstanceViewFactoryContext, this, views); + } + else if (_isAsymmetric) + { + return new IntersectAsymetricView(agentInstanceViewFactoryContext, this, views); + } + return new IntersectDefaultView(agentInstanceViewFactoryContext, this, views); + } + + public EventType EventType + { + get { return _parentEventType; } + } + + public bool CanReuse(View view, AgentInstanceContext agentInstanceContext) + { + return false; + } + + public ICollection UniquenessCandidatePropertyNames + { + get + { + return _viewFactories + .OfType() + .Select(unique => unique.UniquenessCandidatePropertyNames) + .FirstOrDefault(props => props != null); + } + } + + public string ViewName + { + get { return GetViewNameUnionIntersect(true, _viewFactories); } + } + + public ICollection ViewFactoriesContained + { + get { return _viewFactories; } + } + + internal static string GetViewNameUnionIntersect(bool intersect, ICollection factories) + { + var buf = new StringBuilder(); + buf.Append(intersect ? "Intersection" : "Union"); + + if (factories == null) + { + return buf.ToString(); + } + + buf.Append(" of "); + var delimiter = ""; + foreach (var factory in factories) + { + buf.Append(delimiter); + buf.Append(factory.ViewName); + delimiter = ","; + } + + return buf.ToString(); + } + + /// + /// Sets the parent event type. + /// + /// type + public EventType ParentEventType + { + get { return _parentEventType; } + set { _parentEventType = value; } + } + + public int BatchViewIndex + { + get { return _batchViewIndex; } + } + + public bool IsAsymmetric() + { + return _isAsymmetric; + } + + public IntersectBatchViewLocalState GetBatchViewLocalStatePerThread() + { + return _batchViewLocalState.GetOrCreate(); + } + + public IntersectDefaultViewLocalState GetDefaultViewLocalStatePerThread() + { + return _defaultViewLocalState.GetOrCreate(); + } + + public IntersectAsymetricViewLocalState GetAsymetricViewLocalStatePerThread() + { + return _asymetricViewLocalState.GetOrCreate(); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/view/internals/IntersectViewMarker.cs b/NEsper.Core/NEsper.Core/view/internals/IntersectViewMarker.cs new file mode 100755 index 000000000..1b034f37b --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/internals/IntersectViewMarker.cs @@ -0,0 +1,14 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.view.internals +{ + public interface IntersectViewMarker + { + } +} diff --git a/NEsper.Core/NEsper.Core/view/internals/LastPostObserver.cs b/NEsper.Core/NEsper.Core/view/internals/LastPostObserver.cs new file mode 100755 index 000000000..04dccfa2b --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/internals/LastPostObserver.cs @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; + +namespace com.espertech.esper.view.internals +{ + /// + /// Observer interface to a stream publishing new and old events. + /// + public interface LastPostObserver + { + /// + /// Receive new and old events from a stream. + /// + /// the stream number sending the events + /// new events + /// old events + void NewData(int streamId, EventBean[] newEvents, EventBean[] oldEvents); + } +} diff --git a/NEsper.Core/NEsper.Core/view/internals/LastPostObserverView.cs b/NEsper.Core/NEsper.Core/view/internals/LastPostObserverView.cs new file mode 100755 index 000000000..b88dd5c01 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/internals/LastPostObserverView.cs @@ -0,0 +1,101 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; + +namespace com.espertech.esper.view.internals +{ + /// + /// A view that retains the last Update. + /// + public sealed class LastPostObserverView + : View + , CloneableView + { + private Viewable _parent; + private readonly int _streamId; + private LastPostObserver _observer; + + /// Ctor. + /// number of the stream for which the view buffers the generated events. + public LastPostObserverView(int streamId) + { + _streamId = streamId; + } + + /// Set an observer. + /// to be called when results are available + public LastPostObserver Observer + { + set { _observer = value; } + } + + public View CloneView() + { + return new LastPostObserverView(_streamId); + } + + public EventType EventType + { + get { return _parent.EventType; } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public IEnumerator GetEnumerator() + { + return _parent.GetEnumerator(); + } + + public Viewable Parent + { + get { return _parent; } + set { _parent = value; } + } + + public View AddView(View view) + { + throw new UnsupportedOperationException(); + } + + public View[] Views + { + get { return new View[0]; } + } + + public bool RemoveView(View view) + { + throw new UnsupportedOperationException(); + } + + public void RemoveAllViews() + { + throw new UnsupportedOperationException(); + } + + public bool HasViews + { + get { return false; } + } + + public void Update(EventBean[] newData, EventBean[] oldData) + { + if (_observer != null) + { + _observer.NewData(_streamId, newData, oldData); + } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/view/internals/NoopView.cs b/NEsper.Core/NEsper.Core/view/internals/NoopView.cs new file mode 100755 index 000000000..31c56a79d --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/internals/NoopView.cs @@ -0,0 +1,56 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.util; + +namespace com.espertech.esper.view.internals +{ + public class NoopView + : ViewSupport + , DataWindowView + , CloneableView + { + private readonly NoopViewFactory _viewFactory; + + public NoopView(NoopViewFactory viewFactory) + { + _viewFactory = viewFactory; + } + + public ViewFactory ViewFactory + { + get { return _viewFactory; } + } + + public View CloneView() + { + return _viewFactory.MakeView(null); + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + } + + public override EventType EventType + { + get { return _viewFactory.EventType; } + } + + public override IEnumerator GetEnumerator() + { + return CollectionUtil.NULL_EVENT_ITERATOR; + } + + public void VisitView(ViewDataVisitor viewDataVisitor) + { + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/view/internals/NoopViewFactory.cs b/NEsper.Core/NEsper.Core/view/internals/NoopViewFactory.cs new file mode 100755 index 000000000..268cf663f --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/internals/NoopViewFactory.cs @@ -0,0 +1,52 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.view.internals +{ + public class NoopViewFactory + : DataWindowViewFactory + { + private EventType _eventType; + + public void SetViewParameters(ViewFactoryContext viewFactoryContext, IList viewParameters) + { + } + + public void Attach(EventType parentEventType, StatementContext statementContext, ViewFactory optionalParentFactory, IList parentViewFactories) + { + _eventType = parentEventType; + } + + public View MakeView(AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext) + { + return new NoopView(this); + } + + public virtual EventType EventType + { + get { return _eventType; } + } + + public bool CanReuse(View view, AgentInstanceContext agentInstanceContext) + { + return false; + } + + public string ViewName + { + get { return "noop"; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/view/internals/PatternRemoveDispatchView.cs b/NEsper.Core/NEsper.Core/view/internals/PatternRemoveDispatchView.cs new file mode 100755 index 000000000..5cede99ad --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/internals/PatternRemoveDispatchView.cs @@ -0,0 +1,124 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.service; +using com.espertech.esper.pattern; + +namespace com.espertech.esper.view.internals +{ + /// + /// View to handle pattern discarding for a single stream (no join). + /// + public sealed class PatternRemoveDispatchView : ViewSupport, EPStatementDispatch + { + private readonly EvalRootMatchRemover _matchRemoveCallback; + private readonly bool _suppressSameEventMatches; + private readonly bool _discardPartialsOnMatch; + + private bool _hasData = false; + private readonly FlushedEventBuffer _newDataBuffer = new FlushedEventBuffer(); + + public PatternRemoveDispatchView(EvalRootMatchRemover matchRemoveCallback, bool suppressSameEventMatches, bool discardPartialsOnMatch) + { + _matchRemoveCallback = matchRemoveCallback; + _suppressSameEventMatches = suppressSameEventMatches; + _discardPartialsOnMatch = discardPartialsOnMatch; + } + + public override EventType EventType + { + get { return Parent.EventType; } + } + + public override IEnumerator GetEnumerator() + { + return Parent.GetEnumerator(); + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + _newDataBuffer.Add(newData); + _hasData = true; + } + + public void Execute() + { + if (_hasData) { + _hasData = false; + + var matches = _newDataBuffer.GetAndFlush(); + + if (_discardPartialsOnMatch) { + var events = new HashSet(); + foreach (var match in matches) { + AddEventsFromMatch(match, events); + } + if (events.Count > 0) { + _matchRemoveCallback.RemoveMatch(events); + } + } + + if (_suppressSameEventMatches && matches.Length > 1) { + var events = new HashSet(); + AddEventsFromMatch(matches[0], events); + if (matches.Length == 2) { + var overlaps = AddEventsFromMatch(matches[1], events); + if (overlaps) { + matches = new EventBean[] {matches[0]}; + } + } + else { + IList matchesNonOverlapping = new List(matches.Length); + matchesNonOverlapping.Add(matches[0]); + for (var i = 1; i < matches.Length; i++) { + var eventsThisMatch = new HashSet(); + eventsThisMatch.AddAll(events); + var overlaps = AddEventsFromMatch(matches[i], eventsThisMatch); + if (!overlaps) { + events.AddAll(eventsThisMatch); + matchesNonOverlapping.Add(matches[i]); + } + } + matches = matchesNonOverlapping.ToArray(); + } + } + + UpdateChildren(matches, null); + } + } + + private bool AddEventsFromMatch(EventBean match, ISet events) + { + var properties = match.EventType.PropertyDescriptors; + var overlaps = false; + + foreach (var desc in properties) { + var prop = ((IDictionary) match.Underlying).Get(desc.PropertyName); + if (prop == null) { + } + else if (prop is EventBean) { + overlaps |= !events.Add((EventBean) prop); + } + else if (prop is EventBean[]) { + var arr = (EventBean[]) prop; + foreach (var ele in arr) { + overlaps |= !events.Add(ele); + } + } + } + + return overlaps; + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/internals/PriorEventBufferChangeCaptureMulti.cs b/NEsper.Core/NEsper.Core/view/internals/PriorEventBufferChangeCaptureMulti.cs new file mode 100755 index 000000000..9c4a83c44 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/internals/PriorEventBufferChangeCaptureMulti.cs @@ -0,0 +1,18 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; + +namespace com.espertech.esper.view.internals +{ + public interface PriorEventBufferChangeCaptureMulti + { + void Added(EventBean key, EventBean[] referrals); + void Removed(EventBean removed); + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/view/internals/PriorEventBufferChangeCaptureSingle.cs b/NEsper.Core/NEsper.Core/view/internals/PriorEventBufferChangeCaptureSingle.cs new file mode 100755 index 000000000..163465bd6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/internals/PriorEventBufferChangeCaptureSingle.cs @@ -0,0 +1,18 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; + +namespace com.espertech.esper.view.internals +{ + public interface PriorEventBufferChangeCaptureSingle + { + void Added(EventBean key, EventBean referral); + void Removed(EventBean removed); + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/view/internals/PriorEventBufferMulti.cs b/NEsper.Core/NEsper.Core/view/internals/PriorEventBufferMulti.cs new file mode 100755 index 000000000..b466f4207 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/internals/PriorEventBufferMulti.cs @@ -0,0 +1,211 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.view.window; + +namespace com.espertech.esper.view.internals +{ + /// + /// Buffers view-posted insert stream (new data) and remove stream (old data) events for + /// use with determining prior results in these streams, for multiple different prior events. + /// + /// Buffers only exactly those events in new data and old data that are being asked for via + /// the 2 or more 'prior' functions that specify different indexes. For example "select Prior(2, price), Prior(1, price)" + /// results in on buffer instance handling both the need to the immediatly prior (1) and the 2-events-ago event (2). + /// + /// As all views are required to post new data and post old data that removes the new data to subsequent views, this buffer + /// can be attached to all views and should not result in a memory leak. + /// + /// When the buffer receives old data (rstream) events it removes the prior events to the rstream events from the buffer + /// the next time it receives a post (not immediatly) to allow queries to the buffer. + /// + public class PriorEventBufferMulti + : ViewUpdatedCollection + , RelativeAccessByEventNIndex + { + private readonly int _priorToIndexesSize; + private readonly int[] _priorToIndexes; + private readonly IDictionary _priorEventMap; + private readonly RollingEventBuffer _newEvents; + private EventBean[] _lastOldData; + + /// + /// Ctor. + /// + /// holds a list of prior-event indexes. + /// For example, an array {0,4,6} means the current event, 4 events before the current event and 6 events before the current event. + /// + public PriorEventBufferMulti(int[] priorToIndexSet) + { + // Determine the maximum prior index to retain + int maxPriorIndex = 0; + foreach (int priorIndex in priorToIndexSet) + { + if (priorIndex > maxPriorIndex) + { + maxPriorIndex = priorIndex; + } + } + + // Copy the set of indexes into an array, sort in ascending order + _priorToIndexesSize = priorToIndexSet.Length; + _priorToIndexes = new int[_priorToIndexesSize]; + int count = 0; + foreach (int priorIndex in priorToIndexSet) + { + _priorToIndexes[count++] = priorIndex; + } + + _priorToIndexes.SortInPlace(); + + // Construct a rolling buffer of new data for holding max index + 1 (position 1 requires 2 events to keep) + _newEvents = new RollingEventBuffer(maxPriorIndex + 1); + _priorEventMap = new Dictionary(); + } + + public void Update(EventBean[] newData, EventBean[] oldData) + { + // Remove last old data posted in previous post + if (_lastOldData != null) + { + for (int i = 0; i < _lastOldData.Length; i++) + { + _priorEventMap.Remove(_lastOldData[i]); + } + } + + // Post new data to rolling buffer starting with the oldest + if (newData != null) + { + for (int i = 0; i < newData.Length; i++) + { + EventBean newEvent = newData[i]; + + // Add new event + _newEvents.Add(newEvent); + + // Save prior index events in array + EventBean[] priorEvents = new EventBean[_priorToIndexesSize]; + for (int j = 0; j < _priorToIndexesSize; j++) + { + int priorIndex = _priorToIndexes[j]; + priorEvents[j] = _newEvents.Get(priorIndex); + } + _priorEventMap.Put(newEvent, priorEvents); + } + } + + // Save old data to be removed next time we get posted results + _lastOldData = oldData; + } + + public void Update(EventBean[] newData, EventBean[] oldData, PriorEventBufferChangeCaptureMulti capture) + { + // Remove last old data posted in previous post + if (_lastOldData != null) + { + for (int i = 0; i < _lastOldData.Length; i++) + { + EventBean oldDataItem = _lastOldData[i]; + _priorEventMap.Remove(oldDataItem); + capture.Removed(oldDataItem); + } + } + + // Post new data to rolling buffer starting with the oldest + if (newData != null) + { + for (int i = 0; i < newData.Length; i++) + { + EventBean newEvent = newData[i]; + + // Add new event + _newEvents.Add(newEvent); + + // Save prior index events in array + EventBean[] priorEvents = new EventBean[_priorToIndexesSize]; + for (int j = 0; j < _priorToIndexesSize; j++) + { + int priorIndex = _priorToIndexes[j]; + priorEvents[j] = _newEvents.Get(priorIndex); + } + _priorEventMap.Put(newEvent, priorEvents); + capture.Added(newEvent, priorEvents); + } + } + + // Save old data to be removed next time we get posted results + _lastOldData = oldData; + } + + public EventBean GetRelativeToEvent(EventBean theEvent, int priorToIndex) + { + if (priorToIndex >= _priorToIndexesSize) + { + throw new ArgumentException("MapIndex " + priorToIndex + " not allowed, max size is " + _priorToIndexesSize); + } + EventBean[] priorEvents = _priorEventMap.Get(theEvent); + if (priorEvents == null) + { + throw new IllegalStateException("Event not currently in collection, event=" + theEvent); + } + return priorEvents[priorToIndex]; + } + + public EventBean GetRelativeToEnd(int index) + { + // No requirements to return events related to the end of the current buffer + return null; + } + + public int GetWindowToEventCount() + { + // No requirements to return events related to the end of the current buffer + return 0; + } + + public IEnumerator GetWindowToEvent() + { + // No requirements to return events related to the end of the current buffer + return null; + } + + public ICollection GetWindowToEventCollReadOnly() + { + // No requirements to return events related to the end of the current buffer + return null; + } + + public void Destroy() + { + // No action required + } + + public IDictionary PriorEventMap + { + get { return _priorEventMap; } + } + + public RollingEventBuffer NewEvents + { + get { return _newEvents; } + } + + public int NumEventsInsertBuf + { + get { return _newEvents.Count; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/internals/PriorEventBufferSingle.cs b/NEsper.Core/NEsper.Core/view/internals/PriorEventBufferSingle.cs new file mode 100755 index 000000000..18cd0effe --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/internals/PriorEventBufferSingle.cs @@ -0,0 +1,167 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.view.window; + +namespace com.espertech.esper.view.internals +{ + /// + /// Buffers view-posted insert stream (new data) and remove stream (old data) events + /// for use with serving prior results in these streams, for a single prior event. + /// + /// Buffers only exactly those events in new data and old data that are being asked for via + /// the 2 or more 'prior' functions that specify different indexes. For + /// example "select Prior(2, price), Prior(1, price)" results in on buffer instance handling + /// both the need to the immediatly prior (1) and the 2-events-ago event (2). + /// + /// As all views are required to post new data and post old data that removes the new data to + /// subsequent views, this buffer can be attached to all views and should not result in a memory leak. + /// + /// When the buffer receives old data (rstream) events it removes the prior events to the rstream + /// events from the buffer the next time it receives a post (not immediatly) to allow queries to + /// the buffer. + /// + public class PriorEventBufferSingle : ViewUpdatedCollection, RelativeAccessByEventNIndex + { + private readonly int _priorEventIndex; + private readonly IDictionary _priorEventMap; + private readonly RollingEventBuffer _newEvents; + private EventBean[] _lastOldData; + + /// Ctor. + /// is the number-of-events prior to the current event we are interested in + public PriorEventBufferSingle(int priorEventIndex) + { + _priorEventIndex = priorEventIndex; + // Construct a rolling buffer of new data for holding max index + 1 (position 1 requires 2 events to keep) + _newEvents = new RollingEventBuffer(priorEventIndex + 1); + _priorEventMap = new Dictionary(); + } + + public void Update(EventBean[] newData, EventBean[] oldData) + { + // Remove last old data posted in previous post + if (_lastOldData != null) + { + for (int i = 0; i < _lastOldData.Length; i++) + { + _priorEventMap.Remove(_lastOldData[i]); + } + } + + // Post new data to rolling buffer starting with the oldest + if (newData != null) + { + for (int i = 0; i < newData.Length; i++) + { + EventBean newEvent = newData[i]; + + // Add new event + _newEvents.Add(newEvent); + + EventBean priorEvent = _newEvents.Get(_priorEventIndex); + _priorEventMap.Put(newEvent, priorEvent); + } + } + + // Save old data to be removed next time we get posted results + _lastOldData = oldData; + } + + public void Update(EventBean[] newData, EventBean[] oldData, PriorEventBufferChangeCaptureSingle captureSingle) + { + // Remove last old data posted in previous post + if (_lastOldData != null) + { + for (int i = 0; i < _lastOldData.Length; i++) + { + EventBean oldDataItem = _lastOldData[i]; + _priorEventMap.Remove(oldDataItem); + captureSingle.Removed(oldDataItem); + } + } + + // Post new data to rolling buffer starting with the oldest + if (newData != null) + { + for (int i = 0; i < newData.Length; i++) + { + EventBean newEvent = newData[i]; + + // Add new event + _newEvents.Add(newEvent); + + EventBean priorEvent = _newEvents.Get(_priorEventIndex); + _priorEventMap.Put(newEvent, priorEvent); + captureSingle.Added(newEvent, priorEvent); + } + } + + // Save old data to be removed next time we get posted results + _lastOldData = oldData; + } + + // Users are assigned an index + public EventBean GetRelativeToEvent(EventBean theEvent, int priorToIndex) + { + if (priorToIndex != 0) + { + throw new ArgumentException("Single prior event buffer takes only a given index of zero"); + } + return _priorEventMap.Get(theEvent); + } + + public EventBean GetRelativeToEnd(int index) + { + // No requirement to index from end of current buffer + return null; + } + + public IEnumerator GetWindowToEvent() + { + // no requirement for window iterator support + return null; + } + + public int GetWindowToEventCount() + { + // no requirement for count support + return 0; + } + + public ICollection GetWindowToEventCollReadOnly() { + return null; + } + + public IDictionary PriorEventMap + { + get { return _priorEventMap; } + } + + public RollingEventBuffer NewEvents + { + get { return _newEvents; } + } + + public void Destroy() + { + // No action required + } + + public int NumEventsInsertBuf + { + get { return _newEvents.Count; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/internals/PriorEventBufferUnbound.cs b/NEsper.Core/NEsper.Core/view/internals/PriorEventBufferUnbound.cs new file mode 100755 index 000000000..83513d5f6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/internals/PriorEventBufferUnbound.cs @@ -0,0 +1,108 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.view.window; + +namespace com.espertech.esper.view.internals +{ + /// + /// Buffer class for insert stream events only for use with unbound streams that inserts + /// data only, to serve up one or more prior events in the insert stream based on an index. + /// + /// Does not expect or care about the remove stream and simple keeps a rolling buffer of + /// new data events up to the maximum prior event we are asking for. + /// + public class PriorEventBufferUnbound : ViewUpdatedCollection, RandomAccessByIndex + { + private readonly int _maxSize; + private readonly RollingEventBuffer _newEvents; + + /// Ctor. + /// is the highest prior-event index required by any expression + public PriorEventBufferUnbound(int maxPriorIndex) + { + _maxSize = maxPriorIndex + 1; + _newEvents = new RollingEventBuffer(_maxSize); + } + + public void Update(EventBean[] newData, EventBean[] oldData) + { + // Post new data to rolling buffer starting with the oldest + if (newData != null) + { + for (int i = 0; i < newData.Length; i++) + { + EventBean newEvent = newData[i]; + + // Add new event + _newEvents.Add(newEvent); + } + } + } + + public EventBean GetNewData(int index) + { + if (index >= _maxSize) + { + throw new ArgumentException("MapIndex " + index + " not allowed, max size is " + _maxSize); + } + return _newEvents[index]; + } + + public EventBean GetOldData(int index) + { + return null; + } + + public void Destroy() + { + // No action required + } + + public EventBean GetNewDataTail(int index) + { + // No requirement to index from end of current buffer + return null; + } + + public IEnumerator GetWindowEnumerator() + { + // no requirement for window iterator support + return null; + } + + public ICollection WindowCollectionReadOnly + { + get { return null; } + } + + public int WindowCount + { + get + { + // no requirement for count support + return 0; + } + } + + public RollingEventBuffer NewEvents + { + get { return _newEvents; } + } + + public int NumEventsInsertBuf + { + get { return _newEvents.Count; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/internals/PriorEventView.cs b/NEsper.Core/NEsper.Core/view/internals/PriorEventView.cs new file mode 100755 index 000000000..fd37b60c9 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/internals/PriorEventView.cs @@ -0,0 +1,66 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; + +namespace com.espertech.esper.view.internals +{ + /// + /// View that provides access to prior events posted by the _parent view for use by 'prior' expression nodes. + /// + public class PriorEventView + : ViewSupport, + ViewDataVisitable + { + private Viewable _parent; + private readonly ViewUpdatedCollection _buffer; + + /// Ctor. + /// is handling the actual storage of events for use in the 'prior' expression + public PriorEventView(ViewUpdatedCollection buffer) + { + _buffer = buffer; + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + _buffer.Update(newData, oldData); + UpdateChildren(newData, oldData); + } + + public override Viewable Parent + { + set { _parent = value; } + } + + /// Returns the underlying buffer used for access to prior events. + /// buffer + public ViewUpdatedCollection Buffer + { + get { return _buffer; } + } + + public override EventType EventType + { + get { return _parent.EventType; } + } + + public override IEnumerator GetEnumerator() + { + return _parent.GetEnumerator(); + } + + public void VisitView(ViewDataVisitor viewDataVisitor) + { + viewDataVisitor.VisitPrimary(_buffer, "Prior"); + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/view/internals/PriorEventViewFactory.cs b/NEsper.Core/NEsper.Core/view/internals/PriorEventViewFactory.cs new file mode 100755 index 000000000..2e786cbf7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/internals/PriorEventViewFactory.cs @@ -0,0 +1,109 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.prior; + +namespace com.espertech.esper.view.internals +{ + /// + /// Factory for making instances. + /// + public class PriorEventViewFactory : ViewFactory + { + /// + /// unbound to indicate the we are not receiving remove stream events (unbound stream, stream without child + /// views) therefore must use a different buffer. + /// + private bool _isUnbound; + private EventType _eventType; + + public void SetViewParameters(ViewFactoryContext viewFactoryContext, IList expressionParameters) + { + if (expressionParameters.Count != 1) + { + throw new ViewParameterException("View requires a single parameter indicating unbound or not"); + } + _isUnbound = (bool) ViewFactorySupport.ValidateAndEvaluate(ViewName, viewFactoryContext.StatementContext, expressionParameters[0]); + } + + public void Attach( + EventType parentEventType, + StatementContext statementContext, + ViewFactory optionalParentFactory, + IList parentViewFactories) + { + _eventType = parentEventType; + } + + public View MakeView(AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext) + { + return new PriorEventView(agentInstanceViewFactoryContext.PriorViewUpdatedCollection); + } + + public ViewUpdatedCollection MakeViewUpdatedCollection( + IDictionary> callbacksPerIndex, + int agentInstanceId) + { + if (callbacksPerIndex.IsEmpty()) + { + throw new IllegalStateException("No resources requested"); + } + + // Construct an array of requested prior-event indexes (such as 10th prior event, 8th prior = {10, 8}) + var requested = new int[callbacksPerIndex.Count]; + int count = 0; + foreach (int reqIndex in callbacksPerIndex.Keys) + { + requested[count++] = reqIndex; + } + + // For unbound streams the buffer is strictly rolling new events + if (_isUnbound) + { + return new PriorEventBufferUnbound(callbacksPerIndex.Keys.Last()); + } + else if (requested.Length == 1) + { + // For bound streams (with views posting old and new data), and if only one prior index requested + return new PriorEventBufferSingle(requested[0]); + } + else + { + // For bound streams (with views posting old and new data) + // Multiple prior event indexes requested, such as "Prior(2, price), Prior(8, price)" + // Sharing a single viewUpdatedCollection for multiple prior-event indexes + return new PriorEventBufferMulti(requested); + } + } + + public EventType EventType + { + get { return _eventType; } + } + + public bool CanReuse(View view, AgentInstanceContext agentInstanceContext) + { + return false; + } + + public string ViewName + { + get { return "Prior-Event"; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/view/internals/PriorEventViewRelAccess.cs b/NEsper.Core/NEsper.Core/view/internals/PriorEventViewRelAccess.cs new file mode 100755 index 000000000..708c3e5b3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/internals/PriorEventViewRelAccess.cs @@ -0,0 +1,56 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.view.window; + +namespace com.espertech.esper.view.internals +{ + public class PriorEventViewRelAccess : RelativeAccessByEventNIndex + { + private readonly RelativeAccessByEventNIndex _buffer; + private readonly int _relativeIndex; + + /// Ctor. + /// is the buffer to acces + /// is the index to pull out + public PriorEventViewRelAccess(RelativeAccessByEventNIndex buffer, int relativeIndex) + { + _buffer = buffer; + _relativeIndex = relativeIndex; + } + + public EventBean GetRelativeToEvent(EventBean theEvent, int prevIndex) + { + return _buffer.GetRelativeToEvent(theEvent, _relativeIndex); + } + + public EventBean GetRelativeToEnd(int index) + { + // No requirement to index from end of current buffer + return null; + } + + public IEnumerator GetWindowToEvent() + { + return null; + } + + public ICollection GetWindowToEventCollReadOnly() { + return null; + } + + public int GetWindowToEventCount() + { + return 0; + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/internals/RouteResultView.cs b/NEsper.Core/NEsper.Core/view/internals/RouteResultView.cs new file mode 100755 index 000000000..2c285ad4d --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/internals/RouteResultView.cs @@ -0,0 +1,96 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.core.start; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.util; + +namespace com.espertech.esper.view.internals +{ + /// + /// View for processing split-stream syntax. + /// + public class RouteResultView : ViewSupport + { + private readonly EventType _eventType; + private readonly ExprEvaluatorContext _exprEvaluatorContext; + private readonly RouteResultViewHandler _handler; + + public RouteResultView( + bool isFirst, + EventType eventType, + EPStatementHandle epStatementHandle, + InternalEventRouter internalEventRouter, + TableStateInstance[] tableStateInstances, + EPStatementStartMethodOnTriggerItem[] items, + ResultSetProcessor[] processors, + ExprEvaluator[] whereClauses, + AgentInstanceContext agentInstanceContext) + { + if (whereClauses.Length != processors.Length) + { + throw new ArgumentException("Number of where-clauses and processors does not match"); + } + + _exprEvaluatorContext = agentInstanceContext; + _eventType = eventType; + if (isFirst) + { + _handler = new RouteResultViewHandlerFirst( + epStatementHandle, internalEventRouter, tableStateInstances, items, processors, whereClauses, + agentInstanceContext); + } + else + { + _handler = new RouteResultViewHandlerAll( + epStatementHandle, internalEventRouter, tableStateInstances, items, processors, whereClauses, + agentInstanceContext); + } + } + + public override EventType EventType + { + get { return _eventType; } + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + if (newData == null) + { + return; + } + + foreach (EventBean bean in newData) + { + bool isHandled = _handler.Handle(bean, _exprEvaluatorContext); + + if (!isHandled) + { + UpdateChildren( + new EventBean[] + { + bean + }, null); + } + } + } + + public override IEnumerator GetEnumerator() + { + return CollectionUtil.NULL_EVENT_ITERATOR; + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/view/internals/RouteResultViewHandler.cs b/NEsper.Core/NEsper.Core/view/internals/RouteResultViewHandler.cs new file mode 100755 index 000000000..2e4ab877a --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/internals/RouteResultViewHandler.cs @@ -0,0 +1,30 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.view.internals +{ + /// + /// Handler for incoming events for split-stream syntax, encapsulates where-clause + /// evaluation strategies. + /// + public interface RouteResultViewHandler + { + /// + /// Handle event. + /// + /// to handle + /// The expression evaluator context. + /// + /// true if at least one match was found, false if not + /// + bool Handle(EventBean theEvent, ExprEvaluatorContext exprEvaluatorContext); + } +} diff --git a/NEsper.Core/NEsper.Core/view/internals/RouteResultViewHandlerAll.cs b/NEsper.Core/NEsper.Core/view/internals/RouteResultViewHandlerAll.cs new file mode 100755 index 000000000..b16145700 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/internals/RouteResultViewHandlerAll.cs @@ -0,0 +1,92 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.core.start; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.view.internals +{ + /// + /// Handler for split-stream evaluating the all where-clauses and their matching select-clauses. + /// + public class RouteResultViewHandlerAll : RouteResultViewHandlerBase + { + public RouteResultViewHandlerAll( + EPStatementHandle epStatementHandle, + InternalEventRouter internalEventRouter, + TableStateInstance[] tableStateInstances, + EPStatementStartMethodOnTriggerItem[] items, + ResultSetProcessor[] processors, + ExprEvaluator[] whereClauses, + AgentInstanceContext agentInstanceContext) + : base( + epStatementHandle, internalEventRouter, tableStateInstances, items, processors, whereClauses, + agentInstanceContext) + { + } + + public override bool Handle(EventBean theEvent, ExprEvaluatorContext exprEvaluatorContext) + { + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().QSplitStream(true, theEvent, WhereClauses); + } + + bool isHandled = false; + for (int i = 0; i < WhereClauses.Length; i++) + { + EPStatementStartMethodOnTriggerItem currentItem = Items[i]; + EventsPerStream[0] = theEvent; + + // handle no-contained-event evaluation + if (currentItem.PropertyEvaluator == null) + { + isHandled |= ProcessAllCurrentEvent(i, exprEvaluatorContext); + } + else + { + // handle contained-event evaluation + EventBean[] containeds = currentItem.PropertyEvaluator.GetProperty( + EventsPerStream[0], exprEvaluatorContext); + if (containeds == null || containeds.Length == 0) + { + continue; + } + + foreach (EventBean contained in containeds) + { + EventsPerStream[0] = contained; + isHandled |= ProcessAllCurrentEvent(i, exprEvaluatorContext); + } + } + } + + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().ASplitStream(true, isHandled); + } + return isHandled; + } + + private bool ProcessAllCurrentEvent(int index, ExprEvaluatorContext exprEvaluatorContext) + { + bool pass = CheckWhereClauseCurrentEvent(index, exprEvaluatorContext); + if (!pass) + { + return false; + } + return MayRouteCurrentEvent(index, exprEvaluatorContext); + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/view/internals/RouteResultViewHandlerBase.cs b/NEsper.Core/NEsper.Core/view/internals/RouteResultViewHandlerBase.cs new file mode 100755 index 000000000..64988c673 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/internals/RouteResultViewHandlerBase.cs @@ -0,0 +1,121 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.client.annotation; +using com.espertech.esper.collection; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.core.start; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.util; + +namespace com.espertech.esper.view.internals +{ + public abstract class RouteResultViewHandlerBase : RouteResultViewHandler + { + protected readonly InternalEventRouter InternalEventRouter; + protected readonly EPStatementStartMethodOnTriggerItem[] Items; + protected readonly EPStatementHandle EPStatementHandle; + protected readonly ResultSetProcessor[] Processors; + protected readonly ExprEvaluator[] WhereClauses; + protected readonly EventBean[] EventsPerStream = new EventBean[1]; + protected readonly AgentInstanceContext AgentInstanceContext; + protected readonly bool Audit; + private readonly TableStateInstance[] _tableStateInstances; + + protected RouteResultViewHandlerBase( + EPStatementHandle epStatementHandle, + InternalEventRouter internalEventRouter, + TableStateInstance[] tableStateInstances, + EPStatementStartMethodOnTriggerItem[] items, + ResultSetProcessor[] processors, + ExprEvaluator[] whereClauses, + AgentInstanceContext agentInstanceContext) + { + InternalEventRouter = internalEventRouter; + _tableStateInstances = tableStateInstances; + Items = items; + EPStatementHandle = epStatementHandle; + Processors = processors; + WhereClauses = whereClauses; + AgentInstanceContext = agentInstanceContext; + Audit = AuditEnum.INSERT.GetAudit(agentInstanceContext.StatementContext.Annotations) != null; + } + + protected bool CheckWhereClauseCurrentEvent(int index, ExprEvaluatorContext exprEvaluatorContext) + { + bool pass = true; + + if (WhereClauses[index] != null) + { + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().QSplitStreamWhere(index); + } + + var passEvent = WhereClauses[index].Evaluate(new EvaluateParams(EventsPerStream, true, exprEvaluatorContext)); + if ((passEvent == null) || (false.Equals(passEvent))) + { + pass = false; + } + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().ASplitStreamWhere(pass); + } + } + + return pass; + } + + protected bool MayRouteCurrentEvent(int index, ExprEvaluatorContext exprEvaluatorContext) + { + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().QSplitStreamRoute(index); + } + UniformPair result = Processors[index].ProcessViewResult(EventsPerStream, null, false); + bool routed = false; + if ((result != null) && (result.First != null) && (result.First.Length > 0)) + { + Route(result.First[0], index, exprEvaluatorContext); + routed = true; + } + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().ASplitStreamRoute(); + } + return routed; + } + + private void Route(EventBean routed, int index, ExprEvaluatorContext exprEvaluatorContext) + { + if (Audit) + { + AuditPath.AuditInsertInto(AgentInstanceContext.EngineURI, AgentInstanceContext.StatementName, routed); + } + TableStateInstance tableStateInstance = _tableStateInstances[index]; + if (tableStateInstance != null) + { + tableStateInstance.AddEventUnadorned(routed); + } + else + { + bool isNamedWindowInsert = Items[index].IsNamedWindowInsert; + InternalEventRouter.Route( + routed, EPStatementHandle, AgentInstanceContext.StatementContext.InternalEventEngineRouteDest, + exprEvaluatorContext, isNamedWindowInsert); + } + } + + public abstract bool Handle(EventBean theEvent, ExprEvaluatorContext exprEvaluatorContext); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/view/internals/RouteResultViewHandlerFirst.cs b/NEsper.Core/NEsper.Core/view/internals/RouteResultViewHandlerFirst.cs new file mode 100755 index 000000000..296741908 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/internals/RouteResultViewHandlerFirst.cs @@ -0,0 +1,103 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.core.start; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.table.mgmt; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.view.internals +{ + /// + /// Handler for split-stream evaluating the first where-clause matching select-clause. + /// + public class RouteResultViewHandlerFirst : RouteResultViewHandlerBase + { + public RouteResultViewHandlerFirst( + EPStatementHandle epStatementHandle, + InternalEventRouter internalEventRouter, + TableStateInstance[] tableStateInstances, + EPStatementStartMethodOnTriggerItem[] items, + ResultSetProcessor[] processors, + ExprEvaluator[] whereClauses, + AgentInstanceContext agentInstanceContext) + : base( + epStatementHandle, internalEventRouter, tableStateInstances, items, processors, whereClauses, + agentInstanceContext) + { + } + + public override bool Handle(EventBean theEvent, ExprEvaluatorContext exprEvaluatorContext) + { + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().QSplitStream(false, theEvent, WhereClauses); + } + + int index = -1; + + for (int i = 0; i < WhereClauses.Length; i++) + { + EPStatementStartMethodOnTriggerItem item = Items[i]; + EventsPerStream[0] = theEvent; + + // handle no contained-event evaluation + if (item.PropertyEvaluator == null) + { + bool pass = CheckWhereClauseCurrentEvent(i, exprEvaluatorContext); + if (pass) + { + index = i; + break; + } + } + else + { + // need to get contained events first + EventBean[] containeds = Items[i].PropertyEvaluator.GetProperty( + EventsPerStream[0], exprEvaluatorContext); + if (containeds == null || containeds.Length == 0) + { + continue; + } + + foreach (EventBean contained in containeds) + { + EventsPerStream[0] = contained; + bool pass = CheckWhereClauseCurrentEvent(i, exprEvaluatorContext); + if (pass) + { + index = i; + break; + } + } + + if (index != -1) + { + break; + } + } + } + + if (index != -1) + { + MayRouteCurrentEvent(index, exprEvaluatorContext); + } + + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().ASplitStream(false, index != -1); + } + return index != -1; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/view/internals/SingleStreamDispatchView.cs b/NEsper.Core/NEsper.Core/view/internals/SingleStreamDispatchView.cs new file mode 100755 index 000000000..7a83bb4f0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/internals/SingleStreamDispatchView.cs @@ -0,0 +1,52 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.core.service; + +namespace com.espertech.esper.view.internals +{ + /// + /// View to dispatch for a single stream (no join). + /// + public sealed class SingleStreamDispatchView : ViewSupport, EPStatementDispatch + { + private bool _hasData = false; + private readonly FlushedEventBuffer _newDataBuffer = new FlushedEventBuffer(); + private readonly FlushedEventBuffer _oldDataBuffer = new FlushedEventBuffer(); + + public override EventType EventType + { + get { return Parent.EventType; } + } + + public override IEnumerator GetEnumerator() + { + return Parent.GetEnumerator(); + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + _newDataBuffer.Add(newData); + _oldDataBuffer.Add(oldData); + _hasData = true; + } + + public void Execute() + { + if (_hasData) + { + _hasData = false; + UpdateChildren(_newDataBuffer.GetAndFlush(), _oldDataBuffer.GetAndFlush()); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/internals/UnionAsymetricView.cs b/NEsper.Core/NEsper.Core/view/internals/UnionAsymetricView.cs new file mode 100755 index 000000000..dd39905b6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/internals/UnionAsymetricView.cs @@ -0,0 +1,361 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; + +namespace com.espertech.esper.view.internals +{ + /// + /// A view that represents a union of multiple data windows wherein at least one is asymetric: + /// it does not present a insert stream for each insert stream event received. + /// + /// The view is parameterized by two or more data windows. From an external viewpoint, the + /// view retains all events that is in any of the data windows (a union). + /// + public class UnionAsymetricView + : ViewSupport + , LastPostObserver + , CloneableView + , StoppableView + , DataWindowView + , ViewDataVisitableContainer + , ViewContainer + { + private readonly AgentInstanceViewFactoryChainContext _agentInstanceViewFactoryContext; + private readonly UnionViewFactory _unionViewFactory; + private readonly EventType _eventType; + private readonly View[] _views; + private readonly EventBean[][] _oldEventsPerView; + private readonly RefCountedSet _unionWindow; + private readonly IList _removalEvents = new List(); + private readonly ArrayDeque _newEvents = new ArrayDeque(); + + private EventBean[] _newDataChildView; + private bool _isHasRemovestreamData; + private bool _isRetainObserverEvents; + private bool _isDiscardObserverEvents; + + /// + /// Ctor. + /// + /// The agent instance view factory context. + /// the view factory + /// the parent event type + /// the list of data window views + public UnionAsymetricView(AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext, UnionViewFactory factory, EventType eventType, IList viewList) + { + _agentInstanceViewFactoryContext = agentInstanceViewFactoryContext; + _unionViewFactory = factory; + _eventType = eventType; + _views = viewList.ToArray(); + _unionWindow = new RefCountedSet(); + _oldEventsPerView = new EventBean[viewList.Count][]; + + for (var i = 0; i < viewList.Count; i++) + { + var view = new LastPostObserverView(i); + _views[i].RemoveAllViews(); + _views[i].AddView(view); + view.Observer = this; + } + + // recover + for (var i = 0; i < _views.Length; i++) + { + var viewSnapshot = _views[i].GetEnumerator(); + while (viewSnapshot.MoveNext()) + { + var theEvent = viewSnapshot.Current; + _unionWindow.Add(theEvent); + } + } + } + + public View[] ViewContained + { + get { return _views; } + } + + public View CloneView() + { + return _unionViewFactory.MakeView(_agentInstanceViewFactoryContext); + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + // handle remove stream + OneEventCollection oldDataColl = null; + EventBean[] newDataPosted = null; + if (oldData != null) + { + _isDiscardObserverEvents = true; // disable reaction logic in observer + + try + { + foreach (var view in _views) + { + view.Update(null, oldData); + } + } + finally + { + _isDiscardObserverEvents = false; + } + + // remove from union + foreach (var oldEvent in oldData) + { + _unionWindow.RemoveAll(oldEvent); + } + + oldDataColl = new OneEventCollection(); + oldDataColl.Add(oldData); + } + + // add new event to union + if (newData != null) + { + var removedByView = new bool[newData.Length, _views.Length]; + foreach (var newEvent in newData) + { + _unionWindow.Add(newEvent, _views.Length); + } + + // new events must go to all views + // old events, such as when removing from a named window, get removed from all views + _isHasRemovestreamData = false; // changed by observer logic to indicate new data + _isRetainObserverEvents = true; // enable retain logic in observer + try + { + for (var viewIndex = 0; viewIndex < _views.Length; viewIndex++) + { + var view = _views[viewIndex]; + view.Update(newData, null); + + // first-X asymetric view post no insert stream for events that get dropped, remove these + if (_newDataChildView != null) + { + for (var i = 0; i < newData.Length; i++) + { + var found = false; + for (var j = 0; j < _newDataChildView.Length; j++) + { + if (_newDataChildView[i] == newData[i]) + { + found = true; + break; + } + } + if (!found) + { + removedByView[i, viewIndex] = true; + } + } + } + else + { + for (var i = 0; i < newData.Length; i++) + { + removedByView[i, viewIndex] = true; + } + } + } + } + finally + { + _isRetainObserverEvents = false; + } + + // determine removed events, those that have a "true" in the remove by view index for all views + _removalEvents.Clear(); + for (var i = 0; i < newData.Length; i++) + { + var allTrue = true; + for (var j = 0; j < _views.Length; j++) + { + if (!removedByView[i, j]) + { + allTrue = false; + break; + } + } + if (allTrue) + { + _removalEvents.Add(newData[i]); + _unionWindow.RemoveAll(newData[i]); + } + } + + // remove if any + if (_removalEvents.IsNotEmpty()) + { + _isDiscardObserverEvents = true; + var viewOldData = _removalEvents.ToArray(); + try + { + for (var j = 0; j < _views.Length; j++) + { + _views[j].Update(null, viewOldData); + } + } + finally + { + _isDiscardObserverEvents = false; + } + } + + // see if any child view has removed any events. + // if there was an insert stream, handle pushed-out events + if (_isHasRemovestreamData) + { + IList removedEvents = null; + + // process each buffer + for (var i = 0; i < _oldEventsPerView.Length; i++) + { + if (_oldEventsPerView[i] == null) + { + continue; + } + + var viewOldData = _oldEventsPerView[i]; + _oldEventsPerView[i] = null; // clear entry + + // remove events for union, if the last event was removed then add it + foreach (var old in viewOldData) + { + var isNoMoreRef = _unionWindow.Remove(old); + if (isNoMoreRef) + { + if (removedEvents == null) + { + _removalEvents.Clear(); + removedEvents = _removalEvents; + } + removedEvents.Add(old); + } + } + } + + if (removedEvents != null) + { + if (oldDataColl == null) + { + oldDataColl = new OneEventCollection(); + } + foreach (var oldItem in removedEvents) + { + oldDataColl.Add(oldItem); + } + } + } + + _newEvents.Clear(); + for (var i = 0; i < newData.Length; i++) + { + if (!_removalEvents.Contains(newData[i])) + { + _newEvents.Add(newData[i]); + } + } + + if (_newEvents.IsNotEmpty()) + { + newDataPosted = _newEvents.ToArray(); + } + } + + // indicate new and, possibly, old data + if (HasViews && ((newDataPosted != null) || (oldDataColl != null))) + { + UpdateChildren(newDataPosted, oldDataColl != null ? oldDataColl.ToArray() : null); + } + } + + public override EventType EventType + { + get { return _eventType; } + } + + public override IEnumerator GetEnumerator() + { + return _unionWindow.Keys.GetEnumerator(); + } + + public void NewData(int streamId, EventBean[] newEvents, EventBean[] oldEvents) + { + _newDataChildView = newEvents; + + if ((oldEvents == null) || (_isDiscardObserverEvents)) + { + return; + } + + if (_isRetainObserverEvents) + { + _oldEventsPerView[streamId] = oldEvents; + _isHasRemovestreamData = true; + return; + } + + // handle time-based removal + IList removedEvents = null; + + // remove events for union, if the last event was removed then add it + foreach (var old in oldEvents) + { + var isNoMoreRef = _unionWindow.Remove(old); + if (isNoMoreRef) + { + if (removedEvents == null) + { + _removalEvents.Clear(); + removedEvents = _removalEvents; + } + removedEvents.Add(old); + } + } + + if (removedEvents != null) + { + var removed = removedEvents.ToArray(); + UpdateChildren(null, removed); + } + } + + public void Stop() + { + foreach (var view in _views.OfType()) + { + view.Stop(); + } + } + + public void VisitViewContainer(ViewDataVisitorContained viewDataVisitor) + { + IntersectDefaultView.VisitViewContained(viewDataVisitor, _unionViewFactory, _views); + } + + public void VisitView(ViewDataVisitor viewDataVisitor) + { + throw new UnsupportedOperationException(); + } + + public ViewFactory ViewFactory + { + get { return _unionViewFactory; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/internals/UnionView.cs b/NEsper.Core/NEsper.Core/view/internals/UnionView.cs new file mode 100755 index 000000000..51dff9dc0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/internals/UnionView.cs @@ -0,0 +1,291 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.util; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.view.internals +{ + /// + /// A view that represents a union of multiple data windows. + /// + /// The view is parameterized by two or more data windows. From an external viewpoint, + /// the view retains all events that is in any of the data windows (a union). + /// + public class UnionView + : ViewSupport + , LastPostObserver + , CloneableView + , StoppableView + , DataWindowView + , ViewDataVisitableContainer + , ViewContainer + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly AgentInstanceViewFactoryChainContext _agentInstanceViewFactoryContext; + private readonly UnionViewFactory _unionViewFactory; + private readonly EventType _eventType; + private readonly View[] _views; + private readonly EventBean[][] _oldEventsPerView; + private readonly RefCountedSet _unionWindow; + private readonly IList _removalEvents = new List(); + + private bool _isHasRemovestreamData; + private bool _isRetainObserverEvents; + private bool _isDiscardObserverEvents; + + /// + /// Ctor. + /// + /// The agent instance view factory context. + /// the view factory + /// the parent event type + /// the list of data window views + public UnionView( + AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext, + UnionViewFactory factory, + EventType eventType, + IList viewList) + { + _agentInstanceViewFactoryContext = agentInstanceViewFactoryContext; + _unionViewFactory = factory; + _eventType = eventType; + _views = viewList.ToArray(); + _unionWindow = new RefCountedSet(); + _oldEventsPerView = new EventBean[viewList.Count][]; + + for (int i = 0; i < viewList.Count; i++) + { + var view = new LastPostObserverView(i); + _views[i].RemoveAllViews(); + _views[i].AddView(view); + view.Observer = this; + } + + // recover + for (int i = 0; i < _views.Length; i++) + { + var viewSnapshot = _views[i].GetEnumerator(); + while (viewSnapshot.MoveNext()) + { + _unionWindow.Add(viewSnapshot.Current); + } + } + } + + public View[] ViewContained + { + get { return _views; } + } + + public View CloneView() + { + return _unionViewFactory.MakeView(_agentInstanceViewFactoryContext); + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + using (Instrument.With( + i => i.QViewProcessIRStream(this, _unionViewFactory.ViewName, newData, oldData), + i => i.AViewProcessIRStream())) + { + OneEventCollection oldDataColl = null; + if (oldData != null) + { + _isDiscardObserverEvents = true; // disable reaction logic in observer + + try + { + foreach (View view in _views) + { + view.Update(null, oldData); + } + } + finally + { + _isDiscardObserverEvents = false; + } + + // remove from union + foreach (EventBean oldEvent in oldData) + { + _unionWindow.RemoveAll(oldEvent); + } + + oldDataColl = new OneEventCollection(); + oldDataColl.Add(oldData); + } + + // add new event to union + if (newData != null) + { + foreach (EventBean newEvent in newData) + { + _unionWindow.Add(newEvent, _views.Length); + } + + // new events must go to all views + // old events, such as when removing from a named window, get removed from all views + _isHasRemovestreamData = false; // changed by observer logic to indicate new data + _isRetainObserverEvents = true; // enable retain logic in observer + try + { + foreach (View view in _views) + { + view.Update(newData, null); + } + } + finally + { + _isRetainObserverEvents = false; + } + + // see if any child view has removed any events. + // if there was an insert stream, handle pushed-out events + if (_isHasRemovestreamData) + { + IList removedEvents = null; + + // process each buffer + for (int i = 0; i < _oldEventsPerView.Length; i++) + { + if (_oldEventsPerView[i] == null) + { + continue; + } + + EventBean[] viewOldData = _oldEventsPerView[i]; + _oldEventsPerView[i] = null; // clear entry + + // remove events for union, if the last event was removed then add it + foreach (EventBean old in viewOldData) + { + bool isNoMoreRef = _unionWindow.Remove(old); + if (isNoMoreRef) + { + if (removedEvents == null) + { + _removalEvents.Clear(); + removedEvents = _removalEvents; + } + removedEvents.Add(old); + } + } + } + + if (removedEvents != null) + { + if (oldDataColl == null) + { + oldDataColl = new OneEventCollection(); + } + foreach (EventBean oldItem in removedEvents) + { + oldDataColl.Add(oldItem); + } + } + } + } + + if (HasViews) + { + // indicate new and, possibly, old data + EventBean[] oldEvents = oldDataColl != null ? oldDataColl.ToArray() : null; + Instrument.With( + i => i.QViewIndicate(this, _unionViewFactory.ViewName, newData, oldEvents), + i => i.AViewIndicate(), + () => UpdateChildren(newData, oldEvents)); + } + } + } + + public override EventType EventType + { + get { return _eventType; } + } + + public override IEnumerator GetEnumerator() + { + return _unionWindow.Keys.GetEnumerator(); + } + + public void NewData(int streamId, EventBean[] newEvents, EventBean[] oldEvents) + { + if ((oldEvents == null) || (_isDiscardObserverEvents)) + { + return; + } + + if (_isRetainObserverEvents) + { + _oldEventsPerView[streamId] = oldEvents; + _isHasRemovestreamData = true; + return; + } + + // handle time-based removal + IList removedEvents = null; + + // remove events for union, if the last event was removed then add it + foreach (EventBean old in oldEvents) + { + bool isNoMoreRef = _unionWindow.Remove(old); + if (isNoMoreRef) + { + if (removedEvents == null) + { + _removalEvents.Clear(); + removedEvents = _removalEvents; + } + removedEvents.Add(old); + } + } + + if (removedEvents != null) + { + EventBean[] removed = removedEvents.ToArray(); + Instrument.With( + i => i.QViewIndicate(this, _unionViewFactory.ViewName, null, removed), + i => i.AViewIndicate(), + () => UpdateChildren(null, removed)); + } + } + + public void Stop() + { + foreach (var view in _views.OfType()) + { + view.Stop(); + } + } + + public void VisitViewContainer(ViewDataVisitorContained viewDataVisitor) + { + IntersectDefaultView.VisitViewContained(viewDataVisitor, _unionViewFactory, _views); + } + + public void VisitView(ViewDataVisitor viewDataVisitor) + { + throw new UnsupportedOperationException(); + } + + public ViewFactory ViewFactory + { + get { return _unionViewFactory; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/internals/UnionViewFactory.cs b/NEsper.Core/NEsper.Core/view/internals/UnionViewFactory.cs new file mode 100755 index 000000000..21e284faa --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/internals/UnionViewFactory.cs @@ -0,0 +1,98 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.view.internals +{ + /// + /// Factory for union-views. + /// + public class UnionViewFactory + : ViewFactory + , DataWindowViewFactory + , ViewFactoryContainer + { + /// The event type. + private EventType _parentEventType; + + /// The view factories. + private IList _viewFactories; + + /// Ctor. Dependencies injected after reflective instantiation. + public UnionViewFactory() + { + } + + /// Sets the parent event type. + /// type + public EventType ParentEventType + { + set { _parentEventType = value; } + get { return _parentEventType; } + } + + /// Sets the view factories. + /// factories + public IList ViewFactories + { + set { _viewFactories = value; } + get { return _viewFactories; } + } + + public void SetViewParameters(ViewFactoryContext viewFactoryContext, IList viewParameters) + { + } + + public void Attach(EventType parentEventType, StatementContext statementContext, ViewFactory optionalParentFactory, IList parentViewFactories) + { + } + + public View MakeView(AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext) + { + bool hasAsymetric = false; + IList views = new List(); + foreach (ViewFactory viewFactory in _viewFactories) + { + views.Add(viewFactory.MakeView(agentInstanceViewFactoryContext)); + hasAsymetric |= viewFactory is AsymetricDataWindowViewFactory; + } + if (hasAsymetric) + { + return new UnionAsymetricView(agentInstanceViewFactoryContext, this, _parentEventType, views); + } + return new UnionView(agentInstanceViewFactoryContext, this, _parentEventType, views); + } + + public EventType EventType + { + get { return _parentEventType; } + } + + public bool CanReuse(View view, AgentInstanceContext agentInstanceContext) + { + return false; + } + + public string ViewName + { + get { return IntersectViewFactory.GetViewNameUnionIntersect(false, _viewFactories); } + } + + public ICollection ViewFactoriesContained + { + get { return _viewFactories; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/internals/ViewContainer.cs b/NEsper.Core/NEsper.Core/view/internals/ViewContainer.cs new file mode 100755 index 000000000..ef9b8fe3d --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/internals/ViewContainer.cs @@ -0,0 +1,15 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.view.internals +{ + internal interface ViewContainer + { + View[] ViewContained { get; } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/view/internals/ViewFactoryContainer.cs b/NEsper.Core/NEsper.Core/view/internals/ViewFactoryContainer.cs new file mode 100755 index 000000000..279408597 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/internals/ViewFactoryContainer.cs @@ -0,0 +1,17 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +namespace com.espertech.esper.view.internals +{ + internal interface ViewFactoryContainer + { + ICollection ViewFactoriesContained { get; } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/view/stat/BaseBivariateStatisticsView.cs b/NEsper.Core/NEsper.Core/view/stat/BaseBivariateStatisticsView.cs new file mode 100755 index 000000000..a50dbe59d --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/stat/BaseBivariateStatisticsView.cs @@ -0,0 +1,221 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.view.stat +{ + /// + /// View for computing statistics that require 2 input variable arrays containing X and Y datapoints. + /// Subclasses compute correlation or regression values, for instance. + /// + public abstract class BaseBivariateStatisticsView : ViewSupport, DerivedValueView + { + private const String NAME = "Statistics"; + + private readonly ViewFactory _viewFactory; + + /// This bean can be overridden by subclasses providing extra values such as correlation, regression. + private BaseStatisticsBean _statisticsBean = new BaseStatisticsBean(); + + private readonly ExprNode _expressionX; + private readonly ExprNode _expressionY; + private readonly ExprEvaluator _expressionXEval; + private readonly ExprEvaluator _expressionYEval; + private readonly EventBean[] _eventsPerStream = new EventBean[1]; + + /// Services required by implementing classes. + protected readonly AgentInstanceContext AgentInstanceContext; + + /// Additional properties. + private readonly StatViewAdditionalProps _additionalProps; + + /// Event type. + protected readonly EventType _eventType; + + private Object[] _lastValuesEventNew; + private EventBean _lastNewEvent; + + /// Populate bean. + /// results + /// event adapters + /// type + /// additional props + /// decoration values + /// bean + protected abstract EventBean PopulateMap(BaseStatisticsBean baseStatisticsBean, EventAdapterService eventAdapterService, EventType eventType, StatViewAdditionalProps additionalProps, Object[] decoration); + + /// + /// Constructor requires the name of the two fields to use in the parent view to compute the statistics. + /// + /// The view factory. + /// contains required view services + /// is the expression to get the X values from + /// is the expression to get the Y values from + /// type of event + /// additional props + protected BaseBivariateStatisticsView( + ViewFactory viewFactory, + AgentInstanceContext agentInstanceContext, + ExprNode expressionX, + ExprNode expressionY, + EventType eventType, + StatViewAdditionalProps additionalProps) + { + _viewFactory = viewFactory; + AgentInstanceContext = agentInstanceContext; + _expressionX = expressionX; + _expressionXEval = expressionX.ExprEvaluator; + _expressionY = expressionY; + _expressionYEval = expressionY.ExprEvaluator; + _eventType = eventType; + _additionalProps = additionalProps; + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + using (Instrument.With( + i => i.QViewProcessIRStream(this, NAME, newData, oldData), + i => i.AViewProcessIRStream())) + { + // If we have child views, keep a reference to the old values, so we can fireStatementStopped them as old data event. + EventBean oldValues = null; + if (_lastNewEvent == null) + { + if (HasViews) + { + oldValues = PopulateMap(_statisticsBean, AgentInstanceContext.StatementContext.EventAdapterService, _eventType, _additionalProps, _lastValuesEventNew); + } + } + + var evaluateParams = new EvaluateParams(_eventsPerStream, true, AgentInstanceContext); + + // add data points to the bean + if (newData != null) + { + for (var i = 0; i < newData.Length; i++) + { + _eventsPerStream[0] = newData[i]; + var xnum = _expressionXEval.Evaluate(evaluateParams); + var ynum = _expressionYEval.Evaluate(evaluateParams); + if (xnum != null && ynum != null) + { + var x = xnum.AsDouble(); + var y = ynum.AsDouble(); + _statisticsBean.AddPoint(x, y); + } + } + + if ((_additionalProps != null) && (newData.Length != 0)) + { + if (_lastValuesEventNew == null) + { + _lastValuesEventNew = new Object[_additionalProps.AdditionalExpr.Length]; + } + for (var val = 0; val < _additionalProps.AdditionalExpr.Length; val++) + { + _lastValuesEventNew[val] = _additionalProps.AdditionalExpr[val].Evaluate(evaluateParams); + } + } + } + + // remove data points from the bean + if (oldData != null) + { + for (var i = 0; i < oldData.Length; i++) + { + _eventsPerStream[0] = oldData[i]; + var xnum = _expressionXEval.Evaluate(evaluateParams); + var ynum = _expressionYEval.Evaluate(evaluateParams); + if (xnum != null && ynum != null) + { + var x = xnum.AsDouble(); + var y = ynum.AsDouble(); + _statisticsBean.RemovePoint(x, y); + } + } + } + + // If there are child view, fireStatementStopped Update method + if (HasViews) + { + var newDataMap = PopulateMap(_statisticsBean, AgentInstanceContext.StatementContext.EventAdapterService, _eventType, _additionalProps, _lastValuesEventNew); + var newEvents = new EventBean[] { newDataMap }; + EventBean[] oldEvents; + if (_lastNewEvent == null) + { + oldEvents = new EventBean[] { oldValues }; + } + else + { + oldEvents = new EventBean[] { _lastNewEvent }; + } + + Instrument.With( + i => i.QViewIndicate(this, NAME, newEvents, oldEvents), + i => i.AViewIndicate(), + () => UpdateChildren(newEvents, oldEvents)); + + _lastNewEvent = newDataMap; + } + } + } + + public override IEnumerator GetEnumerator() + { + yield return PopulateMap(_statisticsBean, + AgentInstanceContext.StatementContext.EventAdapterService, + _eventType, _additionalProps, _lastValuesEventNew); + } + + /// Returns the expression supplying X data points. + /// X expression + public ExprNode ExpressionX + { + get { return _expressionX; } + } + + /// Returns the expression supplying Y data points. + /// Y expression + public ExprNode ExpressionY + { + get { return _expressionY; } + } + + public BaseStatisticsBean StatisticsBean + { + get { return _statisticsBean; } + internal set { _statisticsBean = value; } + } + + public object[] LastValuesEventNew + { + get { return _lastValuesEventNew; } + set { _lastValuesEventNew = value; } + } + + public StatViewAdditionalProps AdditionalProps + { + get { return _additionalProps; } + } + + public ViewFactory ViewFactory + { + get { return _viewFactory; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/stat/BaseStatisticsBean.cs b/NEsper.Core/NEsper.Core/view/stat/BaseStatisticsBean.cs new file mode 100755 index 000000000..431efb384 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/stat/BaseStatisticsBean.cs @@ -0,0 +1,399 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + + +using System; + +using com.espertech.esper.client; + +namespace com.espertech.esper.view.stat +{ + /// + /// Bean for performing statistical calculations. The bean keeps sums of X and Y datapoints and sums on squares + /// that can be reused by subclasses. The bean calculates standard deviation (sample and population), variance, + /// average and sum. + /// + [Serializable] + public class BaseStatisticsBean : ICloneable + { + /// Calculates standard deviation for X based on the entire population given as arguments. + /// Equivalent to Microsoft Excel formula STDEVPA. + /// + /// standard deviation assuming population for X + /// + public double XStandardDeviationPop + { + get + { + if (DataPoints == 0) + { + return Double.NaN; + } + + double temp = (SumXSq - XSum * XSum / DataPoints) / DataPoints; + return Math.Sqrt(temp); + } + } + + /// Calculates standard deviation for Y based on the entire population given as arguments. + /// Equivalent to Microsoft Excel formula STDEVPA. + /// + /// standard deviation assuming population for Y + /// + public double YStandardDeviationPop + { + get + { + if (DataPoints == 0) + { + return Double.NaN; + } + + double temp = (SumYSq - YSum * YSum / DataPoints) / DataPoints; + return Math.Sqrt(temp); + } + } + + /// Calculates standard deviation for X based on the sample data points supplied. + /// Equivalent to Microsoft Excel formula STDEV. + /// + /// standard deviation assuming sample for X + /// + public double XStandardDeviationSample + { + get + { + if (DataPoints < 2) + { + return Double.NaN; + } + + double variance = XVariance; + return Math.Sqrt(variance); + } + } + + /// Calculates standard deviation for Y based on the sample data points supplied. + /// Equivalent to Microsoft Excel formula STDEV. + /// + /// standard deviation assuming sample for Y + /// + public double YStandardDeviationSample + { + get + { + if (DataPoints < 2) + { + return Double.NaN; + } + + double variance = YVariance; + return Math.Sqrt(variance); + } + } + + /// Calculates standard deviation for X based on the sample data points supplied. + /// Equivalent to Microsoft Excel formula STDEV. + /// + /// variance as the square of the sample standard deviation for X + /// + public double XVariance + { + get + { + if (DataPoints < 2) + { + return Double.NaN; + } + + return (SumXSq - XSum * XSum / DataPoints) / (DataPoints - 1); + } + } + + /// Calculates standard deviation for Y based on the sample data points supplied. + /// Equivalent to Microsoft Excel formula STDEV. + /// + /// variance as the square of the sample standard deviation for Y + /// + public double YVariance + { + get + { + if (DataPoints < 2) + { + return Double.NaN; + } + + return (SumYSq - YSum * YSum / DataPoints) / (DataPoints - 1); + } + + } + /// Returns the number of data points. + /// number of data points + /// + public long N + { + get { return DataPoints; } + } + + /// + /// Gets or sets the number of data points. + /// + /// The number of data points. + public long DataPoints { get; set; } + + /// Returns the sum of all X data points. + /// sum of X data points + /// + public double XSum { get; set; } + + /// Returns the sum of all Y data points. + /// sum of Y data points + /// + public double YSum { get; set; } + + /// Returns the average of all X data points. + /// average of X data points + /// + public double XAverage + { + get + { + if (DataPoints == 0) + { + return Double.NaN; + } + + return XSum / DataPoints; + } + } + + /// Returns the average of all Y data points. + /// average of Y data points + /// + public double YAverage + { + get + { + if (DataPoints == 0) + { + return Double.NaN; + } + + return YSum / DataPoints; + } + } + + /// Returns the sum of all X data points. + /// sum of X data points + /// + public double SumX + { + get { return XSum; } + } + + /// Returns the sum of all Y data points. + /// sum of Y data points + /// + public double SumY + { + get { return YSum; } + } + + /// For use by subclasses, returns sum (X * X). + /// sum of X squared + /// + public double SumXSq { get; set; } + + /// For use by subclasses, returns sum (Y * Y). + /// sum of Y squared + /// + public double SumYSq { get; set; } + + /// For use by subclasses, returns sum (X * Y). + /// sum of X times Y + /// + public double SumXY { get; set; } + + + /// Returns the Y intercept. + /// Y intercept + + public double YIntercept + { + get + { + double slope = Slope; + + if (Double.IsNaN(slope)) { + return Double.NaN; + } + + return YSum/N - Slope*XSum/N; + } + } + + /// Returns the slope. + /// regression slope + + public double Slope + { + get + { + if (N == 0) { + return Double.NaN; + } + + double ssx = SumXSq - XSum*XSum/N; + + if (ssx == 0) { + return Double.NaN; + } + + double sp = SumXY - XSum*YSum/N; + + return sp/ssx; + } + } + + /// Return the correlation value for the two data series (Microsoft Excel function CORREL). + /// correlation value + + public double Correlation + { + get + { + if (N == 0) { + return Double.NaN; + } + + double dx = SumXSq - (XSum*XSum)/N; + double dy = SumYSq - (YSum*YSum)/N; + + if (dx == 0 || dy == 0) { + return Double.NaN; + } + + double sp = SumXY - XSum*YSum/N; + return sp/Math.Sqrt(dx*dy); + } + } + + /// + /// Initializes this instance. + /// + private void Initialize() + { + XSum = 0; + SumXSq = 0; + YSum = 0; + SumYSq = 0; + SumXY = 0; + DataPoints = 0; + } + + /// Add a data point for the X data set only. + /// is the X data point to add. + /// + public void AddPoint(double x) + { + DataPoints++; + XSum += x; + SumXSq += x * x; + } + + /// Add a data point. + /// is the X data point to add. + /// + /// is the Y data point to add. + /// + public void AddPoint(double x, double y) + { + DataPoints++; + XSum += x; + SumXSq += x * x; + YSum += y; + SumYSq += y * y; + SumXY += x * y; + } + + /// Remove a X data point only. + /// is the X data point to remove. + /// + public void RemovePoint(double x) + { + DataPoints--; + if (DataPoints <= 0) + { + Initialize(); + } + else + { + XSum -= x; + SumXSq -= x * x; + } + } + + /// Remove a data point. + /// is the X data point to remove. + /// + /// is the Y data point to remove. + /// + public void RemovePoint(double x, double y) + { + DataPoints--; + if (DataPoints <= 0) + { + Initialize(); + } + else + { + XSum -= x; + SumXSq -= x * x; + YSum -= y; + SumYSq -= y * y; + SumXY -= x * y; + } + } + + /// + /// Creates a new object that is a copy of the current instance. + /// + /// + /// A new object that is a copy of this instance. + /// + public virtual Object Clone() + { + try + { + return MemberwiseClone(); + } + catch (Exception e) + { + throw new EPException(e); + } + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override String ToString() + { + return + "datapoints=" + DataPoints + + " sumX=" + XSum + + " sumXSq=" + SumXSq + + " sumY=" + YSum + + " sumYSq=" + SumYSq + + " sumXY=" + SumXY; + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/stat/CorrelationView.cs b/NEsper.Core/NEsper.Core/view/stat/CorrelationView.cs new file mode 100755 index 000000000..c3c9dfba8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/stat/CorrelationView.cs @@ -0,0 +1,111 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events; + +namespace com.espertech.esper.view.stat +{ + /// + /// A view that calculates correlation on two fields. The view uses + /// internally a instance for the calculations, + /// it also returns this bean as the result. This class accepts most of its behaviour + /// from its parent, . + /// It adds the usage of the correlation bean and the appropriate schema. + /// + public sealed class CorrelationView + : BaseBivariateStatisticsView + , CloneableView + { + /// Constructor. + /// + /// contains required view services + /// is the expression providing X data points + /// is the expression providing X data points + /// event type + /// additional properties + public CorrelationView(ViewFactory viewFactory, AgentInstanceContext agentInstanceContext, ExprNode xExpression, ExprNode yExpression, EventType eventType, StatViewAdditionalProps additionalProps) + : base(viewFactory, agentInstanceContext, xExpression, yExpression, eventType, additionalProps) + { + } + + public View CloneView() + { + return new CorrelationView(ViewFactory, AgentInstanceContext, ExpressionX, ExpressionY, ((ViewSupport)this).EventType, AdditionalProps); + } + + protected override EventBean PopulateMap(BaseStatisticsBean baseStatisticsBean, + EventAdapterService eventAdapterService, + EventType eventType, + StatViewAdditionalProps additionalProps, + Object[] decoration) + { + return DoPopulateMap(baseStatisticsBean, eventAdapterService, eventType, additionalProps, decoration); + } + + /// Populate bean. + /// results + /// event wrapping + /// type to produce + /// addition properties + /// decoration values + /// bean + public static EventBean DoPopulateMap(BaseStatisticsBean baseStatisticsBean, + EventAdapterService eventAdapterService, + EventType eventType, + StatViewAdditionalProps additionalProps, + Object[] decoration) + { + IDictionary result = new Dictionary(); + result.Put(ViewFieldEnum.CORRELATION__CORRELATION.GetName(), baseStatisticsBean.Correlation); + if (additionalProps != null) + { + additionalProps.AddProperties(result, decoration); + } + + return eventAdapterService.AdapterForTypedMap(result, eventType); + } + + public override EventType EventType + { + get { return _eventType; } + } + + public override String ToString() + { + return GetType().FullName + + " fieldX=" + ExpressionX + + " fieldY=" + ExpressionY; + } + + /// + /// Creates the event type for this view. + /// + /// is the event adapter service + /// additional props + /// The stream num. + /// event type of view + public static EventType CreateEventType(StatementContext statementContext, StatViewAdditionalProps additionalProps, int streamNum) + { + IDictionary eventTypeMap = new Dictionary(); + eventTypeMap.Put(ViewFieldEnum.CORRELATION__CORRELATION.GetName(), typeof(double?)); + StatViewAdditionalProps.AddCheckDupProperties(eventTypeMap, additionalProps, + ViewFieldEnum.CORRELATION__CORRELATION); + String outputEventTypeName = statementContext.StatementId + "_correlview_" + streamNum; + return statementContext.EventAdapterService.CreateAnonymousMapType(outputEventTypeName, eventTypeMap, false); + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/stat/CorrelationViewFactory.cs b/NEsper.Core/NEsper.Core/view/stat/CorrelationViewFactory.cs new file mode 100755 index 000000000..4774b0f8f --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/stat/CorrelationViewFactory.cs @@ -0,0 +1,125 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.util; + +namespace com.espertech.esper.view.stat +{ + /// + /// Factory for instances. + /// + public class CorrelationViewFactory : ViewFactory + { + private IList _viewParameters; + private int _streamNumber; + + /// Property name of X field. + private ExprNode _expressionX; + + /// Property name of Y field. + private ExprNode _expressionY; + + /// Additional properties. + private StatViewAdditionalProps _additionalProps; + + /// Event type. + private EventType _eventType; + + public void SetViewParameters(ViewFactoryContext viewFactoryContext, IList expressionParameters) + { + _viewParameters = expressionParameters; + _streamNumber = viewFactoryContext.StreamNum; + } + + public void Attach(EventType parentEventType, StatementContext statementContext, ViewFactory optionalParentFactory, IList parentViewFactories) + { + ExprNode[] validated = ViewFactorySupport.Validate(ViewName, parentEventType, statementContext, _viewParameters, true); + if (validated.Length < 2) + { + throw new ViewParameterException(ViewParamMessage); + } + if ((!validated[0].ExprEvaluator.ReturnType.IsNumeric()) || (!validated[1].ExprEvaluator.ReturnType.IsNumeric())) + { + throw new ViewParameterException(ViewParamMessage); + } + + _expressionX = validated[0]; + _expressionY = validated[1]; + + _additionalProps = StatViewAdditionalProps.Make(validated, 2, parentEventType); + _eventType = CorrelationView.CreateEventType(statementContext, _additionalProps, _streamNumber); + } + + public View MakeView(AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext) + { + return new CorrelationView(this, agentInstanceViewFactoryContext.AgentInstanceContext, ExpressionX, ExpressionY, EventType, AdditionalProps); + } + + public EventType EventType + { + get { return _eventType; } + set { _eventType = value; } + } + + public bool CanReuse(View view, AgentInstanceContext agentInstanceContext) + { + if (!(view is CorrelationView)) + { + return false; + } + + if (_additionalProps != null) + { + return false; + } + + CorrelationView other = (CorrelationView)view; + if ((!ExprNodeUtility.DeepEquals(other.ExpressionX, _expressionX) || + (!ExprNodeUtility.DeepEquals(other.ExpressionY, _expressionY)))) + { + return false; + } + + return true; + } + + public string ViewName + { + get { return "CorrelationStatistics"; } + } + + private string ViewParamMessage + { + get { return ViewName + " view requires two expressions providing x and y values as properties"; } + } + + public ExprNode ExpressionX + { + get { return _expressionX; } + set { _expressionX = value; } + } + + public ExprNode ExpressionY + { + get { return _expressionY; } + set { _expressionY = value; } + } + + public StatViewAdditionalProps AdditionalProps + { + get { return _additionalProps; } + set { _additionalProps = value; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/stat/RegressionLinestView.cs b/NEsper.Core/NEsper.Core/view/stat/RegressionLinestView.cs new file mode 100755 index 000000000..988bc249a --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/stat/RegressionLinestView.cs @@ -0,0 +1,138 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events; + +namespace com.espertech.esper.view.stat +{ + /// + /// A view that calculates regression on two fields. The view uses internally a + /// instance for the calculations, it also returns this bean as the result. This class accepts most of its + /// behaviour from its parent, . + /// It adds the usage of the regression bean and the appropriate schema. + /// + public sealed class RegressionLinestView : BaseBivariateStatisticsView, CloneableView + { + /// + /// Constructor. + /// + /// + /// contains required view services + /// is the field name of the field providing X data points + /// is the field name of the field providing X data points + /// Type of the event. + /// The additional props. + public RegressionLinestView(ViewFactory viewFactory, AgentInstanceContext agentInstanceContext, ExprNode xFieldName, ExprNode yFieldName, EventType eventType, StatViewAdditionalProps additionalProps) + : base(viewFactory, agentInstanceContext, xFieldName, yFieldName, eventType, additionalProps) + { + } + + public View CloneView() + { + return new RegressionLinestView(ViewFactory, AgentInstanceContext, ExpressionX, ExpressionY, EventType, AdditionalProps); + } + + public override EventType EventType + { + get { return _eventType; } + } + + public override String ToString() + { + return GetType().FullName + + " fieldX=" + ExpressionX + + " fieldY=" + ExpressionY; + } + + protected override EventBean PopulateMap(BaseStatisticsBean baseStatisticsBean, + EventAdapterService eventAdapterService, + EventType eventType, + StatViewAdditionalProps additionalProps, + Object[] decoration) + { + return DoPopulateMap(baseStatisticsBean, eventAdapterService, eventType, additionalProps, decoration); + } + + public static EventBean DoPopulateMap(BaseStatisticsBean baseStatisticsBean, + EventAdapterService eventAdapterService, + EventType eventType, + StatViewAdditionalProps additionalProps, + Object[] decoration) + { + IDictionary result = new Dictionary(); + result.Put(ViewFieldEnum.REGRESSION__SLOPE.GetName(), baseStatisticsBean.Slope); + result.Put(ViewFieldEnum.REGRESSION__YINTERCEPT.GetName(), baseStatisticsBean.YIntercept); + result.Put(ViewFieldEnum.REGRESSION__XAVERAGE.GetName(), baseStatisticsBean.XAverage); + result.Put(ViewFieldEnum.REGRESSION__XSTANDARDDEVIATIONPOP.GetName(), baseStatisticsBean.XStandardDeviationPop); + result.Put(ViewFieldEnum.REGRESSION__XSTANDARDDEVIATIONSAMPLE.GetName(), baseStatisticsBean.XStandardDeviationSample); + result.Put(ViewFieldEnum.REGRESSION__XSUM.GetName(), baseStatisticsBean.XSum); + result.Put(ViewFieldEnum.REGRESSION__XVARIANCE.GetName(), baseStatisticsBean.XVariance); + result.Put(ViewFieldEnum.REGRESSION__YAVERAGE.GetName(), baseStatisticsBean.YAverage); + result.Put(ViewFieldEnum.REGRESSION__YSTANDARDDEVIATIONPOP.GetName(), baseStatisticsBean.YStandardDeviationPop); + result.Put(ViewFieldEnum.REGRESSION__YSTANDARDDEVIATIONSAMPLE.GetName(), baseStatisticsBean.YStandardDeviationSample); + result.Put(ViewFieldEnum.REGRESSION__YSUM.GetName(), baseStatisticsBean.YSum); + result.Put(ViewFieldEnum.REGRESSION__YVARIANCE.GetName(), baseStatisticsBean.YVariance); + result.Put(ViewFieldEnum.REGRESSION__DATAPOINTS.GetName(), baseStatisticsBean.DataPoints); + result.Put(ViewFieldEnum.REGRESSION__N.GetName(), baseStatisticsBean.N); + result.Put(ViewFieldEnum.REGRESSION__SUMX.GetName(), baseStatisticsBean.SumX); + result.Put(ViewFieldEnum.REGRESSION__SUMXSQ.GetName(), baseStatisticsBean.SumXSq); + result.Put(ViewFieldEnum.REGRESSION__SUMXY.GetName(), baseStatisticsBean.SumXY); + result.Put(ViewFieldEnum.REGRESSION__SUMY.GetName(), baseStatisticsBean.SumY); + result.Put(ViewFieldEnum.REGRESSION__SUMYSQ.GetName(), baseStatisticsBean.SumYSq); + if (additionalProps != null) + { + additionalProps.AddProperties(result, decoration); + } + return eventAdapterService.AdapterForTypedMap(result, eventType); + } + + /// + /// Creates the event type for this view. + /// + /// is the event adapter service + /// The additional props. + /// The stream num. + /// event type of view + internal static EventType CreateEventType(StatementContext statementContext, StatViewAdditionalProps additionalProps, int streamNum) + { + IDictionary eventTypeMap = new Dictionary(); + eventTypeMap.Put(ViewFieldEnum.REGRESSION__SLOPE.GetName(), typeof(double?)); + eventTypeMap.Put(ViewFieldEnum.REGRESSION__YINTERCEPT.GetName(), typeof(double?)); + eventTypeMap.Put(ViewFieldEnum.REGRESSION__XAVERAGE.GetName(), typeof(double?)); + eventTypeMap.Put(ViewFieldEnum.REGRESSION__XSTANDARDDEVIATIONPOP.GetName(), typeof(double?)); + eventTypeMap.Put(ViewFieldEnum.REGRESSION__XSTANDARDDEVIATIONSAMPLE.GetName(), typeof(double?)); + eventTypeMap.Put(ViewFieldEnum.REGRESSION__XSUM.GetName(), typeof(double?)); + eventTypeMap.Put(ViewFieldEnum.REGRESSION__XVARIANCE.GetName(), typeof(double?)); + eventTypeMap.Put(ViewFieldEnum.REGRESSION__YAVERAGE.GetName(), typeof(double?)); + eventTypeMap.Put(ViewFieldEnum.REGRESSION__YSTANDARDDEVIATIONPOP.GetName(), typeof(double?)); + eventTypeMap.Put(ViewFieldEnum.REGRESSION__YSTANDARDDEVIATIONSAMPLE.GetName(), typeof(double?)); + eventTypeMap.Put(ViewFieldEnum.REGRESSION__YSUM.GetName(), typeof(double?)); + eventTypeMap.Put(ViewFieldEnum.REGRESSION__YVARIANCE.GetName(), typeof(double?)); + eventTypeMap.Put(ViewFieldEnum.REGRESSION__DATAPOINTS.GetName(), typeof(long?)); + eventTypeMap.Put(ViewFieldEnum.REGRESSION__N.GetName(), typeof(long?)); + eventTypeMap.Put(ViewFieldEnum.REGRESSION__SUMX.GetName(), typeof(double?)); + eventTypeMap.Put(ViewFieldEnum.REGRESSION__SUMXSQ.GetName(), typeof(double?)); + eventTypeMap.Put(ViewFieldEnum.REGRESSION__SUMXY.GetName(), typeof(double?)); + eventTypeMap.Put(ViewFieldEnum.REGRESSION__SUMY.GetName(), typeof(double?)); + eventTypeMap.Put(ViewFieldEnum.REGRESSION__SUMYSQ.GetName(), typeof(double?)); + StatViewAdditionalProps.AddCheckDupProperties(eventTypeMap, additionalProps, + ViewFieldEnum.REGRESSION__SLOPE, ViewFieldEnum.REGRESSION__YINTERCEPT); + String outputEventTypeName = statementContext.StatementId + "_regview_" + streamNum; + return statementContext.EventAdapterService.CreateAnonymousMapType(outputEventTypeName, eventTypeMap, false); + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/stat/RegressionLinestViewFactory.cs b/NEsper.Core/NEsper.Core/view/stat/RegressionLinestViewFactory.cs new file mode 100755 index 000000000..2ffbcbe2c --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/stat/RegressionLinestViewFactory.cs @@ -0,0 +1,125 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.util; + +namespace com.espertech.esper.view.stat +{ + /// + /// Factory for instances. + /// + public class RegressionLinestViewFactory : ViewFactory + { + private IList _viewParameters; + private int _streamNumber; + + /// Expression X field. + private ExprNode _expressionX; + + /// Expression Y field. + private ExprNode _expressionY; + + private StatViewAdditionalProps _additionalProps; + + private EventType _eventType; + + public void SetViewParameters(ViewFactoryContext viewFactoryContext, IList expressionParameters) + { + _viewParameters = expressionParameters; + _streamNumber = viewFactoryContext.StreamNum; + } + + public void Attach(EventType parentEventType, StatementContext statementContext, ViewFactory optionalParentFactory, IList parentViewFactories) + { + ExprNode[] validated = ViewFactorySupport.Validate(ViewName, parentEventType, statementContext, _viewParameters, true); + + if (validated.Length < 2) + { + throw new ViewParameterException(ViewParamMessage); + } + if ((!validated[0].ExprEvaluator.ReturnType.IsNumeric()) || (!validated[1].ExprEvaluator.ReturnType.IsNumeric())) + { + throw new ViewParameterException(ViewParamMessage); + } + + _expressionX = validated[0]; + _expressionY = validated[1]; + + _additionalProps = StatViewAdditionalProps.Make(validated, 2, parentEventType); + _eventType = RegressionLinestView.CreateEventType(statementContext, _additionalProps, _streamNumber); + } + + public View MakeView(AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext) + { + return new RegressionLinestView( + this, agentInstanceViewFactoryContext.AgentInstanceContext, ExpressionX, ExpressionY, EventType, AdditionalProps); + } + + + public bool CanReuse(View view, AgentInstanceContext agentInstanceContext) + { + if (!(view is RegressionLinestView)) + { + return false; + } + + if (_additionalProps != null) + { + return false; + } + + RegressionLinestView myView = (RegressionLinestView)view; + if ((!ExprNodeUtility.DeepEquals(myView.ExpressionX, _expressionX)) || + (!ExprNodeUtility.DeepEquals(myView.ExpressionY, _expressionY))) + { + return false; + } + return true; + } + + public EventType EventType + { + get { return _eventType; } + set { _eventType = value; } + } + + public string ViewName + { + get { return "Regression"; } + } + + private string ViewParamMessage + { + get { return ViewName + " view requires two expressions providing x and y values as properties"; } + } + + public StatViewAdditionalProps AdditionalProps + { + get { return _additionalProps; } + set { _additionalProps = value; } + } + + public ExprNode ExpressionY + { + get { return _expressionY; } + set { _expressionY = value; } + } + + public ExprNode ExpressionX + { + get { return _expressionX; } + set { _expressionX = value; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/stat/StatViewAdditionalProps.cs b/NEsper.Core/NEsper.Core/view/stat/StatViewAdditionalProps.cs new file mode 100755 index 000000000..5b91dc48c --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/stat/StatViewAdditionalProps.cs @@ -0,0 +1,119 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.view.stat +{ + public class StatViewAdditionalProps + { + private readonly String[] _additionalProps; + private readonly ExprEvaluator[] _additionalExpr; + + private StatViewAdditionalProps(String[] additionalProps, ExprEvaluator[] additionalExpr) + { + _additionalProps = additionalProps; + _additionalExpr = additionalExpr; + } + + public string[] AdditionalProps + { + get { return _additionalProps; } + } + + public ExprEvaluator[] AdditionalExpr + { + get { return _additionalExpr; } + } + + public static StatViewAdditionalProps Make(ExprNode[] validated, int startIndex, EventType parentEventType) + { + if (validated.Length <= startIndex) + { + return null; + } + + IList additionalProps = new List(); + IList lastValueExpr = new List(); + var copyAllProperties = false; + + for (var i = startIndex; i < validated.Length; i++) + { + if (validated[i] is ExprWildcard) + { + copyAllProperties = true; + } + + additionalProps.Add(ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(validated[i])); + lastValueExpr.Add(validated[i].ExprEvaluator); + } + + if (copyAllProperties) + { + foreach (var propertyDescriptor in parentEventType.PropertyDescriptors) + { + if (propertyDescriptor.IsFragment) + { + continue; + } + additionalProps.Add(propertyDescriptor.PropertyName); + var getter = parentEventType.GetGetter(propertyDescriptor.PropertyName); + var type = propertyDescriptor.PropertyType; + ExprEvaluator exprEvaluator = new ProxyExprEvaluator + { + ProcEvaluate = evaluateParams => getter.Get(evaluateParams.EventsPerStream[0]), + ReturnType = type + }; + lastValueExpr.Add(exprEvaluator); + } + } + + var addPropsArr = additionalProps.ToArray(); + var valueExprArr = lastValueExpr.ToArray(); + return new StatViewAdditionalProps(addPropsArr, valueExprArr); + } + + public void AddProperties(IDictionary newDataMap, Object[] lastValuesEventNew) + { + if (lastValuesEventNew != null) + { + for (var i = 0; i < _additionalProps.Length; i++) + { + newDataMap.Put(_additionalProps[i], lastValuesEventNew[i]); + } + } + } + + public static void AddCheckDupProperties(IDictionary target, StatViewAdditionalProps addProps, params ViewFieldEnum[] builtin) + { + if (addProps == null) + { + return; + } + + for (var i = 0; i < addProps.AdditionalProps.Length; i++) + { + var name = addProps.AdditionalProps[i]; + for (var j = 0; j < builtin.Length; j++) + { + if (string.Equals(name, builtin[j].GetName(), StringComparison.InvariantCultureIgnoreCase)) + { + throw new ArgumentException("The property by name '" + name + "' overlaps the property name that the view provides"); + } + } + target.Put(name, addProps.AdditionalExpr[i].ReturnType); + } + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/stat/UnivariateStatisticsView.cs b/NEsper.Core/NEsper.Core/view/stat/UnivariateStatisticsView.cs new file mode 100755 index 000000000..fb22a6572 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/stat/UnivariateStatisticsView.cs @@ -0,0 +1,234 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.view.stat +{ + /// + /// View for computing statistics, which the view exposes via fields representing the sum, + /// count, standard deviation for sample and for population and variance. + /// + public class UnivariateStatisticsView + : ViewSupport + , CloneableView + , DerivedValueView + { + private readonly UnivariateStatisticsViewFactory _viewFactory; + private readonly AgentInstanceViewFactoryChainContext _agentInstanceContext; + private readonly ExprEvaluator _fieldExpressionEvaluator; + private readonly BaseStatisticsBean _baseStatisticsBean = new BaseStatisticsBean(); + + private EventBean _lastNewEvent; + private readonly EventBean[] _eventsPerStream = new EventBean[1]; + private Object[] _lastValuesEventNew; + + /// + /// Constructor requires the name of the field to use in the parent view to compute the statistics. + /// + /// The view factory. + /// The agent instance context. + public UnivariateStatisticsView(UnivariateStatisticsViewFactory viewFactory, AgentInstanceViewFactoryChainContext agentInstanceContext) + { + this._viewFactory = viewFactory; + this._agentInstanceContext = agentInstanceContext; + this._fieldExpressionEvaluator = viewFactory.FieldExpression.ExprEvaluator; + } + + public View CloneView() + { + return _viewFactory.MakeView(_agentInstanceContext); + } + + /// Returns field name of the field to report statistics on. + /// field name + public ExprNode FieldExpression + { + get { return _viewFactory.FieldExpression; } + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QViewProcessIRStream(this, UnivariateStatisticsViewFactory.NAME, newData, oldData); } + + // If we have child views, keep a reference to the old values, so we can Update them as old data event. + EventBean oldDataMap = null; + if (_lastNewEvent == null) + { + if (HasViews) + { + oldDataMap = PopulateMap(_baseStatisticsBean, _agentInstanceContext.StatementContext.EventAdapterService, _viewFactory.EventType, _viewFactory.AdditionalProps, _lastValuesEventNew); + } + } + + var evaluateParams = new EvaluateParams(_eventsPerStream, true, _agentInstanceContext); + + // add data points to the bean + if (newData != null) + { + for (int i = 0; i < newData.Length; i++) + { + _eventsPerStream[0] = newData[i]; + var pointnum = _fieldExpressionEvaluator.Evaluate(evaluateParams); + if (pointnum != null) + { + double point = pointnum.AsDouble(); + _baseStatisticsBean.AddPoint(point, 0); + } + } + + if ((_viewFactory.AdditionalProps != null) && (newData.Length != 0)) + { + if (_lastValuesEventNew == null) + { + _lastValuesEventNew = new Object[_viewFactory.AdditionalProps.AdditionalExpr.Length]; + } + for (int val = 0; val < _viewFactory.AdditionalProps.AdditionalExpr.Length; val++) + { + _lastValuesEventNew[val] = _viewFactory.AdditionalProps.AdditionalExpr[val].Evaluate(evaluateParams); + } + } + } + + // remove data points from the bean + if (oldData != null) + { + for (int i = 0; i < oldData.Length; i++) + { + _eventsPerStream[0] = oldData[i]; + var pointnum = _fieldExpressionEvaluator.Evaluate(evaluateParams); + if (pointnum != null) + { + double point = pointnum.AsDouble(); + _baseStatisticsBean.RemovePoint(point, 0); + } + } + } + + // If there are child view, call Update method + if (HasViews) + { + EventBean newDataMap = PopulateMap(_baseStatisticsBean, _agentInstanceContext.StatementContext.EventAdapterService, _viewFactory.EventType, _viewFactory.AdditionalProps, _lastValuesEventNew); + + EventBean[] oldEvents; + EventBean[] newEvents = new EventBean[] { newDataMap }; + if (_lastNewEvent == null) + { + oldEvents = new EventBean[] { oldDataMap }; + } + else + { + oldEvents = new EventBean[] { _lastNewEvent }; + } + + Instrument.With( + i => i.QViewIndicate(this, UnivariateStatisticsViewFactory.NAME, newEvents, oldEvents), + i => i.AViewIndicate(), + () => UpdateChildren(newEvents, oldEvents)); + + _lastNewEvent = newDataMap; + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AViewProcessIRStream(); } + } + + public override EventType EventType + { + get { return _viewFactory.EventType; } + } + + public override IEnumerator GetEnumerator() + { + yield return PopulateMap( + _baseStatisticsBean, + _agentInstanceContext.StatementContext.EventAdapterService, + _viewFactory.EventType, + _viewFactory.AdditionalProps, _lastValuesEventNew); + } + + public override String ToString() + { + return GetType().FullName + " fieldExpression=" + _viewFactory.FieldExpression; + } + + public static EventBean PopulateMap(BaseStatisticsBean baseStatisticsBean, + EventAdapterService eventAdapterService, + EventType eventType, + StatViewAdditionalProps additionalProps, + Object[] lastNewValues) + { + var result = new Dictionary(); + result.Put(ViewFieldEnum.UNIVARIATE_STATISTICS__DATAPOINTS.GetName(), baseStatisticsBean.N); + result.Put(ViewFieldEnum.UNIVARIATE_STATISTICS__TOTAL.GetName(), baseStatisticsBean.XSum); + result.Put(ViewFieldEnum.UNIVARIATE_STATISTICS__STDDEV.GetName(), baseStatisticsBean.XStandardDeviationSample); + result.Put(ViewFieldEnum.UNIVARIATE_STATISTICS__STDDEVPA.GetName(), baseStatisticsBean.XStandardDeviationPop); + result.Put(ViewFieldEnum.UNIVARIATE_STATISTICS__VARIANCE.GetName(), baseStatisticsBean.XVariance); + result.Put(ViewFieldEnum.UNIVARIATE_STATISTICS__AVERAGE.GetName(), baseStatisticsBean.XAverage); + if (additionalProps != null) + { + additionalProps.AddProperties(result, lastNewValues); + } + return eventAdapterService.AdapterForTypedMap(result, eventType); + } + + /// + /// Creates the event type for this view. + /// + /// is the event adapter service + /// The additional props. + /// The stream num. + /// event type of view + public static EventType CreateEventType(StatementContext statementContext, StatViewAdditionalProps additionalProps, int streamNum) + { + IDictionary eventTypeMap = new Dictionary(); + eventTypeMap.Put(ViewFieldEnum.UNIVARIATE_STATISTICS__DATAPOINTS.GetName(), typeof(long?)); + eventTypeMap.Put(ViewFieldEnum.UNIVARIATE_STATISTICS__TOTAL.GetName(), typeof(double?)); + eventTypeMap.Put(ViewFieldEnum.UNIVARIATE_STATISTICS__STDDEV.GetName(), typeof(double?)); + eventTypeMap.Put(ViewFieldEnum.UNIVARIATE_STATISTICS__STDDEVPA.GetName(), typeof(double?)); + eventTypeMap.Put(ViewFieldEnum.UNIVARIATE_STATISTICS__VARIANCE.GetName(), typeof(double?)); + eventTypeMap.Put(ViewFieldEnum.UNIVARIATE_STATISTICS__AVERAGE.GetName(), typeof(double?)); + StatViewAdditionalProps.AddCheckDupProperties(eventTypeMap, additionalProps, + ViewFieldEnum.UNIVARIATE_STATISTICS__DATAPOINTS, + ViewFieldEnum.UNIVARIATE_STATISTICS__TOTAL, + ViewFieldEnum.UNIVARIATE_STATISTICS__STDDEV, + ViewFieldEnum.UNIVARIATE_STATISTICS__STDDEVPA, + ViewFieldEnum.UNIVARIATE_STATISTICS__VARIANCE, + ViewFieldEnum.UNIVARIATE_STATISTICS__AVERAGE + ); + String outputEventTypeName = statementContext.StatementId + "_statview_" + streamNum; + return statementContext.EventAdapterService.CreateAnonymousMapType(outputEventTypeName, eventTypeMap, false); + } + + public BaseStatisticsBean BaseStatisticsBean + { + get { return _baseStatisticsBean; } + } + + public object[] LastValuesEventNew + { + get { return _lastValuesEventNew; } + set { _lastValuesEventNew = value; } + } + + public ViewFactory ViewFactory + { + get { return _viewFactory; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/stat/UnivariateStatisticsViewFactory.cs b/NEsper.Core/NEsper.Core/view/stat/UnivariateStatisticsViewFactory.cs new file mode 100755 index 000000000..72e764cc8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/stat/UnivariateStatisticsViewFactory.cs @@ -0,0 +1,113 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.util; + +namespace com.espertech.esper.view.stat +{ + /// + /// Factory for instances. + /// + public class UnivariateStatisticsViewFactory : ViewFactory + { + internal readonly static String NAME = "Univariate statistics"; + + private IList _viewParameters; + private int _streamNumber; + + /// Property name of data field. + private ExprNode _fieldExpression; + private StatViewAdditionalProps _additionalProps; + + private EventType _eventType; + + public void SetViewParameters(ViewFactoryContext viewFactoryContext, IList expressionParameters) + { + _viewParameters = expressionParameters; + _streamNumber = viewFactoryContext.StreamNum; + } + + public void Attach(EventType parentEventType, StatementContext statementContext, ViewFactory optionalParentFactory, IList parentViewFactories) + { + ExprNode[] validated = ViewFactorySupport.Validate(ViewName, parentEventType, statementContext, _viewParameters, true); + if (validated.Length < 1) + { + throw new ViewParameterException(ViewParamMessage); + } + if (!validated[0].ExprEvaluator.ReturnType.IsNumeric()) + { + throw new ViewParameterException(ViewParamMessage); + } + _fieldExpression = validated[0]; + + _additionalProps = StatViewAdditionalProps.Make(validated, 1, parentEventType); + _eventType = UnivariateStatisticsView.CreateEventType(statementContext, _additionalProps, _streamNumber); + } + + public View MakeView(AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext) + { + return new UnivariateStatisticsView(this, agentInstanceViewFactoryContext); + } + + public EventType EventType + { + get { return _eventType; } + set { _eventType = value; } + } + + public bool CanReuse(View view, AgentInstanceContext agentInstanceContext) + { + if (!(view is UnivariateStatisticsView)) + { + return false; + } + if (_additionalProps != null) + { + return false; + } + + var other = (UnivariateStatisticsView)view; + if (!ExprNodeUtility.DeepEquals(other.FieldExpression, _fieldExpression)) + { + return false; + } + + return true; + } + + public string ViewName + { + get { return NAME; } + } + + private string ViewParamMessage + { + get { return ViewName + " view require a single expression returning a numeric value as a parameter"; } + } + + public StatViewAdditionalProps AdditionalProps + { + get { return _additionalProps; } + set { _additionalProps = value; } + } + + public ExprNode FieldExpression + { + get { return _fieldExpression; } + set { _fieldExpression = value; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/stat/WeightedAverageView.cs b/NEsper.Core/NEsper.Core/view/stat/WeightedAverageView.cs new file mode 100755 index 000000000..5289e0e7e --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/stat/WeightedAverageView.cs @@ -0,0 +1,256 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.view.stat +{ + /// + /// View for computing a weighted average. The view uses 2 fields within the parent view to compute the weighted average. + /// The X field and weight field. In a price-volume example it calculates the volume-weighted average price + /// as (sum(price * volume) / sum(volume)). + /// Example: weighted_avg("price", "volume") + /// + public class WeightedAverageView : ViewSupport, CloneableView, DerivedValueView + { + private readonly AgentInstanceViewFactoryChainContext _agentInstanceContext; + private readonly ExprEvaluator _fieldNameXEvaluator; + private readonly ExprEvaluator _fieldNameWeightEvaluator; + + private readonly EventBean[] _eventsPerStream = new EventBean[1]; + + private EventBean _lastNewEvent; + private WeightedAverageViewFactory _viewFactory; + + /// + /// Constructor requires the name of the field to use in the parent view to compute the weighted average on, + /// as well as the name of the field in the parent view to get the weight from. + /// compute the average for. + /// + public WeightedAverageView(WeightedAverageViewFactory viewFactory, AgentInstanceViewFactoryChainContext agentInstanceContext) + { + CurrentValue = Double.NaN; + SumW = Double.NaN; + SumXtimesW = Double.NaN; + _viewFactory = viewFactory; + _fieldNameXEvaluator = viewFactory.FieldNameX.ExprEvaluator; + _fieldNameWeightEvaluator = viewFactory.FieldNameWeight.ExprEvaluator; + _agentInstanceContext = agentInstanceContext; + } + + public View CloneView() + { + return ViewFactory.MakeView(_agentInstanceContext); + } + + /// + /// Returns the expression supplying the X values. + /// + /// expression supplying X data points + public ExprNode FieldNameX + { + get { return _viewFactory.FieldNameX; } + } + + /// + /// Returns the expression supplying the weight values. + /// + /// expression supplying weight + public ExprNode FieldNameWeight + { + get { return _viewFactory.FieldNameWeight; } + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QViewProcessIRStream(this, WeightedAverageViewFactory.NAME, newData, oldData); } + + double oldValue = CurrentValue; + + // If we have child views, keep a reference to the old values, so we can update them as old data event. + EventBean oldDataMap = null; + if (_lastNewEvent == null) + { + if (HasViews) + { + IDictionary oldDataValues = new Dictionary(); + oldDataValues.Put(ViewFieldEnum.WEIGHTED_AVERAGE__AVERAGE.GetName(), oldValue); + AddProperties(oldDataValues); + oldDataMap = _agentInstanceContext.StatementContext.EventAdapterService.AdapterForTypedMap(oldDataValues, ViewFactory.EventType); + } + } + + // add data points to the bean + if (newData != null) + { + var evaluateParams = new EvaluateParams(_eventsPerStream, true, _agentInstanceContext); + for (int i = 0; i < newData.Length; i++) + { + _eventsPerStream[0] = newData[i]; + var pointnum = _fieldNameXEvaluator.Evaluate(evaluateParams); + var weightnum = _fieldNameWeightEvaluator.Evaluate(evaluateParams); + if (pointnum != null && weightnum != null) + { + double point = pointnum.AsDouble(); + double weight = weightnum.AsDouble(); + + if (double.IsNaN(SumXtimesW)) + { + SumXtimesW = point * weight; + SumW = weight; + } + else + { + SumXtimesW += point * weight; + SumW += weight; + } + } + } + + if ((_viewFactory.AdditionalProps != null) && (newData.Length != 0)) + { + if (LastValuesEventNew == null) + { + LastValuesEventNew = new object[_viewFactory.AdditionalProps.AdditionalExpr.Length]; + } + for (int val = 0; val < _viewFactory.AdditionalProps.AdditionalExpr.Length; val++) + { + LastValuesEventNew[val] = _viewFactory.AdditionalProps.AdditionalExpr[val].Evaluate( + new EvaluateParams(_eventsPerStream, true, _agentInstanceContext)); + } + } + } + + // remove data points from the bean + if (oldData != null) + { + var evaluateParams = new EvaluateParams(_eventsPerStream, true, _agentInstanceContext); + + for (int i = 0; i < oldData.Length; i++) + { + _eventsPerStream[0] = oldData[i]; + var pointnum = _fieldNameXEvaluator.Evaluate(evaluateParams); + var weightnum = _fieldNameWeightEvaluator.Evaluate(evaluateParams); + + if (pointnum != null && weightnum != null) + { + double point = pointnum.AsDouble(); + double weight = weightnum.AsDouble(); + SumXtimesW -= point * weight; + SumW -= weight; + } + } + } + + if (SumW != 0) + { + CurrentValue = SumXtimesW / SumW; + } + else + { + CurrentValue = Double.NaN; + } + + // If there are child view, fireStatementStopped update method + if (HasViews) + { + IDictionary newDataMap = new Dictionary(); + newDataMap.Put(ViewFieldEnum.WEIGHTED_AVERAGE__AVERAGE.GetName(), CurrentValue); + AddProperties(newDataMap); + EventBean newDataEvent = _agentInstanceContext.StatementContext.EventAdapterService.AdapterForTypedMap(newDataMap, ViewFactory.EventType); + + EventBean[] newEvents = new EventBean[] { newDataEvent }; + EventBean[] oldEvents; + if (_lastNewEvent == null) + { + oldEvents = new EventBean[] { oldDataMap }; + } + else + { + oldEvents = new EventBean[] { _lastNewEvent }; + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QViewIndicate(this, WeightedAverageViewFactory.NAME, newEvents, oldEvents); } + UpdateChildren(newEvents, oldEvents); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AViewIndicate(); } + + _lastNewEvent = newDataEvent; + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AViewProcessIRStream(); } + } + + private void AddProperties(IDictionary newDataMap) + { + if (_viewFactory.AdditionalProps == null) + { + return; + } + _viewFactory.AdditionalProps.AddProperties(newDataMap, LastValuesEventNew); + } + + public override EventType EventType + { + get { return ViewFactory.EventType; } + } + + public override IEnumerator GetEnumerator() + { + var newDataMap = new Dictionary(); + newDataMap.Put(ViewFieldEnum.WEIGHTED_AVERAGE__AVERAGE.GetName(), CurrentValue); + AddProperties(newDataMap); + var eventBean = _agentInstanceContext.StatementContext.EventAdapterService.AdapterForTypedMap( + newDataMap, _viewFactory.EventType); + if (eventBean != null) + yield return eventBean; + } + + public override string ToString() + { + return GetType().Name + + " fieldName=" + _viewFactory.FieldNameX + + " fieldNameWeight=" + _viewFactory.FieldNameWeight; + } + + /// + /// Creates the event type for this view. + /// + /// is the event adapter service + /// event type of view + public static EventType CreateEventType(StatementContext statementContext, StatViewAdditionalProps additionalProps, int streamNum) + { + IDictionary schemaMap = new Dictionary(); + schemaMap.Put(ViewFieldEnum.WEIGHTED_AVERAGE__AVERAGE.GetName(), typeof(double?)); + StatViewAdditionalProps.AddCheckDupProperties(schemaMap, additionalProps, ViewFieldEnum.WEIGHTED_AVERAGE__AVERAGE); + string outputEventTypeName = statementContext.StatementId + "_wavgview_" + streamNum; + return statementContext.EventAdapterService.CreateAnonymousMapType(outputEventTypeName, schemaMap, false); + } + + public double SumXtimesW { get; set; } + + public double SumW { get; set; } + + public double CurrentValue { get; set; } + + public object[] LastValuesEventNew { get; set; } + + public ViewFactory ViewFactory + { + get { return _viewFactory; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/view/stat/WeightedAverageViewFactory.cs b/NEsper.Core/NEsper.Core/view/stat/WeightedAverageViewFactory.cs new file mode 100755 index 000000000..475b08000 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/stat/WeightedAverageViewFactory.cs @@ -0,0 +1,124 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.util; + +namespace com.espertech.esper.view.stat +{ + /// + /// Factory for instances. + /// + public class WeightedAverageViewFactory : ViewFactory + { + public readonly static String NAME = "Weighted-average"; + + private IList _viewParameters; + private int _streamNumber; + + /// Expression of X field. + private ExprNode _fieldNameX; + /// Expression of weight field. + private ExprNode _fieldNameWeight; + + private StatViewAdditionalProps _additionalProps; + + private EventType _eventType; + + public void SetViewParameters(ViewFactoryContext viewFactoryContext, IList expressionParameters) + { + _viewParameters = expressionParameters; + _streamNumber = viewFactoryContext.StreamNum; + } + + public void Attach(EventType parentEventType, StatementContext statementContext, ViewFactory optionalParentFactory, IList parentViewFactories) + { + ExprNode[] validated = ViewFactorySupport.Validate(ViewName, parentEventType, statementContext, _viewParameters, true); + + if (validated.Length < 2) + { + throw new ViewParameterException(ViewParamMessage); + } + if ((!validated[0].ExprEvaluator.ReturnType.IsNumeric()) || (!validated[1].ExprEvaluator.ReturnType.IsNumeric())) + { + throw new ViewParameterException(ViewParamMessage); + } + + _fieldNameX = validated[0]; + _fieldNameWeight = validated[1]; + _additionalProps = StatViewAdditionalProps.Make(validated, 2, parentEventType); + _eventType = WeightedAverageView.CreateEventType(statementContext, _additionalProps, _streamNumber); + } + + public View MakeView(AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext) + { + return new WeightedAverageView(this, agentInstanceViewFactoryContext); + } + + public EventType EventType + { + get { return _eventType; } + set { _eventType = value; } + } + + public bool CanReuse(View view, AgentInstanceContext agentInstanceContext) + { + if (!(view is WeightedAverageView)) + { + return false; + } + if (_additionalProps != null) + { + return false; + } + + var myView = (WeightedAverageView)view; + if ((!ExprNodeUtility.DeepEquals(_fieldNameWeight, myView.FieldNameWeight)) || + (!ExprNodeUtility.DeepEquals(_fieldNameX, myView.FieldNameX))) + { + return false; + } + return true; + } + + public string ViewName + { + get { return NAME; } + } + + private string ViewParamMessage + { + get { return ViewName + " view requires two expressions returning numeric values as parameters"; } + } + + public ExprNode FieldNameX + { + get { return _fieldNameX; } + set { _fieldNameX = value; } + } + + public ExprNode FieldNameWeight + { + get { return _fieldNameWeight; } + set { _fieldNameWeight = value; } + } + + public StatViewAdditionalProps AdditionalProps + { + get { return _additionalProps; } + set { _additionalProps = value; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/std/AddPropertyValueOptionalView.cs b/NEsper.Core/NEsper.Core/view/std/AddPropertyValueOptionalView.cs new file mode 100755 index 000000000..4350ab104 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/std/AddPropertyValueOptionalView.cs @@ -0,0 +1,232 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.util; +using com.espertech.esper.events; +using com.espertech.esper.view; + +namespace com.espertech.esper.view.std +{ + /// + /// This view simply adds a property to the events posted to it. This is useful for the group-merge views. + /// + public sealed class AddPropertyValueOptionalView + : ViewSupport + , CloneableView + , StoppableView + { + private readonly AgentInstanceViewFactoryChainContext _agentInstanceContext; + private readonly string[] _propertyNames; + private readonly object _propertyValues; + private readonly EventType _eventType; + private bool _mustAddProperty; + + // Keep a history of posted old events to avoid reconstructing the event + // and adhere to the contract of posting the same reference to child views. + // Only for must-add-property. + private IDictionary _newToOldEventMap; + + /// + /// Constructor. + /// + /// is the name of the field that is added to any events received by this view. + /// is the values of the field that is added to any events received by this view. + /// is the event type that the merge view reports to it's child views + /// contains required view services + public AddPropertyValueOptionalView(AgentInstanceViewFactoryChainContext agentInstanceContext, string[] propertyNames, object mergeValues, EventType mergedResultEventType) + { + _propertyNames = propertyNames; + _propertyValues = mergeValues; + _eventType = mergedResultEventType; + _agentInstanceContext = agentInstanceContext; + _newToOldEventMap = new Dictionary(); + } + + public View CloneView() + { + return new AddPropertyValueOptionalView(_agentInstanceContext, _propertyNames, _propertyValues, _eventType); + } + + public override Viewable Parent + { + set + { + if (Log.IsDebugEnabled) + { + Log.Debug(".setParent parent=" + value); + } + base.Parent = value; + + if (!Equals(value.EventType, _eventType)) + { + _mustAddProperty = true; + _newToOldEventMap = new Dictionary(); + } + else + { + _mustAddProperty = false; + } + } + } + + /// + /// Returns field name for which to set the merge value for. + /// + /// field name to use to set value + public string[] PropertyNames + { + get { return _propertyNames; } + } + + /// + /// Returns the value to set for the field. + /// + /// value to set + public object PropertyValues + { + get { return _propertyValues; } + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + if (!_mustAddProperty) + { + UpdateChildren(newData, oldData); + return; + } + + EventBean[] newEvents = null; + EventBean[] oldEvents = null; + + if (newData != null) + { + newEvents = new EventBean[newData.Length]; + + int index = 0; + foreach (EventBean newEvent in newData) + { + EventBean theEvent = AddProperty(newEvent, _propertyNames, _propertyValues, _eventType, _agentInstanceContext.StatementContext.EventAdapterService); + newEvents[index++] = theEvent; + + _newToOldEventMap.Put(newEvent, theEvent); + } + } + + if (oldData != null) + { + oldEvents = new EventBean[oldData.Length]; + + int index = 0; + foreach (EventBean oldEvent in oldData) + { + var outgoing = _newToOldEventMap.Delete(oldEvent); + if (outgoing != null) + { + oldEvents[index++] = outgoing; + } + else + { + EventBean theEvent = AddProperty(oldEvent, _propertyNames, _propertyValues, _eventType, _agentInstanceContext.StatementContext.EventAdapterService); + oldEvents[index++] = theEvent; + } + } + } + + UpdateChildren(newEvents, oldEvents); + } + + public override EventType EventType + { + get { return _eventType; } + } + + public override IEnumerator GetEnumerator() + { + foreach (var nextEvent in Parent) + { + if (_mustAddProperty) + { + yield return AddProperty( + nextEvent, _propertyNames, _propertyValues, _eventType, _agentInstanceContext.StatementContext.EventAdapterService); + } + else + { + yield return nextEvent; + } + } + } + + public void Stop() + { + if (!_newToOldEventMap.IsEmpty()) + { + var oldEvents = new OneEventCollection(); + foreach (var oldEvent in _newToOldEventMap) + { + oldEvents.Add(oldEvent.Value); + } + if (!oldEvents.IsEmpty()) + { + UpdateChildren(null, oldEvents.ToArray()); + } + _newToOldEventMap.Clear(); + } + } + + /// + /// Add a property to the event passed in. + /// + /// event to add property to + /// names of properties to add + /// value of properties to add + /// new event type + /// service for generating events and handling event types + /// event with added property + internal static EventBean AddProperty( + EventBean originalEvent, + string[] propertyNames, + object propertyValues, + EventType targetEventType, + EventAdapterService eventAdapterService) + { + IDictionary values = new Dictionary(); + if (propertyValues is MultiKeyUntyped) + { + var props = (MultiKeyUntyped)propertyValues; + var propertyValuesArr = props.Keys; + + for (int i = 0; i < propertyNames.Length; i++) + { + values.Put(propertyNames[i], propertyValuesArr[i]); + } + } + else + { + values.Put(propertyNames[0], propertyValues); + } + + return eventAdapterService.AdapterForTypedWrapper(originalEvent, values, targetEventType); + } + + public override string ToString() + { + return string.Format("{0} propertyNames={1} propertyValue={2}", GetType().Name, _propertyNames.Render(), _propertyValues); + } + + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/view/std/FirstElementView.cs b/NEsper.Core/NEsper.Core/view/std/FirstElementView.cs new file mode 100755 index 000000000..ec27720e3 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/std/FirstElementView.cs @@ -0,0 +1,116 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.view.std +{ + /// + /// View retaining the very first event. Any subsequent events received are simply + /// discarded and not entered into either insert or remove stream. Only the very first + /// event received is entered into the remove stream. The view thus never posts + /// a remove stream unless explicitly deleted from when used with a named window. + /// + public class FirstElementView : ViewSupport, CloneableView, DataWindowView + { + private readonly FirstElementViewFactory _viewFactory; + + /// The first new element posted from a parent view. + private EventBean _firstEvent; + + public FirstElementView(FirstElementViewFactory viewFactory) + { + this._viewFactory = viewFactory; + } + + public View CloneView() + { + return new FirstElementView(_viewFactory); + } + + public override EventType EventType + { + get + { + // The schema is the parent view's schema + return Parent.EventType; + } + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QViewProcessIRStream(this, FirstElementViewFactory.NAME, newData, oldData);} + + EventBean[] newDataToPost = null; + EventBean[] oldDataToPost = null; + + if (oldData != null) + { + for (int i = 0; i < oldData.Length; i++) + { + if (oldData[i] == _firstEvent) + { + oldDataToPost = new EventBean[] {_firstEvent}; + _firstEvent = null; + } + } + } + + if ((newData != null) && (newData.Length != 0)) + { + if (_firstEvent == null) + { + _firstEvent = newData[0]; + newDataToPost = new EventBean[] {_firstEvent}; + } + } + + if ((HasViews) && ((newDataToPost != null) || (oldDataToPost != null))) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QViewIndicate(this, FirstElementViewFactory.NAME, newDataToPost, oldDataToPost);} + UpdateChildren(newDataToPost, oldDataToPost); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AViewIndicate();} + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AViewProcessIRStream();} + } + + public override IEnumerator GetEnumerator() + { + if (_firstEvent != null) + { + yield return _firstEvent; + } + } + + public override String ToString() + { + return GetType().FullName; + } + + public EventBean FirstEvent + { + get { return _firstEvent; } + set { _firstEvent = value; } + } + + public void VisitView(ViewDataVisitor viewDataVisitor) + { + viewDataVisitor.VisitPrimary(_firstEvent, FirstElementViewFactory.NAME); + } + + public ViewFactory ViewFactory + { + get { return _viewFactory; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/std/FirstElementViewFactory.cs b/NEsper.Core/NEsper.Core/view/std/FirstElementViewFactory.cs new file mode 100755 index 000000000..41bf29495 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/std/FirstElementViewFactory.cs @@ -0,0 +1,66 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.view; + +namespace com.espertech.esper.view.std +{ + /// + /// Factory for instances. + /// + public class FirstElementViewFactory : AsymetricDataWindowViewFactory + { + public static readonly string NAME = "First-Event"; + + private EventType _eventType; + + public void SetViewParameters(ViewFactoryContext viewFactoryContext, IList expressionParameters) + { + ViewFactorySupport.ValidateNoParameters(ViewName, expressionParameters); + } + + public void Attach( + EventType parentEventType, + StatementContext statementContext, + ViewFactory optionalParentFactory, + IList parentViewFactories) + { + _eventType = parentEventType; + } + + public View MakeView(AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext) + { + return new FirstElementView(this); + } + + public EventType EventType + { + get { return _eventType; } + } + + public bool CanReuse(View view, AgentInstanceContext agentInstanceContext) + { + return view is FirstElementView; + } + + public string ViewName + { + get { return NAME; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/view/std/FirstUniqueByPropertyView.cs b/NEsper.Core/NEsper.Core/view/std/FirstUniqueByPropertyView.cs new file mode 100755 index 000000000..fe6200c0e --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/std/FirstUniqueByPropertyView.cs @@ -0,0 +1,202 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.view; + +namespace com.espertech.esper.view.std +{ + /// + /// This view retains the first event for each multi-key of distinct property values. + /// The view does not post a remove stream unless explicitly deleted from. + /// The view swallows any insert stream events that provide no new distinct set of property values. + /// + public class FirstUniqueByPropertyView : ViewSupport, CloneableView, DataWindowView + { + private readonly FirstUniqueByPropertyViewFactory _viewFactory; + private readonly ExprEvaluator[] _uniqueCriteriaEval; + private readonly EventBean[] _eventsPerStream = new EventBean[1]; + private readonly IDictionary _firstEvents = new NullableDictionary(); + private readonly AgentInstanceViewFactoryChainContext _agentInstanceViewFactoryContext; + + /// + /// Constructor. + /// + public FirstUniqueByPropertyView(FirstUniqueByPropertyViewFactory viewFactory, AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext) + { + _viewFactory = viewFactory; + _uniqueCriteriaEval = ExprNodeUtility.GetEvaluators(viewFactory.CriteriaExpressions); + _agentInstanceViewFactoryContext = agentInstanceViewFactoryContext; + } + + public View CloneView() + { + return _viewFactory.MakeView(_agentInstanceViewFactoryContext); + } + + /// + /// Returns the expressions supplying the unique value to keep the most recent record for. + /// + /// expressions for unique value + public ExprNode[] UniqueCriteria + { + get { return _viewFactory.CriteriaExpressions; } + } + + public override EventType EventType + { + get + { + // The schema is the parent view's schema + return Parent.EventType; + } + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QViewProcessIRStream(this, FirstUniqueByPropertyViewFactory.NAME, newData, oldData); } + + EventBean[] newDataToPost = null; + EventBean[] oldDataToPost = null; + + if (oldData != null) + { + foreach (EventBean oldEvent in oldData) + { + // Obtain unique value + object key = GetUniqueKey(oldEvent); + + // If the old event is the current unique event, remove and post as old data + EventBean lastValue = _firstEvents.Get(key); + + if (lastValue != oldEvent) + { + continue; + } + + if (oldDataToPost == null) + { + oldDataToPost = new EventBean[] { oldEvent }; + } + else + { + oldDataToPost = EventBeanUtility.AddToArray(oldDataToPost, oldEvent); + } + + _firstEvents.Remove(key); + InternalHandleRemoved(key, lastValue); + } + } + + if (newData != null) + { + foreach (EventBean newEvent in newData) + { + // Obtain unique value + object key = GetUniqueKey(newEvent); + + // already-seen key + if (_firstEvents.ContainsKey(key)) + { + continue; + } + + // store + _firstEvents.Put(key, newEvent); + InternalHandleAdded(key, newEvent); + + // Post the new value + if (newDataToPost == null) + { + newDataToPost = new EventBean[] { newEvent }; + } + else + { + newDataToPost = EventBeanUtility.AddToArray(newDataToPost, newEvent); + } + } + } + + if ((HasViews) && ((newDataToPost != null) || (oldDataToPost != null))) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QViewIndicate(this, FirstUniqueByPropertyViewFactory.NAME, newDataToPost, oldDataToPost); } + UpdateChildren(newDataToPost, oldDataToPost); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AViewIndicate(); } + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AViewProcessIRStream(); } + } + + public void InternalHandleRemoved(object key, EventBean lastValue) + { + // no action required + } + + public void InternalHandleAdded(object key, EventBean newEvent) + { + // no action required + } + + public override IEnumerator GetEnumerator() + { + return _firstEvents.Values.GetEnumerator(); + } + + public override string ToString() + { + return GetType().Name + " uniqueCriteria=" + _viewFactory.CriteriaExpressions; + } + + protected object GetUniqueKey(EventBean theEvent) + { + var evaluateParams = new EvaluateParams(_eventsPerStream, true, _agentInstanceViewFactoryContext); + + _eventsPerStream[0] = theEvent; + if (_uniqueCriteriaEval.Length == 1) + { + return _uniqueCriteriaEval[0].Evaluate(evaluateParams); + } + + var values = new object[_uniqueCriteriaEval.Length]; + for (int i = 0; i < _uniqueCriteriaEval.Length; i++) + { + values[i] = _uniqueCriteriaEval[i].Evaluate(evaluateParams); + } + return new MultiKeyUntyped(values); + } + + /// + /// Returns true if empty. + /// + /// true if empty + public bool IsEmpty() + { + return _firstEvents.IsEmpty(); + } + + public void VisitView(ViewDataVisitor viewDataVisitor) + { + viewDataVisitor.VisitPrimary(_firstEvents, true, FirstUniqueByPropertyViewFactory.NAME, _firstEvents.Count, _firstEvents.Count); + } + + public ViewFactory ViewFactory + { + get { return _viewFactory; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/view/std/FirstUniqueByPropertyViewFactory.cs b/NEsper.Core/NEsper.Core/view/std/FirstUniqueByPropertyViewFactory.cs new file mode 100755 index 000000000..69a61937b --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/std/FirstUniqueByPropertyViewFactory.cs @@ -0,0 +1,89 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.view.std +{ + /// + /// Factory for instances. + /// + public class FirstUniqueByPropertyViewFactory : AsymetricDataWindowViewFactory, DataWindowViewFactoryUniqueCandidate + { + public static readonly String NAME = "First-Unique-By"; + + /// View parameters. + internal IList ViewParameters; + + /// Property name to evaluate unique values. + internal ExprNode[] CriteriaExpressions; + + private EventType _eventType; + + public void SetViewParameters(ViewFactoryContext viewFactoryContext, IList expressionParameters) + { + ViewParameters = expressionParameters; + } + + public void Attach(EventType parentEventType, StatementContext statementContext, ViewFactory optionalParentFactory, IList parentViewFactories) + { + CriteriaExpressions = ViewFactorySupport.Validate(ViewName, parentEventType, statementContext, ViewParameters, false); + + if (CriteriaExpressions.Length == 0) + { + String errorMessage = ViewName + " view requires a one or more expressions provinding unique values as parameters"; + throw new ViewParameterException(errorMessage); + } + + _eventType = parentEventType; + } + + public View MakeView(AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext) + { + return new FirstUniqueByPropertyView(this, agentInstanceViewFactoryContext); + } + + public EventType EventType + { + get { return _eventType; } + } + + public bool CanReuse(View view, AgentInstanceContext agentInstanceContext) + { + if (!(view is FirstUniqueByPropertyView)) + { + return false; + } + + FirstUniqueByPropertyView myView = (FirstUniqueByPropertyView)view; + if (!ExprNodeUtility.DeepEquals(CriteriaExpressions, myView.UniqueCriteria)) + { + return false; + } + + return myView.IsEmpty(); + } + + public ICollection UniquenessCandidatePropertyNames + { + get { return ExprNodeUtility.GetPropertyNamesIfAllProps(CriteriaExpressions); } + } + + public string ViewName + { + get { return NAME; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/std/GroupByView.cs b/NEsper.Core/NEsper.Core/view/std/GroupByView.cs new file mode 100755 index 000000000..3e8b188b0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/std/GroupByView.cs @@ -0,0 +1,18 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.view.std +{ + public interface GroupByView : View, ViewDataVisitableContainer + { + ExprNode[] CriteriaExpressions { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/view/std/GroupByViewAgedEntry.cs b/NEsper.Core/NEsper.Core/view/std/GroupByViewAgedEntry.cs new file mode 100755 index 000000000..5a30c543c --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/std/GroupByViewAgedEntry.cs @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.view.std +{ + public class GroupByViewAgedEntry + { + public GroupByViewAgedEntry(object subviews, long lastUpdateTime) + { + SubviewHolder = subviews; + LastUpdateTime = lastUpdateTime; + } + + public object SubviewHolder { get; private set; } + + public long LastUpdateTime { get; set; } + } +} diff --git a/NEsper.Core/NEsper.Core/view/std/GroupByViewFactory.cs b/NEsper.Core/NEsper.Core/view/std/GroupByViewFactory.cs new file mode 100755 index 000000000..b67b518f1 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/std/GroupByViewFactory.cs @@ -0,0 +1,170 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.client.annotation; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.view.std +{ + /// + /// Factory for instances. + /// + public class GroupByViewFactory + : ViewFactory + , GroupByViewFactoryMarker + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// View parameters. + protected IList ViewParameters; + + /// List of criteria expressions. + private ExprNode[] _criteriaExpressions; + + private EventType _eventType; + + private bool _isReclaimAged; + + private double _reclaimMaxAge; + private double _reclaimFrequency; + + public void SetViewParameters(ViewFactoryContext viewFactoryContext, IList expressionParameters) + { + ViewParameters = expressionParameters; + + var reclaimGroupAged = HintEnum.RECLAIM_GROUP_AGED.GetHint(viewFactoryContext.StatementContext.Annotations); + + if (reclaimGroupAged != null) + { + _isReclaimAged = true; + String hintValueMaxAge = HintEnum.RECLAIM_GROUP_AGED.GetHintAssignedValue(reclaimGroupAged); + if (hintValueMaxAge == null) + { + throw new ViewParameterException("Required hint value for hint '" + HintEnum.RECLAIM_GROUP_AGED + "' has not been provided"); + } + try + { + _reclaimMaxAge = Double.Parse(hintValueMaxAge); + } + catch (Exception) + { + throw new ViewParameterException("Required hint value for hint '" + HintEnum.RECLAIM_GROUP_AGED + "' value '" + hintValueMaxAge + "' could not be parsed as a double value"); + } + + String hintValueFrequency = HintEnum.RECLAIM_GROUP_FREQ.GetHintAssignedValue(reclaimGroupAged); + if (hintValueFrequency == null) + { + _reclaimFrequency = _reclaimMaxAge; + } + else + { + try + { + _reclaimFrequency = Double.Parse(hintValueFrequency); + } + catch (Exception) + { + throw new ViewParameterException("Required hint value for hint '" + HintEnum.RECLAIM_GROUP_FREQ + "' value '" + hintValueFrequency + "' could not be parsed as a double value"); + } + } + if (_reclaimMaxAge < 0.100) + { + Log.Warn("Reclaim max age parameter is less then 100 milliseconds, are your sure?"); + } + + if (Log.IsDebugEnabled) + { + Log.Debug("Using reclaim-aged strategy for group-window age " + _reclaimMaxAge + " frequency " + _reclaimFrequency); + } + } + } + + public void Attach(EventType parentEventType, StatementContext statementContext, ViewFactory optionalParentFactory, IList parentViewFactories) + { + _criteriaExpressions = ViewFactorySupport.Validate(ViewName, parentEventType, statementContext, ViewParameters, false); + + if (_criteriaExpressions.Length == 0) + { + String errorMessage = ViewName + " view requires a one or more expressions provinding unique values as parameters"; + throw new ViewParameterException(errorMessage); + } + + _eventType = parentEventType; + } + + /// Returns the names of fields to group by + /// field names + public ExprNode[] CriteriaExpressions + { + get { return _criteriaExpressions; } + } + + public View MakeView(AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext) + { + if (IsReclaimAged) + { + return new GroupByViewReclaimAged(agentInstanceViewFactoryContext, _criteriaExpressions, ExprNodeUtility.GetEvaluators(_criteriaExpressions), _reclaimMaxAge, _reclaimFrequency); + } + return new GroupByViewImpl(agentInstanceViewFactoryContext, _criteriaExpressions, ExprNodeUtility.GetEvaluators(_criteriaExpressions)); + } + + public EventType EventType + { + get { return _eventType; } + } + + public bool CanReuse(View view, AgentInstanceContext agentInstanceContext) + { + if (!(view is GroupByView)) + { + return false; + } + + if (IsReclaimAged) + { + return false; + } + + GroupByView myView = (GroupByView)view; + if (!ExprNodeUtility.DeepEquals(myView.CriteriaExpressions, _criteriaExpressions)) + { + return false; + } + + return true; + } + + public bool IsReclaimAged + { + get { return _isReclaimAged; } + } + + public double ReclaimMaxAge + { + get { return _reclaimMaxAge; } + } + + public double ReclaimFrequency + { + get { return _reclaimFrequency; } + } + + public string ViewName + { + get { return "Group-By"; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/std/GroupByViewFactoryMarker.cs b/NEsper.Core/NEsper.Core/view/std/GroupByViewFactoryMarker.cs new file mode 100755 index 000000000..fcc4b1f42 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/std/GroupByViewFactoryMarker.cs @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.view.std +{ + public interface GroupByViewFactoryMarker + { + ExprNode[] CriteriaExpressions { get; } + + bool IsReclaimAged { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/view/std/GroupByViewImpl.cs b/NEsper.Core/NEsper.Core/view/std/GroupByViewImpl.cs new file mode 100755 index 000000000..49c3cbe75 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/std/GroupByViewImpl.cs @@ -0,0 +1,490 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.view.std +{ + /// + /// The group view splits the data in a stream to multiple subviews, based on a key index. + /// The key is one or more fields in the stream. Any view that follows the GROUP view will + /// be executed separately on each subview, one per unique key. The view takes a single + /// parameter which is the field name returning the key value to group. This view can, for + /// example, be used to calculate the average price per symbol for a list of symbols. The + /// view treats its child views and their child views as prototypes. It dynamically instantiates + /// copies of each child view and their child views, and the child view's child views as so on. + /// When there are no more child views or the special merge view is encountered, it ends. The + /// view installs a special merge view unto each leaf child view that merges the value key that + /// was grouped by back into the stream using the group-by field name. + /// + public class GroupByViewImpl : ViewSupport, CloneableView, GroupByView + { + public readonly static String VIEWNAME = "Group-By"; + + private readonly ExprNode[] _criteriaExpressions; + private readonly ExprEvaluator[] _criteriaEvaluators; + private readonly AgentInstanceViewFactoryChainContext _agentInstanceContext; + private readonly EventBean[] _eventsPerStream = new EventBean[1]; + + private readonly String[] _propertyNames; + private readonly IDictionary _subViewsPerKey = new Dictionary(); + + private readonly Dictionary> _groupedEvents = new Dictionary>(); + + /// + /// Constructor. + /// + /// contains required view services + /// is the fields from which to pull the values to group by + /// The criteria evaluators. + public GroupByViewImpl(AgentInstanceViewFactoryChainContext agentInstanceContext, ExprNode[] criteriaExpressions, ExprEvaluator[] criteriaEvaluators) + { + _agentInstanceContext = agentInstanceContext; + _criteriaExpressions = criteriaExpressions; + _criteriaEvaluators = criteriaEvaluators; + + _propertyNames = new String[criteriaExpressions.Length]; + for (var i = 0; i < criteriaExpressions.Length; i++) + { + _propertyNames[i] = ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(criteriaExpressions[i]); + } + } + + public View CloneView() + { + return new GroupByViewImpl(_agentInstanceContext, _criteriaExpressions, _criteriaEvaluators); + } + + /// Returns the field name that provides the key valie by which to group by. + /// field name providing group-by key. + public ExprNode[] CriteriaExpressions + { + get { return _criteriaExpressions; } + } + + public override EventType EventType + { + get + { + // The schema is the parent view's schema + return Parent.EventType; + } + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + using (Instrument.With( + i => i.QViewProcessIRStream(this, "Grouped", newData, oldData), + i => i.AViewProcessIRStream())) + { + // Algorithm for single new event + if ((newData != null) && (oldData == null) && (newData.Length == 1)) + { + var theEvent = newData[0]; + var newDataToPost = new EventBean[] + { + theEvent + }; + + var groupByValuesKey = GetGroupKey(theEvent); + + // Get child views that belong to this group-by value combination + var subViews = _subViewsPerKey.Get(groupByValuesKey); + + // If this is a new group-by value, the list of subviews is null and we need to make clone sub-views + if (subViews == null) + { + subViews = MakeSubViews(this, _propertyNames, groupByValuesKey, _agentInstanceContext); + _subViewsPerKey.Put(groupByValuesKey, subViews); + } + + UpdateChildViews(subViews, newDataToPost, null); + } + else + { + // Algorithm for dispatching multiple events + if (newData != null) + { + foreach (var newValue in newData) + { + HandleEvent(newValue, true); + } + } + + if (oldData != null) + { + foreach (var oldValue in oldData) + { + HandleEvent(oldValue, false); + } + } + + // Update child views + foreach (var entry in _groupedEvents) + { + var newEvents = ConvertToArray(entry.Value.First); + var oldEvents = ConvertToArray(entry.Value.Second); + UpdateChildViews(entry.Key, newEvents, oldEvents); + } + + _groupedEvents.Clear(); + } + } + } + + public override IEnumerator GetEnumerator() + { + throw new UnsupportedOperationException("Cannot iterate over group view, this operation is not supported"); + } + + public override String ToString() + { + return GetType().FullName + " groupFieldNames=" + _criteriaExpressions.Render(); + } + + /// + /// Instantiate subviews for the given group view and the given key value to group-by. Makes shallow + /// copies of each child view and its subviews up to the merge point. Sets up merge data views for + /// merging the group-by key value back in. + /// + /// is the parent view for which to copy subviews for + /// names of expressions or properties + /// is the key values to group-by + /// is the view services that sub-views may need + /// + /// a single view or a list of views that are copies of the original list, with copied children, withdata merge views added to the copied child leaf views. + /// + public static Object MakeSubViews( + GroupByView groupView, + String[] propertyNames, + Object groupByValues, + AgentInstanceViewFactoryChainContext agentInstanceContext) + { + if (!groupView.HasViews) + { + const string message = "Unexpected empty list of child nodes for group view"; + Log.Error(".copySubViews " + message); + throw new EPException(message); + } + + Object subviewHolder; + if (groupView.Views.Length == 1) + { + subviewHolder = CopyChildView(groupView, propertyNames, groupByValues, agentInstanceContext, groupView.Views[0]); + } + else + { + // For each child node + var subViewList = new List(4); + subviewHolder = subViewList; + foreach (var originalChildView in groupView.Views) + { + var copyChildView = CopyChildView(groupView, propertyNames, groupByValues, agentInstanceContext, originalChildView); + subViewList.Add(copyChildView); + } + } + + return subviewHolder; + } + + public void VisitViewContainer(ViewDataVisitorContained viewDataVisitor) + { + viewDataVisitor.VisitPrimary(VIEWNAME, _subViewsPerKey.Count); + foreach (var entry in _subViewsPerKey) + { + VisitView(viewDataVisitor, entry.Key, entry.Value); + } + } + + public static void VisitView(ViewDataVisitorContained viewDataVisitor, Object groupkey, Object subviewHolder) + { + if (subviewHolder == null) + { + return; + } + if (subviewHolder is View) + { + viewDataVisitor.VisitContained(groupkey, (View)subviewHolder); + return; + } + if (subviewHolder is ICollection) + { + var deque = (ICollection)subviewHolder; + foreach (var view in deque) + { + viewDataVisitor.VisitContained(groupkey, view); + return; + } + } + } + + public override bool RemoveView(View view) + { + if (!(view is GroupableView)) + { + base.RemoveView(view); + } + var removed = base.RemoveView(view); + if (!removed) + { + return false; + } + if (!HasViews) + { + _subViewsPerKey.Clear(); + return true; + } + var removedView = (GroupableView)view; + Deque removedKeys = null; + foreach (var entry in _subViewsPerKey) + { + var value = entry.Value; + if (value is View) + { + var subview = (GroupableView)value; + if (CompareViews(subview, removedView)) + { + if (removedKeys == null) + { + removedKeys = new ArrayDeque(); + } + removedKeys.Add(entry.Key); + } + } + else if (value is IList) + { + var subviews = (IList)value; + for (var i = 0; i < subviews.Count; i++) + { + var subview = (GroupableView)subviews[i]; + if (CompareViews(subview, removedView)) + { + subviews.RemoveAt(i); + if (subviews.IsEmpty()) + { + if (removedKeys == null) + { + removedKeys = new ArrayDeque(); + } + removedKeys.Add(entry.Key); + } + break; + } + } + } + } + if (removedKeys != null) + { + foreach (var key in removedKeys) + { + _subViewsPerKey.Remove(key); + } + } + return true; + } + + private bool CompareViews(GroupableView subview, GroupableView removed) + { + return subview.ViewFactory == removed.ViewFactory; + } + + public static void UpdateChildViews(Object subViews, EventBean[] newData, EventBean[] oldData) + { + if (subViews is IList) + { + var viewList = (IList)subViews; + UpdateChildren(viewList, newData, oldData); + } + else + { + ((View)subViews).Update(newData, oldData); + } + } + + private void HandleEvent(EventBean theEvent, bool isNew) + { + var groupByValuesKey = GetGroupKey(theEvent); + + // Get child views that belong to this group-by value combination + var subViews = _subViewsPerKey.Get(groupByValuesKey); + + // If this is a new group-by value, the list of subviews is null and we need to make clone sub-views + if (subViews == null) + { + subViews = MakeSubViews(this, _propertyNames, groupByValuesKey, _agentInstanceContext); + _subViewsPerKey.Put(groupByValuesKey, subViews); + } + + // Construct a pair of lists to hold the events for the grouped value if not already there + var pair = _groupedEvents.Get(subViews); + if (pair == null) + { + pair = new Pair(null, null); + _groupedEvents.Put(subViews, pair); + } + + // Add event to a child view event list for later child Update that includes new and old events + if (isNew) + { + pair.First = AddUpgradeToDequeIfPopulated(pair.First, theEvent); + } + else + { + pair.Second = AddUpgradeToDequeIfPopulated(pair.Second, theEvent); + } + } + + private static View CopyChildView(GroupByView groupView, String[] propertyNames, Object groupByValues, AgentInstanceViewFactoryChainContext agentInstanceContext, View originalChildView) + { + if (originalChildView is MergeView) + { + const string message = "Unexpected merge view as child of group-by view"; + Log.Error(".copySubViews " + message); + throw new EPException(message); + } + + if (!(originalChildView is CloneableView)) + { + throw new EPException("Unexpected error copying subview " + originalChildView.GetType().FullName); + } + var cloneableView = (CloneableView)originalChildView; + + // Copy child node + var copyChildView = cloneableView.CloneView(); + copyChildView.Parent = groupView; + + // Make the sub views for child copying from the original to the child + CopySubViews(groupView.CriteriaExpressions, propertyNames, groupByValues, originalChildView, copyChildView, + agentInstanceContext); + + return copyChildView; + } + + private static void CopySubViews( + ExprNode[] criteriaExpressions, + String[] propertyNames, + Object groupByValues, + View originalView, + View copyView, + AgentInstanceViewFactoryChainContext agentInstanceContext) + { + foreach (var subView in originalView.Views) + { + // Determine if view is our merge view + if (subView is MergeViewMarker) + { + var mergeView = (MergeViewMarker)subView; + if (ExprNodeUtility.DeepEquals(mergeView.GroupFieldNames, criteriaExpressions)) + { + if (mergeView.EventType != copyView.EventType) + { + // We found our merge view - install a new data merge view on top of it + var addPropertyView = new AddPropertyValueOptionalView(agentInstanceContext, propertyNames, groupByValues, mergeView.EventType); + + // Add to the copied parent subview the view merge data view + copyView.AddView(addPropertyView); + + // Add to the new merge data view the actual single merge view instance that clients may attached to + addPropertyView.AddView(mergeView); + + // Add a parent view to the single merge view instance + mergeView.AddParentView(addPropertyView); + } + else + { + // Add to the copied parent subview the view merge data view + copyView.AddView(mergeView); + + // Add a parent view to the single merge view instance + mergeView.AddParentView(copyView); + } + + continue; + } + } + + if (!(subView is CloneableView)) + { + throw new EPException("Unexpected error copying subview"); + } + var cloneableView = (CloneableView)subView; + var copiedChild = cloneableView.CloneView(); + copyView.AddView(copiedChild); + + // Make the sub views for child + CopySubViews(criteriaExpressions, propertyNames, groupByValues, subView, copiedChild, agentInstanceContext); + } + } + + private Object GetGroupKey(EventBean theEvent) + { + _eventsPerStream[0] = theEvent; + if (_criteriaEvaluators.Length == 1) + { + return _criteriaEvaluators[0].Evaluate(new EvaluateParams(_eventsPerStream, true, _agentInstanceContext)); + } + + var values = new Object[_criteriaEvaluators.Length]; + var evaluateParams = new EvaluateParams(_eventsPerStream, true, _agentInstanceContext); + for (var i = 0; i < _criteriaEvaluators.Length; i++) + { + values[i] = _criteriaEvaluators[i].Evaluate(evaluateParams); + } + return new MultiKeyUntyped(values); + } + + internal static Object AddUpgradeToDequeIfPopulated(Object holder, EventBean theEvent) + { + if (holder == null) + { + return theEvent; + } + else if (holder is Deque) + { + var deque = (Deque)holder; + deque.Add(theEvent); + return deque; + } + else + { + var deque = new ArrayDeque(4); + deque.Add((EventBean)holder); + deque.Add(theEvent); + return deque; + } + } + + internal static EventBean[] ConvertToArray(Object eventOrDeque) + { + if (eventOrDeque == null) + { + return null; + } + if (eventOrDeque is EventBean) + { + return new EventBean[] { (EventBean)eventOrDeque }; + } + return ((ICollection)eventOrDeque).ToArray(); + } + + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + } +} diff --git a/NEsper.Core/NEsper.Core/view/std/GroupByViewReclaimAged.cs b/NEsper.Core/NEsper.Core/view/std/GroupByViewReclaimAged.cs new file mode 100755 index 000000000..03823a183 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/std/GroupByViewReclaimAged.cs @@ -0,0 +1,381 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.time; +using com.espertech.esper.util; + +namespace com.espertech.esper.view.std +{ + public class GroupByViewReclaimAged + : ViewSupport + , CloneableView + , GroupByView + { + private readonly ExprNode[] _criteriaExpressions; + private readonly ExprEvaluator[] _criteriaEvaluators; + private readonly AgentInstanceViewFactoryChainContext _agentInstanceContext; + private readonly long _reclaimMaxAge; + private readonly long _reclaimFrequency; + + private readonly EventBean[] _eventsPerStream = new EventBean[1]; + private readonly String[] _propertyNames; + + private readonly IDictionary _subViewsPerKey = + new Dictionary(); + private readonly Dictionary> _groupedEvents = + new Dictionary>(); + private long? _nextSweepTime = null; + + /// + /// Constructor. + /// + /// contains required view services + /// is the fields from which to pull the values to group by + /// The criteria evaluators. + /// age after which to reclaim group + /// frequency in which to check for groups to reclaim + public GroupByViewReclaimAged( + AgentInstanceViewFactoryChainContext agentInstanceContext, + ExprNode[] criteriaExpressions, + ExprEvaluator[] criteriaEvaluators, + double reclaimMaxAgeSeconds, + double reclaimFrequencySeconds) + { + _agentInstanceContext = agentInstanceContext; + _criteriaExpressions = criteriaExpressions; + _criteriaEvaluators = criteriaEvaluators; + + var timeAbacus = agentInstanceContext.StatementContext.EngineImportService.TimeAbacus; + _reclaimMaxAge = timeAbacus.DeltaForSecondsDouble(reclaimMaxAgeSeconds); + _reclaimFrequency = timeAbacus.DeltaForSecondsDouble(reclaimFrequencySeconds); + + _propertyNames = new String[criteriaExpressions.Length]; + for (var i = 0; i < criteriaExpressions.Length; i++) + { + _propertyNames[i] = ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(criteriaExpressions[i]); + } + } + + public View CloneView() + { + return new GroupByViewReclaimAged(_agentInstanceContext, _criteriaExpressions, _criteriaEvaluators, _reclaimMaxAge, _reclaimFrequency); + } + + /// Returns the field name that provides the key valie by which to group by. + /// field name providing group-by key. + public ExprNode[] CriteriaExpressions + { + get { return _criteriaExpressions; } + } + + public override EventType EventType + { + get + { + // The schema is the parent view's schema + return Parent.EventType; + } + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + var currentTime = _agentInstanceContext.TimeProvider.Time; + if ((_nextSweepTime == null) || (_nextSweepTime <= currentTime)) + { + if ((ExecutionPathDebugLog.IsEnabled) && (Log.IsDebugEnabled)) + { + Log.Debug("Reclaiming groups older then " + _reclaimMaxAge + " msec and every " + _reclaimFrequency + "msec in frequency"); + } + _nextSweepTime = currentTime + _reclaimFrequency; + Sweep(currentTime); + } + + // Algorithm for single new event + if ((newData != null) && (oldData == null) && (newData.Length == 1)) + { + var theEvent = newData[0]; + var newDataToPost = new EventBean[] { theEvent }; + + var groupByValuesKey = GetGroupKey(theEvent); + + // Get child views that belong to this group-by value combination + var subViews = _subViewsPerKey.Get(groupByValuesKey); + + // If this is a new group-by value, the list of subviews is null and we need to make clone sub-views + if (subViews == null) + { + var subviewsList = GroupByViewImpl.MakeSubViews(this, _propertyNames, groupByValuesKey, _agentInstanceContext); + subViews = new GroupByViewAgedEntry(subviewsList, currentTime); + _subViewsPerKey.Put(groupByValuesKey, subViews); + } + else + { + subViews.LastUpdateTime = currentTime; + } + + GroupByViewImpl.UpdateChildViews(subViews.SubviewHolder, newDataToPost, null); + } + else + { + + // Algorithm for dispatching multiple events + if (newData != null) + { + foreach (var newValue in newData) + { + HandleEvent(newValue, true); + } + } + + if (oldData != null) + { + foreach (var oldValue in oldData) + { + HandleEvent(oldValue, false); + } + } + + // Update child views + foreach (var entry in _groupedEvents) + { + var newEvents = GroupByViewImpl.ConvertToArray(entry.Value.First); + var oldEvents = GroupByViewImpl.ConvertToArray(entry.Value.Second); + GroupByViewImpl.UpdateChildViews(entry.Key, newEvents, oldEvents); + } + + _groupedEvents.Clear(); + } + } + + private void HandleEvent(EventBean theEvent, bool isNew) + { + var groupByValuesKey = GetGroupKey(theEvent); + + // Get child views that belong to this group-by value combination + var subViews = _subViewsPerKey.Get(groupByValuesKey); + + // If this is a new group-by value, the list of subviews is null and we need to make clone sub-views + if (subViews == null) + { + var subviewsList = GroupByViewImpl.MakeSubViews(this, _propertyNames, groupByValuesKey, _agentInstanceContext); + var currentTime = _agentInstanceContext.StatementContext.TimeProvider.Time; + subViews = new GroupByViewAgedEntry(subviewsList, currentTime); + _subViewsPerKey.Put(groupByValuesKey, subViews); + } + else + { + subViews.LastUpdateTime = _agentInstanceContext.StatementContext.TimeProvider.Time; + } + + // Construct a pair of lists to hold the events for the grouped value if not already there + var pair = _groupedEvents.Get(subViews); + if (pair == null) + { + pair = new Pair(null, null); + _groupedEvents.Put(subViews, pair); + } + + // Add event to a child view event list for later child Update that includes new and old events + if (isNew) + { + pair.First = GroupByViewImpl.AddUpgradeToDequeIfPopulated(pair.First, theEvent); + } + else + { + pair.Second = GroupByViewImpl.AddUpgradeToDequeIfPopulated(pair.Second, theEvent); + } + } + + public override IEnumerator GetEnumerator() + { + throw new UnsupportedOperationException("Cannot iterate over group view, this operation is not supported"); + } + + public override String ToString() + { + return GetType().FullName + " groupFieldNames=" + _criteriaExpressions.Render(); + } + + public void VisitViewContainer(ViewDataVisitorContained viewDataVisitor) + { + viewDataVisitor.VisitPrimary(GroupByViewImpl.VIEWNAME, _subViewsPerKey.Count); + foreach (var entry in _subViewsPerKey) + { + GroupByViewImpl.VisitView(viewDataVisitor, entry.Key, entry.Value.SubviewHolder); + } + } + + private void Sweep(long currentTime) + { + var removed = new ArrayDeque(); + foreach (var entry in _subViewsPerKey) + { + var age = currentTime - entry.Value.LastUpdateTime; + if (age > _reclaimMaxAge) + { + removed.Add(entry.Key); + } + } + + foreach (var key in removed) + { + var entry = _subViewsPerKey.Delete(key); + var subviewHolder = entry.SubviewHolder; + if (subviewHolder is IList) + { + var subviews = (IList)subviewHolder; + foreach (var view in subviews) + { + RemoveSubview(view); + } + } + else if (subviewHolder is View) + { + RemoveSubview((View)subviewHolder); + } + } + } + + public override bool RemoveView(View view) + { + if (!(view is GroupableView)) + { + base.RemoveView(view); + } + var removed = base.RemoveView(view); + if (!removed) + { + return false; + } + if (!HasViews) + { + _subViewsPerKey.Clear(); + return true; + } + var removedView = (GroupableView)view; + Deque removedKeys = null; + foreach (var entry in _subViewsPerKey) + { + var value = entry.Value.SubviewHolder; + if (value is View) + { + var subview = (GroupableView)value; + if (CompareViews(subview, removedView)) + { + if (removedKeys == null) + { + removedKeys = new ArrayDeque(); + } + removedKeys.Add(entry.Key); + } + } + else if (value is IList) + { + var subviews = (IList)value; + for (var i = 0; i < subviews.Count; i++) + { + var subview = (GroupableView)subviews[i]; + if (CompareViews(subview, removedView)) + { + subviews.RemoveAt(i); + if (subviews.IsEmpty()) + { + if (removedKeys == null) + { + removedKeys = new ArrayDeque(); + } + removedKeys.Add(entry.Key); + } + break; + } + } + } + } + if (removedKeys != null) + { + foreach (var key in removedKeys) + { + _subViewsPerKey.Remove(key); + } + } + return true; + } + + private bool CompareViews(GroupableView subview, GroupableView removed) + { + return subview.ViewFactory == removed.ViewFactory; + } + + private void RemoveSubview(View view) + { + view.Parent = null; + RecursiveMergeViewRemove(view); + var stoppableView = view as StoppableView; + if (stoppableView != null) + { + stoppableView.Stop(); + } + } + + private void RecursiveMergeViewRemove(View view) + { + foreach (var child in view.Views) + { + var mergeView = child as MergeView; + if (mergeView != null) + { + mergeView.RemoveParentView(view); + } + else + { + var stoppableView = child as StoppableView; + if (stoppableView != null) + { + stoppableView.Stop(); + } + if (child.Views.Length > 0) + { + RecursiveMergeViewRemove(child); + } + } + } + } + + private Object GetGroupKey(EventBean theEvent) + { + var evaluateParams = new EvaluateParams(_eventsPerStream, true, _agentInstanceContext); + + _eventsPerStream[0] = theEvent; + if (_criteriaEvaluators.Length == 1) + { + return _criteriaEvaluators[0].Evaluate(evaluateParams); + } + + var values = new Object[_criteriaEvaluators.Length]; + for (var i = 0; i < _criteriaEvaluators.Length; i++) + { + values[i] = _criteriaEvaluators[i].Evaluate(evaluateParams); + } + return new MultiKeyUntyped(values); + } + + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + } +} diff --git a/NEsper.Core/NEsper.Core/view/std/LastElementView.cs b/NEsper.Core/NEsper.Core/view/std/LastElementView.cs new file mode 100755 index 000000000..fe4b87c04 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/std/LastElementView.cs @@ -0,0 +1,147 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.view.std +{ + /// + /// This view is a very simple view presenting the last event posted by the parent view + /// to any subviews. Only the very last event object is kept by this view. The Update + /// method invoked by the parent view supplies new data in an object array, of which + /// the view keeps the very last instance as the 'last' or newest event. The view + /// always has the same schema as the parent view and attaches to anything, and accepts + /// no parameters. Thus if 5 pieces of new data arrive, the child view receives 5 elements + /// of new data and also 4 pieces of old data which is the first 4 elements of new data. + /// i.e. New data elements immediatly gets to be old data elements. Old data received from + /// parent is not handled, it is ignored. We thus post old data as follows: last event is + /// not null + new data from index zero to Count-1, where Count is the index of the last element in + /// new data + /// + public class LastElementView + : ViewSupport + , CloneableView + , DataWindowView + { + private readonly LastElementViewFactory _viewFactory; + + /// + /// The last new element posted from a parent view. + /// + private EventBean _lastEvent; + + public LastElementView(LastElementViewFactory viewFactory) + { + this._viewFactory = viewFactory; + } + + public View CloneView() + { + return new LastElementView(_viewFactory); + } + + public override EventType EventType + { + get + { + // The schema is the parent view's schema + return Parent.EventType; + } + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QViewProcessIRStream(this, LastElementViewFactory.NAME, newData, oldData);} + OneEventCollection oldDataToPost = null; + + if ((newData != null) && (newData.Length != 0)) + { + if (_lastEvent != null) + { + oldDataToPost = new OneEventCollection(); + oldDataToPost.Add(_lastEvent); + } + if (newData.Length > 1) + { + for (int i = 0; i < newData.Length - 1; i++) + { + if (oldDataToPost == null) + { + oldDataToPost = new OneEventCollection(); + } + oldDataToPost.Add(newData[i]); + } + } + _lastEvent = newData[newData.Length - 1]; + } + + if (oldData != null) + { + for (int i = 0; i < oldData.Length; i++) + { + if (oldData[i] == _lastEvent) + { + if (oldDataToPost == null) + { + oldDataToPost = new OneEventCollection(); + } + oldDataToPost.Add(oldData[i]); + _lastEvent = null; + } + } + } + + // If there are child views, fireStatementStopped Update method + if (HasViews) + { + if ((oldDataToPost != null) && (!oldDataToPost.IsEmpty())) + { + EventBean[] oldDataArray = oldDataToPost.ToArray(); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QViewIndicate(this, LastElementViewFactory.NAME, newData, oldDataArray);} + UpdateChildren(newData, oldDataArray); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AViewIndicate();} + } + else + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QViewIndicate(this, LastElementViewFactory.NAME, newData, null);} + UpdateChildren(newData, null); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AViewIndicate();} + } + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AViewProcessIRStream();} + } + + public override IEnumerator GetEnumerator() + { + if (_lastEvent != null) + { + yield return _lastEvent; + } + } + + public override String ToString() + { + return GetType().FullName; + } + + public void VisitView(ViewDataVisitor viewDataVisitor) + { + viewDataVisitor.VisitPrimary(_lastEvent, LastElementViewFactory.NAME); + } + + public ViewFactory ViewFactory + { + get { return _viewFactory; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/std/LastElementViewFactory.cs b/NEsper.Core/NEsper.Core/view/std/LastElementViewFactory.cs new file mode 100755 index 000000000..d88d54216 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/std/LastElementViewFactory.cs @@ -0,0 +1,59 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.view.std +{ + /// Factory for instances. + public class LastElementViewFactory : DataWindowViewFactory + { + public static readonly string NAME = "Last-Event"; + + private EventType _eventType; + + public void SetViewParameters(ViewFactoryContext viewFactoryContext, IList expressionParameters) + { + ViewFactorySupport.ValidateNoParameters(ViewName, expressionParameters); + } + + public void Attach( + EventType parentEventType, + StatementContext statementContext, + ViewFactory optionalParentFactory, + IList parentViewFactories) + { + _eventType = parentEventType; + } + + public View MakeView(AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext) + { + return new LastElementView(this); + } + + public EventType EventType + { + get { return _eventType; } + } + + public bool CanReuse(View view, AgentInstanceContext agentInstanceContext) + { + return view is LastElementView; + } + + public string ViewName + { + get { return NAME; } + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/view/std/MergeView.cs b/NEsper.Core/NEsper.Core/view/std/MergeView.cs new file mode 100755 index 000000000..baef2e61e --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/std/MergeView.cs @@ -0,0 +1,123 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.view.std +{ + /// + /// The merge view works together with a group view that splits the data in a stream to + /// multiple subviews, based on a key index. Every group view requires a merge view to + /// merge the many subviews back into a single view. Typically the last view in a chain + /// containing a group view is a merge view. The merge view has no other responsibility + /// then becoming the single last instance in the chain to which external listeners for + /// updates can be attached to receive updates for the many subviews that have this merge + /// view as common child views. The parent view of this view is generally the AddPropertyValueView + /// that adds the grouped-by information back into the data. + /// + public sealed class MergeView : ViewSupport, CloneableView, MergeViewMarker + { + private readonly AgentInstanceViewFactoryChainContext _agentInstanceContext; + private readonly ICollection _parentViews; + private readonly ExprNode[] _groupFieldNames; + private readonly EventType _eventType; + private readonly bool _removable; + + /// + /// Constructor. + /// + /// The agent instance context. + /// is the fields from which to pull the value to group by + /// is passed by the factory as the factory adds the merged fields to an event type + /// if set to true [removable]. + public MergeView( + AgentInstanceViewFactoryChainContext agentInstanceContext, + ExprNode[] groupCriteria, + EventType resultEventType, + bool removable) + { + _removable = removable; + if (!removable) + { + _parentViews = new LinkedList(); + } + else + { + _parentViews = new HashSet(); + } + _agentInstanceContext = agentInstanceContext; + _groupFieldNames = groupCriteria; + _eventType = resultEventType; + } + + public View CloneView() + { + return new MergeView(_agentInstanceContext, _groupFieldNames, _eventType, _removable); + } + + /// Returns the field name that contains the values to group by. + /// field name providing group key value + public ExprNode[] GroupFieldNames + { + get { return _groupFieldNames; } + } + + /// Add a parent data merge view. + /// is the parent data merge view to add + public void AddParentView(View parentView) + { + _parentViews.Add(parentView); + } + + public override EventType EventType + { + get + { + // The schema is the parent view's type, or the type plus the added Field(s) + return _eventType; + } + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + UpdateChildren(newData, oldData); + } + + public override IEnumerator GetEnumerator() + { + // The merge data view has multiple parent views which are AddPropertyValueView + var iterables = new LinkedList>(); + + foreach (View dataView in _parentViews) + { + iterables.AddLast(dataView); + } + + return iterables.SelectMany(parentEnum => parentEnum).GetEnumerator(); + } + + public override String ToString() + { + return GetType().FullName + " groupFieldName=" + _groupFieldNames.Render(); + } + + public void RemoveParentView(View view) + { + _parentViews.Remove(view); + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/std/MergeViewFactory.cs b/NEsper.Core/NEsper.Core/view/std/MergeViewFactory.cs new file mode 100755 index 000000000..adec0b441 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/std/MergeViewFactory.cs @@ -0,0 +1,155 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.view.std +{ + /// + /// Factory for instances. + /// + public class MergeViewFactory + : ViewFactory + , MergeViewFactoryMarker + { + private IList _viewParameters; + private int _streamNumber; + + private ExprNode[] _criteriaExpressions; + private EventType _eventType; + private bool _removable = false; // set to true when retain-age + + public void SetViewParameters(ViewFactoryContext viewFactoryContext, IList expressionParameters) + { + _viewParameters = expressionParameters; + _streamNumber = viewFactoryContext.StreamNum; + } + + public void Attach(EventType parentEventType, StatementContext statementContext, ViewFactory optionalParentFactory, IList parentViewFactories) + { + // Find the group by view matching the merge view + GroupByViewFactoryMarker groupByViewFactory = null; + var unvalidated = _viewParameters.ToArray(); + foreach (var parentView in parentViewFactories) + { + if (!(parentView is GroupByViewFactoryMarker)) + { + continue; + } + var candidateGroupByView = (GroupByViewFactoryMarker)parentView; + if (ExprNodeUtility.DeepEquals(candidateGroupByView.CriteriaExpressions, unvalidated)) + { + groupByViewFactory = candidateGroupByView; + } + } + + if (groupByViewFactory == null) + { + throw new ViewParameterException("Groupwin view for this merge view could not be found among parent views"); + } + _criteriaExpressions = groupByViewFactory.CriteriaExpressions; + _removable = groupByViewFactory.IsReclaimAged; + + // determine types of fields + var fieldTypes = new Type[_criteriaExpressions.Length]; + for (var i = 0; i < fieldTypes.Length; i++) + { + fieldTypes[i] = _criteriaExpressions[i].ExprEvaluator.ReturnType; + } + + // Determine the readonly event type that the merge view generates + // This event type is ultimatly generated by AddPropertyValueView which is added to each view branch for each + // group key. + + // If the parent event type contains the merge fields, we use the same event type + var parentContainsMergeKeys = true; + var fieldNames = new String[_criteriaExpressions.Length]; + for (var i = 0; i < _criteriaExpressions.Length; i++) + { + var name = ExprNodeUtility.ToExpressionStringMinPrecedenceSafe(_criteriaExpressions[i]); + fieldNames[i] = name; + + try + { + if (!(parentEventType.IsProperty(name))) + { + parentContainsMergeKeys = false; + } + } + catch (PropertyAccessException ex) + { + // expected + parentContainsMergeKeys = false; + } + } + + // If the parent view contains the fields to group by, the event type after merging stays the same + if (parentContainsMergeKeys) + { + _eventType = parentEventType; + } + else + // If the parent event type does not contain the fields, such as when a statistics views is + // grouped which simply provides a map of calculated values, + // then we need to add in the merge field as an event property thus changing event types. + { + IDictionary additionalProps = new Dictionary(); + for (var i = 0; i < fieldNames.Length; i++) + { + additionalProps.Put(fieldNames[i], fieldTypes[i]); + } + var outputEventTypeName = statementContext.StatementId + "_mergeview_" + _streamNumber; + _eventType = statementContext.EventAdapterService.CreateAnonymousWrapperType(outputEventTypeName, parentEventType, additionalProps); + } + } + + public View MakeView(AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext) + { + return new MergeView(agentInstanceViewFactoryContext, _criteriaExpressions, _eventType, _removable); + } + + public EventType EventType + { + get { return _eventType; } + } + + public bool CanReuse(View view, AgentInstanceContext agentInstanceContext) + { + if (!(view is MergeView)) + { + return false; + } + + var myView = (MergeView)view; + if (!ExprNodeUtility.DeepEquals(myView.GroupFieldNames, _criteriaExpressions)) + { + return false; + } + return true; + } + + public ExprNode[] CriteriaExpressions + { + get { return _criteriaExpressions; } + } + + public string ViewName + { + get { return "Group-By-Merge"; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/std/MergeViewFactoryMarker.cs b/NEsper.Core/NEsper.Core/view/std/MergeViewFactoryMarker.cs new file mode 100755 index 000000000..2aacb0ed6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/std/MergeViewFactoryMarker.cs @@ -0,0 +1,14 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.view.std +{ + public interface MergeViewFactoryMarker + { + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/view/std/MergeViewMarker.cs b/NEsper.Core/NEsper.Core/view/std/MergeViewMarker.cs new file mode 100755 index 000000000..0547e537d --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/std/MergeViewMarker.cs @@ -0,0 +1,19 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.view.std +{ + public interface MergeViewMarker : View + { + ExprNode[] GroupFieldNames { get; } + + void AddParentView(View mergeDataView); + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/view/std/SizeView.cs b/NEsper.Core/NEsper.Core/view/std/SizeView.cs new file mode 100755 index 000000000..88180cf88 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/std/SizeView.cs @@ -0,0 +1,169 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.view.stat; + +namespace com.espertech.esper.view.std +{ + /// + /// This view is a very simple view presenting the number of elements in a stream + /// or view. The view computes a single long-typed count of the number of events + /// passed through it similar to the base statistics COUNT column. + /// + public class SizeView : ViewSupport, CloneableView + { + private readonly AgentInstanceContext _agentInstanceContext; + private readonly EventType _eventType; + private readonly StatViewAdditionalProps _additionalProps; + + private long _size = 0; + private EventBean _lastSizeEvent; + private Object[] _lastValuesEventNew; + + /// + /// Ctor. + /// + /// is services + /// Type of the event. + /// The additional props. + public SizeView(AgentInstanceContext agentInstanceContext, EventType eventType, StatViewAdditionalProps additionalProps) + { + _agentInstanceContext = agentInstanceContext; + _eventType = eventType; + _additionalProps = additionalProps; + } + + public View CloneView() + { + return new SizeView(_agentInstanceContext, _eventType, _additionalProps); + } + + public override EventType EventType + { + get { return _eventType; } + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QViewProcessIRStream(this, SizeViewFactory.NAME, newData, oldData); } + var priorSize = _size; + + // If we have child views, keep a reference to the old values, so we can Update them as old data event. + EventBean oldDataMap = null; + if (_lastSizeEvent == null) + { + if (HasViews) + { + IDictionary postOldData = new Dictionary(); + postOldData.Put(ViewFieldEnum.SIZE_VIEW__SIZE.GetName(), priorSize); + AddProperties(postOldData); + oldDataMap = _agentInstanceContext.StatementContext.EventAdapterService.AdapterForTypedMap(postOldData, _eventType); + } + } + + // add data points to the window + if (newData != null) + { + _size += newData.Length; + + if ((_additionalProps != null) && (newData.Length != 0)) + { + if (_lastValuesEventNew == null) + { + _lastValuesEventNew = new Object[_additionalProps.AdditionalExpr.Length]; + } + for (var val = 0; val < _additionalProps.AdditionalExpr.Length; val++) + { + _lastValuesEventNew[val] = _additionalProps.AdditionalExpr[val].Evaluate( + new EvaluateParams(new EventBean[] { newData[newData.Length - 1] }, true, _agentInstanceContext)); + } + } + } + + if (oldData != null) + { + _size -= oldData.Length; + } + + // If there are child views, fireStatementStopped Update method + if ((HasViews) && (priorSize != _size)) + { + IDictionary postNewData = new Dictionary(); + postNewData.Put(ViewFieldEnum.SIZE_VIEW__SIZE.GetName(), _size); + AddProperties(postNewData); + var newEvent = _agentInstanceContext.StatementContext.EventAdapterService.AdapterForTypedMap(postNewData, _eventType); + + EventBean[] oldEvents; + if (_lastSizeEvent != null) + { + oldEvents = new EventBean[] { _lastSizeEvent }; + } + else + { + oldEvents = new EventBean[] { oldDataMap }; + } + var newEvents = new EventBean[] { newEvent }; + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QViewIndicate(this, SizeViewFactory.NAME, newEvents, oldEvents); } + UpdateChildren(newEvents, oldEvents); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AViewIndicate(); } + + _lastSizeEvent = newEvent; + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AViewProcessIRStream(); } + } + + public override IEnumerator GetEnumerator() + { + var current = new Dictionary(); + current.Put(ViewFieldEnum.SIZE_VIEW__SIZE.GetName(), _size); + AddProperties(current); + yield return _agentInstanceContext.StatementContext.EventAdapterService.AdapterForTypedMap(current, _eventType); + } + + public override String ToString() + { + return GetType().FullName; + } + + /// + /// Creates the event type for this view + /// + /// is the event adapter service + /// The additional props. + /// The stream num. + /// event type for view + public static EventType CreateEventType(StatementContext statementContext, StatViewAdditionalProps additionalProps, int streamNum) + { + var schemaMap = new Dictionary(); + schemaMap.Put(ViewFieldEnum.SIZE_VIEW__SIZE.GetName(), typeof(long?)); + StatViewAdditionalProps.AddCheckDupProperties(schemaMap, additionalProps, ViewFieldEnum.SIZE_VIEW__SIZE); + var outputEventTypeName = statementContext.StatementId + "_sizeview_" + streamNum; + return statementContext.EventAdapterService.CreateAnonymousMapType(outputEventTypeName, schemaMap, false); + } + + private void AddProperties(IDictionary newDataMap) + { + if (_additionalProps == null) + { + return; + } + _additionalProps.AddProperties(newDataMap, _lastValuesEventNew); + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/std/SizeViewFactory.cs b/NEsper.Core/NEsper.Core/view/std/SizeViewFactory.cs new file mode 100755 index 000000000..7c939aaa7 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/std/SizeViewFactory.cs @@ -0,0 +1,78 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.view.stat; + +namespace com.espertech.esper.view.std +{ + /// Factory for instances. + public class SizeViewFactory : ViewFactory + { + public readonly static String NAME = "Count"; + + private IList _viewParameters; + private int _streamNumber; + + protected StatViewAdditionalProps _additionalProps; + + private EventType _eventType; + + public void SetViewParameters(ViewFactoryContext viewFactoryContext, IList expressionParameters) + { + _viewParameters = expressionParameters; + _streamNumber = viewFactoryContext.StreamNum; + } + + public void Attach(EventType parentEventType, StatementContext statementContext, ViewFactory optionalParentFactory, IList parentViewFactories) + { + ExprNode[] validated = ViewFactorySupport.Validate(ViewName, parentEventType, statementContext, _viewParameters, true); + _additionalProps = StatViewAdditionalProps.Make(validated, 0, parentEventType); + _eventType = SizeView.CreateEventType(statementContext, _additionalProps, _streamNumber); + } + + public View MakeView(AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext) + { + return new SizeView(agentInstanceViewFactoryContext.AgentInstanceContext, _eventType, _additionalProps); + } + + public EventType EventType + { + get { return _eventType; } + } + + public bool CanReuse(View view, AgentInstanceContext agentInstanceContext) + { + if (!(view is SizeView)) + { + return false; + } + if (_additionalProps != null) + { + return false; + } + return true; + } + + public string ViewName + { + get { return NAME; } + } + + public StatViewAdditionalProps AdditionalProps + { + get { return _additionalProps; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/std/UniqueByPropertyView.cs b/NEsper.Core/NEsper.Core/view/std/UniqueByPropertyView.cs new file mode 100755 index 000000000..094a90b44 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/std/UniqueByPropertyView.cs @@ -0,0 +1,203 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.view.std +{ + /// + /// This view includes only the most recent among events having the same value for the specified field or fields. + /// The view accepts the field name as parameter from which the unique values are obtained. + /// For example, a trade's symbol could be used as a unique value. + /// In this example, the first trade for symbol IBM would be posted as new data to child views. + /// When the second trade for symbol IBM arrives the second trade is posted as new data to child views, + /// and the first trade is posted as old data. + /// Should more than one trades for symbol IBM arrive at the same time (like when batched) + /// then the child view will get all new events in newData and all new events in oldData minus the most recent event. + /// When the current new event arrives as old data, the the current unique event gets thrown away and + /// posted as old data to child views. + /// Iteration through the views data shows only the most recent events received for the unique value in the order + /// that events arrived in. + /// The type of the field returning the unique value can be any type but should override equals and hashCode() + /// as the type plays the role of a key in a map storing unique values. + /// + public class UniqueByPropertyView + : ViewSupport + , CloneableView + , DataWindowView + { + private readonly UniqueByPropertyViewFactory _viewFactory; + private readonly ExprEvaluator[] _criteriaExpressionsEvals; + private readonly IDictionary _mostRecentEvents = new NullableDictionary().WithNullSupport(); + private readonly EventBean[] _eventsPerStream = new EventBean[1]; + private readonly AgentInstanceViewFactoryChainContext _agentInstanceViewFactoryContext; + + /// + /// Constructor. + /// + /// context for expression evaluation + public UniqueByPropertyView(UniqueByPropertyViewFactory viewFactory, AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext) + { + _viewFactory = viewFactory; + _criteriaExpressionsEvals = ExprNodeUtility.GetEvaluators(viewFactory.CriteriaExpressions); + _agentInstanceViewFactoryContext = agentInstanceViewFactoryContext; + } + + public View CloneView() + { + return new UniqueByPropertyView(_viewFactory, _agentInstanceViewFactoryContext); + } + + /// + /// Returns the name of the field supplying the unique value to keep the most recent record for. + /// + /// expressions for unique value + public ExprNode[] CriteriaExpressions + { + get { return _viewFactory.CriteriaExpressions; } + } + + public override EventType EventType + { + get + { + // The schema is the parent view's schema + return Parent.EventType; + } + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QViewProcessIRStream(this, UniqueByPropertyViewFactory.NAME, newData, oldData); } + OneEventCollection postOldData = null; + + if (HasViews) + { + postOldData = new OneEventCollection(); + } + + if (newData != null) + { + for (var i = 0; i < newData.Length; i++) + { + // Obtain unique value + var key = GetUniqueKey(newData[i]); + + // If there are no child views, just update the own collection + if (!HasViews) + { + _mostRecentEvents.Put(key, newData[i]); + continue; + } + + // Post the last value as old data + var lastValue = _mostRecentEvents.Get(key); + if (lastValue != null) + { + postOldData.Add(lastValue); + } + + // Override with recent event + _mostRecentEvents.Put(key, newData[i]); + } + } + + if (oldData != null) + { + for (var i = 0; i < oldData.Length; i++) + { + // Obtain unique value + var key = GetUniqueKey(oldData[i]); + + // If the old event is the current unique event, remove and post as old data + var lastValue = _mostRecentEvents.Get(key); + if (lastValue == null || !lastValue.Equals(oldData[i])) + { + continue; + } + + postOldData.Add(lastValue); + _mostRecentEvents.Remove(key); + } + } + + // If there are child views, fireStatementStopped update method + if (HasViews) + { + if (postOldData.IsEmpty()) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QViewIndicate(this, UniqueByPropertyViewFactory.NAME, newData, null); } + UpdateChildren(newData, null); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AViewIndicate(); } + } + else + { + var postOldDataArray = postOldData.ToArray(); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QViewIndicate(this, UniqueByPropertyViewFactory.NAME, newData, postOldDataArray); } + UpdateChildren(newData, postOldDataArray); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AViewIndicate(); } + } + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AViewProcessIRStream(); } + } + + /// + /// Returns true if the view is empty. + /// + /// true if empty + public bool IsEmpty() + { + return _mostRecentEvents.IsEmpty(); + } + + public override IEnumerator GetEnumerator() + { + return _mostRecentEvents.Values.GetEnumerator(); + } + + public override string ToString() + { + return GetType().Name + " uniqueFieldNames=" + _viewFactory.CriteriaExpressions; + } + + protected object GetUniqueKey(EventBean theEvent) + { + var evaluateParams = new EvaluateParams(_eventsPerStream, true, _agentInstanceViewFactoryContext); + + _eventsPerStream[0] = theEvent; + if (_criteriaExpressionsEvals.Length == 1) + { + return _criteriaExpressionsEvals[0].Evaluate(evaluateParams); + } + + var values = new object[_criteriaExpressionsEvals.Length]; + for (var i = 0; i < _criteriaExpressionsEvals.Length; i++) + { + values[i] = _criteriaExpressionsEvals[i].Evaluate(evaluateParams); + } + return new MultiKeyUntyped(values); + } + + public void VisitView(ViewDataVisitor viewDataVisitor) + { + viewDataVisitor.VisitPrimary(_mostRecentEvents, true, UniqueByPropertyViewFactory.NAME, _mostRecentEvents.Count, _mostRecentEvents.Count); + } + + public ViewFactory ViewFactory + { + get { return _viewFactory; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/view/std/UniqueByPropertyViewFactory.cs b/NEsper.Core/NEsper.Core/view/std/UniqueByPropertyViewFactory.cs new file mode 100755 index 000000000..cba609588 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/std/UniqueByPropertyViewFactory.cs @@ -0,0 +1,99 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; + +namespace com.espertech.esper.view.std +{ + /// + /// Factory for instances. + /// + public class UniqueByPropertyViewFactory : DataWindowViewFactoryUniqueCandidate, DataWindowViewFactory + { + public readonly static String NAME = "Unique-By"; + + /// View parameters. + private IList _viewParameters; + + /// Property name to evaluate unique values. + private ExprNode[] _criteriaExpressions; + private EventType _eventType; + + public void SetViewParameters(ViewFactoryContext viewFactoryContext, IList expressionParameters) + { + _viewParameters = expressionParameters; + } + + public void Attach(EventType parentEventType, StatementContext statementContext, ViewFactory optionalParentFactory, IList parentViewFactories) + { + _criteriaExpressions = ViewFactorySupport.Validate(ViewName, parentEventType, statementContext, _viewParameters, false); + + if (_criteriaExpressions.Length == 0) + { + String errorMessage = ViewName + " view requires a one or more expressions providing unique values as parameters"; + throw new ViewParameterException(errorMessage); + } + + _eventType = parentEventType; + } + + public View MakeView(AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext) + { + return new UniqueByPropertyView(this, agentInstanceViewFactoryContext); + } + + public EventType EventType + { + get { return _eventType; } + } + + public bool CanReuse(View view, AgentInstanceContext agentInstanceContext) + { + if (!(view is UniqueByPropertyView)) + { + return false; + } + + UniqueByPropertyView myView = (UniqueByPropertyView)view; + if (!ExprNodeUtility.DeepEquals(_criteriaExpressions, myView.CriteriaExpressions)) + { + return false; + } + + return myView.IsEmpty(); + } + + public ICollection UniquenessCandidatePropertyNames + { + get { return ExprNodeUtility.GetPropertyNamesIfAllProps(_criteriaExpressions); } + } + + public IList ViewParameters + { + get { return _viewParameters; } + } + + public ExprNode[] CriteriaExpressions + { + get { return _criteriaExpressions; } + set { _criteriaExpressions = value; } + } + + public string ViewName + { + get { return NAME; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/stream/EventStreamProxy.cs b/NEsper.Core/NEsper.Core/view/stream/EventStreamProxy.cs new file mode 100755 index 000000000..7e5cf4be2 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/stream/EventStreamProxy.cs @@ -0,0 +1,119 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; + +using Castle.DynamicProxy; + +using com.espertech.esper.client; +using com.espertech.esper.client.annotation; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.events; +using com.espertech.esper.filter; +using com.espertech.esper.util; + +namespace com.espertech.esper.view.stream +{ + public class EventStreamProxy : IInterceptor + { + private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private readonly String _engineURI; + private readonly String _statementName; + private readonly String _eventTypeAndFilter; + private readonly EventStream _eventStream; + + public static EventStream NewInstance(String engineURI, String statementName, String eventTypeAndFilter, EventStream eventStream) + { + var generator = new ProxyGenerator(); + return (EventStream) generator.CreateInterfaceProxyWithoutTarget( + typeof(EventStream), + eventStream.GetType().GetInterfaces(), + new EventStreamProxy(engineURI, statementName, eventTypeAndFilter, eventStream)); + } + + public static EventStream GetAuditProxy(String engineURI, String statementName, Attribute[] annotations, FilterSpecCompiled filterSpec, EventStream designated) + { + var audit = AuditEnum.STREAM.GetAudit(annotations); + if (audit == null) + { + return designated; + } + + var writer = new StringWriter(); + writer.Write(filterSpec.FilterForEventType.Name); + if (filterSpec.Parameters != null && filterSpec.Parameters.Length > 0) + { + writer.Write('('); + String delimiter = ""; + foreach (FilterSpecParam[] paramLine in filterSpec.Parameters) + { + writer.Write(delimiter); + WriteFilter(writer, paramLine); + delimiter = " or "; + } + writer.Write(')'); + } + + return EventStreamProxy.NewInstance(engineURI, statementName, writer.ToString(), designated); + } + + private static void WriteFilter(TextWriter writer, FilterSpecParam[] paramLine) + { + String delimiter = ""; + foreach (FilterSpecParam param in paramLine) + { + writer.Write(delimiter); + writer.Write(param.Lookupable.Expression); + writer.Write(param.FilterOperator.GetTextualOp()); + writer.Write("..."); + delimiter = ","; + } + } + + public EventStreamProxy(String engineURI, String statementName, String eventTypeAndFilter, EventStream eventStream) + { + _engineURI = engineURI; + _statementName = statementName; + _eventTypeAndFilter = eventTypeAndFilter; + _eventStream = eventStream; + } + + /// + /// Intercepts the specified invocation. + /// + /// The invocation. + public void Intercept(IInvocation invocation) + { + if (invocation.Method.Name == "Insert") + { + if (AuditPath.IsInfoEnabled) + { + var arg = invocation.Arguments[0]; + var events = "(undefined)"; + if (arg is EventBean[]) + { + events = EventBeanUtility.Summarize((EventBean[])arg); + } + else if (arg is EventBean) + { + events = EventBeanUtility.Summarize((EventBean)arg); + } + AuditPath.AuditLog(_engineURI, _statementName, AuditEnum.STREAM, _eventTypeAndFilter + " inserted " + events); + + } + } + + invocation.ReturnValue = invocation.Method.Invoke(_eventStream, invocation.Arguments); + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/stream/StreamFactoryService.cs b/NEsper.Core/NEsper.Core/view/stream/StreamFactoryService.cs new file mode 100755 index 000000000..1e1242340 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/stream/StreamFactoryService.cs @@ -0,0 +1,77 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +using com.espertech.esper.collection; +using com.espertech.esper.compat.threading; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.filter; + +namespace com.espertech.esper.view.stream +{ + /// + /// Service on top of the filter service for reuseing filter callbacks and their associated + /// EventStream instances. Same filter specifications (equal) do not need to be added to the + /// filter service twice and the EventStream instance that is the stream of events for that + /// filter can be reused. + /// + /// We are re-using streams such that views under such streams can be reused for efficient + /// resource use. + /// + public interface StreamFactoryService + { + /// + /// Create or reuse existing EventStream instance representing that event filter. When called for some filters, should return same stream. + /// + /// the statement id + /// event filter definition + /// filter service to activate filter if not already active + /// is the statements-own handle for use in registering callbacks with services + /// is indicatng whether the stream will participate in a join statement, informationnecessary for stream reuse and multithreading concerns + /// The agent instance context. + /// if the consumer has order-by + /// if set to true [filter with same type subselect]. + /// The annotations. + /// if set to true [stateless]. + /// The stream num. + /// if set to true [is can iterate unbound]. + /// + /// event stream representing active filter + /// + Pair CreateStream( + int statementId, + FilterSpecCompiled filterSpec, + FilterService filterService, + EPStatementAgentInstanceHandle epStatementAgentInstanceHandle, + bool isJoin, + AgentInstanceContext agentInstanceContext, + bool hasOrderBy, + bool filterWithSameTypeSubselect, + Attribute[] annotations, + bool stateless, + int streamNum, + bool isCanIterateUnbound); + + /// + /// Drop the event stream associated with the filter passed in. Throws an exception if already dropped. + /// + /// is the event filter definition associated with the event stream to be dropped + /// to be used to deactivate filter when the last event stream is dropped + /// is indicatng whether the stream will participate in a join statement, informationnecessary for stream reuse and multithreading concerns + /// if the consumer has an order-by clause + /// if set to true [filter with same type subselect]. + /// if set to true [stateless]. + void DropStream(FilterSpecCompiled filterSpec, FilterService filterService, bool isJoin, bool hasOrderBy, bool filterWithSameTypeSubselect, bool stateless); + + /// Dispose the service. + void Destroy(); + } +} diff --git a/NEsper.Core/NEsper.Core/view/stream/StreamFactoryServiceProvider.cs b/NEsper.Core/NEsper.Core/view/stream/StreamFactoryServiceProvider.cs new file mode 100755 index 000000000..232ef0f08 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/stream/StreamFactoryServiceProvider.cs @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.view.stream +{ + /// + /// Static factory for implementations of the StreamFactoryService interface. + /// + public sealed class StreamFactoryServiceProvider + { + /// + /// Creates an implementation of the StreamFactoryService interface. + /// + /// The engine URI. + /// indicator on whether stream and view resources are to be reused between statements + /// implementation + public static StreamFactoryService NewService(String engineURI, bool isReuseViews) + { + return new StreamFactorySvcImpl(engineURI, isReuseViews); + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/view/stream/StreamFactorySvcImpl.cs b/NEsper.Core/NEsper.Core/view/stream/StreamFactorySvcImpl.cs new file mode 100755 index 000000000..81b961dcf --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/stream/StreamFactorySvcImpl.cs @@ -0,0 +1,278 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Reflection; + +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.compat.threading; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.filter; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.view.stream +{ + /// + /// Service implementation to reuse or not reuse event streams and existing filters + /// depending on the type of statement. + /// + /// For non-join statements, the class manages the reuse of event streams when filters + /// match, and thus when an event stream is reused such can be the views under the stream. + /// For joins however, this can lead to problems in multithread-safety since the statement + /// resource lock would then have to be multiple locks, i.e. the reused statement's resource + /// lock and the join statement's own lock, at a minimum. For join statements, always + /// creating a new event stream and therefore not reusing view resources, for use with joins. + /// + /// This can be very effective in that if a client applications creates a large number of very + /// similar statements in terms of filters and views used then these resources are all re-used + /// across statements. + /// T + /// The re-use is multithread-safe in that + /// (A) statement start/stop is locked against other engine processing + /// (B) the first statement supplies the lock for shared filters and views, protecting multiple + /// threads from entering into the same view. + /// (C) joins statements do not participate in filter and view reuse + /// + public class StreamFactorySvcImpl : StreamFactoryService + { + private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + // Using identify hash map - ignoring the equals semantics on filter specs + // Thus two filter specs objects are always separate entries in the map + private readonly IdentityDictionary _eventStreamsIdentity; + + // Using a reference-counted map for non-join statements + private readonly RefCountedMap _eventStreamsRefCounted; + + private readonly String _engineURI; + private readonly bool _isReuseViews; + + /// + /// Ctor. + /// + /// The engine URI. + /// indicator on whether stream and view resources are to be reused between statements + public StreamFactorySvcImpl(String engineURI, bool isReuseViews) + { + _engineURI = engineURI; + _eventStreamsRefCounted = new RefCountedMap(); + _eventStreamsIdentity = new IdentityDictionary(); + _isReuseViews = isReuseViews; + } + + public void Destroy() + { + _eventStreamsRefCounted.Clear(); + _eventStreamsIdentity.Clear(); + } + + /// + /// See the method of the same name in . + /// Always attempts to reuse an existing event stream. May thus return a new event stream or an existing event + /// stream depending on whether filter criteria match. + /// + /// the statement id + /// is the filter definition + /// filter service to activate filter if not already active + /// is the statement resource lock + /// is indicatng whether the stream will participate in a join statement, information necessary for stream reuse and multithreading concerns + /// + /// if the consumer has order-by + /// if set to true [filter with same type subselect]. + /// The annotations. + /// if set to true [stateless]. + /// The stream num. + /// if set to true [is can iterate unbound]. + /// + /// newly createdStatement event stream, not reusing existing instances + /// + /// Filter spec object already found in collection + public Pair CreateStream( + int statementId, + FilterSpecCompiled filterSpec, + FilterService filterService, + EPStatementAgentInstanceHandle epStatementAgentInstanceHandle, + bool isJoin, + AgentInstanceContext agentInstanceContext, + bool hasOrderBy, + bool filterWithSameTypeSubselect, + Attribute[] annotations, + bool stateless, + int streamNum, + bool isCanIterateUnbound) + { + EventStream eventStream; + + if (Log.IsDebugEnabled) + { + Log.Debug(".createStream hashCode=" + filterSpec.GetHashCode() + " filter=" + filterSpec); + } + + // Check if a stream for this filter already exists + StreamEntry entry; + var forceNewStream = isJoin || (!_isReuseViews) || hasOrderBy || filterWithSameTypeSubselect || stateless; + if (forceNewStream) + { + entry = _eventStreamsIdentity.Get(filterSpec); + } + else + { + entry = _eventStreamsRefCounted[filterSpec]; + } + + // If pair exists, either reference count or illegal state + if (entry != null) + { + if (forceNewStream) + { + throw new IllegalStateException("Filter spec object already found in collection"); + } + else + { + Log.Debug(".createStream filter already found"); + _eventStreamsRefCounted.Reference(filterSpec); + + // audit proxy + eventStream = EventStreamProxy.GetAuditProxy( + _engineURI, epStatementAgentInstanceHandle.StatementHandle.StatementName, annotations, + filterSpec, entry.EventStream); + + // We return the lock of the statement first establishing the stream to use that as the new statement's lock + return new Pair( + eventStream, entry.Callback.AgentInstanceHandle.StatementAgentInstanceLock); + } + } + + // New event stream + var resultEventType = filterSpec.ResultEventType; + var zeroDepthStream = isCanIterateUnbound + ? (EventStream)new ZeroDepthStreamIterable(resultEventType) + : (EventStream)new ZeroDepthStreamNoIterate(resultEventType); + + // audit proxy + var inputStream = EventStreamProxy.GetAuditProxy( + _engineURI, epStatementAgentInstanceHandle.StatementHandle.StatementName, annotations, filterSpec, + zeroDepthStream); + + eventStream = inputStream; + FilterHandleCallback filterCallback; + if (filterSpec.OptionalPropertyEvaluator != null) + { + filterCallback = new ProxyFilterHandleCallback() + { + ProcStatementId = () => statementId, + ProcMatchFound = (theEvent, allStmtMatches) => + { + var result = filterSpec.OptionalPropertyEvaluator.GetProperty(theEvent, agentInstanceContext); + if (result != null) + { + eventStream.Insert(result); + } + }, + ProcIsSubselect = () => false + }; + } + else + { + filterCallback = new ProxyFilterHandleCallback() + { + ProcStatementId = () => statementId, + ProcMatchFound = (theEvent, allStmtMatches) => + { + if (InstrumentationHelper.ENABLED) InstrumentationHelper.Get().QFilterActivationStream(theEvent.EventType.Name, streamNum); + eventStream.Insert(theEvent); + if (InstrumentationHelper.ENABLED) InstrumentationHelper.Get().AFilterActivationStream(); + }, + ProcIsSubselect = () => false + }; + } + + var handle = new EPStatementHandleCallback(epStatementAgentInstanceHandle, filterCallback); + + // Store stream for reuse + entry = new StreamEntry(eventStream, handle); + if (forceNewStream) + { + _eventStreamsIdentity.Put(filterSpec, entry); + } + else + { + _eventStreamsRefCounted[filterSpec] = entry; + } + + // Activate filter + var filterValues = filterSpec.GetValueSet(null, agentInstanceContext, null); + var filterServiceEntry = filterService.Add(filterValues, handle); + entry.FilterServiceEntry = filterServiceEntry; + + return new Pair(inputStream, null); + } + + /// + /// See the method of the same name in . + /// + /// is the filter definition + /// to be used to deactivate filter when the last event stream is dropped + /// is indicatng whether the stream will participate in a join statement, informationnecessary for stream reuse and multithreading concerns + /// if the consumer has an order-by clause + /// + /// + public void DropStream( + FilterSpecCompiled filterSpec, + FilterService filterService, + bool isJoin, + bool hasOrderBy, + bool filterWithSameTypeSubselect, + bool stateless) + { + StreamEntry entry; + var forceNewStream = isJoin || (!_isReuseViews) || hasOrderBy || filterWithSameTypeSubselect || stateless; + + if (forceNewStream) + { + entry = _eventStreamsIdentity.Get(filterSpec); + if (entry == null) + { + throw new IllegalStateException("Filter spec object not in collection"); + } + _eventStreamsIdentity.Remove(filterSpec); + filterService.Remove(entry.Callback, entry.FilterServiceEntry); + } + else + { + entry = _eventStreamsRefCounted[filterSpec]; + var isLast = _eventStreamsRefCounted.Dereference(filterSpec); + if (isLast) + { + filterService.Remove(entry.Callback, entry.FilterServiceEntry); + } + } + } + + public sealed class StreamEntry + { + public StreamEntry(EventStream eventStream, EPStatementHandleCallback callback) + { + EventStream = eventStream; + Callback = callback; + } + + public EventStream EventStream { get; private set; } + + public EPStatementHandleCallback Callback { get; private set; } + + public FilterServiceEntry FilterServiceEntry { get; set; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/window/ExpressionBatchView.cs b/NEsper.Core/NEsper.Core/view/window/ExpressionBatchView.cs new file mode 100755 index 000000000..85b0aa338 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/window/ExpressionBatchView.cs @@ -0,0 +1,271 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.events.arr; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.view.window +{ + /// + /// This view is a moving window extending the into the past until the expression passed to it returns false. + /// + public class ExpressionBatchView : ExpressionViewBase + { + private readonly ExpressionBatchViewFactory _dataWindowViewFactory; + + protected readonly ICollection Window = new LinkedHashSet(); + + protected EventBean[] LastBatch; + protected long NewestEventTimestamp; + protected long OldestEventTimestamp; + protected EventBean OldestEvent; + protected EventBean NewestEvent; + + /// + /// Constructor creates a moving window extending the specified number of elements into the past. + /// + /// for copying this view in a group-by + /// is a collection that the view must Update when receiving events + /// The expiry expression. + /// The aggregation service factory desc. + /// The builtin event props. + /// variable names + /// The agent instance context. + public ExpressionBatchView(ExpressionBatchViewFactory dataWindowViewFactory, + ViewUpdatedCollection viewUpdatedCollection, + ExprEvaluator expiryExpression, + AggregationServiceFactoryDesc aggregationServiceFactoryDesc, + ObjectArrayEventBean builtinEventProps, + ISet variableNames, + AgentInstanceViewFactoryChainContext agentInstanceContext) + : base(viewUpdatedCollection, expiryExpression, aggregationServiceFactoryDesc, builtinEventProps, variableNames, agentInstanceContext) + { + this._dataWindowViewFactory = dataWindowViewFactory; + } + + public override string ViewName + { + get { return _dataWindowViewFactory.ViewName; } + } + + public override View CloneView() + { + return _dataWindowViewFactory.MakeView(AgentInstanceContext); + } + + /// Returns true if the window is empty, or false if not empty. + /// true if empty + public bool IsEmpty() + { + return Window.IsEmpty(); + } + + public override void ScheduleCallback() + { + bool fireBatch = EvaluateExpression(null, Window.Count); + if (fireBatch) + { + Expire(Window.Count); + } + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QViewProcessIRStream(this, _dataWindowViewFactory.ViewName, newData, oldData); } + + bool fireBatch = false; + + // remove points from data window + if (oldData != null) + { + foreach (EventBean anOldData in oldData) + { + Window.Remove(anOldData); + } + if (AggregationService != null) + { + AggregationService.ApplyLeave(oldData, null, AgentInstanceContext); + } + + if (Window.IsNotEmpty()) + { + OldestEvent = Window.First(); + } + else + { + OldestEvent = null; + } + + fireBatch = EvaluateExpression(null, Window.Count); + } + + // add data points to the window + int numEventsInBatch = -1; + if (newData != null && newData.Length > 0) + { + if (Window.IsEmpty()) + { + OldestEventTimestamp = AgentInstanceContext.StatementContext.SchedulingService.Time; + } + NewestEventTimestamp = AgentInstanceContext.StatementContext.SchedulingService.Time; + if (OldestEvent == null) + { + OldestEvent = newData[0]; + } + + foreach (EventBean newEvent in newData) + { + Window.Add(newEvent); + if (AggregationService != null) + { + AggregationService.ApplyEnter(new EventBean[] { newEvent }, null, AgentInstanceContext); + } + NewestEvent = newEvent; + if (!fireBatch) + { + fireBatch = EvaluateExpression(newEvent, Window.Count); + if (fireBatch && !_dataWindowViewFactory.IsIncludeTriggeringEvent) + { + numEventsInBatch = Window.Count - 1; + } + } + } + } + + // may fire the batch + if (fireBatch) + { + Expire(numEventsInBatch); + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AViewProcessIRStream(); } + } + + // Called based on schedule evaluation registered when a variable changes (new data is null). + // Called when new data arrives. + public void Expire(int numEventsInBatch) + { + + if (numEventsInBatch == Window.Count || numEventsInBatch == -1) + { + var batchNewData = Window.ToArray(); + if (ViewUpdatedCollection != null) + { + ViewUpdatedCollection.Update(batchNewData, LastBatch); + } + + // post + if (batchNewData != null || LastBatch != null) + { + Instrument.With( + i => i.QViewIndicate(this, _dataWindowViewFactory.ViewName, batchNewData, LastBatch), + i => i.AViewIndicate(), + () => UpdateChildren(batchNewData, LastBatch)); + } + + // clear + Window.Clear(); + LastBatch = batchNewData; + if (AggregationService != null) + { + AggregationService.ClearResults(AgentInstanceContext); + } + OldestEvent = null; + NewestEvent = null; + } + else + { + var batchNewData = Window.Take(numEventsInBatch).ToArray(); + unchecked + { + for (int ii = 0; ii < batchNewData.Length; ii++) + { + Window.Remove(batchNewData[ii]); + } + } + + if (ViewUpdatedCollection != null) + { + ViewUpdatedCollection.Update(batchNewData, LastBatch); + } + + // post + if (batchNewData != null || LastBatch != null) + { + Instrument.With( + i => i.QViewIndicate(this, _dataWindowViewFactory.ViewName, batchNewData, LastBatch), + i => i.AViewIndicate(), + () => UpdateChildren(batchNewData, LastBatch)); + } + + // clear + LastBatch = batchNewData; + if (AggregationService != null) + { + AggregationService.ApplyLeave(batchNewData, null, AgentInstanceContext); + } + OldestEvent = Window.FirstOrDefault(); + } + } + + public override void VisitView(ViewDataVisitor viewDataVisitor) + { + viewDataVisitor.VisitPrimary(Window, true, _dataWindowViewFactory.ViewName, null); + viewDataVisitor.VisitPrimary(LastBatch, _dataWindowViewFactory.ViewName); + } + + private bool EvaluateExpression(EventBean arriving, int windowSize) + { + ExpressionViewOAFieldEnumExtensions.Populate(BuiltinEventProps.Properties, windowSize, OldestEventTimestamp, NewestEventTimestamp, this, 0, OldestEvent, NewestEvent); + EventsPerStream[0] = arriving; + + foreach (AggregationServiceAggExpressionDesc aggregateNode in AggregateNodes) + { + aggregateNode.AssignFuture(AggregationService); + } + + var result = ExpiryExpression.Evaluate(new EvaluateParams(EventsPerStream, true, AgentInstanceContext)); + if (result == null) + { + return false; + } + + return result.AsBoolean(); + } + + public override IEnumerator GetEnumerator() + { + return Window.GetEnumerator(); + } + + // Handle variable updates by scheduling a re-evaluation with timers + public override void Update(Object newValue, Object oldValue) + { + if (!AgentInstanceContext.StatementContext.SchedulingService.IsScheduled(ScheduleHandle)) + { + AgentInstanceContext.StatementContext.SchedulingService.Add(0, ScheduleHandle, ScheduleSlot); + } + } + + public override ViewFactory ViewFactory + { + get { return _dataWindowViewFactory; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/window/ExpressionBatchViewFactory.cs b/NEsper.Core/NEsper.Core/view/window/ExpressionBatchViewFactory.cs new file mode 100755 index 000000000..65d227f96 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/window/ExpressionBatchViewFactory.cs @@ -0,0 +1,67 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.view.window +{ + /// + /// Factory for . + /// + public class ExpressionBatchViewFactory : ExpressionViewFactoryBase, DataWindowBatchingViewFactory + { + private bool _includeTriggeringEvent = true; + + public override void SetViewParameters(ViewFactoryContext viewFactoryContext, IList expressionParameters) + { + if (expressionParameters.Count != 1 && expressionParameters.Count != 2) + { + var errorMessage = ViewName + " view requires a single expression as a parameter, or an expression and bool flag"; + throw new ViewParameterException(errorMessage); + } + ExpiryExpression = expressionParameters[0]; + + if (expressionParameters.Count > 1) + { + var result = ViewFactorySupport.EvaluateAssertNoProperties(ViewName, expressionParameters[1], 1, new ExprEvaluatorContextStatement(viewFactoryContext.StatementContext, false)); + _includeTriggeringEvent = result.AsBoolean(); + } + } + + public override View MakeView(AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext) + { + var builtinBean = new ObjectArrayEventBean(ExpressionViewOAFieldEnumExtensions.GetPrototypeOA(), BuiltinMapType); + var viewUpdatedCollection = agentInstanceViewFactoryContext.StatementContext.ViewServicePreviousFactory.GetOptPreviousExprRelativeAccess(agentInstanceViewFactoryContext); + return new ExpressionBatchView(this, viewUpdatedCollection, ExpiryExpressionEvaluator, AggregationServiceFactoryDesc, builtinBean, VariableNames, agentInstanceViewFactoryContext); + } + + public override Object MakePreviousGetter() + { + return new RelativeAccessByEventNIndexGetterImpl(); + } + + public bool IsIncludeTriggeringEvent + { + get { return _includeTriggeringEvent; } + } + + public override string ViewName + { + get { return "Expression-batch"; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/window/ExpressionViewBase.cs b/NEsper.Core/NEsper.Core/view/window/ExpressionViewBase.cs new file mode 100755 index 000000000..41636c561 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/window/ExpressionViewBase.cs @@ -0,0 +1,156 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events.arr; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.schedule; +using com.espertech.esper.util; + +namespace com.espertech.esper.view.window +{ + /// + /// This view is a moving window extending the into the past until the expression passed to it returns false. + /// + public abstract class ExpressionViewBase + : ViewSupport + , DataWindowView + , CloneableView + , StoppableView + , StopCallback + { + protected readonly ExprEvaluator ExpiryExpression; + protected readonly ObjectArrayEventBean BuiltinEventProps; + protected readonly EventBean[] EventsPerStream; + protected readonly ISet VariableNames; + protected readonly AgentInstanceViewFactoryChainContext AgentInstanceContext; + protected readonly long ScheduleSlot; + protected readonly EPStatementHandleCallback ScheduleHandle; + protected readonly IList AggregateNodes; + + /// Implemented to check the expiry expression. + public abstract void ScheduleCallback(); + + public abstract View CloneView(); + public abstract void VisitView(ViewDataVisitor viewDataVisitor); + public abstract string ViewName { get; } + public abstract ViewFactory ViewFactory { get; } + + protected ExpressionViewBase( + ViewUpdatedCollection viewUpdatedCollection, + ExprEvaluator expiryExpression, + AggregationServiceFactoryDesc aggregationServiceFactoryDesc, + ObjectArrayEventBean builtinEventProps, + ISet variableNames, + AgentInstanceViewFactoryChainContext agentInstanceContext) + { + ViewUpdatedCollection = viewUpdatedCollection; + ExpiryExpression = expiryExpression; + BuiltinEventProps = builtinEventProps; + EventsPerStream = new EventBean[] {null, builtinEventProps}; + VariableNames = variableNames; + AgentInstanceContext = agentInstanceContext; + + if (variableNames != null && !variableNames.IsEmpty()) { + foreach (String variable in variableNames) { + var variableName = variable; + var agentInstanceId = agentInstanceContext.AgentInstanceId; + var variableService = agentInstanceContext.StatementContext.VariableService; + + agentInstanceContext.StatementContext.VariableService.RegisterCallback(variable, agentInstanceId, Update); + agentInstanceContext.AddTerminationCallback( + new ProxyStopCallback(() => variableService.UnregisterCallback(variableName, agentInstanceId, Update))); + } + + ScheduleHandleCallback callback = new ProxyScheduleHandleCallback + { + ProcScheduledTrigger = extensionServicesContext => Instrument.With( + i => i.QViewScheduledEval(this, ViewName), + i => i.AViewScheduledEval(), + ScheduleCallback) + }; + ScheduleSlot = agentInstanceContext.StatementContext.ScheduleBucket.AllocateSlot(); + ScheduleHandle = new EPStatementHandleCallback(agentInstanceContext.EpStatementAgentInstanceHandle, callback); + agentInstanceContext.AddTerminationCallback(this); + } + else { + ScheduleSlot = -1; + ScheduleHandle = null; + } + + if (aggregationServiceFactoryDesc != null) + { + AggregationService = aggregationServiceFactoryDesc.AggregationServiceFactory.MakeService( + agentInstanceContext.AgentInstanceContext, + agentInstanceContext.AgentInstanceContext.StatementContext.EngineImportService, false, null); + AggregateNodes = aggregationServiceFactoryDesc.Expressions; + } + else { + AggregationService = null; + AggregateNodes = Collections.GetEmptyList(); + } + } + + public override EventType EventType + { + get + { + // The event type is the parent view's event type + return Parent.EventType; + } + } + + public override String ToString() + { + return GetType().FullName; + } + + public void StopView() { + StopScheduleAndVar(); + AgentInstanceContext.RemoveTerminationCallback(this); + } + + public void Stop() { + StopScheduleAndVar(); + } + + public void StopScheduleAndVar() { + if (VariableNames != null && !VariableNames.IsEmpty()) { + foreach (String variable in VariableNames) { + AgentInstanceContext.StatementContext.VariableService.UnregisterCallback( + variable, AgentInstanceContext.AgentInstanceId, Update); + } + + if (AgentInstanceContext.StatementContext.SchedulingService.IsScheduled(ScheduleHandle)) { + AgentInstanceContext.StatementContext.SchedulingService.Remove(ScheduleHandle, ScheduleSlot); + } + } + } + + // Handle variable updates by scheduling a re-evaluation with timers + public virtual void Update(Object newValue, Object oldValue) { + if (!AgentInstanceContext.StatementContext.SchedulingService.IsScheduled(ScheduleHandle)) { + AgentInstanceContext.StatementContext.SchedulingService.Add(0, ScheduleHandle, ScheduleSlot); + } + } + + public virtual ViewUpdatedCollection ViewUpdatedCollection { get; private set; } + + public virtual AggregationService AggregationService { get; private set; } + } +} diff --git a/NEsper.Core/NEsper.Core/view/window/ExpressionViewFactoryBase.cs b/NEsper.Core/NEsper.Core/view/window/ExpressionViewFactoryBase.cs new file mode 100755 index 000000000..7717b432f --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/window/ExpressionViewFactoryBase.cs @@ -0,0 +1,148 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.core; +using com.espertech.esper.epl.declexpr; +using com.espertech.esper.epl.expression.baseagg; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.visitor; +using com.espertech.esper.util; + +namespace com.espertech.esper.view.window +{ + /// + /// Base factory for expression-based window and batch view. + /// + public abstract class ExpressionViewFactoryBase : DataWindowViewFactory, DataWindowViewWithPrevious + { + private EventType _eventType; + private ExprNode _expiryExpression; + private ISet _variableNames; + private AggregationServiceFactoryDesc _aggregationServiceFactoryDesc; + private ExprEvaluator _expiryExpressionEvaluator; + private EventType _builtinMapType; + + public void Attach(EventType parentEventType, StatementContext statementContext, ViewFactory optionalParentFactory, IList parentViewFactories) + { + _eventType = parentEventType; + + // define built-in fields + var builtinTypeDef = ExpressionViewOAFieldEnumExtensions.AsMapOfTypes(_eventType); + _builtinMapType = statementContext.EventAdapterService.CreateAnonymousObjectArrayType( + statementContext.StatementId + "_exprview", builtinTypeDef); + + StreamTypeService streamTypeService = new StreamTypeServiceImpl(new EventType[] { _eventType, _builtinMapType }, new String[2], new bool[2], statementContext.EngineURI, false); + + // validate expression + _expiryExpression = ViewFactorySupport.ValidateExpr(ViewName, statementContext, _expiryExpression, streamTypeService, 0); + _expiryExpressionEvaluator = _expiryExpression.ExprEvaluator; + + var summaryVisitor = new ExprNodeSummaryVisitor(); + _expiryExpression.Accept(summaryVisitor); + if (summaryVisitor.HasSubselect || summaryVisitor.HasStreamSelect || summaryVisitor.HasPreviousPrior) + { + throw new ViewParameterException("Invalid expiry expression: Sub-select, previous or prior functions are not supported in this context"); + } + + var returnType = _expiryExpressionEvaluator.ReturnType; + if (returnType.GetBoxedType() != typeof(bool?)) + { + throw new ViewParameterException("Invalid return value for expiry expression, expected a bool return value but received " + returnType.GetParameterAsString()); + } + + // determine variables used, if any + var visitor = new ExprNodeVariableVisitor(statementContext.VariableService); + _expiryExpression.Accept(visitor); + _variableNames = visitor.VariableNames; + + // determine aggregation nodes, if any + var aggregateNodes = new List(); + ExprAggregateNodeUtil.GetAggregatesBottomUp(_expiryExpression, aggregateNodes); + if (aggregateNodes.IsNotEmpty()) + { + try + { + _aggregationServiceFactoryDesc = AggregationServiceFactoryFactory.GetService( + Collections.GetEmptyList(), + Collections.GetEmptyMap(), + Collections.GetEmptyList(), + null, aggregateNodes, + Collections.GetEmptyList(), + Collections.GetEmptyList(), false, + statementContext.Annotations, + statementContext.VariableService, false, false, null, null, + statementContext.AggregationServiceFactoryService, + streamTypeService.EventTypes, null, + statementContext.ContextName, + null, null, false, false, false, + statementContext.EngineImportService); + } + catch (ExprValidationException ex) + { + throw new ViewParameterException(ex.Message, ex); + } + } + } + + public EventType EventType + { + get { return _eventType; } + } + + public bool CanReuse(View view, AgentInstanceContext agentInstanceContext) + { + return false; + } + + public EventType BuiltinMapType + { + get { return _builtinMapType; } + } + + public ExprNode ExpiryExpression + { + get { return _expiryExpression; } + set + { + _expiryExpression = value; + _expiryExpressionEvaluator = value != null + ? value.ExprEvaluator + : null; + } + } + + public ISet VariableNames + { + get { return _variableNames; } + } + + public AggregationServiceFactoryDesc AggregationServiceFactoryDesc + { + get { return _aggregationServiceFactoryDesc; } + } + + public ExprEvaluator ExpiryExpressionEvaluator + { + get { return _expiryExpressionEvaluator; } + } + + public abstract void SetViewParameters(ViewFactoryContext viewFactoryContext, IList viewParameters); + public abstract View MakeView(AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext); + public abstract string ViewName { get; } + public abstract object MakePreviousGetter(); + } +} diff --git a/NEsper.Core/NEsper.Core/view/window/ExpressionViewOAFieldEnum.cs b/NEsper.Core/NEsper.Core/view/window/ExpressionViewOAFieldEnum.cs new file mode 100755 index 000000000..a8cd68078 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/window/ExpressionViewOAFieldEnum.cs @@ -0,0 +1,90 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.view.window +{ + public enum ExpressionViewOAFieldEnum + { + CURRENT_COUNT, + OLDEST_TIMESTAMP, + NEWEST_TIMESTAMP, + EXPIRED_COUNT, + VIEW_REFERENCE, + NEWEST_EVENT, + OLDEST_EVENT + } + + public static class ExpressionViewOAFieldEnumExtensions + { + public static string GetFieldName(this ExpressionViewOAFieldEnum value) + { + switch (value) + { + case ExpressionViewOAFieldEnum.CURRENT_COUNT: + return ("current_count"); + case ExpressionViewOAFieldEnum.OLDEST_TIMESTAMP: + return ("oldest_timestamp"); + case ExpressionViewOAFieldEnum.NEWEST_TIMESTAMP: + return ("newest_timestamp"); + case ExpressionViewOAFieldEnum.EXPIRED_COUNT: + return ("expired_count"); + case ExpressionViewOAFieldEnum.VIEW_REFERENCE: + return ("view_reference"); + case ExpressionViewOAFieldEnum.NEWEST_EVENT: + return ("newest_event"); + case ExpressionViewOAFieldEnum.OLDEST_EVENT: + return ("oldest_event"); + } + + throw new ArgumentException(); + } + + public static IDictionary AsMapOfTypes(EventType eventType) + { + var builtinTypeDef = new Dictionary(); + builtinTypeDef.Put(ExpressionViewOAFieldEnum.CURRENT_COUNT.GetFieldName(), typeof(int)); + builtinTypeDef.Put(ExpressionViewOAFieldEnum.OLDEST_TIMESTAMP.GetFieldName(), typeof(long)); + builtinTypeDef.Put(ExpressionViewOAFieldEnum.NEWEST_TIMESTAMP.GetFieldName(), typeof(long)); + builtinTypeDef.Put(ExpressionViewOAFieldEnum.EXPIRED_COUNT.GetFieldName(), typeof(int)); + builtinTypeDef.Put(ExpressionViewOAFieldEnum.VIEW_REFERENCE.GetFieldName(), typeof(Object)); + builtinTypeDef.Put(ExpressionViewOAFieldEnum.NEWEST_EVENT.GetFieldName(), eventType); + builtinTypeDef.Put(ExpressionViewOAFieldEnum.OLDEST_EVENT.GetFieldName(), eventType); + return builtinTypeDef; + } + + public static void Populate(Object[] properties, + int windowSize, + long oldestEventTimestamp, + long newestEventTimestamp, + Object viewReference, + int expiredCount, + EventBean oldestEvent, + EventBean newestEvent) + { + properties[(int) ExpressionViewOAFieldEnum.CURRENT_COUNT] = windowSize; + properties[(int) ExpressionViewOAFieldEnum.OLDEST_TIMESTAMP] = oldestEventTimestamp; + properties[(int) ExpressionViewOAFieldEnum.NEWEST_TIMESTAMP] = newestEventTimestamp; + properties[(int) ExpressionViewOAFieldEnum.VIEW_REFERENCE] = viewReference; + properties[(int) ExpressionViewOAFieldEnum.EXPIRED_COUNT] = expiredCount; + properties[(int) ExpressionViewOAFieldEnum.OLDEST_EVENT] = oldestEvent; + properties[(int) ExpressionViewOAFieldEnum.NEWEST_EVENT] = newestEvent; + } + + public static Object[] GetPrototypeOA() + { + return new Object[EnumHelper.CountValues()]; + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/window/ExpressionWindowTimestampEventPair.cs b/NEsper.Core/NEsper.Core/view/window/ExpressionWindowTimestampEventPair.cs new file mode 100755 index 000000000..69db17518 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/window/ExpressionWindowTimestampEventPair.cs @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; + +namespace com.espertech.esper.view.window +{ + public class ExpressionWindowTimestampEventPair + { + public ExpressionWindowTimestampEventPair(long timestamp, EventBean theEvent) + { + Timestamp = timestamp; + TheEvent = theEvent; + } + + public long Timestamp { get; private set; } + + public EventBean TheEvent { get; private set; } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/view/window/ExpressionWindowTimestampEventPairEnumerator.cs b/NEsper.Core/NEsper.Core/view/window/ExpressionWindowTimestampEventPairEnumerator.cs new file mode 100755 index 000000000..d0fdd1504 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/window/ExpressionWindowTimestampEventPairEnumerator.cs @@ -0,0 +1,55 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections; +using System.Collections.Generic; + +using com.espertech.esper.client; + +namespace com.espertech.esper.view.window +{ + public class ExpressionWindowTimestampEventPairEnumerator + : IEnumerator + { + private readonly IEnumerator _events; + + public ExpressionWindowTimestampEventPairEnumerator(IEnumerator events) + { + _events = events; + } + + #region IEnumerator Members + + public bool MoveNext() + { + return _events.MoveNext(); + } + + public void Dispose() + { + _events.Dispose(); + } + + public void Reset() + { + _events.Reset(); + } + + object IEnumerator.Current + { + get { return Current; } + } + + public EventBean Current + { + get { return _events.Current.TheEvent; } + } + + #endregion + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/view/window/ExpressionWindowView.cs b/NEsper.Core/NEsper.Core/view/window/ExpressionWindowView.cs new file mode 100755 index 000000000..35f9a5c6c --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/window/ExpressionWindowView.cs @@ -0,0 +1,258 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.agg.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events.arr; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.view.window +{ + /// + /// This view is a moving window extending the into the past until the expression passed to it returns false. + /// + public class ExpressionWindowView : ExpressionViewBase + { + private readonly ExpressionWindowViewFactory _dataWindowViewFactory; + private readonly ArrayDeque _window = + new ArrayDeque(); + private readonly EventBean[] _removedEvents = new EventBean[1]; + + /// + /// Constructor creates a moving window extending the specified number of elements into the past. + /// + /// for copying this view in a group-by + /// is a collection that the view must update when receiving events + /// The expiry expression. + /// The aggregation service factory desc. + /// The builtin event props. + /// variable names + /// The agent instance context. + public ExpressionWindowView(ExpressionWindowViewFactory dataWindowViewFactory, + ViewUpdatedCollection viewUpdatedCollection, + ExprEvaluator expiryExpression, + AggregationServiceFactoryDesc AggregationServiceFactoryDesc, + ObjectArrayEventBean builtinEventProps, + ISet variableNames, + AgentInstanceViewFactoryChainContext AgentInstanceContext) + : base(viewUpdatedCollection, expiryExpression, AggregationServiceFactoryDesc, builtinEventProps, variableNames, AgentInstanceContext) + { + _dataWindowViewFactory = dataWindowViewFactory; + } + + public override string ViewName + { + get { return _dataWindowViewFactory.ViewName; } + } + + public override View CloneView() + { + return _dataWindowViewFactory.MakeView(AgentInstanceContext); + } + + /// + /// Returns true if the window is empty, or false if not empty. + /// + /// true if empty + public bool IsEmpty() + { + return _window.IsEmpty(); + } + + public override void ScheduleCallback() + { + Expire(null, null); + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QViewProcessIRStream(this, _dataWindowViewFactory.ViewName, newData, oldData); } + + // add data points to the window + if (newData != null) + { + foreach (EventBean newEvent in newData) + { + var pair = new ExpressionWindowTimestampEventPair(AgentInstanceContext.TimeProvider.Time, newEvent); + _window.Add(pair); + InternalHandleAdd(pair); + } + + if (AggregationService != null) + { + AggregationService.ApplyEnter(newData, null, AgentInstanceContext); + } + } + + if (oldData != null) + { + _window.RemoveWhere( + pair => oldData.Any(anOldData => pair.TheEvent == anOldData), + pair => InternalHandleRemoved(pair)); + + if (AggregationService != null) + { + AggregationService.ApplyLeave(oldData, null, AgentInstanceContext); + } + } + + // expire events + Expire(newData, oldData); + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AViewProcessIRStream(); } + } + + public void InternalHandleRemoved(ExpressionWindowTimestampEventPair pair) + { + // no action required + } + + public void InternalHandleExpired(ExpressionWindowTimestampEventPair pair) + { + // no action required + } + + public void InternalHandleAdd(ExpressionWindowTimestampEventPair pair) + { + // no action required + } + + // 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); + } + int expiredCount = 0; + if (!_window.IsEmpty()) + { + ExpressionWindowTimestampEventPair newest = _window.Last; + + while (true) + { + ExpressionWindowTimestampEventPair first = _window.First; + + bool pass = CheckEvent(first, newest, expiredCount); + if (!pass) + { + if (expired == null) + { + expired = new OneEventCollection(); + } + EventBean 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()) + { + if (AggregationService != null) + { + 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 + if (ViewUpdatedCollection != null) + { + ViewUpdatedCollection.Update(newData, expiredArr); + } + + // If there are child views, call update method + if (HasViews) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QViewIndicate(this, _dataWindowViewFactory.ViewName, newData, expiredArr); } + UpdateChildren(newData, expiredArr); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AViewIndicate(); } + } + } + + private bool CheckEvent(ExpressionWindowTimestampEventPair first, ExpressionWindowTimestampEventPair newest, int numExpired) + { + ExpressionViewOAFieldEnumExtensions.Populate(BuiltinEventProps.Properties, _window.Count, first.Timestamp, newest.Timestamp, + this, numExpired, first.TheEvent, newest.TheEvent); + EventsPerStream[0] = first.TheEvent; + + foreach (AggregationServiceAggExpressionDesc aggregateNode in AggregateNodes) + { + aggregateNode.AssignFuture(AggregationService); + } + + var result = ExpiryExpression.Evaluate(new EvaluateParams(EventsPerStream, true, AgentInstanceContext)); + if (result == null) + { + return false; + } + return true.Equals(result); + } + + public override IEnumerator GetEnumerator() + { + return new ExpressionWindowTimestampEventPairEnumerator( + _window.GetEnumerator()); + } + + // Handle variable updates by scheduling a re-evaluation with timers + public override void Update(Object newValue, Object oldValue) + { + if (!AgentInstanceContext.StatementContext.SchedulingService.IsScheduled(ScheduleHandle)) + { + AgentInstanceContext.StatementContext.SchedulingService.Add(0, ScheduleHandle, ScheduleSlot); + } + } + + public ArrayDeque Window + { + get { return _window; } + } + + public override void VisitView(ViewDataVisitor viewDataVisitor) + { + viewDataVisitor.VisitPrimary(_window, true, _dataWindowViewFactory.ViewName, null); + } + + public override ViewFactory ViewFactory + { + get { return _dataWindowViewFactory; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/window/ExpressionWindowViewFactory.cs b/NEsper.Core/NEsper.Core/view/window/ExpressionWindowViewFactory.cs new file mode 100755 index 000000000..124f6e933 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/window/ExpressionWindowViewFactory.cs @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.events.arr; + +namespace com.espertech.esper.view.window +{ + /// + /// Factory for . + /// + public class ExpressionWindowViewFactory : ExpressionViewFactoryBase + { + public override void SetViewParameters(ViewFactoryContext viewFactoryContext, IList expressionParameters) + { + if (expressionParameters.Count != 1) { + string errorMessage = ViewName + " view requires a single expression as a parameter"; + throw new ViewParameterException(errorMessage); + } + ExpiryExpression = expressionParameters[0]; + } + + public override View MakeView(AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext) + { + var builtinBean = new ObjectArrayEventBean(ExpressionViewOAFieldEnumExtensions.GetPrototypeOA(), BuiltinMapType); + var randomAccess = agentInstanceViewFactoryContext.StatementContext.ViewServicePreviousFactory.GetOptPreviousExprRandomAccess(agentInstanceViewFactoryContext); + return new ExpressionWindowView(this, randomAccess, ExpiryExpressionEvaluator, AggregationServiceFactoryDesc, builtinBean, VariableNames, agentInstanceViewFactoryContext); + } + + public override Object MakePreviousGetter() + { + return new RandomAccessByIndexGetter(); + } + + public override string ViewName + { + get { return "Expression"; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/window/ExternallyTimedBatchView.cs b/NEsper.Core/NEsper.Core/view/window/ExternallyTimedBatchView.cs new file mode 100755 index 000000000..1fe543753 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/window/ExternallyTimedBatchView.cs @@ -0,0 +1,255 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.time; +using com.espertech.esper.events; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.view.window +{ + /// + /// Batch window based on timestamp of arriving events. + /// + public class ExternallyTimedBatchView + : ViewSupport + , DataWindowView + , CloneableView + { + private readonly ExternallyTimedBatchViewFactory _factory; + private readonly ExprNode _timestampExpression; + private readonly ExprEvaluator _timestampExpressionEval; + private readonly ExprTimePeriodEvalDeltaConst _timeDeltaComputation; + + private readonly EventBean[] _eventsPerStream = new EventBean[1]; + internal EventBean[] LastBatch; + + private long? _oldestTimestamp; + internal readonly ISet Window = new LinkedHashSet(); + internal long? ReferenceTimestamp; + + internal ViewUpdatedCollection ViewUpdatedCollection; + internal AgentInstanceViewFactoryChainContext AgentInstanceViewFactoryContext; + + /// + /// Constructor. + /// + /// for copying this view in a group-by + /// is the field name containing a long timestamp valuethat should be in ascending order for the natural order of events and is intended to reflect + /// System.currentTimeInMillis but does not necessarily have to. + /// out of the window as oldData in the update method. The view compares + /// each events timestamp against the newest event timestamp and those with a delta + /// greater then secondsBeforeExpiry are pushed out of the window. + /// The timestamp expression eval. + /// The time delta computation. + /// The optional reference point. + /// is a collection that the view must update when receiving events + /// context for expression evalauation + public ExternallyTimedBatchView(ExternallyTimedBatchViewFactory factory, + ExprNode timestampExpression, + ExprEvaluator timestampExpressionEval, + ExprTimePeriodEvalDeltaConst timeDeltaComputation, + long? optionalReferencePoint, + ViewUpdatedCollection viewUpdatedCollection, + AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext) + { + _factory = factory; + _timestampExpression = timestampExpression; + _timestampExpressionEval = timestampExpressionEval; + _timeDeltaComputation = timeDeltaComputation; + ViewUpdatedCollection = viewUpdatedCollection; + AgentInstanceViewFactoryContext = agentInstanceViewFactoryContext; + ReferenceTimestamp = optionalReferencePoint; + } + + public View CloneView() + { + return _factory.MakeView(AgentInstanceViewFactoryContext); + } + + /// + /// Returns the field name to get timestamp values from. + /// + /// field name for timestamp values + public ExprNode TimestampExpression + { + get { return _timestampExpression; } + } + + public override EventType EventType + { + get + { + // The schema is the parent view's schema + return Parent.EventType; + } + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QViewProcessIRStream(this, _factory.ViewName, newData, oldData); } + + // remove points from data window + if (oldData != null && oldData.Length != 0) + { + foreach (EventBean anOldData in oldData) + { + Window.Remove(anOldData); + HandleInternalRemovedEvent(anOldData); + } + DetermineOldestTimestamp(); + } + + // add data points to the window + EventBean[] batchNewData = null; + if (newData != null) + { + foreach (EventBean newEvent in newData) + { + + long timestamp = GetLongValue(newEvent); + if (ReferenceTimestamp == null) + { + ReferenceTimestamp = timestamp; + } + + if (_oldestTimestamp == null) + { + _oldestTimestamp = timestamp; + } + else + { + var delta = _timeDeltaComputation.DeltaAddWReference( + _oldestTimestamp.Value, ReferenceTimestamp.Value); + ReferenceTimestamp = delta.LastReference; + if (timestamp - _oldestTimestamp >= delta.Delta) + { + if (batchNewData == null) + { + batchNewData = Window.ToArray(); + } + else + { + batchNewData = EventBeanUtility.AddToArray(batchNewData, Window); + } + Window.Clear(); + _oldestTimestamp = null; + } + } + + Window.Add(newEvent); + HandleInternalAddEvent(newEvent, batchNewData != null); + } + } + + if (batchNewData != null) + { + HandleInternalPostBatch(Window, batchNewData); + if (ViewUpdatedCollection != null) + { + ViewUpdatedCollection.Update(batchNewData, LastBatch); + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QViewIndicate(this, _factory.ViewName, newData, LastBatch); } + UpdateChildren(batchNewData, LastBatch); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AViewIndicate(); } + LastBatch = batchNewData; + DetermineOldestTimestamp(); + } + if (oldData != null && oldData.Length > 0) + { + if (ViewUpdatedCollection != null) + { + ViewUpdatedCollection.Update(null, oldData); + } + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QViewIndicate(this, _factory.ViewName, null, oldData); } + UpdateChildren(null, oldData); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AViewIndicate(); } + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AViewProcessIRStream(); } + } + + public override IEnumerator GetEnumerator() + { + return Window.GetEnumerator(); + } + + public override String ToString() + { + return GetType().FullName + + " timestampExpression=" + _timestampExpression; + } + /// + /// Returns true to indicate the window is empty, or false if the view is not empty. + /// + /// true if empty + public bool IsEmpty() + { + return Window.IsEmpty(); + } + + public ExprTimePeriodEvalDeltaConst GetTimeDeltaComputation() + { + return _timeDeltaComputation; + } + + public void VisitView(ViewDataVisitor viewDataVisitor) + { + viewDataVisitor.VisitPrimary(Window, true, _factory.ViewName, null); + } + + public ViewFactory ViewFactory + { + get { return _factory; } + } + + protected void DetermineOldestTimestamp() + { + if (Window.IsEmpty()) + { + _oldestTimestamp = null; + } + else + { + _oldestTimestamp = GetLongValue(Window.First()); + } + } + + protected void HandleInternalPostBatch(ISet window, EventBean[] batchNewData) + { + // no action require + } + + protected void HandleInternalRemovedEvent(EventBean anOldData) + { + // no action require + } + + protected void HandleInternalAddEvent(EventBean anNewData, bool isNextBatch) + { + // no action require + } + + private long GetLongValue(EventBean obj) + { + _eventsPerStream[0] = obj; + var num = _timestampExpressionEval.Evaluate( + new EvaluateParams(_eventsPerStream, true, AgentInstanceViewFactoryContext)); + return num.AsLong(); + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/window/ExternallyTimedBatchViewFactory.cs b/NEsper.Core/NEsper.Core/view/window/ExternallyTimedBatchViewFactory.cs new file mode 100755 index 000000000..2c9354613 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/window/ExternallyTimedBatchViewFactory.cs @@ -0,0 +1,131 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.time; +using com.espertech.esper.util; + +namespace com.espertech.esper.view.window +{ + /// + /// Factory for . + /// + public class ExternallyTimedBatchViewFactory + : DataWindowBatchingViewFactory + , DataWindowViewFactory + , DataWindowViewWithPrevious + { + /// The timestamp property name. + private ExprNode _timestampExpression; + private ExprEvaluator _timestampExpressionEval; + private long? _optionalReferencePoint; + /// The number of msec to expire. + private ExprTimePeriodEvalDeltaConstFactory _timeDeltaComputationFactory; + private IList _viewParameters; + private EventType _eventType; + + public void SetViewParameters(ViewFactoryContext viewFactoryContext, IList expressionParameters) + { + _viewParameters = expressionParameters; + } + + public void Attach(EventType parentEventType, StatementContext statementContext, ViewFactory optionalParentFactory, IList parentViewFactories) + { + var windowName = ViewName; + var validated = ViewFactorySupport.Validate(windowName, parentEventType, statementContext, _viewParameters, true); + if (_viewParameters.Count < 2 || _viewParameters.Count > 3) { + throw new ViewParameterException(ViewParamMessage); + } + + // validate first parameter: timestamp expression + if (!validated[0].ExprEvaluator.ReturnType.IsNumeric()) { + throw new ViewParameterException(ViewParamMessage); + } + _timestampExpression = validated[0]; + _timestampExpressionEval = _timestampExpression.ExprEvaluator; + ViewFactorySupport.AssertReturnsNonConstant(windowName, validated[0], 0); + + _timeDeltaComputationFactory = ViewFactoryTimePeriodHelper.ValidateAndEvaluateTimeDeltaFactory(ViewName, statementContext, _viewParameters[1], ViewParamMessage, 1); + + // validate optional parameters + if (validated.Length == 3) { + var constant = ViewFactorySupport.ValidateAndEvaluate(windowName, statementContext, validated[2]); + if ((!constant.IsNumber()) || constant.IsFloatingPointNumber()) { + throw new ViewParameterException("Externally-timed batch view requires a long-typed reference point in msec as a third parameter"); + } + _optionalReferencePoint = constant.AsLong(); + } + + this._eventType = parentEventType; + } + + public Object MakePreviousGetter() + { + return new RelativeAccessByEventNIndexGetterImpl(); + } + + public View MakeView(AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext) { + var timeDeltaComputation = _timeDeltaComputationFactory.Make(ViewName, "view", agentInstanceViewFactoryContext.AgentInstanceContext); + var viewUpdatedCollection = agentInstanceViewFactoryContext.StatementContext.ViewServicePreviousFactory.GetOptPreviousExprRelativeAccess(agentInstanceViewFactoryContext); + return new ExternallyTimedBatchView(this, _timestampExpression, _timestampExpressionEval, timeDeltaComputation, _optionalReferencePoint, viewUpdatedCollection, agentInstanceViewFactoryContext); + } + + public EventType EventType + { + get { return _eventType; } + } + + public bool CanReuse(View view, AgentInstanceContext agentInstanceContext) + { + if (!(view is ExternallyTimedBatchView)) + { + return false; + } + + var myView = (ExternallyTimedBatchView) view; + var delta = _timeDeltaComputationFactory.Make(ViewName, "view", agentInstanceContext); + if ((!delta.EqualsTimePeriod(myView.GetTimeDeltaComputation())) || + (!ExprNodeUtility.DeepEquals(myView.TimestampExpression, _timestampExpression))) + { + return false; + } + return myView.IsEmpty(); + } + + public string ViewName + { + get { return "Externally-timed-batch"; } + } + + public ExprEvaluator TimestampExpressionEval + { + get { return _timestampExpressionEval; } + } + + public long? OptionalReferencePoint + { + get { return _optionalReferencePoint; } + } + + private string ViewParamMessage + { + get + { + return ViewName + + " view requires a timestamp expression and a numeric or time period parameter for window size and an optional long-typed reference point in msec, and an optional list of control keywords as a string parameter (please see the documentation)"; + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/view/window/ExternallyTimedWindowView.cs b/NEsper.Core/NEsper.Core/view/window/ExternallyTimedWindowView.cs new file mode 100755 index 000000000..a98f9d54e --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/window/ExternallyTimedWindowView.cs @@ -0,0 +1,226 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.time; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.util; + +namespace com.espertech.esper.view.window +{ + /// + /// View for a moving window extending the specified amount of time into the past, driven entirely by external timing + /// supplied within long-type timestamp values in a field of the event beans that the view receives. + /// The view is completely driven by timestamp values that are supplied by the events it receives, + /// and does not use the schedule service time. + /// It requires a field name as parameter for a field that returns ascending long-type timestamp values. + /// It also requires a long-type parameter setting the time length in milliseconds of the time window. + /// Events are expected to provide long-type timestamp values in natural order. The view does + /// itself not use the current system time for keeping track of the time window, but just the + /// timestamp values supplied by the events sent in. + /// The arrival of new events with a newer timestamp then past events causes the window to be re-evaluated and the + /// oldest + /// events pushed out of the window. Ie. Assume event X1 with timestamp T1 is in the window. + /// When event Xn with timestamp Tn arrives, and the window time length in milliseconds is t, then if + /// ((Tn - T1) > t == true) then event X1 is pushed as oldData out of the window. It is assumed that + /// events are sent in in their natural order and the timestamp values are ascending. + /// + public class ExternallyTimedWindowView + : ViewSupport + , DataWindowView + , CloneableView + { + private readonly EventBean[] _eventsPerStream = new EventBean[1]; + private readonly ExternallyTimedWindowViewFactory _externallyTimedWindowViewFactory; + private readonly ExprTimePeriodEvalDeltaConst _timeDeltaComputation; + + private readonly TimeWindow _timeWindow; + private readonly ExprNode _timestampExpression; + private readonly ExprEvaluator _timestampExpressionEval; + private readonly ViewUpdatedCollection _viewUpdatedCollection; + internal AgentInstanceViewFactoryChainContext AgentInstanceViewFactoryContext; + + /// + /// Constructor. + /// + /// for copying this view in a group-by + /// is the field name containing a long timestamp valuethat should be in ascending order for the natural order of + /// events and is intended to reflect + /// System.currentTimeInMillis but does not necessarily have to. + /// out of the window as oldData in the update method. The view compares + /// each events timestamp against the newest event timestamp and those with a delta + /// greater then secondsBeforeExpiry are pushed out of the window. + /// The timestamp expression eval. + /// The time delta computation. + /// is a collection that the view must update when receiving events + /// context for expression evalauation + public ExternallyTimedWindowView( + ExternallyTimedWindowViewFactory externallyTimedWindowViewFactory, + ExprNode timestampExpression, + ExprEvaluator timestampExpressionEval, + ExprTimePeriodEvalDeltaConst timeDeltaComputation, + ViewUpdatedCollection viewUpdatedCollection, + AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext) + { + _externallyTimedWindowViewFactory = externallyTimedWindowViewFactory; + _timestampExpression = timestampExpression; + _timestampExpressionEval = timestampExpressionEval; + _timeDeltaComputation = timeDeltaComputation; + _viewUpdatedCollection = viewUpdatedCollection; + _timeWindow = new TimeWindow(agentInstanceViewFactoryContext.IsRemoveStream); + AgentInstanceViewFactoryContext = agentInstanceViewFactoryContext; + } + + /// + /// Returns the field name to get timestamp values from. + /// + /// field name for timestamp values + public ExprNode TimestampExpression + { + get { return _timestampExpression; } + } + + public ExprTimePeriodEvalDeltaConst TimeDeltaComputation + { + get { return _timeDeltaComputation; } + } + + public View CloneView() + { + return _externallyTimedWindowViewFactory.MakeView(AgentInstanceViewFactoryContext); + } + + public override EventType EventType + { + get + { + // The schema is the parent view's schema + return Parent.EventType; + } + } + + 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 expired = null; + if (timestamp != -1) + { + expired = + _timeWindow.ExpireEvents(timestamp - _timeDeltaComputation.DeltaSubtract(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(); } + } + + public override IEnumerator GetEnumerator() + { + return _timeWindow.GetEnumerator(); + } + + public void VisitView(ViewDataVisitor viewDataVisitor) + { + _timeWindow.VisitView(viewDataVisitor, _externallyTimedWindowViewFactory); + } + + public override String ToString() + { + return GetType().FullName + + " timestampExpression=" + _timestampExpression; + } + + private long GetLongValue(EventBean obj) + { + _eventsPerStream[0] = obj; + var num = _timestampExpressionEval.Evaluate(new EvaluateParams(_eventsPerStream, true, AgentInstanceViewFactoryContext)); + return num.AsLong(); + } + + /// + /// Returns true to indicate the window is empty, or false if the view is not empty. + /// + /// true if empty + public bool IsEmpty() + { + return _timeWindow.IsEmpty(); + } + + public ViewUpdatedCollection GetViewUpdatedCollection() + { + return _viewUpdatedCollection; + } + + public ViewFactory ViewFactory + { + get { return _externallyTimedWindowViewFactory; } + } + } +} \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/view/window/ExternallyTimedWindowViewFactory.cs b/NEsper.Core/NEsper.Core/view/window/ExternallyTimedWindowViewFactory.cs new file mode 100755 index 000000000..3e3c6c98c --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/window/ExternallyTimedWindowViewFactory.cs @@ -0,0 +1,126 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.time; +using com.espertech.esper.util; + +namespace com.espertech.esper.view.window +{ + /// Factory for . + public class ExternallyTimedWindowViewFactory + : DataWindowViewFactory, + DataWindowViewWithPrevious + { + /// The timestamp property name. + private ExprNode _timestampExpression; + + private ExprEvaluator _timestampExpressionEval; + + /// The number of msec to expire. + private ExprTimePeriodEvalDeltaConstFactory _timeDeltaComputationFactory; + + private IList _viewParameters; + private EventType _eventType; + + public void SetViewParameters(ViewFactoryContext viewFactoryContext, IList expressionParameters) + { + _viewParameters = expressionParameters; + } + + public void Attach( + EventType parentEventType, + StatementContext statementContext, + ViewFactory optionalParentFactory, + IList parentViewFactories) + { + var validated = ViewFactorySupport.Validate( + ViewName, parentEventType, statementContext, _viewParameters, true); + if (_viewParameters.Count != 2) + { + throw new ViewParameterException(ViewParamMessage); + } + + if (!validated[0].ExprEvaluator.ReturnType.IsNumeric()) + { + throw new ViewParameterException(ViewParamMessage); + } + _timestampExpression = validated[0]; + _timestampExpressionEval = _timestampExpression.ExprEvaluator; + ViewFactorySupport.AssertReturnsNonConstant(ViewName, validated[0], 0); + + _timeDeltaComputationFactory = ViewFactoryTimePeriodHelper.ValidateAndEvaluateTimeDeltaFactory( + ViewName, statementContext, _viewParameters[1], ViewParamMessage, 1); + _eventType = parentEventType; + } + + public Object MakePreviousGetter() + { + return new RandomAccessByIndexGetter(); + } + + public View MakeView(AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext) + { + var timeDeltaComputation = _timeDeltaComputationFactory.Make( + ViewName, "view", agentInstanceViewFactoryContext.AgentInstanceContext); + var randomAccess = + agentInstanceViewFactoryContext.StatementContext.ViewServicePreviousFactory + .GetOptPreviousExprRandomAccess(agentInstanceViewFactoryContext); + return new ExternallyTimedWindowView( + this, _timestampExpression, _timestampExpressionEval, timeDeltaComputation, randomAccess, + agentInstanceViewFactoryContext); + } + + public EventType EventType + { + get { return _eventType; } + } + + public bool CanReuse(View view, AgentInstanceContext agentInstanceContext) + { + if (!(view is ExternallyTimedWindowView)) + { + return false; + } + + var myView = (ExternallyTimedWindowView) view; + var delta = _timeDeltaComputationFactory.Make(ViewName, "view", agentInstanceContext); + if ((!delta.EqualsTimePeriod(myView.TimeDeltaComputation)) || + (!ExprNodeUtility.DeepEquals(myView.TimestampExpression, _timestampExpression))) + { + return false; + } + return myView.IsEmpty(); + } + + public string ViewName + { + get { return "Externally-timed"; } + } + + public ExprEvaluator TimestampExpressionEval + { + get { return _timestampExpressionEval; } + } + + private string ViewParamMessage + { + get + { + return ViewName + + " view requires a timestamp expression and a numeric or time period parameter for window size"; + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/view/window/FirstLengthWindowView.cs b/NEsper.Core/NEsper.Core/view/window/FirstLengthWindowView.cs new file mode 100755 index 000000000..9ecc7424d --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/window/FirstLengthWindowView.cs @@ -0,0 +1,165 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.view.window +{ + /// + /// A length-first view takes the first Count arriving events. Further arriving insert stream + /// events are disregarded until events are deleted. Remove stream events delete + /// from the data window. + /// + public class FirstLengthWindowView : ViewSupport, DataWindowView, CloneableView + { + protected readonly AgentInstanceViewFactoryChainContext AgentInstanceViewFactoryContext; + private readonly FirstLengthWindowViewFactory _lengthFirstFactory; + private readonly LinkedHashSet _indexedEvents; + + /// + /// Ctor. + /// + /// The agent instance view factory context. + /// for copying this view in a group-by + /// the first Count events to consider + public FirstLengthWindowView(AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext, FirstLengthWindowViewFactory lengthFirstWindowViewFactory, int size) + { + if (size < 1) + { + throw new ArgumentException("Illegal argument for size of length window"); + } + + AgentInstanceViewFactoryContext = agentInstanceViewFactoryContext; + _lengthFirstFactory = lengthFirstWindowViewFactory; + Size = size; + _indexedEvents = new LinkedHashSet(); + } + + public View CloneView() + { + return _lengthFirstFactory.MakeView(AgentInstanceViewFactoryContext); + } + + /// Returns true if the window is empty, or false if not empty. + /// true if empty + public bool IsEmpty() + { + return _indexedEvents.IsEmpty; + } + + /// Returns the size of the length window. + /// size of length window + public int Size { get; private set; } + + public override EventType EventType + { + get + { + // The event type is the parent view's event type + return Parent.EventType; + } + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QViewProcessIRStream(this, _lengthFirstFactory.ViewName, newData, oldData); } + + OneEventCollection newDataToPost = null; + OneEventCollection oldDataToPost = null; + + // add data points to the window as long as its not full, ignoring later events + if (newData != null) + { + foreach (EventBean aNewData in newData) + { + if (_indexedEvents.Count < Size) + { + if (newDataToPost == null) + { + newDataToPost = new OneEventCollection(); + } + newDataToPost.Add(aNewData); + _indexedEvents.Add(aNewData); + InternalHandleAdded(aNewData); + } + } + } + + if (oldData != null) + { + foreach (EventBean anOldData in oldData) + { + bool removed = _indexedEvents.Remove(anOldData); + if (removed) + { + if (oldDataToPost == null) + { + oldDataToPost = new OneEventCollection(); + } + oldDataToPost.Add(anOldData); + InternalHandleRemoved(anOldData); + } + } + } + + // If there are child views, call Update method + if (HasViews && ((newDataToPost != null) || (oldDataToPost != null))) + { + EventBean[] nd = (newDataToPost != null) ? newDataToPost.ToArray() : null; + EventBean[] od = (oldDataToPost != null) ? oldDataToPost.ToArray() : null; + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QViewIndicate(this, _lengthFirstFactory.ViewName, nd, od); } + UpdateChildren(nd, od); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AViewIndicate(); } + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AViewProcessIRStream(); } + } + + public void InternalHandleRemoved(EventBean anOldData) + { + // no action required + } + + public void InternalHandleAdded(EventBean aNewData) + { + // no action required + } + + public override IEnumerator GetEnumerator() + { + return _indexedEvents.GetEnumerator(); + } + + public override String ToString() + { + return GetType().FullName + " size=" + Size; + } + + public void VisitView(ViewDataVisitor viewDataVisitor) + { + viewDataVisitor.VisitPrimary(_indexedEvents, true, _lengthFirstFactory.ViewName, null); + } + + public LinkedHashSet IndexedEvents + { + get { return _indexedEvents; } + } + + public ViewFactory ViewFactory + { + get { return _lengthFirstFactory; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/window/FirstLengthWindowViewFactory.cs b/NEsper.Core/NEsper.Core/view/window/FirstLengthWindowViewFactory.cs new file mode 100755 index 000000000..c3dff05fc --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/window/FirstLengthWindowViewFactory.cs @@ -0,0 +1,70 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.view.window +{ + /// Factory for . + public class FirstLengthWindowViewFactory : AsymetricDataWindowViewFactory + { + private EventType _eventType; + + /// Size of length first window. + private ExprEvaluator _sizeEvaluator; + + public void SetViewParameters(ViewFactoryContext viewFactoryContext, IList expressionParameters) + { + _sizeEvaluator = ViewFactorySupport.ValidateSizeSingleParam( + ViewName, viewFactoryContext, expressionParameters); + } + + public void Attach( + EventType parentEventType, + StatementContext statementContext, + ViewFactory optionalParentFactory, + IList parentViewFactories) + { + _eventType = parentEventType; + } + + public View MakeView(AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext) + { + int size = ViewFactorySupport.EvaluateSizeParam( + ViewName, _sizeEvaluator, agentInstanceViewFactoryContext.AgentInstanceContext); + return new FirstLengthWindowView(agentInstanceViewFactoryContext, this, size); + } + + public EventType EventType + { + get { return _eventType; } + } + + public bool CanReuse(View view, AgentInstanceContext agentInstanceContext) + { + if (!(view is FirstLengthWindowView)) + { + return false; + } + + var myView = (FirstLengthWindowView) view; + int size = ViewFactorySupport.EvaluateSizeParam(ViewName, _sizeEvaluator, agentInstanceContext); + return myView.Size == size && myView.IsEmpty(); + } + + public string ViewName + { + get { return "First-Length"; } + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/view/window/FirstTimeView.cs b/NEsper.Core/NEsper.Core/view/window/FirstTimeView.cs new file mode 100755 index 000000000..5b132461c --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/window/FirstTimeView.cs @@ -0,0 +1,223 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.time; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.schedule; +using com.espertech.esper.util; + +namespace com.espertech.esper.view.window +{ + public class FirstTimeView + : ViewSupport + , CloneableView + , StoppableView + , DataWindowView + , StopCallback + { + private readonly FirstTimeViewFactory _timeFirstViewFactory; + protected readonly AgentInstanceViewFactoryChainContext AgentInstanceContext; + private readonly ExprTimePeriodEvalDeltaConst _timeDeltaComputation; + private readonly long _scheduleSlot; + private EPStatementHandleCallback _handle; + + // Current running parameters + private readonly LinkedHashSet _events = new LinkedHashSet(); + private bool _isClosed; + + /// + /// Constructor. + /// + /// For copying this view in a group-by + /// The agent instance context. + /// The time delta computation. + public FirstTimeView( + FirstTimeViewFactory timeFirstViewFactory, + AgentInstanceViewFactoryChainContext agentInstanceContext, + ExprTimePeriodEvalDeltaConst timeDeltaComputation) + { + AgentInstanceContext = agentInstanceContext; + _timeFirstViewFactory = timeFirstViewFactory; + _timeDeltaComputation = timeDeltaComputation; + + _scheduleSlot = agentInstanceContext.StatementContext.ScheduleBucket.AllocateSlot(); + + ScheduleCallback(); + + agentInstanceContext.AddTerminationCallback(Stop); + } + + public View CloneView() + { + return _timeFirstViewFactory.MakeView(AgentInstanceContext); + } + + public ExprTimePeriodEvalDeltaConst TimeDeltaComputation + { + get { return _timeDeltaComputation; } + } + + public override EventType EventType + { + get { return Parent.EventType; } + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + using (Instrument.With( + i => i.QViewProcessIRStream(this, _timeFirstViewFactory.ViewName, newData, oldData), + i => i.AViewProcessIRStream())) + { + OneEventCollection oldDataToPost = null; + if (oldData != null) + { + foreach (EventBean anOldData in oldData) + { + bool removed = _events.Remove(anOldData); + if (removed) + { + if (oldDataToPost == null) + { + oldDataToPost = new OneEventCollection(); + } + oldDataToPost.Add(anOldData); + InternalHandleRemoved(anOldData); + } + } + } + + // add data points to the timeWindow + OneEventCollection newDataToPost = null; + if ((!_isClosed) && (newData != null)) + { + foreach (EventBean aNewData in newData) + { + _events.Add(aNewData); + if (newDataToPost == null) + { + newDataToPost = new OneEventCollection(); + } + newDataToPost.Add(aNewData); + InternalHandleAdded(aNewData); + } + } + + // If there are child views, call Update method + if ((HasViews) && ((newDataToPost != null) || (oldDataToPost != null))) + { + EventBean[] nd = (newDataToPost != null) ? newDataToPost.ToArray() : null; + EventBean[] od = (oldDataToPost != null) ? oldDataToPost.ToArray() : null; + Instrument.With( + i => i.QViewIndicate(this, _timeFirstViewFactory.ViewName, nd, od), + i => i.AViewIndicate(), + () => UpdateChildren(nd, od)); + } + } + } + + public void InternalHandleAdded(EventBean newEvent) + { + // no action + } + + public void InternalHandleRemoved(EventBean oldEvent) + { + // no action + } + + public void InternalHandleClosed() + { + // no action + } + + /// Returns true if the window is empty, or false if not empty. + /// true if empty + public bool IsEmpty() + { + return _events.IsEmpty; + } + + public override IEnumerator GetEnumerator() + { + return _events.GetEnumerator(); + } + + public override String ToString() + { + return GetType().FullName; + } + + private void ScheduleCallback() + { + long afterTime = _timeDeltaComputation.DeltaAdd( + AgentInstanceContext.StatementContext.SchedulingService.Time); + + ScheduleHandleCallback callback = new ProxyScheduleHandleCallback + { + ProcScheduledTrigger = extensionServicesContext => Instrument.With( + i => i.QViewScheduledEval(this, _timeFirstViewFactory.ViewName), + i => i.AViewScheduledEval(), + () => + { + _isClosed = true; + InternalHandleClosed(); + }) + }; + _handle = new EPStatementHandleCallback(AgentInstanceContext.EpStatementAgentInstanceHandle, callback); + AgentInstanceContext.StatementContext.SchedulingService.Add(afterTime, _handle, _scheduleSlot); + } + + public void StopView() + { + StopSchedule(); + AgentInstanceContext.RemoveTerminationCallback(Stop); + } + + public void Stop() + { + StopSchedule(); + } + + public void StopSchedule() + { + if (_handle != null) + { + AgentInstanceContext.StatementContext.SchedulingService.Remove(_handle, _scheduleSlot); + } + } + + public void SetClosed(bool closed) + { + _isClosed = closed; + } + + public LinkedHashSet Events + { + get { return _events; } + } + + public void VisitView(ViewDataVisitor viewDataVisitor) + { + viewDataVisitor.VisitPrimary(_events, true, _timeFirstViewFactory.ViewName, null); + } + + public ViewFactory ViewFactory + { + get { return _timeFirstViewFactory; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/window/FirstTimeViewFactory.cs b/NEsper.Core/NEsper.Core/view/window/FirstTimeViewFactory.cs new file mode 100755 index 000000000..91406a8da --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/window/FirstTimeViewFactory.cs @@ -0,0 +1,87 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.time; + +namespace com.espertech.esper.view.window +{ + /// Factory for . + public class FirstTimeViewFactory + : AsymetricDataWindowViewFactory, + DataWindowBatchingViewFactory + { + private EventType _eventType; + + /// Number of msec before expiry. + private ExprTimePeriodEvalDeltaConstFactory _timeDeltaComputationFactory; + + public void SetViewParameters(ViewFactoryContext viewFactoryContext, IList expressionParameters) + { + if (expressionParameters.Count != 1) + { + throw new ViewParameterException(ViewParamMessage); + } + _timeDeltaComputationFactory = ViewFactoryTimePeriodHelper.ValidateAndEvaluateTimeDeltaFactory( + ViewName, viewFactoryContext.StatementContext, expressionParameters[0], ViewParamMessage, 0); + } + + public void Attach( + EventType parentEventType, + StatementContext statementContext, + ViewFactory optionalParentFactory, + IList parentViewFactories) + { + _eventType = parentEventType; + } + + public View MakeView(AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext) + { + ExprTimePeriodEvalDeltaConst timeDeltaComputation = _timeDeltaComputationFactory.Make( + ViewName, "view", agentInstanceViewFactoryContext.AgentInstanceContext); + return new FirstTimeView(this, agentInstanceViewFactoryContext, timeDeltaComputation); + } + + public EventType EventType + { + get { return _eventType; } + } + + public bool CanReuse(View view, AgentInstanceContext agentInstanceContext) + { + if (!(view is FirstTimeView)) + { + return false; + } + + var myView = (FirstTimeView) view; + ExprTimePeriodEvalDeltaConst delta = _timeDeltaComputationFactory.Make( + ViewName, "view", agentInstanceContext); + if (!delta.EqualsTimePeriod(myView.TimeDeltaComputation)) + { + return false; + } + return myView.IsEmpty(); + } + + public string ViewName + { + get { return "First-TimeInMillis"; } + } + + private string ViewParamMessage + { + get { return ViewName + " view requires a single numeric or time period parameter"; } + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/view/window/IStreamRandomAccess.cs b/NEsper.Core/NEsper.Core/view/window/IStreamRandomAccess.cs new file mode 100755 index 000000000..b57da1533 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/window/IStreamRandomAccess.cs @@ -0,0 +1,136 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.view.window +{ + /// + /// For use with length and time window views that must provide random access into + /// data window contents provided for the "previous" expression if used. + /// + public class IStreamRandomAccess : RandomAccessByIndex, ViewUpdatedCollection + { + private readonly List _arrayList; + private readonly RandomAccessByIndexObserver _updateObserver; + + /// Ctor. + /// is invoked when updates are received + public IStreamRandomAccess(RandomAccessByIndexObserver updateObserver) + { + _updateObserver = updateObserver; + _arrayList = new List(); + } + + public void Update(EventBean[] newData, EventBean[] oldData) + { + if (_updateObserver !=null) + { + _updateObserver.Updated(this); + } + if (newData != null) + { + for (int i = 0; i < newData.Length; i++) + { + _arrayList.Insert(0, newData[i]); + } + } + + if (oldData != null) + { + for (int i = 0; i < oldData.Length; i++) + { + _arrayList.RemoveAt(_arrayList.Count - 1); + } + } + } + + /// Remove event. + /// event to remove + public void Remove(EventBean oldData) + { + if (_updateObserver !=null) + { + _updateObserver.Updated(this); + } + _arrayList.RemoveAt(_arrayList.Count - 1); + } + + /// Apply event + /// to apply + public void Update(EventBean newData) + { + if (_updateObserver !=null) + { + _updateObserver.Updated(this); + } + _arrayList.Insert(0, newData); + } + + public EventBean GetNewData(int index) + { + // New events are added to the start of the list + if (index < _arrayList.Count ) + { + return _arrayList[index]; + } + return null; + } + + public EventBean GetOldData(int index) + { + return null; + } + + public void Destroy() + { + // No action required + } + + /// Returns true for empty. + /// indicator + public bool IsEmpty() + { + return _arrayList.IsEmpty(); + } + + public EventBean GetNewDataTail(int index) + { + // New events are added to the start of the list + if (index < _arrayList.Count && index >= 0) + { + return _arrayList[_arrayList.Count - index - 1]; + } + return null; + } + + public IEnumerator GetWindowEnumerator() + { + return _arrayList.GetEnumerator(); + } + + public ICollection WindowCollectionReadOnly + { + get { return _arrayList; } + } + + public int WindowCount + { + get { return _arrayList.Count; } + } + + public int NumEventsInsertBuf + { + get { return WindowCount; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/window/IStreamRelativeAccess.cs b/NEsper.Core/NEsper.Core/view/window/IStreamRelativeAccess.cs new file mode 100755 index 000000000..c40286512 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/window/IStreamRelativeAccess.cs @@ -0,0 +1,154 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.view.window +{ + /// + /// Provides relative access to insert stream events for certain window. + /// + public class IStreamRelativeAccess : RelativeAccessByEventNIndex, ViewUpdatedCollection + { + private readonly IDictionary _indexPerEvent; + private EventBean[] _lastNewData; + private readonly IStreamRelativeAccessUpdateObserver _updateObserver; + + /// Ctor. + /// is invoked when updates are received + public IStreamRelativeAccess(IStreamRelativeAccessUpdateObserver updateObserver) + { + _updateObserver = updateObserver; + _indexPerEvent = new Dictionary(); + } + + public void Update(EventBean[] newData, EventBean[] oldData) + { + _updateObserver.Updated(this, newData); + _indexPerEvent.Clear(); + _lastNewData = newData; + + if (newData != null) + { + for (int i = 0; i < newData.Length; i++) + { + _indexPerEvent.Put(newData[i], i); + } + } + } + + public EventBean GetRelativeToEvent(EventBean theEvent, int prevIndex) + { + if (_lastNewData == null) + { + return null; + } + + if (prevIndex == 0) + { + return theEvent; + } + + int indexIncoming; + + if (!_indexPerEvent.TryGetValue(theEvent, out indexIncoming)) + { + return null; + } + + if (prevIndex > indexIncoming) + { + return null; + } + + int relativeIndex = indexIncoming - prevIndex; + if ((relativeIndex < _lastNewData.Length) && (relativeIndex >= 0)) + { + return _lastNewData[relativeIndex]; + } + return null; + } + + public EventBean GetRelativeToEnd(int prevIndex) + { + if (_lastNewData == null) + { + return null; + } + + if (prevIndex < _lastNewData.Length && prevIndex >= 0) + { + return _lastNewData[prevIndex]; + } + return null; + } + + public IEnumerator GetWindowToEvent() + { + return Enumerable.Reverse(_lastNewData).GetEnumerator(); + } + + public ICollection GetWindowToEventCollReadOnly() + { + return _lastNewData; + } + + public int GetWindowToEventCount() + { + if (_lastNewData == null) { + return 0; + } + return _lastNewData.Length; + } + + public void Destroy() + { + // No action required + } + + public int NumEventsInsertBuf + { + get { return _indexPerEvent.Count; } + } + } + + public interface IStreamRelativeAccessUpdateObserver + { + /// + /// For indicating that the collection has been updated. + /// is the collection + /// is the new data available + /// + void Updated(RelativeAccessByEventNIndex iStreamRelativeAccess, EventBean[] newData); + } + + public class ProxyIStreamRelativeAccessUpdateObserver : IStreamRelativeAccessUpdateObserver + { + public Action ProcUpdated; + + public ProxyIStreamRelativeAccessUpdateObserver() + { + } + + public ProxyIStreamRelativeAccessUpdateObserver(Action procUpdated) + { + ProcUpdated = procUpdated; + } + + public void Updated(RelativeAccessByEventNIndex iStreamRelativeAccess, EventBean[] newData) + { + ProcUpdated.Invoke(iStreamRelativeAccess, newData); + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/window/KeepAllView.cs b/NEsper.Core/NEsper.Core/view/window/KeepAllView.cs new file mode 100755 index 000000000..5e13a5000 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/window/KeepAllView.cs @@ -0,0 +1,138 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.view.window +{ + /// + /// This view is a keep-all data window that simply keeps all events added. It in + /// addition allows to remove events efficiently for the remove-stream events received + /// by the view. + /// + public class KeepAllView : ViewSupport, DataWindowView, CloneableView + { + protected readonly AgentInstanceViewFactoryChainContext AgentInstanceViewFactoryContext; + private readonly KeepAllViewFactory _keepAllViewFactory; + private readonly LinkedHashSet _indexedEvents; + private readonly ViewUpdatedCollection _viewUpdatedCollection; + + /// + /// Ctor. + /// + /// The agent instance view factory context. + /// for copying this view in a group-by + /// for satisfying queries that select previous events in window order + public KeepAllView(AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext, KeepAllViewFactory keepAllViewFactory, ViewUpdatedCollection viewUpdatedCollection) + { + AgentInstanceViewFactoryContext = agentInstanceViewFactoryContext; + _keepAllViewFactory = keepAllViewFactory; + _indexedEvents = new LinkedHashSet(); + _viewUpdatedCollection = viewUpdatedCollection; + } + + public View CloneView() + { + return _keepAllViewFactory.MakeView(AgentInstanceViewFactoryContext); + } + + /// Returns true if the window is empty, or false if not empty. + /// true if empty + public bool IsEmpty() + { + return _indexedEvents.IsEmpty; + } + + /// Returns the (optional) collection handling random access to window contents for prior or previous events. + /// buffer for events + public ViewUpdatedCollection ViewUpdatedCollection + { + get { return _viewUpdatedCollection; } + } + + public override EventType EventType + { + get + { + // The event type is the parent view's event type + return Parent.EventType; + } + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QViewProcessIRStream(this, _keepAllViewFactory.ViewName, newData, oldData); } + + if (newData != null) + { + foreach (EventBean newEvent in newData) + { + _indexedEvents.Add(newEvent); + InternalHandleAdded(newEvent); + } + } + + if (oldData != null) + { + foreach (EventBean anOldData in oldData) + { + _indexedEvents.Remove(anOldData); + InternalHandleRemoved(anOldData); + } + } + + // Update event buffer for access by expressions, if any + if (_viewUpdatedCollection != null) + { + _viewUpdatedCollection.Update(newData, oldData); + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QViewIndicate(this, _keepAllViewFactory.ViewName, newData, oldData); } + UpdateChildren(newData, oldData); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AViewIndicate(); } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AViewProcessIRStream(); } + } + + public override IEnumerator GetEnumerator() + { + return _indexedEvents.GetEnumerator(); + } + + public void InternalHandleAdded(EventBean newEvent) + { + // no action required + } + + public void InternalHandleRemoved(EventBean oldEvent) + { + // no action required + } + + public void VisitView(ViewDataVisitor viewDataVisitor) + { + viewDataVisitor.VisitPrimary(_indexedEvents, true, _keepAllViewFactory.ViewName, null); + } + + public LinkedHashSet IndexedEvents + { + get { return _indexedEvents; } + } + + public ViewFactory ViewFactory + { + get { return _keepAllViewFactory; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/window/KeepAllViewFactory.cs b/NEsper.Core/NEsper.Core/view/window/KeepAllViewFactory.cs new file mode 100755 index 000000000..3d0a3b026 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/window/KeepAllViewFactory.cs @@ -0,0 +1,77 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.view.window +{ + /// + /// Factory for . + /// + public class KeepAllViewFactory + : DataWindowViewFactory + , DataWindowViewWithPrevious + { + private EventType _eventType; + + public void SetViewParameters(ViewFactoryContext viewFactoryContext, IList expressionParameters) + { + ViewFactorySupport.ValidateNoParameters(ViewName, expressionParameters); + } + + public void Attach( + EventType parentEventType, + StatementContext statementContext, + ViewFactory optionalParentFactory, + IList parentViewFactories) + { + _eventType = parentEventType; + } + + public View MakeView(AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext) + { + ViewUpdatedCollection randomAccess = + agentInstanceViewFactoryContext.StatementContext.ViewServicePreviousFactory + .GetOptPreviousExprRandomAccess(agentInstanceViewFactoryContext); + return new KeepAllView(agentInstanceViewFactoryContext, this, randomAccess); + } + + public EventType EventType + { + get { return _eventType; } + } + + public bool CanReuse(View view, AgentInstanceContext agentInstanceContext) + { + if (!(view is KeepAllView)) + { + return false; + } + + var myView = (KeepAllView) view; + return myView.IsEmpty(); + } + + public string ViewName + { + get { return "Keep-All"; } + } + + public Object MakePreviousGetter() + { + return new RandomAccessByIndexGetter(); + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/view/window/LengthBatchView.cs b/NEsper.Core/NEsper.Core/view/window/LengthBatchView.cs new file mode 100755 index 000000000..03dc229f8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/window/LengthBatchView.cs @@ -0,0 +1,190 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.view.window +{ + /// + /// A data view that aggregates events in a stream and releases them in one batch when a + /// maximum number of events has been collected. The view works similar to a + /// length_window but is not continuous, and similar to a time_batch however is not time-based + /// but reacts to the number of events. + /// + /// The view releases the batched events, when a certain number of batched events has been + /// reached or exceeded, as new data to child views. The prior batch if not empty is released + /// as old data to any child views. The view doesn't release intervals with no old or new data. + /// It also does not collect old data published by a parent view. + /// + /// If there are no events in the current and prior batch, the view will not invoke the Update + /// method of child views. + /// + public class LengthBatchView : ViewSupport, CloneableView, DataWindowView + { + // View parameters + protected readonly AgentInstanceViewFactoryChainContext AgentInstanceViewFactoryContext; + private readonly LengthBatchViewFactory _lengthBatchViewFactory; + private readonly int _size; + private readonly ViewUpdatedCollection _viewUpdatedCollection; + + // Current running windows + protected ArrayDeque LastBatch = null; + protected ArrayDeque CurrentBatch = new ArrayDeque(); + + /// + /// Constructor. + /// + /// The agent instance view factory context. + /// for copying this view in a group-by + /// is the number of events to batch + /// is a collection that the view must Update when receiving events + public LengthBatchView(AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext, + LengthBatchViewFactory lengthBatchViewFactory, + int size, + ViewUpdatedCollection viewUpdatedCollection) + { + AgentInstanceViewFactoryContext = agentInstanceViewFactoryContext; + _lengthBatchViewFactory = lengthBatchViewFactory; + _size = size; + _viewUpdatedCollection = viewUpdatedCollection; + + if (size <= 0) + { + throw new ArgumentException("Invalid size parameter, size=" + size); + } + } + + public View CloneView() + { + return _lengthBatchViewFactory.MakeView(AgentInstanceViewFactoryContext); + } + + /// Returns the number of events to batch (data window size). + /// batch size + public int Size + { + get { return _size; } + } + + public override EventType EventType + { + get { return Parent.EventType; } + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QViewProcessIRStream(this, _lengthBatchViewFactory.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; + } + + // add data points to the current batch + foreach (EventBean newEvent in newData) + { + CurrentBatch.Add(newEvent); + } + + // check if we reached the minimum size + if (CurrentBatch.Count < _size) + { + // done if no overflow + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AViewProcessIRStream(); } + return; + } + + SendBatch(); + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AViewProcessIRStream(); } + } + + /// This method updates child views and clears the batch of events. + protected void SendBatch() + { + // 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.IsNotEmpty()) + { + newData = CurrentBatch.ToArray(); + } + if ((LastBatch != null) && (LastBatch.IsNotEmpty())) + { + oldData = LastBatch.ToArray(); + } + + // Update view buffer to serve expressions require access to events held + if (_viewUpdatedCollection != null) + { + _viewUpdatedCollection.Update(newData, oldData); + } + + // Post new data (current batch) and old data (prior batch) + if ((newData != null) || (oldData != null)) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QViewIndicate(this, _lengthBatchViewFactory.ViewName, newData, oldData); } + UpdateChildren(newData, oldData); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AViewIndicate(); } + } + } + + LastBatch = CurrentBatch; + CurrentBatch = new ArrayDeque(); + } + + /// Returns true if the window is empty, or false if not empty. + /// true if empty + public bool IsEmpty() + { + if (LastBatch != null) + { + if (LastBatch.IsNotEmpty()) + { + return false; + } + } + return CurrentBatch.IsEmpty(); + } + + public override IEnumerator GetEnumerator() + { + return CurrentBatch.GetEnumerator(); + } + + public override String ToString() + { + return GetType().FullName + + " size=" + _size; + } + + public void VisitView(ViewDataVisitor viewDataVisitor) + { + viewDataVisitor.VisitPrimary(CurrentBatch, true, _lengthBatchViewFactory.ViewName, null); + viewDataVisitor.VisitPrimary(LastBatch, true, _lengthBatchViewFactory.ViewName, null); + } + + public ViewFactory ViewFactory + { + get { return _lengthBatchViewFactory; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/window/LengthBatchViewFactory.cs b/NEsper.Core/NEsper.Core/view/window/LengthBatchViewFactory.cs new file mode 100755 index 000000000..08a6bf287 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/window/LengthBatchViewFactory.cs @@ -0,0 +1,92 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.view.window +{ + /// + /// Factory for . + /// + public class LengthBatchViewFactory + : DataWindowViewFactory + , DataWindowViewWithPrevious + , DataWindowBatchingViewFactory + { + private EventType _eventType; + + /// The length window size. + private ExprEvaluator _sizeEvaluator; + + public void SetViewParameters(ViewFactoryContext viewFactoryContext, IList expressionParameters) + { + _sizeEvaluator = ViewFactorySupport.ValidateSizeSingleParam( + ViewName, viewFactoryContext, expressionParameters); + } + + public void Attach( + EventType parentEventType, + StatementContext statementContext, + ViewFactory optionalParentFactory, + IList parentViewFactories) + { + _eventType = parentEventType; + } + + public View MakeView(AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext) + { + int size = ViewFactorySupport.EvaluateSizeParam( + ViewName, _sizeEvaluator, agentInstanceViewFactoryContext.AgentInstanceContext); + ViewUpdatedCollection viewUpdatedCollection = + agentInstanceViewFactoryContext.StatementContext.ViewServicePreviousFactory + .GetOptPreviousExprRelativeAccess(agentInstanceViewFactoryContext); + if (agentInstanceViewFactoryContext.IsRemoveStream) + { + return new LengthBatchViewRStream(agentInstanceViewFactoryContext, this, size); + } + else + { + return new LengthBatchView(agentInstanceViewFactoryContext, this, size, viewUpdatedCollection); + } + } + + public EventType EventType + { + get { return _eventType; } + } + + public bool CanReuse(View view, AgentInstanceContext agentInstanceContext) + { + if (!(view is LengthBatchView)) + { + return false; + } + + var myView = (LengthBatchView) view; + int size = ViewFactorySupport.EvaluateSizeParam(ViewName, _sizeEvaluator, agentInstanceContext); + return myView.Size == size && myView.IsEmpty(); + } + + public string ViewName + { + get { return "Length-Batch"; } + } + + public Object MakePreviousGetter() + { + return new RelativeAccessByEventNIndexGetterImpl(); + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/view/window/LengthBatchViewRStream.cs b/NEsper.Core/NEsper.Core/view/window/LengthBatchViewRStream.cs new file mode 100755 index 000000000..4ec5696ad --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/window/LengthBatchViewRStream.cs @@ -0,0 +1,182 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.view.window +{ + /// + /// Same as the , this view also supports fast-remove + /// from the batch for remove stream events. + /// + public class LengthBatchViewRStream : ViewSupport, CloneableView, DataWindowView + { + // View parameters + protected readonly AgentInstanceViewFactoryChainContext AgentInstanceViewFactoryContext; + private readonly LengthBatchViewFactory _lengthBatchViewFactory; + private readonly int _size; + + // Current running windows + protected LinkedHashSet LastBatch = null; + protected LinkedHashSet CurrentBatch = new LinkedHashSet(); + + /// + /// Constructor. + /// + /// The agent instance view factory context. + /// for copying this view in a group-by + /// is the number of events to batch + public LengthBatchViewRStream(AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext, + LengthBatchViewFactory lengthBatchViewFactory, + int size) + { + _lengthBatchViewFactory = lengthBatchViewFactory; + _size = size; + AgentInstanceViewFactoryContext = agentInstanceViewFactoryContext; + + if (size <= 0) + { + throw new ArgumentException("Invalid size parameter, size=" + size); + } + } + + public View CloneView() + { + return _lengthBatchViewFactory.MakeView(AgentInstanceViewFactoryContext); + } + + /// Returns the number of events to batch (data window size). + /// batch size + public int Size + { + get { return _size; } + } + + public override EventType EventType + { + get { return Parent.EventType; } + } + + public void InternalHandleRemoved(EventBean oldData) + { + // no action required + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QViewProcessIRStream(this, _lengthBatchViewFactory.ViewName, newData, oldData);} + + if (oldData != null) + { + for (int i = 0; i < oldData.Length; i++) + { + if (CurrentBatch.Remove(oldData[i])) { + InternalHandleRemoved(oldData[i]); + } + } + } + + // we don't care about removed data from a prior view + if ((newData == null) || (newData.Length == 0)) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AViewProcessIRStream();} + return; + } + + // add data points to the current batch + foreach (EventBean newEvent in newData) { + CurrentBatch.Add(newEvent); + } + + // check if we reached the minimum size + if (CurrentBatch.Count < _size) + { + // done if no overflow + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AViewProcessIRStream();} + return; + } + + SendBatch(); + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AViewProcessIRStream();} + } + + /// This method updates child views and clears the batch of events. + protected void SendBatch() + { + // 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.IsNotEmpty()) + { + newData = CurrentBatch.ToArray(); + } + if ((LastBatch != null) && (LastBatch.IsNotEmpty())) + { + oldData = LastBatch.ToArray(); + } + + // Post new data (current batch) and old data (prior batch) + if ((newData != null) || (oldData != null)) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QViewIndicate(this, _lengthBatchViewFactory.ViewName, newData, oldData);} + UpdateChildren(newData, oldData); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AViewIndicate();} + } + } + + LastBatch = CurrentBatch; + CurrentBatch = new LinkedHashSet(); + } + + /// Returns true if the window is empty, or false if not empty. + /// true if empty + public bool IsEmpty() + { + if (LastBatch != null) + { + if (LastBatch.IsNotEmpty()) + { + return false; + } + } + return CurrentBatch.IsEmpty; + } + + public override IEnumerator GetEnumerator() + { + return CurrentBatch.GetEnumerator(); + } + + public override String ToString() + { + return GetType().FullName + + " size=" + _size; + } + + public void VisitView(ViewDataVisitor viewDataVisitor) + { + viewDataVisitor.VisitPrimary(CurrentBatch, true, _lengthBatchViewFactory.ViewName, null); + viewDataVisitor.VisitPrimary(LastBatch, true, _lengthBatchViewFactory.ViewName, null); + } + + public ViewFactory ViewFactory + { + get { return _lengthBatchViewFactory; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/window/LengthWindowView.cs b/NEsper.Core/NEsper.Core/view/window/LengthWindowView.cs new file mode 100755 index 000000000..729fee62e --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/window/LengthWindowView.cs @@ -0,0 +1,154 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.metrics.instrumentation; + +namespace com.espertech.esper.view.window +{ + /// + /// This view is a moving window extending the specified number of elements into the past. + /// + public class LengthWindowView : ViewSupport, DataWindowView, CloneableView + { + private readonly AgentInstanceViewFactoryChainContext _agentInstanceViewFactoryContext; + private readonly LengthWindowViewFactory _lengthWindowViewFactory; + private readonly int _size; + private readonly ViewUpdatedCollection _viewUpdatedCollection; + private readonly ArrayDeque _events = new ArrayDeque(); + + /// + /// Constructor creates a moving window extending the specified number of elements into the past. + /// + /// The agent instance view factory context. + /// for copying this view in a group-by + /// is the specified number of elements into the past + /// is a collection that the view must Update when receiving events + public LengthWindowView( + AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext, + LengthWindowViewFactory lengthWindowViewFactory, + int size, + ViewUpdatedCollection viewUpdatedCollection) + { + if (size < 1) + { + throw new ArgumentException("Illegal argument for size of length window"); + } + + _agentInstanceViewFactoryContext = agentInstanceViewFactoryContext; + _lengthWindowViewFactory = lengthWindowViewFactory; + _size = size; + _viewUpdatedCollection = viewUpdatedCollection; + } + + public View CloneView() + { + return _lengthWindowViewFactory.MakeView(_agentInstanceViewFactoryContext); + } + + /// Returns true if the window is empty, or false if not empty. + /// true if empty + public bool IsEmpty() + { + return _events.IsEmpty(); + } + + /// Returns the size of the length window. + /// size of length window + public int Size + { + get { return _size; } + } + + /// Returns the (optional) collection handling random access to window contents for prior or previous events. + /// buffer for events + public ViewUpdatedCollection ViewUpdatedCollection + { + get { return _viewUpdatedCollection; } + } + + public override EventType EventType + { + get + { + // The event type is the parent view's event type + return Parent.EventType; + } + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QViewProcessIRStream(this, _lengthWindowViewFactory.ViewName, newData, oldData); } + + // add data points to the window + // we don't care about removed data from a prior view + if (newData != null) + { + for (int ii = 0; ii < newData.Length; ii++) + { + _events.Add(newData[ii]); + } + } + + // Check for any events that get pushed out of the window + int expiredCount = _events.Count - _size; + EventBean[] expiredArr = null; + if (expiredCount > 0) + { + expiredArr = new EventBean[expiredCount]; + for (int i = 0; i < expiredCount; i++) + { + expiredArr[i] = _events.RemoveFirst(); + } + } + + // Update event buffer for access by expressions, if any + if (_viewUpdatedCollection != null) + { + _viewUpdatedCollection.Update(newData, expiredArr); + } + + // If there are child views, call Update method + if (HasViews) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QViewIndicate(this, _lengthWindowViewFactory.ViewName, newData, expiredArr); } + UpdateChildren(newData, expiredArr); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AViewIndicate(); } + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AViewProcessIRStream(); } + } + + public override IEnumerator GetEnumerator() + { + return _events.GetEnumerator(); + } + + public override String ToString() + { + return GetType().FullName + " size=" + _size; + } + + public void VisitView(ViewDataVisitor viewDataVisitor) + { + viewDataVisitor.VisitPrimary(_events, true, _lengthWindowViewFactory.ViewName, null); + } + + public ViewFactory ViewFactory + { + get { return _lengthWindowViewFactory; } + } + + } +} diff --git a/NEsper.Core/NEsper.Core/view/window/LengthWindowViewFactory.cs b/NEsper.Core/NEsper.Core/view/window/LengthWindowViewFactory.cs new file mode 100755 index 000000000..7700c591a --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/window/LengthWindowViewFactory.cs @@ -0,0 +1,89 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; + +namespace com.espertech.esper.view.window +{ + /// Factory for . + public class LengthWindowViewFactory + : DataWindowViewFactory + , DataWindowViewWithPrevious + { + private EventType _eventType; + + /// Size of length window. + private ExprEvaluator _sizeEvaluator; + + public void SetViewParameters(ViewFactoryContext viewFactoryContext, IList expressionParameters) + { + _sizeEvaluator = ViewFactorySupport.ValidateSizeSingleParam( + ViewName, viewFactoryContext, expressionParameters); + } + + public void Attach( + EventType parentEventType, + StatementContext statementContext, + ViewFactory optionalParentFactory, + IList parentViewFactories) + { + _eventType = parentEventType; + } + + public View MakeView(AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext) + { + int size = ViewFactorySupport.EvaluateSizeParam( + ViewName, _sizeEvaluator, agentInstanceViewFactoryContext.AgentInstanceContext); + ViewUpdatedCollection randomAccess = + agentInstanceViewFactoryContext.StatementContext.ViewServicePreviousFactory + .GetOptPreviousExprRandomAccess(agentInstanceViewFactoryContext); + if (agentInstanceViewFactoryContext.IsRemoveStream) + { + return new LengthWindowViewRStream(agentInstanceViewFactoryContext, this, size); + } + else + { + return new LengthWindowView(agentInstanceViewFactoryContext, this, size, randomAccess); + } + } + + public EventType EventType + { + get { return _eventType; } + } + + public bool CanReuse(View view, AgentInstanceContext agentInstanceContext) + { + if (!(view is LengthWindowView)) + { + return false; + } + + var myView = (LengthWindowView) view; + int size = ViewFactorySupport.EvaluateSizeParam(ViewName, _sizeEvaluator, agentInstanceContext); + return myView.Size == size && myView.IsEmpty(); + } + + public string ViewName + { + get { return "Length"; } + } + + public Object MakePreviousGetter() + { + return new RandomAccessByIndexGetter(); + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Core/NEsper.Core/view/window/LengthWindowViewRStream.cs b/NEsper.Core/NEsper.Core/view/window/LengthWindowViewRStream.cs new file mode 100755 index 000000000..6a1621322 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/window/LengthWindowViewRStream.cs @@ -0,0 +1,163 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.util; + +namespace com.espertech.esper.view.window +{ + /// + /// This view is a moving window extending the specified number of elements into the past, allowing + /// in addition to remove events efficiently for remove-stream events received by the view. + /// + public class LengthWindowViewRStream : ViewSupport, DataWindowView, CloneableView + { + private readonly AgentInstanceViewFactoryChainContext _agentInstanceViewFactoryContext; + private readonly LengthWindowViewFactory _lengthWindowViewFactory; + private readonly int _size; + private readonly LinkedHashSet _indexedEvents; + + /// + /// Constructor creates a moving window extending the specified number of elements into the past. + /// + /// The agent instance view factory context. + /// for copying this view in a group-by + /// is the specified number of elements into the past + public LengthWindowViewRStream(AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext, LengthWindowViewFactory lengthWindowViewFactory, int size) + { + if (size < 1) + { + throw new ArgumentException("Illegal argument for size of length window"); + } + + _agentInstanceViewFactoryContext = agentInstanceViewFactoryContext; + _lengthWindowViewFactory = lengthWindowViewFactory; + _size = size; + _indexedEvents = new LinkedHashSet(); + } + + public View CloneView() + { + return _lengthWindowViewFactory.MakeView(_agentInstanceViewFactoryContext); + } + + /// Returns true if the window is empty, or false if not empty. + /// true if empty + public bool IsEmpty() + { + return _indexedEvents.IsEmpty; + } + + /// Returns the size of the length window. + /// size of length window + public int Size + { + get { return _size; } + } + + public override EventType EventType + { + get + { + // The event type is the parent view's event type + return Parent.EventType; + } + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QViewProcessIRStream(this, _lengthWindowViewFactory.ViewName, newData, oldData);} + + EventBean[] expiredArr = null; + if (oldData != null) + { + foreach (EventBean anOldData in oldData) + { + _indexedEvents.Remove(anOldData); + InternalHandleRemoved(anOldData); + } + + expiredArr = oldData; + } + + // add data points to the window + // we don't care about removed data from a prior view + if (newData != null) + { + foreach (EventBean newEvent in newData) { + _indexedEvents.Add(newEvent); + InternalHandleAdded(newEvent); + } + } + + // Check for any events that get pushed out of the window + int expiredCount = _indexedEvents.Count - _size; + if (expiredCount > 0) + { + expiredArr = _indexedEvents.Take(expiredCount).ToArray(); + foreach (EventBean anExpired in expiredArr) + { + _indexedEvents.Remove(anExpired); + InternalHandleExpired(anExpired); + } + } + + // If there are child views, call Update method + if (HasViews) + { + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().QViewIndicate(this, _lengthWindowViewFactory.ViewName, newData, expiredArr);} + UpdateChildren(newData, expiredArr); + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AViewIndicate();} + } + + if (InstrumentationHelper.ENABLED) { InstrumentationHelper.Get().AViewProcessIRStream();} + } + + public void InternalHandleExpired(EventBean oldData) + { + // no action required + } + + public void InternalHandleRemoved(EventBean expiredData) + { + // no action required + } + + public void InternalHandleAdded(EventBean newData) + { + // no action required + } + + public override IEnumerator GetEnumerator() + { + return _indexedEvents.GetEnumerator(); + } + + public override String ToString() + { + return GetType().FullName + " size=" + _size; + } + + public void VisitView(ViewDataVisitor viewDataVisitor) + { + viewDataVisitor.VisitPrimary(_indexedEvents, true, _lengthWindowViewFactory.ViewName, null); + } + + public ViewFactory ViewFactory + { + get { return _lengthWindowViewFactory; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/window/RandomAccessByIndex.cs b/NEsper.Core/NEsper.Core/view/window/RandomAccessByIndex.cs new file mode 100755 index 000000000..d58c3f7fe --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/window/RandomAccessByIndex.cs @@ -0,0 +1,36 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; + +using com.espertech.esper.client; + +namespace com.espertech.esper.view.window +{ + /// Random access interface to insert stream and remove stream data based on an index. + public interface RandomAccessByIndex + { + /// Returns an new data event given an index. + /// to return new data for + /// new data event + EventBean GetNewData(int index); + + /// Returns an old data event given an index. + /// to return old data for + /// old data event + EventBean GetOldData(int index); + + EventBean GetNewDataTail(int index); + + IEnumerator GetWindowEnumerator(); + + ICollection WindowCollectionReadOnly { get; } + + int WindowCount { get; } + } +} diff --git a/NEsper.Core/NEsper.Core/view/window/RandomAccessByIndexGetter.cs b/NEsper.Core/NEsper.Core/view/window/RandomAccessByIndexGetter.cs new file mode 100755 index 000000000..02207efd6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/window/RandomAccessByIndexGetter.cs @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +namespace com.espertech.esper.view.window +{ + /// + /// Getter that provides an index at runtime. + /// + public class RandomAccessByIndexGetter : RandomAccessByIndexObserver + { + private RandomAccessByIndex _randomAccessByIndex; + + /// Returns the index for access. + /// index + public RandomAccessByIndex Accessor + { + get { return _randomAccessByIndex; } + } + + /// + /// Callback to indicate an Update + /// + /// is the collection + public void Updated(RandomAccessByIndex randomAccessByIndex) + { + _randomAccessByIndex = randomAccessByIndex; + } + } +} // End of namespace diff --git a/NEsper.Core/NEsper.Core/view/window/RandomAccessByIndexObserver.cs b/NEsper.Core/NEsper.Core/view/window/RandomAccessByIndexObserver.cs new file mode 100755 index 000000000..0dfcf5d27 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/window/RandomAccessByIndexObserver.cs @@ -0,0 +1,30 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace com.espertech.esper.view.window +{ + /// For indicating that the collection has been updated. + public interface RandomAccessByIndexObserver + { + /// Callback to indicate an Update + /// is the collection + void Updated(RandomAccessByIndex randomAccessByIndex); + } + + public class ProxyRandomAccessByIndexObserver : RandomAccessByIndexObserver + { + public Action UpdatedFunc { get; set; } + + public void Updated(RandomAccessByIndex randomAccessByIndex) + { + UpdatedFunc.Invoke(randomAccessByIndex); + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/window/RelativeAccessByEventNIndex.cs b/NEsper.Core/NEsper.Core/view/window/RelativeAccessByEventNIndex.cs new file mode 100755 index 000000000..99bd72ad8 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/window/RelativeAccessByEventNIndex.cs @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; + +namespace com.espertech.esper.view.window +{ + /// + /// Provides access to prior events given an event from which to count back, and an index to look at. + /// + public interface RelativeAccessByEventNIndex + { + /// + /// Returns the prior event to the given event counting back the number of events as supplied by index. + /// + /// + /// is the number of events to go back + /// event + EventBean GetRelativeToEvent(EventBean theEvent, int index); + + EventBean GetRelativeToEnd(int index); + + IEnumerator GetWindowToEvent(); + + ICollection GetWindowToEventCollReadOnly(); + + int GetWindowToEventCount(); + } +} diff --git a/NEsper.Core/NEsper.Core/view/window/RelativeAccessByEventNIndexGetter.cs b/NEsper.Core/NEsper.Core/view/window/RelativeAccessByEventNIndexGetter.cs new file mode 100755 index 000000000..6561dc140 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/window/RelativeAccessByEventNIndexGetter.cs @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using com.espertech.esper.client; + +namespace com.espertech.esper.view.window +{ + /// + /// Provides random-access into window contents by event and index as a combination. + /// + public interface RelativeAccessByEventNIndexGetter + { + /// + /// Returns the access into window contents given an event. + /// + /// to which the method returns relative access from + /// buffer + RelativeAccessByEventNIndex GetAccessor(EventBean theEvent); + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/view/window/RelativeAccessByEventNIndexGetterImpl.cs b/NEsper.Core/NEsper.Core/view/window/RelativeAccessByEventNIndexGetterImpl.cs new file mode 100755 index 000000000..04e007d67 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/window/RelativeAccessByEventNIndexGetterImpl.cs @@ -0,0 +1,70 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; + +namespace com.espertech.esper.view.window +{ + /// + /// Provides random-access into window contents by event and index as a combination. + /// + public class RelativeAccessByEventNIndexGetterImpl + : IStreamRelativeAccessUpdateObserver + , RelativeAccessByEventNIndexGetter + { + private readonly IDictionary _accessorByEvent = new Dictionary(); + private readonly IDictionary _eventsByAccessor = new Dictionary(); + + public void Updated(RelativeAccessByEventNIndex iStreamRelativeAccess, EventBean[] newData) + { + // remove data posted from the last update + EventBean[] lastNewData = _eventsByAccessor.Get(iStreamRelativeAccess); + if (lastNewData != null) + { + for (int i = 0; i < lastNewData.Length; i++) + { + _accessorByEvent.Remove(lastNewData[i]); + } + } + + if (newData == null) + { + return; + } + + // hold accessor per event for querying + for (int i = 0; i < newData.Length; i++) + { + _accessorByEvent.Put(newData[i], iStreamRelativeAccess); + } + + // save new data for access to later removal + _eventsByAccessor.Put(iStreamRelativeAccess, newData); + } + + /// + /// Returns the access into window contents given an event. + /// + /// to which the method returns relative access from + /// buffer + public RelativeAccessByEventNIndex GetAccessor(EventBean theEvent) + { + RelativeAccessByEventNIndex iStreamRelativeAccess = _accessorByEvent.Get(theEvent); + if (iStreamRelativeAccess == null) + { + return null; + } + return iStreamRelativeAccess; + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/view/window/TimeAccumView.cs b/NEsper.Core/NEsper.Core/view/window/TimeAccumView.cs new file mode 100755 index 000000000..3c9b10eca --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/window/TimeAccumView.cs @@ -0,0 +1,281 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.time; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.schedule; +using com.espertech.esper.util; + +namespace com.espertech.esper.view.window +{ + /// + /// A data window view that holds events in a stream and only removes events from a stream (rstream) if + /// no more events arrive for a given time interval. + /// + /// No batch version of the view exists as the batch version is simply the remove stream of this view, which removes + /// in batches. + /// + /// + /// The view is continuous, the insert stream consists of arriving events. The remove stream + /// only posts current window contents when no more events arrive for a given timer interval. + /// + /// + public class TimeAccumView + : ViewSupport + , CloneableView + , DataWindowView + , StoppableView + , StopCallback + { + private static readonly ILog Log = + LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly AgentInstanceViewFactoryChainContext _agentInstanceContext; + private readonly ExprTimePeriodEvalDeltaConst _timeDeltaComputation; + private readonly ViewUpdatedCollection _viewUpdatedCollection; + private readonly long _scheduleSlot; + // View parameters + private readonly TimeAccumViewFactory _factory; + // Current running parameters + private readonly List _currentBatch = new List(); + private long _callbackScheduledTime; + private readonly EPStatementHandleCallback _handle; + + /// + /// Constructor. + /// + /// is a collection that the view must update when receiving events + /// fr copying this view in a group-by + /// is required view services + /// delta computation + public TimeAccumView( + TimeAccumViewFactory timeBatchViewFactory, + AgentInstanceViewFactoryChainContext agentInstanceContext, + ExprTimePeriodEvalDeltaConst timeDeltaComputation, + ViewUpdatedCollection viewUpdatedCollection) + { + _agentInstanceContext = agentInstanceContext; + _factory = timeBatchViewFactory; + _timeDeltaComputation = timeDeltaComputation; + _viewUpdatedCollection = viewUpdatedCollection; + + _scheduleSlot = agentInstanceContext.StatementContext.ScheduleBucket.AllocateSlot(); + + var callback = new ProxyScheduleHandleCallback + { + ProcScheduledTrigger = extensionServicesContext => + { + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().QViewScheduledEval(this, _factory.ViewName); + } + SendRemoveStream(); + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().AViewScheduledEval(); + } + } + }; + _handle = new EPStatementHandleCallback(agentInstanceContext.EpStatementAgentInstanceHandle, callback); + agentInstanceContext.AddTerminationCallback(this); + } + + public View CloneView() + { + return _factory.MakeView(_agentInstanceContext); + } + + public ExprTimePeriodEvalDeltaConst TimeDeltaComputation + { + get { return _timeDeltaComputation; } + } + + public override EventType EventType + { + get { return Parent.EventType; } + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().QViewProcessIRStream(this, _factory.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, addSchedule a callback + bool removeSchedule = false; + bool addSchedule = false; + long timestamp = _agentInstanceContext.StatementContext.SchedulingService.Time; + + if (!_currentBatch.IsEmpty()) + { + // check if we need to reschedule + long callbackTime = timestamp + _timeDeltaComputation.DeltaAdd(timestamp); + if (callbackTime != _callbackScheduledTime) + { + removeSchedule = true; + addSchedule = true; + } + } + else + { + addSchedule = true; + } + + if (removeSchedule) + { + _agentInstanceContext.StatementContext.SchedulingService.Remove(_handle, _scheduleSlot); + } + if (addSchedule) + { + long timeIntervalSize = _timeDeltaComputation.DeltaAdd(timestamp); + _agentInstanceContext.StatementContext.SchedulingService.Add(timeIntervalSize, _handle, _scheduleSlot); + _callbackScheduledTime = timeIntervalSize + timestamp; + } + + // add data points to the window + foreach (EventBean newEvent in newData) + { + _currentBatch.Add(newEvent); + } + + // forward insert stream to child views + if (_viewUpdatedCollection != null) + { + _viewUpdatedCollection.Update(newData, null); + } + + // update child views + if (HasViews) + { + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().QViewIndicate(this, _factory.ViewName, newData, null); + } + UpdateChildren(newData, null); + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().AViewIndicate(); + } + } + + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().AViewProcessIRStream(); + } + } + + /// + /// This method sends the remove stream for all accumulated events. + /// + protected void SendRemoveStream() + { + _callbackScheduledTime = -1; + + // If there are child views and the batch was filled, fireStatementStopped update method + if (HasViews) + { + // Convert to object arrays + EventBean[] oldData = null; + if (!_currentBatch.IsEmpty()) + { + oldData = _currentBatch.ToArray(); + } + + // Post old data + if (_viewUpdatedCollection != null) + { + _viewUpdatedCollection.Update(null, oldData); + } + + if (oldData != null) + { + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().QViewIndicate(this, _factory.ViewName, null, oldData); + } + UpdateChildren(null, oldData); + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().AViewIndicate(); + } + } + } + + _currentBatch.Clear(); + } + + /// + /// Returns true if the window is empty, or false if not empty. + /// + /// true if empty + public bool IsEmpty + { + get { return _currentBatch.IsEmpty(); } + } + + public override IEnumerator GetEnumerator() + { + return _currentBatch.GetEnumerator(); + } + + public override String ToString() + { + return GetType().FullName; + } + + public void StopView() + { + StopSchedule(); + _agentInstanceContext.RemoveTerminationCallback(this); + } + + public void Stop() + { + StopSchedule(); + } + + public void StopSchedule() + { + if (_handle != null) + { + _agentInstanceContext.StatementContext.SchedulingService.Remove(_handle, _scheduleSlot); + } + } + + public void VisitView(ViewDataVisitor viewDataVisitor) + { + viewDataVisitor.VisitPrimary(_currentBatch, true, _factory.ViewName, null); + } + + public ViewFactory ViewFactory + { + get { return _factory; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/view/window/TimeAccumViewFactory.cs b/NEsper.Core/NEsper.Core/view/window/TimeAccumViewFactory.cs new file mode 100755 index 000000000..c65a48aae --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/window/TimeAccumViewFactory.cs @@ -0,0 +1,107 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.time; + +namespace com.espertech.esper.view.window +{ + /// + /// Factory for . + /// + public class TimeAccumViewFactory + : DataWindowViewFactory + , DataWindowViewWithPrevious + { + /// Number of msec of quiet time before results are flushed. + private ExprTimePeriodEvalDeltaConstFactory _timeDeltaComputationFactory; + + private EventType _eventType; + + public void SetViewParameters(ViewFactoryContext viewFactoryContext, IList expressionParameters) + { + if (expressionParameters.Count != 1) + { + throw new ViewParameterException(ViewParamMessage); + } + _timeDeltaComputationFactory = ViewFactoryTimePeriodHelper.ValidateAndEvaluateTimeDeltaFactory( + ViewName, viewFactoryContext.StatementContext, expressionParameters[0], ViewParamMessage, 0); + } + + public void Attach( + EventType parentEventType, + StatementContext statementContext, + ViewFactory optionalParentFactory, + IList parentViewFactories) + { + _eventType = parentEventType; + } + + public Object MakePreviousGetter() + { + return new RandomAccessByIndexGetter(); + } + + public View MakeView(AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext) + { + ExprTimePeriodEvalDeltaConst timeDeltaComputation = _timeDeltaComputationFactory.Make( + ViewName, "view", agentInstanceViewFactoryContext.AgentInstanceContext); + ViewUpdatedCollection randomAccess = + agentInstanceViewFactoryContext.StatementContext.ViewServicePreviousFactory + .GetOptPreviousExprRandomAccess(agentInstanceViewFactoryContext); + if (agentInstanceViewFactoryContext.IsRemoveStream) + { + return new TimeAccumViewRStream(this, agentInstanceViewFactoryContext, timeDeltaComputation); + } + else + { + return new TimeAccumView(this, agentInstanceViewFactoryContext, timeDeltaComputation, randomAccess); + } + } + + public EventType EventType + { + get { return _eventType; } + } + + public bool CanReuse(View view, AgentInstanceContext agentInstanceContext) + { + if (!(view is TimeAccumView)) + { + return false; + } + + var myView = (TimeAccumView) view; + ExprTimePeriodEvalDeltaConst timeDeltaComputation = _timeDeltaComputationFactory.Make( + ViewName, "view", agentInstanceContext); + if (!timeDeltaComputation.EqualsTimePeriod(myView.TimeDeltaComputation)) + { + return false; + } + + return myView.IsEmpty; + } + + public string ViewName + { + get { return "TimeInMillis-Accumulative-Batch"; } + } + + private string ViewParamMessage + { + get { return ViewName + " view requires a single numeric parameter or time period parameter"; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/view/window/TimeAccumViewRStream.cs b/NEsper.Core/NEsper.Core/view/window/TimeAccumViewRStream.cs new file mode 100755 index 000000000..654cd9090 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/window/TimeAccumViewRStream.cs @@ -0,0 +1,308 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.time; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.schedule; +using com.espertech.esper.util; + +namespace com.espertech.esper.view.window +{ + /// + /// A data window view that holds events in a stream and only removes events from a stream (rstream) if + /// no more events arrive for a given time interval, also handling the remove stream + /// by keeping set-like semantics. See for the same behavior without + /// remove stream handling. + /// + public class TimeAccumViewRStream + : ViewSupport, + CloneableView, + DataWindowView, + StoppableView, + StopCallback + { + private readonly AgentInstanceViewFactoryChainContext _agentInstanceContext; + private readonly ExprTimePeriodEvalDeltaConst _timeDeltaComputation; + private readonly long _scheduleSlot; + // View parameters + private readonly TimeAccumViewFactory _factory; + // Current running parameters + private readonly Dictionary _currentBatch = new Dictionary(); + private EventBean _lastEvent; + private long _callbackScheduledTime; + private readonly EPStatementHandleCallback _handle; + + /// + /// Constructor. + /// + /// for copying this view in a group-by + /// context + /// time delta eval + public TimeAccumViewRStream( + TimeAccumViewFactory timeBatchViewFactory, + AgentInstanceViewFactoryChainContext agentInstanceContext, + ExprTimePeriodEvalDeltaConst timeDeltaComputation) + { + _agentInstanceContext = agentInstanceContext; + _factory = timeBatchViewFactory; + _timeDeltaComputation = timeDeltaComputation; + + _scheduleSlot = agentInstanceContext.StatementContext.ScheduleBucket.AllocateSlot(); + + var callback = new ProxyScheduleHandleCallback + { + ProcScheduledTrigger = extensionServicesContext => + { + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().QViewScheduledEval(this, _factory.ViewName); + } + SendRemoveStream(); + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().AViewScheduledEval(); + } + } + }; + _handle = new EPStatementHandleCallback(agentInstanceContext.EpStatementAgentInstanceHandle, callback); + agentInstanceContext.AddTerminationCallback(this); + } + + public View CloneView() + { + return _factory.MakeView(_agentInstanceContext); + } + + public ExprTimePeriodEvalDeltaConst TimeDeltaComputation + { + get { return _timeDeltaComputation; } + } + + public override EventType EventType + { + get { return Parent.EventType; } + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().QViewProcessIRStream(this, _factory.ViewName, newData, oldData); + } + + if ((newData != null) && (newData.Length > 0)) + { + // If we have an empty window about to be filled for the first time, add a callback + bool removeSchedule = false; + bool addSchedule = false; + long timestamp = _agentInstanceContext.StatementContext.SchedulingService.Time; + + // if the window is already filled, then we may need to reschedule + if (!_currentBatch.IsEmpty()) + { + // check if we need to reschedule + long callbackTime = timestamp + _timeDeltaComputation.DeltaAdd(timestamp); + if (callbackTime != _callbackScheduledTime) + { + removeSchedule = true; + addSchedule = true; + } + } + else + { + addSchedule = true; + } + + if (removeSchedule) + { + _agentInstanceContext.StatementContext.SchedulingService.Remove(_handle, _scheduleSlot); + _callbackScheduledTime = -1; + } + if (addSchedule) + { + long timeIntervalSize = _timeDeltaComputation.DeltaAdd(timestamp); + _agentInstanceContext.StatementContext.SchedulingService.Add( + timeIntervalSize, _handle, _scheduleSlot); + _callbackScheduledTime = timeIntervalSize + timestamp; + } + + // add data points to the window + for (int i = 0; i < newData.Length; i++) + { + _currentBatch.Put(newData[i], timestamp); + InternalHandleAdded(newData[i], timestamp); + _lastEvent = newData[i]; + } + } + + if ((oldData != null) && (oldData.Length > 0)) + { + bool removedLastEvent = false; + foreach (EventBean anOldData in oldData) + { + _currentBatch.Remove(anOldData); + InternalHandleRemoved(anOldData); + if (anOldData == _lastEvent) + { + removedLastEvent = true; + } + } + + // we may need to reschedule as the newest event may have been deleted + if (_currentBatch.Count == 0) + { + _agentInstanceContext.StatementContext.SchedulingService.Remove(_handle, _scheduleSlot); + _callbackScheduledTime = -1; + _lastEvent = null; + } + else + { + // reschedule if the last event was removed + if (removedLastEvent) + { + var keyset = _currentBatch.Keys; + var events = keyset.ToArray(); + _lastEvent = events[events.Length - 1]; + long lastTimestamp = _currentBatch.Get(_lastEvent); + + // reschedule, newest event deleted + long timestamp = _agentInstanceContext.StatementContext.SchedulingService.Time; + long callbackTime = lastTimestamp + _timeDeltaComputation.DeltaAdd(lastTimestamp); + long deltaFromNow = callbackTime - timestamp; + if (callbackTime != _callbackScheduledTime) + { + _agentInstanceContext.StatementContext.SchedulingService.Remove(_handle, _scheduleSlot); + _agentInstanceContext.StatementContext.SchedulingService.Add( + deltaFromNow, _handle, _scheduleSlot); + _callbackScheduledTime = callbackTime; + } + } + } + } + + // update child views + if (HasViews) + { + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().QViewIndicate(this, _factory.ViewName, newData, oldData); + } + UpdateChildren(newData, oldData); + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().AViewIndicate(); + } + } + + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().AViewProcessIRStream(); + } + } + + public void VisitView(ViewDataVisitor viewDataVisitor) + { + viewDataVisitor.VisitPrimary(_currentBatch, true, _factory.ViewName, _currentBatch.Count, null); + } + + /// + /// This method sends the remove stream for all accumulated events. + /// + protected void SendRemoveStream() + { + _callbackScheduledTime = -1; + + // If there are child views and the batch was filled, fireStatementStopped update method + if (HasViews) + { + // Convert to object arrays + EventBean[] oldData = null; + if (!_currentBatch.IsEmpty()) + { + oldData = _currentBatch.Keys.ToArray(); + } + + if (oldData != null) + { + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().QViewIndicate(this, _factory.ViewName, null, oldData); + } + UpdateChildren(null, oldData); + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().AViewIndicate(); + } + } + } + + _currentBatch.Clear(); + } + + /// + /// Returns true if the window is empty, or false if not empty. + /// + /// true if empty + public bool IsEmpty() + { + return _currentBatch.IsEmpty(); + } + + public override IEnumerator GetEnumerator() + { + return _currentBatch.Keys.GetEnumerator(); + } + + public override String ToString() + { + return GetType().FullName; + } + + public void StopView() + { + StopSchedule(); + _agentInstanceContext.RemoveTerminationCallback(this); + } + + public void Stop() + { + StopSchedule(); + } + + public void StopSchedule() + { + if (_handle != null) + { + _agentInstanceContext.StatementContext.SchedulingService.Remove(_handle, _scheduleSlot); + } + } + + public void InternalHandleRemoved(EventBean anOldData) + { + // no action required + } + + public void InternalHandleAdded(EventBean eventBean, long timestamp) + { + // no action required + } + + public ViewFactory ViewFactory + { + get { return _factory; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/view/window/TimeBatchView.cs b/NEsper.Core/NEsper.Core/view/window/TimeBatchView.cs new file mode 100755 index 000000000..03876021e --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/window/TimeBatchView.cs @@ -0,0 +1,349 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.time; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.schedule; +using com.espertech.esper.util; + +namespace com.espertech.esper.view.window +{ + /// + /// A data view that aggregates events in a stream and releases them in one batch at every specified time interval. + /// The view works similar to a time_window but in not continuous. + /// The view releases the batched events after the interval as new data to child views. The prior batch if + /// not empty is released as old data to child view. The view doesn't release intervals with no old or new data. + /// It also does not collect old data published by a parent view. + /// + /// For example, we want to calculate the average of IBM stock every hour, for the last hour. + /// The view accepts 2 parameter combinations. + /// (1) A time interval is supplied with a reference point - based on this point the intervals are set. + /// (1) A time interval is supplied but no reference point - the reference point is set when the first event arrives. + /// + /// + /// If there are no events in the current and prior batch, the view will not invoke the update method of child views. + /// In that case also, no next callback is scheduled with the scheduling service until the next event arrives. + /// + /// + public class TimeBatchView + : ViewSupport + , CloneableView + , StoppableView + , StopCallback + , DataWindowView + { + private static readonly ILog Log = + LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly AgentInstanceViewFactoryChainContext _agentInstanceContext; + private readonly ExprTimePeriodEvalDeltaConst _timeDeltaComputation; + private readonly long? _initialReferencePoint; + private readonly bool _isForceOutput; + private readonly bool _isStartEager; + private readonly ViewUpdatedCollection _viewUpdatedCollection; + private readonly long _scheduleSlot; + // View parameters + private readonly TimeBatchViewFactory _timeBatchViewFactory; + private EPStatementHandleCallback _handle; + // Current running parameters + private long? _currentReferencePoint; + private ArrayDeque _lastBatch = null; + private ArrayDeque _currentBatch = new ArrayDeque(); + private bool _isCallbackScheduled; + + /// + /// Constructor. + /// + /// is the number of milliseconds to batch events for + /// + /// is the reference point onto which to base intervals, or null if + /// there is no such reference point supplied + /// + /// is a collection that the view must update when receiving events + /// for copying this view in a group-by + /// is true if the batch should produce empty output if there is no value to output following time intervals + /// is true for start-eager + /// context + public TimeBatchView( + TimeBatchViewFactory timeBatchViewFactory, + AgentInstanceViewFactoryChainContext agentInstanceContext, + ExprTimePeriodEvalDeltaConst timeDeltaComputation, + long? referencePoint, + bool forceOutput, + bool isStartEager, + ViewUpdatedCollection viewUpdatedCollection) + { + _agentInstanceContext = agentInstanceContext; + _timeBatchViewFactory = timeBatchViewFactory; + _timeDeltaComputation = timeDeltaComputation; + _initialReferencePoint = referencePoint; + _isStartEager = isStartEager; + _viewUpdatedCollection = viewUpdatedCollection; + _isForceOutput = forceOutput; + + _scheduleSlot = agentInstanceContext.StatementContext.ScheduleBucket.AllocateSlot(); + + // schedule the first callback + if (isStartEager) + { + if (_currentReferencePoint == null) + { + _currentReferencePoint = agentInstanceContext.StatementContext.SchedulingService.Time; + } + ScheduleCallback(); + _isCallbackScheduled = true; + } + + agentInstanceContext.AddTerminationCallback(this); + } + + public View CloneView() + { + return _timeBatchViewFactory.MakeView(_agentInstanceContext); + } + + public ExprTimePeriodEvalDeltaConst TimeDeltaComputation + { + get { return _timeDeltaComputation; } + } + + /// + /// Gets the reference point to use to anchor interval start and end dates to. + /// + /// is the millisecond reference point. + public long? InitialReferencePoint + { + get { return _initialReferencePoint; } + } + + /// + /// True for force-output. + /// + /// indicates force-output + public bool IsForceOutput + { + get { return _isForceOutput; } + } + + /// + /// True for start-eager. + /// + /// indicates start-eager + public bool IsStartEager + { + get { return _isStartEager; } + } + + public override EventType EventType + { + get { return Parent.EventType; } + } + + 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(); + } + } + + /// + /// 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. + /// + 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(); + } + + /// + /// Returns true if the window is empty, or false if not empty. + /// + /// true if empty + public bool IsEmpty() + { + if (_lastBatch != null) + { + if (!_lastBatch.IsEmpty()) + { + return false; + } + } + return _currentBatch.IsEmpty(); + } + + public override IEnumerator GetEnumerator() + { + return _currentBatch.GetEnumerator(); + } + + public override String ToString() + { + return GetType().FullName + + " initialReferencePoint=" + _initialReferencePoint; + } + + public void VisitView(ViewDataVisitor viewDataVisitor) + { + viewDataVisitor.VisitPrimary(_currentBatch, true, _timeBatchViewFactory.ViewName, null); + viewDataVisitor.VisitPrimary(_lastBatch, true, _timeBatchViewFactory.ViewName, null); + } + + protected void ScheduleCallback() + { + long current = _agentInstanceContext.StatementContext.SchedulingService.Time; + ExprTimePeriodEvalDeltaResult deltaWReference = _timeDeltaComputation.DeltaAddWReference( + current, _currentReferencePoint.Value); + long afterTime = deltaWReference.Delta; + _currentReferencePoint = deltaWReference.LastReference; + + var callback = new ProxyScheduleHandleCallback() + { + ProcScheduledTrigger = (extensionServicesContext) => + { + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().QViewScheduledEval(this, _timeBatchViewFactory.ViewName); + } + SendBatch(); + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().AViewScheduledEval(); + } + } + }; + _handle = new EPStatementHandleCallback(_agentInstanceContext.EpStatementAgentInstanceHandle, callback); + _agentInstanceContext.StatementContext.SchedulingService.Add(afterTime, _handle, _scheduleSlot); + } + + public void StopView() + { + StopSchedule(); + _agentInstanceContext.RemoveTerminationCallback(this); + } + + public void Stop() + { + StopSchedule(); + } + + public void StopSchedule() + { + if (_handle != null) + { + _agentInstanceContext.StatementContext.SchedulingService.Remove(_handle, _scheduleSlot); + } + } + + public ViewFactory ViewFactory + { + get { return _timeBatchViewFactory; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/view/window/TimeBatchViewFactory.cs b/NEsper.Core/NEsper.Core/view/window/TimeBatchViewFactory.cs new file mode 100755 index 000000000..045cccbd0 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/window/TimeBatchViewFactory.cs @@ -0,0 +1,170 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.time; +using com.espertech.esper.util; + +namespace com.espertech.esper.view.window +{ + /// + /// Factory for . + /// + public class TimeBatchViewFactory + : TimeBatchViewFactoryParams + , DataWindowViewFactory + , DataWindowViewWithPrevious + , DataWindowBatchingViewFactory + { + /// The reference point, or null if none supplied. + private long? _optionalReferencePoint; + + public void SetViewParameters(ViewFactoryContext viewFactoryContext, IList expressionParameters) + { + if ((expressionParameters.Count < 1) || (expressionParameters.Count > 3)) + { + throw new ViewParameterException(ViewParamMessage); + } + var viewParamValues = new Object[expressionParameters.Count]; + for (int i = 1; i < viewParamValues.Length; i++) + { + viewParamValues[i] = ViewFactorySupport.ValidateAndEvaluate( + ViewName, viewFactoryContext.StatementContext, expressionParameters[i]); + } + + timeDeltaComputationFactory = ViewFactoryTimePeriodHelper.ValidateAndEvaluateTimeDeltaFactory( + ViewName, viewFactoryContext.StatementContext, expressionParameters[0], ViewParamMessage, 0); + + if ((viewParamValues.Length == 2) && (viewParamValues[1] is string)) + { + ProcessKeywords(viewParamValues[1], ViewParamMessage); + } + else + { + if (viewParamValues.Length >= 2) + { + var paramRef = viewParamValues[1]; + if ((!(paramRef.IsNumber())) || (paramRef.IsFloatingPointNumber())) + { + throw new ViewParameterException( + ViewName + " view requires a long-typed reference point in msec as a second parameter"); + } + _optionalReferencePoint = paramRef.AsLong(); + } + if (viewParamValues.Length == 3) + { + ProcessKeywords(viewParamValues[2], ViewParamMessage); + } + } + } + + public void Attach( + EventType parentEventType, + StatementContext statementContext, + ViewFactory optionalParentFactory, + IList parentViewFactories) + { + eventType = parentEventType; + } + + public Object MakePreviousGetter() + { + return new RelativeAccessByEventNIndexGetterImpl(); + } + + public View MakeView(AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext) + { + ExprTimePeriodEvalDeltaConst timeDeltaComputation = timeDeltaComputationFactory.Make( + ViewName, "view", agentInstanceViewFactoryContext.AgentInstanceContext); + ViewUpdatedCollection viewUpdatedCollection = + agentInstanceViewFactoryContext.StatementContext.ViewServicePreviousFactory + .GetOptPreviousExprRelativeAccess(agentInstanceViewFactoryContext); + if (agentInstanceViewFactoryContext.IsRemoveStream) + { + return new TimeBatchViewRStream( + this, agentInstanceViewFactoryContext, timeDeltaComputation, _optionalReferencePoint, IsForceUpdate, + IsStartEager); + } + else + { + return new TimeBatchView( + this, agentInstanceViewFactoryContext, timeDeltaComputation, _optionalReferencePoint, IsForceUpdate, + IsStartEager, viewUpdatedCollection); + } + } + + public EventType EventType + { + get { return eventType; } + } + + public bool CanReuse(View view, AgentInstanceContext agentInstanceContext) + { + if (!(view is TimeBatchView)) + { + return false; + } + + TimeBatchView myView = (TimeBatchView) view; + ExprTimePeriodEvalDeltaConst timeDeltaComputation = timeDeltaComputationFactory.Make( + ViewName, "view", agentInstanceContext); + if (!timeDeltaComputation.EqualsTimePeriod(myView.TimeDeltaComputation)) + { + return false; + } + + if ((myView.InitialReferencePoint != null) && (_optionalReferencePoint != null)) + { + if (!myView.InitialReferencePoint.Equals(_optionalReferencePoint.Value)) + { + return false; + } + } + if (((myView.InitialReferencePoint == null) && (_optionalReferencePoint != null)) || + ((myView.InitialReferencePoint != null) && (_optionalReferencePoint == null))) + { + return false; + } + + if (myView.IsForceOutput != IsForceUpdate) + { + return false; + } + + if (myView.IsStartEager) + { + // since it's already started + return false; + } + + return myView.IsEmpty(); + } + + public string ViewName + { + get { return "TimeInMillis-Batch"; } + } + + private string ViewParamMessage + { + get + { + return ViewName + + " view requires a single numeric or time period parameter, and an optional long-typed reference point in msec, and an optional list of control keywords as a string parameter (please see the documentation)"; + } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/view/window/TimeBatchViewFactoryParams.cs b/NEsper.Core/NEsper.Core/view/window/TimeBatchViewFactoryParams.cs new file mode 100755 index 000000000..bb0e466d6 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/window/TimeBatchViewFactoryParams.cs @@ -0,0 +1,85 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.epl.expression.time; +using com.espertech.esper.view; + +namespace com.espertech.esper.view.window +{ + /// + /// Parameters for batch views that provides common data flow parameter parsing. + /// + public class TimeBatchViewFactoryParams + { + /// Keyword for force update, i.e. update if no data. + internal static readonly string FORCE_UPDATE_KEYWORD = "force_update"; + + /// Keyword for starting eager, i.e. start early. + internal static readonly string START_EAGER_KEYWORD = "start_eager"; + + /// Event type + internal EventType eventType; + + /// + /// Number of msec before batch fires (either interval or number of events). + /// + internal ExprTimePeriodEvalDeltaConstFactory timeDeltaComputationFactory; + + /// + /// Convert keywords into isForceUpdate and isStartEager members + /// + /// flow control keyword string expression + /// error message + /// if parsing failed + protected void ProcessKeywords(Object keywords, string errorMessage) + { + + if (!(keywords is string)) + { + throw new ViewParameterException(errorMessage); + } + + string[] keyword = ((string) keywords).SplitCsv(); + for (int i = 0; i < keyword.Length; i++) + { + string keywordText = keyword[i].ToLowerInvariant().Trim(); + if (keywordText.Length == 0) + { + continue; + } + if (keywordText.Equals(FORCE_UPDATE_KEYWORD)) + { + IsForceUpdate = true; + } + else if (keywordText.Equals(START_EAGER_KEYWORD)) + { + IsForceUpdate = true; + IsStartEager = true; + } + else + { + string keywordRange = FORCE_UPDATE_KEYWORD + "," + START_EAGER_KEYWORD; + throw new ViewParameterException( + "TimeInMillis-length-combination view encountered an invalid keyword '" + keywordText + + "', valid control keywords are: " + keywordRange); + } + } + } + + public bool IsForceUpdate { get; protected set; } + + public bool IsStartEager { get; protected set; } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/view/window/TimeBatchViewRStream.cs b/NEsper.Core/NEsper.Core/view/window/TimeBatchViewRStream.cs new file mode 100755 index 000000000..0dced904c --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/window/TimeBatchViewRStream.cs @@ -0,0 +1,329 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.time; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.schedule; +using com.espertech.esper.util; + +namespace com.espertech.esper.view.window +{ + /// + /// Same as the , this view also supports fast-remove from the batch for remove stream events. + /// + public class TimeBatchViewRStream + : ViewSupport + , CloneableView + , StoppableView + , StopCallback + , DataWindowView + { + private readonly AgentInstanceViewFactoryChainContext _agentInstanceContext; + private readonly ExprTimePeriodEvalDeltaConst _timeDeltaComputation; + private readonly long _scheduleSlot; + // View parameters + private readonly TimeBatchViewFactory _timeBatchViewFactory; + private readonly long? _initialReferencePoint; + private readonly bool _isForceOutput; + private readonly bool _isStartEager; + private EPStatementHandleCallback _handle; + + // Current running parameters + private long? _currentReferencePoint; + private LinkedHashSet _lastBatch = null; + private LinkedHashSet _currentBatch = new LinkedHashSet(); + private bool _isCallbackScheduled; + + /// + /// Constructor. + /// + /// is the number of milliseconds to batch events for + /// + /// is the reference point onto which to base intervals, or null if + /// there is no such reference point supplied + /// + /// fr copying this view in a group-by + /// is true if the batch should produce empty output if there is no value to output following time intervals + /// is true for start-eager + /// context + public TimeBatchViewRStream( + TimeBatchViewFactory timeBatchViewFactory, + AgentInstanceViewFactoryChainContext agentInstanceContext, + ExprTimePeriodEvalDeltaConst timeDeltaComputation, + long? referencePoint, + bool forceOutput, + bool isStartEager) + { + _agentInstanceContext = agentInstanceContext; + _timeBatchViewFactory = timeBatchViewFactory; + _timeDeltaComputation = timeDeltaComputation; + _initialReferencePoint = referencePoint; + _isStartEager = isStartEager; + _isForceOutput = forceOutput; + + _scheduleSlot = agentInstanceContext.StatementContext.ScheduleBucket.AllocateSlot(); + + // schedule the first callback + if (_isStartEager) + { + _currentReferencePoint = agentInstanceContext.StatementContext.SchedulingService.Time; + ScheduleCallback(); + _isCallbackScheduled = true; + } + agentInstanceContext.AddTerminationCallback(this); + } + + public View CloneView() + { + return _timeBatchViewFactory.MakeView(_agentInstanceContext); + } + + public ExprTimePeriodEvalDeltaConst TimeDeltaComputation + { + get { return _timeDeltaComputation; } + } + + /// + /// Gets the reference point to use to anchor interval start and end dates to. + /// + /// is the millisecond reference point. + public long? InitialReferencePoint + { + get { return _initialReferencePoint; } + } + + /// + /// True for force-output. + /// + /// indicates force-output + public bool IsForceOutput + { + get { return _isForceOutput; } + } + + /// + /// True for start-eager. + /// + /// indicates start-eager + public bool IsStartEager + { + get { return _isStartEager; } + } + + public override EventType EventType + { + get { return Parent.EventType; } + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().QViewProcessIRStream(this, _timeBatchViewFactory.ViewName, newData, oldData); + } + + if (oldData != null) + { + for (int i = 0; i < oldData.Length; i++) + { + _currentBatch.Remove(oldData[i]); + InternalHandleRemoved(oldData[i]); + } + } + + // 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(); + } + } + + /// + /// 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. + /// + protected 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(); + } + + 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 LinkedHashSet(); + } + + /// + /// Returns true if the window is empty, or false if not empty. + /// + /// true if empty + public bool IsEmpty() + { + if (_lastBatch != null) + { + if (!_lastBatch.IsEmpty()) + { + return false; + } + } + return _currentBatch.IsEmpty(); + } + + public override IEnumerator GetEnumerator() + { + return _currentBatch.GetEnumerator(); + } + + public override String ToString() + { + return GetType().FullName + + " initialReferencePoint=" + _initialReferencePoint; + } + + protected void ScheduleCallback() + { + long current = _agentInstanceContext.StatementContext.SchedulingService.Time; + ExprTimePeriodEvalDeltaResult deltaWReference = _timeDeltaComputation.DeltaAddWReference(current, _currentReferencePoint.Value); + long afterTime = deltaWReference.Delta; + _currentReferencePoint = deltaWReference.LastReference; + + var callback = new ProxyScheduleHandleCallback() + { + ProcScheduledTrigger = (extensionServicesContext) => + { + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().QViewScheduledEval(this, _timeBatchViewFactory.ViewName); + } + SendBatch(); + if (InstrumentationHelper.ENABLED) + { + InstrumentationHelper.Get().AViewScheduledEval(); + } + } + }; + _handle = new EPStatementHandleCallback(_agentInstanceContext.EpStatementAgentInstanceHandle, callback); + _agentInstanceContext.StatementContext.SchedulingService.Add(afterTime, _handle, _scheduleSlot); + } + + public void StopView() + { + StopSchedule(); + _agentInstanceContext.RemoveTerminationCallback(this); + } + + public void Stop() + { + StopSchedule(); + } + + public void StopSchedule() + { + if (_handle != null) + { + _agentInstanceContext.StatementContext.SchedulingService.Remove(_handle, _scheduleSlot); + } + } + + public void InternalHandleRemoved(EventBean eventBean) + { + // no action required + } + + public void VisitView(ViewDataVisitor viewDataVisitor) + { + viewDataVisitor.VisitPrimary(_currentBatch, true, _timeBatchViewFactory.ViewName, null); + viewDataVisitor.VisitPrimary(_lastBatch, true, _timeBatchViewFactory.ViewName, null); + } + + public ViewFactory ViewFactory + { + get { return _timeBatchViewFactory; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/view/window/TimeLengthBatchView.cs b/NEsper.Core/NEsper.Core/view/window/TimeLengthBatchView.cs new file mode 100755 index 000000000..a8a894a54 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/window/TimeLengthBatchView.cs @@ -0,0 +1,329 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.time; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.schedule; +using com.espertech.esper.util; + +namespace com.espertech.esper.view.window +{ + /// + /// A data view that aggregates events in a stream and releases them in one batch if either + /// one of these conditions is reached, whichever comes first: One, a time interval passes. + /// Two, a given number of events collected. The view releases the batched events after + /// the interval or number of events as new data to child views. The prior batch if not empty + /// is released as old data to child view. The view DOES release intervals with no old or new + /// data. It does not collect old data published by a parent view. If there are no events in + /// the current and prior batch, the view WILL invoke the Update method of child views. + /// + /// The view starts the first interval when the view is created. + /// + public class TimeLengthBatchView + : ViewSupport + , CloneableView + , StoppableView + , DataWindowView + , StopCallback + { + // View parameters + private readonly TimeLengthBatchViewFactory _timeLengthBatchViewFactory; + private readonly AgentInstanceViewFactoryChainContext _agentInstanceContext; + private readonly ExprTimePeriodEvalDeltaConst _timeDeltaComputation; + private readonly long _numberOfEvents; + private readonly bool _isForceOutput; + private readonly bool _isStartEager; + private readonly ViewUpdatedCollection _viewUpdatedCollection; + private readonly long _scheduleSlot; + + // Current running parameters + private List _lastBatch = null; + private List _currentBatch = new List(); + private long? _callbackScheduledTime; + private EPStatementHandleCallback _handle; + + /// + /// Constructor. + /// + /// for copying this view in a group-by + /// The agent instance context. + /// is the number of milliseconds to batch events for + /// is the event count before the batch fires off + /// is true if the batch should produce empty output if there is no value to output following time intervals + /// is true for start-eager + /// is a collection that the view must Update when receiving events + public TimeLengthBatchView( + TimeLengthBatchViewFactory timeBatchViewFactory, + AgentInstanceViewFactoryChainContext agentInstanceContext, + ExprTimePeriodEvalDeltaConst timeDeltaComputation, + long numberOfEvents, + bool forceOutput, + bool isStartEager, + ViewUpdatedCollection viewUpdatedCollection) + { + _agentInstanceContext = agentInstanceContext; + _timeLengthBatchViewFactory = timeBatchViewFactory; + _timeDeltaComputation = timeDeltaComputation; + _numberOfEvents = numberOfEvents; + _isStartEager = isStartEager; + _viewUpdatedCollection = viewUpdatedCollection; + _isForceOutput = forceOutput; + + _scheduleSlot = agentInstanceContext.StatementContext.ScheduleBucket.AllocateSlot(); + + // schedule the first callback + if (isStartEager) + { + ScheduleCallback(0); + } + + agentInstanceContext.AddTerminationCallback(Stop); + } + + public View CloneView() + { + return _timeLengthBatchViewFactory.MakeView(_agentInstanceContext); + } + + /// Returns the interval size in milliseconds. + /// batch size + public ExprTimePeriodEvalDeltaConst TimeDeltaComputation + { + get { return _timeDeltaComputation; } + } + + /// True for force-output. + /// indicates force-output + public bool IsForceOutput + { + get { return _isForceOutput; } + } + + /// Returns the length of the batch. + /// maximum number of events allowed before window gets flushed + public long NumberOfEvents + { + get { return _numberOfEvents; } + } + + /// True for start-eager. + /// indicates start-eager + public bool IsStartEager + { + get { return _isStartEager; } + } + + public override EventType EventType + { + get { return Parent.EventType; } + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + using (Instrument.With( + i => i.QViewProcessIRStream(this, _timeLengthBatchViewFactory.ViewName, newData, oldData), + i => i.AViewProcessIRStream())) + { + if (oldData != null) + { + for (int i = 0; i < oldData.Length; i++) + { + if (_currentBatch.Remove(oldData[i])) + { + InternalHandleRemoved(oldData[i]); + } + } + } + + // we don't care about removed data from a prior view + if ((newData == null) || (newData.Length == 0)) + { + return; + } + + // Add data points + foreach (EventBean newEvent in newData) + { + _currentBatch.Add(newEvent); + InternalHandleAdded(newEvent); + } + + // We are done unless we went over the boundary + if (_currentBatch.Count < _numberOfEvents) + { + // Schedule a callback if there is none scheduled + if (_callbackScheduledTime == null) + { + ScheduleCallback(0); + } + + return; + } + + // send a batch of events + SendBatch(false); + } + } + + public void InternalHandleAdded(EventBean newEvent) + { + // no action required + } + + public void InternalHandleRemoved(EventBean eventBean) + { + // no action required + } + + /// + /// This method updates child views and clears the batch of events. We cancel and + /// old callback and schedule a new callback at this time if there were events in + /// the batch. + /// + /// true if invoked from a schedule, false if not + protected void SendBatch(bool isFromSchedule) + { + // No more callbacks scheduled if called from a schedule + if (isFromSchedule) + { + _callbackScheduledTime = null; + } + else + { + // Remove schedule if called from on overflow due to number of events + if (_callbackScheduledTime != null) + { + _agentInstanceContext.StatementContext.SchedulingService.Remove(_handle, _scheduleSlot); + _callbackScheduledTime = null; + } + } + + // 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.IsNotEmpty()) + { + newData = _currentBatch.ToArray(); + } + if ((_lastBatch != null) && (_lastBatch.IsNotEmpty())) + { + 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)) + { + using (Instrument.With( + i => i.QViewIndicate(this, _timeLengthBatchViewFactory.ViewName, newData, oldData), + i => i.AViewIndicate())) + { + UpdateChildren(newData, oldData); + } + } + } + + // Only if 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.IsNotEmpty()) || ((_lastBatch != null) && (_lastBatch.IsNotEmpty()))) || (_isForceOutput)) + { + ScheduleCallback(0); + } + + // Flush and roll + _lastBatch = _currentBatch; + _currentBatch = new List(); + } + + /// Returns true if the window is empty, or false if not empty. + /// true if empty + public bool IsEmpty() + { + if (_lastBatch != null) + { + if (_lastBatch.IsNotEmpty()) + { + return false; + } + } + return _currentBatch.IsEmpty(); + } + + public override IEnumerator GetEnumerator() + { + return _currentBatch.GetEnumerator(); + } + + public override String ToString() + { + return GetType().FullName + + " numberOfEvents=" + _numberOfEvents; + } + + protected void ScheduleCallback(long delta) + { + ScheduleHandleCallback callback = new ProxyScheduleHandleCallback + { + ProcScheduledTrigger = extensionServicesContext => Instrument.With( + i => i.QViewScheduledEval(this, _timeLengthBatchViewFactory.ViewName), + i => i.AViewScheduledEval(), + () => SendBatch(true)) + }; + _handle = new EPStatementHandleCallback(_agentInstanceContext.EpStatementAgentInstanceHandle, callback); + var currentTime = _agentInstanceContext.StatementContext.SchedulingService.Time; + var scheduled = _timeDeltaComputation.DeltaAdd(currentTime) - delta; + _agentInstanceContext.StatementContext.SchedulingService.Add(scheduled, _handle, _scheduleSlot); + _callbackScheduledTime = _agentInstanceContext.StatementContext.SchedulingService.Time + scheduled; + } + + public void StopView() + { + StopSchedule(); + _agentInstanceContext.RemoveTerminationCallback(Stop); + } + + public void Stop() + { + StopSchedule(); + } + + public void StopSchedule() + { + if (_handle != null) + { + _agentInstanceContext.StatementContext.SchedulingService.Remove(_handle, _scheduleSlot); + } + } + + public void VisitView(ViewDataVisitor viewDataVisitor) + { + viewDataVisitor.VisitPrimary(_lastBatch, true, _timeLengthBatchViewFactory.ViewName, null); + viewDataVisitor.VisitPrimary(_currentBatch, true, _timeLengthBatchViewFactory.ViewName, null); + } + + public ViewFactory ViewFactory + { + get { return _timeLengthBatchViewFactory; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/window/TimeLengthBatchViewFactory.cs b/NEsper.Core/NEsper.Core/view/window/TimeLengthBatchViewFactory.cs new file mode 100755 index 000000000..248bb9baf --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/window/TimeLengthBatchViewFactory.cs @@ -0,0 +1,135 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat; +using com.espertech.esper.compat.collections; +using com.espertech.esper.compat.logging; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.time; +using com.espertech.esper.view; + +namespace com.espertech.esper.view.window +{ + /// + /// Factory for . + /// + public class TimeLengthBatchViewFactory + : TimeBatchViewFactoryParams + , DataWindowViewFactory + , DataWindowViewWithPrevious + , DataWindowBatchingViewFactory + { + /// Number of events to collect before batch fires. + private ExprEvaluator _sizeEvaluator; + + public void SetViewParameters(ViewFactoryContext viewFactoryContext, IList expressionParameters) + { + var validated = ViewFactorySupport.Validate( + ViewName, viewFactoryContext.StatementContext, expressionParameters); + var errorMessage = ViewName + + " view requires a numeric or time period parameter as a time interval size, and an integer parameter as a maximal number-of-events, and an optional list of control keywords as a string parameter (please see the documentation)"; + if ((validated.Length != 2) && (validated.Length != 3)) + { + throw new ViewParameterException(errorMessage); + } + + timeDeltaComputationFactory = ViewFactoryTimePeriodHelper.ValidateAndEvaluateTimeDeltaFactory( + ViewName, viewFactoryContext.StatementContext, expressionParameters[0], errorMessage, 0); + + _sizeEvaluator = ViewFactorySupport.ValidateSizeParam( + ViewName, viewFactoryContext.StatementContext, validated[1], 1); + + if (validated.Length > 2) + { + var keywords = ViewFactorySupport.Evaluate( + validated[2].ExprEvaluator, 2, ViewName, viewFactoryContext.StatementContext); + ProcessKeywords(keywords, errorMessage); + } + } + + public void Attach( + EventType parentEventType, + StatementContext statementContext, + ViewFactory optionalParentFactory, + IList parentViewFactories) + { + eventType = parentEventType; + } + + public Object MakePreviousGetter() + { + return new RelativeAccessByEventNIndexGetterImpl(); + } + + public View MakeView(AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext) + { + var timeDeltaComputation = timeDeltaComputationFactory.Make( + ViewName, "view", agentInstanceViewFactoryContext.AgentInstanceContext); + var size = ViewFactorySupport.EvaluateSizeParam( + ViewName, _sizeEvaluator, agentInstanceViewFactoryContext.AgentInstanceContext); + var viewUpdatedCollection = + agentInstanceViewFactoryContext.StatementContext.ViewServicePreviousFactory + .GetOptPreviousExprRelativeAccess(agentInstanceViewFactoryContext); + return new TimeLengthBatchView( + this, agentInstanceViewFactoryContext, timeDeltaComputation, size, IsForceUpdate, IsStartEager, + viewUpdatedCollection); + } + + public EventType EventType + { + get { return eventType; } + } + + public bool CanReuse(View view, AgentInstanceContext agentInstanceContext) + { + if (!(view is TimeLengthBatchView)) + { + return false; + } + + var myView = (TimeLengthBatchView) view; + var timeDeltaComputation = timeDeltaComputationFactory.Make( + ViewName, "view", agentInstanceContext); + if (!timeDeltaComputation.EqualsTimePeriod(myView.TimeDeltaComputation)) + { + return false; + } + + var size = ViewFactorySupport.EvaluateSizeParam(ViewName, _sizeEvaluator, agentInstanceContext); + if (myView.NumberOfEvents != size) + { + return false; + } + + if (myView.IsForceOutput != IsForceUpdate) + { + return false; + } + + if (myView.IsStartEager) + { + // since it's already started + return false; + } + + return myView.IsEmpty(); + } + + public string ViewName + { + get { return "TimeInMillis-Length-Batch"; } + } + } +} // end of namespace diff --git a/NEsper.Core/NEsper.Core/view/window/TimeWindowView.cs b/NEsper.Core/NEsper.Core/view/window/TimeWindowView.cs new file mode 100755 index 000000000..d6ce52bfa --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/window/TimeWindowView.cs @@ -0,0 +1,267 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.compat.collections; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression; +using com.espertech.esper.epl.expression.time; +using com.espertech.esper.metrics.instrumentation; +using com.espertech.esper.schedule; +using com.espertech.esper.util; + +namespace com.espertech.esper.view.window +{ + /// + /// This view is a moving timeWindow extending the specified amount of milliseconds into the + /// past. The view bases the timeWindow on the time obtained from the scheduling service. All + /// incoming events receive a timestamp and are placed in a sorted map by timestamp. The view + /// does not care about old data published by the parent view to this view. Events leave or + /// expire from the time timeWindow by means of a scheduled callback registered with the + /// scheduling service. Thus child views receive updates containing old data only asynchronously + /// as the system-time-based timeWindow moves on. However child views receive updates containing + /// new data as soon as the new data arrives. + /// + public class TimeWindowView + : ViewSupport + , CloneableView + , DataWindowView + , ScheduleAdjustmentCallback + , StoppableView + , StopCallback + { + private readonly TimeWindowViewFactory _timeWindowViewFactory; + private readonly ExprTimePeriodEvalDeltaConst _timeDeltaComputation; + private readonly TimeWindow _timeWindow; + private readonly ViewUpdatedCollection _viewUpdatedCollection; + private readonly AgentInstanceViewFactoryChainContext _agentInstanceContext; + private readonly long _scheduleSlot; + private readonly EPStatementHandleCallback _handle; + + /// + /// Constructor. + /// + /// The agent instance context. + /// for copying the view in a group-by + /// is the computation of number of milliseconds before events gets pushedout of the timeWindow as oldData in the Update method. + /// is a collection the view must Update when receiving events + public TimeWindowView( + AgentInstanceViewFactoryChainContext agentInstanceContext, + TimeWindowViewFactory timeWindowViewFactory, + ExprTimePeriodEvalDeltaConst timeDeltaComputation, + ViewUpdatedCollection viewUpdatedCollection) + { + _agentInstanceContext = agentInstanceContext; + _timeWindowViewFactory = timeWindowViewFactory; + _timeDeltaComputation = timeDeltaComputation; + _viewUpdatedCollection = viewUpdatedCollection; + _scheduleSlot = agentInstanceContext.StatementContext.ScheduleBucket.AllocateSlot(); + _timeWindow = new TimeWindow(agentInstanceContext.IsRemoveStream); + + ScheduleHandleCallback callback = new ProxyScheduleHandleCallback + { + ProcScheduledTrigger = extensionServicesContext => Instrument.With( + i => i.QViewScheduledEval(this, timeWindowViewFactory.ViewName), + i => i.AViewScheduledEval(), + Expire) + }; + + _handle = new EPStatementHandleCallback(agentInstanceContext.EpStatementAgentInstanceHandle, callback); + + if (agentInstanceContext.StatementContext.ScheduleAdjustmentService != null) + agentInstanceContext.StatementContext.ScheduleAdjustmentService.AddCallback(this); + agentInstanceContext.AddTerminationCallback(Stop); + } + + public void Adjust(long delta) + { + _timeWindow.Adjust(delta); + } + + public View CloneView() + { + return _timeWindowViewFactory.MakeView(_agentInstanceContext); + } + + /// Returns the (optional) collection handling random access to window contents for prior or previous events. + /// buffer for events + public ViewUpdatedCollection GetViewUpdatedCollection() + { + return _viewUpdatedCollection; + } + + public override EventType EventType + { + get { return Parent.EventType; } + } + + public override void Update(EventBean[] newData, EventBean[] oldData) + { + Instrument.With( + i => i.QViewProcessIRStream(this, _timeWindowViewFactory.ViewName, newData, oldData), + i => i.AViewProcessIRStream(), + () => + { + long timestamp = _agentInstanceContext.StatementContext.SchedulingService.Time; + + if (oldData != null) + { + for (int i = 0; i < oldData.Length; i++) + { + _timeWindow.Remove(oldData[i]); + } + } + + // we don't care about removed data from a prior view + if ((newData != null) && (newData.Length > 0)) + { + // If we have an empty window about to be filled for the first time, schedule a callback + // for now plus timeDeltaComputation + if (_timeWindow.IsEmpty()) + { + long current = _agentInstanceContext.StatementContext.SchedulingService.Time; + ScheduleCallback(_timeDeltaComputation.DeltaAdd(current)); + } + + // add data points to the timeWindow + for (int i = 0; i < newData.Length; i++) + { + _timeWindow.Add(timestamp, newData[i]); + } + + if (_viewUpdatedCollection != null) + { + _viewUpdatedCollection.Update(newData, null); + } + } + + // Update child views + if (HasViews) + { + Instrument.With( + i => i.QViewIndicate(this, _timeWindowViewFactory.ViewName, newData, oldData), + i => i.AViewIndicate(), + () => UpdateChildren(newData, oldData)); + } + }); + } + + /// + /// This method removes (expires) objects from the window and schedules a new callback for the + /// time when the next oldest message would expire from the window. + /// + public void Expire() + { + long current = _agentInstanceContext.StatementContext.SchedulingService.Time; + long expireBeforeTimestamp = current - _timeDeltaComputation.DeltaSubtract(current) + 1; + + // Remove from the timeWindow any events that have an older or timestamp then the given timestamp + // The window : from X to (X - timeDeltaComputation + 1) + var expired = _timeWindow.ExpireEvents(expireBeforeTimestamp); + + // If there are child views, fireStatementStopped Update method + if (HasViews) + { + if ((expired != null) && (expired.IsNotEmpty())) + { + EventBean[] oldEvents = expired.ToArray(); + if (_viewUpdatedCollection != null) + { + _viewUpdatedCollection.Update(null, oldEvents); + } + + Instrument.With( + i => i.QViewIndicate(this, _timeWindowViewFactory.ViewName, null, oldEvents), + i => i.AViewIndicate(), + () => UpdateChildren(null, oldEvents)); + } + } + + ScheduleExpiryCallback(); + } + + protected void ScheduleExpiryCallback() + { + // If we still have events in the window, schedule new callback + if (_timeWindow.IsEmpty()) + { + return; + } + var oldestTimestamp = _timeWindow.OldestTimestamp; + var currentTimestamp = _agentInstanceContext.StatementContext.SchedulingService.Time; + var scheduleTime = _timeDeltaComputation.DeltaAdd(oldestTimestamp.Value) + oldestTimestamp - currentTimestamp; + ScheduleCallback(scheduleTime.Value); + } + + public ExprTimePeriodEvalDeltaConst TimeDeltaComputation + { + get { return _timeDeltaComputation; } + } + + private void ScheduleCallback(long timeAfterCurrentTime) + { + _agentInstanceContext.StatementContext.SchedulingService.Add(timeAfterCurrentTime, _handle, _scheduleSlot); + } + + public override IEnumerator GetEnumerator() + { + return _timeWindow.GetEnumerator(); + } + + public override String ToString() + { + return GetType().FullName; + } + + /// Returns true if the window is empty, or false if not empty. + /// true if empty + public bool IsEmpty() + { + return _timeWindow.IsEmpty(); + } + + public void StopView() + { + StopSchedule(); + _agentInstanceContext.RemoveTerminationCallback(Stop); + } + + public void Stop() + { + StopSchedule(); + } + + public void StopSchedule() + { + if (_handle != null) + { + _agentInstanceContext.StatementContext.SchedulingService.Remove(_handle, _scheduleSlot); + } + if (_agentInstanceContext.StatementContext.ScheduleAdjustmentService != null) + { + _agentInstanceContext.StatementContext.ScheduleAdjustmentService.RemoveCallback(this); + } + } + + public void VisitView(ViewDataVisitor viewDataVisitor) + { + _timeWindow.VisitView(viewDataVisitor, _timeWindowViewFactory); + } + + public ViewFactory ViewFactory + { + get { return _timeWindowViewFactory; } + } + } +} diff --git a/NEsper.Core/NEsper.Core/view/window/TimeWindowViewFactory.cs b/NEsper.Core/NEsper.Core/view/window/TimeWindowViewFactory.cs new file mode 100755 index 000000000..c9bf463c4 --- /dev/null +++ b/NEsper.Core/NEsper.Core/view/window/TimeWindowViewFactory.cs @@ -0,0 +1,102 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2006-2017 Esper Team. All rights reserved. / +// http://esper.codehaus.org / +// ---------------------------------------------------------------------------------- / +// The software in this package is published under the terms of the GPL license / +// a copy of which has been included with this distribution in the license.txt file. / +/////////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +using com.espertech.esper.client; +using com.espertech.esper.collection; +using com.espertech.esper.core.context.util; +using com.espertech.esper.core.service; +using com.espertech.esper.epl.expression.core; +using com.espertech.esper.epl.expression.time; + +namespace com.espertech.esper.view.window +{ + /// Factory for . + public class TimeWindowViewFactory + : DataWindowViewFactory + , DataWindowViewWithPrevious + { + private EventType _eventType; + private ExprTimePeriodEvalDeltaConstFactory _timeDeltaComputationFactory; + + public ExprTimePeriodEvalDeltaConstFactory TimeDeltaComputationFactory + { + get { return _timeDeltaComputationFactory; } + } + + private string ViewParamMessage + { + get { return ViewName + " view requires a single numeric or time period parameter"; } + } + + public View MakeView(AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext) + { + ExprTimePeriodEvalDeltaConst timeDeltaComputation = _timeDeltaComputationFactory.Make( + ViewName, "view", agentInstanceViewFactoryContext.AgentInstanceContext); + ViewUpdatedCollection randomAccess = + agentInstanceViewFactoryContext.StatementContext.ViewServicePreviousFactory + .GetOptPreviousExprRandomAccess(agentInstanceViewFactoryContext); + return new TimeWindowView(agentInstanceViewFactoryContext, this, timeDeltaComputation, randomAccess); + } + + public EventType EventType + { + get { return _eventType; } + } + + public bool CanReuse(View view, AgentInstanceContext agentInstanceContext) + { + if (!(view is TimeWindowView)) + { + return false; + } + + var myView = (TimeWindowView) view; + ExprTimePeriodEvalDeltaConst delta = _timeDeltaComputationFactory.Make( + ViewName, "view", agentInstanceContext); + if (!delta.EqualsTimePeriod(myView.TimeDeltaComputation)) + { + return false; + } + + // For reuse of the time window it doesn't matter if it provides random access or not + return myView.IsEmpty(); + } + + public string ViewName + { + get { return "TimeInMillis"; } + } + + public Object MakePreviousGetter() + { + return new RandomAccessByIndexGetter(); + } + + public void SetViewParameters(ViewFactoryContext viewFactoryContext, IList expressionParameters) + { + if (expressionParameters.Count != 1) + { + throw new ViewParameterException(ViewParamMessage); + } + _timeDeltaComputationFactory = ViewFactoryTimePeriodHelper.ValidateAndEvaluateTimeDeltaFactory( + ViewName, viewFactoryContext.StatementContext, expressionParameters[0], ViewParamMessage, 0); + } + + public void Attach( + EventType parentEventType, + StatementContext statementContext, + ViewFactory optionalParentFactory, + IList parentViewFactories) + { + _eventType = parentEventType; + } + } +} // end of namespace \ No newline at end of file diff --git a/NEsper.Documentation/NEsper.Documentation.shfbproj b/NEsper.Documentation/NEsper.Documentation.shfbproj index 305bab3f3..2cf55bb57 100755 --- a/NEsper.Documentation/NEsper.Documentation.shfbproj +++ b/NEsper.Documentation/NEsper.Documentation.shfbproj @@ -13,8 +13,8 @@ NEsper.Documentation NEsper.Documentation - .NET Framework 4.7 - ..\build\NEsper-6.0.1docs\ + .NET Framework 4.5.2 + ..\build\NEsper-6.0.1\docs\ NEsper en-US @@ -148,4 +148,4 @@ OnBuildSuccess - \ No newline at end of file + diff --git a/NEsper.IO/NEsper.IO.Tests/App.config b/NEsper.IO/NEsper.IO.Tests/App.config index 28b3f8d74..571c1d8aa 100644 --- a/NEsper.IO/NEsper.IO.Tests/App.config +++ b/NEsper.IO/NEsper.IO.Tests/App.config @@ -11,7 +11,7 @@ - + diff --git a/NEsper.IO/NEsper.IO.Tests/NEsper.IO.Tests.csproj b/NEsper.IO/NEsper.IO.Tests/NEsper.IO.Tests.csproj index c49219d4d..e013c9175 100644 --- a/NEsper.IO/NEsper.IO.Tests/NEsper.IO.Tests.csproj +++ b/NEsper.IO/NEsper.IO.Tests/NEsper.IO.Tests.csproj @@ -15,7 +15,7 @@ 3.5 - v4.7 + v4.5.2 publish\ true Disk diff --git a/NEsper.IO/NEsper.IO/NEsper.IO.csproj b/NEsper.IO/NEsper.IO/NEsper.IO.csproj index 42d48610d..bbb1383bc 100644 --- a/NEsper.IO/NEsper.IO/NEsper.IO.csproj +++ b/NEsper.IO/NEsper.IO/NEsper.IO.csproj @@ -15,7 +15,7 @@ 3.5 - v4.7 + v4.5.2 publish\ true Disk diff --git a/NEsper.IO/NEsper.IO/app.config b/NEsper.IO/NEsper.IO/app.config index af3e4155b..6be379744 100644 --- a/NEsper.IO/NEsper.IO/app.config +++ b/NEsper.IO/NEsper.IO/app.config @@ -8,4 +8,4 @@ - + diff --git a/NEsper.nuspec b/NEsper.nuspec index 5844341ec..86c5f29a3 100755 --- a/NEsper.nuspec +++ b/NEsper.nuspec @@ -2,7 +2,7 @@ NEsper - 6.0.1.1001 + 6.0.1.1002 Espertech Espertech en-us @@ -26,10 +26,10 @@ - - - - - + + + + + diff --git a/NEsper.proj b/NEsper.proj index 679e370c7..e11f97483 100644 --- a/NEsper.proj +++ b/NEsper.proj @@ -155,40 +155,40 @@ - + - - - - - + + + + + - + - + - - + + - + - + - + - + @@ -226,7 +226,7 @@ - + diff --git a/NEsper/NEsper.Benchmark/NEsper.Benchmark.Client/NEsper.Benchmark.Client.csproj b/NEsper/NEsper.Benchmark/NEsper.Benchmark.Client/NEsper.Benchmark.Client.csproj index 37d18d931..4674b7e18 100644 --- a/NEsper/NEsper.Benchmark/NEsper.Benchmark.Client/NEsper.Benchmark.Client.csproj +++ b/NEsper/NEsper.Benchmark/NEsper.Benchmark.Client/NEsper.Benchmark.Client.csproj @@ -15,7 +15,7 @@ 3.5 - v4.7 + v4.5.2 publish\ true Disk diff --git a/NEsper/NEsper.Benchmark/NEsper.Benchmark.Client/app.config b/NEsper/NEsper.Benchmark/NEsper.Benchmark.Client/app.config index 4fed93bb6..ad1957f67 100644 --- a/NEsper/NEsper.Benchmark/NEsper.Benchmark.Client/app.config +++ b/NEsper/NEsper.Benchmark/NEsper.Benchmark.Client/app.config @@ -1,3 +1,3 @@ - + diff --git a/NEsper/NEsper.Benchmark/NEsper.Benchmark.Common/NEsper.Benchmark.Common.csproj b/NEsper/NEsper.Benchmark/NEsper.Benchmark.Common/NEsper.Benchmark.Common.csproj index 1ad73fec5..ec7f9df98 100644 --- a/NEsper/NEsper.Benchmark/NEsper.Benchmark.Common/NEsper.Benchmark.Common.csproj +++ b/NEsper/NEsper.Benchmark/NEsper.Benchmark.Common/NEsper.Benchmark.Common.csproj @@ -15,7 +15,7 @@ 3.5 - v4.7 + v4.5.2 publish\ true Disk diff --git a/NEsper/NEsper.Benchmark/NEsper.Benchmark.Perforator/NEsper.Benchmark.Perforator.csproj b/NEsper/NEsper.Benchmark/NEsper.Benchmark.Perforator/NEsper.Benchmark.Perforator.csproj index 89548ab54..dd4fbdaac 100644 --- a/NEsper/NEsper.Benchmark/NEsper.Benchmark.Perforator/NEsper.Benchmark.Perforator.csproj +++ b/NEsper/NEsper.Benchmark/NEsper.Benchmark.Perforator/NEsper.Benchmark.Perforator.csproj @@ -15,7 +15,7 @@ 3.5 - v4.7 + v4.5.2 publish\ true Disk diff --git a/NEsper/NEsper.Benchmark/NEsper.Benchmark.Perforator/app.config b/NEsper/NEsper.Benchmark/NEsper.Benchmark.Perforator/app.config index a881b2f0b..61009f95d 100644 --- a/NEsper/NEsper.Benchmark/NEsper.Benchmark.Perforator/app.config +++ b/NEsper/NEsper.Benchmark/NEsper.Benchmark.Perforator/app.config @@ -48,5 +48,5 @@ - + diff --git a/NEsper/NEsper.Benchmark/NEsper.Benchmark.Server/NEsper.Benchmark.Server.csproj b/NEsper/NEsper.Benchmark/NEsper.Benchmark.Server/NEsper.Benchmark.Server.csproj index 08affe3d2..d08beab96 100644 --- a/NEsper/NEsper.Benchmark/NEsper.Benchmark.Server/NEsper.Benchmark.Server.csproj +++ b/NEsper/NEsper.Benchmark/NEsper.Benchmark.Server/NEsper.Benchmark.Server.csproj @@ -55,7 +55,7 @@ 3.5 - v4.7 + v4.5.2 publish\ true Disk diff --git a/NEsper/NEsper.Benchmark/NEsper.Benchmark.Server/app.config b/NEsper/NEsper.Benchmark/NEsper.Benchmark.Server/app.config index a9eae3eb6..abb99532d 100644 --- a/NEsper/NEsper.Benchmark/NEsper.Benchmark.Server/app.config +++ b/NEsper/NEsper.Benchmark/NEsper.Benchmark.Server/app.config @@ -62,7 +62,7 @@ - + diff --git a/NEsper/NEsper.Benchmark/NEsper.Benchmark.Stats/NEsper.Benchmark.Stats.csproj b/NEsper/NEsper.Benchmark/NEsper.Benchmark.Stats/NEsper.Benchmark.Stats.csproj index 86747c606..7258d13f5 100644 --- a/NEsper/NEsper.Benchmark/NEsper.Benchmark.Stats/NEsper.Benchmark.Stats.csproj +++ b/NEsper/NEsper.Benchmark/NEsper.Benchmark.Stats/NEsper.Benchmark.Stats.csproj @@ -15,7 +15,7 @@ 3.5 - v4.7 + v4.5.2 publish\ true Disk diff --git a/NEsper/NEsper.Benchmark/NEsper.Benchmark.Stats/app.config b/NEsper/NEsper.Benchmark/NEsper.Benchmark.Stats/app.config index 4df95200d..858b0c9a7 100644 --- a/NEsper/NEsper.Benchmark/NEsper.Benchmark.Stats/app.config +++ b/NEsper/NEsper.Benchmark/NEsper.Benchmark.Stats/app.config @@ -1,6 +1,6 @@ - + diff --git a/NEsper/NEsper.Examples/example/NEsper.Examples.MSMQ/NEsper.Examples.MSMQ.csproj b/NEsper/NEsper.Examples/example/NEsper.Examples.MSMQ/NEsper.Examples.MSMQ.csproj index 97d834bca..9ff06d42b 100644 --- a/NEsper/NEsper.Examples/example/NEsper.Examples.MSMQ/NEsper.Examples.MSMQ.csproj +++ b/NEsper/NEsper.Examples/example/NEsper.Examples.MSMQ/NEsper.Examples.MSMQ.csproj @@ -10,7 +10,7 @@ Properties NEsper.Examples.MSMQ NEsper.Examples.MSMQ - v4.7 + v4.5.2 512 diff --git a/NEsper/NEsper.Examples/example/NEsper.Examples.MSMQ/app.config b/NEsper/NEsper.Examples/example/NEsper.Examples.MSMQ/app.config index 4fed93bb6..ad1957f67 100644 --- a/NEsper/NEsper.Examples/example/NEsper.Examples.MSMQ/app.config +++ b/NEsper/NEsper.Examples/example/NEsper.Examples.MSMQ/app.config @@ -1,3 +1,3 @@ - + diff --git a/NEsper/NEsper.Examples/example/NEsper.Examples.NamedWindowQuery/NEsper.Examples.NamedWindowQuery.csproj b/NEsper/NEsper.Examples/example/NEsper.Examples.NamedWindowQuery/NEsper.Examples.NamedWindowQuery.csproj index 44c7db366..146bd322e 100644 --- a/NEsper/NEsper.Examples/example/NEsper.Examples.NamedWindowQuery/NEsper.Examples.NamedWindowQuery.csproj +++ b/NEsper/NEsper.Examples/example/NEsper.Examples.NamedWindowQuery/NEsper.Examples.NamedWindowQuery.csproj @@ -10,7 +10,7 @@ Properties NEsper.Examples.NamedWindowQuery NEsper.Examples.NamedWindowQuery - v4.7 + v4.5.2 512 diff --git a/NEsper/NEsper.Examples/example/NEsper.Examples.NamedWindowQuery/app.config b/NEsper/NEsper.Examples/example/NEsper.Examples.NamedWindowQuery/app.config index 4fed93bb6..ad1957f67 100644 --- a/NEsper/NEsper.Examples/example/NEsper.Examples.NamedWindowQuery/app.config +++ b/NEsper/NEsper.Examples/example/NEsper.Examples.NamedWindowQuery/app.config @@ -1,3 +1,3 @@ - + diff --git a/NEsper/NEsper.Examples/example/NEsper.Examples.VirtualDW/NEsper.Examples.VirtualDW.csproj b/NEsper/NEsper.Examples/example/NEsper.Examples.VirtualDW/NEsper.Examples.VirtualDW.csproj index 8cebc0f01..5a1222277 100644 --- a/NEsper/NEsper.Examples/example/NEsper.Examples.VirtualDW/NEsper.Examples.VirtualDW.csproj +++ b/NEsper/NEsper.Examples/example/NEsper.Examples.VirtualDW/NEsper.Examples.VirtualDW.csproj @@ -10,7 +10,7 @@ Properties NEsper.Examples.VirtualDW NEsper.Examples.VirtualDW - v4.7 + v4.5.2 512 diff --git a/NEsper/NEsper.Examples/example/NEsper.Examples.VirtualDW/app.config b/NEsper/NEsper.Examples/example/NEsper.Examples.VirtualDW/app.config index 4fed93bb6..ad1957f67 100644 --- a/NEsper/NEsper.Examples/example/NEsper.Examples.VirtualDW/app.config +++ b/NEsper/NEsper.Examples/example/NEsper.Examples.VirtualDW/app.config @@ -1,3 +1,3 @@ - + diff --git a/NEsper/NEsper.Examples/example/atm/App.config b/NEsper/NEsper.Examples/example/atm/App.config index e2fecc5e4..fb818f12e 100644 --- a/NEsper/NEsper.Examples/example/atm/App.config +++ b/NEsper/NEsper.Examples/example/atm/App.config @@ -40,7 +40,7 @@ - + diff --git a/NEsper/NEsper.Examples/example/atm/NEsper.Examples.ATM.csproj b/NEsper/NEsper.Examples/example/atm/NEsper.Examples.ATM.csproj index 2ff5c11de..033d3933a 100644 --- a/NEsper/NEsper.Examples/example/atm/NEsper.Examples.ATM.csproj +++ b/NEsper/NEsper.Examples/example/atm/NEsper.Examples.ATM.csproj @@ -14,7 +14,7 @@ 3.5 - v4.7 + v4.5.2 publish\ true Disk diff --git a/NEsper/NEsper.Examples/example/autoid/App.config b/NEsper/NEsper.Examples/example/autoid/App.config index 7654711da..fc3f59742 100644 --- a/NEsper/NEsper.Examples/example/autoid/App.config +++ b/NEsper/NEsper.Examples/example/autoid/App.config @@ -60,7 +60,7 @@ - + diff --git a/NEsper/NEsper.Examples/example/autoid/NEsper.Examples.AutoId.csproj b/NEsper/NEsper.Examples/example/autoid/NEsper.Examples.AutoId.csproj index 02bb744b9..3caa3e869 100644 --- a/NEsper/NEsper.Examples/example/autoid/NEsper.Examples.AutoId.csproj +++ b/NEsper/NEsper.Examples/example/autoid/NEsper.Examples.AutoId.csproj @@ -12,7 +12,7 @@ 3.5 - v4.7 + v4.5.2 publish\ true Disk diff --git a/NEsper/NEsper.Examples/example/feedexample/NEsper.Examples.FeedExample.csproj b/NEsper/NEsper.Examples/example/feedexample/NEsper.Examples.FeedExample.csproj index aec07fece..cdd401051 100644 --- a/NEsper/NEsper.Examples/example/feedexample/NEsper.Examples.FeedExample.csproj +++ b/NEsper/NEsper.Examples/example/feedexample/NEsper.Examples.FeedExample.csproj @@ -15,7 +15,7 @@ 3.5 - v4.7 + v4.5.2 publish\ true Disk diff --git a/NEsper/NEsper.Examples/example/marketdatafeed/App.config b/NEsper/NEsper.Examples/example/marketdatafeed/App.config index e62a90603..953ba619f 100644 --- a/NEsper/NEsper.Examples/example/marketdatafeed/App.config +++ b/NEsper/NEsper.Examples/example/marketdatafeed/App.config @@ -40,7 +40,7 @@ - + diff --git a/NEsper/NEsper.Examples/example/marketdatafeed/NEsper.Examples.MarketDataFeed.csproj b/NEsper/NEsper.Examples/example/marketdatafeed/NEsper.Examples.MarketDataFeed.csproj index 25c9dd612..cdc621167 100644 --- a/NEsper/NEsper.Examples/example/marketdatafeed/NEsper.Examples.MarketDataFeed.csproj +++ b/NEsper/NEsper.Examples/example/marketdatafeed/NEsper.Examples.MarketDataFeed.csproj @@ -12,7 +12,7 @@ 3.5 - v4.7 + v4.5.2 publish\ true Disk diff --git a/NEsper/NEsper.Examples/example/matchmaker/App.config b/NEsper/NEsper.Examples/example/matchmaker/App.config index e62a90603..953ba619f 100644 --- a/NEsper/NEsper.Examples/example/matchmaker/App.config +++ b/NEsper/NEsper.Examples/example/matchmaker/App.config @@ -40,7 +40,7 @@ - + diff --git a/NEsper/NEsper.Examples/example/matchmaker/NEsper.Examples.MatchMaker.csproj b/NEsper/NEsper.Examples/example/matchmaker/NEsper.Examples.MatchMaker.csproj index 6e6821136..e18fc33ac 100644 --- a/NEsper/NEsper.Examples/example/matchmaker/NEsper.Examples.MatchMaker.csproj +++ b/NEsper/NEsper.Examples/example/matchmaker/NEsper.Examples.MatchMaker.csproj @@ -14,7 +14,7 @@ 3.5 - v4.7 + v4.5.2 publish\ true Disk diff --git a/NEsper/NEsper.Examples/example/qos_sla/App.config b/NEsper/NEsper.Examples/example/qos_sla/App.config index e2fecc5e4..fb818f12e 100644 --- a/NEsper/NEsper.Examples/example/qos_sla/App.config +++ b/NEsper/NEsper.Examples/example/qos_sla/App.config @@ -40,7 +40,7 @@ - + diff --git a/NEsper/NEsper.Examples/example/qos_sla/NEsper.Examples.QoS_SLA.csproj b/NEsper/NEsper.Examples/example/qos_sla/NEsper.Examples.QoS_SLA.csproj index 9d923d780..db3cc1285 100644 --- a/NEsper/NEsper.Examples/example/qos_sla/NEsper.Examples.QoS_SLA.csproj +++ b/NEsper/NEsper.Examples/example/qos_sla/NEsper.Examples.QoS_SLA.csproj @@ -14,7 +14,7 @@ 3.5 - v4.7 + v4.5.2 publish\ true Disk diff --git a/NEsper/NEsper.Examples/example/rsi/App.config b/NEsper/NEsper.Examples/example/rsi/App.config index e62a90603..953ba619f 100644 --- a/NEsper/NEsper.Examples/example/rsi/App.config +++ b/NEsper/NEsper.Examples/example/rsi/App.config @@ -40,7 +40,7 @@ - + diff --git a/NEsper/NEsper.Examples/example/rsi/NEsper.Examples.RSI.csproj b/NEsper/NEsper.Examples/example/rsi/NEsper.Examples.RSI.csproj index 1dbb42a04..90f50a7f6 100644 --- a/NEsper/NEsper.Examples/example/rsi/NEsper.Examples.RSI.csproj +++ b/NEsper/NEsper.Examples/example/rsi/NEsper.Examples.RSI.csproj @@ -14,7 +14,7 @@ 3.5 - v4.7 + v4.5.2 publish\ true Disk diff --git a/NEsper/NEsper.Examples/example/stockticker/App.config b/NEsper/NEsper.Examples/example/stockticker/App.config index ec7b3a0e0..80673b114 100644 --- a/NEsper/NEsper.Examples/example/stockticker/App.config +++ b/NEsper/NEsper.Examples/example/stockticker/App.config @@ -40,7 +40,7 @@ - + diff --git a/NEsper/NEsper.Examples/example/stockticker/NEsper.Examples.StockTicker.csproj b/NEsper/NEsper.Examples/example/stockticker/NEsper.Examples.StockTicker.csproj index eb8d4360b..49a4a6cd1 100644 --- a/NEsper/NEsper.Examples/example/stockticker/NEsper.Examples.StockTicker.csproj +++ b/NEsper/NEsper.Examples/example/stockticker/NEsper.Examples.StockTicker.csproj @@ -14,7 +14,7 @@ 3.5 - v4.7 + v4.5.2 publish\ true Disk diff --git a/NEsper/NEsper.Examples/example/transaction/NEsper.Examples.Transaction.csproj b/NEsper/NEsper.Examples/example/transaction/NEsper.Examples.Transaction.csproj index b93e8e427..15da296a3 100644 --- a/NEsper/NEsper.Examples/example/transaction/NEsper.Examples.Transaction.csproj +++ b/NEsper/NEsper.Examples/example/transaction/NEsper.Examples.Transaction.csproj @@ -12,7 +12,7 @@ 3.5 - v4.7 + v4.5.2 publish\ true Disk diff --git a/NEsper/NEsper.Examples/example/transaction/app.config b/NEsper/NEsper.Examples/example/transaction/app.config index a9be1f530..2aa4cc7f1 100644 --- a/NEsper/NEsper.Examples/example/transaction/app.config +++ b/NEsper/NEsper.Examples/example/transaction/app.config @@ -1,7 +1,7 @@ - + diff --git a/NEsper/NEsper.Examples/support/NEsper.Examples.Support.csproj b/NEsper/NEsper.Examples/support/NEsper.Examples.Support.csproj index 6b3abc4fd..cbba770a7 100644 --- a/NEsper/NEsper.Examples/support/NEsper.Examples.Support.csproj +++ b/NEsper/NEsper.Examples/support/NEsper.Examples.Support.csproj @@ -12,7 +12,7 @@ 3.5 - v4.7 + v4.5.2 publish\ true Disk diff --git a/NEsper/NEsper.Examples/support/app.config b/NEsper/NEsper.Examples/support/app.config index b8f5a09e1..002f1cb86 100755 --- a/NEsper/NEsper.Examples/support/app.config +++ b/NEsper/NEsper.Examples/support/app.config @@ -16,4 +16,4 @@ - + diff --git a/NEsper/NEsper.Regression/App.config b/NEsper/NEsper.Regression/App.config index 20ab9822e..8d5c34b7d 100755 --- a/NEsper/NEsper.Regression/App.config +++ b/NEsper/NEsper.Regression/App.config @@ -11,7 +11,7 @@ - + diff --git a/NEsper/NEsper.Regression/NEsper.Regression.csproj b/NEsper/NEsper.Regression/NEsper.Regression.csproj index fa05113ca..5560775e3 100755 --- a/NEsper/NEsper.Regression/NEsper.Regression.csproj +++ b/NEsper/NEsper.Regression/NEsper.Regression.csproj @@ -9,7 +9,7 @@ Properties com.espertech.esper NEsper.Regression - v4.7 + v4.5.2 512 ..\..\ true diff --git a/NEsper/NEsper.Scripting.ClearScript/NEsper.Scripting.ClearScript.csproj b/NEsper/NEsper.Scripting.ClearScript/NEsper.Scripting.ClearScript.csproj index b609b0af4..8dc785c18 100755 --- a/NEsper/NEsper.Scripting.ClearScript/NEsper.Scripting.ClearScript.csproj +++ b/NEsper/NEsper.Scripting.ClearScript/NEsper.Scripting.ClearScript.csproj @@ -9,7 +9,7 @@ Properties NEsper.Scripting.ClearScript NEsper.Scripting.ClearScript - v4.7 + v4.5.2 512 diff --git a/NEsper/NEsper.Scripting.Jurassic/NEsper.Scripting.Jurassic.csproj b/NEsper/NEsper.Scripting.Jurassic/NEsper.Scripting.Jurassic.csproj index 34c4fd450..d69c414b6 100644 --- a/NEsper/NEsper.Scripting.Jurassic/NEsper.Scripting.Jurassic.csproj +++ b/NEsper/NEsper.Scripting.Jurassic/NEsper.Scripting.Jurassic.csproj @@ -10,7 +10,7 @@ Properties NEsper.Scripting.Jurassic NEsper.Scripting.Jurassic - v4.7 + v4.5.2 512 ..\..\ true diff --git a/NEsper/NEsper.Tests/App.config b/NEsper/NEsper.Tests/App.config index 20ab9822e..8d5c34b7d 100644 --- a/NEsper/NEsper.Tests/App.config +++ b/NEsper/NEsper.Tests/App.config @@ -11,7 +11,7 @@ - + diff --git a/NEsper/NEsper.Tests/NEsper.Tests.csproj b/NEsper/NEsper.Tests/NEsper.Tests.csproj index d39c7238e..bb7b610c5 100644 --- a/NEsper/NEsper.Tests/NEsper.Tests.csproj +++ b/NEsper/NEsper.Tests/NEsper.Tests.csproj @@ -10,7 +10,7 @@ Properties com.espertech.esper NEsper.Tests - v4.7 + v4.5.2 512 diff --git a/NEsper/NEsper/App.config b/NEsper/NEsper/App.config index ff21ab56a..50b026a80 100644 --- a/NEsper/NEsper/App.config +++ b/NEsper/NEsper/App.config @@ -7,7 +7,7 @@ - + diff --git a/NEsper/NEsper/NEsper.csproj b/NEsper/NEsper/NEsper.csproj index 1913e182f..00bd98d2a 100755 --- a/NEsper/NEsper/NEsper.csproj +++ b/NEsper/NEsper/NEsper.csproj @@ -16,7 +16,7 @@ 3.5 false - v4.7 + v4.5.2 obj\net45\ publish\ true @@ -4131,7 +4131,6 @@ - diff --git a/NEsper/NEsper/client/dataflow/io/DataInputToObjectCollector.cs b/NEsper/NEsper/client/dataflow/io/DataInputToObjectCollector.cs deleted file mode 100644 index 661673d67..000000000 --- a/NEsper/NEsper/client/dataflow/io/DataInputToObjectCollector.cs +++ /dev/null @@ -1,21 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2006-2015 Esper Team. All rights reserved. / -// http://esper.codehaus.org / -// ---------------------------------------------------------------------------------- / -// The software in this package is published under the terms of the GPL license / -// a copy of which has been included with this distribution in the license.txt file. / -/////////////////////////////////////////////////////////////////////////////////////// - -using System; -using System.IO; - -namespace com.espertech.esper.client.dataflow.io -{ - /// Collects an object from {@link java.io.DataInput} and emits the object to an emitter. - public interface DataInputToObjectCollector { - /// Reads provided {@link java.io.DataInput} and emits an object using the provided emitter. - /// contains input and emitter - /// IOException when the read operation cannot be completed - void Collect(DataInputToObjectCollectorContext context) - } -} diff --git a/NEsper/NEsper/client/dataflow/io/DataInputToObjectCollectorContext.cs b/NEsper/NEsper/client/dataflow/io/DataInputToObjectCollectorContext.cs deleted file mode 100644 index acbed7d2f..000000000 --- a/NEsper/NEsper/client/dataflow/io/DataInputToObjectCollectorContext.cs +++ /dev/null @@ -1,45 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2006-2015 Esper Team. All rights reserved. / -// http://esper.codehaus.org / -// ---------------------------------------------------------------------------------- / -// The software in this package is published under the terms of the GPL license / -// a copy of which has been included with this distribution in the license.txt file. / -/////////////////////////////////////////////////////////////////////////////////////// - -using System; -using System.IO; - -using com.espertech.esper.dataflow.interfaces; - -namespace com.espertech.esper.client.dataflow.io -{ - /// Context for use with {@link DataInputToObjectCollector} carries data input and emitter. - public class DataInputToObjectCollectorContext { - private EPDataFlowEmitter emitter; - private DataInput dataInput; - - /// Returns the emitter. - /// emitter - public EPDataFlowEmitter GetEmitter() { - return emitter; - } - - /// Sets the emitter - /// emitter - public void SetEmitter(EPDataFlowEmitter emitter) { - this.emitter = emitter; - } - - /// Returns the data input. - /// data input - public DataInput GetDataInput() { - return dataInput; - } - - /// Sets the data input. - /// data input - public void SetDataInput(DataInput dataInput) { - this.dataInput = dataInput; - } - } -} diff --git a/NEsper/NEsper/client/dataflow/io/DataInputToObjectCollectorSerializable.cs b/NEsper/NEsper/client/dataflow/io/DataInputToObjectCollectorSerializable.cs deleted file mode 100644 index 347e7a8cc..000000000 --- a/NEsper/NEsper/client/dataflow/io/DataInputToObjectCollectorSerializable.cs +++ /dev/null @@ -1,40 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2006-2017 Esper Team. All rights reserved. / -// http://esper.codehaus.org / -// ---------------------------------------------------------------------------------- / -// The software in this package is published under the terms of the GPL license / -// a copy of which has been included with this distribution in the license.txt file. / -/////////////////////////////////////////////////////////////////////////////////////// - -using System; -using System.IO; - -using com.espertech.esper.compat; -using com.espertech.esper.compat.collections; -using com.espertech.esper.compat.logging; -using com.espertech.esper.events; -using com.espertech.esper.util; - -namespace com.espertech.esper.client.dataflow.io -{ - /// - /// Reads a from and emits the resulting object. - /// - /// The input must carry an int-typed number of bytes followed by the serialized object. - /// - /// - public class DataInputToObjectCollectorSerializable : DataInputToObjectCollector { - private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); - - public void Collect(DataInputToObjectCollectorContext context) { - int size = context.DataInput.ReadInt(); - var bytes = new byte[size]; - context.DataInput.ReadFully(bytes); - Object @event = SerializerUtil.ByteArrToObject(bytes); - if (Log.IsDebugEnabled) { - Log.Debug("Submitting event " + EventBeanUtility.SummarizeUnderlying(@event)); - } - context.Emitter.Submit(@event); - } - } -} // end of namespace diff --git a/NEsper/NEsper/client/dataflow/io/ObjectToDataOutputCollectorContext.cs b/NEsper/NEsper/client/dataflow/io/ObjectToDataOutputCollectorContext.cs deleted file mode 100644 index deca1d29b..000000000 --- a/NEsper/NEsper/client/dataflow/io/ObjectToDataOutputCollectorContext.cs +++ /dev/null @@ -1,43 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2006-2015 Esper Team. All rights reserved. / -// http://esper.codehaus.org / -// ---------------------------------------------------------------------------------- / -// The software in this package is published under the terms of the GPL license / -// a copy of which has been included with this distribution in the license.txt file. / -/////////////////////////////////////////////////////////////////////////////////////// - -using System; -using System.IO; - -namespace com.espertech.esper.client.dataflow.io -{ - /// Context for use with {@link ObjectToDataOutputCollector} carries object and data output. - public class ObjectToDataOutputCollectorContext { - private DataOutput dataOutput; - private Object event; - - /// Returns the data output - /// data output - public DataOutput GetDataOutput() { - return dataOutput; - } - - /// Sets the data output - /// data output - public void SetDataOutput(DataOutput dataOutput) { - this.dataOutput = dataOutput; - } - - /// Returns the event object. - /// event object - public Object GetEvent() { - return @event; - } - - /// Sets the event object. - /// event object - public void SetEvent(Object @event) { - this.event = @event; - } - } -} diff --git a/NEsper/NEsper/client/dataflow/io/ObjectToDataOutputCollectorSerializable.cs b/NEsper/NEsper/client/dataflow/io/ObjectToDataOutputCollectorSerializable.cs deleted file mode 100644 index 6c9728622..000000000 --- a/NEsper/NEsper/client/dataflow/io/ObjectToDataOutputCollectorSerializable.cs +++ /dev/null @@ -1,25 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2006-2015 Esper Team. All rights reserved. / -// http://esper.codehaus.org / -// ---------------------------------------------------------------------------------- / -// The software in this package is published under the terms of the GPL license / -// a copy of which has been included with this distribution in the license.txt file. / -/////////////////////////////////////////////////////////////////////////////////////// - -using System; -using System.IO; - -using com.espertech.esper.util; - -namespace com.espertech.esper.client.dataflow.io -{ - /// Writes a {@link java.io.Serializable} object to {@link java.io.DataOutput}. The output contains the byte array length integer followed by the byte array of the serialized object. - public class ObjectToDataOutputCollectorSerializable : ObjectToDataOutputCollector { - - public void Collect(ObjectToDataOutputCollectorContext context) { - byte[] bytes = SerializerUtil.ObjectToByteArr(context.Event); - context.DataOutput.WriteInt(bytes.Length); - context.DataOutput.Write(bytes); - } - } -} diff --git a/NEsper/NEsper/client/util/DateTime.cs b/NEsper/NEsper/client/util/DateTime.cs deleted file mode 100644 index 640f55440..000000000 --- a/NEsper/NEsper/client/util/DateTime.cs +++ /dev/null @@ -1,224 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2006-2017 Esper Team. All rights reserved. / -// http://esper.codehaus.org / -// ---------------------------------------------------------------------------------- / -// The software in this package is published under the terms of the GPL license / -// a copy of which has been included with this distribution in the license.txt file. / -/////////////////////////////////////////////////////////////////////////////////////// - -using System; -using System.Collections.Generic; - -using com.espertech.esper.compat; -using com.espertech.esper.compat.collections; -using com.espertech.esper.compat.logging; - -using java.text; -using java.time; -using java.time.format; - -namespace com.espertech.esper.client.util -{ - /// Utility class for date-time functions. - public class DateTime { - - /// The default date-time format. - public static readonly string DEFAULT_XMLLIKE_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS"; - - /// The default date-time format with time zone. - public static readonly string DEFAULT_XMLLIKE_DATE_FORMAT_WITH_ZONE = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"; - - private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); - - /// - /// Returns a calendar from a given string using the default SimpleDateFormat for parsing. - /// - /// to parse - /// calendar - public static Calendar ToCalendar(string datestring) { - return ParseGetCal(datestring, new SimpleDateFormat()); - } - - /// - /// Returns a calendar from a given string using the provided format. - /// - /// to parse - /// to use for parsing - /// calendar - public static Calendar ToCalendar(string datestring, string format) { - Date d = Parse(datestring, format); - Calendar cal = Calendar.Instance; - cal.TimeInMillis = d.Time; - return cal; - } - - /// - /// Returns a date from a given string using the default SimpleDateFormat for parsing. - /// - /// to parse - /// date object - public static Date ToDate(string datestring) { - return Parse(datestring); - } - - /// - /// Returns a date from a given string using the provided format. - /// - /// to parse - /// to use for parsing - /// date object - public static Date ToDate(string datestring, string format) { - return Parse(datestring, format); - } - - /// - /// Returns a long-millisecond value from a given string using the default SimpleDateFormat for parsing. - /// - /// to parse - /// long msec - public static long ToMillisec(string datestring) { - Date date = Parse(datestring); - if (date == null) { - return null; - } - return Date.Time; - } - - /// - /// Returns a long-millisecond value from a given string using the provided format. - /// - /// to parse - /// to use for parsing - /// long msec - public static long ToMillisec(string datestring, string format) { - Date date = Parse(datestring, format); - if (date == null) { - return null; - } - return Date.Time; - } - - /// - /// Print the provided date object using the default date format {@link #DEFAULT_XMLLIKE_DATE_FORMAT} - /// - /// should be long, Date or Calendar - /// date string - public static string Print(Object date) { - return Print(date, new SimpleDateFormat(DEFAULT_XMLLIKE_DATE_FORMAT)); - } - - /// - /// Print the provided date object using the default date format {@link #DEFAULT_XMLLIKE_DATE_FORMAT} - /// - /// should be long, Date or Calendar - /// date string - public static string PrintWithZone(Object date) { - return Print(date, new SimpleDateFormat(DEFAULT_XMLLIKE_DATE_FORMAT_WITH_ZONE)); - } - - private static string Print(Object date, SimpleDateFormat sdf) { - if (date is long) { - return Sdf.Format(new Date((long) date)); - } - if (date is Date) { - return Sdf.Format((Date) date); - } - if (date is Calendar) { - return Sdf.Format(((Calendar) date).Time); - } - throw new IllegalArgumentException("Date format for type '" + date.Class + "' not possible"); - } - - /// - /// Parse the date-time string using {@link #DEFAULT_XMLLIKE_DATE_FORMAT}. - /// - /// date-time string - /// milliseconds - public static long ParseDefaultMSec(string dateTime) { - return Parse(dateTime, new SimpleDateFormat(DEFAULT_XMLLIKE_DATE_FORMAT)).Time; - } - - /// - /// Parse the date-time string using {@link #DEFAULT_XMLLIKE_DATE_FORMAT}. - /// - /// date-time string - /// LocalDateTime - public static LocalDateTime ParseDefaultLocalDateTime(string dateTime) { - return LocalDateTime.Parse(dateTime, DateTimeFormatter.OfPattern(DEFAULT_XMLLIKE_DATE_FORMAT)); - } - - /// - /// Parse the date-time string using {@link #DEFAULT_XMLLIKE_DATE_FORMAT} assume System default time zone - /// - /// date-time string - /// ZonedDateTime - public static ZonedDateTime ParseDefaultZonedDateTime(string dateTime) { - return ParseDefaultLocalDateTime(dateTime).AtZone(ZoneId.SystemDefault()); - } - - /// - /// Parse the date-time string using {@link #DEFAULT_XMLLIKE_DATE_FORMAT_WITH_ZONE}. - /// - /// date-time string - /// milliseconds - public static long ParseDefaultMSecWZone(string dateTimeWithZone) { - return Parse(dateTimeWithZone, new SimpleDateFormat(DEFAULT_XMLLIKE_DATE_FORMAT_WITH_ZONE)).Time; - } - - /// - /// Parse the date-time string using {@link #DEFAULT_XMLLIKE_DATE_FORMAT}. - /// - /// date-time string - /// date - public static Date ParseDefaultDate(string dateTime) { - return Parse(dateTime, new SimpleDateFormat(DEFAULT_XMLLIKE_DATE_FORMAT)); - } - - /// - /// Parse the date-time string using {@link #DEFAULT_XMLLIKE_DATE_FORMAT}. - /// - /// date-time string - /// calendar - public static Calendar ParseDefaultCal(string dateTime) { - Calendar cal = Calendar.Instance; - cal.TimeInMillis = ParseDefaultMSec(dateTime); - return cal; - } - - private static Date Parse(string str) { - return Parse(str, new SimpleDateFormat()); - } - - private static Date Parse(string str, string format) { - SimpleDateFormat sdf; - try { - sdf = new SimpleDateFormat(format); - } catch (Exception ex) { - Log.Warn("Error in date format '" + str + "': " + ex.Message, ex); - return null; - } - return Parse(str, sdf); - } - - private static Date Parse(string str, SimpleDateFormat format) { - Date d; - try { - d = format.Parse(str); - } catch (ParseException e) { - Log.Warn("Error parsing date '" + str + "' according to format '" + format.ToPattern() + "': " + e.Message, e); - return null; - } - return d; - } - - private static Calendar ParseGetCal(string str, SimpleDateFormat format) { - Date d = Parse(str, format); - if (d == null) { - return null; - } - Calendar cal = Calendar.Instance; - cal.TimeInMillis = d.Time; - return cal; - } - } -} // end of namespace diff --git a/NEsper/NEsper/collection/IterablesListIterator.cs b/NEsper/NEsper/collection/IterablesListIterator.cs deleted file mode 100644 index 00bd5655f..000000000 --- a/NEsper/NEsper/collection/IterablesListIterator.cs +++ /dev/null @@ -1,93 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2006-2017 Esper Team. All rights reserved. / -// http://esper.codehaus.org / -// ---------------------------------------------------------------------------------- / -// The software in this package is published under the terms of the GPL license / -// a copy of which has been included with this distribution in the license.txt file. / -/////////////////////////////////////////////////////////////////////////////////////// - -using System; -using System.Collections.Generic; - -using com.espertech.esper.client; -using com.espertech.esper.compat; -using com.espertech.esper.compat.collections; -using com.espertech.esper.compat.logging; - -namespace com.espertech.esper.collection -{ - /// - /// An iterator over a list of iterables. - /// The IterablesListIterator iterator takes a list of Iterable instances as a parameter. The iterator will - /// Start at the very first Iterable and obtain it's iterator. It then allows iteration over this first iterator - /// until that iterator returns no next value. Then the IterablesListIterator iterator will obtain the next iterable and iterate - /// over this next iterable's iterator until no more values can be obtained. This continues until the last Iterable - /// in the order of the list of Iterables. - /// - public sealed class IterablesListIterator : IEnumerator { - private readonly IEnumerator> listIterator; - private IEnumerator currentIterator; - - /// - /// Constructor - takes a list of Iterable that supply the iterators to iterate over. - /// - /// super-iterate of iterables - public IterablesListIterator(Iterator> iteratorOfIterables) { - listIterator = iteratorOfIterables; - NextIterable(); - } - - - public EventBean Next() { - if (currentIterator == null) { - throw new NoSuchElementException(); - } - if (currentIterator.HasNext()) { - return CurrentIterator.Next(); - } - - NextIterable(); - - if (currentIterator == null) { - throw new NoSuchElementException(); - } - return CurrentIterator.Next(); - } - - public bool HasNext() { - if (currentIterator == null) { - return false; - } - - if (currentIterator.HasNext()) { - return true; - } - - NextIterable(); - - if (currentIterator == null) { - return false; - } - - return true; - } - - public void Remove() { - throw new UnsupportedOperationException(); - } - - private void NextIterable() { - while (listIterator.HasNext()) { - Iterable iterable = listIterator.Next(); - currentIterator = iterable.GetEnumerator(); - if (currentIterator.HasNext()) { - return; - } - } - - currentIterator = null; - } - } - - -} // end of namespace diff --git a/NEsper/NEsper/compat/threading/BlitReaderWriterLock.cs b/NEsper/NEsper/compat/threading/BlitReaderWriterLock.cs deleted file mode 100644 index 74cf4fa9d..000000000 --- a/NEsper/NEsper/compat/threading/BlitReaderWriterLock.cs +++ /dev/null @@ -1,323 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2006-2015 Esper Team. All rights reserved. / -// http://esper.codehaus.org / -// ---------------------------------------------------------------------------------- / -// The software in this package is published under the terms of the GPL license / -// a copy of which has been included with this distribution in the license.txt file. / -/////////////////////////////////////////////////////////////////////////////////////// - -using System; -using System.Threading; - -namespace com.espertech.esper.compat.threading -{ - public sealed class BlitReaderWriterLock : IReaderWriterLock - { - private long _bitIndicator; - - /// - /// Gets the read-side lockable - /// - public ILockable ReadLock { get; private set; } - - /// - /// Gets the write-side lockable - /// - public ILockable WriteLock { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - public BlitReaderWriterLock() - { - _bitIndicator = 0L; - ReadLock = new ReaderLock(this); - WriteLock = new WriterLock(this); - } - - private const int MaxIterations = 20; - - /// - /// Acquires the reader lock. - /// - /// The milliseconds timeout. - public void AcquireReaderLock(int millisecondsTimeout) - { - var timeA = Environment.TickCount; - var timeB = timeA + millisecondsTimeout; - - int ii = 0; - for (; Environment.TickCount < timeB; ii++) - { - long pBitField = _bitIndicator; // predictive read doesn't require interlocked - long lBitField = pBitField >> 60; // upper 4-bits - switch (lBitField) - { - case 0L: // unlocked - { - long nBitField = - (1L << 60) | // reader-lock - (pBitField & 0x7fffffffffff0000) | - (1L); - - long rBitField = Interlocked.CompareExchange( - ref _bitIndicator, - nBitField, - pBitField); - if (rBitField == pBitField) - { - return; - } - } - - if (ii < 5) - { - Thread.SpinWait(20); - } - else if (ii < 10) - { - Thread.Sleep(0); - } - else - { - Thread.Sleep(10); - } - break; - - case 1L: // reader-lock - { - long nReaders = - (pBitField & 0xffff) + 1; - long nBitField = - (pBitField & 0x7fffffffffff0000) | - (nReaders); - - long rBitField = Interlocked.CompareExchange( - ref _bitIndicator, - nBitField, - pBitField); - if (rBitField == pBitField) - { - return; - } - } - - if (ii < 5) - { - Thread.SpinWait(20); - } - else if (ii < 10) - { - Thread.Sleep(0); - } - else - { - Thread.Sleep(10); - } - break; - - case 2L: // writer-lock - Thread.Sleep(0); - break; - } - } - - throw new TimeoutException("unable to acquire reader lock"); - } - - /// - /// Releases the reader lock. - /// - public void ReleaseReaderLock() - { - for (int ii = MaxIterations; ii > 0; ii--) { - long pBitField = _bitIndicator; - long nBitField = (pBitField & 0xffff); - - nBitField--; - if (nBitField > 0) { - nBitField |= (pBitField & 0x7fffffffffff0000); - } - else { - nBitField |= (pBitField & 0x0fffffffffff0000); - } - - long rBitField = Interlocked.CompareExchange( - ref _bitIndicator, - nBitField, - pBitField); - if (rBitField == pBitField) { - return; - } - - Thread.SpinWait(20); - } - - throw new TimeoutException("unable to release reader lock"); - } - - /// - /// Acquires the writer lock. - /// - /// The milliseconds timeout. - public void AcquireWriterLock(int millisecondsTimeout) - { - var timeA = Environment.TickCount; - var timeB = timeA + millisecondsTimeout; - - int ii = 0; - for (; Environment.TickCount < timeB; ii++) - { - long pBitField = _bitIndicator; - long lBitField = pBitField >> 60; // upper 4-bits - switch (lBitField) - { - case 0L: // unlocked - { - long nBitField = - (2L << 60) | // writer-lock - (pBitField & 0x7fffffffffff0000) | - (1L); - - long rBitField = Interlocked.CompareExchange( - ref _bitIndicator, - nBitField, - pBitField); - if (rBitField == pBitField) - { - return; - } - } - - Thread.SpinWait(20); - break; - - case 1L: // reader-lock - Thread.Sleep(0); - break; - - case 2L: // writer-lock - Thread.Sleep(0); - break; - } - } - - throw new TimeoutException("unable to acquire writer lock"); - } - - /// - /// Releases the writer lock. - /// - public void ReleaseWriterLock() - { - for (int ii = MaxIterations; ii > 0; ii--) - { - long pBitField = _bitIndicator; - long nBitField = (pBitField & 0x0fffffffffff0000); - - long rBitField = Interlocked.CompareExchange( - ref _bitIndicator, - nBitField, - pBitField); - - if (rBitField == pBitField) { - return; - } - - Thread.SpinWait(20); - } - - throw new TimeoutException("unable to release writer lock"); - } - - /// - /// Internal reader lock. - /// - internal class ReaderLock : ILockable - { - internal readonly BlitReaderWriterLock LockObj; - internal readonly TrackedDisposable Disposable; - - /// - /// Initializes a new instance of the class. - /// - /// The lock obj. - internal ReaderLock(BlitReaderWriterLock lockObj) - { - LockObj = lockObj; - Disposable = new TrackedDisposable(LockObj.ReleaseReaderLock); - } - - /// - /// Acquires the lock; the lock is released when the disposable - /// object that was returned is disposed. - /// - /// - public IDisposable Acquire() - { - LockObj.AcquireReaderLock(BaseLock.RLockTimeout); - return Disposable; - } - - /// - /// Acquires the lock; the lock is released when the disposable - /// object that was returned is disposed. - /// - /// The msec. - /// - public IDisposable Acquire(int msec) - { - LockObj.AcquireReaderLock(msec); - return Disposable; - } - - public IDisposable ReleaseAcquire() - { - LockObj.ReleaseReaderLock(); - return new TrackedDisposable(() => LockObj.AcquireReaderLock(BaseLock.RLockTimeout)); - } - } - - /// - /// Internal writer lock. - /// - internal class WriterLock : ILockable - { - internal readonly BlitReaderWriterLock LockObj; - internal readonly TrackedDisposable Disposable; - - /// - /// Initializes a new instance of the class. - /// - /// The lock obj. - internal WriterLock(BlitReaderWriterLock lockObj) - { - LockObj = lockObj; - Disposable = new TrackedDisposable(LockObj.ReleaseWriterLock); - } - - /// - /// Acquires the lock; the lock is released when the disposable - /// object that was returned is disposed. - /// - /// - public IDisposable Acquire() - { - LockObj.AcquireWriterLock(BaseLock.WLockTimeout); - return Disposable; - } - - public IDisposable Acquire(int msec) - { - LockObj.AcquireWriterLock(msec); - return Disposable; - } - - public IDisposable ReleaseAcquire() - { - LockObj.ReleaseWriterLock(); - return new TrackedDisposable(() => LockObj.AcquireWriterLock(BaseLock.WLockTimeout)); - } - } - } -} diff --git a/NEsper/NEsper/core/thread/EngineThreadFactory.cs b/NEsper/NEsper/core/thread/EngineThreadFactory.cs deleted file mode 100644 index 4b03dcb5b..000000000 --- a/NEsper/NEsper/core/thread/EngineThreadFactory.cs +++ /dev/null @@ -1,57 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2006-2017 Esper Team. All rights reserved. / -// http://esper.codehaus.org / -// ---------------------------------------------------------------------------------- / -// The software in this package is published under the terms of the GPL license / -// a copy of which has been included with this distribution in the license.txt file. / -/////////////////////////////////////////////////////////////////////////////////////// - -using System; - -using com.espertech.esper.compat; -using com.espertech.esper.compat.collections; -using com.espertech.esper.compat.logging; - -namespace com.espertech.esper.core.thread -{ - /// Thread factory for threading options. - public class EngineThreadFactory : java.util.concurrent.ThreadFactory { - private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); - private readonly string engineURI; - private readonly string prefix; - private readonly ThreadGroup threadGroup; - private readonly int threadPriority; - private int currThreadCount; - - /// - /// Ctor. - /// - /// engine URI - /// prefix for thread names - /// thread group - /// priority to use - public EngineThreadFactory(string engineURI, string prefix, ThreadGroup threadGroup, int threadPrio) { - if (engineURI == null) { - this.engineURI = "default"; - } else { - this.engineURI = engineURI; - } - this.prefix = prefix; - this.threadGroup = threadGroup; - this.threadPriority = threadPrio; - } - - public Thread NewThread(Runnable runnable) { - string name = "com.espertech.esper." + prefix + "-" + engineURI + "-" + currThreadCount; - currThreadCount++; - var t = new Thread(threadGroup, runnable, name); - t.Daemon = true; - t.Priority = threadPriority; - - if (Log.IsDebugEnabled) { - Log.Debug("Creating thread '" + name + "' : " + t + " priority " + threadPriority); - } - return t; - } - } -} // end of namespace diff --git a/NEsper/NEsper/epl/agg/access/AggregationAccessorFirstLastIndexWEval.cs b/NEsper/NEsper/epl/agg/access/AggregationAccessorFirstLastIndexWEval.cs index eda861a6b..33ca81f21 100644 --- a/NEsper/NEsper/epl/agg/access/AggregationAccessorFirstLastIndexWEval.cs +++ b/NEsper/NEsper/epl/agg/access/AggregationAccessorFirstLastIndexWEval.cs @@ -13,8 +13,6 @@ using com.espertech.esper.compat.collections; using com.espertech.esper.epl.expression.core; -using Microsoft.JScript; - namespace com.espertech.esper.epl.agg.access { /// diff --git a/NEsper/NEsper/epl/agg/aggregator/AggregatorAvgBigDecimal.cs b/NEsper/NEsper/epl/agg/aggregator/AggregatorAvgBigDecimal.cs deleted file mode 100644 index ab32ef5b2..000000000 --- a/NEsper/NEsper/epl/agg/aggregator/AggregatorAvgBigDecimal.cs +++ /dev/null @@ -1,85 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2006-2017 Esper Team. All rights reserved. / -// http://esper.codehaus.org / -// ---------------------------------------------------------------------------------- / -// The software in this package is published under the terms of the GPL license / -// a copy of which has been included with this distribution in the license.txt file. / -/////////////////////////////////////////////////////////////////////////////////////// - -using System; - -using com.espertech.esper.compat; -using com.espertech.esper.compat.collections; -using com.espertech.esper.compat.logging; - -using java.math; - -namespace com.espertech.esper.epl.agg.aggregator -{ - /// Average that generates a BigDecimal numbers. - public class AggregatorAvgBigDecimal : AggregationMethod { - private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); - protected BigDecimal sum; - protected long numDataPoints; - protected MathContext optionalMathContext; - - /// - /// Ctor. - /// - /// math context - public AggregatorAvgBigDecimal(MathContext optionalMathContext) { - sum = new BigDecimal(0.0); - this.optionalMathContext = optionalMathContext; - } - - public void Clear() { - sum = new BigDecimal(0.0); - numDataPoints = 0; - } - - public void Enter(Object @object) { - if (@object == null) { - return; - } - numDataPoints++; - if (object is BigInteger) { - sum = sum.Add(new BigDecimal((BigInteger) object)); - return; - } - sum = sum.Add((BigDecimal) object); - } - - public void Leave(Object @object) { - if (@object == null) { - return; - } - - if (numDataPoints <= 1) { - Clear(); - } else { - numDataPoints--; - if (object is BigInteger) { - sum = sum.Subtract(new BigDecimal((BigInteger) object)); - } else { - sum = sum.Subtract((BigDecimal) object); - } - } - } - - public Object GetValue() { - if (numDataPoints == 0) { - return null; - } - try { - if (optionalMathContext == null) { - return Sum.Divide(new BigDecimal(numDataPoints)); - } - return Sum.Divide(new BigDecimal(numDataPoints), optionalMathContext); - } catch (ArithmeticException ex) { - Log.Error("Error computing avg aggregation result: " + ex.Message, ex); - return new BigDecimal(0); - } - } - - } -} // end of namespace diff --git a/NEsper/NEsper/epl/core/ResultSetProcessorSimpleTransform.cs b/NEsper/NEsper/epl/core/ResultSetProcessorSimpleTransform.cs index 7a9735790..d8e718320 100644 --- a/NEsper/NEsper/epl/core/ResultSetProcessorSimpleTransform.cs +++ b/NEsper/NEsper/epl/core/ResultSetProcessorSimpleTransform.cs @@ -6,8 +6,6 @@ // a copy of which has been included with this distribution in the license.txt file. / /////////////////////////////////////////////////////////////////////////////////////// -using System.IO.Ports; - using com.espertech.esper.client; using com.espertech.esper.collection; diff --git a/NEsper/NEsper/epl/db/DatabaseDMConnFactory.cs b/NEsper/NEsper/epl/db/DatabaseDMConnFactory.cs deleted file mode 100644 index d1553f998..000000000 --- a/NEsper/NEsper/epl/db/DatabaseDMConnFactory.cs +++ /dev/null @@ -1,138 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2006-2017 Esper Team. All rights reserved. / -// http://esper.codehaus.org / -// ---------------------------------------------------------------------------------- / -// The software in this package is published under the terms of the GPL license / -// a copy of which has been included with this distribution in the license.txt file. / -/////////////////////////////////////////////////////////////////////////////////////// - -using System; -using System.Collections.Generic; - -using com.espertech.esper.client; -using com.espertech.esper.compat; -using com.espertech.esper.compat.collections; -using com.espertech.esper.compat.logging; -using com.espertech.esper.epl.core; - -using java.sql; - -namespace com.espertech.esper.epl.db -{ - /// - /// Database connection factory using to obtain connections. - /// - public class DatabaseDMConnFactory : DatabaseConnectionFactory { - private readonly ConfigurationDBRef.DriverManagerConnection driverConfig; - private readonly ConfigurationDBRef.ConnectionSettings connectionSettings; - - /// - /// Ctor. - /// - /// is the driver manager configuration - /// are connection-level settings - /// engine imports - /// thrown if the driver class cannot be loaded - public DatabaseDMConnFactory(ConfigurationDBRef.DriverManagerConnection driverConfig, - ConfigurationDBRef.ConnectionSettings connectionSettings, - EngineImportService engineImportService) - { - this.driverConfig = driverConfig; - this.connectionSettings = connectionSettings; - - // load driver class - string driverClassName = driverConfig.ClassName; - try { - engineImportService.ClassForNameProvider.ClassForName(driverClassName); - } catch (ClassNotFoundException ex) { - throw new DatabaseConfigException("Error loading driver class '" + driverClassName + '\'', ex); - } catch (RuntimeException ex) { - throw new DatabaseConfigException("Error loading driver class '" + driverClassName + '\'', ex); - } - } - - /// - /// Method to set connection-level configuration settings. - /// - /// is the connection to set on - /// are the settings to apply - /// is thrown if an SQLException is thrown - protected static void SetConnectionOptions(Connection connection, - ConfigurationDBRef.ConnectionSettings connectionSettings) - { - try { - if (connectionSettings.ReadOnly != null) { - connection.ReadOnly = connectionSettings.ReadOnly; - } - } catch (SQLException ex) { - throw new DatabaseConfigException("Error setting read-only to " + connectionSettings.ReadOnly + - " on connection with detail " + GetDetail(ex), ex); - } - - try { - if (connectionSettings.TransactionIsolation != null) { - connection.TransactionIsolation = connectionSettings.TransactionIsolation; - } - } catch (SQLException ex) { - throw new DatabaseConfigException("Error setting transaction isolation level to " + - connectionSettings.TransactionIsolation + " on connection with detail " + GetDetail(ex), ex); - } - - try { - if (connectionSettings.Catalog != null) { - connection.Catalog = connectionSettings.Catalog; - } - } catch (SQLException ex) { - throw new DatabaseConfigException("Error setting catalog to '" + connectionSettings.Catalog + - "' on connection with detail " + GetDetail(ex), ex); - } - - try { - if (connectionSettings.AutoCommit != null) { - connection.Catalog = connectionSettings.Catalog; - } - } catch (SQLException ex) { - throw new DatabaseConfigException("Error setting auto-commit to " + connectionSettings.AutoCommit + - " on connection with detail " + GetDetail(ex), ex); - } - } - - private static string GetDetail(SQLException ex) { - return "SQLException: " + ex.Message + - " SQLState: " + ex.SQLState + - " VendorError: " + ex.ErrorCode; - } - - public Connection GetConnection() { - // use driver manager to get a connection - Connection connection; - string url = driverConfig.Url; - Properties properties = driverConfig.OptionalProperties; - if (properties == null) { - properties = new Properties(); - } - try { - string user = driverConfig.OptionalUserName; - string pwd = driverConfig.OptionalPassword; - if ((user == null) && (pwd == null) && (properties.IsEmpty())) { - connection = DriverManager.GetConnection(url); - } else if (!properties.IsEmpty()) { - connection = DriverManager.GetConnection(url, properties); - } else { - connection = DriverManager.GetConnection(url, user, pwd); - } - } catch (SQLException ex) { - string detail = "SQLException: " + ex.Message + - " SQLState: " + ex.SQLState + - " VendorError: " + ex.ErrorCode; - - throw new DatabaseConfigException("Error obtaining database connection using url '" + url + - "' with detail " + detail, ex); - } - - SetConnectionOptions(connection, connectionSettings); - - return connection; - } - } -} // end of namespace diff --git a/NEsper/NEsper/epl/db/DatabaseDSConnFactory.cs b/NEsper/NEsper/epl/db/DatabaseDSConnFactory.cs deleted file mode 100644 index 0c292ae7d..000000000 --- a/NEsper/NEsper/epl/db/DatabaseDSConnFactory.cs +++ /dev/null @@ -1,94 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2006-2017 Esper Team. All rights reserved. / -// http://esper.codehaus.org / -// ---------------------------------------------------------------------------------- / -// The software in this package is published under the terms of the GPL license / -// a copy of which has been included with this distribution in the license.txt file. / -/////////////////////////////////////////////////////////////////////////////////////// - -using System; -using System.Collections.Generic; - -using com.espertech.esper.client; -using com.espertech.esper.compat; -using com.espertech.esper.compat.collections; -using com.espertech.esper.compat.logging; - -using java.sql; - -using javax.naming; -using javax.sql; - -namespace com.espertech.esper.epl.db -{ - /// - /// Database connection factory using and to obtain connections. - /// - public class DatabaseDSConnFactory : DatabaseConnectionFactory { - private readonly ConfigurationDBRef.DataSourceConnection dsConfig; - private readonly ConfigurationDBRef.ConnectionSettings connectionSettings; - - private DataSource dataSource; - - /// - /// Ctor. - /// - /// is the datasource object name and initial context properties. - /// are the connection-level settings - public DatabaseDSConnFactory(ConfigurationDBRef.DataSourceConnection dsConfig, - ConfigurationDBRef.ConnectionSettings connectionSettings) { - this.dsConfig = dsConfig; - this.connectionSettings = connectionSettings; - } - - public Connection GetConnection() { - if (dataSource == null) { - Properties envProps = dsConfig.EnvProperties; - if (envProps == null) { - envProps = new Properties(); - } - - InitialContext ctx; - try { - if (!envProps.IsEmpty()) { - ctx = new InitialContext(envProps); - } else { - ctx = new InitialContext(); - } - } catch (NamingException ex) { - throw new DatabaseConfigException("Error instantiating initial context", ex); - } - - DataSource ds; - string lookupName = dsConfig.ContextLookupName; - try { - ds = (DataSource) ctx.Lookup(lookupName); - } catch (NamingException ex) { - throw new DatabaseConfigException("Error looking up data source in context using name '" + lookupName + '\'', ex); - } - - if (ds == null) { - throw new DatabaseConfigException("Null data source obtained through context using name '" + lookupName + '\''); - } - - dataSource = ds; - } - - Connection connection; - try { - connection = dataSource.Connection; - } catch (SQLException ex) { - string detail = "SQLException: " + ex.Message + - " SQLState: " + ex.SQLState + - " VendorError: " + ex.ErrorCode; - - throw new DatabaseConfigException("Error obtaining database connection using datasource " + - "with detail " + detail, ex); - } - - DatabaseDMConnFactory.ConnectionOptions = connection, connectionSettings; - - return connection; - } - } -} // end of namespace diff --git a/NEsper/NEsper/epl/db/DatabaseDSFactoryConnFactory.cs b/NEsper/NEsper/epl/db/DatabaseDSFactoryConnFactory.cs deleted file mode 100644 index 1919f5124..000000000 --- a/NEsper/NEsper/epl/db/DatabaseDSFactoryConnFactory.cs +++ /dev/null @@ -1,109 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2006-2017 Esper Team. All rights reserved. / -// http://esper.codehaus.org / -// ---------------------------------------------------------------------------------- / -// The software in this package is published under the terms of the GPL license / -// a copy of which has been included with this distribution in the license.txt file. / -/////////////////////////////////////////////////////////////////////////////////////// - -using System; -using System.Collections.Generic; -using System.Reflection; - -using com.espertech.esper.client; -using com.espertech.esper.compat; -using com.espertech.esper.compat.collections; -using com.espertech.esper.compat.logging; -using com.espertech.esper.epl.core; -using com.espertech.esper.util; - -using java.sql; - -using javax.sql; - -namespace com.espertech.esper.epl.db -{ - /// - /// Database connection factory using and to obtain connections. - /// - public class DatabaseDSFactoryConnFactory : DatabaseConnectionFactory { - private readonly ConfigurationDBRef.ConnectionSettings connectionSettings; - private DataSource dataSource; - - /// - /// Ctor. - /// - /// is the datasource object name and initial context properties. - /// are the connection-level settings - /// engine imports - /// when the factory cannot be configured - public DatabaseDSFactoryConnFactory(ConfigurationDBRef.DataSourceFactory dsConfig, - ConfigurationDBRef.ConnectionSettings connectionSettings, - EngineImportService engineImportService) - { - this.connectionSettings = connectionSettings; - - Type clazz; - try { - clazz = engineImportService.ClassForNameProvider.ClassForName(dsConfig.FactoryClassname); - } catch (ClassNotFoundException e) { - throw new DatabaseConfigException("Type '" + dsConfig.FactoryClassname + "' cannot be loaded", e); - } - - Object obj; - try { - obj = clazz.NewInstance(); - } catch (InstantiationException e) { - throw new ConfigurationException("Type '" + clazz + "' cannot be instantiated", e); - } catch (IllegalAccessException e) { - throw new ConfigurationException("Illegal access instantiating class '" + clazz + "'", e); - } - - // find method : static DataSource CreateDataSource(Properties properties) - Method method; - try { - method = clazz.GetMethod("createDataSource", Typeof(Properties)); - } catch (NoSuchMethodException e) { - throw new ConfigurationException("Type '" + clazz + "' does not provide a static method by name createDataSource accepting a single Properties object as parameter", e); - } - if (method == null) { - throw new ConfigurationException("Type '" + clazz + "' does not provide a static method by name createDataSource accepting a single Properties object as parameter"); - } - if (!JavaClassHelper.IsImplementsInterface(method.ReturnType, Typeof(DataSource))) { - throw new ConfigurationException("On class '" + clazz + "' the static method by name createDataSource does not return a DataSource"); - } - - Object result; - try { - result = method.Invoke(obj, dsConfig.Properties); - } catch (IllegalAccessException e) { - throw new ConfigurationException("Type '" + clazz + "' failed in method createDataSource :" + e.Message, e); - } catch (InvocationTargetException e) { - throw new ConfigurationException("Type '" + clazz + "' failed in method createDataSource :" + e.Message, e); - } - if (result == null) { - throw new ConfigurationException("Method createDataSource returned a null value for DataSource"); - } - - dataSource = (DataSource) result; - } - - public Connection GetConnection() { - Connection connection; - try { - connection = dataSource.Connection; - } catch (SQLException ex) { - string detail = "SQLException: " + ex.Message + - " SQLState: " + ex.SQLState + - " VendorError: " + ex.ErrorCode; - - throw new DatabaseConfigException("Error obtaining database connection using datasource " + - "with detail " + detail, ex); - } - - DatabaseDMConnFactory.ConnectionOptions = connection, connectionSettings; - - return connection; - } - } -} // end of namespace diff --git a/NEsper/NEsper/epl/expression/prev/ExprPreviousNode.cs b/NEsper/NEsper/epl/expression/prev/ExprPreviousNode.cs index 6aa8bec00..2a154f23c 100644 --- a/NEsper/NEsper/epl/expression/prev/ExprPreviousNode.cs +++ b/NEsper/NEsper/epl/expression/prev/ExprPreviousNode.cs @@ -17,8 +17,6 @@ using com.espertech.esper.metrics.instrumentation; using com.espertech.esper.util; -using Microsoft.JScript; - namespace com.espertech.esper.epl.expression.prev { /// diff --git a/NEsper/NEsper/epl/parse/Antlr4ErrorListener.cs b/NEsper/NEsper/epl/parse/Antlr4ErrorListener.cs index 28b220790..f42351094 100644 --- a/NEsper/NEsper/epl/parse/Antlr4ErrorListener.cs +++ b/NEsper/NEsper/epl/parse/Antlr4ErrorListener.cs @@ -6,6 +6,7 @@ // a copy of which has been included with this distribution in the license.txt file. / /////////////////////////////////////////////////////////////////////////////////////// +using System.IO; using System.Reflection; using Antlr4.Runtime; @@ -38,6 +39,18 @@ public void SyntaxError( throw e; } + public void SyntaxError( + TextWriter output, + IRecognizer recognizer, + T offendingSymbol, + int line, + int charPositionInLine, + string msg, + RecognitionException e) + { + throw e; + } + public void ReportAmbiguity( Parser recognizer, DFA dfa, diff --git a/NEsper/NEsper/epl/script/ExprNodeScriptEvalJSR223.cs b/NEsper/NEsper/epl/script/ExprNodeScriptEvalJSR223.cs deleted file mode 100644 index a84063ed6..000000000 --- a/NEsper/NEsper/epl/script/ExprNodeScriptEvalJSR223.cs +++ /dev/null @@ -1,70 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2006-2017 Esper Team. All rights reserved. / -// http://esper.codehaus.org / -// ---------------------------------------------------------------------------------- / -// The software in this package is published under the terms of the GPL license / -// a copy of which has been included with this distribution in the license.txt file. / -/////////////////////////////////////////////////////////////////////////////////////// - -using System; - -using com.espertech.esper.client; -using com.espertech.esper.compat; -using com.espertech.esper.compat.collections; -using com.espertech.esper.compat.logging; -using com.espertech.esper.epl.expression.core; - -using javax.script; - -namespace com.espertech.esper.epl.script -{ - public class ExprNodeScriptEvalJSR223 : ExprNodeScriptEvalBase, ExprNodeScriptEvaluator { - - private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); - - private readonly CompiledScript executable; - - public ExprNodeScriptEvalJSR223(string scriptName, string statementName, string[] names, ExprEvaluator[] parameters, Type returnType, EventType eventTypeCollection, CompiledScript executable) { - Super(scriptName, statementName, names, parameters, returnType, eventTypeCollection); - this.executable = executable; - } - - public Object Evaluate(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) { - Bindings bindings = GetBindings(context); - for (int i = 0; i < names.Length; i++) { - bindings.Put(names[i], parameters[i].Evaluate(eventsPerStream, isNewData, context)); - } - return EvaluateInternal(bindings); - } - - public Object Evaluate(Object[] lookupValues, ExprEvaluatorContext context) { - Bindings bindings = GetBindings(context); - for (int i = 0; i < names.Length; i++) { - bindings.Put(names[i], lookupValues[i]); - } - return EvaluateInternal(bindings); - } - - private Bindings GetBindings(ExprEvaluatorContext context) { - Bindings bindings = executable.Engine.CreateBindings(); - bindings.Put(ExprNodeScript.CONTEXT_BINDING_NAME, context.AllocateAgentInstanceScriptContext); - return bindings; - } - - private Object EvaluateInternal(Bindings bindings) { - try { - Object result = executable.Eval(bindings); - - if (coercer != null) { - return Coercer.CoerceBoxed((Number) result); - } - - return result; - } catch (ScriptException e) { - string message = "Unexpected exception executing script '" + scriptName + "' for statement '" + statementName + "' : " + e.Message; - Log.Error(message, e); - throw new EPException(message, e); - } - } - } -} // end of namespace diff --git a/NEsper/NEsper/epl/script/ExprNodeScriptEvalMVEL.cs b/NEsper/NEsper/epl/script/ExprNodeScriptEvalMVEL.cs deleted file mode 100644 index ff5e5b1d7..000000000 --- a/NEsper/NEsper/epl/script/ExprNodeScriptEvalMVEL.cs +++ /dev/null @@ -1,72 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2006-2017 Esper Team. All rights reserved. / -// http://esper.codehaus.org / -// ---------------------------------------------------------------------------------- / -// The software in this package is published under the terms of the GPL license / -// a copy of which has been included with this distribution in the license.txt file. / -/////////////////////////////////////////////////////////////////////////////////////// - -using System; -using System.Collections.Generic; -using System.Reflection; - -using com.espertech.esper.client; -using com.espertech.esper.compat; -using com.espertech.esper.compat.collections; -using com.espertech.esper.compat.logging; -using com.espertech.esper.epl.expression.core; -using com.espertech.esper.epl.script.mvel; - -namespace com.espertech.esper.epl.script -{ - public class ExprNodeScriptEvalMVEL : ExprNodeScriptEvalBase, ExprNodeScriptEvaluator { - - private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); - - private readonly Object executable; - - public ExprNodeScriptEvalMVEL(string scriptName, string statementName, string[] names, ExprEvaluator[] parameters, Type returnType, EventType eventTypeCollection, Object executable) { - Super(scriptName, statementName, names, parameters, returnType, eventTypeCollection); - this.executable = executable; - } - - public Object Evaluate(EventBean[] eventsPerStream, bool isNewData, ExprEvaluatorContext context) { - IDictionary paramsList = GetParamsList(context); - for (int i = 0; i < names.Length; i++) { - paramsList.Put(names[i], parameters[i].Evaluate(eventsPerStream, isNewData, context)); - } - return EvaluateInternal(paramsList); - } - - public Object Evaluate(Object[] lookupValues, ExprEvaluatorContext context) { - IDictionary paramsList = GetParamsList(context); - for (int i = 0; i < names.Length; i++) { - paramsList.Put(names[i], lookupValues[i]); - } - return EvaluateInternal(paramsList); - } - - private IDictionary GetParamsList(ExprEvaluatorContext context) { - var paramsList = new Dictionary(); - paramsList.Put(ExprNodeScript.CONTEXT_BINDING_NAME, context.AllocateAgentInstanceScriptContext); - return paramsList; - } - - private Object EvaluateInternal(IDictionary paramsList) { - try { - Object result = MVELInvoker.ExecuteExpression(executable, paramsList); - - if (coercer != null) { - return Coercer.CoerceBoxed((Number) result); - } - - return result; - } catch (InvocationTargetException ex) { - Throwable mvelException = ex.Cause; - string message = "Unexpected exception executing script '" + scriptName + "' for statement '" + statementName + "' : " + mvelException.Message; - Log.Error(message, mvelException); - throw new EPException(message, ex); - } - } - } -} // end of namespace diff --git a/NEsper/NEsper/epl/table/mgmt/TableMetadataInternalEventToPublic.cs b/NEsper/NEsper/epl/table/mgmt/TableMetadataInternalEventToPublic.cs index 9074c86fb..c6a9ab20a 100644 --- a/NEsper/NEsper/epl/table/mgmt/TableMetadataInternalEventToPublic.cs +++ b/NEsper/NEsper/epl/table/mgmt/TableMetadataInternalEventToPublic.cs @@ -12,8 +12,6 @@ using com.espertech.esper.events; using com.espertech.esper.events.arr; -using Microsoft.JScript; - namespace com.espertech.esper.epl.table.mgmt { public class TableMetadataInternalEventToPublic diff --git a/NEsper/NEsper/epl/view/FilterExprViewIterator.cs b/NEsper/NEsper/epl/view/FilterExprViewIterator.cs deleted file mode 100644 index 609a858ac..000000000 --- a/NEsper/NEsper/epl/view/FilterExprViewIterator.cs +++ /dev/null @@ -1,85 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2006-2017 Esper Team. All rights reserved. / -// http://esper.codehaus.org / -// ---------------------------------------------------------------------------------- / -// The software in this package is published under the terms of the GPL license / -// a copy of which has been included with this distribution in the license.txt file. / -/////////////////////////////////////////////////////////////////////////////////////// - -using System; -using System.Collections.Generic; - -using com.espertech.esper.client; -using com.espertech.esper.compat; -using com.espertech.esper.compat.collections; -using com.espertech.esper.compat.logging; -using com.espertech.esper.epl.expression.core; - -namespace com.espertech.esper.epl.view -{ - /// Iterator for reading and filtering a source event iterator. - public class FilterExprViewIterator : IEnumerator { - private readonly IEnumerator sourceIterator; - private readonly ExprEvaluator filter; - private readonly ExprEvaluatorContext exprEvaluatorContext; - private readonly EventBean[] evalEventArr; - - private EventBean nextResult; - - /// - /// Ctor. - /// - /// is the iterator supplying events to filter out. - /// is the filter expression - /// context for expression evalauation - public FilterExprViewIterator(Iterator sourceIterator, ExprEvaluator filter, ExprEvaluatorContext exprEvaluatorContext) { - this.sourceIterator = sourceIterator; - this.filter = filter; - this.exprEvaluatorContext = exprEvaluatorContext; - evalEventArr = new EventBean[1]; - } - - public bool HasNext() { - if (nextResult != null) { - return true; - } - FindNext(); - if (nextResult != null) { - return true; - } - return false; - } - - public EventBean Next() { - if (nextResult != null) { - EventBean result = nextResult; - nextResult = null; - return result; - } - FindNext(); - if (nextResult != null) { - EventBean result = nextResult; - nextResult = null; - return result; - } - throw new NoSuchElementException(); - } - - private void FindNext() { - while (sourceIterator.HasNext()) { - EventBean candidate = sourceIterator.Next(); - evalEventArr[0] = candidate; - - bool? pass = (bool?) filter.Evaluate(evalEventArr, true, exprEvaluatorContext); - if ((pass != null) && pass) { - nextResult = candidate; - break; - } - } - } - - public void Remove() { - throw new UnsupportedOperationException(); - } - } -} // end of namespace diff --git a/NEsper/NEsper/util/ManagedReadWriteLock.cs b/NEsper/NEsper/util/ManagedReadWriteLock.cs deleted file mode 100644 index 0bfa7b4b5..000000000 --- a/NEsper/NEsper/util/ManagedReadWriteLock.cs +++ /dev/null @@ -1,129 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2006-2017 Esper Team. All rights reserved. / -// http://esper.codehaus.org / -// ---------------------------------------------------------------------------------- / -// The software in this package is published under the terms of the GPL license / -// a copy of which has been included with this distribution in the license.txt file. / -/////////////////////////////////////////////////////////////////////////////////////// - -using System; -using System.Collections.Generic; - -using com.espertech.esper.compat; -using com.espertech.esper.compat.collections; -using com.espertech.esper.compat.logging; - -using java.util.concurrent.locks; - -namespace com.espertech.esper.util -{ - /// - /// Simple read-write lock based on that associates a - /// name with the lock and traces read/write locking and unlocking. - /// - public class ManagedReadWriteLock { - /// Acquire text. - public static readonly string ACQUIRE_TEXT = "Acquire "; - /// Acquired text. - public static readonly string ACQUIRED_TEXT = "Got "; - /// Acquired text. - public static readonly string TRY_TEXT = "Trying "; - /// Release text. - public static readonly string RELEASE_TEXT = "Release "; - /// Released text. - public static readonly string RELEASED_TEXT = "Freed "; - private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); - private readonly ReentrantReadWriteLock lock; - private readonly string name; - - /// - /// Ctor. - /// - /// of lock - /// true if a fair lock, false if not - public ManagedReadWriteLock(string name, bool isFair) { - this.name = name; - this.lock = new ReentrantReadWriteLock(isFair); - } - - /// Lock write lock. - public void AcquireWriteLock() { - if (ThreadLogUtil.ENABLED_TRACE) { - ThreadLogUtil.TraceLock(ACQUIRE_TEXT + " write " + name, lock); - } - - lock.WriteLock().Lock(); - - if (ThreadLogUtil.ENABLED_TRACE) { - ThreadLogUtil.TraceLock(ACQUIRED_TEXT + " write " + name, lock); - } - } - - /// - /// Try write lock with timeout, returning an indicator whether the lock was acquired or not. - /// - /// number of milliseconds to wait for lock - /// indicator whether the lock could be acquired or not - public bool TryWriteLock(long msec) { - if (ThreadLogUtil.ENABLED_TRACE) { - ThreadLogUtil.TraceLock(TRY_TEXT + " write " + name, lock); - } - - bool result = false; - try { - result = lock.WriteLock().TryLock(msec, TimeUnit.MILLISECONDS); - } catch (InterruptedException ex) { - Log.Warn("Lock wait interupted"); - } - - if (ThreadLogUtil.ENABLED_TRACE) { - ThreadLogUtil.TraceLock(TRY_TEXT + " write " + name + " : " + result, lock); - } - - return result; - } - - /// Unlock write lock. - public void ReleaseWriteLock() { - if (ThreadLogUtil.ENABLED_TRACE) { - ThreadLogUtil.TraceLock(RELEASE_TEXT + " write " + name, lock); - } - - lock.WriteLock().Unlock(); - - if (ThreadLogUtil.ENABLED_TRACE) { - ThreadLogUtil.TraceLock(RELEASED_TEXT + " write " + name, lock); - } - } - - /// Lock read lock. - public void AcquireReadLock() { - if (ThreadLogUtil.ENABLED_TRACE) { - ThreadLogUtil.TraceLock(ACQUIRE_TEXT + " read " + name, lock); - } - - lock.ReadLock().Lock(); - - if (ThreadLogUtil.ENABLED_TRACE) { - ThreadLogUtil.TraceLock(ACQUIRED_TEXT + " read " + name, lock); - } - } - - /// Unlock read lock. - public void ReleaseReadLock() { - if (ThreadLogUtil.ENABLED_TRACE) { - ThreadLogUtil.TraceLock(RELEASE_TEXT + " read " + name, lock); - } - - lock.ReadLock().Unlock(); - - if (ThreadLogUtil.ENABLED_TRACE) { - ThreadLogUtil.TraceLock(RELEASED_TEXT + " read " + name, lock); - } - } - - public ReentrantReadWriteLock GetLock() { - return lock; - } - } -} // end of namespace diff --git a/NEsper/NEsper/util/ObjectInputStreamWithTCCL.cs b/NEsper/NEsper/util/ObjectInputStreamWithTCCL.cs deleted file mode 100644 index b636da27a..000000000 --- a/NEsper/NEsper/util/ObjectInputStreamWithTCCL.cs +++ /dev/null @@ -1,46 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2006-2017 Esper Team. All rights reserved. / -// http://esper.codehaus.org / -// ---------------------------------------------------------------------------------- / -// The software in this package is published under the terms of the GPL license / -// a copy of which has been included with this distribution in the license.txt file. / -/////////////////////////////////////////////////////////////////////////////////////// - -using System; -using System.IO; - -using com.espertech.esper.compat; -using com.espertech.esper.compat.collections; -using com.espertech.esper.compat.logging; - -namespace com.espertech.esper.util -{ - public class ObjectInputStreamWithTCCL : ObjectInputStream{ - - private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); - - public ObjectInputStreamWithTCCL(InputStream input) { - Super(input); - } - - public ObjectInputStreamWithTCCL() { - } - - public override Type ResolveClass(ObjectStreamClass desc) { - - if (Log.IsDebugEnabled) { - Log.Debug("Resolving class " + desc.Name + " id " + desc.SerialVersionUID + " classloader " + Thread.CurrentThread().ContextClassLoader.Class); - } - - ClassLoader currentTccl = null; - try { - currentTccl = Thread.CurrentThread().ContextClassLoader; - if (currentTccl != null) { - return CurrentTccl.LoadClass(desc.Name); - } - } catch (Exception e) { - } - return Base.ResolveClass(desc); - } - } -} // end of namespace diff --git a/NEsper/NEsper/util/ResourceLoader.cs b/NEsper/NEsper/util/ResourceLoader.cs deleted file mode 100644 index c7eaa56f4..000000000 --- a/NEsper/NEsper/util/ResourceLoader.cs +++ /dev/null @@ -1,92 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2006-2017 Esper Team. All rights reserved. / -// http://esper.codehaus.org / -// ---------------------------------------------------------------------------------- / -// The software in this package is published under the terms of the GPL license / -// a copy of which has been included with this distribution in the license.txt file. / -/////////////////////////////////////////////////////////////////////////////////////// - -using System; -using System.Net; - -using com.espertech.esper.client; -using com.espertech.esper.compat; -using com.espertech.esper.compat.collections; -using com.espertech.esper.compat.logging; - -namespace com.espertech.esper.util -{ - /// - /// Utility class for loading or resolving external resources via URL and class path. - /// - public class ResourceLoader { - /// - /// Resolve a resource into a URL using the URL string or classpath-relative filename and - /// using a name for any exceptions thrown. - /// - /// is the name for use in exceptions - /// is a URL string or classpath-relative filename - /// class loader - /// URL or null if resolution was unsuccessful - public static URL ResolveClassPathOrURLResource(string resourceName, string urlOrClasspathResource, ClassLoader classLoader) { - URL url; - try { - url = new URL(urlOrClasspathResource); - } catch (MalformedURLException ex) { - url = GetClasspathResourceAsURL(resourceName, urlOrClasspathResource, classLoader); - } - return url; - } - - /// - /// Returns an URL from an application resource in the classpath. - /// - /// The method first removes the '/' character from the resource name if - /// the first character is '/'. - /// - /// - /// The lookup order is as follows: - /// - /// - /// If a thread context class loader exists, use Thread.CurrentThread().getResourceAsStream - /// to obtain an InputStream. - /// - /// - /// If no input stream was returned, use the Typeof(Configuration).getResourceAsStream. - /// to obtain an InputStream. - /// - /// - /// If no input stream was returned, use the Typeof(Configuration).ClassLoader.getResourceAsStream. - /// to obtain an InputStream. - /// - /// - /// If no input stream was returned, throw an Exception. - /// - /// - /// is the name for use in exceptions - /// is the classpath-relative filename to resolve into a URL - /// class loader - /// URL for resource - public static URL GetClasspathResourceAsURL(string resourceName, string resource, ClassLoader classLoader) { - string stripped = resource.StartsWith("/") ? - resource.Substring(1) : resource; - - URL url = null; - if (classLoader != null) { - url = classLoader.GetResource(stripped); - } - if (url == null) { - url = Typeof(ResourceLoader).GetResource(resource); - } - if (url == null) { - url = Typeof(ResourceLoader).ClassLoader.GetResource(stripped); - } - if (url == null) { - throw new EPException(resourceName + " resource '" + resource + "' not found"); - } - return url; - } - - - } -} // end of namespace diff --git a/NEsper/NEsper/util/SimpleByteArrayInputStream.cs b/NEsper/NEsper/util/SimpleByteArrayInputStream.cs deleted file mode 100644 index 0313f1a22..000000000 --- a/NEsper/NEsper/util/SimpleByteArrayInputStream.cs +++ /dev/null @@ -1,67 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2006-2017 Esper Team. All rights reserved. / -// http://esper.codehaus.org / -// ---------------------------------------------------------------------------------- / -// The software in this package is published under the terms of the GPL license / -// a copy of which has been included with this distribution in the license.txt file. / -/////////////////////////////////////////////////////////////////////////////////////// - -using System; -using System.IO; - -using com.espertech.esper.compat; -using com.espertech.esper.compat.collections; -using com.espertech.esper.compat.logging; - -namespace com.espertech.esper.util -{ - /// Input stream that relies on a simple byte array, unchecked. - public class SimpleByteArrayInputStream : InputStream{ - private byte[] buf = null; - private int count = 0; - private int pos = 0; - - /// - /// Ctor. - /// - /// is the byte buffer - /// is the size of the buffer - public SimpleByteArrayInputStream(byte[] buf, int count) { - this.buf = buf; - this.count = count; - } - - public int Available() { - return count - pos; - } - - public int Read() { - return (pos < count) ? (buf[pos++] & 0xff) : -1; - } - - public int Read(byte[] b, int off, int len) { - if (pos >= count) { - return -1; - } - - if ((pos + len) > count) { - len = count - pos; - } - - System.Arraycopy(buf, pos, b, off, len); - pos += len; - return len; - } - - public long Skip(long n) { - if ((pos + n) > count) { - n = count - pos; - } - if (n < 0) { - return 0; - } - pos += n; - return n; - } - } -} // end of namespace diff --git a/NEsper/NEsper/util/SimpleByteArrayOutputStream.cs b/NEsper/NEsper/util/SimpleByteArrayOutputStream.cs deleted file mode 100644 index b05612234..000000000 --- a/NEsper/NEsper/util/SimpleByteArrayOutputStream.cs +++ /dev/null @@ -1,73 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2006-2017 Esper Team. All rights reserved. / -// http://esper.codehaus.org / -// ---------------------------------------------------------------------------------- / -// The software in this package is published under the terms of the GPL license / -// a copy of which has been included with this distribution in the license.txt file. / -/////////////////////////////////////////////////////////////////////////////////////// - -using System; -using System.IO; - -using com.espertech.esper.compat; -using com.espertech.esper.compat.collections; -using com.espertech.esper.compat.logging; - -namespace com.espertech.esper.util -{ - /// - /// Output stream that relies on a simple byte array, unchecked. - /// - public class SimpleByteArrayOutputStream : OutputStream{ - private byte[] buf = null; - private int size = 0; - - /// Ctor. - public SimpleByteArrayOutputStream() { - This(5 * 1024); - } - - /// - /// Ctor. - /// - /// initial size - public SimpleByteArrayOutputStream(int initSize) { - this.size = 0; - this.buf = new byte[initSize]; - } - - private void VerifyBufferSize(int sz) { - if (sz > buf.Length) { - byte[] old = buf; - buf = new byte[Math.Max(sz, 2 * buf.Length)]; - System.Arraycopy(old, 0, buf, 0, old.Length); - } - } - - public void Write(byte[] b) { - VerifyBufferSize(size + b.Length); - System.Arraycopy(b, 0, buf, size, b.Length); - size += b.Length; - } - - public void Write(byte[] b, int off, int len) { - VerifyBufferSize(size + len); - System.Arraycopy(b, off, buf, size, len); - size += len; - } - - public void Write(int b) { - VerifyBufferSize(size + 1); - buf[size++] = (byte) b; - } - - /// - /// Return the input stream for the output buffer. - /// - /// input stream for existing buffer - public InputStream GetInputStream() { - return new SimpleByteArrayInputStream(buf, size); - } - - } -} // end of namespace diff --git a/NEsper/NEsper/util/SimpleNumberCoercerFactory.cs b/NEsper/NEsper/util/SimpleNumberCoercerFactory.cs deleted file mode 100644 index c57bb3088..000000000 --- a/NEsper/NEsper/util/SimpleNumberCoercerFactory.cs +++ /dev/null @@ -1,243 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2006-2017 Esper Team. All rights reserved. / -// http://esper.codehaus.org / -// ---------------------------------------------------------------------------------- / -// The software in this package is published under the terms of the GPL license / -// a copy of which has been included with this distribution in the license.txt file. / -/////////////////////////////////////////////////////////////////////////////////////// - -using System; - -using com.espertech.esper.compat; -using com.espertech.esper.compat.collections; -using com.espertech.esper.compat.logging; - -using java.math; - -namespace com.espertech.esper.util -{ - /// - /// Factory for conversion/coercion and widening implementations for numbers. - /// - public class SimpleNumberCoercerFactory { - private static SimpleNumberCoercerNull nullCoerce = new SimpleNumberCoercerNull(); - private static SimpleNumberCoercerDouble doubleCoerce = new SimpleNumberCoercerDouble(); - private static SimpleNumberCoercerLong longCoerce = new SimpleNumberCoercerLong(); - private static SimpleNumberCoercerFloat floatCoerce = new SimpleNumberCoercerFloat(); - private static SimpleNumberCoercerInt intCoerce = new SimpleNumberCoercerInt(); - private static SimpleNumberCoercerShort shortCoerce = new SimpleNumberCoercerShort(); - private static SimpleNumberCoercerByte byteCoerce = new SimpleNumberCoercerByte(); - private static SimpleNumberCoercerBigInt bigIntCoerce = new SimpleNumberCoercerBigInt(); - private static SimpleNumberCoercerBigIntNull bigIntCoerceNull = new SimpleNumberCoercerBigIntNull(); - private static SimpleNumberCoercerBigDecLong bigDecCoerceLong = new SimpleNumberCoercerBigDecLong(); - private static SimpleNumberCoercerBigDecDouble bigDecCoerceDouble = new SimpleNumberCoercerBigDecDouble(); - private static SimpleNumberCoercerBigDecNull bigDecCoerceNull = new SimpleNumberCoercerBigDecNull(); - - /// - /// Returns a coercer/widener to BigDecimal for a given type. - /// - /// to widen - /// widener - public static SimpleNumberBigDecimalCoercer GetCoercerBigDecimal(Type fromType) { - if (fromType == Typeof(BigDecimal)) { - return bigDecCoerceNull; - } - if (JavaClassHelper.IsFloatingPointClass(fromType)) { - return bigDecCoerceDouble; - } - return bigDecCoerceLong; - } - - /// - /// Returns a coercer/widener to BigInteger for a given type. - /// - /// to widen - /// widener - public static SimpleNumberBigIntegerCoercer GetCoercerBigInteger(Type fromType) { - if (fromType == Typeof(BigInteger)) { - return bigIntCoerceNull; - } - return bigIntCoerce; - } - - /// - /// Returns a coercer/widener/narrower to a result number type from a given type. - /// - /// to widen/narrow, can be null to indicate that no shortcut-coercer is used - /// type to widen/narrow to - /// widener/narrower - public static SimpleNumberCoercer GetCoercer(Type fromType, Type resultBoxedType) { - if (fromType == resultBoxedType) { - return nullCoerce; - } - if (resultBoxedType == Typeof(double?)) { - return doubleCoerce; - } - if (resultBoxedType == Typeof(long)) { - return longCoerce; - } - if (resultBoxedType == Typeof(Float)) { - return floatCoerce; - } - if (resultBoxedType == Typeof(int?)) { - return intCoerce; - } - if (resultBoxedType == Typeof(Short)) { - return shortCoerce; - } - if (resultBoxedType == Typeof(Byte)) { - return byteCoerce; - } - if (resultBoxedType == Typeof(BigInteger)) { - return bigIntCoerce; - } - if (resultBoxedType == Typeof(BigDecimal)) { - if (JavaClassHelper.IsFloatingPointClass(fromType)) { - return bigDecCoerceDouble; - } - return bigDecCoerceLong; - } - throw new IllegalArgumentException("Cannot coerce to number subtype " + resultBoxedType.Name); - } - - private static class SimpleNumberCoercerNull : SimpleNumberCoercer { - public Number CoerceBoxed(Number numToCoerce) { - return numToCoerce; - } - - public Type GetReturnType() { - return Typeof(Number); - } - } - - private static class SimpleNumberCoercerDouble : SimpleNumberCoercer { - public Number CoerceBoxed(Number numToCoerce) { - return NumToCoerce.DoubleValue(); - } - - public Type GetReturnType() { - return Typeof(double?); - } - } - - private static class SimpleNumberCoercerLong : SimpleNumberCoercer { - public Number CoerceBoxed(Number numToCoerce) { - return NumToCoerce.LongValue(); - } - - public Type GetReturnType() { - return Typeof(long); - } - } - - private static class SimpleNumberCoercerInt : SimpleNumberCoercer { - public Number CoerceBoxed(Number numToCoerce) { - return NumToCoerce.IntValue(); - } - - public Type GetReturnType() { - return Typeof(int?); - } - } - - private static class SimpleNumberCoercerFloat : SimpleNumberCoercer { - public Number CoerceBoxed(Number numToCoerce) { - return NumToCoerce.FloatValue(); - } - - public Type GetReturnType() { - return Typeof(Float); - } - } - - private static class SimpleNumberCoercerShort : SimpleNumberCoercer { - public Number CoerceBoxed(Number numToCoerce) { - return NumToCoerce.ShortValue(); - } - - public Type GetReturnType() { - return Typeof(Short); - } - } - - private static class SimpleNumberCoercerByte : SimpleNumberCoercer { - public Number CoerceBoxed(Number numToCoerce) { - return NumToCoerce.ByteValue(); - } - - public Type GetReturnType() { - return Typeof(Byte); - } - } - - private static class SimpleNumberCoercerBigInt : SimpleNumberCoercer, SimpleNumberBigIntegerCoercer { - public Number CoerceBoxed(Number numToCoerce) { - return BigInteger.ValueOf(numToCoerce.LongValue()); - } - - public BigInteger CoerceBoxedBigInt(Number numToCoerce) { - return BigInteger.ValueOf(numToCoerce.LongValue()); - } - - public Type GetReturnType() { - return Typeof(long); - } - } - - private static class SimpleNumberCoercerBigDecLong : SimpleNumberCoercer, SimpleNumberBigDecimalCoercer { - public Number CoerceBoxed(Number numToCoerce) { - return new BigDecimal(numToCoerce.LongValue()); - } - - public BigDecimal CoerceBoxedBigDec(Number numToCoerce) { - return new BigDecimal(numToCoerce.LongValue()); - } - - public Type GetReturnType() { - return Typeof(long); - } - } - - private static class SimpleNumberCoercerBigDecDouble : SimpleNumberCoercer, SimpleNumberBigDecimalCoercer { - public Number CoerceBoxed(Number numToCoerce) { - return new BigDecimal(numToCoerce.DoubleValue()); - } - - public BigDecimal CoerceBoxedBigDec(Number numToCoerce) { - return new BigDecimal(numToCoerce.DoubleValue()); - } - - public Type GetReturnType() { - return Typeof(double?); - } - } - - private static class SimpleNumberCoercerBigIntNull : SimpleNumberCoercer, SimpleNumberBigIntegerCoercer { - public Number CoerceBoxed(Number numToCoerce) { - return numToCoerce; - } - - public BigInteger CoerceBoxedBigInt(Number numToCoerce) { - return (BigInteger) numToCoerce; - } - - public Type GetReturnType() { - return Typeof(Number); - } - } - - private static class SimpleNumberCoercerBigDecNull : SimpleNumberCoercer, SimpleNumberBigDecimalCoercer { - public Number CoerceBoxed(Number numToCoerce) { - return numToCoerce; - } - - public BigDecimal CoerceBoxedBigDec(Number numToCoerce) { - return (BigDecimal) numToCoerce; - } - - public Type GetReturnType() { - return Typeof(Number); - } - } - } -} // end of namespace